aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.checkpatch.conf15
-rw-r--r--.codecov.yml24
-rw-r--r--.github/actions/build-failure-log/action.yml7
-rw-r--r--.github/actions/dump-log/action.yml7
-rw-r--r--.github/actions/run-failure-log/action.yml7
-rw-r--r--.github/workflows/ci-pipeline-arm64.yml347
-rw-r--r--.github/workflows/ci-pipeline.yml465
-rw-r--r--.github/workflows/coverity.yml25
-rw-r--r--.github/workflows/gh-pages.yml52
-rw-r--r--.gitignore15
-rw-r--r--.travis.yml260
-rw-r--r--CHANGELOG3661
-rw-r--r--DEPENDENCIES426
-rw-r--r--LICENSE19
-rw-r--r--Makefile.am58
-rw-r--r--Makefile.inc25
-rw-r--r--README65
-rwxr-xr-xbootstrap2
-rw-r--r--config/README10
-rw-r--r--config/odp-linux-generic.conf398
-rw-r--r--configure.ac547
-rw-r--r--doc/.gitignore1
-rw-r--r--doc/Doxyfile_common15
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/Makefile.inc3
-rw-r--r--doc/application-api-guide/Doxyfile13
-rw-r--r--doc/application-api-guide/Makefile.am2
-rw-r--r--doc/application-api-guide/api_guide_lines.dox23
-rw-r--r--doc/application-api-guide/examples.dox35
-rw-r--r--doc/application-api-guide/odp.dox22
-rw-r--r--doc/application-api-guide/release.dox8
-rw-r--r--doc/driver-api-guide/.gitignore1
-rw-r--r--doc/driver-api-guide/Doxyfile14
-rw-r--r--doc/driver-api-guide/Makefile.am5
-rw-r--r--doc/driver-api-guide/odp.dox20
-rw-r--r--doc/helper-guide/Doxyfile17
-rw-r--r--doc/helper-guide/odp.dox10
-rw-r--r--doc/images/.gitignore1
-rw-r--r--doc/images/ODP-Logo-HQ.svg115
-rw-r--r--doc/images/ipsec-inline.svg31
-rw-r--r--doc/images/ipsec-lookaside.svg29
-rw-r--r--doc/images/ipsec_sa_states.msc76
-rw-r--r--doc/images/pktio_fsm.gv1
-rw-r--r--doc/images/reflen.svg45
-rw-r--r--doc/images/timeout_fsm.gv9
-rw-r--r--doc/images/timer_fsm.gv16
-rw-r--r--doc/implementers-guide/Makefile.am6
-rw-r--r--doc/implementers-guide/implementers-guide.adoc256
-rw-r--r--doc/m4/configure.m447
-rw-r--r--doc/platform-api-guide/Doxyfile17
-rw-r--r--doc/platform-api-guide/Makefile.am3
-rw-r--r--doc/process-guide/faq.adoc9
-rw-r--r--doc/process-guide/release-guide.adoc2
-rw-r--r--doc/users-guide/Makefile.am15
-rw-r--r--doc/users-guide/users-guide-cls.adoc34
-rw-r--r--doc/users-guide/users-guide-comp.adoc168
-rw-r--r--doc/users-guide/users-guide-crypto.adoc81
-rw-r--r--doc/users-guide/users-guide-ipsec.adoc467
-rw-r--r--doc/users-guide/users-guide-packet.adoc69
-rw-r--r--doc/users-guide/users-guide-pktio.adoc407
-rw-r--r--doc/users-guide/users-guide-timer.adoc48
-rw-r--r--doc/users-guide/users-guide-tm.adoc8
-rw-r--r--doc/users-guide/users-guide-utilities-examples.adoc18
-rw-r--r--doc/users-guide/users-guide.adoc317
-rw-r--r--example/Makefile.am18
-rw-r--r--example/Makefile.inc25
-rw-r--r--example/classifier/.gitignore1
-rw-r--r--example/classifier/Makefile.am36
-rw-r--r--example/classifier/odp_classifier.c1071
-rwxr-xr-xexample/classifier/odp_classifier_run.sh32
-rw-r--r--example/classifier/udp64.pcapbin0 -> 18544 bytes
-rw-r--r--example/cli/.gitignore3
-rw-r--r--example/cli/Makefile.am31
-rw-r--r--example/cli/odp_cli.c242
-rwxr-xr-xexample/cli/odp_cli_run.sh7
-rw-r--r--example/debug/.gitignore3
-rw-r--r--example/debug/Makefile.am9
-rw-r--r--example/debug/odp_debug.c724
-rw-r--r--example/example_debug.h93
-rw-r--r--example/generator/.gitignore1
-rw-r--r--example/generator/Makefile.am10
-rw-r--r--example/generator/odp_generator.c1457
-rw-r--r--example/hello/Makefile.am6
-rw-r--r--example/hello/odp_hello.c52
-rw-r--r--example/ipfragreass/Makefile.am25
-rw-r--r--example/ipfragreass/odp_ipfragreass.c56
-rw-r--r--example/ipfragreass/odp_ipfragreass_atomics.h55
-rw-r--r--example/ipfragreass/odp_ipfragreass_atomics_arm.h127
-rw-r--r--example/ipfragreass/odp_ipfragreass_fragment.c8
-rw-r--r--example/ipfragreass/odp_ipfragreass_fragment.h8
-rw-r--r--example/ipfragreass/odp_ipfragreass_helpers.c14
-rw-r--r--example/ipfragreass/odp_ipfragreass_helpers.h14
-rw-r--r--example/ipfragreass/odp_ipfragreass_ip.h8
-rw-r--r--example/ipfragreass/odp_ipfragreass_reassemble.c61
-rw-r--r--example/ipfragreass/odp_ipfragreass_reassemble.h48
-rw-r--r--example/ipsec/.gitignore1
-rw-r--r--example/ipsec/Makefile.am36
-rw-r--r--example/ipsec/README171
-rw-r--r--example/ipsec/odp_ipsec.c1619
-rw-r--r--example/ipsec/odp_ipsec_cache.c219
-rw-r--r--example/ipsec/odp_ipsec_cache.h133
-rw-r--r--example/ipsec/odp_ipsec_fwd_db.c167
-rw-r--r--example/ipsec/odp_ipsec_fwd_db.h95
-rw-r--r--example/ipsec/odp_ipsec_loop_db.c56
-rw-r--r--example/ipsec/odp_ipsec_loop_db.h128
-rw-r--r--example/ipsec/odp_ipsec_misc.h343
-rwxr-xr-xexample/ipsec/odp_ipsec_run_ah_in12
-rwxr-xr-xexample/ipsec/odp_ipsec_run_ah_out12
-rwxr-xr-xexample/ipsec/odp_ipsec_run_both_in14
-rwxr-xr-xexample/ipsec/odp_ipsec_run_both_out14
-rwxr-xr-xexample/ipsec/odp_ipsec_run_esp_in13
-rwxr-xr-xexample/ipsec/odp_ipsec_run_esp_out13
-rwxr-xr-xexample/ipsec/odp_ipsec_run_live17
-rwxr-xr-xexample/ipsec/odp_ipsec_run_router9
-rwxr-xr-xexample/ipsec/odp_ipsec_run_simple10
-rw-r--r--example/ipsec/odp_ipsec_sa_db.c318
-rw-r--r--example/ipsec/odp_ipsec_sa_db.h131
-rw-r--r--example/ipsec/odp_ipsec_sp_db.c148
-rw-r--r--example/ipsec/odp_ipsec_sp_db.h70
-rw-r--r--example/ipsec/odp_ipsec_stream.c635
-rw-r--r--example/ipsec/odp_ipsec_stream.h130
-rw-r--r--example/ipsec_api/.gitignore2
-rw-r--r--example/ipsec_api/Makefile.am76
-rw-r--r--example/ipsec_api/README155
-rw-r--r--example/ipsec_api/odp_ipsec.c1406
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_in.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_out.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_in.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_out.sh43
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_live.sh61
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_router.sh57
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_simple.sh41
-rw-r--r--example/ipsec_api/odp_ipsec_cache.c203
-rw-r--r--example/ipsec_api/odp_ipsec_cache.h114
l---------example/ipsec_api/odp_ipsec_fwd_db.c1
l---------example/ipsec_api/odp_ipsec_fwd_db.h1
l---------example/ipsec_api/odp_ipsec_misc.h1
l---------example/ipsec_api/odp_ipsec_sa_db.c1
l---------example/ipsec_api/odp_ipsec_sa_db.h1
l---------example/ipsec_api/odp_ipsec_sp_db.c1
l---------example/ipsec_api/odp_ipsec_sp_db.h1
l---------example/ipsec_api/odp_ipsec_stream.c1
l---------example/ipsec_api/odp_ipsec_stream.h1
-rw-r--r--example/ipsec_crypto/.gitignore2
-rw-r--r--example/ipsec_crypto/Makefile.am72
-rw-r--r--example/ipsec_crypto/README169
-rw-r--r--example/ipsec_crypto/odp_ipsec.c1787
-rw-r--r--example/ipsec_crypto/odp_ipsec_cache.c234
-rw-r--r--example/ipsec_crypto/odp_ipsec_cache.h134
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_ah_in.sh43
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_ah_out.sh43
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_both_in.sh44
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_both_out.sh44
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_esp_in.sh43
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_esp_out.sh43
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_live.sh65
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_router.sh57
-rwxr-xr-xexample/ipsec_crypto/odp_ipsec_crypto_run_simple.sh41
-rw-r--r--example/ipsec_crypto/odp_ipsec_fwd_db.c173
-rw-r--r--example/ipsec_crypto/odp_ipsec_fwd_db.h98
-rw-r--r--example/ipsec_crypto/odp_ipsec_misc.h328
-rw-r--r--example/ipsec_crypto/odp_ipsec_sa_db.c339
-rw-r--r--example/ipsec_crypto/odp_ipsec_sa_db.h138
-rw-r--r--example/ipsec_crypto/odp_ipsec_sp_db.c159
-rw-r--r--example/ipsec_crypto/odp_ipsec_sp_db.h71
-rw-r--r--example/ipsec_crypto/odp_ipsec_stream.c747
-rw-r--r--example/ipsec_crypto/odp_ipsec_stream.h141
-rw-r--r--example/l2fwd/README8
l---------example/l2fwd/odp_l2fwd.c1
-rw-r--r--example/l2fwd_simple/.gitignore1
-rw-r--r--example/l2fwd_simple/Makefile.am33
-rwxr-xr-xexample/l2fwd_simple/l2fwd_simple_run.sh28
-rw-r--r--example/l2fwd_simple/odp_l2fwd_simple.c214
-rw-r--r--example/l3fwd/.gitignore1
-rw-r--r--example/l3fwd/Makefile.am41
-rw-r--r--example/l3fwd/odp_l3fwd.c311
-rw-r--r--example/l3fwd/odp_l3fwd_db.c30
-rw-r--r--example/l3fwd/odp_l3fwd_db.h12
-rw-r--r--example/l3fwd/odp_l3fwd_lpm.c18
-rw-r--r--example/l3fwd/odp_l3fwd_lpm.h8
-rwxr-xr-xexample/l3fwd/odp_l3fwd_run.sh32
-rw-r--r--example/m4/configure.m435
-rw-r--r--example/packet/.gitignore2
-rw-r--r--example/packet/Makefile.am38
-rw-r--r--example/packet/odp_packet_dump.c765
-rw-r--r--example/packet/odp_pktio.c195
-rwxr-xr-xexample/packet/packet_dump_run.sh26
-rwxr-xr-xexample/packet/pktio_run.sh72
-rw-r--r--example/ping/.gitignore5
-rw-r--r--example/ping/Makefile.am34
-rw-r--r--example/ping/icmp_echo_req.pcapbin0 -> 11424 bytes
-rw-r--r--example/ping/odp_ping.c766
-rwxr-xr-xexample/ping/ping_run.sh33
-rw-r--r--example/simple_pipeline/.gitignore4
-rw-r--r--example/simple_pipeline/Makefile.am34
-rw-r--r--example/simple_pipeline/odp_simple_pipeline.c965
-rwxr-xr-xexample/simple_pipeline/simple_pipeline_run.sh37
-rw-r--r--example/simple_pipeline/udp64.pcap (renamed from test/common_plat/performance/udp64.pcap)bin7624 -> 7624 bytes
-rw-r--r--example/switch/.gitignore1
-rw-r--r--example/switch/Makefile.am33
-rw-r--r--example/switch/odp_switch.c333
-rwxr-xr-xexample/switch/switch_run.sh36
-rw-r--r--example/sysinfo/.gitignore3
-rw-r--r--example/sysinfo/Makefile.am9
-rw-r--r--example/sysinfo/odp_sysinfo.c1226
-rw-r--r--example/time/.gitignore1
-rw-r--r--example/time/Makefile.am10
-rw-r--r--example/time/time_global_test.c364
-rw-r--r--example/timer/.gitignore1
-rw-r--r--example/timer/Makefile.am15
-rw-r--r--example/timer/odp_timer_simple.c86
-rw-r--r--example/timer/odp_timer_test.c544
-rw-r--r--example/traffic_mgmt/.gitignore4
-rw-r--r--example/traffic_mgmt/Makefile.am10
-rw-r--r--example/traffic_mgmt/odp_traffic_mgmt.c323
-rw-r--r--helper/.gitignore4
-rw-r--r--helper/Makefile.am93
l---------helper/check-globals.sh1
-rw-r--r--helper/chksum.c104
-rw-r--r--helper/cli.c1019
-rw-r--r--helper/cuckootable.c58
-rw-r--r--helper/eth.c10
-rw-r--r--helper/hashtable.c37
-rw-r--r--helper/include/odp/helper/.gitignore1
-rw-r--r--helper/include/odp/helper/autoheader_external.h.in17
-rw-r--r--helper/include/odp/helper/chksum.h81
-rw-r--r--helper/include/odp/helper/cli.h182
-rw-r--r--helper/include/odp/helper/deprecated.h37
-rw-r--r--helper/include/odp/helper/eth.h32
-rw-r--r--helper/include/odp/helper/gtp.h48
-rw-r--r--helper/include/odp/helper/icmp.h61
-rw-r--r--helper/include/odp/helper/igmp.h48
-rw-r--r--helper/include/odp/helper/ip.h122
-rw-r--r--helper/include/odp/helper/ipsec.h70
-rw-r--r--helper/include/odp/helper/linux.h6
-rw-r--r--helper/include/odp/helper/linux/process.h14
-rw-r--r--helper/include/odp/helper/linux/pthread.h14
-rw-r--r--helper/include/odp/helper/macros.h67
-rw-r--r--helper/include/odp/helper/odph_api.h18
-rw-r--r--helper/include/odp/helper/odph_cuckootable.h10
-rw-r--r--helper/include/odp/helper/odph_debug.h114
-rw-r--r--helper/include/odp/helper/odph_hashtable.h10
-rw-r--r--helper/include/odp/helper/odph_iplookuptable.h20
-rw-r--r--helper/include/odp/helper/odph_lineartable.h10
-rw-r--r--helper/include/odp/helper/sctp.h49
-rw-r--r--helper/include/odp/helper/strong_types.h26
-rw-r--r--helper/include/odp/helper/table.h34
-rw-r--r--helper/include/odp/helper/tcp.h11
-rw-r--r--helper/include/odp/helper/threads.h329
-rw-r--r--helper/include/odp/helper/udp.h14
-rw-r--r--helper/include/odp/helper/version.h.in72
-rw-r--r--helper/include/odph_list_internal.h88
-rw-r--r--helper/ip.c6
-rw-r--r--helper/iplookuptable.c96
-rw-r--r--helper/ipsec.c218
-rw-r--r--helper/libodphelper.pc.in11
-rw-r--r--helper/lineartable.c22
-rw-r--r--helper/linux/thread.c11
-rw-r--r--helper/m4/configure.m472
-rw-r--r--helper/m4/libcli.m447
-rw-r--r--helper/odph_debug.h89
-rw-r--r--helper/odph_list_internal.h85
-rw-r--r--helper/test/.gitignore4
-rw-r--r--helper/test/Makefile.am86
-rw-r--r--helper/test/chksum.c24
-rw-r--r--helper/test/cli.c110
-rw-r--r--helper/test/cuckootable.c13
-rw-r--r--helper/test/debug.c29
-rw-r--r--helper/test/iplookuptable.c10
-rw-r--r--helper/test/linux/Makefile.am5
-rw-r--r--helper/test/linux/process.c31
-rw-r--r--helper/test/linux/pthread.c21
-rw-r--r--helper/test/macros.c71
-rw-r--r--helper/test/odpthreads.c103
-rwxr-xr-xhelper/test/odpthreads_as_processes8
-rwxr-xr-xhelper/test/odpthreads_as_pthreads8
-rw-r--r--helper/test/parse.c10
-rw-r--r--helper/test/table.c61
-rw-r--r--helper/test/version.c16
-rw-r--r--helper/threads.c547
-rw-r--r--helper/version.c18
-rw-r--r--include/Makefile.am583
-rw-r--r--include/README117
-rw-r--r--include/odp.h23
-rw-r--r--include/odp/api/abi-default/align.h57
-rw-r--r--include/odp/api/abi-default/atomic.h90
-rw-r--r--include/odp/api/abi-default/barrier.h36
-rw-r--r--include/odp/api/abi-default/buffer.h18
-rw-r--r--include/odp/api/abi-default/buffer_types.h32
-rw-r--r--include/odp/api/abi-default/byteorder.h71
-rw-r--r--include/odp/api/abi-default/classification.h38
-rw-r--r--include/odp/api/abi-default/comp.h33
-rw-r--r--include/odp/api/abi-default/cpu.h20
-rw-r--r--include/odp/api/abi-default/cpumask.h52
-rw-r--r--include/odp/api/abi-default/crypto.h19
-rw-r--r--include/odp/api/abi-default/crypto_types.h31
-rw-r--r--include/odp/api/abi-default/debug.h33
-rw-r--r--include/odp/api/abi-default/dma.h18
-rw-r--r--include/odp/api/abi-default/dma_types.h46
-rw-r--r--include/odp/api/abi-default/errno.h18
-rw-r--r--include/odp/api/abi-default/event.h18
-rw-r--r--include/odp/api/abi-default/event_types.h55
-rw-r--r--include/odp/api/abi-default/hash.h18
-rw-r--r--include/odp/api/abi-default/init.h33
-rw-r--r--include/odp/api/abi-default/ipsec.h19
-rw-r--r--include/odp/api/abi-default/ipsec_types.h34
-rw-r--r--include/odp/api/abi-default/ml_types.h48
-rw-r--r--include/odp/api/abi-default/packet.h18
-rw-r--r--include/odp/api/abi-default/packet_flags.h22
-rw-r--r--include/odp/api/abi-default/packet_io.h18
-rw-r--r--include/odp/api/abi-default/packet_io_types.h69
-rw-r--r--include/odp/api/abi-default/packet_types.h134
-rw-r--r--include/odp/api/abi-default/pool.h18
-rw-r--r--include/odp/api/abi-default/pool_types.h36
-rw-r--r--include/odp/api/abi-default/proto_stats.h18
-rw-r--r--include/odp/api/abi-default/proto_stats_types.h34
-rw-r--r--include/odp/api/abi-default/queue.h18
-rw-r--r--include/odp/api/abi-default/queue_types.h34
-rw-r--r--include/odp/api/abi-default/random.h18
-rw-r--r--include/odp/api/abi-default/rwlock.h32
-rw-r--r--include/odp/api/abi-default/rwlock_recursive.h34
-rw-r--r--include/odp/api/abi-default/schedule.h18
-rw-r--r--include/odp/api/abi-default/schedule_types.h51
-rw-r--r--include/odp/api/abi-default/shared_memory.h35
-rw-r--r--include/odp/api/abi-default/spinlock.h27
-rw-r--r--include/odp/api/abi-default/spinlock_recursive.h32
-rw-r--r--include/odp/api/abi-default/stash.h18
-rw-r--r--include/odp/api/abi-default/stash_types.h33
-rw-r--r--include/odp/api/abi-default/std.h18
-rw-r--r--include/odp/api/abi-default/std_types.h35
-rw-r--r--include/odp/api/abi-default/sync.h22
-rw-r--r--include/odp/api/abi-default/thread.h18
-rw-r--r--include/odp/api/abi-default/thread_types.h26
-rw-r--r--include/odp/api/abi-default/thrmask.h46
-rw-r--r--include/odp/api/abi-default/ticketlock.h30
-rw-r--r--include/odp/api/abi-default/time.h18
-rw-r--r--include/odp/api/abi-default/time_types.h47
-rw-r--r--include/odp/api/abi-default/timer.h18
-rw-r--r--include/odp/api/abi-default/timer_types.h53
-rw-r--r--include/odp/api/abi-default/traffic_mngr.h158
-rw-r--r--include/odp/api/abi-default/version.h28
-rw-r--r--include/odp/api/align.h26
-rw-r--r--include/odp/api/atomic.h26
-rw-r--r--include/odp/api/barrier.h29
-rw-r--r--include/odp/api/buffer.h26
-rw-r--r--include/odp/api/buffer_types.h26
-rw-r--r--include/odp/api/byteorder.h26
-rw-r--r--include/odp/api/chksum.h24
-rw-r--r--include/odp/api/classification.h29
-rw-r--r--include/odp/api/comp.h26
-rw-r--r--include/odp/api/cpu.h28
-rw-r--r--include/odp/api/cpumask.h26
-rw-r--r--include/odp/api/crypto.h26
-rw-r--r--include/odp/api/crypto_types.h26
-rw-r--r--include/odp/api/debug.h26
-rw-r--r--include/odp/api/deprecated.h24
-rw-r--r--include/odp/api/dma.h26
-rw-r--r--include/odp/api/dma_types.h26
-rw-r--r--include/odp/api/errno.h26
-rw-r--r--include/odp/api/event.h26
-rw-r--r--include/odp/api/event_types.h26
-rw-r--r--include/odp/api/hash.h26
-rw-r--r--include/odp/api/hints.h24
-rw-r--r--include/odp/api/init.h26
-rw-r--r--include/odp/api/ipsec.h26
-rw-r--r--include/odp/api/ipsec_types.h26
-rw-r--r--include/odp/api/ml.h24
-rw-r--r--include/odp/api/ml_quantize.h24
-rw-r--r--include/odp/api/ml_types.h26
-rw-r--r--include/odp/api/packet.h27
-rw-r--r--include/odp/api/packet_flags.h26
-rw-r--r--include/odp/api/packet_io.h26
-rw-r--r--include/odp/api/packet_io_stats.h26
-rw-r--r--include/odp/api/packet_io_types.h26
-rw-r--r--include/odp/api/packet_types.h27
-rw-r--r--include/odp/api/pool.h26
-rw-r--r--include/odp/api/pool_types.h26
-rw-r--r--include/odp/api/proto_stats.h29
-rw-r--r--include/odp/api/proto_stats_types.h26
-rw-r--r--include/odp/api/protocols.h24
-rw-r--r--include/odp/api/queue.h27
-rw-r--r--include/odp/api/queue_types.h26
-rw-r--r--include/odp/api/random.h26
-rw-r--r--include/odp/api/random_types.h24
-rw-r--r--include/odp/api/reassembly.h24
-rw-r--r--include/odp/api/rwlock.h26
-rw-r--r--include/odp/api/rwlock_recursive.h26
-rw-r--r--include/odp/api/schedule.h26
-rw-r--r--include/odp/api/schedule_types.h26
-rw-r--r--include/odp/api/shared_memory.h26
-rw-r--r--include/odp/api/spec/align.h24
-rw-r--r--include/odp/api/spec/atomic.h177
-rw-r--r--include/odp/api/spec/barrier.h10
-rw-r--r--include/odp/api/spec/buffer.h80
-rw-r--r--include/odp/api/spec/buffer_types.h43
-rw-r--r--include/odp/api/spec/byteorder.h10
-rw-r--r--include/odp/api/spec/chksum.h51
-rw-r--r--include/odp/api/spec/classification.h1103
-rw-r--r--include/odp/api/spec/comp.h613
-rw-r--r--include/odp/api/spec/compiler.h53
-rw-r--r--include/odp/api/spec/cpu.h52
-rw-r--r--include/odp/api/spec/cpumask.h42
-rw-r--r--include/odp/api/spec/crypto.h768
-rw-r--r--include/odp/api/spec/crypto_types.h1059
-rw-r--r--include/odp/api/spec/debug.h18
-rw-r--r--include/odp/api/spec/deprecated.h.in10
-rw-r--r--include/odp/api/spec/dma.h370
-rw-r--r--include/odp/api/spec/dma_types.h576
-rw-r--r--include/odp/api/spec/errno.h15
-rw-r--r--include/odp/api/spec/event.h229
-rw-r--r--include/odp/api/spec/event_types.h114
-rw-r--r--include/odp/api/spec/hash.h14
-rw-r--r--include/odp/api/spec/hints.h21
-rw-r--r--include/odp/api/spec/init.h299
-rw-r--r--include/odp/api/spec/ipsec.h644
-rw-r--r--include/odp/api/spec/ipsec_types.h1495
-rw-r--r--include/odp/api/spec/ml.h699
-rw-r--r--include/odp/api/spec/ml_quantize.h124
-rw-r--r--include/odp/api/spec/ml_types.h873
-rw-r--r--include/odp/api/spec/packet.h1300
-rw-r--r--include/odp/api/spec/packet_flags.h288
-rw-r--r--include/odp/api/spec/packet_io.h784
-rw-r--r--include/odp/api/spec/packet_io_stats.h450
-rw-r--r--include/odp/api/spec/packet_io_types.h1253
-rw-r--r--include/odp/api/spec/packet_types.h525
-rw-r--r--include/odp/api/spec/pool.h429
-rw-r--r--include/odp/api/spec/pool_types.h944
-rw-r--r--include/odp/api/spec/proto_stats.h130
-rw-r--r--include/odp/api/spec/proto_stats_types.h129
-rw-r--r--include/odp/api/spec/protocols.h40
-rw-r--r--include/odp/api/spec/queue.h345
-rw-r--r--include/odp/api/spec/queue_types.h314
-rw-r--r--include/odp/api/spec/random.h45
-rw-r--r--include/odp/api/spec/random_types.h57
-rw-r--r--include/odp/api/spec/reassembly.h130
-rw-r--r--include/odp/api/spec/rwlock.h13
-rw-r--r--include/odp/api/spec/rwlock_recursive.h10
-rw-r--r--include/odp/api/spec/schedule.h328
-rw-r--r--include/odp/api/spec/schedule_types.h250
-rw-r--r--include/odp/api/spec/shared_memory.h211
-rw-r--r--include/odp/api/spec/spinlock.h10
-rw-r--r--include/odp/api/spec/spinlock_recursive.h10
-rw-r--r--include/odp/api/spec/stash.h439
-rw-r--r--include/odp/api/spec/stash_types.h312
-rw-r--r--include/odp/api/spec/std.h96
-rw-r--r--include/odp/api/spec/std_clib.h84
-rw-r--r--include/odp/api/spec/std_types.h146
-rw-r--r--include/odp/api/spec/sync.h71
-rw-r--r--include/odp/api/spec/system_info.h318
-rw-r--r--include/odp/api/spec/thread.h119
-rw-r--r--include/odp/api/spec/thread_types.h64
-rw-r--r--include/odp/api/spec/threshold.h104
-rw-r--r--include/odp/api/spec/thrmask.h10
-rw-r--r--include/odp/api/spec/ticketlock.h13
-rw-r--r--include/odp/api/spec/time.h149
-rw-r--r--include/odp/api/spec/time_types.h73
-rw-r--r--include/odp/api/spec/timer.h609
-rw-r--r--include/odp/api/spec/timer_types.h620
-rw-r--r--include/odp/api/spec/traffic_mngr.h1649
-rw-r--r--include/odp/api/spec/version.h.in32
-rw-r--r--include/odp/api/spinlock.h26
-rw-r--r--include/odp/api/spinlock_recursive.h26
-rw-r--r--include/odp/api/stash.h26
-rw-r--r--include/odp/api/stash_types.h26
-rw-r--r--include/odp/api/std.h21
-rw-r--r--include/odp/api/std_types.h27
-rw-r--r--include/odp/api/sync.h26
-rw-r--r--include/odp/api/system_info.h26
-rw-r--r--include/odp/api/thread.h26
-rw-r--r--include/odp/api/thread_types.h26
-rw-r--r--include/odp/api/threshold.h24
-rw-r--r--include/odp/api/thrmask.h26
-rw-r--r--include/odp/api/ticketlock.h26
-rw-r--r--include/odp/api/time.h26
-rw-r--r--include/odp/api/time_types.h26
-rw-r--r--include/odp/api/timer.h26
-rw-r--r--include/odp/api/timer_types.h26
-rw-r--r--include/odp/api/traffic_mngr.h26
-rw-r--r--include/odp/api/version.h26
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/buffer.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/classification.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/cpu.h18
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/crypto.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/event.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/packet.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/pool.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/queue.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/shared_memory.h8
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/arm32-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/buffer.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/classification.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/cpu.h18
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/crypto.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/event.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/packet.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/pool.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/queue.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/shared_memory.h8
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/arm64-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/buffer.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/classification.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/cpu.h18
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/crypto.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/event.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/packet.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/pool.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/queue.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/shared_memory.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/default-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/arch/default/api/abi/buffer.h33
-rw-r--r--include/odp/arch/default/api/abi/classification.h40
-rw-r--r--include/odp/arch/default/api/abi/crypto.h36
-rw-r--r--include/odp/arch/default/api/abi/event.h42
-rw-r--r--include/odp/arch/default/api/abi/packet.h50
-rw-r--r--include/odp/arch/default/api/abi/pool.h43
-rw-r--r--include/odp/arch/default/api/abi/queue.h35
-rw-r--r--include/odp/arch/default/api/abi/shared_memory.h35
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/buffer.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/classification.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/crypto.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/event.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/packet.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/pool.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/queue.h7
-rw-r--r--include/odp/arch/mips64-linux/odp/api/abi/shared_memory.h7
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/buffer.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/classification.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/cpu.h19
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/crypto.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/event.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/packet.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/pool.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/queue.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/shared_memory.h8
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/power64-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/buffer.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/classification.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/cpu.h18
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/crypto.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/event.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/packet.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/pool.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/queue.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/shared_memory.h8
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/x86_32-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/align.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/atomic.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/barrier.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/buffer.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/buffer_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/byteorder.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/classification.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/comp.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/cpu.h18
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/cpumask.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/crypto.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/crypto_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/debug.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/dma.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/dma_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/errno.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/event.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/event_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/hash.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/init.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/ipsec.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/ipsec_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/ml_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/packet.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/packet_flags.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/packet_io.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/packet_io_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/packet_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/pool.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/pool_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/proto_stats.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/proto_stats_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/queue.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/queue_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/random.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/rwlock.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/rwlock_recursive.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/schedule.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/schedule_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/shared_memory.h8
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/spinlock.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/spinlock_recursive.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/stash.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/stash_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/std.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/std_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/sync.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/thread.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/thread_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/thrmask.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/ticketlock.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/time.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/time_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/timer.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/timer_types.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/traffic_mngr.h5
-rw-r--r--include/odp/arch/x86_64-linux/odp/api/abi/version.h5
-rw-r--r--include/odp/autoheader_external.h.in20
-rw-r--r--include/odp/autoheader_internal.h.in38
-rw-r--r--include/odp/drv/spec/README4
-rw-r--r--include/odp/drv/spec/compiler.h51
-rw-r--r--include/odp/drv/spec/std_types.h40
-rw-r--r--include/odp/visibility_begin.h13
-rw-r--r--include/odp/visibility_end.h13
-rw-r--r--include/odp_api.h18
-rw-r--r--include/odp_drv.h27
-rw-r--r--m4/ax_check_compile_flag.m472
-rw-r--r--m4/ax_compiler_vendor.m487
-rw-r--r--m4/ax_compiler_version.m4492
-rw-r--r--m4/ax_prog_doxygen.m4186
-rw-r--r--m4/ax_pthread.m4332
-rw-r--r--m4/ax_valgrind_check.m423
-rw-r--r--m4/odp_atomic.m4194
-rw-r--r--m4/odp_check_flag.m436
-rw-r--r--m4/odp_dpdk.m4249
-rw-r--r--m4/odp_libconfig.m447
-rw-r--r--m4/odp_openssl.m464
-rw-r--r--m4/odp_pthread.m427
-rw-r--r--m4/odp_timer.m415
-rw-r--r--m4/odp_visibility.m425
-rw-r--r--pkgconfig/libodp-linux.pc.in11
-rw-r--r--pkgconfig/libodphelper.pc.in11
-rw-r--r--platform/Makefile.inc113
-rw-r--r--platform/linux-generic/.gitignore3
-rw-r--r--platform/linux-generic/Makefile.am661
-rw-r--r--platform/linux-generic/Makefile.inc2
-rw-r--r--platform/linux-generic/README90
-rw-r--r--platform/linux-generic/_fdserver.c694
-rw-r--r--platform/linux-generic/_ishm.c1810
-rw-r--r--platform/linux-generic/_ishmphy.c207
-rw-r--r--platform/linux-generic/arch/aarch64/cpu_flags.c1050
-rw-r--r--platform/linux-generic/arch/aarch64/cpu_flags.h18
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/atomic.h10
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/atomic_inlines.h276
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/cpu.h25
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/cpu_inlines.h58
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/hash_crc32.h101
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/sync_inlines.h31
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/time_cpu.h51
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/time_inlines.h5
-rw-r--r--platform/linux-generic/arch/aarch64/odp/api/abi/wait_until.h47
-rw-r--r--platform/linux-generic/arch/aarch64/odp_atomic.c54
-rw-r--r--platform/linux-generic/arch/aarch64/odp_atomic.h323
-rw-r--r--platform/linux-generic/arch/aarch64/odp_cpu.h199
-rw-r--r--platform/linux-generic/arch/aarch64/odp_cpu_cycles.c48
-rw-r--r--platform/linux-generic/arch/aarch64/odp_crypto_armv8.c894
-rw-r--r--platform/linux-generic/arch/aarch64/odp_random.h164
-rw-r--r--platform/linux-generic/arch/aarch64/odp_sysinfo_parse.c394
-rw-r--r--platform/linux-generic/arch/aarch64/odp_wait_until.h100
-rw-r--r--platform/linux-generic/arch/arm/odp/api/abi/cpu.h21
-rw-r--r--platform/linux-generic/arch/arm/odp/api/abi/cpu_inlines.h30
-rw-r--r--platform/linux-generic/arch/arm/odp/api/cpu_arch.h30
-rw-r--r--platform/linux-generic/arch/arm/odp_cpu.h84
-rw-r--r--platform/linux-generic/arch/arm/odp_cpu_arch.c100
-rw-r--r--platform/linux-generic/arch/arm/odp_sysinfo_parse.c36
-rw-r--r--platform/linux-generic/arch/common/odp/api/abi/time_cpu_inlines.h98
-rw-r--r--platform/linux-generic/arch/common/odp_time_cpu.c72
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/atomic_generic.h274
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/atomic_inlines.h5
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/cpu.h21
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/cpu_generic.h32
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/cpu_inlines.h22
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/hash_crc32.h35
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/sync_inlines.h31
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/time_inlines.h44
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/wait_until.h5
-rw-r--r--platform/linux-generic/arch/default/odp/api/abi/wait_until_generic.h25
-rw-r--r--platform/linux-generic/arch/default/odp/api/cpu_arch.h24
-rw-r--r--platform/linux-generic/arch/default/odp_atomic.c45
-rw-r--r--platform/linux-generic/arch/default/odp_atomic.h112
-rw-r--r--platform/linux-generic/arch/default/odp_cpu.h23
-rw-r--r--platform/linux-generic/arch/default/odp_cpu_arch.c64
-rw-r--r--platform/linux-generic/arch/default/odp_cpu_cycles.c45
-rw-r--r--platform/linux-generic/arch/default/odp_hash_crc32.c464
-rw-r--r--platform/linux-generic/arch/default/odp_random.c31
-rw-r--r--platform/linux-generic/arch/default/odp_random.h39
-rw-r--r--platform/linux-generic/arch/default/odp_sysinfo_parse.c29
-rw-r--r--platform/linux-generic/arch/default/odp_time.c110
-rw-r--r--platform/linux-generic/arch/default/odp_wait_until.h53
-rw-r--r--platform/linux-generic/arch/mips64/odp/api/cpu_arch.h32
-rw-r--r--platform/linux-generic/arch/mips64/odp_cpu_arch.c47
-rw-r--r--platform/linux-generic/arch/mips64/odp_sysinfo_parse.c68
-rw-r--r--platform/linux-generic/arch/powerpc/odp/api/abi/cpu.h23
-rw-r--r--platform/linux-generic/arch/powerpc/odp/api/cpu_arch.h24
-rw-r--r--platform/linux-generic/arch/powerpc/odp_cpu_arch.c64
-rw-r--r--platform/linux-generic/arch/powerpc/odp_sysinfo_parse.c32
-rw-r--r--platform/linux-generic/arch/x86/cpu_flags.c169
-rw-r--r--platform/linux-generic/arch/x86/cpu_flags.h9
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/cpu.h21
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/cpu_inlines.h44
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/cpu_rdtsc.h27
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/hash_crc32.h76
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/sync_inlines.h31
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/time_cpu.h33
-rw-r--r--platform/linux-generic/arch/x86/odp/api/abi/time_inlines.h5
-rw-r--r--platform/linux-generic/arch/x86/odp/api/cpu_arch.h29
-rw-r--r--platform/linux-generic/arch/x86/odp_cpu.h12
-rw-r--r--platform/linux-generic/arch/x86/odp_cpu_arch.c101
-rw-r--r--platform/linux-generic/arch/x86/odp_cpu_cycles.c19
-rw-r--r--platform/linux-generic/arch/x86/odp_random.h158
-rw-r--r--platform/linux-generic/arch/x86/odp_sysinfo_parse.c99
-rw-r--r--platform/linux-generic/arch/x86/odp_time_cpu.c106
l---------platform/linux-generic/check-globals.sh1
-rw-r--r--platform/linux-generic/doc/platform_specific.dox8
-rw-r--r--platform/linux-generic/dumpconfig/.gitignore1
-rw-r--r--platform/linux-generic/dumpconfig/Makefile.am10
-rw-r--r--platform/linux-generic/dumpconfig/dumpconfig.c41
-rw-r--r--platform/linux-generic/example/Makefile.am5
-rw-r--r--platform/linux-generic/example/ml/.gitignore5
-rw-r--r--platform/linux-generic/example/ml/Makefile.am46
-rw-r--r--platform/linux-generic/example/ml/README.md94
-rw-r--r--platform/linux-generic/example/ml/example_digit.csv1
-rw-r--r--platform/linux-generic/example/ml/mnist-12.onnxbin0 -> 26143 bytes
-rw-r--r--platform/linux-generic/example/ml/mnist.c300
-rw-r--r--platform/linux-generic/example/ml/model_explorer.c93
-rw-r--r--platform/linux-generic/example/ml/model_read.c47
-rw-r--r--platform/linux-generic/example/ml/model_read.h29
-rwxr-xr-xplatform/linux-generic/example/ml/odp_ml_run_mnist.sh9
-rwxr-xr-xplatform/linux-generic/example/ml/odp_ml_run_model_explorer.sh8
-rwxr-xr-xplatform/linux-generic/example/ml/odp_ml_run_simple_linear.sh8
-rw-r--r--platform/linux-generic/example/ml/simple_linear.c281
-rw-r--r--platform/linux-generic/example/ml/simple_linear.onnxbin0 -> 214 bytes
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/align.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/atomic.h94
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/barrier.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/buffer.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/buffer_types.h38
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/byteorder.h86
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/classification.h40
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/comp.h32
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/cpumask.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/crypto.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/crypto_types.h39
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/debug.h66
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/dma.h25
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/dma_types.h40
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/errno.h16
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/event.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/event_types.h58
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/hash.h19
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/init.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/ipsec.h30
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/ipsec_types.h39
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/ml_types.h45
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/packet.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/packet_flags.h24
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/packet_io.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/packet_io_types.h64
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/packet_types.h106
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/pool.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/pool_types.h40
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/proto_stats.h25
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/proto_stats_types.h38
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/queue.h25
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/queue_types.h40
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/random.h24
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/rwlock.h8
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/rwlock_recursive.h8
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/schedule.h26
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/schedule_types.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/shared_memory.h43
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/spinlock.h8
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/spinlock_recursive.h8
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/stash.h22
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/stash_types.h36
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/std.h24
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/std_types.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/sync.h32
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/thread.h6
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/thread_types.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/thrmask.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/ticketlock.h41
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/time.h6
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/time_types.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/timer.h6
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/timer_types.h47
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/traffic_mngr.h5
-rw-r--r--platform/linux-generic/include-abi/odp/api/abi/version.h5
-rw-r--r--platform/linux-generic/include/_fdserver_internal.h39
-rw-r--r--platform/linux-generic/include/_ishm_internal.h53
-rw-r--r--platform/linux-generic/include/_ishmphy_internal.h25
-rw-r--r--platform/linux-generic/include/ishmphy_internal.h6
-rw-r--r--platform/linux-generic/include/odp/api/align.h58
-rw-r--r--platform/linux-generic/include/odp/api/atomic.h42
-rw-r--r--platform/linux-generic/include/odp/api/barrier.h31
-rw-r--r--platform/linux-generic/include/odp/api/buffer.h39
-rw-r--r--platform/linux-generic/include/odp/api/byteorder.h43
-rw-r--r--platform/linux-generic/include/odp/api/classification.h46
-rw-r--r--platform/linux-generic/include/odp/api/compiler.h34
-rw-r--r--platform/linux-generic/include/odp/api/cpu.h28
-rw-r--r--platform/linux-generic/include/odp/api/cpumask.h28
-rw-r--r--platform/linux-generic/include/odp/api/crypto.h41
-rw-r--r--platform/linux-generic/include/odp/api/debug.h77
-rw-r--r--platform/linux-generic/include/odp/api/deprecated.h26
-rw-r--r--platform/linux-generic/include/odp/api/errno.h27
-rw-r--r--platform/linux-generic/include/odp/api/event.h36
-rw-r--r--platform/linux-generic/include/odp/api/hash.h34
-rw-r--r--platform/linux-generic/include/odp/api/hints.h34
-rw-r--r--platform/linux-generic/include/odp/api/init.h36
-rw-r--r--platform/linux-generic/include/odp/api/packet.h38
-rw-r--r--platform/linux-generic/include/odp/api/packet_flags.h31
-rw-r--r--platform/linux-generic/include/odp/api/packet_io.h41
-rw-r--r--platform/linux-generic/include/odp/api/packet_io_stats.h26
-rw-r--r--platform/linux-generic/include/odp/api/plat/atomic_inlines.h456
-rw-r--r--platform/linux-generic/include/odp/api/plat/atomic_types.h88
-rw-r--r--platform/linux-generic/include/odp/api/plat/barrier_types.h38
-rw-r--r--platform/linux-generic/include/odp/api/plat/buffer_inline_types.h35
-rw-r--r--platform/linux-generic/include/odp/api/plat/buffer_inlines.h82
-rw-r--r--platform/linux-generic/include/odp/api/plat/buffer_types.h46
-rw-r--r--platform/linux-generic/include/odp/api/plat/byteorder_inlines.h59
-rw-r--r--platform/linux-generic/include/odp/api/plat/byteorder_types.h84
-rw-r--r--platform/linux-generic/include/odp/api/plat/classification_types.h49
-rw-r--r--platform/linux-generic/include/odp/api/plat/cpu_inlines.h59
-rw-r--r--platform/linux-generic/include/odp/api/plat/cpumask_types.h54
-rw-r--r--platform/linux-generic/include/odp/api/plat/crypto_inlines.h68
-rw-r--r--platform/linux-generic/include/odp/api/plat/crypto_types.h45
-rw-r--r--platform/linux-generic/include/odp/api/plat/debug_inlines.h122
-rw-r--r--platform/linux-generic/include/odp/api/plat/dma_inlines.h133
-rw-r--r--platform/linux-generic/include/odp/api/plat/event_inline_types.h43
-rw-r--r--platform/linux-generic/include/odp/api/plat/event_inlines.h197
-rw-r--r--platform/linux-generic/include/odp/api/plat/event_types.h54
-rw-r--r--platform/linux-generic/include/odp/api/plat/event_validation_external.h109
-rw-r--r--platform/linux-generic/include/odp/api/plat/event_vector_inline_types.h49
-rw-r--r--platform/linux-generic/include/odp/api/plat/hash_inlines.h45
-rw-r--r--platform/linux-generic/include/odp/api/plat/init_types.h35
-rw-r--r--platform/linux-generic/include/odp/api/plat/ipsec_inlines.h56
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_flag_inlines.h253
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_flag_inlines_api.h41
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_inline_types.h174
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_inlines.h684
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_inlines_api.h97
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_io_inlines.h39
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_io_types.h64
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_types.h169
-rw-r--r--platform/linux-generic/include/odp/api/plat/packet_vector_inlines.h102
-rw-r--r--platform/linux-generic/include/odp/api/plat/pool_inline_types.h45
-rw-r--r--platform/linux-generic/include/odp/api/plat/pool_inlines.h39
-rw-r--r--platform/linux-generic/include/odp/api/plat/pool_types.h55
-rw-r--r--platform/linux-generic/include/odp/api/plat/queue_inline_types.h71
-rw-r--r--platform/linux-generic/include/odp/api/plat/queue_inlines.h70
-rw-r--r--platform/linux-generic/include/odp/api/plat/queue_types.h48
-rw-r--r--platform/linux-generic/include/odp/api/plat/rwlock_inlines.h103
-rw-r--r--platform/linux-generic/include/odp/api/plat/rwlock_recursive_inlines.h140
-rw-r--r--platform/linux-generic/include/odp/api/plat/rwlock_recursive_types.h38
-rw-r--r--platform/linux-generic/include/odp/api/plat/rwlock_types.h37
-rw-r--r--platform/linux-generic/include/odp/api/plat/schedule_inline_types.h66
-rw-r--r--platform/linux-generic/include/odp/api/plat/schedule_inlines.h133
-rw-r--r--platform/linux-generic/include/odp/api/plat/schedule_types.h62
-rw-r--r--platform/linux-generic/include/odp/api/plat/shared_memory_types.h50
-rw-r--r--platform/linux-generic/include/odp/api/plat/spinlock_inlines.h63
-rw-r--r--platform/linux-generic/include/odp/api/plat/spinlock_recursive_inlines.h96
-rw-r--r--platform/linux-generic/include/odp/api/plat/spinlock_recursive_types.h36
-rw-r--r--platform/linux-generic/include/odp/api/plat/spinlock_types.h34
-rw-r--r--platform/linux-generic/include/odp/api/plat/static_inline.h.in43
-rw-r--r--platform/linux-generic/include/odp/api/plat/std_clib_inlines.h36
-rw-r--r--platform/linux-generic/include/odp/api/plat/std_inlines.h39
-rw-r--r--platform/linux-generic/include/odp/api/plat/strong_types.h16
-rw-r--r--platform/linux-generic/include/odp/api/plat/sync_inlines.h45
-rw-r--r--platform/linux-generic/include/odp/api/plat/thread_inline_types.h34
-rw-r--r--platform/linux-generic/include/odp/api/plat/thread_inlines.h49
-rw-r--r--platform/linux-generic/include/odp/api/plat/thread_types.h34
-rw-r--r--platform/linux-generic/include/odp/api/plat/thrmask_types.h48
-rw-r--r--platform/linux-generic/include/odp/api/plat/ticketlock_inlines.h92
-rw-r--r--platform/linux-generic/include/odp/api/plat/ticketlock_inlines_api.h36
-rw-r--r--platform/linux-generic/include/odp/api/plat/ticketlock_types.h35
-rw-r--r--platform/linux-generic/include/odp/api/plat/time_inlines.h189
-rw-r--r--platform/linux-generic/include/odp/api/plat/time_types.h53
-rw-r--r--platform/linux-generic/include/odp/api/plat/timer_inline_types.h38
-rw-r--r--platform/linux-generic/include/odp/api/plat/timer_inlines.h104
-rw-r--r--platform/linux-generic/include/odp/api/plat/timer_types.h51
-rw-r--r--platform/linux-generic/include/odp/api/plat/traffic_mngr_types.h185
-rw-r--r--platform/linux-generic/include/odp/api/plat/version_types.h30
-rw-r--r--platform/linux-generic/include/odp/api/pool.h37
-rw-r--r--platform/linux-generic/include/odp/api/queue.h44
-rw-r--r--platform/linux-generic/include/odp/api/random.h34
-rw-r--r--platform/linux-generic/include/odp/api/rwlock.h28
-rw-r--r--platform/linux-generic/include/odp/api/rwlock_recursive.h28
-rw-r--r--platform/linux-generic/include/odp/api/schedule.h36
-rw-r--r--platform/linux-generic/include/odp/api/schedule_types.h28
-rw-r--r--platform/linux-generic/include/odp/api/shared_memory.h36
-rw-r--r--platform/linux-generic/include/odp/api/spinlock.h28
-rw-r--r--platform/linux-generic/include/odp/api/spinlock_recursive.h28
-rw-r--r--platform/linux-generic/include/odp/api/std_clib.h28
-rw-r--r--platform/linux-generic/include/odp/api/std_types.h42
-rw-r--r--platform/linux-generic/include/odp/api/sync.h39
-rw-r--r--platform/linux-generic/include/odp/api/system_info.h29
-rw-r--r--platform/linux-generic/include/odp/api/thread.h28
-rw-r--r--platform/linux-generic/include/odp/api/thrmask.h36
-rw-r--r--platform/linux-generic/include/odp/api/ticketlock.h32
-rw-r--r--platform/linux-generic/include/odp/api/time.h31
-rw-r--r--platform/linux-generic/include/odp/api/timer.h40
-rw-r--r--platform/linux-generic/include/odp/api/traffic_mngr.h35
-rw-r--r--platform/linux-generic/include/odp/api/version.h27
-rw-r--r--platform/linux-generic/include/odp/drv/README2
-rw-r--r--platform/linux-generic/include/odp/drv/compiler.h34
-rw-r--r--platform/linux-generic/include/odp/drv/std_types.h42
-rw-r--r--platform/linux-generic/include/odp/visibility_begin.h15
-rw-r--r--platform/linux-generic/include/odp/visibility_end.h15
-rw-r--r--platform/linux-generic/include/odp_align_internal.h67
-rw-r--r--platform/linux-generic/include/odp_atomic_internal.h518
-rw-r--r--platform/linux-generic/include/odp_bitmap_internal.h317
-rw-r--r--platform/linux-generic/include/odp_bitset.h93
-rw-r--r--platform/linux-generic/include/odp_buffer_inlines.h35
-rw-r--r--platform/linux-generic/include/odp_buffer_internal.h99
-rw-r--r--platform/linux-generic/include/odp_chksum_internal.h202
-rw-r--r--platform/linux-generic/include/odp_classification_datamodel.h218
-rw-r--r--platform/linux-generic/include/odp_classification_inlines.h377
-rw-r--r--platform/linux-generic/include/odp_classification_internal.h226
-rw-r--r--platform/linux-generic/include/odp_config_internal.h142
-rw-r--r--platform/linux-generic/include/odp_crypto_internal.h78
-rw-r--r--platform/linux-generic/include/odp_debug_internal.h82
-rw-r--r--platform/linux-generic/include/odp_errno_define.h8
-rw-r--r--platform/linux-generic/include/odp_ethtool_rss.h63
-rw-r--r--platform/linux-generic/include/odp_ethtool_stats.h29
-rw-r--r--platform/linux-generic/include/odp_event_internal.h98
-rw-r--r--platform/linux-generic/include/odp_event_validation_internal.h50
-rw-r--r--platform/linux-generic/include/odp_event_vector_internal.h79
-rw-r--r--platform/linux-generic/include/odp_fdserver_internal.h37
-rw-r--r--platform/linux-generic/include/odp_forward_typedefs_internal.h11
-rw-r--r--platform/linux-generic/include/odp_global_data.h103
-rw-r--r--platform/linux-generic/include/odp_init_internal.h113
-rw-r--r--platform/linux-generic/include/odp_internal.h141
-rw-r--r--platform/linux-generic/include/odp_ipsec_internal.h411
-rw-r--r--platform/linux-generic/include/odp_ishmphy_internal.h23
-rw-r--r--platform/linux-generic/include/odp_ishmpool_internal.h52
-rw-r--r--platform/linux-generic/include/odp_libconfig_internal.h39
-rw-r--r--platform/linux-generic/include/odp_llqueue.h321
-rw-r--r--platform/linux-generic/include/odp_macros_internal.h101
-rw-r--r--platform/linux-generic/include/odp_ml_fp16.h23
-rw-r--r--platform/linux-generic/include/odp_name_table_internal.h10
-rw-r--r--platform/linux-generic/include/odp_packet_dpdk.h118
-rw-r--r--platform/linux-generic/include/odp_packet_internal.h526
-rw-r--r--platform/linux-generic/include/odp_packet_io_internal.h365
-rw-r--r--platform/linux-generic/include/odp_packet_io_ipc_internal.h48
-rw-r--r--platform/linux-generic/include/odp_packet_io_ring_internal.h589
-rw-r--r--platform/linux-generic/include/odp_packet_io_stats.h40
-rw-r--r--platform/linux-generic/include/odp_packet_io_stats_common.h21
-rw-r--r--platform/linux-generic/include/odp_packet_netmap.h68
-rw-r--r--platform/linux-generic/include/odp_packet_socket.h172
-rw-r--r--platform/linux-generic/include/odp_packet_tap.h21
-rw-r--r--platform/linux-generic/include/odp_parse_internal.h118
-rw-r--r--platform/linux-generic/include/odp_pcapng.h27
-rw-r--r--platform/linux-generic/include/odp_pkt_queue_internal.h15
-rw-r--r--platform/linux-generic/include/odp_pool_internal.h181
-rw-r--r--platform/linux-generic/include/odp_posix_extensions.h6
-rw-r--r--platform/linux-generic/include/odp_queue_basic_internal.h124
-rw-r--r--platform/linux-generic/include/odp_queue_if.h79
-rw-r--r--platform/linux-generic/include/odp_queue_internal.h83
-rw-r--r--platform/linux-generic/include/odp_queue_lf.h35
-rw-r--r--platform/linux-generic/include/odp_queue_scalable_internal.h101
-rw-r--r--platform/linux-generic/include/odp_random_openssl_internal.h21
-rw-r--r--platform/linux-generic/include/odp_random_std_internal.h22
-rw-r--r--platform/linux-generic/include/odp_ring_common.h20
-rw-r--r--platform/linux-generic/include/odp_ring_internal.h271
-rw-r--r--platform/linux-generic/include/odp_ring_mpmc_internal.h348
-rw-r--r--platform/linux-generic/include/odp_ring_mpmc_u32_internal.h23
-rw-r--r--platform/linux-generic/include/odp_ring_mpmc_u64_internal.h23
-rw-r--r--platform/linux-generic/include/odp_ring_ptr_internal.h23
-rw-r--r--platform/linux-generic/include/odp_ring_spsc_internal.h128
-rw-r--r--platform/linux-generic/include/odp_ring_st_internal.h109
-rw-r--r--platform/linux-generic/include/odp_ring_u32_internal.h23
-rw-r--r--platform/linux-generic/include/odp_ring_u64_internal.h23
-rw-r--r--platform/linux-generic/include/odp_schedule_if.h100
-rw-r--r--platform/linux-generic/include/odp_schedule_scalable.h148
-rw-r--r--platform/linux-generic/include/odp_schedule_scalable_config.h52
-rw-r--r--platform/linux-generic/include/odp_schedule_scalable_ordered.h123
-rw-r--r--platform/linux-generic/include/odp_shm_internal.h42
-rw-r--r--platform/linux-generic/include/odp_socket_common.h71
-rw-r--r--platform/linux-generic/include/odp_sorted_list_internal.h9
-rw-r--r--platform/linux-generic/include/odp_string_internal.h34
-rw-r--r--platform/linux-generic/include/odp_sysfs_stats.h29
-rw-r--r--platform/linux-generic/include/odp_sysinfo_internal.h43
-rw-r--r--platform/linux-generic/include/odp_time_internal.h24
-rw-r--r--platform/linux-generic/include/odp_timer_internal.h50
-rw-r--r--platform/linux-generic/include/odp_timer_wheel_internal.h9
-rw-r--r--platform/linux-generic/include/odp_traffic_mngr_internal.h68
-rw-r--r--platform/linux-generic/include/odp_types_internal.h22
-rw-r--r--platform/linux-generic/include/protocols/eth.h12
-rw-r--r--platform/linux-generic/include/protocols/ip.h22
-rw-r--r--platform/linux-generic/include/protocols/ipsec.h18
-rw-r--r--platform/linux-generic/include/protocols/sctp.h49
-rw-r--r--platform/linux-generic/include/protocols/tcp.h9
-rw-r--r--platform/linux-generic/include/protocols/thash.h109
-rw-r--r--platform/linux-generic/include/protocols/udp.h12
-rw-r--r--platform/linux-generic/libodp-linux.pc.in12
-rw-r--r--platform/linux-generic/m4/configure.m4155
-rw-r--r--platform/linux-generic/m4/odp_cpu.m439
-rw-r--r--platform/linux-generic/m4/odp_crypto.m455
-rw-r--r--platform/linux-generic/m4/odp_dpdk.m476
-rw-r--r--platform/linux-generic/m4/odp_event_validation.m427
-rw-r--r--platform/linux-generic/m4/odp_ipsec_mb.m423
-rw-r--r--platform/linux-generic/m4/odp_libconfig.m440
-rw-r--r--platform/linux-generic/m4/odp_ml.m450
-rw-r--r--platform/linux-generic/m4/odp_netmap.m445
-rw-r--r--platform/linux-generic/m4/odp_openssl.m460
-rw-r--r--platform/linux-generic/m4/odp_pcap.m420
-rw-r--r--platform/linux-generic/m4/odp_pcapng.m424
-rw-r--r--platform/linux-generic/m4/odp_pthread.m48
-rw-r--r--platform/linux-generic/m4/odp_schedule.m413
-rw-r--r--platform/linux-generic/m4/odp_scheduler.m415
-rw-r--r--platform/linux-generic/m4/odp_timer.m48
-rw-r--r--platform/linux-generic/m4/odp_wfe.m418
-rw-r--r--platform/linux-generic/m4/odp_xdp.m419
-rw-r--r--platform/linux-generic/miniz/miniz.c619
-rw-r--r--platform/linux-generic/miniz/miniz.h363
-rw-r--r--platform/linux-generic/miniz/miniz_common.h68
-rw-r--r--platform/linux-generic/miniz/miniz_tdef.c1564
-rw-r--r--platform/linux-generic/miniz/miniz_tdef.h183
-rw-r--r--platform/linux-generic/miniz/miniz_tinfl.c725
-rw-r--r--platform/linux-generic/miniz/miniz_tinfl.h146
-rw-r--r--platform/linux-generic/odp_atomic.c29
-rw-r--r--platform/linux-generic/odp_atomic_api.c9
-rw-r--r--platform/linux-generic/odp_barrier.c13
-rw-r--r--platform/linux-generic/odp_bitmap.c315
-rw-r--r--platform/linux-generic/odp_buffer.c90
-rw-r--r--platform/linux-generic/odp_buffer_api.c9
-rw-r--r--platform/linux-generic/odp_byteorder.c10
-rw-r--r--platform/linux-generic/odp_byteorder_api.c9
-rw-r--r--platform/linux-generic/odp_chksum.c12
-rw-r--r--platform/linux-generic/odp_classification.c1861
-rw-r--r--platform/linux-generic/odp_comp.c678
-rw-r--r--platform/linux-generic/odp_cpu.c16
-rw-r--r--platform/linux-generic/odp_cpu_api.c9
-rw-r--r--platform/linux-generic/odp_cpumask.c66
-rw-r--r--platform/linux-generic/odp_cpumask_task.c119
-rw-r--r--platform/linux-generic/odp_crypto.c1131
-rw-r--r--platform/linux-generic/odp_crypto_api.c9
-rw-r--r--platform/linux-generic/odp_crypto_ipsecmb.c893
-rw-r--r--platform/linux-generic/odp_crypto_null.c508
-rw-r--r--platform/linux-generic/odp_crypto_openssl.c2830
-rw-r--r--platform/linux-generic/odp_dma.c861
-rw-r--r--platform/linux-generic/odp_dma_api.c9
-rw-r--r--platform/linux-generic/odp_errno.c19
-rw-r--r--platform/linux-generic/odp_event.c112
-rw-r--r--platform/linux-generic/odp_event_api.c9
-rw-r--r--platform/linux-generic/odp_event_validation.c258
-rw-r--r--platform/linux-generic/odp_fdserver.c719
-rw-r--r--platform/linux-generic/odp_hash.c489
-rw-r--r--platform/linux-generic/odp_hash_api.c9
-rw-r--r--platform/linux-generic/odp_hash_crc_gen.c246
-rw-r--r--platform/linux-generic/odp_impl.c36
-rw-r--r--platform/linux-generic/odp_init.c738
-rw-r--r--platform/linux-generic/odp_ipsec.c2723
-rw-r--r--platform/linux-generic/odp_ipsec_api.c9
-rw-r--r--platform/linux-generic/odp_ipsec_events.c173
-rw-r--r--platform/linux-generic/odp_ipsec_sad.c1305
-rw-r--r--platform/linux-generic/odp_ishm.c2241
-rw-r--r--platform/linux-generic/odp_ishmphy.c143
-rw-r--r--platform/linux-generic/odp_ishmpool.c657
-rw-r--r--platform/linux-generic/odp_libconfig.c342
-rw-r--r--platform/linux-generic/odp_ml.c2643
-rw-r--r--platform/linux-generic/odp_ml_fp16.c425
-rw-r--r--platform/linux-generic/odp_ml_null.c232
-rw-r--r--platform/linux-generic/odp_ml_quantize.c79
-rw-r--r--platform/linux-generic/odp_name_table.c116
-rw-r--r--platform/linux-generic/odp_packet.c2982
-rw-r--r--platform/linux-generic/odp_packet_api.c13
-rw-r--r--platform/linux-generic/odp_packet_flags.c170
-rw-r--r--platform/linux-generic/odp_packet_flags_api.c10
-rw-r--r--platform/linux-generic/odp_packet_io.c2798
-rw-r--r--platform/linux-generic/odp_packet_io_api.c9
-rw-r--r--platform/linux-generic/odp_packet_vector.c140
-rw-r--r--platform/linux-generic/odp_parse.c475
-rw-r--r--platform/linux-generic/odp_pcapng.c604
-rw-r--r--platform/linux-generic/odp_pkt_queue.c112
-rw-r--r--platform/linux-generic/odp_pool.c2021
-rw-r--r--platform/linux-generic/odp_pool_api.c9
-rw-r--r--platform/linux-generic/odp_pool_mem_src_ops.c20
-rw-r--r--platform/linux-generic/odp_queue.c785
-rw-r--r--platform/linux-generic/odp_queue_api.c9
-rw-r--r--platform/linux-generic/odp_queue_basic.c1298
-rw-r--r--platform/linux-generic/odp_queue_if.c123
-rw-r--r--platform/linux-generic/odp_queue_lf.c368
-rw-r--r--platform/linux-generic/odp_queue_scalable.c1199
-rw-r--r--platform/linux-generic/odp_queue_spsc.c134
-rw-r--r--platform/linux-generic/odp_random.c64
-rw-r--r--platform/linux-generic/odp_random_openssl.c39
-rw-r--r--platform/linux-generic/odp_random_std.c109
-rw-r--r--platform/linux-generic/odp_rwlock.c79
-rw-r--r--platform/linux-generic/odp_rwlock_api.c8
-rw-r--r--platform/linux-generic/odp_rwlock_recursive.c107
-rw-r--r--platform/linux-generic/odp_rwlock_recursive_api.c8
-rw-r--r--platform/linux-generic/odp_schedule.c1429
-rw-r--r--platform/linux-generic/odp_schedule_api.c9
-rw-r--r--platform/linux-generic/odp_schedule_basic.c2407
-rw-r--r--platform/linux-generic/odp_schedule_if.c151
-rw-r--r--platform/linux-generic/odp_schedule_iquery.c1552
-rw-r--r--platform/linux-generic/odp_schedule_scalable.c2207
-rw-r--r--platform/linux-generic/odp_schedule_scalable_ordered.c367
-rw-r--r--platform/linux-generic/odp_schedule_sp.c522
-rw-r--r--platform/linux-generic/odp_shared_memory.c64
-rw-r--r--platform/linux-generic/odp_sorted_list.c24
-rw-r--r--platform/linux-generic/odp_spinlock.c40
-rw-r--r--platform/linux-generic/odp_spinlock_api.c8
-rw-r--r--platform/linux-generic/odp_spinlock_recursive.c70
-rw-r--r--platform/linux-generic/odp_spinlock_recursive_api.c8
-rw-r--r--platform/linux-generic/odp_stash.c930
-rw-r--r--platform/linux-generic/odp_std.c17
-rw-r--r--platform/linux-generic/odp_std_api.c9
-rw-r--r--platform/linux-generic/odp_std_clib.c10
-rw-r--r--platform/linux-generic/odp_string.c51
-rw-r--r--platform/linux-generic/odp_sync.c10
-rw-r--r--platform/linux-generic/odp_sync_api.c9
-rw-r--r--platform/linux-generic/odp_system_info.c382
-rw-r--r--platform/linux-generic/odp_thread.c194
-rw-r--r--platform/linux-generic/odp_thread_api.c10
-rw-r--r--platform/linux-generic/odp_thrmask.c6
-rw-r--r--platform/linux-generic/odp_ticketlock.c19
-rw-r--r--platform/linux-generic/odp_ticketlock_api.c9
-rw-r--r--platform/linux-generic/odp_time.c309
-rw-r--r--platform/linux-generic/odp_time_api.c9
-rw-r--r--platform/linux-generic/odp_timer.c2318
-rw-r--r--platform/linux-generic/odp_timer_api.c9
-rw-r--r--platform/linux-generic/odp_timer_wheel.c74
-rw-r--r--platform/linux-generic/odp_traffic_mngr.c1836
-rw-r--r--platform/linux-generic/odp_version.c6
-rw-r--r--platform/linux-generic/odp_weak.c8
-rw-r--r--platform/linux-generic/pktio/dpdk.c2260
-rw-r--r--platform/linux-generic/pktio/ethtool.c165
-rw-r--r--platform/linux-generic/pktio/ethtool_rss.c251
-rw-r--r--platform/linux-generic/pktio/io_ops.c34
-rw-r--r--platform/linux-generic/pktio/ipc.c764
-rw-r--r--platform/linux-generic/pktio/loop.c790
-rw-r--r--platform/linux-generic/pktio/netmap.c973
-rw-r--r--platform/linux-generic/pktio/null.c214
-rw-r--r--platform/linux-generic/pktio/pcap.c380
-rw-r--r--platform/linux-generic/pktio/pktio_common.c177
-rw-r--r--platform/linux-generic/pktio/ring.c658
-rw-r--r--platform/linux-generic/pktio/socket.c947
-rw-r--r--platform/linux-generic/pktio/socket_common.c295
-rw-r--r--platform/linux-generic/pktio/socket_mmap.c949
-rw-r--r--platform/linux-generic/pktio/socket_xdp.c1247
-rw-r--r--platform/linux-generic/pktio/stats/ethtool_stats.c280
-rw-r--r--platform/linux-generic/pktio/stats/packet_io_stats.c190
-rw-r--r--platform/linux-generic/pktio/stats/sysfs_stats.c202
-rw-r--r--platform/linux-generic/pktio/sysfs.c77
-rw-r--r--platform/linux-generic/pktio/tap.c390
-rw-r--r--platform/linux-generic/test/.gitignore3
-rw-r--r--platform/linux-generic/test/Makefile.am58
-rw-r--r--platform/linux-generic/test/example/Makefile.am10
-rw-r--r--platform/linux-generic/test/example/classifier/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/classifier/pktio_env42
-rw-r--r--platform/linux-generic/test/example/ipsec_api/Makefile.am21
-rw-r--r--platform/linux-generic/test/example/ipsec_api/pktio_env75
-rw-r--r--platform/linux-generic/test/example/ipsec_crypto/Makefile.am21
-rw-r--r--platform/linux-generic/test/example/ipsec_crypto/pktio_env75
-rw-r--r--platform/linux-generic/test/example/l2fwd_simple/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/l2fwd_simple/pktio_env45
-rw-r--r--platform/linux-generic/test/example/l3fwd/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/l3fwd/pktio_env49
-rw-r--r--platform/linux-generic/test/example/packet/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/packet/pktio_env48
-rw-r--r--platform/linux-generic/test/example/ping/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/ping/pktio_env48
-rw-r--r--platform/linux-generic/test/example/simple_pipeline/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/simple_pipeline/pktio_env45
-rw-r--r--platform/linux-generic/test/example/switch/Makefile.am1
-rw-r--r--platform/linux-generic/test/example/switch/pktio_env52
-rw-r--r--platform/linux-generic/test/inline-timer.conf8
-rw-r--r--platform/linux-generic/test/packet_align.conf21
-rw-r--r--platform/linux-generic/test/performance/Makefile.am1
-rw-r--r--platform/linux-generic/test/performance/dmafwd/Makefile.am18
-rw-r--r--platform/linux-generic/test/performance/dmafwd/pktio_env57
-rw-r--r--platform/linux-generic/test/pktio_ipc/.gitignore (renamed from test/linux-generic/pktio_ipc/.gitignore)0
-rw-r--r--platform/linux-generic/test/pktio_ipc/Makefile.am31
-rw-r--r--platform/linux-generic/test/pktio_ipc/ipc_common.c170
-rw-r--r--platform/linux-generic/test/pktio_ipc/ipc_common.h99
-rw-r--r--platform/linux-generic/test/pktio_ipc/pktio_ipc1.c381
-rw-r--r--platform/linux-generic/test/pktio_ipc/pktio_ipc2.c268
-rwxr-xr-xplatform/linux-generic/test/pktio_ipc/pktio_ipc_run.sh85
-rw-r--r--platform/linux-generic/test/process-mode.conf9
-rw-r--r--platform/linux-generic/test/sched-basic.conf13
-rw-r--r--platform/linux-generic/test/stash-custom.conf8
-rw-r--r--platform/linux-generic/test/validation/api/Makefile.inc1
-rw-r--r--platform/linux-generic/test/validation/api/ml/.gitignore1
-rw-r--r--platform/linux-generic/test/validation/api/ml/Makefile.am34
-rw-r--r--platform/linux-generic/test/validation/api/ml/README.md23
-rw-r--r--platform/linux-generic/test/validation/api/ml/batch_add.onnxbin0 -> 144 bytes
-rw-r--r--platform/linux-generic/test/validation/api/ml/batch_add_gen.py32
-rwxr-xr-xplatform/linux-generic/test/validation/api/ml/gen_models.sh14
-rw-r--r--platform/linux-generic/test/validation/api/ml/ml_linux.c1167
-rw-r--r--platform/linux-generic/test/validation/api/ml/requirements.txt2
-rw-r--r--platform/linux-generic/test/validation/api/ml/simple_linear.onnxbin0 -> 214 bytes
-rw-r--r--platform/linux-generic/test/validation/api/ml/simple_linear_gen.py34
-rw-r--r--platform/linux-generic/test/validation/api/pktio/.gitignore (renamed from test/common_plat/validation/api/.gitignore)0
-rw-r--r--platform/linux-generic/test/validation/api/pktio/Makefile.am32
-rw-r--r--platform/linux-generic/test/validation/api/pktio/pktio_env118
-rwxr-xr-xplatform/linux-generic/test/validation/api/pktio/pktio_run.sh115
-rwxr-xr-xplatform/linux-generic/test/validation/api/pktio/pktio_run_dpdk.sh93
-rwxr-xr-xplatform/linux-generic/test/validation/api/pktio/pktio_run_pcap.sh37
-rwxr-xr-xplatform/linux-generic/test/validation/api/pktio/pktio_run_tap.sh117
-rw-r--r--platform/linux-generic/test/validation/api/shmem/.gitignore (renamed from test/linux-generic/validation/api/shmem/.gitignore)0
-rw-r--r--platform/linux-generic/test/validation/api/shmem/Makefile.am14
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_common.h22
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_linux.c331
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_linux.h7
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_odp1.c89
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_odp1.h5
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_odp2.c103
-rw-r--r--platform/linux-generic/test/validation/api/shmem/shmem_odp2.h5
-rwxr-xr-xscripts/build-pktio-dpdk33
-rwxr-xr-xscripts/check-globals.sh37
-rwxr-xr-xscripts/checkpatch.pl3549
-rwxr-xr-xscripts/ci-checkpatches.sh22
-rwxr-xr-xscripts/ci/build.sh18
-rwxr-xr-xscripts/ci/build_arm64.sh38
-rwxr-xr-xscripts/ci/build_armhf.sh22
-rwxr-xr-xscripts/ci/build_i386.sh19
-rwxr-xr-xscripts/ci/build_ppc64el.sh24
-rwxr-xr-xscripts/ci/build_riscv64.sh17
-rwxr-xr-xscripts/ci/build_static.sh17
-rwxr-xr-xscripts/ci/build_x86_64.sh11
-rwxr-xr-xscripts/ci/check.sh17
-rwxr-xr-xscripts/ci/check_inline_timer.sh16
-rwxr-xr-xscripts/ci/check_pktio.sh14
-rwxr-xr-xscripts/ci/coverage.sh35
-rwxr-xr-xscripts/ci/coverity.sh19
-rwxr-xr-xscripts/ci/distcheck.sh19
-rwxr-xr-xscripts/ci/out_of_tree.sh13
-rw-r--r--scripts/spelling.txt553
-rw-r--r--test/Makefile.am1
-rw-r--r--test/Makefile.inc43
-rw-r--r--test/README13
-rw-r--r--test/common/Makefile.am28
-rw-r--r--test/common/mask_common.c473
-rw-r--r--test/common/mask_common.h59
-rw-r--r--test/common/odp_cunit_common.c751
-rw-r--r--test/common/odp_cunit_common.h184
-rw-r--r--test/common/packet_common.c131
-rw-r--r--test/common/packet_common.h65
-rw-r--r--test/common/test_common_macros.h15
-rw-r--r--test/common/test_packet_custom.h122
-rw-r--r--test/common/test_packet_ipsec.h186
-rw-r--r--test/common/test_packet_ipv4.h457
-rw-r--r--test/common/test_packet_ipv4_with_crc.h232
-rw-r--r--test/common/test_packet_ipv6.h121
-rw-r--r--test/common_plat/Makefile.am7
-rw-r--r--test/common_plat/common/Makefile.am14
-rw-r--r--test/common_plat/common/mask_common.c475
-rw-r--r--test/common_plat/common/mask_common.h61
-rw-r--r--test/common_plat/common/odp_cunit_common.c373
-rw-r--r--test/common_plat/common/odp_cunit_common.h106
-rw-r--r--test/common_plat/m4/configure.m433
-rw-r--r--test/common_plat/m4/miscellaneous.m49
-rw-r--r--test/common_plat/m4/performance.m49
-rw-r--r--test/common_plat/m4/validation.m461
-rw-r--r--test/common_plat/miscellaneous/.gitignore3
-rw-r--r--test/common_plat/miscellaneous/Makefile.am12
-rw-r--r--test/common_plat/miscellaneous/odp_api_from_cpp.cpp11
-rw-r--r--test/common_plat/performance/.gitignore10
-rw-r--r--test/common_plat/performance/Makefile.am55
-rw-r--r--test/common_plat/performance/dummy_crc.h493
-rw-r--r--test/common_plat/performance/odp_bench_packet.c1697
-rw-r--r--test/common_plat/performance/odp_crypto.c982
-rw-r--r--test/common_plat/performance/odp_l2fwd.c1689
-rwxr-xr-xtest/common_plat/performance/odp_l2fwd_run.sh119
-rw-r--r--test/common_plat/performance/odp_pktio_ordered.c1347
-rwxr-xr-xtest/common_plat/performance/odp_pktio_ordered_run.sh58
-rw-r--r--test/common_plat/performance/odp_pktio_perf.c1089
-rw-r--r--test/common_plat/performance/odp_sched_latency.c792
-rwxr-xr-xtest/common_plat/performance/odp_sched_latency_run.sh28
-rw-r--r--test/common_plat/performance/odp_scheduling.c987
-rwxr-xr-xtest/common_plat/performance/odp_scheduling_run.sh29
-rw-r--r--test/common_plat/validation/Makefile.am3
-rw-r--r--test/common_plat/validation/api/Makefile.am28
-rw-r--r--test/common_plat/validation/api/Makefile.inc19
-rw-r--r--test/common_plat/validation/api/README35
-rw-r--r--test/common_plat/validation/api/atomic/Makefile.am10
-rw-r--r--test/common_plat/validation/api/atomic/atomic.c909
-rw-r--r--test/common_plat/validation/api/atomic/atomic.h39
-rw-r--r--test/common_plat/validation/api/atomic/atomic_main.c12
-rw-r--r--test/common_plat/validation/api/barrier/Makefile.am10
-rw-r--r--test/common_plat/validation/api/barrier/barrier.c421
-rw-r--r--test/common_plat/validation/api/barrier/barrier.h30
-rw-r--r--test/common_plat/validation/api/barrier/barrier_main.c12
-rw-r--r--test/common_plat/validation/api/buffer/Makefile.am10
-rw-r--r--test/common_plat/validation/api/buffer/buffer.c287
-rw-r--r--test/common_plat/validation/api/buffer/buffer.h32
-rw-r--r--test/common_plat/validation/api/buffer/buffer_main.c11
-rw-r--r--test/common_plat/validation/api/classification/Makefile.am14
-rw-r--r--test/common_plat/validation/api/classification/classification.c43
-rw-r--r--test/common_plat/validation/api/classification/classification.h121
-rw-r--r--test/common_plat/validation/api/classification/classification_main.c12
-rw-r--r--test/common_plat/validation/api/classification/odp_classification_basic.c330
-rw-r--r--test/common_plat/validation/api/classification/odp_classification_common.c427
-rw-r--r--test/common_plat/validation/api/classification/odp_classification_test_pmr.c1961
-rw-r--r--test/common_plat/validation/api/classification/odp_classification_tests.c725
-rw-r--r--test/common_plat/validation/api/classification/odp_classification_testsuites.h63
-rw-r--r--test/common_plat/validation/api/cpumask/Makefile.am11
-rw-r--r--test/common_plat/validation/api/cpumask/cpumask.c116
-rw-r--r--test/common_plat/validation/api/cpumask/cpumask.h28
-rw-r--r--test/common_plat/validation/api/cpumask/cpumask_main.c11
-rw-r--r--test/common_plat/validation/api/crypto/Makefile.am11
-rw-r--r--test/common_plat/validation/api/crypto/crypto.c129
-rw-r--r--test/common_plat/validation/api/crypto/crypto.h53
-rw-r--r--test/common_plat/validation/api/crypto/crypto_main.c12
-rw-r--r--test/common_plat/validation/api/crypto/odp_crypto_test_inp.c1589
-rw-r--r--test/common_plat/validation/api/crypto/odp_crypto_test_inp.h22
-rw-r--r--test/common_plat/validation/api/crypto/test_vectors.h450
-rw-r--r--test/common_plat/validation/api/crypto/test_vectors_len.h50
-rw-r--r--test/common_plat/validation/api/errno/Makefile.am10
-rw-r--r--test/common_plat/validation/api/errno/errno.c46
-rw-r--r--test/common_plat/validation/api/errno/errno.h24
-rw-r--r--test/common_plat/validation/api/errno/errno_main.c12
-rw-r--r--test/common_plat/validation/api/hash/Makefile.am10
-rw-r--r--test/common_plat/validation/api/hash/hash.c54
-rw-r--r--test/common_plat/validation/api/hash/hash.h24
-rw-r--r--test/common_plat/validation/api/hash/hash_main.c12
-rw-r--r--test/common_plat/validation/api/init/.gitignore3
-rw-r--r--test/common_plat/validation/api/init/Makefile.am16
-rw-r--r--test/common_plat/validation/api/init/init.c188
-rw-r--r--test/common_plat/validation/api/init/init.h32
-rw-r--r--test/common_plat/validation/api/init/init_main_abort.c11
-rw-r--r--test/common_plat/validation/api/init/init_main_log.c11
-rw-r--r--test/common_plat/validation/api/init/init_main_ok.c11
-rw-r--r--test/common_plat/validation/api/lock/Makefile.am10
-rw-r--r--test/common_plat/validation/api/lock/lock.c1265
-rw-r--r--test/common_plat/validation/api/lock/lock.h46
-rw-r--r--test/common_plat/validation/api/lock/lock_main.c12
-rw-r--r--test/common_plat/validation/api/packet/Makefile.am10
-rw-r--r--test/common_plat/validation/api/packet/packet.c2451
-rw-r--r--test/common_plat/validation/api/packet/packet.h56
-rw-r--r--test/common_plat/validation/api/packet/packet_main.c12
-rw-r--r--test/common_plat/validation/api/pktio/Makefile.am10
-rw-r--r--test/common_plat/validation/api/pktio/parser.c545
-rw-r--r--test/common_plat/validation/api/pktio/parser.h180
-rw-r--r--test/common_plat/validation/api/pktio/pktio.c2213
-rw-r--r--test/common_plat/validation/api/pktio/pktio.h67
-rw-r--r--test/common_plat/validation/api/pktio/pktio_main.c12
-rw-r--r--test/common_plat/validation/api/pool/Makefile.am10
-rw-r--r--test/common_plat/validation/api/pool/pool.c126
-rw-r--r--test/common_plat/validation/api/pool/pool.h28
-rw-r--r--test/common_plat/validation/api/pool/pool_main.c12
-rw-r--r--test/common_plat/validation/api/queue/Makefile.am10
-rw-r--r--test/common_plat/validation/api/queue/queue.c350
-rw-r--r--test/common_plat/validation/api/queue/queue.h31
-rw-r--r--test/common_plat/validation/api/queue/queue_main.c12
-rw-r--r--test/common_plat/validation/api/random/Makefile.am10
-rw-r--r--test/common_plat/validation/api/random/random.c90
-rw-r--r--test/common_plat/validation/api/random/random.h26
-rw-r--r--test/common_plat/validation/api/random/random_main.c12
-rw-r--r--test/common_plat/validation/api/scheduler/.gitignore1
-rw-r--r--test/common_plat/validation/api/scheduler/Makefile.am10
-rw-r--r--test/common_plat/validation/api/scheduler/scheduler.c1673
-rw-r--r--test/common_plat/validation/api/scheduler/scheduler.h62
-rw-r--r--test/common_plat/validation/api/scheduler/scheduler_main.c12
-rw-r--r--test/common_plat/validation/api/shmem/Makefile.am10
-rw-r--r--test/common_plat/validation/api/shmem/shmem.c738
-rw-r--r--test/common_plat/validation/api/shmem/shmem.h27
-rw-r--r--test/common_plat/validation/api/shmem/shmem_main.c12
-rw-r--r--test/common_plat/validation/api/std_clib/.gitignore1
-rw-r--r--test/common_plat/validation/api/std_clib/Makefile.am10
-rw-r--r--test/common_plat/validation/api/std_clib/std_clib.c110
-rw-r--r--test/common_plat/validation/api/std_clib/std_clib.h21
-rw-r--r--test/common_plat/validation/api/std_clib/std_clib_main.c12
-rw-r--r--test/common_plat/validation/api/system/Makefile.am10
-rw-r--r--test/common_plat/validation/api/system/system.c352
-rw-r--r--test/common_plat/validation/api/system/system.h44
-rw-r--r--test/common_plat/validation/api/system/system_main.c12
-rw-r--r--test/common_plat/validation/api/thread/Makefile.am12
-rw-r--r--test/common_plat/validation/api/thread/thread.c140
-rw-r--r--test/common_plat/validation/api/thread/thread.h33
-rw-r--r--test/common_plat/validation/api/thread/thread_main.c12
-rw-r--r--test/common_plat/validation/api/time/Makefile.am19
-rw-r--r--test/common_plat/validation/api/time/time.c481
-rwxr-xr-xtest/common_plat/validation/api/time/time.sh42
-rw-r--r--test/common_plat/validation/api/time/time_main.c12
-rw-r--r--test/common_plat/validation/api/time/time_test.h38
-rw-r--r--test/common_plat/validation/api/timer/Makefile.am10
-rw-r--r--test/common_plat/validation/api/timer/timer.c605
-rw-r--r--test/common_plat/validation/api/timer/timer.h27
-rw-r--r--test/common_plat/validation/api/timer/timer_main.c12
-rw-r--r--test/common_plat/validation/api/traffic_mngr/Makefile.am19
-rw-r--r--test/common_plat/validation/api/traffic_mngr/traffic_mngr.c4017
-rw-r--r--test/common_plat/validation/api/traffic_mngr/traffic_mngr.h45
-rwxr-xr-xtest/common_plat/validation/api/traffic_mngr/traffic_mngr.sh41
-rw-r--r--test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c12
-rw-r--r--test/linux-generic/Makefile.am80
-rw-r--r--test/linux-generic/Makefile.inc22
-rw-r--r--test/linux-generic/m4/configure.m49
-rw-r--r--test/linux-generic/m4/performance.m49
-rw-r--r--test/linux-generic/mmap_vlan_ins/.gitignore2
-rw-r--r--test/linux-generic/mmap_vlan_ins/Makefile.am15
-rw-r--r--test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.c226
-rwxr-xr-xtest/linux-generic/mmap_vlan_ins/mmap_vlan_ins.sh75
-rw-r--r--test/linux-generic/mmap_vlan_ins/pktio_env120
-rw-r--r--test/linux-generic/mmap_vlan_ins/vlan.pcapbin9728 -> 0 bytes
-rw-r--r--test/linux-generic/performance/.gitignore2
-rw-r--r--test/linux-generic/performance/Makefile.am13
-rwxr-xr-xtest/linux-generic/performance/odp_scheduling_run_proc.sh30
-rw-r--r--test/linux-generic/pktio_ipc/Makefile.am20
-rw-r--r--test/linux-generic/pktio_ipc/ipc_common.c174
-rw-r--r--test/linux-generic/pktio_ipc/ipc_common.h100
-rw-r--r--test/linux-generic/pktio_ipc/pktio_ipc1.c371
-rw-r--r--test/linux-generic/pktio_ipc/pktio_ipc2.c261
-rwxr-xr-xtest/linux-generic/pktio_ipc/pktio_ipc_run.sh95
-rw-r--r--test/linux-generic/ring/.gitignore1
-rw-r--r--test/linux-generic/ring/Makefile.am14
-rw-r--r--test/linux-generic/ring/ring_basic.c361
-rw-r--r--test/linux-generic/ring/ring_main.c12
-rw-r--r--test/linux-generic/ring/ring_stress.c244
-rw-r--r--test/linux-generic/ring/ring_suites.c74
-rw-r--r--test/linux-generic/ring/ring_suites.h34
-rwxr-xr-xtest/linux-generic/run-test67
-rw-r--r--test/linux-generic/validation/Makefile.inc1
-rw-r--r--test/linux-generic/validation/api/Makefile.inc1
-rw-r--r--test/linux-generic/validation/api/pktio/.gitignore2
-rw-r--r--test/linux-generic/validation/api/pktio/Makefile.am15
-rw-r--r--test/linux-generic/validation/api/pktio/pktio_env120
-rwxr-xr-xtest/linux-generic/validation/api/pktio/pktio_run.sh126
-rwxr-xr-xtest/linux-generic/validation/api/pktio/pktio_run_dpdk.sh95
-rwxr-xr-xtest/linux-generic/validation/api/pktio/pktio_run_netmap.sh123
-rwxr-xr-xtest/linux-generic/validation/api/pktio/pktio_run_pcap.sh36
-rwxr-xr-xtest/linux-generic/validation/api/pktio/pktio_run_tap.sh119
-rw-r--r--test/linux-generic/validation/api/shmem/Makefile.am28
-rw-r--r--test/linux-generic/validation/api/shmem/shmem.h21
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_common.h24
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_linux.c319
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_linux.h9
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_odp1.c85
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_odp1.h7
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_odp2.c104
-rw-r--r--test/linux-generic/validation/api/shmem/shmem_odp2.h7
-rw-r--r--test/m4/configure.m454
-rw-r--r--test/m4/miscellaneous.m423
-rw-r--r--test/m4/performance.m49
-rw-r--r--test/m4/validation.m434
-rw-r--r--test/miscellaneous/.gitignore5
-rw-r--r--test/miscellaneous/Makefile.am78
-rw-r--r--test/miscellaneous/odp_api_from_cpp.cpp26
-rw-r--r--test/miscellaneous/odp_api_headers.c36
-rw-r--r--test/miscellaneous/odp_dyn_workers.c1357
-rwxr-xr-xtest/miscellaneous/odp_dyn_workers_run.sh20
-rw-r--r--test/performance/.gitignore33
-rw-r--r--test/performance/Makefile.am120
-rw-r--r--test/performance/bench_common.c247
-rw-r--r--test/performance/bench_common.h239
-rw-r--r--test/performance/dummy_crc.h463
-rw-r--r--test/performance/odp_atomic_perf.c1403
-rw-r--r--test/performance/odp_bench_buffer.c894
-rw-r--r--test/performance/odp_bench_misc.c1061
-rw-r--r--test/performance/odp_bench_packet.c1777
-rw-r--r--test/performance/odp_bench_pktio_sp.c1140
-rw-r--r--test/performance/odp_bench_timer.c740
-rw-r--r--test/performance/odp_cpu_bench.c835
-rwxr-xr-xtest/performance/odp_cpu_bench_run.sh18
-rw-r--r--test/performance/odp_crc.c305
-rw-r--r--test/performance/odp_crypto.c1524
-rwxr-xr-xtest/performance/odp_crypto_run.sh18
-rw-r--r--test/performance/odp_dma_perf.c1951
-rwxr-xr-xtest/performance/odp_dma_perf_run.sh73
-rw-r--r--test/performance/odp_dmafwd.c1475
-rwxr-xr-xtest/performance/odp_dmafwd_run.sh73
-rw-r--r--test/performance/odp_ipsec.c1420
-rwxr-xr-xtest/performance/odp_ipsec_run.sh18
-rw-r--r--test/performance/odp_ipsecfwd.c2074
-rw-r--r--test/performance/odp_ipsecfwd.conf41
-rw-r--r--test/performance/odp_l2fwd.c3113
-rwxr-xr-xtest/performance/odp_l2fwd_run.sh107
-rw-r--r--test/performance/odp_lock_perf.c696
-rw-r--r--test/performance/odp_mem_perf.c484
-rw-r--r--test/performance/odp_packet_gen.c2319
-rwxr-xr-xtest/performance/odp_packet_gen_run.sh83
-rw-r--r--test/performance/odp_pktio_ordered.c1374
-rwxr-xr-xtest/performance/odp_pktio_ordered_run.sh48
-rw-r--r--test/performance/odp_pktio_perf.c1125
-rw-r--r--test/performance/odp_pool_latency.c1407
-rw-r--r--test/performance/odp_pool_perf.c747
-rw-r--r--test/performance/odp_queue_perf.c649
-rw-r--r--test/performance/odp_random.c549
-rw-r--r--test/performance/odp_sched_latency.c1070
-rwxr-xr-xtest/performance/odp_sched_latency_run.sh30
-rw-r--r--test/performance/odp_sched_perf.c1516
-rwxr-xr-xtest/performance/odp_sched_perf_run.sh45
-rw-r--r--test/performance/odp_sched_pktio.c1598
-rwxr-xr-xtest/performance/odp_sched_pktio_run.sh97
-rw-r--r--test/performance/odp_stash_perf.c523
-rw-r--r--test/performance/odp_stress.c874
-rw-r--r--test/performance/odp_timer_accuracy.c1435
-rwxr-xr-xtest/performance/odp_timer_accuracy_run.sh17
-rw-r--r--test/performance/odp_timer_perf.c1400
-rwxr-xr-xtest/performance/odp_timer_perf_run.sh31
-rw-r--r--test/performance/udp64.pcapbin0 -> 7624 bytes
-rw-r--r--test/test_debug.h93
-rw-r--r--test/validation/Makefile.am3
-rw-r--r--test/validation/api/.gitignore (renamed from test/linux-generic/.gitignore)0
-rw-r--r--test/validation/api/Makefile.am100
-rw-r--r--test/validation/api/Makefile.inc3
-rw-r--r--test/validation/api/README32
-rw-r--r--test/validation/api/align/.gitignore1
-rw-r--r--test/validation/api/align/Makefile.am4
-rw-r--r--test/validation/api/align/align.c156
-rw-r--r--test/validation/api/atomic/.gitignore (renamed from test/common_plat/validation/api/atomic/.gitignore)0
-rw-r--r--test/validation/api/atomic/Makefile.am4
-rw-r--r--test/validation/api/atomic/atomic.c1715
-rw-r--r--test/validation/api/barrier/.gitignore (renamed from test/common_plat/validation/api/barrier/.gitignore)0
-rw-r--r--test/validation/api/barrier/Makefile.am4
-rw-r--r--test/validation/api/barrier/barrier.c457
-rw-r--r--test/validation/api/buffer/.gitignore (renamed from test/common_plat/validation/api/buffer/.gitignore)0
-rw-r--r--test/validation/api/buffer/Makefile.am4
-rw-r--r--test/validation/api/buffer/buffer.c608
-rw-r--r--test/validation/api/byteorder/.gitignore1
-rw-r--r--test/validation/api/byteorder/Makefile.am4
-rw-r--r--test/validation/api/byteorder/byteorder.c107
-rw-r--r--test/validation/api/chksum/.gitignore1
-rw-r--r--test/validation/api/chksum/Makefile.am4
-rw-r--r--test/validation/api/chksum/chksum.c452
-rw-r--r--test/validation/api/classification/.gitignore (renamed from test/common_plat/validation/api/classification/.gitignore)0
-rw-r--r--test/validation/api/classification/Makefile.am11
-rw-r--r--test/validation/api/classification/classification.c46
-rw-r--r--test/validation/api/classification/classification.h83
-rw-r--r--test/validation/api/classification/odp_classification_basic.c751
-rw-r--r--test/validation/api/classification/odp_classification_common.c595
-rw-r--r--test/validation/api/classification/odp_classification_test_pmr.c2184
-rw-r--r--test/validation/api/classification/odp_classification_tests.c978
-rw-r--r--test/validation/api/classification/odp_classification_testsuites.h92
-rw-r--r--test/validation/api/comp/.gitignore1
-rw-r--r--test/validation/api/comp/Makefile.am7
-rw-r--r--test/validation/api/comp/comp.c571
-rw-r--r--test/validation/api/comp/test_vectors.h1995
-rw-r--r--test/validation/api/cpumask/.gitignore (renamed from test/common_plat/validation/api/cpumask/.gitignore)0
-rw-r--r--test/validation/api/cpumask/Makefile.am5
-rw-r--r--test/validation/api/cpumask/cpumask.c198
-rw-r--r--test/validation/api/crypto/.gitignore (renamed from test/common_plat/validation/api/crypto/.gitignore)0
-rw-r--r--test/validation/api/crypto/Makefile.am14
-rw-r--r--test/validation/api/crypto/crypto_op_test.c616
-rw-r--r--test/validation/api/crypto/crypto_op_test.h46
-rw-r--r--test/validation/api/crypto/odp_crypto_test_inp.c2412
-rw-r--r--test/validation/api/crypto/test_vector_defs.h3165
-rw-r--r--test/validation/api/crypto/test_vectors.h70
-rw-r--r--test/validation/api/crypto/test_vectors_len.h149
-rw-r--r--test/validation/api/crypto/util.c308
-rw-r--r--test/validation/api/crypto/util.h47
-rw-r--r--test/validation/api/dma/.gitignore1
-rw-r--r--test/validation/api/dma/Makefile.am4
-rw-r--r--test/validation/api/dma/dma.c1703
-rw-r--r--test/validation/api/errno/.gitignore (renamed from test/common_plat/validation/api/errno/.gitignore)0
-rw-r--r--test/validation/api/errno/Makefile.am4
-rw-r--r--test/validation/api/errno/errno.c43
-rw-r--r--test/validation/api/event/.gitignore1
-rw-r--r--test/validation/api/event/Makefile.am4
-rw-r--r--test/validation/api/event/event.c471
-rw-r--r--test/validation/api/hash/.gitignore (renamed from test/common_plat/validation/api/hash/.gitignore)0
-rw-r--r--test/validation/api/hash/Makefile.am4
-rw-r--r--test/validation/api/hash/hash.c763
-rw-r--r--test/validation/api/hints/.gitignore1
-rw-r--r--test/validation/api/hints/Makefile.am4
-rw-r--r--test/validation/api/hints/hints.c92
-rw-r--r--test/validation/api/init/.gitignore1
-rw-r--r--test/validation/api/init/Makefile.am15
-rwxr-xr-xtest/validation/api/init/init_abort.sh3
-rwxr-xr-xtest/validation/api/init/init_defaults.sh3
-rwxr-xr-xtest/validation/api/init/init_feature_disabled.sh3
-rwxr-xr-xtest/validation/api/init/init_feature_enabled.sh3
-rwxr-xr-xtest/validation/api/init/init_log.sh3
-rwxr-xr-xtest/validation/api/init/init_log_thread.sh3
-rw-r--r--test/validation/api/init/init_main.c325
-rwxr-xr-xtest/validation/api/init/init_num_thr.sh3
-rwxr-xr-xtest/validation/api/init/init_test_param_init.sh3
-rwxr-xr-xtest/validation/api/init/init_test_term_abnormal.sh3
-rw-r--r--test/validation/api/ipsec/.gitignore1
-rw-r--r--test/validation/api/ipsec/Makefile.am25
-rw-r--r--test/validation/api/ipsec/ipsec.c1551
-rw-r--r--test/validation/api/ipsec/ipsec.h162
-rwxr-xr-xtest/validation/api/ipsec/ipsec_async.sh3
-rwxr-xr-xtest/validation/api/ipsec/ipsec_inline_in.sh3
-rwxr-xr-xtest/validation/api/ipsec/ipsec_inline_out.sh3
-rw-r--r--test/validation/api/ipsec/ipsec_main.c83
-rwxr-xr-xtest/validation/api/ipsec/ipsec_sync.sh3
-rw-r--r--test/validation/api/ipsec/ipsec_test_in.c2367
-rw-r--r--test/validation/api/ipsec/ipsec_test_out.c2066
-rw-r--r--test/validation/api/ipsec/reass_test_vectors.c351
-rw-r--r--test/validation/api/ipsec/reass_test_vectors.h65
-rw-r--r--test/validation/api/ipsec/test_vectors.h2166
-rw-r--r--test/validation/api/lock/.gitignore (renamed from test/common_plat/validation/api/lock/.gitignore)0
-rw-r--r--test/validation/api/lock/Makefile.am4
-rw-r--r--test/validation/api/lock/lock.c1259
-rw-r--r--test/validation/api/ml/.gitignore1
-rw-r--r--test/validation/api/ml/Makefile.am4
-rw-r--r--test/validation/api/ml/ml.c572
-rw-r--r--test/validation/api/packet/.gitignore (renamed from test/common_plat/validation/api/packet/.gitignore)0
-rw-r--r--test/validation/api/packet/Makefile.am4
-rw-r--r--test/validation/api/packet/packet.c4584
-rw-r--r--test/validation/api/pktio/.gitignore (renamed from test/common_plat/validation/api/pktio/.gitignore)0
-rw-r--r--test/validation/api/pktio/Makefile.am4
-rw-r--r--test/validation/api/pktio/lso.c938
-rw-r--r--test/validation/api/pktio/lso.h17
-rw-r--r--test/validation/api/pktio/parser.c607
-rw-r--r--test/validation/api/pktio/parser.h17
-rw-r--r--test/validation/api/pktio/pktio.c5516
-rw-r--r--test/validation/api/pool/.gitignore (renamed from test/common_plat/validation/api/pool/.gitignore)0
-rw-r--r--test/validation/api/pool/Makefile.am4
-rw-r--r--test/validation/api/pool/pool.c2384
-rw-r--r--test/validation/api/queue/.gitignore (renamed from test/common_plat/validation/api/queue/.gitignore)0
-rw-r--r--test/validation/api/queue/Makefile.am4
-rw-r--r--test/validation/api/queue/queue.c1174
-rw-r--r--test/validation/api/random/.gitignore (renamed from test/common_plat/validation/api/random/.gitignore)0
-rw-r--r--test/validation/api/random/Makefile.am5
-rw-r--r--test/validation/api/random/random.c536
-rw-r--r--test/validation/api/scheduler/.gitignore2
-rw-r--r--test/validation/api/scheduler/Makefile.am5
-rw-r--r--test/validation/api/scheduler/scheduler.c3768
-rw-r--r--test/validation/api/scheduler/scheduler_no_predef_groups.c223
-rw-r--r--test/validation/api/shmem/.gitignore (renamed from test/common_plat/validation/api/shmem/.gitignore)0
-rw-r--r--test/validation/api/shmem/Makefile.am4
-rw-r--r--test/validation/api/shmem/shmem.c1173
-rw-r--r--test/validation/api/stash/.gitignore1
-rw-r--r--test/validation/api/stash/Makefile.am4
-rw-r--r--test/validation/api/stash/stash.c1395
-rw-r--r--test/validation/api/std/.gitignore1
-rw-r--r--test/validation/api/std/Makefile.am4
-rw-r--r--test/validation/api/std/std.c107
-rw-r--r--test/validation/api/system/.gitignore (renamed from test/common_plat/validation/api/system/.gitignore)0
-rw-r--r--test/validation/api/system/Makefile.am4
-rw-r--r--test/validation/api/system/system.c697
-rw-r--r--test/validation/api/thread/.gitignore (renamed from test/common_plat/validation/api/thread/.gitignore)0
-rw-r--r--test/validation/api/thread/Makefile.am6
-rw-r--r--test/validation/api/thread/thread.c268
-rw-r--r--test/validation/api/time/.gitignore (renamed from test/common_plat/validation/api/time/.gitignore)0
-rw-r--r--test/validation/api/time/Makefile.am4
-rw-r--r--test/validation/api/time/time.c1200
-rw-r--r--test/validation/api/timer/.gitignore (renamed from test/common_plat/validation/api/timer/.gitignore)0
-rw-r--r--test/validation/api/timer/Makefile.am4
-rw-r--r--test/validation/api/timer/timer.c3307
-rw-r--r--test/validation/api/traffic_mngr/.gitignore (renamed from test/common_plat/validation/api/traffic_mngr/.gitignore)0
-rw-r--r--test/validation/api/traffic_mngr/Makefile.am5
-rw-r--r--test/validation/api/traffic_mngr/traffic_mngr.c5045
1871 files changed, 239196 insertions, 83154 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf
index 990a54d2d..abb822996 100644
--- a/.checkpatch.conf
+++ b/.checkpatch.conf
@@ -1,14 +1,21 @@
--no-tree
--strict
--ignore=SPLIT_STRING
---ignore SSCANF_TO_KSTRTO
+--ignore=SSCANF_TO_KSTRTO
--ignore=NEW_TYPEDEFS
--ignore=DEPRECATED_VARIABLE
--ignore=COMPARISON_TO_NULL
--ignore=BIT_MACRO
---ignore=PREFER_PRINTF
---ignore=PREFER_SCANF
--ignore=VOLATILE
--ignore=AVOID_EXTERNS
+--ignore=CONST_STRUCT
+--ignore=PREFER_KERNEL_TYPES
+--ignore=CONSTANT_COMPARISON
+--ignore=BLOCK_COMMENT_STYLE
+--ignore=UNNECESSARY_PARENTHESES
+--ignore=SPDX_LICENSE_TAG
+--ignore=PREFER_FALLTHROUGH
+--ignore=LONG_LINE_STRING
+--ignore=EMAIL_SUBJECT
+--ignore=C99_COMMENT_TOLERANCE
--codespell
---codespellfile=/usr/share/codespell/dictionary.txt
diff --git a/.codecov.yml b/.codecov.yml
index 327f65496..d5eddbf7d 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,23 +1,19 @@
codecov:
notify:
- require_ci_to_pass: yes
+ require_ci_to_pass: no
+ wait_for_ci: no
coverage:
- precision: 3
+ precision: 2
round: down
- range: "50...75"
+ range: "70...100"
status:
project:
default:
- enabled: yes
target: 70%
threshold: 5%
- patch:
- default:
- enabled: yes
- target: 70%
- threshold: 5%
- changes: no
+ patch: off
+ changes: off
parsers:
gcov:
@@ -27,7 +23,7 @@ parsers:
method: no
macro: no
-comment:
- layout: "header, diff, files"
- behavior: default
- require_changes: no
+comment: false
+
+ignore:
+ - "platform/linux-generic/miniz"
diff --git a/.github/actions/build-failure-log/action.yml b/.github/actions/build-failure-log/action.yml
new file mode 100644
index 000000000..571274c41
--- /dev/null
+++ b/.github/actions/build-failure-log/action.yml
@@ -0,0 +1,7 @@
+name: 'Build Failure Logger'
+description: 'Log output of failing build'
+runs:
+ using: 'composite'
+ steps:
+ - run: find . -name config.log -exec cat {} \;
+ shell: bash
diff --git a/.github/actions/dump-log/action.yml b/.github/actions/dump-log/action.yml
new file mode 100644
index 000000000..039583686
--- /dev/null
+++ b/.github/actions/dump-log/action.yml
@@ -0,0 +1,7 @@
+name: 'Dump Logs'
+description: 'Dump logs of successful run'
+runs:
+ using: 'composite'
+ steps:
+ - run: find . -name "*.log" -exec echo -e "\n\n @@@@@ {} @@@@@\n\n" \; -exec cat {} \;
+ shell: bash
diff --git a/.github/actions/run-failure-log/action.yml b/.github/actions/run-failure-log/action.yml
new file mode 100644
index 000000000..520d178c8
--- /dev/null
+++ b/.github/actions/run-failure-log/action.yml
@@ -0,0 +1,7 @@
+name: 'Run Failure Logger'
+description: 'Log output of failing run'
+runs:
+ using: 'composite'
+ steps:
+ - run: find . -name "*.trs" | xargs grep -l '^.test-result. FAIL' | while read trs ; do echo FAILURE detected at $trs; cat ${trs%%.trs}.log ; done
+ shell: bash
diff --git a/.github/workflows/ci-pipeline-arm64.yml b/.github/workflows/ci-pipeline-arm64.yml
new file mode 100644
index 000000000..78cbfcc7d
--- /dev/null
+++ b/.github/workflows/ci-pipeline-arm64.yml
@@ -0,0 +1,347 @@
+name: CI arm64
+
+# github.repository has been used to ensure CI is only run on the repo where
+# self-hosted runners are installed. This will prevent [self-hosted, ARM64] CI failing on forks
+
+on: [push, pull_request, merge_group]
+env:
+ ARCH: arm64
+ CC: gcc
+ CONTAINER_NAMESPACE: ghcr.io/opendataplane/odp-docker-images
+ OS: ubuntu_20.04
+
+jobs:
+ Build_gcc:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ 'CFLAGS=-O3',
+ 'CFLAGS=-O1',
+ 'CFLAGS=-O0 --enable-debug=full',
+ 'CFLAGS=-Os',
+ 'CFLAGS=-pedantic',
+ '--enable-lto',
+ '--enable-lto --enable-abi-compat',
+ '--enable-pcapng-support']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=gcc
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_clang:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ 'CFLAGS=-O3',
+ 'CFLAGS=-O1',
+ 'CFLAGS=-O0 --enable-debug=full',
+ 'CFLAGS=-Os',
+ 'CFLAGS=-pedantic',
+ '--enable-pcapng-support',
+ '--without-openssl --without-pcap',
+ '--with-crypto=armv8crypto',
+ '--with-crypto=ipsecmb',
+ '--enable-wfe-locks']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=clang
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_static_u22:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ env:
+ OS: ubuntu_22.04
+ CONF: "--disable-shared --without-openssl --without-pcap"
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [10, 11, 12]
+ conf: ['', '--enable-lto']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${CONF} ${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/build_static.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_OS:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ os: ['rocky_linux_8']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${{matrix.os}}-${ARCH}-native /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_gcc_u22:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ env:
+ OS: ubuntu_22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [10, 11, 12, 13]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_out-of-tree:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/out_of_tree.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_XDP:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ env:
+ CONF: "--enable-xdp"
+ OS: ubuntu_22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Run_distcheck:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['--enable-user-guides', '--enable-user-guides --enable-abi-compat']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/distcheck.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_gcc:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ '--enable-abi-compat',
+ '--enable-deprecated --enable-helper-deprecated --enable-debug=full',
+ '--enable-dpdk-zero-copy --disable-static-applications',
+ '--disable-host-optimization --enable-event-validation=warn',
+ '--disable-host-optimization --enable-abi-compat',
+ '--without-openssl --without-pcap',
+ '--with-crypto=armv8crypto',
+ '--with-crypto=ipsecmb',
+ '--enable-wfe-locks']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=gcc -e ARCH="${ARCH}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+ - if: ${{ success() }}
+ uses: ./.github/actions/dump-log
+
+ Run_clang:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ '--enable-abi-compat',
+ '--enable-deprecated --enable-helper-deprecated --enable-debug=full',
+ '--enable-dpdk-zero-copy --disable-static-applications',
+ '--disable-host-optimization --enable-event-validation=warn',
+ '--disable-host-optimization --enable-abi-compat']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=clang -e ARCH="${ARCH}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_CFLAGS:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ cflags: ['-march=armv8.2-a -O2', '-march=armv8.2-a+crypto -O2']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}" -e ARCH="${ARCH}"
+ -e CXX=g++-10 -e CFLAGS="${{matrix.cflags}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_OS:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ strategy:
+ fail-fast: false
+ matrix:
+ os: ['ubuntu_22.04']
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${{matrix.os}}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_sched_config:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/sched-basic.conf $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_stash_config:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/stash-custom.conf $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_scheduler_sp:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_SCHEDULER=sp $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_scheduler_scalable:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_SCHEDULER=scalable -e CI_SKIP=pktio_test_pktin_event_sched $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_process_mode:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/process-mode.conf
+ -e ODPH_PROC_MODE=1 $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_inline_timer:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/inline-timer.conf
+ $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check_inline_timer.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_packet_align:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/packet_align.conf
+ $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native /odp/scripts/ci/check_pktio.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-19_11:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native-dpdk_19.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-20_11:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native-dpdk_20.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-21_11:
+ if: ${{ github.repository == 'OpenDataPlane/odp' }}
+ runs-on: [self-hosted, ARM64]
+ steps:
+ - uses: OpenDataPlane/action-clean-up@main
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-native-dpdk_21.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
new file mode 100644
index 000000000..c03adb711
--- /dev/null
+++ b/.github/workflows/ci-pipeline.yml
@@ -0,0 +1,465 @@
+name: CI x86_64
+
+on: [push, pull_request, merge_group]
+env:
+ ARCH: x86_64
+ CC: gcc
+ CONTAINER_NAMESPACE: ghcr.io/opendataplane/odp-docker-images
+ OS: ubuntu_20.04
+
+jobs:
+ Checkpatch:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install codespell
+ - name: Check pull request
+ if: github.event_name == 'pull_request'
+ env:
+ CHECKPATCH_COMMAND: ./scripts/checkpatch.pl
+ uses: webispy/checkpatch-action@v9
+ - name: Check push
+ if: github.event_name == 'push' && github.ref != 'refs/heads/master'
+ run: |
+ AFTER=${{ github.event.after }}
+ BEFORE=${{ github.event.before }}
+ if [ -z "${BEFORE//0}" ] || [ -z "${AFTER//0}" ] || ${{ github.event.forced }}; then
+ COMMIT_RANGE=""
+ else
+ COMMIT_RANGE="${BEFORE}..${AFTER}"
+ fi
+ ./scripts/ci-checkpatches.sh ${COMMIT_RANGE}
+
+ Documentation:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install doxygen asciidoctor libconfig-dev libssl-dev mscgen cmake graphviz
+ sudo gem install asciidoctor
+ - name: Build
+ shell: bash
+ run: |
+ ./bootstrap
+ ./configure --enable-user-guides
+ pushd doc
+ make
+ popd
+ touch ./doxygen.log
+ # Doxygen does not trap on warnings, check for them here.
+ make doxygen-doc 2>&1 | tee ./doxygen.log
+ ! fgrep -rq warning ./doxygen.log
+
+ Build_gcc:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ 'CFLAGS=-O3',
+ 'CFLAGS=-O1',
+ 'CFLAGS=-O0 --enable-debug=full',
+ 'CFLAGS=-Os',
+ 'CFLAGS=-pedantic',
+ '--enable-lto',
+ '--enable-lto --enable-abi-compat',
+ '--enable-pcapng-support']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=gcc
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_clang:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ 'CFLAGS=-O3',
+ 'CFLAGS=-O1',
+ 'CFLAGS=-O0 --enable-debug=full',
+ 'CFLAGS=-Os',
+ 'CFLAGS=-pedantic',
+ '--enable-pcapng-support',
+ '--without-openssl --without-pcap']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=clang
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_static_u20:
+ runs-on: ubuntu-20.04
+ env:
+ OS: ubuntu_20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [9]
+ conf: ['--disable-shared', '--enable-lto --disable-shared']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_static.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_static_u22:
+ runs-on: ubuntu-20.04
+ env:
+ OS: ubuntu_22.04
+ CONF: "--disable-shared --without-openssl --without-pcap"
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [10, 11, 12]
+ conf: ['', '--enable-lto']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${CONF} ${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_static.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_arm64:
+ runs-on: ubuntu-20.04
+ env:
+ ARCH: arm64
+ CONF: "--enable-dpdk-shared"
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ conf: ['', '--enable-abi-compat', 'CFLAGS=-march=armv8.2-a', 'CFLAGS=-march=armv8-a+lse',
+ '--with-crypto=armv8crypto', '--enable-wfe-locks']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${CONF} ${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_armhf:
+ runs-on: ubuntu-20.04
+ env:
+ ARCH: armhf
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_ppc64el:
+ runs-on: ubuntu-20.04
+ env:
+ ARCH: ppc64el
+ CONF: "--enable-dpdk-shared"
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${CONF} ${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_i386:
+ runs-on: ubuntu-20.04
+ env:
+ ARCH: i386
+ OS: ubuntu_18.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_riscv64:
+ runs-on: ubuntu-20.04
+ env:
+ ARCH: riscv64
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_OS:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ os: ['centos_7', 'rocky_linux_8']
+ conf: ['--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${{matrix.os}}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_gcc_u20:
+ runs-on: ubuntu-20.04
+ env:
+ OS: ubuntu_20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [7, 8]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_gcc_u22:
+ runs-on: ubuntu-20.04
+ env:
+ OS: ubuntu_22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc_ver: [10, 11, 12, 13]
+ conf: ['', '--enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="gcc-${{matrix.cc_ver}}" -e CXX="g++-${{matrix.cc_ver}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_out-of-tree:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/out_of_tree.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Build_XDP:
+ runs-on: ubuntu-20.04
+ env:
+ CONF: "--enable-xdp"
+ OS: ubuntu_22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/build_${ARCH}.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/build-failure-log
+
+ Run_distcheck:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['--enable-user-guides', '--enable-user-guides --enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/distcheck.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_gcc:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ '--enable-abi-compat',
+ '--enable-deprecated --enable-helper-deprecated --enable-debug=full',
+ '--enable-dpdk-zero-copy --disable-static-applications',
+ '--disable-host-optimization --enable-event-validation=warn',
+ '--disable-host-optimization --enable-abi-compat',
+ '--without-openssl --without-pcap']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=gcc -e ARCH="${ARCH}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+ - if: ${{ success() }}
+ uses: ./.github/actions/dump-log
+
+ Run_clang:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ conf: ['',
+ '--enable-abi-compat',
+ '--enable-deprecated --enable-helper-deprecated --enable-debug=full',
+ '--enable-dpdk-zero-copy --disable-static-applications',
+ '--disable-host-optimization --enable-event-validation=warn',
+ '--disable-host-optimization --enable-abi-compat']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC=clang -e ARCH="${ARCH}"
+ -e CONF="${{matrix.conf}}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_OS:
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [gcc, clang]
+ os: ['ubuntu_22.04']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${{matrix.cc}}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${{matrix.os}}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_sched_config:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/sched-basic.conf $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_stash_config:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/stash-custom.conf $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_scheduler_sp:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_SCHEDULER=sp $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_scheduler_scalable:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_SCHEDULER=scalable -e CI_SKIP=pktio_test_pktin_event_sched $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_process_mode:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/process-mode.conf
+ -e ODPH_PROC_MODE=1 $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_inline_timer:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/inline-timer.conf
+ $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check_inline_timer.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_packet_align:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" -e ODP_CONFIG_FILE=/odp/platform/linux-generic/test/packet_align.conf
+ $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check_pktio.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-19_11:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-dpdk_19.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-20_11:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-dpdk_20.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_dpdk-21_11:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CONF="${CONF}" $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH}-dpdk_21.11 /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
+
+ Run_sanitizer:
+ runs-on: ubuntu-20.04
+ env:
+ OS: ubuntu_22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ flags: ['-fsanitize=address,undefined -fno-sanitize-recover=all']
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g -e CC="${CC}" -e ARCH="${ARCH}"
+ -e CFLAGS="-O0 -g -Wno-error ${{matrix.flags}}"
+ -e CXXFLAGS="-O0 -g -Wno-error ${{matrix.flags}}"
+ -e LDFLAGS="-g ${{matrix.flags}}"
+ $CONTAINER_NAMESPACE/odp-ci-${OS}-${ARCH} /odp/scripts/ci/check.sh
+ - if: ${{ failure() }}
+ uses: ./.github/actions/run-failure-log
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
new file mode 100644
index 000000000..136077c1f
--- /dev/null
+++ b/.github/workflows/coverity.yml
@@ -0,0 +1,25 @@
+name: Coverity Scan
+
+on:
+ schedule:
+ - cron: '0 0 * * *' # Once every day at 00:00 UTC
+env:
+ CC: gcc
+ ARCH: x86_64
+ CONTAINER_NAMESPACE: ghcr.io/opendataplane/odp-docker-images
+ OS: ubuntu_20.04
+ COVERITY_EMAIL: dummy@opendataplane.org
+ COVERITY_PROJECT: ODP
+
+jobs:
+ Coverity-analysis:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: sudo docker run -i -v `pwd`:/odp --privileged --shm-size 8g
+ -e CC="${CC}" -e GITHUB_SHA="${GITHUB_SHA}"
+ -e COVERITY_TOKEN="${{ secrets.COVERITY_TOKEN }}"
+ -e COVERITY_EMAIL="${COVERITY_EMAIL}"
+ -e COVERITY_PROJECT="${COVERITY_PROJECT}"
+ ${CONTAINER_NAMESPACE}/odp-ci-${OS}-${ARCH}-coverity-linux-generic
+ /odp/scripts/ci/coverity.sh
diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
new file mode 100644
index 000000000..fd7664a85
--- /dev/null
+++ b/.github/workflows/gh-pages.yml
@@ -0,0 +1,52 @@
+name: GitHub Pages
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ Documentation:
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install doxygen asciidoctor libconfig-dev libssl-dev mscgen cmake graphviz
+ sudo gem install asciidoctor
+ - name: Build
+ run: |
+ ./bootstrap
+ ./configure --enable-user-guides
+ pushd doc
+ make
+ popd
+ # Doxygen does not trap on warnings so check for them here
+ make doxygen-doc 2>&1 | tee ./doxygen.log
+ ! fgrep -rq warning ./doxygen.log
+
+ - name: Before deploy
+ run: |
+ pushd doc
+ mkdir gh-pages
+ cp -r application-api-guide/output/html/* gh-pages/
+ cp -r platform-api-guide/output/html/ gh-pages/platform-api-guide
+ cp -r helper-guide/output/html/ gh-pages/helper-guide
+ mkdir gh-pages/implementers-guide
+ cp implementers-guide/implementers-guide.html gh-pages/implementers-guide/index.html
+ mkdir gh-pages/users-guide
+ cp users-guide/users-guide.html gh-pages/users-guide/index.html
+ mkdir gh-pages/process-guide
+ cp process-guide/*.html gh-pages/process-guide/
+ popd
+
+ - name: Deploy
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ uses: crazy-max/ghaction-github-pages@v4
+ with:
+ allow_empty_commit: false
+ build_dir: ./doc/gh-pages
+ jekyll: false
+ target_branch: gh-pages
diff --git a/.gitignore b/.gitignore
index cce24282a..0a58c0ee9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,17 @@
*.d
*.gcda
*.gcno
+*.gcov
+*.trs
*.la
*.lo
+*.log
*.o
*.orig
*.patch
*.rej
*.swp
+*.xml
*~
.deps/
.dirstamp
@@ -34,12 +38,19 @@ depcomp
doc/output
dpdk/
install-sh
+include/odp/autoheader_build.h.in
+include/odp/autoheader_build.h
+include/odp/autoheader_external.h
+include/odp/autoheader_internal.h
+include/odp/stamp-h*
+helper/include/odp/helper/autoheader_external.h
+helper/include/odp/helper/stamp-h*
lib/
libtool
ltmain.sh
-m4/*.m4
+m4/libtool.m4
+m4/lt*.m4
missing
perf.data*
-pkgconfig/libodp*.pc
tags
test-driver
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d750af1d3..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,260 +0,0 @@
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Please update xxxx for your coverity token and notification email if required
-# pushing to github/master will run make check
-# pushing to github/coverity_scan will also launch a static analysis
-# See https://scan.coverity.com/travis_ci
-
-language: c
-sudo: required
-dist: trusty
-group: deprecated-2017Q2
-env:
- global:
- # COVERITY_SCAN_TOKEN
- # ** specific to your project **
- # Note:
- # You should have a github account and travis linked travis account.
- # The secure key to be filled below is the 685 character long encrypted
- # token you can find as follow from your coverity dashboard
- # (at https://scan.coverity.com/dashboard):
- # Click on the github project (<you>/odp)
- # Click on "submit build"
- # Click on "Configure Travis CI"
- # Look at the COVERITY_SCAN_TOKEN in the env: global: section
- # of the configuration example.
- # copy the secure:<key> below
- #
- - secure: "xxxx"
- #
- # By default Linaro CODECOV_TOKEN token is used. It's ok to use it to see
- # for individual commit validation. But you you want to track tests history
- # you need generated new one at https://codecov.io specific for your repo.
- - CODECOV_TOKEN=8e1c0fd8-62ff-411e-a79f-5839f6662c11
-
-addons:
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - gcc
- - clang-3.8
- - automake autoconf libtool libssl-dev graphviz mscgen doxygen
- - libpcap-dev
-# coverity_scan:
-# project:
-# name: "$TRAVIS_REPO_SLUG"
-# notification_email: xxxx
-# build_command_prepend: "./bootstrap && ./configure --enable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-user-guides --enable-test-perf-proc --enable-test-example"
-# build_command: "make"
-# branch_pattern: coverity_scan
-
-compiler:
- - gcc
- - clang-3.8
-
-cache:
- ccache: true
- pip: true
- directories:
- - dpdk
- - netmap
- - $HOME/cunit-install
-
-env:
- - CONF=""
- - CONF="--disable-abi-compat"
- - CONF="--enable-schedule-sp"
- - CONF="--enable-schedule-iquery"
- - CONF="--enable-dpdk-zero-copy"
-
-before_install:
-
- # Install cross toolchains, etc
- # apt-get update may fail thanks to Ubuntu removing Packages/indices while not removing relevant parts of Release file
- - if [ -n "$CROSS_ARCH" ] ;
- then
- BUILD_GNU_TYPE=`dpkg-architecture -a"$CROSS_ARCH" -qDEB_BUILD_GNU_TYPE` ;
- CROSS_GNU_TYPE=`dpkg-architecture -a"$CROSS_ARCH" -qDEB_HOST_GNU_TYPE` ;
- CROSS_MULTIARCH=`dpkg-architecture -a"$CROSS_ARCH" -qDEB_HOST_MULTIARCH` ;
- CROSS="--host="$CROSS_GNU_TYPE" --build="$BUILD_GNU_TYPE" --prefix=/usr --includedir=/usr/include/"$CROSS_MULTIARCH" --libdir=/usr/lib/"$CROSS_MULTIARCH" --libexecdir=/usr/lib/"$CROSS_MULTIARCH"" ;
- sudo dpkg --add-architecture "$CROSS_ARCH" ;
- sudo -E apt-add-repository -y "deb http://ports.ubuntu.com trusty main" ;
- sudo -E apt-add-repository -y "deb http://ports.ubuntu.com trusty-updates main" ;
- sudo -E apt-get -y update || true ;
- sudo -E apt-get -y --no-install-suggests --no-install-recommends --force-yes install build-essential gcc-"$CROSS_GNU_TYPE" pkg-config-"$CROSS_GNU_TYPE" ;
- sudo -E apt-get -y --no-install-suggests --no-install-recommends --force-yes install libc6-dev:"$CROSS_ARCH" libssl-dev:"$CROSS_ARCH" zlib1g-dev:"$CROSS_ARCH" libconfig-dev:"$CROSS_ARCH" ;
- fi
- - if test ! -L /usr/lib/ccache/${CC%% *} ; then sudo ln -s -t /usr/lib/ccache/ `which ${CC%% *}` ; fi
- - ccache -s
- # Install cunit for the validation tests because distro version is too old and fails C99 compile
- - sudo apt-get remove libcunit1-dev libcunit1
- - export LD_LIBRARY_PATH="$HOME/cunit-install/$CROSS_ARCH/lib:$LD_LIBRARY_PATH"
- - |
- if [ ! -f "$HOME/cunit-install/$CROSS_ARCH/lib/libcunit.a" ]; then
- export CUNIT_VERSION=2.1-3
- curl -sSOL https://github.com/Linaro/libcunit/releases/download/${CUNIT_VERSION}/CUnit-${CUNIT_VERSION}.tar.bz2
- tar -jxf *.bz2
- pushd CUnit*
- libtoolize --force --copy
- aclocal
- autoheader
- automake --add-missing --include-deps --copy
- autoconf
- ./configure --prefix=$HOME/cunit-install/$CROSS_ARCH --enable-debug --enable-automated --enable-basic --enable-console --enable-examples --enable-test $CROSS || cat config.log
- make
- sudo make install
- popd
- fi
- - find $HOME/cunit-install
-
-install:
- - echo 1000 | sudo tee /proc/sys/vm/nr_hugepages
- - sudo mkdir -p /mnt/huge
- - sudo mount -t hugetlbfs nodev /mnt/huge
-
- - sudo apt-get -qq update
- - sudo apt-get install linux-headers-`uname -r`
- - sudo pip install coverage
- - gem install asciidoctor
- - PATH=${PATH//:\.\/node_modules\/\.bin/}
-
-# DPDK pktio. Note that cache must be purged if dpdk version changes.
- - TARGET=${TARGET:-"x86_64-native-linuxapp-gcc"}
- - |
- if [ ! -f "dpdk/${TARGET}/lib/libdpdk.a" ]; then
- git -c advice.detachedHead=false clone -q --depth=1 --single-branch --branch=v17.02 http://dpdk.org/git/dpdk dpdk
- pushd dpdk
- git log --oneline --decorate
- make config T=${TARGET} O=${TARGET}
- pushd ${TARGET}
- sed -ri 's,(CONFIG_RTE_LIBRTE_PMD_PCAP=).*,\1y,' .config
- popd
- make install T=${TARGET} EXTRA_CFLAGS="-fPIC"
- rm -r ./doc ./${TARGET}/app ./${TARGET}/build
- popd
- fi
-
-# Netmap pktio
- - |
- if [ ! -f "netmap/LINUX/netmap.ko" ]; then
- git -c advice.detachedHead=false clone -q --depth=1 --single-branch --branch=v11.2 https://github.com/luigirizzo/netmap.git
- pushd netmap/LINUX
- ./configure
- make
- sudo insmod ./netmap.ko
- popd
- fi
-
-script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install --enable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-user-guides --enable-test-perf-proc --enable-test-example --with-dpdk-path=`pwd`/dpdk/${TARGET} --with-netmap-path=`pwd`/netmap --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH $CONF
- - make -j $(nproc)
- - mkdir /dev/shm/odp
- - sudo LD_LIBRARY_PATH="$HOME/cunit-install/$CROSS_ARCH/lib:$LD_LIBRARY_PATH" ODP_SHM_DIR=/dev/shm/odp make check
- - make install
-
- - echo "Checking linking and run from install..."
- - pushd $HOME
- - echo "Dynamic link.."
- - ${CC} ${OLDPWD}/example/hello/odp_hello.c -o odp_hello_inst `PKG_CONFIG_PATH=${HOME}/odp-install/lib/pkgconfig pkg-config --cflags --libs libodp-linux`
- - LD_LIBRARY_PATH="${HOME}/odp-install/lib:$LD_LIBRARY_PATH" ./odp_hello_inst
- - echo "Static link.."
- - ${CC} ${OLDPWD}/example/hello/odp_hello.c -o odp_hello_inst `PKG_CONFIG_PATH=${HOME}/odp-install/lib/pkgconfig pkg-config --cflags --libs libodp-linux --static` -static
- - ./odp_hello_inst
- - ccache -s
-
-jobs:
- include:
- - stage: test
- compiler: aarch64-linux-gnu-gcc
- env: TEST="aarch64-linux-gnu" CROSS_ARCH="arm64"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- compiler: "\"clang-3.8 --target=aarch64-linux-gnu\""
- env: TEST="clang-3.8 aarch64-linux-gnu" CROSS_ARCH="arm64"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- compiler: arm-linux-gnueabihf-gcc
- env: TEST="arm-linux-gnueabihf" CROSS_ARCH="armhf"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- compiler: "\"clang-3.8 --target=arm-linux-gnueabihf\""
- env: TEST="clang-3.8 arm-linux-gnueabihf" CROSS_ARCH="armhf" CFLAGS="-march=armv7-a"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- compiler: powerpc-linux-gnu-gcc
- env: TEST="powerpc-linux-gnueabihf" CROSS_ARCH="powerpc"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- compiler: "\"clang-3.8 --target=powerpc-linux-gnu\""
- env: TEST="clang-3.8 powerpc-linux-gnu" CROSS_ARCH="powerpc"
- install: true
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install $CROSS
- --disable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - make -j $(nproc)
- - stage: test
- env: TEST=coverage
- compiler: gcc
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install --enable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-user-guides --enable-test-perf-proc --enable-test-example --with-dpdk-path=`pwd`/dpdk/${TARGET} --with-netmap-path=`pwd`/netmap CFLAGS="-O0 -coverage" --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH CXXFLAGS="-O0 -coverage" LDFLAGS="--coverage"
- - sudo LD_LIBRARY_PATH="$HOME/cunit-install/$CROSS_ARCH/lib:$LD_LIBRARY_PATH" PATH=${PATH//:\.\/node_modules\/\.bin/} make check
- - find . -type f -iname '*.[ch]' -not -path ".git/*" -execdir gcov {} \; ; bash <(curl -s https://codecov.io/bash) -X coveragepy
- - stage: test
- env: TEST=distcheck
- compiler: gcc
- script:
- - ./bootstrap
- - ./configure --prefix=$HOME/odp-install --enable-test-cpp --enable-test-vald --enable-test-helper --enable-test-perf --enable-user-guides --enable-test-perf-proc --enable-test-example --with-cunit-path=$HOME/cunit-install/$CROSS_ARCH
- - sudo PATH="$PATH" LD_LIBRARY_PATH="$HOME/cunit-install/$CROSS_ARCH/lib:$LD_LIBRARY_PATH" make distcheck CFLAGS="-I$HOME/cunit-install/$CROSS_ARCH/include" LDFLAGS="-L$HOME/cunit-install/$CROSS_ARCH/lib"
- - stage: test
- env: TEST=doxygen
- compiler: gcc
- script:
- # doxygen does not trap on warnings, check for them here.
- - ./bootstrap
- - ./configure
- - make doxygen-doc |tee doxygen.log
- - fgrep -rvq warning ./doxygen.log
- - stage: test
- env: TEST=checkpatch
- compiler: gcc
- script:
- - echo ${TRAVIS_COMMIT_RANGE};
- - ODP_PATCHES=`echo ${TRAVIS_COMMIT_RANGE} | sed 's/\.//'`;
- - if [ -z "${ODP_PATCHES}" ]; then env; exit 1; fi;
- - ./scripts/ci-checkpatches.sh ${ODP_PATCHES};
-
-after_failure:
- - cat config.log
- - find . -name 'test-suite.log' -execdir grep -il "FAILED" {} \; -exec echo {} \; -exec cat {} \;
diff --git a/CHANGELOG b/CHANGELOG
index 866e51e95..e9d953a92 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,3630 @@
+== OpenDataPlane (1.44.0.0)
+
+=== Backward compatible API changes
+==== ML
+* Add new API module for Machine Learning offload.
+
+=== Implementation
+==== ML
+* Implement the API using ONNX Runtime. A non-standard ONNX install path can be
+passed with `--with-ort-path` configure option. See `DEPENDENCIES` for
+additional information.
+
+=== Example Applications
+==== debug
+* Print packet IO link information.
+* Add packet IO interface name option (`-i`).
+
+==== packet_dump
+* Add support for measuring packet input delay.
+
+=== Performance Tests
+==== l2fwd
+* Add packet input timestamping option (`-T`).
+* Add packet data prefetch option (`-F`).
+* Add packet data read option (`-R`).
+* Add packet print option (`-V`).
+
+==== pool_latency
+* New pool latency tester application for profiling pool allocation and free
+latencies under different load patterns.
+
+==== sched_perf
+* Add options to read (`-u`) and modify (`-U`) event user area contents per
+event reception.
+* Run application indefinitely when the number of events to schedule option
+(`-s`) is set to zero.
+
+== OpenDataPlane (1.43.0.0)
+
+=== Backward incompatible API changes
+==== Classifier
+* Add new `odp_cls_capability_t.max_pmr` and
+`odp_cls_capability_t.max_pmr_per_cos` packet matching rule capabilities.
+* Rename `odp_cls_capability_t.max_pmr_terms` field to
+`odp_cls_capability_t.max_terms_per_pmr`.
+
+==== Time
+* Change local/global time specification to allow timestamp values to start from
+an implementation defined value, instead of always starting from zero.
+
+==== Timer
+* Deprecate `odp_timer_set_t` type. Use `odp_timer_retval_t` instead.
+* Deprecate `odp_timer_pool_start()` function in comments. Will be properly
+deprecated in an upcoming release. Use `odp_timer_pool_start_multi()` instead.
+* Deprecate `odp_timeout_fresh()` function.
+* Change `odp_timer_free()` specification to remove the possibility to free a
+timer that is running.
+* Change `odp_timer_pool_create()` specification to state that timer pool
+handles must not be used by other APIs, except `odp_timer_pool_to_u64()`,
+ before being started.
+
+=== Backward compatible API changes
+==== Event
+* Add `odp_event_pool()` function, which returns a handle to the pool where the
+event was allocated from.
+
+==== Hints
+* Fix a type conversion issue in `odp_unlikely()` implementation.
+
+==== Packet
+* Clarify that `ODP_PACKET_FREE_CTRL_DONT_FREE` option does not affect direct
+packet free calls.
+* Clarify that packet IO time is specific to the packet IO interface.
+
+==== Pool
+* Clarify that disabled/unused per thread statistics counters will not
+necessarily be zeroed by `odp_pool_stats()`.
+
+==== Scheduler
+* Clarify event ordering in ordered scheduling contexts.
+
+==== Thread
+* Add new functions `odp_thread_control_count_max()` and
+`odp_thread_worker_count_max()` for reading the maximum number of control and
+worker threads.
+* Add new functions `odp_thread_control_count()` and `odp_thread_worker_count()`
+for reading the current number of control and worker threads.
+
+==== Time
+* Add `odp_time_add_ns()` function for adding nanoseconds into a time value.
+* Add `odp_time_startup()` function for requesting ODP instance startup time.
+* Clarify `odp_time_sum()` specification by adding a notification that resulting
+timestamp may wrap around if large timestamp values are summed up.
+
+==== Timer
+* Add `odp_timer_pool_start_multi()` function for starting timer pools, which
+takes the to-be-started pool handles as arguments.
+* Clarify that timer ticks and related nanosecond values are specific to a timer
+pool. Also, state explicitly that those may not start from zero.
+
+=== Remove deprecated APIs
+==== Classifier
+* Remove deprecated `odp_cls_drop_t` enum.
+* Remove deprecated `odp_cos_drop_set()` function.
+* Remove deprecated `odp_cos_drop()` function.
+* Remove deprecated `odp_cos_with_l2_priority()` function.
+* Remove deprecated `odp_cos_with_l3_qos()` function.
+
+==== Crypto
+* Remove deprecated `ODP_CRYPTO_SES_CREATE_ERR_NONE`,
+`ODP_CRYPTO_SES_CREATE_ERR_ENOMEM`, `ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER`, and
+`ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH` defines.
+* Remove deprecated `odp_crypto_alg_err_t.ODP_CRYPTO_ALG_ERR_KEY_SIZE` and
+`odp_crypto_alg_err_t.ODP_CRYPTO_ALG_ERR_IV_INVALID` enums.
+* Remove deprecated `odp_crypto_hw_err_t` enum.
+* Remove deprecated `odp_crypto_packet_result_t.ok` field.
+
+==== Scheduler
+* Remove deprecated `ODP_SCHED_PRIO_HIGHEST`, `ODP_SCHED_PRIO_NORMAL`,
+`ODP_SCHED_PRIO_LOWEST`, and `ODP_SCHED_PRIO_DEFAULT` defines.
+
+==== Timer
+* Remove deprecated `ODP_CLOCK_CPU` and `ODP_CLOCK_EXT` defines.
+* Remove deprecated `ODP_TIMER_TOOEARLY`, `ODP_TIMER_TOOLATE` and
+`ODP_TIMER_NOEVENT` defines.
+* Remove deprecated `odp_timer_set_abs()` function.
+* Remove deprecated `odp_timer_set_rel()` function.
+
+=== Helper (1.5.0)
+==== Backward incompatible changes
+* Remove deprecated `odph_odpthread_t` and `odph_odpthread_params_t` types.
+* Remove deprecated `odph_thread_param_t.instance` field.
+* Remove deprecated `odph_odpthreads_create()` and `odph_odpthreads_join()`
+functions.
+
+=== Implementation
+==== Ticketlock
+* Add WFE based aarch64 ticketlock implementation (`--enable-wfe-locks`) for
+power saving.
+
+=== Performance Tests
+==== dma_perf
+* Add option for using software memory copy in addition to DMA transfers.
+* Add options for using sparse packets and memory as the transfer segment type.
+
+== OpenDataPlane (1.42.1.0)
+
+=== Backward compatible API changes
+==== Classifier
+* Add new `odp_cls_pmr_create_multi()` function for creating multiple packet
+matching rules with a single call.
+* Add new `odp_cls_pmr_destroy_multi()` function for destroying multiple PMRs
+with a single call.
+* Add new `odp_cls_cos_create_multi()` function for creating multiple class-of-
+services with a single call.
+* Add new `odp_cos_destroy_multi()` function for destroying multiple CoSes with
+a single call.
+
+==== Crypto
+* Clarify packet ordering of async crypto ops (`odp_crypto_op_enq()`).
+* Add `odp_crypto_packet_op_param_t.null_crypto` parameter that tells that a
+packet should be processed as if the configured algorithms were null cipher and
+null auth algorithm.
+* Add `ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP` crypto operation type that allows both
+basic and out-of-place operations in the same session.
+
+==== Packet IO
+* Add new API define `ODP_PKTIO_MAX_INDEX` for maximum packet IO interface
+index.
+
+==== Queue
+* Add new `odp_queue_create_multi()` function for creating multiple queues with
+a single call.
+* Add new `odp_queue_destroy_multi()` function for destroying multiple queues
+with a single call.
+* Clarify `odp_queue_param_t` default value specification.
+
+==== System
+* Add enumerations for ARM ISA versions ARMv8.8-A, ARMv8.9-A, and ARMv9.3-A.
+
+==== Timer
+* Change `odp_timer_pool_param_t.num_timers` description to allow applications
+to attempt to allocate more timers than there are in a timer pool.
+* Add new `odp_timeout_alloc_multi()` function for allocating multiple timeouts
+with a single call.
+* Add new `odp_timeout_free_multi()` function for freeing multiple timeouts with
+a single call.
+
+=== Helper (1.4.0)
+==== Backward incompatible changes
+===== Protocols
+* Rename `ODP_GTP_HLEN` define to `ODPH_GTP_HLEN`.
+* Rename `ODP_IGMP_HLEN` define to `ODPH_IGMP_HLEN`.
+* Remove unused ICMP defines and add missing `ODPH_` prefix to the remaining
+ones.
+
+==== Backward compatible changes
+===== Macros
+* Add new `ODPH_MIN()` and `ODPH_MAX()` helper macros for comparison operations.
+* Add new `ODPH_ARRAY_SIZE()` helper macro for calculating array size.
+* Add new `ODPH_ABS()` helper macro for calculating the absolute value of a
+signed variable.
+
+=== Implementation
+==== CPU
+* Optimize `odp_cpu_cycles()` performance on aarch64 by implementing the
+function using `cntvct_el0` virtual counter register.
+
+==== Packet IO
+* Transmitting packet vectors is not supported by the current API, so remove
+the code from the implementation. This may improve performance when transmitting
+packets through event queues.
+* Add support for packet transmit completion check through polling
+(`ODP_PACKET_TX_COMPL_POLL`).
+
+=== Example Applications
+==== sysinfo
+* Add prints for various missing capabilities (e.g. pool, packet IO, timer).
+
+==== timer_accuracy
+* Add new (`-c, --count`) option to set the CPU count.
+* Add new (`-t, --queue_type`) option to set the queue sync type.
+* Add new (`-q, --num_queue`) option to set the number of queues.
+* Add new (`-G, --sched_groups`) option to use a dedicated schedule group for
+each queue.
+
+=== Performance Tests
+==== bench_pktio_sp
+* Add new `odp_bench_pktio_sp` application for measuring delays of ODP packet IO
+slow path functions.
+
+==== dmafwd
+* Add new `odp_dmafwd` application for comparing packet forwarding throughput
+when received packets are first copied either with software memory copy
+or with DMA offload and then echoed back to sender(s).
+
+==== packet_gen
+* Add new (`-m, --tx_mode`) option for selecting how packets are transmitted.
+
+==== stash_perf
+* Add new `odp_stash_perf` application for measuring ODP stash fast path API
+performance.
+
+==== timer_perf
+* Add new mode (`-m 2`) for measuring `odp_schedule()` overhead while
+continuously restarting expiring timers.
+
+== OpenDataPlane (1.42.0.0)
+
+=== Backward incompatible API changes
+==== Crypto
+* Add `odp_crypto_session_param_t.cipher_range_in_bits` and
+`odp_crypto_session_param_t.auth_range_in_bits` session parameters that control
+whether cipher/auth range is given in bits or bytes.
+* Change `odp_crypto_cipher_capability_t.bit_mode` cipher/auth capability flag
+to indicate whether bit mode is supported in addition to byte mode that is
+always supported.
+
+=== Backward compatible API changes
+==== Barrier
+* Add memory barriers (`odp_mb_sync()`, `odp_mb_sync_load()`,
+`odp_mb_sync_store()`) which ensure that load/store instructions started before
+the barrier are complete, before load/store instructions after the barrier are
+started.
+
+==== Crypto
+* Allow non-zero-length cipher and auth ranges
+(`odp_crypto_packet_op_param_t.cipher_range/auth_range`) for the null cipher and
+auth algorithms in the OOP operation type.
+
+==== DMA
+* Add new `odp_dma_pool_capability_t.uarea_persistence` pool capability to
+signal if implementation is able to maintain the content of pool user areas
+across frees and allocations.
+* Add new `odp_dma_pool_param_t.uarea_init` pool parameters `init_fn` and
+`args` that can be used to initialize event user areas of a pool at pool
+creation time.
+* Move `odp_dma_seg_t` structure fields so that `addr/packet` field is directly
+followed by `len` and `offset` fields. After this change all commonly used
+fields are located in the first 16 bytes of the structure.
+
+==== Event
+* Add new `odp_event_types_multi()` function for reading event types and
+subtypes (optional) from all given events.
+* Add new `odp_event_user_area_and_flag()` function for reading both event
+user area pointer and user flag value.
+
+==== Init
+* Add new `ODP_LOG_WARN` log level to `odp_log_level_t`.
+
+==== Packet
+* Change `odp_packet_user_flag()` and `odp_packet_vector_user_flag()`
+documentations to specify that the return values are positive if user flag is
+set.
+
+==== Pool
+* Add new `uarea_persistence` pool capability to signal if implementation is
+able to maintain the content of pool user areas across frees and allocations.
+* Add new `uarea_init` pool parameters `init_fn` and `args` that can be used to
+initialize event user areas of a pool at pool creation time.
+
+==== Timer
+* Add new `odp_timeout_from_event_multi()` function for converting multiple
+events of type `ODP_EVENT_TIMEOUT` to timeout handles.
+* Clarify `odp_timer_alloc()` and `odp_timer_restart()` documentation.
+
+=== Implementation
+==== Time
+* Refactor time codebase to enable more optimized architecture specific
+implementations. x86 and aarch64 architectures are assumed to always have
+support for HW time and fallback to POSIX time functions has been removed for
+improved performance.
+
+== OpenDataPlane (1.41.1.0)
+
+=== Backward compatible API changes
+==== DMA
+* Add `odp_dma_compl_user_area()` function which returns pointer to the user
+area configured with DMA completion event pool parameters.
+* Clarify that DMA session names don't need to be unique.
+
+==== Event
+* Add `odp_event_user_area()` function which returns a pointer to the user area
+associated with the event.
+
+==== Init
+* Add `odp_term_abnormal()` function that can be used to abnormally terminate
+an ODP application after a non-recoverable error. Depending on the
+implementation, this function may attempt to dump stack and other memory areas,
+clean up and stop HW operations and/or perform other actions helpful in
+postmortem analysis.
+
+==== Packet
+* Extend packet transmit completion (`odp_packet_tx_compl_request()`) with poll
+mode. When enabled, packet transmit completion status can be checked with
+`odp_packet_tx_compl_done()`.
+* Add `odp_packet_free_ctrl_set()` function for controlling packet output
+interface to not free a packet after transmitting it.
+* Add `odp_packet_free_ctrl()` function for reading packet's free control
+option value.
+
+==== Pool
+* Relax `odp_pool_stats()` specification to allow implementations to have some
+delay until pool statistics are updated.
+* Add new function `odp_pool_stats_selected()` for reading only selected pool
+statistic(s).
+
+==== Timer
+* Split `odp_timer_cancel()` failure reason into timing related and other
+failures.
+* Rename `odp_timer_set_t` to `odp_timer_retval_t` as it is used now for start,
+restart, and cancel operations. `odp_timer_set_t` remains as a typedef for
+backward compatibility.
+* Add `odp_timer_sample_ticks()` function that samples tick value of multiple
+timer pools simultaneously.
+* Allow implementation to place the first timer as close as it can to one period
+when starting a periodic timer with `first_tick` set to 0.
+* Clarify that periodic timer base frequency uses fractional numbers the same
+way as timer tick info (`odp_timer_tick_info_t`). Integer part specifies full
+hertz and fractional part specifies parts of a hertz.
+* Clarify that `odp_timer_periodic_capability()` call does not overwrite the
+base frequency value when 1 is returned.
+* Clarify that some implementations may support only certain periodic timer base
+frequencies and those depend on source clock frequency.
+
+=== Performance Tests
+==== bench_timer
+* Add new test application for measuring call latency of small functions in the
+timer API.
+
+==== dma_perf
+* Refactor DMA test application to support multiple worker threads. The number
+of in-flight transfers can be configured and workers will always try to keep
+that many transfers active through single or per-worker DMA session.
+
+==== ipsecfwd
+* Add support for running the application in process mode.
+
+== OpenDataPlane (1.41.0.0)
+
+=== Backward incompatible API changes
+==== Classifier
+* Require that PMRs must be destroyed before the CoS they refer to is destroyed.
+
+==== Crypto
+* Deprecate the old session creation error names
+(`ODP_CRYPTO_SES_CREATE_ERR_NONE`, `ODP_CRYPTO_SES_CREATE_ERR_ENOMEM`,
+`ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER`, `ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH`)
+that have been replaced by shorter error names.
+* Change return value of `odp_crypto_result()` to indicate crypto operation
+success/failure.
+* Deprecate `odp_crypto_packet_result_t.ok` field. Replaced by
+`odp_crypto_result()` return value.
+* Specify that the status fields of `odp_crypto_packet_result_t` are valid
+only when the operation failed (`odp_crypto_result()` returned -1).
+* Deprecate `odp_crypto_hw_err_t` type and `odp_crypto_op_status_t.hw_err`
+field. All errors are now reported through `alg_err` field.
+* Deprecate `ODP_CRYPTO_ALG_ERR_KEY_SIZE` and `ODP_CRYPTO_ALG_ERR_IV_INVALID`
+error codes.
+* Require that cipher range (`odp_crypto_packet_op_param_t.cipher_range`) and
+auth range (`odp_crypto_packet_op_param_t.auth_range`) have zero offset and zero
+length when used with null cipher and null auth algorithm, respectively, with
+the out-of-place operation type.
+
+==== Errno
+* Remove mention about errno values specified in the API spec. Setting errno has
+been removed from all ODP APIs outside errno module.
+
+==== Packet IO
+* Remove legacy reference to errno from `odp_pktio_open()` function.
+
+==== Stash
+* Remove special meaning of `odp_stash_capability_t.max_num_obj` value zero.
+
+==== Timer
+* Remove legacy references to errno from `odp_timer_pool_create()` and
+`odp_timer_alloc()` functions.
+
+=== Backward compatible API changes
+==== Classifier
+* Clarify that the CoS to be destroyed with `odp_cos_destroy()` must not be in
+use.
+* Clarify that `odp_cos_queue()` returns `ODP_QUEUE_INVALID` if the queue is not
+set.
+* Allow CoS pool (`odp_cls_cos_param_t.pool`) to be set to `ODP_POOL_INVALID`,
+in which case the originating pktin pool is used.
+* Clarify when CoS queue may and may not be invalid.
+
+==== Crypto
+* Change IV (`odp_crypto_packet_op_param_t.cipher_iv_ptr`,
+`odp_crypto_packet_op_param_t.auth_iv_ptr`) and AAD (
+`odp_crypto_packet_op_param_t.aad_ptr`) pointers to pointers to constants.
+* Clarify that algorithm order (`odp_crypto_session_param_t.auth_cipher_text`)
+is ignored with null algorithms.
+* Clarify when cipher (`odp_crypto_packet_op_param_t.cipher_range`) and auth
+(`odp_crypto_packet_op_param_t.auth_range`) ranges are ignored.
+* Allow the result pointer for `odp_crypto_result()` to be null, making filling
+the result structure optional.
+* Clarify the description of `ODP_CRYPTO_ALG_ERR_DATA_SIZE`.
+* Add a new crypto operation error `ODP_CRYPTO_ALG_ERR_OTHER` to cover cases
+for which the other errors do not fit.
+* Clarify that null cipher and auth algorithms ignore key length, IV length,
+digest length, etc. session parameters.
+* Clarify that crypto operations do not affect parse flags in packet metadata
+and thus `odp_packet_has_error()` cannot be used for checking operation result.
+
+==== Packet IO
+* Allow the default CoS to be removed by passing `ODP_COS_INVALID` to
+`odp_pktio_default_cos_set()`.
+
+==== Pool
+* Clarify that the pool to be destroyed with `odp_pool_destroy()` must not be in
+use.
+* Clarify that `odp_pool_capability_t.max_pools` is used for all pool types
+defined in `odp_pool_type_t`.
+
+==== Stash
+* Add new stash create parameter `odp_stash_param_t.strict_size` for performance
+optimization. The new option is disabled by default and the total object count
+limitation is removed from stash put functions.
+* Add new capabilities for maximum number of object handles per stash for
+each object size (`odp_stash_capability_t.max_num`).
+
+==== Timer
+* Clarify that zero `odp_timer_periodic_start_t.first_tick` means that the first
+expiration time is one period after the current time, not at the current time.
+
+=== Remove deprecated APIs
+==== Crypto
+* Remove deprecated `odp_crypto_compl_t` crypto completion event.
+* Remove deprecated `odp_crypto_op_param_t` type.
+* Remove deprecated `odp_crypto_op_result_t` type.
+* Remove deprecated `odp_crypto_session_param_t.pref_mode` field.
+* Remove deprecated `odp_crypto_compl_from_event()` function.
+* Remove deprecated `odp_crypto_compl_to_event()` function.
+* Remove deprecated `odp_crypto_compl_free()` function.
+* Remove deprecated `odp_crypto_operation()` function.
+* Remove deprecated `odp_crypto_compl_result()` function.
+* Remove deprecated `odp_crypto_compl_to_u64()` function.
+
+==== Event
+* Remove deprecated `ODP_EVENT_CRYPTO_COMPL` event type.
+
+=== Implementation
+==== Packet IO
+* Remove netmap pktio device.
+* Change recommended DPDK version for DPDK pktio device to v22.11.
+
+==== Stash
+* Change implementation to use overflow safe MPMC rings by default. Previous
+strict size ring-based implementation can be used by enabling
+`odp_stash_param_t.strict_size` parameter.
+
+== OpenDataPlane (1.40.0.0)
+
+=== Backward incompatible API changes
+==== Packet
+* Specify which packet metadata flags cannot be set simultaneously using
+`odp_packet_has_XX_set()` functions.
+
+==== Timer
+* Use `ODP_DEPRECATE()` macro for `odp_timer_set_abs()` and
+`odp_timer_set_rel()` functions. Previously, the deprecation was only mentioned
+in the function documentation.
+* Deprecate old timer pool clock sources `ODP_CLOCK_CPU` and `ODP_CLOCK_EXT`,
+which have been replaced by `ODP_CLOCK_DEFAULT` and `ODP_CLOCK_SRC_1`.
+* Deprecate old timer set return values `ODP_TIMER_TOOEARLY`,
+`ODP_TIMER_TOOLATE`, and `ODP_TIMER_NOEVENT`, which have been replaced by
+`ODP_TIMER_TOO_NEAR`, `ODP_TIMER_TOO_FAR`, and `ODP_TIMER_FAIL`.
+
+=== Backward compatible API changes
+==== Crypto
+* Add new operation type session parameter
+(`odp_crypto_session_param_t.op_type`) that controls how crypto operations
+interpret their parameters and handle output packets. Defaults to backward
+compatible `ODP_CRYPTO_OP_TYPE_LEGACY` mode.
+* Add `ODP_CRYPTO_OP_TYPE_BASIC` operation type with simplified interface
+compared to `ODP_CRYPTO_OP_TYPE_LEGACY`.
+* Add `ODP_CRYPTO_OP_TYPE_OOP` operation type that writes the output of the
+crypto operation into a caller provided output packet and does not consume the
+input packet.
+* Clarify that `odp_crypto_op()` copies all packet data and metadata from the
+input packet to the output packet in `ODP_CRYPTO_OP_TYPE_LEGACY` and
+`ODP_CRYPTO_OP_TYPE_BASIC` modes.
+* Require that `odp_crypto_result()` is called before packet data of
+asynchronously processed packets can be assumed to be valid in non-legacy modes.
+* Fix EIA2 IV length in API documentation. EIA2 uses 64-bit IV, not 128-bit as
+previously mentioned in the API text.
+
+==== Packet
+* Clarify `odp_packet_has_vlan()` and `odp_packet_has_vlan_qinq()`
+specifications.
+
+=== Remove deprecated APIs
+==== Crypto
+* Remove deprecated per-session IV configuration.
+
+==== Packet IO
+* Remove deprecated `in_unknown_protos` field from `odp_pktio_stats_t`.
+* Remove deprecated `odp_pktin_ts_res()` function.
+* Remove deprecated `odp_pktin_ts_from_ns()` function.
+
+==== Shared Memory
+* Remove deprecated `ODP_SHM_SW_ONLY` define.
+
+==== Traffic Manager
+* Remove deprecated `odp_tm_capabilities()` function.
+* Remove deprecated `commit_bps` field from `odp_tm_shaper_params_t`.
+* Remove deprecated `peak_bps` field from `odp_tm_shaper_params_t`.
+
+=== Implementation
+==== Crypto
+* Add Multi-Buffer Crypto for IPsec library (Arm optimized) based crypto
+implementation. See `DEPENDENCIES` for additional information.
+
+==== Debug
+* Add support for runtime event validation (buffer endmark checking). Event
+validation can be enabled during configure with
+`--enable-event-validation [warn/abort]` or with `--enabled-debug=full`. See
+`README` for additional information.
+
+== OpenDataPlane (1.39.0.0)
+
+=== Backward incompatible API changes
+==== Classifier
+* Deprecate `odp_cos_with_l2_priority()` function. Use `ODP_PMR_VLAN_PCP_0`
+instead.
+* Deprecate packet drop policy option (`odp_cls_cos_param_t.drop_policy`) and
+related functions `odp_cos_drop()` and `odp_cos_drop_set()`.
+
+==== Shared Memory
+* Change `odp_shm_info()` specification to disallow usage of invalid SHM
+handles.
+
+=== Backward compatible API changes
+==== Buffer
+* Add multi variants of event conversion functions
+(`odp_buffer_from_event_multi()` and `odp_buffer_to_event_multi()`).
+
+==== Classifier
+* Add PFC priority level (`odp_bp_param_t.pfc_level`) to back pressure
+parameters.
+* Clarify PMR specification to state that in case of multiple PMRs matching
+within a CoS, it is implementation specific which PMR is selected.
+
+==== Crypto
+* Fix a stale reference to the renamed `hash_result_not_in_auth_range`
+session parameter to use the correct name (`hash_result_in_auth_range`)
+in the comment text of `hash_result_offset`.
+
+==== Packet
+* Add support for additional L3 and L4 protocol types.
+
+==== Packet IO
+* Add `ODP_PKTIN_MAX_QUEUES` define for maximum number of packet input queues.
+* Add new packet input queue size configuration option
+`odp_pktin_queue_param_t.queue_size` and matching capabilities
+`odp_pktio_capability_t.min_input_queue_size` and
+`odp_pktio_capability_t.max_input_queue_size`.
+* Add missing documentation to `odp_pktio_link_status_t`,
+`odp_pktio_link_duplex_t`, and `odp_pktio_link_pause_t` enumerations.
+* Add `ODP_PKTIO_LINK_PFC_ON` enumeration for PFC flow control mode.
+* Add capabilities (`odp_pktio_capability_t.flow_control`) and configuration
+parameters to control reception (`odp_pktio_config_t.flow_control.pause_rx`) and
+transmission (`odp_pktio_config_t.flow_control.pause_tx`) of Ethernet pause
+frames.
+
+==== Shared Memory
+* Add `odp_shm_segment_info()` function for retrieving information about each
+memory segment of an SHM block.
+* Clarified `odp_shm_reserve()` operation with the default options (no flags).
+
+==== System
+* Add `odp_system_meminfo()` function for retrieving information about ODP
+memory usage.
+
+== OpenDataPlane (1.38.0.0)
+
+=== Backward incompatible API changes
+==== Pool
+* Change `odp_pool_capability_t.pkt.max_uarea_size` specification to state
+that the value of zero means user area is not supported.
+* Specify that the default value of `odp_pool_param_t.pkt.uarea_size` is zero
+and implementation may round up the given value.
+
+=== Backward compatible API changes
+==== Buffer
+* Add `odp_buffer_user_area()` function which returns pointer to the user area
+configured with pool create parameters.
+
+==== Crypto
+* Add experimental ZUC-256 support.
+
+==== Packet
+* Add `odp_packet_vector_user_area()` function which returns pointer to the user
+area configured with pool create parameters.
+* Add new user flag metadata to packets (`odp_packet_user_flag()`,
+`odp_packet_user_flag_set()`) and packet vectors
+(`odp_packet_vector_user_flag()`, `odp_packet_vector_user_flag_set()`).
+
+==== Pool
+* Add user area size capability and parameter into buffer, timeout, and vector
+event pools.
+
+==== Stash
+* Add batch variants of all put/get functions and capabilities for maximum
+supported batch sizes.
+
+==== Thread
+* Clarify `odp_thread_id()` specification to state that thread IDs are assigned
+sequentially starting from 0 in the order threads call `odp_init_local()`.
+
+==== Timer
+* Add `odp_timeout_user_area()` function which returns pointer to the user area
+configured with pool create parameters.
+
+== OpenDataPlane (1.37.2.0)
+
+=== Backward compatible API changes
+==== CPU Mask
+* Allow usage of NULL pointer with `odp_cpumask_default_worker()` and
+`odp_cpumask_default_control()` calls. This enables applications to check the
+number of worker/control CPUs without allocating a temporary mask.
+* Clarify `odp_cpumask_default_worker()` and `odp_cpumask_default_control()`
+specifications to mention that system may allow usage of other CPUs as well.
+
+==== Packet IO
+* Specify that interfaces that support promiscuous mode set operation have
+promiscuous mode disabled by default.
+
+==== Pool
+* Add new statistics counters (`odp_pool_stats_t.thread.cache_available`) for
+reading per thread pool cache usage.
+
+==== Stash
+* Add `odp_stash_print()` function for printing implementation specific debug
+information to the ODP log.
+* Add statistics counters to stash API. Counter support is defined by stash
+capabilities (`odp_stash_capability_t.stats`). Supported counters can be enabled
+in `odp_stash_create()` and read with `odp_stash_stats()`.
+
+== OpenDataPlane (1.37.1.0)
+
+=== Backward compatible API changes
+==== Packet IO
+* Clarify that `odp_pktout_send_lso()` can be used also for packets that have
+payload less than `max_payload_len` bytes.
+
+==== Stash
+* Change 32-bit and 64-bit specific get/put functions' parameter names to avoid
+name conflicts.
+
+==== Traffic Manager
+* Add option to do rate limiting instead of rate shaping in TM nodes and queues.
+
+== OpenDataPlane (1.37.0.0)
+
+=== Backward incompatible API changes
+==== Classifier
+* Deprecate `odp_cos_with_l3_qos()` function. Use new `ODP_PMR_IP_DSCP` PMR
+term instead.
+
+=== Backward compatible API changes
+==== Classifier
+* Add new PMR term enumeration `ODP_PMR_IP_DSCP` for Differentiated Services
+Code Point (DSCP) bits in IP header.
+* Add new PMR term enumeration `ODP_PMR_VLAN_PCP_0` for Priority Code Point
+(PCP) bits in VLAN header.
+* Clarify `ODP_PMR_ETHTYPE_0`, `ODP_PMR_VLAN_ID_0`, and `ODP_PMR_VLAN_ID_X`
+PMR term specifications.
+* Remove unused `odp_cos_hdr_flow_fields_t` enumeration.
+
+==== Scheduler
+* Add new `odp_schedule_order_wait()` function which waits until the currently
+held scheduling context is the first in order.
+
+=== Implementation
+==== Packet IO
+* Add new experimental AF_XDP socket based packet IO device.
+
+== OpenDataPlane (1.36.0.0)
+
+=== Backward incompatible API changes
+
+==== Classifier
+* Add an action parameter `odp_cos_action_t` to CoS parameters
+(`odp_cls_cos_param_t`). Action may be to enqueue or drop the packet classified
+to the CoS. The old methods of creating a drop CoS have been replaced by the
+new drop action.
+
+==== Crypto
+* Deprecate `odp_crypto_operation()`, the associated data structures, and the
+completion event. Use `odp_crypto_op()` or `odp_crypto_op_enq()` instead.
+
+==== Traffic Manager
+* Split `odp_tm_capabilities_t.tm_queue_threshold` capability into byte and
+packet modes.
+* Split `odp_tm_level_capabilities_t.tm_node_threshold` capability into byte and
+packet modes.
+
+=== Backward compatible API changes
+==== Classifier
+* Add CoS specific statistics counters (`odp_cls_cos_stats_t`) and
+matching capabilities (`odp_cls_stats_capability_t`). Statistics counters can be
+read with `odp_cls_cos_stats()`.
+
+==== Common
+* Convert `unsigned int` types to `uint32_t`.
+
+==== Traffic Manager
+* Remove unused TM shaper color enum `odp_tm_shaper_color_t` and
+`ODP_NUM_SHAPER_COLORS` define.
+
+== OpenDataPlane (1.35.0.0)
+
+=== Backward incompatible API changes
+
+==== Scheduler
+* Deprecate scheduling priority level defines `ODP_SCHED_PRIO_HIGHEST`,
+`ODP_SCHED_PRIO_NORMAL`, `ODP_SCHED_PRIO_LOWEST`, and `ODP_SCHED_PRIO_DEFAULT`.
+These defines have been replaced by matching functions.
+
+==== Traffic Manager
+* Change meaning of `odp_tm_enq_multi()` return value of zero from failure code
+to part of normal operation.
+
+=== Backward compatible API changes
+==== Packet
+* Remove references to deprecated `odp_pktin_ts_res()` and
+`odp_pktin_ts_from_ns()` functions.
+
+==== Packet IO
+* Add `ODP_PKTOUT_MAX_QUEUES` define for the maximum number of packet output
+queues.
+* Add new packet output queue size configuration option
+`odp_pktout_queue_param_t.queue_size` and matching capabilities
+`odp_pktio_capability_t.min_output_queue_size` and
+`odp_pktio_capability_t.max_output_queue_size`.
+* Clarify `odp_pktio_config()` usage in the interface setup sequence.
+* Allow large send offload (LSO) usage with traffic manager
+(`odp_pktio_config_t.enable_lso`).
+
+==== Timer
+* Clarify timer tick properties and especially that it may run with a higher
+frequency than the requested timer pool resolution.
+* Add new timer pool parameter (`odp_timer_pool_param_t.exp_mode`), which
+application can use to select if timer expiration may happen before or only
+after the specified time.
+* Add new `odp_timer_start()` and `odp_timer_restart()` functions.
+* Add support for periodic timers. New capabilities, parameters, and functions
+are added to create/start/ack periodic timers.
+
+==== Traffic Manager
+* Add `odp_tm_enq_multi_lso()` function which does also LSO as part of the TM
+output operation.
+
+=== Remove deprecated APIs
+==== Classifier
+* Remove deprecated `ODP_PMR_INVAL` define.
+
+==== Crypto
+* Remove deprecated old style cipher algorithm enumerations
+`ODP_CIPHER_ALG_AES128_CBC` and `ODP_CIPHER_ALG_AES128_GCM`.
+* Remove deprecated old style authentication algorithm enumerations
+`ODP_AUTH_ALG_MD5_96`, `ODP_AUTH_ALG_SHA256_128`, and `ODP_AUTH_ALG_AES128_GCM`.
+* Remove deprecated fields `aes128_cbc` and `aes128_gcm` from
+`odp_crypto_cipher_algos_t`.
+* Remove deprecated fields `md5_96`, `sha256_128`, and `aes128_gcm` from
+`odp_crypto_auth_algos_t`.
+* Remove deprecated data range specifier `odp_crypto_data_range_t`.
+* Remove deprecated `iv` field from `odp_crypto_session_param_t`.
+* Remove deprecated `odp_crypto_session_params_t` type.
+* Remove deprecated `override_iv_ptr` field from `odp_crypto_op_param_t`.
+* Remove deprecated `override_iv_ptr` field from `odp_crypto_packet_op_param_t`.
+* Remove deprecated `odp_crypto_op_params_t` type.
+* Remove deprecated `odp_crypto_compl_status_t` type.
+
+==== Packet IO
+* Remove deprecated parser layer defines (`ODP_PKTIO_PARSER_LAYER_*`).
+* Remove deprecated `odp_pktio_capability_t.loop_supported` capability.
+* Remove deprecated `odp_pktio_mtu()` function.
+
+==== Queue
+* Remove deprecated scheduled queue capabilities from `odp_queue_capability_t`.
+
+==== Miscellaneous
+* Remove deprecated `odp.h` header file which has been replaced by `odp_api.h`.
+
+== OpenDataPlane (1.34.0.0)
+
+=== Backward incompatible API changes
+==== Crypto
+* Specify that hash result field is not cleared in hash generation in encode
+sessions. Applications can do the clearing if needed if the hash result lies
+within the authenticated range.
+* Specify that the hash result field is left to an undefined value after
+verification.
+* Add a new session parameter
+`odp_crypto_session_param_t.hash_result_in_auth_range` to indicate if the hash
+result location may overlap the authenticated range. Leaving the flag unset may
+enable optimizations in the implementation.
+* Define new fields for cipher and auth IV lengths in the session parameter
+structure `odp_crypto_session_param_t`. Deprecated the old fields for cipher and
+auth IV lengths.
+* Deprecate the mechanism of configuring initialization vectors in sessions
+since reusing the same IV with the same key is almost always wrong. Require that
+IV is provided for each packet in the operation parameters.
+
+==== Traffic Manager
+* Add capabilities to `odp_tm_level_capabilities_t` for platforms to express
+shaper rate and burst min and max limits.
+* Remove support for min and max shaper rate via platform defined macros
+`ODP_TM_MIN_SHAPER_BW` and `ODP_TM_MAX_SHAPER_BW`. Min and Max shaper bandwidths
+can be read from TM per level capabilities (`odp_tm_level_capabilities_t`).
+
+=== Backward compatible API changes
+==== Crypto
+* Clarify that in case of AEAD algorithms the `odp_crypto_op_param_t.auth_range`
+parameter is not used, except with AES-GMAC.
+* Clarify `ODP_AUTH_ALG_AES_GMAC` API text to not sound as if ODP did not use
+AAD as defined in the GMAC algorithm specification.
+* Make the names of session creation errors (`odp_crypto_ses_create_err_t`) a
+bit shorter but retain the old names for backward compatibility.
+* Add crypto session creation error codes (`odp_crypto_ses_create_err_t`) for
+cases where the requested combination of algorithms, order of algorithms or
+other combination of creation parameters is not supported.
+
+==== Random
+* Explicitly state that the same series of `odp_random_test_data()` calls is
+required to generate the same data.
+
+==== Traffic Manager
+* Clarify that peak rate and peak burst are ignored when dual rate is set to
+false in `odp_tm_shaper_params_t`, indicating that the single rate is commit
+rate.
+* Define the default values for many parameters to be set by various init
+functions.
+
+=== ABI changes
+* Removed mips64 architecture support. MIPS devices may still use ODP by
+utilizing the generic default architecture.
+
+== OpenDataPlane (1.33.0.0)
+
+=== Backward incompatible API changes
+==== Shared Memory
+* Added a bit mask capability `odp_shm_capability_t.flags` for ODP_SHM_* flags
+
+==== Timer
+* Added initialization function `odp_timer_pool_param_init()` for timer pool
+parameters. Application must use it to initialize parameters to their
+default values.
+
+=== Backward compatible API changes
+==== Classifier
+* Added missing default values for `odp_cls_cos_param_t.num_queue`,
+`odp_cls_cos_param_t.red.enable`, `odp_cls_cos_param_t.bp.enable`, and
+`odp_pmr_param_t.range_term`.
+
+==== Crypto
+* Clarified that `odp_crypto_session_create()` parameters, including the key and
+IV data, can be freed after session creation.
+
+==== DMA
+* Added new DMA module which enables applications to offload memory transfers
+(copies) to DMA hardware. See `include/odp/api/spec/dma.h` for more information.
+
+==== IPsec
+* Added possibility to request completed packets as packet vector events
+instead of packet events. Packet vector capabilities are provided by
+`odp_ipsec_capability_t.vector` and the configuration is done using
+`odp_ipsec_config_t.vector`.
+* Clarified that `odp_ipsec_sa_create()` parameters, including the various
+memory buffers pointed to by the parameters, can be freed after SA creation.
+
+==== Packet IO
+* Clarified `odp_pktin_vector_config_t.enable` documentation to state that when
+packet vectors are enabled, packets may be delivered both as packet vector
+events and packet events. Packet vectors are disabled by default.
+* Clarified that the type of input queues (scheduled versus plain) is deduced
+from the pktio input mode in `odp_pktin_queue_config()`, and that the default
+queue type value and the queue type value passed in
+`odp_pktin_queue_param_t.queue_param` are ignored.
+
+=== Pool
+* Added new pool type for DMA completion event pools. These pools are created
+with `odp_dma_pool_create()` and pool capability is included in
+`odp_dma_capability_t`. Otherwise, pool APIs are used normally for these pools.
+
+==== Shared Memory
+* Added `ODP_SHM_NO_HP` flag which can be used to prevent the implementation
+from allocating the shared memory block from huge pages
+
+==== Std
+* Added DMA flag to `odp_feature_t`
+
+== OpenDataPlane (1.32.1.0)
+=== Backward compatible API changes
+==== Init
+* Added `odp_instance()` function for reading the current ODP instance handle
+
+==== IPsec
+* Clarified bit field unions' `all` member intended usage
+
+==== Packet
+* Clarified `odp_proto_chksums_t.all_chksum` intended usage
+
+==== Packet IO
+* Clarified `odp_pktin_hash_proto_t.all_bits` intended usage
+
+==== Pool
+* Added `odp_pool_print_all()` function for printing implementation defined
+information about all created pools to the ODP log
+* Clarified `odp_pool_param_t.pkt.seg_len` documentation
+
+==== Stash
+* Added 32-bit only `odp_stash_put_u32()` and `odp_stash_get_u32()` functions
+* Added 64-bit only `odp_stash_put_u64()` and `odp_stash_get_u64()` functions
+* Added pointer only `odp_stash_put_ptr()` and `odp_stash_get_ptr()` functions
+
+=== Helper (1.3.0)
+==== Backward incompatible changes
+===== CLI
+* Removed unused `instance` parameter from `odph_cli_init()`
+
+===== Deprecation Framework
+Added a deprecation framework to enable controlled API deprecation.
+
+When a helper API is deprecated, validation tests will be updated to use the
+replacement API. By default, attempts to compile code with deprecated helper
+APIs will fail. To make moving to a new API version easier, helper library
+supports `--enable-helper-deprecated` `configure` option, which makes the
+deprecated APIs visible again.
+
+===== Linux
+* Deprecated `odph_odpthreads_create()` function. Use `odph_thread_create()`
+instead.
+* Deprecated `odph_odpthreads_join()` function. Use `odph_thread_join()`
+instead.
+* Deprecated unused `odph_thread_param_t.instance` struct member
+* Deprecated `odph_odpthread_t` and `odph_odpthread_params_t` types
+
+== OpenDataPlane (1.32.0.0)
+=== Backward incompatible API changes
+==== IPsec
+* Added destination queue capabilities `odp_ipsec_capability_t.queue_type_sched`
+and `odp_ipsec_capability_t.queue_type_plain`
+* Modified specification to not promise the original packet back after all error
+cases
+
+=== Backward compatible API changes
+==== IPsec
+* Added original ESP/AH packet length into IPsec packet result
+(`odp_ipsec_packet_result_t.orig_ip_len`)
+
+==== Packet
+* Added `odp_packet_reass_info()` function for reading completed reassembly
+details
+
+==== Std
+* Moved all contents of support and feature modules into ODP Std module
+
+=== Helper (1.2.0)
+==== Backward incompatible changes
+===== CLI
+* Replaced `odph_cli_start()` function with `odph_cli_run()`, which doesn't
+automatically create a separate thread for running the CLI server.
+
+===== Linux
+* Deprecated `odph_odpthread_t` and `odph_odpthread_params_t` types
+* Added `odph_thread_param_init()` function for initializing thread parameters
+* Added `odph_thread_common_param_init()` function for initializing thread
+common parameters
+
+==== Backward compatible changes
+===== CLI
+* Added `odph_cli_param_t.hostname` parameter for providing a custom CLI prompt
+hostname
+* Added `odph_cli_log_va()` function for user defined CLI commands
+
+===== Linux
+* Added `odph_thread_param_t.stack_size` parameter for configuring minimum
+thread stack size
+* Added `odph_thread_common_param_t.sync_timeout` parameter for configuring
+synchronized thread creation timeout
+
+=== Implementation
+==== Crypto
+* AES-EEA2 and AES-EIA2 crypto algorithms that operate in bit mode interpret the
+crypto/auth offset now as bits as specified in the ODP API. This correction
+requires corresponding changes in applications that have relied on the old,
+incorrect behavior that interpreted the offset in bytes.
+
+== OpenDataPlane (1.31.0.0)
+=== Backward incompatible API changes
+==== Traffic Manager
+* Added new TM feature capabilities and an egress specific capability function
+`odp_tm_egress_capabilities()`
+* Deprecated `odp_tm_capabilities()` function which is replaced by
+`odp_tm_egress_capabilities()`
+* Added `odp_tm_capabilities_t.max_schedulers_per_node` capability to express
+the maximum number of schedulers per TM node
+* Added support for non-global packet priority mode
+
+=== Backward compatible API changes
+==== Classifier
+* Added queue specific statistics counters (`odp_cls_queue_stats()`)
+
+==== IPsec
+* Added ICV length into SA configuration parameters
+(`odp_ipsec_crypto_param_t.icv_len`)
+
+==== Packet
+* Moved packet type definitions into a separate `packet_types.h` header to
+enable easier function inlining
+* Added `odp_packet_disassemble()`, `odp_packet_reassemble()`, and other new
+functions for packets allocated from external memory pools
+* Added packet protocol statistics functions `odp_packet_proto_stats_request()`
+and `odp_packet_proto_stats()`
+
+==== Packet IO
+* Added `odp_pktout_config_opt_t.bit.proto_stats_ena` option for enabling packet
+protocol statistics updates
+
+==== Pool
+* Added new concept of external memory pools, which are populated with
+application provided memory
+* Added new `odp_pool_type_t` enumeration
+* Moved pool type definitions into a separate `pool_types.h` header to enable
+easier function inlining
+
+==== Protocol Stats
+* Added new generic protocol statistics framework
+
+==== Queue
+* Moved queue type definitions into a separate `queue_types.h` header to enable
+easier function inlining
+
+==== Std
+* Renamed std_clib module std and moved all generic ODP data types and functions
+there
+
+==== Traffic Manager
+* Increased scheduling weight parameter size (`uint8_t` to `uint32_t`)
+* Added queue specific statistics counters (`odp_tm_queue_stats()`)
+* Added `odp_tm_enq_multi()` function for enqueueing multiple packets at a time
+* Added `odp_tm_queue_params_t.ordered_enqueue` option which can be used to
+control if ordering is enabled
+
+=== Helper (1.1.1)
+* Added `odph_ipsec_auth_icv_len_default()` function for returning the default
+ICV length of an algorithm
+* Added support for `AES-CMAC` into `odph_ipsec_alg_check()`
+* Added default ICV length check into `odph_ipsec_alg_check()`
+
+== OpenDataPlane (1.30.1.0)
+=== API
+==== Packet IO
+* Modified `odp_pktio_stats_t.in_octets/out_octets` definitions to not include
+CRC (4 bytes)
+* Added `odp_pktio_stats_capability_t` struct to `odp_pktio_capability_t` to
+inform about supported statistics counters
+* Added new statistics counters and related capabilities for
+received/transmitted multicast and broadcast Ethernet packets (`in_mcast_pkts`,
+`in_bcast_pkts`, `out_mcast_pkts`, `out_bcast_pkts`)
+* Added new pktio input/output queue specific statistics counters
+(`odp_pktin_queue_stats_t` and `odp_pktout_queue_stats_t`) and related
+functions and capabilities
+* Added new functions for reading and printing ODP implementation specific
+custom packet IO statistics counters: `odp_pktio_extra_stat_info()`,
+`odp_pktio_extra_stats()`, `odp_pktio_extra_stats_print()`
+* Specified that the default value of `odp_pktio_config_t.enable_loop` option is
+false
+
+==== IPsec
+* Specified that the default values of the IPsec specific reassembly enables
+are false
+
+==== Scheduler
+* Added `odp_schedule_print()` function for printing debug information about
+scheduler status into the ODP log
+
+==== Standard Types
+* Added `odp_fract_u64_t` type which can be used to define fractional numbers
+accurately
+* Added `odp_fract_u64_to_dbl()` function for converting `odp_fract_u64_t`
+fractional numbers into doubles
+
+==== Timer
+* Added timer tick info as part of `odp_timer_pool_info_t`. This enables timer
+implementation to specify tick frequency and duration very accurately.
+
+== OpenDataPlane (1.30.0.0)
+=== API
+==== IPsec
+* New success bytes field in stats (`odp_ipsec_stats_t.success_bytes`)
+
+==== Packet IO
+* Specify explicitly that an application may enqueue to packet input side event
+queues, and cannot dequeue from output side event queues
+
+==== Time
+* Added time stamp read functions which read time stamp value more strictly
+in the program order: `odp_time_local_strict()`, `odp_time_local_strict_ns()`,
+`odp_time_global_strict()`, `odp_time_global_strict_ns()`
+
+==== Timer
+* Added new default clock source enumeration `ODP_CLOCK_DEFAULT`
+(=`ODP_CLOCK_SRC_0`) and support for multiple clock sources (`ODP_CLOCK_SRC_1`,
+`ODP_CLOCK_SRC_2`...). The old `ODP_CLOCK_CPU` and `ODP_CLOCK_EXT` enumerations
+will be deprecated in the future.
+* Renamed set operation return codes (`odp_timer_set_t`) to better document
+expiration time position to current time:
+`ODP_TIMER_TOOEARLY` -> `ODP_TIMER_TOO_NEAR`, `ODP_TIMER_TOOLATE` ->
+`ODP_TIMER_TOO_FAR`
+* Renamed set operation failure code (`odp_timer_set_t`) to cover all error
+cases: `ODP_TIMER_NOEVENT` -> `ODP_TIMER_FAIL`
+
+==== Traffic Manager
+* Added option to enable packet mode shaper (packets per second as opposed to
+bits per second)
+* Added new mandatory `odp_tm_start()` and `odp_tm_stop()` calls for starting and
+stopping traffic manager system
+* Added capabilities (`odp_tm_capabilities_t`) for supported dynamic
+configuration updates
+* Deprecated shaper commit information rate bps
+(`odp_tm_shaper_params_t.commit_bps`) and peak information rate bps
+(`odp_tm_shaper_params_t.peak_bps`) fields. `odp_tm_shaper_params_t.commit_rate`
+and `odp_tm_shaper_params_t.peak_rate` fields should be used instead.
+
+=== Helper (1.1.0)
+* Added new mandatory `odph_cli_init()` and `odph_cli_term()` functions for
+initializing and terminating the CLI helper
+* Added `odph_cli_register_command()` function for registering user defined CLI
+commands
+
+== OpenDataPlane (1.29.0.0)
+=== API
+==== Packet IO
+* Modified statistics counters (`odp_pktio_stats_t`) definitions
+* Deprecated statistics counter `odp_pktio_stats_t.in_unknown_protos`
+* New `odp_pktio_stats_t.in_packets` and `odp_pktio_stats_t.out_packets`
+statistics counters
+* New APIs for packet input inline IP reassembly offload
+
+==== Packet
+* New `odp_packet_reass_status()` function added for checking packet reassembly
+status
+* New `odp_packet_reass_partial_state()` function added for reading packet
+partial reassembly state
+
+==== IPsec
+* New APIs for inline IP reassembly offload
+
+==== Init
+* New `odp_log_thread_fn_set()` function added for setting a thread specific log
+function
+
+=== Implementation
+* ABI compatibility default value has been changed to disabled. User can enable
+ABI compatibility with `--enable-abi-compat` configure option.
+
+== OpenDataPlane (1.28.0.0)
+=== API
+==== Crypto
+* New crypto capabilities for defining if a queue type (scheduled/plain) can be
+used as a crypto completion event destination
+
+==== Event
+* New packet TX completion event type and related functions
+
+==== Packet
+* New packet APIs for requesting packet TX completion events
+* New packet APIs for requesting packet TX drop based on packet age
+
+==== Classifier
+* New `odp_cls_print_all()` function for printing implementation specific
+debug information about all classifier rules
+
+==== System
+* New enumerations for ARMv8.7-A, ARMv9.0-A, ARMv9.1-A, and ARMv9.2-A ISA
+versions
+
+== OpenDataPlane (1.24.0.0)
+=== Summary of Changes
+
+This release introduces a new stash API module. The other main API additions are
+pool buffer caching configuration and packet IO link information. The release
+also includes several smaller API improvements and clarifications.
+
+=== API
+==== Common
+* Added missing const qualifiers
+
+Some API calls missed const qualifiers on read-only data pointers.
+
+* Improved Doxygen module descriptions
+* Use param_init functions for parameter defaults
+
+When available, parameter init functions must be used to initialize parameters
+into their default values.
+
+=== Align
+* Added `ODP_CACHE_LINE_ROUNDUP` macro
+
+Added macro for rounding up a value to the next multiple of cache line size.
+This round up is needed e.g. when selecting buffer sizes so that false sharing
+is avoided.
+
+==== CPU
+* Make supporting CPU frequency and cycle counter optional
+
+CPU frequencies or CPU cycle counter may not be available on all HW/SW
+platforms. Zero is returned if those cannot be read.
+
+==== Feature
+* Added feature bits `stash` and `compress` into `odp_feature_t`.
+
+==== Packet
+* Clarify packet length function argument definitions
+
+Modify documentations of functions, which decrease packet length, to clearly
+state what are the allowed values for length argument. This is done to avoid
+creating zero length packets which are not allowed by the packet API.
+
+* Added `odp_packet_input_set()` function
+
+An application may use this for testing or other purposes, when perception of
+the packet input interface need to be changed.
+
+==== Packet I/O
+* Added `odp_pktio_link_info()` function for reading link status information
+** Autonegotiation mode (unknown/enabled/disabled)
+** Duplex mode (unknown/half duplex/full duplex)
+** Flow control (unknown/on/off)
+** Link status (unknown/up/down)
+** Media (media type as string)
+** Speed (unknown/Mbps)
+
+* Modified `odp_pktio_link_status()` to return `odp_pktio_link_status_t` enum
+(backward compatible values)
+
+==== Pool
+* Added `cache_size` parameters to `odp_pool_capability_t` and `odp_pool_param_t`
+
+Added thread local cache size parameter and capability. This allows application
+to control thread local caching and prepare large enough pool when
+implementation caches events per thread. The default value is implementation
+specific for backwards compatibility.
+
+* Removed default value of packet `max_len` from `odp_pool_param_t`
+
+The default value is implementation specific and may not be equal to the maximum
+capability.
+
+* Added packet data `align` parameter to `odp_pool_param_t`
+
+Added packet pool parameter to request minimum data alignment for user allocated
+packets. When user allocates a new packet and fills in protocol headers, it's
+convenient that data alignment does not need to be checked (and tuned) on each
+allocated packet.
+
+==== Queue
+* Unify `max_size capa` specification for all plain queue types
+
+Specify queue maximum size capability the same way for all non-blocking levels
+(`ODP_BLOCKING`, `ODP_NONBLOCKING_LF` and `ODP_NONBLOCKING_WF`). Max_size value
+of zero means that there is no size limit.
+
+* Clarify that queue operations include memory barriers
+
+Clarify that queue enqueue operations include memory barrier of release
+semantics and queue dequeue operations include acquire semantics.
+
+==== Random
+* Clarify how much data `odp_random_data()` and `odp_random_test_data()` output on success.
+
+It may not be possible for random generator functions to return requested number
+of bytes. Clarify that implementation is not required to loop until `len` bytes
+are available. Instead application should contain such logic.
+
+==== Scheduler
+* Clarify synchronization of store operations during atomic context
+
+Stores performed while holding an atomic scheduling context are seen correctly
+by other thread when they hold the same context later on. This is guaranteed
+also when queue enqueue is not used in between.
+
+* Clarify that schedule operations include memory barriers
+
+Clarify that event schedule operations include memory barrier of acquire
+semantics.
+
+==== Shared Memory
+* Add `ODP_SHM_HW_ACCESS` flag
+
+This can be used to memory allocations where both CPUs and HW accelerators
+access the same memory area. These HW accelerators may be programmed outside of
+ODP APIs, but the memory is reserved and shared normally inside/between ODP
+applications.
+
+==== Stash
+* Added new stash API module
+
+Application needs often store object handles for later usage. From current APIs,
+e.g. buffers and queues could be used to store these handles, but buffers
+consume more memory than is necessary and event queues are not needed for this
+simple use case. This new API maybe implemented e.g. as a ring of object handles
+in memory, or with a HW buffer manager.
+
+==== Time
+* Added `odp_time_local_ns()` and `odp_time_global_ns()` functions for acquiring
+current time stamp in nanoseconds
+
+Added functions to get the current local/global
+time stamp directly in nanoseconds. For example, `odp_time_local_ns()` is
+equivalent of calling `odp_time_to_ns(odp_time_local())`. This simplifies use
+cases where time will be always converted to nanoseconds. However, when time API
+performance is important conversions to nanoseconds should be avoided or
+minimized.
+
+==== Timer
+* Clarify that `odp_timeout_tick()` returns original expiration time
+
+Specification was open if returned expiration time is the original or actual
+expiration time. HW based implementations will not likely modify timeout event
+with actual expiration time. Also original expiration time is more valuable to
+an application as it can be used to calculate e.g. the next period.
+
+* Add resolution in hertz parameter `res_hz` into `odp_timer_pool_param_t`
+
+Added option to specify timer pool resolution in hertz. High resolution values
+in nanoseconds result small numbers and thus poor granularity. Hertz offers
+better granularity with high resolution values. User gives resolution either in
+nanoseconds or hertz, and sets the other parameter to zero.
+
+==== Traffic Manager
+* Add missing handle debug functions
+
+Traffic Manager API defines all types as platform specific, yet unit tests
+expect to be able to print them. Therefore introduce u64 debug print conversion
+functions for all TM types: `odp_tm_to_u64()`, `odp_tm_queue_to_u64()`,
+`odp_tm_node_to_u64()`, `odp_tm_shaper_to_u64()`, `odp_tm_sched_to_u64()`,
+`odp_tm_threshold_to_u64()`, `odp_tm_wred_to_u64()`
+
+* Info structures are written only on success
+
+Clarify that info structures (`odp_tm_node_info_t`, `odp_tm_node_fanin_info_t`,
+`odp_tm_queue_info_t`, `odp_tm_query_info_t`) are written only on success.
+
+==== Version
+* Added `ODP_VERSION_API` define and `ODP_VERSION_API_NUM` macro
+
+Added a macro and version number defines for easier comparison of API version
+numbers.
+
+=== Validation Tests
+==== Buffer
+* Rewrote buffer tests for improved coverage
+* Allow allocated buffer size to be larger than requested
+
+==== Classification
+* Fix duplicate global variable definition
+* Use `odp_schedule_wait_time()` correctly
+
+==== Crypto
+* Fix var len array scope
+
+==== Init
+* Add tests for new `compress` and `stash` feature bits
+* Improved log prints
+
+==== IPSec
+* Fixed invalid allocation of zero length test packet
+* Fixed invalid test for user pointer value
+
+==== Packet
+* Add max pools test
+* Add packet alloc align test
+* Fix max_num expectations
+* Prevent test trying to allocate zero length packets
+* Remove pools from suite init
+* Remove reset test packet from suite init
+* Rename default pool
+* Use common pool capability
+
+==== Packet I/O
+* Check parser flags on receive
+* Check pktio index
+* Decrease timeout in `odp_pktin_recv_tmo()` test
+* Make `pktio_check_start_stop()` test conditional
+* Remove unnecessary test start-up input flushes
+* Test `odp_packet_input_set()`
+* Test user pointer on receive
+* Do no attempt to continue with invalid queue handle
+
+==== Pool
+* Add pool cache size tests
+* Add test for packet pool seg_len parameter
+
+==== Queue
+* Lockfree queue max_size may be zero
+
+==== Scheduler
+* Add new stress test
+* Fix plain+sched test
+* Test `odp_schedule_group_create()` with non-zero mask
+
+==== Shared Memory
+* Add reserve flag tests
+* Fix printf format types for pointers
+
+==== Stash
+* Add tests for the new API
+
+==== System
+* Add test for `ODP_CACHE_LINE_ROUNDUP`
+* Add version macro test
+* Call version string functions
+* Cpu cycle counter may not be supported
+* Make `odp_cpu_hz_max()` tests conditional
+
+==== Timer
+* Timeout tick equals requested tick
+* Always initialize `odp_timer_pool_param_t` contents
+
+==== Traffic Manager
+* Fix shaper profile parameter check
+* Init structs between tests
+
+=== Example Applications
+==== Bench_packet
+* Added tests for missing packet functions
+
+==== Hello
+* Removed `-c` command line option
+
+==== L2fwd
+* Added number of packets option `-n`
+* Added packet copy option `-p`
+* Added pool per interface option `-y`
+
+==== Packet_dump
+* Added VLAN support
+
+==== Packet_gen
+* Added new simple packet generator test application
+
+==== Sched_latency
+* Added option for selecting event forwarding mode `-f`
+* Increased the number of warm-up and scheduling rounds
+
+==== Sched_perf
+* Added data touch options `-n` and `-m`
+* Added queue context data touch options `-k` and `-l`
+* Improved test reliability
+
+==== Switch
+* Added 5 minute aging time to MAC table entries
+* Added signal handler for SIGINT
+* Added support for detecting invalid and broadcast ethernet addresses
+* Improved packet drop statistics printing
+
+==== Timer_accuracy
+* Added burst gap option `-g`
+* Added mode option `-m`
+* Added option `-e` to retry on too early error
+* Added output file option `-o`
+* Added timer burst option `-b`
+
+=== Implementation Improvements
+==== GCC 10 support
+Fixed issues reported by GCC 10. The code base builds now with GCC 10
+also when LTO is enabled.
+
+==== Add configure option for setting path to the default config file
+Added `--with-config-file=FILE` configuration option for setting path to the
+default configuration file. The default configuration file has to include all
+configuration options.
+
+=== Bug Fixes
+==== Numbered Bugs / Issues
+* Fixed: https://github.com/OpenDataPlane/odp/issues/796[Issue 796]
+seg[0].data MUST return to its initial value on odp_packet_reset
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/826[Issue 826]
+max_size requires clarification for ODP_NONBLOCKING_LF and ODP_NONBLOCKING_WF
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/915[Issue 915]
+SIGSEGV: example/sysinfo (raspberry Pi 3B+, arm7, odp-linux, gcc 8.2.0)
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/930[Issue 930]
+Build failing with GCC 9.2
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/959[Issue 959]
+ODP build fails with GCC 10.1
+
+==== Unnumbered Bugs / Issues
+* Fixed: crypto: fix session allocation when out of sessions
+* Fixed: dpdk: DPDK renamed protocol structs
+* Fixed: dpdk: fix rx/tx checksum offload
+* Fixed: fix print failures on 32-bit systems
+* Fixed: pool: fix overflow when creating a huge packet pool
+* Fixed: pool: rename pool global variable
+* Fixed: sched scalable: fix pktio ingress queue polling dead lock
+* Fixed: test: fix global variables that are defined multiple times
+* Fixed: timer: fix timer pool create sync with inline timer scan
+
+== OpenDataPlane (1.23.0.0)
+=== Summary of Changes
+The ODP API changes in this release are related to the classifier module.
+
+The implementation changes include bug fixes (classifier, socket pktio),
+configurability improvements (inline timer), new packet segmentation
+implementation, and performance improvements (pool).
+
+=== API
+==== Classifier
+The PMR term documentation was not explicit about endianness of value and mask
+fields. Most terms assumed CPU endian, but terms with larger data sizes assumed
+big endian (MAC, IPv6 address and custom).
+
+Term specification was harmonized so that all terms expect value/mask data to be
+in big-endian format, have fixed size, and allow free memory alignment. Packet
+length term is an exception to this as it does not represent a field in a
+packet.
+
+Added new `ODP_PMR_CUSTOM_L3` term to match custom layer 3 protocol fields. PMR
+offset refers to the start of layer 3 in the packet. Other PMR rules (e.g. L2
+classification rules) may precede custom L3 rules.
+
+=== Helper
+Duplicate `ODPH_UNUSED` macro has been removed. `ODP_UNUSED` should be used
+instead.
+
+=== Validation Tests
+==== Common
+Improved test result output when some tests are inactive (skipped due to missing
+capability). Now inactive tests are listed by default in the test suite results.
+
+==== Classifier
+Cleaned up validation test code. Classifier implementation missed reporting some
+capabilities, tests didn't check those capabilities and they were tested. Source
+IPv4 term test was missing.
+
+Added serial PMR test, which tests series of PMR and CoS:
+
+* From default CoS to dest IPv4 CoS
+* From dest IPv4 CoS to dest UDP CoS
+
+Added parallel PMR test, which test serial and parallel PMR and CoS:
+
+* From default CoS to dest IPv4 CoS
+* From dest IPv4 CoS to a parallel UDP CoS per destination port number
+
+==== Scheduler
+Added new validation tests to verify that packet order is maintained when events
+are enqueued and dequeued to/from a plain queue while maintaining atomic/ordered
+scheduling context.
+
+==== Timer
+Added private timer pool test, which creates a timer pool with the `priv` flag
+set. The same thread calls schedule / queue_deq that created the pool.
+
+=== Example Applications
+==== timer_perf
+Added new test application to measure schedule call CPU cycle consumption with
+various timer pool parameter combinations. This measurement is mostly
+interesting with software-based timer implementations, where timers may be
+polled from the schedule call.
+
+==== New odp_ping application
+This application replies to IPv4 ping requests. It can be used to test
+connectivity with standard ping utility. ARP table needs to be setup manually on
+the sender side as the application does not reply to ARP requests.
+
+==== odp_classifier
+Added source CoS name into the parameter list of `-p` option. This enables
+linking classification rules together. When a source CoS is not defined, the
+default CoS is used as the source CoS.
+
+Option `-p` parameter list format is now:
+
+ <term>:<xxx>:<yyy>:<src_cos>:<dst_cos>
+
+Where `<src_cos>` is optional and number of "xxx", "yyy", etc. parameters is
+term depend.
+
+Added `-v` option which prints received packet with CoS queue
+name. This can be used for debugging classification rules.
+
+Added support for `ODP_PMR_VLAN_ID_0`, `ODP_PMR_ETHTYPE_0`, `ODP_PMR_CUSTOM_L3`,
+`ODP_PMR_ETHTYPE_X`, `ODP_PMR_VLAN_ID_X`, `ODP_PMR_UDP_DPORT`,
+`ODP_PMR_UDP_SPORT`, `ODP_PMR_TCP_DPORT`, and `ODP_PMR_TCP_SPORT` rules.
+
+==== timer_accuracy
+Fix bug in timer start offset calculation. Test results were offset by current
+local time value.
+
+Added delay and extra schedule calls to ensure that (software) timer
+implementation has passed its initial state before setting up timers.
+
+Added `-f` option to control offset to the first timer. This can be used e.g. to
+avoid best/worst case synchronization of timers to timer tick boundaries /
+resolution.
+
+==== packet_dump
+Added ICMP support. Print ICMP offset and ICMPv4 type/code. This helps to verify
+that ping requests are received.
+
+==== bench_packet
+Added missing tests for the following packet functions:
+
+* `odp_packet_data_seg_len()`
+* `odp_packet_free_sp()`
+* `odp_packet_from_event_multi()`
+* `odp_packet_to_event_multi()`
+* `odp_packet_subtype()`
+* `odp_packet_parse()`
+* `odp_packet_parse_multi()`
+
+=== Implementation
+==== Classifier
+Cleaned up implementation. Fixed endianness.
+
+==== Inline Timer
+Cleaned up code. Fixed accuracy issue with high resolution (<100us) timer pools.
+Added configuration option `inline_poll_interval_nsec` to select default poll
+interval in nsec.
+
+Added configure file option for inline timer (`inline_thread_type`) to select if
+control threads poll shared timer pools or not. Control threads still poll their
+private pools. With this user can configure control and worker threads to use
+separate timer pools.
+
+==== Packet I/O
+Enable explicit pktio type definition. The pktio type is selected by adding
+pktio_type prefix in front of device name separated by a colon
+(<pktio_type>:<if_name>).
+
+The socket mmsg pktio implementation has been refactored and performance has
+been improved.
+
+The IPC pktio implementation has been modified to use standard ODP internal
+`ring_ptr_t` rings to implement IPC rings. This enables removing the duplicate
+`_ring_t` implementation.
+
+==== Pool
+Packet segmentation has been reimplemented using a linked list. The new
+implementation is simpler and has the added benefit of greatly reducing
+packet and buffer header sizes.
+
+=== Bug Fixes
+==== Unnumbered Bugs / Issues
+* Fixed socket mmap pktio throughput collapse under heavy traffic
+
+* Fixed missing config header in install directory
+
+* Include only ODP defines in autogenerated header files
+
+* Fixed GCC 9 `address-of-packed-member` warnings
+
+* Disable building static test applications without static ODP lib
+
+* Fixed a segfault in `odp_pktin_recv_mq_tmo()` implementation
+
+* Fixed classifier vlan match bugs and tests
+
+* Fixed classifier matching of parallel PMRs
+
+
+== OpenDataPlane (1.22.0.0)
+=== Summary of Changes
+ODP v1.22.0.0 adds several smaller API changes mainly related to API
+clarification.
+
+=== API Changes
+==== Added `odp_queue_order_t` parameter to `odp_queue_param_t`
+Added a parameter to control if a destination queue does not require event
+re-ordering. By default, event order is maintained for all destination queues as
+before. Application may use `ODP_QUEUE_ORDER_IGNORE` for those queues that are
+used under ordered queue context, but do not require re-ordering and/or do need
+to avoid extra re-ordering delays.
+
+==== Added `ODP_SHM_HP` flag for `odp_shm_reserve()`
+When set, this flag guarantees that the memory reserved by `odp_shm_reserve()`
+is allocated from huge pages. If enough huge page memory is not available the
+call will fail.
+
+==== Improved initialization and termination specification
+Improve specification of initialization and termination steps. Explicitly list
+those functions that may be used before global init (those that are needed for
+setting up parameters). No changes to functionality.
+
+==== Improved queue context specification
+Highlight that queue context default value is NULL. This was defined already in
+`odp_queue_param_t` specification.
+
+==== New Crypto Algorithm Support
+Support for new crypto algorithms is added by defining symbols for:
+
+* `ODP_CIPHER_ALG_3DES_ECB`
+* `ODP_CIPHER_ALG_AES_ECB`
+* `ODP_CIPHER_ALG_AES_CFB128`
+* `ODP_CIPHER_ALG_AES_XTS`
+* `ODP_CIPHER_ALG_AES_EEA2`
+
+Support for new crypto authentication algorithms is added by defining symbols
+for:
+
+* `ODP_AUTH_ALG_SHA224_HMAC`
+* `ODP_AUTH_ALG_AES_EIA2`
+
+Added enumeration for digest (unkeyed) algorithms. They are added as auth
+algorithms with empty key required.
+
+* `ODP_AUTH_ALG_MD5`
+* `ODP_AUTH_ALG_SHA1`
+* `ODP_AUTH_ALG_SHA224`
+* `ODP_AUTH_ALG_SHA256`
+* `ODP_AUTH_ALG_SHA384`
+* `ODP_AUTH_ALG_SHA512`
+
+==== Crypto specification improvements / fixes
+Correct documentation for `ODP_AUTH_ALG_SNOW3G_UIA2` to reference 128-EIA1
+instead of 128-EEA1.
+
+Clarify IV data format and point to proper documents for 3GPP algorithms.
+
+==== Timer specification clarification
+Timer API intention has always been to allow any event type to be used as
+timeout events. Application should use `ODP_EVENT_TIMEOUT` type events
+(`odp_timeout_t`) by default, but also other event types may be used. Also,
+clarified timer set/reset functionality of `odp_timer_set_abs()` and
+`odp_timer_set_rel()` functions.
+
+API functionality not changed, just wording updated.
+
+==== Added timer resolution capability
+Typically, timer implementation needs to trade-off between highest resolution
+and longest timeout. Add new capability information
+(`odp_timer_res_capability_t`) and function to check limits between resolution
+and maximum timeout length.
+
+`odp_timer_res_capability()`::
+This function fills in capability limits for timer pool resolution and min/max
+timeout values, based on either resolution or maximum timeout.
+
+==== Added defines for minute and hour
+Defines for a minute and an hour are useful e.g. when setting timers for a bit
+longer (background, session lifetime, etc.) timeouts.
+
+* `ODP_TIME_MIN_IN_NS`
+* `ODP_TIME_HOUR_IN_NS`
+
+=== Helper Changes
+==== Added helper library version
+Added helper library version defines, so that application can track helper
+version independent of ODP API version:
+
+* `ODPH_VERSION_GENERATION`
+* `ODPH_VERSION_MAJOR`
+* `ODPH_VERSION_MINOR`
+
+Also, added a function for generating easy printout of the versions number.
+
+`odph_version_str()`::
+The version string defines the helper library version the following format:
+ `<generation>.<major>.<minor>`
+
+==== Added new thread create and join functions
+Defined new versions of thread create and join calls. The new calls explicitly
+support thread create and join in multiple steps. Also, per thread
+(`odph_thread_param_t`) and common parameters (`odph_thread_common_param_t`)
+have been improved.
+
+`odph_thread_create()`::
+Create and pin threads (as Linux pthreads or processes)
+
+`odph_thread_join()`::
+Wait previously launched threads to exit
+
+Old functions `odph_odpthreads_create()` and `odph_odpthreads_join()`
+have been deprecated.
+
+==== Added helper debug defines
+Added debug defines/macros into helper API and configure options to
+enable/disable helper debugging. These defines may be used both in helper and
+application code:
+
+* `ODPH_DEBUG` is 1 when helper debugging is enabled (`--enable-helper-debug`)
+* `ODPH_DEBUG_PRINT` is 1 when helper debug printing is enabled (`--enable-helper-debug-print`)
+* `ODPH_ASSERT()` generates assertion code when helper debugging is enabled
+
+=== Validation Test Improvements
+==== Timer Test Improvements
+Enables passing tests on high core count devices.
+
+Test min/max timeouts with the highest resolution and longest timeout
+parameters.
+
+==== Scheduler Test Fixes
+The group test would fail if `odp_schedule(ODP_SCHED_NO_WAIT)` wouldn't return
+any events on the first call.
+
+`scheduler_test_pause_resume()` would get stuck if all events were not
+successfully enqueued.
+
+Fixed a number of synchronization problems revealed by a scheduler
+implementation doing pre-scheduling. Makes sure scheduling context is always
+properly released.
+
+==== Classification Test Fixes
+Some of the test array elements may have not been set if the number of supported
+scheduling priorities was low.
+
+==== Pktio Test Improvements
+Added more debug information on magic number misses.
+
+==== Initialization Test Improvements / Fixes
+Added missing local init/term calls to all test cases.
+
+Added test case to set num_worker and num_control parameters.
+
+Added test case to set not used features flags in init parameters.
+
+==== Buffer Test Fixes
+Test suite missed to output the return value of `odp_cunit_run()`.
+
+==== Queue Test Improvements
+Check that queue context pointer value is NULL by default.
+
+==== IPsec Test Fixes
+Check pktio level inline IPsec support before running tests.
+
+==== Crypto Test Improvements
+Added support for AES-194-GMAC and AES-256-GMAC algorithms.
+
+Added more AES-CBC and AES-CTR test vectors.
+
+=== Example Changes
+==== Added new pipeline example application
+The application receives packets from one interface and passes them through 0-N
+worker stages before outputting them from a second network interface. The RX,
+worker, and TX stages are connected using plain queues and each stage is run on
+a separate CPU thread. Optionally, the worker stages calculate CRC-32C over
+packet data.
+
+==== Time example fixes
+Fix single worker deadlock and run the application as part of `make check`.
+
+==== IPsec example fixes
+Fixed a number of issues in IPsec example applications and run them during
+`make check`.
+
+==== New command line options
+===== l2fwd
+* added `-b` option to control maximum packet receive burst size
+
+===== pool_perf
+* added `-n` option to allocate multiple event bursts before freeing those
+* added `-t` option to select between buffer or packet pool types
+* added `-s` option to select data size
+
+===== sched_perf
+
+* added `-w` option to simulate application work
+* added `-g` and `-j` options to test scheduling with a number of schedule groups
+
+=== Implementation Improvements
+==== Pool Implementation Improvements
+Refactor local buffer caching into separate functions and optimize
+implementation by caching buffer header pointers instead of indices.
+
+New configuration options have been added for local cache
+(`pool:local_cache_size`) and burst sizes (`pool:burst_size`).
+
+Pool implementation has been modified to store buffer headers instead of
+indices to reduce type conversion overhead.
+
+==== Timer Implementation Improvements
+Added warm up period into POSIX timer pool startup to avoid the first timeout
+to slip. Otherwise, timer pthread receives the first signal after about 15ms and
+first timeout of the pool slips.
+
+==== DPDK Packet I/O Improvements
+The zero-copy DPDK pktio implementation has been cleaned up resulting a small
+performance improvement. Linking to DPDK library has also been simplified.
+
+=== Miscellaneous
+* ODP project has been moved from Linaro to OpenFastPath Foundation. Project
+documentation and domain addresses have been updated to reflect this change.
+
+* Added `--without-pcap` configuration option to explicitly build ODP without
+PCAP pktio regardless of if the library is available on the build host.
+
+* Added `--enable-lto` configuration option to build ODP with link time
+optimization (`-flto` flag).
+
+* Travis default distribution is updated to Ubuntu Xenial.
+
+* Added `-O3`, `-O0`, and LTO build tests to Travis
+
+* Supported Netmap version has been bumped to v13.0
+
+=== Bug Fixes
+==== Numbered Bugs / Issues
+* Fixed: https://github.com/OpenDataPlane/odp/issues/827[Issue 827]
+API doc links are not working
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/817[Issue 817]
+ODP fails to compile with latest OpenSSL
+
+* Fixed: https://github.com/OpenDataPlane/odp/issues/784[Issue 784]
+Bus error
+
+==== Unnumbered Bugs / Issues
+* Fixed build problems on Debian 8
+
+* Fixed AES-GMAC with OpenSSL 1.1.1b and later
+
+* Fixed out-of-tree build
+
+* Fixed pcapng pipe read permissions and made possible to capture traffic from
+multiple interfaces
+
+== OpenDataPlane (1.21.0.0)
+=== Summary of Changes
+ODP v1.21.0.0 adds two new API families as well as several small improvements.
+
+==== APIs
+===== Compression Support
+A new family of APIs is added that provides for session-oriented support for
+packet data compression and decompression. Compression sessions define the
+parameters used to control compression operations and their associated
+integrity hashes. Once created, sessions are input parameters to the new
+`odp_comp_op()` and `odp_comp_op_enq()` APIs that provide access to
+synchronous and asynchronous compression services on packets.
+
+Associated with the completion of asynchronous compression operations, a new
+packet subtype, `ODP_EVENT_PACKET_COMP` is defined. This subtype indicates
+that the packet includes additional metadata (retrievable via
+`odp_comp_result()`) that provides the result of the requested operation.
+
+A number of different compression and associated hash algorithms are defined,
+which are communicated with three new capability APIs:
+
+`odp_comp_capability()`::
+Provides information about general compression related capabilities
+offered by this implementation
+
+`odp_comp_alg_capability()`::
+Provides details about the capabilities of individual compression algorithms
+supported.
+
+`odp_comp_hash_alg_capability()`::
+Provides details about the capabilities of individual hash algorithms
+supported for use with compression.
+
+===== Flow Aware Scheduler Support
+A new capability for flow aware scheduling is added. As part of this, the
+scheduler now supports capabilities and configurability. As a result, the
+initialization sequence for applications that make use of the ODP scheduler
+has changed slightly. The new API call sequence is:
+[source,c]
+-----
+odp_schedule_capability()
+odp_schedule_config_init()
+odp_schedule_config()
+odp_schedule()
+-----
+It is a programming error to call `odp_schedule()` (or its variants) without
+having first initialized the scheduler with an `odp_schedule_config()` call.
+This call may only be issued once per ODP instance as scheduler configuration
+is global.
+
+By default the scheduler operates as before. When configured to operate in
+flow aware mode, the scheduler will now respect event flow ids (managed by the
+new `odp_event_flow_id()` and `odp_event_flow_id_set()` APIs) when making
+scheduling decisions. This means that flow identification is a combination of
+event flow id and queue id. For example, when operating in flow aware mode the
+scheduler may dispatch events from an atomic queue to multiple threads
+concurrently, as long as those events have different flow ids. For
+applications that process large numbers of lightweight flows that have limited
+context needs, this can lead to throughput improvements as well as reduced
+implementation memory footprint.
+
+==== DPDK v18.11 Support
+The latest LTS release of DPDK (v18.11) is now supported by ODP. Support for
+the previous LTS release (v17.11) is retained. Prior versions of DPDK are
+no longer supported.
+
+==== Queue Capabilities Moved to Scheduler
+As part of the introduction of flow-aware scheduling, capabilities associated
+with `SCHED` queues have been moved from the `odp_queue_capabilities_t` struct
+returned by `odp_queue_capabilities()` to the new `odp_sched_capabilities_t`
+struct returned by `odp_sched_capabilities()`.
+
+Capabilities moved include `max_ordered_locks`, `max_sched_groups`, and
+`sched_prios`. The `max_sched_groups` capability is renamed `max_groups`. In
+addition, `max_queues`, `max_queue_size`, and the support capabilities for
+lock free and wait free non blocking queues is now part of the scheduler
+capabilities.
+
+In support of flow aware scheduling mode, the `max_flows` scheduler capability
+is renamed `max_flow_id`. A value of 0 indicates that flow aware mode
+scheduling is not available in this ODP implementation.
+
+=== Test/Validation Improvements
+==== Travis readability improvement
+The "BUILD_ONLY` environment variable has been renamed `CHECK` for improved
+output readability.
+
+==== Flow aware scheduler testing
+As part of flow aware mode, scheduler validation tests will now test this mode
+if `odp_schedule_capability()` indicates that flow-aware mode is supported.
+
+=== Bug Fixes
+==== Unnumbered Bugs/Issues
+
+* Latest version of netmap `nm_ring_empty()` implementation has changed.
+PktIO netmap support updated to support this as well as previous releases.
+
+* Improved compatibility of PktIO socket support with latest versions
+of the clang compiler.
+
+* Add match pattern for missing DPDK PMD drivers for improved compatibility.
+
+== OpenDataPlane (1.20.0.0)
+=== Summary of Changes
+ODP v1.20.0.0 is a refresh of ODP, incorporating significant configurability
+and performance improvements as well as new APIs and API restructures.
+
+==== APIs
+===== Symbol `ODP_SHM_NULL` Removed.
+An invalid `odp_shm_t` has the value `ODP_SHM_INVALID`, consistent with other
+ODP types. The legacy synonym `ODP_SHM_NULL` is now removed for consistency.
+
+===== New 3GPP Crypto Algorithm Support
+New support for 3GPP crypto algorithms is added by defining symbols for
+
+* `ODP_CIPHER_ALG_KASUMI_F8`
+* `ODP_CIPHER_ALG_SNOW3G_UEA2`
+* `ODP_CIPHER_ALG_ZUC_EEA3`
+
+In addition new authentication algorithm symbols are defined for
+
+* `ODP_AUTH_ALG_KASUMI_F9`
+* `ODP_AUTH_ALG_SNOW3G_UIA2`
+* `ODP_AUTH_ALG_ZUC_EIA3`
+
+These values are returned as ODP capabilities as well as being accepted in
+crypto session creation for implementations that indicate support for them.
+
+===== Crypto Capability for Bitwise Operation
+The new `bit_mode` capability Boolean is added to the
+`odp_crypto_cipher_capability_t` struct to indicate that an implementation
+supports operating in bit mode. When operating in bit
+mode, field offsets and lengths are expressed in terms of bits rather than
+bytes. However, such lengths must always be specified in multiples of 8.
+
+===== Improved Crypto Spec Documentation
+The ODP crypto API specification is tightened to specify default values for
+cipher and authentication algorithms. Also documented when key and IV
+parameters need to be set.
+
+===== IPsec Extensions
+IPsec requires "salt" (extra keying material) when the GMAC authentication
+algorithm is used. To accommodate this the `auth_key_extra` field is added to
+the `odp_ipsec_crypto_param_t` struct and documentation is added clarifying
+when this field is needed and how it should be used.
+
+===== Classifier Type Rename
+The `odp_pmr_t` type name for an invalid value is renamed from `ODP_PMR_INVAL`
+to `ODP_PMR_INVALID` for consistency with the rest of ODP type names. The old
+symbol is still available when ODP is configured with
+`--enable-deprecated`.
+
+===== New API for Packet Event Subtypes
+The `odp_packet_subtype()` API is added that returns the subtype of a packet
+event directly.
+
+===== Streamlined Packet Parsing Results
+The `odp_packet_parse_result()` API is added that returns the result of
+packet parsing as a single `odp_packet_parse_result_t` struct. This can
+offer efficiency improvements for applications that need all parse results
+rather than making individual parse result calls.
+
+===== PktIO Extensions to Support per-Queue Configuration
+PktIO interfaces support multiple input queues to enable increased parallelism
+in I/O processing. Previously, all of these input queues were required to
+belong to the same scheduler group. The `odp_pktin_queue_param_t` struct is
+now extended with an optional `odp_pktin_queue_param_ovr_t` struct that
+permits individual pktin queues to be assigned to separate scheduler groups.
+This may permit improved performance for advanced application use cases.
+
+===== Timer Pool Capabilities
+The `odp_timer_capability_t` struct is extended to return three additional
+pieces of information:
+
+`max_pools_combined`::
+The total number of timer pools that can be created combining different
+clock sources
+
+`max_pools`::
+The maximum number of timer pools for a given clock source.
+
+`max_timers`::
+The maximum number of timers in a single pool. A zero value means number is
+limited only by available memory.
+
+===== Add Scheduler mix/max/default Priority Functions
+Three new APIs: `odp_schedule_max_prio()`, `odp_schedule_min_prio()`, and
+`odp_schedule_default_prio()` are added that return the min, max, and default
+values specified for the `prio` field in the `odp_schedule_param_t` struct.
+
+With the introduction of these scheduling priority functions the previously
+defined macros (`ODP_SCHED_PRIO_HIGHEST`, `ODP_SCHED_PRIO_NORMAL`, and
+`ODP_SCHED_PRIO_LOWEST`) are now deprecated and should no longer be used.
+
+===== Specification of `odp_schedule_prio_t` as an `int`
+Previously, the `odp_schedule_prio_t` type definition was left to each
+implementation. With the addition of explicit schedule priority ranges, this
+type is now specified to be an `int` to permit efficient implementation
+(including inlining) of these functions.
+
+====== New Scheduler APIs
+The new scheduler APIs `odp_schedule_multi_wait()` and
+`odp_schedule_multi_no_wait()` are added to provide more efficiently
+implementable versions of these functions. The existing scheduler APIs remain
+unchanged. These new APIs can simply provide a fastpath for some
+applications/implementations as an alternative to specifying a parameter on
+`odp_schedule_multi()`.
+
+===== Memory Model in `odp_init_global()`
+The `odp_init_t` parameter passed to `odp_init_global()` is extended to
+add the `mem_model` field. This field is defined by the new `odp_mem_model_t`
+struct and is used to specify whether the application will be using a
+thread (`ODP_MEM_MODEL_THREAD`) or process (`ODP_MEM_MODEL_PROCESS`)
+memory model. The default is a thread model is used for compatibility with
+previous levels of ODP.
+
+==== ABI Changes
+A number of changes to the ODP ABI have also been made in this release to
+improve application binary portability.
+
+===== Strong Typing for Timer Pools
+The `odp_timer_pool_t` is now strongly typed.
+
+===== Consistent Initialization
+The values of the various `ODP_xxx_INVALID` symbols for ODP abstract types in
+the `odp-linux` reference implementation are now consistently zeros. This
+reduces errors and improves portability.
+
+=== Implementation Improvements
+==== Configuration File
+A new configuration file mechanism is introduced that makes use of
+https://www.hyperrealm.com/libconfig/libconfig_manual.html[libconfig] to
+enable various runtime ODP parameters to be specified dynamically.
+
+Default configuration values for the `odp-linux` reference implementation are
+contained in the `config/odp-linux-generic.conf` file. Users may override
+these default values by supplying their own configuration file. At
+`odp_init_global()` time, if the `ODP_CONFIG_FILE` environment variable is set,
+this is used to locate the path to the override configuration file.
+
+==== Process Mode Support
+The `odp-linux` reference implementation now supports applications that run in
+process mode (`mem_model` = `ODP_MEM_MODEL_PROCESS`) as well as the default
+thread mode. This support only applies within a single ODP instance, so any
+`fork()` calls must be done only _after_ `odp_init_global()` has been called
+to initialize ODP on a root process.
+
+==== Removal of `iQuery` Scheduler
+The `iQuery` scheduler is removed from the `odp-linux` reference
+implementation, as it offers no performance advantages and has not seen
+application use.
+
+==== Number of CPUs
+The `odp-linux` reference implementation now supports up to 256 CPUs by
+default (increased from 128).
+
+==== Support for Large Burst Sizes
+The `odp-linux` reference implementation now supports large burst sizes for
+both I/O and non-I/O scheduled events. Large bursts (when available) are
+received directly to the application without any stashing for improved
+throughput. Burst sizes are configurable via the new configuration file
+mechanism, as described above.
+
+==== `--without-openssl` Warnings
+When building `odp-linux` using `--without-openssl` a warning will be issued
+cautioning that strong cryptography will not be available.
+
+==== Inline Queue Enq/Deq APIs
+The various enq/deq APIs are now subject to inlining when `odp-linux` is
+built with `--disable-abi-compat`.
+
+==== Configurable Timer Controls
+Inline timers are now controlled via a config file option. Timer polling
+frequency is similarly controlled via the config file.
+
+==== Huge Page Configuration
+The config file is now used to specify the huge page usage limit.
+
+==== Single and Multi-Consumer/Producer Rings
+The queue implementation in `odp-linux` now automatically makes use of
+optimized single and multi-consumer/producer rings to significantly speed
+up enq/deq processing.
+
+==== `odp_shm_print_all()` Improvements
+The output from `odp_shm_print_all()` is reworked to provide more useful
+and comprehensive shared memory usage information in `odp-linux`.
+
+==== IPsec Improvements
+SA lifetime checking is now more scalable to multiple threads. This
+significantly reduces overhead for multithreaded IPsec applications.
+
+==== Native Builds
+When running in non-ABI compatibility mode, `odp-linux` now enables
+native machine-specific optimizations for the CPU architecture of the
+local machine.
+
+=== Validation Test Improvements
+==== SCTP Test Packets
+SCTP test packets are now used in parser testing. SCTP headers are added to
+ODP and ODP helpers and SCTP checksums are now inserted and verified as part
+of validation testing.
+
+==== `odp_packet_reset()` Test
+The packet validation test suite now properly tests `odp_packet_reset()`.
+
+=== Helper Changes
+In support of process mode, ODP helper functions have been changed to
+better match these new capabilities
+
+==== New `enum`
+The `odph_linux_thread_type_t enum` has been replaced with the new
+`odp_mem_model_t` type.
+
+==== Helper Options
+The new `odph_options()` getter function is added that returns
+applicable options in effect via the new `odph_helper_options_t` struct.
+This currently includes the memory model (thread or process) that is in use.
+
+==== SCTP Helpers
+The new helper APIs `odph_sctp_chksum_set()` and `odph_sctp_chksum_verify()`
+are added to facilitate working with SCTP packet checksums.
+
+=== Performance Test Improvements
+==== Pool Performance
+A new `odp_pool_perf` test has been added that stress-tests ODP pool
+functions in a multithreaded environment to generate performance statistics.
+
+==== Scheduler Performance
+A new `odp_sched_perf` test has been added that stress-tests the scheduler
+in a multithreaded environment.
+
+==== CPU Performance
+A new `odp_cpu_bench` performance test has been added that runs
+compute-intensive packet operations in a multithreaded environment and prints
+the observed maximum throughput for each thread.
+
+=== Example Improvements
+==== Classifier Example changes
+The `odp_classifier` example program now uses a reduced number of threads by
+default to reduce elapsed run time. `ODP_THREAD_COUNT_MAX` is also now used as
+the max worker count.
+
+==== Generator Improvements
+The `odp_generator` example has numerous cleanups and performance improvements.
+
+==== IPsec Example
+The `odp_ipsec` example now properly stops and closes pktio devices on exit.
+
+==== Packet Dumping
+A new `odp_packet_dump` example is added that prints received packets to the
+terminal. This is useful for debugging packet I/O interfaces.
+
+==== Sysinfo Example
+A new `odp_sysinfo` example is provided that prints system information. Useful
+for checking the ODP environment during debugging. This includes providing
+detailed information about the various crypto facilities supported, as well
+as the feature flags used at build time (_e.g.,_ if the binary was built with
+ARMv8.0 or ARMv8.1 instructions).
+
+==== Traffic Manager Example
+The traffic manager example now properly destroys all TM queues it creates
+for improved reliability. It also now always prints a proper termination
+summary message.
+
+=== Bug Fixes
+==== Numbered Bugs/Issues
+===== https://bugs.linaro.org/show_bug.cgi?id=3983[Bug 3983]
+Compile fails on OpenSuSE 42.2 Leap with error: negative width in bit field
+'__error_if_negative'
+
+===== https://bugs.linaro.org/show_bug.cgi?id=3989[Bug 3989]
+odp_system_info_init() issues
+
+===== https://bugs.linaro.org/show_bug.cgi?id=3999[Bug 3999]
+IPsec antireplay check drops packets when sequence number jumps.
+
+===== https://bugs.linaro.org/show_bug.cgi?id=4002[Bug 4002]
+IPsec SA creation must fail for ESN-enabled SAs
+
+===== https://bugs.linaro.org/show_bug.cgi?id=4013[Bug 4013]
+Per-SA IPv4 ID allocation may cause duplicate IDs.
+
+===== https://bugs.linaro.org/show_bug.cgi?id=4017[Bug 4017]
+Unexpected IP ID causes IPsec API validation to fail
+
+===== https://github.com/Linaro/odp/issues/662[Issue 662]
+rte_mempool_ops_alloc() is not dpdk api
+
+==== Unnumbered Bugs/Issues
+* Fixed enq/deq issues encountered on architectures with weak memory ordering.
+* Return 0 from `odp_sys_huge_page_size_all()` if hugepages are not
+supported/detected. Tests modified to not treat this as an error.
+* Set `ODP_CACHE_LINE_SIZE` to 128 on ppc64le systems.
+* iplookuptable fix putting values into table
+* DPDK pktio support now works properly across multiple ODP instances.
+* Zero timer pool memory on reserve (fixes timer failures due to uninitialized
+variables).
+* `-march=native` disabled for `clang`. This fixes a known issue with recent
+levels of clang.
+
+=== Known Issues
+==== https://bugs.linaro.org/show_bug.cgi?id=3998[Bug 3998]
+IPsec extended sequence number support is missing
+
+==== https://bugs.linaro.org/show_bug.cgi?id=4014[Bug 4014]
+Separate IP ID allocation for transport and tunnel mode SAs may cause
+duplicate IDs
+
+==== https://bugs.linaro.org/show_bug.cgi?id=4018[Bug 4018]
+Unexpected IV causes IPsec API validation to fail
+
+==== https://bugs.linaro.org/show_bug.cgi?id=4040[Bug 4040]
+Clang build fails on Ubuntu 18.04
+
+== OpenDataPlane (1.19.0.2)
+=== Summary of Changes
+ODP v1.19.0.2 is the second service update for the Tiger Moth release. It
+incorporates a number of corrections and enhancements that further improve the
+quality and testability of ODP.
+
+==== APIs
+There are no API changes in this release.
+
+==== DPDK Service Release Sync
+ODP is now paired with DPDK 17.11.3 for its DPDK-related support. This is the
+current service level for the DPDK 17.11 LTS package used by ODP Tiger Moth.
+
+=== Implementation Improvements
+This release incorporates several improvements in the `odp-linux` reference
+implementation of ODP.
+
+==== Enhanced Inlining
+ODP supports inlining of functions in embedded environments when ABI
+compatibility is not needed. ODP itself now makes use of inlined functions for
+all relevant internal use of its APIs, leading to improved performance.
+
+==== Completion of CRC API Implementation
+The `odp_hash_crc_gen64()` API is now properly implemented and appropriate
+validation tests added to support it.
+
+In addition, a streamlined table-based implementation of the basic CRC
+functions eliminates the previous dependency on `zlib`.
+
+==== PktIO-Specific Parsing
+To better integrate with DPDK parsing capabilities, ODP packet parsing has
+been restructured to operate at the PktIO level. This permits DPDK PktIO types
+to exploit the native DPDK packet parser and checksum facilities, leading
+to better integration.
+
+==== PktIO Internal Cleanup and Restructure
+The PktIO functions have been streamlined and refactored in a number of ways
+to provide better long-term maintainability of these functions. This includes
+moving per-PktIO data into individual files rather than sharing a common file,
+as well as better placement for I/O statistics.
+
+==== Checksum Validation Support
+Loop PktIO interfaces now add the capability to validate packet L3 and L4
+checksums as part of receive processing. The existing `odp_pktio_capability()`
+API now reports that checksum validation is available for these interfaces.
+
+==== Single Producer / Single Consumer Queue Performance Optimizations
+When defining lock free queues that have only a single producer and consumer,
+a streamlined implementation offers significant speedup.
+
+==== Fast PCAPng Packet Capture
+Fast pcap capture is now provided in `odp-linux` to capture packets on any
+interface. This is enabled via the `--enable-pcapng-support` configuration
+option. Once enabled, packets can be captured using a sequence such as:
+-----
+sudo mkdir /var/run/odp/
+start ODP application that reads/writes to the interface of interest
+start the ODP application
+sudo dd if=/var/run/odp/<pid>-<ifname>-flow-<queue#> of=~/test.pcap
+
+cntrl^c to end capture
+wireshark ~/test.pcap to view capture
+-----
+
+Interfaces of interest are identified by a string consisting of the
+application process ID, the interface name, and the queue number of interest,
+if the interface supports multiple queues.
+
+==== Removal of GPL M4 Macros
+A number of autotools/autoconf M4 macros used in configuring `odp-linux` have
+been rewritten to avoid potential GPL licensing concerns. These macros are
+used only during ODP configuration processing and have no role in ODP
+or ODP application use.
+
+=== Validation Test Improvements
+==== Queue Pair Validation Tests
+The validation test suite for queue API testing is enhanced to now test
+operation on queue pairs properly. This enables the various enqueue/dequeue
+modes defined by the ODP specification to be more fully exercised, leading
+to improved API conformance.
+
+==== Scheduling Test Improvements
+The scheduling validation tests now better use the various capability APIs to
+ensure that implementations are only tested for advertised capabilities.
+
+=== Crypto Test Improvements
+The crypto validation tests now better use the various capability APIs to
+ensure that implementations are tested across advertised crypto capabilities.
+
+=== Performance Test Improvements
+==== New Performance Test
+A new `odp_queue_perf` test has been added to test plain (non-scheduled)
+queue performance in various modes.
+
+=== Helper Changes
+* The `getopt` library calls are no longer used to avoid packaging conflicts
+that can arise with this use. There are no changes to helper functionality.
+This change simply improves packaging.
+
+=== Examples Improvements
+* The `odp_generator` example adds UDP port range support.
+
+==== CI Improvements
+Numerous changes to Travis integration are added to improve the quality and
+reliability of Continuous Integration (CI) testing.
+
+=== Bug Fixes
+==== https://bugs.linaro.org/show_bug.cgi?id=3787[Bug 3787]
+Timeout accuracy breaks down with odd resolution requests
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3867[Bug 3867]
+timer validation test fails when using 1GB huge pages
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3879[Bug 3879]
+example/l2fwd_simple fails on some systems when using 1GB huge pages
+
+=== Unnumbered Bug Fixes
+* Corrected the handling of timeout events in the scalable scheduler.
+* Fixed IPsec link order to streamline builds.
+* Fixed scaling issues with scheduler support for large core count systems.
+
+=== Known Issues
+==== https://bugs.linaro.org/show_bug.cgi?id=3774[Bug 3774]
+Shmem validation test runs indefinitely with 1GB huge pages
+
+==== Running ODP on Systems with more than 64 Cores
+There are several issues that seem to arise when running ODP applications on
+platforms with more than 64 cores. While the most critical of these, which
+affected the scheduler, have been fixed in this release, there are others
+that are still under investigation. These will be addressed in the next
+release of ODP.
+
+== OpenDataPlane (1.19.0.1)
+=== Summary of Changes
+ODP v1.19.0.1 is the first service update for the Tiger Moth release. It
+incorporates a number of corrections and enhancements that improve the quality
+and testability of ODP.
+
+==== APIs
+There are no API changes in this release.
+
+==== DPDK Service Release Sync
+ODP is now paired with DPDK 17.11.2 for it's DPDK-related support. This is the
+current service level for the DPDK 17.11 LTS package used by ODP Tiger Moth
+and incorporates a number of important bug fixes.
+
+=== Implementation Improvements
+The ODP reference implementation has been improved in a number of areas.
+
+==== GCC 8 Support
+The GCC 8 series of compilers provides additional warnings about possible
+string truncation. A few ODP modules were changed to avoid misleading
+warnings when compiling with this level of GCC.
+
+==== Linking with `libnuma`
+The `libnuma` library is used for DPDK pktios to provide proper memory
+allocation on NUMA-aware systems. Linking with this library is improved to
+avoid extraneous error messages at build time.
+
+==== Packet metadata reorganization
+Packet metadata is reorganized to reduce the cache footprint used by the ODP
+reference implementation, resulting in performance improvements.
+
+==== Random split from crypto module
+The `odp_random_xxx()` family of APIs was moved to a separate module
+(`odp_random.c`) for modularity and better isolation from planned crypto
+enhancements.
+
+==== Shmem improvements
+Unnecessary locking is removed from the `odp_fdserver` module, streamlining
+operations on shared memory.
+
+==== Timer pools
+The default number of timer pools supported by the ODP reference
+implementation has been reduced from 255 to 32. This lower number remains
+generously adequate for most applications and meaningfully reduces memory
+footprint, giving better performance.
+
+==== Timer resolution
+During initialization ODP normally measures timer resolution to set the
+reported `highest_res_ns`. When such measurement is not able to be performed,
+this is not limited to 500ns to avoid bounds errors with overly precise
+resolutions.
+
+=== Validation Improvements
+The ODP validation test suite has been improved in a number of areas.
+
+==== Crypto validation test
+The validation test now correctly handles corner cases when the implementation
+under test fails to process any test packets. It also includes the
+previously missing `ODP_CIPHER_ALG_AES_CTR` name.
+
+Additionally, since individual implementations indicate which crypto/hash
+algorithms are supported via the `odp_crypto_capability()` API, the crypto
+validation test now properly uses this information and only tests those
+algorithms that the implementation reports as supported. The list of
+unsupported algorithms is also reported as part of the test results.
+
+==== `odp_sched_pktio` test improvements
+The number of input/output queues used by this test can now be specified,
+providing additional controls for test flexibility. In addition, pktout
+queues are now selected based on input queue rather than worker id, thus
+ensuring packet order flow is maintained.
+
+Finally, an inactivity timer is added that allows the test to report when
+packets were handled due to timeout rather than I/O activity.
+
+==== Timer validation test
+The validation test for the ODP timer APIs has been reorganized to better
+characterize an implementation's conformance to the ODP Timer API
+specification. Since implementations can have widely differing timer accuracy
+levels, particularly when running in virtualized environments, the test also
+relaxes its bounds checking and enforcement somewhat to better avoid false
+negative test results.
+
+Additionally, a timer pool create/destroy test was added as this area was
+not adequately covered previously.
+
+=== Documentation Improvements
+The `EXTRA_ASCIIDOC_FLAGS` environment variable may now be used to supply
+additional build flags for Asciidoctor, which can be used to override
+icons and/or fonts for distribution or other needs.
+
+=== Bug Fixes
+==== https://bugs.linaro.org/show_bug.cgi?id=3657[Bug 3657]
+PktIO does not work with Mellanox Interfaces
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3685[Bug 3685]
+RX UDP checksum offload drops valid UDP packets with Niantic
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3686[Bug 3686]
+IP header checksum not inserted if L4 offset not set
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3690[Bug 3690]
+fdserver process interferes with signal handling
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3763[Bug 3763]
+tests should fail if odp_crypto_op/op_enq process 0 packets
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3764[Bug 3764]
+IPsec code can occasionally damage packets
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3772[Bug 3772]
+Timer segfaults when creating and destroying multiple timer pools
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3788[Bug 3788]
+linux-gen: ipc test fails to reserve memory
+
+=== Known Issues
+==== https://bugs.linaro.org/show_bug.cgi?id=3774[Bug 3774]
+Shmem validation test runs indefinitely with 1GB huge pages
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3787[Bug 3787]
+Timeout accuracy breaks down with odd resolution requests
+
+
+== OpenDataPlane (1.19.0.0)
+=== Summary of Changes
+ODP v1.19.0.0 is the official Tiger Moth final release. It incorporates final
+implementation changes and bug fixes and completes the Tiger Moth ODP
+development cycle.
+
+==== APIs
+No functional changes for this release. The Tiger Moth API was frozen in ODP
+v1.18.0.0.
+
+===== API Documentation Update
+The specification for the `odp_packet_l4_chksum_status()` API has been
+clarified to reflect that in IPv4 UDP checksums are optional. As a result, a
+zero (nonexistent) checksum will be reported as `ODP_PACKET_CHKSUM_OK`.
+
+==== C++ Test Improvements
+The {cpp} test included in the ODP validation suite now uses `cout` instead
+of `printf()` to ensure that {cpp} is being used to compile it.
+
+==== Queue and Scheduler Configuration
+For the ODP Reference Implementation, The `config/odp-linux-generic.conf` file
+is extended with sections to control the default and maximum sizes for basic
+queues, and the priority spread used by the scheduler for scheduled queues.
+
+The configuration file is a template named `platform/odp-$platform.conf` so
+this can be easily inherited by other ODP implementations.
+
+==== Runtime Default `config` File Read Order Improvements
+For the ODP Reference Implementation, the default values of the
+application-provided `config` file (if used) override the values provided by
+the built-in `config/odp-linux-generic.conf` file.
+
+=== Implementation Improvements
+The `odp-linux` reference implementation is improved in a number of areas:
+
+==== Netmap Ring Configuration for VALE
+PktIO netmap support now uses the ODP config file to allow rings used for VALE
+processing to be specified. The supplied defaults provide optimal performance
+in typical settings.
+
+==== AES-XCBC-MAC and SHA384-HMAC
+These crypto/authentication algorithms are now implemented.
+
+==== Packet Checksum Validation and Insertion
+Proper packet checksum validation and insertion, in conformance with the
+relevant ODP APIs, is now provided.
+
+=== Dependency Changes
+
+==== DPDK 17.11 Support
+The Tiger Moth LTS release is synchronized with the most recent DPDK LTS
+release for DPDK pktio support.
+
+==== Removal of dependency on `xxd` package.
+This dependency is removed. The Reference Implementation build tools now use
+the standard `od` tool rather than the optional `xxd` package.
+
+=== Performance Tests
+
+==== `odp_sched_pktio`
+A new test has been added to test the performance of PktIO operations in
+scheduled mode. Scheduled PktIO is inherently more scalable and simpler from
+an application standpoint than direct (polled) I/O, but depending on the
+efficiency of the scheduler implementation can incur additional levels of
+overhead. This test can give insight into a given platform's scheduler
+efficiency. For the `odp-linux` reference implementation, this test has shown
+scheduled I/O to be within 10% of rates achievable via direct I/O, meaning
+that for many applications the simplicity and scalability of the event model
+is preferable.
+
+==== `odp_ipsec`
+A new test has been added that measures outbound (TX) IPsec performance with
+a variety of cipher and authentication algorithms.
+
+=== Example Changes
+
+==== `l2fwd` Example
+The `README` file associated with this example has been clarified to explain
+that this example is a throughput test and as a result does not preserve
+packet order under all conditions.
+
+=== Bug Fixes
+==== https://bugs.linaro.org/show_bug.cgi?id=3611[Bug 3611]
+ODP linux-generic fails on AArch64 in non-ABI-compat mode.
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3657[Bug 3657]
+PktIO does not work with Mellanox Interfaces
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3685[Bug 3685]
+RX UDP checksum offload drops valid UDP packets with Niantic
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3686[Bug 3686]
+IP header checksum not inserted if L4 offset not set
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3690[Bug 3690]
+fdserver process interferes with signal handling
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3736[Bug 3736]
+return value not checked for some fdserver interface functions
+
+=== Known Issues
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2988[Bug 2988]
+ODP exposes symbols outside of odp*/_odp* namespace
+
+== OpenDataPlane (1.18.0.1)
+=== Summary of Changes
+ODP v1.18.0.1 is a fix level for Tiger Moth Release Candidate 2 (RC 2).
+It incorporates fixes and performance / serviceability enhancements but no
+API changes.
+
+==== APIs
+No changes for this release.
+
+==== Corrected Crypto Functionality
+This release corrects a merge issue with crypto functionality that resulted in
+incorrect crypto operation for certain cipher suites.
+
+==== Runtime Configuration
+Introduces a runtime configuration file that can be used by applications to
+set selected ODP configuration parameters dynamically at runtime rather than
+at `configure` time. At present this is used to configure parameters needed
+by DPDK PMDs when using PktIO interfaces in the DPDK class. The intention is
+to expand the use of this file over time to allow more dynamic control
+of other aspect of ODP runtime configuration.
+
+For the ODP Reference Implementation, a template configuration file is
+provided in `config/odp-linux.conf`. Introduction of this support generates
+an additional dependency on the `libconfig` package, which must be present to
+build ODP.
+
+==== IPsec Traffic Flow Confidentiality (TFC) Corrections
+A few missing implementation features associated with TFC packet generation
+have been added in this fix level. This support is now functionally complete in
+the ODP Reference Implementation.
+
+==== Debug Print Improvements
+The information provided in debug builds of the Reference Implementation is
+improved to print the interface name on open, start, stop, and close calls.
+The driver name and number of queues are also printed to ease verification of
+correct configuration.
+
+==== Default Scheduler Performance Improvements
+The performance of the default scheduler in the Reference Implementation is
+significantly improved in providing scheduled access to PktIO queues. Scheduled
+I/O now operates within 10% of the performance achievable using Direct I/O,
+while providing incomparably better scalability in multicore environments.
+
+==== `.so` Numbering Changes
+In preparation for the Tiger Moth official release, ODP has adopted a
+simplified `.so` naming scheme, which is introduced here. ODP `.so` numbers
+are now tied to the ODP release number since ODP does not promise backward
+compatibility across release boundaries.
+
+== OpenDataPlane (1.18.0.0)
+=== New Features
+ODP v1.18.0.0 is Tiger Moth Release Candidate 2 (RC 2). It completes the new
+APIs that are part of the Tiger Moth Long Term Support (LTS) release of ODP
+as well as various performance refinements and bug fixes. As of RC2 the ODP
+API is now frozen for the Tiger Moth development series.
+
+==== APIs
+The following new and changed APIs are included in this release:
+
+===== Addition of Shared Memory (SHM) Initialization Parameters
+The `odp_init_t` struct used as the argument to the `odp_init_global()` API
+has been expanded to include a `max_memory` field that specifies the maximum
+amount of shared memory (shm) that the application will use. This is to
+better enable ODP implementations to optimize their use of shared memory in
+support of the application. If left as (or defaulted) to 0, the implementation
+may choose a default limit for the application.
+
+===== Crypto Changes
+A number of crypto refinements are included in this release:
+
+* The single initialization vector (`iv`) in the `odp_crypto_session_param_t`
+is replaced by a separate `cipher_iv` and `auth_iv` fields.
+
+* The single initialization vector (`override_iv_ptr`) in the
+`odp_crypto_op_param_t` is replaced by a separate `cipher_iv_ptr` and
+`auth_iv_ptr` fields.
+
+* The special nature of GCM and GMAC authenticated encryption modes is
+clarified in that these ciphers always combine ciphering with authentication
+and hence require both to be specified when used. This is simply a
+documentation change as this requirement has always existed.
+
+* Enumerations for AES_CCM (`ODP_CIPHER_ALG_AES_CCM` and
+`ODP_AUTH_ALG_AES_CCM`) authenticated encryption modes are added.
+
+* Enumeration for the AES_CMAC authenticated encryption mode
+(`ODP_AUTH_ALG_AES_CMAC`) is added.
+
+* Enumerations for the ChaCha20-Poly1305 (`ODP_CIPHER_ALG_CHACHA20_POLY1305`
+and `ODP_AUTH_ALG_CHACHA20_POLY1305`) authenticated encryption modes are
+added.
+
+* Enumeration for the SHA-384 authentication algorithm
+(`ODP_AUTH_ALG_SHA384_HMAC`) is added.
+
+* Enumeration for the AES-XCBC-MAC authentication algorithm
+(`ODP_AUTH_ALG_AES_XCBC_MAC`) is added.
+
+===== Lock-free and block-free queues
+The `odp_nonblocking_t` enums introduced in ODP v1.17.0.0 are now returned
+as separate `odp_queue_capability()` limits for plain and scheduled queues. The
+ODP reference implementations now support `ODP_NONBLOCKING_LF` queues.
+
+===== User pointer initialized to NULL
+The specification for `odp_packet_user_ptr()` is clarified that unless
+overridden by `odp_packet_user_ptr_set()` the value of NULL will be returned.
+
+===== Removal of `ODP_PKTIN_WAIT` option
+The `ODP_PKTIN_WAIT` option on `odp_pktin_recv_tmo()` and
+`odp_pktin_recv_mq_tmo()` is removed. Timeout options now consist of
+`ODP_PKTIN_NO_WAIT` and a user-supplied timeout value. Since this timeout
+value can be specified to be arbitrarily long, there is no need for an
+indefinite wait capability as provision of such a capability proved
+problematic for some ODP implementations.
+
+===== Addition of packet protocol APIs
+The APIs `odp_packet_l2_type()`, `odp_packet_l3_type()`, and
+`odp_packet_l4_type()` are added to return the Layer 2, 3, and 4 protocols,
+respectively, associated with packets that have been parsed to the
+corresponding layer. If the packet was not parsed to the associated layer
+these return `ODP_PROTO_Ln_TYPE_NONE`.
+
+===== Packet addressability improvements
+The documentation of `odp_packet_data()` is clarified to indicated when this
+shortcut may be used safely and a new API, `odp_packet_data_seg_len()`, is
+added that returns both the address of the start of packet data as well
+as the number of bytes addressable from that pointer.
+
+===== Asynchronous ordered locks
+Two new APIs, `odp_schedule_order_lock_start()` and
+`odp_schedule_order_lock_wait()` are added to allow for asynchronous
+ordered lock acquisition in addition to the existing synchronous
+`odp_schedule_order_lock()` API. In some implementations and applications,
+there may be a performance advantage to indicating the intent to acquire an
+ordered lock to allow the implementation to prepare for this while the
+application continues parallel processing and then enter the critical section
+protected by the ordered lock at a later time. In this case ordered lock
+protection is not guaranteed until the `odp_schedule_order_lock_wait()` call
+returns.
+
+===== IPsec API miscellaneous changes and enhancements
+IPsec support is further enhanced with the following:
+
+* The `odp_ipsec_ipv4_param_t` and `odp_ipsec_ipv6_param_t` structures
+are added to formalize the specification of IPv4 and IPv6 options in the
+`odp_ipsec_tunnel_param_t` configuration.
+
+* The `mode` field of the `odp_ipsec_out_t` is renamed to `frag_mode` for
+better clarity. In addition the `flag.frag-mode` option bit in the
+`odp_ipsec_out_opt_t` struct is defined to hold per-operation options for
+the `odp_ipsec_out_param_t` struct.
+
+* The `odp_ipsec_capability_t` struct returned by the `odp_ipsec_capability()`
+API is expanded to include the `odp_proto_chksums_t` available on inbound
+IPsec traffic. This indicates whether and how inbound packet checksums may
+be validated for decrypted IPsec traffic.
+
+===== IPsec Traffic Flow Confidentiality (TFC) support
+Traffic Flow Confidentiality (TFC) allows applications to defend against
+traffic analysis attacks by inserting dummy packets as well as add pad bytes
+to packets traversing IPsec flows.
+
+Dummy packets have an L3 type of `ODP_PROTO_L3_TYPE_NONE` in tunnel mode and
+`ODP_PROTO_L4_TYPE_NO_NEXT` in transport mode. Padded packets have additional
+data suffixed to them that extends beyond the L3 or L4 packet lengths.
+
+For RX processing, inline dummy packets may or may not be dropped from the
+inbound packet stream. For lookaside processing they are always visible. For TX
+processing, the `odp_ipsec_out_opt_t` struct specifies the `tfc_pad` bit if
+the packet is to be padded or the `tfc_dummy` bit if a dummy packet is to
+be inserted. The dummy packet length is specified by the `tfc_pad_len` option.
+
+=== Streamlined ABI Support
+ABI support has been reorganized to make it more modular and to omit headers
+and related ABI files when configure to disable this support.
+
+=== Reference Implementation Fixes and Improvements
+The ODP Reference Implementation corporates a number of improvements that
+result in better code organization as well as improved processing efficiency.
+
+==== Pktio null device support
+In the LNG Reference Implementations of ODP, the `odp_pktio_open()` API may now
+specify devices of class `null` to indicate the PktIO is treated as a dummy
+device. Null devices behave just like Linux `/dev/null` in that they never
+receive packets and simply discard any packets sent to them.
+
+Note that not all ODP implementations may support this device class. The
+specific device classes supported by each ODP implementation may vary and are
+listed in their own documentation.
+
+==== Runtime Scheduler Selection
+The ODP Reference Implementation offers both a default and a number of
+alternate scheduler implementations. Previously these were selectable only at
+`configure` time. They can now be dynamically selected at runtime by the use
+of the `ODP_SCHEDULER` environment variable. If this environment variable is
+not set, the default (basic) scheduler is used. It can be set to select
+alternate schedulers:
+
+* `ODP_SCHEDULER=basic` Explicitly selects the default scheduler
+* `ODP_SCHEDULER=sp` Selects the strict priority scheduler
+* `ODP_SCHEDULER=iquery` Selects the iQuery scheduler
+* `ODP_SCHEDULER=scalable` Selects the scalable scheduler
+
+==== Streamlined Queue Implementation
+The ODP Reference Implementation now uses a ring model for implementing
+ODP queues. This results in greatly improved efficiency for queue operations.
+The default maximum queue depth used is 4096 elements, and this information is
+returned via the `odp_queue_capability()` API.
+
+==== Validation Test Simplification
+The tests that are part of the validation test suite are reorganized and
+simplified by having a single test file for each API rather than separate
+CUnit driver files and test files.
+
+=== Test/Example Improvements
+
+==== Crypto Test Improvements
+The `crypto` validation test suite now offers better information on which
+crypto algorithms were skipped because they are not available. Testing of
+full HMAC lengths is now added
+
+==== ODP Generator Improvements
+The `odp_generator` example now offers configurable RX burst size, selectable
+packet handling (Direct I/O or Scheduled I/O), as well as streamlined packet
+processing.
+
+==== `l2fwd` Example Improvements
+The `l2fwd` example offers improved efficiency via better cache usage.
+
+=== Bug Fixes
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3517[Bug 3517]
+timer test might fail
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3572[Bug 3572]
+time_main test fails if run under heavy load
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3576[Bug 3576]
+classification: CoS queues in invalid table index
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3577[Bug 3577]
+classification: multiqueue CoS will always fail
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3578[Bug 3578]
+classification: requested number of queues is ignored in multiqueue CoS
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3579[Bug 3579]
+cls: capability to return max hash queues
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3581[Bug 3581]
+classification: invalid memory access in RSS hash
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3582[Bug 3582]
+classification: incorrect IPv6 RSS hash
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3594[Bug 3594]
+IPsec SA may be used before fully initialized
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3595[Bug 3595]
+IPsec SA lookup may leave extra SAs locked
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3597[Bug 3597]
+new generator test assumes that null:0 pktio is always present
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3613[Bug 3613]
+packet_main test can fail
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3618[Bug 3618]
+DPDK pktio stops receiving packets if all configured RX queues are not used
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3628[Bug 3628]
+Another timer_main failure
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3632[Bug 3632]
+Creating a pool with total size over 4.29GB (UINT32_MAX) leads to
+undefined behavior
+
+=== Known Issues
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2988[Bug 2988]
+ODP exposes symbols outside of odp*/_odp* namespace
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3611[Bug 3611]
+ODP linux-generic fails on AArch64 in non-ABI-compat mode
+
+== OpenDataPlane (1.17.0.0)
+=== New Features
+ODP v1.17.0.0 is Tiger Moth Release Candidate 1 (RC 1). It introduces
+significant new API support as well as functional refinements that expand
+ODP offload support to cover IPsec, as well as other improvements.
+
+==== APIs
+The following new and changed APIs are included in this release:
+
+===== Event Extensions
+The ODP event model has been expanded to include new types as well as event
+subtypes. Subtypes are used to qualify an event by indicating that the event
+carries additional metadata relating to specific usage or operations.
+
+New event `ODP_EVENT_IPSEC_STATUS` (to be discussed
+below) is added. The initial subtypes defined are: `ODP_EVENT_PACKET_BASIC`,
+`ODP_EVENT_PACKET_CRYPTO`, `ODP_EVENT_PACKET_IPSEC`, and
+`ODP_EVENT_NO_SUBTYPE`, which are also discussed below.
+
+Associated with this support, new event APIs are added:
+
+* `odp_event_subtype()` extracts the `odp_event_subtype_t` from an
+`odp_event_t`.
+
+* `odp_event_types()` returns the `odp_event_type_t` and `odp_event_subtype_t`
+of an `odp_event_t` in a single call.
+
+* `odp_event_type_multi()` scans a list of `odp_event_t` objects and returns
+the number that share the same indicated `odp_event_type_t`. This allows
+multiple events to be processed by groups of the same event type.
+
+* `odp_event_filter_packet()` scans a list of `odp_event_t` objects and
+partitions them into a returned array of `odp_packet_t` objects and a remaining
+array of non-packet events.
+
+* `odp_event_free_multi()` frees multiple events in a single call.
+
+* `odp_event_free_sp()` frees multiple events originating from the same
+pool in a single call. The caller must assure that the input events are from
+the same pool.
+
+===== IPsec support
+ODP Tiger Moth introduces comprehensive protocol offload support for IPsec,
+allowing applications to leverage the IPsec acceleration capabilities of many
+SoCs. Support includes APIs for Security Association (SA) creation and
+lifecycle management, as well as IPsec packet operations for inbound (RX) and
+outbound (TX) processing. Packet operations are further divided into lookaside
+and inline support.
+
+====== Lookaside Support
+Lookaside processing enables IPsec packets to be decrypted into plain packets
+or plain packets to be encrypted into IPsec packets in a single operation under
+explicit application control. This is useful for packets that need pre- or
+post-processing, or to better fit with existing application design.
+
+Two forms of lookaside processing are provided: the `odp_ipsec_in()` and
+`odp_ipsec_out()` APIs provide synchronous decrypt and encrypt support,
+respectively. The corresponding `odp_ipsec_in_enq()` and `odp_ipsec_out_enq()`
+APIs provide these same services in asynchronous form where operations can be
+launched and completed later.
+
+====== Inline Support
+In contrast to lookaside support, IPsec inline support permits applications to
+fully leverage the offload capabilities found in many SoCs by allowing inbound
+IPsec packets to be recognized and decrypted automatically before they are
+presented to the application for processing. This is done by configuring a
+Security Association (SA) and its associated PktIO to operate in inline mode.
+
+Similarly, following output SA and PktIO configuration, the
+`odp_ipsec_out_inline()` API permits a packet to be encrypted into an IPsec
+packet and automatically scheduled for TX processing in a single
+operation. Such "fire and forget" processing enables applications to leverage
+IPsec HW support for such processing in a portable manner.
+
+Applications using IPsec inline support need only "touch" a packet once
+compared to three times when using lookaside processing, leading to greater
+processing efficiency.
+
+====== IPsec Events
+New event types and subtypes are introduced to provide support for IPsec
+processing. The `ODP_EVENT_PACKET` type has a new subtype:
+`ODP_EVENT_PACKET_IPSEC` that provides extended metadata associated with
+IPsec packets that have been processed. The new `ODP_EVENT_IPSEC_STATUS`
+event, in turn, is used to report IPsec status events such as completion
+notifications associated with `odp_ipsec_sa_disable()` calls.
+
+The `odp_ipsec_result()` API is used to obtain IPsec result metadata from
+a packet that has event subtype `ODP_EVENT_PACKET_IPSEC`, while the
+`odp_ipsec_status()` API is used to obtain IPsec status metadata from an
+`ODP_EVENT_IPSEC_STATUS` event.
+
+===== Parser APIs
+Packet parsing has been overhauled with the introduction of two new APIs:
+
+* `odp_packet_parse()`
+* `odp_packet_parse_multi()`
+
+These use an `odp_packet_parse_param_t` struct to control the type and depth
+of parsing to be performed. These routines are intended to be used to
+process packets that have been decapsulated following IPsec decryption or other
+tunneling or on IP fragments after they have been reassembled.
+
+Associated with this improved parse support, the `odp_parser_layer_t` struct
+is deprecated and replaced with a more general `odp_proto_layer_t` struct that
+is used both in PktIO configuration as well as the new parser APIs.
+
+===== Crypto AES-CTR and AES-GMAC Support
+The ODP crypto APIs are extended to provide support for AES-CTR cipher and
+AES-GMAC authentication modes, reflecting the growing availability of
+accelerated support for these.
+
+===== Crypto removal of DES-CBC
+DES-CBC is no longer considered secure and support for it is removed in ODP.
+
+===== Crypto move AAD length to sessions
+The Additional Authentication Data (AAD) length is now part of the
+`odp_crypto_session_t` rather than individual crypto operations. This provides
+better compatibility with DPDK, which made a similar API adjustment in it's
+17.08 release.
+
+===== Crypto Packet APIs
+While the `odp_crypto_operation()` API is retained for compatibility,
+new packet-oriented variants are introduced that provide additional
+capabilities and flexibility. These APIs are:
+
+* `odp_crypto_op()` Performs synchronous crypto operations on one or more
+input packets under the control of an associated `odp_crypto_packet_op_param_t`
+struct.
+
+* `odp_crypto_op_enq()` Performs asynchronous crypto operations on or or more
+input packets under the control of an associated `odp_crypto_packet_op_param_t`
+struct.
+
+While `odp_crypto_operation()` calls result in `ODP_EVENT_CRYPTO_COMPL` events
+for compatibility, the new packet-oriented APIs result in `ODP_EVENT_PACKET`
+events that carry the new event subtype `ODP_EVENT_PACKET_CRYPTO`. These
+packets contain additional metadata associated with the crypto operation.
+New APIs added for manipulating this metadata include:
+
+* `odp_crypto_packet_from_event()` converts an `odp_event_t` of type
+`ODP_EVENT_PACKET` and subtype `ODP_EVENT_PACKET_CRYPTO` to an `odp_packet_t`.
+
+* `odp_crypto_packet_to_event()` converts an `odp_packet_t` crypto packet
+back into an `odp_event_t`.
+
+* `odp_crypto_result()` extracts the `odp_crypto_packet_result_t` struct that
+contains the crypto metadata associated with an `odp_packet_t` of event
+subtype `ODP_EVENT_PACKET_CRYPTO`. This struct provides a summary bit that
+says whether the operation completed successfully as well as
+`odp_crypto_op_status_t` fields for the `cipher_status` and `auth_status` if a
+problem was detected.
+
+===== Classification Random Early Detection (RED) Support
+Random Early Detection (RED) provides a means for input HW to ensure that
+traffic is treated fairly under conditions of temporary resource overload due
+to excessive inbound traffic. ODP RED support provides the ability to measure
+and respond to resource pressure on either pools or queues, and only provides
+flexibility in how such conditions are to be processed. They can result in
+input packet drops or backpressure being indicated by transmitting pause
+frames, depending on the underlying platform capabilities.
+
+The `odp_cls_capability_t` struct returned by the `odp_cls_capability()` API
+has been expanded to cover this support.
+
+===== Time difference in nanoseconds
+The new `odp_time_diff_ns()` API permits the delta between two `odp_time_t`
+values to be computed in a single call.
+
+===== PktIO API Changes
+====== PktIO Maximum Frame Lengths
+The `odp_pktio_mtu()` API is deprecated and replaced by two new APIs:
+`odp_pktin_maxlen()` and `odp_pktout_maxlen()`. These return the maximum
+sized packets that are supported for RX and TX processing,
+respectively, on a given `odp_pktio_t`.
+
+====== PktIO settable MAC address
+The `odp_pktio_mac_addr_set()` API is added to allow setting of the MAC
+address associated with an `odp_pktio_t`. The `odp_pktio_set_op_t` field of
+the `odp_pktio_capability_t` returned by the `odp_pktio_capability()` API now
+includes the `mac_addr`` field to indicate that this `odp_ptkio_t` supports
+setting its MAC address.
+
+====== Multiple loop devices
+The reserved device name `loop` is now extended to `loopX` where X == integer
+(_e.g.,_ `loop1`, `loop2`, etc.). For compatibility, `loop` is a synonym for
+`loop0`.
+
+===== Pool API Changes
+====== Pool extent info
+The `odp_pool_info()` API is extended to return the `min_data_addr` and
+`max_data_addr` fields. These provide information about the minimum and maximum
+application-visible addresses that may be seen in objects allocated from a
+particular `odp_pool_t`. Some applications use this information to enable them
+to store buffer addresses in compressed format. For example, if the
+"span" of valid addresses is less than 4GB this allows a 64-bit buffer address
+to be stored as a 32-bit offset.
+
+Since this is purely informational, ODP implementations are under no constraint
+as to what addresses may be returned for these fields. 0 and `UNINTPTR_MAX`
+may be used if there are no limits on pool extents.
+
+====== Pool subparameter support
+The `odp_pool_param_t` structure has been expanded to provide more flexibility
+to support platforms that are able to offer multiple segment sizes within a
+single pool. This can lead to greater storage efficiency. These are called
+subparameters and implementations supporting up to 7 of these are accommodated
+with these extensions.
+
+The `odp_pool_capability_t` structure is expanded to return the number of
+subparameters supported by this implementation. The application, in turn,
+specifies its expected packet size and number distribution in the
+`odp_pool_pkt_subparam_t` structure that is part of the `odp_pool_param_t`
+used to define the characteristics of `ODP_POOL_PACKET` pools.
+
+This is fully compatible with previous packet pool support since ODP
+implementations are under no obligation to support pool subparameters and
+these, when present, are advisory in nature. They simply serve to allow the
+application to better communicate its expected packet distribution within a
+pool so that the ODP implementation may better optimize storage use in the
+pool.
+
+===== Checksum support
+Checksum processing support has been formalized with the addition of APIs for
+determining packet checksum status, controlling packet checksum processing,
+retrieving partially computed checksums on packets, and computing checksum
+partial sums for memory areas.
+
+====== Checksum status
+The APIs `odp_packet_l3_chksum_status()` and `odp_packet_l4_status()` are
+added to allow the results of packet input checksum processing to be
+queried. These APIs return an `odp_packet_chksum_status_t` enum that indicates
+whether checksum validation processing was performed and if so whether the
+layer 3 or 4 checksum was found to be valid. This is applicable to both
+normal packet input as well as those processed via IPsec.
+
+====== Checksum insertion
+PktIOs output checksum processing is configured as part of the
+`odp_pktout_config_opt_t` struct used as input to `odp_pktio_config()` API.
+These control whether L3 and/or L4 checksums are to be inserted by default
+as part of packet TX processing.
+
+Individual packets may override these defaults via the new
+`odp_packet_l3_chksum_insert()` and `odp_packet_l4_chksum_insert()` APIs. These
+take precedence over the PktIO default, allowing checksums to be inserted
+when the PktIO default is to not insert checksums or to suppress checksum
+insertion if when the PktIO default is to insert checksums.
+
+====== One's complement sums
+Two new APIs: `odp_packet_ones_comp()` and `odp_chksum_ones_comp16()` are
+added to assist in application-managed checksum processing. If an
+implementation has computed a partial checksum as part of the receive
+processing for an IP fragment, for example, then `odp_packet_ones_comp()` can
+be used to retrieve this computed value, as well as the byte range over which
+it was computed. The `odp_chksum_ones_comp16()` API, by contrast, allows the
+application to perform a 16-bit ones-complement sum over a range of in-memory
+bytes. Together these can be used to calculate IPv4, TCP, and UDP checksums.
+
+===== Packet multi-event conversion and single pool support
+New packet APIs have been added to streamline packet processing:
+
+* `odp_packet_free_sp()` is the same as `odp_packet_free_multi()` except that
+the application guarantees that all packets come from the same pool.
+
+* `odp_packet_from_event_multi()` allows multiple events to be converted
+from `odp_event_t` to `odp_packet_t` objects in a single call. The caller
+guarantees that all input `odp_event_t` objects are of type `ODP_EVENT_PACKET`.
+
+* `odp_packet_to_event_multi()` converts multiple `odp_packet_t` objects to
+corresponding `odp_event_t` objects in a single call.
+
+===== Shared Memory API changes
+Several changes have been made to the ODP shared memory APIs:
+
+* The `name` field used as input to `odp_shm_reserve()` is now optional.
+If specified as `NULL` the shared memory area is anonymous and cannot be
+looked up with `odp_shm_lookup()`. There is also no requirement that names be
+unique. Duplicate names result in indeterminate output from `odp_shm_lookup()`.
+
+* The `odp_shm_info_t` now includes the `page_size` of the shared memory block
+and it's (optional) name.
+
+* `odp_shm_print()` API is added to print implementation-defined information
+associated with the `odp_shm_t` to the ODP log for diagnostic purposes.
+
+===== Add support for non-blocking Queues
+New queue attributes are introduced to characterize queue behavior as
+blocking or non-blocking. A blocking queue may stall other threads if a thread
+is interrupted or suspending during an enqueue or dequeue operation.
+Nonblocking queues may be either lock free or wait free and provide
+progress and fairness guarantees to all threads regardless of
+interruptions or stalls on the part of threads performing queue operations.
+
+The various `odp_nonblocking_t` attributes available are returned by the
+`odp_queue_capability()` API for both plain and scheduled queues and are in
+turn requested as part of the `odp_queue_param_t` struct passed to the
+`odp_queue_create()` API. The intent is to allow applications that have
+realtime response requirements to better express these needs and utilize
+platform-specific capabilities in this area.
+
+===== Scheduler ordered lock API changes
+The following changes have been made to the scheduler APIs:
+
+* Documentation clarifies that an ordered context may only hold one ordered
+lock at a time. Results are undefined if a second ordered lock is attempted to
+be acquired while already holding one.
+
+* The `odp_schedule_order_unlock_lock()` API is added to permit an ordered
+context to switch from one ordered lock to another in a single operation.
+
+===== Timer Capabilities
+The `odp_timer_capability()` API is added to return an `odp_timer_capability_t`
+struct that details platform-specific timer capabilities for application use.
+The only capability currently defined is `highest_res_ns`, which indicates the
+highest supported resolution supported by a timer. This is the minimum valid
+value for the `res_ns` timer pool parameter.
+
+=== Scalable Scheduler
+The `odp-linux` reference implementation adds a new _scalable scheduler_ to
+the existing default, strict priority, and iquery schedulers. This is enabled
+by:
+
+`./configure --enable-schedule-scalable`
+
+The scalable scheduler is designed to offer superior scalability in many core
+environments, especially on AArch64 platforms.
+
+=== Miscellaneous Fixes and Improvements
+The following miscellaneous improvements have been made to the `linux-generic`
+reference implementation of ODP.
+
+==== Additional packet inline functions
+When compiling with `--enable-abi-compat=no` the following additional packet
+functions are inlined:
+
+* `odp_packet_l2_offset()`
+* `odp_packet_l2_ptr()`
+* `odp_packet_l3_offset()`
+* `odp_packet_l3_ptr()`
+* `odp_packet_l4_offset()`
+* `odp_packet_l4_ptr()`
+
+==== Dependencies
+The ODP test suite now automatically skips C++ tests if no C++ compiler
+is available.
+
+The odp_pktio_ordered tests are only performed if PCAP is available.
+
+The DEPENDENCIES file has been updated to reflect build/test requirements for
+running under Red Hat Linux distributions.
+
+==== DPDK 17.08 Support
+PktIO DPDK support has been upgraded to DPDK 17.08.
+
+=== Test/Example improvements
+=== l2fwd Example
+A verbose option is added to provide more detail on test runs.
+
+=== ODP generator
+Numerous performance improvements have been made that results in significantly
+better I/O rates. This includes a configuration option to control checksum
+usage.
+
+=== Bug Fixes
+==== https://bugs.linaro.org/show_bug.cgi?id=3465[Bug 3465]
+CID 1461688: odp_pool_create: Dereference before null check
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3466[Bug 3466]
+CID 1396968: buggy check
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3491[Bug 3491]
+l2fwd segfaults on api-next with dpdk checksum insertion override
+
+=== Known Issues
+==== https://bugs.linaro.org/show_bug.cgi?id=3210[Bug 3210]
+packet header parsing routines should verify header checksums
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3517[Bug 3517]
+timer test might fail
+
+== OpenDataPlane (1.16.0.0)
+=== New Features
+ODP v1.16.0.0 is the final preview release before the official release of
+Tiger Moth. It introduces new APIs and extensions, as well as bug fixes and
+functional improvements.
+
+==== APIs
+The following new and changed APIs are included in this release:
+
+===== Initialization Changes
+The new `odp_feature_t` type is introduced that defines various feature bits
+for ODP components. This is used in an expanded `odp_init_t` argument to
+`odp_init_global()` to specify which ODP features are unused by the
+application. For example, if the application knows it will not be making use
+of crypto features or the classifier, this may permit the ODP implementation
+to configure itself more efficiently. Results are undefined if an application
+asserts that it will not be using a feature and it attempts to do so
+afterwards.
+
+Associated with this new support the `odp_init_param_init()` API is added
+to initialize the `odp_init_t` struct to default values.
+
+===== Packet API Changes
+
+* The `odp_packet_unshared_len()` API is removed. Testing showed that this
+API was non-essential and conflicted with the goal of implementation
+efficiency.
+* The `odp_print_packet_data()` API is added. This permits packet data to
+be logged along with platform-defined metadata for debugging or diagnostic
+purposes.
+
+===== PktIO API Changes
+
+* The `loop_supported` attribute of the `odp_pktio_capability_t` struct is
+deprecated since this is redundant. The `enable_loop` field of the
+`odp_pktio_config_t` struct (which is returned as part of the
+`odp_packet_capability_t` struct) is the proper way to determine whether a
+PktIO supports loopback mode.
+
+===== System Info API Changes
+
+* The documentation for the `odp_sys_huge_page_size()` API is updated to
+clarify that a 0 return code indicates that huge pages are not supported by
+this platform.
+* The `odp_sys_huge_page_size_all()` API is added to return all
+huge page sizes supported by this platform.
+
+===== Timer API Changes
+
+* The documentation for the various parameters contained in the
+`odp_timer_pool_param_t` struct are expanded and clarified.
+
+=== Miscellaneous Fixes and Improvements
+
+==== Default Packet Headroom
+The default packet headroom in `odp-linux` has been increased from 66 to
+128 bytes for better compatibility with `odp-dpdk`.
+
+==== Zero-copy Packet References
+The `odp-linux` reference implementation now fully supports zero-copy
+packet references. Previously these APIs were implemented via packet copies,
+which while functionally correct, were not how these APIs are intended to
+operate.
+
+==== DPDK Zero-copy I/O support
+The `--enable-dpdk-zero-copy` `configure` option is added to allow DPDK PktIO
+devices to avoid data copies, leading to improved performance.
+
+==== DPDK Checksum offload support
+DPDK PktIO now makes use of RX and TX IP/UDP/TCP checksum offload.
+
+==== Shared memory stored in /dev/shm
+In the `odp-linux` reference implementation, shared memory is now backed to
+`/dev/shm` rather than `/tmp` for better reliability and robustness. This may
+be overridden as part of ODP build-time customization if desired.
+
+==== IPC Improvements
+PktIO IPC support has received improvements in both performance and
+reliability and is now suitable for development use.
+
+==== Netmap Improvements
+The thread ID is now used to create unique vdev MAC addresses to avoid
+conflicts with multiple ODP processes running on the same host system.
+
+==== `drv` directory removed
+The `include/odp/drv` directory and related files have been removed. Driver
+support is moved to a follow-on ODP release, so removing these files avoids
+confusion as they are still incomplete.
+
+=== Dependency Changes
+
+==== Dependency on autoconf-archive removed
+Since some build environments do not supply autoconf-archive, this dependency
+is removed.
+
+==== DPDK support upgraded to 17.08 release
+The ODP DPDK Packet I/O support has been upgraded to work with the DPDK 17.08
+release.
+
+==== Added support for OpenSSL 1.1.x releases
+ODP use of OpenSSL for crypto processing has been upgraded to allow use of
+OpenSSL 1.1.x.
+
+=== Build System Restructure
+The ODP build system has been overhauled to support more comprehensive and
+efficient automated testing under Travis CI. Greater use of Autoconf is now
+made to control ODP configuration and build options, permitting greater
+environmental flexibility. This includes an expanded range of test coverage,
+including cross-compilation support for ARMv8, MIPS,and Power architectures,
+as well as testing under the latest levels of GCC and clang.
+
+=== Arm Architecture Support Improvements
+
+* ARMv8 generic timer support is now included
+* Improved time efficiency and accuracy by using native ARMv8 time/clock
+instructions.
+
+==== Test Improvements
+The `test` directory has been reorganized and streamlined. Platform-specific
+tests are moved from `test/linux-generic` to
+`platform/linux-generic/test/`. As a result, the `test/common_plat`
+directory is deleted so that `test/validation`, `test/performance`, etc. are
+now used for all platform-independent tests.
+
+==== Examples Improvements
+
+===== IPv4 Fragmentation Reassembly Example
+The `ipfragreass` example program has been added to demonstrate IPv4 fragment
+reassembly.
+
+===== ODP Generator Improvements
+The `odp_generator` example program now uses packet references for improved
+performance in UDP and ICMP traffic. The program also now makes use of HW
+checksum offload support, when available.
+
+=== Documentation Improvements
+
+* The ODP Users Guide has clarified usage information about the ODP time
+APIs for better portability.
+* A section has been added to the ODP Users Guide on API specification
+principles. This clarifies expected behavior of ODP applications and
+implementations and makes explicit what the specification means by "undefined
+behavior".
+* All Doxygen used in ODP is upgraded to conform to the stricter documentation
+requirements introduced by Doxygen 1.8.13.
+
+=== Bug Fixes
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2254[Bug 2254]
+check-odp: valgrind generates "No rule to make target"
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2407[Bug 2407]
+test odp_l2fwd_run.sh contains todo items
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2812[Bug 2812]
+Helper/test/process fails on a single core system
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2861[Bug 2861]
+Remove redundant loop_support parameter in pktio capability
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2938[Bug 2938]
+Make file deps failure
+
+==== https://bugs.linaro.org/show_bug.cgi?id=2976[Bug 2976]
+IP headers checksum functions are incorrect
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3024[Bug 3024]
+odp_traffic_mngr example is broken
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3026[Bug 3026]
+pktio_ipc_run test can fail due to segfault
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3043[Bug 3043]
+User guide error (packet diagram fix)
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3052[Bug 3052]
+api-next out of tree build broken
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3066[Bug 3066]
+Cross compile broken for ARMv8
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3126[Bug 3126]
+IPC pktio test fails with taskset -c 1-2
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3177[Bug 3177]
+Test case for classification enable
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3182[Bug 3182]
+Memory allocation checks (in traffic manager)
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3216[Bug 3216]
+Adding --enable-helper-linux configure flag breaks build
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3238[Bug 3238]
+Doxygen warnings on helper header files
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3241[Bug 3241]
+codecov: _odp_packet_cmp_data is not covered
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3242[Bug 3242]
+setup_pktio_entry missing unlock
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3249[Bug 3249]
+odp_cpu_hz() does not work on all Linux distros
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3262[Bug 3262]
+Missing doxygen detected by Travis
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3289[Bug 3289]
+'num_queues' isn't ignored when "classifier_enable" is enabled
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3300[Bug 3300]
+Validation tests cannot be disabled after commit b4d17b1
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3411[Bug 3411]
+wrong openssl_lock pointer type
+
+=== Known Issues
+
+==== https://bugs.linaro.org/show_bug.cgi?id=3245[Bug 3245]
+Cannot run l2fwd application on Cavium ThunderX platform
+
== OpenDataPlane (1.15.0.0)
=== New Features
ODP v1.15.0.0 continues the preview of Tiger Moth, introducing new APIs and
@@ -370,7 +3997,7 @@ directory to provide stress-testing of packet ordering features of ODP.
=== Documentation
In addition to expanded documentation related to the new packet reference APIs,
-a section on applicatin portability has been added that discusses the types
+a section on application portability has been added that discusses the types
of portability offered by ODP and the tradeoffs that application writers should
consider when using ODP.
@@ -554,7 +4181,7 @@ manipulation.
==== Ordered Queue Improvements
The implementation of ordered queues has been streamlined and made more
-scaleable in multi-core systems.
+scalable in multi-core systems.
==== Packet Segmentation Improvements
The more advance segmentation capabilities present in the new ODP packet
@@ -1060,9 +4687,9 @@ containing a larger number of CPUs (typically 24 or more).
implementation-specific capability limits for enhanced program portability.
==== Classification
-* Addtion of a structure that defines system level classification capability
+* Addition of a structure that defines system level classification capability
* Addition of range PMRs to complement the existing value PMRs to permit
- application to specifiy classification rules over a range of values.
+ application to specify classification rules over a range of values.
==== Cryptography
* Provides a way to get the available cipher and authentication algorithms.
@@ -1087,13 +4714,13 @@ containing a larger number of CPUs (typically 24 or more).
* Replaced config definition for maximum pktio entries with maximum packet IO
index call
* Added the classify_enable bit to the odp_pktin_queue_param_t that allows
- applications to explicity control which pktin queues are subject to full
+ applications to explicitly control which pktin queues are subject to full
classifier support.
* Addition of pktin configuration options to control packet timestamping
and checksum validation processing.
* Addition of pktout configuration options to control packet checksum
offload processing.
-* Add the ability to query (via capabilties) whether a pktio interface
+* Add the ability to query (via capabilities) whether a pktio interface
supports operating in loopback mode and if so to enable/disable this
mode of operation.
* Round out the polling APIs be adding the ability to receive packets in
@@ -1119,8 +4746,8 @@ containing a larger number of CPUs (typically 24 or more).
* Add a capability inquiry API to obtain implementation traffic mngr limits.
* Split TOS marking into two separate calls.
* Add new APIs to support VLAN, ECN, and drop precedence marking.
-* Add destroy APIs for shapers, scheduler profiles, threshholds, WRED profiles,
- TM queues, and TM nodes for symmetry and completeness to allow applictions
+* Add destroy APIs for shapers, scheduler profiles, thresholds, WRED profiles,
+ TM queues, and TM nodes for symmetry and completeness to allow applications
to terminate gracefully without resource leakage.
* Add the ability to disconnect TM queues from their fanouts.
* Add TM node contexts to permit applications to anchor user context areas
@@ -1205,7 +4832,7 @@ opendataplane (1.8.0.0)
- test: performance: crypto: measure crypto operation performance
- test: pktio_perf: finalize port to use new pktio api
- test: pktio_perf: port to use new pktio api
- - validataion: packet: add tests for broadcast and multicast flags
+ - validation: packet: add tests for broadcast and multicast flags
- validation: classification: add test case for odp_cos_drop() function
- validation: classifier: port to use new pktio api
- validation: packet: test if user area is properly set
@@ -1447,10 +5074,10 @@ opendataplane (1.7.0.0)
- linux-generic: sysinfo: make the cpu_hz per-CPU data
- linux-generic: sysinfo: make the model_str per-CPU data
- linux-generic: sysinfo: move ARM system info codes to default arch file
- - linux-generic: sysinfo: move MIPS system info codes to its plarform file
- - linux-generic: sysinfo: move PowerPC system info codes to its plarform file
+ - linux-generic: sysinfo: move MIPS system info codes to its platform file
+ - linux-generic: sysinfo: move PowerPC system info codes to its platform file
- linux-generic: sysinfo: move cpu_arch_str to odp_system_info_t
- - linux-generic: sysinfo: move x86 system info codes to its plarform file
+ - linux-generic: sysinfo: move x86 system info codes to its platform file
- linux-generic: sysinfo: rename odp_cpu_hz_current with odp_ prefix
- linux-generic: sysinfo: rename variable cpu_hz to cpu_hz_max
- linux-generic: sysinfo: revise odp_cpu_hz() to return current frequency
@@ -1663,7 +5290,7 @@ opendataplane (1.4.0.0)
- odp_cpumask_def_control() renamed to odp_cpumask_default_control()
- odp init extended with num worker and control threads
- new: int odp_queue_lock_count(odp_queue_t queue);
- - refine api doc for scheduler and schedule orderd locks
+ - refine api doc for scheduler and schedule ordered locks
- argument of odp_schedule_order_lock() and odp_schedule_order_unlock changed to unsigned
- new: int odp_thread_count_max(void)
- ** Packet **
@@ -1740,7 +5367,7 @@ opendataplane (1.4.0.0)
- pktio: fill in L2 parse results by default
- pktio: implement odp_pktio_param_init() API
- packet: implement flow hash support
- - schedule: fix odp_schdule_wait_time
+ - schedule: fix odp_schedule_wait_time
- queue: change lock_index from uint32_t to unsigned to match API
- queue: direct internal enqueues to target queue
- queue: fix pktout_enqueue() logic
@@ -1837,7 +5464,7 @@ opendataplane (1.3.0.0)
opendataplane (1.2.0.0)
* API:
- docs: doxygen grouping clean up and remove excess references to ODP
- - pool: remove shm paramter from odp_pool_create()
+ - pool: remove shm parameter from odp_pool_create()
- packet_io: clarify what happens when not all packets are sent
- cpumask: added default masks and cpumask_setall
- thrmask: added thread mask
@@ -1852,7 +5479,7 @@ opendataplane (1.2.0.0)
- deleted odph_linux_cpumask_default
* test:
* validation:
- - tests execution moved to platfrom side
+ - tests execution moved to platform side
- test: pktio_perf: add missing atomic init
- test: synchronizers: use thread_id instead of cpu_id to detect slow threa
- validation: pktio: do not dequeue from scheduled queue
@@ -2013,7 +5640,7 @@ opendataplane (1.0.2)
- example: l2fwd print packets per second
- linux-generic: linux: destroy used pthread attr
* bugs:
- - validation: packet: use non-zero as true indicato
+ - validation: packet: use non-zero as true indication
- linux-generic: pktio check for NULL entry
- linux-generic: fix incorrect pmr_term_value update in odp_pmr_create_xxx() function
- replace strtok_r with strtok and fix leaks
diff --git a/DEPENDENCIES b/DEPENDENCIES
index a194cad1c..5af0d4ac3 100644
--- a/DEPENDENCIES
+++ b/DEPENDENCIES
@@ -1,24 +1,45 @@
Prerequisites for building the OpenDataPlane (ODP) API
-1. Linux kernel >= 2.6.32
+1. Linux
+ CentOS 7 (kernel v3.10) is the oldest Linux distributions tested by the ODP
+ CI. Earlier versions may or may not work.
- Earlier versions may or may not work.
+ For CentOS/RedHat distros, configure the system to use Fedora EPEL repos and
+ third-party packages:
+ $ sudo yum install epel-release
+
+ Additionally, for CentOS 8 distros, enable the powertools repository:
+ $ sudo yum install dnf-plugins-core
+ $ sudo yum config-manager --set-enabled powertools
2. autotools
automake
autoconf
libtool
+ pkg-config
On Debian/Ubuntu systems:
- $ sudo apt-get install automake autoconf libtool
+ $ sudo apt-get install automake autoconf libtool pkg-config
On CentOS/RedHat/Fedora systems:
- $ sudo yum install automake autoconf libtool
+ $ sudo yum install automake autoconf libtool pkgconfig
+
+3. Required packages
+
+ Libraries currently required to link: libconfig, openssl, libatomic
-3. Required libraries
+ On Debian/Ubuntu systems:
+ $ sudo apt-get install libconfig-dev libatomic
+
+ On CentOS/RedHat/Fedora systems:
+ $ sudo yum install libconfig-devel libatomic
- Libraries currently required to link: openssl
+ It is possible to build ODP without OpenSSL by passing flag
+ --without-openssl to configure script. However this will result in
+ ODP-generic not supporting any cryptography algorithm (except NULL) and any
+ strong random generation (only BASIC is supported and it is not
+ cryptographically secure). Use at your own risk!
3.1 OpenSSL native compile
@@ -51,143 +72,166 @@ Prerequisites for building the OpenDataPlane (ODP) API
# Build and install 32 bit version of openssl
$ ./Configure linux-generic32 --cross-compile-prefix=arm-linux-gnueabihf- \
- --prefix=/home/user/src/install-openssl shared
+ --prefix=/home/${USER}/src/install-openssl shared
$ make
$ make install
# Build and install 64 bit version of openssl
$ ./Configure linux-generic64 --cross-compile-prefix=aarch64-linux-gnu- \
- --prefix=/home/user/src/install-openssl-aarch64 shared
+ --prefix=/home/${USER}/src/install-openssl-aarch64 shared
$ make
$ make install
# You may now build either 32 or 64 bit ODP
- $ git clone git://git.linaro.org/lng/odp.git odp
+ $ git clone https://github.com/OpenDataPlane/odp.git odp
$ cd odp
$ ./bootstrap
# Build 32 bit version of ODP
$ ./configure --host=arm-linux-gnueabihf \
- --with-openssl-path=/home/user/src/install-openssl
+ --with-openssl-path=/home/${USER}/src/install-openssl
$ make
# Or build 64 bit version of ODP
$ ./configure --host=aarch64-linux-gnu \
- --with-openssl-path=/home/user/src/install-openssl-aarch64
+ --with-openssl-path=/home/${USER}/src/install-openssl-aarch64
$ make
-3.3 Netmap packet I/O support (optional)
+3.3 ARMv8 Crypto native compile (optional)
- Netmap accelerated ODP packet I/O.
+ AArch64cryptolib is a from-scratch implementation of cryptographic primitives
+ aiming for optimized performance on Arm A-class cores. This implementation
+ requires Armv8 cryptography extensions. Currently, only AES-GCM algorithm is
+ supported.
-3.3.1 Building netmap kernel modules
+3.3.1 Building ARMv8 crypto library
- ODP works at least with the latest release version of netmap, which is
- currently 11.2. However, if possible one should try to use the latest netmap
- master branch commit for the best performance and the latest bug fixes.
+ # Checkout and build AArch64cryptolib code
+ $ git clone https://github.com/ARM-software/AArch64cryptolib
+ $ cd AArch64cryptolib/
+ $ make
- # Checkout netmap code
- $ git clone https://github.com/luigirizzo/netmap.git
- $ cd netmap
- $ git checkout v11.2 (optional)
+ For additional instructions, refer to README.md
- This is enough to build ODP. If you don't want to build netmap kernel
- modules you can jump to section 3.3.2.
+3.3.2 Building ODP with ARMv8 crypto library
+ $ ./bootstrap
- Netmap consists of a core kernel module (netmap.ko), optional modified
- device drivers and user space API headers to access the netmap
- functionality. It is recommended to build both the core module and modified
- device drivers for optimal performance.
+ # Append the Armv8 crypto library path to PKG_CONFIG_PATH if necessary
+ $ echo $PKG_CONFIG_PATH
+ $ export PKG_CONFIG_PATH=<AArch64cryptolib-path>/pkgconfig:$PKG_CONFIG_PATH
- Netmap builds as an out-of-tree kernel module, you need matching kernel
- sources to compile it. General build instructions can be found in the packet
- README: https://github.com/luigirizzo/netmap/blob/master/LINUX/README.
+ # Compile and build ODP with Armv8 crypto library
+ $ ./configure --with-crypto=armv8crypto
+ $ make
- If you are running Ubuntu/Debian with the stock kernel and you want to
- compile both netmap.ko and modified drivers, these steps will guide you
- through it.
+3.4 Multi-buffer Crypto for IPsec Library (optional)
- # Download kernel headers
- $ sudo apt-get install linux-headers-$(uname -r)
+ Multi-Buffer Crypto for IPsec Library is a set of functions that
+ implement authentication and encryption processing for IPsec, these functions
+ take advantage of SIMD instructions to improve performance.
- # Download kernel source matching to the headers
- $ sudo apt-get install linux-source
- # or
- $ apt-get source linux-image-$(uname -r)
+ Note ODP assumes that IPSec MB library is compiled with SAFE_PARAM enabled
+ (enabled by default), otherwise crypto operations with too long cipher/auth
+ ranges will have undefined behaviour.
- The source archive will be placed in /usr/src/linux-source-<kernel-version>
- (or in the current directory if using apt-get source). You will need to
- locate it and extract it to a convenient place.
+3.4.1 Building Multi-buffer Crypto for IPSec Library for Arm
- # Compile netmap
- $ cd <netmap_dir>/LINUX
- $ ./configure --kernel-sources=<path_to_kernel_src>
+ # Checkout and build Arm code
+ $ git clone https://git.gitlab.arm.com/arm-reference-solutions/ipsec-mb.git
+ $ cd ipsec-mb/
+ $ git checkout SECLIB-IPSEC-2022.12.13
$ make
+ $ sudo make install
-3.3.2 Building ODP
+ For additional instructions, refer to README.md in crypto library repository.
- $ cd <odp_dir>
+3.4.2 Building ODP with Multi-buffer IPSec Library
$ ./bootstrap
- $ ./configure --with-netmap-path=<netmap_dir>
+
+ # Compile and build ODP with Multi-buffer IPSec library
+ $ ./configure --with-crypto=ipsecmb
$ make
-3.3.3 Inserting netmap kernel modules
- In order to use netmap I/O you need to insert at least the core netmap
- kernel module.
+3.5 DPDK packet I/O support (optional)
- $ cd <netmap_dir>/LINUX
- $ sudo insmod netmap.ko
+ Use DPDK for ODP packet I/O. Currently supported DPDK versions are v19.11,
+ v20.11, v21.11, and v22.11 (recommended).
- To insert the optional modified drivers you first need to remove the
- original drivers, if loaded (and if not linked into the kernel). For
- example, if using ixgbe:
+ Note: only packet I/O is accelerated with DPDK. See
+ https://github.com/OpenDataPlane/odp-dpdk.git
+ for a full DPDK based ODP implementation.
- $ cd <netmap_path>/LINUX
- $ sudo rmmod ixgbe
- $ sudo insmod ixgbe/ixgbe.ko
+3.5.1 DPDK pktio requirements
- To restore the original drivers you should be able to use modprobe.
+ DPDK pktio adds a dependency to NUMA library.
+ # Debian/Ubuntu
+ $ sudo apt-get install libnuma-dev
+
+ # CentOS/RedHat/Fedora
+ $ sudo yum install numactl-devel
+
+3.5.2 Native DPDK install
+ # Debian/Ubuntu starting from 20.04
+ $ sudo apt-get install dpdk-dev
+
+3.5.3 Build DPDK v19.11 from source
+ $ git clone https://dpdk.org/git/dpdk-stable --branch 19.11 --depth 1 ./<dpdk-dir>
+
+ # Make and edit DPDK configuration
+ $ export TARGET="x86_64-native-linuxapp-gcc"
+ $ make config T=${TARGET} O=${TARGET}
+ $ pushd ${TARGET}
-3.3.4 Running ODP with netmap I/O
+ # Enable pcap PMD to use DPDK pktio without supported NICs:
+ $ sed -ri 's,(CONFIG_RTE_LIBRTE_PMD_PCAP=).*,\1y,' .config
+ $ popd
- ODP applications will use netmap for packet I/O by default as long as the
- netmap kernel module is loaded. If socket I/O is desired instead, it can be
- activated by setting the environment variable ODP_PKTIO_DISABLE_NETMAP.
+ # Build and install DPDK
+ $ make install T=${TARGET} EXTRA_CFLAGS="-fPIC" DESTDIR=./install
-3.4 DPDK packet I/O support (optional)
+ # Configure ODP
+ $ ./configure --with-dpdk-path=<dpdk-dir>
- Use DPDK for ODP packet I/O.
+3.5.4 Build DPDK v20.11 and onwards from source
+ $ git clone https://dpdk.org/git/dpdk-stable --branch <version, e.g. 22.11> --depth 1 ./<dpdk-dir>
- Note: only packet I/O is accelerated with DPDK. Use
- https://git.linaro.org/lng/odp-dpdk.git
- for fully accelerated odp dpdk platform.
+ # Prepare the build directory
+ $ cd <dpdk-dir>
+ $ meson build
+ $ cd build
-3.4.1 Building DPDK and ODP with DPDK pktio support
+ # Optionally, configure the location where DPDK will be installed. By default,
+ # DPDK will be installed in /usr/local:
+ $ meson configure -Dprefix=$(pwd)/../install
- DPDK packet I/O has been tested to work with DPDK v17.02.
+ # Build and install DPDK
+ $ ninja install
- Follow steps in ./scripts/build-pktio-dpdk
+ # Configure ODP
+ $ ./configure --enable-dpdk
+ # Or, if DPDK was not installed to the default location, set PKG_CONFIG_PATH:
+ $ PKG_CONFIG_PATH=<dpdk-dir>/install/lib/x86_64-linux-gnu/pkgconfig ./configure --enable-dpdk
-3.4.2 Setup system
+3.5.5 Setup system
# Load DPDK modules
- $ sudo /sbin/modprobe uio
+ $ sudo modprobe uio
$ cd <dpdk-dir>
$ sudo insmod x86_64-native-linuxapp-gcc/kmod/igb_uio.ko
- Reserve and mount hugepages and bind supported interfaces to DPDK modules
+ Reserve and mount huge pages and bind supported interfaces to DPDK modules
following the DPDK documentation. ODP DPDK packet I/O has been tested with
- 512 x 2MB hugepages. All this can be done with the DPDK setup script
- (<dpdk-dir>/tools/dpdk-setup.sh).
+ 512 x 2MB huge pages. All this can be done with the DPDK setup script
+ (<dpdk-dir>/usertools/dpdk-setup.sh).
-3.4.3 Running ODP with DPDK pktio
+3.5.6 Running ODP with DPDK pktio
ODP applications will try use DPDK for packet I/O by default. If some other
I/O type is desired instead, DPDK I/O can be disabled by setting the
environment variable ODP_PKTIO_DISABLE_DPDK.
- DPDK interfaces are accessed using indices. For example, two first DPDK
+ DPDK interfaces are accessed using indexes. For example, two first DPDK
interfaces can be used with the odp_l2fwd example as follows:
$ cd <odp_dir>
$ sudo ./test/performance/odp_l2fwd -i 0,1 -c 2 -m 0
@@ -197,16 +241,112 @@ Prerequisites for building the OpenDataPlane (ODP) API
1024MB of memory:
$ sudo ODP_PKTIO_DPDK_PARAMS="-m 1024" ./test/performance/odp_l2fwd -i 0 -c 1
+3.6 AF_XDP socket based packet I/O support (optional)
+
+ Use AF_XDP socket for packet I/O. A kernel version of 5.10 or higher is
+ required, older kernels may or may not work.
+
+ More information about XDP and AF_XDP can be found here:
+ https://www.kernel.org/doc/Documentation/networking/af_xdp.rst
+
+ The status of the implementation is **experimental** and may cause issues
+ e.g. with some packet length, packet segment length and pool size
+ combinations that would otherwise conform to reported capabilities.
+
+ Note that, currently, AF_XDP socket packet I/O cannot be instantiated if
+ DPDK zero-copy is enabled. Additionally, RSS hash key and flow hash
+ configuration is done based on the NIC/driver default values and should be
+ manually reconfigured with e.g. ethtool if changes are required.
+
+3.6.1 AF_XDP socket packet I/O requirements
+
+ AF_XDP socket packet I/O implementation requires libxdp and libbpf libraries.
+ They can be fetched from XDP-project in GitHub:
+
+ $ git clone https://github.com/xdp-project/xdp-tools
+
+ (Contains submodules which should be cloned as well.)
+
+ Additional packages might be needed to be installed as well: llvm-dev and
+ gcc-multilib.
+
+ $ ./configure
+ $ make
+
+ After building, libraries should be installed.
+
+ $ cd <path to built libxdp>
+ $ make install
+ $ cd <path to built libbpf>
+ $ make install
+
+3.6.2 Build ODP with AF_XDP socket packet I/O support
+
+ After building and installing libxdp and libbpf, ODP can be configured to be
+ built with AF_XDP support (modify PKG_CONFIG_PATH as needed).
+
+ $ ./configure --enable-xdp
+
+3.6.3 Running ODP with AF_XDP socket packet I/O with Mellanox NICs
+
+ AF_XDP socket packet I/Os bind to NIC TRX-combined queues. Steering packets
+ to correct input sockets is configured via NIC RSS. With some NICs
+ (e.g. Mellanox), the driver queue configuration is adjusted by the NIC with
+ additional queues on top of the configured amount of TRX queues which are
+ then used as the actual queues. This will require additional forwarding
+ rules as RSS is not possible to be configured programmatically in this case.
+ Otherwise packets do not get forwarded to the correct queues.
+
+ For example:
+
+ ethtool -N <if name> flow-type ether dst <mac of if> action 5
+
+ Would forward Ethernet frames with a given destination address to queue 5,
+ which would be the first actual queue in case 5 regular combined queues
+ were configured (zero-indexing).
+
+3.7 Machine Learning API support (optional)
+ Use ML API for model inferencing. ML implementation uses ONNX Runtime library
+ (https://github.com/microsoft/onnxruntime). ODP has been tested with ONNX
+ Runtime version 1.16.3.
+
+3.7.1 Prebuilt onnxruntime download
+ Download a default CPU version onnxruntime-linux-x64-*.tgz and unzip it to
+ any folder.
+
+ $ wget -P ~ https://github.com/microsoft/onnxruntime/releases/download/v<version>/onnxruntime-linux-x64-<version>.tgz
+ $ mkdir <onnxruntime path>
+ $ cd <onnxruntime path>/
+ $ tar --strip=1 -zxvf ~/onnxruntime-linux-x64-<version>.tgz
+
+3.7.1 Build onnxruntime from source
+ $ git clone --recursive https://github.com/Microsoft/onnxruntime.git
+ $ cd onnxruntime
+
+ # Configure
+ $ ./build.sh --config RelWithDebInfo --build_shared_lib --parallel
+ $ tools/ci_build/github/linux/copy_strip_binary.sh -r build/Linux/ -a onnxruntime -l libonnxruntime.so.1.14.0 -c RelWithDebInfo -s . -t <commit id>
+ $ cp -r build/Linux/onnxruntime/ <onnxruntime path>
+
+3.7.2 Build ODP with ML support
+ After installing onnxruntime and example dependencies, ODP can be configured to be
+ built with ML support by giving onnxruntime path with --with-ort-path.
+
+ $ ../configure --with-ort-path=<onnxruntime path>
+
4.0 Packages needed to build API tests
- Cunit test framework version 2.1-3 is required
- Cunit prvodes a framework to run the API test suite that proves conformance to the
+ CUnit test framework version 2.1-3 is required
+ CUnit provides a framework to run the API test suite that proves conformance to the
ODP API. The home page http://cunit.sourceforge.net/doc/introduction.html
-4.1 Native Cunit install
+4.1 Native CUnit install
# Debian/Ubuntu
- $ apt-get install libcunit1-dev
+ $ sudo apt-get install libcunit1-dev
+
+ # CentOS/RedHat/Fedora systems
+ $ sudo yum install CUnit-devel
4.2 Built from src
@@ -222,58 +362,126 @@ Prerequisites for building the OpenDataPlane (ODP) API
make
sudo make install
+ # On RedHat you also have to add path /usr/local/lib to /etc/ld.so.conf
+
# ... OR ... Install CUnit into user defined location. The same path is
- # used in step 4.4 --with-cunit-path=/home/<my_cunit_path>
+ # used in step 4.4: PKG_CONFIG_PATH=/home/<my_cunit_path>/lib/pkgconfig
./configure --prefix=/home/<my_cunit_path>
make
make install
- # Also (in Ubuntu at least) run ldconfig to update shared lib cache or
- # reboot, before trying to run e.g. 'make distcheck'.
+ # Also (in Ubuntu/RedHat at least) run ldconfig to update shared lib
+ # cache or reboot, before trying to run e.g. 'make distcheck'.
sudo ldconfig
-4.3 Cross compile of Cunit
+4.3 Cross compile of CUnit
$ git svn clone http://svn.code.sf.net/p/cunit/code/trunk cunit-code
$ cd cunit-code
$ ./bootstrap
+
+ # Build and install 32 bit version of cunit
$ ./configure --host=arm-linux-gnueabihf --prefix=/home/${USER}/src/install-cunit
+ $ make
+ $ make install
-4.4 Using Cunit with ODP
- $ Add the configuration option to the regular configuration options
- # Use the default path ...
- ./configure --enable-cunit
+ # Build and install 64 bit version of cunit
+ $ ./configure --host=aarch64-linux-gnu --prefix=/home/${USER}/src/install-cunit
+ $ make
+ $ make install
- # ... OR the user defined path.
- ./configure --with-cunit-path=/home/<my_cunit_path>
+4.4 Using CUnit with ODP
-5.0 Documentation Images & Doxygen
+ Configure will automatically look for CUnit if validation testsuite is
+ enabled. By default it uses pkg-config to locate CUnit. Usually no
+ additional configuration will be required. Few corner cases:
- Images are stored as svg files. No conversions for these are needed.
+ # User directory installation
+ ./configure PKG_CONFIG_PATH=/home/<my_cunit_path>/lib/pkgconfig
- Message squence diagrams are stored as msc files and the svg versions generated when the docs are built
- mscgen is used
- #Debian/Ubuntu
- # apt-get install mscgen
+ # ... OR directly specifying flags
+ ./configure CUNIT_CFLAGS="-I/home/<my_cunit_path>/include" CUNIT_LIBS="/home/<my_cunit_path>/lib -lcunit"
-5.1 API Guide
-See http://www.stack.nl/~dimitri/doxygen/manual/install.html
+5.0 Cross Compilation for arm64
-The tested version of doxygen is 1.8.8
+ To cross compile binaries for arm64 on an x86_64 Debian based system, install
+ the following packages: crossbuild-essential-arm64, libconfig-dev:arm64
+ (optionally libssl-dev:arm64).
+
+5.1 Pre-installation setup (optional)
-5.1.1 HTML
# Debian/Ubuntu
- $ apt-get install doxygen graphviz
+ $ sudo dpkg --add-architecture arm64
+
+ Modify /etc/apt/sources.list to add remote repositories for fetching arm64
+ software packages for your Ubuntu version. Once this is complete, run:
+ $ sudo apt-get update
-5.2 User guides
+5.2 Install packages
-5.2.1 HTML
# Debian/Ubuntu
- $ apt-get install asciidoctor source-highlight librsvg2-bin
+ $ sudo apt-get install crossbuild-essential-arm64
+ $ sudo apt-get install libconfig-dev:arm64
+
+ Installing OpenSSL is optional. Refer to section 3 for more details.
+ $ sudo apt-get install libssl-dev:arm64
+
+5.3 Building ODP
+
+ $ ./bootstrap
+ $ ./configure --host=aarch64-linux-gnu
+ $ make
-6.0 Submitting patches
+ To build ODP with cross-compiled cunit for arm64, refer to sections 4.3
+ and 4.4.
+
+6.0 Documentation Images & Doxygen
+
+ Images are stored as svg files. No conversions for these are needed.
+
+ Message sequence diagrams are stored as msc files and the svg versions are generated
+ when the docs are built.
+ # Debian/Ubuntu
+ $ sudo apt-get install mscgen
+
+ # CentOS 8/RedHat/Fedora
+ $ sudo yum install mscgen
+
+6.1 API Guide
+ See https://www.doxygen.nl/manual/install.html
+
+ The tested version of doxygen is 1.8.8
+
+6.1.1 HTML
+ # Debian/Ubuntu
+ $ sudo apt-get install doxygen graphviz
+
+ # CentOS/RedHat/Fedora
+ $ sudo yum install doxygen graphviz
+
+6.2 User guides
+
+6.2.1 HTML
+ # Debian/Ubuntu
+ $ sudo apt-get install asciidoctor source-highlight librsvg2-bin
+
+ # CentOS/RedHat/Fedora
+ $ sudo yum install asciidoc source-highlight librsvg2
+
+7.0 Submitting patches
When submitting patches they should be checked with ./scripts/checkpatch.pl
To have this tool also check spelling you need codespell.
# Debian/Ubuntu
- #sudo apt install codespell
+ $ sudo apt-get install codespell
+
+8.0 Command Line Interface (optional)
+
+ ODP applications (see e.g. ./example/cli) may use CLI helper (./helper/include/odp/helper/cli.h)
+ to provide a command line interface to the user. CLI helper depends on libcli library.
+
+ # Debian/Ubuntu
+ $ sudo apt-get install libcli-dev
+
+ # CentOS/RedHat/Fedora
+ $ sudo yum install libcli-devel
diff --git a/LICENSE b/LICENSE
index 441ba51f2..409fd1135 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,20 @@
-Copyright (c) 2013-2014, Linaro Limited
-All rights reserved.
-
-SPDX-License-Identifier: BSD-3-Clause
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2013-2019 Linaro Limited
+Copyright (c) 2019-2023 OpenFastPath Foundation
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
-Redistributions of source code must retain the above copyright notice, this
+1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
-Neither the name of Linaro Limited nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
diff --git a/Makefile.am b/Makefile.am
index 76ceb851e..8b65244c1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,22 +1,46 @@
ACLOCAL_AMFLAGS=-I m4
-AUTOMAKE_OPTIONS = foreign
-AM_DISTCHECK_CONFIGURE_FLAGS = --enable-test-cpp \
- --enable-test-example \
- --enable-test-helper \
- --enable-test-perf \
- --enable-test-perf-proc \
- --enable-test-vald \
- --enable-user-guides \
- --with-testdir
-
-#@with_platform@ works alone in subdir but not as part of a path???
-SUBDIRS = @platform_with_platform@ \
+AM_DISTCHECK_CONFIGURE_FLAGS = --enable-user-guides \
+ --enable-helper-linux
+
+if PLATFORM_IS_LINUX_GENERIC
+PLATFORM_DIR = platform/linux-generic
+PLATFORM_DUMPCONF_DIR = platform/linux-generic/dumpconfig
+PLATFORM_TEST_DIR = platform/linux-generic/test
+PLATFORM_EXAMPLE_DIR = platform/linux-generic/example
+endif
+
+SUBDIRS = \
+ include \
+ $(PLATFORM_DIR) \
+ $(PLATFORM_DUMPCONF_DIR) \
helper \
- helper/test \
- doc \
- example . \
- test
+ doc
+
+# Tests are run in this SUBDIRS order. The intention is to run validation tests first,
+# then example/performance/platform specific tests.
+if WITH_TESTS
+SUBDIRS += test/common
+SUBDIRS += test/miscellaneous
+SUBDIRS += test/validation
+endif
+
+if WITH_EXAMPLES
+SUBDIRS += example
+SUBDIRS += $(PLATFORM_EXAMPLE_DIR)
+endif
+
+if WITH_TESTS
+SUBDIRS += test/performance
+SUBDIRS += helper/test
+SUBDIRS += $(PLATFORM_TEST_DIR)
+endif
@DX_RULES@
-EXTRA_DIST = bootstrap $(DX_CONFIG) CHANGELOG config/README
+EXTRA_DIST = bootstrap CHANGELOG config/README
+
+distcheck-hook:
+ if test -n "$(DX_CLEANFILES)" ; \
+ then \
+ $(MAKE) doxygen-doc ; \
+ fi
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 000000000..26588e812
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,25 @@
+AM_CFLAGS = $(ODP_CFLAGS)
+AM_CXXFLAGS = $(ODP_CXXFLAGS)
+AM_LDFLAGS = $(ODP_LDFLAGS)
+
+ODP_INCLUDES = \
+ -I$(top_builddir)/include \
+ -I$(top_srcdir)/include
+
+if ODP_ABI_COMPAT
+ODP_INCLUDES += \
+ -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@
+else
+ODP_INCLUDES += \
+ -I$(top_srcdir)/platform/@with_platform@/include \
+ -I$(top_srcdir)/platform/@with_platform@/arch/@ARCH_DIR@ \
+ -I$(top_srcdir)/platform/@with_platform@/arch/default \
+ -I$(top_srcdir)/platform/@with_platform@/arch/common \
+ -I$(top_srcdir)/platform/@with_platform@/include-abi
+endif
+
+HELPER_INCLUDES = \
+ -I$(top_builddir)/helper/include \
+ -I$(top_srcdir)/helper/include
+
+LIB = $(top_builddir)/lib
diff --git a/README b/README
index 4350b953c..4b172f560 100644
--- a/README
+++ b/README
@@ -1,48 +1,55 @@
-Copyright (c) 2013-2014, Linaro Limited
-All rights reserved.
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2013-2019 Linaro Limited
+Copyright (c) 2019-2023 OpenFastPath Foundation
-SPDX-License-Identifier: BSD-3-Clause
+OpenDataPlane (ODP) project web page:
+ https://www.opendataplane.org
-OpenDataPlane (ODP) project source code.
- http://www.opendataplane.org/
+ODP project in GitHub:
+ https://github.com/OpenDataPlane
Main git repository:
- git://git.linaro.org/lng/odp.git
+ https://github.com/OpenDataPlane/odp.git
- How to build:
- Look in platform/linux-*/README for more detail how to build.
- Directory test contains test applications for ODP API calls and features support.
+How to build:
+ See DEPENDENCIES file about system requirements and dependencies to external
+ libraries/packages. It contains also some more detailed build instructions.
- For a list of build dependencies, read DEPENDENCIES.
- In general you can build:
- ./bootstrap
- ./configure
- Use 'make' to build ODP library and samples and 'make doxygen-doc' to build
- ODP API documentation. 'make install' will copy all required headers and
- binary files to the install directory.
+ Generally, ODP and test applications are built with these three steps:
+ ./bootstrap
+ ./configure
+ make
- Configure options:
+ See help on configure options:
./configure --help
- To execute all the testcases, assuming CUnit is installed for the validation tests:
- ./bootstrap
- ./configure --enable-test-perf --enable-test-vald
+ ODP header files and libraries can be installed with:
+ make install
+
+ ODP API documentation can be built with (requires Doxygen):
+ make doxygen-doc
+
+ Validation tests (requires CUnit) and other test applications can be
+ executed with:
make check
- To generate the users guides:
+ Users guides can be generated with:
./bootstrap
- ./configure --enable-user-guide
+ ./configure --enable-user-guides
make
-Patches tracking system:
- http://patches.opendataplane.org/project/lng-odp/list/
+Licensing:
+ The default license for ODP project is BSD-3-Clause (see LICENSE file).
+ SPDX short-form identifiers (https://spdx.dev/ids/) are used in project
+ source files to identify the used license. The SPDX license list can be
+ found from https://spdx.org/licenses/.
Mailing list:
- lng-odp@lists.linaro.org
+ odp@lists.opendataplane.org
- Please read CONTRIBUTING file before submitting patches.
- Email prefixes:
- [PATCH] means patch is for odp.git
+Contributing:
+ Please read CONTRIBUTING file before submitting patches. ODP project follows
+ Linux kernel coding style.
Bug tracking:
- https://bugs.linaro.org/describecomponents.cgi?product=OpenDataPlane%20-%20linux-%20generic%20reference
+ https://github.com/OpenDataPlane/odp/issues
diff --git a/bootstrap b/bootstrap
index 6fd91c816..095d83c82 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,7 +1,7 @@
#! /bin/sh
set -x
aclocal -I config -I m4
-libtoolize --copy
+libtoolize --copy --force
autoheader
automake --add-missing --copy --warnings=all
autoconf --force
diff --git a/config/README b/config/README
index 3f4336103..9d6b87b41 100644
--- a/config/README
+++ b/config/README
@@ -1,2 +1,12 @@
ODP configuration options
-------------------------
+
+Runtime configuration options can be passed to an ODP instance by
+setting ODP_CONFIG_FILE environment variable to point to a libconfig
+format configuration file. A template configuration file with default
+values is provided in config/odp-linux-generic.conf. The values set in
+config/odp-linux-generic.conf are hardcoded during configure/build
+phase. If ODP_CONFIG_FILE is not set at runtime, hardcoded default
+values are used instead of any configuration file. A configuration file
+passed using ODP_CONFIG_FILE doesn't have to include all available
+options. The missing options are replaced with hardcoded default values.
diff --git a/config/odp-linux-generic.conf b/config/odp-linux-generic.conf
new file mode 100644
index 000000000..93997ecb3
--- /dev/null
+++ b/config/odp-linux-generic.conf
@@ -0,0 +1,398 @@
+# ODP runtime configuration options
+#
+# This template configuration file (odp-linux-generic.conf) is hardcoded
+# during configure/build phase and the values defined here are used if
+# optional ODP_CONFIG_FILE is not set. This configuration file MUST
+# include all configuration options.
+#
+# ODP_CONFIG_FILE can be used to override default values and it doesn't
+# have to include all available options. The missing options are
+# replaced with hardcoded default values.
+#
+# The options defined here are implementation specific and valid option
+# values should be checked from the implementation code.
+#
+# See libconfig syntax: https://hyperrealm.github.io/libconfig/libconfig_manual.html#Configuration-Files
+
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+# System options
+system: {
+ # CPU frequency value returned by odp_cpu_hz() and odp_cpu_hz_id()
+ # calls on platforms where frequency isn't available using standard
+ # Linux methods.
+ cpu_mhz = 0
+
+ # CPU max frequency value returned by odp_cpu_hz_max() and
+ # odp_cpu_hz_max_id() calls on platforms where max frequency isn't
+ # available using standard Linux methods.
+ cpu_mhz_max = 1400
+
+ # When enabled (1), implementation reads the CPU frequency values from
+ # OS only once during ODP initialization. Enabling this option removes
+ # system calls from odp_cpu_hz() and odp_cpu_hz_id() implementations.
+ #
+ # NOTE: This option should only be used on systems where CPU frequency
+ # scaling is disabled.
+ cpu_hz_static = 0
+
+ # Maximum number of ODP threads that can be created.
+ # odp_thread_count_max() returns this value or the build time
+ # maximum ODP_THREAD_COUNT_MAX, whichever is lower. This setting
+ # can be used to reduce thread related resource usage.
+ thread_count_max = 256
+}
+
+# Shared memory options
+shm: {
+ # Number of cached default size huge pages. These pages are allocated
+ # during odp_init_global() and freed back to the kernel in
+ # odp_term_global(). A value of zero means no pages are cached.
+ # No negative values should be used here, they are reserved for future
+ # implementations.
+ #
+ # ODP will reserve as many huge pages as possible, which may be less
+ # than requested here if the system does not have enough huge pages
+ # available.
+ #
+ # When using process mode threads, this value should be set to 0
+ # because the current implementation won't work properly otherwise.
+ num_cached_hp = 0
+
+ # Huge page usage limit in kilobytes. Memory reservations larger than
+ # this value are done using huge pages (if available). Smaller
+ # reservations are done using normal pages to conserve memory.
+ huge_page_limit_kb = 64
+
+ # Amount of memory pre-reserved for ODP_SHM_SINGLE_VA usage in kilobytes
+ single_va_size_kb = 262144
+}
+
+# Pool options
+pool: {
+ # Default thread local cache size. Cache size in pool parameters is
+ # initialized to this value. Value must be a multiple of burst_size
+ # (min 2 x burst_size).
+ #
+ # The total maximum number of cached events is the number of threads
+ # using the pool multiplied with local_cache_size.
+ local_cache_size = 256
+
+ # Transfer size between local cache and global pool. Must be larger
+ # than zero.
+ burst_size = 32
+
+ # Packet pool options
+ pkt: {
+ # Maximum packet data length in bytes
+ max_len = 65536
+
+ # Maximum number of packets per pool. Power of two minus one
+ # results optimal memory usage (e.g. (256 * 1024) - 1).
+ max_num = 262143
+
+ # Base alignment for segment data. When set to zero,
+ # cache line size is used. Use power of two values. This is
+ # also the maximum value for the packet pool alignment param.
+ base_align = 0
+ }
+
+ buf: {
+ # Minimum data alignment. The alignment request in pool
+ # parameters is rounded up to this value. When set to zero,
+ # cache line size is used. Use power of two values.
+ min_align = 0
+ }
+}
+
+# General pktio options
+pktio: {
+ # Frame start offset from packet base pointer at packet input. This can
+ # be used (together with pool.pkt.base_align option) to tune packet data
+ # alignment for received frames. Currently, packet IO drivers
+ # (zero-copy DPDK, loop and ipc) that do not copy data ignore this
+ # option.
+ pktin_frame_offset = 0
+
+ # Pool size allocated for potential completion events for transmitted and
+ # dropped packets. Separate pool for different packet IO instances.
+ tx_compl_pool_size = 1024
+}
+
+# DPDK pktio options
+pktio_dpdk: {
+ # Default options
+ num_rx_desc = 128
+ num_tx_desc = 512
+ rx_drop_en = 0
+
+ # Store RX RSS hash result as ODP flow hash
+ set_flow_hash = 0
+
+ # Enable reception of Ethernet frames sent to any multicast group
+ multicast_en = 1
+
+ # Driver specific options (use PMD names from DPDK)
+ net_ixgbe: {
+ rx_drop_en = 1
+ }
+}
+
+# XDP pktio options
+pktio_xdp: {
+ # Number of RX and TX descriptors to be reserved for AF_XDP socket
+ # memory. Adjusting these may improve performance depending on NIC ring
+ # configuration. In zero-copy mode, packet pools used as pktio pools
+ # need to be large enough to accommodate RX and TX descriptors of every
+ # pktio queue. Values must be a power of two.
+ num_rx_desc = 1024
+ num_tx_desc = 1024
+}
+
+queue_basic: {
+ # Maximum queue size. Value must be a power of two.
+ max_queue_size = 8192
+
+ # Default queue size. Value must be a power of two.
+ default_queue_size = 4096
+}
+
+sched_basic: {
+ # Priority level spread
+ #
+ # Each priority level is spread into multiple scheduler internal queues.
+ # This value defines the number of those queues. Minimum value is 1.
+ # Each thread prefers one of the queues over other queues. A higher
+ # spread value typically improves parallelism and thus is better for
+ # high thread counts, but causes uneven service level for low thread
+ # counts. Typically, optimal value is the number of threads using
+ # the scheduler.
+ prio_spread = 4
+
+ # Weight of the preferred scheduler internal queue
+ #
+ # Each thread prefers one of the internal queues over other queues.
+ # This value controls how many times the preferred queue is polled
+ # between a poll to another internal queue. Minimum value is 1. A higher
+ # value typically improves parallelism as threads work mostly on their
+ # preferred queues, but causes uneven service level for low thread
+ # counts as non-preferred queues are served less often
+ prio_spread_weight = 63
+
+ # Dynamic load balance of scheduler internal queues
+ #
+ # When enabled (1), scheduler checks periodically internal queue load levels and
+ # moves event queues from one spread to another in order to even out the loads.
+ # Load level of an internal queue (group/prio/spread) is measures as number of
+ # event queues allocated to it, divided by number of threads serving it.
+ load_balance = 1
+
+ # Burst size configuration per priority. The first array element
+ # represents the highest queue priority. The scheduler tries to get
+ # burst_size_default[prio] events from a queue and stashes those that
+ # cannot be passed to the application immediately. More events than the
+ # default burst size may be returned from application request, but no
+ # more than burst_size_max[prio].
+ #
+ # Large burst sizes improve throughput, but decrease application
+ # responsiveness to higher priority events due to head of line blocking
+ # caused by a burst of lower priority events.
+ burst_size_default = [ 32, 32, 32, 32, 32, 16, 8, 4]
+ burst_size_max = [255, 255, 255, 255, 255, 16, 16, 8]
+
+ # Burst size configuration per priority for each scheduled queue type.
+ # Overrides default values set in 'burst_size_default' and
+ # 'burst_size_max' if != 0.
+ burst_size_parallel = [0, 0, 0, 0, 0, 0, 0, 0]
+ burst_size_max_parallel = [0, 0, 0, 0, 0, 0, 0, 0]
+ burst_size_atomic = [0, 0, 0, 0, 0, 0, 0, 0]
+ burst_size_max_atomic = [0, 0, 0, 0, 0, 0, 0, 0]
+ burst_size_ordered = [0, 0, 0, 0, 0, 0, 0, 0]
+ burst_size_max_ordered = [0, 0, 0, 0, 0, 0, 0, 0]
+
+ # Automatically updated schedule groups
+ #
+ # DEPRECATED: use odp_schedule_config() API instead
+ #
+ # API specification defines that ODP_SCHED_GROUP_ALL,
+ # _WORKER and _CONTROL are updated automatically. These options can be
+ # used to disable these group when not used. Set value to 0 to disable
+ # a group. Performance may improve when unused groups are disabled.
+ group_enable: {
+ all = 1
+ worker = 1
+ control = 1
+ }
+
+ # Ordered queue reorder stash size
+ #
+ # Number of events each thread can stash internally before having to
+ # wait for the right order context. Reorder stash can improve
+ # performance if threads process events in bursts. If 'order_stash_size'
+ # > 0, events may be dropped by the implementation if the target queue
+ # is full. To prevent this set 'order_stash_size' to 0.
+ order_stash_size = 512
+
+ # Power saving options for schedule with wait
+ #
+ # When waiting for events during a schedule call, save power by
+ # sleeping in the poll loop. First, run schedule loop normally for
+ # poll_time_nsec nanoseconds. If there are no events to schedule in that
+ # time, continue polling, but sleep on each round. Sleep time is
+ # sleep_time_nsec nanoseconds, or the time to the next timer expiration,
+ # whichever is smaller. Timer pools are scanned just before sleep.
+ #
+ # During sleep, the thread is not polling for packet input or timers.
+ # Each thread measures time and sleeps independently of other threads.
+ #
+ # When using this feature, it may be necessary to decrease
+ # /proc/<pid>/timerslack_ns, or use a real-time priority. Sleeping may
+ # have an adverse effect on performance for a short time after sleep.
+ powersave: {
+ # Time in nsec to poll before sleeping
+ #
+ # <1: Disabled. Never sleep. sleep_time_nsec is ignored.
+ poll_time_nsec = 0
+
+ # Time in nsec to sleep
+ #
+ # Must be less than one second. Actual sleep time may vary.
+ sleep_time_nsec = 0
+ }
+}
+
+stash: {
+ # Maximum number of stashes
+ max_num = 512
+
+ # Maximum number of objects in a stash
+ #
+ # The value may be rounded up by the implementation. For optimal memory
+ # usage set value to a power of two - 1.
+ max_num_obj = 4095
+}
+
+timer: {
+ # Use inline timer implementation
+ #
+ # By default, timer processing is done in background threads (thread per
+ # timer pool). With inline implementation timers are processed by ODP
+ # application threads instead. When using inline timers the application
+ # has to call odp_schedule() or odp_queue_deq() regularly to actuate
+ # timer processing.
+ #
+ # 0: Use POSIX timer and background threads to process timers
+ # 1: Use inline timer implementation and application threads to process
+ # timers
+ inline = 0
+
+ # Inline timer poll interval
+ #
+ # When set to 1 inline timers are polled during every schedule round.
+ # Increasing the value reduces timer processing overhead while
+ # decreasing accuracy. Ignored when inline timer is not used.
+ inline_poll_interval = 10
+
+ # Inline timer poll interval in nanoseconds
+ #
+ # When inline_poll_interval is larger than 1, use this option to limit
+ # inline timer polling rate in nanoseconds. By default, this defines the
+ # maximum rate a thread may poll timers. If a timer pool is created with
+ # a higher resolution than this, the polling rate is increased
+ # accordingly. Ignored when inline timer is not used.
+ inline_poll_interval_nsec = 500000
+
+ # Inline timer use of threads
+ #
+ # Select which thread types process non-private timer pools in inline
+ # timer implementation. Thread type does not affect private timer
+ # pool procesessing, those are always processed by the thread which
+ # created the pool. Ignored when inline timer is not used.
+ #
+ # 0: Both control and worker threads process non-private timer pools
+ # 1: Only worker threads process non-private timer pools
+ # 2: Only control threads process non-private timer pools
+ inline_thread_type = 0
+}
+
+ipsec: {
+ # Packet ordering method for asynchronous IPsec processing
+ #
+ # Asynchronous IPsec processing maintains original packet order when
+ # started within ordered or atomic scheduling context. In addition
+ # to that, ODP API specifies that the order of IPsec processing
+ # (i.e. anti-replay window update and sequence number generation)
+ # is the same as the original packet order.
+ #
+ # The following settings control how the order is maintained in
+ # asynchronous IPsec operations. They have no effect on synchronous
+ # operations where the ODP application is responsible of the ordering.
+ #
+ # Values:
+ #
+ # 0: Ordering is not attempted.
+ #
+ # This has the lowest overhead and the greatest parallelism but
+ # is not fully compliant with the API specification.
+ #
+ # Lack of ordering means that outbound IPsec packets, although
+ # remaining in the correct order, may have their sequence numbers
+ # assigned out of order. This can cause unexpected packet loss if
+ # the anti-replay window of the receiving end is not large enough
+ # to cover the possible misordering.
+ #
+ # Similarly, since anti-replay check is not done in the reception
+ # order, the anti-replay check sees additional packet misordering
+ # on top of the true misordering of the received packets. This
+ # means that a larger anti-replay window may be required to avoid
+ # packet loss.
+ #
+ # 1: Ordering by waiting
+ #
+ # Correct processing order is maintained by a simple mechanism
+ # that makes a thread wait until its scheduling context has
+ # reached the head of its input queue.
+ #
+ # This limits parallelism when single input queue is used, even
+ # when packets get distributed to multiple SAs.
+ ordering: {
+ # Odering method for asynchronous inbound operations.
+ async_inbound = 0
+
+ # Odering method for asynchronous outbound operations.
+ async_outbound = 0
+ }
+}
+
+ml: {
+ # Enable onnxruntime profiling, when enabled, a json file will be
+ # generated after inference. chrome://tracing/ can be used to check
+ # the profiling. Use 0 to disable and 1 to enable profiling.
+ enable_profiling = 0
+
+ # Choose onnxruntime execution mode, which can be "SEQUENTIAL" or
+ # "PARALLEL"
+ execution_mode = "SEQUENTIAL"
+
+ # Set the number of threads used to parallelize the execution of the
+ # graph across nodes. A value of 0 means onnxruntime will pick a default.
+ inter_op_num_threads = 0
+
+ # Set the number of threads used to parallelize the execution within
+ # a node. A value of 0 means onnxruntime will pick a default.
+ intra_op_num_threads = 0
+
+ # Set graph optimization level. Valid values are:
+ # DISABLE_ALL: disables all optimizations
+ # ENABLE_BASIC: enables basic optimizations
+ # ENABLE_EXTENDED: enables basic and extended optimizations
+ # ENABLE_ALL: enables all available optimizations including layout optimization
+ graph_optimization_level = "ENABLE_ALL"
+
+ # Serialize the optimized model to disk. When initializing a session
+ # with the same model, no need to apply optimization anymore, thus
+ # reducing model startup time.
+ optimized_model_filepath = ""
+}
diff --git a/configure.ac b/configure.ac
index 51dd9352e..f0b2c9952 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,27 +1,56 @@
AC_PREREQ([2.5])
##########################################################################
-# Set correct API version
+# ODP API version
##########################################################################
-m4_define([odpapi_generation_version], [1])
-m4_define([odpapi_major_version], [15])
-m4_define([odpapi_minor_version], [0])
-m4_define([odpapi_point_version], [0])
-m4_define([odpapi_version],
- [odpapi_generation_version.odpapi_major_version.odpapi_minor_version.odpapi_point_version])
-AC_INIT([OpenDataPlane],[odpapi_version],[lng-odp@lists.linaro.org])
+m4_define([odp_version_generation], [1])
+m4_define([odp_version_major], [44])
+m4_define([odp_version_minor], [0])
+m4_define([odp_version_patch], [0])
-ODP_VERSION_API_GENERATION=odpapi_generation_version
+m4_define([odp_version_api],
+ [odp_version_generation.odp_version_major.odp_version_minor])
+m4_define([odp_version_library],
+ [odp_version_generation.odp_version_major.odp_version_minor.odp_version_patch])
+
+AC_INIT([OpenDataPlane],[odp_version_library],[odp@lists.opendataplane.org])
+
+ODP_VERSION_API_GENERATION=odp_version_generation
AC_SUBST(ODP_VERSION_API_GENERATION)
-ODP_VERSION_API_MAJOR=odpapi_major_version
+ODP_VERSION_API_MAJOR=odp_version_major
AC_SUBST(ODP_VERSION_API_MAJOR)
-ODP_VERSION_API_MINOR=odpapi_minor_version
+ODP_VERSION_API_MINOR=odp_version_minor
AC_SUBST(ODP_VERSION_API_MINOR)
-AC_CONFIG_FILES([include/odp/api/spec/version.h
- include/odp/api/spec/deprecated.h])
-
-AM_INIT_AUTOMAKE([1.9 tar-pax subdir-objects])
-AC_CONFIG_SRCDIR([helper/config.h.in])
-AM_CONFIG_HEADER([helper/config.h])
+ODP_VERSION_API=odp_version_api
+AC_SUBST(ODP_VERSION_API)
+
+##########################################################################
+# Helper library version
+##########################################################################
+m4_define([odph_version_generation], [1])
+m4_define([odph_version_major], [5])
+m4_define([odph_version_minor], [0])
+
+m4_define([odph_version],
+ [odph_version_generation.odph_version_major.odph_version_minor])
+
+ODPH_VERSION_GENERATION=odph_version_generation
+AC_SUBST(ODPH_VERSION_GENERATION)
+ODPH_VERSION_MAJOR=odph_version_major
+AC_SUBST(ODPH_VERSION_MAJOR)
+ODPH_VERSION_MINOR=odph_version_minor
+AC_SUBST(ODPH_VERSION_MINOR)
+ODPH_VERSION=odph_version
+AC_SUBST(ODPH_VERSION)
+
+# Initialize automake
+AM_INIT_AUTOMAKE([1.9 tar-pax subdir-objects foreign nostdinc -Wall -Werror])
+AC_CONFIG_SRCDIR([include/odp/api/spec/init.h])
+AC_CONFIG_HEADERS(m4_normalize([
+ include/odp/autoheader_build.h
+ include/odp/autoheader_external.h
+ include/odp/autoheader_internal.h
+ helper/include/odp/helper/autoheader_external.h
+]))
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
@@ -31,18 +60,17 @@ AM_SILENT_RULES([yes])
##########################################################################
# Set platform library version
#
-# Follow version rules described here:
-# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-# Version is Current:Revision:Age
-# 1. if there are only source changes, use C:R+1:A
-# 2. if interfaces were added use C+1:0:A+1
-# 3. if interfaces were removed, then use C+1:0:0
+# ODP does not promise backwards compatibility between releases, so we
+# just enforce major:minor:release as version number.
##########################################################################
-ODP_LIBSO_VERSION=115:0:2
+m4_define([odpso_version],
+ [odp_version_generation[]odp_version_major:odp_version_minor:odp_version_patch])
+
+ODP_LIBSO_VERSION=odpso_version
AC_SUBST(ODP_LIBSO_VERSION)
-ODPHELPER_LIBSO_VERSION=112:1:0
+ODPHELPER_LIBSO_VERSION=odpso_version
AC_SUBST(ODPHELPER_LIBSO_VERSION)
# Checks for programs.
@@ -62,83 +90,117 @@ LT_INIT([])
AC_SUBST([LIBTOOL_DEPS])
AM_PROG_LIBTOOL
-# Checks for library functions.
-AC_FUNC_MALLOC
-AC_FUNC_MMAP
-AC_CHECK_FUNCS([bzero clock_gettime gethostbyname getpagesize gettimeofday memset munmap socket strchr strerror strrchr strstr strtoull])
+PKG_PROG_PKG_CONFIG
-# Checks for header files.
-AC_HEADER_RESOLV
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h])
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_HEADER_STDBOOL
-AC_C_INLINE
-AC_TYPE_SIZE_T
-AC_TYPE_SSIZE_T
-AC_TYPE_UINT8_T
-AC_TYPE_UINT16_T
-AC_TYPE_INT32_T
-AC_TYPE_UINT32_T
-AC_TYPE_UINT64_T
+##########################################################################
+# Default warning setup
+##########################################################################
+ODP_CFLAGS="$ODP_CFLAGS -W -Wall -Werror"
+ODP_CXXFLAGS="$ODP_CXXFLAGS -W -Wall -Werror"
+
+# Additional warnings:
+ODP_CHECK_CFLAG([-Wstrict-prototypes])
+ODP_CHECK_CFLAG([-Wmissing-prototypes])
+ODP_CHECK_CFLAG([-Wmissing-declarations])
+ODP_CHECK_CFLAG([-Wold-style-definition])
+ODP_CHECK_CFLAG([-Wpointer-arith])
+ODP_CHECK_CFLAG([-Wcast-align])
+ODP_CHECK_CFLAG([-Wnested-externs])
+ODP_CHECK_CFLAG([-Wcast-qual])
+ODP_CHECK_CFLAG([-Wformat-nonliteral])
+ODP_CHECK_CFLAG([-Wformat-security])
+ODP_CHECK_CFLAG([-Wundef])
+ODP_CHECK_CFLAG([-Wwrite-strings])
+ODP_CHECK_CFLAG([-Wformat-truncation=0])
+ODP_CHECK_CFLAG([-Wformat-overflow=0])
+ODP_CHECK_CFLAG([-Wshadow=local])
+
+# GCC 10 sometimes gets confused about object sizes and gives bogus warnings.
+# Make the affected warnings generate only warnings, not errors.
+AS_IF([test "$GCC" == yes],
+ AS_IF([test `$CC -dumpversion | cut -d '.' -f 1` -ge 10],
+ ODP_CHECK_CFLAG([-Wno-error=array-bounds])
+ ODP_CHECK_CFLAG([-Wno-error=stringop-overflow])
+ ODP_CHECK_CXXFLAG([-Wno-error=stringop-overflow])
+ )
+)
-#########################################################################
-# Get GCC version
-#########################################################################
+ODP_CFLAGS="$ODP_CFLAGS -std=c11"
+ODP_CXXFLAGS="$ODP_CXXFLAGS -std=c++11"
-AX_COMPILER_VERSION
-CC_VERSION=$ax_cv_c_compiler_version
+# Extra flags for example to suppress certain warning types
+ODP_CFLAGS="$ODP_CFLAGS $ODP_CFLAGS_EXTRA"
-CC_VERSION_MAJOR=$(echo $CC_VERSION | cut -d'.' -f1)
-CC_VERSION_MINOR=$(echo $CC_VERSION | cut -d'.' -f2)
-CC_VERSION_PATCH=$(echo $CC_VERSION | cut -d'.' -f3)
+##########################################################################
+# Check if compiler supports cmpxchng16 on x86-based architectures
+##########################################################################
+ODP_CHECK_CFLAG([-mcx16])
##########################################################################
# Allow valgrind suite to run against the defined tests
##########################################################################
+AX_VALGRIND_DFLT([sgcheck], [off])
+AX_VALGRIND_DFLT([helgrind], [off])
+AX_VALGRIND_DFLT([drd], [off])
AX_VALGRIND_CHECK
##########################################################################
+# Architecture-specific dir
+##########################################################################
+AC_ARG_WITH([archincludedir],
+ [AS_HELP_STRING([--with-archincludedir=DIR],
+ [directory to hold arch-specific headers [default=INCLUDEDIR]])],
+ [archincludedir=$with_archincludedir],
+ [archincludedir="${includedir}"])
+AC_SUBST([archincludedir])
+
+##########################################################################
# Which architecture optimizations will we use
##########################################################################
AS_CASE([$host],
[x86*], [ARCH_DIR=x86],
[i686*], [ARCH_DIR=x86],
- [mips64*], [ARCH_DIR=mips64],
[powerpc*], [ARCH_DIR=powerpc],
- [aarch64*], [ARCH_DIR=arm],
+ [aarch64*], [ARCH_DIR=aarch64],
[arm*], [ARCH_DIR=arm],
- [ARCH_DIR=undefined]
+ [ARCH_DIR=default]
)
AC_SUBST([ARCH_DIR])
##########################################################################
-# Warn on the defaults if arch is undefined
-##########################################################################
-if test "${ARCH_DIR}" = "undefined";
-then
- echo "ARCH_DIR is undefined, please add your ARCH_DIR based on host=${host}"
- exit 1
-fi
-
-##########################################################################
# Architecture for ABI support
##########################################################################
AS_CASE([$host],
[x86*], [ARCH_ABI=x86_64-linux],
[i686*], [ARCH_ABI=x86_32-linux],
- [mips64*], [ARCH_ABI=mips64-linux],
[powerpc*], [ARCH_ABI=power64-linux],
[aarch64*], [ARCH_ABI=arm64-linux],
[arm*], [ARCH_ABI=arm32-linux],
- [ARCH_ABI=undefined]
+ [ARCH_ABI=default-linux]
)
AC_SUBST([ARCH_ABI])
-if test "${ARCH_ABI}" = "undefined";
+AC_ARG_ENABLE([host-optimization],
+ [AS_HELP_STRING([--disable-host-optimization],
+ [disables using host-specific ARCH and ABI files]
+ [[default=enabled]])],
+ [], [enable_host_optimization=yes])
+if test "x$enable_host_optimization" = "xno" ; then
+ ARCH_DIR=default
+ ARCH_ABI=default-linux
+fi
+
+##########################################################################
+# Warn on the defaults if arch is undefined
+##########################################################################
+if test "${ARCH_DIR}" = "default";
+then
+ AC_MSG_WARN([ARCH_DIR is undefined, please add your ARCH_DIR based on host=${host}])
+fi
+
+if test "${ARCH_ABI}" = "default-linux";
then
- echo "ARCH_ABI is undefined, please add your ARCH_ABI based on host=${host}"
- exit 1
+ AC_MSG_WARN([ARCH_ABI is undefined, please add your ARCH_ABI based on host=${host}])
fi
##########################################################################
@@ -148,34 +210,77 @@ PKGCONFIG_VERSION=$(echo $VERSION | awk -F '.git' '{print $1}')
AC_SUBST(PKGCONFIG_VERSION)
##########################################################################
-# Determine which platform to build for
+# Enable/disable ABI compatible build
##########################################################################
-AC_ARG_WITH([platform],
- [AS_HELP_STRING([--with-platform=platform],
- [select platform to be used, default linux-generic])],
- [],
- [with_platform=linux-generic
- ])
+ODP_ABI_COMPAT=0
+abi_compat=no
+AC_ARG_ENABLE([abi-compat],
+ [AS_HELP_STRING([--enable-abi-compat],
+ [enables ABI compatible mode, disables inline code in header files]
+ [[default=disabled]])],
+ [if test "x$enableval" = "xyes"; then
+ ODP_ABI_COMPAT=1
+ abi_compat=yes
+ fi])
+if test "x$abi_compat" = "xno" ; then
+ # If there is no ABI compatibility the .so numbers are meaningless
+ ODP_LIBSO_VERSION=0:0:0
-AC_SUBST([with_platform])
-AC_SUBST([platform_with_platform], ["platform/${with_platform}"])
+ # Do not use -march=native with clang (due to possible failures on
+ # clang optimizations).
+ $CC --version | grep -q clang
-##########################################################################
-# Run platform specific checks and settings
-##########################################################################
-IMPLEMENTATION_NAME=""
-if test "${with_platform}" = "linux-generic";
+ if test $? -ne 0; then
+ ODP_CHECK_CFLAG([-march=native])
+ fi
+fi
+AM_CONDITIONAL(ODP_ABI_COMPAT, [test "x$ODP_ABI_COMPAT" = "x1"])
+
+##########################################################################
+# Enable/disable link time optimization
+##########################################################################
+lto_enabled=no
+AC_ARG_ENABLE([lto],
+ [AS_HELP_STRING([--enable-lto],
+ [Enable Link Time Optimization (LTO) in compiler and linker]
+ [[default=disabled]])],
+ [if test "x$enableval" = "xyes"; then
+ lto_enabled=yes
+ # Fat LTO object file contains GIMPLE bytecodes and the usual
+ # final code. There are less build problems (e.g. due to older
+ # binutils), but object files are larger.
+ #
+ # -flto=auto and -ffat-lto-objects are currently not supported by clang.
+ $CC --version | grep -q clang
+ if test $? -ne 0; then
+ ODP_LTO_FLAGS="-flto=auto -ffat-lto-objects"
+ else
+ ODP_LTO_FLAGS="-flto"
+ fi
+ fi])
+
+ODP_CFLAGS="$ODP_CFLAGS $ODP_LTO_FLAGS"
+ODP_LDFLAGS="$ODP_LDFLAGS $ODP_LTO_FLAGS"
+
+##########################################################################
+# Build examples/tests dynamically
+##########################################################################
+AC_ARG_ENABLE([static-applications],
+ [AS_HELP_STRING([--disable-static-applications],
+ [disable static linking of examples and tests]
+ [with ODP [default=enabled]])], [],
+ [enable_static_applications=yes])
+AS_IF([test "x$enable_static" != "xno" -a "x$enable_static_applications" != "xno"],
+ [enable_static_applications=yes], [enable_static_applications=no])
+
+AM_CONDITIONAL([STATIC_APPS], [test "x$enable_static_applications" != "xno"])
+if test "x$DPDK_SHARED" = "xyes" -a "x$enable_static_applications" != "xno" ;
then
- m4_include([./platform/linux-generic/m4/configure.m4])
- m4_include([./test/linux-generic/m4/configure.m4])
- IMPLEMENTATION_NAME="odp-linux"
-else
- echo "UNSUPPORTED PLATFORM: ${with_platform}"
- exit 1
+ AC_MSG_WARN([Static linking of examples and tests might fail when ]
+ [shared DPDK is detected. If build fails please retry with ]
+ [--disable-static-applications])
fi
-ODP_CFLAGS="$ODP_CFLAGS -DIMPLEMENTATION_NAME=$IMPLEMENTATION_NAME"
-
##########################################################################
# Include m4 files
##########################################################################
@@ -185,114 +290,64 @@ m4_include([./helper/m4/configure.m4])
m4_include([./test/m4/configure.m4])
##########################################################################
-# Set SDK install path
-##########################################################################
-AC_ARG_WITH([sdk-install-path],
-AC_HELP_STRING([--with-sdk-install-path=DIR path to external libs and headers],
- [(or in the default path if not specified).]),
-[SDK_INSTALL_PATH=$withval SDK_INSTALL_PATH_=1],[SDK_INSTALL_PATH_=])
-
-AC_SUBST(SDK_INSTALL_PATH)
-
-##########################################################################
# Set the install directory for test binaries/scripts
##########################################################################
AC_ARG_WITH([testdir],
- AC_HELP_STRING([--with-testdir=DIR installation directory for tests]),
- [if test "$withval" = "yes"; then
- testdir=$libdir/odp/tests
- else
- testdir=$withval
- fi], [])
+ [AS_HELP_STRING([--with-testdir[=DIR]],
+ [installation directory for tests [default=none]])],
+ [testdir=$withval],
+ [testdir=no])
+AS_IF([test "x$testdir" = "xyes"], [testdir=$libdir/odp/tests],
+ [test "x$testdir" = "xno"], [testdir=])
AC_SUBST([testdir])
+AM_CONDITIONAL([test_installdir], [test "x$testdir" != "xno"])
##########################################################################
# Set conditionals as computed within platform specific files
##########################################################################
-AM_CONDITIONAL([SDK_INSTALL_PATH_], [test "x${SDK_INSTALL_PATH_}" = "x1"])
-AM_CONDITIONAL([test_installdir], [test "$testdir" != ""])
-AM_CONDITIONAL([cunit_support], [test x$cunit_support = xyes ])
-AM_CONDITIONAL([test_vald], [test x$test_vald = xyes ])
-AM_CONDITIONAL([test_perf], [test x$test_perf = xyes ])
-AM_CONDITIONAL([test_perf_proc], [test x$test_perf_proc = xyes ])
-AM_CONDITIONAL([test_cpp], [test x$test_cpp = xyes ])
-AM_CONDITIONAL([test_helper], [test x$test_helper = xyes ])
-AM_CONDITIONAL([test_example], [test x$test_example = xyes ])
AM_CONDITIONAL([HAVE_DOXYGEN], [test "x${DOXYGEN}" = "xdoxygen"])
AM_CONDITIONAL([user_guide], [test "x${user_guides}" = "xyes" ])
AM_CONDITIONAL([HAVE_MSCGEN], [test "x${MSCGEN}" = "xmscgen"])
AM_CONDITIONAL([helper_linux], [test x$helper_linux = xyes ])
+AM_CONDITIONAL([helper_cli], [test x$helper_cli = xyes ])
AM_CONDITIONAL([ARCH_IS_ARM], [test "x${ARCH_DIR}" = "xarm"])
-AM_CONDITIONAL([ARCH_IS_MIPS64], [test "x${ARCH_DIR}" = "xmips64"])
+AM_CONDITIONAL([ARCH_IS_AARCH64], [test "x${ARCH_DIR}" = "xaarch64"])
+AM_CONDITIONAL([ARCH_IS_DEFAULT], [test "x${ARCH_DIR}" = "xdefault"])
AM_CONDITIONAL([ARCH_IS_POWERPC], [test "x${ARCH_DIR}" = "xpowerpc"])
AM_CONDITIONAL([ARCH_IS_X86], [test "x${ARCH_DIR}" = "xx86"])
+AM_CONDITIONAL([ARCH_IS_X86_32], [test "x${ARCH_ABI}" = "xx86_32-linux"])
+AM_CONDITIONAL([ARCH_IS_X86_64], [test "x${ARCH_ABI}" = "xx86_64-linux"])
##########################################################################
-# Setup doxygen documentation
+# Enable/disable ODP_DEBUG
##########################################################################
-DX_HTML_FEATURE(ON)
-DX_PDF_FEATURE(OFF)
-DX_PS_FEATURE(OFF)
-DX_INIT_DOXYGEN($PACKAGE_NAME,
- ${srcdir}/doc/application-api-guide/Doxyfile,
- ${builddir}/doc/application-api-guide/output,
- ${srcdir}/doc/helper-guide/Doxyfile,
- ${builddir}/doc/helper-guide/output,
- ${srcdir}/doc/platform-api-guide/Doxyfile,
- ${builddir}/doc/platform-api-guide/output,
- ${srcdir}/doc/driver-api-guide/Doxyfile,
- ${builddir}/doc/driver-api-guide/output)
+AC_ARG_ENABLE([debug],
+ [AS_HELP_STRING([--enable-debug],
+ [include additional debugging code.]
+ [Set to 'full' to enable all --enable-*-debug-* options]
+ [[default=disabled]])],
+ [], [enable_debug=no])
+
+AS_IF([test "x$enable_debug" != "xno"], [ODP_DEBUG=1],
+ [ODP_DEBUG=0])
+AC_DEFINE_UNQUOTED([ODP_DEBUG], [$ODP_DEBUG],
+ [Define to 1 to include additional debug code])
##########################################################################
# Enable/disable ODP_DEBUG_PRINT
##########################################################################
-ODP_DEBUG_PRINT=0
AC_ARG_ENABLE([debug-print],
- [ --enable-debug-print display debugging information],
- [if test "x$enableval" = "xyes"; then
- ODP_DEBUG_PRINT=1
- else
- ODP_DEBUG_PRINT=0
- fi])
-ODP_CFLAGS="$ODP_CFLAGS -DODP_DEBUG_PRINT=$ODP_DEBUG_PRINT"
+ [AS_HELP_STRING([--enable-debug-print],
+ [display debugging information [default=disabled]])],
+ [], [AS_IF([test "x$enable_debug" = "xfull"], [enable_debug_print=yes],
+ [enable_debug_print=no])])
+AS_IF([test "x$enable_debug_print" != "xno"], [ODP_DEBUG_PRINT=1],
+ [ODP_DEBUG_PRINT=0])
+AC_DEFINE_UNQUOTED([ODP_DEBUG_PRINT], [$ODP_DEBUG_PRINT],
+ [Define to 1 to display debug information])
-ODPH_DEBUG_PRINT=0
-AC_ARG_ENABLE([helper-debug-print],
- [ --enable-helper-debug-print display helper debugging information],
- [if test "x$enableval" = "xyes"; then
- ODPH_DEBUG_PRINT=1
- else
- ODPH_DEBUG_PRINT=0
- fi])
-ODP_CFLAGS="$ODP_CFLAGS -DODPH_DEBUG_PRINT=$ODPH_DEBUG_PRINT"
-
-##########################################################################
-# Enable/disable ODP_DEBUG
-##########################################################################
-ODP_DEBUG=0
-AC_ARG_ENABLE([debug],
- [ --enable-debug include additional code],
- [if test "x$enableval" = "xyes"; then
- ODP_DEBUG=1
- else
- ODP_DEBUG=0
- fi])
-ODP_CFLAGS="$ODP_CFLAGS -DODP_DEBUG=$ODP_DEBUG"
-
-##########################################################################
-# Enable/disable ABI compatible build
-##########################################################################
-ODP_ABI_COMPAT=1
-abi_compat=yes
-AC_ARG_ENABLE([abi-compat],
- [ --disable-abi-compat disables ABI compatible mode, enables inline code in header files],
- [if test "x$enableval" = "xno"; then
- ODP_ABI_COMPAT=0
- abi_compat=no
- #if there is no ABI compatibility the .so numbers are meaningless
- ODP_LIBSO_VERSION=0:0:0
- fi])
-AC_SUBST(ODP_ABI_COMPAT)
+debug_settings="ODP_DEBUG=${ODP_DEBUG}, ODP_DEBUG_PRINT=${ODP_DEBUG_PRINT}, \
+ODPH_DEBUG=${ODPH_DEBUG}, ODPH_DEBUG_PRINT=${ODPH_DEBUG_PRINT}"
##########################################################################
# Enable/disable deprecated API definitions
@@ -300,7 +355,8 @@ AC_SUBST(ODP_ABI_COMPAT)
ODP_DEPRECATED_API=0
deprecated=no
AC_ARG_ENABLE([deprecated],
- [ --enable-deprecated enable deprecated API definitions],
+ [AS_HELP_STRING([--enable-deprecated],
+ [enable deprecated API definitions [default=disabled]])],
[if test "x$enableval" = "xyes"; then
ODP_DEPRECATED_API=1
deprecated=yes
@@ -308,113 +364,134 @@ AC_ARG_ENABLE([deprecated],
AC_SUBST(ODP_DEPRECATED_API)
##########################################################################
-# Default warning setup
+# Determine which platform to build for
##########################################################################
-ODP_CFLAGS="$ODP_CFLAGS -W -Wall -Werror -Wstrict-prototypes -Wmissing-prototypes"
-ODP_CFLAGS="$ODP_CFLAGS -Wmissing-declarations -Wold-style-definition -Wpointer-arith"
-ODP_CFLAGS="$ODP_CFLAGS -Wcast-align -Wnested-externs -Wcast-qual -Wformat-nonliteral"
-ODP_CFLAGS="$ODP_CFLAGS -Wformat-security -Wundef -Wwrite-strings"
-ODP_CFLAGS="$ODP_CFLAGS -std=c99"
-
-dnl Use -Werror in the checks below since Clang emits a warning instead of
-dnl an error when it encounters an unknown warning option.
-AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough=0],
- [ODP_CFLAGS="$ODP_CFLAGS -Wimplicit-fallthrough=0"],
- [], [-Werror])
-AX_CHECK_COMPILE_FLAG([-Wformat-truncation=0],
- [ODP_CFLAGS="$ODP_CFLAGS -Wformat-truncation=0"],
- [], [-Werror])
-AX_CHECK_COMPILE_FLAG([-Wformat-overflow=0],
- [ODP_CFLAGS="$ODP_CFLAGS -Wformat-overflow=0"],
- [], [-Werror])
+AC_ARG_WITH([platform],
+ [AS_HELP_STRING([--with-platform=platform],
+ [select platform to be used [default=linux-generic]])],
+ [],
+ [with_platform=linux-generic
+ ])
-# Extra flags for example to suppress certain warning types
-ODP_CFLAGS="$ODP_CFLAGS $ODP_CFLAGS_EXTRA"
+AC_SUBST([with_platform])
##########################################################################
-# Check if compiler supports cmpxchng16 on x86-based architectures
+# Run platform specific checks and settings
+##########################################################################
+# Placeholder for platform dependency libraries which might come from custom
+# paths. Platform should fill it in configure.m4 with AS_VAR_APPEND.
+# This works around a bug in libtool.m4 on some systems:
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=297726
+PLAT_DEP_LIBS=
+AC_SUBST([PLAT_DEP_LIBS])
+# Platforms should append to this variable to have their settings printed
+# at the end of the configure report.
+PLAT_CFG_TEXT="
+ Platform specific:"
+
+AS_IF([test "${with_platform}" = "linux-generic"],
+ [m4_include([./platform/linux-generic/m4/configure.m4])],
+ [AC_MSG_ERROR([UNSUPPORTED PLATFORM: ${with_platform}])])
+
+AC_DEFINE_UNQUOTED([_ODP_IMPLEMENTATION_NAME], ["$ODP_IMPLEMENTATION_NAME"],
+ [Define to the name of the implementation])
+
+AM_CONDITIONAL([ODP_USE_CONFIG], [test "x$odp_use_config" = "xtrue"])
+AC_SUBST([ODP_LIB_NAME])
+
+##########################################################################
+# Setup doxygen documentation
##########################################################################
-case "${host}" in
- i?86? | x86*)
- if test "${CC}" != "gcc" -o ${CC_VERSION_MAJOR} -ge 5; then
- my_save_cflags="$CFLAGS"
+DX_HTML_FEATURE(ON)
+DX_PDF_FEATURE(OFF)
+DX_PS_FEATURE(OFF)
+DX_DOT_FEATURE(ON)
+DX_ENV_APPEND(WITH_PLATFORM, $with_platform)
- CFLAGS=-mcx16
- AC_MSG_CHECKING([whether CC supports -mcx16])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
- [AC_MSG_RESULT([yes])]
- [ODP_CFLAGS="$ODP_CFLAGS $CFLAGS"],
- [AC_MSG_RESULT([no])]
- )
- CFLAGS="$my_save_cflags"
- fi
- ;;
-esac
+DX_INIT_DOXYGEN($PACKAGE_NAME,
+ ${srcdir}/doc/application-api-guide/Doxyfile,
+ ${builddir}/doc/application-api-guide/output,
+ ${srcdir}/doc/helper-guide/Doxyfile,
+ ${builddir}/doc/helper-guide/output,
+ ${srcdir}/doc/platform-api-guide/Doxyfile,
+ ${builddir}/doc/platform-api-guide/output)
##########################################################################
# Default include setup
##########################################################################
-AM_CFLAGS="$AM_CFLAGS $ODP_CFLAGS"
-AM_CXXFLAGS="-std=c++11"
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([include/Makefile
+ include/odp/api/spec/version.h
+ include/odp/api/spec/deprecated.h])
-AC_CONFIG_FILES([Makefile
- pkgconfig/libodp-linux.pc
- pkgconfig/libodphelper.pc
- ])
+AC_CONFIG_FILES([helper/Makefile
+ helper/include/odp/helper/version.h])
##########################################################################
# distribute the changed variables among the Makefiles
-AC_SUBST([LIBS])
-AC_SUBST([AM_CPPFLAGS])
-AC_SUBST([CPPFLAGS])
-AC_SUBST([AM_CFLAGS])
-AC_SUBST([CFLAGS])
-AC_SUBST([AM_LDFLAGS])
-AC_SUBST([LDFLAGS])
+AC_SUBST([ODP_CFLAGS])
+AC_SUBST([ODP_CXXFLAGS])
+AC_SUBST([ODP_LDFLAGS])
+
AC_SUBST([EXEEXT])
+AM_CONDITIONAL([enable_static], [test x$enable_static = xyes ])
+AM_CONDITIONAL([enable_shared], [test x$enable_shared = xyes ])
+
+AM_CONDITIONAL([cross_compile], [test x$host != x -a x$host != x$build ])
+
+CC_VERSION=$($CC --version | head -n 1)
+
AC_OUTPUT
AC_MSG_RESULT([
$PACKAGE $VERSION
+ ODP API version: ${ODP_VERSION_API}
+ ODP Library name: ${ODP_LIB_NAME}
ODP Library version: ${ODP_LIBSO_VERSION}
- Helper Library version: ${ODPHELPER_LIBSO_VERSION}
+ Helper version: ${ODPH_VERSION}
- implementation_name: ${IMPLEMENTATION_NAME}
+ implementation_name: ${ODP_IMPLEMENTATION_NAME}
+ build: ${build}
host: ${host}
ARCH_DIR ${ARCH_DIR}
ARCH_ABI ${ARCH_ABI}
with_platform: ${with_platform}
helper_linux: ${helper_linux}
+ helper_cli: ${helper_cli}
prefix: ${prefix}
sysconfdir: ${sysconfdir}
libdir: ${libdir}
includedir: ${includedir}
testdir: ${testdir}
- WITH_ARCH: ${WITH_ARCH}
cc: ${CC}
cc version: ${CC_VERSION}
cppflags: ${CPPFLAGS}
- am_cppflags: ${AM_CPPFLAGS}
- am_cxxflags: ${AM_CXXFLAGS}
- cflags: ${CFLAGS}
- am_cflags: ${AM_CFLAGS}
- ldflags: ${LDFLAGS}
- am_ldflags: ${AM_LDFLAGS}
+ cflags: ${ODP_CFLAGS} ${CFLAGS}
+ cxxflags: ${ODP_CXXFLAGS} ${CXXFLAGS}
+ ld: ${LD}
+ ldflags: ${ODP_LDFLAGS} ${LDFLAGS}
libs: ${LIBS}
+ dependency libs: ${PLAT_DEP_LIBS}
defs: ${DEFS}
static libraries: ${enable_static}
shared libraries: ${enable_shared}
ABI compatible: ${abi_compat}
- Deprecated APIs: ${deprecated}
+ link time optimization: ${lto_enabled}
+ deprecated APIs: ${deprecated}
+ deprecated helper APIs: ${enable_helper_deprecated}
+ debug: ${debug_settings}
cunit: ${cunit_support}
+ static tests linkage: ${enable_static_applications}
+ with_examples: ${with_examples}
+ with_tests: ${with_tests}
test_vald: ${test_vald}
test_perf: ${test_perf}
- test_perf_proc: ${test_perf_proc}
test_cpp: ${test_cpp}
test_helper: ${test_helper}
test_example: ${test_example}
user_guides: ${user_guides}
+${PLAT_CFG_TEXT}
])
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 000000000..d7ec32eaf
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+!*.xml
diff --git a/doc/Doxyfile_common b/doc/Doxyfile_common
index 595585354..6c169b4e7 100644
--- a/doc/Doxyfile_common
+++ b/doc/Doxyfile_common
@@ -15,9 +15,9 @@ REFERENCES_RELATION = YES
ALPHABETICAL_INDEX = NO
QHP_NAMESPACE =
GENERATE_TREEVIEW = YES
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
CLASS_DIAGRAMS = NO
-HAVE_DOT = YES
+HAVE_DOT = $(HAVE_DOT)
CALL_GRAPH = YES
DOT_MULTI_TARGETS = NO
EXAMPLE_PATTERNS = *.c
@@ -30,3 +30,14 @@ MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
INTERNAL_DOCS = YES
DOT_IMAGE_FORMAT = svg
+DOT_GRAPH_MAX_NODES = 60
+PREDEFINED = __GNUC__ \
+ __attribute__(x)= \
+ ODP_ALIGNED(x)= \
+ __BIG_ENDIAN_BITFIELD \
+ __LITTLE_ENDIAN_BITFIELD \
+ __x86_64__ \
+ ODP_PACKED \
+ ODP_DEPRECATE(x)=x \
+ ODP_DEPRECATED_API=1 \
+ "ODP_HANDLE_T(type)=odp_handle_t type"
diff --git a/doc/Makefile.am b/doc/Makefile.am
index c6124f46a..708a294e7 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,8 +1,7 @@
SUBDIRS = \
application-api-guide \
helper-guide \
- platform-api-guide \
- driver-api-guide
+ platform-api-guide
if user_guide
SUBDIRS += implementers-guide users-guide process-guide
diff --git a/doc/Makefile.inc b/doc/Makefile.inc
index 237031271..1732c355a 100644
--- a/doc/Makefile.inc
+++ b/doc/Makefile.inc
@@ -13,4 +13,5 @@ SUFFIXES = .svg .msc .gv .html .adoc
.adoc.html:
asciidoctor $(ASCIIDOC_FLAGS) --out-file=$@ $<
-ASCIIDOC_FLAGS =-a data-uri -b html5 -a icons=font -a toc2
+ASCIIDOC_FLAGS =-a data-uri -b html5 -a icons=font -a toc2 \
+ $(EXTRA_ASCIIDOC_FLAGS)
diff --git a/doc/application-api-guide/Doxyfile b/doc/application-api-guide/Doxyfile
index 9cb183c13..9c718f203 100644
--- a/doc/application-api-guide/Doxyfile
+++ b/doc/application-api-guide/Doxyfile
@@ -1,12 +1,13 @@
@INCLUDE = $(SRCDIR)/doc/Doxyfile_common
PROJECT_NAME = "API Reference Manual"
-PROJECT_NUMBER = $(VERSION)
+PROJECT_NUMBER = $(ODP_VERSION_API)
PROJECT_LOGO = $(SRCDIR)/doc/images/ODP-Logo-HQ.svg
INPUT = $(SRCDIR)/doc/application-api-guide \
- $(SRCDIR)/include
-EXCLUDE_PATTERNS = drv* odp_drv.h
-EXAMPLE_PATH = $(SRCDIR)/example $(SRCDIR)
-PREDEFINED = __GNUC__ \
- "ODP_HANDLE_T(type)=odp_handle_t type"
+ include \
+ $(SRCDIR)/include \
+ $(SRCDIR)/example \
+ $(SRCDIR)/test/performance
+EXAMPLE_PATH = $(SRCDIR)/example $(SRCDIR)/CONTRIBUTING $(SRCDIR)/include/README \
+ $(SRCDIR)/test/performance
WARNINGS = NO
diff --git a/doc/application-api-guide/Makefile.am b/doc/application-api-guide/Makefile.am
index 6109e825d..29599597d 100644
--- a/doc/application-api-guide/Makefile.am
+++ b/doc/application-api-guide/Makefile.am
@@ -1,6 +1,6 @@
EXTRA_DIST = \
+ Doxyfile \
api_guide_lines.dox \
- examples.dox \
odp.dox \
release.dox
diff --git a/doc/application-api-guide/api_guide_lines.dox b/doc/application-api-guide/api_guide_lines.dox
index a6488c322..e1dfe1b3c 100644
--- a/doc/application-api-guide/api_guide_lines.dox
+++ b/doc/application-api-guide/api_guide_lines.dox
@@ -1,13 +1,10 @@
-/* Copyright (c) 2014, Linaro Limited
-
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
-@page api_guide_lines API Guide Lines
+@page api_guide_lines API Developer Guidelines
@tableofcontents
@@ -39,7 +36,7 @@ typedef struct odp_<descriptive_name>_s {
} odp_<descriptive_name>_t;
@endcode
-The use of typedef allows implementations to choose underlying data representations that map efficiently to platform capabilities while providing accessor functions to provide structured access to implementation information in a portable manner
+The use of typedef allows implementations to choose underlying data representations that map efficiently to platform capabilities while providing accessor functions to provide structured access to implementation information in a portable manner.
Similarly, the use of enum is RECOMMENDED to provide value abstraction for API parameters while enabling the implementation to choose code points that map well to platform native values.
Several native C types are used conventionally within ODP and SHOULD be employed in API design:
@@ -59,7 +56,7 @@ Minimizing pathlength in API design involves several considerations:
- The number of parameters passed to a call.
In general, ODP APIs designed for frequent use SHOULD have few parameters.
Limiting parameter count to one or two well-chosen parameters SHOULD be the goal for APIs designed for frequent use.
- If a call requires more complex parameter data then it is RECOMMENDED that instead of multiple parameters a single pointer to a struct that can be statically templated and modified by the caller be used.
+ If a call requires more complex parameter data then it is RECOMMENDED that instead of multiple parameters, a single pointer to a struct that can be statically templated and modified by the caller be used.
- The use of macros and inlining.
ODP APIs MAY be implemented as preprocessor macros and/or inline functions.
This is especially true for accessor functions that are designed to provide getters/setters for object meta data.
@@ -85,7 +82,7 @@ Functions must attempt to be so clear in their intent that referencing the docum
@subsection getters Getting information
@subsubsection is_has Is / Has
-An api with "is" or "has" are both considered @ref boolean questions. They can only return true or false and it reflects the current state of something.
+An API with "is" or "has" are both considered @ref boolean questions. They can only return true or false and it reflects the current state of something.
An example might be a packet interface, you might want to know if it is in promiscuous mode.
@code odp_bool_t state = odp_pktio_is_promiscuous(pktio handle) @endcode
@@ -98,10 +95,10 @@ Another case might be if a packet has a vlan flag set
@subsubsection get Get
Where possible returned information should be an enum if it reflects a finite list of information.
-In general get apis drop the actual tag "get" in the function name.
+In general, get APIs drop the actual tag "get" in the function name.
@subsection converter Converter Functions
-To maintain efficiency in fastpath code converter functions should expect correct inputs with undefined results otherwise.
+To maintain efficiency in fastpath code, converter functions should expect correct inputs with undefined results otherwise.
@code
static inline odp_foo_t _odp_foo_from_bar(odp_bar_t bar)
@@ -147,7 +144,7 @@ An example of this might be the number of queues that an application can create.
An attempt to allocate more queues than the underlying implementation supports would result in this failure code being returned via errno.
@subsection boolean Boolean
-For odp all booleans are integers. To aid application readability they are defined as the type odp_bool_t.
+For ODP all booleans are integers. To aid application readability they are defined as the type odp_bool_t.
The values !0 = true, 0 = false are used for this purpose.
@subsection success Success and Failure
@@ -203,6 +200,6 @@ For example an API marked as deprecated in 1.1.0 will still be present in 1.2.0
A deprecated API will contain the doxygen tag \@deprecated with a description of the reason for the change.
@section defaults Default behaviours
-When an API has a default behaviour it must be possible for the application to explicitly call for that behaviour, this guards against the default changing and breaking the application.
+When an API has a default behaviour it must be possible for the application to explicitly call for that behaviour; this guards against the default changing and breaking the application.
*/
diff --git a/doc/application-api-guide/examples.dox b/doc/application-api-guide/examples.dox
deleted file mode 100644
index 80fe46701..000000000
--- a/doc/application-api-guide/examples.dox
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @example odp_classifier.c
- * ODP classifier example application
- */
-
-/**
- * @example odp_generator.c
- * ODP loopback demo application
- */
-
-/**
- * @example odp_l2fwd.c
- * ODP l2fwd example application
- */
-
-/**
- *@example odp_pktio.c
- * ODP basic packet IO loopback test application
- */
-
-/**
- * @example odp_timer_test.c
- * ODP timer example application
- */
-
- /**
- * @example odp_ipfragreass.c
- * ODP IPv4 lock-free fragmentation and reassembly example application
- */
diff --git a/doc/application-api-guide/odp.dox b/doc/application-api-guide/odp.dox
index 579217945..a03c5bab5 100644
--- a/doc/application-api-guide/odp.dox
+++ b/doc/application-api-guide/odp.dox
@@ -1,7 +1,5 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
/**
@@ -25,9 +23,9 @@
* compiled against a specific ODP implementation layer. The purpose
* of the implementation layer is to provide an optimal mapping of ODP
* APIs to the underlying capabilities (including hardware
- * co-processing and acceleration support) of of SoCs hosting ODP
+ * co-processing and acceleration support) of SoCs hosting ODP
* implementations. As a bootstrapping mechanism for applications, as
- * well as to provide a model for ODP implementers, ODP provides a
+ * well as provide a model for ODP implementers, ODP provides a
* 'linux-generic' reference implementation designed to run on any SoC
* which has a Linux kernel. While linux-generic is not a performance
* target, it does provide a starting point for ODP implementers and
@@ -36,12 +34,16 @@
* for general Linux data plane support.
*
* @section contact Contact Details
- * - The main web site is http://www.opendataplane.org/
- * - The git repo is https://git.linaro.org/lng/odp.git
- * - Bug tracking is https://bugs.linaro.org/buglist.cgi?product=OpenDataPlane
+ * - The main web site is https://www.opendataplane.org/
+ * - The git repo is https://github.com/OpenDataPlane/odp.git
+ * - Bug tracking is https://github.com/OpenDataPlane/odp/issues
*
*/
-/**@page contributing Contributing Guide Lines
+/**@page api_principles API Principles
+ * @verbinclude include/README
+ */
+
+/**@page contributing Contributing Guidelines
* @verbinclude CONTRIBUTING
*/
diff --git a/doc/application-api-guide/release.dox b/doc/application-api-guide/release.dox
index eb6eca738..b646cf894 100644
--- a/doc/application-api-guide/release.dox
+++ b/doc/application-api-guide/release.dox
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -48,7 +46,7 @@ Existing application code shall not have to change if the new API is not used.
- New element to an enum that is an input to ODP
@subsection minor Minor
-The digit is used for backward compatible changes
+The digit is used for backward compatible changes.
Any existing app should work as before.
- Documentation updates
diff --git a/doc/driver-api-guide/.gitignore b/doc/driver-api-guide/.gitignore
deleted file mode 100644
index 53752db25..000000000
--- a/doc/driver-api-guide/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-output
diff --git a/doc/driver-api-guide/Doxyfile b/doc/driver-api-guide/Doxyfile
deleted file mode 100644
index 680d1d428..000000000
--- a/doc/driver-api-guide/Doxyfile
+++ /dev/null
@@ -1,14 +0,0 @@
-@INCLUDE = $(SRCDIR)/doc/Doxyfile_common
-
-PROJECT_NAME = "Driver Interface (drv) Reference Manual"
-PROJECT_NUMBER = $(VERSION)
-PROJECT_LOGO = $(SRCDIR)/doc/images/ODP-Logo-HQ.svg
-INPUT = $(SRCDIR)/doc/driver-api-guide \
- $(SRCDIR)/include/odp/drv \
- $(SRCDIR)/include/odp_drv.h
-EXCLUDE_PATTERNS = drv* odp_drv.h
-EXAMPLE_PATH = $(SRCDIR)/example $(SRCDIR)
-PREDEFINED = __GNUC__ \
- "ODP_HANDLE_T(type)=odp_handle_t type" \
- odpdrv_bool_t=int
-WARNINGS = NO
diff --git a/doc/driver-api-guide/Makefile.am b/doc/driver-api-guide/Makefile.am
deleted file mode 100644
index 4fc4755d1..000000000
--- a/doc/driver-api-guide/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-EXTRA_DIST = \
- odp.dox
-
-clean-local:
- rm -rf output
diff --git a/doc/driver-api-guide/odp.dox b/doc/driver-api-guide/odp.dox
deleted file mode 100644
index 687a79e04..000000000
--- a/doc/driver-api-guide/odp.dox
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @mainpage
- *
- * @section sec_1 Introduction
- *
- * OpenDataPlane (ODP) provides a driver interface
-
- *
- * @section contact Contact Details
- * - The main web site is http://www.opendataplane.org/
- * - The git repo is https://git.linaro.org/lng/odp.git
- * - Bug tracking is https://bugs.linaro.org/buglist.cgi?product=OpenDataPlane
- *
- */
diff --git a/doc/helper-guide/Doxyfile b/doc/helper-guide/Doxyfile
index bcc24539c..cf370e129 100644
--- a/doc/helper-guide/Doxyfile
+++ b/doc/helper-guide/Doxyfile
@@ -1,5 +1,5 @@
-PROJECT_NAME = "Helper Reference Manual for $(WITH_PLATFORM)"
-PROJECT_NUMBER = $(VERSION)
+PROJECT_NAME = "Helper Reference Manual"
+PROJECT_NUMBER = $(ODPH_VERSION)
PROJECT_LOGO = $(SRCDIR)/doc/images/ODP-Logo-HQ.svg
QUIET = YES
OUTPUT_DIRECTORY = $(DOCDIR)
@@ -20,9 +20,9 @@ REFERENCES_RELATION = YES
ALPHABETICAL_INDEX = NO
QHP_NAMESPACE =
GENERATE_TREEVIEW = YES
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
CLASS_DIAGRAMS = NO
-HAVE_DOT = YES
+HAVE_DOT = $(HAVE_DOT)
CALL_GRAPH = YES
DOT_MULTI_TARGETS = NO
EXAMPLE_PATTERNS = *.c
@@ -33,13 +33,6 @@ LAYOUT_FILE = $(SRCDIR)/doc/doxygenlayout.xml
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
-PREDEFINED = __GNUC__ \
- __attribute__(x)= \
- ODP_ALIGNED(x)= \
- __BIG_ENDIAN_BITFIELD \
- __LITTLE_ENDIAN_BITFIELD \
- __x86_64__ \
- ODP_PACKED \
- "ODP_HANDLE_T(type)=odp_handle_t type"
INTERNAL_DOCS = YES
DOT_IMAGE_FORMAT = svg
+PREDEFINED = ODPH_DEPRECATE(x)=x
diff --git a/doc/helper-guide/odp.dox b/doc/helper-guide/odp.dox
index 0a86d4786..ae341918e 100644
--- a/doc/helper-guide/odp.dox
+++ b/doc/helper-guide/odp.dox
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -20,7 +18,7 @@
*
* @section contact Contact Details
* - The main web site is http://www.opendataplane.org/
- * - The git repo is https://git.linaro.org/lng/odp.git
- * - Bug tracking is https://bugs.linaro.org/buglist.cgi?product=OpenDataPlane
+ * - The git repo is https://github.com/OpenDataPlane/odp.git
+ * - Bug tracking is https://github.com/OpenDataPlane/odp/issues
*
*/
diff --git a/doc/images/.gitignore b/doc/images/.gitignore
index 003dbe653..9bcc44f58 100644
--- a/doc/images/.gitignore
+++ b/doc/images/.gitignore
@@ -1,4 +1,5 @@
resource_management.svg
+ipsec_sa_states.svg
pktio_fsm.svg
timer_fsm.svg
timeout_fsm.svg
diff --git a/doc/images/ODP-Logo-HQ.svg b/doc/images/ODP-Logo-HQ.svg
index bd83bcd93..7f33caff8 100644
--- a/doc/images/ODP-Logo-HQ.svg
+++ b/doc/images/ODP-Logo-HQ.svg
@@ -9,12 +9,12 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Layer_1"
data-name="Layer 1"
- viewBox="0 0 190 49"
+ viewBox="0 0 200 50"
version="1.1"
- inkscape:version="0.91 r13725"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="ODP-Logo-HQ.svg"
- width="190"
- height="49">
+ width="200"
+ height="50">
<metadata
id="metadata71">
<rdf:RDF>
@@ -36,21 +36,22 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="2511"
- inkscape:window-height="1416"
+ inkscape:window-width="1800"
+ inkscape:window-height="991"
id="namedview69"
showgrid="false"
- inkscape:zoom="2.253318"
- inkscape:cx="297.0423"
- inkscape:cy="74.997335"
- inkscape:window-x="1969"
- inkscape:window-y="24"
+ inkscape:zoom="5.2958056"
+ inkscape:cx="108.40047"
+ inkscape:cy="24.5"
+ inkscape:window-x="0"
+ inkscape:window-y="31"
inkscape:window-maximized="1"
- inkscape:current-layer="Layer_1"
+ inkscape:current-layer="g4462"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
- fit-margin-bottom="0" />
+ fit-margin-bottom="0"
+ inkscape:snap-page="false" />
<defs
id="defs3">
<style
@@ -60,150 +61,128 @@
id="title7">OpenDataPlane-branding_ODP Logo</title>
<g
id="g4462"
- transform="matrix(0.32035335,0,0,0.32660788,-0.15849903,0.00353742)">
- <text
- transform="translate(531.29,127.66)"
- id="text9"
- class="cls-1"
- style="font-size:44.31000137px;font-family:'Squada One';fill:#808080">.org</text>
+ transform="matrix(0.26109597,0,0,0.26619358,1.0037914,4.7794569)">
<rect
id="rect11"
- transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-70.58,74.14)"
+ transform="rotate(-45)"
ry="14.91"
rx="14.91"
height="114.8"
width="114.8"
- y="46.509998"
- x="45.290001"
+ y="50.883595"
+ x="-53.179798"
class="cls-2"
style="fill:#333333" />
<path
- transform="translate(-27.18,-28.91)"
id="path13"
- d="m 102.15,104 0,74.9 0.14,0 c 3.61,-0.15 8.18,-1.58 10.93,-4.34 l 60.1,-60.09 a 14.84,14.84 0 0 0 4.34,-9.92 l 0,-0.53 -75.51,0 z"
+ d="m 79.013941,73.671268 v 74.900002 h 0.14 c 3.61,-0.15 8.18,-1.58 10.93,-4.34 L 150.18395,84.141268 a 14.84,14.84 0 0 0 4.34,-9.92 v -0.53 H 79.013941 Z"
class="cls-2"
inkscape:connector-curvature="0"
style="fill:#333333" />
<path
- transform="translate(-27.18,-28.91)"
style="fill:#00ffff"
id="path15"
- d="m 102.15,104 0,-75 0.14,0 c 3.61,0.15 8.18,1.58 10.93,4.34 l 60.1,60.09 a 15,15 0 0 1 4.34,10 l 0,0.58 -75.51,0 z"
+ d="M 79.013941,73.671268 V -1.3287319 h 0.14 c 3.61,0.15 8.18,1.58 10.93,4.34 L 150.18395,63.101268 a 15,15 0 0 1 4.34,10 v 0.58 H 79.013941 Z"
inkscape:connector-curvature="0" />
<path
- transform="translate(-27.18,-28.91)"
style="fill:#00ffff"
id="path17"
- d="m 102.15,104 0,74.9 0.15,0 c -3.61,-0.15 -8,-1.58 -10.79,-4.34 l -60,-60.09"
+ d="m 79.013941,73.671268 v 74.900002 h 0.15 c -3.61,-0.15 -8,-1.58 -10.79,-4.34 L 8.3739406,84.141268"
inkscape:connector-curvature="0" />
<path
- transform="translate(-27.18,-28.91)"
id="path19"
- d="m 102.15,104 0,-75 0.15,0 c -3.61,0.15 -8,1.58 -10.79,4.34 l -60,60.09"
+ d="M 79.013941,73.671268 V -1.3287319 h 0.15 c -3.61,0.15 -8,1.58 -10.79,4.34 L 8.3739406,63.101268"
class="cls-2"
inkscape:connector-curvature="0"
style="fill:#333333" />
<path
- transform="translate(-27.18,-28.91)"
id="path23"
- d="M 102.23,49.19 48.4,103.54 102.75,157.37 156.58,103 Z m 0.38,78.91 -25.12,-24.88 24.88,-25.12 25.12,24.9 z"
+ d="m 79.093941,18.861268 -53.83,54.35 54.35,53.830002 53.830009,-54.370002 z m 0.38,78.91 -25.12,-24.88 24.88,-25.12 25.119999,24.9 z"
class="cls-4"
inkscape:connector-curvature="0"
style="fill:#333333;stroke:#ffe817;stroke-width:4px;stroke-miterlimit:10" />
<rect
id="rect25"
- transform="matrix(0.70364278,-0.7105539,0.7105539,0.70364278,-70.07,74.46)"
+ transform="rotate(-45.28)"
height="35.360001"
width="35.360001"
- y="85.419998"
- x="84.809998"
+ y="89.899895"
+ x="-13.548523"
class="cls-5"
style="fill:none;stroke:#ffe817;stroke-width:4px;stroke-miterlimit:10" />
- <text
- transform="translate(531.29,127.66)"
- id="text27"
- class="cls-1"
- style="font-size:44.31000137px;font-family:'Squada One';fill:#808080">.org</text>
<rect
id="rect29"
- transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-70.58,74.14)"
+ transform="rotate(-45)"
ry="14.91"
rx="14.91"
height="114.8"
width="114.8"
- y="46.509998"
- x="45.290001"
+ y="50.883595"
+ x="-53.179798"
class="cls-2"
style="fill:#333333" />
<path
- transform="translate(-27.18,-28.91)"
id="path31"
- d="m 102.15,104 0,74.9 0.14,0 c 3.61,-0.15 8.18,-1.58 10.93,-4.34 l 60.1,-60.09 a 14.84,14.84 0 0 0 4.34,-9.92 l 0,-0.53 -75.51,0 z"
+ d="m 79.013941,73.671268 v 74.900002 h 0.14 c 3.61,-0.15 8.18,-1.58 10.93,-4.34 L 150.18395,84.141268 a 14.84,14.84 0 0 0 4.34,-9.92 v -0.53 H 79.013941 Z"
class="cls-6"
inkscape:connector-curvature="0"
style="fill:#666666" />
<path
- transform="translate(-27.18,-28.91)"
id="path33"
- d="m 102.15,104 0,-75 0.14,0 c 3.61,0.15 8.18,1.58 10.93,4.34 l 60.1,60.09 a 15,15 0 0 1 4.34,10 l 0,0.58 -75.51,0 z"
+ d="M 79.013941,73.671268 V -1.3287319 h 0.14 c 3.61,0.15 8.18,1.58 10.93,4.34 L 150.18395,63.101268 a 15,15 0 0 1 4.34,10 v 0.58 H 79.013941 Z"
class="cls-7"
inkscape:connector-curvature="0"
style="fill:#1a1a1a" />
<path
- transform="translate(-27.18,-28.91)"
id="path35"
- d="m 102.15,104 0,74.9 0.15,0 c -3.61,-0.15 -8,-1.58 -10.79,-4.34 l -60,-60.09"
+ d="m 79.013941,73.671268 v 74.900002 h 0.15 c -3.61,-0.15 -8,-1.58 -10.79,-4.34 L 8.3739406,84.141268"
class="cls-7"
inkscape:connector-curvature="0"
style="fill:#1a1a1a" />
<path
- transform="translate(-27.18,-28.91)"
id="path37"
- d="m 102.15,104 0,-75 0.15,0 c -3.61,0.15 -8,1.58 -10.79,4.34 l -60,60.09"
+ d="M 79.013941,73.671268 V -1.3287319 h 0.15 c -3.61,0.15 -8,1.58 -10.79,4.34 L 8.3739406,63.101268"
class="cls-6"
inkscape:connector-curvature="0"
style="fill:#666666" />
<path
- transform="translate(-27.18,-28.91)"
style="fill:#000000;fill-opacity:0"
id="path39"
- d="m 235.1,86.13 0,30.75 q 0,10.07 -9.46,10.07 l -10.15,0 Q 206,127 206,116.89 l 0,-30.76 q 0,-10.07 9.46,-10.07 l 10.15,0 q 9.49,0 9.49,10.07 z m -10.68,32 0,-33.3"
+ d="m 207.92,57.219998 v 30.75 q 0,10.07 -9.46,10.07 h -10.15 q -9.49,0.05 -9.49,-10.06 v -30.76 q 0,-10.07 9.46,-10.07 h 10.15 q 9.49,0 9.49,10.07 z m -10.68,32 v -33.3"
class="cls-8"
inkscape:connector-curvature="0" />
<path
- transform="translate(-27.18,-28.91)"
id="path41"
- d="m 260.67,127 -1.6,0"
+ d="m 233.49,98.089998 h -1.6"
class="cls-8"
inkscape:connector-curvature="0"
style="fill:#009645" />
<path
- transform="translate(-27.18,-28.91)"
id="path65"
- d="M 102.23,49.19 48.4,103.54 102.75,157.37 156.58,103 Z m 0.38,78.91 -25.12,-24.88 24.88,-25.12 25.12,24.9 z"
+ d="m 79.093941,18.861268 -53.83,54.35 54.35,53.830002 53.830009,-54.370002 z m 0.38,78.91 -25.12,-24.88 24.88,-25.12 25.119999,24.9 z"
class="cls-9"
inkscape:connector-curvature="0"
style="fill:#666666;stroke:#ffe817;stroke-width:4px;stroke-miterlimit:10" />
<rect
id="rect67"
- transform="matrix(0.70364278,-0.7105539,0.7105539,0.70364278,-70.07,74.46)"
+ transform="rotate(-45.28)"
height="35.360001"
width="35.360001"
- y="85.419998"
- x="84.809998"
+ y="89.899895"
+ x="-13.548523"
class="cls-5"
style="fill:none;stroke:#ffe817;stroke-width:4px;stroke-miterlimit:10" />
<text
- transform="scale(0.92486289,1.0812414)"
- sodipodi:linespacing="125%"
+ transform="scale(0.92486285,1.0812414)"
id="text4199"
- y="81.79998"
- x="174.92181"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:83.23766327px;line-height:125%;font-family:'Squada One';-inkscape-font-specification:'Squada One, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#009645;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ y="91.752487"
+ x="174.00948"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:73.42362213px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#009645;fill-opacity:1;stroke:none;stroke-width:1.03697407px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
- y="81.79998"
- x="174.92181"
+ y="91.752487"
+ x="174.00948"
id="tspan4201"
- sodipodi:role="line">OpenDataPlane</tspan></text>
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:73.42362213px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.03697407px">OpenDataPlane</tspan></text>
</g>
</svg>
diff --git a/doc/images/ipsec-inline.svg b/doc/images/ipsec-inline.svg
new file mode 100644
index 000000000..e5b160b1a
--- /dev/null
+++ b/doc/images/ipsec-inline.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.2" width="471.92221mm" height="171.72112mm" viewBox="0 0 47192.222 17172.112" preserveAspectRatio="xMidYMid" xml:space="preserve" id="svg1189" sodipodi:docname="ipsec-inline.svg" style="fill-rule:evenodd;stroke-width:28.22200012;stroke-linejoin:round" inkscape:version="0.92.1 r15371"><metadata id="metadata1193"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1407" inkscape:window-height="722" id="namedview1191" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="0.55044522" inkscape:cx="939.7648" inkscape:cy="333.33699" inkscape:window-x="65" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg1189"/><defs class="ClipPathGroup" id="defs8"><clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"><rect x="0" y="0" width="59411" height="42012" id="rect2"/></clipPath><clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse"><rect x="59" y="42" width="59293" height="41928" id="rect5"/></clipPath></defs><defs id="defs93"><font id="EmbeddedFont_1" horiz-adv-x="2048" horiz-origin-x="0" horiz-origin-y="0" vert-origin-x="45" vert-origin-y="90" vert-adv-y="90"><font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1826" descent="423" id="font-face10"/><missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z" id="missing-glyph12"/><glyph unicode="y" horiz-adv-x="1059" d="M 604,1 C 579,-64 553,-123 527,-175 500,-227 471,-272 438,-309 405,-346 369,-374 329,-394 289,-413 243,-423 191,-423 168,-423 147,-423 128,-423 109,-423 88,-420 67,-414 L 67,-279 C 80,-282 94,-284 110,-284 126,-284 140,-284 151,-284 204,-284 253,-264 298,-225 343,-186 383,-124 417,-38 L 434,5 5,1082 197,1082 425,484 C 432,466 440,442 451,412 461,382 471,352 482,322 492,292 501,265 509,241 517,217 522,202 523,196 525,203 530,218 538,240 545,261 554,285 564,312 573,339 583,366 593,393 603,420 611,444 618,464 L 830,1082 1020,1082 604,1 Z" id="glyph14"/><glyph unicode="v" horiz-adv-x="1059" d="M 613,0 L 400,0 7,1082 199,1082 437,378 C 442,363 447,346 454,325 460,304 466,282 473,259 480,236 486,215 492,194 497,173 502,155 506,141 510,155 515,173 522,194 528,215 534,236 541,258 548,280 555,302 562,323 569,344 575,361 580,376 L 826,1082 1017,1082 613,0 Z" id="glyph16"/><glyph unicode="u" horiz-adv-x="901" d="M 314,1082 L 314,396 C 314,343 318,299 326,264 333,229 346,200 363,179 380,157 403,142 432,133 460,124 495,119 537,119 580,119 618,127 653,142 687,157 716,178 741,207 765,235 784,270 797,312 810,353 817,401 817,455 L 817,1082 997,1082 997,228 C 997,205 997,181 998,156 998,131 998,107 999,85 1000,62 1000,43 1001,27 1002,11 1002,3 1003,3 L 833,3 C 832,6 832,15 831,30 830,44 830,61 829,79 828,98 827,117 826,136 825,156 825,172 825,185 L 822,185 C 805,154 786,125 765,100 744,75 720,53 693,36 666,18 634,4 599,-6 564,-15 523,-20 476,-20 416,-20 364,-13 321,2 278,17 242,39 214,70 186,101 166,140 153,188 140,236 133,294 133,361 L 133,1082 314,1082 Z" id="glyph18"/><glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 527,1 499,-5 471,-10 442,-14 409,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 467,127 484,128 501,131 517,134 535,137 554,141 L 554,8 Z" id="glyph20"/><glyph unicode="s" horiz-adv-x="927" d="M 950,299 C 950,248 940,203 921,164 901,124 872,91 835,64 798,37 752,16 698,2 643,-13 581,-20 511,-20 448,-20 392,-15 342,-6 291,4 247,20 209,41 171,62 139,91 114,126 88,161 69,203 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 550,117 585,120 618,125 650,130 678,140 701,153 724,166 743,183 756,205 769,226 775,253 775,285 775,318 767,345 752,366 737,387 715,404 688,418 661,432 628,444 589,455 550,465 507,476 460,489 417,500 374,513 331,527 288,541 250,560 216,583 181,606 153,634 132,668 111,702 100,745 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 763,842 752,866 736,885 720,904 701,919 678,931 655,942 630,951 602,956 573,961 544,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,785 282,761 297,742 311,723 331,707 357,694 382,681 413,669 449,660 485,650 525,640 568,629 597,622 626,614 656,606 686,597 715,587 744,576 772,564 799,550 824,535 849,519 870,500 889,478 908,456 923,430 934,401 945,372 950,338 950,299 Z" id="glyph22"/><glyph unicode="r" horiz-adv-x="556" d="M 142,0 L 142,830 C 142,853 142,876 142,900 141,923 141,946 140,968 139,990 139,1011 138,1030 137,1049 137,1067 136,1082 L 306,1082 C 307,1067 308,1049 309,1030 310,1010 311,990 312,969 313,948 313,929 314,910 314,891 314,874 314,861 L 318,861 C 331,902 344,938 359,969 373,999 390,1024 409,1044 428,1063 451,1078 478,1088 505,1097 537,1102 575,1102 590,1102 604,1101 617,1099 630,1096 641,1094 648,1092 L 648,927 C 636,930 622,933 606,935 590,936 572,937 552,937 511,937 476,928 447,909 418,890 394,865 376,832 357,799 344,759 335,714 326,668 322,618 322,564 L 322,0 142,0 Z" id="glyph24"/><glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,464 1046,388 1033,319 1020,250 998,190 967,140 936,90 895,51 844,23 793,-6 730,-20 655,-20 578,-20 510,-5 452,24 394,53 350,101 319,168 L 314,168 C 315,167 315,161 316,150 316,139 316,126 317,110 317,94 317,76 318,57 318,37 318,17 318,-2 L 318,-425 138,-425 138,864 C 138,891 138,916 138,940 137,964 137,986 136,1005 135,1025 135,1042 134,1056 133,1070 133,1077 132,1077 L 306,1077 C 307,1075 308,1068 309,1057 310,1045 311,1031 312,1014 313,998 314,980 315,961 316,943 316,925 316,908 L 320,908 C 337,943 356,972 377,997 398,1021 423,1041 450,1057 477,1072 508,1084 542,1091 575,1098 613,1101 655,1101 730,1101 793,1088 844,1061 895,1034 936,997 967,949 998,900 1020,842 1033,774 1046,705 1053,629 1053,546 Z M 864,542 C 864,609 860,668 852,720 844,772 830,816 811,852 791,888 765,915 732,934 699,953 658,962 609,962 569,962 531,956 496,945 461,934 430,912 404,880 377,848 356,804 341,748 326,691 318,618 318,528 318,451 324,387 337,334 350,281 368,238 393,205 417,172 447,149 483,135 519,120 560,113 607,113 657,113 699,123 732,142 765,161 791,189 811,226 830,263 844,308 852,361 860,414 864,474 864,542 Z" id="glyph26"/><glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 490,-20 422,-9 363,14 304,37 254,71 213,118 172,165 140,223 119,294 97,364 86,447 86,542 86,915 248,1102 571,1102 655,1102 728,1090 789,1067 850,1044 900,1009 939,962 978,915 1006,857 1025,787 1044,717 1053,635 1053,542 Z M 864,542 C 864,626 858,695 845,750 832,805 813,848 788,881 763,914 732,937 696,950 660,963 619,969 574,969 528,969 487,962 450,949 413,935 381,912 355,879 329,846 309,802 296,747 282,692 275,624 275,542 275,458 282,389 297,334 312,279 332,235 358,202 383,169 414,146 449,133 484,120 522,113 563,113 609,113 651,120 688,133 725,146 757,168 783,201 809,234 829,278 843,333 857,388 864,458 864,542 Z" id="glyph28"/><glyph unicode="n" horiz-adv-x="900" d="M 825,0 L 825,686 C 825,739 821,783 814,818 806,853 793,882 776,904 759,925 736,941 708,950 679,959 644,963 602,963 559,963 521,956 487,941 452,926 423,904 399,876 374,847 355,812 342,771 329,729 322,681 322,627 L 322,0 142,0 142,853 C 142,876 142,900 142,925 141,950 141,974 140,996 139,1019 139,1038 138,1054 137,1070 137,1078 136,1078 L 306,1078 C 307,1075 307,1066 308,1052 309,1037 310,1021 311,1002 312,984 312,965 313,945 314,926 314,910 314,897 L 317,897 C 334,928 353,957 374,982 395,1007 419,1029 446,1047 473,1064 505,1078 540,1088 575,1097 616,1102 663,1102 723,1102 775,1095 818,1080 861,1065 897,1043 925,1012 953,981 974,942 987,894 1000,845 1006,788 1006,721 L 1006,0 825,0 Z" id="glyph30"/><glyph unicode="m" horiz-adv-x="1456" d="M 768,0 L 768,686 C 768,739 765,783 758,818 751,853 740,882 725,904 709,925 688,941 663,950 638,959 607,963 570,963 532,963 498,956 467,941 436,926 410,904 389,876 367,847 350,812 339,771 327,729 321,681 321,627 L 321,0 142,0 142,853 C 142,876 142,900 142,925 141,950 141,974 140,996 139,1019 139,1038 138,1054 137,1070 137,1078 136,1078 L 306,1078 C 307,1075 307,1066 308,1052 309,1037 310,1021 311,1002 312,984 312,965 313,945 314,926 314,910 314,897 L 317,897 C 333,928 350,957 369,982 388,1007 410,1029 435,1047 460,1064 488,1078 521,1088 553,1097 590,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 946,928 964,957 984,982 1004,1007 1027,1029 1054,1047 1081,1064 1111,1078 1144,1088 1177,1097 1215,1102 1258,1102 1313,1102 1360,1095 1400,1080 1439,1065 1472,1043 1497,1012 1522,981 1541,942 1553,894 1565,845 1571,788 1571,721 L 1571,0 1393,0 1393,686 C 1393,739 1390,783 1383,818 1376,853 1365,882 1350,904 1334,925 1313,941 1288,950 1263,959 1232,963 1195,963 1157,963 1123,956 1092,942 1061,927 1035,906 1014,878 992,850 975,815 964,773 952,731 946,682 946,627 L 946,0 768,0 Z" id="glyph32"/><glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z" id="glyph34"/><glyph unicode="k" horiz-adv-x="927" d="M 816,0 L 450,494 318,385 318,0 138,0 138,1484 318,1484 318,557 793,1082 1004,1082 565,617 1027,0 816,0 Z" id="glyph36"/><glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z" id="glyph38"/><glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 337,934 359,965 382,991 405,1016 431,1037 459,1054 487,1071 518,1083 551,1091 584,1098 622,1102 663,1102 732,1102 789,1093 834,1074 878,1055 913,1029 939,996 964,962 982,922 992,875 1001,828 1006,777 1006,721 L 1006,0 825,0 825,686 C 825,732 822,772 817,807 811,842 800,871 784,894 768,917 745,934 716,946 687,957 649,963 602,963 559,963 521,955 487,940 452,925 423,903 399,875 374,847 355,813 342,773 329,733 322,688 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1076 322,1054 321,1032 320,1010 320,990 319,971 318,952 317,937 316,924 315,911 315,902 314,897 L 317,897 Z" id="glyph40"/><glyph unicode="g" horiz-adv-x="954" d="M 548,-425 C 486,-425 431,-419 383,-406 335,-393 294,-375 260,-352 226,-328 198,-300 177,-267 156,-234 140,-198 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 594,-288 631,-282 664,-271 697,-260 726,-241 749,-217 772,-191 790,-159 803,-119 816,-79 822,-30 822,27 L 822,201 820,201 C 807,174 790,148 771,123 751,98 727,75 699,56 670,37 637,21 600,10 563,-2 520,-8 472,-8 403,-8 345,4 296,27 247,50 207,84 176,130 145,176 122,233 108,302 93,370 86,449 86,539 86,626 93,704 108,773 122,842 145,901 178,950 210,998 252,1035 304,1061 355,1086 418,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,914 825,933 826,953 827,974 828,994 829,1012 830,1031 831,1046 832,1060 833,1073 835,1080 836,1080 L 1007,1080 C 1006,1074 1006,1064 1005,1050 1004,1035 1004,1018 1003,998 1002,978 1002,956 1002,932 1001,907 1001,882 1001,856 L 1001,30 C 1001,-121 964,-234 890,-311 815,-387 701,-425 548,-425 Z M 822,541 C 822,616 814,681 798,735 781,788 760,832 733,866 706,900 676,925 642,941 607,957 572,965 536,965 490,965 451,957 418,941 385,925 357,900 336,866 314,831 298,787 288,734 277,680 272,616 272,541 272,463 277,398 288,345 298,292 314,249 335,216 356,183 383,160 416,146 449,132 488,125 533,125 569,125 604,133 639,148 673,163 704,188 731,221 758,254 780,297 797,350 814,403 822,466 822,541 Z" id="glyph42"/><glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1243 185,1280 192,1314 199,1347 213,1377 233,1402 252,1427 279,1446 313,1461 347,1475 391,1482 445,1482 466,1482 489,1481 512,1479 535,1477 555,1474 572,1470 L 572,1333 C 561,1335 548,1337 533,1339 518,1340 504,1341 492,1341 465,1341 444,1337 427,1330 410,1323 396,1312 387,1299 377,1285 370,1268 367,1248 363,1228 361,1205 361,1179 L 361,1082 572,1082 572,951 361,951 Z" id="glyph44"/><glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,446 282,394 294,347 305,299 323,258 348,224 372,189 403,163 441,144 479,125 525,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 1008,206 992,176 972,146 951,115 924,88 890,64 856,39 814,19 763,4 712,-12 650,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,649 100,735 125,806 150,876 185,933 229,977 273,1021 324,1053 383,1073 442,1092 504,1102 571,1102 662,1102 738,1087 799,1058 860,1029 909,988 946,937 983,885 1009,824 1025,754 1040,684 1048,608 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 538,969 507,964 474,955 441,945 410,928 382,903 354,878 330,845 311,803 292,760 281,706 278,641 L 862,641 Z" id="glyph46"/><glyph unicode="d" horiz-adv-x="954" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 C 823,921 823,931 823,946 822,960 822,975 822,991 821,1006 821,1021 821,1035 821,1049 821,1059 821,1065 L 821,1484 1001,1484 1001,219 C 1001,193 1001,168 1002,143 1002,119 1002,97 1003,77 1004,57 1004,40 1005,26 1006,11 1006,4 1007,4 L 835,4 C 834,11 833,20 832,32 831,44 830,58 829,73 828,89 827,105 826,123 825,140 825,157 825,174 L 821,174 Z M 275,542 C 275,467 280,403 289,350 298,297 313,253 334,219 355,184 381,159 413,143 445,127 484,119 530,119 577,119 619,127 656,142 692,157 722,182 747,217 771,251 789,296 802,351 815,406 821,474 821,554 821,631 815,696 802,749 789,802 771,844 746,877 721,910 691,933 656,948 620,962 579,969 532,969 488,969 450,961 418,946 386,931 359,906 338,872 317,838 301,794 291,740 280,685 275,619 275,542 Z" id="glyph48"/><glyph unicode="c" horiz-adv-x="875" d="M 275,546 C 275,484 280,427 289,375 298,323 313,278 334,241 355,203 384,174 419,153 454,132 497,122 548,122 612,122 666,139 709,173 752,206 778,258 788,328 L 970,328 C 964,283 951,239 931,197 911,155 884,118 850,86 815,54 773,28 724,9 675,-10 618,-20 553,-20 468,-20 396,-6 337,23 278,52 230,91 193,142 156,192 129,251 112,320 95,388 87,462 87,542 87,615 93,679 105,735 117,790 134,839 156,881 177,922 203,957 232,986 261,1014 293,1037 328,1054 362,1071 398,1083 436,1091 474,1098 512,1102 551,1102 612,1102 666,1094 713,1077 760,1060 801,1038 836,1009 870,980 898,945 919,906 940,867 955,824 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 495,961 452,953 418,936 383,919 355,893 334,859 313,824 298,781 289,729 280,677 275,616 275,546 Z" id="glyph50"/><glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,150 316,132 315,113 314,94 313,77 312,61 311,45 310,31 309,19 308,8 307,2 306,2 L 132,2 C 133,8 133,18 134,32 135,47 135,64 136,84 137,104 137,126 138,150 138,174 138,199 138,225 L 138,1484 318,1484 318,1061 C 318,1041 318,1022 318,1004 317,985 317,969 316,955 315,938 315,923 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,615 859,679 850,732 841,785 826,829 805,864 784,898 758,923 726,939 694,955 655,963 609,963 562,963 520,955 484,940 447,925 417,900 393,866 368,832 350,787 337,732 324,677 318,609 318,529 318,452 324,387 337,334 350,281 368,239 393,206 417,173 447,149 483,135 519,120 560,113 607,113 651,113 689,121 721,136 753,151 780,176 801,210 822,244 838,288 849,343 859,397 864,463 864,540 Z" id="glyph52"/><glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,124 87,203 87,303 87,375 101,434 128,480 155,526 190,562 234,588 277,614 327,632 383,642 439,652 496,657 554,657 L 797,657 797,717 C 797,762 792,800 783,832 774,863 759,889 740,908 721,928 697,942 668,951 639,960 604,965 565,965 530,965 499,963 471,958 443,953 419,944 398,931 377,918 361,900 348,878 335,855 327,827 323,793 L 135,810 C 142,853 154,892 173,928 192,963 218,994 253,1020 287,1046 330,1066 382,1081 433,1095 496,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1090,111 1100,112 1110,113 1120,114 1130,116 1139,118 L 1139,6 C 1116,1 1094,-3 1072,-6 1049,-9 1025,-10 1000,-10 966,-10 937,-5 913,4 888,13 868,26 853,45 838,63 826,86 818,113 810,140 805,171 803,207 L 797,207 C 778,172 757,141 734,113 711,85 684,61 653,42 622,22 588,7 549,-4 510,-15 465,-20 414,-20 Z M 455,115 C 512,115 563,125 606,146 649,167 684,194 713,226 741,259 762,294 776,332 790,371 797,408 797,443 L 797,531 600,531 C 556,531 514,528 475,522 435,517 400,506 370,489 340,472 316,449 299,418 281,388 272,349 272,300 272,241 288,195 320,163 351,131 396,115 455,115 Z" id="glyph54"/><glyph unicode="_" horiz-adv-x="1191" d="M -31,-407 L -31,-277 1162,-277 1162,-407 -31,-407 Z" id="glyph56"/><glyph unicode="X" horiz-adv-x="1324" d="M 1112,0 L 689,616 257,0 46,0 582,732 87,1409 298,1409 690,856 1071,1409 1282,1409 800,739 1323,0 1112,0 Z" id="glyph58"/><glyph unicode="T" horiz-adv-x="1139" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z" id="glyph60"/><glyph unicode="S" horiz-adv-x="1139" d="M 1272,389 C 1272,330 1261,275 1238,225 1215,175 1179,132 1131,96 1083,59 1023,31 950,11 877,-10 790,-20 690,-20 515,-20 378,11 280,72 182,133 120,222 93,338 L 278,375 C 287,338 302,305 321,275 340,245 367,219 400,198 433,176 473,159 522,147 571,135 629,129 697,129 754,129 806,134 853,144 900,153 941,168 975,188 1009,208 1036,234 1055,266 1074,297 1083,335 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 613,659 573,668 534,679 494,689 456,701 420,716 383,730 349,747 317,766 285,785 257,809 234,836 211,863 192,894 179,930 166,965 159,1006 159,1053 159,1120 173,1177 200,1225 227,1272 264,1311 312,1342 360,1373 417,1395 482,1409 547,1423 618,1430 694,1430 781,1430 856,1423 918,1410 980,1396 1032,1375 1075,1348 1118,1321 1152,1287 1178,1247 1203,1206 1224,1159 1239,1106 L 1051,1073 C 1042,1107 1028,1137 1011,1164 993,1191 970,1213 941,1231 912,1249 878,1263 837,1272 796,1281 747,1286 692,1286 627,1286 572,1280 528,1269 483,1257 448,1241 421,1221 394,1201 374,1178 363,1151 351,1124 345,1094 345,1063 345,1021 356,987 377,960 398,933 426,910 462,892 498,874 540,859 587,847 634,835 685,823 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z" id="glyph62"/><glyph unicode="R" horiz-adv-x="1218" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 911,1409 982,1400 1044,1382 1105,1363 1157,1337 1199,1302 1241,1267 1273,1225 1295,1175 1317,1125 1328,1069 1328,1006 1328,961 1322,917 1309,874 1296,831 1275,791 1247,755 1219,719 1183,688 1140,662 1097,636 1045,618 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1047 1129,1084 1114,1115 1099,1146 1078,1173 1050,1194 1022,1215 988,1230 948,1241 908,1251 863,1256 812,1256 L 359,1256 359,736 820,736 C 875,736 922,743 962,757 1002,770 1035,789 1061,813 1086,837 1105,865 1118,898 1130,931 1136,966 1136,1004 Z" id="glyph64"/><glyph unicode="Q" horiz-adv-x="1377" d="M 1495,711 C 1495,612 1482,521 1457,439 1431,356 1394,284 1346,222 1297,160 1238,110 1168,71 1097,32 1017,6 928,-6 942,-49 958,-85 976,-115 993,-145 1013,-169 1036,-189 1059,-207 1084,-221 1112,-231 1139,-239 1170,-244 1204,-244 1223,-244 1243,-243 1264,-240 1285,-237 1304,-234 1319,-231 L 1319,-365 C 1294,-371 1266,-376 1236,-381 1205,-385 1174,-387 1141,-387 1084,-387 1034,-378 991,-361 948,-344 911,-319 879,-287 846,-255 818,-216 795,-170 772,-123 751,-71 733,-12 628,-12 535,7 456,46 376,84 310,136 257,200 204,265 164,340 137,427 110,513 97,607 97,709 97,819 112,919 143,1008 174,1097 219,1172 278,1235 337,1297 411,1346 498,1379 585,1413 684,1430 797,1430 909,1430 1009,1413 1096,1379 1183,1345 1256,1297 1315,1234 1374,1171 1418,1096 1449,1007 1480,918 1495,820 1495,711 Z M 1300,711 C 1300,796 1289,873 1268,942 1246,1011 1214,1071 1172,1120 1129,1169 1077,1207 1014,1234 951,1261 879,1274 797,1274 713,1274 639,1261 576,1234 513,1207 460,1169 418,1120 375,1071 344,1011 323,942 302,873 291,796 291,711 291,626 302,549 324,479 345,408 377,348 420,297 462,246 515,206 578,178 641,149 713,135 795,135 883,135 959,149 1023,178 1086,207 1139,247 1180,298 1221,349 1251,409 1271,480 1290,551 1300,628 1300,711 Z" id="glyph66"/><glyph unicode="P" horiz-adv-x="1086" d="M 1258,985 C 1258,924 1248,867 1228,814 1207,761 1177,715 1137,676 1096,637 1046,606 985,583 924,560 854,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 844,1409 917,1399 979,1379 1041,1358 1093,1330 1134,1293 1175,1256 1206,1211 1227,1159 1248,1106 1258,1048 1258,985 Z M 1066,983 C 1066,1072 1039,1140 984,1187 929,1233 847,1256 738,1256 L 359,1256 359,700 746,700 C 856,700 937,724 989,773 1040,822 1066,892 1066,983 Z" id="glyph68"/><glyph unicode="O" horiz-adv-x="1377" d="M 1495,711 C 1495,601 1479,501 1448,411 1416,321 1370,244 1310,180 1250,116 1177,67 1090,32 1003,-3 905,-20 795,-20 679,-20 577,-2 490,35 403,71 330,122 272,187 214,252 170,329 141,418 112,507 97,605 97,711 97,821 112,920 143,1009 174,1098 219,1173 278,1236 337,1298 411,1346 498,1380 585,1413 684,1430 797,1430 909,1430 1009,1413 1096,1379 1183,1345 1256,1297 1315,1234 1374,1171 1418,1096 1449,1007 1480,918 1495,820 1495,711 Z M 1300,711 C 1300,796 1289,873 1268,942 1246,1011 1214,1071 1172,1120 1129,1169 1077,1207 1014,1234 951,1261 879,1274 797,1274 713,1274 639,1261 576,1234 513,1207 460,1169 418,1120 375,1071 344,1011 323,942 302,873 291,796 291,711 291,626 302,549 324,479 345,408 377,348 420,297 462,246 515,206 578,178 641,149 713,135 795,135 883,135 959,149 1023,178 1086,207 1139,247 1180,298 1221,349 1251,409 1271,480 1290,551 1300,628 1300,711 Z" id="glyph70"/><glyph unicode="M" horiz-adv-x="1324" d="M 1366,0 L 1366,940 C 1366,974 1366,1009 1367,1044 1368,1079 1369,1112 1370,1141 1371,1175 1373,1208 1375,1240 1366,1206 1356,1172 1346,1139 1337,1110 1328,1080 1318,1048 1307,1015 1297,986 1287,960 L 923,0 789,0 420,960 C 416,970 412,982 408,995 403,1008 399,1023 394,1038 389,1053 384,1068 379,1084 374,1099 369,1115 364,1130 353,1165 342,1202 331,1240 332,1203 333,1166 334,1129 335,1098 336,1065 337,1031 338,996 338,966 338,940 L 338,0 168,0 168,1409 419,1409 794,432 C 799,419 804,402 811,381 818,360 824,338 830,316 836,294 842,273 847,254 852,234 855,219 857,208 859,219 863,234 868,254 873,274 880,295 887,317 894,339 900,360 907,381 914,402 920,419 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z" id="glyph72"/><glyph unicode="L" horiz-adv-x="900" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z" id="glyph74"/><glyph unicode="K" horiz-adv-x="1191" d="M 1106,0 L 543,680 359,540 359,0 168,0 168,1409 359,1409 359,703 1038,1409 1263,1409 663,797 1343,0 1106,0 Z" id="glyph76"/><glyph unicode="I" horiz-adv-x="186" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z" id="glyph78"/><glyph unicode="E" horiz-adv-x="1112" d="M 168,0 L 168,1409 1237,1409 1237,1253 359,1253 359,801 1177,801 1177,647 359,647 359,156 1278,156 1278,0 168,0 Z" id="glyph80"/><glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,602 1363,498 1328,409 1293,319 1244,244 1183,184 1122,123 1049,78 966,47 882,16 792,0 695,0 L 168,0 168,1409 634,1409 C 743,1409 843,1396 935,1369 1026,1342 1105,1300 1171,1244 1237,1187 1289,1116 1326,1029 1363,942 1381,839 1381,719 Z M 1189,719 C 1189,814 1175,896 1148,964 1121,1031 1082,1087 1033,1130 984,1173 925,1205 856,1226 787,1246 712,1256 630,1256 L 359,1256 359,153 673,153 C 747,153 816,165 879,189 942,213 996,249 1042,296 1088,343 1124,402 1150,473 1176,544 1189,626 1189,719 Z" id="glyph82"/><glyph unicode="C" horiz-adv-x="1297" d="M 792,1274 C 712,1274 641,1261 580,1234 518,1207 466,1169 425,1120 383,1071 351,1011 330,942 309,873 298,796 298,711 298,626 310,549 333,479 356,408 389,348 432,297 475,246 527,207 590,179 652,151 722,137 800,137 855,137 905,144 950,159 995,173 1035,193 1072,219 1108,245 1140,276 1169,312 1198,347 1223,387 1245,430 L 1401,352 C 1376,299 1344,250 1307,205 1270,160 1226,120 1176,87 1125,54 1068,28 1005,9 941,-10 870,-20 791,-20 677,-20 577,-2 492,35 406,71 334,122 277,187 219,252 176,329 147,418 118,507 104,605 104,711 104,821 119,920 150,1009 180,1098 224,1173 283,1236 341,1298 413,1346 498,1380 583,1413 681,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1194,1054 1176,1086 1153,1117 1130,1147 1102,1174 1068,1197 1034,1220 994,1239 949,1253 903,1267 851,1274 792,1274 Z" id="glyph84"/><glyph unicode="B" horiz-adv-x="1086" d="M 1258,397 C 1258,326 1244,265 1216,215 1188,164 1150,123 1103,92 1056,60 1001,37 938,22 875,7 809,0 740,0 L 168,0 168,1409 680,1409 C 758,1409 828,1403 889,1390 950,1377 1002,1356 1045,1328 1088,1300 1120,1265 1143,1222 1165,1179 1176,1127 1176,1067 1176,1028 1171,991 1160,956 1149,921 1132,890 1110,862 1087,833 1059,809 1026,789 992,768 953,753 908,743 965,736 1015,723 1059,704 1102,685 1139,660 1168,630 1197,600 1220,565 1235,526 1250,486 1258,443 1258,397 Z M 984,1044 C 984,1120 958,1174 906,1207 854,1240 779,1256 680,1256 L 359,1256 359,810 680,810 C 736,810 783,816 822,827 861,838 892,853 916,874 940,894 957,918 968,947 979,976 984,1008 984,1044 Z M 1065,412 C 1065,457 1057,495 1041,526 1024,557 1001,583 970,603 939,623 903,638 860,647 817,656 768,661 715,661 L 359,661 359,153 730,153 C 779,153 824,157 865,165 906,173 941,187 971,207 1000,227 1023,254 1040,287 1057,320 1065,362 1065,412 Z" id="glyph86"/><glyph unicode="A" horiz-adv-x="1350" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 768,1026 C 757,1053 747,1080 738,1107 728,1134 719,1159 712,1182 705,1204 699,1223 694,1238 689,1253 686,1262 685,1265 684,1262 681,1252 676,1237 671,1222 665,1203 658,1180 650,1157 641,1132 632,1105 622,1078 612,1051 602,1024 L 422,561 949,561 768,1026 Z" id="glyph88"/><glyph unicode=" " horiz-adv-x="556" id="glyph90"/></font></defs><defs class="TextShapeIndex" id="defs97"><g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46 id47 id48 id49 id50 id51 id52 id53 id54 id55 id56 id57 id58 id59 id60 id61 id62 id63 id64 id65" id="g95"/></defs><defs class="EmbeddedBulletChars" id="defs129"><g id="bullet-char-template(57356)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 580,1141 1163,571 580,0 -4,571 Z" id="path99" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(57354)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 8,1128 H 1137 V 0 H 8 Z" id="path102" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10146)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 174,0 602,739 174,1481 1456,739 Z M 1358,739 309,1346 659,739 Z" id="path105" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10132)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 2015,739 1276,0 H 717 l 543,543 H 174 v 393 h 1086 l -543,545 h 557 z" id="path108" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10007)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 0,-2 c -7,16 -16,29 -25,39 l 381,530 c -94,256 -141,385 -141,387 0,25 13,38 40,38 9,0 21,-2 34,-5 21,4 42,12 65,25 l 27,-13 111,-251 280,301 64,-25 24,25 c 21,-10 41,-24 62,-43 C 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 c 0,-27 -21,-55 -63,-84 l 16,-20 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 c -22,-34 -53,-51 -92,-51 -42,0 -63,17 -64,51 -7,9 -10,24 -10,44 0,9 1,19 2,30 z" id="path111" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10004)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 c 0,78 14,145 41,201 34,71 87,106 158,106 53,0 88,-31 106,-94 l 23,-176 c 8,-64 28,-97 59,-98 l 735,706 c 11,11 33,17 66,17 42,0 63,-15 63,-46 V 965 c 0,-36 -10,-64 -30,-84 L 442,47 C 390,-6 338,-33 285,-33 Z" id="path114" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(9679)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 c 0,181 53,324 160,431 106,107 249,161 430,161 179,0 323,-54 432,-161 108,-107 162,-251 162,-431 0,-180 -54,-324 -162,-431 C 1136,54 992,0 813,0 Z" id="path117" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(8226)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 346,457 c -73,0 -137,26 -191,78 -54,51 -81,114 -81,188 0,73 27,136 81,188 54,52 118,78 191,78 73,0 134,-26 185,-79 51,-51 77,-114 77,-187 0,-75 -25,-137 -76,-188 -50,-52 -112,-78 -186,-78 z" id="path120" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(8211)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M -4,459 H 1135 V 606 H -4 Z" id="path123" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(61548)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 173,740 c 0,163 58,303 173,419 116,115 255,173 419,173 163,0 302,-58 418,-173 116,-116 174,-256 174,-419 0,-163 -58,-303 -174,-418 C 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z" id="path126" inkscape:connector-curvature="0"/></g></defs><defs class="TextEmbeddedBitmaps" id="defs131"/><g id="g136" transform="translate(-4840.889,-3040)"><g id="id2" class="Master_Slide"><g id="bg-id2" class="Background"/><g id="bo-id2" class="BackgroundObjects"/></g></g><g class="SlideGroup" id="g1187" transform="translate(-4840.889,-3040)"><g id="g1185"><g id="container-id1"><g id="id1" class="Slide" clip-path="url(#presentation_clip_path)"><g class="Page" id="g1181"><g class="com.sun.star.drawing.CustomShape" id="g157"><g id="id3"><rect class="BoundingBox" x="7530" y="7229" width="3561" height="1654" id="rect138" style="fill:none;stroke:none"/><path d="m 7806,7230 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7505 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path140" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 7806,7230 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7505 c 0,-138 -138,-275 -276,-275 z" id="path142" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 7531,7230 Z" id="path144" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11089,8881 Z" id="path146" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text154"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan152" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="7773" y="8276" id="tspan150"><tspan id="tspan148" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g178"><g id="id4"><rect class="BoundingBox" x="7785" y="7611" width="3561" height="1654" id="rect159" style="fill:none;stroke:none"/><path d="m 8061,7612 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7887 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path161" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8061,7612 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7887 c 0,-138 -138,-275 -276,-275 z" id="path163" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 7786,7612 Z" id="path165" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11344,9263 Z" id="path167" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text175"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan173" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8028" y="8658" id="tspan171"><tspan id="tspan169" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g199"><g id="id5"><rect class="BoundingBox" x="8037" y="7991" width="3561" height="1654" id="rect180" style="fill:none;stroke:none"/><path d="m 8313,7992 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8267 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path182" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8313,7992 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8267 c 0,-138 -138,-275 -276,-275 z" id="path184" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 8038,7992 Z" id="path186" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11596,9643 Z" id="path188" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text196"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan194" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8773" y="9038" id="tspan192"><tspan id="tspan190" style="fill:#000000;stroke:none">PKTIO </tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g220"><g id="id6"><rect class="BoundingBox" x="8037" y="10151" width="3561" height="1654" id="rect201" style="fill:none;stroke:none"/><path d="m 8313,10152 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path203" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8313,10152 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path205" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 8038,10152 Z" id="path207" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11596,11803 Z" id="path209" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text217"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan215" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8818" y="11198" id="tspan213"><tspan id="tspan211" style="fill:#000000;stroke:none">Buffers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g241"><g id="id7"><rect class="BoundingBox" x="12610" y="8691" width="4322" height="1654" id="rect222" style="fill:none;stroke:none"/><path d="m 12886,8692 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8967 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 4319,1651 z" id="path224" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 12886,8692 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8967 c 0,-138 -138,-275 -276,-275 z" id="path226" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 12611,8692 Z" id="path228" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 16930,10343 Z" id="path230" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text238"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan236" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="12905" y="9738" id="tspan234"><tspan id="tspan232" style="fill:#000000;stroke:none">Classification</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g266"><g id="id8"><rect class="BoundingBox" x="18072" y="7228" width="1909" height="639" id="rect243" style="fill:none;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path245" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path247" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,7229 Z" id="path249" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,7865 Z" id="path251" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path253" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,7229 Z" id="path255" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,7865 Z" id="path257" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path259" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,7229 Z" id="path261" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,7865 Z" id="path263" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g291"><g id="id9"><rect class="BoundingBox" x="18072" y="8245" width="1909" height="639" id="rect268" style="fill:none;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path270" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path272" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,8246 Z" id="path274" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,8882 Z" id="path276" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path278" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,8246 Z" id="path280" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,8882 Z" id="path282" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path284" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,8246 Z" id="path286" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,8882 Z" id="path288" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g316"><g id="id10"><rect class="BoundingBox" x="18072" y="9262" width="1909" height="639" id="rect293" style="fill:none;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path295" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path297" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,9263 Z" id="path299" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,9899 Z" id="path301" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path303" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,9263 Z" id="path305" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,9899 Z" id="path307" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path309" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,9263 Z" id="path311" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,9899 Z" id="path313" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g341"><g id="id11"><rect class="BoundingBox" x="18072" y="10404" width="1909" height="639" id="rect318" style="fill:none;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path320" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path322" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,10405 Z" id="path324" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,11041 Z" id="path326" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path328" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,10405 Z" id="path330" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,11041 Z" id="path332" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path334" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,10405 Z" id="path336" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,11041 Z" id="path338" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g350"><g id="id12"><rect class="BoundingBox" x="11595" y="7398" width="6479" height="1421" id="rect343" style="fill:none;stroke:none"/><path d="m 11596,8817 c 1822,0 -1285,-1218 6005,-1268" id="path345" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,7547 -451,-148 2,300 z" id="path347" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g359"><g id="id13"><rect class="BoundingBox" x="16928" y="8564" width="1146" height="955" id="rect352" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 474,-567 747,-827" id="path354" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,8564 -474,4 97,284 z" id="path356" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g368"><g id="id14"><rect class="BoundingBox" x="16928" y="9418" width="1146" height="302" id="rect361" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 491,34 710,53" id="path363" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,9581 -446,-163 -8,300 z" id="path365" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g377"><g id="id15"><rect class="BoundingBox" x="16928" y="9516" width="1146" height="1208" id="rect370" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 469,738 759,1060" id="path372" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,10723 -361,-308 -112,278 z" id="path374" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g386"><g id="id16"><rect class="BoundingBox" x="11595" y="8816" width="1017" height="702" id="rect379" style="fill:none;stroke:none"/><path d="m 11596,8817 c 762,0 439,365 621,568" id="path381" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 12611,9517 -372,-294 -102,282 z" id="path383" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g407"><g id="id17"><rect class="BoundingBox" x="21501" y="8246" width="4322" height="1654" id="rect388" style="fill:none;stroke:none"/><path d="m 21777,8247 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8522 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 4319,1651 z" id="path390" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 21777,8247 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8522 c 0,-138 -138,-275 -276,-275 z" id="path392" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 21502,8247 Z" id="path394" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 25821,9898 Z" id="path396" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text404"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan402" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="22237" y="9293" id="tspan400"><tspan id="tspan398" style="fill:#000000;stroke:none">Scheduler</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g416"><g id="id18"><rect class="BoundingBox" x="19978" y="7546" width="6859" height="1257" id="rect409" style="fill:none;stroke:none"/><path d="m 19979,7547 c 8298,0 5408,934 6428,1108" id="path411" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 26836,8683 -439,-181 -20,300 z" id="path413" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g425"><g id="id19"><rect class="BoundingBox" x="19978" y="8563" width="1525" height="617" id="rect418" style="fill:none;stroke:none"/><path d="m 19979,8564 c 1143,0 584,351 1090,470" id="path420" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -434,-192 -28,299 z" id="path422" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g434"><g id="id20"><rect class="BoundingBox" x="19978" y="8965" width="1525" height="618" id="rect427" style="fill:none;stroke:none"/><path d="m 19979,9581 c 1143,0 584,-352 1090,-470" id="path429" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -462,-107 28,299 z" id="path431" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g443"><g id="id21"><rect class="BoundingBox" x="19978" y="9047" width="1525" height="1678" id="rect436" style="fill:none;stroke:none"/><path d="m 19979,10723 c 1143,0 572,-1180 1116,-1545" id="path438" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -474,-24 81,289 z" id="path440" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.TextShape" id="g456"><g id="id22"><rect class="BoundingBox" x="17808" y="5933" width="3569" height="1214" id="rect445" style="fill:none;stroke:none"/><text class="TextShape" id="text453"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan451" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="18058" y="6630" id="tspan449"><tspan id="tspan447" style="fill:#000000;stroke:none">Queue</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g477"><g id="id23"><rect class="BoundingBox" x="26834" y="7857" width="3561" height="1654" id="rect458" style="fill:none;stroke:none"/><path d="m 27110,7858 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8133 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path460" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27110,7858 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8133 c 0,-138 -138,-275 -276,-275 z" id="path462" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 26835,7858 Z" id="path464" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30393,9509 Z" id="path466" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text474"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan472" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27077" y="8904" id="tspan470"><tspan id="tspan468" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g498"><g id="id24"><rect class="BoundingBox" x="27088" y="8237" width="3561" height="1654" id="rect479" style="fill:none;stroke:none"/><path d="m 27364,8238 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8513 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path481" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27364,8238 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8513 c 0,-138 -138,-275 -276,-275 z" id="path483" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27089,8238 Z" id="path485" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30647,9889 Z" id="path487" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text495"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan493" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27331" y="9284" id="tspan491"><tspan id="tspan489" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g519"><g id="id25"><rect class="BoundingBox" x="27343" y="8619" width="3561" height="1654" id="rect500" style="fill:none;stroke:none"/><path d="m 27619,8620 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8895 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path502" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27619,8620 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8895 c 0,-138 -138,-275 -276,-275 z" id="path504" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27344,8620 Z" id="path506" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30902,10271 Z" id="path508" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text516"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan514" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27522" y="9666" id="tspan512"><tspan id="tspan510" style="fill:#000000;stroke:none">odp_thread</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g528"><g id="id26"><rect class="BoundingBox" x="25819" y="8914" width="1272" height="302" id="rect521" style="fill:none;stroke:none"/><path d="m 25820,9072 c 952,0 521,-5 836,-8" id="path523" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 27090,9063 -450,-149 v 300 z" id="path525" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g537"><g id="id27"><rect class="BoundingBox" x="25819" y="9071" width="1527" height="494" id="rect530" style="fill:none;stroke:none"/><path d="m 25820,9072 c 1144,0 585,257 1090,345" id="path532" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 27345,9445 -438,-181 -21,299 z" id="path534" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g562"><g id="id28"><rect class="BoundingBox" x="18072" y="7228" width="1909" height="639" id="rect539" style="fill:none;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path541" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path543" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,7229 Z" id="path545" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,7865 Z" id="path547" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path549" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,7229 Z" id="path551" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,7865 Z" id="path553" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path555" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,7229 Z" id="path557" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,7865 Z" id="path559" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g587"><g id="id29"><rect class="BoundingBox" x="18072" y="8245" width="1909" height="639" id="rect564" style="fill:none;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path566" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path568" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,8246 Z" id="path570" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,8882 Z" id="path572" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path574" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,8246 Z" id="path576" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,8882 Z" id="path578" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path580" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,8246 Z" id="path582" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,8882 Z" id="path584" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g612"><g id="id30"><rect class="BoundingBox" x="18072" y="9262" width="1909" height="639" id="rect589" style="fill:none;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path591" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path593" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,9263 Z" id="path595" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,9899 Z" id="path597" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path599" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,9263 Z" id="path601" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,9899 Z" id="path603" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path605" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,9263 Z" id="path607" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,9899 Z" id="path609" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g637"><g id="id31"><rect class="BoundingBox" x="18072" y="10404" width="1909" height="639" id="rect614" style="fill:none;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path616" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path618" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,10405 Z" id="path620" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,11041 Z" id="path622" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path624" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,10405 Z" id="path626" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,11041 Z" id="path628" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path630" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,10405 Z" id="path632" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,11041 Z" id="path634" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g662"><g id="id32"><rect class="BoundingBox" x="35089" y="7471" width="1909" height="639" id="rect639" style="fill:none;stroke:none"/><path d="m 35090,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path641" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path643" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,7472 Z" id="path645" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,8108 Z" id="path647" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path649" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,7472 Z" id="path651" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,8108 Z" id="path653" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path655" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,7472 Z" id="path657" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,8108 Z" id="path659" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g687"><g id="id33"><rect class="BoundingBox" x="35089" y="8787" width="1909" height="639" id="rect664" style="fill:none;stroke:none"/><path d="m 35090,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path666" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path668" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,8788 Z" id="path670" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,9424 Z" id="path672" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path674" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,8788 Z" id="path676" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,9424 Z" id="path678" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path680" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,8788 Z" id="path682" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,9424 Z" id="path684" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g712"><g id="id34"><rect class="BoundingBox" x="35089" y="9930" width="1909" height="639" id="rect689" style="fill:none;stroke:none"/><path d="m 35090,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path691" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path693" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,9931 Z" id="path695" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,10567 Z" id="path697" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path699" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,9931 Z" id="path701" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,10567 Z" id="path703" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path705" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,9931 Z" id="path707" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,10567 Z" id="path709" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g721"><g id="id35"><rect class="BoundingBox" x="30901" y="9444" width="4190" height="950" id="rect714" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1257,720 3726,797" id="path716" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,10249 -448,-157 -4,300 z" id="path718" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g730"><g id="id36"><rect class="BoundingBox" x="30901" y="8958" width="4190" height="489" id="rect723" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1258,-303 3724,-336" id="path725" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,9106 -451,-147 2,300 z" id="path727" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g739"><g id="id37"><rect class="BoundingBox" x="30901" y="7653" width="4190" height="1794" id="rect732" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1254,-1485 3732,-1642" id="path734" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,7790 -454,-136 9,300 z" id="path736" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g760"><g id="id38"><rect class="BoundingBox" x="38898" y="8872" width="4577" height="1654" id="rect741" style="fill:none;stroke:none"/><path d="m 39174,8873 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 4023 c 137,0 275,-138 275,-276 V 9148 c 0,-138 -138,-275 -275,-275 z m -275,0 z m 4574,1651 z" id="path743" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 39174,8873 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 4023 c 137,0 275,-138 275,-276 V 9148 c 0,-138 -138,-275 -275,-275 z" id="path745" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 38899,8873 Z" id="path747" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 43473,10524 Z" id="path749" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text757"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan755" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="39511" y="9919" id="tspan753"><tspan id="tspan751" style="fill:#000000;stroke:none">Traffic Mngr</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g781"><g id="id39"><rect class="BoundingBox" x="30771" y="12437" width="3561" height="1654" id="rect762" style="fill:none;stroke:none"/><path d="m 31047,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path764" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path766" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,12438 Z" id="path768" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,14089 Z" id="path770" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text778"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan776" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31581" y="13484" id="tspan774"><tspan id="tspan772" style="fill:#000000;stroke:none">Packet</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g802"><g id="id40"><rect class="BoundingBox" x="27214" y="17390" width="3561" height="1654" id="rect783" style="fill:none;stroke:none"/><path d="m 27490,17391 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path785" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,17391 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path787" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,17391 Z" id="path789" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,19042 Z" id="path791" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text799"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan797" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28200" y="18437" id="tspan795"><tspan id="tspan793" style="fill:#000000;stroke:none">IPsec</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g823"><g id="id41"><rect class="BoundingBox" x="30771" y="14087" width="3561" height="1654" id="rect804" style="fill:none;stroke:none"/><path d="m 31047,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path806" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path808" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,14088 Z" id="path810" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,15739 Z" id="path812" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text820"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan818" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31459" y="15134" id="tspan816"><tspan id="tspan814" style="fill:#000000;stroke:none">Shmem</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g844"><g id="id42"><rect class="BoundingBox" x="27214" y="15738" width="3561" height="1654" id="rect825" style="fill:none;stroke:none"/><path d="m 27490,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path827" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path829" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,15739 Z" id="path831" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,17390 Z" id="path833" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text841"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan839" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28289" y="16785" id="tspan837"><tspan id="tspan835" style="fill:#000000;stroke:none">Sync</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g865"><g id="id43"><rect class="BoundingBox" x="30771" y="15738" width="3561" height="1654" id="rect846" style="fill:none;stroke:none"/><path d="m 31047,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path848" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path850" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,15739 Z" id="path852" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,17390 Z" id="path854" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text862"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan860" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31552" y="16785" id="tspan858"><tspan id="tspan856" style="fill:#000000;stroke:none">Buffers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g874"><g id="id44"><rect class="BoundingBox" x="36995" y="9105" width="1906" height="716" id="rect867" style="fill:none;stroke:none"/><path d="m 36996,9106 c 1428,0 678,452 1468,566" id="path869" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 38900,9698 -440,-178 -19,299 z" id="path871" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g883"><g id="id45"><rect class="BoundingBox" x="36995" y="9574" width="1906" height="677" id="rect876" style="fill:none;stroke:none"/><path d="m 36996,10249 c 1428,0 678,-421 1468,-527" id="path878" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 38900,9698 -458,-123 18,299 z" id="path880" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g904"><g id="id46"><rect class="BoundingBox" x="45376" y="7811" width="3561" height="1654" id="rect885" style="fill:none;stroke:none"/><path d="m 45652,7812 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8087 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path887" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 45652,7812 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8087 c 0,-138 -138,-275 -276,-275 z" id="path889" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45377,7812 Z" id="path891" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 48935,9463 Z" id="path893" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text901"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan899" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="45619" y="8858" id="tspan897"><tspan id="tspan895" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g925"><g id="id47"><rect class="BoundingBox" x="45631" y="8191" width="3561" height="1654" id="rect906" style="fill:none;stroke:none"/><path d="m 45907,8192 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8467 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path908" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 45907,8192 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8467 c 0,-138 -138,-275 -276,-275 z" id="path910" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45632,8192 Z" id="path912" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 49190,9843 Z" id="path914" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text922"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan920" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="45874" y="9238" id="tspan918"><tspan id="tspan916" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g946"><g id="id48"><rect class="BoundingBox" x="45883" y="8572" width="3561" height="1654" id="rect927" style="fill:none;stroke:none"/><path d="m 46159,8573 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8848 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path929" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 46159,8573 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8848 c 0,-138 -138,-275 -276,-275 z" id="path931" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45884,8573 Z" id="path933" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 49442,10224 Z" id="path935" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text943"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan941" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="46708" y="9619" id="tspan939"><tspan id="tspan937" style="fill:#000000;stroke:none">PKTIO</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g955"><g id="id49"><rect class="BoundingBox" x="43472" y="9256" width="2414" height="444" id="rect948" style="fill:none;stroke:none"/><path d="m 43473,9698 c 1809,0 802,-246 1978,-293" id="path950" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 45885,9398 -453,-142 6,300 z" id="path952" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g976"><g id="id50"><rect class="BoundingBox" x="27214" y="12437" width="3561" height="1654" id="rect957" style="fill:none;stroke:none"/><path d="m 27490,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path959" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path961" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,12438 Z" id="path963" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,14089 Z" id="path965" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text973"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan971" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28037" y="13484" id="tspan969"><tspan id="tspan967" style="fill:#000000;stroke:none">Timers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g997"><g id="id51"><rect class="BoundingBox" x="27214" y="14087" width="3561" height="1654" id="rect978" style="fill:none;stroke:none"/><path d="m 27490,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path980" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path982" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,14088 Z" id="path984" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,15739 Z" id="path986" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text994"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan992" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28183" y="15134" id="tspan990"><tspan id="tspan988" style="fill:#000000;stroke:none">Event</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g1006"><g id="id52"><rect class="BoundingBox" x="9223" y="3801" width="37935" height="4013" id="rect999" style="fill:none;stroke:none"/><path d="M 47156,7812 C 47156,2457 10447,2740 9336,7029" id="path1001" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 9310,7230 210,-425 -297,-41 z" id="path1003" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.TextShape" id="g1019"><g id="id53"><rect class="BoundingBox" x="26705" y="3040" width="5335" height="1017" id="rect1008" style="fill:none;stroke:none"/><text class="TextShape" id="text1016"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1014" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="26955" y="3737" id="tspan1012"><tspan id="tspan1010" style="fill:#000000;stroke:none">Loopback</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g1040"><g id="id54"><rect class="BoundingBox" x="4854" y="8465" width="1967" height="1066" id="rect1021" style="fill:none;stroke:none"/><path d="m 4855,8731 h 1473 v -265 l 491,531 -491,532 V 9263 H 4855 Z m 0,-265 z m 1964,1063 z" id="path1023" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 4855,8731 h 1473 v -265 l 491,531 -491,532 V 9263 H 4855 Z" id="path1025" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 4855,8466 Z" id="path1027" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 6819,9529 Z" id="path1029" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text1037"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1035" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="5274" y="9218" id="tspan1033"><tspan id="tspan1031" style="fill:#000000;stroke:none">RX</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g1049"><g id="id55"><rect class="BoundingBox" x="36995" y="7789" width="8384" height="994" id="rect1042" style="fill:none;stroke:none"/><path d="m 36996,7790 c 8817,0 4991,769 7903,842" id="path1044" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 45378,8637 -448,-155 -4,300 z" id="path1046" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g1070"><g id="id56"><rect class="BoundingBox" x="50054" y="8465" width="1967" height="1066" id="rect1051" style="fill:none;stroke:none"/><path d="m 50055,8731 h 1473 v -265 l 491,531 -491,532 v -266 h -1473 z m 0,-265 z m 1964,1063 z" id="path1053" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 50055,8731 h 1473 v -265 l 491,531 -491,532 v -266 h -1473 z" id="path1055" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 50055,8466 Z" id="path1057" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 52019,9529 Z" id="path1059" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text1067"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1065" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="50508" y="9218" id="tspan1063"><tspan id="tspan1061" style="fill:#000000;stroke:none">TX</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g1091"><g id="id57"><rect class="BoundingBox" x="30842" y="17383" width="3561" height="1654" id="rect1072" style="fill:none;stroke:none"/><path d="m 31118,17384 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path1074" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31118,17384 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path1076" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30843,17384 Z" id="path1078" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34401,19035 Z" id="path1080" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text1088"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1086" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31688" y="18430" id="tspan1084"><tspan id="tspan1082" style="fill:#000000;stroke:none">Crypto</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g1100"><g id="id58"><rect class="BoundingBox" x="26672" y="10239" width="2453" height="7978" id="rect1093" style="fill:none;stroke:none"/><path d="m 29123,10270 c 0,-96 -1205,-81 -1870,1930 -665,2011 -739,4834 -323,5755" id="path1095" inkscape:connector-curvature="0" style="fill:none;stroke:#0000ff"/><path d="m 27216,18216 -222,-419 -207,217 z" id="path1097" inkscape:connector-curvature="0" style="fill:#0000ff;stroke:none"/></g></g><g class="com.sun.star.drawing.TextShape" id="g1113"><g id="id59"><rect class="BoundingBox" x="21574" y="18877" width="4448" height="1144" id="rect1102" style="fill:none;stroke:none"/><text class="TextShape" id="text1110"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1108" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="21824" y="19669" id="tspan1106"><tspan id="tspan1104" style="fill:#000000;stroke:none">RX Decrypted</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.TextShape" id="g1126"><g id="id60"><rect class="BoundingBox" x="26287" y="10977" width="3822" height="1144" id="rect1115" style="fill:none;stroke:none"/><text class="TextShape" id="text1123"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1121" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="26537" y="11769" id="tspan1119"><tspan id="tspan1117" style="fill:#000000;stroke:none">TX Encrypt</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.TextShape" id="g1139"><g id="id61"><rect class="BoundingBox" x="21925" y="16777" width="3746" height="1144" id="rect1128" style="fill:none;stroke:none"/><text class="TextShape" id="text1136"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1134" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="22175" y="17569" id="tspan1132"><tspan id="tspan1130" style="fill:#000000;stroke:none">RX Decrypt</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.TextShape" id="g1152"><g id="id62"><rect class="BoundingBox" x="33336" y="10978" width="4525" height="1144" id="rect1141" style="fill:none;stroke:none"/><text class="TextShape" id="text1149"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan1147" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="33586" y="11770" id="tspan1145"><tspan id="tspan1143" style="fill:#000000;stroke:none">TX Encrypted</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g1161"><g id="id63"><rect class="BoundingBox" x="11595" y="8816" width="15622" height="9550" id="rect1154" style="fill:none;stroke:none"/><path d="m 11596,8817 c 2775,0 -4933,9276 15215,9398" id="path1156" inkscape:connector-curvature="0" style="fill:none;stroke:#ff3333"/><path d="m 27216,18216 -450,-151 v 300 z" id="path1158" inkscape:connector-curvature="0" style="fill:#ff3333;stroke:none"/></g></g><g class="com.sun.star.drawing.OpenBezierShape" id="g1170"><g id="id64"><rect class="BoundingBox" x="31861" y="9540" width="3208" height="8671" id="rect1163" style="fill:none;stroke:none"/><path d="m 34401,18209 c 876,0 543,-4495 666,-4495 0,-2304 -3205,-1018 -3205,-2572 0,-1576 176,-1044 286,-1182" id="path1165" inkscape:connector-curvature="0" style="fill:none;stroke:#6666ff"/><path d="m 32235,9540 -251,412 297,68 z" id="path1167" inkscape:connector-curvature="0" style="fill:#6666ff;stroke:none"/></g></g><g class="com.sun.star.drawing.OpenBezierShape" id="g1179"><g id="id65"><rect class="BoundingBox" x="11769" y="8874" width="23215" height="11326" id="rect1172" style="fill:none;stroke:none"/><path d="m 11922,9364 c 8,5473 152,2277 152,7144 0,2460 7353,3690 22061,3690 1150,0 1017,-1989 266,-1989" id="path1174" inkscape:connector-curvature="0" style="fill:none;stroke:#ff3333"/><path d="m 11922,8874 -152,458 305,-1 z" id="path1176" inkscape:connector-curvature="0" style="fill:#ff3333;stroke:none"/></g></g></g></g></g></g></g></svg>
diff --git a/doc/images/ipsec-lookaside.svg b/doc/images/ipsec-lookaside.svg
new file mode 100644
index 000000000..0cab3f4d6
--- /dev/null
+++ b/doc/images/ipsec-lookaside.svg
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.2" width="471.92221mm" height="188.64999mm" viewBox="0 0 47192.222 18865" preserveAspectRatio="xMidYMid" xml:space="preserve" id="svg3797" sodipodi:docname="ipsec-lookaside.svg" style="fill-rule:evenodd;stroke-width:28.22200012;stroke-linejoin:round" inkscape:version="0.92.1 r15371"><metadata id="metadata3801"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview3799" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="0.1304858" inkscape:cx="939.7648" inkscape:cy="33.977951" inkscape:window-x="65" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg3797"/><defs class="ClipPathGroup" id="defs2637"><clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"><rect x="0" y="0" width="59411" height="42012" id="rect2631"/></clipPath><clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse"><rect x="59" y="42" width="59293" height="41928" id="rect2634"/></clipPath></defs><defs id="defs2726"><font id="EmbeddedFont_1" horiz-adv-x="2048" horiz-origin-x="0" horiz-origin-y="0" vert-origin-x="45" vert-origin-y="90" vert-adv-y="90"><font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1826" descent="423" id="font-face2639"/><missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z" id="missing-glyph2641"/><glyph unicode="y" horiz-adv-x="1059" d="M 604,1 C 579,-64 553,-123 527,-175 500,-227 471,-272 438,-309 405,-346 369,-374 329,-394 289,-413 243,-423 191,-423 168,-423 147,-423 128,-423 109,-423 88,-420 67,-414 L 67,-279 C 80,-282 94,-284 110,-284 126,-284 140,-284 151,-284 204,-284 253,-264 298,-225 343,-186 383,-124 417,-38 L 434,5 5,1082 197,1082 425,484 C 432,466 440,442 451,412 461,382 471,352 482,322 492,292 501,265 509,241 517,217 522,202 523,196 525,203 530,218 538,240 545,261 554,285 564,312 573,339 583,366 593,393 603,420 611,444 618,464 L 830,1082 1020,1082 604,1 Z" id="glyph2643"/><glyph unicode="v" horiz-adv-x="1059" d="M 613,0 L 400,0 7,1082 199,1082 437,378 C 442,363 447,346 454,325 460,304 466,282 473,259 480,236 486,215 492,194 497,173 502,155 506,141 510,155 515,173 522,194 528,215 534,236 541,258 548,280 555,302 562,323 569,344 575,361 580,376 L 826,1082 1017,1082 613,0 Z" id="glyph2645"/><glyph unicode="u" horiz-adv-x="901" d="M 314,1082 L 314,396 C 314,343 318,299 326,264 333,229 346,200 363,179 380,157 403,142 432,133 460,124 495,119 537,119 580,119 618,127 653,142 687,157 716,178 741,207 765,235 784,270 797,312 810,353 817,401 817,455 L 817,1082 997,1082 997,228 C 997,205 997,181 998,156 998,131 998,107 999,85 1000,62 1000,43 1001,27 1002,11 1002,3 1003,3 L 833,3 C 832,6 832,15 831,30 830,44 830,61 829,79 828,98 827,117 826,136 825,156 825,172 825,185 L 822,185 C 805,154 786,125 765,100 744,75 720,53 693,36 666,18 634,4 599,-6 564,-15 523,-20 476,-20 416,-20 364,-13 321,2 278,17 242,39 214,70 186,101 166,140 153,188 140,236 133,294 133,361 L 133,1082 314,1082 Z" id="glyph2647"/><glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 527,1 499,-5 471,-10 442,-14 409,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 467,127 484,128 501,131 517,134 535,137 554,141 L 554,8 Z" id="glyph2649"/><glyph unicode="s" horiz-adv-x="927" d="M 950,299 C 950,248 940,203 921,164 901,124 872,91 835,64 798,37 752,16 698,2 643,-13 581,-20 511,-20 448,-20 392,-15 342,-6 291,4 247,20 209,41 171,62 139,91 114,126 88,161 69,203 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 550,117 585,120 618,125 650,130 678,140 701,153 724,166 743,183 756,205 769,226 775,253 775,285 775,318 767,345 752,366 737,387 715,404 688,418 661,432 628,444 589,455 550,465 507,476 460,489 417,500 374,513 331,527 288,541 250,560 216,583 181,606 153,634 132,668 111,702 100,745 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 763,842 752,866 736,885 720,904 701,919 678,931 655,942 630,951 602,956 573,961 544,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,785 282,761 297,742 311,723 331,707 357,694 382,681 413,669 449,660 485,650 525,640 568,629 597,622 626,614 656,606 686,597 715,587 744,576 772,564 799,550 824,535 849,519 870,500 889,478 908,456 923,430 934,401 945,372 950,338 950,299 Z" id="glyph2651"/><glyph unicode="r" horiz-adv-x="556" d="M 142,0 L 142,830 C 142,853 142,876 142,900 141,923 141,946 140,968 139,990 139,1011 138,1030 137,1049 137,1067 136,1082 L 306,1082 C 307,1067 308,1049 309,1030 310,1010 311,990 312,969 313,948 313,929 314,910 314,891 314,874 314,861 L 318,861 C 331,902 344,938 359,969 373,999 390,1024 409,1044 428,1063 451,1078 478,1088 505,1097 537,1102 575,1102 590,1102 604,1101 617,1099 630,1096 641,1094 648,1092 L 648,927 C 636,930 622,933 606,935 590,936 572,937 552,937 511,937 476,928 447,909 418,890 394,865 376,832 357,799 344,759 335,714 326,668 322,618 322,564 L 322,0 142,0 Z" id="glyph2653"/><glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,464 1046,388 1033,319 1020,250 998,190 967,140 936,90 895,51 844,23 793,-6 730,-20 655,-20 578,-20 510,-5 452,24 394,53 350,101 319,168 L 314,168 C 315,167 315,161 316,150 316,139 316,126 317,110 317,94 317,76 318,57 318,37 318,17 318,-2 L 318,-425 138,-425 138,864 C 138,891 138,916 138,940 137,964 137,986 136,1005 135,1025 135,1042 134,1056 133,1070 133,1077 132,1077 L 306,1077 C 307,1075 308,1068 309,1057 310,1045 311,1031 312,1014 313,998 314,980 315,961 316,943 316,925 316,908 L 320,908 C 337,943 356,972 377,997 398,1021 423,1041 450,1057 477,1072 508,1084 542,1091 575,1098 613,1101 655,1101 730,1101 793,1088 844,1061 895,1034 936,997 967,949 998,900 1020,842 1033,774 1046,705 1053,629 1053,546 Z M 864,542 C 864,609 860,668 852,720 844,772 830,816 811,852 791,888 765,915 732,934 699,953 658,962 609,962 569,962 531,956 496,945 461,934 430,912 404,880 377,848 356,804 341,748 326,691 318,618 318,528 318,451 324,387 337,334 350,281 368,238 393,205 417,172 447,149 483,135 519,120 560,113 607,113 657,113 699,123 732,142 765,161 791,189 811,226 830,263 844,308 852,361 860,414 864,474 864,542 Z" id="glyph2655"/><glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 490,-20 422,-9 363,14 304,37 254,71 213,118 172,165 140,223 119,294 97,364 86,447 86,542 86,915 248,1102 571,1102 655,1102 728,1090 789,1067 850,1044 900,1009 939,962 978,915 1006,857 1025,787 1044,717 1053,635 1053,542 Z M 864,542 C 864,626 858,695 845,750 832,805 813,848 788,881 763,914 732,937 696,950 660,963 619,969 574,969 528,969 487,962 450,949 413,935 381,912 355,879 329,846 309,802 296,747 282,692 275,624 275,542 275,458 282,389 297,334 312,279 332,235 358,202 383,169 414,146 449,133 484,120 522,113 563,113 609,113 651,120 688,133 725,146 757,168 783,201 809,234 829,278 843,333 857,388 864,458 864,542 Z" id="glyph2657"/><glyph unicode="n" horiz-adv-x="900" d="M 825,0 L 825,686 C 825,739 821,783 814,818 806,853 793,882 776,904 759,925 736,941 708,950 679,959 644,963 602,963 559,963 521,956 487,941 452,926 423,904 399,876 374,847 355,812 342,771 329,729 322,681 322,627 L 322,0 142,0 142,853 C 142,876 142,900 142,925 141,950 141,974 140,996 139,1019 139,1038 138,1054 137,1070 137,1078 136,1078 L 306,1078 C 307,1075 307,1066 308,1052 309,1037 310,1021 311,1002 312,984 312,965 313,945 314,926 314,910 314,897 L 317,897 C 334,928 353,957 374,982 395,1007 419,1029 446,1047 473,1064 505,1078 540,1088 575,1097 616,1102 663,1102 723,1102 775,1095 818,1080 861,1065 897,1043 925,1012 953,981 974,942 987,894 1000,845 1006,788 1006,721 L 1006,0 825,0 Z" id="glyph2659"/><glyph unicode="m" horiz-adv-x="1456" d="M 768,0 L 768,686 C 768,739 765,783 758,818 751,853 740,882 725,904 709,925 688,941 663,950 638,959 607,963 570,963 532,963 498,956 467,941 436,926 410,904 389,876 367,847 350,812 339,771 327,729 321,681 321,627 L 321,0 142,0 142,853 C 142,876 142,900 142,925 141,950 141,974 140,996 139,1019 139,1038 138,1054 137,1070 137,1078 136,1078 L 306,1078 C 307,1075 307,1066 308,1052 309,1037 310,1021 311,1002 312,984 312,965 313,945 314,926 314,910 314,897 L 317,897 C 333,928 350,957 369,982 388,1007 410,1029 435,1047 460,1064 488,1078 521,1088 553,1097 590,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 946,928 964,957 984,982 1004,1007 1027,1029 1054,1047 1081,1064 1111,1078 1144,1088 1177,1097 1215,1102 1258,1102 1313,1102 1360,1095 1400,1080 1439,1065 1472,1043 1497,1012 1522,981 1541,942 1553,894 1565,845 1571,788 1571,721 L 1571,0 1393,0 1393,686 C 1393,739 1390,783 1383,818 1376,853 1365,882 1350,904 1334,925 1313,941 1288,950 1263,959 1232,963 1195,963 1157,963 1123,956 1092,942 1061,927 1035,906 1014,878 992,850 975,815 964,773 952,731 946,682 946,627 L 946,0 768,0 Z" id="glyph2661"/><glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z" id="glyph2663"/><glyph unicode="k" horiz-adv-x="927" d="M 816,0 L 450,494 318,385 318,0 138,0 138,1484 318,1484 318,557 793,1082 1004,1082 565,617 1027,0 816,0 Z" id="glyph2665"/><glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z" id="glyph2667"/><glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 337,934 359,965 382,991 405,1016 431,1037 459,1054 487,1071 518,1083 551,1091 584,1098 622,1102 663,1102 732,1102 789,1093 834,1074 878,1055 913,1029 939,996 964,962 982,922 992,875 1001,828 1006,777 1006,721 L 1006,0 825,0 825,686 C 825,732 822,772 817,807 811,842 800,871 784,894 768,917 745,934 716,946 687,957 649,963 602,963 559,963 521,955 487,940 452,925 423,903 399,875 374,847 355,813 342,773 329,733 322,688 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1076 322,1054 321,1032 320,1010 320,990 319,971 318,952 317,937 316,924 315,911 315,902 314,897 L 317,897 Z" id="glyph2669"/><glyph unicode="g" horiz-adv-x="954" d="M 548,-425 C 486,-425 431,-419 383,-406 335,-393 294,-375 260,-352 226,-328 198,-300 177,-267 156,-234 140,-198 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 594,-288 631,-282 664,-271 697,-260 726,-241 749,-217 772,-191 790,-159 803,-119 816,-79 822,-30 822,27 L 822,201 820,201 C 807,174 790,148 771,123 751,98 727,75 699,56 670,37 637,21 600,10 563,-2 520,-8 472,-8 403,-8 345,4 296,27 247,50 207,84 176,130 145,176 122,233 108,302 93,370 86,449 86,539 86,626 93,704 108,773 122,842 145,901 178,950 210,998 252,1035 304,1061 355,1086 418,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,914 825,933 826,953 827,974 828,994 829,1012 830,1031 831,1046 832,1060 833,1073 835,1080 836,1080 L 1007,1080 C 1006,1074 1006,1064 1005,1050 1004,1035 1004,1018 1003,998 1002,978 1002,956 1002,932 1001,907 1001,882 1001,856 L 1001,30 C 1001,-121 964,-234 890,-311 815,-387 701,-425 548,-425 Z M 822,541 C 822,616 814,681 798,735 781,788 760,832 733,866 706,900 676,925 642,941 607,957 572,965 536,965 490,965 451,957 418,941 385,925 357,900 336,866 314,831 298,787 288,734 277,680 272,616 272,541 272,463 277,398 288,345 298,292 314,249 335,216 356,183 383,160 416,146 449,132 488,125 533,125 569,125 604,133 639,148 673,163 704,188 731,221 758,254 780,297 797,350 814,403 822,466 822,541 Z" id="glyph2671"/><glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1243 185,1280 192,1314 199,1347 213,1377 233,1402 252,1427 279,1446 313,1461 347,1475 391,1482 445,1482 466,1482 489,1481 512,1479 535,1477 555,1474 572,1470 L 572,1333 C 561,1335 548,1337 533,1339 518,1340 504,1341 492,1341 465,1341 444,1337 427,1330 410,1323 396,1312 387,1299 377,1285 370,1268 367,1248 363,1228 361,1205 361,1179 L 361,1082 572,1082 572,951 361,951 Z" id="glyph2673"/><glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,446 282,394 294,347 305,299 323,258 348,224 372,189 403,163 441,144 479,125 525,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 1008,206 992,176 972,146 951,115 924,88 890,64 856,39 814,19 763,4 712,-12 650,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,649 100,735 125,806 150,876 185,933 229,977 273,1021 324,1053 383,1073 442,1092 504,1102 571,1102 662,1102 738,1087 799,1058 860,1029 909,988 946,937 983,885 1009,824 1025,754 1040,684 1048,608 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 538,969 507,964 474,955 441,945 410,928 382,903 354,878 330,845 311,803 292,760 281,706 278,641 L 862,641 Z" id="glyph2675"/><glyph unicode="d" horiz-adv-x="954" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 C 823,921 823,931 823,946 822,960 822,975 822,991 821,1006 821,1021 821,1035 821,1049 821,1059 821,1065 L 821,1484 1001,1484 1001,219 C 1001,193 1001,168 1002,143 1002,119 1002,97 1003,77 1004,57 1004,40 1005,26 1006,11 1006,4 1007,4 L 835,4 C 834,11 833,20 832,32 831,44 830,58 829,73 828,89 827,105 826,123 825,140 825,157 825,174 L 821,174 Z M 275,542 C 275,467 280,403 289,350 298,297 313,253 334,219 355,184 381,159 413,143 445,127 484,119 530,119 577,119 619,127 656,142 692,157 722,182 747,217 771,251 789,296 802,351 815,406 821,474 821,554 821,631 815,696 802,749 789,802 771,844 746,877 721,910 691,933 656,948 620,962 579,969 532,969 488,969 450,961 418,946 386,931 359,906 338,872 317,838 301,794 291,740 280,685 275,619 275,542 Z" id="glyph2677"/><glyph unicode="c" horiz-adv-x="875" d="M 275,546 C 275,484 280,427 289,375 298,323 313,278 334,241 355,203 384,174 419,153 454,132 497,122 548,122 612,122 666,139 709,173 752,206 778,258 788,328 L 970,328 C 964,283 951,239 931,197 911,155 884,118 850,86 815,54 773,28 724,9 675,-10 618,-20 553,-20 468,-20 396,-6 337,23 278,52 230,91 193,142 156,192 129,251 112,320 95,388 87,462 87,542 87,615 93,679 105,735 117,790 134,839 156,881 177,922 203,957 232,986 261,1014 293,1037 328,1054 362,1071 398,1083 436,1091 474,1098 512,1102 551,1102 612,1102 666,1094 713,1077 760,1060 801,1038 836,1009 870,980 898,945 919,906 940,867 955,824 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 495,961 452,953 418,936 383,919 355,893 334,859 313,824 298,781 289,729 280,677 275,616 275,546 Z" id="glyph2679"/><glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,150 316,132 315,113 314,94 313,77 312,61 311,45 310,31 309,19 308,8 307,2 306,2 L 132,2 C 133,8 133,18 134,32 135,47 135,64 136,84 137,104 137,126 138,150 138,174 138,199 138,225 L 138,1484 318,1484 318,1061 C 318,1041 318,1022 318,1004 317,985 317,969 316,955 315,938 315,923 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,615 859,679 850,732 841,785 826,829 805,864 784,898 758,923 726,939 694,955 655,963 609,963 562,963 520,955 484,940 447,925 417,900 393,866 368,832 350,787 337,732 324,677 318,609 318,529 318,452 324,387 337,334 350,281 368,239 393,206 417,173 447,149 483,135 519,120 560,113 607,113 651,113 689,121 721,136 753,151 780,176 801,210 822,244 838,288 849,343 859,397 864,463 864,540 Z" id="glyph2681"/><glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,124 87,203 87,303 87,375 101,434 128,480 155,526 190,562 234,588 277,614 327,632 383,642 439,652 496,657 554,657 L 797,657 797,717 C 797,762 792,800 783,832 774,863 759,889 740,908 721,928 697,942 668,951 639,960 604,965 565,965 530,965 499,963 471,958 443,953 419,944 398,931 377,918 361,900 348,878 335,855 327,827 323,793 L 135,810 C 142,853 154,892 173,928 192,963 218,994 253,1020 287,1046 330,1066 382,1081 433,1095 496,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1090,111 1100,112 1110,113 1120,114 1130,116 1139,118 L 1139,6 C 1116,1 1094,-3 1072,-6 1049,-9 1025,-10 1000,-10 966,-10 937,-5 913,4 888,13 868,26 853,45 838,63 826,86 818,113 810,140 805,171 803,207 L 797,207 C 778,172 757,141 734,113 711,85 684,61 653,42 622,22 588,7 549,-4 510,-15 465,-20 414,-20 Z M 455,115 C 512,115 563,125 606,146 649,167 684,194 713,226 741,259 762,294 776,332 790,371 797,408 797,443 L 797,531 600,531 C 556,531 514,528 475,522 435,517 400,506 370,489 340,472 316,449 299,418 281,388 272,349 272,300 272,241 288,195 320,163 351,131 396,115 455,115 Z" id="glyph2683"/><glyph unicode="_" horiz-adv-x="1191" d="M -31,-407 L -31,-277 1162,-277 1162,-407 -31,-407 Z" id="glyph2685"/><glyph unicode="X" horiz-adv-x="1324" d="M 1112,0 L 689,616 257,0 46,0 582,732 87,1409 298,1409 690,856 1071,1409 1282,1409 800,739 1323,0 1112,0 Z" id="glyph2687"/><glyph unicode="T" horiz-adv-x="1139" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z" id="glyph2689"/><glyph unicode="S" horiz-adv-x="1139" d="M 1272,389 C 1272,330 1261,275 1238,225 1215,175 1179,132 1131,96 1083,59 1023,31 950,11 877,-10 790,-20 690,-20 515,-20 378,11 280,72 182,133 120,222 93,338 L 278,375 C 287,338 302,305 321,275 340,245 367,219 400,198 433,176 473,159 522,147 571,135 629,129 697,129 754,129 806,134 853,144 900,153 941,168 975,188 1009,208 1036,234 1055,266 1074,297 1083,335 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 613,659 573,668 534,679 494,689 456,701 420,716 383,730 349,747 317,766 285,785 257,809 234,836 211,863 192,894 179,930 166,965 159,1006 159,1053 159,1120 173,1177 200,1225 227,1272 264,1311 312,1342 360,1373 417,1395 482,1409 547,1423 618,1430 694,1430 781,1430 856,1423 918,1410 980,1396 1032,1375 1075,1348 1118,1321 1152,1287 1178,1247 1203,1206 1224,1159 1239,1106 L 1051,1073 C 1042,1107 1028,1137 1011,1164 993,1191 970,1213 941,1231 912,1249 878,1263 837,1272 796,1281 747,1286 692,1286 627,1286 572,1280 528,1269 483,1257 448,1241 421,1221 394,1201 374,1178 363,1151 351,1124 345,1094 345,1063 345,1021 356,987 377,960 398,933 426,910 462,892 498,874 540,859 587,847 634,835 685,823 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z" id="glyph2691"/><glyph unicode="R" horiz-adv-x="1218" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 911,1409 982,1400 1044,1382 1105,1363 1157,1337 1199,1302 1241,1267 1273,1225 1295,1175 1317,1125 1328,1069 1328,1006 1328,961 1322,917 1309,874 1296,831 1275,791 1247,755 1219,719 1183,688 1140,662 1097,636 1045,618 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1047 1129,1084 1114,1115 1099,1146 1078,1173 1050,1194 1022,1215 988,1230 948,1241 908,1251 863,1256 812,1256 L 359,1256 359,736 820,736 C 875,736 922,743 962,757 1002,770 1035,789 1061,813 1086,837 1105,865 1118,898 1130,931 1136,966 1136,1004 Z" id="glyph2693"/><glyph unicode="Q" horiz-adv-x="1377" d="M 1495,711 C 1495,612 1482,521 1457,439 1431,356 1394,284 1346,222 1297,160 1238,110 1168,71 1097,32 1017,6 928,-6 942,-49 958,-85 976,-115 993,-145 1013,-169 1036,-189 1059,-207 1084,-221 1112,-231 1139,-239 1170,-244 1204,-244 1223,-244 1243,-243 1264,-240 1285,-237 1304,-234 1319,-231 L 1319,-365 C 1294,-371 1266,-376 1236,-381 1205,-385 1174,-387 1141,-387 1084,-387 1034,-378 991,-361 948,-344 911,-319 879,-287 846,-255 818,-216 795,-170 772,-123 751,-71 733,-12 628,-12 535,7 456,46 376,84 310,136 257,200 204,265 164,340 137,427 110,513 97,607 97,709 97,819 112,919 143,1008 174,1097 219,1172 278,1235 337,1297 411,1346 498,1379 585,1413 684,1430 797,1430 909,1430 1009,1413 1096,1379 1183,1345 1256,1297 1315,1234 1374,1171 1418,1096 1449,1007 1480,918 1495,820 1495,711 Z M 1300,711 C 1300,796 1289,873 1268,942 1246,1011 1214,1071 1172,1120 1129,1169 1077,1207 1014,1234 951,1261 879,1274 797,1274 713,1274 639,1261 576,1234 513,1207 460,1169 418,1120 375,1071 344,1011 323,942 302,873 291,796 291,711 291,626 302,549 324,479 345,408 377,348 420,297 462,246 515,206 578,178 641,149 713,135 795,135 883,135 959,149 1023,178 1086,207 1139,247 1180,298 1221,349 1251,409 1271,480 1290,551 1300,628 1300,711 Z" id="glyph2695"/><glyph unicode="P" horiz-adv-x="1086" d="M 1258,985 C 1258,924 1248,867 1228,814 1207,761 1177,715 1137,676 1096,637 1046,606 985,583 924,560 854,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 844,1409 917,1399 979,1379 1041,1358 1093,1330 1134,1293 1175,1256 1206,1211 1227,1159 1248,1106 1258,1048 1258,985 Z M 1066,983 C 1066,1072 1039,1140 984,1187 929,1233 847,1256 738,1256 L 359,1256 359,700 746,700 C 856,700 937,724 989,773 1040,822 1066,892 1066,983 Z" id="glyph2697"/><glyph unicode="O" horiz-adv-x="1377" d="M 1495,711 C 1495,601 1479,501 1448,411 1416,321 1370,244 1310,180 1250,116 1177,67 1090,32 1003,-3 905,-20 795,-20 679,-20 577,-2 490,35 403,71 330,122 272,187 214,252 170,329 141,418 112,507 97,605 97,711 97,821 112,920 143,1009 174,1098 219,1173 278,1236 337,1298 411,1346 498,1380 585,1413 684,1430 797,1430 909,1430 1009,1413 1096,1379 1183,1345 1256,1297 1315,1234 1374,1171 1418,1096 1449,1007 1480,918 1495,820 1495,711 Z M 1300,711 C 1300,796 1289,873 1268,942 1246,1011 1214,1071 1172,1120 1129,1169 1077,1207 1014,1234 951,1261 879,1274 797,1274 713,1274 639,1261 576,1234 513,1207 460,1169 418,1120 375,1071 344,1011 323,942 302,873 291,796 291,711 291,626 302,549 324,479 345,408 377,348 420,297 462,246 515,206 578,178 641,149 713,135 795,135 883,135 959,149 1023,178 1086,207 1139,247 1180,298 1221,349 1251,409 1271,480 1290,551 1300,628 1300,711 Z" id="glyph2699"/><glyph unicode="M" horiz-adv-x="1324" d="M 1366,0 L 1366,940 C 1366,974 1366,1009 1367,1044 1368,1079 1369,1112 1370,1141 1371,1175 1373,1208 1375,1240 1366,1206 1356,1172 1346,1139 1337,1110 1328,1080 1318,1048 1307,1015 1297,986 1287,960 L 923,0 789,0 420,960 C 416,970 412,982 408,995 403,1008 399,1023 394,1038 389,1053 384,1068 379,1084 374,1099 369,1115 364,1130 353,1165 342,1202 331,1240 332,1203 333,1166 334,1129 335,1098 336,1065 337,1031 338,996 338,966 338,940 L 338,0 168,0 168,1409 419,1409 794,432 C 799,419 804,402 811,381 818,360 824,338 830,316 836,294 842,273 847,254 852,234 855,219 857,208 859,219 863,234 868,254 873,274 880,295 887,317 894,339 900,360 907,381 914,402 920,419 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z" id="glyph2701"/><glyph unicode="L" horiz-adv-x="900" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z" id="glyph2703"/><glyph unicode="K" horiz-adv-x="1191" d="M 1106,0 L 543,680 359,540 359,0 168,0 168,1409 359,1409 359,703 1038,1409 1263,1409 663,797 1343,0 1106,0 Z" id="glyph2705"/><glyph unicode="I" horiz-adv-x="186" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z" id="glyph2707"/><glyph unicode="E" horiz-adv-x="1112" d="M 168,0 L 168,1409 1237,1409 1237,1253 359,1253 359,801 1177,801 1177,647 359,647 359,156 1278,156 1278,0 168,0 Z" id="glyph2709"/><glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,602 1363,498 1328,409 1293,319 1244,244 1183,184 1122,123 1049,78 966,47 882,16 792,0 695,0 L 168,0 168,1409 634,1409 C 743,1409 843,1396 935,1369 1026,1342 1105,1300 1171,1244 1237,1187 1289,1116 1326,1029 1363,942 1381,839 1381,719 Z M 1189,719 C 1189,814 1175,896 1148,964 1121,1031 1082,1087 1033,1130 984,1173 925,1205 856,1226 787,1246 712,1256 630,1256 L 359,1256 359,153 673,153 C 747,153 816,165 879,189 942,213 996,249 1042,296 1088,343 1124,402 1150,473 1176,544 1189,626 1189,719 Z" id="glyph2711"/><glyph unicode="C" horiz-adv-x="1297" d="M 792,1274 C 712,1274 641,1261 580,1234 518,1207 466,1169 425,1120 383,1071 351,1011 330,942 309,873 298,796 298,711 298,626 310,549 333,479 356,408 389,348 432,297 475,246 527,207 590,179 652,151 722,137 800,137 855,137 905,144 950,159 995,173 1035,193 1072,219 1108,245 1140,276 1169,312 1198,347 1223,387 1245,430 L 1401,352 C 1376,299 1344,250 1307,205 1270,160 1226,120 1176,87 1125,54 1068,28 1005,9 941,-10 870,-20 791,-20 677,-20 577,-2 492,35 406,71 334,122 277,187 219,252 176,329 147,418 118,507 104,605 104,711 104,821 119,920 150,1009 180,1098 224,1173 283,1236 341,1298 413,1346 498,1380 583,1413 681,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1194,1054 1176,1086 1153,1117 1130,1147 1102,1174 1068,1197 1034,1220 994,1239 949,1253 903,1267 851,1274 792,1274 Z" id="glyph2713"/><glyph unicode="B" horiz-adv-x="1086" d="M 1258,397 C 1258,326 1244,265 1216,215 1188,164 1150,123 1103,92 1056,60 1001,37 938,22 875,7 809,0 740,0 L 168,0 168,1409 680,1409 C 758,1409 828,1403 889,1390 950,1377 1002,1356 1045,1328 1088,1300 1120,1265 1143,1222 1165,1179 1176,1127 1176,1067 1176,1028 1171,991 1160,956 1149,921 1132,890 1110,862 1087,833 1059,809 1026,789 992,768 953,753 908,743 965,736 1015,723 1059,704 1102,685 1139,660 1168,630 1197,600 1220,565 1235,526 1250,486 1258,443 1258,397 Z M 984,1044 C 984,1120 958,1174 906,1207 854,1240 779,1256 680,1256 L 359,1256 359,810 680,810 C 736,810 783,816 822,827 861,838 892,853 916,874 940,894 957,918 968,947 979,976 984,1008 984,1044 Z M 1065,412 C 1065,457 1057,495 1041,526 1024,557 1001,583 970,603 939,623 903,638 860,647 817,656 768,661 715,661 L 359,661 359,153 730,153 C 779,153 824,157 865,165 906,173 941,187 971,207 1000,227 1023,254 1040,287 1057,320 1065,362 1065,412 Z" id="glyph2715"/><glyph unicode="A" horiz-adv-x="1350" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 768,1026 C 757,1053 747,1080 738,1107 728,1134 719,1159 712,1182 705,1204 699,1223 694,1238 689,1253 686,1262 685,1265 684,1262 681,1252 676,1237 671,1222 665,1203 658,1180 650,1157 641,1132 632,1105 622,1078 612,1051 602,1024 L 422,561 949,561 768,1026 Z" id="glyph2717"/><glyph unicode=")" horiz-adv-x="557" d="M 555,528 C 555,435 548,346 534,262 520,177 498,96 468,18 438,-60 400,-136 353,-209 306,-282 251,-354 186,-424 L 12,-424 C 75,-354 129,-282 175,-209 220,-136 258,-60 287,19 316,98 338,179 353,264 367,349 374,437 374,530 374,623 367,711 353,796 338,881 316,962 287,1041 258,1119 220,1195 175,1269 129,1342 75,1414 12,1484 L 186,1484 C 251,1414 306,1342 353,1269 400,1196 438,1120 468,1042 498,964 520,883 534,798 548,713 555,625 555,532 L 555,528 Z" id="glyph2719"/><glyph unicode="(" horiz-adv-x="557" d="M 127,532 C 127,625 134,713 148,798 162,883 184,964 214,1042 244,1120 282,1196 329,1269 376,1342 431,1414 496,1484 L 670,1484 C 607,1414 553,1342 508,1269 462,1195 424,1119 395,1041 366,962 344,881 330,796 315,711 308,623 308,530 308,437 315,349 330,264 344,179 366,98 395,19 424,-60 462,-136 508,-209 553,-282 607,-354 670,-424 L 496,-424 C 431,-354 376,-282 329,-209 282,-136 244,-60 214,18 184,96 162,177 148,262 134,346 127,435 127,528 L 127,532 Z" id="glyph2721"/><glyph unicode=" " horiz-adv-x="556" id="glyph2723"/></font></defs><defs class="TextShapeIndex" id="defs2730"><g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46 id47 id48 id49 id50 id51 id52 id53 id54 id55 id56 id57 id58 id59 id60 id61 id62" id="g2728"/></defs><defs class="EmbeddedBulletChars" id="defs2762"><g id="bullet-char-template(57356)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 580,1141 1163,571 580,0 -4,571 Z" id="path2732" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(57354)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 8,1128 H 1137 V 0 H 8 Z" id="path2735" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10146)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 174,0 602,739 174,1481 1456,739 Z M 1358,739 309,1346 659,739 Z" id="path2738" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10132)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 2015,739 1276,0 H 717 l 543,543 H 174 v 393 h 1086 l -543,545 h 557 z" id="path2741" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10007)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 0,-2 c -7,16 -16,29 -25,39 l 381,530 c -94,256 -141,385 -141,387 0,25 13,38 40,38 9,0 21,-2 34,-5 21,4 42,12 65,25 l 27,-13 111,-251 280,301 64,-25 24,25 c 21,-10 41,-24 62,-43 C 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 c 0,-27 -21,-55 -63,-84 l 16,-20 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 c -22,-34 -53,-51 -92,-51 -42,0 -63,17 -64,51 -7,9 -10,24 -10,44 0,9 1,19 2,30 z" id="path2744" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(10004)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 c 0,78 14,145 41,201 34,71 87,106 158,106 53,0 88,-31 106,-94 l 23,-176 c 8,-64 28,-97 59,-98 l 735,706 c 11,11 33,17 66,17 42,0 63,-15 63,-46 V 965 c 0,-36 -10,-64 -30,-84 L 442,47 C 390,-6 338,-33 285,-33 Z" id="path2747" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(9679)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 c 0,181 53,324 160,431 106,107 249,161 430,161 179,0 323,-54 432,-161 108,-107 162,-251 162,-431 0,-180 -54,-324 -162,-431 C 1136,54 992,0 813,0 Z" id="path2750" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(8226)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 346,457 c -73,0 -137,26 -191,78 -54,51 -81,114 -81,188 0,73 27,136 81,188 54,52 118,78 191,78 73,0 134,-26 185,-79 51,-51 77,-114 77,-187 0,-75 -25,-137 -76,-188 -50,-52 -112,-78 -186,-78 z" id="path2753" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(8211)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="M -4,459 H 1135 V 606 H -4 Z" id="path2756" inkscape:connector-curvature="0"/></g><g id="bullet-char-template(61548)" transform="matrix(4.8828125e-4,0,0,-4.8828125e-4,0,0)"><path d="m 173,740 c 0,163 58,303 173,419 116,115 255,173 419,173 163,0 302,-58 418,-173 116,-116 174,-256 174,-419 0,-163 -58,-303 -174,-418 C 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z" id="path2759" inkscape:connector-curvature="0"/></g></defs><defs class="TextEmbeddedBitmaps" id="defs2764"/><g id="g2769" transform="translate(-4840.889,-3040)"><g id="id2" class="Master_Slide"><g id="bg-id2" class="Background"/><g id="bo-id2" class="BackgroundObjects"/></g></g><g class="SlideGroup" id="g3795" transform="translate(-4840.889,-3040)"><g id="g3793"><g id="container-id1"><g id="id1" class="Slide" clip-path="url(#presentation_clip_path)"><g class="Page" id="g3789"><g class="com.sun.star.drawing.CustomShape" id="g2790"><g id="id3"><rect class="BoundingBox" x="7530" y="7229" width="3561" height="1654" id="rect2771" style="fill:none;stroke:none"/><path d="m 7806,7230 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7505 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path2773" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 7806,7230 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7505 c 0,-138 -138,-275 -276,-275 z" id="path2775" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 7531,7230 Z" id="path2777" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11089,8881 Z" id="path2779" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text2787"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan2785" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="7773" y="8276" id="tspan2783"><tspan id="tspan2781" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g2811"><g id="id4"><rect class="BoundingBox" x="7785" y="7611" width="3561" height="1654" id="rect2792" style="fill:none;stroke:none"/><path d="m 8061,7612 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7887 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path2794" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8061,7612 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 7887 c 0,-138 -138,-275 -276,-275 z" id="path2796" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 7786,7612 Z" id="path2798" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11344,9263 Z" id="path2800" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text2808"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan2806" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8028" y="8658" id="tspan2804"><tspan id="tspan2802" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g2832"><g id="id5"><rect class="BoundingBox" x="8037" y="7991" width="3561" height="1654" id="rect2813" style="fill:none;stroke:none"/><path d="m 8313,7992 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8267 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path2815" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8313,7992 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8267 c 0,-138 -138,-275 -276,-275 z" id="path2817" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 8038,7992 Z" id="path2819" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11596,9643 Z" id="path2821" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text2829"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan2827" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8773" y="9038" id="tspan2825"><tspan id="tspan2823" style="fill:#000000;stroke:none">PKTIO </tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g2853"><g id="id6"><rect class="BoundingBox" x="8037" y="10151" width="3561" height="1654" id="rect2834" style="fill:none;stroke:none"/><path d="m 8313,10152 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path2836" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 8313,10152 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path2838" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 8038,10152 Z" id="path2840" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 11596,11803 Z" id="path2842" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text2850"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan2848" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="8818" y="11198" id="tspan2846"><tspan id="tspan2844" style="fill:#000000;stroke:none">Buffers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g2874"><g id="id7"><rect class="BoundingBox" x="12610" y="8691" width="4322" height="1654" id="rect2855" style="fill:none;stroke:none"/><path d="m 12886,8692 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8967 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 4319,1651 z" id="path2857" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 12886,8692 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8967 c 0,-138 -138,-275 -276,-275 z" id="path2859" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 12611,8692 Z" id="path2861" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 16930,10343 Z" id="path2863" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text2871"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan2869" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="12905" y="9738" id="tspan2867"><tspan id="tspan2865" style="fill:#000000;stroke:none">Classification</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g2899"><g id="id8"><rect class="BoundingBox" x="18072" y="7228" width="1909" height="639" id="rect2876" style="fill:none;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path2878" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2880" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,7229 Z" id="path2882" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,7865 Z" id="path2884" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2886" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,7229 Z" id="path2888" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,7865 Z" id="path2890" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2892" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,7229 Z" id="path2894" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,7865 Z" id="path2896" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g2924"><g id="id9"><rect class="BoundingBox" x="18072" y="8245" width="1909" height="639" id="rect2901" style="fill:none;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path2903" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2905" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,8246 Z" id="path2907" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,8882 Z" id="path2909" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2911" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,8246 Z" id="path2913" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,8882 Z" id="path2915" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2917" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,8246 Z" id="path2919" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,8882 Z" id="path2921" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g2949"><g id="id10"><rect class="BoundingBox" x="18072" y="9262" width="1909" height="639" id="rect2926" style="fill:none;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path2928" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2930" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,9263 Z" id="path2932" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,9899 Z" id="path2934" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2936" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,9263 Z" id="path2938" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,9899 Z" id="path2940" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2942" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,9263 Z" id="path2944" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,9899 Z" id="path2946" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g2974"><g id="id11"><rect class="BoundingBox" x="18072" y="10404" width="1909" height="639" id="rect2951" style="fill:none;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path2953" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2955" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,10405 Z" id="path2957" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,11041 Z" id="path2959" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2961" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,10405 Z" id="path2963" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,11041 Z" id="path2965" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path2967" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,10405 Z" id="path2969" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,11041 Z" id="path2971" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g2983"><g id="id12"><rect class="BoundingBox" x="11595" y="7398" width="6479" height="1421" id="rect2976" style="fill:none;stroke:none"/><path d="m 11596,8817 c 1822,0 -1285,-1218 6005,-1268" id="path2978" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,7547 -451,-148 2,300 z" id="path2980" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g2992"><g id="id13"><rect class="BoundingBox" x="16928" y="8564" width="1146" height="955" id="rect2985" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 474,-567 747,-827" id="path2987" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,8564 -474,4 97,284 z" id="path2989" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3001"><g id="id14"><rect class="BoundingBox" x="16928" y="9418" width="1146" height="302" id="rect2994" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 491,34 710,53" id="path2996" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,9581 -446,-163 -8,300 z" id="path2998" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3010"><g id="id15"><rect class="BoundingBox" x="16928" y="9516" width="1146" height="1208" id="rect3003" style="fill:none;stroke:none"/><path d="m 16929,9517 c 859,0 469,738 759,1060" id="path3005" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,10723 -361,-308 -112,278 z" id="path3007" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3019"><g id="id16"><rect class="BoundingBox" x="11595" y="8816" width="1017" height="702" id="rect3012" style="fill:none;stroke:none"/><path d="m 11596,8817 c 762,0 439,365 621,568" id="path3014" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 12611,9517 -372,-294 -102,282 z" id="path3016" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g3040"><g id="id17"><rect class="BoundingBox" x="21501" y="8246" width="4322" height="1654" id="rect3021" style="fill:none;stroke:none"/><path d="m 21777,8247 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8522 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 4319,1651 z" id="path3023" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 21777,8247 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3768 c 138,0 276,-138 276,-276 V 8522 c 0,-138 -138,-275 -276,-275 z" id="path3025" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 21502,8247 Z" id="path3027" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 25821,9898 Z" id="path3029" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3037"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3035" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="22237" y="9293" id="tspan3033"><tspan id="tspan3031" style="fill:#000000;stroke:none">Scheduler</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3049"><g id="id18"><rect class="BoundingBox" x="19978" y="7546" width="6859" height="1257" id="rect3042" style="fill:none;stroke:none"/><path d="m 19979,7547 c 8298,0 5408,934 6428,1108" id="path3044" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 26836,8683 -439,-181 -20,300 z" id="path3046" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3058"><g id="id19"><rect class="BoundingBox" x="19978" y="8563" width="1525" height="617" id="rect3051" style="fill:none;stroke:none"/><path d="m 19979,8564 c 1143,0 584,351 1090,470" id="path3053" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -434,-192 -28,299 z" id="path3055" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3067"><g id="id20"><rect class="BoundingBox" x="19978" y="8965" width="1525" height="618" id="rect3060" style="fill:none;stroke:none"/><path d="m 19979,9581 c 1143,0 584,-352 1090,-470" id="path3062" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -462,-107 28,299 z" id="path3064" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3076"><g id="id21"><rect class="BoundingBox" x="19978" y="9047" width="1525" height="1678" id="rect3069" style="fill:none;stroke:none"/><path d="m 19979,10723 c 1143,0 572,-1180 1116,-1545" id="path3071" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 21502,9072 -474,-24 81,289 z" id="path3073" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.TextShape" id="g3089"><g id="id22"><rect class="BoundingBox" x="17808" y="5933" width="3569" height="1214" id="rect3078" style="fill:none;stroke:none"/><text class="TextShape" id="text3086"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3084" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="18058" y="6630" id="tspan3082"><tspan id="tspan3080" style="fill:#000000;stroke:none">Queue</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3110"><g id="id23"><rect class="BoundingBox" x="26834" y="7857" width="3561" height="1654" id="rect3091" style="fill:none;stroke:none"/><path d="m 27110,7858 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8133 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3093" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27110,7858 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8133 c 0,-138 -138,-275 -276,-275 z" id="path3095" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 26835,7858 Z" id="path3097" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30393,9509 Z" id="path3099" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3107"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3105" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27077" y="8904" id="tspan3103"><tspan id="tspan3101" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3131"><g id="id24"><rect class="BoundingBox" x="27088" y="8237" width="3561" height="1654" id="rect3112" style="fill:none;stroke:none"/><path d="m 27364,8238 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8513 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3114" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27364,8238 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8513 c 0,-138 -138,-275 -276,-275 z" id="path3116" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27089,8238 Z" id="path3118" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30647,9889 Z" id="path3120" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3128"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3126" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27331" y="9284" id="tspan3124"><tspan id="tspan3122" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3152"><g id="id25"><rect class="BoundingBox" x="27343" y="8619" width="3561" height="1654" id="rect3133" style="fill:none;stroke:none"/><path d="m 27619,8620 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8895 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3135" inkscape:connector-curvature="0" style="fill:#ffff00;stroke:none"/><path d="m 27619,8620 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8895 c 0,-138 -138,-275 -276,-275 z" id="path3137" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27344,8620 Z" id="path3139" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30902,10271 Z" id="path3141" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3149"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3147" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="27522" y="9666" id="tspan3145"><tspan id="tspan3143" style="fill:#000000;stroke:none">odp_thread</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3161"><g id="id26"><rect class="BoundingBox" x="25819" y="8914" width="1272" height="302" id="rect3154" style="fill:none;stroke:none"/><path d="m 25820,9072 c 952,0 521,-5 836,-8" id="path3156" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 27090,9063 -450,-149 v 300 z" id="path3158" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3170"><g id="id27"><rect class="BoundingBox" x="25819" y="9071" width="1527" height="494" id="rect3163" style="fill:none;stroke:none"/><path d="m 25820,9072 c 1144,0 585,257 1090,345" id="path3165" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 27345,9445 -438,-181 -21,299 z" id="path3167" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3195"><g id="id28"><rect class="BoundingBox" x="18072" y="7228" width="1909" height="639" id="rect3172" style="fill:none;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3174" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3176" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,7229 Z" id="path3178" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,7865 Z" id="path3180" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3182" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,7229 Z" id="path3184" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,7865 Z" id="path3186" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,7229 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3188" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,7229 Z" id="path3190" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,7865 Z" id="path3192" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3220"><g id="id29"><rect class="BoundingBox" x="18072" y="8245" width="1909" height="639" id="rect3197" style="fill:none;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3199" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3201" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,8246 Z" id="path3203" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,8882 Z" id="path3205" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3207" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,8246 Z" id="path3209" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,8882 Z" id="path3211" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,8246 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3213" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,8246 Z" id="path3215" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,8882 Z" id="path3217" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3245"><g id="id30"><rect class="BoundingBox" x="18072" y="9262" width="1909" height="639" id="rect3222" style="fill:none;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3224" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3226" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,9263 Z" id="path3228" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,9899 Z" id="path3230" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3232" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,9263 Z" id="path3234" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,9899 Z" id="path3236" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,9263 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3238" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,9263 Z" id="path3240" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,9899 Z" id="path3242" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3270"><g id="id31"><rect class="BoundingBox" x="18072" y="10404" width="1909" height="639" id="rect3247" style="fill:none;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3249" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 18073,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3251" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18073,10405 Z" id="path3253" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18709,11041 Z" id="path3255" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 18708,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3257" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 18708,10405 Z" id="path3259" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19344,11041 Z" id="path3261" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 19343,10405 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3263" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19343,10405 Z" id="path3265" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 19979,11041 Z" id="path3267" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3295"><g id="id32"><rect class="BoundingBox" x="35089" y="7471" width="1909" height="639" id="rect3272" style="fill:none;stroke:none"/><path d="m 35090,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3274" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3276" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,7472 Z" id="path3278" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,8108 Z" id="path3280" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3282" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,7472 Z" id="path3284" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,8108 Z" id="path3286" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,7472 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3288" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,7472 Z" id="path3290" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,8108 Z" id="path3292" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3320"><g id="id33"><rect class="BoundingBox" x="35089" y="8787" width="1909" height="639" id="rect3297" style="fill:none;stroke:none"/><path d="m 35090,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3299" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3301" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,8788 Z" id="path3303" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,9424 Z" id="path3305" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3307" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,8788 Z" id="path3309" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,9424 Z" id="path3311" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,8788 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3313" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,8788 Z" id="path3315" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,9424 Z" id="path3317" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.PolyPolygonShape" id="g3345"><g id="id34"><rect class="BoundingBox" x="35089" y="9930" width="1909" height="639" id="rect3322" style="fill:none;stroke:none"/><path d="m 35090,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z m -1,-636 h 477 l 159,318 -159,318 h -477 l 159,-318 z m 0,0 z m 636,636 z" id="path3324" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 35090,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3326" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35090,9931 Z" id="path3328" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35726,10567 Z" id="path3330" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 35725,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3332" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 35725,9931 Z" id="path3334" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36361,10567 Z" id="path3336" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="m 36360,9931 h 477 l 159,318 -159,318 h -477 l 159,-318 z" id="path3338" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36360,9931 Z" id="path3340" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 36996,10567 Z" id="path3342" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3354"><g id="id35"><rect class="BoundingBox" x="30901" y="9444" width="4190" height="950" id="rect3347" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1257,720 3726,797" id="path3349" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,10249 -448,-157 -4,300 z" id="path3351" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3363"><g id="id36"><rect class="BoundingBox" x="30901" y="8958" width="4190" height="489" id="rect3356" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1258,-303 3724,-336" id="path3358" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,9106 -451,-147 2,300 z" id="path3360" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3372"><g id="id37"><rect class="BoundingBox" x="30901" y="7653" width="4190" height="1794" id="rect3365" style="fill:none;stroke:none"/><path d="m 30902,9445 c 3141,0 1254,-1485 3732,-1642" id="path3367" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 35090,7790 -454,-136 9,300 z" id="path3369" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3381"><g id="id38"><rect class="BoundingBox" x="17459" y="9444" width="13734" height="2353" id="rect3374" style="fill:none;stroke:none"/><path d="m 30902,9445 c 751,0 502,2350 -6573,2350 -7074,0 -7217,-778 -6677,-1009" id="path3376" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,10723 -468,-76 48,296 z" id="path3378" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g3402"><g id="id39"><rect class="BoundingBox" x="38898" y="8872" width="4577" height="1654" id="rect3383" style="fill:none;stroke:none"/><path d="m 39174,8873 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 4023 c 137,0 275,-138 275,-276 V 9148 c 0,-138 -138,-275 -275,-275 z m -275,0 z m 4574,1651 z" id="path3385" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 39174,8873 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 4023 c 137,0 275,-138 275,-276 V 9148 c 0,-138 -138,-275 -275,-275 z" id="path3387" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 38899,8873 Z" id="path3389" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 43473,10524 Z" id="path3391" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3399"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3397" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="39511" y="9919" id="tspan3395"><tspan id="tspan3393" style="fill:#000000;stroke:none">Traffic Mngr</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3423"><g id="id40"><rect class="BoundingBox" x="30771" y="12437" width="3561" height="1654" id="rect3404" style="fill:none;stroke:none"/><path d="m 31047,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3406" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3408" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,12438 Z" id="path3410" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,14089 Z" id="path3412" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3420"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3418" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31581" y="13484" id="tspan3416"><tspan id="tspan3414" style="fill:#000000;stroke:none">Packet</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3444"><g id="id41"><rect class="BoundingBox" x="27214" y="17390" width="3561" height="1654" id="rect3425" style="fill:none;stroke:none"/><path d="m 27490,17391 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3427" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,17391 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3429" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,17391 Z" id="path3431" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,19042 Z" id="path3433" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3441"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3439" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28200" y="18437" id="tspan3437"><tspan id="tspan3435" style="fill:#000000;stroke:none">IPsec</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3465"><g id="id42"><rect class="BoundingBox" x="30771" y="14087" width="3561" height="1654" id="rect3446" style="fill:none;stroke:none"/><path d="m 31047,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3448" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3450" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,14088 Z" id="path3452" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,15739 Z" id="path3454" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3462"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3460" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31459" y="15134" id="tspan3458"><tspan id="tspan3456" style="fill:#000000;stroke:none">Shmem</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3486"><g id="id43"><rect class="BoundingBox" x="27214" y="15738" width="3561" height="1654" id="rect3467" style="fill:none;stroke:none"/><path d="m 27490,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3469" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3471" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,15739 Z" id="path3473" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,17390 Z" id="path3475" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3483"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3481" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28289" y="16785" id="tspan3479"><tspan id="tspan3477" style="fill:#000000;stroke:none">Sync</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3507"><g id="id44"><rect class="BoundingBox" x="30771" y="15738" width="3561" height="1654" id="rect3488" style="fill:none;stroke:none"/><path d="m 31047,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3490" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31047,15739 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3492" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30772,15739 Z" id="path3494" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34330,17390 Z" id="path3496" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3504"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3502" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31552" y="16785" id="tspan3500"><tspan id="tspan3498" style="fill:#000000;stroke:none">Buffers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3516"><g id="id45"><rect class="BoundingBox" x="24597" y="9444" width="6944" height="8898" id="rect3509" style="fill:none;stroke:none"/><path d="m 30902,9445 c 1251,0 834,2604 -2827,2604 -3661,0 -4961,5742 -1281,6145" id="path3511" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 27216,18216 -441,-175 -17,300 z" id="path3513" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3525"><g id="id46"><rect class="BoundingBox" x="36995" y="9105" width="1906" height="716" id="rect3518" style="fill:none;stroke:none"/><path d="m 36996,9106 c 1428,0 678,452 1468,566" id="path3520" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 38900,9698 -440,-178 -19,299 z" id="path3522" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3534"><g id="id47"><rect class="BoundingBox" x="36995" y="9574" width="1906" height="677" id="rect3527" style="fill:none;stroke:none"/><path d="m 36996,10249 c 1428,0 678,-421 1468,-527" id="path3529" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 38900,9698 -458,-123 18,299 z" id="path3531" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g3555"><g id="id48"><rect class="BoundingBox" x="45376" y="7811" width="3561" height="1654" id="rect3536" style="fill:none;stroke:none"/><path d="m 45652,7812 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8087 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3538" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 45652,7812 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8087 c 0,-138 -138,-275 -276,-275 z" id="path3540" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45377,7812 Z" id="path3542" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 48935,9463 Z" id="path3544" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3552"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3550" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="45619" y="8858" id="tspan3548"><tspan id="tspan3546" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3576"><g id="id49"><rect class="BoundingBox" x="45631" y="8191" width="3561" height="1654" id="rect3557" style="fill:none;stroke:none"/><path d="m 45907,8192 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8467 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3559" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 45907,8192 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8467 c 0,-138 -138,-275 -276,-275 z" id="path3561" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45632,8192 Z" id="path3563" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 49190,9843 Z" id="path3565" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3573"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3571" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="45874" y="9238" id="tspan3569"><tspan id="tspan3567" style="fill:#000000;stroke:none">PKTIO API</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3597"><g id="id50"><rect class="BoundingBox" x="45883" y="8572" width="3561" height="1654" id="rect3578" style="fill:none;stroke:none"/><path d="m 46159,8573 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8848 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3580" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 46159,8573 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 V 8848 c 0,-138 -138,-275 -276,-275 z" id="path3582" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 45884,8573 Z" id="path3584" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 49442,10224 Z" id="path3586" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3594"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3592" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="46708" y="9619" id="tspan3590"><tspan id="tspan3588" style="fill:#000000;stroke:none">PKTIO</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3606"><g id="id51"><rect class="BoundingBox" x="43472" y="9256" width="2414" height="444" id="rect3599" style="fill:none;stroke:none"/><path d="m 43473,9698 c 1809,0 802,-246 1978,-293" id="path3601" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 45885,9398 -453,-142 6,300 z" id="path3603" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g3627"><g id="id52"><rect class="BoundingBox" x="27214" y="12437" width="3561" height="1654" id="rect3608" style="fill:none;stroke:none"/><path d="m 27490,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3610" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,12438 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3612" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,12438 Z" id="path3614" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,14089 Z" id="path3616" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3624"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3622" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28037" y="13484" id="tspan3620"><tspan id="tspan3618" style="fill:#000000;stroke:none">Timers</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3648"><g id="id53"><rect class="BoundingBox" x="27214" y="14087" width="3561" height="1654" id="rect3629" style="fill:none;stroke:none"/><path d="m 27490,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3631" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 27490,14088 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3633" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 27215,14088 Z" id="path3635" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30773,15739 Z" id="path3637" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3645"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3643" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="28183" y="15134" id="tspan3641"><tspan id="tspan3639" style="fill:#000000;stroke:none">Event</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3657"><g id="id54"><rect class="BoundingBox" x="9223" y="3801" width="37935" height="4013" id="rect3650" style="fill:none;stroke:none"/><path d="M 47156,7812 C 47156,2457 10447,2740 9336,7029" id="path3652" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 9310,7230 210,-425 -297,-41 z" id="path3654" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3666"><g id="id55"><rect class="BoundingBox" x="17708" y="10723" width="17467" height="9710" id="rect3659" style="fill:none;stroke:none"/><path d="m 34412,18264 c 1701,0 1134,2167 -7853,2167 -8987,0 -9083,-7740 -8683,-9398" id="path3661" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 18073,10723 -364,304 255,158 z" id="path3663" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.TextShape" id="g3679"><g id="id56"><rect class="BoundingBox" x="21199" y="20231" width="9901" height="1674" id="rect3668" style="fill:none;stroke:none"/><text class="TextShape" id="text3676"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3674" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="21449" y="20928" id="tspan3672"><tspan id="tspan3670" style="fill:#000000;stroke:none">Lookaside IPsec Op Completion</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.TextShape" id="g3692"><g id="id57"><rect class="BoundingBox" x="26705" y="3040" width="5335" height="1017" id="rect3681" style="fill:none;stroke:none"/><text class="TextShape" id="text3689"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3687" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="26955" y="3737" id="tspan3685"><tspan id="tspan3683" style="fill:#000000;stroke:none">Loopback</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.TextShape" id="g3715"><g id="id58"><rect class="BoundingBox" x="21128" y="14616" width="3684" height="2385" id="rect3694" style="fill:none;stroke:none"/><text class="TextShape" id="text3712"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3700" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="21378" y="15313" id="tspan3698"><tspan id="tspan3696" style="fill:#000000;stroke:none">IPsec Op</tspan></tspan></tspan><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3710" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="21378" y="16016" id="tspan3704"><tspan id="tspan3702" style="fill:#000000;stroke:none">(Encrypt or </tspan></tspan><tspan class="TextPosition" x="21378" y="16719" id="tspan3708"><tspan id="tspan3706" style="fill:#000000;stroke:none">Decrypt)</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3736"><g id="id59"><rect class="BoundingBox" x="4854" y="8465" width="1967" height="1066" id="rect3717" style="fill:none;stroke:none"/><path d="m 4855,8731 h 1473 v -265 l 491,531 -491,532 V 9263 H 4855 Z m 0,-265 z m 1964,1063 z" id="path3719" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 4855,8731 h 1473 v -265 l 491,531 -491,532 V 9263 H 4855 Z" id="path3721" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 4855,8466 Z" id="path3723" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 6819,9529 Z" id="path3725" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3733"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3731" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="5274" y="9218" id="tspan3729"><tspan id="tspan3727" style="fill:#000000;stroke:none">RX</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.ConnectorShape" id="g3745"><g id="id60"><rect class="BoundingBox" x="36995" y="7789" width="8384" height="994" id="rect3738" style="fill:none;stroke:none"/><path d="m 36996,7790 c 8817,0 4991,769 7903,842" id="path3740" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/><path d="m 45378,8637 -448,-155 -4,300 z" id="path3742" inkscape:connector-curvature="0" style="fill:#000000;stroke:none"/></g></g><g class="com.sun.star.drawing.CustomShape" id="g3766"><g id="id61"><rect class="BoundingBox" x="50054" y="8465" width="1967" height="1066" id="rect3747" style="fill:none;stroke:none"/><path d="m 50055,8731 h 1473 v -265 l 491,531 -491,532 v -266 h -1473 z m 0,-265 z m 1964,1063 z" id="path3749" inkscape:connector-curvature="0" style="fill:#729fcf;stroke:none"/><path d="m 50055,8731 h 1473 v -265 l 491,531 -491,532 v -266 h -1473 z" id="path3751" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 50055,8466 Z" id="path3753" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 52019,9529 Z" id="path3755" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3763"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3761" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="50508" y="9218" id="tspan3759"><tspan id="tspan3757" style="fill:#000000;stroke:none">TX</tspan></tspan></tspan></text>
+</g></g><g class="com.sun.star.drawing.CustomShape" id="g3787"><g id="id62"><rect class="BoundingBox" x="30853" y="17438" width="3561" height="1654" id="rect3768" style="fill:none;stroke:none"/><path d="m 31129,17439 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z m -275,0 z m 3558,1651 z" id="path3770" inkscape:connector-curvature="0" style="fill:#b6d7a8;stroke:none"/><path d="m 31129,17439 c -138,0 -275,137 -275,275 v 1100 c 0,138 137,276 275,276 h 3007 c 138,0 276,-138 276,-276 v -1100 c 0,-138 -138,-275 -276,-275 z" id="path3772" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 30854,17439 Z" id="path3774" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><path d="M 34412,19090 Z" id="path3776" inkscape:connector-curvature="0" style="fill:none;stroke:#3465a4"/><text class="TextShape" id="text3784"><tspan class="TextParagraph" font-size="635px" font-weight="400" id="tspan3782" style="font-weight:400;font-size:635px;font-family:'Liberation Sans', sans-serif"><tspan class="TextPosition" x="31699" y="18485" id="tspan3780"><tspan id="tspan3778" style="fill:#000000;stroke:none">Crypto</tspan></tspan></tspan></text>
+</g></g></g></g></g></g></g></svg>
diff --git a/doc/images/ipsec_sa_states.msc b/doc/images/ipsec_sa_states.msc
new file mode 100644
index 000000000..7030877e3
--- /dev/null
+++ b/doc/images/ipsec_sa_states.msc
@@ -0,0 +1,76 @@
+msc {
+
+ a [label = "Application"],
+ o [label = "ODP"],
+ p [label = "Platform"];
+
+ --- [label = "IPsec configuration, done once"];
+ a->o [label = "odp_ipsec_config()"];
+ o->p [label = "Config IPsec"];
+ o->a [label = "OK"];
+
+ |||;
+ --- [label = "IPsec SA creation, per SA"];
+ |||;
+
+ a->o [label = "odp_ipsec_sa_create()"];
+ o->p [label = "SA Create"];
+ o->a [label = "OK"];
+
+ |||;
+ --- [label = "IPsec operations, per SA"];
+ |||;
+
+ a->o [label = "odp_ipsec_in()"];
+ o->p [label = "IPsec Decrypt"];
+ p->a [label = "Done"];
+
+ a->o [label = "odp_ipsec_out()"];
+ o->p [label = "IPsec Encrypt"];
+ p->a [label = "Done"];
+
+ a->o [label = "odp_ipsec_out_inline()"];
+ o->p [label = "IPsec Encrypt Inline"];
+ o->a [label = "OK"];
+ p->o [label = "OK"];
+
+ a->o [label = "odp_ipsec_in_enq()"];
+ o->p [label = "Initiate IPsec operation"];
+ a->o [label = "odp_ipsec_out_enq()"];
+ o->p [label = "Initiate IPsec operation"];
+
+ |||;
+ --- [label = "Time passes"];
+ |||;
+
+ p->o [label = "IPsec op complete"];
+ a->o [label = "odp_schedule()"];
+ o->p [label = "Get Event"];
+ p->a [label = "ODP_EVENT_PACKET subtype ODP_EVENT_PACKET_IPSEC"];
+ a->o [label = "odp_ipsec_result()"];
+ o->a [label = "OK"];
+
+ |||;
+ --- [label = "App done with SA, per SA"];
+ |||;
+
+ a->o [label = "odp_ipsec_sa_disable()"];
+ o->p [label = "Disable/Delete SA"];
+ o->a [label = "OK"];
+ p->o [label = "Done"];
+
+ |||;
+ --- [label = "Time passes"];
+ |||;
+
+ a->o [label = "odp_schedule()"];
+ o->p [label = "Get Event"];
+ p->a [label = "ODP_EVENT_IPSEC_STATUS"];
+ a->o [label = "odp_ipsec_status"];
+ o->a [label = "ODP_IPSEC_STATUS_SA_DISABLED"];
+
+ a->o [label = "odp_ipsec_sa_destroy()"];
+ o->a [label = "OK"];
+
+
+} \ No newline at end of file
diff --git a/doc/images/pktio_fsm.gv b/doc/images/pktio_fsm.gv
index 09199c607..b28fbb0d4 100644
--- a/doc/images/pktio_fsm.gv
+++ b/doc/images/pktio_fsm.gv
@@ -1,6 +1,5 @@
digraph pktio_state_machine {
rankdir=LR;
- size="9,12";
node [fontsize=28];
edge [fontsize=28];
node [shape=doublecircle]; Unallocated Ready;
diff --git a/doc/images/reflen.svg b/doc/images/reflen.svg
deleted file mode 100644
index de232903a..000000000
--- a/doc/images/reflen.svg
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" viewBox="0 0 915.17047 183.26279" stroke-miterlimit="10" id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="reflen.svg" width="915.17047" height="183.26279" style="fill:none;stroke:none;stroke-linecap:square;stroke-miterlimit:10">
- <metadata id="metadata71">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- <dc:title/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs id="defs69"/>
- <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1628" inkscape:window-height="868" id="namedview67" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="0.62083333" inkscape:cx="571.31623" inkscape:cy="459.71573" inkscape:window-x="65" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg2"/>
- <clipPath id="g188eb9bc0a_2_33.0">
- <path d="M 0,0 960,0 960,540 0,540 0,0 Z" id="path5" inkscape:connector-curvature="0" style="clip-rule:nonzero"/>
- </clipPath>
- <path style="fill:#b6d7a8;fill-rule:nonzero" inkscape:connector-curvature="0" id="path11" d="m 463.3302,61.79527 314.36218,0 0,82.55118 -314.36218,0 z"/>
- <path style="fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round" inkscape:connector-curvature="0" id="path13" d="m 463.3302,61.79527 314.36218,0 0,82.55118 -314.36218,0 z"/>
- <path style="fill:#b6d7a8;fill-rule:nonzero" inkscape:connector-curvature="0" id="path15" d="m 786.29646,61.79514 127.87402,0 0,82.55118 -127.87402,0 z"/>
- <path style="fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:2, 6" inkscape:connector-curvature="0" id="path17" d="m 786.29646,61.79514 127.87402,0 0,82.55118 -127.87402,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path19" d="m 821.96286,108.49073 0.23437,1.48437 q -0.70312,0.14063 -1.26562,0.14063 -0.90625,0 -1.40625,-0.28125 -0.5,-0.29688 -0.70313,-0.75 -0.20312,-0.46875 -0.20312,-1.98438 l 0,-5.65625 -1.23438,0 0,-1.3125 1.23438,0 0,-2.4375 1.65625,-1 0,3.4375 1.6875,0 0,1.3125 -1.6875,0 0,5.75 q 0,0.71875 0.0781,0.92188 0.0937,0.20312 0.29688,0.32812 0.20312,0.125 0.57812,0.125 0.26563,0 0.73438,-0.0781 z m 7.9646,0.28125 q -0.9375,0.79687 -1.79688,1.125 -0.85937,0.3125 -1.84375,0.3125 -1.60937,0 -2.48437,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32812,-1.32813 0.32813,-0.59375 0.85938,-0.95312 0.53125,-0.35938 1.20312,-0.54688 0.5,-0.14062 1.48438,-0.25 2.03125,-0.25 2.98437,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64062,-0.5625 -1.90625,-0.5625 -1.17187,0 -1.73437,0.40625 -0.5625,0.40625 -0.82813,1.46875 l -1.64062,-0.23438 q 0.23437,-1.04687 0.73437,-1.6875 0.51563,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26563,0 2.04688,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51562,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10938,0.60938 0.4375,1.17188 l -1.75,0 q -0.26562,-0.51563 -0.32812,-1.21875 z m -0.14063,-3.71875 q -0.90625,0.35937 -2.73437,0.625 -1.03125,0.14062 -1.45313,0.32812 -0.42187,0.1875 -0.65625,0.54688 -0.23437,0.35937 -0.23437,0.79687 0,0.67188 0.5,1.125 0.51562,0.4375 1.48437,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10938,-1.15625 0.26562,-0.57813 0.26562,-1.67188 l 0,-0.60937 z m 4.09443,-6.75 0,-1.90625 1.67187,0 0,1.90625 -1.67187,0 z m 0,11.6875 0,-9.85938 1.67187,0 0,9.85938 -1.67187,0 z m 4.09796,0 0,-13.59375 1.67187,0 0,13.59375 -1.67187,0 z m 4.16046,0 0,-9.85938 1.5,0 0,1.5 q 0.57812,-1.04687 1.0625,-1.375 0.48437,-0.34375 1.07812,-0.34375 0.84375,0 1.71875,0.54688 l -0.57812,1.54687 q -0.60938,-0.35937 -1.23438,-0.35937 -0.54687,0 -0.98437,0.32812 -0.42188,0.32813 -0.60938,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67187,0 z m 5.60327,-4.92188 q 0,-2.73437 1.53125,-4.0625 1.26563,-1.09375 3.09375,-1.09375 2.03125,0 3.3125,1.34375 1.29688,1.32813 1.29688,3.67188 0,1.90625 -0.57813,3 -0.5625,1.07812 -1.65625,1.6875 -1.07812,0.59375 -2.375,0.59375 -2.0625,0 -3.34375,-1.32813 -1.28125,-1.32812 -1.28125,-3.8125 z m 1.71875,0 q 0,1.89063 0.82813,2.82813 0.82812,0.9375 2.07812,0.9375 1.25,0 2.0625,-0.9375 0.82813,-0.95313 0.82813,-2.89063 0,-1.82812 -0.82813,-2.76562 -0.82812,-0.9375 -2.0625,-0.9375 -1.25,0 -2.07812,0.9375 -0.82813,0.9375 -0.82813,2.82812 z m 8.65698,0 q 0,-2.73437 1.53125,-4.0625 1.26563,-1.09375 3.09375,-1.09375 2.03125,0 3.3125,1.34375 1.29688,1.32813 1.29688,3.67188 0,1.90625 -0.57813,3 -0.5625,1.07812 -1.65625,1.6875 -1.07812,0.59375 -2.375,0.59375 -2.0625,0 -3.34375,-1.32813 -1.28125,-1.32812 -1.28125,-3.8125 z m 1.71875,0 q 0,1.89063 0.82813,2.82813 0.82812,0.9375 2.07812,0.9375 1.25,0 2.0625,-0.9375 0.82813,-0.95313 0.82813,-2.89063 0,-1.82812 -0.82813,-2.76562 -0.82812,-0.9375 -2.0625,-0.9375 -1.25,0 -2.07812,0.9375 -0.82813,0.9375 -0.82813,2.82812 z m 9.28198,4.92188 0,-9.85938 1.5,0 0,1.39063 q 0.45313,-0.71875 1.21875,-1.15625 0.78125,-0.45313 1.76563,-0.45313 1.09375,0 1.79687,0.45313 0.70313,0.45312 0.98438,1.28125 1.17187,-1.73438 3.04687,-1.73438 1.46875,0 2.25,0.8125 0.79688,0.8125 0.79688,2.5 l 0,6.76563 -1.67188,0 0,-6.20313 q 0,-1 -0.15625,-1.4375 -0.15625,-0.45312 -0.59375,-0.71875 -0.42187,-0.26562 -1,-0.26562 -1.03125,0 -1.71875,0.6875 -0.6875,0.6875 -0.6875,2.21875 l 0,5.71875 -1.67187,0 0,-6.40625 q 0,-1.10938 -0.40625,-1.65625 -0.40625,-0.5625 -1.34375,-0.5625 -0.70313,0 -1.3125,0.375 -0.59375,0.35937 -0.85938,1.07812 -0.26562,0.71875 -0.26562,2.0625 l 0,5.10938 -1.67188,0 z"/>
- <path style="fill:#d9ead3;fill-rule:nonzero" inkscape:connector-curvature="0" id="path21" d="m 166.25364,61.79527 298.01575,0 0,82.55118 -298.01575,0 z"/>
- <path style="fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round" inkscape:connector-curvature="0" id="path23" d="m 166.25364,61.79527 298.01575,0 0,82.55118 -298.01575,0 z"/>
- <path style="fill:#d9ead3;fill-rule:nonzero" inkscape:connector-curvature="0" id="path25" d="m 1,61.79514 156.66142,0 0,82.55118 -156.66142,0 z"/>
- <path style="fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:2, 6" inkscape:connector-curvature="0" id="path27" d="m 1,61.79514 156.66142,0 0,82.55118 -156.66142,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path29" d="m 38.57682,109.99073 0,-13.59375 1.671875,0 0,4.875 q 1.171875,-1.35938 2.953125,-1.35938 1.09375,0 1.890625,0.4375 0.8125,0.42188 1.15625,1.1875 0.359375,0.76563 0.359375,2.20313 l 0,6.25 -1.671875,0 0,-6.25 q 0,-1.25 -0.546875,-1.8125 -0.546875,-0.57813 -1.53125,-0.57813 -0.75,0 -1.40625,0.39063 -0.640625,0.375 -0.921875,1.04687 -0.28125,0.65625 -0.28125,1.8125 l 0,5.39063 -1.671875,0 z m 17.125717,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.515625,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.421875,-1.32813 -1.265625,-1.32812 -1.265625,-3.73437 0,-2.48438 1.265625,-3.85938 1.28125,-1.375 3.328125,-1.375 1.984375,0 3.234375,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.01563,0.4375 l -7.34375,0 q 0.09375,1.625 0.921875,2.48437 0.828125,0.85938 2.0625,0.85938 0.90625,0 1.546875,-0.46875 0.65625,-0.48438 1.046875,-1.54688 z m -5.484375,-2.70312 5.5,0 q -0.109375,-1.23438 -0.625,-1.85938 -0.796875,-0.96875 -2.078125,-0.96875 -1.140625,0 -1.9375,0.78125 -0.78125,0.76563 -0.859375,2.04688 z m 15.547592,4.65625 q -0.9375,0.79687 -1.796875,1.125 -0.859375,0.3125 -1.84375,0.3125 -1.609375,0 -2.484375,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.328125,-1.32813 0.328125,-0.59375 0.859375,-0.95312 0.53125,-0.35938 1.203125,-0.54688 0.5,-0.14062 1.484375,-0.25 2.03125,-0.25 2.984375,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.640625,-0.5625 -1.90625,-0.5625 -1.171875,0 -1.734375,0.40625 -0.5625,0.40625 -0.828125,1.46875 l -1.640625,-0.23438 q 0.234375,-1.04687 0.734375,-1.6875 0.515625,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.265625,0 2.046875,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.515625,1.14062 0.09375,0.42188 0.09375,1.53125 l 0,2.23438 q 0,2.32812 0.09375,2.95312 0.109375,0.60938 0.4375,1.17188 l -1.75,0 q -0.265625,-0.51563 -0.328125,-1.21875 z m -0.140625,-3.71875 q -0.90625,0.35937 -2.734375,0.625 -1.03125,0.14062 -1.453125,0.32812 -0.421875,0.1875 -0.65625,0.54688 -0.234375,0.35937 -0.234375,0.79687 0,0.67188 0.5,1.125 0.515625,0.4375 1.484375,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.109375,-1.15625 0.265625,-0.57813 0.265625,-1.67188 l 0,-0.60937 z m 10.469464,4.9375 0,-1.25 q -0.9375,1.46875 -2.75,1.46875 -1.17187,0 -2.17187,-0.64063 -0.984377,-0.65625 -1.531252,-1.8125 -0.53125,-1.17187 -0.53125,-2.6875 0,-1.46875 0.484375,-2.67187 0.5,-1.20313 1.468747,-1.84375 0.98438,-0.64063 2.20313,-0.64063 0.89062,0 1.57812,0.375 0.70313,0.375 1.14063,0.98438 l 0,-4.875 1.65625,0 0,13.59375 -1.54688,0 z m -5.28125,-4.92188 q 0,1.89063 0.79688,2.82813 0.8125,0.9375 1.89062,0.9375 1.09375,0 1.85938,-0.89063 0.76562,-0.89062 0.76562,-2.73437 0,-2.01563 -0.78125,-2.95313 -0.78125,-0.95312 -1.92187,-0.95312 -1.10938,0 -1.85938,0.90625 -0.75,0.90625 -0.75,2.85937 z m 9.25072,4.92188 0,-9.85938 1.5,0 0,1.5 q 0.57812,-1.04687 1.0625,-1.375 0.48437,-0.34375 1.07812,-0.34375 0.84375,0 1.71875,0.54688 l -0.57812,1.54687 q -0.60938,-0.35937 -1.23438,-0.35937 -0.54687,0 -0.98437,0.32812 -0.42188,0.32813 -0.60938,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67187,0 z m 5.6033,-4.92188 q 0,-2.73437 1.53125,-4.0625 1.26563,-1.09375 3.09375,-1.09375 2.03125,0 3.3125,1.34375 1.29688,1.32813 1.29688,3.67188 0,1.90625 -0.57813,3 -0.5625,1.07812 -1.65625,1.6875 -1.07812,0.59375 -2.375,0.59375 -2.0625,0 -3.34375,-1.32813 -1.28125,-1.32812 -1.28125,-3.8125 z m 1.71875,0 q 0,1.89063 0.82813,2.82813 0.82812,0.9375 2.07812,0.9375 1.25,0 2.0625,-0.9375 0.82813,-0.95313 0.82813,-2.89063 0,-1.82812 -0.82813,-2.76562 -0.82812,-0.9375 -2.0625,-0.9375 -1.25,0 -2.07812,0.9375 -0.82813,0.9375 -0.82813,2.82812 z m 8.65697,0 q 0,-2.73437 1.53125,-4.0625 1.26562,-1.09375 3.093737,-1.09375 2.03125,0 3.3125,1.34375 1.29688,1.32813 1.29688,3.67188 0,1.90625 -0.57813,3 -0.5625,1.07812 -1.65625,1.6875 -1.07812,0.59375 -2.375,0.59375 -2.062497,0 -3.343737,-1.32813 -1.28125,-1.32812 -1.28125,-3.8125 z m 1.71875,0 q 0,1.89063 0.82812,2.82813 0.82812,0.9375 2.078117,0.9375 1.25,0 2.0625,-0.9375 0.82813,-0.95313 0.82813,-2.89063 0,-1.82812 -0.82813,-2.76562 -0.82812,-0.9375 -2.0625,-0.9375 -1.249997,0 -2.078117,0.9375 -0.82812,0.9375 -0.82812,2.82812 z m 9.281957,4.92188 0,-9.85938 1.5,0 0,1.39063 q 0.45312,-0.71875 1.21875,-1.15625 0.78125,-0.45313 1.76562,-0.45313 1.09375,0 1.79688,0.45313 0.70312,0.45312 0.98437,1.28125 1.17188,-1.73438 3.04688,-1.73438 1.46875,0 2.25,0.8125 0.79687,0.8125 0.79687,2.5 l 0,6.76563 -1.67187,0 0,-6.20313 q 0,-1 -0.15625,-1.4375 -0.15625,-0.45312 -0.59375,-0.71875 -0.42188,-0.26562 -1,-0.26562 -1.03125,0 -1.71875,0.6875 -0.6875,0.6875 -0.6875,2.21875 l 0,5.71875 -1.67188,0 0,-6.40625 q 0,-1.10938 -0.40625,-1.65625 -0.40625,-0.5625 -1.34375,-0.5625 -0.70312,0 -1.3125,0.375 -0.59375,0.35937 -0.85937,1.07812 -0.26563,0.71875 -0.26563,2.0625 l 0,5.10938 -1.67187,0 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path31" d="m 322.39276,88.56955 283.0551,0 0,37.98425 -283.0551,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path33" d="m 416.94009,107.26291 0,-13.64063 1.53125,0 0,1.28125 q 0.53125,-0.75 1.20313,-1.125 0.6875,-0.375 1.64062,-0.375 1.26563,0 2.23438,0.65625 0.96875,0.64063 1.45312,1.82813 0.5,1.1875 0.5,2.59375 0,1.51562 -0.54687,2.73437 -0.54688,1.20313 -1.57813,1.84375 -1.03125,0.64063 -2.17187,0.64063 -0.84375,0 -1.51563,-0.34375 -0.65625,-0.35938 -1.07812,-0.89063 l 0,4.79688 -1.67188,0 z m 1.51563,-8.65625 q 0,1.90625 0.76562,2.8125 0.78125,0.90625 1.875,0.90625 1.10938,0 1.89063,-0.9375 0.79687,-0.9375 0.79687,-2.92188 0,-1.875 -0.78125,-2.8125 -0.76562,-0.9375 -1.84375,-0.9375 -1.0625,0 -1.89062,1 -0.8125,1 -0.8125,2.89063 z m 15.29757,3.65625 q -0.9375,0.79687 -1.79687,1.125 -0.85938,0.3125 -1.84375,0.3125 -1.60938,0 -2.48438,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32813,-1.32813 0.32812,-0.59375 0.85937,-0.95312 0.53125,-0.35938 1.20313,-0.54688 0.5,-0.14062 1.48437,-0.25 2.03125,-0.25 2.98438,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64063,-0.5625 -1.90625,-0.5625 -1.17188,0 -1.73438,0.40625 -0.5625,0.40625 -0.82812,1.46875 l -1.64063,-0.23438 q 0.23438,-1.04687 0.73438,-1.6875 0.51562,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26562,0 2.04687,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51563,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10937,0.60938 0.4375,1.17188 l -1.75,0 q -0.26563,-0.51563 -0.32813,-1.21875 z m -0.14062,-3.71875 q -0.90625,0.35937 -2.73438,0.625 -1.03125,0.14062 -1.45312,0.32812 -0.42188,0.1875 -0.65625,0.54688 -0.23438,0.35937 -0.23438,0.79687 0,0.67188 0.5,1.125 0.51563,0.4375 1.48438,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10937,-1.15625 0.26563,-0.57813 0.26563,-1.67188 l 0,-0.60937 z m 10.51635,1.32812 1.64063,0.21875 q -0.26563,1.6875 -1.375,2.65625 -1.10938,0.95313 -2.73438,0.95313 -2.01562,0 -3.25,-1.3125 -1.21875,-1.32813 -1.21875,-3.79688 0,-1.59375 0.51563,-2.78125 0.53125,-1.20312 1.60937,-1.79687 1.09375,-0.60938 2.35938,-0.60938 1.60937,0 2.625,0.8125 1.01562,0.8125 1.3125,2.3125 l -1.625,0.25 q -0.23438,-1 -0.82813,-1.5 -0.59375,-0.5 -1.42187,-0.5 -1.26563,0 -2.0625,0.90625 -0.78125,0.90625 -0.78125,2.85938 0,1.98437 0.76562,2.89062 0.76563,0.89063 1.98438,0.89063 0.98437,0 1.64062,-0.59375 0.65625,-0.60938 0.84375,-1.85938 z m 2.90625,3.60938 0,-13.59375 1.67188,0 0,7.75 3.95312,-4.01563 2.15625,0 -3.76562,3.65625 4.14062,6.20313 -2.0625,0 -3.25,-5.03125 -1.17187,1.125 0,3.90625 -1.67188,0 z m 16.0625,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 12.76633,4.375 0.23437,1.48437 q -0.70312,0.14063 -1.26562,0.14063 -0.90625,0 -1.40625,-0.28125 -0.5,-0.29688 -0.70313,-0.75 -0.20312,-0.46875 -0.20312,-1.98438 l 0,-5.65625 -1.23438,0 0,-1.3125 1.23438,0 0,-2.4375 1.65625,-1 0,3.4375 1.6875,0 0,1.3125 -1.6875,0 0,5.75 q 0,0.71875 0.0781,0.92188 0.0937,0.20312 0.29688,0.32812 0.20312,0.125 0.57812,0.125 0.26563,0 0.73438,-0.0781 z m 13.10098,1.5 0,-1.25 q -0.9375,1.46875 -2.75,1.46875 -1.17188,0 -2.17188,-0.64063 -0.98437,-0.65625 -1.53125,-1.8125 -0.53125,-1.17187 -0.53125,-2.6875 0,-1.46875 0.48438,-2.67187 0.5,-1.20313 1.46875,-1.84375 0.98437,-0.64063 2.20312,-0.64063 0.89063,0 1.57813,0.375 0.70312,0.375 1.14062,0.98438 l 0,-4.875 1.65625,0 0,13.59375 -1.54687,0 z m -5.28125,-4.92188 q 0,1.89063 0.79687,2.82813 0.8125,0.9375 1.89063,0.9375 1.09375,0 1.85937,-0.89063 0.76563,-0.89062 0.76563,-2.73437 0,-2.01563 -0.78125,-2.95313 -0.78125,-0.95312 -1.92188,-0.95312 -1.10937,0 -1.85937,0.90625 -0.75,0.90625 -0.75,2.85937 z m 15.70386,3.70313 q -0.9375,0.79687 -1.79688,1.125 -0.85937,0.3125 -1.84375,0.3125 -1.60937,0 -2.48437,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32812,-1.32813 0.32813,-0.59375 0.85938,-0.95312 0.53125,-0.35938 1.20312,-0.54688 0.5,-0.14062 1.48438,-0.25 2.03125,-0.25 2.98437,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64062,-0.5625 -1.90625,-0.5625 -1.17187,0 -1.73437,0.40625 -0.5625,0.40625 -0.82813,1.46875 l -1.64062,-0.23438 q 0.23437,-1.04687 0.73437,-1.6875 0.51563,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26563,0 2.04688,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51562,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10938,0.60938 0.4375,1.17188 l -1.75,0 q -0.26562,-0.51563 -0.32812,-1.21875 z m -0.14063,-3.71875 q -0.90625,0.35937 -2.73437,0.625 -1.03125,0.14062 -1.45313,0.32812 -0.42187,0.1875 -0.65625,0.54688 -0.23437,0.35937 -0.23437,0.79687 0,0.67188 0.5,1.125 0.51562,0.4375 1.48437,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10938,-1.15625 0.26562,-0.57813 0.26562,-1.67188 l 0,-0.60937 z m 7.73505,3.4375 0.23437,1.48437 q -0.70312,0.14063 -1.26562,0.14063 -0.90625,0 -1.40625,-0.28125 -0.5,-0.29688 -0.70313,-0.75 -0.20312,-0.46875 -0.20312,-1.98438 l 0,-5.65625 -1.23438,0 0,-1.3125 1.23438,0 0,-2.4375 1.65625,-1 0,3.4375 1.6875,0 0,1.3125 -1.6875,0 0,5.75 q 0,0.71875 0.0781,0.92188 0.0937,0.20312 0.29688,0.32812 0.20312,0.125 0.57812,0.125 0.26563,0 0.73438,-0.0781 z m 7.9646,0.28125 q -0.9375,0.79687 -1.79688,1.125 -0.85937,0.3125 -1.84375,0.3125 -1.60937,0 -2.48437,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32812,-1.32813 0.32813,-0.59375 0.85938,-0.95312 0.53125,-0.35938 1.20312,-0.54688 0.5,-0.14062 1.48438,-0.25 2.03125,-0.25 2.98437,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64062,-0.5625 -1.90625,-0.5625 -1.17187,0 -1.73437,0.40625 -0.5625,0.40625 -0.82813,1.46875 l -1.64062,-0.23438 q 0.23437,-1.04687 0.73437,-1.6875 0.51563,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26563,0 2.04688,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51562,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10938,0.60938 0.4375,1.17188 l -1.75,0 q -0.26562,-0.51563 -0.32812,-1.21875 z m -0.14063,-3.71875 q -0.90625,0.35937 -2.73437,0.625 -1.03125,0.14062 -1.45313,0.32812 -0.42187,0.1875 -0.65625,0.54688 -0.23437,0.35937 -0.23437,0.79687 0,0.67188 0.5,1.125 0.51562,0.4375 1.48437,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10938,-1.15625 0.26562,-0.57813 0.26562,-1.67188 l 0,-0.60937 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path35" d="m 346.50918,129.48167 q -1.375,-1.75 -2.32813,-4.07813 -0.95312,-2.34375 -0.95312,-4.84375 0,-2.21875 0.70312,-4.23437 0.84375,-2.34375 2.57813,-4.67188 l 1.20312,0 q -1.125,1.92188 -1.48437,2.75 -0.5625,1.28125 -0.89063,2.67188 -0.40625,1.73437 -0.40625,3.48437 0,4.46875 2.78125,8.92188 l -1.20312,0 z m 3.02518,-4 0,-13.59375 1.67187,0 0,4.875 q 1.17188,-1.35938 2.95313,-1.35938 1.09375,0 1.89062,0.4375 0.8125,0.42188 1.15625,1.1875 0.35938,0.76563 0.35938,2.20313 l 0,6.25 -1.67188,0 0,-6.25 q 0,-1.25 -0.54687,-1.8125 -0.54688,-0.57813 -1.53125,-0.57813 -0.75,0 -1.40625,0.39063 -0.64063,0.375 -0.92188,1.04687 -0.28125,0.65625 -0.28125,1.8125 l 0,5.39063 -1.67187,0 z m 17.1257,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51563,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42187,-1.32813 -1.26563,-1.32812 -1.26563,-3.73437 0,-2.48438 1.26563,-3.85938 1.28125,-1.375 3.32812,-1.375 1.98438,0 3.23438,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92188,2.48437 0.82812,0.85938 2.0625,0.85938 0.90625,0 1.54687,-0.46875 0.65625,-0.48438 1.04688,-1.54688 z m -5.48438,-2.70312 5.5,0 q -0.10937,-1.23438 -0.625,-1.85938 -0.79687,-0.96875 -2.07812,-0.96875 -1.14063,0 -1.9375,0.78125 -0.78125,0.76563 -0.85938,2.04688 z m 15.54761,4.65625 q -0.9375,0.79687 -1.79687,1.125 -0.85938,0.3125 -1.84375,0.3125 -1.60938,0 -2.48438,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32813,-1.32813 0.32812,-0.59375 0.85937,-0.95312 0.53125,-0.35938 1.20313,-0.54688 0.5,-0.14062 1.48437,-0.25 2.03125,-0.25 2.98438,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64063,-0.5625 -1.90625,-0.5625 -1.17188,0 -1.73438,0.40625 -0.5625,0.40625 -0.82812,1.46875 l -1.64063,-0.23438 q 0.23438,-1.04687 0.73438,-1.6875 0.51562,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26562,0 2.04687,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51563,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10937,0.60938 0.4375,1.17188 l -1.75,0 q -0.26563,-0.51563 -0.32813,-1.21875 z m -0.14062,-3.71875 q -0.90625,0.35937 -2.73438,0.625 -1.03125,0.14062 -1.45312,0.32812 -0.42188,0.1875 -0.65625,0.54688 -0.23438,0.35937 -0.23438,0.79687 0,0.67188 0.5,1.125 0.51563,0.4375 1.48438,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10937,-1.15625 0.26563,-0.57813 0.26563,-1.67188 l 0,-0.60937 z m 10.46945,4.9375 0,-1.25 q -0.9375,1.46875 -2.75,1.46875 -1.17188,0 -2.17188,-0.64063 -0.98437,-0.65625 -1.53125,-1.8125 -0.53125,-1.17187 -0.53125,-2.6875 0,-1.46875 0.48438,-2.67187 0.5,-1.20313 1.46875,-1.84375 0.98437,-0.64063 2.20312,-0.64063 0.89063,0 1.57813,0.375 0.70312,0.375 1.14062,0.98438 l 0,-4.875 1.65625,0 0,13.59375 -1.54687,0 z m -5.28125,-4.92188 q 0,1.89063 0.79687,2.82813 0.8125,0.9375 1.89063,0.9375 1.09375,0 1.85937,-0.89063 0.76563,-0.89062 0.76563,-2.73437 0,-2.01563 -0.78125,-2.95313 -0.78125,-0.95312 -1.92188,-0.95312 -1.10937,0 -1.85937,0.90625 -0.75,0.90625 -0.75,2.85937 z m 16.01635,1.75 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 9.09445,5.875 0,-9.85938 1.5,0 0,1.5 q 0.57813,-1.04687 1.0625,-1.375 0.48438,-0.34375 1.07813,-0.34375 0.84375,0 1.71875,0.54688 l -0.57813,1.54687 q -0.60937,-0.35937 -1.23437,-0.35937 -0.54688,0 -0.98438,0.32812 -0.42187,0.32813 -0.60937,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67188,0 z m 5.55643,-2.9375 1.65625,-0.26563 q 0.14062,1 0.76562,1.53125 0.64063,0.51563 1.78125,0.51563 1.15625,0 1.70313,-0.46875 0.5625,-0.46875 0.5625,-1.09375 0,-0.5625 -0.48438,-0.89063 -0.34375,-0.21875 -1.70312,-0.5625 -1.84375,-0.46875 -2.5625,-0.79687 -0.70313,-0.34375 -1.07813,-0.9375 -0.35937,-0.60938 -0.35937,-1.32813 0,-0.65625 0.29687,-1.21875 0.3125,-0.5625 0.82813,-0.9375 0.39062,-0.28125 1.0625,-0.48437 0.67187,-0.20313 1.4375,-0.20313 1.17187,0 2.04687,0.34375 0.875,0.32813 1.28125,0.90625 0.42188,0.5625 0.57813,1.51563 l -1.625,0.21875 q -0.10938,-0.75 -0.65625,-1.17188 -0.53125,-0.4375 -1.5,-0.4375 -1.15625,0 -1.64063,0.39063 -0.48437,0.375 -0.48437,0.875 0,0.32812 0.20312,0.59375 0.20313,0.26562 0.64063,0.4375 0.25,0.0937 1.46875,0.4375 1.76562,0.46875 2.46875,0.76562 0.70312,0.29688 1.09375,0.875 0.40625,0.57813 0.40625,1.4375 0,0.82813 -0.48438,1.57813 -0.48437,0.73437 -1.40625,1.14062 -0.92187,0.39063 -2.07812,0.39063 -1.92188,0 -2.9375,-0.79688 -1,-0.79687 -1.28125,-2.35937 z m 18.69894,0.73437 0,-3.71875 -3.70312,0 0,-1.5625 3.70312,0 0,-3.70312 1.57813,0 0,3.70312 3.6875,0 0,1.5625 -3.6875,0 0,3.71875 -1.57813,0 z m 12.56262,5.98438 0,-13.64063 1.53125,0 0,1.28125 q 0.53125,-0.75 1.20313,-1.125 0.6875,-0.375 1.64062,-0.375 1.26563,0 2.23438,0.65625 0.96875,0.64063 1.45312,1.82813 0.5,1.1875 0.5,2.59375 0,1.51562 -0.54687,2.73437 -0.54688,1.20313 -1.57813,1.84375 -1.03125,0.64063 -2.17187,0.64063 -0.84375,0 -1.51563,-0.34375 -0.65625,-0.35938 -1.07812,-0.89063 l 0,4.79688 -1.67188,0 z m 1.51563,-8.65625 q 0,1.90625 0.76562,2.8125 0.78125,0.90625 1.875,0.90625 1.10938,0 1.89063,-0.9375 0.79687,-0.9375 0.79687,-2.92188 0,-1.875 -0.78125,-2.8125 -0.76562,-0.9375 -1.84375,-0.9375 -1.0625,0 -1.89062,1 -0.8125,1 -0.8125,2.89063 z m 15.29761,3.65625 q -0.9375,0.79687 -1.79688,1.125 -0.85937,0.3125 -1.84375,0.3125 -1.60937,0 -2.48437,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32812,-1.32813 0.32813,-0.59375 0.85938,-0.95312 0.53125,-0.35938 1.20312,-0.54688 0.5,-0.14062 1.48438,-0.25 2.03125,-0.25 2.98437,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64062,-0.5625 -1.90625,-0.5625 -1.17187,0 -1.73437,0.40625 -0.5625,0.40625 -0.82813,1.46875 l -1.64062,-0.23438 q 0.23437,-1.04687 0.73437,-1.6875 0.51563,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26563,0 2.04688,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51562,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10938,0.60938 0.4375,1.17188 l -1.75,0 q -0.26562,-0.51563 -0.32812,-1.21875 z m -0.14063,-3.71875 q -0.90625,0.35937 -2.73437,0.625 -1.03125,0.14062 -1.45313,0.32812 -0.42187,0.1875 -0.65625,0.54688 -0.23437,0.35937 -0.23437,0.79687 0,0.67188 0.5,1.125 0.51562,0.4375 1.48437,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10938,-1.15625 0.26562,-0.57813 0.26562,-1.67188 l 0,-0.60937 z m 4.0007,8.73437 -0.17187,-1.5625 q 0.54687,0.14063 0.95312,0.14063 0.54688,0 0.875,-0.1875 0.34375,-0.1875 0.5625,-0.51563 0.15625,-0.25 0.5,-1.25 0.0469,-0.14062 0.15625,-0.40625 l -3.73437,-9.875 1.79687,0 2.04688,5.71875 q 0.40625,1.07813 0.71875,2.28125 0.28125,-1.15625 0.6875,-2.25 l 2.09375,-5.75 1.67187,0 -3.75,10.03125 q -0.59375,1.625 -0.9375,2.23438 -0.4375,0.82812 -1.01562,1.20312 -0.57813,0.39063 -1.375,0.39063 -0.48438,0 -1.07813,-0.20313 z m 9.375,-3.79687 0,-13.59375 1.67188,0 0,13.59375 -1.67188,0 z m 3.55109,-4.92188 q 0,-2.73437 1.53125,-4.0625 1.26563,-1.09375 3.09375,-1.09375 2.03125,0 3.3125,1.34375 1.29688,1.32813 1.29688,3.67188 0,1.90625 -0.57813,3 -0.5625,1.07812 -1.65625,1.6875 -1.07812,0.59375 -2.375,0.59375 -2.0625,0 -3.34375,-1.32813 -1.28125,-1.32812 -1.28125,-3.8125 z m 1.71875,0 q 0,1.89063 0.82813,2.82813 0.82812,0.9375 2.07812,0.9375 1.25,0 2.0625,-0.9375 0.82813,-0.95313 0.82813,-2.89063 0,-1.82812 -0.82813,-2.76562 -0.82812,-0.9375 -2.0625,-0.9375 -1.25,0 -2.07812,0.9375 -0.82813,0.9375 -0.82813,2.82812 z m 15.71948,3.70313 q -0.9375,0.79687 -1.79687,1.125 -0.85938,0.3125 -1.84375,0.3125 -1.60938,0 -2.48438,-0.78125 -0.87503,-0.79688 -0.87503,-2.03125 0,-0.73438 0.32816,-1.32813 0.32812,-0.59375 0.85937,-0.95312 0.53125,-0.35938 1.20313,-0.54688 0.5,-0.14062 1.48437,-0.25 2.03125,-0.25 2.98438,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64063,-0.5625 -1.90625,-0.5625 -1.17188,0 -1.73438,0.40625 -0.5625,0.40625 -0.82812,1.46875 l -1.64063,-0.23438 q 0.23438,-1.04687 0.73438,-1.6875 0.51562,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26562,0 2.04687,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51563,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10937,0.60938 0.4375,1.17188 l -1.75,0 q -0.26563,-0.51563 -0.32813,-1.21875 z m -0.14062,-3.71875 q -0.90625,0.35937 -2.73438,0.625 -1.03125,0.14062 -1.45312,0.32812 -0.42188,0.1875 -0.65625,0.54688 -0.23438,0.35937 -0.23438,0.79687 0,0.67188 0.5,1.125 0.51563,0.4375 1.48438,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10937,-1.15625 0.26563,-0.57813 0.26563,-1.67188 l 0,-0.60937 z m 10.46942,4.9375 0,-1.25 q -0.9375,1.46875 -2.75,1.46875 -1.17188,0 -2.17188,-0.64063 -0.98437,-0.65625 -1.53125,-1.8125 -0.53125,-1.17187 -0.53125,-2.6875 0,-1.46875 0.48438,-2.67187 0.5,-1.20313 1.46875,-1.84375 0.98437,-0.64063 2.20312,-0.64063 0.89063,0 1.57813,0.375 0.70312,0.375 1.14062,0.98438 l 0,-4.875 1.65625,0 0,13.59375 -1.54687,0 z m -5.28125,-4.92188 q 0,1.89063 0.79687,2.82813 0.8125,0.9375 1.89063,0.9375 1.09375,0 1.85937,-0.89063 0.76563,-0.89062 0.76563,-2.73437 0,-2.01563 -0.78125,-2.95313 -0.78125,-0.95312 -1.92188,-0.95312 -1.10937,0 -1.85937,0.90625 -0.75,0.90625 -0.75,2.85937 z m 17.96527,2.71875 0,-3.71875 -3.70313,0 0,-1.5625 3.70313,0 0,-3.70312 1.57812,0 0,3.70312 3.6875,0 0,1.5625 -3.6875,0 0,3.71875 -1.57812,0 z m 16.21893,0.70313 0.23438,1.48437 q -0.70313,0.14063 -1.26563,0.14063 -0.90625,0 -1.40625,-0.28125 -0.5,-0.29688 -0.70312,-0.75 -0.20313,-0.46875 -0.20313,-1.98438 l 0,-5.65625 -1.23437,0 0,-1.3125 1.23437,0 0,-2.4375 1.65625,-1 0,3.4375 1.6875,0 0,1.3125 -1.6875,0 0,5.75 q 0,0.71875 0.0781,0.92188 0.0937,0.20312 0.29687,0.32812 0.20313,0.125 0.57813,0.125 0.26562,0 0.73437,-0.0781 z m 1.51142,1.5 0,-9.85938 1.5,0 0,1.5 q 0.57812,-1.04687 1.0625,-1.375 0.48437,-0.34375 1.07812,-0.34375 0.84375,0 1.71875,0.54688 l -0.57812,1.54687 q -0.60938,-0.35937 -1.23438,-0.35937 -0.54687,0 -0.98437,0.32812 -0.42188,0.32813 -0.60938,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67187,0 z m 12.66577,-1.21875 q -0.9375,0.79687 -1.79688,1.125 -0.85937,0.3125 -1.84375,0.3125 -1.60937,0 -2.48437,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32812,-1.32813 0.32813,-0.59375 0.85938,-0.95312 0.53125,-0.35938 1.20312,-0.54688 0.5,-0.14062 1.48438,-0.25 2.03125,-0.25 2.98437,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64062,-0.5625 -1.90625,-0.5625 -1.17187,0 -1.73437,0.40625 -0.5625,0.40625 -0.82813,1.46875 l -1.64062,-0.23438 q 0.23437,-1.04687 0.73437,-1.6875 0.51563,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26563,0 2.04688,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51562,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10938,0.60938 0.4375,1.17188 l -1.75,0 q -0.26562,-0.51563 -0.32812,-1.21875 z m -0.14063,-3.71875 q -0.90625,0.35937 -2.73437,0.625 -1.03125,0.14062 -1.45313,0.32812 -0.42187,0.1875 -0.65625,0.54688 -0.23437,0.35937 -0.23437,0.79687 0,0.67188 0.5,1.125 0.51562,0.4375 1.48437,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10938,-1.15625 0.26562,-0.57813 0.26562,-1.67188 l 0,-0.60937 z m 4.09448,-6.75 0,-1.90625 1.67188,0 0,1.90625 -1.67188,0 z m 0,11.6875 0,-9.85938 1.67188,0 0,9.85938 -1.67188,0 z m 4.09797,0 0,-13.59375 1.67187,0 0,13.59375 -1.67187,0 z m 10.92608,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 9.09442,5.875 0,-9.85938 1.5,0 0,1.5 q 0.57812,-1.04687 1.0625,-1.375 0.48437,-0.34375 1.07812,-0.34375 0.84375,0 1.71875,0.54688 l -0.57812,1.54687 q -0.60938,-0.35937 -1.23438,-0.35937 -0.54687,0 -0.98437,0.32812 -0.42188,0.32813 -0.60938,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67187,0 z m 5.55646,-2.9375 1.65625,-0.26563 q 0.14062,1 0.76562,1.53125 0.64063,0.51563 1.78125,0.51563 1.15625,0 1.70313,-0.46875 0.5625,-0.46875 0.5625,-1.09375 0,-0.5625 -0.48438,-0.89063 -0.34375,-0.21875 -1.70312,-0.5625 -1.84375,-0.46875 -2.5625,-0.79687 -0.70313,-0.34375 -1.07813,-0.9375 -0.35937,-0.60938 -0.35937,-1.32813 0,-0.65625 0.29687,-1.21875 0.3125,-0.5625 0.82813,-0.9375 0.39062,-0.28125 1.0625,-0.48437 0.67187,-0.20313 1.4375,-0.20313 1.17187,0 2.04687,0.34375 0.875,0.32813 1.28125,0.90625 0.42188,0.5625 0.57813,1.51563 l -1.625,0.21875 q -0.10938,-0.75 -0.65625,-1.17188 -0.53125,-0.4375 -1.5,-0.4375 -1.15625,0 -1.64063,0.39063 -0.48437,0.375 -0.48437,0.875 0,0.32812 0.20312,0.59375 0.20313,0.26562 0.64063,0.4375 0.25,0.0937 1.46875,0.4375 1.76562,0.46875 2.46875,0.76562 0.70312,0.29688 1.09375,0.875 0.40625,0.57813 0.40625,1.4375 0,0.82813 -0.48438,1.57813 -0.48437,0.73437 -1.40625,1.14062 -0.92187,0.39063 -2.07812,0.39063 -1.92188,0 -2.9375,-0.79688 -1,-0.79687 -1.28125,-2.35937 z m 11.09375,6.9375 -1.1875,0 q 2.76562,-4.45313 2.76562,-8.92188 0,-1.73437 -0.39062,-3.45312 -0.32813,-1.39063 -0.89063,-2.67188 -0.35937,-0.84375 -1.48437,-2.78125 l 1.1875,0 q 1.75,2.32813 2.57812,4.67188 0.71875,2.01562 0.71875,4.23437 0,2.5 -0.96875,4.84375 -0.95312,2.32813 -2.32812,4.07813 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path37" d="m 85.343263,0 172.346467,0 0,28.50394 -172.346467,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path39" d="m 95.843263,21.17197 0,-13.59375 6.031247,0 q 1.8125,0 2.75,0.35937 0.95312,0.35938 1.51562,1.29688 0.5625,0.92187 0.5625,2.04687 0,1.45313 -0.9375,2.45313 -0.92187,0.98437 -2.89062,1.25 0.71875,0.34375 1.09375,0.67187 0.78125,0.73438 1.48437,1.8125 l 2.375,3.70313 -2.26562,0 -1.79688,-2.82813 q -0.79687,-1.21875 -1.3125,-1.875 -0.5,-0.65625 -0.90625,-0.90625 -0.40625,-0.26562 -0.8125,-0.35937 -0.3125,-0.0781 -1.015617,-0.0781 l -2.07812,0 0,6.04688 -1.79688,0 z m 1.79688,-7.59375 3.859367,0 q 1.23437,0 1.92187,-0.25 0.70313,-0.26563 1.0625,-0.82813 0.375,-0.5625 0.375,-1.21875 0,-0.96875 -0.70312,-1.57812 -0.70313,-0.625 -2.21875,-0.625 l -4.296867,0 0,4.5 z m 18.176057,4.42187 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 9.51634,5.875 0,-8.54688 -1.48437,0 0,-1.3125 1.48437,0 0,-1.04687 q 0,-0.98438 0.17188,-1.46875 0.23437,-0.65625 0.84375,-1.04688 0.60937,-0.40625 1.70312,-0.40625 0.70313,0 1.5625,0.15625 l -0.25,1.46875 q -0.51562,-0.0937 -0.98437,-0.0937 -0.76563,0 -1.07813,0.32813 -0.3125,0.3125 -0.3125,1.20312 l 0,0.90625 1.92188,0 0,1.3125 -1.92188,0 0,8.54688 -1.65625,0 z m 11.52705,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 9.09447,5.875 0,-9.85938 1.5,0 0,1.5 q 0.57812,-1.04687 1.0625,-1.375 0.48437,-0.34375 1.07812,-0.34375 0.84375,0 1.71875,0.54688 l -0.57812,1.54687 q -0.60938,-0.35937 -1.23438,-0.35937 -0.54687,0 -0.98437,0.32812 -0.42188,0.32813 -0.60938,0.90625 -0.28125,0.89063 -0.28125,1.95313 l 0,5.15625 -1.67187,0 z m 12.9783,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51563,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42187,-1.32813 -1.26563,-1.32812 -1.26563,-3.73437 0,-2.48438 1.26563,-3.85938 1.28125,-1.375 3.32812,-1.375 1.98438,0 3.23438,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92188,2.48437 0.82812,0.85938 2.0625,0.85938 0.90625,0 1.54687,-0.46875 0.65625,-0.48438 1.04688,-1.54688 z m -5.48438,-2.70312 5.5,0 q -0.10937,-1.23438 -0.625,-1.85938 -0.79687,-0.96875 -2.07812,-0.96875 -1.14063,0 -1.9375,0.78125 -0.78125,0.76563 -0.85938,2.04688 z m 9.1101,5.875 0,-9.85938 1.5,0 0,1.40625 q 1.09375,-1.625 3.14062,-1.625 0.89063,0 1.64063,0.32813 0.75,0.3125 1.10937,0.84375 0.375,0.51562 0.53125,1.21875 0.0937,0.46875 0.0937,1.625 l 0,6.0625 -1.67187,0 0,-6 q 0,-1.01563 -0.20313,-1.51563 -0.1875,-0.51562 -0.6875,-0.8125 -0.5,-0.29687 -1.17187,-0.29687 -1.0625,0 -1.84375,0.67187 -0.76563,0.67188 -0.76563,2.57813 l 0,5.375 -1.67187,0 z m 16.81321,-3.60938 1.64063,0.21875 q -0.26563,1.6875 -1.375,2.65625 -1.10938,0.95313 -2.73438,0.95313 -2.01562,0 -3.25,-1.3125 -1.21875,-1.32813 -1.21875,-3.79688 0,-1.59375 0.51563,-2.78125 0.53125,-1.20312 1.60937,-1.79687 1.09375,-0.60938 2.35938,-0.60938 1.60937,0 2.625,0.8125 1.01562,0.8125 1.3125,2.3125 l -1.625,0.25 q -0.23438,-1 -0.82813,-1.5 -0.59375,-0.5 -1.42187,-0.5 -1.26563,0 -2.0625,0.90625 -0.78125,0.90625 -0.78125,2.85938 0,1.98437 0.76562,2.89062 0.76563,0.89063 1.98438,0.89063 0.98437,0 1.64062,-0.59375 0.65625,-0.60938 0.84375,-1.85938 z m 9.64063,0.4375 1.71875,0.21875 q -0.40625,1.5 -1.51563,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42187,-1.32813 -1.26563,-1.32812 -1.26563,-3.73437 0,-2.48438 1.26563,-3.85938 1.28125,-1.375 3.32812,-1.375 1.98438,0 3.23438,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92188,2.48437 0.82812,0.85938 2.0625,0.85938 0.90625,0 1.54687,-0.46875 0.65625,-0.48438 1.04688,-1.54688 z m -5.48438,-2.70312 5.5,0 q -0.10937,-1.23438 -0.625,-1.85938 -0.79687,-0.96875 -2.07812,-0.96875 -1.14063,0 -1.9375,0.78125 -0.78125,0.76563 -0.85938,2.04688 z m 14.51215,5.875 0,-13.59375 5.125,0 q 1.35937,0 2.07812,0.125 1,0.17187 1.67188,0.64062 0.67187,0.46875 1.07812,1.3125 0.42188,0.84375 0.42188,1.84375 0,1.73438 -1.10938,2.9375 -1.09375,1.20313 -3.98437,1.20313 l -3.48438,0 0,5.53125 -1.79687,0 z m 1.79687,-7.14063 3.51563,0 q 1.75,0 2.46875,-0.64062 0.73437,-0.65625 0.73437,-1.82813 0,-0.85937 -0.4375,-1.46875 -0.42187,-0.60937 -1.125,-0.79687 -0.45312,-0.125 -1.67187,-0.125 l -3.48438,0 0,4.85937 z m 16.86545,5.92188 q -0.9375,0.79687 -1.79687,1.125 -0.85938,0.3125 -1.84375,0.3125 -1.60938,0 -2.48438,-0.78125 -0.875,-0.79688 -0.875,-2.03125 0,-0.73438 0.32813,-1.32813 0.32812,-0.59375 0.85937,-0.95312 0.53125,-0.35938 1.20313,-0.54688 0.5,-0.14062 1.48437,-0.25 2.03125,-0.25 2.98438,-0.57812 0,-0.34375 0,-0.4375 0,-1.01563 -0.46875,-1.4375 -0.64063,-0.5625 -1.90625,-0.5625 -1.17188,0 -1.73438,0.40625 -0.5625,0.40625 -0.82812,1.46875 l -1.64063,-0.23438 q 0.23438,-1.04687 0.73438,-1.6875 0.51562,-0.64062 1.46875,-0.98437 0.96875,-0.35938 2.25,-0.35938 1.26562,0 2.04687,0.29688 0.78125,0.29687 1.15625,0.75 0.375,0.45312 0.51563,1.14062 0.0937,0.42188 0.0937,1.53125 l 0,2.23438 q 0,2.32812 0.0937,2.95312 0.10937,0.60938 0.4375,1.17188 l -1.75,0 q -0.26563,-0.51563 -0.32813,-1.21875 z m -0.14062,-3.71875 q -0.90625,0.35937 -2.73438,0.625 -1.03125,0.14062 -1.45312,0.32812 -0.42188,0.1875 -0.65625,0.54688 -0.23438,0.35937 -0.23438,0.79687 0,0.67188 0.5,1.125 0.51563,0.4375 1.48438,0.4375 0.96875,0 1.71875,-0.42187 0.75,-0.4375 1.10937,-1.15625 0.26563,-0.57813 0.26563,-1.67188 l 0,-0.60937 z m 10.51634,1.32812 1.64062,0.21875 q -0.26562,1.6875 -1.375,2.65625 -1.10937,0.95313 -2.73437,0.95313 -2.01563,0 -3.25,-1.3125 -1.21875,-1.32813 -1.21875,-3.79688 0,-1.59375 0.51562,-2.78125 0.53125,-1.20312 1.60938,-1.79687 1.09375,-0.60938 2.35937,-0.60938 1.60938,0 2.625,0.8125 1.01563,0.8125 1.3125,2.3125 l -1.625,0.25 q -0.23437,-1 -0.82812,-1.5 -0.59375,-0.5 -1.42188,-0.5 -1.26562,0 -2.0625,0.90625 -0.78125,0.90625 -0.78125,2.85938 0,1.98437 0.76563,2.89062 0.76562,0.89063 1.98437,0.89063 0.98438,0 1.64063,-0.59375 0.65625,-0.60938 0.84375,-1.85938 z m 2.90625,3.60938 0,-13.59375 1.67187,0 0,7.75 3.95313,-4.01563 2.15626,0 -3.76564,3.65625 4.14064,6.20313 -2.06251,0 -3.25,-5.03125 -1.17188,1.125 0,3.90625 -1.67187,0 z m 16.06251,-3.17188 1.71875,0.21875 q -0.40625,1.5 -1.51562,2.34375 -1.09375,0.82813 -2.8125,0.82813 -2.15625,0 -3.42188,-1.32813 -1.26562,-1.32812 -1.26562,-3.73437 0,-2.48438 1.26562,-3.85938 1.28125,-1.375 3.32813,-1.375 1.98437,0 3.23437,1.34375 1.25,1.34375 1.25,3.79688 0,0.14062 -0.0156,0.4375 l -7.34375,0 q 0.0937,1.625 0.92187,2.48437 0.82813,0.85938 2.0625,0.85938 0.90625,0 1.54688,-0.46875 0.65625,-0.48438 1.04687,-1.54688 z m -5.48437,-2.70312 5.5,0 q -0.10938,-1.23438 -0.625,-1.85938 -0.79688,-0.96875 -2.07813,-0.96875 -1.14062,0 -1.9375,0.78125 -0.78125,0.76563 -0.85937,2.04688 z m 12.76633,4.375 0.23437,1.48437 q -0.70312,0.14063 -1.26562,0.14063 -0.90625,0 -1.40625,-0.28125 -0.5,-0.29688 -0.70313,-0.75 -0.20312,-0.46875 -0.20312,-1.98438 l 0,-5.65625 -1.23438,0 0,-1.3125 1.23438,0 0,-2.4375 1.65625,-1 0,3.4375 1.6875,0 0,1.3125 -1.6875,0 0,5.75 q 0,0.71875 0.0781,0.92188 0.0937,0.20312 0.29688,0.32812 0.20312,0.125 0.57812,0.125 0.26563,0 0.73438,-0.0781 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path41" d="m 134.49812,27.32282 32.28346,34.17325"/>
- <path style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" inkscape:connector-curvature="0" id="path43" d="m 134.49812,27.32282 28.16315,29.81174"/>
- <path style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" inkscape:connector-curvature="0" id="path45" d="m 161.46058,58.26883 4.31709,2.16455 -1.91574,-4.43314 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path47" d="m 165.9199,145.17847 0,0 c 0,4.72269 0.63806,8.55118 1.42514,8.55118 l 145.48018,0 c 0.78708,0 1.42514,3.82849 1.42514,8.55118 l 0,0 c 0,-4.72269 0.63806,-8.55118 1.42514,-8.55118 l 145.89014,0 c 0.78708,0 1.42514,-3.82849 1.42514,-8.55118 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path49" d="m 165.9199,145.17847 0,0 c 0,4.72269 0.63806,8.55118 1.42514,8.55118 l 145.48018,0 c 0.78708,0 1.42514,3.82849 1.42514,8.55118 l 0,0 c 0,-4.72269 0.63806,-8.55118 1.42514,-8.55118 l 145.89014,0 c 0.78708,0 1.42514,-3.82849 1.42514,-8.55118"/>
- <path style="fill-rule:nonzero;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" inkscape:connector-curvature="0" id="path51" d="m 165.9199,145.17847 0,0 c 0,4.72269 0.63806,8.55118 1.42514,8.55118 l 145.48018,0 c 0.78708,0 1.42514,3.82849 1.42514,8.55118 l 0,0 c 0,-4.72269 0.63806,-8.55118 1.42514,-8.55118 l 145.89014,0 c 0.78708,0 1.42514,-3.82849 1.42514,-8.55118"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path53" d="m 157.93869,157.62497 314.3622,0 0,25.63782 -314.3622,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path55" d="m 182.6591,173.3639 q 0,1.78125 -1.28125,3.04687 -1.28125,1.26563 -3.07813,1.26563 -1.8125,0 -3.09375,-1.26563 -1.26562,-1.28125 -1.26562,-3.04687 0,-1.78125 1.26562,-3.04688 1.28125,-1.26562 3.09375,-1.26562 1.79688,0 3.07813,1.26562 1.28125,1.25 1.28125,3.04688 z m -0.79688,0 q 0,-1.46875 -1.04687,-2.5 -1.03125,-1.03125 -2.51563,-1.03125 -1.48437,0 -2.53125,1.04687 -1.04687,1.03125 -1.04687,2.48438 0,1.45312 1.04687,2.5 1.04688,1.03125 2.53125,1.03125 1.48438,0 2.51563,-1.03125 1.04687,-1.04688 1.04687,-2.5 z m 11.65186,-7.64063 0,10.85938 1.04687,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -1.82812,0 0,-1.54688 q -1.35938,1.85938 -3.45313,1.85938 -1.04687,0 -2.01562,-0.5625 -0.96875,-0.5625 -1.53125,-1.59375 -0.5625,-1.04688 -0.5625,-2.15625 0,-1.10938 0.5625,-2.14063 0.5625,-1.04687 1.53125,-1.60937 0.96875,-0.5625 2.03125,-0.5625 2.04687,0 3.4375,1.85937 l 0,-4.40625 -1.04688,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82813,0 z m -0.78125,7.64063 q 0,-1.48438 -1.01563,-2.5 -1,-1.03125 -2.375,-1.03125 -1.39062,0 -2.39062,1.03125 -1,1.01562 -1,2.5 0,1.46875 1,2.5 1,1.03125 2.39062,1.03125 1.375,0 2.375,-1.03125 1.01563,-1.03125 1.01563,-2.5 z m 5.43311,-4.03125 0,1.42187 q 0.6875,-0.84375 1.48437,-1.26562 0.8125,-0.4375 1.89063,-0.4375 1.15625,0 2.125,0.54687 0.96875,0.53125 1.5,1.5 0.54687,0.95313 0.54687,2 0,1.6875 -1.20312,2.89063 -1.20313,1.1875 -2.95313,1.1875 -2.09375,0 -3.39062,-1.70313 l 0,4.6875 1.89062,0 q 0.28125,0 0.39063,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.39063,0.10937 l -3.71875,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.0937 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.04688,0 0,-10.04687 -1.04688,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.82813,0 z m 6.75,3.76562 q 0,-1.34375 -0.98438,-2.29687 -0.96875,-0.96875 -2.375,-0.96875 -1.42187,0 -2.40625,0.96875 -0.98437,0.96875 -0.98437,2.29687 0,1.35938 0.98437,2.32813 0.98438,0.95312 2.40625,0.95312 1.39063,0 2.375,-0.95312 0.98438,-0.96875 0.98438,-2.32813 z m 12.76123,9.46875 -10.35938,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.10938 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 10.35938,0 q 0.29687,0 0.40625,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.40625,0.10937 z m 3.29248,-13.23437 0,1.42187 q 0.6875,-0.84375 1.48437,-1.26562 0.8125,-0.4375 1.89063,-0.4375 1.15625,0 2.125,0.54687 0.96873,0.53125 1.49998,1.5 0.54688,0.95313 0.54688,2 0,1.6875 -1.20313,2.89063 -1.20311,1.1875 -2.95311,1.1875 -2.09375,0 -3.39062,-1.70313 l 0,4.6875 1.89062,0 q 0.28125,0 0.39063,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.39063,0.10937 l -3.71875,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.0937 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.04688,0 0,-10.04687 -1.04688,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.82813,0 z m 6.74998,3.76562 q 0,-1.34375 -0.98437,-2.29687 -0.96874,-0.96875 -2.37499,-0.96875 -1.42187,0 -2.40625,0.96875 -0.98437,0.96875 -0.98437,2.29687 0,1.35938 0.98437,2.32813 0.98438,0.95312 2.40625,0.95312 1.39063,0 2.37499,-0.95312 0.98437,-0.96875 0.98437,-2.32813 z m 9.82373,4.26563 0,-1.125 q -1.6875,1.4375 -3.625,1.4375 -1.40625,0 -2.1875,-0.70313 -0.78125,-0.71875 -0.78125,-1.75 0,-1.14062 1.03125,-1.98437 1.04688,-0.84375 3.03125,-0.84375 0.54688,0 1.17188,0.0781 0.625,0.0625 1.35937,0.21875 l 0,-1.26562 q 0,-0.64063 -0.59375,-1.10938 -0.59375,-0.48437 -1.78125,-0.48437 -0.90625,0 -2.54687,0.53125 -0.29688,0.0937 -0.375,0.0937 -0.15625,0 -0.26563,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.15625 0.0937,-0.25 0.14062,-0.14062 1.0625,-0.375 1.4375,-0.39062 2.1875,-0.39062 1.46875,0 2.29687,0.73437 0.82813,0.71875 0.82813,1.64063 l 0,5.15625 1.03125,0 q 0.29687,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10938,0.10938 -0.40625,0.10938 l -1.82813,0 z m 0,-3.875 q -0.54687,-0.15625 -1.15625,-0.23438 -0.60937,-0.0781 -1.28125,-0.0781 -1.70312,0 -2.65625,0.73437 -0.73437,0.54688 -0.73437,1.3125 0,0.70313 0.54687,1.1875 0.5625,0.48438 1.625,0.48438 1,0 1.85938,-0.40625 0.875,-0.40625 1.79687,-1.28125 l 0,-1.71875 z m 12.41748,-3.375 0,-0.25 q 0,-0.29688 0.10938,-0.40625 0.10937,-0.125 0.28125,-0.125 0.17187,0 0.28125,0.125 0.10937,0.10937 0.10937,0.40625 l 0,1.75 q 0,0.29687 -0.10937,0.42187 -0.10938,0.10938 -0.28125,0.10938 -0.15625,0 -0.26563,-0.0937 -0.10937,-0.10938 -0.125,-0.35938 -0.0625,-0.67187 -0.875,-1.26562 -0.79687,-0.59375 -2.17187,-0.59375 -1.73438,0 -2.64063,1.09375 -0.90625,1.07812 -0.90625,2.48437 0,1.51563 0.98438,2.5 1,0.98438 2.57812,0.98438 0.92188,0 1.85938,-0.32813 0.9375,-0.34375 1.70312,-1.09375 0.1875,-0.17187 0.34375,-0.17187 0.15625,0 0.26563,0.10937 0.10937,0.0937 0.10937,0.25 0,0.40625 -0.9375,1.01563 -1.51562,1 -3.375,1 -1.875,0 -3.09375,-1.20313 -1.20312,-1.20312 -1.20312,-3.04687 0,-1.89063 1.23437,-3.125 1.23438,-1.25 3.125,-1.25 1.78125,0 3,1.0625 z m 6.57373,3.78125 0,3.46875 -1.82812,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.04687,0 0,-10.07813 -1.04687,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82812,0 0,7.17188 3.3125,-2.78125 -0.40625,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 2.5,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -0.90625,0 -3.04687,2.54687 3.85937,3.92188 0.89063,0 q 0.29687,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10938,0.10938 -0.40625,0.10938 l -2.51563,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 0.54688,0 -3.34375,-3.4375 -0.89063,0.75 z m 17.19873,-0.35938 -7.95312,0 q 0.21875,1.51563 1.28125,2.4375 1.0625,0.92188 2.625,0.92188 0.875,0 1.82812,-0.28125 0.95313,-0.29688 1.5625,-0.76563 0.17188,-0.14062 0.29688,-0.14062 0.15625,0 0.26562,0.125 0.10938,0.10937 0.10938,0.26562 0,0.15625 -0.15625,0.29688 -0.4375,0.46875 -1.57813,0.875 -1.14062,0.40625 -2.32812,0.40625 -2,0 -3.34375,-1.3125 -1.34375,-1.3125 -1.34375,-3.1875 0,-1.6875 1.25,-2.90625 1.26562,-1.21875 3.125,-1.21875 1.90625,0 3.14062,1.25 1.23438,1.25 1.21875,3.23437 z m -0.79687,-0.79687 q -0.23438,-1.28125 -1.21875,-2.09375 -0.98438,-0.8125 -2.34375,-0.8125 -1.375,0 -2.35938,0.8125 -0.96875,0.79687 -1.21875,2.09375 l 7.14063,0 z m 6.3706,-3.40625 4.23438,0 q 0.28125,0 0.39062,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.39062,0.10938 l -4.23438,0 0,5.1875 q 0,0.6875 0.54688,1.14062 0.54687,0.45313 1.59375,0.45313 0.78125,0 1.70312,-0.23438 0.92188,-0.25 1.4375,-0.54687 0.1875,-0.10938 0.29688,-0.10938 0.15625,0 0.26562,0.125 0.10938,0.10938 0.10938,0.26563 0,0.14062 -0.125,0.25 -0.29688,0.3125 -1.4375,0.67187 -1.14063,0.35938 -2.1875,0.35938 -1.375,0 -2.1875,-0.64063 -0.79688,-0.64062 -0.79688,-1.73437 l 0,-5.1875 -1.4375,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.4375,0 0,-2.29688 q 0,-0.28125 0.10938,-0.40625 0.10937,-0.125 0.28125,-0.125 0.17187,0 0.28125,0.125 0.10937,0.125 0.10937,0.40625 l 0,2.29688 z m 18.18311,13.23437 -10.35938,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.10938 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 10.35938,0 q 0.29687,0 0.40625,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.40625,0.10937 z m 8.90185,-5.20312 0,-1.14063 q -1.60937,1.45313 -3.45312,1.45313 -1.14063,0 -1.73438,-0.625 -0.78125,-0.8125 -0.78125,-1.90625 l 0,-5.03125 -1.04687,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.82812,0 0,5.8125 q 0,0.76562 0.48438,1.26562 0.48437,0.48438 1.20312,0.48438 1.90625,0 3.5,-1.75 l 0,-5.03125 -1.4375,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 2.20313,0 0,7.25 0.67187,0 q 0.26563,0 0.39063,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.39063,0.10938 l -1.4375,0 z m 6.21436,-8.03125 0,1.17187 q 0.8125,-0.82812 1.46875,-1.14062 0.65625,-0.3125 1.48437,-0.3125 0.89063,0 1.625,0.375 0.51563,0.28125 0.9375,0.9375 0.42188,0.64062 0.42188,1.3125 l 0,4.90625 0.65625,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -2.07813,0 q -0.29687,0 -0.42187,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.42187,-0.10937 l 0.64063,0 0,-4.78125 q 0,-0.82813 -0.60938,-1.39063 -0.59375,-0.57812 -1.60937,-0.57812 -0.76563,0 -1.32813,0.3125 -0.5625,0.3125 -1.60937,1.54687 l 0,4.89063 0.875,0 q 0.28125,0 0.39062,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.39062,0.10938 l -2.54688,0 q -0.26562,0 -0.39062,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.39062,-0.10937 l 0.89063,0 0,-6.46875 -0.65625,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.4375,0 z m 16.4956,0.5 q 0,-0.26563 0.10938,-0.375 0.10937,-0.125 0.28125,-0.125 0.17187,0 0.28125,0.125 0.10937,0.10937 0.10937,0.40625 l 0,1.32812 q 0,0.28125 -0.10937,0.40625 -0.10938,0.10938 -0.28125,0.10938 -0.17188,0 -0.28125,-0.0937 -0.0937,-0.10938 -0.10938,-0.34375 -0.0625,-0.54688 -0.57812,-0.90625 -0.76563,-0.53125 -2.01563,-0.53125 -1.3125,0 -2.03125,0.53125 -0.54687,0.40625 -0.54687,0.89062 0,0.5625 0.64062,0.9375 0.45313,0.25 1.70313,0.39063 1.60937,0.17187 2.25,0.40625 0.89062,0.32812 1.32812,0.90625 0.45313,0.5625 0.45313,1.23437 0,0.98438 -0.95313,1.76563 -0.95312,0.78125 -2.8125,0.78125 -1.84375,0 -3.01562,-0.9375 0,0.3125 -0.0469,0.40625 -0.0312,0.0937 -0.125,0.15625 -0.0937,0.0625 -0.21875,0.0625 -0.17187,0 -0.28125,-0.10938 -0.10937,-0.125 -0.10937,-0.40625 l 0,-1.60937 q 0,-0.28125 0.10937,-0.39063 0.10938,-0.125 0.28125,-0.125 0.17188,0 0.28125,0.125 0.10938,0.10938 0.10938,0.29688 0,0.42187 0.21875,0.71875 0.32812,0.4375 1.03125,0.73437 0.71875,0.29688 1.75,0.29688 1.51562,0 2.25,-0.5625 0.75,-0.57813 0.75,-1.20313 0,-0.71875 -0.75,-1.15625 -0.76563,-0.4375 -2.21875,-0.57812 -1.45313,-0.15625 -2.07813,-0.39063 -0.625,-0.25 -0.98437,-0.73437 -0.35938,-0.48438 -0.35938,-1.03125 0,-1.01563 0.98438,-1.59375 0.98437,-0.59375 2.35937,-0.59375 1.60938,0 2.625,0.78125 z m 6.26123,-4.10938 0,4.79688 q 0.73438,-0.8125 1.40625,-1.14063 0.6875,-0.32812 1.54688,-0.32812 0.90625,0 1.53125,0.32812 0.64062,0.3125 1.0625,0.98438 0.4375,0.65625 0.4375,1.39062 l 0,4.82813 0.85937,0 q 0.29688,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.40625,0.10938 l -2.53125,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 0.875,0 0,-4.78125 q 0,-0.84375 -0.60937,-1.40625 -0.60938,-0.5625 -1.6875,-0.5625 -0.85938,0 -1.45313,0.42187 -0.4375,0.29688 -1.4375,1.39063 l 0,4.9375 0.875,0 q 0.28125,0 0.39063,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10938,0.10938 -0.39063,0.10938 l -2.54687,0 q -0.26563,0 -0.39063,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.39063,-0.10937 l 0.89062,0 0,-10.07813 -1.04687,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82812,0 z m 16.19873,11.64063 0,-1.125 q -1.6875,1.4375 -3.625,1.4375 -1.40625,0 -2.1875,-0.70313 -0.78125,-0.71875 -0.78125,-1.75 0,-1.14062 1.03125,-1.98437 1.04688,-0.84375 3.03125,-0.84375 0.54688,0 1.17188,0.0781 0.625,0.0625 1.35937,0.21875 l 0,-1.26562 q 0,-0.64063 -0.59375,-1.10938 -0.59375,-0.48437 -1.78125,-0.48437 -0.90625,0 -2.54687,0.53125 -0.29688,0.0937 -0.375,0.0937 -0.15625,0 -0.26563,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.15625 0.0937,-0.25 0.14062,-0.14062 1.0625,-0.375 1.4375,-0.39062 2.1875,-0.39062 1.46875,0 2.29687,0.73437 0.82813,0.71875 0.82813,1.64063 l 0,5.15625 1.03125,0 q 0.29687,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10938,0.10938 -0.40625,0.10938 l -1.82813,0 z m 0,-3.875 q -0.54687,-0.15625 -1.15625,-0.23438 -0.60937,-0.0781 -1.28125,-0.0781 -1.70312,0 -2.65625,0.73437 -0.73437,0.54688 -0.73437,1.3125 0,0.70313 0.54687,1.1875 0.5625,0.48438 1.625,0.48438 1,0 1.85938,-0.40625 0.875,-0.40625 1.79687,-1.28125 l 0,-1.71875 z m 8.22998,-4.15625 0,1.96875 q 1.53125,-1.375 2.28125,-1.76563 0.75,-0.40625 1.39063,-0.40625 0.70312,0 1.29687,0.46875 0.59375,0.46875 0.59375,0.71875 0,0.17188 -0.10937,0.29688 -0.10938,0.10937 -0.29688,0.10937 -0.0937,0 -0.15625,-0.0312 -0.0625,-0.0312 -0.23437,-0.21875 -0.32813,-0.3125 -0.57813,-0.4375 -0.23437,-0.125 -0.46875,-0.125 -0.5,0 -1.21875,0.40625 -0.71875,0.40625 -2.5,2 l 0,4.26563 3.45313,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -6.10938,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.26562 0,-0.15625 0.10937,-0.26563 0.10938,-0.10937 0.40625,-0.10937 l 1.875,0 0,-6.5 -1.4375,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 2.21875,0 z m 16.55811,4.20312 -7.95313,0 q 0.21875,1.51563 1.28125,2.4375 1.0625,0.92188 2.625,0.92188 0.875,0 1.82813,-0.28125 0.95312,-0.29688 1.5625,-0.76563 0.17187,-0.14062 0.29687,-0.14062 0.15625,0 0.26563,0.125 0.10937,0.10937 0.10937,0.26562 0,0.15625 -0.15625,0.29688 -0.4375,0.46875 -1.57812,0.875 -1.14063,0.40625 -2.32813,0.40625 -2,0 -3.34375,-1.3125 -1.34375,-1.3125 -1.34375,-3.1875 0,-1.6875 1.25,-2.90625 1.26563,-1.21875 3.125,-1.21875 1.90625,0 3.14063,1.25 1.23437,1.25 1.21875,3.23437 z m -0.79688,-0.79687 q -0.23437,-1.28125 -1.21875,-2.09375 -0.98437,-0.8125 -2.34375,-0.8125 -1.375,0 -2.35937,0.8125 -0.96875,0.79687 -1.21875,2.09375 l 7.14062,0 z m 11.79248,-7.01563 0,10.85938 1.04688,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -1.82813,0 0,-1.54688 q -1.35937,1.85938 -3.45312,1.85938 -1.04688,0 -2.01563,-0.5625 -0.96875,-0.5625 -1.53125,-1.59375 -0.5625,-1.04688 -0.5625,-2.15625 0,-1.10938 0.5625,-2.14063 0.5625,-1.04687 1.53125,-1.60937 0.96875,-0.5625 2.03125,-0.5625 2.04688,0 3.4375,1.85937 l 0,-4.40625 -1.04687,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82812,0 z m -0.78125,7.64063 q 0,-1.48438 -1.01562,-2.5 -1,-1.03125 -2.375,-1.03125 -1.39063,0 -2.39063,1.03125 -1,1.01562 -1,2.5 0,1.46875 1,2.5 1,1.03125 2.39063,1.03125 1.375,0 2.375,-1.03125 1.01562,-1.03125 1.01562,-2.5 z m 13.54248,9.20312 -10.35937,0 q -0.28125,0 -0.40625,-0.10937 -0.10938,-0.10938 -0.10938,-0.28125 0,-0.17188 0.10938,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 10.35937,0 q 0.29688,0 0.40625,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10937,0.10937 -0.40625,0.10937 z m 6.62061,-16.84375 0,10.85938 3.0625,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -6.90625,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 3.0625,0 0,-10.07813 -2.23438,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.10938,-0.28125 0.125,-0.10938 0.42187,-0.10938 l 3.01563,0 z m 15.22998,7.8125 -7.95313,0 q 0.21875,1.51563 1.28125,2.4375 1.0625,0.92188 2.625,0.92188 0.875,0 1.82813,-0.28125 0.95312,-0.29688 1.5625,-0.76563 0.17187,-0.14062 0.29687,-0.14062 0.15625,0 0.26563,0.125 0.10937,0.10937 0.10937,0.26562 0,0.15625 -0.15625,0.29688 -0.4375,0.46875 -1.57812,0.875 -1.14063,0.40625 -2.32813,0.40625 -2,0 -3.34375,-1.3125 -1.34375,-1.3125 -1.34375,-3.1875 0,-1.6875 1.25,-2.90625 1.26563,-1.21875 3.125,-1.21875 1.90625,0 3.14063,1.25 1.23437,1.25 1.21875,3.23437 z m -0.79688,-0.79687 q -0.23437,-1.28125 -1.21875,-2.09375 -0.98437,-0.8125 -2.34375,-0.8125 -1.375,0 -2.35937,0.8125 -0.96875,0.79687 -1.21875,2.09375 l 7.14062,0 z m 5.46436,-3.40625 0,1.17187 q 0.8125,-0.82812 1.46875,-1.14062 0.65625,-0.3125 1.48437,-0.3125 0.89063,0 1.625,0.375 0.51563,0.28125 0.9375,0.9375 0.42188,0.64062 0.42188,1.3125 l 0,4.90625 0.65625,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -2.07813,0 q -0.29687,0 -0.42187,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.42187,-0.10937 l 0.64063,0 0,-4.78125 q 0,-0.82813 -0.60938,-1.39063 -0.59375,-0.57812 -1.60937,-0.57812 -0.76563,0 -1.32813,0.3125 -0.5625,0.3125 -1.60937,1.54687 l 0,4.89063 0.875,0 q 0.28125,0 0.39062,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.39062,0.10938 l -2.54688,0 q -0.26562,0 -0.39062,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.39062,-0.10937 l 0.89063,0 0,-6.46875 -0.65625,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.4375,0 z m 13.82373,3.40625 q 0,-1.0625 0.28125,-2.25 0.28125,-1.20313 1.0625,-2.76563 0.79687,-1.57812 1.15625,-1.90625 0.10937,-0.0937 0.23437,-0.0937 0.17188,0 0.28125,0.10938 0.125,0.10937 0.125,0.26562 0,0.0937 -0.0625,0.20313 -1.03125,1.875 -1.46875,3.40625 -0.42187,1.51562 -0.42187,3.03125 0,1.53125 0.42187,3.0625 0.4375,1.51562 1.46875,3.375 0.0625,0.125 0.0625,0.20312 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.28125,0.10938 -0.125,0 -0.23437,-0.0937 -0.34375,-0.3125 -1.125,-1.84375 -0.78125,-1.53125 -1.07813,-2.70313 -0.29687,-1.1875 -0.29687,-2.39062 z m 11.71435,0.0156 q 0,1.0625 -0.28125,2.26563 -0.26562,1.1875 -1.0625,2.75 -0.78125,1.57812 -1.14062,1.89062 -0.10938,0.10938 -0.25,0.10938 -0.15625,0 -0.28125,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.0781 0.0625,-0.20312 1.01563,-1.85938 1.45313,-3.375 0.4375,-1.53125 0.4375,-3.04688 0,-1.53125 -0.4375,-3.04687 -0.4375,-1.53125 -1.45313,-3.40625 -0.0625,-0.10938 -0.0625,-0.20313 0,-0.15625 0.10938,-0.26562 0.125,-0.10938 0.28125,-0.10938 0.14062,0 0.25,0.0937 0.34375,0.32813 1.10937,1.85938 0.78125,1.51562 1.07813,2.70312 0.29687,1.17188 0.29687,2.375 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path57" d="m 169.91769,54.48294 0,0 c 0,-5.24454 0.70856,-9.49606 1.58261,-9.49606 l 300.22058,0 c 0.87406,0 1.58264,-4.25153 1.58264,-9.49607 l 0,0 c 0,5.24454 0.70856,9.49607 1.58261,9.49607 l 300.2206,0 c 0.87403,0 1.58258,4.25152 1.58258,9.49606 z"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path59" d="m 169.91769,54.48294 0,0 c 0,-5.24454 0.70856,-9.49606 1.58261,-9.49606 l 300.22058,0 c 0.87406,0 1.58264,-4.25153 1.58264,-9.49607 l 0,0 c 0,5.24454 0.70856,9.49607 1.58261,9.49607 l 300.2206,0 c 0.87403,0 1.58258,4.25152 1.58258,9.49606"/>
- <path style="fill-rule:nonzero;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" inkscape:connector-curvature="0" id="path61" d="m 169.91769,54.48294 0,0 c 0,-5.24454 0.70856,-9.49606 1.58261,-9.49606 l 300.22058,0 c 0.87406,0 1.58264,-4.25153 1.58264,-9.49607 l 0,0 c 0,5.24454 0.70856,9.49607 1.58261,9.49607 l 300.2206,0 c 0.87403,0 1.58258,4.25152 1.58258,9.49606"/>
- <path style="fill:#000000;fill-opacity:0;fill-rule:nonzero" inkscape:connector-curvature="0" id="path63" d="m 374.07254,0 202.26773,0 0,28.50394 -202.26773,0 z"/>
- <path style="fill:#000000;fill-rule:nonzero" inkscape:connector-curvature="0" id="path65" d="m 393.13504,17.17197 q 0,1.78125 -1.28125,3.04687 -1.28125,1.26563 -3.07812,1.26563 -1.8125,0 -3.09375,-1.26563 -1.26563,-1.28125 -1.26563,-3.04687 0,-1.78125 1.26563,-3.04688 1.28125,-1.26562 3.09375,-1.26562 1.79687,0 3.07812,1.26562 1.28125,1.25 1.28125,3.04688 z m -0.79687,0 q 0,-1.46875 -1.04688,-2.5 -1.03125,-1.03125 -2.51562,-1.03125 -1.48438,0 -2.53125,1.04687 -1.04688,1.03125 -1.04688,2.48438 0,1.45312 1.04688,2.5 1.04687,1.03125 2.53125,1.03125 1.48437,0 2.51562,-1.03125 1.04688,-1.04688 1.04688,-2.5 z m 11.65185,-7.64063 0,10.85938 1.04688,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -1.82813,0 0,-1.54688 q -1.35937,1.85938 -3.45312,1.85938 -1.04688,0 -2.01563,-0.5625 -0.96875,-0.5625 -1.53125,-1.59375 -0.5625,-1.04688 -0.5625,-2.15625 0,-1.10938 0.5625,-2.14063 0.5625,-1.04687 1.53125,-1.60937 0.96875,-0.5625 2.03125,-0.5625 2.04688,0 3.4375,1.85937 l 0,-4.40625 -1.04687,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82812,0 z m -0.78125,7.64063 q 0,-1.48438 -1.01562,-2.5 -1,-1.03125 -2.375,-1.03125 -1.39063,0 -2.39063,1.03125 -1,1.01562 -1,2.5 0,1.46875 1,2.5 1,1.03125 2.39063,1.03125 1.375,0 2.375,-1.03125 1.01562,-1.03125 1.01562,-2.5 z m 5.43311,-4.03125 0,1.42187 q 0.6875,-0.84375 1.48437,-1.26562 0.8125,-0.4375 1.89063,-0.4375 1.15625,0 2.125,0.54687 0.96875,0.53125 1.5,1.5 0.54687,0.95313 0.54687,2 0,1.6875 -1.20312,2.89063 -1.20313,1.1875 -2.95313,1.1875 -2.09375,0 -3.39062,-1.70313 l 0,4.6875 1.89062,0 q 0.28125,0 0.39063,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.39063,0.10937 l -3.71875,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.0937 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.04688,0 0,-10.04687 -1.04688,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.82813,0 z m 6.75,3.76562 q 0,-1.34375 -0.98438,-2.29687 -0.96875,-0.96875 -2.375,-0.96875 -1.42187,0 -2.40625,0.96875 -0.98437,0.96875 -0.98437,2.29687 0,1.35938 0.98437,2.32813 0.98438,0.95312 2.40625,0.95312 1.39063,0 2.375,-0.95312 0.98438,-0.96875 0.98438,-2.32813 z m 12.76123,9.46875 -10.35938,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.10938 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 10.35938,0 q 0.29687,0 0.40625,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.40625,0.10937 z m 3.29248,-13.23437 0,1.42187 q 0.6875,-0.84375 1.48437,-1.26562 0.8125,-0.4375 1.89063,-0.4375 1.15625,0 2.125,0.54687 0.96875,0.53125 1.5,1.5 0.54687,0.95313 0.54687,2 0,1.6875 -1.20312,2.89063 -1.20313,1.1875 -2.95313,1.1875 -2.09375,0 -3.39062,-1.70313 l 0,4.6875 1.89062,0 q 0.28125,0 0.39063,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10938,0.10937 -0.39063,0.10937 l -3.71875,0 q -0.28125,0 -0.40625,-0.10937 -0.10937,-0.0937 -0.10937,-0.28125 0,-0.17188 0.10937,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.04688,0 0,-10.04687 -1.04688,0 q -0.28125,0 -0.40625,-0.10938 -0.10937,-0.10937 -0.10937,-0.28125 0,-0.17187 0.10937,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.82813,0 z m 6.75,3.76562 q 0,-1.34375 -0.98438,-2.29687 -0.96875,-0.96875 -2.375,-0.96875 -1.42187,0 -2.40625,0.96875 -0.98437,0.96875 -0.98437,2.29687 0,1.35938 0.98437,2.32813 0.98438,0.95312 2.40625,0.95312 1.39063,0 2.375,-0.95312 0.98438,-0.96875 0.98438,-2.32813 z m 9.82373,4.26563 0,-1.125 q -1.6875,1.4375 -3.625,1.4375 -1.40625,0 -2.1875,-0.70313 -0.78125,-0.71875 -0.78125,-1.75 0,-1.14062 1.03125,-1.98437 1.04687,-0.84375 3.03125,-0.84375 0.54687,0 1.17187,0.0781 0.625,0.0625 1.35938,0.21875 l 0,-1.26562 q 0,-0.64063 -0.59375,-1.10938 -0.59375,-0.48437 -1.78125,-0.48437 -0.90625,0 -2.54688,0.53125 -0.29687,0.0937 -0.375,0.0937 -0.15625,0 -0.26562,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.15625 0.0937,-0.25 0.14063,-0.14062 1.0625,-0.375 1.4375,-0.39062 2.1875,-0.39062 1.46875,0 2.29688,0.73437 0.82812,0.71875 0.82812,1.64063 l 0,5.15625 1.03125,0 q 0.29688,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.40625,0.10938 l -1.82812,0 z m 0,-3.875 q -0.54688,-0.15625 -1.15625,-0.23438 -0.60938,-0.0781 -1.28125,-0.0781 -1.70313,0 -2.65625,0.73437 -0.73438,0.54688 -0.73438,1.3125 0,0.70313 0.54688,1.1875 0.5625,0.48438 1.625,0.48438 1,0 1.85937,-0.40625 0.875,-0.40625 1.79688,-1.28125 l 0,-1.71875 z m 12.41748,-3.375 0,-0.25 q 0,-0.29688 0.10937,-0.40625 0.10938,-0.125 0.28125,-0.125 0.17188,0 0.28125,0.125 0.10938,0.10937 0.10938,0.40625 l 0,1.75 q 0,0.29687 -0.10938,0.42187 -0.10937,0.10938 -0.28125,0.10938 -0.15625,0 -0.26562,-0.0937 -0.10938,-0.10938 -0.125,-0.35938 -0.0625,-0.67187 -0.875,-1.26562 -0.79688,-0.59375 -2.17188,-0.59375 -1.73437,0 -2.64062,1.09375 -0.90625,1.07812 -0.90625,2.48437 0,1.51563 0.98437,2.5 1,0.98438 2.57813,0.98438 0.92187,0 1.85937,-0.32813 0.9375,-0.34375 1.70313,-1.09375 0.1875,-0.17187 0.34375,-0.17187 0.15625,0 0.26562,0.10937 0.10938,0.0937 0.10938,0.25 0,0.40625 -0.9375,1.01563 -1.51563,1 -3.375,1 -1.875,0 -3.09375,-1.20313 -1.20313,-1.20312 -1.20313,-3.04687 0,-1.89063 1.23438,-3.125 1.23437,-1.25 3.125,-1.25 1.78125,0 3,1.0625 z m 6.57373,3.78125 0,3.46875 -1.82813,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.04688,0 0,-10.07813 -1.04688,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.125,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 1.82813,0 0,7.17188 3.3125,-2.78125 -0.40625,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 2.5,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -0.90625,0 -3.04688,2.54687 3.85938,3.92188 0.89062,0 q 0.29688,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.40625,0.10938 l -2.51562,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 0.54687,0 -3.34375,-3.4375 -0.89062,0.75 z m 17.19876,-0.35938 -7.95316,0 q 0.21875,1.51563 1.28125,2.4375 1.0625,0.92188 2.625,0.92188 0.875,0 1.82813,-0.28125 0.95315,-0.29688 1.56253,-0.76563 0.17187,-0.14062 0.29687,-0.14062 0.15625,0 0.26563,0.125 0.10937,0.10937 0.10937,0.26562 0,0.15625 -0.15625,0.29688 -0.4375,0.46875 -1.57815,0.875 -1.14063,0.40625 -2.32813,0.40625 -2,0 -3.34375,-1.3125 -1.34375,-1.3125 -1.34375,-3.1875 0,-1.6875 1.25,-2.90625 1.26563,-1.21875 3.125,-1.21875 1.90625,0 3.14066,1.25 1.23437,1.25 1.21875,3.23437 z m -0.79688,-0.79687 q -0.23437,-1.28125 -1.21878,-2.09375 -0.98437,-0.8125 -2.34375,-0.8125 -1.375,0 -2.35937,0.8125 -0.96875,0.79687 -1.21875,2.09375 l 7.14065,0 z m 6.37061,-3.40625 4.23437,0 q 0.28125,0 0.39063,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10938,0.10938 -0.39063,0.10938 l -4.23437,0 0,5.1875 q 0,0.6875 0.54687,1.14062 0.54688,0.45313 1.59375,0.45313 0.78125,0 1.70313,-0.23438 0.92187,-0.25 1.4375,-0.54687 0.1875,-0.10938 0.29687,-0.10938 0.15625,0 0.26563,0.125 0.10937,0.10938 0.10937,0.26563 0,0.14062 -0.125,0.25 -0.29687,0.3125 -1.4375,0.67187 -1.14062,0.35938 -2.1875,0.35938 -1.375,0 -2.1875,-0.64063 -0.79687,-0.64062 -0.79687,-1.73437 l 0,-5.1875 -1.4375,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.4375,0 0,-2.29688 q 0,-0.28125 0.10937,-0.40625 0.10938,-0.125 0.28125,-0.125 0.17188,0 0.28125,0.125 0.10938,0.125 0.10938,0.40625 l 0,2.29688 z m 18.1831,13.23437 -10.35937,0 q -0.28125,0 -0.40625,-0.10937 -0.10938,-0.10938 -0.10938,-0.28125 0,-0.17188 0.10938,-0.28125 0.125,-0.10938 0.40625,-0.10938 l 10.35937,0 q 0.29688,0 0.40625,0.10938 0.125,0.10937 0.125,0.28125 0,0.17187 -0.125,0.28125 -0.10937,0.10937 -0.40625,0.10937 z m 6.62061,-16.84375 0,10.85938 3.0625,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -6.90625,0 q -0.28125,0 -0.40625,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 3.0625,0 0,-10.07813 -2.23438,0 q -0.28125,0 -0.40625,-0.10937 -0.125,-0.10938 -0.125,-0.28125 0,-0.17188 0.10938,-0.28125 0.125,-0.10938 0.42187,-0.10938 l 3.01563,0 z m 15.22998,7.8125 -7.95313,0 q 0.21875,1.51563 1.28125,2.4375 1.0625,0.92188 2.625,0.92188 0.875,0 1.82813,-0.28125 0.95312,-0.29688 1.5625,-0.76563 0.17187,-0.14062 0.29687,-0.14062 0.15625,0 0.26563,0.125 0.10937,0.10937 0.10937,0.26562 0,0.15625 -0.15625,0.29688 -0.4375,0.46875 -1.57812,0.875 -1.14063,0.40625 -2.32813,0.40625 -2,0 -3.34375,-1.3125 -1.34375,-1.3125 -1.34375,-3.1875 0,-1.6875 1.25,-2.90625 1.26563,-1.21875 3.125,-1.21875 1.90625,0 3.14063,1.25 1.23437,1.25 1.21875,3.23437 z m -0.79688,-0.79687 q -0.23437,-1.28125 -1.21875,-2.09375 -0.98437,-0.8125 -2.34375,-0.8125 -1.375,0 -2.35937,0.8125 -0.96875,0.79687 -1.21875,2.09375 l 7.14062,0 z m 5.46436,-3.40625 0,1.17187 q 0.8125,-0.82812 1.46875,-1.14062 0.65625,-0.3125 1.48437,-0.3125 0.89063,0 1.625,0.375 0.51563,0.28125 0.9375,0.9375 0.42188,0.64062 0.42188,1.3125 l 0,4.90625 0.65625,0 q 0.28125,0 0.40625,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.125,0.10938 -0.40625,0.10938 l -2.07813,0 q -0.29687,0 -0.42187,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.17187 0.10938,-0.28125 0.125,-0.10937 0.42187,-0.10937 l 0.64063,0 0,-4.78125 q 0,-0.82813 -0.60938,-1.39063 -0.59375,-0.57812 -1.60937,-0.57812 -0.76563,0 -1.32813,0.3125 -0.5625,0.3125 -1.60937,1.54687 l 0,4.89063 0.875,0 q 0.28125,0 0.39062,0.10937 0.125,0.10938 0.125,0.28125 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.39062,0.10938 l -2.54688,0 q -0.26562,0 -0.39062,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.39062,-0.10937 l 0.89063,0 0,-6.46875 -0.65625,0 q -0.28125,0 -0.40625,-0.10938 -0.125,-0.10937 -0.125,-0.28125 0,-0.17187 0.125,-0.28125 0.125,-0.10937 0.40625,-0.10937 l 1.4375,0 z m 13.82373,3.40625 q 0,-1.0625 0.28125,-2.25 0.28125,-1.20313 1.0625,-2.76563 0.79687,-1.57812 1.15625,-1.90625 0.10937,-0.0937 0.23437,-0.0937 0.17188,0 0.28125,0.10938 0.125,0.10937 0.125,0.26562 0,0.0937 -0.0625,0.20313 -1.03125,1.875 -1.46875,3.40625 -0.42187,1.51562 -0.42187,3.03125 0,1.53125 0.42187,3.0625 0.4375,1.51562 1.46875,3.375 0.0625,0.125 0.0625,0.20312 0,0.17188 -0.125,0.28125 -0.10937,0.10938 -0.28125,0.10938 -0.125,0 -0.23437,-0.0937 -0.34375,-0.3125 -1.125,-1.84375 -0.78125,-1.53125 -1.07813,-2.70313 -0.29687,-1.1875 -0.29687,-2.39062 z m 11.71435,0.0156 q 0,1.0625 -0.28125,2.26563 -0.26562,1.1875 -1.0625,2.75 -0.78125,1.57812 -1.14062,1.89062 -0.10938,0.10938 -0.25,0.10938 -0.15625,0 -0.28125,-0.10938 -0.10938,-0.10937 -0.10938,-0.28125 0,-0.0781 0.0625,-0.20312 1.01563,-1.85938 1.45313,-3.375 0.4375,-1.53125 0.4375,-3.04688 0,-1.53125 -0.4375,-3.04687 -0.4375,-1.53125 -1.45313,-3.40625 -0.0625,-0.10938 -0.0625,-0.20313 0,-0.15625 0.10938,-0.26562 0.125,-0.10938 0.28125,-0.10938 0.14062,0 0.25,0.0937 0.34375,0.32813 1.10937,1.85938 0.78125,1.51562 1.07813,2.70312 0.29687,1.17188 0.29687,2.375 z"/>
-</svg>
diff --git a/doc/images/timeout_fsm.gv b/doc/images/timeout_fsm.gv
index 21ecb59d9..179434bce 100644
--- a/doc/images/timeout_fsm.gv
+++ b/doc/images/timeout_fsm.gv
@@ -1,6 +1,5 @@
digraph timer_state_machine {
rankdir=LR;
- size="12,20";
node [fontsize=28];
edge [fontsize=28];
node [shape=doublecircle]; TO_Unalloc;
@@ -9,17 +8,13 @@ digraph timer_state_machine {
TO_Unalloc -> TO_Alloc [label="odp_timeout_alloc()"];
TO_Alloc -> TO_Unalloc [label="odp_timeout_free()"];
TO_Alloc -> TO_Pending [fontcolor=green,
- label="odp_timer_set_abs()"];
- TO_Alloc -> TO_Pending [fontcolor=green,
- label="odp_timer_set_rel()"];
+ label="odp_timer_start()"];
TO_Pending -> TO_Alloc [fontcolor=green,
label="odp_timer_cancel()"];
TO_Pending -> TO_Enqueued [fontcolor=green, label="timer expires"];
TO_Enqueued -> TO_Delivered [label="odp_schedule()"];
TO_Delivered -> TO_Pending [fontcolor=green,
- label="odp_timer_set_abs()"];
- TO_Delivered -> TO_Pending [fontcolor=green,
- label="odp_timer_set_rel()"];
+ label="odp_timer_start()"];
TO_Delivered -> TO_Delivered [label="odp_timeout_from_event()"];
TO_Delivered -> TO_Delivered [label="odp_timeout_timer()"];
TO_Delivered -> TO_Unalloc
diff --git a/doc/images/timer_fsm.gv b/doc/images/timer_fsm.gv
index 1798d31f2..eb1df8ae9 100644
--- a/doc/images/timer_fsm.gv
+++ b/doc/images/timer_fsm.gv
@@ -1,20 +1,14 @@
digraph timer_state_machine {
rankdir=LR;
- size="12,20";
node [fontsize=28];
edge [fontsize=28];
node [shape=doublecircle]; Timer_Unalloc;
- node [shape=circle]; Timer_Alloc Timer_Set Timer_Expired
+ node [shape=circle]; Timer_Alloc Timer_Active Timer_Expired
Timer_Unalloc -> Timer_Alloc [label="odp_timer_alloc()"];
Timer_Alloc -> Timer_Unalloc [label="odp_timer_free()"];
- Timer_Alloc -> Timer_Set [fontcolor=green,label="odp_timer_set_abs()"];
- Timer_Alloc -> Timer_Set [fontcolor=green,label="odp_timer_set_rel()"];
- Timer_Set -> Timer_Alloc [fontcolor=green,label="odp_timer_cancel()"];
- Timer_Set -> Timer_Expired [fontcolor=green,label="timer expires"];
+ Timer_Alloc -> Timer_Active [fontcolor=green,label="odp_timer_start()"];
+ Timer_Active -> Timer_Alloc [fontcolor=green,label="odp_timer_cancel()"];
+ Timer_Active -> Timer_Expired [fontcolor=green,label="timer expires"];
Timer_Expired -> Timer_Unalloc [label="odp_timer_free()"];
- Timer_Expired -> Timer_Set [fontcolor=green,
- label="odp_timer_set_abs()"];
- Timer_Expired -> Timer_Set [fontcolor=green,
- label="odp_timer_set_rel()"];
-
+ Timer_Expired -> Timer_Active [fontcolor=green, label="odp_timer_start()"];
}
diff --git a/doc/implementers-guide/Makefile.am b/doc/implementers-guide/Makefile.am
index 07ee141c8..c407339e2 100644
--- a/doc/implementers-guide/Makefile.am
+++ b/doc/implementers-guide/Makefile.am
@@ -2,10 +2,12 @@ include ../Makefile.inc
SRC = implementers-guide.adoc
TARGET = implementers-guide.html
+IMAGES = $(IMAGES_DIR)/abi_llvm.svg \
+ $(IMAGES_DIR)/abi_traditional.svg
-EXTRA_DIST = $(SRC)
+EXTRA_DIST = $(SRC) $(IMAGES)
doc_DATA = $(TARGET)
-$(TARGET): $(SRC)
+$(TARGET): $(SRC) $(IMAGES)
CLEANFILES = $(doc_DATA)
diff --git a/doc/implementers-guide/implementers-guide.adoc b/doc/implementers-guide/implementers-guide.adoc
index 0e2edc0c1..c20f04906 100644
--- a/doc/implementers-guide/implementers-guide.adoc
+++ b/doc/implementers-guide/implementers-guide.adoc
@@ -75,57 +75,35 @@ with discussion of how these are done in *odp-linux*.
[[include_structure]]
== The Include Structure
-The implementers view of the include source tree allows the common interface
+The implementers view of the include source tree allows the common API
definitions and documentation to be reused by all the platforms defined in the
tree, but leave the actual definitions to be defined by the specific platform.
-The different ODP interfaces (api and drv) are defined and implemented using
-similar structures:
.Implementers include structure (in repository)
----
./
├── include/
│ ├── odp/
-│ │ ├── api/
-│ │ │ └── spec/
-│ │ │ └── The Public API specification and its documentation. <1>
-│ │ │
-│ │ └── drv/
+│ │ └── api/
│ │ └── spec/
-│ │ └── The Public Nic driver interface and its documentation. <5>
-│ │
+│ │ └── The Public API specification and its documentation. <1>
│ │
│ ├── odp_api.h This file should be the only file included by the any ODP
│ │ application. <4>
│ │
-│ └── odp_drv.h This file should be the only file included by the any ODP
-│ nic driver. <8>
-│
└── platform/
└── <implementation name>/
└── include/
├── Internal header files seen only by the implementation.
└── odp/
- ├── api/ <2>
- │ ├── In-line function definitions of the public API for this
- │ │ platform seen by the application.
- │ │
- │ └── plat/ <3>
- │ └── Platform specific types, enums etc as seen by the
- │ application but require overriding by the
- │ implementation.
- │
- ├── drv/ <6>
- │ ├── In-line function definitions of the nic driver interface
- │ │ for this platform seen by the application.
- │ │
- │ └── plat/ <7>
- │ └── Platform specific types, enums etc as seen by the
- │ nic driver but require overriding by the
- │ implementation.
- │
- └── com/
- └── Things common to both interfaces are placed here.
+ └── api/ <2>
+ ├── In-line function definitions of the public API for this
+ │ platform seen by the application.
+ │
+ └── plat/ <3>
+ └── Platform specific types, enums etc as seen by the
+ application but require overriding by the
+ implementation.
----
<1> The specification, defining the ODP application programming interface (API)
@@ -140,20 +118,6 @@ to allow the platform to provide definitions that match the underlying hardware.
<4> Applications in turn include the include/odp_api.h file which includes the
'platform/<implementation name>/include/odp/api' files to provide a complete
definition of the API.
-<5> The specification, defining the driver programming interface (drv)
-is held in 'include/odp/drv/spec/'. The interface is defined by a set of '.h'
-files including doxygen documentation.
-<6> Each public specification file is included by a counterpart in
-'platform/<implementation name>/include/odp/drv'.
-The include of the specification is AFTER the platform specific definitions
-to allow the platform to provide definitions that match the underlying hardware.
-<7> The implementation code may include files from
-'platform/<implementation name>/include/odp/drv/plat'
-<8> Nic drivers in turn include the include/odp_drv.h file which includes the
-'platform/<implementation name>/include/odp/drv' files to provide a complete
-definition of the ODP driver interface.
-
-
After ODP installation (make install), the structure becomes as follows:
@@ -162,16 +126,10 @@ After ODP installation (make install), the structure becomes as follows:
./
└── include/
├── odp/
- │ ├── api/ API In-line for this platform.
- │ │ ├── plat/ API Platform specific types.
- │ │ └── spec/ The public API specification.
- │ │
- │ └── drv/ Driver interface In-line for this platform.
- │ ├── plat/ Driver interface Platform specific types.
- │ └── spec/ The public Driver interface specification.
- │
- ├── odp_api.h
- └── odp_drv.h
+ │ └── api/ API In-line for this platform.
+ │ ├── plat/ API Platform specific types.
+ │ └── spec/ The public API specification.
+ └── odp_api.h
----
== ODP library naming recommendations
@@ -224,9 +182,9 @@ divided in two distinct areas:
This grouping defines tests that are expected to be executable and succeed on
any platform, though possibly with very different performance, depending on
the underlying platform. They are written in plain C code, and may only use
-functions defined in the standard libC (C99) library (besides the ODP
-functions being tested, of course). A free C99 specification can be found at
-the http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf[open-std.org]
+functions defined in the standard libC (C11) library (besides the ODP
+functions being tested, of course). A free C11 draft specification can be found
+at the http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf[open-std.org]
web site. No other languages (like scripting) are allowed as their usage
would make assumptions on the platform capability.
@@ -245,12 +203,12 @@ Examples of modules for the application interface includes "classification"
(functions dealing with time, excluding timers which have their own module),
timer, ...
The complete module list can be seen at:
-http://docs.opendataplane.org/master/linux-generic-doxygen-html/modules.html[ODP Modules] +
+https://www.opendataplane.org/api-documentation/master/api/modules.html[ODP Modules] +
Within the platform agnostic area, the validation tests for a given interface
are also grouped by modules, matching the ODP interface modules: for instance,
`test/common_plat/validation/api` mainly contains a list of
directories matching each module name (as defined by the doxygen `@defgroup`
-or `@ingroup` statement present in each API `.h` file).
+or `@addtogroup` statement present in each API `.h` file).
Within each of these directories, a library (called `libtest<module>.la`) and
its associated `.h` file (called `<module>.h`) defines all the test functions
@@ -362,16 +320,14 @@ TESTS = validation/api/pktio/pktio_run.sh \
$(ALL_API_VALIDATION)/crypto/crypto_main$(EXEEXT) \
$(ALL_API_VALIDATION)/errno/errno_main$(EXEEXT) \
$(ALL_API_VALIDATION)/hash/hash_main$(EXEEXT) \
- $(ALL_API_VALIDATION)/init/init_main_ok$(EXEEXT) \
- $(ALL_API_VALIDATION)/init/init_main_abort$(EXEEXT) \
- $(ALL_API_VALIDATION)/init/init_main_log$(EXEEXT) \
+ $(ALL_API_VALIDATION)/init/init_defaults$(EXEEXT) \
$(ALL_API_VALIDATION)/lock/lock_main$(EXEEXT) \
$(ALL_API_VALIDATION)/packet/packet_main$(EXEEXT) \
$(ALL_API_VALIDATION)/pool/pool_main$(EXEEXT) \
$(ALL_API_VALIDATION)/queue/queue_main$(EXEEXT) \
$(ALL_API_VALIDATION)/random/random_main$(EXEEXT) \
$(ALL_API_VALIDATION)/scheduler/scheduler_main$(EXEEXT) \
- $(ALL_API_VALIDATION)/std_clib/std_clib_main$(EXEEXT) \
+ $(ALL_API_VALIDATION)/std/std_main$(EXEEXT) \
$(ALL_API_VALIDATION)/thread/thread_main$(EXEEXT) \
$(ALL_API_VALIDATION)/time/time_main$(EXEEXT) \
$(ALL_API_VALIDATION)/timer/timer_main$(EXEEXT) \
@@ -600,9 +556,9 @@ could (hopefully) be as simple as changing the OS related helper lib.
In the linux helper, two functions are given to create and join ODP threads:
-`odph_odpthreads_create()`
+`odph_thread_create()`
-`odph_odpthreads_join()`
+`odph_thread_join()`
These two functions abstract what an ODP thread really is and their usage
is recommended as they would be implemented in other OS`s helper lib.
@@ -726,4 +682,168 @@ creating a managed binary is itself a secondary compilation and optimization
step. The difference is that performing this step is a system rather than a
developer responsibility.
+== Configuration
+Each ODP implementation will choose various sizes, limits, and similar
+internal parameters that are well matched to its design and platform
+capabilities. However, it is often useful to expose at least some of these
+parameters and allow users to select specific values to use either
+at compile time or runtime. This section discusses options for doing this,
+using the configuration options offered in the `odp-linux` reference
+implementation as an example.
+
+=== Static Configuration
+Static configuration requires the ODP implementation to be recompiled. The
+reasons for choosing static configuration vary but can involve both design
+simplicity (_e.g.,_ arrays can be statically configured) or performance
+considerations (_e.g.,_ including optional debug code). Two approaches to
+static configuration are `#define` statements and use of autotools.
+
+==== `#define` Statements
+Certain implementation limits can best be represented by `#define` statements
+that are set at compile time. Examples of this can be seen in the `odp-linux`
+reference implementation in the file
+`platform/linux-generic/include/odp_config_internal.h`.
+
+.Compile-time implementation limits (excerpt)
+[source,c]
+-----
+/*
+ * Maximum number of supported CPU identifiers. The maximum supported CPU ID is
+ * CONFIG_NUM_CPU_IDS - 1.
+ */
+#define CONFIG_NUM_CPU_IDS 256
+
+/*
+ * Maximum number of pools
+ */
+#define CONFIG_POOLS 64
+-----
+
+Here two fundamental limits, the number of CPUs supported and the maximum
+number of pools that can be created via the `odp_pool_create()` API are
+defined. By using `#define`, the implementation can configure supporting
+structures (bit strings and arrays) statically, and can also allow static
+compile-time validation/consistency checks to be done using facilities like
+`ODP_STATIC_ASSERT()`. This results in more efficient code since these limits
+need not be computed at runtime.
+
+Users are able to change these limits (potentially within documented absolute
+bounds) by changing the relevant source file and recompiling that ODP
+implementation.
+
+==== Use of `autotools configure`
+The ODP reference implementation, like many open source projects, makes use of
+https://www.gnu.org/software/automake/faq/autotools-faq.html[autotools]
+to simplify project configuration and support for various build targets.
+These same tools permit compile-time configuration options to be specified
+without requiring changes to source files.
+
+In addition to the "standard" `configure` options for specifying prefixes,
+target install paths, etc., the `odp-linux` reference implementation supports
+a large number of static configuration options that control how ODP is
+built. Use the `./configure --help` command for a complete list. Here we
+discuss simply a few for illustrative purposes:
+
+`--enable-debug`::
+The ODP API specification simply says that "results are undefined" when
+invalid parameters are passed to ODP APIs. This is done for performance
+reasons so that implementations don't need to insert extraneous parameter
+checking that would impact runtime performance in fast-path operations. While
+this is a reasonable trade off, it can complicate application debugging.
+To address this, the ODP implementation makes use of the `_ODP_ASSERT()` macro
+that by default disappears at compile time unless the `--enable-debug`
+configuration option was specified. Running with a debug build of ODP trades
+off performance for improved parameter/bounds checking to make application
+debugging easier.
+
+`--enable-user-guides`::
+By default, building ODP only builds the code. When this option is specified,
+the supporting user documentation (including this file) is also built.
+
+`--enable-abi-compat`::
+Enable ABI compatible ODP build, which permits application
+binary portability across different ODP implementations targeting the same
+Instruction Set Architecture (ISA). While this is useful in cloud/host
+environments, it does involve some performance cost to provide binary
+compatibility. For embedded use of ODP, disabling ABI compatibility means
+tighter code can be generated by inlining more of the ODP implementation
+into the calling application code. When built without ABI compatibility,
+moving an application to another ODP implementation requires that the
+application be recompiled. For most embedded uses this is a reasonable
+trade off in exchange for better application performance on a specific
+target platform.
+
+=== Dynamic Configuration
+While compile-time limits have the advantage of simplicity, they are also
+not very flexible since they require an ODP implementation to be regenerated
+to change them. The alternative is for implementations to support _dynamic
+configuration_ that enables ODP to change implementation behavior without
+source changes or recompilation.
+
+The options for dynamic configuration include: command line parameters,
+environment variables, and configuration files.
+
+==== Command line parameters
+Applications that accept a command line passed to their `main()` function can
+use this to tailor how they use ODP. This may involve self-imposed limits
+driven by the application or these can specify arguments that are to be
+passed to ODP initialization via the `odp_init_global()` API. The syntax of
+that API is:
+[source,c]
+-----
+int odp_init_global(odp_instance_t *instance,
+ const odp_init_t *params,
+ const odp_platform_init_t *platform_params);
+-----
+and the `odp_init_t` struct is used to pass platform-independent parameters
+that control ODP behavior while the `odp_platform_init_t` struct is used to
+pass platform-specific parameters. The `odp-linux` reference platform does
+not make use of these platform-specific parameters, however the `odp-dpdk`
+reference implementation uses these to allow applications to pass DPDK
+initialization parameters to it via these params.
+
+ODP itself uses the `odp_init_t` parameters to allow applications to specify
+override logging and abort functions. These routines are called to perform
+these functions on behalf of the ODP implementation, thus better allowing
+ODP to interoperate with application-defined logging and error handling
+facilities.
+
+==== Environment variables
+Linux environment variables set via the shell provide a convenient means of
+passing dynamic configuration values. Each ODP implementation defines which
+environment variables it looks for and how they are used. For example, the
+`odp-dpdk` implementation uses the variable `ODP_PLATFORM_PARAMS` as an
+alternate means of passing DPDK initialization parameters.
+
+Another important environment variable that ODP uses is `ODP_CONFIG_FILE`
+that is used to specify the file path of a _configuration override file_, as
+described in the next section.
+
+==== Configuration files
+The https://hyperrealm.github.io/libconfig/[libconfig] library provides a
+standard set of APIs and tools for parsing configuration files. ODP uses this
+to provide a range of dynamic configuration options that users may
+wish to specify.
+
+ODP uses a _base configuration file_ that contains system-wide defaults, and
+is located in the `config/odp-linux-generic.conf` file within the ODP
+distribution. This specifies a range of overridable configuration options that
+control things like shared memory usage, queue and scheduler limits and tuning
+parameters, timer processing options, as well as I/O parameters for various
+pktio classes.
+
+While users of ODP may modify this base file before building it, users can
+also supply an override configuration file that sets specific values of
+interest while leaving other parameters set to their defaults as defined by
+the base configuration file. As noted earlier, the `ODP_CONFIG_FILE`
+environment variable is used to point to the override file to be used.
+
+=== Summary
+There is a place for both static and dynamic configuration in any ODP
+implementation. This section described some of the most common and
+discussed how the ODP-supplied reference implementations make use of them.
+Other ODP implementations are free to copy and/or build on these, or use
+whatever other mechanisms are native to the platforms supported by those ODP
+implementations.
+
include::../glossary.adoc[]
diff --git a/doc/m4/configure.m4 b/doc/m4/configure.m4
index 6e02f7617..8dcb3025e 100644
--- a/doc/m4/configure.m4
+++ b/doc/m4/configure.m4
@@ -1,12 +1,4 @@
##########################################################################
-# Check for doxygen availability
-##########################################################################
-AC_CHECK_PROGS([DOXYGEN], [doxygen])
-if test -z "$DOXYGEN";
- then AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])
-fi
-
-##########################################################################
# Check for asciidoctor availability
##########################################################################
AC_CHECK_PROGS([ASCIIDOCTOR], [asciidoctor])
@@ -15,32 +7,45 @@ if test -z "$ASCIIDOCTOR";
fi
##########################################################################
+# Check for mscgen availability
+##########################################################################
+AC_CHECK_PROGS([MSCGEN], [mscgen])
+if test -z "$MSCGEN";
+ then AC_MSG_WARN([mscgen not found - continuing without sequence message support])
+fi
+
+##########################################################################
+# Check for dot availability
+##########################################################################
+AC_CHECK_PROGS([DOT], [dot])
+if test -z "$DOT";
+ then AC_MSG_WARN([dot not found - continuing without dot graphics support])
+fi
+
+##########################################################################
# Enable/disable user guide generation
##########################################################################
user_guides=no
AC_ARG_ENABLE([user-guides],
- [ --enable-user-guides generate supplemental users guides],
+ [AS_HELP_STRING([--enable-user-guides],
+ [generate supplemental users guides [default=disabled]])],
[if test "x$enableval" = "xyes"; then
if test -z "$ASCIIDOCTOR";
then AC_MSG_ERROR([cannot generate user guides without asciidoctor])
- else
- user_guides=yes
fi
+ if test -z "$MSCGEN";
+ then AC_MSG_ERROR([cannot generate user guides without mscgen])
+ fi
+ if test -z "$DOT";
+ then AC_MSG_ERROR([cannot generate user guides without dot])
+ fi
+ user_guides=yes
fi])
-##########################################################################
-# Check for mscgen availability
-##########################################################################
- AC_CHECK_PROGS([MSCGEN], [mscgen])
- if test -z "$MSCGEN";
- then AC_MSG_WARN([mscgen not found - continuing without sequence message support])
- fi
-
AC_CONFIG_FILES([doc/application-api-guide/Makefile
doc/helper-guide/Makefile
doc/implementers-guide/Makefile
doc/Makefile
doc/platform-api-guide/Makefile
doc/process-guide/Makefile
- doc/users-guide/Makefile
- doc/driver-api-guide/Makefile])
+ doc/users-guide/Makefile])
diff --git a/doc/platform-api-guide/Doxyfile b/doc/platform-api-guide/Doxyfile
index 1f2d49a4b..8fc251cd3 100644
--- a/doc/platform-api-guide/Doxyfile
+++ b/doc/platform-api-guide/Doxyfile
@@ -1,21 +1,12 @@
@INCLUDE = $(SRCDIR)/doc/Doxyfile_common
PROJECT_NAME = "API Reference Manual for $(WITH_PLATFORM)"
-PROJECT_NUMBER = $(VERSION)
+PROJECT_NUMBER = $(ODP_VERSION_API)
PROJECT_LOGO = $(SRCDIR)/doc/images/ODP-Logo-HQ.svg
INPUT = $(SRCDIR)/doc/application-api-guide \
$(SRCDIR)/doc/platform-api-guide \
+ include/odp/api \
$(SRCDIR)/include/odp/api \
$(SRCDIR)/platform/$(WITH_PLATFORM)/doc \
- $(SRCDIR)/platform/$(WITH_PLATFORM)/include/odp/api \
- $(SRCDIR)/platform/$(WITH_PLATFORM)/arch/$(WITH_ARCH)
-EXAMPLE_PATH = $(SRCDIR)/example $(SRCDIR)/platform $(SRCDIR)
-PREDEFINED = __GNUC__ \
- __attribute__(x)= \
- ODP_ALIGNED(x)= \
- __BIG_ENDIAN_BITFIELD \
- __LITTLE_ENDIAN_BITFIELD \
- __x86_64__ \
- ODP_PACKED \
- ODP_DEPRECATE(x)=x \
- "ODP_HANDLE_T(type)=odp_handle_t type"
+ $(SRCDIR)/platform/$(WITH_PLATFORM)/include/odp/api
+EXAMPLE_PATH = $(SRCDIR)/example $(SRCDIR)/platform $(SRCDIR)/CONTRIBUTING $(SRCDIR)/include/README
diff --git a/doc/platform-api-guide/Makefile.am b/doc/platform-api-guide/Makefile.am
index 27cd902ee..d1b1b0dce 100644
--- a/doc/platform-api-guide/Makefile.am
+++ b/doc/platform-api-guide/Makefile.am
@@ -3,8 +3,7 @@
#distribution.
EXTRA_DIST = \
- Doxyfile \
- $(top_srcdir)/platform/${with_platform}/doc/*.dox
+ Doxyfile
clean-local:
diff --git a/doc/process-guide/faq.adoc b/doc/process-guide/faq.adoc
index 8f0c263e1..8b3de4de7 100644
--- a/doc/process-guide/faq.adoc
+++ b/doc/process-guide/faq.adoc
@@ -27,7 +27,7 @@ in the nuances of any target platform.
At the same time, ODP is also a set of implementations of these APIs that are
optimized for each platform that supports ODP. Implementations of ODP currently
exist for a wide range of platforms spanning diverse instruction set
-architectures including ARM, MIPS, Power, x86, as well as proprietary SoC
+architectures including ARM, Power, x86, as well as proprietary SoC
architectures, and include both general-purpose servers as well as specialized
networking SoCs.
@@ -150,8 +150,8 @@ advantage of vendor extensions that have not yet been standardized.
== What platforms does ODP support?
-To date, ODP is running on seven different network platforms that span five
-different processing architectures (ARMv7, ARMv8, MIPS64, Power, and x86),
+To date, ODP is running on several different network platforms that span four
+different processing architectures (ARMv7, ARMv8, Power, and x86),
offering both application portability and accelerated performance tailored to
each platform. Other implementations are under development by both LNG member
companies and other companies participating in the project.
@@ -219,9 +219,6 @@ vendors package this capability as a NIC + ODP library. The ODP project also
supports its own ODP-DPDK [1] implementation to help migrations from the lower
level DPDK API to the ODPs abstraction.
-But traditional NICs are supported odp-linux has PKTIO support for Netmap and
-DPDK.
-
== Does ODP support polling mode?
ODP does not dictate a model, although the majority of current contributors see
diff --git a/doc/process-guide/release-guide.adoc b/doc/process-guide/release-guide.adoc
index 595af91a4..e5d72cb09 100644
--- a/doc/process-guide/release-guide.adoc
+++ b/doc/process-guide/release-guide.adoc
@@ -23,7 +23,7 @@ potentially backwards incompatible happening at lower frequency. Generation
releases are significantly less frequent. The specific digits used for the
generation and version numbering are described in the ODP API Documentation.
The roles referenced in this document are defined in the
-http://www.opendataplane.org/api-documentation/by-laws/[By-laws].
+https://www.opendataplane.org/developers/bylaws/index.html[By-laws].
=== Patch to release flow ===
Changes to the API are staged in api-next until they have been sufficiently
diff --git a/doc/users-guide/Makefile.am b/doc/users-guide/Makefile.am
index 55fe7e92c..f5386dfa9 100644
--- a/doc/users-guide/Makefile.am
+++ b/doc/users-guide/Makefile.am
@@ -2,14 +2,18 @@ include ../Makefile.inc
SRC = users-guide.adoc \
users-guide-cls.adoc \
+ users-guide-comp.adoc \
users-guide-crypto.adoc \
+ users-guide-ipsec.adoc \
users-guide-packet.adoc \
users-guide-pktio.adoc \
users-guide-timer.adoc \
- users-guide-tm.adoc
+ users-guide-tm.adoc \
+ users-guide-utilities-examples.adoc
TARGET = users-guide.html
IMAGES = $(IMAGES_DIR)/overview.svg \
$(IMAGES_DIR)/atomic_queue.svg \
+ $(IMAGES_DIR)/ipsec_sa_states.svg \
$(IMAGES_DIR)/odp_components.svg \
$(IMAGES_DIR)/ODP-Logo-HQ.svg \
$(IMAGES_DIR)/odp_rx_processing.svg \
@@ -30,9 +34,17 @@ IMAGES = $(IMAGES_DIR)/overview.svg \
$(IMAGES_DIR)/pktio_fsm.svg \
$(IMAGES_DIR)/pktout_direct_send.svg \
$(IMAGES_DIR)/pktout_queue_send.svg \
+ $(IMAGES_DIR)/ref.svg \
+ $(IMAGES_DIR)/refpkt1.svg \
+ $(IMAGES_DIR)/refpkt2.svg \
+ $(IMAGES_DIR)/refpktmulti.svg \
+ $(IMAGES_DIR)/refpktsingle.svg \
+ $(IMAGES_DIR)/refstatic.svg \
$(IMAGES_DIR)/release_git.svg \
$(IMAGES_DIR)/segment.svg \
$(IMAGES_DIR)/simple_release_git.svg \
+ $(IMAGES_DIR)/ipsec-lookaside.svg \
+ $(IMAGES_DIR)/ipsec-inline.svg \
$(IMAGES_DIR)/timeout_fsm.svg \
$(IMAGES_DIR)/timer_fsm.svg \
$(IMAGES_DIR)/tm_hierarchy.svg \
@@ -44,6 +56,7 @@ IMAGES += $(IMAGES_DIR)/resource_management.svg
endif
IMAGES_SRCS = \
+ $(IMAGES_DIR)/ipsec_sa_states.msc \
$(IMAGES_DIR)/pktio_fsm.gv \
$(IMAGES_DIR)/resource_management.msc \
$(IMAGES_DIR)/timeout_fsm.gv \
diff --git a/doc/users-guide/users-guide-cls.adoc b/doc/users-guide/users-guide-cls.adoc
index a689826c7..41badebed 100644
--- a/doc/users-guide/users-guide-cls.adoc
+++ b/doc/users-guide/users-guide-cls.adoc
@@ -7,7 +7,7 @@ prioritization, classification and scheduling of each packet, so that the
software application can run faster, scale better and adhere to QoS
requirements.
-The following API abstraction are not modelled after any existing product
+The following API abstraction are not modeled after any existing product
implementation, but is instead defined in terms of what a typical data-plane
application may require from such a platform, without sacrificing simplicity and
avoiding ambiguity. Certain terms that are being used within the context of
@@ -135,29 +135,13 @@ using ODP_POOL_INVALID or ODP_QUEUE_INVALID field then any packet assigned to th
=== Packet Classification
For each odp_pktio port, the API allows the assignment of a class-of-service to
-a packet using one of three methods:
-
-1. The packet may be assigned a specific class-of-service based on its Layer-2
-(802.1P/902.1Q VLAN tag) priority field. Since the standard field defines 8
-discrete priority levels, the API allows to assign an odp_cos to each of these
-priority levels with the `odp_cos_with_l2_priority()` function.
-
-2. Similarly, a class-of-service may be assigned using the Layer-3 (IP DiffServ)
-header field. The application supplies an array of odp_cos values that covers
-the entire range of the standard protocol header field, where array elements do
-not need to contain unique values. There is also a need to specify if Layer-3
-priority takes precedence over Layer-2 priority in a packet with both headers
-present.
-
-3. Additionally, the application may also program a number of pattern matching
-rules that assign a class-of-service for packets with header fields matching
-specified values. The field-matching rules take precedence over the previously
-described priority-based assignment of a class-of-service. Using these matching
-rules the application should be able for example to identify all packets
-containing VoIP traffic based on the protocol being UDP, and a specific
-destination or source port numbers, and appropriately assign these packets a
-class-of-service that maps to a higher priority queue, assuring voice packets a
-lower and bound latency.
+a packet. Application can program a number of pattern matching rules that
+assign a class-of-service for packets with header fields matching specified
+values. Using these matching rules the application should be able for example
+to identify all packets containing VoIP traffic based on the protocol being
+UDP, and a specific destination or source port numbers, and appropriately
+assign these packets a class-of-service that maps to a higher priority queue,
+assuring voice packets a lower and bound latency.
=== Packet meta data Elements
@@ -203,7 +187,7 @@ with either cost1, cos11, cos12. In this case the packet was subjected to two
match attempts in total.
The remaining two lines illustrate how a packet that matches pmr_match11 could
-end up wth either cos11, cos21 or cos31, depending on whether it matches
+end up with either cos11, cos21 or cos31, depending on whether it matches
pmr_march1, pmr_march2 or pmr_match3.
=== Practical example
diff --git a/doc/users-guide/users-guide-comp.adoc b/doc/users-guide/users-guide-comp.adoc
new file mode 100644
index 000000000..11a39a0c2
--- /dev/null
+++ b/doc/users-guide/users-guide-comp.adoc
@@ -0,0 +1,168 @@
+== Compression services
+ODP provides APIs to perform compression and decompression operations required
+by applications. ODP compression APIs are session based and provide
+compression algorithm offload services, with and without associated
+integrity hashing. This section covers the main compression APIs.
+
+ODP provides support for the following compression algorithms:
+
+`ODP_COMP_ALG_NONE`::
+The null compression algorithm. Used for testing as well as to
+specify hash-only operations.
+`ODP_COMP_ALG_DEFLATE`::
+The deflate compression algorithm specified by
+https://www.ietf.org/rfc/rfc1951.txt[RFC 1951].
+`ODP_COMP_ALG_ZLIB`::
+The ZLIB compression algorithm specified by
+https://www.ietf.org/rfc/rfc1950.txt[RFC 1950].
+`ODP_COMP_ALG_LZS`::
+The LZS compression algorithm as specified by ANSI X3.241.
+
+The following hash algorithms are also defined to be used in conjunction
+with these compression algorithms:
+
+`ODP_COMP_HASH_ALG_NONE`::
+A dummy that specifies no associated hashing is to be performed.
+`ODP_COMP_HASH_ALG_SHA1`::
+SHA-1 hashing with a 64-bit digest length.
+`ODP_COMP_HASH_ALG_SHA256`::
+SHA-2 hashing with a 256-bit digest length.
+
+=== Compression Sessions
+ODP compression services are session based and operate on input packets and
+deliver output packets. A compression session (`odp_comp_session_t`) provides
+the context for controlling the operations performed on packets. All of the
+packets processed by a session share the parameters that define the
+session.
+
+ODP supports synchronous and asynchronous compression sessions. For
+asynchronous sessions, the output of a compression operation is posted to
+a queue defined as the completion queue in its session parameters.
+
+Other session parameters include: the type of operation (compression or
+decompression), the operating mode (synchronous or asynchronous), the
+compression and hashing algorithms to be used, as well as any parameters
+needed by those algorithms to configure them. For asynchronous compression
+sessions, the application also specifies whether queue order must be
+maintained. Additional throughput may be achieved in some implementations if
+strict ordering is not required.
+
+The parameters that describe the characteristics of a compression session
+are encoded in the `odp_comp_session_param_t` struct that is passed to the
+`odp_comp_session_create()` API. A successful call returns an
+`odp_comp_session_t` handle that is then used as an input parameter to
+compression operation calls.
+
+When an application is finished with a compression session, the
+`odp_comp_session_destroy()` API is used to release the resources
+associated with an `odp_comp_session_t`.
+
+=== Compression operations
+After session creation, a compression operation can be applied to a packet
+in one of two ways: synchronous and asynchronous, depending on how the
+session was created.
+
+==== Synchronous compression operations
+Synchronous compression operations take the following form:
+
+.Invoking synchronous compression operations
+[source,c]
+-----
+int odp_comp_op(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[]);
+-----
+An input packet array is compressed/decompressed into a supplied output
+packet array under the control of a supplied parameter struct.
+
+The supplied `odp_comp_packet_op_param_t` struct looks as follows:
+
+.ODP compression parameter structure
+[source,c]
+-----
+/**
+ * Compression per packet operation parameters
+ */
+typedef struct odp_comp_packet_op_param_t {
+ /** Session handle */
+ odp_comp_session_t session;
+
+ /** Input data range to process. where,
+ *
+ * offset - starting offset
+ * length - length of data for compression operation
+ * */
+ odp_packet_data_range_t in_data_range;
+
+ /** Output packet data range.
+ * Indicates where processed packet will be written. where,
+ *
+ * offset - starting offset
+ * length - length of buffer available for output
+ *
+ * Output packet data is not modified outside of this provided data
+ * range. If output data length is not sufficient for compression
+ * operation ODP_COMP_STATUS_OUT_OF_SPACE_TERM error will occur
+ */
+ odp_packet_data_range_t out_data_range;
+} odp_comp_packet_op_param_t;
+-----
+Note that this struct points to the session used to control the operation and
+specifies the input and output packet data ranges to be used for the
+operation. For input, the output data range must be sufficiently sized to
+contain the result of the operation to avoid an out of space error. Upon
+output, this range is updated to reflect the actual data written. This
+information can then be used to trim off any excess padding before
+continuing processing of the output packet(s).
+
+==== Asynchronous compression operations
+Asynchronous compression operations are invoked with a slightly
+different API:
+
+.Invoking asynchronous compression operations
+[source,c]
+-----
+int odp_comp_op_enq(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[]);
+-----
+Here the session pointed to by the `odp_comp_packet_op_param_t` indicates
+the completion queue to be used for the operation, so a zero return from
+`odp_comp_op_enq()` means only that the operation was successfully
+initiated.
+
+The resulting completion queue can then be polled either directly
+via `odp_queue_deq()` or indirectly via the scheduler. The result is
+presented as an event of type `ODP_EVENT_PACKET` with subtype
+`ODP_EVENT_PACKET_COMP`.
+
+When receiving this event, the `odp_comp_packet_from_event()` API is used to
+convert the event into a usable `odp_packet_t`, and the `odp_comp_result()`
+API is used to retrieve the `odp_comp_packet_result_t` metadata associated
+with this packet. This struct looks as follows:
+
+.Compression output result
+[source,c]
+-----
+/**
+ * Compression packet operation result
+ */
+typedef struct odp_comp_packet_result_t {
+ /** Operation status code */
+ odp_comp_status_t status;
+
+ /** Input packet handle */
+ odp_packet_t pkt_in;
+
+ /** Output packet data range
+ * Specifies offset and length of data resulting from compression
+ * operation. When hashing is configured output_data_range.len equals
+ * length of output data + length of digest.
+ */
+ odp_packet_data_range_t output_data_range;
+} odp_comp_packet_result_t;
+-----
+Note that if the originating `odp_comp_op_enq()` call specified an array of
+input packets, each of these generates a separate result event. The order of
+these events on the completion queue associated with the compression session is
+controlled by the session's `packet_order` flag. If this flag is set then the
+results will be in the same order as the original input list. If not, then
+results are free to be reordered to make them available as soon as possible.
diff --git a/doc/users-guide/users-guide-crypto.adoc b/doc/users-guide/users-guide-crypto.adoc
index c18e369bb..0f33d6548 100644
--- a/doc/users-guide/users-guide-crypto.adoc
+++ b/doc/users-guide/users-guide-crypto.adoc
@@ -1,8 +1,10 @@
== Cryptographic services
-ODP provides APIs to perform cryptographic operations required by various
-communication protocols (_e.g.,_ IPsec). ODP cryptographic APIs are session
-based.
+ODP provides APIs to perform cryptographic operations required by
+applications. ODP cryptographic APIs are session based and provide
+cryptographic algorithm offload services. ODP also offers cryptographic
+protocol offload services for protocols such as IPsec using a different set
+of APIs. This section covers the main crypto APIs.
ODP provides APIs for following cryptographic services:
@@ -26,40 +28,53 @@ order of cipher and hashing can be controlled by the `auth_cipher_text`
session parameter.
Other Session parameters include algorithms, keys, initialization vector
-(optional), encode or decode, output queue for async mode and output packet
+lengths, encode or decode, output queue for async mode and output packet
pool for allocation of an output packet if required.
+The parameters that describe the characteristics of a crypto session are
+encoded in the `odp_crypto_session_param_t` struct that is passed to the
+`odp_crypto_session_create()` API. A successful call returns an
+`odp_crypto_session_t` object that in turn is passed as an input parameter to
+crypto operation calls.
+
+When an application is finished with a crypto session the
+`odp_crypto_session_destroy()` API is used to release the resources associated
+with an `odp_crypto_session_t`.
+
=== Crypto operations
After session creation, a cryptographic operation can be applied to a packet
-using the `odp_crypto_operation()` API. Applications may indicate a preference
-for synchronous or asynchronous processing in the session's `pref_mode`
-parameter. However crypto operations may complete synchronously even if an
-asynchronous preference is indicated, and applications must examine the
-`posted` output parameter from `odp_crypto_operation()` to determine whether
-the operation has completed or if an `ODP_EVENT_CRYPTO_COMPL` notification is
-expected. In the case of an async operation, the `posted` output parameter
-will be set to true.
-
-
-The operation arguments specify for each packet the areas that are to be
-encrypted or decrypted and authenticated. Also, there is an option of overriding
-the initialization vector specified in session parameters.
-
-An operation can be executed in in-place, out-of-place or new buffer mode.
-In in-place mode output packet is same as the input packet.
-In case of out-of-place mode output packet is different from input packet as
-specified by the application, while in new buffer mode implementation allocates
-a new output buffer from the session’s output pool.
-
-The application can also specify a context associated with a given operation
-that will be retained during async operation and can be retrieved via the
-completion event.
-
-Results of an asynchronous session will be posted as completion events to the
-session’s completion queue, which can be accessed directly or via the ODP
-scheduler. The completion event contains the status of the operation and the
-result. The application has the responsibility to free the completion event.
+synchronously or asynchronously. `odp_crypto_op()` is the synchronous API
+while `odp_crypto_op_enq()` is the asynchronous API. To check which of these
+are supported by the ODP implementation, examine the `sync_mode` and
+`async_mode` fields in the `odp_crypto_capability_t` struct returned by the
+`odp_crypto_capability()` API.
+
+Both forms take an input array of packets, an optional output array of packets
+to receive the results, and an array of `odp_crypto_packet_op_param_t` structs
+that describe the operation to be performed on each input packet. The output
+array may be the same packets to request in-place operation, or may be
+specified as `ODP_PACKET_INVALID` to request that ODP allocate output packets
+from the pool associated with the `odp_crypto_session_t` being used.
+
+The op_mode field of `odp_crypto_session_t` indicates whether asynchronous
+or synchronous operations are used with the session. If `op_mode` is set
+to `ODP_CRYPTO_SYNC` then the synchronous API must be used and if `op_mode`
+is set to `ODP_CRYPTO_ASYNC` then the asynchronous API must be used. It is
+an error to use a form of the API that does not match the mode of the crypto
+session.
+
+The output of a crypto operation is an `odp_packet_t` (one for each input
+packet) that is returned either synchronously or asynchronously. Asynchronous
+return is in the form of `ODP_EVENT_PACKET` events that have event subtype
+`ODP_EVENT_PACKET_CRYPTO`. The packet associated with such events is obtained
+via the `odp_crypto_packet_from_event()` API. The `odp_crypto_result()` API,
+in turn, retrieves the `odp_crypto_packet_result_t` from this `odp_packet_t`
+that contains:
+
+* An indication of whether the crypto operation was successful or not
+* The `odp_crypto_op_status_t` for the requested cipher operation
+* The `odp_crypto_op_status_t` for the requested authentication operation
=== Random number Generation
@@ -79,7 +94,7 @@ any software generated pseudo-random data. May not be available on all
platforms.
These form a hierarchy with BASIC being the lowest kind of random and TRUE
-behing the highest. The main API for accessing random data is:
+being the highest. The main API for accessing random data is:
[source,c]
-----
diff --git a/doc/users-guide/users-guide-ipsec.adoc b/doc/users-guide/users-guide-ipsec.adoc
new file mode 100644
index 000000000..6af676620
--- /dev/null
+++ b/doc/users-guide/users-guide-ipsec.adoc
@@ -0,0 +1,467 @@
+== IPsec services
+
+In addition to general cryptographic services, ODP offers offload support for
+the IPsec protocol. IPsec is a general term referencing a suite of protocols
+and packet formats and as such a full discussion of IPsec is beyond the scope
+of this document. See https://tools.ietf.org/html/rfc4301[RFC 4301] and
+related RFCs for more detail. This section assumes the reader is already
+familiar with IPsec and focuses on explaining the ODP APIs that support it.
+
+ODP provides APIs for the following IPsec services:
+
+* General IPsec configuration
+* Security Association (SA) configuration and lifecycle management
+* Synchronous and Asynchronous IPsec lookaside processing
+* Inline processing for full IPsec RX and/or TX offload
+* Pipelining for RX traffic
+* Fragmentation support for TX traffic
+* IPsec event management
+
+=== IPsec Capabilities and Configuration
+As with other features, ODP provides APIs that permit applications to query
+platform-specific IPsec capabilities. The `odp_ipsec_capability()` API queries
+the general IPsec features available while the `odp_ipsec_cipher_capability()`
+and `odp_ipsec_auth_capability()` APIs provide detail on the range of
+cipher and authentication algorithms supported by IPsec on this platform.
+
+General IPsec capabilities that are reported include:
+
+* The IPsec operation modes supported by this implementation. Different
+operation modes may be _not supported_, _supported_, or _preferred_. A
+preferred form means that this mode takes advantage of hardware
+acceleration features to achieve best performance.
+* Whether IPsec AH processing is supported. All ODP platforms must provide
+support for IPsec ESP processing, however since AH is relatively rare, it
+may not be supported, or supported only via software emulation (_e.g.,_ be
+non-preferred).
+* Whether IPsec headers can be retained on decrypt for inbound inline
+operations.
+* Whether classification pipelining is supported (to be discussed below).
+
+In addition, capabilities also inform the application of the maximum number
+of destination queues and classification CoS targets supported. These
+will be discussed further later.
+
+==== IPsec Operation Modes
+IPsec operates in one of three modes: Synchronous, Asynchronous, and Inline.
+
+==== Lookaside Processing
+Synchronous and Asynchronous are types of _lookaside_ processing. Which of
+these forms may be used depends on the IPsec operation mode. So synchronous
+APIs may only be used when operating in synchronous mode, and asynchronous
+APIs may only be used when operating in asynchronous mode.
+
+In lookaside mode, the application receives (or creates) an IPsec packet and
+then uses ODP to perform one of two functions:
+
+* To decrypt an IPsec packet into a "normal" packet
+* To take a "normal" packet and encrypt it into an IPsec packet.
+
+This process may be performed _synchronously_ with the APIs `odp_ipsec_in()`
+(to decrypt) and `odp_ipsec_out()` (to encrypt). Upon return from these calls
+the requested packet transformation is complete, or an error return code
+indicates that it could not be performed (_e.g.,_ packet decryption failed).
+
+Synchronous processing may be preferred if the application has a large number
+of worker threads so that blocking any individual worker while IPsec processing
+is performed represents a reasonable design. The alternative is to use
+_asynchronous_ forms of these APIs:
+
+* `odp_ipsec_in_enq()` for decrypt
+* `odp_ipsec_out_enq()` for encrypt
+
+These simply pass packets to IPsec for processing. When this processing is
+complete, the resulting packets are sent to the completion queue associated
+with the SA used by the operation, serving as IPsec completion events as
+shown here:
+
+image::ipsec-lookaside.svg[align="center"]
+
+If the operation fails because SA lookup failed for inbound processing, then
+these result packets are sent to the default queue specified as part of the
+`odp_ipsec_inbound_config_t` used in the `odp_ipsec_config()` call.
+
+Following an asynchronous IPsec call, the worker thread moves on to process
+other events until the IPsec completion shows up. At that point the worker
+thread sees whether the operation was successful or not and continues
+processing for that packet. These events may be direct-polled with
+`odp_queue_deq()` if the completion queue was created as a plain queue, or
+processed via the ODP scheduler if the completion queue was created as a
+scheduled queue.
+
+==== Inline Processing
+While lookaside processing offers flexibility, it still requires extra
+processing steps not required by modern hardware. To avoid this overhead
+ODP also offers _inline_ processing support for IPsec. In this mode the
+processing of IPsec packets on the RX and TX paths is fully offloaded as
+shown here:
+
+image::ipsec-inline.svg[align="center"]
+
+It is worth noting that, depending on the implementation and application
+needs, inline processing may be enabled only for one direction (inbound or
+outbound) or for both directions.
+
+On the receive side, once configured for inline processing, arriving IPsec
+packets that are recognized at the PktIO interface are decrypted automatically
+before the application ever sees them. On the transmit side, the application
+calls `odp_ipsec_out_inline()` and the packet is encrypted and queued for
+transmission as a single operation without further application involvement.
+Note that if an inbound IPsec packet is not recognized (_e.g.,_ it belongs to
+an unknown SA) then it will be presented to the application as-is without
+further processing. The application may then use a lookaside call to process
+the packet if it is able to supply a matching SA by other means.
+
+On the receive side, after an IPsec packet is decrypted, it may be
+_pipelined_ to the ODP classifier or added to a poll queue, as the
+application wishes. The advantage of classification pipelining is that inbound
+IPsec traffic is automatically decrypted and classified into appropriate
+flow-based queues for ease of processing.
+
+On the transmit side, since IPsec encryption and tunneling may exceed an
+output MTU, ODP also offers support for MTU configuration and automatic IPsec
+TX fragmentation.
+
+Both classification pipelining and TX fragmentation support are support
+features that are indicated by `odp_ipsec_capability()`.
+
+Note that at present inline IPsec output support sends resulting packets
+directly to an output PktIO. If it's desired to send them to the ODP
+Traffic Manager for shaping prior to transmission, use the lookaside APIs
+to perform the IPsec encrypt and then call `odp_tm_enq()` on the resulting
+packet.
+
+=== IPsec Configuration
+Prior to making use of IPsec services, the `odp_ipsec_config()` API is used to
+configure IPsec processing options. This API takes a pointer to an
+`odp_ipsec_config_t` struct as its argument.
+
+The `odp_ipsec_config_t` struct specifies the inbound and outbound processing
+modes (SYNC, ASYNC, or INLINE) that the application plans to use, the maximum
+number of Security Associations it will use, and sets inbound and outbound
+processing options.
+
+==== IPsec Inbound Configuration
+Inbound configuration options for IPsec specify the default `odp_queue_t` to
+be used for processing global events like SA lookup failures, how Security
+Parameter Index (SPI) lookup is to be performed, and whether the application
+requires ODP to retain outer headers for decrypted IPsec packets.
+
+Parsing options specify how "deep" decrypted packets are to be parsed
+after IPsec processing by specifying the packet layers of interest to the
+application (None, L2, L3, L4, or All). And which checksums should be verified
+on decrypted packets.
+
+==== IPsec Outbound Configuration
+Outbound configuration options for IPsec specify checksum insertion processing
+that should be performed prior to encryption.
+
+=== IPsec Events
+IPsec introduces one new event type and one new event subtype. These are:
+
+* IPsec packet events. These are events of type `ODP_EVENT_PACKET` that have
+subtype `ODP_EVENT_PACKET_IPSEC`. These are packets that carry additional
+IPsec-related metadata in the form of an `odp_ipsec_packet_result_t` struct
+that can be retrieved from the packet via the `odp_ipsec_result()` API.
+
+* IPsec status notifications. These are events of type `ODP_EVENT_IPSEC_STATUS`
+that indicate status events not associated with any particular IPsec
+packet. Such events carry status in the form of an `odp_ipsec_status_t`
+struct that is retrieved from the event via the `odp_ipsec_status()` API.
+
+IPsec-related events are thus part of normal and exception processing when
+working with IPsec.
+
+=== Security Associations (SAs)
+The fundamental "building block" for IPsec processing is the _Security
+Association (SA)_. Similar to a crypto session, the SA encapsulates the keying
+material and context needed to perform IPsec protocol processing for inbound
+or outbound packets on a given flow, as well as additional processing options
+that control how IPsec is to be used for packets processed under this
+SA. Security Associations are unidirectional (RX or TX) so a flow that
+requires both inbound (decrypt) and outbound (encrypt) IPsec functions will
+have two SAs associated with it. SAs in ODP are represented by the
+abstract type `odp_ipsec_sa_t`.
+
+After ODP initialization, IPsec support is dormant until it is configured
+by a call to `odp_ipsec_config()` as described earlier. Once configured,
+SAs may be created by calling `odp_ipsec_sa_create()`.
+
+==== SA Creation and Configuration
+The `odp_ipsec_sa_create()` API takes an `odp_ipsec_sa_param_t` argument that
+describes the SA to be created. Use the `odp_ipsec_sa_param_init()` API to
+initialize this to its default state and then override selected fields within
+the param struct as needed.
+
+Items specified in the `odp_ipsec_sa_param_t` struct include:
+
+* The direction of the SA (inbound or outbound).
+
+* The IPsec protocol being used (ESP or AH).
+
+* The IPsec protocol mode (Transport or Tunnel).
+
+* The parameters needed for the crypto and authentication algorithms to be
+used by this SA.
+
+* Miscellaneous SA options that control behavior such as use of Extended
+Sequence Numbers (ESNs), the use of UDP encapsulation, various copy
+options for header fields, and whether the TTL (Hop Limit) field should be
+decremented when operating in tunnel mode.
+
+* Parameters controlling the SA lifetime.
+
+* The Security Parameter Index (SPI) that packets will use to indicate that
+they belong to this SA.
+
+* The pipeline mode used by this SA.
+
+* The destination `odp_queue_t` to be used for events associated with this SA.
+
+* The user context pointer (and length) associated with this SA for
+application use.
+
+In addition, there are specific direction-specific parameters that vary
+based on whether the SA is for inbound or outbound use. For inbound SAs:
+
+* Controls for how this SA is to be looked up.
+
+* The minimum size of the anti-replay window to be used.
+
+* The default CoS to use when classification pipelining packets matching this
+SA.
+
+For outbound SAs:
+
+* Tunnel parameters to use when doing outbound processing in tunnel mode.
+
+* The fragmentation mode to be used.
+
+* The MTU to be used to control the maximum length IP packets that outbound
+IPsec operations may produce. This can be changed dynamically by the
+`odp_ipsec_sa_mtu_update()` API.
+
+As can be seen, SAs have a large degree of configurability.
+
+==== SA Lifecycle Management
+In discussing the lifecycle of an SA and the operations it supports, it is
+useful to refer to the following sequence diagram for IPsec configuration, SA
+management, and IPsec operations:
+
+image:ipsec_sa_states.svg[align="center"]
+
+After creation, IPsec services are active for this Security Association. The
+specific APIs that can be used on this SA depends on the IPsec operating mode
+that has been configured.
+
+===== IPsec Lookaside Processing
+If IPsec is operating in lookaside mode for the SA's direction (the
+`odp_ipsec_op_mode_t` is `ODP_IPSEC_OP_MODE_SYNC` or `ODP_IPSEC_OP_MODE_ASYNC`),
+then inbound or outbound lookaside operations may be performed. Asynchronous
+lookaside operations are also permitted if the SA is operating in inline
+mode, as described in the next section.
+
+The synchronous forms of these APIs are:
+
+* `odp_ipsec_in()`
+* `odp_ipsec_out()`
+
+Upon return from these calls, the return code tells the application the number
+of number of input packets that were consumed by the operation. The result of
+the operation is determined by calling the `odp_ipsec_result()` API for each
+output packet to retrieve its associated `odp_ipsec_result_t`.
+
+The asynchronous forms of these APIs are:
+
+* `odp_ipsec_in_enq()`
+* `odp_ipsec_out_enq()`
+
+Here again, the return code indicates how many input packets were
+processed. The success or failure is determined by inspecting the
+`odp_ipsec_result_t` associated with each packet completion event. These are
+presented as events of type `ODP_EVENT_PACKET` with subtype
+`ODP_EVENT_PACKET_IPSEC`.
+
+For both synchronous and asynchronous IPsec operations an input packet array
+is transformed into an output packet array as specified by a controlling
+parameter struct. For inbound operations, the `odp_ipsec_in_param_t` is
+used to specify how SA processing is to be performed for the requested
+operation. The caller may say that SA lookup processing should be performed
+for each input packet, a single (specified) SA should be used for all packets,
+or that each packet has a specified individual SA.
+
+For outbound lookaside operations, a corresponding `odp_ipsec_out_param_t`
+serves a similar role, but here the SA must be specified since the input
+packet(s) are non-IPsec packets. Again the option is to use a single SA for
+all input packets or one per input packet.
+
+For outbound operations, an associated array of `odp_ipsec_out_opt_t` structs
+is also used to control the fragmentation mode to be used as part of the
+outbound processing. Options here are to not fragment, to fragment before
+IPsec processing, after IPsec processing, or to only check whether IP
+fragmentation is needed but not to perform it. For check processing, the `mtu`
+status error bit in the `odp_ipsec_packet_result_t` is set if check processing
+detects that the resulting packet will not fit into the configured MTU. Note
+that the MTU associated with a given SA is set at SA creation and can be
+changed at any time via the `odp_ipsec_sa_mtu_update()` API.
+
+Once an asynchronous lookaside operation has been initiated, the worker thread
+that issued the asynchronous call can handle other events while waiting for
+the operation to complete. Completion of an asynchronous operation is
+indicated by the worker receiving an `ODP_EVENT_PACKET` that has subtype
+`ODP_EVENT_PACKET_IPSEC`. These events can be retrieved directly by polling
+the completion queue associated with the SA, or (more typically) via the ODP
+scheduler. Typical code for such completion processing would look as follows:
+
+[source,c]
+-----
+while (1) {
+ ev = odp_schedule(&queue, ODP_SCHED_WAIT);
+ ev_type = odp_event_types(ev, &ev_subtype);
+
+ switch (ev_type) {
+ case ODP_EVENT_PACKET:
+
+ switch (ev_subtype) {
+ case ODP_EVENT_PACKET_IPSEC:
+ pkt = odp_packet_from_event(ev);
+
+ if (odp_unlikely(odp_ipsec_result(&result, pkt) != 0)) {
+ /* Stale event, discard */
+ odp_event_free(ev);
+ continue;
+ }
+
+ if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
+ if (result.status.error != ODP_IPSEC_OK) {
+ ...process error result
+ odp_event_free(ev);
+ continue;
+ } else {
+ ...process packet warnings
+ }
+ }
+
+ my_context = odp_ipsec_sa_context(result.sa);
+
+ if (result.flag.inline_mode) {
+ ...process inline inbound packet
+ } else {
+ ...process the async completion event
+ }
+
+ ...
+ break;
+
+ case ...
+ }
+ break;
+
+ case ODP_EVENT_IPSEC_STATUS:
+ ...process IPsec status event
+ break;
+
+ }
+}
+-----
+
+===== IPsec Inline Processing
+When IPsec is configured to operate in `ODP_IPSEC_OP_MODE_INLINE` mode,
+inbound processing is implicit. The application never sees these packets until
+after IPsec has already decrypted them. As shown in the code sketch above,
+such packets appear as events of subtype `ODP_EVENT_PACKET_IPSEC` and the
+`flag` field in the associated `odp_ipsec_packet_result_t` indicates
+`inline_mode`.
+
+For outbound IPsec processing, the `odp_ipsec_out_inline()` API operates as
+a "fire and forget" API. A success return code from this call indicates that
+the packet will be encrypted and transmitted to the `odp_pktio_t` indicated
+in the `odp_ipsec_out_inline_param_t` specified at the time of the call without
+any further application involvement. Only if a problem arises will the packet
+be returned to the application with an `odp_ipsec_packet_result_t` indicating
+the nature of the problem.
+
+Note that while operating in inline mode, asynchronous lookaside operations are
+also permitted. This provide the application with additional flexibility if,
+for example, some packets need additional handling that cannot be supported
+directly with inline IPsec processing.
+
+==== SA Lifetimes
+A fundamental principle of good security is that the keying material
+associated with sessions has a limited lifetime. In effect, keys grow "stale"
+over time or due to being used to encrypt too much data. The metrics used
+to limit effective SA lifetimes are:
+
+* Duration (time)
+* Usage (volume of traffic using the keys)
+
+Associated with each of these metrics are "soft" and "hard" limits. When a
+hard limit is reached, the SA is expired and cannot be used further. To support
+graceful transition to a replacement SA, soft limits are used. A soft limit is
+similar to a "low fuel" warning light on a car. It alerts the application that
+the SA is nearing the end of its useful life and should be renegotiated even
+as the SA continues to work normally.
+
+ODP support for SA limits is based on packet/byte counts. Applications that
+wish to use time-based SA limits may do so on their own using the timing
+facilities that ODP provides. However, since especially with inline IPsec
+processing, the application may not have explicit knowledge of the traffic
+volumes associated with a given SA, support for usage-based limits is
+integrated into ODP IPsec support.
+
+At `odp_ipsec_sa_create()` time, one of the fields in the
+`odp_ipsec_sa_param_t` struct is the `odp_ipsec_lifetime_t` sub-structure.
+This struct allows hard and/or soft limits to be specified in terms of total
+bytes encrypted/decrypted, total packet count, or both. A limit specification
+of 0 indicates no limit for that metric. If either is specified, the limit
+is triggered on whichever occurs first. Given the defined behavior of hard vs.
+soft limits, the soft limits, if used, should always be specified as lower
+than the hard limits. These should be sufficiently lower to enable adequate
+time to switch over to a replacement SA before the hard limit is reached.
+
+As noted, when an SA hard limit is reached the SA immediately enters the
+expired state and attempts to use it further are failed with an
+`odp_ipsec_result_t` that indicates a hard expiration limit. When a soft
+limit is reached for packets sent via `odp_ipsec_out_inline()`, this results
+in an `ODP_EVENT_IPSEC_STATUS` event being sent to the application on the
+queue associated with the SA that has reached the soft limit. This status
+event has an `odp_ipsec_status_id_t` of `ODP_IPSEC_STATUS_WARN` with a
+`odp_ipsec_warn_t` bits set to indicate the type of soft expiration reached.
+Receipt of this event alerts the application that the SA is nearing the end of
+its useful life and that it should be replaced. It is the application's
+responsibility to heed this warning. It is implementation-defined how many
+such warnings are issued when a soft limit is exceeded (once, first N packets,
+or all packets beyond the limit), so applications should be written to
+allow for possible repeated warnings.
+
+When operating in lookaside mode, expiration limits are carried as a warning
+in the `odp_op_status_t` section of the `odp_ipsec_result_t` struct. The same
+is true for inline inbound packets. When the soft limit is reached, these
+packets will carry a warning flag indicating this condition.
+
+==== SA Disablement and Destruction
+When it is time to retire an SA, the application does so by first issuing a
+call to the `odp_ipsec_sa_disable()` API. This call initiates termination
+processing for an SA by stopping use of the SA for new operations while still
+allowing those that are "in flight" to complete processing. Following this call
+the application continues to receive and process IPsec events as normal.
+
+Disable completion is indicated by the application seeing an event of type
+`ODP_EVENT_IPSEC_STATUS` for this SA that contains an `odp_ipsec_status_id_t`
+of `ODP_IPSEC_STATUS_SA_DISABLE`. For inbound SAs, receipt of this event means
+that the application has seen all IPsec packets associated with this SA that
+were pending at the time of the disable call. For outbound SAs, receipt of
+this event means that the application has seen all result events associated
+with packets sent via this SA.
+
+Note that once a packet has been "seen" by the application, it becomes the
+application's responsibility to ensure that it is fully processed before
+attempting to destroy its associated SA. The disable call exists to give
+the application assurance that there are no pending IPsec events for this
+SA associated with packets that it has not seen before.
+
+So after disabling the SA, the application can process pending packets
+normally until it sees the disable status event. At that point it knows that
+all pending packets that arrived before the disable have been seen and it is
+safe for the application to destroy it via `odp_ipsec_sa_destroy()`, thus
+completing the SA lifecycle.
diff --git a/doc/users-guide/users-guide-packet.adoc b/doc/users-guide/users-guide-packet.adoc
index d5f2ff192..246cf7702 100644
--- a/doc/users-guide/users-guide-packet.adoc
+++ b/doc/users-guide/users-guide-packet.adoc
@@ -42,11 +42,11 @@ to manipulate its structure.
To support packet manipulation, predefined _headroom_ and _tailroom_
areas are logically associated with a packet. Packets can be adjusted by
_pulling_ and _pushing_ these areas. Typical packet processing might consist
-of stripping headers from a packet via `odp_pull_head()` calls as part of
+of stripping headers from a packet via `odp_packet_pull_head()` calls as part of
receive processing and then replacing them with new headers via
-`odp_push_head()` calls as the packet is being prepared for transmit. Note that
-while headroom and tailroom represent reserved areas of memory, these areas
-not not addressable or directly usable by ODP applications until they are
+`odp_packet_push_head()` calls as the packet is being prepared for transmit.
+Note that while headroom and tailroom represent reserved areas of memory, these
+areas are not addressable or directly usable by ODP applications until they are
made part of the packet via associated push operations. Similarly, bytes
removed via pull operations become part of a packet's headroom or tailroom
and are again no longer accessible to the application.
@@ -473,32 +473,16 @@ reliability, the shared data contained in any packet referred to by references
should be treated as read only once it has been successfully referenced until
it is known that all references to it have been freed.
-To assist applications in working with references, ODP provides two additional
-APIs:
+To assist applications in working with references, ODP provides the additional
+API:
[source,c]
-----
int odp_packet_has_ref(odp_packet_t pkt);
-
-uint32_t odp_packet_unshared_len(odp_packet_t pkt);
-----
The `odp_packet_has_ref()` API says whether any other packets
exist that share any bytes with this packet.
-Because references and referenced packets consist of an unshared
-prefix, that is modifiable, followed by a shared body that should not be
-modified, the `odp_packet_unshared_len()` API is available that operates as
-shown here:
-
-.Packet Reference Lengths
-image::reflen.svg[align="center"]
-
-`odp_packet_unshared_len()` returns the same value as `odp_packet_len()` when
-`odp_packet_has_ref()` returns 0, but for packets for which
-`odp_packet_has_ref()` returns 1, only returns the number of unshared bytes
-prefixed to them. To ensure portability and reliability, only offsets
-0..`odp_packet_unshared_len()`-1 should be modified by the caller.
-
===== Compound References
Note that architecturally ODP does not limit referencing and so it is possible
that a reference may be used as a basis for creating another reference. The
@@ -509,13 +493,44 @@ As noted earlier, the intent behind references is that they are lightweight
objects that can be implemented without requiring data copies. The existence
of compound references may complicate this goal for some implementations. As a
result, implementations are always free to perform partial or full copies of
-packets as part of any reference creation call. The
-`odp_packet_unshared_len()` API will always provide an authoritative answer to
-the question of how many bytes of a packet may safely be modified in any
-context, so whether or not copies have been performed applications can be
-assured of portability across all conforming ODP implementations.
+packets as part of any reference creation call.
Note also that a packet may not reference itself, nor may circular reference
relationships be formed, _e.g.,_ packet A is used as a header for a reference
to packet B and B is used as a header for a reference to packet A. Results
are undefined if such circular references are attempted.
+
+=== Packet Parsing, Checksum Processing, and Overrides
+Packet parsing is normally triggered automatically as part of packet RX
+processing. However, the application can trigger parsing explicitly via the
+API:
+[source,c]
+-----
+int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
+ const odp_packet_parse_param_t *param);
+-----
+This is typically done following packet decapsulation or other preprocessing
+that would prevent RX parsing from "seeing" the relevant portion of the
+packet. The `odp_packet_parse_param_t` struct that is passed to control the
+depth of the desired parse, as well as whether checksum validation should be
+performed as part of the parse, and if so which checksums require this
+processing.
+
+Packets containing Layer 3 (IPv4) and Layer 4 (TCP, UDP, SCTP) checksums
+can have these validated (on RX) and generated (on TX) automatically.
+This is normally controlled by the settings on the PktIOs that
+receive/transmit them, however they can also be controlled on an
+individual packet basis.
+
+Packets have associated `odp_packet_chksum_status_t` metadata that indicates
+the state any checksums contained in that packet. These can be queried via
+the APIs `odp_packet_l3_chksum_status()` and `odp_packet_l4_chksum_status()`,
+respectively. Checksums can either be known good, known bad, or unknown, where
+unknown means that checksum validation processing has not occurred or the
+attempt to validate the checksum failed.
+
+Similarly, the `odp_packet_l3_chksum_insert()` and
+`odp_packet_l4_chksum_insert()` APIs may be used to override default checksum
+processing for individual packets prior to transmission. If no explicit
+checksum processing is specified for a packet, then any checksum generation
+is controlled by the PktIO configuration of the interface used to transmit it.
diff --git a/doc/users-guide/users-guide-pktio.adoc b/doc/users-guide/users-guide-pktio.adoc
index 80a58d2fb..b7da188d0 100644
--- a/doc/users-guide/users-guide-pktio.adoc
+++ b/doc/users-guide/users-guide-pktio.adoc
@@ -38,6 +38,9 @@ PktIO objects begin life by being _opened_ via the call:
* errno set. Use odp_pktio_lookup() to obtain a handle to an already open
* device. Packet IO parameters provide interface level configuration options.
*
+ * Use odp_pktio_param_init() to initialize packet IO parameters into their
+ * default values. Default values are also used when 'param' pointer is NULL.
+ *
* Packet input queue configuration must be setup with
* odp_pktin_queue_config() before odp_pktio_start() is called. When packet
* input mode is ODP_PKTIN_MODE_DISABLED, odp_pktin_queue_config() call is
@@ -66,7 +69,7 @@ PktIO objects begin life by being _opened_ via the call:
* @param name Packet IO device name
* @param pool Default pool from which to allocate storage for packets
* received over this interface, must be of type ODP_POOL_PACKET
- * @param param Packet IO parameters
+ * @param param Packet IO parameters. Uses defaults when NULL.
*
* @return Packet IO handle
* @retval ODP_PKTIO_INVALID on failure
@@ -85,7 +88,7 @@ PktIO objects begin life by being _opened_ via the call:
* @see odp_pktio_start(), odp_pktio_stop(), odp_pktio_close()
*/
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool,
- const odp_pktio_param_t *param);
+ const odp_pktio_param_t *param);
-----
`odp_pktio_open()` takes three arguments: a *name*, which is an
implementation-defined string that identifies the logical interface to be
@@ -97,25 +100,27 @@ I/O options to be associated with this PktIO instance.
/**
* Packet IO parameters
*
- * In minimum, user must select input and output modes. Use 0 for defaults.
- * Initialize entire struct with zero to maintain API compatibility.
+ * Packet IO interface level parameters. Use odp_pktio_param_init() to
+ * initialize the structure with default values.
*/
typedef struct odp_pktio_param_t {
/** Packet input mode
*
* The default value is ODP_PKTIN_MODE_DIRECT. */
odp_pktin_mode_t in_mode;
+
/** Packet output mode
*
* The default value is ODP_PKTOUT_MODE_DIRECT. */
odp_pktout_mode_t out_mode;
+
} odp_pktio_param_t;
-----
ODP defines *"loop"* as a reserved name to indicate that this PktIO represents
a loopback interface. Loopback interfaces are useful as a means of recycling
packets back for reclassification after decryption or decapsulation, as well as
-for diagnostic or testing purposes. For example, when receiving IPSEC traffic,
-the classifier is able to recognize that the traffic is IPSEC, however until
+for diagnostic or testing purposes. For example, when receiving IPsec traffic,
+the classifier is able to recognize that the traffic is IPsec, however until
the traffic is decrypted it is unable to say what that traffic contains.
So following decryption, sending the decrypted packet back to a loopback
interface allows the classifier to take a "second look" at the packet and
@@ -127,6 +132,335 @@ use for packet allocation if not overridden by the classifier due to a
specific or default Class-of-Service (CoS) match on the packet. The *param*
struct, in turn, specifies the input and output *modes* of the PktIO.
+=== PktIO Capabilities and PktIn/PktOut Configuration
+Associated with each PktIO is a set of _capabilities_ that provide information
+such as the maximum number of input/output queues it supports, its configuration
+options, and the operations is supports. These are aggregated into
+the struct:
+[source,c]
+-----
+/**
+ * Packet IO capabilities
+ */
+typedef struct odp_pktio_capability_t {
+ /** Maximum number of input queues */
+ unsigned max_input_queues;
+
+ /** Maximum number of output queues */
+ unsigned max_output_queues;
+
+ /** Supported pktio configuration options */
+ odp_pktio_config_t config;
+
+ /** Supported set operations
+ *
+ * A bit set to one indicates a supported operation. All other bits are
+ * set to zero. */
+ odp_pktio_set_op_t set_op;
+
+} odp_pktio_capability_t;
+-----
+That is returned by the `odp_pktio_capability()` API. This returns the
+limits and default values for these capabilities which can in turn be set
+via the `odp_pktio_config()` API, which takes as input the struct:
+[source,c]
+-----
+/**
+ * Packet IO configuration options
+ *
+ * Packet IO interface level configuration options. Use odp_pktio_capability()
+ * to see which options are supported by the implementation.
+ * Use odp_pktio_config_init() to initialize the structure with default values.
+ */
+typedef struct odp_pktio_config_t {
+ /** Packet input configuration options bit field
+ *
+ * Default value for all bits is zero. */
+ odp_pktin_config_opt_t pktin;
+
+ /** Packet output configuration options bit field
+ *
+ * Default value for all bits is zero. */
+ odp_pktout_config_opt_t pktout;
+
+ /** Packet input parser configuration */
+ odp_pktio_parser_config_t parser;
+
+ /** Interface loopback mode
+ *
+ * In this mode the packets sent out through the interface is
+ * looped back to input of the same interface. Supporting loopback mode
+ * is an optional feature per interface and should be queried in the
+ * interface capability before enabling the same. */
+ odp_bool_t enable_loop;
+
+ /** Inbound IPSEC inlined with packet input
+ *
+ * Enable/disable inline inbound IPSEC operation. When enabled packet
+ * input directs all IPSEC packets automatically to IPSEC inbound
+ * processing. IPSEC configuration (through IPSEC API) must be done
+ * before enabling this feature in pktio.
+ * Packets that are not (recognized as) IPSEC are processed
+ * according to the packet input configuration.
+ *
+ * 0: Disable inbound IPSEC inline operation (default)
+ * 1: Enable inbound IPSEC inline operation
+ *
+ * @see odp_ipsec_config(), odp_ipsec_sa_create()
+ */
+ odp_bool_t inbound_ipsec;
+
+ /** Outbound IPSEC inlined with packet output
+ *
+ * Enable/disable inline outbound IPSEC operation. When enabled IPSEC
+ * outbound processing can send outgoing IPSEC packets directly
+ * to the pktio interface for output. IPSEC configuration is done
+ * through the IPSEC API.
+ *
+ * Outbound IPSEC inline operation cannot be combined with traffic
+ * manager (ODP_PKTOUT_MODE_TM).
+ *
+ * 0: Disable outbound IPSEC inline operation (default)
+ * 1: Enable outbound IPSEC inline operation
+ *
+ * @see odp_ipsec_config(), odp_ipsec_sa_create()
+ */
+ odp_bool_t outbound_ipsec;
+
+} odp_pktio_config_t;
+-----
+The IPsec related configurations will be discussed later in the IPsec chapter,
+but for now we'll focus on the PktIn/PktOut configuration and the
+parser configuration.
+
+==== PktIn Configuration
+For PktIOs that will receive packets, the `odp_pktin_config_opt_t` struct
+controls RX processing to be performed on these packets as they are received:
+[source,c]
+-----
+/**
+ * Packet input configuration options bit field
+ *
+ * Packet input configuration options listed in a bit field structure. Packet
+ * input timestamping may be enabled for all packets or at least for those that
+ * belong to time synchronization protocol (PTP).
+ *
+ * Packet input checksum checking may be enabled or disabled. When it is
+ * enabled, implementation will attempt to verify checksum correctness on
+ * incoming packets and depending on drop configuration either deliver erroneous
+ * packets with appropriate flags set (e.g. odp_packet_has_l3_error(),
+ * odp_packet_l3_chksum_status()) or drop those. When packet dropping is
+ * enabled, application will never receive a packet with the specified error
+ * and may avoid to check the error flag.
+ *
+ * If checksum checking is enabled, IPv4 header checksum checking is always
+ * done for packets that do not have IP options and L4 checksum checking
+ * is done for unfragmented packets that do not have IPv4 options or IPv6
+ * extension headers. In other cases checksum checking may or may not
+ * be done. For example, L4 checksum of fragmented packets is typically
+ * not checked.
+ *
+ * IPv4 checksum checking may be enabled only when parsing level is
+ * ODP_PROTO_LAYER_L3 or higher. Similarly, L4 level checksum checking
+ * may be enabled only with parsing level ODP_PROTO_LAYER_L4 or higher.
+ *
+ * Whether checksum checking was done and whether a checksum was correct
+ * can be queried for each received packet with odp_packet_l3_chksum_status()
+ * and odp_packet_l4_chksum_status().
+ */
+typedef union odp_pktin_config_opt_t {
+ /** Option flags */
+ struct {
+ /** Timestamp all packets on packet input */
+ uint64_t ts_all : 1;
+
+ /** Timestamp (at least) IEEE1588 / PTP packets
+ * on packet input */
+ uint64_t ts_ptp : 1;
+
+ /** Check IPv4 header checksum on packet input */
+ uint64_t ipv4_chksum : 1;
+
+ /** Check UDP checksum on packet input */
+ uint64_t udp_chksum : 1;
+
+ /** Check TCP checksum on packet input */
+ uint64_t tcp_chksum : 1;
+
+ /** Check SCTP checksum on packet input */
+ uint64_t sctp_chksum : 1;
+
+ /** Drop packets with an IPv4 error on packet input */
+ uint64_t drop_ipv4_err : 1;
+
+ /** Drop packets with an IPv6 error on packet input */
+ uint64_t drop_ipv6_err : 1;
+
+ /** Drop packets with a UDP error on packet input */
+ uint64_t drop_udp_err : 1;
+
+ /** Drop packets with a TCP error on packet input */
+ uint64_t drop_tcp_err : 1;
+
+ /** Drop packets with a SCTP error on packet input */
+ uint64_t drop_sctp_err : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint64_t all_bits;
+} odp_pktin_config_opt_t;
+-----
+These are used to control packet timestamping as well as default packet checksum
+verification processing.
+
+==== PktIO Parsing Configuration
+For RX processing, packets may also be parsed automatically as part of
+receipt as controlled by the `odp_pktio_parser_config_t` struct:
+[source,c]
+-----
+/**
+ * Parser configuration
+ */
+typedef struct odp_pktio_parser_config_t {
+ /** Protocol parsing level in packet input
+ *
+ * Application requires that protocol headers in a packet are checked
+ * up to this layer during packet input. Use ODP_PROTO_LAYER_ALL for
+ * all layers. Packet metadata for this and all preceding layers are
+ * set. In addition, offset (and pointer) to the next layer is set.
+ * Other layer/protocol specific metadata have undefined values.
+ *
+ * The default value is ODP_PROTO_LAYER_ALL. */
+ odp_proto_layer_t layer;
+
+} odp_pktio_parser_config_t;
+-----
+Note that parsing is automatically done whenever classification is enabled
+for an RX interface (see below).
+
+==== PktOut Configuration
+For PktIOs that will transmit packets, the `odp_pktout_config_opt_t` struct
+controls TX processing to be performed on these packets as they are
+transmitted:
+[source,c]
+-----
+/**
+ * Packet output configuration options bit field
+ *
+ * Packet output configuration options listed in a bit field structure. Packet
+ * output checksum insertion may be enabled or disabled (e.g. ipv4_chksum_ena):
+ *
+ * 0: Disable checksum insertion. Application will not request checksum
+ * insertion for any packet. This is the default value for xxx_chksum_ena
+ * bits.
+ * 1: Enable checksum insertion. Application will request checksum insertion
+ * for some packets.
+ *
+ * When checksum insertion is enabled, application may use configuration options
+ * to set the default behaviour on packet output (e.g. ipv4_chksum):
+ *
+ * 0: Do not insert checksum by default. This is the default value for
+ * xxx_chksum bits.
+ * 1: Calculate and insert checksum by default.
+ *
+ * These defaults may be overridden on per packet basis using e.g.
+ * odp_packet_l4_chksum_insert().
+ *
+ * For correct operation, packet metadata must provide valid offsets and type
+ * flags for the appropriate layer 3 and layer 4 protocols. L3 and L4 offsets
+ * can be updated with odp_packet_l3_offset_set() and odp_packet_l4_offset_set()
+ * calls. L3 and L4 type flags can be updated using odp_packet_has_*_set() calls
+ * For example, UDP checksum calculation needs both L3 and L4 types (IP and UDP) and
+ * L3 and L4 offsets (to access IP and UDP headers), while IP checksum
+ * calculation only needs L3 type (IP) and L3 offset (to access IP header).
+ * When application (e.g. a switch) does not modify L3/L4 data and thus checksum
+ * does not need to be updated, checksum insertion should be disabled for optimal
+ * performance.
+ *
+ * UDP, TCP and SCTP checksum insertion must not be requested for IP fragments.
+ * Use checksum override function (odp_packet_l4_chksum_insert()) to disable
+ * checksumming when sending a fragment through a packet IO interface that has
+ * the relevant L4 checksum insertion enabled.
+ *
+ * Result of checksum insertion at packet output is undefined if the protocol
+ * headers required for checksum calculation are not well formed. Packet must
+ * contain at least as many data bytes after L3/L4 offsets as the headers
+ * indicate. Other data bytes of the packet are ignored for the checksum
+ * insertion.
+ *
+ * No packet refs is an offload when set indicates that this pktio
+ * can always free the packet buffer after transmission and need not check
+ * for packet references and application will make sure that a packet
+ * transmitted via this pktio will always have only single reference.
+ *
+ * Packet Tx timestamp capture may be enabled or disabled.
+ * When enabled, packet Tx timestamps will be captured per packet basis for
+ * packets requesting it (odp_packet_ts_request()). The timestamp can be
+ * retrieved using odp_pktout_ts_read() API after the packet has been
+ * transmitted. Since packet Tx depends on scheduling, shaping and then finally
+ * transmission to physical link, user has to wait long enough for
+ * odp_pktout_ts_read() to provide the timestamp captured for the last packet.
+ */
+typedef union odp_pktout_config_opt_t {
+ /** Option flags for packet output */
+ struct {
+ /** Enable Tx timestamp capture */
+ uint64_t ts_ena : 1;
+
+ /** Enable IPv4 header checksum insertion */
+ uint64_t ipv4_chksum_ena : 1;
+
+ /** Enable UDP checksum insertion */
+ uint64_t udp_chksum_ena : 1;
+
+ /** Enable TCP checksum insertion */
+ uint64_t tcp_chksum_ena : 1;
+
+ /** Enable SCTP checksum insertion */
+ uint64_t sctp_chksum_ena : 1;
+
+ /** Insert IPv4 header checksum by default */
+ uint64_t ipv4_chksum : 1;
+
+ /** Insert UDP checksum on packet by default */
+ uint64_t udp_chksum : 1;
+
+ /** Insert TCP checksum on packet by default */
+ uint64_t tcp_chksum : 1;
+
+ /** Insert SCTP checksum on packet by default */
+ uint64_t sctp_chksum : 1;
+
+ /** Packet references not used on packet output
+ *
+ * When set, application indicates that it will not transmit
+ * packet references on this packet IO interface.
+ * Since every ODP implementation supports it, it is always
+ * ok to set this flag.
+ *
+ * 0: Packet references may be transmitted on the
+ * interface (the default value).
+ * 1: Packet references will not be transmitted on the
+ * interface.
+ */
+ uint64_t no_packet_refs : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint64_t all_bits;
+} odp_pktout_config_opt_t;
+-----
+These are used to control default checksum generation processing for
+transmitted packets.
+
=== PktIO Input and Output Modes
PktIO objects support four different Input and Output modes, that may be
specified independently at *open* time.
@@ -134,7 +468,7 @@ specified independently at *open* time.
.PktIO Input Modes
* `ODP_PKTIN_MODE_DIRECT`
* `ODP_PKTIN_MODE_QUEUE`
-* `ODP_OKTIN_MODE_SCHED`
+* `ODP_PKTIN_MODE_SCHED`
* `ODP_PKTIN_MODE_DISABLED`
.PktIO Output Modes
@@ -209,25 +543,48 @@ typedef struct odp_pktin_queue_param_t {
* applicable. */
odp_pktio_op_mode_t op_mode;
+ /** Enable classifier
+ *
+ * * 0: Classifier is disabled (default)
+ * * 1: Classifier is enabled. Use classifier to direct incoming
+ * packets into pktin event queues. Classifier can be enabled
+ * only in ODP_PKTIN_MODE_SCHED and ODP_PKTIN_MODE_QUEUE modes.
+ * Both classifier and hashing cannot be enabled simultaneously
+ * ('hash_enable' must be 0). */
+ odp_bool_t classifier_enable;
+
/** Enable flow hashing
- * 0: Do not hash flows
- * 1: Hash flows to input queues */
+ *
+ * * 0: Do not hash flows (default)
+ * * 1: Enable flow hashing. Use flow hashing to spread incoming
+ * packets into input queues. Hashing can be enabled in all
+ * modes. Both classifier and hashing cannot be enabled
+ * simultaneously ('classifier_enable' must be 0). */
odp_bool_t hash_enable;
- /** Protocol field selection for hashing. Multiple protocols can be
- * selected. */
+ /** Protocol field selection for hashing
+ *
+ * Multiple protocols can be selected. Ignored when 'hash_enable' is
+ * zero. The default value is all bits zero. */
odp_pktin_hash_proto_t hash_proto;
- /** Number of input queues to be created. More than one input queue
- * require input hashing or classifier setup. Hash_proto is ignored
- * when hash_enable is zero or num_queues is one. This value must be
- * between 1 and interface capability. Queue type is defined by the
- * input mode. The default value is 1. */
+ /** Number of input queues to be created
+ *
+ * When classifier is enabled in odp_pktin_queue_config() this
+ * value is ignored, otherwise at least one queue is required.
+ * More than one input queues require flow hashing configured.
+ * The maximum value is defined by pktio capability 'max_input_queues'.
+ * Queue type is defined by the input mode. The default value is 1. */
unsigned num_queues;
- /** Queue parameters for creating input queues in ODP_PKTIN_MODE_QUEUE
+ /** Queue parameters
+ *
+ * These are used for input queue creation in ODP_PKTIN_MODE_QUEUE
* or ODP_PKTIN_MODE_SCHED modes. Scheduler parameters are considered
- * only in ODP_PKTIN_MODE_SCHED mode. */
+ * only in ODP_PKTIN_MODE_SCHED mode. Default values are defined in
+ * odp_queue_param_t documentation.
+ * When classifier is enabled in odp_pktin_queue_config() this
+ * value is ignored. */
odp_queue_param_t queue_param;
} odp_pktin_queue_param_t;
@@ -347,8 +704,10 @@ Once started, the PktIn queue handles are used as arguments to
/**
* Receive packets directly from an interface input queue
*
- * Receives up to 'num' packets from the pktio interface input queue. When
- * input queue parameter 'op_mode' has been set to ODP_PKTIO_OP_MT_UNSAFE,
+ * Receives up to 'num' packets from the pktio interface input queue. Returns
+ * the number of packets received.
+ *
+ * When input queue parameter 'op_mode' has been set to ODP_PKTIO_OP_MT_UNSAFE,
* the operation is optimized for single thread operation per queue and the same
* queue must not be accessed simultaneously from multiple threads.
*
@@ -478,6 +837,11 @@ Once the PktIO has been configured for output and started via
* is less than 'num', the remaining packets at the end of packets[] array
* are not consumed, and the caller has to take care of them.
*
+ * Entire packet data is sent out (odp_packet_len() bytes of data, starting from
+ * odp_packet_data()). All other packet metadata is ignored unless otherwise
+ * specified e.g. for protocol offload purposes. Link protocol specific frame
+ * checksum and padding are added to frames before transmission.
+ *
* @param queue Packet output queue handle for sending packets
* @param packets[] Array of packets to send
* @param num Number of packets to send
@@ -485,7 +849,8 @@ Once the PktIO has been configured for output and started via
* @return Number of packets sent
* @retval <0 on failure
*/
-int odp_pktout_send(odp_pktout_queue_t queue, odp_packet_t packets[], int num);
+int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[],
+ int num);;
-----
Note that the argument to this call specifies the PktOut queue that the
packet is to be added to rather than the PktIO itself. This permits multiple
diff --git a/doc/users-guide/users-guide-timer.adoc b/doc/users-guide/users-guide-timer.adoc
index 9cd30de11..45507a872 100644
--- a/doc/users-guide/users-guide-timer.adoc
+++ b/doc/users-guide/users-guide-timer.adoc
@@ -6,7 +6,8 @@ timing features found in various platforms that support ODP implementations.
Timers are drawn from specialized pools called _timer pools_ that have their
own abstract type (`odp_timer_pool_t`). Each timer pool is a logically
independent time source with its own _resolution_ measured in nanoseconds (ns)
-and a maximum number of timers that it can support. Applications can have many
+and a maximum number of timers that it can support. The max _resolution_ is
+able to be obtained from `odp_timer_capability()`. Applications can have many
timers active at the same time and can set them to use either relative or
absolute time. Associated with each timer is a queue that is to receive events
when this timer expires. This queue is created by a separate
@@ -28,7 +29,7 @@ _i.e.,_ trigger both state machines.
.ODP Timers lifecycle State Diagram
image::timer_fsm.svg[align="center"]
-.ODP Timeout event lifecyle State Diagram
+.ODP Timeout event lifecycle State Diagram
image::timeout_fsm.svg[align="center"]
Reminder:
@@ -45,11 +46,11 @@ as an input parameter to enable the pool-specific conversion ratios to be
used.
Associated with each timer pool is a free running tick counter that can be
-sampled at any time via the `odp_timer_current_tick()` API. Timers can be set
-to an absolute future tick value via `odp_timer_set_abs()` or to a future tick
-value relative to the current tick via `odp_timer_set_rel()`. Implementations
-may impose minimum and maximum future values supported by a given timer pool
-and timer set operations will fail if the requested value is outside of the
+sampled at any time via the `odp_timer_current_tick()` API. Timers are started
+with `odp_timer_start()` and the expiration time can be an absolute future tick
+value or a future tick value relative to the current tick. Implementations may
+impose minimum and maximum future values supported by a given timer pool and
+timer start operations will fail if the requested value is outside of the
supported range.
Before a set timer expires, it can be canceled via the `odp_timer_cancel()`
@@ -60,16 +61,11 @@ expired.
=== Timer Pool Management
To facilitate implementation of the ODP timer APIs, an additional timer API is
provided. During initialization, applications are expected to create the timer
-pools they need and then call `odp_timer_pool_start()`. ODP implementations
-may or may not fail further attempts to create timer pools after this API is
-called. For best portability, applications should not attempt to create
-further timer pools after calling `odp_timer_pool_start()`. Note that no such
-restrictions exist on timeout pools, as these are just ordinary ODP pools.
-
-Following start, applications may allocate, set, cancel, and free timers
-from their associated timer pools. During termination processing, after all
-timers allocated from a timer pool have been freed, the pool itself should be
-released via a call to `odp_timer_pool_destroy()`.
+pools they need and then call `odp_timer_pool_start_multi()`. Following start,
+applications may allocate, set, cancel, and free timers from their associated
+timer pools. During termination processing, after all timers allocated from a
+timer pool have been freed, the pool itself should be released via a call to
+`odp_timer_pool_destroy()`.
=== Timeout Event Management
The purpose of ODP timers is to schedule their associated timeout events, which
@@ -117,11 +113,6 @@ while (1) {
void *userptr = odp_timeout_user_ptr(timeout);
uint64_t expiration = odp_timeout_tick(timeout);
- if (!odp_timeout_fresh(timeout)) {
- odp_timeout_free(timeout);
- continue;
- }
-
...process the timeout event
break;
@@ -129,16 +120,3 @@ while (1) {
}
}
-----
-When a worker thread receives a timeout event via `odp_schedule()`, it needs
-to determine whether the event is still relevant. A timeout event that is still
-relevant is said to be _fresh_ while one that is no longer relevant is said to
-be _stale_. Timeouts may be stale for any number of reasons, most of which are
-known only to the application itself. However, there are a few cases where the
-ODP implementation may be able to assist in this determination and for those
-cases the `odp_timeout_fresh()` API is provided.
-
-ODP defines a fresh timeout simply as one that has not been reset or
-canceled since it expired. So if `odp_timeout_fresh()` returns 0 then it is
-likely that the application should ignore this event, however if it returns 1
-then it remains an application responsibility to handle the event appropriate
-to its needs.
diff --git a/doc/users-guide/users-guide-tm.adoc b/doc/users-guide/users-guide-tm.adoc
index 251297335..55efb1b21 100644
--- a/doc/users-guide/users-guide-tm.adoc
+++ b/doc/users-guide/users-guide-tm.adoc
@@ -10,7 +10,7 @@ A given platform supporting this TM API could support one or more pure hardware
based packet scheduling systems, one or more pure software based systems or one
or more hybrid systems - where because of hardware constraints some of the
packet scheduling is done in hardware and some is done in software. In
-addition, there may also be additional API's beyond those described here for:
+addition, there may also be additional APIs beyond those described here for:
- controlling advanced capabilities supported by specific hardware, software
or hybrid subsystems
@@ -84,7 +84,7 @@ traffic, while allowing for less idle outputs.
==== Weighted Fair Queuing
-Weighted Fair Queuing (WFQ) is used to arbitrate amongst multiple input
+Weighted Fair Queuing (WFQ) is used to arbitrate among multiple input
packets with the same priority. Each input can be assigned a weight in the
range MIN_WFQ_WEIGHT..MAX_WFQ_WEIGHT (nominally 1..255) that affects the way
the algorithm chooses the next packet. If all of the weights are equal AND all
@@ -158,7 +158,7 @@ final scheduling decision is controlled by equal priority schedulers,
strict priority multiplexers, bandwidth shapers - at multiple levels - all
forming a tree rooted at a single egress object. In other words, all
tm_queues and tm_nodes have the property that their logical "output" feeds
-into one fan-in of a subsequent tm_node or egresss object - forming a proper
+into one fan-in of a subsequent tm_node or egress object - forming a proper
tree.
.Hierarchical Scheduling
@@ -178,7 +178,7 @@ choice" of what packet/tm_queue should next be serviced.
Tm_nodes are the main "entity"/object that a TM system is composed of. Each
tm_node is a mini-TM subsystem of its own, but the interconnection and
interplay of a multi-level "tree" of tm_nodes can allow the user to specify
-some very sophisticated behaviours. Each tm_node can contain a set of scheduler
+some very sophisticated behaviors. Each tm_node can contain a set of scheduler
(one per strict priority level), a strict priority multiplexer, a bandwidth
shaper and a WRED component - or a subset of these.
diff --git a/doc/users-guide/users-guide-utilities-examples.adoc b/doc/users-guide/users-guide-utilities-examples.adoc
new file mode 100644
index 000000000..2d5ad0113
--- /dev/null
+++ b/doc/users-guide/users-guide-utilities-examples.adoc
@@ -0,0 +1,18 @@
+== Utilities and examples
+
+=== PcapNg capture
+If compiled using `--enable-pcapng-support` ODP will offer packet capturing
+functionality in PcapNg format. If the /var/run/odp directory exists prior to
+launching the application ODP will create a fifo for each NIC queue.
+Queue naming will be of the following format: *<odp global pid>-<NIC
+name>-flow-<queue number>*. Linux dd application can be used for capturing a
+sample of the live stream from the fifo. Killing ether the application or dd
+will stop the capturing process.
+
+. `./configure --enable-pcapng-support`
+. `sudo mkdir /var/run/odp`
+. `sudo ./test/performance/odp_packet_gen -i enp2s0 --eth_dst A0:F6:FD:AE:62:6C
+--ipv4_dst 192.168.49.20 --ipv4_src 192.168.49.4 -g 0`
+. `sudo dd if=/var/run/odp/26737-enp2s0-flow-0 of=~/test.pcap`
+. `ctrl^c`
+. `wireshark ~/test.pcap`
diff --git a/doc/users-guide/users-guide.adoc b/doc/users-guide/users-guide.adoc
index 18d8cb8fb..73ec55c41 100644
--- a/doc/users-guide/users-guide.adoc
+++ b/doc/users-guide/users-guide.adoc
@@ -40,11 +40,29 @@ by abstract handles of type `odp_packet_t`, and packet-related APIs take
arguments of this type. What an `odp_packet_t` actually is is not part of the
ODP API specification--that is the responsibility of each ODP implementation.
+.API Specification Principles
+The ODP API specification is designed to permit wide latitude on the part of
+implementations while at the same time supporting highly efficient processing,
+especially for APIs that are executed frequently.
+
+Both applications and implementations must comply with the API
+specification. If not otherwise documented, results are undefined if an
+application acts against the specification. For example, if an application
+passes bad parameters to an ODP API one implementation may report an error,
+while another may not check them (to maximize performance) but would just
+crash while using the bad values.
+
+Note that many ODP component areas provide an `odp_xxx_capability()` API that
+returns platform-specific information regarding valid input to other APIs in
+that component. For best portability applications should always use these
+capability APIs to determine valid parameter input.
+
.Summary: ODP API attributes:
* Open Source, open contribution, BSD-3 licensed.
* Vendor and platform neutral.
* Application-centric. Covers functional needs of data plane applications.
* Ensures portability by specifying the functional behavior of ODP.
+* Both applications and implementations must conform to the API specification.
* Defined jointly and openly by application writers and platform implementers.
* Architected to be implementable on a wide range of platforms efficiently
* Sponsored, governed, and maintained by the Linaro Networking Group (LNG)
@@ -133,7 +151,7 @@ of the specification or other minor changes that do not affect either the
syntax or semantics of the specification. Such changes in the API specification
are expected to be rare. Increments to the minor level
represent the introduction of new APIs or functional capabilities, or changes
-to he specified syntax or functional behavior of APIs and thus may require
+to the specified syntax or functional behavior of APIs and thus may require
application source code changes. Such changes are well documented in the
release notes for each revision of the specification. Finally, increments to
the major level represent significant structural changes that most likely
@@ -229,14 +247,14 @@ polled by ODP _Threads_, or can pass through the _Classifier_ and sorted into
Queues that represent individual flows. These queues can then be dispatched
to application threads via the _Scheduler_.
-Threads, in term can invoke various ODP APIs to manipulate packet contents
+Threads, in turn can invoke various ODP APIs to manipulate packet contents
prior to disposing of them. For output processing, packets make by directly
queued to a PktIO output queue or else they may be handed to the _Traffic
Manager_ for programmatic _Quality of Service (QoS)_ processing before winding
up being transmitted (TX). Note that output interfaces may operate in
_loopback_ mode, in which case packets sent to them are re-routed back to the
-input lines for "second pass" processing. For example, an incoming IPSec packet
-cannot be properly classified (beyond being IPSec traffic) until it is
+input lines for "second pass" processing. For example, an incoming IPsec packet
+cannot be properly classified (beyond being IPsec traffic) until it is
decrypted. Once decrypted and its actual contents made visible, it can then
be classified into its real flow.
@@ -294,7 +312,7 @@ appropriate type represented by the event.
A queue is a message passing channel that holds events. Events can be
added to a queue via enqueue operations or removed from a queue via dequeue
operations. The endpoints of a queue will vary depending on how it is used.
-Queues come in two major types: polled and scheduled, which will be
+Queues come in two major types: plain and scheduled, which will be
discussed in more detail when the event model is introduced. Queues may also
have an associated context, which represents a persistent state for all
events that make use of it. These states are what permit threads to perform
@@ -386,7 +404,7 @@ To get rate of time source `odp_time_local_res()`, `odp_time_global_res()`
are used. To wait, `odp_time_wait_ns()` and `odp_time_wait_until()` are used,
during which a thread potentially busy loops the entire wait time.
-The `odp_time_t` opaque type represents local or global timestamps.
+The `odp_time_t` opaque type represents local, global and PktIO timestamps.
==== Portability Considerations
The ODP Time APIs are designed to permit high-precision relative time
@@ -540,20 +558,44 @@ calling the terminate functions should only be done when the application is
sure it has closed the ingress and subsequently drained all queues, etc.
=== Startup
-The first API that must be called by an ODP application is 'odp_init_global()'.
+The first API that must be called by an ODP application is `odp_init_global()`:
+[source,c]
+-----
+int odp_init_global(odp_instance_t *instance,
+ const odp_init_t *param,
+ const odp_platform_init_t *platform_param);
+-----
This takes two pointers. The first, `odp_init_t`, contains ODP initialization
data that is platform independent and portable, while the second,
`odp_platform_init_t`, is passed unparsed to the implementation
to be used for platform specific data that is not yet, or may never be
-suitable for the ODP API.
+suitable for the ODP API. Each of these parameters is optional and may be
+specified as NULL to accept the implementation-defined default initialization
+values.
-Calling odp_init_global() establishes the ODP API framework and MUST be
+Calling `odp_init_global()` establishes the ODP API framework and MUST be
called before any other ODP API may be called. Note that it is only called
-once per application. Following global initialization, each thread in turn
+once per application. A successful call to `odp_init_global()` returns rc = 0
+and sets the `instance` variable supplied as input to the call to a handle
+representing this unique ODP instance.
+
+The `odp_init_t` parameter is used to specify various customizations to the
+ODP environment being established by this call. For example, the caller can
+specify the maximum number of worker threads it will use, the thread masks
+associated with these threads, as well as whether the default logging or
+abort functions are to be overridden with an application-supplied handler.
+
+The application may also provide optimization hints to the ODP implementation
+if it knows that it will never use specific ODP feature sets, such as the
+packet classifier or traffic manager. Implementations may use such hints to
+provide optimized behavior to applications that are known not to need these
+features.
+
+Following global initialization, each thread in turn
calls 'odp_init_local()'. This establishes the local ODP thread
context for that thread and MUST be called before other ODP APIs may be
-called by that thread. The sole argument to this call is the _thread type_,
-which is either `ODP_THREAD_WORKER` or `ODP_THREAD_CONTROL`.
+called by that thread. The sole argument to this call is the `instance`
+variable returned by `odp_init_global()`.
=== Shutdown
Shutdown is the logical reverse of the initialization procedure, with
@@ -619,7 +661,7 @@ area and how best to use ODP to achieve these goals.
=== Portability and Coexistence
Because ODP offers a programming _framework_ rather than a programming
_environment_, it is designed to be able to work alongside APIs offered by
-other frameworks with minimual interference. Therefore when we speak of
+other frameworks with minimal interference. Therefore when we speak of
portability in an ODP context, we of necessity speak of portability of those
portions of the application that make use of ODP APIs. If an application uses
non-ODP APIs then those must be taken into consideration as well when
@@ -662,7 +704,7 @@ Embedded applications will typically work with a copy of ODP downloaded from
a git repository so that it can be configured for the application's precise
needs. To specify that the application wishes to use the embedded profile:
-`./configure --enable-abi-compat=no ...`
+`./configure --disable-abi-compat ...`
should be used as part of the ODP configuration options. This allows
applications to use inline forms of ODP APIs to give optimal performance
@@ -682,11 +724,7 @@ that is installed via `sudo apt-get install` or equivalent command).
When using a copy of ODP downloaded from a repository, the cloud profile is
selected at configure time:
-`./configure --enable-abi-compat=yes ...`
-
-Note that `--enable-abi-compat=yes` is the default, so this need not be
-specified. Unless `no` is specified for this option, the result will be
-applications designed to run in the cloud profile.
+`./configure --enable-abi-compat ...`
=== ABI Characteristics
An ABI consists of several conventions that ensure that a program compiled
@@ -714,10 +752,10 @@ Architecture (ISA), such as x86-64 or AArch64. Binaries cannot directly port
between ISAs--that requires a recompilation.
Each ODP implementation will identify which ABI definition it supports, if any.
-When compiling against an ODP implementation in ABI compabitilty mode, the
+When compiling against an ODP implementation in ABI compatibility mode, the
resulting binary is automatically binary compatible with all other ODP
implementations that share this ABI. For example, for the x86-64 ISA, both
-the `odp-linux` and `odp-dpdk` implemtations are a common ABI.
+the `odp-linux` and `odp-dpdk` implementations are a common ABI.
== Shared memory
=== Allocating shared memory
@@ -756,16 +794,16 @@ shared_data_t *shared_data;
shared_data = odp_shm_addr(shm);
----
-The address returned by `odp_shm_addr()` is valid only in the calling ODP
-thread space: odp_shm_t handles can be shared between ODP threads and remain
-valid within any threads, whereas the address returned by `odp_shm_addr(shm)`
-may differ from ODP threads to ODP threads (for the same 'shm' block), and
-should therefore not be shared between ODP threads.
-For instance, it would be correct to send a shm handle using IPC between two
-ODP threads and let each of these thread do their own `odp_shm_addr()` to
-get the block address. Directly sending the address returned by
-`odp_shm_addr()` from one ODP thread to another would however possibly fail
-(the address may have no sense in the receiver address space).
+The address returned by `odp_shm_addr()` is normally valid only in the calling
+ODP thread space: odp_shm_t handles can be shared between ODP threads and
+remain valid within any threads, whereas the address returned by
+`odp_shm_addr(shm)` may differ from ODP threads to ODP threads (for the same
+'shm' block), and should therefore not be shared between ODP threads. For
+instance, it would be correct to send a shm handle using IPC between two ODP
+threads and let each of these thread do their own `odp_shm_addr()` to get the
+block address. Directly sending the address returned by `odp_shm_addr()` from
+one ODP thread to another would however possibly fail (the address may make no
+sense in the receiver address space).
The address returned by `odp_shm_addr()` is nevertheless guaranteed to be
aligned according to the alignment requirements provided at block creation
@@ -777,7 +815,13 @@ All shared memory blocks are contiguous in any ODP thread addressing space:
as provided in the `odp_shm_reserve()` call) is read and writeable and
mapping the shared memory block. There is no fragmentation.
-=== Memory behaviour
+The exception to this rule is if the `odp_shm_t` is created with the
+`ODP_SHM_SINGLE_VA` flag. This requests that `odp_shm_addr()` return the same
+virtual address for all ODP threads in this instance. Note that there may be a
+performance cost or shm size limit associated with providing this function in
+some implementations.
+
+=== Memory behavior
By default ODP threads are assumed to behave as cache coherent systems:
Any change performed on a shared memory block is guaranteed to eventually
become visible to other ODP threads sharing this memory block.
@@ -896,15 +940,6 @@ to other ODP instances running on the same OS.
Other ODP instances willing to see this exported memory should use the
`odp_shm_import()` ODP function.
-==== ODP_SHM_SW_ONLY
-This flag tells ODP that the shared memory will be used by the ODP application
-software only: no HW (such as DMA, or other accelerator) will ever
-try to access the memory. No other ODP call will be involved on this memory
-(as ODP calls could implicitly involve HW, depending on the ODP
-implementation), except for `odp_shm_lookup()` and `odp_shm_free()`.
-ODP implementations may use this flag as a hint for performance optimization,
-or may as well ignore this flag.
-
==== ODP_SHM_SINGLE_VA
This flag is used to guarantee the uniqueness of the address at which
the shared memory is mapped: without this flag, a given memory block may be
@@ -917,28 +952,28 @@ same value on all ODP threads, for a given memory block, in this case)
Note that ODP implementations may have restrictions of the amount of memory
which can be allocated with this flag.
-== Queues
+== Queues and the Scheduler
Queues are the fundamental event sequencing mechanism provided by ODP and all
ODP applications make use of them either explicitly or implicitly. Queues are
created via the 'odp_queue_create()' API that returns a handle of type
`odp_queue_t` that is used to refer to this queue in all subsequent APIs that
-reference it. Queues have one of two ODP-defined _types_, POLL, and SCHED that
-determine how they are used. POLL queues directly managed by the ODP
+reference it. Queues have one of two ODP-defined _types_, PLAIN, and SCHED that
+determine how they are used. PLAIN queues directly managed by the ODP
application while SCHED queues make use of the *ODP scheduler* to provide
automatic scalable dispatching and synchronization services.
-.Operations on POLL queues
+.Operations on PLAIN queues
[source,c]
----
-odp_queue_t poll_q1 = odp_queue_create("poll queue 1", ODP_QUEUE_TYPE_POLL, NULL);
-odp_queue_t poll_q2 = odp_queue_create("poll queue 2", ODP_QUEUE_TYPE_POLL, NULL);
+odp_queue_t plain_q1 = odp_queue_create("poll queue 1", ODP_QUEUE_TYPE_PLAIN, NULL);
+odp_queue_t plain_q2 = odp_queue_create("poll queue 2", ODP_QUEUE_TYPE_PLAIN, NULL);
...
-odp_event_t ev = odp_queue_deq(poll_q1);
+odp_event_t ev = odp_queue_deq(plain_q1);
...do something
-int rc = odp_queue_enq(poll_q2, ev);
+int rc = odp_queue_enq(plain_q2, ev);
----
-The key distinction is that dequeueing events from POLL queues is an
+The key distinction is that dequeueing events from PLAIN queues is an
application responsibility while dequeueing events from SCHED queues is the
responsibility of the ODP scheduler.
@@ -950,7 +985,7 @@ odp_queue_param_init(&qp);
odp_schedule_prio_t prio = ...;
odp_schedule_group_t sched_group = ...;
qp.sched.prio = prio;
-qp.sched.sync = ODP_SCHED_SYNC_[NONE|ATOMIC|ORDERED];
+qp.sched.sync = ODP_SCHED_SYNC_[PARALLEL|ATOMIC|ORDERED];
qp.sched.group = sched_group;
qp.lock_count = n; /* Only relevant for ordered queues */
odp_queue_t sched_q1 = odp_queue_create("sched queue 1", ODP_QUEUE_TYPE_SCHED, &qp);
@@ -1006,8 +1041,8 @@ Three types of queue scheduler synchronization area supported: Parallel,
Atomic, and Ordered.
==== Parallel Queues
-SCHED queues that specify a sync mode of ODP_SCHED_SYNC_NONE are unrestricted
-in how events are processed.
+SCHED queues that specify a sync mode of ODP_SCHED_SYNC_PARALLEL are
+unrestricted in how events are processed.
.Parallel Queue Scheduling
image::parallel_queue.svg[align="center"]
@@ -1136,11 +1171,179 @@ until the locking order for this lock for all prior events has been resolved
and then enters the critical section. The *odp_schedule_order_unlock()* call
releases the critical section and allows the next order to enter it.
+=== Scheduler Capabilities and Configuration
+As with other ODP components, the ODP scheduler offers a range of capabilities
+and configuration options that are used by applications to control its
+behavior.
+
+The sequence of API calls used by applications that make use of the scheduler
+is as follows:
+
+.ODP API Scheduler Usage
+[source,c]
+-----
+odp_schedule_capability()
+odp_schedule_config_init()
+odp_schedule_config()
+odp_schedule()
+-----
+The `odp_schedule_capability()` API returns an `odp_schedule_capability_t`
+struct that defines various limits and capabilities offered by this
+implementation of the ODP scheduler:
+
+.ODP Scheduler Capabilities
+[source,c]
+-----
+/**
+ * Scheduler capabilities
+ */
+typedef struct odp_schedule_capability_t {
+ /** Maximum number of ordered locks per queue */
+ uint32_t max_ordered_locks;
+
+ /** Maximum number of scheduling groups */
+ uint32_t max_groups;
+
+ /** Number of scheduling priorities */
+ uint32_t max_prios;
+
+ /** Maximum number of scheduled (ODP_BLOCKING) queues of the default
+ * size. */
+ uint32_t max_queues;
+
+ /** Maximum number of events a scheduled (ODP_BLOCKING) queue can store
+ * simultaneously. The value of zero means that scheduled queues do not
+ * have a size limit, but a single queue can store all available
+ * events. */
+ uint32_t max_queue_size;
+
+ /** Maximum flow ID per queue
+ *
+ * Valid flow ID range in flow aware mode of scheduling is from 0 to
+ * this maximum value. So, maximum number of flows per queue is this
+ * value plus one. A value of 0 indicates that flow aware mode is not
+ * supported. */
+ uint32_t max_flow_id;
+
+ /** Lock-free (ODP_NONBLOCKING_LF) queues support.
+ * The specification is the same as for the blocking implementation. */
+ odp_support_t lockfree_queues;
+
+ /** Wait-free (ODP_NONBLOCKING_WF) queues support.
+ * The specification is the same as for the blocking implementation. */
+ odp_support_t waitfree_queues;
+
+} odp_schedule_capability_t;
+-----
+This struct indicates the various scheduling limits supported by this ODP
+implementation. Of note is the `max_flow_id` capability, which indicates
+whether this implementation is able to operate in _flow aware mode_.
+
+==== Flow Aware Scheduling
+A _flow_ is a sequence of events that share some application-specific meaning
+and context. A good example of a flow might be a TCP connection. Various
+events associated with that connection, such as packets containing
+connection data, as well as associated timeout events used for transmission
+control, are logically connected and meaningful to the application processing
+that TCP connection.
+
+Normally a single flow is associated with an ODP queue. That is, all events
+on a given queue belong to the same flow. So the queue id is synonymous with
+the flow id for those events. However, this is not without drawbacks. Queues
+are relatively heavyweight objects and provide both synchronization as well as
+user contexts. The number of queues supported by a given implementation
+(`max_queues`) may be less than the number of flows an application needs to
+be able to process.
+
+To address these needs, ODP allows schedulers to operate in flow aware mode
+in which flow id is maintained separately as part of each event. Two new
+APIs:
+
+* `odp_event_flow_id()`
+* `odp_event_flow_id_set()`
+
+are used to query and set a 32-bit flow id associated with individual events.
+The assignment and interpretation of individual flow ids is under application
+control.
+
+When operating in flow aware mode, it is the combination of flow id and
+queue id that is used by the scheduler in making scheduling decisions. So,
+for example, an Atomic queue would normally be able to dispatch events only a
+single thread at a time. When operating in flow aware mode, however, the
+scheduler will provide this exclusion only when two events on the same atomic
+queue have the same flow id. If they have different flow ids, then they can be
+scheduled concurrently to different threads.
+
+Note that when operating in this mode, any sharing of queue context must be
+done with application-provided synchronization controls (similar to how
+parallel queues behave).
+
+==== Scheduler Configuration
+After determining the scheduler's capabilities, but before starting to use
+the scheduler to process events, applications must configure the scheduler
+by calling `odp_schedule_config()`.
+
+The argument to this call is the `odp_schedule_config_t` struct:
+
+.ODP Scheduler Configuration
+[source,c]
+-----
+/**
+ * Schedule configuration
+ */
+typedef struct odp_schedule_config_t {
+ /** Maximum number of scheduled queues to be supported.
+ *
+ * @see odp_schedule_capability_t
+ */
+ uint32_t num_queues;
+
+ /** Maximum number of events required to be stored simultaneously in
+ * scheduled queue. This number must not exceed 'max_queue_size'
+ * capability. A value of 0 configures default queue size supported by
+ * the implementation.
+ */
+ uint32_t queue_size;
+
+ /** Maximum flow ID per queue
+ *
+ * This value must not exceed 'max_flow_id' capability. Flow aware
+ * mode of scheduling is enabled when the value is greater than 0.
+ * The default value is 0.
+ *
+ * Application can assign events to specific flows by calling
+ * odp_event_flow_id_set() before enqueuing events into a scheduled
+ * queue. When in flow aware mode, the event flow id value affects
+ * scheduling of the event and synchronization is maintained per flow
+ * within each queue.
+ *
+ * Depending on implementation, there may be much more flows supported
+ * than queues, as flows are lightweight entities.
+ *
+ * @see odp_schedule_capability_t, odp_event_flow_id()
+ */
+ uint32_t max_flow_id;
+
+} odp_schedule_config_t;
+-----
+The `odp_schedule_config_init()` API should be used to initialize this
+struct to its default values. The application then sets whatever
+overrides it needs prior to calling `odp_schedule_config()` to activate
+them. Note that `NULL` may be passed as the argument to `odp_schedule_config()`
+if the application simply wants to use the implementation-defined default
+configuration. In the default configuration, the scheduler does not operate in
+flow aware mode.
+
+Once configured, `odp_schedule()` calls can be made to get events. It is
+a programming error to attempt to use the scheduler before it has been
+configured.
+
=== Queue Scheduling Summary
NOTE: Both ordered and parallel queues improve throughput over atomic queues
due to parallel event processing, but require that the application take
-steps to ensure context data synchronization if needed.
+steps to ensure context data synchronization if needed. The same is true for
+atomic queues when the scheduler is operating in flow aware mode.
include::users-guide-packet.adoc[]
@@ -1150,8 +1353,14 @@ include::users-guide-timer.adoc[]
include::users-guide-crypto.adoc[]
+include::users-guide-ipsec.adoc[]
+
+include::users-guide-comp.adoc[]
+
include::users-guide-tm.adoc[]
include::users-guide-cls.adoc[]
+include::users-guide-utilities-examples.adoc[]
+
include::../glossary.adoc[]
diff --git a/example/Makefile.am b/example/Makefile.am
index 9503a1baf..2ef6abd9d 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1,12 +1,22 @@
SUBDIRS = classifier \
- generator \
+ debug \
hello \
- ipsec \
- ipfragreass \
+ ipsec_api \
+ ipsec_crypto \
l2fwd_simple \
l3fwd \
packet \
+ ping \
+ simple_pipeline \
switch \
- time \
+ sysinfo \
timer \
traffic_mgmt
+
+if HAVE_DW_ATOMIC_CMP_EXC
+SUBDIRS += ipfragreass
+endif
+
+if helper_cli
+SUBDIRS += cli
+endif
diff --git a/example/Makefile.inc b/example/Makefile.inc
index c6d7ff5ff..895901f46 100644
--- a/example/Makefile.inc
+++ b/example/Makefile.inc
@@ -1,14 +1,17 @@
-include $(top_srcdir)/platform/@with_platform@/Makefile.inc
-LIB = $(top_builddir)/lib
-LDADD = $(LIB)/libodp-linux.la $(LIB)/libodphelper.la $(DPDK_PMDS)
+include $(top_srcdir)/Makefile.inc
+
+TESTS_ENVIRONMENT = EXEEXT=${EXEEXT}
+
+LDADD = $(LIB)/libodphelper.la $(LIB)/lib$(ODP_LIB_NAME).la
+
AM_CFLAGS += \
-I$(srcdir) \
-I$(top_srcdir)/example \
- -I$(top_srcdir)/platform/@with_platform@/include \
- -I$(top_srcdir)/include/ \
- -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ \
- -I$(top_srcdir)/helper/include \
- -I$(top_builddir)/platform/@with_platform@/include \
- -I$(top_builddir)/include
-
-AM_LDFLAGS += -L$(LIB)
+ $(ODP_INCLUDES) \
+ $(HELPER_INCLUDES)
+
+if STATIC_APPS
+AM_LDFLAGS += -static
+endif
+
+AM_LDFLAGS += $(PLAT_DEP_LIBS)
diff --git a/example/classifier/.gitignore b/example/classifier/.gitignore
index a356d48da..9156628bb 100644
--- a/example/classifier/.gitignore
+++ b/example/classifier/.gitignore
@@ -1 +1,2 @@
odp_classifier
+pktio_env
diff --git a/example/classifier/Makefile.am b/example/classifier/Makefile.am
index 0c66e145b..4be6648b6 100644
--- a/example/classifier/Makefile.am
+++ b/example/classifier/Makefile.am
@@ -1,10 +1,34 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_classifier$(EXEEXT)
-odp_classifier_LDFLAGS = $(AM_LDFLAGS) -static
-odp_classifier_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_classifier
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
+odp_classifier_SOURCES = odp_classifier.c
-dist_odp_classifier_SOURCES = odp_classifier.c
+if test_example
+if ODP_PKTIO_PCAP
+TESTS = odp_classifier_run.sh
+endif
+endif
+EXTRA_DIST = odp_classifier_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/classifier/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/classifier/odp_classifier.c b/example/classifier/odp_classifier.c
index d67fe5ace..a361bb124 100644
--- a/example/classifier/odp_classifier.c
+++ b/example/classifier/odp_classifier.c
@@ -1,7 +1,15 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @example odp_classifier.c
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * Classifier API example application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdlib.h>
@@ -9,10 +17,11 @@
#include <getopt.h>
#include <unistd.h>
#include <inttypes.h>
-#include <example_debug.h>
+#include <signal.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
+
#include <strings.h>
#include <errno.h>
#include <stdio.h>
@@ -20,12 +29,22 @@
/** @def MAX_WORKERS
* @brief Maximum number of worker threads
*/
-#define MAX_WORKERS 32
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+/** @def MAX_PKT_BURST
+ * @brief Maximum packet burst size
+ */
+#define MAX_PKT_BURST 64
+
+/** @def DEF_PKT_BURST
+ * @brief Default packet burst size
+ */
+#define DEF_PKT_BURST 32
/** @def SHM_PKT_POOL_SIZE
- * @brief Size of the shared memory block
+ * @brief Packet pool size (number of packets)
*/
-#define SHM_PKT_POOL_SIZE (512*2048)
+#define SHM_PKT_POOL_SIZE 10000
/** @def SHM_PKT_POOL_BUF_SIZE
* @brief Buffer size of the packet pool buffer
@@ -35,13 +54,16 @@
/** @def MAX_PMR_COUNT
* @brief Maximum number of Classification Policy
*/
-#define MAX_PMR_COUNT 8
+#define MAX_PMR_COUNT 32
/** @def DISPLAY_STRING_LEN
* @brief Length of string used to display term value
*/
#define DISPLAY_STRING_LEN 32
+/** Maximum PMR value size */
+#define MAX_VAL_SIZE 16
+
/** Get rid of path in filename - only for unix-type paths using '/' */
#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
strrchr((file_name), '/') + 1 : (file_name))
@@ -54,25 +76,46 @@ typedef struct {
odp_atomic_u64_t queue_pkt_count; /**< count of received packets */
odp_atomic_u64_t pool_pkt_count; /**< count of received packets */
char cos_name[ODP_COS_NAME_LEN]; /**< cos name */
+ char src_cos_name[ODP_COS_NAME_LEN]; /**< source cos name */
struct {
odp_cls_pmr_term_t term; /**< odp pmr term value */
- uint64_t val; /**< pmr term value */
- uint64_t mask; /**< pmr term mask */
+ uint8_t value_be[MAX_VAL_SIZE]; /**< value in big endian */
+ uint8_t mask_be[MAX_VAL_SIZE]; /**< mask in big endian */
uint32_t val_sz; /**< size of the pmr term */
uint32_t offset; /**< pmr term offset */
} rule;
char value[DISPLAY_STRING_LEN]; /**< Display string for value */
char mask[DISPLAY_STRING_LEN]; /**< Display string for mask */
+ int has_src_cos;
+
} global_statistics;
typedef struct {
+ char cos_name[ODP_COS_NAME_LEN];
+ uint64_t count;
+} ci_pass_counters;
+
+typedef struct {
+ odp_pktout_queue_t pktout[MAX_WORKERS];
+ int num_pktout;
global_statistics stats[MAX_PMR_COUNT];
+ ci_pass_counters ci_pass_rules[MAX_PMR_COUNT];
int policy_count; /**< global policy count */
+ int num_ci_pass_rules; /**< ci pass count */
int appl_mode; /**< application mode */
odp_atomic_u64_t total_packets; /**< total received packets */
- int cpu_count; /**< Number of CPUs to use */
+ unsigned int cpu_count; /**< Number of CPUs to use */
uint32_t time; /**< Number of seconds to run */
char *if_name; /**< pointer to interface names */
+ int shutdown; /**< Shutdown threads if !0 */
+ int shutdown_sig;
+ int verbose;
+ int promisc_mode; /**< Promiscuous mode enabled */
+ int classifier_enable;
+ int parse_layer;
+ int cos_pools;
+ int pool_size;
+ int burst_size;
} appl_args_t;
enum packet_mode {
@@ -80,22 +123,47 @@ enum packet_mode {
APPL_MODE_REPLY /**< Packet is sent back */
};
-static int shutdown; /**< Shutdown threads if !0 */
+static appl_args_t *appl_args_gbl;
-/* helper funcs */
static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len);
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static int parse_args(int argc, char *argv[], appl_args_t *appl_args);
static void print_info(char *progname, appl_args_t *appl_args);
-static void usage(char *progname);
-static void configure_cos(odp_cos_t default_cos, appl_args_t *args);
-static odp_cos_t configure_default_cos(odp_pktio_t pktio, appl_args_t *args);
-static int convert_str_to_pmr_enum(char *token, odp_cls_pmr_term_t *term,
- uint32_t *offset);
-static int parse_pmr_policy(appl_args_t *appl_args, char *argv[], char *optarg);
-
-static inline
-void print_cls_statistics(appl_args_t *args)
+static void usage(void);
+
+static inline int check_ci_pass_count(appl_args_t *args)
+{
+ int i, j;
+ uint64_t count;
+
+ if (args->num_ci_pass_rules == 0)
+ return 0;
+
+ for (i = 0; i < args->num_ci_pass_rules; i++) {
+ for (j = 0; j < args->policy_count; j++) {
+ if (!strcmp(args->stats[j].cos_name,
+ args->ci_pass_rules[i].cos_name)) {
+ count = odp_atomic_load_u64(&args->stats[i].queue_pkt_count);
+ if (args->ci_pass_rules[i].count > count) {
+ ODPH_ERR("Error: Cos = %s, expected packets = %" PRIu64 ","
+ "received packet = %" PRIu64 "\n",
+ args->stats[j].cos_name,
+ args->ci_pass_rules[i].count, count);
+ return -1;
+ }
+ break;
+ }
+ }
+ if (j == args->policy_count) {
+ ODPH_ERR("Error: invalid Cos:%s specified for CI pass count\n",
+ args->ci_pass_rules[i].cos_name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline void print_cls_statistics(appl_args_t *args)
{
int i;
uint32_t timeout;
@@ -128,7 +196,7 @@ void print_cls_statistics(appl_args_t *args)
printf("\n");
for (i = 0; i < args->policy_count; i++)
printf("%-12s |", args->stats[i].cos_name);
- printf("Total Packets");
+ printf("%-6s %-6s", "Total", "Mpps");
printf("\n");
for (i = 0; i < args->policy_count; i++)
printf("%-6s %-6s|", "queue", "pool");
@@ -141,7 +209,12 @@ void print_cls_statistics(appl_args_t *args)
if (timeout == 0)
infinite = 1;
+ uint64_t total_packets, last_total_packets = 0;
+ odp_time_t start = odp_time_local(), end;
+ float mpps;
+
for (; timeout > 0 || infinite; timeout--) {
+ sleep(1);
for (i = 0; i < args->policy_count; i++) {
printf("%-6" PRIu64 " ",
odp_atomic_load_u64(&args->stats[i]
@@ -151,52 +224,35 @@ void print_cls_statistics(appl_args_t *args)
.pool_pkt_count));
}
- printf("%-" PRIu64, odp_atomic_load_u64(&args->
- total_packets));
+ end = odp_time_local();
+ total_packets = odp_atomic_load_u64(&args->total_packets);
+ mpps = (total_packets - last_total_packets) /
+ (odp_time_diff_ns(end, start) / 1000.0);
+ printf("%-6" PRIu64 " %-6.3f\n", total_packets, mpps);
+ last_total_packets = total_packets;
+ start = end;
- sleep(1);
- printf("\r");
- fflush(stdout);
+ if (args->shutdown_sig)
+ break;
}
printf("\n");
}
-static inline
-int parse_mask(const char *str, uint64_t *mask)
-{
- uint64_t b;
- int ret;
-
- ret = sscanf(str, "%" SCNx64, &b);
- *mask = b;
- return ret != 1;
-}
-
-static
-int parse_value(const char *str, uint64_t *val, uint32_t *val_sz)
+static int parse_custom(const char *str, uint8_t *buf_be, int max_size)
{
- size_t len;
- size_t i;
- int converted;
- union {
- uint64_t u64;
- uint8_t u8[8];
- } buf = {.u64 = 0};
+ int i, len;
+ /* hex string without 0x prefix */
len = strlen(str);
- if (len > 2 * sizeof(buf))
+ if (len > 2 * max_size)
return -1;
- for (i = 0; i < len; i += 2) {
- converted = sscanf(&str[i], "%2" SCNx8, &buf.u8[i / 2]);
- if (1 != converted)
+ for (i = 0; i < len; i += 2)
+ if (sscanf(&str[i], "%2" SCNx8, &buf_be[i / 2]) != 1)
return -1;
- }
- *val = buf.u64;
- *val_sz = len / 2;
- return 0;
+ return len / 2;
}
/**
@@ -213,6 +269,10 @@ static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool)
odp_pktio_t pktio;
odp_pktio_param_t pktio_param;
odp_pktin_queue_param_t pktin_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t cfg;
+ odp_pktout_queue_param_t pktout_queue_param;
+ int num_tx;
odp_pktio_param_init(&pktio_param);
pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
@@ -220,31 +280,76 @@ static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool)
/* Open a packet IO instance */
pktio = odp_pktio_open(dev, pool, &pktio_param);
if (pktio == ODP_PKTIO_INVALID) {
- if (odp_errno() == EPERM)
- EXAMPLE_ERR("Root level permission required\n");
+ ODPH_ERR("pktio create failed for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
- EXAMPLE_ERR("pktio create failed for %s\n", dev);
+ if (odp_pktio_capability(pktio, &capa)) {
+ ODPH_ERR("pktio capability failed for %s\n", dev);
exit(EXIT_FAILURE);
}
odp_pktin_queue_param_init(&pktin_param);
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ pktin_param.classifier_enable = appl_args_gbl->classifier_enable;
if (odp_pktin_queue_config(pktio, &pktin_param)) {
- EXAMPLE_ERR("pktin queue config failed for %s\n", dev);
+ ODPH_ERR("pktin queue config failed for %s\n", dev);
exit(EXIT_FAILURE);
}
- if (odp_pktout_queue_config(pktio, NULL)) {
- EXAMPLE_ERR("pktout queue config failed for %s\n", dev);
+ num_tx = appl_args_gbl->cpu_count;
+
+ if (num_tx > (int)capa.max_output_queues) {
+ printf("Sharing %i output queues between %i workers\n",
+ capa.max_output_queues, num_tx);
+ num_tx = capa.max_output_queues;
+ }
+
+ appl_args_gbl->num_pktout = num_tx;
+
+ odp_pktout_queue_param_init(&pktout_queue_param);
+ pktout_queue_param.num_queues = num_tx;
+
+ if (odp_pktout_queue_config(pktio, &pktout_queue_param)) {
+ ODPH_ERR("pktout queue config failed for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue(pktio, appl_args_gbl->pktout, num_tx) != num_tx) {
+ ODPH_ERR("Pktout queue query failed: %s\n", dev);
exit(EXIT_FAILURE);
}
- printf(" created pktio:%02" PRIu64
- ", dev:%s, queue mode (ATOMIC queues)\n"
- " \tdefault pktio%02" PRIu64 "\n",
- odp_pktio_to_u64(pktio), dev,
- odp_pktio_to_u64(pktio));
+ if (appl_args_gbl->promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
+ if (!capa.set_op.op.promisc_mode) {
+ ODPH_ERR("enabling promisc mode not supported %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktio_promisc_mode_set(pktio, true)) {
+ ODPH_ERR("failed to enable promisc mode for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ printf("created pktio:%" PRIu64 ", dev:%s", odp_pktio_to_u64(pktio), dev);
+
+ odph_ethaddr_t mac;
+
+ if (odp_pktio_mac_addr(pktio, &mac, sizeof(mac)) == sizeof(mac)) {
+ printf(", mac");
+ for (int c = 0; c < (int)sizeof(mac); c++)
+ printf(":%02x", mac.addr[c]);
+ }
+
+ printf("\n");
+
+ odp_pktio_config_init(&cfg);
+ cfg.parser.layer = appl_args_gbl->parse_layer;
+ if (odp_pktio_config(pktio, &cfg)) {
+ ODPH_ERR("failed to configure pktio %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
return pktio;
}
@@ -256,76 +361,113 @@ static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool)
static int pktio_receive_thread(void *arg)
{
int thr;
- odp_pktout_queue_t pktout;
- odp_packet_t pkt;
+ odp_packet_t pkt[MAX_PKT_BURST];
odp_pool_t pool;
- odp_event_t ev;
- unsigned long err_cnt = 0;
+ odp_event_t ev[MAX_PKT_BURST];
odp_queue_t queue;
- int i;
+ int i, j, num, dropped, sent;
+ global_statistics *stats;
+ unsigned long err_cnt = 0;
thr = odp_thread_id();
appl_args_t *appl = (appl_args_t *)arg;
- global_statistics *stats;
+ uint64_t wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ odp_pktout_queue_t pktout = appl_args_gbl->pktout[thr % appl_args_gbl->num_pktout];
/* Loop packets */
for (;;) {
- odp_pktio_t pktio_tmp;
-
- if (shutdown)
+ if (appl->shutdown)
break;
/* Use schedule to get buf from any input queue */
- ev = odp_schedule(&queue,
- odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+ num = odp_schedule_multi(&queue, wait_time, ev, appl_args_gbl->burst_size);
/* Loop back to receive packets incase of invalid event */
- if (odp_unlikely(ev == ODP_EVENT_INVALID))
+ if (odp_unlikely(!num))
continue;
- pkt = odp_packet_from_event(ev);
+ odp_packet_from_event_multi(pkt, ev, num);
- /* Total packets received */
- odp_atomic_inc_u64(&appl->total_packets);
+ if (odp_unlikely(appl->verbose)) {
+ for (j = 0; j < num; j++) {
+ odp_queue_info_t info;
+ uint32_t len = odp_packet_len(pkt[j]);
- /* Drop packets with errors */
- if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
- EXAMPLE_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
- continue;
+ if (odp_queue_info(queue, &info) == 0)
+ printf("Queue: %s\n", info.name);
+
+ if (len > 96)
+ len = 96;
+
+ odp_packet_print_data(pkt[j], 0, len);
+ }
}
- pktio_tmp = odp_packet_input(pkt);
+ /* Total packets received */
+ odp_atomic_add_u64(&appl->total_packets, num);
- if (odp_pktout_queue(pktio_tmp, &pktout, 1) != 1) {
- EXAMPLE_ERR(" [%02i] Error: no output queue\n", thr);
- return -1;
+ /* Drop packets with errors */
+ dropped = drop_err_pkts(pkt, num);
+ if (odp_unlikely(dropped)) {
+ num -= dropped;
+ err_cnt += dropped;
+ ODPH_ERR("Drop frame - err_cnt:%lu\n", err_cnt);
}
- pool = odp_packet_pool(pkt);
+ for (j = 0; j < num; j++) {
+ pool = odp_packet_pool(pkt[j]);
- /* Swap Eth MACs and possibly IP-addrs before sending back */
- swap_pkt_addrs(&pkt, 1);
- for (i = 0; i < MAX_PMR_COUNT; i++) {
- stats = &appl->stats[i];
- if (queue == stats->queue)
- odp_atomic_inc_u64(&stats->queue_pkt_count);
- if (pool == stats->pool)
- odp_atomic_inc_u64(&stats->pool_pkt_count);
+ for (i = 0; i < MAX_PMR_COUNT; i++) {
+ stats = &appl->stats[i];
+ if (queue == stats->queue)
+ odp_atomic_inc_u64(&stats->queue_pkt_count);
+ if (pool == stats->pool)
+ odp_atomic_inc_u64(&stats->pool_pkt_count);
+ }
}
if (appl->appl_mode == APPL_MODE_DROP) {
- odp_packet_free(pkt);
+ odp_packet_free_multi(pkt, num);
continue;
}
- if (odp_pktout_send(pktout, &pkt, 1) < 1) {
- EXAMPLE_ERR(" [%i] Packet send failed.\n", thr);
- odp_packet_free(pkt);
+ /* Swap Eth MACs and possibly IP-addrs before sending back */
+ swap_pkt_addrs(pkt, num);
+
+ sent = odp_pktout_send(pktout, pkt, num);
+ sent = sent < 0 ? 0 : sent;
+
+ if (sent != num) {
+ ODPH_ERR(" [%i] Packet send failed\n", thr);
+ odp_packet_free_multi(pkt + sent, num - sent);
}
}
return 0;
}
+static odp_pool_t pool_create(const char *name)
+{
+ static odp_pool_t pool = ODP_POOL_INVALID;
+ odp_pool_param_t pool_params;
+
+ if (!appl_args_gbl->cos_pools && pool != ODP_POOL_INVALID)
+ return pool;
+
+ odp_pool_param_init(&pool_params);
+ pool_params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ pool_params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ pool_params.pkt.num = appl_args_gbl->pool_size;
+ pool_params.type = ODP_POOL_PACKET;
+ pool = odp_pool_create(name, &pool_params);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: failed to create pool %s\n", name);
+ exit(EXIT_FAILURE);
+ }
+
+ return pool;
+}
+
static odp_cos_t configure_default_cos(odp_pktio_t pktio, appl_args_t *args)
{
odp_queue_param_t qparam;
@@ -335,53 +477,42 @@ static odp_cos_t configure_default_cos(odp_pktio_t pktio, appl_args_t *args)
odp_queue_t queue_default;
odp_pool_t pool_default;
odp_cos_t cos_default;
- odp_pool_param_t pool_params;
odp_cls_cos_param_t cls_param;
global_statistics *stats = args->stats;
odp_queue_param_init(&qparam);
qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
qparam.sched.group = ODP_SCHED_GROUP_ALL;
queue_default = odp_queue_create(queue_name, &qparam);
if (queue_default == ODP_QUEUE_INVALID) {
- EXAMPLE_ERR("Error: default queue create failed.\n");
+ ODPH_ERR("Error: default queue create failed\n");
exit(EXIT_FAILURE);
}
- odp_pool_param_init(&pool_params);
- pool_params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- pool_params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- pool_params.pkt.num = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE;
- pool_params.type = ODP_POOL_PACKET;
- pool_default = odp_pool_create(pool_name, &pool_params);
-
- if (pool_default == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: default pool create failed.\n");
- exit(EXIT_FAILURE);
- }
+ pool_default = pool_create(pool_name);
odp_cls_cos_param_init(&cls_param);
cls_param.pool = pool_default;
cls_param.queue = queue_default;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
+
cos_default = odp_cls_cos_create(cos_name, &cls_param);
if (cos_default == ODP_COS_INVALID) {
- EXAMPLE_ERR("Error: default cos create failed.\n");
+ ODPH_ERR("Error: default cos create failed\n");
exit(EXIT_FAILURE);
}
if (0 > odp_pktio_default_cos_set(pktio, cos_default)) {
- EXAMPLE_ERR("odp_pktio_default_cos_set failed");
+ ODPH_ERR("odp_pktio_default_cos_set failed\n");
exit(EXIT_FAILURE);
}
stats[args->policy_count].cos = cos_default;
/* add default queue to global stats */
stats[args->policy_count].queue = queue_default;
- stats[args->policy_count].pool = pool_default;
+ if (appl_args_gbl->cos_pools)
+ stats[args->policy_count].pool = pool_default;
snprintf(stats[args->policy_count].cos_name,
sizeof(stats[args->policy_count].cos_name),
"%s", cos_name);
@@ -391,14 +522,29 @@ static odp_cos_t configure_default_cos(odp_pktio_t pktio, appl_args_t *args)
return cos_default;
}
+static int find_cos(appl_args_t *args, const char *name, odp_cos_t *cos)
+{
+ global_statistics *stats;
+ int i;
+
+ for (i = 0; i < args->policy_count - 1; i++) {
+ stats = &args->stats[i];
+
+ if (strcmp(stats->cos_name, name) == 0) {
+ *cos = stats->cos;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
static void configure_cos(odp_cos_t default_cos, appl_args_t *args)
{
char cos_name[ODP_COS_NAME_LEN];
- char queue_name[ODP_QUEUE_NAME_LEN];
char pool_name[ODP_POOL_NAME_LEN];
- odp_pool_param_t pool_params;
+ const char *queue_name;
odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
int i;
global_statistics *stats;
odp_queue_param_t qparam;
@@ -408,59 +554,70 @@ static void configure_cos(odp_cos_t default_cos, appl_args_t *args)
odp_queue_param_init(&qparam);
qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = i % odp_schedule_num_prio();
qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
qparam.sched.group = ODP_SCHED_GROUP_ALL;
- snprintf(queue_name, sizeof(queue_name), "%sQueue%d",
- args->stats[i].cos_name, i);
+ queue_name = args->stats[i].cos_name;
stats->queue = odp_queue_create(queue_name, &qparam);
if (ODP_QUEUE_INVALID == stats->queue) {
- EXAMPLE_ERR("odp_queue_create failed");
+ ODPH_ERR("odp_queue_create failed\n");
exit(EXIT_FAILURE);
}
- odp_pool_param_init(&pool_params);
- pool_params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- pool_params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- pool_params.pkt.num = SHM_PKT_POOL_SIZE /
- SHM_PKT_POOL_BUF_SIZE;
- pool_params.type = ODP_POOL_PACKET;
-
snprintf(pool_name, sizeof(pool_name), "%sPool%d",
args->stats[i].cos_name, i);
- stats->pool = odp_pool_create(pool_name, &pool_params);
-
- if (stats->pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: default pool create failed.\n");
- exit(EXIT_FAILURE);
- }
snprintf(cos_name, sizeof(cos_name), "CoS%s",
stats->cos_name);
odp_cls_cos_param_init(&cls_param);
- cls_param.pool = stats->pool;
+ cls_param.pool = pool_create(pool_name);
+ if (appl_args_gbl->cos_pools)
+ stats->pool = cls_param.pool;
cls_param.queue = stats->queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
+
stats->cos = odp_cls_cos_create(cos_name, &cls_param);
+ odp_atomic_init_u64(&stats->queue_pkt_count, 0);
+ odp_atomic_init_u64(&stats->pool_pkt_count, 0);
+ }
+
+ for (i = 0; i < args->policy_count - 1; i++) {
+ odp_pmr_param_t pmr_param;
+ odp_cos_t src_cos = default_cos;
+
+ stats = &args->stats[i];
+
+ if (stats->has_src_cos) {
+ if (find_cos(args, stats->src_cos_name, &src_cos)) {
+ ODPH_ERR("find_cos failed\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
odp_cls_pmr_param_init(&pmr_param);
pmr_param.term = stats->rule.term;
- pmr_param.match.value = &stats->rule.val;
- pmr_param.match.mask = &stats->rule.mask;
+ pmr_param.match.value = stats->rule.value_be;
+ pmr_param.match.mask = stats->rule.mask_be;
pmr_param.val_sz = stats->rule.val_sz;
pmr_param.offset = stats->rule.offset;
- stats->pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos,
+ stats->pmr = odp_cls_pmr_create(&pmr_param, 1, src_cos,
stats->cos);
- if (stats->pmr == ODP_PMR_INVAL) {
- EXAMPLE_ERR("odp_pktio_pmr_cos failed");
+ if (stats->pmr == ODP_PMR_INVALID) {
+ ODPH_ERR("odp_pktio_pmr_cos failed\n");
exit(EXIT_FAILURE);
}
-
- odp_atomic_init_u64(&stats->queue_pkt_count, 0);
- odp_atomic_init_u64(&stats->pool_pkt_count, 0);
}
+
+}
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ if (appl_args_gbl == NULL)
+ return;
+ appl_args_gbl->shutdown_sig = 1;
}
/**
@@ -468,30 +625,44 @@ static void configure_cos(odp_cos_t default_cos, appl_args_t *args)
*/
int main(int argc, char *argv[])
{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
odp_pool_t pool;
int num_workers;
int i;
odp_cpumask_t cpumask;
char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_pool_param_t params;
odp_pktio_t pktio;
appl_args_t *args;
odp_cos_t default_cos;
odp_shm_t shm;
int ret;
odp_instance_t instance;
- odph_odpthread_params_t thr_params;
+ odp_init_t init_param;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+
+ signal(SIGINT, sig_handler);
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
/* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed\n");
exit(EXIT_FAILURE);
}
/* Init this thread */
if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
+ ODPH_ERR("Error: ODP local init failed\n");
exit(EXIT_FAILURE);
}
@@ -500,27 +671,28 @@ int main(int argc, char *argv[])
ODP_CACHE_LINE_SIZE, 0);
if (shm == ODP_SHM_INVALID) {
- EXAMPLE_ERR("Error: shared mem reserve failed.\n");
+ ODPH_ERR("Error: shared mem reserve failed\n");
exit(EXIT_FAILURE);
}
args = odp_shm_addr(shm);
if (args == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
+ ODPH_ERR("Error: shared mem alloc failed\n");
exit(EXIT_FAILURE);
}
+ appl_args_gbl = args;
memset(args, 0, sizeof(*args));
/* Parse and store the application arguments */
- parse_args(argc, argv, args);
+ if (parse_args(argc, argv, args))
+ goto args_error;
/* Print both system and application information */
print_info(NO_PATH(argv[0]), args);
- /* Default to system CPU count unless user specified */
num_workers = MAX_WORKERS;
- if (args->cpu_count)
+ if (args->cpu_count && args->cpu_count < MAX_WORKERS)
num_workers = args->cpu_count;
/* Get default worker cpumask */
@@ -532,18 +704,10 @@ int main(int argc, char *argv[])
printf("cpu mask: %s\n", cpumaskstr);
/* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create("packet_pool", &params);
+ pool = pool_create("packet_pool");
- if (pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
/* odp_pool_print(pool); */
odp_atomic_init_u64(&args->total_packets, 0);
@@ -556,52 +720,80 @@ int main(int argc, char *argv[])
configure_cos(default_cos, args);
+ printf("\n");
+ odp_pool_print_all();
+ odp_cls_print_all();
+
if (odp_pktio_start(pktio)) {
- EXAMPLE_ERR("Error: unable to start pktio.\n");
+ ODPH_ERR("Error: unable to start pktio\n");
exit(EXIT_FAILURE);
}
/* Create and init worker threads */
memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+ odph_thread_param_init(&thr_param);
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = pktio_receive_thread;
- thr_params.arg = args;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
+ thr_param.start = pktio_receive_thread;
+ thr_param.arg = args;
+ thr_param.thr_type = ODP_THREAD_WORKER;
- print_cls_statistics(args);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ if (args->verbose == 0) {
+ print_cls_statistics(args);
+ } else {
+ int timeout = args->time;
+
+ for (i = 0; timeout == 0 || i < timeout; i++) {
+ if (args->shutdown_sig)
+ break;
+
+ sleep(1);
+ }
+ }
odp_pktio_stop(pktio);
- shutdown = 1;
- odph_odpthreads_join(thread_tbl);
+ args->shutdown = 1;
+ odph_thread_join(thread_tbl, num_workers);
+
+ if (check_ci_pass_count(args)) {
+ ODPH_ERR("Error: Packet count verification failed\n");
+ exit(EXIT_FAILURE);
+ }
for (i = 0; i < args->policy_count; i++) {
if ((i != args->policy_count - 1) &&
odp_cls_pmr_destroy(args->stats[i].pmr))
- EXAMPLE_ERR("err: odp_cls_pmr_destroy for %d\n", i);
+ ODPH_ERR("err: odp_cls_pmr_destroy for %d\n", i);
if (odp_cos_destroy(args->stats[i].cos))
- EXAMPLE_ERR("err: odp_cos_destroy for %d\n", i);
+ ODPH_ERR("err: odp_cos_destroy for %d\n", i);
if (odp_queue_destroy(args->stats[i].queue))
- EXAMPLE_ERR("err: odp_queue_destroy for %d\n", i);
- if (odp_pool_destroy(args->stats[i].pool))
- EXAMPLE_ERR("err: odp_pool_destroy for %d\n", i);
+ ODPH_ERR("err: odp_queue_destroy for %d\n", i);
+ if (args->cos_pools && odp_pool_destroy(args->stats[i].pool))
+ ODPH_ERR("err: odp_pool_destroy for %d\n", i);
}
- free(args->if_name);
- odp_shm_free(shm);
if (odp_pktio_close(pktio))
- EXAMPLE_ERR("err: close pktio error\n");
+ ODPH_ERR("err: close pktio error\n");
if (odp_pool_destroy(pool))
- EXAMPLE_ERR("err: odp_pool_destroy error\n");
+ ODPH_ERR("err: odp_pool_destroy error\n");
+
+ free(args->if_name);
+
+args_error:
+ odp_shm_free(shm);
ret = odp_term_local();
if (ret)
- EXAMPLE_ERR("odp_term_local error %d\n", ret);
+ ODPH_ERR("odp_term_local error %d\n", ret);
ret = odp_term_global(instance);
if (ret)
- EXAMPLE_ERR("odp_term_global error %d\n", ret);
+ ODPH_ERR("odp_term_global error %d\n", ret);
printf("Exit\n\n");
return ret;
}
@@ -615,26 +807,26 @@ int main(int argc, char *argv[])
* @param pkt_tbl Array of packet
* @param len Length of pkt_tbl[]
*
- * @return Number of packets with no detected error
+ * @return Number of packets dropped
*/
static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
{
odp_packet_t pkt;
- unsigned pkt_cnt = len;
unsigned i, j;
+ int dropped = 0;
for (i = 0, j = 0; i < len; ++i) {
pkt = pkt_tbl[i];
if (odp_unlikely(odp_packet_has_error(pkt))) {
odp_packet_free(pkt); /* Drop */
- pkt_cnt--;
+ dropped++;
} else if (odp_unlikely(i != j++)) {
pkt_tbl[j-1] = pkt;
}
}
- return pkt_cnt;
+ return dropped;
}
/**
@@ -674,105 +866,289 @@ static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len)
}
}
-static int convert_str_to_pmr_enum(char *token, odp_cls_pmr_term_t *term,
- uint32_t *offset)
+static int convert_str_to_pmr_enum(char *token, odp_cls_pmr_term_t *term)
{
if (NULL == token)
return -1;
- if (0 == strcasecmp(token, "ODP_PMR_SIP_ADDR")) {
+ if (strcasecmp(token, "ODP_PMR_ETHTYPE_0") == 0) {
+ *term = ODP_PMR_ETHTYPE_0;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_ETHTYPE_X") == 0) {
+ *term = ODP_PMR_ETHTYPE_X;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_VLAN_ID_0") == 0) {
+ *term = ODP_PMR_VLAN_ID_0;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_VLAN_ID_X") == 0) {
+ *term = ODP_PMR_VLAN_ID_X;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_UDP_DPORT") == 0) {
+ *term = ODP_PMR_UDP_DPORT;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_TCP_DPORT") == 0) {
+ *term = ODP_PMR_TCP_DPORT;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_UDP_SPORT") == 0) {
+ *term = ODP_PMR_UDP_SPORT;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_TCP_SPORT") == 0) {
+ *term = ODP_PMR_TCP_SPORT;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_DIP_ADDR") == 0) {
+ *term = ODP_PMR_DIP_ADDR;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_SIP_ADDR") == 0) {
*term = ODP_PMR_SIP_ADDR;
return 0;
- } else {
- errno = 0;
- *offset = strtoul(token, NULL, 0);
- if (errno)
- return -1;
+ } else if (strcasecmp(token, "ODP_PMR_DMAC") == 0) {
+ *term = ODP_PMR_DMAC;
+ return 0;
+ } else if (strcasecmp(token, "ODP_PMR_CUSTOM_FRAME") == 0) {
*term = ODP_PMR_CUSTOM_FRAME;
return 0;
+ } else if (strcasecmp(token, "ODP_PMR_CUSTOM_L3") == 0) {
+ *term = ODP_PMR_CUSTOM_L3;
+ return 0;
}
+
return -1;
}
-
-static int parse_pmr_policy(appl_args_t *appl_args, char *argv[], char *optarg)
+static int parse_pmr_policy(appl_args_t *appl_args, char *optarg)
{
int policy_count;
- char *token;
+ char *token, *cos0, *cos1, *cur_char;
size_t len;
odp_cls_pmr_term_t term;
global_statistics *stats;
+ odph_ethaddr_t mac;
char *pmr_str;
- uint32_t offset;
- uint32_t ip_addr;
+ uint32_t offset, ip_addr, u32;
+ unsigned long int value, mask;
+ uint16_t u16;
+ int val_sz, mask_sz;
policy_count = appl_args->policy_count;
stats = appl_args->stats;
/* last array index is needed for default queue */
if (policy_count >= MAX_PMR_COUNT - 1) {
- EXAMPLE_ERR("Maximum allowed PMR reached\n");
+ ODPH_ERR("Too many policies. Max count is %i.\n",
+ MAX_PMR_COUNT - 1);
return -1;
}
len = strlen(optarg);
len++;
pmr_str = malloc(len);
+ if (pmr_str == NULL) {
+ ODPH_ERR("Memory allocation failed\n");
+ return -1;
+ }
strcpy(pmr_str, optarg);
/* PMR TERM */
+ /* <term>:<xxx>:<yyy>:<src_cos>:<dst_cos> */
token = strtok(pmr_str, ":");
- if (convert_str_to_pmr_enum(token, &term, &offset)) {
- EXAMPLE_ERR("Invalid ODP_PMR_TERM string\n");
- exit(EXIT_FAILURE);
+ if (convert_str_to_pmr_enum(token, &term)) {
+ ODPH_ERR("Invalid ODP_PMR_TERM string\n");
+ goto error;
}
stats[policy_count].rule.term = term;
+ stats[policy_count].rule.offset = 0;
/* PMR value */
- switch (term) {
+ switch (term) {
+ case ODP_PMR_ETHTYPE_0:
+ /* Fall through */
+ case ODP_PMR_ETHTYPE_X:
+ /* Fall through */
+ /* :<type>:<mask> */
+ case ODP_PMR_VLAN_ID_0:
+ /* Fall through */
+ case ODP_PMR_VLAN_ID_X:
+ /* Fall through */
+ /* :<vlan_id>:<mask> */
+ case ODP_PMR_UDP_DPORT:
+ /* Fall through */
+ case ODP_PMR_TCP_DPORT:
+ /* Fall through */
+ case ODP_PMR_UDP_SPORT:
+ /* Fall through */
+ case ODP_PMR_TCP_SPORT:
+ /* :<port>:<mask> */
+ token = strtok(NULL, ":");
+ strncpy(stats[policy_count].value, token,
+ DISPLAY_STRING_LEN - 1);
+ value = strtoul(token, NULL, 0);
+ u16 = value;
+ u16 = odp_cpu_to_be_16(u16);
+ memcpy(stats[policy_count].rule.value_be, &u16, sizeof(u16));
+
+ token = strtok(NULL, ":");
+ strncpy(stats[policy_count].mask, token,
+ DISPLAY_STRING_LEN - 1);
+ mask = strtoul(token, NULL, 0);
+ u16 = mask;
+ u16 = odp_cpu_to_be_16(u16);
+ memcpy(stats[policy_count].rule.mask_be, &u16, sizeof(u16));
+
+ stats[policy_count].rule.val_sz = 2;
+ break;
+ case ODP_PMR_DIP_ADDR:
+ /* Fall through */
case ODP_PMR_SIP_ADDR:
+ /* :<IP addr>:<mask> */
token = strtok(NULL, ":");
strncpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN - 1);
if (odph_ipv4_addr_parse(&ip_addr, token)) {
- EXAMPLE_ERR("Bad IP address\n");
- exit(EXIT_FAILURE);
+ ODPH_ERR("Bad IP address\n");
+ goto error;
}
- stats[policy_count].rule.val = ip_addr;
+ u32 = odp_cpu_to_be_32(ip_addr);
+ memcpy(stats[policy_count].rule.value_be, &u32, sizeof(u32));
token = strtok(NULL, ":");
strncpy(stats[policy_count].mask, token,
DISPLAY_STRING_LEN - 1);
- parse_mask(token, &stats[policy_count].rule.mask);
+ mask = strtoul(token, NULL, 0);
+ u32 = mask;
+ u32 = odp_cpu_to_be_32(u32);
+ memcpy(stats[policy_count].rule.mask_be, &u32, sizeof(u32));
+
stats[policy_count].rule.val_sz = 4;
- stats[policy_count].rule.offset = 0;
+ break;
+ case ODP_PMR_DMAC:
+ /* :<MAC addr>:<mask> */
+ token = strtok(NULL, ":");
+ strncpy(stats[policy_count].value, token,
+ DISPLAY_STRING_LEN - 1);
+
+ /* Replace hyphens in the MAC string with colons to be compatible with
+ * odph_eth_addr_parse(). */
+ cur_char = token;
+ while ((cur_char = strchr(cur_char, '-')) != NULL)
+ *cur_char++ = ':';
+
+ if (odph_eth_addr_parse(&mac, token)) {
+ ODPH_ERR("Invalid MAC address. Use format 11-22-33-44-55-66.\n");
+ goto error;
+ }
+
+ memcpy(stats[policy_count].rule.value_be, mac.addr, ODPH_ETHADDR_LEN);
+ stats[policy_count].rule.val_sz = 6;
+
+ token = strtok(NULL, ":");
+ strncpy(stats[policy_count].mask, token, DISPLAY_STRING_LEN - 1);
+ mask_sz = parse_custom(token, stats[policy_count].rule.mask_be, ODPH_ETHADDR_LEN);
+ if (mask_sz != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("Invalid mask. Provide mask without 0x prefix.\n");
+ goto error;
+ }
break;
case ODP_PMR_CUSTOM_FRAME:
+ /* Fall through */
+ case ODP_PMR_CUSTOM_L3:
+ /* :<offset>:<value>:<mask> */
+ token = strtok(NULL, ":");
+ errno = 0;
+ offset = strtoul(token, NULL, 0);
+ stats[policy_count].rule.offset = offset;
+ if (errno)
+ goto error;
+
token = strtok(NULL, ":");
strncpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN - 1);
- parse_value(token, &stats[policy_count].rule.val,
- &stats[policy_count].rule.val_sz);
+ val_sz = parse_custom(token,
+ stats[policy_count].rule.value_be,
+ MAX_VAL_SIZE);
+ stats[policy_count].rule.val_sz = val_sz;
+ if (val_sz <= 0)
+ goto error;
+
token = strtok(NULL, ":");
strncpy(stats[policy_count].mask, token,
DISPLAY_STRING_LEN - 1);
- parse_mask(token, &stats[policy_count].rule.mask);
- stats[policy_count].rule.offset = offset;
+ mask_sz = parse_custom(token,
+ stats[policy_count].rule.mask_be,
+ MAX_VAL_SIZE);
+ if (mask_sz != val_sz)
+ goto error;
break;
default:
- usage(argv[0]);
- exit(EXIT_FAILURE);
+ goto error;
}
- /* Queue Name */
- token = strtok(NULL, ":");
+ /* Optional source CoS name and name of this CoS
+ * :<src_cos>:<cos> */
+ cos0 = strtok(NULL, ":");
+ cos1 = strtok(NULL, ":");
+ if (cos0 == NULL)
+ goto error;
+
+ if (cos1) {
+ stats[policy_count].has_src_cos = 1;
+ strncpy(stats[policy_count].src_cos_name, cos0,
+ ODP_COS_NAME_LEN - 1);
+ strncpy(stats[policy_count].cos_name, cos1,
+ ODP_COS_NAME_LEN - 1);
+ } else {
+ strncpy(stats[policy_count].cos_name, cos0,
+ ODP_COS_NAME_LEN - 1);
+ }
- strncpy(stats[policy_count].cos_name, token, ODP_QUEUE_NAME_LEN - 1);
appl_args->policy_count++;
free(pmr_str);
return 0;
+
+error:
+ free(pmr_str);
+ return -1;
+}
+
+static int parse_policy_ci_pass_count(appl_args_t *appl_args, char *optarg)
+{
+ int num_ci_pass_rules;
+ char *token, *value;
+ size_t len;
+ ci_pass_counters *ci_pass_rules;
+ char *count_str;
+
+ num_ci_pass_rules = appl_args->num_ci_pass_rules;
+ ci_pass_rules = appl_args->ci_pass_rules;
+
+ /* last array index is needed for default queue */
+ if (num_ci_pass_rules >= MAX_PMR_COUNT) {
+ ODPH_ERR("Too many ci pass counters. Max count is %i.\n",
+ MAX_PMR_COUNT);
+ return -1;
+ }
+
+ len = strlen(optarg);
+ len++;
+ count_str = malloc(len);
+ if (count_str == NULL) {
+ ODPH_ERR("Memory allocation failed\n");
+ return -1;
+ }
+ strcpy(count_str, optarg);
+
+ token = strtok(count_str, ":");
+ value = strtok(NULL, ":");
+ if (!token || !value) {
+ free(count_str);
+ return -1;
+ }
+ strcpy(ci_pass_rules[num_ci_pass_rules].cos_name, token);
+ ci_pass_rules[num_ci_pass_rules].count = atoll(value);
+ appl_args->num_ci_pass_rules++;
+ free(count_str);
+ return 0;
}
/**
@@ -782,33 +1158,45 @@ static int parse_pmr_policy(appl_args_t *appl_args, char *argv[], char *optarg)
* @param argv[] argument vector
* @param appl_args Store application arguments here
*/
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+static int parse_args(int argc, char *argv[], appl_args_t *appl_args)
{
int opt;
int long_index;
size_t len;
int i;
int interface = 0;
- int policy = 0;
+ int ret = 0;
static const struct option longopts[] = {
{"count", required_argument, NULL, 'c'},
- {"interface", required_argument, NULL, 'i'}, /* return 'i' */
- {"policy", required_argument, NULL, 'p'}, /* return 'p' */
- {"mode", required_argument, NULL, 'm'}, /* return 'm' */
- {"time", required_argument, NULL, 't'}, /* return 't' */
- {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {"interface", required_argument, NULL, 'i'},
+ {"policy", required_argument, NULL, 'p'},
+ {"mode", required_argument, NULL, 'm'},
+ {"time", required_argument, NULL, 't'},
+ {"ci_pass", required_argument, NULL, 'C'},
+ {"promisc_mode", no_argument, NULL, 'P'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"enable", required_argument, NULL, 'e'},
+ {"layer", required_argument, NULL, 'l'},
+ {"dedicated", required_argument, NULL, 'd'},
+ {"size", required_argument, NULL, 's'},
+ {"burst", required_argument, NULL, 'b'},
{NULL, 0, NULL, 0}
};
- static const char *shortopts = "+c:t:i:p:m:t:h";
+ static const char *shortopts = "+c:t:i:p:m:t:C:Pvhe:l:d:s:b:";
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
+ appl_args->cpu_count = 1; /* Use one worker by default */
+ appl_args->verbose = 0;
+ appl_args->promisc_mode = 0;
+ appl_args->classifier_enable = 1;
+ appl_args->parse_layer = ODP_PROTO_LAYER_ALL;
+ appl_args->cos_pools = 1;
+ appl_args->pool_size = SHM_PKT_POOL_SIZE;
+ appl_args->burst_size = DEF_PKT_BURST;
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
+ while (ret == 0) {
opt = getopt_long(argc, argv, shortopts,
longopts, &long_index);
@@ -820,9 +1208,10 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
appl_args->cpu_count = atoi(optarg);
break;
case 'p':
- if (0 > parse_pmr_policy(appl_args, argv, optarg))
- continue;
- policy = 1;
+ if (parse_pmr_policy(appl_args, optarg)) {
+ ret = -1;
+ break;
+ }
break;
case 't':
appl_args->time = atoi(optarg);
@@ -830,25 +1219,20 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
case 'i':
len = strlen(optarg);
if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
+ ret = -1;
+ break;
}
len += 1; /* add room for '\0' */
appl_args->if_name = malloc(len);
if (appl_args->if_name == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
+ ret = -1;
+ break;
}
strcpy(appl_args->if_name, optarg);
interface = 1;
break;
-
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
case 'm':
i = atoi(optarg);
if (i == 0)
@@ -856,22 +1240,56 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
else
appl_args->appl_mode = APPL_MODE_REPLY;
break;
-
+ case 'C':
+ if (parse_policy_ci_pass_count(appl_args, optarg)) {
+ ret = -1;
+ break;
+ }
+ break;
+ case 'P':
+ appl_args->promisc_mode = 1;
+ break;
+ case 'v':
+ appl_args->verbose = 1;
+ break;
+ case 'h':
+ ret = -1;
+ break;
+ case 'e':
+ appl_args->classifier_enable = atoi(optarg);
+ break;
+ case 'l':
+ appl_args->parse_layer = atoi(optarg);
+ break;
+ case 'd':
+ appl_args->cos_pools = atoi(optarg);
+ break;
+ case 's':
+ appl_args->pool_size = atoi(optarg);
+ break;
+ case 'b':
+ appl_args->burst_size = atoi(optarg);
+ break;
default:
break;
}
}
- if (!interface || !policy) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- if (appl_args->if_name == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
+ if (!interface)
+ ret = -1;
+
+ if (appl_args->if_name == NULL)
+ ret = -1;
+
+ if (ret) {
+ usage();
+ free(appl_args->if_name);
}
- optind = 1; /* reset 'extern optind' from the getopt lib */
+ /* reset optind from the getopt lib */
+ optind = 1;
+
+ return ret;
}
/**
@@ -879,70 +1297,83 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
*/
static void print_info(char *progname, appl_args_t *appl_args)
{
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %"PRIu64"\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_cpu_model_str(),
- odp_cpu_hz_max(), odp_sys_cache_line_size(),
- odp_cpu_count());
+ odp_sys_info_print();
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
- "Using IF:%s ",
+ "Using IF: %s\n",
progname, appl_args->if_name);
+ printf("Promisc mode: %s\n", appl_args->promisc_mode ? "enabled" : "disabled");
printf("\n\n");
fflush(NULL);
}
/**
- * Prinf usage information
+ * Print usage information
*/
-static void usage(char *progname)
+static void usage(void)
{
printf("\n"
- "OpenDataPlane Classifier example.\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s -i eth1 -m 0 -p \"ODP_PMR_SIP_ADDR:10.10.10.5:FFFFFFFF:queue1\" \\\n"
- "\t\t\t-p \"ODP_PMR_SIP_ADDR:10.10.10.7:000000FF:queue2\" \\\n"
- "\t\t\t-p \"ODP_PMR_SIP_ADDR:10.5.5.10:FFFFFF00:queue3\"\n"
- "\n"
- "For the above example configuration the following will be the packet distribution\n"
- "queue1\t\tPackets with source ip address 10.10.10.5\n"
- "queue2\t\tPackets with source ip address whose last 8 bits match 7\n"
- "queue3\t\tPackets with source ip address in the subnet 10.5.5.0\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -i, --interface Eth interface\n"
- " -p, --policy [<odp_cls_pmr_term_t>|<offset>]:<value>:<mask bits>:<queue name>\n"
- "\n"
- "<odp_cls_pmr_term_t> Packet Matching Rule defined with odp_cls_pmr_term_t "
- "for the policy\n"
- "<offset> Absolute offset in bytes from frame start to define a "
- "ODP_PMR_CUSTOM_FRAME Packet Matching Rule for the policy\n"
- "\n"
- "<value> PMR value to be matched.\n"
- "\n"
- "<mask bits> PMR mask bits to be applied on the PMR term value\n"
- "\n"
- "Optional OPTIONS\n"
- " -c, --count <number> CPU count.\n"
- " default: CPU core count.\n"
- "\n"
- " -m, --mode 0: Packet Drop mode. Received packets will be dropped\n"
- " !0: Packet ICMP mode. Received packets will be sent back\n"
- " default: Packet Drop mode\n"
- "\n"
- " -t, --timeout !0: Time for which the classifier will be run in seconds\n"
- " 0: Runs in infinite loop\n"
- " default: Runs in infinite loop\n"
- "\n"
- " -h, --help Display help and exit.\n"
- "\n", NO_PATH(progname), NO_PATH(progname)
- );
+ "ODP Classifier example.\n"
+ "Usage: odp_classifier OPTIONS\n"
+ " E.g. odp_classifier -i eth1 -m 0 -p \"ODP_PMR_SIP_ADDR:10.10.10.0:0xFFFFFF00:queue1\" \\\n"
+ " -p \"ODP_PMR_SIP_ADDR:10.10.10.10:0xFFFFFFFF:queue1:queue2\"\n"
+ "\n"
+ "The above example would classify:\n"
+ " 1) Packets from source IP address 10.10.10.0/24 to queue1, except ...\n"
+ " 2) Packets from source IP address 10.10.10.10 to queue2\n"
+ " 3) All other packets to DefaultCos\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface <interface name>\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -p, --policy <PMR term>:<offset>:<value>:<mask>:<src queue>:<dst queue>\n"
+ "\n"
+ " <PMR term> PMR term name defined in odp_cls_pmr_term_t\n"
+ " <offset> If term is ODP_PMR_CUSTOM_FRAME or _CUSTOM_L3, offset in bytes is used\n"
+ " <value> PMR value to be matched\n"
+ " <mask> PMR mask bits to be applied on the PMR value.\n"
+ " CUSTOM PMR terms accept plain hex string, other PMR terms require\n"
+ " hex string with '0x' prefix.\n"
+ " <src queue> Optional name of the source queue (CoS). The default CoS is used when\n"
+ " this is not defined.\n"
+ " <dst queue> Name of the destination queue (CoS).\n"
+ "\n"
+ " -c, --count <num> CPU count, 0=all available, default=1\n"
+ "\n"
+ " -m, --mode <mode> 0: Packet Drop mode. Received packets will be dropped\n"
+ " !0: Echo mode. Received packets will be sent back\n"
+ " default: Packet Drop mode\n"
+ "\n"
+ " -t, --time <sec> !0: Time for which the classifier will be run in seconds\n"
+ " 0: Runs in infinite loop\n"
+ " default: Runs in infinite loop\n"
+ "\n"
+ " -e, --enable <enable> 0: Classifier is disabled\n"
+ " 1: Classifier is enabled\n"
+ " default: Classifier is enabled\n"
+ "\n"
+ " -l, --layer <layer> Parse packets up to and including this layer. See odp_proto_layer_t\n"
+ " default: ODP_PROTO_LAYER_ALL\n"
+ "\n"
+ " -d, --dedicated <enable> 0: One pool for pktio and all CoSes\n"
+ " 1: Dedicated pools for pktio and each CoS\n"
+ " default: Dedicated pools\n"
+ "\n"
+ " -s, --size <num> Number of packets in each packet pool\n"
+ " default: %d\n"
+ "\n"
+ " -b, --burst <num> Packet burst size\n"
+ " default: %d\n"
+ "\n"
+ " -C, --ci_pass <dst queue:count>\n"
+ " Minimum acceptable packet count for a CoS destination queue.\n"
+ " If the received packet count is smaller than this value,\n"
+ " the application will exit with an error.\n"
+ " E.g: -C \"queue1:100\" -C \"queue2:200\" -C \"DefaultQueue:100\"\n"
+ " -P, --promisc_mode Enable promiscuous mode.\n"
+ " -v, --verbose Verbose output.\n"
+ " -h, --help Display help and exit.\n"
+ "\n", SHM_PKT_POOL_SIZE, DEF_PKT_BURST);
}
diff --git a/example/classifier/odp_classifier_run.sh b/example/classifier/odp_classifier_run.sh
new file mode 100755
index 000000000..ad0c3a76a
--- /dev/null
+++ b/example/classifier/odp_classifier_run.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_classifier${EXEEXT} -t $TIME_OUT_VAL -i $IF0 -m 0 -p \
+ "ODP_PMR_SIP_ADDR:10.10.10.0:0xFFFFFF00:queue1" -P -C "queue1:${CPASS_COUNT_ARG1}" \
+ -C "DefaultCos:${CPASS_COUNT_ARG2}"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/classifier/udp64.pcap b/example/classifier/udp64.pcap
new file mode 100644
index 000000000..fb05ef0f7
--- /dev/null
+++ b/example/classifier/udp64.pcap
Binary files differ
diff --git a/example/cli/.gitignore b/example/cli/.gitignore
new file mode 100644
index 000000000..2a19d7a64
--- /dev/null
+++ b/example/cli/.gitignore
@@ -0,0 +1,3 @@
+odp_cli
+*.log
+*.trs
diff --git a/example/cli/Makefile.am b/example/cli/Makefile.am
new file mode 100644
index 000000000..0e97a09ed
--- /dev/null
+++ b/example/cli/Makefile.am
@@ -0,0 +1,31 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_cli
+
+odp_cli_SOURCES = odp_cli.c
+
+if test_example
+TESTS = odp_cli_run.sh
+endif
+
+EXTRA_DIST = odp_cli_run.sh
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/cli/odp_cli.c b/example/cli/odp_cli.c
new file mode 100644
index 000000000..381fc8a59
--- /dev/null
+++ b/example/cli/odp_cli.c
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_cli.c
+ *
+ * This example shows how to start and stop ODP CLI using the CLI helper
+ * API functions. This example application can also be used to try out
+ * the CLI by connecting to a running application with a telnet client.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <signal.h>
+
+typedef struct {
+ int time;
+ char *addr;
+ uint16_t port;
+} options_t;
+
+static void usage(const char *prog)
+{
+ printf("\n"
+ "Usage: %s [options]\n"
+ "\n"
+ "OPTIONS:\n"
+ " -t, --time <sec> Keep CLI open for <sec> seconds. (default -1 (infinite))\n"
+ " -a, --address <addr> Bind listening socket to IP address <addr>.\n"
+ " -p, --port <port> Bind listening socket to port <port>.\n"
+ "\n"
+ "ODP helper defaults are used for address and port, if the options are\n"
+ "not given.\n"
+ "\n",
+ prog);
+}
+
+static void parse_args(int argc, char *argv[], options_t *opt)
+{
+ static const struct option longopts[] = {
+ { "time", required_argument, NULL, 't' },
+ { "address", required_argument, NULL, 'a' },
+ { "port", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+t:a:p:h";
+
+ while (1) {
+ int c = getopt_long(argc, argv, shortopts, longopts, NULL);
+
+ if (c == -1)
+ break; /* No more options */
+
+ switch (c) {
+ case 't':
+ opt->time = atoi(optarg);
+ break;
+ case 'a':
+ opt->addr = optarg;
+ break;
+ case 'p':
+ opt->port = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ }
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+static volatile int shutdown_sig;
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ shutdown_sig = 1;
+}
+
+static void my_cmd(int argc, char *argv[])
+{
+ odph_cli_log("%s(%d): %s\n", __FILE__, __LINE__, __func__);
+
+ for (int i = 0; i < argc; i++)
+ odph_cli_log("argv[%d]: %s\n", i, argv[i]);
+}
+
+static int cli_server(void *arg ODP_UNUSED)
+{
+ /* Run CLI server. */
+ if (odph_cli_run()) {
+ ODPH_ERR("odph_cli_run() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ signal(SIGINT, sig_handler);
+
+ odph_helper_options_t helper_options;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_t init;
+
+ odp_init_param_init(&init);
+ init.mem_model = helper_options.mem_model;
+
+ options_t opt = {
+ .time = -1,
+ .addr = NULL,
+ .port = 0,
+ };
+
+ parse_args(argc, argv, &opt);
+
+ /* Initialize ODP. */
+
+ odp_instance_t inst;
+
+ if (odp_init_global(&inst, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Prepare CLI parameters. */
+
+ odph_cli_param_t cli_param;
+
+ odph_cli_param_init(&cli_param);
+
+ if (opt.addr)
+ cli_param.address = opt.addr;
+
+ if (opt.port)
+ cli_param.port = opt.port;
+
+ /* Initialize CLI helper. */
+ if (odph_cli_init(&cli_param)) {
+ ODPH_ERR("CLI helper initialization failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Register user command. */
+ if (odph_cli_register_command("my_command", my_cmd,
+ "Example user command.")) {
+ ODPH_ERR("Registering user command failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create server thread. */
+
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odph_thread_t thr_server;
+
+ if (odp_cpumask_default_control(&cpumask, 1) != 1) {
+ ODPH_ERR("Failed to get default CPU mask.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = inst;
+ thr_common.cpumask = &cpumask;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.thr_type = ODP_THREAD_CONTROL;
+ thr_param.start = cli_server;
+
+ memset(&thr_server, 0, sizeof(thr_server));
+
+ if (odph_thread_create(&thr_server, &thr_common, &thr_param, 1) != 1) {
+ ODPH_ERR("Failed to create server thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("CLI server started on %s:%d\n", cli_param.address,
+ cli_param.port);
+
+ /* Wait for the given number of seconds. */
+ for (int i = 0; (opt.time < 0 || i < opt.time) && !shutdown_sig; i++)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
+
+ printf("Stopping CLI server.\n");
+
+ /* Stop CLI server. */
+ if (odph_cli_stop()) {
+ ODPH_ERR("CLI stop failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Wait for server thread to exit. */
+ if (odph_thread_join(&thr_server, 1) != 1) {
+ ODPH_ERR("Failed to join server thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Terminate CLI helper. */
+ if (odph_cli_term()) {
+ ODPH_ERR("CLI helper termination failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Terminate ODP. */
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("Global term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/example/cli/odp_cli_run.sh b/example/cli/odp_cli_run.sh
new file mode 100755
index 000000000..bb212fffb
--- /dev/null
+++ b/example/cli/odp_cli_run.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 Nokia
+#
+
+./odp_cli${EXEEXT} -t 2
diff --git a/example/debug/.gitignore b/example/debug/.gitignore
new file mode 100644
index 000000000..c5f758048
--- /dev/null
+++ b/example/debug/.gitignore
@@ -0,0 +1,3 @@
+odp_debug
+*.log
+*.trs
diff --git a/example/debug/Makefile.am b/example/debug/Makefile.am
new file mode 100644
index 000000000..d6f128af0
--- /dev/null
+++ b/example/debug/Makefile.am
@@ -0,0 +1,9 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_debug
+
+odp_debug_SOURCES = odp_debug.c
+
+if test_example
+TESTS = odp_debug
+endif
diff --git a/example/debug/odp_debug.c b/example/debug/odp_debug.c
new file mode 100644
index 000000000..79107ca8f
--- /dev/null
+++ b/example/debug/odp_debug.c
@@ -0,0 +1,724 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+/**
+ * @example odp_debug.c
+ *
+ * This example application demonstrates the usage of various debug print functions of ODP API.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_NAME_LEN 128
+
+typedef struct test_global_t {
+ int system;
+ int shm;
+ int pool;
+ int queue;
+ int pktio;
+ int ipsec;
+ int timer;
+ int stash;
+ char pktio_name[MAX_NAME_LEN];
+
+} test_global_t;
+
+static test_global_t test_global;
+
+static void print_usage(void)
+{
+ printf("This example prints out debug information on various ODP objects.\n"
+ "Select debug functions to be called with options. All listed functions\n"
+ "are called when no options are given.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -S, --system Call odp_sys_info_print() and odp_sys_config_print()\n"
+ " -s, --shm Create a SHM and call odp_shm_print()\n"
+ " -p, --pool Create various types of pools and call odp_pool_print()\n"
+ " -q, --queue Create various types of queues and call odp_queue_print()\n"
+ " -i, --interface <name> Start a packet IO interface, and call odp_pktio_print(),\n"
+ " odp_pktio_extra_stats_print(), etc. Uses loop interface by default.\n"
+ " -I, --ipsec Call odp_ipsec_print()\n"
+ " -t, --timer Call timer pool, timer and timeout print functions\n"
+ " -a, --stash Create stash and call odp_stash_print()\n"
+ " -h, --help Display help and exit.\n\n");
+}
+
+static int parse_options(int argc, char *argv[], test_global_t *global)
+{
+ int opt, long_index;
+ char *str;
+ uint32_t str_len = 0;
+
+ const struct option longopts[] = {
+ {"system", no_argument, NULL, 'S'},
+ {"shm", no_argument, NULL, 's'},
+ {"pool", no_argument, NULL, 'p'},
+ {"queue", no_argument, NULL, 'q'},
+ {"interface", required_argument, NULL, 'i'},
+ {"ipsec", no_argument, NULL, 'I'},
+ {"timer", no_argument, NULL, 't'},
+ {"stash", no_argument, NULL, 'a'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *shortopts = "+Sspqi:Itah";
+ int ret = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'S':
+ global->system = 1;
+ break;
+ case 's':
+ global->shm = 1;
+ break;
+ case 'p':
+ global->pool = 1;
+ break;
+ case 'q':
+ global->queue = 1;
+ break;
+ case 'i':
+ global->pktio = 1;
+ str = optarg;
+ str_len = strlen(str);
+
+ if (str_len && str_len < MAX_NAME_LEN)
+ strcpy(global->pktio_name, str);
+ break;
+ case 'I':
+ global->ipsec = 1;
+ break;
+ case 't':
+ global->timer = 1;
+ break;
+ case 'a':
+ global->stash = 1;
+ break;
+ case 'h':
+ default:
+ print_usage();
+ return -1;
+ }
+ }
+
+ if (global->pktio && (str_len == 0 || str_len >= MAX_NAME_LEN)) {
+ ODPH_ERR("Bad interface name length: %u\n", str_len);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int shm_debug(void)
+{
+ const char *name = "debug_shm";
+ odp_shm_t shm = ODP_SHM_INVALID;
+
+ shm = odp_shm_reserve(name, 8 * 1024, 64, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM reserve failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_shm_print_all();
+
+ printf("\n");
+ odp_shm_print(shm);
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("SHM free failed: %s\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int buffer_debug(odp_pool_t pool)
+{
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ if (buf == ODP_BUFFER_INVALID) {
+ ODPH_ERR("Buffer alloc failed\n");
+ return -1;
+ }
+
+ printf("\n");
+ odp_buffer_print(buf);
+
+ odp_buffer_free(buf);
+
+ return 0;
+}
+
+static int packet_debug(odp_pool_t pool, int len)
+{
+ odp_packet_t pkt = odp_packet_alloc(pool, len);
+
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("Packet alloc failed\n");
+ return -1;
+ }
+
+ printf("\n");
+ odp_packet_print(pkt);
+
+ printf("\n");
+ odp_packet_print_data(pkt, 0, len);
+
+ odp_packet_free(pkt);
+
+ return 0;
+}
+
+static int pool_debug(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ const char *name;
+ int pkt_len = 100;
+
+ name = "debug_buffer_pool";
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_BUFFER;
+ param.buf.num = 10;
+ param.buf.size = 1000;
+
+ pool = odp_pool_create(name, &param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_pool_print(pool);
+
+ if (buffer_debug(pool))
+ return -1;
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed: %s\n", name);
+ return -1;
+ }
+
+ name = "debug_packet_pool";
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = 10;
+ param.pkt.len = pkt_len;
+ param.pkt.max_len = 1000;
+
+ pool = odp_pool_create(name, &param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_pool_print(pool);
+
+ if (packet_debug(pool, pkt_len))
+ return -1;
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed: %s\n", name);
+ return -1;
+ }
+
+ name = "debug_tmo_pool";
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = 10;
+
+ pool = odp_pool_create(name, &param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_pool_print(pool);
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed: %s\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int queue_debug(void)
+{
+ odp_queue_param_t param;
+ const char *name;
+ int i;
+ int num = 3;
+ odp_queue_t queue[num];
+
+ name = "plain_queue";
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_PLAIN;
+
+ queue[0] = odp_queue_create(name, &param);
+
+ if (queue[0] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_queue_print(queue[0]);
+
+ name = "parallel_sched_queue";
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue[1] = odp_queue_create(name, &param);
+
+ if (queue[1] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_queue_print(queue[1]);
+
+ name = "atomic_sched_queue";
+ param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ param.sched.prio = odp_schedule_max_prio();
+
+ queue[2] = odp_queue_create(name, &param);
+
+ if (queue[2] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed: %s\n", name);
+ return -1;
+ }
+
+ printf("\n");
+ odp_queue_print(queue[2]);
+
+ printf("\n");
+ odp_queue_print_all();
+
+ printf("\n");
+ odp_schedule_print();
+
+ for (i = 0; i < num; i++) {
+ if (odp_queue_destroy(queue[i])) {
+ ODPH_ERR("Queue destroy failed: %i\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int pktio_debug(test_global_t *global)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_pktio_t pktio;
+ odp_pktio_link_info_t info;
+ uint8_t mac[ODPH_ETHADDR_LEN];
+ int pkt_len = 100;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = 10;
+ pool_param.pkt.len = pkt_len;
+
+ pool = odp_pool_create("debug_pktio_pool", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+
+ pktio = odp_pktio_open(global->pktio_name, pool, NULL);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Pktio open failed\n");
+ return -1;
+ }
+
+ /* Start interface with default config */
+ if (odp_pktin_queue_config(pktio, NULL)) {
+ ODPH_ERR("Packet input queue config failed\n");
+ return -1;
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Packet output queue config failed\n");
+ return -1;
+ }
+
+ if (odp_pktio_start(pktio)) {
+ ODPH_ERR("Pktio start failed\n");
+ return -1;
+ }
+
+ printf("\nWaiting link up");
+
+ /* Wait max 5 seconds for link up */
+ for (int i = 0; i < 25; i++) {
+ if (odp_pktio_link_status(pktio) == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+
+ odp_time_wait_ns(200 * ODP_TIME_MSEC_IN_NS);
+ printf(".");
+ fflush(NULL);
+ }
+
+ printf("\n\n");
+
+ printf("Packet IO\n---------\n");
+ printf(" index: %i\n", odp_pktio_index(pktio));
+ printf(" handle: 0x%" PRIx64 "\n", odp_pktio_to_u64(pktio));
+
+ if (odp_pktio_mac_addr(pktio, mac, ODPH_ETHADDR_LEN) == ODPH_ETHADDR_LEN) {
+ printf(" mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]);
+ }
+
+ printf(" input maxlen: %u\n", odp_pktin_maxlen(pktio));
+ printf(" output maxlen: %u\n", odp_pktout_maxlen(pktio));
+ printf(" promisc mode: %i\n", odp_pktio_promisc_mode(pktio));
+ printf(" timestamp res: %" PRIu64 " Hz\n\n", odp_pktio_ts_res(pktio));
+
+ if (odp_pktio_link_info(pktio, &info) == 0) {
+ printf("Link info\n---------\n");
+ printf(" auto neg: %i\n", info.autoneg);
+ printf(" duplex: %i\n", info.duplex);
+ printf(" media: %s\n", info.media);
+ printf(" pause_rx: %i\n", info.pause_rx);
+ printf(" pause_tx: %i\n", info.pause_tx);
+ printf(" speed: %u Mbit/s\n", info.speed);
+ printf(" status: %i\n", info.status);
+ }
+
+ printf("\n");
+ odp_pktio_print(pktio);
+
+ printf("\n");
+ odp_pktio_extra_stats_print(pktio);
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Pktio stop failed\n");
+ return -1;
+ }
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Pktio close failed\n");
+ return -1;
+ }
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipsec_debug(void)
+{
+ printf("\n");
+ odp_ipsec_print();
+
+ return 0;
+}
+
+static int timer_debug(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_timeout_t timeout;
+ odp_timer_res_capability_t timer_res_capa;
+ odp_timer_capability_t timer_capa;
+ odp_timer_pool_t timer_pool;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_start_t start_param;
+ odp_timer_t timer;
+ odp_queue_t queue;
+ odp_queue_param_t queue_param;
+ odp_event_t event;
+ uint64_t tick;
+ uint64_t max_tmo = ODP_TIME_SEC_IN_NS;
+ uint64_t res = 100 * ODP_TIME_MSEC_IN_NS;
+ int started = 0;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = 10;
+
+ pool = odp_pool_create("debug_timer", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+
+ timeout = odp_timeout_alloc(pool);
+ if (timeout == ODP_TIMEOUT_INVALID) {
+ ODPH_ERR("Timeout alloc failed\n");
+ return -1;
+ }
+
+ if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
+ ODPH_ERR("Timer capa failed\n");
+ return -1;
+ }
+
+ if (timer_capa.max_tmo.max_tmo < max_tmo)
+ max_tmo = timer_capa.max_tmo.max_tmo;
+
+ memset(&timer_res_capa, 0, sizeof(odp_timer_res_capability_t));
+ timer_res_capa.max_tmo = max_tmo;
+ if (odp_timer_res_capability(ODP_CLOCK_DEFAULT, &timer_res_capa)) {
+ ODPH_ERR("Timer resolution capability failed\n");
+ return -1;
+ }
+
+ if (timer_res_capa.res_ns > res)
+ res = timer_res_capa.res_ns;
+
+ odp_timer_pool_param_init(&timer_param);
+ timer_param.res_ns = res;
+ timer_param.min_tmo = max_tmo / 10;
+ timer_param.max_tmo = max_tmo;
+ timer_param.num_timers = 10;
+ timer_param.clk_src = ODP_CLOCK_DEFAULT;
+
+ timer_pool = odp_timer_pool_create("debug_timer", &timer_param);
+
+ if (timer_pool == ODP_TIMER_POOL_INVALID) {
+ ODPH_ERR("Timer pool create failed\n");
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ return -1;
+ }
+
+ odp_queue_param_init(&queue_param);
+ if (timer_capa.queue_type_sched)
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue = odp_queue_create("debug_timer", &queue_param);
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed.\n");
+ return -1;
+ }
+
+ printf("\n");
+ odp_timer_pool_print(timer_pool);
+
+ tick = odp_timer_ns_to_tick(timer_pool, max_tmo / 2);
+
+ timer = odp_timer_alloc(timer_pool, queue, (void *)(uintptr_t)0xdeadbeef);
+
+ printf("\n");
+ odp_timeout_print(timeout);
+
+ event = odp_timeout_to_event(timeout);
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tick;
+ start_param.tmo_ev = event;
+
+ if (odp_timer_start(timer, &start_param) == ODP_TIMER_SUCCESS)
+ started = 1;
+ else
+ ODPH_ERR("Timer start failed.\n");
+
+ printf("\n");
+ odp_timer_print(timer);
+
+ if (started && odp_timer_cancel(timer, &event) != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer cancel failed\n");
+ return -1;
+ } else {
+ timeout = odp_timeout_from_event(event);
+
+ printf("\n");
+ odp_timeout_print(timeout);
+
+ odp_timeout_free(timeout);
+ }
+
+ if (odp_timer_free(timer)) {
+ ODPH_ERR("Timer free failed\n");
+ return -1;
+ }
+
+ odp_timer_pool_destroy(timer_pool);
+
+ if (odp_queue_destroy(queue)) {
+ ODPH_ERR("Queue destroy failed\n");
+ return -1;
+ }
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int stash_debug(void)
+{
+ odp_stash_param_t param;
+ odp_stash_t stash;
+ uint32_t val = 0xdeadbeef;
+
+ odp_stash_param_init(&param);
+ param.num_obj = 10;
+ param.obj_size = 4;
+
+ stash = odp_stash_create("debug_stash", &param);
+
+ if (stash == ODP_STASH_INVALID) {
+ ODPH_ERR("Stash create failed\n");
+ return -1;
+ }
+
+ if (odp_stash_put_u32(stash, &val, 1) != 1) {
+ ODPH_ERR("Stash put failed\n");
+ return -1;
+ }
+
+ printf("\n");
+ odp_stash_print(stash);
+
+ if (odp_stash_get_u32(stash, &val, 1) != 1) {
+ ODPH_ERR("Stash get failed\n");
+ return -1;
+ }
+
+ if (odp_stash_destroy(stash)) {
+ ODPH_ERR("Stash destroy failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t inst;
+ test_global_t *global = &test_global;
+
+ printf("ODP debug example\n\n");
+ memset(global, 0, sizeof(test_global_t));
+
+ if (argc < 2) {
+ /* If not arguments, run all test cases */
+ global->system = 1;
+ global->shm = 1;
+ global->pool = 1;
+ global->queue = 1;
+ global->pktio = 1;
+ global->ipsec = 1;
+ global->timer = 1;
+ global->stash = 1;
+ strcpy(global->pktio_name, "loop");
+ } else {
+ if (parse_options(argc, argv, global))
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure scheduler before creating any scheduled queues */
+ if (odp_schedule_config(NULL)) {
+ ODPH_ERR("Schedule config failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->system) {
+ printf("\n");
+ odp_sys_info_print();
+
+ printf("\n");
+ odp_sys_config_print();
+ }
+
+ if (global->shm && shm_debug()) {
+ ODPH_ERR("SHM debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->pool && pool_debug()) {
+ ODPH_ERR("Pool debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->queue && queue_debug()) {
+ ODPH_ERR("Queue debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->pktio && pktio_debug(global)) {
+ ODPH_ERR("Packet debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->ipsec && ipsec_debug()) {
+ ODPH_ERR("IPSEC debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->timer && timer_debug()) {
+ ODPH_ERR("Timer debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (global->stash && stash_debug()) {
+ ODPH_ERR("Stash debug failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("Global term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/example/example_debug.h b/example/example_debug.h
deleted file mode 100644
index dd3aa7f3d..000000000
--- a/example/example_debug.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-/**
- * @file
- *
- * example debug
- */
-
-#ifndef EXAMPLE_DEBUG_H_
-#define EXAMPLE_DEBUG_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef EXAMPLE_DEBUG_PRINT
-#define EXAMPLE_DEBUG_PRINT 1
-#endif
-
-/**
- * log level.
- */
-typedef enum example_log_level {
- EXAMPLE_LOG_DBG,
- EXAMPLE_LOG_ERR,
- EXAMPLE_LOG_ABORT
-} example_log_level_e;
-
-/**
- * default LOG macro.
- */
-#define EXAMPLE_LOG(level, fmt, ...) \
-do { \
- switch (level) { \
- case EXAMPLE_LOG_ERR: \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case EXAMPLE_LOG_DBG: \
- if (EXAMPLE_DEBUG_PRINT == 1) \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case EXAMPLE_LOG_ABORT: \
- fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- abort(); \
- break; \
- default: \
- fprintf(stderr, "Unknown LOG level"); \
- break;\
- } \
-} while (0)
-
-/**
- * Debug printing macro, which prints output when DEBUG flag is set.
- */
-#define EXAMPLE_DBG(fmt, ...) \
- EXAMPLE_LOG(EXAMPLE_LOG_DBG, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function).
- */
-#define EXAMPLE_ERR(fmt, ...) \
- EXAMPLE_LOG(EXAMPLE_LOG_ERR, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function),
- * then abort.
- */
-#define EXAMPLE_ABORT(fmt, ...) \
- EXAMPLE_LOG(EXAMPLE_LOG_ABORT, fmt, ##__VA_ARGS__)
-
-/**
- * Intentionally unused variables to functions
- */
-#define EXAMPLE_UNUSED __attribute__((__unused__))
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/generator/.gitignore b/example/generator/.gitignore
deleted file mode 100644
index 85aa1d1ec..000000000
--- a/example/generator/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-odp_generator
diff --git a/example/generator/Makefile.am b/example/generator/Makefile.am
deleted file mode 100644
index 49a74978a..000000000
--- a/example/generator/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include $(top_srcdir)/example/Makefile.inc
-
-bin_PROGRAMS = odp_generator$(EXEEXT)
-odp_generator_LDFLAGS = $(AM_LDFLAGS) -static
-odp_generator_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
-
-dist_odp_generator_SOURCES = odp_generator.c
diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
deleted file mode 100644
index f3ec43be5..000000000
--- a/example/generator/odp_generator.c
+++ /dev/null
@@ -1,1457 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/** enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <time.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <sys/time.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp/helper/odph_api.h>
-
-#define MAX_WORKERS 32 /* Max number of workers */
-#define POOL_NUM_PKT 2048 /* Number of packets in packet pool */
-#define POOL_PKT_LEN 1856 /* Max packet length */
-#define DEFAULT_PKT_INTERVAL 1000 /* Interval between each packet */
-#define MAX_UDP_TX_BURST 32
-#define MAX_RX_BURST 32
-
-#define APPL_MODE_UDP 0 /**< UDP mode */
-#define APPL_MODE_PING 1 /**< ping mode */
-#define APPL_MODE_RCV 2 /**< receive mode */
-
-/** print appl mode */
-#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-
-/**
- * Interfaces
- */
-
-typedef struct {
- odp_pktio_t pktio;
- odp_pktout_queue_t pktout[MAX_WORKERS];
- unsigned pktout_count;
-} interface_t;
-
-/**
- * Parsed command line application arguments
- */
-typedef struct {
- int num_workers; /**< Number of worker thread */
- const char *mask; /**< CPU mask */
- int if_count; /**< Number of interfaces to be used */
- char **if_names; /**< Array of pointers to interface names */
- char *if_str; /**< Storage for interface names */
- odp_pool_t pool; /**< Pool for packet IO */
- odph_ethaddr_t srcmac; /**< src mac addr */
- odph_ethaddr_t dstmac; /**< dest mac addr */
- unsigned int srcip; /**< src ip addr */
- unsigned int dstip; /**< dest ip addr */
- uint16_t srcport; /**< src udp port */
- uint16_t dstport; /**< dest udp port */
- int mode; /**< work mode */
- int number; /**< packets number to be sent */
- int payload; /**< data len */
- int timeout; /**< wait time */
- int interval; /**< wait interval ms between sending
- each packet */
- int udp_tx_burst; /**< number of udp packets to send with one
- API call */
-} appl_args_t;
-
-/**
- * counters
-*/
-static struct {
- odp_atomic_u64_t seq; /**< ip seq to be send */
- odp_atomic_u64_t ip; /**< ip packets */
- odp_atomic_u64_t udp; /**< udp packets */
- odp_atomic_u64_t icmp; /**< icmp packets */
- odp_atomic_u64_t cnt; /**< sent packets*/
- odp_atomic_u64_t tx_drops; /**< packets dropped in transmit */
-} counters;
-
-/** * Thread specific arguments
- */
-typedef struct {
- odp_pktout_queue_t pktout; /**< Packet output queue to use*/
- odp_pool_t pool; /**< Pool for packet IO */
- odp_timer_pool_t tp; /**< Timer pool handle */
- odp_queue_t tq; /**< Queue for timeouts */
- odp_timer_t tim; /**< Timer handle */
- odp_timeout_t tmo_ev; /**< Timeout event */
- int mode; /**< Thread mode */
-} thread_args_t;
-
-/**
- * Grouping of both parsed CL args and thread specific args - alloc together
- */
-typedef struct {
- /** Application (parsed) arguments */
- appl_args_t appl;
- /** Thread specific arguments */
- thread_args_t thread[MAX_WORKERS];
-} args_t;
-
-/** Global pointer to args */
-static args_t *args;
-
-/** Barrier to sync threads execution */
-static odp_barrier_t barrier;
-
-/* helper funcs */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
-static void print_info(char *progname, appl_args_t *appl_args);
-static void usage(char *progname);
-static int scan_ip(char *buf, unsigned int *paddr);
-static void print_global_stats(int num_workers);
-
-/**
- * Sleep for the specified amount of milliseconds
- * Use ODP timer, busy wait until timer expired and timeout event received
- */
-static void millisleep(uint32_t ms,
- odp_timer_pool_t tp,
- odp_timer_t tim,
- odp_queue_t q,
- odp_timeout_t tmo)
-{
- uint64_t ticks = odp_timer_ns_to_tick(tp, 1000000ULL * ms);
- odp_event_t ev = odp_timeout_to_event(tmo);
- int rc = odp_timer_set_rel(tim, ticks, &ev);
-
- if (rc != ODP_TIMER_SUCCESS)
- EXAMPLE_ABORT("odp_timer_set_rel() failed\n");
- /* Spin waiting for timeout event */
- while ((ev = odp_queue_deq(q)) == ODP_EVENT_INVALID)
- (void)0;
-}
-
-/**
- * Scan ip
- * Parse ip address.
- *
- * @param buf ip address string xxx.xxx.xxx.xx
- * @param paddr ip address for odp_packet
- * @return 1 success, 0 failed
-*/
-static int scan_ip(char *buf, unsigned int *paddr)
-{
- int part1, part2, part3, part4;
- char tail = 0;
- int field;
-
- if (buf == NULL)
- return 0;
-
- field = sscanf(buf, "%d . %d . %d . %d %c",
- &part1, &part2, &part3, &part4, &tail);
-
- if (field < 4 || field > 5) {
- printf("expect 4 field,get %d/n", field);
- return 0;
- }
-
- if (tail != 0) {
- printf("ip address mixed with non number/n");
- return 0;
- }
-
- if ((part1 >= 0 && part1 <= 255) && (part2 >= 0 && part2 <= 255) &&
- (part3 >= 0 && part3 <= 255) && (part4 >= 0 && part4 <= 255)) {
- if (paddr)
- *paddr = part1 << 24 | part2 << 16 | part3 << 8 | part4;
- return 1;
- } else {
- printf("not good ip %d:%d:%d:%d/n", part1, part2, part3, part4);
- }
-
- return 0;
-}
-
-/**
- * Setup array of reference packets
- *
- * @param pool Packet pool
- * @param pkt_ref_array Packet array
- * @param pkt_ref_array_size Packet array size
- * @param setup_ref Packet setup function
- * @return 0 success, -1 failed
-*/
-static int setup_pkt_ref_array(odp_pool_t pool,
- odp_packet_t *pkt_ref_array,
- int pkt_ref_array_size,
- odp_packet_t (*setup_ref)(odp_pool_t))
-{
- int i;
-
- for (i = 0; i < pkt_ref_array_size; i++) {
- pkt_ref_array[i] = (*setup_ref)(pool);
- if (pkt_ref_array[i] == ODP_PACKET_INVALID)
- break;
- }
-
- if (i < pkt_ref_array_size) {
- odp_packet_free_multi(pkt_ref_array, i);
- return -1;
- }
- return 0;
-}
-
-/**
- * Setup array of packets
- *
- * @param pkt_ref_array Reference packet array
- * @param pkt_array Packet array
- * @param pkt_array_size Packet array size
- * @param setup_pkt Packet setup function
- * @return 0 success, -1 failed
-*/
-static int setup_pkt_array(odp_packet_t *pkt_ref_array,
- odp_packet_t *pkt_array,
- int pkt_array_size,
- int (*setup_pkt)(odp_packet_t))
-{
- int i;
-
- for (i = 0; i < pkt_array_size; i++) {
- if ((*setup_pkt)(pkt_ref_array[i]))
- break;
-
- pkt_array[i] = odp_packet_ref_static(pkt_ref_array[i]);
- if (pkt_array[i] == ODP_PACKET_INVALID)
- break;
- }
- if (i < pkt_array_size) {
- if (i)
- odp_packet_free_multi(pkt_array, i - 1);
-
- return -1;
- }
- return 0;
-}
-
-/**
- * set up an udp packet reference
- *
- * @param pool Buffer pool to create packet in
- *
- *
- * @retval Handle of created packet
- * @retval ODP_PACKET_INVALID Packet could not be created
- *
- */
-static odp_packet_t setup_udp_pkt_ref(odp_pool_t pool)
-{
- odp_packet_t pkt;
- char *buf;
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ip;
- odph_udphdr_t *udp;
-
- pkt = odp_packet_alloc(pool, args->appl.payload + ODPH_UDPHDR_LEN +
- ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN);
-
- if (pkt == ODP_PACKET_INVALID)
- return pkt;
-
- buf = odp_packet_data(pkt);
-
- /* ether */
- odp_packet_l2_offset_set(pkt, 0);
- eth = (odph_ethhdr_t *)buf;
- memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODPH_ETHADDR_LEN);
- memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODPH_ETHADDR_LEN);
- eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
-
- /* ip */
- odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
- odp_packet_has_ipv4_set(pkt, 1);
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip);
- ip->src_addr = odp_cpu_to_be_32(args->appl.srcip);
- ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODPH_UDPHDR_LEN +
- ODPH_IPV4HDR_LEN);
- ip->proto = ODPH_IPPROTO_UDP;
- ip->id = 0;
- ip->ttl = 64;
- ip->chksum = 0;
-
- /* udp */
- odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- odp_packet_has_udp_set(pkt, 1);
- udp = (odph_udphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- udp->src_port = odp_cpu_to_be_16(args->appl.srcport);
- udp->dst_port = odp_cpu_to_be_16(args->appl.dstport);
- udp->length = odp_cpu_to_be_16(args->appl.payload + ODPH_UDPHDR_LEN);
- udp->chksum = 0;
- udp->chksum = odph_ipv4_udp_chksum(pkt);
-
- return pkt;
-}
-
-/**
- * set up an udp packet
- *
- * @param pkt Reference UDP packet
- *
- * @return Success/Failed
- * @retval 0 on success, -1 on fail
- */
-static int setup_udp_pkt(odp_packet_t pkt)
-{
- char *buf;
- odph_ipv4hdr_t *ip;
- unsigned short seq;
-
- buf = (char *)odp_packet_data(pkt);
-
- /*Update IP ID and checksum*/
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xFFFF;
- ip->id = odp_cpu_to_be_16(seq);
- ip->chksum = 0;
- ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
-
- return 0;
-}
-
-/**
- * Set up an icmp packet reference
- *
- * @param pool Buffer pool to create packet in
- *
- * @return Handle of created packet
- * @retval ODP_PACKET_INVALID Packet could not be created
- */
-static odp_packet_t setup_icmp_pkt_ref(odp_pool_t pool)
-{
- odp_packet_t pkt;
- char *buf;
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ip;
- odph_icmphdr_t *icmp;
-
- args->appl.payload = 56;
- pkt = odp_packet_alloc(pool, args->appl.payload + ODPH_ICMPHDR_LEN +
- ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN);
-
- if (pkt == ODP_PACKET_INVALID)
- return pkt;
-
- buf = odp_packet_data(pkt);
-
- /* ether */
- odp_packet_l2_offset_set(pkt, 0);
- eth = (odph_ethhdr_t *)buf;
- memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODPH_ETHADDR_LEN);
- memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODPH_ETHADDR_LEN);
- eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
- /* ip */
- odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip);
- ip->src_addr = odp_cpu_to_be_32(args->appl.srcip);
- ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->ttl = 64;
- ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODPH_ICMPHDR_LEN +
- ODPH_IPV4HDR_LEN);
- ip->proto = ODPH_IPPROTO_ICMPv4;
- ip->id = 0;
- ip->chksum = 0;
-
- /* icmp */
- icmp = (odph_icmphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- icmp->type = ICMP_ECHO;
- icmp->code = 0;
- icmp->un.echo.id = 0;
- icmp->un.echo.sequence = 0;
- icmp->chksum = 0;
-
- return pkt;
-}
-
-/**
- * Set up an icmp packet
- *
- * @param pkt Reference ICMP packet
- *
- * @return Success/Failed
- * @retval 0 on success, -1 on fail
- */
-static int setup_icmp_pkt(odp_packet_t pkt)
-{
- char *buf;
- odph_ipv4hdr_t *ip;
- odph_icmphdr_t *icmp;
- uint64_t tval;
- uint8_t *tval_d;
- unsigned short seq;
-
- buf = (char *)odp_packet_data(pkt);
-
- /* ip */
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xffff;
- ip->id = odp_cpu_to_be_16(seq);
- ip->chksum = 0;
- ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
-
- /* icmp */
- icmp = (odph_icmphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- icmp->un.echo.sequence = ip->id;
-
- tval_d = (uint8_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN +
- ODPH_ICMPHDR_LEN);
- tval = odp_time_to_ns(odp_time_local());
- memcpy(tval_d, &tval, sizeof(uint64_t));
-
- icmp->chksum = 0;
- icmp->chksum = odph_chksum(icmp, args->appl.payload + ODPH_ICMPHDR_LEN);
-
- return 0;
-}
-
-/**
- * Create a pktio object
- *
- * @param dev Name of device to open
- * @param pool Pool to associate with device for packet RX/TX
- *
- * @return The handle of the created pktio object.
- * @warning This routine aborts if the create is unsuccessful.
- */
-static int create_pktio(const char *dev, odp_pool_t pool,
- unsigned num_rx_queues,
- unsigned num_tx_queues,
- interface_t *itf)
-{
- odp_pktio_capability_t capa;
- int ret;
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t pktin_param;
- odp_pktout_queue_param_t pktout_param;
- odp_pktio_op_mode_t pktout_mode;
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- /* Open a packet IO instance */
- itf->pktio = odp_pktio_open(dev, pool, &pktio_param);
-
- if (itf->pktio == ODP_PKTIO_INVALID) {
- EXAMPLE_ERR("Error: pktio create failed for %s\n", dev);
- return -1;
- }
-
- if (odp_pktio_capability(itf->pktio, &capa)) {
- EXAMPLE_ERR("Error: Failed to get interface capabilities %s\n",
- dev);
- return -1;
- }
- if (num_rx_queues > capa.max_input_queues)
- num_rx_queues = capa.max_input_queues;
-
- odp_pktin_queue_param_init(&pktin_param);
- pktin_param.num_queues = num_rx_queues;
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
-
- if (odp_pktin_queue_config(itf->pktio, &pktin_param)) {
- EXAMPLE_ERR("Error: pktin queue config failed for %s\n", dev);
- return -1;
- }
-
- pktout_mode = ODP_PKTIO_OP_MT_UNSAFE;
- if (num_tx_queues > capa.max_output_queues) {
- num_tx_queues = capa.max_output_queues;
- pktout_mode = ODP_PKTIO_OP_MT;
- }
-
- odp_pktout_queue_param_init(&pktout_param);
- pktout_param.num_queues = num_tx_queues;
- pktout_param.op_mode = pktout_mode;
-
- if (odp_pktout_queue_config(itf->pktio, &pktout_param)) {
- EXAMPLE_ERR("Error: pktout queue config failed for %s\n", dev);
- return -1;
- }
-
- ret = odp_pktio_start(itf->pktio);
- if (ret)
- EXAMPLE_ABORT("Error: unable to start %s\n", dev);
-
- itf->pktout_count = num_tx_queues;
- if (odp_pktout_queue(itf->pktio, itf->pktout, itf->pktout_count) !=
- (int)itf->pktout_count) {
- EXAMPLE_ERR("Error: failed to get output queues for %s\n", dev);
- return -1;
- }
-
- printf(" created pktio:%02" PRIu64
- ", dev:%s, queue mode (ATOMIC queues)\n"
- " default pktio%02" PRIu64 "\n",
- odp_pktio_to_u64(itf->pktio), dev,
- odp_pktio_to_u64(itf->pktio));
- fflush(NULL);
-
- return 0;
-}
-
-/**
- * Packet IO loopback worker thread using ODP queues
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-
-static int gen_send_thread(void *arg)
-{
- int thr;
- int ret = 0;
- thread_args_t *thr_args;
- odp_pktout_queue_t pktout;
- odp_packet_t pkt_ref_array[MAX_UDP_TX_BURST];
- odp_packet_t pkt_array[MAX_UDP_TX_BURST];
- int pkt_array_size;
- int burst_start, burst_size;
- odp_packet_t (*setup_pkt_ref)(odp_pool_t) = NULL;
- int (*setup_pkt)(odp_packet_t) = NULL;
-
- thr = odp_thread_id();
- thr_args = arg;
-
- pktout = thr_args->pktout;
-
- /* Create reference packets*/
- if (args->appl.mode == APPL_MODE_UDP) {
- pkt_array_size = args->appl.udp_tx_burst;
- setup_pkt_ref = setup_udp_pkt_ref;
- setup_pkt = setup_udp_pkt;
- } else if (args->appl.mode == APPL_MODE_PING) {
- pkt_array_size = 1;
- setup_pkt_ref = setup_icmp_pkt_ref;
- setup_pkt = setup_icmp_pkt;
- } else {
- EXAMPLE_ERR(" [%02i] Error: invalid processing mode %d\n",
- thr, args->appl.mode);
- return -1;
- }
-
- if (setup_pkt_ref_array(thr_args->pool, pkt_ref_array,
- pkt_array_size, setup_pkt_ref)) {
- EXAMPLE_ERR("[%02i] Error: failed to create"
- " reference packets\n", thr);
- return -1;
- }
-
- printf(" [%02i] created mode: SEND\n", thr);
-
- odp_barrier_wait(&barrier);
-
- for (;;) {
- if (args->appl.number != -1 &&
- odp_atomic_fetch_add_u64(&counters.cnt, pkt_array_size) >=
- (unsigned int)args->appl.number)
- break;
-
- /* Setup TX burst*/
- if (setup_pkt_array(pkt_ref_array, pkt_array,
- pkt_array_size, setup_pkt)) {
- EXAMPLE_ERR("[%02i] Error: failed to setup packets\n",
- thr);
- break;
- }
-
- /* Send TX burst*/
- for (burst_start = 0, burst_size = pkt_array_size;;) {
- ret = odp_pktout_send(pktout, &pkt_array[burst_start],
- burst_size);
- if (ret == burst_size) {
- break;
- } else if (ret >= 0 && ret < burst_size) {
- odp_atomic_add_u64(&counters.tx_drops,
- burst_size - ret);
- burst_start += ret;
- burst_size -= ret;
- odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
- continue;
- }
- EXAMPLE_ERR(" [%02i] packet send failed\n", thr);
- odp_packet_free_multi(&pkt_array[burst_start],
- burst_size);
- break;
- }
-
- if (args->appl.interval != 0) {
- printf(" [%02i] send pkt no:%ju seq %ju\n",
- thr,
- odp_atomic_load_u64(&counters.seq),
- odp_atomic_load_u64(&counters.seq)%0xffff);
- millisleep(args->appl.interval,
- thr_args->tp,
- thr_args->tim,
- thr_args->tq,
- thr_args->tmo_ev);
- }
- }
-
- /* receive number of reply pks until timeout */
- if (args->appl.mode == APPL_MODE_PING && args->appl.number > 0) {
- while (args->appl.timeout >= 0) {
- if (odp_atomic_load_u64(&counters.icmp) >=
- (unsigned int)args->appl.number)
- break;
- millisleep(DEFAULT_PKT_INTERVAL,
- thr_args->tp,
- thr_args->tim,
- thr_args->tq,
- thr_args->tmo_ev);
- args->appl.timeout--;
- }
- }
-
- odp_packet_free_multi(pkt_ref_array, pkt_array_size);
-
- return 0;
-}
-
-/**
- * Process icmp packets
- *
- * @param icmp icmp header address
- * @param msg output buffer
- */
-
-static void process_icmp_pkt(odph_icmphdr_t *icmp, char *msg)
-{
- uint64_t trecv;
- uint64_t tsend;
- uint64_t rtt_ms, rtt_us;
-
- msg[0] = 0;
-
- if (icmp->type == ICMP_ECHOREPLY) {
- odp_atomic_inc_u64(&counters.icmp);
-
- memcpy(&tsend, (uint8_t *)icmp + ODPH_ICMPHDR_LEN,
- sizeof(uint64_t));
- trecv = odp_time_to_ns(odp_time_local());
- rtt_ms = (trecv - tsend) / ODP_TIME_MSEC_IN_NS;
- rtt_us = (trecv - tsend) / ODP_TIME_USEC_IN_NS -
- 1000 * rtt_ms;
- sprintf(msg,
- "ICMP Echo Reply seq %d time %"
- PRIu64 ".%.03" PRIu64" ms",
- odp_be_to_cpu_16(icmp->un.echo.sequence),
- rtt_ms, rtt_us);
- } else if (icmp->type == ICMP_ECHO) {
- sprintf(msg, "Icmp Echo Request");
- }
-}
-
-/**
- * Print odp packets
- *
- * @param thr worker id
- * @param pkt_tbl packets to be print
- * @param len packet number
- */
-static void print_pkts(int thr, odp_packet_t pkt_tbl[], unsigned len)
-{
- odp_packet_t pkt;
- char *buf;
- odph_ipv4hdr_t *ip;
- odph_icmphdr_t *icmp;
- unsigned i;
- size_t offset;
- char msg[1024];
-
- for (i = 0; i < len; ++i) {
- pkt = pkt_tbl[i];
-
- /* only ip pkts */
- if (!odp_packet_has_ipv4(pkt))
- continue;
-
- odp_atomic_inc_u64(&counters.ip);
- buf = odp_packet_data(pkt);
- ip = (odph_ipv4hdr_t *)(buf + odp_packet_l3_offset(pkt));
- offset = odp_packet_l4_offset(pkt);
-
- /* udp */
- if (ip->proto == ODPH_IPPROTO_UDP) {
- odp_atomic_inc_u64(&counters.udp);
- }
-
- /* icmp */
- if (ip->proto == ODPH_IPPROTO_ICMPv4) {
- icmp = (odph_icmphdr_t *)(buf + offset);
-
- process_icmp_pkt(icmp, msg);
- printf(" [%02i] %s\n", thr, msg);
- }
- }
-}
-
-/**
- * Main receive function
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-static int gen_recv_thread(void *arg)
-{
- int thr;
- odp_packet_t pkts[MAX_RX_BURST], pkt;
- odp_event_t events[MAX_RX_BURST];
- int pkt_cnt, ev_cnt, i;
-
- thr = odp_thread_id();
- (void)arg;
-
- printf(" [%02i] created mode: RECEIVE\n", thr);
- odp_barrier_wait(&barrier);
-
- for (;;) {
- if (args->appl.number != -1 &&
- odp_atomic_load_u64(&counters.icmp) >=
- (unsigned int)args->appl.number) {
- break;
- }
-
- /* Use schedule to get buf from any input queue */
- ev_cnt = odp_schedule_multi(NULL, ODP_SCHED_WAIT,
- events, MAX_RX_BURST);
- if (ev_cnt == 0)
- continue;
- for (i = 0, pkt_cnt = 0; i < ev_cnt; i++) {
- pkt = odp_packet_from_event(events[i]);
-
- /* Drop packets with errors */
- if (odp_unlikely(odp_packet_has_error(pkt))) {
- odp_packet_free(pkt);
- continue;
- }
- pkts[pkt_cnt++] = pkt;
- }
-
- print_pkts(thr, pkts, pkt_cnt);
-
- odp_packet_free_multi(pkts, pkt_cnt);
- }
-
- return 0;
-}
-
-/**
- * printing verbose statistics
- *
- */
-static void print_global_stats(int num_workers)
-{
- odp_time_t cur, wait, next;
- uint64_t pkts_snd = 0, pkts_snd_prev = 0;
- uint64_t pps_snd = 0, maximum_pps_snd = 0;
- uint64_t pkts_rcv = 0, pkts_rcv_prev = 0;
- uint64_t pps_rcv = 0, maximum_pps_rcv = 0;
- int verbose_interval = 20;
- odp_thrmask_t thrd_mask;
-
- odp_barrier_wait(&barrier);
-
- wait = odp_time_local_from_ns(verbose_interval * ODP_TIME_SEC_IN_NS);
- next = odp_time_sum(odp_time_local(), wait);
-
- while (odp_thrmask_worker(&thrd_mask) == num_workers) {
- if (args->appl.number != -1 &&
- odp_atomic_load_u64(&counters.cnt) >=
- (unsigned int)args->appl.number) {
- break;
- }
-
- cur = odp_time_local();
- if (odp_time_cmp(next, cur) > 0)
- continue;
-
- next = odp_time_sum(cur, wait);
- switch (args->appl.mode) {
- case APPL_MODE_RCV:
- pkts_rcv = odp_atomic_load_u64(&counters.ip);
- break;
- case APPL_MODE_PING:
- pkts_snd = odp_atomic_load_u64(&counters.seq);
- pkts_rcv = odp_atomic_load_u64(&counters.icmp);
- break;
- case APPL_MODE_UDP:
- pkts_snd = odp_atomic_load_u64(&counters.seq);
- break;
- default:
- continue;
- }
-
- pps_snd = (pkts_snd - pkts_snd_prev) / verbose_interval;
- pkts_snd_prev = pkts_snd;
- if (pps_snd > maximum_pps_snd)
- maximum_pps_snd = pps_snd;
-
- pps_rcv = (pkts_rcv - pkts_rcv_prev) / verbose_interval;
- pkts_rcv_prev = pkts_rcv;
- if (pps_rcv > maximum_pps_rcv)
- maximum_pps_rcv = pps_rcv;
-
- printf("sent: %" PRIu64 ", drops: %" PRIu64 ", "
- "send rate: %" PRIu64 " pps, "
- "max send rate: %" PRIu64 " pps, "
- "rcv: %" PRIu64 ", "
- "recv rate: %" PRIu64 " pps, "
- "max recv rate: %" PRIu64 " pps\n",
- pkts_snd, odp_atomic_load_u64(&counters.tx_drops),
- pps_snd, maximum_pps_snd,
- pkts_rcv, pps_rcv, maximum_pps_rcv);
- fflush(NULL);
- }
-}
-
-/**
- * ODP packet example main function
- */
-int main(int argc, char *argv[])
-{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- odp_pool_t pool;
- int num_workers;
- unsigned num_rx_queues, num_tx_queues;
- int i;
- odp_shm_t shm;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_pool_param_t params;
- odp_timer_pool_param_t tparams;
- odp_timer_pool_t tp;
- odp_pool_t tmop;
- odp_queue_t tq;
- odp_event_t ev;
- interface_t *ifs;
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* init counters */
- odp_atomic_init_u64(&counters.seq, 0);
- odp_atomic_init_u64(&counters.ip, 0);
- odp_atomic_init_u64(&counters.udp, 0);
- odp_atomic_init_u64(&counters.icmp, 0);
- odp_atomic_init_u64(&counters.cnt, 0);
- odp_atomic_init_u64(&counters.tx_drops, 0);
-
- /* Reserve memory for args from shared mem */
- shm = odp_shm_reserve("shm_args", sizeof(args_t),
- ODP_CACHE_LINE_SIZE, 0);
- args = odp_shm_addr(shm);
-
- if (args == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(args, 0, sizeof(*args));
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &args->appl);
-
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]), &args->appl);
-
- /* Default to max number of workers, unless user specified number of
- * workers or cpumask */
- num_workers = MAX_WORKERS;
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
-
- if (args->appl.num_workers) {
- /* -w option: number of workers */
- num_workers = args->appl.num_workers;
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- } else if (args->appl.mask) {
- /* -c option: cpumask */
- odp_cpumask_from_str(&cpumask, args->appl.mask);
- num_workers = odp_cpumask_count(&cpumask);
- }
-
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- printf("num worker threads: %i\n", num_workers);
- printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n", cpumaskstr);
- fflush(NULL);
-
- /* ping mode need two workers */
- if (args->appl.mode == APPL_MODE_PING) {
- if (num_workers < 2) {
- EXAMPLE_ERR("Need at least two worker threads\n");
- exit(EXIT_FAILURE);
- } else {
- num_workers = 2;
- }
- }
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = POOL_PKT_LEN;
- params.pkt.len = POOL_PKT_LEN;
- params.pkt.num = POOL_NUM_PKT;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create("packet_pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
- odp_pool_print(pool);
-
- /* Create timer pool */
- tparams.res_ns = 1 * ODP_TIME_MSEC_IN_NS;
- tparams.min_tmo = 0;
- tparams.max_tmo = 10000 * ODP_TIME_SEC_IN_NS;
- tparams.num_timers = num_workers; /* One timer per worker */
- tparams.priv = 0; /* Shared */
- tparams.clk_src = ODP_CLOCK_CPU;
- tp = odp_timer_pool_create("timer_pool", &tparams);
- if (tp == ODP_TIMER_POOL_INVALID) {
- EXAMPLE_ERR("Timer pool create failed.\n");
- exit(EXIT_FAILURE);
- }
- odp_timer_pool_start();
-
- /* Create timeout pool */
- odp_pool_param_init(&params);
- params.tmo.num = tparams.num_timers; /* One timeout per timer */
- params.type = ODP_POOL_TIMEOUT;
-
- tmop = odp_pool_create("timeout_pool", &params);
- if (tmop == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: timeout pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- ifs = malloc(sizeof(interface_t) * args->appl.if_count);
-
- if (args->appl.mode == APPL_MODE_PING ||
- args->appl.mode == APPL_MODE_UDP)
- num_rx_queues = 1;
- else
- num_rx_queues = num_workers;
-
- if (args->appl.mode == APPL_MODE_PING ||
- args->appl.mode == APPL_MODE_RCV)
- num_tx_queues = 1;
- else {
- num_tx_queues = num_workers / args->appl.if_count;
- if (num_workers % args->appl.if_count)
- num_tx_queues++;
- }
-
- for (i = 0; i < args->appl.if_count; ++i)
- if (create_pktio(args->appl.if_names[i], pool, num_rx_queues,
- num_tx_queues, &ifs[i])) {
- EXAMPLE_ERR("Error: create interface %s failed.\n",
- args->appl.if_names[i]);
- exit(EXIT_FAILURE);
- }
-
- /* Create and init worker threads */
- memset(thread_tbl, 0, sizeof(thread_tbl));
-
- /* Init threads params */
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- /* num workers + print thread */
- odp_barrier_init(&barrier, num_workers + 1);
-
- if (args->appl.mode == APPL_MODE_PING) {
- odp_cpumask_t cpu_mask;
- int cpu_first, cpu_next;
-
- odp_cpumask_zero(&cpu_mask);
- cpu_first = odp_cpumask_first(&cpumask);
- odp_cpumask_set(&cpu_mask, cpu_first);
-
- tq = odp_queue_create("", NULL);
- if (tq == ODP_QUEUE_INVALID) {
- EXAMPLE_ERR("queue_create failed\n");
- abort();
- }
- (void)args->thread[1].pktout; /* Not used*/
- args->thread[1].pool = pool;
- args->thread[1].tp = tp;
- args->thread[1].tq = tq;
- args->thread[1].tim = odp_timer_alloc(tp, tq, NULL);
- if (args->thread[1].tim == ODP_TIMER_INVALID) {
- EXAMPLE_ERR("timer_alloc failed\n");
- abort();
- }
- args->thread[1].tmo_ev = odp_timeout_alloc(tmop);
- if (args->thread[1].tmo_ev == ODP_TIMEOUT_INVALID) {
- EXAMPLE_ERR("timeout_alloc failed\n");
- abort();
- }
- args->thread[1].mode = args->appl.mode;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = gen_recv_thread;
- thr_params.arg = &args->thread[1];
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- odph_odpthreads_create(&thread_tbl[1], &cpu_mask, &thr_params);
-
- tq = odp_queue_create("", NULL);
- if (tq == ODP_QUEUE_INVALID) {
- EXAMPLE_ERR("queue_create failed\n");
- abort();
- }
- args->thread[0].pktout = ifs[0].pktout[0];
- args->thread[0].pool = pool;
- args->thread[0].tp = tp;
- args->thread[0].tq = tq;
- args->thread[0].tim = odp_timer_alloc(tp, tq, NULL);
- if (args->thread[0].tim == ODP_TIMER_INVALID) {
- EXAMPLE_ERR("timer_alloc failed\n");
- abort();
- }
- args->thread[0].tmo_ev = odp_timeout_alloc(tmop);
- if (args->thread[0].tmo_ev == ODP_TIMEOUT_INVALID) {
- EXAMPLE_ERR("timeout_alloc failed\n");
- abort();
- }
- args->thread[0].mode = args->appl.mode;
- cpu_next = odp_cpumask_next(&cpumask, cpu_first);
- odp_cpumask_zero(&cpu_mask);
- odp_cpumask_set(&cpu_mask, cpu_next);
-
- thr_params.start = gen_send_thread;
- thr_params.arg = &args->thread[0];
-
- odph_odpthreads_create(&thread_tbl[0], &cpu_mask, &thr_params);
-
- } else {
- int cpu = odp_cpumask_first(&cpumask);
-
- for (i = 0; i < num_workers; ++i) {
- odp_cpumask_t thd_mask;
- int (*thr_run_func)(void *);
- int if_idx, pktout_idx;
-
- if (args->appl.mode == APPL_MODE_RCV)
- (void)args->thread[i].pktout; /*not used*/
- else {
- if_idx = i % args->appl.if_count;
- pktout_idx = (i / args->appl.if_count) %
- ifs[if_idx].pktout_count;
-
- args->thread[i].pktout =
- ifs[if_idx].pktout[pktout_idx];
- }
- tq = odp_queue_create("", NULL);
- if (tq == ODP_QUEUE_INVALID) {
- EXAMPLE_ERR("queue_create failed\n");
- abort();
- }
- args->thread[i].pool = pool;
- args->thread[i].tp = tp;
- args->thread[i].tq = tq;
- args->thread[i].tim = odp_timer_alloc(tp, tq, NULL);
- if (args->thread[i].tim == ODP_TIMER_INVALID) {
- EXAMPLE_ERR("timer_alloc failed\n");
- abort();
- }
- args->thread[i].tmo_ev = odp_timeout_alloc(tmop);
- if (args->thread[i].tmo_ev == ODP_TIMEOUT_INVALID) {
- EXAMPLE_ERR("timeout_alloc failed\n");
- abort();
- }
- args->thread[i].mode = args->appl.mode;
-
- if (args->appl.mode == APPL_MODE_UDP) {
- thr_run_func = gen_send_thread;
- } else if (args->appl.mode == APPL_MODE_RCV) {
- thr_run_func = gen_recv_thread;
- } else {
- EXAMPLE_ERR("ERR MODE\n");
- exit(EXIT_FAILURE);
- }
- /*
- * Create threads one-by-one instead of all-at-once,
- * because each thread might get different arguments.
- * Calls odp_thread_create(cpu) for each thread
- */
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
-
- thr_params.start = thr_run_func;
- thr_params.arg = &args->thread[i];
-
- odph_odpthreads_create(&thread_tbl[i],
- &thd_mask, &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
- }
- }
-
- print_global_stats(num_workers);
-
- /* Master thread waits for other threads to exit */
- for (i = 0; i < num_workers; ++i)
- odph_odpthreads_join(&thread_tbl[i]);
-
- for (i = 0; i < args->appl.if_count; ++i)
- odp_pktio_stop(ifs[i].pktio);
-
- for (i = 0; i < num_workers; ++i) {
- odp_timer_cancel(args->thread[i].tim, &ev);
- odp_timer_free(args->thread[i].tim);
- odp_timeout_free(args->thread[i].tmo_ev);
- }
-
- for (i = 0; i < num_workers; ++i) {
- while (1) {
- ev = odp_queue_deq(args->thread[i].tq);
- if (ev == ODP_EVENT_INVALID)
- break;
- odp_event_free(ev);
- }
- odp_queue_destroy(args->thread[i].tq);
- }
-
- for (i = 0; i < args->appl.if_count; ++i)
- odp_pktio_close(ifs[i].pktio);
- free(ifs);
- free(args->appl.if_names);
- free(args->appl.if_str);
- if (0 != odp_pool_destroy(pool))
- fprintf(stderr, "unable to destroy pool \"pool\"\n");
- odp_timer_pool_destroy(tp);
- if (0 != odp_pool_destroy(tmop))
- fprintf(stderr, "unable to destroy pool \"tmop\"\n");
- if (0 != odp_shm_free(shm))
- fprintf(stderr, "unable to free \"shm\"\n");
- odp_term_local();
- odp_term_global(instance);
- printf("Exit\n\n");
-
- return 0;
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
- int opt;
- int long_index;
- char *token;
- size_t len;
- odp_cpumask_t cpumask, cpumask_args, cpumask_and;
- int i, num_workers;
- static const struct option longopts[] = {
- {"interface", required_argument, NULL, 'I'},
- {"workers", required_argument, NULL, 'w'},
- {"cpumask", required_argument, NULL, 'c'},
- {"srcmac", required_argument, NULL, 'a'},
- {"dstmac", required_argument, NULL, 'b'},
- {"srcip", required_argument, NULL, 's'},
- {"dstip", required_argument, NULL, 'd'},
- {"srcport", required_argument, NULL, 'e'},
- {"dstport", required_argument, NULL, 'f'},
- {"packetsize", required_argument, NULL, 'p'},
- {"mode", required_argument, NULL, 'm'},
- {"count", required_argument, NULL, 'n'},
- {"timeout", required_argument, NULL, 't'},
- {"interval", required_argument, NULL, 'i'},
- {"help", no_argument, NULL, 'h'},
- {"udp_tx_burst", required_argument, NULL, 'x'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+I:a:b:s:d:p:i:m:n:t:w:c:x:he:f:";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- appl_args->mode = -1; /* Invalid, must be changed by parsing */
- appl_args->number = -1;
- appl_args->payload = 56;
- appl_args->timeout = -1;
- appl_args->interval = DEFAULT_PKT_INTERVAL;
- appl_args->udp_tx_burst = 16;
- appl_args->srcport = 0;
- appl_args->dstport = 0;
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'w':
- appl_args->num_workers = atoi(optarg);
- break;
- case 'c':
- appl_args->mask = optarg;
- odp_cpumask_from_str(&cpumask_args, args->appl.mask);
- num_workers = odp_cpumask_default_worker(&cpumask, 0);
- odp_cpumask_and(&cpumask_and, &cpumask_args, &cpumask);
- if (odp_cpumask_count(&cpumask_and) <
- odp_cpumask_count(&cpumask_args)) {
- EXAMPLE_ERR("Wrong cpu mask, max cpu's:%d\n",
- num_workers);
- exit(EXIT_FAILURE);
- }
- break;
- /* parse packet-io interface names */
- case 'I':
- len = strlen(optarg);
- if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- appl_args->if_str = malloc(len);
- if (appl_args->if_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* count the number of tokens separated by ',' */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL;
- token = strtok(NULL, ","), i++)
- ;
-
- appl_args->if_count = i;
-
- if (appl_args->if_count == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* allocate storage for the if names */
- appl_args->if_names =
- calloc(appl_args->if_count, sizeof(char *));
-
- /* store the if names (reset names string) */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- appl_args->if_names[i] = token;
- }
- break;
-
- case 'm':
- if (optarg[0] == 'u') {
- appl_args->mode = APPL_MODE_UDP;
- } else if (optarg[0] == 'p') {
- appl_args->mode = APPL_MODE_PING;
- } else if (optarg[0] == 'r') {
- appl_args->mode = APPL_MODE_RCV;
- } else {
- EXAMPLE_ERR("wrong mode!\n");
- exit(EXIT_FAILURE);
- }
- break;
-
- case 'a':
- if (odph_eth_addr_parse(&appl_args->srcmac, optarg)) {
- EXAMPLE_ERR("wrong src mac:%s\n", optarg);
- exit(EXIT_FAILURE);
- }
- break;
-
- case 'b':
- if (odph_eth_addr_parse(&appl_args->dstmac, optarg)) {
- EXAMPLE_ERR("wrong dst mac:%s\n", optarg);
- exit(EXIT_FAILURE);
- }
- break;
-
- case 's':
- if (scan_ip(optarg, &appl_args->srcip) != 1) {
- EXAMPLE_ERR("wrong src ip:%s\n", optarg);
- exit(EXIT_FAILURE);
- }
- break;
-
- case 'd':
- if (scan_ip(optarg, &appl_args->dstip) != 1) {
- EXAMPLE_ERR("wrong dst ip:%s\n", optarg);
- exit(EXIT_FAILURE);
- }
- break;
-
- case 'e':
- appl_args->srcport = (unsigned short)atoi(optarg);
- break;
- case 'f':
- appl_args->dstport = (unsigned short)atoi(optarg);
- break;
- case 'p':
- appl_args->payload = atoi(optarg);
- break;
-
- case 'n':
- appl_args->number = atoi(optarg);
- break;
-
- case 't':
- appl_args->timeout = atoi(optarg);
- break;
-
- case 'i':
- appl_args->interval = atoi(optarg);
- if (appl_args->interval <= 200 && geteuid() != 0) {
- EXAMPLE_ERR("should be root user\n");
- exit(EXIT_FAILURE);
- }
- break;
- case 'x':
- appl_args->udp_tx_burst = atoi(optarg);
- if (appl_args->udp_tx_burst > MAX_UDP_TX_BURST) {
- EXAMPLE_ERR("wrong UDP Tx burst size (max %d)\n",
- MAX_UDP_TX_BURST);
- exit(EXIT_FAILURE);
- }
- break;
-
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
-
- default:
- break;
- }
- }
-
- if (appl_args->if_count == 0 || appl_args->mode == -1) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args)
-{
- int i;
-
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %"PRIu64"\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n"
- "IF-count: %i\n"
- "Using IFs: ",
- progname, appl_args->if_count);
- for (i = 0; i < appl_args->if_count; ++i)
- printf(" %s", appl_args->if_names[i]);
- printf("\n"
- "Mode: ");
- if (appl_args->mode == 0)
- PRINT_APPL_MODE(APPL_MODE_UDP);
- else if (appl_args->mode == 1)
- PRINT_APPL_MODE(APPL_MODE_PING);
- else
- PRINT_APPL_MODE(APPL_MODE_RCV);
- printf("\n\n");
- fflush(NULL);
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s -I eth1 -r\n"
- "\n"
- "OpenDataPlane example application.\n"
- "\n"
- " Work mode:\n"
- " 1.send udp packets\n"
- " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 -m u\n"
- " 2.receive udp packets\n"
- " odp_generator -I eth0 -m r\n"
- " 3.work likes ping\n"
- " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 --cpumask 0xc -m p\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -I, --interface Eth interfaces (comma-separated, no spaces)\n"
- " -a, --srcmac src mac address\n"
- " -b, --dstmac dst mac address\n"
- " -s, --srcip src ip address\n"
- " -d, --dstip dst ip address\n"
- " -m, --mode work mode: send udp(u), receive(r), send icmp(p)\n"
- "\n"
- "Optional OPTIONS\n"
- " -h, --help Display help and exit.\n"
- " -e, --srcport src udp port\n"
- " -f, --dstport dst udp port\n"
- " -p, --packetsize payload length of the packets\n"
- " -t, --timeout only for ping mode, wait ICMP reply timeout seconds\n"
- " -i, --interval wait interval ms between sending each packet\n"
- " default is 1000ms. 0 for flood mode\n"
- " -w, --workers specify number of workers need to be assigned to application\n"
- " default is to assign all\n"
- " -n, --count the number of packets to be send\n"
- " -c, --cpumask to set on cores\n"
- " -x, --udp_tx_burst size of UDP TX burst\n"
- "\n", NO_PATH(progname), NO_PATH(progname)
- );
-}
diff --git a/example/hello/Makefile.am b/example/hello/Makefile.am
index 2e4e0cee5..a2d976015 100644
--- a/example/hello/Makefile.am
+++ b/example/hello/Makefile.am
@@ -1,10 +1,8 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_hello$(EXEEXT)
-odp_hello_LDFLAGS = $(AM_LDFLAGS) -static
-odp_hello_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_hello
-dist_odp_hello_SOURCES = odp_hello.c
+odp_hello_SOURCES = odp_hello.c
if test_example
TESTS = odp_hello
diff --git a/example/hello/odp_hello.c b/example/hello/odp_hello.c
index dba349548..58cc35502 100644
--- a/example/hello/odp_hello.c
+++ b/example/hello/odp_hello.c
@@ -1,52 +1,41 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
-/* This is a minimal application which demonstrates the startup and shutdown
+/**
+ * @example odp_hello.c
+ *
+ * This is a minimal application which demonstrates the startup and shutdown
* steps of an ODP application. It can be also used to debug API related
* build problems, etc. It does not use helpers to minimize dependency to
* anything else than the ODP API header file.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
-/* Linux CPU affinity */
-#define _GNU_SOURCE
-#include <sched.h>
-
-/* Linux PID */
-#include <sys/types.h>
-#include <unistd.h>
-
#include <stdio.h>
#include <string.h>
#include <odp_api.h>
typedef struct {
- int cpu;
int num;
} options_t;
static int parse_args(int argc, char *argv[], options_t *opt)
{
- static const char * const args[] = {"-c", "-n"};
+ static const char * const args[] = {"-n"};
int i, tmp;
for (i = 1; i < argc; i++) {
- if ((strcmp(argv[i], args[0]) == 0) &&
+ if ((strcmp(argv[i], args[0]) == 0) && argv[i + 1] &&
(sscanf(argv[i + 1], "%i", &tmp) == 1)) {
- opt->cpu = tmp;
- i++;
- } else if ((strcmp(argv[i], args[1]) == 0) &&
- (sscanf(argv[i + 1], "%i", &tmp) == 1)) {
opt->num = tmp;
i++;
} else {
printf("\nUsage:\n"
- " %s CPU number\n"
- " %s Number of iterations\n\n",
- args[0], args[1]);
+ " [%s Number of iterations]\n\n",
+ args[0]);
return -1;
}
}
@@ -58,36 +47,19 @@ int main(int argc, char *argv[])
{
odp_instance_t inst;
options_t opt;
- pid_t pid;
- cpu_set_t cpu_set;
int i;
- odp_cpumask_t mask;
memset(&opt, 0, sizeof(opt));
- opt.cpu = 0;
opt.num = 1;
if (parse_args(argc, argv, &opt))
return -1;
- pid = getpid();
-
if (odp_init_global(&inst, NULL, NULL)) {
printf("Global init failed.\n");
return -1;
}
- odp_cpumask_default_control(&mask, 0);
- opt.cpu = odp_cpumask_first(&mask);
-
- CPU_ZERO(&cpu_set);
- CPU_SET(opt.cpu, &cpu_set);
-
- if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpu_set)) {
- printf("Set CPU affinity failed.\n");
- return -1;
- }
-
if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
printf("Local init failed.\n");
return -1;
diff --git a/example/ipfragreass/Makefile.am b/example/ipfragreass/Makefile.am
index f805a69ba..f9180ea93 100644
--- a/example/ipfragreass/Makefile.am
+++ b/example/ipfragreass/Makefile.am
@@ -1,22 +1,17 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_ipfragreass$(EXEEXT)
-odp_ipfragreass_LDFLAGS = $(AM_LDFLAGS) -static
-odp_ipfragreass_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+LDADD += $(ATOMIC_LIBS) $(ATOMIC_LIBS_OPT)
-noinst_HEADERS = \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_atomics.h \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_atomics_arm.h \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_fragment.h \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_helpers.h \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_ip.h \
- $(top_srcdir)/example/ipfragreass/odp_ipfragreass_reassemble.h \
- $(top_srcdir)/example/example_debug.h
+bin_PROGRAMS = odp_ipfragreass
-dist_odp_ipfragreass_SOURCES = odp_ipfragreass.c \
- odp_ipfragreass_fragment.c \
- odp_ipfragreass_helpers.c \
- odp_ipfragreass_reassemble.c
+odp_ipfragreass_SOURCES = odp_ipfragreass.c \
+ odp_ipfragreass_fragment.c \
+ odp_ipfragreass_helpers.c \
+ odp_ipfragreass_reassemble.c \
+ odp_ipfragreass_fragment.h \
+ odp_ipfragreass_helpers.h \
+ odp_ipfragreass_ip.h \
+ odp_ipfragreass_reassemble.h
if test_example
TESTS = odp_ipfragreass
diff --git a/example/ipfragreass/odp_ipfragreass.c b/example/ipfragreass/odp_ipfragreass.c
index 6ad0db9fd..2b7df861e 100644
--- a/example/ipfragreass/odp_ipfragreass.c
+++ b/example/ipfragreass/odp_ipfragreass.c
@@ -1,13 +1,13 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
- * @file
+ * @example odp_ipfragreass.c
+ *
+ * IPv4 lock-free fragmentation and reassembly example application
*
- * @example odp_ipfragreass.c ODP IPv4 lock-free fragmentation and reassembly
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdio.h>
@@ -16,7 +16,6 @@
#include <assert.h>
#include <odp/helper/odph_api.h>
-#include <example_debug.h>
#include "odp_ipfragreass_fragment.h"
#include "odp_ipfragreass_reassemble.h"
@@ -47,12 +46,12 @@ static odp_queue_t reassembled_pkts;
static odp_atomic_u32_t packets_reassembled;
/** Number of fragments processed per thread in reassembly (for printing) */
-static struct {
+static struct ODP_ALIGNED_CACHE {
uint32_t frags;
-} ODP_ALIGNED_CACHE thread_stats[MAX_WORKERS];
+} thread_stats[MAX_WORKERS];
/** Shared hash map structure for reassembly */
-static union fraglist *fraglists;
+static odp_atomic_u128_t *fraglists;
/** Barrier for synchronising reassembly worker threads */
static odp_barrier_t barrier;
@@ -75,6 +74,7 @@ static void init(odp_instance_t *instance, odp_pool_t *fragment_pool,
odp_queue_param_t frag_queue_params;
odp_queue_param_t reass_queue_params;
char cpumask_str[ODP_CPUMASK_STR_SIZE];
+ union fraglist init_data;
srand(seed);
printf("= Seed: %d\n", seed);
@@ -104,7 +104,8 @@ static void init(odp_instance_t *instance, odp_pool_t *fragment_pool,
}
/* Reserve (and initialise) shared memory for reassembly fraglists */
- *shm = odp_shm_reserve("fraglists", FRAGLISTS * sizeof(union fraglist),
+ *shm = odp_shm_reserve("fraglists",
+ FRAGLISTS * sizeof(odp_atomic_u128_t),
ODP_CACHE_LINE_SIZE, 0);
if (*shm == ODP_SHM_INVALID) {
fprintf(stderr, "ERROR: odp_shm_reserve\n");
@@ -115,8 +116,10 @@ static void init(odp_instance_t *instance, odp_pool_t *fragment_pool,
fprintf(stderr, "ERROR: odp_shm_addr\n");
exit(1);
}
+
+ init_fraglist(&init_data);
for (i = 0; i < FRAGLISTS; ++i)
- init_fraglist(&fraglists[i]);
+ odp_atomic_init_u128(&fraglists[i], init_data.raw);
/* Create a queue for holding fragments */
odp_queue_param_init(&frag_queue_params);
@@ -161,7 +164,7 @@ static void init(odp_instance_t *instance, odp_pool_t *fragment_pool,
*
* @return Always returns zero
*/
-static int run_worker(void *arg EXAMPLE_UNUSED)
+static int run_worker(void *arg ODP_UNUSED)
{
int threadno = odp_thread_id() - 1;
int iterations = 0;
@@ -225,14 +228,15 @@ static int run_worker(void *arg EXAMPLE_UNUSED)
/**
* ODP fragmentation and reassembly example main function
*/
-int main(void)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
odp_pool_t fragment_pool;
odp_shm_t shm;
odp_cpumask_t cpumask;
- odph_odpthread_t threads[MAX_WORKERS] = {};
- odph_odpthread_params_t thread_params;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
odp_packet_t dequeued_pkts[NUM_PACKETS];
odp_event_t ev;
odp_u16be_t ip_id = 0;
@@ -290,19 +294,25 @@ int main(void)
}
/* Spawn the worker threads for reassembly */
- memset(&thread_params, 0, sizeof(thread_params));
- thread_params.start = run_worker;
- thread_params.arg = 0;
- thread_params.thr_type = ODP_THREAD_WORKER;
- thread_params.instance = instance;
- odph_odpthreads_create(threads, &cpumask, &thread_params);
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_worker;
+ thr_param.arg = 0;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
/* Go! */
printf("\n= Starting reassembly...\n");
odp_barrier_wait(&barrier);
/* Wait for all threads to complete and output statistics */
- odph_odpthreads_join(threads);
+ odph_thread_join(thread_tbl, num_workers);
for (i = 0; i < num_workers; ++i)
printf("=== Thread %02d processed %3d fragments\n", i,
thread_stats[i].frags);
diff --git a/example/ipfragreass/odp_ipfragreass_atomics.h b/example/ipfragreass/odp_ipfragreass_atomics.h
deleted file mode 100644
index 691978e51..000000000
--- a/example/ipfragreass/odp_ipfragreass_atomics.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_FRAGREASS_PP_ATOMICS_H_
-#define ODP_FRAGREASS_PP_ATOMICS_H_
-
-#include <example_debug.h>
-
-#if __SIZEOF_POINTER__ == 4
-/**
- * A wrapper function to perform a 64-bit "strong" atomic compare and swap
- * (CAS) operation. This generic version of the function is used if an
- * architecture-specific (e.g. lockless) variant is not specified.
- *
- * @param var The location at which the CAS should take place
- * @param exp A pointer to the expected value
- * @param neu A pointer to the new value to be written on success
- * @param mo_success The memory order on success
- * @param mo_failure The memory order on failure
- *
- * @return Whether the operation succeeded
- */
-static inline bool atomic_strong_cas_dblptr(uint64_t *var, uint64_t *exp,
- uint64_t neu, int mo_success,
- int mo_failure)
-{
- return __atomic_compare_exchange_n(var, exp, neu, 0, mo_success,
- mo_failure);
-}
-#elif __SIZEOF_POINTER__ == 8
-/**
- * A wrapper function to perform a 128-bit "strong" atomic compare and swap
- * (CAS) operation. This generic version of the function is used if an
- * architecture-specific (e.g. lockless) variant is not specified.
- *
- * @param var The location at which the CAS should take place
- * @param exp A pointer to the expected value
- * @param neu A pointer to the new value to be written on success
- * @param mo_success The memory order on success
- * @param mo_failure The memory order on failure
- *
- * @return Whether the operation succeeded
- */
-static inline bool atomic_strong_cas_dblptr(__int128 *var, __int128 *exp,
- __int128 neu, int mo_success,
- int mo_failure)
-{
- return __atomic_compare_exchange_n(var, exp, neu, 0, mo_success,
- mo_failure);
-}
-#endif
-#endif
diff --git a/example/ipfragreass/odp_ipfragreass_atomics_arm.h b/example/ipfragreass/odp_ipfragreass_atomics_arm.h
deleted file mode 100644
index e75ae5887..000000000
--- a/example/ipfragreass/odp_ipfragreass_atomics_arm.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_FRAGREASS_PP_ATOMICS_ARM_H_
-#define ODP_FRAGREASS_PP_ATOMICS_ARM_H_
-
-#include <example_debug.h>
-
-#if __SIZEOF_POINTER__ == 8 && defined(__aarch64__)
-static inline __int128 lld(__int128 *var, int mo)
-{
- __int128 old;
- uint64_t lo, hi;
-
- if (mo == __ATOMIC_ACQUIRE)
- __asm__ volatile("ldaxp %0, %1, [%2]" : "=&r" (lo), "=&r" (hi)
- : "r" (var) : "memory");
- else /* mo == __ATOMIC_RELAXED */
- __asm__ volatile("ldxp %0, %1, [%2]" : "=&r" (lo), "=&r" (hi)
- : "r" (var) : );
- old = hi;
- old <<= 64;
- old |= lo;
-
- return old;
-
-}
-
-static inline uint32_t scd(__int128 *var, __int128 neu, int mo)
-{
- uint32_t ret;
- uint64_t lo = neu, hi = neu >> 64;
-
- if (mo == __ATOMIC_RELEASE)
- __asm__ volatile("stlxp %w0, %1, %2, [%3]" : "=&r" (ret)
- : "r" (lo), "r" (hi), "r" (var) : "memory");
- else /* mo == __ATOMIC_RELAXED */
- __asm__ volatile("stxp %w0, %1, %2, [%3]" : "=&r" (ret)
- : "r" (lo), "r" (hi), "r" (var) : "memory");
- return ret;
-}
-
-static inline bool atomic_strong_cas_dblptr(__int128 *var, __int128 *exp,
- __int128 neu, int mo_success,
- int mo_failure EXAMPLE_UNUSED)
-{
- register __int128 old;
- register __int128 expected = *exp;
- int ll_mo, sc_mo;
-
- ll_mo = (mo_success != __ATOMIC_RELAXED &&
- mo_success != __ATOMIC_RELEASE) ? __ATOMIC_ACQUIRE
- : __ATOMIC_RELAXED;
- sc_mo = (mo_success == __ATOMIC_RELEASE ||
- mo_success == __ATOMIC_ACQ_REL ||
- mo_success == __ATOMIC_SEQ_CST) ? __ATOMIC_RELEASE
- : __ATOMIC_RELAXED;
-
- /*
- * To prevent spurious failures and ensure atomicity, we must write some
- * value back -- whether it's the value we wanted to write, or the value
- * that is currently there. Repeat until we perform a successful write.
- */
- do {
- old = lld(var, ll_mo);
- } while (scd(var, old == expected ? neu : old, sc_mo));
-
- *exp = old;
- return (old == expected);
-}
-#elif __SIZEOF_POINTER__ == 4 && defined(__ARM_ARCH) && __ARM_ARCH == 7
-static inline uint64_t lld(uint64_t *var, int mo)
-{
- uint64_t old;
-
- __asm__ volatile("ldrexd %0, %H0, [%1]" : "=&r" (old) : "r" (var) : );
- if (mo == __ATOMIC_ACQUIRE)
- __asm__ volatile("dmb ish" ::: "memory");
- return old;
-}
-
-static inline uint32_t scd(uint64_t *var, uint64_t neu, int mo)
-{
- uint32_t ret;
-
- if (mo == __ATOMIC_RELEASE)
- __asm__ volatile("dmb ish" ::: "memory");
- __asm__ volatile("strexd %0, %1, %H1, [%2]" : "=&r" (ret)
- : "r" (neu), "r" (var) : );
- return ret;
-}
-
-static inline bool atomic_strong_cas_dblptr(uint64_t *var, uint64_t *exp,
- uint64_t neu, int mo_success,
- int mo_failure EXAMPLE_UNUSED)
-{
- register uint64_t old;
- register uint64_t expected = *exp;
- int ll_mo, sc_mo;
-
- ll_mo = (mo_success != __ATOMIC_RELAXED &&
- mo_success != __ATOMIC_RELEASE) ? __ATOMIC_ACQUIRE
- : __ATOMIC_RELAXED;
- sc_mo = (mo_success == __ATOMIC_RELEASE ||
- mo_success == __ATOMIC_ACQ_REL ||
- mo_success == __ATOMIC_SEQ_CST) ? __ATOMIC_RELEASE
- : __ATOMIC_RELAXED;
-
- /*
- * To prevent spurious failures and ensure atomicity, we must write some
- * value back -- whether it's the value we wanted to write, or the value
- * that is currently there. Repeat until we perform a successful write.
- */
- do {
- old = lld(var, ll_mo);
- } while (scd(var, old == expected ? neu : old, sc_mo));
-
- *exp = old;
- return (old == expected);
-}
-#else
-#include "odp_ipfragreass_atomics.h"
-#endif
-#endif
diff --git a/example/ipfragreass/odp_ipfragreass_fragment.c b/example/ipfragreass/odp_ipfragreass_fragment.c
index b2fe6ad2a..2e9d9ad64 100644
--- a/example/ipfragreass/odp_ipfragreass_fragment.c
+++ b/example/ipfragreass/odp_ipfragreass_fragment.c
@@ -1,9 +1,9 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#include <stdio.h>
#include <assert.h>
diff --git a/example/ipfragreass/odp_ipfragreass_fragment.h b/example/ipfragreass/odp_ipfragreass_fragment.h
index f497fad70..748f84186 100644
--- a/example/ipfragreass/odp_ipfragreass_fragment.h
+++ b/example/ipfragreass/odp_ipfragreass_fragment.h
@@ -1,9 +1,9 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef ODP_FRAGREASS_PP_FRAG_H_
#define ODP_FRAGREASS_PP_FRAG_H_
diff --git a/example/ipfragreass/odp_ipfragreass_helpers.c b/example/ipfragreass/odp_ipfragreass_helpers.c
index e8d89b970..a48157fcf 100644
--- a/example/ipfragreass/odp_ipfragreass_helpers.c
+++ b/example/ipfragreass/odp_ipfragreass_helpers.c
@@ -1,9 +1,9 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#include <stdio.h>
#include <stdlib.h>
@@ -20,9 +20,9 @@ static void set_random_payload(odp_packet_t packet, uint32_t payload_offset,
uint32_t size)
{
uint32_t i, j;
- uint32_t bytes_remaining;
unsigned char *buffer;
unsigned char seed = rand() % (UINT8_MAX + 1);
+ uint32_t bytes_remaining = 0;
/*
* Set the payload to a run of consecutive numbers from a random seed.
@@ -99,11 +99,11 @@ int packet_memcmp(odp_packet_t a, odp_packet_t b, uint32_t offset_a,
{
uint32_t i = 0;
void *data_a, *data_b;
+ uint32_t bytes_remaining_a = 0;
+ uint32_t bytes_remaining_b = 0;
while (i < length) {
int status;
- uint32_t bytes_remaining_a;
- uint32_t bytes_remaining_b;
uint32_t bytes_remaining;
data_a = odp_packet_offset(a, offset_a + i, &bytes_remaining_a,
diff --git a/example/ipfragreass/odp_ipfragreass_helpers.h b/example/ipfragreass/odp_ipfragreass_helpers.h
index ffb3bfb7a..9f89ca4c4 100644
--- a/example/ipfragreass/odp_ipfragreass_helpers.h
+++ b/example/ipfragreass/odp_ipfragreass_helpers.h
@@ -1,20 +1,14 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef ODP_FRAGREASS_PP_HELPERS_H_
#define ODP_FRAGREASS_PP_HELPERS_H_
#include <odp/helper/ip.h>
-#if defined(__ARM_ARCH)
-#include "odp_ipfragreass_atomics_arm.h"
-#else
-#include "odp_ipfragreass_atomics.h"
-#endif
-
/**
* Generate a random IPv4 UDP packet from the specified parameters
*
diff --git a/example/ipfragreass/odp_ipfragreass_ip.h b/example/ipfragreass/odp_ipfragreass_ip.h
index e7281e5a2..97e68324c 100644
--- a/example/ipfragreass/odp_ipfragreass_ip.h
+++ b/example/ipfragreass/odp_ipfragreass_ip.h
@@ -1,9 +1,9 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef ODP_FRAGREASS_PP_IP_H_
#define ODP_FRAGREASS_PP_IP_H_
diff --git a/example/ipfragreass/odp_ipfragreass_reassemble.c b/example/ipfragreass/odp_ipfragreass_reassemble.c
index fd1618f54..89055e6e4 100644
--- a/example/ipfragreass/odp_ipfragreass_reassemble.c
+++ b/example/ipfragreass/odp_ipfragreass_reassemble.c
@@ -1,9 +1,11 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <odp_api.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
@@ -87,7 +89,7 @@ static inline uint32_t hash(odph_ipv4hdr_t *hdr)
{
uint32_t a = hdr->src_addr;
uint32_t b = hdr->dst_addr;
- uint32_t c = hdr->id << 16 | hdr->proto;
+ uint32_t c = (uint32_t)hdr->id << 16 | hdr->proto;
/* A degenerate 3x32-bit Jenkins hash */
c ^= b;
@@ -181,11 +183,9 @@ static struct packet *extract_complete_packet(struct packet *tail,
struct packet *current = tail;
odph_ipv4hdr_t tail_hdr;
uint16_t final_frag_offset;
- uint16_t expected_frag_offset;
tail_hdr = *(odph_ipv4hdr_t *)odp_packet_data(tail->handle);
final_frag_offset = ipv4hdr_fragment_offset_oct(tail_hdr);
- expected_frag_offset = final_frag_offset;
while (current) {
odph_ipv4hdr_t curr_hdr;
uint16_t curr_offset_oct;
@@ -256,7 +256,6 @@ static struct packet *extract_complete_packet(struct packet *tail,
break;
}
- expected_frag_offset -= prev_oct;
current = prev;
}
@@ -303,11 +302,17 @@ static int send_packet(struct packet *tail, odp_queue_t out)
*/
while (current && equal_flow(current, &result)) {
struct packet new_result = *current;
- int concat_success;
+ int concat_success, trunc_success;
current = prev_packet(new_result);
header = odp_packet_data(result.handle);
- odp_packet_pull_head(result.handle, ipv4hdr_ihl(*header));
+ trunc_success = odp_packet_trunc_head(&result.handle, ipv4hdr_ihl(*header),
+ NULL, NULL);
+ if (trunc_success < 0) {
+ fprintf(stderr, "ERROR: odp_packet_trunc_head\n");
+ return -1;
+ }
+
concat_success = odp_packet_concat(&new_result.handle,
result.handle);
if (concat_success < 0) {
@@ -376,7 +381,7 @@ static void sort_fraglist(union fraglist *fl, struct flts now)
*
* @return The number of packets reassembled and sent to the output queue
*/
-static int add_fraglist_to_fraglist(union fraglist *fl, union fraglist frags,
+static int add_fraglist_to_fraglist(odp_atomic_u128_t *fl, union fraglist frags,
struct packet *frags_head, struct flts now,
odp_queue_t out, odp_bool_t dont_assemble)
{
@@ -395,8 +400,7 @@ redo:;
struct flts oldfl_earliest;
struct flts frags_earliest;
- __atomic_load(&fl->half[0], &oldfl.half[0], __ATOMIC_RELAXED);
- __atomic_load(&fl->half[1], &oldfl.half[1], __ATOMIC_RELAXED);
+ oldfl.raw = odp_atomic_load_u128(fl);
/*
* If we're updating a non-empty fraglist, we should always attempt
@@ -429,9 +433,7 @@ redo:;
* yet. If not, just write out our changes and move on.
*/
if (newfl.part_len < newfl.whole_len || dont_assemble) {
- if (!atomic_strong_cas_dblptr(&fl->raw, &oldfl.raw, newfl.raw,
- __ATOMIC_RELEASE,
- __ATOMIC_RELAXED)) {
+ if (!odp_atomic_cas_rel_u128(fl, &oldfl.raw, newfl.raw)) {
/* Failed to add this fragment? Try again. */
set_prev_packet(frags_head, NULL);
goto redo;
@@ -450,8 +452,7 @@ redo:;
* otherwise we'll update the slot with our changes later.
*/
init_fraglist(&nullfl);
- if (!atomic_strong_cas_dblptr(&fl->raw, &oldfl.raw, nullfl.raw,
- __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
+ if (!odp_atomic_cas_acq_u128(fl, &oldfl.raw, nullfl.raw)) {
/* Failed to take this fraglist? Try again. */
set_prev_packet(frags_head, NULL);
goto redo;
@@ -554,7 +555,7 @@ redo:;
*
* @return The number of packets reassembled and sent to the output
*/
-static int add_frag_to_fraglist(union fraglist *fl, struct packet *frag,
+static int add_frag_to_fraglist(odp_atomic_u128_t *fl, struct packet *frag,
uint16_t frag_payload_len,
uint16_t frag_reass_payload_len,
odp_queue_t out)
@@ -580,7 +581,7 @@ static int add_frag_to_fraglist(union fraglist *fl, struct packet *frag,
* @param out The queue to which reassembled packets should be written
* @param force Whether all flows in the fraglist should be considered stale
*/
-static void remove_stale_flows(union fraglist *fl, union fraglist oldfl,
+static void remove_stale_flows(odp_atomic_u128_t *fl, union fraglist oldfl,
struct flts timestamp_now, odp_queue_t out,
odp_bool_t force)
{
@@ -679,7 +680,7 @@ static void remove_stale_flows(union fraglist *fl, union fraglist oldfl,
* @param out The queue to which reassembled packets should be written
* @param force Whether all flows in the fraglist should be considered stale
*/
-static void garbage_collect_fraglist(union fraglist *fl, odp_queue_t out,
+static void garbage_collect_fraglist(odp_atomic_u128_t *fl, odp_queue_t out,
odp_bool_t force)
{
uint64_t time_now;
@@ -692,8 +693,9 @@ static void garbage_collect_fraglist(union fraglist *fl, odp_queue_t out,
do {
time_now = odp_time_to_ns(odp_time_global());
timestamp_now.t = time_now / TS_RES_NS;
- __atomic_load(&fl->half[0], &oldfl.half[0], __ATOMIC_RELAXED);
- __atomic_load(&fl->half[1], &oldfl.half[1], __ATOMIC_RELAXED);
+
+ oldfl.raw = odp_atomic_load_u128(fl);
+
elapsed.t = timestamp_now.t - oldfl.earliest;
if (oldfl.tail == NULL ||
@@ -706,11 +708,8 @@ static void garbage_collect_fraglist(union fraglist *fl, odp_queue_t out,
union fraglist nullfl;
init_fraglist(&nullfl);
- success = atomic_strong_cas_dblptr(&fl->raw, &oldfl.raw,
- nullfl.raw,
- __ATOMIC_ACQUIRE,
- __ATOMIC_RELAXED);
-
+ success = odp_atomic_cas_acq_u128(fl, &oldfl.raw,
+ nullfl.raw);
if (success)
remove_stale_flows(fl, oldfl, timestamp_now,
out, force);
@@ -718,7 +717,7 @@ static void garbage_collect_fraglist(union fraglist *fl, odp_queue_t out,
} while (!success);
}
-int reassemble_ipv4_packets(union fraglist *fraglists, int num_fraglists,
+int reassemble_ipv4_packets(odp_atomic_u128_t *fraglists, int num_fraglists,
struct packet *fragments, int num_fragments,
odp_queue_t out)
{
@@ -731,7 +730,7 @@ int reassemble_ipv4_packets(union fraglist *fraglists, int num_fraglists,
uint16_t frag_payload_len;
uint16_t frag_reass_payload_len;
uint32_t key;
- union fraglist *fl;
+ odp_atomic_u128_t *fl;
int status;
frag = fragments[i];
@@ -761,7 +760,7 @@ int reassemble_ipv4_packets(union fraglist *fraglists, int num_fraglists,
return packets_reassembled;
}
-void garbage_collect_fraglists(union fraglist *fraglists, int num_fraglists,
+void garbage_collect_fraglists(odp_atomic_u128_t *fraglists, int num_fraglists,
odp_queue_t out, odp_bool_t destroy_all)
{
int i;
diff --git a/example/ipfragreass/odp_ipfragreass_reassemble.h b/example/ipfragreass/odp_ipfragreass_reassemble.h
index ffc4bfb59..b00f3825e 100644
--- a/example/ipfragreass/odp_ipfragreass_reassemble.h
+++ b/example/ipfragreass/odp_ipfragreass_reassemble.h
@@ -1,19 +1,19 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef ODP_FRAGREASS_PP_REASSEMBLE_H_
#define ODP_FRAGREASS_PP_REASSEMBLE_H_
+#include <odp_api.h>
#include <odp/helper/ip.h>
#include "odp_ipfragreass_ip.h"
#include "odp_ipfragreass_helpers.h"
-ODP_STATIC_ASSERT((__SIZEOF_POINTER__ == 4 || __SIZEOF_POINTER__ == 8),
- "ODPREASS_PTR__SIZE_ERROR");
+ODP_STATIC_ASSERT(__SIZEOF_POINTER__ <= 8, "ODP_REASS_PTR__SIZE_ERROR");
/**
* The time in nanoseconds after reception of the earliest fragment that a
@@ -22,41 +22,25 @@ ODP_STATIC_ASSERT((__SIZEOF_POINTER__ == 4 || __SIZEOF_POINTER__ == 8),
#define FLOW_TIMEOUT_NS 15000000000ULL
/** Convert nanoseconds into a unit for packet.arrival */
-#if __SIZEOF_POINTER__ == 4
-#define TS_RES_NS ((uint64_t)5000000000) /**< ns -> 5s */
-#elif __SIZEOF_POINTER__ == 8
#define TS_RES_NS ((uint64_t)1000000) /**< ns -> 1ms */
-#endif
/**
* The maximum value of the packet.arrival field.
*/
-#if __SIZEOF_POINTER__ == 4
-#define EARLIEST_MAX 15
-#elif __SIZEOF_POINTER__ == 8
#define EARLIEST_MAX UINT32_MAX
-#endif
/**
* The time in packet.arrival ticks that indications of the time "now" are
* permitted to be off by.
*/
-#if __SIZEOF_POINTER__ == 4
-#define TS_NOW_TOLERANCE 1
-#elif __SIZEOF_POINTER__ == 8
#define TS_NOW_TOLERANCE 5000
-#endif
/**
* The timestamp format used for fragments. Sadly, this has to be a structure
* as we may need a bit field.
*/
struct flts {
-#if __SIZEOF_POINTER__ == 4
- uint8_t t:4;
-#elif __SIZEOF_POINTER__ == 8
uint32_t t;
-#endif
};
/**
@@ -81,11 +65,7 @@ union fraglist {
* The timestamp of the earliest arriving fragment in this
* fraglist
*/
-#if __SIZEOF_POINTER__ == 4
- uint32_t earliest:4;
-#elif __SIZEOF_POINTER__ == 8
uint32_t earliest;
-#endif
/**
* The sum of the payloads of the fragments in this list
@@ -116,17 +96,7 @@ union fraglist {
struct packet *tail;
};
-#if __SIZEOF_POINTER__ == 4
- struct {
- uint32_t half[2];
- };
- uint64_t raw;
-#elif __SIZEOF_POINTER__ == 8
- struct {
- uint64_t half[2];
- };
- __int128 raw;
-#endif
+ odp_u128_t raw;
};
/**
@@ -193,7 +163,7 @@ static inline void set_prev_packet(struct packet *packet, struct packet *prev)
*
* @return The number of packets successfully reassembled and written to "out"
*/
-int reassemble_ipv4_packets(union fraglist *fraglists, int num_fraglists,
+int reassemble_ipv4_packets(odp_atomic_u128_t *fraglists, int num_fraglists,
struct packet *fragments, int num_fragments,
odp_queue_t out);
@@ -205,7 +175,7 @@ int reassemble_ipv4_packets(union fraglist *fraglists, int num_fraglists,
* @param out The queue to which reassembled packets should be written
* @param destroy_all Whether all encountered flows should be cleaned up
*/
-void garbage_collect_fraglists(union fraglist *fraglists, int num_fraglists,
+void garbage_collect_fraglists(odp_atomic_u128_t *fraglists, int num_fraglists,
odp_queue_t out, odp_bool_t destroy_all);
#endif
diff --git a/example/ipsec/.gitignore b/example/ipsec/.gitignore
deleted file mode 100644
index 5b410d31b..000000000
--- a/example/ipsec/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-odp_ipsec
diff --git a/example/ipsec/Makefile.am b/example/ipsec/Makefile.am
deleted file mode 100644
index fd9b3c722..000000000
--- a/example/ipsec/Makefile.am
+++ /dev/null
@@ -1,36 +0,0 @@
-include $(top_srcdir)/example/Makefile.inc
-
-AM_CPPFLAGS = $(OPENSSL_CPPFLAGS)
-
-bin_PROGRAMS = odp_ipsec$(EXEEXT)
-odp_ipsec_LDFLAGS = $(AM_LDFLAGS) -static
-odp_ipsec_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-
-noinst_HEADERS = \
- $(top_srcdir)/example/ipsec/odp_ipsec_cache.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_fwd_db.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_loop_db.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_misc.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_sa_db.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_sp_db.h \
- $(top_srcdir)/example/ipsec/odp_ipsec_stream.h \
- $(top_srcdir)/example/example_debug.h
-
-dist_bin_SCRIPTS = \
- $(srcdir)/odp_ipsec_run_ah_in \
- $(srcdir)/odp_ipsec_run_ah_out \
- $(srcdir)/odp_ipsec_run_both_in \
- $(srcdir)/odp_ipsec_run_both_out \
- $(srcdir)/odp_ipsec_run_esp_in \
- $(srcdir)/odp_ipsec_run_esp_out \
- $(srcdir)/odp_ipsec_run_live \
- $(srcdir)/odp_ipsec_run_router \
- $(srcdir)/odp_ipsec_run_simple
-
-dist_odp_ipsec_SOURCES = odp_ipsec.c \
- odp_ipsec_sa_db.c \
- odp_ipsec_sp_db.c \
- odp_ipsec_fwd_db.c \
- odp_ipsec_loop_db.c \
- odp_ipsec_cache.c \
- odp_ipsec_stream.c
diff --git a/example/ipsec/README b/example/ipsec/README
deleted file mode 100644
index 74f1d26e7..000000000
--- a/example/ipsec/README
+++ /dev/null
@@ -1,171 +0,0 @@
-Copyright (c) 2014, Linaro Limited
-All rights reserved.
-
-SPDX-License-Identifier: BSD-3-Clause
-
-1. Intro
-
-The IPsec example application "odp_ipsec" functions as a simple L3 IPv4 router
-with support IPsec 3DES cipher and HMAC-MD5 authentication in both the transmit
-and receive directions. Note that only IPsec "transport" mode is supported.
-
-2. Prerequisites
-
- 2.1 SSL development libraries
-
-Development has been done to this point with the openssl-devel libraries,
-the makefile specifically links with "-lcrypto".
-
-3. Topology
-
-The following test topology was used for development. Each of the VMs
-is running Fedora16. Sanity testing consists of pinging VM2 from VM0
-such that the packets traverse VM1. Packets between VM1 and VM2 are
-IPsec AH and ESP encapsulated.
-
- VM0 VM1 (UUT) VM2
-+------------+ +--------------+ +------------+
-| | (clear) | | (crypto) | |
-| | subnet | | subnet | |
-| p7p1 |<---------------->| p7p1 p8p1 |<---------------->| p7p1 |
-| .2 | 192.168.111.0 | .1 .1 | 192.168.222.0 | .2 |
-| | | | | |
-+------------+ +--------------+ +------------+
-
-4. VM configurations
-
- 4.1 VM0 configuration
-
-VM0 has the follwing interface configuration:
-
- cat /etc/sysconfig/network-scripts/ifcfg-p7p1
- DEVICE=p7p1
- HWADDR=08:00:27:76:B5:E0
- BOOTPROTO=static
- IPADDR=192.168.111.2
- NETMASK=255.255.255.0
- ONBOOT=yes
-
-In addition, static ARP and IPv4 routes must be added on VM0:
-
- sudo ip route add 192.168.222.0/24 via 192.168.111.1
- sudo arp -s 192.168.111.1 08:00:27:04:BF:8C
-
- 4.2 VM1 configuration
-
-For the unit under test, IP forwarding and IP tables were disabled.
-
-VM1 has the follwing interface configurations:
-
- cat /etc/sysconfig/network-scripts/ifcfg-p7p1
- DEVICE=p7p1
- HWADDR=08:00:27:04:BF:8C
- BOOTPROTO=static
- IPADDR=192.168.111.1
- NETMASK=255.255.255.0
- ONBOOT=yes
-
- cat /etc/sysconfig/network-scripts/ifcfg-p8p1
- DEVICE=p8p1
- HWADDR=08:00:27:4C:55:CC
- BOOTPROTO=static
- IPADDR=192.168.222.1
- NETMASK=255.255.255.0
- ONBOOT=yes
-
-The application is launched on VM1 with the following command line
-using a bash script:
-
- cat test/ipsec/run_test.sh
- #!/bin/bash
- sudo ./odp_ipsec -i p7p1,p8p1 \
- -r 192.168.111.2/32:p7p1:08.00.27.76.B5.E0 \
- -r 192.168.222.2/32:p8p1:08.00.27.F5.8B.DB \
- -p 192.168.111.0/24:192.168.222.0/24:out:both \
- -e 192.168.111.2:192.168.222.2:3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
- -a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
- -p 192.168.222.0/24:192.168.111.0/24:in:both \
- -e 192.168.222.2:192.168.111.2:3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
- -a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
- -c 2 -f 0 -m 0
-
- 4.3 VM2 configuration
-
-VM2 must be setup with an IPsec configuration complementing
-the configuration used by the "odp_ipsec" application running
-on VM1. The configuration is applied using "setkey"
-
-VM2 has the following setkey configuration file applied:
-
- cat /media/sf_SharedVM2/setkey_vm2.txt
- #!/sbin/setkey -f
-
- # Flush the SAD and SPD
- flush;
- spdflush;
-
- add 192.168.111.2 192.168.222.2 ah 0x200 -A hmac-md5
- 0xa731649644c5dee92cbd9c2e7e188ee6;
- add 192.168.222.2 192.168.111.2 ah 0x300 -A hmac-md5
- 0x27f6d123d7077b361662fc6e451f65d8;
-
- add 192.168.111.2 192.168.222.2 esp 0x201 -E 3des-cbc
- 0x656c8523255ccc23a66c1917aa0cf30991fce83532a4b224;
- add 192.168.222.2 192.168.111.2 esp 0x301 -E 3des-cbc
- 0xc966199f24d095f3990a320d749056401e82b26570320292;
-
- spdadd 192.168.111.2 192.168.222.2 any -P in ipsec
- esp/transport//require
- ah/transport//require;
-
- spdadd 192.168.222.2 192.168.111.2 any -P out ipsec
- esp/transport//require
- ah/transport//require;
-
-VM2 has the follwing interface configuration:
-
- cat /etc/sysconfig/network-scripts/ifcfg-p7p1
- DEVICE=p7p1
- HWADDR=08:00:27:F5:8B:DB
- BOOTPROTO=static
- IPADDR=192.168.222.2
- NETMASK=255.255.255.0
- ONBOOT=yes
-
-In addition, static ARP and IPv4 routes must be added on VM2:
-
- sudo ip route add 192.168.111.0/24 via 192.168.222.1
- sudo arp -s 192.168.222.1 08:00:27:4c:55:cc
-
-5. Sanity Test with Real Traffic
-
-Once all three VMs have been configured and static ARP and route
-entries added, VM0 should be able to ping VM2 at the 192.168.222.2
-address.
-
-At VM0 console issue the ping to VM2's address:
-
- sudo ping -c 2 -i 0.1 192.168.222.2
- PING 192.168.222.2 (192.168.222.2) 56(84) bytes of data.
- 64 bytes from 192.168.222.2: icmp_req=1 ttl=64 time=33.9 ms
- 64 bytes from 192.168.222.2: icmp_req=2 ttl=64 time=23.3 ms
-
-At VM2 console use tcpdump to observe IPsec packets :
-
- sudo tcpdump -nt -i p7p1
- tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
- listening on p7p1, link-type EN10MB (Ethernet), capture size 65535 bytes
-
- IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x6): ESP(spi=0x00000201,seq=0x6), length 88
- IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7a): ESP(spi=0x00000301,seq=0x7a), length 88
- IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x7): ESP(spi=0x00000201,seq=0x7), length 88
- IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7b): ESP(spi=0x00000301,seq=0x7b), length 88
-
-6. Standalone Loopback Tests
-
-BASH batch files are now included to run several simple loopback tests that
-do not require any packet IO. The scripts create internal "loopback" (not
-real Linux loopback interfaces but simply ODP queues) as opposed to packet
-interfaces.
-Before running the example bash scripts add odp_ipsec to your PATH
-export PATH="<path_to_odp_ipsec>:$PATH"
diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
deleted file mode 100644
index 9ed3aa201..000000000
--- a/example/ipsec/odp_ipsec.c
+++ /dev/null
@@ -1,1619 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_example_ipsec.c ODP basic packet IO cross connect with IPsec test application
- */
-
-/* enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp/helper/odph_api.h>
-
-#include <stdbool.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-
-#include <sys/socket.h>
-#include <netpacket/packet.h>
-#include <net/ethernet.h>
-#include <arpa/inet.h>
-
-#include <odp_ipsec_misc.h>
-#include <odp_ipsec_sa_db.h>
-#include <odp_ipsec_sp_db.h>
-#include <odp_ipsec_fwd_db.h>
-#include <odp_ipsec_loop_db.h>
-#include <odp_ipsec_cache.h>
-#include <odp_ipsec_stream.h>
-
-#define MAX_WORKERS 32 /**< maximum number of worker threads */
-
-/**
- * Parsed command line application arguments
- */
-typedef struct {
- int cpu_count;
- int if_count; /**< Number of interfaces to be used */
- char **if_names; /**< Array of pointers to interface names */
- crypto_api_mode_e mode; /**< Crypto API preferred mode */
- odp_pool_t pool; /**< Buffer pool for packet IO */
- char *if_str; /**< Storage for interface names */
-} appl_args_t;
-
-/**
- * Grouping of both parsed CL args and thread specific args - alloc together
- */
-typedef struct {
- /** Application (parsed) arguments */
- appl_args_t appl;
-} args_t;
-
-/* helper funcs */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
-static void print_info(char *progname, appl_args_t *appl_args);
-static void usage(char *progname);
-
-/** Global pointer to args */
-static args_t *args;
-
-/**
- * Buffer pool for packet IO
- */
-#define SHM_PKT_POOL_BUF_COUNT 1024
-#define SHM_PKT_POOL_BUF_SIZE 4096
-#define SHM_PKT_POOL_SIZE (SHM_PKT_POOL_BUF_COUNT * SHM_PKT_POOL_BUF_SIZE)
-
-static odp_pool_t pkt_pool = ODP_POOL_INVALID;
-
-/**
- * Buffer pool for crypto session output packets
- */
-#define SHM_OUT_POOL_BUF_COUNT 1024
-#define SHM_OUT_POOL_BUF_SIZE 4096
-#define SHM_OUT_POOL_SIZE (SHM_OUT_POOL_BUF_COUNT * SHM_OUT_POOL_BUF_SIZE)
-
-static odp_pool_t out_pool = ODP_POOL_INVALID;
-
-/** ATOMIC queue for IPsec sequence number assignment */
-static odp_queue_t seqnumq;
-
-/** ORDERED queue (eventually) for per packet crypto API completion events */
-static odp_queue_t completionq;
-
-/** Synchronize threads before packet processing begins */
-static odp_barrier_t sync_barrier;
-
-/**
- * Packet processing states/steps
- */
-typedef enum {
- PKT_STATE_INPUT_VERIFY, /**< Verify IPv4 and ETH */
- PKT_STATE_IPSEC_IN_CLASSIFY, /**< Initiate input IPsec */
- PKT_STATE_IPSEC_IN_FINISH, /**< Finish input IPsec */
- PKT_STATE_ROUTE_LOOKUP, /**< Use DST IP to find output IF */
- PKT_STATE_IPSEC_OUT_CLASSIFY, /**< Intiate output IPsec */
- PKT_STATE_IPSEC_OUT_SEQ, /**< Assign IPsec sequence numbers */
- PKT_STATE_IPSEC_OUT_FINISH, /**< Finish output IPsec */
- PKT_STATE_TRANSMIT, /**< Send packet to output IF queue */
-} pkt_state_e;
-
-/**
- * Packet processing result codes
- */
-typedef enum {
- PKT_CONTINUE, /**< No events posted, keep processing */
- PKT_POSTED, /**< Event posted, stop processing */
- PKT_DROP, /**< Reason to drop detected, stop processing */
- PKT_DONE /**< Finished with packet, stop processing */
-} pkt_disposition_e;
-
-/**
- * Per packet IPsec processing context
- */
-typedef struct {
- uint8_t ip_tos; /**< Saved IP TOS value */
- uint16_t ip_frag_offset; /**< Saved IP flags value */
- uint8_t ip_ttl; /**< Saved IP TTL value */
- int hdr_len; /**< Length of IPsec headers */
- int trl_len; /**< Length of IPsec trailers */
- uint16_t tun_hdr_offset; /**< Offset of tunnel header from
- buffer start */
- uint16_t ah_offset; /**< Offset of AH header from buffer start */
- uint16_t esp_offset; /**< Offset of ESP header from buffer start */
-
- /* Input only */
- uint32_t src_ip; /**< SA source IP address */
- uint32_t dst_ip; /**< SA dest IP address */
-
- /* Output only */
- odp_crypto_op_param_t params; /**< Parameters for crypto call */
- uint32_t *ah_seq; /**< AH sequence number location */
- uint32_t *esp_seq; /**< ESP sequence number location */
- uint16_t *tun_hdr_id; /**< Tunnel header ID > */
-} ipsec_ctx_t;
-
-/**
- * Per packet processing context
- */
-typedef struct {
- odp_buffer_t buffer; /**< Buffer for context */
- pkt_state_e state; /**< Next processing step */
- ipsec_ctx_t ipsec; /**< IPsec specific context */
- odp_pktout_queue_t pktout; /**< Packet output queue */
-} pkt_ctx_t;
-
-#define SHM_CTX_POOL_BUF_SIZE (sizeof(pkt_ctx_t))
-#define SHM_CTX_POOL_BUF_COUNT (SHM_PKT_POOL_BUF_COUNT + SHM_OUT_POOL_BUF_COUNT)
-#define SHM_CTX_POOL_SIZE (SHM_CTX_POOL_BUF_COUNT * SHM_CTX_POOL_BUF_SIZE)
-
-static odp_pool_t ctx_pool = ODP_POOL_INVALID;
-
-/**
- * Get per packet processing context from packet buffer
- *
- * @param pkt Packet
- *
- * @return pointer to context area
- */
-static
-pkt_ctx_t *get_pkt_ctx_from_pkt(odp_packet_t pkt)
-{
- return (pkt_ctx_t *)odp_packet_user_ptr(pkt);
-}
-
-/**
- * Allocate per packet processing context and associate it with
- * packet buffer
- *
- * @param pkt Packet
- *
- * @return pointer to context area
- */
-static
-pkt_ctx_t *alloc_pkt_ctx(odp_packet_t pkt)
-{
- odp_buffer_t ctx_buf = odp_buffer_alloc(ctx_pool);
- pkt_ctx_t *ctx;
-
- if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf))
- return NULL;
-
- ctx = odp_buffer_addr(ctx_buf);
- memset(ctx, 0, sizeof(*ctx));
- ctx->buffer = ctx_buf;
- odp_packet_user_ptr_set(pkt, ctx);
-
- return ctx;
-}
-
-/**
- * Release per packet resources
- *
- * @param ctx Packet context
- */
-static
-void free_pkt_ctx(pkt_ctx_t *ctx)
-{
- odp_buffer_free(ctx->buffer);
-}
-
-/**
- * Example supports either polling queues or using odp_schedule
- */
-typedef odp_queue_t (*queue_create_func_t)
- (const char *, const odp_queue_param_t *);
-typedef odp_event_t (*schedule_func_t) (odp_queue_t *);
-
-static queue_create_func_t queue_create;
-static schedule_func_t schedule;
-
-#define MAX_POLL_QUEUES 256
-
-static odp_queue_t poll_queues[MAX_POLL_QUEUES];
-static int num_polled_queues;
-
-/**
- * odp_queue_create wrapper to enable polling versus scheduling
- */
-static
-odp_queue_t polled_odp_queue_create(const char *name,
- const odp_queue_param_t *param)
-{
- odp_queue_t my_queue;
- odp_queue_param_t qp;
- odp_queue_type_t type;
-
- odp_queue_param_init(&qp);
- if (param)
- memcpy(&qp, param, sizeof(odp_queue_param_t));
-
- type = qp.type;
-
- if (ODP_QUEUE_TYPE_SCHED == type) {
- printf("%s: change %s to PLAIN\n", __func__, name);
- qp.type = ODP_QUEUE_TYPE_PLAIN;
- }
-
- my_queue = odp_queue_create(name, &qp);
-
- if (ODP_QUEUE_TYPE_SCHED == type) {
- poll_queues[num_polled_queues++] = my_queue;
- printf("%s: adding %"PRIu64"\n", __func__,
- odp_queue_to_u64(my_queue));
- }
-
- return my_queue;
-}
-
-static inline
-odp_event_t odp_schedule_cb(odp_queue_t *from)
-{
- return odp_schedule(from, ODP_SCHED_WAIT);
-}
-
-/**
- * odp_schedule replacement to poll queues versus using ODP scheduler
- */
-static
-odp_event_t polled_odp_schedule_cb(odp_queue_t *from)
-{
- int idx = 0;
-
- while (1) {
- if (idx >= num_polled_queues)
- idx = 0;
-
- odp_queue_t queue = poll_queues[idx++];
- odp_event_t buf;
-
- buf = odp_queue_deq(queue);
-
- if (ODP_EVENT_INVALID != buf) {
- *from = queue;
- return buf;
- }
- }
-
- *from = ODP_QUEUE_INVALID;
- return ODP_EVENT_INVALID;
-}
-
-/**
- * IPsec pre argument processing intialization
- */
-static
-void ipsec_init_pre(void)
-{
- odp_queue_param_t qparam;
- odp_pool_param_t params;
-
- /*
- * Create queues
- *
- * - completion queue (should eventually be ORDERED)
- * - sequence number queue (must be ATOMIC)
- */
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST;
- qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
-
- completionq = queue_create("completion", &qparam);
- if (ODP_QUEUE_INVALID == completionq) {
- EXAMPLE_ERR("Error: completion queue creation failed\n");
- exit(EXIT_FAILURE);
- }
-
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST;
- qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
-
- seqnumq = queue_create("seqnum", &qparam);
- if (ODP_QUEUE_INVALID == seqnumq) {
- EXAMPLE_ERR("Error: sequence number queue creation failed\n");
- exit(EXIT_FAILURE);
- }
-
- /* Create output buffer pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = SHM_OUT_POOL_BUF_SIZE;
- params.pkt.len = SHM_OUT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
- params.type = ODP_POOL_PACKET;
-
- out_pool = odp_pool_create("out_pool", &params);
-
- if (ODP_POOL_INVALID == out_pool) {
- EXAMPLE_ERR("Error: message pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Initialize our data bases */
- init_sp_db();
- init_sa_db();
- init_tun_db();
- init_ipsec_cache();
-}
-
-/**
- * IPsec post argument processing intialization
- *
- * Resolve SP DB with SA DB and create corresponding IPsec cache entries
- *
- * @param api_mode Mode to use when invoking per packet crypto API
- */
-static
-void ipsec_init_post(crypto_api_mode_e api_mode)
-{
- sp_db_entry_t *entry;
-
- /* Attempt to find appropriate SA for each SP */
- for (entry = sp_db->list; NULL != entry; entry = entry->next) {
- sa_db_entry_t *cipher_sa = NULL;
- sa_db_entry_t *auth_sa = NULL;
- tun_db_entry_t *tun = NULL;
-
- if (entry->esp) {
- cipher_sa = find_sa_db_entry(&entry->src_subnet,
- &entry->dst_subnet,
- 1);
- tun = find_tun_db_entry(cipher_sa->src_ip,
- cipher_sa->dst_ip);
- }
- if (entry->ah) {
- auth_sa = find_sa_db_entry(&entry->src_subnet,
- &entry->dst_subnet,
- 0);
- tun = find_tun_db_entry(auth_sa->src_ip,
- auth_sa->dst_ip);
- }
-
- if (cipher_sa || auth_sa) {
- if (create_ipsec_cache_entry(cipher_sa,
- auth_sa,
- tun,
- api_mode,
- entry->input,
- completionq,
- out_pool)) {
- EXAMPLE_ERR("Error: IPSec cache entry failed.\n"
- );
- exit(EXIT_FAILURE);
- }
- } else {
- printf(" WARNING: SA not found for SP\n");
- dump_sp_db_entry(entry);
- }
- }
-}
-
-/**
- * Initialize loopback
- *
- * Initialize ODP queues to create our own idea of loopbacks, which allow
- * testing without physical interfaces. Interface name string will be of
- * the format "loopX" where X is the decimal number of the interface.
- *
- * @param intf Loopback interface name string
- */
-#if 0 /* Temporarely disable loopback mode. Needs packet output event queues */
-static
-void initialize_loop(char *intf)
-{
- int idx;
- odp_queue_t outq_def;
- odp_queue_t inq_def;
- char queue_name[ODP_QUEUE_NAME_LEN];
- odp_queue_param_t qparam;
- uint8_t *mac;
- char mac_str[MAX_STRING];
-
- /* Derive loopback interface index */
- idx = loop_if_index(intf);
- if (idx < 0) {
- EXAMPLE_ERR("Error: loopback \"%s\" invalid\n", intf);
- exit(EXIT_FAILURE);
- }
-
- /* Create input queue */
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- snprintf(queue_name, sizeof(queue_name), "%i-loop_inq_def", idx);
- queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
-
- inq_def = queue_create(queue_name, &qparam);
- if (ODP_QUEUE_INVALID == inq_def) {
- EXAMPLE_ERR("Error: input queue creation failed for %s\n",
- intf);
- exit(EXIT_FAILURE);
- }
- /* Create output queue */
- snprintf(queue_name, sizeof(queue_name), "%i-loop_outq_def", idx);
- queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
-
- outq_def = queue_create(queue_name, NULL);
- if (ODP_QUEUE_INVALID == outq_def) {
- EXAMPLE_ERR("Error: output queue creation failed for %s\n",
- intf);
- exit(EXIT_FAILURE);
- }
-
- /* Initialize the loopback DB entry */
- create_loopback_db_entry(idx, inq_def, outq_def, pkt_pool);
- mac = query_loopback_db_mac(idx);
-
- printf("Created loop:%02i, queue mode (ATOMIC queues)\n"
- " default loop%02i-INPUT queue:%" PRIu64 "\n"
- " default loop%02i-OUTPUT queue:%" PRIu64 "\n"
- " source mac address %s\n",
- idx, idx, odp_queue_to_u64(inq_def), idx,
- odp_queue_to_u64(outq_def),
- mac_addr_str(mac_str, mac));
-
- /* Resolve any routes using this interface for output */
- resolve_fwd_db(intf, outq_def, mac);
-}
-#endif
-/**
- * Initialize interface
- *
- * Initialize ODP pktio and queues, query MAC address and update
- * forwarding database.
- *
- * @param intf Interface name string
- */
-static
-void initialize_intf(char *intf)
-{
- odp_pktio_t pktio;
- odp_pktout_queue_t pktout;
- odp_queue_t inq;
- int ret;
- uint8_t src_mac[ODPH_ETHADDR_LEN];
- char src_mac_str[MAX_STRING];
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t pktin_param;
-
- odp_pktio_param_init(&pktio_param);
-
- if (getenv("ODP_IPSEC_USE_POLL_QUEUES"))
- pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
- else
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- /*
- * Open a packet IO instance for thread and get default output queue
- */
- pktio = odp_pktio_open(intf, pkt_pool, &pktio_param);
- if (ODP_PKTIO_INVALID == pktio) {
- EXAMPLE_ERR("Error: pktio create failed for %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- odp_pktin_queue_param_init(&pktin_param);
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
-
- if (odp_pktin_queue_config(pktio, &pktin_param)) {
- EXAMPLE_ERR("Error: pktin config failed for %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- if (odp_pktout_queue_config(pktio, NULL)) {
- EXAMPLE_ERR("Error: pktout config failed for %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- if (odp_pktin_event_queue(pktio, &inq, 1) != 1) {
- EXAMPLE_ERR("Error: failed to get input queue for %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
- EXAMPLE_ERR("Error: failed to get pktout queue for %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- ret = odp_pktio_start(pktio);
- if (ret) {
- EXAMPLE_ERR("Error: unable to start %s\n", intf);
- exit(EXIT_FAILURE);
- }
-
- /* Read the source MAC address for this interface */
- ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
- if (ret <= 0) {
- EXAMPLE_ERR("Error: failed during MAC address get for %s\n",
- intf);
- exit(EXIT_FAILURE);
- }
-
- printf("Created pktio:%02" PRIu64 ", queue mode (ATOMIC queues)\n"
- " default pktio%02" PRIu64 "-INPUT queue:%" PRIu64 "\n"
- " source mac address %s\n",
- odp_pktio_to_u64(pktio), odp_pktio_to_u64(pktio),
- odp_queue_to_u64(inq),
- mac_addr_str(src_mac_str, src_mac));
-
- /* Resolve any routes using this interface for output */
- resolve_fwd_db(intf, pktout, src_mac);
-}
-
-/**
- * Packet Processing - Input verification
- *
- * @param pkt Packet to inspect
- * @param ctx Packet process context (not used)
- *
- * @return PKT_CONTINUE if good, supported packet else PKT_DROP
- */
-static
-pkt_disposition_e do_input_verify(odp_packet_t pkt,
- pkt_ctx_t *ctx EXAMPLE_UNUSED)
-{
- if (odp_unlikely(odp_packet_has_error(pkt)))
- return PKT_DROP;
-
- if (!odp_packet_has_eth(pkt))
- return PKT_DROP;
-
- if (!odp_packet_has_ipv4(pkt))
- return PKT_DROP;
-
- return PKT_CONTINUE;
-}
-
-/**
- * Packet Processing - Route lookup in forwarding database
- *
- * @param pkt Packet to route
- * @param ctx Packet process context
- *
- * @return PKT_CONTINUE if route found else PKT_DROP
- */
-static
-pkt_disposition_e do_route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
-{
- odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- fwd_db_entry_t *entry;
-
- entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
-
- if (entry) {
- odph_ethhdr_t *eth =
- (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
-
- memcpy(&eth->dst, entry->dst_mac, ODPH_ETHADDR_LEN);
- memcpy(&eth->src, entry->src_mac, ODPH_ETHADDR_LEN);
- ctx->pktout = entry->pktout;
-
- return PKT_CONTINUE;
- }
-
- return PKT_DROP;
-}
-
-/**
- * Packet Processing - Input IPsec packet classification
- *
- * Verify the received packet has IPsec headers and a match
- * in the IPsec cache, if so issue crypto request else skip
- * input crypto.
- *
- * @param pkt Packet to classify
- * @param ctx Packet process context
- * @param skip Pointer to return "skip" indication
- *
- * @return PKT_CONTINUE if done else PKT_POSTED
- */
-static
-pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt,
- pkt_ctx_t *ctx,
- odp_bool_t *skip,
- odp_crypto_op_result_t *result)
-{
- uint8_t *buf = odp_packet_data(pkt);
- odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- int hdr_len;
- odph_ahhdr_t *ah = NULL;
- odph_esphdr_t *esp = NULL;
- ipsec_cache_entry_t *entry;
- odp_crypto_op_param_t params;
- odp_bool_t posted = 0;
-
- /* Default to skip IPsec */
- *skip = TRUE;
-
- /* Check IP header for IPSec protocols and look it up */
- hdr_len = locate_ipsec_headers(ip, &ah, &esp);
- if (!ah && !esp)
- return PKT_CONTINUE;
- entry = find_ipsec_cache_entry_in(odp_be_to_cpu_32(ip->src_addr),
- odp_be_to_cpu_32(ip->dst_addr),
- ah,
- esp);
- if (!entry)
- return PKT_CONTINUE;
-
- /* Account for configured ESP IV length in packet */
- hdr_len += entry->esp.iv_len;
-
- /* Initialize parameters block */
- memset(&params, 0, sizeof(params));
- params.ctx = ctx;
- params.session = entry->state.session;
- params.pkt = pkt;
- params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID;
-
- /*Save everything to context */
- ctx->ipsec.ip_tos = ip->tos;
- ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
- ctx->ipsec.ip_ttl = ip->ttl;
- ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
- ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
- ctx->ipsec.hdr_len = hdr_len;
- ctx->ipsec.trl_len = 0;
- ctx->ipsec.src_ip = entry->src_ip;
- ctx->ipsec.dst_ip = entry->dst_ip;
-
- /*If authenticating, zero the mutable fields build the request */
- if (ah) {
- ip->chksum = 0;
- ip->tos = 0;
- ip->frag_offset = 0;
- ip->ttl = 0;
-
- params.auth_range.offset = ((uint8_t *)ip) - buf;
- params.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
- params.hash_result_offset = ah->icv - buf;
- }
-
- /* If deciphering build request */
- if (esp) {
- params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf;
- params.cipher_range.length = ipv4_data_len(ip) - hdr_len;
- params.override_iv_ptr = esp->iv;
- }
-
- /* Issue crypto request */
- *skip = FALSE;
- ctx->state = PKT_STATE_IPSEC_IN_FINISH;
- if (odp_crypto_operation(&params,
- &posted,
- result)) {
- abort();
- }
- return (posted) ? PKT_POSTED : PKT_CONTINUE;
-}
-
-/**
- * Packet Processing - Input IPsec packet processing cleanup
- *
- * @param pkt Packet to handle
- * @param ctx Packet process context
- *
- * @return PKT_CONTINUE if successful else PKT_DROP
- */
-static
-pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt,
- pkt_ctx_t *ctx,
- odp_crypto_op_result_t *result)
-{
- odph_ipv4hdr_t *ip;
- int hdr_len = ctx->ipsec.hdr_len;
- int trl_len = 0;
-
- /* Check crypto result */
- if (!result->ok) {
- if (!is_crypto_compl_status_ok(&result->cipher_status))
- return PKT_DROP;
- if (!is_crypto_compl_status_ok(&result->auth_status))
- return PKT_DROP;
- }
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
-
- /*
- * Finish auth
- */
- if (ctx->ipsec.ah_offset) {
- uint8_t *buf = odp_packet_data(pkt);
- odph_ahhdr_t *ah;
-
- ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
- ip->proto = ah->next_header;
- }
-
- /*
- * Finish cipher by finding ESP trailer and processing
- *
- * NOTE: ESP authentication ICV not supported
- */
- if (ctx->ipsec.esp_offset) {
- uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len);
- odph_esptrl_t *esp_t = (odph_esptrl_t *)(eop) - 1;
-
- ip->proto = esp_t->next_header;
- trl_len += esp_t->pad_len + sizeof(*esp_t);
- }
-
- /* We have a tunneled IPv4 packet */
- if (ip->proto == ODPH_IPV4) {
- odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len);
- odp_packet_pull_tail(pkt, trl_len);
- odph_ethhdr_t *eth;
-
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- eth->type = ODPH_ETHTYPE_IPV4;
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
-
- /* Check inbound policy */
- if ((ip->src_addr != ctx->ipsec.src_ip ||
- ip->dst_addr != ctx->ipsec.dst_ip))
- return PKT_DROP;
-
- return PKT_CONTINUE;
- }
-
- /* Finalize the IPv4 header */
- ipv4_adjust_len(ip, -(hdr_len + trl_len));
- ip->ttl = ctx->ipsec.ip_ttl;
- ip->tos = ctx->ipsec.ip_tos;
- ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
-
- /* Correct the packet length and move payload into position */
- memmove(ipv4_data_p(ip),
- ipv4_data_p(ip) + hdr_len,
- odp_be_to_cpu_16(ip->tot_len));
- odp_packet_pull_tail(pkt, hdr_len + trl_len);
-
- /* Fall through to next state */
- return PKT_CONTINUE;
-}
-
-/**
- * Packet Processing - Output IPsec packet classification
- *
- * Verify the outbound packet has a match in the IPsec cache,
- * if so issue prepend IPsec headers and prepare parameters
- * for crypto API call. Post the packet to ATOMIC queue so
- * that sequence numbers can be applied in packet order as
- * the next processing step.
- *
- * @param pkt Packet to classify
- * @param ctx Packet process context
- * @param skip Pointer to return "skip" indication
- *
- * @return PKT_CONTINUE if done else PKT_POSTED
- */
-static
-pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt,
- pkt_ctx_t *ctx,
- odp_bool_t *skip)
-{
- uint8_t *buf = odp_packet_data(pkt);
- odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- uint16_t ip_data_len = ipv4_data_len(ip);
- uint8_t *ip_data = ipv4_data_p(ip);
- ipsec_cache_entry_t *entry;
- odp_crypto_op_param_t params;
- int hdr_len = 0;
- int trl_len = 0;
- odph_ahhdr_t *ah = NULL;
- odph_esphdr_t *esp = NULL;
-
- /* Default to skip IPsec */
- *skip = TRUE;
-
- /* Find record */
- entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
- odp_be_to_cpu_32(ip->dst_addr),
- ip->proto);
- if (!entry)
- return PKT_CONTINUE;
-
- /* Save IPv4 stuff */
- ctx->ipsec.ip_tos = ip->tos;
- ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
- ctx->ipsec.ip_ttl = ip->ttl;
-
- /* Initialize parameters block */
- memset(&params, 0, sizeof(params));
- params.session = entry->state.session;
- params.ctx = ctx;
- params.pkt = pkt;
- params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID;
-
- if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
- hdr_len += sizeof(odph_ipv4hdr_t);
- ip_data = (uint8_t *)ip;
- ip_data_len += sizeof(odph_ipv4hdr_t);
- }
- /* Compute ah and esp, determine length of headers, move the data */
- if (entry->ah.alg) {
- ah = (odph_ahhdr_t *)(ip_data + hdr_len);
- hdr_len += sizeof(odph_ahhdr_t);
- hdr_len += entry->ah.icv_len;
- }
- if (entry->esp.alg) {
- esp = (odph_esphdr_t *)(ip_data + hdr_len);
- hdr_len += sizeof(odph_esphdr_t);
- hdr_len += entry->esp.iv_len;
- }
- memmove(ip_data + hdr_len, ip_data, ip_data_len);
- ip_data += hdr_len;
-
- /* update outer header in tunnel mode */
- if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
- /* tunnel addresses */
- ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip);
- ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip);
- }
-
- /* For cipher, compute encrypt length, build headers and request */
- if (esp) {
- uint32_t encrypt_len;
- odph_esptrl_t *esp_t;
-
- encrypt_len = ESP_ENCODE_LEN(ip_data_len +
- sizeof(*esp_t),
- entry->esp.block_len);
- trl_len = encrypt_len - ip_data_len;
-
- esp->spi = odp_cpu_to_be_32(entry->esp.spi);
- memcpy(esp + 1, entry->state.iv, entry->esp.iv_len);
-
- esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1;
- esp_t->pad_len = trl_len - sizeof(*esp_t);
- if (entry->mode == IPSEC_SA_MODE_TUNNEL)
- esp_t->next_header = ODPH_IPV4;
- else
- esp_t->next_header = ip->proto;
- ip->proto = ODPH_IPPROTO_ESP;
-
- params.cipher_range.offset = ip_data - buf;
- params.cipher_range.length = encrypt_len;
- }
-
- /* For authentication, build header clear mutables and build request */
- if (ah) {
- memset(ah, 0, sizeof(*ah) + entry->ah.icv_len);
- ah->spi = odp_cpu_to_be_32(entry->ah.spi);
- ah->ah_len = 1 + (entry->ah.icv_len / 4);
- if (entry->mode == IPSEC_SA_MODE_TUNNEL && !esp)
- ah->next_header = ODPH_IPV4;
- else
- ah->next_header = ip->proto;
- ip->proto = ODPH_IPPROTO_AH;
-
- ip->chksum = 0;
- ip->tos = 0;
- ip->frag_offset = 0;
- ip->ttl = 0;
-
- params.auth_range.offset = ((uint8_t *)ip) - buf;
- params.auth_range.length =
- odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len);
- params.hash_result_offset = ah->icv - buf;
- }
-
- /* Set IPv4 length before authentication */
- ipv4_adjust_len(ip, hdr_len + trl_len);
- if (!odp_packet_push_tail(pkt, hdr_len + trl_len))
- return PKT_DROP;
-
- /* Save remaining context */
- ctx->ipsec.hdr_len = hdr_len;
- ctx->ipsec.trl_len = trl_len;
- ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
- ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
- ctx->ipsec.tun_hdr_offset = (entry->mode == IPSEC_SA_MODE_TUNNEL) ?
- ((uint8_t *)ip - buf) : 0;
- ctx->ipsec.ah_seq = &entry->state.ah_seq;
- ctx->ipsec.esp_seq = &entry->state.esp_seq;
- ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id;
- memcpy(&ctx->ipsec.params, &params, sizeof(params));
-
- *skip = FALSE;
-
- return PKT_POSTED;
-}
-
-/**
- * Packet Processing - Output IPsec packet sequence number assignment
- *
- * Assign the necessary sequence numbers and then issue the crypto API call
- *
- * @param pkt Packet to handle
- * @param ctx Packet process context
- *
- * @return PKT_CONTINUE if done else PKT_POSTED
- */
-static
-pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt,
- pkt_ctx_t *ctx,
- odp_crypto_op_result_t *result)
-{
- uint8_t *buf = odp_packet_data(pkt);
- odp_bool_t posted = 0;
-
- /* We were dispatched from atomic queue, assign sequence numbers */
- if (ctx->ipsec.ah_offset) {
- odph_ahhdr_t *ah;
-
- ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
- ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++);
- }
- if (ctx->ipsec.esp_offset) {
- odph_esphdr_t *esp;
-
- esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf);
- esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++);
- }
- if (ctx->ipsec.tun_hdr_offset) {
- odph_ipv4hdr_t *ip;
- int ret;
-
- ip = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf);
- ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++);
- if (!ip->id) {
- /* re-init tunnel hdr id */
- ret = odp_random_data((uint8_t *)ctx->ipsec.tun_hdr_id,
- sizeof(*ctx->ipsec.tun_hdr_id),
- 1);
- if (ret != sizeof(*ctx->ipsec.tun_hdr_id))
- abort();
- }
- }
-
- /* Issue crypto request */
- if (odp_crypto_operation(&ctx->ipsec.params,
- &posted,
- result)) {
- abort();
- }
- return (posted) ? PKT_POSTED : PKT_CONTINUE;
-}
-
-/**
- * Packet Processing - Output IPsec packet processing cleanup
- *
- * @param pkt Packet to handle
- * @param ctx Packet process context
- *
- * @return PKT_CONTINUE if successful else PKT_DROP
- */
-static
-pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt,
- pkt_ctx_t *ctx,
- odp_crypto_op_result_t *result)
-{
- odph_ipv4hdr_t *ip;
-
- /* Check crypto result */
- if (!result->ok) {
- if (!is_crypto_compl_status_ok(&result->cipher_status))
- return PKT_DROP;
- if (!is_crypto_compl_status_ok(&result->auth_status))
- return PKT_DROP;
- }
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
-
- /* Finalize the IPv4 header */
- ip->ttl = ctx->ipsec.ip_ttl;
- ip->tos = ctx->ipsec.ip_tos;
- ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
-
- /* Fall through to next state */
- return PKT_CONTINUE;
-}
-
-/**
- * Packet IO worker thread
- *
- * Loop calling odp_schedule to obtain packets from one of three sources,
- * and continue processing the packet based on the state stored in its
- * per packet context.
- *
- * - Input interfaces (i.e. new work)
- * - Sequence number assignment queue
- * - Per packet crypto API completion queue
- *
- * @param arg Required by "odph_odpthreads_create", unused
- *
- * @return NULL (should never return)
- */
-static
-int pktio_thread(void *arg EXAMPLE_UNUSED)
-{
- int thr;
- odp_packet_t pkt;
- odp_event_t ev;
- unsigned long pkt_cnt = 0;
-
- thr = odp_thread_id();
-
- printf("Pktio thread [%02i] starts\n", thr);
-
- odp_barrier_wait(&sync_barrier);
-
- /* Loop packets */
- for (;;) {
- pkt_disposition_e rc;
- pkt_ctx_t *ctx;
- odp_queue_t dispatchq;
- odp_crypto_op_result_t result;
-
- /* Use schedule to get event from any input queue */
- ev = schedule(&dispatchq);
-
- /* Determine new work versus completion or sequence number */
- if (ODP_EVENT_PACKET == odp_event_type(ev)) {
- pkt = odp_packet_from_event(ev);
- if (seqnumq == dispatchq) {
- ctx = get_pkt_ctx_from_pkt(pkt);
- } else {
- ctx = alloc_pkt_ctx(pkt);
- if (!ctx) {
- odp_packet_free(pkt);
- continue;
- }
- ctx->state = PKT_STATE_INPUT_VERIFY;
- }
- } else if (ODP_EVENT_CRYPTO_COMPL == odp_event_type(ev)) {
- odp_crypto_compl_t compl;
-
- compl = odp_crypto_compl_from_event(ev);
- odp_crypto_compl_result(compl, &result);
- odp_crypto_compl_free(compl);
- pkt = result.pkt;
- ctx = result.ctx;
- } else {
- abort();
- }
-
- /*
- * We now have a packet and its associated context. Loop here
- * executing processing based on the current state value stored
- * in the context as long as the processing return code
- * indicates PKT_CONTINUE.
- *
- * For other return codes:
- *
- * o PKT_DONE - finished with the packet
- * o PKT_DROP - something incorrect about the packet, drop it
- * o PKT_POSTED - packet/event has been queued for later
- */
- do {
- odp_bool_t skip = FALSE;
-
- switch (ctx->state) {
- case PKT_STATE_INPUT_VERIFY:
-
- rc = do_input_verify(pkt, ctx);
- ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
- break;
-
- case PKT_STATE_IPSEC_IN_CLASSIFY:
-
- ctx->state = PKT_STATE_ROUTE_LOOKUP;
- rc = do_ipsec_in_classify(pkt,
- ctx,
- &skip,
- &result);
- break;
-
- case PKT_STATE_IPSEC_IN_FINISH:
-
- rc = do_ipsec_in_finish(pkt, ctx, &result);
- ctx->state = PKT_STATE_ROUTE_LOOKUP;
- break;
-
- case PKT_STATE_ROUTE_LOOKUP:
-
- rc = do_route_fwd_db(pkt, ctx);
- ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
- break;
-
- case PKT_STATE_IPSEC_OUT_CLASSIFY:
-
- rc = do_ipsec_out_classify(pkt,
- ctx,
- &skip);
- if (odp_unlikely(skip)) {
- ctx->state = PKT_STATE_TRANSMIT;
- } else {
- ctx->state = PKT_STATE_IPSEC_OUT_SEQ;
- if (odp_queue_enq(seqnumq, ev))
- rc = PKT_DROP;
- }
- break;
-
- case PKT_STATE_IPSEC_OUT_SEQ:
-
- ctx->state = PKT_STATE_IPSEC_OUT_FINISH;
- rc = do_ipsec_out_seq(pkt, ctx, &result);
- break;
-
- case PKT_STATE_IPSEC_OUT_FINISH:
-
- rc = do_ipsec_out_finish(pkt, ctx, &result);
- ctx->state = PKT_STATE_TRANSMIT;
- break;
-
- case PKT_STATE_TRANSMIT:
-
- if (odp_pktout_send(ctx->pktout, &pkt, 1) < 1) {
- rc = PKT_DROP;
- } else {
- rc = PKT_DONE;
- }
- break;
-
- default:
- rc = PKT_DROP;
- break;
- }
- } while (PKT_CONTINUE == rc);
-
- /* Free context on drop or transmit */
- if ((PKT_DROP == rc) || (PKT_DONE == rc))
- free_pkt_ctx(ctx);
-
-
- /* Check for drop */
- if (PKT_DROP == rc)
- odp_packet_free(pkt);
-
- /* Print packet counts every once in a while */
- if (PKT_DONE == rc) {
- if (odp_unlikely(pkt_cnt++ % 1000 == 0)) {
- printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
- fflush(NULL);
- }
- }
- }
-
- /* unreachable */
- return 0;
-}
-
-/**
- * ODP ipsec example main function
- */
-int
-main(int argc, char *argv[])
-{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- int num_workers;
- int i;
- int stream_count;
- odp_shm_t shm;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_pool_param_t params;
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
-
- /* create by default scheduled queues */
- queue_create = odp_queue_create;
- schedule = odp_schedule_cb;
-
- /* check for using poll queues */
- if (getenv("ODP_IPSEC_USE_POLL_QUEUES")) {
- queue_create = polled_odp_queue_create;
- schedule = polled_odp_schedule_cb;
- }
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Reserve memory for args from shared mem */
- shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE,
- 0);
-
- args = odp_shm_addr(shm);
-
- if (NULL == args) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(args, 0, sizeof(*args));
-
- /* Must init our databases before parsing args */
- ipsec_init_pre();
- init_fwd_db();
- init_loopback_db();
- init_stream_db();
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &args->appl);
-
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]), &args->appl);
-
- /* Default to system CPU count unless user specified */
- num_workers = MAX_WORKERS;
- if (args->appl.cpu_count)
- num_workers = args->appl.cpu_count;
-
- /* Get default worker cpumask */
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- printf("num worker threads: %i\n", num_workers);
- printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n", cpumaskstr);
-
- /* Create a barrier to synchronize thread startup */
- odp_barrier_init(&sync_barrier, num_workers);
-
- /* Create packet buffer pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
- params.type = ODP_POOL_PACKET;
-
- pkt_pool = odp_pool_create("packet_pool", &params);
-
- if (ODP_POOL_INVALID == pkt_pool) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Create context buffer pool */
- params.buf.size = SHM_CTX_POOL_BUF_SIZE;
- params.buf.align = 0;
- params.buf.num = SHM_CTX_POOL_BUF_COUNT;
- params.type = ODP_POOL_BUFFER;
-
- ctx_pool = odp_pool_create("ctx_pool", &params);
-
- if (ODP_POOL_INVALID == ctx_pool) {
- EXAMPLE_ERR("Error: context pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Populate our IPsec cache */
- printf("Using %s mode for crypto API\n\n",
- (CRYPTO_API_SYNC == args->appl.mode) ? "SYNC" :
- (CRYPTO_API_ASYNC_IN_PLACE == args->appl.mode) ?
- "ASYNC_IN_PLACE" : "ASYNC_NEW_BUFFER");
- ipsec_init_post(args->appl.mode);
-
- /* Initialize interfaces (which resolves FWD DB entries */
- for (i = 0; i < args->appl.if_count; i++) {
-#if 0 /* Temporarely disable loopback mode. Needs packet output event queues */
- if (!strncmp("loop", args->appl.if_names[i], strlen("loop")))
- initialize_loop(args->appl.if_names[i]);
- else
-#endif
- initialize_intf(args->appl.if_names[i]);
- }
-
- /* If we have test streams build them before starting workers */
- resolve_stream_db();
- stream_count = create_stream_db_inputs();
-
- /*
- * Create and init worker threads
- */
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = pktio_thread;
- thr_params.arg = NULL;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-
- /*
- * If there are streams attempt to verify them else
- * wait indefinitely
- */
- if (stream_count) {
- odp_bool_t done;
- do {
- done = verify_stream_db_outputs();
- sleep(1);
- } while (!done);
- printf("All received\n");
- } else {
- odph_odpthreads_join(thread_tbl);
- }
-
- free(args->appl.if_names);
- free(args->appl.if_str);
-
- shm = odp_shm_lookup("shm_args");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_args failed\n");
- shm = odp_shm_lookup("shm_ipsec_cache");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_ipsec_cache failed\n");
- shm = odp_shm_lookup("shm_fwd_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_fwd_db failed\n");
- shm = odp_shm_lookup("loopback_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free loopback_db failed\n");
- shm = odp_shm_lookup("shm_sa_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_sa_db failed\n");
- shm = odp_shm_lookup("shm_tun_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_tun_db failed\n");
- shm = odp_shm_lookup("shm_sp_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free shm_sp_db failed\n");
- shm = odp_shm_lookup("stream_db");
- if (odp_shm_free(shm) != 0)
- EXAMPLE_ERR("Error: shm free stream_db failed\n");
-
- printf("Exit\n\n");
-
- return 0;
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
- int opt;
- int long_index;
- char *token;
- size_t len;
- int rc = 0;
- int i;
-
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"interface", required_argument, NULL, 'i'}, /* return 'i' */
- {"mode", required_argument, NULL, 'm'}, /* return 'm' */
- {"route", required_argument, NULL, 'r'}, /* return 'r' */
- {"policy", required_argument, NULL, 'p'}, /* return 'p' */
- {"ah", required_argument, NULL, 'a'}, /* return 'a' */
- {"esp", required_argument, NULL, 'e'}, /* return 'e' */
- {"tunnel", required_argument, NULL, 't'}, /* return 't' */
- {"stream", required_argument, NULL, 's'}, /* return 's' */
- {"help", no_argument, NULL, 'h'}, /* return 'h' */
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:i:m:h:r:p:a:e:t:s:";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- printf("\nParsing command line options\n");
-
- appl_args->mode = 0; /* turn off async crypto API by default */
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (!rc) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (-1 == opt)
- break; /* No more options */
-
- switch (opt) {
- case 'c':
- appl_args->cpu_count = atoi(optarg);
- break;
- /* parse packet-io interface names */
- case 'i':
- len = strlen(optarg);
- if (0 == len) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- appl_args->if_str = malloc(len);
- if (appl_args->if_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* count the number of tokens separated by ',' */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL;
- token = strtok(NULL, ","), i++)
- ;
-
- appl_args->if_count = i;
-
- if (0 == appl_args->if_count) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* allocate storage for the if names */
- appl_args->if_names =
- calloc(appl_args->if_count, sizeof(char *));
-
- /* store the if names (reset names string) */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- appl_args->if_names[i] = token;
- }
- break;
-
- case 'm':
- appl_args->mode = atoi(optarg);
- break;
-
- case 'r':
- rc = create_fwd_db_entry(optarg, appl_args->if_names,
- appl_args->if_count);
- break;
-
- case 'p':
- rc = create_sp_db_entry(optarg);
- break;
-
- case 'a':
- rc = create_sa_db_entry(optarg, FALSE);
- break;
-
- case 'e':
- rc = create_sa_db_entry(optarg, TRUE);
- break;
-
- case 't':
- rc = create_tun_db_entry(optarg);
- break;
-
- case 's':
- rc = create_stream_db_entry(optarg);
- break;
-
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
-
- default:
- break;
- }
- }
-
- if (rc) {
- printf("ERROR: failed parsing -%c option\n", opt);
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- if (0 == appl_args->if_count) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args)
-{
- int i;
-
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %"PRIu64"\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n"
- "IF-count: %i\n"
- "Using IFs: ",
- progname, appl_args->if_count);
- for (i = 0; i < appl_args->if_count; ++i)
- printf(" %s", appl_args->if_names[i]);
-
- printf("\n");
-
- dump_fwd_db();
- dump_sp_db();
- dump_sa_db();
- dump_tun_db();
- printf("\n\n");
- fflush(NULL);
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s -i eth1,eth2,eth3 -m 0\n"
- "\n"
- "OpenDataPlane example application.\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
- " -m, --mode 0: SYNC\n"
- " 1: ASYNC_IN_PLACE\n"
- " 2: ASYNC_NEW_BUFFER\n"
- " Default: 0: SYNC api mode\n"
- "\n"
- "Routing / IPSec OPTIONS:\n"
- " -r, --route SubNet:Intf:NextHopMAC\n"
- " -p, --policy SrcSubNet:DstSubNet:(in|out):(ah|esp|both)\n"
- " -e, --esp SrcIP:DstIP:(3des|null):SPI:Key192\n"
- " -a, --ah SrcIP:DstIP:(sha256|md5|null):SPI:Key(256|128)\n"
- "\n"
- " Where: NextHopMAC is raw hex/dot notation, i.e. 03.BA.44.9A.CE.02\n"
- " IP is decimal/dot notation, i.e. 192.168.1.1\n"
- " SubNet is decimal/dot/slash notation, i.e 192.168.0.0/16\n"
- " SPI is raw hex, 32 bits\n"
- " KeyXXX is raw hex, XXX bits long\n"
- "\n"
- " Examples:\n"
- " -r 192.168.222.0/24:p8p1:08.00.27.F5.8B.DB\n"
- " -p 192.168.111.0/24:192.168.222.0/24:out:esp\n"
- " -e 192.168.111.2:192.168.222.2:3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
- " -a 192.168.111.2:192.168.222.2:md5:201:a731649644c5dee92cbd9c2e7e188ee6\n"
- "\n"
- "Optional OPTIONS\n"
- " -c, --count <number> CPU count.\n"
- " -h, --help Display help and exit.\n"
- " environment variables: ODP_IPSEC_USE_POLL_QUEUES\n"
- " to enable use of poll queues instead of scheduled (default)\n"
- " ODP_IPSEC_STREAM_VERIFY_MDEQ\n"
- " to enable use of multiple dequeue for queue draining during\n"
- " stream verification instead of single dequeue (default)\n"
- "\n", NO_PATH(progname), NO_PATH(progname)
- );
-}
diff --git a/example/ipsec/odp_ipsec_cache.c b/example/ipsec/odp_ipsec_cache.c
deleted file mode 100644
index dba0ea0ab..000000000
--- a/example/ipsec/odp_ipsec_cache.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp/helper/ipsec.h>
-
-#include <odp_ipsec_cache.h>
-
-/** Global pointer to ipsec_cache db */
-ipsec_cache_t *ipsec_cache;
-
-void init_ipsec_cache(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("shm_ipsec_cache",
- sizeof(ipsec_cache_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- ipsec_cache = odp_shm_addr(shm);
-
- if (ipsec_cache == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(ipsec_cache, 0, sizeof(*ipsec_cache));
-}
-
-int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
- sa_db_entry_t *auth_sa,
- tun_db_entry_t *tun,
- crypto_api_mode_e api_mode,
- odp_bool_t in,
- odp_queue_t completionq,
- odp_pool_t out_pool)
-{
- odp_crypto_session_param_t params;
- ipsec_cache_entry_t *entry;
- odp_crypto_ses_create_err_t ses_create_rc;
- odp_crypto_session_t session;
- sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT;
-
- /* Verify we have a good entry */
- entry = &ipsec_cache->array[ipsec_cache->index];
- if (MAX_DB <= ipsec_cache->index)
- return -1;
-
- /* Verify SA mode match in case of cipher&auth */
- if (cipher_sa && auth_sa &&
- (cipher_sa->mode != auth_sa->mode))
- return -1;
-
- odp_crypto_session_param_init(&params);
-
- /* Setup parameters and call crypto library to create session */
- params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE;
- params.auth_cipher_text = TRUE;
- if (CRYPTO_API_SYNC == api_mode) {
- params.pref_mode = ODP_CRYPTO_SYNC;
- params.compl_queue = ODP_QUEUE_INVALID;
- params.output_pool = ODP_POOL_INVALID;
- } else {
- params.pref_mode = ODP_CRYPTO_ASYNC;
- params.compl_queue = completionq;
- params.output_pool = out_pool;
- }
-
- if (CRYPTO_API_ASYNC_NEW_BUFFER == api_mode)
- entry->in_place = FALSE;
- else
- entry->in_place = TRUE;
-
-
- /* Cipher */
- if (cipher_sa) {
- params.cipher_alg = cipher_sa->alg.u.cipher;
- params.cipher_key.data = cipher_sa->key.data;
- params.cipher_key.length = cipher_sa->key.length;
- params.iv.data = entry->state.iv;
- params.iv.length = cipher_sa->iv_len;
- mode = cipher_sa->mode;
- } else {
- params.cipher_alg = ODP_CIPHER_ALG_NULL;
- params.iv.data = NULL;
- params.iv.length = 0;
- }
-
- /* Auth */
- if (auth_sa) {
- params.auth_alg = auth_sa->alg.u.auth;
- params.auth_key.data = auth_sa->key.data;
- params.auth_key.length = auth_sa->key.length;
- params.auth_digest_len = auth_sa->icv_len;
- mode = auth_sa->mode;
- } else {
- params.auth_alg = ODP_AUTH_ALG_NULL;
- }
-
- /* Generate an IV */
- if (params.iv.length) {
- int32_t size = params.iv.length;
-
- int32_t ret = odp_random_data(params.iv.data, size, 1);
- if (ret != size)
- return -1;
- }
-
- /* Synchronous session create for now */
- if (odp_crypto_session_create(&params, &session, &ses_create_rc))
- return -1;
- if (ODP_CRYPTO_SES_CREATE_ERR_NONE != ses_create_rc)
- return -1;
-
- /* Copy remainder */
- if (cipher_sa) {
- entry->src_ip = cipher_sa->src_ip;
- entry->dst_ip = cipher_sa->dst_ip;
- entry->esp.alg = cipher_sa->alg.u.cipher;
- entry->esp.spi = cipher_sa->spi;
- entry->esp.block_len = cipher_sa->block_len;
- entry->esp.iv_len = cipher_sa->iv_len;
- memcpy(&entry->esp.key, &cipher_sa->key, sizeof(ipsec_key_t));
- }
- if (auth_sa) {
- entry->src_ip = auth_sa->src_ip;
- entry->dst_ip = auth_sa->dst_ip;
- entry->ah.alg = auth_sa->alg.u.auth;
- entry->ah.spi = auth_sa->spi;
- entry->ah.icv_len = auth_sa->icv_len;
- memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t));
- }
-
- if (tun) {
- entry->tun_src_ip = tun->tun_src_ip;
- entry->tun_dst_ip = tun->tun_dst_ip;
- mode = IPSEC_SA_MODE_TUNNEL;
-
- int ret;
-
- if (!in) {
- /* init tun hdr id */
- ret = odp_random_data((uint8_t *)
- &entry->state.tun_hdr_id,
- sizeof(entry->state.tun_hdr_id),
- 1);
- if (ret != sizeof(entry->state.tun_hdr_id))
- return -1;
- }
- }
- entry->mode = mode;
-
- /* Initialize state */
- entry->state.esp_seq = 0;
- entry->state.ah_seq = 0;
- entry->state.session = session;
-
- /* Add entry to the appropriate list */
- ipsec_cache->index++;
- if (in) {
- entry->next = ipsec_cache->in_list;
- ipsec_cache->in_list = entry;
- } else {
- entry->next = ipsec_cache->out_list;
- ipsec_cache->out_list = entry;
- }
-
- return 0;
-}
-
-ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
- uint32_t dst_ip,
- odph_ahhdr_t *ah,
- odph_esphdr_t *esp)
-{
- ipsec_cache_entry_t *entry = ipsec_cache->in_list;
-
- /* Look for a hit */
- for (; NULL != entry; entry = entry->next) {
- if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip))
- if ((entry->tun_src_ip != src_ip) ||
- (entry->tun_dst_ip != dst_ip))
- continue;
- if (ah &&
- ((!entry->ah.alg) ||
- (entry->ah.spi != odp_be_to_cpu_32(ah->spi))))
- continue;
- if (esp &&
- ((!entry->esp.alg) ||
- (entry->esp.spi != odp_be_to_cpu_32(esp->spi))))
- continue;
- break;
- }
-
- return entry;
-}
-
-ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
- uint32_t dst_ip,
- uint8_t proto EXAMPLE_UNUSED)
-{
- ipsec_cache_entry_t *entry = ipsec_cache->out_list;
-
- /* Look for a hit */
- for (; NULL != entry; entry = entry->next) {
- if ((entry->src_ip == src_ip) && (entry->dst_ip == dst_ip))
- break;
- }
- return entry;
-}
diff --git a/example/ipsec/odp_ipsec_cache.h b/example/ipsec/odp_ipsec_cache.h
deleted file mode 100644
index 7a4b95cd4..000000000
--- a/example/ipsec/odp_ipsec_cache.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_CACHE_H_
-#define ODP_IPSEC_CACHE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_api.h>
-#include <odp/helper/ipsec.h>
-
-#include <odp_ipsec_misc.h>
-#include <odp_ipsec_sa_db.h>
-
-/**
- * Mode specified on command line indicating how to exercise API
- */
-typedef enum {
- CRYPTO_API_SYNC, /**< Synchronous mode */
- CRYPTO_API_ASYNC_IN_PLACE, /**< Asynchronous in place */
- CRYPTO_API_ASYNC_NEW_BUFFER /**< Asynchronous new buffer */
-} crypto_api_mode_e;
-
-/**
- * IPsec cache data base entry
- */
-typedef struct ipsec_cache_entry_s {
- struct ipsec_cache_entry_s *next; /**< Next entry on list */
- odp_bool_t in_place; /**< Crypto API mode */
- uint32_t src_ip; /**< Source v4 address */
- uint32_t dst_ip; /**< Destination v4 address */
- sa_mode_t mode; /**< SA mode - transport/tun */
- uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */
- uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */
- struct {
- odp_cipher_alg_t alg; /**< Cipher algorithm */
- uint32_t spi; /**< Cipher SPI */
- uint32_t block_len; /**< Cipher block length */
- uint32_t iv_len; /**< Cipher IV length */
- ipsec_key_t key; /**< Cipher key */
- } esp;
- struct {
- odp_auth_alg_t alg; /**< Auth algorithm */
- uint32_t spi; /**< Auth SPI */
- uint32_t icv_len; /**< Auth ICV length */
- ipsec_key_t key; /**< Auth key */
- } ah;
-
- /* Per SA state */
- struct {
- odp_crypto_session_t session; /**< Crypto session handle */
- uint32_t esp_seq; /**< ESP TX sequence number */
- uint32_t ah_seq; /**< AH TX sequence number */
- uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */
- odp_u16be_t tun_hdr_id; /**< Tunnel header IP ID */
- } state;
-} ipsec_cache_entry_t;
-
-/**
- * IPsec cache data base global structure
- */
-typedef struct ipsec_cache_s {
- uint32_t index; /**< Index of next available entry */
- ipsec_cache_entry_t *in_list; /**< List of active input entries */
- ipsec_cache_entry_t *out_list; /**< List of active output entries */
- ipsec_cache_entry_t array[MAX_DB]; /**< Entry storage */
-} ipsec_cache_t;
-
-/** Global pointer to ipsec_cache db */
-extern ipsec_cache_t *ipsec_cache;
-
-/** Initialize IPsec cache */
-void init_ipsec_cache(void);
-
-/**
- * Create an entry in the IPsec cache
- *
- * @param cipher_sa Cipher SA DB entry pointer
- * @param auth_sa Auth SA DB entry pointer
- * @param tun Tunnel DB entry pointer
- * @param api_mode Crypto API mode for testing
- * @param in Direction (input versus output)
- * @param completionq Completion queue
- * @param out_pool Output buffer pool
- *
- * @return 0 if successful else -1
- */
-int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
- sa_db_entry_t *auth_sa,
- tun_db_entry_t *tun,
- crypto_api_mode_e api_mode,
- odp_bool_t in,
- odp_queue_t completionq,
- odp_pool_t out_pool);
-
-/**
- * Find a matching IPsec cache entry for input packet
- *
- * @param src_ip Source IPv4 address
- * @param dst_ip Destination IPv4 address
- * @param ah Pointer to AH header in packet else NULL
- * @param esp Pointer to ESP header in packet else NULL
- *
- * @return pointer to IPsec cache entry else NULL
- */
-ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
- uint32_t dst_ip,
- odph_ahhdr_t *ah,
- odph_esphdr_t *esp);
-
-/**
- * Find a matching IPsec cache entry for output packet
- *
- * @param src_ip Source IPv4 address
- * @param dst_ip Destination IPv4 address
- * @param proto IPv4 protocol (currently all protocols match)
- *
- * @return pointer to IPsec cache entry else NULL
- */
-ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
- uint32_t dst_ip,
- uint8_t proto);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_fwd_db.c b/example/ipsec/odp_ipsec_fwd_db.c
deleted file mode 100644
index e1f638461..000000000
--- a/example/ipsec/odp_ipsec_fwd_db.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp_ipsec_fwd_db.h>
-
-/** Global pointer to fwd db */
-fwd_db_t *fwd_db;
-
-void init_fwd_db(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("shm_fwd_db",
- sizeof(fwd_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- fwd_db = odp_shm_addr(shm);
-
- if (fwd_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(fwd_db, 0, sizeof(*fwd_db));
-}
-
-int create_fwd_db_entry(char *input, char **if_names, int if_count)
-{
- int pos = 0, i, match = 0;
- char *local;
- char *str;
- char *save;
- char *token;
- fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];
-
- /* Verify we haven't run out of space */
- if (MAX_DB <= fwd_db->index)
- return -1;
-
- /* Make a local copy */
- local = malloc(strlen(input) + 1);
- if (NULL == local)
- return -1;
- strcpy(local, input);
-
- /* Setup for using "strtok_r" to search input string */
- str = local;
- save = NULL;
-
- /* Parse tokens separated by ':' */
- while (NULL != (token = strtok_r(str, ":", &save))) {
- str = NULL; /* reset str for subsequent strtok_r calls */
-
- /* Parse token based on its position */
- switch (pos) {
- case 0:
- parse_ipv4_string(token,
- &entry->subnet.addr,
- &entry->subnet.mask);
- break;
- case 1:
- strncpy(entry->oif, token, OIF_LEN - 1);
- entry->oif[OIF_LEN - 1] = 0;
- for (i = 0; i < if_count; i++) {
- if (!strcmp(if_names[i], entry->oif)) {
- match = 1;
- break;
- }
- }
- if (!match) {
- printf("ERROR: interface name not correct for route\n");
- free(local);
- return -1;
- }
- break;
- case 2:
- parse_mac_string(token, entry->dst_mac);
- break;
- default:
- printf("ERROR: extra token \"%s\" at position %d\n",
- token, pos);
- break;
- }
-
- /* Advance to next position */
- pos++;
- }
-
- /* Verify we parsed exactly the number of tokens we expected */
- if (3 != pos) {
- printf("ERROR: \"%s\" contains %d tokens, expected 3\n",
- input,
- pos);
- free(local);
- return -1;
- }
-
- /* Add route to the list */
- fwd_db->index++;
- entry->next = fwd_db->list;
- fwd_db->list = entry;
-
- free(local);
- return 0;
-}
-
-void resolve_fwd_db(char *intf, odp_pktout_queue_t pktout, uint8_t *mac)
-{
- fwd_db_entry_t *entry;
-
- /* Walk the list and attempt to set output queue and MAC */
- for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
- if (strcmp(intf, entry->oif))
- continue;
-
- entry->pktout = pktout;
- memcpy(entry->src_mac, mac, ODPH_ETHADDR_LEN);
- }
-}
-
-void dump_fwd_db_entry(fwd_db_entry_t *entry)
-{
- char subnet_str[MAX_STRING];
- char mac_str[MAX_STRING];
-
- printf(" %s %s %s\n",
- ipv4_subnet_str(subnet_str, &entry->subnet),
- entry->oif,
- mac_addr_str(mac_str, entry->dst_mac));
-}
-
-void dump_fwd_db(void)
-{
- fwd_db_entry_t *entry;
-
- printf("\n"
- "Routing table\n"
- "-------------\n");
-
- for (entry = fwd_db->list; NULL != entry; entry = entry->next)
- dump_fwd_db_entry(entry);
-}
-
-fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip)
-{
- fwd_db_entry_t *entry;
-
- for (entry = fwd_db->list; NULL != entry; entry = entry->next)
- if (entry->subnet.addr == (dst_ip & entry->subnet.mask))
- break;
- return entry;
-}
diff --git a/example/ipsec/odp_ipsec_fwd_db.h b/example/ipsec/odp_ipsec_fwd_db.h
deleted file mode 100644
index 152bdfbde..000000000
--- a/example/ipsec/odp_ipsec_fwd_db.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_FWD_DB_H_
-#define ODP_IPSEC_FWD_DB_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_api.h>
-#include <odp_ipsec_misc.h>
-
-#define OIF_LEN 32
-
-/**
- * Forwarding data base entry
- */
-typedef struct fwd_db_entry_s {
- struct fwd_db_entry_s *next; /**< Next entry on list */
- char oif[OIF_LEN]; /**< Output interface name */
- odp_pktout_queue_t pktout; /**< Output transmit queue */
- uint8_t src_mac[ODPH_ETHADDR_LEN]; /**< Output source MAC */
- uint8_t dst_mac[ODPH_ETHADDR_LEN]; /**< Output destination MAC */
- ip_addr_range_t subnet; /**< Subnet for this router */
-} fwd_db_entry_t;
-
-/**
- * Forwarding data base global structure
- */
-typedef struct fwd_db_s {
- uint32_t index; /**< Next available entry */
- fwd_db_entry_t *list; /**< List of active routes */
- fwd_db_entry_t array[MAX_DB]; /**< Entry storage */
-} fwd_db_t;
-
-/** Global pointer to fwd db */
-extern fwd_db_t *fwd_db;
-
-/** Initialize FWD DB */
-void init_fwd_db(void);
-
-/**
- * Create a forwarding database entry
- *
- * String is of the format "SubNet:Intf:NextHopMAC"
- *
- * @param input Pointer to string describing route
- *
- * @param if_names Array of Name of the interfaces available
- *
- * @param if_count number of interfaces in if_names array
- *
- * @return 0 if successful else -1
- */
-int create_fwd_db_entry(char *input, char **if_names, int if_count);
-
-/**
- * Scan FWD DB entries and resolve output queue and source MAC address
- *
- * @param intf Interface name string
- * @param outq Output queue for packet transmit
- * @param mac MAC address of this interface
- */
-void resolve_fwd_db(char *intf, odp_pktout_queue_t pktout, uint8_t *mac);
-
-/**
- * Display one fowarding database entry
- *
- * @param entry Pointer to entry to display
- */
-void dump_fwd_db_entry(fwd_db_entry_t *entry);
-
-/**
- * Display the forwarding database
- */
-void dump_fwd_db(void);
-
-/**
- * Find a matching forwarding database entry
- *
- * @param dst_ip Destination IPv4 address
- *
- * @return pointer to forwarding DB entry else NULL
- */
-fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_loop_db.c b/example/ipsec/odp_ipsec_loop_db.c
deleted file mode 100644
index 1d5e404ee..000000000
--- a/example/ipsec/odp_ipsec_loop_db.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp_ipsec_loop_db.h>
-
-loopback_db_t *loopback_db;
-
-void init_loopback_db(void)
-{
- int idx;
- odp_shm_t shm;
-
- shm = odp_shm_reserve("loopback_db",
- sizeof(loopback_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- loopback_db = odp_shm_addr(shm);
-
- if (loopback_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(loopback_db, 0, sizeof(*loopback_db));
-
- for (idx = 0; idx < MAX_LOOPBACK; idx++) {
- loopback_db->intf[idx].inq_def = ODP_QUEUE_INVALID;
- loopback_db->intf[idx].outq_def = ODP_QUEUE_INVALID;
- }
-}
-
-void create_loopback_db_entry(int idx,
- odp_queue_t inq_def,
- odp_queue_t outq_def,
- odp_pool_t pkt_pool)
-{
- loopback_db_entry_t *entry = &loopback_db->intf[idx];
-
- /* Save queues */
- entry->inq_def = inq_def;
- entry->outq_def = outq_def;
- entry->pkt_pool = pkt_pool;
-
- /* Create dummy MAC address */
- memset(entry->mac, (0xF0 | idx), sizeof(entry->mac));
-}
diff --git a/example/ipsec/odp_ipsec_loop_db.h b/example/ipsec/odp_ipsec_loop_db.h
deleted file mode 100644
index 1f9ade6d3..000000000
--- a/example/ipsec/odp_ipsec_loop_db.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_LOOP_DB_H_
-#define ODP_IPSEC_LOOP_DB_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_api.h>
-#include <odp_ipsec_misc.h>
-
-/**
- * Loopback database entry structure
- */
-typedef struct loopback_db_entry_s {
- odp_queue_t inq_def;
- odp_queue_t outq_def;
- odp_pool_t pkt_pool;
- uint8_t mac[ODPH_ETHADDR_LEN];
-} loopback_db_entry_t;
-
-typedef struct loopback_db_s {
- loopback_db_entry_t intf[MAX_LOOPBACK];
-} loopback_db_t;
-
-extern loopback_db_t *loopback_db;
-
-/** Initialize loopback database global control structure */
-void init_loopback_db(void);
-
-/**
- * Create loopback DB entry for an interface
- *
- * Loopback interfaces are specified from command line with
- * an index 0-9.
- *
- * @param idx Index of interface in database
- * @param inq_def Input queue
- * @param outq_def Output queue
- * @param pkt_pool Pool to create packets from
- */
-void create_loopback_db_entry(int idx,
- odp_queue_t inq_def,
- odp_queue_t outq_def,
- odp_pool_t pkt_pool);
-
-/**
- * Parse loop interface index
- *
- * @param b Pointer to buffer to parse
- *
- * @return interface index (0 to (MAX_LOOPBACK - 1)) else -1
- */
-static inline
-int loop_if_index(char *b)
-{
- int ret;
- int idx;
-
- /* Derive loopback interface index */
- ret = sscanf(b, "loop%d", &idx);
- if ((1 != ret) || (idx < 0) || (idx >= MAX_LOOPBACK))
- return -1;
- return idx;
-}
-
-/**
- * Query loopback DB entry MAC address
- *
- * @param idx Loopback DB index of the interface
- *
- * @return MAC address pointer
- */
-static inline
-uint8_t *query_loopback_db_mac(int idx)
-{
- return loopback_db->intf[idx].mac;
-}
-
-/**
- * Query loopback DB entry input queue
- *
- * @param idx Loopback DB index of the interface
- *
- * @return ODP queue
- */
-static inline
-odp_queue_t query_loopback_db_inq(int idx)
-{
- return loopback_db->intf[idx].inq_def;
-}
-
-/**
- * Query loopback DB entry output queue
- *
- * @param idx Loopback DB index of the interface
- *
- * @return ODP queue
- */
-static inline
-odp_queue_t query_loopback_db_outq(int idx)
-{
- return loopback_db->intf[idx].outq_def;
-}
-
-/**
- * Query loopback DB entry packet pool
- *
- * @param idx Loopback DB index of the interface
- *
- * @return ODP buffer pool
- */
-static inline
-odp_pool_t query_loopback_db_pkt_pool(int idx)
-{
- return loopback_db->intf[idx].pkt_pool;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_misc.h b/example/ipsec/odp_ipsec_misc.h
deleted file mode 100644
index 45cb022eb..000000000
--- a/example/ipsec/odp_ipsec_misc.h
+++ /dev/null
@@ -1,343 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_MISC_H_
-#define ODP_IPSEC_MISC_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#define MAX_DB 32 /**< maximum number of data base entries */
-#define MAX_LOOPBACK 10 /**< maximum number of loop back interfaces */
-#define MAX_STRING 32 /**< maximum string length */
-#define MAX_IV_LEN 32 /**< Maximum IV length in bytes */
-
-#define KEY_BITS_3DES 192 /**< 3DES cipher key length in bits */
-#define KEY_BITS_MD5_96 128 /**< MD5_96 auth key length in bits */
-#define KEY_BITS_SHA256_128 256 /**< SHA256_128 auth key length in bits */
-
-/**< Number of bits represnted by a string of hexadecimal characters */
-#define KEY_STR_BITS(str) (4 * strlen(str))
-
-/** IPv4 helpers for data length and uint8t pointer */
-#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) - sizeof(odph_ipv4hdr_t))
-#define ipv4_data_p(ip) ((uint8_t *)((odph_ipv4hdr_t *)ip + 1))
-
-/** Helper for calculating encode length using data length and block size */
-#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-
-/**
- * IPsec key
- */
-typedef struct {
- uint8_t data[32]; /**< Key data */
- uint8_t length; /**< Key length */
-} ipsec_key_t;
-
-/**
- * IPsec algorithm
- */
-typedef struct {
- odp_bool_t cipher;
- union {
- odp_cipher_alg_t cipher;
- odp_auth_alg_t auth;
- } u;
-} ipsec_alg_t;
-
-/**
- * IP address range (subnet)
- */
-typedef struct ip_addr_range_s {
- uint32_t addr; /**< IP address */
- uint32_t mask; /**< mask, 1 indicates bits are valid */
-} ip_addr_range_t;
-
-/**
- * Parse text string representing a key into ODP key structure
- *
- * @param keystring Pointer to key string to convert
- * @param key Pointer to ODP key structure to populate
- * @param alg Cipher/authentication algorithm associated with the key
- *
- * @return 0 if successful else -1
- */
-static inline
-int parse_key_string(char *keystring,
- ipsec_key_t *key,
- ipsec_alg_t *alg)
-{
- int idx;
- int key_bits_in = KEY_STR_BITS(keystring);
- char temp[3];
-
- key->length = 0;
-
- /* Algorithm is either cipher or authentication */
- if (alg->cipher) {
- if ((alg->u.cipher == ODP_CIPHER_ALG_3DES_CBC) &&
- (KEY_BITS_3DES == key_bits_in))
- key->length = key_bits_in / 8;
-
- } else {
- if ((alg->u.auth == ODP_AUTH_ALG_MD5_HMAC) &&
- (KEY_BITS_MD5_96 == key_bits_in))
- key->length = key_bits_in / 8;
- else if ((alg->u.auth == ODP_AUTH_ALG_SHA256_HMAC) &&
- (KEY_BITS_SHA256_128 == key_bits_in))
- key->length = key_bits_in / 8;
- }
-
- for (idx = 0; idx < key->length; idx++) {
- temp[0] = *keystring++;
- temp[1] = *keystring++;
- temp[2] = 0;
- key->data[idx] = strtol(temp, NULL, 16);
- }
-
- return key->length ? 0 : -1;
-}
-
-/**
- * Check IPv4 address against a range/subnet
- *
- * @param addr IPv4 address to check
- * @param range Pointer to address range to check against
- *
- * @return 1 if match else 0
- */
-static inline
-int match_ip_range(uint32_t addr, ip_addr_range_t *range)
-{
- return (range->addr == (addr & range->mask));
-}
-
-/**
- * Generate text string representing IPv4 address
- *
- * @param b Pointer to buffer to store string
- * @param addr IPv4 address
- *
- * @return Pointer to supplied buffer
- */
-static inline
-char *ipv4_addr_str(char *b, uint32_t addr)
-{
- sprintf(b, "%03d.%03d.%03d.%03d",
- 0xFF & ((addr) >> 24),
- 0xFF & ((addr) >> 16),
- 0xFF & ((addr) >> 8),
- 0xFF & ((addr) >> 0));
- return b;
-}
-
-/**
- * Parse text string representing an IPv4 address or subnet
- *
- * String is of the format "XXX.XXX.XXX.XXX(/W)" where
- * "XXX" is decimal value and "/W" is optional subnet length
- *
- * @param ipaddress Pointer to IP address/subnet string to convert
- * @param addr Pointer to return IPv4 address
- * @param mask Pointer (optional) to return IPv4 mask
- *
- * @return 0 if successful else -1
- */
-static inline
-int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *mask)
-{
- int b[4];
- int qualifier = 32;
- int converted;
-
- if (strchr(ipaddress, '/')) {
- converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
- &b[3], &b[2], &b[1], &b[0],
- &qualifier);
- if (5 != converted)
- return -1;
- } else {
- converted = sscanf(ipaddress, "%d.%d.%d.%d",
- &b[3], &b[2], &b[1], &b[0]);
- if (4 != converted)
- return -1;
- }
-
- if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
- return -1;
- if (!qualifier || (qualifier > 32))
- return -1;
-
- *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
- if (mask)
- *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1));
-
- return 0;
-}
-
-/**
- * Generate text string representing IPv4 range/subnet, output
- * in "XXX.XXX.XXX.XXX/W" format
- *
- * @param b Pointer to buffer to store string
- * @param range Pointer to IPv4 address range
- *
- * @return Pointer to supplied buffer
- */
-static inline
-char *ipv4_subnet_str(char *b, ip_addr_range_t *range)
-{
- int idx;
- int len;
-
- for (idx = 0; idx < 32; idx++)
- if (range->mask & (1 << idx))
- break;
- len = 32 - idx;
-
- sprintf(b, "%03d.%03d.%03d.%03d/%d",
- 0xFF & ((range->addr) >> 24),
- 0xFF & ((range->addr) >> 16),
- 0xFF & ((range->addr) >> 8),
- 0xFF & ((range->addr) >> 0),
- len);
- return b;
-}
-
-/**
- * Generate text string representing MAC address
- *
- * @param b Pointer to buffer to store string
- * @param mac Pointer to MAC address
- *
- * @return Pointer to supplied buffer
- */
-static inline
-char *mac_addr_str(char *b, uint8_t *mac)
-{
- sprintf(b, "%02X.%02X.%02X.%02X.%02X.%02X",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- return b;
-}
-
-/**
- * Parse text string representing a MAC address into byte araray
- *
- * String is of the format "XX.XX.XX.XX.XX.XX" where XX is hexadecimal
- *
- * @param macaddress Pointer to MAC address string to convert
- * @param mac Pointer to MAC address byte array to populate
- *
- * @return 0 if successful else -1
- */
-static inline
-int parse_mac_string(char *macaddress, uint8_t *mac)
-{
- int macwords[ODPH_ETHADDR_LEN];
- int converted;
-
- converted = sscanf(macaddress,
- "%x.%x.%x.%x.%x.%x",
- &macwords[0], &macwords[1], &macwords[2],
- &macwords[3], &macwords[4], &macwords[5]);
- if (6 != converted)
- return -1;
-
- mac[0] = macwords[0];
- mac[1] = macwords[1];
- mac[2] = macwords[2];
- mac[3] = macwords[3];
- mac[4] = macwords[4];
- mac[5] = macwords[5];
-
- return 0;
-}
-
-/**
- * Locate IPsec headers (AH and/or ESP) in packet
- *
- * @param ip Pointer to packets IPv4 header
- * @param ah_p Pointer to location to return AH header pointer
- * @param esp_p Pointer to location to return ESP header pointer
- *
- * @return length of IPsec headers found
- */
-static inline
-int locate_ipsec_headers(odph_ipv4hdr_t *ip,
- odph_ahhdr_t **ah_p,
- odph_esphdr_t **esp_p)
-{
- uint8_t *in = ipv4_data_p(ip);
- odph_ahhdr_t *ah = NULL;
- odph_esphdr_t *esp = NULL;
-
- if (ODPH_IPPROTO_AH == ip->proto) {
- ah = (odph_ahhdr_t *)in;
- in += ((ah)->ah_len + 2) * 4;
- if (ODPH_IPPROTO_ESP == ah->next_header) {
- esp = (odph_esphdr_t *)in;
- in += sizeof(odph_esphdr_t);
- }
- } else if (ODPH_IPPROTO_ESP == ip->proto) {
- esp = (odph_esphdr_t *)in;
- in += sizeof(odph_esphdr_t);
- }
-
- *ah_p = ah;
- *esp_p = esp;
- return in - (ipv4_data_p(ip));
-}
-
-/**
- * Adjust IPv4 length
- *
- * @param ip Pointer to IPv4 header
- * @param adj Signed adjustment value
- */
-static inline
-void ipv4_adjust_len(odph_ipv4hdr_t *ip, int adj)
-{
- ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj);
-}
-
-/**
- * Verify crypto operation completed successfully
- *
- * @param status Pointer to cryto completion structure
- *
- * @return TRUE if all OK else FALSE
- */
-static inline
-odp_bool_t is_crypto_compl_status_ok(odp_crypto_compl_status_t *status)
-{
- if (status->alg_err != ODP_CRYPTO_ALG_ERR_NONE)
- return FALSE;
- if (status->hw_err != ODP_CRYPTO_HW_ERR_NONE)
- return FALSE;
- return TRUE;
-}
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_run_ah_in b/example/ipsec/odp_ipsec_run_ah_in
deleted file mode 100755
index 252f44bfd..000000000
--- a/example/ipsec/odp_ipsec_run_ah_in
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-#
-# Test input AH
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
--p 192.168.222.0/24:192.168.111.0/24:in:ah \
--a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
--s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_ah_out b/example/ipsec/odp_ipsec_run_ah_out
deleted file mode 100755
index 9256c0723..000000000
--- a/example/ipsec/odp_ipsec_run_ah_out
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-#
-# Test output AH
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
--p 192.168.111.0/24:192.168.222.0/24:out:ah \
--a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
--s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_both_in b/example/ipsec/odp_ipsec_run_both_in
deleted file mode 100755
index c3f169cec..000000000
--- a/example/ipsec/odp_ipsec_run_both_in
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-#
-# Test AH and ESP input
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
--p 192.168.222.0/24:192.168.111.0/24:in:both \
--a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
--e 192.168.222.2:192.168.111.2:\
-3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
--s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_both_out b/example/ipsec/odp_ipsec_run_both_out
deleted file mode 100755
index 8ba067223..000000000
--- a/example/ipsec/odp_ipsec_run_both_out
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-#
-# Test AH and ESP output
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
--p 192.168.111.0/24:192.168.222.0/24:out:both \
--e 192.168.111.2:192.168.222.2:\
-3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
--a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
--s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_esp_in b/example/ipsec/odp_ipsec_run_esp_in
deleted file mode 100755
index 12fdae691..000000000
--- a/example/ipsec/odp_ipsec_run_esp_in
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-#
-# Test input ESP
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
--p 192.168.222.0/24:192.168.111.0/24:in:esp \
--e 192.168.222.2:192.168.111.2:\
-3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
--s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_esp_out b/example/ipsec/odp_ipsec_run_esp_out
deleted file mode 100755
index 73c4ff0ef..000000000
--- a/example/ipsec/odp_ipsec_run_esp_out
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-#
-# Test output ESP
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
--p 192.168.111.0/24:192.168.222.0/24:out:esp \
--e 192.168.111.2:192.168.222.2:\
-3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
--s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_live b/example/ipsec/odp_ipsec_run_live
deleted file mode 100755
index 90947ad7e..000000000
--- a/example/ipsec/odp_ipsec_run_live
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Live router test
-# - 2 interfaces interfaces
-# - Specify API mode on command line
-sudo odp_ipsec -i p7p1,p8p1 \
--r 192.168.111.2/32:p7p1:08.00.27.76.B5.E0 \
--r 192.168.222.2/32:p8p1:08.00.27.F5.8B.DB \
--p 192.168.111.0/24:192.168.222.0/24:out:both \
--e 192.168.111.2:192.168.222.2:\
-3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
--a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
--p 192.168.222.0/24:192.168.111.0/24:in:both \
--e 192.168.222.2:192.168.111.2:\
-3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
--a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_run_router b/example/ipsec/odp_ipsec_run_router
deleted file mode 100755
index 363029737..000000000
--- a/example/ipsec/odp_ipsec_run_router
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-#
-# Live router test
-# - 2 interfaces interfaces
-# - Specify API mode on command line
-sudo odp_ipsec -i p7p1,p8p1 \
--r 192.168.111.2/32:p7p1:08.00.27.76.B5.E0 \
--r 192.168.222.2/32:p8p1:08.00.27.F5.8B.DB \
--c 1 -m $1
diff --git a/example/ipsec/odp_ipsec_run_simple b/example/ipsec/odp_ipsec_run_simple
deleted file mode 100755
index 12115838d..000000000
--- a/example/ipsec/odp_ipsec_run_simple
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-#
-# Simple router test
-# - 2 loop interfaces
-# - 10 packets
-# - Specify API mode on command line
-odp_ipsec -i loop1,loop2 \
--r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
--s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
--c 2 -m $1
diff --git a/example/ipsec/odp_ipsec_sa_db.c b/example/ipsec/odp_ipsec_sa_db.c
deleted file mode 100644
index 10bbcb8f2..000000000
--- a/example/ipsec/odp_ipsec_sa_db.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp_ipsec_sa_db.h>
-
-/** Global pointer to sa db */
-static sa_db_t *sa_db;
-
-/** Global pointer to tun db */
-static tun_db_t *tun_db;
-
-void init_sa_db(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("shm_sa_db",
- sizeof(sa_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- sa_db = odp_shm_addr(shm);
-
- if (sa_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(sa_db, 0, sizeof(*sa_db));
-}
-
-void init_tun_db(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("shm_tun_db",
- sizeof(tun_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
- tun_db = odp_shm_addr(shm);
-
- if (!tun_db) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(tun_db, 0, sizeof(*tun_db));
-}
-
-int create_sa_db_entry(char *input, odp_bool_t cipher)
-{
- int pos = 0;
- char *local;
- char *str;
- char *save;
- char *token;
- sa_db_entry_t *entry = &sa_db->array[sa_db->index];
-
- /* Verify we have a good entry */
- if (MAX_DB <= sa_db->index)
- return -1;
-
- /* Make a local copy */
- local = malloc(strlen(input) + 1);
- if (NULL == local)
- return -1;
- strcpy(local, input);
-
- /* Set cipher versus auth */
- entry->alg.cipher = cipher;
-
- /* Setup for using "strtok_r" to search input string */
- str = local;
- save = NULL;
-
- /* Parse tokens separated by ':' */
- while (NULL != (token = strtok_r(str, ":", &save))) {
- str = NULL; /* reset str for subsequent strtok_r calls */
-
- /* Parse token based on its position */
- switch (pos) {
- case 0:
- parse_ipv4_string(token, &entry->src_ip, NULL);
- break;
- case 1:
- parse_ipv4_string(token, &entry->dst_ip, NULL);
- break;
- case 2:
- if (cipher) {
- if (0 == strcmp(token, "3des")) {
- entry->alg.u.cipher =
- ODP_CIPHER_ALG_3DES_CBC;
- entry->block_len = 8;
- entry->iv_len = 8;
- } else {
- entry->alg.u.cipher =
- ODP_CIPHER_ALG_NULL;
- }
- } else {
- if (0 == strcmp(token, "md5")) {
- entry->alg.u.auth =
- ODP_AUTH_ALG_MD5_HMAC;
- entry->icv_len = 12;
- } else if (!strcmp(token, "sha256")) {
- entry->alg.u.auth =
- ODP_AUTH_ALG_SHA256_HMAC;
- entry->icv_len = 16;
- } else {
- entry->alg.u.auth = ODP_AUTH_ALG_NULL;
- }
- }
- break;
- case 3:
- entry->spi = strtol(token, NULL, 16);
- break;
- case 4:
- parse_key_string(token,
- &entry->key,
- &entry->alg);
- break;
- default:
- printf("ERROR: extra token \"%s\" at position %d\n",
- token, pos);
- break;
- }
-
- /* Advance to next position */
- pos++;
- }
-
- /* Verify we parsed exactly the number of tokens we expected */
- if (5 != pos) {
- printf("ERROR: \"%s\" contains %d tokens, expected 5\n",
- input,
- pos);
- free(local);
- return -1;
- }
-
- /* Add route to the list */
- sa_db->index++;
- entry->next = sa_db->list;
- sa_db->list = entry;
-
- free(local);
- return 0;
-}
-
-int create_tun_db_entry(char *input)
-{
- int pos = 0;
- char *local;
- char *str;
- char *save;
- char *token;
- tun_db_entry_t *entry = &tun_db->array[tun_db->index];
-
- /* Verify we have a good entry */
- if (MAX_DB <= tun_db->index)
- return -1;
-
- /* Make a local copy */
- local = malloc(strlen(input) + 1);
- if (NULL == local)
- return -1;
- strcpy(local, input);
-
- /* Setup for using "strtok_r" to search input string */
- str = local;
- save = NULL;
-
- /* Parse tokens separated by ':' */
- while (NULL != (token = strtok_r(str, ":", &save))) {
- str = NULL; /* reset str for subsequent strtok_r calls */
-
- /* Parse token based on its position */
- switch (pos) {
- case 0:
- parse_ipv4_string(token, &entry->src_ip, NULL);
- break;
- case 1:
- parse_ipv4_string(token, &entry->dst_ip, NULL);
- break;
- case 2:
- parse_ipv4_string(token, &entry->tun_src_ip, NULL);
- break;
- case 3:
- parse_ipv4_string(token, &entry->tun_dst_ip, NULL);
- break;
- default:
- printf("ERROR: extra token \"%s\" at position %d\n",
- token, pos);
- break;
- }
- pos++;
- }
-
- /* Verify we parsed exactly the number of tokens we expected */
- if (4 != pos) {
- printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
- input,
- pos);
- free(local);
- return -1;
- }
-
- /* Add route to the list */
- tun_db->index++;
- entry->next = tun_db->list;
- tun_db->list = entry;
-
- free(local);
- return 0;
-}
-
-tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
- uint32_t ip_dst)
-{
- tun_db_entry_t *entry = NULL;
-
- /* Scan all entries and return first match */
- for (entry = tun_db->list; NULL != entry; entry = entry->next) {
- if (entry->src_ip != ip_src)
- continue;
- if (entry->dst_ip != ip_dst)
- continue;
- break;
- }
- return entry;
-}
-
-void dump_sa_db(void)
-{
- sa_db_entry_t *entry;
-
- printf("\n"
- "Security association table\n"
- "--------------------------\n");
-
- for (entry = sa_db->list; NULL != entry; entry = entry->next) {
- uint32_t idx;
- char src_ip_str[MAX_STRING];
- char dst_ip_str[MAX_STRING];
- uint8_t *p = entry->key.data;
-
-
- printf(" %s %s %s %X %d ",
- entry->alg.cipher ? "esp" : "ah ",
- ipv4_addr_str(src_ip_str, entry->src_ip),
- ipv4_addr_str(dst_ip_str, entry->dst_ip),
- entry->spi,
- entry->alg.cipher ?
- (int)entry->alg.u.cipher :
- (int)entry->alg.u.auth);
-
- /* Brute force key display */
- for (idx = 0; idx < entry->key.length; idx++)
- printf("%02X", *p++);
-
- printf("\n");
- }
-}
-
-sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
- ip_addr_range_t *dst,
- odp_bool_t cipher)
-{
- sa_db_entry_t *entry = NULL;
-
- /* Scan all entries and return first match */
- for (entry = sa_db->list; NULL != entry; entry = entry->next) {
- if (cipher != entry->alg.cipher)
- continue;
- if (!match_ip_range(entry->src_ip, src))
- continue;
- if (!match_ip_range(entry->dst_ip, dst))
- continue;
- break;
- }
- return entry;
-}
-
-void dump_tun_db(void)
-{
- tun_db_entry_t *entry;
-
- printf("\n"
- "Tunnel table\n"
- "--------------------------\n");
-
- for (entry = tun_db->list; NULL != entry; entry = entry->next) {
- char src_ip_str[MAX_STRING];
- char dst_ip_str[MAX_STRING];
- char tun_src_ip_str[MAX_STRING];
- char tun_dst_ip_str[MAX_STRING];
-
- printf(" %s:%s %s:%s ",
- ipv4_addr_str(src_ip_str, entry->src_ip),
- ipv4_addr_str(dst_ip_str, entry->dst_ip),
- ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip),
- ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip)
- );
-
- printf("\n");
- }
-}
diff --git a/example/ipsec/odp_ipsec_sa_db.h b/example/ipsec/odp_ipsec_sa_db.h
deleted file mode 100644
index cdcd9c1ab..000000000
--- a/example/ipsec/odp_ipsec_sa_db.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_SA_DB_H_
-#define ODP_IPSEC_SA_DB_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_ipsec_misc.h>
-
-typedef enum sa_mode_s {
- IPSEC_SA_MODE_TRANSPORT,
- IPSEC_SA_MODE_TUNNEL
-} sa_mode_t;
-/**
- * Security Association (SA) data base entry
- */
-typedef struct sa_db_entry_s {
- struct sa_db_entry_s *next; /**< Next entry on list */
- uint32_t src_ip; /**< Source IPv4 address */
- uint32_t dst_ip; /**< Desitnation IPv4 address */
- uint32_t spi; /**< Security Parameter Index */
- ipsec_alg_t alg; /**< Cipher/auth algorithm */
- ipsec_key_t key; /**< Cipher/auth key */
- uint32_t block_len; /**< Cipher block length */
- uint32_t iv_len; /**< Initialization Vector length */
- uint32_t icv_len; /**< Integrity Check Value length */
- sa_mode_t mode; /**< SA mode - transport/tun */
-} sa_db_entry_t;
-
-/**
- * Security Association (SA) data base global structure
- */
-typedef struct sa_db_s {
- uint32_t index; /**< Index of next available entry */
- sa_db_entry_t *list; /**< List of active entries */
- sa_db_entry_t array[MAX_DB]; /**< Entry storage */
-} sa_db_t;
-
-/** Initialize SA database global control structure */
-void init_sa_db(void);
-
-/**
- * Create an SA DB entry
- *
- * String is of the format "SrcIP:DstIP:Alg:SPI:Key"
- *
- * @param input Pointer to string describing SA
- * @param cipher TRUE if cipher else FALSE for auth
- *
- * @return 0 if successful else -1
- */
-int create_sa_db_entry(char *input, odp_bool_t cipher);
-/**
- * Display the SA DB
- */
-void dump_sa_db(void);
-
-/**
- * Find a matching SA DB entry
- *
- * @param src Pointer to source subnet/range
- * @param dst Pointer to destination subnet/range
- * @param cipher TRUE if cipher else FALSE for auth
- *
- * @return pointer to SA DB entry else NULL
- */
-sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
- ip_addr_range_t *dst,
- odp_bool_t cipher);
-
-/**
- * Tunnel entry
- */
-typedef struct tun_db_entry_s {
- struct tun_db_entry_s *next;
- uint32_t src_ip; /**< Inner Source IPv4 address */
- uint32_t dst_ip; /**< Inner Destination IPv4 address */
- uint32_t tun_src_ip; /**< Tunnel Source IPv4 address */
- uint32_t tun_dst_ip; /**< Tunnel Source IPv4 address */
-} tun_db_entry_t;
-
-/**
- * Tunnel database
- */
-typedef struct tun_db_s {
- uint32_t index; /**< Index of next available entry */
- tun_db_entry_t *list; /**< List of active entries */
- tun_db_entry_t array[MAX_DB]; /**< Entry storage */
-} tun_db_t;
-
-/** Initialize tun database global control structure */
-void init_tun_db(void);
-
-/**
- * Create an tunnel DB entry
- *
- * String is of the format "SrcIP:DstIP:TunSrcIp:TunDstIp"
- *
- * @param input Pointer to string describing tun
- *
- * @return 0 if successful else -1
- */
-int create_tun_db_entry(char *input);
-
-/**
- * Display the tun DB
- */
-void dump_tun_db(void);
-
-/**
- * Find a matching tun DB entry
- *
- * @param ip_src Inner source IP address
- * @param ip_dst Inner destination IP address
- *
- * @return pointer to tun DB entry else NULL
- */
-tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
- uint32_t ip_dst);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_sp_db.c b/example/ipsec/odp_ipsec_sp_db.c
deleted file mode 100644
index ed631c7ba..000000000
--- a/example/ipsec/odp_ipsec_sp_db.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp_ipsec_sp_db.h>
-
-/** Global pointer to sp db */
-sp_db_t *sp_db;
-
-void init_sp_db(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("shm_sp_db",
- sizeof(sp_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- sp_db = odp_shm_addr(shm);
-
- if (sp_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(sp_db, 0, sizeof(*sp_db));
-}
-
-int create_sp_db_entry(char *input)
-{
- int pos = 0;
- char *local;
- char *str;
- char *save;
- char *token;
- sp_db_entry_t *entry = &sp_db->array[sp_db->index];
-
- /* Verify we have a good entry */
- if (MAX_DB <= sp_db->index)
- return -1;
-
- /* Make a local copy */
- local = malloc(strlen(input) + 1);
- if (NULL == local)
- return -1;
- strcpy(local, input);
-
- /* Setup for using "strtok_r" to search input string */
- str = local;
- save = NULL;
-
- /* Parse tokens separated by ':' */
- while (NULL != (token = strtok_r(str, ":", &save))) {
- str = NULL; /* reset str for subsequent strtok_r calls */
-
- /* Parse token based on its position */
- switch (pos) {
- case 0:
- parse_ipv4_string(token,
- &entry->src_subnet.addr,
- &entry->src_subnet.mask);
- break;
- case 1:
- parse_ipv4_string(token,
- &entry->dst_subnet.addr,
- &entry->dst_subnet.mask);
- break;
- case 2:
- if (0 == strcmp(token, "in"))
- entry->input = TRUE;
- else
- entry->input = FALSE;
- break;
- case 3:
- if (0 == strcmp(token, "esp")) {
- entry->esp = TRUE;
- } else if (0 == strcmp(token, "ah")) {
- entry->ah = TRUE;
- } else if (0 == strcmp(token, "both")) {
- entry->esp = TRUE;
- entry->ah = TRUE;
- }
- break;
- default:
- printf("ERROR: extra token \"%s\" at position %d\n",
- token, pos);
- break;
- }
-
- /* Advance to next position */
- pos++;
- }
-
- /* Verify we parsed exactly the number of tokens we expected */
- if (4 != pos) {
- printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
- input,
- pos);
- free(local);
- return -1;
- }
-
- /* Add route to the list */
- sp_db->index++;
- entry->next = sp_db->list;
- sp_db->list = entry;
-
- free(local);
- return 0;
-}
-
-void dump_sp_db_entry(sp_db_entry_t *entry)
-{
- char src_subnet_str[MAX_STRING];
- char dst_subnet_str[MAX_STRING];
-
- printf(" %s %s %s %s:%s\n",
- ipv4_subnet_str(src_subnet_str, &entry->src_subnet),
- ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet),
- entry->input ? "in" : "out",
- entry->esp ? "esp" : "none",
- entry->ah ? "ah" : "none");
-}
-
-void dump_sp_db(void)
-{
- sp_db_entry_t *entry;
-
- printf("\n"
- "Security policy table\n"
- "---------------------\n");
-
- for (entry = sp_db->list; NULL != entry; entry = entry->next)
- dump_sp_db_entry(entry);
-}
diff --git a/example/ipsec/odp_ipsec_sp_db.h b/example/ipsec/odp_ipsec_sp_db.h
deleted file mode 100644
index 735c20ed5..000000000
--- a/example/ipsec/odp_ipsec_sp_db.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_SP_DB_H_
-#define ODP_IPSEC_SP_DB_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_ipsec_misc.h>
-
-/**
- * Security Policy (SP) data base entry
- */
-typedef struct sp_db_entry_s {
- struct sp_db_entry_s *next; /**< Next entry on list */
- ip_addr_range_t src_subnet; /**< Source IPv4 subnet/range */
- ip_addr_range_t dst_subnet; /**< Destination IPv4 subnet/range */
- odp_bool_t input; /**< Direction when applied */
- odp_bool_t esp; /**< Enable cipher (ESP) */
- odp_bool_t ah; /**< Enable authentication (AH) */
-} sp_db_entry_t;
-
-/**
- * Security Policy (SP) data base global structure
- */
-typedef struct sp_db_s {
- uint32_t index; /**< Index of next available entry */
- sp_db_entry_t *list; /**< List of active entries */
- sp_db_entry_t array[MAX_DB]; /**< Entry storage */
-} sp_db_t;
-
-/** Global pointer to sp db */
-extern sp_db_t *sp_db;
-
-/** Initialize SP database global control structure */
-void init_sp_db(void);
-
-/**
- * Create an SP DB entry
- *
- * String is of the format "SrcSubNet:DstSubNet:(in|out):(ah|esp|both)"
- *
- * @param input Pointer to string describing SP
- *
- * @return 0 if successful else -1
- */
-int create_sp_db_entry(char *input);
-
-/**
- * Display one SP DB entry
- *
- * @param entry Pointer to entry to display
- */
-void dump_sp_db_entry(sp_db_entry_t *entry);
-
-/**
- * Display the SP DB
- */
-void dump_sp_db(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c
deleted file mode 100644
index e37fbee29..000000000
--- a/example/ipsec/odp_ipsec_stream.c
+++ /dev/null
@@ -1,635 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <openssl/des.h>
-#include <openssl/rand.h>
-#include <openssl/hmac.h>
-#include <openssl/evp.h>
-
-#include <example_debug.h>
-
-#include <odp_api.h>
-
-#include <odp/helper/odph_api.h>
-
-#include <odp_ipsec_stream.h>
-#include <odp_ipsec_loop_db.h>
-
-#define STREAM_MAGIC 0xBABE01234567CAFE
-
-#define LOOP_DEQ_COUNT 32 /**< packets to dequeue at once */
-
-/**
- * Stream packet header
- */
-typedef struct ODP_PACKED stream_pkt_hdr_s {
- odp_u64be_t magic; /**< Stream magic value for verification */
- uint8_t data[0]; /**< Incrementing data stream */
-} stream_pkt_hdr_t;
-
-stream_db_t *stream_db;
-
-void init_stream_db(void)
-{
- odp_shm_t shm;
-
- shm = odp_shm_reserve("stream_db",
- sizeof(stream_db_t),
- ODP_CACHE_LINE_SIZE,
- 0);
-
- stream_db = odp_shm_addr(shm);
-
- if (stream_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- memset(stream_db, 0, sizeof(*stream_db));
-}
-
-int create_stream_db_entry(char *input)
-{
- int pos = 0;
- char *local;
- char *str;
- char *save;
- char *token;
- stream_db_entry_t *entry = &stream_db->array[stream_db->index];
-
- /* Verify we have a good entry */
- if (MAX_DB <= stream_db->index)
- return -1;
-
- /* Make a local copy */
- local = malloc(strlen(input) + 1);
- if (NULL == local)
- return -1;
- strcpy(local, input);
-
- /* Setup for using "strtok_r" to search input string */
- str = local;
- save = NULL;
-
- /* Parse tokens separated by ':' */
- while (NULL != (token = strtok_r(str, ":", &save))) {
- str = NULL; /* reset str for subsequent strtok_r calls */
-
- /* Parse token based on its position */
- switch (pos) {
- case 0:
- parse_ipv4_string(token, &entry->src_ip, NULL);
- break;
- case 1:
- parse_ipv4_string(token, &entry->dst_ip, NULL);
- break;
- case 2:
- entry->input.loop = loop_if_index(token);
- if (entry->input.loop < 0) {
- EXAMPLE_ERR("Error: stream must have input"
- " loop\n");
- exit(EXIT_FAILURE);
- }
- break;
- case 3:
- entry->output.loop = loop_if_index(token);
- break;
- case 4:
- entry->count = atoi(token);
- break;
- case 5:
- entry->length = atoi(token);
- if (entry->length < sizeof(stream_pkt_hdr_t))
- entry->length = 0;
- else
- entry->length -= sizeof(stream_pkt_hdr_t);
- break;
- default:
- printf("ERROR: extra token \"%s\" at position %d\n",
- token, pos);
- break;
- }
-
- /* Advance to next position */
- pos++;
- }
-
- /* Verify we parsed exactly the number of tokens we expected */
- if (6 != pos) {
- printf("ERROR: \"%s\" contains %d tokens, expected 6\n",
- input,
- pos);
- free(local);
- return -1;
- }
-
- /* Add stream to the list */
- entry->id = stream_db->index++;
- entry->next = stream_db->list;
- stream_db->list = entry;
-
- free(local);
- return 0;
-}
-
-void resolve_stream_db(void)
-{
- stream_db_entry_t *stream = NULL;
-
- /* For each stream look for input and output IPsec entries */
- for (stream = stream_db->list; NULL != stream; stream = stream->next) {
- ipsec_cache_entry_t *entry;
-
- /* Lookup input entry */
- entry = find_ipsec_cache_entry_in(stream->src_ip,
- stream->dst_ip,
- NULL,
- NULL);
- stream->input.entry = entry;
-
- /* Lookup output entry */
- entry = find_ipsec_cache_entry_out(stream->src_ip,
- stream->dst_ip,
- 0);
- stream->output.entry = entry;
- }
-}
-
-odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
- uint8_t *dmac,
- odp_pool_t pkt_pool)
-{
- ipsec_cache_entry_t *entry = NULL;
- odp_packet_t pkt;
- uint8_t *base;
- uint8_t *data;
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ip;
- odph_ipv4hdr_t *inner_ip = NULL;
- odph_ahhdr_t *ah = NULL;
- odph_esphdr_t *esp = NULL;
- odph_icmphdr_t *icmp;
- stream_pkt_hdr_t *test;
- unsigned i;
-
- if (stream->input.entry)
- entry = stream->input.entry;
- else if (stream->output.entry)
- entry = stream->output.entry;
-
- /* Get packet */
- pkt = odp_packet_alloc(pkt_pool, 0);
- if (ODP_PACKET_INVALID == pkt)
- return ODP_PACKET_INVALID;
- base = odp_packet_data(pkt);
- data = odp_packet_data(pkt);
-
- /* Ethernet */
- odp_packet_has_eth_set(pkt, 1);
- eth = (odph_ethhdr_t *)data;
- data += sizeof(*eth);
-
- memset((char *)eth->src.addr, (0x80 | stream->id), ODPH_ETHADDR_LEN);
- memcpy((char *)eth->dst.addr, dmac, ODPH_ETHADDR_LEN);
- eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
-
- /* IPv4 */
- odp_packet_has_ipv4_set(pkt, 1);
- ip = (odph_ipv4hdr_t *)data;
- data += sizeof(*ip);
-
- /* Wait until almost finished to fill in mutable fields */
- memset((char *)ip, 0, sizeof(*ip));
- ip->ver_ihl = 0x45;
- ip->id = odp_cpu_to_be_16(stream->id);
- /* Outer IP header in tunnel mode */
- if (entry && entry->mode == IPSEC_SA_MODE_TUNNEL &&
- (entry == stream->input.entry)) {
- ip->proto = ODPH_IPV4;
- ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip);
- ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip);
- } else {
- ip->proto = ODPH_IPPROTO_ICMPv4;
- ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
- ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
- }
-
- /* AH (if specified) */
- if (entry && (entry == stream->input.entry) &&
- (ODP_AUTH_ALG_NULL != entry->ah.alg)) {
- if (entry->ah.alg != ODP_AUTH_ALG_MD5_HMAC &&
- entry->ah.alg != ODP_AUTH_ALG_SHA256_HMAC)
- abort();
-
- ah = (odph_ahhdr_t *)data;
- data += sizeof(*ah);
- data += entry->ah.icv_len;
-
- memset((char *)ah, 0, sizeof(*ah) + entry->ah.icv_len);
- ah->ah_len = 1 + (entry->ah.icv_len / 4);
- ah->spi = odp_cpu_to_be_32(entry->ah.spi);
- ah->seq_no = odp_cpu_to_be_32(stream->input.ah_seq++);
- }
-
- /* ESP (if specified) */
- if (entry && (entry == stream->input.entry) &&
- (ODP_CIPHER_ALG_NULL != entry->esp.alg)) {
- if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
- abort();
-
- esp = (odph_esphdr_t *)data;
- data += sizeof(*esp);
- data += entry->esp.iv_len;
-
- esp->spi = odp_cpu_to_be_32(entry->esp.spi);
- esp->seq_no = odp_cpu_to_be_32(stream->input.esp_seq++);
- RAND_bytes(esp->iv, 8);
- }
-
- /* Inner IP header in tunnel mode */
- if (entry && (entry == stream->input.entry) &&
- (entry->mode == IPSEC_SA_MODE_TUNNEL)) {
- inner_ip = (odph_ipv4hdr_t *)data;
- memset((char *)inner_ip, 0, sizeof(*inner_ip));
- inner_ip->ver_ihl = 0x45;
- inner_ip->proto = ODPH_IPPROTO_ICMPv4;
- inner_ip->id = odp_cpu_to_be_16(stream->id);
- inner_ip->ttl = 64;
- inner_ip->tos = 0;
- inner_ip->frag_offset = 0;
- inner_ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
- inner_ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
- inner_ip->chksum = odph_chksum(inner_ip, sizeof(*inner_ip));
- data += sizeof(*inner_ip);
- }
-
- /* ICMP header so we can see it on wireshark */
- icmp = (odph_icmphdr_t *)data;
- data += sizeof(*icmp);
- icmp->type = ICMP_ECHO;
- icmp->code = 0;
- icmp->un.echo.id = odp_cpu_to_be_16(0x1234);
- icmp->un.echo.sequence = odp_cpu_to_be_16(stream->created);
-
- /* Packet payload of incrementing bytes */
- test = (stream_pkt_hdr_t *)data;
- data += sizeof(*test);
- test->magic = odp_cpu_to_be_64(STREAM_MAGIC);
- for (i = 0; i < stream->length; i++)
- *data++ = (uint8_t)i;
-
- /* Close ICMP */
- icmp->chksum = 0;
- icmp->chksum = odph_chksum(icmp, data - (uint8_t *)icmp);
-
- /* Close ESP if specified */
- if (esp) {
- int payload_len = data - (uint8_t *)icmp;
- uint8_t *encrypt_start = (uint8_t *)icmp;
-
- if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
- payload_len = data - (uint8_t *)inner_ip;
- encrypt_start = (uint8_t *)inner_ip;
- }
-
- int encrypt_len;
- odph_esptrl_t *esp_t;
- DES_key_schedule ks1, ks2, ks3;
- uint8_t iv[8];
-
- memcpy(iv, esp->iv, sizeof(iv));
-
- encrypt_len = ESP_ENCODE_LEN(payload_len + sizeof(*esp_t),
- entry->esp.block_len);
- memset(data, 0, encrypt_len - payload_len);
- data += encrypt_len - payload_len;
-
- esp_t = (odph_esptrl_t *)(data) - 1;
- esp_t->pad_len = encrypt_len - payload_len - sizeof(*esp_t);
- esp_t->next_header = ip->proto;
- ip->proto = ODPH_IPPROTO_ESP;
-
- DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
- DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
- DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
-
- DES_ede3_cbc_encrypt(encrypt_start,
- encrypt_start,
- encrypt_len,
- &ks1,
- &ks2,
- &ks3,
- (DES_cblock *)iv,
- 1);
- }
-
- /* Since ESP can pad we can now fix IP length */
- ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip);
-
- /* Close AH if specified */
- if (ah) {
- uint8_t hash[EVP_MAX_MD_SIZE];
- int auth_len = data - (uint8_t *)ip;
-
- ah->next_header = ip->proto;
- ip->proto = ODPH_IPPROTO_AH;
-
- HMAC(EVP_md5(),
- entry->ah.key.data,
- entry->ah.key.length,
- (uint8_t *)ip,
- auth_len,
- hash,
- NULL);
-
- memcpy(ah->icv, hash, 12);
- }
-
- /* Correct set packet length offsets */
- odp_packet_push_tail(pkt, data - base);
- odp_packet_l2_offset_set(pkt, (uint8_t *)eth - base);
- odp_packet_l3_offset_set(pkt, (uint8_t *)ip - base);
- odp_packet_l4_offset_set(pkt, ((uint8_t *)ip - base) + sizeof(*ip));
-
- /* Now fill in final IP header fields */
- ip->ttl = 64;
- ip->tos = 0;
- ip->frag_offset = 0;
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
- return pkt;
-}
-
-odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream,
- odp_packet_t pkt)
-{
- ipsec_cache_entry_t *entry = NULL;
- uint8_t *data;
- odph_ipv4hdr_t *ip;
- odph_ahhdr_t *ah = NULL;
- odph_esphdr_t *esp = NULL;
- int hdr_len;
- odph_icmphdr_t *icmp;
- stream_pkt_hdr_t *test;
- uint32_t src_ip, dst_ip;
-
- if (stream->input.entry)
- entry = stream->input.entry;
- else if (stream->output.entry)
- entry = stream->output.entry;
-
- /* Basic IPv4 verify (add checksum verification) */
- data = odp_packet_l3_ptr(pkt, NULL);
- ip = (odph_ipv4hdr_t *)data;
- data += sizeof(*ip);
- if (0x45 != ip->ver_ihl)
- return FALSE;
-
- src_ip = odp_be_to_cpu_32(ip->src_addr);
- dst_ip = odp_be_to_cpu_32(ip->dst_addr);
- if ((stream->src_ip != src_ip) && stream->output.entry &&
- (stream->output.entry->tun_src_ip != src_ip))
- return FALSE;
- if ((stream->dst_ip != dst_ip) && stream->output.entry &&
- (stream->output.entry->tun_dst_ip != dst_ip))
- return FALSE;
-
- if ((stream->src_ip != src_ip) && stream->input.entry &&
- (stream->input.entry->tun_src_ip != src_ip))
- return FALSE;
- if ((stream->dst_ip != dst_ip) && stream->input.entry &&
- (stream->input.entry->tun_dst_ip != dst_ip))
- return FALSE;
-
- /* Find IPsec headers if any and compare against entry */
- hdr_len = locate_ipsec_headers(ip, &ah, &esp);
-
- /* Cleartext packet */
- if (!ah && !esp)
- goto clear_packet;
- if (ah) {
- if (!entry)
- return FALSE;
- if (ODP_AUTH_ALG_NULL == entry->ah.alg)
- return FALSE;
- if (odp_be_to_cpu_32(ah->spi) != entry->ah.spi)
- return FALSE;
- if (ODP_AUTH_ALG_MD5_HMAC != entry->ah.alg)
- abort();
- } else {
- if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg))
- return FALSE;
- }
- if (esp) {
- if (!entry)
- return FALSE;
- if (ODP_CIPHER_ALG_NULL == entry->esp.alg)
- return FALSE;
- if (odp_be_to_cpu_32(esp->spi) != entry->esp.spi)
- return FALSE;
- if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
- abort();
- hdr_len += entry->esp.iv_len;
- } else {
- if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg))
- return FALSE;
- }
- data += hdr_len;
-
- /* Verify authentication (if present) */
- if (ah) {
- uint8_t ip_tos;
- uint8_t ip_ttl;
- uint16_t ip_frag_offset;
- uint8_t icv[12];
- uint8_t hash[EVP_MAX_MD_SIZE];
-
- /* Save/clear mutable fields */
- ip_tos = ip->tos;
- ip_ttl = ip->ttl;
- ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
- ip->tos = 0;
- ip->ttl = 0;
- ip->frag_offset = 0;
- ip->chksum = 0;
- memcpy(icv, ah->icv, 12);
- memset(ah->icv, 0, 12);
-
- /* Calculate HMAC and compare */
- HMAC(EVP_md5(),
- entry->ah.key.data,
- entry->ah.key.length,
- (uint8_t *)ip,
- odp_be_to_cpu_16(ip->tot_len),
- hash,
- NULL);
-
- if (0 != memcmp(icv, hash, sizeof(icv)))
- return FALSE;
-
- ip->proto = ah->next_header;
- ip->tos = ip_tos;
- ip->ttl = ip_ttl;
- ip->frag_offset = odp_cpu_to_be_16(ip_frag_offset);
- }
-
- /* Decipher if present */
- if (esp) {
- odph_esptrl_t *esp_t;
- DES_key_schedule ks1, ks2, ks3;
- uint8_t iv[8];
- int encrypt_len = ipv4_data_len(ip) - hdr_len;
-
- memcpy(iv, esp->iv, sizeof(iv));
-
- DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
- DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
- DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
-
- DES_ede3_cbc_encrypt((uint8_t *)data,
- (uint8_t *)data,
- encrypt_len,
- &ks1,
- &ks2,
- &ks3,
- (DES_cblock *)iv,
- 0);
-
- esp_t = (odph_esptrl_t *)(data + encrypt_len) - 1;
- ip->proto = esp_t->next_header;
- }
-
-clear_packet:
- /* Verify IP/ICMP packet */
- if (entry && (entry->mode == IPSEC_SA_MODE_TUNNEL) && (ah || esp)) {
- if (ODPH_IPV4 != ip->proto)
- return FALSE;
- odph_ipv4hdr_t *inner_ip = (odph_ipv4hdr_t *)data;
-
- icmp = (odph_icmphdr_t *)(inner_ip + 1);
- data = (uint8_t *)icmp;
- } else {
- if (ODPH_IPPROTO_ICMPv4 != ip->proto)
- return FALSE;
- icmp = (odph_icmphdr_t *)data;
- }
-
- /* Verify ICMP header */
- data += sizeof(*icmp);
- if (ICMP_ECHO != icmp->type)
- return FALSE;
- if (0x1234 != odp_be_to_cpu_16(icmp->un.echo.id))
- return FALSE;
-
- /* Now check our packet */
- test = (stream_pkt_hdr_t *)data;
- if (STREAM_MAGIC != odp_be_to_cpu_64(test->magic))
- return FALSE;
-
- return TRUE;
-}
-
-int create_stream_db_inputs(void)
-{
- int created = 0;
- odp_pool_t pkt_pool;
- stream_db_entry_t *stream = NULL;
-
- /* Lookup the packet pool */
- pkt_pool = odp_pool_lookup("packet_pool");
- if (pkt_pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: pkt_pool not found\n");
- exit(EXIT_FAILURE);
- }
-
- /* For each stream create corresponding input packets */
- for (stream = stream_db->list; NULL != stream; stream = stream->next) {
- int count;
- uint8_t *dmac = query_loopback_db_mac(stream->input.loop);
- odp_queue_t queue = query_loopback_db_inq(stream->input.loop);
-
- for (count = stream->count; count > 0; count--) {
- odp_packet_t pkt;
-
- pkt = create_ipv4_packet(stream, dmac, pkt_pool);
- if (ODP_PACKET_INVALID == pkt) {
- printf("Packet buffers exhausted\n");
- break;
- }
- stream->created++;
- if (odp_queue_enq(queue, odp_packet_to_event(pkt))) {
- odp_packet_free(pkt);
- printf("Queue enqueue failed\n");
- break;
- }
-
- /* Count this stream when we create first packet */
- if (1 == stream->created)
- created++;
- }
- }
-
- return created;
-}
-
-odp_bool_t verify_stream_db_outputs(void)
-{
- odp_bool_t done = TRUE;
- stream_db_entry_t *stream = NULL;
- const char *env;
-
- env = getenv("ODP_IPSEC_STREAM_VERIFY_MDEQ");
- /* For each stream look for output packets */
- for (stream = stream_db->list; NULL != stream; stream = stream->next) {
- int idx;
- int count;
- odp_queue_t queue;
- odp_event_t ev_tbl[LOOP_DEQ_COUNT];
-
- queue = query_loopback_db_outq(stream->output.loop);
-
- if (ODP_QUEUE_INVALID == queue)
- continue;
-
- for (;;) {
- if (env) {
- count = odp_queue_deq_multi(queue,
- ev_tbl,
- LOOP_DEQ_COUNT);
- } else {
- ev_tbl[0] = odp_queue_deq(queue);
- count = (ev_tbl[0] != ODP_EVENT_INVALID) ?
- 1 : 0;
- }
- if (!count)
- break;
- for (idx = 0; idx < count; idx++) {
- odp_bool_t good;
- odp_packet_t pkt;
-
- pkt = odp_packet_from_event(ev_tbl[idx]);
-
- good = verify_ipv4_packet(stream, pkt);
- if (good)
- stream->verified++;
- odp_packet_free(pkt);
- }
- }
-
- printf("Stream %d %d\n", stream->created, stream->verified);
-
- if (stream->created != stream->verified)
- done = FALSE;
- }
- return done;
-}
diff --git a/example/ipsec/odp_ipsec_stream.h b/example/ipsec/odp_ipsec_stream.h
deleted file mode 100644
index 6f6f7dff4..000000000
--- a/example/ipsec/odp_ipsec_stream.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_IPSEC_STREAM_H_
-#define ODP_IPSEC_STREAM_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_api.h>
-#include <odp_ipsec_misc.h>
-#include <odp_ipsec_cache.h>
-
-/**
- * Stream database entry structure
- */
-typedef struct stream_db_entry_s {
- struct stream_db_entry_s *next; /**< Next entry on list */
- int id; /**< Stream ID */
- uint32_t src_ip; /**< Source IPv4 address */
- uint32_t dst_ip; /**< Destination IPv4 address */
- int count; /**< Packet count */
- uint32_t length; /**< Packet payload length */
- uint32_t created; /**< Number successfully created */
- uint32_t verified; /**< Number successfully verified */
- struct {
- int loop; /**< Input loop interface index */
- uint32_t ah_seq; /**< AH sequence number if present */
- uint32_t esp_seq; /**< ESP sequence number if present */
- ipsec_cache_entry_t *entry; /**< IPsec to apply on input */
- } input;
- struct {
- int loop; /**< Output loop interface index */
- ipsec_cache_entry_t *entry; /**t IPsec to verify on output */
- } output;
-} stream_db_entry_t;
-
-/**
- * Stream database
- */
-typedef struct stream_db_s {
- uint32_t index; /**< Index of next available entry */
- stream_db_entry_t *list; /**< List of active entries */
- stream_db_entry_t array[MAX_DB]; /**< Entry storage */
-} stream_db_t;
-
-extern stream_db_t *stream_db;
-
-/** Initialize stream database global control structure */
-void init_stream_db(void);
-
-/**
- * Create an stream DB entry
- *
- * String is of the format "SrcIP:DstIP:InInt:OutIntf:Count:Length"
- *
- * @param input Pointer to string describing stream
- *
- * @return 0 if successful else -1
- */
-int create_stream_db_entry(char *input);
-
-/**
- * Resolve the stream DB against the IPsec input and output caches
- *
- * For each stream, look the source and destination IP address up in the
- * input and output IPsec caches. If a hit is found, store the hit in
- * the stream DB to be used when creating packets.
- */
-void resolve_stream_db(void);
-
-/**
- * Create IPv4 packet for stream
- *
- * Create one ICMP test packet based on the stream structure. If an input
- * IPsec cache entry is associated with the stream, build a packet that should
- * successfully match that entry and be correctly decoded by it.
- *
- * @param stream Stream DB entry
- * @param dmac Destination MAC address to use
- * @param pkt_pool Packet buffer pool to allocate from
- *
- * @return packet else ODP_PACKET_INVALID
- */
-odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
- uint8_t *dmac,
- odp_pool_t pkt_pool);
-
-/**
- * Verify an IPv4 packet received on a loop output queue
- *
- * @param stream Stream to verify the packet against
- * @param pkt Packet to verify
- *
- * @return TRUE if packet verifies else FALSE
- */
-odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream,
- odp_packet_t pkt);
-
-/**
- * Create input packets based on the stream DB
- *
- * Create input packets based on the configured streams and enqueue them
- * into loop interface input queues. Once packet processing starts these
- * packets will be remomved and processed as if they had come from a normal
- * packet interface.
- *
- * @return number of streams successfully processed
- */
-int create_stream_db_inputs(void);
-
-/**
- * Verify stream DB outputs
- *
- * For each stream, poll the output loop interface queue and verify
- * any packets found on it
- *
- * @return TRUE if all packets on all streams verified else FALSE
- */
-odp_bool_t verify_stream_db_outputs(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/example/ipsec_api/.gitignore b/example/ipsec_api/.gitignore
new file mode 100644
index 000000000..77f399c54
--- /dev/null
+++ b/example/ipsec_api/.gitignore
@@ -0,0 +1,2 @@
+odp_ipsec_api
+pktio_env
diff --git a/example/ipsec_api/Makefile.am b/example/ipsec_api/Makefile.am
new file mode 100644
index 000000000..5a71d04e5
--- /dev/null
+++ b/example/ipsec_api/Makefile.am
@@ -0,0 +1,76 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_ipsec_api
+
+if test_example
+TESTS = \
+ odp_ipsec_api_run_ah_in.sh \
+ odp_ipsec_api_run_ah_out.sh \
+ odp_ipsec_api_run_ah_tun_in.sh \
+ odp_ipsec_api_run_ah_tun_out.sh \
+ odp_ipsec_api_run_esp_in.sh \
+ odp_ipsec_api_run_esp_out.sh \
+ odp_ipsec_api_run_esp_tun_in.sh \
+ odp_ipsec_api_run_esp_tun_out.sh \
+ odp_ipsec_api_run_live.sh \
+ odp_ipsec_api_run_router.sh \
+ odp_ipsec_api_run_simple.sh
+endif
+
+EXTRA_DIST = \
+ odp_ipsec_api_run_ah_in.sh \
+ odp_ipsec_api_run_ah_out.sh \
+ odp_ipsec_api_run_ah_tun_in.sh \
+ odp_ipsec_api_run_ah_tun_out.sh \
+ odp_ipsec_api_run_esp_in.sh \
+ odp_ipsec_api_run_esp_out.sh \
+ odp_ipsec_api_run_esp_tun_in.sh \
+ odp_ipsec_api_run_esp_tun_out.sh \
+ odp_ipsec_api_run_live.sh \
+ odp_ipsec_api_run_router.sh \
+ odp_ipsec_api_run_simple.sh
+
+odp_ipsec_api_SOURCES = \
+ odp_ipsec.c \
+ odp_ipsec_sa_db.c \
+ odp_ipsec_sa_db.h \
+ odp_ipsec_sp_db.c \
+ odp_ipsec_sp_db.h \
+ odp_ipsec_fwd_db.c \
+ odp_ipsec_fwd_db.h \
+ odp_ipsec_cache.c \
+ odp_ipsec_cache.h \
+ odp_ipsec_misc.h
+
+if WITH_OPENSSL
+odp_ipsec_api_SOURCES += \
+ odp_ipsec_stream.c \
+ odp_ipsec_stream.h
+
+AM_CPPFLAGS = $(OPENSSL_CPPFLAGS)
+LDADD += $(OPENSSL_LIBS)
+
+else
+AM_CPPFLAGS = -DNO_OPENSSL
+endif
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s ../../platform/$(with_platform)/test/example/ipsec_api/pktio_env pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/ipsec_api/README b/example/ipsec_api/README
new file mode 100644
index 000000000..7a392a728
--- /dev/null
+++ b/example/ipsec_api/README
@@ -0,0 +1,155 @@
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2014-2018 Linaro Limited
+Copyright (c) 2020 Nokia
+
+1. Intro
+
+The IPsec API example application "odp_ipsec_api" functions as a simple L3 IPv4
+router which supports IPsec AH and ESP protocols in both transmit and receive
+directions. AH and ESP protocols are not supported simultaneously.
+
+With ESP, the application supports 3DES and NULL encryption algorithms, with
+NULL authentication. With AH, HMAC-MD5 or HMAC-SHA-256 integrity algorithms are
+supported.
+
+2. Prerequisites
+
+ 2.1 SSL development libraries
+
+Development has been done to this point with the openssl-devel libraries,
+the makefile specifically links with "-lcrypto".
+
+3. Topology
+
+The following test topology was used for development. Each of the VMs
+is running Fedora 32. Sanity testing consists of pinging VM2 from VM0
+such that the packets traverse VM1. Packets between VM1 and VM2 are
+IPsec AH or ESP encapsulated.
+
+ VM0 VM1 (UUT) VM2
++------------+ +--------------+ +------------+
+| | (clear) | | (crypto) | |
+| | subnet | | subnet | |
+| p7p1 |<---------------->| p7p1 p8p1 |<---------------->| p7p1 |
+| .2 | 192.168.111.0 | .1 .1 | 192.168.222.0 | .2 |
+| | | | | |
++------------+ +--------------+ +------------+
+
+4. VM configurations
+
+ 4.1 VM0 configuration
+
+VM0 has the following interface configuration:
+
+$ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+DEVICE=p7p1
+HWADDR=08:00:27:76:B5:E0
+BOOTPROTO=static
+IPADDR=192.168.111.2
+NETMASK=255.255.255.0
+ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM0:
+
+$ sudo ip route add 192.168.222.0/24 via 192.168.111.1
+$ sudo arp -s 192.168.111.1 08:00:27:04:BF:8C
+
+ 4.2 VM1 configuration
+
+For the unit under test, IP forwarding and IP tables were disabled.
+
+VM1 has the following interface configurations:
+
+$ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+DEVICE=p7p1
+HWADDR=08:00:27:04:BF:8C
+BOOTPROTO=static
+IPADDR=192.168.111.1
+NETMASK=255.255.255.0
+ONBOOT=yes
+
+$ cat /etc/sysconfig/network-scripts/ifcfg-p8p1
+DEVICE=p8p1
+HWADDR=08:00:27:4C:55:CC
+BOOTPROTO=static
+IPADDR=192.168.222.1
+NETMASK=255.255.255.0
+ONBOOT=yes
+
+The application is launched on VM1 with the following command:
+
+$ sudo ./odp_ipsec_api -i p7p1,p8p1 \
+-r 192.168.111.2/32,p7p1,08:00:27:76:B5:E0 \
+-r 192.168.222.2/32,p8p1,08:00:27:F5:8B:DB \
+-p 192.168.111.0/24,192.168.222.0/24,out,esp \
+-e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+-p 192.168.222.0/24,192.168.111.0/24,in,esp \
+-e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+-c 2 -m 0
+
+ 4.3 VM2 configuration
+
+VM2 has the following interface configuration:
+
+$ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+DEVICE=p7p1
+HWADDR=08:00:27:F5:8B:DB
+BOOTPROTO=static
+IPADDR=192.168.222.2
+NETMASK=255.255.255.0
+ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM2:
+
+$ sudo ip route add 192.168.111.0/24 via 192.168.222.1
+$ sudo arp -s 192.168.222.1 08:00:27:4C:55:CC
+
+VM2 must be setup with an IPsec configuration complementing the configuration
+used by the "odp_ipsec_api" application running on VM1. The configuration is
+applied using "setkey" (provided by ipsec-tools package).
+
+VM2 uses the following setkey configuration:
+
+$ cat setkey_vm2.conf
+# Flush the SAD and SPD
+flush;
+spdflush;
+add 192.168.111.2 192.168.222.2 esp 0x201 -E 3des-cbc
+0x656c8523255ccc23a66c1917aa0cf30991fce83532a4b224;
+add 192.168.222.2 192.168.111.2 esp 0x301 -E 3des-cbc
+0xc966199f24d095f3990a320d749056401e82b26570320292;
+spdadd 192.168.111.2 192.168.222.2 any -P in ipsec esp/transport//require;
+spdadd 192.168.222.2 192.168.111.2 any -P out ipsec esp/transport//require;
+
+Apply the setkey configuration:
+$ sudo setkey -f setkey_vm2.conf
+
+5. Sanity Test with Real Traffic
+
+Once all three VMs have been configured, static ARP and route entries added,
+setkey configuration applied, and odp_ipsec_api application is running, VM0
+should be able to ping VM2 at the 192.168.222.2 address.
+
+At VM0 console issue the ping to VM2's address:
+
+$ sudo ping -c 2 -i 0.1 192.168.222.2
+PING 192.168.222.2 (192.168.222.2) 56(84) bytes of data.
+64 bytes from 192.168.222.2: icmp_seq=1 ttl=64 time=0.614 ms
+64 bytes from 192.168.222.2: icmp_seq=2 ttl=64 time=0.560 ms
+
+At VM2 console use tcpdump to observe IPsec packets:
+
+$ sudo tcpdump -nt -i p7p1
+dropped privs to tcpdump
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on enp0s9, link-type EN10MB (Ethernet), capture size 262144 bytes
+IP 192.168.111.2 > 192.168.222.2: ESP(spi=0x00000201,seq=0x196), length 88
+IP 192.168.222.2 > 192.168.111.2: ESP(spi=0x00000301,seq=0xf4), length 88
+IP 192.168.111.2 > 192.168.222.2: ESP(spi=0x00000201,seq=0x197), length 88
+IP 192.168.222.2 > 192.168.111.2: ESP(spi=0x00000301,seq=0xf5), length 88
+
+6. Standalone Loopback Tests
+
+Bash script files are also included to run several simple loopback tests that
+do not require any packet IO. The scripts create internal "loopback" packet
+interfaces.
diff --git a/example/ipsec_api/odp_ipsec.c b/example/ipsec_api/odp_ipsec.c
new file mode 100644
index 000000000..b6cc9ee22
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec.c
@@ -0,0 +1,1406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @example ipsec_api/odp_ipsec.c
+ *
+ * IPsec example application using ODP IPsec API
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+#include <odp_ipsec_sp_db.h>
+#include <odp_ipsec_fwd_db.h>
+#include <odp_ipsec_cache.h>
+
+#ifndef NO_OPENSSL
+#include <odp_ipsec_stream.h>
+#else
+static void init_stream_db(void) {}
+static void deinit_stream_db(void) {}
+static void resolve_stream_db(void) {}
+static int create_stream_db_inputs(void)
+{
+ return 0;
+}
+
+static odp_bool_t verify_stream_db_outputs(void)
+{
+ return true;
+}
+
+static int create_stream_db_entry(char *input ODP_UNUSED)
+{
+ return -1;
+}
+#endif
+
+/* maximum number of worker threads */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+#define MAX_POLL_QUEUES 256
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ unsigned int cpu_count;
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ odp_ipsec_op_mode_t mode; /**< IPsec operation mode */
+ odp_pool_t pool; /**< Buffer pool for packet IO */
+ char *if_str; /**< Storage for interface names */
+ odp_bool_t lookup;
+} appl_args_t;
+
+/**
+ * Grouping of both parsed CL args and global application data
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ odp_shm_t shm;
+ odp_pool_t ctx_pool;
+ odp_pool_t pkt_pool;
+ /** ORDERED queue for per packet crypto API completion events */
+ odp_queue_t completionq;
+ /** Synchronize threads before packet processing begins */
+ odp_barrier_t sync_barrier;
+ odp_queue_t poll_queues[MAX_POLL_QUEUES];
+ int num_polled_queues;
+ /* Stop workers if set to 1 */
+ odp_atomic_u32_t exit_threads;
+} global_data_t;
+
+/* helper funcs */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Buffer pool for packet IO
+ */
+#define SHM_PKT_POOL_BUF_COUNT 1024
+#define SHM_PKT_POOL_BUF_SIZE 4096
+#define SHM_PKT_POOL_SIZE (SHM_PKT_POOL_BUF_COUNT * SHM_PKT_POOL_BUF_SIZE)
+
+/**
+ * Packet processing states/steps
+ */
+typedef enum {
+ PKT_STATE_INPUT_VERIFY, /**< Verify IPv4 and ETH */
+ PKT_STATE_IPSEC_IN_CLASSIFY, /**< Initiate input IPsec */
+ PKT_STATE_ROUTE_LOOKUP, /**< Use DST IP to find output IF */
+ PKT_STATE_IPSEC_OUT_CLASSIFY, /**< Intiate output IPsec */
+ PKT_STATE_TRANSMIT, /**< Send packet to output IF queue */
+} pkt_state_e;
+
+/**
+ * Packet processing result codes
+ */
+typedef enum {
+ PKT_CONTINUE, /**< No events posted, keep processing */
+ PKT_POSTED, /**< Event posted, stop processing */
+ PKT_DROP, /**< Reason to drop detected, stop processing */
+ PKT_DONE /**< Finished with packet, stop processing */
+} pkt_disposition_e;
+
+/**
+ * Per packet processing context
+ */
+typedef struct {
+ odp_buffer_t buffer; /**< Buffer for context */
+ pkt_state_e state; /**< Next processing step */
+ odp_pktout_queue_t pktout; /**< Packet output queue */
+ odp_pktio_t pktio; /**< Packet I/O */
+ odph_ethhdr_t eth; /**< L2 header */
+} pkt_ctx_t;
+
+#define SHM_CTX_POOL_BUF_SIZE (sizeof(pkt_ctx_t))
+#define SHM_CTX_POOL_BUF_COUNT (SHM_PKT_POOL_BUF_COUNT)
+#define SHM_CTX_POOL_SIZE (SHM_CTX_POOL_BUF_COUNT * SHM_CTX_POOL_BUF_SIZE)
+
+static global_data_t *global;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (global == NULL)
+ return;
+ odp_atomic_store_u32(&global->exit_threads, 1);
+}
+
+/**
+ * Allocate per packet processing context and associate it with
+ * packet buffer
+ *
+ * @param pkt Packet
+ *
+ * @return pointer to context area
+ */
+static
+pkt_ctx_t *alloc_pkt_ctx(odp_packet_t pkt)
+{
+ odp_buffer_t ctx_buf = odp_buffer_alloc(global->ctx_pool);
+ pkt_ctx_t *ctx;
+
+ if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf))
+ return NULL;
+
+ ctx = odp_buffer_addr(ctx_buf);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->buffer = ctx_buf;
+ odp_packet_user_ptr_set(pkt, ctx);
+
+ return ctx;
+}
+
+/**
+ * Release per packet resources
+ *
+ * @param ctx Packet context
+ */
+static
+void free_pkt_ctx(pkt_ctx_t *ctx)
+{
+ odp_buffer_free(ctx->buffer);
+}
+
+/**
+ * Example supports either polling queues or using odp_schedule
+ */
+typedef odp_queue_t (*queue_create_func_t)
+ (const char *, const odp_queue_param_t *);
+typedef odp_event_t (*schedule_func_t) (odp_queue_t *);
+
+static queue_create_func_t queue_create;
+static schedule_func_t schedule_fn;
+
+/**
+ * odp_queue_create wrapper to enable polling versus scheduling
+ */
+static
+odp_queue_t polled_odp_queue_create(const char *name,
+ const odp_queue_param_t *param)
+{
+ odp_queue_t my_queue;
+ odp_queue_param_t qp;
+ odp_queue_type_t type;
+
+ odp_queue_param_init(&qp);
+ if (param)
+ memcpy(&qp, param, sizeof(odp_queue_param_t));
+
+ type = qp.type;
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ printf("%s: change %s to PLAIN\n", __func__, name);
+ qp.type = ODP_QUEUE_TYPE_PLAIN;
+ }
+
+ my_queue = odp_queue_create(name, &qp);
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ global->poll_queues[global->num_polled_queues++] = my_queue;
+ printf("%s: adding %" PRIu64 "\n", __func__,
+ odp_queue_to_u64(my_queue));
+ }
+
+ return my_queue;
+}
+
+static inline
+odp_event_t odp_schedule_cb(odp_queue_t *from)
+{
+ return odp_schedule(from, ODP_SCHED_NO_WAIT);
+}
+
+/**
+ * odp_schedule replacement to poll queues versus using ODP scheduler
+ */
+static
+odp_event_t polled_odp_schedule_cb(odp_queue_t *from)
+{
+ int idx = 0;
+
+ while (idx < global->num_polled_queues) {
+ odp_queue_t queue = global->poll_queues[idx++];
+ odp_event_t ev;
+
+ ev = odp_queue_deq(queue);
+
+ if (ODP_EVENT_INVALID != ev) {
+ if (from)
+ *from = queue;
+
+ return ev;
+ }
+ }
+
+ if (from)
+ *from = ODP_QUEUE_INVALID;
+
+ return ODP_EVENT_INVALID;
+}
+
+/**
+ * IPsec pre argument processing initialization
+ */
+static
+void ipsec_init_pre(void)
+{
+ odp_queue_param_t qparam;
+
+ /*
+ * Create queues
+ *
+ * - completion queue (should eventually be ORDERED)
+ * - sequence number queue (must be ATOMIC)
+ */
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global->completionq = queue_create("completion", &qparam);
+ if (ODP_QUEUE_INVALID == global->completionq) {
+ ODPH_ERR("Error: completion queue creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ /* Initialize our data bases */
+ init_sp_db();
+ init_sa_db();
+ init_tun_db();
+ init_ipsec_cache();
+}
+
+/**
+ * IPsec post argument processing initialization
+ *
+ * Resolve SP DB with SA DB and create corresponding IPsec cache entries
+ *
+ * @param api_mode Mode to use for IPsec operation
+ */
+static
+void ipsec_init_post(odp_ipsec_op_mode_t api_mode)
+{
+ sp_db_entry_t *entry;
+ odp_ipsec_config_t ipsec_config;
+ odp_ipsec_capability_t ipsec_cap;
+
+ if (odp_ipsec_capability(&ipsec_cap) != ODP_IPSEC_OK) {
+ ODPH_ERR("Error: failure getting IPSec caps\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_ipsec_config_init(&ipsec_config);
+ ipsec_config.inbound.parse_level = ODP_PROTO_LAYER_ALL;
+ ipsec_config.inbound_mode = api_mode;
+ ipsec_config.outbound_mode = api_mode;
+ ipsec_config.inbound.default_queue = global->completionq;
+ if (odp_ipsec_config(&ipsec_config) != ODP_IPSEC_OK) {
+ ODPH_ERR("Error: failure setting IPSec config\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Attempt to find appropriate SA for each SP */
+ for (entry = sp_db->list; NULL != entry; entry = entry->next) {
+ sa_db_entry_t *cipher_sa = NULL;
+ sa_db_entry_t *auth_sa = NULL;
+ tun_db_entry_t *tun = NULL;
+
+ if (entry->esp) {
+ cipher_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 1);
+ tun = find_tun_db_entry(cipher_sa->src_ip,
+ cipher_sa->dst_ip);
+ }
+ if (entry->ah) {
+ auth_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 0);
+ tun = find_tun_db_entry(auth_sa->src_ip,
+ auth_sa->dst_ip);
+ }
+
+ if (cipher_sa || auth_sa) {
+ if (create_ipsec_cache_entry(cipher_sa,
+ auth_sa,
+ tun,
+ entry->input,
+ global->completionq)) {
+ ODPH_ERR("Error: IPSec cache entry failed.\n"
+ );
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ printf(" WARNING: SA not found for SP\n");
+ dump_sp_db_entry(entry);
+ }
+ }
+}
+
+#ifndef NO_OPENSSL
+static
+int check_stream_db_in(const char *intf)
+{
+ stream_db_entry_t *stream = NULL;
+
+ /* For each stream look for input and output IPsec entries */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ if (!strcmp(stream->input.intf, intf))
+ return 1;
+ }
+
+ return 0;
+}
+
+static
+int check_stream_db_out(const char *intf)
+{
+ stream_db_entry_t *stream = NULL;
+
+ /* For each stream look for input and output IPsec entries */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ if (!strcmp(stream->output.intf, intf))
+ return 1;
+ }
+
+ return 0;
+}
+#else
+static
+int check_stream_db_in(const char *intf ODP_UNUSED)
+{
+ return 0;
+}
+
+static
+int check_stream_db_out(const char *intf ODP_UNUSED)
+{
+ return 0;
+}
+#endif
+
+/**
+ * Initialize interface
+ *
+ * Initialize ODP pktio and queues, query MAC address and update
+ * forwarding database.
+ *
+ * @param intf Interface name string
+ */
+static
+void initialize_intf(char *intf)
+{
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout;
+ odp_queue_t inq;
+ int ret;
+ uint8_t src_mac[ODPH_ETHADDR_LEN];
+ char src_mac_str[MAX_STRING];
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES") ||
+ check_stream_db_out(intf))
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+ else
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ /*
+ * Open a packet IO instance for thread and get default output queue
+ */
+ pktio = odp_pktio_open(intf, global->pkt_pool, &pktio_param);
+ if (ODP_PKTIO_INVALID == pktio) {
+ ODPH_ERR("Error: pktio create failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Error: pktin config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Error: pktout config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktin_event_queue(pktio, &inq, 1) != 1) {
+ ODPH_ERR("Error: failed to get input queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
+ ODPH_ERR("Error: failed to get pktout queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktio_capability(pktio, &capa) != 0) {
+ ODPH_ERR("Error: failed to get capabilities for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktio_config_init(&config);
+ if (check_stream_db_in(intf) &&
+ global->appl.mode == ODP_IPSEC_OP_MODE_INLINE)
+ config.inbound_ipsec = capa.config.inbound_ipsec;
+ if (check_stream_db_out(intf) &&
+ global->appl.mode == ODP_IPSEC_OP_MODE_INLINE)
+ config.outbound_ipsec = capa.config.outbound_ipsec;
+
+ if (odp_pktio_config(pktio, &config) != 0) {
+ ODPH_ERR("Error: failed to set config for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = odp_pktio_start(pktio);
+ if (ret) {
+ ODPH_ERR("Error: unable to start %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read the source MAC address for this interface */
+ ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
+ if (ret <= 0) {
+ ODPH_ERR("Error: failed during MAC address get for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Created pktio:%02" PRIu64 ", queue mode (ATOMIC queues)\n"
+ " default pktio%02" PRIu64 "-INPUT queue:%" PRIu64 "\n"
+ " source mac address %s\n",
+ odp_pktio_to_u64(pktio), odp_pktio_to_u64(pktio),
+ odp_queue_to_u64(inq),
+ mac_addr_str(src_mac_str, src_mac));
+
+ /* Resolve any routes using this interface for output */
+ resolve_fwd_db(intf, pktio, pktout, src_mac);
+}
+
+/**
+ * Packet Processing - Input verification
+ *
+ * @param pkt Packet to inspect
+ * @param ctx Packet process context (not used)
+ *
+ * @return PKT_CONTINUE if good, supported packet else PKT_DROP
+ */
+static
+pkt_disposition_e do_input_verify(odp_packet_t pkt,
+ pkt_ctx_t *ctx ODP_UNUSED)
+{
+ if (odp_unlikely(odp_packet_has_error(pkt)))
+ return PKT_DROP;
+
+ if (!odp_packet_has_eth(pkt))
+ return PKT_DROP;
+
+ if (!odp_packet_has_ipv4(pkt))
+ return PKT_DROP;
+
+ return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Route lookup in forwarding database
+ *
+ * @param pkt Packet to route
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if route found else PKT_DROP
+ */
+static
+pkt_disposition_e do_route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ fwd_db_entry_t *entry;
+
+ entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
+
+ if (entry) {
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+
+ if (l3_offset > sizeof(odph_ethhdr_t))
+ odp_packet_pull_head(pkt,
+ l3_offset - sizeof(odph_ethhdr_t));
+ else
+ odp_packet_push_head(pkt,
+ sizeof(odph_ethhdr_t) - l3_offset);
+
+ memcpy(&ctx->eth.dst, entry->dst_mac, ODPH_ETHADDR_LEN);
+ memcpy(&ctx->eth.src, entry->src_mac, ODPH_ETHADDR_LEN);
+ ctx->eth.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ if (global->appl.mode != ODP_IPSEC_OP_MODE_INLINE) {
+ odp_packet_l2_offset_set(pkt, 0);
+ odp_packet_copy_from_mem(pkt, 0, ODPH_ETHHDR_LEN,
+ &ctx->eth);
+ }
+
+ ctx->pktio = entry->pktio;
+ ctx->pktout = entry->pktout;
+
+ return PKT_CONTINUE;
+ }
+
+ return PKT_DROP;
+}
+
+/**
+ * Packet Processing - Input IPsec packet classification
+ *
+ * Verify the received packet has IPsec headers and a match
+ * in the IPsec cache, if so issue crypto request else skip
+ * input crypto.
+ *
+ * @param pkt Packet to classify
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_in_classify(odp_packet_t *ppkt)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*ppkt, NULL);
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_in_param_t in_param;
+ int rc;
+
+ /* Check IP header for IPSec protocols and look it up */
+ locate_ipsec_headers(ip, &ah, &esp);
+ if (!ah && !esp)
+ return PKT_CONTINUE;
+ entry = find_ipsec_cache_entry_in(odp_be_to_cpu_32(ip->src_addr),
+ odp_be_to_cpu_32(ip->dst_addr),
+ ah,
+ esp);
+ if (!entry)
+ return PKT_CONTINUE;
+
+ memset(&in_param, 0, sizeof(in_param));
+ if (global->appl.lookup) {
+ in_param.num_sa = 0;
+ in_param.sa = NULL;
+ } else {
+ in_param.num_sa = 1;
+ in_param.sa = &entry->ipsec_sa;
+ }
+
+ /* Issue crypto request */
+ if (global->appl.mode != ODP_IPSEC_OP_MODE_SYNC) {
+ rc = odp_ipsec_in_enq(ppkt, 1, &in_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_POSTED;
+ } else {
+ int out = 1;
+ odp_ipsec_packet_result_t result;
+
+ rc = odp_ipsec_in(ppkt, 1, ppkt, &out, &in_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ if (odp_ipsec_result(&result, *ppkt) < 0) {
+ ODPH_DBG("odp_ipsec_result() failed\n");
+ return PKT_DROP;
+ }
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in inbound IPsec processing\n");
+ return PKT_DROP;
+ }
+ return PKT_CONTINUE;
+ }
+}
+
+/**
+ * Packet Processing - Output IPsec packet classification
+ *
+ * Verify the outbound packet has a match in the IPsec cache,
+ * if so issue prepend IPsec headers and prepare parameters
+ * for crypto API call. Post the packet to ATOMIC queue so
+ * that sequence numbers can be applied in packet order as
+ * the next processing step.
+ *
+ * @param pkt Packet to classify
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_classify(odp_packet_t *ppkt, pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*ppkt, NULL);
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_out_param_t out_param;
+ int rc;
+
+ /* Find record */
+ entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
+ odp_be_to_cpu_32(ip->dst_addr),
+ ip->proto);
+ if (!entry)
+ return PKT_CONTINUE;
+
+ memset(&out_param, 0, sizeof(out_param));
+ out_param.num_sa = 1;
+ out_param.num_opt = 0;
+ out_param.sa = &entry->ipsec_sa;
+ out_param.opt = NULL;
+
+ /* Issue crypto request */
+ if (global->appl.mode == ODP_IPSEC_OP_MODE_INLINE) {
+ odp_ipsec_out_inline_param_t inline_param;
+
+ inline_param.pktio = ctx->pktio;
+ inline_param.outer_hdr.ptr = (void *)&ctx->eth;
+ inline_param.outer_hdr.len = ODPH_ETHHDR_LEN;
+ rc = odp_ipsec_out_inline(ppkt, 1, &out_param, &inline_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_DONE;
+ } else if (global->appl.mode != ODP_IPSEC_OP_MODE_SYNC) {
+ rc = odp_ipsec_out_enq(ppkt, 1, &out_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_POSTED;
+ } else {
+ int out = 1;
+ odp_ipsec_packet_result_t result;
+
+ rc = odp_ipsec_out(ppkt, 1, ppkt, &out, &out_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ if (odp_ipsec_result(&result, *ppkt) < 0) {
+ ODPH_DBG("odp_ipsec_result() failed\n");
+ return PKT_DROP;
+ }
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in outbound IPsec processing\n");
+ return PKT_DROP;
+ }
+ return PKT_CONTINUE;
+ }
+}
+
+/**
+ * Packet IO worker thread
+ *
+ * Loop calling odp_schedule to obtain packets from one of three sources,
+ * and continue processing the packet based on the state stored in its
+ * per packet context.
+ *
+ * - Input interfaces (i.e. new work)
+ * - Sequence number assignment queue
+ * - Per packet crypto API completion queue
+ *
+ * @param arg Required by "odph_thread_create", unused
+ *
+ * @return NULL (should never return)
+ */
+static
+int pktio_thread(void *arg ODP_UNUSED)
+{
+ int thr;
+ odp_packet_t pkt;
+ odp_event_t ev;
+ unsigned long pkt_cnt = 0;
+
+ thr = odp_thread_id();
+
+ printf("Pktio thread [%02i] starts\n", thr);
+
+ odp_barrier_wait(&global->sync_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ pkt_disposition_e rc = PKT_CONTINUE;
+ pkt_ctx_t *ctx;
+ odp_queue_t dispatchq;
+ odp_event_subtype_t subtype;
+
+ /* Use schedule to get event from any input queue */
+ ev = schedule_fn(&dispatchq);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ /* Determine new work versus completion or sequence number */
+ if (ODP_EVENT_PACKET == odp_event_types(ev, &subtype)) {
+ pkt = odp_packet_from_event(ev);
+ if (ODP_EVENT_PACKET_BASIC == subtype) {
+ ctx = alloc_pkt_ctx(pkt);
+ if (!ctx) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ ctx->state = PKT_STATE_INPUT_VERIFY;
+ } else if (ODP_EVENT_PACKET_IPSEC == subtype) {
+ odp_ipsec_packet_result_t result;
+
+ if (odp_unlikely(odp_ipsec_result(&result,
+ pkt) < 0)) {
+ ODPH_DBG("Error Event\n");
+ odp_event_free(ev);
+ continue;
+ }
+
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in IPsec\n");
+ rc = PKT_DROP;
+ }
+
+ if (result.flag.inline_mode) {
+ ctx = alloc_pkt_ctx(pkt);
+ if (!ctx) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ if (odp_unlikely(
+ odp_packet_has_error(pkt) ||
+ !odp_packet_has_ipv4(pkt)))
+ rc = PKT_DROP;
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ } else {
+ ctx = odp_packet_user_ptr(pkt);
+ }
+ } else {
+ ODPH_DBG("Unsupported Packet\n");
+ odp_event_free(ev);
+ continue;
+ }
+ } else if (ODP_EVENT_IPSEC_STATUS == odp_event_type(ev)) {
+ odp_ipsec_status_t status;
+
+ if (odp_unlikely(odp_ipsec_status(&status, ev) < 0)) {
+ ODPH_DBG("Error Event\n");
+ odp_event_free(ev);
+ continue;
+ }
+
+ printf("IPsec status %d result %d for SA %" PRIx64 "\n",
+ status.id, status.result,
+ odp_ipsec_sa_to_u64(status.sa));
+
+ odp_event_free(ev);
+ continue;
+ } else {
+ abort();
+ }
+
+ /*
+ * We now have a packet and its associated context. Loop here
+ * executing processing based on the current state value stored
+ * in the context as long as the processing return code
+ * indicates PKT_CONTINUE.
+ *
+ * For other return codes:
+ *
+ * o PKT_DONE - finished with the packet
+ * o PKT_DROP - something incorrect about the packet, drop it
+ * o PKT_POSTED - packet/event has been queued for later
+ */
+ while (rc == PKT_CONTINUE) {
+ switch (ctx->state) {
+ case PKT_STATE_INPUT_VERIFY:
+
+ rc = do_input_verify(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_IN_CLASSIFY:
+
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ rc = do_ipsec_in_classify(&pkt);
+ break;
+
+ case PKT_STATE_ROUTE_LOOKUP:
+
+ rc = do_route_fwd_db(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_OUT_CLASSIFY:
+ ctx->state = PKT_STATE_TRANSMIT;
+ rc = do_ipsec_out_classify(&pkt, ctx);
+ break;
+
+ case PKT_STATE_TRANSMIT:
+
+ if (odp_pktout_send(ctx->pktout, &pkt, 1) < 1)
+ rc = PKT_DROP;
+ else
+ rc = PKT_DONE;
+ break;
+
+ default:
+ rc = PKT_DROP;
+ break;
+ }
+ }
+
+ /* Free context on drop or transmit */
+ if ((PKT_DROP == rc) || (PKT_DONE == rc))
+ free_pkt_ctx(ctx);
+
+ /* Check for drop */
+ if (PKT_DROP == rc)
+ odp_packet_free(pkt);
+
+ /* Print packet counts every once in a while */
+ if (PKT_DONE == rc) {
+ if (odp_unlikely(pkt_cnt++ % 1000 == 0)) {
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ODP ipsec example main function
+ */
+int
+main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int num_workers;
+ int i;
+ int stream_count;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odp_ipsec_capability_t ipsec_capa;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odp_event_t ev;
+
+ /* create by default scheduled queues */
+ queue_create = odp_queue_create;
+ schedule_fn = odp_schedule_cb;
+
+ /* check for using poll queues */
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES")) {
+ queue_create = polled_odp_queue_create;
+ schedule_fn = polled_odp_schedule_cb;
+ }
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Signal handler has to be registered before global init in case ODP
+ * implementation creates internal threads/processes. */
+ signal(SIGINT, sig_handler);
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_ipsec_capability(&ipsec_capa)) {
+ ODPH_ERR("Error: IPsec capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (queue_create == polled_odp_queue_create) {
+ if (!ipsec_capa.queue_type_plain) {
+ ODPH_ERR("Error: Plain type dest queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (!ipsec_capa.queue_type_sched) {
+ ODPH_ERR("Error: scheduled type dest queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+
+ if (NULL == global) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(global, 0, sizeof(global_data_t));
+ global->shm = shm;
+ odp_atomic_init_u32(&global->exit_threads, 0);
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
+ /* Must init our databases before parsing args */
+ ipsec_init_pre();
+ init_fwd_db();
+ init_stream_db();
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &global->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &global->appl);
+
+ num_workers = MAX_WORKERS;
+ if (global->appl.cpu_count && global->appl.cpu_count < MAX_WORKERS)
+ num_workers = global->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ /* Create a barrier to synchronize thread startup */
+ odp_barrier_init(&global->sync_barrier, num_workers);
+
+ /* Create packet buffer pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
+ params.type = ODP_POOL_PACKET;
+
+ global->pkt_pool = odp_pool_create("packet_pool", &params);
+
+ if (ODP_POOL_INVALID == global->pkt_pool) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create context buffer pool */
+ params.buf.size = SHM_CTX_POOL_BUF_SIZE;
+ params.buf.align = 0;
+ params.buf.num = SHM_CTX_POOL_BUF_COUNT;
+ params.type = ODP_POOL_BUFFER;
+
+ global->ctx_pool = odp_pool_create("ctx_pool", &params);
+
+ if (ODP_POOL_INVALID == global->ctx_pool) {
+ ODPH_ERR("Error: context pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Populate our IPsec cache */
+ printf("Using %s mode for IPsec API\n\n",
+ (ODP_IPSEC_OP_MODE_SYNC == global->appl.mode) ? "SYNC" :
+ (ODP_IPSEC_OP_MODE_ASYNC == global->appl.mode) ? "ASYNC" :
+ "INLINE");
+ ipsec_init_post(global->appl.mode);
+
+ /* Initialize interfaces (which resolves FWD DB entries */
+ for (i = 0; i < global->appl.if_count; i++)
+ initialize_intf(global->appl.if_names[i]);
+
+ /* If we have test streams build them before starting workers */
+ resolve_stream_db();
+ stream_count = create_stream_db_inputs();
+ if (stream_count < 0) {
+ ODPH_ERR("Error: creating input packets failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Create and init worker threads
+ */
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = pktio_thread;
+ thr_param.arg = NULL;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ /* If there are streams attempt to verify them. Otherwise, run until
+ * SIGINT is received. */
+ if (stream_count) {
+ odp_bool_t done;
+
+ do {
+ done = verify_stream_db_outputs();
+ usleep(100000);
+ } while (!done);
+ printf("All received\n");
+ odp_atomic_store_u32(&global->exit_threads, 1);
+ }
+ odph_thread_join(thread_tbl, num_workers);
+
+ /* Stop and close used pktio devices */
+ for (i = 0; i < global->appl.if_count; i++) {
+ odp_pktio_t pktio = odp_pktio_lookup(global->appl.if_names[i]);
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
+ ODPH_ERR("Error: failed to close pktio %s\n",
+ global->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ free(global->appl.if_names);
+ free(global->appl.if_str);
+
+ if (destroy_ipsec_cache())
+ ODPH_ERR("Error: crypto session destroy failed\n");
+
+ /* Drop any remaining events. ipsec_sa_disable sends status event in
+ * async mode */
+ while ((ev = schedule_fn(NULL)) != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ if (odp_queue_destroy(global->completionq))
+ ODPH_ERR("Error: queue destroy failed\n");
+
+ if (odp_pool_destroy(global->pkt_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+ if (odp_pool_destroy(global->ctx_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+
+ shm = odp_shm_lookup("shm_ipsec_cache");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_ipsec_cache failed\n");
+ shm = odp_shm_lookup("shm_fwd_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_fwd_db failed\n");
+ shm = odp_shm_lookup("shm_sa_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sa_db failed\n");
+ shm = odp_shm_lookup("shm_tun_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_tun_db failed\n");
+ shm = odp_shm_lookup("shm_sp_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sp_db failed\n");
+
+ deinit_stream_db();
+
+ if (odp_shm_free(global->shm)) {
+ ODPH_ERR("Error: shm free global data failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *token;
+ size_t len;
+ int rc = 0;
+ int i;
+
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"lookup", no_argument, NULL, 'l'},
+ {"mode", required_argument, NULL, 'm'}, /* return 'm' */
+ {"route", required_argument, NULL, 'r'}, /* return 'r' */
+ {"policy", required_argument, NULL, 'p'}, /* return 'p' */
+ {"ah", required_argument, NULL, 'a'}, /* return 'a' */
+ {"esp", required_argument, NULL, 'e'}, /* return 'e' */
+ {"tunnel", required_argument, NULL, 't'}, /* return 't' */
+ {"stream", required_argument, NULL, 's'}, /* return 's' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:i:m:r:p:a:e:t:s:lh";
+
+ appl_args->cpu_count = 1; /* use one worker by default */
+
+ printf("\nParsing command line options\n");
+
+ while (!rc) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (-1 == opt)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (0 == len) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_str = malloc(len);
+ if (appl_args->if_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ appl_args->if_count = i;
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'l':
+ appl_args->lookup = true;
+ break;
+
+ case 'm':
+ appl_args->mode = atoi(optarg);
+ break;
+
+ case 'r':
+ rc = create_fwd_db_entry(optarg, appl_args->if_names,
+ appl_args->if_count);
+ break;
+
+ case 'p':
+ rc = create_sp_db_entry(optarg, FALSE);
+ break;
+
+ case 'a':
+ rc = create_sa_db_entry(optarg, FALSE);
+ break;
+
+ case 'e':
+ rc = create_sa_db_entry(optarg, TRUE);
+ break;
+
+ case 't':
+ rc = create_tun_db_entry(optarg);
+ break;
+
+ case 's':
+ rc = create_stream_db_entry(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (rc) {
+ printf("ERROR: failed parsing -%c option\n", opt);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ odp_sys_info_print();
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+
+ printf("\n");
+
+ dump_fwd_db();
+ dump_sp_db();
+ dump_sa_db();
+ dump_tun_db();
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth1,eth2,eth3 -m 0\n"
+ "\n"
+ "OpenDataPlane example application.\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " -m, --mode 0: SYNC\n"
+ " 1: ASYNC\n"
+ " Default: 1: ASYNC api mode\n"
+ "\n"
+ "Routing / IPSec OPTIONS:\n"
+ " -r, --route SubNet,Intf,NextHopMAC\n"
+ " -p, --policy SrcSubNet,DstSubNet,(in|out),(ah|esp)\n"
+ " -e, --esp SrcIP,DstIP,(3des|null),SPI,Key192\n"
+ " -a, --ah SrcIP,DstIP,(sha256|sha1|md5|null),SPI,Key(256|160|128)\n"
+ "\n"
+ " Where: NextHopMAC is raw hex/colon notation, i.e. 03:BA:44:9A:CE:02\n"
+ " IP is decimal/dot notation, i.e. 192.168.1.1\n"
+ " SubNet is decimal/dot/slash notation, i.e 192.168.0.0/16\n"
+ " SPI is raw hex, 32 bits\n"
+ " KeyXXX is raw hex, XXX bits long\n"
+ "\n"
+ " Examples:\n"
+ " -r 192.168.222.0/24,p8p1,08:00:27:F5:8B:DB\n"
+ " -p 192.168.111.0/24,192.168.222.0/24,out,esp\n"
+ " -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
+ " -a 192.168.111.2,192.168.222.2,md5,201,a731649644c5dee92cbd9c2e7e188ee6\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -s, --stream SrcIP,DstIP,InIntf,OutIntf,Count,Length\n"
+ " -h, --help Display help and exit.\n"
+ " environment variables: ODP_IPSEC_USE_POLL_QUEUES\n"
+ " to enable use of poll queues instead of scheduled (default)\n"
+ " ODP_IPSEC_STREAM_VERIFY_MDEQ\n"
+ " to enable use of multiple dequeue for queue draining during\n"
+ " stream verification instead of single dequeue (default)\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa_entry, int *sa_flags);
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa_entry ODP_UNUSED, int *sa_flags ODP_UNUSED)
+{
+ return true;
+}
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_in.sh b/example/ipsec_api/odp_ipsec_api_run_ah_in.sh
new file mode 100755
index 000000000..087c52aa3
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_in.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test input AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,ah \
+ -a 192.168.222.2,192.168.111.2,sha1,300,27f6d123d7077b361662fc6e451f65d800000000 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_out.sh b/example/ipsec_api/odp_ipsec_api_run_ah_out.sh
new file mode 100755
index 000000000..6acc8628a
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_out.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test output AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,ah \
+ -a 192.168.111.2,192.168.222.2,sha1,200,a731649644c5dee92cbd9c2e7e188ee600000000 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh b/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh
new file mode 100755
index 000000000..02fe3df3a
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test input AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,ah \
+ -a 192.168.222.2,192.168.111.2,sha1,300,27f6d123d7077b361662fc6e451f65d800000000 \
+ -t 192.168.222.2,192.168.111.2,10.0.222.2,10.0.111.2 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh b/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh
new file mode 100755
index 000000000..4efb4c23d
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test output AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,ah \
+ -a 192.168.111.2,192.168.222.2,sha1,200,a731649644c5dee92cbd9c2e7e188ee600000000 \
+ -t 192.168.111.2,192.168.222.2,10.0.111.2,10.0.222.2 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_in.sh b/example/ipsec_api/odp_ipsec_api_run_esp_in.sh
new file mode 100755
index 000000000..1c3414498
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_in.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test input ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,esp \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_out.sh b/example/ipsec_api/odp_ipsec_api_run_esp_out.sh
new file mode 100755
index 000000000..c9809e8ac
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_out.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test output ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,esp \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh b/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh
new file mode 100755
index 000000000..3b4f91e41
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test input ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,esp \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -t 192.168.222.2,192.168.111.2,10.0.222.2,10.0.111.2 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh b/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh
new file mode 100755
index 000000000..2bda78665
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test output ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,esp \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -t 192.168.111.2,192.168.222.2,10.0.111.2,10.0.222.2 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_live.sh b/example/ipsec_api/odp_ipsec_api_run_live.sh
new file mode 100755
index 000000000..9febf2868
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_live.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=1
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_api_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_api -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -p 192.168.111.0/24,192.168.222.0/24,out,esp \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,esp \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -c 2 "$@" & echo $! > $PID) | tee $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application exits
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_api/odp_ipsec_api_run_router.sh b/example/ipsec_api/odp_ipsec_api_run_router.sh
new file mode 100755
index 000000000..198721ea0
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_router.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=2
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_api_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_api -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -c 1 "$@" & echo $! > $PID) | tee -a $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application stops
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_api/odp_ipsec_api_run_simple.sh b/example/ipsec_api/odp_ipsec_api_run_simple.sh
new file mode 100755
index 000000000..2921f978b
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_simple.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Simple router test
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_cache.c b/example/ipsec_api/odp_ipsec_cache.c
new file mode 100644
index 000000000..827c9dce2
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_cache.c
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_cache.h>
+
+/** Global pointer to ipsec_cache db */
+ipsec_cache_t *ipsec_cache;
+
+void init_ipsec_cache(void)
+{
+ odp_shm_t shm;
+ int i;
+
+ shm = odp_shm_reserve("shm_ipsec_cache",
+ sizeof(ipsec_cache_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ipsec_cache = odp_shm_addr(shm);
+
+ if (ipsec_cache == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(ipsec_cache, 0, sizeof(*ipsec_cache));
+
+ for (i = 0; i < MAX_DB; i++)
+ ipsec_cache->array[i].ipsec_sa = ODP_IPSEC_SA_INVALID;
+}
+
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ odp_bool_t in,
+ odp_queue_t completionq)
+{
+ odp_ipsec_sa_param_t param;
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_sa_t ipsec_sa;
+ uint32_t tun_src_ip, tun_dst_ip;
+ sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT;
+
+ /* Verify we have a good entry */
+ entry = &ipsec_cache->array[ipsec_cache->index];
+ if (MAX_DB <= ipsec_cache->index)
+ return -1;
+
+ odp_ipsec_sa_param_init(&param);
+ param.dir = in ? ODP_IPSEC_DIR_INBOUND : ODP_IPSEC_DIR_OUTBOUND;
+ param.inbound.lookup_mode = in ? ODP_IPSEC_LOOKUP_SPI :
+ ODP_IPSEC_LOOKUP_DISABLED;
+ param.proto = cipher_sa ? ODP_IPSEC_ESP : ODP_IPSEC_AH;
+
+ param.mode = tun ? ODP_IPSEC_MODE_TUNNEL : ODP_IPSEC_MODE_TRANSPORT;
+
+ param.dest_queue = completionq;
+
+ /* Cipher */
+ if (cipher_sa) {
+ param.crypto.cipher_alg = cipher_sa->alg.u.cipher;
+ param.crypto.cipher_key.data = cipher_sa->key.data;
+ param.crypto.cipher_key.length = cipher_sa->key.length;
+ param.spi = cipher_sa->spi;
+ } else {
+ param.crypto.cipher_alg = ODP_CIPHER_ALG_NULL;
+ }
+
+ /* Auth */
+ if (auth_sa) {
+ param.crypto.auth_alg = auth_sa->alg.u.auth;
+ param.crypto.auth_key.data = auth_sa->key.data;
+ param.crypto.auth_key.length = auth_sa->key.length;
+ param.spi = auth_sa->spi;
+ } else {
+ param.crypto.auth_alg = ODP_AUTH_ALG_NULL;
+ }
+
+ if (ODP_IPSEC_MODE_TUNNEL == param.mode) {
+ tun_src_ip = odp_cpu_to_be_32(tun->tun_src_ip);
+ tun_dst_ip = odp_cpu_to_be_32(tun->tun_dst_ip);
+ param.outbound.tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ param.outbound.tunnel.ipv4.src_addr = &tun_src_ip;
+ param.outbound.tunnel.ipv4.dst_addr = &tun_dst_ip;
+ }
+
+ ipsec_sa = odp_ipsec_sa_create(&param);
+ if (ODP_IPSEC_SA_INVALID == ipsec_sa) {
+ ODPH_ERR("Error: SA creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Copy remainder */
+ if (cipher_sa) {
+ entry->src_ip = cipher_sa->src_ip;
+ entry->dst_ip = cipher_sa->dst_ip;
+ entry->esp.alg = cipher_sa->alg.u.cipher;
+ entry->esp.spi = cipher_sa->spi;
+ entry->esp.block_len = cipher_sa->block_len;
+ entry->esp.iv_len = cipher_sa->iv_len;
+ memcpy(&entry->esp.key, &cipher_sa->key, sizeof(ipsec_key_t));
+ }
+ if (auth_sa) {
+ entry->src_ip = auth_sa->src_ip;
+ entry->dst_ip = auth_sa->dst_ip;
+ entry->ah.alg = auth_sa->alg.u.auth;
+ entry->ah.spi = auth_sa->spi;
+ entry->ah.icv_len = auth_sa->icv_len;
+ memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t));
+ }
+
+ if (tun) {
+ entry->tun_src_ip = tun->tun_src_ip;
+ entry->tun_dst_ip = tun->tun_dst_ip;
+ mode = IPSEC_SA_MODE_TUNNEL;
+ }
+ entry->mode = mode;
+
+ /* Add entry to the appropriate list */
+ ipsec_cache->index++;
+ if (in) {
+ entry->next = ipsec_cache->in_list;
+ ipsec_cache->in_list = entry;
+ } else {
+ entry->next = ipsec_cache->out_list;
+ ipsec_cache->out_list = entry;
+ }
+
+ entry->ipsec_sa = ipsec_sa;
+
+ return 0;
+}
+
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_esphdr_t *esp)
+{
+ ipsec_cache_entry_t *entry = ipsec_cache->in_list;
+
+ /* Look for a hit */
+ for (; NULL != entry; entry = entry->next) {
+ if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip))
+ if ((entry->tun_src_ip != src_ip) ||
+ (entry->tun_dst_ip != dst_ip))
+ continue;
+ if (ah &&
+ ((!entry->ah.alg) ||
+ (entry->ah.spi != odp_be_to_cpu_32(ah->spi))))
+ continue;
+ if (esp &&
+ ((!entry->esp.alg) ||
+ (entry->esp.spi != odp_be_to_cpu_32(esp->spi))))
+ continue;
+ break;
+ }
+
+ return entry;
+}
+
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+ uint32_t dst_ip,
+ uint8_t proto ODP_UNUSED)
+{
+ ipsec_cache_entry_t *entry = ipsec_cache->out_list;
+
+ /* Look for a hit */
+ for (; NULL != entry; entry = entry->next) {
+ if ((entry->src_ip == src_ip) && (entry->dst_ip == dst_ip))
+ break;
+ }
+ return entry;
+}
+
+int destroy_ipsec_cache(void)
+{
+ ipsec_cache_entry_t *entry;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < MAX_DB; i++) {
+ entry = &ipsec_cache->array[i];
+ if (entry->ipsec_sa != ODP_IPSEC_SA_INVALID) {
+ ret += odp_ipsec_sa_disable(entry->ipsec_sa);
+ ret += odp_ipsec_sa_destroy(entry->ipsec_sa);
+ }
+ }
+
+ return ret;
+}
diff --git a/example/ipsec_api/odp_ipsec_cache.h b/example/ipsec_api/odp_ipsec_cache.h
new file mode 100644
index 000000000..5dd0c80b3
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_cache.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_CACHE_H_
+#define ODP_IPSEC_CACHE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/ipsec.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+
+/**
+ * IPsec cache data base entry
+ */
+typedef struct ipsec_cache_entry_s {
+ struct ipsec_cache_entry_s *next; /**< Next entry on list */
+ uint32_t src_ip; /**< Source v4 address */
+ uint32_t dst_ip; /**< Destination v4 address */
+ sa_mode_t mode; /**< SA mode - transport/tun */
+ uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */
+ uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */
+ struct {
+ odp_cipher_alg_t alg; /**< Cipher algorithm */
+ uint32_t spi; /**< Cipher SPI */
+ uint32_t block_len; /**< Cipher block length */
+ uint32_t iv_len; /**< Cipher IV length */
+ ipsec_key_t key; /**< Cipher key */
+ } esp;
+ struct {
+ odp_auth_alg_t alg; /**< Auth algorithm */
+ uint32_t spi; /**< Auth SPI */
+ uint32_t icv_len; /**< Auth ICV length */
+ ipsec_key_t key; /**< Auth key */
+ } ah;
+
+ odp_ipsec_sa_t ipsec_sa;
+} ipsec_cache_entry_t;
+
+/**
+ * IPsec cache data base global structure
+ */
+typedef struct ipsec_cache_s {
+ uint32_t index; /**< Index of next available entry */
+ ipsec_cache_entry_t *in_list; /**< List of active input entries */
+ ipsec_cache_entry_t *out_list; /**< List of active output entries */
+ ipsec_cache_entry_t array[MAX_DB]; /**< Entry storage */
+} ipsec_cache_t;
+
+/** Global pointer to ipsec_cache db */
+extern ipsec_cache_t *ipsec_cache;
+
+/** Initialize IPsec cache */
+void init_ipsec_cache(void);
+
+/**
+ * Create an entry in the IPsec cache
+ *
+ * @param cipher_sa Cipher SA DB entry pointer
+ * @param auth_sa Auth SA DB entry pointer
+ * @param tun Tunnel DB entry pointer
+ * @param in Direction (input versus output)
+ * @param completionq Completion queue
+ *
+ * @return 0 if successful else -1
+ */
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ odp_bool_t in,
+ odp_queue_t completionq);
+
+/**
+ * Find a matching IPsec cache entry for input packet
+ *
+ * @param src_ip Source IPv4 address
+ * @param dst_ip Destination IPv4 address
+ * @param ah Pointer to AH header in packet else NULL
+ * @param esp Pointer to ESP header in packet else NULL
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_esphdr_t *esp);
+
+/**
+ * Find a matching IPsec cache entry for output packet
+ *
+ * @param src_ip Source IPv4 address
+ * @param dst_ip Destination IPv4 address
+ * @param proto IPv4 protocol (currently all protocols match)
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+ uint32_t dst_ip,
+ uint8_t proto);
+
+int destroy_ipsec_cache(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_api/odp_ipsec_fwd_db.c b/example/ipsec_api/odp_ipsec_fwd_db.c
new file mode 120000
index 000000000..aba996aaf
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_fwd_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_fwd_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_fwd_db.h b/example/ipsec_api/odp_ipsec_fwd_db.h
new file mode 120000
index 000000000..f9a8ab957
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_fwd_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_fwd_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_misc.h b/example/ipsec_api/odp_ipsec_misc.h
new file mode 120000
index 000000000..7bb7de030
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_misc.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_misc.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sa_db.c b/example/ipsec_api/odp_ipsec_sa_db.c
new file mode 120000
index 000000000..7ce1a5292
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sa_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sa_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sa_db.h b/example/ipsec_api/odp_ipsec_sa_db.h
new file mode 120000
index 000000000..9b022c202
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sa_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sa_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sp_db.c b/example/ipsec_api/odp_ipsec_sp_db.c
new file mode 120000
index 000000000..c7bd160fa
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sp_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sp_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sp_db.h b/example/ipsec_api/odp_ipsec_sp_db.h
new file mode 120000
index 000000000..26369e9bb
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sp_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sp_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_stream.c b/example/ipsec_api/odp_ipsec_stream.c
new file mode 120000
index 000000000..57e25fe0e
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_stream.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_stream.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_stream.h b/example/ipsec_api/odp_ipsec_stream.h
new file mode 120000
index 000000000..6ad6700cc
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_stream.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_stream.h \ No newline at end of file
diff --git a/example/ipsec_crypto/.gitignore b/example/ipsec_crypto/.gitignore
new file mode 100644
index 000000000..e8b9e69a1
--- /dev/null
+++ b/example/ipsec_crypto/.gitignore
@@ -0,0 +1,2 @@
+odp_ipsec_crypto
+pktio_env
diff --git a/example/ipsec_crypto/Makefile.am b/example/ipsec_crypto/Makefile.am
new file mode 100644
index 000000000..1db0d72d0
--- /dev/null
+++ b/example/ipsec_crypto/Makefile.am
@@ -0,0 +1,72 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_ipsec_crypto
+
+if test_example
+TESTS = \
+ odp_ipsec_crypto_run_ah_in.sh \
+ odp_ipsec_crypto_run_ah_out.sh \
+ odp_ipsec_crypto_run_both_in.sh \
+ odp_ipsec_crypto_run_both_out.sh \
+ odp_ipsec_crypto_run_esp_in.sh \
+ odp_ipsec_crypto_run_esp_out.sh \
+ odp_ipsec_crypto_run_live.sh \
+ odp_ipsec_crypto_run_router.sh \
+ odp_ipsec_crypto_run_simple.sh
+endif
+EXTRA_DIST = \
+ odp_ipsec_crypto_run_ah_in.sh \
+ odp_ipsec_crypto_run_ah_out.sh \
+ odp_ipsec_crypto_run_both_in.sh \
+ odp_ipsec_crypto_run_both_out.sh \
+ odp_ipsec_crypto_run_esp_in.sh \
+ odp_ipsec_crypto_run_esp_out.sh \
+ odp_ipsec_crypto_run_live.sh \
+ odp_ipsec_crypto_run_router.sh \
+ odp_ipsec_crypto_run_simple.sh
+
+odp_ipsec_crypto_SOURCES = \
+ odp_ipsec.c \
+ odp_ipsec_sa_db.c \
+ odp_ipsec_sp_db.c \
+ odp_ipsec_fwd_db.c \
+ odp_ipsec_cache.c \
+ odp_ipsec_cache.h \
+ odp_ipsec_fwd_db.h \
+ odp_ipsec_misc.h \
+ odp_ipsec_sa_db.h \
+ odp_ipsec_sp_db.h
+
+if WITH_OPENSSL
+odp_ipsec_crypto_SOURCES += \
+ odp_ipsec_stream.c \
+ odp_ipsec_stream.h
+
+AM_CPPFLAGS = $(OPENSSL_CPPFLAGS)
+LDADD += $(OPENSSL_LIBS)
+
+else
+AM_CPPFLAGS = -DNO_OPENSSL
+endif
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s ../../platform/$(with_platform)/test/example/ipsec_crypto/pktio_env pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
+ rm -f pktio_env
diff --git a/example/ipsec_crypto/README b/example/ipsec_crypto/README
new file mode 100644
index 000000000..0411396a1
--- /dev/null
+++ b/example/ipsec_crypto/README
@@ -0,0 +1,169 @@
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2014-2018 Linaro Limited
+
+1. Intro
+
+The odp_ipsec_crypto example application demonstrates an IPsec implementation
+using ODP crypto APIs. The application functions as a simple L3 IPv4 router
+which supports IPsec 3DES cipher and HMAC-MD5 authentication in both the
+transmit and receive directions. Note that only IPsec "transport" mode is
+supported.
+
+2. Prerequisites
+
+ 2.1 SSL development libraries
+
+Development has been done to this point with the openssl-devel libraries,
+the makefile specifically links with "-lcrypto".
+
+3. Topology
+
+The following test topology was used for development. Each of the VMs
+is running Fedora 32. Sanity testing consists of pinging VM2 from VM0
+such that the packets traverse VM1. Packets between VM1 and VM2 are
+IPsec AH and ESP encapsulated.
+
+ VM0 VM1 (UUT) VM2
++------------+ +--------------+ +------------+
+| | (clear) | | (crypto) | |
+| | subnet | | subnet | |
+| p7p1 |<---------------->| p7p1 p8p1 |<---------------->| p7p1 |
+| .2 | 192.168.111.0 | .1 .1 | 192.168.222.0 | .2 |
+| | | | | |
++------------+ +--------------+ +------------+
+
+4. VM configurations
+
+ 4.1 VM0 configuration
+
+VM0 has the following interface configuration:
+
+ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+ DEVICE=p7p1
+ HWADDR=08:00:27:76:B5:E0
+ BOOTPROTO=static
+ IPADDR=192.168.111.2
+ NETMASK=255.255.255.0
+ ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM0:
+
+ sudo ip route add 192.168.222.0/24 via 192.168.111.1
+ sudo arp -s 192.168.111.1 08:00:27:04:BF:8C
+
+ 4.2 VM1 configuration
+
+For the unit under test, IP forwarding and IP tables were disabled.
+
+VM1 has the following interface configurations:
+
+ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+ DEVICE=p7p1
+ HWADDR=08:00:27:04:BF:8C
+ BOOTPROTO=static
+ IPADDR=192.168.111.1
+ NETMASK=255.255.255.0
+ ONBOOT=yes
+
+ cat /etc/sysconfig/network-scripts/ifcfg-p8p1
+ DEVICE=p8p1
+ HWADDR=08:00:27:4C:55:CC
+ BOOTPROTO=static
+ IPADDR=192.168.222.1
+ NETMASK=255.255.255.0
+ ONBOOT=yes
+
+The application is launched on VM1 with the following command:
+
+ sudo ./odp_ipsec_crypto -i p7p1,p8p1 \
+ -r 192.168.111.2/32,p7p1,08:00:27:76:B5:E0 \
+ -r 192.168.222.2/32,p8p1,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,both \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -a 192.168.111.2,192.168.222.2,md5,200,a731649644c5dee92cbd9c2e7e188ee6 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,both \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -a 192.168.222.2,192.168.111.2,md5,300,27f6d123d7077b361662fc6e451f65d8 \
+ -c 2 -m 0
+
+ 4.3 VM2 configuration
+
+VM2 has the following interface configuration:
+
+ cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+ DEVICE=p7p1
+ HWADDR=08:00:27:F5:8B:DB
+ BOOTPROTO=static
+ IPADDR=192.168.222.2
+ NETMASK=255.255.255.0
+ ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM2:
+
+ sudo ip route add 192.168.111.0/24 via 192.168.222.1
+ sudo arp -s 192.168.222.1 08:00:27:4c:55:cc
+
+VM2 must be setup with an IPsec configuration complementing
+the configuration used by the "odp_ipsec_crypto" application running
+on VM1. The configuration is applied using "setkey" (provided by ipsec-tools
+package).
+
+VM2 uses the following setkey configuration:
+
+ cat setkey_vm2.conf
+ # Flush the SAD and SPD
+ flush;
+ spdflush;
+
+ add 192.168.111.2 192.168.222.2 ah 0x200 -A hmac-md5
+ 0xa731649644c5dee92cbd9c2e7e188ee6;
+ add 192.168.222.2 192.168.111.2 ah 0x300 -A hmac-md5
+ 0x27f6d123d7077b361662fc6e451f65d8;
+
+ add 192.168.111.2 192.168.222.2 esp 0x201 -E 3des-cbc
+ 0x656c8523255ccc23a66c1917aa0cf30991fce83532a4b224;
+ add 192.168.222.2 192.168.111.2 esp 0x301 -E 3des-cbc
+ 0xc966199f24d095f3990a320d749056401e82b26570320292;
+
+ spdadd 192.168.111.2 192.168.222.2 any -P in ipsec
+ esp/transport//require
+ ah/transport//require;
+
+ spdadd 192.168.222.2 192.168.111.2 any -P out ipsec
+ esp/transport//require
+ ah/transport//require;
+
+Apply the setkey configuration:
+ sudo setkey -f setkey_vm2.conf
+
+5. Sanity Test with Real Traffic
+
+Once all three VMs have been configured, static ARP and route entries added,
+setkey configuration applied, and odp_ipsec_crypto application is running, VM0
+should be able to ping VM2 at the 192.168.222.2 address.
+
+At VM0 console issue the ping to VM2's address:
+
+ sudo ping -c 2 -i 0.1 192.168.222.2
+ PING 192.168.222.2 (192.168.222.2) 56(84) bytes of data.
+ 64 bytes from 192.168.222.2: icmp_req=1 ttl=64 time=33.9 ms
+ 64 bytes from 192.168.222.2: icmp_req=2 ttl=64 time=23.3 ms
+
+At VM2 console use tcpdump to observe IPsec packets:
+
+ sudo tcpdump -nt -i p7p1
+ tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+ listening on p7p1, link-type EN10MB (Ethernet), capture size 65535 bytes
+
+ IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x6): ESP(spi=0x00000201,seq=0x6), length 88
+ IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7a): ESP(spi=0x00000301,seq=0x7a), length 88
+ IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x7): ESP(spi=0x00000201,seq=0x7), length 88
+ IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7b): ESP(spi=0x00000301,seq=0x7b), length 88
+
+6. Standalone Loopback Tests
+
+BASH batch files are now included to run several simple loopback tests that
+do not require any packet IO. The scripts create internal "loopback" packet
+interfaces.
+Before running the example bash scripts add odp_ipsec_crypto to your PATH
+export PATH="<path_to_odp_ipsec_crypto>:$PATH"
diff --git a/example/ipsec_crypto/odp_ipsec.c b/example/ipsec_crypto/odp_ipsec.c
new file mode 100644
index 000000000..490d2ad0a
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec.c
@@ -0,0 +1,1787 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @example ipsec_crypto/odp_ipsec.c
+ *
+ * IPsec example application using ODP crypto API
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+#include <odp_ipsec_sp_db.h>
+#include <odp_ipsec_fwd_db.h>
+#include <odp_ipsec_cache.h>
+
+#ifndef NO_OPENSSL
+#include <odp_ipsec_stream.h>
+#else
+static void init_stream_db(void) {}
+static void deinit_stream_db(void) {}
+static void resolve_stream_db(void) {}
+static int create_stream_db_inputs(void)
+{
+ return 0;
+}
+
+static odp_bool_t verify_stream_db_outputs(void)
+{
+ return true;
+}
+
+static int create_stream_db_entry(char *input ODP_UNUSED)
+{
+ return -1;
+}
+#endif
+
+/* maximum number of worker threads */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+#define MAX_POLL_QUEUES 256
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ unsigned int cpu_count;
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ crypto_api_mode_e mode; /**< Crypto API preferred mode */
+ odp_pool_t pool; /**< Buffer pool for packet IO */
+ char *if_str; /**< Storage for interface names */
+} appl_args_t;
+
+/**
+ * Grouping of both parsed CL args and global application data
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ odp_shm_t shm;
+ odp_pool_t ctx_pool;
+ odp_pool_t out_pool;
+ odp_pool_t pkt_pool;
+ /** ATOMIC queue for IPsec sequence number assignment */
+ odp_queue_t seqnumq;
+ /** ORDERED queue for per packet crypto API completion events */
+ odp_queue_t completionq;
+ /** Synchronize threads before packet processing begins */
+ odp_barrier_t sync_barrier;
+ odp_queue_t poll_queues[MAX_POLL_QUEUES];
+ int num_polled_queues;
+ /* Stop workers if set to 1 */
+ odp_atomic_u32_t exit_threads;
+ odp_crypto_capability_t crypto_capa;
+} global_data_t;
+
+/* helper funcs */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Buffer pool for packet IO
+ */
+#define SHM_PKT_POOL_BUF_COUNT 1024
+#define SHM_PKT_POOL_BUF_SIZE 4096
+#define SHM_PKT_POOL_SIZE (SHM_PKT_POOL_BUF_COUNT * SHM_PKT_POOL_BUF_SIZE)
+
+/**
+ * Buffer pool for crypto session output packets
+ */
+#define SHM_OUT_POOL_BUF_COUNT 1024
+#define SHM_OUT_POOL_BUF_SIZE 4096
+#define SHM_OUT_POOL_SIZE (SHM_OUT_POOL_BUF_COUNT * SHM_OUT_POOL_BUF_SIZE)
+
+/**
+ * Packet processing states/steps
+ */
+typedef enum {
+ PKT_STATE_INPUT_VERIFY, /**< Verify IPv4 and ETH */
+ PKT_STATE_IPSEC_IN_CLASSIFY, /**< Initiate input IPsec */
+ PKT_STATE_IPSEC_IN_FINISH, /**< Finish input IPsec */
+ PKT_STATE_ROUTE_LOOKUP, /**< Use DST IP to find output IF */
+ PKT_STATE_IPSEC_OUT_CLASSIFY, /**< Intiate output IPsec */
+ PKT_STATE_IPSEC_OUT_SEQ, /**< Assign IPsec sequence numbers */
+ PKT_STATE_IPSEC_OUT_FINISH, /**< Finish output IPsec */
+ PKT_STATE_TRANSMIT, /**< Send packet to output IF queue */
+} pkt_state_e;
+
+/**
+ * Packet processing result codes
+ */
+typedef enum {
+ PKT_CONTINUE, /**< No events posted, keep processing */
+ PKT_POSTED, /**< Event posted, stop processing */
+ PKT_DROP, /**< Reason to drop detected, stop processing */
+ PKT_DONE /**< Finished with packet, stop processing */
+} pkt_disposition_e;
+
+/**
+ * Per packet IPsec processing context
+ */
+typedef struct {
+ uint8_t ip_tos; /**< Saved IP TOS value */
+ uint16_t ip_frag_offset; /**< Saved IP flags value */
+ uint8_t ip_ttl; /**< Saved IP TTL value */
+ int hdr_len; /**< Length of IPsec headers */
+ int trl_len; /**< Length of IPsec trailers */
+ uint16_t tun_hdr_offset; /**< Offset of tunnel header from
+ buffer start */
+ uint16_t ah_offset; /**< Offset of AH header from buffer start */
+ uint16_t esp_offset; /**< Offset of ESP header from buffer start */
+
+ /* Input only */
+ uint32_t src_ip; /**< SA source IP address */
+ uint32_t dst_ip; /**< SA dest IP address */
+
+ /* Output only */
+ odp_crypto_packet_op_param_t params; /**< Parameters for crypto call */
+ uint32_t *ah_seq; /**< AH sequence number location */
+ uint32_t *esp_seq; /**< ESP sequence number location */
+ uint16_t *tun_hdr_id; /**< Tunnel header ID > */
+} ipsec_ctx_t;
+
+/**
+ * Per packet processing context
+ */
+typedef struct {
+ odp_buffer_t buffer; /**< Buffer for context */
+ pkt_state_e state; /**< Next processing step */
+ ipsec_ctx_t ipsec; /**< IPsec specific context */
+ odp_pktout_queue_t pktout; /**< Packet output queue */
+} pkt_ctx_t;
+
+#define SHM_CTX_POOL_BUF_SIZE (sizeof(pkt_ctx_t))
+#define SHM_CTX_POOL_BUF_COUNT (SHM_PKT_POOL_BUF_COUNT + SHM_OUT_POOL_BUF_COUNT)
+#define SHM_CTX_POOL_SIZE (SHM_CTX_POOL_BUF_COUNT * SHM_CTX_POOL_BUF_SIZE)
+
+static global_data_t *global;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (global == NULL)
+ return;
+ odp_atomic_store_u32(&global->exit_threads, 1);
+}
+
+/**
+ * Get per packet processing context from packet buffer
+ *
+ * @param pkt Packet
+ *
+ * @return pointer to context area
+ */
+static
+pkt_ctx_t *get_pkt_ctx_from_pkt(odp_packet_t pkt)
+{
+ return (pkt_ctx_t *)odp_packet_user_ptr(pkt);
+}
+
+/**
+ * Allocate per packet processing context and associate it with
+ * packet buffer
+ *
+ * @param pkt Packet
+ *
+ * @return pointer to context area
+ */
+static
+pkt_ctx_t *alloc_pkt_ctx(odp_packet_t pkt)
+{
+ odp_buffer_t ctx_buf = odp_buffer_alloc(global->ctx_pool);
+ pkt_ctx_t *ctx;
+
+ if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf))
+ return NULL;
+
+ ctx = odp_buffer_addr(ctx_buf);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->buffer = ctx_buf;
+ odp_packet_user_ptr_set(pkt, ctx);
+
+ return ctx;
+}
+
+/**
+ * Release per packet resources
+ *
+ * @param ctx Packet context
+ */
+static
+void free_pkt_ctx(pkt_ctx_t *ctx)
+{
+ odp_buffer_free(ctx->buffer);
+}
+
+/**
+ * Example supports either polling queues or using odp_schedule
+ */
+typedef odp_queue_t (*queue_create_func_t)
+ (const char *, const odp_queue_param_t *);
+typedef odp_event_t (*schedule_func_t) (odp_queue_t *);
+
+static queue_create_func_t queue_create;
+static schedule_func_t schedule_fn;
+
+/**
+ * odp_queue_create wrapper to enable polling versus scheduling
+ */
+static
+odp_queue_t polled_odp_queue_create(const char *name,
+ const odp_queue_param_t *param)
+{
+ odp_queue_t my_queue;
+ odp_queue_param_t qp;
+ odp_queue_type_t type;
+
+ odp_queue_param_init(&qp);
+ if (param)
+ memcpy(&qp, param, sizeof(odp_queue_param_t));
+
+ type = qp.type;
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ printf("%s: change %s to PLAIN\n", __func__, name);
+ qp.type = ODP_QUEUE_TYPE_PLAIN;
+ }
+
+ my_queue = odp_queue_create(name, &qp);
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ global->poll_queues[global->num_polled_queues++] = my_queue;
+ printf("%s: adding %"PRIu64"\n", __func__,
+ odp_queue_to_u64(my_queue));
+ }
+
+ return my_queue;
+}
+
+static inline
+odp_event_t odp_schedule_cb(odp_queue_t *from)
+{
+ return odp_schedule(from, ODP_SCHED_NO_WAIT);
+}
+
+/**
+ * odp_schedule replacement to poll queues versus using ODP scheduler
+ */
+static
+odp_event_t polled_odp_schedule_cb(odp_queue_t *from)
+{
+ int idx = 0;
+
+ while (idx < global->num_polled_queues) {
+ odp_queue_t queue = global->poll_queues[idx++];
+ odp_event_t ev;
+
+ ev = odp_queue_deq(queue);
+
+ if (ODP_EVENT_INVALID != ev) {
+ *from = queue;
+ return ev;
+ }
+ }
+
+ *from = ODP_QUEUE_INVALID;
+ return ODP_EVENT_INVALID;
+}
+
+/**
+ * IPsec pre argument processing initialization
+ */
+static
+void ipsec_init_pre(void)
+{
+ odp_queue_param_t qparam;
+ odp_pool_param_t params;
+
+ /*
+ * Create queues
+ *
+ * - completion queue (should eventually be ORDERED)
+ * - sequence number queue (must be ATOMIC)
+ */
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global->completionq = queue_create("completion", &qparam);
+ if (ODP_QUEUE_INVALID == global->completionq) {
+ ODPH_ERR("Error: completion queue creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global->seqnumq = queue_create("seqnum", &qparam);
+ if (ODP_QUEUE_INVALID == global->seqnumq) {
+ ODPH_ERR("Error: sequence number queue creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create output buffer pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = SHM_OUT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_OUT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
+ params.type = ODP_POOL_PACKET;
+
+ global->out_pool = odp_pool_create("out_pool", &params);
+
+ if (ODP_POOL_INVALID == global->out_pool) {
+ ODPH_ERR("Error: message pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initialize our data bases */
+ init_sp_db();
+ init_sa_db();
+ init_tun_db();
+ init_ipsec_cache();
+}
+
+/**
+ * IPsec post argument processing initialization
+ *
+ * Resolve SP DB with SA DB and create corresponding IPsec cache entries
+ *
+ * @param api_mode Mode to use when invoking per packet crypto API
+ */
+static
+void ipsec_init_post(crypto_api_mode_e api_mode)
+{
+ sp_db_entry_t *entry;
+
+ /* Attempt to find appropriate SA for each SP */
+ for (entry = sp_db->list; NULL != entry; entry = entry->next) {
+ sa_db_entry_t *cipher_sa = NULL;
+ sa_db_entry_t *auth_sa = NULL;
+ tun_db_entry_t *tun = NULL;
+
+ if (entry->esp) {
+ cipher_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 1);
+ tun = find_tun_db_entry(cipher_sa->src_ip,
+ cipher_sa->dst_ip);
+ }
+ if (entry->ah) {
+ auth_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 0);
+ tun = find_tun_db_entry(auth_sa->src_ip,
+ auth_sa->dst_ip);
+ }
+
+ if (cipher_sa || auth_sa) {
+ if (create_ipsec_cache_entry(cipher_sa,
+ auth_sa,
+ tun,
+ api_mode,
+ entry->input,
+ global->completionq,
+ global->out_pool)) {
+ ODPH_ERR("Error: IPSec cache entry failed.\n"
+ );
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ printf(" WARNING: SA not found for SP\n");
+ dump_sp_db_entry(entry);
+ }
+ }
+}
+
+#ifndef NO_OPENSSL
+static
+int check_stream_db_out(const char *intf)
+{
+ stream_db_entry_t *stream = NULL;
+
+ /* For each stream look for input and output IPsec entries */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ if (!strcmp(stream->output.intf, intf))
+ return 1;
+ }
+
+ return 0;
+}
+#else
+static
+int check_stream_db_out(const char *intf ODP_UNUSED)
+{
+ return 0;
+}
+#endif
+
+/**
+ * Initialize interface
+ *
+ * Initialize ODP pktio and queues, query MAC address and update
+ * forwarding database.
+ *
+ * @param intf Interface name string
+ */
+static
+void initialize_intf(char *intf)
+{
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout;
+ odp_queue_t inq;
+ int ret;
+ uint8_t src_mac[ODPH_ETHADDR_LEN];
+ char src_mac_str[MAX_STRING];
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES") ||
+ check_stream_db_out(intf))
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+ else
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ /*
+ * Open a packet IO instance for thread and get default output queue
+ */
+ pktio = odp_pktio_open(intf, global->pkt_pool, &pktio_param);
+ if (ODP_PKTIO_INVALID == pktio) {
+ ODPH_ERR("Error: pktio create failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Error: pktin config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Error: pktout config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktin_event_queue(pktio, &inq, 1) != 1) {
+ ODPH_ERR("Error: failed to get input queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
+ ODPH_ERR("Error: failed to get pktout queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = odp_pktio_start(pktio);
+ if (ret) {
+ ODPH_ERR("Error: unable to start %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read the source MAC address for this interface */
+ ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
+ if (ret <= 0) {
+ ODPH_ERR("Error: failed during MAC address get for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Created pktio:%02" PRIu64 ", queue mode (ATOMIC queues)\n"
+ " default pktio%02" PRIu64 "-INPUT queue:%" PRIu64 "\n"
+ " source mac address %s\n",
+ odp_pktio_to_u64(pktio), odp_pktio_to_u64(pktio),
+ odp_queue_to_u64(inq),
+ mac_addr_str(src_mac_str, src_mac));
+
+ /* Resolve any routes using this interface for output */
+ resolve_fwd_db(intf, pktio, pktout, src_mac);
+}
+
+/**
+ * Packet Processing - Input verification
+ *
+ * @param pkt Packet to inspect
+ * @param ctx Packet process context (not used)
+ *
+ * @return PKT_CONTINUE if good, supported packet else PKT_DROP
+ */
+static
+pkt_disposition_e do_input_verify(odp_packet_t pkt,
+ pkt_ctx_t *ctx ODP_UNUSED)
+{
+ if (odp_unlikely(odp_packet_has_error(pkt)))
+ return PKT_DROP;
+
+ if (!odp_packet_has_eth(pkt))
+ return PKT_DROP;
+
+ if (!odp_packet_has_ipv4(pkt))
+ return PKT_DROP;
+
+ return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Route lookup in forwarding database
+ *
+ * @param pkt Packet to route
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if route found else PKT_DROP
+ */
+static
+pkt_disposition_e do_route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ fwd_db_entry_t *entry;
+
+ entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
+
+ if (entry) {
+ odph_ethhdr_t *eth =
+ (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+
+ memcpy(&eth->dst, entry->dst_mac, ODPH_ETHADDR_LEN);
+ memcpy(&eth->src, entry->src_mac, ODPH_ETHADDR_LEN);
+ ctx->pktout = entry->pktout;
+
+ return PKT_CONTINUE;
+ }
+
+ return PKT_DROP;
+}
+
+/**
+ * Packet Processing - Input IPsec packet classification
+ *
+ * Verify the received packet has IPsec headers and a match
+ * in the IPsec cache, if so issue crypto request else skip
+ * input crypto.
+ *
+ * @param pkt Packet to classify
+ * @param ctx Packet process context
+ * @param skip Pointer to return "skip" indication
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_in_classify(odp_packet_t *pkt,
+ pkt_ctx_t *ctx,
+ odp_bool_t *skip)
+{
+ uint8_t *buf = odp_packet_data(*pkt);
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*pkt, NULL);
+ int hdr_len;
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+ ipsec_cache_entry_t *entry;
+ odp_crypto_packet_op_param_t params;
+ odp_packet_t out_pkt;
+
+ /* Default to skip IPsec */
+ *skip = TRUE;
+
+ /* Check IP header for IPSec protocols and look it up */
+ hdr_len = locate_ipsec_headers(ip, &ah, &esp);
+ if (!ah && !esp)
+ return PKT_CONTINUE;
+ entry = find_ipsec_cache_entry_in(odp_be_to_cpu_32(ip->src_addr),
+ odp_be_to_cpu_32(ip->dst_addr),
+ ah,
+ esp);
+ if (!entry)
+ return PKT_CONTINUE;
+
+ /* Account for configured ESP IV length in packet */
+ hdr_len += entry->esp.iv_len;
+
+ /* Initialize parameters block */
+ memset(&params, 0, sizeof(params));
+ params.session = entry->state.session;
+
+ /*Save everything to context */
+ ctx->ipsec.ip_tos = ip->tos;
+ ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+ ctx->ipsec.ip_ttl = ip->ttl;
+ ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+ ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+ ctx->ipsec.hdr_len = hdr_len;
+ ctx->ipsec.trl_len = 0;
+ ctx->ipsec.src_ip = entry->src_ip;
+ ctx->ipsec.dst_ip = entry->dst_ip;
+
+ /*If authenticating, zero the mutable fields build the request */
+ if (ah) {
+ ip->chksum = 0;
+ ip->tos = 0;
+ ip->frag_offset = 0;
+ ip->ttl = 0;
+
+ params.auth_range.offset = ((uint8_t *)ip) - buf;
+ params.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
+ params.hash_result_offset = ah->icv - buf;
+ }
+
+ /* If deciphering build request */
+ if (esp) {
+ params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf;
+ params.cipher_range.length = ipv4_data_len(ip) - hdr_len;
+ params.cipher_iv_ptr = esp->iv;
+ }
+
+ if (entry->sa_flags & BIT_MODE_CIPHER) {
+ params.cipher_range.offset *= 8;
+ params.cipher_range.length *= 8;
+ }
+ if (entry->sa_flags & BIT_MODE_AUTH) {
+ params.auth_range.offset *= 8;
+ params.auth_range.length *= 8;
+ }
+
+ /* Issue crypto request */
+ *skip = FALSE;
+ ctx->state = PKT_STATE_IPSEC_IN_FINISH;
+ if (entry->async) {
+ if (odp_crypto_op_enq(pkt, NULL, &params, 1) != 1) {
+ ODPH_ERR("Error: odp_crypto_op_enq() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ return PKT_POSTED;
+ }
+
+ if (odp_crypto_op(pkt, &out_pkt, &params, 1) != 1) {
+ ODPH_ERR("Error: odp_crypto_op() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ *pkt = out_pkt;
+
+ return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Input IPsec packet processing cleanup
+ *
+ * @param pkt Packet to handle
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if successful else PKT_DROP
+ */
+static
+pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt,
+ pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip;
+ int hdr_len = ctx->ipsec.hdr_len;
+ int trl_len = 0;
+
+ if (odp_crypto_result(NULL, pkt) != 0)
+ return PKT_DROP;
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+
+ /*
+ * Finish auth
+ */
+ if (ctx->ipsec.ah_offset) {
+ uint8_t *buf = odp_packet_data(pkt);
+ odph_ahhdr_t *ah;
+
+ ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
+ ip->proto = ah->next_header;
+ }
+
+ /*
+ * Finish cipher by finding ESP trailer and processing
+ *
+ * NOTE: ESP authentication ICV not supported
+ */
+ if (ctx->ipsec.esp_offset) {
+ uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len);
+ odph_esptrl_t *esp_t = (odph_esptrl_t *)(eop) - 1;
+
+ ip->proto = esp_t->next_header;
+ trl_len += esp_t->pad_len + sizeof(*esp_t);
+ }
+
+ /* We have a tunneled IPv4 packet */
+ if (ip->proto == ODPH_IPV4) {
+ odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len);
+ odp_packet_pull_tail(pkt, trl_len);
+ odph_ethhdr_t *eth;
+
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ eth->type = ODPH_ETHTYPE_IPV4;
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+
+ /* Check inbound policy */
+ if ((ip->src_addr != ctx->ipsec.src_ip ||
+ ip->dst_addr != ctx->ipsec.dst_ip))
+ return PKT_DROP;
+
+ return PKT_CONTINUE;
+ }
+
+ /* Finalize the IPv4 header */
+ ipv4_adjust_len(ip, -(hdr_len + trl_len));
+ ip->ttl = ctx->ipsec.ip_ttl;
+ ip->tos = ctx->ipsec.ip_tos;
+ ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
+ ip->chksum = 0;
+ odph_ipv4_csum_update(pkt);
+
+ /* Correct the packet length and move payload into position */
+ memmove(ipv4_data_p(ip),
+ ipv4_data_p(ip) + hdr_len,
+ odp_be_to_cpu_16(ip->tot_len));
+ odp_packet_pull_tail(pkt, hdr_len + trl_len);
+
+ /* Fall through to next state */
+ return PKT_CONTINUE;
+}
+
+static int generate_iv(uint8_t *buf, uint32_t size)
+{
+ uint32_t n = 0;
+ int32_t ret;
+
+ while (n < size) {
+ ret = odp_random_data(buf + n, size - n, ODP_RANDOM_CRYPTO);
+ if (ret < 0)
+ return 1;
+ n += ret;
+ }
+ return 0;
+}
+
+/**
+ * Packet Processing - Output IPsec packet classification
+ *
+ * Verify the outbound packet has a match in the IPsec cache,
+ * if so issue prepend IPsec headers and prepare parameters
+ * for crypto API call. Post the packet to ATOMIC queue so
+ * that sequence numbers can be applied in packet order as
+ * the next processing step.
+ *
+ * @param pkt Packet to classify
+ * @param ctx Packet process context
+ * @param skip Pointer to return "skip" indication
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt,
+ pkt_ctx_t *ctx,
+ odp_bool_t *skip)
+{
+ uint8_t *buf = odp_packet_data(pkt);
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ uint16_t ip_data_len = ipv4_data_len(ip);
+ uint8_t *ip_data = ipv4_data_p(ip);
+ ipsec_cache_entry_t *entry;
+ odp_crypto_packet_op_param_t params;
+ int hdr_len = 0;
+ int trl_len = 0;
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+
+ /* Default to skip IPsec */
+ *skip = TRUE;
+
+ /* Find record */
+ entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
+ odp_be_to_cpu_32(ip->dst_addr),
+ ip->proto);
+ if (!entry)
+ return PKT_CONTINUE;
+
+ /* Save IPv4 stuff */
+ ctx->ipsec.ip_tos = ip->tos;
+ ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+ ctx->ipsec.ip_ttl = ip->ttl;
+
+ /* Initialize parameters block */
+ memset(&params, 0, sizeof(params));
+ params.session = entry->state.session;
+
+ if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
+ hdr_len += sizeof(odph_ipv4hdr_t);
+ ip_data = (uint8_t *)ip;
+ ip_data_len += sizeof(odph_ipv4hdr_t);
+ }
+ /* Compute ah and esp, determine length of headers, move the data */
+ if (entry->ah.alg) {
+ ah = (odph_ahhdr_t *)(ip_data + hdr_len);
+ hdr_len += sizeof(odph_ahhdr_t);
+ hdr_len += entry->ah.icv_len;
+ }
+ if (entry->esp.alg) {
+ esp = (odph_esphdr_t *)(ip_data + hdr_len);
+ hdr_len += sizeof(odph_esphdr_t);
+ hdr_len += entry->esp.iv_len;
+ }
+ memmove(ip_data + hdr_len, ip_data, ip_data_len);
+ ip_data += hdr_len;
+
+ /* update outer header in tunnel mode */
+ if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
+ /* tunnel addresses */
+ ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip);
+ ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip);
+ }
+
+ /* For cipher, compute encrypt length, build headers and request */
+ if (esp) {
+ uint32_t encrypt_len;
+ odph_esptrl_t *esp_t;
+
+ encrypt_len = ESP_ENCODE_LEN(ip_data_len +
+ sizeof(*esp_t),
+ entry->esp.block_len);
+ trl_len = encrypt_len - ip_data_len;
+
+ esp->spi = odp_cpu_to_be_32(entry->esp.spi);
+ if (generate_iv(esp->iv, entry->esp.iv_len))
+ return PKT_DROP;
+ params.cipher_iv_ptr = esp->iv;
+
+ esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1;
+ esp_t->pad_len = trl_len - sizeof(*esp_t);
+ if (entry->mode == IPSEC_SA_MODE_TUNNEL)
+ esp_t->next_header = ODPH_IPV4;
+ else
+ esp_t->next_header = ip->proto;
+ ip->proto = ODPH_IPPROTO_ESP;
+
+ params.cipher_range.offset = ip_data - buf;
+ params.cipher_range.length = encrypt_len;
+ }
+
+ /* For authentication, build header clear mutables and build request */
+ if (ah) {
+ memset(ah, 0, sizeof(*ah) + entry->ah.icv_len);
+ ah->spi = odp_cpu_to_be_32(entry->ah.spi);
+ ah->ah_len = 1 + (entry->ah.icv_len / 4);
+ if (entry->mode == IPSEC_SA_MODE_TUNNEL && !esp)
+ ah->next_header = ODPH_IPV4;
+ else
+ ah->next_header = ip->proto;
+ ip->proto = ODPH_IPPROTO_AH;
+
+ ip->chksum = 0;
+ ip->tos = 0;
+ ip->frag_offset = 0;
+ ip->ttl = 0;
+
+ params.auth_range.offset = ((uint8_t *)ip) - buf;
+ params.auth_range.length =
+ odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len);
+ params.hash_result_offset = ah->icv - buf;
+ }
+
+ /* Set IPv4 length before authentication */
+ ipv4_adjust_len(ip, hdr_len + trl_len);
+ if (!odp_packet_push_tail(pkt, hdr_len + trl_len))
+ return PKT_DROP;
+
+ /* Save remaining context */
+ ctx->ipsec.hdr_len = hdr_len;
+ ctx->ipsec.trl_len = trl_len;
+ ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+ ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+ ctx->ipsec.tun_hdr_offset = (entry->mode == IPSEC_SA_MODE_TUNNEL) ?
+ ((uint8_t *)ip - buf) : 0;
+ ctx->ipsec.ah_seq = &entry->state.ah_seq;
+ ctx->ipsec.esp_seq = &entry->state.esp_seq;
+ ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id;
+ memcpy(&ctx->ipsec.params, &params, sizeof(params));
+
+ *skip = FALSE;
+
+ return PKT_POSTED;
+}
+
+/**
+ * Packet Processing - Output IPsec packet sequence number assignment
+ *
+ * Assign the necessary sequence numbers and then issue the crypto API call
+ *
+ * @param pkt Packet to handle
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_seq(odp_packet_t *pkt,
+ pkt_ctx_t *ctx)
+{
+ uint8_t *buf = odp_packet_data(*pkt);
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*pkt, NULL);
+ odp_packet_t out_pkt;
+ ipsec_cache_entry_t *entry;
+
+ entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
+ odp_be_to_cpu_32(ip->dst_addr),
+ ip->proto);
+ if (!entry)
+ return PKT_DROP;
+
+ /* We were dispatched from atomic queue, assign sequence numbers */
+ if (ctx->ipsec.ah_offset) {
+ odph_ahhdr_t *ah;
+
+ ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
+ ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++);
+ }
+ if (ctx->ipsec.esp_offset) {
+ odph_esphdr_t *esp;
+
+ esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf);
+ esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++);
+ }
+ if (ctx->ipsec.tun_hdr_offset) {
+ odph_ipv4hdr_t *ip_tun;
+
+ ip_tun = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf);
+ ip_tun->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++);
+ }
+
+ /* Issue crypto request */
+ if (entry->async) {
+ if (odp_crypto_op_enq(pkt, NULL,
+ &ctx->ipsec.params, 1) != 1) {
+ ODPH_ERR("Error: odp_crypto_op_enq() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ return PKT_POSTED;
+ }
+
+ if (odp_crypto_op(pkt, &out_pkt, &ctx->ipsec.params, 1) != 1) {
+ ODPH_ERR("Error: odp_crypto_op() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ *pkt = out_pkt;
+
+ return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Output IPsec packet processing cleanup
+ *
+ * @param pkt Packet to handle
+ * @param ctx Packet process context
+ *
+ * @return PKT_CONTINUE if successful else PKT_DROP
+ */
+static
+pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt,
+ pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip;
+
+ if (odp_crypto_result(NULL, pkt) != 0)
+ return PKT_DROP;
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+
+ /* Finalize the IPv4 header */
+ ip->ttl = ctx->ipsec.ip_ttl;
+ ip->tos = ctx->ipsec.ip_tos;
+ ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
+ ip->chksum = 0;
+ odph_ipv4_csum_update(pkt);
+
+ /* Fall through to next state */
+ return PKT_CONTINUE;
+}
+
+/**
+ * Packet IO worker thread
+ *
+ * Loop calling odp_schedule to obtain packets from one of three sources,
+ * and continue processing the packet based on the state stored in its
+ * per packet context.
+ *
+ * - Input interfaces (i.e. new work)
+ * - Sequence number assignment queue
+ * - Per packet crypto API completion queue
+ *
+ * @param arg Required by "odph_thread_create", unused
+ *
+ * @return NULL (should never return)
+ */
+static
+int pktio_thread(void *arg ODP_UNUSED)
+{
+ int thr;
+ odp_packet_t pkt;
+ odp_event_t ev;
+ unsigned long pkt_cnt = 0;
+
+ thr = odp_thread_id();
+
+ printf("Pktio thread [%02i] starts\n", thr);
+
+ odp_barrier_wait(&global->sync_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ pkt_disposition_e rc;
+ pkt_ctx_t *ctx;
+ odp_queue_t dispatchq;
+ odp_event_subtype_t subtype;
+
+ /* Use schedule to get event from any input queue */
+ ev = schedule_fn(&dispatchq);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ /* Determine new work versus completion or sequence number */
+ if (ODP_EVENT_PACKET == odp_event_types(ev, &subtype)) {
+ pkt = odp_packet_from_event(ev);
+ if (global->seqnumq == dispatchq ||
+ global->completionq == dispatchq) {
+ ctx = get_pkt_ctx_from_pkt(pkt);
+ } else {
+ ctx = alloc_pkt_ctx(pkt);
+ if (!ctx) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ ctx->state = PKT_STATE_INPUT_VERIFY;
+ }
+ } else {
+ ODPH_ERR("Error: Bad event type\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * We now have a packet and its associated context. Loop here
+ * executing processing based on the current state value stored
+ * in the context as long as the processing return code
+ * indicates PKT_CONTINUE.
+ *
+ * For other return codes:
+ *
+ * o PKT_DONE - finished with the packet
+ * o PKT_DROP - something incorrect about the packet, drop it
+ * o PKT_POSTED - packet/event has been queued for later
+ */
+ do {
+ odp_bool_t skip = FALSE;
+
+ switch (ctx->state) {
+ case PKT_STATE_INPUT_VERIFY:
+
+ rc = do_input_verify(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_IN_CLASSIFY:
+
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ rc = do_ipsec_in_classify(&pkt,
+ ctx,
+ &skip);
+ break;
+
+ case PKT_STATE_IPSEC_IN_FINISH:
+
+ rc = do_ipsec_in_finish(pkt, ctx);
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ break;
+
+ case PKT_STATE_ROUTE_LOOKUP:
+
+ rc = do_route_fwd_db(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_OUT_CLASSIFY:
+
+ rc = do_ipsec_out_classify(pkt,
+ ctx,
+ &skip);
+ if (odp_unlikely(skip)) {
+ ctx->state = PKT_STATE_TRANSMIT;
+ } else {
+ ctx->state = PKT_STATE_IPSEC_OUT_SEQ;
+ if (odp_queue_enq(global->seqnumq, ev))
+ rc = PKT_DROP;
+ }
+ break;
+
+ case PKT_STATE_IPSEC_OUT_SEQ:
+
+ ctx->state = PKT_STATE_IPSEC_OUT_FINISH;
+ rc = do_ipsec_out_seq(&pkt, ctx);
+ break;
+
+ case PKT_STATE_IPSEC_OUT_FINISH:
+
+ rc = do_ipsec_out_finish(pkt, ctx);
+ ctx->state = PKT_STATE_TRANSMIT;
+ break;
+
+ case PKT_STATE_TRANSMIT:
+
+ if (odp_pktout_send(ctx->pktout, &pkt, 1) < 1) {
+ rc = PKT_DROP;
+ } else {
+ rc = PKT_DONE;
+ }
+ break;
+
+ default:
+ rc = PKT_DROP;
+ break;
+ }
+ } while (PKT_CONTINUE == rc);
+
+ /* Free context on drop or transmit */
+ if ((PKT_DROP == rc) || (PKT_DONE == rc))
+ free_pkt_ctx(ctx);
+
+
+ /* Check for drop */
+ if (PKT_DROP == rc)
+ odp_packet_free(pkt);
+
+ /* Print packet counts every once in a while */
+ if (PKT_DONE == rc) {
+ if (odp_unlikely(pkt_cnt++ % 1000 == 0)) {
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ODP ipsec example main function
+ */
+int
+main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int num_workers;
+ int i;
+ int stream_count;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odp_crypto_capability_t crypto_capa;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init_param;
+
+ /* create by default scheduled queues */
+ queue_create = odp_queue_create;
+ schedule_fn = odp_schedule_cb;
+
+ /* check for using poll queues */
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES")) {
+ queue_create = polled_odp_queue_create;
+ schedule_fn = polled_odp_schedule_cb;
+ }
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Signal handler has to be registered before global init in case ODP
+ * implementation creates internal threads/processes. */
+ signal(SIGINT, sig_handler);
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_crypto_capability(&crypto_capa)) {
+ ODPH_ERR("Error: Crypto capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((NULL == getenv("ODP_IPSEC_USE_POLL_QUEUES")) &&
+ (crypto_capa.queue_type_sched == 0)) {
+ ODPH_ERR("Error: scheduled type compl queue not supported.\n");
+ exit(EXIT_FAILURE);
+ } else if ((NULL != getenv("ODP_IPSEC_USE_POLL_QUEUES")) &&
+ crypto_capa.queue_type_plain == 0) {
+ ODPH_ERR("Error: Plain type compl queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+
+ if (NULL == global) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(global, 0, sizeof(global_data_t));
+ global->shm = shm;
+ odp_atomic_init_u32(&global->exit_threads, 0);
+ global->crypto_capa = crypto_capa;
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
+ /* Must init our databases before parsing args */
+ ipsec_init_pre();
+ init_fwd_db();
+ init_stream_db();
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &global->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &global->appl);
+
+ num_workers = MAX_WORKERS;
+ if (global->appl.cpu_count && global->appl.cpu_count < MAX_WORKERS)
+ num_workers = global->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ /* Create a barrier to synchronize thread startup */
+ odp_barrier_init(&global->sync_barrier, num_workers);
+
+ /* Create packet buffer pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
+ params.type = ODP_POOL_PACKET;
+
+ global->pkt_pool = odp_pool_create("packet_pool", &params);
+
+ if (ODP_POOL_INVALID == global->pkt_pool) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create context buffer pool */
+ params.buf.size = SHM_CTX_POOL_BUF_SIZE;
+ params.buf.align = 0;
+ params.buf.num = SHM_CTX_POOL_BUF_COUNT;
+ params.type = ODP_POOL_BUFFER;
+
+ global->ctx_pool = odp_pool_create("ctx_pool", &params);
+
+ if (ODP_POOL_INVALID == global->ctx_pool) {
+ ODPH_ERR("Error: context pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Populate our IPsec cache */
+ printf("Using %s mode for crypto API\n\n",
+ (CRYPTO_API_SYNC == global->appl.mode) ? "SYNC" : "ASYNC");
+ ipsec_init_post(global->appl.mode);
+
+ /* Initialize interfaces (which resolves FWD DB entries */
+ for (i = 0; i < global->appl.if_count; i++)
+ initialize_intf(global->appl.if_names[i]);
+
+ /* If we have test streams build them before starting workers */
+ resolve_stream_db();
+ stream_count = create_stream_db_inputs();
+ if (stream_count < 0) {
+ ODPH_ERR("Error: creating input packets failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Create and init worker threads
+ */
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = pktio_thread;
+ thr_param.arg = NULL;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ /* If there are streams attempt to verify them. Otherwise, run until
+ * SIGINT is received. */
+ if (stream_count) {
+ odp_bool_t done;
+
+ do {
+ done = verify_stream_db_outputs();
+ usleep(100000);
+ } while (!done);
+ printf("All received\n");
+ odp_atomic_store_u32(&global->exit_threads, 1);
+ }
+ odph_thread_join(thread_tbl, num_workers);
+
+ /* Stop and close used pktio devices */
+ for (i = 0; i < global->appl.if_count; i++) {
+ odp_pktio_t pktio = odp_pktio_lookup(global->appl.if_names[i]);
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
+ ODPH_ERR("Error: failed to close pktio %s\n",
+ global->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ free(global->appl.if_names);
+ free(global->appl.if_str);
+
+ if (destroy_ipsec_cache())
+ ODPH_ERR("Error: crypto session destroy failed\n");
+
+ if (odp_queue_destroy(global->completionq))
+ ODPH_ERR("Error: queue destroy failed\n");
+ if (odp_queue_destroy(global->seqnumq))
+ ODPH_ERR("Error: queue destroy failed\n");
+
+ if (odp_pool_destroy(global->pkt_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+ if (odp_pool_destroy(global->ctx_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+ if (odp_pool_destroy(global->out_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+
+ shm = odp_shm_lookup("shm_ipsec_cache");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_ipsec_cache failed\n");
+ shm = odp_shm_lookup("shm_fwd_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_fwd_db failed\n");
+ shm = odp_shm_lookup("shm_sa_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sa_db failed\n");
+ shm = odp_shm_lookup("shm_tun_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_tun_db failed\n");
+ shm = odp_shm_lookup("shm_sp_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sp_db failed\n");
+
+ deinit_stream_db();
+
+ if (odp_shm_free(global->shm)) {
+ ODPH_ERR("Error: shm free global data failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *token;
+ size_t len;
+ int rc = 0;
+ int i;
+
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"mode", required_argument, NULL, 'm'}, /* return 'm' */
+ {"route", required_argument, NULL, 'r'}, /* return 'r' */
+ {"policy", required_argument, NULL, 'p'}, /* return 'p' */
+ {"ah", required_argument, NULL, 'a'}, /* return 'a' */
+ {"esp", required_argument, NULL, 'e'}, /* return 'e' */
+ {"tunnel", required_argument, NULL, 't'}, /* return 't' */
+ {"stream", required_argument, NULL, 's'}, /* return 's' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:i:m:r:p:a:e:t:s:h";
+
+ printf("\nParsing command line options\n");
+
+ appl_args->cpu_count = 1; /* use one worker by default */
+ appl_args->mode = 0; /* turn off async crypto API by default */
+
+ while (!rc) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (-1 == opt)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (0 == len) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_str = malloc(len);
+ if (appl_args->if_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ appl_args->if_count = i;
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'm':
+ appl_args->mode = atoi(optarg);
+ break;
+
+ case 'r':
+ rc = create_fwd_db_entry(optarg, appl_args->if_names,
+ appl_args->if_count);
+ break;
+
+ case 'p':
+ rc = create_sp_db_entry(optarg, TRUE);
+ break;
+
+ case 'a':
+ rc = create_sa_db_entry(optarg, FALSE);
+ break;
+
+ case 'e':
+ rc = create_sa_db_entry(optarg, TRUE);
+ break;
+
+ case 't':
+ rc = create_tun_db_entry(optarg);
+ break;
+
+ case 's':
+ rc = create_stream_db_entry(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (rc) {
+ printf("ERROR: failed parsing -%c option\n", opt);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ odp_sys_info_print();
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+
+ printf("\n");
+
+ dump_fwd_db();
+ dump_sp_db();
+ dump_sa_db();
+ dump_tun_db();
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth1,eth2,eth3 -m 0\n"
+ "\n"
+ "OpenDataPlane example application.\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " -m, --mode 0: SYNC\n"
+ " 1: ASYNC\n"
+ " Default: 0: SYNC api mode\n"
+ "\n"
+ "Routing / IPSec OPTIONS:\n"
+ " -r, --route SubNet,Intf,NextHopMAC\n"
+ " -p, --policy SrcSubNet,DstSubNet,(in|out),(ah|esp|both)\n"
+ " -e, --esp SrcIP,DstIP,(3des|null),SPI,Key192\n"
+ " -a, --ah SrcIP,DstIP,(sha256|sha1|md5|null),SPI,Key(256|160|128)\n"
+ "\n"
+ " Where: NextHopMAC is raw hex/colon notation, i.e. 03:BA;44:9A:CE:02\n"
+ " IP is decimal/dot notation, i.e. 192.168.1.1\n"
+ " SubNet is decimal/dot/slash notation, i.e 192.168.0.0/16\n"
+ " SPI is raw hex, 32 bits\n"
+ " KeyXXX is raw hex, XXX bits long\n"
+ "\n"
+ " Examples:\n"
+ " -r 192.168.222.0/24,p8p1,08:00:27:F5:8B:DB\n"
+ " -p 192.168.111.0/24,192.168.222.0/24,out,esp\n"
+ " -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
+ " -a 192.168.111.2,192.168.222.2,md5,201,a731649644c5dee92cbd9c2e7e188ee6\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -s, --stream SrcIP,DstIP,InIntf,OutIntf,Count,Length\n"
+ " -h, --help Display help and exit.\n"
+ " environment variables: ODP_IPSEC_USE_POLL_QUEUES\n"
+ " to enable use of poll queues instead of scheduled (default)\n"
+ " ODP_IPSEC_STREAM_VERIFY_MDEQ\n"
+ " to enable use of multiple dequeue for queue draining during\n"
+ " stream verification instead of single dequeue (default)\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
+
+static odp_bool_t cipher_supported(odp_cipher_alg_t alg, const sa_db_entry_t *sa, int *sa_flags)
+{
+ const odp_crypto_cipher_algos_t *algos = &global->crypto_capa.ciphers;
+ odp_bool_t alg_ok = true;
+ int num;
+
+ switch (alg) {
+ case ODP_CIPHER_ALG_NULL:
+ if (!algos->bit.null)
+ alg_ok = false;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ if (!algos->bit.trides_cbc)
+ alg_ok = false;
+ break;
+ default:
+ alg_ok = false;
+ break;
+ }
+ if (!alg_ok) {
+ printf("ERROR: cipher algorithm not supported\n");
+ return false;
+ }
+
+ num = odp_crypto_cipher_capability(alg, NULL, 0);
+ if (num < 0) {
+ printf("ERROR: odp_crypto_cipher_capability() failed\n");
+ return false;
+ }
+ odp_crypto_cipher_capability_t cipher_capa[num];
+
+ (void)odp_crypto_cipher_capability(alg, cipher_capa, num);
+ for (int n = 0; n < num; n++) {
+ odp_crypto_cipher_capability_t *capa = &cipher_capa[n];
+
+ if (capa->key_len == sa->key.length &&
+ capa->iv_len == sa->iv_len) {
+ if (capa->bit_mode)
+ *sa_flags |= BIT_MODE_CIPHER;
+ return true;
+ }
+ }
+ printf("ERROR: cipher key length or IV length not supported\n");
+ return false;
+}
+
+static odp_bool_t auth_supported(odp_auth_alg_t alg, const sa_db_entry_t *sa, int *sa_flags)
+{
+ const odp_crypto_auth_algos_t *algos = &global->crypto_capa.auths;
+ odp_bool_t alg_ok = true;
+ int num;
+
+ switch (alg) {
+ case ODP_AUTH_ALG_NULL:
+ if (!algos->bit.null)
+ alg_ok = false;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ if (!algos->bit.md5_hmac)
+ alg_ok = false;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ if (!algos->bit.sha1_hmac)
+ alg_ok = false;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ if (!algos->bit.sha256_hmac)
+ alg_ok = false;
+ break;
+ default:
+ alg_ok = false;
+ break;
+ }
+ if (!alg_ok) {
+ printf("ERROR: auth algorithm not supported\n");
+ return false;
+ }
+
+ num = odp_crypto_auth_capability(alg, NULL, 0);
+ if (num < 0) {
+ printf("ERROR: odp_crypto_auth_capability() failed\n");
+ return false;
+ }
+ odp_crypto_auth_capability_t auth_capa[num];
+
+ (void)odp_crypto_auth_capability(alg, auth_capa, num);
+ for (int n = 0; n < num; n++) {
+ odp_crypto_auth_capability_t *capa = &auth_capa[n];
+
+ if (capa->digest_len == sa->icv_len &&
+ capa->key_len == sa->key.length &&
+ capa->iv_len == 0) {
+ if (capa->bit_mode)
+ *sa_flags |= BIT_MODE_AUTH;
+ return true;
+ }
+ }
+ printf("ERROR: auth ICV length or key length not supported\n");
+ return false;
+}
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa, int *sa_flags);
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa, int *sa_flags)
+{
+ return sa->alg.cipher ? cipher_supported(sa->alg.u.cipher, sa, sa_flags)
+ : auth_supported(sa->alg.u.auth, sa, sa_flags);
+}
diff --git a/example/ipsec_crypto/odp_ipsec_cache.c b/example/ipsec_crypto/odp_ipsec_cache.c
new file mode 100644
index 000000000..0461d4fbf
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_cache.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+
+#include <odp/helper/ipsec.h>
+
+#include <odp_ipsec_cache.h>
+
+/** Global pointer to ipsec_cache db */
+ipsec_cache_t *ipsec_cache;
+
+void init_ipsec_cache(void)
+{
+ odp_shm_t shm;
+ int i;
+
+ shm = odp_shm_reserve("shm_ipsec_cache",
+ sizeof(ipsec_cache_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ipsec_cache = odp_shm_addr(shm);
+
+ if (ipsec_cache == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(ipsec_cache, 0, sizeof(*ipsec_cache));
+
+ for (i = 0; i < MAX_DB; i++)
+ ipsec_cache->array[i].state.session =
+ ODP_CRYPTO_SESSION_INVALID;
+}
+
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ crypto_api_mode_e api_mode,
+ odp_bool_t in,
+ odp_queue_t completionq,
+ odp_pool_t out_pool)
+{
+ odp_crypto_session_param_t params;
+ ipsec_cache_entry_t *entry;
+ odp_crypto_ses_create_err_t ses_create_rc;
+ odp_crypto_session_t session;
+ sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT;
+
+ /* Verify we have a good entry */
+ entry = &ipsec_cache->array[ipsec_cache->index];
+ if (MAX_DB <= ipsec_cache->index)
+ return -1;
+
+ /* Verify SA mode match in case of cipher&auth */
+ if (cipher_sa && auth_sa &&
+ (cipher_sa->mode != auth_sa->mode))
+ return -1;
+
+ odp_crypto_session_param_init(&params);
+
+ /* Setup parameters and call crypto library to create session */
+ params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE;
+ params.op_type = ODP_CRYPTO_OP_TYPE_BASIC;
+ params.auth_cipher_text = TRUE;
+ if (CRYPTO_API_SYNC == api_mode) {
+ params.op_mode = ODP_CRYPTO_SYNC;
+ params.compl_queue = ODP_QUEUE_INVALID;
+ params.output_pool = ODP_POOL_INVALID;
+ entry->async = FALSE;
+ } else {
+ params.op_mode = ODP_CRYPTO_ASYNC;
+ params.compl_queue = completionq;
+ params.output_pool = out_pool;
+ entry->async = TRUE;
+ }
+
+ entry->sa_flags = 0;
+
+ /* Cipher */
+ if (cipher_sa) {
+ params.cipher_alg = cipher_sa->alg.u.cipher;
+ params.cipher_key.data = cipher_sa->key.data;
+ params.cipher_key.length = cipher_sa->key.length;
+ params.cipher_iv_len = cipher_sa->iv_len;
+ mode = cipher_sa->mode;
+ if (cipher_sa->flags & BIT_MODE_CIPHER)
+ entry->sa_flags |= BIT_MODE_CIPHER;
+ } else {
+ params.cipher_alg = ODP_CIPHER_ALG_NULL;
+ params.cipher_iv_len = 0;
+ }
+
+ /* Auth */
+ if (auth_sa) {
+ params.auth_alg = auth_sa->alg.u.auth;
+ params.auth_key.data = auth_sa->key.data;
+ params.auth_key.length = auth_sa->key.length;
+ params.auth_digest_len = auth_sa->icv_len;
+ mode = auth_sa->mode;
+ params.hash_result_in_auth_range = true;
+ if (auth_sa->flags & BIT_MODE_AUTH)
+ entry->sa_flags |= BIT_MODE_AUTH;
+ } else {
+ params.auth_alg = ODP_AUTH_ALG_NULL;
+ }
+
+ /* Synchronous session create for now */
+ if (odp_crypto_session_create(&params, &session, &ses_create_rc))
+ return -1;
+ if (ODP_CRYPTO_SES_ERR_NONE != ses_create_rc)
+ return -1;
+
+ /* Copy remainder */
+ if (cipher_sa) {
+ entry->src_ip = cipher_sa->src_ip;
+ entry->dst_ip = cipher_sa->dst_ip;
+ entry->esp.alg = cipher_sa->alg.u.cipher;
+ entry->esp.spi = cipher_sa->spi;
+ entry->esp.block_len = cipher_sa->block_len;
+ entry->esp.iv_len = cipher_sa->iv_len;
+ memcpy(&entry->esp.key, &cipher_sa->key, sizeof(ipsec_key_t));
+ }
+ if (auth_sa) {
+ entry->src_ip = auth_sa->src_ip;
+ entry->dst_ip = auth_sa->dst_ip;
+ entry->ah.alg = auth_sa->alg.u.auth;
+ entry->ah.spi = auth_sa->spi;
+ entry->ah.icv_len = auth_sa->icv_len;
+ memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t));
+ }
+
+ if (tun) {
+ entry->tun_src_ip = tun->tun_src_ip;
+ entry->tun_dst_ip = tun->tun_dst_ip;
+ mode = IPSEC_SA_MODE_TUNNEL;
+
+ int ret;
+
+ if (!in) {
+ /* init tun hdr id */
+ ret = odp_random_data((uint8_t *)
+ &entry->state.tun_hdr_id,
+ sizeof(entry->state.tun_hdr_id),
+ 1);
+ if (ret != sizeof(entry->state.tun_hdr_id))
+ return -1;
+ }
+ }
+ entry->mode = mode;
+
+ /* Initialize state */
+ entry->state.esp_seq = 0;
+ entry->state.ah_seq = 0;
+ entry->state.session = session;
+
+ /* Add entry to the appropriate list */
+ ipsec_cache->index++;
+ if (in) {
+ entry->next = ipsec_cache->in_list;
+ ipsec_cache->in_list = entry;
+ } else {
+ entry->next = ipsec_cache->out_list;
+ ipsec_cache->out_list = entry;
+ }
+
+ return 0;
+}
+
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_esphdr_t *esp)
+{
+ ipsec_cache_entry_t *entry = ipsec_cache->in_list;
+
+ /* Look for a hit */
+ for (; NULL != entry; entry = entry->next) {
+ if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip))
+ if ((entry->tun_src_ip != src_ip) ||
+ (entry->tun_dst_ip != dst_ip))
+ continue;
+ if (ah &&
+ ((!entry->ah.alg) ||
+ (entry->ah.spi != odp_be_to_cpu_32(ah->spi))))
+ continue;
+ if (esp &&
+ ((!entry->esp.alg) ||
+ (entry->esp.spi != odp_be_to_cpu_32(esp->spi))))
+ continue;
+ break;
+ }
+
+ return entry;
+}
+
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+ uint32_t dst_ip,
+ uint8_t proto ODP_UNUSED)
+{
+ ipsec_cache_entry_t *entry = ipsec_cache->out_list;
+
+ /* Look for a hit */
+ for (; NULL != entry; entry = entry->next) {
+ if ((entry->src_ip == src_ip) && (entry->dst_ip == dst_ip))
+ break;
+ }
+ return entry;
+}
+
+int destroy_ipsec_cache(void)
+{
+ ipsec_cache_entry_t *entry;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < MAX_DB; i++) {
+ entry = &ipsec_cache->array[i];
+ if (entry->state.session != ODP_CRYPTO_SESSION_INVALID)
+ ret += odp_crypto_session_destroy(entry->state.session);
+ }
+
+ return ret;
+}
diff --git a/example/ipsec_crypto/odp_ipsec_cache.h b/example/ipsec_crypto/odp_ipsec_cache.h
new file mode 100644
index 000000000..29c1b983a
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_cache.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_CACHE_H_
+#define ODP_IPSEC_CACHE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/ipsec.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+
+/**
+ * Mode specified on command line indicating how to exercise API
+ */
+typedef enum {
+ CRYPTO_API_SYNC, /**< Synchronous mode */
+ CRYPTO_API_ASYNC, /**< Asynchronous mode */
+} crypto_api_mode_e;
+
+/**
+ * IPsec cache data base entry
+ */
+typedef struct ipsec_cache_entry_s {
+ struct ipsec_cache_entry_s *next; /**< Next entry on list */
+ odp_bool_t async; /**< ASYNC or SYNC mode */
+ int sa_flags;
+ uint32_t src_ip; /**< Source v4 address */
+ uint32_t dst_ip; /**< Destination v4 address */
+ sa_mode_t mode; /**< SA mode - transport/tun */
+ uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */
+ uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */
+ struct {
+ odp_cipher_alg_t alg; /**< Cipher algorithm */
+ uint32_t spi; /**< Cipher SPI */
+ uint32_t block_len; /**< Cipher block length */
+ uint32_t iv_len; /**< Cipher IV length */
+ ipsec_key_t key; /**< Cipher key */
+ } esp;
+ struct {
+ odp_auth_alg_t alg; /**< Auth algorithm */
+ uint32_t spi; /**< Auth SPI */
+ uint32_t icv_len; /**< Auth ICV length */
+ ipsec_key_t key; /**< Auth key */
+ } ah;
+
+ /* Per SA state */
+ struct {
+ odp_crypto_session_t session; /**< Crypto session handle */
+ uint32_t esp_seq; /**< ESP TX sequence number */
+ uint32_t ah_seq; /**< AH TX sequence number */
+ odp_u16be_t tun_hdr_id; /**< Tunnel header IP ID */
+ } state;
+} ipsec_cache_entry_t;
+
+/**
+ * IPsec cache data base global structure
+ */
+typedef struct ipsec_cache_s {
+ uint32_t index; /**< Index of next available entry */
+ ipsec_cache_entry_t *in_list; /**< List of active input entries */
+ ipsec_cache_entry_t *out_list; /**< List of active output entries */
+ ipsec_cache_entry_t array[MAX_DB]; /**< Entry storage */
+} ipsec_cache_t;
+
+/** Global pointer to ipsec_cache db */
+extern ipsec_cache_t *ipsec_cache;
+
+/** Initialize IPsec cache */
+void init_ipsec_cache(void);
+
+/**
+ * Create an entry in the IPsec cache
+ *
+ * @param cipher_sa Cipher SA DB entry pointer
+ * @param auth_sa Auth SA DB entry pointer
+ * @param tun Tunnel DB entry pointer
+ * @param api_mode Crypto API mode for testing
+ * @param in Direction (input versus output)
+ * @param completionq Completion queue
+ * @param out_pool Output buffer pool
+ *
+ * @return 0 if successful else -1
+ */
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ crypto_api_mode_e api_mode,
+ odp_bool_t in,
+ odp_queue_t completionq,
+ odp_pool_t out_pool);
+
+/**
+ * Find a matching IPsec cache entry for input packet
+ *
+ * @param src_ip Source IPv4 address
+ * @param dst_ip Destination IPv4 address
+ * @param ah Pointer to AH header in packet else NULL
+ * @param esp Pointer to ESP header in packet else NULL
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_esphdr_t *esp);
+
+/**
+ * Find a matching IPsec cache entry for output packet
+ *
+ * @param src_ip Source IPv4 address
+ * @param dst_ip Destination IPv4 address
+ * @param proto IPv4 protocol (currently all protocols match)
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+ uint32_t dst_ip,
+ uint8_t proto);
+
+int destroy_ipsec_cache(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_ah_in.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_ah_in.sh
new file mode 100755
index 000000000..0c8112306
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_ah_in.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test input AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,ah \
+ -a 192.168.222.2,192.168.111.2,md5,300,27f6d123d7077b361662fc6e451f65d8 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_ah_out.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_ah_out.sh
new file mode 100755
index 000000000..b0bb210f5
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_ah_out.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test output AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,ah \
+ -a 192.168.111.2,192.168.222.2,md5,200,a731649644c5dee92cbd9c2e7e188ee6 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_both_in.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_both_in.sh
new file mode 100755
index 000000000..4b8c6ab63
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_both_in.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Test AH and ESP input
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,both \
+ -a 192.168.222.2,192.168.111.2,md5,300,27f6d123d7077b361662fc6e451f65d8 \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_both_out.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_both_out.sh
new file mode 100755
index 000000000..665534d98
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_both_out.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Test AH and ESP output
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,both \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -a 192.168.111.2,192.168.222.2,md5,200,a731649644c5dee92cbd9c2e7e188ee6 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_esp_in.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_esp_in.sh
new file mode 100755
index 000000000..6e6ff5769
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_esp_in.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test input ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,08:00:27:76:B5:E0 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,esp \
+ -e 192.168.222.2,192.168.111.2,3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_esp_out.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_esp_out.sh
new file mode 100755
index 000000000..2b7107f80
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_esp_out.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test output ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -p 192.168.111.0/24,192.168.222.0/24,out,esp \
+ -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_live.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_live.sh
new file mode 100755
index 000000000..1393c7af3
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_live.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=1
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_crypto_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_crypto -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -p 192.168.111.0/24,192.168.222.0/24,out,both \
+ -e 192.168.111.2,192.168.222.2,\
+ 3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+ -a 192.168.111.2,192.168.222.2,md5,200,a731649644c5dee92cbd9c2e7e188ee6 \
+ -p 192.168.222.0/24,192.168.111.0/24,in,both \
+ -e 192.168.222.2,192.168.111.2,\
+ 3des,301,c966199f24d095f3990a320d749056401e82b26570320292 \
+ -a 192.168.222.2,192.168.111.2,md5,300,27f6d123d7077b361662fc6e451f65d8 \
+ -c 2 "$@" & echo $! > $PID) | tee -a $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application exits
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_router.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_router.sh
new file mode 100755
index 000000000..40c353ee0
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_router.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=2
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_crypto_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_crypto -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -c 1 "$@" & echo $! > $PID) | tee -a $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application exits
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_crypto/odp_ipsec_crypto_run_simple.sh b/example/ipsec_crypto/odp_ipsec_crypto_run_simple.sh
new file mode 100755
index 000000000..00b176b36
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_crypto_run_simple.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Simple router test
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_crypto -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_crypto/odp_ipsec_fwd_db.c b/example/ipsec_crypto/odp_ipsec_fwd_db.c
new file mode 100644
index 000000000..292d9c7c6
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_fwd_db.c
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_fwd_db.h>
+
+/** Global pointer to fwd db */
+fwd_db_t *fwd_db;
+
+void init_fwd_db(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("shm_fwd_db",
+ sizeof(fwd_db_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fwd_db = odp_shm_addr(shm);
+
+ if (fwd_db == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(fwd_db, 0, sizeof(*fwd_db));
+}
+
+int create_fwd_db_entry(char *input, char **if_names, int if_count)
+{
+ int pos = 0, i, match = 0;
+ char *local;
+ char *str;
+ char *save;
+ char *token;
+ fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];
+
+ /* Verify we haven't run out of space */
+ if (MAX_DB <= fwd_db->index)
+ return -1;
+
+ /* Make a local copy */
+ local = malloc(strlen(input) + 1);
+ if (NULL == local)
+ return -1;
+ strcpy(local, input);
+
+ /* Setup for using "strtok_r" to search input string */
+ str = local;
+ save = NULL;
+
+ /* Parse tokens separated by ',' */
+ while (NULL != (token = strtok_r(str, ",", &save))) {
+ str = NULL; /* reset str for subsequent strtok_r calls */
+
+ /* Parse token based on its position */
+ switch (pos) {
+ case 0:
+ parse_ipv4_string(token,
+ &entry->subnet.addr,
+ &entry->subnet.mask);
+ break;
+ case 1:
+ strncpy(entry->oif, token, OIF_LEN - 1);
+ entry->oif[OIF_LEN - 1] = 0;
+ for (i = 0; i < if_count; i++) {
+ if (!strcmp(if_names[i], entry->oif)) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match) {
+ printf("ERROR: interface name not correct for route\n");
+ free(local);
+ return -1;
+ }
+ break;
+ case 2:
+ parse_mac_string(token, entry->dst_mac);
+ break;
+ default:
+ printf("ERROR: extra token \"%s\" at position %d\n",
+ token, pos);
+ break;
+ }
+
+ /* Advance to next position */
+ pos++;
+ }
+
+ /* Verify we parsed exactly the number of tokens we expected */
+ if (3 != pos) {
+ printf("ERROR: \"%s\" contains %d tokens, expected 3\n",
+ input,
+ pos);
+ free(local);
+ return -1;
+ }
+
+ /* Add route to the list */
+ fwd_db->index++;
+ entry->next = fwd_db->list;
+ fwd_db->list = entry;
+
+ free(local);
+ return 0;
+}
+
+void resolve_fwd_db(char *intf, odp_pktio_t pktio, odp_pktout_queue_t pktout,
+ uint8_t *mac)
+{
+ fwd_db_entry_t *entry;
+
+ /* Walk the list and attempt to set output queue and MAC */
+ for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
+ if (strcmp(intf, entry->oif))
+ continue;
+
+ entry->pktio = pktio;
+ entry->pktout = pktout;
+ memcpy(entry->src_mac, mac, ODPH_ETHADDR_LEN);
+ }
+}
+
+void dump_fwd_db_entry(fwd_db_entry_t *entry)
+{
+ char subnet_str[MAX_STRING];
+ char mac_str[MAX_STRING];
+
+ printf(" %s %s %s\n",
+ ipv4_subnet_str(subnet_str, &entry->subnet),
+ entry->oif,
+ mac_addr_str(mac_str, entry->dst_mac));
+}
+
+void dump_fwd_db(void)
+{
+ fwd_db_entry_t *entry;
+
+ printf("\n"
+ "Routing table\n"
+ "-------------\n");
+
+ for (entry = fwd_db->list; NULL != entry; entry = entry->next)
+ dump_fwd_db_entry(entry);
+}
+
+fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip)
+{
+ fwd_db_entry_t *entry;
+
+ for (entry = fwd_db->list; NULL != entry; entry = entry->next)
+ if (entry->subnet.addr == (dst_ip & entry->subnet.mask))
+ break;
+ return entry;
+}
diff --git a/example/ipsec_crypto/odp_ipsec_fwd_db.h b/example/ipsec_crypto/odp_ipsec_fwd_db.h
new file mode 100644
index 000000000..d9c84b29b
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_fwd_db.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_FWD_DB_H_
+#define ODP_IPSEC_FWD_DB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp_ipsec_misc.h>
+
+#define OIF_LEN 32
+
+/**
+ * Forwarding data base entry
+ */
+typedef struct fwd_db_entry_s {
+ struct fwd_db_entry_s *next; /**< Next entry on list */
+ char oif[OIF_LEN]; /**< Output interface name */
+ odp_pktio_t pktio; /**< Output PktI/O interface */
+ odp_pktout_queue_t pktout; /**< Output transmit queue */
+ uint8_t src_mac[ODPH_ETHADDR_LEN]; /**< Output source MAC */
+ uint8_t dst_mac[ODPH_ETHADDR_LEN]; /**< Output destination MAC */
+ ip_addr_range_t subnet; /**< Subnet for this router */
+} fwd_db_entry_t;
+
+/**
+ * Forwarding data base global structure
+ */
+typedef struct fwd_db_s {
+ uint32_t index; /**< Next available entry */
+ fwd_db_entry_t *list; /**< List of active routes */
+ fwd_db_entry_t array[MAX_DB]; /**< Entry storage */
+} fwd_db_t;
+
+/** Global pointer to fwd db */
+extern fwd_db_t *fwd_db;
+
+/** Initialize FWD DB */
+void init_fwd_db(void);
+
+/**
+ * Create a forwarding database entry
+ *
+ * String is of the format "SubNet,Intf,NextHopMAC"
+ *
+ * @param input Pointer to string describing route
+ *
+ * @param if_names Array of Name of the interfaces available
+ *
+ * @param if_count number of interfaces in if_names array
+ *
+ * @return 0 if successful else -1
+ */
+int create_fwd_db_entry(char *input, char **if_names, int if_count);
+
+/**
+ * Scan FWD DB entries and resolve output queue and source MAC address
+ *
+ * @param intf Interface name string
+ * @param pktio Output packet interface
+ * @param outq Output queue for packet transmit
+ * @param mac MAC address of this interface
+ */
+void resolve_fwd_db(char *intf, odp_pktio_t pktio, odp_pktout_queue_t pktout,
+ uint8_t *mac);
+
+/**
+ * Display one forwarding database entry
+ *
+ * @param entry Pointer to entry to display
+ */
+void dump_fwd_db_entry(fwd_db_entry_t *entry);
+
+/**
+ * Display the forwarding database
+ */
+void dump_fwd_db(void);
+
+/**
+ * Find a matching forwarding database entry
+ *
+ * @param dst_ip Destination IPv4 address
+ *
+ * @return pointer to forwarding DB entry else NULL
+ */
+fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_crypto/odp_ipsec_misc.h b/example/ipsec_crypto/odp_ipsec_misc.h
new file mode 100644
index 000000000..5fba274d0
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_misc.h
@@ -0,0 +1,328 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_MISC_H_
+#define ODP_IPSEC_MISC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MAX_DB 32 /**< maximum number of data base entries */
+#define MAX_LOOPBACK 10 /**< maximum number of loop back interfaces */
+#define MAX_STRING 32 /**< maximum string length */
+
+#define KEY_BITS_3DES 192 /**< 3DES cipher key length in bits */
+#define KEY_BITS_MD5_96 128 /**< MD5_96 auth key length in bits */
+#define KEY_BITS_SHA1_96 160 /**< MD5_96 auth key length in bits */
+#define KEY_BITS_SHA256_128 256 /**< SHA256_128 auth key length in bits */
+
+/**< Number of bits represented by a string of hexadecimal characters */
+#define KEY_STR_BITS(str) (4 * strlen(str))
+
+/** IPv4 helpers for data length and uint8t pointer */
+#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) - sizeof(odph_ipv4hdr_t))
+#define ipv4_data_p(ip) ((uint8_t *)((odph_ipv4hdr_t *)ip + 1))
+
+/** Helper for calculating encode length using data length and block size */
+#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+/**
+ * IPsec key
+ */
+typedef struct {
+ uint8_t data[32]; /**< Key data */
+ uint8_t length; /**< Key length */
+} ipsec_key_t;
+
+/**
+ * IPsec algorithm
+ */
+typedef struct {
+ odp_bool_t cipher;
+ union {
+ odp_cipher_alg_t cipher;
+ odp_auth_alg_t auth;
+ } u;
+} ipsec_alg_t;
+
+/**
+ * IP address range (subnet)
+ */
+typedef struct ip_addr_range_s {
+ uint32_t addr; /**< IP address */
+ uint32_t mask; /**< mask, 1 indicates bits are valid */
+} ip_addr_range_t;
+
+/**
+ * Parse text string representing a key into ODP key structure
+ *
+ * @param keystring Pointer to key string to convert
+ * @param key Pointer to ODP key structure to populate
+ * @param alg Cipher/authentication algorithm associated with the key
+ *
+ * @return 0 if successful else -1
+ */
+static inline
+int parse_key_string(char *keystring,
+ ipsec_key_t *key,
+ ipsec_alg_t *alg)
+{
+ int idx;
+ int key_bits_in = KEY_STR_BITS(keystring);
+ char temp[3];
+
+ key->length = 0;
+
+ /* Algorithm is either cipher or authentication */
+ if (alg->cipher) {
+ if ((alg->u.cipher == ODP_CIPHER_ALG_3DES_CBC) &&
+ (KEY_BITS_3DES == key_bits_in))
+ key->length = key_bits_in / 8;
+
+ } else {
+ if ((alg->u.auth == ODP_AUTH_ALG_MD5_HMAC) &&
+ (KEY_BITS_MD5_96 == key_bits_in))
+ key->length = key_bits_in / 8;
+ else if ((alg->u.auth == ODP_AUTH_ALG_SHA1_HMAC) &&
+ (KEY_BITS_SHA1_96 == key_bits_in))
+ key->length = key_bits_in / 8;
+ else if ((alg->u.auth == ODP_AUTH_ALG_SHA256_HMAC) &&
+ (KEY_BITS_SHA256_128 == key_bits_in))
+ key->length = key_bits_in / 8;
+ }
+
+ for (idx = 0; idx < key->length; idx++) {
+ temp[0] = *keystring++;
+ temp[1] = *keystring++;
+ temp[2] = 0;
+ key->data[idx] = strtol(temp, NULL, 16);
+ }
+
+ return key->length ? 0 : -1;
+}
+
+/**
+ * Check IPv4 address against a range/subnet
+ *
+ * @param addr IPv4 address to check
+ * @param range Pointer to address range to check against
+ *
+ * @return 1 if match else 0
+ */
+static inline
+int match_ip_range(uint32_t addr, ip_addr_range_t *range)
+{
+ return (range->addr == (addr & range->mask));
+}
+
+/**
+ * Generate text string representing IPv4 address
+ *
+ * @param b Pointer to buffer to store string
+ * @param addr IPv4 address
+ *
+ * @return Pointer to supplied buffer
+ */
+static inline
+char *ipv4_addr_str(char *b, uint32_t addr)
+{
+ sprintf(b, "%03d.%03d.%03d.%03d",
+ 0xFF & ((addr) >> 24),
+ 0xFF & ((addr) >> 16),
+ 0xFF & ((addr) >> 8),
+ 0xFF & ((addr) >> 0));
+ return b;
+}
+
+/**
+ * Parse text string representing an IPv4 address or subnet
+ *
+ * String is of the format "XXX.XXX.XXX.XXX(/W)" where
+ * "XXX" is decimal value and "/W" is optional subnet length
+ *
+ * @param ipaddress Pointer to IP address/subnet string to convert
+ * @param addr Pointer to return IPv4 address
+ * @param mask Pointer (optional) to return IPv4 mask
+ *
+ * @return 0 if successful else -1
+ */
+static inline
+int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *mask)
+{
+ unsigned int b[4];
+ int qualifier = 32;
+ int converted;
+
+ if (strchr(ipaddress, '/')) {
+ converted = sscanf(ipaddress, "%u.%u.%u.%u/%d",
+ &b[3], &b[2], &b[1], &b[0],
+ &qualifier);
+ if (5 != converted)
+ return -1;
+ } else {
+ converted = sscanf(ipaddress, "%u.%u.%u.%u",
+ &b[3], &b[2], &b[1], &b[0]);
+ if (4 != converted)
+ return -1;
+ }
+
+ if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
+ return -1;
+ if (!qualifier || (qualifier > 32))
+ return -1;
+
+ *addr = (uint32_t)b[0] | (uint32_t)b[1] << 8 | (uint32_t)b[2] << 16 | (uint32_t)b[3] << 24;
+ if (mask)
+ *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1));
+
+ return 0;
+}
+
+/**
+ * Generate text string representing IPv4 range/subnet, output
+ * in "XXX.XXX.XXX.XXX/W" format
+ *
+ * @param b Pointer to buffer to store string
+ * @param range Pointer to IPv4 address range
+ *
+ * @return Pointer to supplied buffer
+ */
+static inline
+char *ipv4_subnet_str(char *b, ip_addr_range_t *range)
+{
+ int idx;
+ int len;
+
+ for (idx = 0; idx < 32; idx++)
+ if (range->mask & (1 << idx))
+ break;
+ len = 32 - idx;
+
+ sprintf(b, "%03d.%03d.%03d.%03d/%d",
+ 0xFF & ((range->addr) >> 24),
+ 0xFF & ((range->addr) >> 16),
+ 0xFF & ((range->addr) >> 8),
+ 0xFF & ((range->addr) >> 0),
+ len);
+ return b;
+}
+
+/**
+ * Generate text string representing MAC address
+ *
+ * @param b Pointer to buffer to store string
+ * @param mac Pointer to MAC address
+ *
+ * @return Pointer to supplied buffer
+ */
+static inline
+char *mac_addr_str(char *b, uint8_t *mac)
+{
+ sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return b;
+}
+
+/**
+ * Parse text string representing a MAC address into byte araray
+ *
+ * String is of the format "XX:XX:XX:XX:XX:XX" where XX is hexadecimal
+ *
+ * @param macaddress Pointer to MAC address string to convert
+ * @param mac Pointer to MAC address byte array to populate
+ *
+ * @return 0 if successful else -1
+ */
+static inline
+int parse_mac_string(char *macaddress, uint8_t *mac)
+{
+ unsigned int macwords[ODPH_ETHADDR_LEN];
+ int converted;
+
+ converted = sscanf(macaddress,
+ "%x:%x:%x:%x:%x:%x",
+ &macwords[0], &macwords[1], &macwords[2],
+ &macwords[3], &macwords[4], &macwords[5]);
+ if (6 != converted)
+ return -1;
+
+ mac[0] = macwords[0];
+ mac[1] = macwords[1];
+ mac[2] = macwords[2];
+ mac[3] = macwords[3];
+ mac[4] = macwords[4];
+ mac[5] = macwords[5];
+
+ return 0;
+}
+
+/**
+ * Locate IPsec headers (AH and/or ESP) in packet
+ *
+ * @param ip Pointer to packets IPv4 header
+ * @param ah_p Pointer to location to return AH header pointer
+ * @param esp_p Pointer to location to return ESP header pointer
+ *
+ * @return length of IPsec headers found
+ */
+static inline
+int locate_ipsec_headers(odph_ipv4hdr_t *ip,
+ odph_ahhdr_t **ah_p,
+ odph_esphdr_t **esp_p)
+{
+ uint8_t *in = ipv4_data_p(ip);
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+
+ if (ODPH_IPPROTO_AH == ip->proto) {
+ ah = (odph_ahhdr_t *)in;
+ in += ((ah)->ah_len + 2) * 4;
+ if (ODPH_IPPROTO_ESP == ah->next_header) {
+ esp = (odph_esphdr_t *)in;
+ in += sizeof(odph_esphdr_t);
+ }
+ } else if (ODPH_IPPROTO_ESP == ip->proto) {
+ esp = (odph_esphdr_t *)in;
+ in += sizeof(odph_esphdr_t);
+ }
+
+ *ah_p = ah;
+ *esp_p = esp;
+ return in - (ipv4_data_p(ip));
+}
+
+/**
+ * Adjust IPv4 length
+ *
+ * @param ip Pointer to IPv4 header
+ * @param adj Signed adjustment value
+ */
+static inline
+void ipv4_adjust_len(odph_ipv4hdr_t *ip, int adj)
+{
+ ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_crypto/odp_ipsec_sa_db.c b/example/ipsec_crypto/odp_ipsec_sa_db.c
new file mode 100644
index 000000000..9ddbc1152
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_sa_db.c
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_sa_db.h>
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa, int *sa_flags);
+
+/** Global pointer to sa db */
+static sa_db_t *sa_db;
+
+/** Global pointer to tun db */
+static tun_db_t *tun_db;
+
+void init_sa_db(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("shm_sa_db",
+ sizeof(sa_db_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ sa_db = odp_shm_addr(shm);
+
+ if (sa_db == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(sa_db, 0, sizeof(*sa_db));
+}
+
+void init_tun_db(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("shm_tun_db",
+ sizeof(tun_db_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tun_db = odp_shm_addr(shm);
+
+ if (!tun_db) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(tun_db, 0, sizeof(*tun_db));
+}
+
+int create_sa_db_entry(char *input, odp_bool_t cipher)
+{
+ int pos = 0;
+ char *local;
+ char *str;
+ char *save;
+ char *token;
+ sa_db_entry_t *entry = &sa_db->array[sa_db->index];
+
+ /* Verify we have a good entry */
+ if (MAX_DB <= sa_db->index)
+ return -1;
+
+ /* Make a local copy */
+ local = malloc(strlen(input) + 1);
+ if (NULL == local)
+ return -1;
+ strcpy(local, input);
+
+ /* Set cipher versus auth */
+ entry->alg.cipher = cipher;
+
+ /* Setup for using "strtok_r" to search input string */
+ str = local;
+ save = NULL;
+
+ /* Parse tokens separated by ',' */
+ while (NULL != (token = strtok_r(str, ",", &save))) {
+ str = NULL; /* reset str for subsequent strtok_r calls */
+
+ /* Parse token based on its position */
+ switch (pos) {
+ case 0:
+ parse_ipv4_string(token, &entry->src_ip, NULL);
+ break;
+ case 1:
+ parse_ipv4_string(token, &entry->dst_ip, NULL);
+ break;
+ case 2:
+ if (cipher) {
+ if (0 == strcmp(token, "3des")) {
+ entry->alg.u.cipher =
+ ODP_CIPHER_ALG_3DES_CBC;
+ entry->block_len = 8;
+ entry->iv_len = 8;
+ } else {
+ entry->alg.u.cipher =
+ ODP_CIPHER_ALG_NULL;
+ }
+ } else {
+ if (0 == strcmp(token, "md5")) {
+ entry->alg.u.auth =
+ ODP_AUTH_ALG_MD5_HMAC;
+ entry->icv_len = 12;
+ } else if (!strcmp(token, "sha1")) {
+ entry->alg.u.auth =
+ ODP_AUTH_ALG_SHA1_HMAC;
+ entry->icv_len = 12;
+ } else if (!strcmp(token, "sha256")) {
+ entry->alg.u.auth =
+ ODP_AUTH_ALG_SHA256_HMAC;
+ entry->icv_len = 16;
+ } else {
+ entry->alg.u.auth = ODP_AUTH_ALG_NULL;
+ }
+ }
+ break;
+ case 3:
+ entry->spi = strtol(token, NULL, 16);
+ break;
+ case 4:
+ parse_key_string(token,
+ &entry->key,
+ &entry->alg);
+ break;
+ default:
+ printf("ERROR: extra token \"%s\" at position %d\n",
+ token, pos);
+ break;
+ }
+
+ /* Advance to next position */
+ pos++;
+ }
+
+ /* Verify we parsed exactly the number of tokens we expected */
+ if (5 != pos) {
+ printf("ERROR: \"%s\" contains %d tokens, expected 5\n",
+ input,
+ pos);
+ free(local);
+ return -1;
+ }
+
+ if (!sa_config_supported(entry, &entry->flags)) {
+ free(local);
+ return -1;
+ }
+
+ /* Add route to the list */
+ sa_db->index++;
+ entry->next = sa_db->list;
+ sa_db->list = entry;
+
+ free(local);
+ return 0;
+}
+
+int create_tun_db_entry(char *input)
+{
+ int pos = 0;
+ char *local;
+ char *str;
+ char *save;
+ char *token;
+ tun_db_entry_t *entry = &tun_db->array[tun_db->index];
+
+ /* Verify we have a good entry */
+ if (MAX_DB <= tun_db->index)
+ return -1;
+
+ /* Make a local copy */
+ local = malloc(strlen(input) + 1);
+ if (NULL == local)
+ return -1;
+ strcpy(local, input);
+
+ /* Setup for using "strtok_r" to search input string */
+ str = local;
+ save = NULL;
+
+ /* Parse tokens separated by ',' */
+ while (NULL != (token = strtok_r(str, ",", &save))) {
+ str = NULL; /* reset str for subsequent strtok_r calls */
+
+ /* Parse token based on its position */
+ switch (pos) {
+ case 0:
+ parse_ipv4_string(token, &entry->src_ip, NULL);
+ break;
+ case 1:
+ parse_ipv4_string(token, &entry->dst_ip, NULL);
+ break;
+ case 2:
+ parse_ipv4_string(token, &entry->tun_src_ip, NULL);
+ break;
+ case 3:
+ parse_ipv4_string(token, &entry->tun_dst_ip, NULL);
+ break;
+ default:
+ printf("ERROR: extra token \"%s\" at position %d\n",
+ token, pos);
+ break;
+ }
+ pos++;
+ }
+
+ /* Verify we parsed exactly the number of tokens we expected */
+ if (4 != pos) {
+ printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
+ input,
+ pos);
+ free(local);
+ return -1;
+ }
+
+ /* Add route to the list */
+ tun_db->index++;
+ entry->next = tun_db->list;
+ tun_db->list = entry;
+
+ free(local);
+ return 0;
+}
+
+tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
+ uint32_t ip_dst)
+{
+ tun_db_entry_t *entry = NULL;
+
+ /* Scan all entries and return first match */
+ for (entry = tun_db->list; NULL != entry; entry = entry->next) {
+ if (entry->src_ip != ip_src)
+ continue;
+ if (entry->dst_ip != ip_dst)
+ continue;
+ break;
+ }
+ return entry;
+}
+
+void dump_sa_db(void)
+{
+ sa_db_entry_t *entry;
+
+ printf("\n"
+ "Security association table\n"
+ "--------------------------\n");
+
+ for (entry = sa_db->list; NULL != entry; entry = entry->next) {
+ uint32_t idx;
+ char src_ip_str[MAX_STRING];
+ char dst_ip_str[MAX_STRING];
+ uint8_t *p = entry->key.data;
+
+
+ printf(" %s %s %s %X %d ",
+ entry->alg.cipher ? "esp" : "ah ",
+ ipv4_addr_str(src_ip_str, entry->src_ip),
+ ipv4_addr_str(dst_ip_str, entry->dst_ip),
+ entry->spi,
+ entry->alg.cipher ?
+ (int)entry->alg.u.cipher :
+ (int)entry->alg.u.auth);
+
+ /* Brute force key display */
+ for (idx = 0; idx < entry->key.length; idx++)
+ printf("%02X", *p++);
+
+ printf("\n");
+ }
+}
+
+sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
+ ip_addr_range_t *dst,
+ odp_bool_t cipher)
+{
+ sa_db_entry_t *entry = NULL;
+
+ /* Scan all entries and return first match */
+ for (entry = sa_db->list; NULL != entry; entry = entry->next) {
+ if (cipher != entry->alg.cipher)
+ continue;
+ if (!match_ip_range(entry->src_ip, src))
+ continue;
+ if (!match_ip_range(entry->dst_ip, dst))
+ continue;
+ break;
+ }
+ return entry;
+}
+
+void dump_tun_db(void)
+{
+ tun_db_entry_t *entry;
+
+ printf("\n"
+ "Tunnel table\n"
+ "--------------------------\n");
+
+ for (entry = tun_db->list; NULL != entry; entry = entry->next) {
+ char src_ip_str[MAX_STRING];
+ char dst_ip_str[MAX_STRING];
+ char tun_src_ip_str[MAX_STRING];
+ char tun_dst_ip_str[MAX_STRING];
+
+ printf(" %s:%s %s:%s ",
+ ipv4_addr_str(src_ip_str, entry->src_ip),
+ ipv4_addr_str(dst_ip_str, entry->dst_ip),
+ ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip),
+ ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip)
+ );
+
+ printf("\n");
+ }
+}
diff --git a/example/ipsec_crypto/odp_ipsec_sa_db.h b/example/ipsec_crypto/odp_ipsec_sa_db.h
new file mode 100644
index 000000000..83f103af3
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_sa_db.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_SA_DB_H_
+#define ODP_IPSEC_SA_DB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ipsec_misc.h>
+
+typedef enum sa_mode_s {
+ IPSEC_SA_MODE_TRANSPORT,
+ IPSEC_SA_MODE_TUNNEL
+} sa_mode_t;
+
+typedef enum sa_flags_s {
+ BIT_MODE_CIPHER = 1,
+ BIT_MODE_AUTH = 2,
+} sa_flags_t;
+
+/**
+ * Security Association (SA) data base entry
+ */
+typedef struct sa_db_entry_s {
+ struct sa_db_entry_s *next; /**< Next entry on list */
+ uint32_t src_ip; /**< Source IPv4 address */
+ uint32_t dst_ip; /**< Destination IPv4 address */
+ uint32_t spi; /**< Security Parameter Index */
+ ipsec_alg_t alg; /**< Cipher/auth algorithm */
+ ipsec_key_t key; /**< Cipher/auth key */
+ uint32_t block_len; /**< Cipher block length */
+ uint32_t iv_len; /**< Initialization Vector length */
+ uint32_t icv_len; /**< Integrity Check Value length */
+ sa_mode_t mode; /**< SA mode - transport/tun */
+ int flags; /**< Miscellaneous flags */
+} sa_db_entry_t;
+
+/**
+ * Security Association (SA) data base global structure
+ */
+typedef struct sa_db_s {
+ uint32_t index; /**< Index of next available entry */
+ sa_db_entry_t *list; /**< List of active entries */
+ sa_db_entry_t array[MAX_DB]; /**< Entry storage */
+} sa_db_t;
+
+/** Initialize SA database global control structure */
+void init_sa_db(void);
+
+/**
+ * Create an SA DB entry
+ *
+ * String is of the format "SrcIP,DstIP,Alg,SPI,Key"
+ *
+ * @param input Pointer to string describing SA
+ * @param cipher TRUE if cipher else FALSE for auth
+ *
+ * @return 0 if successful else -1
+ */
+int create_sa_db_entry(char *input, odp_bool_t cipher);
+/**
+ * Display the SA DB
+ */
+void dump_sa_db(void);
+
+/**
+ * Find a matching SA DB entry
+ *
+ * @param src Pointer to source subnet/range
+ * @param dst Pointer to destination subnet/range
+ * @param cipher TRUE if cipher else FALSE for auth
+ *
+ * @return pointer to SA DB entry else NULL
+ */
+sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
+ ip_addr_range_t *dst,
+ odp_bool_t cipher);
+
+/**
+ * Tunnel entry
+ */
+typedef struct tun_db_entry_s {
+ struct tun_db_entry_s *next;
+ uint32_t src_ip; /**< Inner Source IPv4 address */
+ uint32_t dst_ip; /**< Inner Destination IPv4 address */
+ uint32_t tun_src_ip; /**< Tunnel Source IPv4 address */
+ uint32_t tun_dst_ip; /**< Tunnel Source IPv4 address */
+} tun_db_entry_t;
+
+/**
+ * Tunnel database
+ */
+typedef struct tun_db_s {
+ uint32_t index; /**< Index of next available entry */
+ tun_db_entry_t *list; /**< List of active entries */
+ tun_db_entry_t array[MAX_DB]; /**< Entry storage */
+} tun_db_t;
+
+/** Initialize tun database global control structure */
+void init_tun_db(void);
+
+/**
+ * Create an tunnel DB entry
+ *
+ * String is of the format "SrcIP,DstIP,TunSrcIp,TunDstIp"
+ *
+ * @param input Pointer to string describing tun
+ *
+ * @return 0 if successful else -1
+ */
+int create_tun_db_entry(char *input);
+
+/**
+ * Display the tun DB
+ */
+void dump_tun_db(void);
+
+/**
+ * Find a matching tun DB entry
+ *
+ * @param ip_src Inner source IP address
+ * @param ip_dst Inner destination IP address
+ *
+ * @return pointer to tun DB entry else NULL
+ */
+tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
+ uint32_t ip_dst);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_crypto/odp_ipsec_sp_db.c b/example/ipsec_crypto/odp_ipsec_sp_db.c
new file mode 100644
index 000000000..956fb212d
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_sp_db.c
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_sp_db.h>
+
+/** Global pointer to sp db */
+sp_db_t *sp_db;
+
+void init_sp_db(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("shm_sp_db",
+ sizeof(sp_db_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ sp_db = odp_shm_addr(shm);
+
+ if (sp_db == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(sp_db, 0, sizeof(*sp_db));
+}
+
+int create_sp_db_entry(char *input, odp_bool_t both_supported)
+{
+ int pos = 0;
+ char *local;
+ char *str;
+ char *save;
+ char *token;
+ sp_db_entry_t *entry = &sp_db->array[sp_db->index];
+
+ /* Verify we have a good entry */
+ if (MAX_DB <= sp_db->index)
+ return -1;
+
+ /* Make a local copy */
+ local = malloc(strlen(input) + 1);
+ if (NULL == local)
+ return -1;
+ strcpy(local, input);
+
+ /* Setup for using "strtok_r" to search input string */
+ str = local;
+ save = NULL;
+
+ /* Parse tokens separated by ',' */
+ while (NULL != (token = strtok_r(str, ",", &save))) {
+ str = NULL; /* reset str for subsequent strtok_r calls */
+
+ /* Parse token based on its position */
+ switch (pos) {
+ case 0:
+ parse_ipv4_string(token,
+ &entry->src_subnet.addr,
+ &entry->src_subnet.mask);
+ break;
+ case 1:
+ parse_ipv4_string(token,
+ &entry->dst_subnet.addr,
+ &entry->dst_subnet.mask);
+ break;
+ case 2:
+ if (0 == strcmp(token, "in"))
+ entry->input = TRUE;
+ else
+ entry->input = FALSE;
+ break;
+ case 3:
+ if (0 == strcmp(token, "esp")) {
+ entry->esp = TRUE;
+ } else if (0 == strcmp(token, "ah")) {
+ entry->ah = TRUE;
+ } else if (0 == strcmp(token, "both")) {
+ entry->esp = TRUE;
+ entry->ah = TRUE;
+ }
+ break;
+ default:
+ printf("ERROR: extra token \"%s\" at position %d\n",
+ token, pos);
+ break;
+ }
+
+ /* Advance to next position */
+ pos++;
+ }
+
+ /* Check if enabling both AH and ESP protocols is supported */
+ if (!both_supported && (entry->ah && entry->esp)) {
+ printf("ERROR: enabling both AH and ESP is not supported\n");
+ free(local);
+ return -1;
+ }
+
+ /* Verify we parsed exactly the number of tokens we expected */
+ if (4 != pos) {
+ printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
+ input,
+ pos);
+ free(local);
+ return -1;
+ }
+
+ /* Add route to the list */
+ sp_db->index++;
+ entry->next = sp_db->list;
+ sp_db->list = entry;
+
+ free(local);
+ return 0;
+}
+
+void dump_sp_db_entry(sp_db_entry_t *entry)
+{
+ char src_subnet_str[MAX_STRING];
+ char dst_subnet_str[MAX_STRING];
+
+ printf(" %s %s %s %s:%s\n",
+ ipv4_subnet_str(src_subnet_str, &entry->src_subnet),
+ ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet),
+ entry->input ? "in" : "out",
+ entry->esp ? "esp" : "none",
+ entry->ah ? "ah" : "none");
+}
+
+void dump_sp_db(void)
+{
+ sp_db_entry_t *entry;
+
+ printf("\n"
+ "Security policy table\n"
+ "---------------------\n");
+
+ for (entry = sp_db->list; NULL != entry; entry = entry->next)
+ dump_sp_db_entry(entry);
+}
diff --git a/example/ipsec_crypto/odp_ipsec_sp_db.h b/example/ipsec_crypto/odp_ipsec_sp_db.h
new file mode 100644
index 000000000..ac6c0c896
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_sp_db.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_SP_DB_H_
+#define ODP_IPSEC_SP_DB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ipsec_misc.h>
+
+/**
+ * Security Policy (SP) data base entry
+ */
+typedef struct sp_db_entry_s {
+ struct sp_db_entry_s *next; /**< Next entry on list */
+ ip_addr_range_t src_subnet; /**< Source IPv4 subnet/range */
+ ip_addr_range_t dst_subnet; /**< Destination IPv4 subnet/range */
+ odp_bool_t input; /**< Direction when applied */
+ odp_bool_t esp; /**< Enable cipher (ESP) */
+ odp_bool_t ah; /**< Enable authentication (AH) */
+} sp_db_entry_t;
+
+/**
+ * Security Policy (SP) data base global structure
+ */
+typedef struct sp_db_s {
+ uint32_t index; /**< Index of next available entry */
+ sp_db_entry_t *list; /**< List of active entries */
+ sp_db_entry_t array[MAX_DB]; /**< Entry storage */
+} sp_db_t;
+
+/** Global pointer to sp db */
+extern sp_db_t *sp_db;
+
+/** Initialize SP database global control structure */
+void init_sp_db(void);
+
+/**
+ * Create an SP DB entry
+ *
+ * String is of the format "SrcSubNet,DstSubNet,(in|out),(ah|esp|[both])"
+ *
+ * @param input Pointer to a string describing SP
+ * @param both_supported Enabling both AH and ESP is supported
+ *
+ * @return 0 if successful else -1
+ */
+int create_sp_db_entry(char *input, odp_bool_t both_supported);
+
+/**
+ * Display one SP DB entry
+ *
+ * @param entry Pointer to entry to display
+ */
+void dump_sp_db_entry(sp_db_entry_t *entry);
+
+/**
+ * Display the SP DB
+ */
+void dump_sp_db(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_crypto/odp_ipsec_stream.c b/example/ipsec_crypto/odp_ipsec_stream.c
new file mode 100644
index 000000000..505ee900b
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_stream.c
@@ -0,0 +1,747 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/des.h>
+#include <openssl/rand.h>
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+
+#include <odp_api.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_stream.h>
+
+#define STREAM_MAGIC 0xBABE01234567CAFE
+
+#define LOOP_DEQ_COUNT 32 /**< packets to dequeue at once */
+
+/* Ignore warnings about APIs deprecated in OpenSSL 3.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+/**
+ * Stream packet header
+ */
+typedef struct ODP_PACKED stream_pkt_hdr_s {
+ odp_u64be_t magic; /**< Stream magic value for verification */
+ uint8_t data[]; /**< Incrementing data stream */
+} stream_pkt_hdr_t;
+
+static const char *shm_name = "stream_db";
+stream_db_t *stream_db;
+
+void init_stream_db(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve(shm_name,
+ sizeof(stream_db_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ stream_db = odp_shm_addr(shm);
+
+ if (stream_db == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(stream_db, 0, sizeof(*stream_db));
+}
+
+void deinit_stream_db(void)
+{
+ stream_db_entry_t *stream = NULL;
+
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ free(stream->input.intf);
+ free(stream->output.intf);
+ }
+
+ odp_shm_t shm = odp_shm_lookup(shm_name);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem not found.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+int create_stream_db_entry(char *input)
+{
+ int pos = 0;
+ char *local;
+ char *str;
+ char *save;
+ char *token;
+ stream_db_entry_t *entry = &stream_db->array[stream_db->index];
+
+ /* Verify we have a good entry */
+ if (MAX_DB <= stream_db->index)
+ return -1;
+
+ /* Make a local copy */
+ local = malloc(strlen(input) + 1);
+ if (NULL == local)
+ return -1;
+ strcpy(local, input);
+
+ /* Setup for using "strtok_r" to search input string */
+ str = local;
+ save = NULL;
+
+ /* Parse tokens separated by ',' */
+ while (NULL != (token = strtok_r(str, ",", &save))) {
+ str = NULL; /* reset str for subsequent strtok_r calls */
+
+ /* Parse token based on its position */
+ switch (pos) {
+ case 0:
+ parse_ipv4_string(token, &entry->src_ip, NULL);
+ break;
+ case 1:
+ parse_ipv4_string(token, &entry->dst_ip, NULL);
+ break;
+ case 2:
+ entry->input.intf = strdup(token);
+ break;
+ case 3:
+ entry->output.intf = strdup(token);
+ break;
+ case 4:
+ entry->count = atoi(token);
+ break;
+ case 5:
+ entry->length = atoi(token);
+ if (entry->length < sizeof(stream_pkt_hdr_t))
+ entry->length = 0;
+ else
+ entry->length -= sizeof(stream_pkt_hdr_t);
+ break;
+ default:
+ printf("ERROR: extra token \"%s\" at position %d\n",
+ token, pos);
+ break;
+ }
+
+ /* Advance to next position */
+ pos++;
+ }
+
+ /* Verify we parsed exactly the number of tokens we expected */
+ if (6 != pos) {
+ printf("ERROR: \"%s\" contains %d tokens, expected 6\n",
+ input,
+ pos);
+ free(local);
+ return -1;
+ }
+
+ /* Add stream to the list */
+ entry->id = stream_db->index++;
+ entry->next = stream_db->list;
+ stream_db->list = entry;
+
+ free(local);
+ return 0;
+}
+
+static const EVP_MD *get_evp_md(odp_auth_alg_t auth)
+{
+ const EVP_MD *evp_md;
+
+ switch (auth) {
+ case ODP_AUTH_ALG_MD5_HMAC:
+ evp_md = EVP_md5();
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ evp_md = EVP_sha1();
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ evp_md = EVP_sha256();
+ break;
+ default:
+ evp_md = NULL;
+ }
+
+ return evp_md;
+}
+
+void resolve_stream_db(void)
+{
+ stream_db_entry_t *stream = NULL;
+
+ /* For each stream look for input and output IPsec entries */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ ipsec_cache_entry_t *entry;
+
+ /* Lookup input entry */
+ entry = find_ipsec_cache_entry_in(stream->src_ip,
+ stream->dst_ip,
+ NULL,
+ NULL);
+ stream->input.entry = entry;
+
+ stream->input.pktio = odp_pktio_lookup(stream->input.intf);
+
+ if (entry)
+ stream->evp_md = get_evp_md(entry->ah.alg);
+
+ /* Lookup output entry */
+ entry = find_ipsec_cache_entry_out(stream->src_ip,
+ stream->dst_ip,
+ 0);
+ stream->output.entry = entry;
+
+ stream->output.pktio = odp_pktio_lookup(stream->output.intf);
+
+ if (stream->evp_md == NULL && entry)
+ stream->evp_md = get_evp_md(entry->ah.alg);
+ }
+}
+
+odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
+ uint8_t *dmac,
+ odp_pool_t pkt_pool,
+ uint32_t max_len)
+{
+ ipsec_cache_entry_t *entry = NULL;
+ odp_packet_t pkt;
+ uint8_t *base;
+ uint8_t *data;
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ odph_ipv4hdr_t *inner_ip = NULL;
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+ odph_icmphdr_t *icmp;
+ stream_pkt_hdr_t *test;
+ unsigned i;
+
+ if (stream->input.entry)
+ entry = stream->input.entry;
+ else if (stream->output.entry)
+ entry = stream->output.entry;
+
+ /* Make sure there is enough space for protocol overhead */
+ if ((stream->length + 200) > max_len) {
+ ODPH_ERR("Error: too large test packet\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ /* Get packet */
+ pkt = odp_packet_alloc(pkt_pool, max_len);
+ if (ODP_PACKET_INVALID == pkt) {
+ ODPH_ERR("Error: packet alloc failed\n");
+ return ODP_PACKET_INVALID;
+ }
+ base = odp_packet_data(pkt);
+ data = odp_packet_data(pkt);
+
+ /* Ethernet */
+ odp_packet_has_eth_set(pkt, 1);
+ eth = (odph_ethhdr_t *)data;
+ data += sizeof(*eth);
+
+ memset((char *)eth->src.addr, (0x80 | stream->id), ODPH_ETHADDR_LEN);
+ memcpy((char *)eth->dst.addr, dmac, ODPH_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ /* IPv4 */
+ odp_packet_has_ipv4_set(pkt, 1);
+ ip = (odph_ipv4hdr_t *)data;
+ data += sizeof(*ip);
+
+ /* Wait until almost finished to fill in mutable fields */
+ memset((char *)ip, 0, sizeof(*ip));
+ ip->ver_ihl = 0x45;
+ ip->id = odp_cpu_to_be_16(stream->id);
+ /* Outer IP header in tunnel mode */
+ if (entry && entry->mode == IPSEC_SA_MODE_TUNNEL &&
+ (entry == stream->input.entry)) {
+ ip->proto = ODPH_IPV4;
+ ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip);
+ ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip);
+ } else {
+ ip->proto = ODPH_IPPROTO_ICMPV4;
+ ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
+ ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
+ }
+
+ /* AH (if specified) */
+ if (entry && (entry == stream->input.entry) &&
+ (ODP_AUTH_ALG_NULL != entry->ah.alg)) {
+ if (entry->ah.alg != ODP_AUTH_ALG_MD5_HMAC &&
+ entry->ah.alg != ODP_AUTH_ALG_SHA1_HMAC &&
+ entry->ah.alg != ODP_AUTH_ALG_SHA256_HMAC)
+ abort();
+
+ ah = (odph_ahhdr_t *)data;
+ data += sizeof(*ah);
+ data += entry->ah.icv_len;
+
+ memset((char *)ah, 0, sizeof(*ah) + entry->ah.icv_len);
+ ah->ah_len = 1 + (entry->ah.icv_len / 4);
+ ah->spi = odp_cpu_to_be_32(entry->ah.spi);
+ ah->seq_no = odp_cpu_to_be_32(stream->input.ah_seq++);
+ }
+
+ /* ESP (if specified) */
+ if (entry && (entry == stream->input.entry) &&
+ (ODP_CIPHER_ALG_NULL != entry->esp.alg)) {
+ if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+ abort();
+
+ esp = (odph_esphdr_t *)data;
+ data += sizeof(*esp);
+ data += entry->esp.iv_len;
+
+ esp->spi = odp_cpu_to_be_32(entry->esp.spi);
+ esp->seq_no = odp_cpu_to_be_32(stream->input.esp_seq++);
+ RAND_bytes(esp->iv, 8);
+ }
+
+ /* Inner IP header in tunnel mode */
+ if (entry && (entry == stream->input.entry) &&
+ (entry->mode == IPSEC_SA_MODE_TUNNEL)) {
+ inner_ip = (odph_ipv4hdr_t *)data;
+ memset((char *)inner_ip, 0, sizeof(*inner_ip));
+ inner_ip->ver_ihl = 0x45;
+ inner_ip->tot_len = odp_cpu_to_be_16(sizeof(odph_ipv4hdr_t) +
+ sizeof(odph_icmphdr_t) +
+ sizeof(stream_pkt_hdr_t) +
+ stream->length);
+ inner_ip->proto = ODPH_IPPROTO_ICMPV4;
+ inner_ip->id = odp_cpu_to_be_16(stream->id);
+ inner_ip->ttl = 64;
+ inner_ip->tos = 0;
+ inner_ip->frag_offset = 0;
+ inner_ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
+ inner_ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
+ inner_ip->chksum = ~odp_chksum_ones_comp16(inner_ip,
+ sizeof(*inner_ip));
+ data += sizeof(*inner_ip);
+ }
+
+ /* ICMP header so we can see it on wireshark */
+ icmp = (odph_icmphdr_t *)data;
+ data += sizeof(*icmp);
+ icmp->type = ODPH_ICMP_ECHO;
+ icmp->code = 0;
+ icmp->un.echo.id = odp_cpu_to_be_16(0x1234);
+ icmp->un.echo.sequence = odp_cpu_to_be_16(stream->created);
+
+ /* Packet payload of incrementing bytes */
+ test = (stream_pkt_hdr_t *)data;
+ data += sizeof(*test);
+ test->magic = odp_cpu_to_be_64(STREAM_MAGIC);
+ for (i = 0; i < stream->length; i++)
+ *data++ = (uint8_t)i;
+
+ /* Close ICMP */
+ icmp->chksum = 0;
+ icmp->chksum = ~odp_chksum_ones_comp16(icmp, data - (uint8_t *)icmp);
+
+ /* Close ESP if specified */
+ if (esp) {
+ int payload_len = data - (uint8_t *)icmp;
+ uint8_t *encrypt_start = (uint8_t *)icmp;
+
+ if (entry->mode == IPSEC_SA_MODE_TUNNEL) {
+ payload_len = data - (uint8_t *)inner_ip;
+ encrypt_start = (uint8_t *)inner_ip;
+ }
+
+ int encrypt_len;
+ odph_esptrl_t *esp_t;
+ DES_key_schedule ks1, ks2, ks3;
+ uint8_t iv[8];
+
+ memcpy(iv, esp->iv, sizeof(iv));
+
+ encrypt_len = ESP_ENCODE_LEN(payload_len + sizeof(*esp_t),
+ entry->esp.block_len);
+ for (int n = 0; n < encrypt_len - payload_len; n++)
+ *data++ = n + 1;
+
+ esp_t = (odph_esptrl_t *)(data) - 1;
+ esp_t->pad_len = encrypt_len - payload_len - sizeof(*esp_t);
+ esp_t->next_header = ip->proto;
+ ip->proto = ODPH_IPPROTO_ESP;
+
+ DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
+ DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
+ DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
+
+ DES_ede3_cbc_encrypt(encrypt_start,
+ encrypt_start,
+ encrypt_len,
+ &ks1,
+ &ks2,
+ &ks3,
+ (DES_cblock *)iv,
+ 1);
+ }
+
+ /* Since ESP can pad we can now fix IP length */
+ ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip);
+
+ /* Close AH if specified */
+ if (ah) {
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ int auth_len = data - (uint8_t *)ip;
+
+ ah->next_header = ip->proto;
+ ip->proto = ODPH_IPPROTO_AH;
+
+ HMAC(stream->evp_md,
+ entry->ah.key.data,
+ entry->ah.key.length,
+ (uint8_t *)ip,
+ auth_len,
+ hash,
+ NULL);
+
+ memcpy(ah->icv, hash, entry->ah.icv_len);
+ }
+
+ /* Correct set packet length offsets */
+ odp_packet_pull_tail(pkt, max_len - (data - base));
+ odp_packet_l2_offset_set(pkt, (uint8_t *)eth - base);
+ odp_packet_l3_offset_set(pkt, (uint8_t *)ip - base);
+ odp_packet_l4_offset_set(pkt, ((uint8_t *)ip - base) + sizeof(*ip));
+
+ /* Now fill in final IP header fields */
+ ip->ttl = 64;
+ ip->tos = 0;
+ ip->frag_offset = 0;
+ ip->chksum = 0;
+ odph_ipv4_csum_update(pkt);
+ return pkt;
+}
+
+odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream,
+ odp_packet_t pkt)
+{
+ ipsec_cache_entry_t *entry = NULL;
+ uint8_t *data;
+ odph_ipv4hdr_t *ip;
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+ int hdr_len;
+ odph_icmphdr_t *icmp;
+ stream_pkt_hdr_t *test;
+ uint32_t src_ip, dst_ip;
+
+ if (stream->input.entry)
+ entry = stream->input.entry;
+ else if (stream->output.entry)
+ entry = stream->output.entry;
+
+ /* Basic IPv4 verify (add checksum verification) */
+ data = odp_packet_l3_ptr(pkt, NULL);
+ ip = (odph_ipv4hdr_t *)data;
+ data += sizeof(*ip);
+ if (0x45 != ip->ver_ihl)
+ return FALSE;
+
+ src_ip = odp_be_to_cpu_32(ip->src_addr);
+ dst_ip = odp_be_to_cpu_32(ip->dst_addr);
+ if ((stream->src_ip != src_ip) && stream->output.entry &&
+ (stream->output.entry->tun_src_ip != src_ip))
+ return FALSE;
+ if ((stream->dst_ip != dst_ip) && stream->output.entry &&
+ (stream->output.entry->tun_dst_ip != dst_ip))
+ return FALSE;
+
+ if ((stream->src_ip != src_ip) && stream->input.entry &&
+ (stream->input.entry->tun_src_ip != src_ip))
+ return FALSE;
+ if ((stream->dst_ip != dst_ip) && stream->input.entry &&
+ (stream->input.entry->tun_dst_ip != dst_ip))
+ return FALSE;
+
+ /* Find IPsec headers if any and compare against entry */
+ hdr_len = locate_ipsec_headers(ip, &ah, &esp);
+
+ /* Verify if the packet is IPsec encapsulated or is cleartext as
+ * expected
+ */
+ if (((stream->output.entry && (!ah && !esp))) ||
+ (stream->input.entry && (ah || esp)))
+ return FALSE;
+
+ /* Cleartext packet */
+ if (!ah && !esp)
+ goto clear_packet;
+ if (ah) {
+ if (!entry)
+ return FALSE;
+ if (ODP_AUTH_ALG_NULL == entry->ah.alg)
+ return FALSE;
+ if (odp_be_to_cpu_32(ah->spi) != entry->ah.spi)
+ return FALSE;
+ if (ODP_AUTH_ALG_MD5_HMAC != entry->ah.alg &&
+ ODP_AUTH_ALG_SHA1_HMAC != entry->ah.alg &&
+ ODP_AUTH_ALG_SHA256_HMAC != entry->ah.alg)
+ abort();
+ } else {
+ if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg))
+ return FALSE;
+ }
+ if (esp) {
+ if (!entry)
+ return FALSE;
+ if (ODP_CIPHER_ALG_NULL == entry->esp.alg)
+ return FALSE;
+ if (odp_be_to_cpu_32(esp->spi) != entry->esp.spi)
+ return FALSE;
+ if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+ abort();
+ hdr_len += entry->esp.iv_len;
+ } else {
+ if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg))
+ return FALSE;
+ }
+ data += hdr_len;
+
+ /* Verify authentication (if present) */
+ if (ah) {
+ uint8_t ip_tos;
+ uint8_t ip_ttl;
+ uint16_t ip_frag_offset;
+ uint8_t icv[entry->ah.icv_len];
+ uint8_t hash[EVP_MAX_MD_SIZE];
+
+ /* Save/clear mutable fields */
+ ip_tos = ip->tos;
+ ip_ttl = ip->ttl;
+ ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+ ip->tos = 0;
+ ip->ttl = 0;
+ ip->frag_offset = 0;
+ ip->chksum = 0;
+ memcpy(icv, ah->icv, entry->ah.icv_len);
+ memset(ah->icv, 0, entry->ah.icv_len);
+
+ /* Calculate HMAC and compare */
+ HMAC(stream->evp_md,
+ entry->ah.key.data,
+ entry->ah.key.length,
+ (uint8_t *)ip,
+ odp_be_to_cpu_16(ip->tot_len),
+ hash,
+ NULL);
+
+ if (0 != memcmp(icv, hash, sizeof(icv)))
+ return FALSE;
+
+ ip->proto = ah->next_header;
+ ip->tos = ip_tos;
+ ip->ttl = ip_ttl;
+ ip->frag_offset = odp_cpu_to_be_16(ip_frag_offset);
+ }
+
+ /* Decipher if present */
+ if (esp) {
+ odph_esptrl_t *esp_t;
+ DES_key_schedule ks1, ks2, ks3;
+ uint8_t iv[8];
+ int encrypt_len = ipv4_data_len(ip) - hdr_len;
+
+ memcpy(iv, esp->iv, sizeof(iv));
+
+ DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
+ DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
+ DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
+
+ DES_ede3_cbc_encrypt((uint8_t *)data,
+ (uint8_t *)data,
+ encrypt_len,
+ &ks1,
+ &ks2,
+ &ks3,
+ (DES_cblock *)iv,
+ 0);
+
+ esp_t = (odph_esptrl_t *)(data + encrypt_len) - 1;
+ ip->proto = esp_t->next_header;
+ }
+
+clear_packet:
+ /* Verify IP/ICMP packet */
+ if (entry && (entry->mode == IPSEC_SA_MODE_TUNNEL) && (ah || esp)) {
+ if (ODPH_IPV4 != ip->proto)
+ return FALSE;
+ odph_ipv4hdr_t *inner_ip = (odph_ipv4hdr_t *)data;
+
+ icmp = (odph_icmphdr_t *)(inner_ip + 1);
+ data = (uint8_t *)icmp;
+ } else {
+ if (ODPH_IPPROTO_ICMPV4 != ip->proto)
+ return FALSE;
+ icmp = (odph_icmphdr_t *)data;
+ }
+
+ /* Verify ICMP header */
+ data += sizeof(*icmp);
+ if (ODPH_ICMP_ECHO != icmp->type)
+ return FALSE;
+ if (0x1234 != odp_be_to_cpu_16(icmp->un.echo.id))
+ return FALSE;
+
+ /* Now check our packet */
+ test = (stream_pkt_hdr_t *)data;
+ if (STREAM_MAGIC != odp_be_to_cpu_64(test->magic))
+ return FALSE;
+
+ return TRUE;
+}
+
+int create_stream_db_inputs(void)
+{
+ int created = 0;
+ odp_pool_t pkt_pool;
+ odp_pool_info_t pool_info;
+ stream_db_entry_t *stream = NULL;
+ uint32_t max_len;
+
+ /* Lookup the packet pool */
+ pkt_pool = odp_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: pkt_pool not found\n");
+ return -1;
+ }
+ if (odp_pool_info(pkt_pool, &pool_info)) {
+ ODPH_ERR("Error: pool info failed\n");
+ return -1;
+ }
+
+ /* Only single segment packets are supported */
+ max_len = pool_info.params.pkt.seg_len;
+
+ /* For each stream create corresponding input packets */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ int count;
+ int ret;
+ uint8_t dmac[ODPH_ETHADDR_LEN];
+ odp_pktout_queue_t queue;
+
+ ret = odp_pktio_mac_addr(stream->input.pktio,
+ dmac, sizeof(dmac));
+ if (ret <= 0) {
+ ODPH_ERR("Error: failed during MAC address get for "
+ "%s\n", stream->input.intf);
+ continue;
+ }
+
+ ret = odp_pktout_queue(stream->input.pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Error: failed to get outqueue for %s\n",
+ stream->input.intf);
+ continue;
+ }
+
+ for (count = stream->count; count > 0; count--) {
+ odp_packet_t pkt;
+
+ pkt = create_ipv4_packet(stream, dmac, pkt_pool, max_len);
+ if (ODP_PACKET_INVALID == pkt) {
+ ODPH_ERR("Error: packet buffers exhausted\n");
+ break;
+ }
+ stream->created++;
+ if (odp_pktout_send(queue, &pkt, 1) != 1) {
+ odp_packet_free(pkt);
+ ODPH_ERR("Error: queue enqueue failed\n");
+ break;
+ }
+
+ /* Count this stream when we create first packet */
+ if (1 == stream->created)
+ created++;
+ }
+ }
+ if ((stream_db->index > 0) && created == 0) {
+ ODPH_ERR("Error: failed to create any input streams\n");
+ return -1;
+ }
+
+ return created;
+}
+
+odp_bool_t verify_stream_db_outputs(void)
+{
+ odp_bool_t done = TRUE;
+ stream_db_entry_t *stream = NULL;
+ const char *env;
+
+ env = getenv("ODP_IPSEC_STREAM_VERIFY_MDEQ");
+ /* For each stream look for output packets */
+ for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+ int idx;
+ int count;
+ int ret;
+ odp_queue_t queue;
+ odp_event_t ev_tbl[LOOP_DEQ_COUNT];
+
+ ret = odp_pktin_event_queue(stream->output.pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Error: failed to get inqueue for %s\n",
+ stream->output.intf);
+ continue;
+ }
+
+ for (;;) {
+ if (env) {
+ count = odp_queue_deq_multi(queue,
+ ev_tbl,
+ LOOP_DEQ_COUNT);
+ } else {
+ ev_tbl[0] = odp_queue_deq(queue);
+ count = (ev_tbl[0] != ODP_EVENT_INVALID) ?
+ 1 : 0;
+ }
+ if (!count)
+ break;
+ for (idx = 0; idx < count; idx++) {
+ odp_bool_t good;
+ odp_packet_t pkt;
+
+ pkt = odp_packet_from_event(ev_tbl[idx]);
+
+ good = verify_ipv4_packet(stream, pkt);
+ if (good)
+ stream->verified++;
+ odp_packet_free(pkt);
+ }
+ }
+
+ printf("Stream %d %d\n", stream->created, stream->verified);
+
+ if (stream->created != stream->verified)
+ done = FALSE;
+ }
+ return done;
+}
diff --git a/example/ipsec_crypto/odp_ipsec_stream.h b/example/ipsec_crypto/odp_ipsec_stream.h
new file mode 100644
index 000000000..c6bbb618b
--- /dev/null
+++ b/example/ipsec_crypto/odp_ipsec_stream.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_STREAM_H_
+#define ODP_IPSEC_STREAM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <openssl/evp.h>
+
+#include <odp_api.h>
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_cache.h>
+
+/**
+ * Stream database entry structure
+ */
+typedef struct stream_db_entry_s {
+ struct stream_db_entry_s *next; /**< Next entry on list */
+ int id; /**< Stream ID */
+ uint32_t src_ip; /**< Source IPv4 address */
+ uint32_t dst_ip; /**< Destination IPv4 address */
+ int count; /**< Packet count */
+ uint32_t length; /**< Packet payload length */
+ uint32_t created; /**< Number successfully created */
+ uint32_t verified; /**< Number successfully verified */
+ const EVP_MD *evp_md; /**< Digest method */
+ struct {
+ char *intf; /**< Input interface name */
+ odp_pktio_t pktio; /**< Input PktI/O interface */
+ uint32_t ah_seq; /**< AH sequence number if present */
+ uint32_t esp_seq; /**< ESP sequence number if present */
+ ipsec_cache_entry_t *entry; /**< IPsec to apply on input */
+ } input;
+ struct {
+ char *intf; /**< Output interface name */
+ odp_pktio_t pktio; /**< Output PktI/O interface */
+ ipsec_cache_entry_t *entry; /**t IPsec to verify on output */
+ } output;
+} stream_db_entry_t;
+
+/**
+ * Stream database
+ */
+typedef struct stream_db_s {
+ uint32_t index; /**< Index of next available entry */
+ stream_db_entry_t *list; /**< List of active entries */
+ stream_db_entry_t array[MAX_DB]; /**< Entry storage */
+} stream_db_t;
+
+extern stream_db_t *stream_db;
+
+/** Initialize stream database global control structure */
+void init_stream_db(void);
+
+/** Deinitialize stream database global control structure */
+void deinit_stream_db(void);
+
+/**
+ * Create an stream DB entry
+ *
+ * String is of the format "SrcIP,DstIP,InInt,OutIntf,Count,Length"
+ *
+ * @param input Pointer to string describing stream
+ *
+ * @return 0 if successful else -1
+ */
+int create_stream_db_entry(char *input);
+
+/**
+ * Resolve the stream DB against the IPsec input and output caches
+ *
+ * For each stream, look the source and destination IP address up in the
+ * input and output IPsec caches. If a hit is found, store the hit in
+ * the stream DB to be used when creating packets.
+ */
+void resolve_stream_db(void);
+
+/**
+ * Create IPv4 packet for stream
+ *
+ * Create one ICMP test packet based on the stream structure. If an input
+ * IPsec cache entry is associated with the stream, build a packet that should
+ * successfully match that entry and be correctly decoded by it.
+ *
+ * @param stream Stream DB entry
+ * @param dmac Destination MAC address to use
+ * @param pkt_pool Packet buffer pool to allocate from
+ * @param max_len Maximum packet length
+ *
+ * @return packet else ODP_PACKET_INVALID
+ */
+odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
+ uint8_t *dmac,
+ odp_pool_t pkt_pool,
+ uint32_t max_len);
+
+/**
+ * Verify an IPv4 packet received on a loop output queue
+ *
+ * @param stream Stream to verify the packet against
+ * @param pkt Packet to verify
+ *
+ * @return TRUE if packet verifies else FALSE
+ */
+odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream,
+ odp_packet_t pkt);
+
+/**
+ * Create input packets based on the stream DB
+ *
+ * Create input packets based on the configured streams and enqueue them
+ * into loop interface input queues. Once packet processing starts these
+ * packets will be removed and processed as if they had come from a normal
+ * packet interface.
+ *
+ * @return number of streams successfully processed
+ * @return <0 on failure
+ */
+int create_stream_db_inputs(void);
+
+/**
+ * Verify stream DB outputs
+ *
+ * For each stream, poll the output loop interface queue and verify
+ * any packets found on it
+ *
+ * @return TRUE if all packets on all streams verified else FALSE
+ */
+odp_bool_t verify_stream_db_outputs(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/l2fwd/README b/example/l2fwd/README
deleted file mode 100644
index 891620bd7..000000000
--- a/example/l2fwd/README
+++ /dev/null
@@ -1,8 +0,0 @@
- ODP L2FWD application
-
-Source code and Makefiles placed under test/common_plat/performance/
-directory.
-
-This L2 forwarding application can be used as example reference as well
-as performance test for different odp modes (direct, queue or scheduler
-with parallel, atomic or ordered queues).
diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
deleted file mode 120000
index eea3a34c4..000000000
--- a/example/l2fwd/odp_l2fwd.c
+++ /dev/null
@@ -1 +0,0 @@
-../../test/common_plat/performance/odp_l2fwd.c \ No newline at end of file
diff --git a/example/l2fwd_simple/.gitignore b/example/l2fwd_simple/.gitignore
index b59fa7e9b..8d15c2726 100644
--- a/example/l2fwd_simple/.gitignore
+++ b/example/l2fwd_simple/.gitignore
@@ -1,3 +1,4 @@
odp_l2fwd_simple
+pktio_env
*.log
*.trs
diff --git a/example/l2fwd_simple/Makefile.am b/example/l2fwd_simple/Makefile.am
index fe2065452..e23b47881 100644
--- a/example/l2fwd_simple/Makefile.am
+++ b/example/l2fwd_simple/Makefile.am
@@ -1,17 +1,34 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_l2fwd_simple$(EXEEXT)
-odp_l2fwd_simple_LDFLAGS = $(AM_LDFLAGS) -static
-odp_l2fwd_simple_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_l2fwd_simple
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
-
-dist_odp_l2fwd_simple_SOURCES = odp_l2fwd_simple.c
+odp_l2fwd_simple_SOURCES = odp_l2fwd_simple.c
if test_example
-if HAVE_PCAP
+if ODP_PKTIO_PCAP
TESTS = l2fwd_simple_run.sh
endif
endif
EXTRA_DIST = l2fwd_simple_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/l2fwd_simple/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/l2fwd_simple/l2fwd_simple_run.sh b/example/l2fwd_simple/l2fwd_simple_run.sh
index 130a3a848..741240e27 100755
--- a/example/l2fwd_simple/l2fwd_simple_run.sh
+++ b/example/l2fwd_simple/l2fwd_simple_run.sh
@@ -1,19 +1,20 @@
#!/bin/bash
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
-PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
-echo "using PCAP_IN = ${PCAP_IN}"
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
-./odp_l2fwd_simple pcap:in=${PCAP_IN} pcap:out=pcapout.pcap 02:00:00:00:00:01 02:00:00:00:00:02 &
+setup_interfaces
-sleep 1
-kill -s SIGINT $!
-wait $!
+./odp_l2fwd_simple${EXEEXT} $IF0 $IF1 02:00:00:00:00:01 02:00:00:00:00:02 -t 2
STATUS=$?
if [ "$STATUS" -ne 0 ]; then
@@ -21,11 +22,8 @@ if [ "$STATUS" -ne 0 ]; then
exit 1
fi
-if [ `stat -c %s pcapout.pcap` -ne `stat -c %s ${PCAP_IN}` ]; then
- echo "File sizes disagree"
- exit 1
-fi
+validate_result
-rm -f pcapout.pcap
+cleanup_interfaces
exit 0
diff --git a/example/l2fwd_simple/odp_l2fwd_simple.c b/example/l2fwd_simple/odp_l2fwd_simple.c
index e63814555..5d9aaec87 100644
--- a/example/l2fwd_simple/odp_l2fwd_simple.c
+++ b/example/l2fwd_simple/odp_l2fwd_simple.c
@@ -1,7 +1,13 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @example odp_l2fwd_simple.c
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * Minimal L2 forwarding example application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdlib.h>
@@ -17,20 +23,24 @@
#define MAX_PKT_BURST 32
#define MAX_WORKERS 1
-static int exit_thr;
-static int g_ret;
-
-struct {
+typedef struct {
odp_pktio_t if0, if1;
odp_pktin_queue_t if0in, if1in;
odp_pktout_queue_t if0out, if1out;
odph_ethaddr_t src, dst;
-} global;
+ odp_shm_t shm;
+ odp_atomic_u32_t exit_thr;
+ int wait_sec;
+} global_data_t;
+
+static global_data_t *global;
static void sig_handler(int signo ODP_UNUSED)
{
printf("sig_handler!\n");
- exit_thr = 1;
+ if (global == NULL)
+ return;
+ odp_atomic_store_u32(&global->exit_thr, 1);
}
static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
@@ -52,7 +62,7 @@ static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
}
odp_pktio_config_init(&config);
- config.parser.layer = ODP_PKTIO_PARSER_LAYER_L2;
+ config.parser.layer = ODP_PROTO_LAYER_L2;
odp_pktio_config(pktio, &config);
odp_pktin_queue_param_init(&in_queue_param);
@@ -87,26 +97,31 @@ static int run_worker(void *arg ODP_UNUSED)
{
odp_packet_t pkt_tbl[MAX_PKT_BURST];
int pkts, sent, tx_drops, i;
- int total_pkts = 0;
+ uint64_t wait_time = odp_pktin_wait_time(ODP_TIME_SEC_IN_NS);
- if (odp_pktio_start(global.if0)) {
+ if (odp_pktio_start(global->if0)) {
printf("unable to start input interface\n");
exit(1);
}
printf("started input interface\n");
- if (odp_pktio_start(global.if1)) {
+ if (odp_pktio_start(global->if1)) {
printf("unable to start output interface\n");
exit(1);
}
printf("started output interface\n");
printf("started all\n");
- while (!exit_thr) {
- pkts = odp_pktin_recv_tmo(global.if0in, pkt_tbl, MAX_PKT_BURST,
- ODP_PKTIN_NO_WAIT);
+ while (!odp_atomic_load_u32(&global->exit_thr)) {
+ pkts = odp_pktin_recv_tmo(global->if0in, pkt_tbl, MAX_PKT_BURST,
+ wait_time);
- if (odp_unlikely(pkts <= 0))
+ if (odp_unlikely(pkts <= 0)) {
+ if (global->wait_sec > 0)
+ if (!(--global->wait_sec))
+ break;
continue;
+ }
+
for (i = 0; i < pkts; i++) {
odp_packet_t pkt = pkt_tbl[i];
odph_ethhdr_t *eth;
@@ -116,65 +131,46 @@ static int run_worker(void *arg ODP_UNUSED)
return 0;
}
eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- eth->src = global.src;
- eth->dst = global.dst;
+ eth->src = global->src;
+ eth->dst = global->dst;
}
- sent = odp_pktout_send(global.if1out, pkt_tbl, pkts);
+ sent = odp_pktout_send(global->if1out, pkt_tbl, pkts);
if (sent < 0)
sent = 0;
- total_pkts += sent;
tx_drops = pkts - sent;
if (odp_unlikely(tx_drops))
odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
}
- if (total_pkts < 10)
- g_ret = -1;
-
return 0;
}
int main(int argc, char **argv)
{
+ odph_helper_options_t helper_options;
odp_pool_t pool;
odp_pool_param_t params;
odp_cpumask_t cpumask;
- odph_odpthread_t thd[MAX_WORKERS];
+ odph_thread_t thd[MAX_WORKERS];
odp_instance_t instance;
- odph_odpthread_params_t thr_params;
- int opt;
- int long_index;
-
- static const struct option longopts[] = { {NULL, 0, NULL, 0} };
- static const char *shortopts = "";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- /*
- * parse own options: currentely none, but this will move optind
- * to the first non-option argument. (in case there where helprt args)
- */
- opterr = 0; /* do not issue errors on helper options */
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
- if (-1 == opt)
- break; /* No more options */
- }
-
- if (argc != optind + 4 ||
- odph_eth_addr_parse(&global.dst, argv[optind + 2]) != 0 ||
- odph_eth_addr_parse(&global.src, argv[optind + 3]) != 0) {
- printf("Usage: odp_l2fwd_simple eth0 eth1 01:02:03:04:05:06"
- " 07:08:09:0a:0b:0c\n");
- printf("Where eth0 and eth1 are the used interfaces"
- " (must have 2 of them)\n");
- printf("And the hexadecimal numbers are destination MAC address"
- " and source MAC address\n");
- exit(1);
+ odp_init_t init_param;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odph_ethaddr_t correct_src;
+ uint32_t mtu1, mtu2;
+ odp_shm_t shm;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ printf("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
}
- if (odp_init_global(&instance, NULL, NULL)) {
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (odp_init_global(&instance, &init_param, NULL)) {
printf("Error: ODP global init failed.\n");
exit(1);
}
@@ -184,6 +180,41 @@ int main(int argc, char **argv)
exit(1);
}
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("_appl_global_data", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ printf("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ printf("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(global_data_t));
+ odp_atomic_init_u32(&global->exit_thr, 0);
+ global->shm = shm;
+
+ if (argc > 7 ||
+ odph_eth_addr_parse(&global->dst, argv[3]) != 0 ||
+ odph_eth_addr_parse(&global->src, argv[4]) != 0) {
+ printf("Usage: odp_l2fwd_simple eth0 eth1 01:02:03:04:05:06"
+ " 07:08:09:0a:0b:0c [-t sec]\n");
+ printf("Where eth0 and eth1 are the used interfaces"
+ " (must have 2 of them)\n");
+ printf("And the hexadecimal numbers are destination MAC address"
+ " and source MAC address\n");
+ exit(1);
+ }
+ if (argc == 7 && !strncmp(argv[5], "-t", 2))
+ global->wait_sec = atoi(argv[6]);
+
+ if (global->wait_sec)
+ printf("running test for %d sec\n", global->wait_sec);
+
/* Create packet pool */
odp_pool_param_init(&params);
params.pkt.seg_len = POOL_SEG_LEN;
@@ -198,29 +229,74 @@ int main(int argc, char **argv)
exit(1);
}
- global.if0 = create_pktio(argv[optind], pool, &global.if0in,
- &global.if0out);
- global.if1 = create_pktio(argv[optind + 1], pool, &global.if1in,
- &global.if1out);
+ global->if0 = create_pktio(argv[1], pool, &global->if0in,
+ &global->if0out);
+ global->if1 = create_pktio(argv[2], pool, &global->if1in,
+ &global->if1out);
+
+ /* Do some operations to increase code coverage in tests */
+ if (odp_pktio_mac_addr(global->if0, &correct_src, sizeof(correct_src))
+ != sizeof(correct_src))
+ printf("Warning: can't get MAC address\n");
+ else if (memcmp(&correct_src, &global->src, sizeof(correct_src)) != 0)
+ printf("Warning: src MAC invalid\n");
+
+ odp_pktio_promisc_mode_set(global->if0, true);
+ odp_pktio_promisc_mode_set(global->if1, true);
+ (void)odp_pktio_promisc_mode(global->if0);
+ (void)odp_pktio_promisc_mode(global->if1);
+
+ mtu1 = odp_pktin_maxlen(global->if0);
+ mtu2 = odp_pktout_maxlen(global->if1);
+ if (mtu1 && mtu2 && mtu1 > mtu2)
+ printf("Warning: input MTU bigger than output MTU\n");
odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_worker;
- thr_params.arg = NULL;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
+ odph_thread_common_param_init(&thr_common);
+ odph_thread_param_init(&thr_param);
+
+ thr_param.start = run_worker;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
signal(SIGINT, sig_handler);
- odph_odpthreads_create(thd, &cpumask, &thr_params);
- odph_odpthreads_join(thd);
+ if (odph_thread_create(thd, &thr_common, &thr_param, MAX_WORKERS) !=
+ MAX_WORKERS) {
+ printf("Error: failed to create threads\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odph_thread_join(thd, MAX_WORKERS) != MAX_WORKERS) {
+ printf("Error: failed to join threads\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktio_stop(global->if0) || odp_pktio_close(global->if0)) {
+ printf("Error: failed to close interface %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (odp_pktio_stop(global->if1) || odp_pktio_close(global->if1)) {
+ printf("Error: failed to close interface %s\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
if (odp_pool_destroy(pool)) {
printf("Error: pool destroy\n");
exit(EXIT_FAILURE);
}
+ global = NULL;
+ odp_mb_full();
+ if (odp_shm_free(shm)) {
+ printf("Error: shm free global data\n");
+ exit(EXIT_FAILURE);
+ }
+
if (odp_term_local()) {
printf("Error: term local\n");
exit(EXIT_FAILURE);
@@ -231,5 +307,5 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
- return g_ret;
+ return 0;
}
diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore
index 3411830e4..8b9e2355f 100644
--- a/example/l3fwd/.gitignore
+++ b/example/l3fwd/.gitignore
@@ -1,3 +1,4 @@
odp_l3fwd
+pktio_env
*.log
*.trs
diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am
index 422a3bf3a..80ad91911 100644
--- a/example/l3fwd/Makefile.am
+++ b/example/l3fwd/Makefile.am
@@ -1,20 +1,39 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_l3fwd$(EXEEXT)
-odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static
-odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -I${top_srcdir}/test
+bin_PROGRAMS = odp_l3fwd
-noinst_HEADERS = \
- $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \
- $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \
- $(top_srcdir)/example/example_debug.h
-
-dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c
+odp_l3fwd_SOURCES = \
+ odp_l3fwd.c \
+ odp_l3fwd_db.c \
+ odp_l3fwd_lpm.c \
+ odp_l3fwd_db.h \
+ odp_l3fwd_lpm.h
if test_example
-if HAVE_PCAP
+if ODP_PKTIO_PCAP
TESTS = odp_l3fwd_run.sh
endif
endif
-
EXTRA_DIST = odp_l3fwd_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/l3fwd/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c
index f579d36d3..c74c7d4d6 100644
--- a/example/l3fwd/odp_l3fwd.c
+++ b/example/l3fwd/odp_l3fwd.c
@@ -1,7 +1,13 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @example odp_l3fwd.c
+ *
+ * L3 forwarding example application
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdlib.h>
@@ -11,8 +17,6 @@
#include <unistd.h>
#include <inttypes.h>
-#include <test_debug.h>
-
#include <odp_api.h>
#include <odp/helper/odph_api.h>
@@ -23,14 +27,14 @@
#define POOL_SEG_LEN 1856
#define MAX_PKT_BURST 32
-#define MAX_NB_WORKER 32
+#define MAX_NB_WORKER (ODP_THREAD_COUNT_MAX - 1)
#define MAX_NB_PKTIO 32
#define MAX_NB_QUEUE 32
#define MAX_NB_QCONFS 1024
#define MAX_NB_ROUTE 32
#define INVALID_ID (-1)
-#define PRINT_INTERVAL 10 /* interval seconds of printing stats */
+#define PRINT_INTERVAL 1 /* interval seconds of printing stats */
/** Get rid of path in filename - only for unix-type paths using '/' */
#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
@@ -68,32 +72,36 @@ struct thread_arg_s {
};
typedef struct {
- char *if_names[MAX_NB_PKTIO];
+ char *if_names_buf; /* memory buffer for all if_names */
+ char *if_names[MAX_NB_PKTIO]; /* pointers to name strings stored in if_names_buf */
int if_count;
char *route_str[MAX_NB_ROUTE];
- int worker_count;
+ unsigned int worker_count;
struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];
- int qconf_count;
+ unsigned int qconf_count;
uint32_t duration; /* seconds to run */
uint8_t hash_mode; /* 1:hash, 0:lpm */
uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from cmdline */
int error_check; /* Check packets for errors */
} app_args_t;
-struct {
+typedef struct {
app_args_t cmd_args;
struct l3fwd_pktio_s l3fwd_pktios[MAX_NB_PKTIO];
- odph_odpthread_t l3fwd_workers[MAX_NB_WORKER];
struct thread_arg_s worker_args[MAX_NB_WORKER];
odph_ethaddr_t eth_dest_mac[MAX_NB_PKTIO];
+ /** Global barrier to synchronize main and workers */
+ odp_barrier_t barrier;
+ /** Shm for storing global data */
+ odp_shm_t shm;
+ /** Break workers loop if set to 1 */
+ odp_atomic_u32_t exit_threads;
/* forward func, hash or lpm */
int (*fwd_func)(odp_packet_t pkt, int sif);
-} global;
+} global_data_t;
-/** Global barrier to synchronize main and workers */
-static odp_barrier_t barrier;
-static int exit_threads; /**< Break workers loop if set to 1 */
+static global_data_t *global;
static int create_pktio(const char *name, odp_pool_t pool,
struct l3fwd_pktio_s *fwd_pktio)
@@ -122,9 +130,13 @@ static int create_pktio(const char *name, odp_pool_t pool,
}
odp_pktio_config_init(&config);
- config.parser.layer = global.cmd_args.error_check ?
- ODP_PKTIO_PARSER_LAYER_ALL :
- ODP_PKTIO_PARSER_LAYER_L4;
+ config.parser.layer = global->cmd_args.error_check ?
+ ODP_PROTO_LAYER_ALL :
+ ODP_PROTO_LAYER_L4;
+
+ /* Provide hint to pktio that packet references are not used */
+ config.pktout.bit.no_packet_refs = 1;
+
odp_pktio_config(pktio, &config);
fwd_pktio->nb_rxq = (int)capa.max_input_queues;
@@ -145,7 +157,7 @@ static void setup_fwd_db(void)
int if_idx;
app_args_t *args;
- args = &global.cmd_args;
+ args = &global->cmd_args;
if (args->hash_mode)
init_fwd_hash_cache();
else
@@ -157,9 +169,9 @@ static void setup_fwd_db(void)
fib_tbl_insert(entry->subnet.addr, if_idx,
entry->subnet.depth);
if (args->dest_mac_changed[if_idx])
- global.eth_dest_mac[if_idx] = entry->dst_mac;
+ global->eth_dest_mac[if_idx] = entry->dst_mac;
else
- entry->dst_mac = global.eth_dest_mac[if_idx];
+ entry->dst_mac = global->eth_dest_mac[if_idx];
}
}
@@ -237,8 +249,8 @@ static inline int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)
if (ret)
dif = sif;
- eth->dst = global.eth_dest_mac[dif];
- eth->src = global.l3fwd_pktios[dif].mac_addr;
+ eth->dst = global->eth_dest_mac[dif];
+ eth->src = global->l3fwd_pktios[dif].mac_addr;
return dif;
}
@@ -265,7 +277,7 @@ static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
pkt = pkt_tbl[i];
err = 0;
- if (global.cmd_args.error_check)
+ if (global->cmd_args.error_check)
err = odp_packet_has_error(pkt);
if (odp_unlikely(err || !odp_packet_has_ipv4(pkt))) {
@@ -286,7 +298,7 @@ static int run_worker(void *arg)
odp_pktin_queue_t inq;
int input_ifs[thr_arg->nb_pktio];
odp_pktin_queue_t input_queues[thr_arg->nb_pktio];
- odp_pktout_queue_t output_queues[global.cmd_args.if_count];
+ odp_pktout_queue_t output_queues[global->cmd_args.if_count];
odp_packet_t pkt_tbl[MAX_PKT_BURST];
odp_packet_t *tbl;
int pkts, drop, sent;
@@ -296,16 +308,16 @@ static int run_worker(void *arg)
int num_pktio = 0;
/* Copy all required handles to local memory */
- for (i = 0; i < global.cmd_args.if_count; i++) {
+ for (i = 0; i < global->cmd_args.if_count; i++) {
int txq_idx = thr_arg->pktio[i].txq_idx;
- output_queues[i] = global.l3fwd_pktios[i].ifout[txq_idx];
+ output_queues[i] = global->l3fwd_pktios[i].ifout[txq_idx];
if_idx = thr_arg->pktio[i].if_idx;
for (j = 0; j < thr_arg->pktio[i].nb_rxq; j++) {
int rxq_idx = thr_arg->pktio[i].rxq[j];
- inq = global.l3fwd_pktios[if_idx].ifin[rxq_idx];
+ inq = global->l3fwd_pktios[if_idx].ifin[rxq_idx];
input_ifs[num_pktio] = if_idx;
input_queues[num_pktio] = inq;
num_pktio++;
@@ -313,14 +325,14 @@ static int run_worker(void *arg)
}
if (num_pktio == 0)
- LOG_ABORT("No pktio devices found\n");
+ ODPH_ABORT("No pktio devices found\n");
if_idx = input_ifs[pktio];
inq = input_queues[pktio];
- odp_barrier_wait(&barrier);
+ odp_barrier_wait(&global->barrier);
- while (!exit_threads) {
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
if (num_pktio > 1) {
if_idx = input_ifs[pktio];
inq = input_queues[pktio];
@@ -340,12 +352,12 @@ static int run_worker(void *arg)
if (odp_unlikely(pkts < 1))
continue;
- dif = global.fwd_func(pkt_tbl[0], if_idx);
+ dif = global->fwd_func(pkt_tbl[0], if_idx);
tbl = &pkt_tbl[0];
while (pkts) {
dst_port = dif;
for (i = 1; i < pkts; i++) {
- dif = global.fwd_func(tbl[i], if_idx);
+ dif = global->fwd_func(tbl[i], if_idx);
if (dif != dst_port)
break;
}
@@ -490,7 +502,7 @@ static void print_usage(char *progname)
" -d, --duration Seconds to run and print stats\n"
" optional, default as 0, run forever\n"
" -t, --thread Number of threads to do forwarding\n"
- " optional, default as availbe worker cpu count\n"
+ " 0=all available, default=1\n"
" -q, --queue Configure rx queue(s) for port\n"
" optional, format: [(port, queue, thread),...]\n"
" for example: -q '(0, 0, 1),(1,0,2)'\n"
@@ -507,7 +519,8 @@ static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)
int long_index;
char *token, *local;
size_t len, route_index = 0;
- int i, mem_failure = 0;
+ int mem_failure = 0;
+ unsigned int i;
static struct option longopts[] = {
{"interface", required_argument, NULL, 'i'}, /* return 'i' */
@@ -521,6 +534,8 @@ static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)
{NULL, 0, NULL, 0}
};
+ args->worker_count = 1; /* use one worker by default */
+
while (1) {
opt = getopt_long(argc, argv, "+s:t:d:i:r:q:e:h",
longopts, &long_index);
@@ -565,30 +580,25 @@ static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
+ args->if_names_buf = local;
- /* count the number of tokens separated by ',' */
+ /* store the if names (reset names string) */
strcpy(local, optarg);
for (token = strtok(local, ","), i = 0;
- token != NULL;
- token = strtok(NULL, ","), i++)
- ;
-
+ token != NULL; token = strtok(NULL, ","), i++) {
+ if (i >= MAX_NB_PKTIO) {
+ printf("too many ports specified, "
+ "truncated to %d", MAX_NB_PKTIO);
+ break; /* for */
+ }
+ args->if_names[i] = token;
+ }
if (i == 0) {
print_usage(argv[0]);
free(local);
exit(EXIT_FAILURE);
- } else if (i > MAX_NB_PKTIO) {
- printf("too many ports specified, "
- "truncated to %d", MAX_NB_PKTIO);
}
args->if_count = i;
-
- /* store the if names (reset names string) */
- strcpy(local, optarg);
- for (token = strtok(local, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- args->if_names[i] = token;
- }
break;
/*Configure Route in forwarding database*/
@@ -652,19 +662,7 @@ static void print_info(char *progname, app_args_t *args)
{
int i;
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_version_impl_name(),
- odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
+ odp_sys_info_print();
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
@@ -691,8 +689,8 @@ static void print_info(char *progname, app_args_t *args)
*/
static void setup_worker_qconf(app_args_t *args)
{
- int nb_worker, if_count, pktio;
- int i, j, rxq_idx;
+ int j, rxq_idx, pktio;
+ unsigned int i, nb_worker, if_count;
struct thread_arg_s *arg;
struct l3fwd_pktio_s *port;
uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];
@@ -704,10 +702,10 @@ static void setup_worker_qconf(app_args_t *args)
if (!args->qconf_count) {
if (nb_worker > if_count) {
for (i = 0; i < nb_worker; i++) {
- arg = &global.worker_args[i];
+ arg = &global->worker_args[i];
arg->thr_idx = i;
j = i % if_count;
- port = &global.l3fwd_pktios[j];
+ port = &global->l3fwd_pktios[j];
arg->pktio[0].rxq[0] =
port->rxq_idx % port->nb_rxq;
arg->pktio[0].nb_rxq = 1;
@@ -718,9 +716,9 @@ static void setup_worker_qconf(app_args_t *args)
} else {
for (i = 0; i < if_count; i++) {
j = i % nb_worker;
- arg = &global.worker_args[j];
+ arg = &global->worker_args[j];
arg->thr_idx = j;
- port = &global.l3fwd_pktios[i];
+ port = &global->l3fwd_pktios[i];
rxq_idx = arg->pktio[i].nb_rxq;
pktio = arg->nb_pktio;
arg->pktio[pktio].rxq[rxq_idx] =
@@ -740,33 +738,33 @@ static void setup_worker_qconf(app_args_t *args)
q = &args->qconf_config[i];
if (q->core_idx >= nb_worker || q->if_idx >= if_count)
- LOG_ABORT("Error queue (%d, %d, %d), max port: "
- "%d, max core: %d\n", q->if_idx, q->rxq_idx,
- q->core_idx, args->if_count - 1,
- args->worker_count - 1);
+ ODPH_ABORT("Error queue (%d, %d, %d), max port: %d, "
+ "max core: %d\n", q->if_idx, q->rxq_idx,
+ q->core_idx, args->if_count - 1,
+ args->worker_count - 1);
/* check if one queue is configured twice or more */
if (queue_mask[q->if_idx][q->rxq_idx])
- LOG_ABORT("Error queue (%d, %d, %d), reconfig queue\n",
- q->if_idx, q->rxq_idx, q->core_idx);
+ ODPH_ABORT("Error queue (%d, %d, %d), reconfig queue\n",
+ q->if_idx, q->rxq_idx, q->core_idx);
queue_mask[q->if_idx][q->rxq_idx] = 1;
- port = &global.l3fwd_pktios[q->if_idx];
+ port = &global->l3fwd_pktios[q->if_idx];
if (port->rxq_idx < q->rxq_idx)
- LOG_ABORT("Error queue (%d, %d, %d), queue should be"
- " in sequence and start from 0, queue %d\n",
- q->if_idx, q->rxq_idx, q->core_idx,
- q->rxq_idx);
+ ODPH_ABORT("Error queue (%d, %d, %d), queue should be "
+ "in sequence and start from 0, queue %d\n",
+ q->if_idx, q->rxq_idx, q->core_idx,
+ q->rxq_idx);
if (q->rxq_idx > port->nb_rxq) {
- LOG_ABORT("Error queue (%d, %d, %d), max queue %d\n",
- q->if_idx, q->rxq_idx, q->core_idx,
- port->nb_rxq - 1);
+ ODPH_ABORT("Error queue (%d, %d, %d), max queue %d\n",
+ q->if_idx, q->rxq_idx, q->core_idx,
+ port->nb_rxq - 1);
}
port->rxq_idx = q->rxq_idx + 1;
/* put the queue into worker_args */
- arg = &global.worker_args[q->core_idx];
+ arg = &global->worker_args[q->core_idx];
/* Check if interface already has queues configured */
for (j = 0; j < args->if_count; j++) {
@@ -784,9 +782,9 @@ static void setup_worker_qconf(app_args_t *args)
}
/* distribute tx queues among threads */
for (i = 0; i < args->worker_count; i++) {
- arg = &global.worker_args[i];
+ arg = &global->worker_args[i];
for (j = 0; j < args->if_count; j++) {
- port = &global.l3fwd_pktios[j];
+ port = &global->l3fwd_pktios[j];
arg->pktio[j].txq_idx =
port->txq_idx % port->nb_txq;
port->txq_idx++;
@@ -802,7 +800,7 @@ static void setup_worker_qconf(app_args_t *args)
const char *name;
int nb_rxq, nb_txq;
- port = &global.l3fwd_pktios[i];
+ port = &global->l3fwd_pktios[i];
name = args->if_names[i];
odp_pktin_queue_param_init(&in_queue_param);
odp_pktout_queue_param_init(&out_queue_param);
@@ -810,20 +808,21 @@ static void setup_worker_qconf(app_args_t *args)
in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
- in_queue_param.hash_enable = 1;
- in_queue_param.hash_proto.proto.ipv4 = 1;
- in_queue_param.hash_proto.proto.ipv4_tcp = 1;
- in_queue_param.hash_proto.proto.ipv4_udp = 1;
-
in_queue_param.num_queues = port->rxq_idx;
if (port->rxq_idx > port->nb_rxq) {
in_queue_param.num_queues = port->nb_rxq;
in_queue_param.op_mode = ODP_PKTIO_OP_MT;
}
+ in_queue_param.hash_enable = (in_queue_param.num_queues > 1) ?
+ 1 : 0;
+ in_queue_param.hash_proto.proto.ipv4 = 1;
+ in_queue_param.hash_proto.proto.ipv4_tcp = 1;
+ in_queue_param.hash_proto.proto.ipv4_udp = 1;
+
if (odp_pktin_queue_config(port->pktio, &in_queue_param))
- LOG_ABORT("Fail to config input queue for port %s\n",
- name);
+ ODPH_ABORT("Fail to config input queue for port %s\n",
+ name);
out_queue_param.num_queues = port->txq_idx;
if (port->txq_idx > port->nb_txq) {
@@ -831,26 +830,27 @@ static void setup_worker_qconf(app_args_t *args)
out_queue_param.op_mode = ODP_PKTIO_OP_MT;
}
if (odp_pktout_queue_config(port->pktio, &out_queue_param))
- LOG_ABORT("Fail to config output queue for port %s\n",
- name);
+ ODPH_ABORT("Fail to config output queue for port %s\n",
+ name);
inq = port->ifin;
nb_rxq = in_queue_param.num_queues;
if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)
- LOG_ABORT("Fail to set pktin queue for port %s\n",
- name);
+ ODPH_ABORT("Fail to set pktin queue for port %s\n",
+ name);
outq = port->ifout;
nb_txq = out_queue_param.num_queues;
if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)
- LOG_ABORT("Fail to set pktout queue for port %s\n",
- name);
+ ODPH_ABORT("Fail to set pktout queue for port %s\n",
+ name);
}
}
static void print_qconf_table(app_args_t *args)
{
- int i, j, k, qid, if_idx;
+ unsigned int i;
+ int j, k, qid, if_idx;
char buf[32];
struct thread_arg_s *thr_arg;
@@ -860,7 +860,7 @@ static void print_qconf_table(app_args_t *args)
"port/id", "rxq", "thread");
for (i = 0; i < args->worker_count; i++) {
- thr_arg = &global.worker_args[i];
+ thr_arg = &global->worker_args[i];
for (j = 0; j < args->if_count; j++) {
if (!thr_arg->pktio[j].nb_rxq)
continue;
@@ -905,7 +905,7 @@ static int print_speed_stats(int num_workers, int duration, int timeout)
timeout = 1;
}
/* Wait for all threads to be ready*/
- odp_barrier_wait(&barrier);
+ odp_barrier_wait(&global->barrier);
do {
pkts = 0;
@@ -914,9 +914,9 @@ static int print_speed_stats(int num_workers, int duration, int timeout)
sleep(timeout);
for (i = 0; i < num_workers; i++) {
- pkts += global.worker_args[i].packets;
- rx_drops += global.worker_args[i].rx_drops;
- tx_drops += global.worker_args[i].tx_drops;
+ pkts += global->worker_args[i].packets;
+ rx_drops += global->worker_args[i].rx_drops;
+ tx_drops += global->worker_args[i].tx_drops;
}
if (stats_enabled) {
pps = (pkts - pkts_prev) / timeout;
@@ -942,14 +942,15 @@ static int print_speed_stats(int num_workers, int duration, int timeout)
int main(int argc, char **argv)
{
- odph_odpthread_t thread_tbl[MAX_NB_WORKER];
+ odph_thread_t thread_tbl[MAX_NB_WORKER];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_NB_WORKER];
odp_pool_t pool;
odp_pool_param_t params;
odp_shm_t shm;
odp_instance_t instance;
- odph_odpthread_params_t thr_params;
odp_cpumask_t cpumask;
- int cpu, i, j, nb_worker;
+ int i, j, nb_worker;
uint8_t mac[ODPH_ETHADDR_LEN];
uint8_t *dst_mac;
app_args_t *args;
@@ -966,17 +967,34 @@ int main(int argc, char **argv)
exit(1);
}
- /* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */
- memset(&global, 0, sizeof(global));
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("_appl_global_data", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ printf("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ printf("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(global_data_t));
+ odp_atomic_init_u32(&global->exit_threads, 0);
+ global->shm = shm;
+
+ /* Initialize the dest mac as 2:0:0:0:0:x */
mac[0] = 2;
for (i = 0; i < MAX_NB_PKTIO; i++) {
mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;
- memcpy(global.eth_dest_mac[i].addr, mac, ODPH_ETHADDR_LEN);
+ memcpy(global->eth_dest_mac[i].addr, mac, ODPH_ETHADDR_LEN);
}
/* Initialize the thread arguments */
for (i = 0; i < MAX_NB_WORKER; i++) {
- thr_arg = &global.worker_args[i];
+ thr_arg = &global->worker_args[i];
for (j = 0; j < MAX_NB_PKTIO; j++) {
thr_arg->thr_idx = INVALID_ID;
thr_arg->pktio[j].txq_idx = INVALID_ID;
@@ -987,7 +1005,7 @@ int main(int argc, char **argv)
}
/* Parse cmdline arguments */
- args = &global.cmd_args;
+ args = &global->cmd_args;
parse_cmdline_args(argc, argv, args);
/* Init l3fwd table */
@@ -1035,7 +1053,7 @@ int main(int argc, char **argv)
char *if_name;
if_name = args->if_names[i];
- port = &global.l3fwd_pktios[i];
+ port = &global->l3fwd_pktios[i];
if (create_pktio(if_name, pool, port)) {
printf("Error: create pktio %s\n", if_name);
exit(1);
@@ -1047,9 +1065,8 @@ int main(int argc, char **argv)
setup_fwd_db();
dump_fwd_db();
- /* Dicide available workers */
nb_worker = MAX_NB_WORKER;
- if (args->worker_count)
+ if (args->worker_count && args->worker_count < MAX_NB_WORKER)
nb_worker = args->worker_count;
nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);
args->worker_count = nb_worker;
@@ -1060,9 +1077,9 @@ int main(int argc, char **argv)
/* Decide ip lookup method */
if (args->hash_mode)
- global.fwd_func = l3fwd_pkt_hash;
+ global->fwd_func = l3fwd_pkt_hash;
else
- global.fwd_func = l3fwd_pkt_lpm;
+ global->fwd_func = l3fwd_pkt_lpm;
/* Start all the available ports */
for (i = 0; i < args->if_count; i++) {
@@ -1071,7 +1088,7 @@ int main(int argc, char **argv)
char buf[32];
if_name = args->if_names[i];
- port = &global.l3fwd_pktios[i];
+ port = &global->l3fwd_pktios[i];
/* start pktio */
if (odp_pktio_start(port->pktio)) {
printf("unable to start pktio: %s\n", if_name);
@@ -1088,37 +1105,40 @@ int main(int argc, char **argv)
printf("start pktio: %s, mac %s\n", if_name, buf);
}
- odp_barrier_init(&barrier, nb_worker + 1);
+ odp_barrier_init(&global->barrier, nb_worker + 1);
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_worker;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
- memset(thread_tbl, 0, sizeof(thread_tbl));
- cpu = odp_cpumask_first(&cpumask);
for (i = 0; i < nb_worker; i++) {
- struct thread_arg_s *arg;
- odp_cpumask_t thr_mask;
-
- arg = &global.worker_args[i];
- odp_cpumask_zero(&thr_mask);
- odp_cpumask_set(&thr_mask, cpu);
- thr_params.arg = arg;
- odph_odpthreads_create(&thread_tbl[i], &thr_mask,
- &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = run_worker;
+ thr_param[i].arg = &global->worker_args[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
}
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, thr_param, nb_worker);
+
print_speed_stats(nb_worker, args->duration, PRINT_INTERVAL);
- exit_threads = 1;
+ odp_atomic_store_u32(&global->exit_threads, 1);
/* wait for other threads to join */
- for (i = 0; i < nb_worker; i++)
- odph_odpthreads_join(&thread_tbl[i]);
+ odph_thread_join(thread_tbl, nb_worker);
+
+ /* Stop and close used pktio devices */
+ for (i = 0; i < args->if_count; i++) {
+ odp_pktio_t pktio = global->l3fwd_pktios[i].pktio;
- /* if_names share a single buffer, so only one free */
- free(args->if_names[0]);
+ if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
+ printf("Error: failed to close pktio\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* if_names share a single buffer */
+ free(args->if_names_buf);
for (i = 0; i < MAX_NB_ROUTE; i++)
free(args->route_str[i]);
@@ -1144,6 +1164,11 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
+ if (odp_shm_free(global->shm)) {
+ printf("Error: shm free global data\n");
+ exit(EXIT_FAILURE);
+ }
+
if (odp_term_local()) {
printf("Error: term local\n");
exit(EXIT_FAILURE);
diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c
index 0670aa455..7d82695be 100644
--- a/example/l3fwd/odp_l3fwd_db.c
+++ b/example/l3fwd/odp_l3fwd_db.c
@@ -1,9 +1,9 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@@ -11,8 +11,9 @@
#include <stdlib.h>
#include <string.h>
-#include <example_debug.h>
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
#include <odp_l3fwd_db.h>
/** Jenkins hash support.
@@ -179,7 +180,7 @@ static flow_table_t fwd_lookup_cache;
static void create_fwd_hash_cache(void)
{
odp_shm_t hash_shm;
- flow_bucket_t *bucket;
+ flow_bucket_t *bucket = NULL;
flow_entry_t *flows;
uint32_t bucket_count, flow_count, size;
uint32_t i;
@@ -191,8 +192,9 @@ static void create_fwd_hash_cache(void)
size = sizeof(flow_bucket_t) * bucket_count +
sizeof(flow_entry_t) * flow_count;
hash_shm = odp_shm_reserve("flow_table", size, ODP_CACHE_LINE_SIZE, 0);
+ if (hash_shm != ODP_SHM_INVALID)
+ bucket = odp_shm_addr(hash_shm);
- bucket = odp_shm_addr(hash_shm);
if (!bucket) {
/* Try the second time with small request */
flow_count /= 4;
@@ -201,9 +203,14 @@ static void create_fwd_hash_cache(void)
sizeof(flow_entry_t) * flow_count;
hash_shm = odp_shm_reserve("flow_table", size,
ODP_CACHE_LINE_SIZE, 0);
+ if (hash_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
bucket = odp_shm_addr(hash_shm);
if (!bucket) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
+ ODPH_ERR("Error: shared mem alloc failed.\n");
exit(-1);
}
}
@@ -342,10 +349,15 @@ void init_fwd_db(void)
ODP_CACHE_LINE_SIZE,
0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
fwd_db = odp_shm_addr(shm);
if (fwd_db == NULL) {
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
+ ODPH_ERR("Error: shared mem alloc failed.\n");
exit(EXIT_FAILURE);
}
memset(fwd_db, 0, sizeof(*fwd_db));
diff --git a/example/l3fwd/odp_l3fwd_db.h b/example/l3fwd/odp_l3fwd_db.h
index 8df2999df..5c9d63e6e 100644
--- a/example/l3fwd/odp_l3fwd_db.h
+++ b/example/l3fwd/odp_l3fwd_db.h
@@ -1,9 +1,9 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef _ODP_L3FWD_DB_H_
#define _ODP_L3FWD_DB_H_
@@ -39,7 +39,7 @@ typedef struct ip_addr_range_s {
/**
* TCP/UDP flow
*/
-typedef struct ipv4_tuple5_s {
+typedef struct ODP_ALIGNED_CACHE ipv4_tuple5_s {
union {
struct {
int32_t src_ip;
@@ -55,7 +55,7 @@ typedef struct ipv4_tuple5_s {
int64_t lo64;
};
};
-} ipv4_tuple5_t ODP_ALIGNED_CACHE;
+} ipv4_tuple5_t;
/**
* Forwarding data base entry
diff --git a/example/l3fwd/odp_l3fwd_lpm.c b/example/l3fwd/odp_l3fwd_lpm.c
index 65a338916..41572d179 100644
--- a/example/l3fwd/odp_l3fwd_lpm.c
+++ b/example/l3fwd/odp_l3fwd_lpm.c
@@ -1,8 +1,9 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@@ -10,8 +11,8 @@
#include <stdio.h>
#include <stdlib.h>
-#include <example_debug.h>
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
#include <odp_l3fwd_lpm.h>
@@ -154,9 +155,14 @@ void fib_tbl_init(void)
size = FIB_NEXT_SIZE * FIB_SUB_COUNT;
/*Reserve memory for Routing hash table*/
lpm_shm = odp_shm_reserve("fib_lpm_sub", size, ODP_CACHE_LINE_SIZE, 0);
+ if (lpm_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
fe = odp_shm_addr(lpm_shm);
if (!fe) {
- EXAMPLE_ERR("Error: shared mem alloc failed for lpm cache.\n");
+ ODPH_ERR("Error: shared mem alloc failed for lpm cache.\n");
exit(-1);
}
diff --git a/example/l3fwd/odp_l3fwd_lpm.h b/example/l3fwd/odp_l3fwd_lpm.h
index 925664250..bc3382532 100644
--- a/example/l3fwd/odp_l3fwd_lpm.h
+++ b/example/l3fwd/odp_l3fwd_lpm.h
@@ -1,9 +1,9 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
#ifndef _ODP_L3FWD_LPM_H_
#define _ODP_L3FWD_LPM_H_
diff --git a/example/l3fwd/odp_l3fwd_run.sh b/example/l3fwd/odp_l3fwd_run.sh
index 6f0b11a04..0b39d5a52 100755
--- a/example/l3fwd/odp_l3fwd_run.sh
+++ b/example/l3fwd/odp_l3fwd_run.sh
@@ -1,28 +1,30 @@
#!/bin/bash
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
-PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
-PCAP_OUT="pcapout.pcap"
-PCAP_IN_SIZE=`stat -c %s ${PCAP_IN}`
-echo "using PCAP_IN = ${PCAP_IN}, PCAP_OUT = ${PCAP_OUT}"
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
-./odp_l3fwd -i pcap:in=${PCAP_IN},pcap:out=${PCAP_OUT} \
- -r "10.0.0.0/24,pcap:out=${PCAP_OUT}" -d 30
+setup_interfaces
+
+./odp_l3fwd${EXEEXT} -i $IF0,$IF1 -r "10.0.0.0/24,$IF1" -d 1
STATUS=$?
-PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
-rm -f ${PCAP_OUT}
-if [ ${STATUS} -ne 0 ] || [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
- echo "Error: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
exit 1
fi
-echo "Pass: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+validate_result
+
+cleanup_interfaces
exit 0
diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
index 18218d056..b51d8bd42 100644
--- a/example/m4/configure.m4
+++ b/example/m4/configure.m4
@@ -1,25 +1,38 @@
##########################################################################
-# Enable/disable test-example
+# Build and install example applications
+##########################################################################
+AC_ARG_WITH([examples],
+ [AS_HELP_STRING([--without-examples],
+ [don't build and install example applications]
+ [[default=with]])],
+ [],
+ [with_examples=yes])
+AM_CONDITIONAL([WITH_EXAMPLES], [test x$with_examples != xno])
+
+##########################################################################
+# Test examples during 'make check'
##########################################################################
-test_example=no
AC_ARG_ENABLE([test-example],
- [ --enable-test-example run basic test against examples],
- [if test "x$enableval" = "xyes"; then
- test_example=yes
- else
- test_example=no
- fi])
+ [AS_HELP_STRING([--enable-test-example],
+ [run basic test against examples [default=enabled]])],
+ [test_example=$enableval],
+ [test_example=yes])
+AM_CONDITIONAL([test_example], [test x$test_example = xyes ])
AC_CONFIG_FILES([example/classifier/Makefile
- example/generator/Makefile
+ example/cli/Makefile
+ example/debug/Makefile
example/hello/Makefile
- example/ipsec/Makefile
+ example/ipsec_api/Makefile
+ example/ipsec_crypto/Makefile
example/ipfragreass/Makefile
example/l2fwd_simple/Makefile
example/l3fwd/Makefile
example/packet/Makefile
+ example/ping/Makefile
+ example/simple_pipeline/Makefile
example/switch/Makefile
- example/time/Makefile
+ example/sysinfo/Makefile
example/timer/Makefile
example/traffic_mgmt/Makefile
example/Makefile])
diff --git a/example/packet/.gitignore b/example/packet/.gitignore
index 4610a1922..b3869816f 100644
--- a/example/packet/.gitignore
+++ b/example/packet/.gitignore
@@ -1,4 +1,6 @@
+odp_packet_dump
odp_pktio
+pktio_env
*.log
*.trs
pcapout.pcap
diff --git a/example/packet/Makefile.am b/example/packet/Makefile.am
index af729b098..5e4d9f5ea 100644
--- a/example/packet/Makefile.am
+++ b/example/packet/Makefile.am
@@ -1,17 +1,37 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_pktio$(EXEEXT)
-odp_pktio_LDFLAGS = $(AM_LDFLAGS) -static
-odp_pktio_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_packet_dump \
+ odp_pktio
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
+odp_packet_dump_SOURCES = odp_packet_dump.c
-dist_odp_pktio_SOURCES = odp_pktio.c
+odp_pktio_SOURCES = odp_pktio.c
if test_example
-if HAVE_PCAP
-TESTS = pktio_run.sh
+if ODP_PKTIO_PCAP
+TESTS = packet_dump_run.sh pktio_run.sh
endif
endif
-EXTRA_DIST = pktio_run.sh udp64.pcap
+EXTRA_DIST = packet_dump_run.sh pktio_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/packet/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/packet/odp_packet_dump.c b/example/packet/odp_packet_dump.c
new file mode 100644
index 000000000..2e16861d9
--- /dev/null
+++ b/example/packet/odp_packet_dump.c
@@ -0,0 +1,765 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2024 Nokia
+ */
+
+/**
+ * @example odp_packet_dump.c
+ *
+ * Packet dump example application which prints received packets to terminal
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_PKTIOS 32
+#define MAX_PKTIO_NAME 255
+#define MAX_PKT_NUM 1024
+#define MAX_FILTERS 32
+
+typedef struct test_options_t {
+ uint64_t num_packet;
+ uint32_t data_offset;
+ uint32_t data_len;
+ int verbose;
+ int num_pktio;
+ char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
+ int num_filter_l3;
+ int filter_l3[MAX_FILTERS];
+ int num_filter_l4;
+ int filter_l4[MAX_FILTERS];
+
+} test_options_t;
+
+typedef struct test_global_t {
+ test_options_t opt;
+ odp_pool_t pool;
+ odp_atomic_u32_t stop;
+
+ struct {
+ odp_pktio_t pktio;
+ int started;
+
+ } pktio[MAX_PKTIOS];
+
+} test_global_t;
+
+static test_global_t test_global;
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ odp_atomic_store_u32(&test_global.stop, 1);
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Print received packets\n"
+ "\n"
+ "OPTIONS:\n"
+ " -i, --interface <name> Packet IO interfaces (comma-separated, no spaces)\n"
+ " -n, --num_packet <number> Exit after this many packets. Use 0 to run infinitely. Default 0.\n"
+ " -o, --data_offset <number> Data print start offset in bytes. Default 0.\n"
+ " -l, --data_length <number> Data print length in bytes. Default 0.\n"
+ " --filter_l3 <type> Print only packets with matching L3 type. Comma-separated\n"
+ " list (no spaces) of ODP L3 type values (e.g. value of ODP_PROTO_L3_TYPE_IPV4).\n"
+ " --filter_l4 <type> Print only packets with matching L4 type. Comma-separated\n"
+ " list (no spaces) of ODP L4 type values (e.g. value of ODP_PROTO_L4_TYPE_TCP).\n"
+ " -v, --verbose Print extra packet information.\n"
+ " -h, --help Display help and exit.\n\n");
+}
+
+static int parse_int_list(char *str, int integer[], int max_num)
+{
+ int str_len, len;
+ int i = 0;
+
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ len = strcspn(str, ",");
+ str[len] = 0;
+
+ if (i == max_num) {
+ ODPH_ERR("Maximum number of options is %i\n", max_num);
+ return -1;
+ }
+
+ integer[i] = atoi(str);
+
+ str_len -= len + 1;
+ str += len + 1;
+ i++;
+ }
+
+ return i;
+}
+
+static int parse_options(int argc, char *argv[], test_global_t *global)
+{
+ int i, opt, long_index;
+ char *name, *str;
+ int len, str_len, num;
+
+ const struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'},
+ {"num_packet", required_argument, NULL, 'n'},
+ {"data_offset", required_argument, NULL, 'o'},
+ {"data_length", required_argument, NULL, 'l'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"filter_l3", required_argument, NULL, 0 },
+ {"filter_l4", required_argument, NULL, 1 },
+ {NULL, 0, NULL, 0}
+ };
+ const char *shortopts = "+i:n:o:l:vh";
+ int ret = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 0:
+ /* --filter_l3 */
+ num = parse_int_list(optarg, global->opt.filter_l3,
+ MAX_FILTERS);
+ global->opt.num_filter_l3 = num;
+
+ if (num < 0)
+ ret = -1;
+ break;
+ case 1:
+ /* --filter_l4 */
+ num = parse_int_list(optarg, global->opt.filter_l4,
+ MAX_FILTERS);
+ global->opt.num_filter_l4 = num;
+
+ if (num < 0)
+ ret = -1;
+ break;
+ case 'i':
+ i = 0;
+ str = optarg;
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ len = strcspn(str, ",");
+ str_len -= len + 1;
+
+ if (i == MAX_PKTIOS) {
+ ODPH_ERR("Too many interfaces\n");
+ ret = -1;
+ break;
+ }
+
+ if (len > MAX_PKTIO_NAME) {
+ ODPH_ERR("Too long interface name %s\n", str);
+ ret = -1;
+ break;
+ }
+
+ name = global->opt.pktio_name[i];
+ memcpy(name, str, len);
+ str += len + 1;
+ i++;
+ }
+
+ global->opt.num_pktio = i;
+
+ break;
+ case 'o':
+ global->opt.data_offset = atoi(optarg);
+ break;
+ case 'l':
+ global->opt.data_len = atoi(optarg);
+ break;
+ case 'n':
+ global->opt.num_packet = atoll(optarg);
+ break;
+ case 'v':
+ global->opt.verbose = 1;
+ break;
+ case 'h':
+ default:
+ print_usage();
+ return -1;
+ }
+ }
+
+ if (global->opt.num_pktio == 0) {
+ ODPH_ERR("At least one pktio interface needed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int open_pktios(test_global_t *global)
+{
+ odp_pool_param_t pool_param;
+ odp_pktio_param_t pktio_param;
+ odp_pool_t pool;
+ odp_pktio_capability_t pktio_capa;
+ odp_pool_capability_t pool_capa;
+ odp_pktio_t pktio;
+ odp_pktio_config_t pktio_config;
+ odp_pktin_queue_param_t pktin_param;
+ char *name;
+ int i, num_pktio;
+ uint32_t num_pkt = MAX_PKT_NUM;
+
+ num_pktio = global->opt.num_pktio;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_num < MAX_PKT_NUM)
+ num_pkt = pool_capa.pkt.max_num;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = num_pkt;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet pool", &pool_param);
+
+ global->pool = pool;
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DISABLED;
+
+ for (i = 0; i < num_pktio; i++)
+ global->pktio[i].pktio = ODP_PKTIO_INVALID;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_pktio; i++) {
+ name = global->opt.pktio_name[i];
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Pktio open failed for %s\n", name);
+ return -1;
+ }
+
+ global->pktio[i].pktio = pktio;
+
+ if (odp_pktio_capability(pktio, &pktio_capa)) {
+ ODPH_ERR("Pktio capability failed for %s\n", name);
+ return -1;
+ }
+
+ odp_pktio_print(pktio);
+
+ odp_pktio_config_init(&pktio_config);
+ pktio_config.pktin.bit.ts_all = pktio_capa.config.pktin.bit.ts_all;
+ pktio_config.parser.layer = ODP_PROTO_LAYER_ALL;
+
+ odp_pktio_config(pktio, &pktio_config);
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ pktin_param.num_queues = 1;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Pktin config failed for %s\n", name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int start_pktios(test_global_t *global)
+{
+ int i;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ if (odp_pktio_start(global->pktio[i].pktio)) {
+ ODPH_ERR("Pktio start failed for %s\n", global->opt.pktio_name[i]);
+
+ return -1;
+ }
+
+ global->pktio[i].started = 1;
+ }
+
+ return 0;
+}
+
+static int stop_pktios(test_global_t *global)
+{
+ odp_pktio_t pktio;
+ int i, ret = 0;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID || global->pktio[i].started == 0)
+ continue;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Pktio stop failed for %s\n", global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static void empty_queues(void)
+{
+ odp_event_t ev;
+ uint64_t wait_time = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS / 2);
+
+ /* Drop all events from all queues */
+ while (1) {
+ ev = odp_schedule(NULL, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+}
+
+static int close_pktios(test_global_t *global)
+{
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ int i, ret = 0;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Pktio close failed for %s\n", global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ pool = global->pool;
+
+ if (pool == ODP_POOL_INVALID)
+ return ret;
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void print_mac_addr(uint8_t *addr)
+{
+ printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void print_ipv4_addr(uint8_t *addr)
+{
+ printf("%u.%u.%u.%u\n",
+ addr[0], addr[1], addr[2], addr[3]);
+}
+
+static void print_port(uint8_t *ptr)
+{
+ uint16_t *port = (uint16_t *)(uintptr_t)ptr;
+
+ printf("%u\n", odp_be_to_cpu_16(*port));
+}
+
+static void print_data(odp_packet_t pkt, uint32_t offset, uint32_t len)
+{
+ const uint32_t bytes_per_row = 16;
+ const uint32_t num_char = 1 + (bytes_per_row * 3) + 1;
+ uint8_t data[bytes_per_row];
+ char row[num_char];
+ uint32_t copy_len, i, j;
+ uint32_t data_len = odp_packet_len(pkt);
+
+ if (offset > data_len)
+ return;
+
+ if (offset + len > data_len)
+ len = data_len - offset;
+
+ while (len) {
+ i = 0;
+
+ if (len > bytes_per_row)
+ copy_len = bytes_per_row;
+ else
+ copy_len = len;
+
+ odp_packet_copy_to_mem(pkt, offset, copy_len, data);
+
+ i += snprintf(&row[i], num_char - i, " ");
+
+ for (j = 0; j < copy_len; j++)
+ i += snprintf(&row[i], num_char - i, " %02x", data[j]);
+
+ row[i] = 0;
+ printf("%s\n", row);
+
+ len -= copy_len;
+ offset += copy_len;
+ }
+}
+
+static int print_packet(test_global_t *global, odp_packet_t pkt,
+ uint64_t num_packet)
+{
+ odp_pktio_t pktio = odp_packet_input(pkt);
+ odp_pktio_info_t pktio_info;
+ odp_time_t pktio_time, time;
+ uint64_t sec, nsec;
+ uint32_t offset;
+ int i, type, match;
+ int num_filter_l3 = global->opt.num_filter_l3;
+ int num_filter_l4 = global->opt.num_filter_l4;
+ uint8_t *data = odp_packet_data(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint32_t l2_offset = odp_packet_l2_offset(pkt);
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+ uint32_t l4_offset = odp_packet_l4_offset(pkt);
+ int tcp = odp_packet_has_tcp(pkt);
+ int udp = odp_packet_has_udp(pkt);
+ int sctp = odp_packet_has_sctp(pkt);
+ int icmp = odp_packet_has_icmp(pkt);
+ int ipv4 = odp_packet_has_ipv4(pkt);
+
+ if (odp_packet_has_ts(pkt)) {
+ pktio_time = odp_pktio_time(pktio, NULL);
+ time = odp_packet_ts(pkt);
+ } else {
+ time = odp_time_local();
+ pktio_time = ODP_TIME_NULL;
+ }
+
+ /* Filter based on L3 type */
+ if (num_filter_l3) {
+ match = 0;
+
+ for (i = 0; i < num_filter_l3; i++) {
+ type = global->opt.filter_l3[i];
+
+ if (type == odp_packet_l3_type(pkt)) {
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match)
+ return 0;
+ }
+
+ /* Filter based on L4 type */
+ if (num_filter_l4) {
+ match = 0;
+
+ for (i = 0; i < num_filter_l4; i++) {
+ type = global->opt.filter_l4[i];
+
+ if (type == odp_packet_l4_type(pkt)) {
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match)
+ return 0;
+ }
+
+ nsec = odp_time_to_ns(time);
+ sec = nsec / ODP_TIME_SEC_IN_NS;
+ nsec = nsec - (sec * ODP_TIME_SEC_IN_NS);
+
+ if (odp_pktio_info(pktio, &pktio_info)) {
+ ODPH_ERR("Pktio info failed\n");
+ return -1;
+ }
+
+ printf("PACKET [%" PRIu64 "]\n", num_packet);
+ printf(" time: %" PRIu64 ".%09" PRIu64 " sec\n", sec, nsec);
+
+ if (odp_time_cmp(pktio_time, ODP_TIME_NULL)) {
+ nsec = odp_time_to_ns(pktio_time);
+ sec = nsec / ODP_TIME_SEC_IN_NS;
+ nsec = nsec - (sec * ODP_TIME_SEC_IN_NS);
+ printf(" pktio time: %" PRIu64 ".%09" PRIu64 " sec\n", sec, nsec);
+ printf(" input delay: %" PRIu64 " nsec\n", odp_time_diff_ns(pktio_time, time));
+ }
+
+ printf(" interface name: %s\n", pktio_info.name);
+ printf(" packet length: %u bytes\n", odp_packet_len(pkt));
+
+ /* L2 */
+ if (odp_packet_has_eth(pkt)) {
+ uint8_t *eth = data + l2_offset;
+
+ printf(" Ethernet offset: %u bytes\n", l2_offset);
+ if (l2_offset + 6 <= seg_len) {
+ printf(" dst address: ");
+ print_mac_addr(eth);
+ }
+
+ if (l2_offset + 12 <= seg_len) {
+ printf(" src address: ");
+ print_mac_addr(eth + 6);
+ }
+
+ /* VLAN */
+ if (odp_packet_has_vlan(pkt)) {
+ int qinq = odp_packet_has_vlan_qinq(pkt);
+ uint16_t *tpid = (uint16_t *)(uintptr_t)(eth + 12);
+ uint16_t *tci = tpid + 1;
+
+ if (qinq)
+ printf(" VLAN (outer):\n");
+ else
+ printf(" VLAN:\n");
+
+ if (l2_offset + 14 <= seg_len) {
+ printf(" TPID: 0x%04x\n",
+ odp_be_to_cpu_16(*tpid));
+ }
+
+ if (l2_offset + 16 <= seg_len) {
+ printf(" TCI: 0x%04x (VID: %u)\n",
+ odp_be_to_cpu_16(*tci),
+ odp_be_to_cpu_16(*tci) & 0x0fff);
+ }
+
+ if (qinq) {
+ printf(" VLAN (inner):\n");
+ tpid += 2;
+ tci += 2;
+
+ if (l2_offset + 18 <= seg_len) {
+ printf(" TPID: 0x%04x\n",
+ odp_be_to_cpu_16(*tpid));
+ }
+
+ if (l2_offset + 20 <= seg_len) {
+ printf(" TCI: 0x%04x (VID: %u)\n",
+ odp_be_to_cpu_16(*tci),
+ odp_be_to_cpu_16(*tci) & 0x0fff);
+ }
+ }
+ }
+
+ } else if (odp_packet_has_l2(pkt)) {
+ printf(" L2 (%i) offset: %u bytes\n",
+ odp_packet_l2_type(pkt), l2_offset);
+ }
+
+ /* L3 */
+ if (ipv4) {
+ printf(" IPv4 offset: %u bytes\n", l3_offset);
+ offset = l3_offset + 12;
+ if (offset + 4 <= seg_len) {
+ printf(" src address: ");
+ print_ipv4_addr(data + offset);
+ }
+
+ offset = l3_offset + 16;
+ if (offset + 4 <= seg_len) {
+ printf(" dst address: ");
+ print_ipv4_addr(data + offset);
+ }
+ } else if (odp_packet_has_ipv6(pkt)) {
+ printf(" IPv6 offset: %u bytes\n", l3_offset);
+ } else if (odp_packet_has_l3(pkt)) {
+ printf(" L3 (%i) offset: %u bytes\n",
+ odp_packet_l3_type(pkt), l3_offset);
+ }
+
+ /* L4 */
+ if (tcp || udp || sctp) {
+ if (tcp)
+ printf(" TCP offset: %u bytes\n", l4_offset);
+ else if (udp)
+ printf(" UDP offset: %u bytes\n", l4_offset);
+ else
+ printf(" SCTP offset: %u bytes\n", l4_offset);
+
+ offset = l4_offset;
+ if (offset + 2 <= seg_len) {
+ printf(" src port: ");
+ print_port(data + offset);
+ }
+
+ offset = l4_offset + 2;
+ if (offset + 2 <= seg_len) {
+ printf(" dst port: ");
+ print_port(data + offset);
+ }
+ } else if (icmp) {
+ printf(" ICMP offset: %u bytes\n", l4_offset);
+ if (ipv4) {
+ uint32_t len;
+ uint8_t *u8 = odp_packet_l4_ptr(pkt, &len);
+
+ if (u8 && len >= 2) {
+ printf(" type: %u\n", u8[0]);
+ printf(" code: %u\n", u8[1]);
+ }
+ }
+ } else if (odp_packet_has_l4(pkt)) {
+ printf(" L4 (%i) offset: %u bytes\n",
+ odp_packet_l4_type(pkt), l4_offset);
+ }
+
+ /* User defined data range */
+ if (global->opt.data_len)
+ print_data(pkt, global->opt.data_offset, global->opt.data_len);
+
+ if (global->opt.verbose)
+ odp_packet_print(pkt);
+
+ printf("\n");
+
+ return 1;
+}
+
+static int receive_packets(test_global_t *global)
+{
+ odp_event_t ev;
+ odp_packet_t pkt;
+ int printed;
+ uint64_t num_packet = 0;
+
+ while (!odp_atomic_load_u32(&global->stop)) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ if (odp_event_type(ev) != ODP_EVENT_PACKET) {
+ ODPH_ERR("Bad event type: %i\n", odp_event_type(ev));
+ odp_event_free(ev);
+ continue;
+ }
+
+ pkt = odp_packet_from_event(ev);
+
+ printed = print_packet(global, pkt, num_packet);
+
+ odp_packet_free(pkt);
+
+ if (odp_unlikely(printed < 0))
+ return -1;
+
+ if (!printed)
+ continue;
+
+ num_packet++;
+ if (global->opt.num_packet &&
+ num_packet >= global->opt.num_packet)
+ break;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ test_global_t *global;
+ int ret = 0;
+
+ global = &test_global;
+ memset(global, 0, sizeof(test_global_t));
+ odp_atomic_init_u32(&global->stop, 0);
+
+ signal(SIGINT, sig_handler);
+
+ if (parse_options(argc, argv, global))
+ return -1;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, NULL, NULL)) {
+ ODPH_ERR("Global init failed\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed\n");
+ return -1;
+ }
+
+ global->pool = ODP_POOL_INVALID;
+
+ odp_schedule_config(NULL);
+
+ odp_sys_info_print();
+
+ if (open_pktios(global)) {
+ ODPH_ERR("Pktio open failed\n");
+ return -1;
+ }
+
+ if (start_pktios(global)) {
+ ODPH_ERR("Pktio start failed\n");
+ return -1;
+ }
+
+ if (receive_packets(global)) {
+ ODPH_ERR("Packet receive failed\n");
+ return -1;
+ }
+
+ if (stop_pktios(global)) {
+ ODPH_ERR("Pktio stop failed\n");
+ return -1;
+ }
+
+ empty_queues();
+
+ if (close_pktios(global)) {
+ ODPH_ERR("Pktio close failed\n");
+ return -1;
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Term local failed\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Term global failed\n");
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
index ea8428259..a82537fe7 100644
--- a/example/packet/odp_pktio.c
+++ b/example/packet/odp_pktio.c
@@ -1,7 +1,13 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @example odp_pktio.c
+ *
+ * Basic packet IO loopback example application
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdlib.h>
@@ -10,15 +16,13 @@
#include <unistd.h>
#include <inttypes.h>
-#include <example_debug.h>
-
#include <odp_api.h>
#include <odp/helper/odph_api.h>
/** @def MAX_WORKERS
* @brief Maximum number of worker threads
*/
-#define MAX_WORKERS 32
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
/** @def SHM_PKT_POOL_SIZE
* @brief Size of the shared memory block
@@ -63,12 +67,12 @@
* Parsed command line application arguments
*/
typedef struct {
- int cpu_count; /**< Number of CPUs to use */
+ unsigned int cpu_count; /**< Number of CPUs to use */
int if_count; /**< Number of interfaces to be used */
char **if_names; /**< Array of pointers to interface names */
int mode; /**< Packet IO mode */
char *if_str; /**< Storage for interface names */
- int time; /**< Time to run app */
+ double time; /**< Time to run app */
} appl_args_t;
/**
@@ -85,10 +89,12 @@ typedef struct {
typedef struct {
/** Application (parsed) arguments */
appl_args_t appl;
+ /** Shm for global data */
+ odp_shm_t shm;
/** Thread specific arguments */
thread_args_t thread[MAX_WORKERS];
/** Flag to exit worker threads */
- int exit_threads;
+ odp_atomic_u32_t exit_threads;
} args_t;
/** Global pointer to args */
@@ -131,13 +137,13 @@ static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool, int mode)
pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
break;
default:
- EXAMPLE_ABORT("invalid mode %d\n", mode);
+ ODPH_ABORT("invalid mode %d\n", mode);
}
/* Open a packet IO instance */
pktio = odp_pktio_open(dev, pool, &pktio_param);
if (pktio == ODP_PKTIO_INVALID)
- EXAMPLE_ABORT("Error: pktio create failed for %s\n", dev);
+ ODPH_ABORT("Error: pktio create failed for %s\n", dev);
odp_pktin_queue_param_init(&pktin_param);
@@ -145,14 +151,14 @@ static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool, int mode)
pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
if (odp_pktin_queue_config(pktio, &pktin_param))
- EXAMPLE_ABORT("Error: pktin config failed for %s\n", dev);
+ ODPH_ABORT("Error: pktin config failed for %s\n", dev);
if (odp_pktout_queue_config(pktio, NULL))
- EXAMPLE_ABORT("Error: pktout config failed for %s\n", dev);
+ ODPH_ABORT("Error: pktout config failed for %s\n", dev);
ret = odp_pktio_start(pktio);
if (ret != 0)
- EXAMPLE_ABORT("Error: unable to start %s\n", dev);
+ ODPH_ABORT("Error: unable to start %s\n", dev);
printf(" created pktio:%02" PRIu64
", dev:%s, queue mode (ATOMIC queues)\n"
@@ -186,8 +192,8 @@ static int pktio_queue_thread(void *arg)
pktio = odp_pktio_lookup(thr_args->pktio_dev);
if (pktio == ODP_PKTIO_INVALID) {
- EXAMPLE_ERR(" [%02i] Error: lookup of pktio %s failed\n",
- thr, thr_args->pktio_dev);
+ ODPH_ERR(" [%02i] Error: lookup of pktio %s failed\n",
+ thr, thr_args->pktio_dev);
return -1;
}
@@ -200,13 +206,13 @@ static int pktio_queue_thread(void *arg)
if ((thr_args->mode == APPL_MODE_PKT_QUEUE) &&
(odp_pktin_event_queue(pktio, &inq, 1) != 1)) {
- EXAMPLE_ERR(" [%02i] Error: no input queue for %s\n",
- thr, thr_args->pktio_dev);
+ ODPH_ERR(" [%02i] Error: no input queue for %s\n",
+ thr, thr_args->pktio_dev);
return -1;
}
/* Loop packets */
- while (!args->exit_threads) {
+ while (!odp_atomic_load_u32(&args->exit_threads)) {
odp_pktio_t pktio_tmp;
if (inq != ODP_QUEUE_INVALID)
@@ -223,14 +229,14 @@ static int pktio_queue_thread(void *arg)
/* Drop packets with errors */
if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
- EXAMPLE_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
+ ODPH_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
continue;
}
pktio_tmp = odp_packet_input(pkt);
if (odp_pktout_queue(pktio_tmp, &pktout, 1) != 1) {
- EXAMPLE_ERR(" [%02i] Error: no pktout queue\n", thr);
+ ODPH_ERR(" [%02i] Error: no pktout queue\n", thr);
return -1;
}
@@ -239,7 +245,7 @@ static int pktio_queue_thread(void *arg)
/* Enqueue the packet for output */
if (odp_pktout_send(pktout, &pkt, 1) != 1) {
- EXAMPLE_ERR(" [%i] Packet send failed.\n", thr);
+ ODPH_ERR(" [%i] Packet send failed.\n", thr);
odp_packet_free(pkt);
continue;
}
@@ -277,8 +283,8 @@ static int pktio_ifburst_thread(void *arg)
pktio = odp_pktio_lookup(thr_args->pktio_dev);
if (pktio == ODP_PKTIO_INVALID) {
- EXAMPLE_ERR(" [%02i] Error: lookup of pktio %s failed\n",
- thr, thr_args->pktio_dev);
+ ODPH_ERR(" [%02i] Error: lookup of pktio %s failed\n",
+ thr, thr_args->pktio_dev);
return -1;
}
@@ -286,17 +292,17 @@ static int pktio_ifburst_thread(void *arg)
thr, odp_pktio_to_u64(pktio));
if (odp_pktin_queue(pktio, &pktin, 1) != 1) {
- EXAMPLE_ERR(" [%02i] Error: no pktin queue\n", thr);
+ ODPH_ERR(" [%02i] Error: no pktin queue\n", thr);
return -1;
}
if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
- EXAMPLE_ERR(" [%02i] Error: no pktout queue\n", thr);
+ ODPH_ERR(" [%02i] Error: no pktout queue\n", thr);
return -1;
}
/* Loop packets */
- while (!args->exit_threads) {
+ while (!odp_atomic_load_u32(&args->exit_threads)) {
pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
if (pkts > 0) {
/* Drop packets with errors */
@@ -318,8 +324,8 @@ static int pktio_ifburst_thread(void *arg)
}
if (odp_unlikely(pkts_ok != pkts))
- EXAMPLE_ERR("Dropped frames:%u - err_cnt:%lu\n",
- pkts-pkts_ok, ++err_cnt);
+ ODPH_ERR("Dropped frames:%u - err_cnt:%lu\n",
+ pkts - pkts_ok, ++err_cnt);
/* Print packet counts every once in a while */
tmp += pkts_ok;
@@ -341,44 +347,68 @@ static int pktio_ifburst_thread(void *arg)
*/
int main(int argc, char *argv[])
{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_WORKERS];
odp_pool_t pool;
int num_workers;
int i;
- int cpu;
odp_cpumask_t cpumask;
char cpumaskstr[ODP_CPUMASK_STR_SIZE];
odp_pool_param_t params;
odp_instance_t instance;
- odph_odpthread_params_t thr_params;
+ odp_init_t init_param;
+ odp_shm_t shm;
- args = calloc(1, sizeof(args_t));
- if (args == NULL) {
- EXAMPLE_ERR("Error: args mem alloc failed.\n");
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
exit(EXIT_FAILURE);
}
- /* Parse and store the application arguments */
- parse_args(argc, argv, &args->appl);
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
/* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
exit(EXIT_FAILURE);
}
/* Init this thread */
if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
+ ODPH_ERR("Error: ODP local init failed.\n");
exit(EXIT_FAILURE);
}
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("_appl_global_data", sizeof(args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ args = odp_shm_addr(shm);
+ if (args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(args, 0, sizeof(args_t));
+ odp_atomic_init_u32(&args->exit_threads, 0);
+ args->shm = shm;
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &args->appl);
+
/* Print both system and application information */
print_info(NO_PATH(argv[0]), &args->appl);
- /* Default to system CPU count unless user specified */
num_workers = MAX_WORKERS;
- if (args->appl.cpu_count)
+ if (args->appl.cpu_count && args->appl.cpu_count < MAX_WORKERS)
num_workers = args->appl.cpu_count;
/* Get default worker cpumask */
@@ -399,25 +429,24 @@ int main(int argc, char *argv[])
pool = odp_pool_create("packet_pool", &params);
if (pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
+ ODPH_ERR("Error: packet pool create failed.\n");
exit(EXIT_FAILURE);
}
odp_pool_print(pool);
+ /* Config and start scheduler */
+ odp_schedule_config(NULL);
+
/* Create a pktio instance for each interface */
for (i = 0; i < args->appl.if_count; ++i)
create_pktio(args->appl.if_names[i], pool, args->appl.mode);
/* Create and init worker threads */
- memset(thread_tbl, 0, sizeof(thread_tbl));
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
- cpu = odp_cpumask_first(&cpumask);
for (i = 0; i < num_workers; ++i) {
- odp_cpumask_t thd_mask;
int (*thr_run_func)(void *);
int if_idx;
@@ -430,21 +459,16 @@ int main(int argc, char *argv[])
thr_run_func = pktio_ifburst_thread;
else /* APPL_MODE_PKT_QUEUE */
thr_run_func = pktio_queue_thread;
- /*
- * Create threads one-by-one instead of all-at-once,
- * because each thread might get different arguments.
- * Calls odp_thread_create(cpu) for each thread
- */
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
-
- thr_params.start = thr_run_func;
- thr_params.arg = &args->thread[i];
-
- odph_odpthreads_create(&thread_tbl[i], &thd_mask, &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = thr_run_func;
+ thr_param[i].arg = &args->thread[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
}
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
+
if (args->appl.time) {
odp_time_wait_ns(args->appl.time *
ODP_TIME_SEC_IN_NS);
@@ -455,22 +479,26 @@ int main(int argc, char *argv[])
odp_pktio_stop(pktio);
}
/* use delay to let workers clean up queues */
- odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
- args->exit_threads = 1;
+ odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS);
+ odp_atomic_store_u32(&args->exit_threads, 1);
}
/* Master thread waits for other threads to exit */
- for (i = 0; i < num_workers; ++i)
- odph_odpthreads_join(&thread_tbl[i]);
+ odph_thread_join(thread_tbl, num_workers);
for (i = 0; i < args->appl.if_count; ++i)
odp_pktio_close(odp_pktio_lookup(args->thread[i].pktio_dev));
free(args->appl.if_names);
free(args->appl.if_str);
- free(args);
odp_pool_destroy(pool);
+
+ if (odp_shm_free(args->shm)) {
+ ODPH_ERR("Error: shm free global data\n");
+ exit(EXIT_FAILURE);
+ }
+
odp_term_local();
return odp_term_global(instance);
}
@@ -499,7 +527,10 @@ static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
odp_packet_free(pkt); /* Drop */
pkt_cnt--;
} else if (odp_unlikely(i != j++)) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
pkt_tbl[j-1] = pkt;
+#pragma GCC diagnostic pop
}
}
@@ -567,16 +598,12 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
{NULL, 0, NULL, 0}
};
- static const char *shortopts = "+c:i:+m:t:h";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
+ static const char *shortopts = "+c:i:m:t:h";
+ appl_args->cpu_count = 1; /* use one worker by default */
appl_args->mode = APPL_MODE_PKT_SCHED;
appl_args->time = 0; /**< loop forever */
- opterr = 0; /* do not issue errors on helper options */
-
while (1) {
opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
@@ -588,7 +615,7 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
appl_args->cpu_count = atoi(optarg);
break;
case 't':
- appl_args->time = atoi(optarg);
+ appl_args->time = atof(optarg);
break;
/* parse packet-io interface names */
case 'i':
@@ -673,17 +700,7 @@ static void print_info(char *progname, appl_args_t *appl_args)
{
int i;
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %"PRIu64"\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
+ odp_sys_info_print();
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
@@ -710,7 +727,7 @@ static void print_info(char *progname, appl_args_t *appl_args)
}
/**
- * Prinf usage information
+ * Print usage information
*/
static void usage(char *progname)
{
@@ -724,8 +741,8 @@ static void usage(char *progname)
" -i, --interface Eth interfaces (comma-separated, no spaces)\n"
"\n"
"Optional OPTIONS\n"
- " -c, --count <number> CPU count.\n"
- " -t, --time <seconds> Number of seconds to run.\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -t, --time <seconds> Number of seconds to run (e.g. 0.1).\n"
" -m, --mode 0: Receive and send directly (no queues)\n"
" 1: Receive and send via queues.\n"
" 2: Receive via scheduler, send via queues.\n"
diff --git a/example/packet/packet_dump_run.sh b/example/packet/packet_dump_run.sh
new file mode 100755
index 000000000..fea5d8b3c
--- /dev/null
+++ b/example/packet/packet_dump_run.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_packet_dump${EXEEXT} -i $IF0 -n 10 -o 0 -l 64
+STATUS=$?
+if [ "$STATUS" -ne 0 ]; then
+ echo "Error: status was: $STATUS, expected 0"
+ exit 1
+fi
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/packet/pktio_run.sh b/example/packet/pktio_run.sh
index 3adb2d62e..f08c45936 100755
--- a/example/packet/pktio_run.sh
+++ b/example/packet/pktio_run.sh
@@ -1,62 +1,68 @@
#!/bin/bash
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
-PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
-PCAP_OUT="pcapout.pcap"
-PCAP_IN_SIZE=`stat -c %s ${PCAP_IN}`
-echo "using PCAP in=${PCAP_IN}:out=${PCAP_OUT} size %${PCAP_IN_SIZE}"
+TEST_TIME=0.1
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
# burst mode
-./odp_pktio -ipcap:in=${PCAP_IN}:out=${PCAP_OUT} -t 5 -m 0
+./odp_pktio${EXEEXT} -i $IF1 -t $TEST_TIME -m 0
STATUS=$?
-PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
-rm -f ${PCAP_OUT}
-
-if [ ${STATUS} -ne 0 ] || [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
- echo "Error: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
exit 1
fi
-echo "Pass -m 0: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+
+validate_result
+echo "Pass -m 0: status ${STATUS}"
# queue mode
-./odp_pktio -ipcap:in=${PCAP_IN}:out=${PCAP_OUT} -t 5 -m 1
+./odp_pktio${EXEEXT} -i $IF1 -t $TEST_TIME -m 1
STATUS=$?
-PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
-rm -f ${PCAP_OUT}
-if [ ${STATUS} -ne 0 ] || [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
- echo "Error: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
exit 2
fi
-echo "Pass -m 1: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+
+validate_result
+echo "Pass -m 1: status ${STATUS}"
# sched/queue mode
-./odp_pktio -ipcap:in=${PCAP_IN}:out=${PCAP_OUT} -t 5 -m 2
+./odp_pktio${EXEEXT} -i $IF1 -t $TEST_TIME -m 2
STATUS=$?
-PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
-rm -f ${PCAP_OUT}
-if [ ${STATUS} -ne 0 ] || [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
- echo "Error: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
exit 3
fi
-echo "Pass -m 2: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+
+validate_result
+echo "Pass -m 2: status ${STATUS}"
# cpu number option test 1
-./odp_pktio -ipcap:in=${PCAP_IN}:out=${PCAP_OUT} -t 5 -m 0 -c 1
+./odp_pktio${EXEEXT} -i $IF1 -t $TEST_TIME -m 0 -c 1
STATUS=$?
-PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
-rm -f ${PCAP_OUT}
-if [ ${STATUS} -ne 0 ] || [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
- echo "Error: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
exit 4
fi
-echo "Pass -m 0 -c 1: status ${STATUS}, in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+
+validate_result
+echo "Pass -m 0 -c 1: status ${STATUS}"
+
+cleanup_interfaces
exit 0
diff --git a/example/ping/.gitignore b/example/ping/.gitignore
new file mode 100644
index 000000000..6222c2866
--- /dev/null
+++ b/example/ping/.gitignore
@@ -0,0 +1,5 @@
+odp_ping
+pktio_env
+*.log
+*.trs
+pcapout.pcap
diff --git a/example/ping/Makefile.am b/example/ping/Makefile.am
new file mode 100644
index 000000000..009b0993a
--- /dev/null
+++ b/example/ping/Makefile.am
@@ -0,0 +1,34 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_ping
+
+odp_ping_SOURCES = odp_ping.c
+
+if test_example
+if ODP_PKTIO_PCAP
+TESTS = ping_run.sh
+endif
+endif
+EXTRA_DIST = ping_run.sh icmp_echo_req.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/ping/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/ping/icmp_echo_req.pcap b/example/ping/icmp_echo_req.pcap
new file mode 100644
index 000000000..1164a11c9
--- /dev/null
+++ b/example/ping/icmp_echo_req.pcap
Binary files differ
diff --git a/example/ping/odp_ping.c b/example/ping/odp_ping.c
new file mode 100644
index 000000000..97b856895
--- /dev/null
+++ b/example/ping/odp_ping.c
@@ -0,0 +1,766 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+/**
+ * @example odp_ping.c
+ *
+ * This application replies to IPv4 ping requests. It can be used to test
+ * connectivity with standard ping utility. ARP table needs to be setup manually
+ * on the sender side as the application does not reply to ARP requests.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_PKTIOS 32
+#define MAX_PKTIO_NAME 255
+#define MAX_PKT_NUM 1024
+
+ODP_STATIC_ASSERT(MAX_PKTIOS < UINT8_MAX, "MAX_PKTIOS too large for index lookup");
+
+typedef struct test_options_t {
+ uint64_t num_packet;
+ uint32_t timeout;
+ int promisc;
+ int verbose;
+ int num_pktio;
+ char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
+
+} test_options_t;
+
+typedef struct test_global_t {
+ test_options_t opt;
+ uint64_t rx_packets;
+ uint64_t tx_replies;
+ odp_pool_t pool;
+ odp_atomic_u32_t stop;
+
+ struct {
+ odph_ethaddr_t eth_addr;
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout;
+ int started;
+
+ } pktio[MAX_PKTIOS];
+
+ /* Pktio index lookup table */
+ uint8_t pktio_from_idx[ODP_PKTIO_MAX_INDEX + 1];
+
+} test_global_t;
+
+static test_global_t test_global;
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ odp_atomic_store_u32(&test_global.stop, 1);
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "ODP ping example. Replies to ICMPv4 ping requests.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -i, --interface <name> Packet IO interfaces (comma-separated, no spaces)\n"
+ " -n, --num_packet <number> Exit after this many packets. Use 0 to run infinitely. Default 0.\n"
+ " -t, --timeout <sec> Exit after this many seconds. Use 0 to run infinitely. Default 0.\n"
+ " -p, --promisc Set interface into promiscuous mode.\n"
+ " -v, --verbose Print extra packet information.\n"
+ " -h, --help Display help and exit.\n\n");
+}
+
+static int parse_options(int argc, char *argv[], test_global_t *global)
+{
+ int i, opt, long_index;
+ char *name, *str;
+ int len, str_len;
+
+ const struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'},
+ {"num_packet", required_argument, NULL, 'n'},
+ {"timeout", required_argument, NULL, 't'},
+ {"promisc", no_argument, NULL, 'p'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *shortopts = "+i:n:t:pvh";
+ int ret = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'i':
+ i = 0;
+ str = optarg;
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ len = strcspn(str, ",");
+ str_len -= len + 1;
+
+ if (i == MAX_PKTIOS) {
+ ODPH_ERR("Too many interfaces\n");
+ ret = -1;
+ break;
+ }
+
+ if (len > MAX_PKTIO_NAME) {
+ ODPH_ERR("Too long interface name: %s\n", str);
+ ret = -1;
+ break;
+ }
+
+ name = global->opt.pktio_name[i];
+ memcpy(name, str, len);
+ str += len + 1;
+ i++;
+ }
+
+ global->opt.num_pktio = i;
+
+ break;
+ case 'n':
+ global->opt.num_packet = atoll(optarg);
+ break;
+ case 't':
+ global->opt.timeout = atoi(optarg);
+ break;
+ case 'p':
+ global->opt.promisc = 1;
+ break;
+ case 'v':
+ global->opt.verbose = 1;
+ break;
+ case 'h':
+ default:
+ print_usage();
+ return -1;
+ }
+ }
+
+ if (global->opt.num_pktio == 0) {
+ ODPH_ERR("At least one pktio interface needed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int open_pktios(test_global_t *global)
+{
+ odp_pool_param_t pool_param;
+ odp_pktio_param_t pktio_param;
+ odp_pool_t pool;
+ odp_pool_capability_t pool_capa;
+ odp_pktio_capability_t pktio_capa;
+ odp_pktio_t pktio;
+ odp_pktio_config_t pktio_config;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktout_queue_t pktout;
+ char *name;
+ int i, num_pktio;
+ uint32_t num_pkt = MAX_PKT_NUM;
+
+ num_pktio = global->opt.num_pktio;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_num < MAX_PKT_NUM)
+ num_pkt = pool_capa.pkt.max_num;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = num_pkt;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet pool", &pool_param);
+
+ global->pool = pool;
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ for (i = 0; i < num_pktio; i++)
+ global->pktio[i].pktio = ODP_PKTIO_INVALID;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_pktio; i++) {
+ name = global->opt.pktio_name[i];
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Pktio open failed: %s\n", name);
+ return -1;
+ }
+
+ global->pktio[i].pktio = pktio;
+
+ if (odp_pktio_capability(pktio, &pktio_capa)) {
+ ODPH_ERR("Packet IO capability failed\n");
+ return -1;
+ }
+
+ if (odp_pktio_mac_addr(pktio,
+ &global->pktio[i].eth_addr.addr,
+ ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("MAC address read failed: %s\n", name);
+ return -1;
+ }
+
+ odp_pktio_config_init(&pktio_config);
+ pktio_config.pktin.bit.ts_all = 1;
+ pktio_config.parser.layer = ODP_PROTO_LAYER_ALL;
+
+ odp_pktio_config(pktio, &pktio_config);
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ pktin_param.num_queues = 1;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Pktin config failed: %s\n", name);
+ return -1;
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.num_queues = 1;
+
+ if (odp_pktout_queue_config(pktio, &pktout_param)) {
+ ODPH_ERR("Pktout config failed: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
+ ODPH_ERR("Pktout queue request failed: %s\n", name);
+ return -1;
+ }
+
+ global->pktio[i].pktout = pktout;
+
+ if (global->opt.promisc && odp_pktio_promisc_mode(pktio) != 1) {
+ if (pktio_capa.set_op.op.promisc_mode == 0) {
+ ODPH_ERR("Promiscuous mode cannot be set: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktio_promisc_mode_set(pktio, 1)) {
+ ODPH_ERR("Promiscuous mode set failed: %s\n", name);
+ return -1;
+ }
+ }
+
+ odp_pktio_print(pktio);
+ }
+
+ return 0;
+}
+
+static int init_pktio_lookup_tbl(test_global_t *global)
+{
+ for (int i = 0; i < global->opt.num_pktio; i++) {
+ odp_pktio_t pktio = global->pktio[i].pktio;
+ int pktio_idx = odp_pktio_index(pktio);
+
+ if (pktio_idx < 0) {
+ ODPH_ERR("odp_pktio_index() failed: %s\n", global->opt.pktio_name[i]);
+ return -1;
+ }
+
+ global->pktio_from_idx[pktio_idx] = i;
+ }
+ return 0;
+}
+
+static int start_pktios(test_global_t *global)
+{
+ int i;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ if (odp_pktio_start(global->pktio[i].pktio)) {
+ ODPH_ERR("Pktio start failed: %s\n", global->opt.pktio_name[i]);
+ return -1;
+ }
+
+ global->pktio[i].started = 1;
+ }
+
+ return 0;
+}
+
+static int stop_pktios(test_global_t *global)
+{
+ odp_pktio_t pktio;
+ int i, ret = 0;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID || global->pktio[i].started == 0)
+ continue;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Pktio stop failed: %s\n", global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static void empty_queues(void)
+{
+ odp_event_t ev;
+ uint64_t wait_time = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS / 2);
+
+ /* Drop all events from all queues */
+ while (1) {
+ ev = odp_schedule(NULL, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+}
+
+static int close_pktios(test_global_t *global)
+{
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ int i, ret = 0;
+
+ for (i = 0; i < global->opt.num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Pktio close failed: %s\n", global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ pool = global->pool;
+
+ if (pool == ODP_POOL_INVALID)
+ return ret;
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void print_mac_addr(uint8_t *addr)
+{
+ printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void print_ipv4_addr(uint8_t *addr)
+{
+ printf("%u.%u.%u.%u\n",
+ addr[0], addr[1], addr[2], addr[3]);
+}
+
+static void print_data(odp_packet_t pkt, uint32_t offset, uint32_t len)
+{
+ const uint32_t bytes_per_row = 16;
+ const uint32_t num_char = 1 + (bytes_per_row * 3) + 1;
+ uint8_t data[bytes_per_row];
+ char row[num_char];
+ uint32_t copy_len, i, j;
+ uint32_t data_len = odp_packet_len(pkt);
+
+ if (offset > data_len)
+ return;
+
+ if (offset + len > data_len)
+ len = data_len - offset;
+
+ while (len) {
+ i = 0;
+
+ if (len > bytes_per_row)
+ copy_len = bytes_per_row;
+ else
+ copy_len = len;
+
+ odp_packet_copy_to_mem(pkt, offset, copy_len, data);
+
+ i += snprintf(&row[i], num_char - i, " ");
+
+ for (j = 0; j < copy_len; j++)
+ i += snprintf(&row[i], num_char - i, " %02x", data[j]);
+
+ row[i] = 0;
+ printf("%s\n", row);
+
+ len -= copy_len;
+ offset += copy_len;
+ }
+}
+
+static void print_packet(odp_packet_t pkt, uint64_t num_packet)
+{
+ odp_pktio_t pktio;
+ odp_pktio_info_t pktio_info;
+ odp_time_t time;
+ uint64_t sec, nsec;
+ uint32_t offset;
+ uint8_t *data = odp_packet_data(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint32_t l2_offset = odp_packet_l2_offset(pkt);
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+ uint32_t l4_offset = odp_packet_l4_offset(pkt);
+ uint32_t data_len = odp_packet_len(pkt);
+ int icmp = odp_packet_has_icmp(pkt);
+ int ipv4 = odp_packet_has_ipv4(pkt);
+
+ if (odp_packet_has_ts(pkt))
+ time = odp_packet_ts(pkt);
+ else
+ time = odp_time_local();
+
+ nsec = odp_time_to_ns(time);
+ sec = nsec / ODP_TIME_SEC_IN_NS;
+ nsec = nsec - (sec * ODP_TIME_SEC_IN_NS);
+ pktio = odp_packet_input(pkt);
+
+ printf("PACKET [%" PRIu64 "]\n", num_packet);
+ printf(" time: %" PRIu64 ".%09" PRIu64 " sec\n", sec, nsec);
+ if (odp_pktio_info(pktio, &pktio_info) == 0)
+ printf(" interface name: %s\n", pktio_info.name);
+ else
+ printf(" interface name: n/a\n");
+ printf(" packet length: %u bytes\n", odp_packet_len(pkt));
+
+ /* L2 */
+ if (odp_packet_has_eth(pkt)) {
+ printf(" Ethernet offset: %u bytes\n", l2_offset);
+ offset = l2_offset;
+ if (offset + 6 <= seg_len) {
+ printf(" dst address: ");
+ print_mac_addr(data + offset);
+ }
+
+ offset = l2_offset + 6;
+ if (offset + 6 <= seg_len) {
+ printf(" src address: ");
+ print_mac_addr(data + offset);
+ }
+ } else if (odp_packet_has_l2(pkt)) {
+ printf(" L2 (%i) offset: %u bytes\n",
+ odp_packet_l2_type(pkt), l2_offset);
+ }
+
+ /* L3 */
+ if (ipv4) {
+ printf(" IPv4 offset: %u bytes\n", l3_offset);
+ offset = l3_offset + 12;
+ if (offset + 4 <= seg_len) {
+ printf(" src address: ");
+ print_ipv4_addr(data + offset);
+ }
+
+ offset = l3_offset + 16;
+ if (offset + 4 <= seg_len) {
+ printf(" dst address: ");
+ print_ipv4_addr(data + offset);
+ }
+ } else if (odp_packet_has_ipv6(pkt)) {
+ printf(" IPv6 offset: %u bytes\n", l3_offset);
+ } else if (odp_packet_has_l3(pkt)) {
+ printf(" L3 (%i) offset: %u bytes\n",
+ odp_packet_l3_type(pkt), l3_offset);
+ }
+
+ /* L4 */
+ if (icmp) {
+ printf(" ICMP offset: %u bytes\n", l4_offset);
+ if (ipv4) {
+ uint32_t len;
+ uint8_t *u8 = odp_packet_l4_ptr(pkt, &len);
+
+ if (u8 && len >= 2) {
+ printf(" type: %u\n", u8[0]);
+ printf(" code: %u\n", u8[1]);
+ }
+ }
+ } else if (odp_packet_has_l4(pkt)) {
+ printf(" L4 (%i) offset: %u bytes\n",
+ odp_packet_l4_type(pkt), l4_offset);
+ }
+
+ print_data(pkt, 0, data_len);
+
+ printf("\n");
+}
+
+/* Updated checksum when a 16 bit word has been changed from old to new */
+static uint16_t update_chksum(uint16_t chksum, uint16_t old, uint16_t new)
+{
+ uint16_t chksum_comp = ~chksum;
+ uint16_t old_comp = ~old;
+ uint32_t sum = chksum_comp + old_comp + new;
+
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+static void icmp_reply(test_global_t *global, odp_packet_t pkt)
+{
+ uint32_t dst_ip;
+ odph_ipv4hdr_t *ip_hdr;
+ odph_ethhdr_t *eth_hdr;
+ uint16_t old, new;
+ uint32_t len = 0;
+ int index = global->pktio_from_idx[odp_packet_input_index(pkt)];
+ odp_pktout_queue_t pktout = global->pktio[index].pktout;
+ odph_ethaddr_t *eth_addr = &global->pktio[index].eth_addr;
+ int icmp = odp_packet_has_icmp(pkt);
+ int ipv4 = odp_packet_has_ipv4(pkt);
+ int eth = odp_packet_has_eth(pkt);
+ odph_icmphdr_t *icmp_hdr = odp_packet_l4_ptr(pkt, &len);
+
+ if (odp_packet_has_error(pkt))
+ goto error;
+
+ if (eth == 0 || ipv4 == 0 || icmp == 0)
+ goto error;
+
+ /* ICMP type, code and chksum fields are located in the first 4 bytes */
+ if (icmp_hdr == NULL || len < 4)
+ goto error;
+
+ if (icmp_hdr->type != ODPH_ICMP_ECHO || icmp_hdr->code != 0)
+ goto error;
+
+ /* Echo reply */
+ old = *(uint16_t *)(uintptr_t)icmp_hdr;
+ icmp_hdr->type = ODPH_ICMP_ECHOREPLY;
+ new = *(uint16_t *)(uintptr_t)icmp_hdr;
+ icmp_hdr->chksum = update_chksum(icmp_hdr->chksum, old, new);
+
+ /* Swap IP addresses */
+ ip_hdr = odp_packet_l3_ptr(pkt, &len);
+ if (ip_hdr == NULL || len < 20)
+ goto error;
+
+ dst_ip = ip_hdr->dst_addr;
+ ip_hdr->dst_addr = ip_hdr->src_addr;
+ ip_hdr->src_addr = dst_ip;
+
+ /* Swap Ethernet addresses */
+ eth_hdr = odp_packet_l2_ptr(pkt, &len);
+ if (eth_hdr == NULL || len < 14)
+ goto error;
+
+ eth_hdr->dst = eth_hdr->src;
+ eth_hdr->src = *eth_addr;
+
+ if (odp_pktout_send(pktout, &pkt, 1) != 1)
+ goto error;
+
+ global->tx_replies++;
+ return;
+
+error:
+ odp_packet_free(pkt);
+}
+
+static void print_stat(test_global_t *global, uint64_t rx_packets,
+ uint64_t diff_ns)
+{
+ uint64_t prev = global->rx_packets;
+ double per_sec = 1000000000.0 * (rx_packets - prev) / diff_ns;
+
+ printf("Received %" PRIu64 " packets (%.1f / sec). "
+ "Sent %" PRIu64 " replies.\n",
+ rx_packets, per_sec, global->tx_replies);
+
+ global->rx_packets = rx_packets;
+}
+
+static int receive_packets(test_global_t *global)
+{
+ odp_event_t ev;
+ odp_packet_t pkt;
+ uint64_t diff_ns;
+ int print = 0;
+ uint64_t num_packet = 0;
+ uint64_t timeout_ns = global->opt.timeout * ODP_TIME_SEC_IN_NS;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_time_t start = odp_time_local();
+ odp_time_t cur = start;
+ odp_time_t prev = start;
+
+ while (!odp_atomic_load_u32(&global->stop)) {
+ ev = odp_schedule(NULL, wait);
+
+ cur = odp_time_local();
+ diff_ns = odp_time_diff_ns(cur, prev);
+ if (diff_ns >= ODP_TIME_SEC_IN_NS) {
+ prev = cur;
+ print = 1;
+ }
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (print) {
+ print_stat(global, num_packet, diff_ns);
+ print = 0;
+ }
+
+ if (timeout_ns) {
+ if (odp_time_diff_ns(cur, start) >= timeout_ns)
+ break;
+ }
+
+ continue;
+ }
+
+ if (odp_event_type(ev) != ODP_EVENT_PACKET) {
+ printf("Bad event type: %i\n", odp_event_type(ev));
+ odp_event_free(ev);
+ continue;
+ }
+
+ pkt = odp_packet_from_event(ev);
+
+ if (global->opt.verbose)
+ print_packet(pkt, num_packet);
+
+ /* Reply or drop packet */
+ icmp_reply(global, pkt);
+
+ num_packet++;
+ if (print) {
+ print_stat(global, num_packet, diff_ns);
+ print = 0;
+ }
+
+ if (global->opt.num_packet && num_packet >= global->opt.num_packet)
+ break;
+ }
+
+ /* Timeout before num packets received */
+ if (global->opt.num_packet && num_packet < global->opt.num_packet) {
+ ODPH_ERR("Received %" PRIu64 "/%" PRIu64 " packets\n",
+ num_packet, global->opt.num_packet);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ test_global_t *global;
+ int ret = 0;
+
+ global = &test_global;
+ memset(global, 0, sizeof(test_global_t));
+ odp_atomic_init_u32(&global->stop, 0);
+
+ signal(SIGINT, sig_handler);
+
+ if (parse_options(argc, argv, global))
+ return -1;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, NULL, NULL)) {
+ ODPH_ERR("Global init failed\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed\n");
+ return -1;
+ }
+
+ global->pool = ODP_POOL_INVALID;
+
+ odp_schedule_config(NULL);
+
+ odp_sys_info_print();
+
+ if (open_pktios(global)) {
+ ODPH_ERR("Pktio open failed\n");
+ return -1;
+ }
+
+ if (init_pktio_lookup_tbl(global)) {
+ ODPH_ERR("Mapping pktio indexes failed\n");
+ return -1;
+ }
+
+ if (start_pktios(global)) {
+ ODPH_ERR("Pktio start failed\n");
+ return -1;
+ }
+
+ if (receive_packets(global)) {
+ ret = -1;
+ }
+
+ if (stop_pktios(global)) {
+ ODPH_ERR("Pktio stop failed\n");
+ return -1;
+ }
+
+ empty_queues();
+
+ if (close_pktios(global)) {
+ ODPH_ERR("Pktio close failed\n");
+ return -1;
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Term local failed\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Term global failed\n");
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/example/ping/ping_run.sh b/example/ping/ping_run.sh
new file mode 100755
index 000000000..9598db70a
--- /dev/null
+++ b/example/ping/ping_run.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2019 Nokia
+#
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# Ping test with 100 ICMP echo request packets. Timeout 5 sec.
+# Promiscuous and verbose mode enabled.
+./odp_ping${EXEEXT} -v -p -t 5 -n 100 -i $IF0
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+echo "Pass: status ${STATUS}"
+
+exit 0
diff --git a/example/simple_pipeline/.gitignore b/example/simple_pipeline/.gitignore
new file mode 100644
index 000000000..28cb24d41
--- /dev/null
+++ b/example/simple_pipeline/.gitignore
@@ -0,0 +1,4 @@
+odp_simple_pipeline
+pktio_env
+*.log
+*.trs
diff --git a/example/simple_pipeline/Makefile.am b/example/simple_pipeline/Makefile.am
new file mode 100644
index 000000000..f258434aa
--- /dev/null
+++ b/example/simple_pipeline/Makefile.am
@@ -0,0 +1,34 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_simple_pipeline
+
+odp_simple_pipeline_SOURCES = odp_simple_pipeline.c
+
+if test_example
+if ODP_PKTIO_PCAP
+TESTS = simple_pipeline_run.sh
+endif
+endif
+EXTRA_DIST = simple_pipeline_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/simple_pipeline/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/simple_pipeline/odp_simple_pipeline.c b/example/simple_pipeline/odp_simple_pipeline.c
new file mode 100644
index 000000000..3a05bf1c5
--- /dev/null
+++ b/example/simple_pipeline/odp_simple_pipeline.c
@@ -0,0 +1,965 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+ /**
+ * @example odp_simple_pipeline.c
+ *
+ * Simple pipeline example application which receives packets from one
+ * interface and passes them through 0-N worker stages before outputting them
+ * from a second network interface. The RX, worker, and TX stages are connected
+ * using plain queues and each stage is run on a separate CPU thread.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define POOL_PKT_NUM 8192
+#define POOL_PKT_LEN 1536
+#define MAX_PKT_BURST 32
+/* Three threads required for RX, TX and statistics */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 3)
+#define QUEUE_SIZE 1024
+#define MAX_PKTIOS 2
+#define DUMMY_HASH 1234567890
+
+/* Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+/* Statistics */
+typedef union ODP_ALIGNED_CACHE {
+ struct {
+ uint64_t pps; /* Packet per second */
+ uint64_t rx_cnt; /* RX packets */
+ uint64_t tx_cnt; /* TX packets */
+ uint64_t rx_drops; /* Dropped packets on RX */
+ uint64_t tx_drops; /* Dropped packets on TX */
+ } s;
+ uint8_t padding[ODP_CACHE_LINE_SIZE];
+} stats_t;
+
+/* Thread specific data */
+typedef struct thread_args_t {
+ odp_queue_t rx_queue;
+ odp_queue_t tx_queue;
+ stats_t stats;
+} thread_args_t;
+
+/* Parsed command line application arguments */
+typedef struct {
+ char **if_names; /* Array of pointers to interface names */
+ odph_ethaddr_t dst_addr; /* Destination MAC address */
+ int accuracy; /* Statistics print interval in seconds */
+ int extra_work; /* Add extra processing to worker stage */
+ int dst_change; /* Change destination eth address */
+ int src_change; /* Change source eth address */
+ int dst_set; /* Custom destination eth address given */
+ int time; /* Time in seconds to run. */
+ int num_workers; /* Number of pipeline worker stages */
+ char *if_str; /* Storage for interface names */
+} appl_args_t;
+
+/* Global application data */
+typedef struct {
+ odp_queue_t queue[ODP_THREAD_COUNT_MAX];
+ /* Thread specific arguments */
+ thread_args_t thread[ODP_THREAD_COUNT_MAX];
+ /* Barriers to synchronize main and workers */
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ /* Pktio interfaces */
+ odp_pktio_t if0, if1;
+ odp_pktin_queue_t if0in, if1in;
+ odp_pktout_queue_t if0out, if1out;
+ odph_ethaddr_t src_addr; /* Source MAC address */
+ odph_ethaddr_t dst_addr; /* Destination MAC address */
+ odp_atomic_u32_t exit_threads;
+ /* Application (parsed) arguments */
+ appl_args_t appl;
+} global_data_t;
+
+static global_data_t *global;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ odp_atomic_store_u32(&global->exit_threads, 1);
+}
+
+static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
+ odp_pktin_queue_t *pktin,
+ odp_pktout_queue_t *pktout)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t in_param;
+ odp_pktout_queue_param_t out_param;
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ printf("Error: failed to open %s\n", name);
+ exit(1);
+ }
+
+ odp_pktio_config_init(&config);
+ config.parser.layer = ODP_PROTO_LAYER_L2;
+ odp_pktio_config(pktio, &config);
+
+ odp_pktin_queue_param_init(&in_param);
+ odp_pktout_queue_param_init(&out_param);
+
+ in_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+ if (odp_pktin_queue_config(pktio, &in_param)) {
+ printf("Error: failed to config input queue for %s\n", name);
+ exit(1);
+ }
+
+ out_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+ if (odp_pktout_queue_config(pktio, &out_param)) {
+ printf("Error: failed to config output queue for %s\n", name);
+ exit(1);
+ }
+
+ if (odp_pktin_queue(pktio, pktin, 1) != 1) {
+ printf("Error: pktin queue query failed for %s\n", name);
+ exit(1);
+ }
+ if (odp_pktout_queue(pktio, pktout, 1) != 1) {
+ printf("Error: pktout queue query failed for %s\n", name);
+ exit(1);
+ }
+ return pktio;
+}
+
+/*
+ * Fill packets' eth addresses and convert packets to events
+ *
+ * pkt_tbl Array of packets
+ * event_tbl[out] Array of events
+ * num Number of packets in the array
+ */
+static inline unsigned int prep_events(odp_packet_t pkt_tbl[],
+ odp_event_t event_tbl[],
+ unsigned int num)
+{
+ unsigned int i;
+ unsigned int events = 0;
+
+ if (!global->appl.dst_change && !global->appl.src_change) {
+ odp_packet_to_event_multi(pkt_tbl, event_tbl, num);
+ return num;
+ }
+
+ for (i = 0; i < num; ++i) {
+ odp_packet_t pkt = pkt_tbl[i];
+ odph_ethhdr_t *eth;
+
+ odp_packet_prefetch(pkt, 0, ODPH_ETHHDR_LEN);
+
+ if (odp_unlikely(!odp_packet_has_eth(pkt))) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ eth = odp_packet_data(pkt);
+
+ if (global->appl.src_change)
+ eth->src = global->src_addr;
+
+ if (global->appl.dst_change)
+ eth->dst = global->dst_addr;
+
+ event_tbl[events++] = odp_packet_to_event(pkt);
+ }
+ return events;
+}
+
+static inline int rx_thread(void *arg)
+{
+ thread_args_t *thr_args = arg;
+ odp_event_t event_tbl[MAX_PKT_BURST];
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ odp_pktin_queue_t pktin_queue = global->if0in;
+ odp_queue_t out_queue = thr_args->tx_queue;
+ stats_t *stats = &thr_args->stats;
+ int pkts, events, sent, drops;
+
+ odp_barrier_wait(&global->init_barrier);
+
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ pkts = odp_pktin_recv(pktin_queue, pkt_tbl, MAX_PKT_BURST);
+ if (odp_unlikely(pkts <= 0))
+ continue;
+
+ stats->s.rx_cnt += pkts;
+
+ events = prep_events(pkt_tbl, event_tbl, pkts);
+ drops = events - pkts;
+ if (odp_unlikely(drops))
+ stats->s.rx_drops += pkts - events;
+
+ sent = odp_queue_enq_multi(out_queue, event_tbl, events);
+ if (odp_unlikely(sent < 0))
+ sent = 0;
+
+ stats->s.tx_cnt += sent;
+
+ drops = events - sent;
+ if (odp_unlikely(drops)) {
+ stats->s.tx_drops += drops;
+ odp_packet_free_multi(&pkt_tbl[sent], drops);
+ }
+ }
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&global->term_barrier);
+
+ return 0;
+}
+
+static inline int tx_thread(void *arg)
+{
+ thread_args_t *thr_args = arg;
+ odp_event_t event_tbl[MAX_PKT_BURST];
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ odp_queue_t rx_queue = thr_args->rx_queue;
+ odp_pktout_queue_t pktout_queue = global->if1out;
+ stats_t *stats = &thr_args->stats;
+ int events, sent, tx_drops;
+
+ odp_barrier_wait(&global->init_barrier);
+
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ events = odp_queue_deq_multi(rx_queue, event_tbl,
+ MAX_PKT_BURST);
+ if (odp_unlikely(events <= 0))
+ continue;
+
+ stats->s.rx_cnt += events;
+
+ odp_packet_from_event_multi(pkt_tbl, event_tbl, events);
+
+ sent = odp_pktout_send(pktout_queue, pkt_tbl, events);
+ if (odp_unlikely(sent < 0))
+ sent = 0;
+
+ stats->s.tx_cnt += sent;
+
+ tx_drops = events - sent;
+ if (odp_unlikely(tx_drops)) {
+ stats->s.tx_drops += tx_drops;
+ odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
+ }
+ }
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&global->term_barrier);
+
+ /* Empty queue before exiting */
+ events = 1;
+ while (events > 0) {
+ events = odp_queue_deq_multi(rx_queue, event_tbl,
+ MAX_PKT_BURST);
+
+ if (events > 0)
+ odp_event_free_multi(event_tbl, events);
+ }
+
+ return 0;
+}
+
+/*
+ * Work on packets
+ */
+static inline void work_on_events(odp_event_t event_tbl[], unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ odp_packet_t pkt = odp_packet_from_event(event_tbl[i]);
+
+ if (odp_hash_crc32c(odp_packet_data(pkt),
+ odp_packet_seg_len(pkt), 123) == DUMMY_HASH)
+ printf("Dummy hash match\n");
+ }
+}
+
+static inline int worker_thread(void *arg ODP_UNUSED)
+{
+ thread_args_t *thr_args = arg;
+ odp_event_t event_tbl[MAX_PKT_BURST];
+ stats_t *stats = &thr_args->stats;
+ odp_queue_t rx_queue = thr_args->rx_queue;
+ odp_queue_t tx_queue = thr_args->tx_queue;
+ int events, sent, tx_drops;
+ int extra_work = global->appl.extra_work;
+
+ odp_barrier_wait(&global->init_barrier);
+
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ events = odp_queue_deq_multi(rx_queue, event_tbl,
+ MAX_PKT_BURST);
+
+ if (odp_unlikely(events <= 0))
+ continue;
+
+ stats->s.rx_cnt += events;
+
+ if (extra_work)
+ work_on_events(event_tbl, events);
+
+ sent = odp_queue_enq_multi(tx_queue, event_tbl, events);
+ if (odp_unlikely(sent < 0))
+ sent = 0;
+
+ stats->s.tx_cnt += sent;
+
+ tx_drops = events - sent;
+ if (odp_unlikely(tx_drops)) {
+ stats->s.tx_drops += tx_drops;
+ odp_event_free_multi(&event_tbl[sent], tx_drops);
+ }
+ }
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&global->term_barrier);
+
+ /* Empty queue before exiting */
+ events = 1;
+ while (events > 0) {
+ events = odp_queue_deq_multi(rx_queue, event_tbl,
+ MAX_PKT_BURST);
+
+ if (events > 0)
+ odp_event_free_multi(event_tbl, events);
+ }
+
+ return 0;
+}
+
+static int setup_thread_masks(odp_cpumask_t *thr_mask_rx,
+ odp_cpumask_t *thr_mask_tx,
+ odp_cpumask_t *thr_mask_workers,
+ int num_workers)
+{
+ odp_cpumask_t cpumask;
+ int num_threads = 0;
+ int i, cpu;
+
+ if (num_workers > MAX_WORKERS) {
+ printf("Worker count limited to MAX_WORKERS define (=%d)\n",
+ MAX_WORKERS);
+ num_workers = MAX_WORKERS;
+ }
+
+ /* Two threads required for RX and TX*/
+ num_threads = num_workers + 2;
+
+ num_workers = odp_cpumask_default_worker(&cpumask, num_threads);
+ if (num_workers != num_threads) {
+ printf("Error: Not enough available CPU cores: %d/%d\n",
+ num_workers, num_threads);
+ exit(1);
+ }
+
+ odp_cpumask_zero(thr_mask_rx);
+ odp_cpumask_zero(thr_mask_tx);
+ odp_cpumask_zero(thr_mask_workers);
+
+ cpu = odp_cpumask_first(&cpumask);
+ for (i = 0; i < num_threads; i++) {
+ if (i == 0)
+ odp_cpumask_set(thr_mask_rx, cpu);
+ else if (i == 1)
+ odp_cpumask_set(thr_mask_tx, cpu);
+ else
+ odp_cpumask_set(thr_mask_workers, cpu);
+ cpu = odp_cpumask_next(&cpumask, cpu);
+ }
+
+ return num_threads;
+}
+
+/*
+ * Print statistics
+ *
+ * num_workers Number of worker threads
+ * thr_stats Pointers to stats storage
+ * duration Number of seconds to loop in
+ * timeout Number of seconds for stats calculation
+ */
+static int print_speed_stats(int num_workers, stats_t **thr_stats,
+ int duration, int timeout)
+{
+ uint64_t total_pkts = 0;
+ uint64_t pkts_prev = 0;
+ uint64_t maximum_pps = 0;
+ stats_t thr_stats_prev[num_workers];
+ int i;
+ int elapsed = 0;
+ int stats_enabled = 1;
+ int loop_forever = (duration == 0);
+
+ memset(thr_stats_prev, 0, sizeof(thr_stats_prev));
+
+ if (timeout <= 0) {
+ stats_enabled = 0;
+ timeout = 1;
+ }
+
+ /* Wait for all threads to be ready*/
+ odp_barrier_wait(&global->init_barrier);
+
+ do {
+ uint64_t total_rx_drops = 0;
+ uint64_t total_tx_drops = 0;
+ uint64_t pps;
+
+ sleep(timeout);
+
+ for (i = 0; i < num_workers; i++) {
+ uint64_t rx_cnt = thr_stats[i]->s.rx_cnt;
+ uint64_t tx_cnt = thr_stats[i]->s.tx_cnt;
+ uint64_t rx_drops = thr_stats[i]->s.rx_drops;
+ uint64_t tx_drops = thr_stats[i]->s.tx_drops;
+
+ /* Count only transmitted packets */
+ if (i == (num_workers - 1))
+ total_pkts = tx_cnt;
+
+ total_rx_drops += rx_drops;
+ total_tx_drops += tx_drops;
+
+ pps = (tx_cnt - thr_stats_prev[i].s.tx_cnt) / timeout;
+ thr_stats_prev[i].s.pps = pps;
+ thr_stats_prev[i].s.rx_cnt = rx_cnt;
+ thr_stats_prev[i].s.tx_cnt = tx_cnt;
+ thr_stats_prev[i].s.rx_drops = rx_drops;
+ thr_stats_prev[i].s.tx_drops = tx_drops;
+ }
+ if (stats_enabled) {
+ printf("----------------------------------------\n");
+ for (i = 0; i < num_workers; i++) {
+ if (i == 0)
+ printf("RX thread: ");
+ else if (i == (num_workers - 1))
+ printf("TX thread: ");
+ else
+ printf("Worker %d: ", i - 1);
+
+ printf("%" PRIu64 " pps, "
+ "%" PRIu64 " rx drops, "
+ "%" PRIu64 " tx drops\n",
+ thr_stats_prev[i].s.pps,
+ thr_stats_prev[i].s.rx_drops,
+ thr_stats_prev[i].s.tx_drops);
+ }
+ pps = (total_pkts - pkts_prev) / timeout;
+ if (pps > maximum_pps)
+ maximum_pps = pps;
+ printf("TOTAL: %" PRIu64 " pps, "
+ "%" PRIu64 " rx drops, "
+ "%" PRIu64 " tx drops, "
+ "%" PRIu64 " max pps\n",
+ pps, total_rx_drops, total_tx_drops,
+ maximum_pps);
+
+ pkts_prev = total_pkts;
+ }
+ elapsed += timeout;
+ } while (!odp_atomic_load_u32(&global->exit_threads) && (loop_forever ||
+ (elapsed < duration)));
+
+ if (stats_enabled)
+ printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
+ maximum_pps);
+
+ return total_pkts > 0 ? 0 : -1;
+}
+
+/*
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ odp_sys_info_print();
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "Using IFs: %s %s\n"
+ "Worker stages: %d\n"
+ "Extra work: %d\n\n",
+ progname, appl_args->if_names[0], appl_args->if_names[1],
+ appl_args->num_workers, appl_args->extra_work);
+
+ fflush(NULL);
+}
+
+/*
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane simple pipeline example application.\n"
+ "\n"
+ "Usage: %s [options]\n"
+ "\n"
+ " E.g. %s -i eth0,eth1 -e -w 3\n\n"
+ " ---- ---- ---- ---- ----\n"
+ " | RX | -> | W1 | -> | W2 | -> | W3 | -> | TX |\n"
+ " ---- ---- ---- ---- ----\n\n"
+ " In the above example,\n"
+ " each application stage is executed by a separate CPU thread and the stages\n"
+ " are connected using plain queues. The RX stage receives packets from eth0 and\n"
+ " enqueues them to the first worker stage (W1). The workers stages calculate\n"
+ " CRC-32C over packet data. After the final worker stage (W3) has processed\n"
+ " packets they are enqueued to the TX stage, which transmits the packets out\n"
+ " from interface eth1.\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface <name> Two eth interfaces (comma-separated, no spaces)\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -a, --accuracy <sec> Time in seconds get print statistics\n"
+ " (default is 10 seconds).\n"
+ " -d, --dst_change <arg> 0: Don't change packets' dst eth addresses\n"
+ " 1: Change packets' dst eth addresses (default)\n"
+ " -s, --src_change <arg> 0: Don't change packets' src eth addresses\n"
+ " 1: Change packets' src eth addresses (default)\n"
+ " -r, --dst_addr <addr> Destination address\n"
+ " Requires also the -d flag to be set\n"
+ " -t, --time <sec> Time in seconds to run\n"
+ " -w, --workers <num> Number of worker stages (default 0)\n"
+ " -e, --extra-work Calculate CRC-32C over packet data in worker stage\n"
+ " -h, --help Display help and exit\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
+
+/*
+ * Parse and store the command line arguments
+ *
+ * argc Argument count
+ * argv Argument vector
+ * appl_args[out] Storage for application arguments
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ char *token;
+ size_t len;
+ int opt;
+ int long_index;
+ int i;
+ int if_count = 0;
+ static const struct option longopts[] = {
+ {"accuracy", required_argument, NULL, 'a'},
+ {"extra-work", no_argument, NULL, 'e'},
+ {"dst_addr", required_argument, NULL, 'r'},
+ {"dst_change", required_argument, NULL, 'd'},
+ {"src_change", required_argument, NULL, 's'},
+ {"interface", required_argument, NULL, 'i'},
+ {"time", required_argument, NULL, 't'},
+ {"workers", required_argument, NULL, 'w'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+a:d:er:s:t:i:w:h";
+
+ appl_args->accuracy = 10; /* get and print pps stats second */
+ appl_args->dst_change = 1; /* change eth dst address by default */
+ appl_args->src_change = 1; /* change eth src address by default */
+ appl_args->time = 0; /* loop forever if time to run is 0 */
+ appl_args->extra_work = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'a':
+ appl_args->accuracy = atoi(optarg);
+ break;
+ case 'd':
+ appl_args->dst_change = atoi(optarg);
+ break;
+ case 'e':
+ appl_args->extra_work = 1;
+ break;
+ case 'r':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ if (odph_eth_addr_parse(&appl_args->dst_addr,
+ optarg) != 0) {
+ printf("invalid MAC address\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->dst_set = 1;
+
+ break;
+ case 's':
+ appl_args->src_change = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_str = malloc(len);
+ if (appl_args->if_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ if_count = i;
+
+ if (if_count != 2) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names = calloc(if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+ case 'w':
+ appl_args->num_workers = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (if_count != 2) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+int main(int argc, char **argv)
+{
+ odp_cpumask_t thr_mask_rx;
+ odp_cpumask_t thr_mask_tx;
+ odp_cpumask_t thr_mask_worker;
+ odp_init_t init_param;
+ odp_instance_t instance;
+ odp_pool_t pool;
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+ odp_queue_capability_t queue_capa;
+ odp_queue_param_t queue_param;
+ odp_shm_t shm;
+ odph_helper_options_t helper_options;
+ odph_thread_t thr_tbl[ODP_THREAD_COUNT_MAX];
+ odph_thread_param_t thr_param[ODP_THREAD_COUNT_MAX];
+ odph_thread_common_param_t thr_common;
+ odph_ethaddr_t new_addr;
+ stats_t *stats[ODP_THREAD_COUNT_MAX];
+ thread_args_t *thr_args;
+ uint32_t pkt_len, seg_len, pkt_num;
+ int num_threads, num_workers;
+ int i;
+ int ret;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ printf("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ printf("Error: ODP global init failed.\n");
+ exit(1);
+ }
+
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ printf("Error: ODP local init failed.\n");
+ exit(1);
+ }
+
+ /* Reserve memory for global data */
+ shm = odp_shm_reserve("simple_pipeline", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ printf("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ printf("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(global_data_t));
+ odp_atomic_init_u32(&global->exit_threads, 0);
+
+ signal(SIGINT, sig_handler);
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &global->appl);
+
+ num_threads = setup_thread_masks(&thr_mask_rx, &thr_mask_tx,
+ &thr_mask_worker,
+ global->appl.num_workers);
+ num_workers = num_threads - 2;
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &global->appl);
+
+ /* Create queues for pipeline */
+ if (odp_queue_capability(&queue_capa)) {
+ printf("Error: reading queue capability failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (queue_capa.plain.max_num < (unsigned int)num_threads) {
+ printf("Error: insufficient number of queues supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ queue_param.enq_mode = ODP_QUEUE_OP_MT_UNSAFE;
+ queue_param.deq_mode = ODP_QUEUE_OP_MT_UNSAFE;
+ queue_param.size = QUEUE_SIZE;
+ if (queue_capa.plain.max_size &&
+ queue_param.size > queue_capa.plain.max_size)
+ queue_param.size = queue_capa.plain.max_size;
+ for (i = 0; i < num_threads; i++) {
+ odp_queue_t queue = odp_queue_create("plain_queue",
+ &queue_param);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ printf("Error: queue create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ global->queue[i] = queue;
+ }
+
+ /* Create packet pool */
+ if (odp_pool_capability(&pool_capa)) {
+ printf("Error: reading pool capability failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pkt_len = POOL_PKT_LEN;
+ seg_len = POOL_PKT_LEN;
+ pkt_num = POOL_PKT_NUM;
+
+ if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len)
+ pkt_len = pool_capa.pkt.max_len;
+
+ if (pool_capa.pkt.max_seg_len && seg_len > pool_capa.pkt.max_seg_len)
+ seg_len = pool_capa.pkt.max_seg_len;
+
+ if (pool_capa.pkt.max_num && pkt_num > pool_capa.pkt.max_num)
+ pkt_num = pool_capa.pkt.max_num;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.seg_len = seg_len;
+ pool_param.pkt.len = pkt_len;
+ pool_param.pkt.num = pkt_num;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet pool", &pool_param);
+ if (pool == ODP_POOL_INVALID) {
+ printf("Error: packet pool create failed.\n");
+ exit(1);
+ }
+
+ global->if0 = create_pktio(global->appl.if_names[0], pool,
+ &global->if0in, &global->if0out);
+ global->if1 = create_pktio(global->appl.if_names[1], pool,
+ &global->if1in, &global->if1out);
+
+ /* Save TX interface Ethernet address */
+ if (odp_pktio_mac_addr(global->if1, global->src_addr.addr,
+ ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+ printf("Error: TX interface Ethernet address unknown\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Save destination Ethernet address */
+ if (global->appl.dst_change) {
+ /* 02:00:00:00:00:XX */
+ memset(&new_addr, 0, sizeof(odph_ethaddr_t));
+ if (global->appl.dst_set) {
+ memcpy(&new_addr, &global->appl.dst_addr,
+ sizeof(odph_ethaddr_t));
+ } else {
+ new_addr.addr[0] = 0x02;
+ new_addr.addr[5] = 1;
+ }
+ global->dst_addr = new_addr;
+ }
+
+ if (odp_pktio_start(global->if0)) {
+ printf("Error: unable to start input interface\n");
+ exit(1);
+ }
+ if (odp_pktio_start(global->if1)) {
+ printf("Error: unable to start output interface\n");
+ exit(1);
+ }
+
+ odp_barrier_init(&global->init_barrier, num_threads + 1);
+ odp_barrier_init(&global->term_barrier, num_threads + 1);
+
+ for (i = 0; i < num_threads; i++)
+ stats[i] = &global->thread[i].stats;
+
+ memset(thr_tbl, 0, sizeof(thr_tbl));
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+
+ /* RX thread */
+ thr_args = &global->thread[0];
+ thr_args->tx_queue = global->queue[0];
+ odph_thread_param_init(&thr_param[0]);
+ thr_param[0].start = rx_thread;
+ thr_param[0].arg = thr_args;
+ thr_param[0].thr_type = ODP_THREAD_WORKER;
+ thr_common.cpumask = &thr_mask_rx;
+ odph_thread_create(thr_tbl, &thr_common, thr_param, 1);
+
+ /* Worker threads */
+ for (i = 0; i < num_workers; i++) {
+ thr_args = &global->thread[i + 1];
+ thr_args->rx_queue = global->queue[i];
+ thr_args->tx_queue = global->queue[i + 1];
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = worker_thread;
+ thr_param[i].arg = thr_args;
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ if (num_workers) {
+ thr_common.cpumask = &thr_mask_worker;
+ odph_thread_create(&thr_tbl[1], &thr_common, thr_param,
+ num_workers);
+ }
+
+ /* TX thread */
+ thr_args = &global->thread[num_threads - 1];
+ thr_args->rx_queue = global->queue[num_workers];
+ odph_thread_param_init(&thr_param[0]);
+ thr_param[0].start = tx_thread;
+ thr_param[0].arg = thr_args;
+ thr_param[0].thr_type = ODP_THREAD_WORKER;
+ thr_common.cpumask = &thr_mask_tx;
+ odph_thread_create(&thr_tbl[num_threads - 1], &thr_common, thr_param,
+ 1);
+
+ ret = print_speed_stats(num_threads, stats, global->appl.time,
+ global->appl.accuracy);
+
+ if (odp_pktio_stop(global->if0)) {
+ printf("Error: failed to stop interface %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (odp_pktio_stop(global->if1)) {
+ printf("Error: failed to stop interface %s\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_atomic_store_u32(&global->exit_threads, 1);
+ odp_barrier_wait(&global->term_barrier);
+
+ odph_thread_join(thr_tbl, num_threads);
+
+ free(global->appl.if_names);
+ free(global->appl.if_str);
+
+ if (odp_pktio_close(global->if0)) {
+ printf("Error: failed to close interface %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (odp_pktio_close(global->if1)) {
+ printf("Error: failed to close interface %s\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ if (odp_queue_destroy(global->queue[i])) {
+ printf("Error: failed to destroy queue %d\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_pool_destroy(pool)) {
+ printf("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ printf("Error: shm free global data\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ printf("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/example/simple_pipeline/simple_pipeline_run.sh b/example/simple_pipeline/simple_pipeline_run.sh
new file mode 100755
index 000000000..ba66e506e
--- /dev/null
+++ b/example/simple_pipeline/simple_pipeline_run.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2019 Nokia
+#
+
+# Exit code expected by automake for skipped tests
+TEST_SKIPPED=77
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+if [ $(nproc --all) -lt 3 ]; then
+ echo "Not enough CPU cores. Skipping test."
+ exit $TEST_SKIPPED
+fi
+
+setup_interfaces
+
+./odp_simple_pipeline${EXEEXT} -i $IF0,$IF1 -e -t 1 -a 1
+STATUS=$?
+
+if [ "$STATUS" -ne 0 ]; then
+ echo "Error: status was: $STATUS, expected 0"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/test/common_plat/performance/udp64.pcap b/example/simple_pipeline/udp64.pcap
index 45f9d6e63..45f9d6e63 100644
--- a/test/common_plat/performance/udp64.pcap
+++ b/example/simple_pipeline/udp64.pcap
Binary files differ
diff --git a/example/switch/.gitignore b/example/switch/.gitignore
index 1bd93e32d..63ef8af6e 100644
--- a/example/switch/.gitignore
+++ b/example/switch/.gitignore
@@ -1,3 +1,4 @@
odp_switch
+pktio_env
*.log
*.trs
diff --git a/example/switch/Makefile.am b/example/switch/Makefile.am
index 4134bcf3d..303a78c00 100644
--- a/example/switch/Makefile.am
+++ b/example/switch/Makefile.am
@@ -1,17 +1,34 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_switch$(EXEEXT)
-odp_switch_LDFLAGS = $(AM_LDFLAGS) -static
-odp_switch_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_switch
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
-
-dist_odp_switch_SOURCES = odp_switch.c
+odp_switch_SOURCES = odp_switch.c
if test_example
-if HAVE_PCAP
+if ODP_PKTIO_PCAP
TESTS = switch_run.sh
endif
endif
EXTRA_DIST = switch_run.sh udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s $(top_srcdir)/platform/$(with_platform)/test/example/switch/pktio_env \
+ pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/switch/odp_switch.c b/example/switch/odp_switch.c
index 5bec6a043..bd987a61f 100644
--- a/example/switch/odp_switch.c
+++ b/example/switch/odp_switch.c
@@ -1,7 +1,14 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2020 Nokia
+ */
+
+/**
+ * @example odp_switch.c
+ *
+ * Minimal learning Ethernet switch example application
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <stdio.h>
@@ -9,12 +16,13 @@
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <signal.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
/** Maximum number of worker threads */
-#define MAX_WORKERS 32
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
/** Size of the shared memory block */
#define SHM_PKT_POOL_SIZE 8192
@@ -34,6 +42,9 @@
/** Number of MAC table entries. Must match to hash length. */
#define MAC_TBL_SIZE UINT16_MAX
+/** Aging time for MAC table entries in minutes. Must be <= UINT8_MAX. */
+#define AGING_TIME 5
+
/** Get rid of path in filename - only for unix-type paths using '/' */
#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
strrchr((file_name), '/') + 1 : (file_name))
@@ -43,6 +54,7 @@ typedef union {
struct {
odph_ethaddr_t mac; /**< Ethernet MAC address */
uint8_t port; /**< Port index */
+ uint8_t tick; /**< Tick of the latest received packet */
} s;
uint64_t u64;
@@ -52,8 +64,8 @@ typedef union {
* Parsed command line application arguments
*/
typedef struct {
- int cpu_count; /**< Number of CPUs to be used */
- unsigned if_count; /**< Number of interfaces to be used */
+ unsigned int cpu_count; /**< Number of CPUs to be used */
+ unsigned int if_count; /**< Number of interfaces to be used */
int num_workers; /**< Number of worker threads */
char **if_names; /**< Array of pointers to interface names */
int time; /**< Time in seconds to run */
@@ -61,12 +73,16 @@ typedef struct {
char *if_str; /**< Storage for interface names */
} appl_args_t;
-static int exit_threads; /**< Break workers loop if set to 1 */
+typedef enum frame_type_t {
+ FRAME_UNICAST,
+ FRAME_BROADCAST,
+ FRAME_INVALID
+} frame_type_t;
/**
* Statistics
*/
-typedef union {
+typedef union ODP_ALIGNED_CACHE {
struct {
/** Number of received packets */
uint64_t rx_packets;
@@ -79,14 +95,14 @@ typedef union {
} s;
uint8_t padding[ODP_CACHE_LINE_SIZE];
-} stats_t ODP_ALIGNED_CACHE;
+} stats_t;
/**
* Packet buffer
*/
typedef struct pkt_buf_t {
odp_packet_t pkt[MAX_PKT_BURST]; /**< Array of packet handles */
- unsigned len; /**< Number of packets in buffer */
+ unsigned int len; /**< Number of packets in buffer */
} pkt_buf_t;
/**
@@ -118,6 +134,10 @@ typedef struct {
appl_args_t appl; /**< Parsed application arguments */
thread_args_t thread[MAX_WORKERS]; /**< Thread specific arguments */
odp_pool_t pool; /**< Packet pool */
+ /** Global barrier to synchronize main and workers */
+ odp_barrier_t barrier;
+ /** Break workers loop if set to 1 */
+ odp_atomic_u32_t exit_threads;
/** Table of pktio handles */
struct {
odp_pktio_t pktio;
@@ -136,8 +156,12 @@ typedef struct {
/** Global pointer to args */
static args_t *gbl_args;
-/** Global barrier to synchronize main and workers */
-static odp_barrier_t barrier;
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+}
/**
* Calculate MAC table index using Ethernet address hash
@@ -156,15 +180,29 @@ static inline uint16_t calc_mac_tbl_idx(odph_ethaddr_t *mac)
}
/**
+ * Calculate diff between ticks and take care of value wrap
+ */
+static inline uint8_t diff_ticks(uint8_t t2, uint8_t t1)
+{
+ if (t1 < t2)
+ return t2 - t1;
+ else if (t1 > t2)
+ return UINT8_MAX + t2 - t1;
+ return 0;
+}
+
+/**
* Get Ethernet address port index from MAC table
*
* @param mac Pointer to Ethernet address
* @param port[out] Pointer to port index for output
+ * @param cur_tick Current tick
*
- * @retval 0 on success
- * @retval -1 on failure
+ * @retval 1 on entry found
+ * @retval 0 on entry not found or expired
*/
-static inline int mac_table_get(odph_ethaddr_t *mac, uint8_t *port)
+static inline int mac_table_get(odph_ethaddr_t *mac, uint8_t *port,
+ uint8_t cur_tick)
{
mac_tbl_entry_t entry;
uint16_t idx;
@@ -174,10 +212,13 @@ static inline int mac_table_get(odph_ethaddr_t *mac, uint8_t *port)
entry.u64 = odp_atomic_load_u64(&gbl_args->mac_tbl[idx]);
if (memcmp(mac->addr, entry.s.mac.addr, ODPH_ETHADDR_LEN))
- return -1;
+ return 0;
+
+ if (odp_unlikely(diff_ticks(cur_tick, entry.s.tick) > AGING_TIME))
+ return 0;
*port = entry.s.port;
- return 0;
+ return 1;
}
/**
@@ -185,18 +226,24 @@ static inline int mac_table_get(odph_ethaddr_t *mac, uint8_t *port)
*
* @param mac Pointer to Ethernet address
* @param port Pointer to port index
+ * @param cur_tick Current tick
*/
-static inline void mac_table_put(odph_ethaddr_t *mac, uint8_t port)
+static inline void mac_table_update(odph_ethaddr_t *mac, uint8_t port,
+ uint8_t cur_tick)
{
mac_tbl_entry_t entry;
uint16_t idx;
idx = calc_mac_tbl_idx(mac);
+ entry.u64 = odp_atomic_load_u64(&gbl_args->mac_tbl[idx]);
- entry.s.mac = *mac;
- entry.s.port = port;
-
- odp_atomic_store_u64(&gbl_args->mac_tbl[idx], entry.u64);
+ if (memcmp(entry.s.mac.addr, mac->addr, ODPH_ETHADDR_LEN) ||
+ entry.s.port != port || entry.s.tick != cur_tick) {
+ entry.s.mac = *mac;
+ entry.s.port = port;
+ entry.s.tick = cur_tick;
+ odp_atomic_store_u64(&gbl_args->mac_tbl[idx], entry.u64);
+ }
}
/**
@@ -240,7 +287,7 @@ static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
}
odp_pktio_config_init(&config);
- config.parser.layer = ODP_PKTIO_PARSER_LAYER_L2;
+ config.parser.layer = ODP_PROTO_LAYER_L2;
odp_pktio_config(pktio, &config);
odp_pktin_queue_param_init(&pktin_param);
@@ -263,8 +310,7 @@ static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
mode_tx = ODP_PKTIO_OP_MT;
}
- pktin_param.hash_enable = 1;
- pktin_param.hash_proto.proto.ipv4 = 1;
+ pktin_param.hash_enable = (num_rx > 1) ? 1 : 0;
pktin_param.hash_proto.proto.ipv4_tcp = 1;
pktin_param.hash_proto.proto.ipv4_udp = 1;
pktin_param.num_queues = num_rx;
@@ -320,6 +366,8 @@ static int print_speed_stats(int num_workers, stats_t (*thr_stats)[MAX_PKTIOS],
uint64_t tx_pkts_prev[MAX_PKTIOS] = {0};
uint64_t rx_pkts_tot;
uint64_t tx_pkts_tot;
+ uint64_t rx_drops_tot;
+ uint64_t tx_drops_tot;
uint64_t rx_pps;
uint64_t tx_pps;
int i, j;
@@ -333,16 +381,18 @@ static int print_speed_stats(int num_workers, stats_t (*thr_stats)[MAX_PKTIOS],
timeout = 1;
}
/* Wait for all threads to be ready*/
- odp_barrier_wait(&barrier);
+ odp_barrier_wait(&gbl_args->barrier);
do {
uint64_t rx_pkts[MAX_PKTIOS] = {0};
uint64_t tx_pkts[MAX_PKTIOS] = {0};
- uint64_t rx_drops = 0;
- uint64_t tx_drops = 0;
+ uint64_t rx_drops[MAX_PKTIOS] = {0};
+ uint64_t tx_drops[MAX_PKTIOS] = {0};
rx_pkts_tot = 0;
tx_pkts_tot = 0;
+ rx_drops_tot = 0;
+ tx_drops_tot = 0;
sleep(timeout);
elapsed += timeout;
@@ -351,8 +401,8 @@ static int print_speed_stats(int num_workers, stats_t (*thr_stats)[MAX_PKTIOS],
for (j = 0; j < num_ifaces; j++) {
rx_pkts[j] += thr_stats[i][j].s.rx_packets;
tx_pkts[j] += thr_stats[i][j].s.tx_packets;
- rx_drops += thr_stats[i][j].s.rx_drops;
- tx_drops += thr_stats[i][j].s.tx_drops;
+ rx_drops[j] += thr_stats[i][j].s.rx_drops;
+ tx_drops[j] += thr_stats[i][j].s.tx_drops;
}
}
@@ -364,20 +414,24 @@ static int print_speed_stats(int num_workers, stats_t (*thr_stats)[MAX_PKTIOS],
tx_pps = (tx_pkts[j] - tx_pkts_prev[j]) / timeout;
printf(" Port %d: %" PRIu64 " rx pps, %" PRIu64
" tx pps, %" PRIu64 " rx pkts, %" PRIu64
- " tx pkts\n", j, rx_pps, tx_pps, rx_pkts[j],
- tx_pkts[j]);
+ " tx pkts, %" PRIu64 " rx drops, %" PRIu64
+ " tx drops\n", j, rx_pps, tx_pps, rx_pkts[j],
+ tx_pkts[j], rx_drops[j], tx_drops[j]);
rx_pkts_prev[j] = rx_pkts[j];
tx_pkts_prev[j] = tx_pkts[j];
rx_pkts_tot += rx_pkts[j];
tx_pkts_tot += tx_pkts[j];
+ rx_drops_tot += rx_drops[j];
+ tx_drops_tot += tx_drops[j];
}
printf("Total: %" PRIu64 " rx pkts, %" PRIu64 " tx pkts, %"
PRIu64 " rx drops, %" PRIu64 " tx drops\n", rx_pkts_tot,
- tx_pkts_tot, rx_drops, tx_drops);
+ tx_pkts_tot, rx_drops_tot, tx_drops_tot);
- } while (loop_forever || (elapsed < duration));
+ } while (!odp_atomic_load_u32(&gbl_args->exit_threads) &&
+ (loop_forever || (elapsed < duration)));
return rx_pkts_tot >= 100 ? 0 : -1;
}
@@ -439,7 +493,7 @@ static inline void broadcast_packet(odp_packet_t pkt, thread_args_t *thr_arg,
{
odp_bool_t first = 1;
uint8_t port_out;
- unsigned buf_len;
+ unsigned int buf_len;
for (port_out = 0; port_out < gbl_args->appl.if_count; port_out++) {
if (port_out == port_in)
@@ -465,6 +519,34 @@ static inline void broadcast_packet(odp_packet_t pkt, thread_args_t *thr_arg,
}
/**
+ * Check Ethernet frame for broadcast/invalid addresses
+ *
+ * @param eth Pointer to an Ethernet header
+ *
+ * @retval Ethernet frame_type_t
+ */
+static frame_type_t check_frame(odph_ethhdr_t *eth)
+{
+ static uint8_t broadcast_addr[ODPH_ETHADDR_LEN] = {0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff};
+ static uint8_t null_addr[ODPH_ETHADDR_LEN] = {0, 0, 0, 0, 0, 0};
+
+ /* Drop invalid frames */
+ if (odp_unlikely(!memcmp(eth->src.addr, broadcast_addr,
+ ODPH_ETHADDR_LEN) ||
+ !memcmp(eth->dst.addr, null_addr,
+ ODPH_ETHADDR_LEN) ||
+ !memcmp(eth->src.addr, null_addr,
+ ODPH_ETHADDR_LEN))) {
+ return FRAME_INVALID;
+ }
+ if (!memcmp(eth->dst.addr, broadcast_addr, ODPH_ETHADDR_LEN))
+ return FRAME_BROADCAST;
+
+ return FRAME_UNICAST;
+}
+
+/**
* Forward packets to correct output buffers
*
* Packets, whose destination MAC address is already known from previously
@@ -476,38 +558,44 @@ static inline void broadcast_packet(odp_packet_t pkt, thread_args_t *thr_arg,
* @param num Number of packets in the array
* @param thr_arg Thread arguments
* @param port_in Input port index
+ * @param cur_tick Current tick
*/
-static inline void forward_packets(odp_packet_t pkt_tbl[], unsigned num,
- thread_args_t *thr_arg, uint8_t port_in)
+static inline void forward_packets(odp_packet_t pkt_tbl[], unsigned int num,
+ thread_args_t *thr_arg, uint8_t port_in,
+ uint8_t cur_tick)
{
odp_packet_t pkt;
odph_ethhdr_t *eth;
- unsigned i;
- unsigned buf_id;
- int ret;
+ unsigned int i;
+ unsigned int buf_id;
uint8_t port_out = 0;
+ int frame_type;
for (i = 0; i < num; i++) {
pkt = pkt_tbl[i];
if (!odp_packet_has_eth(pkt)) {
+ thr_arg->stats[port_in]->s.rx_drops++;
odp_packet_free(pkt);
continue;
}
eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- /* Lookup source MAC address */
- ret = mac_table_get(&eth->src, &port_out);
+ /* Check Ethernet frame type */
+ frame_type = check_frame(eth);
+ if (odp_unlikely(frame_type == FRAME_INVALID)) {
+ thr_arg->stats[port_in]->s.rx_drops++;
+ odp_packet_free(pkt);
+ continue;
+ }
- /* Update for address table if necessary */
- if (ret < 0 || port_out != port_in)
- mac_table_put(&eth->src, port_in);
+ /* Update source address MAC table entry */
+ mac_table_update(&eth->src, port_in, cur_tick);
- /* Lookup destination MAC address */
- ret = mac_table_get(&eth->dst, &port_out);
- if (ret < 0) {
- /* If address was not found, broadcast packet */
+ /* Broadcast frame is necessary */
+ if (frame_type == FRAME_BROADCAST ||
+ !mac_table_get(&eth->dst, &port_out, cur_tick)) {
broadcast_packet(pkt, thr_arg, port_in);
continue;
}
@@ -585,8 +673,11 @@ static int run_worker(void *arg)
odp_packet_t pkt_tbl[MAX_PKT_BURST];
odp_pktin_queue_t pktin;
odp_pktout_queue_t pktout;
- unsigned num_pktio;
- unsigned pktio = 0;
+ odp_time_t time_prev;
+ odp_time_t minute;
+ uint8_t cur_tick;
+ unsigned int num_pktio;
+ unsigned int pktio = 0;
uint8_t port_in;
uint8_t port_out;
int pkts;
@@ -595,11 +686,17 @@ static int run_worker(void *arg)
pktin = thr_args->rx_pktio[pktio].pktin;
port_in = thr_args->rx_pktio[pktio].port_idx;
- odp_barrier_wait(&barrier);
+ odp_barrier_wait(&gbl_args->barrier);
- while (!exit_threads) {
+ minute = odp_time_local_from_ns(ODP_TIME_MIN_IN_NS);
+ time_prev = odp_time_local();
+ cur_tick = (odp_time_to_ns(time_prev) / ODP_TIME_MIN_IN_NS) % UINT8_MAX;
+
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ odp_time_t time_cur;
+ odp_time_t time_diff;
int sent;
- unsigned drops;
+ unsigned int drops;
if (num_pktio > 1) {
pktin = thr_args->rx_pktio[pktio].pktin;
@@ -613,15 +710,25 @@ static int run_worker(void *arg)
if (odp_unlikely(pkts <= 0))
continue;
+ time_cur = odp_time_local();
+ time_diff = odp_time_diff(time_cur, time_prev);
+
+ if (odp_unlikely(odp_time_cmp(time_diff, minute))) {
+ /* Tick stored as 8 bit value */
+ cur_tick = (odp_time_to_ns(time_cur) /
+ ODP_TIME_MIN_IN_NS) % UINT8_MAX;
+ time_prev = time_cur;
+ }
+
thr_args->stats[port_in]->s.rx_packets += pkts;
/* Sort packets to thread local tx buffers */
- forward_packets(pkt_tbl, pkts, thr_args, port_in);
+ forward_packets(pkt_tbl, pkts, thr_args, port_in, cur_tick);
/* Empty all thread local tx buffers */
for (port_out = 0; port_out < gbl_args->appl.if_count;
port_out++) {
- unsigned tx_pkts;
+ unsigned int tx_pkts;
odp_packet_t *tx_pkt_tbl;
if (port_out == port_in ||
@@ -643,7 +750,7 @@ static int run_worker(void *arg)
drops = tx_pkts - sent;
if (odp_unlikely(drops)) {
- unsigned i;
+ unsigned int i;
thr_args->stats[port_out]->s.tx_drops += drops;
@@ -725,7 +832,7 @@ static void usage(char *progname)
" Interface count min 2, max %i\n"
"\n"
"Optional OPTIONS:\n"
- " -c, --count <number> CPU count.\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
" -t, --time <number> Time in seconds to run.\n"
" -a, --accuracy <number> Statistics print interval in seconds\n"
" (default is 10 second).\n"
@@ -747,7 +854,7 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
int long_index;
char *token;
size_t len;
- unsigned i;
+ unsigned int i;
static const struct option longopts[] = {
{"count", required_argument, NULL, 'c'},
{"time", required_argument, NULL, 't'},
@@ -757,16 +864,12 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
{NULL, 0, NULL, 0}
};
- static const char *shortopts = "+c:+t:+a:i:h";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
+ static const char *shortopts = "+c:t:a:i:h";
+ appl_args->cpu_count = 1; /* use one worker by default */
appl_args->time = 0; /* loop forever if time to run is 0 */
appl_args->accuracy = 10; /* get and print pps stats second */
- opterr = 0; /* do not issue errors on helper options */
-
while (1) {
opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
@@ -845,21 +948,9 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
*/
static void print_info(char *progname, appl_args_t *appl_args)
{
- unsigned i;
+ unsigned int i;
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_version_impl_name(),
- odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
+ odp_sys_info_print();
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
@@ -877,6 +968,7 @@ static void gbl_args_init(args_t *args)
int pktio;
memset(args, 0, sizeof(args_t));
+ odp_atomic_init_u32(&args->exit_threads, 0);
for (pktio = 0; pktio < MAX_PKTIOS; pktio++)
args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
@@ -884,9 +976,11 @@ static void gbl_args_init(args_t *args)
int main(int argc, char **argv)
{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_WORKERS];
int i, j;
- int cpu;
int num_workers;
odp_shm_t shm;
odp_cpumask_t cpumask;
@@ -896,10 +990,22 @@ int main(int argc, char **argv)
stats_t (*stats)[MAX_PKTIOS];
int if_count;
odp_instance_t instance;
- odph_odpthread_params_t thr_params;
+ odp_init_t init_param;
+
+ signal(SIGINT, sig_handler);
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ printf("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
/* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
+ if (odp_init_global(&instance, &init_param, NULL)) {
printf("Error: ODP global init failed.\n");
exit(EXIT_FAILURE);
}
@@ -913,6 +1019,12 @@ int main(int argc, char **argv)
/* Reserve memory for args from shared mem */
shm = odp_shm_reserve("shm_args", sizeof(args_t),
ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ printf("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
gbl_args = odp_shm_addr(shm);
if (gbl_args == NULL) {
@@ -921,7 +1033,7 @@ int main(int argc, char **argv)
}
gbl_args_init(gbl_args);
- for (i = 0; (unsigned)i < MAC_TBL_SIZE; i++)
+ for (i = 0; (unsigned int)i < MAC_TBL_SIZE; i++)
odp_atomic_init_u64(&gbl_args->mac_tbl[i], 0);
/* Parse and store the application arguments */
@@ -930,9 +1042,8 @@ int main(int argc, char **argv)
/* Print both system and application information */
print_info(NO_PATH(argv[0]), &gbl_args->appl);
- /* Default to system CPU count unless user specified */
num_workers = MAX_WORKERS;
- if (gbl_args->appl.cpu_count)
+ if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
num_workers = gbl_args->appl.cpu_count;
/* Get default worker cpumask */
@@ -974,10 +1085,12 @@ int main(int argc, char **argv)
if (create_pktio(dev, i, num_rx, num_workers, gbl_args->pool))
exit(EXIT_FAILURE);
- ret = odp_pktio_promisc_mode_set(gbl_args->pktios[i].pktio, 1);
- if (ret != 0) {
- printf("Error: failed to set port to promiscuous mode.\n");
- exit(EXIT_FAILURE);
+ if (odp_pktio_promisc_mode(gbl_args->pktios[i].pktio) != 1) {
+ ret = odp_pktio_promisc_mode_set(gbl_args->pktios[i].pktio, 1);
+ if (ret != 0) {
+ printf("Error: failed to set %s to promiscuous mode.\n", dev);
+ exit(EXIT_FAILURE);
+ }
}
}
gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
@@ -988,31 +1101,27 @@ int main(int argc, char **argv)
memset(thread_tbl, 0, sizeof(thread_tbl));
- odp_barrier_init(&barrier, num_workers + 1);
+ odp_barrier_init(&gbl_args->barrier, num_workers + 1);
stats = gbl_args->stats;
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
- thr_params.start = run_worker;
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
/* Create worker threads */
- cpu = odp_cpumask_first(&cpumask);
for (i = 0; i < num_workers; ++i) {
- odp_cpumask_t thd_mask;
-
for (j = 0; j < MAX_PKTIOS; j++)
gbl_args->thread[i].stats[j] = &stats[i][j];
- thr_params.arg = &gbl_args->thread[i];
-
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
- odph_odpthreads_create(&thread_tbl[i], &thd_mask, &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = run_worker;
+ thr_param[i].arg = &gbl_args->thread[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
}
+ odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
+
/* Start packet receive and transmit */
for (i = 0; i < if_count; ++i) {
odp_pktio_t pktio;
@@ -1028,11 +1137,20 @@ int main(int argc, char **argv)
ret = print_speed_stats(num_workers, gbl_args->stats,
gbl_args->appl.time, gbl_args->appl.accuracy);
- exit_threads = 1;
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
/* Master thread waits for other threads to exit */
- for (i = 0; i < num_workers; ++i)
- odph_odpthreads_join(&thread_tbl[i]);
+ odph_thread_join(thread_tbl, num_workers);
+
+ /* Stop and close used pktio devices */
+ for (i = 0; i < if_count; i++) {
+ odp_pktio_t pktio = gbl_args->pktios[i].pktio;
+
+ if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
+ printf("Error: failed to close pktio\n");
+ exit(EXIT_FAILURE);
+ }
+ }
free(gbl_args->appl.if_names);
free(gbl_args->appl.if_str);
@@ -1057,6 +1175,5 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
- printf("Exit: %d\n\n", ret);
return ret;
}
diff --git a/example/switch/switch_run.sh b/example/switch/switch_run.sh
index d9aa8bd06..c01c505ba 100755
--- a/example/switch/switch_run.sh
+++ b/example/switch/switch_run.sh
@@ -1,38 +1,30 @@
#!/bin/bash
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
-NUM_RX_PORT=3
RETVAL=0
-PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
-
-echo "Switch test using PCAP_IN = ${PCAP_IN}"
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
-RX_PORTS=""
-for i in `seq 1 $NUM_RX_PORT`;
-do
- RX_PORTS="${RX_PORTS},pcap:out=pcapout${i}.pcap"
-done
+setup_interfaces
-./odp_switch -i pcap:in=${PCAP_IN}${RX_PORTS} -t 1
+./odp_switch${EXEEXT} -i $IF0,$IF1,$IF2,$IF3 -t 1 -a 1
STATUS=$?
if [ "$STATUS" -ne 0 ]; then
echo "Error: status was: $STATUS, expected 0"
RETVAL=1
fi
-for i in `seq 1 $NUM_RX_PORT`;
-do
- if [ `stat -c %s pcapout${i}.pcap` -ne `stat -c %s ${PCAP_IN}` ]; then
- echo "Error: Output file $i size not matching"
- RETVAL=1
- fi
- rm -f pcapout${i}.pcap
-done
+validate_result
+
+cleanup_interfaces
exit $RETVAL
diff --git a/example/sysinfo/.gitignore b/example/sysinfo/.gitignore
new file mode 100644
index 000000000..208e8dfcf
--- /dev/null
+++ b/example/sysinfo/.gitignore
@@ -0,0 +1,3 @@
+odp_sysinfo
+*.log
+*.trs
diff --git a/example/sysinfo/Makefile.am b/example/sysinfo/Makefile.am
new file mode 100644
index 000000000..a8b55b5cc
--- /dev/null
+++ b/example/sysinfo/Makefile.am
@@ -0,0 +1,9 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_sysinfo
+
+odp_sysinfo_SOURCES = odp_sysinfo.c
+
+if test_example
+TESTS = odp_sysinfo
+endif
diff --git a/example/sysinfo/odp_sysinfo.c b/example/sysinfo/odp_sysinfo.c
new file mode 100644
index 000000000..df33f45cf
--- /dev/null
+++ b/example/sysinfo/odp_sysinfo.c
@@ -0,0 +1,1226 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+ /**
+ * @example odp_sysinfo.c
+ *
+ * Example application which queries and prints out various system information
+ * and capabilities which are available through ODP APIs.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define KB 1024
+#define MB (1024 * 1024)
+#define MAX_HUGE_PAGES 32
+#define MAX_IFACES 32
+#define MAX_NAME_LEN 128
+
+#define PROG_NAME "odp_sysinfo"
+
+typedef struct {
+ char name[MAX_NAME_LEN];
+ odp_pktio_capability_t capa;
+ odp_proto_stats_capability_t proto_stats_capa;
+} pktio_t;
+
+typedef struct {
+ int num_pktio;
+ pktio_t pktio[MAX_IFACES];
+ struct {
+ odp_timer_capability_t capa[ODP_CLOCK_NUM_SRC];
+ odp_timer_pool_info_t pool_info[ODP_CLOCK_NUM_SRC];
+ int num;
+ } timer;
+} appl_args_t;
+
+/* Check that prints can use %u instead of %PRIu32 */
+ODP_STATIC_ASSERT(sizeof(unsigned int) >= sizeof(uint32_t), "unsigned int smaller than uint32_t");
+
+static const char *support_level(odp_support_t support)
+{
+ switch (support) {
+ case ODP_SUPPORT_NO: return "no";
+ case ODP_SUPPORT_YES: return "yes";
+ case ODP_SUPPORT_PREFERRED: return "yes, preferred";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *cpu_arch_name(odp_system_info_t *sysinfo)
+{
+ odp_cpu_arch_t cpu_arch = sysinfo->cpu_arch;
+
+ switch (cpu_arch) {
+ case ODP_CPU_ARCH_ARM:
+ return "ARM";
+ case ODP_CPU_ARCH_MIPS:
+ return "MIPS";
+ case ODP_CPU_ARCH_PPC:
+ return "PPC";
+ case ODP_CPU_ARCH_RISCV:
+ return "RISC-V";
+ case ODP_CPU_ARCH_X86:
+ return "x86";
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *arm_isa(odp_cpu_arch_arm_t isa)
+{
+ switch (isa) {
+ case ODP_CPU_ARCH_ARMV6:
+ return "ARMv6";
+ case ODP_CPU_ARCH_ARMV7:
+ return "ARMv7-A";
+ case ODP_CPU_ARCH_ARMV8_0:
+ return "ARMv8.0-A";
+ case ODP_CPU_ARCH_ARMV8_1:
+ return "ARMv8.1-A";
+ case ODP_CPU_ARCH_ARMV8_2:
+ return "ARMv8.2-A";
+ case ODP_CPU_ARCH_ARMV8_3:
+ return "ARMv8.3-A";
+ case ODP_CPU_ARCH_ARMV8_4:
+ return "ARMv8.4-A";
+ case ODP_CPU_ARCH_ARMV8_5:
+ return "ARMv8.5-A";
+ case ODP_CPU_ARCH_ARMV8_6:
+ return "ARMv8.6-A";
+ case ODP_CPU_ARCH_ARMV8_7:
+ return "ARMv8.7-A";
+ case ODP_CPU_ARCH_ARMV8_8:
+ return "ARMv8.8-A";
+ case ODP_CPU_ARCH_ARMV8_9:
+ return "ARMv8.9-A";
+ case ODP_CPU_ARCH_ARMV9_0:
+ return "ARMv9.0-A";
+ case ODP_CPU_ARCH_ARMV9_1:
+ return "ARMv9.1-A";
+ case ODP_CPU_ARCH_ARMV9_2:
+ return "ARMv9.2-A";
+ case ODP_CPU_ARCH_ARMV9_3:
+ return "ARMv9.3-A";
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *x86_isa(odp_cpu_arch_x86_t isa)
+{
+ switch (isa) {
+ case ODP_CPU_ARCH_X86_I686:
+ return "x86_i686";
+ case ODP_CPU_ARCH_X86_64:
+ return "x86_64";
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *cpu_arch_isa(odp_system_info_t *sysinfo, int isa_sw)
+{
+ odp_cpu_arch_t cpu_arch = sysinfo->cpu_arch;
+
+ switch (cpu_arch) {
+ case ODP_CPU_ARCH_ARM:
+ if (isa_sw)
+ return arm_isa(sysinfo->cpu_isa_sw.arm);
+ else
+ return arm_isa(sysinfo->cpu_isa_hw.arm);
+ case ODP_CPU_ARCH_MIPS:
+ return "Unknown";
+ case ODP_CPU_ARCH_PPC:
+ return "Unknown";
+ case ODP_CPU_ARCH_RISCV:
+ return "Unknown";
+ case ODP_CPU_ARCH_X86:
+ if (isa_sw)
+ return x86_isa(sysinfo->cpu_isa_sw.x86);
+ else
+ return x86_isa(sysinfo->cpu_isa_hw.x86);
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *cipher_alg_name(odp_cipher_alg_t cipher)
+{
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ return "null";
+ case ODP_CIPHER_ALG_DES:
+ return "des";
+ case ODP_CIPHER_ALG_3DES_CBC:
+ return "3des_cbc";
+ case ODP_CIPHER_ALG_AES_CBC:
+ return "aes_cbc";
+ case ODP_CIPHER_ALG_AES_CTR:
+ return "aes_ctr";
+ case ODP_CIPHER_ALG_AES_ECB:
+ return "aes_ecb";
+ case ODP_CIPHER_ALG_AES_CFB128:
+ return "aes_cfb128";
+ case ODP_CIPHER_ALG_AES_XTS:
+ return "aes_xts";
+ case ODP_CIPHER_ALG_AES_GCM:
+ return "aes_gcm";
+ case ODP_CIPHER_ALG_AES_CCM:
+ return "aes_ccm";
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ return "chacha20_poly1305";
+ case ODP_CIPHER_ALG_KASUMI_F8:
+ return "kasumi_f8";
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ return "snow3g_uea2";
+ case ODP_CIPHER_ALG_AES_EEA2:
+ return "aes_eea2";
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ return "zuc_eea3";
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *auth_alg_name(odp_auth_alg_t auth)
+{
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ return "null";
+ case ODP_AUTH_ALG_MD5_HMAC:
+ return "md5_hmac";
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ return "sha1_hmac";
+ case ODP_AUTH_ALG_SHA224_HMAC:
+ return "sha224_hmac";
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ return "sha256_hmac";
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ return "sha384_hmac";
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ return "sha512_hmac";
+ case ODP_AUTH_ALG_AES_GCM:
+ return "aes_gcm";
+ case ODP_AUTH_ALG_AES_GMAC:
+ return "aes_gmac";
+ case ODP_AUTH_ALG_AES_CCM:
+ return "aes_ccm";
+ case ODP_AUTH_ALG_AES_CMAC:
+ return "aes_cmac";
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ return "aes_xcbc_mac";
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ return "chacha20_poly1305";
+ case ODP_AUTH_ALG_KASUMI_F9:
+ return "kasumi_f9";
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ return "snow3g_uia2";
+ case ODP_AUTH_ALG_AES_EIA2:
+ return "aes_eia2";
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ return "zuc_eia3";
+ case ODP_AUTH_ALG_MD5:
+ return "md5";
+ case ODP_AUTH_ALG_SHA1:
+ return "sha1";
+ case ODP_AUTH_ALG_SHA224:
+ return "sha224";
+ case ODP_AUTH_ALG_SHA256:
+ return "sha256";
+ case ODP_AUTH_ALG_SHA384:
+ return "sha384";
+ case ODP_AUTH_ALG_SHA512:
+ return "sha512";
+ default:
+ return "Unknown";
+ }
+}
+
+typedef void (*cipher_op_t)(odp_cipher_alg_t alg);
+typedef void (*auth_op_t)(odp_auth_alg_t alg);
+
+static void foreach_cipher(odp_crypto_cipher_algos_t ciphers, cipher_op_t op)
+{
+ if (ciphers.bit.null)
+ op(ODP_CIPHER_ALG_NULL);
+ if (ciphers.bit.des)
+ op(ODP_CIPHER_ALG_DES);
+ if (ciphers.bit.trides_cbc)
+ op(ODP_CIPHER_ALG_3DES_CBC);
+ if (ciphers.bit.trides_ecb)
+ op(ODP_CIPHER_ALG_3DES_ECB);
+ if (ciphers.bit.aes_cbc)
+ op(ODP_CIPHER_ALG_AES_CBC);
+ if (ciphers.bit.aes_ctr)
+ op(ODP_CIPHER_ALG_AES_CTR);
+ if (ciphers.bit.aes_ecb)
+ op(ODP_CIPHER_ALG_AES_ECB);
+ if (ciphers.bit.aes_cfb128)
+ op(ODP_CIPHER_ALG_AES_CFB128);
+ if (ciphers.bit.aes_xts)
+ op(ODP_CIPHER_ALG_AES_XTS);
+ if (ciphers.bit.aes_gcm)
+ op(ODP_CIPHER_ALG_AES_GCM);
+ if (ciphers.bit.aes_ccm)
+ op(ODP_CIPHER_ALG_AES_CCM);
+ if (ciphers.bit.chacha20_poly1305)
+ op(ODP_CIPHER_ALG_CHACHA20_POLY1305);
+ if (ciphers.bit.kasumi_f8)
+ op(ODP_CIPHER_ALG_KASUMI_F8);
+ if (ciphers.bit.snow3g_uea2)
+ op(ODP_CIPHER_ALG_SNOW3G_UEA2);
+ if (ciphers.bit.aes_eea2)
+ op(ODP_CIPHER_ALG_AES_EEA2);
+ if (ciphers.bit.zuc_eea3)
+ op(ODP_CIPHER_ALG_ZUC_EEA3);
+}
+
+static void foreach_auth(odp_crypto_auth_algos_t auths, auth_op_t op)
+{
+ if (auths.bit.null)
+ op(ODP_AUTH_ALG_NULL);
+ if (auths.bit.md5_hmac)
+ op(ODP_AUTH_ALG_MD5_HMAC);
+ if (auths.bit.sha1_hmac)
+ op(ODP_AUTH_ALG_SHA1_HMAC);
+ if (auths.bit.sha224_hmac)
+ op(ODP_AUTH_ALG_SHA224_HMAC);
+ if (auths.bit.sha256_hmac)
+ op(ODP_AUTH_ALG_SHA256_HMAC);
+ if (auths.bit.sha384_hmac)
+ op(ODP_AUTH_ALG_SHA384_HMAC);
+ if (auths.bit.sha512_hmac)
+ op(ODP_AUTH_ALG_SHA512_HMAC);
+ if (auths.bit.aes_gcm)
+ op(ODP_AUTH_ALG_AES_GCM);
+ if (auths.bit.aes_gmac)
+ op(ODP_AUTH_ALG_AES_GMAC);
+ if (auths.bit.aes_ccm)
+ op(ODP_AUTH_ALG_AES_CCM);
+ if (auths.bit.aes_cmac)
+ op(ODP_AUTH_ALG_AES_CMAC);
+ if (auths.bit.aes_xcbc_mac)
+ op(ODP_AUTH_ALG_AES_XCBC_MAC);
+ if (auths.bit.chacha20_poly1305)
+ op(ODP_AUTH_ALG_CHACHA20_POLY1305);
+ if (auths.bit.kasumi_f9)
+ op(ODP_AUTH_ALG_KASUMI_F9);
+ if (auths.bit.snow3g_uia2)
+ op(ODP_AUTH_ALG_SNOW3G_UIA2);
+ if (auths.bit.aes_eia2)
+ op(ODP_AUTH_ALG_AES_EIA2);
+ if (auths.bit.zuc_eia3)
+ op(ODP_AUTH_ALG_ZUC_EIA3);
+ if (auths.bit.md5)
+ op(ODP_AUTH_ALG_MD5);
+ if (auths.bit.sha1)
+ op(ODP_AUTH_ALG_SHA1);
+ if (auths.bit.sha224)
+ op(ODP_AUTH_ALG_SHA224);
+ if (auths.bit.sha256)
+ op(ODP_AUTH_ALG_SHA256);
+ if (auths.bit.sha384)
+ op(ODP_AUTH_ALG_SHA384);
+ if (auths.bit.sha512)
+ op(ODP_AUTH_ALG_SHA512);
+}
+
+static void print_cipher_capa(odp_cipher_alg_t cipher)
+{
+ int caps = odp_crypto_cipher_capability(cipher, NULL, 0);
+ int rc, i;
+
+ if (caps <= 0)
+ return;
+
+ odp_crypto_cipher_capability_t capa[caps];
+
+ rc = odp_crypto_cipher_capability(cipher, capa, caps);
+ if (rc < 0)
+ return;
+
+ printf(" %s:\n", cipher_alg_name(cipher));
+ for (i = 0; i < rc; i++)
+ printf(" key %d iv %d\n",
+ capa[i].key_len, capa[i].iv_len);
+}
+
+static void print_auth_capa(odp_auth_alg_t auth)
+{
+ int caps = odp_crypto_auth_capability(auth, NULL, 0);
+ int rc, i;
+
+ if (caps <= 0)
+ return;
+
+ odp_crypto_auth_capability_t capa[caps];
+
+ rc = odp_crypto_auth_capability(auth, capa, caps);
+ if (rc < 0)
+ return;
+
+ printf(" %s:\n", auth_alg_name(auth));
+ for (i = 0; i < rc; i++) {
+ printf(" digest %d", capa[i].digest_len);
+ if (capa[i].key_len != 0)
+ printf(" key %d", capa[i].key_len);
+ if (capa[i].iv_len != 0)
+ printf(" iv %d", capa[i].iv_len);
+ if (capa[i].aad_len.max != 0)
+ printf(" aad %d, %d, %d",
+ capa[i].aad_len.min, capa[i].aad_len.max,
+ capa[i].aad_len.inc);
+ printf("\n");
+ }
+}
+
+static void print_cipher(odp_cipher_alg_t alg)
+{
+ printf("%s ", cipher_alg_name(alg));
+}
+
+static void print_auth(odp_auth_alg_t alg)
+{
+ printf("%s ", auth_alg_name(alg));
+}
+
+static int pktio_capability(appl_args_t *appl_args)
+{
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ int ret = 0;
+
+ odp_pool_param_init(&pool_param);
+
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = 128;
+
+ pool = odp_pool_create("pktio_pool", &pool_param);
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Creating packet pool failed\n");
+ return -1;
+ }
+
+ for (int i = 0; i < appl_args->num_pktio; i++) {
+ odp_pktio_param_t param;
+ odp_pktio_t pktio;
+
+ odp_pktio_param_init(&param);
+
+ param.in_mode = ODP_PKTIN_MODE_SCHED;
+ param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(appl_args->pktio[i].name, pool, &param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Opening pktio %s failed\n", appl_args->pktio[i].name);
+ ret = -1;
+ break;
+ }
+
+ if (odp_pktio_capability(pktio, &appl_args->pktio[i].capa)) {
+ ODPH_ERR("Reading pktio %s capa failed\n", appl_args->pktio[i].name);
+ ret = -1;
+ }
+
+ if (odp_proto_stats_capability(pktio, &appl_args->pktio[i].proto_stats_capa)) {
+ ODPH_ERR("Reading pktio %s proto stats capa failed\n",
+ appl_args->pktio[i].name);
+ ret = -1;
+ }
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Closing pktio %s failed\n", appl_args->pktio[i].name);
+ ret = -1;
+ }
+
+ if (ret)
+ break;
+ }
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Destroying pktio pool failed\n");
+ return -1;
+ }
+ return ret;
+}
+
+static void print_pktio_capa(appl_args_t *appl_args)
+{
+ for (int i = 0; i < appl_args->num_pktio; i++) {
+ odp_pktio_capability_t *capa = &appl_args->pktio[i].capa;
+
+ printf("\n");
+ printf(" PKTIO (%s)\n", appl_args->pktio[i].name);
+ printf(" (in_mode: ODP_PKTIN_MODE_SCHED)\n");
+ printf(" (out_mode: ODP_PKTOUT_MODE_DIRECT)\n");
+ printf(" max_input_queues: %u\n", capa->max_input_queues);
+ printf(" min_input_queue_size: %u\n", capa->min_input_queue_size);
+ printf(" max_input_queue_size: %u\n", capa->max_input_queue_size);
+ printf(" max_output_queues: %u\n", capa->max_output_queues);
+ printf(" min_output_queue_size: %u\n", capa->min_output_queue_size);
+ printf(" max_output_queue_size: %u\n", capa->max_output_queue_size);
+ printf(" config.pktin: 0x%" PRIx64 "\n",
+ capa->config.pktin.all_bits);
+ printf(" config.pktout: 0x%" PRIx64 "\n",
+ capa->config.pktout.all_bits);
+ printf(" set_op: 0x%" PRIx32 "\n", capa->set_op.all_bits);
+ printf(" vector.supported: %s\n",
+ support_level(capa->vector.supported));
+ printf(" vector.max_size: %u\n", capa->vector.max_size);
+ printf(" vector.min_size: %u\n", capa->vector.min_size);
+ printf(" vector.max_tmo_ns: %" PRIu64 " ns\n",
+ capa->vector.max_tmo_ns);
+ printf(" vector.min_tmo_ns: %" PRIu64 " ns\n",
+ capa->vector.min_tmo_ns);
+ printf(" lso.max_profiles: %u\n", capa->lso.max_profiles);
+ printf(" lso.max_profiles_per_pktio: %u\n", capa->lso.max_profiles_per_pktio);
+ printf(" lso.max_packet_segments: %u\n", capa->lso.max_packet_segments);
+ printf(" lso.max_segments: %u\n", capa->lso.max_segments);
+ printf(" lso.max_payload_len: %u B\n", capa->lso.max_payload_len);
+ printf(" lso.max_payload_offset: %u B\n", capa->lso.max_payload_offset);
+ printf(" lso.mod_op.add_segment_num: %u\n", capa->lso.mod_op.add_segment_num);
+ printf(" lso.mod_op.add_payload_len: %u\n", capa->lso.mod_op.add_payload_len);
+ printf(" lso.mod_op.add_payload_offset: %u\n",
+ capa->lso.mod_op.add_payload_offset);
+ printf(" lso.max_num_custom: %u\n", capa->lso.max_num_custom);
+ printf(" lso.proto.custom: %u\n", capa->lso.proto.custom);
+ printf(" lso.proto.ipv4: %u\n", capa->lso.proto.ipv4);
+ printf(" lso.proto.ipv6: %u\n", capa->lso.proto.ipv6);
+ printf(" lso.proto.tcp_ipv4: %u\n", capa->lso.proto.tcp_ipv4);
+ printf(" lso.proto.tcp_ipv6: %u\n", capa->lso.proto.tcp_ipv6);
+ printf(" lso.proto.sctp_ipv4: %u\n", capa->lso.proto.sctp_ipv4);
+ printf(" lso.proto.sctp_ipv6: %u\n", capa->lso.proto.sctp_ipv6);
+ printf(" maxlen.equal: %i\n", capa->maxlen.equal);
+ printf(" maxlen.min_input: %u B\n", capa->maxlen.min_input);
+ printf(" maxlen.max_input: %u B\n", capa->maxlen.max_input);
+ printf(" maxlen.min_output: %u B\n", capa->maxlen.min_output);
+ printf(" maxlen.max_output: %u B\n", capa->maxlen.max_output);
+ printf(" max_tx_aging_tmo_ns: %" PRIu64 " ns\n",
+ capa->max_tx_aging_tmo_ns);
+ printf(" tx_compl.queue_type_sched: %i\n", capa->tx_compl.queue_type_sched);
+ printf(" tx_compl.queue_type_plain: %i\n", capa->tx_compl.queue_type_plain);
+ printf(" tx_compl.mode_event: %u\n", capa->tx_compl.mode_event);
+ printf(" tx_compl.mode_poll: %u\n", capa->tx_compl.mode_poll);
+ printf(" tx_compl.max_compl_id: %u\n", capa->tx_compl.max_compl_id);
+ printf(" free_ctrl.dont_free: %u\n", capa->free_ctrl.dont_free);
+ printf(" reassembly.ip: %i\n", capa->reassembly.ip);
+ printf(" reassembly.ipv4: %i\n", capa->reassembly.ipv4);
+ printf(" reassembly.ipv6: %i\n", capa->reassembly.ipv6);
+ printf(" reassembly.max_wait_time: %" PRIu64 " ns\n",
+ capa->reassembly.max_wait_time);
+ printf(" reassembly.max_num_frags: %u\n", capa->reassembly.max_num_frags);
+ printf(" stats.pktio: 0x%" PRIx64 "\n",
+ capa->stats.pktio.all_counters);
+ printf(" stats.pktin_queue: 0x%" PRIx64 "\n",
+ capa->stats.pktin_queue.all_counters);
+ printf(" stats.pktout_queue: 0x%" PRIx64 "\n",
+ capa->stats.pktout_queue.all_counters);
+ printf(" flow_control.pause_rx: %u\n", capa->flow_control.pause_rx);
+ printf(" flow_control.pfc_rx: %u\n", capa->flow_control.pfc_rx);
+ printf(" flow_control.pause_tx: %u\n", capa->flow_control.pause_tx);
+ printf(" flow_control.pfc_tx: %u\n", capa->flow_control.pfc_tx);
+ }
+}
+
+static void print_proto_stats_capa(appl_args_t *appl_args)
+{
+ for (int i = 0; i < appl_args->num_pktio; i++) {
+ odp_proto_stats_capability_t *capa = &appl_args->pktio[i].proto_stats_capa;
+
+ printf("\n");
+ printf(" PROTO STATS (%s)\n", appl_args->pktio[i].name);
+ printf(" tx.counters: 0x%" PRIx64 "\n", capa->tx.counters.all_bits);
+ printf(" tx.oct_count0_adj: %i\n", capa->tx.oct_count0_adj);
+ printf(" tx.oct_count1_adj: %i\n", capa->tx.oct_count1_adj);
+ }
+}
+
+static int timer_capability(appl_args_t *appl_args)
+{
+ for (int i = 0; i < ODP_CLOCK_NUM_SRC; i++) {
+ int ret;
+ odp_timer_pool_t pool;
+ odp_timer_pool_param_t params;
+ odp_timer_capability_t *capa = &appl_args->timer.capa[appl_args->timer.num];
+ odp_timer_pool_info_t *info = &appl_args->timer.pool_info[appl_args->timer.num];
+
+ ret = odp_timer_capability(i, capa);
+ if (ret && i == ODP_CLOCK_DEFAULT) {
+ ODPH_ERR("odp_timer_capability() failed for default clock source: %d\n",
+ ret);
+ return -1;
+ }
+ if (ret == -1)
+ continue;
+ if (ret < -1) {
+ ODPH_ERR("odp_timer_capability() for clock source %d failed: %d\n", i, ret);
+ return -1;
+ }
+
+ odp_timer_pool_param_init(&params);
+ params.clk_src = i;
+ params.res_ns = capa->max_res.res_ns;
+ params.min_tmo = capa->max_res.min_tmo;
+ params.max_tmo = capa->max_res.max_tmo;
+ params.num_timers = 1;
+
+ pool = odp_timer_pool_create("timer_pool", &params);
+ if (pool == ODP_TIMER_POOL_INVALID) {
+ ODPH_ERR("odp_timer_pool_create() failed for clock source: %d\n", i);
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&pool, 1) != 1) {
+ ODPH_ERR("odp_timer_pool_start_multi() failed for clock source: %d\n", i);
+ return -1;
+ }
+
+ ret = odp_timer_pool_info(pool, info);
+ if (ret) {
+ ODPH_ERR("odp_timer_pool_info() for clock source %d failed: %d\n", i, ret);
+ return -1;
+ }
+
+ odp_timer_pool_destroy(pool);
+
+ appl_args->timer.num++;
+ }
+ return 0;
+}
+
+static void print_timer_capa(appl_args_t *appl_args)
+{
+ for (int i = 0; i < appl_args->timer.num; i++) {
+ odp_timer_capability_t *capa = &appl_args->timer.capa[i];
+ odp_timer_pool_info_t *info = &appl_args->timer.pool_info[i];
+
+ printf("\n");
+ printf(" TIMER (SRC %d)\n", i);
+
+ printf(" max_pools_combined: %u\n", capa->max_pools_combined);
+ printf(" max_pools: %u\n", capa->max_pools);
+ printf(" max_timers: %u\n", capa->max_timers);
+ printf(" queue_type_sched: %i\n", capa->queue_type_sched);
+ printf(" queue_type_plain: %i\n", capa->queue_type_plain);
+ printf(" highest_res_ns: %" PRIu64 " nsec\n", capa->highest_res_ns);
+ printf(" maximum resolution\n");
+ printf(" res_ns: %" PRIu64 " nsec\n", capa->max_res.res_ns);
+ printf(" res_hz: %" PRIu64 " hz\n", capa->max_res.res_hz);
+ printf(" min_tmo: %" PRIu64 " nsec\n", capa->max_res.min_tmo);
+ printf(" max_tmo: %" PRIu64 " nsec\n", capa->max_res.max_tmo);
+ printf(" maximum timeout\n");
+ printf(" res_ns: %" PRIu64 " nsec\n", capa->max_tmo.res_ns);
+ printf(" res_hz: %" PRIu64 " hz\n", capa->max_tmo.res_hz);
+ printf(" min_tmo: %" PRIu64 " nsec\n", capa->max_tmo.min_tmo);
+ printf(" max_tmo: %" PRIu64 " nsec\n", capa->max_tmo.max_tmo);
+ printf(" periodic\n");
+ printf(" max_pools: %u\n", capa->periodic.max_pools);
+ printf(" max_timers: %u\n", capa->periodic.max_timers);
+ printf(" min_base_freq_hz: %" PRIu64 " %" PRIu64 "/%" PRIu64 " Hz\n",
+ capa->periodic.min_base_freq_hz.integer,
+ capa->periodic.min_base_freq_hz.numer,
+ capa->periodic.min_base_freq_hz.denom);
+ printf(" max_base_freq_hz: %" PRIu64 " %" PRIu64 "/%" PRIu64 " Hz\n",
+ capa->periodic.max_base_freq_hz.integer,
+ capa->periodic.max_base_freq_hz.numer,
+ capa->periodic.max_base_freq_hz.denom);
+ printf(" timer pool tick info (max_res)\n");
+ printf(" freq: %" PRIu64 " %" PRIu64 "/%" PRIu64 " Hz\n",
+ info->tick_info.freq.integer,
+ info->tick_info.freq.numer,
+ info->tick_info.freq.denom);
+ printf(" nsec: %" PRIu64 " %" PRIu64 "/%" PRIu64 " ns\n",
+ info->tick_info.nsec.integer,
+ info->tick_info.nsec.numer,
+ info->tick_info.nsec.denom);
+ printf(" clk_cycle: %" PRIu64 " %" PRIu64 "/%" PRIu64 " cycles\n",
+ info->tick_info.clk_cycle.integer,
+ info->tick_info.clk_cycle.numer,
+ info->tick_info.clk_cycle.denom);
+ }
+}
+
+static void usage(void)
+{
+ printf("\n"
+ "System Information\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth0\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -i, --interfaces Ethernet interfaces for packet I/O, comma-separated, no\n"
+ " spaces.\n"
+ " -h, --help Display help and exit.\n"
+ "\n", PROG_NAME, PROG_NAME);
+}
+
+static void parse_interfaces(appl_args_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, ",");
+
+ while (tmp && config->num_pktio < MAX_IFACES) {
+ if (strlen(tmp) + 1 > MAX_NAME_LEN) {
+ ODPH_ERR("Unable to store interface name (MAX_NAME_LEN=%d)\n",
+ MAX_NAME_LEN);
+ exit(EXIT_FAILURE);
+ }
+ strncpy(config->pktio[config->num_pktio].name, tmp, MAX_NAME_LEN);
+
+ config->num_pktio++;
+
+ tmp = strtok(NULL, ",");
+ }
+
+ free(tmp_str);
+}
+
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"interfaces", required_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ static const char *shortopts = "i:h";
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'i':
+ parse_interfaces(appl_args, optarg);
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ case '?':
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ odp_instance_t inst;
+ int i, num_hp, num_hp_print;
+ int num_ava, num_work, num_ctrl;
+ odp_cpumask_t ava_mask, work_mask, ctrl_mask;
+ odp_system_info_t sysinfo;
+ odp_shm_capability_t shm_capa;
+ odp_pool_capability_t pool_capa;
+ odp_pool_ext_capability_t pool_ext_capa;
+ odp_cls_capability_t cls_capa;
+ odp_comp_capability_t comp_capa;
+ odp_dma_capability_t dma_capa;
+ odp_queue_capability_t queue_capa;
+ odp_crypto_capability_t crypto_capa;
+ odp_ipsec_capability_t ipsec_capa;
+ odp_schedule_capability_t schedule_capa;
+ odp_stash_capability_t stash_capa;
+ odp_ml_capability_t ml_capa;
+ appl_args_t appl_args;
+ uint64_t huge_page[MAX_HUGE_PAGES];
+ char ava_mask_str[ODP_CPUMASK_STR_SIZE];
+ char work_mask_str[ODP_CPUMASK_STR_SIZE];
+ char ctrl_mask_str[ODP_CPUMASK_STR_SIZE];
+ int crypto_ret;
+ int ipsec_ret;
+
+ memset(&appl_args, 0, sizeof(appl_args_t));
+
+ printf("\n");
+ printf("ODP system info example\n");
+ printf("***********************************************************\n");
+ printf("\n");
+
+ parse_args(argc, argv, &appl_args);
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n");
+ printf("odp_sys_info_print()\n");
+ printf("***********************************************************\n");
+ odp_sys_info_print();
+
+ printf("\n");
+ printf("odp_sys_config_print()\n");
+ printf("***********************************************************\n");
+ odp_sys_config_print();
+
+ if (odp_system_info(&sysinfo)) {
+ ODPH_ERR("system info call failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(ava_mask_str, 0, ODP_CPUMASK_STR_SIZE);
+ num_ava = odp_cpumask_all_available(&ava_mask);
+ odp_cpumask_to_str(&ava_mask, ava_mask_str, ODP_CPUMASK_STR_SIZE);
+
+ memset(work_mask_str, 0, ODP_CPUMASK_STR_SIZE);
+ num_work = odp_cpumask_default_worker(&work_mask, 0);
+ odp_cpumask_to_str(&work_mask, work_mask_str, ODP_CPUMASK_STR_SIZE);
+
+ memset(ctrl_mask_str, 0, ODP_CPUMASK_STR_SIZE);
+ num_ctrl = odp_cpumask_default_control(&ctrl_mask, 0);
+ odp_cpumask_to_str(&ctrl_mask, ctrl_mask_str, ODP_CPUMASK_STR_SIZE);
+
+ num_hp = odp_sys_huge_page_size_all(huge_page, MAX_HUGE_PAGES);
+
+ num_hp_print = num_hp;
+ if (num_hp_print > MAX_HUGE_PAGES)
+ num_hp_print = MAX_HUGE_PAGES;
+
+ if (odp_shm_capability(&shm_capa)) {
+ ODPH_ERR("shm capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("pool capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pool_ext_capability(ODP_POOL_PACKET, &pool_ext_capa)) {
+ ODPH_ERR("external packet pool capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (pktio_capability(&appl_args)) {
+ ODPH_ERR("pktio capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_cls_capability(&cls_capa)) {
+ ODPH_ERR("classifier capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_comp_capability(&comp_capa)) {
+ ODPH_ERR("compression capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_dma_capability(&dma_capa)) {
+ ODPH_ERR("dma capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_queue_capability(&queue_capa)) {
+ ODPH_ERR("queue capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_schedule_capability(&schedule_capa)) {
+ ODPH_ERR("schedule capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_stash_capability(&stash_capa, ODP_STASH_TYPE_DEFAULT)) {
+ ODPH_ERR("stash capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (timer_capability(&appl_args)) {
+ ODPH_ERR("timer capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_ml_capability(&ml_capa)) {
+ ODPH_ERR("ml capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ crypto_ret = odp_crypto_capability(&crypto_capa);
+ if (crypto_ret < 0)
+ ODPH_ERR("crypto capability failed\n");
+
+ ipsec_ret = odp_ipsec_capability(&ipsec_capa);
+ if (ipsec_ret < 0)
+ ODPH_ERR("IPsec capability failed\n");
+
+ printf("\n");
+ printf("S Y S T E M I N F O R M A T I O N\n");
+ printf("***********************************************************\n");
+ printf("\n");
+ printf(" ODP API version: %s\n", odp_version_api_str());
+ printf(" ODP impl name: %s\n", odp_version_impl_name());
+ printf(" ODP impl details: %s\n", odp_version_impl_str());
+ printf(" CPU model: %s\n", odp_cpu_model_str());
+ printf(" CPU arch: %s\n", cpu_arch_name(&sysinfo));
+ printf(" CPU ISA version: %s\n", cpu_arch_isa(&sysinfo, 0));
+ printf(" SW ISA version: %s\n", cpu_arch_isa(&sysinfo, 1));
+ printf(" CPU max freq: %" PRIu64 " hz\n", odp_cpu_hz_max());
+ printf(" Current CPU: %i\n", odp_cpu_id());
+ printf(" Current CPU freq: %" PRIu64 " hz\n", odp_cpu_hz());
+ printf(" CPU count: %i\n", odp_cpu_count());
+ printf(" CPU available num: %i\n", num_ava);
+ printf(" CPU available mask: %s\n", ava_mask_str);
+ printf(" CPU worker num: %i\n", num_work);
+ printf(" CPU worker mask: %s\n", work_mask_str);
+ printf(" CPU control num: %i\n", num_ctrl);
+ printf(" CPU control mask: %s\n", ctrl_mask_str);
+ printf(" Max threads (define): %i\n", ODP_THREAD_COUNT_MAX);
+ printf(" Max threads: %i\n", odp_thread_count_max());
+ printf(" Byte order: %s (%i / %i)\n",
+ ODP_BYTE_ORDER == ODP_BIG_ENDIAN ? "big" : "little",
+ ODP_BIG_ENDIAN, ODP_LITTLE_ENDIAN);
+ printf(" Bitfield order: %s (%i / %i)\n",
+ ODP_BITFIELD_ORDER == ODP_BIG_ENDIAN_BITFIELD ?
+ "big" : "little",
+ ODP_BIG_ENDIAN_BITFIELD, ODP_LITTLE_ENDIAN_BITFIELD);
+ printf(" Cache line size: %i B\n", odp_sys_cache_line_size());
+ printf(" Page size: %" PRIu64 " kB\n", odp_sys_page_size() / KB);
+ printf(" Default huge page size: %" PRIu64 " kB\n", odp_sys_huge_page_size() / KB);
+ printf(" Num huge page sizes: %i\n", num_hp);
+
+ for (i = 0; i < num_hp_print; i++)
+ printf(" Huge page size [%i]: %" PRIu64 " kB\n",
+ i, huge_page[i] / KB);
+
+ printf("\n");
+ printf(" SHM\n");
+ printf(" max_blocks: %u\n", shm_capa.max_blocks);
+ printf(" max_size: %" PRIu64 " MB\n", shm_capa.max_size / MB);
+ printf(" max_align: %" PRIu64 " B\n", shm_capa.max_align);
+ printf(" flags: 0x%x: %s%s%s%s%s%s\n", shm_capa.flags,
+ (shm_capa.flags & ODP_SHM_PROC) ? "PROC " : "",
+ (shm_capa.flags & ODP_SHM_SINGLE_VA) ? "SINGLE_VA " : "",
+ (shm_capa.flags & ODP_SHM_EXPORT) ? "EXPORT " : "",
+ (shm_capa.flags & ODP_SHM_HP) ? "HP " : "",
+ (shm_capa.flags & ODP_SHM_HW_ACCESS) ? "HW_ACCESS " : "",
+ (shm_capa.flags & ODP_SHM_NO_HP) ? "NO_HP " : "");
+
+ printf("\n");
+ printf(" POOL\n");
+ printf(" max_pools: %u\n", pool_capa.max_pools);
+ printf(" buf.max_pools: %u\n", pool_capa.buf.max_pools);
+ printf(" buf.max_align: %u B\n", pool_capa.buf.max_align);
+ printf(" buf.max_size: %u kB\n", pool_capa.buf.max_size / KB);
+ printf(" buf.max_num: %u\n", pool_capa.buf.max_num);
+ printf(" buf.max_uarea_size: %u B\n", pool_capa.buf.max_uarea_size);
+ printf(" buf.uarea_persistence: %i\n", pool_capa.buf.uarea_persistence);
+ printf(" buf.min_cache_size: %u\n", pool_capa.buf.min_cache_size);
+ printf(" buf.max_cache_size: %u\n", pool_capa.buf.max_cache_size);
+ printf(" buf.stats: 0x%" PRIx64 "\n", pool_capa.buf.stats.all);
+ printf(" pkt.max_pools: %u\n", pool_capa.pkt.max_pools);
+ printf(" pkt.max_len: %u kB\n", pool_capa.pkt.max_len / KB);
+ printf(" pkt.max_num: %u\n", pool_capa.pkt.max_num);
+ printf(" pkt.max_align: %u B\n", pool_capa.pkt.max_align);
+ printf(" pkt.min_headroom: %u B\n", pool_capa.pkt.min_headroom);
+ printf(" pkt.max_headroom: %u B\n", pool_capa.pkt.max_headroom);
+ printf(" pkt.min_tailroom: %u B\n", pool_capa.pkt.min_tailroom);
+ printf(" pkt.max_segs_per_pkt: %u\n", pool_capa.pkt.max_segs_per_pkt);
+ printf(" pkt.min_seg_len: %u B\n", pool_capa.pkt.min_seg_len);
+ printf(" pkt.max_seg_len: %u B\n", pool_capa.pkt.max_seg_len);
+ printf(" pkt.max_uarea_size: %u B\n", pool_capa.pkt.max_uarea_size);
+ printf(" pkt.uarea_persistence: %i\n", pool_capa.pkt.uarea_persistence);
+ printf(" pkt.max_num_subparam: %u\n", pool_capa.pkt.max_num_subparam);
+ printf(" pkt.min_cache_size: %u\n", pool_capa.pkt.min_cache_size);
+ printf(" pkt.max_cache_size: %u\n", pool_capa.pkt.max_cache_size);
+ printf(" pkt.stats: 0x%" PRIx64 "\n", pool_capa.pkt.stats.all);
+ printf(" tmo.max_pools: %u\n", pool_capa.tmo.max_pools);
+ printf(" tmo.max_num: %u\n", pool_capa.tmo.max_num);
+ printf(" tmo.max_uarea_size: %u B\n", pool_capa.tmo.max_uarea_size);
+ printf(" tmo.uarea_persistence: %i\n", pool_capa.tmo.uarea_persistence);
+ printf(" tmo.min_cache_size: %u\n", pool_capa.tmo.min_cache_size);
+ printf(" tmo.max_cache_size: %u\n", pool_capa.tmo.max_cache_size);
+ printf(" tmo.stats: 0x%" PRIx64 "\n", pool_capa.tmo.stats.all);
+ printf(" vector.max_pools: %u\n", pool_capa.vector.max_pools);
+ printf(" vector.max_num: %u\n", pool_capa.vector.max_num);
+ printf(" vector.max_size: %u\n", pool_capa.vector.max_size);
+ printf(" vector.max_uarea_size: %u B\n", pool_capa.vector.max_uarea_size);
+ printf(" vector.uarea_persistence: %i\n", pool_capa.vector.uarea_persistence);
+ printf(" vector.min_cache_size: %u\n", pool_capa.vector.min_cache_size);
+ printf(" vector.max_cache_size: %u\n", pool_capa.vector.max_cache_size);
+ printf(" vector.stats: 0x%" PRIx64 "\n", pool_capa.vector.stats.all);
+
+ printf("\n");
+ printf(" POOL EXT (pkt)\n");
+ printf(" max_pools: %u\n", pool_ext_capa.max_pools);
+ if (pool_ext_capa.max_pools) {
+ printf(" min_cache_size: %u\n", pool_ext_capa.min_cache_size);
+ printf(" max_cache_size: %u\n", pool_ext_capa.max_cache_size);
+ printf(" stats: 0x%" PRIx64 "\n", pool_ext_capa.stats.all);
+ printf(" pkt.max_num_buf: %u\n", pool_ext_capa.pkt.max_num_buf);
+ printf(" pkt.max_buf_size: %u B\n", pool_ext_capa.pkt.max_buf_size);
+ printf(" pkt.odp_header_size: %u B\n", pool_ext_capa.pkt.odp_header_size);
+ printf(" pkt.odp_trailer_size: %u B\n", pool_ext_capa.pkt.odp_trailer_size);
+ printf(" pkt.min_mem_align: %u B\n", pool_ext_capa.pkt.min_mem_align);
+ printf(" pkt.min_buf_align: %u B\n", pool_ext_capa.pkt.min_buf_align);
+ printf(" pkt.min_head_align: %u B\n", pool_ext_capa.pkt.min_head_align);
+ printf(" pkt.buf_size_aligned: %u\n", pool_ext_capa.pkt.buf_size_aligned);
+ printf(" pkt.max_headroom: %u B\n", pool_ext_capa.pkt.max_headroom);
+ printf(" pkt.max_headroom_size: %u B\n", pool_ext_capa.pkt.max_headroom_size);
+ printf(" pkt.max_segs_per_pkt: %u\n", pool_ext_capa.pkt.max_segs_per_pkt);
+ printf(" pkt.max_uarea_size: %u B\n", pool_ext_capa.pkt.max_uarea_size);
+ printf(" pkt.uarea_persistence: %i\n", pool_ext_capa.pkt.uarea_persistence);
+ }
+
+ print_pktio_capa(&appl_args);
+
+ print_proto_stats_capa(&appl_args);
+
+ printf("\n");
+ printf(" CLASSIFIER\n");
+ printf(" supported_terms: 0x%" PRIx64 "\n", cls_capa.supported_terms.all_bits);
+ printf(" max_pmr: %u\n", cls_capa.max_pmr);
+ printf(" max_pmr_per_cos: %u\n", cls_capa.max_pmr_per_cos);
+ printf(" max_terms_per_pmr: %u\n", cls_capa.max_terms_per_pmr);
+ printf(" max_cos: %u\n", cls_capa.max_cos);
+ printf(" max_hash_queues: %u\n", cls_capa.max_hash_queues);
+ printf(" hash_protocols: 0x%x\n", cls_capa.hash_protocols.all_bits);
+ printf(" pmr_range_supported: %i\n", cls_capa.pmr_range_supported);
+ printf(" random_early_detection: %s\n", support_level(cls_capa.random_early_detection));
+ printf(" threshold_red: 0x%" PRIx8 "\n", cls_capa.threshold_red.all_bits);
+ printf(" back_pressure: %s\n", support_level(cls_capa.back_pressure));
+ printf(" threshold_bp: 0x%" PRIx8 "\n", cls_capa.threshold_bp.all_bits);
+ printf(" max_mark: %" PRIu64 "\n", cls_capa.max_mark);
+ printf(" stats.queue: 0x%" PRIx64 "\n", cls_capa.stats.queue.all_counters);
+
+ printf("\n");
+ printf(" COMPRESSION\n");
+ printf(" max_sessions: %u\n", comp_capa.max_sessions);
+ printf(" compl_algos: 0x%x\n", comp_capa.comp_algos.all_bits);
+ printf(" hash_algos: 0x%x\n", comp_capa.hash_algos.all_bits);
+ printf(" sync support: %i\n", comp_capa.sync);
+ printf(" async support: %i\n", comp_capa.async);
+
+ printf("\n");
+ printf(" DMA\n");
+ printf(" max_sessions: %u\n", dma_capa.max_sessions);
+ printf(" max_transfers: %u\n", dma_capa.max_transfers);
+ printf(" max_src_segs: %u\n", dma_capa.max_src_segs);
+ printf(" max_dst_segs: %u\n", dma_capa.max_dst_segs);
+ printf(" max_segs: %u\n", dma_capa.max_segs);
+ printf(" max_seg_len: %u B\n", dma_capa.max_seg_len);
+ printf(" compl_mode_mask: 0x%x\n", dma_capa.compl_mode_mask);
+ printf(" queue_type_sched: %i\n", dma_capa.queue_type_sched);
+ printf(" queue_type_plain: %i\n", dma_capa.queue_type_plain);
+ printf(" pool.max_pools: %u\n", dma_capa.pool.max_pools);
+ printf(" pool.max_num: %u\n", dma_capa.pool.max_num);
+ printf(" pool.max_uarea_size: %u B\n", dma_capa.pool.max_uarea_size);
+ printf(" pool.uarea_persistence: %u\n", dma_capa.pool.uarea_persistence);
+ printf(" pool.min_cache_size: %u\n", dma_capa.pool.min_cache_size);
+ printf(" pool.max_cache_size: %u\n", dma_capa.pool.max_cache_size);
+
+ printf("\n");
+ printf(" QUEUE\n");
+ printf(" max queues: %u\n", queue_capa.max_queues);
+ printf(" plain.max_num: %u\n", queue_capa.plain.max_num);
+ printf(" plain.max_size: %u\n", queue_capa.plain.max_size);
+ printf(" plain.lf.max_num: %u\n", queue_capa.plain.lockfree.max_num);
+ printf(" plain.lf.max_size: %u\n", queue_capa.plain.lockfree.max_size);
+ printf(" plain.wf.max_num: %u\n", queue_capa.plain.waitfree.max_num);
+ printf(" plain.wf.max_size: %u\n", queue_capa.plain.waitfree.max_size);
+
+ printf("\n");
+ printf(" SCHEDULER\n");
+ printf(" max_ordered_locks: %u\n", schedule_capa.max_ordered_locks);
+ printf(" max_groups: %u\n", schedule_capa.max_groups);
+ printf(" max_prios: %u\n", schedule_capa.max_prios);
+ printf(" max_queues: %u\n", schedule_capa.max_queues);
+ printf(" max_queue_size: %u\n", schedule_capa.max_queue_size);
+ printf(" max_flow_id: %u\n", schedule_capa.max_flow_id);
+ printf(" lockfree_queues: %s\n", support_level(schedule_capa.lockfree_queues));
+ printf(" waitfree_queues: %s\n", support_level(schedule_capa.waitfree_queues));
+ printf(" order_wait: %s\n", support_level(schedule_capa.order_wait));
+
+ printf("\n");
+ printf(" STASH\n");
+ printf(" max_stashes_any_type: %u\n", stash_capa.max_stashes_any_type);
+ printf(" max_stashes: %u\n", stash_capa.max_stashes);
+ printf(" max_num_obj: %" PRIu64 "\n", stash_capa.max_num_obj);
+ printf(" max_num.u8: %" PRIu64 "\n", stash_capa.max_num.u8);
+ printf(" max_num.u16: %" PRIu64 "\n", stash_capa.max_num.u16);
+ printf(" max_num.u32: %" PRIu64 "\n", stash_capa.max_num.u32);
+ printf(" max_num.u64: %" PRIu64 "\n", stash_capa.max_num.u64);
+ printf(" max_num.u128: %" PRIu64 "\n", stash_capa.max_num.u128);
+ printf(" max_num.max_obj_size: %" PRIu64 "\n", stash_capa.max_num.max_obj_size);
+ printf(" max_obj_size: %u B\n", stash_capa.max_obj_size);
+ printf(" max_cache_size: %u\n", stash_capa.max_cache_size);
+ printf(" max_get_batch: %u\n", stash_capa.max_get_batch);
+ printf(" max_put_batch: %u\n", stash_capa.max_put_batch);
+ printf(" stats: 0x%" PRIx64 "\n", stash_capa.stats.all);
+
+ printf("\n");
+ printf(" ML\n");
+ printf(" max_models: %u\n", ml_capa.max_models);
+ printf(" max_models_loaded: %u\n", ml_capa.max_models_loaded);
+ printf(" max_model_size: %" PRIu64 "B\n", ml_capa.max_model_size);
+ printf(" max_compl_id: %u\n", ml_capa.max_compl_id);
+ printf(" max_inputs: %u\n", ml_capa.max_inputs);
+ printf(" max_outputs: %u\n", ml_capa.max_outputs);
+ printf(" max_segs_per_input: %u\n", ml_capa.max_segs_per_input);
+ printf(" max_segs_per_output: %u\n", ml_capa.max_segs_per_output);
+ printf(" min_input_align: %u\n", ml_capa.min_input_align);
+ printf(" min_output_align: %u\n", ml_capa.min_output_align);
+ printf(" packed_input_data: %u\n", ml_capa.packed_input_data);
+ printf(" packed_output_data: %u\n", ml_capa.packed_output_data);
+ printf(" load.compl_mode_mask: 0x%x\n", ml_capa.load.compl_mode_mask);
+ printf(" load.compl_queue_plain: %i\n", ml_capa.load.compl_queue_plain);
+ printf(" load.compl_queue_sched: %i\n", ml_capa.load.compl_queue_sched);
+ printf(" run.compl_mode_mask: 0x%x\n", ml_capa.run.compl_mode_mask);
+ printf(" run.compl_queue_plain: %i\n", ml_capa.run.compl_queue_plain);
+ printf(" run.compl_queue_sched: %i\n", ml_capa.run.compl_queue_sched);
+ printf(" pool.max_pools: %u\n", ml_capa.pool.max_pools);
+ printf(" pool.max_num: %u\n", ml_capa.pool.max_num);
+ printf(" pool.max_uarea_size: %u B\n", ml_capa.pool.max_uarea_size);
+ printf(" pool.uarea_persistence: %u\n", ml_capa.pool.uarea_persistence);
+ printf(" pool.min_cache_size: %u\n", ml_capa.pool.min_cache_size);
+ printf(" pool.max_cache_size: %u\n", ml_capa.pool.max_cache_size);
+
+ print_timer_capa(&appl_args);
+
+ if (crypto_ret == 0) {
+ printf("\n");
+ printf(" CRYPTO\n");
+ printf(" max sessions: %u\n", crypto_capa.max_sessions);
+ printf(" sync mode support: %s\n", support_level(crypto_capa.sync_mode));
+ printf(" async mode support: %s\n", support_level(crypto_capa.async_mode));
+ printf(" queue_type_sched: %i\n", crypto_capa.queue_type_sched);
+ printf(" queue_type_plain: %i\n", crypto_capa.queue_type_plain);
+ printf(" cipher algorithms: ");
+ foreach_cipher(crypto_capa.ciphers, print_cipher);
+ printf("\n");
+ foreach_cipher(crypto_capa.ciphers, print_cipher_capa);
+ printf(" cipher algorithms (HW): ");
+ foreach_cipher(crypto_capa.hw_ciphers, print_cipher);
+ printf("\n");
+ foreach_cipher(crypto_capa.hw_ciphers, print_cipher_capa);
+ printf(" auth algorithms: ");
+ foreach_auth(crypto_capa.auths, print_auth);
+ printf("\n");
+ foreach_auth(crypto_capa.auths, print_auth_capa);
+ printf(" auth algorithms (HW): ");
+ foreach_auth(crypto_capa.hw_auths, print_auth);
+ printf("\n");
+ foreach_auth(crypto_capa.hw_auths, print_auth_capa);
+ }
+
+ if (ipsec_ret == 0) {
+ printf("\n");
+ printf(" IPSEC\n");
+ printf(" max SAs: %u\n", ipsec_capa.max_num_sa);
+ printf(" sync mode support: %s\n",
+ support_level(ipsec_capa.op_mode_sync));
+ printf(" async mode support: %s\n",
+ support_level(ipsec_capa.op_mode_async));
+ printf(" inline inbound mode support: %s\n",
+ support_level(ipsec_capa.op_mode_inline_in));
+ printf(" inline outbound mode support: %s\n",
+ support_level(ipsec_capa.op_mode_inline_out));
+ printf(" AH support: %s\n",
+ support_level(ipsec_capa.proto_ah));
+ printf(" post-IPsec fragmentation: %s\n",
+ support_level(ipsec_capa.frag_after));
+ printf(" pre-IPsec fragmentation: %s\n",
+ support_level(ipsec_capa.frag_before));
+ printf(" post-IPsec classification: %s\n",
+ support_level(ipsec_capa.pipeline_cls));
+ printf(" retaining outer headers: %s\n",
+ support_level(ipsec_capa.retain_header));
+ printf(" inbound checksum offload support:\n");
+ printf(" IPv4 header checksum: %s\n",
+ support_level(ipsec_capa.chksums_in.chksum.ipv4));
+ printf(" UDP checksum: %s\n",
+ support_level(ipsec_capa.chksums_in.chksum.udp));
+ printf(" TCP checksum: %s\n",
+ support_level(ipsec_capa.chksums_in.chksum.tcp));
+ printf(" SCTP checksum: %s\n",
+ support_level(ipsec_capa.chksums_in.chksum.sctp));
+ printf(" max destination CoSes: %u\n", ipsec_capa.max_cls_cos);
+ printf(" max destination queues: %u\n", ipsec_capa.max_queues);
+ printf(" queue_type_sched: %i\n", ipsec_capa.queue_type_sched);
+ printf(" queue_type_plain: %i\n", ipsec_capa.queue_type_plain);
+ printf(" vector support: %s\n",
+ support_level(ipsec_capa.vector.supported));
+ printf(" min_size: %u\n", ipsec_capa.vector.min_size);
+ printf(" max_size: %u\n", ipsec_capa.vector.max_size);
+ printf(" min_tmo_ns: %" PRIu64 " ns\n",
+ ipsec_capa.vector.min_tmo_ns);
+ printf(" max_tmo_ns: %" PRIu64 " ns\n",
+ ipsec_capa.vector.max_tmo_ns);
+ printf(" max anti-replay window size: %u\n",
+ ipsec_capa.max_antireplay_ws);
+ printf(" inline TM pipelining: %s\n",
+ support_level(ipsec_capa.inline_ipsec_tm));
+ printf(" testing capabilities:\n");
+ printf(" sa_operations.seq_num: %i\n",
+ ipsec_capa.test.sa_operations.seq_num);
+ printf(" sa_operations.antireplay_window_top: %i\n",
+ ipsec_capa.test.sa_operations.antireplay_window_top);
+ printf(" post-IPsec reassembly support:\n");
+ printf(" ip: %i\n", ipsec_capa.reassembly.ip);
+ printf(" ipv4: %i\n", ipsec_capa.reassembly.ipv4);
+ printf(" ipv6: %i\n", ipsec_capa.reassembly.ipv6);
+ printf(" max_wait_time: %" PRIu64 "\n",
+ ipsec_capa.reassembly.max_wait_time);
+ printf(" max_num_frags: %" PRIu16 "\n",
+ ipsec_capa.reassembly.max_num_frags);
+ printf(" reass_async: %i\n", ipsec_capa.reass_async);
+ printf(" reass_inline: %i\n", ipsec_capa.reass_inline);
+ printf(" cipher algorithms: ");
+ foreach_cipher(ipsec_capa.ciphers, print_cipher);
+ printf("\n");
+ printf(" auth algorithms: ");
+ foreach_auth(ipsec_capa.auths, print_auth);
+ printf("\n");
+ }
+
+ printf("\n");
+ printf(" SHM MEMORY BLOCKS:\n");
+ odp_shm_print_all();
+
+ printf("\n");
+ printf("***********************************************************\n");
+ printf("\n");
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("Global term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/example/time/.gitignore b/example/time/.gitignore
deleted file mode 100644
index 938c1aaed..000000000
--- a/example/time/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-odp_time_global
diff --git a/example/time/Makefile.am b/example/time/Makefile.am
deleted file mode 100644
index c1db37591..000000000
--- a/example/time/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include $(top_srcdir)/example/Makefile.inc
-
-bin_PROGRAMS = odp_time_global$(EXEEXT)
-odp_time_global_LDFLAGS = $(AM_LDFLAGS) -static
-odp_time_global_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
-
-dist_odp_time_global_SOURCES = time_global_test.c
diff --git a/example/time/time_global_test.c b/example/time/time_global_test.c
deleted file mode 100644
index e24e9f434..000000000
--- a/example/time/time_global_test.c
+++ /dev/null
@@ -1,364 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <inttypes.h>
-
-#include <odp_api.h>
-#include <example_debug.h>
-#include <odp/helper/odph_api.h>
-
-#define MAX_WORKERS 32
-#define ITERATION_NUM 2048
-#define LOG_BASE 8
-#define LOG_ENTRY_SIZE 19
-#define LOG_LINE_SIZE (LOG_BASE * LOG_ENTRY_SIZE + 1)
-
-#define QUEUE_NAME_PREFIX "thread_queue_"
-
-typedef struct {
- odp_time_t timestamp;
- int id;
-} timestamp_event_t;
-
-typedef struct {
- uint8_t thr;
- uint8_t id;
- odp_time_t time;
-} log_entry_t;
-
-typedef struct {
- uint32_t iteration_num;
- odp_atomic_u32_t iteration_counter;
- odp_atomic_u32_t id_counter;
- odp_atomic_u32_t log_counter;
- odp_atomic_u32_t err_counter;
- odp_barrier_t start_barrier;
- odp_barrier_t end_barrier;
- int thread_num;
- log_entry_t *log;
- int log_enries_num;
-} test_globals_t;
-
-static void print_log(test_globals_t *gbls)
-{
- uint32_t err_num;
- int i, j, k, pad;
- char line[LOG_LINE_SIZE];
-
- memset(line, '-', LOG_LINE_SIZE - 1);
- line[LOG_LINE_SIZE - 1] = 0;
- for (i = 1; i <= gbls->thread_num; i++) {
- printf("\n==== history of %d buffer, time,ns (thread) ====\n%s\n",
- i, line);
-
- /* print log for buffer */
- k = 0;
- for (j = 0; j < gbls->log_enries_num; j++)
- if (gbls->log[j].id == i) {
- printf("%10" PRIu64 " (%-3d)",
- odp_time_to_ns(gbls->log[j].time),
- gbls->log[j].thr);
-
- if (!(++k % LOG_BASE))
- printf(" |\n");
- else
- printf(" =>");
- }
-
- if ((k % LOG_BASE)) {
- pad = (LOG_BASE - k % LOG_BASE) * LOG_ENTRY_SIZE - 4;
- printf(" end%*c\n%s\n", pad, '|', line);
- } else {
- printf("%s\n", line);
- }
- }
-
- printf("\n\n");
-
- err_num = odp_atomic_load_u32(&gbls->err_counter);
- if (err_num)
- printf("Number of errors: %u\n", err_num);
-}
-
-static void
-generate_next_queue(test_globals_t *gbls, odp_queue_t *queue, unsigned int id)
-{
- int thr;
- unsigned int rand_id;
- char queue_name[sizeof(QUEUE_NAME_PREFIX) + 2];
-
- thr = odp_thread_id();
-
- /* generate next random id */
- do {
- odp_random_data((uint8_t *)&rand_id, sizeof(rand_id), 1);
- rand_id = rand_id % gbls->thread_num + 1;
- } while (rand_id == id);
-
- sprintf(queue_name, QUEUE_NAME_PREFIX "%d", rand_id);
- *queue = odp_queue_lookup(queue_name);
-
- if (ODP_QUEUE_INVALID == *queue)
- EXAMPLE_ABORT("Cannot lookup thread queue \"%s\", thread %d\n",
- queue_name, thr);
-}
-
-static void test_global_timestamps(test_globals_t *gbls,
- odp_queue_t queue, unsigned int id)
-{
- int thr;
- int log_entry;
- odp_event_t ev;
- odp_time_t time;
- odp_buffer_t buf;
- odp_queue_t queue_next;
- timestamp_event_t *timestamp_ev;
-
- thr = odp_thread_id();
- while (odp_atomic_load_u32(&gbls->iteration_counter) <
- gbls->iteration_num) {
- ev = odp_queue_deq(queue);
-
- if (ev == ODP_EVENT_INVALID)
- continue;
-
- buf = odp_buffer_from_event(ev);
- timestamp_ev = (timestamp_event_t *)odp_buffer_addr(buf);
-
- time = odp_time_global();
- if (odp_time_cmp(time, timestamp_ev->timestamp) < 0) {
- EXAMPLE_ERR("timestamp is less than previous time_prev=%"
- PRIu64 "ns, time_next=%"
- PRIu64 "ns, thread %d\n",
- odp_time_to_ns(timestamp_ev->timestamp),
- odp_time_to_ns(time), thr);
- odp_atomic_inc_u32(&gbls->err_counter);
- }
-
- /* update the log */
- log_entry = odp_atomic_fetch_inc_u32(&gbls->log_counter);
- gbls->log[log_entry].time = timestamp_ev->timestamp;
- gbls->log[log_entry].id = timestamp_ev->id;
- gbls->log[log_entry].thr = thr;
-
- /* assign new current time and send */
- generate_next_queue(gbls, &queue_next, id);
- timestamp_ev->timestamp = time;
- if (odp_queue_enq(queue_next, ev))
- EXAMPLE_ABORT("Cannot enqueue event %"
- PRIu64 " on queue %"
- PRIu64 ", thread %d\n",
- odp_event_to_u64(ev),
- odp_queue_to_u64(queue_next), thr);
-
- odp_atomic_inc_u32(&gbls->iteration_counter);
- }
-}
-
-/**
- * @internal Worker thread
- *
- * @param ptr Pointer to test arguments
- *
- * @return Pointer to exit status
- */
-static int run_thread(void *ptr)
-{
- int thr;
- uint32_t id;
- odp_event_t ev;
- odp_buffer_t buf;
- test_globals_t *gbls;
- odp_pool_t buffer_pool;
- odp_queue_t queue, queue_next;
- timestamp_event_t *timestamp_ev;
- char queue_name[sizeof(QUEUE_NAME_PREFIX) + 2];
-
- gbls = ptr;
- thr = odp_thread_id();
- printf("Thread %i starts on cpu %i\n", thr, odp_cpu_id());
-
- /*
- * Allocate own queue for receiving timestamps.
- * Own queue is needed to guarantee that next thread for receiving
- * buffer is not the same thread.
- */
- id = odp_atomic_fetch_inc_u32(&gbls->id_counter);
- sprintf(queue_name, QUEUE_NAME_PREFIX "%d", id);
- queue = odp_queue_create(queue_name, NULL);
- if (queue == ODP_QUEUE_INVALID)
- EXAMPLE_ABORT("Cannot create thread queue, thread %d", thr);
-
- /* allocate buffer for timestamp */
- buffer_pool = odp_pool_lookup("time buffers pool");
- if (buffer_pool == ODP_POOL_INVALID)
- EXAMPLE_ABORT("Buffer pool was not found, thread %d\n", thr);
-
- buf = odp_buffer_alloc(buffer_pool);
- if (buf == ODP_BUFFER_INVALID)
- EXAMPLE_ABORT("Buffer was not allocated, thread %d\n", thr);
-
- /* wait all threads allocated their queues */
- odp_barrier_wait(&gbls->start_barrier);
-
- /* enqueue global timestamp to some queue of some other thread */
- generate_next_queue(gbls, &queue_next, id);
-
- /* save global timestamp and id for tracing */
- ev = odp_buffer_to_event(buf);
- timestamp_ev = (timestamp_event_t *)odp_buffer_addr(buf);
- timestamp_ev->id = id;
- timestamp_ev->timestamp = odp_time_global();
- if (odp_queue_enq(queue_next, ev))
- EXAMPLE_ABORT("Cannot enqueue timestamp event %"
- PRIu64 " on queue %" PRIu64 ", thread %d",
- odp_event_to_u64(ev),
- odp_queue_to_u64(queue_next), thr);
-
- test_global_timestamps(gbls, queue, id);
-
- /* wait all threads are finished their jobs */
- odp_barrier_wait(&gbls->end_barrier);
-
- /* free all events on the allocated queue */
- while (1) {
- ev = odp_queue_deq(queue);
- if (ev == ODP_EVENT_INVALID)
- break;
-
- buf = odp_buffer_from_event(ev);
- odp_buffer_free(buf);
- }
-
- /* free allocated queue */
- if (odp_queue_destroy(queue))
- EXAMPLE_ABORT("Cannot destroy queue %" PRIu64 "",
- odp_queue_to_u64(queue));
-
- printf("Thread %i exits\n", thr);
- fflush(NULL);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- int err = 0;
- odp_pool_t pool = ODP_POOL_INVALID;
- int num_workers;
- test_globals_t *gbls;
- odp_cpumask_t cpumask;
- odp_pool_param_t params;
- odp_shm_t shm_glbls = ODP_SHM_INVALID;
- odp_shm_t shm_log = ODP_SHM_INVALID;
- int log_size, log_enries_num;
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, NULL, NULL);
-
- printf("\nODP global time test starts\n");
-
- if (odp_init_global(&instance, NULL, NULL)) {
- err = 1;
- EXAMPLE_ERR("ODP global init failed.\n");
- goto end;
- }
-
- /* Init this thread. */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- err = 1;
- EXAMPLE_ERR("ODP local init failed.\n");
- goto err_global;
- }
-
- num_workers = MAX_WORKERS;
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
-
- shm_glbls = odp_shm_reserve("test_globals", sizeof(test_globals_t),
- ODP_CACHE_LINE_SIZE, 0);
- if (ODP_SHM_INVALID == shm_glbls) {
- err = 1;
- EXAMPLE_ERR("Error: shared mem reserve failed.\n");
- goto err;
- }
-
- log_enries_num = num_workers * (ITERATION_NUM + num_workers);
- log_size = sizeof(log_entry_t) * log_enries_num;
- shm_log = odp_shm_reserve("test_log", log_size, ODP_CACHE_LINE_SIZE, 0);
- if (ODP_SHM_INVALID == shm_log) {
- err = 1;
- EXAMPLE_ERR("Error: shared mem reserve failed.\n");
- goto err;
- }
-
- gbls = odp_shm_addr(shm_glbls);
- gbls->thread_num = num_workers;
- gbls->iteration_num = ITERATION_NUM;
- odp_atomic_store_u32(&gbls->iteration_counter, 0);
- odp_atomic_store_u32(&gbls->id_counter, 1);
- odp_atomic_store_u32(&gbls->log_counter, 0);
- odp_atomic_store_u32(&gbls->err_counter, 0);
- gbls->log_enries_num = log_enries_num;
- gbls->log = odp_shm_addr(shm_log);
- odp_barrier_init(&gbls->start_barrier, num_workers);
- odp_barrier_init(&gbls->end_barrier, num_workers);
- memset(gbls->log, 0, log_size);
-
- params.buf.size = sizeof(timestamp_event_t);
- params.buf.align = ODP_CACHE_LINE_SIZE;
- params.buf.num = num_workers;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create("time buffers pool", &params);
- if (pool == ODP_POOL_INVALID) {
- err = 1;
- EXAMPLE_ERR("Pool create failed.\n");
- goto err;
- }
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_thread;
- thr_params.arg = gbls;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- /* Create and launch worker threads */
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-
- /* Wait for worker threads to exit */
- odph_odpthreads_join(thread_tbl);
-
- print_log(gbls);
-
-err:
- if (pool != ODP_POOL_INVALID)
- if (odp_pool_destroy(pool))
- err = 1;
-
- if (shm_log != ODP_SHM_INVALID)
- if (odp_shm_free(shm_log))
- err = 1;
-
- if (shm_glbls != ODP_SHM_INVALID)
- if (odp_shm_free(shm_glbls))
- err = 1;
-
- if (odp_term_local())
- err = 1;
-err_global:
- if (odp_term_global(instance))
- err = 1;
-end:
- if (err) {
- EXAMPLE_ERR("Err: ODP global time test failed\n\n");
- return -1;
- }
-
- printf("ODP global time test complete\n\n");
- return 0;
-}
diff --git a/example/timer/.gitignore b/example/timer/.gitignore
index eaa9a3619..9502b573b 100644
--- a/example/timer/.gitignore
+++ b/example/timer/.gitignore
@@ -1,4 +1,3 @@
*.log
*.trs
-odp_timer_test
odp_timer_simple
diff --git a/example/timer/Makefile.am b/example/timer/Makefile.am
index edb8b2ca2..007a2842b 100644
--- a/example/timer/Makefile.am
+++ b/example/timer/Makefile.am
@@ -1,18 +1,9 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_timer_test$(EXEEXT) \
- odp_timer_simple$(EXEEXT)
-odp_timer_test_LDFLAGS = $(AM_LDFLAGS) -static
-odp_timer_test_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-dist_odp_timer_test_SOURCES = odp_timer_test.c
+bin_PROGRAMS = odp_timer_simple
-odp_timer_simple_LDFLAGS = $(AM_LDFLAGS) -static
-odp_timer_simple_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-dist_odp_timer_simple_SOURCES = odp_timer_simple.c
+odp_timer_simple_SOURCES = odp_timer_simple.c
if test_example
-TESTS = odp_timer_simple
+TESTS = odp_timer_simple
endif
-
-noinst_HEADERS = \
- $(top_srcdir)/example/example_debug.h
diff --git a/example/timer/odp_timer_simple.c b/example/timer/odp_timer_simple.c
index 70804bb7b..ceba66c62 100644
--- a/example/timer/odp_timer_simple.c
+++ b/example/timer/odp_timer_simple.c
@@ -1,23 +1,24 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
+
/**
- * @file
+ * @example odp_timer_simple.c
+ *
+ * Minimal example application demonstrating ODP timer API usage
*
- * @example odp_timer_simple.c ODP simple example to schedule timer
- * action for 1 second.
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
*/
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
-#include <example_debug.h>
/* ODP main header */
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
@@ -35,6 +36,7 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
uint64_t tick;
odp_timeout_t tmo;
int ret = 0;
+ odp_timer_capability_t timer_capa;
/*
* Init ODP app
@@ -61,24 +63,40 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
/*
* Create pool of timeouts
*/
- tparams.res_ns = 10 * ODP_TIME_MSEC_IN_NS;
+ if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
+ ret += 1;
+ goto err_tp;
+ }
+
+ odp_timer_pool_param_init(&tparams);
+ tparams.res_ns = ODPH_MAX(10 * ODP_TIME_MSEC_IN_NS,
+ timer_capa.highest_res_ns);
tparams.min_tmo = 10 * ODP_TIME_MSEC_IN_NS;
tparams.max_tmo = 1 * ODP_TIME_SEC_IN_NS;
tparams.num_timers = 1; /* One timer per worker */
tparams.priv = 0; /* Shared */
- tparams.clk_src = ODP_CLOCK_CPU;
+ tparams.clk_src = ODP_CLOCK_DEFAULT;
timer_pool = odp_timer_pool_create("timer_pool", &tparams);
if (timer_pool == ODP_TIMER_POOL_INVALID) {
ret += 1;
goto err;
}
+ if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ ret += 1;
+ goto err;
+ }
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
/*
* Create a queue for timer test
*/
odp_queue_param_init(&qparam);
qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.prio = odp_schedule_default_prio();
qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
qparam.sched.group = ODP_SCHED_GROUP_ALL;
@@ -90,22 +108,21 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
tim = odp_timer_alloc(timer_pool, queue, NULL);
if (tim == ODP_TIMER_INVALID) {
- EXAMPLE_ERR("Failed to allocate timer\n");
+ ODPH_ERR("Failed to allocate timer\n");
ret += 1;
goto err;
}
tmo = odp_timeout_alloc(timeout_pool);
if (tmo == ODP_TIMEOUT_INVALID) {
- EXAMPLE_ERR("Failed to allocate timeout\n");
+ ODPH_ERR("Failed to allocate timeout\n");
return -1;
}
ev = odp_timeout_to_event(tmo);
- /* Calculate period for timer in uint64_t value, in current case
- * we will schedule timer for 1 second */
- period = odp_timer_ns_to_tick(timer_pool, 1 * ODP_TIME_SEC_IN_NS);
+ /* Calculate timer period in ticks */
+ period = odp_timer_ns_to_tick(timer_pool, 100 * ODP_TIME_MSEC_IN_NS);
/* Wait time to return from odp_schedule() if there are no
* events
@@ -114,45 +131,56 @@ int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
for (i = 0; i < 5; i++) {
odp_time_t time;
+ odp_timer_start_t start_param;
/* Program timeout action on current tick + period */
tick = odp_timer_current_tick(timer_pool);
- rc = odp_timer_set_abs(tim, tick + period, &ev);
+
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick = tick + period;
+ start_param.tmo_ev = ev;
+
+ rc = odp_timer_start(tim, &start_param);
/* Too early or too late timeout requested */
if (odp_unlikely(rc != ODP_TIMER_SUCCESS))
- EXAMPLE_ABORT("odp_timer_set_abs() failed: %d\n",
- rc);
+ ODPH_ABORT("odp_timer_start() failed: %d\n", rc);
/* Wait for 2 seconds for timeout action to be generated */
ev = odp_schedule(&queue, sched_tmo);
if (ev == ODP_EVENT_INVALID)
- EXAMPLE_ABORT("Invalid event\n");
+ ODPH_ABORT("Invalid event\n");
if (odp_event_type(ev) != ODP_EVENT_TIMEOUT)
- EXAMPLE_ABORT("Unexpected event type (%u) received\n",
- odp_event_type(ev));
+ ODPH_ABORT("Unexpected event type (%u) received\n",
+ odp_event_type(ev));
time = odp_time_global();
printf("timer tick %d, time ns %" PRIu64 "\n",
i, odp_time_to_ns(time));
/* Do not free current event, just go back to loop and program
- * timeout to next second.
+ * the next timeout.
*/
}
/* Destroy created resources */
- rc += odp_timer_cancel(tim, &ev);
- rc += -(odp_timer_free(tim) == ODP_EVENT_INVALID);
odp_event_free(ev);
- ret += odp_queue_destroy(queue);
+ if (odp_timer_free(tim))
+ ret++;
+
+ if (odp_queue_destroy(queue))
+ ret++;
err:
odp_timer_pool_destroy(timer_pool);
err_tp:
- ret += odp_pool_destroy(timeout_pool);
- ret += odp_term_local();
+ if (odp_pool_destroy(timeout_pool))
+ ret++;
+
+ if (odp_term_local())
+ ret++;
err_local:
- ret += odp_term_global(instance);
+ if (odp_term_global(instance))
+ ret++;
err_global:
return ret;
}
diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
deleted file mode 100644
index 2196b8c1f..000000000
--- a/example/timer/odp_timer_test.c
+++ /dev/null
@@ -1,544 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include <example_debug.h>
-
-/* ODP main header */
-#include <odp_api.h>
-
-/* ODP helper for Linux apps */
-#include <odp/helper/odph_api.h>
-
-/* GNU lib C */
-#include <getopt.h>
-
-
-#define MAX_WORKERS 32 /**< Max worker threads */
-#define NUM_TMOS 10000 /**< Number of timers */
-#define WAIT_NUM 10 /**< Max tries to rx last tmo per worker */
-
-
-/** Test arguments */
-typedef struct {
- int cpu_count; /**< CPU count*/
- int resolution_us; /**< Timeout resolution in usec*/
- int min_us; /**< Minimum timeout in usec*/
- int max_us; /**< Maximum timeout in usec*/
- int period_us; /**< Timeout period in usec*/
- int tmo_count; /**< Timeout count*/
-} test_args_t;
-
-/** @private Helper struct for timers */
-struct test_timer {
- odp_timer_t tim;
- odp_event_t ev;
-};
-
-/** Test global variables */
-typedef struct {
- test_args_t args; /**< Test argunments*/
- odp_barrier_t test_barrier; /**< Barrier for test synchronisation*/
- odp_pool_t pool; /**< pool handle*/
- odp_timer_pool_t tp; /**< Timer pool handle*/
- odp_atomic_u32_t remain; /**< Number of timeouts to receive*/
- struct test_timer tt[256]; /**< Array of all timer helper structs*/
- uint32_t num_workers; /**< Number of threads */
-} test_globals_t;
-
-/** @private Timer set status ASCII strings */
-static const char *timerset2str(odp_timer_set_t val)
-{
- switch (val) {
- case ODP_TIMER_SUCCESS:
- return "success";
- case ODP_TIMER_TOOEARLY:
- return "too early";
- case ODP_TIMER_TOOLATE:
- return "too late";
- case ODP_TIMER_NOEVENT:
- return "no event";
- default:
- return "?";
- }
-};
-
-/** @private test timeout */
-static void remove_prescheduled_events(void)
-{
- odp_event_t ev;
- odp_queue_t queue;
- odp_schedule_pause();
- while ((ev = odp_schedule(&queue, ODP_SCHED_NO_WAIT)) !=
- ODP_EVENT_INVALID) {
- odp_event_free(ev);
- }
-}
-
-/** @private test timeout */
-static void test_abs_timeouts(int thr, test_globals_t *gbls)
-{
- uint64_t period;
- uint64_t period_ns;
- odp_queue_t queue;
- uint64_t tick;
- struct test_timer *ttp;
- odp_timeout_t tmo;
- uint32_t num_workers = gbls->num_workers;
-
- EXAMPLE_DBG(" [%i] test_timeouts\n", thr);
-
- queue = odp_queue_lookup("timer_queue");
-
- period_ns = gbls->args.period_us * ODP_TIME_USEC_IN_NS;
- period = odp_timer_ns_to_tick(gbls->tp, period_ns);
-
- EXAMPLE_DBG(" [%i] period %"PRIu64" ticks, %"PRIu64" ns\n", thr,
- period, period_ns);
-
- EXAMPLE_DBG(" [%i] current tick %"PRIu64"\n", thr,
- odp_timer_current_tick(gbls->tp));
-
- ttp = &gbls->tt[thr];
- ttp->tim = odp_timer_alloc(gbls->tp, queue, ttp);
- if (ttp->tim == ODP_TIMER_INVALID) {
- EXAMPLE_ERR("Failed to allocate timer\n");
- return;
- }
- tmo = odp_timeout_alloc(gbls->pool);
- if (tmo == ODP_TIMEOUT_INVALID) {
- EXAMPLE_ERR("Failed to allocate timeout\n");
- return;
- }
- ttp->ev = odp_timeout_to_event(tmo);
- tick = odp_timer_current_tick(gbls->tp);
-
- while (1) {
- int wait = 0;
- odp_event_t ev;
- odp_timer_set_t rc;
-
- if (ttp) {
- tick += period;
- rc = odp_timer_set_abs(ttp->tim, tick, &ttp->ev);
- if (odp_unlikely(rc != ODP_TIMER_SUCCESS)) {
- /* Too early or too late timeout requested */
- EXAMPLE_ABORT("odp_timer_set_abs() failed: %s\n",
- timerset2str(rc));
- }
- }
-
- /* Get the next expired timeout.
- * We invoke the scheduler in a loop with a timeout because
- * we are not guaranteed to receive any more timeouts. The
- * scheduler isn't guaranteeing fairness when scheduling
- * buffers to threads.
- * Use 1.5 second timeout for scheduler */
- uint64_t sched_tmo =
- odp_schedule_wait_time(1500000000ULL);
- do {
- ev = odp_schedule(&queue, sched_tmo);
- /* Check if odp_schedule() timed out, possibly there
- * are no remaining timeouts to receive */
- if (++wait > WAIT_NUM &&
- odp_atomic_load_u32(&gbls->remain) < num_workers)
- EXAMPLE_ABORT("At least one TMO was lost\n");
- } while (ev == ODP_EVENT_INVALID &&
- (int)odp_atomic_load_u32(&gbls->remain) > 0);
-
- if (ev == ODP_EVENT_INVALID)
- break; /* No more timeouts */
- if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) {
- /* Not a default timeout event */
- EXAMPLE_ABORT("Unexpected event type (%u) received\n",
- odp_event_type(ev));
- }
- odp_timeout_t tmo = odp_timeout_from_event(ev);
- tick = odp_timeout_tick(tmo);
- ttp = odp_timeout_user_ptr(tmo);
- ttp->ev = ev;
- if (!odp_timeout_fresh(tmo)) {
- /* Not the expected expiration tick, timer has
- * been reset or cancelled or freed */
- EXAMPLE_ABORT("Unexpected timeout received (timer "
- "%" PRIu64", tick %" PRIu64 ")\n",
- odp_timer_to_u64(ttp->tim), tick);
- }
- EXAMPLE_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick);
-
- uint32_t rx_num = odp_atomic_fetch_dec_u32(&gbls->remain);
-
- if (!rx_num)
- EXAMPLE_ABORT("Unexpected timeout received (timer "
- "%" PRIu64 ", tick %" PRIu64 ")\n",
- odp_timer_to_u64(ttp->tim), tick);
- else if (rx_num > num_workers)
- continue;
-
- odp_event_free(ttp->ev);
- odp_timer_free(ttp->tim);
- ttp = NULL;
- }
-
- /* Remove any prescheduled events */
- remove_prescheduled_events();
-}
-
-
-/**
- * @internal Worker thread
- *
- * @param ptr Pointer to test arguments
- *
- * @return Pointer to exit status
- */
-static int run_thread(void *ptr)
-{
- int thr;
- odp_pool_t msg_pool;
- test_globals_t *gbls;
-
- gbls = ptr;
- thr = odp_thread_id();
-
- printf("Thread %i starts on cpu %i\n", thr, odp_cpu_id());
-
- /*
- * Find the pool
- */
- msg_pool = odp_pool_lookup("msg_pool");
-
- if (msg_pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR(" [%i] msg_pool not found\n", thr);
- return -1;
- }
-
- odp_barrier_wait(&gbls->test_barrier);
-
- test_abs_timeouts(thr, gbls);
-
-
- printf("Thread %i exits\n", thr);
- fflush(NULL);
- return 0;
-}
-
-
-/**
- * @internal Print help
- */
-static void print_usage(void)
-{
- printf("\n\nUsage: ./odp_example [options]\n");
- printf("Options:\n");
- printf(" -c, --count <number> CPU count\n");
- printf(" -r, --resolution <us> timeout resolution in usec\n");
- printf(" -m, --min <us> minimum timeout in usec\n");
- printf(" -x, --max <us> maximum timeout in usec\n");
- printf(" -p, --period <us> timeout period in usec\n");
- printf(" -t, --timeouts <count> timeout repeat count\n");
- printf(" -h, --help this help\n");
- printf("\n\n");
-}
-
-
-/**
- * @internal Parse arguments
- *
- * @param argc Argument count
- * @param argv Argument vector
- * @param args Test arguments
- */
-static void parse_args(int argc, char *argv[], test_args_t *args)
-{
- int opt;
- int long_index;
-
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"resolution", required_argument, NULL, 'r'},
- {"min", required_argument, NULL, 'm'},
- {"max", required_argument, NULL, 'x'},
- {"period", required_argument, NULL, 'p'},
- {"timeouts", required_argument, NULL, 't'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:r:m:x:p:t:h";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- /* defaults */
- args->cpu_count = 0; /* all CPU's */
- args->resolution_us = 10000;
- args->min_us = 0;
- args->max_us = 10000000;
- args->period_us = 1000000;
- args->tmo_count = 30;
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'c':
- args->cpu_count = atoi(optarg);
- break;
- case 'r':
- args->resolution_us = atoi(optarg);
- break;
- case 'm':
- args->min_us = atoi(optarg);
- break;
- case 'x':
- args->max_us = atoi(optarg);
- break;
- case 'p':
- args->period_us = atoi(optarg);
- break;
- case 't':
- args->tmo_count = atoi(optarg);
- break;
- case 'h':
- print_usage();
- exit(EXIT_SUCCESS);
- break;
-
- default:
- break;
- }
- }
-
- if (args->period_us < args->resolution_us)
- printf("\n\tWarn: timeout is set less then resolution\n");
-}
-
-
-/**
- * Test main function
- */
-int main(int argc, char *argv[])
-{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- int num_workers;
- odp_queue_t queue;
- uint64_t tick, ns;
- odp_queue_param_t param;
- odp_pool_param_t params;
- odp_timer_pool_param_t tparams;
- odp_timer_pool_info_t tpinfo;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
- odp_shm_t shm = ODP_SHM_INVALID;
- test_globals_t *gbls = NULL;
- int err = 0;
-
- printf("\nODP timer example starts\n");
-
- if (odp_init_global(&instance, NULL, NULL)) {
- err = 1;
- printf("ODP global init failed.\n");
- goto err_global;
- }
-
- /* Init this thread. */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- err = 1;
- printf("ODP local init failed.\n");
- goto err_local;
- }
-
- printf("\n");
- printf("ODP system info\n");
- printf("---------------\n");
- printf("ODP API version: %s\n", odp_version_api_str());
- printf("CPU model: %s\n", odp_cpu_model_str());
- printf("CPU freq (hz): %"PRIu64"\n", odp_cpu_hz_max());
- printf("Cache line size: %i\n", odp_sys_cache_line_size());
- printf("Max CPU count: %i\n", odp_cpu_count());
-
- printf("\n");
-
- /* Reserve memory for test_globals_t from shared mem */
- shm = odp_shm_reserve("shm_test_globals", sizeof(test_globals_t),
- ODP_CACHE_LINE_SIZE, 0);
- if (ODP_SHM_INVALID == shm) {
- err = 1;
- EXAMPLE_ERR("Error: shared mem reserve failed.\n");
- goto err;
- }
-
- gbls = odp_shm_addr(shm);
- if (NULL == gbls) {
- err = 1;
- EXAMPLE_ERR("Error: shared mem alloc failed.\n");
- goto err;
- }
- memset(gbls, 0, sizeof(test_globals_t));
- gbls->pool = ODP_POOL_INVALID;
- gbls->tp = ODP_TIMER_POOL_INVALID;
-
- parse_args(argc, argv, &gbls->args);
-
- memset(thread_tbl, 0, sizeof(thread_tbl));
-
- /* Default to system CPU count unless user specified */
- num_workers = MAX_WORKERS;
- if (gbls->args.cpu_count)
- num_workers = gbls->args.cpu_count;
-
- /* Get default worker cpumask */
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- printf("num worker threads: %i\n", num_workers);
- printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n", cpumaskstr);
-
- printf("resolution: %i usec\n", gbls->args.resolution_us);
- printf("min timeout: %i usec\n", gbls->args.min_us);
- printf("max timeout: %i usec\n", gbls->args.max_us);
- printf("period: %i usec\n", gbls->args.period_us);
- printf("timeouts: %i\n", gbls->args.tmo_count);
-
- /*
- * Create pool for timeouts
- */
- odp_pool_param_init(&params);
- params.tmo.num = NUM_TMOS;
- params.type = ODP_POOL_TIMEOUT;
-
- gbls->pool = odp_pool_create("msg_pool", &params);
-
- if (gbls->pool == ODP_POOL_INVALID) {
- err = 1;
- EXAMPLE_ERR("Pool create failed.\n");
- goto err;
- }
-
- tparams.res_ns = gbls->args.resolution_us * ODP_TIME_USEC_IN_NS;
- tparams.min_tmo = gbls->args.min_us * ODP_TIME_USEC_IN_NS;
- tparams.max_tmo = gbls->args.max_us * ODP_TIME_USEC_IN_NS;
- tparams.num_timers = num_workers; /* One timer per worker */
- tparams.priv = 0; /* Shared */
- tparams.clk_src = ODP_CLOCK_CPU;
- gbls->tp = odp_timer_pool_create("timer_pool", &tparams);
- if (gbls->tp == ODP_TIMER_POOL_INVALID) {
- err = 1;
- EXAMPLE_ERR("Timer pool create failed.\n");
- goto err;
- }
- odp_timer_pool_start();
-
- odp_shm_print_all();
- (void)odp_timer_pool_info(gbls->tp, &tpinfo);
- printf("Timer pool\n");
- printf("----------\n");
- printf(" name: %s\n", tpinfo.name);
- printf(" resolution: %"PRIu64" ns\n", tpinfo.param.res_ns);
- printf(" min tmo: %"PRIu64" ticks\n", tpinfo.param.min_tmo);
- printf(" max tmo: %"PRIu64" ticks\n", tpinfo.param.max_tmo);
- printf("\n");
-
- /*
- * Create a queue for timer test
- */
- odp_queue_param_init(&param);
- param.type = ODP_QUEUE_TYPE_SCHED;
- param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- param.sched.group = ODP_SCHED_GROUP_ALL;
-
- queue = odp_queue_create("timer_queue", &param);
-
- if (queue == ODP_QUEUE_INVALID) {
- err = 1;
- EXAMPLE_ERR("Timer queue create failed.\n");
- goto err;
- }
-
- printf("CPU freq %"PRIu64" Hz\n", odp_cpu_hz_max());
- printf("Timer ticks vs nanoseconds:\n");
- ns = 0;
- tick = odp_timer_ns_to_tick(gbls->tp, ns);
-
- printf(" %12" PRIu64 " ns -> %12" PRIu64 " ticks\n", ns, tick);
- printf(" %12" PRIu64 " ticks -> %12" PRIu64 " ns\n", tick,
- odp_timer_tick_to_ns(gbls->tp, tick));
-
- for (ns = 1; ns <= 100 * ODP_TIME_SEC_IN_NS; ns *= 10) {
- tick = odp_timer_ns_to_tick(gbls->tp, ns);
-
- printf(" %12" PRIu64 " ns -> %12" PRIu64 " ticks\n", ns,
- tick);
- printf(" %12" PRIu64 " ticks -> %12" PRIu64 " ns\n", tick,
- odp_timer_tick_to_ns(gbls->tp, tick));
- }
-
- printf("\n");
-
- gbls->num_workers = num_workers;
-
- /* Initialize number of timeouts to receive */
- odp_atomic_init_u32(&gbls->remain, gbls->args.tmo_count * num_workers);
-
- /* Barrier to sync test case execution */
- odp_barrier_init(&gbls->test_barrier, num_workers);
-
- /* Create and launch worker threads */
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_thread;
- thr_params.arg = gbls;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-
- /* Wait for worker threads to exit */
- odph_odpthreads_join(thread_tbl);
-
- /* free resources */
- if (odp_queue_destroy(queue))
- err = 1;
-
-err:
-
- if (gbls != NULL && gbls->tp != ODP_TIMER_POOL_INVALID)
- odp_timer_pool_destroy(gbls->tp);
-
- if (gbls != NULL && gbls->pool != ODP_POOL_INVALID)
- if (odp_pool_destroy(gbls->pool))
- err = 1;
-
- if (shm != ODP_SHM_INVALID)
- if (odp_shm_free(shm))
- err = 1;
-
- if (odp_term_local())
- err = 1;
-err_local:
- if (odp_term_global(instance))
- err = 1;
-err_global:
- if (err) {
- printf("Err: ODP timer test failed\n\n");
- return -1;
- }
-
- printf("ODP timer test complete\n\n");
- return 0;
-}
diff --git a/example/traffic_mgmt/.gitignore b/example/traffic_mgmt/.gitignore
index 9e742f0d8..9902e7029 100644
--- a/example/traffic_mgmt/.gitignore
+++ b/example/traffic_mgmt/.gitignore
@@ -1 +1,3 @@
-odp_traffic_mgmt \ No newline at end of file
+*.log
+*.trs
+odp_traffic_mgmt
diff --git a/example/traffic_mgmt/Makefile.am b/example/traffic_mgmt/Makefile.am
index c8ff79755..999fd8d40 100644
--- a/example/traffic_mgmt/Makefile.am
+++ b/example/traffic_mgmt/Makefile.am
@@ -1,9 +1,9 @@
include $(top_srcdir)/example/Makefile.inc
-bin_PROGRAMS = odp_traffic_mgmt$(EXEEXT)
-odp_traffic_mgmt_LDFLAGS = $(AM_LDFLAGS) -static
-odp_traffic_mgmt_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+bin_PROGRAMS = odp_traffic_mgmt
-noinst_HEADERS = $(top_srcdir)/example/example_debug.h
+if test_example
+TESTS = odp_traffic_mgmt
+endif
-dist_odp_traffic_mgmt_SOURCES = odp_traffic_mgmt.c
+odp_traffic_mgmt_SOURCES = odp_traffic_mgmt.c
diff --git a/example/traffic_mgmt/odp_traffic_mgmt.c b/example/traffic_mgmt/odp_traffic_mgmt.c
index c83b61522..4ed4f2044 100644
--- a/example/traffic_mgmt/odp_traffic_mgmt.c
+++ b/example/traffic_mgmt/odp_traffic_mgmt.c
@@ -1,20 +1,32 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Marvell
*/
+ /**
+ * @example odp_traffic_mgmt.c
+ *
+ * Traffic manager API example application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
-#include <unistd.h>
-#include <signal.h>
+#include <execinfo.h>
#include <inttypes.h>
+#include <signal.h>
#include <sys/resource.h>
-#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
#include <odp_api.h>
-#include <example_debug.h>
+#include <odp/helper/odph_api.h>
#define NUM_SVC_CLASSES 4
#define USERS_PER_SVC_CLASS 2
@@ -33,9 +45,6 @@
#define FALSE 0
#define TRUE 1
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-
#define RANDOM_BUF_LEN 1024
typedef struct {
@@ -57,8 +66,8 @@ static const odp_init_t ODP_INIT_PARAMS = {
static profile_params_set_t COMPANY_PROFILE_PARAMS = {
.shaper_params = {
- .commit_bps = 50 * MBPS, .commit_burst = 1000000,
- .peak_bps = 0, .peak_burst = 0,
+ .commit_rate = 50 * MBPS, .commit_burst = 1000000,
+ .peak_rate = 0, .peak_burst = 0,
.dual_rate = FALSE, .shaper_len_adjust = 20
},
@@ -68,7 +77,16 @@ static profile_params_set_t COMPANY_PROFILE_PARAMS = {
},
.wred_params = {
- [ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+ [ODP_PACKET_GREEN] = {
+ .min_threshold = PERCENT(70),
+ .med_threshold = PERCENT(90),
+ .med_drop_prob = PERCENT(80),
+ .max_drop_prob = PERCENT(100),
+ .enable_wred = TRUE,
+ .use_byte_fullness = FALSE,
+ },
+
+ [ODP_PACKET_YELLOW] = {
.min_threshold = PERCENT(70),
.med_threshold = PERCENT(90),
.med_drop_prob = PERCENT(80),
@@ -90,9 +108,9 @@ static profile_params_set_t COMPANY_PROFILE_PARAMS = {
static profile_params_set_t COS0_PROFILE_PARAMS = {
.shaper_params = {
- .commit_bps = 1 * MBPS, .commit_burst = 100000,
- .peak_bps = 4 * MBPS, .peak_burst = 200000,
- .dual_rate = FALSE, .shaper_len_adjust = 20
+ .commit_rate = 1 * MBPS, .commit_burst = 100000,
+ .peak_rate = 4 * MBPS, .peak_burst = 200000,
+ .dual_rate = TRUE, .shaper_len_adjust = 20
},
.threshold_params = {
@@ -101,7 +119,16 @@ static profile_params_set_t COS0_PROFILE_PARAMS = {
},
.wred_params = {
- [ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+ [ODP_PACKET_GREEN] = {
+ .min_threshold = PERCENT(80),
+ .med_threshold = PERCENT(90),
+ .med_drop_prob = PERCENT(50),
+ .max_drop_prob = PERCENT(100),
+ .enable_wred = TRUE,
+ .use_byte_fullness = FALSE,
+ },
+
+ [ODP_PACKET_YELLOW] = {
.min_threshold = PERCENT(80),
.med_threshold = PERCENT(90),
.med_drop_prob = PERCENT(50),
@@ -123,9 +150,9 @@ static profile_params_set_t COS0_PROFILE_PARAMS = {
static profile_params_set_t COS1_PROFILE_PARAMS = {
.shaper_params = {
- .commit_bps = 500 * KBPS, .commit_burst = 50000,
- .peak_bps = 1500 * KBPS, .peak_burst = 150000,
- .dual_rate = FALSE, .shaper_len_adjust = 20
+ .commit_rate = 500 * KBPS, .commit_burst = 50000,
+ .peak_rate = 1500 * KBPS, .peak_burst = 150000,
+ .dual_rate = TRUE, .shaper_len_adjust = 20
},
.threshold_params = {
@@ -134,7 +161,16 @@ static profile_params_set_t COS1_PROFILE_PARAMS = {
},
.wred_params = {
- [ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+ [ODP_PACKET_GREEN] = {
+ .min_threshold = PERCENT(40),
+ .med_threshold = PERCENT(90),
+ .med_drop_prob = PERCENT(70),
+ .max_drop_prob = PERCENT(100),
+ .enable_wred = TRUE,
+ .use_byte_fullness = FALSE,
+ },
+
+ [ODP_PACKET_YELLOW] = {
.min_threshold = PERCENT(40),
.med_threshold = PERCENT(90),
.med_drop_prob = PERCENT(70),
@@ -156,9 +192,9 @@ static profile_params_set_t COS1_PROFILE_PARAMS = {
static profile_params_set_t COS2_PROFILE_PARAMS = {
.shaper_params = {
- .commit_bps = 200 * KBPS, .commit_burst = 20000,
- .peak_bps = 400 * KBPS, .peak_burst = 40000,
- .dual_rate = FALSE, .shaper_len_adjust = 20
+ .commit_rate = 200 * KBPS, .commit_burst = 20000,
+ .peak_rate = 400 * KBPS, .peak_burst = 40000,
+ .dual_rate = TRUE, .shaper_len_adjust = 20
},
.threshold_params = {
@@ -167,7 +203,16 @@ static profile_params_set_t COS2_PROFILE_PARAMS = {
},
.wred_params = {
- [ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+ [ODP_PACKET_GREEN] = {
+ .min_threshold = PERCENT(50),
+ .med_threshold = PERCENT(80),
+ .med_drop_prob = PERCENT(70),
+ .max_drop_prob = PERCENT(100),
+ .enable_wred = TRUE,
+ .use_byte_fullness = FALSE,
+ },
+
+ [ODP_PACKET_YELLOW] = {
.min_threshold = PERCENT(50),
.med_threshold = PERCENT(80),
.med_drop_prob = PERCENT(70),
@@ -189,8 +234,8 @@ static profile_params_set_t COS2_PROFILE_PARAMS = {
static profile_params_set_t COS3_PROFILE_PARAMS = {
.shaper_params = {
- .commit_bps = 100 * KBPS, .commit_burst = 5000,
- .peak_bps = 0, .peak_burst = 0,
+ .commit_rate = 100 * KBPS, .commit_burst = 5000,
+ .peak_rate = 0, .peak_burst = 0,
.dual_rate = FALSE, .shaper_len_adjust = 20
},
@@ -200,7 +245,16 @@ static profile_params_set_t COS3_PROFILE_PARAMS = {
},
.wred_params = {
- [ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+ [ODP_PACKET_GREEN] = {
+ .min_threshold = PERCENT(40),
+ .med_threshold = PERCENT(70),
+ .med_drop_prob = PERCENT(80),
+ .max_drop_prob = PERCENT(100),
+ .enable_wred = TRUE,
+ .use_byte_fullness = FALSE,
+ },
+
+ [ODP_PACKET_YELLOW] = {
.min_threshold = PERCENT(40),
.med_threshold = PERCENT(70),
.med_drop_prob = PERCENT(80),
@@ -229,7 +283,7 @@ static odp_tm_t odp_tm_test;
static odp_pool_t odp_pool;
-static odp_tm_queue_t queue_num_tbls[NUM_SVC_CLASSES][TM_QUEUES_PER_CLASS + 1];
+static odp_tm_queue_t queue_num_tbls[NUM_SVC_CLASSES][TM_QUEUES_PER_CLASS];
static uint32_t next_queue_nums[NUM_SVC_CLASSES];
static uint8_t random_buf[RANDOM_BUF_LEN];
@@ -238,11 +292,44 @@ static uint32_t next_rand_byte;
static odp_atomic_u32_t atomic_pkts_into_tm;
static odp_atomic_u32_t atomic_pkts_from_tm;
-static uint32_t g_num_pkts_to_send = 1000;
+static uint32_t g_num_pkts_to_send = 100;
static uint8_t g_print_tm_stats = TRUE;
static void tester_egress_fcn(odp_packet_t odp_pkt);
+static uint64_t tm_shaper_min_rate;
+static uint64_t tm_shaper_max_rate;
+static uint32_t tm_shaper_min_burst;
+static uint32_t tm_shaper_max_burst;
+
+static uint64_t
+clamp_rate(uint64_t rate)
+{
+ uint64_t val = ODPH_MIN(ODPH_MAX(rate, tm_shaper_min_rate), tm_shaper_max_rate);
+
+ if (!rate)
+ return 0;
+
+ if (val != rate)
+ printf("INFO: Clamped shaper rate from %" PRIu64 " bps"
+ " to %" PRIu64 " bps\n", rate, val);
+ return val;
+}
+
+static uint32_t
+clamp_burst(uint32_t burst)
+{
+ uint32_t val = ODPH_MIN(ODPH_MAX(burst, tm_shaper_min_burst), tm_shaper_max_burst);
+
+ if (!burst)
+ return 0;
+
+ if (val != burst)
+ printf("INFO: Clamped shaper burst from %" PRIu32 "bits to %" PRIu32 "bits\n",
+ burst, val);
+ return val;
+}
+
/* Returns the number of errors encountered. */
static uint32_t create_profile_set(profile_params_set_t *profile_params_set,
@@ -267,10 +354,14 @@ static uint32_t create_profile_set(profile_params_set_t *profile_params_set,
odp_tm_shaper_params_init(&shaper_params);
shaper = &profile_params_set->shaper_params;
- shaper_params.commit_bps = shaper->commit_bps * shaper_scale;
- shaper_params.peak_bps = shaper->peak_bps * shaper_scale;
- shaper_params.commit_burst = shaper->commit_burst * shaper_scale;
- shaper_params.peak_burst = shaper->peak_burst * shaper_scale;
+ shaper_params.commit_rate = clamp_rate(shaper->commit_rate *
+ shaper_scale);
+ shaper_params.peak_rate = clamp_rate(shaper->peak_rate *
+ shaper_scale);
+ shaper_params.commit_burst = clamp_burst(shaper->commit_burst *
+ shaper_scale);
+ shaper_params.peak_burst = clamp_burst(shaper->peak_burst *
+ shaper_scale);
shaper_params.dual_rate = shaper->dual_rate;
shaper_params.shaper_len_adjust = shaper->shaper_len_adjust;
profile_set->shaper_profile = odp_tm_shaper_create(name,
@@ -433,7 +524,7 @@ static int config_example_user(odp_tm_node_t cos_tm_node,
return rc;
svc_class_queue_num = next_queue_nums[svc_class]++;
- queue_num_tbls[svc_class][svc_class_queue_num + 1] =
+ queue_num_tbls[svc_class][svc_class_queue_num] =
tm_queue;
}
}
@@ -500,6 +591,7 @@ static int create_and_config_tm(void)
{
odp_tm_level_requirements_t *per_level;
odp_tm_requirements_t requirements;
+ odp_tm_capabilities_t tm_capa;
odp_tm_egress_t egress;
uint32_t level, err_cnt;
@@ -529,6 +621,43 @@ static int create_and_config_tm(void)
egress.egress_fcn = tester_egress_fcn;
odp_tm_test = odp_tm_create("TM test", &requirements, &egress);
+ if (odp_tm_test == ODP_TM_INVALID) {
+ printf("Error: failed to create TM\n");
+ return -1;
+ }
+
+ if (odp_tm_capability(odp_tm_test, &tm_capa) != 0) {
+ printf("Error: failed to get tm capability");
+ return -1;
+ }
+
+ tm_shaper_min_rate = tm_capa.per_level[0].min_rate;
+ tm_shaper_max_rate = tm_capa.per_level[0].max_rate;
+ tm_shaper_min_burst = tm_capa.per_level[0].min_burst;
+ tm_shaper_max_burst = tm_capa.per_level[0].max_burst;
+
+ for (level = 1; level < tm_capa.max_levels; level++) {
+ odp_tm_level_capabilities_t *level_capa = &tm_capa.per_level[level];
+
+ if (level_capa->min_rate > tm_shaper_min_rate)
+ tm_shaper_min_rate = level_capa->min_rate;
+
+ if (level_capa->min_burst > tm_shaper_min_burst)
+ tm_shaper_min_burst = level_capa->min_burst;
+
+ if (level_capa->max_rate < tm_shaper_max_rate)
+ tm_shaper_max_rate = level_capa->max_rate;
+
+ if (level_capa->max_burst < tm_shaper_max_burst)
+ tm_shaper_max_burst = level_capa->max_burst;
+ }
+
+ if (tm_shaper_min_rate > tm_shaper_max_rate ||
+ tm_shaper_min_burst > tm_shaper_max_burst) {
+ printf("Error: No shaper rate supported by all TM levels");
+ return -1;
+ }
+
err_cnt = init_profile_sets();
if (err_cnt != 0)
printf("%s init_profile_sets encountered %" PRIu32 " errors\n",
@@ -573,13 +702,17 @@ static uint32_t pkt_service_class(void)
* of delayed traffic so as to stimulate more interesting behaviors.
*/
rand8 = random_8();
- switch (rand8) {
- case 0 ... 24: return 0;
- case 25 ... 49: return 1;
- case 50 ... 150: return 2;
- case 151 ... 255: return 3;
- default: return 3;
- }
+
+ if (rand8 <= 24)
+ return 0;
+ else if (rand8 >= 25 && rand8 <= 49)
+ return 1;
+ else if (rand8 >= 50 && rand8 <= 150)
+ return 2;
+ else if (rand8 >= 151 && rand8 <= 255)
+ return 3;
+ else
+ return 3;
}
static odp_packet_t make_odp_packet(uint16_t pkt_len)
@@ -632,9 +765,9 @@ static int traffic_generator(uint32_t pkts_to_send)
while (pkt_cnt < pkts_to_send) {
svc_class = pkt_service_class();
queue_num = random_16() & (TM_QUEUES_PER_CLASS - 1);
- tm_queue = queue_num_tbls[svc_class][queue_num + 1];
+ tm_queue = queue_num_tbls[svc_class][queue_num];
pkt_len = ((uint32_t)((random_8() & 0x7F) + 2)) * 32;
- pkt_len = MIN(pkt_len, 1500);
+ pkt_len = ODPH_MIN(pkt_len, 1500u);
pkt = make_odp_packet(pkt_len);
pkt_cnt++;
@@ -742,6 +875,47 @@ static void signal_handler(int signal)
abort();
}
+static int destroy_tm_queues(void)
+{
+ int i;
+ int class;
+ int ret;
+
+ for (i = 0; i < NUM_SVC_CLASSES; i++)
+ for (class = 0; class < TM_QUEUES_PER_CLASS; class++) {
+ odp_tm_queue_t tm_queue;
+ odp_tm_queue_info_t info;
+
+ tm_queue = queue_num_tbls[i][class];
+
+ ret = odp_tm_queue_info(tm_queue, &info);
+ if (ret) {
+ printf("Err: odp_tm_queue_info %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_tm_node_disconnect(info.next_tm_node);
+ if (ret) {
+ printf("Err: odp_tm_node_disconnect %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_tm_queue_disconnect(tm_queue);
+ if (ret) {
+ printf("odp_tm_queue_disconnect %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_tm_queue_destroy(tm_queue);
+ if (ret) {
+ printf("odp_tm_queue_destroy %d\n", ret);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[])
{
struct sigaction signal_action;
@@ -766,18 +940,28 @@ int main(int argc, char *argv[])
rc = odp_init_global(&instance, &ODP_INIT_PARAMS, NULL);
if (rc != 0) {
printf("Error: odp_init_global() failed, rc = %d\n", rc);
- abort();
+ return -1;
}
+
rc = odp_init_local(instance, ODP_THREAD_CONTROL);
if (rc != 0) {
printf("Error: odp_init_local() failed, rc = %d\n", rc);
- abort();
+ return -1;
}
if (process_cmd_line_options(argc, argv) < 0)
return -1;
- create_and_config_tm();
+ rc = create_and_config_tm();
+ if (rc != 0)
+ return rc;
+
+ /* Start TM */
+ rc = odp_tm_start(odp_tm_test);
+ if (rc != 0) {
+ printf("Error: odp_tm_start() failed, rc=%d\n", rc);
+ return -1;
+ }
odp_random_data(random_buf, RANDOM_BUF_LEN, 1);
next_rand_byte = 0;
@@ -793,5 +977,44 @@ int main(int argc, char *argv[])
pkts_into_tm, pkts_from_tm);
odp_tm_stats_print(odp_tm_test);
+
+ /* Stop TM */
+ rc = odp_tm_stop(odp_tm_test);
+ if (rc != 0) {
+ printf("Error: odp_tm_stop() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ rc = destroy_tm_queues();
+ if (rc != 0) {
+ printf("Error: destroy_tm_queues() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ rc = odp_pool_destroy(odp_pool);
+ if (rc != 0) {
+ printf("Error: odp_pool_destroy() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ rc = odp_tm_destroy(odp_tm_test);
+ if (rc != 0) {
+ printf("Error: odp_tm_destroy() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ rc = odp_term_local();
+ if (rc != 0) {
+ printf("Error: odp_term_local() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ rc = odp_term_global(instance);
+ if (rc != 0) {
+ printf("Error: odp_term_global() failed, rc = %d\n", rc);
+ return -1;
+ }
+
+ printf("Quit\n");
return 0;
}
diff --git a/helper/.gitignore b/helper/.gitignore
index f282c157d..edf7a0ba0 100644
--- a/helper/.gitignore
+++ b/helper/.gitignore
@@ -1,3 +1 @@
-config.h.in
-config.h
-stamp-h1
+libodphelper.pc
diff --git a/helper/Makefile.am b/helper/Makefile.am
index 2c5452dcc..f7685a221 100644
--- a/helper/Makefile.am
+++ b/helper/Makefile.am
@@ -1,49 +1,59 @@
-include $(top_srcdir)/platform/@with_platform@/Makefile.inc
+include $(top_srcdir)/Makefile.inc
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = $(top_builddir)/pkgconfig/libodphelper.pc
+pkgconfig_DATA = libodphelper.pc
-LIB = $(top_builddir)/lib
-AM_CFLAGS += -I$(srcdir)/include
-AM_CFLAGS += -I$(top_srcdir)/platform/@with_platform@/include
-AM_CFLAGS += -I$(top_srcdir)/include
-AM_CFLAGS += -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@
-AM_CFLAGS += -I$(top_builddir)/platform/@with_platform@/include
-AM_CFLAGS += -I$(top_builddir)/include
+AM_CPPFLAGS = \
+ $(ODP_INCLUDES) \
+ $(HELPER_INCLUDES) \
+ $(LIBCLI_CPPFLAGS)
+AM_CFLAGS += $(PTHREAD_CFLAGS)
AM_LDFLAGS += -version-number '$(ODPHELPER_LIBSO_VERSION)'
helperincludedir = $(includedir)/odp/helper/
helperinclude_HEADERS = \
- $(srcdir)/include/odp/helper/chksum.h\
- $(srcdir)/include/odp/helper/eth.h\
- $(srcdir)/include/odp/helper/icmp.h\
- $(srcdir)/include/odp/helper/ip.h\
- $(srcdir)/include/odp/helper/ipsec.h\
- $(srcdir)/include/odp/helper/odph_api.h\
- $(srcdir)/include/odp/helper/odph_cuckootable.h\
- $(srcdir)/include/odp/helper/odph_hashtable.h\
- $(srcdir)/include/odp/helper/odph_iplookuptable.h\
- $(srcdir)/include/odp/helper/odph_lineartable.h\
- $(srcdir)/include/odp/helper/strong_types.h\
- $(srcdir)/include/odp/helper/tcp.h\
- $(srcdir)/include/odp/helper/table.h\
- $(srcdir)/include/odp/helper/threads.h \
- $(srcdir)/include/odp/helper/udp.h
+ include/odp/helper/autoheader_external.h\
+ include/odp/helper/deprecated.h\
+ include/odp/helper/chksum.h\
+ include/odp/helper/odph_debug.h \
+ include/odp/helper/eth.h\
+ include/odp/helper/gtp.h\
+ include/odp/helper/icmp.h\
+ include/odp/helper/igmp.h\
+ include/odp/helper/ip.h\
+ include/odp/helper/ipsec.h\
+ include/odp/helper/macros.h\
+ include/odp/helper/odph_api.h\
+ include/odp/helper/odph_cuckootable.h\
+ include/odp/helper/odph_hashtable.h\
+ include/odp/helper/odph_iplookuptable.h\
+ include/odp/helper/odph_lineartable.h\
+ include/odp/helper/sctp.h \
+ include/odp/helper/strong_types.h\
+ include/odp/helper/tcp.h\
+ include/odp/helper/table.h\
+ include/odp/helper/threads.h \
+ include/odp/helper/udp.h \
+ include/odp/helper/version.h
if helper_linux
helperinclude_HEADERS += \
- $(srcdir)/include/odp/helper/linux.h
+ include/odp/helper/linux.h
helperlinuxincludedir = $(includedir)/odp/helper/linux
helperlinuxinclude_HEADERS = \
- $(srcdir)/include/odp/helper/linux/pthread.h \
- $(srcdir)/include/odp/helper/linux/process.h
+ include/odp/helper/linux/pthread.h \
+ include/odp/helper/linux/process.h
+endif
+
+if helper_cli
+helperinclude_HEADERS += \
+ include/odp/helper/cli.h
endif
noinst_HEADERS = \
- $(srcdir)/odph_debug.h \
- $(srcdir)/odph_list_internal.h
+ include/odph_list_internal.h
__LIB__libodphelper_la_SOURCES = \
eth.c \
@@ -53,11 +63,34 @@ __LIB__libodphelper_la_SOURCES = \
lineartable.c \
cuckootable.c \
iplookuptable.c \
- threads.c
+ ipsec.c \
+ threads.c \
+ version.c
if helper_linux
__LIB__libodphelper_la_SOURCES += \
linux/thread.c
endif
+if helper_cli
+__LIB__libodphelper_la_SOURCES += \
+ cli.c
+endif
+
+__LIB__libodphelper_la_LIBADD = $(PTHREAD_LIBS)
+__LIB__libodphelper_la_LIBADD += $(LIBCLI_LIBS)
+
lib_LTLIBRARIES = $(LIB)/libodphelper.la
+
+CHECK_GLOBALS_REGEX = " (odph_|_deprecated_odph_|__odr_asan)"
+
+TESTS_ENVIRONMENT = \
+ LIBTOOL="$(LIBTOOL)" \
+ NM="$(NM)" \
+ LIB="$(LIB)" \
+ lib_LTLIBRARIES="$(lib_LTLIBRARIES)" \
+ CHECK_GLOBALS_REGEX=$(CHECK_GLOBALS_REGEX)
+
+dist_check_SCRIPTS = check-globals.sh
+
+TESTS = $(dist_check_SCRIPTS)
diff --git a/helper/check-globals.sh b/helper/check-globals.sh
new file mode 120000
index 000000000..821608c6e
--- /dev/null
+++ b/helper/check-globals.sh
@@ -0,0 +1 @@
+../scripts/check-globals.sh \ No newline at end of file
diff --git a/helper/chksum.c b/helper/chksum.c
index ae70d97e6..69781291c 100644
--- a/helper/chksum.c
+++ b/helper/chksum.c
@@ -1,12 +1,11 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
-#include <odp.h>
+#include <odp_api.h>
#include <odp/helper/ip.h>
#include <odp/helper/udp.h>
+#include <odp/helper/sctp.h>
#include <odp/helper/tcp.h>
#include <odp/helper/chksum.h>
#include <stddef.h>
@@ -103,9 +102,10 @@ static inline int odph_process_l4_hdr(odp_packet_t odp_pkt,
odph_udphdr_t *udp_hdr_ptr;
odph_tcphdr_t *tcp_hdr_ptr;
odp_bool_t split_l4_hdr, is_tcp;
- uint32_t l4_offset, l4_len, hdr_len, pkt_chksum_offset;
- uint16_t *pkt_chksum_ptr;
+ uint32_t l4_offset, l4_len, pkt_chksum_offset;
+ odp_una_u16_t *pkt_chksum_ptr;
uint8_t *l4_ptr;
+ uint32_t hdr_len = 0;
/* Parse the TCP/UDP header. */
l4_offset = odp_packet_l4_offset(odp_pkt);
@@ -128,7 +128,7 @@ static inline int odph_process_l4_hdr(odp_packet_t odp_pkt,
* should come from the udp header, unlike for TCP where is
* derived. */
l4_len = odp_be_to_cpu_16(udp_hdr_ptr->length);
- pkt_chksum_ptr = (uint16_t *)(void *)&udp_hdr_ptr->chksum;
+ pkt_chksum_ptr = (odp_una_u16_t *)&udp_hdr_ptr->chksum;
pkt_chksum_offset = l4_offset + offsetof(odph_udphdr_t, chksum);
} else if (odp_packet_has_tcp(odp_pkt)) {
tcp_hdr_ptr = (odph_tcphdr_t *)l4_ptr;
@@ -139,7 +139,7 @@ static inline int odph_process_l4_hdr(odp_packet_t odp_pkt,
ODPH_TCPHDR_LEN, tcp_hdr_ptr);
}
- pkt_chksum_ptr = (uint16_t *)(void *)&tcp_hdr_ptr->cksm;
+ pkt_chksum_ptr = (odp_una_u16_t *)&tcp_hdr_ptr->cksm;
pkt_chksum_offset = l4_offset + offsetof(odph_tcphdr_t, cksm);
is_tcp = true;
} else {
@@ -182,9 +182,10 @@ static inline int odph_process_l3_hdr(odp_packet_t odp_pkt,
odph_ipv6hdr_t *ipv6_hdr_ptr, ipv6_hdr;
odp_bool_t split_l3_hdr;
swap_buf_t swap_buf;
- uint32_t l3_offset, l4_offset, l3_hdrs_len, hdr_len, addrs_len;
+ uint32_t l3_offset, l4_offset, l3_hdrs_len, addrs_len;
uint32_t protocol, l3_len, l4_len, idx, ipv6_payload_len, sum;
- uint16_t *addrs_ptr;
+ odp_una_u16_t *addrs_ptr;
+ uint32_t hdr_len = 0;
/* The following computation using the l3 and l4 offsets handles both
* the case of IPv4 options and IPv6 extension headers uniformly. */
@@ -203,7 +204,7 @@ static inline int odph_process_l3_hdr(odp_packet_t odp_pkt,
ipv4_hdr_ptr = &ipv4_hdr;
}
- addrs_ptr = (uint16_t *)(void *)&ipv4_hdr_ptr->src_addr;
+ addrs_ptr = (odp_una_u16_t *)&ipv4_hdr_ptr->src_addr;
addrs_len = 2 * ODPH_IPV4ADDR_LEN;
protocol = ipv4_hdr_ptr->proto;
l3_len = odp_be_to_cpu_16(ipv4_hdr_ptr->tot_len);
@@ -216,7 +217,7 @@ static inline int odph_process_l3_hdr(odp_packet_t odp_pkt,
ipv6_hdr_ptr = &ipv6_hdr;
}
- addrs_ptr = (uint16_t *)(void *)&ipv6_hdr_ptr->src_addr;
+ addrs_ptr = (odp_una_u16_t *)&ipv6_hdr_ptr->src_addr;
addrs_len = 2 * ODPH_IPV6ADDR_LEN;
protocol = ipv6_hdr_ptr->next_hdr;
ipv6_payload_len = odp_be_to_cpu_16(ipv6_hdr_ptr->payload_len);
@@ -268,11 +269,12 @@ int odph_udp_tcp_chksum(odp_packet_t odp_pkt,
odph_l4_hdr_t udp_tcp_hdr;
odp_bool_t split_l4_hdr, is_tcp, is_last;
odp_bool_t has_odd_byte_in;
- uint32_t l4_len, sum, ones_compl_sum, remaining_seg_len;
+ uint32_t l4_len, sum, ones_compl_sum;
uint32_t data_len, pkt_chksum_offset, offset;
uint16_t *pkt_chksum_ptr, chksum;
uint8_t *data_ptr, odd_byte_in_out;
int rc, ret_code;
+ uint32_t remaining_seg_len = 0;
/* First parse and process the l4 header */
rc = odph_process_l4_hdr(odp_pkt, op, &udp_tcp_hdr, chksum_ptr, &l4_len,
@@ -349,3 +351,77 @@ int odph_udp_tcp_chksum(odp_packet_t odp_pkt,
return ret_code;
}
+
+static uint32_t odph_packet_crc32c(odp_packet_t pkt,
+ uint32_t offset,
+ uint32_t length,
+ uint32_t init_val)
+{
+ uint32_t sum = init_val;
+
+ if (offset + length > odp_packet_len(pkt))
+ return sum;
+
+ while (length > 0) {
+ uint32_t seg_len = 0;
+ void *data = odp_packet_offset(pkt, offset, &seg_len, NULL);
+
+ if (seg_len > length)
+ seg_len = length;
+
+ sum = odp_hash_crc32c(data, seg_len, sum);
+ length -= seg_len;
+ offset += seg_len;
+ }
+
+ return sum;
+}
+
+int odph_sctp_chksum_set(odp_packet_t pkt)
+{
+ uint32_t l4_offset = odp_packet_l4_offset(pkt);
+ uint32_t sum = 0;
+
+ if (!odp_packet_has_sctp(pkt))
+ return -1;
+
+ if (l4_offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
+
+ odp_packet_copy_from_mem(pkt,
+ l4_offset + ODPH_SCTPHDR_LEN - 4,
+ 4,
+ &sum);
+
+ sum = ~odph_packet_crc32c(pkt, l4_offset,
+ odp_packet_len(pkt) - l4_offset,
+ ~0);
+ return odp_packet_copy_from_mem(pkt,
+ l4_offset + ODPH_SCTPHDR_LEN - 4,
+ 4,
+ &sum);
+}
+
+int odph_sctp_chksum_verify(odp_packet_t pkt)
+{
+ uint32_t l4_offset = odp_packet_l4_offset(pkt);
+ uint32_t sum;
+ uint32_t temp = 0;
+
+ if (!odp_packet_has_sctp(pkt))
+ return -1;
+
+ sum = odph_packet_crc32c(pkt, l4_offset,
+ ODPH_SCTPHDR_LEN - 4,
+ ~0);
+ sum = odp_hash_crc32c(&temp, 4, sum);
+ sum = ~odph_packet_crc32c(pkt, l4_offset + ODPH_SCTPHDR_LEN,
+ odp_packet_len(pkt) - l4_offset -
+ ODPH_SCTPHDR_LEN,
+ sum);
+
+ odp_packet_copy_to_mem(pkt, l4_offset + ODPH_SCTPHDR_LEN - 4,
+ 4, &temp);
+
+ return (temp == sum) ? 0 : 2;
+}
diff --git a/helper/cli.c b/helper/cli.c
new file mode 100644
index 000000000..4ce4bf62e
--- /dev/null
+++ b/helper/cli.c
@@ -0,0 +1,1019 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <odp/helper/cli.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <libcli.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <strings.h>
+#include <inttypes.h>
+
+/* Socketpair socket roles. */
+enum {
+ SP_SERVER = 0,
+ SP_CONTROL = 1,
+};
+
+#define MAX_NAME_LEN 20
+#define MAX_HELP_LEN 100
+
+typedef struct {
+ odph_cli_user_cmd_func_t fn;
+ char name[MAX_NAME_LEN];
+ char help[MAX_HELP_LEN];
+} user_cmd_t;
+
+typedef struct {
+ volatile int cli_fd;
+ /* Server will exit if this is false. */
+ volatile int run;
+ /* Socketpair descriptors. */
+ int sp[2];
+ int listen_fd;
+ /* Guards cli_fd and run, which must be accessed atomically. */
+ odp_spinlock_t lock;
+ odp_spinlock_t api_lock;
+ odph_cli_param_t cli_param;
+ struct sockaddr_in addr;
+ uint32_t num_user_commands;
+ user_cmd_t user_cmd[];
+} cli_shm_t;
+
+static const char *shm_name = "_odp_cli";
+
+static const odph_cli_param_t param_default = {
+ .address = "127.0.0.1",
+ .port = 55555,
+ .max_user_commands = 50,
+ .hostname = "ODP",
+};
+
+void odph_cli_param_init(odph_cli_param_t *param)
+{
+ *param = param_default;
+}
+
+static cli_shm_t *shm_lookup(void)
+{
+ cli_shm_t *shm = NULL;
+ odp_shm_t shm_hdl = odp_shm_lookup(shm_name);
+
+ if (shm_hdl != ODP_SHM_INVALID)
+ shm = (cli_shm_t *)odp_shm_addr(shm_hdl);
+
+ return shm;
+}
+
+int odph_cli_init(const odph_cli_param_t *param)
+{
+ if (odp_shm_lookup(shm_name) != ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shm %s already exists\n", shm_name);
+ return -1;
+ }
+
+ cli_shm_t *shm = NULL;
+ int shm_size = sizeof(cli_shm_t) +
+ param->max_user_commands * sizeof(user_cmd_t);
+ odp_shm_t shm_hdl =
+ odp_shm_reserve(shm_name, shm_size, 64, 0);
+
+ if (shm_hdl != ODP_SHM_INVALID)
+ shm = (cli_shm_t *)odp_shm_addr(shm_hdl);
+
+ if (!shm) {
+ ODPH_ERR("Error: failed to reserve shm %s\n", shm_name);
+ return -1;
+ }
+
+ memset(shm, 0, shm_size);
+ odp_spinlock_init(&shm->lock);
+ odp_spinlock_init(&shm->api_lock);
+ shm->listen_fd = -1;
+ shm->cli_fd = -1;
+
+ shm->addr.sin_family = AF_INET;
+ shm->addr.sin_port = htons(param->port);
+
+ switch (inet_pton(AF_INET, param->address, &shm->addr.sin_addr)) {
+ case -1:
+ ODPH_ERR("Error: inet_pton(): %s\n", strerror(errno));
+ return -1;
+ case 0:
+ ODPH_ERR("Error: inet_pton(): illegal address format\n");
+ return -1;
+ default:
+ break;
+ }
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, shm->sp)) {
+ ODPH_ERR("Error: socketpair(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ shm->cli_param = *param;
+
+ return 0;
+}
+
+int odph_cli_register_command(const char *name, odph_cli_user_cmd_func_t func,
+ const char *help)
+{
+ cli_shm_t *shm = shm_lookup();
+
+ if (!shm) {
+ ODPH_ERR("Error: shm %s not found\n", shm_name);
+ return -1;
+ }
+
+ odp_spinlock_lock(&shm->api_lock);
+
+ odp_spinlock_lock(&shm->lock);
+ if (shm->run) {
+ odp_spinlock_unlock(&shm->lock);
+ ODPH_ERR("Error: cannot register commands while cli server is running\n");
+ goto error;
+ }
+ odp_spinlock_unlock(&shm->lock);
+
+ if (shm->num_user_commands >= shm->cli_param.max_user_commands) {
+ ODPH_ERR("Error: maximum number of user commands already registered\n");
+ goto error;
+ }
+
+ user_cmd_t *cmd = &shm->user_cmd[shm->num_user_commands];
+
+ cmd->fn = func;
+
+ if (strlen(name) >= MAX_NAME_LEN - 1) {
+ ODPH_ERR("Error: command name too long\n");
+ goto error;
+ }
+ strcpy(cmd->name, name);
+
+ if (strlen(help) >= MAX_HELP_LEN - 1) {
+ ODPH_ERR("Error: command help too long\n");
+ goto error;
+ }
+ strcpy(cmd->help, help);
+
+ shm->num_user_commands++;
+ odp_spinlock_unlock(&shm->api_lock);
+ return 0;
+
+error:
+ odp_spinlock_unlock(&shm->api_lock);
+ return -1;
+}
+
+/*
+ * Check that number of given arguments matches required number of
+ * arguments. Print error messages if this is not the case. Return 0
+ * on success, -1 otherwise.
+ */
+static int check_num_args(struct cli_def *cli, int argc, int req_argc)
+{
+ if (argc < req_argc) {
+ cli_error(cli, "%% Incomplete command.");
+ return -1;
+ }
+
+ if (argc > req_argc) {
+ cli_error(cli, "%% Extra parameter given to command.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Not shared, used only in the server thread. */
+static struct cli_def *cli;
+static char *cli_log_fn_buf;
+
+ODP_PRINTF_FORMAT(2, 0)
+static int cli_log_va(odp_log_level_t level, const char *fmt, va_list in_args)
+{
+ (void)level;
+
+ va_list args;
+ char *str = NULL, *p, *last;
+ int len;
+
+ /*
+ * This function should be just a simple call to cli_vabufprint().
+ * Unfortunately libcli (at least versions 1.9.7 - 1.10.4) has a few
+ * bugs. cli_print() prints a newline at the end even if the string
+ * doesn't end in a newline. cli_*bufprint() on the other hand just
+ * throws away everything after the last newline.
+ *
+ * The following code ensures that each cli_*print() ends in a newline.
+ * If the string does not end in a newline, we keep the part of the
+ * string after the last newline and use it the next time we're called.
+ */
+ va_copy(args, in_args);
+ len = vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+
+ if (len < 0) {
+ ODPH_ERR("vsnprintf failed\n");
+ goto out;
+ }
+
+ len++;
+ str = malloc(len);
+
+ if (!str) {
+ ODPH_ERR("malloc failed\n");
+ return -1;
+ }
+
+ va_copy(args, in_args);
+ len = vsnprintf(str, len, fmt, args);
+ va_end(args);
+
+ if (len < 0) {
+ ODPH_ERR("vsnprintf failed\n");
+ goto out;
+ }
+
+ p = str;
+ last = strrchr(p, '\n');
+
+ if (last) {
+ *last++ = 0;
+ if (cli_log_fn_buf) {
+ cli_bufprint(cli, "%s%s\n", cli_log_fn_buf, p);
+ free(cli_log_fn_buf);
+ cli_log_fn_buf = NULL;
+ } else {
+ cli_bufprint(cli, "%s\n", p);
+ }
+ p = last;
+ }
+
+ if (*p) {
+ if (cli_log_fn_buf) {
+ char *buffer_new =
+ malloc(strlen(cli_log_fn_buf) + strlen(p) + 1);
+
+ if (!buffer_new) {
+ ODPH_ERR("malloc failed\n");
+ goto out;
+ }
+
+ strcpy(buffer_new, cli_log_fn_buf);
+ strcat(buffer_new, p);
+ free(cli_log_fn_buf);
+ cli_log_fn_buf = buffer_new;
+ } else {
+ cli_log_fn_buf = malloc(strlen(p) + 1);
+
+ if (!cli_log_fn_buf) {
+ ODPH_ERR("malloc failed\n");
+ goto out;
+ }
+
+ strcpy(cli_log_fn_buf, p);
+ }
+ }
+
+out:
+ free(str);
+
+ return len;
+}
+
+ODP_PRINTF_FORMAT(2, 3)
+static int cli_log(odp_log_level_t level, const char *fmt, ...)
+{
+ (void)level;
+
+ int r;
+ va_list args;
+
+ va_start(args, fmt);
+ r = cli_log_va(level, fmt, args);
+ va_end(args);
+
+ return r;
+}
+
+static int cmd_odp_cls_print_all(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_cls_print_all();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_ipsec_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_ipsec_print();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_shm_print_all(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_shm_print_all();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_sys_config_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_sys_config_print();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_sys_info_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_sys_info_print();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pktio_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[],
+ int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pktio_t hdl = odp_pktio_lookup(argv[0]);
+
+ if (hdl == ODP_PKTIO_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_pktio_print(hdl);
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pktio_extra_stats_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[], int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pktio_t hdl = odp_pktio_lookup(argv[0]);
+
+ if (hdl == ODP_PKTIO_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_pktio_extra_stats_print(hdl);
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pool_print_all(struct cli_def *cli,
+ const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_pool_print_all();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pool_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[],
+ int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pool_t hdl = odp_pool_lookup(argv[0]);
+
+ if (hdl == ODP_POOL_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_pool_print(hdl);
+
+ return CLI_OK;
+}
+
+static int cmd_odp_queue_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[],
+ int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_queue_t hdl = odp_queue_lookup(argv[0]);
+
+ if (hdl == ODP_QUEUE_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_queue_print(hdl);
+
+ return CLI_OK;
+}
+
+static int cmd_odp_queue_print_all(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_queue_print_all();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_schedule_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[] ODP_UNUSED, int argc)
+{
+ if (check_num_args(cli, argc, 0))
+ return CLI_ERROR;
+
+ odp_schedule_print();
+
+ return CLI_OK;
+}
+
+static int cmd_odp_shm_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[],
+ int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_shm_t hdl = odp_shm_lookup(argv[0]);
+
+ if (hdl == ODP_SHM_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_shm_print(hdl);
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pktio_stats_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[], int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pktio_t hdl = odp_pktio_lookup(argv[0]);
+
+ if (hdl == ODP_PKTIO_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ odp_pktio_stats_t stats;
+
+ if (odp_pktio_stats(hdl, &stats) < 0) {
+ cli_error(cli, "%% Unable to query stats.");
+ return CLI_ERROR;
+ }
+
+ cli_log(ODP_LOG_PRINT, "Pktio statistics\n----------------\n");
+ cli_log(ODP_LOG_PRINT, " in_octets: %" PRIu64 "\n", stats.in_octets);
+ cli_log(ODP_LOG_PRINT, " in_packets: %" PRIu64 "\n", stats.in_packets);
+ cli_log(ODP_LOG_PRINT, " in_ucast_pkts: %" PRIu64 "\n", stats.in_ucast_pkts);
+ cli_log(ODP_LOG_PRINT, " in_mcast_pkts: %" PRIu64 "\n", stats.in_mcast_pkts);
+ cli_log(ODP_LOG_PRINT, " in_bcast_pkts: %" PRIu64 "\n", stats.in_bcast_pkts);
+ cli_log(ODP_LOG_PRINT, " in_discards: %" PRIu64 "\n", stats.in_discards);
+ cli_log(ODP_LOG_PRINT, " in_errors: %" PRIu64 "\n", stats.in_errors);
+ cli_log(ODP_LOG_PRINT, " out_octets: %" PRIu64 "\n", stats.out_octets);
+ cli_log(ODP_LOG_PRINT, " out_packets: %" PRIu64 "\n", stats.out_packets);
+ cli_log(ODP_LOG_PRINT, " out_ucast_pkts: %" PRIu64 "\n", stats.out_ucast_pkts);
+ cli_log(ODP_LOG_PRINT, " out_mcast_pkts: %" PRIu64 "\n", stats.out_mcast_pkts);
+ cli_log(ODP_LOG_PRINT, " out_bcast_pkts: %" PRIu64 "\n", stats.out_bcast_pkts);
+ cli_log(ODP_LOG_PRINT, " out_discards: %" PRIu64 "\n", stats.out_discards);
+ cli_log(ODP_LOG_PRINT, " out_errors: %" PRIu64 "\n\n", stats.out_errors);
+
+ return CLI_OK;
+}
+
+static void cli_log_pktin_queue_stats(odp_pktin_queue_stats_t *stats)
+{
+ cli_log(ODP_LOG_PRINT, " octets: %" PRIu64 "\n", stats->octets);
+ cli_log(ODP_LOG_PRINT, " packets: %" PRIu64 "\n", stats->packets);
+ cli_log(ODP_LOG_PRINT, " discards: %" PRIu64 "\n", stats->discards);
+ cli_log(ODP_LOG_PRINT, " errors: %" PRIu64 "\n", stats->errors);
+}
+
+static void cli_log_pktout_queue_stats(odp_pktout_queue_stats_t *stats)
+{
+ cli_log(ODP_LOG_PRINT, " octets: %" PRIu64 "\n", stats->octets);
+ cli_log(ODP_LOG_PRINT, " packets: %" PRIu64 "\n", stats->packets);
+ cli_log(ODP_LOG_PRINT, " discards: %" PRIu64 "\n", stats->discards);
+ cli_log(ODP_LOG_PRINT, " errors: %" PRIu64 "\n", stats->errors);
+}
+
+static int cmd_odp_pktio_queue_stats_print(struct cli_def *cli, const char *command ODP_UNUSED,
+ char *argv[], int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pktio_t hdl = odp_pktio_lookup(argv[0]);
+
+ if (hdl == ODP_PKTIO_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ int in_q_cnt = odp_pktin_queue(hdl, NULL, 0);
+
+ if (in_q_cnt > 0) {
+ odp_pktin_queue_t in_qs[in_q_cnt];
+ odp_pktin_queue_stats_t in_stats;
+
+ in_q_cnt = odp_pktin_queue(hdl, in_qs, in_q_cnt);
+
+ cli_log(ODP_LOG_PRINT, "Pktin queue statistics\n----------------------\n");
+
+ for (int i = 0; i < in_q_cnt; i++) {
+ cli_log(ODP_LOG_PRINT, "Pktin queue: %d:\n", i);
+
+ if (odp_pktin_queue_stats(in_qs[i], &in_stats) < 0) {
+ cli_log(ODP_LOG_PRINT,
+ " (Unable to read statistics, skipping)\n");
+ continue;
+ }
+
+ cli_log_pktin_queue_stats(&in_stats);
+ }
+ }
+
+ int out_q_cnt = odp_pktout_queue(hdl, NULL, 0);
+
+ if (out_q_cnt > 0) {
+ odp_pktout_queue_t out_qs[out_q_cnt];
+ odp_pktout_queue_stats_t out_stats;
+
+ out_q_cnt = odp_pktout_queue(hdl, out_qs, out_q_cnt);
+
+ cli_log(ODP_LOG_PRINT, "Pktout queue statistics\n-----------------------\n");
+
+ for (int i = 0; i < out_q_cnt; i++) {
+ cli_log(ODP_LOG_PRINT, "Pktout queue: %d:\n", i);
+
+ if (odp_pktout_queue_stats(out_qs[i], &out_stats) < 0) {
+ cli_log(ODP_LOG_PRINT,
+ " (Unable to read statistics, skipping)\n");
+ continue;
+ }
+
+ cli_log_pktout_queue_stats(&out_stats);
+ }
+ }
+
+ cli_log(ODP_LOG_PRINT, "\n");
+
+ return CLI_OK;
+}
+
+static int cmd_odp_pktio_event_queue_stats_print(struct cli_def *cli,
+ const char *command ODP_UNUSED, char *argv[],
+ int argc)
+{
+ if (check_num_args(cli, argc, 1))
+ return CLI_ERROR;
+
+ odp_pktio_t hdl = odp_pktio_lookup(argv[0]);
+
+ if (hdl == ODP_PKTIO_INVALID) {
+ cli_error(cli, "%% Name not found.");
+ return CLI_ERROR;
+ }
+
+ int in_q_cnt = odp_pktin_event_queue(hdl, NULL, 0);
+
+ if (in_q_cnt > 0) {
+ odp_queue_t in_qs[in_q_cnt];
+ odp_pktin_queue_stats_t in_stats;
+
+ in_q_cnt = odp_pktin_event_queue(hdl, in_qs, in_q_cnt);
+
+ cli_log(ODP_LOG_PRINT,
+ "Pktin event queue statistics\n----------------------------\n");
+
+ for (int i = 0; i < in_q_cnt; i++) {
+ cli_log(ODP_LOG_PRINT, "Pktin event queue: %d:\n", i);
+
+ if (odp_pktin_event_queue_stats(hdl, in_qs[i], &in_stats) < 0) {
+ cli_log(ODP_LOG_PRINT,
+ " (Unable to read statistics, skipping)\n");
+ continue;
+ }
+
+ cli_log_pktin_queue_stats(&in_stats);
+ }
+ }
+
+ int out_q_cnt = odp_pktout_event_queue(hdl, NULL, 0);
+
+ if (out_q_cnt > 0) {
+ odp_queue_t out_qs[out_q_cnt];
+ odp_pktout_queue_stats_t out_stats;
+
+ out_q_cnt = odp_pktout_event_queue(hdl, out_qs, out_q_cnt);
+
+ cli_log(ODP_LOG_PRINT,
+ "Pktout event queue statistics\n-----------------------------\n");
+
+ for (int i = 0; i < out_q_cnt; i++) {
+ cli_log(ODP_LOG_PRINT, "Pktout event queue: %d:\n", i);
+
+ if (odp_pktout_event_queue_stats(hdl, out_qs[i], &out_stats) < 0) {
+ cli_log(ODP_LOG_PRINT,
+ " (Unable to read statistics, skipping)\n");
+ continue;
+ }
+
+ cli_log_pktout_queue_stats(&out_stats);
+ }
+ }
+
+ cli_log(ODP_LOG_PRINT, "\n");
+
+ return CLI_OK;
+}
+
+static int cmd_user_cmd(struct cli_def *cli ODP_UNUSED, const char *command,
+ char *argv[], int argc)
+{
+ cli_shm_t *shm = shm_lookup();
+
+ if (!shm) {
+ ODPH_ERR("Error: shm %s not found\n", shm_name);
+ return CLI_ERROR;
+ }
+
+ for (uint32_t i = 0; i < shm->num_user_commands; i++) {
+ if (!strcasecmp(command, shm->user_cmd[i].name)) {
+ shm->user_cmd[i].fn(argc, argv);
+ break;
+ }
+ }
+
+ return CLI_OK;
+}
+
+static struct cli_def *create_cli(cli_shm_t *shm)
+{
+ struct cli_def *cli;
+
+ cli = cli_init();
+ cli_set_banner(cli, NULL);
+ cli_set_hostname(cli, shm->cli_param.hostname);
+
+#define CMD(name, help) \
+ cli_register_command(cli, NULL, #name, cmd_ ## name, \
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, help)
+
+ CMD(odp_cls_print_all, NULL);
+ CMD(odp_ipsec_print, NULL);
+ CMD(odp_pktio_event_queue_stats_print, "<name>");
+ CMD(odp_pktio_extra_stats_print, "<name>");
+ CMD(odp_pktio_print, "<name>");
+ CMD(odp_pktio_queue_stats_print, "<name>");
+ CMD(odp_pktio_stats_print, "<name>");
+ CMD(odp_pool_print_all, NULL);
+ CMD(odp_pool_print, "<name>");
+ CMD(odp_queue_print_all, NULL);
+ CMD(odp_queue_print, "<name>");
+ CMD(odp_schedule_print, NULL);
+ CMD(odp_shm_print_all, NULL);
+ CMD(odp_shm_print, "<name>");
+ CMD(odp_sys_config_print, NULL);
+ CMD(odp_sys_info_print, NULL);
+
+ for (uint32_t i = 0; i < shm->num_user_commands; i++) {
+ cli_register_command(cli, NULL, shm->user_cmd[i].name,
+ cmd_user_cmd, PRIVILEGE_UNPRIVILEGED,
+ MODE_EXEC, shm->user_cmd[i].help);
+ }
+
+ return cli;
+}
+
+ODP_PRINTF_FORMAT(1, 2)
+int odph_cli_log(const char *fmt, ...)
+{
+ int r;
+ va_list args;
+
+ va_start(args, fmt);
+ r = cli_log_va(ODP_LOG_PRINT, fmt, args);
+ va_end(args);
+
+ return r;
+}
+
+ODP_PRINTF_FORMAT(1, 0)
+int odph_cli_log_va(const char *fmt, va_list in_args)
+{
+ int r;
+
+ r = cli_log_va(ODP_LOG_PRINT, fmt, in_args);
+ return r;
+}
+
+static int msg_recv(int fd)
+{
+ uint32_t msg;
+ int num = recv(fd, &msg, sizeof(msg), MSG_NOSIGNAL);
+
+ if (num != sizeof(msg)) {
+ ODPH_ERR("Error: recv() = %d: %s\n", num, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int msg_send(int fd)
+{
+ uint32_t msg = 0;
+ int num = send(fd, &msg, sizeof(msg), MSG_DONTWAIT | MSG_NOSIGNAL);
+
+ if (num != sizeof(msg)) {
+ ODPH_ERR("Error: send() = %d: %s\n", num, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cli_server(cli_shm_t *shm)
+{
+ cli = create_cli(shm);
+
+ while (1) {
+ struct pollfd pfd[2] = {
+ { .fd = shm->sp[SP_SERVER], .events = POLLIN, },
+ { .fd = shm->listen_fd, .events = POLLIN, },
+ };
+
+ if (poll(pfd, 2, -1) < 0) {
+ ODPH_ERR("Error: poll(): %s\n", strerror(errno));
+ break;
+ }
+
+ /*
+ * If we have an event on a socketpair socket, it's
+ * time to exit.
+ */
+ if (pfd[0].revents)
+ break;
+
+ /*
+ * If we don't have an event on the listening socket, poll
+ * again.
+ */
+ if (!pfd[1].revents)
+ continue;
+
+ int fd = accept(shm->listen_fd, NULL, 0);
+
+ if (fd < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+
+ ODPH_ERR("Error: accept(): %s\n", strerror(errno));
+ break;
+ }
+
+ /*
+ * The only way to stop cli_loop() is to close the socket, after
+ * which cli_loop() gets an error on the next select() and then
+ * calls close() before returning. This is a problem because the
+ * fd may be reused before the select() or the final close().
+ *
+ * To avoid this problem, switch to a higher fd number
+ * (select() maximum). We will still run into problems if the
+ * descriptor numbers in the process reach FD_SETSIZE - 1 =
+ * 1023.
+ */
+ int newfd = dup2(fd, FD_SETSIZE - 1);
+
+ if (newfd < 0) {
+ ODPH_ERR("Error: dup2(): %s\n", strerror(errno));
+ close(fd);
+ continue;
+ }
+
+ close(fd);
+ fd = newfd;
+
+ odp_spinlock_lock(&shm->lock);
+ if (!shm->run) {
+ odp_spinlock_unlock(&shm->lock);
+ /*
+ * odph_cli_stop() has been called. Close the
+ * socket we just accepted and exit.
+ */
+ close(fd);
+ break;
+ }
+ shm->cli_fd = fd;
+ odp_spinlock_unlock(&shm->lock);
+
+ odp_log_thread_fn_set(cli_log);
+ /*
+ * cli_loop() returns only when client is disconnected. One
+ * possible reason for disconnect is odph_cli_stop().
+ */
+ cli_loop(cli, shm->cli_fd);
+ odp_log_thread_fn_set(NULL);
+
+ odp_spinlock_lock(&shm->lock);
+ /*
+ * cli_loop() closes the socket before returning (undocumented).
+ */
+ shm->cli_fd = -1;
+ odp_spinlock_unlock(&shm->lock);
+
+ /*
+ * Throw away anything left in the buffer (in case the last
+ * print didn't end in a newline).
+ */
+ free(cli_log_fn_buf);
+ cli_log_fn_buf = NULL;
+ }
+
+ cli_done(cli);
+
+ if (msg_send(shm->sp[SP_SERVER]))
+ return -1;
+
+ return 0;
+}
+
+int odph_cli_run(void)
+{
+ cli_shm_t *shm = shm_lookup();
+
+ if (!shm) {
+ ODPH_ERR("Error: shm %s not found\n", shm_name);
+ return -1;
+ }
+
+ odp_spinlock_lock(&shm->api_lock);
+ odp_spinlock_lock(&shm->lock);
+ if (shm->run) {
+ odp_spinlock_unlock(&shm->lock);
+ odp_spinlock_unlock(&shm->api_lock);
+ ODPH_ERR("Error: cli server is already running\n");
+ return -1;
+ }
+ shm->run = 1;
+ shm->cli_fd = -1;
+ odp_spinlock_unlock(&shm->lock);
+
+ /* Create listening socket. */
+
+ shm->listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (shm->listen_fd < 0) {
+ ODPH_ERR("Error: socket(): %s\n", strerror(errno));
+ goto error;
+ }
+
+ int on = 1;
+
+ if (setsockopt(shm->listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
+ ODPH_ERR("Error: setsockopt(): %s\n", strerror(errno));
+ goto error;
+ }
+
+ if (bind(shm->listen_fd, (struct sockaddr *)&shm->addr,
+ sizeof(shm->addr))) {
+ ODPH_ERR("Error: bind(): %s\n", strerror(errno));
+ goto error;
+ }
+
+ if (listen(shm->listen_fd, 1)) {
+ ODPH_ERR("Error: listen(): %s\n", strerror(errno));
+ goto error;
+ }
+
+ odp_spinlock_unlock(&shm->api_lock);
+
+ return cli_server(shm);
+
+error:
+ shm->run = 0;
+ if (shm->listen_fd >= 0)
+ close(shm->listen_fd);
+ if (shm->cli_fd >= 0)
+ close(shm->cli_fd);
+ odp_spinlock_unlock(&shm->api_lock);
+ return -1;
+}
+
+int odph_cli_stop(void)
+{
+ cli_shm_t *shm = shm_lookup();
+
+ if (!shm) {
+ ODPH_ERR("Error: shm %s not found\n", shm_name);
+ return -1;
+ }
+
+ odp_spinlock_lock(&shm->api_lock);
+ odp_spinlock_lock(&shm->lock);
+ if (!shm->run) {
+ odp_spinlock_unlock(&shm->lock);
+ odp_spinlock_unlock(&shm->api_lock);
+ ODPH_ERR("Error: cli server has not been started\n");
+ return -1;
+ }
+ shm->run = 0;
+ /*
+ * Close the current cli connection. This stops cli_loop(). If cli
+ * client is disconnecting at the same time, cli_fd may already have
+ * been closed.
+ */
+ if (shm->cli_fd >= 0) {
+ close(shm->cli_fd);
+ shm->cli_fd = -1;
+ }
+ odp_spinlock_unlock(&shm->lock);
+
+ /*
+ * Send a message to the server thread in order to break it out of a
+ * blocking poll() call.
+ */
+ if (msg_send(shm->sp[SP_CONTROL]))
+ goto error;
+
+ /*
+ * Wait for the server to exit.
+ */
+ if (msg_recv(shm->sp[SP_CONTROL]))
+ goto error;
+
+ close(shm->listen_fd);
+ odp_spinlock_unlock(&shm->api_lock);
+ return 0;
+
+error:
+ odp_spinlock_unlock(&shm->api_lock);
+ return -1;
+}
+
+int odph_cli_term(void)
+{
+ cli_shm_t *shm = NULL;
+ odp_shm_t shm_hdl = odp_shm_lookup(shm_name);
+
+ if (shm_hdl != ODP_SHM_INVALID)
+ shm = (cli_shm_t *)odp_shm_addr(shm_hdl);
+
+ if (!shm) {
+ ODPH_ERR("Error: shm %s not found\n", shm_name);
+ return -1;
+ }
+
+ close(shm->sp[SP_SERVER]);
+ close(shm->sp[SP_CONTROL]);
+
+ if (odp_shm_free(shm_hdl)) {
+ ODPH_ERR("Error: odp_shm_free() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/helper/cuckootable.c b/helper/cuckootable.c
index 32800911a..85f715b3c 100644
--- a/helper/cuckootable.c
+++ b/helper/cuckootable.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/*-
@@ -42,8 +40,8 @@
#include <errno.h>
#include <stdio.h>
-#include "odp/helper/odph_cuckootable.h"
-#include "odph_debug.h"
+#include <odp/helper/odph_cuckootable.h>
+#include <odp/helper/odph_debug.h>
#include <odp_api.h>
/* More efficient access to a map of single ullong */
@@ -87,16 +85,16 @@ struct cuckoo_table_key_value {
};
/** @internal bucket structure
- * Put the elements with defferent keys but a same signature
+ * Put the elements with different keys but a same signature
* into a bucket, and each bucket has at most HASH_BUCKET_ENTRIES
* elements.
*/
-struct cuckoo_table_bucket {
+struct ODP_ALIGNED_CACHE cuckoo_table_bucket {
struct cuckoo_table_signatures signatures[HASH_BUCKET_ENTRIES];
/* Includes dummy key index that always contains index 0 */
odp_buffer_t key_buf[HASH_BUCKET_ENTRIES + 1];
uint8_t flag[HASH_BUCKET_ENTRIES];
-} ODP_ALIGNED_CACHE;
+};
/* More efficient access to a map of single ullong */
#define ULLONG_FOR_EACH_1(IDX, MAP) \
@@ -104,7 +102,7 @@ struct cuckoo_table_bucket {
MAP = (MAP & (MAP - 1)))
/** A hash table structure. */
-typedef struct {
+typedef struct ODP_ALIGNED_CACHE {
/**< for check */
uint32_t magicword;
/**< Name of the hash. */
@@ -124,7 +122,7 @@ typedef struct {
/** Table with buckets storing all the hash values and key indexes
to the key table*/
struct cuckoo_table_bucket *buckets;
-} odph_cuckoo_table_impl ODP_ALIGNED_CACHE;
+} odph_cuckoo_table_impl;
/**
* Aligns input parameter to the next power of 2
@@ -182,6 +180,8 @@ odph_cuckoo_table_create(
odp_queue_t queue;
odp_queue_param_t qparam;
+ odp_queue_capability_t qcapa;
+ odp_pool_capability_t pcapa;
char pool_name[ODPH_TABLE_NAME_LEN + 3],
queue_name[ODPH_TABLE_NAME_LEN + 3];
@@ -189,6 +189,26 @@ odph_cuckoo_table_create(
uint32_t impl_size, kv_entry_size,
bucket_num, bucket_size;
+ if (odp_queue_capability(&qcapa)) {
+ ODPH_DBG("queue capa failed\n");
+ return NULL;
+ }
+
+ if (qcapa.plain.max_size && qcapa.plain.max_size < capacity) {
+ ODPH_DBG("queue max_size too small\n");
+ return NULL;
+ }
+
+ if (odp_pool_capability(&pcapa)) {
+ ODPH_DBG("pool capa failed\n");
+ return NULL;
+ }
+
+ if (pcapa.buf.max_num && pcapa.buf.max_num < capacity) {
+ ODPH_DBG("pool max_num too small\n");
+ return NULL;
+ }
+
/* Check for valid parameters */
if (
(capacity > HASH_ENTRIES_MAX) ||
@@ -214,9 +234,8 @@ odph_cuckoo_table_create(
bucket_num = align32pow2(capacity) / HASH_BUCKET_ENTRIES;
bucket_size = bucket_num * sizeof(struct cuckoo_table_bucket);
- shm_tbl = odp_shm_reserve(
- name, impl_size + bucket_size,
- ODP_CACHE_LINE_SIZE, ODP_SHM_SW_ONLY);
+ shm_tbl = odp_shm_reserve(name, impl_size + bucket_size,
+ ODP_CACHE_LINE_SIZE, 0);
if (shm_tbl == ODP_SHM_INVALID) {
ODPH_DBG(
@@ -238,12 +257,17 @@ odph_cuckoo_table_create(
pool = odp_pool_lookup(pool_name);
if (pool != ODP_POOL_INVALID)
- odp_pool_destroy(pool);
+ if (odp_pool_destroy(pool)) {
+ odp_shm_free(shm_tbl);
+ ODPH_DBG("failed to destroy pre-existing pool\n");
+ return NULL;
+ }
odp_pool_param_init(&param);
param.type = ODP_POOL_BUFFER;
param.buf.size = kv_entry_size;
- param.buf.align = ODP_CACHE_LINE_SIZE;
+ if (pcapa.buf.max_align >= ODP_CACHE_LINE_SIZE)
+ param.buf.align = ODP_CACHE_LINE_SIZE;
param.buf.num = capacity;
pool = odp_pool_create(pool_name, &param);
@@ -263,7 +287,7 @@ odph_cuckoo_table_create(
queue = odp_queue_create(queue_name, &qparam);
if (queue == ODP_QUEUE_INVALID) {
ODPH_DBG("failed to create free_slots queue\n");
- odp_pool_destroy(pool);
+ (void)odp_pool_destroy(pool);
odp_shm_free(shm_tbl);
return NULL;
}
diff --git a/helper/eth.c b/helper/eth.c
index 9a151fa23..171563ca8 100644
--- a/helper/eth.c
+++ b/helper/eth.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#include <odp/helper/eth.h>
@@ -11,7 +9,7 @@
int odph_eth_addr_parse(odph_ethaddr_t *mac, const char *str)
{
- int byte[ODPH_ETHADDR_LEN];
+ unsigned int byte[ODPH_ETHADDR_LEN];
int i;
memset(byte, 0, sizeof(byte));
@@ -22,7 +20,7 @@ int odph_eth_addr_parse(odph_ethaddr_t *mac, const char *str)
return -1;
for (i = 0; i < ODPH_ETHADDR_LEN; i++)
- if (byte[i] < 0 || byte[i] > 255)
+ if (byte[i] > 255)
return -1;
mac->addr[0] = byte[0];
diff --git a/helper/hashtable.c b/helper/hashtable.c
index f26b18b27..5770dc43b 100644
--- a/helper/hashtable.c
+++ b/helper/hashtable.c
@@ -1,15 +1,15 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
+
#include <stdio.h>
#include <string.h>
#include <malloc.h>
+#include <stdalign.h>
-#include "odp/helper/odph_hashtable.h"
+#include <odp/helper/odph_hashtable.h>
+#include <odp/helper/odph_debug.h>
#include "odph_list_internal.h"
-#include "odph_debug.h"
#include <odp_api.h>
#define ODPH_SUCCESS 0
@@ -26,7 +26,7 @@
#define ODPH_MAX_BUCKET_NUM 0x10000
/** @inner element structure of hash table
- * To resolve the hash confict:
+ * To resolve the hash conflict:
* we put the elements with different keys but a same HASH-value
* into a list
*/
@@ -37,9 +37,9 @@ typedef struct odph_hash_node {
* Its length is key_size + value_size,
* suppose key_size = m; value_size = n;
* its structure is like:
- * k_byte1 k_byte2...k_byten v_byte1...v_bytem
+ * k_byte1 k_byte2...k_bytem v_byte1...v_byten
*/
- char content[0];
+ char content[];
} odph_hash_node;
typedef struct {
@@ -61,6 +61,14 @@ typedef struct {
char name[ODPH_TABLE_NAME_LEN]; /**< table name */
} odph_hash_table_imp;
+static uint32_t node_size(const odph_hash_table_imp *tbl)
+{
+ const uint32_t mask = alignof(odph_hash_node) - 1;
+
+ /* Round node size up so that pointers in the hash nodes are aligned. */
+ return (sizeof(odph_hash_node) + tbl->key_size + tbl->value_size + mask) & ~mask;
+}
+
odph_table_t odph_hash_table_create(const char *name, uint32_t capacity,
uint32_t key_size,
uint32_t value_size)
@@ -80,7 +88,7 @@ odph_table_t odph_hash_table_create(const char *name, uint32_t capacity,
ODPH_DBG("name already exist\n");
return NULL;
}
- shmem = odp_shm_reserve(name, capacity << 20, 64, ODP_SHM_SW_ONLY);
+ shmem = odp_shm_reserve(name, capacity << 20, 64, 0);
if (shmem == ODP_SHM_INVALID) {
ODPH_DBG("shm reserve fail\n");
return NULL;
@@ -109,7 +117,7 @@ odph_table_t odph_hash_table_create(const char *name, uint32_t capacity,
- ODPH_MAX_BUCKET_NUM * sizeof(odph_list_head)
- ODPH_MAX_BUCKET_NUM * sizeof(odp_rwlock_t);
- node_num = node_mem / (sizeof(odph_hash_node) + key_size + value_size);
+ node_num = node_mem / node_size(tbl);
tbl->hash_node_num = node_num;
tbl->hash_node_pool =
(odph_hash_node *)(void *)((char *)tbl->list_head_pool
@@ -198,9 +206,7 @@ static odph_hash_node *hashnode_take(odph_table_t table)
* should add the size of Flexible Array
*/
node = (odph_hash_node *)(void *)((char *)tbl->hash_node_pool
- + idx * (sizeof(odph_hash_node)
- + tbl->key_size
- + tbl->value_size));
+ + idx * node_size(tbl));
if (node->list_node.next == NULL &&
node->list_node.prev == NULL) {
ODPH_INIT_LIST_HEAD(&node->list_node);
@@ -223,8 +229,7 @@ static void hashnode_give(odph_table_t table, odph_hash_node *node)
tbl = (odph_hash_table_imp *)(void *)table;
odph_list_del(&node->list_node);
- memset(node, 0,
- (sizeof(odph_hash_node) + tbl->key_size + tbl->value_size));
+ memset(node, 0, node_size(tbl));
}
/* should make sure the input table exists and is available */
diff --git a/helper/include/odp/helper/.gitignore b/helper/include/odp/helper/.gitignore
new file mode 100644
index 000000000..67020331b
--- /dev/null
+++ b/helper/include/odp/helper/.gitignore
@@ -0,0 +1 @@
+version.h
diff --git a/helper/include/odp/helper/autoheader_external.h.in b/helper/include/odp/helper/autoheader_external.h.in
new file mode 100644
index 000000000..81f10a737
--- /dev/null
+++ b/helper/include/odp/helper/autoheader_external.h.in
@@ -0,0 +1,17 @@
+
+#ifndef ODPH_AUTOHEADER_EXTERNAL_H_
+#define ODPH_AUTOHEADER_EXTERNAL_H_
+
+/* Define to 1 to enable CLI helper */
+#undef ODPH_CLI
+
+/* Define to 1 to include additional helper debug code */
+#undef ODPH_DEBUG
+
+/* Define to 1 to display helper debug information */
+#undef ODPH_DEBUG_PRINT
+
+/* Define to 1 to enable deprecated helper API definitions */
+#undef ODPH_DEPRECATED_API
+
+#endif
diff --git a/helper/include/odp/helper/chksum.h b/helper/include/odp/helper/chksum.h
index 520e92753..f9b3f2dc2 100644
--- a/helper/include/odp/helper/chksum.h
+++ b/helper/include/odp/helper/chksum.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -18,8 +16,10 @@ extern "C" {
#include <odp_api.h>
-/** @addtogroup odph_chksum ODPH CHECK SUM
- * @{
+/** @defgroup odph_chksum ODPH CHECKSUM
+ * TCP/UDP/SCTP checksum
+ *
+ * @{
*/
/**
@@ -35,33 +35,6 @@ typedef enum {
} odph_chksum_op_t;
/**
- * Checksum
- *
- * @param buffer calculate chksum for buffer
- * @param len buffer length
- *
- * @return checksum value in host cpu order
- */
-static inline odp_u16sum_t odph_chksum(void *buffer, int len)
-{
- uint16_t *buf = (uint16_t *)buffer;
- uint32_t sum = 0;
- uint16_t result;
-
- for (sum = 0; len > 1; len -= 2)
- sum += *buf++;
-
- if (len == 1)
- sum += *(unsigned char *)buf;
-
- sum = (sum >> 16) + (sum & 0xFFFF);
- sum += (sum >> 16);
- result = ~sum;
-
- return (__odp_force odp_u16sum_t) result;
-}
-
-/**
* General Purpose TCP/UDP checksum function
*
* This function handles all the different checksum operations like
@@ -217,6 +190,48 @@ static inline int odph_udp_chksum_verify(odp_packet_t odp_pkt)
}
/**
+ * Generate SCTP checksum
+ *
+ * This function supports SCTP over either IPv4 or IPV6 - including handling
+ * any IPv4 header options and any IPv6 extension headers. However it
+ * does not handle tunneled pkts (i.e. any case where there is more than
+ * one IPv4/IPv6 header).
+ * This function also handles non-contiguous pkts. In particular it can
+ * handle arbitrary packet segmentation, including cases where the segments
+ * are not 2 byte aligned, nor have a length that is a multiple of 2. This
+ * function also can handle jumbo frames (at least up to 10K).
+ *
+ * This function will insert the calculated CRC32-c checksum into the proper
+ * location in the SCTP header.
+ *
+ * @param odp_pkt Calculate and insert chksum for this SCTP pkt, which can
+ * be over IPv4 or IPv6.
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odph_sctp_chksum_set(odp_packet_t odp_pkt);
+
+/**
+ * Verify SCTP checksum
+ *
+ * This function supports SCTP over either IPv4 or IPV6 - including handling
+ * any IPv4 header options and any IPv6 extension headers. However it
+ * does not handle tunneled pkts (i.e. any case where there is more than
+ * one IPv4/IPv6 header).
+ * This function also handles non-contiguous pkts. In particular it can
+ * handle arbitrary packet segmentation, including cases where the segments
+ * are not 2 byte aligned, nor have a length that is a multiple of 2. This
+ * function also can handle jumbo frames (at least up to 10K).
+ *
+ * @param odp_pkt Calculate and compare the chksum for this SCTP pkt,
+ * which can be over IPv4 or IPv6.
+ * @retval <0 on failure
+ * @retval 0 if the incoming chksum field is correct
+ * @retval 2 when the chksum field is incorrect
+ */
+int odph_sctp_chksum_verify(odp_packet_t odp_pkt);
+
+/**
* @}
*/
diff --git a/helper/include/odp/helper/cli.h b/helper/include/odp/helper/cli.h
new file mode 100644
index 000000000..8a77ab763
--- /dev/null
+++ b/helper/include/odp/helper/cli.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP CLI helper
+ */
+
+#ifndef ODPH_CLI_H_
+#define ODPH_CLI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdarg.h>
+
+/**
+ * @defgroup odph_cli ODPH CLI
+ * Command line interface
+ *
+ * @details
+ * This API allows control of ODP CLI server, which may be connected to using a
+ * telnet client. CLI commands may be used to get information from an ODP
+ * instance, for debugging purposes.
+ *
+ * @{
+ */
+
+/**
+ * User defined command function type. See odph_cli_register_command().
+ *
+ * The arguments (argv) are the arguments to the command given in the CLI
+ * client. For example, having registered a command with the name "my_command",
+ * and given the command "my_command one two" in the CLI client, the user
+ * command function would be called with argc = 2, argv[0] = "one" and argv[1] =
+ * "two".
+ */
+typedef void (*odph_cli_user_cmd_func_t)(int argc, char *argv[]);
+
+/** ODP CLI server parameters */
+typedef struct {
+ /**
+ * A character string containing an IP address. Default is
+ * "127.0.0.1".
+ */
+ const char *address;
+ /** TCP port. Default is 55555. */
+ uint16_t port;
+ /** Maximum number of user defined commands. Default is 50. */
+ uint32_t max_user_commands;
+ /** Hostname to be displayed as the first part of the prompt. */
+ const char *hostname;
+} odph_cli_param_t;
+
+/**
+ * Initialize CLI server params
+ *
+ * Initialize an odph_cli_param_t to its default values for all
+ * fields.
+ *
+ * @param[out] param Pointer to parameter structure
+ */
+void odph_cli_param_init(odph_cli_param_t *param);
+
+/**
+ * Initialize CLI helper
+ *
+ * This function initializes the CLI helper. It must be called before
+ * odph_cli_register_command() and odph_cli_run().
+ *
+ * In process mode (ODPH_PROC_MODE), this function must be called before
+ * creating the thread which calls odph_cli_run().
+ *
+ * @param param CLI server parameters to use
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odph_cli_init(const odph_cli_param_t *param);
+
+/**
+ * Register a user defined command
+ *
+ * Register a command with a name, function, and an optional help text. The
+ * registered command is displayed in the output of the "help" command. When the
+ * command is invoked by the CLI client, the registered function is called with
+ * the parameters entered by the CLI client user.
+ *
+ * Command names are case-insensitive. In the CLI client, they are displayed in
+ * the case they were registered in, but they may be invoked using any case.
+ *
+ * This function should be called after odph_cli_init() and before
+ * odph_cli_run().
+ *
+ * @param name Command name (case-insensitive)
+ * @param func Command function
+ * @param help Help or description for the command. This appears in the output
+ * of the "help" command. May be NULL.
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odph_cli_register_command(const char *name, odph_cli_user_cmd_func_t func,
+ const char *help);
+
+/**
+ * Run CLI server
+ *
+ * When executing this function, the CLI is accepting client connections and
+ * running commands from a client, if one is connected.
+ *
+ * This function should be called after odph_cli_init() and after any
+ * odph_cli_register_command() calls. After calling this function,
+ * odph_cli_stop() must be called before calling this function again.
+ *
+ * Returns only on a fatal error, or after odph_cli_stop() is called.
+ *
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odph_cli_run(void);
+
+/**
+ * Stop CLI server
+ *
+ * Stop accepting new client connections and disconnect any connected client.
+ *
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odph_cli_stop(void);
+
+/**
+ * Print to CLI
+ *
+ * A user defined command may call this function to print to the CLI client.
+ * This function should only be called in a user defined command (see
+ * odph_cli_register_command()). If called anywhere else, the behavior is
+ * undefined.
+ *
+ * @param fmt printf-style message format
+ * @return On success, the number of characters printed or buffered, without
+ * accounting for any line feed conversions. If an error is encountered,
+ * a negative value is returned.
+ */
+int odph_cli_log(const char *fmt, ...);
+
+/**
+ * Print to CLI
+ *
+ * Similar to odph_cli_log(), except that this one takes its arguments as
+ * a va_list.
+ *
+ * @param fmt printf-style message format
+ * @param in_args variadic arguments
+ * @return On success, the number of characters printed or buffered, without
+ * accounting for any line feed conversions. If an error is encountered,
+ * a negative value is returned.
+ */
+int odph_cli_log_va(const char *fmt, va_list in_args);
+
+/**
+ * Terminate CLI helper
+ *
+ * Free any resources allocated by the CLI helper.
+ *
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odph_cli_term(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/include/odp/helper/deprecated.h b/helper/include/odp/helper/deprecated.h
new file mode 100644
index 000000000..9251f2ca8
--- /dev/null
+++ b/helper/include/odp/helper/deprecated.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * Macro for deprecated API definitions
+ */
+
+#ifndef ODPH_DEPRECATED_H_
+#define ODPH_DEPRECATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/helper/autoheader_external.h>
+
+/**
+ * @def ODPH_DEPRECATE
+ *
+ * Macro to deprecate helper API definitions
+ */
+
+#if ODPH_DEPRECATED_API
+#define ODPH_DEPRECATE(x) x
+#else
+#define ODPH_DEPRECATE(x) _deprecated_ ## x
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/include/odp/helper/eth.h b/helper/include/odp/helper/eth.h
index 9f47ddfaf..2a1f3f159 100644
--- a/helper/include/odp/helper/eth.h
+++ b/helper/include/odp/helper/eth.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -19,8 +17,11 @@ extern "C" {
#include <odp_api.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @defgroup odph_protocols ODPH PROTOCOLS
+ * Network protocols
+ *
+ * @{
*/
#define ODPH_ETHADDR_LEN 6 /**< Ethernet address length */
@@ -51,13 +52,9 @@ extern "C" {
* Ethernet MAC address
*/
typedef struct ODP_PACKED {
- uint8_t addr[ODPH_ETHADDR_LEN]; /**< @private Address */
+ uint8_t addr[ODPH_ETHADDR_LEN]; /**< Address */
} odph_ethaddr_t;
-/** @internal Compile time assert */
-ODP_STATIC_ASSERT(sizeof(odph_ethaddr_t) == ODPH_ETHADDR_LEN,
- "ODPH_ETHADDR_T__SIZE_ERROR");
-
/**
* Ethernet header
*/
@@ -67,10 +64,6 @@ typedef struct ODP_PACKED {
odp_u16be_t type; /**< EtherType */
} odph_ethhdr_t;
-/** @internal Compile time assert */
-ODP_STATIC_ASSERT(sizeof(odph_ethhdr_t) == ODPH_ETHHDR_LEN,
- "ODPH_ETHHDR_T__SIZE_ERROR");
-
/**
* IEEE 802.1Q VLAN header
*
@@ -85,9 +78,16 @@ typedef struct ODP_PACKED {
odp_u16be_t type; /**< Inner EtherType */
} odph_vlanhdr_t;
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+ODP_STATIC_ASSERT(sizeof(odph_ethaddr_t) == ODPH_ETHADDR_LEN,
+ "ODPH_ETHADDR_T__SIZE_ERROR");
+
+ODP_STATIC_ASSERT(sizeof(odph_ethhdr_t) == ODPH_ETHHDR_LEN,
+ "ODPH_ETHHDR_T__SIZE_ERROR");
+
ODP_STATIC_ASSERT(sizeof(odph_vlanhdr_t) == ODPH_VLANHDR_LEN,
"ODPH_VLANHDR_T__SIZE_ERROR");
+/** @endcond */
/* Ethernet header Ether Type ('type') values, a selected few */
#define ODPH_ETHTYPE_IPV4 0x0800 /**< Internet Protocol version 4 */
diff --git a/helper/include/odp/helper/gtp.h b/helper/include/odp/helper/gtp.h
new file mode 100644
index 000000000..b4fb21cb8
--- /dev/null
+++ b/helper/include/odp/helper/gtp.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP GTP header
+ */
+#ifndef _ODPH_GTP_H_
+#define _ODPH_GTP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+
+/**
+ * @addtogroup odph_protocols
+ * @{
+ */
+
+/**
+ * Simplified GTP protocol header.
+ * Contains 8-bit gtp_hdr_info, 8-bit msg_type,
+ * 16-bit plen, 32-bit teid.
+ * No optional fields and next extension header.
+ */
+typedef struct ODP_PACKED {
+ uint8_t gtp_hdr_info; /**< GTP header info */
+ uint8_t msg_type; /**< GTP message type */
+ odp_u16be_t plen; /**< Total payload length */
+ odp_u32be_t teid; /**< Tunnel endpoint ID */
+} odph_gtphdr_t;
+
+/** GTP header length */
+#define ODPH_GTP_HLEN sizeof(odph_gtphdr_t)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_GTP_H_ */
diff --git a/helper/include/odp/helper/icmp.h b/helper/include/odp/helper/icmp.h
index bef967842..9dbc02a9f 100644
--- a/helper/include/odp/helper/icmp.h
+++ b/helper/include/odp/helper/icmp.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
@@ -20,8 +18,9 @@ extern "C" {
#include <odp_api.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @addtogroup odph_protocols
+ * @{
*/
/** ICMP header length */
@@ -48,55 +47,13 @@ typedef struct ODP_PACKED {
} un; /**< icmp sub header */
} odph_icmphdr_t;
-#define ICMP_ECHOREPLY 0 /**< Echo Reply */
-#define ICMP_DEST_UNREACH 3 /**< Destination Unreachable */
-#define ICMP_SOURCE_QUENCH 4 /**< Source Quench */
-#define ICMP_REDIRECT 5 /**< Redirect (change route) */
-#define ICMP_ECHO 8 /**< Echo Request */
-#define ICMP_TIME_EXCEEDED 11 /**< Time Exceeded */
-#define ICMP_PARAMETERPROB 12 /**< Parameter Problem */
-#define ICMP_TIMESTAMP 13 /**< Timestamp Request */
-#define ICMP_TIMESTAMPREPLY 14 /**< Timestamp Reply */
-#define ICMP_INFO_REQUEST 15 /**< Information Request */
-#define ICMP_INFO_REPLY 16 /**< Information Reply */
-#define ICMP_ADDRESS 17 /**< Address Mask Request */
-#define ICMP_ADDRESSREPLY 18 /**< Address Mask Reply */
-#define NR_ICMP_TYPES 18 /**< Number of icmp types */
-
-/* Codes for UNREACH. */
-#define ICMP_NET_UNREACH 0 /**< Network Unreachable */
-#define ICMP_HOST_UNREACH 1 /**< Host Unreachable */
-#define ICMP_PROT_UNREACH 2 /**< Protocol Unreachable */
-#define ICMP_PORT_UNREACH 3 /**< Port Unreachable */
-#define ICMP_FRAG_NEEDED 4 /**< Fragmentation Needed/DF set*/
-#define ICMP_SR_FAILED 5 /**< Source Route failed */
-#define ICMP_NET_UNKNOWN 6 /**< Network Unknown */
-#define ICMP_HOST_UNKNOWN 7 /**< Host Unknown */
-#define ICMP_HOST_ISOLATED 8 /**< Host Isolated */
-#define ICMP_NET_ANO 9 /**< ICMP_NET_ANO */
-#define ICMP_HOST_ANO 10 /**< ICMP_HOST_ANO */
-#define ICMP_NET_UNR_TOS 11 /**< ICMP_NET_UNR_TOS */
-#define ICMP_HOST_UNR_TOS 12 /**< ICMP_HOST_UNR_TOS */
-#define ICMP_PKT_FILTERED 13 /**< Packet filtered */
-#define ICMP_PREC_VIOLATION 14 /**< Precedence violation */
-#define ICMP_PREC_CUTOFF 15 /**< Precedence cut off */
-#define NR_ICMP_UNREACH 15 /**< instead of hardcoding
- immediate value */
-
-/* Codes for REDIRECT. */
-#define ICMP_REDIR_NET 0 /**< Redirect Net */
-#define ICMP_REDIR_HOST 1 /**< Redirect Host */
-#define ICMP_REDIR_NETTOS 2 /**< Redirect Net for TOS */
-#define ICMP_REDIR_HOSTTOS 3 /**< Redirect Host for TOS */
-
-/* Codes for TIME_EXCEEDED. */
-#define ICMP_EXC_TTL 0 /**< TTL count exceeded */
-#define ICMP_EXC_FRAGTIME 1 /**< Fragment Reass time
- exceeded*/
+#define ODPH_ICMP_ECHOREPLY 0 /**< Echo Reply */
+#define ODPH_ICMP_ECHO 8 /**< Echo Request */
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
ODP_STATIC_ASSERT(sizeof(odph_icmphdr_t) == ODPH_ICMPHDR_LEN,
"ODPH_ICMPHDR_T__SIZE_ERROR");
+/** @endcond */
/**
* @}
diff --git a/helper/include/odp/helper/igmp.h b/helper/include/odp/helper/igmp.h
new file mode 100644
index 000000000..64adc1bda
--- /dev/null
+++ b/helper/include/odp/helper/igmp.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP IGMP header
+ */
+#ifndef _ODPH_IGMP_H_
+#define _ODPH_IGMP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+
+/**
+ * @addtogroup odph_protocols
+ * @{
+ */
+
+/**
+ * Simplified IGMP protocol header.
+ * Contains 8-bit type, 8-bit code,
+ * 16-bit csum, 32-bit group.
+ * No optional fields and next extension header.
+ */
+typedef struct ODP_PACKED {
+ uint8_t type; /**< Message Type */
+ uint8_t code; /**< Max response code */
+ odp_u16be_t csum; /**< Checksum */
+ odp_u32be_t group; /**< Group address */
+} odph_igmphdr_t;
+
+/** IGMP header length */
+#define ODPH_IGMP_HLEN sizeof(odph_igmphdr_t)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_IGMP_H_ */
diff --git a/helper/include/odp/helper/ip.h b/helper/include/odp/helper/ip.h
index 91776fad6..733c145d8 100644
--- a/helper/include/odp/helper/ip.h
+++ b/helper/include/odp/helper/ip.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -22,8 +20,9 @@ extern "C" {
#include <string.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @addtogroup odph_protocols
+ * @{
*/
#define ODPH_IPV4 4 /**< IP version 4 */
@@ -74,6 +73,9 @@ extern "C" {
/** @internal Returns true if IPv4 packet is a fragment */
#define ODPH_IPV4HDR_IS_FRAGMENT(frag_offset) ((frag_offset) & 0x3fff)
+/** @internal Checksum offset in IPv4 header */
+#define ODPH_IPV4HDR_CSUM_OFFSET 10
+
/** IPv4 header */
typedef struct ODP_PACKED {
uint8_t ver_ihl; /**< Version / Header length */
@@ -88,9 +90,45 @@ typedef struct ODP_PACKED {
odp_u32be_t dst_addr; /**< Destination address */
} odph_ipv4hdr_t;
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
ODP_STATIC_ASSERT(sizeof(odph_ipv4hdr_t) == ODPH_IPV4HDR_LEN,
"ODPH_IPV4HDR_T__SIZE_ERROR");
+/** @endcond */
+
+/**
+ * Calculate IPv4 header checksum
+ *
+ * @param pkt The packet to be checksummed
+ * @param offset Offset into pkt of start of IP header
+ * @param ip Pointer to IPv4 header to be checksummed
+ * @param[out] chksum Field to receive checksum results
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ */
+static inline int odph_ipv4_csum(odp_packet_t pkt,
+ uint32_t offset,
+ odph_ipv4hdr_t *ip,
+ odp_u16sum_t *chksum)
+{
+ uint32_t nleft = (uint32_t)(ODPH_IPV4HDR_IHL(ip->ver_ihl) * 4);
+ uint16_t buf[nleft / 2];
+ int res;
+
+ if (odp_unlikely(nleft < sizeof(*ip)))
+ return -1;
+ ip->chksum = 0;
+ memcpy(buf, ip, sizeof(*ip));
+ res = odp_packet_copy_to_mem(pkt, offset + (uint32_t)sizeof(*ip),
+ nleft - (uint32_t)sizeof(*ip),
+ buf + sizeof(*ip) / 2);
+ if (odp_unlikely(res < 0))
+ return res;
+
+ *chksum = (odp_u16sum_t)~odp_chksum_ones_comp16(buf, nleft);
+
+ return 0;
+}
/**
* Check if IPv4 checksum is valid
@@ -102,49 +140,57 @@ ODP_STATIC_ASSERT(sizeof(odph_ipv4hdr_t) == ODPH_IPV4HDR_LEN,
static inline int odph_ipv4_csum_valid(odp_packet_t pkt)
{
uint32_t offset;
- odp_u16be_t res = 0;
- uint16_t *w;
- int nleft = sizeof(odph_ipv4hdr_t);
+ int res;
odph_ipv4hdr_t ip;
- odp_u16be_t chksum;
+ odp_u16sum_t chksum, cur_chksum;
offset = odp_packet_l3_offset(pkt);
if (offset == ODP_PACKET_OFFSET_INVALID)
return 0;
- odp_packet_copy_to_mem(pkt, offset, sizeof(odph_ipv4hdr_t), &ip);
+ res = odp_packet_copy_to_mem(pkt, offset, sizeof(odph_ipv4hdr_t), &ip);
+ if (odp_unlikely(res < 0))
+ return 0;
- w = (uint16_t *)(void *)&ip;
chksum = ip.chksum;
- ip.chksum = 0x0;
- res = odph_chksum(w, nleft);
- return (res == chksum) ? 1 : 0;
+ res = odph_ipv4_csum(pkt, offset, &ip, &cur_chksum);
+ if (odp_unlikely(res < 0))
+ return 0;
+
+ return (cur_chksum == chksum) ? 1 : 0;
}
/**
* Calculate and fill in IPv4 checksum
*
- * @note when using this api to populate data destined for the wire
- * odp_cpu_to_be_16() can be used to remove sparse warnings
- *
* @param pkt ODP packet
*
- * @return IPv4 checksum in host cpu order, or 0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-static inline odp_u16sum_t odph_ipv4_csum_update(odp_packet_t pkt)
+static inline int odph_ipv4_csum_update(odp_packet_t pkt)
{
- uint16_t *w;
- odph_ipv4hdr_t *ip;
- int nleft = sizeof(odph_ipv4hdr_t);
+ uint32_t offset;
+ odph_ipv4hdr_t ip;
+ odp_u16sum_t chksum;
+ int res;
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- if (ip == NULL)
- return 0;
+ offset = odp_packet_l3_offset(pkt);
+ if (offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
+
+ res = odp_packet_copy_to_mem(pkt, offset, sizeof(ip), &ip);
+ if (odp_unlikely(res < 0))
+ return res;
- w = (uint16_t *)(void *)ip;
- ip->chksum = odph_chksum(w, nleft);
- return ip->chksum;
+ res = odph_ipv4_csum(pkt, offset, &ip, &chksum);
+ if (odp_unlikely(res < 0))
+ return res;
+
+ return odp_packet_copy_from_mem(pkt,
+ offset + ODPH_IPV4HDR_CSUM_OFFSET,
+ 2, &chksum);
}
/** IPv6 version */
@@ -186,9 +232,10 @@ typedef struct ODP_PACKED {
uint8_t dst_addr[16]; /**< Destination address */
} odph_ipv6hdr_t;
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
ODP_STATIC_ASSERT(sizeof(odph_ipv6hdr_t) == ODPH_IPV6HDR_LEN,
"ODPH_IPV6HDR_T__SIZE_ERROR");
+/** @endcond */
/**
* IPv6 Header extensions
@@ -201,22 +248,23 @@ typedef struct ODP_PACKED {
uint8_t filler[6]; /**< Fill out first 8 byte segment */
} odph_ipv6hdr_ext_t;
-/** @name
+/*
* IP protocol values (IPv4:'proto' or IPv6:'next_hdr')
- * @{*/
+ */
#define ODPH_IPPROTO_HOPOPTS 0x00 /**< IPv6 hop-by-hop options */
-#define ODPH_IPPROTO_ICMPv4 0x01 /**< Internet Control Message Protocol (1) */
+#define ODPH_IPPROTO_ICMPV4 0x01 /**< Internet Control Message Protocol (1) */
+#define ODPH_IPPROTO_IGMP 0x02 /**< Internet Group Message Protocol (1) */
#define ODPH_IPPROTO_TCP 0x06 /**< Transmission Control Protocol (6) */
#define ODPH_IPPROTO_UDP 0x11 /**< User Datagram Protocol (17) */
#define ODPH_IPPROTO_ROUTE 0x2B /**< IPv6 Routing header (43) */
#define ODPH_IPPROTO_FRAG 0x2C /**< IPv6 Fragment (44) */
#define ODPH_IPPROTO_AH 0x33 /**< Authentication Header (51) */
#define ODPH_IPPROTO_ESP 0x32 /**< Encapsulating Security Payload (50) */
-#define ODPH_IPPROTO_ICMPv6 0x3A /**< Internet Control Message Protocol (58) */
+#define ODPH_IPPROTO_ICMPV6 0x3A /**< Internet Control Message Protocol (58) */
+#define ODPH_IPPROTO_SCTP 0x84 /**< Stream Control Transmission protocol
+ (132) */
#define ODPH_IPPROTO_INVALID 0xFF /**< Reserved invalid by IANA */
-/**@}*/
-
/**
* Parse IPv4 address from a string
*
diff --git a/helper/include/odp/helper/ipsec.h b/helper/include/odp/helper/ipsec.h
index 034a34115..11b7a3829 100644
--- a/helper/include/odp/helper/ipsec.h
+++ b/helper/include/odp/helper/ipsec.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
*/
@@ -20,8 +19,9 @@ extern "C" {
#include <odp_api.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @addtogroup odph_protocols
+ * @{
*/
#define ODPH_ESPHDR_LEN 8 /**< IPSec ESP header length */
@@ -34,26 +34,18 @@ extern "C" {
typedef struct ODP_PACKED {
odp_u32be_t spi; /**< Security Parameter Index */
odp_u32be_t seq_no; /**< Sequence Number */
- uint8_t iv[0]; /**< Initialization vector */
+ uint8_t iv[]; /**< Initialization vector */
} odph_esphdr_t;
-/** @internal Compile time assert */
-ODP_STATIC_ASSERT(sizeof(odph_esphdr_t) == ODPH_ESPHDR_LEN,
- "ODPH_ESPHDR_T__SIZE_ERROR");
-
/**
* IPSec ESP trailer
*/
typedef struct ODP_PACKED {
uint8_t pad_len; /**< Padding length (0-255) */
uint8_t next_header; /**< Next header protocol */
- uint8_t icv[0]; /**< Integrity Check Value (optional) */
+ uint8_t icv[]; /**< Integrity Check Value (optional) */
} odph_esptrl_t;
-/** @internal Compile time assert */
-ODP_STATIC_ASSERT(sizeof(odph_esptrl_t) == ODPH_ESPTRL_LEN,
- "ODPH_ESPTRL_T__SIZE_ERROR");
-
/**
* IPSec AH header
*/
@@ -63,12 +55,54 @@ typedef struct ODP_PACKED {
odp_u16be_t pad; /**< Padding (must be 0) */
odp_u32be_t spi; /**< Security Parameter Index */
odp_u32be_t seq_no; /**< Sequence Number */
- uint8_t icv[0]; /**< Integrity Check Value */
+ uint8_t icv[]; /**< Integrity Check Value */
} odph_ahhdr_t;
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+ODP_STATIC_ASSERT(sizeof(odph_esphdr_t) == ODPH_ESPHDR_LEN,
+ "ODPH_ESPHDR_T__SIZE_ERROR");
+
+ODP_STATIC_ASSERT(sizeof(odph_esptrl_t) == ODPH_ESPTRL_LEN,
+ "ODPH_ESPTRL_T__SIZE_ERROR");
+
ODP_STATIC_ASSERT(sizeof(odph_ahhdr_t) == ODPH_AHHDR_LEN,
"ODPH_AHHDR_T__SIZE_ERROR");
+/** @endcond */
+
+/**
+ * Check IPSEC algorithm support
+ *
+ * Based on the capabilities exposed by the ODP implementation, check whether
+ * the specified IPSEC algorithm configuration with the default ICV length
+ * is supported by the implementation. The caller provides the IPSEC
+ * capability structure as an argument to the helper function.
+ *
+ * @param capa IPSEC capability structure
+ * @param cipher_alg Cipher algorithm
+ * @param cipher_key_len Length of cipher key in bytes
+ * @param auth_alg Authentication algorithm
+ * @param auth_key_len Length of authentication key in bytes
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odph_ipsec_alg_check(const odp_ipsec_capability_t *capa,
+ odp_cipher_alg_t cipher_alg,
+ uint32_t cipher_key_len,
+ odp_auth_alg_t auth_alg,
+ uint32_t auth_key_len);
+
+/**
+ * Return the default ICV length of an algorithm
+ *
+ * IPsec API specifies default ICV length for each authentication and
+ * combined mode algorithm. This function returns the default ICV length.
+ *
+ * @param auth_alg Authentication algorithm
+ *
+ * @return The default ICV length in bytes
+ */
+uint32_t odph_ipsec_auth_icv_len_default(odp_auth_alg_t auth_alg);
/**
* @}
diff --git a/helper/include/odp/helper/linux.h b/helper/include/odp/helper/linux.h
index 396203a27..df852569f 100644
--- a/helper/include/odp/helper/linux.h
+++ b/helper/include/odp/helper/linux.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
diff --git a/helper/include/odp/helper/linux/process.h b/helper/include/odp/helper/linux/process.h
index 9d74146b0..0f34d604b 100644
--- a/helper/include/odp/helper/linux/process.h
+++ b/helper/include/odp/helper/linux/process.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
@@ -11,20 +9,22 @@
*
* This file is not part of ODP APIs, but can be optionally used to ease common
* setups in a Linux system. User is free to implement the same setups in
- * otherways (not via this file).
+ * other ways (not via this file).
*/
#ifndef ODPH_LINUX_PROCESS_H_
#define ODPH_LINUX_PROCESS_H_
#include <odp/helper/threads.h>
+#include <odp_api.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @ingroup odph_linux
- * @{
+/**
+ * @addtogroup odph_thread
+ * @{
*/
/**
diff --git a/helper/include/odp/helper/linux/pthread.h b/helper/include/odp/helper/linux/pthread.h
index feeda5e28..5a2f6fd53 100644
--- a/helper/include/odp/helper/linux/pthread.h
+++ b/helper/include/odp/helper/linux/pthread.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
@@ -11,20 +9,22 @@
*
* This file is not part of ODP APIs, but can be optionally used to ease common
* setups in a Linux system. User is free to implement the same setups in
- * otherways (not via this file).
+ * other ways (not via this file).
*/
#ifndef ODPH_LINUX_PTHREAD_H_
#define ODPH_LINUX_PTHREAD_H_
#include <odp/helper/threads.h>
+#include <odp_api.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @ingroup odph_linux
- * @{
+/**
+ * @addtogroup odph_thread
+ * @{
*/
/**
diff --git a/helper/include/odp/helper/macros.h b/helper/include/odp/helper/macros.h
new file mode 100644
index 000000000..1623f17e7
--- /dev/null
+++ b/helper/include/odp/helper/macros.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * Common helper macros
+ */
+
+#ifndef ODPH_MACROS_H_
+#define ODPH_MACROS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup odph_macros ODPH MACROS
+ * Helper macros
+ *
+ * @{
+ */
+
+/**
+ * Return number of elements in array
+ */
+#define ODPH_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/**
+ * Return minimum of two numbers
+ */
+#define ODPH_MIN(a, b) \
+ __extension__ ({ \
+ __typeof__(a) min_a = (a); \
+ __typeof__(b) min_b = (b); \
+ min_a < min_b ? min_a : min_b; \
+ })
+
+/**
+ * Return maximum of two numbers
+ */
+#define ODPH_MAX(a, b) \
+ __extension__ ({ \
+ __typeof__(a) max_a = (a); \
+ __typeof__(b) max_b = (b); \
+ max_a > max_b ? max_a : max_b; \
+ })
+
+/**
+ * Return absolute value of signed variable
+ */
+#define ODPH_ABS(v) \
+ __extension__ ({ \
+ __typeof__(v) abs_v = (v); \
+ abs_v < 0 ? -abs_v : abs_v; \
+ })
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODPH_MACROS_H_ */
diff --git a/helper/include/odp/helper/odph_api.h b/helper/include/odp/helper/odph_api.h
index 7ed0e7786..94d43a61b 100644
--- a/helper/include/odp/helper/odph_api.h
+++ b/helper/include/odp/helper/odph_api.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
@@ -18,20 +16,32 @@
extern "C" {
#endif
+#include <odp/helper/autoheader_external.h>
+
+#include <odp/helper/odph_debug.h>
#include <odp/helper/chksum.h>
#include <odp/helper/odph_cuckootable.h>
#include <odp/helper/eth.h>
+#include <odp/helper/gtp.h>
#include <odp/helper/odph_hashtable.h>
#include <odp/helper/icmp.h>
+#include <odp/helper/igmp.h>
#include <odp/helper/ip.h>
#include <odp/helper/ipsec.h>
+#include <odp/helper/macros.h>
#include <odp/helper/odph_lineartable.h>
#include <odp/helper/odph_iplookuptable.h>
+#include <odp/helper/sctp.h>
#include <odp/helper/strong_types.h>
#include <odp/helper/tcp.h>
#include <odp/helper/table.h>
#include <odp/helper/threads.h>
#include <odp/helper/udp.h>
+#include <odp/helper/version.h>
+
+#ifdef ODPH_CLI
+#include <odp/helper/cli.h>
+#endif
#ifdef __cplusplus
}
diff --git a/helper/include/odp/helper/odph_cuckootable.h b/helper/include/odp/helper/odph_cuckootable.h
index 55f43fed0..1c87a3d42 100644
--- a/helper/include/odp/helper/odph_cuckootable.h
+++ b/helper/include/odp/helper/odph_cuckootable.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/*-
@@ -53,7 +51,9 @@ extern "C" {
#endif
/**
- * @addtogroup odph_cuckootable ODPH CUCKOO TABLE
+ * @defgroup odph_cuckootable ODPH CUCKOO TABLE
+ * Cuckoo table
+ *
* @{
*/
diff --git a/helper/include/odp/helper/odph_debug.h b/helper/include/odp/helper/odph_debug.h
new file mode 100644
index 000000000..41b425ab2
--- /dev/null
+++ b/helper/include/odp/helper/odph_debug.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
+ */
+
+/**
+ * @file
+ *
+ * Helper debug
+ */
+
+#ifndef ODPH_DEBUG_H_
+#define ODPH_DEBUG_H_
+
+#include <odp/helper/autoheader_external.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma GCC diagnostic push
+
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/**
+ * @defgroup odph_debug ODPH DEBUG
+ * Debug logging
+ *
+ * @{
+ */
+
+/**
+ * Assert macro for applications and helper code
+ *
+ * No code is generated when ODPH_DEBUG=0. Prints error message and aborts when
+ * ODPH_DEBUG=1 and 'cond' is false.
+ */
+#define ODPH_ASSERT(cond) \
+ do { \
+ if ((ODPH_DEBUG == 1) && (!(cond))) { \
+ fprintf(stderr, "%s:%d:%s(): %s\n", __FILE__, __LINE__,\
+ __func__, #cond); \
+ abort(); \
+ } \
+ } while (0)
+
+/**
+ * log level.
+ */
+typedef enum odph_log_level {
+ ODPH_LOG_DBG,
+ ODPH_LOG_ERR,
+ ODPH_LOG_ABORT
+} odph_log_level_e;
+
+/**
+ * default LOG macro.
+ */
+#define ODPH_LOG(level, fmt, ...) \
+do { \
+ if (level != ODPH_LOG_DBG || ODPH_DEBUG_PRINT == 1) \
+ fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); \
+ if (level == ODPH_LOG_ABORT) \
+ abort(); \
+} while (0)
+
+/**
+ * Debug printing macro, which prints output when DEBUG flag is set.
+ */
+#define ODPH_DBG(...) \
+ do { \
+ __extension__ ({ \
+ ODPH_LOG(ODPH_LOG_DBG, ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * Print output to stderr (file, line and function).
+ */
+#define ODPH_ERR(...) \
+ do { \
+ __extension__ ({ \
+ ODPH_LOG(ODPH_LOG_ERR, ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * Print output to stderr (file, line and function),
+ * then abort.
+ */
+#define ODPH_ABORT(...) \
+ do { \
+ __extension__ ({ \
+ ODPH_LOG(ODPH_LOG_ABORT, ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * @}
+ */
+
+#pragma GCC diagnostic pop
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/include/odp/helper/odph_hashtable.h b/helper/include/odp/helper/odph_hashtable.h
index 1f91f31ec..b2dd21920 100644
--- a/helper/include/odp/helper/odph_hashtable.h
+++ b/helper/include/odp/helper/odph_hashtable.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -20,7 +18,9 @@ extern "C" {
#endif
/**
- * @addtogroup odph_hash_table ODPH HASH TABLE
+ * @defgroup odph_hash_table ODPH HASH TABLE
+ * Hash table
+ *
* @{
*/
diff --git a/helper/include/odp/helper/odph_iplookuptable.h b/helper/include/odp/helper/odph_iplookuptable.h
index 84c2cfdd6..41235ecc6 100644
--- a/helper/include/odp/helper/odph_iplookuptable.h
+++ b/helper/include/odp/helper/odph_iplookuptable.h
@@ -1,18 +1,11 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
* @file
*
* ODP IP Lookup Table
- *
- * This is an implementation of the IP lookup table. The key of
- * this table is IPv4 address (32 bits), and the value can be
- * defined by user. This table uses the 16,8,8 ip lookup (longest
- * prefix matching) algorithm.
*/
#ifndef ODPH_IPLOOKUP_TABLE_H_
@@ -25,7 +18,14 @@ extern "C" {
#endif
/**
- * @addtogroup odph_iplookuptable ODPH IP LOOKUP TABLE
+ * @defgroup odph_iplookuptable ODPH IP LOOKUP TABLE
+ * IP lookup table
+ *
+ * @details
+ * This is an implementation of the IP lookup table. The key of this table is
+ * IPv4 address (32 bits), and the value can be defined by user. This table uses
+ * the 16,8,8 ip lookup (longest prefix matching) algorithm.
+ *
* @{
*/
diff --git a/helper/include/odp/helper/odph_lineartable.h b/helper/include/odp/helper/odph_lineartable.h
index cd9fda39e..dc61113a5 100644
--- a/helper/include/odp/helper/odph_lineartable.h
+++ b/helper/include/odp/helper/odph_lineartable.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -21,7 +19,9 @@ extern "C" {
#endif
/**
- * @addtogroup odph_lineartable ODPH LINEAR TABLE
+ * @defgroup odph_lineartable ODPH LINEAR TABLE
+ * Linear table
+ *
* @{
*/
diff --git a/helper/include/odp/helper/sctp.h b/helper/include/odp/helper/sctp.h
new file mode 100644
index 000000000..f6661e390
--- /dev/null
+++ b/helper/include/odp/helper/sctp.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP SCTP header
+ */
+
+#ifndef ODPH_SCTP_H_
+#define ODPH_SCTP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+
+/**
+ * @addtogroup odph_protocols
+ * @{
+ */
+
+/** SCTP header length */
+#define ODPH_SCTPHDR_LEN 12
+
+/** SCTP header */
+typedef struct ODP_PACKED {
+ odp_u16be_t src_port; /**< Source port */
+ odp_u16be_t dst_port; /**< Destination port */
+ odp_u32be_t tag; /**< Verification tag */
+ odp_u32be_t chksum; /**< SCTP header and data checksum */
+} odph_sctphdr_t;
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+ODP_STATIC_ASSERT(sizeof(odph_sctphdr_t) == ODPH_SCTPHDR_LEN,
+ "ODPH_SCTPHDR_T__SIZE_ERROR");
+/** @endcond */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/include/odp/helper/strong_types.h b/helper/include/odp/helper/strong_types.h
index 501d0f28f..b0f504c16 100644
--- a/helper/include/odp/helper/strong_types.h
+++ b/helper/include/odp/helper/strong_types.h
@@ -1,24 +1,18 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
* @file
*
* ODP Strong Types. Common macros for implementing strong typing
- * for ODP abstract data types
+ * for ODPH abstract data types
*/
#ifndef ODPH_STRONG_TYPES_H_
#define ODPH_STRONG_TYPES_H_
-/** @addtogroup odph_strong_types ODPH STRONG TYPES
- * @{
- */
-
-/** Use strong typing for ODP types */
+/** Use strong typing for ODPH types */
#ifdef __cplusplus
/** @internal C++ helper macro for strong typing @param type @return */
#define ODPH_HANDLE_T(type) struct _##type { uint8_t unused_dummy_var; } *type
@@ -28,17 +22,13 @@
#define ODPH_HANDLE_T(type) odph_handle_t type
#endif
-/** Internal macro to get value of an ODP handle */
-#define _odph_typeval(handle) ((uint32_t)(uintptr_t)(handle))
+/** Internal macro to get value of an ODPH handle */
+#define _odph_typeval(handle) ((uintptr_t)(handle))
-/** Internal macro to get printable value of an ODP handle */
-#define _odph_pri(handle) ((uint64_t)_odph_typeval(handle))
+/** Internal macro to get printable value of an ODPH handle */
+#define _odph_pri(handle) ((uint64_t)(uintptr_t)(handle))
/** Internal macro to convert a scalar to a typed handle */
#define _odph_cast_scalar(type, val) ((type)(uintptr_t)(val))
-/**
- * @}
- */
-
#endif
diff --git a/helper/include/odp/helper/table.h b/helper/include/odp/helper/table.h
index 96c9c5fe7..6a24e742b 100644
--- a/helper/include/odp/helper/table.h
+++ b/helper/include/odp/helper/table.h
@@ -1,14 +1,25 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
* @file
*
* ODP table
+ */
+
+#ifndef ODPH_TABLE_H_
+#define ODPH_TABLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup odph_tables ODPH TABLES
+ * Table interface
*
+ * @details
* TCAM(Ternary Content Addressable Memory) is used widely in packet
* forwarding to speedup the table lookup.
*
@@ -56,7 +67,7 @@
* actions and action meta-data describing what processing to be
* applied for the packets of the current flow, such as whether
* encryption/decryption is required on this packet, what kind of cipher
- * algorithm should be chosed.
+ * algorithm should be chosen.
* <li>Algorithm: Hash
* </ol>
*
@@ -71,17 +82,6 @@
* notes: key/value and key/associated data mean the same thing
* in this file unless otherwise mentioned.
*
- */
-
-#ifndef ODPH_TABLE_H_
-#define ODPH_TABLE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @addtogroup odph_tables ODPH TABLES
* @{
*/
@@ -149,7 +149,7 @@ typedef int (*odph_table_destroy)(odph_table_t table);
/**
* Add (key,associated data) pair into the specific table.
- * When no associated data is currently assocated with key,
+ * When no associated data is currently associated with key,
* then the (key,assocatied data) association is created.
* When key is already associated with data0, then association (key, data0)
* will be removed and association (key, associated data) is created.
diff --git a/helper/include/odp/helper/tcp.h b/helper/include/odp/helper/tcp.h
index e91b52e24..8a14efa15 100644
--- a/helper/include/odp/helper/tcp.h
+++ b/helper/include/odp/helper/tcp.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
@@ -20,8 +18,9 @@ extern "C" {
#include <odp_api.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @addtogroup odph_protocols
+ * @{
*/
#define ODPH_TCPHDR_LEN 20 /**< Min length of TCP header (no options) */
diff --git a/helper/include/odp/helper/threads.h b/helper/include/odp/helper/threads.h
index 526f0d489..c18a46e8a 100644
--- a/helper/include/odp/helper/threads.h
+++ b/helper/include/odp/helper/threads.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
*/
@@ -10,9 +9,9 @@
*
* ODP Linux helper API
*
- * This file is an optional helper to odp.h APIs. These functions are provided
+ * This file is an optional helper to ODP APIs. These functions are provided
* to ease common setups in a Linux system. User is free to implement the same
- * setups in otherways (not via this API).
+ * setups in other ways (not via this API).
*/
#ifndef ODPH_LINUX_H_
@@ -22,12 +21,17 @@
extern "C" {
#endif
+#include <odp_api.h>
+
#include <pthread.h>
#include <getopt.h>
#include <sys/types.h>
-/** @addtogroup odph_linux ODPH LINUX
- * @{
+/**
+ * @defgroup odph_thread ODPH THREAD
+ * Setup threads/processes
+ *
+ * @{
*/
/** Thread parameter for Linux pthreads and processes */
@@ -54,32 +58,53 @@ typedef struct {
int status; /**< Process state change status */
} odph_linux_process_t;
-/** odpthread linux type: whether an ODP thread is a linux thread or process */
-typedef enum odph_odpthread_linuxtype_e {
- ODPTHREAD_NOT_STARTED = 0,
- ODPTHREAD_PROCESS,
- ODPTHREAD_PTHREAD
-} odph_odpthread_linuxtype_t;
-
-/** odpthread parameters for odp threads (pthreads and processes) */
+/** Thread parameters (pthreads and processes) */
typedef struct {
- int (*start)(void *); /**< Thread entry point function */
- void *arg; /**< Argument for the function */
- odp_thread_type_t thr_type; /**< ODP thread type */
- odp_instance_t instance; /**< ODP instance handle */
-} odph_odpthread_params_t;
+ /** Thread entry point function */
+ int (*start)(void *arg);
-/** The odpthread starting arguments, used both in process or thread mode */
+ /** Argument for the function */
+ void *arg;
+
+ /** ODP thread type */
+ odp_thread_type_t thr_type;
+
+ /** Minimum stack size in bytes. 0 = use default. */
+ uint64_t stack_size;
+
+} odph_thread_param_t;
+
+/** Helper internal thread start arguments. Used both in process and thread
+ * mode */
typedef struct {
- odph_odpthread_linuxtype_t linuxtype; /**< process or pthread */
- odph_odpthread_params_t thr_params; /**< odpthread start parameters */
-} odph_odpthread_start_args_t;
+ /** Thread status */
+ uint32_t status;
+
+ /** Thread initialization status */
+ odp_atomic_u32_t *init_status;
+
+ /** Process or thread */
+ odp_mem_model_t mem_model;
+
+ /** ODP instance handle */
+ odp_instance_t instance;
-/** Linux odpthread state information, used both in process or thread mode */
+ /** Thread parameters */
+ odph_thread_param_t thr_params;
+
+} odph_thread_start_args_t;
+
+/** Thread state information. Used both in process and thread mode */
typedef struct {
- odph_odpthread_start_args_t start_args; /**< start arguments */
- int cpu; /**< CPU ID */
- int last; /**< true if last table entry */
+ /** Start arguments */
+ odph_thread_start_args_t start_args;
+
+ /** CPU ID */
+ int cpu;
+
+ /** 1: last table entry */
+ uint8_t last;
+
/** Variant field mappings for thread/process modes */
union {
/** For thread implementation */
@@ -87,41 +112,164 @@ typedef struct {
pthread_t thread_id; /**< Pthread ID */
pthread_attr_t attr; /**< Pthread attributes */
} thread;
+
/** For process implementation */
struct {
pid_t pid; /**< Process ID */
int status; /**< Process state chge status*/
} proc;
};
-} odph_odpthread_t;
+
+} odph_thread_t;
+
+/** Linux helper options */
+typedef struct {
+ odp_mem_model_t mem_model; /**< Process or thread */
+} odph_helper_options_t;
+
+/** Common parameters for odph_thread_create() call */
+typedef struct {
+ /**
+ * ODP instance handle
+ *
+ * This is used for all threads, instead of 'instance' field of per
+ * thread parameters (odph_thread_param_t).
+ */
+ odp_instance_t instance;
+
+ /**
+ * CPU mask for thread pinning
+ */
+ const odp_cpumask_t *cpumask;
+
+ /**
+ * Select between Linux pthreads and processes
+ *
+ * 0: Use pthreads
+ * 1: Use processes
+ *
+ * Default value is 0.
+ */
+ int thread_model;
+
+ /**
+ * Synchronized thread creation
+ *
+ * 0: Don't synchronize thread creation
+ * 1: Create threads in series so that the next thread is created
+ * only after the previous thread have signaled that it has passed
+ * ODP local initialization.
+ *
+ * Default value is 0.
+ */
+ int sync;
+
+ /**
+ * Synchronized thread creation timeout in nanoseconds
+ *
+ * When synchronized thread creation has been requested, waiting for the
+ * synchronization signal times out once the time indicated by this
+ * parameter has passed.
+ *
+ * If this parameter is 0, the default value is used.
+ *
+ * Default value is ODP_TIME_SEC_IN_NS.
+ */
+ uint64_t sync_timeout;
+
+ /**
+ * Thread parameter sharing
+ *
+ * 0: Thread parameters are not shared. The thread parameter table
+ * contains 'num' elements.
+ * 1: The thread parameter table contains a single element, which is
+ * used for creating all 'num' threads.
+ *
+ * Default value is 0.
+ */
+ int share_param;
+
+} odph_thread_common_param_t;
+
+/**
+ * Initialize thread params
+ *
+ * Initialize an odph_thread_param_t to its default values for all fields.
+ *
+ * @param[out] param Pointer to parameter structure
+ */
+void odph_thread_param_init(odph_thread_param_t *param);
/**
- * Creates and launches odpthreads (as linux threads or processes)
+ * Initialize thread common params
+ *
+ * Initialize an odph_thread_common_param_t to its default values for all
+ * fields.
+ *
+ * @param[out] param Pointer to parameter structure
+ */
+void odph_thread_common_param_init(odph_thread_common_param_t *param);
+
+/**
+ * Create and pin threads (as Linux pthreads or processes)
+ *
+ * Function may be called multiple times to create threads in steps. Each call
+ * launches 'num' threads and pins those to separate CPUs based on the cpumask.
+ * Use 'thread_model' parameter to select if Linux pthreads or processes are
+ * used. This selection may be overridden with ODP helper options. See e.g.
+ * --odph_proc under odph_options() documentation.
+ *
+ * Thread creation may be synchronized by setting 'sync' parameter. It
+ * serializes thread start up (odp_init_local() calls), which helps to
+ * stabilize application start up sequence.
+ *
+ * By default, the thread parameter table contains 'num' elements, one for
+ * each thread to be created. However, all threads may be created
+ * with a single thread parameter table element by setting 'share_param'
+ * parameter.
*
- * Creates, pins and launches threads to separate CPU's based on the cpumask.
+ * Use odph_thread_common_param_init() and odph_thread_param_init() to
+ * initialize parameters with default values.
*
- * @param thread_tbl Thread table
- * @param mask CPU mask
- * @param thr_params ODP thread parameters
+ * Thread table must be large enough to hold 'num' elements. Also the cpumask
+ * must contain 'num' CPUs. Threads are pinned to CPUs in order - the first
+ * thread goes to the smallest CPU number of the mask, etc.
+ *
+ * Launched threads may be waited for exit with odph_thread_join(), or with
+ * direct Linux system calls.
+ *
+ * @param[out] thread Thread table for output
+ * @param param Common parameters for all threads to be created
+ * @param thr_param Table of thread parameters
+ * @param num Number of threads to create
*
* @return Number of threads created
+ * @retval -1 On failure
+ *
+ * @see odph_thread_join()
*/
-int odph_odpthreads_create(odph_odpthread_t *thread_tbl,
- const odp_cpumask_t *mask,
- const odph_odpthread_params_t *thr_params);
+int odph_thread_create(odph_thread_t thread[],
+ const odph_thread_common_param_t *param,
+ const odph_thread_param_t thr_param[],
+ int num);
/**
- * Waits odpthreads (as linux threads or processes) to exit.
+ * Wait previously launched threads to exit
+ *
+ * Function waits for threads launched with odph_thread_create() to exit.
+ * Threads may be waited to exit in a different order than those were created.
+ * A function call may be used to wait any number of launched threads to exit.
+ * A particular thread may be waited only once.
*
- * Returns when all odpthreads have terminated.
+ * @param thread Table of threads to exit
+ * @param num Number of threads to exit
*
- * @param thread_tbl Thread table
- * @return The number of joined threads or -1 on error.
- * (error occurs if any of the start_routine return non-zero or if
- * the thread join/process wait itself failed -e.g. as the result of a kill)
+ * @return Number of threads exited
+ * @retval -1 On failure
*
+ * @see odph_thread_create()
*/
-int odph_odpthreads_join(odph_odpthread_t *thread_tbl);
+int odph_thread_join(odph_thread_t thread[], int num);
/**
* Set CPU affinity of the current odp thread
@@ -146,74 +294,39 @@ int odph_odpthread_setaffinity(const int cpu);
int odph_odpthread_getaffinity(void);
/**
- * Merge getopt options
- *
- * Given two sets of getopt options (each containing possibly both short
- * options -a string- and long options -a option array-) this function
- * return a single set (i.e. a string for short and an array for long)
- * being the concatenation of the two given sets.
- * Due to the fact that the size of these arrays is unknown at compilation
- * time, this function actually mallocs the the resulting arrays.
- * The fourth and fith parameters are actually pointers where these malloc'ed
- * areas are returned.
- * This means that the caller of this function has to free the two returned
- * areas!
- *
- * @param shortopts1 first set of short options (a string)
- * @param shortopts2 second set of short options (a string)
- * @param longopts1 first set of long options (a getopt option array)
- * @param longopts2 second set of long options (a getopt option array)
- * @param shortopts a pointer where the address of the short options list
- * (a string) is returned. It contains the concatenation of
- * the two given short option strings.
- * @param longopts a pointer where the address of the long options list
- * (a getopt option array) is returned.
- * It contains the concatenation of the two given long
- * option arrays.
- * if any of shortopts1, shortopts2, longopts1, longopts2 is NULL, the
- * corresponding list as assumed to be empty.
- * if any of shortopts, longopts is NULL, the corresponding malloc is not
- * performed.
- *
- * @return On success: 0 : both shortopts and longopts are returned (assuming
- * the given pointer where not null), possibly
- * pointing to an empty string or an empty option array.
- * On success, the caller is due to free these areas.
- * On failure: -1: Nothing is malloc'ed.
+ * Parse linux helper options
+ *
+ * Parse the command line options. Pick up (--odph_ prefixed) options meant for
+ * the helper itself. When helper options are found, those are removed from
+ * argv[] and remaining options are packed to the beginning of the array.
+ *
+ * <table> <caption> Currently supported options </caption>
+ *
+ * <tr><th>Command line <th>Environment variable <th>Description
+ * <tr><td>--odph_proc <td>ODPH_PROC_MODE <td>When defined, threads are
+ * Linux processes. Otherwise,
+ * pthreads are used instead.
+ * </table>
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ *
+ * @return New argument count. Original argument count decremented by
+ * the number of removed helper options.
*/
-int odph_merge_getopt_options(const char *shortopts1,
- const char *shortopts2,
- const struct option *longopts1,
- const struct option *longopts2,
- char **shortopts,
- struct option **longopts);
+int odph_parse_options(int argc, char *argv[]);
/**
- * Parse linux helper options
+ * Get linux helper options
*
- * Parse the command line options. Pick up options meant for the helper itself.
- * If the caller is also having a set of option to parse, it should include
- * their description here (shortopts desribes the short options and longopts
- * describes the long options, as for getopt_long()).
- * This function will issue errors on unknown arguments, so callers failing
- * to pass their own command line options description here will see their
- * options rejected.
- * (the caller wants to set opterr to zero when parsing its own stuff
- * with getopts to avoid reacting on helper's options).
- *
- * @param argc argument count
- * @param argv argument values
- * @param caller_shortopts caller's set of short options (string). or NULL.
- * @param caller_longopts caller's set of long options (getopt option array).
- * or NULL.
- *
- * @return On success: 0
- * On failure: -1 failure occurs if a value passed for a helper
- * option is invalid, or on meeting unknown options.
+ * Return used ODP helper options. odph_parse_options() must be called before
+ * using this function.
+ *
+ * @param[out] options ODP helper options
+ *
+ * @return 0 on success, -1 on failure
*/
-int odph_parse_options(int argc, char *argv[],
- const char *caller_shortopts,
- const struct option *caller_longopts);
+int odph_options(odph_helper_options_t *options);
/**
* @}
diff --git a/helper/include/odp/helper/udp.h b/helper/include/odp/helper/udp.h
index 1ba2dff3b..53ac7281a 100644
--- a/helper/include/odp/helper/udp.h
+++ b/helper/include/odp/helper/udp.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -20,8 +18,9 @@ extern "C" {
#include <odp_api.h>
#include <odp/helper/chksum.h>
-/** @addtogroup odph_header ODPH HEADER
- * @{
+/**
+ * @addtogroup odph_protocols
+ * @{
*/
/** UDP header length */
@@ -52,9 +51,10 @@ static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt)
return (rc == 0) ? chksum : 0;
}
-/** @internal Compile time assert */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
ODP_STATIC_ASSERT(sizeof(odph_udphdr_t) == ODPH_UDPHDR_LEN,
"ODPH_UDPHDR_T__SIZE_ERROR");
+/** @endcond */
/**
* @}
diff --git a/helper/include/odp/helper/version.h.in b/helper/include/odp/helper/version.h.in
new file mode 100644
index 000000000..e576de561
--- /dev/null
+++ b/helper/include/odp/helper/version.h.in
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+
+/**
+ * @file
+ *
+ * ODP helper version
+ */
+
+#ifndef ODPH_VERSION_H_
+#define ODPH_VERSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup odph_version ODPH VERSION
+ * Helper library version
+ *
+ * @{
+ */
+
+/**
+ * ODP helper generation version
+ *
+ * Introduction of major new features or changes that make very significant
+ * changes to the helper library.
+ */
+#define ODPH_VERSION_GENERATION @ODPH_VERSION_GENERATION@
+
+/**
+ * ODP helper major version
+ *
+ * Introduction of major new features or changes. Helper libraries with common
+ * generation, but with different major version numbers are likely not backward
+ * compatible.
+ */
+#define ODPH_VERSION_MAJOR @ODPH_VERSION_MAJOR@
+
+/**
+ * ODP helper minor version
+ *
+ * Minor version is incremented when introducing backward compatible changes.
+ * Helper libraries with common generation and major version, but with
+ * different minor version numbers are backward compatible.
+ */
+#define ODPH_VERSION_MINOR @ODPH_VERSION_MINOR@
+
+/**
+ * ODP helper version string
+ *
+ * The version string defines the helper library version in this format:
+ * @verbatim <generation>.<major>.<minor> @endverbatim
+ *
+ * The string is null terminated.
+ *
+ * @return Pointer to helper library version string
+ */
+const char *odph_version_str(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/include/odph_list_internal.h b/helper/include/odph_list_internal.h
new file mode 100644
index 000000000..d90b07ebc
--- /dev/null
+++ b/helper/include/odph_list_internal.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP list
+ * a simple implementation of Doubly linked list
+ */
+
+#ifndef ODPH_LIST_INTER_H_
+#define ODPH_LIST_INTER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+typedef struct odph_list_object {
+ struct odph_list_object *next;
+
+ struct odph_list_object *prev;
+} odph_list_object;
+
+typedef odph_list_object odph_list_head;
+
+static inline void ODPH_INIT_LIST_HEAD(odph_list_object *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void __odph_list_add(odph_list_object *new,
+ odph_list_object *prev,
+ odph_list_object *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void odph_list_add(odph_list_object *new, odph_list_object *head)
+{
+ __odph_list_add(new, head, head->next);
+}
+
+static inline void odph_list_add_tail(struct odph_list_object *new,
+ odph_list_object *head)
+{
+ __odph_list_add(new, head->prev, head);
+}
+
+static inline void __odph_list_del(struct odph_list_object *prev,
+ odph_list_object *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void odph_list_del(struct odph_list_object *entry)
+{
+ __odph_list_del(entry->prev, entry->next);
+ ODPH_INIT_LIST_HEAD(entry);
+}
+
+static inline int odph_list_empty(const struct odph_list_object *head)
+{
+ return head->next == head;
+}
+
+#define container_of(ptr, type, list_node) \
+ ((type *)(void *)((char *)ptr - offsetof(type, list_node)))
+
+#define ODPH_LIST_FOR_EACH(pos, list_head, type, list_node) \
+ for (pos = container_of((list_head)->next, type, list_node); \
+ &pos->list_node != (list_head); \
+ pos = container_of(pos->list_node.next, type, list_node))
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/helper/ip.c b/helper/ip.c
index eb73e5a08..964544ddb 100644
--- a/helper/ip.c
+++ b/helper/ip.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#include <odp/helper/ip.h>
diff --git a/helper/iplookuptable.c b/helper/iplookuptable.c
index ac7d05872..31273a0a5 100644
--- a/helper/iplookuptable.c
+++ b/helper/iplookuptable.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#include <string.h>
@@ -10,8 +8,8 @@
#include <stdio.h>
#include <odp/helper/odph_iplookuptable.h>
+#include <odp/helper/odph_debug.h>
#include "odph_list_internal.h"
-#include "odph_debug.h"
#include <odp_api.h>
/** @magic word, write to the first byte of the memory block
@@ -81,9 +79,9 @@ typedef struct trie_node {
} trie_node_t;
/** Number of L2\L3 entries(subtrees) per cache cube. */
-#define CACHE_NUM_SUBTREE (1 << 13)
+#define CACHE_NUM_SUBTREE (4 * 1024)
/** Number of trie nodes per cache cube. */
-#define CACHE_NUM_TRIE (1 << 20)
+#define CACHE_NUM_TRIE (4 * 1024)
/** @typedef cache_type_t
* Cache node type
@@ -94,7 +92,7 @@ typedef enum {
} cache_type_t;
/** A IP lookup table structure. */
-typedef struct {
+typedef struct ODP_ALIGNED_CACHE {
/**< for check */
uint32_t magicword;
/** Name of the hash. */
@@ -116,7 +114,7 @@ typedef struct {
odp_queue_t free_slots[2];
/** The number of pool used by each queue. */
uint32_t cache_count[2];
-} odph_iplookup_table_impl ODP_ALIGNED_CACHE;
+} odph_iplookup_table_impl;
/***********************************************************
***************** Cache management ********************
@@ -150,12 +148,12 @@ cache_destroy(odph_iplookup_table_impl *impl)
sprintf(
pool_name, "%s_%d_%d",
impl->name, i, count);
- odp_pool_destroy(odp_pool_lookup(pool_name));
+ (void)odp_pool_destroy(odp_pool_lookup(pool_name));
}
}
}
-/** According to the type of cahce, set the value of
+/** According to the type of cache, set the value of
* a buffer to the initial value.
*/
static void
@@ -185,16 +183,39 @@ cache_alloc_new_pool(
{
odp_pool_t pool;
odp_pool_param_t param;
+ odp_pool_capability_t pool_capa;
odp_queue_t queue = tbl->free_slots[type];
odp_buffer_t buffer;
char pool_name[ODPH_TABLE_NAME_LEN + 8];
uint32_t size = 0, num = 0;
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("pool capa failed\n");
+ return -1;
+ }
+
+ if (pool_capa.buf.max_num) {
+ if (pool_capa.buf.max_num < CACHE_NUM_TRIE ||
+ pool_capa.buf.max_num < CACHE_NUM_SUBTREE) {
+ ODPH_ERR("pool size too small\n");
+ return -1;
+ }
+ }
+
+ if (pool_capa.buf.max_size) {
+ if (pool_capa.buf.max_size < ENTRY_SIZE * ENTRY_NUM_SUBTREE ||
+ pool_capa.buf.max_size < sizeof(trie_node_t)) {
+ ODPH_ERR("buffer size too small\n");
+ return -1;
+ }
+ }
+
/* Create new pool (new free buffers). */
odp_pool_param_init(&param);
param.type = ODP_POOL_BUFFER;
- param.buf.align = ODP_CACHE_LINE_SIZE;
+ if (pool_capa.buf.max_align >= ODP_CACHE_LINE_SIZE)
+ param.buf.align = ODP_CACHE_LINE_SIZE;
if (type == CACHE_TYPE_SUBTREE) {
num = CACHE_NUM_SUBTREE;
size = ENTRY_SIZE * ENTRY_NUM_SUBTREE;
@@ -221,7 +242,11 @@ cache_alloc_new_pool(
while ((buffer = odp_buffer_alloc(pool))
!= ODP_BUFFER_INVALID) {
cache_init_buffer(buffer, type, size);
- odp_queue_enq(queue, odp_buffer_to_event(buffer));
+ if (odp_queue_enq(queue, odp_buffer_to_event(buffer))) {
+ ODPH_DBG("queue enqueue failed\n");
+ odp_buffer_free(buffer);
+ break;
+ }
}
tbl->cache_count[type]++;
@@ -447,10 +472,28 @@ odph_table_t odph_iplookup_table_create(const char *name,
odp_shm_t shm_tbl;
odp_queue_t queue;
odp_queue_param_t qparam;
+ odp_queue_capability_t queue_capa;
unsigned i;
- uint32_t impl_size, l1_size;
+ uint32_t impl_size, l1_size, queue_size;
char queue_name[ODPH_TABLE_NAME_LEN + 2];
+ if (odp_queue_capability(&queue_capa)) {
+ ODPH_ERR("queue capa failed\n");
+ return NULL;
+ }
+
+ if (queue_capa.plain.max_size) {
+ if (queue_capa.plain.max_size < CACHE_NUM_TRIE ||
+ queue_capa.plain.max_size < CACHE_NUM_SUBTREE) {
+ ODPH_ERR("queue size too small\n");
+ return NULL;
+ }
+ }
+
+ queue_size = CACHE_NUM_TRIE;
+ if (CACHE_NUM_SUBTREE > CACHE_NUM_TRIE)
+ queue_size = CACHE_NUM_SUBTREE;
+
/* Check for valid parameters */
if (strlen(name) == 0) {
ODPH_DBG("invalid parameters\n");
@@ -468,9 +511,7 @@ odph_table_t odph_iplookup_table_create(const char *name,
impl_size = sizeof(odph_iplookup_table_impl);
l1_size = ENTRY_SIZE * ENTRY_NUM_L1;
- shm_tbl = odp_shm_reserve(
- name, impl_size + l1_size,
- ODP_CACHE_LINE_SIZE, ODP_SHM_SW_ONLY);
+ shm_tbl = odp_shm_reserve(name, impl_size + l1_size, ODP_CACHE_LINE_SIZE, 0);
if (shm_tbl == ODP_SHM_INVALID) {
ODPH_DBG(
@@ -500,6 +541,7 @@ odph_table_t odph_iplookup_table_create(const char *name,
odp_queue_param_init(&qparam);
qparam.type = ODP_QUEUE_TYPE_PLAIN;
+ qparam.size = queue_size;
sprintf(queue_name, "%s_%d", name, i);
queue = odp_queue_create(queue_name, &qparam);
if (queue == ODP_QUEUE_INVALID) {
@@ -583,24 +625,25 @@ prefix_insert_into_lx(
odph_iplookup_table_impl *tbl, prefix_entry_t *entry,
uint8_t cidr, odp_buffer_t nexthop, uint8_t level)
{
- uint8_t ret = 0;
+ int ret = 0;
uint32_t i = 0, limit = (1 << (level - cidr));
prefix_entry_t *e = entry, *ne = NULL;
for (i = 0; i < limit; i++, e++) {
- if (e->child == 1) {
- if (e->cidr > cidr)
- continue;
+ if (e->cidr > cidr)
+ continue;
+ if (e->child == 1) {
e->cidr = cidr;
/* push to next level */
ne = (prefix_entry_t *)e->ptr;
ret = prefix_insert_into_lx(
tbl, ne, cidr, nexthop, cidr + 8);
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return ret;
} else {
- if (e->cidr > cidr)
- continue;
-
e->child = 0;
e->cidr = cidr;
e->nexthop = nexthop;
@@ -676,8 +719,9 @@ odph_iplookup_table_put_value(odph_table_t tbl, void *key, void *value)
nexthop = *((odp_buffer_t *)value);
- if (prefix->cidr == 0)
+ if (prefix->cidr == 0 || prefix->cidr > 32)
return -1;
+
prefix->ip = prefix->ip & (0xffffffff << (IP_LENGTH - prefix->cidr));
/* insert into trie */
@@ -897,7 +941,7 @@ odph_iplookup_table_remove_value(odph_table_t tbl, void *key)
ip = prefix->ip;
cidr = prefix->cidr;
- if (cidr == 0)
+ if (cidr == 0 || cidr > 32)
return -EINVAL;
prefix_entry_t *entry = &impl->l1e[ip >> 16];
diff --git a/helper/ipsec.c b/helper/ipsec.c
new file mode 100644
index 000000000..bb7d5d0eb
--- /dev/null
+++ b/helper/ipsec.c
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2020 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/helper/ipsec.h>
+#include <odp/helper/odph_debug.h>
+
+uint32_t odph_ipsec_auth_icv_len_default(odp_auth_alg_t auth_alg)
+{
+ uint32_t icv_len;
+
+ switch (auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ icv_len = 0;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ icv_len = 12;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ icv_len = 12;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ icv_len = 16;
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ icv_len = 24;
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ icv_len = 32;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ icv_len = 16;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ icv_len = 16;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ icv_len = 16;
+ break;
+ case ODP_AUTH_ALG_AES_CMAC:
+ icv_len = 12;
+ break;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ icv_len = 12;
+ break;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ icv_len = 16;
+ break;
+ default:
+ ODPH_DBG("Unsupported authentication algorithm\n");
+ icv_len = 0;
+ break;
+ }
+ return icv_len;
+}
+
+int odph_ipsec_alg_check(const odp_ipsec_capability_t *capa,
+ odp_cipher_alg_t cipher_alg,
+ uint32_t cipher_key_len,
+ odp_auth_alg_t auth_alg,
+ uint32_t auth_key_len)
+{
+ int i, num, max_capa;
+ uint32_t default_icv_len;
+ odp_bool_t found;
+
+ /* Check whether requested cipher algorithm is supported */
+ switch (cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ if (!capa->ciphers.bit.null)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_DES:
+ if (!capa->ciphers.bit.des)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ if (!capa->ciphers.bit.trides_cbc)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ if (!capa->ciphers.bit.aes_cbc)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ if (!capa->ciphers.bit.aes_ctr)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ if (!capa->ciphers.bit.aes_gcm)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ if (!capa->ciphers.bit.aes_ccm)
+ return -1;
+ break;
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ if (!capa->ciphers.bit.chacha20_poly1305)
+ return -1;
+ break;
+ default:
+ ODPH_DBG("Unsupported cipher algorithm\n");
+ return -1;
+ }
+
+ /* Check whether requested auth algorithm is supported */
+ switch (auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ if (!capa->auths.bit.null)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ if (!capa->auths.bit.md5_hmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ if (!capa->auths.bit.sha1_hmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ if (!capa->auths.bit.sha256_hmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ if (!capa->auths.bit.sha384_hmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ if (!capa->auths.bit.sha512_hmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ if (!capa->auths.bit.aes_xcbc_mac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ if (!capa->auths.bit.aes_gcm)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ if (!capa->auths.bit.aes_gmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ if (!capa->auths.bit.aes_ccm)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_AES_CMAC:
+ if (!capa->auths.bit.aes_cmac)
+ return -1;
+ break;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ if (!capa->auths.bit.chacha20_poly1305)
+ return -1;
+ break;
+ default:
+ ODPH_DBG("Unsupported authentication algorithm\n");
+ return -1;
+ }
+
+ /* Check whether requested cipher key length is supported */
+ max_capa = odp_ipsec_cipher_capability(cipher_alg, NULL, 0);
+ if (max_capa <= 0)
+ return -1;
+
+ odp_ipsec_cipher_capability_t cipher_capa[max_capa];
+
+ num = odp_ipsec_cipher_capability(cipher_alg, cipher_capa, max_capa);
+ if (num <= 0) {
+ ODPH_DBG("Could not get cipher capabilities\n");
+ return -1;
+ }
+
+ found = false;
+ for (i = 0; i < num; i++) {
+ if (cipher_capa[i].key_len == cipher_key_len) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ODPH_DBG("Unsupported key length\n");
+ return -1;
+ }
+
+ /* Check whether requested auth key length is supported */
+ max_capa = odp_ipsec_auth_capability(auth_alg, NULL, 0);
+ if (max_capa <= 0)
+ return max_capa;
+
+ odp_ipsec_auth_capability_t auth_capa[max_capa];
+
+ num = odp_ipsec_auth_capability(auth_alg, auth_capa, max_capa);
+ if (num <= 0) {
+ ODPH_DBG("Could not get auth capabilities\n");
+ return -1;
+ }
+
+ default_icv_len = odph_ipsec_auth_icv_len_default(auth_alg);
+ found = false;
+ for (i = 0; i < num; i++) {
+ if (auth_capa[i].key_len == auth_key_len &&
+ auth_capa[i].icv_len == default_icv_len) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ODPH_DBG("Unsupported auth key length & ICV length pair\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/helper/libodphelper.pc.in b/helper/libodphelper.pc.in
new file mode 100644
index 000000000..be99eeefc
--- /dev/null
+++ b/helper/libodphelper.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libodphelper
+Description: Helper for the ODP packet processing engine
+Version: @PKGCONFIG_VERSION@
+Libs: -L${libdir} -lodphelper
+Libs.private: @LIBCLI_LIBS@
+Cflags: -I${includedir}
diff --git a/helper/lineartable.c b/helper/lineartable.c
index dd4a59958..290a90c02 100644
--- a/helper/lineartable.c
+++ b/helper/lineartable.c
@@ -1,15 +1,13 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
-#include "odp/helper/odph_lineartable.h"
-#include "odph_debug.h"
+#include <odp/helper/odph_lineartable.h>
+#include <odp/helper/odph_debug.h>
#include <odp_api.h>
#define ODPH_SUCCESS 0
@@ -28,7 +26,7 @@
typedef struct {
uint32_t magicword; /**< for check */
uint32_t init_cap; /**< input param of capacity */
- /** given the capacity, caculate out the max supported nodes number */
+ /** given the capacity, calculate out the max supported nodes number */
uint32_t node_sum;
/** size of a lineartable element,including the rwlock in the head */
uint32_t value_size;
@@ -41,7 +39,7 @@ typedef struct {
*/
odph_table_t odph_linear_table_create(const char *name, uint32_t capacity,
- uint32_t un ODPH_UNUSED,
+ uint32_t un ODP_UNUSED,
uint32_t value_size)
{
uint32_t idx;
@@ -54,14 +52,14 @@ odph_table_t odph_linear_table_create(const char *name, uint32_t capacity,
printf("create para input error or less than !");
return NULL;
}
- /* check name confict in shm*/
+ /* check name conflict in shm*/
if (odp_shm_lookup(name) != ODP_SHM_INVALID) {
ODPH_DBG("name already exist\n");
return NULL;
}
/* alloc memory from shm */
- shmem = odp_shm_reserve(name, capacity << 20, 64, ODP_SHM_SW_ONLY);
+ shmem = odp_shm_reserve(name, capacity << 20, 64, 0);
if (shmem == ODP_SHM_INVALID) {
ODPH_DBG("shm reserve fail\n");
return NULL;
@@ -75,7 +73,7 @@ odph_table_t odph_linear_table_create(const char *name, uint32_t capacity,
strncpy(tbl->name, name, ODPH_TABLE_NAME_LEN - 1);
- /* for linear table, the key is just the index, without confict
+ /* for linear table, the key is just the index, without conflict
* so we just need to record the value content
* there is a rwlock in the head of every node
*/
@@ -179,7 +177,7 @@ static int odph_lineartable_put_value(odph_table_t table,
/* should make sure the input table exists and is available */
static int odph_lineartable_get_value(odph_table_t table,
void *key, void *buffer,
- uint32_t buffer_size ODPH_UNUSED)
+ uint32_t buffer_size ODP_UNUSED)
{
odph_linear_table_imp *tbl;
uint32_t ikey = 0;
diff --git a/helper/linux/thread.c b/helper/linux/thread.c
index 52d4efc5e..d5b016833 100644
--- a/helper/linux/thread.c
+++ b/helper/linux/thread.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#ifndef _GNU_SOURCE
@@ -22,7 +20,7 @@
#include <odp_api.h>
#include <odp/helper/linux/pthread.h>
#include <odp/helper/linux/process.h>
-#include "odph_debug.h"
+#include <odp/helper/odph_debug.h>
static void *_odph_run_start_routine(void *arg)
{
@@ -89,7 +87,8 @@ int odph_linux_pthread_create(odph_linux_pthread_t *pthread_tbl,
_odph_run_start_routine,
&pthread_tbl[i].thr_params);
if (ret != 0) {
- ODPH_ERR("Failed to start thread on cpu #%d\n", cpu);
+ ODPH_ERR("Failed to start thread on CPU #%d: %d\n", cpu,
+ ret);
break;
}
diff --git a/helper/m4/configure.m4 b/helper/m4/configure.m4
index 343f5e3cf..04768f25e 100644
--- a/helper/m4/configure.m4
+++ b/helper/m4/configure.m4
@@ -1,23 +1,67 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016 Linaro Limited
+#
+
+##########################################################################
+# Include m4 files
+##########################################################################
+m4_include([helper/m4/libcli.m4])
+
##########################################################################
# Enable/disable test-helper
##########################################################################
-test_helper=no
AC_ARG_ENABLE([test-helper],
- [ --enable-test-helper run test in helper/test],
- [if test "x$enableval" = "xyes"; then
- test_helper=yes
- fi])
+ [AS_HELP_STRING([--enable-test-helper],
+ [run test in helper/test [default=enabled]])],
+ [test_helper=$enableval],
+ [test_helper=yes])
+AM_CONDITIONAL([test_helper], [test x$test_helper = xyes ])
##########################################################################
-# Enable/disable helper-ext
-# platform specific non portable extensions
+# Enable/disable Linux helpers
##########################################################################
-helper_linux=no
AC_ARG_ENABLE([helper-linux],
- [ --enable-helper-linux build helper platform extensions (not portable)],
- [if test "x$enableval" = "xyes"; then
- helper_linux=yes
- fi])
+ [AS_HELP_STRING([--disable-helper-linux],
+ [disable Linux helpers [default=enabled]])],
+ [helper_linux=$enableval],
+ [helper_linux=yes])
+
+##########################################################################
+# Enable/disable ODPH_DEBUG
+##########################################################################
+AC_ARG_ENABLE([helper-debug],
+ [AS_HELP_STRING([--enable-helper-debug],
+ [helpers include additional debugging code [default=disabled]])],
+ [], [AS_IF([test "x$enable_debug" = "xfull"], [enable_helper_debug=yes],
+ [enable_helper_debug=no])])
+AS_IF([test "x$enable_helper_debug" != "xno"], [ODPH_DEBUG=1], [ODPH_DEBUG=0])
+AC_DEFINE_UNQUOTED([ODPH_DEBUG], [$ODPH_DEBUG],
+ [Define to 1 to include additional helper debug code])
+
+##########################################################################
+# Enable/disable ODPH_DEBUG_PRINT
+##########################################################################
+AC_ARG_ENABLE([helper-debug-print],
+ [AS_HELP_STRING([--enable-helper-debug-print],
+ [display helper debugging information [default=disabled]])],
+ [], [AS_IF([test "x$enable_debug" = "xfull"], [enable_helper_debug_print=yes],
+ [enable_helper_debug_print=no])])
+AS_IF([test "x$enable_helper_debug_print" != "xno"], [ODPH_DEBUG_PRINT=1],
+ [ODPH_DEBUG_PRINT=0])
+AC_DEFINE_UNQUOTED([ODPH_DEBUG_PRINT], [$ODPH_DEBUG_PRINT],
+ [Define to 1 to display helper debug information])
+
+##########################################################################
+# Enable/disable deprecated helper API definitions
+##########################################################################
+AC_ARG_ENABLE([helper-deprecated],
+ [AS_HELP_STRING([--enable-helper-deprecated],
+ [enable deprecated helper API definitions [default=disabled]])],
+ [], [enable_helper_deprecated=no])
+AS_IF([test "x$enable_helper_deprecated" != "xno"], [ODPH_DEPRECATED_API=1],
+ [ODPH_DEPRECATED_API=0])
+AC_DEFINE_UNQUOTED([ODPH_DEPRECATED_API], [$ODPH_DEPRECATED_API],
+ [Define to 1 to enable deprecated helper API definitions])
-AC_CONFIG_FILES([helper/Makefile
- helper/test/Makefile])
+AC_CONFIG_FILES([helper/libodphelper.pc
+ helper/test/Makefile])
diff --git a/helper/m4/libcli.m4 b/helper/m4/libcli.m4
new file mode 100644
index 000000000..7edaf3a5d
--- /dev/null
+++ b/helper/m4/libcli.m4
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 Nokia
+#
+
+##########################################################################
+# Set optional libcli path
+##########################################################################
+AC_ARG_WITH([libcli-path],
+ [AS_HELP_STRING([--with-libcli-path=DIR],
+ [path to libcli libs and headers [default=system]])],
+ [libcli_path_given=yes
+ LIBCLI_CPPFLAGS="-I$withval/include"
+ LIBCLI_LIBS="-L$withval/lib"
+ LIBCLI_RPATH="-R$withval/lib"],
+ [])
+
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_CPPFLAGS=$CPPFLAGS
+OLD_LIBS=$LIBS
+CPPFLAGS="$LIBCLI_CPPFLAGS $CPPFLAGS"
+LIBS="$LIBCLI_LIBS $LIBS"
+
+#########################################################################
+# If libcli is available, enable CLI helper
+#########################################################################
+helper_cli=no
+AC_CHECK_HEADER(libcli.h,
+ [AC_CHECK_LIB(cli, cli_init, [helper_cli=yes], [], [-lcrypt])],
+ [AS_IF([test "x$libcli_path_given" = "xyes"],
+ [AC_MSG_ERROR([libcli not found at the specified path (--with-libcli-path)])])])
+
+AS_IF([test "x$helper_cli" != "xno"],
+ [AC_DEFINE_UNQUOTED([ODPH_CLI], [1], [Define to 1 to enable CLI helper])
+ LIBCLI_LIBS="$LIBCLI_RPATH $LIBCLI_LIBS -lcli -lcrypt"],
+ [LIBCLI_CPPFLAGS=""
+ LIBCLI_LIBS=""])
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+LIBS=$OLD_LIBS
+CPPFLAGS=$OLD_CPPFLAGS
+
+AC_SUBST([LIBCLI_CPPFLAGS])
+AC_SUBST([LIBCLI_LIBS])
diff --git a/helper/odph_debug.h b/helper/odph_debug.h
deleted file mode 100644
index 36c743c2e..000000000
--- a/helper/odph_debug.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-/**
- * @file
- *
- * HELPER debug
- */
-
-#ifndef HELPER_DEBUG_H_
-#define HELPER_DEBUG_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * log level.
- */
-typedef enum HELPER_log_level {
- ODPH_LOG_DBG,
- ODPH_LOG_ERR,
- ODPH_LOG_ABORT
-} HELPER_log_level_e;
-
-/**
- * default LOG macro.
- */
-#define ODPH_LOG(level, fmt, ...) \
-do { \
- switch (level) { \
- case ODPH_LOG_ERR: \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case ODPH_LOG_DBG: \
- if (ODPH_DEBUG_PRINT == 1) \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case ODPH_LOG_ABORT: \
- fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- abort(); \
- break; \
- default: \
- fprintf(stderr, "Unknown LOG level"); \
- break;\
- } \
-} while (0)
-
-/**
- * Debug printing macro, which prints output when DEBUG flag is set.
- */
-#define ODPH_DBG(fmt, ...) \
- ODPH_LOG(ODPH_LOG_DBG, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function).
- */
-#define ODPH_ERR(fmt, ...) \
- ODPH_LOG(ODPH_LOG_ERR, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function),
- * then abort.
- */
-#define ODPH_ABORT(fmt, ...) \
- ODPH_LOG(ODPH_LOG_ABORT, fmt, ##__VA_ARGS__)
-
-/**
- * @}
- */
-
-/**
- * Mark intentionally unused argument for functions
- */
-#define ODPH_UNUSED __attribute__((__unused__))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/helper/odph_list_internal.h b/helper/odph_list_internal.h
deleted file mode 100644
index 9e532b088..000000000
--- a/helper/odph_list_internal.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP list
- * a simple implementation of Doubly linked list
- */
-
-#ifndef ODPH_LIST_INTER_H_
-#define ODPH_LIST_INTER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct odph_list_object {
- struct odph_list_object *next, *prev;
-} odph_list_object;
-
-typedef odph_list_object odph_list_head;
-
-static inline void ODPH_INIT_LIST_HEAD(odph_list_object *list)
-{
- list->next = list;
- list->prev = list;
-}
-
-static inline void __odph_list_add(odph_list_object *new,
- odph_list_object *prev,
- odph_list_object *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-static inline void odph_list_add(odph_list_object *new, odph_list_object *head)
-{
- __odph_list_add(new, head, head->next);
-}
-
-static inline void odph_list_add_tail(struct odph_list_object *new,
- odph_list_object *head)
-{
- __odph_list_add(new, head->prev, head);
-}
-
-static inline void __odph_list_del(struct odph_list_object *prev,
- odph_list_object *next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-static inline void odph_list_del(struct odph_list_object *entry)
-{
- __odph_list_del(entry->prev, entry->next);
- ODPH_INIT_LIST_HEAD(entry);
-}
-
-static inline int odph_list_empty(const struct odph_list_object *head)
-{
- return head->next == head;
-}
-
-#define container_of(ptr, type, list_node) \
- ((type *)(void *)((char *)ptr - offsetof(type, list_node)))
-
-#define ODPH_LIST_FOR_EACH(pos, list_head, type, list_node) \
- for (pos = container_of((list_head)->next, type, list_node); \
- &pos->list_node != (list_head); \
- pos = container_of(pos->list_node.next, type, list_node))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
diff --git a/helper/test/.gitignore b/helper/test/.gitignore
index 1a81e0047..3db451f68 100644
--- a/helper/test/.gitignore
+++ b/helper/test/.gitignore
@@ -1,11 +1,15 @@
*.trs
*.log
chksum
+cli
cuckootable
iplookuptable
+macros
odpthreads
parse
process
table
thread
pthread
+version
+debug
diff --git a/helper/test/Makefile.am b/helper/test/Makefile.am
index beef91913..9cf48d7d9 100644
--- a/helper/test/Makefile.am
+++ b/helper/test/Makefile.am
@@ -1,39 +1,28 @@
-include $(top_srcdir)/platform/@with_platform@/Makefile.inc
+include $(top_srcdir)/test/Makefile.inc
-LIB = $(top_builddir)/lib
-
-#in the following line, the libs using the symbols should come before
-#the libs containing them! The includer is given a chance to add things
-#before libodp by setting PRE_LDADD before the inclusion.
-LDADD = $(PRE_LDADD) $(LIB)/libodphelper.la $(LIB)/libodp-linux.la
-
-INCFLAGS = \
- -I$(top_builddir)/platform/@with_platform@/include \
- -I$(top_srcdir)/helper/include \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ \
- -I$(top_srcdir)/platform/@with_platform@/include \
- -I$(top_builddir)/include \
- -I$(top_srcdir)/helper
-
-ODP_PLATFORM=${with_platform}
-
-AM_CFLAGS += $(INCFLAGS)
-AM_LDFLAGS += -static
-
-EXECUTABLES = chksum$(EXEEXT) \
- cuckootable$(EXEEXT) \
- parse$(EXEEXT)\
- table$(EXEEXT) \
- iplookuptable$(EXEEXT)
+EXECUTABLES = version \
+ debug \
+ chksum \
+ cuckootable \
+ macros \
+ parse\
+ table \
+ iplookuptable
#These are platform specific extensions that are not portable
#They are a convenience to app writers who have chosen to
#restrict their application to Linux.
if helper_linux
-EXECUTABLES += linux/pthread$(EXEEXT) \
- linux/process$(EXEEXT)
+EXECUTABLES += linux/pthread \
+ linux/process
+linux_pthread_SOURCES = linux/pthread.c
+linux_process_SOURCES = linux/process.c
+endif
+
+if helper_cli
+EXECUTABLES += cli
+cli_SOURCES = cli.c
endif
COMPILE_ONLY = odpthreads
@@ -45,15 +34,36 @@ if test_helper
TESTS = $(EXECUTABLES) $(TESTSCRIPTS)
endif
-dist_bin_SCRIPTS =
-
test_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
-EXTRA_DIST = odpthreads_as_processes odpthreads_as_pthreads
+dist_check_SCRIPTS = odpthreads_as_processes odpthreads_as_pthreads
+
+chksum_SOURCES = chksum.c
+cuckootable_SOURCES = cuckootable.c
+macros_SOURCES = macros.c
+odpthreads_SOURCES = odpthreads.c
+parse_SOURCES = parse.c
+table_SOURCES = table.c
+iplookuptable_SOURCES = iplookuptable.c
+version_SOURCES = version.c
+debug_SOURCES = debug.c
-dist_chksum_SOURCES = chksum.c
-dist_cuckootable_SOURCES = cuckootable.c
-dist_odpthreads_SOURCES = odpthreads.c
-dist_parse_SOURCES = parse.c
-dist_table_SOURCES = table.c
-dist_iplookuptable_SOURCES = iplookuptable.c
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/helper/test/chksum.c b/helper/test/chksum.c
index 1beae47f1..d7273284a 100644
--- a/helper/test/chksum.c
+++ b/helper/test/chksum.c
@@ -1,10 +1,7 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-#include "odph_debug.h"
#include <odp_api.h>
#include <odp/helper/odph_api.h>
@@ -19,7 +16,7 @@ struct udata_struct {
};
/* Create additional dataplane threads */
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
int status = 0;
@@ -108,9 +105,20 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
ODPH_IPV4HDR_LEN);
ip->proto = ODPH_IPPROTO_UDP;
ip->id = odp_cpu_to_be_16(1);
- ip->chksum = 0;
+ ip->tos = 0;
+ ip->frag_offset = 0;
+ ip->ttl = 0;
odp_packet_has_ipv4_set(test_packet, 1);
- odph_ipv4_csum_update(test_packet);
+ if (odph_ipv4_csum_update(test_packet) < 0)
+ status = -1;
+
+ if (!odph_ipv4_csum_valid(test_packet))
+ status = -1;
+
+ printf("IP chksum = 0x%x\n", odp_be_to_cpu_16(ip->chksum));
+
+ if (odp_be_to_cpu_16(ip->chksum) != 0x3965)
+ status = -1;
/* udp */
odp_packet_l4_offset_set(test_packet, ODPH_ETHHDR_LEN
diff --git a/helper/test/cli.c b/helper/test/cli.c
new file mode 100644
index 000000000..08e750153
--- /dev/null
+++ b/helper/test/cli.c
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+static int cli_server(void *arg ODP_UNUSED)
+{
+ if (odph_cli_run()) {
+ ODPH_ERR("odph_cli_run() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ odph_helper_options_t helper_options;
+ odp_init_t init_param;
+
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ memset(&instance, 0, sizeof(instance));
+
+ if (odp_init_global(&instance, NULL, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odph_cli_param_t cli_param;
+
+ odph_cli_param_init(&cli_param);
+
+ if (odph_cli_init(&cli_param)) {
+ ODPH_ERR("Error: odph_cli_init() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odph_thread_t thr_server;
+
+ if (odp_cpumask_default_control(&cpumask, 1) != 1) {
+ ODPH_ERR("Failed to get default CPU mask.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.thr_type = ODP_THREAD_CONTROL;
+ thr_param.start = cli_server;
+
+ memset(&thr_server, 0, sizeof(thr_server));
+
+ if (odph_thread_create(&thr_server, &thr_common, &thr_param, 1) != 1) {
+ ODPH_ERR("Failed to create server thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Wait for a bit to ensure that the server thread has time to start.
+ */
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 10);
+
+ if (odph_cli_stop()) {
+ ODPH_ERR("Error: odph_cli_stop() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odph_thread_join(&thr_server, 1) != 1) {
+ ODPH_ERR("Failed to join server thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odph_cli_term()) {
+ ODPH_ERR("Error: odph_cli_term() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: ODP local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: ODP global term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/helper/test/cuckootable.c b/helper/test/cuckootable.c
index be655911f..d17f79562 100644
--- a/helper/test/cuckootable.c
+++ b/helper/test/cuckootable.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/*-
@@ -48,7 +46,6 @@
#include <time.h>
#include <odp_api.h>
-#include <odph_debug.h>
#include <odp/helper/odph_api.h>
/*******************************************************************************
@@ -393,7 +390,7 @@ static int test_five_keys(void)
#define BUCKET_ENTRIES 4
#define HASH_ENTRIES_MAX 1048576
/*
- * Do tests for cuchoo tabke creation with bad parameters.
+ * Do tests for cuchoo table creation with bad parameters.
*/
static int test_creation_with_bad_parameters(void)
{
@@ -427,7 +424,7 @@ static int test_creation_with_bad_parameters(void)
return 0;
}
-#define PERFORMANCE_CAPACITY 1000000
+#define PERFORMANCE_CAPACITY 4000
/*
* Test the performance of cuckoo hash table.
@@ -541,7 +538,7 @@ test_cuckoo_hash_table(void)
return 0;
}
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
int ret = 0;
diff --git a/helper/test/debug.c b/helper/test/debug.c
new file mode 100644
index 000000000..8c50edc75
--- /dev/null
+++ b/helper/test/debug.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#include <odp/helper/autoheader_external.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
+{
+ printf("\nHelper library version is: %s\n\n", odph_version_str());
+
+ printf("Helper debugging:\n");
+ printf(" ODPH_DEBUG: %i\n", ODPH_DEBUG);
+ printf(" ODPH_DEBUG_PRINT: %i\n\n", ODPH_DEBUG_PRINT);
+
+ /* ASSERT(true) should work always */
+ ODPH_ASSERT(1);
+
+ /* ASSERT(false) should not abort when not debugging */
+ if (ODPH_DEBUG == 0)
+ ODPH_ASSERT(0);
+
+ return 0;
+}
diff --git a/helper/test/iplookuptable.c b/helper/test/iplookuptable.c
index b5d774cbc..669d334dd 100644
--- a/helper/test/iplookuptable.c
+++ b/helper/test/iplookuptable.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#include <stdio.h>
@@ -11,9 +9,7 @@
#include <errno.h>
#include <odp_api.h>
-#include <odph_debug.h>
#include <odp/helper/odph_api.h>
-#include <odp/helper/ip.h>
static void print_prefix_info(
const char *msg, uint32_t ip, uint8_t cidr)
@@ -138,7 +134,7 @@ static int test_ip_lookup_table(void)
return 0;
}
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
int ret = 0;
diff --git a/helper/test/linux/Makefile.am b/helper/test/linux/Makefile.am
deleted file mode 100644
index f95e04d51..000000000
--- a/helper/test/linux/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-
-thread_LDADD = $(LIB)/libodphelper.la $(LIB)/libodp-linux.la
-dist_thread_SOURCES = pthread.c
-dist_process_SOURCES = process.c
-process_LDADD = $(LIB)/libodphelper.la $(LIB)/libodp-linux.la
diff --git a/helper/test/linux/process.c b/helper/test/linux/process.c
index e08ef8686..54614a695 100644
--- a/helper/test/linux/process.c
+++ b/helper/test/linux/process.c
@@ -1,17 +1,17 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-#include <odph_debug.h>
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
#include <odp/helper/linux/pthread.h>
#include <odp/helper/linux/process.h>
+#include <string.h>
+
#define NUMBER_WORKERS 16 /* 0 = max */
-static void *worker_fn(void *arg ODPH_UNUSED)
+static void *worker_fn(void *arg ODP_UNUSED)
{
/* depend on the odp helper to call odp_init_local */
printf("Worker thread on CPU %d\n", odp_cpu_id());
@@ -20,7 +20,7 @@ static void *worker_fn(void *arg ODPH_UNUSED)
}
/* Create additional dataplane processes */
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_cpumask_t cpu_mask;
int num_workers;
@@ -56,9 +56,10 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
cpu = odp_cpumask_first(&cpu_mask);
printf("the first CPU: %i\n", cpu);
- /* reserve cpu 0 for the control plane so remove it from
- * the default mask */
- odp_cpumask_clr(&cpu_mask, 0);
+ /* If possible, remove CPU 0 from the default mask to reserve it for the
+ * control plane. */
+ if (num_workers > 1)
+ odp_cpumask_clr(&cpu_mask, 0);
num_workers = odp_cpumask_count(&cpu_mask);
(void)odp_cpumask_to_str(&cpu_mask, cpumaskstr, sizeof(cpumaskstr));
printf("new cpu mask: %s\n", cpumaskstr);
@@ -79,10 +80,20 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
if (ret == 0) {
/* Child process */
worker_fn(NULL);
+
+ if (odp_term_local() < 0) {
+ ODPH_ERR("Error: ODP local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
} else {
/* Parent process */
odph_linux_process_wait_n(proc, num_workers);
+ if (odp_term_local()) {
+ ODPH_ERR("Error: ODP local term failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
if (odp_term_global(instance)) {
ODPH_ERR("Error: ODP global term failed.\n");
exit(EXIT_FAILURE);
diff --git a/helper/test/linux/pthread.c b/helper/test/linux/pthread.c
index 2bec0d178..f6c624df7 100644
--- a/helper/test/linux/pthread.c
+++ b/helper/test/linux/pthread.c
@@ -1,15 +1,15 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-#include <odph_debug.h>
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
#include <odp/helper/linux/pthread.h>
+#include <string.h>
+
#define NUMBER_WORKERS 16
-static void *worker_fn(void *arg ODPH_UNUSED)
+static void *worker_fn(void *arg ODP_UNUSED)
{
/* depend on the odp helper to call odp_init_local */
@@ -21,7 +21,7 @@ static void *worker_fn(void *arg ODPH_UNUSED)
}
/* Create additional dataplane threads */
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odph_linux_pthread_t thread_tbl[NUMBER_WORKERS];
odp_cpumask_t cpu_mask;
@@ -56,9 +56,10 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
cpu = odp_cpumask_first(&cpu_mask);
printf("the first CPU: %i\n", cpu);
- /* reserve cpu 0 for the control plane so remove it from
- * the default mask */
- odp_cpumask_clr(&cpu_mask, 0);
+ /* If possible, remove CPU 0 from the default mask to reserve it for the
+ * control plane. */
+ if (num_workers > 1)
+ odp_cpumask_clr(&cpu_mask, 0);
num_workers = odp_cpumask_count(&cpu_mask);
(void)odp_cpumask_to_str(&cpu_mask, cpumaskstr, sizeof(cpumaskstr));
printf("new cpu mask: %s\n", cpumaskstr);
diff --git a/helper/test/macros.c b/helper/test/macros.c
new file mode 100644
index 000000000..7ecf7bb2d
--- /dev/null
+++ b/helper/test/macros.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
+{
+ int a, b;
+ int ret = 0;
+ int arr_1[1];
+ int arr_10[10];
+
+ printf("Running helper macro tests\n");
+
+ if (ODPH_MIN(0, 10) != 0)
+ ret++;
+
+ if (ODPH_MAX(0, 10) != 10)
+ ret++;
+
+ if (ODPH_MIN(-1, 10) != -1)
+ ret++;
+
+ if (ODPH_MAX(-1, 10) != 10)
+ ret++;
+
+ a = 0;
+ b = 10;
+ if (ODPH_MIN(a--, b--) != 0)
+ ret++;
+
+ a = 0;
+ b = 10;
+ if (ODPH_MAX(++a, ++b) != 11)
+ ret++;
+
+ if (ODPH_ARRAY_SIZE(arr_1) != 1)
+ ret++;
+
+ if (ODPH_ARRAY_SIZE(arr_10) != 10)
+ ret++;
+
+ if (ODPH_ABS(-1) != 1)
+ ret++;
+
+ if (ODPH_ABS(1) != 1)
+ ret++;
+
+ if (ODPH_ABS(0) != 0)
+ ret++;
+
+ a = -1;
+ if (ODPH_ABS(a++) != 1)
+ ret++;
+
+ a = -1;
+ if (ODPH_ABS(--a) != 2)
+ ret++;
+
+ if (!ret)
+ printf("All tests passed\n");
+ else
+ printf("%d tests failed\n", ret);
+
+ return ret ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/helper/test/odpthreads.c b/helper/test/odpthreads.c
index 219e1b65b..bf623569b 100644
--- a/helper/test/odpthreads.c
+++ b/helper/test/odpthreads.c
@@ -1,7 +1,6 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
*/
/*
@@ -12,8 +11,12 @@
#include <unistd.h>
#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
-#include <odph_debug.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
@@ -23,9 +26,9 @@
static void main_exit(void);
/* ODP application instance */
-static odp_instance_t odp_instance;
+static odp_instance_t instance;
-static int worker_fn(void *arg ODPH_UNUSED)
+static int worker_fn(void *arg ODP_UNUSED)
{
int cpu;
odp_cpumask_t workers;
@@ -62,23 +65,36 @@ static int worker_fn(void *arg ODPH_UNUSED)
/* Create additional dataplane opdthreads */
int main(int argc, char *argv[])
{
- odph_odpthread_params_t thr_params;
- odph_odpthread_t thread_tbl[NUMBER_WORKERS];
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[NUMBER_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
odp_cpumask_t cpu_mask;
+ odp_init_t init_param;
int num_workers;
int cpu, affinity;
int ret;
char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ struct rlimit rlimit;
+ pthread_attr_t attr;
+ size_t stack_size;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, NULL, NULL);
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
- if (odp_init_global(&odp_instance, NULL, NULL)) {
+ if (odp_init_global(&instance, &init_param, NULL)) {
ODPH_ERR("Error: ODP global init failed.\n");
exit(EXIT_FAILURE);
}
- if (odp_init_local(odp_instance, ODP_THREAD_CONTROL)) {
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
ODPH_ERR("Error: ODP local init failed.\n");
exit(EXIT_FAILURE);
}
@@ -126,26 +142,65 @@ int main(int argc, char *argv[])
cpu = odp_cpumask_first(&cpu_mask);
printf("the first CPU: %i\n", cpu);
- /* reserve cpu 0 for the control plane so remove it from
- * the default mask */
- odp_cpumask_clr(&cpu_mask, 0);
+ /* If possible, remove CPU 0 from the default mask to reserve it for the
+ * control plane. */
+ if (num_workers > 1)
+ odp_cpumask_clr(&cpu_mask, 0);
num_workers = odp_cpumask_count(&cpu_mask);
(void)odp_cpumask_to_str(&cpu_mask, cpumaskstr, sizeof(cpumaskstr));
printf("new cpu mask: %s\n", cpumaskstr);
printf("new num worker threads: %i\n\n", num_workers);
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = worker_fn;
- thr_params.arg = NULL;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = odp_instance;
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpu_mask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = worker_fn;
+ thr_param.arg = NULL;
+ thr_param.thr_type = ODP_THREAD_WORKER;
- odph_odpthreads_create(&thread_tbl[0], &cpu_mask, &thr_params);
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
- ret = odph_odpthreads_join(thread_tbl);
+ ret = odph_thread_join(thread_tbl, num_workers);
if (ret < 0)
exit(EXIT_FAILURE);
+ /* Test threads with non-default stack size and sync timeout. */
+
+ pthread_attr_init(&attr);
+
+ if (pthread_attr_getstacksize(&attr, &stack_size)) {
+ ODPH_ERR("pthread_attr_getstacksize() failed\n");
+ return -1;
+ }
+
+ printf("\n");
+ printf("pthread default stack size: %zu\n", stack_size);
+
+ if (getrlimit(RLIMIT_STACK, &rlimit)) {
+ ODPH_ERR("getrlimit() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ printf("stack size soft limit (rlim_cur): %lu\n", rlimit.rlim_cur);
+
+ if (rlimit.rlim_cur < stack_size)
+ stack_size = rlimit.rlim_cur;
+
+ thr_param.stack_size = stack_size - ODP_PAGE_SIZE;
+ printf("use stack size: %" PRIu64 "\n", thr_param.stack_size);
+ thr_common.sync_timeout = 5 * ODP_TIME_SEC_IN_NS;
+ printf("use sync timeout: %" PRIu64 "\n", thr_common.sync_timeout);
+ printf("\n");
+
+ if (odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers) != num_workers)
+ exit(EXIT_FAILURE);
+
+ if (odph_thread_join(thread_tbl, num_workers) != num_workers)
+ exit(EXIT_FAILURE);
+
return 0;
}
@@ -156,7 +211,7 @@ static void main_exit(void)
_exit(EXIT_FAILURE);
}
- if (odp_term_global(odp_instance)) {
+ if (odp_term_global(instance)) {
ODPH_ERR("Error: ODP global term failed.\n");
_exit(EXIT_FAILURE);
}
diff --git a/helper/test/odpthreads_as_processes b/helper/test/odpthreads_as_processes
index 89405f363..f71717b6b 100755
--- a/helper/test/odpthreads_as_processes
+++ b/helper/test/odpthreads_as_processes
@@ -1,9 +1,7 @@
#!/bin/sh
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
PATH=$(dirname $0):$PATH
@@ -11,4 +9,4 @@ PATH=.:$PATH
# The odpthreads test recognise the "--odph_proc" option to create
# odp threads as linux processes:
-odpthreads --odph_proc
+odpthreads${EXEEXT} --odph_proc
diff --git a/helper/test/odpthreads_as_pthreads b/helper/test/odpthreads_as_pthreads
index ef569c3d4..b666e1a4d 100755
--- a/helper/test/odpthreads_as_pthreads
+++ b/helper/test/odpthreads_as_pthreads
@@ -1,9 +1,7 @@
#!/bin/sh
#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
#
PATH=$(dirname $0):$PATH
@@ -11,4 +9,4 @@ PATH=.:$PATH
# The odpthreads test without the "--odph_proc" option defaults to create
# odp threads as linux pthreads:
-odpthreads
+odpthreads${EXEEXT}
diff --git a/helper/test/parse.c b/helper/test/parse.c
index 0429f2cc3..e5a6cf17e 100644
--- a/helper/test/parse.c
+++ b/helper/test/parse.c
@@ -1,11 +1,7 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
-#include <odph_debug.h>
-
#include <odp_api.h>
#include <odp/helper/odph_api.h>
@@ -346,7 +342,7 @@ static int test_ipv4(void)
return 0;
}
-int main(void)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
int ret = 0;
diff --git a/helper/test/table.c b/helper/test/table.c
index ac454da5e..59a572818 100644
--- a/helper/test/table.c
+++ b/helper/test/table.c
@@ -1,30 +1,11 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier:BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-#include <odph_debug.h>
-#include <odp/helper/odph_api.h>
#include <odp_api.h>
+#include <odp/helper/odph_api.h>
-/**
- * Address Resolution Protocol (ARP)
- * Description: Once a route has been identified for an IP packet (so the
- * output interface and the IP address of the next hop station are known),
- * the MAC address of the next hop station is needed in order to send this
- * packet onto the next leg of the journey towards its destination
- * (as identified by its destination IP address). The MAC address of the next
- * hop station becomes the destination MAC address of the outgoing
- * Ethernet frame.
- * Hash table name: ARP table
- * Number of keys: Thousands
- * Key format: The pair of (Output interface, Next Hop IP address),
- * which is typically 5 bytes for IPv4 and 17 bytes for IPv6.
- * value (data): MAC address of the next hop station (6 bytes).
- */
-
-int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
{
odp_instance_t instance;
int ret = 0;
@@ -32,13 +13,13 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
odph_table_t tmp_tbl;
odph_table_ops_t *test_ops;
char tmp[32];
- char ip_addr1[] = "12345678";
- char ip_addr2[] = "11223344";
- char ip_addr3[] = "55667788";
- char mac_addr1[] = "0A1122334401";
- char mac_addr2[] = "0A1122334402";
- char mac_addr3[] = "0B4433221101";
- char mac_addr4[] = "0B4433221102";
+ char key1[] = "1234";
+ char key2[] = "1122";
+ char key3[] = "3344";
+ char value1[] = "0A1122334401";
+ char value2[] = "0A1122334402";
+ char value3[] = "0B4433221101";
+ char value4[] = "0B4433221102";
ret = odp_init_global(&instance, NULL, NULL);
if (ret != 0) {
@@ -54,49 +35,49 @@ int main(int argc ODPH_UNUSED, char *argv[] ODPH_UNUSED)
printf("test hash table:\n");
test_ops = &odph_hash_table_ops;
- table = test_ops->f_create("test", 2, 4, 16);
+ table = test_ops->f_create("test", 2, sizeof(key1), sizeof(value1));
if (table == NULL) {
printf("table create fail\n");
return -1;
}
- ret += test_ops->f_put(table, &ip_addr1, mac_addr1);
+ ret += test_ops->f_put(table, &key1, value1);
- ret += test_ops->f_put(table, &ip_addr2, mac_addr2);
+ ret += test_ops->f_put(table, &key2, value2);
- ret += test_ops->f_put(table, &ip_addr3, mac_addr3);
+ ret += test_ops->f_put(table, &key3, value3);
if (ret != 0) {
printf("put value fail\n");
return -1;
}
- ret = test_ops->f_get(table, &ip_addr1, &tmp, 32);
+ ret = test_ops->f_get(table, &key1, &tmp, 32);
if (ret != 0) {
printf("get value fail\n");
return -1;
}
printf("\t1 get '123' tmp = %s,\n", tmp);
- ret = test_ops->f_put(table, &ip_addr1, mac_addr4);
+ ret = test_ops->f_put(table, &key1, value4);
if (ret != 0) {
printf("repeat put value fail\n");
return -1;
}
- ret = test_ops->f_get(table, &ip_addr1, &tmp, 32);
- if (ret != 0 || strcmp(tmp, mac_addr4) != 0) {
+ ret = test_ops->f_get(table, &key1, &tmp, 32);
+ if (ret != 0 || memcmp(tmp, value4, sizeof(value4)) != 0) {
printf("get value fail\n");
return -1;
}
printf("\t2 repeat get '123' value = %s\n", tmp);
- ret = test_ops->f_remove(table, &ip_addr1);
+ ret = test_ops->f_remove(table, &key1);
if (ret != 0) {
printf("remove value fail\n");
return -1;
}
- ret = test_ops->f_get(table, &ip_addr1, tmp, 32);
+ ret = test_ops->f_get(table, &key1, tmp, 32);
if (ret == 0) {
printf("remove value fail actually\n");
return -1;
diff --git a/helper/test/version.c b/helper/test/version.c
new file mode 100644
index 000000000..6af6df490
--- /dev/null
+++ b/helper/test/version.c
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
+{
+ printf("\nHelper library versions is: %s\n\n", odph_version_str());
+
+ return 0;
+}
diff --git a/helper/threads.c b/helper/threads.c
index cb747e5bf..72003d0f4 100644
--- a/helper/threads.c
+++ b/helper/threads.c
@@ -1,44 +1,57 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sched.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <inttypes.h>
#include <odp_api.h>
#include <odp/helper/threads.h>
-#include "odph_debug.h"
+#include <odp/helper/odph_debug.h>
-static struct {
- int proc; /* true when process mode is required, false otherwise */
-} helper_options;
+#define FAILED_CPU -1
+
+/* Thread status codes */
+#define NOT_STARTED 0
+#define INIT_DONE 1
+#define STARTED 2
+
+static odph_helper_options_t helper_options;
/*
- * wrapper for odpthreads, either implemented as linux threads or processes.
- * (in process mode, if start_routine returns NULL, the process return FAILURE).
+ * Run a thread, either as Linux pthread or process.
+ * In process mode, if start_routine returns NULL, the process return FAILURE.
*/
-static void *_odph_thread_run_start_routine(void *arg)
+static void *run_thread(void *arg)
{
int status;
int ret;
- odph_odpthread_params_t *thr_params;
-
- odph_odpthread_start_args_t *start_args = arg;
+ odp_instance_t instance;
+ odph_thread_param_t *thr_params;
+ odph_thread_start_args_t *start_args = arg;
thr_params = &start_args->thr_params;
+ instance = start_args->instance;
/* ODP thread local init */
- if (odp_init_local(thr_params->instance, thr_params->thr_type)) {
+ if (odp_init_local(instance, thr_params->thr_type)) {
ODPH_ERR("Local init failed\n");
- if (start_args->linuxtype == ODPTHREAD_PROCESS)
+ if (start_args->mem_model == ODP_MEM_MODEL_PROCESS)
_exit(EXIT_FAILURE);
return (void *)-1;
}
@@ -46,10 +59,13 @@ static void *_odph_thread_run_start_routine(void *arg)
ODPH_DBG("helper: ODP %s thread started as linux %s. (pid=%d)\n",
thr_params->thr_type == ODP_THREAD_WORKER ?
"worker" : "control",
- (start_args->linuxtype == ODPTHREAD_PTHREAD) ?
+ (start_args->mem_model == ODP_MEM_MODEL_THREAD) ?
"pthread" : "process",
(int)getpid());
+ if (start_args->init_status)
+ odp_atomic_store_rel_u32(start_args->init_status, INIT_DONE);
+
status = thr_params->start(thr_params->arg);
ret = odp_term_local();
@@ -57,7 +73,7 @@ static void *_odph_thread_run_start_routine(void *arg)
ODPH_ERR("Local term failed\n");
/* for process implementation of odp threads, just return status... */
- if (start_args->linuxtype == ODPTHREAD_PROCESS)
+ if (start_args->mem_model == ODP_MEM_MODEL_PROCESS)
_exit(status);
/* threads implementation return void* pointers: cast status to that. */
@@ -65,11 +81,9 @@ static void *_odph_thread_run_start_routine(void *arg)
}
/*
- * Create a single ODPthread as a linux process
+ * Create a single linux process
*/
-static int _odph_linux_process_create(odph_odpthread_t *thread_tbl,
- int cpu,
- const odph_odpthread_params_t *thr_params)
+static int create_process(odph_thread_t *thread, int cpu, uint64_t stack_size)
{
cpu_set_t cpu_set;
pid_t pid;
@@ -77,20 +91,19 @@ static int _odph_linux_process_create(odph_odpthread_t *thread_tbl,
CPU_ZERO(&cpu_set);
CPU_SET(cpu, &cpu_set);
- thread_tbl->start_args.thr_params = *thr_params; /* copy */
- thread_tbl->start_args.linuxtype = ODPTHREAD_PROCESS;
- thread_tbl->cpu = cpu;
+ thread->start_args.mem_model = ODP_MEM_MODEL_PROCESS;
+ thread->cpu = cpu;
pid = fork();
if (pid < 0) {
ODPH_ERR("fork() failed\n");
- thread_tbl->start_args.linuxtype = ODPTHREAD_NOT_STARTED;
+ thread->cpu = FAILED_CPU;
return -1;
}
/* Parent continues to fork */
if (pid > 0) {
- thread_tbl->proc.pid = pid;
+ thread->proc.pid = pid;
return 0;
}
@@ -107,17 +120,65 @@ static int _odph_linux_process_create(odph_odpthread_t *thread_tbl,
return -2;
}
- _odph_thread_run_start_routine(&thread_tbl->start_args);
+ if (stack_size) {
+ struct rlimit rlimit;
+
+ if (getrlimit(RLIMIT_STACK, &rlimit)) {
+ ODPH_ERR("getrlimit() failed: %s\n", strerror(errno));
+ return -3;
+ }
+
+ rlimit.rlim_cur = stack_size;
+
+ if (setrlimit(RLIMIT_STACK, &rlimit)) {
+ ODPH_ERR("setrlimit() failed: %s\n", strerror(errno));
+ return -4;
+ }
+ }
+
+ run_thread(&thread->start_args);
return 0; /* never reached */
}
/*
- * Create a single ODPthread as a linux thread
+ * Wait single process to exit
*/
-static int odph_linux_thread_create(odph_odpthread_t *thread_tbl,
- int cpu,
- const odph_odpthread_params_t *thr_params)
+static int wait_process(odph_thread_t *thread)
+{
+ pid_t pid;
+ int status = 0;
+
+ pid = waitpid(thread->proc.pid, &status, 0);
+
+ if (pid < 0) {
+ ODPH_ERR("waitpid() failed\n");
+ return -1;
+ }
+
+ /* Examine the child process' termination status */
+ if (WIFEXITED(status) &&
+ WEXITSTATUS(status) != EXIT_SUCCESS) {
+ ODPH_ERR("Child exit status:%d (pid:%d)\n",
+ WEXITSTATUS(status), (int)pid);
+ return -1;
+ }
+
+ if (WIFSIGNALED(status)) {
+ int signo = WTERMSIG(status);
+
+ ODPH_ERR("Child term signo:%d - %s (pid:%d)\n",
+ signo, strsignal(signo), (int)pid);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Create a single linux pthread
+ */
+static int create_pthread(odph_thread_t *thread, int cpu, uint64_t stack_size)
{
int ret;
cpu_set_t cpu_set;
@@ -125,23 +186,40 @@ static int odph_linux_thread_create(odph_odpthread_t *thread_tbl,
CPU_ZERO(&cpu_set);
CPU_SET(cpu, &cpu_set);
- pthread_attr_init(&thread_tbl->thread.attr);
+ pthread_attr_init(&thread->thread.attr);
- thread_tbl->cpu = cpu;
+ thread->cpu = cpu;
- pthread_attr_setaffinity_np(&thread_tbl->thread.attr,
+ pthread_attr_setaffinity_np(&thread->thread.attr,
sizeof(cpu_set_t), &cpu_set);
- thread_tbl->start_args.thr_params = *thr_params; /* copy */
- thread_tbl->start_args.linuxtype = ODPTHREAD_PTHREAD;
+ if (stack_size) {
+ /*
+ * Round up to page size. "On some systems,
+ * pthread_attr_setstacksize() can fail with the error EINVAL if
+ * stacksize is not a multiple of the system page size." (man
+ * page)
+ */
+ stack_size = (stack_size + ODP_PAGE_SIZE - 1) & ~(ODP_PAGE_SIZE - 1);
+
+ if (stack_size < (uint64_t)PTHREAD_STACK_MIN)
+ stack_size = PTHREAD_STACK_MIN;
- ret = pthread_create(&thread_tbl->thread.thread_id,
- &thread_tbl->thread.attr,
- _odph_thread_run_start_routine,
- &thread_tbl->start_args);
+ if (pthread_attr_setstacksize(&thread->thread.attr, stack_size)) {
+ ODPH_ERR("pthread_attr_setstacksize() failed\n");
+ return -1;
+ }
+ }
+
+ thread->start_args.mem_model = ODP_MEM_MODEL_THREAD;
+
+ ret = pthread_create(&thread->thread.thread_id,
+ &thread->thread.attr,
+ run_thread,
+ &thread->start_args);
if (ret != 0) {
- ODPH_ERR("Failed to start thread on cpu #%d\n", cpu);
- thread_tbl->start_args.linuxtype = ODPTHREAD_NOT_STARTED;
+ ODPH_ERR("Failed to start thread on CPU #%d: %d\n", cpu, ret);
+ thread->cpu = FAILED_CPU;
return ret;
}
@@ -149,129 +227,184 @@ static int odph_linux_thread_create(odph_odpthread_t *thread_tbl,
}
/*
- * create an odpthread set (as linux processes or linux threads or both)
+ * Wait single pthread to exit
*/
-int odph_odpthreads_create(odph_odpthread_t *thread_tbl,
- const odp_cpumask_t *mask,
- const odph_odpthread_params_t *thr_params)
+static int wait_pthread(odph_thread_t *thread)
{
- int i;
- int num;
- int cpu_count;
- int cpu;
-
- num = odp_cpumask_count(mask);
+ int ret;
+ void *thread_ret = NULL;
- memset(thread_tbl, 0, num * sizeof(odph_odpthread_t));
+ /* Wait thread to exit */
+ ret = pthread_join(thread->thread.thread_id, &thread_ret);
- cpu_count = odp_cpu_count();
+ if (ret) {
+ ODPH_ERR("pthread_join failed (%i) from cpu #%i\n",
+ ret, thread->cpu);
+ return -1;
+ }
- if (num < 1 || num > cpu_count) {
- ODPH_ERR("Invalid number of odpthreads:%d"
- " (%d cores available)\n",
- num, cpu_count);
+ if (thread_ret) {
+ ODPH_ERR("Bad exit status cpu #%i %p\n",
+ thread->cpu, thread_ret);
return -1;
}
- cpu = odp_cpumask_first(mask);
- for (i = 0; i < num; i++) {
- if (!helper_options.proc) {
- if (odph_linux_thread_create(&thread_tbl[i],
- cpu,
- thr_params))
- break;
- } else {
- if (_odph_linux_process_create(&thread_tbl[i],
- cpu,
- thr_params))
- break;
- }
+ ret = pthread_attr_destroy(&thread->thread.attr);
- cpu = odp_cpumask_next(mask, cpu);
+ if (ret) {
+ ODPH_ERR("pthread_attr_destroy failed (%i) from cpu #%i\n",
+ ret, thread->cpu);
+ return -1;
}
- thread_tbl[num - 1].last = 1;
- return i;
+ return 0;
}
-/*
- * wait for the odpthreads termination (linux processes and threads)
- */
-int odph_odpthreads_join(odph_odpthread_t *thread_tbl)
+void odph_thread_param_init(odph_thread_param_t *param)
{
- pid_t pid;
- int i = 0;
- int terminated = 0;
- /* child process return code (!=0 is error) */
- int status = 0;
- /* "child" thread return code (!NULL is error) */
- void *thread_ret = NULL;
- int ret;
- int retval = 0;
-
- /* joins linux threads or wait for processes */
- do {
- /* pthreads: */
- switch (thread_tbl[i].start_args.linuxtype) {
- case ODPTHREAD_PTHREAD:
- /* Wait thread to exit */
- ret = pthread_join(thread_tbl[i].thread.thread_id,
- &thread_ret);
- if (ret != 0) {
- ODPH_ERR("Failed to join thread from cpu #%d\n",
- thread_tbl[i].cpu);
- retval = -1;
- } else {
- terminated++;
- if (thread_ret != NULL) {
- ODPH_ERR("Bad exit status cpu #%d %p\n",
- thread_tbl[i].cpu, thread_ret);
- retval = -1;
- }
- }
- pthread_attr_destroy(&thread_tbl[i].thread.attr);
- break;
+ memset(param, 0, sizeof(*param));
+}
+
+void odph_thread_common_param_init(odph_thread_common_param_t *param)
+{
+ memset(param, 0, sizeof(*param));
+ param->sync_timeout = ODP_TIME_SEC_IN_NS;
+}
+
+int odph_thread_create(odph_thread_t thread[],
+ const odph_thread_common_param_t *param,
+ const odph_thread_param_t thr_param[],
+ int num)
+{
+ int i, num_cpu, cpu;
+ const odp_cpumask_t *cpumask = param->cpumask;
+ int use_pthread = 1;
+ odp_atomic_u32_t *init_status = NULL;
- case ODPTHREAD_PROCESS:
+ if (param->thread_model == 1)
+ use_pthread = 0;
- /* processes: */
- pid = waitpid(thread_tbl[i].proc.pid, &status, 0);
+ if (helper_options.mem_model == ODP_MEM_MODEL_PROCESS)
+ use_pthread = 0;
- if (pid < 0) {
- ODPH_ERR("waitpid() failed\n");
- retval = -1;
+ if (num < 1) {
+ ODPH_ERR("Bad number of threads (%i)\n", num);
+ return -1;
+ }
+
+ num_cpu = odp_cpumask_count(cpumask);
+
+ if (num_cpu != num) {
+ ODPH_ERR("Number of threads (%i) and CPUs (%i) does not match"
+ "\n", num, num_cpu);
+ return -1;
+ }
+
+ memset(thread, 0, num * sizeof(odph_thread_t));
+
+ if (param->sync) {
+ init_status = mmap(NULL, sizeof(odp_atomic_u32_t), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+ if (init_status == MAP_FAILED) {
+ ODPH_ERR("mmap() failed: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ cpu = odp_cpumask_first(cpumask);
+ for (i = 0; i < num; i++) {
+ odph_thread_start_args_t *start_args = &thread[i].start_args;
+
+ /* Copy thread parameters */
+ if (param->share_param)
+ start_args->thr_params = thr_param[0];
+ else
+ start_args->thr_params = thr_param[i];
+
+ start_args->instance = param->instance;
+ start_args->status = NOT_STARTED;
+ start_args->init_status = init_status;
+ if (init_status)
+ odp_atomic_init_u32(init_status, NOT_STARTED);
+
+ if (use_pthread) {
+ if (create_pthread(&thread[i], cpu, start_args->thr_params.stack_size))
+ break;
+ } else {
+ if (create_process(&thread[i], cpu, start_args->thr_params.stack_size))
+ break;
+ }
+
+ /* Wait newly created thread to update status */
+ if (init_status) {
+ odp_time_t t1, t2;
+ uint64_t diff_ns;
+ uint32_t status;
+ int timeout = 0;
+ uint64_t timeout_ns = param->sync_timeout;
+
+ if (!timeout_ns)
+ timeout_ns = ODP_TIME_SEC_IN_NS;
+
+ t1 = odp_time_local();
+
+ do {
+ odp_cpu_pause();
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+ timeout = diff_ns > timeout_ns;
+ status = odp_atomic_load_acq_u32(init_status);
+
+ } while (status != INIT_DONE && timeout == 0);
+
+ if (timeout) {
+ ODPH_ERR("Thread (i:%i) start up timeout: sync timeout %" PRIu64 ""
+ " , t1 %" PRIu64 ", t2 %" PRIu64 "\n", i,
+ param->sync_timeout, odp_time_to_ns(t1),
+ odp_time_to_ns(t2));
break;
}
+ }
- terminated++;
+ start_args->status = STARTED;
- /* Examine the child process' termination status */
- if (WIFEXITED(status) &&
- WEXITSTATUS(status) != EXIT_SUCCESS) {
- ODPH_ERR("Child exit status:%d (pid:%d)\n",
- WEXITSTATUS(status), (int)pid);
- retval = -1;
- }
- if (WIFSIGNALED(status)) {
- int signo = WTERMSIG(status);
+ cpu = odp_cpumask_next(cpumask, cpu);
+ }
- ODPH_ERR("Child term signo:%d - %s (pid:%d)\n",
- signo, strsignal(signo), (int)pid);
- retval = -1;
- }
- break;
+ if (init_status) {
+ if (munmap(init_status, sizeof(odp_atomic_u32_t)))
+ ODPH_ERR("munmap() failed: %s\n", strerror(errno));
+ }
- case ODPTHREAD_NOT_STARTED:
- ODPH_DBG("No join done on not started ODPthread.\n");
- break;
- default:
- ODPH_DBG("Invalid case statement value!\n");
+ return i;
+}
+
+int odph_thread_join(odph_thread_t thread[], int num)
+{
+ odph_thread_start_args_t *start_args;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ start_args = &thread[i].start_args;
+
+ if (start_args->status != STARTED) {
+ ODPH_DBG("Thread (i:%i) not started.\n", i);
break;
}
- } while (!thread_tbl[i++].last);
+ if (thread[i].start_args.mem_model == ODP_MEM_MODEL_THREAD) {
+ if (wait_pthread(&thread[i]))
+ break;
+ } else {
+ if (wait_process(&thread[i]))
+ break;
+ }
+
+ start_args->status = NOT_STARTED;
+ }
- return (retval < 0) ? retval : terminated;
+ return i;
}
/* man gettid() notes:
@@ -327,129 +460,43 @@ int odph_odpthread_getaffinity(void)
return -1;
}
-/*
- * return the number of elements in an array of getopt options, excluding the
- * terminating {0,0,0,0}
- */
-static int get_getopt_options_length(const struct option *longopts)
+int odph_parse_options(int argc, char *argv[])
{
- int l = 0;
-
- if (!longopts)
- return 0;
-
- while (longopts[l].name)
- l++;
+ char *env;
+ int i, j;
- return l;
-}
+ helper_options.mem_model = ODP_MEM_MODEL_THREAD;
-/* Merge getopt options */
-int odph_merge_getopt_options(const char *shortopts1,
- const char *shortopts2,
- const struct option *longopts1,
- const struct option *longopts2,
- char **shortopts,
- struct option **longopts)
-{
- int shortopts1_len;
- int shortopts2_len;
- int longopts1_len;
- int longopts2_len;
- int index;
- int res_index = 0;
- struct option termination = {0, 0, 0, 0};
-
- /* merge short options: */
- if (shortopts) {
- shortopts1_len = (shortopts1) ? strlen(shortopts1) : 0;
- shortopts2_len = (shortopts2) ? strlen(shortopts2) : 0;
- *shortopts = malloc(shortopts1_len + shortopts2_len + 1);
- if (!*shortopts)
- return -1;
+ /* Enable process mode using environment variable. Setting environment
+ * variable is easier for CI testing compared to command line
+ * argument. */
+ env = getenv("ODPH_PROC_MODE");
+ if (env && atoi(env))
+ helper_options.mem_model = ODP_MEM_MODEL_PROCESS;
- (*shortopts)[0] = 0;
+ /* Find and remove option */
+ for (i = 0; i < argc;) {
+ if (strcmp(argv[i], "--odph_proc") == 0) {
+ helper_options.mem_model = ODP_MEM_MODEL_PROCESS;
- if (shortopts1)
- strcpy((*shortopts), shortopts1);
- if (shortopts2)
- strcat((*shortopts), shortopts2);
- }
+ for (j = i; j < argc - 1; j++)
+ argv[j] = argv[j + 1];
- /* merge long options */
- if (!longopts)
- return 0;
+ argc--;
+ continue;
+ }
- longopts1_len = get_getopt_options_length(longopts1);
- longopts2_len = get_getopt_options_length(longopts2);
- *longopts = malloc(sizeof(struct option) *
- (longopts1_len + longopts2_len + 1));
- if (!*longopts) {
- if (shortopts)
- free(*shortopts);
- return -1;
+ i++;
}
- for (index = 0; (longopts1) && (longopts1[index].name); index++)
- (*longopts)[res_index++] = longopts1[index];
-
- for (index = 0; (longopts2) && (longopts2[index].name); index++)
- (*longopts)[res_index++] = longopts2[index];
-
- (*longopts)[res_index] = termination;
-
- return 0;
+ return argc;
}
-/*
- * Parse command line options to extract options affecting helpers.
- */
-int odph_parse_options(int argc, char *argv[],
- const char *caller_shortopts,
- const struct option *caller_longopts)
+int odph_options(odph_helper_options_t *options)
{
- int c;
- char *shortopts;
- struct option *longopts;
- int res = 0;
-
- static struct option helper_long_options[] = {
- /* These options set a flag. */
- {"odph_proc", no_argument, &helper_options.proc, 1},
- {0, 0, 0, 0}
- };
-
- static const char *helper_short_options = "";
-
- /* defaults: */
- helper_options.proc = false;
-
- /* merge caller's command line options descriptions with helper's: */
- if (odph_merge_getopt_options(caller_shortopts, helper_short_options,
- caller_longopts, helper_long_options,
- &shortopts, &longopts) < 0)
- return -1;
+ memset(options, 0, sizeof(odph_helper_options_t));
- while (1) {
- /* getopt_long stores the option index here. */
- int option_index = 0;
+ options->mem_model = helper_options.mem_model;
- c = getopt_long (argc, argv,
- shortopts, longopts, &option_index);
-
- /* Detect the end of the options. */
- if (c == -1)
- break;
-
- /* check for unknown options or missing arguments */
- if (c == '?' || c == ':')
- res = -1;
- }
-
- optind = 0; /* caller expects this to be zero if it parses too*/
-
- free(shortopts);
- free(longopts);
-
- return res;
+ return 0;
}
diff --git a/helper/version.c b/helper/version.c
new file mode 100644
index 000000000..f2635a6c2
--- /dev/null
+++ b/helper/version.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#include <odp/helper/version.h>
+
+#define VERSION_STR_EXPAND(x) #x
+#define VERSION_TO_STR(x) VERSION_STR_EXPAND(x)
+
+#define VERSION_STR \
+VERSION_TO_STR(ODPH_VERSION_GENERATION) "." \
+VERSION_TO_STR(ODPH_VERSION_MAJOR) "." \
+VERSION_TO_STR(ODPH_VERSION_MINOR)
+
+const char *odph_version_str(void)
+{
+ return VERSION_STR;
+}
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 000000000..9e23c4fa3
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,583 @@
+include_HEADERS = \
+ odp_api.h
+
+odpincludedir= $(includedir)/odp
+odpinclude_HEADERS = \
+ odp/autoheader_external.h \
+ odp/visibility_begin.h \
+ odp/visibility_end.h
+
+odpapiincludedir= $(includedir)/odp/api/
+odpapiinclude_HEADERS = \
+ odp/api/align.h \
+ odp/api/atomic.h \
+ odp/api/barrier.h \
+ odp/api/buffer.h \
+ odp/api/buffer_types.h \
+ odp/api/byteorder.h \
+ odp/api/chksum.h \
+ odp/api/classification.h \
+ odp/api/comp.h \
+ odp/api/cpu.h \
+ odp/api/cpumask.h \
+ odp/api/crypto.h \
+ odp/api/crypto_types.h \
+ odp/api/debug.h \
+ odp/api/deprecated.h \
+ odp/api/dma.h \
+ odp/api/dma_types.h \
+ odp/api/errno.h \
+ odp/api/event.h \
+ odp/api/event_types.h \
+ odp/api/hash.h \
+ odp/api/hints.h \
+ odp/api/init.h \
+ odp/api/ipsec.h \
+ odp/api/ipsec_types.h \
+ odp/api/ml.h \
+ odp/api/ml_quantize.h \
+ odp/api/ml_types.h \
+ odp/api/packet.h \
+ odp/api/packet_types.h \
+ odp/api/packet_flags.h \
+ odp/api/packet_io.h \
+ odp/api/packet_io_types.h \
+ odp/api/packet_io_stats.h \
+ odp/api/protocols.h \
+ odp/api/pool.h \
+ odp/api/pool_types.h \
+ odp/api/proto_stats.h \
+ odp/api/proto_stats_types.h \
+ odp/api/queue.h \
+ odp/api/queue_types.h \
+ odp/api/random.h \
+ odp/api/random_types.h \
+ odp/api/reassembly.h \
+ odp/api/rwlock.h \
+ odp/api/rwlock_recursive.h \
+ odp/api/schedule.h \
+ odp/api/schedule_types.h \
+ odp/api/shared_memory.h \
+ odp/api/spinlock.h \
+ odp/api/spinlock_recursive.h \
+ odp/api/stash.h \
+ odp/api/stash_types.h \
+ odp/api/std.h \
+ odp/api/std_types.h \
+ odp/api/sync.h \
+ odp/api/system_info.h \
+ odp/api/thread.h \
+ odp/api/thread_types.h \
+ odp/api/threshold.h \
+ odp/api/thrmask.h \
+ odp/api/ticketlock.h \
+ odp/api/time.h \
+ odp/api/time_types.h \
+ odp/api/timer.h \
+ odp/api/timer_types.h \
+ odp/api/traffic_mngr.h \
+ odp/api/version.h
+
+odpapispecincludedir= $(includedir)/odp/api/spec
+odpapispecinclude_HEADERS = \
+ odp/api/spec/align.h \
+ odp/api/spec/atomic.h \
+ odp/api/spec/barrier.h \
+ odp/api/spec/buffer.h \
+ odp/api/spec/buffer_types.h \
+ odp/api/spec/byteorder.h \
+ odp/api/spec/chksum.h \
+ odp/api/spec/classification.h \
+ odp/api/spec/comp.h \
+ odp/api/spec/cpu.h \
+ odp/api/spec/cpumask.h \
+ odp/api/spec/crypto.h \
+ odp/api/spec/crypto_types.h \
+ odp/api/spec/debug.h \
+ odp/api/spec/dma.h \
+ odp/api/spec/dma_types.h \
+ odp/api/spec/errno.h \
+ odp/api/spec/event.h \
+ odp/api/spec/event_types.h \
+ odp/api/spec/hash.h \
+ odp/api/spec/hints.h \
+ odp/api/spec/init.h \
+ odp/api/spec/ipsec.h \
+ odp/api/spec/ipsec_types.h \
+ odp/api/spec/ml.h \
+ odp/api/spec/ml_quantize.h \
+ odp/api/spec/ml_types.h \
+ odp/api/spec/packet.h \
+ odp/api/spec/packet_types.h \
+ odp/api/spec/packet_flags.h \
+ odp/api/spec/packet_io.h \
+ odp/api/spec/packet_io_types.h \
+ odp/api/spec/packet_io_stats.h \
+ odp/api/spec/protocols.h \
+ odp/api/spec/pool.h \
+ odp/api/spec/pool_types.h \
+ odp/api/spec/proto_stats.h \
+ odp/api/spec/proto_stats_types.h \
+ odp/api/spec/queue.h \
+ odp/api/spec/queue_types.h \
+ odp/api/spec/random.h \
+ odp/api/spec/random_types.h \
+ odp/api/spec/reassembly.h \
+ odp/api/spec/rwlock.h \
+ odp/api/spec/rwlock_recursive.h \
+ odp/api/spec/schedule.h \
+ odp/api/spec/schedule_types.h \
+ odp/api/spec/shared_memory.h \
+ odp/api/spec/spinlock.h \
+ odp/api/spec/spinlock_recursive.h \
+ odp/api/spec/stash.h \
+ odp/api/spec/stash_types.h \
+ odp/api/spec/std.h \
+ odp/api/spec/std_types.h \
+ odp/api/spec/sync.h \
+ odp/api/spec/system_info.h \
+ odp/api/spec/thread.h \
+ odp/api/spec/thread_types.h \
+ odp/api/spec/threshold.h \
+ odp/api/spec/thrmask.h \
+ odp/api/spec/ticketlock.h \
+ odp/api/spec/time.h \
+ odp/api/spec/time_types.h \
+ odp/api/spec/timer.h \
+ odp/api/spec/timer_types.h \
+ odp/api/spec/traffic_mngr.h
+
+nodist_odpapispecinclude_HEADERS = \
+ odp/api/spec/deprecated.h \
+ odp/api/spec/version.h
+
+odpapiabidefaultincludedir= $(includedir)/odp/api/abi-default
+odpapiabidefaultinclude_HEADERS = \
+ odp/api/abi-default/align.h \
+ odp/api/abi-default/atomic.h \
+ odp/api/abi-default/barrier.h \
+ odp/api/abi-default/buffer.h \
+ odp/api/abi-default/buffer_types.h \
+ odp/api/abi-default/byteorder.h \
+ odp/api/abi-default/classification.h \
+ odp/api/abi-default/comp.h \
+ odp/api/abi-default/cpu.h \
+ odp/api/abi-default/cpumask.h \
+ odp/api/abi-default/crypto.h \
+ odp/api/abi-default/crypto_types.h \
+ odp/api/abi-default/debug.h \
+ odp/api/abi-default/dma.h \
+ odp/api/abi-default/dma_types.h \
+ odp/api/abi-default/errno.h \
+ odp/api/abi-default/event.h \
+ odp/api/abi-default/event_types.h \
+ odp/api/abi-default/hash.h \
+ odp/api/abi-default/init.h \
+ odp/api/abi-default/ipsec.h \
+ odp/api/abi-default/ipsec_types.h \
+ odp/api/abi-default/ml_types.h \
+ odp/api/abi-default/packet.h \
+ odp/api/abi-default/packet_types.h \
+ odp/api/abi-default/packet_flags.h \
+ odp/api/abi-default/packet_io.h \
+ odp/api/abi-default/packet_io_types.h \
+ odp/api/abi-default/proto_stats.h \
+ odp/api/abi-default/proto_stats_types.h \
+ odp/api/abi-default/pool.h \
+ odp/api/abi-default/pool_types.h \
+ odp/api/abi-default/queue.h \
+ odp/api/abi-default/queue_types.h \
+ odp/api/abi-default/random.h \
+ odp/api/abi-default/rwlock.h \
+ odp/api/abi-default/rwlock_recursive.h \
+ odp/api/abi-default/schedule.h \
+ odp/api/abi-default/schedule_types.h \
+ odp/api/abi-default/shared_memory.h \
+ odp/api/abi-default/spinlock.h \
+ odp/api/abi-default/spinlock_recursive.h \
+ odp/api/abi-default/stash.h \
+ odp/api/abi-default/stash_types.h \
+ odp/api/abi-default/std.h \
+ odp/api/abi-default/std_types.h \
+ odp/api/abi-default/sync.h \
+ odp/api/abi-default/thread.h \
+ odp/api/abi-default/thread_types.h \
+ odp/api/abi-default/thrmask.h \
+ odp/api/abi-default/ticketlock.h \
+ odp/api/abi-default/time.h \
+ odp/api/abi-default/time_types.h \
+ odp/api/abi-default/timer.h \
+ odp/api/abi-default/timer_types.h \
+ odp/api/abi-default/traffic_mngr.h \
+ odp/api/abi-default/version.h
+
+# Install ABI headers only if required
+if ODP_ABI_COMPAT
+
+odpapiabiarchincludedir = $(archincludedir)/odp/api/abi
+if ARCH_IS_ARM
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/arm32-linux/odp/api/abi/align.h \
+ odp/arch/arm32-linux/odp/api/abi/atomic.h \
+ odp/arch/arm32-linux/odp/api/abi/barrier.h \
+ odp/arch/arm32-linux/odp/api/abi/buffer.h \
+ odp/arch/arm32-linux/odp/api/abi/buffer_types.h \
+ odp/arch/arm32-linux/odp/api/abi/byteorder.h \
+ odp/arch/arm32-linux/odp/api/abi/classification.h \
+ odp/arch/arm32-linux/odp/api/abi/comp.h \
+ odp/arch/arm32-linux/odp/api/abi/cpu.h \
+ odp/arch/arm32-linux/odp/api/abi/cpumask.h \
+ odp/arch/arm32-linux/odp/api/abi/crypto.h \
+ odp/arch/arm32-linux/odp/api/abi/crypto_types.h \
+ odp/arch/arm32-linux/odp/api/abi/debug.h \
+ odp/arch/arm32-linux/odp/api/abi/dma.h \
+ odp/arch/arm32-linux/odp/api/abi/dma_types.h \
+ odp/arch/arm32-linux/odp/api/abi/errno.h \
+ odp/arch/arm32-linux/odp/api/abi/event.h \
+ odp/arch/arm32-linux/odp/api/abi/event_types.h \
+ odp/arch/arm32-linux/odp/api/abi/hash.h \
+ odp/arch/arm32-linux/odp/api/abi/init.h \
+ odp/arch/arm32-linux/odp/api/abi/ipsec.h \
+ odp/arch/arm32-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/arm32-linux/odp/api/abi/ml_types.h \
+ odp/arch/arm32-linux/odp/api/abi/packet.h \
+ odp/arch/arm32-linux/odp/api/abi/packet_types.h \
+ odp/arch/arm32-linux/odp/api/abi/packet_flags.h \
+ odp/arch/arm32-linux/odp/api/abi/packet_io.h \
+ odp/arch/arm32-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/arm32-linux/odp/api/abi/pool.h \
+ odp/arch/arm32-linux/odp/api/abi/pool_types.h \
+ odp/arch/arm32-linux/odp/api/abi/proto_stats.h \
+ odp/arch/arm32-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/arm32-linux/odp/api/abi/queue.h \
+ odp/arch/arm32-linux/odp/api/abi/queue_types.h \
+ odp/arch/arm32-linux/odp/api/abi/random.h \
+ odp/arch/arm32-linux/odp/api/abi/rwlock.h \
+ odp/arch/arm32-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/arm32-linux/odp/api/abi/schedule.h \
+ odp/arch/arm32-linux/odp/api/abi/schedule_types.h \
+ odp/arch/arm32-linux/odp/api/abi/shared_memory.h \
+ odp/arch/arm32-linux/odp/api/abi/spinlock.h \
+ odp/arch/arm32-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/arm32-linux/odp/api/abi/stash.h \
+ odp/arch/arm32-linux/odp/api/abi/stash_types.h \
+ odp/arch/arm32-linux/odp/api/abi/std.h \
+ odp/arch/arm32-linux/odp/api/abi/std_types.h \
+ odp/arch/arm32-linux/odp/api/abi/sync.h \
+ odp/arch/arm32-linux/odp/api/abi/thread.h \
+ odp/arch/arm32-linux/odp/api/abi/thread_types.h \
+ odp/arch/arm32-linux/odp/api/abi/thrmask.h \
+ odp/arch/arm32-linux/odp/api/abi/ticketlock.h \
+ odp/arch/arm32-linux/odp/api/abi/time.h \
+ odp/arch/arm32-linux/odp/api/abi/time_types.h \
+ odp/arch/arm32-linux/odp/api/abi/timer.h \
+ odp/arch/arm32-linux/odp/api/abi/timer_types.h \
+ odp/arch/arm32-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/arm32-linux/odp/api/abi/version.h
+endif
+if ARCH_IS_AARCH64
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/arm64-linux/odp/api/abi/align.h \
+ odp/arch/arm64-linux/odp/api/abi/atomic.h \
+ odp/arch/arm64-linux/odp/api/abi/barrier.h \
+ odp/arch/arm64-linux/odp/api/abi/buffer.h \
+ odp/arch/arm64-linux/odp/api/abi/buffer_types.h \
+ odp/arch/arm64-linux/odp/api/abi/byteorder.h \
+ odp/arch/arm64-linux/odp/api/abi/classification.h \
+ odp/arch/arm64-linux/odp/api/abi/comp.h \
+ odp/arch/arm64-linux/odp/api/abi/cpu.h \
+ odp/arch/arm64-linux/odp/api/abi/cpumask.h \
+ odp/arch/arm64-linux/odp/api/abi/crypto.h \
+ odp/arch/arm64-linux/odp/api/abi/crypto_types.h \
+ odp/arch/arm64-linux/odp/api/abi/debug.h \
+ odp/arch/arm64-linux/odp/api/abi/dma.h \
+ odp/arch/arm64-linux/odp/api/abi/dma_types.h \
+ odp/arch/arm64-linux/odp/api/abi/errno.h \
+ odp/arch/arm64-linux/odp/api/abi/event.h \
+ odp/arch/arm64-linux/odp/api/abi/event_types.h \
+ odp/arch/arm64-linux/odp/api/abi/hash.h \
+ odp/arch/arm64-linux/odp/api/abi/init.h \
+ odp/arch/arm64-linux/odp/api/abi/ipsec.h \
+ odp/arch/arm64-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/arm64-linux/odp/api/abi/ml_types.h \
+ odp/arch/arm64-linux/odp/api/abi/packet.h \
+ odp/arch/arm64-linux/odp/api/abi/packet_types.h \
+ odp/arch/arm64-linux/odp/api/abi/packet_flags.h \
+ odp/arch/arm64-linux/odp/api/abi/packet_io.h \
+ odp/arch/arm64-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/arm64-linux/odp/api/abi/pool.h \
+ odp/arch/arm64-linux/odp/api/abi/pool_types.h \
+ odp/arch/arm64-linux/odp/api/abi/proto_stats.h \
+ odp/arch/arm64-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/arm64-linux/odp/api/abi/queue.h \
+ odp/arch/arm64-linux/odp/api/abi/queue_types.h \
+ odp/arch/arm64-linux/odp/api/abi/random.h \
+ odp/arch/arm64-linux/odp/api/abi/rwlock.h \
+ odp/arch/arm64-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/arm64-linux/odp/api/abi/schedule.h \
+ odp/arch/arm64-linux/odp/api/abi/schedule_types.h \
+ odp/arch/arm64-linux/odp/api/abi/shared_memory.h \
+ odp/arch/arm64-linux/odp/api/abi/spinlock.h \
+ odp/arch/arm64-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/arm64-linux/odp/api/abi/stash.h \
+ odp/arch/arm64-linux/odp/api/abi/stash_types.h \
+ odp/arch/arm64-linux/odp/api/abi/std.h \
+ odp/arch/arm64-linux/odp/api/abi/std_types.h \
+ odp/arch/arm64-linux/odp/api/abi/sync.h \
+ odp/arch/arm64-linux/odp/api/abi/thread.h \
+ odp/arch/arm64-linux/odp/api/abi/thread_types.h \
+ odp/arch/arm64-linux/odp/api/abi/thrmask.h \
+ odp/arch/arm64-linux/odp/api/abi/ticketlock.h \
+ odp/arch/arm64-linux/odp/api/abi/time.h \
+ odp/arch/arm64-linux/odp/api/abi/time_types.h \
+ odp/arch/arm64-linux/odp/api/abi/timer.h \
+ odp/arch/arm64-linux/odp/api/abi/timer_types.h \
+ odp/arch/arm64-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/arm64-linux/odp/api/abi/version.h
+endif
+if ARCH_IS_DEFAULT
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/default-linux/odp/api/abi/align.h \
+ odp/arch/default-linux/odp/api/abi/atomic.h \
+ odp/arch/default-linux/odp/api/abi/barrier.h \
+ odp/arch/default-linux/odp/api/abi/buffer.h \
+ odp/arch/default-linux/odp/api/abi/buffer_types.h \
+ odp/arch/default-linux/odp/api/abi/byteorder.h \
+ odp/arch/default-linux/odp/api/abi/classification.h \
+ odp/arch/default-linux/odp/api/abi/comp.h \
+ odp/arch/default-linux/odp/api/abi/cpu.h \
+ odp/arch/default-linux/odp/api/abi/cpumask.h \
+ odp/arch/default-linux/odp/api/abi/crypto.h \
+ odp/arch/default-linux/odp/api/abi/crypto_types.h \
+ odp/arch/default-linux/odp/api/abi/debug.h \
+ odp/arch/default-linux/odp/api/abi/dma.h \
+ odp/arch/default-linux/odp/api/abi/dma_types.h \
+ odp/arch/default-linux/odp/api/abi/errno.h \
+ odp/arch/default-linux/odp/api/abi/event.h \
+ odp/arch/default-linux/odp/api/abi/event_types.h \
+ odp/arch/default-linux/odp/api/abi/hash.h \
+ odp/arch/default-linux/odp/api/abi/init.h \
+ odp/arch/default-linux/odp/api/abi/ipsec.h \
+ odp/arch/default-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/default-linux/odp/api/abi/ml_types.h \
+ odp/arch/default-linux/odp/api/abi/packet.h \
+ odp/arch/default-linux/odp/api/abi/packet_types.h \
+ odp/arch/default-linux/odp/api/abi/packet_flags.h \
+ odp/arch/default-linux/odp/api/abi/packet_io.h \
+ odp/arch/default-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/default-linux/odp/api/abi/pool.h \
+ odp/arch/default-linux/odp/api/abi/pool_types.h \
+ odp/arch/default-linux/odp/api/abi/proto_stats.h \
+ odp/arch/default-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/default-linux/odp/api/abi/queue.h \
+ odp/arch/default-linux/odp/api/abi/queue_types.h \
+ odp/arch/default-linux/odp/api/abi/random.h \
+ odp/arch/default-linux/odp/api/abi/rwlock.h \
+ odp/arch/default-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/default-linux/odp/api/abi/schedule.h \
+ odp/arch/default-linux/odp/api/abi/schedule_types.h \
+ odp/arch/default-linux/odp/api/abi/shared_memory.h \
+ odp/arch/default-linux/odp/api/abi/spinlock.h \
+ odp/arch/default-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/default-linux/odp/api/abi/stash.h \
+ odp/arch/default-linux/odp/api/abi/stash_types.h \
+ odp/arch/default-linux/odp/api/abi/std.h \
+ odp/arch/default-linux/odp/api/abi/std_types.h \
+ odp/arch/default-linux/odp/api/abi/sync.h \
+ odp/arch/default-linux/odp/api/abi/thread.h \
+ odp/arch/default-linux/odp/api/abi/thread_types.h \
+ odp/arch/default-linux/odp/api/abi/thrmask.h \
+ odp/arch/default-linux/odp/api/abi/ticketlock.h \
+ odp/arch/default-linux/odp/api/abi/time.h \
+ odp/arch/default-linux/odp/api/abi/time_types.h \
+ odp/arch/default-linux/odp/api/abi/timer.h \
+ odp/arch/default-linux/odp/api/abi/timer_types.h \
+ odp/arch/default-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/default-linux/odp/api/abi/version.h
+endif
+if ARCH_IS_POWERPC
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/power64-linux/odp/api/abi/align.h \
+ odp/arch/power64-linux/odp/api/abi/atomic.h \
+ odp/arch/power64-linux/odp/api/abi/barrier.h \
+ odp/arch/power64-linux/odp/api/abi/buffer.h \
+ odp/arch/power64-linux/odp/api/abi/buffer_types.h \
+ odp/arch/power64-linux/odp/api/abi/byteorder.h \
+ odp/arch/power64-linux/odp/api/abi/classification.h \
+ odp/arch/power64-linux/odp/api/abi/comp.h \
+ odp/arch/power64-linux/odp/api/abi/cpu.h \
+ odp/arch/power64-linux/odp/api/abi/cpumask.h \
+ odp/arch/power64-linux/odp/api/abi/crypto.h \
+ odp/arch/power64-linux/odp/api/abi/crypto_types.h \
+ odp/arch/power64-linux/odp/api/abi/debug.h \
+ odp/arch/power64-linux/odp/api/abi/dma.h \
+ odp/arch/power64-linux/odp/api/abi/dma_types.h \
+ odp/arch/power64-linux/odp/api/abi/errno.h \
+ odp/arch/power64-linux/odp/api/abi/event.h \
+ odp/arch/power64-linux/odp/api/abi/event_types.h \
+ odp/arch/power64-linux/odp/api/abi/hash.h \
+ odp/arch/power64-linux/odp/api/abi/init.h \
+ odp/arch/power64-linux/odp/api/abi/ipsec.h \
+ odp/arch/power64-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/power64-linux/odp/api/abi/ml_types.h \
+ odp/arch/power64-linux/odp/api/abi/packet.h \
+ odp/arch/power64-linux/odp/api/abi/packet_types.h \
+ odp/arch/power64-linux/odp/api/abi/packet_flags.h \
+ odp/arch/power64-linux/odp/api/abi/packet_io.h \
+ odp/arch/power64-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/power64-linux/odp/api/abi/pool.h \
+ odp/arch/power64-linux/odp/api/abi/pool_types.h \
+ odp/arch/power64-linux/odp/api/abi/proto_stats.h \
+ odp/arch/power64-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/power64-linux/odp/api/abi/queue.h \
+ odp/arch/power64-linux/odp/api/abi/queue_types.h \
+ odp/arch/power64-linux/odp/api/abi/random.h \
+ odp/arch/power64-linux/odp/api/abi/rwlock.h \
+ odp/arch/power64-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/power64-linux/odp/api/abi/schedule.h \
+ odp/arch/power64-linux/odp/api/abi/schedule_types.h \
+ odp/arch/power64-linux/odp/api/abi/shared_memory.h \
+ odp/arch/power64-linux/odp/api/abi/spinlock.h \
+ odp/arch/power64-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/power64-linux/odp/api/abi/stash.h \
+ odp/arch/power64-linux/odp/api/abi/stash_types.h \
+ odp/arch/power64-linux/odp/api/abi/std.h \
+ odp/arch/power64-linux/odp/api/abi/std_types.h \
+ odp/arch/power64-linux/odp/api/abi/sync.h \
+ odp/arch/power64-linux/odp/api/abi/thread.h \
+ odp/arch/power64-linux/odp/api/abi/thread_types.h \
+ odp/arch/power64-linux/odp/api/abi/thrmask.h \
+ odp/arch/power64-linux/odp/api/abi/ticketlock.h \
+ odp/arch/power64-linux/odp/api/abi/time.h \
+ odp/arch/power64-linux/odp/api/abi/time_types.h \
+ odp/arch/power64-linux/odp/api/abi/timer.h \
+ odp/arch/power64-linux/odp/api/abi/timer_types.h \
+ odp/arch/power64-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/power64-linux/odp/api/abi/version.h
+endif
+if ARCH_IS_X86_32
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/x86_32-linux/odp/api/abi/align.h \
+ odp/arch/x86_32-linux/odp/api/abi/atomic.h \
+ odp/arch/x86_32-linux/odp/api/abi/barrier.h \
+ odp/arch/x86_32-linux/odp/api/abi/buffer.h \
+ odp/arch/x86_32-linux/odp/api/abi/buffer_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/byteorder.h \
+ odp/arch/x86_32-linux/odp/api/abi/classification.h \
+ odp/arch/x86_32-linux/odp/api/abi/comp.h \
+ odp/arch/x86_32-linux/odp/api/abi/cpu.h \
+ odp/arch/x86_32-linux/odp/api/abi/cpumask.h \
+ odp/arch/x86_32-linux/odp/api/abi/crypto.h \
+ odp/arch/x86_32-linux/odp/api/abi/crypto_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/debug.h \
+ odp/arch/x86_32-linux/odp/api/abi/dma.h \
+ odp/arch/x86_32-linux/odp/api/abi/dma_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/errno.h \
+ odp/arch/x86_32-linux/odp/api/abi/event.h \
+ odp/arch/x86_32-linux/odp/api/abi/event_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/hash.h \
+ odp/arch/x86_32-linux/odp/api/abi/init.h \
+ odp/arch/x86_32-linux/odp/api/abi/ipsec.h \
+ odp/arch/x86_32-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/ml_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/packet.h \
+ odp/arch/x86_32-linux/odp/api/abi/packet_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/packet_flags.h \
+ odp/arch/x86_32-linux/odp/api/abi/packet_io.h \
+ odp/arch/x86_32-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/pool.h \
+ odp/arch/x86_32-linux/odp/api/abi/pool_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/proto_stats.h \
+ odp/arch/x86_32-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/queue.h \
+ odp/arch/x86_32-linux/odp/api/abi/queue_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/random.h \
+ odp/arch/x86_32-linux/odp/api/abi/rwlock.h \
+ odp/arch/x86_32-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/x86_32-linux/odp/api/abi/schedule.h \
+ odp/arch/x86_32-linux/odp/api/abi/schedule_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/shared_memory.h \
+ odp/arch/x86_32-linux/odp/api/abi/spinlock.h \
+ odp/arch/x86_32-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/x86_32-linux/odp/api/abi/stash.h \
+ odp/arch/x86_32-linux/odp/api/abi/stash_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/std.h \
+ odp/arch/x86_32-linux/odp/api/abi/std_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/sync.h \
+ odp/arch/x86_32-linux/odp/api/abi/thread.h \
+ odp/arch/x86_32-linux/odp/api/abi/thread_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/thrmask.h \
+ odp/arch/x86_32-linux/odp/api/abi/ticketlock.h \
+ odp/arch/x86_32-linux/odp/api/abi/time.h \
+ odp/arch/x86_32-linux/odp/api/abi/time_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/timer.h \
+ odp/arch/x86_32-linux/odp/api/abi/timer_types.h \
+ odp/arch/x86_32-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/x86_32-linux/odp/api/abi/version.h
+endif
+if ARCH_IS_X86_64
+odpapiabiarchinclude_HEADERS = \
+ odp/arch/x86_64-linux/odp/api/abi/align.h \
+ odp/arch/x86_64-linux/odp/api/abi/atomic.h \
+ odp/arch/x86_64-linux/odp/api/abi/barrier.h \
+ odp/arch/x86_64-linux/odp/api/abi/buffer.h \
+ odp/arch/x86_64-linux/odp/api/abi/buffer_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/byteorder.h \
+ odp/arch/x86_64-linux/odp/api/abi/classification.h \
+ odp/arch/x86_64-linux/odp/api/abi/comp.h \
+ odp/arch/x86_64-linux/odp/api/abi/cpu.h \
+ odp/arch/x86_64-linux/odp/api/abi/cpumask.h \
+ odp/arch/x86_64-linux/odp/api/abi/crypto.h \
+ odp/arch/x86_64-linux/odp/api/abi/crypto_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/debug.h \
+ odp/arch/x86_64-linux/odp/api/abi/dma.h \
+ odp/arch/x86_64-linux/odp/api/abi/dma_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/errno.h \
+ odp/arch/x86_64-linux/odp/api/abi/event.h \
+ odp/arch/x86_64-linux/odp/api/abi/event_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/hash.h \
+ odp/arch/x86_64-linux/odp/api/abi/init.h \
+ odp/arch/x86_64-linux/odp/api/abi/ipsec.h \
+ odp/arch/x86_64-linux/odp/api/abi/ipsec_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/ml_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/packet.h \
+ odp/arch/x86_64-linux/odp/api/abi/packet_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/packet_flags.h \
+ odp/arch/x86_64-linux/odp/api/abi/packet_io.h \
+ odp/arch/x86_64-linux/odp/api/abi/packet_io_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/pool.h \
+ odp/arch/x86_64-linux/odp/api/abi/pool_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/proto_stats.h \
+ odp/arch/x86_64-linux/odp/api/abi/proto_stats_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/queue.h \
+ odp/arch/x86_64-linux/odp/api/abi/queue_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/random.h \
+ odp/arch/x86_64-linux/odp/api/abi/rwlock.h \
+ odp/arch/x86_64-linux/odp/api/abi/rwlock_recursive.h \
+ odp/arch/x86_64-linux/odp/api/abi/schedule.h \
+ odp/arch/x86_64-linux/odp/api/abi/schedule_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/shared_memory.h \
+ odp/arch/x86_64-linux/odp/api/abi/spinlock.h \
+ odp/arch/x86_64-linux/odp/api/abi/spinlock_recursive.h \
+ odp/arch/x86_64-linux/odp/api/abi/stash.h \
+ odp/arch/x86_64-linux/odp/api/abi/stash_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/std.h \
+ odp/arch/x86_64-linux/odp/api/abi/std_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/sync.h \
+ odp/arch/x86_64-linux/odp/api/abi/thread.h \
+ odp/arch/x86_64-linux/odp/api/abi/thread_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/thrmask.h \
+ odp/arch/x86_64-linux/odp/api/abi/ticketlock.h \
+ odp/arch/x86_64-linux/odp/api/abi/time.h \
+ odp/arch/x86_64-linux/odp/api/abi/time_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/timer.h \
+ odp/arch/x86_64-linux/odp/api/abi/timer_types.h \
+ odp/arch/x86_64-linux/odp/api/abi/traffic_mngr.h \
+ odp/arch/x86_64-linux/odp/api/abi/version.h
+endif
+endif # ODP_ABI_COMPAT
+
+# Rerefence all nodist_*_HEADERS here
+.PHONY: $(nodist_odpapispecinclude_HEADERS)
+$(nodist_odpapispecinclude_HEADERS):
+ $(MAKE) -C $(top_builddir) $(subdir)/$@
diff --git a/include/README b/include/README
new file mode 100644
index 000000000..90498c7fa
--- /dev/null
+++ b/include/README
@@ -0,0 +1,117 @@
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2017 Linaro Limited
+Copyright (c) 2023 Nokia
+
+# ODP specification
+
+ODP specification consists of several types of files, which together provide
+full list of types, values and functions that ODP implementation MUST provide.
+
+## API Principles
+
+Both applications and implementations must comply with the API specification. If
+not otherwise documented, results are undefined if an application acts against
+the specification. For example, if an application passes bad parameters to an
+ODP API, one implementation may report an error, while another may not check
+them (to maximize performance) and would just crash.
+
+Many ODP component areas provide an odp_xxx_capability() API that returns
+platform-specific information regarding supported features in that component.
+For best portability applications should always use these capability APIs.
+
+ODP APIs are described using opaque data types of which definition are left up
+to the ODP implementation. For example, ODP packets are referenced by handles of
+type odp_packet_t, and packet-related APIs take arguments of this type. What an
+odp_packet_t actually is, is not part of the ODP API specification and
+applications cannot make assumptions about the underlying type.
+
+Application gains ODP handle ownership when it receives it from an ODP API call.
+For example, application can receive an odp_event_t handle from `odp_schedule()`
+call. The ownership ends when the handle is passed back to ODP, for example with
+`odp_queue_enq()` call. Application MUST NOT use the handle anymore after it
+has lost the ownership.
+
+## API headers
+
+ODP API headers are divided into the following directories.
+```
+include/
+├── odp_api.h
+└── odp/
+ ├── api/
+ │ ├── abi-default/
+ │ └── spec/
+ └── arch/
+ └── @ARCH_ABI@/
+ └── odp/
+ └── api/
+ └── abi/
+platform/
+└── @with_platform@/
+ ├── include/
+ └── include-abi/
+ └── odp/
+ └── api/
+ └── abi/
+```
+
+### Application header
+
+This header found at `include/odp_api.h` is an entry point for an application.
+Application MUST include only odp_api.h, nothing else. This file includes all
+files from ODP specification.
+
+### API specification
+
+These are the files from `include/odp/api/spec` directory. They specify a set
+of function prototypes, types, type names, enumerations, etc. that MUST be
+provided by ODP implementation. Doxygen comments inside these files document
+the API specification. Content of some types and value of some enumerations are
+left undefined in API spec. These are defined either in ABI spec or
+implementation specific (non-ABI compatible) headers. An implementation MUST use
+these headers AS IS, without any modifications to be compatible with ODP
+specification.
+
+### ABI compatibility specification
+
+These are the files from `include/odp/arch/@ARCH_ABI@/odp/api/abi/` directory.
+They specify a set of types and values that MUST be used AS IS without any
+modifications by an implementation if it supports and is compiled for
+ABI-compatibility mode.
+
+### Default ABI headers
+
+These are the files from `include/odp/api/abi-default` directory. They provide
+default specification for ODP types and values for ABI compatibility. CPU
+architecture specific ABI compatibility files heavily depend on these headers.
+These files MUST NOT be changed by an implementation.
+
+### Additional API headers
+
+These are the files from `include/odp/api` directory. They glue together API
+and ABI specification headers. Although they are not part of ODP specification
+itself, they provide an easy way for an implementation to use ODP API/ABI
+header files. An implementation SHOULD use these headers AS IS unless it has
+strong reason not to do so.
+
+## Platform-specific headers
+
+### Platform ABI headers
+
+These are the headers found at
+`platform/@with_platform@/include-abi/odp/api/abi` directory. They are used by
+the rest of ODP code if implementation is compiled with ABI compatibility
+disabled. They should implement at least a set of types and values documented
+in ODP API specification headers. They are permitted to provide any platform
+specific optimizations (i.e. they might provide types and/or values that map
+directly onto the hardware details, they might provide inline functions to
+speed up execution of the application, etc.). These headers MAY use ODP default
+ABI headers if they do fit.
+
+### Additional platform-specific headers
+
+Platform MAY provide additional headers at `platform/@with_platform/include`.
+However, these headers SHOULD NOT be used directly by an application, because
+this will tie it to the exact implementation details. Application MUST include
+only <odp_api.h> header. Platform ABI headers MAY use these headers to
+implement platform-specific optimizations.
diff --git a/include/odp.h b/include/odp.h
deleted file mode 100644
index 26c2f7a62..000000000
--- a/include/odp.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * WARNING: THIS FILE IS DEPRECATED AND WILL BE REMOVED!!!
- * ODP APPLICATIONS SHOULD NOW INCLUDE odp_api.h INSTEAD.
- *
- * This file is here to ease the transition period but will be removed.
- * This change has been made to enable the creation of other ODP interfaces.
- *
- */
-
-#ifndef ODP_H_
-#define ODP_H_
-
-#include <odp_api.h>
-
-#endif
diff --git a/include/odp/api/abi-default/align.h b/include/odp/api/abi-default/align.h
new file mode 100644
index 000000000..fa95d728b
--- /dev/null
+++ b/include/odp/api/abi-default/align.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP alignments
+ */
+
+#ifndef ODP_ABI_ALIGN_H_
+#define ODP_ABI_ALIGN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/cpu.h>
+
+/** @addtogroup odp_compiler_optim
+ * @{
+ */
+
+#ifdef __GNUC__
+
+#define ODP_ALIGNED(x) __attribute__((__aligned__(x)))
+
+#define ODP_PACKED __attribute__((__packed__))
+
+#define ODP_OFFSETOF(type, member) __builtin_offsetof(type, member)
+
+#define ODP_FIELD_SIZEOF(type, member) sizeof(((type *)0)->member)
+
+#else
+#error Non-gcc compatible compiler
+#endif
+
+/* ODP_CACHE_LINE_SIZE is defined in odp/api/abi/cpu.h */
+
+#define ODP_PAGE_SIZE 4096
+
+#define ODP_ALIGNED_CACHE ODP_ALIGNED(ODP_CACHE_LINE_SIZE)
+
+#define ODP_ALIGNED_PAGE ODP_ALIGNED(ODP_PAGE_SIZE)
+
+#define ODP_CACHE_LINE_ROUNDUP(x) \
+((ODP_CACHE_LINE_SIZE) * (((x) + (ODP_CACHE_LINE_SIZE) - 1) / (ODP_CACHE_LINE_SIZE)))
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/atomic.h b/include/odp/api/abi-default/atomic.h
new file mode 100644
index 000000000..9999360fc
--- /dev/null
+++ b/include/odp/api/abi-default/atomic.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP atomic operations
+ */
+
+#ifndef ODP_ABI_ATOMIC_H_
+#define ODP_ABI_ATOMIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/align.h>
+
+/**
+ * @internal
+ * Atomic 32-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint32_t)) odp_atomic_u32_s {
+ uint32_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u32_t;
+
+#if __GCC_ATOMIC_LLONG_LOCK_FREE >= 2
+
+/**
+ * @internal
+ * Atomic 64-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) odp_atomic_u64_s {
+ uint64_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u64_t;
+
+#else
+
+/**
+ * @internal
+ * Use embedded lock for atomic 64-bit variable implementation
+ */
+#define ODP_ATOMIC_U64_LOCK 1
+
+/**
+ * @internal
+ * Atomic 64-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) odp_atomic_u64_s {
+ uint64_t v; /**< Actual storage for the atomic variable */
+ /* Some architectures do not support lock-free operations on 64-bit
+ * data types. We use a spin lock to ensure atomicity. */
+ char lock; /**< Spin lock (if needed) used to ensure atomic access */
+} odp_atomic_u64_t;
+
+#endif
+
+#if defined(__SIZEOF_INT128__) || defined(_ODP_LOCK_FREE_128BIT_ATOMICS)
+
+/**
+ * @internal
+ * Atomic 128-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(odp_u128_t)) odp_atomic_u128_s {
+ odp_u128_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u128_t;
+
+#else
+
+/**
+ * @internal
+ * Atomic 128-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(odp_u128_t)) odp_atomic_u128_s {
+ odp_u128_t v; /**< Actual storage for the atomic variable */
+ /* Some architectures do not support lock-free operations on 128-bit
+ * data types. We use a spin lock to ensure atomicity. */
+ char lock; /**< Spin lock (if needed) used to ensure atomic access */
+} odp_atomic_u128_t;
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/barrier.h b/include/odp/api/abi-default/barrier.h
new file mode 100644
index 000000000..ee0329a97
--- /dev/null
+++ b/include/odp/api/abi-default/barrier.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP barrier
+ */
+
+#ifndef ODP_ABI_BARRIER_H_
+#define ODP_ABI_BARRIER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/atomic.h>
+
+/**
+ * @internal
+ * ODP thread synchronization barrier
+ */
+struct odp_barrier_s {
+ uint32_t count; /**< Thread count */
+ odp_atomic_u32_t bar; /**< Barrier counter */
+};
+
+typedef struct odp_barrier_s odp_barrier_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/buffer.h b/include/odp/api/abi-default/buffer.h
new file mode 100644
index 000000000..dce3fcac3
--- /dev/null
+++ b/include/odp/api/abi-default/buffer.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_BUFFER_H_
+#define ODP_ABI_BUFFER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/buffer_types.h b/include/odp/api/abi-default/buffer_types.h
new file mode 100644
index 000000000..9179ae321
--- /dev/null
+++ b/include/odp/api/abi-default/buffer_types.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ABI_BUFFER_TYPES_H_
+#define ODP_ABI_BUFFER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_buffer_t;
+
+/** @addtogroup odp_buffer
+ * @{
+ */
+
+typedef _odp_abi_buffer_t *odp_buffer_t;
+
+#define ODP_BUFFER_INVALID ((odp_buffer_t)0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/byteorder.h b/include/odp/api/abi-default/byteorder.h
new file mode 100644
index 000000000..a3a512598
--- /dev/null
+++ b/include/odp/api/abi-default/byteorder.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP byteorder
+ */
+
+#ifndef ODP_ABI_BYTEORDER_H_
+#define ODP_ABI_BYTEORDER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+#ifndef __BYTE_ORDER__
+#error __BYTE_ORDER__ not defined!
+#endif
+
+#ifndef __ORDER_BIG_ENDIAN__
+#error __ORDER_BIG_ENDIAN__ not defined!
+#endif
+
+#ifndef __ORDER_LITTLE_ENDIAN__
+#error __ORDER_LITTLE_ENDIAN__ not defined!
+#endif
+
+/** @addtogroup odp_compiler_optim
+ * @{
+ */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define ODP_LITTLE_ENDIAN 1
+ #define ODP_BIG_ENDIAN 0
+ #define ODP_BYTE_ORDER ODP_LITTLE_ENDIAN
+ #define ODP_LITTLE_ENDIAN_BITFIELD 1
+ #define ODP_BIG_ENDIAN_BITFIELD 0
+ #define ODP_BITFIELD_ORDER ODP_LITTLE_ENDIAN_BITFIELD
+#else
+ #define ODP_LITTLE_ENDIAN 0
+ #define ODP_BIG_ENDIAN 1
+ #define ODP_BYTE_ORDER ODP_BIG_ENDIAN
+ #define ODP_LITTLE_ENDIAN_BITFIELD 0
+ #define ODP_BIG_ENDIAN_BITFIELD 1
+ #define ODP_BITFIELD_ORDER ODP_BIG_ENDIAN_BITFIELD
+#endif
+
+typedef uint16_t odp_u16le_t;
+typedef uint16_t odp_u16be_t;
+
+typedef uint32_t odp_u32le_t;
+typedef uint32_t odp_u32be_t;
+
+typedef uint64_t odp_u64le_t;
+typedef uint64_t odp_u64be_t;
+
+typedef uint16_t odp_u16sum_t;
+typedef uint32_t odp_u32sum_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/classification.h b/include/odp/api/abi-default/classification.h
new file mode 100644
index 000000000..fdc98f252
--- /dev/null
+++ b/include/odp/api/abi-default/classification.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_CLASSIFICATION_H_
+#define ODP_ABI_CLASSIFICATION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_cos_t;
+
+/** Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_pmr_t;
+
+/** @addtogroup odp_classification
+ * @{
+ */
+
+typedef _odp_abi_cos_t *odp_cos_t;
+typedef _odp_abi_pmr_t *odp_pmr_t;
+
+#define ODP_COS_INVALID ((odp_cos_t)0)
+#define ODP_PMR_INVALID ((odp_pmr_t)0)
+
+#define ODP_COS_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/comp.h b/include/odp/api/abi-default/comp.h
new file mode 100644
index 000000000..3f936aa20
--- /dev/null
+++ b/include/odp/api/abi-default/comp.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_COMP_H_
+#define ODP_ABI_COMP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_comp_session_t;
+
+/** @addtogroup odp_compression
+ * @{
+ */
+
+typedef _odp_abi_comp_session_t *odp_comp_session_t;
+
+#define ODP_COMP_SESSION_INVALID ((odp_comp_session_t)0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/cpu.h b/include/odp/api/abi-default/cpu.h
new file mode 100644
index 000000000..7bc444236
--- /dev/null
+++ b/include/odp/api/abi-default/cpu.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_CPU_H_
+#define ODP_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ODP_CACHE_LINE_SIZE
+#define ODP_CACHE_LINE_SIZE 64
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/cpumask.h b/include/odp/api/abi-default/cpumask.h
new file mode 100644
index 000000000..bb7638f0c
--- /dev/null
+++ b/include/odp/api/abi-default/cpumask.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP CPU masks and enumeration
+ */
+
+#ifndef ODP_ABI_CPUMASK_H_
+#define ODP_ABI_CPUMASK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @addtogroup odp_cpumask
+ * @{
+ */
+
+#include <odp/api/std_types.h>
+#include <odp/api/align.h>
+#include <sched.h>
+
+#define ODP_CPUMASK_SIZE (sizeof(cpu_set_t) * 8)
+
+#define ODP_CPUMASK_STR_SIZE ((ODP_CPUMASK_SIZE + 3) / 4 + 3)
+
+/**
+ * CPU mask
+ *
+ * Don't access directly, use access functions.
+ */
+typedef struct ODP_ALIGNED(8) odp_cpumask_t {
+ /** @private CPU mask storage
+ *
+ * This is private to the implementation.
+ * Don't access directly, use access functions.
+ */
+ uint8_t _u8[ODP_CPUMASK_SIZE / 8];
+} odp_cpumask_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/crypto.h b/include/odp/api/abi-default/crypto.h
new file mode 100644
index 000000000..aa80587c4
--- /dev/null
+++ b/include/odp/api/abi-default/crypto.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_CRYPTO_H_
+#define ODP_ABI_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/crypto_types.h b/include/odp/api/abi-default/crypto_types.h
new file mode 100644
index 000000000..8d860b6ef
--- /dev/null
+++ b/include/odp/api/abi-default/crypto_types.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_CRYPTO_TYPES_H_
+#define ODP_ABI_CRYPTO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @addtogroup odp_crypto
+ * @{
+ */
+
+#define ODP_CRYPTO_SESSION_INVALID (0xffffffffffffffffULL)
+
+typedef uint64_t odp_crypto_session_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/debug.h b/include/odp/api/abi-default/debug.h
new file mode 100644
index 000000000..5b196d589
--- /dev/null
+++ b/include/odp/api/abi-default/debug.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP debug
+ */
+
+#ifndef ODP_ABI_DEBUG_H_
+#define ODP_ABI_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal Compile time assertion macro. Fails compilation and outputs 'msg'
+ * if condition 'cond' is false. Macro definition is empty when compiler is not
+ * supported or the compiler does not support static assertion.
+ */
+#ifndef __cplusplus
+#define ODP_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
+#else
+#define ODP_STATIC_ASSERT(cond, msg) static_assert(cond, msg)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/dma.h b/include/odp/api/abi-default/dma.h
new file mode 100644
index 000000000..dcc67bc71
--- /dev/null
+++ b/include/odp/api/abi-default/dma.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ABI_DMA_H_
+#define ODP_ABI_DMA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/dma_types.h b/include/odp/api/abi-default/dma_types.h
new file mode 100644
index 000000000..005ba3d16
--- /dev/null
+++ b/include/odp/api/abi-default/dma_types.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ABI_DMA_TYPES_H_
+#define ODP_ABI_DMA_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_dma_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_dma_compl_t;
+
+/** @addtogroup odp_dma
+ * @{
+ */
+
+typedef _odp_abi_dma_t *odp_dma_t;
+
+#define ODP_DMA_INVALID ((odp_dma_t)0)
+
+typedef _odp_abi_dma_t *odp_dma_compl_t;
+
+#define ODP_DMA_COMPL_INVALID ((odp_dma_compl_t)0)
+
+typedef uint64_t odp_dma_transfer_id_t;
+
+#define ODP_DMA_TRANSFER_ID_INVALID ((odp_dma_transfer_id_t)0)
+
+#define ODP_DMA_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/errno.h b/include/odp/api/abi-default/errno.h
new file mode 100644
index 000000000..d8eee49fb
--- /dev/null
+++ b/include/odp/api/abi-default/errno.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP errno
+ */
+
+#ifndef ODP_ABI_ERRNO_H_
+#define ODP_ABI_ERRNO_H_
+
+/* Empty header to allow platforms to override inlining
+ * of errno functions.
+ */
+
+#endif
diff --git a/include/odp/api/abi-default/event.h b/include/odp/api/abi-default/event.h
new file mode 100644
index 000000000..295f8608d
--- /dev/null
+++ b/include/odp/api/abi-default/event.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_EVENT_H_
+#define ODP_ABI_EVENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/event_types.h b/include/odp/api/abi-default/event_types.h
new file mode 100644
index 000000000..e5b50d9c0
--- /dev/null
+++ b/include/odp/api/abi-default/event_types.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#ifndef ODP_ABI_EVENT_TYPES_H_
+#define ODP_ABI_EVENT_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_event_t;
+
+/** @addtogroup odp_event
+ * @{
+ */
+
+typedef _odp_abi_event_t *odp_event_t;
+
+#define ODP_EVENT_INVALID ((odp_event_t)0)
+
+typedef enum {
+ ODP_EVENT_BUFFER = 1,
+ ODP_EVENT_PACKET = 2,
+ ODP_EVENT_TIMEOUT = 3,
+ ODP_EVENT_IPSEC_STATUS = 5,
+ ODP_EVENT_PACKET_VECTOR = 6,
+ ODP_EVENT_PACKET_TX_COMPL = 7,
+ ODP_EVENT_DMA_COMPL = 8,
+ ODP_EVENT_ML_COMPL = 9
+} odp_event_type_t;
+
+typedef enum {
+ ODP_EVENT_NO_SUBTYPE = 0,
+ ODP_EVENT_PACKET_BASIC = 1,
+ ODP_EVENT_PACKET_CRYPTO = 2,
+ ODP_EVENT_PACKET_IPSEC = 3,
+ ODP_EVENT_PACKET_COMP = 4,
+ ODP_EVENT_ML_COMPL_LOAD = 5,
+ ODP_EVENT_ML_COMPL_RUN = 6
+} odp_event_subtype_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/hash.h b/include/odp/api/abi-default/hash.h
new file mode 100644
index 000000000..9cd0fb03d
--- /dev/null
+++ b/include/odp/api/abi-default/hash.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP hash
+ */
+
+#ifndef ODP_ABI_HASH_H_
+#define ODP_ABI_HASH_H_
+
+/* Empty header to allow platforms to override inlining
+ * of hash functions.
+ */
+
+#endif
diff --git a/include/odp/api/abi-default/init.h b/include/odp/api/abi-default/init.h
new file mode 100644
index 000000000..221567e24
--- /dev/null
+++ b/include/odp/api/abi-default/init.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP initialization.
+ */
+
+#ifndef ODP_ABI_INIT_H_
+#define ODP_ABI_INIT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+typedef uint64_t odp_instance_t;
+
+/**
+ * @internal platform specific data
+ */
+typedef struct odp_platform_init_t {
+ char dummy; /**< @internal Dummy */
+} odp_platform_init_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/ipsec.h b/include/odp/api/abi-default/ipsec.h
new file mode 100644
index 000000000..ab3d5b643
--- /dev/null
+++ b/include/odp/api/abi-default/ipsec.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_IPSEC_H_
+#define ODP_ABI_IPSEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the packet inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/ipsec_types.h b/include/odp/api/abi-default/ipsec_types.h
new file mode 100644
index 000000000..9d099b80d
--- /dev/null
+++ b/include/odp/api/abi-default/ipsec_types.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_IPSEC_TYPES_H_
+#define ODP_ABI_IPSEC_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_ipsec_sa_t;
+
+/** @addtogroup odp_ipsec
+ * @{
+ */
+
+typedef _odp_abi_ipsec_sa_t *odp_ipsec_sa_t;
+
+#define ODP_IPSEC_SA_INVALID ((odp_ipsec_sa_t)0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/ml_types.h b/include/odp/api/abi-default/ml_types.h
new file mode 100644
index 000000000..723beb1bc
--- /dev/null
+++ b/include/odp/api/abi-default/ml_types.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef ODP_ABI_ML_TYPES_H_
+#define ODP_ABI_ML_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_ml_model_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_ml_compl_t;
+
+/** @internal Implementation specific ML parameters */
+struct _odp_ml_model_extra_param_t {
+ /** @internal Dummy field to avoid empty struct */
+ char dummy;
+};
+
+/** @addtogroup odp_ml
+ * @{
+ */
+
+typedef _odp_abi_ml_model_t *odp_ml_model_t;
+typedef _odp_abi_ml_compl_t *odp_ml_compl_t;
+typedef struct _odp_ml_model_extra_param_t odp_ml_model_extra_param_t;
+
+#define ODP_ML_MODEL_INVALID ((odp_ml_model_t)0)
+#define ODP_ML_COMPL_INVALID ((odp_ml_compl_t)0)
+
+#define ODP_ML_MODEL_NAME_LEN 64
+#define ODP_ML_MODEL_IO_NAME_LEN 64
+#define ODP_ML_SHAPE_NAME_LEN 16
+#define ODP_ML_EXTRA_STAT_NAME_LEN 64
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/packet.h b/include/odp/api/abi-default/packet.h
new file mode 100644
index 000000000..033f50cea
--- /dev/null
+++ b/include/odp/api/abi-default/packet.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_PACKET_H_
+#define ODP_ABI_PACKET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the packet inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/packet_flags.h b/include/odp/api/abi-default/packet_flags.h
new file mode 100644
index 000000000..ee1e6ae11
--- /dev/null
+++ b/include/odp/api/abi-default/packet_flags.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_ABI_PACKET_FLAGS_H_
+#define ODP_ABI_PACKET_FLAGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/packet_io.h b/include/odp/api/abi-default/packet_io.h
new file mode 100644
index 000000000..f9b089778
--- /dev/null
+++ b/include/odp/api/abi-default/packet_io.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_PACKET_IO_H_
+#define ODP_ABI_PACKET_IO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the packet inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/packet_io_types.h b/include/odp/api/abi-default/packet_io_types.h
new file mode 100644
index 000000000..1aa1cf816
--- /dev/null
+++ b/include/odp/api/abi-default/packet_io_types.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO
+ */
+
+#ifndef ODP_ABI_PACKET_IO_TYPES_H_
+#define ODP_ABI_PACKET_IO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_pktio_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_lso_profile_t;
+
+/** @addtogroup odp_packet_io
+ * @{
+ */
+
+typedef _odp_abi_pktio_t *odp_pktio_t;
+typedef _odp_abi_lso_profile_t *odp_lso_profile_t;
+
+/** @internal */
+typedef struct odp_pktin_queue_t {
+ odp_pktio_t pktio; /**< @internal pktio handle */
+ int index; /**< @internal pktio queue index */
+} odp_pktin_queue_t;
+
+/** @internal */
+typedef struct odp_pktout_queue_t {
+ odp_pktio_t pktio; /**< @internal pktio handle */
+ int index; /**< @internal pktio queue index */
+} odp_pktout_queue_t;
+
+#define ODP_PKTIO_INVALID ((odp_pktio_t)0)
+#define ODP_LSO_PROFILE_INVALID ((odp_lso_profile_t)0)
+
+#define ODP_PKTIO_MAX_INDEX 63
+
+#define ODP_PKTIO_MACADDR_MAXSIZE 16
+
+#define ODP_PKTIN_NO_WAIT 0
+
+#define ODP_PKTIN_MAX_QUEUES 64
+
+#define ODP_PKTOUT_MAX_QUEUES 64
+
+#define ODP_PKTIO_STATS_EXTRA_NAME_LEN 64
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/packet_types.h b/include/odp/api/abi-default/packet_types.h
new file mode 100644
index 000000000..e8b2c8484
--- /dev/null
+++ b/include/odp/api/abi-default/packet_types.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ABI_PACKET_TYPES_H_
+#define ODP_ABI_PACKET_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_packet_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< *internal Dummy */ } _odp_abi_packet_seg_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< *internal Dummy */ } _odp_abi_packet_buf_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< *internal Dummy */ } _odp_abi_packet_vector_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< *internal Dummy */ } _odp_abi_packet_tx_compl_t;
+
+/** @addtogroup odp_packet
+ * @{
+ */
+
+typedef _odp_abi_packet_t *odp_packet_t;
+typedef _odp_abi_packet_seg_t *odp_packet_seg_t;
+typedef _odp_abi_packet_buf_t *odp_packet_buf_t;
+typedef _odp_abi_packet_vector_t *odp_packet_vector_t;
+typedef _odp_abi_packet_tx_compl_t *odp_packet_tx_compl_t;
+
+#define ODP_PACKET_INVALID ((odp_packet_t)0)
+#define ODP_PACKET_SEG_INVALID ((odp_packet_seg_t)0)
+#define ODP_PACKET_BUF_INVALID ((odp_packet_buf_t)0)
+#define ODP_PACKET_OFFSET_INVALID 0xffff
+#define ODP_PACKET_VECTOR_INVALID ((odp_packet_vector_t)0)
+#define ODP_PACKET_TX_COMPL_INVALID ((odp_packet_tx_compl_t)0)
+
+/** Packet Color */
+typedef enum {
+ ODP_PACKET_GREEN = 0,
+ ODP_PACKET_YELLOW = 1,
+ ODP_PACKET_RED = 2,
+ ODP_PACKET_ALL_COLORS = 3,
+} odp_packet_color_t;
+
+/** Packet Checksum Status */
+typedef enum {
+ ODP_PACKET_CHKSUM_UNKNOWN = 0,
+ ODP_PACKET_CHKSUM_BAD,
+ ODP_PACKET_CHKSUM_OK
+} odp_packet_chksum_status_t;
+
+/** Parse result flags */
+typedef struct odp_packet_parse_result_flag_t {
+ /** Flags union */
+ union {
+ /** All flags as a 64 bit word */
+ uint64_t all;
+
+ /** Flags as a bitfield struct */
+ struct {
+ /** See odp_packet_has_error() */
+ uint64_t has_error : 1;
+ /** See odp_packet_has_l2_error() */
+ uint64_t has_l2_error : 1;
+ /** See odp_packet_has_l3_error() */
+ uint64_t has_l3_error : 1;
+ /** See odp_packet_has_l4_error() */
+ uint64_t has_l4_error : 1;
+ /** See odp_packet_has_l2() */
+ uint64_t has_l2 : 1;
+ /** See odp_packet_has_l3() */
+ uint64_t has_l3 : 1;
+ /** See odp_packet_has_l4() */
+ uint64_t has_l4 : 1;
+ /** See odp_packet_has_eth() */
+ uint64_t has_eth : 1;
+ /** See odp_packet_has_eth_bcast() */
+ uint64_t has_eth_bcast : 1;
+ /** See odp_packet_has_eth_mcast() */
+ uint64_t has_eth_mcast : 1;
+ /** See odp_packet_has_jumbo() */
+ uint64_t has_jumbo : 1;
+ /** See odp_packet_has_vlan() */
+ uint64_t has_vlan : 1;
+ /** See odp_packet_has_vlan_qinq() */
+ uint64_t has_vlan_qinq : 1;
+ /** See odp_packet_has_arp() */
+ uint64_t has_arp : 1;
+ /** See odp_packet_has_ipv4() */
+ uint64_t has_ipv4 : 1;
+ /** See odp_packet_has_ipv6() */
+ uint64_t has_ipv6 : 1;
+ /** See odp_packet_has_ip_bcast() */
+ uint64_t has_ip_bcast : 1;
+ /** See odp_packet_has_ip_mcast() */
+ uint64_t has_ip_mcast : 1;
+ /** See odp_packet_has_ipfrag() */
+ uint64_t has_ipfrag : 1;
+ /** See odp_packet_has_ipopt() */
+ uint64_t has_ipopt : 1;
+ /** See odp_packet_has_ipsec() */
+ uint64_t has_ipsec : 1;
+ /** See odp_packet_has_udp() */
+ uint64_t has_udp : 1;
+ /** See odp_packet_has_tcp() */
+ uint64_t has_tcp : 1;
+ /** See odp_packet_has_sctp() */
+ uint64_t has_sctp : 1;
+ /** See odp_packet_has_icmp() */
+ uint64_t has_icmp : 1;
+ };
+ };
+
+} odp_packet_parse_result_flag_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/pool.h b/include/odp/api/abi-default/pool.h
new file mode 100644
index 000000000..dd2f66ac5
--- /dev/null
+++ b/include/odp/api/abi-default/pool.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_POOL_H_
+#define ODP_ABI_POOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the packet inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/pool_types.h b/include/odp/api/abi-default/pool_types.h
new file mode 100644
index 000000000..ce1042c12
--- /dev/null
+++ b/include/odp/api/abi-default/pool_types.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_POOL_TYPES_H_
+#define ODP_ABI_POOL_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_pool_t;
+
+/** @addtogroup odp_pool
+ * @{
+ */
+
+typedef _odp_abi_pool_t *odp_pool_t;
+
+#define ODP_POOL_INVALID ((odp_pool_t)0)
+
+#define ODP_POOL_NAME_LEN 32
+
+#define ODP_POOL_MAX_THREAD_STATS 128
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/proto_stats.h b/include/odp/api/abi-default/proto_stats.h
new file mode 100644
index 000000000..9b3147762
--- /dev/null
+++ b/include/odp/api/abi-default/proto_stats.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+#ifndef ODP_ABI_PROTO_STATS_H_
+#define ODP_ABI_PROTO_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required to enable API function inlining */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/proto_stats_types.h b/include/odp/api/abi-default/proto_stats_types.h
new file mode 100644
index 000000000..e17adf886
--- /dev/null
+++ b/include/odp/api/abi-default/proto_stats_types.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ABI_PROTO_STATS_TYPES_H_
+#define ODP_ABI_PROTO_STATS_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_proto_stats_t;
+
+/** @addtogroup odp_proto_stats
+ * @{
+ */
+
+typedef _odp_abi_proto_stats_t *odp_proto_stats_t;
+
+#define ODP_PROTO_STATS_INVALID ((odp_proto_stats_t)0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/queue.h b/include/odp/api/abi-default/queue.h
new file mode 100644
index 000000000..5ad307801
--- /dev/null
+++ b/include/odp/api/abi-default/queue.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_QUEUE_H_
+#define ODP_ABI_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the queue inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/queue_types.h b/include/odp/api/abi-default/queue_types.h
new file mode 100644
index 000000000..677348c18
--- /dev/null
+++ b/include/odp/api/abi-default/queue_types.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ABI_QUEUE_TYPES_H_
+#define ODP_ABI_QUEUE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_queue_t;
+
+/** @addtogroup odp_queue
+ * @{
+ */
+
+typedef _odp_abi_queue_t *odp_queue_t;
+
+#define ODP_QUEUE_INVALID ((odp_queue_t)0)
+
+#define ODP_QUEUE_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/random.h b/include/odp/api/abi-default/random.h
new file mode 100644
index 000000000..b99419155
--- /dev/null
+++ b/include/odp/api/abi-default/random.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_RANDOM_H_
+#define ODP_ABI_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the packet inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/rwlock.h b/include/odp/api/abi-default/rwlock.h
new file mode 100644
index 000000000..300108e67
--- /dev/null
+++ b/include/odp/api/abi-default/rwlock.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP rwlock
+ */
+
+#ifndef ODP_ABI_RWLOCK_H_
+#define ODP_ABI_RWLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/atomic.h>
+
+/** @internal */
+typedef struct odp_rwlock_s {
+ odp_atomic_u32_t cnt; /**< lock count
+ 0 lock not taken
+ -1 write lock taken
+ >0 read lock(s) taken */
+} odp_rwlock_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/rwlock_recursive.h b/include/odp/api/abi-default/rwlock_recursive.h
new file mode 100644
index 000000000..eb5c000c0
--- /dev/null
+++ b/include/odp/api/abi-default/rwlock_recursive.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP recursive read/write lock
+ */
+
+#ifndef ODP_ABI_RWLOCK_RECURSIVE_H_
+#define ODP_ABI_RWLOCK_RECURSIVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/rwlock.h>
+#include <odp/api/std_types.h>
+#include <odp/api/thread.h>
+
+/** @internal */
+typedef struct odp_rwlock_recursive_s {
+ odp_rwlock_t lock; /**< the lock */
+ int wr_owner; /**< write owner thread */
+ uint32_t wr_cnt; /**< write recursion count */
+ uint8_t rd_cnt[ODP_THREAD_COUNT_MAX]; /**< read recursion count */
+} odp_rwlock_recursive_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/schedule.h b/include/odp/api/abi-default/schedule.h
new file mode 100644
index 000000000..73c51a7c6
--- /dev/null
+++ b/include/odp/api/abi-default/schedule.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_SCHEDULE_H_
+#define ODP_ABI_SCHEDULE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the schedule inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/schedule_types.h b/include/odp/api/abi-default/schedule_types.h
new file mode 100644
index 000000000..eeb18771e
--- /dev/null
+++ b/include/odp/api/abi-default/schedule_types.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP schedule
+ */
+
+#ifndef ODP_ABI_SCHEDULE_TYPES_H_
+#define ODP_ABI_SCHEDULE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @addtogroup odp_scheduler
+ * @{
+ */
+
+#define ODP_SCHED_WAIT UINT64_MAX
+#define ODP_SCHED_NO_WAIT 0
+
+#define ODP_SCHED_GROUP_NAME_LEN 32
+
+typedef int odp_schedule_sync_t;
+
+#define ODP_SCHED_SYNC_PARALLEL 0
+#define ODP_SCHED_SYNC_ATOMIC 1
+#define ODP_SCHED_SYNC_ORDERED 2
+
+typedef int odp_schedule_group_t;
+
+/* These must be kept in sync with thread_globals_t in odp_thread.c */
+#define ODP_SCHED_GROUP_INVALID ((odp_schedule_group_t)-1)
+#define ODP_SCHED_GROUP_ALL 0
+#define ODP_SCHED_GROUP_WORKER 1
+#define ODP_SCHED_GROUP_CONTROL 2
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/shared_memory.h b/include/odp/api/abi-default/shared_memory.h
new file mode 100644
index 000000000..70d6e906f
--- /dev/null
+++ b/include/odp/api/abi-default/shared_memory.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_SHM_H_
+#define ODP_ABI_SHM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_shm_t;
+
+/** @addtogroup odp_shared_memory
+ * @{
+ */
+
+typedef _odp_abi_shm_t *odp_shm_t;
+
+#define ODP_SHM_INVALID ((odp_shm_t)0)
+#define ODP_SHM_NAME_LEN 32
+
+#define ODP_SHM_IOVA_INVALID ((uint64_t)-1)
+#define ODP_SHM_PA_INVALID ODP_SHM_IOVA_INVALID
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/spinlock.h b/include/odp/api/abi-default/spinlock.h
new file mode 100644
index 000000000..68f8aa8aa
--- /dev/null
+++ b/include/odp/api/abi-default/spinlock.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP spinlock
+ */
+
+#ifndef ODP_ABI_SPINLOCK_H_
+#define ODP_ABI_SPINLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal */
+typedef struct odp_spinlock_s {
+ char lock; /**< lock flag, should match odp_atomic_flag_t */
+} odp_spinlock_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/spinlock_recursive.h b/include/odp/api/abi-default/spinlock_recursive.h
new file mode 100644
index 000000000..6f8068033
--- /dev/null
+++ b/include/odp/api/abi-default/spinlock_recursive.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP recursive spinlock
+ */
+
+#ifndef ODP_ABI_SPINLOCK_RECURSIVE_H_
+#define ODP_ABI_SPINLOCK_RECURSIVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spinlock.h>
+#include <odp/api/std_types.h>
+
+/** @internal */
+typedef struct odp_spinlock_recursive_s {
+ odp_spinlock_t lock; /**< the lock */
+ int owner; /**< thread owning the lock */
+ uint32_t cnt; /**< recursion count */
+} odp_spinlock_recursive_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/stash.h b/include/odp/api/abi-default/stash.h
new file mode 100644
index 000000000..ec9316e68
--- /dev/null
+++ b/include/odp/api/abi-default/stash.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef ODP_ABI_STASH_H_
+#define ODP_ABI_STASH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/stash_types.h b/include/odp/api/abi-default/stash_types.h
new file mode 100644
index 000000000..6779f3af6
--- /dev/null
+++ b/include/odp/api/abi-default/stash_types.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_STASH_TYPES_H_
+#define ODP_ABI_STASH_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_stash_t;
+
+/** @addtogroup odp_stash
+ * @{
+ */
+
+typedef _odp_abi_stash_t *odp_stash_t;
+
+#define ODP_STASH_INVALID ((odp_stash_t)0)
+
+#define ODP_STASH_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/std.h b/include/odp/api/abi-default/std.h
new file mode 100644
index 000000000..7de653653
--- /dev/null
+++ b/include/odp/api/abi-default/std.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_STD_H_
+#define ODP_ABI_STD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/std_types.h b/include/odp/api/abi-default/std_types.h
new file mode 100644
index 000000000..cb8c4230f
--- /dev/null
+++ b/include/odp/api/abi-default/std_types.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_STD_TYPES_H_
+#define ODP_ABI_STD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* uint64_t, uint32_t, etc */
+#include <stdint.h>
+
+/* size_t */
+#include <stddef.h>
+
+/* true and false for odp_bool_t */
+#include <stdbool.h>
+
+/** @addtogroup odp_std
+ * @{
+ */
+
+typedef int odp_bool_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/sync.h b/include/odp/api/abi-default/sync.h
new file mode 100644
index 000000000..862081a50
--- /dev/null
+++ b/include/odp/api/abi-default/sync.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP barrier
+ */
+
+#ifndef ODP_ABI_SYNC_H_
+#define ODP_ABI_SYNC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/thread.h b/include/odp/api/abi-default/thread.h
new file mode 100644
index 000000000..3b7ce41dc
--- /dev/null
+++ b/include/odp/api/abi-default/thread.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_THREAD_H_
+#define ODP_ABI_THREAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/thread_types.h b/include/odp/api/abi-default/thread_types.h
new file mode 100644
index 000000000..d8c27fb98
--- /dev/null
+++ b/include/odp/api/abi-default/thread_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ABI_THREAD_TYPES_H_
+#define ODP_ABI_THREAD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @addtogroup odp_thread
+ * @{
+ */
+
+#define ODP_THREAD_COUNT_MAX 256
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/thrmask.h b/include/odp/api/abi-default/thrmask.h
new file mode 100644
index 000000000..a5aff670b
--- /dev/null
+++ b/include/odp/api/abi-default/thrmask.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP thread masks
+ */
+
+#ifndef ODP_ABI_THRMASK_H_
+#define ODP_ABI_THRMASK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @addtogroup odp_thread
+ * @{
+ */
+
+#include <odp/api/cpumask.h>
+
+/**
+ * Minimum size of output buffer for odp_thrmask_to_str()
+ */
+#define ODP_THRMASK_STR_SIZE ODP_CPUMASK_STR_SIZE
+
+/**
+ * Thread mask
+ *
+ * Don't access directly, use access functions.
+ */
+typedef struct odp_thrmask_t {
+ odp_cpumask_t m; /**< @private Mask*/
+} odp_thrmask_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/ticketlock.h b/include/odp/api/abi-default/ticketlock.h
new file mode 100644
index 000000000..d8489ad9d
--- /dev/null
+++ b/include/odp/api/abi-default/ticketlock.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP ticketlock
+ */
+
+#ifndef ODP_ABI_TICKETLOCK_H_
+#define ODP_ABI_TICKETLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/atomic.h>
+
+/** @internal */
+typedef struct odp_ticketlock_s {
+ odp_atomic_u32_t next_ticket; /**< Next ticket */
+ odp_atomic_u32_t cur_ticket; /**< Current ticket */
+} odp_ticketlock_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/time.h b/include/odp/api/abi-default/time.h
new file mode 100644
index 000000000..e8af62c0f
--- /dev/null
+++ b/include/odp/api/abi-default/time.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ABI_TIME_H_
+#define ODP_ABI_TIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/time_types.h b/include/odp/api/abi-default/time_types.h
new file mode 100644
index 000000000..afbe6d188
--- /dev/null
+++ b/include/odp/api/abi-default/time_types.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_TIME_TYPES_H_
+#define ODP_ABI_TIME_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @addtogroup odp_time
+ * @{
+ **/
+
+/**
+ * @internal Time structure used for both POSIX timespec and HW counter
+ * implementations.
+ */
+typedef struct odp_time_t {
+ /** @internal Variant mappings for time type */
+ union {
+ /** @internal Used with generic 64 bit operations */
+ uint64_t u64;
+
+ /** @internal Nanoseconds */
+ uint64_t nsec;
+
+ /** @internal HW timer counter value */
+ uint64_t count;
+
+ };
+} odp_time_t;
+
+#define ODP_TIME_NULL ((odp_time_t){.u64 = 0})
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/timer.h b/include/odp/api/abi-default/timer.h
new file mode 100644
index 000000000..39b71c6a3
--- /dev/null
+++ b/include/odp/api/abi-default/timer.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_ABI_TIMER_H_
+#define ODP_ABI_TIMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty header required due to the timer inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/timer_types.h b/include/odp/api/abi-default/timer_types.h
new file mode 100644
index 000000000..89b237fdb
--- /dev/null
+++ b/include/odp/api/abi-default/timer_types.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP timer service
+ */
+
+#ifndef ODP_ABI_TIMER_TYPES_H_
+#define ODP_ABI_TIMER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_timer_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_timeout_t;
+
+/** @internal Dummy type for strong typing */
+typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_timer_pool_t;
+
+/** @addtogroup odp_timer
+ * @{
+ **/
+
+typedef _odp_abi_timer_pool_t *odp_timer_pool_t;
+
+#define ODP_TIMER_POOL_INVALID ((odp_timer_pool_t)0)
+
+#define ODP_TIMER_POOL_NAME_LEN 32
+
+typedef _odp_abi_timer_t *odp_timer_t;
+
+#define ODP_TIMER_INVALID ((odp_timer_t)0)
+
+typedef _odp_abi_timeout_t *odp_timeout_t;
+
+#define ODP_TIMEOUT_INVALID ((odp_timeout_t)0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/traffic_mngr.h b/include/odp/api/abi-default/traffic_mngr.h
new file mode 100644
index 000000000..32a0ab473
--- /dev/null
+++ b/include/odp/api/abi-default/traffic_mngr.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP traffic mngr
+ */
+
+#ifndef ODP_ABI_TRAFFIC_MNGR_H_
+#define ODP_ABI_TRAFFIC_MNGR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @addtogroup odp_traffic_mngr
+ * Macros and operations on a TM system.
+ * @{
+ */
+
+/** The ODP_TM_MAX_NUM_SYSTEMS constant specifies the maximum number of TM
+ * systems that may be created. On some platforms this might be much more
+ * limited to as little as one hardware TM system.
+ */
+#define ODP_TM_MAX_NUM_SYSTEMS 8
+
+/** The ODP_TM_MAX_PRIORITIES constant specifies the largest range of
+ * priorities that any TM system can support. All strict priority values MUST
+ * in the range 0..ODP_TM_MAX_PRIORITIES-1.
+ */
+#define ODP_TM_MAX_PRIORITIES 16
+
+/** The ODP_TM MAX_LEVELS constant specifies the largest range of
+ * tm_node levels that any TM system can support. Hence all tm_node level
+ * values MUST be in the range 0..ODP_TM_MAX_LEVELS-1. Smaller tm_node
+ * levels are associated with tm_nodes closer to the TM system egress.
+ */
+#define ODP_TM_MAX_LEVELS 8
+
+/**
+ * The smallest SCHED weight is 1 (i.e. 0 is not a legal WFQ/WRR value).
+ */
+#define ODP_TM_MIN_SCHED_WEIGHT 1U
+
+/** The ODP_TM_MAX_SCHED_WEIGHT constant is the largest weight any TM system
+ * can support (at least from a configuration standpoint). A given TM system
+ * could have a smaller value.
+ */
+#define ODP_TM_MAX_SCHED_WEIGHT 255U
+
+/** The ODP_TM_MAX_TM_QUEUES constant is the largest number of tm_queues
+ * that can be handled by any one TM system.
+ */
+#define ODP_TM_MAX_TM_QUEUES (4 * 1024)
+
+/** The ODP_TM_MAX_NUM_OUTPUTS constant is the largest number of outputs that
+ * can be configured for any one TM system.
+ */
+#define ODP_TM_MAX_NUM_OUTPUTS 256
+
+/** The ODP_TM_MAX_NUM_TM_NODES constant is the largest number of tm_nodes that
+ * can be in existence for any one TM system.
+ */
+#define ODP_TM_MAX_NUM_TM_NODES (4 * 1024)
+
+/** The ODP_TM_MAX_TM_NODE_FANIN constant is the largest number of fan-in
+ * "inputs" that can be simultaneously connected to a single tm_node.
+ * *TBD* Does this need to be as large as ODP_TM_MAX_TM_QUEUES? *TBD*
+ */
+#define ODP_TM_MAX_TM_NODE_FANIN (4 * 1024)
+
+/** The INVALID_PRIORITY constant is used when one needs to indicate an
+ * invalid priority value.
+ */
+#define ODP_TM_INVALID_PRIORITY 255
+
+/** The odp_tm_percent_t type is used when specifying fields that are
+ * percentages. It is a fixed point integer whose units are 1/100 of a
+ * percent. Hence 100% is represented as the integer value 10000. Note
+ * that because it is often used as a ratio of the current queue value and
+ * maximum queue threshold, it can be > 100%, but in any event will never
+ * be larger than 500% (i.e. it MUST be capped at 50000).
+ */
+typedef uint16_t odp_tm_percent_t;
+
+/** The odp_tm_handle_t type is a generic type that can stand for any of the
+ * other ODP_TM handle types.
+ */
+typedef uint64_t odp_tm_handle_t;
+
+/** Each odp_tm_t value represents a specific TM system. Almost all
+ * functions in this API require a odp_tm_t value - either directly
+ * as a function parameter or indirectly by having another ODP TM handle value
+ * as a function parameter.
+ */
+typedef odp_tm_handle_t odp_tm_t;
+
+/** Each odp_tm_queue_t value is an opaque ODP handle representing a specific
+ * tm_queue within a specific TM system.
+ */
+typedef odp_tm_handle_t odp_tm_queue_t;
+
+/** Each odp_tm_node_t value is an opaque ODP handle representing a specific
+ * tm_node within a specific TM system.
+ */
+typedef odp_tm_handle_t odp_tm_node_t;
+
+/** Each odp_tm_shaper_t value is an opaque ODP handle representing a specific
+ * shaper profile usable across all TM systems described by this API. A given
+ * shaper profile can then be attached to any tm_queue or tm_node.
+ */
+typedef odp_tm_handle_t odp_tm_shaper_t;
+
+/** Each odp_tm_sched_t value is an opaque ODP handle representing a specific
+ * tm_node scheduler profile usable across all TM systems described by this
+ * API. A given tm_node scheduler profile can then be attached to any tm_node.
+ */
+typedef odp_tm_handle_t odp_tm_sched_t;
+
+/** Each odp_tm_threshold_t value is an opaque ODP handle representing a
+ * specific queue threshold profile usable across all TM systems described by
+ * this API. A given queue threshold profile can then be attached to any
+ * tm_queue or tm_node.
+ */
+typedef odp_tm_handle_t odp_tm_threshold_t;
+
+/** Each odp_tm_wred_t value is an opaque ODP handle representing a specific
+ * WRED profile usable across all TM systems described by this API. A given
+ * WRED profile can then be attached to any tm_queue or tm_node.
+ */
+typedef odp_tm_handle_t odp_tm_wred_t;
+
+/** The ODP_TM_INVALID constant can be used with any ODP TM handle type and
+ * indicates that this value does NOT represent a valid TM object.
+ */
+#define ODP_TM_INVALID 0
+
+/**
+ * @def ODP_TM_ROOT
+ * Constant that is used to refer to the egress/root node of the TM subsystem's
+ * tree/hierarchy of nodes.
+ */
+#define ODP_TM_ROOT ((odp_tm_handle_t)-1)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/abi-default/version.h b/include/odp/api/abi-default/version.h
new file mode 100644
index 000000000..f15058623
--- /dev/null
+++ b/include/odp/api/abi-default/version.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_VERSION_H_
+#define ODP_ABI_VERSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Version string expand */
+#define ODP_VERSION_STR_EXPAND(x) #x
+
+/** @internal Version to string */
+#define ODP_VERSION_TO_STR(x) ODP_VERSION_STR_EXPAND(x)
+
+/** @internal API version string */
+#define ODP_VERSION_API_STR \
+ODP_VERSION_TO_STR(ODP_VERSION_API_GENERATION) "." \
+ODP_VERSION_TO_STR(ODP_VERSION_API_MAJOR) "." \
+ODP_VERSION_TO_STR(ODP_VERSION_API_MINOR)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/align.h b/include/odp/api/align.h
new file mode 100644
index 000000000..c003b714d
--- /dev/null
+++ b/include/odp/api/align.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP alignments
+ */
+
+#ifndef ODP_API_ALIGN_H_
+#define ODP_API_ALIGN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/align.h>
+
+#include <odp/api/spec/align.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/atomic.h b/include/odp/api/atomic.h
new file mode 100644
index 000000000..7f79256e8
--- /dev/null
+++ b/include/odp/api/atomic.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP atomic operations
+ */
+
+#ifndef ODP_API_ATOMIC_H_
+#define ODP_API_ATOMIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/atomic.h>
+
+#include <odp/api/spec/atomic.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/barrier.h b/include/odp/api/barrier.h
new file mode 100644
index 000000000..768b66c0d
--- /dev/null
+++ b/include/odp/api/barrier.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP execution barriers
+ */
+
+#ifndef ODP_API_BARRIER_H_
+#define ODP_API_BARRIER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/atomic.h>
+#include <odp/api/abi/shared_memory.h>
+#include <odp/api/abi/barrier.h>
+
+#include <odp/api/spec/barrier.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/buffer.h b/include/odp/api/buffer.h
new file mode 100644
index 000000000..13a31d169
--- /dev/null
+++ b/include/odp/api/buffer.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP buffer descriptor
+ */
+
+#ifndef ODP_API_BUFFER_H_
+#define ODP_API_BUFFER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/buffer.h>
+
+#include <odp/api/spec/buffer.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/buffer_types.h b/include/odp/api/buffer_types.h
new file mode 100644
index 000000000..73d2294be
--- /dev/null
+++ b/include/odp/api/buffer_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP buffer types
+ */
+
+#ifndef ODP_API_BUFFER_TYPES_H_
+#define ODP_API_BUFFER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/buffer_types.h>
+
+#include <odp/api/spec/buffer_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/byteorder.h b/include/odp/api/byteorder.h
new file mode 100644
index 000000000..39857381f
--- /dev/null
+++ b/include/odp/api/byteorder.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP byteorder
+ */
+
+#ifndef ODP_API_BYTEORDER_H_
+#define ODP_API_BYTEORDER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/byteorder.h>
+
+#include <odp/api/spec/byteorder.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/chksum.h b/include/odp/api/chksum.h
new file mode 100644
index 000000000..39cb08f7c
--- /dev/null
+++ b/include/odp/api/chksum.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP checksum functions
+ */
+
+#ifndef ODP_API_CHKSUM_H_
+#define ODP_API_CHKSUM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/chksum.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/classification.h b/include/odp/api/classification.h
new file mode 100644
index 000000000..5a14a9e79
--- /dev/null
+++ b/include/odp/api/classification.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP classification descriptor
+ */
+
+#ifndef ODP_API_CLASSIFICATION_H_
+#define ODP_API_CLASSIFICATION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/abi/classification.h>
+#include <odp/api/abi/packet_types.h>
+#include <odp/api/abi/queue_types.h>
+
+#include <odp/api/spec/classification.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/comp.h b/include/odp/api/comp.h
new file mode 100644
index 000000000..fa14d4631
--- /dev/null
+++ b/include/odp/api/comp.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_API_COMP_H_
+#define ODP_API_COMP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/comp.h>
+
+#include <odp/api/spec/comp.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/cpu.h b/include/odp/api/cpu.h
new file mode 100644
index 000000000..43729b5ab
--- /dev/null
+++ b/include/odp/api/cpu.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP CPU
+ */
+
+#ifndef ODP_API_CPU_H_
+#define ODP_API_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/cpu.h>
+
+/* Thread inline file implements cpu API function */
+#include <odp/api/thread.h>
+#include <odp/api/spec/cpu.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/cpumask.h b/include/odp/api/cpumask.h
new file mode 100644
index 000000000..662888288
--- /dev/null
+++ b/include/odp/api/cpumask.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP CPU masks and enumeration
+ */
+
+#ifndef ODP_API_CPUMASK_H_
+#define ODP_API_CPUMASK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/cpumask.h>
+
+#include <odp/api/spec/cpumask.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/crypto.h b/include/odp/api/crypto.h
new file mode 100644
index 000000000..ce9d0f26a
--- /dev/null
+++ b/include/odp/api/crypto.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_API_CRYPTO_H_
+#define ODP_API_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/crypto.h>
+
+#include <odp/api/spec/crypto.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/crypto_types.h b/include/odp/api/crypto_types.h
new file mode 100644
index 000000000..922410c11
--- /dev/null
+++ b/include/odp/api/crypto_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_API_CRYPTO_TYPES_H_
+#define ODP_API_CRYPTO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/crypto_types.h>
+
+#include <odp/api/spec/crypto_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/debug.h b/include/odp/api/debug.h
new file mode 100644
index 000000000..a9eccbc43
--- /dev/null
+++ b/include/odp/api/debug.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP debug
+ */
+
+#ifndef ODP_API_DEBUG_H_
+#define ODP_API_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/debug.h>
+
+#include <odp/api/spec/debug.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/deprecated.h b/include/odp/api/deprecated.h
new file mode 100644
index 000000000..afa2dd4e7
--- /dev/null
+++ b/include/odp/api/deprecated.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * Control deprecated API definitions
+ */
+
+#ifndef ODP_API_DEPRECATED_H_
+#define ODP_API_DEPRECATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/deprecated.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/dma.h b/include/odp/api/dma.h
new file mode 100644
index 000000000..20f079859
--- /dev/null
+++ b/include/odp/api/dma.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP DMA
+ */
+
+#ifndef ODP_API_DMA_H_
+#define ODP_API_DMA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/dma.h>
+
+#include <odp/api/spec/dma.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/dma_types.h b/include/odp/api/dma_types.h
new file mode 100644
index 000000000..c6aedcd49
--- /dev/null
+++ b/include/odp/api/dma_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP DMA
+ */
+
+#ifndef ODP_API_DMA_TYPES_H_
+#define ODP_API_DMA_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/dma_types.h>
+
+#include <odp/api/spec/dma_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/errno.h b/include/odp/api/errno.h
new file mode 100644
index 000000000..4b37dbf60
--- /dev/null
+++ b/include/odp/api/errno.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP errno API
+ */
+
+#ifndef ODP_API_ERRNO_H_
+#define ODP_API_ERRNO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/errno.h>
+
+#include <odp/api/spec/errno.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/event.h b/include/odp/api/event.h
new file mode 100644
index 000000000..26fb9b97d
--- /dev/null
+++ b/include/odp/api/event.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP event
+ */
+
+#ifndef ODP_API_EVENT_H_
+#define ODP_API_EVENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/event.h>
+
+#include <odp/api/spec/event.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/event_types.h b/include/odp/api/event_types.h
new file mode 100644
index 000000000..8254583d7
--- /dev/null
+++ b/include/odp/api/event_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event API type definitions
+ */
+
+#ifndef ODP_API_EVENT_TYPES_H_
+#define ODP_API_EVENT_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/event_types.h>
+
+#include <odp/api/spec/event_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/hash.h b/include/odp/api/hash.h
new file mode 100644
index 000000000..135fd2aef
--- /dev/null
+++ b/include/odp/api/hash.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP Hash function
+ */
+
+#ifndef ODP_API_HASH_H_
+#define ODP_API_HASH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/hash.h>
+
+#include <odp/api/spec/hash.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/hints.h b/include/odp/api/hints.h
new file mode 100644
index 000000000..bcd2780d1
--- /dev/null
+++ b/include/odp/api/hints.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP compiler hints
+ */
+
+#ifndef ODP_API_HINTS_H_
+#define ODP_API_HINTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/hints.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/init.h b/include/odp/api/init.h
new file mode 100644
index 000000000..259c6f765
--- /dev/null
+++ b/include/odp/api/init.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP initialization.
+ */
+
+#ifndef ODP_API_INIT_H_
+#define ODP_API_INIT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/init.h>
+
+#include <odp/api/spec/init.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ipsec.h b/include/odp/api/ipsec.h
new file mode 100644
index 000000000..b61edaa6a
--- /dev/null
+++ b/include/odp/api/ipsec.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP IPSEC API - platform specific header
+ */
+
+#ifndef ODP_API_IPSEC_H_
+#define ODP_API_IPSEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/ipsec.h>
+
+#include <odp/api/spec/ipsec.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ipsec_types.h b/include/odp/api/ipsec_types.h
new file mode 100644
index 000000000..ddbf04ed1
--- /dev/null
+++ b/include/odp/api/ipsec_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP IPsec
+ */
+
+#ifndef ODP_API_IPSEC_TYPES_H_
+#define ODP_API_IPSEC_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/ipsec_types.h>
+
+#include <odp/api/spec/ipsec_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ml.h b/include/odp/api/ml.h
new file mode 100644
index 000000000..55213dd52
--- /dev/null
+++ b/include/odp/api/ml.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) offload
+ */
+
+#ifndef ODP_API_ML_H_
+#define ODP_API_ML_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/ml.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ml_quantize.h b/include/odp/api/ml_quantize.h
new file mode 100644
index 000000000..cab2a3f22
--- /dev/null
+++ b/include/odp/api/ml_quantize.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) offload
+ */
+
+#ifndef ODP_API_ML_QUANTIZE_H_
+#define ODP_API_ML_QUANTIZE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/ml_quantize.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ml_types.h b/include/odp/api/ml_types.h
new file mode 100644
index 000000000..3c3f8a416
--- /dev/null
+++ b/include/odp/api/ml_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) types
+ */
+
+#ifndef ODP_API_ML_TYPES_H_
+#define ODP_API_ML_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/ml_types.h>
+
+#include <odp/api/spec/ml_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet.h b/include/odp/api/packet.h
new file mode 100644
index 000000000..e5733595f
--- /dev/null
+++ b/include/odp/api/packet.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_API_PACKET_H_
+#define ODP_API_PACKET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet.h>
+
+#include <odp/api/spec/packet.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet_flags.h b/include/odp/api/packet_flags.h
new file mode 100644
index 000000000..15e085270
--- /dev/null
+++ b/include/odp/api/packet_flags.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP packet flags
+ */
+
+#ifndef ODP_API_PACKET_FLAGS_H_
+#define ODP_API_PACKET_FLAGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_flags.h>
+
+#include <odp/api/spec/packet_flags.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet_io.h b/include/odp/api/packet_io.h
new file mode 100644
index 000000000..5f3e9d3e3
--- /dev/null
+++ b/include/odp/api/packet_io.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO
+ */
+
+#ifndef ODP_API_PACKET_IO_H_
+#define ODP_API_PACKET_IO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_io.h>
+
+#include <odp/api/spec/packet_io.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet_io_stats.h b/include/odp/api/packet_io_stats.h
new file mode 100644
index 000000000..fa16ae290
--- /dev/null
+++ b/include/odp/api/packet_io_stats.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP packet IO stats
+ */
+
+#ifndef ODP_API_PACKET_IO_STATS_H_
+#define ODP_API_PACKET_IO_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_io_types.h>
+
+#include <odp/api/spec/packet_io_stats.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet_io_types.h b/include/odp/api/packet_io_types.h
new file mode 100644
index 000000000..c5fd72e96
--- /dev/null
+++ b/include/odp/api/packet_io_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO
+ */
+
+#ifndef ODP_API_PACKET_IO_TYPES_H_
+#define ODP_API_PACKET_IO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_io_types.h>
+
+#include <odp/api/spec/packet_io_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/packet_types.h b/include/odp/api/packet_types.h
new file mode 100644
index 000000000..647810259
--- /dev/null
+++ b/include/odp/api/packet_types.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP packet
+ */
+
+#ifndef ODP_API_PACKET_TYPES_H_
+#define ODP_API_PACKET_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_io_types.h>
+#include <odp/api/abi/packet_types.h>
+
+#include <odp/api/spec/packet_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/pool.h b/include/odp/api/pool.h
new file mode 100644
index 000000000..0f4dfab27
--- /dev/null
+++ b/include/odp/api/pool.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP pool
+ */
+
+#ifndef ODP_API_POOL_H_
+#define ODP_API_POOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/pool.h>
+
+#include <odp/api/spec/pool.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/pool_types.h b/include/odp/api/pool_types.h
new file mode 100644
index 000000000..d2b88c332
--- /dev/null
+++ b/include/odp/api/pool_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP pool
+ */
+
+#ifndef ODP_API_POOL_TYPES_H_
+#define ODP_API_POOL_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/pool_types.h>
+
+#include <odp/api/spec/pool_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/proto_stats.h b/include/odp/api/proto_stats.h
new file mode 100644
index 000000000..2f16dfbf0
--- /dev/null
+++ b/include/odp/api/proto_stats.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP proto stats
+ */
+
+#ifndef ODP_API_PROTO_STATS_H_
+#define ODP_API_PROTO_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/abi/queue.h>
+#include <odp/api/abi/proto_stats_types.h>
+#include <odp/api/abi/proto_stats.h>
+
+#include <odp/api/spec/proto_stats.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/proto_stats_types.h b/include/odp/api/proto_stats_types.h
new file mode 100644
index 000000000..c6bae0842
--- /dev/null
+++ b/include/odp/api/proto_stats_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP proto stats types
+ */
+
+#ifndef ODP_API_PROTO_STATS_TYPES_H_
+#define ODP_API_PROTO_STATS_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/proto_stats_types.h>
+
+#include <odp/api/spec/proto_stats_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/protocols.h b/include/odp/api/protocols.h
new file mode 100644
index 000000000..232e96904
--- /dev/null
+++ b/include/odp/api/protocols.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP protocols
+ */
+
+#ifndef ODP_API_PROTOCOLS_H_
+#define ODP_API_PROTOCOLS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/protocols.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/queue.h b/include/odp/api/queue.h
new file mode 100644
index 000000000..b3728f1ab
--- /dev/null
+++ b/include/odp/api/queue.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP queue
+ */
+
+#ifndef ODP_API_QUEUE_H_
+#define ODP_API_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/queue.h>
+
+#include <odp/api/spec/queue.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/queue_types.h b/include/odp/api/queue_types.h
new file mode 100644
index 000000000..3aefdb3d0
--- /dev/null
+++ b/include/odp/api/queue_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP queue
+ */
+
+#ifndef ODP_API_QUEUE_TYPES_H_
+#define ODP_API_QUEUE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/queue_types.h>
+
+#include <odp/api/spec/queue_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/random.h b/include/odp/api/random.h
new file mode 100644
index 000000000..27cd593b0
--- /dev/null
+++ b/include/odp/api/random.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP random number API
+ */
+
+#ifndef ODP_API_RANDOM_H_
+#define ODP_API_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/random.h>
+
+#include <odp/api/spec/random.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/random_types.h b/include/odp/api/random_types.h
new file mode 100644
index 000000000..2b90f61ee
--- /dev/null
+++ b/include/odp/api/random_types.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP random number API
+ */
+
+#ifndef ODP_API_RANDOM_TYPES_H_
+#define ODP_API_RANDOM_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/random_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/reassembly.h b/include/odp/api/reassembly.h
new file mode 100644
index 000000000..2d52f6e15
--- /dev/null
+++ b/include/odp/api/reassembly.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP REASSEMBLY API - platform specific header
+ */
+
+#ifndef ODP_API_REASSEMBLY_H_
+#define ODP_API_REASSEMBLY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/reassembly.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/rwlock.h b/include/odp/api/rwlock.h
new file mode 100644
index 000000000..ca852a5be
--- /dev/null
+++ b/include/odp/api/rwlock.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP RW Locks
+ */
+
+#ifndef ODP_API_RWLOCK_H_
+#define ODP_API_RWLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/rwlock.h>
+
+#include <odp/api/spec/rwlock.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_RWLOCK_H_ */
diff --git a/include/odp/api/rwlock_recursive.h b/include/odp/api/rwlock_recursive.h
new file mode 100644
index 000000000..288975476
--- /dev/null
+++ b/include/odp/api/rwlock_recursive.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP recursive read/write lock
+ */
+
+#ifndef ODP_API_RWLOCK_RECURSIVE_H_
+#define ODP_API_RWLOCK_RECURSIVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/rwlock_recursive.h>
+
+#include <odp/api/spec/rwlock_recursive.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/schedule.h b/include/odp/api/schedule.h
new file mode 100644
index 000000000..1a0e31276
--- /dev/null
+++ b/include/odp/api/schedule.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP schedule
+ */
+
+#ifndef ODP_API_SCHEDULE_H_
+#define ODP_API_SCHEDULE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/schedule.h>
+
+#include <odp/api/spec/schedule.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/schedule_types.h b/include/odp/api/schedule_types.h
new file mode 100644
index 000000000..1b415b578
--- /dev/null
+++ b/include/odp/api/schedule_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP schedule
+ */
+
+#ifndef ODP_API_SCHEDULE_TYPES_H_
+#define ODP_API_SCHEDULE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/schedule_types.h>
+
+#include <odp/api/spec/schedule_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/shared_memory.h b/include/odp/api/shared_memory.h
new file mode 100644
index 000000000..a08f84cc4
--- /dev/null
+++ b/include/odp/api/shared_memory.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP shared memory
+ */
+
+#ifndef ODP_API_SHARED_MEMORY_H_
+#define ODP_API_SHARED_MEMORY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/shared_memory.h>
+
+#include <odp/api/spec/shared_memory.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/spec/align.h b/include/odp/api/spec/align.h
index fdf8c29e1..d5e5910aa 100644
--- a/include/odp/api/spec/align.h
+++ b/include/odp/api/spec/align.h
@@ -1,18 +1,15 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP alignments
*/
-#ifndef ODP_API_ALIGN_H_
-#define ODP_API_ALIGN_H_
+#ifndef ODP_API_SPEC_ALIGN_H_
+#define ODP_API_SPEC_ALIGN_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -49,12 +46,12 @@ extern "C" {
/**
* @def ODP_CACHE_LINE_SIZE
- * Cache line size
+ * Cache line size in bytes
*/
/**
* @def ODP_PAGE_SIZE
- * Page size
+ * Page size in bytes
*/
/**
@@ -68,6 +65,15 @@ extern "C" {
*/
/**
+ * @def ODP_CACHE_LINE_ROUNDUP
+ * Round up to cache line size
+ *
+ * Rounds up the passed value to the next multiple of cache line size
+ * (ODP_CACHE_LINE_SIZE). Returns the original value if it is already
+ * a multiple of cache line size or zero.
+ */
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/atomic.h b/include/odp/api/spec/atomic.h
index 408829df2..3a098ead1 100644
--- a/include/odp/api/spec/atomic.h
+++ b/include/odp/api/spec/atomic.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
*/
/**
@@ -10,8 +9,8 @@
* ODP atomic operations
*/
-#ifndef ODP_API_ATOMIC_H_
-#define ODP_API_ATOMIC_H_
+#ifndef ODP_API_SPEC_ATOMIC_H_
+#define ODP_API_SPEC_ATOMIC_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,16 +19,18 @@ extern "C" {
/**
* @defgroup odp_atomic ODP ATOMIC
+ * Atomic variables.
+ *
* @details
* <b> Atomic integers using relaxed memory ordering </b>
*
- * Atomic integer types (odp_atomic_u32_t and odp_atomic_u64_t) can be used to
- * implement e.g. shared counters. If not otherwise documented, operations in
- * this API are implemented using <b> RELAXED memory ordering </b> (see memory
- * order descriptions in the C11 specification). Relaxed operations do not
- * provide synchronization or ordering for other memory accesses (initiated
- * before or after the operation), only atomicity of the operation itself is
- * guaranteed.
+ * Atomic integer types (odp_atomic_u32_t, odp_atomic_u64_t and
+ * odp_atomic_u128_t) can be used to implement e.g. shared counters. If not
+ * otherwise documented, operations in this API are implemented using
+ * <b> RELAXED memory ordering </b> (see memory order descriptions in
+ * the C11 specification). Relaxed operations do not provide synchronization or
+ * ordering for other memory accesses (initiated before or after the operation),
+ * only atomicity of the operation itself is guaranteed.
*
* <b> Operations with non-relaxed memory ordering </b>
*
@@ -52,6 +53,9 @@ extern "C" {
*/
/**
+ * @typedef odp_atomic_u128_t
+ * Atomic 128-bit unsigned integer
+ *
* @typedef odp_atomic_u64_t
* Atomic 64-bit unsigned integer
*
@@ -187,9 +191,10 @@ void odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t new_min);
* Compare and swap atomic uint32 variable
*
* Compares value of atomic variable to the value pointed by 'old_val'.
- * If values are equal, the operation writes 'new_val' into the atomic variable
- * and returns success. If they are not equal, the operation writes current
- * value of atomic variable into 'old_val' and returns failure.
+ * If the values are equal, the operation writes 'new_val' into the atomic variable
+ * and returns success. The operation returns failure only when the values are
+ * not equal (strong CAS operation). The current value of atomic variable is written
+ * into 'old_val' on failure.
*
* @param atom Pointer to atomic variable
* @param[in,out] old_val Pointer to the old value of the atomic variable.
@@ -197,10 +202,8 @@ void odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t new_min);
* @param new_val New value to be written into the atomic variable
*
* @return 0 on failure, !0 on success
- *
*/
-int odp_atomic_cas_u32(odp_atomic_u32_t *atom, uint32_t *old_val,
- uint32_t new_val);
+int odp_atomic_cas_u32(odp_atomic_u32_t *atom, uint32_t *old_val, uint32_t new_val);
/**
* Exchange value of atomic uint32 variable
@@ -343,9 +346,10 @@ void odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t new_min);
* Compare and swap atomic uint64 variable
*
* Compares value of atomic variable to the value pointed by 'old_val'.
- * If values are equal, the operation writes 'new_val' into the atomic variable
- * and returns success. If they are not equal, the operation writes current
- * value of atomic variable into 'old_val' and returns failure.
+ * If the values are equal, the operation writes 'new_val' into the atomic variable
+ * and returns success. The operation returns failure only when the values are
+ * not equal (strong CAS operation). The current value of atomic variable is written
+ * into 'old_val' on failure.
*
* @param atom Pointer to atomic variable
* @param[in,out] old_val Pointer to the old value of the atomic variable.
@@ -354,8 +358,7 @@ void odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t new_min);
*
* @return 0 on failure, !0 on success
*/
-int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val,
- uint64_t new_val);
+int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val, uint64_t new_val);
/**
* Exchange value of atomic uint64 variable
@@ -371,6 +374,58 @@ int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val,
uint64_t odp_atomic_xchg_u64(odp_atomic_u64_t *atom, uint64_t new_val);
/*
+ * 128-bit operations in RELAXED memory ordering
+ * --------------------------------------------
+ */
+
+/**
+ * Initialize atomic odp_u128_t variable
+ *
+ * Initializes the atomic variable with 'val'. This operation is not atomic.
+ * Application must ensure that there's no race condition while initializing
+ * the variable.
+ *
+ * @param atom Pointer to atomic variable
+ * @param val Value to initialize the variable with
+ */
+void odp_atomic_init_u128(odp_atomic_u128_t *atom, odp_u128_t val);
+
+/**
+ * Load value of atomic odp_u128_t variable
+ *
+ * @param atom Pointer to atomic variable
+ *
+ * @return Value of the variable
+ */
+odp_u128_t odp_atomic_load_u128(odp_atomic_u128_t *atom);
+
+/**
+ * Store value to atomic odp_u128_t variable
+ *
+ * @param atom Pointer to atomic variable
+ * @param val Value to store in the variable
+ */
+void odp_atomic_store_u128(odp_atomic_u128_t *atom, odp_u128_t val);
+
+/**
+ * Compare and swap atomic odp_u128_t variable
+ *
+ * Compares value of atomic variable to the value pointed by 'old_val'.
+ * If the values are equal, the operation writes 'new_val' into the atomic variable
+ * and returns success. The operation returns failure only when the values are
+ * not equal (strong CAS operation). The current value of atomic variable is written
+ * into 'old_val' on failure.
+ *
+ * @param atom Pointer to atomic variable
+ * @param[in,out] old_val Pointer to the old value of the atomic variable.
+ * Operation updates this value on failure.
+ * @param new_val New value to be written into the atomic variable
+ *
+ * @return 0 on failure, !0 on success
+ */
+int odp_atomic_cas_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val, odp_u128_t new_val);
+
+/*
* 32-bit operations in non-RELAXED memory ordering
* ------------------------------------------------
*/
@@ -620,6 +675,80 @@ typedef union odp_atomic_op_t {
*/
int odp_atomic_lock_free_u64(odp_atomic_op_t *atomic_op);
+/*
+ * 128-bit operations in non-RELAXED memory ordering
+ * ------------------------------------------------
+ */
+
+/**
+ * Compare and swap atomic odp_u128_t variable using ACQUIRE memory ordering
+ *
+ * Otherwise identical to odp_atomic_cas_u128() but ensures ACQUIRE memory
+ * ordering on success. Memory ordering is RELAXED on failure.
+ *
+ * @param atom Pointer to atomic variable
+ * @param[in,out] old_val Pointer to the old value of the atomic variable.
+ * Operation updates this value on failure.
+ * @param new_val New value to be written into the atomic variable
+ *
+ * @return 0 on failure, !0 on success
+ */
+int odp_atomic_cas_acq_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val);
+
+/**
+ * Compare and swap atomic odp_u128_t variable using RELEASE memory ordering
+ *
+ * Otherwise identical to odp_atomic_cas_u128() but ensures RELEASE memory
+ * ordering on success. Memory ordering is RELAXED on failure.
+ *
+ * @param atom Pointer to atomic variable
+ * @param[in,out] old_val Pointer to the old value of the atomic variable.
+ * Operation updates this value on failure.
+ * @param new_val New value to be written into the atomic variable
+ *
+ * @return 0 on failure, !0 on success
+ */
+int odp_atomic_cas_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val);
+
+/**
+ * Compare and swap atomic odp_u128_t variable using ACQUIRE-and-RELEASE memory
+ * ordering
+ *
+ * Otherwise identical to odp_atomic_cas_u128() but ensures ACQUIRE-and-RELEASE
+ * memory ordering on success. Memory ordering is RELAXED on failure.
+ *
+ * @param atom Pointer to atomic variable
+ * @param[in,out] old_val Pointer to the old value of the atomic variable.
+ * Operation updates this value on failure.
+ * @param new_val New value to be written into the atomic variable
+ *
+ * @return 0 on failure, !0 on success
+ */
+int odp_atomic_cas_acq_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val);
+
+/**
+ * Query which atomic odp_atomic_u128_t operations are lock-free
+ *
+ * Lock-free implementations have higher performance and scale better than
+ * implementations using locks.
+ *
+ * Init operations (e.g. odp_atomic_init_u128()) are not atomic. This function
+ * clears the op.init bit but will never set it to one.
+ *
+ * Note: 128-bit atomic API includes only init, load, store and CAS operations.
+ *
+ * @param atomic_op Pointer to atomic operation structure for storing
+ * operation flags. All bits are initialized to zero during
+ * the operation. The parameter is ignored when NULL.
+ * @retval 0 None of the 128-bit atomic operations are lock-free
+ * @retval 1 Some of the 128-bit atomic operations are lock-free
+ * @retval 2 All 128-bit atomic operations are lock-free
+ */
+int odp_atomic_lock_free_u128(odp_atomic_op_t *atomic_op);
+
/**
* @}
*/
diff --git a/include/odp/api/spec/barrier.h b/include/odp/api/spec/barrier.h
index 6de683c73..e83d46c35 100644
--- a/include/odp/api/spec/barrier.h
+++ b/include/odp/api/spec/barrier.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP execution barriers
*/
-#ifndef ODP_API_BARRIER_H_
-#define ODP_API_BARRIER_H_
+#ifndef ODP_API_SPEC_BARRIER_H_
+#define ODP_API_SPEC_BARRIER_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/buffer.h b/include/odp/api/spec/buffer.h
index 94829b324..5ce2355b8 100644
--- a/include/odp/api/spec/buffer.h
+++ b/include/odp/api/spec/buffer.h
@@ -1,37 +1,30 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
*/
-
/**
* @file
*
- * ODP buffer descriptor
+ * ODP buffer
*/
-#ifndef ODP_API_BUFFER_H_
-#define ODP_API_BUFFER_H_
+#ifndef ODP_API_SPEC_BUFFER_H_
+#define ODP_API_SPEC_BUFFER_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_buffer ODP BUFFER
- * Operations on a buffer.
- * @{
- */
+#include <odp/api/buffer_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/std_types.h>
-/**
- * @typedef odp_buffer_t
- * ODP buffer
- */
-
-/**
- * @def ODP_BUFFER_INVALID
- * Invalid buffer
+/** @addtogroup odp_buffer
+ * Buffer event metadata and operations.
+ * @{
*/
/**
@@ -48,6 +41,17 @@ extern "C" {
odp_buffer_t odp_buffer_from_event(odp_event_t ev);
/**
+ * Convert multiple buffer events to buffer handles
+ *
+ * All events must be of type ODP_EVENT_BUFFER.
+ *
+ * @param[out] buf Buffer handle array for output
+ * @param ev Array of event handles to convert
+ * @param num Number of buffers and events
+ */
+void odp_buffer_from_event_multi(odp_buffer_t buf[], const odp_event_t ev[], int num);
+
+/**
* Convert buffer handle to event
*
* @param buf Buffer handle
@@ -57,6 +61,15 @@ odp_buffer_t odp_buffer_from_event(odp_event_t ev);
odp_event_t odp_buffer_to_event(odp_buffer_t buf);
/**
+ * Convert multiple buffer handles to events
+ *
+ * @param buf Array of buffer handles to convert
+ * @param[out] ev Event handle array for output
+ * @param num Number of buffers and events
+ */
+void odp_buffer_to_event_multi(const odp_buffer_t buf[], odp_event_t ev[], int num);
+
+/**
* Buffer start address
*
* @param buf Buffer handle
@@ -75,7 +88,24 @@ void *odp_buffer_addr(odp_buffer_t buf);
uint32_t odp_buffer_size(odp_buffer_t buf);
/**
- * Tests if buffer is valid
+ * Buffer user area
+ *
+ * Returns pointer to the user area associated with the buffer. Size of the area is fixed
+ * and defined in buffer pool parameters.
+ *
+ * @param buf Buffer handle
+ *
+ * @return Pointer to the user area of the buffer
+ * @retval NULL The buffer does not have user area
+ */
+void *odp_buffer_user_area(odp_buffer_t buf);
+
+/**
+ * Check that buffer is valid
+ *
+ * This function can be used for debugging purposes to check if a buffer handle represents
+ * a valid buffer. The level of error checks depends on the implementation. The call should not
+ * crash if the buffer handle is corrupted.
*
* @param buf Buffer handle
*
@@ -96,8 +126,8 @@ odp_pool_t odp_buffer_pool(odp_buffer_t buf);
/**
* Buffer alloc
*
- * The validity of a buffer can be checked at any time with
- * odp_buffer_is_valid().
+ * Allocates a buffer from the pool. Returns ODP_BUFFER_INVALID when a buffer
+ * can not be allocated.
*
* @param pool Pool handle
*
@@ -108,8 +138,8 @@ odp_buffer_t odp_buffer_alloc(odp_pool_t pool);
/**
* Allocate multiple buffers
-
- * Otherwise like odp_buffer_alloc(), but allocates multiple buffers from a pool
+ *
+ * Otherwise like odp_buffer_alloc(), but allocates multiple buffers from a pool.
*
* @param pool Pool handle
* @param[out] buf Array of buffer handles for output
diff --git a/include/odp/api/spec/buffer_types.h b/include/odp/api/spec/buffer_types.h
new file mode 100644
index 000000000..7307e72f7
--- /dev/null
+++ b/include/odp/api/spec/buffer_types.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP buffer types
+ */
+
+#ifndef ODP_API_SPEC_BUFFER_TYPES_H_
+#define ODP_API_SPEC_BUFFER_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_buffer ODP BUFFER
+ * @{
+ */
+
+/**
+ * @typedef odp_buffer_t
+ * ODP buffer
+ */
+
+/**
+ * @def ODP_BUFFER_INVALID
+ * Invalid buffer
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/byteorder.h b/include/odp/api/spec/byteorder.h
index 38c0bdbf7..65d2e722a 100644
--- a/include/odp/api/spec/byteorder.h
+++ b/include/odp/api/spec/byteorder.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP byteorder
*/
-#ifndef ODP_API_BYTEORDER_H_
-#define ODP_API_BYTEORDER_H_
+#ifndef ODP_API_SPEC_BYTEORDER_H_
+#define ODP_API_SPEC_BYTEORDER_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/chksum.h b/include/odp/api/spec/chksum.h
new file mode 100644
index 000000000..800aab2c2
--- /dev/null
+++ b/include/odp/api/spec/chksum.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP checksum functions.
+ */
+
+#ifndef ODP_API_SPEC_CHKSUM_H_
+#define ODP_API_SPEC_CHKSUM_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_chksum ODP CHECKSUM
+ * Checksum functions.
+ * @{
+ */
+
+/**
+ * Ones' complement sum of 16-bit words
+ *
+ * Calculates 16-bit ones' complement sum over the data. In case of odd number
+ * of bytes, calculation uses a zero byte as padding at the end. This algorithm
+ * may be used as part of e.g. IPv4/UDP/TCP checksum generation and checking.
+ *
+ * @param data Pointer to data. Data address must be 16-bit aligned
+ * in minimum.
+ * @param data_len Data length in bytes. In case of an odd number, calculation
+ * includes one byte of padding.
+ *
+ * @return Ones' complement sum
+ */
+uint16_t odp_chksum_ones_comp16(const void *data, uint32_t data_len);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/classification.h b/include/odp/api/spec/classification.h
index 39831b240..518b2bd3c 100644
--- a/include/odp/api/spec/classification.h
+++ b/include/odp/api/spec/classification.h
@@ -1,58 +1,198 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
-
/**
* @file
*
* ODP classification descriptor
*/
-#ifndef ODP_API_CLASSIFY_H_
-#define ODP_API_CLASSIFY_H_
+#ifndef ODP_API_SPEC_CLASSIFICATION_H_
+#define ODP_API_SPEC_CLASSIFICATION_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
+#include <odp/api/packet_io_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/std_types.h>
+#include <odp/api/threshold.h>
+
/** @defgroup odp_classification ODP CLASSIFICATION
- * Classification operations.
+ * Packet input classification.
* @{
*/
+/**
+ * @typedef odp_pmr_t
+ * Packet matching rule handle
+ */
/**
- * @typedef odp_cos_t
- * ODP Class of service handle
+ * @def ODP_PMR_INVALID
+ * Invalid packet matching rule handle
*/
/**
- * @typedef odp_flowsig_t
- * flow signature type, only used for packet metadata field.
+ * @typedef odp_cos_t
+ * Class of service handle
*/
/**
* @def ODP_COS_INVALID
- * This value is returned from odp_cls_cos_create() on failure,
- * May also be used as a sink class of service that
- * results in packets being discarded.
+ * Invalid class of service handle
*/
/**
* @def ODP_COS_NAME_LEN
- * Maximum ClassOfService name length in chars including null char
+ * Maximum class of service name length in chars including null char
*/
/**
- * @def ODP_PMR_INVAL
- * Invalid odp_pmr_t value.
- * This value is returned from odp_cls_pmr_create()
- * function on failure.
+ * Packet Matching Rule terms
+ *
+ * This enumeration selects the protocol field that is matched against PMR
+ * value/mask or value range. Protocol field values and masks are passed in big
+ * endian (network endian) format. However, ODP_PMR_LEN value and range are
+ * passed in CPU native endian (uint32_t words), as the term does not represent
+ * a protocol field.
+ *
+ * PMR value/mask data size is term specific. This size must be set into val_sz
+ * field of odp_pmr_param_t. There is no alignment requirement for PMR
+ * value/mask data.
*/
+typedef enum {
+ /** Total length of received packet. Exceptionally, value and mask are
+ * uint32_t (val_sz = 4) in CPU endian. */
+ ODP_PMR_LEN,
+
+ /** Initial (outer) Ethertype only (val_sz = 2)
+ *
+ * PMR matches Ethertype field when packet does not have VLAN headers. When there are
+ * VLAN headers, it matches Tag protocol identifier (TPID) field of the first VLAN header.
+ * I.e. it matches a field in the same offset from the start of the packet in both cases.
+ */
+ ODP_PMR_ETHTYPE_0,
+
+ /** Ethertype of most inner VLAN tag (val_sz = 2) */
+ ODP_PMR_ETHTYPE_X,
+
+ /** First (outer) VLAN ID (val_sz = 2)
+ *
+ * VLAN ID value and mask are stored into 12 least significant bits of a 16-bit word.
+ * The word is passed in big endian format.
+ */
+ ODP_PMR_VLAN_ID_0,
+
+ /** Last (most inner) VLAN ID (val_sz = 2)
+ *
+ * VLAN ID value and mask are stored into 12 least significant bits of a 16-bit word.
+ * The word is passed in big endian format.
+ */
+ ODP_PMR_VLAN_ID_X,
+
+ /** PCP bits in the first (outer) VLAN header (val_sz = 1)
+ *
+ * Priority Code Point (PCP) value is stored into three least significant bits of
+ * the octet pointed by odp_pmr_param_t::value. The same applies for odp_pmr_param_t::mask.
+ */
+ ODP_PMR_VLAN_PCP_0,
+
+ /** Destination MAC address (val_sz = 6) */
+ ODP_PMR_DMAC,
+
+ /** IPv4 Protocol or IPv6 Next Header (val_sz = 1) */
+ ODP_PMR_IPPROTO,
+
+ /** Differentiated Services Code Point (DSCP) bits in IPv4 or IPv6 header (val_sz = 1)
+ *
+ * DSCP value is stored into six least significant bits of the octet pointed by
+ * odp_pmr_param_t::value. The same applies for odp_pmr_param_t::mask.
+ */
+ ODP_PMR_IP_DSCP,
+
+ /** Destination UDP port (val_sz = 2) */
+ ODP_PMR_UDP_DPORT,
+
+ /** Destination TCP port (val_sz = 2) */
+ ODP_PMR_TCP_DPORT,
+
+ /** Source UDP port (val_sz = 2) */
+ ODP_PMR_UDP_SPORT,
+
+ /** Source TCP port (val_sz = 2) */
+ ODP_PMR_TCP_SPORT,
+
+ /** Source IPv4 address (val_sz = 4) */
+ ODP_PMR_SIP_ADDR,
+
+ /** Destination IPv4 address (val_sz = 4) */
+ ODP_PMR_DIP_ADDR,
+
+ /** Source IPv6 address (val_sz = 16) */
+ ODP_PMR_SIP6_ADDR,
+
+ /** Destination IPv6 address (val_sz = 16) */
+ ODP_PMR_DIP6_ADDR,
+
+ /** IPsec session identifier (val_sz = 4)*/
+ ODP_PMR_IPSEC_SPI,
+
+ /** NVGRE/VXLAN network identifier (val_sz = 4) */
+ ODP_PMR_LD_VNI,
+
+ /**
+ * Custom frame match rule
+ *
+ * PMR offset is counted from the start of the packet. The match is
+ * defined by the offset, the expected value, and its size. Custom frame
+ * rules must be applied before any other PMR.
+ */
+ ODP_PMR_CUSTOM_FRAME,
+
+ /**
+ * Custom layer 3 match rule
+ *
+ * PMR offset is counted from the start of layer 3 in the packet.
+ * The match is defined by the offset, the expected value, and its size.
+ * Custom L3 rules may be combined with other PMRs.
+ */
+ ODP_PMR_CUSTOM_L3,
+
+ /** IGMP Group address (val_sz = 4), implies IPPROTO=2 */
+ ODP_PMR_IGMP_GRP_ADDR,
+
+ /** ICMP identifier (val_sz = 2), implies IPPROTO=1 and ICMP_TYPE=0 or ICMP_TYPE=8 */
+ ODP_PMR_ICMP_ID,
+
+ /** ICMP type (val_sz = 1), implies IPPROTO=1 */
+ ODP_PMR_ICMP_TYPE,
+
+ /** ICMP code (val_sz = 1), implies IPPROTO=1 */
+ ODP_PMR_ICMP_CODE,
+
+ /** Source SCTP port (val_sz = 2), implies IPPROTO=132 */
+ ODP_PMR_SCTP_SPORT,
+
+ /** Destination SCTP port (val_sz = 2), implies IPPROTO=132 */
+ ODP_PMR_SCTP_DPORT,
+
+ /** GTPv1 tunnel endpoint identifier (val_sz = 4)
+ *
+ * Matches if and only if IP protocol is UDP, UDP destination port
+ * is 2152 and the UDP payload interpreted as GTP header has GTP
+ * version 1 and TEID as specified.
+ */
+ ODP_PMR_GTPV1_TEID,
+
+ /** Inner header may repeat above values with this offset */
+ ODP_PMR_INNER_HDR_OFF = 32
+
+} odp_cls_pmr_term_t;
/**
* Supported PMR term values
@@ -72,10 +212,14 @@ typedef union odp_cls_pmr_terms_t {
uint64_t vlan_id_0:1;
/** Last VLAN ID (inner) */
uint64_t vlan_id_x:1;
+ /** PCP in the first VLAN header (#ODP_PMR_VLAN_PCP_0) */
+ uint64_t vlan_pcp_0:1;
/** destination MAC address */
uint64_t dmac:1;
/** IP Protocol or IPv6 Next Header */
uint64_t ip_proto:1;
+ /** DSCP in IP header (#ODP_PMR_IP_DSCP) */
+ uint64_t ip_dscp:1;
/** Destination UDP port, implies IPPROTO=17 */
uint64_t udp_dport:1;
/** Destination TCP port implies IPPROTO=6 */
@@ -96,77 +240,451 @@ typedef union odp_cls_pmr_terms_t {
uint64_t ipsec_spi:1;
/** NVGRE/VXLAN network identifier */
uint64_t ld_vni:1;
- /** Custom match rule, offset from start of
- * frame. The match is defined by the offset, the
- * expected value, and its size.
- */
- uint64_t custom_frame:1;
-
+ /** Custom frame match rule. PMR offset is counted from
+ * the start of the packet. */
+ uint64_t custom_frame:1;
+ /** Custom layer 3 match rule. PMR offset is counted from
+ * the start of layer 3 in the packet. */
+ uint64_t custom_l3:1;
+ /** IGMP Group address, implies IPPROTO=2 */
+ uint64_t igmp_grp_addr:1;
+ /** ICMP identifier, implies IPPROTO=1 and ICMP_TYPE=0 or ICMP_TYPE=8 */
+ uint64_t icmp_id:1;
+ /** ICMP type, implies IPPROTO=1 */
+ uint64_t icmp_type:1;
+ /** ICMP code, implies IPPROTO=1 */
+ uint64_t icmp_code:1;
+ /** Source SCTP port, implies IPPROTO=132 */
+ uint64_t sctp_sport:1;
+ /** Destination SCTP port, implies IPPROTO=132 */
+ uint64_t sctp_dport:1;
+ /** GTPv1 tunnel endpoint identifier */
+ uint64_t gtpv1_teid:1;
} bit;
+
/** All bits of the bit field structure */
uint64_t all_bits;
+
} odp_cls_pmr_terms_t;
/**
+ * Packet Matching Rule parameter structure
+ *
+ * Match value/mask size and endianness are defined in PMR term documentation
+ * (see odp_cls_pmr_term_t). Most values and masks are passed in big
+ * endian format without data alignment requirement. ODP_PMR_LEN is
+ * an exception to this (uint32_t in CPU endian).
+ */
+typedef struct odp_pmr_param_t {
+ /** Packet Matching Rule term */
+ odp_cls_pmr_term_t term;
+
+ /** True if the value is range and false if match. Default is false. */
+ odp_bool_t range_term;
+
+ /** Variant mappings for types of matches */
+ union {
+ /** Parameters for single-valued matches */
+ struct {
+ /** Points to the value to be matched. Value size and
+ * endianness are defined by the term used. Values of
+ * protocol fields are defined in big endian format.
+ */
+ const void *value;
+
+ /** Mask of the bits to be matched. The same size and
+ * endianness is used than with the value. */
+ const void *mask;
+ } match;
+
+ /** Parameter for range value matches */
+ struct {
+ /** Start value of the range */
+ const void *val_start;
+
+ /** End value of the range */
+ const void *val_end;
+ } range;
+ };
+
+ /** Size of the value to be matched */
+ uint32_t val_sz;
+
+ /** Offset to the value
+ *
+ * Byte offset to the value to be matched in a packet. PMR term defines
+ * starting point for the offset. Used only with custom PMR terms,
+ * ignored with other terms.
+ */
+ uint32_t offset;
+
+} odp_pmr_param_t;
+
+/**
+ * Packet Matching Rule creation options
+ */
+typedef struct odp_pmr_create_opt_t {
+ /** PMR terms
+ *
+ * Array of odp_pmr_param_t entries, one entry per term desired.
+ * Use odp_cls_pmr_param_init() to initialize parameters into their default values.
+ */
+ odp_pmr_param_t *terms;
+
+ /** Number of terms in the match rule. */
+ int num_terms;
+
+ /** Classification mark value
+ *
+ * Value to be set in the CLS mark of a packet when the packet matches this
+ * Packet Matching Rule. The default value is zero. The maximum value is indicated in
+ * odp_cls_capability_t::max_mark capability.
+ */
+ uint64_t mark;
+
+} odp_pmr_create_opt_t;
+
+/** Random Early Detection (RED)
+ * Random Early Detection is enabled to initiate a drop probability for the
+ * incoming packet when the packets in the queue/pool cross the specified
+ * threshold values. RED is enabled when 'red_enable' boolean is true and
+ * the resource usage is equal to or greater than the minimum threshold value.
+ * Resource usage could be defined either as the percentage of pool being full
+ * or the number of packets/bytes occupied in the queue depending on the
+ * platform capabilities.
+ *
+ * When RED is enabled for a particular flow then further incoming packets are
+ * assigned a drop probability based on the size of the pool/queue.
+ *
+ * Drop probability is configured as follows
+ * * Drop probability is 100%, when resource usage >= threshold.max
+ * * Drop probability is 0%, when resource usage <= threshold.min
+ * * Drop probability is between 0...100 % when resource usage is between
+ * threshold.min and threshold.max
+ *
+ * RED is logically configured in the CoS and could be implemented in either
+ * pool or queue linked to the CoS depending on platform capabilities.
+ * Application should make sure not to link multiple CoS with different RED or
+ * BP configuration to the same queue or pool.
+ */
+typedef struct odp_red_param_t {
+ /** A boolean to enable RED
+ * When true, RED is enabled and configured with RED parameters.
+ * Otherwise, RED parameters are ignored. Default value is false.
+ */
+ odp_bool_t enable;
+
+ /** Threshold parameters for RED
+ * RED is enabled when the resource usage is equal to or greater than
+ * the minimum threshold value and is disabled otherwise
+ */
+ odp_threshold_t threshold;
+
+} odp_red_param_t;
+
+/** Back pressure (BP)
+ * When back pressure is enabled for a particular flow, the HW can send
+ * back pressure information to the remote peer indicating a network congestion.
+ */
+typedef struct odp_bp_param_t {
+ /** A boolean to enable Back pressure
+ * When true, back pressure is enabled and configured with the BP
+ * parameters. Otherwise BP parameters are ignored. Default value
+ * is false.
+ */
+ odp_bool_t enable;
+
+ /** Threshold value for back pressure.
+ * BP is enabled when the resource usage is equal to or greater than the
+ * max backpressure threshold. Min threshold parameters are ignored for
+ * BP configuration.
+ * @see odp_red_param_t for 'resource usage' documentation.
+ */
+ odp_threshold_t threshold;
+
+ /**
+ * PFC priority level
+ *
+ * When enabled (#ODP_PKTIO_LINK_PFC_ON), PFC frames are generated when the above
+ * threshold is exceeded. The generated frames request the receiver to temporary halt
+ * transmission of traffic on this priority level (0 .. 7).
+ */
+ uint8_t pfc_level;
+
+} odp_bp_param_t;
+
+/**
+ * Classifier CoS specific statistics counters
+ *
+ * Counters are incremented per packet classified to the CoS. In a CoS chain,
+ * counters are incremented in every CoS for which counters are enabled.
+ */
+typedef struct odp_cls_cos_stats_t {
+ /** Number of octets in classified packets. In case of Ethernet, packet
+ * size includes MAC header. */
+ uint64_t octets;
+
+ /** Number of classified packets, including packets dropped due to drop
+ * action. */
+ uint64_t packets;
+
+ /** Number of discarded packets due to other reasons than packet
+ * errors or drop action. */
+ uint64_t discards;
+
+ /** Number of packets with errors. */
+ uint64_t errors;
+
+} odp_cls_cos_stats_t;
+
+/**
+ * Classifier queue specific statistics counters
+ *
+ * Counters are incremented per packet destined to the queue per originating
+ * CoS. Note that a single queue can be a destination for multiple CoS's.
+ */
+typedef struct odp_cls_queue_stats_t {
+ /** Number of octets in successfully delivered packets. In case of
+ * Ethernet, packet size includes MAC header. */
+ uint64_t octets;
+
+ /** Number of successfully delivered packets. */
+ uint64_t packets;
+
+ /** Number of discarded packets due to other reasons (e.g. RED) than
+ * errors. */
+ uint64_t discards;
+
+ /** Number of packets with errors. Depending on packet input
+ * configuration, packets with errors may be dropped or not. */
+ uint64_t errors;
+
+} odp_cls_queue_stats_t;
+
+/**
+ * Classifier statistics capabilities
+ */
+typedef struct odp_cls_stats_capability_t {
+ /** CoS level capabilities */
+ struct {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_cls_cos_stats_t::octets */
+ uint64_t octets : 1;
+
+ /** See odp_cls_cos_stats_t::packets */
+ uint64_t packets : 1;
+
+ /** See odp_cls_cos_stats_t::discards */
+ uint64_t discards : 1;
+
+ /** See odp_cls_cos_stats_t::errors */
+ uint64_t errors : 1;
+
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+ } cos;
+
+ /** Queue level capabilities */
+ struct {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_cls_queue_stats_t::octets */
+ uint64_t octets : 1;
+
+ /** See odp_cls_queue_stats_t::packets */
+ uint64_t packets : 1;
+
+ /** See odp_cls_queue_stats_t::discards */
+ uint64_t discards : 1;
+
+ /** See odp_cls_queue_stats_t::errors */
+ uint64_t errors : 1;
+
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+ } queue;
+
+} odp_cls_stats_capability_t;
+
+/**
* Classification capabilities
* This capability structure defines system level classification capability
*/
typedef struct odp_cls_capability_t {
/** PMR terms supported by the classifier
- * A bit mask of one bit for each of odp_pmr_term_t
- */
+ *
+ * A bit mask of one bit for each of odp_pmr_term_t. */
odp_cls_pmr_terms_t supported_terms;
- /** Maximum number of PMR terms */
- unsigned max_pmr_terms;
+ /** Maximum number of single-term PMRs
+ *
+ * Depending on the implementation, using several/composite terms for a
+ * single PMR may end up incurring more than one PMR element from this
+ * total capacity. */
+ uint32_t max_pmr;
- /** Number of PMR terms available for use now */
- unsigned available_pmr_terms;
+ /** Maximum number of PMRs per CoS */
+ uint32_t max_pmr_per_cos;
+
+ /** Maximum number of terms per composite PMR */
+ uint32_t max_terms_per_pmr;
/** Maximum number of CoS supported */
- unsigned max_cos;
+ uint32_t max_cos;
+
+ /** Maximum number of concurrent CoS stats
+ *
+ * Maximum number of CoSes that can have statistics enabled at the same
+ * time. If this value is zero, then CoS level statistics are not
+ * supported. */
+ uint32_t max_cos_stats;
+
+ /** Maximum number of queues supported per CoS
+ *
+ * If the value is 1, then hashing is not supported. */
+ uint32_t max_hash_queues;
+
+ /** Protocol header combination supported for Hashing */
+ odp_pktin_hash_proto_t hash_protocols;
/** A Boolean to denote support of PMR range */
odp_bool_t pmr_range_supported;
+
+ /** Support for Random Early Detection */
+ odp_support_t random_early_detection;
+
+ /** Supported threshold type for RED */
+ odp_threshold_types_t threshold_red;
+
+ /** Support for Back Pressure to the remote peer */
+ odp_support_t back_pressure;
+
+ /** Supported threshold type for BP */
+ odp_threshold_types_t threshold_bp;
+
+ /** Maximum value of odp_pmr_create_opt_t::mark */
+ uint64_t max_mark;
+
+ /** Statistics counters capabilities */
+ odp_cls_stats_capability_t stats;
+
} odp_cls_capability_t;
/**
- * class of service packet drop policies
+ * Enumeration of actions for CoS.
*/
typedef enum {
- ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */
- ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool policy */
-} odp_cls_drop_t;
+ /**
+ * Enqueue packet
+ *
+ * Packets that arrive in the CoS are enqueued to a destination queue.
+ */
+ ODP_COS_ACTION_ENQUEUE,
-/**
- * Packet header field enumeration
- * for fields that may be used to calculate
- * the flow signature, if present in a packet.
- */
-typedef enum {
- ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */
- ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address */
- ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC address */
- ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */
- ODP_COS_FHDR_L3_FLOW, /**< IPv6 flow_id */
- ODP_COS_FHDR_L3_SAP, /**< IP source address */
- ODP_COS_FHDR_L3_DAP, /**< IP destination address */
- ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. TCP/UDP/ICMP) */
- ODP_COS_FHDR_L4_SAP, /**< Transport source port */
- ODP_COS_FHDR_L4_DAP, /**< Transport destination port */
- ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */
- ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network identifier */
- ODP_COS_FHDR_USER /**< Application-specific header field(s) */
-} odp_cos_hdr_flow_fields_t;
+ /**
+ * Drop packet
+ *
+ * Packets that arrive in the CoS are dropped. Packets are freed into
+ * their originating pool.
+ */
+ ODP_COS_ACTION_DROP,
+
+} odp_cos_action_t;
/**
* Class of service parameters
* Used to communicate class of service creation options
*/
typedef struct odp_cls_cos_param {
- odp_queue_t queue; /**< Queue associated with CoS */
- odp_pool_t pool; /**< Pool associated with CoS */
- odp_cls_drop_t drop_policy; /**< Drop policy associated with CoS */
+ /** Action to take. When action is ODP_COS_ACTION_DROP, all the other
+ * parameters are ignored. If action is ODP_COS_ACTION_ENQUEUE, then
+ * queue must be set, or num_queue must be greater than one.
+ *
+ * The final match in the CoS chain defines the action for a packet.
+ * I.e. packet is dropped only when the CoS of the last matching rule
+ * has drop action. Actions in the previous CoSes in the chain are
+ * ignored.
+ *
+ * Default is ODP_COS_ACTION_ENQUEUE.
+ */
+ odp_cos_action_t action;
+
+ /** Enable statistics. If true, counters are incremented when packets
+ * are classified to the CoS. Default is false.
+ *
+ * @see odp_cls_cos_stats()
+ */
+ odp_bool_t stats_enable;
+
+ /** Number of queues to be linked to this CoS.
+ * If the number is greater than 1 then hashing is enabled.
+ * If number is equal to 1 then hashing is disabled.
+ * When hashing is enabled the queues are created by the implementation
+ * and application need not configure any queue to the class of service.
+ * When hashing is disabled application has to configure the queue to
+ * the class of service.
+ * Depending on the implementation this number might be rounded-off to
+ * nearest supported value (e.g power of 2)
+ *
+ * Default value is 1.
+ */
+ uint32_t num_queue;
+
+ /** Variant mapping for queue hash configuration */
+ union {
+ /** Mapping used when num_queue = 1, hashing is disabled in
+ * this case and application has to configure this queue and
+ * packets are delivered to this queue */
+ odp_queue_t queue;
+
+ /** Mapping used when num_queue > 1, hashing is enabled in
+ * this case and queues are created by the implementation */
+ struct {
+ /** Queue parameters */
+ odp_queue_param_t queue_param;
+
+ /** Protocol header fields which are included in
+ * packet input hash calculation */
+ odp_pktin_hash_proto_t hash_proto;
+ };
+ };
+ /** Pool associated with CoS
+ *
+ * May be set to ODP_POOL_INVALID, in which case the default pool of
+ * the originating packet input is used (see odp_pktio_open()). If
+ * there is no originating packet input (e.g. with lookaside IPsec),
+ * then this parameter must be set to a valid pool.
+ *
+ * Default is ODP_POOL_INVALID.
+ */
+ odp_pool_t pool;
+
+ /** Random Early Detection configuration */
+ odp_red_param_t red;
+
+ /** Back Pressure configuration */
+ odp_bp_param_t bp;
+
+ /** Packet input vector configuration */
+ odp_pktin_vector_config_t vector;
+
} odp_cls_cos_param_t;
/**
@@ -174,7 +692,7 @@ typedef struct odp_cls_cos_param {
*
* Initialize an odp_cls_cos_param_t to its default value for all fields
*
- * @param param Address of the odp_cls_cos_param_t to be initialized
+ * @param param Address of the odp_cls_cos_param_t to be initialized
*/
void odp_cls_cos_param_init(odp_cls_cos_param_t *param);
@@ -183,319 +701,376 @@ void odp_cls_cos_param_init(odp_cls_cos_param_t *param);
*
* Outputs classification capabilities on success.
*
- * @param[out] capability Pointer to classification capability structure.
+ * @param[out] capability Pointer to classification capability structure.
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*/
int odp_cls_capability(odp_cls_capability_t *capability);
/**
* Create a class-of-service
*
+ * Depending on the action parameter, packets to the CoS are either enqueued to
+ * a destination queue, or dropped.
+ *
* The use of class-of-service name is optional. Unique names are not required.
+ * Use odp_cls_cos_param_init() to initialize parameters into their default
+ * values.
*
- * @param name Name of the class-of-service or NULL. Maximum string
- * length is ODP_COS_NAME_LEN.
- * @param param Class-of-service parameters
+ * @param name Name of the class-of-service or NULL. Maximum string
+ * length is ODP_COS_NAME_LEN.
+ * @param param Class-of-service parameters
*
- * @retval Class-of-service handle
- * @retval ODP_COS_INVALID on failure.
+ * @retval Class-of-service handle
+ * @retval ODP_COS_INVALID on failure.
+ */
+odp_cos_t odp_cls_cos_create(const char *name,
+ const odp_cls_cos_param_t *param);
+
+/**
+ * Create multiple class-of-services
+ *
+ * Otherwise like odp_cls_cos_create(), but creates multiple CoSes with a
+ * single call. The output CoS handles are written in the same order as input
+ * parameters. A single odp_cls_cos_create_multi() call is equivalent to calling
+ * odp_cls_cos_create() 'num' times in row.
+ *
+ * Each parameter array must contain 'num' elements with the exception that
+ * 'name' array may also be NULL.
+ *
+ * @param name Array of CoS name pointers or NULL. NULL is also valid
+ * CoS name pointer value.
+ * @param param Array of CoS parameters
+ * @param[out] cos Array of CoS handles for output
+ * @param num Number of CoSes to create
*
- * @note ODP_QUEUE_INVALID and ODP_POOL_INVALID are valid values for queue
- * and pool associated with a class of service and when any one of these values
- * are configured as INVALID then the packets assigned to the CoS gets dropped.
+ * @return Number of CoSes actually created (0 ... num)
+ * @retval <0 on failure
*/
-odp_cos_t odp_cls_cos_create(const char *name, odp_cls_cos_param_t *param);
+int odp_cls_cos_create_multi(const char *name[],
+ const odp_cls_cos_param_t param[],
+ odp_cos_t cos[], int num);
/**
- * Discard a class-of-service along with all its associated resources
+ * Queue hash result
+ * Returns the queue within a CoS in which a particular packet will be enqueued
+ * based on the packet parameters and hash protocol field configured with the
+ * class of service.
*
- * @param[in] cos_id class-of-service instance.
+ * @param cos CoS handle
+ * @param packet Packet handle
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval Returns the queue handle on which this packet will be enqueued.
+ * @retval ODP_QUEUE_INVALID for error case
+ *
+ * @note The packet has to be updated with valid header pointers L2, L3 and L4.
*/
-int odp_cos_destroy(odp_cos_t cos_id);
+odp_queue_t odp_cls_hash_result(odp_cos_t cos, odp_packet_t packet);
/**
- * Assign a queue for a class-of-service
+ * Discard a class-of-service along with all its associated resources
*
- * @param[in] cos_id class-of-service instance.
+ * Before destroying a CoS, all the PMRs referring to the CoS (as a source or
+ * destination CoS) must be destroyed first. Also, the CoS must not be in use
+ * as the default CoS in any pktio (see odp_pktio_default_cos_set()) or as the
+ * destination CoS of any IPsec SA.
*
- * @param[in] queue_id Identifier of a queue where all packets
- * of this specific class of service
- * will be enqueued.
+ * @param cos CoS handle
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-int odp_cos_queue_set(odp_cos_t cos_id, odp_queue_t queue_id);
+int odp_cos_destroy(odp_cos_t cos);
/**
-* Get the queue associated with the specific class-of-service
-*
-* @param[in] cos_id class-of-service instance.
-*
-* @retval queue_handle Queue handle associated with the
-* given class-of-service
-*
-* @retval ODP_QUEUE_INVALID on failure
-*/
-odp_queue_t odp_cos_queue(odp_cos_t cos_id);
+ * Destroy multiple class-of-services
+ *
+ * Otherwise like odp_cos_destroy(), but destroys multiple CoSes with a single
+ * call.
+ *
+ * @param cos Array of CoS handles
+ * @param num Number of CoSes to destroy
+ *
+ * @retval Number of CoSes actually destroyed (1 ... num)
+ * @retval <0 on failure
+ */
+int odp_cos_destroy_multi(odp_cos_t cos[], int num);
/**
- * Assign packet drop policy for specific class-of-service
+ * Assign a queue for a class-of-service
*
- * @param[in] cos_id class-of-service instance.
- * @param[in] drop_policy Desired packet drop policy for this class.
+ * Action of the given CoS may not be ODP_COS_ACTION_DROP.
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @param cos CoS handle
+ * @param queue Handle of the queue where all packets of this specific
+ * class of service will be enqueued. Must not be
+ * ODP_QUEUE_INVALID.
*
- * @note Optional.
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-int odp_cos_drop_set(odp_cos_t cos_id, odp_cls_drop_t drop_policy);
+int odp_cos_queue_set(odp_cos_t cos, odp_queue_t queue);
/**
-* Get the drop policy configured for a specific class-of-service instance.
+* Get the queue associated with the specific class-of-service
*
-* @param[in] cos_id class-of-service instance.
+* @param cos CoS handle
*
-* @retval Drop policy configured with the given
-* class-of-service
+* @retval Queue handle associated with the given class-of-service
+* @retval ODP_QUEUE_INVALID on failure, or if there are multiple queues, or if
+* the CoS action is ODP_COS_ACTION_DROP.
*/
-odp_cls_drop_t odp_cos_drop(odp_cos_t cos_id);
+odp_queue_t odp_cos_queue(odp_cos_t cos);
/**
- * Request to override per-port class of service
- * based on Layer-2 priority field if present.
+ * Get the number of queues linked with the specific class-of-service
+ *
+ * @param cos CoS handle
*
- * @param[in] pktio_in Ingress port identifier.
- * @param[in] num_qos Number of QoS levels, typically 8.
- * @param[in] qos_table Values of the Layer-2 QoS header field.
- * @param[in] cos_table Class-of-service assigned to each of the
- * allowed Layer-2 QOS levels.
- * @retval 0 on success
- * @retval <0 on failure
+ * @return Number of queues linked with the class-of-service.
*/
-int odp_cos_with_l2_priority(odp_pktio_t pktio_in,
- uint8_t num_qos,
- uint8_t qos_table[],
- odp_cos_t cos_table[]);
+uint32_t odp_cls_cos_num_queue(odp_cos_t cos);
/**
- * Request to override per-port class of service
- * based on Layer-3 priority field if present.
+ * Get the list of queue associated with the specific class-of-service
*
- * @param[in] pktio_in Ingress port identifier.
- * @param[in] num_qos Number of allowed Layer-3 QoS levels.
- * @param[in] qos_table Values of the Layer-3 QoS header field.
- * @param[in] cos_table Class-of-service assigned to each of the
- * allowed Layer-3 QOS levels.
- * @param[in] l3_preference when true, Layer-3 QoS overrides
- * L2 QoS when present.
+ * @param cos CoS handle
+ * @param[out] queue Array of queue handles associated with
+ * the class-of-service.
+ * @param num Maximum number of queue handles to output.
*
- * @retval 0 on success
- * @retval <0 on failure
- *
- * @note Optional.
+ * @return Number of queues linked with CoS
+ * @retval 0 on failure
*/
-int odp_cos_with_l3_qos(odp_pktio_t pktio_in,
- uint32_t num_qos,
- uint8_t qos_table[],
- odp_cos_t cos_table[],
- odp_bool_t l3_preference);
-
+uint32_t odp_cls_cos_queues(odp_cos_t cos, odp_queue_t queue[], uint32_t num);
/**
- * @typedef odp_cos_flow_set_t
- * Set of header fields that take part in flow signature hash calculation:
- * bit positions per odp_cos_hdr_flow_fields_t enumeration.
+ * Get statistics for a CoS
+ *
+ * The statistics counters are incremented for packets classified to the
+ * given CoS.
+ *
+ * Counters that are not supported are set to zero.
+ *
+ * It's implementation defined if odp_pktio_stats_reset() call affects these
+ * counters.
+ *
+ * @param cos CoS handle
+ * @param[out] stats Statistics structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
+int odp_cls_cos_stats(odp_cos_t cos, odp_cls_cos_stats_t *stats);
/**
- * @typedef odp_pmr_t
- * PMR - Packet Matching Rule
- * Up to 32 bit of ternary matching of one of the available header fields
+ * Get statistics for a queue assigned to a CoS
+ *
+ * The statistics counters are incremented only for packets originating from the
+ * given CoS. Queue handles can be requested with odp_cos_queue() and
+ * odp_cls_cos_queues().
+ *
+ * Counters not supported by the queue are set to zero.
+ *
+ * It's implementation defined if odp_pktio_stats_reset() call affects these
+ * counters.
+ *
+ * @param cos CoS handle
+ * @param queue Queue handle
+ * @param[out] stats Statistics structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
+int odp_cls_queue_stats(odp_cos_t cos, odp_queue_t queue,
+ odp_cls_queue_stats_t *stats);
/**
- * Packet Matching Rule field enumeration
- * for fields that may be used to calculate
- * the PMR, if present in a packet.
+ * Initialize packet matching rule parameters
+ *
+ * Initialize an odp_pmr_param_t to its default values for all fields
+ *
+ * @param param Address of the odp_pmr_param_t to be initialized
*/
-typedef enum {
- ODP_PMR_LEN, /**< Total length of received packet*/
- ODP_PMR_ETHTYPE_0, /**< Initial (outer)
- Ethertype only (*val=uint16_t)*/
- ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag
- (*val=uint16_t)*/
- ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) (*val=uint16_t) */
- ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) */
- ODP_PMR_DMAC, /**< destination MAC address (*val=uint64_t)*/
- ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header
- (*val=uint8_t) */
- ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies IPPROTO=17*/
- ODP_PMR_TCP_DPORT, /**< Destination TCP port implies IPPROTO=6*/
- ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t)*/
- ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t)*/
- ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t)*/
- ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t)*/
- ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16])*/
- ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16])*/
- ODP_PMR_IPSEC_SPI, /**< IPsec session identifier(*val=uint32_t)*/
- ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier
- (*val=uint32_t)*/
- ODP_PMR_CUSTOM_FRAME, /**< Custom match rule, offset from start of
- frame. The match is defined by the offset, the
- expected value, and its size. They must be
- applied before any other PMR.
- (*val=uint8_t[val_sz])*/
-
- /** Inner header may repeat above values with this offset */
- ODP_PMR_INNER_HDR_OFF = 32
-} odp_cls_pmr_term_t;
+void odp_cls_pmr_param_init(odp_pmr_param_t *param);
/**
- * Packet Matching Rule parameter structure
+ * Initialize packet matching rule creation option
+ *
+ * Initialize an odp_pmr_create_opt_t to its default values for all fields
+ *
+ * @param opt Address of the odp_pmr_create_opt_t to be initialized
*/
-typedef struct odp_pmr_param_t {
- odp_cls_pmr_term_t term; /**< Packet Matching Rule term */
-
- /** True if the value is range and false if match */
- odp_bool_t range_term;
+void odp_cls_pmr_create_opt_init(odp_pmr_create_opt_t *opt);
- /** Variant mappings for types of matches */
- union {
- /** Parameters for single-valued matches */
- struct {
- /** Value to be matched */
- const void *value;
-
- /** Masked set of bits to be matched */
- const void *mask;
- } match;
-
- /** Parameter for range value matches */
- struct {
- /** Start and End values are included in the range */
- /** start value of range */
- const void *val_start;
-
- /** End value of range */
- const void *val_end;
- } range;
- };
- uint32_t val_sz; /**< Size of the term value */
-
- uint32_t offset; /**< User-defined offset in packet
- Used if term == ODP_PMR_CUSTOM_FRAME only,
- ignored otherwise */
-} odp_pmr_param_t;
+/**
+ * Create Packet Matching Rule (PMR)
+ *
+ * Creates a PMR between source and destination Class of Service (CoS). A packet arriving to
+ * a CoS is matched against all the PMRs that define it as their source CoS. A PMR match moves
+ * the packet from the source to the destination CoS. If multiple PMRs of a CoS match with
+ * the packet, it is implementation specific which PMR is selected.
+ *
+ * A composite PMR is created when PMR parameters define more than one term. A composite PMR is
+ * considered to match only if a packet matches with all its terms. It is implementation specific
+ * which term combinations are supported as composite PMRs. When creating a composite PMR,
+ * application should check the return value and perform appropriate fallback actions if the create
+ * call returns failure. See odp_cls_capability_t::max_pmr and
+ * odp_cls_capability_t::max_terms_per_pmr for related capabilities.
+ *
+ * Use odp_cls_pmr_param_init() to initialize parameters into their default values.
+ *
+ * PMRs created with this function are equivant to PMRs created through odp_cls_pmr_create_opt()
+ * with the same PMR terms and with all additional options set to their default values (e.g.
+ * CLS mark is set to zero in all matching packets).
+ *
+ * @param terms Array of odp_pmr_param_t entries, one entry per term
+ * @param num_terms Number of terms in the PMR.
+ * @param src_cos source CoS handle
+ * @param dst_cos destination CoS handle
+ *
+ * @return PMR handle on success
+ * @retval ODP_PMR_INVALID on failure
+ */
+odp_pmr_t odp_cls_pmr_create(const odp_pmr_param_t *terms, int num_terms,
+ odp_cos_t src_cos, odp_cos_t dst_cos);
/**
- * Initialize packet matching rule parameters
+ * Create a packet matching rule with options
*
- * Initialize an odp_pmr_param_t to its default values for all fields
+ * Similar to odp_cls_pmr_create() function with additional PMR creation
+ * options specified through odp_pmr_create_opt_t.
+ *
+ * Use odp_cls_pmr_create_opt_init() to initialize options into their default
+ * values.
+ *
+ * @param opt points to PMR create options
+ * @param src_cos source CoS handle
+ * @param dst_cos destination CoS handle
+ *
+ * @return Handle to the Packet Match Rule.
+ * @retval ODP_PMR_INVALID on failure
*
- * @param param Address of the odp_pmr_param_t to be initialized
*/
-void odp_cls_pmr_param_init(odp_pmr_param_t *param);
+odp_pmr_t odp_cls_pmr_create_opt(const odp_pmr_create_opt_t *opt,
+ odp_cos_t src_cos, odp_cos_t dst_cos);
/**
- * Create a packet match rule between source and destination class of service.
- * This packet matching rule is applied on all packets arriving at the source
- * class of service and packets satisfying this PMR are sent to the destination
- * class of service.
- * A composite PMR rule is created when the number of terms in the match rule
- * is more than one. The composite rule is considered as matching only if
- * the packet satisfies all the terms in Packet Match Rule.
- * The underlying platform may not support all or any specific combination
- * of value match rules, and the application should take care
- * of inspecting the return value when installing such rules, and perform
- * appropriate fallback action.
- *
- * @param[in] terms Array of odp_pmr_param_t entries, one entry per
- * term desired.
- * @param[in] num_terms Number of terms in the match rule.
- * @param[in] src_cos source CoS handle
- * @param[in] dst_cos destination CoS handle
- *
- * @return Handle to the Packet Match Rule.
- * @retval ODP_PMR_INVAL on failure
+ * Create multiple packet matching rules
+ *
+ * Otherwise like odp_cls_pmr_create_opt(), but creates multiple rules with a
+ * single call. The output PMR handles are written in the same order as input
+ * parameters. A single odp_cls_pmr_create_multi() call is equivalent to calling
+ * odp_cls_pmr_create_opt() 'num' times in row.
+ *
+ * Each parameter array must contain 'num' elements.
+ *
+ * @param opt Array of PMR create options
+ * @param src_cos Array of source CoS handles
+ * @param dst_cos Array of destination CoS handles
+ * @param[out] pmr Array of PMR handles for output
+ * @param num Number of packet matching rules to create
+ *
+ * @return Number of PMRs actually created (0 ... num)
+ * @retval <0 on failure
*/
-odp_pmr_t odp_cls_pmr_create(const odp_pmr_param_t *terms, int num_terms,
- odp_cos_t src_cos, odp_cos_t dst_cos);
+int odp_cls_pmr_create_multi(const odp_pmr_create_opt_t opt[],
+ odp_cos_t src_cos[], odp_cos_t dst_cos[],
+ odp_pmr_t pmr[], int num);
/**
* Function to destroy a packet match rule
+ *
* Destroying a PMR removes the link between the source and destination
* class of service and this PMR will no longer be applied for packets arriving
- * at the source class of service. All the resource associated with the PMR
- * be release but the class of service will remain intact.
+ * at the source class of service. All the resources associated with the PMR
+ * will be released but the class of service will remain intact.
+ *
* Depending on the implementation details, destroying a composite rule
* may not guarantee the availability of hardware resources to create the
* same or essentially similar rule.
*
- * @param[in] pmr_id Identifier of the PMR to be destroyed
+ * @param pmr Handle of the PMR to be destroyed
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-int odp_cls_pmr_destroy(odp_pmr_t pmr_id);
+int odp_cls_pmr_destroy(odp_pmr_t pmr);
/**
-* Assigns a packet pool for a specific class of service.
+ * Destroy multiple packet matching rules
+ *
+ * Otherwise like odp_cls_pmr_destroy(), but destroys multiple PMRs with a
+ * single call.
+ *
+ * @param pmr Array of PMR handles
+ * @param num Number of PMRs to destroy
+ *
+ * @retval Number of PMRs actually destroyed (1 ... num)
+ * @retval <0 on failure
+ */
+int odp_cls_pmr_destroy_multi(odp_pmr_t pmr[], int num);
+
+/**
+* Assigns a packet pool for a specific class of service
+*
* All the packets belonging to the given class of service will
* be allocated from the assigned packet pool.
* The packet pool associated with class of service will supersede the
* packet pool associated with the pktio interface.
*
-* @param cos_id class of service handle
-* @param pool_id packet pool handle
+* @param cos CoS handle
+* @param pool_id Packet pool handle
*
-* @retval 0 on success
-* @retval <0 on failure
+* @retval 0 on success
+* @retval <0 on failure
*/
-int odp_cls_cos_pool_set(odp_cos_t cos_id, odp_pool_t pool_id);
+int odp_cls_cos_pool_set(odp_cos_t cos, odp_pool_t pool_id);
/**
* Get the pool associated with the given class of service
*
-* @param cos_id class of service handle
+* @param cos CoS handle
*
-* @retval pool handle of the associated pool
-* @retval ODP_POOL_INVALID if no associated pool found or
-* in case of an error
+* @retval pool handle of the associated pool
+* @retval ODP_POOL_INVALID on failure, or if the pool has not been set
*/
-odp_pool_t odp_cls_cos_pool(odp_cos_t cos_id);
+odp_pool_t odp_cls_cos_pool(odp_cos_t cos);
/**
* Get printable value for an odp_cos_t
*
- * @param hdl odp_cos_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * @param cos CoS handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
*
* @note This routine is intended to be used for diagnostic purposes
* to enable applications to generate a printable value that represents
* an odp_cos_t handle.
*/
-uint64_t odp_cos_to_u64(odp_cos_t hdl);
+uint64_t odp_cos_to_u64(odp_cos_t cos);
/**
* Get printable value for an odp_pmr_t
*
- * @param hdl odp_pmr_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * @param pmr odp_pmr_t handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
*
* @note This routine is intended to be used for diagnostic purposes
* to enable applications to generate a printable value that represents
* an odp_pmr_t handle.
*/
-uint64_t odp_pmr_to_u64(odp_pmr_t hdl);
+uint64_t odp_pmr_to_u64(odp_pmr_t pmr);
+
+/**
+ * Print classifier info
+ *
+ * Print implementation defined information about classifier. The information is
+ * intended to be used for debugging.
+ */
+void odp_cls_print_all(void);
/**
* @}
diff --git a/include/odp/api/spec/comp.h b/include/odp/api/spec/comp.h
new file mode 100644
index 000000000..49ccf4509
--- /dev/null
+++ b/include/odp/api/spec/comp.h
@@ -0,0 +1,613 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP Compression
+ */
+
+#ifndef ODP_API_SPEC_COMP_H_
+#define ODP_API_SPEC_COMP_H_
+
+#include <odp/visibility_begin.h>
+
+#include <odp/api/event_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/std_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_compression ODP COMP
+ * Data compression and decompression.
+ *
+ * Hash calculation may be combined with de-/compression operations.
+ * @{
+ */
+
+/**
+ * @def ODP_COMP_SESSION_INVALID
+ * Invalid session handle
+ */
+
+/**
+ * @typedef odp_comp_session_t
+ * Compression/Decompression session handle
+ */
+
+/**
+ * Compression operation mode
+ */
+typedef enum {
+ /** Synchronous Compression operation
+ *
+ * Application uses synchronous operation,
+ * which outputs all results on function return.
+ * */
+ ODP_COMP_OP_MODE_SYNC,
+
+ /** Asynchronous Compression operation
+ *
+ * Application uses asynchronous operation,
+ * which return results via events.
+ * */
+ ODP_COMP_OP_MODE_ASYNC
+} odp_comp_op_mode_t;
+
+/**
+ * Compression operation type.
+ */
+typedef enum {
+ /** Operation type - Compress */
+ ODP_COMP_OP_COMPRESS,
+
+ /** Operation type - Decompress */
+ ODP_COMP_OP_DECOMPRESS
+} odp_comp_op_t;
+
+/**
+ * Compression hash algorithms
+ */
+typedef enum {
+ /** No hash algorithm selected. */
+ ODP_COMP_HASH_ALG_NONE,
+
+ /** SHA-1 hash algorithm. */
+ ODP_COMP_HASH_ALG_SHA1,
+
+ /** SHA-2 hash algorithm 256-bit digest length. */
+ ODP_COMP_HASH_ALG_SHA256
+} odp_comp_hash_alg_t;
+
+/**
+ * Compression algorithms
+ *
+ */
+typedef enum {
+ /** No algorithm specified. Added for testing purpose. */
+ ODP_COMP_ALG_NULL,
+
+ /** DEFLATE - RFC1951 */
+ ODP_COMP_ALG_DEFLATE,
+
+ /** ZLIB - RFC1950 */
+ ODP_COMP_ALG_ZLIB,
+
+ /** LZS */
+ ODP_COMP_ALG_LZS
+} odp_comp_alg_t;
+
+/**
+ * Compression operation status codes
+ */
+typedef enum {
+ /** Operation completed successfully*/
+ ODP_COMP_STATUS_SUCCESS,
+
+ /** Operation terminated due to insufficient output buffer */
+ ODP_COMP_STATUS_OUT_OF_SPACE_TERM,
+
+ /** Operation failure */
+ ODP_COMP_STATUS_FAILURE,
+} odp_comp_status_t;
+
+/**
+ * Hash algorithms in a bit field structure
+ */
+typedef union odp_comp_hash_algos_t {
+ /** hash algorithms */
+ struct {
+ /** ODP_COMP_HASH_ALG_NONE */
+ uint32_t none : 1;
+
+ /** ODP_COMP_HASH_ALG_SHA1 */
+ uint32_t sha1 : 1;
+
+ /** ODP_COMP_HASH_ALG_SHA256 */
+ uint32_t sha256 : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure.
+ */
+ uint32_t all_bits;
+} odp_comp_hash_algos_t;
+
+/**
+ * Compression algorithms in a bit field structure
+ */
+typedef union odp_comp_algos_t {
+ /** Compression algorithms */
+ struct {
+ /** ODP_COMP_ALG_NULL */
+ uint32_t null : 1;
+
+ /** ODP_COMP_ALG_DEFLATE */
+ uint32_t deflate : 1;
+
+ /** ODP_COMP_ALG_ZLIB */
+ uint32_t zlib : 1;
+
+ /** ODP_COMP_ALG_LZS */
+ uint32_t lzs : 1;
+ } bit;
+
+ /** All bits of the bit field structure
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure.
+ */
+ uint32_t all_bits;
+} odp_comp_algos_t;
+
+/**
+ * Compression Interface Capabilities
+ */
+typedef struct odp_comp_capability_t {
+ /** Maximum number of sessions */
+ uint32_t max_sessions;
+
+ /** Supported compression algorithms */
+ odp_comp_algos_t comp_algos;
+
+ /** Supported hash algorithms */
+ odp_comp_hash_algos_t hash_algos;
+
+ /** Synchronous compression mode support (ODP_COMP_OP_MODE_SYNC) */
+ odp_support_t sync;
+
+ /** Asynchronous compression mode support (ODP_COMP_OP_MODE_ASYNC) */
+ odp_support_t async;
+} odp_comp_capability_t;
+
+/**
+ * Hash algorithm capabilities
+ */
+typedef struct odp_comp_hash_alg_capability_t {
+ /** Digest length in bytes */
+ uint32_t digest_len;
+} odp_comp_hash_alg_capability_t;
+
+/**
+ * Compression algorithm capabilities
+ */
+typedef struct odp_comp_alg_capability_t {
+ /** Maximum compression level supported by implementation of this
+ * algorithm. Indicates number of compression levels supported by
+ * implementation. Valid range from (1 ... max_level)
+ */
+ uint32_t max_level;
+
+ /** Supported hash algorithms */
+ odp_comp_hash_algos_t hash_algo;
+
+ /** Compression ratio
+ * Optimal compression operation ratio for this algorithm.
+ * This is an estimate of maximum compression operation output for this
+ * algorithm. It is expressed as a percentage of maximum expected
+ * output data size with respect to input data size.
+ * i.e a value of 200% denotes the output data is 2x times the input
+ * data size. This is an optimal/most case estimate and it is possible
+ * that the percentage of output data produced might be greater
+ * than this value.
+ *
+ * @see odp_percent_t
+ */
+ odp_percent_t compression_ratio;
+} odp_comp_alg_capability_t;
+
+/**
+ * Compression Huffman type. Used by DEFLATE algorithm
+ */
+typedef enum odp_comp_huffman_code {
+ /** Fixed Huffman code */
+ ODP_COMP_HUFFMAN_FIXED,
+
+ /** Dynamic Huffman code */
+ ODP_COMP_HUFFMAN_DYNAMIC,
+
+ /** Default huffman code selected by implementation */
+ ODP_COMP_HUFFMAN_DEFAULT,
+} odp_comp_huffman_code_t;
+
+/**
+ * Compression DEFLATEe algorithm parameters.
+ * Also initialized by other deflate based algorithms , ex. ZLIB
+ */
+typedef struct odp_comp_deflate_param {
+ /**
+ * Compression level
+ *
+ * Valid range is integer between (0 ... max_level)
+ * level supported by the implementation.
+ *
+ * where,
+ * 0 - implementation default
+ *
+ * 1 - fastest compression i.e. output produced at
+ * best possible speed at the expense of compression quality
+ *
+ * max_level - High quality compression
+ *
+ * @see odp_comp_alg_capability_t::max_level
+ */
+ uint32_t comp_level;
+
+ /** huffman code to use */
+ odp_comp_huffman_code_t huffman_code;
+} odp_comp_deflate_param_t;
+
+/**
+ * Compression algorithm specific parameters
+ */
+typedef union odp_comp_alg_param_t {
+ /** deflate parameter */
+ odp_comp_deflate_param_t deflate;
+
+ /** Struct for defining zlib algorithm parameters */
+ struct {
+ /** deflate algo params */
+ odp_comp_deflate_param_t deflate;
+ } zlib;
+} odp_comp_alg_param_t;
+
+ /**
+ * Compression session creation parameters
+ */
+typedef struct odp_comp_session_param_t {
+ /** Compression operation type Compress vs Decompress */
+ odp_comp_op_t op;
+
+ /** Compression operation mode
+ *
+ * Operation mode Synchronous vs Asynchronous
+ *
+ * @see odp_comp_op(), odp_comp_op_enq()
+ */
+ odp_comp_op_mode_t mode;
+
+ /** Compression algorithm
+ *
+ * @see odp_comp_capability()
+ */
+ odp_comp_alg_t comp_algo;
+
+ /** Hash algorithm
+ *
+ * @see odp_comp_alg_capability()
+ */
+ odp_comp_hash_alg_t hash_algo;
+
+ /** parameters specific to compression */
+ odp_comp_alg_param_t alg_param;
+
+ /** Session packet enqueue ordering
+ * Boolean to indicate if packet enqueue ordering is required per
+ * session. Valid only for Asynchronous operation mode
+ * (ODP_COMP_OP_MODE_ASYNC). Packet order is always maintained for
+ * synchronous operation mode (ODP_COMP_OP_MODE_SYNC)
+ *
+ * true: packet session enqueue order maintained
+ *
+ * false: packet session enqueue order is not maintained
+ *
+ * @note: By disabling packet order requirement, performance oriented
+ * application can leverage HW offered parallelism to increase operation
+ * performance.
+ */
+ odp_bool_t packet_order;
+
+ /** Destination queue for compression operations result.
+ * Results are enqueued as ODP_EVENT_PACKET with subtype
+ * ODP_EVENT_PACKET_COMP
+ */
+ odp_queue_t compl_queue;
+} odp_comp_session_param_t;
+
+/**
+ * Compression packet operation result
+ */
+typedef struct odp_comp_packet_result_t {
+ /** Operation status code */
+ odp_comp_status_t status;
+
+ /** Input packet handle */
+ odp_packet_t pkt_in;
+
+ /** Output packet data range
+ * Specifies offset and length of data resulting from compression
+ * operation. When hashing is configured output_data_range.len equals
+ * length of output data + length of digest.
+ */
+ odp_packet_data_range_t output_data_range;
+} odp_comp_packet_result_t;
+
+/**
+ * Compression per packet operation parameters
+ */
+typedef struct odp_comp_packet_op_param_t {
+ /** Session handle */
+ odp_comp_session_t session;
+
+ /** Input data range to process. where,
+ *
+ * offset - starting offset
+ * length - length of data for compression operation
+ * */
+ odp_packet_data_range_t in_data_range;
+
+ /** Output packet data range.
+ * Indicates where processed packet will be written. where,
+ *
+ * offset - starting offset
+ * length - length of buffer available for output
+ *
+ * Output packet data is not modified outside of this provided data
+ * range. If output data length is not sufficient for compression
+ * operation ODP_COMP_STATUS_OUT_OF_SPACE_TERM error will occur
+ */
+ odp_packet_data_range_t out_data_range;
+} odp_comp_packet_op_param_t;
+
+/**
+ * Query compression capabilities
+ *
+ * Output compression capabilities on success.
+ *
+ * @param[out] capa Pointer to capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_comp_capability(odp_comp_capability_t *capa);
+
+/**
+ * Query supported compression algorithm capabilities
+ *
+ * Output algorithm capabilities.
+ *
+ * @param comp Compression algorithm
+ * @param[out] capa Compression algorithm capability
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_comp_alg_capability(odp_comp_alg_t comp,
+ odp_comp_alg_capability_t *capa);
+
+/**
+ * Query supported hash algorithm capabilities
+ *
+ * Outputs all supported configuration options for the algorithm.
+ *
+ * @param hash Hash algorithm
+ * @param capa Hash algorithm capability
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_comp_hash_alg_capability(odp_comp_hash_alg_t hash,
+ odp_comp_hash_alg_capability_t *capa);
+
+/**
+ * Initialize compression session parameters
+ *
+ * Initialize an odp_comp_session_param_t to its default values for
+ * all fields.
+ *
+ * @param param Pointer to odp_comp_session_param_t to be initialized
+ */
+void odp_comp_session_param_init(odp_comp_session_param_t *param);
+
+/**
+ * Compression session creation
+ *
+ * Create a comp session according to the session parameters. Use
+ * odp_comp_session_param_init() to initialize parameters into their
+ * default values.
+ *
+ * @param param Session parameters
+ *
+ * @retval Comp session handle
+ * @retval ODP_COMP_SESSION_INVALID on failure
+ */
+odp_comp_session_t
+odp_comp_session_create(const odp_comp_session_param_t *param);
+
+/**
+ * Compression session destroy
+ *
+ * Destroy an unused session. Result is undefined if session is being used
+ * (i.e. asynchronous operation is in progress).
+ *
+ * @param session Session handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_comp_session_destroy(odp_comp_session_t session);
+
+/**
+ * Synchronous packet compression operation
+ *
+ * This operation does packet compression in synchronous mode. A successful
+ * operation returns the number of successfully processed input packets and
+ * updates the results in the corresponding output packets. Outputted packets
+ * contain compression results metadata (odp_comp_packet_result_t), which
+ * should be checked for operation status. Length of outputted data can be got
+ * from output_data_range.len.
+ *
+ * When hashing is configured along with compression operation the
+ * result is appended at the end of the output data, output_data_range.len
+ * equals length of output data + 'digest_len'. Processed data length
+ * can be computed by subtracting 'digest_len' from output_data_range.len where
+ * 'digest_len' can be queried from odp_comp_hash_alg_capability().
+ * Hash is always performed on plain text. Hash validation in decompression is
+ * performed by the application.
+ * For every input packet entry in 'pkt_in' array, application should pass
+ * corresponding valid output packet handle. If any error occurs during
+ * processing of packets, the API returns with number of entries successfully
+ * processed.
+ * Output packet metadatas like length or data pointer will not be updated.
+ *
+ * @param pkt_in Packets to be processed
+ * @param pkt_out Packet handle array for resulting packets
+ * @param num_pkt Number of packets to be processed
+ * @param param Operation parameters
+ *
+ * @return Number of input packets consumed (0 ... num_pkt)
+ * @retval <0 on failure
+ *
+ * @note The 'pkt_in','pkt_out'and 'param' arrays should be of same length,
+ * Results are undefined if otherwise.
+
+ * @note Same packet handle cannot be used as input and output parameter.
+ * In-place compression operation is not supported
+ */
+int odp_comp_op(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[]);
+
+/**
+ * Asynchronous packet compression operation
+ *
+ * This operation does packet compression in asynchronous mode. It processes
+ * packets otherwise identical to odp_comp_op(), but the resulting packets are
+ * enqueued to 'compl_queue' configured during session (odp_comp_session_t)
+ * creation. For every input packet entry in in_pkt array, user should pass
+ * corresponding valid output packet handle. On return, API returns with
+ * number of entries successfully submitted for operation.
+ *
+ * When hashing is configured along with compression operation the
+ * result is appended at the end of the output data, output_data_range.len
+ * equals length of output data + 'digest_len'. Processed data length
+ * can be computed by subtracting 'digest_len' from output_data_range.len where
+ * 'digest_len' can be queried from odp_comp_hash_alg_capability().
+ * Hash is always performed on plain text. Hash validation in decompression is
+ * performed by the application.
+ *
+ * In case of partially accepted array i.e.
+ * when number of packets returned < num_pkt, application may attempt to
+ * resubmit subsequent entries via calling any of the operation API.
+ *
+ * All the packets successfully enqueued will be submitted to 'compl_queue'
+ * after compression operation, Application should check 'status' of the
+ * operation in odp_comp_packet_result_t.
+ * Output packet metadatas like length or data pointer will not be updated.
+ *
+ * Please note it is always recommended that application using async mode,
+ * provide sufficiently large buffer size to avoid
+ * ODP_COMP_STATUS_OUT_OF_SPACE_TERM.
+ *
+ * @param pkt_in Packets to be processed
+ * @param pkt_out Packet handle array for resulting packets
+ * @param num_pkt Number of packets to be processed
+ * @param param Operation parameters
+ *
+ * @return Number of input packets enqueued (0 ... num_pkt)
+ * @retval <0 on failure
+ *
+ * @note The 'pkt_in','pkt_out'and 'param' arrays should be of same length,
+ * Results are undefined if otherwise.
+
+ * @note Same packet handle cannot be used as input and output parameter.
+ * In-place compression operation is not supported
+
+ * @see odp_comp_op(), odp_comp_packet_result()
+ */
+int odp_comp_op_enq(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[]);
+
+/**
+ * Get compression operation results from processed packet.
+ *
+ * Successful compression operations of all modes (ODP_COMP_OP_MODE_SYNC and
+ * ODP_COMP_OP_MODE_ASYNC) produce packets which contain compression result
+ * metadata. This function copies operation results from compression processed
+ * packet. Event subtype of this packet is ODP_EVENT_PACKET_COMP. Results are
+ * undefined if non-compression processed packet is passed as input.
+ *
+ * @param[out] result pointer to operation result for output
+ * @param packet compression processed packet (ODP_EVENT_PACKET_COMP)
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ */
+int odp_comp_result(odp_comp_packet_result_t *result, odp_packet_t packet);
+
+ /**
+ * Convert compression processed packet event to packet handle
+ *
+ * Get packet handle corresponding to processed packet event. Event subtype
+ * must be ODP_EVENT_PACKET_COMP. Compression operation results can be
+ * examined with odp_comp_result().
+ *
+ * @param event Event handle
+ *
+ * @return Valid Packet handle on success,
+ * @retval ODP_PACKET_INVALID on failure
+ *
+ * @see odp_event_subtype(), odp_comp_result()
+ *
+ */
+odp_packet_t odp_comp_packet_from_event(odp_event_t event);
+
+ /**
+ * Convert processed packet handle to event
+ *
+ * The packet handle must be an output of a compression operation
+ *
+ * @param pkt Packet handle from compression operation
+ * @return Event handle
+ */
+odp_event_t odp_comp_packet_to_event(odp_packet_t pkt);
+
+/**
+ * Get printable value for an odp_comp_session_t
+ *
+ * @param hdl odp_comp_session_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_comp_session_t handle.
+ */
+uint64_t odp_comp_session_to_u64(odp_comp_session_t hdl);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
+
diff --git a/include/odp/api/spec/compiler.h b/include/odp/api/spec/compiler.h
deleted file mode 100644
index c88350e2c..000000000
--- a/include/odp/api/spec/compiler.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * Compiler related
- */
-
-#ifndef ODP_API_COMPILER_H_
-#define ODP_API_COMPILER_H_
-#include <odp/visibility_begin.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_compiler_optim
- * Macro for old compilers
- * @{
- */
-
-/** @internal GNU compiler version */
-#define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
-
-/**
- * @internal
- * Compiler __builtin_bswap16() is not available on all platforms
- * until GCC 4.8.0 - work around this by offering __odp_builtin_bswap16()
- * Don't use this function directly, instead see odp_byteorder.h
- */
-#if GCC_VERSION < 40800
-#define __odp_builtin_bswap16(u16) ((((u16)&0x00ff) << 8)|(((u16)&0xff00) >> 8))
-#else
-#define __odp_builtin_bswap16(u16) __builtin_bswap16(u16)
-#endif
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <odp/visibility_end.h>
-#endif
diff --git a/include/odp/api/spec/cpu.h b/include/odp/api/spec/cpu.h
index 0f47e4798..9550354fa 100644
--- a/include/odp/api/spec/cpu.h
+++ b/include/odp/api/spec/cpu.h
@@ -1,18 +1,15 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP CPU API
*/
-#ifndef ODP_CPU_H_
-#define ODP_CPU_H_
+#ifndef ODP_API_SPEC_CPU_H_
+#define ODP_API_SPEC_CPU_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -22,10 +19,10 @@ extern "C" {
#include <odp/api/std_types.h>
/** @defgroup odp_cpu ODP CPU
+ * CPU cycle count, frequency, etc. information.
* @{
*/
-
/**
* CPU identifier
*
@@ -69,44 +66,48 @@ const char *odp_cpu_model_str_id(int id);
/**
* Current CPU frequency in Hz
*
- * Returns current frequency of this CPU
+ * Returns current frequency of this CPU. Returns zero if the frequency
+ * request is not supported.
*
* @return CPU frequency in Hz
- * @retval 0 on failure
+ * @retval 0 Not supported or a failure
*/
uint64_t odp_cpu_hz(void);
/**
* Current CPU frequency of a CPU (in Hz)
*
- * Returns current frequency of specified CPU
+ * Returns current frequency of the specified CPU. Returns zero if the frequency
+ * request is not supported.
*
* @param id CPU ID
*
* @return CPU frequency in Hz
- * @retval 0 on failure
+ * @retval 0 Not supported or a failure
*/
uint64_t odp_cpu_hz_id(int id);
/**
* Maximum CPU frequency in Hz
*
- * Returns maximum frequency of this CPU
+ * Returns the maximum frequency of this CPU. Returns zero if the frequency
+ * request is not supported.
*
* @return CPU frequency in Hz
- * @retval 0 on failure
+ * @retval 0 Not supported or a failure
*/
uint64_t odp_cpu_hz_max(void);
/**
* Maximum CPU frequency of a CPU (in Hz)
*
- * Returns maximum frequency of specified CPU
+ * Returns the maximum frequency of the specified CPU. Returns zero if the
+ * frequency request is not supported.
*
* @param id CPU ID
*
* @return CPU frequency in Hz
- * @retval 0 on failure
+ * @retval 0 Not supported or a failure
*/
uint64_t odp_cpu_hz_max_id(int id);
@@ -116,14 +117,15 @@ uint64_t odp_cpu_hz_max_id(int id);
* Return current CPU cycle count. Cycle count may not be reset at ODP init
* and thus may wrap back to zero between two calls. Use odp_cpu_cycles_max()
* to read the maximum count value after which it wraps. Cycle count frequency
- * follows the CPU frequency and thus may change at any time. The count may
- * advance in steps larger than one. Use odp_cpu_cycles_resolution() to read
- * the step size.
+ * follows the CPU frequency and thus may change at any time. Cycle count should
+ * not be used for time measurements due to the possibility of frequency
+ * variation. The count may advance in steps larger than one. Use
+ * odp_cpu_cycles_resolution() to read the step size.
*
- * @note Do not use CPU count for time measurements since the frequency may
- * vary.
+ * Returns zero if CPU cycle counter is not supported.
*
* @return Current CPU cycle count
+ * @retval 0 Not supported
*/
uint64_t odp_cpu_cycles(void);
@@ -144,9 +146,11 @@ uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1);
/**
* Maximum CPU cycle count
*
- * Maximum CPU cycle count value before it wraps back to zero.
+ * Maximum CPU cycle count value before it wraps back to zero. Returns zero
+ * if CPU cycle counter is not supported.
*
* @return Maximum CPU cycle count value
+ * @retval 0 Not supported
*/
uint64_t odp_cpu_cycles_max(void);
@@ -154,9 +158,11 @@ uint64_t odp_cpu_cycles_max(void);
* Resolution of CPU cycle count
*
* CPU cycle count may advance in steps larger than one. This function returns
- * resolution of odp_cpu_cycles() in CPU cycles.
+ * resolution of odp_cpu_cycles() in CPU cycles. Returns zero if CPU cycle
+ * counter is not supported.
*
* @return CPU cycle count resolution in CPU cycles
+ * @retval 0 Not supported
*/
uint64_t odp_cpu_cycles_resolution(void);
diff --git a/include/odp/api/spec/cpumask.h b/include/odp/api/spec/cpumask.h
index 22d8e8f24..a6c08e6c0 100644
--- a/include/odp/api/spec/cpumask.h
+++ b/include/odp/api/spec/cpumask.h
@@ -1,18 +1,16 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
*/
-
/**
* @file
*
* ODP CPU masks and enumeration
*/
-#ifndef ODP_API_CPUMASK_H_
-#define ODP_API_CPUMASK_H_
+#ifndef ODP_API_SPEC_CPUMASK_H_
+#define ODP_API_SPEC_CPUMASK_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -209,26 +207,36 @@ int odp_cpumask_last(const odp_cpumask_t *mask);
int odp_cpumask_next(const odp_cpumask_t *mask, int cpu);
/**
- * Default cpumask for worker threads
+ * Default CPU mask for worker threads
+ *
+ * Initializes CPU mask with the default set of CPUs available for worker threads. It's system
+ * specific if other CPUs may be used for worker threads as well. Parameter 'num' defines
+ * the maximum number of CPUs to be returned. Use zero for all available CPUs. The mask pointer
+ * may be set to NULL when CPU mask is not required.
*
- * Initializes cpumask with CPUs available for worker threads. Sets up to 'num'
- * CPUs and returns the count actually set. Use zero for all available CPUs.
+ * Returns the number of CPUs selected and written into the CPU mask (when not NULL).
*
* @param[out] mask CPU mask to initialize
- * @param num Number of worker threads, zero for all available CPUs
- * @return Actual number of CPUs used to create the mask
+ * @param num Maximum number of CPUs to return. Use zero for all available CPUs.
+ *
+ * @return Number of selected CPUs
*/
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num);
/**
- * Default cpumask for control threads
+ * Default CPU mask for control threads
+ *
+ * Initializes CPU mask with the default set of CPUs available for control threads. It's system
+ * specific if other CPUs may be used for control threads as well. Parameter 'num' defines
+ * the maximum number of CPUs to be returned. Use zero for all available CPUs. The mask pointer
+ * may be set to NULL when CPU mask is not required.
*
- * Initializes cpumask with CPUs available for control threads. Sets up to 'num'
- * CPUs and returns the count actually set. Use zero for all available CPUs.
+ * Returns the number of CPUs selected and written into the CPU mask (when not NULL).
*
* @param[out] mask CPU mask to initialize
- * @param num Number of control threads, zero for all available CPUs
- * @return Actual number of CPUs used to create the mask
+ * @param num Maximum number of CPUs to return. Use zero for all available CPUs.
+ *
+ * @return Number of selected CPUs
*/
int odp_cpumask_default_control(odp_cpumask_t *mask, int num);
diff --git a/include/odp/api/spec/crypto.h b/include/odp/api/spec/crypto.h
index 470cba050..8b7d1df54 100644
--- a/include/odp/api/spec/crypto.h
+++ b/include/odp/api/spec/crypto.h
@@ -1,532 +1,32 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
-
/**
* @file
*
* ODP crypto
*/
-#ifndef ODP_API_CRYPTO_H_
-#define ODP_API_CRYPTO_H_
+#ifndef ODP_API_SPEC_CRYPTO_H_
+#define ODP_API_SPEC_CRYPTO_H_
#include <odp/visibility_begin.h>
-#include <odp/api/deprecated.h>
+#include <odp/api/crypto_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/std_types.h>
#ifdef __cplusplus
extern "C" {
#endif
-#include <odp/api/packet.h>
-
-/** @defgroup odp_crypto ODP CRYPTO
- * Macros, enums, types and operations to utilise crypto.
+/** @addtogroup odp_crypto
+ * Data ciphering and authentication.
* @{
*/
/**
- * @def ODP_CRYPTO_SESSION_INVALID
- * Invalid session handle
- */
-
-/**
- * @typedef odp_crypto_session_t
- * Crypto API opaque session handle
- */
-
-/**
- * @typedef odp_crypto_compl_t
-* Crypto API completion event (platform dependent).
-*/
-
-/**
- * Crypto API operation mode
- */
-typedef enum {
- /** Synchronous, return results immediately */
- ODP_CRYPTO_SYNC,
- /** Asynchronous, return results via posted event */
- ODP_CRYPTO_ASYNC,
-} odp_crypto_op_mode_t;
-
-/**
- * Crypto API operation type
- */
-typedef enum {
- /** Encrypt and/or compute authentication ICV */
- ODP_CRYPTO_OP_ENCODE,
- /** Decrypt and/or verify authentication ICV */
- ODP_CRYPTO_OP_DECODE,
-} odp_crypto_op_t;
-
-/**
- * Crypto API cipher algorithm
- */
-typedef enum {
- /** No cipher algorithm specified */
- ODP_CIPHER_ALG_NULL,
-
- /** DES */
- ODP_CIPHER_ALG_DES,
-
- /** Triple DES with cipher block chaining */
- ODP_CIPHER_ALG_3DES_CBC,
-
- /** AES with cipher block chaining */
- ODP_CIPHER_ALG_AES_CBC,
-
- /** AES in Galois/Counter Mode
- *
- * @note Must be paired with cipher ODP_AUTH_ALG_AES_GCM
- */
- ODP_CIPHER_ALG_AES_GCM,
-
- /** @deprecated Use ODP_CIPHER_ALG_AES_CBC instead */
- ODP_DEPRECATE(ODP_CIPHER_ALG_AES128_CBC),
-
- /** @deprecated Use ODP_CIPHER_ALG_AES_GCM instead */
- ODP_DEPRECATE(ODP_CIPHER_ALG_AES128_GCM)
-
-} odp_cipher_alg_t;
-
-/**
- * Crypto API authentication algorithm
- */
-typedef enum {
- /** No authentication algorithm specified */
- ODP_AUTH_ALG_NULL,
-
- /** HMAC-MD5
- *
- * MD5 algorithm in HMAC mode
- */
- ODP_AUTH_ALG_MD5_HMAC,
-
- /** HMAC-SHA-1
- *
- * SHA-1 algorithm in HMAC mode
- */
- ODP_AUTH_ALG_SHA1_HMAC,
-
- /** HMAC-SHA-256
- *
- * SHA-256 algorithm in HMAC mode
- */
- ODP_AUTH_ALG_SHA256_HMAC,
-
- /** HMAC-SHA-512
- *
- * SHA-512 algorithm in HMAC mode
- */
- ODP_AUTH_ALG_SHA512_HMAC,
-
- /** AES in Galois/Counter Mode
- *
- * @note Must be paired with cipher ODP_CIPHER_ALG_AES_GCM
- */
- ODP_AUTH_ALG_AES_GCM,
-
- /** @deprecated Use ODP_AUTH_ALG_MD5_HMAC instead */
- ODP_DEPRECATE(ODP_AUTH_ALG_MD5_96),
-
- /** @deprecated Use ODP_AUTH_ALG_SHA256_HMAC instead */
- ODP_DEPRECATE(ODP_AUTH_ALG_SHA256_128),
-
- /** @deprecated Use ODP_AUTH_ALG_AES_GCM instead */
- ODP_DEPRECATE(ODP_AUTH_ALG_AES128_GCM)
-
-} odp_auth_alg_t;
-
-/**
- * Cipher algorithms in a bit field structure
- */
-typedef union odp_crypto_cipher_algos_t {
- /** Cipher algorithms */
- struct {
- /** ODP_CIPHER_ALG_NULL */
- uint32_t null : 1;
-
- /** ODP_CIPHER_ALG_DES */
- uint32_t des : 1;
-
- /** ODP_CIPHER_ALG_3DES_CBC */
- uint32_t trides_cbc : 1;
-
- /** ODP_CIPHER_ALG_AES_CBC */
- uint32_t aes_cbc : 1;
-
- /** ODP_CIPHER_ALG_AES_GCM */
- uint32_t aes_gcm : 1;
-
- /** @deprecated Use aes_cbc instead */
- uint32_t ODP_DEPRECATE(aes128_cbc) : 1;
-
- /** @deprecated Use aes_gcm instead */
- uint32_t ODP_DEPRECATE(aes128_gcm) : 1;
-
- } bit;
-
- /** All bits of the bit field structure
- *
- * This field can be used to set/clear all flags, or bitwise
- * operations over the entire structure. */
- uint32_t all_bits;
-} odp_crypto_cipher_algos_t;
-
-/**
- * Authentication algorithms in a bit field structure
- */
-typedef union odp_crypto_auth_algos_t {
- /** Authentication algorithms */
- struct {
- /** ODP_AUTH_ALG_NULL */
- uint32_t null : 1;
-
- /** ODP_AUTH_ALG_MD5_HMAC */
- uint32_t md5_hmac : 1;
-
- /** ODP_AUTH_ALG_SHA1_HMAC */
- uint32_t sha1_hmac : 1;
-
- /** ODP_AUTH_ALG_SHA256_HMAC */
- uint32_t sha256_hmac : 1;
-
- /** ODP_AUTH_ALG_SHA512_HMAC */
- uint32_t sha512_hmac : 1;
-
- /** ODP_AUTH_ALG_AES_GCM */
- uint32_t aes_gcm : 1;
-
- /** @deprecated Use md5_hmac instead */
- uint32_t ODP_DEPRECATE(md5_96) : 1;
-
- /** @deprecated Use sha256_hmac instead */
- uint32_t ODP_DEPRECATE(sha256_128) : 1;
-
- /** @deprecated Use aes_gcm instead */
- uint32_t ODP_DEPRECATE(aes128_gcm) : 1;
-
- } bit;
-
- /** All bits of the bit field structure
- *
- * This field can be used to set/clear all flags, or bitwise
- * operations over the entire structure. */
- uint32_t all_bits;
-} odp_crypto_auth_algos_t;
-
-/**
- * Crypto API key structure
- */
-typedef struct odp_crypto_key {
- /** Key data */
- uint8_t *data;
-
- /** Key length in bytes */
- uint32_t length;
-
-} odp_crypto_key_t;
-
-/**
- * Crypto API IV structure
- */
-typedef struct odp_crypto_iv {
- /** IV data */
- uint8_t *data;
-
- /** IV length in bytes */
- uint32_t length;
-
-} odp_crypto_iv_t;
-
-/**
- * Crypto API data range specifier
- *
- * @deprecated Use odp_packet_data_range_t instead
- */
-typedef odp_packet_data_range_t ODP_DEPRECATE(odp_crypto_data_range_t);
-
-/**
- * Crypto API session creation parameters
- */
-typedef struct odp_crypto_session_param_t {
- /** Encode vs. decode operation */
- odp_crypto_op_t op;
-
- /** Authenticate cipher vs. plain text
- *
- * Controls ordering of authentication and cipher operations,
- * and is relative to the operation (encode vs decode). When encoding,
- * TRUE indicates the authentication operation should be performed
- * after the cipher operation else before. When decoding, TRUE
- * indicates the reverse order of operation.
- *
- * true: Authenticate cipher text
- * false: Authenticate plain text
- */
- odp_bool_t auth_cipher_text;
-
- /** Preferred sync vs. async */
- odp_crypto_op_mode_t pref_mode;
-
- /** Cipher algorithm
- *
- * Use odp_crypto_capability() for supported algorithms.
- */
- odp_cipher_alg_t cipher_alg;
-
- /** Cipher key
- *
- * Use odp_crypto_cipher_capa() for supported key and IV lengths.
- */
- odp_crypto_key_t cipher_key;
-
- /** Cipher Initialization Vector (IV) */
- odp_crypto_iv_t iv;
-
- /** Authentication algorithm
- *
- * Use odp_crypto_capability() for supported algorithms.
- */
- odp_auth_alg_t auth_alg;
-
- /** Authentication key
- *
- * Use odp_crypto_auth_capability() for supported key lengths.
- */
- odp_crypto_key_t auth_key;
-
- /** Authentication digest length in bytes
- *
- * Use odp_crypto_auth_capability() for supported digest lengths.
- */
- uint32_t auth_digest_len;
-
- /** Async mode completion event queue
- *
- * When odp_crypto_operation() is asynchronous, the completion queue is
- * used to return the completion status of the operation to the
- * application.
- */
- odp_queue_t compl_queue;
-
- /** Output pool
- *
- * When the output packet is not specified during the call to
- * odp_crypto_operation(), the output packet will be allocated
- * from this pool.
- */
- odp_pool_t output_pool;
-
-} odp_crypto_session_param_t;
-
-/** @deprecated Use odp_crypto_session_param_t instead */
-typedef odp_crypto_session_param_t ODP_DEPRECATE(odp_crypto_session_params_t);
-
-/**
- * Crypto API per packet operation parameters
- */
-typedef struct odp_crypto_op_param_t {
- /** Session handle from creation */
- odp_crypto_session_t session;
-
- /** User context */
- void *ctx;
-
- /** Input packet
- *
- * Specifies the input packet for the crypto operation. When the
- * 'out_pkt' variable is set to ODP_PACKET_INVALID (indicating a new
- * packet should be allocated for the resulting packet).
- */
- odp_packet_t pkt;
-
- /** Output packet
- *
- * Both "in place" (the original packet 'pkt' is modified) and
- * "copy" (the packet is replicated to a new packet which contains
- * the modified data) modes are supported. The "in place" mode of
- * operation is indicated by setting 'out_pkt' equal to 'pkt'.
- * For the copy mode of operation, setting 'out_pkt' to a valid packet
- * value indicates the caller wishes to specify the destination packet.
- * Setting 'out_pkt' to ODP_PACKET_INVALID indicates the caller wishes
- * the destination packet be allocated from the output pool specified
- * during session creation.
- */
- odp_packet_t out_pkt;
-
- /** Override session IV pointer */
- uint8_t *override_iv_ptr;
-
- /** Offset from start of packet for hash result
- *
- * Specifies the offset where the hash result is to be stored. In case
- * of decode sessions, input hash values will be read from this offset,
- * and overwritten with hash results. If this offset lies within
- * specified 'auth_range', implementation will mute this field before
- * calculating the hash result.
- */
- uint32_t hash_result_offset;
-
- /** Additional Authenticated Data (AAD) */
- struct {
- /** Pointer to ADD */
- uint8_t *ptr;
-
- /** AAD length in bytes. Use odp_crypto_auth_capability() for
- * supported AAD lengths. */
- uint32_t length;
- } aad;
-
- /** Data range to apply cipher */
- odp_packet_data_range_t cipher_range;
-
- /** Data range to authenticate */
- odp_packet_data_range_t auth_range;
-
-} odp_crypto_op_param_t;
-
-/** @deprecated Use odp_crypto_op_param_t instead */
-typedef odp_crypto_op_param_t ODP_DEPRECATE(odp_crypto_op_params_t);
-
-/**
- * Crypto API session creation return code
- */
-typedef enum {
- /** Session created */
- ODP_CRYPTO_SES_CREATE_ERR_NONE,
- /** Creation failed, no resources */
- ODP_CRYPTO_SES_CREATE_ERR_ENOMEM,
- /** Creation failed, bad cipher params */
- ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER,
- /** Creation failed, bad auth params */
- ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH,
-} odp_crypto_ses_create_err_t;
-
-/**
- * Crypto API algorithm return code
- */
-typedef enum {
- /** Algorithm successful */
- ODP_CRYPTO_ALG_ERR_NONE,
- /** Invalid data block size */
- ODP_CRYPTO_ALG_ERR_DATA_SIZE,
- /** Key size invalid for algorithm */
- ODP_CRYPTO_ALG_ERR_KEY_SIZE,
- /** Computed ICV value mismatch */
- ODP_CRYPTO_ALG_ERR_ICV_CHECK,
- /** IV value not specified */
- ODP_CRYPTO_ALG_ERR_IV_INVALID,
-} odp_crypto_alg_err_t;
-
-/**
- * Crypto API hardware centric return code
- */
-typedef enum {
- /** Operation completed successfully */
- ODP_CRYPTO_HW_ERR_NONE,
- /** Error detected during DMA of data */
- ODP_CRYPTO_HW_ERR_DMA,
- /** Operation failed due to pool depletion */
- ODP_CRYPTO_HW_ERR_BP_DEPLETED,
-} odp_crypto_hw_err_t;
-
-/**
- * Cryto API per packet operation completion status
- */
-typedef struct odp_crypto_compl_status {
- /** Algorithm specific return code */
- odp_crypto_alg_err_t alg_err;
-
- /** Hardware specific return code */
- odp_crypto_hw_err_t hw_err;
-
-} odp_crypto_compl_status_t;
-
-/**
- * Crypto API operation result
- */
-typedef struct odp_crypto_op_result {
- /** Request completed successfully */
- odp_bool_t ok;
-
- /** User context from request */
- void *ctx;
-
- /** Output packet */
- odp_packet_t pkt;
-
- /** Cipher status */
- odp_crypto_compl_status_t cipher_status;
-
- /** Authentication status */
- odp_crypto_compl_status_t auth_status;
-
-} odp_crypto_op_result_t;
-
-/**
- * Crypto capabilities
- */
-typedef struct odp_crypto_capability_t {
- /** Maximum number of crypto sessions */
- uint32_t max_sessions;
-
- /** Supported cipher algorithms */
- odp_crypto_cipher_algos_t ciphers;
-
- /** Cipher algorithms implemented with HW offload */
- odp_crypto_cipher_algos_t hw_ciphers;
-
- /** Supported authentication algorithms */
- odp_crypto_auth_algos_t auths;
-
- /** Authentication algorithms implemented with HW offload */
- odp_crypto_auth_algos_t hw_auths;
-
-} odp_crypto_capability_t;
-
-/**
- * Cipher algorithm capabilities
- */
-typedef struct odp_crypto_cipher_capability_t {
- /** Key length in bytes */
- uint32_t key_len;
-
- /** IV length in bytes */
- uint32_t iv_len;
-
-} odp_crypto_cipher_capability_t;
-
-/**
- * Authentication algorithm capabilities
- */
-typedef struct odp_crypto_auth_capability_t {
- /** Digest length in bytes */
- uint32_t digest_len;
-
- /** Key length in bytes */
- uint32_t key_len;
-
- /** Additional Authenticated Data (AAD) lengths */
- struct {
- /** Minimum AAD length in bytes */
- uint32_t min;
-
- /** Maximum AAD length in bytes */
- uint32_t max;
-
- /** Increment of supported lengths between min and max
- * (in bytes) */
- uint32_t inc;
- } aad_len;
-
-} odp_crypto_auth_capability_t;
-
-/**
* Query crypto capabilities
*
* Outputs crypto capabilities on success.
@@ -582,16 +82,20 @@ int odp_crypto_auth_capability(odp_auth_alg_t auth,
*
* Create a crypto session according to the session parameters. Use
* odp_crypto_session_param_init() to initialize parameters into their
- * default values.
+ * default values. If call ends up with an error no new session will be
+ * created.
*
- * @param param Session parameters
- * @param session Created session else ODP_CRYPTO_SESSION_INVALID
- * @param status Failure code if unsuccessful
+ * The parameter structure as well as the key and IV data pointed to by it
+ * can be freed after the call.
+ *
+ * @param param Session parameters
+ * @param[out] session Created session else ODP_CRYPTO_SESSION_INVALID
+ * @param[out] status Failure code if unsuccessful
*
* @retval 0 on success
* @retval <0 on failure
*/
-int odp_crypto_session_create(odp_crypto_session_param_t *param,
+int odp_crypto_session_create(const odp_crypto_session_param_t *param,
odp_crypto_session_t *session,
odp_crypto_ses_create_err_t *status);
@@ -609,97 +113,217 @@ int odp_crypto_session_create(odp_crypto_session_param_t *param,
int odp_crypto_session_destroy(odp_crypto_session_t session);
/**
- * Return crypto completion handle that is associated with event
- *
- * Note: any invalid parameters will cause undefined behavior and may cause
- * the application to abort or crash.
+ * Get printable value for an odp_crypto_session_t
*
- * @param ev An event of type ODP_EVENT_CRYPTO_COMPL
+ * @param hdl odp_crypto_session_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
*
- * @return crypto completion handle
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_crypto_session_t handle.
*/
-odp_crypto_compl_t odp_crypto_compl_from_event(odp_event_t ev);
+uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl);
/**
- * Convert crypto completion handle to event handle
+ * Initialize crypto session parameters
*
- * @param completion_event Completion event to convert to generic event
+ * Initialize an odp_crypto_session_param_t to its default values for
+ * all fields.
*
- * @return Event handle
+ * @param param Pointer to odp_crypto_session_param_t to be initialized
*/
-odp_event_t odp_crypto_compl_to_event(odp_crypto_compl_t completion_event);
+void odp_crypto_session_param_init(odp_crypto_session_param_t *param);
/**
- * Release crypto completion event
+ * Return crypto processed packet that is associated with event
*
- * @param completion_event Completion event we are done accessing
- */
-void odp_crypto_compl_free(odp_crypto_compl_t completion_event);
-
-/**
- * Crypto per packet operation
+ * Get packet handle to an crypto processed packet event. Event subtype must be
+ * ODP_EVENT_PACKET_CRYPTO. Crypto operation results can be examined with
+ * odp_crypto_result().
*
- * Performs the cryptographic operations specified during session creation
- * on the packet. If the operation is performed synchronously, "posted"
- * will return FALSE and the result of the operation is immediately available.
- * If "posted" returns TRUE the result will be delivered via the completion
- * queue specified when the session was created.
+ * Note: any invalid parameters will cause undefined behavior and may cause
+ * the application to abort or crash.
*
- * @param param Operation parameters
- * @param posted Pointer to return posted, TRUE for async operation
- * @param result Results of operation (when posted returns FALSE)
+ * @param ev Event handle
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @return Packet handle
*/
-int odp_crypto_operation(odp_crypto_op_param_t *param,
- odp_bool_t *posted,
- odp_crypto_op_result_t *result);
+odp_packet_t odp_crypto_packet_from_event(odp_event_t ev);
/**
- * Crypto per packet operation query result from completion event
+ * Convert crypto packet handle to event
+ *
+ * The packet handle must be an output of an crypto operation.
*
- * @param completion_event Event containing operation results
- * @param result Pointer to result structure
+ * @param pkt Packet handle from crypto operation
+ *
+ * @return Event handle
*/
-void odp_crypto_compl_result(odp_crypto_compl_t completion_event,
- odp_crypto_op_result_t *result);
+odp_event_t odp_crypto_packet_to_event(odp_packet_t pkt);
/**
- * Get printable value for an odp_crypto_session_t
+ * Get crypto operation results from a crypto processed packet
*
- * @param hdl odp_crypto_session_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * Crypto operations of all types (SYNC and ASYNC) produce packets which
+ * contain crypto result metadata. This function returns success status
+ * of the crypto operation that was applied to a packet and optionally
+ * writes additional information in a result structure.
*
- * @note This routine is intended to be used for diagnostic purposes
- * to enable applications to generate a printable value that represents
- * an odp_crypto_session_t handle.
+ * If the crypto operation succeeded, zero is returned and the values
+ * written in the cipher_status and auth_status fields of the result
+ * structure have undefined values.
+ *
+ * If the crypto operation failed, -1 is returned and the cipher_status
+ * and auth_status fields of the result structure indicate the reason for
+ * the failure.
+ *
+ * The subtype of the passed packet must be ODP_EVENT_PACKET_CRYPTO,
+ * otherwise the result of the call is undefined.
+ *
+ * @param packet A crypto processed packet (ODP_EVENT_PACKET_CRYPTO)
+ * @param[out] result Pointer to operation result for output or NULL
+ *
+ * @retval 0 Crypto operation succeeded
+ * @retval -1 Crypto operation failed
+ * @retval <-1 Failed to get crypto operation status of the packet
*/
-uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl);
+int odp_crypto_result(odp_crypto_packet_result_t *result,
+ odp_packet_t packet);
/**
- * Get printable value for an odp_crypto_compl_t
+ * Crypto packet operation
*
- * @param hdl odp_crypto_compl_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * Performs the SYNC cryptographic operations specified during session creation
+ * on the packets. All arrays should be of num_pkt size.
*
- * @note This routine is intended to be used for diagnostic purposes
- * to enable applications to generate a printable value that represents
- * an odp_crypto_compl_t handle.
+ * Result of the crypto operation can be checked using odp_crypto_result().
+ * Parse flags in packet metadata are not affected by the crypto operation.
+ * In particular, odp_packet_has_error() can not be used for checking if the
+ * crypto operation succeeded.
+ *
+ * Use of the pkt_out parameter depends on the configured crypto operation
+ * type as described below.
+ *
+ * ODP_CRYPTO_OP_TYPE_LEGACY:
+ *
+ * Caller should initialize each element of pkt_out either with the desired
+ * output packet handle or with ODP_PACKET_INVALID to make ODP allocate a new
+ * packet from provided pool.
+ *
+ * All packet data and metadata are copied from the input packet to the output
+ * packet before the requested crypto operation is performed to the output
+ * packet. If an output packet is given to the operation, it must be at least
+ * as long as the input packet and, in encode operations, long enough for the
+ * hash result to be fully inside the packet data. Memory layout of the output
+ * packet may change during the crypto operation. If the output packet is
+ * longer than needed, it is not truncated and the extra data bytes retain
+ * their content.
+ *
+ * It is ok to pass the same packet handle as both the input packet and the
+ * output packet for the same crypto operation. In that case the input packet
+ * is consumed but returned as the output packet (with possibly different
+ * memory layout).
+ *
+ * ODP_CRYPTO_OP_TYPE_BASIC:
+ *
+ * ODP allocates the output packet from the pool from which the input
+ * packet was allocated. The processed input packet is consumed. All
+ * packet data and metadata are copied from the input packet to the output
+ * packet before the requested crypto operation is applied to the output
+ * packet. Memory layout (including packet data pointers, head and tail room,
+ * segmentation) of the output packet may differ from that of the input
+ * packet.
+ *
+ * The value of pkt_out[n] is ignored as pkt_out[n] is used purely as an
+ * output parameter that returns the handle of the newly allocated packet.
+ *
+ * ODP_CRYPTO_OP_TYPE_OOP:
+ *
+ * Writes the output bytes of the crypto operation in a caller provided
+ * output packet passed through pkt_out[n]. Input packets are not consumed
+ * nor modified. Memory layout (including packet data pointers, head and
+ * tail room, segmentation) of the output packet may change during the
+ * operation.
+ *
+ * Crypto output is the processed crypto_range, auth_range and
+ * MAC/digest (in encode sessions) of the input packet. The operation
+ * behaves as if crypto range and auth range were first copied from the
+ * input packet to the output packet and then the crypto operation
+ * was applied to the output packet.
+ *
+ * Auth range of (AEAD) algorithms that ignore auth range is not copied.
+ *
+ * The offset of the crypto range and auth range in the output packet is
+ * the same as in the input packet, adjusted by dst_offset_shift operation
+ * parameter.
+ *
+ * pkt_out[n] must be a valid handle to a packet that is long enough to
+ * contain the shifted crypto range, auth range and, in encode sessions,
+ * the MAC/digest result. pkt_out[n] must not be the same as any input
+ * packet or any other output packet.
+ *
+ * OOP_CRYPTO_OP_TYPE_BASIC_AND_OOP:
+ *
+ * Behaves as the ODP_CRYPTO_OP_TYPE_BASIC operation type if pkt_out[n] is
+ * ODP_PACKET_INVALID. Otherwise behaves as the ODP_CRYPTO_OP_TYPE_OOP
+ * operation type.
+ *
+ * @param pkt_in Packets to be processed
+ * @param[in,out] pkt_out Packet handle array for resulting packets
+ * @param param Operation parameters array
+ * @param num_pkt Number of packets to be processed
+ *
+ * @return Number of input packets processed (0 ... num_pkt)
+ * @retval <0 on failure
*/
-uint64_t odp_crypto_compl_to_u64(odp_crypto_compl_t hdl);
+int odp_crypto_op(const odp_packet_t pkt_in[],
+ odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt);
/**
- * Initialize crypto session parameters
+ * Crypto packet operation
*
- * Initialize an odp_crypto_session_param_t to its default values for
- * all fields.
+ * Performs the ASYNC cryptographic operations specified during session
+ * creation on the packets. Behaves otherwise like odp_crypto_op() but
+ * returns output packets through events.
*
- * @param param Pointer to odp_crypto_session_param_t to be initialized
+ * With operation types other than ODP_CRYPTO_OP_TYPE_LEGACY, packet
+ * data of processed packets may not be valid before odp_crypto_result()
+ * has been called.
+ *
+ * With ODP_CRYPTO_OP_TYPE_OOP, an enqueued input packet is consumed but
+ * returned back unmodified after the crypto operation is complete. The
+ * caller may not access the input packet until getting the handle back
+ * through odp_crypto_result().
+ *
+ * All arrays should be of num_pkt size, except that pkt_out parameter
+ * is ignored when the crypto operation type is ODP_CRYPTO_OP_TYPE_BASIC.
+ *
+ * From packet ordering perspective this function behaves as if each input
+ * packet was enqueued to a crypto session specific ODP queue in the order
+ * the packets appear in the parameter array. The conceptual session input
+ * queue has the same order type (ODP_QUEUE_ORDER_KEEP or
+ * ODP_QUEUE_ORDER_IGNORE) as the completion queue of the session.
+ * The order of output events of a crypto session in a completion queue is
+ * the same as the order of the corresponding input packets in the conceptual
+ * session input queue. The order of output events of different crypto
+ * sessions is not defined even when they go through the same crypto
+ * completion queue.
+ *
+ * @param pkt_in Packets to be processed
+ * @param pkt_out Packet handle array for resulting packets
+ * @param param Operation parameters array
+ * @param num_pkt Number of packets to be processed
+ *
+ * @return Number of input packets consumed (0 ... num_pkt)
+ * @retval <0 on failure
*/
-void odp_crypto_session_param_init(odp_crypto_session_param_t *param);
+int odp_crypto_op_enq(const odp_packet_t pkt_in[],
+ const odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt);
/**
* @}
diff --git a/include/odp/api/spec/crypto_types.h b/include/odp/api/spec/crypto_types.h
new file mode 100644
index 000000000..579022762
--- /dev/null
+++ b/include/odp/api/spec/crypto_types.h
@@ -0,0 +1,1059 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto types */
+
+#ifndef ODP_API_SPEC_CRYPTO_TYPES_H_
+#define ODP_API_SPEC_CRYPTO_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#include <odp/api/packet_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/std_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_crypto ODP CRYPTO
+ * @{
+ */
+
+/**
+ * @def ODP_CRYPTO_SESSION_INVALID
+ * Invalid session handle
+ */
+
+/**
+ * @typedef odp_crypto_session_t
+ * Crypto API opaque session handle
+ */
+
+/**
+ * Crypto API operation mode
+ */
+typedef enum {
+ /** Synchronous, return results immediately */
+ ODP_CRYPTO_SYNC,
+ /** Asynchronous, return results via posted event */
+ ODP_CRYPTO_ASYNC,
+} odp_crypto_op_mode_t;
+
+/**
+ * Crypto API operation
+ */
+typedef enum {
+ /** Encrypt and/or compute authentication ICV */
+ ODP_CRYPTO_OP_ENCODE,
+ /** Decrypt and/or verify authentication ICV */
+ ODP_CRYPTO_OP_DECODE,
+} odp_crypto_op_t;
+
+/**
+ * Crypto API cipher algorithm
+ */
+typedef enum {
+ /** No cipher algorithm specified */
+ ODP_CIPHER_ALG_NULL,
+
+ /** DES */
+ ODP_CIPHER_ALG_DES,
+
+ /** Triple DES with cipher block chaining */
+ ODP_CIPHER_ALG_3DES_CBC,
+
+ /** Triple DES with Electronic Codebook */
+ ODP_CIPHER_ALG_3DES_ECB,
+
+ /** AES with cipher block chaining */
+ ODP_CIPHER_ALG_AES_CBC,
+
+ /** AES with counter mode */
+ ODP_CIPHER_ALG_AES_CTR,
+
+ /** AES with electronic codebook */
+ ODP_CIPHER_ALG_AES_ECB,
+
+ /** AES with 128-bit cipher feedback */
+ ODP_CIPHER_ALG_AES_CFB128,
+
+ /** AES with XEX-based tweaked-codebook mode with ciphertext stealing
+ * (XTS) */
+ ODP_CIPHER_ALG_AES_XTS,
+
+ /** AES-GCM
+ *
+ * AES in Galois/Counter Mode (GCM) algorithm. GCM provides both
+ * authentication and ciphering of data (authenticated encryption)
+ * in the same operation. Hence this algorithm must be paired always
+ * with ODP_AUTH_ALG_AES_GCM authentication.
+ */
+ ODP_CIPHER_ALG_AES_GCM,
+
+ /** AES-CCM
+ *
+ * AES in Counter with CBC-MAC (CCM) mode algorithm. CCM provides both
+ * authentication and ciphering of data (authenticated encryption)
+ * in the same operation. Hence this algorithm must be paired always
+ * with ODP_AUTH_ALG_AES_CCM authentication.
+ */
+ ODP_CIPHER_ALG_AES_CCM,
+
+ /** ChaCha20-Poly1305
+ *
+ * ChaCha20 with Poly1305 provide both authentication and ciphering of
+ * data (authenticated encryption) in the same operation. Hence this
+ * algorithm must be paired always with ODP_AUTH_ALG_CHACHA20_POLY1305
+ * authentication.
+ */
+ ODP_CIPHER_ALG_CHACHA20_POLY1305,
+
+ /** Confidentiality F8 algorithm (UEA1)
+ *
+ * KASUMI-based F8 algorithm (also known as UEA1).
+ *
+ * IV should be formatted according to the 3GPP TS 35.201:
+ * COUNT || BEARER || DIRECTION || 0...0
+ */
+ ODP_CIPHER_ALG_KASUMI_F8,
+
+ /** Confidentiality UEA2 algorithm (128-EEA1)
+ *
+ * SNOW 3G-based UEA2 algorithm (also known as 128-EEA1).
+ *
+ * IV (128 bits) should be formatted according to the ETSI/SAGE
+ * UEA2 & UIA2 specification:
+ * COUNT || BEARER || DIRECTION || 0...0 ||
+ * COUNT || BEARER || DIRECTION || 0...0 ||
+ */
+ ODP_CIPHER_ALG_SNOW3G_UEA2,
+
+ /** Confidentiality 128-EEA2 algorithm
+ *
+ * AES-CTR-based 128-EEA2 algorithm.
+ *
+ * IV (128 bits) should be formatted according to the ETSI/SAGE
+ * 128-EAA2 & 128-EIA2 specification:
+ * COUNT || BEARER || DIRECTION || 0....0
+ */
+ ODP_CIPHER_ALG_AES_EEA2,
+
+ /** ZUC based confidentiality algorithm
+ *
+ * 128-EEA3/128-NEA3 algorithm when key length is 128 bits.
+ *
+ * IV (128 bits) should be formatted according to the ETSI/SAGE
+ * 128-EEA3 & 128-EIA3 specification:
+ * COUNT || BEARER || DIRECTION || 0...0 ||
+ * COUNT || BEARER || DIRECTION || 0...0 ||
+ *
+ * 256-bit key length support is experimental and subject to
+ * change. The following variants may be supported:
+ *
+ * - ZUC-256 with 25 byte IV (of which 184 bits are variable)
+ * as specified in "The ZUC-256 Stream Cipher".
+ * - ZUC-256 with 16 byte IV as specified in
+ * "An Addendum to the ZUC-256 Stream Cipher",
+ * https://eprint.iacr.org/2021/1439
+ */
+ ODP_CIPHER_ALG_ZUC_EEA3,
+
+} odp_cipher_alg_t;
+
+/**
+ * Crypto API authentication algorithm
+ */
+typedef enum {
+ /** No authentication algorithm specified */
+ ODP_AUTH_ALG_NULL,
+
+ /** HMAC-MD5
+ *
+ * MD5 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_MD5_HMAC,
+
+ /** HMAC-SHA-1
+ *
+ * SHA-1 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_SHA1_HMAC,
+
+ /** HMAC-SHA-224
+ *
+ * SHA-224 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_SHA224_HMAC,
+
+ /** HMAC-SHA-256
+ *
+ * SHA-256 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_SHA256_HMAC,
+
+ /** HMAC-SHA-384
+ *
+ * SHA-384 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_SHA384_HMAC,
+
+ /** HMAC-SHA-512
+ *
+ * SHA-512 algorithm in HMAC mode
+ */
+ ODP_AUTH_ALG_SHA512_HMAC,
+
+ /** AES-GCM
+ *
+ * AES in Galois/Counter Mode (GCM) algorithm. GCM provides both
+ * authentication and ciphering of data (authenticated encryption)
+ * in the same operation. Hence this algorithm must be paired always
+ * with ODP_CIPHER_ALG_AES_GCM cipher.
+ */
+ ODP_AUTH_ALG_AES_GCM,
+
+ /** AES-GMAC
+ *
+ * AES Galois Message Authentication Code (GMAC) algorithm. AES-GMAC
+ * is based on AES-GCM operation, but provides authentication only.
+ * Hence this algorithm can be paired only with ODP_CIPHER_ALG_NULL
+ * cipher.
+ *
+ * NIST and RFC specifications of GMAC refer to all data to be
+ * authenticated as AAD. In ODP the data to be authenticated, i.e.
+ * AAD, is ODP packet data and specified using the auth_range
+ * parameter. The aad_length and aad_ptr parameters, which would
+ * require the data to be contiguous in memory, are ignored with
+ * AES-GMAC.
+ *
+ * GMAC needs an initialization vector, which must be passed via
+ * operation parameters (auth_iv_ptr).
+ */
+ ODP_AUTH_ALG_AES_GMAC,
+
+ /** AES-CCM
+ *
+ * AES in Counter with CBC-MAC (CCM) mode algorithm. CCM provides both
+ * authentication and ciphering of data (authenticated encryption)
+ * in the same operation. Hence this algorithm must be paired always
+ * with ODP_CIPHER_ALG_AES_CCM cipher.
+ */
+ ODP_AUTH_ALG_AES_CCM,
+
+ /** AES-CMAC
+ *
+ * AES Cipher-based Message Authentication Code (CMAC) algorithm. CMAC
+ * is a keyed hash function that is based on a symmetric key block
+ * cipher, such as the AES.
+ */
+ ODP_AUTH_ALG_AES_CMAC,
+
+ /** AES-XCBC-MAC
+ *
+ * AES CBC MAC for arbitrary-length messages (XCBC-MAC).
+ *
+ */
+ ODP_AUTH_ALG_AES_XCBC_MAC,
+
+ /** ChaCha20-Poly1305 AEAD
+ *
+ * ChaCha20 with Poly1305 provide both authentication and ciphering of
+ * data (authenticated encryption) in the same operation. Hence this
+ * algorithm must be paired always with
+ * ODP_CIPHER_ALG_CHACHA20_POLY1305 cipher.
+ */
+ ODP_AUTH_ALG_CHACHA20_POLY1305,
+
+ /** Integrity F9 algorithm (UIA1)
+ *
+ * KASUMI-based F9 algorithm (also known as UIA1).
+ *
+ * IV (9 bytes) is a concatenation of COUNT (32b), FRESH (32b) and
+ * DIRECTION (LSB-aligned, 1b).
+ * IV (8 bytes) is a concatenation of COUNT (32b) and FRESH (32b)
+ * DIRECTION (1b) and padding should come at the end of message.
+ */
+ ODP_AUTH_ALG_KASUMI_F9,
+
+ /** Integrity UIA2 algorithm (128-EIA1)
+ *
+ * SNOW 3G-based UIA2 algorithm (also known as 128-EIA1).
+ * IV (128 bits) should be formatted according to the ETSI/SAGE
+ * UEA2 & UIA2 specification:
+ * COUNT || FRESH ||
+ * DIRECTION XOR COUNT0 || COUNT1 .. COUNT31 ||
+ * FRESH0 .. FRESH15 || FRESH16 XOR DIRECTION || FRESH17 .. FRESH31
+ */
+ ODP_AUTH_ALG_SNOW3G_UIA2,
+
+ /** Integrity 128-EIA2 algorithm
+ *
+ * AES_CMAC-based 128-EIA2 algorithm.
+ *
+ * IV (64 bits) should be formatted according to the ETSI/SAGE
+ * 128-EEA2 & 128-EIA2 specification:
+ * COUNT || BEARER || DIRECTION || 0....0
+ */
+ ODP_AUTH_ALG_AES_EIA2,
+
+ /** ZUC-based integrity algorithm.
+ *
+ * 128-EIA3/128-NIA3 algorithm when key length is 128 bits.
+ *
+ * IV (128 bits) should be formatted according to the ETSI/SAGE
+ * 128-EEA3 & 128-EIA2 specification:
+ * COUNT || BEARER ||
+ * DIRECTION XOR COUNT0 || COUNT1 .. COUNT31 ||
+ * BEARER || 0...0 || DIRECTION || 0...0
+ *
+ * 256-bit key length support is experimental and subject to
+ * change. The following variants may be supported:
+ *
+ * - ZUC-256 with 25 byte IV (of which 184 bits are variable) and
+ * 32/64/128 bit MAC as specified in "The ZUC-256 Stream Cipher".
+ * - ZUC-256 with 16 byte IV and 32/64/128 bit MAC as specified in
+ * "An Addendum to the ZUC-256 Stream Cipher",
+ * https://eprint.iacr.org/2021/1439
+ */
+ ODP_AUTH_ALG_ZUC_EIA3,
+
+ /** MD5 algorithm */
+ ODP_AUTH_ALG_MD5,
+
+ /** SHA1 algorithm */
+ ODP_AUTH_ALG_SHA1,
+
+ /** 224 bit SHA2 algorithm */
+ ODP_AUTH_ALG_SHA224,
+
+ /** 256 bit SHA2 algorithm */
+ ODP_AUTH_ALG_SHA256,
+
+ /** 384 bit SHA2 algorithm */
+ ODP_AUTH_ALG_SHA384,
+
+ /** 512 bit SHA2 algorithm */
+ ODP_AUTH_ALG_SHA512,
+
+} odp_auth_alg_t;
+
+/**
+ * Cipher algorithms in a bit field structure
+ */
+typedef union odp_crypto_cipher_algos_t {
+ /** Cipher algorithms */
+ struct {
+ /** ODP_CIPHER_ALG_NULL */
+ uint32_t null : 1;
+
+ /** ODP_CIPHER_ALG_DES */
+ uint32_t des : 1;
+
+ /** ODP_CIPHER_ALG_3DES_CBC */
+ uint32_t trides_cbc : 1;
+
+ /** ODP_CIPHER_ALG_3DES_ECB */
+ uint32_t trides_ecb : 1;
+
+ /** ODP_CIPHER_ALG_AES_CBC */
+ uint32_t aes_cbc : 1;
+
+ /** ODP_CIPHER_ALG_AES_CTR */
+ uint32_t aes_ctr : 1;
+
+ /** ODP_CIPHER_ALG_AES_ECB */
+ uint32_t aes_ecb : 1;
+
+ /** ODP_CIPHER_ALG_AES_CFB128 */
+ uint32_t aes_cfb128 : 1;
+
+ /** ODP_CIPHER_ALG_AES_XTS */
+ uint32_t aes_xts : 1;
+
+ /** ODP_CIPHER_ALG_AES_GCM */
+ uint32_t aes_gcm : 1;
+
+ /** ODP_CIPHER_ALG_AES_CCM */
+ uint32_t aes_ccm : 1;
+
+ /** ODP_CIPHER_ALG_CHACHA20_POLY1305 */
+ uint32_t chacha20_poly1305 : 1;
+
+ /** ODP_CIPHER_ALG_KASUMI_F8 */
+ uint32_t kasumi_f8 : 1;
+
+ /** ODP_CIPHER_ALG_SNOW3G_UEA2 */
+ uint32_t snow3g_uea2 : 1;
+
+ /** ODP_CIPHER_ALG_AES_EEA2 */
+ uint32_t aes_eea2 : 1;
+
+ /** ODP_CIPHER_ALG_ZUC_EEA3 */
+ uint32_t zuc_eea3 : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint32_t all_bits;
+} odp_crypto_cipher_algos_t;
+
+/**
+ * Authentication algorithms in a bit field structure
+ */
+typedef union odp_crypto_auth_algos_t {
+ /** Authentication algorithms */
+ struct {
+ /** ODP_AUTH_ALG_NULL */
+ uint32_t null : 1;
+
+ /** ODP_AUTH_ALG_MD5_HMAC */
+ uint32_t md5_hmac : 1;
+
+ /** ODP_AUTH_ALG_SHA1_HMAC */
+ uint32_t sha1_hmac : 1;
+
+ /** ODP_AUTH_ALG_SHA224_HMAC */
+ uint32_t sha224_hmac : 1;
+
+ /** ODP_AUTH_ALG_SHA256_HMAC */
+ uint32_t sha256_hmac : 1;
+
+ /** ODP_AUTH_ALG_SHA384_HMAC */
+ uint32_t sha384_hmac : 1;
+
+ /** ODP_AUTH_ALG_SHA512_HMAC */
+ uint32_t sha512_hmac : 1;
+
+ /** ODP_AUTH_ALG_AES_GCM */
+ uint32_t aes_gcm : 1;
+
+ /** ODP_AUTH_ALG_AES_GMAC*/
+ uint32_t aes_gmac : 1;
+
+ /** ODP_AUTH_ALG_AES_CCM */
+ uint32_t aes_ccm : 1;
+
+ /** ODP_AUTH_ALG_AES_CMAC*/
+ uint32_t aes_cmac : 1;
+
+ /** ODP_AUTH_ALG_AES_XCBC_MAC*/
+ uint32_t aes_xcbc_mac : 1;
+
+ /** ODP_AUTH_ALG_CHACHA20_POLY1305 */
+ uint32_t chacha20_poly1305 : 1;
+
+ /** ODP_AUTH_ALG_KASUMI_F9 */
+ uint32_t kasumi_f9 : 1;
+
+ /** ODP_AUTH_ALG_SNOW3G_UIA2 */
+ uint32_t snow3g_uia2 : 1;
+
+ /** ODP_AUTH_ALG_AES_EIA2 */
+ uint32_t aes_eia2 : 1;
+
+ /** ODP_AUTH_ALG_ZUC_EIA3 */
+ uint32_t zuc_eia3 : 1;
+
+ /** ODP_AUTH_ALG_MD5 */
+ uint32_t md5 : 1;
+
+ /** ODP_AUTH_ALG_SHA1 */
+ uint32_t sha1 : 1;
+
+ /** ODP_AUTH_ALG_SHA224 */
+ uint32_t sha224 : 1;
+
+ /** ODP_AUTH_ALG_SHA256 */
+ uint32_t sha256 : 1;
+
+ /** ODP_AUTH_ALG_SHA384 */
+ uint32_t sha384 : 1;
+
+ /** ODP_AUTH_ALG_SHA512 */
+ uint32_t sha512 : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint32_t all_bits;
+} odp_crypto_auth_algos_t;
+
+/**
+ * Crypto API key structure
+ */
+typedef struct odp_crypto_key {
+ /** Key data */
+ uint8_t *data;
+
+ /** Key length in bytes */
+ uint32_t length;
+
+} odp_crypto_key_t;
+
+/**
+ * Type of odp_crypto_op()/odp_crypto_op_enq() calls.
+ */
+typedef enum odp_crypto_op_type_t {
+ /**
+ * Input packet data and metadata are copied to the output packet
+ * and then processed. Output packet is allocated by the caller
+ * or by ODP.
+ *
+ * This is the default value but will be deprecated in the future.
+ */
+ ODP_CRYPTO_OP_TYPE_LEGACY,
+
+ /**
+ * Input packet data and metadata are copied to the output packet
+ * and then processed. Output packet is allocated by ODP.
+ */
+ ODP_CRYPTO_OP_TYPE_BASIC,
+
+ /**
+ * Out-of-place crypto operation. Output packet is provided by
+ * the caller and the input packet is not consumed nor modified.
+ *
+ * Output of the crypto operation is written in the caller provided
+ * output packet without affecting other data and metadata of the
+ * output packet. Memory layout of the output packet may change
+ * during the operation.
+ *
+ * Crypto output is the processed crypto_range, auth_range and
+ * MAC/digest (in encode sessions) of the input packet.
+ */
+ ODP_CRYPTO_OP_TYPE_OOP,
+
+ /**
+ * Basic or out-of-place crypto operation depending on op params.
+ *
+ * If the output packet specified in a crypto operation (i.e.
+ * pkt_out[i] is ODP_PACKET_INVALID) then the packet is processed
+ * the same way as in the ODP_CRYPTO_OP_TYPE_BASIC operation type.
+ * Otherwise the packet is processed as in the ODP_CRYPTO_OP_TYPE_OOP
+ * operation type.
+ *
+ * Sessions of this operation type may have lower performance than
+ * the more specific operation types.
+ */
+ ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP,
+
+} odp_crypto_op_type_t;
+
+/**
+ * Crypto API session creation parameters
+ */
+typedef struct odp_crypto_session_param_t {
+ /** Encode vs. decode operation
+ *
+ * The default value is ODP_CRYPTO_OP_ENCODE.
+ */
+ odp_crypto_op_t op;
+
+ /** Crypto operation type
+ *
+ * This field defines how the crypto operation functions are
+ * to be called and what they return. In particular, this field
+ * specifies the interpretation of the output packet parameter,
+ * how output packets are allocated and what data and metadata
+ * they contain.
+ *
+ * The default value is ODP_CRYPTO_OP_TYPE_LEGACY.
+ */
+ odp_crypto_op_type_t op_type;
+
+ /** Cipher range unit
+ *
+ * When this flag is true, cipher range offset and length are in bits.
+ * Otherwise the offset and length are in bytes.
+ *
+ * If cipher capabilities do not include bit_mode, setting this to
+ * true causes a session creation failure.
+ *
+ * The default value is false.
+ */
+ odp_bool_t cipher_range_in_bits;
+
+ /** Auth range unit
+ *
+ * When this flag is true, auth range offset and length are in bits.
+ * Otherwise the offset and length are in bytes.
+ *
+ * If auth capabilities do not include bit_mode, setting this to
+ * true causes a session creation failure.
+ *
+ * The default value is false.
+ */
+ odp_bool_t auth_range_in_bits;
+
+ /** Authenticate cipher vs. plain text
+ *
+ * Controls ordering of authentication and cipher operations,
+ * and is relative to the operation (encode vs decode). When encoding,
+ * TRUE indicates the authentication operation should be performed
+ * after the cipher operation else before. When decoding, TRUE
+ * indicates the reverse order of operation.
+ *
+ * The value is ignored with authenticated encryption algorithms
+ * such as AES-GCM. The value is also ignored when one of the
+ * algorithms is null.
+ *
+ * true: Authenticate cipher text
+ * false: Authenticate plain text
+ *
+ * The default value is false.
+ */
+ odp_bool_t auth_cipher_text;
+
+ /** Hash result location may overlap authentication range
+ *
+ * This flag indicates that the hash result location may (but is
+ * not required to) overlap authentication range. Setting this
+ * flag may reduce performance.
+ *
+ * Default value is false.
+ */
+ odp_bool_t hash_result_in_auth_range;
+
+ /** Enable skipping crypto on per-packet basis
+ *
+ * When this flag is true, the null_crypto flag of crypto operation
+ * parameters can be set to request skipping of ciphering and
+ * authentication of a packet regardless of session configuration.
+ * This may be useful for preserving packet order between packets
+ * that require crypto processing and packets that do not.
+ *
+ * This flag must be set false when op_mode is ODP_CRYPTO_SYNC.
+ *
+ * The default value is false.
+ */
+ odp_bool_t null_crypto_enable;
+
+ /** Operation mode when using packet interface: sync or async
+ *
+ * The default value is ODP_CRYPTO_SYNC.
+ */
+ odp_crypto_op_mode_t op_mode;
+
+ /** Cipher algorithm
+ *
+ * Select cipher algorithm to be used. ODP_CIPHER_ALG_NULL indicates
+ * that ciphering is disabled. Use odp_crypto_capability() for
+ * supported algorithms. Note that some algorithms restrict choice of
+ * the pairing authentication algorithm. When ciphering is enabled
+ * cipher key and IV need to be set. The default value is
+ * ODP_CIPHER_ALG_NULL.
+ *
+ * When ciphering is disabled, i.e. cipher_alg is ODP_CIPHER_ALG_NULL,
+ * cipher_key and cipher_iv_len parameters are ignored.
+ */
+ odp_cipher_alg_t cipher_alg;
+
+ /** Cipher key
+ *
+ * Use odp_crypto_cipher_capa() for supported key and IV lengths.
+ */
+ odp_crypto_key_t cipher_key;
+
+ /** Cipher IV length. The default value is zero. */
+ uint32_t cipher_iv_len;
+
+ /** Authentication algorithm
+ *
+ * Select authentication algorithm to be used. ODP_AUTH_ALG_NULL
+ * indicates that authentication is disabled. Use
+ * odp_crypto_capability() for supported algorithms. Note that some
+ * algorithms restrict choice of the pairing cipher algorithm. When
+ * single algorithm provides both ciphering and authentication
+ * (i.e. Authenticated Encryption), authentication side key
+ * (auth_key) and IV (auth_iv) are ignored, and cipher side values are
+ * used instead. These algorithms ignore authentication side key
+ * and IV: ODP_AUTH_ALG_AES_GCM, ODP_AUTH_ALG_AES_CCM and
+ * ODP_AUTH_ALG_CHACHA20_POLY1305. Otherwise, all authentication side
+ * parameters must be set when authentication is enabled. The default
+ * value is ODP_AUTH_ALG_NULL.
+ *
+ * When authentication is disabled, i.e. auth_alg is
+ * ODP_AUTH_ALG_NULL, auth_key, auth_iv_len, auth_digest_len,
+ * auth_aad_len and hash_result_in_auth_range parameters are ignored.
+ */
+ odp_auth_alg_t auth_alg;
+
+ /** Authentication key
+ *
+ * Use odp_crypto_auth_capability() for supported key lengths.
+ */
+ odp_crypto_key_t auth_key;
+
+ /** Authentication IV length. The default value is zero. */
+ uint32_t auth_iv_len;
+
+ /** Authentication digest length in bytes
+ *
+ * Use odp_crypto_auth_capability() for supported digest lengths.
+ */
+ uint32_t auth_digest_len;
+
+ /** Additional Authenticated Data (AAD) length in bytes
+ *
+ * AAD length is constant for all operations (packets) of the session.
+ * Set to zero when AAD is not used. Use odp_crypto_auth_capability()
+ * for supported AAD lengths. The default value is zero.
+ */
+ uint32_t auth_aad_len;
+
+ /** Async mode completion event queue
+ *
+ * The completion queue is used to return completions from
+ * odp_crypto_op_enq() to the application.
+ */
+ odp_queue_t compl_queue;
+
+ /** Output pool
+ *
+ * When the output packet is not specified during the call to
+ * crypto operation in the legacy operation type, the output
+ * packet will be allocated from this pool.
+ *
+ * In ODP_CRYPTO_OP_TYPE_BASIC and ODP_CRYPTO_OP_TYPE_OOP
+ * operation types this must be set to ODP_POOL_INVALID.
+ */
+ odp_pool_t output_pool;
+
+} odp_crypto_session_param_t;
+
+/**
+ * Crypto packet API per packet operation parameters
+ */
+typedef struct odp_crypto_packet_op_param_t {
+ /** Session handle from creation */
+ odp_crypto_session_t session;
+
+ /** IV pointer for cipher */
+ const uint8_t *cipher_iv_ptr;
+
+ /** IV pointer for authentication */
+ const uint8_t *auth_iv_ptr;
+
+ /** Offset from start of packet for hash result
+ *
+ * In case of decode sessions, the expected hash will be read from
+ * this offset from the input packet and compared with the calculated
+ * hash. After the operation the hash bytes will have undefined
+ * values except with out-of-place sessions (ODP_CRYPTO_OP_TYPE_OOP
+ * operation type).
+ *
+ * With out-of-place decode sessions the input packet is not modified
+ * but if the hash location overlaps the cipher range or the auth
+ * range, then the corresponding location in the output packet will
+ * have undefined content.
+ *
+ * In case of encode sessions the calculated hash will be stored in
+ * this offset in the output packet.
+ *
+ * If the hash_result_in_auth_range session parameter is true,
+ * the hash result location may overlap auth_range. In that case the
+ * result location will be treated as containing zero bytes for the
+ * purpose of hash calculation in decode sessions.
+ */
+ uint32_t hash_result_offset;
+
+ /** Pointer to AAD. AAD length is defined by 'auth_aad_len'
+ * session parameter.
+ */
+ const uint8_t *aad_ptr;
+
+ /** Data range to be ciphered.
+ *
+ * The range is given in bits or bytes as configured at session
+ * creation.
+ *
+ * Ignored by the null cipher with operation types other than
+ * ODP_CRYPTO_OP_TYPE_OOP.
+ *
+ * With the OOP operation type the cipher range is copied to the
+ * output packet even with the null cipher. Non-zero-length ranges
+ * are not necessarily supported with the null cipher and the OOP
+ * operation type. If the requested range is not supported, the
+ * crypto operation will fail. The failure is indicated through
+ * odp_crypto_result() or through a negative return value of
+ * odp_crypto_op()/odp_crypto_op_enq().
+ **/
+ odp_packet_data_range_t cipher_range;
+
+ /** Data range to be authenticated
+ *
+ * The range is given in bits or bytes as configured at session
+ * creation.
+ *
+ * The value is ignored with authenticated encryption algorithms,
+ * such as AES-GCM, which authenticate data in the cipher range
+ * and the AAD.
+ *
+ * Ignored by the null auth algorithm with operation types other than
+ * ODP_CRYPTO_OP_TYPE_OOP.
+ *
+ * With the OOP operation type the auth range is copied to the
+ * output packet even with the null auth algorithm. Non-zero-length
+ * ranges are not necessarily supported with the null algorithm and
+ * the OOP operation type. If the requested range is not supported,
+ * the crypto operation will fail. The failure is indicated through
+ * odp_crypto_result() or through a negative return value of
+ * odp_crypto_op()/odp_crypto_op_enq().
+ *
+ * As a special case AES-GMAC uses this field instead of aad_ptr
+ * for the data bytes to be authenticated.
+ */
+ odp_packet_data_range_t auth_range;
+
+ /** Shift of the output offsets with ODP_CRYPTO_OP_TYPE_OOP
+ *
+ * The processed crypto range and auth range of the input packet
+ * will be written in the output packet at the offset specified
+ * in the ranges (i.e. the same as in the input packet), shifted
+ * by this many bytes. This allows directing the output to
+ * a different packet offset than the offset of the input data.
+ *
+ * This is ignored if the crypto operation type is not
+ * ODP_CRYPTO_OP_TYPE_OOP.
+ */
+ int32_t dst_offset_shift;
+
+ /** Use null crypto algorithms
+ *
+ * Process packet using the null cipher and null auth algorithm
+ * instead of the algoithms configured in the session. This flag is
+ * ignored if the null_crypto_enable session parameter is not set.
+ */
+ uint8_t null_crypto :1;
+
+} odp_crypto_packet_op_param_t;
+
+/**
+ * Crypto API session creation return code
+ */
+typedef enum {
+ /** Session created */
+ ODP_CRYPTO_SES_ERR_NONE,
+ /** Creation failed, no resources */
+ ODP_CRYPTO_SES_ERR_ENOMEM,
+ /** Creation failed, bad cipher params */
+ ODP_CRYPTO_SES_ERR_CIPHER,
+ /** Creation failed, bad auth params */
+ ODP_CRYPTO_SES_ERR_AUTH,
+
+ /** Unsupported combination of algorithms
+ *
+ * The combination of cipher and auth algorithms with their
+ * specific parameters is not supported even if the algorithms
+ * appear in capabilities and are supported in combination with
+ * other algorithms or other algorithm specific parameters.
+ */
+ ODP_CRYPTO_SES_ERR_ALG_COMBO,
+
+ /** Unsupported order of cipher and auth
+ *
+ * The requested mutual order of ciphering and authentication
+ * is not supported with the chosen individual cipher and
+ * authentication algorithms.
+ */
+ ODP_CRYPTO_SES_ERR_ALG_ORDER,
+
+ /** Unsupported combination of session creation parameters
+ *
+ * The combination of provided session creation parameters is not
+ * supported. This error can occur when there are limitations that
+ * are not expressible through crypto capabilities or other error
+ * status values.
+ */
+ ODP_CRYPTO_SES_ERR_PARAMS,
+} odp_crypto_ses_create_err_t;
+
+/**
+ * Crypto API algorithm return code
+ */
+typedef enum {
+ /** Algorithm successful */
+ ODP_CRYPTO_ALG_ERR_NONE,
+ /** Invalid range or packet size */
+ ODP_CRYPTO_ALG_ERR_DATA_SIZE,
+ /** Computed ICV value mismatch */
+ ODP_CRYPTO_ALG_ERR_ICV_CHECK,
+ /** Other error */
+ ODP_CRYPTO_ALG_ERR_OTHER,
+} odp_crypto_alg_err_t;
+
+/**
+ * Crypto API per packet operation completion status
+ */
+typedef struct odp_crypto_op_status {
+ /** Algorithm specific return code */
+ odp_crypto_alg_err_t alg_err;
+} odp_crypto_op_status_t;
+
+/**
+ * Crypto packet API operation result
+ */
+typedef struct odp_crypto_packet_result_t {
+ /** Input packet passed to odp_crypo_op_enq() when the operation
+ * type of the session is ODP_CRYPTO_OP_TYPE_OOP. In other cases
+ * this field does not have a valid value.
+ */
+ odp_packet_t pkt_in;
+
+ /** Cipher status */
+ odp_crypto_op_status_t cipher_status;
+
+ /** Authentication status */
+ odp_crypto_op_status_t auth_status;
+
+} odp_crypto_packet_result_t;
+
+/**
+ * Crypto capabilities
+ */
+typedef struct odp_crypto_capability_t {
+ /** Maximum number of crypto sessions */
+ uint32_t max_sessions;
+
+ /** Supported packet operation in SYNC mode */
+ odp_support_t sync_mode;
+
+ /** Supported packet operation in ASYNC mode */
+ odp_support_t async_mode;
+
+ /** Supported cipher algorithms */
+ odp_crypto_cipher_algos_t ciphers;
+
+ /** Cipher algorithms implemented with HW offload */
+ odp_crypto_cipher_algos_t hw_ciphers;
+
+ /** Supported authentication algorithms */
+ odp_crypto_auth_algos_t auths;
+
+ /** Authentication algorithms implemented with HW offload */
+ odp_crypto_auth_algos_t hw_auths;
+
+ /**
+ * Scheduled crypto completion queue support
+ *
+ * This defines whether scheduled queues are supported as crypto
+ * compl_queue.
+ * 0: Scheduled queues are not supported as crypto completion queues
+ * 1: Scheduled queues are supported as crypto completion queues
+ * @see odp_crypto_session_param_t
+ */
+ odp_bool_t queue_type_sched;
+
+ /**
+ * Plain crypto completion queue support
+ *
+ * This defines whether plain queues are supported as crypto
+ * compl_queue.
+ * 0: Plain queues are not supported as crypto completion queues
+ * 1: Plain queues are supported as crypto completion queues
+ * @see odp_crypto_session_param_t
+ */
+ odp_bool_t queue_type_plain;
+} odp_crypto_capability_t;
+
+/**
+ * Cipher algorithm capabilities
+ */
+typedef struct odp_crypto_cipher_capability_t {
+ /** Key length in bytes */
+ uint32_t key_len;
+
+ /** IV length in bytes */
+ uint32_t iv_len;
+
+ /** Cipher supports bit mode
+ *
+ * This cipher can work on a range of bits in addition to a range of
+ * bytes. When this capability is not present, only byte ranges are
+ * supported. The unit of cipher range is selected at session creation
+ * through the cipher_range_in_bits session parameter.
+ *
+ * Note: In bit mode the cipher range must start on a byte boundary.
+ * Using an offset which is not divisible by 8 will result in
+ * undefined behaviour.
+ *
+ * Note2: If the range length in bit mode is not a multiple of 8,
+ * the remaining bits of the data in the last byte of the input/output
+ * will be the most significant bits, i.e. the most significant bit is
+ * considered to be the first bit of a byte for the purpose of input
+ * and output data range. The output bits that fall out of the output
+ * range are undefined.
+ */
+ odp_bool_t bit_mode;
+
+} odp_crypto_cipher_capability_t;
+
+/**
+ * Authentication algorithm capabilities
+ */
+typedef struct odp_crypto_auth_capability_t {
+ /** Digest length in bytes */
+ uint32_t digest_len;
+
+ /** Key length in bytes */
+ uint32_t key_len;
+
+ /** IV length in bytes */
+ uint32_t iv_len;
+
+ /** Additional Authenticated Data (AAD) lengths */
+ struct {
+ /** Minimum AAD length in bytes */
+ uint32_t min;
+
+ /** Maximum AAD length in bytes */
+ uint32_t max;
+
+ /** Increment of supported lengths between min and max
+ * (in bytes) */
+ uint32_t inc;
+ } aad_len;
+
+ /** Auth algorithm supports bit mode
+ *
+ * This auth algorithm can work on a range of bits in addition to
+ * a range of bytes. When this capability is not present, only byte
+ * ranges are supported. The unit of auth range is selected at session
+ * creation through the auth_range_in_bits session parameter.
+ *
+ * Note: In bit mode the auth range must start on a byte boundary.
+ * Using an offset which is not divisible by 8 will result in
+ * undefined behaviour.
+ *
+ * Note2: If the range length in bit mode is not a multiple of 8,
+ * the remaining bits of the data in the last byte of the input/output
+ * will be the most significant bits, i.e. the most significant bit is
+ * considered to be the first bit of a byte for the purpose of input
+ * and output data range. The output bits that fall out of the output
+ * range are undefined.
+ */
+ odp_bool_t bit_mode;
+
+} odp_crypto_auth_capability_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/debug.h b/include/odp/api/spec/debug.h
index b3b170f3e..4a3365a3e 100644
--- a/include/odp/api/spec/debug.h
+++ b/include/odp/api/spec/debug.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
/**
* @file
@@ -9,14 +7,18 @@
* ODP debug
*/
-#ifndef ODP_API_DEBUG_H_
-#define ODP_API_DEBUG_H_
+#ifndef ODP_API_SPEC_DEBUG_H_
+#define ODP_API_SPEC_DEBUG_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
+/** @addtogroup odp_initialization
+ * @{
+ */
+
/**
* @def ODP_STATIC_ASSERT
* Compile time assertion macro. Fails compilation and outputs message 'msg'
@@ -28,6 +30,10 @@ extern "C" {
* @param msg Compile time error message to be displayed if cond is false
*/
+/**
+ * @}
+ */
+
#ifdef __cplusplus
}
#endif
diff --git a/include/odp/api/spec/deprecated.h.in b/include/odp/api/spec/deprecated.h.in
index 224f60ff3..d062842c1 100644
--- a/include/odp/api/spec/deprecated.h.in
+++ b/include/odp/api/spec/deprecated.h.in
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* Macro for deprecated API definitions
*/
-#ifndef ODP_API_DEPRECATED_H_
-#define ODP_API_DEPRECATED_H_
+#ifndef ODP_API_SPEC_DEPRECATED_H_
+#define ODP_API_SPEC_DEPRECATED_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/dma.h b/include/odp/api/spec/dma.h
new file mode 100644
index 000000000..5303dc03f
--- /dev/null
+++ b/include/odp/api/spec/dma.h
@@ -0,0 +1,370 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP DMA
+ */
+
+#ifndef ODP_API_SPEC_DMA_H_
+#define ODP_API_SPEC_DMA_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/dma_types.h>
+#include <odp/api/pool_types.h>
+
+/** @addtogroup odp_dma
+ * @{
+ */
+
+/**
+ * Query DMA capabilities
+ *
+ * Outputs DMA capabilities on success.
+ *
+ * @param[out] capa Pointer to a capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_dma_capability(odp_dma_capability_t *capa);
+
+/**
+ * Initialize DMA session parameters
+ *
+ * Initialize an odp_dma_param_t to its default values.
+ *
+ * @param[out] param Parameter structure to be initialized
+ */
+void odp_dma_param_init(odp_dma_param_t *param);
+
+/**
+ * Create DMA session
+ *
+ * Create a DMA session according to the parameters. The use of session name is optional. Unique
+ * names are not required. However, odp_dma_lookup() returns only a single matching named session.
+ * Use odp_dma_param_init() to initialize parameters into their default values.
+ *
+ * @param name DMA session name or NULL. Maximum string length is ODP_DMA_NAME_LEN.
+ * @param param DMA session parameters
+ *
+ * @return DMA session handle on success
+ * @retval ODP_DMA_INVALID on failure
+ */
+odp_dma_t odp_dma_create(const char *name, const odp_dma_param_t *param);
+
+/**
+ * Destroy DMA session
+ *
+ * A DMA session may be destroyed only when there are no active transfers in the session (all
+ * previously started transfers have completed).
+ *
+ * @param dma DMA session to be destroyed
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_dma_destroy(odp_dma_t dma);
+
+/**
+ * Find DMA session by name
+ *
+ * @param name DMA session name
+ *
+ * @return Handle of the first matching DMA session
+ * @retval ODP_DMA_INVALID DMA session could not be found
+ */
+odp_dma_t odp_dma_lookup(const char *name);
+
+/**
+ * Initialize DMA transfer parameters
+ *
+ * Initialize an odp_dma_transfer_param_t to its default values.
+ *
+ * @param[out] trs_param Parameter structure to be initialized
+ */
+void odp_dma_transfer_param_init(odp_dma_transfer_param_t *trs_param);
+
+/**
+ * Initialize DMA transfer completion parameters
+ *
+ * Initialize an odp_dma_compl_param_t to its default values.
+ *
+ * @param[out] compl_param Parameter structure to be initialized
+ */
+void odp_dma_compl_param_init(odp_dma_compl_param_t *compl_param);
+
+/**
+ * Perform DMA transfer
+ *
+ * Performs DMA transfer according to the session and transfer parameters. Returns 1 when
+ * the transfer was completed successfully. Returns 0 when the transfer was not performed
+ * due to resources being temporarily busy. In this case, the same transfer is likely to succeed
+ * after enough resources are available. Returns <0 on failure.
+ *
+ * The call outputs optionally transfer results on a non-zero return value. Use NULL as 'result'
+ * pointer if results are not required.
+ *
+ * @param dma DMA session
+ * @param trs_param Transfer parameters
+ * @param[out] result Pointer to transfer result structure for output, or NULL when not used
+ *
+ * @retval 1 when transfer completed successfully
+ * @retval 0 when resources are busy and transfer was not performed
+ * @retval <0 on failure
+ */
+int odp_dma_transfer(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param,
+ odp_dma_result_t *result);
+
+/**
+ * Perform multiple DMA transfers
+ *
+ * Like odp_dma_transfer(), but performs 'num' transfers.
+ *
+ * @param dma DMA session
+ * @param trs_param Array of transfer parameter pointers
+ * @param[out] result Array of transfer result pointers for output, or NULL when not used
+ * @param num Number of transfers to perform. Both arrays have this many elements.
+ *
+ * @return Number of transfers completed successfully (1 ... num)
+ * @retval 0 when resources are busy and no transfers were performed
+ * @retval <0 on failure
+ */
+int odp_dma_transfer_multi(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param[],
+ odp_dma_result_t *result[], int num);
+
+/**
+ * Start DMA transfer
+ *
+ * Starts asynchronous DMA transfer according to the session and transfer parameters.
+ * Completion parameters specify how transfer completion is reported. Returns 1 when the transfer
+ * was started successfully. Returns 0 when the transfer was not started due to resources being
+ * temporarily busy. In this case, the same transfer is likely to start successfully after enough
+ * resources are available. Returns <0 on failure.
+ *
+ * @param dma DMA session
+ * @param trs_param Transfer parameters
+ * @param compl_param Transfer completion parameters
+ *
+ * @retval 1 when transfer started successfully
+ * @retval 0 when resources are busy and transfer was not started
+ * @retval <0 on failure
+ *
+ * @see odp_dma_transfer_id_alloc(), odp_dma_transfer_done(), odp_dma_compl_result()
+ */
+int odp_dma_transfer_start(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param,
+ const odp_dma_compl_param_t *compl_param);
+
+/**
+ * Start multiple DMA transfers
+ *
+ * Like odp_dma_transfer_start(), but starts 'num' transfers.
+ *
+ * @param dma DMA session
+ * @param trs_param Array of transfer parameter pointers
+ * @param compl_param Array of transfer completion parameter pointers
+ * @param num Number of transfers to start. Both parameter arrays have this many elements.
+ *
+ * @return Number of transfers started successfully (1 ... num)
+ * @retval 0 when resources are busy and no transfers were started
+ * @retval <0 on failure
+ */
+int odp_dma_transfer_start_multi(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param[],
+ const odp_dma_compl_param_t *compl_param[], int num);
+
+/**
+ * Check if DMA transfer has completed
+ *
+ * Application must call this function for every transfer that was started in ODP_DMA_COMPL_POLL
+ * mode until a non-zero value is returned. The transfer identifier from completion parameters of
+ * the transfer start call is used. When a non-zero value is returned, the transfer is complete
+ * and the identifier may be freed or reused for another transfer.
+ *
+ * The call outputs optionally transfer results on a non-zero return value. Use NULL as 'result'
+ * pointer if results are not required.
+ *
+ * @param dma DMA session
+ * @param transfer_id Transfer identifier
+ * @param[out] result Pointer to transfer result structure for output, or NULL when not used.
+ *
+ * @retval 0 transfer has not finished
+ * @retval >0 transfer has finished successfully
+ * @retval <0 on failure
+ */
+int odp_dma_transfer_done(odp_dma_t dma, odp_dma_transfer_id_t transfer_id,
+ odp_dma_result_t *result);
+
+/**
+ * Allocate DMA transfer identifier
+ *
+ * Transfer identifiers are used in #ODP_DMA_COMPL_POLL mode. It identifies a previously started
+ * transfer for an odp_dma_transfer_done() call. The maximum number of transfer identifiers is
+ * implementation specific, but there are at least odp_dma_capability_t::max_transfers identifiers
+ * per session.
+ *
+ * @param dma DMA session
+ *
+ * @return Transfer identifier
+ * @retval ODP_DMA_TRANSFER_ID_INVALID Transfer identifier could not be allocated
+ */
+odp_dma_transfer_id_t odp_dma_transfer_id_alloc(odp_dma_t dma);
+
+/**
+ * Free DMA transfer identifier
+ *
+ * @param dma DMA session
+ * @param transfer_id DMA transfer identifier to be freed
+ */
+void odp_dma_transfer_id_free(odp_dma_t dma, odp_dma_transfer_id_t transfer_id);
+
+/**
+ * Get printable value for DMA session handle
+ *
+ * @param dma Handle to be converted for debugging
+ *
+ * @return uint64_t value that can be used to print/display this handle
+ */
+uint64_t odp_dma_to_u64(odp_dma_t dma);
+
+/**
+ * Print debug info about DMA session
+ *
+ * Print implementation defined information about DMA session to the ODP log.
+ * The information is intended to be used for debugging.
+ *
+ * @param dma DMA session handle
+ */
+void odp_dma_print(odp_dma_t dma);
+
+/**
+ * Check DMA completion event
+ *
+ * Reads DMA completion event (ODP_EVENT_DMA_COMPL), and returns if the transfer succeeded or
+ * failed. The call outputs optionally transfer results. Use NULL as 'result' pointer if results
+ * are not required.
+ *
+ * @param dma_compl DMA completion event
+ * @param[out] result Pointer to transfer result structure for output, or NULL when not used.
+ *
+ * @retval 0 Transfer was successful
+ * @retval <0 Transfer failed
+ */
+int odp_dma_compl_result(odp_dma_compl_t dma_compl, odp_dma_result_t *result);
+
+/**
+ * Convert event to DMA completion event
+ *
+ * Converts an ODP_EVENT_DMA_COMPL type event to a DMA completion event.
+ *
+ * @param ev Event handle
+ *
+ * @return DMA completion event handle
+ */
+odp_dma_compl_t odp_dma_compl_from_event(odp_event_t ev);
+
+/**
+ * Convert DMA completion event to event
+ *
+ * @param dma_compl DMA completion event handle
+ *
+ * @return Event handle
+ */
+odp_event_t odp_dma_compl_to_event(odp_dma_compl_t dma_compl);
+
+/**
+ * Get printable value for DMA completion event handle
+ *
+ * @param dma_compl Handle to be converted for debugging
+ *
+ * @return uint64_t value that can be used to print/display this handle
+ */
+uint64_t odp_dma_compl_to_u64(odp_dma_compl_t dma_compl);
+
+/**
+ * DMA completion event user area
+ *
+ * Returns pointer to the user area associated with the completion event. Size of the area is fixed
+ * and defined in completion event pool parameters.
+ *
+ * @param dma_compl DMA completion event handle
+ *
+ * @return Pointer to the user area of the completion event
+ * @retval NULL The completion event does not have user area
+ */
+void *odp_dma_compl_user_area(odp_dma_compl_t dma_compl);
+
+/**
+ * Allocate DMA completion event
+ *
+ * Allocates a DMA completion event from a pool. The pool must have been created with
+ * odp_dma_pool_create() call. All completion event metadata are set to their default values.
+ *
+ * @param pool Pool handle
+ *
+ * @return DMA completion event handle
+ * @retval ODP_DMA_COMPL_INVALID Completion event could not be allocated
+ */
+odp_dma_compl_t odp_dma_compl_alloc(odp_pool_t pool);
+
+/**
+ * Free DMA completion event
+ *
+ * Frees a DMA completion event into the pool it was allocated from.
+ *
+ * @param dma_compl DMA completion event handle
+ */
+void odp_dma_compl_free(odp_dma_compl_t dma_compl);
+
+/**
+ * Print DMA completion event debug information
+ *
+ * Prints implementation specific debug information about
+ * the completion event to the ODP log.
+ *
+ * @param dma_compl DMA completion event handle
+ */
+void odp_dma_compl_print(odp_dma_compl_t dma_compl);
+
+/**
+ * Initialize DMA completion event pool parameters
+ *
+ * Initialize an odp_dma_pool_param_t to its default values.
+ *
+ * @param[out] pool_param Parameter structure to be initialized
+ */
+void odp_dma_pool_param_init(odp_dma_pool_param_t *pool_param);
+
+/**
+ * Create DMA completion event pool
+ *
+ * Creates a pool of DMA completion events (ODP_EVENT_DMA_COMPL). Pool type is ODP_POOL_DMA_COMPL.
+ * The use of pool name is optional. Unique names are not required. However, odp_pool_lookup()
+ * returns only a single matching pool. Use odp_dma_pool_param_init() to initialize pool parameters
+ * into their default values. Parameters values must not exceed pool capabilities
+ * (odp_dma_pool_capability_t).
+ *
+ * @param name Name of the pool or NULL. Maximum string length is ODP_POOL_NAME_LEN.
+ * @param pool_param Pool parameters
+ *
+ * @return Handle of the created pool
+ * @retval ODP_POOL_INVALID Pool could not be created
+ */
+odp_pool_t odp_dma_pool_create(const char *name, const odp_dma_pool_param_t *pool_param);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
+
diff --git a/include/odp/api/spec/dma_types.h b/include/odp/api/spec/dma_types.h
new file mode 100644
index 000000000..0a0e267df
--- /dev/null
+++ b/include/odp/api/spec/dma_types.h
@@ -0,0 +1,576 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP DMA
+ */
+
+#ifndef ODP_API_SPEC_DMA_TYPES_H_
+#define ODP_API_SPEC_DMA_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/event_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_dma ODP DMA
+ * DMA offload
+ * @{
+ */
+
+/**
+ * @typedef odp_dma_t
+ * DMA session
+ */
+
+/**
+ * @typedef odp_dma_transfer_id_t
+ * DMA transfer identifier
+ */
+
+/**
+ * @typedef odp_dma_compl_t
+ * DMA completion event
+ */
+
+/**
+ * @def ODP_DMA_INVALID
+ * Invalid DMA session
+ */
+
+/**
+ * @def ODP_DMA_TRANSFER_ID_INVALID
+ * Invalid DMA transfer identifier
+ */
+
+/**
+ * @def ODP_DMA_COMPL_INVALID
+ * Invalid DMA completion event
+ */
+
+/**
+ * @def ODP_DMA_NAME_LEN
+ * Maximum DMA name length in chars including null char
+ */
+
+/**
+ * DMA completion event pool capabilities
+ *
+ * Pool statistics are not supported with DMA completion event pools.
+ */
+typedef struct odp_dma_pool_capability_t {
+ /** Maximum number of DMA completion event pools
+ *
+ * See odp_pool_capability_t::max_pools for total capability. */
+ uint32_t max_pools;
+
+ /** Maximum number of DMA completion events in a pool */
+ uint32_t max_num;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * See buf.uarea_persistence of odp_pool_capability_t for details
+ * (odp_pool_capability_t::uarea_persistence). */
+ odp_bool_t uarea_persistence;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+} odp_dma_pool_capability_t;
+
+/**
+ * DMA completion event pool parameters
+ */
+typedef struct odp_dma_pool_param_t {
+ /** Number of DMA completion events in the pool
+ *
+ * Maximum value is defined by 'max_num' pool capability */
+ uint32_t num;
+
+ /** User area size in bytes
+ *
+ * Maximum value is defined by 'max_uarea_size' pool capability. Specify as 0 if no user
+ * area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Parameters for user area initialization */
+ struct {
+ /** See uarea_init.init_fn of odp_pool_param_t for details
+ * (odp_pool_param_t::init_fn). Function is called during
+ * odp_dma_pool_create(). */
+ void (*init_fn)(void *uarea, uint32_t size, void *args, uint32_t index);
+
+ /** See uarea_init.args of odp_pool_param_t for details
+ * (odp_pool_param_t::args). */
+ void *args;
+
+ } uarea_init;
+
+ /** Maximum number of events cached locally per thread
+ *
+ * See odp_pool_param_t::cache_size documentation for details. Valid values range from
+ * 'min_cache_size' to 'max_cache_size' capability. The default value is implementation
+ * specific and set by odp_dma_pool_param_init().
+ */
+ uint32_t cache_size;
+
+} odp_dma_pool_param_t;
+
+/* Includes pool_types.h, which depends on odp_dma_pool_param_t. */
+#include <odp/api/queue_types.h>
+
+/**
+ * DMA transfer direction
+ *
+ * Transfer direction defines source and destination memory type of DMA transfers. API specification
+ * defines only one option (#ODP_DMA_MAIN_TO_MAIN) for the transfer direction. It is used for
+ * transfers within the main memory. Some implementations may extend this enumeration with
+ * implementation specific directions and memory types (e.g. from main memory to a device, etc.).
+ */
+typedef uint32_t odp_dma_direction_t;
+
+/** DMA transfer within the main memory */
+#define ODP_DMA_MAIN_TO_MAIN 0x1u
+
+/**
+ * DMA transfer type
+ *
+ * Transfer type defines how DMA transfers operate data. Currently, only one transfer type is
+ * defined (#ODP_DMA_TYPE_COPY).
+ *
+ */
+typedef uint32_t odp_dma_transfer_type_t;
+
+/** Copy data
+ *
+ * Copy all data from source segment(s) to destination segment(s). There may be different
+ * number of source and destination segments in a transfer, but total length of all source
+ * segments must be equal to total length of all destination segments. Segments must not
+ * point to overlapping memory addresses. There are no alignment requirements for
+ * segment addresses or lengths. Data transfer from source to destination may happen
+ * in any segment and byte order.
+ */
+#define ODP_DMA_TYPE_COPY 0x1u
+
+/**
+ * DMA transfer completion mode
+ *
+ * Transfer completion mode defines how transfer completion is reported to the application.
+ * Completion modes are: #ODP_DMA_COMPL_NONE, #ODP_DMA_COMPL_SYNC, #ODP_DMA_COMPL_EVENT, and
+ * #ODP_DMA_COMPL_POLL
+ *
+ * If not otherwise specified, a DMA transfer is complete when memory reads and writes are complete
+ * for all its segments, and writes are visible to all memory observers (threads and
+ * HW accelerators).
+ */
+typedef uint32_t odp_dma_compl_mode_t;
+
+/** No completion indication
+ *
+ * Application uses odp_dma_transfer_start() call to start a DMA transfer, but does
+ * not request a completion notification for it. This can be useful for example when application
+ * starts a burst of transfers, but requests a completion event only on the last one
+ * (none on others).
+ */
+#define ODP_DMA_COMPL_NONE 0x1u
+
+/** Synchronous transfer
+ *
+ * Application uses odp_dma_transfer() call for DMA transfers. Each call performs
+ * the requested transfer and returns when the transfer is complete.
+ */
+#define ODP_DMA_COMPL_SYNC 0x2u
+
+/** Asynchronous transfer with completion event
+ *
+ * Application uses odp_dma_transfer_start() call to start a DMA transfer. The
+ * transfer is complete when application receives the completion event.
+ */
+#define ODP_DMA_COMPL_EVENT 0x4u
+
+/** Asynchronous transfer with completion polling
+ *
+ * Application uses odp_dma_transfer_start() call to start a DMA transfer and uses
+ * odp_dma_transfer_done() call to check if the transfer has completed.
+ */
+#define ODP_DMA_COMPL_POLL 0x8u
+
+/**
+ * DMA transfer data format
+ */
+typedef enum {
+ /** Data format is raw memory address */
+ ODP_DMA_FORMAT_ADDR = 0,
+
+ /** Data format is odp_packet_t */
+ ODP_DMA_FORMAT_PACKET
+
+} odp_dma_data_format_t;
+
+/**
+ * DMA transfer ordering
+ *
+ * These options specify ordering of consecutive DMA transfers within a session. Transfer order
+ * is defined by the order of consecutive transfer (start) calls and the order of transfers
+ * within each multi-transfer call. Note that ordering option matters also when using
+ * odp_dma_transfer_multi() call, as ODP_DMA_ORDER_NONE allows implementation to perform transfers
+ * in parallel.
+ *
+ * These options do not apply to data (segment or byte) processing order within a transfer.
+ * If two transfers read/write overlapping memory areas, an appropriate transfer ordering option
+ * (e.g. ODP_DMA_ORDER_ALL) needs to be used for correct operation.
+ */
+typedef enum {
+ /** No specific ordering between transfers
+ *
+ * This may result the best performance (maximum implementation parallelism) as
+ * transfers may start and complete in any order. */
+ ODP_DMA_ORDER_NONE = 0,
+
+ /** Report transfer completions in order
+ *
+ * Transfers may be performed in any order, but transfer completions must be reported
+ * in the same order they were started within a session. This allows application to
+ * start multiple transfers and wait only completion of the last one. */
+ ODP_DMA_ORDER_COMPL,
+
+ /** Perform all transfers in order
+ *
+ * Perform transfers and report their completions in the same order they were started
+ * within a session. This enables for example a subsequent transfer to read data
+ * written by a previous transfer. */
+ ODP_DMA_ORDER_ALL
+
+} odp_dma_transfer_order_t;
+
+/**
+ * DMA transfer multi-thread safeness
+ */
+typedef enum {
+ /** Multi-thread safe operation
+ *
+ * Multiple threads may perform DMA transfers concurrently on the same DMA session.
+ */
+ ODP_DMA_MT_SAFE = 0,
+
+ /** Application serializes operations
+ *
+ * Multiple threads may perform DMA transfers on the same DMA session, but application
+ * serializes all transfer related calls (odp_dma_transfer(), odp_dma_transfer_start(),
+ * _start_multi(), _done() and _result()). Threads do not call any of these operations
+ * concurrently.
+ */
+ ODP_DMA_MT_SERIAL
+
+} odp_dma_mt_mode_t;
+
+/**
+ * DMA capabilities
+ */
+typedef struct odp_dma_capability_t {
+ /** Maximum number of DMA sessions
+ *
+ * The value of zero means that DMA offload is not available.
+ */
+ uint32_t max_sessions;
+
+ /** Maximum number of transfers per DMA session
+ *
+ * Maximum number of transfers that can be in-flight (started but not yet completed)
+ * per session. When this limit is reached, new transfer requests may not be accepted
+ * until some previously started transfers are complete. */
+ uint32_t max_transfers;
+
+ /** Maximum number of source segments in a single transfer */
+ uint32_t max_src_segs;
+
+ /** Maximum number of destination segments in a single transfer */
+ uint32_t max_dst_segs;
+
+ /** Maximum number of destination and source segments combined in a single transfer */
+ uint32_t max_segs;
+
+ /** Maximum segment length in bytes
+ *
+ * This is the maximum length of any source or destination segment. */
+ uint32_t max_seg_len;
+
+ /** Supported completion modes
+ *
+ * Each supported completion mode has a corresponding flag set in the mask.
+ * Synchronous transfer (ODP_DMA_COMPL_SYNC) is always supported.
+ */
+ odp_dma_compl_mode_t compl_mode_mask;
+
+ /**
+ * Scheduled queue support
+ *
+ * 0: Scheduled queues are not supported as DMA completion queues
+ * 1: Scheduled queues are supported as DMA completion queues
+ */
+ odp_bool_t queue_type_sched;
+
+ /**
+ * Plain queue support
+ *
+ * 0: Plain queues are not supported as DMA completion queues
+ * 1: Plain queues are supported as DMA completion queues
+ */
+ odp_bool_t queue_type_plain;
+
+ /** DMA completion event pool capabilities */
+ odp_dma_pool_capability_t pool;
+
+} odp_dma_capability_t;
+
+/**
+ * DMA session parameters
+ */
+typedef struct odp_dma_param_t {
+ /** Transfer direction
+ *
+ * The default value is ODP_DMA_MAIN_TO_MAIN.
+ */
+ odp_dma_direction_t direction;
+
+ /** Transfer type
+ *
+ * The default value is ODP_DMA_TYPE_COPY.
+ */
+ odp_dma_transfer_type_t type;
+
+ /** Transfer completion modes
+ *
+ * Specify the completion modes application will use within the session.
+ *
+ * Multiple modes may be selected, but it is implementation specific which combinations
+ * are supported. If an unsupported combination is requested odp_dma_create() returns
+ * a failure. See odp_dma_capability_t::compl_mode_mask for the supported modes.
+ */
+ odp_dma_compl_mode_t compl_mode_mask;
+
+ /** Transfer operation multi-thread safeness
+ *
+ * The default value is ODP_DMA_MT_SAFE.
+ */
+ odp_dma_mt_mode_t mt_mode;
+
+ /** Transfer ordering
+ *
+ * The default value is ODP_DMA_ORDER_NONE.
+ */
+ odp_dma_transfer_order_t order;
+
+} odp_dma_param_t;
+
+/**
+ * DMA segment
+ */
+typedef struct odp_dma_seg_t {
+ /** Segment start address or packet handle */
+ union {
+ /** Segment start address in memory
+ *
+ * Defines segment start when data format is #ODP_DMA_FORMAT_ADDR. Ignored with
+ * other data formats.
+ */
+ void *addr;
+
+ /** Packet handle
+ *
+ * Defines the packet when data format is #ODP_DMA_FORMAT_PACKET. Ignored
+ * with other data formats. */
+ odp_packet_t packet;
+
+ };
+
+ /** Segment length in bytes
+ *
+ * Defines segment length with all data formats. The maximum value is defined by
+ * max_seg_len capability. When data format is #ODP_DMA_FORMAT_PACKET, the value must not
+ * exceed odp_packet_len() - 'offset'.
+ */
+ uint32_t len;
+
+ /** Segment start offset into the packet
+ *
+ * Defines segment start within the packet data. The offset is calculated from
+ * odp_packet_data() position, and the value must not exceed odp_packet_len().
+ * Ignored when data format is other than #ODP_DMA_FORMAT_PACKET.
+ */
+ uint32_t offset;
+
+ /** Segment hints
+ *
+ * Depending on the implementation, setting these hints may improve performance.
+ * Initialize all unused bits to zero.
+ */
+ union {
+ /** Segment hints bit field */
+ struct {
+ /** Allow full cache line access
+ *
+ * When set to 1, data on the same cache line with the destination segment
+ * is allowed to be overwritten. This hint is ignored on source segments.
+ */
+ uint16_t full_lines : 1;
+ };
+
+ /** All bits of the bit field structure
+ *
+ * This can be used to set/clear all bits, or to perform bitwise operations
+ * on those.
+ */
+ uint16_t all_hints;
+ };
+
+} odp_dma_seg_t;
+
+/**
+ * DMA transfer parameters
+ *
+ * These parameters define data sources and destinations for a DMA transfer. Capabilities specify
+ * the maximum number of segments and the maximum segment length that are supported.
+ *
+ * The selected data format specifies how segment structure fields are used. When data format is
+ * ODP_DMA_FORMAT_ADDR, set segment start address (odp_dma_seg_t::addr) and
+ * length (odp_dma_seg_t::len). When data format is ODP_DMA_FORMAT_PACKET, set packet
+ * handle (odp_dma_seg_t::packet), segment start offset (odp_dma_seg_t::offset) and length.
+ * If a DMA segment spans over multiple packet segments, it is considered as equally many
+ * DMA segments. So, take packet segmentation into account when making sure that the maximum
+ * number of DMA segments capabilities are not exceeded.
+ */
+typedef struct odp_dma_transfer_param_t {
+ /** Source data format
+ *
+ * The default value is ODP_DMA_FORMAT_ADDR.
+ */
+ odp_dma_data_format_t src_format;
+
+ /** Destination data format
+ *
+ * The default value is ODP_DMA_FORMAT_ADDR.
+ */
+ odp_dma_data_format_t dst_format;
+
+ /** Number of source segments
+ *
+ * The default value is 1.
+ */
+ uint32_t num_src;
+
+ /** Number of destination segments
+ *
+ * The default value is 1.
+ */
+ uint32_t num_dst;
+
+ /** Table of source segments
+ *
+ * The table has 'num_src' entries. Data format is defined by 'src_format'.
+ */
+ odp_dma_seg_t *src_seg;
+
+ /** Table of destination segments
+ *
+ * The table has 'num_dst' entries. Data format is defined by 'dst_format'.
+ */
+ odp_dma_seg_t *dst_seg;
+
+} odp_dma_transfer_param_t;
+
+/**
+ * DMA transfer completion parameters
+ */
+typedef struct odp_dma_compl_param_t {
+ /** Completion mode
+ *
+ * Select a completion mode: #ODP_DMA_COMPL_EVENT, #ODP_DMA_COMPL_POLL or
+ * #ODP_DMA_COMPL_NONE. The mode must match one of the modes selected in session creation
+ * parameters (odp_dma_param_t::compl_mode_mask).
+ *
+ * ODP_DMA_COMPL_NONE can be used to specify that completion indication is not requested.
+ * Application may for example start a series of transfers and request completion
+ * indication only on the last one.
+ */
+ odp_dma_compl_mode_t compl_mode;
+
+ /** Transfer identifier
+ *
+ * Transfer identifier is used in ODP_DMA_COMPL_POLL mode. Application passes the same
+ * identifier here and to a later odp_dma_transfer_done() call to check transfer
+ * completion status. Identifiers are allocated with odp_dma_transfer_id_alloc().
+ * The identifier of a completed transfer may be reused for another transfer.
+ */
+ odp_dma_transfer_id_t transfer_id;
+
+ /** Completion event
+ *
+ * When a transfer is started in ODP_DMA_COMPL_EVENT mode, this event is sent to
+ * the completion queue when the transfer is complete. The event type must be
+ * ODP_EVENT_DMA_COMPL. Use odp_dma_compl_result() to retrieve transfer results from
+ * the event.
+ */
+ odp_event_t event;
+
+ /** Completion queue
+ *
+ * The completion event is sent into this queue in ODP_DMA_COMPL_EVENT mode.
+ */
+ odp_queue_t queue;
+
+ /** User context pointer
+ *
+ * User defined context pointer which is copied to transfer results (odp_dma_result_t). The
+ * value does not need to represent a valid address (any intptr_t value is allowed).
+ *
+ * The default value is NULL.
+ */
+ void *user_ptr;
+
+} odp_dma_compl_param_t;
+
+/** DMA transfer results */
+typedef struct odp_dma_result_t {
+ /** DMA transfer success
+ *
+ * true: DMA transfer was successful
+ * false: DMA transfer failed
+ */
+ odp_bool_t success;
+
+ /** User context pointer
+ *
+ * User defined context pointer value from transfer completion parameters
+ * (odp_dma_compl_param_t). The default value is NULL.
+ */
+ void *user_ptr;
+
+} odp_dma_result_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
+
diff --git a/include/odp/api/spec/errno.h b/include/odp/api/spec/errno.h
index 9b60a98ba..85c002e2b 100644
--- a/include/odp/api/spec/errno.h
+++ b/include/odp/api/spec/errno.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP errno API
*/
-#ifndef ODP_ERRNO_H_
-#define ODP_ERRNO_H_
+#ifndef ODP_API_SPEC_ERRNO_H_
+#define ODP_API_SPEC_ERRNO_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,6 +18,8 @@ extern "C" {
/**
* @defgroup odp_errno ODP ERRNO
+ * Error number.
+ *
* @details
* <b> ODP errno </b>
*
@@ -29,8 +29,7 @@ extern "C" {
* may initialize errno to zero at anytime by calling odp_errno_zero(). Other
* ODP functions never set errno to zero. Valid errno values are non-zero
* and implementation specific. It's also implementation specific which
- * functions set errno in addition to those explicitly specified by
- * the API spec. ODP errno is initially zero.
+ * functions set errno. ODP errno is initially zero.
*
* @{
*/
diff --git a/include/odp/api/spec/event.h b/include/odp/api/spec/event.h
index fdfa52d1c..69464125b 100644
--- a/include/odp/api/spec/event.h
+++ b/include/odp/api/spec/event.h
@@ -1,55 +1,168 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
*/
-
/**
* @file
*
* ODP event
*/
-#ifndef ODP_API_EVENT_H_
-#define ODP_API_EVENT_H_
+#ifndef ODP_API_SPEC_EVENT_H_
+#define ODP_API_SPEC_EVENT_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_event ODP EVENT
- * Operations on an event.
+#include <odp/api/event_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/pool_types.h>
+
+/** @addtogroup odp_event
+ * Generic event metadata and operations.
* @{
*/
+/**
+ * Event type of an event
+ *
+ * Event type specifies purpose and general format of an event.
+ *
+ * @param event Event handle
+ *
+ * @return Event type
+ */
+odp_event_type_t odp_event_type(odp_event_t event);
/**
- * @typedef odp_event_t
- * ODP event
+ * Event subtype of an event
+ *
+ * Event subtype expands event type specification by providing more detailed
+ * purpose and format of an event.
+ *
+ * @param event Event handle
+ *
+ * @return Event subtype
*/
+odp_event_subtype_t odp_event_subtype(odp_event_t event);
/**
- * @def ODP_EVENT_INVALID
- * Invalid event
+ * Event type and subtype of an event
+ *
+ * Returns event type and outputs event subtype.
+ *
+ * @param event Event handle
+ * @param[out] subtype Pointer to event subtype for output
+ *
+ * @return Event type
*/
+odp_event_type_t odp_event_types(odp_event_t event,
+ odp_event_subtype_t *subtype);
/**
- * @typedef odp_event_type_t
- * ODP event types:
- * ODP_EVENT_BUFFER, ODP_EVENT_PACKET, ODP_EVENT_TIMEOUT,
- * ODP_EVENT_CRYPTO_COMPL
+ * Event types and subtypes of multiple events
+ *
+ * Outputs the event types and subtypes (optional) of all given events into the
+ * output arrays. Application can pass NULL as 'subtype' parameter if subtype
+ * values are not required. The types are written in the same order as input
+ * events.
+ *
+ * @param event Array of event handles
+ * @param[out] type Event type array for output
+ * @param[out] subtype Event subtype array for output, or NULL
+ * @param num Number of events, event types, and optionally subtypes
*/
+void odp_event_types_multi(const odp_event_t event[], odp_event_type_t type[],
+ odp_event_subtype_t subtype[], int num);
/**
- * Get event type
+ * Event type of multiple events
*
- * @param event Event handle
+ * Returns the number of first events in the array which have the same event
+ * type. Outputs the event type of those events.
*
- * @return Event type
+ * @param event Array of event handles
+ * @param num Number of events (> 0)
+ * @param[out] type Event type pointer for output
+ *
+ * @return Number of first events (1 ... num) with the same event type
+ * (includes event[0])
*/
-odp_event_type_t odp_event_type(odp_event_t event);
+int odp_event_type_multi(const odp_event_t event[], int num,
+ odp_event_type_t *type);
+
+/**
+ * Event pool
+ *
+ * Returns handle to the pool where the event was allocated from. If the
+ * underlying event type does not have an API for returning pool handle
+ * (e.g. ODP_EVENT_IPSEC_STATUS), ODP_POOL_INVALID is returned.
+ *
+ * @param event Event handle
+ *
+ * @return Pool handle or ODP_POOL_INVALID depending on event type
+ */
+odp_pool_t odp_event_pool(odp_event_t event);
+
+/**
+ * Event user area
+ *
+ * Returns pointer to the user area associated with the event. This maps to the
+ * user area of underlying event type (e.g. odp_packet_user_area() for packets).
+ * If the event does not have user area, NULL is returned.
+ *
+ * @param event Event handle
+ *
+ * @return Pointer to the user area of the event
+ * @retval NULL The event does not have user area
+ */
+void *odp_event_user_area(odp_event_t event);
+
+/**
+ * Event user area and flag
+ *
+ * Returns pointer to the user area and outputs value of user flag associated
+ * with the event. The user area maps to the user area of underlying event type
+ * (e.g. odp_packet_user_area() for packets). If the event does not have user
+ * area, NULL is returned.
+ *
+ * The user flag maps to the user flag value of underlying event type (e.g.
+ * odp_packet_user_flag() for packets). If the event does not have user flag, a
+ * negative value is outputted.
+ *
+ * @param event Event handle
+ * @param[out] flag User flag value pointer for output. >0 if user flag is
+ * set, 0 if flags is not set, or <0 if event does not have
+ * user flag.
+ *
+ * @return Pointer to the user area of the event
+ * @retval NULL The event does not have user area
+ */
+void *odp_event_user_area_and_flag(odp_event_t event, int *flag);
+
+/**
+ * Filter and convert packet events
+ *
+ * Checks event type of all input events, converts all packet events and outputs
+ * packet handles. Returns the number packet handles outputted. Outputs the
+ * remaining, non-packet event handles to 'remain' array. Handles are outputted
+ * to both arrays in the same order those are stored in 'event' array. Both
+ * output arrays must fit 'num' elements.
+ *
+ * @param event Array of event handles
+ * @param[out] packet Packet handle array for output
+ * @param[out] remain Event handle array for output of remaining, non-packet
+ * events
+ * @param num Number of events (> 0)
+ *
+ * @return Number of packets outputted (0 ... num)
+ */
+int odp_event_filter_packet(const odp_event_t event[],
+ odp_packet_t packet[],
+ odp_event_t remain[], int num);
/**
* Get printable value for an odp_event_t
@@ -65,6 +178,20 @@ odp_event_type_t odp_event_type(odp_event_t event);
uint64_t odp_event_to_u64(odp_event_t hdl);
/**
+ * Check that event is valid
+ *
+ * This function can be used for debugging purposes to check if an event handle represents
+ * a valid event. The level of error checks depends on the implementation. The call should not
+ * crash if the event handle is corrupted.
+ *
+ * @param event Event handle
+ *
+ * @retval 1 Event handle represents a valid event.
+ * @retval 0 Event handle does not represent a valid event.
+ */
+int odp_event_is_valid(odp_event_t event);
+
+/**
* Free event
*
* Frees the event based on its type. Results are undefined if event
@@ -76,6 +203,66 @@ uint64_t odp_event_to_u64(odp_event_t hdl);
void odp_event_free(odp_event_t event);
/**
+ * Free multiple events
+ *
+ * Otherwise like odp_event_free(), but frees multiple events to their
+ * originating pools.
+ *
+ * @param event Array of event handles
+ * @param num Number of events to free
+ */
+void odp_event_free_multi(const odp_event_t event[], int num);
+
+/**
+ * Free multiple events to the same pool
+ *
+ * Otherwise like odp_event_free_multi(), but all events must be from the
+ * same originating pool.
+ *
+ * @param event Array of event handles
+ * @param num Number of events to free
+ */
+void odp_event_free_sp(const odp_event_t event[], int num);
+
+/**
+ * Event flow id value
+ *
+ * Returns the flow id value set in the event.
+ * Usage of flow id enables scheduler to maintain multiple synchronization
+ * contexts per single queue. For example, when multiple flows are assigned to
+ * an atomic queue, events of a single flow (events from the same queue with
+ * the same flow id value) are guaranteed to be processed by only single thread
+ * at a time. For packets received through packet input initial
+ * event flow id will be same as flow hash generated for packets. The hash
+ * algorithm and therefore the resulting flow id value is implementation
+ * specific. Use pktio API configuration options to select the fields used for
+ * initial flow id calculation. For all other events initial flow id is zero
+ * An application can change event flow id using odp_event_flow_id_set().
+ *
+ * @param event Event handle
+ *
+ * @return Flow id of the event
+ *
+ */
+uint32_t odp_event_flow_id(odp_event_t event);
+
+/**
+ * Set event flow id value
+ *
+ * Store the event flow id for the event and sets the flow id flag.
+ * When scheduler is configured as flow aware, scheduled queue synchronization
+ * will be based on this id within each queue.
+ * When scheduler is configured as flow unaware, event flow id is ignored by
+ * the implementation.
+ * The value of flow id must be less than the number of flows configured in the
+ * scheduler.
+ *
+ * @param event Event handle
+ * @param flow_id Flow event id to be set.
+ */
+void odp_event_flow_id_set(odp_event_t event, uint32_t flow_id);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/event_types.h b/include/odp/api/spec/event_types.h
new file mode 100644
index 000000000..b967e2871
--- /dev/null
+++ b/include/odp/api/spec/event_types.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event API type definitions
+ */
+
+#ifndef ODP_API_SPEC_EVENT_TYPES_H_
+#define ODP_API_SPEC_EVENT_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_event ODP EVENT
+ * @{
+ */
+
+/**
+ * @typedef odp_event_t
+ * ODP event
+ */
+
+/**
+ * @def ODP_EVENT_INVALID
+ * Invalid event
+ */
+
+/**
+ * @typedef odp_event_type_t
+ * Event type
+ *
+ * Event type specifies purpose and general format of an event. It can be
+ * checked with odp_event_type() or odp_event_types(). Each event type has
+ * functions (e.g. odp_buffer_from_event()) to convert between the generic event
+ * handle (odp_event_t) and the type specific handle (e.g. odp_buffer_t).
+ * Results are undefined, if conversion function of a wrong event type is used.
+ * Application cannot change event type by chaining conversion functions.
+ *
+ * List of event types:
+ * - ODP_EVENT_BUFFER
+ * - Buffer event (odp_buffer_t) for simple data storage and message passing
+ * - ODP_EVENT_PACKET
+ * - Packet event (odp_packet_t) containing packet data and plenty of
+ * packet processing related metadata
+ * - ODP_EVENT_TIMEOUT
+ * - Timeout event (odp_timeout_t) from a timer
+ * - ODP_EVENT_IPSEC_STATUS
+ * - IPSEC status update event (odp_ipsec_status_t)
+ * - ODP_EVENT_PACKET_VECTOR
+ * - Vector of packet events (odp_packet_t) as odp_packet_vector_t
+ * - ODP_EVENT_PACKET_TX_COMPL
+ * - Packet Tx completion event (odp_packet_tx_compl_t) generated as a result of a Packet Tx
+ * completion.
+ * - ODP_EVENT_DMA_COMPL
+ * - DMA completion event (odp_dma_compl_t) indicates that a DMA transfer has finished
+ * - ODP_EVENT_ML_COMPL
+ * - ML completion event (odp_ml_compl_t) indicates that an ML operation has finished
+ */
+
+/**
+ * @typedef odp_event_subtype_t
+ * Event subtype
+ *
+ * Event subtype expands event type specification by providing more detailed
+ * purpose and format of an event. It can be checked with odp_event_subtype() or
+ * odp_event_types(). Each event subtype may define specific functions
+ * (e.g. odp_ipsec_packet_from_event()) to convert between the generic event
+ * handle (odp_event_t) and event type specific handle (e.g. odp_packet_t). When
+ * subtype is known, these subtype specific functions should be preferred over
+ * the event type general function (e.g. odp_packet_from_event()). Results are
+ * undefined, if conversion function of a wrong event subtype is used.
+ * Application cannot change event subtype by chaining conversion functions.
+ *
+ * List of event subtypes:
+ * - ODP_EVENT_PACKET_BASIC
+ * - Packet event (odp_packet_t) with basic packet metadata
+ * - ODP_EVENT_PACKET_COMP
+ * - Packet event (odp_packet_t) generated as a result of a compression/
+ * decompression operation. It contains compression specific metadata in
+ * addition to the basic packet metadata.
+ * - ODP_EVENT_PACKET_CRYPTO
+ * - Packet event (odp_packet_t) generated as a result of a Crypto
+ * operation. It contains crypto specific metadata in addition to the
+ * basic packet metadata.
+ * - ODP_EVENT_PACKET_IPSEC
+ * - Packet event (odp_packet_t) generated as a result of an IPsec
+ * operation. It contains IPSEC specific metadata in addition to the basic
+ * packet metadata.
+ * - ODP_EVENT_NO_SUBTYPE
+ * - An event type does not have any subtypes defined
+ * - ODP_EVENT_ML_COMPL_LOAD
+ * - ML completion event (odp_ml_compl_t) that contains results from a completed model load or
+ * unload operation.
+ * - ODP_EVENT_ML_COMPL_RUN
+ * - ML completion event (odp_ml_compl_t) that contains results from a completed model run
+ * operation.
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/hash.h b/include/odp/api/spec/hash.h
index 66b740e2c..cb4f3241a 100644
--- a/include/odp/api/spec/hash.h
+++ b/include/odp/api/spec/hash.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP Hash functions
*/
-#ifndef ODP_API_HASH_H_
-#define ODP_API_HASH_H_
+#ifndef ODP_API_SPEC_HASH_H_
+#define ODP_API_SPEC_HASH_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,8 +18,8 @@ extern "C" {
#include <odp/api/std_types.h>
-/** @defgroup odp_hash ODP HASH FUNCTIONS
- * ODP Hash functions
+/** @defgroup odp_hash ODP HASH
+ * Hash functions.
* @{
*/
diff --git a/include/odp/api/spec/hints.h b/include/odp/api/spec/hints.h
index 7434c6a5c..fa5b1b9bb 100644
--- a/include/odp/api/spec/hints.h
+++ b/include/odp/api/spec/hints.h
@@ -1,18 +1,15 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP compiler hints
*/
-#ifndef ODP_API_HINTS_H_
-#define ODP_API_HINTS_H_
+#ifndef ODP_API_SPEC_HINTS_H_
+#define ODP_API_SPEC_HINTS_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -59,19 +56,18 @@ extern "C" {
/**
* Branch likely taken
*/
-#define odp_likely(x) __builtin_expect((x), 1)
+#define odp_likely(x) __builtin_expect(!!(x), 1)
/**
* Branch unlikely taken
*/
-#define odp_unlikely(x) __builtin_expect((x), 0)
-
+#define odp_unlikely(x) __builtin_expect(!!(x), 0)
/*
* __builtin_prefetch (const void *addr, rw, locality)
*
* rw 0..1 (0: read, 1: write)
- * locality 0..3 (0: dont leave to cache, 3: leave on all cache levels)
+ * locality 0..3 (0: don't leave to cache, 3: leave on all cache levels)
*/
/**
@@ -84,8 +80,6 @@ extern "C" {
*/
#define odp_prefetch_store(x) __builtin_prefetch((x), 1, 3)
-
-
#else
#define ODP_WEAK_SYMBOL
@@ -99,7 +93,6 @@ extern "C" {
#endif
-
/**
* @}
*/
diff --git a/include/odp/api/spec/init.h b/include/odp/api/spec/init.h
index 154cdf8f3..ee787665f 100644
--- a/include/odp/api/spec/init.h
+++ b/include/odp/api/spec/init.h
@@ -1,26 +1,14 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
-
/**
* @file
- *
- * ODP initialization.
- * ODP requires a global level init for the process and a local init per
- * thread before the other ODP APIs may be called.
- * - odp_init_global()
- * - odp_init_local()
- *
- * For a graceful termination the matching termination APIs exit
- * - odp_term_global()
- * - odp_term_local()
*/
-#ifndef ODP_API_INIT_H_
-#define ODP_API_INIT_H_
+#ifndef ODP_API_SPEC_INIT_H_
+#define ODP_API_SPEC_INIT_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -29,11 +17,11 @@ extern "C" {
#include <odp/api/std_types.h>
#include <odp/api/hints.h>
-#include <odp/api/thread.h>
+#include <odp/api/thread_types.h>
#include <odp/api/cpumask.h>
/** @defgroup odp_initialization ODP INITIALIZATION
- * Initialisation operations.
+ * ODP instance initialization and termination.
* @{
*/
@@ -43,14 +31,39 @@ extern "C" {
*/
/**
+ * Called from signal handler
+ */
+#define ODP_TERM_FROM_SIGH ((uint64_t)0x1)
+
+/**
+ * Last standard flag for abnormal terminate
+ *
+ * An implementation may use this for adding its own flags after the standard flags.
+ */
+#define ODP_TERM_LAST_FLAG ODP_TERM_FROM_SIGH
+
+/**
* ODP log level.
*/
typedef enum {
+ /** Debug */
ODP_LOG_DBG,
+
+ /** Warning */
+ ODP_LOG_WARN,
+
+ /** Error */
ODP_LOG_ERR,
+
+ /** Unimplemented */
ODP_LOG_UNIMPLEMENTED,
+
+ /** Abort */
ODP_LOG_ABORT,
+
+ /** Print */
ODP_LOG_PRINT
+
} odp_log_level_t;
/**
@@ -91,11 +104,12 @@ int odp_override_log(odp_log_level_t level, const char *fmt, ...);
* - By overriding the ODP implementation default abort function
* odp_override_abort().
*
- * @warning The latter option is less portable and GNU linker dependent
- * (utilizes function attribute "weak"). If both are defined, the odp_init_t
- * function pointer has priority over the override function.
+ * The latter option is less portable and GNU linker dependent (utilizes function
+ * attribute "weak"). If both are defined, the odp_init_t function pointer has
+ * priority over the override function.
*
- * @warning this function shall not return
+ * Note that no ODP calls should be called in the abort function and the function
+ * should not return.
*/
void odp_override_abort(void) ODP_NORETURN;
@@ -106,23 +120,55 @@ typedef int (*odp_log_func_t)(odp_log_level_t level, const char *fmt, ...);
typedef void (*odp_abort_func_t)(void) ODP_NORETURN;
/**
- * ODP initialization data
+ * Application memory model
+ */
+typedef enum {
+ /** Thread memory model: by default all memory is shareable between
+ * threads.
+ *
+ * Within a single ODP instance all ODP handles and pointers to ODP
+ * allocated data may be shared amongst threads independent of data
+ * allocation time (e.g. before or after thread creation). */
+ ODP_MEM_MODEL_THREAD = 0,
+
+ /** Process memory model: by default all memory is not shareable between
+ * processes.
+ *
+ * Within a single ODP instance all ODP handles and pointers to ODP
+ * allocated data (excluding non-single VA SHM blocks) may be shared
+ * amongst processes independent of data allocation time (e.g. before
+ * or after fork).
+ *
+ * @see ODP_SHM_SINGLE_VA
+ */
+ ODP_MEM_MODEL_PROCESS
+
+} odp_mem_model_t;
+
+/**
+ * Global initialization parameters
*
- * Data that is required to initialize the ODP API with the
- * application specific data such as specifying a logging callback, the log
- * level etc.
+ * These parameters may be used at global initialization time to configure and
+ * optimize ODP implementation to match the intended usage. Application
+ * specifies maximum resource usage. Implementation may round up resource
+ * reservations as needed. Initialization function returns a failure if resource
+ * requirements are too high. Init parameters may be used also to override
+ * logging and abort functions.
*
- * @note It is expected that all unassigned members are zero
+ * Use odp_init_param_init() to initialize the parameters into their default
+ * values. Unused parameters are left to default values.
*/
typedef struct odp_init_t {
/** Maximum number of worker threads the user will run concurrently.
Valid range is from 0 to platform specific maximum. Set both
num_worker and num_control to zero for default number of threads. */
int num_worker;
+
/** Maximum number of control threads the user will run concurrently.
Valid range is from 0 to platform specific maximum. Set both
num_worker and num_control to zero for default number of threads. */
int num_control;
+
/** Pointer to bit mask mapping CPUs available to this ODP instance
for running worker threads.
Initialize to a NULL pointer to use default CPU mapping.
@@ -138,6 +184,7 @@ typedef struct odp_init_t {
worker masks
*/
const odp_cpumask_t *worker_cpus;
+
/** Pointer to bit mask mapping CPUs available to this ODP instance
for running control threads.
Initialize to a NULL pointer to use default CPU mapping.
@@ -149,13 +196,46 @@ typedef struct odp_init_t {
worker and control masks do not overlap.
*/
const odp_cpumask_t *control_cpus;
+
/** Replacement for the default log fn */
odp_log_func_t log_fn;
+
/** Replacement for the default abort fn */
odp_abort_func_t abort_fn;
+
+ /** Unused features. These are hints to the ODP implementation that
+ * the application will not use any APIs associated with these
+ * features. Implementations may use this information to provide
+ * optimized behavior. Results are undefined if applications assert
+ * that a feature will not be used and it is used anyway.
+ */
+ odp_feature_t not_used;
+
+ /** Application memory model. The main application thread has to call
+ * odp_init_global() and odp_init_local() before creating threads that
+ * share ODP data. The default value is ODP_MEM_MODEL_THREAD.
+ */
+ odp_mem_model_t mem_model;
+
+ /** Shared memory parameters */
+ struct {
+ /** Maximum memory usage in bytes. This is the maximum
+ * amount of shared memory that application will reserve
+ * concurrently. Use 0 when not set. Default value is 0.
+ */
+ uint64_t max_memory;
+ } shm;
+
} odp_init_t;
/**
+ * Initialize the odp_init_t to default values for all fields
+ *
+ * @param[out] param Address of the odp_init_t to be initialized
+ */
+void odp_init_param_init(odp_init_t *param);
+
+/**
* @typedef odp_platform_init_t
* ODP platform initialization data
*
@@ -165,16 +245,28 @@ typedef struct odp_init_t {
* passing any required platform specific data.
*/
-
/**
* Global ODP initialization
*
- * This function must be called once (per instance) before calling any other
- * ODP API functions. A successful call creates a new ODP instance into the
- * system and outputs a handle for it. The handle is used in other calls
- * (e.g. odp_init_local()) as a reference to the instance. When user provides
- * configuration parameters, the platform may configure and optimize the
- * instance to match user requirements.
+ * An ODP instance is created with an odp_init_global() call. By default, each
+ * thread of the instance must call odp_init_local() before calling any other
+ * ODP API functions. Exceptions to this are functions that are needed for
+ * setting up parameters for odp_init_global() and odp_init_local() calls:
+ * - odp_init_param_init()
+ * - odp_cpumask_zero(), odp_cpumask_set(), etc functions to format
+ * odp_cpumask_t. However, these cpumask functions are excluded as their
+ * behaviour depend on global initialization parameters:
+ * odp_cpumask_default_worker(), odp_cpumask_default_control() and
+ * odp_cpumask_all_available()
+ *
+ * A successful odp_init_global() call outputs a handle for the new instance.
+ * The handle is used in other initialization and termination calls.
+ * For a graceful termination, odp_term_local() must be called first on each
+ * thread and then odp_term_global() only once.
+ *
+ * When user provides configuration parameters, the platform may configure and
+ * optimize the instance to match user requirements. A failure is returned if
+ * requirements cannot be met.
*
* Configuration parameters are divided into standard and platform specific
* parts. Standard parameters are supported by any ODP platform, where as
@@ -194,80 +286,135 @@ typedef struct odp_init_t {
* @retval 0 on success
* @retval <0 on failure
*
- * @see odp_term_global()
- * @see odp_init_local() which is required per thread before use.
+ * @see odp_init_local(), odp_term_global(), odp_init_param_init()
*/
int odp_init_global(odp_instance_t *instance,
const odp_init_t *params,
const odp_platform_init_t *platform_params);
/**
- * Global ODP termination
+ * Thread local ODP initialization
*
- * This function is the final ODP call made when terminating
- * an ODP application in a controlled way. It cannot handle exceptional
- * circumstances. In general it calls the API modules terminate functions in
- * the reverse order to that which the module init functions were called
- * during odp_init_global().
+ * All threads must call this function before calling any other ODP API
+ * functions. See odp_init_global() documentation for exceptions to this rule.
+ * Global initialization (odp_init_global()) must have completed prior calling
+ * this function.
*
- * This function must be called only after all threads of the instance have
- * executed odp_term_local(). To simplify synchronization between threads
- * odp_term_local() identifies which one is the last thread of an instance.
+ * The instance parameter specifies which ODP instance the thread joins.
+ * A thread may only join a single ODP instance at a time. The thread
+ * type parameter indicates if the thread does most part of application
+ * processing (ODP_THREAD_WORKER), or if it performs mostly background
+ * tasks (ODP_THREAD_CONTROL).
*
* @param instance Instance handle
+ * @param thr_type Thread type
*
* @retval 0 on success
* @retval <0 on failure
*
- * @warning The unwinding of HW resources to allow them to be reused without
- * reseting the device is a complex task that the application is expected to
- * coordinate. This api may have platform dependent implications.
+ * @see odp_init_global(), odp_term_local()
+ */
+int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type);
+
+/**
+ * Thread local ODP termination
+ *
+ * This function is the second to final ODP call made when terminating
+ * an ODP application in a controlled way. It cannot handle exceptional
+ * circumstances. It is recommended that all ODP resources are freed before
+ * the last thread (of the instance) calls this function. This helps ODP
+ * to avoid memory and other resource leaks.
+ *
+ * odp_term_global() may be called only after all threads of the instance have
+ * executed odp_term_local(). To simplify synchronization between threads
+ * a return value identifies which one is the last thread of an instance.
+ *
+ * @retval 1 on success and more ODP threads exist
+ * @retval 0 on success and this is the last ODP thread
+ * @retval <0 on failure
*
- * @see odp_init_global()
- * @see odp_term_local() which must have been called prior to this.
+ * @see odp_init_local(), odp_term_global()
*/
-int odp_term_global(odp_instance_t instance);
+int odp_term_local(void);
/**
- * Thread local ODP initialization
+ * Global ODP termination
*
- * All threads must call this function before calling any other ODP API
- * functions. The instance parameter specifies which ODP instance the thread
- * joins. A thread may be simultaneously part of single ODP instance only.
+ * This function is the final ODP call made when terminating an ODP application
+ * in a controlled way. It cannot handle exceptional circumstances.
+ *
+ * This function must be called only after all threads of the instance have
+ * executed odp_term_local().
*
* @param instance Instance handle
- * @param thr_type Thread type
*
* @retval 0 on success
* @retval <0 on failure
*
- * @see odp_term_local()
- * @see odp_init_global() which must have been called prior to this.
+ * @see odp_init_global(), odp_term_local()
*/
-int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type);
+int odp_term_global(odp_instance_t instance);
/**
- * Thread local ODP termination
+ * Abnormal ODP termination after a non-recoverable error
+ *
+ * Application may call this function to terminate an ODP instance after facing
+ * a non-recoverable error. Depending on the implementation, this function may
+ * attempt to dump stack and other memory areas, clean up and stop HW
+ * operations and/or perform other actions helpful in postmortem analysis.
+ * Depending on the nature of the error resulting in the abnormal termination,
+ * these actions may partially or completely fail. Flags (ODP_TERM_*) parameter
+ * can be used to control and data parameter can be used to pass additional
+ * flag-specific information to the termination process. Implementation
+ * specific flags with implementation specific data may also exist, see from
+ * implementation documentation how those should be utilized.
+ *
+ * Some coordination across threads is required when abnormally terminating, if
+ * other threads continue calling ODP functions during or after termination,
+ * their operation is most likely affected.
+ *
+ * When the function returns, the ODP instance has been destroyed either
+ * completely or partially. Application must not attempt to call any ODP
+ * functions during its remaining lifetime, but terminate as soon as feasible.
*
- * This function is the second to final ODP call made when terminating
- * an ODP application in a controlled way. It cannot handle exceptional
- * circumstances. In general it calls the API modules per thread terminate
- * functions in the reverse order to that which the module init functions were
- * called during odp_init_local().
+ * @param instance Instance handle
+ * @param flags A bit mask of control flags (ODP_TERM_*), set to 0
+ * when no flags
+ * @param data Additional data, set to NULL when no additional data
*
- * @retval 1 on success and more ODP threads exist
- * @retval 0 on success and this is the last ODP thread
- * @retval <0 on failure
+ * @retval 0 on all actions successfully performed
+ * @retval <0 on failure to perform all actions, implementation specific status
+ * code for debugging
+ */
+int odp_term_abnormal(odp_instance_t instance, uint64_t flags, void *data);
+
+/**
+ * Set thread specific log function
*
- * @warning The unwinding of HW resources to allow them to be reused without
- * reseting the device is a complex task that the application is expected
- * to coordinate.
+ * By default, all ODP log writes use the global log function, which may be set
+ * as part of odp_init_t. Using this operation, an alternative ODP log function
+ * may be set for the calling thread. When set, ODP uses the thread specific log
+ * function for all log writes originating from ODP API calls made by the
+ * calling thread. Setting the log function to NULL causes the calling thread to
+ * use the global log function.
*
- * @see odp_init_local()
- * @see odp_term_global() should be called by the last ODP thread before exit
- * of an application.
+ * @param func Log function
*/
-int odp_term_local(void);
+void odp_log_thread_fn_set(odp_log_func_t func);
+
+/**
+ * Get instance handle
+ *
+ * A successful call outputs the calling thread's ODP instance handle.
+ *
+ * @param[out] instance Instance handle pointer for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ *
+ * @see odp_init_global(), odp_init_local()
+ */
+int odp_instance(odp_instance_t *instance);
/**
* @}
diff --git a/include/odp/api/spec/ipsec.h b/include/odp/api/spec/ipsec.h
new file mode 100644
index 000000000..cdb18116b
--- /dev/null
+++ b/include/odp/api/spec/ipsec.h
@@ -0,0 +1,644 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP IPsec API
+ */
+
+#ifndef ODP_API_SPEC_IPSEC_H_
+#define ODP_API_SPEC_IPSEC_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/crypto_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/ipsec_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/std_types.h>
+
+/** @addtogroup odp_ipsec
+ * IPSEC protocol offload.
+ * @{
+ */
+
+/**
+ * Query IPSEC capabilities
+ *
+ * Outputs IPSEC capabilities on success.
+ *
+ * @param[out] capa Pointer to capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ipsec_capability(odp_ipsec_capability_t *capa);
+
+/**
+ * Query supported IPSEC cipher algorithm capabilities
+ *
+ * Outputs all supported configuration options for the algorithm. Output is
+ * sorted (from the smallest to the largest) first by key length, then by IV
+ * length. Use this information to select key lengths, etc cipher algorithm
+ * options for SA creation (odp_ipsec_crypto_param_t).
+ *
+ * @param cipher Cipher algorithm
+ * @param[out] capa Array of capability structures for output
+ * @param num Maximum number of capability structures to output
+ *
+ * @return Number of capability structures for the algorithm. If this is larger
+ * than 'num', only 'num' first structures were output and application
+ * may call the function again with a larger value of 'num'.
+ * @retval <0 on failure
+ */
+int odp_ipsec_cipher_capability(odp_cipher_alg_t cipher,
+ odp_ipsec_cipher_capability_t capa[], int num);
+
+/**
+ * Query supported IPSEC authentication algorithm capabilities
+ *
+ * Outputs all supported configuration options for the algorithm. Output is
+ * sorted (from the smallest to the largest) first by ICV length, then by key
+ * length. Use this information to select key lengths, etc authentication
+ * algorithm options for SA creation (odp_ipsec_crypto_param_t).
+ *
+ * @param auth Authentication algorithm
+ * @param[out] capa Array of capability structures for output
+ * @param num Maximum number of capability structures to output
+ *
+ * @return Number of capability structures for the algorithm. If this is larger
+ * than 'num', only 'num' first structures were output and application
+ * may call the function again with a larger value of 'num'.
+ * @retval <0 on failure
+ */
+int odp_ipsec_auth_capability(odp_auth_alg_t auth,
+ odp_ipsec_auth_capability_t capa[], int num);
+
+/**
+ * Initialize IPSEC configuration options
+ *
+ * Initialize an odp_ipsec_config_t to its default values.
+ *
+ * @param[out] config Pointer to IPSEC configuration structure
+ */
+void odp_ipsec_config_init(odp_ipsec_config_t *config);
+
+/**
+ * Global IPSEC configuration
+ *
+ * Initialize and configure IPSEC offload with global configuration options.
+ * This must be called before any SAs are created. Use odp_ipsec_capability()
+ * to examine which features and modes are supported. This function must be
+ * called before creating the first SA with odp_ipsec_sa_create(). Calling this
+ * function multiple times results in undefined behaviour.
+ *
+ * @param config Pointer to IPSEC configuration structure
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ *
+ * @see odp_ipsec_capability(), odp_ipsec_config_init()
+ */
+int odp_ipsec_config(const odp_ipsec_config_t *config);
+
+/**
+ * Initialize IPSEC SA parameters
+ *
+ * Initialize an odp_ipsec_sa_param_t to its default values for all fields.
+ *
+ * @param param Pointer to the parameter structure
+ */
+void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param);
+
+/**
+ * Create IPSEC SA
+ *
+ * Create a new IPSEC SA according to the parameters.
+ *
+ * The parameter structure as well as all key, address and other memory
+ * buffers pointed to by it can be freed after the call.
+ *
+ * @param param IPSEC SA parameters
+ *
+ * @return IPSEC SA handle
+ * @retval ODP_IPSEC_SA_INVALID on failure
+ *
+ * @see odp_ipsec_sa_param_init()
+ */
+odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param);
+
+/**
+ * Disable IPSEC SA
+ *
+ * Application must use this call to disable a SA before destroying it. The call
+ * marks the SA disabled, so that IPSEC implementation stops using it. For
+ * example, inbound SPI lookups will not match any more. Application must
+ * stop providing the SA as parameter to new IPSEC input/output operations
+ * before calling disable. Packets in progress during the call may still match
+ * the SA and be processed successfully.
+ *
+ * When in synchronous operation mode, the call will return when it's possible
+ * to destroy the SA. In asynchronous mode, the same is indicated by an
+ * ODP_EVENT_IPSEC_STATUS event sent to the queue specified for the SA. The
+ * status event is guaranteed to be the last event for the SA, i.e. all
+ * in-progress operations have completed and resulting events (including status
+ * events) have been enqueued before it.
+ *
+ * @param sa IPSEC SA to be disabled
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_sa_destroy()
+ */
+int odp_ipsec_sa_disable(odp_ipsec_sa_t sa);
+
+/**
+ * Destroy IPSEC SA
+ *
+ * Destroy an unused IPSEC SA. Result is undefined if the SA is being used
+ * (i.e. asynchronous operation is in progress).
+ *
+ * @param sa IPSEC SA to be destroyed
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_sa_create()
+ */
+int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa);
+
+/**
+ * Printable format of odp_ipsec_sa_t
+ *
+ * @param sa IPSEC SA handle
+ *
+ * @return uint64_t value that can be used to print/display this handle
+ */
+uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa);
+
+/**
+ * Inbound synchronous IPSEC operation
+ *
+ * This operation does inbound IPSEC processing in synchronous mode
+ * (ODP_IPSEC_OP_MODE_SYNC). A successful operation returns the number of
+ * packets consumed and outputs a new packet handle for each outputted packet.
+ * Outputted packets contain IPSEC result metadata (odp_ipsec_packet_result_t),
+ * which should be checked for transformation errors, etc. Outputted packets
+ * with error status have undefined content, except that in case of sa_lookup
+ * error the original input packet data is returned. The operation does not
+ * modify packets that it does not consume. It cannot consume all input
+ * packets if 'num_out' is smaller than 'num_in'.
+ *
+ * Packet context pointer and user area content are copied from input to output
+ * packets. Output packets are allocated from the same pool(s) as input packets.
+ *
+ * When 'param.num_sa' is zero, this operation performs SA look up for each
+ * packet. Otherwise, application must provide the SA(s) as part of operation
+ * input parameters (odp_ipsec_in_param_t). The operation outputs used SA(s) as
+ * part of per packet results (odp_ipsec_packet_result_t), or an error
+ * status if a SA was not found.
+ *
+ * Each input packet must have a valid value for these metadata (other metadata
+ * is ignored):
+ * - L3 offset: Offset to the first byte of the (outmost) IP header
+ * - L4 offset: When udp_encap is enabled, offset to the first byte of the
+ * encapsulating UDP header
+ *
+ * Additionally, implementation checks input IP packet length (odp_packet_len()
+ * minus odp_packet_l3_offset()) against protocol headers and reports an error
+ * (status.error.proto) if packet data length is less than protocol headers
+ * indicate.
+ *
+ * Packets are processed in the input order. Packet order is maintained from
+ * input 'pkt' array to output 'pkt' array. Packet order is not guaranteed
+ * between calling threads.
+ *
+ * Input packets must not be IP fragments.
+ *
+ * The operation does packet transformation according to IPSEC standards (see
+ * e.g. RFC 4302 and 4303). Resulting packets are well formed, reconstructed
+ * original IP packets, with IPSEC headers removed and valid header field values
+ * restored. The amount and content of packet data before the IP header is
+ * undefined. Some amount of TFC padding may follow the IP packet payload,
+ * in which case packet length is larger than protocol headers indicate.
+ * TFC dummy packets have l3_type set to ODP_PROTO_L3_TYPE_NONE in tunnel mode
+ * or l4_type set to ODP_PROTO_L4_TYPE_NO_NEXT in transport mode. Dummy
+ * packets contain implementation specific amount of (dummy) data. Furthermore,
+ * inline IPSEC processing may drop dummy packets.
+ *
+ * Each successfully transformed packet has a valid value for these metadata
+ * regardless of the inner packet parse configuration
+ * (odp_ipsec_inbound_config_t):
+ * - l3_offset: Offset to the first byte of the original IP packet. The value
+ * is implementation specific for tunnel mode TFC dummy packets.
+ * - l3_type: Specifies if the original packet is IPv4 or IPv6. For tunnel
+ * mode TFC dummy packets set to ODP_PROTO_L3_TYPE_NONE.
+ * - l4_type: Always set to ODP_PROTO_L4_TYPE_NO_NEXT for transport mode dummy
+ * packets. Otherwise, depends on parse configuration. Default
+ * value is ODP_PROTO_L4_TYPE_NONE.
+ * - pktio: For inline IPSEC processed packets, original packet input
+ * interface
+ *
+ * Other metadata for parse results and error checks depend on configuration
+ * (selected parse and error check levels).
+ *
+ * @param pkt_in Packets to be processed
+ * @param num_in Number of packets to be processed
+ * @param[out] pkt_out Packet handle array for resulting packets
+ * @param[in, out] num_out Number of resulting packets. Application sets this
+ * to 'pkt_out' array size. A successful operation sets
+ * this to the number of outputted packets
+ * (1 ... num_out).
+ * @param param Inbound operation parameters
+ *
+ * @return Number of input packets consumed (0 ... num_in)
+ * @retval <0 On failure
+ *
+ * @see odp_packet_user_ptr(), odp_packet_user_area(), odp_packet_l3_offset(),
+ * odp_packet_l4_offset()
+ */
+int odp_ipsec_in(const odp_packet_t pkt_in[], int num_in,
+ odp_packet_t pkt_out[], int *num_out,
+ const odp_ipsec_in_param_t *param);
+
+/**
+ * Outbound synchronous IPSEC operation
+ *
+ * This operation does outbound IPSEC processing in synchronous mode
+ * (ODP_IPSEC_OP_MODE_SYNC). A successful operation returns the number of
+ * packets consumed and outputs a new packet handle for each outputted packet.
+ * Outputted packets contain IPSEC result metadata (odp_ipsec_packet_result_t),
+ * which should be checked for transformation errors, etc. Outputted packets
+ * with error status have undefined content, except that in case of MTU error
+ * the original input packet data is returned. The operation does not modify
+ * packets that it does not consume. It cannot consume all input packets if
+ * 'num_out' is smaller than 'num_in'.
+ *
+ * Packet context pointer and user area content are copied from input to output
+ * packets. Output packets are allocated from the same pool(s) as input packets.
+ *
+ * When outbound IP fragmentation offload is enabled, the number of outputted
+ * packets may be greater than the number of input packets.
+ *
+ * Each input packet must have a valid value for these metadata (other metadata
+ * is ignored):
+ * - L3 offset: Offset to the first byte of the (outmost) IP header
+ * - L4 offset: Offset to the L4 header if L4 checksum offload is requested
+ *
+ * Additionally, input IP packet length (odp_packet_len() minus
+ * odp_packet_l3_offset()) must match values in protocol headers. Otherwise
+ * results are undefined.
+ *
+ * Packets are processed in the input order. Packet order is maintained from
+ * input 'pkt' array to output 'pkt' array. Packet order is not guaranteed
+ * between calling threads.
+ *
+ * The operation does packet transformation according to IPSEC standards (see
+ * e.g. RFC 4302 and 4303). Resulting packets are well formed IP packets
+ * with IPSEC, etc headers constructed according to the standards. The amount
+ * and content of packet data before the IP header is undefined. Use outbound
+ * operation parameters to specify the amount of TFC padding appended to
+ * the packet during IPSEC transformation. Options can be used also to create
+ * TFC dummy packets. Packet data content is ignored in tunnel mode TFC dummy
+ * packet creation as tfc_pad_len option defines solely the packet length.
+ * In all other cases, payload length for the IPSEC transformation is specified
+ * by odp_packet_len() minus odp_packet_l3_offset() plus tfc_pad_len option.
+ *
+ * Each successfully transformed packet has a valid value for these metadata:
+ * - L3 offset: Offset to the first byte of the (outmost) IP header
+ *
+ * @param pkt_in Packets to be processed
+ * @param num_in Number of packets to be processed
+ * @param[out] pkt_out Packet handle array for resulting packets
+ * @param[in, out] num_out Number of resulting packets. Application sets this
+ * to 'pkt_out' array size. A successful operation sets
+ * this to the number of outputted packets
+ * (1 ... num_out).
+ * @param param Outbound operation parameters
+ *
+ * @return Number of input packets consumed (0 ... num_in)
+ * @retval <0 On failure
+ *
+ * @see odp_packet_user_ptr(), odp_packet_user_area(), odp_packet_l3_offset()
+ */
+int odp_ipsec_out(const odp_packet_t pkt_in[], int num_in,
+ odp_packet_t pkt_out[], int *num_out,
+ const odp_ipsec_out_param_t *param);
+
+/**
+ * Inbound asynchronous IPSEC operation
+ *
+ * This operation does inbound IPSEC processing in asynchronous mode. It
+ * processes packets otherwise identically to odp_ipsec_in(), but outputs
+ * resulting packets as ODP_EVENT_PACKET events (with ODP_EVENT_PACKET_IPSEC
+ * subtype). The following ordering considerations apply to the events.
+ *
+ * Asynchronous mode maintains packet order per SA when application calls the
+ * operation within an ordered or atomic scheduler context of the same queue.
+ * Resulting events for the same SA are enqueued in order. Packet order per SA
+ * at a destination queue is the same as if application would have enqueued
+ * packets there with odp_queue_enq_multi().
+ *
+ * Packet order is also maintained when application otherwise guarantees
+ * (e.g. using locks) that the operation is not called simultaneously from
+ * multiple threads for the same SA(s).
+ *
+ * Logically, packet processing (e.g. sequence number check) happens in the
+ * output order as defined above.
+ *
+ * The function may be used also in inline processing mode, e.g. for IPSEC
+ * packets for which inline processing is not possible. Packets for the same SA
+ * may be processed simultaneously in both modes (initiated by this function
+ * and inline operation).
+ *
+ * Post-processing may be required after the reception of an IPsec packet
+ * event to complete IPsec processing for the packet. The post-processing
+ * happens in the odp_ipsec_result() function that must be called at least
+ * once before packet data or metadata (other than packet type and subtype)
+ * may be accessed.
+ *
+ * If reassembly is attempted but fails, the result packet delivered to the
+ * application will have reassembly status as ODP_PACKET_REASS_INCOMPLETE and
+ * will not have ODP_EVENT_PACKET_IPSEC subtype. In that case, the application
+ * can call odp_packet_reass_partial_state() to get fragments of the packet. The
+ * fragments will have subtype as ODP_EVENT_PACKET_IPSEC and the application
+ * must call odp_ipsec_result() for such a fragment before accessing its packet
+ * data.
+ *
+ * @param pkt Packets to be processed
+ * @param num Number of packets to be processed
+ * @param param Inbound operation parameters
+ *
+ * @return Number of input packets consumed (0 ... num)
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_in(), odp_ipsec_result()
+ */
+int odp_ipsec_in_enq(const odp_packet_t pkt[], int num,
+ const odp_ipsec_in_param_t *param);
+
+/**
+ * Outbound asynchronous IPSEC operation
+ *
+ * This operation does outbound IPSEC processing in asynchronous mode. It
+ * processes packets otherwise identically to odp_ipsec_out(), but outputs
+ * resulting packets as ODP_EVENT_PACKET events (with ODP_EVENT_PACKET_IPSEC
+ * subtype). The following ordering considerations apply to the events.
+ *
+ * Asynchronous mode maintains packet order per SA when application calls the
+ * operation within an ordered or atomic scheduler context of the same queue.
+ * Resulting events for the same SA are enqueued in order. Packet order per SA
+ * at a destination queue is the same as if application would have enqueued
+ * packets there with odp_queue_enq_multi().
+ *
+ * Packet order is also maintained when application otherwise guarantees
+ * (e.g. using locks) that the operation is not called simultaneously from
+ * multiple threads for the same SA(s).
+ *
+ * Logically, packet processing (e.g. sequence number assignment) happens in the
+ * output order as defined above.
+ *
+ * The function may be used also in inline processing mode, e.g. for IPSEC
+ * packets for which inline processing is not possible.
+ *
+ * Post-processing may be required after the reception of an IPsec packet
+ * event to complete IPsec processing for the packet. The post-processing
+ * happens in the odp_ipsec_result() function that must be called at least
+ * once before packet data or metadata (other than packet type and subtype)
+ * may be accessed.
+ *
+ * @param pkt Packets to be processed
+ * @param num Number of packets to be processed
+ * @param param Outbound operation parameters
+ *
+ * @return Number of input packets consumed (0 ... num)
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_out(), odp_ipsec_result()
+ */
+int odp_ipsec_out_enq(const odp_packet_t pkt[], int num,
+ const odp_ipsec_out_param_t *param);
+
+/**
+ * Outbound inline IPSEC operation
+ *
+ * This operation does outbound inline IPSEC processing for the packets. It's
+ * otherwise identical to odp_ipsec_out_enq(), but outputs all successfully
+ * transformed packets to the specified output interface (or tm_queue), instead of
+ * generating events for those.
+ *
+ * Inline operation parameters are defined per packet. The array of parameters
+ * must have 'num' elements and is pointed to by 'inline_param'.
+ *
+ * @param pkt Packets to be processed
+ * @param num Number of packets to be processed
+ * @param param Outbound operation parameters
+ * @param inline_param Outbound inline operation specific parameters
+ *
+ * @return Number of packets consumed (0 ... num)
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_out_enq()
+ */
+int odp_ipsec_out_inline(const odp_packet_t pkt[], int num,
+ const odp_ipsec_out_param_t *param,
+ const odp_ipsec_out_inline_param_t *inline_param);
+
+/**
+ * Convert IPSEC processed packet event to packet handle
+ *
+ * Get packet handle to an IPSEC processed packet event. Event subtype must be
+ * ODP_EVENT_IPSEC_PACKET. IPSEC operation results can be examined with
+ * odp_ipsec_result().
+ *
+ * @param ev Event handle
+ *
+ * @return Packet handle
+ *
+ * @see odp_event_subtype(), odp_ipsec_result()
+ */
+odp_packet_t odp_ipsec_packet_from_event(odp_event_t ev);
+
+/**
+ * Convert IPSEC processed packet handle to event
+ *
+ * The packet handle must be an output of an IPSEC operation.
+ *
+ * @param pkt Packet handle from IPSEC operation
+ *
+ * @return Event handle
+ */
+odp_event_t odp_ipsec_packet_to_event(odp_packet_t pkt);
+
+/**
+ * Get IPSEC operation results from an IPSEC processed packet
+ *
+ * Successful IPSEC operations of all types (SYNC, ASYNC and INLINE) produce
+ * packets which contain IPSEC result metadata. This function copies the
+ * operation results from an IPSEC processed packet. Event subtype of this kind
+ * of packet is ODP_EVENT_PACKET_IPSEC. Results are undefined if a non-IPSEC
+ * processed packet is passed as input.
+ *
+ * Some packet API operations output a new packet handle
+ * (e.g. odp_packet_concat()). IPSEC metadata remain valid as long as the packet
+ * handle is not changed from the original (output of e.g. odp_ipsec_in() or
+ * odp_ipsec_packet_from_event() call) IPSEC processed packet handle.
+ *
+ * @param[out] result Pointer to operation result for output
+ * @param packet An IPSEC processed packet (ODP_EVENT_PACKET_IPSEC)
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_in(), odp_ipsec_in_enq(), odp_ipsec_out(),
+ * odp_ipsec_out_enq(), odp_ipsec_packet_from_event()
+ */
+int odp_ipsec_result(odp_ipsec_packet_result_t *result, odp_packet_t packet);
+
+/**
+ * Get IPSEC status information from an ODP_EVENT_IPSEC_STATUS event
+ *
+ * Copies IPSEC status information from an event. The event must be of
+ * type ODP_EVENT_IPSEC_STATUS.
+ *
+ * @param[out] status Pointer to status information structure for output.
+ * @param event An ODP_EVENT_IPSEC_STATUS event
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ *
+ * @see odp_ipsec_sa_disable()
+ */
+int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event);
+
+/**
+ * IPSEC test API for modifying internal state of an SA.
+ *
+ * This function is not meant to be used by normal applications but by special
+ * test applications that test or debug the operation of the underlying ODP
+ * implementation. Calling this function may degrade the performance of the
+ * calling thread, other threads or the IPSEC implementation in general.
+ *
+ * Calling this function for an SA at the same time when the SA is used for
+ * processing traffic or when the SA is being modified through other parts
+ * of IPSEC API may result in undefined behaviour.
+ *
+ * SA state update through this function may not be supported by all ODP
+ * implementations, ODP instances or SA instances or at every moment. This
+ * function may return failure for unspecified reasons even when the capability
+ * call indicated support for updating a particular parameter and previous
+ * similar calls succeeded.
+ *
+ * @param sa IPSEC SA to be updated
+ * @param op Specifies operation to be performed
+ * @param param Pointer to IPSEC TEST SA param structure to be
+ * used for the operation
+ *
+ * @return 0 On success
+ * @retval <0 On failure
+ */
+int odp_ipsec_test_sa_update(odp_ipsec_sa_t sa,
+ odp_ipsec_test_sa_operation_t op,
+ const odp_ipsec_test_sa_param_t *param);
+
+/**
+ * Update MTU for outbound IP fragmentation
+ *
+ * When IP fragmentation offload is enabled, the SA is created with an MTU.
+ * This call may be used to update MTU at any time. MTU updates are not
+ * expected to happen very frequently.
+ *
+ * @param sa IPSEC SA to be updated
+ * @param mtu The new MTU value
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ */
+int odp_ipsec_sa_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu);
+
+/**
+ * Get user defined SA context pointer
+ *
+ * @param sa IPSEC SA handle
+ *
+ * @return User defined SA context pointer value
+ * @retval NULL On failure
+ */
+void *odp_ipsec_sa_context(odp_ipsec_sa_t sa);
+
+/**
+ * Print global IPSEC configuration info
+ *
+ * Print implementation-defined information about the global IPSEC
+ * configuration.
+ */
+void odp_ipsec_print(void);
+
+/**
+ * Print IPSEC SA info
+ *
+ * @param sa SA handle
+ *
+ * Print implementation-defined IPSEC SA debug information to the ODP log.
+ */
+void odp_ipsec_sa_print(odp_ipsec_sa_t sa);
+
+/**
+ * Get IPSEC stats for the IPSEC SA handle
+ *
+ * @param sa IPSEC SA handle
+ * @param[out] stats Stats output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ipsec_stats(odp_ipsec_sa_t sa, odp_ipsec_stats_t *stats);
+
+/**
+ * Get IPSEC stats for multiple IPSEC SA handles
+ *
+ * @param sa Array of IPSEC SA handles
+ * @param[out] stats Stats array for output
+ * @param num Number of SA handles
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ipsec_stats_multi(odp_ipsec_sa_t sa[], odp_ipsec_stats_t stats[], int num);
+
+/**
+ * Retrieve information about an IPSEC SA
+ *
+ * The cipher and auth key data(including key extra) will not be exposed and
+ * the corresponding pointers will be set to NULL. The IP address pointers
+ * will point to the corresponding buffers available in the SA info structure.
+ *
+ * The user defined SA context pointer is an opaque field and hence the value
+ * provided during the SA creation will be returned.
+ *
+ * @param sa The IPSEC SA for which to retrieve information
+ * @param[out] sa_info Pointer to caller allocated SA info structure to be
+ * filled in
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ **/
+int odp_ipsec_sa_info(odp_ipsec_sa_t sa, odp_ipsec_sa_info_t *sa_info);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/ipsec_types.h b/include/odp/api/spec/ipsec_types.h
new file mode 100644
index 000000000..71b53a770
--- /dev/null
+++ b/include/odp/api/spec/ipsec_types.h
@@ -0,0 +1,1495 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP IPsec API type definitions
+ */
+
+#ifndef ODP_API_SPEC_IPSEC_TYPES_H_
+#define ODP_API_SPEC_IPSEC_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/classification.h>
+#include <odp/api/crypto_types.h>
+#include <odp/api/packet_io_types.h>
+#include <odp/api/protocols.h>
+#include <odp/api/std_types.h>
+#include <odp/api/traffic_mngr.h>
+
+/** @defgroup odp_ipsec ODP IPSEC
+ * @{
+ */
+
+/**
+ * @typedef odp_ipsec_sa_t
+ * IPSEC Security Association (SA)
+ */
+
+ /**
+ * @def ODP_IPSEC_SA_INVALID
+ * Invalid IPSEC SA
+ */
+
+/**
+ * IPSEC operation mode
+ */
+typedef enum odp_ipsec_op_mode_t {
+ /** Synchronous IPSEC operation
+ *
+ * Application uses synchronous IPSEC operations,
+ * which output all results on function return.
+ */
+ ODP_IPSEC_OP_MODE_SYNC = 0,
+
+ /** Asynchronous IPSEC operation
+ *
+ * Application uses asynchronous IPSEC operations,
+ * which return results via events.
+ */
+ ODP_IPSEC_OP_MODE_ASYNC,
+
+ /** Inline IPSEC operation
+ *
+ * Packet input/output is connected directly to IPSEC inbound/outbound
+ * processing. Application uses asynchronous or inline IPSEC
+ * operations.
+ *
+ * Inline processed inbound packets are delivered to the application
+ * in the same way as packets processed by odp_ipsec_in_enq().
+ */
+ ODP_IPSEC_OP_MODE_INLINE,
+
+ /** IPSEC is disabled in inbound / outbound direction */
+ ODP_IPSEC_OP_MODE_DISABLED
+
+} odp_ipsec_op_mode_t;
+
+/**
+ * IPSEC TEST SA operation
+ */
+typedef enum odp_ipsec_test_sa_operation_t {
+ /** Update next sequence number
+ *
+ * The seq_num parameter is an outbound SA specific parameter.
+ * Invoking the odp_ipsec_test_sa_update() API to update this
+ * field on an inbound SA will cause the API to return failure.
+ */
+ ODP_IPSEC_TEST_SA_UPDATE_SEQ_NUM = 0,
+
+ /** Update highest authenticated sequence number
+ *
+ * The antireplay_window_top parameter is inbound SA specific.
+ * Invoking the odp_ipsec_test_sa_update() API to update this
+ * field on an outbound SA will cause the API to return failure.
+ */
+ ODP_IPSEC_TEST_SA_UPDATE_ANTIREPLAY_WINDOW_TOP
+
+} odp_ipsec_test_sa_operation_t;
+
+/**
+ * IPSEC TEST SA parameter
+ */
+typedef union odp_ipsec_test_sa_param_t {
+ /** Next sequence number
+ *
+ * @see ODP_IPSEC_TEST_SA_UPDATE_SEQ_NUM
+ */
+ uint64_t seq_num;
+
+ /** Highest authenticated sequence number
+ *
+ * @see ODP_IPSEC_TEST_SA_UPDATE_ANTIREPLAY_WINDOW_TOP
+ */
+ uint64_t antireplay_window_top;
+
+} odp_ipsec_test_sa_param_t;
+
+/**
+ * Configuration options for IPSEC inbound processing
+ */
+typedef struct odp_ipsec_inbound_config_t {
+ /** Default destination queue for IPSEC events
+ *
+ * When inbound SA lookup fails in the asynchronous mode,
+ * resulting IPSEC events are enqueued into this queue.
+ */
+ odp_queue_t default_queue;
+
+ /** Constraints for SPI values used with inbound SA lookup. Minimal
+ * SPI range and unique values may improve performance. */
+ struct {
+ /** Minimum SPI value for SA lookup. Default value is 0. */
+ uint32_t min_spi;
+
+ /** Maximum SPI value for SA lookup. Default value is
+ * UINT32_MAX. */
+ uint32_t max_spi;
+
+ /** Select if SPI values for SA lookup are unique or may contain
+ * the same SPI value multiple times. The default value is 0.
+ *
+ * 0: All SAs in SA lookup have unique SPI value
+ * 1: The same SPI value may be used for multiple SAs
+ */
+ odp_bool_t spi_overlap;
+
+ } lookup;
+
+ /** Retain outer headers
+ *
+ * Select up to which protocol layer (at least) outer headers are
+ * retained in inbound inline processing. Default value is
+ * ODP_PROTO_LAYER_NONE.
+ *
+ * ODP_PROTO_LAYER_NONE: Application does not require any outer
+ * headers to be retained.
+ *
+ * ODP_PROTO_LAYER_L2: Retain headers up to layer 2.
+ *
+ * ODP_PROTO_LAYER_L3: Retain headers up to layer 3, otherwise the
+ * same as ODP_PROTO_LAYER_ALL.
+ *
+ * ODP_PROTO_LAYER_L4: Retain headers up to layer 4, otherwise the
+ * same as ODP_PROTO_LAYER_ALL.
+ *
+ * ODP_PROTO_LAYER_ALL: In tunnel mode, all headers before IPSEC are
+ * retained. In transport mode, all headers
+ * before IP (carrying IPSEC) are retained.
+ *
+ */
+ odp_proto_layer_t retain_outer;
+
+ /** Parse packet headers after IPSEC transformation
+ *
+ * Select header parsing level after inbound processing. Headers of the
+ * resulting packet must be checked (at least) up to this level.
+ * Parsing starts from IP (layer 3). Packet metadata from IP to this
+ * layer is set. In addition, offset (and pointer) to the next layer
+ * is set. Other layer/protocol specific metadata have undefined
+ * values.
+ *
+ * Each successfully transformed packet has a valid value for L3 offset
+ * regardless of the parse configuration. Default value is
+ * ODP_PROTO_LAYER_NONE. ODP_PROTO_LAYER_L2 is not a valid value.
+ */
+ odp_proto_layer_t parse_level;
+
+ /** Flags to control IPSEC payload data checks up to the selected parse
+ * level. Checksum checking status can be queried for each packet with
+ * odp_packet_l3_chksum_status() and odp_packet_l4_chksum_status().
+ * Default value for all bits is 0 (skip all checksum checks).
+ */
+ odp_proto_chksums_t chksums;
+
+ /** Post-IPsec reassembly configuration
+ *
+ * This field provides global IPsec configuration parameters for
+ * fragment reassembly. The enable flag does not turn on reassembly
+ * but tells if reassembly may be enabled in SA parameters.
+ *
+ * The enable flag may be set only if retain_outer is
+ * ODP_PROTO_LAYER_NONE.
+ */
+ odp_reass_config_t reassembly;
+
+ /** Attempt reassembly after inbound IPsec processing in
+ * odp_ipsec_in_enq(). Default value is false.
+ */
+ odp_bool_t reass_async;
+
+ /** Attempt reassembly after inline inbound IPsec processing.
+ * Default value is false.
+ **/
+ odp_bool_t reass_inline;
+
+} odp_ipsec_inbound_config_t;
+
+/**
+ * Configuration options for IPSEC outbound processing
+ */
+typedef struct odp_ipsec_outbound_config_t {
+ /** Flags to control L3/L4 checksum insertion as part of outbound
+ * packet processing. These flags control checksum insertion (for the
+ * payload packet) in the same way as the checksum flags in
+ * odp_pktout_config_opt_t control checksum insertion when sending
+ * packets out through a pktio interface. Also packet checksum override
+ * functions (e.g. odp_packet_l4_chksum_insert()) can be used in
+ * the same way.
+ */
+ union {
+ /** Mapping for individual bits */
+ struct {
+ /** Insert IPv4 header checksum on the payload packet
+ * before IPSEC transformation. Default value is 0. */
+ uint32_t inner_ipv4 : 1;
+
+ /** Insert UDP header checksum on the payload packet
+ * before IPSEC transformation. Default value is 0. */
+ uint32_t inner_udp : 1;
+
+ /** Insert TCP header checksum on the payload packet
+ * before IPSEC transformation. Default value is 0. */
+ uint32_t inner_tcp : 1;
+
+ /** Insert SCTP header checksum on the payload packet
+ * before IPSEC transformation. Default value is 0. */
+ uint32_t inner_sctp : 1;
+
+ } chksum;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint32_t all_chksum;
+ };
+
+} odp_ipsec_outbound_config_t;
+
+/**
+ * IPSEC TEST capability
+ */
+typedef struct odp_ipsec_test_capability_t {
+ /** Parameters supported for sa_update */
+ struct {
+ /** Next sequence number value
+ *
+ * @see ODP_IPSEC_TEST_SA_UPDATE_SEQ_NUM
+ */
+ odp_bool_t seq_num;
+
+ /** Highest authenticated sequence number
+ *
+ * @see ODP_IPSEC_TEST_SA_UPDATE_ANTIREPLAY_WINDOW_TOP
+ */
+ odp_bool_t antireplay_window_top;
+
+ } sa_operations;
+
+} odp_ipsec_test_capability_t;
+
+/**
+ * IPSEC capability
+ */
+typedef struct odp_ipsec_capability_t {
+ /** Maximum number of IPSEC SAs */
+ uint32_t max_num_sa;
+
+ /** Synchronous IPSEC operation mode (ODP_IPSEC_OP_MODE_SYNC) support */
+ odp_support_t op_mode_sync;
+
+ /**
+ * Asynchronous IPSEC operation mode (ODP_IPSEC_OP_MODE_ASYNC) support
+ */
+ odp_support_t op_mode_async;
+
+ /**
+ * Inline inbound IPSEC operation mode (ODP_IPSEC_OP_MODE_INLINE)
+ * support
+ */
+ odp_support_t op_mode_inline_in;
+
+ /**
+ * Inline outgoing IPSEC operation mode (ODP_IPSEC_OP_MODE_INLINE)
+ * support
+ */
+ odp_support_t op_mode_inline_out;
+
+ /** IP Authenticated Header (ODP_IPSEC_AH) support */
+ odp_support_t proto_ah;
+
+ /** Fragment after IPsec support */
+ odp_support_t frag_after;
+
+ /** Fragment before IPsec support */
+ odp_support_t frag_before;
+
+ /**
+ * Support of pipelined classification (ODP_IPSEC_PIPELINE_CLS) of
+ * resulting inbound packets
+ */
+ odp_support_t pipeline_cls;
+
+ /**
+ * Support of retaining outer headers (retain_outer) in inbound inline
+ * processed packets
+ */
+ odp_support_t retain_header;
+
+ /**
+ * Inner packet checksum check offload support in inbound direction.
+ */
+ odp_proto_chksums_t chksums_in;
+
+ /** Maximum number of different destination CoSes in classification
+ * pipelining. The same CoS may be used for many SAs. This is equal or
+ * less than 'max_cos' capability in classifier API.
+ */
+ uint32_t max_cls_cos;
+
+ /**
+ * Scheduled queue support
+ *
+ * 0: Scheduled queues are not supported either as IPsec SA destination
+ * queues or as IPsec default queue
+ * 1: Scheduled queues are supported as both IPsec SA destination queues
+ * and IPsec default queue
+ * @see odp_ipsec_sa_param_t
+ */
+ odp_bool_t queue_type_sched;
+
+ /**
+ * Plain queue support
+ *
+ * 0: Plain queues are not supported either as IPsec SA destination
+ * queues or as IPsec default queue
+ * 1: Plain queues are supported as both IPsec SA destination queues and
+ * IPsec default queue
+ * @see odp_ipsec_sa_param_t
+ */
+ odp_bool_t queue_type_plain;
+
+ /** Maximum number of different destination queues. The same queue may
+ * be used for many SAs. */
+ uint32_t max_queues;
+
+ /** Support for returning completion packets as vectors */
+ odp_pktin_vector_capability_t vector;
+
+ /** Maximum anti-replay window size. */
+ uint32_t max_antireplay_ws;
+
+ /** Supported cipher algorithms */
+ odp_crypto_cipher_algos_t ciphers;
+
+ /** Supported authentication algorithms */
+ odp_crypto_auth_algos_t auths;
+
+ /** Support of traffic manager (TM) after inline outbound IPSEC
+ * processing. On unsupported platforms, application is not allowed
+ * to use a TM enabled pktio (ODP_PKTOUT_MODE_TM) with outbound
+ * inline IPSEC.
+ *
+ * @see odp_pktio_open(), odp_pktio_param_t
+ */
+ odp_support_t inline_ipsec_tm;
+
+ /** IPSEC TEST capabilities
+ *
+ * @see odp_ipsec_test_sa_update()
+ */
+ odp_ipsec_test_capability_t test;
+
+ /** Post-IPsec reassembly capability */
+ odp_reass_capability_t reassembly;
+
+ /** Support of reassembly after inbound processing in odp_ipsec_in_enq() */
+ odp_bool_t reass_async;
+
+ /** Support of reassembly after inline inbound IPsec processing */
+ odp_bool_t reass_inline;
+
+} odp_ipsec_capability_t;
+
+/**
+ * Cipher algorithm capabilities
+ */
+typedef struct odp_ipsec_cipher_capability_t {
+ /** Key length in bytes */
+ uint32_t key_len;
+
+} odp_ipsec_cipher_capability_t;
+
+/**
+ * Authentication algorithm capabilities
+ */
+typedef struct odp_ipsec_auth_capability_t {
+ /** Key length in bytes */
+ uint32_t key_len;
+
+ /** ICV length in bytes */
+ uint32_t icv_len;
+} odp_ipsec_auth_capability_t;
+
+/**
+ * IPSEC configuration options
+ */
+typedef struct odp_ipsec_config_t {
+ /** Inbound IPSEC operation mode. Application selects which mode
+ * will be used for inbound IPSEC operations.
+ *
+ * @see odp_ipsec_in(), odp_ipsec_in_enq()
+ */
+ odp_ipsec_op_mode_t inbound_mode;
+
+ /** Outbound IPSEC operation mode. Application selects which mode
+ * will be used for outbound IPSEC operations.
+ *
+ * @see odp_ipsec_out(), odp_ipsec_out_enq(), odp_ipsec_out_inline()
+ */
+ odp_ipsec_op_mode_t outbound_mode;
+
+ /** Maximum number of IPSEC SAs that application will use
+ * simultaneously */
+ uint32_t max_num_sa;
+
+ /** IPSEC inbound processing configuration */
+ odp_ipsec_inbound_config_t inbound;
+
+ /** IPSEC outbound processing configuration */
+ odp_ipsec_outbound_config_t outbound;
+
+ /** Enable stats collection
+ *
+ * Default value is false (stats collection disabled).
+ *
+ * @see odp_ipsec_stats(), odp_ipsec_stats_multi()
+ */
+ odp_bool_t stats_en;
+
+ /**
+ * Packet vector configuration for async and inline operations
+ *
+ * This packet vector configuration affects packets delivered to
+ * the application through the default queue and the SA destination
+ * queues. It does not affect packets delivered through pktio
+ * input queues.
+ */
+ odp_pktin_vector_config_t vector;
+
+} odp_ipsec_config_t;
+
+/**
+ * IPSEC SA direction
+ */
+typedef enum odp_ipsec_dir_t {
+ /** Inbound IPSEC SA */
+ ODP_IPSEC_DIR_INBOUND = 0,
+
+ /** Outbound IPSEC SA */
+ ODP_IPSEC_DIR_OUTBOUND
+
+} odp_ipsec_dir_t;
+
+/**
+ * IPSEC protocol mode
+ */
+typedef enum odp_ipsec_mode_t {
+ /** IPSEC tunnel mode */
+ ODP_IPSEC_MODE_TUNNEL = 0,
+
+ /** IPSEC transport mode */
+ ODP_IPSEC_MODE_TRANSPORT
+
+} odp_ipsec_mode_t;
+
+/**
+ * IPSEC protocol
+ */
+typedef enum odp_ipsec_protocol_t {
+ /** ESP protocol */
+ ODP_IPSEC_ESP = 0,
+
+ /** AH protocol */
+ ODP_IPSEC_AH
+
+} odp_ipsec_protocol_t;
+
+/**
+ * IPSEC tunnel type
+ */
+typedef enum odp_ipsec_tunnel_type_t {
+ /** Outer header is IPv4 */
+ ODP_IPSEC_TUNNEL_IPV4 = 0,
+
+ /** Outer header is IPv6 */
+ ODP_IPSEC_TUNNEL_IPV6
+
+} odp_ipsec_tunnel_type_t;
+
+/**
+ * IPSEC crypto parameters
+ */
+typedef struct odp_ipsec_crypto_param_t {
+ /** Cipher algorithm
+ *
+ * Select cipher algorithm to be used. ODP_CIPHER_ALG_NULL indicates
+ * that ciphering is disabled. See 'ciphers' field of
+ * odp_ipsec_capability_t for supported cipher algorithms. Algorithm
+ * descriptions can be found from odp_cipher_alg_t documentation. Note
+ * that some algorithms restrict choice of the pairing authentication
+ * algorithm. When ciphering is enabled, cipher key and potential extra
+ * key material (cipher_key_extra) need to be set. The default value
+ * is ODP_CIPHER_ALG_NULL.
+ */
+ odp_cipher_alg_t cipher_alg;
+
+ /** Cipher key */
+ odp_crypto_key_t cipher_key;
+
+ /** Extra keying material for cipher algorithm
+ *
+ * Additional data used as salt or nonce if the algorithm requires it,
+ * other algorithms ignore this field. These algorithms require this
+ * field to be set:
+ * - ODP_CIPHER_ALG_AES_CTR: 4 bytes of nonce
+ * - ODP_CIPHER_ALG_AES_GCM: 4 bytes of salt
+ * - ODP_CIPHER_ALG_AES_CCM: 3 bytes of salt
+ * - ODP_CIPHER_ALG_CHACHA20_POLY1305: 4 bytes of salt
+ */
+ odp_crypto_key_t cipher_key_extra;
+
+ /** Authentication algorithm
+ *
+ * Select authentication algorithm to be used. ODP_AUTH_ALG_NULL
+ * indicates that authentication is disabled. See 'auths' field of
+ * odp_ipsec_capability_t for supported authentication algorithms.
+ * Algorithm descriptions can be found from odp_auth_alg_t
+ * documentation. Note that some algorithms restrict choice of the
+ * pairing cipher algorithm. When single algorithm provides both
+ * ciphering and authentication (i.e. Authenticated Encryption),
+ * authentication side key information ('auth_key' and
+ * 'auth_key_extra') is ignored, and cipher side values are
+ * used instead. These algorithms ignore authentication side key
+ * information: ODP_AUTH_ALG_AES_GCM, ODP_AUTH_ALG_AES_CCM and
+ * ODP_AUTH_ALG_CHACHA20_POLY1305. Otherwise, authentication side
+ * parameters must be set when authentication is enabled. The default
+ * value is ODP_AUTH_ALG_NULL.
+ */
+ odp_auth_alg_t auth_alg;
+
+ /** Authentication key */
+ odp_crypto_key_t auth_key;
+
+ /** Extra keying material for authentication algorithm
+ *
+ * Additional data used as salt or nonce if the algorithm requires it,
+ * other algorithms ignore this field. These algorithms require this
+ * field to be set:
+ * - ODP_AUTH_ALG_AES_GMAC: 4 bytes of salt
+ */
+ odp_crypto_key_t auth_key_extra;
+
+ /**
+ * Length of integrity check value (ICV) in bytes.
+ *
+ * Some algorithms support multiple ICV lengths when used with IPsec.
+ * This field can be used to select a non-default ICV length.
+ *
+ * Zero value indicates that the default ICV length shall be used.
+ * The default length depends on the selected algorithm as follows:
+ *
+ * Algorithm Default length Other lengths
+ * ----------------------------------------------------------------
+ * ODP_AUTH_ALG_NULL 0
+ * ODP_AUTH_ALG_MD5_HMAC 12
+ * ODP_AUTH_ALG_SHA1_HMAC 12
+ * ODP_AUTH_ALG_SHA256_HMAC 16
+ * ODP_AUTH_ALG_SHA384_HMAC 24
+ * ODP_AUTH_ALG_SHA512_HMAC 32
+ * ODP_AUTH_ALG_AES_GCM 16 8, 12
+ * ODP_AUTH_ALG_AES_GMAC 16
+ * ODP_AUTH_ALG_AES_CCM 16 8, 12
+ * ODP_AUTH_ALG_AES_CMAC 12
+ * ODP_AUTH_ALG_AES_XCBC_MAC 12
+ * ODP_AUTH_ALG_CHACHA20_POLY1305 16
+ *
+ * The requested ICV length must be supported for the selected
+ * algorithm as indicated by odp_ipsec_auth_capability().
+ *
+ * The default value is 0.
+ */
+ uint32_t icv_len;
+
+} odp_ipsec_crypto_param_t;
+
+/** IPv4 header parameters */
+typedef struct odp_ipsec_ipv4_param_t {
+ /** IPv4 source address (NETWORK ENDIAN) */
+ void *src_addr;
+
+ /** IPv4 destination address (NETWORK ENDIAN) */
+ void *dst_addr;
+
+ /** IPv4 Differentiated Services Code Point. The default value is 0. */
+ uint8_t dscp;
+
+ /** IPv4 Don't Fragment bit. The default value is 0. */
+ uint8_t df;
+
+ /** IPv4 Time To Live. The default value is 255. */
+ uint8_t ttl;
+
+} odp_ipsec_ipv4_param_t;
+
+/** IPv6 header parameters */
+typedef struct odp_ipsec_ipv6_param_t {
+ /** IPv6 source address (NETWORK ENDIAN) */
+ void *src_addr;
+
+ /** IPv6 destination address (NETWORK ENDIAN) */
+ void *dst_addr;
+
+ /** IPv6 flow label. The default value is 0. */
+ uint32_t flabel;
+
+ /** IPv6 Differentiated Services Code Point. The default value is 0. */
+ uint8_t dscp;
+
+ /** IPv6 hop limit. The default value is 255. */
+ uint8_t hlimit;
+
+} odp_ipsec_ipv6_param_t;
+
+/**
+ * IPSEC tunnel parameters
+ *
+ * These parameters are used to build outbound tunnel headers. All values are
+ * passed in CPU native byte / bit order if not specified otherwise.
+ * IP addresses must be in NETWORK byte order as those are passed in with
+ * pointers and copied byte-by-byte from memory to the packet.
+ */
+typedef struct odp_ipsec_tunnel_param_t {
+ /** Tunnel type: IPv4 or IPv6. The default is IPv4. */
+ odp_ipsec_tunnel_type_t type;
+
+ /** Tunnel type specific parameters */
+ struct {
+ /** IPv4 header parameters */
+ odp_ipsec_ipv4_param_t ipv4;
+
+ /** IPv6 header parameters */
+ odp_ipsec_ipv6_param_t ipv6;
+ };
+} odp_ipsec_tunnel_param_t;
+
+/**
+ * IPSEC SA option flags
+ */
+typedef struct odp_ipsec_sa_opt_t {
+ /** Extended Sequence Numbers (ESN)
+ *
+ * * 1: Use extended (64 bit) sequence numbers
+ * * 0: Use normal sequence numbers (the default value)
+ */
+ uint32_t esn : 1;
+
+ /** UDP encapsulation
+ *
+ * * 1: Do UDP encapsulation/decapsulation so that IPSEC packets can
+ * traverse through NAT boxes.
+ * * 0: No UDP encapsulation (the default value)
+ */
+ uint32_t udp_encap : 1;
+
+ /** Copy DSCP bits
+ *
+ * * 1: Copy IPv4 or IPv6 DSCP bits from inner IP header to
+ * the outer IP header in encapsulation, and vice versa in
+ * decapsulation.
+ * * 0: Use values from odp_ipsec_tunnel_param_t in encapsulation and
+ * do not change DSCP field in decapsulation (the default value).
+ */
+ uint32_t copy_dscp : 1;
+
+ /** Copy IPv6 Flow Label
+ *
+ * * 1: Copy IPv6 flow label from inner IPv6 header to the
+ * outer IPv6 header.
+ * * 0: Use value from odp_ipsec_tunnel_param_t (the default value)
+ */
+ uint32_t copy_flabel : 1;
+
+ /** Copy IPv4 Don't Fragment bit
+ *
+ * * 1: Copy the DF bit from the inner IPv4 header to the outer
+ * IPv4 header.
+ * * 0: Use value from odp_ipsec_tunnel_param_t (the default value)
+ */
+ uint32_t copy_df : 1;
+
+ /** Decrement inner packet Time To Live (TTL) field
+ *
+ * * 1: In tunnel mode, decrement inner packet IPv4 TTL or
+ * IPv6 Hop Limit after tunnel decapsulation, or before tunnel
+ * encapsulation.
+ * * 0: Inner packet is not modified (the default value)
+ */
+ uint32_t dec_ttl : 1;
+
+} odp_ipsec_sa_opt_t;
+
+/**
+ * IPSEC SA lifetime limits
+ *
+ * These limits are used for setting up SA lifetime. IPSEC operations check
+ * against the limits and output a status code (e.g. soft_exp_bytes) when
+ * a limit is crossed. It's implementation defined how many times soft
+ * lifetime expiration is reported: only once, first N or all packets following
+ * the limit crossing. Any number of limits may be used simultaneously.
+ * Use zero when there is no limit.
+ *
+ * The default value is zero (i.e. no limit) for all the limits.
+ */
+typedef struct odp_ipsec_lifetime_t {
+ /** Soft expiry limits for the session */
+ struct {
+ /** Limit in bytes */
+ uint64_t bytes;
+
+ /** Limit in packet */
+ uint64_t packets;
+ } soft_limit;
+
+ /** Hard expiry limits for the session */
+ struct {
+ /** Limit in bytes */
+ uint64_t bytes;
+
+ /** Limit in packet */
+ uint64_t packets;
+ } hard_limit;
+} odp_ipsec_lifetime_t;
+
+/**
+ * Fragmentation mode
+ *
+ * These options control outbound IP packet fragmentation offload. When offload
+ * is enabled, IPSEC operation will determine if fragmentation is needed and
+ * does it according to the mode.
+ */
+typedef enum odp_ipsec_frag_mode_t {
+ /** Do not fragment IP packets */
+ ODP_IPSEC_FRAG_DISABLED = 0,
+
+ /** Fragment IP packet before IPSEC operation */
+ ODP_IPSEC_FRAG_BEFORE,
+
+ /** Fragment IP packet after IPSEC operation */
+ ODP_IPSEC_FRAG_AFTER,
+
+ /** Only check if IP fragmentation is needed,
+ * do not fragment packets. */
+ ODP_IPSEC_FRAG_CHECK
+} odp_ipsec_frag_mode_t;
+
+/**
+ * Packet lookup mode
+ *
+ * Lookup mode controls how an SA participates in SA lookup offload.
+ * Inbound operations perform SA lookup if application does not provide a SA as
+ * a parameter. In inline mode, a lookup miss directs the packet back to normal
+ * packet input interface processing. SA lookup failure status
+ * (status.error.sa_lookup) is reported through odp_ipsec_packet_result_t.
+ */
+typedef enum odp_ipsec_lookup_mode_t {
+ /** Inbound SA lookup is disabled for the SA. */
+ ODP_IPSEC_LOOKUP_DISABLED = 0,
+
+ /** Inbound SA lookup is enabled. Lookup matches only SPI value. */
+ ODP_IPSEC_LOOKUP_SPI,
+
+ /** Inbound SA lookup is enabled. Lookup matches both SPI value and
+ * destination IP address. Functionality is otherwise identical to
+ * ODP_IPSEC_LOOKUP_SPI. */
+ ODP_IPSEC_LOOKUP_DSTADDR_SPI
+
+} odp_ipsec_lookup_mode_t;
+
+/**
+ * IPSEC pipeline configuration
+ */
+typedef enum odp_ipsec_pipeline_t {
+ /** Do not pipeline. Send all resulting events to the application. */
+ ODP_IPSEC_PIPELINE_NONE = 0,
+
+ /** Send resulting packets to the classifier
+ *
+ * IPSEC capability 'pipeline_cls' determines if pipelined
+ * classification is supported. */
+ ODP_IPSEC_PIPELINE_CLS
+
+} odp_ipsec_pipeline_t;
+
+/**
+ * IPSEC header type
+ */
+typedef enum odp_ipsec_ip_version_t {
+ /** Header is IPv4 */
+ ODP_IPSEC_IPV4 = 4,
+
+ /** Header is IPv6 */
+ ODP_IPSEC_IPV6 = 6
+
+} odp_ipsec_ip_version_t;
+
+/**
+ * IPSEC Security Association (SA) parameters
+ */
+typedef struct odp_ipsec_sa_param_t {
+ /** IPSEC SA direction: inbound or outbound */
+ odp_ipsec_dir_t dir;
+
+ /** IPSEC protocol: ESP or AH. The default value is ODP_IPSEC_ESP. */
+ odp_ipsec_protocol_t proto;
+
+ /** IPSEC protocol mode: transport or tunnel */
+ odp_ipsec_mode_t mode;
+
+ /** Parameters for crypto and authentication algorithms */
+ odp_ipsec_crypto_param_t crypto;
+
+ /** Various SA option flags */
+ odp_ipsec_sa_opt_t opt;
+
+ /** SA lifetime parameters */
+ odp_ipsec_lifetime_t lifetime;
+
+ /** SPI value */
+ uint32_t spi;
+
+ /** Destination queue for IPSEC events
+ *
+ * Operations in asynchronous or inline mode enqueue resulting events
+ * into this queue. The default queue ('default_queue') is used when
+ * SA is not known.
+ */
+ odp_queue_t dest_queue;
+
+ /** User defined SA context pointer
+ *
+ * User defined context pointer associated with the SA.
+ * The implementation may prefetch the context data. Default value
+ * of the pointer is NULL.
+ */
+ void *context;
+
+ /** Context data length
+ *
+ * User defined context data length in bytes for prefetching.
+ * The implementation may use this value as a hint for the number of
+ * context data bytes to prefetch. Default value is zero (no hint).
+ */
+ uint32_t context_len;
+
+ /** IPSEC SA direction dependent parameters */
+ struct {
+ /** Inbound specific parameters */
+ struct {
+ /** SA lookup mode
+ * The default value is ODP_IPSEC_LOOKUP_DISABLED.
+ */
+ odp_ipsec_lookup_mode_t lookup_mode;
+
+ /** Additional SA lookup parameters. Values are
+ * considered only in ODP_IPSEC_LOOKUP_DSTADDR_SPI
+ * lookup mode. */
+ struct {
+ /** Select IP version */
+ odp_ipsec_ip_version_t ip_version;
+
+ /** IP destination address (NETWORK ENDIAN) to
+ * be matched in addition to SPI value. */
+ void *dst_addr;
+
+ } lookup_param;
+
+ /** Minimum anti-replay window size. Use 0 to disable
+ * anti-replay service. The default value is 0.
+ */
+ uint32_t antireplay_ws;
+
+ /** Select pipelined destination for resulting events
+ *
+ * Asynchronous and inline modes generate events.
+ * Select where those events are sent. Inbound SAs may
+ * choose to use pipelined classification. The default
+ * value is ODP_IPSEC_PIPELINE_NONE.
+ */
+ odp_ipsec_pipeline_t pipeline;
+
+ /** Classifier destination CoS for resulting packets
+ *
+ * Successfully decapsulated packets are sent to
+ * classification through this CoS. Other resulting
+ * events are sent to 'dest_queue'. This field is
+ * considered only when 'pipeline' is
+ * ODP_IPSEC_PIPELINE_CLS. The CoS must not be shared
+ * between any pktio interface default CoS. The maximum
+ * number of different CoS supported is defined by
+ * IPSEC capability max_cls_cos.
+ */
+ odp_cos_t dest_cos;
+
+ /** Enable reassembly of IPsec tunneled fragments
+ *
+ * Attempt reassembly of fragments after IPsec tunnel
+ * decapsulation.
+ *
+ * Reassembly is attempted for inline or asynchronously
+ * processed packets, not for packets processed using
+ * the synchronous API function.
+ *
+ * Fragments received through different SAs will not be
+ * reassembled into the same packet.
+ *
+ * IPsec statistics reflect IPsec processing before
+ * reassembly and thus count all individual fragments.
+ *
+ * Reassembly may be enabled for an SA only if
+ * reassembly was enabled in the global IPsec
+ * configuration.
+ *
+ * Default value is false.
+ *
+ * @see odp_ipsec_config()
+ *
+ */
+ odp_bool_t reassembly_en;
+
+ } inbound;
+
+ /** Outbound specific parameters */
+ struct {
+ /** Parameters for tunnel mode */
+ odp_ipsec_tunnel_param_t tunnel;
+
+ /** Fragmentation mode
+ * The default value is ODP_IPSEC_FRAG_DISABLED.
+ */
+ odp_ipsec_frag_mode_t frag_mode;
+
+ /** MTU for outbound IP fragmentation offload
+ *
+ * This is the maximum length of IP packets that
+ * outbound IPSEC operations may produce. The value may
+ * be updated later with odp_ipsec_sa_mtu_update().
+ */
+ uint32_t mtu;
+
+ } outbound;
+ };
+
+} odp_ipsec_sa_param_t;
+
+/**
+ * IPSEC stats content
+ */
+typedef struct odp_ipsec_stats_t {
+ /** Number of packets processed successfully */
+ uint64_t success;
+
+ /** Number of packets with protocol errors */
+ uint64_t proto_err;
+
+ /** Number of packets with authentication errors */
+ uint64_t auth_err;
+
+ /** Number of packets with antireplay check failures */
+ uint64_t antireplay_err;
+
+ /** Number of packets with algorithm errors */
+ uint64_t alg_err;
+
+ /** Number of packets with MTU errors */
+ uint64_t mtu_err;
+
+ /** Number of packets with hard lifetime(bytes) expired */
+ uint64_t hard_exp_bytes_err;
+
+ /** Number of packets with hard lifetime(packets) expired */
+ uint64_t hard_exp_pkts_err;
+
+ /** Total bytes of packet data processed by IPsec SA in success cases
+ *
+ * The range of packet bytes included in the success_bytes count is
+ * implementation defined but includes at least the bytes input for
+ * encryption or bytes output after decryption in ESP or the bytes
+ * authenticated in AH.
+ */
+ uint64_t success_bytes;
+} odp_ipsec_stats_t;
+
+/**
+ * IPSEC SA information
+ */
+typedef struct odp_ipsec_sa_info_t {
+ /** IPsec SA parameters
+ *
+ * This is not necessarily an exact copy of the actual parameter
+ * structure used in SA creation. The fields that were relevant
+ * for the SA in the creation phase will have the same values,
+ * but other fields, such as tunnel parameters for a transport
+ * mode SA, will have undefined values.
+ */
+ odp_ipsec_sa_param_t param;
+
+ /** IPSEC SA direction dependent parameters */
+ union {
+ /** Inbound specific parameters */
+ struct {
+ /** Additional SA lookup parameters. */
+ struct {
+ /** IP destination address (NETWORK ENDIAN) to
+ * be matched in addition to SPI value. */
+ uint8_t dst_addr[ODP_IPV6_ADDR_SIZE];
+ } lookup_param;
+
+ /** Antireplay window size
+ *
+ * Antireplay window size configured for the SA.
+ * This value can be different from what application
+ * had requested.
+ */
+ uint32_t antireplay_ws;
+
+ /** Antireplay window top
+ *
+ * Sequence number representing a recent top of the
+ * anti-replay window. There may be a delay before the
+ * SA state is reflected in the value. The value will be
+ * zero if no packets have been processed or if the
+ * anti-replay service is not enabled.
+ */
+ uint64_t antireplay_window_top;
+ } inbound;
+
+ /** Outbound specific parameters */
+ struct {
+ /** Sequence number
+ *
+ * Sequence number used for a recently processed packet.
+ * There may be a delay before the SA state is reflected
+ * in the value. When no packets have been processed,
+ * the value will be zero.
+ */
+ uint64_t seq_num;
+
+ /** Tunnel IP address */
+ union {
+ /** IPv4 */
+ struct {
+ /** IPv4 source address */
+ uint8_t src_addr[ODP_IPV4_ADDR_SIZE];
+ /** IPv4 destination address */
+ uint8_t dst_addr[ODP_IPV4_ADDR_SIZE];
+ } ipv4;
+
+ /** IPv6 */
+ struct {
+ /** IPv6 source address */
+ uint8_t src_addr[ODP_IPV6_ADDR_SIZE];
+ /** IPv6 destination address */
+ uint8_t dst_addr[ODP_IPV6_ADDR_SIZE];
+ } ipv6;
+ } tunnel;
+ } outbound;
+ };
+} odp_ipsec_sa_info_t;
+
+/** IPSEC operation status has no errors */
+#define ODP_IPSEC_OK 0
+
+/** IPSEC errors */
+typedef struct odp_ipsec_error_t {
+ /** IPSEC errors */
+ union {
+ /** Error bits */
+ struct {
+ /** Protocol error. Not a valid ESP or AH packet,
+ * packet data length error, etc. */
+ uint32_t proto : 1;
+
+ /** SA lookup failed */
+ uint32_t sa_lookup : 1;
+
+ /** Authentication failed */
+ uint32_t auth : 1;
+
+ /** Anti-replay check failed */
+ uint32_t antireplay : 1;
+
+ /** Other algorithm error */
+ uint32_t alg : 1;
+
+ /** Packet does not fit into the given MTU size */
+ uint32_t mtu : 1;
+
+ /** Hard lifetime expired: bytes */
+ uint32_t hard_exp_bytes : 1;
+
+ /** Hard lifetime expired: packets */
+ uint32_t hard_exp_packets : 1;
+ };
+
+ /** All error bits
+ *
+ * This field can be used to set, clear or compare
+ * multiple bits. For example, 'status.error.all != 0'
+ * checks if there are any errors.
+ */
+ uint32_t all;
+ };
+
+} odp_ipsec_error_t;
+
+/** IPSEC warnings */
+typedef struct odp_ipsec_warn_t {
+ /** IPSEC warnings */
+ union {
+ /** Warning bits */
+ struct {
+ /** Soft lifetime expired: bytes */
+ uint32_t soft_exp_bytes : 1;
+
+ /** Soft lifetime expired: packets */
+ uint32_t soft_exp_packets : 1;
+ };
+
+ /** All warning bits
+ *
+ * This field can be used to set/clear all bits, or to perform
+ * bitwise operations over those. */
+ uint32_t all;
+ };
+
+} odp_ipsec_warn_t;
+
+/** IPSEC operation status */
+typedef struct odp_ipsec_op_status_t {
+ /** IPSEC status bits */
+ union {
+ /** IPSEC errors and warnings */
+ struct {
+ /** IPSEC errors */
+ odp_ipsec_error_t error;
+
+ /** IPSEC warnings */
+ odp_ipsec_warn_t warn;
+ };
+
+ /** All status bits. Combines all error and warning bits.
+ * For example, 'status.all != ODP_IPSEC_OK' checks if there
+ * are any errors or warnings. */
+ uint64_t all;
+
+ };
+
+} odp_ipsec_op_status_t;
+
+/** IPSEC operation flags */
+typedef struct odp_ipsec_op_flag_t {
+ /** IPSEC operations flags */
+ union {
+ /** Operation flags */
+ struct {
+ /** Packet was processed in inline mode */
+ uint32_t inline_mode : 1;
+
+ };
+
+ /** All flag bits
+ *
+ * This field can be used to set/clear all flags, or to perform
+ * bitwise operations over those. */
+ uint32_t all;
+ };
+
+} odp_ipsec_op_flag_t;
+
+/**
+ * IPSEC outbound operation options
+ *
+ * These may be used to override some SA level options
+ */
+typedef struct odp_ipsec_out_opt_t {
+ /** Union of all flag bits */
+ union {
+ /** Option flags. Set flag for those options that are
+ * used, all other options are ignored. */
+ struct {
+ /** Use fragmentation mode option */
+ uint32_t frag_mode: 1;
+
+ /** Use TFC padding length option */
+ uint32_t tfc_pad: 1;
+
+ /** Tunnel mode TFC dummy packet. This can be used only
+ * in tunnel mode. When the flag is set, packet length
+ * and content is ignored and instead a TFC dummy
+ * packet is created during IPSEC operation. The dummy
+ * packet length is defined by 'tfc_pad_len' option.
+ * If the SA is configured to copy IP header fields
+ * from inner IP packet, those fields must be passed
+ * with IP parameters option. */
+ uint32_t tfc_dummy: 1;
+
+ /** Use IP parameters option */
+ uint32_t ip_param: 1;
+
+ } flag;
+
+ /** All flag bits
+ *
+ * This field can be used to set/clear all flags, or to perform
+ * bitwise operations over those. */
+ uint32_t all_flags;
+ };
+
+ /** Fragmentation mode */
+ odp_ipsec_frag_mode_t frag_mode;
+
+ /** TFC padding length
+ *
+ * Number of TFC padding bytes added to the packet during IPSEC
+ * processing. Resulting packet should not exceed the maximum packet
+ * length of the pool, otherwise IPSEC operation may fail.
+ * Implementation guarantees that the padding does not contain any
+ * confidential information. */
+ uint32_t tfc_pad_len;
+
+ /** Union of IP parameters */
+ union {
+ /** Override IPv4 parameters in outer header creation.
+ * IP addresses are ignored. */
+ odp_ipsec_ipv4_param_t ipv4;
+
+ /** Override IPv6 parameters in outer header creation.
+ * IP addresses are ignored. */
+ odp_ipsec_ipv6_param_t ipv6;
+ };
+
+} odp_ipsec_out_opt_t;
+
+/**
+ * IPSEC outbound operation parameters
+ */
+typedef struct odp_ipsec_out_param_t {
+ /** Number of SAs
+ *
+ * Outbound IPSEC operation needs SA from application. Use either
+ * single SA for all packets, or a SA per packet.
+ *
+ * Valid values are:
+ * - 1: Single SA for all packets
+ * - N: A SA per packet. N must match the number of packets.
+ */
+ int num_sa;
+
+ /** Number of outbound operation options
+ *
+ * Valid values are:
+ * - 0: No options
+ * - 1: Single option for all packets
+ * - N: An option per packet. N must match the number of packets.
+ */
+ int num_opt;
+
+ /** Pointer to an array of IPSEC SAs */
+ const odp_ipsec_sa_t *sa;
+
+ /** Pointer to an array of outbound operation options
+ *
+ * May be NULL when num_opt is zero.
+ */
+ const odp_ipsec_out_opt_t *opt;
+
+} odp_ipsec_out_param_t;
+
+/**
+ * IPSEC inbound operation parameters
+ */
+typedef struct odp_ipsec_in_param_t {
+ /** Number of SAs
+ *
+ * Inbound IPSEC operation processes a packet using the SA provided by
+ * the application. If the application does not provide an SA, the
+ * operation searches for the SA by matching the input packet with all
+ * inbound SAs according to the lookup mode (odp_ipsec_lookup_mode_t)
+ * configured in each SA. When passing SAs, use either single SA for
+ * all packets, or a SA per packet.
+ *
+ * Valid values are:
+ * - 0: No SAs. SA lookup is done for all packets.
+ * - 1: Single SA for all packets
+ * - N: A SA per packet. N must match the number of packets.
+ */
+ int num_sa;
+
+ /** Pointer to an array of IPSEC SAs
+ *
+ * May be NULL when num_sa is zero.
+ */
+ const odp_ipsec_sa_t *sa;
+
+} odp_ipsec_in_param_t;
+
+/**
+ * Outbound inline IPSEC operation parameters
+ */
+typedef struct odp_ipsec_out_inline_param_t {
+ /** Packet output interface for inline outbound operation without TM
+ *
+ * Outbound inline IPSEC operation uses this packet IO interface to
+ * output the packet after a successful IPSEC transformation. The pktio
+ * must have been configured to operate in inline IPSEC mode.
+ *
+ * The pktio must not have been configured with ODP_PKTOUT_MODE_TM.
+ * For IPSEC inline output to TM enabled interfaces set this field
+ * to ODP_PKTIO_INVALID and specify the TM queue to be used through
+ * the tm_queue parameter. Inline IPSEC output through TM can be
+ * done only if the platform has inline_ipsec_tm capability.
+ */
+ odp_pktio_t pktio;
+
+ /** TM queue for inline outbound operation
+ *
+ * TM queue to be used for inline IPSEC output when pktio field
+ * is ODP_PKTIO_INVALID, indicating use of TM. Otherwise ignored.
+ *
+ * @see odp_ipsec_capability()
+ */
+ odp_tm_queue_t tm_queue;
+
+ /** Outer headers for inline output operation
+ *
+ * Outbound inline IPSEC operation uses this information to prepend
+ * outer headers to the IPSEC packet before sending it out.
+ */
+ struct {
+ /** Points to first byte of outer headers to be copied in
+ * front of the outgoing IPSEC packet. Implementation copies
+ * the headers during odp_ipsec_out_inline() call.
+ *
+ * Null value indicates that the outer headers are in the
+ * packet data, starting at L2 offset and ending at the byte
+ * before L3 offset. In this case, value of 'len' field must
+ * be greater than zero and set to L3 offset minus L2 offset.
+ */
+ const uint8_t *ptr;
+
+ /** Outer header length in bytes */
+ uint32_t len;
+ } outer_hdr;
+
+} odp_ipsec_out_inline_param_t;
+
+/**
+ * IPSEC operation result for a packet
+ */
+typedef struct odp_ipsec_packet_result_t {
+ /** IPSEC operation status. Use this to check if IPSEC operation
+ * reported any errors or warnings (e.g. status.all != ODP_IPSEC_OK).
+ */
+ odp_ipsec_op_status_t status;
+
+ /** IPSEC operation flags */
+ odp_ipsec_op_flag_t flag;
+
+ /** IPSEC SA that was used to create the packet
+ *
+ * Operation updates this SA handle value, when SA look up is performed
+ * as part of the operation and the look up is successful. Operation
+ * status code indicates if the look up failed. Otherwise, the SA
+ * provided by the application is copied here.
+ */
+ odp_ipsec_sa_t sa;
+
+ /** Packet outer header status before inbound inline processing.
+ * This is valid only when outer headers are retained
+ * (see odp_ipsec_inbound_config_t) and flag.inline_mode is set.
+ */
+ struct {
+ /** Points to the first byte of retained outer headers. These
+ * headers are stored in a contiquous, per packet,
+ * implementation specific memory space. Since the memory space
+ * may overlap with e.g. packet head/tailroom, the content
+ * becomes invalid if packet data storage is modified in
+ * any way. The memory space may not be shareable to other
+ * threads. */
+ uint8_t *ptr;
+
+ /** Outer header length in bytes */
+ uint32_t len;
+ } outer_hdr;
+
+ /** Total IP length of the original ESP or AH packet before IPsec
+ * decapsulation. This is valid only for inbound inline and async
+ * processed packets. Zero value means that the length information
+ * is not available.
+ *
+ * If the result packet was reassembled from multiple IPsec
+ * protected packets, this is the sum of the lengths of all the
+ * involved IPsec packets.
+ */
+ uint32_t orig_ip_len;
+
+} odp_ipsec_packet_result_t;
+
+/**
+ * IPSEC status ID
+ */
+typedef enum odp_ipsec_status_id_t {
+ /** Response to SA disable command
+ *
+ * Following status event (odp_ipsec_status_t) fields have valid
+ * content, other fields must be ignored:
+ * - sa: The SA that was requested to be disabled
+ * - result: Operation result
+ */
+ ODP_IPSEC_STATUS_SA_DISABLE = 0,
+
+ /** Warning from inline IPSEC processing
+ *
+ * Following status event (odp_ipsec_status_t) fields have valid
+ * content, other fields must be ignored:
+ * - sa: The SA that caused the warning
+ * - warn: The warning(s) reported by this event
+ *
+ * This status event is generated only for outbound SAs in
+ * ODP_IPSEC_OP_MODE_INLINE mode.
+ */
+ ODP_IPSEC_STATUS_WARN
+
+} odp_ipsec_status_id_t;
+
+/**
+ * IPSEC status content
+ */
+typedef struct odp_ipsec_status_t {
+ /** IPSEC status ID */
+ odp_ipsec_status_id_t id;
+
+ /** IPSEC SA that was target of the operation */
+ odp_ipsec_sa_t sa;
+
+ /** Result of the operation
+ *
+ * 0: Success
+ * <0: Failure
+ */
+ int result;
+
+ /** Warnings of an ODP_IPSEC_STATUS_WARN status event */
+ odp_ipsec_warn_t warn;
+
+} odp_ipsec_status_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/ml.h b/include/odp/api/spec/ml.h
new file mode 100644
index 000000000..1a7710ab3
--- /dev/null
+++ b/include/odp/api/spec/ml.h
@@ -0,0 +1,699 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) offload
+ */
+
+#ifndef ODP_API_SPEC_ML_H_
+#define ODP_API_SPEC_ML_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/event_types.h>
+#include <odp/api/ml_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/std_types.h>
+
+/**
+ * @addtogroup odp_ml
+ * Machine Learning (ML) offload
+ * @{
+ *
+ * <b> ML API call sequence </b>
+ *
+ * Before ML offload can be used, it must be configured with an odp_ml_config() call. An application
+ * fills in configuration parameters to describe its intended ML offload usage. The parameter
+ * values may help ODP implementation to optimize memory and other HW resource usage. The
+ * application may use odp_ml_capability() to check ML capabilities both before and after the
+ * configuration step.
+ *
+ * After configuration, an ML model binary is passed with other parameters to odp_ml_model_create()
+ * call which checks and prepares the model for usage. The application may use odp_ml_model_info(),
+ * odp_ml_model_input_info() and odp_ml_model_output_info() calls to check model input and output
+ * data formats. Before the application can use the model for inference, it loads the model with
+ * an odp_ml_model_load() or odp_ml_model_load_start() call. After a successful load, the
+ * application may use e.g. odp_ml_run() or odp_ml_run_start() to perform inferences.
+ *
+ * When all previously started inference operations are complete, application uses
+ * odp_ml_model_unload() or odp_ml_model_unload_start() to unload the model. After a successful
+ * unload, the model may be destroyed with an odp_ml_model_destroy() call, or loaded again.
+ *
+ * <b> Completion identifiers </b>
+ *
+ * Completion identifiers are used with ML operations in asynchronous poll mode
+ * (#ODP_ML_COMPL_MODE_POLL). Application declares the maximum identifier value it will
+ * use per model with odp_ml_model_param_t.max_compl_id parameter. It cannot exceed
+ * the implementation capability of odp_ml_capability_t.max_compl_id. Completion identifier
+ * values are model specific. The same value can be used simultaneously with two different
+ * models, but cannot be used simultaneously in two ML operations on the same model. A value may be
+ * reused for the next ML operation (on the same model) only after the previous operation is
+ * complete. Within those limitations, application may use/reuse completion identifier
+ * values from 0 to max_compl_id range freely.
+ */
+
+/**
+ * Query ML capabilities
+ *
+ * Outputs ML capabilities on success. Use this capability call to check ML offload implementation
+ * limits and its support of various ML API features. When ML offload is not available,
+ * odp_ml_capability_t.max_models is zero.
+ *
+ * @param[out] capa Pointer to a capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ml_capability(odp_ml_capability_t *capa);
+
+/**
+ * Initialize ML configuration parameters
+ *
+ * Initialize an odp_ml_config_t to its default values.
+ *
+ * @param[out] config Configuration structure to be initialized
+ */
+void odp_ml_config_init(odp_ml_config_t *config);
+
+/**
+ * Configure ML offload
+ *
+ * Initializes and configures ML offload according to the configuration parameters. This function
+ * must be called only once and before any ML resources are created. Use odp_ml_capability() to
+ * query configuration capabilities and odp_ml_config_init() to initialize configuration
+ * parameters into their default values.
+ *
+ * @param config ML configuration parameters
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ml_config(const odp_ml_config_t *config);
+
+/**
+ * Initialize ML model parameters
+ *
+ * Initialize an odp_ml_model_param_t to its default values.
+ *
+ * @param[out] param Parameters structure to be initialized
+ */
+void odp_ml_model_param_init(odp_ml_model_param_t *param);
+
+/**
+ * Create an ML model
+ *
+ * Creates an ML model according to the parameters. Use odp_ml_model_param_init() to initialize
+ * parameters into their default values. The use of model name is optional. Unique names are not
+ * required. However, odp_ml_model_lookup() returns only a single matching model. Maximum name
+ * string length is #ODP_ML_MODEL_NAME_LEN.
+ *
+ * The call copies the model binary and prepares it for loading. Application may free memory
+ * buffers pointed by the parameters when the call returns. Use odp_ml_model_load()
+ * or odp_ml_model_load_start() to load the model. A model is ready for inference runs
+ * (see e.g. odp_ml_run()) after it has been loaded successfully.
+ *
+ * When model metadata misses some details of model input / output data format, user can
+ * pass those with odp_ml_model_param_t.extra_info. Some ODP implementations may define
+ * implementation specific extra parameters (e.g. hints about HW resource usage), user can pass
+ * those with odp_ml_model_param_t.extra_param when applicable.
+ *
+ * @param name Name of the model, or NULL
+ * @param param ML model parameters
+ *
+ * @return ML model handle on success
+ * @retval ODP_ML_MODEL_INVALID on failure
+ */
+odp_ml_model_t odp_ml_model_create(const char *name, const odp_ml_model_param_t *param);
+
+/**
+ * Destroy an ML model
+ *
+ * Destroys a model and releases the resources reserved for it. If the model has been loaded, it
+ * must be unloaded (see odp_ml_model_unload() or odp_ml_model_unload_start()) prior to calling
+ * this function.
+ *
+ * @param model ML model to be destroyed
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ml_model_destroy(odp_ml_model_t model);
+
+/**
+ * Find a model by name
+ *
+ * @param name Name of the model
+ *
+ * @return Handle of the first matching ML model
+ * @retval ODP_ML_MODEL_INVALID Model could not be found
+ */
+odp_ml_model_t odp_ml_model_lookup(const char *name);
+
+/**
+ * Load ML model
+ *
+ * Loads ML model in synchronous mode. When the call returns, load is complete and the model is
+ * ready for inference requests. A loaded model must be unloaded before it can be destroyed.
+ * The same model can be loaded and unloaded multiple times before being destroyed.
+ *
+ * The call optionally outputs results. Use NULL as 'result' pointer if results are not required.
+ *
+ * Application should not try to keep loaded more than configured number of models
+ * (odp_ml_config_t.max_models_loaded). Check ML capabilities for maximum number of loaded
+ * models (odp_ml_capability_t.max_models_loaded) and support of load completion modes
+ * (odp_ml_capability_t.load).
+ *
+ * @param model ML model to be loaded
+ * @param[out] result Pointer to load result structure for output, or NULL
+ *
+ * @retval 0 Model load was successful
+ * @retval <0 on failure
+ */
+int odp_ml_model_load(odp_ml_model_t model, odp_ml_load_result_t *result);
+
+/**
+ * Start asynchronous model load
+ *
+ * Otherwise like odp_ml_model_load(), but loads the model asynchronously. A successful call
+ * requests the model to be loaded, but does not wait for load completion. Completion parameters
+ * are used to select if load completion is reported in poll (#ODP_ML_COMPL_MODE_POLL) or event
+ * (#ODP_ML_COMPL_MODE_EVENT) mode. For poll mode, odp_ml_model_load_status() is called to check
+ * for completion. For event mode, ML offload sends the completion event into the completion
+ * queue when the load is complete. Use odp_ml_compl_param_init() to initialize completion
+ * parameters into their default values.
+ *
+ * @param model ML model to be loaded
+ * @param compl_param Completion parameters for load
+ *
+ * @retval 0 Model load started successfully
+ * @retval <0 on failure
+ */
+int odp_ml_model_load_start(odp_ml_model_t model, const odp_ml_compl_param_t *compl_param);
+
+/**
+ * Check model load completion
+ *
+ * Checks if a previously started model load (in #ODP_ML_COMPL_MODE_POLL mode) has completed.
+ * The completion identifier value from load operation completion parameters
+ * (odp_ml_compl_param_t.compl_id) is passed as a parameter. It specifies the load operation to be
+ * checked. Initially 0 is returned for all configured (but unused) completion identifier values.
+ * An odp_ml_model_load_start() call clears the previous completion status of an identifier, and
+ * this function returns 0 while the load is in progress. When the load is successfully
+ * complete, >0 is returned. If the load completed with a failure, -1 is returned. The same
+ * value is returned until the next start operation that reuses the identifier (with the same
+ * model). The completion identifier may be reused only after >0 or -1 is returned.
+ *
+ * Optionally, outputs more detailed operation results into odp_ml_load_result_t structure.
+ * Use NULL as 'result' pointer if these results are not required.
+ *
+ * @param model ML model being loaded
+ * @param compl_id Completion identifier that was used in load start
+ * @param[out] result Pointer to load result structure for output, or NULL
+ *
+ * @retval >0 Model load was successful
+ * @retval 0 Model load has not finished
+ * @retval -1 Model load failed
+ * @retval <-1 Failed to read completion status (e.g. bad handle)
+ */
+int odp_ml_model_load_status(odp_ml_model_t model, uint32_t compl_id, odp_ml_load_result_t *result);
+
+/**
+ * Unload ML model
+ *
+ * Unloads ML model in synchronous mode. All previously started inference operations must have been
+ * completed before model unload is attempted. When the call returns, unload is complete and the
+ * model is ready to be destroyed or loaded again.
+ *
+ * The call optionally outputs results. Use NULL as 'result' pointer if results are not required.
+ *
+ * @param model ML model to be unloaded
+ * @param[out] result Pointer to load result structure for output, or NULL
+ *
+ * @retval 0 Model unload was successful
+ * @retval <0 on failure
+ */
+int odp_ml_model_unload(odp_ml_model_t model, odp_ml_load_result_t *result);
+
+/**
+ * Start asynchronous model unload
+ *
+ * Otherwise like odp_ml_model_unload(), but unloads the model asynchronously. A successful call
+ * requests the model to be unloaded, but does not wait for unload completion. Completion
+ * parameters are used to select if unload completion is reported in poll (#ODP_ML_COMPL_MODE_POLL)
+ * or event (#ODP_ML_COMPL_MODE_EVENT) mode. For poll mode, odp_ml_model_unload_status() is called
+ * to check for completion. For event mode, ML offload sends the completion event into the
+ * completion queue when the unload is complete. Use odp_ml_compl_param_init() to initialize
+ * completion parameters into their default values.
+ *
+ * @param model ML model to be unloaded
+ * @param compl_param Completion parameters for unload
+ *
+ * @retval 0 Model unload started successfully
+ * @retval <0 on failure
+ */
+int odp_ml_model_unload_start(odp_ml_model_t model, const odp_ml_compl_param_t *compl_param);
+
+/**
+ * Check model unload completion
+ *
+ * Checks if a previously started model unload (in #ODP_ML_COMPL_MODE_POLL mode) has completed.
+ * The completion identifier value from unload operation completion parameters
+ * (odp_ml_compl_param_t.compl_id) is passed as a parameter. It specifies the unload operation to be
+ * checked. Initially 0 is returned for all configured (but unused) completion identifier values.
+ * An odp_ml_model_unload_start() call clears the previous completion status of an identifier, and
+ * this function returns 0 while the unload is in progress. When the unload is successfully
+ * complete, >0 is returned. If the unload completed with a failure, -1 is returned. The same
+ * value is returned until the next start operation that reuses the identifier (with the same
+ * model). The completion identifier may be reused only after >0 or -1 is returned.
+ *
+ * Optionally, outputs more detailed operation results into odp_ml_load_result_t structure.
+ * Use NULL as 'result' pointer if these results are not required.
+ *
+ * @param model ML model being unloaded
+ * @param compl_id Completion identifier that was used in unload start
+ * @param[out] result Pointer to load result structure for output, or NULL
+ *
+ * @retval >0 Model unload was successful
+ * @retval 0 Model unload has not finished
+ * @retval -1 Model unload failed
+ * @retval <-1 Failed to read completion status (e.g. bad handle)
+ */
+int odp_ml_model_unload_status(odp_ml_model_t model, uint32_t compl_id,
+ odp_ml_load_result_t *result);
+
+/**
+ * Initialize model run parameters
+ *
+ * Initialize an odp_ml_run_param_t to its default values.
+ *
+ * @param[out] param Model run parameters structure to be initialized
+ */
+void odp_ml_run_param_init(odp_ml_run_param_t *param);
+
+/**
+ * Run the model in synchronous mode
+ *
+ * Performs an ML inference operation using the model and input data pointed by the data descriptor.
+ * A successful operation writes inference output data into memory buffers pointed by the data
+ * descriptor. Input/output data buffers are described as an array of segment descriptors. Each
+ * segment descriptor specifies a memory buffer used with only one model input/output. Multiple
+ * subsequent descriptors may be used to specify segmented data for the same input/output.
+ * When the model has multiple inputs/outputs, descriptor order in the array follows the model
+ * input/output order reported by odp_ml_model_input_info() and odp_ml_model_output_info() calls.
+ * All memory buffers for the first input/output are specified before any buffers for the second
+ * input/output, and so on.
+ *
+ * When some model inputs/outputs have #ODP_ML_SHAPE_BATCH shape type, the batch size is specified
+ * in run parameters (odp_ml_run_param_t.batch_size). The same batch size is used for all such
+ * inputs/outputs. Application may request additional operation results by setting 'result' pointer
+ * in run parameters. Use odp_ml_run_param_init() to initialize run parameters into their default
+ * values. Default run parameter values are used when 'param' is NULL.
+ *
+ * Returns 1 when model run completed successfully. Returns 0 when the operation was not performed
+ * due to ML offload resources being temporarily busy. Returns <0 on failure.
+ *
+ * @param model ML model to be run
+ * @param data Model input/output data descriptor
+ * @param param Model run parameters, or NULL
+ *
+ * @retval 1 Model run completed successfully
+ * @retval 0 Resources are busy and model was not run
+ * @retval <0 on failure
+ */
+int odp_ml_run(odp_ml_model_t model, const odp_ml_data_t *data, const odp_ml_run_param_t *param);
+
+/**
+ * Run the model multiple times in synchronous mode
+ *
+ * Otherwise like odp_ml_run(), but runs the model 'num' times with different input/output
+ * data buffers. Output data buffers of one ML inference operation must not overlap with
+ * input/output data buffers of another one.
+ *
+ * Returns number of model runs successfully completed. When return value is less than 'num',
+ * the remaining runs were not performed due to ML offload resources being temporarily busy.
+ * Returns <0 on failure.
+ *
+ * @param model ML model to be run
+ * @param data Array of model input/output data descriptors. The array has 'num' elements.
+ * @param param Array of model run parameters, or NULL. The array has 'num' elements
+ * when used.
+ * @param num Number of model runs to perform
+ *
+ * @return Number of model runs completed successfully (1 ... num)
+ * @retval 0 Resources are busy and model was not run
+ * @retval <0 on failure
+ */
+int odp_ml_run_multi(odp_ml_model_t model, const odp_ml_data_t data[],
+ const odp_ml_run_param_t param[], int num);
+
+/**
+ * Start model run in asynchronous mode
+ *
+ * Otherwise like odp_ml_run(), but runs the model asynchronously. A successful call
+ * requests the model to be run, but does not wait for run completion. Completion parameters
+ * select if run completion is reported in poll (#ODP_ML_COMPL_MODE_POLL) or event
+ * (#ODP_ML_COMPL_MODE_EVENT) mode. For poll mode, odp_ml_run_status() is called to check
+ * for completion. For event mode, ML offload sends the completion event into the completion queue
+ * when the run is complete. Use odp_ml_compl_param_init() to initialize completion parameters
+ * into their default values.
+ *
+ * Additional operation results (odp_ml_run_result_t) are available through the status call
+ * (odp_ml_run_status()) or completion event (odp_ml_compl_run_result()). Results are
+ * not output through the run parameters structure (i.e. odp_ml_run_param_t.result is ignored).
+ *
+ * Returns 1 when model run was started successfully. Returns 0 when model run was not started
+ * due to ML offload resources being temporarily busy. Returns <0 on failure.
+ *
+ * @param model ML model to be run
+ * @param data Model input/output data descriptor
+ * @param compl_param Completion parameters
+ * @param run_param Model run parameters, or NULL
+ *
+ * @retval 1 Model run started successfully
+ * @retval 0 Resources are busy and model run was not started
+ * @retval <0 on failure
+ */
+int odp_ml_run_start(odp_ml_model_t model, const odp_ml_data_t *data,
+ const odp_ml_compl_param_t *compl_param, const odp_ml_run_param_t *run_param);
+
+/**
+ * Start multiple model runs in asynchronous mode
+ *
+ * Otherwise like odp_ml_run_start(), but starts 'num' model runs with different input/output
+ * data buffers. Output data buffers of one ML inference operation must not overlap with
+ * input/output data buffers of another one.
+ *
+ * Returns number of model runs started successfully. When return value is less than 'num',
+ * the remaining runs were not started due to ML offload resources being temporarily busy.
+ * Returns <0 on failure.
+ *
+ * @param model ML model to be run
+ * @param data Array of model input/output data descriptors. The array has 'num' elements.
+ * @param compl_param Array of completion parameters. The array has 'num' elements.
+ * @param run_param Array of model run parameters, or NULL. The array has 'num' elements
+ * when used.
+ * @param num Number of model runs to start
+ *
+ * @return Number of model runs started successfully (1 ... num)
+ * @retval 0 Resources are busy and model runs were not started
+ * @retval <0 on failure
+ */
+int odp_ml_run_start_multi(odp_ml_model_t model, const odp_ml_data_t data[],
+ const odp_ml_compl_param_t compl_param[],
+ const odp_ml_run_param_t run_param[], int num);
+
+/**
+ * Check model run completion
+ *
+ * Checks if a previously started model run (in #ODP_ML_COMPL_MODE_POLL mode) has completed.
+ * The completion identifier value from run operation completion parameters
+ * (odp_ml_compl_param_t.compl_id) is passed as a parameter. It specifies the run operation to be
+ * checked. Initially 0 is returned for all configured (but unused) completion identifier values.
+ * An odp_ml_run_start() call clears the previous completion status of an identifier, and
+ * this function returns 0 while the run is in progress. When the run is successfully
+ * complete, >0 is returned. If the run completed with a failure, -1 is returned. The same
+ * value is returned until the next start operation that reuses the identifier (with the same
+ * model). The completion identifier may be reused only after >0 or -1 is returned.
+ *
+ * Optionally, outputs more detailed operation results into odp_ml_run_result_t structure.
+ * Use NULL as 'result' pointer if these results are not required.
+ *
+ * @param model ML model running
+ * @param compl_id Completion identifier that was used in run start
+ * @param[out] result Pointer to run result structure for output, or NULL
+ *
+ * @retval >0 Model run was successful
+ * @retval 0 Model run has not finished
+ * @retval -1 Model run failed
+ * @retval <-1 Failed to read completion status (e.g. bad handle)
+ */
+int odp_ml_run_status(odp_ml_model_t model, uint32_t compl_id, odp_ml_run_result_t *result);
+
+/**
+ * Initialize ML completion event pool parameters
+ *
+ * Initialize an odp_ml_compl_pool_param_t to its default values.
+ *
+ * @param[out] param Parameter structure to be initialized
+ */
+void odp_ml_compl_pool_param_init(odp_ml_compl_pool_param_t *param);
+
+/**
+ * Create ML completion event pool
+ *
+ * Creates a pool of ML completion events (ODP_EVENT_ML_COMPL). Pool type is ODP_POOL_ML_COMPL.
+ * The use of pool name is optional. Unique names are not required. However, odp_pool_lookup()
+ * returns only a single matching pool. Use odp_ml_compl_pool_param_init() to initialize pool
+ * parameters into their default values. Parameters values must not exceed pool capabilities
+ * (see odp_ml_compl_pool_capability_t).
+ *
+ * @param name Name of the pool or NULL. Maximum string length is #ODP_POOL_NAME_LEN.
+ * @param param Pool parameters
+ *
+ * @return Pool handle on success
+ * @retval ODP_POOL_INVALID on failure
+ */
+odp_pool_t odp_ml_compl_pool_create(const char *name, const odp_ml_compl_pool_param_t *param);
+
+/**
+ * Allocate ML completion event
+ *
+ * Allocates an ML completion event from a pool. The pool must have been created with
+ * odp_ml_compl_pool_create() call. All completion event metadata are set to their default values.
+ *
+ * @param pool ML completion event pool
+ *
+ * @return ML completion event handle
+ * @retval ODP_ML_COMPL_INVALID Completion event could not be allocated
+ */
+odp_ml_compl_t odp_ml_compl_alloc(odp_pool_t pool);
+
+/**
+ * Free ML completion event
+ *
+ * Frees an ML completion event into the pool it was allocated from.
+ *
+ * @param ml_compl ML completion event handle
+ */
+void odp_ml_compl_free(odp_ml_compl_t ml_compl);
+
+/**
+ * Check ML model run results from completion event
+ *
+ * Reads model run results from an ML completion event (ODP_EVENT_ML_COMPL). The event indicates
+ * completion of a previously started inference operation. Subtype of the completion event must be
+ * ODP_EVENT_ML_COMPL_RUN. Function return value indicates if the model run succeeded or failed.
+ * Additionally, outputs more detailed results into the provided odp_ml_run_result_t
+ * structure. Use NULL as 'result' pointer if those results are not required.
+ *
+ * @param ml_compl ML completion event (subtype ODP_EVENT_ML_COMPL_RUN)
+ * @param[out] result Pointer to ML run result structure for output, or NULL.
+ *
+ * @retval 0 Model run was successful
+ * @retval -1 Model run failed
+ * @retval <-1 Failed to read results from the event (e.g. bad handle)
+ */
+int odp_ml_compl_run_result(odp_ml_compl_t ml_compl, odp_ml_run_result_t *result);
+
+/**
+ * Check ML model load / unload results from completion event
+ *
+ * Reads model load / unload results from an ML completion event (ODP_EVENT_ML_COMPL). The event
+ * indicates completion of a previously started model load / unload operation. Subtype of the
+ * completion event must be ODP_EVENT_ML_COMPL_LOAD. Function return value indicates if the model
+ * load / unload succeeded or failed. Additionally, outputs more detailed results into the provided
+ * odp_ml_load_result_t structure. Use NULL as 'result' pointer if those results are not required.
+ *
+ * @param ml_compl ML completion event (subtype ODP_EVENT_ML_COMPL_LOAD)
+ * @param[out] result Pointer to model load / unload result structure for output, or NULL.
+ *
+ * @retval 0 Model load / unload was successful
+ * @retval -1 Model load / unload failed
+ * @retval <-1 Failed to read results from the event (e.g. bad handle)
+ */
+int odp_ml_compl_load_result(odp_ml_compl_t ml_compl, odp_ml_load_result_t *result);
+
+/**
+ * ML completion event user area
+ *
+ * Returns pointer to the user area associated with the completion event. Size of the area is
+ * fixed and defined in pool parameters.
+ *
+ * @param ml_compl ML completion event
+ *
+ * @return Pointer to the user area of the completion event
+ * @retval NULL The completion event does not have user area
+ */
+void *odp_ml_compl_user_area(odp_ml_compl_t ml_compl);
+
+/**
+ * Convert event to ML completion event
+ *
+ * Converts an ODP_EVENT_ML_COMPL type event to an ML completion event.
+ *
+ * @param event Event handle
+ *
+ * @return ML completion event handle
+ */
+odp_ml_compl_t odp_ml_compl_from_event(odp_event_t event);
+
+/**
+ * Convert ML completion event to event
+ *
+ * @param ml_compl ML completion event handle
+ *
+ * @return Event handle
+ */
+odp_event_t odp_ml_compl_to_event(odp_ml_compl_t ml_compl);
+
+/**
+ * Convert ML completion event handle to a uint64_t value for debugging
+ *
+ * @param ml_compl ML completion event handle to be converted
+ *
+ * @return uint64_t value that can be used for debugging (e.g. printed)
+ */
+uint64_t odp_ml_compl_to_u64(odp_ml_compl_t ml_compl);
+
+/**
+ * Initialize ML completion parameters
+ *
+ * Initialize an odp_ml_compl_param_t to its default values.
+ *
+ * @param[out] param Address of parameters structure to be initialized
+ */
+void odp_ml_compl_param_init(odp_ml_compl_param_t *param);
+
+/**
+ * Retrieve model information
+ *
+ * Retrieve information about the model. Model information includes e.g. version numbers and
+ * number of model inputs/outputs. Information about each input and output can be retrieved with
+ * odp_ml_model_input_info() and odp_ml_model_output_info() calls.
+ *
+ * @param model ML model handle
+ * @param[out] info Pointer to model information structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_ml_model_info(odp_ml_model_t model, odp_ml_model_info_t *info);
+
+/**
+ * Retrieve model input information
+ *
+ * Writes information about each model input into the array. If there are more inputs than array
+ * elements, writes only 'num' elements. Returns the number of model inputs on success, and zero on
+ * failure. When 'num' is zero, ignores value of 'info' and returns normally.
+ *
+ * @param model ML model handle
+ * @param[out] info Pointer to model input information array for output
+ * @param num Number of elements in the array
+ *
+ * @return Number of model inputs
+ * @retval 0 on failure
+ */
+uint32_t odp_ml_model_input_info(odp_ml_model_t model, odp_ml_input_info_t info[], uint32_t num);
+
+/**
+ * Retrieve model output information
+ *
+ * Writes information about each model output into the array. If there are more outputs than array
+ * elements, writes only 'num' elements. Returns the number of model outputs on success, and zero on
+ * failure. When 'num' is zero, ignores value of 'info' and returns normally.
+ *
+ * @param model ML model handle
+ * @param[out] info Pointer to model output information array for output
+ * @param num Number of elements in the array
+ *
+ * @return Number of model outputs
+ * @retval 0 on failure
+ */
+uint32_t odp_ml_model_output_info(odp_ml_model_t model, odp_ml_output_info_t info[], uint32_t num);
+
+/**
+ * Convert ML model handle to a uint64_t value for debugging
+ *
+ * @param model ML model handle
+ *
+ * @return uint64_t value that can be used for debugging (e.g. printed)
+ */
+uint64_t odp_ml_model_to_u64(odp_ml_model_t model);
+
+/**
+ * Print debug information about the model.
+ *
+ * Print implementation defined information about ML model to the ODP log. The information is
+ * intended to be used for debugging.
+
+ * @param model ML model handle
+ */
+void odp_ml_model_print(odp_ml_model_t model);
+
+/**
+ * Print ML debug information
+ *
+ * Print implementation defined information about ML offload to the ODP log. The information is
+ * intended to be used for debugging.
+ */
+void odp_ml_print(void);
+
+/**
+ * Extra statistics counter information
+ *
+ * Returns the number of extra statistics counters supported by the ML offload, and outputs
+ * information (e.g. name) about those. Counters are implementation specific and maintained
+ * per model. Statistics counting is enabled through model create parameters.
+ *
+ * When 'info' pointer is not NULL, fills in up to 'num' counter info structures. If the return
+ * value is larger than 'num', there are more counters than the function was allowed to output.
+ * If the return value N is less than 'num' (on success), only first N structures have been written.
+ *
+ * Info array elements are filled in the same order than odp_ml_model_extra_stats() outputs
+ * counter values.
+ *
+ * @param model ML model
+ * @param[out] info Pointer to extra statistics counter information array for output.
+ * NULL may be used to query only the number of counters.
+ * @param num Number of elements in the array
+ *
+ * @return Number of extra statistics counters
+ * @retval <0 on failure
+ */
+int odp_ml_model_extra_stat_info(odp_ml_model_t model, odp_ml_extra_stat_info_t info[], int num);
+
+/**
+ * Read extra statistics counter values
+ *
+ * Reads extra statistics counter values and returns the number of supported counters. Outputs
+ * up to 'num' counter values into 'stats' array. If the return value is larger than 'num',
+ * there are more counters than the function was allowed to output. If the return value N is less
+ * than 'num' (on success), only first N counters have been written. The order of counters in
+ * the array matches the counter information array order on odp_ml_model_extra_stat_info() output.
+ *
+ * @param model ML model
+ * @param[out] stats Pointer to extra statistics counter array for output
+ * @param num Number of elements in the array
+ *
+ * @return Number of extra statistics counters
+ * @retval <0 on failure
+ */
+int odp_ml_model_extra_stats(odp_ml_model_t model, uint64_t stats[], int num);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/ml_quantize.h b/include/odp/api/spec/ml_quantize.h
new file mode 100644
index 000000000..25565ef27
--- /dev/null
+++ b/include/odp/api/spec/ml_quantize.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) quantization functions
+ */
+
+#ifndef ODP_API_SPEC_ML_QUANTIZE_H_
+#define ODP_API_SPEC_ML_QUANTIZE_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @addtogroup odp_ml
+ * @{
+ */
+
+/**
+ * Quantize 32-bit float to uint8_t
+ *
+ * Quantizes 'num' 32-bit floating point values to uint8_t values using the provided scale and
+ * zero point.
+ *
+ * dst_u8 = (src_fp32 / scale) + zerop
+ *
+ * @param[out] dst_u8 Destination address for quantized values
+ * @param src_fp32 Source address of values to be quantized
+ * @param num Number of values
+ * @param scale Scale for quantization
+ * @param zerop Zero point for quantization
+ */
+void odp_ml_fp32_to_uint8(uint8_t *dst_u8, const float *src_fp32, uint32_t num,
+ float scale, uint8_t zerop);
+
+/**
+ * De-quantize 32-bit float from uint8_t
+ *
+ * De-quantizes 'num' 32-bit floating point values from uint8_t values using the provided scale and
+ * zero point.
+ *
+ * dst_fp32 = (src_u8 - zerop) * scale
+ *
+ * @param[out] dst_fp32 Destination address for de-quantized values
+ * @param src_u8 Source address of values to be de-quantized
+ * @param num Number of values
+ * @param scale Scale for de-quantization
+ * @param zerop Zero point for de-quantization
+ */
+void odp_ml_fp32_from_uint8(float *dst_fp32, const uint8_t *src_u8, uint32_t num,
+ float scale, uint8_t zerop);
+
+/**
+ * Quantize 32-bit float to int8_t
+ *
+ * Quantizes 'num' 32-bit floating point values to int8_t values using the provided scale and
+ * zero point.
+ *
+ * dst_i8 = (src_fp32 / scale) + zerop
+ *
+ * @param[out] dst_i8 Destination address for quantized values
+ * @param src_fp32 Source address of values to be quantized
+ * @param num Number of values
+ * @param scale Scale for quantization
+ * @param zerop Zero point for quantization
+ */
+void odp_ml_fp32_to_int8(int8_t *dst_i8, const float *src_fp32, uint32_t num, float scale,
+ int8_t zerop);
+
+/**
+ * De-quantize 32-bit float from int8_t
+ *
+ * De-quantizes 'num' 32-bit floating point values from int8_t values using the provided scale and
+ * zero point.
+ *
+ * dst_fp32 = (src_i8 - zerop) * scale
+ *
+ * @param[out] dst_fp32 Destination address for de-quantized values
+ * @param src_i8 Source address of values to be de-quantized
+ * @param num Number of values
+ * @param scale Scale for de-quantization
+ * @param zerop Zero point for de-quantization
+ */
+void odp_ml_fp32_from_int8(float *dst_fp32, const int8_t *src_i8, uint32_t num, float scale,
+ int8_t zerop);
+
+/**
+ * Quantize 32-bit float to 16-bit float
+ *
+ * Quantizes 'num' 32-bit floating point values to 16-bit floating point values.
+ *
+ * @param[out] dst_fp16 Destination address for quantized values
+ * @param src_fp32 Source address of values to be quantized
+ * @param num Number of values
+ */
+void odp_ml_fp32_to_fp16(uint16_t *dst_fp16, const float *src_fp32, uint32_t num);
+
+/**
+ * De-quantize 32-bit float from 16-bit float
+ *
+ * De-quantizes 'num' 32-bit floating point values from 16-bit floating point values.
+ *
+ * @param[out] dst_fp32 Destination address for de-quantized values
+ * @param src_fp16 Source address of values to be de-quantized
+ * @param num Number of values
+ */
+void odp_ml_fp32_from_fp16(float *dst_fp32, const uint16_t *src_fp16, uint32_t num);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/ml_types.h b/include/odp/api/spec/ml_types.h
new file mode 100644
index 000000000..2b8f9d6c8
--- /dev/null
+++ b/include/odp/api/spec/ml_types.h
@@ -0,0 +1,873 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP Machine Learning (ML) types
+ */
+
+#ifndef ODP_API_SPEC_ML_TYPES_H_
+#define ODP_API_SPEC_ML_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_ml ODP ML
+ * @{
+ */
+
+/**
+ * @typedef odp_ml_model_t
+ * ODP ML model handle
+ */
+
+/**
+ * @def ODP_ML_MODEL_INVALID
+ * Invalid ML model
+ */
+
+/**
+ * @typedef odp_ml_compl_t
+ * ML completion event
+ */
+
+/**
+ * @def ODP_ML_COMPL_INVALID
+ * Invalid ML completion event
+ */
+
+/**
+ * @def ODP_ML_MODEL_NAME_LEN
+ * Maximum length of model name in chars (including null char)
+ */
+
+/**
+ * @def ODP_ML_MODEL_IO_NAME_LEN
+ * Maximum length of model input/output name in chars (including null char)
+ */
+
+/**
+ * @def ODP_ML_SHAPE_NAME_LEN
+ * Maximum length of data dimension name in chars (including null char)
+ */
+
+/**
+ * @def ODP_ML_EXTRA_STAT_NAME_LEN
+ * Maximum length of extra statistics counter name in chars (including null char)
+ */
+
+/**
+ * @typedef odp_ml_model_extra_param_t
+ * ODP implementation specific extra parameters for model creation
+ */
+
+/** Maximum number of dimensions in input / output data shape */
+#define ODP_ML_MAX_DIMS 8
+
+/** Dimension size is dynamic */
+#define ODP_ML_DIM_DYNAMIC 0
+
+/** Synchronous operation */
+#define ODP_ML_COMPL_MODE_SYNC 0x1u
+
+/**
+ * Asynchronous poll mode operation
+ *
+ * A function call starts an operation and a status function call indicates when
+ * the operation has finished.
+ */
+#define ODP_ML_COMPL_MODE_POLL 0x2u
+
+/**
+ * Asynchronous event mode operation
+ *
+ * A function call starts an operation and a completion event indicates when
+ * the operation has finished.
+ */
+#define ODP_ML_COMPL_MODE_EVENT 0x4u
+
+/** ML completion mode */
+typedef uint32_t odp_ml_compl_mode_t;
+
+/**
+ * ML completion event pool capabilities
+ *
+ * Pool statistics are not supported with ML completion event pools.
+ */
+typedef struct odp_ml_compl_pool_capability_t {
+ /**
+ * Maximum number of ML completion event pools
+ *
+ * See odp_pool_capability_t.max_pools for maximum number of pools of any type. It
+ * includes also ML completion event pools.
+ */
+ uint32_t max_pools;
+
+ /** Maximum number of ML completion events in a pool */
+ uint32_t max_num;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** User area persistence
+ *
+ * See buf.uarea_persistence of odp_pool_capability_t for details
+ * (odp_pool_capability_t.uarea_persistence).
+ */
+ odp_bool_t uarea_persistence;
+
+ /** Maximum size of local thread cache */
+ uint32_t max_cache_size;
+
+ /** Minimum size of local thread cache */
+ uint32_t min_cache_size;
+
+} odp_ml_compl_pool_capability_t;
+
+/**
+ * ML completion event pool parameters
+ *
+ * Use odp_ml_compl_pool_param_init() to initialize the structure to its default values.
+ */
+typedef struct odp_ml_compl_pool_param_t {
+ /**
+ * Number of ML completion events in the pool
+ *
+ * The maximum supported value is defined by ML pool capability 'max_num'
+ * (odp_ml_compl_pool_capability_t.max_num).
+ */
+ uint32_t num;
+
+ /**
+ * User area size in bytes
+ *
+ * The maximum supported value is defined by ML pool capability 'max_uarea_size'.
+ * Specify as zero if no user area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Parameters for user area initialization */
+ struct {
+ /** See uarea_init.init_fn of odp_pool_param_t for details
+ * (odp_pool_param_t.init_fn). Function is called during
+ * odp_ml_compl_pool_create(). The default value is NULL. */
+ void (*init_fn)(void *uarea, uint32_t size, void *args, uint32_t index);
+
+ /** See uarea_init.args of odp_pool_param_t for details
+ * (odp_pool_param_t.args). The default value is NULL. */
+ void *args;
+
+ } uarea_init;
+
+ /**
+ * Maximum number of events cached locally per thread
+ *
+ * See odp_pool_param_t.cache_size documentation for details. Valid values range from
+ * 'min_cache_size' to 'max_cache_size' ML pool capability. The default value is
+ * implementation specific and set by odp_ml_compl_pool_param_init().
+ */
+ uint32_t cache_size;
+
+} odp_ml_compl_pool_param_t;
+
+/** Machine learning capabilities */
+typedef struct odp_ml_capability_t {
+ /** Maximum number of models
+ *
+ * Maximum number of models that can be created simultaneously. The value is zero when
+ * ML offload is not available. */
+ uint32_t max_models;
+
+ /** Maximum number of models that can be loaded simultaneously */
+ uint32_t max_models_loaded;
+
+ /** Maximum model size in bytes */
+ uint64_t max_model_size;
+
+ /** Maximum completion identifier value */
+ uint32_t max_compl_id;
+
+ /** Maximum number of model inputs */
+ uint32_t max_inputs;
+
+ /** Maximum number of model outputs */
+ uint32_t max_outputs;
+
+ /**
+ * Maximum number of data segments per model input
+ *
+ * Segmented input data is not supported when 1.
+ */
+ uint32_t max_segs_per_input;
+
+ /**
+ * Maximum number of data segments per model output
+ *
+ * Segmented output data is not supported when 1.
+ */
+ uint32_t max_segs_per_output;
+
+ /**
+ * Minimum input data alignment in bytes
+ *
+ * For each model input, the first data segment must start at this or a higher power of two
+ * memory alignment in bytes. The value is 1 when there is no alignment requirement.
+ */
+ uint32_t min_input_align;
+
+ /**
+ * Minimum output data alignment in bytes
+ *
+ * For each model output, the first data segment must start at this or a higher power of two
+ * memory alignment in bytes. The value is 1 when there is no alignment requirement.
+ */
+ uint32_t min_output_align;
+
+ /**
+ * Input data packing
+ *
+ * 0: Data packing is not required.
+ * 1: Data for all model inputs must be continuous in memory. The memory block starts with
+ * data for the first input and continues through all inputs in-order and without gaps
+ * between inputs. The minimum alignment requirement (min_input_align) applies only for
+ * the first input.
+ */
+ odp_bool_t packed_input_data;
+
+ /**
+ * Output data packing
+ *
+ * 0: Data packing is not required.
+ * 1: Data buffer space for all model outputs must be continuous in memory. The memory
+ * block starts with buffer space for the first output and continues through all outputs
+ * in-order and without gaps between outputs. The minimum alignment requirement
+ * (min_output_align) applies only for the first output.
+ */
+ odp_bool_t packed_output_data;
+
+ /** Model load / unload capabilities */
+ struct {
+ /**
+ * Supported completion modes for model load / unload operations
+ *
+ * Mask of supported completion modes. Each supported mode has the corresponding
+ * flag (e.g. #ODP_ML_COMPL_MODE_SYNC) set in the mask.
+ */
+ odp_ml_compl_mode_t compl_mode_mask;
+
+ /**
+ * Support of model load / unload completion into plain queues
+ *
+ * Specifies if plain queues are supported as destination queues for
+ * load / unload completion events (#ODP_ML_COMPL_MODE_EVENT).
+ *
+ * 0: Plain queues are not supported as completion queues
+ * 1: Plain queues are supported as completion queues
+ */
+ odp_bool_t compl_queue_plain;
+
+ /**
+ * Support of model load / unload completion into scheduled queues
+ *
+ * Specifies if scheduled queues are supported as destination queues for
+ * load / unload completion events (#ODP_ML_COMPL_MODE_EVENT).
+ *
+ * 0: Scheduled queues are not supported as completion queues
+ * 1: Scheduled queues are supported as completion queues
+ */
+ odp_bool_t compl_queue_sched;
+
+ } load;
+
+ /** Model run capabilities */
+ struct {
+ /**
+ * Supported completion modes for model run operations
+ *
+ * Mask of supported completion modes. Each supported mode has the corresponding
+ * flag (e.g. #ODP_ML_COMPL_MODE_SYNC) set in the mask.
+ */
+ odp_ml_compl_mode_t compl_mode_mask;
+
+ /**
+ * Support of model run completion into plain queues
+ *
+ * Specifies if plain queues are supported as destination queues for
+ * run completion events (#ODP_ML_COMPL_MODE_EVENT).
+ *
+ * 0: Plain queues are not supported as completion queues
+ * 1: Plain queues are supported as completion queues
+ */
+ odp_bool_t compl_queue_plain;
+
+ /**
+ * Support of model run completion into scheduled queues
+ *
+ * Specifies if scheduled queues are supported as destination queues for
+ * run completion events (#ODP_ML_COMPL_MODE_EVENT).
+ *
+ * 0: Scheduled queues are not supported as completion queues
+ * 1: Scheduled queues are supported as completion queues
+ */
+ odp_bool_t compl_queue_sched;
+
+ } run;
+
+ /** ML completion event pool capabilities */
+ odp_ml_compl_pool_capability_t pool;
+
+} odp_ml_capability_t;
+
+/** Machine learning configuration parameters */
+typedef struct odp_ml_config_t {
+ /**
+ * Maximum number of models
+ *
+ * Application may create and use this many models simultaneously. The default value is 1.
+ */
+ uint32_t max_models_created;
+
+ /**
+ * Maximum number of models loaded
+ *
+ * Maximum number of models that the application will keep loaded simultaneously.
+ * The default value is 1.
+ */
+ uint32_t max_models_loaded;
+
+ /**
+ * Maximum model binary size in bytes
+ *
+ * All model binaries application will pass to odp_ml_model_create() are this size or
+ * smaller.
+ */
+ uint64_t max_model_size;
+
+ /**
+ * Load / unload completion modes
+ *
+ * Mask of completion modes that application will use with model load/unload operations.
+ * Multiple modes may be selected, but it is implementation specific if some combinations
+ * are not supported. In case of an unsupported combination odp_ml_config() returns
+ * failure. Check odp_ml_capability_t.load for supported modes. The default value is 0.
+ */
+ odp_ml_compl_mode_t load_mode_mask;
+
+ /**
+ * Run completion modes
+ *
+ * Mask of completion modes that application will use with model run operations.
+ * Multiple modes may be selected, but it is implementation specific if some combinations
+ * are not supported. In case of an unsupported combination odp_ml_config() returns
+ * failure. Check odp_ml_capability_t.run for supported modes. The default value is 0.
+ */
+ odp_ml_compl_mode_t run_mode_mask;
+
+} odp_ml_config_t;
+
+/** Model input / output data type enumeration */
+typedef enum {
+ /** Data type is not defined */
+ ODP_ML_DATA_TYPE_NONE = 0,
+
+ /** 8-bit integer */
+ ODP_ML_DATA_TYPE_INT8,
+
+ /** 8-bit unsigned integer */
+ ODP_ML_DATA_TYPE_UINT8,
+
+ /** 16-bit integer */
+ ODP_ML_DATA_TYPE_INT16,
+
+ /** 16-bit unsigned integer */
+ ODP_ML_DATA_TYPE_UINT16,
+
+ /** 24-bit integer */
+ ODP_ML_DATA_TYPE_INT24,
+
+ /** 24-bit unsigned integer */
+ ODP_ML_DATA_TYPE_UINT24,
+
+ /** 32-bit integer */
+ ODP_ML_DATA_TYPE_INT32,
+
+ /** 32-bit unsigned integer */
+ ODP_ML_DATA_TYPE_UINT32,
+
+ /** 64-bit integer */
+ ODP_ML_DATA_TYPE_INT64,
+
+ /** 64-bit unsigned integer */
+ ODP_ML_DATA_TYPE_UINT64,
+
+ /** 16-bit floating point number */
+ ODP_ML_DATA_TYPE_FP16,
+
+ /** 16-bit brain floating point (bfloat16) number */
+ ODP_ML_DATA_TYPE_BFP16,
+
+ /** 32-bit floating point number */
+ ODP_ML_DATA_TYPE_FP32,
+
+ /** 64-bit floating point number */
+ ODP_ML_DATA_TYPE_FP64,
+
+} odp_ml_data_type_t;
+
+/** Model input / output data shape type */
+typedef enum {
+ /** Type of shape is not defined */
+ ODP_ML_SHAPE_NONE = 0,
+
+ /** Static shape of data
+ *
+ * Shape is static when all dimensions have fixed sizes.
+ */
+ ODP_ML_SHAPE_STATIC,
+
+ /** Dynamic batch size
+ *
+ * Shape that has only one dynamic dimension, and the dimension is used as batch size of
+ * input / output data. The same batch size is applied for all inputs and outputs of
+ * the model.
+ */
+ ODP_ML_SHAPE_BATCH,
+
+} odp_ml_shape_type_t;
+
+/** Model input / output data shape information */
+typedef struct odp_ml_shape_info_t {
+ /** Shape type */
+ odp_ml_shape_type_t type;
+
+ /** Number of dimensions
+ *
+ * Number of input / output data dimensions. When zero, the model does not have
+ * dimension information available. ODP API supports in maximum #ODP_ML_MAX_DIMS
+ * dimensions.
+ */
+ uint32_t num_dim;
+
+ /** Dimension sizes
+ *
+ * Number of data values in each ('num_dim') dimension. Type of the data is defined by
+ * odp_ml_data_type_t enumeration. Depending on the shape type, some dimensions may have
+ * dynamic size which is denoted with #ODP_ML_DIM_DYNAMIC value. When shape type is
+ * #ODP_ML_SHAPE_BATCH, the shape has one dynamic dimension which is used as the batch
+ * size.
+ *
+ * For example, a static (#ODP_ML_SHAPE_STATIC) NCHW tensor could be presented as:
+ *
+ * num_dim = 4;
+ * dim[0] = 1; // no batching, N = 1
+ * dim[1] = 3; // 3 color channels
+ * dim[2] = 720; // height 720 pixels
+ * dim[3] = 1280; // width 1280 pixels
+ *
+ * ... and with dynamic batch size (#ODP_ML_SHAPE_BATCH):
+ *
+ * num_dim = 4;
+ * dim[0] = ODP_ML_DIM_DYNAMIC; // dynamic in range: dim_min[0] ... dim_max[0]
+ * dim[1] = 3;
+ * dim[2] = 720;
+ * dim[3] = 1280;
+ */
+ uint32_t dim[ODP_ML_MAX_DIMS];
+
+ /** Minimum dimension sizes
+ *
+ * Defines the minimum value for each dynamic size (#ODP_ML_DIM_DYNAMIC) in dim[] array.
+ * Zero is used when the minimum value is unknown. When dimension size is static, the
+ * value is equal to dim[] array value.
+ */
+ uint32_t dim_min[ODP_ML_MAX_DIMS];
+
+ /** Maximum dimension sizes
+ *
+ * Defines the maximum value for each dynamic size (#ODP_ML_DIM_DYNAMIC) in dim[] array.
+ * Zero is used when the maximum value is unknown. When dimension size is static, the
+ * value is equal to dim[] array value.
+ */
+ uint32_t dim_max[ODP_ML_MAX_DIMS];
+
+ /** Dimension name
+ *
+ * Name of each ('num_dim') dimension as a null terminated string. Null string is used if
+ * a dimension does not have a name. Maximum string length is #ODP_ML_SHAPE_NAME_LEN
+ * including the null character.
+ *
+ * For example, an NCHW tensor could have dimensions named as:
+ * dim_name = {"N", "C", "H", "W"}
+ */
+ char dim_name[ODP_ML_MAX_DIMS][ODP_ML_SHAPE_NAME_LEN];
+
+} odp_ml_shape_info_t;
+
+/** Model input information */
+typedef struct odp_ml_input_info_t {
+ /** Model input name */
+ char name[ODP_ML_MODEL_IO_NAME_LEN];
+
+ /** Model input data type */
+ odp_ml_data_type_t data_type;
+
+ /** Size of model input data type in bytes */
+ uint32_t data_type_size;
+
+ /** Model input data shape */
+ odp_ml_shape_info_t shape;
+
+} odp_ml_input_info_t;
+
+/** Model output information */
+typedef struct odp_ml_output_info_t {
+ /** Model output name */
+ char name[ODP_ML_MODEL_IO_NAME_LEN];
+
+ /** Model output data type */
+ odp_ml_data_type_t data_type;
+
+ /** Size of model output data type in bytes */
+ uint32_t data_type_size;
+
+ /** Model output data shape */
+ odp_ml_shape_info_t shape;
+
+} odp_ml_output_info_t;
+
+/** Model information */
+typedef struct odp_ml_model_info_t {
+ /** Model name */
+ char name[ODP_ML_MODEL_NAME_LEN];
+
+ /**
+ * Model version number
+ *
+ * Version number of the model binary. The number changes when the model is modified
+ * in any way.
+ */
+ uint64_t model_version;
+
+ /**
+ * Model interface version number
+ *
+ * The model interface version number changes only when model input or output data
+ * format is modified. Data formats are the same for two model versions that have
+ * the same interface version number.
+ */
+ uint64_t interface_version;
+
+ /** Model index assigned by the implementation */
+ uint32_t index;
+
+ /** Number of model inputs */
+ uint32_t num_inputs;
+
+ /** Number of model outputs */
+ uint32_t num_outputs;
+
+} odp_ml_model_info_t;
+
+/**
+ * Model input / output data format
+ */
+typedef struct odp_ml_data_format_t {
+ /** Model input / output data type */
+ odp_ml_data_type_t data_type;
+
+ /** Size of data type in bytes */
+ uint32_t data_type_size;
+
+ /** Model input / output data shape */
+ odp_ml_shape_info_t shape;
+
+} odp_ml_data_format_t;
+
+/**
+ * Machine learning model parameters
+ *
+ * Use odp_ml_model_param_init() to initialize the structure to its default values.
+ */
+typedef struct odp_ml_model_param_t {
+ /**
+ * Model binary
+ *
+ * Points to model binary stored into a memory buffer. Model format is
+ * implementation specific. */
+ void *model;
+
+ /** Size of the model binary in bytes */
+ uint64_t size;
+
+ /**
+ * Maximum completion identifier value
+ *
+ * When application uses asynchronous poll mode (#ODP_ML_COMPL_MODE_POLL) operations with
+ * the model, it will choose completion identifier values between 0 and this value. Valid
+ * values range from 0 to max_compl_id capability. The default value is zero.
+ */
+ uint32_t max_compl_id;
+
+ /**
+ * Enable / disable extra statistics counters
+ *
+ * Extra statistics may be read with odp_ml_model_extra_stats() when enabled. Statistics
+ * are disabled by default.
+ */
+ odp_bool_t extra_stat_enable;
+
+ /**
+ * Extra model information
+ *
+ * When model metadata misses some details of model input / output data format, user can
+ * pass those with this structure. When 'num_inputs' / 'num_outputs' is non-zero, data
+ * format of all model inputs / outputs are overridden by the provided values. Values are
+ * ignored when 'num_inputs' / 'num_outputs' is zero.
+ */
+ struct {
+ /**
+ * Number of model inputs
+ *
+ * Number of model inputs and elements in 'input_format' array. When non-zero,
+ * the value must match the number of inputs defined in model metadata. The default
+ * value is 0.
+ */
+ uint32_t num_inputs;
+
+ /**
+ * Number of model outputs
+ *
+ * Number of model outputs and elements in 'output_format' array. When non-zero,
+ * the value must match the number of outputs defined in model metadata. The default
+ * value is 0.
+ */
+ uint32_t num_outputs;
+
+ /**
+ * Model input data format array
+ *
+ * Points to an array of data formats. The array has 'num_inputs' elements. Inputs
+ * are defined in the same order they are listed in model metadata.
+ * An odp_ml_model_create() call copies these values. The default value is NULL.
+ */
+ const odp_ml_data_format_t *input_format;
+
+ /**
+ * Model output data format array
+ *
+ * Points to an array of data formats. The array has 'num_outputs' elements. Outputs
+ * are defined in the same order they are listed in model metadata.
+ * An odp_ml_model_create() call copies these values. The default value is NULL.
+ */
+ const odp_ml_data_format_t *output_format;
+
+ } extra_info;
+
+ /**
+ * ODP implementation specific extra parameters
+ *
+ * See ODP implementation documentation for details about extra parameter usage. For
+ * example, extra parameters may give hints about HW resource usage with the model to be
+ * created. An odp_ml_model_create() call copies these parameter values. When NULL, all
+ * extra parameters are set to their default values. The default value is NULL.
+ */
+ const odp_ml_model_extra_param_t *extra_param;
+
+} odp_ml_model_param_t;
+
+/** Results of model run operation */
+typedef struct odp_ml_run_result_t {
+ /** Model run error code
+ *
+ * Zero when model run completed successfully. Otherwise, error code contains
+ * an implementation specific value.
+ */
+ uint64_t error_code;
+
+ /** User context pointer value from odp_ml_compl_param_t */
+ void *user_ptr;
+
+} odp_ml_run_result_t;
+
+/** Result of model load / unload operation */
+typedef struct odp_ml_load_result_t {
+ /** Model load / unload error code
+ *
+ * Zero when model load / unload completed successfully. Otherwise, error code contains
+ * an implementation specific value.
+ */
+ uint64_t error_code;
+
+ /** User context pointer value from odp_ml_compl_param_t */
+ void *user_ptr;
+
+} odp_ml_load_result_t;
+
+/**
+ * ML completion parameters
+ *
+ * Use odp_ml_compl_param_init() to initialize the structure to its default values.
+ */
+typedef struct odp_ml_compl_param_t {
+ /**
+ * Completion mode
+ *
+ * The selected completion mode defines which other parameters are used. When
+ * #ODP_ML_COMPL_MODE_EVENT mode is selected, 'event' and 'queue' must have valid values
+ * but value of 'compl_id' is ignored, or vice versa when #ODP_ML_COMPL_MODE_POLL mode is
+ * selected.
+ */
+ odp_ml_compl_mode_t mode;
+
+ /**
+ * Completion event
+ *
+ * Event to be enqueued by ML offload to the completion queue when ML operation
+ * is complete. Event type must be ODP_EVENT_ML_COMPL. ML offload sets the subtype of
+ * the event to ODP_EVENT_ML_COMPL_LOAD or ODP_EVENT_ML_COMPL_RUN based on the completed
+ * operation.
+ */
+ odp_event_t event;
+
+ /**
+ * Completion queue
+ *
+ * Destination queue for the completion event.
+ */
+ odp_queue_t queue;
+
+ /**
+ * Completion identifier
+ *
+ * When completion mode is #ODP_ML_COMPL_MODE_POLL, ML operation completion status is
+ * reported through this completion identifier. The value passed here is used in
+ * a following status call to check model load, unload, or inference completion
+ * (see e.g. odp_ml_model_load_status()).
+ *
+ * Application selects a value between 0 and max_compl_id defined in model creation
+ * parameters (see odp_ml_model_param_t). Only single ML operation (per model) may be
+ * started with the same identifier value at a time. A value may be reused for the next
+ * ML operation only after the previous operation is complete.
+ */
+ uint32_t compl_id;
+
+ /**
+ * User defined context pointer
+ *
+ * ODP implementation does not refer to the pointer, but just copies it to the result.
+ * For example, application may use this pointer to link a received completion event
+ * to the originating model run request and its input / output data. The default value
+ * is NULL.
+ */
+ void *user_ptr;
+
+} odp_ml_compl_param_t;
+
+/** Model input / output data segment */
+typedef struct odp_ml_data_seg_t {
+ /** Segment start address */
+ void *addr;
+
+ /** Segment size in bytes */
+ uint64_t size;
+
+} odp_ml_data_seg_t;
+
+/** Model input / output data for a model inference run */
+typedef struct odp_ml_data_t {
+ /**
+ * Number of input data segments
+ *
+ * Number of elements in 'input_seg' array (at least one per input).
+ */
+ uint32_t num_input_seg;
+
+ /**
+ * Number of output data segments
+ *
+ * Number of elements in 'output_seg' array (at least one per output).
+ */
+ uint32_t num_output_seg;
+
+ /**
+ * Model input data segments
+ *
+ * Points to an array of data segment descriptors for model input data. Each segment
+ * (odp_ml_data_seg_t) specifies data for one input only. Multiple consecutive segments may
+ * be used to specify data for the same input. Sum of those segment sizes must match data
+ * size of the input. Inputs are defined in the same order which odp_ml_model_input_info()
+ * reports those.
+ *
+ * Input data segments may overlap in memory.
+ */
+ odp_ml_data_seg_t *input_seg;
+
+ /**
+ * Model output data segments
+ *
+ * Points to an array of data segment descriptors for model output data. Each segment
+ * (odp_ml_data_seg_t) specifies data buffer space for one output only. Multiple
+ * consecutive segments may be used to specify buffer space for the same output.
+ * Sum of those segment sizes must match data size of the output. Outputs are defined
+ * in the same order which odp_ml_model_output_info() reports those.
+ *
+ * An output data segment must not overlap with any other (input or output) segment
+ * in memory.
+ */
+ odp_ml_data_seg_t *output_seg;
+
+} odp_ml_data_t;
+
+/**
+ * Parameters for model run
+ *
+ * Use odp_ml_run_param_init() to initialize the structure to its default values.
+ */
+typedef struct odp_ml_run_param_t {
+ /**
+ * Batch size
+ *
+ * Batch size for all model inputs and outputs that have #ODP_ML_SHAPE_BATCH shape type.
+ * The default value is 0.
+ */
+ uint32_t batch_size;
+
+ /**
+ * Model run results
+ *
+ * Points to a result structure for model run result output. Results are output only
+ * in synchronous mode (#ODP_ML_COMPL_MODE_SYNC). The pointer value is ignored in
+ * asynchronous modes. Use NULL when results are not required. The default value is NULL.
+ */
+ odp_ml_run_result_t *result;
+
+} odp_ml_run_param_t;
+
+/**
+ * ML extra statistics counter information
+ */
+typedef struct odp_ml_extra_stat_info_t {
+ /** Name of the statistics counter */
+ char name[ODP_ML_EXTRA_STAT_NAME_LEN];
+
+} odp_ml_extra_stat_info_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/packet.h b/include/odp/api/spec/packet.h
index 79d8773e9..7f6c732ee 100644
--- a/include/odp/api/spec/packet.h
+++ b/include/odp/api/spec/packet.h
@@ -1,87 +1,47 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
-
/**
* @file
*
* ODP packet descriptor
*/
-#ifndef ODP_API_PACKET_H_
-#define ODP_API_PACKET_H_
+#ifndef ODP_API_SPEC_PACKET_H_
+#define ODP_API_SPEC_PACKET_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-#include <odp/api/time.h>
+#include <odp/api/event_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/packet_io_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/proto_stats_types.h>
+#include <odp/api/std_types.h>
+#include <odp/api/time_types.h>
-/** @defgroup odp_packet ODP PACKET
- * Operations on a packet.
+/** @addtogroup odp_packet
+ * Packet event metadata and operations.
* @{
*/
/**
- * @typedef odp_packet_t
- * ODP packet
- */
-
-/**
- * @def ODP_PACKET_INVALID
- * Invalid packet
- */
-
-/**
- * @def ODP_PACKET_OFFSET_INVALID
- * Invalid packet offset
- */
-
-/**
- * @typedef odp_packet_seg_t
- * ODP packet segment
- */
-
-/**
- * @def ODP_PACKET_SEG_INVALID
- * Invalid packet segment
- */
-
- /**
- * @typedef odp_packet_color_t
- * Color of packet for shaper/drop processing
- */
-
- /**
- * @def ODP_PACKET_GREEN
- * Packet is green
- */
-
- /**
- * @def ODP_PACKET_YELLOW
- * Packet is yellow
- */
-
- /**
- * @def ODP_PACKET_RED
- * Packet is red
- */
-
-/**
- * Packet API data range specifier
+ * Event subtype of a packet
+ *
+ * Returns the subtype of a packet event. Subtype tells if the packet contains
+ * only basic metadata (ODP_EVENT_PACKET_BASIC) or in addition to that some
+ * specific metadata (e.g. ODP_EVENT_PACKET_CRYPTO or ODP_EVENT_PACKET_IPSEC).
+ *
+ * @param packet Packet handle
+ *
+ * @return Packet subtype
*/
-typedef struct odp_packet_data_range {
- /** Offset from beginning of packet */
- uint32_t offset;
-
- /** Length of data to operate on */
- uint32_t length;
-
-} odp_packet_data_range_t;
+odp_event_subtype_t odp_packet_subtype(odp_packet_t packet);
/*
*
@@ -150,15 +110,25 @@ void odp_packet_free(odp_packet_t pkt);
void odp_packet_free_multi(const odp_packet_t pkt[], int num);
/**
+ * Free multiple packets to the same pool
+ *
+ * Otherwise like odp_packet_free_multi(), but all packets must be from the
+ * same originating pool.
+ *
+ * @param pkt Array of packet handles
+ * @param num Number of packets to free
+ */
+void odp_packet_free_sp(const odp_packet_t pkt[], int num);
+
+/**
* Reset packet
*
* Resets all packet metadata to their default values. Packet length is used
* to initialize pointers and lengths. It must be less than the total buffer
- * length of the packet minus the default headroom length. Packet is not
- * modified on failure.
+ * length of the packet. Packet is not modified on failure.
*
* @param pkt Packet handle
- * @param len Packet data length
+ * @param len Packet data length (1 ... odp_packet_buf_len())
*
* @retval 0 on success
* @retval <0 on failure
@@ -181,6 +151,18 @@ int odp_packet_reset(odp_packet_t pkt, uint32_t len);
odp_packet_t odp_packet_from_event(odp_event_t ev);
/**
+ * Convert multiple packet events to packet handles
+ *
+ * All events must be of type ODP_EVENT_PACKET.
+ *
+ * @param[out] pkt Packet handle array for output
+ * @param ev Array of event handles to convert
+ * @param num Number of packets and events
+ */
+void odp_packet_from_event_multi(odp_packet_t pkt[], const odp_event_t ev[],
+ int num);
+
+/**
* Convert packet handle to event
*
* @param pkt Packet handle
@@ -189,6 +171,53 @@ odp_packet_t odp_packet_from_event(odp_event_t ev);
*/
odp_event_t odp_packet_to_event(odp_packet_t pkt);
+/**
+ * Convert multiple packet handles to events
+ *
+ * @param pkt Array of packet handles to convert
+ * @param[out] ev Event handle array for output
+ * @param num Number of packets and events
+ */
+void odp_packet_to_event_multi(const odp_packet_t pkt[], odp_event_t ev[],
+ int num);
+
+/**
+ * Get information about successful reassembly offload that has happened
+ *
+ * This function may be called only if the reassembly status of a packet
+ * is ODP_PACKET_REASS_COMPLETE.
+ *
+ * @param pkt Completely reassembled packet.
+ * @param[out] info Pointer to the info structure to be filled
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_packet_reass_info(odp_packet_t pkt, odp_packet_reass_info_t *info);
+
+/**
+ * Get partial reassembly state from a packet
+ *
+ * In case of incomplete reassembly, a packet carries information on
+ * the time already used for the reassembly attempt and one or more
+ * fragments. The fragments are not necessarily the original received
+ * fragments but may be partially reassembled parts of the packet.
+ *
+ * This function may be called only if the reassembly status of a packet
+ * is ODP_PACKET_REASS_INCOMPLETE.
+ *
+ * @param pkt Incompletely reassembled packet. The packet will
+ * be consumed if the function succeeds.
+ * @param[out] frags Packet handle array for output. The size of this array must
+ * be at least `odp_reass_config_t::max_num_frags`.
+ * @param[out] res Pointer to result structure
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_packet_reass_partial_state(odp_packet_t pkt, odp_packet_t frags[],
+ odp_packet_reass_partial_state_t *res);
+
/*
*
* Pointers and lengths
@@ -227,43 +256,71 @@ uint32_t odp_packet_buf_len(odp_packet_t pkt);
/**
* Packet data pointer
*
- * Returns the current packet data pointer. When a packet is received
- * from packet input, this points to the first byte of the received
- * packet. Packet level offsets are calculated relative to this position.
+ * Returns pointer to the first byte of packet data. When packet is segmented,
+ * only a portion of packet data follows the pointer. When unsure, use e.g.
+ * odp_packet_seg_len() to check the data length following the pointer. Packet
+ * level offsets are calculated relative to this position.
*
- * User can adjust the data pointer with head_push/head_pull (does not modify
- * segmentation) and add_data/rem_data calls (may modify segmentation).
+ * When a packet is received from packet input, this points to the first byte
+ * of the received packet. Pool configuration parameters may be used to ensure
+ * that the first packet segment contains all/most of the data relevant to the
+ * application.
+ *
+ * User can adjust the data pointer with e.g. push_head/pull_head (does not
+ * modify segmentation) and extend_head/trunc_head (may modify segmentation)
+ * calls.
*
* @param pkt Packet handle
*
* @return Pointer to the packet data
*
- * @see odp_packet_l2_ptr(), odp_packet_seg_len()
+ * @see odp_packet_seg_len(), odp_packet_push_head(), odp_packet_extend_head()
*/
void *odp_packet_data(odp_packet_t pkt);
/**
- * Packet segment data length
+ * Packet data length following the data pointer
*
- * Returns number of data bytes following the current data pointer
- * (odp_packet_data()) location in the segment.
+ * Returns number of data bytes (in the segment) following the current data
+ * pointer position. When unsure, use this function to check how many bytes
+ * can be accessed linearly after data pointer (odp_packet_data()). This
+ * equals to odp_packet_len() for single segment packets.
*
* @param pkt Packet handle
*
- * @return Segment data length in bytes (pointed by odp_packet_data())
+ * @return Segment data length in bytes following odp_packet_data()
*
* @see odp_packet_data()
*/
uint32_t odp_packet_seg_len(odp_packet_t pkt);
/**
+ * Packet data pointer with segment length
+ *
+ * Returns both data pointer and number of data bytes (in the segment)
+ * following it. This is equivalent to calling odp_packet_data() and
+ * odp_packet_seg_len().
+ *
+ * @param pkt Packet handle
+ * @param[out] seg_len Pointer to output segment length
+ *
+ * @return Pointer to the packet data
+ *
+ * @see odp_packet_data(), odp_packet_seg_len()
+ */
+void *odp_packet_data_seg_len(odp_packet_t pkt, uint32_t *seg_len);
+
+/**
* Packet data length
*
- * Returns sum of data lengths over all packet segments.
+ * Returns total data length over all packet segments. This equals the sum of
+ * segment level data lengths (odp_packet_seg_data_len()).
*
* @param pkt Packet handle
*
* @return Packet data length
+ *
+ * @see odp_packet_seg_len(), odp_packet_data(), odp_packet_seg_data_len()
*/
uint32_t odp_packet_len(odp_packet_t pkt);
@@ -510,7 +567,8 @@ int odp_packet_extend_head(odp_packet_t *pkt, uint32_t len, void **data_ptr,
*
* @param[in, out] pkt Pointer to packet handle. A successful operation outputs
* the new packet handle.
- * @param len Number of bytes to truncate the head (0 ... packet_len)
+ * @param len Number of bytes to truncate the head
+ * (0 ... packet_len - 1)
* @param[out] data_ptr Pointer to output the new data pointer.
* Ignored when NULL.
* @param[out] seg_len Pointer to output segment length at 'data_ptr' above.
@@ -583,7 +641,8 @@ int odp_packet_extend_tail(odp_packet_t *pkt, uint32_t len, void **data_ptr,
*
* @param[in, out] pkt Pointer to packet handle. A successful operation outputs
* the new packet handle.
- * @param len Number of bytes to truncate the tail (0 ... packet_len)
+ * @param len Number of bytes to truncate the tail
+ * (0 ... packet_len - 1)
* @param[out] tail_ptr Pointer to output the new tail pointer.
* Ignored when NULL.
* @param[out] tailroom Pointer to output the new tailroom. Ignored when NULL.
@@ -647,7 +706,9 @@ int odp_packet_add_data(odp_packet_t *pkt, uint32_t offset, uint32_t len);
* @param[in, out] pkt Pointer to packet handle. A successful operation outputs
* the new packet handle.
* @param offset Byte offset into the packet
- * @param len Number of bytes to remove from the offset
+ * @param len Number of bytes to remove from the offset. When offset
+ * is zero: 0 ... packet_len - 1 bytes, otherwise
+ * 0 ... packet_len - offset bytes.
*
* @retval 0 Operation successful, old pointers remain valid
* @retval >0 Operation successful, old pointers need to be updated
@@ -850,6 +911,7 @@ int odp_packet_concat(odp_packet_t *dst, odp_packet_t src);
* @param[in, out] pkt Pointer to packet handle. A successful operation
* outputs a new packet handle for the head packet.
* @param len Data length remaining in the head packet
+ * (1 ... packet_len - 1)
* @param tail Pointer to output the tail packet handle
*
* @retval 0 Operation successful, old pointers remain valid
@@ -858,6 +920,135 @@ int odp_packet_concat(odp_packet_t *dst, odp_packet_t src);
*/
int odp_packet_split(odp_packet_t *pkt, uint32_t len, odp_packet_t *tail);
+/**
+ * Packet buffer head pointer
+ *
+ * Packet buffer start address. Buffer level headroom starts from here. For the first
+ * packet buffer of a packet this is equivalent to odp_packet_head().
+ *
+ * @param pkt_buf Packet buffer
+ *
+ * @return Packet buffer head pointer
+ */
+void *odp_packet_buf_head(odp_packet_buf_t pkt_buf);
+
+/**
+ * Packet buffer size in bytes
+ *
+ * Packet buffer size is calculated from the buffer head pointer (see odp_packet_buf_head()).
+ * It contains all buffer level headroom, data, and tailroom. For a single segmented packet this is
+ * equivalent to odp_packet_buf_len().
+ *
+ * @param pkt_buf Packet buffer
+ *
+ * @return Packet buffer size
+ */
+uint32_t odp_packet_buf_size(odp_packet_buf_t pkt_buf);
+
+/**
+ * Packet buffer data offset
+ *
+ * Offset from the buffer head pointer to the first byte of packet data in the packet buffer.
+ * Valid values range from 0 to buf_size - 1. For the first packet buffer of a packet
+ * this is equivalent to odp_packet_headroom().
+ *
+ * @param pkt_buf Packet buffer
+ *
+ * @return Packet buffer data offset
+ */
+uint32_t odp_packet_buf_data_offset(odp_packet_buf_t pkt_buf);
+
+/**
+ * Packet buffer data length in bytes
+ *
+ * Packet buffer contains this many bytes of packet data. Valid values range from 1 to
+ * buf_size - data_offset. For the first packet buffer of a packet this is equivalent to
+ * odp_packet_seg_len().
+ *
+ * @param pkt_buf Packet buffer
+ *
+ * @return Packet buffer data length
+ */
+uint32_t odp_packet_buf_data_len(odp_packet_buf_t pkt_buf);
+
+/**
+ * Packet buffer data set
+ *
+ * Update packet data start offset and length in the packet buffer. Valid offset values range
+ * from 0 to buf_size - 1. Valid length values range from 1 to buf_size - data_offset.
+ *
+ * @param pkt_buf Packet buffer
+ * @param data_offset Packet buffer data offset in bytes (from the buffer head pointer)
+ * @param data_len Packet buffer data length in bytes
+ */
+void odp_packet_buf_data_set(odp_packet_buf_t pkt_buf, uint32_t data_offset, uint32_t data_len);
+
+/**
+ * Convert packet buffer head pointer to handle
+ *
+ * Converts a packet buffer head pointer (from a previous odp_packet_buf_head() call) to a packet
+ * buffer handle. This allows an application to save memory as it can store only buffer pointers
+ * (instead of pointers and handles) and convert those to handles when needed. This conversion
+ * may be done only for packet buffers that are not part of any packet (i.e. buffers between
+ * odp_packet_disassemble() and odp_packet_reassemble() calls).
+ *
+ * This call can be used only for packets of an external memory pool (see odp_pool_ext_create()).
+ *
+ * @param pool Pool from which the packet buffer (disassembled packet) originate from
+ * @param head Head pointer
+ *
+ * @return Packet buffer handle on success
+ * @retval ODP_PACKET_BUF_INVALID on failure
+ */
+odp_packet_buf_t odp_packet_buf_from_head(odp_pool_t pool, void *head);
+
+/**
+ * Disassemble packet into packet buffers
+ *
+ * Breaks up a packet into a list of packet buffers. Outputs a packet buffer handle for each
+ * segment of the packet (see odp_packet_num_segs()). After a successful operation the packet
+ * handle must not be referenced anymore. Packet buffers are reassembled into a new packet (or
+ * several new packets) with a later odp_packet_reassemble() call(s). All packet buffers must be
+ * reassembled into a packet and freed into the originating pool before the pool is destroyed.
+ *
+ * This call can be used only for packets of an external memory pool (see odp_pool_ext_create()).
+ *
+ * @param pkt Packet to be disassembled
+ * @param[out] pkt_buf Packet buffer handle array for output
+ * @param num Number of elements in packet buffer handle array. Must be equal to or
+ * larger than number of segments in the packet.
+ *
+ * @return Number of handles written (equals the number of segments in the packet)
+ * @retval 0 on failure
+ */
+uint32_t odp_packet_disassemble(odp_packet_t pkt, odp_packet_buf_t pkt_buf[], uint32_t num);
+
+/**
+ * Reassemble packet from packet buffers
+ *
+ * Forms a new packet from packet buffers of a previous odp_packet_disassemble() call(s). Packet
+ * buffers from different disassembled packets may be used, but all buffers must be from packets of
+ * the same pool. Packet pool capability 'max_segs_per_pkt' defines the maximum number of
+ * packet buffers that can be reassembled to form a new packet.
+ *
+ * Application may use odp_packet_buf_data_set() to adjust data_offset and data_len values
+ * in each packet buffer to match the current packet data placement. The operation
+ * maintains packet data content and position. Each buffer becomes a segment in the new packet.
+ * Packet metadata related to data length and position are set according data layout
+ * in the buffers. All other packet metadata are set to their default values. After a successful
+ * operation packet buffer handles must not be referenced anymore.
+ *
+ * This call can be used only for packets of an external memory pool (see odp_pool_ext_create()).
+ *
+ * @param pool Pool from which all packet buffers (disassembled packets) originate from
+ * @param pkt_buf Packet buffers to form a new packet
+ * @param num Number of packet buffers. Must not exceed max_segs_per_pkt pool capability.
+ *
+ * @return Handle of the newly formed packet
+ * @retval ODP_PACKET_INVALID on failure
+ */
+odp_packet_t odp_packet_reassemble(odp_pool_t pool, odp_packet_buf_t pkt_buf[], uint32_t num);
+
/*
*
* References
@@ -904,7 +1095,7 @@ odp_packet_t odp_packet_ref_static(odp_packet_t pkt);
* dynamic references must not be mixed. Results are undefined if these
* restrictions are not observed.
*
- * The packet handle 'pkt' may itself by a (dynamic) reference to a packet.
+ * The packet handle 'pkt' may itself be a (dynamic) reference to a packet.
*
* If the caller does not intend to modify either the packet or the new
* reference to it, odp_packet_ref_static() may be used to create
@@ -931,7 +1122,7 @@ odp_packet_t odp_packet_ref(odp_packet_t pkt, uint32_t offset);
* packet consists metadata and data of the 'hdr' packet, followed by the
* shared part of packet 'pkt'.
*
- * The packet handle ('pkt') may itself by a (dynamic) reference to a packet,
+ * The packet handle ('pkt') may itself be a (dynamic) reference to a packet,
* but the header packet handle ('hdr') must be unique. Both packets must be
* have been allocated from the same pool and the handles must not refer to
* the same packet. Results are undefined if these restrictions are not
@@ -956,25 +1147,6 @@ odp_packet_t odp_packet_ref_pkt(odp_packet_t pkt, uint32_t offset,
odp_packet_t hdr);
/**
- * Packet unshared data length
- *
- * When a packet has multiple references, packet data is divided into two
- * parts: unshared and shared. The unshared part always precedes the shared
- * part. This call returns number of bytes in the unshared part. When a
- * packet has only a single reference (see odp_packet_has_ref()), all packet
- * data is unshared and unshared length equals the packet length
- * (odp_packet_len()).
- *
- * Application may modify only the unshared part, the rest of the packet data
- * must be treated as read only.
- *
- * @param pkt Packet handle
- *
- * @return Packet unshared data length
- */
-uint32_t odp_packet_unshared_len(odp_packet_t pkt);
-
-/**
* Test if packet has multiple references
*
* A packet that has multiple references share data with other packets. In case
@@ -1143,6 +1315,77 @@ int odp_packet_move_data(odp_packet_t pkt, uint32_t dst_offset,
*/
/**
+ * Parse packet
+ *
+ * Parse protocol headers in packet data and update layer/protocol specific
+ * metadata (e.g. offsets, errors, protocols, checksum statuses, etc). Parsing
+ * starts at 'offset', which is the first header byte of protocol 'param.proto'.
+ * Parameter 'param.last_layer' defines the last layer application requests
+ * to check. Use ODP_PROTO_LAYER_ALL for all layers. A successful operation
+ * sets (or resets) packet metadata for all layers from the layer of
+ * 'param.proto' to the application defined last layer. In addition, offset
+ * (and pointer) to the next layer is set. Other layer/protocol specific
+ * metadata have undefined values. When operation fails, all layer/protocol
+ * specific metadata have undefined values.
+ *
+ * @param pkt Packet handle
+ * @param offset Byte offset into the packet
+ * @param param Parse parameters. Proto and last_layer fields must be set.
+ * Clear all check bits that are not used.
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
+ const odp_packet_parse_param_t *param);
+
+/**
+ * Parse multiple packets
+ *
+ * Otherwise like odp_packet_parse(), but parses multiple packets. Packets may
+ * have unique offsets, but must start with the same protocol. The same
+ * parse parameters are applied to all packets.
+ *
+ * @param pkt Packet handle array
+ * @param offset Byte offsets into the packets
+ * @param num Number of packets and offsets
+ * @param param Parse parameters. Proto and last_layer fields must be set.
+ * Clear all check bits that are not used.
+ *
+ * @return Number of packets parsed successfully (0 ... num)
+ * @retval <0 on failure
+ */
+int odp_packet_parse_multi(const odp_packet_t pkt[], const uint32_t offset[],
+ int num, const odp_packet_parse_param_t *param);
+
+/**
+ * Read parse results
+ *
+ * Read out the most commonly used packet parse results. The same information is
+ * available through individual function calls, but this call may be more
+ * efficient when reading multiple results from a packet.
+ *
+ * @param pkt Packet handle
+ * @param[out] result Pointer for parse result output
+ */
+void odp_packet_parse_result(odp_packet_t pkt,
+ odp_packet_parse_result_t *result);
+
+/**
+ * Read parse results from multiple packets
+ *
+ * Otherwise same functionality as odp_packet_parse_result() but handles
+ * multiple packets.
+ *
+ * @param pkt Packet handle array
+ * @param[out] result Parse result array for output
+ * @param num Number of packets and results
+ */
+void odp_packet_parse_result_multi(const odp_packet_t pkt[],
+ odp_packet_parse_result_t *result[],
+ int num);
+
+/**
* Packet pool
*
* Returns handle to the packet pool where the packet was allocated from.
@@ -1167,6 +1410,22 @@ odp_pool_t odp_packet_pool(odp_packet_t pkt);
odp_pktio_t odp_packet_input(odp_packet_t pkt);
/**
+ * Set packet input interface
+ *
+ * Set packet input interface to a valid packet IO handle or ODP_PKTIO_INVALID.
+ * An application may use this for testing or other purposes, when perception
+ * of the packet input interface need to be changed. The call updates both
+ * input interface handle (odp_packet_input()) and index
+ * (odp_packet_input_index()).
+ *
+ * @param pkt Packet handle
+ * @param pktio Handle to a valid packet input interface or ODP_PKTIO_INVALID.
+ * ODP_PKTIO_INVALID indicates that the packet was not received by
+ * any packet IO interface.
+ */
+void odp_packet_input_set(odp_packet_t pkt, odp_pktio_t pktio);
+
+/**
* Packet input interface index
*
* Returns the index of the packet I/O interface that received the packet, or
@@ -1182,7 +1441,10 @@ int odp_packet_input_index(odp_packet_t pkt);
/**
* User context pointer
*
- * Return previously stored user context pointer.
+ * Return previously stored user context pointer. If not otherwise documented,
+ * the pointer value is maintained over packet manipulating operations.
+ * Implementation initializes the pointer value to NULL during new packet
+ * creation (e.g. alloc and packet input) and reset.
*
* @param pkt Packet handle
*
@@ -1198,10 +1460,10 @@ void *odp_packet_user_ptr(odp_packet_t pkt);
* value of type intptr_t. ODP may use the pointer for data prefetching, but
* must ignore any invalid addresses.
*
- * @param pkt Packet handle
- * @param ctx User context pointer
+ * @param pkt Packet handle
+ * @param user_ptr User context pointer
*/
-void odp_packet_user_ptr_set(odp_packet_t pkt, const void *ctx);
+void odp_packet_user_ptr_set(odp_packet_t pkt, const void *user_ptr);
/**
* User area address
@@ -1228,17 +1490,43 @@ void *odp_packet_user_area(odp_packet_t pkt);
uint32_t odp_packet_user_area_size(odp_packet_t pkt);
/**
+ * Check user flag
+ *
+ * Implementation clears user flag during new packet creation (e.g. alloc and packet input)
+ * and reset. User may set the flag with odp_packet_user_flag_set(). Implementation never
+ * sets the flag, only clears it. The flag may be useful e.g. to mark when the user area
+ * content is valid.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval 0 User flag is clear
+ * @retval >0 User flag is set
+ */
+int odp_packet_user_flag(odp_packet_t pkt);
+
+/**
+ * Set user flag
+ *
+ * Set (or clear) the user flag.
+ *
+ * @param pkt Packet handle
+ * @param val New value for the flag. Zero clears the flag, other values set the flag.
+ */
+void odp_packet_user_flag_set(odp_packet_t pkt, int val);
+
+/**
* Layer 2 start pointer
*
- * Returns pointer to the start of the layer 2 header. Optionally, outputs
- * number of data bytes in the segment following the pointer.
+ * Returns pointer to the start of layer 2. Optionally, outputs number of data
+ * bytes in the segment following the pointer. The pointer value is generated
+ * from the current layer 2 offset.
*
* @param pkt Packet handle
* @param[out] len Number of data bytes remaining in the segment (output).
* Ignored when NULL.
*
- * @return Layer 2 start pointer
- * @retval NULL packet does not contain a valid L2 header
+ * @return Layer 2 start pointer
+ * @retval NULL Layer 2 offset has not been set
*
* @see odp_packet_l2_offset(), odp_packet_l2_offset_set(), odp_packet_has_l2()
*/
@@ -1247,16 +1535,16 @@ void *odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len);
/**
* Layer 2 start offset
*
- * Returns offset to the start of the layer 2 header. The offset is calculated
- * from the current odp_packet_data() position in bytes.
- *
- * User is responsible to update the offset when modifying the packet data
- * pointer position.
+ * Returns offset to the start of layer 2. The offset is calculated from the
+ * current odp_packet_data() position in bytes. Packet parsing sets the offset
+ * according to parse configuration and layers recognized in the packet. Data
+ * start position updating functions (e.g. odp_packet_push_head()) do not modify
+ * the offset, but user sets a new value when needed.
*
* @param pkt Packet handle
*
- * @return Layer 2 start offset
- * @retval ODP_PACKET_OFFSET_INVALID packet does not contain a valid L2 header
+ * @return Layer 2 start offset
+ * @retval ODP_PACKET_OFFSET_INVALID Layer 2 offset has not been set
*
* @see odp_packet_l2_offset_set(), odp_packet_has_l2()
*/
@@ -1265,9 +1553,9 @@ uint32_t odp_packet_l2_offset(odp_packet_t pkt);
/**
* Set layer 2 start offset
*
- * Set offset to the start of the layer 2 header. The offset is calculated from
- * the current odp_packet_data() position in bytes. Offset must not exceed
- * packet data length. Packet is not modified on an error.
+ * Set offset to the start of layer 2. The offset is calculated from the current
+ * odp_packet_data() position in bytes. Offset must not exceed packet data
+ * length. Offset is not modified on an error.
*
* @param pkt Packet handle
* @param offset Layer 2 start offset (0 ... odp_packet_len()-1)
@@ -1280,15 +1568,16 @@ int odp_packet_l2_offset_set(odp_packet_t pkt, uint32_t offset);
/**
* Layer 3 start pointer
*
- * Returns pointer to the start of the layer 3 header. Optionally, outputs
- * number of data bytes in the segment following the pointer.
+ * Returns pointer to the start of layer 3. Optionally, outputs number of data
+ * bytes in the segment following the pointer. The pointer value is generated
+ * from the current layer 3 offset.
*
* @param pkt Packet handle
* @param[out] len Number of data bytes remaining in the segment (output).
* Ignored when NULL.
*
- * @return Layer 3 start pointer
- * @retval NULL packet does not contain a valid L3 header
+ * @return Layer 3 start pointer
+ * @retval NULL Layer 3 offset has not been set
*
* @see odp_packet_l3_offset(), odp_packet_l3_offset_set(), odp_packet_has_l3()
*/
@@ -1297,16 +1586,16 @@ void *odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len);
/**
* Layer 3 start offset
*
- * Returns offset to the start of the layer 3 header. The offset is calculated
- * from the current odp_packet_data() position in bytes.
- *
- * User is responsible to update the offset when modifying the packet data
- * pointer position.
+ * Returns offset to the start of layer 3. The offset is calculated from the
+ * current odp_packet_data() position in bytes. Packet parsing sets the offset
+ * according to parse configuration and layers recognized in the packet. Data
+ * start position updating functions (e.g. odp_packet_push_head()) do not modify
+ * the offset, but user sets a new value when needed.
*
* @param pkt Packet handle
*
- * @return Layer 3 start offset, or ODP_PACKET_OFFSET_INVALID when packet does
- * not contain a valid L3 header.
+ * @return Layer 3 start offset
+ * @retval ODP_PACKET_OFFSET_INVALID Layer 3 offset has not been set
*
* @see odp_packet_l3_offset_set(), odp_packet_has_l3()
*/
@@ -1315,9 +1604,9 @@ uint32_t odp_packet_l3_offset(odp_packet_t pkt);
/**
* Set layer 3 start offset
*
- * Set offset to the start of the layer 3 header. The offset is calculated from
- * the current odp_packet_data() position in bytes. Offset must not exceed
- * packet data length. Packet is not modified on an error.
+ * Set offset to the start of layer 3. The offset is calculated from the current
+ * odp_packet_data() position in bytes. Offset must not exceed packet data
+ * length. Offset is not modified on an error.
*
* @param pkt Packet handle
* @param offset Layer 3 start offset (0 ... odp_packet_len()-1)
@@ -1330,15 +1619,16 @@ int odp_packet_l3_offset_set(odp_packet_t pkt, uint32_t offset);
/**
* Layer 4 start pointer
*
- * Returns pointer to the start of the layer 4 header. Optionally, outputs
- * number of data bytes in the segment following the pointer.
+ * Returns pointer to the start of layer 4. Optionally, outputs number of data
+ * bytes in the segment following the pointer. The pointer value is generated
+ * from the current layer 4 offset.
*
* @param pkt Packet handle
* @param[out] len Number of data bytes remaining in the segment (output).
* Ignored when NULL.
*
- * @return Layer 4 start pointer
- * @retval NULL packet does not contain a valid L4 header
+ * @return Layer 4 start pointer
+ * @retval NULL Layer 4 offset has not been set
*
* @see odp_packet_l4_offset(), odp_packet_l4_offset_set(), odp_packet_has_l4()
*/
@@ -1347,16 +1637,16 @@ void *odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len);
/**
* Layer 4 start offset
*
- * Returns offset to the start of the layer 4 header. The offset is calculated
- * from the current odp_packet_data() position in bytes.
- *
- * User is responsible to update the offset when modifying the packet data
- * pointer position.
+ * Returns offset to the start of layer 4. The offset is calculated from the
+ * current odp_packet_data() position in bytes. Packet parsing sets the offset
+ * according to parse configuration and layers recognized in the packet. Data
+ * start position updating functions (e.g. odp_packet_push_head()) do not modify
+ * the offset, but user sets a new value when needed.
*
* @param pkt Packet handle
*
- * @return Layer 4 start offset
- * @retval ODP_PACKET_OFFSET_INVALID packet does not contain a valid L4 header
+ * @return Layer 4 start offset
+ * @retval ODP_PACKET_OFFSET_INVALID Layer 4 offset has not been set
*
* @see odp_packet_l4_offset_set(), odp_packet_has_l4()
*/
@@ -1365,9 +1655,9 @@ uint32_t odp_packet_l4_offset(odp_packet_t pkt);
/**
* Set layer 4 start offset
*
- * Set offset to the start of the layer 4 header. The offset is calculated from
- * the current odp_packet_data() position in bytes. Offset must not exceed
- * packet data length. Packet is not modified on an error.
+ * Set offset to the start of layer 4. The offset is calculated from the current
+ * odp_packet_data() position in bytes. Offset must not exceed packet data
+ * length. Offset is not modified on an error.
*
* @param pkt Packet handle
* @param offset Layer 4 start offset (0 ... odp_packet_len()-1)
@@ -1378,6 +1668,146 @@ uint32_t odp_packet_l4_offset(odp_packet_t pkt);
int odp_packet_l4_offset_set(odp_packet_t pkt, uint32_t offset);
/**
+ * Layer 2 protocol type
+ *
+ * Returns layer 2 protocol type. Initial type value is ODP_PROTO_L2_TYPE_NONE.
+ *
+ * @param pkt Packet handle
+ *
+ * @return Layer 2 protocol type
+ */
+odp_proto_l2_type_t odp_packet_l2_type(odp_packet_t pkt);
+
+/**
+ * Layer 3 protocol type
+ *
+ * Returns layer 3 protocol type. Initial type value is ODP_PROTO_L3_TYPE_NONE.
+ *
+ * In addition to protocol types specified in ODP_PROTO_L3_TYPE_* defines,
+ * the function may also return other L3 protocol types (e.g. from IEEE
+ * EtherTypes list) recognized by the parser. If protocol type is not
+ * recognized, ODP_PROTO_L3_TYPE_NONE is returned.
+ *
+ * @param pkt Packet handle
+ *
+ * @return Layer 3 protocol type
+ */
+odp_proto_l3_type_t odp_packet_l3_type(odp_packet_t pkt);
+
+/**
+ * Layer 4 protocol type
+ *
+ * Returns layer 4 protocol type. Initial type value is ODP_PROTO_L4_TYPE_NONE.
+ *
+ * In addition to protocol types specified in ODP_PROTO_L4_TYPE_* defines,
+ * the function may also return other L4 protocol types (e.g. from IANA protocol
+ * number list) recognized by the parser. If protocol type is not recognized,
+ * ODP_PROTO_L4_TYPE_NONE is returned.
+ *
+ * @param pkt Packet handle
+ *
+ * @return Layer 4 protocol type
+ */
+odp_proto_l4_type_t odp_packet_l4_type(odp_packet_t pkt);
+
+/**
+ * Layer 3 checksum check status
+ *
+ * Returns the result of the latest layer 3 checksum check done for the packet.
+ * The status tells if checksum check was attempted and the result of the
+ * attempt. It depends on packet input (or IPSEC) configuration, packet content
+ * and implementation capabilities if checksum check is attempted for a packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @return L3 checksum check status
+ */
+odp_packet_chksum_status_t odp_packet_l3_chksum_status(odp_packet_t pkt);
+
+/**
+ * Layer 4 checksum check status
+ *
+ * Returns the result of the latest layer 4 checksum check done for the packet.
+ * The status tells if checksum check was attempted and the result of the
+ * attempt. It depends on packet input (or IPSEC) configuration, packet content
+ * and implementation capabilities if checksum check is attempted for a packet.
+ *
+ * When a UDP packet does not have a checksum (e.g. checksum field of a UDP/IPv4
+ * packet is zero), checksum check result is ODP_PACKET_CHKSUM_OK.
+ *
+ * @param pkt Packet handle
+ *
+ * @return L4 checksum check status
+ */
+odp_packet_chksum_status_t odp_packet_l4_chksum_status(odp_packet_t pkt);
+
+/**
+ * Layer 3 checksum insertion override
+ *
+ * Override checksum insertion configuration per packet. This per packet setting
+ * overrides a higher level configuration for checksum insertion into a L3
+ * header during packet output processing.
+ *
+ * Calling this function is always allowed but the checksum will not be
+ * inserted if the packet is output through a pktio that does not have
+ * the relevant checksum insertion enabled.
+ *
+ * L3 type and L3 offset in packet metadata should provide valid protocol
+ * and header offset for checksum insertion purposes.
+ *
+ * @param pkt Packet handle
+ * @param insert 0: do not insert L3 checksum
+ * 1: insert L3 checksum
+ *
+ * @see odp_packet_l3_offset(), odp_packet_has_ipv4(), odp_packet_has_ipv6()
+ */
+void odp_packet_l3_chksum_insert(odp_packet_t pkt, int insert);
+
+/**
+ * Layer 4 checksum insertion override
+ *
+ * Override checksum insertion configuration per packet. This per packet setting
+ * overrides a higher level configuration for checksum insertion into a L4
+ * header during packet output processing.
+ *
+ * Calling this function is always allowed but the checksum will not be
+ * inserted if the packet is output through a pktio that does not have
+ * the relevant checksum insertion enabled.
+ *
+ * L3 type, L4 type, L3 offset and L4 offset in packet metadata should provide
+ * valid protocols and header offsets for checksum insertion purposes.
+ *
+ * @param pkt Packet handle
+ * @param insert 0: do not insert L4 checksum
+ * 1: insert L4 checksum
+ *
+ * @see odp_packet_l3_offset(), odp_packet_has_ipv4(), odp_packet_has_ipv6()
+ * @see odp_packet_l4_offset(), odp_packet_has_tcp(), odp_packet_has_udp()
+ * @see odp_packet_has_sctp()
+ */
+void odp_packet_l4_chksum_insert(odp_packet_t pkt, int insert);
+
+/**
+ * Ones' complement sum of packet data
+ *
+ * Returns 16-bit ones' complement sum that was calculated over a portion of
+ * packet data during a packet processing operation (e.g. packet input or
+ * IPSEC offload). The data range is output with 'range' parameter, and usually
+ * includes IP payload (L4 headers and payload). When 'range.length' is zero,
+ * the sum has not been calculated. In case of odd number of bytes,
+ * calculation uses a zero byte as padding at the end. The sum may be used as
+ * part of e.g. UDP/TCP checksum checking, especially with IP fragments.
+ *
+ * @param pkt Packet handle
+ * @param[out] range Data range of the sum (output). The calculation started
+ * from range.offset and included range.length bytes. When
+ * range.length is zero, the sum has not been calculated.
+ *
+ * @return Ones' complement sum over the data range
+ */
+uint16_t odp_packet_ones_comp(odp_packet_t pkt, odp_packet_data_range_t *range);
+
+/**
* Packet flow hash value
*
* Returns the hash generated from the packet header. Use
@@ -1417,17 +1847,18 @@ void odp_packet_flow_hash_set(odp_packet_t pkt, uint32_t flow_hash);
/**
* Packet timestamp
*
- * Returns packet timestamp value as odp_time_t type. Use time API for
- * additional operations on packet timestamp values or conversion into
- * nanoseconds. Use odp_packet_has_ts() to check if packet has a valid
- * timestamp. Packet input interface timestamp resolution can be checked with
- * odp_pktin_ts_res().
+ * Returns timestamp value recorded into the packet. Use odp_packet_has_ts() to check if the packet
+ * has a valid timestamp. Packet input uses packet IO interface specific time source and thus
+ * timestamp (or nanosecond) values from one interface cannot be mixed with values from another
+ * interface (or time source in general). Packet IO interface timestamp resolution can be checked
+ * with odp_pktio_ts_res() and current time with odp_pktio_time().
+ *
+ * Time API operations (e.g. odp_time_diff()) can be used with packet timestamp values or
+ * when converting those into nanoseconds (odp_time_to_ns()).
*
* @param pkt Packet handle
*
* @return Timestamp value
- *
- * @see odp_pktin_ts_res(), odp_packet_has_ts(), odp_time_to_ns()
*/
odp_time_t odp_packet_ts(odp_packet_t pkt);
@@ -1439,12 +1870,27 @@ odp_time_t odp_packet_ts(odp_packet_t pkt);
* @param pkt Packet handle
* @param timestamp Timestamp value
*
- * @see odp_packet_ts(), odp_packet_has_ts(),
- * odp_pktin_ts_from_ns()
+ * @see odp_packet_ts(), odp_packet_has_ts(), odp_pktio_ts_from_ns()
*/
void odp_packet_ts_set(odp_packet_t pkt, odp_time_t timestamp);
/**
+ * Request Tx timestamp capture
+ *
+ * Control whether timestamp needs to be captured when this packet is
+ * transmitted. By default, Tx timestamping is disabled. This API is allowed to
+ * be called always, but the Tx timestamp is not captured if the output packet
+ * IO device is not configured to enable timestamping.
+ *
+ * @param pkt Packet handle
+ * @param enable 0: do not capture timestamp on Tx
+ * 1: capture timestamp on Tx
+ *
+ * @see odp_pktout_ts_read()
+ */
+void odp_packet_ts_request(odp_packet_t pkt, int enable);
+
+/**
* Get packet color
*
* @param pkt Packet handle
@@ -1494,6 +1940,521 @@ int8_t odp_packet_shaper_len_adjust(odp_packet_t pkt);
*/
void odp_packet_shaper_len_adjust_set(odp_packet_t pkt, int8_t adj);
+/**
+ * Classification mark value
+ *
+ * Get the value of the CLS mark of the packet. The mark value is zero by default, but may be
+ * changed by the classification subsystem.
+ *
+ * @param pkt Packet handle
+ *
+ * @return mark value
+ *
+ * @see odp_cls_capability_t::max_mark, odp_pmr_create_opt_t::mark, odp_cls_pmr_create_opt()
+ */
+uint64_t odp_packet_cls_mark(odp_packet_t pkt);
+
+/**
+ * Request Large Send Offload (LSO) for a packet
+ *
+ * Setup packet metadata which requests LSO segmentation to be performed during packet output.
+ * The selected LSO profile specifies details of the segmentation operation to be done. Depending on
+ * LSO profile options, additional metadata (e.g. L3/L4 protocol header offsets) may need to be
+ * set on the packet.
+ *
+ * @param pkt Packet handle
+ * @param lso_opt LSO options
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ */
+int odp_packet_lso_request(odp_packet_t pkt, const odp_packet_lso_opt_t *lso_opt);
+
+/**
+ * Clear LSO request from a packet
+ *
+ * Clears packet metadata not to request LSO segmentation.
+ *
+ * @param pkt Packet handle
+ */
+void odp_packet_lso_request_clr(odp_packet_t pkt);
+
+/**
+ * Check if LSO is requested for the packet
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero LSO is requested
+ * @retval 0 LSO is not requested
+ */
+int odp_packet_has_lso_request(odp_packet_t pkt);
+
+/**
+ * Payload data offset
+ *
+ * Returns offset to the start of payload data. Packet data before this offset is considered as
+ * protocol headers. The offset is calculated from the current odp_packet_data() position in bytes.
+ * Data start position updating functions (e.g. odp_packet_push_head()) do not modify the offset,
+ * but user sets a new value when needed.
+ *
+ * Packet parsing does not set this offset. Initial offset value is undefined. Application may
+ * utilize the offset for internal purposes or when requesting LSO segmentation for the packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @return Payload data offset
+ * @retval ODP_PACKET_OFFSET_INVALID Payload data offset has not been set
+ */
+uint32_t odp_packet_payload_offset(odp_packet_t pkt);
+
+/**
+ * Set payload data start offset
+ *
+ * Set offset to the start of payload data. The offset is calculated from the current
+ * odp_packet_data() position in bytes. Offset must not exceed packet data length. Offset is not
+ * modified on an error.
+ *
+ * @param pkt Packet handle
+ * @param offset Payload data start offset (0 ... odp_packet_len()-1) or ODP_PACKET_OFFSET_INVALID
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_packet_payload_offset_set(odp_packet_t pkt, uint32_t offset);
+
+/**
+ * Enable or disable Tx packet aging
+ *
+ * Enable or disable Tx packet drop based on packet age. When enabled, packet will be dropped
+ * if it is in Tx pktout queue or traffic shapers/schedulers for longer than timeout set.
+ *
+ * When tmo_ns is
+ * !0: Aging is enabled
+ * 0: Aging is disabled
+ *
+ * Aging is disabled by default. Maximum tmo value is defined by max_tx_aging_tmo_ns capa.
+ *
+ * @param pkt Packet handle
+ * @param tmo_ns Packet aging drop timeout in nsec. When 0, aging drop is disabled (default).
+ *
+ * @see odp_pktio_capability_t::max_tx_aging_tmo_ns
+ */
+void odp_packet_aging_tmo_set(odp_packet_t pkt, uint64_t tmo_ns);
+
+/**
+ * Check if packet has Tx aging drop enabled
+ *
+ * @param pkt Packet handle
+ *
+ * @return Aging drop timeout if enabled.
+ * @retval >0 Aging drop timeout in nano seconds and implies aging drop is enabled.
+ * @retval 0 If Aging drop is disabled.
+ */
+uint64_t odp_packet_aging_tmo(odp_packet_t pkt);
+
+/**
+ * Request packet transmit completion
+ *
+ * Enables or disables packet transmit completion request for the packet. Completion may be
+ * requested either in event (#ODP_PACKET_TX_COMPL_EVENT) or poll (#ODP_PACKET_TX_COMPL_POLL) mode.
+ * When event mode is enabled, an event of type ODP_EVENT_PACKET_TX_COMPL will be sent to the
+ * destination queue to signal transmit completion. When poll mode is enabled,
+ * odp_packet_tx_compl_done() is used with the provided completion identifier to check the
+ * completion. In both cases, transmit completion is reported only after pktio interface has
+ * finished processing the packet.
+ *
+ * A previously enabled request can be disabled by setting the mode to ODP_PACKET_TX_COMPL_DISABLED.
+ * Transmit completion request is disabled by default.
+ *
+ * @param pkt Packet handle
+ * @param opt Packet transmit completion request options
+ *
+ * @retval 0 On success
+ * @retval <0 On failure
+ */
+int odp_packet_tx_compl_request(odp_packet_t pkt, const odp_packet_tx_compl_opt_t *opt);
+
+/**
+ * Check if packet transmit completion is requested
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Transmit completion is requested
+ * @retval 0 Transmit completion is not requested
+ */
+int odp_packet_has_tx_compl_request(odp_packet_t pkt);
+
+/**
+ * Set packet free control option
+ *
+ * This option enables application to control if a packet is freed/not freed back into a pool
+ * after an ODP offload feature has finished processing it. The option does not have an effect on
+ * odp_packet_free() or other direct free calls. It affects only packets that are sent directly
+ * through a packet output interface queue (odp_pktout_queue_t or odp_queue_t), also when packets
+ * are LSO offloaded. Packets transmitted through inline IPsec or TM are not affected.
+ *
+ * Packet output interface frees transmitted packets by default. When the option is set to
+ * #ODP_PACKET_FREE_CTRL_DONT_FREE, packet output will not free the packet after transmit and
+ * application may reuse or free the packet as soon as its transmission is complete
+ * (see e.g. odp_packet_tx_compl_done()). Check packet IO interface capability free_ctrl.dont_free
+ * (odp_pktio_capability_t.free_ctrl) for the option support. When an interface does not support
+ * the option, it ignores the value.
+ *
+ * The option must not be enabled on packets that have multiple references. The default value is
+ * #ODP_PACKET_FREE_CTRL_DISABLED.
+ *
+ * @param pkt Packet handle
+ * @param ctrl Packet free control option value
+ */
+void odp_packet_free_ctrl_set(odp_packet_t pkt, odp_packet_free_ctrl_t ctrl);
+
+/**
+ * Returns packet free control option value
+ *
+ * @param pkt Packet handle
+ *
+ * @return The current value of the packet free control option
+ */
+odp_packet_free_ctrl_t odp_packet_free_ctrl(odp_packet_t pkt);
+
+/**
+ * Request packet proto stats.
+ *
+ * The statistics enabled in the proto stats object are updated for the packet in
+ * packet output when the packet is transmitted or dropped. The statistics update
+ * is done as the last step of output processing after possible packet
+ * transformations (e.g. fragmentation, IPsec) that may occur. If a packet is
+ * fragmented or segmented to multiple packets as part of output processing, all
+ * the resulting packets inherit the proto stats object association from the
+ * original packet.
+ *
+ * The relevant octet counts will be updated with the actual packet length at
+ * the time of transmission or drop plus the respective adjustment value passed
+ * in the 'opt' parameter. The octet counts thus include possible additional
+ * headers added by ODP during packet output (e.g. ESP header added by inline
+ * outbound IPsec processing). Ethernet padding and FCS are not included in the
+ * octet counts. The adjustment value is added only if the respective capability
+ * field is true and otherwise ignored.
+ *
+ * @param pkt Packet handle
+ * @param opt Proto stats options. If NULL, then proto stats update is
+ * disabled for this packet.
+ *
+ * @see odp_proto_stats_capability_t::tx
+ */
+void odp_packet_proto_stats_request(odp_packet_t pkt, odp_packet_proto_stats_opt_t *opt);
+
+/**
+ * Get proto stats object.
+ *
+ * Get the proto stats object associated with the given packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @return Proto stats object handle or ODP_PROTO_STATS_INVALID if not set.
+ */
+odp_proto_stats_t odp_packet_proto_stats(odp_packet_t pkt);
+
+/*
+ *
+ * Packet vector handling routines
+ * ********************************************************
+ *
+ */
+
+/**
+ * Get packet vector handle from event
+ *
+ * Converts an ODP_EVENT_PACKET_VECTOR type event to a packet vector handle
+ *
+ * @param ev Event handle
+ * @return Packet vector handle
+ *
+ * @see odp_event_type()
+ */
+odp_packet_vector_t odp_packet_vector_from_event(odp_event_t ev);
+
+/**
+ * Convert packet vector handle to event
+ *
+ * @param pktv Packet vector handle
+ *
+ * @return Event handle
+ */
+odp_event_t odp_packet_vector_to_event(odp_packet_vector_t pktv);
+
+/**
+ * Allocate a packet vector from a packet vector pool
+ *
+ * Allocates a packet vector from the specified packet vector pool.
+ * The pool must have been created with the ODP_POOL_VECTOR type.
+ *
+ * @param pool Packet vector pool handle
+ *
+ * @return Handle of allocated packet vector
+ * @retval ODP_PACKET_VECTOR_INVALID Packet vector could not be allocated
+ *
+ * @note A newly allocated vector shall not contain any packets, instead, alloc
+ * operation shall reserve the space for odp_pool_param_t::vector::max_size packets.
+ */
+odp_packet_vector_t odp_packet_vector_alloc(odp_pool_t pool);
+
+/**
+ * Free packet vector
+ *
+ * Frees the packet vector into the packet vector pool it was allocated from.
+ *
+ * @param pktv Packet vector handle
+ *
+ * @note This API just frees the vector, not any packets inside the vector.
+ * Application can use odp_event_free() to free the vector and packets inside
+ * the vector.
+ */
+void odp_packet_vector_free(odp_packet_vector_t pktv);
+
+/**
+ * Get packet vector table
+ *
+ * Packet vector table is an array of packets (odp_packet_t) stored in
+ * contiguous memory location. Upon completion of this API, the implementation
+ * returns the packet table pointer in pkt_tbl.
+ *
+ * @param pktv Packet vector handle
+ * @param[out] pkt_tbl Points to packet vector table
+ *
+ * @return Number of packets available in the vector.
+ *
+ * @note When pktin subsystem is producing the packet vectors,
+ * odp_pktin_vector_config_t::pool shall be used to configure the pool to form
+ * the vector table.
+ *
+ * @note The maximum number of packets this vector can hold is defined by
+ * odp_pool_param_t::vector::max_size. The return value of this function will not
+ * be greater than odp_pool_param_t::vector::max_size
+ *
+ * @note The pkt_tbl points to the packet vector table. Application can edit the
+ * packet handles in the table directly (up to odp_pool_param_t::vector::max_size).
+ * Application must update the size of the table using odp_packet_vector_size_set()
+ * when there is a change in the size of the vector.
+ *
+ * @note Invalid packet handles (ODP_PACKET_INVALID) are not allowed to be
+ * stored in the table to allow consumers of odp_packet_vector_t handle to have
+ * optimized implementation. So consumption of packets in the middle of the
+ * vector would call for moving the remaining packets up to form a contiguous
+ * array of packets and update the size of the new vector using
+ * odp_packet_vector_size_set().
+ *
+ * @note The table memory is backed by a vector pool buffer. The ownership of
+ * the table memory is linked to the ownership of the event. I.e. after sending
+ * the event to a queue, the sender loses ownership to the table also.
+ */
+uint32_t odp_packet_vector_tbl(odp_packet_vector_t pktv, odp_packet_t **pkt_tbl);
+
+/**
+ * Number of packets in a vector
+ *
+ * @param pktv Packet vector handle
+ *
+ * @return The number of packets available in the vector
+ */
+uint32_t odp_packet_vector_size(odp_packet_vector_t pktv);
+
+/**
+ * Set the number of packets stored in a vector
+ *
+ * Update the number of packets stored in a vector. When the application is
+ * producing a packet vector, this function shall be used by the application
+ * to set the number of packets available in this vector.
+ *
+ * @param pktv Packet vector handle
+ * @param size Number of packets in this vector
+ *
+ * @note The maximum number of packets this vector can hold is defined by
+ * odp_pool_param_t::vector::max_size. The size value must not be greater than
+ * odp_pool_param_t::vector::max_size
+ *
+ * @note All handles in the vector table (0 .. size - 1) need to be valid packet
+ * handles.
+ *
+ * @see odp_packet_vector_tbl()
+ *
+ */
+void odp_packet_vector_size_set(odp_packet_vector_t pktv, uint32_t size);
+
+/**
+ * Packet vector user area
+ *
+ * Returns pointer to the user area associated with the packet vector. Size of the area is fixed
+ * and defined in vector pool parameters.
+ *
+ * @param pktv Packet vector handle
+ *
+ * @return Pointer to the user area of the packet vector
+ * @retval NULL The packet vector does not have user area
+ */
+void *odp_packet_vector_user_area(odp_packet_vector_t pktv);
+
+/**
+ * Check user flag
+ *
+ * Implementation clears user flag during new packet vector creation (e.g. alloc and packet input)
+ * and reset. User may set the flag with odp_packet_vector_user_flag_set(). Implementation never
+ * sets the flag, only clears it. The flag may be useful e.g. to mark when the user area
+ * content is valid.
+ *
+ * @param pktv Packet vector handle
+ *
+ * @retval 0 User flag is clear
+ * @retval >0 User flag is set
+ */
+int odp_packet_vector_user_flag(odp_packet_vector_t pktv);
+
+/**
+ * Set user flag
+ *
+ * Set (or clear) the user flag.
+ *
+ * @param pktv Packet vector handle
+ * @param val New value for the flag. Zero clears the flag, other values set the flag.
+ */
+void odp_packet_vector_user_flag_set(odp_packet_vector_t pktv, int val);
+
+/**
+ * Check that packet vector is valid
+ *
+ * This function can be used for debugging purposes to check if a packet vector handle represents
+ * a valid packet vector. The level of error checks depends on the implementation. Considerable
+ * number of cpu cycles may be consumed depending on the level. The call should not crash if
+ * the packet vector handle is corrupted.
+ *
+ * @param pktv Packet vector handle
+ *
+ * @retval 0 Packet vector is not valid
+ * @retval 1 Packet vector is valid
+ */
+int odp_packet_vector_valid(odp_packet_vector_t pktv);
+
+/**
+ * Packet vector pool
+ *
+ * Returns handle to the packet vector pool where the packet vector was
+ * allocated from.
+ *
+ * @param pktv Packet vector handle
+ *
+ * @return Packet vector pool handle
+ */
+odp_pool_t odp_packet_vector_pool(odp_packet_vector_t pktv);
+
+/**
+ * Print packet vector debug information
+ *
+ * Print all packet vector debug information to ODP log.
+ *
+ * @param pktv Packet vector handle
+ */
+void odp_packet_vector_print(odp_packet_vector_t pktv);
+
+/**
+ * Get printable value for an odp_packet_vector_t
+ *
+ * @param hdl odp_packet_vector_t handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes to enable
+ * applications to generate a printable value that represents an
+ * odp_packet_vector_t handle.
+ */
+uint64_t odp_packet_vector_to_u64(odp_packet_vector_t hdl);
+
+/**
+ * Check reassembly status of the packet
+ *
+ * @param pkt Packet handle
+ * @return Reassembly status
+ *
+ */
+odp_packet_reass_status_t odp_packet_reass_status(odp_packet_t pkt);
+
+/*
+ *
+ * Packet Tx completion event handling routines
+ * ********************************************************
+ */
+
+/**
+ * Get packet Tx completion handle from event
+ *
+ * Converts an ODP_EVENT_PACKET_TX_COMPL type event to packet Tx completion
+ * handle.
+ *
+ * @param ev Event handle
+ *
+ * @return Packet Tx completion handle
+ *
+ * @see odp_event_type()
+ */
+odp_packet_tx_compl_t odp_packet_tx_compl_from_event(odp_event_t ev);
+
+/** Convert packet Tx completion to event
+ *
+ * @param tx_compl Packet Tx completion
+ *
+ * @return Event handle
+ */
+odp_event_t odp_packet_tx_compl_to_event(odp_packet_tx_compl_t tx_compl);
+
+/**
+ * Free packet Tx completion
+ *
+ * Frees the packet Tx completion back to platform. It frees packet Tx
+ * completion that gets allocated as part of packet Tx completion request
+ * for a given packet.
+ *
+ * @param tx_compl Packet Tx completion handle
+ *
+ * @see odp_packet_tx_completion_request()
+ */
+void odp_packet_tx_compl_free(odp_packet_tx_compl_t tx_compl);
+
+/**
+ * User context pointer
+ *
+ * Return user context pointer from packet Tx completion event.
+ * This is the same value set to packet using odp_packet_user_ptr_set().
+ *
+ * @param tx_compl Packet Tx completion handle
+ *
+ * @return User context pointer
+ *
+ * @see odp_packet_user_ptr_set()
+ */
+void *odp_packet_tx_compl_user_ptr(odp_packet_tx_compl_t tx_compl);
+
+/**
+ * Check packet transmit completion
+ *
+ * Checks if a previously sent packet with a ODP_PACKET_TX_COMPL_POLL type transmit completion
+ * request (see odp_packet_tx_compl_opt_t) has been transmitted. The packet send function call
+ * clears completion identifier status, and 0 is returned while the transmit is in progress.
+ * When >0 is returned, transmit of the packet is complete and the completion identifier may be
+ * reused for another transmit.
+ *
+ * When transmit of a packet is complete, it indicates that transmit of other packets sent
+ * before the packet through the same queue have also completed.
+ *
+ * Returns initially 0 for all configured completion identifiers.
+ *
+ * @param pktio Packet IO interface that was used to send the packet
+ * @param compl_id Completion identifier that was used in the transmit completion request
+ *
+ * @retval >0 Packet transmit is complete
+ * @retval 0 Packet transmit is not complete
+ * @retval <0 Failed to read packet transmit status
+ */
+int odp_packet_tx_compl_done(odp_pktio_t pktio, uint32_t compl_id);
+
/*
*
* Debugging
@@ -1502,19 +2463,34 @@ void odp_packet_shaper_len_adjust_set(odp_packet_t pkt, int8_t adj);
*/
/**
- * Print packet to the console
+ * Print packet debug information
*
- * Print all packet debug information to the console.
+ * Print all packet debug information to the ODP log.
*
* @param pkt Packet handle
*/
void odp_packet_print(odp_packet_t pkt);
/**
- * Perform full packet validity check
+ * Print packet data
+ *
+ * Print packet debug information with packet data to the ODP log. Operation
+ * prints 'len' bytes of packet data starting from 'offset' byte. Offset plus
+ * length must not exceed packet length (odp_packet_len()).
+ *
+ * @param pkt Packet handle
+ * @param offset Byte offset into the packet
+ * @param len Number of bytes to print
+ */
+void odp_packet_print_data(odp_packet_t pkt, uint32_t offset, uint32_t len);
+
+/**
+ * Check that packet is valid
*
- * The operation may consume considerable number of cpu cycles depending on
- * the check level.
+ * This function can be used for debugging purposes to check if a packet handle represents
+ * a valid packet. The level of error checks depends on the implementation. Considerable number of
+ * cpu cycles may be consumed depending on the level. The call should not crash if the packet
+ * handle is corrupted.
*
* @param pkt Packet handle
*
diff --git a/include/odp/api/spec/packet_flags.h b/include/odp/api/spec/packet_flags.h
index 377b75ba0..3bbd8f331 100644
--- a/include/odp/api/spec/packet_flags.h
+++ b/include/odp/api/spec/packet_flags.h
@@ -1,18 +1,16 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
-
/**
* @file
*
* ODP packet flags
*/
-#ifndef ODP_API_PACKET_FLAGS_H_
-#define ODP_API_PACKET_FLAGS_H_
+#ifndef ODP_API_SPEC_PACKET_FLAGS_H_
+#define ODP_API_SPEC_PACKET_FLAGS_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,90 +18,124 @@ extern "C" {
#endif
#include <odp/api/std_types.h>
-#include <odp/api/packet.h>
+#include <odp/api/packet_types.h>
/** @addtogroup odp_packet
- * Boolean operations on a packet.
+ * @par Operations on packet metadata flags
+ *
+ * If user sets multiple conflicting packet metadata flags
+ * using odp_packet_has_XX_set() functions, only the last set flag value is
+ * guaranteed to hold. The values of other conflicting flags are implementation
+ * specific. The conflicting flag combinations are defined in function
+ * documentations.
* @{
*/
/**
- * Check for packet errors
+ * Check for all parse errors in packet
*
- * Checks all error flags at once.
+ * Check if packet parsing has found any errors in the packet. The level of
+ * error checking depends on the parse configuration (e.g. included layers and
+ * checksums). Protocol layer functions (e.g. odp_packet_has_l3()) indicate
+ * which layers have been checked, and layer error functions
+ * (e.g. odp_packet_has_l3_error()) which layers have errors.
*
- * @param pkt Packet handle
- * @retval non-zero packet has errors
- * @retval 0 packet has no errors
+ * If packet subtype is ODP_EVENT_PACKET_IPSEC, odp_packet_has_error() would
+ * indicate parsing errors after IPSEC processing. IPSEC errors/warnings need
+ * to be checked using odp_ipsec_result().
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet has errors
+ * @retval 0 No errors were found
*/
int odp_packet_has_error(odp_packet_t pkt);
/**
- * Check for packet L2 errors
+ * Check for errors in layer 2
*
- * check for all L2 errors
+ * When layer 2 is included in the parse configuration, check if any errors were
+ * found in layer 2 of the packet.
*
- * @param pkt Packet handle
- * @retval non-zero packet has L2 errors
- * @retval 0 packet has no L2 error
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet has errors in layer 2
+ * @retval 0 No errors were found in layer 2
*/
int odp_packet_has_l2_error(odp_packet_t pkt);
/**
- * Check for L2 header, e.g. ethernet
+ * Check for errors in layer 3
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a valid & known L2 header
- * @retval 0 if packet does not contain a valid & known L2 header
+ * When layer 3 is included in the parse configuration, check if any errors were
+ * found in layer 3 of the packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet has errors in layer 3
+ * @retval 0 No errors found in layer 3
*/
-int odp_packet_has_l2(odp_packet_t pkt);
+int odp_packet_has_l3_error(odp_packet_t pkt);
/**
- * Check for packet L3 errors
+ * Check for errors in layer 4
*
- * check for all L3 errors
+ * When layer 4 is included in the parse configuration, check if any errors were
+ * found in layer 4 of the packet.
*
- * @param pkt Packet handle
- * @retval non-zero packet has L3 errors
- * @retval 0 packet has no L3 error
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet has errors in layer 4
+ * @retval 0 No errors were found in layer 4
*/
-int odp_packet_has_l3_error(odp_packet_t pkt);
+int odp_packet_has_l4_error(odp_packet_t pkt);
/**
- * Check for L3 header, e.g. IPv4, IPv6
+ * Check for layer 2 protocols
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a valid & known L3 header
- * @retval 0 if packet does not contain a valid & known L3 header
+ * When layer 2 is included in the parse configuration, check if packet parsing
+ * has found and checked a layer 2 protocol (e.g. Ethernet) in the packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero A layer 2 protocol header was found and checked
+ * @retval 0 No layer 2 protocol was found
*/
-int odp_packet_has_l3(odp_packet_t pkt);
+int odp_packet_has_l2(odp_packet_t pkt);
/**
- * Check for packet L4 errors
+ * Check for layer 3 protocols
*
- * check for all L4 errors
+ * When layer 3 is included in the parse configuration, check if packet parsing
+ * has found and checked a layer 3 protocol (e.g. IPv4, IPv6) in the packet.
*
- * @param pkt Packet handle
- * @retval non-zero packet has L4 errors
- * @retval 0 packet has no L4 error
+ * @param pkt Packet handle
+ *
+ * @retval non-zero A layer 3 protocol header was found and checked
+ * @retval 0 No layer 3 protocol was found
*/
-int odp_packet_has_l4_error(odp_packet_t pkt);
+int odp_packet_has_l3(odp_packet_t pkt);
/**
- * Check for L4 header, e.g. UDP, TCP, SCTP (also ICMP)
+ * Check for layer 4 protocols
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a valid & known L4 header
- * @retval 0 if packet does not contain a valid & known L4 header
+ * When layer 4 is included in the parse configuration, check if packet parsing
+ * has found and checked a layer 4 protocol (e.g. UDP, TCP, SCTP) in the packet.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero A layer 4 protocol header was found and checked
+ * @retval 0 No layer 4 protocol was found
*/
int odp_packet_has_l4(odp_packet_t pkt);
/**
* Check for Ethernet header
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a valid eth header
- * @retval 0 if packet does not contain a valid eth header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains an Ethernet header
+ * @retval 0 Packet does not contain an Ethernet header
*/
int odp_packet_has_eth(odp_packet_t pkt);
@@ -113,9 +145,10 @@ int odp_packet_has_eth(odp_packet_t pkt);
* ODP recognizes the destination MAC address FF:FF:FF:FF:FF:FF as
* a broadcast address. All others are considered non-broadcast.
*
- * @param pkt Packet handle
- * @retval non-zero if Ethernet destination address is the broadcast address
- * @retval 0 if Ethernet destination address is not the broadcast address
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Ethernet destination address is the broadcast address
+ * @retval 0 Ethernet destination address is not the broadcast address
*/
int odp_packet_has_eth_bcast(odp_packet_t pkt);
@@ -124,63 +157,74 @@ int odp_packet_has_eth_bcast(odp_packet_t pkt);
*
* ODP recognizes the destination MAC address as multicast if bit 7 is 1.
*
- * @param pkt Packet handle
- * @retval non-zero if Ethernet destination address is a multicast address
- * @retval 0 if Ethernet destination address is not a multicast address
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Ethernet destination address is a multicast address
+ * @retval 0 Ethernet destination address is not a multicast address
*/
int odp_packet_has_eth_mcast(odp_packet_t pkt);
/**
* Check for jumbo frame
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a jumbo frame
- * @retval 0 if packet does not contain a jumbo frame
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet is a jumbo frame
+ * @retval 0 Packet is not a jumbo frame
*/
int odp_packet_has_jumbo(odp_packet_t pkt);
/**
* Check for VLAN
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a VLAN header
- * @retval 0 if packet does not contain a VLAN header
+ * Check if packet contains normal or QinQ VLAN header.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a VLAN header
+ * @retval 0 Packet does not contain a VLAN header
*/
int odp_packet_has_vlan(odp_packet_t pkt);
/**
* Check for VLAN QinQ (stacked VLAN)
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a VLAN QinQ header
- * @retval 0 if packet does not contain a VLAN QinQ header
+ * Check if packet contains QinQ VLAN header.
+ *
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a VLAN QinQ header
+ * @retval 0 Packet does not contain a VLAN QinQ header
*/
int odp_packet_has_vlan_qinq(odp_packet_t pkt);
/**
* Check for ARP
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains an ARP message
- * @retval 0 if packet does not contain an ARP message
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains an ARP message
+ * @retval 0 Packet does not contain an ARP message
*/
int odp_packet_has_arp(odp_packet_t pkt);
/**
* Check for IPv4
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains an IPv4 header
- * @retval 0 if packet does not contain an IPv4 header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains an IPv4 header
+ * @retval 0 Packet does not contain an IPv4 header
*/
int odp_packet_has_ipv4(odp_packet_t pkt);
/**
* Check for IPv6
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains an IPv6 header
- * @retval 0 if packet does not contain an IPv6 header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains an IPv6 header
+ * @retval 0 Packet does not contain an IPv6 header
*/
int odp_packet_has_ipv6(odp_packet_t pkt);
@@ -192,9 +236,10 @@ int odp_packet_has_ipv6(odp_packet_t pkt);
*
* For IPv6, no destination addresses are recognized as broadcast addresses.
*
- * @param pkt Packet handle
- * @retval non-zero if IP destination address is a broadcast address
- * @retval 0 if IP destination address is not a broadcast address
+ * @param pkt Packet handle
+ *
+ * @retval non-zero IP destination address is a broadcast address
+ * @retval 0 IP destination address is not a broadcast address
*/
int odp_packet_has_ip_bcast(odp_packet_t pkt);
@@ -207,91 +252,100 @@ int odp_packet_has_ip_bcast(odp_packet_t pkt);
* For IPv6 ODP recognizes destination IP addresses with prefixes FF00::
* through FFFF:: as multicast addresses.
*
- * @param pkt Packet handle
- * @retval non-zero if IP destination address is a multicast address
- * @retval 0 if IP destination address is not a multicast address
+ * @param pkt Packet handle
+ *
+ * @retval non-zero IP destination address is a multicast address
+ * @retval 0 IP destination address is not a multicast address
*/
int odp_packet_has_ip_mcast(odp_packet_t pkt);
/**
* Check for IP fragment
*
- * @param pkt Packet handle
- * @retval non-zero if packet is an IP fragment
- * @retval 0 if packet is not an IP fragment
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet is an IP fragment
+ * @retval 0 Packet is not an IP fragment
*/
int odp_packet_has_ipfrag(odp_packet_t pkt);
/**
* Check for IP options
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains IP options
- * @retval 0 if packet does not contain IP options
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains IP options
+ * @retval 0 Packet does not contain IP options
*/
int odp_packet_has_ipopt(odp_packet_t pkt);
/**
* Check for IPSec
*
- * @param pkt Packet handle
- * @retval non-zero if packet requires IPSec processing
- * @retval 0 if packet does not require IPSec processing
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet requires IPSec processing
+ * @retval 0 Packet does not require IPSec processing
*/
int odp_packet_has_ipsec(odp_packet_t pkt);
/**
* Check for UDP
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a UDP header
- * @retval 0 if packet does not contain a UDP header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a UDP header
+ * @retval 0 Packet does not contain a UDP header
*/
int odp_packet_has_udp(odp_packet_t pkt);
/**
* Check for TCP
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a TCP header
- * @retval 0 if packet does not contain a TCP header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a TCP header
+ * @retval 0 Packet does not contain a TCP header
*/
int odp_packet_has_tcp(odp_packet_t pkt);
/**
* Check for SCTP
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a SCTP header
- * @retval 0 if packet does not contain a SCTP header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a SCTP header
+ * @retval 0 Packet does not contain a SCTP header
*/
int odp_packet_has_sctp(odp_packet_t pkt);
/**
* Check for ICMP
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains an ICMP header
- * @retval 0 if packet does not contain an ICMP header
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains an ICMP header
+ * @retval 0 Packet does not contain an ICMP header
*/
int odp_packet_has_icmp(odp_packet_t pkt);
/**
* Check for packet flow hash
*
- * @param pkt Packet handle
- * @retval non-zero if packet contains a hash value
- * @retval 0 if packet does not contain a hash value
+ * @param pkt Packet handle
+ *
+ * @retval non-zero Packet contains a hash value
+ * @retval 0 Packet does not contain a hash value
*/
int odp_packet_has_flow_hash(odp_packet_t pkt);
/**
* Check for packet timestamp
*
- * @param pkt Packet handle
+ * @param pkt Packet handle
*
- * @retval non-zero if packet contains a timestamp value
- * @retval 0 if packet does not contain a timestamp value
+ * @retval non-zero Packet contains a timestamp value
+ * @retval 0 Packet does not contain a timestamp value
*
* @see odp_packet_has_ts_clr()
*/
@@ -356,6 +410,9 @@ void odp_packet_has_jumbo_set(odp_packet_t pkt, int val);
/**
* Set flag for VLAN
*
+ * Set when packet contains normal VLAN header. Only one VLAN flag
+ * (VLAN/VLAN QinQ) can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -364,6 +421,9 @@ void odp_packet_has_vlan_set(odp_packet_t pkt, int val);
/**
* Set flag for VLAN QinQ (stacked VLAN)
*
+ * Set when packet contains QinQ VLAN header. Only one VLAN flag
+ * (VLAN/VLAN QinQ) can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -372,6 +432,8 @@ void odp_packet_has_vlan_qinq_set(odp_packet_t pkt, int val);
/**
* Set flag for ARP
*
+ * Only one of ARP/IPv4/IPv6 flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -380,6 +442,8 @@ void odp_packet_has_arp_set(odp_packet_t pkt, int val);
/**
* Set flag for IPv4
*
+ * Only one of ARP/IPv4/IPv6 flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -388,6 +452,8 @@ void odp_packet_has_ipv4_set(odp_packet_t pkt, int val);
/**
* Set flag for IPv6
*
+ * Only one of ARP/IPv4/IPv6 flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -396,6 +462,8 @@ void odp_packet_has_ipv6_set(odp_packet_t pkt, int val);
/**
* Set flag for IP broadcast address
*
+ * Only one of IP broadcast/multicast flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -404,6 +472,8 @@ void odp_packet_has_ip_bcast_set(odp_packet_t pkt, int val);
/**
* Set flag for IP multicast address
*
+ * Only one of IP broadcast/multicast flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -436,6 +506,8 @@ void odp_packet_has_ipsec_set(odp_packet_t pkt, int val);
/**
* Set flag for UDP
*
+ * Only one of TCP/UDP/SCTP/ICMP flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -444,6 +516,8 @@ void odp_packet_has_udp_set(odp_packet_t pkt, int val);
/**
* Set flag for TCP
*
+ * Only one of TCP/UDP/SCTP/ICMP flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -452,6 +526,8 @@ void odp_packet_has_tcp_set(odp_packet_t pkt, int val);
/**
* Set flag for SCTP
*
+ * Only one of TCP/UDP/SCTP/ICMP flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
@@ -460,6 +536,8 @@ void odp_packet_has_sctp_set(odp_packet_t pkt, int val);
/**
* Set flag for ICMP
*
+ * Only one of TCP/UDP/SCTP/ICMP flags can be set simultaneously.
+ *
* @param pkt Packet handle
* @param val Value
*/
diff --git a/include/odp/api/spec/packet_io.h b/include/odp/api/spec/packet_io.h
index cec1f22af..a83617f7c 100644
--- a/include/odp/api/spec/packet_io.h
+++ b/include/odp/api/spec/packet_io.h
@@ -1,34 +1,36 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
*/
-
/**
* @file
*
* ODP Packet IO
*/
-#ifndef ODP_API_PACKET_IO_H_
-#define ODP_API_PACKET_IO_H_
+#ifndef ODP_API_SPEC_PACKET_IO_H_
+#define ODP_API_SPEC_PACKET_IO_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
+#include <odp/api/classification.h>
+#include <odp/api/packet_types.h>
#include <odp/api/packet_io_stats.h>
-#include <odp/api/queue.h>
-#include <odp/api/time.h>
+#include <odp/api/packet_io_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/reassembly.h>
+#include <odp/api/time_types.h>
-/** @defgroup odp_packet_io ODP PACKET IO
- * Operations on a packet Input/Output interface.
+/** @addtogroup odp_packet_io
+ * Packet IO interfaces.
*
* Packet IO is the Ingress and Egress interface to ODP processing. It
* allows manipulation of the interface for setting such attributes as
- * the mtu, mac etc.
+ * number of queues, MAC address etc.
* Pktio is usually followed by the classifier and a default class COS
* can be set so that the scheduler may distribute flows. The interface
* may be used directly in polled mode with odp_pktin_recv() and
@@ -40,425 +42,12 @@ extern "C" {
*/
/**
- * @typedef odp_pktio_t
- * Packet IO handle
- */
-
-/**
- * @typedef odp_pktin_queue_t
- * Direct packet input queue handle
- */
-
-/**
- * @typedef odp_pktout_queue_t
- * Direct packet output queue handle
- */
-
-/**
- * @def ODP_PKTIO_INVALID
- * Invalid packet IO handle
- */
-
-/**
- * @def ODP_PKTIO_MACADDR_MAXSIZE
- * Minimum size of output buffer for odp_pktio_mac_addr()
- * Actual MAC address sizes may be different.
- */
-
-/**
- * @def ODP_PKTIN_NO_WAIT
- * Do not wait on packet input
- */
-
-/**
- * @def ODP_PKTIN_WAIT
- * Wait infinitely on packet input
- */
-
-/**
- * Packet input mode
- */
-typedef enum odp_pktin_mode_t {
- /** Direct packet input from the interface */
- ODP_PKTIN_MODE_DIRECT = 0,
- /** Packet input through scheduler and scheduled event queues */
- ODP_PKTIN_MODE_SCHED,
- /** Packet input through plain event queues */
- ODP_PKTIN_MODE_QUEUE,
- /** Application will never receive from this interface */
- ODP_PKTIN_MODE_DISABLED
-} odp_pktin_mode_t;
-
-/**
- * Packet output mode
- */
-typedef enum odp_pktout_mode_t {
- /** Direct packet output on the interface */
- ODP_PKTOUT_MODE_DIRECT = 0,
- /** Packet output through event queues */
- ODP_PKTOUT_MODE_QUEUE,
- /** Packet output through traffic manager API */
- ODP_PKTOUT_MODE_TM,
- /** Application will never send to this interface */
- ODP_PKTOUT_MODE_DISABLED
-} odp_pktout_mode_t;
-
-/**
- * Packet input hash protocols
- *
- * The list of protocol header field combinations, which are included into
- * packet input hash calculation.
- */
-typedef union odp_pktin_hash_proto_t {
- /** Protocol header fields for hashing */
- struct {
- /** IPv4 addresses and UDP port numbers */
- uint32_t ipv4_udp : 1;
- /** IPv4 addresses and TCP port numbers */
- uint32_t ipv4_tcp : 1;
- /** IPv4 addresses */
- uint32_t ipv4 : 1;
- /** IPv6 addresses and UDP port numbers */
- uint32_t ipv6_udp : 1;
- /** IPv6 addresses and TCP port numbers */
- uint32_t ipv6_tcp : 1;
- /** IPv6 addresses */
- uint32_t ipv6 : 1;
- } proto;
-
- /** All bits of the bit field structure */
- uint32_t all_bits;
-} odp_pktin_hash_proto_t;
-
-/**
- * Packet IO operation mode
- */
-typedef enum odp_pktio_op_mode_t {
- /** Multithread safe operation
- *
- * Direct packet IO operation (recv or send) is multithread safe. Any
- * number of application threads may perform the operation
- * concurrently. */
- ODP_PKTIO_OP_MT = 0,
-
- /** Not multithread safe operation
- *
- * Direct packet IO operation (recv or send) may not be multithread
- * safe. Application ensures synchronization between threads so that
- * simultaneously only single thread attempts the operation on
- * the same (pktin or pktout) queue. */
- ODP_PKTIO_OP_MT_UNSAFE
-
-} odp_pktio_op_mode_t;
-
-/**
- * Packet input queue parameters
- */
-typedef struct odp_pktin_queue_param_t {
- /** Operation mode
- *
- * The default value is ODP_PKTIO_OP_MT. Application may enable
- * performance optimization by defining ODP_PKTIO_OP_MT_UNSAFE when
- * applicable. */
- odp_pktio_op_mode_t op_mode;
-
- /** Enable classifier
- *
- * * 0: Classifier is disabled (default)
- * * 1: Classifier is enabled. Use classifier to direct incoming
- * packets into pktin event queues. Classifier can be enabled
- * only in ODP_PKTIN_MODE_SCHED and ODP_PKTIN_MODE_QUEUE modes.
- * Both classifier and hashing cannot be enabled simultaneously
- * ('hash_enable' must be 0). */
- odp_bool_t classifier_enable;
-
- /** Enable flow hashing
- *
- * * 0: Do not hash flows (default)
- * * 1: Enable flow hashing. Use flow hashing to spread incoming
- * packets into input queues. Hashing can be enabled in all
- * modes. Both classifier and hashing cannot be enabled
- * simultaneously ('classifier_enable' must be 0). */
- odp_bool_t hash_enable;
-
- /** Protocol field selection for hashing
- *
- * Multiple protocols can be selected. Ignored when 'hash_enable' is
- * zero. The default value is all bits zero. */
- odp_pktin_hash_proto_t hash_proto;
-
- /** Number of input queues to be created
- *
- * When classifier is enabled in odp_pktin_queue_config() this
- * value is ignored, otherwise at least one queue is required.
- * More than one input queues require flow hashing configured.
- * The maximum value is defined by pktio capability 'max_input_queues'.
- * Queue type is defined by the input mode. The default value is 1. */
- unsigned num_queues;
-
- /** Queue parameters
- *
- * These are used for input queue creation in ODP_PKTIN_MODE_QUEUE
- * or ODP_PKTIN_MODE_SCHED modes. Scheduler parameters are considered
- * only in ODP_PKTIN_MODE_SCHED mode. Default values are defined in
- * odp_queue_param_t documentation.
- * When classifier is enabled in odp_pktin_queue_config() this
- * value is ignored. */
- odp_queue_param_t queue_param;
-
-} odp_pktin_queue_param_t;
-
-/**
- * Packet output queue parameters
- *
- * These parameters are used in ODP_PKTOUT_MODE_DIRECT and
- * ODP_PKTOUT_MODE_QUEUE modes.
- */
-typedef struct odp_pktout_queue_param_t {
- /** Operation mode
- *
- * The default value is ODP_PKTIO_OP_MT. Application may enable
- * performance optimization by defining ODP_PKTIO_OP_MT_UNSAFE when
- * applicable. */
- odp_pktio_op_mode_t op_mode;
-
- /** Number of output queues to be created. The value must be between
- * 1 and interface capability. The default value is 1. */
- unsigned num_queues;
-
-} odp_pktout_queue_param_t;
-
-/**
- * Packet IO parameters
- *
- * Packet IO interface level parameters. Use odp_pktio_param_init() to
- * initialize the structure with default values.
- */
-typedef struct odp_pktio_param_t {
- /** Packet input mode
- *
- * The default value is ODP_PKTIN_MODE_DIRECT. */
- odp_pktin_mode_t in_mode;
-
- /** Packet output mode
- *
- * The default value is ODP_PKTOUT_MODE_DIRECT. */
- odp_pktout_mode_t out_mode;
-
-} odp_pktio_param_t;
-
-/**
- * Packet input configuration options bit field
- *
- * Packet input configuration options listed in a bit field structure. Packet
- * input timestamping may be enabled for all packets or at least for those that
- * belong to time synchronization protocol (PTP).
- *
- * Packet input checksum checking may be enabled or disabled. When it is
- * enabled, implementation will verify checksum correctness on incoming packets
- * and depending on drop configuration either deliver erroneous packets with
- * appropriate flags set (e.g. odp_packet_has_l3_error()) or drop those.
- * When packet dropping is enabled, application will never receive a packet
- * with the specified error and may avoid to check the error flag.
- */
-typedef union odp_pktin_config_opt_t {
- /** Option flags */
- struct {
- /** Timestamp all packets on packet input */
- uint64_t ts_all : 1;
-
- /** Timestamp (at least) IEEE1588 / PTP packets
- * on packet input */
- uint64_t ts_ptp : 1;
-
- /** Check IPv4 header checksum on packet input */
- uint64_t ipv4_chksum : 1;
-
- /** Check UDP checksum on packet input */
- uint64_t udp_chksum : 1;
-
- /** Check TCP checksum on packet input */
- uint64_t tcp_chksum : 1;
-
- /** Check SCTP checksum on packet input */
- uint64_t sctp_chksum : 1;
-
- /** Drop packets with an IPv4 error on packet input */
- uint64_t drop_ipv4_err : 1;
-
- /** Drop packets with an IPv6 error on packet input */
- uint64_t drop_ipv6_err : 1;
-
- /** Drop packets with a UDP error on packet input */
- uint64_t drop_udp_err : 1;
-
- /** Drop packets with a TCP error on packet input */
- uint64_t drop_tcp_err : 1;
-
- /** Drop packets with a SCTP error on packet input */
- uint64_t drop_sctp_err : 1;
-
- } bit;
-
- /** All bits of the bit field structure
- *
- * This field can be used to set/clear all flags, or bitwise
- * operations over the entire structure. */
- uint64_t all_bits;
-} odp_pktin_config_opt_t;
-
-/**
- * Packet output configuration options bit field
- *
- * Packet output configuration options listed in a bit field structure. Packet
- * output checksum insertion may be enabled or disabled. When it is enabled,
- * implementation will calculate and insert checksum into every outgoing packet
- * by default. Application may use a packet metadata flag to disable checksum
- * insertion per packet bases. For correct operation, packet metadata must
- * provide valid offsets for the appropriate protocols. For example, UDP
- * checksum calculation needs both L3 and L4 offsets (to access IP and UDP
- * headers). When application (e.g. a switch) does not modify L3/L4 data and
- * thus checksum does not need to be updated, output checksum insertion should
- * be disabled for optimal performance.
- */
-typedef union odp_pktout_config_opt_t {
- /** Option flags */
- struct {
- /** Insert IPv4 header checksum on packet output */
- uint64_t ipv4_chksum : 1;
-
- /** Insert UDP checksum on packet output */
- uint64_t udp_chksum : 1;
-
- /** Insert TCP checksum on packet output */
- uint64_t tcp_chksum : 1;
-
- /** Insert SCTP checksum on packet output */
- uint64_t sctp_chksum : 1;
-
- } bit;
-
- /** All bits of the bit field structure
- *
- * This field can be used to set/clear all flags, or bitwise
- * operations over the entire structure. */
- uint64_t all_bits;
-} odp_pktout_config_opt_t;
-
-/**
- * Parser layers
- */
-typedef enum odp_pktio_parser_layer_t {
- /** No layers */
- ODP_PKTIO_PARSER_LAYER_NONE = 0,
-
- /** Layer L2 protocols (Ethernet, VLAN, ARP, etc) */
- ODP_PKTIO_PARSER_LAYER_L2,
-
- /** Layer L3 protocols (IPv4, IPv6, ICMP, IPsec, etc) */
- ODP_PKTIO_PARSER_LAYER_L3,
-
- /** Layer L4 protocols (UDP, TCP, SCTP) */
- ODP_PKTIO_PARSER_LAYER_L4,
-
- /** All layers */
- ODP_PKTIO_PARSER_LAYER_ALL
-
-} odp_pktio_parser_layer_t;
-
-/**
- * Parser configuration
- */
-typedef struct odp_pktio_parser_config_t {
- /** Protocol parsing level in packet input
- *
- * Parse protocol layers in minimum up to this level during packet
- * input. The default value is ODP_PKTIO_PARSER_LAYER_ALL. */
- odp_pktio_parser_layer_t layer;
-
-} odp_pktio_parser_config_t;
-
-/**
- * Packet IO configuration options
- *
- * Packet IO interface level configuration options. Use odp_pktio_capability()
- * to see which options are supported by the implementation.
- * Use odp_pktio_config_init() to initialize the structure with default values.
- */
-typedef struct odp_pktio_config_t {
- /** Packet input configuration options bit field
- *
- * Default value for all bits is zero. */
- odp_pktin_config_opt_t pktin;
-
- /** Packet output configuration options bit field
- *
- * Default value for all bits is zero. */
- odp_pktout_config_opt_t pktout;
-
- /** Packet input parser configuration */
- odp_pktio_parser_config_t parser;
-
- /** Interface loopback mode
- *
- * In this mode the packets sent out through the interface is
- * looped back to input of the same interface. Supporting loopback mode
- * is an optional feature per interface and should be queried in the
- * interface capability before enabling the same. */
- odp_bool_t enable_loop;
-
-} odp_pktio_config_t;
-
-/**
- * Packet IO set operations
- *
- * Supported packet IO interface set operations listed in a bit field structure.
- */
-typedef union odp_pktio_set_op_t {
- /** Operation flags */
- struct {
- /** Promiscuous mode */
- uint32_t promisc_mode : 1;
- } op;
- /** All bits of the bit field structure.
- * This field can be used to set/clear all flags, or bitwise
- * operations over the entire structure. */
- uint32_t all_bits;
-} odp_pktio_set_op_t;
-
-/**
- * Packet IO capabilities
- */
-typedef struct odp_pktio_capability_t {
- /** Maximum number of input queues */
- unsigned max_input_queues;
-
- /** Maximum number of output queues */
- unsigned max_output_queues;
-
- /** Supported pktio configuration options */
- odp_pktio_config_t config;
-
- /** Supported set operations
- *
- * A bit set to one indicates a supported operation. All other bits are
- * set to zero. */
- odp_pktio_set_op_t set_op;
-
- /** Support of Loopback mode
- *
- * A boolean to denote whether loop back mode is supported on this
- * specific interface. */
- odp_bool_t loop_supported;
-} odp_pktio_capability_t;
-
-/**
* Open a packet IO interface
*
* An ODP program can open a single packet IO interface per device, attempts
- * to open an already open device will fail, returning ODP_PKTIO_INVALID with
- * errno set. Use odp_pktio_lookup() to obtain a handle to an already open
- * device. Packet IO parameters provide interface level configuration options.
+ * to open an already open device will fail, returning ODP_PKTIO_INVALID. Use
+ * odp_pktio_lookup() to obtain a handle to an already open device. Packet IO
+ * parameters provide interface level configuration options.
*
* Use odp_pktio_param_init() to initialize packet IO parameters into their
* default values. Default values are also used when 'param' pointer is NULL.
@@ -473,6 +62,12 @@ typedef struct odp_pktio_capability_t {
* output mode is ODP_PKTOUT_MODE_DISABLED or ODP_PKTOUT_MODE_TM,
* odp_pktout_queue_config() call is optional and will ignore all parameters.
*
+ * Advanced packet IO interface offload features and options can be setup with
+ * odp_pktio_config() before the interface is started. These features include e.g.
+ * checksum, segmentation (LSO), reassembly and inline IPSEC offloads. When
+ * odp_pktio_config() is not used, the interface is started with the default
+ * values of odp_pktio_config_t.
+ *
* Packet receive and transmit on the interface is enabled with a call to
* odp_pktio_start(). If not specified otherwise, any interface level
* configuration must not be changed when the interface is active (between start
@@ -482,6 +77,7 @@ typedef struct odp_pktio_capability_t {
* * odp_pktio_open()
* * odp_pktin_queue_config()
* * odp_pktout_queue_config()
+ * * [optionally] odp_pktio_config()
* * odp_pktio_start()
*
* ... and tear down sequence is:
@@ -505,7 +101,7 @@ typedef struct odp_pktio_capability_t {
* the odp_pktio_default_cos_set() routine, or because a matching PMR
* assigned the packet to a specific CoS. The default pool specified
* here is applicable only for those packets that are not assigned to a
- * more specific CoS.
+ * more specific CoS that specifies another pool.
*
* @see odp_pktio_start(), odp_pktio_stop(), odp_pktio_close()
*/
@@ -530,10 +126,11 @@ int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa);
*
* Return the maximum packet IO interface index. Interface indexes
* (e.g. returned by odp_pktio_index()) range from zero to this maximum value.
+ * The return value does not exceed #ODP_PKTIO_MAX_INDEX.
*
* @return Maximum packet IO interface index
*/
-unsigned odp_pktio_max_index(void);
+unsigned int odp_pktio_max_index(void);
/**
* Configure packet IO interface options
@@ -616,8 +213,10 @@ int odp_pktout_queue_config(odp_pktio_t pktio,
* output. If return value (N) is less than 'num', only queues[0 ... N-1] have
* been written.
*
- * Packets (and other events) from these queues are received with
- * odp_queue_deq(), odp_schedule(), etc calls.
+ * In addition to packet input, application and other parts of ODP (e.g. timer)
+ * may enqueue events into these queues. Depending on the queue mode, application
+ * uses either odp_queue_deq() or odp_schedule() (or variants of those) to receive
+ * packets and other events from these queues.
*
* @param pktio Packet IO handle
* @param[out] queues Points to an array of queue handles for output
@@ -659,7 +258,7 @@ int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num);
*
* Packets are enqueued to these queues with odp_queue_enq() or
* odp_queue_enq_multi(). Behaviour is undefined if other events than packets
- * are enqueued.
+ * are enqueued. Application cannot dequeue from these queues.
*
* @param pktio Packet IO handle
* @param[out] queues Points to an array of queue handles for output
@@ -784,7 +383,6 @@ int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num);
* @param num Maximum number of packets to receive
* @param wait Wait time specified as as follows:
* * ODP_PKTIN_NO_WAIT: Do not wait
- * * ODP_PKTIN_WAIT: Wait infinitely
* * Other values specify the minimum time to wait.
* Use odp_pktin_wait_time() to convert nanoseconds
* to a valid parameter value. Wait time may be
@@ -823,7 +421,6 @@ int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[],
* @param num Maximum number of packets to receive
* @param wait Wait time specified as as follows:
* * ODP_PKTIN_NO_WAIT: Do not wait
- * * ODP_PKTIN_WAIT: Wait infinitely
* * Other values specify the minimum time to wait.
* Use odp_pktin_wait_time() to convert nanoseconds
* to a valid parameter value. Wait time may be
@@ -832,9 +429,8 @@ int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[],
* @return Number of packets received
* @retval <0 on failure
*/
-int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q,
- unsigned *from, odp_packet_t packets[], int num,
- uint64_t wait);
+int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], uint32_t num_q, uint32_t *from,
+ odp_packet_t packets[], int num, uint64_t wait);
/**
* Packet input wait time
@@ -855,35 +451,115 @@ uint64_t odp_pktin_wait_time(uint64_t nsec);
* the operation is optimized for single thread operation per queue and the same
* queue must not be accessed simultaneously from multiple threads.
*
- * A successful call returns the actual number of packets sent. If return value
- * is less than 'num', the remaining packets at the end of packets[] array
- * are not consumed, and the caller has to take care of them.
+ * A successful call returns the actual number of packets accepted for transmit. If return value
+ * is less than 'num', the remaining packets at the end of packets[] array are not consumed,
+ * and the caller has to take care of them. Transmitted packets are freed back into their
+ * originating pools by default. If interface supports #ODP_PACKET_FREE_CTRL_DONT_FREE
+ * option and it is set (odp_packet_free_ctrl_set()) in a packet, the packet is not freed
+ * but application owns it again after its transmit is complete. Application may use
+ * odp_packet_tx_compl_request() to request an indication when transmit of a packet is complete.
+ *
+ * Entire packet data is sent out (odp_packet_len() bytes of data, starting from
+ * odp_packet_data()). All other packet metadata is ignored unless otherwise
+ * specified e.g. for protocol offload purposes. Link protocol specific frame
+ * checksum and padding are added to frames before transmission.
*
* @param queue Packet output queue handle for sending packets
* @param packets[] Array of packets to send
* @param num Number of packets to send
*
- * @return Number of packets sent
+ * @return Number of packets accepted for transmit
* @retval <0 on failure
*/
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[],
int num);
/**
- * Return the currently configured MTU value of a packet IO interface.
+ * Initialize LSO profile parameters
*
- * @param[in] pktio Packet IO handle.
+ * Initialize an odp_lso_profile_param_t to its default values for all fields.
*
- * @return MTU value on success
- * @retval 0 on failure
+ * @param param Address of the odp_lso_profile_param_t to be initialized
+ */
+void odp_lso_profile_param_init(odp_lso_profile_param_t *param);
+
+/**
+ * Create LSO profile
+ *
+ * LSO profile defines the set of segmentation operations to be performed to a packet. LSO profiles
+ * are created before the packet IO interface is started (after odp_pktio_config() and before
+ * odp_pktio_start()).
+ *
+ * See odp_lso_capability_t for maximum number of profiles supported and other LSO capabilities.
+ *
+ * @param pktio Packet IO interface which is used with this LSO profile
+ * @param param LSO profile parameters
+ *
+ * @return LSO profile handle
+ * @retval ODP_LSO_PROFILE_INVALID on failure
*/
-uint32_t odp_pktio_mtu(odp_pktio_t pktio);
+odp_lso_profile_t odp_lso_profile_create(odp_pktio_t pktio, const odp_lso_profile_param_t *param);
/**
- * Enable/Disable promiscuous mode on a packet IO interface.
+ * Destroy LSO profile
+ *
+ * LSO profiles can be destroyed only when the packet IO interface is not active (i.e. after it
+ * has been stopped).
*
- * @param[in] pktio Packet IO handle.
- * @param[in] enable 1 to enable, 0 to disable.
+ * @param lso_profile LSO profile to be destroyed
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_lso_profile_destroy(odp_lso_profile_t lso_profile);
+
+/**
+ * Send packets with segmentation offload
+ *
+ * Like odp_pktout_send(), but splits a packet payload into 'max_payload_len' or smaller segments
+ * during output. Packet headers (before 'payload_offset') are copied into each segment and
+ * automatically modified before transmission. Header updates are based on segmentation protocol
+ * selection (odp_lso_profile_param_t::lso_proto) in LSO profile. Header checksums are updated
+ * after modifications. L3/L4 header modifications (see e.g. ODP_LSO_PROTO_TCP_IPV4) require that
+ * L3/L4 layer offsets in the packet are valid (see e.g. odp_packet_l3_offset()).
+ *
+ * In addition, custom field updates may be used to cover unsupported or proprietary protocols.
+ * Custom fields must not overlap with each other and can be used only when ODP_LSO_PROTO_CUSTOM
+ * is selected.
+ *
+ * Packets are processed and transmitted in the array order. Segments of each packet are transmitted
+ * in ascending order.
+ *
+ * When all packets share the same LSO options, usage of 'lso_opt' parameter may improve
+ * performance as a number of packet metadata writes/reads are avoided. Results are undefined if
+ * 'lso_opt' is NULL and a packet misses LSO options.
+ *
+ * Packets with less than (or equal to) 'max_payload_len' payload bytes can be sent also, however
+ * odp_pktout_send() should be more optimal for those than this function.
+ *
+ * Check LSO support level from packet IO capabilities (odp_pktio_capability_t).
+ *
+ * @param queue Packet output queue handle
+ * @param packet[] Array of packets to be LSO processed and sent
+ * @param num Number of packets
+ * @param lso_opt When set, LSO options to be used for all packets. When NULL, LSO options are
+ * read from each packet (see odp_packet_lso_request()).
+ *
+ * @return Number of packets successfully segmented (0 ... num)
+ * @retval <0 on failure
+ */
+int odp_pktout_send_lso(odp_pktout_queue_t queue, const odp_packet_t packet[], int num,
+ const odp_packet_lso_opt_t *lso_opt);
+
+/**
+ * Set promiscuous mode
+ *
+ * Enable or disable promiscuous mode on a packet IO interface. Use packet IO capability
+ * odp_pktio_set_op_t::promisc_mode to check if an interface supports this operation.
+ * When the operation is supported, promiscuous mode is disabled by default.
+ *
+ * @param pktio Packet IO handle.
+ * @param enable 1 to enable, 0 to disable.
*
* @retval 0 on success
* @retval <0 on failure
@@ -893,7 +569,7 @@ int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable);
/**
* Determine if promiscuous mode is enabled for a packet IO interface.
*
- * @param[in] pktio Packet IO handle.
+ * @param pktio Packet IO handle.
*
* @retval 1 if promiscuous mode is enabled.
* @retval 0 if promiscuous mode is disabled.
@@ -902,6 +578,62 @@ int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable);
int odp_pktio_promisc_mode(odp_pktio_t pktio);
/**
+ * Maximum frame length at packet input
+ *
+ * Maximum frame length in bytes that the packet IO interface can receive.
+ * For Ethernet, the frame length bytes start with MAC addresses and continue
+ * to the end of the payload. So, Ethernet checksum, interpacket gap
+ * and preamble bytes are excluded from the length.
+ *
+ * @param pktio Packet IO handle.
+ *
+ * @return Maximum frame length at packet input
+ * @retval 0 on failure
+ */
+uint32_t odp_pktin_maxlen(odp_pktio_t pktio);
+
+/**
+ * Maximum frame length at packet output
+ *
+ * Maximum frame length in bytes that the packet IO interface can transmit.
+ * For Ethernet, the frame length bytes start with MAC addresses and continue
+ * to the end of the payload. So, Ethernet checksum, interpacket gap
+ * and preamble bytes are excluded from the length.
+ *
+ * @param pktio Packet IO handle.
+ *
+ * @return Maximum frame length at packet output
+ * @retval 0 on failure
+ */
+uint32_t odp_pktout_maxlen(odp_pktio_t pktio);
+
+/**
+ * Set maximum frame lengths
+ *
+ * Set the maximum frame lengths in bytes that the packet IO interface can
+ * receive and transmit. For Ethernet, the frame length bytes start with MAC
+ * addresses and continue to the end of the payload. So, Ethernet checksum,
+ * interpacket gap, and preamble bytes are excluded from the lengths.
+ *
+ * Use odp_pktio_capability() to query interface capabilities. If setting
+ * maximum frame length is only supported in input or output direction, the
+ * parameter for the unsupported direction has to be set to zero. When
+ * 'equal' flag in odp_pktio_capability_t::maxlen is set, the same maximum
+ * frame length value has to be used for both input and output directions.
+ *
+ * @param pktio Packet IO handle
+ * @param maxlen_input Maximum frame length at packet input
+ * @param maxlen_output Maximum frame length at packet output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ *
+ * @see odp_pktin_maxlen(), odp_pktout_maxlen()
+ */
+int odp_pktio_maxlen_set(odp_pktio_t pktio, uint32_t maxlen_input,
+ uint32_t maxlen_output);
+
+/**
* Get the default MAC address of a packet IO interface.
*
* @param pktio Packet IO handle
@@ -914,16 +646,31 @@ int odp_pktio_promisc_mode(odp_pktio_t pktio);
int odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, int size);
/**
+ * Set the default MAC address of a packet IO interface.
+ *
+ * Support of this operation on a packet IO interface is reported
+ * through ‘mac_addr’ set operation capability.
+ *
+ * @param pktio Packet IO handle
+ * @param mac_addr MAC address to be set as default address
+ * @param size Size of the MAC address
+ *
+ * @return 0 on success
+ * @retval <0 on failure
+ */
+int odp_pktio_mac_addr_set(odp_pktio_t pktio, const void *mac_addr,
+ int size);
+
+/**
* Setup per-port default class-of-service.
*
- * @param[in] pktio Ingress port pktio handle.
- * @param[in] default_cos Class-of-service set to all packets arriving
- * at this ingress port,
- * unless overridden by subsequent
- * header-based filters.
+ * @param pktio Ingress port pktio handle.
+ * @param default_cos Class-of-service set to all packets arriving at this
+ * ingress port. Use ODP_COS_INVALID to remove the default
+ * CoS.
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*
* @note The default_cos has to be unique per odp_pktio_t instance.
*/
@@ -932,12 +679,12 @@ int odp_pktio_default_cos_set(odp_pktio_t pktio, odp_cos_t default_cos);
/**
* Setup per-port error class-of-service
*
- * @param[in] pktio Ingress port pktio handle.
- * @param[in] error_cos class-of-service set to all packets arriving
- * at this ingress port that contain an error.
+ * @param pktio Ingress port pktio handle.
+ * @param error_cos class-of-service set to all packets arriving at this
+ * ingress port that contain an error.
*
- * @retval 0 on success
- * @retval <0 on failure
+ * @retval 0 on success
+ * @retval <0 on failure
*
* @note Optional.
*/
@@ -946,24 +693,28 @@ int odp_pktio_error_cos_set(odp_pktio_t pktio, odp_cos_t error_cos);
/**
* Setup per-port header offset
*
- * @param[in] pktio Ingress port pktio handle.
- * @param[in] offset Number of bytes the classifier must skip.
+ * @param pktio Ingress port pktio handle.
+ * @param offset Number of bytes the classifier must skip.
*
- * @retval 0 on success
- * @retval <0 on failure
- * @note Optional.
+ * This option is input to packet input parser/classifier indicating
+ * how many bytes of data should be skipped from start of packet,
+ * before parsing starts. So this option effects all packet input
+ * protocol identification and other offloads.
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*
+ * @note Optional.
*/
int odp_pktio_skip_set(odp_pktio_t pktio, uint32_t offset);
/**
* Specify per-port buffer headroom
*
- * @param[in] pktio Ingress port pktio handle.
- * @param[in] headroom Number of bytes of space preceding
- * packet data to reserve for use as headroom.
- * Must not exceed the implementation
- * defined ODP_PACKET_MAX_HEADROOM.
+ * @param pktio Ingress port pktio handle.
+ * @param headroom Number of bytes of space preceding packet data to reserve
+ * for use as headroom. Must not exceed the implementation
+ * defined ODP_PACKET_MAX_HEADROOM.
*
* @retval 0 on success
* @retval <0 on failure
@@ -1045,21 +796,10 @@ void odp_pktio_print(odp_pktio_t pktio);
*
* @param pktio Packet IO handle.
*
- * @retval 1 link is up
- * @retval 0 link is down
- * @retval <0 on failure
+ * @retval ODP_PKTIO_LINK_STATUS_UP or ODP_PKTIO_LINK_STATUS_DOWN on success
+ * @retval ODP_PKTIO_LINK_STATUS_UNKNOWN on failure
*/
-int odp_pktio_link_status(odp_pktio_t pktio);
-
-/**
- * Packet IO information
- */
-typedef struct odp_pktio_info_t {
- const char *name; /**< Packet IO device name */
- const char *drv_name; /**< Packet IO driver name (implementation specific) */
- odp_pool_t pool; /**< Packet pool */
- odp_pktio_param_t param; /**< Packet IO parameters */
-} odp_pktio_info_t;
+odp_pktio_link_status_t odp_pktio_link_status(odp_pktio_t pktio);
/**
* Retrieve information about a pktio
@@ -1080,30 +820,80 @@ typedef struct odp_pktio_info_t {
int odp_pktio_info(odp_pktio_t pktio, odp_pktio_info_t *info);
/**
- * Packet input timestamp resolution in hertz
+ * Retrieve information about packet IO link status
+ *
+ * Fills in link information structure with the current link status values.
+ * May be called any time with a valid pktio handle. The call is not intended
+ * for fast path use. The info structure is written only on success.
+ *
+ * @param pktio Packet IO handle
+ * @param[out] info Pointer to packet IO link info struct for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pktio_link_info(odp_pktio_t pktio, odp_pktio_link_info_t *info);
+
+/**
+ * Packet IO timestamp resolution in hertz
*
- * This is the resolution of packet input timestamps. Returns zero on a failure
- * or when timestamping is disabled.
+ * This is the resolution of packet input and output timestamps using a packet
+ * IO time source.
*
* @param pktio Packet IO handle
*
- * @return Packet input timestamp resolution in hertz
+ * @return Packet IO timestamp resolution in hertz
* @retval 0 on failure
*/
-uint64_t odp_pktin_ts_res(odp_pktio_t pktio);
+uint64_t odp_pktio_ts_res(odp_pktio_t pktio);
/**
- * Convert nanoseconds to packet input time
+ * Convert nanoseconds to packet IO time
*
- * Packet input time source is used for timestamping incoming packets.
- * This function is used convert nanosecond time to packet input timestamp time.
+ * Packet IO time source is used for timestamping incoming and outgoing packets.
+ * This function is used to convert nanosecond time to packet input or output
+ * timestamp time.
*
* @param pktio Packet IO handle
* @param ns Time in nanoseconds
*
- * @return Packet input timestamp
+ * @return Packet IO timestamp
+ * @retval ODP_TIME_NULL on failure
+ */
+odp_time_t odp_pktio_ts_from_ns(odp_pktio_t pktio, uint64_t ns);
+
+/**
+ * Current packet IO time and global time
+ *
+ * Returns current packet IO time and optionally global time. The returned
+ * global time is that of global time source, where as the packet IO time is of
+ * packet IO time source that is used to timestamp incoming and outgoing
+ * packets.
+ *
+ * @param pktio Packet IO handle
+ * @param[out] ts_global Pointer to odp_time_t for output or NULL.
+ * On success, global timestamp will be taken at the
+ * same point of time as packet IO time.
+ *
+ * @return Current packet IO time
+ * @retval ODP_TIME_NULL on failure
+ */
+odp_time_t odp_pktio_time(odp_pktio_t pktio, odp_time_t *ts_global);
+
+/**
+ * Read last captured Tx timestamp of a packet if available and clear it for
+ * next timestamp.
+ *
+ * @param pktio Packet IO handle
+ * @param[out] ts Pointer to odp_time_t for output
+ *
+ * @retval 0 on success
+ * @retval >0 Timestamp not available either because none has been requested or
+ * the requested timestamp is not yet available. In case it is the
+ * latter, then retry again later for retrieving the timestamp.
+ * @retval <0 on failure
*/
-odp_time_t odp_pktin_ts_from_ns(odp_pktio_t pktio, uint64_t ns);
+int odp_pktout_ts_read(odp_pktio_t pktio, odp_time_t *ts);
/**
* @}
diff --git a/include/odp/api/spec/packet_io_stats.h b/include/odp/api/spec/packet_io_stats.h
index 299ecd0e1..d711d75f7 100644
--- a/include/odp/api/spec/packet_io_stats.h
+++ b/include/odp/api/spec/packet_io_stats.h
@@ -1,137 +1,429 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
*/
/**
* @file
*
- * ODP Packet IO
+ * ODP Packet IO statistics
*/
-#ifndef ODP_API_PACKET_IO_STATS_H_
-#define ODP_API_PACKET_IO_STATS_H_
+#ifndef ODP_API_SPEC_PACKET_IO_STATS_H_
+#define ODP_API_SPEC_PACKET_IO_STATS_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
+#include <odp/api/queue_types.h>
+
/** @addtogroup odp_packet_io
* @{
*/
/**
- * Packet IO statistics
+ * @def ODP_PKTIO_STATS_EXTRA_NAME_LEN
+ * Maximum packet IO extra statistics counter name length in chars including
+ * null char
+ */
+
+/**
+ * Packet IO statistics counters
*
- * Packet IO statistics counters follow RFCs for Management Information Base
- * (MIB)for use with network management protocols in the Internet community:
- * https://tools.ietf.org/html/rfc3635
- * https://tools.ietf.org/html/rfc2863
- * https://tools.ietf.org/html/rfc2819
+ * In the counter definitions the term successfully refers to packets which were
+ * not discarded or detected to contain errors by the packet IO interface. In
+ * case of Ethernet, it's implementation specific whether valid pause frames are
+ * included in the counters or not.
*/
typedef struct odp_pktio_stats_t {
- /**
- * The number of octets in valid MAC frames received on this interface,
- * including the MAC header and FCS. See ifHCInOctets counter
- * description in RFC 3635 for details.
- */
+ /** Number of octets in successfully received packets. In case of
+ * Ethernet, packet size includes MAC header. */
uint64_t in_octets;
- /**
- * The number of packets, delivered by this sub-layer to a higher
- * (sub-)layer, which were not addressed to a multicast or broadcast
- * address at this sub-layer. See ifHCInUcastPkts in RFC 2863, RFC 3635.
- */
+ /** Number of successfully received packets. */
+ uint64_t in_packets;
+
+ /** Number of successfully received Ethernet packets with a unicast
+ * destination MAC address. */
uint64_t in_ucast_pkts;
- /**
- * The number of inbound packets which were chosen to be discarded
- * even though no errors had been detected to prevent their being
- * deliverable to a higher-layer protocol. One possible reason for
- * discarding such a packet could be to free up buffer space.
- * See ifInDiscards in RFC 2863.
- */
+ /** Number of successfully received Ethernet packets with a multicast
+ * destination MAC address. */
+ uint64_t in_mcast_pkts;
+
+ /** Number of successfully received Ethernet packets with a broadcast
+ * destination MAC address. */
+ uint64_t in_bcast_pkts;
+
+ /** Number of inbound packets which were discarded due to a lack of free
+ * resources (e.g. buffers) or other reasons than packet errors. */
uint64_t in_discards;
- /**
- * The sum for this interface of AlignmentErrors, FCSErrors, FrameTooLongs,
- * InternalMacReceiveErrors. See ifInErrors in RFC 3635.
- */
+ /** Number of inbound packets with errors. Depending on packet input
+ * configuration, packets with errors may be dropped or not. */
uint64_t in_errors;
- /**
- * For packet-oriented interfaces, the number of packets received via
- * the interface which were discarded because of an unknown or
- * unsupported protocol. For character-oriented or fixed-length
- * interfaces that support protocol multiplexing the number of
- * transmission units received via the interface which were discarded
- * because of an unknown or unsupported protocol. For any interface
- * that does not support protocol multiplexing, this counter will always
- * be 0. See ifInUnknownProtos in RFC 2863, RFC 3635.
- */
- uint64_t in_unknown_protos;
-
- /**
- * The number of octets transmitted in valid MAC frames on this
- * interface, including the MAC header and FCS. This does include
- * the number of octets in valid MAC Control frames transmitted on
- * this interface. See ifHCOutOctets in RFC 3635.
- */
+ /** Number of octets in successfully transmitted packets. In case of
+ * Ethernet, packet size includes MAC header. */
uint64_t out_octets;
- /**
- * The total number of packets that higher-level protocols requested
- * be transmitted, and which were not addressed to a multicast or
- * broadcast address at this sub-layer, including those that were
- * discarded or not sent. does not include MAC Control frames.
- * See ifHCOutUcastPkts RFC 2863, 3635.
- */
+ /** Number of successfully transmitted packets. */
+ uint64_t out_packets;
+
+ /** Number of successfully transmitted Ethernet packets with a unicast
+ * destination MAC address. */
uint64_t out_ucast_pkts;
- /**
- * The number of outbound packets which were chosen to be discarded
- * even though no errors had been detected to prevent their being
- * transmitted. One possible reason for discarding such a packet could
- * be to free up buffer space. See OutDiscards in RFC 2863.
- */
+ /** Number of successfully transmitted Ethernet packets with a multicast
+ * destination MAC address. */
+ uint64_t out_mcast_pkts;
+
+ /** Number of successfully transmitted Ethernet packets with a broadcast
+ * destination MAC address. */
+ uint64_t out_bcast_pkts;
+
+ /** Number of outbound packets which were discarded due to a lack of
+ * free resources (e.g. buffers) or other reasons than errors. */
uint64_t out_discards;
- /**
- * The sum for this interface of SQETestErrors, LateCollisions,
- * ExcessiveCollisions, InternalMacTransmitErrors and
- * CarrierSenseErrors. See ifOutErrors in RFC 3635.
- */
+ /** Number of packets with transmission errors. */
uint64_t out_errors;
} odp_pktio_stats_t;
/**
+ * Packet IO input queue specific statistics counters
+ *
+ * Statistics counters for an individual packet input queue. Refer to packet IO
+ * level statistics odp_pktio_stats_t for counter definitions.
+ */
+typedef struct odp_pktin_queue_stats_t {
+ /** See odp_pktio_stats_t::in_octets */
+ uint64_t octets;
+
+ /** See odp_pktio_stats_t::in_packets */
+ uint64_t packets;
+
+ /** See odp_pktio_stats_t::in_discards */
+ uint64_t discards;
+
+ /** See odp_pktio_stats_t::in_errors */
+ uint64_t errors;
+
+} odp_pktin_queue_stats_t;
+
+/**
+ * Packet IO output queue specific statistics counters
+ *
+ * Statistics counters for an individual packet output queue. Refer to packet IO
+ * level statistics odp_pktio_stats_t for counter definitions.
+ */
+typedef struct odp_pktout_queue_stats_t {
+ /** See odp_pktio_stats_t::out_octets */
+ uint64_t octets;
+
+ /** See odp_pktio_stats_t::out_packets */
+ uint64_t packets;
+
+ /** See odp_pktio_stats_t::out_discards */
+ uint64_t discards;
+
+ /** See odp_pktio_stats_t::out_errors */
+ uint64_t errors;
+
+} odp_pktout_queue_stats_t;
+
+/**
+ * Packet IO statistics capabilities
+ */
+typedef struct odp_pktio_stats_capability_t {
+ /** Interface level capabilities */
+ struct {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_pktio_stats_t::in_octets */
+ uint64_t in_octets : 1;
+
+ /** See odp_pktio_stats_t::in_packets */
+ uint64_t in_packets : 1;
+
+ /** See odp_pktio_stats_t::in_ucast_pkts */
+ uint64_t in_ucast_pkts : 1;
+
+ /** See odp_pktio_stats_t::in_mcast_pkts */
+ uint64_t in_mcast_pkts : 1;
+
+ /** See odp_pktio_stats_t::in_bcast_pkts */
+ uint64_t in_bcast_pkts : 1;
+
+ /** See odp_pktio_stats_t::in_discards */
+ uint64_t in_discards : 1;
+
+ /** See odp_pktio_stats_t::in_errors */
+ uint64_t in_errors : 1;
+
+ /** See odp_pktio_stats_t::out_octets */
+ uint64_t out_octets : 1;
+
+ /** See odp_pktio_stats_t::out_packets */
+ uint64_t out_packets : 1;
+
+ /** See odp_pktio_stats_t::out_ucast_pkts */
+ uint64_t out_ucast_pkts : 1;
+
+ /** See odp_pktio_stats_t::out_mcast_pkts */
+ uint64_t out_mcast_pkts : 1;
+
+ /** See odp_pktio_stats_t::out_bcast_pkts */
+ uint64_t out_bcast_pkts : 1;
+
+ /** See odp_pktio_stats_t::out_discards */
+ uint64_t out_discards : 1;
+
+ /** See odp_pktio_stats_t::out_errors */
+ uint64_t out_errors : 1;
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+ } pktio;
+
+ /** Input queue level capabilities */
+ struct {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_pktin_queue_stats_t::octets */
+ uint64_t octets : 1;
+
+ /** See odp_pktin_queue_stats_t::packets */
+ uint64_t packets : 1;
+
+ /** See odp_pktin_queue_stats_t::discards */
+ uint64_t discards : 1;
+
+ /** See odp_pktin_queue_stats_t::errors */
+ uint64_t errors : 1;
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+ } pktin_queue;
+
+ /** Output queue level capabilities */
+ struct {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_pktout_queue_stats_t::octets */
+ uint64_t octets : 1;
+
+ /** See odp_pktout_queue_stats_t::packets */
+ uint64_t packets : 1;
+
+ /** See odp_pktout_queue_stats_t::discards */
+ uint64_t discards : 1;
+
+ /** See odp_pktout_queue_stats_t::errors */
+ uint64_t errors : 1;
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+ } pktout_queue;
+
+} odp_pktio_stats_capability_t;
+
+/**
+ * Packet IO extra statistics counter information
+ */
+typedef struct odp_pktio_extra_stat_info_t {
+ /** Name of the counter */
+ char name[ODP_PKTIO_STATS_EXTRA_NAME_LEN];
+
+} odp_pktio_extra_stat_info_t;
+
+/**
* Get statistics for pktio handle
*
- * @param pktio Packet IO handle
- * @param[out] stats Output buffer for counters
+ * Counters not supported by the interface are set to zero.
+ *
+ * @param pktio Packet IO handle
+ * @param[out] stats Output buffer for counters
+ *
* @retval 0 on success
* @retval <0 on failure
+ */
+int odp_pktio_stats(odp_pktio_t pktio, odp_pktio_stats_t *stats);
+
+/**
+ * Get statistics for direct packet input queue
+ *
+ * Packet input queue handles can be requested with odp_pktin_queue(). Counters
+ * not supported by the interface are set to zero.
*
- * @note: If counter is not supported by platform it has
- * to be set to 0.
+ * @param queue Packet input queue handle
+ * @param[out] stats Output buffer for counters
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-int odp_pktio_stats(odp_pktio_t pktio,
- odp_pktio_stats_t *stats);
+int odp_pktin_queue_stats(odp_pktin_queue_t queue,
+ odp_pktin_queue_stats_t *stats);
/**
- * Reset statistics for pktio handle
+ * Get statistics for packet input event queue
+ *
+ * The queue must be a packet input event queue. Event queue handles can be
+ * requested with odp_pktin_event_queue(). Counters not supported by the
+ * interface are set to zero.
+ *
+ * @param pktio Packet IO handle
+ * @param queue Packet input event queue handle
+ * @param[out] stats Output buffer for counters
*
- * Reset all pktio counters to 0.
- * @param pktio Packet IO handle
* @retval 0 on success
* @retval <0 on failure
+ */
+int odp_pktin_event_queue_stats(odp_pktio_t pktio, odp_queue_t queue,
+ odp_pktin_queue_stats_t *stats);
+
+/**
+ * Get statistics for direct packet output queue
+ *
+ * Packet output queue handles can be requested with odp_pktout_queue().
+ * Counters not supported by the interface are set to zero.
*
+ * @param queue Packet output queue handle
+ * @param[out] stats Output buffer for counters
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pktout_queue_stats(odp_pktout_queue_t queue,
+ odp_pktout_queue_stats_t *stats);
+
+/**
+ * Get statistics for packet output event queue
+ *
+ * The queue must be a packet output event queue. Event queue handles can be
+ * requested with odp_pktout_event_queue(). Counters not supported by the
+ * interface are set to zero.
+ *
+ * @param pktio Packet IO handle
+ * @param queue Packet output event queue handle
+ * @param[out] stats Output buffer for counters
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pktout_event_queue_stats(odp_pktio_t pktio, odp_queue_t queue,
+ odp_pktout_queue_stats_t *stats);
+
+/**
+ * Reset statistics for pktio handle
+ *
+ * Reset all interface level statistics counters (odp_pktio_stats_t) to zero.
+ * It's implementation defined if other packet IO related statistics are
+ * affected.
+ *
+ * @param pktio Packet IO handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
int odp_pktio_stats_reset(odp_pktio_t pktio);
/**
+ * Get extra statistics counter information for a packet IO interface
+ *
+ * Returns the number of implementation specific packet IO extra statistics
+ * counters supported by the interface. Outputs up to 'num' extra statistics
+ * counter info structures when the 'info' array pointer is not NULL. If the
+ * return value is larger than 'num', there are more extra counters than the
+ * function was allowed to output. If the return value (N) is less than 'num',
+ * only info[0 ... N-1] have been written.
+ *
+ * The index of a counter in the 'info' array can be used to read the value of
+ * the individual counter with odp_pktio_extra_stat_counter(). The order of
+ * counters in the output array matches with odp_pktio_extra_stats().
+ *
+ * @param pktio Packet IO handle
+ * @param[out] info Array of extra statistics info structs for output
+ * @param num Maximum number of info structs to output
+ *
+ * @return Number of extra statistics
+ * @retval <0 on failure
+ */
+int odp_pktio_extra_stat_info(odp_pktio_t pktio,
+ odp_pktio_extra_stat_info_t info[], int num);
+
+/**
+ * Get extra statistics for a packet IO interface
+ *
+ * Returns the number of implementation specific packet IO extra statistics
+ * counters supported by the interface. Outputs up to 'num' counters when the
+ * 'stats' array pointer is not NULL. If the return value is larger than 'num',
+ * there are more counters than the function was allowed to output. If the
+ * return value (N) is less than 'num', only stats[0 ... N-1] have been written.
+ *
+ * The index of a counter in the 'stats' array can be used to read the value of
+ * the individual counter with odp_pktio_extra_stat_counter(). The order of
+ * counters in the output array matches with odp_pktio_extra_stat_info().
+ *
+ * @param pktio Packet IO handle
+ * @param[out] stats Array of extra statistics for output
+ * @param num Maximum number of extra statistics to output
+ *
+ * @return Number of extra statistics
+ * @retval <0 on failure
+ */
+int odp_pktio_extra_stats(odp_pktio_t pktio, uint64_t stats[], int num);
+
+/**
+ * Get extra statistic counter value
+ *
+ * 'id' is the index of the particular counter in the output array of
+ * odp_pktio_extra_stat_info() or odp_pktio_extra_stats().
+ *
+ *
+ * @param pktio Packet IO handle
+ * @param id ID of the extra statistics counter
+ * @param[out] stat Pointer for statistic counter output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pktio_extra_stat_counter(odp_pktio_t pktio, uint32_t id,
+ uint64_t *stat);
+
+/**
+ * Print extra statistics for a packet IO interface
+ *
+ * Print all packet IO device extra statistics to ODP log.
+ *
+ * @param pktio Packet IO handle
+ */
+void odp_pktio_extra_stats_print(odp_pktio_t pktio);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/packet_io_types.h b/include/odp/api/spec/packet_io_types.h
new file mode 100644
index 000000000..9e56e087a
--- /dev/null
+++ b/include/odp/api/spec/packet_io_types.h
@@ -0,0 +1,1253 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO types
+ */
+
+#ifndef ODP_API_SPEC_PACKET_IO_TYPES_H_
+#define ODP_API_SPEC_PACKET_IO_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/packet_types.h>
+#include <odp/api/packet_io_stats.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/reassembly.h>
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_packet_io ODP PACKET IO
+ * @{
+ */
+
+/**
+ * @typedef odp_pktio_t
+ * Packet IO handle
+ */
+
+/**
+ * @typedef odp_pktin_queue_t
+ * Direct packet input queue handle
+ */
+
+/**
+ * @typedef odp_pktout_queue_t
+ * Direct packet output queue handle
+ */
+
+/**
+ * @typedef odp_lso_profile_t
+ * LSO profile handle
+ */
+
+/**
+ * @def ODP_PKTIO_INVALID
+ * Invalid packet IO handle
+ */
+
+/**
+ * @def ODP_LSO_PROFILE_INVALID
+ * Invalid LSO profile handle
+ */
+
+/**
+ * @def ODP_PKTIO_MAX_INDEX
+ * Maximum packet IO interface index. Use odp_pktio_max_index() to check the
+ * runtime maximum value, which may be smaller than this value.
+ */
+
+/**
+ * @def ODP_PKTIO_MACADDR_MAXSIZE
+ * Minimum size of output buffer for odp_pktio_mac_addr()
+ * Actual MAC address sizes may be different.
+ */
+
+/**
+ * @def ODP_PKTIN_NO_WAIT
+ * Do not wait on packet input
+ */
+
+/**
+ * @def ODP_PKTIN_MAX_QUEUES
+ * Maximum number of packet input queues supported by the API. Use
+ * odp_pktio_capability() to check the maximum number of queues per interface.
+ */
+
+/**
+ * @def ODP_PKTOUT_MAX_QUEUES
+ * Maximum number of packet output queues supported by the API. Use
+ * odp_pktio_capability() to check the maximum number of queues per interface.
+ */
+
+/**
+ * Packet input mode
+ */
+typedef enum odp_pktin_mode_t {
+ /** Direct packet input from the interface */
+ ODP_PKTIN_MODE_DIRECT = 0,
+ /** Packet input through scheduler and scheduled event queues */
+ ODP_PKTIN_MODE_SCHED,
+ /** Packet input through plain event queues */
+ ODP_PKTIN_MODE_QUEUE,
+ /** Application will never receive from this interface */
+ ODP_PKTIN_MODE_DISABLED
+} odp_pktin_mode_t;
+
+/**
+ * Packet output mode
+ */
+typedef enum odp_pktout_mode_t {
+ /** Direct packet output on the interface */
+ ODP_PKTOUT_MODE_DIRECT = 0,
+ /** Packet output through event queues */
+ ODP_PKTOUT_MODE_QUEUE,
+ /** Packet output through traffic manager API */
+ ODP_PKTOUT_MODE_TM,
+ /** Application will never send to this interface */
+ ODP_PKTOUT_MODE_DISABLED
+} odp_pktout_mode_t;
+
+/**
+ * Packet input hash protocols
+ *
+ * The list of protocol header field combinations, which are included into
+ * packet input hash calculation.
+ */
+typedef union odp_pktin_hash_proto_t {
+ /** Protocol header fields for hashing */
+ struct {
+ /** IPv4 addresses and UDP port numbers */
+ uint32_t ipv4_udp : 1;
+ /** IPv4 addresses and TCP port numbers */
+ uint32_t ipv4_tcp : 1;
+ /** IPv4 addresses */
+ uint32_t ipv4 : 1;
+ /** IPv6 addresses and UDP port numbers */
+ uint32_t ipv6_udp : 1;
+ /** IPv6 addresses and TCP port numbers */
+ uint32_t ipv6_tcp : 1;
+ /** IPv6 addresses */
+ uint32_t ipv6 : 1;
+ } proto;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all bits, or to perform bitwise
+ * operations over those. */
+ uint32_t all_bits;
+} odp_pktin_hash_proto_t;
+
+/**
+ * Packet IO operation mode
+ */
+typedef enum odp_pktio_op_mode_t {
+ /** Multithread safe operation
+ *
+ * Direct packet IO operation (recv or send) is multithread safe. Any
+ * number of application threads may perform the operation
+ * concurrently. */
+ ODP_PKTIO_OP_MT = 0,
+
+ /** Not multithread safe operation
+ *
+ * Direct packet IO operation (recv or send) may not be multithread
+ * safe. Application ensures synchronization between threads so that
+ * simultaneously only single thread attempts the operation on
+ * the same (pktin or pktout) queue. */
+ ODP_PKTIO_OP_MT_UNSAFE
+
+} odp_pktio_op_mode_t;
+
+/**
+ * Packet input queue parameters override
+ */
+typedef struct odp_pktin_queue_param_ovr_t {
+ /** Override for schedule group in odp_schedule_param_t
+ *
+ * This parameter is considered only when queue type is
+ * ODP_QUEUE_TYPE_SCHED. */
+ odp_schedule_group_t group;
+} odp_pktin_queue_param_ovr_t;
+
+/**
+ * Packet input vector configuration
+ */
+typedef struct odp_pktin_vector_config_t {
+ /** Enable packet input vector
+ *
+ * When true, packet input vector is enabled and configured with vector
+ * config parameters. Otherwise, packet input vector configuration
+ * parameters are ignored. When vectors are enabled, packets may
+ * be delivered both as packet vector events and packet events.
+ * The default value is false.
+ */
+ odp_bool_t enable;
+
+ /** Vector pool
+ *
+ * Vector pool to allocate the vectors to hold packets.
+ * The pool must have been created with the ODP_POOL_VECTOR type.
+ */
+ odp_pool_t pool;
+
+ /** Maximum time to wait for packets
+ *
+ * Maximum timeout in nanoseconds to wait for the producer to form the
+ * vector of packet events (odp_packet_vector_t). This value should be
+ * in the range of odp_pktin_vector_capability_t::min_tmo_ns to
+ * odp_pktin_vector_capability_t::max_tmo_ns.
+ */
+ uint64_t max_tmo_ns;
+
+ /** Maximum number of packets in a vector
+ *
+ * The packet input subsystem forms packet vector events when either
+ * it reaches odp_pktin_vector_config_t::max_tmo_ns or producer reaches
+ * max_size packets. This value should be in the range of
+ * odp_pktin_vector_capability_t::min_size to
+ * odp_pktin_vector_capability_t::max_size.
+ *
+ * @note The maximum number of packets this vector can hold is defined
+ * by odp_pool_param_t::vector::max_size with odp_pktin_vector_config_t::pool.
+ * The max_size should not be greater than odp_pool_param_t::vector::max_size.
+ */
+ uint32_t max_size;
+
+} odp_pktin_vector_config_t;
+
+/**
+ * Packet input queue parameters
+ */
+typedef struct odp_pktin_queue_param_t {
+ /** Operation mode
+ *
+ * The default value is ODP_PKTIO_OP_MT. Application may enable
+ * performance optimization by defining ODP_PKTIO_OP_MT_UNSAFE when
+ * applicable. */
+ odp_pktio_op_mode_t op_mode;
+
+ /** Enable classifier
+ *
+ * * 0: Classifier is disabled (default)
+ * * 1: Classifier is enabled. Use classifier to direct incoming
+ * packets into pktin event queues. Classifier can be enabled
+ * only in ODP_PKTIN_MODE_SCHED and ODP_PKTIN_MODE_QUEUE modes.
+ * Both classifier and hashing cannot be enabled simultaneously
+ * ('hash_enable' must be 0). */
+ odp_bool_t classifier_enable;
+
+ /** Enable flow hashing
+ *
+ * * 0: Do not hash flows (default)
+ * * 1: Enable flow hashing. Use flow hashing to spread incoming
+ * packets into input queues. Hashing can be enabled in all
+ * modes. Both classifier and hashing cannot be enabled
+ * simultaneously ('classifier_enable' must be 0). */
+ odp_bool_t hash_enable;
+
+ /** Protocol field selection for hashing
+ *
+ * Multiple protocols can be selected. Ignored when 'hash_enable' is
+ * zero. The default value is all bits zero. */
+ odp_pktin_hash_proto_t hash_proto;
+
+ /** Number of input queues to be created
+ *
+ * When classifier is enabled in odp_pktin_queue_config() this
+ * value is ignored, otherwise at least one queue is required.
+ * More than one input queues require flow hashing configured.
+ * The maximum value is defined by pktio capability 'max_input_queues'.
+ * Queue type is defined by the input mode. The default value is 1. */
+ uint32_t num_queues;
+
+ /** Input queue size array
+ *
+ * An array containing queue sizes for each 'num_queues' input queues
+ * in ODP_PKTIN_MODE_DIRECT mode. The value of zero means
+ * implementation specific default size. Nonzero values must be between
+ * 'min_input_queue_size' and 'max_input_queue_size' capabilities. The
+ * implementation may round-up given values. The default value is zero.
+ */
+ uint32_t queue_size[ODP_PKTIN_MAX_QUEUES];
+
+ /** Queue parameters
+ *
+ * These are used for input queue creation in ODP_PKTIN_MODE_QUEUE
+ * or ODP_PKTIN_MODE_SCHED modes. Scheduler parameters are considered
+ * only in ODP_PKTIN_MODE_SCHED mode. Default values are defined in
+ * odp_queue_param_t documentation. The type field is ignored
+ * and the queue type is deduced from the pktio input mode.
+ * When classifier is enabled in odp_pktin_queue_config() this
+ * value is ignored. */
+ odp_queue_param_t queue_param;
+
+ /** Queue parameters override
+ *
+ * When the override array is defined, the same parameter value
+ * in 'queue_param' is ignored and these per queue parameter
+ * values are used instead. Array elements are used in order
+ * (i.e. the first queue gets parameters from the first array
+ * element, etc).
+ * Must point to an array of num_queues elements or NULL to
+ * disable queue parameters override. The default value is
+ * NULL.
+ */
+ odp_pktin_queue_param_ovr_t *queue_param_ovr;
+
+ /** Packet input vector configuration */
+ odp_pktin_vector_config_t vector;
+
+} odp_pktin_queue_param_t;
+
+/**
+ * Packet output queue parameters
+ *
+ * These parameters are used in ODP_PKTOUT_MODE_DIRECT and
+ * ODP_PKTOUT_MODE_QUEUE modes.
+ */
+typedef struct odp_pktout_queue_param_t {
+ /** Operation mode
+ *
+ * The default value is ODP_PKTIO_OP_MT. Application may enable
+ * performance optimization by defining ODP_PKTIO_OP_MT_UNSAFE when
+ * applicable. */
+ odp_pktio_op_mode_t op_mode;
+
+ /** Number of output queues to be created. The value must be between
+ * 1 and interface capability. The default value is 1. */
+ uint32_t num_queues;
+
+ /** Output queue size array
+ *
+ * An array containing queue sizes for each 'num_queues' output queues.
+ * The value of zero means implementation specific default size.
+ * Nonzero values must be between 'min_output_queue_size' and
+ * 'max_output_queue_size' capabilities. The implementation may
+ * round-up given values. The default value is zero.
+ */
+ uint32_t queue_size[ODP_PKTOUT_MAX_QUEUES];
+
+} odp_pktout_queue_param_t;
+
+/**
+ * Packet IO parameters
+ *
+ * Packet IO interface level parameters. Use odp_pktio_param_init() to
+ * initialize the structure with default values.
+ */
+typedef struct odp_pktio_param_t {
+ /** Packet input mode
+ *
+ * The default value is ODP_PKTIN_MODE_DIRECT. */
+ odp_pktin_mode_t in_mode;
+
+ /** Packet output mode
+ *
+ * The default value is ODP_PKTOUT_MODE_DIRECT. */
+ odp_pktout_mode_t out_mode;
+
+} odp_pktio_param_t;
+
+/**
+ * Packet input configuration options bit field
+ *
+ * Packet input configuration options listed in a bit field structure. Packet
+ * input timestamping may be enabled for all packets or at least for those that
+ * belong to time synchronization protocol (PTP).
+ *
+ * Packet input checksum checking may be enabled or disabled. When it is
+ * enabled, implementation will attempt to verify checksum correctness on
+ * incoming packets and depending on drop configuration either deliver erroneous
+ * packets with appropriate flags set (e.g. odp_packet_has_l3_error(),
+ * odp_packet_l3_chksum_status()) or drop those. When packet dropping is
+ * enabled, application will never receive a packet with the specified error
+ * and may avoid to check the error flag.
+ *
+ * If checksum checking is enabled, IPv4 header checksum checking is always
+ * done for packets that do not have IP options and L4 checksum checking
+ * is done for unfragmented packets that do not have IPv4 options or IPv6
+ * extension headers. In other cases checksum checking may or may not
+ * be done. For example, L4 checksum of fragmented packets is typically
+ * not checked.
+ *
+ * IPv4 checksum checking may be enabled only when parsing level is
+ * ODP_PROTO_LAYER_L3 or higher. Similarly, L4 level checksum checking
+ * may be enabled only with parsing level ODP_PROTO_LAYER_L4 or higher.
+ *
+ * Whether checksum checking was done and whether a checksum was correct
+ * can be queried for each received packet with odp_packet_l3_chksum_status()
+ * and odp_packet_l4_chksum_status().
+ */
+typedef union odp_pktin_config_opt_t {
+ /** Option flags */
+ struct {
+ /** Timestamp all packets on packet input */
+ uint64_t ts_all : 1;
+
+ /** Timestamp (at least) IEEE1588 / PTP packets
+ * on packet input */
+ uint64_t ts_ptp : 1;
+
+ /** Check IPv4 header checksum on packet input */
+ uint64_t ipv4_chksum : 1;
+
+ /** Check UDP checksum on packet input */
+ uint64_t udp_chksum : 1;
+
+ /** Check TCP checksum on packet input */
+ uint64_t tcp_chksum : 1;
+
+ /** Check SCTP checksum on packet input */
+ uint64_t sctp_chksum : 1;
+
+ /** Drop packets with an IPv4 error on packet input */
+ uint64_t drop_ipv4_err : 1;
+
+ /** Drop packets with an IPv6 error on packet input */
+ uint64_t drop_ipv6_err : 1;
+
+ /** Drop packets with a UDP error on packet input */
+ uint64_t drop_udp_err : 1;
+
+ /** Drop packets with a TCP error on packet input */
+ uint64_t drop_tcp_err : 1;
+
+ /** Drop packets with a SCTP error on packet input */
+ uint64_t drop_sctp_err : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint64_t all_bits;
+} odp_pktin_config_opt_t;
+
+/**
+ * Packet output configuration options bit field
+ *
+ * Packet output configuration options listed in a bit field structure. Packet
+ * output checksum insertion may be enabled or disabled (e.g. ipv4_chksum_ena):
+ *
+ * 0: Disable checksum insertion. Application will not request checksum
+ * insertion for any packet. This is the default value for xxx_chksum_ena
+ * bits.
+ * 1: Enable checksum insertion. Application will request checksum insertion
+ * for some packets.
+ *
+ * When checksum insertion is enabled, application may use configuration options
+ * to set the default behaviour on packet output (e.g. ipv4_chksum):
+ *
+ * 0: Do not insert checksum by default. This is the default value for
+ * xxx_chksum bits.
+ * 1: Calculate and insert checksum by default.
+ *
+ * These defaults may be overridden on per packet basis using e.g.
+ * odp_packet_l4_chksum_insert().
+ *
+ * For correct operation, packet metadata must provide valid offsets and type
+ * flags for the appropriate layer 3 and layer 4 protocols. L3 and L4 offsets
+ * can be updated with odp_packet_l3_offset_set() and odp_packet_l4_offset_set()
+ * calls. L3 and L4 type flags can be updated using odp_packet_has_*_set() calls
+ * For example, UDP checksum calculation needs both L3 and L4 types (IP and UDP) and
+ * L3 and L4 offsets (to access IP and UDP headers), while IP checksum
+ * calculation only needs L3 type (IP) and L3 offset (to access IP header).
+ * When application (e.g. a switch) does not modify L3/L4 data and thus checksum
+ * does not need to be updated, checksum insertion should be disabled for optimal
+ * performance.
+ *
+ * UDP, TCP and SCTP checksum insertion must not be requested for IP fragments.
+ * Use checksum override function (odp_packet_l4_chksum_insert()) to disable
+ * checksumming when sending a fragment through a packet IO interface that has
+ * the relevant L4 checksum insertion enabled.
+ *
+ * Result of checksum insertion at packet output is undefined if the protocol
+ * headers required for checksum calculation are not well formed. Packet must
+ * contain at least as many data bytes after L3/L4 offsets as the headers
+ * indicate. Other data bytes of the packet are ignored for the checksum
+ * insertion.
+ */
+typedef union odp_pktout_config_opt_t {
+ /** Option flags for packet output */
+ struct {
+ /** Enable Tx timestamp capture */
+ uint64_t ts_ena : 1;
+
+ /** Enable IPv4 header checksum insertion */
+ uint64_t ipv4_chksum_ena : 1;
+
+ /** Enable UDP checksum insertion */
+ uint64_t udp_chksum_ena : 1;
+
+ /** Enable TCP checksum insertion */
+ uint64_t tcp_chksum_ena : 1;
+
+ /** Enable SCTP checksum insertion */
+ uint64_t sctp_chksum_ena : 1;
+
+ /** Insert IPv4 header checksum by default */
+ uint64_t ipv4_chksum : 1;
+
+ /** Insert UDP checksum on packet by default */
+ uint64_t udp_chksum : 1;
+
+ /** Insert TCP checksum on packet by default */
+ uint64_t tcp_chksum : 1;
+
+ /** Insert SCTP checksum on packet by default */
+ uint64_t sctp_chksum : 1;
+
+ /** Packet references not used on packet output
+ *
+ * When set, application indicates that it will not transmit
+ * packet references on this packet IO interface.
+ * Since every ODP implementation supports it, it is always
+ * ok to set this flag.
+ *
+ * 0: Packet references may be transmitted on the
+ * interface (the default value).
+ * 1: Packet references will not be transmitted on the
+ * interface.
+ */
+ uint64_t no_packet_refs : 1;
+
+ /** Enable packet aging and drop
+ *
+ * 0: application will not request packet aging (default)
+ * 1: application may request packet aging
+ */
+ uint64_t aging_ena : 1;
+
+ /**
+ * For backwards compatibility, setting this flag is the same as setting
+ * tx_compl.mode_event in odp_pktio_config_t. The default value is zero.
+ *
+ * @deprecated Use odp_pktio_config_t::mode_event instead.
+ */
+ uint64_t tx_compl_ena : 1;
+
+ /** Enable packet protocol stats update */
+ uint64_t proto_stats_ena : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint64_t all_bits;
+} odp_pktout_config_opt_t;
+
+/**
+ * Parser configuration
+ */
+typedef struct odp_pktio_parser_config_t {
+ /** Protocol parsing level in packet input
+ *
+ * Application requires that protocol headers in a packet are checked
+ * up to this layer during packet input. Use ODP_PROTO_LAYER_ALL for
+ * all layers. Packet metadata for this and all preceding layers are
+ * set. In addition, offset (and pointer) to the next layer is set.
+ * Other layer/protocol specific metadata have undefined values.
+ *
+ * The default value is ODP_PROTO_LAYER_ALL. */
+ odp_proto_layer_t layer;
+
+} odp_pktio_parser_config_t;
+
+/** Ethernet flow control modes */
+typedef enum odp_pktio_link_pause_t {
+ /** Flow control mode is unknown */
+ ODP_PKTIO_LINK_PAUSE_UNKNOWN = -1,
+ /** No flow control */
+ ODP_PKTIO_LINK_PAUSE_OFF = 0,
+ /** Pause frame flow control enabled */
+ ODP_PKTIO_LINK_PAUSE_ON = 1,
+ /** Priority-based Flow Control (PFC) enabled */
+ ODP_PKTIO_LINK_PFC_ON = 2
+
+} odp_pktio_link_pause_t;
+
+/**
+ * Packet IO configuration options
+ *
+ * Packet IO interface level configuration options. Use odp_pktio_capability()
+ * to see which options are supported by the implementation.
+ * Use odp_pktio_config_init() to initialize the structure with default values.
+ */
+typedef struct odp_pktio_config_t {
+ /** Packet input configuration options bit field
+ *
+ * Default value for all bits is zero. */
+ odp_pktin_config_opt_t pktin;
+
+ /** Packet output configuration options bit field
+ *
+ * Default value for all bits is zero. */
+ odp_pktout_config_opt_t pktout;
+
+ /** Packet input parser configuration */
+ odp_pktio_parser_config_t parser;
+
+ /** Interface loopback mode
+ *
+ * In this mode the packets sent out through the interface is
+ * looped back to input of the same interface. Supporting loopback mode
+ * is an optional feature per interface and should be queried in the
+ * interface capability before enabling the same.
+ *
+ * Default value is false.
+ */
+ odp_bool_t enable_loop;
+
+ /** Inbound IPSEC inlined with packet input
+ *
+ * Enable/disable inline inbound IPSEC operation. When enabled packet
+ * input directs all IPSEC packets automatically to IPSEC inbound
+ * processing. IPSEC configuration (through IPSEC API) must be done
+ * before enabling this feature in pktio.
+ * Packets that are not (recognized as) IPSEC are processed
+ * according to the packet input configuration.
+ *
+ * 0: Disable inbound IPSEC inline operation (default)
+ * 1: Enable inbound IPSEC inline operation
+ *
+ * @see odp_ipsec_config(), odp_ipsec_sa_create()
+ */
+ odp_bool_t inbound_ipsec;
+
+ /** Outbound IPSEC inlined with packet output
+ *
+ * Enable/disable inline outbound IPSEC operation. When enabled IPSEC
+ * outbound processing can send outgoing IPSEC packets directly
+ * to the pktio interface for output. IPSEC configuration is done
+ * through the IPSEC API.
+ *
+ * Support of outbound IPSEC inline operation with traffic manager
+ * (ODP_PKTOUT_MODE_TM) can be queried with odp_ipsec_capability().
+ *
+ * * 0: Disable outbound IPSEC inline operation (default)
+ * * 1: Enable outbound IPSEC inline operation
+ *
+ * @see odp_ipsec_config(), odp_ipsec_sa_create(), odp_ipsec_out_inline()
+ */
+ odp_bool_t outbound_ipsec;
+
+ /** Enable Large Send Offload (LSO)
+ *
+ * Enables LSO on the interface. Use LSO capabilities (odp_lso_capability_t) to check if
+ * the interface supports LSO (in the selected packet output mode). LSO cannot be enabled
+ * in ODP_PKTOUT_MODE_QUEUE mode. Also, LSO operation cannot be combined with IPSEC on
+ * packet output.
+ *
+ * Application requests LSO for outgoing packets with odp_pktout_send_lso() or
+ * odp_tm_enq_multi_lso(). Other packet output calls ignore LSO metadata in packets.
+ *
+ * 0: Application will not use LSO (default)
+ * 1: Application will use LSO
+ */
+ odp_bool_t enable_lso;
+
+ /** Packet input reassembly configuration */
+ odp_reass_config_t reassembly;
+
+ /** Link flow control configuration */
+ struct {
+ /** Reception of flow control frames
+ *
+ * Configures interface operation when an Ethernet flow control frame is received:
+ * * ODP_PKTIO_LINK_PAUSE_OFF: Flow control is disabled
+ * * ODP_PKTIO_LINK_PAUSE_ON: Enable traditional Ethernet pause frame handling.
+ * When a pause frame is received, all packet output
+ * is halted temporarily.
+ * * ODP_PKTIO_LINK_PFC_ON: Enable Priority-based Flow Control (PFC)
+ * handling. When a PFC frame is received, packet
+ * output of certain (VLAN) class of service levels
+ * are halted temporarily.
+ *
+ * The default value is ODP_PKTIO_LINK_PAUSE_OFF.
+ */
+ odp_pktio_link_pause_t pause_rx;
+
+ /** Transmission of flow control frames
+ *
+ * Configures Ethernet flow control frame generation on the interface:
+ * * ODP_PKTIO_LINK_PAUSE_OFF: Flow control is disabled
+ * * ODP_PKTIO_LINK_PAUSE_ON: Enable traditional Ethernet pause frame
+ * generation. Pause frames are generated to request
+ * the remote end of the link to halt all
+ * transmissions temporarily.
+ * * ODP_PKTIO_LINK_PFC_ON: Enable Priority-based Flow Control (PFC) frame
+ * generation. PFC frames are generated to request
+ * the remote end of the link to halt transmission
+ * of certain (VLAN) class of service levels
+ * temporarily.
+ *
+ * When PFC is enabled, classifier API is used to configure CoS nodes with back
+ * pressure threshold and PFC priority level parameters (odp_bp_param_t).
+ *
+ * The default value is ODP_PKTIO_LINK_PAUSE_OFF.
+ */
+ odp_pktio_link_pause_t pause_tx;
+
+ } flow_control;
+
+ /**
+ * Packet transmit completion configuration
+ */
+ struct {
+ /**
+ * Enable packet transmit completion events
+ *
+ * Use pktio capability tx_compl to check if packet transmit completion mode
+ * #ODP_PACKET_TX_COMPL_EVENT is supported.
+ *
+ * 0: Application will not request ODP_PACKET_TX_COMPL_EVENT mode
+ * completion (default)
+ * 1: Application may request ODP_PACKET_TX_COMPL_EVENT mode completion
+ */
+ uint32_t mode_event : 1;
+
+ /**
+ * Enable packet transmit completion check through polling
+ *
+ * Use pktio capability tx_compl to check if packet transmit completion mode
+ * #ODP_PACKET_TX_COMPL_POLL is supported.
+ *
+ * 0: Application will not request ODP_PACKET_TX_COMPL_POLL mode
+ * completion (default)
+ * 1: Application may request ODP_PACKET_TX_COMPL_POLL mode completion
+ */
+ uint32_t mode_poll : 1;
+
+ /**
+ * Maximum completion index
+ *
+ * When ODP_PACKET_TX_COMPL_POLL mode is enabled, the maximum completion index
+ * value that application will use. The value must be between 0 and
+ * tx_compl.max_compl_id capability. The default value is zero.
+ */
+ uint32_t max_compl_id;
+
+ } tx_compl;
+
+} odp_pktio_config_t;
+
+/**
+ * Packet IO set operations
+ *
+ * Supported packet IO interface set operations listed in a bit field structure.
+ */
+typedef union odp_pktio_set_op_t {
+ /** Operation flags */
+ struct {
+ /** Promiscuous mode */
+ uint32_t promisc_mode : 1;
+ /** MAC address */
+ uint32_t mac_addr : 1;
+ /** Per port header offset(skip)set */
+ uint32_t skip_offset : 1;
+ /** Maximum frame length */
+ uint32_t maxlen : 1;
+ } op;
+ /** All bits of the bit field structure.
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure. */
+ uint32_t all_bits;
+} odp_pktio_set_op_t;
+
+/** Maximum number of custom LSO fields supported by ODP API */
+#define ODP_LSO_MAX_CUSTOM 8
+
+/** LSO custom modification options */
+typedef enum odp_lso_modify_t {
+ /** Add current segment number. Numbering starts from zero. */
+ ODP_LSO_ADD_SEGMENT_NUM = 0x1,
+
+ /** Add number of payload bytes in the segment */
+ ODP_LSO_ADD_PAYLOAD_LEN = 0x2,
+
+ /** Add number of payload bytes in all previous segments */
+ ODP_LSO_ADD_PAYLOAD_OFFSET = 0x4
+
+} odp_lso_modify_t;
+
+/** LSO protocol options
+ *
+ * An LSO operation may perform segmentation on these protocols.
+ */
+typedef enum odp_lso_protocol_t {
+ /** Protocol not selected. */
+ ODP_LSO_PROTO_NONE = 0,
+
+ /** Custom protocol. LSO performs only custom field updates to the packet headers. */
+ ODP_LSO_PROTO_CUSTOM,
+
+ /** LSO performs IPv4 fragmentation.
+ *
+ * IP header length and checksum fields are updated. IP fragmentation related fields are
+ * filled and IPv4 Identification field value is copied from the original packet. */
+ ODP_LSO_PROTO_IPV4,
+
+ /** LSO performs IPv6 fragmentation. */
+ ODP_LSO_PROTO_IPV6,
+
+ /** LSO performs TCP segmentation on top of IPv4.
+ *
+ * IP header length and checksum fields are updated. IP fragmentation is not performed
+ * and IPv4 Don't Fragment bit is not set. Unique IPv4 Identification field values
+ * are generated. Those are usually increments of the IPv4 ID field value in the packet.
+ */
+ ODP_LSO_PROTO_TCP_IPV4,
+
+ /** LSO performs TCP segmentation on top of IPv6. */
+ ODP_LSO_PROTO_TCP_IPV6,
+
+ /** LSO performs SCTP segmentation on top of IPv4. */
+ ODP_LSO_PROTO_SCTP_IPV4,
+
+ /** LSO performs SCTP segmentation on top of IPv6. */
+ ODP_LSO_PROTO_SCTP_IPV6
+
+} odp_lso_protocol_t;
+
+/** Large Send Offload (LSO) capabilities */
+typedef struct odp_lso_capability_t {
+ /** Maximum number of LSO profiles. When zero, LSO is not supported. */
+ uint32_t max_profiles;
+
+ /** Maximum number of LSO profiles per packet IO interface. When zero, LSO is not
+ * supported by the interface. */
+ uint32_t max_profiles_per_pktio;
+
+ /** Maximum number of segments in an input packet. When one, LSO operation accepts only
+ * non-segmented packets as input. */
+ uint32_t max_packet_segments;
+
+ /** Maximum number of segments an LSO operation may create. This implies that
+ * the maximum supported input packet payload size for an LSO operation is
+ * max_segments * max_payload_len bytes. */
+ uint32_t max_segments;
+
+ /** Maximum payload length per an LSO generated packet (in bytes). This is the maximum value
+ * for max_payload_len in odp_packet_lso_opt_t. */
+ uint32_t max_payload_len;
+
+ /** Maximum supported offset to the packet payload (in bytes). This is the maximum value
+ * for payload_offset in odp_packet_lso_opt_t. */
+ uint32_t max_payload_offset;
+
+ /** Supported LSO custom modification options */
+ struct {
+ /** ODP_LSO_ADD_SEGMENT_NUM support */
+ uint16_t add_segment_num:1;
+
+ /** ODP_LSO_ADD_PAYLOAD_LEN support */
+ uint16_t add_payload_len:1;
+
+ /** ODP_LSO_ADD_PAYLOAD_OFFSET support */
+ uint16_t add_payload_offset:1;
+
+ } mod_op;
+
+ /** Maximum number of custom fields supported per LSO profile. When zero, custom
+ * fields are not supported. */
+ uint8_t max_num_custom;
+
+ /** Supported LSO protocol options */
+ struct {
+ /** ODP_LSO_PROTO_CUSTOM support */
+ uint32_t custom:1;
+
+ /** ODP_LSO_PROTO_IPV4 support */
+ uint32_t ipv4:1;
+
+ /** ODP_LSO_PROTO_IPV6 support */
+ uint32_t ipv6:1;
+
+ /** ODP_LSO_PROTO_TCP_IPV4 support */
+ uint32_t tcp_ipv4:1;
+
+ /** ODP_LSO_PROTO_TCP_IPV6 support */
+ uint32_t tcp_ipv6:1;
+
+ /** ODP_LSO_PROTO_SCTP_IPV4 support */
+ uint32_t sctp_ipv4:1;
+
+ /** ODP_LSO_PROTO_SCTP_IPV6 support */
+ uint32_t sctp_ipv6:1;
+
+ } proto;
+
+} odp_lso_capability_t;
+
+/**
+ * Packet input vector capabilities
+ */
+typedef struct odp_pktin_vector_capability_t {
+ /** Packet input vector availability */
+ odp_support_t supported;
+
+ /** Maximum number of packets that can be accumulated into a packet
+ * vector by a producer
+ *
+ * odp_pktin_vector_config_t::max_size should not be greater than this
+ * value. */
+ uint32_t max_size;
+
+ /** Minimum value allowed to be configured to
+ * odp_pktin_vector_config_t::max_size */
+ uint32_t min_size;
+
+ /** Maximum timeout in nanoseconds for the producer to wait for the
+ * vector of packets
+ *
+ * odp_pktin_vector_config_t::max_tmo_ns should not be greater than this
+ * value. */
+ uint64_t max_tmo_ns;
+
+ /** Minimum value allowed to be configured to
+ * odp_pktin_vector_config_t::max_tmo_ns */
+ uint64_t min_tmo_ns;
+
+} odp_pktin_vector_capability_t;
+
+/**
+ * Packet IO capabilities
+ *
+ * Note that interface capabilities may differ between packet output modes. For example,
+ * LSO may not be supported in ODP_PKTOUT_MODE_TM mode, while it is supported in
+ * ODP_PKTOUT_MODE_DIRECT mode.
+ */
+typedef struct odp_pktio_capability_t {
+ /** Maximum number of input queues
+ *
+ * Value does not exceed ODP_PKTIN_MAX_QUEUES. */
+ uint32_t max_input_queues;
+
+ /** Minimum input queue size
+ *
+ * Zero if configuring queue size is not supported. */
+ uint32_t min_input_queue_size;
+
+ /** Maximum input queue size
+ *
+ * Zero if configuring queue size is not supported. */
+ uint32_t max_input_queue_size;
+
+ /** Maximum number of output queues
+ *
+ * Value does not exceed ODP_PKTOUT_MAX_QUEUES. */
+ uint32_t max_output_queues;
+
+ /** Minimum output queue size
+ *
+ * Zero if configuring queue size is not supported. */
+ uint32_t min_output_queue_size;
+
+ /** Maximum output queue size
+ *
+ * Zero if configuring queue size is not supported. */
+ uint32_t max_output_queue_size;
+
+ /** Supported pktio configuration options */
+ odp_pktio_config_t config;
+
+ /** Supported set operations
+ *
+ * A bit set to one indicates a supported operation. All other bits are
+ * set to zero. */
+ odp_pktio_set_op_t set_op;
+
+ /** Packet input vector capability */
+ odp_pktin_vector_capability_t vector;
+
+ /** LSO capabilities */
+ odp_lso_capability_t lso;
+
+ /** Supported frame lengths for odp_pktio_maxlen_set()
+ *
+ * A frame length value of zero indicates an unsupported operation. */
+ struct {
+ /** Equal maximum frame length for both packet input and output
+ *
+ * When set, the same maximum frame length value has to be used
+ * for both input and output directions. */
+ odp_bool_t equal;
+ /** Minimum valid value for 'maxlen_input' */
+ uint32_t min_input;
+ /** Maximum valid value for 'maxlen_input' */
+ uint32_t max_input;
+ /** Minimum valid value for 'maxlen_output' */
+ uint32_t min_output;
+ /** Maximum valid value for 'maxlen_output' */
+ uint32_t max_output;
+ } maxlen;
+
+ /**
+ * Max Tx aging timeout in nano seconds supported when packet aging
+ * feature is supported.
+ *
+ * 0: aging is not supported
+ * >0: maximum aging timeout supported in nanoseconds
+ */
+ uint64_t max_tx_aging_tmo_ns;
+
+ /** Supported packet Tx completion options */
+ struct {
+ /**
+ * Scheduled queue support
+ *
+ * This defines whether schedule queues are supported for receiving Tx
+ * completion events.
+ *
+ * 0: Scheduled queues are not supported for receiving Tx completion events.
+ * 1: Scheduled queues are supported for receiving Tx completion events.
+ * @see odp_packet_tx_compl_request()
+ */
+ odp_bool_t queue_type_sched;
+
+ /**
+ * Plain queue support
+ *
+ * This defines whether plain queues are supported for receiving Tx
+ * completion events.
+ *
+ * 0: Plain queues are not supported for receiving Tx completion events.
+ * 1: Plain queues are supported for receiving Tx completion events.
+ * @see odp_packet_tx_compl_request()
+ */
+ odp_bool_t queue_type_plain;
+
+ /**
+ * For backwards compatibility, mode_all is synonym of mode_event.
+ *
+ * @deprecated Use mode_event instead.
+ */
+ uint32_t mode_all : 1;
+
+ /** Packet transmit completion mode ODP_PACKET_TX_COMPL_EVENT support */
+ uint32_t mode_event : 1;
+
+ /** Packet transmit completion mode ODP_PACKET_TX_COMPL_POLL support */
+ uint32_t mode_poll : 1;
+
+ /** Maximum supported completion ID value */
+ uint32_t max_compl_id;
+
+ } tx_compl;
+
+ /** Supported packet free control options */
+ struct {
+ /**
+ * Packet free control option #ODP_PACKET_FREE_CTRL_DONT_FREE support
+ * with odp_packet_free_ctrl_set().
+ */
+ uint32_t dont_free : 1;
+
+ } free_ctrl;
+
+ /** Packet input reassembly capability */
+ odp_reass_capability_t reassembly;
+
+ /** Statistics counters capabilities */
+ odp_pktio_stats_capability_t stats;
+
+ /** Supported flow control modes */
+ struct {
+ /** Reception of traditional Ethernet pause frames */
+ uint32_t pause_rx: 1;
+
+ /** Reception of PFC frames */
+ uint32_t pfc_rx: 1;
+
+ /** Generation of traditional Ethernet pause frames */
+ uint32_t pause_tx: 1;
+
+ /** Generation of PFC frames */
+ uint32_t pfc_tx: 1;
+
+ } flow_control;
+
+} odp_pktio_capability_t;
+
+/**
+ * LSO profile parameters
+ */
+typedef struct odp_lso_profile_param_t {
+ /**
+ * Segmentation protocol
+ *
+ * Selects on which protocol LSO operation performs segmentation (e.g. IP fragmentation vs.
+ * TCP segmentation). When ODP_LSO_PROTO_CUSTOM is selected, only custom field
+ * modifications are performed. The default value is ODP_LSO_PROTO_NONE. Check LSO
+ * capability for supported protocols.
+ */
+ odp_lso_protocol_t lso_proto;
+
+ /**
+ * Custom fields
+ *
+ * Set lso_proto to ODP_LSO_PROTO_CUSTOM when using custom fields. Fields are defined
+ * in the same order they appear in the packet.
+ */
+ struct {
+ /** Custom field to be modified by LSO */
+ struct {
+ /** Field modify operation. Selects how value of the field is modified
+ * from its original value during segmentation. Field value is assumed
+ * to be in network (big endian) byte order. */
+ odp_lso_modify_t mod_op;
+
+ /** Field offset in bytes from packet start */
+ uint32_t offset;
+
+ /** Field size in bytes. Valid values are 1, 2, 4, and 8 bytes. */
+ uint8_t size;
+
+ } field[ODP_LSO_MAX_CUSTOM];
+
+ /** Number of custom fields specified. The default value is 0. */
+ uint8_t num_custom;
+
+ } custom;
+
+} odp_lso_profile_param_t;
+
+/** Link status */
+typedef enum odp_pktio_link_status_t {
+ /** Link status is unknown */
+ ODP_PKTIO_LINK_STATUS_UNKNOWN = -1,
+ /** Link status is down */
+ ODP_PKTIO_LINK_STATUS_DOWN = 0,
+ /** Link status is up */
+ ODP_PKTIO_LINK_STATUS_UP = 1
+
+} odp_pktio_link_status_t;
+
+/**
+ * Packet IO information
+ */
+typedef struct odp_pktio_info_t {
+ /** Packet IO device name */
+ const char *name;
+
+ /** Packet IO driver name (implementation specific) */
+ const char *drv_name;
+
+ /** Packet pool */
+ odp_pool_t pool;
+
+ /** Packet IO parameters */
+ odp_pktio_param_t param;
+
+} odp_pktio_info_t;
+
+/** @name Link speed
+ * Packet IO link speeds in Mbps
+ * @anchor link_speed
+ * @{
+ */
+
+/** Link speed unknown */
+#define ODP_PKTIO_LINK_SPEED_UNKNOWN 0
+/** Link speed 10 Mbit/s */
+#define ODP_PKTIO_LINK_SPEED_10M 10
+/** Link speed 100 Mbit/s */
+#define ODP_PKTIO_LINK_SPEED_100M 100
+/** Link speed 1 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_1G 1000
+/** Link speed 2.5 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_2_5G 2500
+/** Link speed 5 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_5G 5000
+/** Link speed 10 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_10G 10000
+/** Link speed 20 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_20G 20000
+/** Link speed 25 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_25G 25000
+/** Link speed 40 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_40G 40000
+/** Link speed 50 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_50G 50000
+/** Link speed 56 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_56G 56000
+/** Link speed 100 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_100G 100000
+/** Link speed 200 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_200G 200000
+/** Link speed 400 Gbit/s */
+#define ODP_PKTIO_LINK_SPEED_400G 400000
+
+/** @} */
+
+/** Autonegotiation mode */
+typedef enum odp_pktio_link_autoneg_t {
+ /** Autonegotiation state unknown */
+ ODP_PKTIO_LINK_AUTONEG_UNKNOWN = -1,
+ /** Autonegotiation disabled */
+ ODP_PKTIO_LINK_AUTONEG_OFF = 0,
+ /** Autonegotiation enabled */
+ ODP_PKTIO_LINK_AUTONEG_ON = 1
+
+} odp_pktio_link_autoneg_t;
+
+/** Duplex mode */
+typedef enum odp_pktio_link_duplex_t {
+ /** Link duplex mode is unknown */
+ ODP_PKTIO_LINK_DUPLEX_UNKNOWN = -1,
+ /** Half duplex mode */
+ ODP_PKTIO_LINK_DUPLEX_HALF = 0,
+ /** Full duplex mode */
+ ODP_PKTIO_LINK_DUPLEX_FULL = 1
+
+} odp_pktio_link_duplex_t;
+
+/**
+ * Packet IO link information
+ */
+typedef struct odp_pktio_link_info_t {
+ /** Link autonegotiation */
+ odp_pktio_link_autoneg_t autoneg;
+ /** Duplex mode */
+ odp_pktio_link_duplex_t duplex;
+ /** Link media type
+ *
+ * The implementation owned string describes link media type. Values are
+ * implementation specific short names like copper, fiber, or virtual.
+ * The value of "unknown" is used when media type cannot be determined. */
+ const char *media;
+ /** Reception of pause frames */
+ odp_pktio_link_pause_t pause_rx;
+ /** Transmission of pause frames */
+ odp_pktio_link_pause_t pause_tx;
+ /** Link speed in Mbps
+ *
+ * The value of zero means that the link speed is unknown.
+ * ODP_PKTIO_LINK_SPEED_* (@ref link_speed) defines can be used to
+ * compare the value to standard link speeds. */
+ uint32_t speed;
+ /** Link status */
+ odp_pktio_link_status_t status;
+} odp_pktio_link_info_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/packet_types.h b/include/odp/api/spec/packet_types.h
new file mode 100644
index 000000000..b9d55abde
--- /dev/null
+++ b/include/odp/api/spec/packet_types.h
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP packet types
+ */
+
+#ifndef ODP_API_SPEC_PACKET_TYPES_H_
+#define ODP_API_SPEC_PACKET_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/proto_stats_types.h>
+#include <odp/api/queue_types.h>
+
+/** @defgroup odp_packet ODP PACKET
+ * @{
+ */
+
+/**
+ * @typedef odp_packet_t
+ * ODP packet
+ */
+
+/**
+ * @def ODP_PACKET_INVALID
+ * Invalid packet
+ */
+
+/**
+ * @def ODP_PACKET_OFFSET_INVALID
+ * Invalid packet offset
+ */
+
+/**
+ * @typedef odp_packet_seg_t
+ * ODP packet segment
+ *
+ * A packet segment refers to a contiguous part of packet data (in memory). Segments of a packet
+ * can be examined with odp_packet_seg_data(), odp_packet_seg_data_len() and other calls.
+ */
+
+/**
+ * @def ODP_PACKET_SEG_INVALID
+ * Invalid packet segment
+ */
+
+/**
+ * @typedef odp_packet_buf_t
+ * ODP packet buffer
+ *
+ * Packet buffers are not part of any packet, but they result from a previous
+ * odp_packet_disassemble() call. A new packet is formed from packet buffers with
+ * a odp_packet_reassemble() call.
+ */
+
+/**
+ * @def ODP_PACKET_BUF_INVALID
+ * Invalid packet buffer
+ */
+
+/**
+ * @typedef odp_packet_color_t
+ * Color of packet for shaper/drop processing
+ *
+ * @var ODP_PACKET_GREEN
+ * Packet is green
+ *
+ * @var ODP_PACKET_YELLOW
+ * Packet is yellow
+ *
+ * @var ODP_PACKET_RED
+ * Packet is red
+ */
+
+/**
+ * Maximum number of packet colors which accommodates ODP_PACKET_GREEN, ODP_PACKET_YELLOW and
+ * ODP_PACKET_RED.
+ */
+#define ODP_NUM_PACKET_COLORS 3
+
+/**
+ * Layer 2 protocol type
+ */
+typedef uint8_t odp_proto_l2_type_t;
+
+/** Layer 2 protocol type not defined */
+#define ODP_PROTO_L2_TYPE_NONE 0
+
+ /** Layer 2 protocol is Ethernet */
+#define ODP_PROTO_L2_TYPE_ETH 1
+
+/**
+ * Layer 3 protocol type
+ */
+typedef uint16_t odp_proto_l3_type_t;
+
+/** Layer 3 protocol type not defined */
+#define ODP_PROTO_L3_TYPE_NONE 0xFFFF
+
+/* Types from IEEE EtherType assignments list */
+
+/** Layer 3 protocol is ARP */
+#define ODP_PROTO_L3_TYPE_ARP 0x0806
+
+/** Layer 3 protocol is RARP */
+#define ODP_PROTO_L3_TYPE_RARP 0x8035
+
+/** Layer 3 protocol is MPLS */
+#define ODP_PROTO_L3_TYPE_MPLS 0x8847
+
+/** Layer 3 protocol type is IPv4 */
+#define ODP_PROTO_L3_TYPE_IPV4 0x0800
+
+/** Layer 3 protocol type is IPv6 */
+#define ODP_PROTO_L3_TYPE_IPV6 0x86DD
+
+/**
+ * Layer 4 protocol type
+ */
+typedef uint8_t odp_proto_l4_type_t;
+
+/** Layer 4 protocol type not defined */
+ #define ODP_PROTO_L4_TYPE_NONE 255
+
+/* Types from IANA assigned Internet protocol numbers list */
+
+/** Layer 4 protocol type is ICMPv4 */
+ #define ODP_PROTO_L4_TYPE_ICMPV4 1
+
+/** Layer 4 protocol type is IGMP */
+#define ODP_PROTO_L4_TYPE_IGMP 2
+
+/** Layer 4 protocol type is IPv4 */
+#define ODP_PROTO_L4_TYPE_IPV4 4
+
+/** Layer 4 protocol type is TCP */
+ #define ODP_PROTO_L4_TYPE_TCP 6
+
+/** Layer 4 protocol type is UDP */
+#define ODP_PROTO_L4_TYPE_UDP 17
+
+/** Layer 4 protocol type is IPv6 */
+#define ODP_PROTO_L4_TYPE_IPV6 41
+
+/** Layer 4 protocol type is GRE */
+#define ODP_PROTO_L4_TYPE_GRE 47
+
+/** Layer 4 protocol type is IPSEC ESP */
+#define ODP_PROTO_L4_TYPE_ESP 50
+
+/** Layer 4 protocol type is IPSEC AH */
+#define ODP_PROTO_L4_TYPE_AH 51
+
+/** Layer 4 protocol type is ICMPv6 */
+#define ODP_PROTO_L4_TYPE_ICMPV6 58
+
+/** Layer 4 protocol type is No Next Header for IPv6 */
+#define ODP_PROTO_L4_TYPE_NO_NEXT 59
+
+/** Layer 4 protocol type is IP Payload Compression Protocol */
+#define ODP_PROTO_L4_TYPE_IPCOMP 108
+
+/** Layer 4 protocol type is SCTP */
+#define ODP_PROTO_L4_TYPE_SCTP 132
+
+/** Layer 4 protocol type is ROHC */
+#define ODP_PROTO_L4_TYPE_ROHC 142
+
+/**
+ * @typedef odp_packet_chksum_status_t
+ * Checksum check status in packet
+ *
+ * @var ODP_PACKET_CHKSUM_UNKNOWN
+ * Checksum was not checked. Checksum check was not
+ * attempted or the attempt failed.
+ *
+ * @var ODP_PACKET_CHKSUM_BAD
+ * Checksum was checked and it was not correct.
+ *
+ * @var ODP_PACKET_CHKSUM_OK
+ * Checksum was checked and it was correct.
+ */
+
+/**
+ * @typedef odp_packet_vector_t
+ * ODP packet vector
+ */
+
+/**
+ * @def ODP_PACKET_VECTOR_INVALID
+ * Invalid packet vector
+ */
+
+/**
+ * @typedef odp_packet_tx_compl_t
+ * ODP Packet Tx completion
+ */
+
+/**
+ * @def ODP_PACKET_TX_COMPL_INVALID
+ * Invalid packet Tx completion
+ */
+
+/**
+ * Protocol
+ */
+typedef enum odp_proto_t {
+ /** No protocol defined */
+ ODP_PROTO_NONE = 0,
+
+ /** Ethernet (including VLAN) */
+ ODP_PROTO_ETH,
+
+ /** IP version 4 */
+ ODP_PROTO_IPV4,
+
+ /** IP version 6 */
+ ODP_PROTO_IPV6
+
+} odp_proto_t;
+
+/**
+ * Protocol layer
+ */
+typedef enum odp_proto_layer_t {
+ /** No layers */
+ ODP_PROTO_LAYER_NONE = 0,
+
+ /** Layer L2 protocols (Ethernet, VLAN, etc) */
+ ODP_PROTO_LAYER_L2,
+
+ /** Layer L3 protocols (IPv4, IPv6, ICMP, IPSEC, etc) */
+ ODP_PROTO_LAYER_L3,
+
+ /** Layer L4 protocols (UDP, TCP, SCTP) */
+ ODP_PROTO_LAYER_L4,
+
+ /** All layers */
+ ODP_PROTO_LAYER_ALL
+
+} odp_proto_layer_t;
+
+/**
+ * Packet API data range specifier
+ */
+typedef struct odp_packet_data_range {
+ /** Offset from beginning of packet */
+ uint32_t offset;
+
+ /** Length of data to operate on */
+ uint32_t length;
+
+} odp_packet_data_range_t;
+
+/**
+ * Reassembly status of a packet
+ */
+typedef enum odp_packet_reass_status_t {
+ /** Reassembly was not attempted */
+ ODP_PACKET_REASS_NONE = 0,
+
+ /** Reassembly was attempted but is incomplete. Partial reassembly
+ * result can be accessed using ``odp_packet_reass_partial_state()``.
+ *
+ * The packet does not contain valid packet data and cannot be used
+ * in normal packet operations.
+ */
+ ODP_PACKET_REASS_INCOMPLETE,
+
+ /** Reassembly was successfully done. The packet has been
+ * reassembled from multiple received fragments. */
+ ODP_PACKET_REASS_COMPLETE,
+} odp_packet_reass_status_t;
+
+/**
+ * Information about a completed reassembly
+ */
+typedef struct odp_packet_reass_info_t {
+ /** Number of fragments reassembled */
+ uint16_t num_frags;
+} odp_packet_reass_info_t;
+
+/**
+ * Result from odp_packet_reass_partial_state()
+ */
+typedef struct odp_packet_reass_partial_state_t {
+ /** Number of fragments returned */
+ uint16_t num_frags;
+
+ /** Time, in ns, since the reception of the first received fragment */
+ uint64_t elapsed_time;
+} odp_packet_reass_partial_state_t;
+
+/**
+ * Flags to control packet data checksum checking
+ */
+typedef union odp_proto_chksums_t {
+ /** Individual checksum bits. */
+ struct {
+ /** IPv4 header checksum */
+ uint32_t ipv4 : 1;
+
+ /** UDP checksum */
+ uint32_t udp : 1;
+
+ /** TCP checksum */
+ uint32_t tcp : 1;
+
+ /** SCTP checksum */
+ uint32_t sctp : 1;
+
+ } chksum;
+
+ /** All checksum bits
+ *
+ * This field can be used to set/clear all flags, or to perform bitwise
+ * operations over those. */
+ uint32_t all_chksum;
+
+} odp_proto_chksums_t;
+
+/**
+ * Packet parse parameters
+ */
+typedef struct odp_packet_parse_param_t {
+ /** Protocol header at parse starting point. Valid values for this
+ * field are: ODP_PROTO_ETH, ODP_PROTO_IPV4, ODP_PROTO_IPV6. */
+ odp_proto_t proto;
+
+ /** Continue parsing until this layer. Must be the same or higher
+ * layer than the layer of 'proto'. */
+ odp_proto_layer_t last_layer;
+
+ /** Flags to control payload data checksums checks up to the selected
+ * parse layer. Checksum checking status can be queried for each packet
+ * with odp_packet_l3_chksum_status() and
+ * odp_packet_l4_chksum_status().
+ */
+ odp_proto_chksums_t chksums;
+
+} odp_packet_parse_param_t;
+
+/**
+ * Packet parse results
+ */
+typedef struct odp_packet_parse_result_t {
+ /** Parse result flags */
+ odp_packet_parse_result_flag_t flag;
+
+ /** See odp_packet_len() */
+ uint32_t packet_len;
+
+ /** See odp_packet_l2_offset() */
+ uint32_t l2_offset;
+ /** See odp_packet_l3_offset() */
+ uint32_t l3_offset;
+ /** See odp_packet_l4_offset() */
+ uint32_t l4_offset;
+
+ /** See odp_packet_l3_chksum_status() */
+ odp_packet_chksum_status_t l3_chksum_status;
+ /** See odp_packet_l4_chksum_status() */
+ odp_packet_chksum_status_t l4_chksum_status;
+
+ /** See odp_packet_l2_type() */
+ odp_proto_l2_type_t l2_type;
+ /** See odp_packet_l3_type() */
+ odp_proto_l3_type_t l3_type;
+ /** See odp_packet_l4_type() */
+ odp_proto_l4_type_t l4_type;
+
+} odp_packet_parse_result_t;
+
+/**
+ * LSO options
+ */
+typedef struct odp_packet_lso_opt_t {
+ /** LSO profile handle
+ *
+ * The selected LSO profile specifies details of the segmentation operation to be done.
+ * Depending on LSO profile options, additional metadata (e.g. L3/L4 protocol header
+ * offsets) may need to be set on the packet. See LSO documentation
+ * (e.g. odp_pktout_send_lso() and odp_lso_protocol_t) for additional metadata
+ * requirements.
+ */
+ odp_lso_profile_t lso_profile;
+
+ /** LSO payload offset
+ *
+ * LSO operation considers packet data before 'payload_offset' as
+ * protocol headers and copies those in front of every created segment. It will modify
+ * protocol headers according to the LSO profile before segment transmission.
+ *
+ * When stored into a packet, this offset can be read with odp_packet_payload_offset() and
+ * modified with odp_packet_payload_offset_set().
+ */
+ uint32_t payload_offset;
+
+ /** Maximum payload length in an LSO segment
+ *
+ * Max_payload_len parameter defines the maximum number of payload bytes in each
+ * created segment. Depending on the implementation, segments with less payload may be
+ * created. However, this value is used typically to divide packet payload evenly over
+ * all segments except the last one, which contains the remaining payload bytes.
+ */
+ uint32_t max_payload_len;
+
+} odp_packet_lso_opt_t;
+
+/**
+ * Packet transmit completion mode
+ */
+typedef enum odp_packet_tx_compl_mode_t {
+ /** Disable packet transmit completion */
+ ODP_PACKET_TX_COMPL_DISABLED = 0,
+
+ /**
+ * Enable packet transmit completion event
+ *
+ * A packet transmit completion event is sent for both transmitted and dropped packets.
+ */
+ ODP_PACKET_TX_COMPL_EVENT,
+
+ /**
+ * Enable packet transmit completion check through polling
+ *
+ * Packet transmit completion status is updated for both transmitted and dropped packets.
+ */
+ ODP_PACKET_TX_COMPL_POLL,
+
+} odp_packet_tx_compl_mode_t;
+
+/**
+ * For backwards compatibility, ODP_PACKET_TX_COMPL_ALL is synonym of ODP_PACKET_TX_COMPL_EVENT.
+ *
+ * @deprecated Use #ODP_PACKET_TX_COMPL_EVENT instead.
+ */
+#define ODP_PACKET_TX_COMPL_ALL ODP_PACKET_TX_COMPL_EVENT
+
+/**
+ * Packet transmit completion request options
+ */
+typedef struct odp_packet_tx_compl_opt_t {
+ /**
+ * Packet transmit completion mode
+ *
+ * When completion mode is #ODP_PACKET_TX_COMPL_DISABLED, all other fields of this struct
+ * are ignored.
+ */
+ odp_packet_tx_compl_mode_t mode;
+
+ /** Union of packet transmit completion destinations */
+ union {
+ /**
+ * Destination queue
+ *
+ * When completion mode is #ODP_PACKET_TX_COMPL_EVENT, a packet transmit completion
+ * event will be sent to this queue.
+ */
+ odp_queue_t queue;
+
+ /**
+ * Completion identifier
+ *
+ * When completion mode is #ODP_PACKET_TX_COMPL_POLL, a packet transmit completion
+ * status will be reported through this completion identifier. Application selects
+ * a value between 0 and tx_compl.max_compl_id in packet IO configuration options
+ * (see odp_pktio_config_t). Only single packet may be transmitted with the same
+ * identifier value at a time (through the same packet IO interface). A value may
+ * be reused for the next transmit only after transmit of the previous packet is
+ * complete. Multiple packets may have the same identifier value set as long as
+ * those packets are not transmitted simultaneously.
+ */
+ uint32_t compl_id;
+ };
+
+} odp_packet_tx_compl_opt_t;
+
+/**
+ * Packet free control option
+ */
+typedef enum odp_packet_free_ctrl_t {
+ /** Packet free control disabled */
+ ODP_PACKET_FREE_CTRL_DISABLED = 0,
+
+ /** Don't free packet after processing it */
+ ODP_PACKET_FREE_CTRL_DONT_FREE,
+
+} odp_packet_free_ctrl_t;
+
+/**
+ * Packet proto stats options
+ */
+typedef struct odp_packet_proto_stats_opt_t {
+ /** Packet proto stats object handle
+ *
+ * Stats in the packet proto stats object will be updated.
+ */
+ odp_proto_stats_t stat;
+
+ /** Octet counter 0 adjust */
+ int32_t oct_count0_adj;
+
+ /** Octet counter 1 adjust */
+ int32_t oct_count1_adj;
+} odp_packet_proto_stats_opt_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/pool.h b/include/odp/api/spec/pool.h
index 6fc5b6b4a..1b71a5a09 100644
--- a/include/odp/api/spec/pool.h
+++ b/include/odp/api/spec/pool.h
@@ -1,18 +1,16 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
*/
-
/**
* @file
*
* ODP pool
*/
-#ifndef ODP_API_POOL_H_
-#define ODP_API_POOL_H_
+#ifndef ODP_API_SPEC_POOL_H_
+#define ODP_API_SPEC_POOL_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,130 +18,14 @@ extern "C" {
#endif
#include <odp/api/std_types.h>
+#include <odp/api/pool_types.h>
-/** @defgroup odp_pool ODP POOL
- * Operations on a pool.
+/** @addtogroup odp_pool
+ * Packet and buffer (event) pools.
* @{
*/
/**
- * @typedef odp_pool_t
- * ODP pool
- */
-
-/**
- * @def ODP_POOL_INVALID
- * Invalid pool
- */
-
-/**
- * @def ODP_POOL_NAME_LEN
- * Maximum pool name length in chars including null char
- */
-
-/**
- * Pool capabilities
- */
-typedef struct odp_pool_capability_t {
- /** Maximum number of pools of any type */
- unsigned max_pools;
-
- /** Buffer pool capabilities */
- struct {
- /** Maximum number of buffer pools */
- unsigned max_pools;
-
- /** Maximum buffer data alignment in bytes */
- uint32_t max_align;
-
- /** Maximum buffer data size in bytes
- *
- * The value of zero means that size is limited only by the
- * available memory size for the pool. */
- uint32_t max_size;
-
- /** Maximum number of buffers of any size
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_num;
- } buf;
-
- /** Packet pool capabilities */
- struct {
- /** Maximum number of packet pools */
- unsigned max_pools;
-
- /** Maximum packet data length in bytes
- *
- * This defines the maximum packet data length that can be
- * stored into a packet. Attempts to allocate or extend packets
- * to sizes larger than this limit will fail.
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_len;
-
- /** Maximum number of packets of any length
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_num;
-
- /** Minimum packet level headroom length in bytes
- *
- * The minimum number of headroom bytes that newly created
- * packets have by default. The default apply to both ODP
- * packet input and user allocated packets.*/
- uint32_t min_headroom;
-
- /** Minimum packet level tailroom length in bytes
- *
- * The minimum number of tailroom bytes that newly created
- * packets have by default. The default apply to both ODP
- * packet input and user allocated packets.*/
- uint32_t min_tailroom;
-
- /** Maximum number of segments per packet */
- uint32_t max_segs_per_pkt;
-
- /** Minimum packet segment data length in bytes
- *
- * The user defined segment length (seg_len in
- * odp_pool_param_t) will be rounded up into this value. */
- uint32_t min_seg_len;
-
- /** Maximum packet segment data length in bytes
- *
- * The user defined segment length (seg_len in odp_pool_param_t)
- * must not be larger than this.
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_seg_len;
-
- /** Maximum user area size in bytes
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_uarea_size;
- } pkt;
-
- /** Timeout pool capabilities */
- struct {
- /** Maximum number of timeout pools */
- unsigned max_pools;
-
- /** Maximum number of timeout events in a pool
- *
- * The value of zero means that limited only by the available
- * memory size for the pool. */
- uint32_t max_num;
- } tmo;
-
-} odp_pool_capability_t;
-
-/**
* Query pool capabilities
*
* Outputs pool capabilities on success.
@@ -156,110 +38,35 @@ typedef struct odp_pool_capability_t {
int odp_pool_capability(odp_pool_capability_t *capa);
/**
- * Pool parameters
- * Used to communicate pool creation options.
- * @note A single thread may not be able to allocate all 'num' elements
- * from the pool at any particular time, as other threads or hardware
- * blocks are allowed to keep some for caching purposes.
- */
-typedef struct odp_pool_param_t {
- /** Pool type */
- int type;
-
- /** Variant parameters for different pool types */
- union {
- /** Parameters for buffer pools */
- struct {
- /** Number of buffers in the pool */
- uint32_t num;
-
- /** Buffer size in bytes. The maximum number of bytes
- application will store in each buffer. */
- uint32_t size;
-
- /** Minimum buffer alignment in bytes. Valid values are
- powers of two. Use 0 for default alignment.
- Default will always be a multiple of 8. */
- uint32_t align;
- } buf;
-
- /** Parameters for packet pools */
- struct {
- /** The number of packets that the pool must provide
- that are packet length 'len' bytes or smaller.
- The maximum value is defined by pool capability
- pkt.max_num. */
- uint32_t num;
-
- /** Minimum packet length that the pool must provide
- 'num' packets. The number of packets may be less
- than 'num' when packets are larger than 'len'.
- The maximum value is defined by pool capability
- pkt.max_len. Use 0 for default. */
- uint32_t len;
-
- /** Maximum packet length that will be allocated from
- the pool. The maximum value is defined by pool
- capability pkt.max_len. Use 0 for default (the
- pool maximum). */
- uint32_t max_len;
-
- /** Minimum number of packet data bytes that are stored
- in the first segment of a packet. The maximum value
- is defined by pool capability pkt.max_seg_len.
- Use 0 for default. */
- uint32_t seg_len;
-
- /** User area size in bytes. The maximum value is
- defined by pool capability pkt.max_uarea_size.
- Specify as 0 if no user area is needed. */
- uint32_t uarea_size;
- } pkt;
-
- /** Parameters for timeout pools */
- struct {
- /** Number of timeouts in the pool */
- uint32_t num;
- } tmo;
- };
-} odp_pool_param_t;
-
-/** Packet pool*/
-#define ODP_POOL_PACKET ODP_EVENT_PACKET
-/** Buffer pool */
-#define ODP_POOL_BUFFER ODP_EVENT_BUFFER
-/** Timeout pool */
-#define ODP_POOL_TIMEOUT ODP_EVENT_TIMEOUT
-
-/**
* Create a pool
*
* This routine is used to create a pool. The use of pool name is optional.
* Unique names are not required. However, odp_pool_lookup() returns only a
- * single matching pool.
+ * single matching pool. Use odp_pool_param_init() to initialize parameters
+ * into their default values.
*
* @param name Name of the pool or NULL. Maximum string length is
* ODP_POOL_NAME_LEN.
- * @param params Pool parameters.
+ * @param param Pool parameters.
*
* @return Handle of the created pool
* @retval ODP_POOL_INVALID Pool could not be created
*/
-
-odp_pool_t odp_pool_create(const char *name, odp_pool_param_t *params);
+odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param);
/**
* Destroy a pool previously created by odp_pool_create()
*
+ * This routine destroys a previously created pool, and will destroy any
+ * internal shared memory objects associated with the pool. The pool must not
+ * be in use (in pktio, classifier, timer, etc.) when calling this function.
+ * Results are undefined if an attempt is made to destroy a pool that contains
+ * allocated or otherwise active buffers.
+ *
* @param pool Handle of the pool to be destroyed
*
* @retval 0 Success
* @retval -1 Failure
- *
- * @note This routine destroys a previously created pool, and will destroy any
- * internal shared memory objects associated with the pool. Results are
- * undefined if an attempt is made to destroy a pool that contains allocated
- * or otherwise active buffers.
*/
int odp_pool_destroy(odp_pool_t pool);
@@ -274,15 +81,6 @@ int odp_pool_destroy(odp_pool_t pool);
odp_pool_t odp_pool_lookup(const char *name);
/**
- * Pool information struct
- * Used to get information about a pool.
- */
-typedef struct odp_pool_info_t {
- const char *name; /**< pool name */
- odp_pool_param_t params; /**< pool parameters */
-} odp_pool_info_t;
-
-/**
* Retrieve information about a pool
*
* @param pool Pool handle
@@ -293,7 +91,6 @@ typedef struct odp_pool_info_t {
* @retval 0 Success
* @retval -1 Failure. Info could not be retrieved.
*/
-
int odp_pool_info(odp_pool_t pool, odp_pool_info_t *info);
/**
@@ -307,6 +104,14 @@ int odp_pool_info(odp_pool_t pool, odp_pool_info_t *info);
void odp_pool_print(odp_pool_t pool);
/**
+ * Print debug info about all pools
+ *
+ * Print implementation defined information about all created pools to the ODP
+ * log. The information is intended to be used for debugging.
+ */
+void odp_pool_print_all(void);
+
+/**
* Get printable value for an odp_pool_t
*
* @param hdl odp_pool_t handle to be printed
@@ -329,6 +134,188 @@ uint64_t odp_pool_to_u64(odp_pool_t hdl);
void odp_pool_param_init(odp_pool_param_t *param);
/**
+ * Maximum pool index
+ *
+ * Return the maximum pool index. Pool indexes (e.g. returned by odp_pool_index())
+ * range from zero to this maximum value.
+ *
+ * @return Maximum pool index
+ */
+unsigned int odp_pool_max_index(void);
+
+/**
+ * Get pool index
+ *
+ * @param pool Pool handle
+ *
+ * @return Pool index (0..odp_pool_max_index())
+ * @retval <0 on failure
+ */
+int odp_pool_index(odp_pool_t pool);
+
+/**
+ * Read pool statistics
+ *
+ * Read statistics counters that were enabled in pool creation parameters (odp_pool_param_t.stats).
+ * The function writes all disabled counters to zero, except per thread counters
+ * (thread.cache_available[]) which have undefined values.
+ *
+ * When per thread counters are enabled, application sets 'stats.thread.first' and
+ * 'stats.thread.last' to select the threads ('first' <= 'last'). A single call may read statistics
+ * from one to #ODP_POOL_MAX_THREAD_STATS threads. Valid thread ID values range from 0 to
+ * odp_thread_count_max() - 1. A successful call fills the output array starting always from the
+ * first element 'stats.thread.cache_available[0]' (='stats.thread.first'). Unused array elements
+ * have undefined values.
+ *
+ * Depending on the implementation, there may be some delay until performed pool operations are
+ * visible in the statistics.
+ *
+ * @param pool Pool handle
+ * @param[in,out] stats Output buffer for counters
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pool_stats(odp_pool_t pool, odp_pool_stats_t *stats);
+
+/**
+ * Get selected pool statistics
+ *
+ * Read the selected counters given in odp_pool_stats_opt_t bit field structure. Only counters
+ * included in odp_pool_stats_selected_t can be read and the selected counters must have been
+ * enabled during pool creation. Values of the unselected counters are undefined. Depending on the
+ * implementation, there may be some delay until performed pool operations are visible in the
+ * statistics.
+ *
+ * Depending on the implementation, this function may have higher performance compared to
+ * odp_pool_stats(), as only the selected set of counters is read.
+ *
+ * @param pool Pool handle
+ * @param[out] stats Output buffer for counters
+ * @param opt Bit field for selecting the counters to be read
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pool_stats_selected(odp_pool_t pool, odp_pool_stats_selected_t *stats,
+ const odp_pool_stats_opt_t *opt);
+
+/**
+ * Reset statistics for pool
+ *
+ * Reset all statistics counters to zero except: odp_pool_stats_t::available,
+ * odp_pool_stats_t::cache_available, odp_pool_stats_t::thread::cache_available
+ *
+ * @param pool Pool handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pool_stats_reset(odp_pool_t pool);
+
+/**
+ * Query capabilities of an external memory pool type
+ *
+ * Outputs pool capabilities on success. Returns failure if a bad pool type is used. When
+ * the requested pool type is valid but not supported, sets the value of 'max_pools' to zero.
+ *
+ * @param type Pool type
+ * @param[out] capa Pointer to capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pool_ext_capability(odp_pool_type_t type, odp_pool_ext_capability_t *capa);
+
+/**
+ * Initialize pool params
+ *
+ * Initialize an odp_pool_ext_param_t to its default values for all fields
+ * based on the selected pool type.
+ *
+ * @param type Pool type
+ * @param param odp_pool_ext_param_t to be initialized
+ */
+void odp_pool_ext_param_init(odp_pool_type_t type, odp_pool_ext_param_t *param);
+
+/**
+ * Create an external memory pool
+ *
+ * This routine is used to create a pool. The use of pool name is optional.
+ * Unique names are not required. However, odp_pool_lookup() returns only a
+ * single matching pool. Use odp_pool_ext_param_init() to initialize parameters
+ * into their default values.
+ *
+ * @param name Name of the pool or NULL. Maximum string length is ODP_POOL_NAME_LEN.
+ * @param param Pool parameters
+ *
+ * @return Pool handle on success
+ * @retval ODP_POOL_INVALID on failure
+ */
+odp_pool_t odp_pool_ext_create(const char *name, const odp_pool_ext_param_t *param);
+
+/**
+ * Populate external memory pool with buffer memory
+ *
+ * Populate can be called multiple times to add memory buffers into the pool. Application must
+ * populate the pool with the exact number of buffers specified in pool parameters. The pool is
+ * ready to be used for allocations only after all populate calls have returned successfully.
+ * Application marks the last populate call with ODP_POOL_POPULATE_DONE flag.
+ *
+ * Depending on pool usage (and ODP implementation), the memory may need to be accessible by
+ * HW accelerators. Application may use e.g. odp_shm_reserve() with ODP_SHM_HW_ACCESS flag to
+ * ensure HW access. The memory area used for one pool, starting from (or before) the lowest
+ * addressed buffer and extending to the end (or after) of the highest addressed buffer, must not
+ * overlap with the memory area used for any other pool. Pool capabilities
+ * (odp_pool_ext_capability_t) specify the minimum alignment of the memory area.
+ *
+ * Pool type defines memory buffer layout and where the buffer pointer (buf[N]) points
+ * in the layout. Pool capabilities specify requirements for buffer size, layout and
+ * pointer alignment.
+ *
+ * For packet pools, packet buffer layout is shown below. The packet headroom (odp_packet_head())
+ * starts immediately after the application header. For a segmented packet, each segment has this
+ * same layout. Buffer size includes all headers, headroom, data, tailroom and trailer.
+ *
+ * @code{.unparsed}
+ *
+ * +-------------------------------+ -- --
+ * buf[N] ---> | | | |
+ * | ODP header (optional) | > odp_header_size |
+ * | | | |
+ * +-------------------------------+ -- |
+ * | | | |
+ * | Application header (optional) | > app_header_size |
+ * | | | > buf_size
+ * +-------------------------------+ -- |
+ * odp_packet_head()--> | | |
+ * | Packet data | |
+ * | (headroom, data, tailroom) | |
+ * | | |
+ * | | |
+ * +-------------------------------+ -- |
+ * | | | |
+ * | ODP trailer (optional) | > odp_trailer_size |
+ * | | | |
+ * +-------------------------------+ -- --
+ *
+ * @endcode
+ *
+ * @param pool External memory pool
+ * @param buf Buffer pointers to be populated into the pool
+ * @param buf_size Buffer size
+ * @param num Number of buffer pointers
+ * @param flags 0: No flags
+ * ODP_POOL_POPULATE_DONE: Marks the last populate call and completes the pool
+ * population phase
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_pool_ext_populate(odp_pool_t pool, void *buf[], uint32_t buf_size, uint32_t num,
+ uint32_t flags);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/pool_types.h b/include/odp/api/spec/pool_types.h
new file mode 100644
index 000000000..cb3db4737
--- /dev/null
+++ b/include/odp/api/spec/pool_types.h
@@ -0,0 +1,944 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * Type definitions for pools
+ */
+
+#ifndef ODP_API_SPEC_POOL_TYPES_H_
+#define ODP_API_SPEC_POOL_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/dma_types.h>
+#include <odp/api/ml_types.h>
+
+/** @defgroup odp_pool ODP POOL
+ * @{
+ */
+
+/**
+ * @typedef odp_pool_t
+ * ODP pool
+ */
+
+/**
+ * @def ODP_POOL_INVALID
+ * Invalid pool
+ */
+
+/**
+ * @def ODP_POOL_NAME_LEN
+ * Maximum pool name length in chars including null char
+ */
+
+/**
+ * @def ODP_POOL_MAX_THREAD_STATS
+ * Maximum number of per thread statistics a single odp_pool_stats() call can read
+ */
+
+/** Maximum number of packet pool subparameters */
+#define ODP_POOL_MAX_SUBPARAMS 7
+
+/**
+ * Pool statistics counters options
+ *
+ * Pool statistics counters listed in a bit field structure.
+ */
+typedef union odp_pool_stats_opt_t {
+ /** Option flags */
+ struct {
+ /** See odp_pool_stats_t::available */
+ uint64_t available : 1;
+
+ /** See odp_pool_stats_t::alloc_ops */
+ uint64_t alloc_ops : 1;
+
+ /** See odp_pool_stats_t::alloc_fails */
+ uint64_t alloc_fails : 1;
+
+ /** See odp_pool_stats_t::free_ops */
+ uint64_t free_ops : 1;
+
+ /** See odp_pool_stats_t::total_ops */
+ uint64_t total_ops : 1;
+
+ /** See odp_pool_stats_t::cache_available */
+ uint64_t cache_available : 1;
+
+ /** See odp_pool_stats_t::cache_alloc_ops */
+ uint64_t cache_alloc_ops : 1;
+
+ /** See odp_pool_stats_t::cache_free_ops */
+ uint64_t cache_free_ops : 1;
+
+ /** See odp_pool_stats_t::thread::cache_available */
+ uint64_t thread_cache_available : 1;
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or for bitwise
+ * operations over the entire structure. */
+ uint64_t all;
+
+} odp_pool_stats_opt_t;
+
+/**
+ * Pool statistics counters
+ *
+ * In addition to API alloc and free calls, statistics counters may be updated
+ * by alloc/free operations from implementation internal software or hardware
+ * components.
+ */
+typedef struct odp_pool_stats_t {
+ /** The number of available events in the pool */
+ uint64_t available;
+
+ /** The number of alloc operations from the pool. Includes both
+ * successful and failed operations (pool empty). */
+ uint64_t alloc_ops;
+
+ /** The number of failed alloc operations (pool empty) */
+ uint64_t alloc_fails;
+
+ /** The number of free operations to the pool */
+ uint64_t free_ops;
+
+ /** The total number of alloc and free operations. Includes both
+ * successful and failed operations (pool empty). */
+ uint64_t total_ops;
+
+ /** The number of available events in the local caches of all threads */
+ uint64_t cache_available;
+
+ /** The number of successful alloc operations from pool caches (returned
+ * at least one event). */
+ uint64_t cache_alloc_ops;
+
+ /** The number of free operations, which stored events to pool caches. */
+ uint64_t cache_free_ops;
+
+ /** Per thread counters */
+ struct {
+ /** First thread identifier to read counters from. Ignored when
+ * 'thread.cache_available' is not enabled. */
+ uint16_t first;
+
+ /** Last thread identifier to read counters from. Ignored when
+ * 'thread.cache_available' is not enabled. */
+ uint16_t last;
+
+ /** The number of available events in each thread local cache
+ *
+ * If 'first' and 'last' include all threads of the instance,
+ * the sum of 'thread.cache_available' matches
+ * 'cache_available'. */
+ uint64_t cache_available[ODP_POOL_MAX_THREAD_STATS];
+ } thread;
+
+} odp_pool_stats_t;
+
+/**
+ * Pool statistics counters
+ *
+ * Same as odp_pool_stats_t excluding per thread counters.
+ */
+typedef struct odp_pool_stats_selected_t {
+ /** See odp_pool_stats_t::available */
+ uint64_t available;
+
+ /** See odp_pool_stats_t::alloc_ops */
+ uint64_t alloc_ops;
+
+ /** See odp_pool_stats_t::alloc_fails */
+ uint64_t alloc_fails;
+
+ /** See odp_pool_stats_t::free_ops */
+ uint64_t free_ops;
+
+ /** See odp_pool_stats_t::total_ops */
+ uint64_t total_ops;
+
+ /** See odp_pool_stats_t::cache_available */
+ uint64_t cache_available;
+
+ /** See odp_pool_stats_t::cache_alloc_ops */
+ uint64_t cache_alloc_ops;
+
+ /** See odp_pool_stats_t::cache_free_ops */
+ uint64_t cache_free_ops;
+
+} odp_pool_stats_selected_t;
+
+/**
+ * Pool capabilities
+ */
+typedef struct odp_pool_capability_t {
+ /** Maximum number of pools of any type (odp_pool_type_t) */
+ uint32_t max_pools;
+
+ /** Buffer pool capabilities */
+ struct {
+ /** Maximum number of buffer pools */
+ uint32_t max_pools;
+
+ /** Maximum buffer data alignment in bytes */
+ uint32_t max_align;
+
+ /** Maximum buffer data size in bytes
+ *
+ * The value of zero means that size is limited only by the
+ * available memory size for the pool. */
+ uint32_t max_size;
+
+ /** Maximum number of buffers of any size
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_num;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * When supported, implementation does not overwrite buffer user area
+ * content at any point of buffer lifetime nor after freeing a buffer
+ * back into pool.
+ *
+ * 0: User area content is maintained throughout regular buffer usage
+ * after allocation, but may be modified after free (default)
+ * 1: User area content is maintained throughout regular buffer usage
+ * and additionally also after buffer is freed into the pool (between
+ * buffer free and allocation) */
+ odp_bool_t uarea_persistence;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Supported statistics counters */
+ odp_pool_stats_opt_t stats;
+ } buf;
+
+ /** Packet pool capabilities */
+ struct {
+ /** Maximum number of packet pools */
+ uint32_t max_pools;
+
+ /** Maximum packet data length in bytes
+ *
+ * This defines the maximum packet data length that can be
+ * stored into a packet. Attempts to allocate or extend packets
+ * to sizes larger than this limit will fail.
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_len;
+
+ /** Maximum number of packets of any length
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_num;
+
+ /** Maximum packet data alignment in bytes
+ *
+ * This is the maximum value of packet pool alignment
+ * (pkt.align) parameter. */
+ uint32_t max_align;
+
+ /** Minimum packet level headroom length in bytes
+ *
+ * The minimum number of headroom bytes that newly created
+ * packets have by default. The default apply to both ODP
+ * packet input and user allocated packets.*/
+ uint32_t min_headroom;
+
+ /** Maximum packet level headroom length in bytes
+ *
+ * The maximum value of packet pool headroom parameter
+ * that can be configured. This value applies to both ODP
+ * packet input and user allocated packets.*/
+ uint32_t max_headroom;
+
+ /** Minimum packet level tailroom length in bytes
+ *
+ * The minimum number of tailroom bytes that newly created
+ * packets have by default. The default apply to both ODP
+ * packet input and user allocated packets.*/
+ uint32_t min_tailroom;
+
+ /** Maximum number of segments per packet */
+ uint32_t max_segs_per_pkt;
+
+ /** Minimum packet segment data length in bytes
+ *
+ * The user defined segment length (seg_len in
+ * odp_pool_param_t) will be rounded up into this value. */
+ uint32_t min_seg_len;
+
+ /** Maximum packet segment data length in bytes
+ *
+ * The user defined segment length (seg_len in odp_pool_param_t)
+ * must not be larger than this.
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_seg_len;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * See buf.uarea_persistence for details. */
+ odp_bool_t uarea_persistence;
+
+ /** Maximum number of subparameters
+ *
+ * Maximum number of packet pool subparameters. Valid range is
+ * 0 ... ODP_POOL_MAX_SUBPARAMS. */
+ uint8_t max_num_subparam;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Supported statistics counters */
+ odp_pool_stats_opt_t stats;
+ } pkt;
+
+ /** Timeout pool capabilities */
+ struct {
+ /** Maximum number of timeout pools */
+ uint32_t max_pools;
+
+ /** Maximum number of timeout events in a pool
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_num;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * See buf.uarea_persistence for details. */
+ odp_bool_t uarea_persistence;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Supported statistics counters */
+ odp_pool_stats_opt_t stats;
+ } tmo;
+
+ /** Vector pool capabilities */
+ struct {
+ /** Maximum number of vector pools */
+ uint32_t max_pools;
+
+ /** Maximum number of vector events in a pool
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_num;
+
+ /** Maximum number of handles (such as odp_packet_t) in a vector. */
+ uint32_t max_size;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * See buf.uarea_persistence for details. */
+ odp_bool_t uarea_persistence;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Supported statistics counters */
+ odp_pool_stats_opt_t stats;
+ } vector;
+
+} odp_pool_capability_t;
+
+/**
+ * Packet pool subparameters
+ */
+typedef struct odp_pool_pkt_subparam_t {
+ /** Number of 'len' byte packets. */
+ uint32_t num;
+
+ /** Packet length in bytes */
+ uint32_t len;
+
+} odp_pool_pkt_subparam_t;
+
+/**
+ * Pool types
+ */
+typedef enum odp_pool_type_t {
+ /** Packet pool*/
+ ODP_POOL_PACKET = ODP_EVENT_PACKET,
+
+ /** Buffer pool */
+ ODP_POOL_BUFFER = ODP_EVENT_BUFFER,
+
+ /** Timeout pool */
+ ODP_POOL_TIMEOUT = ODP_EVENT_TIMEOUT,
+
+ /** Vector event pool
+ *
+ * Each vector event holds an array of handles. All handles of a vector
+ * are the same type (such as odp_packet_t).
+ * @see ODP_EVENT_PACKET_VECTOR
+ */
+ ODP_POOL_VECTOR,
+
+ /** DMA completion event pool */
+ ODP_POOL_DMA_COMPL,
+
+ /** ML completion event pool */
+ ODP_POOL_ML_COMPL
+
+} odp_pool_type_t;
+
+/**
+ * Pool parameters
+ */
+typedef struct odp_pool_param_t {
+ /** Pool type */
+ odp_pool_type_t type;
+
+ /** Parameters for buffer pools */
+ struct {
+ /** Number of buffers in the pool */
+ uint32_t num;
+
+ /** Buffer size in bytes. The maximum number of bytes
+ * application will store in each buffer.
+ */
+ uint32_t size;
+
+ /** Minimum buffer alignment in bytes. Valid values are
+ * powers of two. Use 0 for default alignment.
+ * Default will always be a multiple of 8.
+ */
+ uint32_t align;
+
+ /** Minimum user area size in bytes. The maximum value is defined by
+ * pool capability buf.max_uarea_size. Specify as 0 if no user
+ * area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Maximum number of buffers cached locally per thread
+ *
+ * A non-zero value allows implementation to cache buffers
+ * locally per each thread. Thread local caching may improve
+ * performance, but requires application to take account that
+ * some buffers may be stored locally per thread and thus are
+ * not available for allocation from other threads.
+ *
+ * This is the maximum number of buffers to be cached per
+ * thread. The actual cache size is implementation specific.
+ * The value must not be less than 'min_cache_size' or exceed
+ * 'max_cache_size' capability. The default value is
+ * implementation specific and set by odp_pool_param_init().
+ */
+ uint32_t cache_size;
+ } buf;
+
+ /** Parameters for packet pools */
+ struct {
+ /** Minimum number of 'len' byte packets.
+ *
+ * The pool must contain at least this many packets that are
+ * 'len' bytes or smaller. An implementation may round up the
+ * value, as long as the 'max_num' parameter below is not
+ * violated. The maximum value for this field is defined by
+ * pool capability pkt.max_num.
+ */
+ uint32_t num;
+
+ /** Maximum number of packets.
+ *
+ * This is the maximum number of packets of any length that can
+ * be allocated from the pool. The maximum value is defined by
+ * pool capability pkt.max_num. Use 0 when there's no
+ * requirement for the maximum number of packets. The default
+ * value is 0.
+ */
+ uint32_t max_num;
+
+ /** Minimum length of 'num' packets.
+ *
+ * The pool must contain at least 'num' packets up to this
+ * packet length (1 ... 'len' bytes). The maximum value for
+ * this field is defined by pool capability pkt.max_len.
+ * Use 0 for default.
+ */
+ uint32_t len;
+
+ /** Maximum packet length that will be allocated from
+ * the pool. The maximum value is defined by pool capability
+ * pkt.max_len. Use 0 for default.
+ */
+ uint32_t max_len;
+
+ /** Minimum packet data alignment in bytes.
+ *
+ * Valid values are powers of two. User allocated packets have
+ * start of data (see odp_packet_data()) aligned to this or
+ * a higher alignment (power of two value). This parameter
+ * does not apply to packets that ODP allocates internally
+ * (e.g. packets from packet input).
+ *
+ * The maximum value is defined by pool capability
+ * pkt.max_align. Use 0 for default alignment.
+ */
+ uint32_t align;
+
+ /** Minimum number of packet data bytes that can be stored in
+ * the first segment of a newly allocated packet (starting from
+ * odp_packet_data()). The maximum value is defined by
+ * pool capability pkt.max_seg_len. Use 0 for default.
+ */
+ uint32_t seg_len;
+
+ /** Minimum user area size in bytes. The maximum value is defined by
+ * pool capability pkt.max_uarea_size. Specify as 0 if no user
+ * area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Minimum headroom size in bytes. Each newly allocated
+ * packet from the pool must have at least this much headroom.
+ * The maximum value is defined by pool capability
+ * pkt.max_headroom. Use zero if headroom is not needed.
+ */
+ uint32_t headroom;
+
+ /** Number of subparameters
+ *
+ * The number of subparameter table entries used. The maximum
+ * value is defined by pool capability pkt.max_num_subparam.
+ * The default value is 0.
+ */
+ uint8_t num_subparam;
+
+ /** Subparameter table
+ *
+ * Subparameters continue pool configuration with additional
+ * packet length requirements. The first table entry follows
+ * the num/len specification above. So that, sub[0].len > 'len'
+ * and sub[0].num refers to packet lengths between 'len' + 1
+ * and sub[0].len. Similarly, sub[1] follows sub[0]
+ * specification, and so on.
+ *
+ * Each requirement is supported separately and may be rounded
+ * up, as long as the 'max_num' parameter is not violated. It's
+ * implementation specific if some requirements are supported
+ * simultaneously (e.g. due to subpool design).
+ */
+ odp_pool_pkt_subparam_t sub[ODP_POOL_MAX_SUBPARAMS];
+
+ /** Maximum number of packets cached locally per thread
+ *
+ * See buf.cache_size documentation for details.
+ */
+ uint32_t cache_size;
+ } pkt;
+
+ /** Parameters for timeout pools */
+ struct {
+ /** Number of timeouts in the pool */
+ uint32_t num;
+
+ /** Minimum user area size in bytes. The maximum value is defined by
+ * pool capability tmo.max_uarea_size. Specify as 0 if no user
+ * area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Maximum number of timeouts cached locally per thread
+ *
+ * See buf.cache_size documentation for details.
+ */
+ uint32_t cache_size;
+ } tmo;
+
+ /** Parameters for vector pools */
+ struct {
+ /** Number of vectors in the pool */
+ uint32_t num;
+
+ /** Maximum number of handles (such as odp_packet_t) in a vector. */
+ uint32_t max_size;
+
+ /** Minimum user area size in bytes. The maximum value is defined by
+ * pool capability vector.max_uarea_size. Specify as 0 if no user
+ * area is needed. The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Maximum number of vectors cached locally per thread
+ *
+ * See buf.cache_size documentation for details.
+ */
+ uint32_t cache_size;
+ } vector;
+
+ /** Parameters for user area initialization */
+ struct {
+ /** User area initialization function
+ *
+ * Application defined user area initialization function to be
+ * called for each event of the pool during odp_pool_create(). Ignored
+ * if user area persistence is not supported
+ * (odp_pool_capability_t::uarea_persistence) or pool will not have any
+ * user area. The default value is NULL.
+ *
+ * @param uarea Pointer to the user area of an event
+ * @param size User area size
+ * @param args Pointer to application defined arguments
+ * @param index Index of the event (0..num events in pool - 1), not
+ * necessarily in order
+ */
+ void (*init_fn)(void *uarea, uint32_t size, void *args, uint32_t index);
+
+ /** Pointer to application defined arguments to be passed to every call
+ * of init_fn. The default value is NULL.
+ */
+ void *args;
+ } uarea_init;
+
+ /**
+ * Configure statistics counters
+ *
+ * An application can read the enabled statistics counters using
+ * odp_pool_stats(). For optimal performance an application should
+ * enable only the required counters.
+ */
+ odp_pool_stats_opt_t stats;
+
+} odp_pool_param_t;
+
+/**
+ * External memory pool population done
+ *
+ * Application uses this flag to mark the last odp_pool_ext_populate() call, which completes
+ * external memory pool population phase.
+ */
+#define ODP_POOL_POPULATE_DONE 0x1
+
+/**
+ * External memory pool capabilities
+ *
+ * Generic fields (not specific to a pool type) contain capabilities
+ * of the requested pool type.
+ */
+typedef struct odp_pool_ext_capability_t {
+ /** Requested pool type
+ *
+ * Pool type from the odp_pool_ext_capability() call is recorded here for reference. */
+ odp_pool_type_t type;
+
+ /** Maximum number of pools
+ *
+ * Maximum number of external memory pools of the requested type. */
+ uint32_t max_pools;
+
+ /** Minimum size of thread local cache */
+ uint32_t min_cache_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Supported statistics counters */
+ odp_pool_stats_opt_t stats;
+
+ /** Packet pool capabilities */
+ struct {
+ /** Maximum number of packet buffers */
+ uint32_t max_num_buf;
+
+ /** Maximum packet buffer size in bytes */
+ uint32_t max_buf_size;
+
+ /** ODP header size in bytes
+ *
+ * Application must reserve this many bytes from the start of a packet buffer
+ * for ODP implementation usage. When the value is zero, ODP implementation does
+ * not need header space to be reserved for it. Application will not modify this
+ * memory area (after buffer populate call).
+ */
+ uint32_t odp_header_size;
+
+ /** ODP trailer size in bytes
+ *
+ * Application must reserve this many bytes from the end of a packet buffer
+ * for ODP implementation usage. When the value is zero, ODP implementation does
+ * not need trailer space to be reserved for it. Application will not modify this
+ * memory area (after buffer populate call).
+ */
+ uint32_t odp_trailer_size;
+
+ /** Minimum packet pool memory area alignment in bytes
+ *
+ * The memory area used for a packet pool, starting from (or before) the lowest
+ * addressed buffer and extending to the end (or after) of the highest addressed
+ * buffer, must have at least this (power of two) alignment. The value is 1 when
+ * there is no alignment requirement.
+ */
+ uint32_t min_mem_align;
+
+ /** Minimum packet buffer pointer alignment in bytes
+ *
+ * Packet buffer pointers populated into a pool must be evenly divisible with
+ * this value. The value is 1 when there is no alignment requirement.
+ */
+ uint32_t min_buf_align;
+
+ /** Minimum packet headroom alignment in bytes
+ *
+ * Packet buffers populated into a pool must have their headroom start address
+ * evenly divisible with this value. The value is 1 when there is no alignment
+ * requirement.
+ */
+ uint32_t min_head_align;
+
+ /** Packet buffer alignment flags
+ *
+ * These flags specify additional alignment requirements for packet buffers.
+ * If not stated otherwise, min_buf_align and min_head_align alignment
+ * requirements apply also.
+ */
+ struct {
+ /** Packet buffers are size aligned
+ *
+ * When set, packet buffer pointers must be aligned to the buffer size.
+ * For example, if the buffer size would be 2304 bytes (0x900),
+ * each buffer start address must be a multiple of 0x900
+ * (e.g. 0x12000900, 0x12001200, 0x12004800, etc). */
+ uint16_t buf_size_aligned : 1;
+
+ };
+
+ /** Maximum headroom parameter value
+ *
+ * The packet pool headroom parameter may not exceed this value.
+ */
+ uint32_t max_headroom;
+
+ /** Maximum headroom size in bytes
+ *
+ * Any newly allocated packet will have at most this much headroom. Application
+ * may use this to ensure that packet buffer size is large enough to fit both
+ * buffer headers, headroom and data.
+ */
+ uint32_t max_headroom_size;
+
+ /** Maximum number of segments per packet */
+ uint32_t max_segs_per_pkt;
+
+ /** Maximum user area size in bytes */
+ uint32_t max_uarea_size;
+
+ /** Pool user area persistence
+ *
+ * See buf.uarea_persistence of odp_pool_capability_t for details
+ * (odp_pool_capability_t::uarea_persistence). */
+ odp_bool_t uarea_persistence;
+
+ } pkt;
+
+} odp_pool_ext_capability_t;
+
+/**
+ * External memory pool parameters
+ */
+typedef struct odp_pool_ext_param_t {
+ /** Pool type */
+ odp_pool_type_t type;
+
+ /** Parameters for user area initialization */
+ struct {
+ /** See uarea_init.init_fn of odp_pool_param_t for details
+ * (odp_pool_param_t::init_fn). However, note that with external memory
+ * pools, this function is called during memory population and not during
+ * pool creation (odp_pool_ext_populate()). Depending on the implementation,
+ * the function may be called each time pool is being populated with
+ * odp_pool_ext_populate() or during the last population call
+ * (odp_pool_ext_populate() with #ODP_POOL_POPULATE_DONE). */
+ void (*init_fn)(void *uarea, uint32_t size, void *args, uint32_t index);
+
+ /** See uarea_init.args of odp_pool_param_t for details
+ * (odp_pool_param_t::args). */
+ void *args;
+
+ } uarea_init;
+
+ /** Maximum thread local cache size for the pool
+ *
+ * Valid value range is from min_cache_size to max_cache_size capability.
+ * The default value is implementation specific. See odp_pool_param_t (buf.cache_size)
+ * for more detailed documentation.
+ */
+ uint32_t cache_size;
+
+ /**
+ * Pool statistics configuration
+ *
+ * All pool statistics are disabled by default. For optimal performance, enable only those
+ * counters that are actually used. Counters may be read with odp_pool_stats().
+ */
+ odp_pool_stats_opt_t stats;
+
+ /** Parameters for packet pools */
+ struct {
+ /** Number of packet buffers
+ *
+ * The number of packet buffers application will populate into the pool.
+ * The maximum value is defined by pool capability pkt.max_num_buf.
+ */
+ uint32_t num_buf;
+
+ /** Packet buffer size
+ *
+ * Total buffer size in bytes including all headers, trailer, head-/tailroom
+ * and data. This is calculated from buffer start pointer to the end of buffer
+ * data area (including tailroom) or ODP trailer (see odp_trailer_size capability).
+ * All packet buffers application populates into the pool are of this size.
+ */
+ uint32_t buf_size;
+
+ /** Application header size
+ *
+ * Application reserves this many bytes for its own buffer header usage.
+ * The application header follows immediately the ODP buffer header
+ * (see odp_header_size capability). ODP implementation will not modify this
+ * memory area. The default value is 0.
+ */
+ uint32_t app_header_size;
+
+ /** User area size
+ *
+ * Per packet user area size in bytes. As with normal pools, user area location
+ * is ODP implementation specific. Use zero if no user area is needed.
+ * The maximum value is defined by pool capability pkt.max_uarea_size.
+ * The default value is 0.
+ */
+ uint32_t uarea_size;
+
+ /** Minimum headroom size
+ *
+ * Each newly allocated packet from the pool must have at least this much
+ * headroom in bytes. The configuration applies to both ODP packet input and
+ * application allocated packets. Use zero if headroom is not needed. The maximum
+ * value is defined by pool capability pkt.max_headroom. Implementation may
+ * round up the initial headroom size up to pool capability pkt.max_headroom_size.
+ */
+ uint32_t headroom;
+
+ } pkt;
+
+} odp_pool_ext_param_t;
+
+/**
+ * Pool information struct
+ * Used to get information about a pool.
+ */
+typedef struct odp_pool_info_t {
+ /** Pool type */
+ odp_pool_type_t type;
+
+ /** Pool name */
+ const char *name;
+
+ /** External memory pool
+ *
+ * 0: Pool is a normal pool
+ * 1: Pool is an external memory pool
+ */
+ odp_bool_t pool_ext;
+
+ /** Pool parameters union */
+ union {
+ /** Copy of pool parameters. This is set when pool_ext is 0. */
+ odp_pool_param_t params;
+
+ /** Copy of external memory pool parameters. This is set when pool_ext is 1. */
+ odp_pool_ext_param_t pool_ext_param;
+
+ /** Copy of pool parameters when pool type is ODP_POOL_DMA_COMPL. */
+ odp_dma_pool_param_t dma_pool_param;
+
+ /** Copy of pool parameters when pool type is ODP_POOL_ML_COMPL. */
+ odp_ml_compl_pool_param_t ml_pool_param;
+ };
+
+ /** Additional info for packet pools */
+ struct {
+ /** Maximum number of packets of any length
+ *
+ * This is the maximum number of packets that can be allocated
+ * from the pool at anytime. Application can use this e.g.
+ * to prepare enough per packet contexts.
+ */
+ uint32_t max_num;
+
+ } pkt;
+
+ /** Minimum data address.
+ *
+ * This is the minimum address that application accessible
+ * data of any object (event) allocated from the pool may
+ * locate. When there's no application accessible data
+ * (e.g. ODP_POOL_TIMEOUT pools), the value may be zero.
+ */
+ uintptr_t min_data_addr;
+
+ /** Maximum data address.
+ *
+ * This is the maximum address that application accessible
+ * data of any object (event) allocated from the pool may
+ * locate. When there's no application accessible data
+ * (e.g. ODP_POOL_TIMEOUT pools), the value may be zero.
+ */
+ uintptr_t max_data_addr;
+
+} odp_pool_info_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/proto_stats.h b/include/odp/api/spec/proto_stats.h
new file mode 100644
index 000000000..7dd57ac0f
--- /dev/null
+++ b/include/odp/api/spec/proto_stats.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP Proto Stats
+ */
+
+#ifndef ODP_API_SPEC_PROTO_STATS_H_
+#define ODP_API_SPEC_PROTO_STATS_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/proto_stats_types.h>
+
+/** @addtogroup odp_proto_stats
+ * Flow specific packet statistics.
+ * @{
+ */
+
+/**
+ * Initialize proto stats parameters
+ *
+ * Initialize an odp_proto_stats_param_t to its default values.
+ * By default all the statistics are disabled.
+ *
+ * @param param Proto stats parameter pointer.
+ */
+void odp_proto_stats_param_init(odp_proto_stats_param_t *param);
+
+/**
+ * Get proto stats capability
+ *
+ * Get supported protocol statistics and metadata for a PKTIO.
+ *
+ * @param pktio Packet IO handle
+ * @param[out] capa Pointer where capabilities are updated
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_proto_stats_capability(odp_pktio_t pktio, odp_proto_stats_capability_t *capa);
+
+/**
+ * Create a proto stats object
+ *
+ * Create a proto stats object with given name and parameters.
+ * A proto stats object can be created with any set of statistics but only the
+ * statistics that are supported by a PKTIO are updated in a proto stats object
+ * for that PKTIO associated packets. Same proto stats object can be used with
+ * any PKTIO.
+ *
+ * @param name Object name
+ * @param param Proto stats parameters
+ *
+ * @return Proto stats object handle
+ * @retval ODP_PROTO_STATS_INVALID on failure
+ */
+odp_proto_stats_t odp_proto_stats_create(const char *name, const odp_proto_stats_param_t *param);
+
+/**
+ * Lookup a proto stats object by name
+ *
+ * Lookup an already created proto stats object by name.
+ *
+ * @param name Proto stats object name
+ *
+ * @return Proto stats object handle
+ * @retval ODP_PROTO_STATS_INVALID on failure
+ */
+odp_proto_stats_t odp_proto_stats_lookup(const char *name);
+
+/**
+ * Destroy a proto stats object
+ *
+ * Destroy a proto stats object already created.
+ *
+ * Before destroying proto stats object having tx statistics enabled,
+ * for all PKTIO devices to which packets were Tx'ed earlier with
+ * this proto stats object, odp_pktio_stop() must be called. Additionally,
+ * existing packets that refer to the proto stats object being destroyed
+ * must not be sent at the same time as or after the proto stats object
+ * destruction.
+ *
+ * @param stat Proto stats handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_proto_stats_destroy(odp_proto_stats_t stat);
+
+/**
+ * Get all proto stats counters
+ *
+ * Get current values of all counters of the proto stats object.
+ * The values of counters that are not enabled in the proto stats object are undefined.
+ *
+ * @param stat Proto stats object handle
+ * @param[out] data Pointer to a caller allocated structure where the statistics will
+ * be written to.
+ *
+ * @retval =0 on success
+ * @retval <0 on failure
+ */
+int odp_proto_stats(odp_proto_stats_t stat, odp_proto_stats_data_t *data);
+
+/**
+ * Print proto stats object info to ODP log.
+ *
+ * Print implementation-defined proto stats debug information to ODP log.
+ *
+ * @param stat Proto stats object handle
+ */
+void odp_proto_stats_print(odp_proto_stats_t stat);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/proto_stats_types.h b/include/odp/api/spec/proto_stats_types.h
new file mode 100644
index 000000000..4c08e60ab
--- /dev/null
+++ b/include/odp/api/spec/proto_stats_types.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP proto stats types
+ */
+
+#ifndef ODP_API_SPEC_PROTO_STATS_TYPES_H_
+#define ODP_API_SPEC_PROTO_STATS_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_proto_stats ODP PROTO STATS
+ * @{
+ */
+
+/**
+ * @typedef odp_proto_stats_t
+ * ODP proto stats handle
+ */
+
+/**
+ * @def ODP_PROTO_STATS_INVALID
+ * Invalid proto stats handle
+ */
+
+/** ODP proto stats counters
+ *
+ * Statistics that can be enabled in proto stats object. For Tx stats counters,
+ * Pktout config `odp_pktout_config_opt_t::bit::proto_stats_ena` needs to be
+ * enabled.
+ *
+ * Tx packet and octet sent/drop statistics might include packets sent/dropped via
+ * Traffic Manager or Tx packet Aging or due to any other Tx errors. It is
+ * implementation specific as to what all Tx sent/drop events are accounted for.
+ */
+typedef union odp_proto_stats_counters_t {
+ /** Option flags */
+ struct {
+ /** Tx packet sent count */
+ uint64_t tx_pkts : 1;
+
+ /** Tx packet drop count */
+ uint64_t tx_pkt_drops : 1;
+
+ /** Tx packet sent Octet counter 0 */
+ uint64_t tx_oct_count0 : 1;
+
+ /** Tx packet drop Octet counter 0 */
+ uint64_t tx_oct_count0_drops : 1;
+
+ /** Tx packet sent octet counter 1 */
+ uint64_t tx_oct_count1 : 1;
+
+ /** Tx packet drop octet counter 1 */
+ uint64_t tx_oct_count1_drops : 1;
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or bitwise
+ * operations over the entire structure.
+ */
+ uint64_t all_bits;
+} odp_proto_stats_counters_t;
+
+/** ODP proto stats params */
+typedef struct odp_proto_stats_param_t {
+ /** Stats counters to enable */
+ odp_proto_stats_counters_t counters;
+} odp_proto_stats_param_t;
+
+/**
+ * Proto stats capabilities
+ */
+typedef struct odp_proto_stats_capability_t {
+ /** Tx capabilities */
+ struct {
+ /** Stats counters supported */
+ odp_proto_stats_counters_t counters;
+
+ /** Packet adjust support for Octet counter 0 */
+ odp_bool_t oct_count0_adj;
+
+ /** Packet adjust support for Octet counter 1 */
+ odp_bool_t oct_count1_adj;
+ } tx;
+} odp_proto_stats_capability_t;
+
+/** ODP proto stats counters */
+typedef struct odp_proto_stats_data_t {
+ /** Packet sent count */
+ uint64_t tx_pkts;
+
+ /** Packet drop count */
+ uint64_t tx_pkt_drops;
+
+ /** Packet sent Octet counter 0 */
+ uint64_t tx_oct_count0;
+
+ /** Packet drop Octet counter 0 */
+ uint64_t tx_oct_count0_drops;
+
+ /** Packet sent octet counter 1 */
+ uint64_t tx_oct_count1;
+
+ /** Packet drop octet counter 1 */
+ uint64_t tx_oct_count1_drops;
+} odp_proto_stats_data_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/protocols.h b/include/odp/api/spec/protocols.h
new file mode 100644
index 000000000..104002937
--- /dev/null
+++ b/include/odp/api/spec/protocols.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP protocols
+ */
+
+#ifndef ODP_API_SPEC_PROTOCOLS_H_
+#define ODP_API_SPEC_PROTOCOLS_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup odp_protocols ODP PROTOCOLS
+ * Network protocols
+ * @{
+ */
+
+/** IPv4 address size */
+#define ODP_IPV4_ADDR_SIZE 4
+
+/** IPv6 address size */
+#define ODP_IPV6_ADDR_SIZE 16
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/queue.h b/include/odp/api/spec/queue.h
index 9dd0a5618..9ce2ac73f 100644
--- a/include/odp/api/spec/queue.h
+++ b/include/odp/api/spec/queue.h
@@ -1,218 +1,39 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
-
/**
* @file
*
* ODP queue
*/
-#ifndef ODP_API_QUEUE_H_
-#define ODP_API_QUEUE_H_
+#ifndef ODP_API_SPEC_QUEUE_H_
+#define ODP_API_SPEC_QUEUE_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-#include <odp/api/schedule_types.h>
-#include <odp/api/event.h>
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/std_types.h>
-/** @defgroup odp_queue ODP QUEUE
- * Macros and operation on a queue.
+/** @addtogroup odp_queue
+ * Queues for event passing and scheduling.
* @{
*/
/**
- * @typedef odp_queue_t
- * ODP queue
- */
-
-/**
- * @typedef odp_queue_group_t
- * Queue group instance type
- */
-
-/**
- * @def ODP_QUEUE_INVALID
- * Invalid queue
- */
-
-/**
- * @def ODP_QUEUE_NAME_LEN
- * Maximum queue name length in chars including null char
- */
-
-/**
- * Queue type
- */
-typedef enum odp_queue_type_t {
- /** Plain queue
- *
- * Plain queues offer simple FIFO storage of events. Application may
- * dequeue directly from these queues. */
- ODP_QUEUE_TYPE_PLAIN = 0,
-
- /** Scheduled queue
- *
- * Scheduled queues are connected to the scheduler. Application must
- * not dequeue events directly from these queues but use the scheduler
- * instead. */
- ODP_QUEUE_TYPE_SCHED
-} odp_queue_type_t;
-
-/**
- * Queue operation mode
- */
-typedef enum odp_queue_op_mode_t {
- /** Multithread safe operation
- *
- * Queue operation (enqueue or dequeue) is multithread safe. Any
- * number of application threads may perform the operation
- * concurrently. */
- ODP_QUEUE_OP_MT = 0,
-
- /** Not multithread safe operation
- *
- * Queue operation (enqueue or dequeue) may not be multithread safe.
- * Application ensures synchronization between threads so that
- * simultaneously only single thread attempts the operation on
- * the same queue. */
- ODP_QUEUE_OP_MT_UNSAFE,
-
- /** Disabled
- *
- * Direct enqueue or dequeue operation from application is disabled.
- * An attempt to enqueue/dequeue directly will result undefined
- * behaviour. Various ODP functions (e.g. packet input, timer,
- * crypto, scheduler, etc) are able to perform enqueue or
- * dequeue operations normally on the queue.
- * */
- ODP_QUEUE_OP_DISABLED
-
-} odp_queue_op_mode_t;
-
-/**
- * Queue capabilities
- */
-typedef struct odp_queue_capability_t {
- /** Maximum number of event queues of any type (default size). Use
- * this in addition to queue type specific 'max_num', if both queue
- * types are used simultaneously. */
- uint32_t max_queues;
-
- /** Maximum number of ordered locks per queue */
- unsigned max_ordered_locks;
-
- /** Maximum number of scheduling groups */
- unsigned max_sched_groups;
-
- /** Number of scheduling priorities */
- unsigned sched_prios;
-
- /** Plain queue capabilities */
- struct {
- /** Maximum number of plain queues of the default size. */
- uint32_t max_num;
-
- /** Maximum number of events a plain queue can store
- * simultaneously. The value of zero means that plain
- * queues do not have a size limit, but a single queue can
- * store all available events. */
- uint32_t max_size;
-
- } plain;
-
- /** Scheduled queue capabilities */
- struct {
- /** Maximum number of scheduled queues of the default size. */
- uint32_t max_num;
-
- /** Maximum number of events a scheduled queue can store
- * simultaneously. The value of zero means that scheduled
- * queues do not have a size limit, but a single queue can
- * store all available events. */
- uint32_t max_size;
-
- } sched;
-
-} odp_queue_capability_t;
-
-/**
- * ODP Queue parameters
- */
-typedef struct odp_queue_param_t {
- /** Queue type
- *
- * Valid values for other parameters in this structure depend on
- * the queue type. */
- odp_queue_type_t type;
-
- /** Enqueue mode
- *
- * Default value for both queue types is ODP_QUEUE_OP_MT. Application
- * may enable performance optimizations by defining MT_UNSAFE or
- * DISABLED modes when applicable. */
- odp_queue_op_mode_t enq_mode;
-
- /** Dequeue mode
- *
- * For PLAIN queues, the default value is ODP_QUEUE_OP_MT. Application
- * may enable performance optimizations by defining MT_UNSAFE or
- * DISABLED modes when applicable. However, when a plain queue is input
- * to the implementation (e.g. a queue for packet output), the
- * parameter is ignored in queue creation and the value is
- * ODP_QUEUE_OP_DISABLED.
- *
- * For SCHED queues, the parameter is ignored in queue creation and
- * the value is ODP_QUEUE_OP_DISABLED. */
- odp_queue_op_mode_t deq_mode;
-
- /** Scheduler parameters
- *
- * These parameters are considered only when queue type is
- * ODP_QUEUE_TYPE_SCHED. */
- odp_schedule_param_t sched;
-
- /** Queue context pointer
- *
- * User defined context pointer associated with the queue. The same
- * pointer can be accessed with odp_queue_context() and
- * odp_queue_context_set() calls. The implementation may read the
- * pointer for prefetching the context data. Default value of the
- * pointer is NULL. */
- void *context;
-
- /** Queue context data length
- *
- * User defined context data length in bytes for prefetching.
- * The implementation may use this value as a hint for the number of
- * context data bytes to prefetch. Default value is zero (no hint). */
- uint32_t context_len;
-
- /** Queue size
- *
- * The queue must be able to store at minimum this many events
- * simultaneously. The value must not exceed 'max_size' queue
- * capability. The value of zero means implementation specific
- * default size. */
- uint32_t size;
-
-} odp_queue_param_t;
-
-/**
* Queue create
*
- * Create a queue according to the queue parameters. Queue type is specified by
- * queue parameter 'type'. Use odp_queue_param_init() to initialize parameters
- * into their default values. Default values are also used when 'param' pointer
- * is NULL. The default queue type is ODP_QUEUE_TYPE_PLAIN. The use of queue
- * name is optional. Unique names are not required. However, odp_queue_lookup()
- * returns only a single matching queue.
+ * Create a queue according to the queue parameters. The use of queue name is
+ * optional. Unique names are not required. However, odp_queue_lookup() returns
+ * only a single matching queue. Use odp_queue_param_init() to initialize
+ * parameters into their default values. Default values are also used when
+ * 'param' pointer is NULL.
*
* @param name Name of the queue or NULL. Maximum string length is
* ODP_QUEUE_NAME_LEN.
@@ -224,12 +45,40 @@ typedef struct odp_queue_param_t {
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param);
/**
+ * Create multiple queues
+ *
+ * Otherwise like odp_queue_create(), but creates multiple queues with a single
+ * call. The output queue handles are written in the same order as input
+ * parameters. A single odp_queue_create_multi() call is equivalent to calling
+ * odp_queue_create() 'num' times in row.
+ *
+ * If 'share_param' value is false, 'param' array must contain 'num' elements.
+ * If the value is true, only a single element is required and it's used as
+ * queue parameters for all created queues. If 'name' array is not NULL, the
+ * array must contain 'num' elements.
+ *
+ * @param name Array of queue name pointers or NULL. NULL is also
+ * valid queue name pointer value.
+ * @param param Array of queue parameters
+ * @param share_param If true, use same parameters ('param[0]') for all
+ * queues.
+ * @param[out] queue Array of queue handles for output
+ * @param num Number of queues to create
+ *
+ * @return Number of queues actually created (0 ... num)
+ * @retval <0 on failure
+ */
+int odp_queue_create_multi(const char *name[], const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[],
+ int num);
+
+/**
* Destroy ODP queue
*
* Destroys ODP queue. The queue must be empty and detached from other
- * ODP API (crypto, pktio, etc). Application must ensure that no other
- * operations on this queue are invoked in parallel. Otherwise behavior
- * is undefined.
+ * ODP API (crypto, pktio, classifier, timer, etc). Application must ensure
+ * that no other operations on this queue are invoked in parallel. Otherwise
+ * behavior is undefined.
*
* @param queue Queue handle
*
@@ -239,6 +88,20 @@ odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param);
int odp_queue_destroy(odp_queue_t queue);
/**
+ * Destroy multiple queues
+ *
+ * Otherwise like odp_queue_destroy(), but destroys multiple queues with a
+ * single call.
+ *
+ * @param queue Array of queue handles
+ * @param num Number of queues to destroy
+ *
+ * @retval Number of queues actually destroyed (1 ... num)
+ * @retval <0 on failure
+ */
+int odp_queue_destroy_multi(odp_queue_t queue[], int num);
+
+/**
* Find a queue by name
*
* @param name Queue name
@@ -279,18 +142,30 @@ int odp_queue_context_set(odp_queue_t queue, void *context, uint32_t len);
/**
* Get queue context
*
+ * Returns previously stored queue context pointer. The context pointer may
+ * be set with odp_queue_context_set() or during queue creation
+ * (see odp_queue_param_t). The pointer value is set to NULL by default.
+ *
* @param queue Queue handle
*
* @return pointer to the queue context
* @retval NULL on failure
+ *
+ * @see odp_queue_context_set(), odp_queue_create()
*/
void *odp_queue_context(odp_queue_t queue);
/**
- * Queue enqueue
+ * Enqueue an event to a queue
*
- * Enqueue the 'ev' on 'queue'. On failure the event is not consumed, the caller
- * has to take care of it.
+ * Enqueues the event into the queue. The caller loses ownership of the event on
+ * a successful call. The event is not enqueued on failure, and the caller
+ * maintains ownership of it.
+ *
+ * When successful, this function acts as a release memory barrier between
+ * the sender (the calling thread) and the receiver of the event. The receiver
+ * sees correctly the memory stores done by the sender before it enqueued
+ * the event.
*
* @param queue Queue handle
* @param ev Event handle
@@ -303,14 +178,15 @@ int odp_queue_enq(odp_queue_t queue, odp_event_t ev);
/**
* Enqueue multiple events to a queue
*
- * Enqueue the events from 'events[]' on 'queue'. A successful call returns the
- * actual number of events enqueued. If return value is less than 'num', the
- * remaining events at the end of events[] are not consumed, and the caller
- * has to take care of them.
+ * Like odp_queue_enq(), but enqueues multiple events into the queue. Events are
+ * stored into the queue in the order they are in the array. A successful
+ * call returns the actual number of events enqueued. If return value is less
+ * than 'num', the remaining events at the end of events[] are not enqueued,
+ * and the caller maintains ownership of those.
*
* @param queue Queue handle
- * @param[in] events Array of event handles
- * @param num Number of event handles to enqueue
+ * @param events Array of event handles
+ * @param num Number of events to enqueue
*
* @return Number of events actually enqueued (0 ... num)
* @retval <0 on failure
@@ -318,27 +194,34 @@ int odp_queue_enq(odp_queue_t queue, odp_event_t ev);
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num);
/**
- * Queue dequeue
+ * Dequeue an event from a queue
+ *
+ * Returns the next event from head of the queue, or ODP_EVENT_INVALID when the
+ * queue is empty. Cannot be used for ODP_QUEUE_TYPE_SCHED type queues
+ * (use odp_schedule() instead).
*
- * Dequeues next event from head of the queue. Cannot be used for
- * ODP_QUEUE_TYPE_SCHED type queues (use odp_schedule() instead).
+ * When successful, this function acts as an acquire memory barrier between
+ * the sender and the receiver (the calling thread) of the event. The receiver
+ * sees correctly the memory stores done by the sender before it enqueued
+ * the event.
*
* @param queue Queue handle
*
* @return Event handle
- * @retval ODP_EVENT_INVALID on failure (e.g. queue empty)
+ * @retval ODP_EVENT_INVALID on failure, or when the queue is empty
*/
odp_event_t odp_queue_deq(odp_queue_t queue);
/**
* Dequeue multiple events from a queue
*
- * Dequeues multiple events from head of the queue. Cannot be used for
- * ODP_QUEUE_TYPE_SCHED type queues (use odp_schedule() instead).
+ * Like odp_queue_deq(), but dequeues multiple events from head of the queue.
+ * Cannot be used for ODP_QUEUE_TYPE_SCHED type queues (use odp_schedule_multi()
+ * instead). A successful call returns the actual number of events dequeued.
*
- * @param queue Queue handle
+ * @param queue Queue handle
* @param[out] events Array of event handles for output
- * @param num Maximum number of events to dequeue
+ * @param num Maximum number of events to dequeue
* @return Number of events actually dequeued (0 ... num)
* @retval <0 on failure
@@ -393,10 +276,11 @@ odp_schedule_group_t odp_queue_sched_group(odp_queue_t queue);
*
* @param queue Queue handle
*
- * @return Number of ordered locks associated with this ordered queue
- * @retval <0 Specified queue is not ordered
+ * @return Number of ordered locks associated with this ordered queue
+ * @retval 0 Specified queue is not ordered or no ordered lock associated
+ * with the ordered queue.
*/
-int odp_queue_lock_count(odp_queue_t queue);
+uint32_t odp_queue_lock_count(odp_queue_t queue);
/**
* Get printable value for an odp_queue_t
@@ -423,15 +307,6 @@ uint64_t odp_queue_to_u64(odp_queue_t hdl);
void odp_queue_param_init(odp_queue_param_t *param);
/**
- * Queue information
- * Retrieve information about a queue with odp_queue_info()
- */
-typedef struct odp_queue_info_t {
- const char *name; /**< queue name */
- odp_queue_param_t param; /**< queue parameters */
-} odp_queue_info_t;
-
-/**
* Retrieve information about a queue
*
* Invalid queue handles or handles to free/destroyed queues leads to
@@ -446,6 +321,24 @@ typedef struct odp_queue_info_t {
int odp_queue_info(odp_queue_t queue, odp_queue_info_t *info);
/**
+ * Print queue info
+ *
+ * Print implementation defined information about the specified queue to the ODP
+ * log. The information is intended to be used for debugging.
+ *
+ * @param queue Queue handle
+ */
+void odp_queue_print(odp_queue_t queue);
+
+/**
+ * Print debug info about all queues
+ *
+ * Print implementation defined information about all created queues to the ODP
+ * log. The information is intended to be used for debugging.
+ */
+void odp_queue_print_all(void);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/queue_types.h b/include/odp/api/spec/queue_types.h
new file mode 100644
index 000000000..9edf7271d
--- /dev/null
+++ b/include/odp/api/spec/queue_types.h
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP queue types
+ */
+
+#ifndef ODP_API_SPEC_QUEUE_TYPES_H_
+#define ODP_API_SPEC_QUEUE_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/schedule_types.h>
+
+/** @defgroup odp_queue ODP QUEUE
+ * @{
+ */
+
+/**
+ * @typedef odp_queue_t
+ * ODP queue
+ */
+
+/**
+ * @def ODP_QUEUE_INVALID
+ * Invalid queue
+ */
+
+/**
+ * @def ODP_QUEUE_NAME_LEN
+ * Maximum queue name length in chars including null char
+ */
+
+/**
+ * Queue type
+ */
+typedef enum odp_queue_type_t {
+ /** Plain queue
+ *
+ * Plain queues offer simple FIFO storage of events. Application may
+ * dequeue directly from these queues. */
+ ODP_QUEUE_TYPE_PLAIN = 0,
+
+ /** Scheduled queue
+ *
+ * Scheduled queues are connected to the scheduler. Application must
+ * not dequeue events directly from these queues but use the scheduler
+ * instead. */
+ ODP_QUEUE_TYPE_SCHED
+} odp_queue_type_t;
+
+/**
+ * Queue operation mode
+ */
+typedef enum odp_queue_op_mode_t {
+ /** Multithread safe operation
+ *
+ * Queue operation (enqueue or dequeue) is multithread safe. Any
+ * number of application threads may perform the operation
+ * concurrently. */
+ ODP_QUEUE_OP_MT = 0,
+
+ /** Not multithread safe operation
+ *
+ * Queue operation (enqueue or dequeue) may not be multithread safe.
+ * Application ensures synchronization between threads so that
+ * simultaneously only single thread attempts the operation on
+ * the same queue. */
+ ODP_QUEUE_OP_MT_UNSAFE,
+
+ /** Disabled
+ *
+ * Direct enqueue or dequeue operation from application is disabled.
+ * An attempt to enqueue/dequeue directly will result undefined
+ * behaviour. Various ODP functions (e.g. packet input, timer,
+ * crypto, scheduler, etc) are able to perform enqueue or
+ * dequeue operations normally on the queue.
+ * */
+ ODP_QUEUE_OP_DISABLED
+
+} odp_queue_op_mode_t;
+
+/**
+ * Non-blocking level
+ *
+ * A non-blocking level defines implementation guarantees for application
+ * progress when multiple threads operate on the same resource (e.g. a queue)
+ * simultaneously. The first level (ODP_BLOCKING) does not have any block
+ * freedom guarantees, but a suspending thread may block the other threads for
+ * the entire time it remains suspended (infinitely if crashed).
+ * On the contrary, actual non-blocking levels provide guarantees of progress:
+ *
+ * ODP_NONBLOCKING_LF: A non-blocking and lock-free implementation guarantees
+ * that at least one of the threads successfully completes
+ * its operations, regardless of what other threads do.
+ * Application progress is guaranteed, but individual
+ * threads may starve while trying to execute their
+ * operations on the shared resource.
+ *
+ * ODP_NONBLOCKING_WF: A non-blocking and wait-free implementation guarantees
+ * application progress with starvation freedom. All
+ * threads are guaranteed to complete their operations in
+ * a bounded number of steps, regardless of what other
+ * threads do.
+ *
+ * Non-blocking levels are listed from the weakest to the strongest guarantee of
+ * block freedom. Performance of a non-blocking implementation may be lower than
+ * the blocking one. Non-blocking guarantees are important e.g. for real-time
+ * applications when real-time and non real-time threads share a resource.
+ */
+typedef enum odp_nonblocking_t {
+ /** Blocking implementation. A suspeding thread may block all other
+ * threads, i.e. no block freedom guarantees. This is the lowest level.
+ */
+ ODP_BLOCKING = 0,
+
+ /** Non-blocking and lock-free implementation. Other threads can make
+ * progress while a thread is suspended. Starvation freedom is not
+ * guaranteed.
+ */
+ ODP_NONBLOCKING_LF,
+
+ /** Non-blocking and wait-free implementation. Other threads can make
+ * progress while a thread is suspended. Starvation freedom is
+ * guaranteed.
+ */
+ ODP_NONBLOCKING_WF
+
+} odp_nonblocking_t;
+
+/**
+ * Original event order maintenance options
+ *
+ * Options to keep or ignore the original event order of a source queue. This
+ * option is relevant for (plain or parallel scheduled) queues that are
+ * destinations for events enqueued while holding an ordered queue
+ * synchronization context. By default, an ordered context maintains original
+ * event order regardless of the destination queue type. Event re-ordering may
+ * cause extra synchronization effort for implementation and a long delay before
+ * application can receive a re-ordered event from the destination queue. This
+ * is wasteful and in some cases the extra delay is not acceptable for those
+ * destination queues that do not need to maintain the original event order.
+ * Application can use ODP_QUEUE_ORDER_IGNORE option to prevent implementation
+ * from performing unnecessary event re-ordering and negative side-effects of
+ * that.
+ */
+typedef enum odp_queue_order_t {
+ /** Keep original event order. Events enqueued into this queue while
+ * holding an ordered queue synchronization context maintain the
+ * original event order of the source queue.
+ */
+ ODP_QUEUE_ORDER_KEEP = 0,
+
+ /** Ignore original event order. Events enqueued into this queue do not
+ * need to maintain the original event order of the source queue.
+ * Implementation must avoid significant event re-ordering delays.
+ */
+ ODP_QUEUE_ORDER_IGNORE
+
+} odp_queue_order_t;
+
+/**
+ * Queue capabilities
+ */
+typedef struct odp_queue_capability_t {
+ /** Maximum number of event queues of any type (default size). Use
+ * this in addition to queue type specific 'max_num', if both queue
+ * types are used simultaneously. */
+ uint32_t max_queues;
+
+ /** Plain queue capabilities */
+ struct {
+ /** Maximum number of plain (ODP_BLOCKING) queues of the
+ * default size. */
+ uint32_t max_num;
+
+ /** Maximum number of events a plain (ODP_BLOCKING) queue can
+ * store simultaneously. The value of zero means that plain
+ * queues do not have a size limit, but a single queue can
+ * store all available events. */
+ uint32_t max_size;
+
+ /** Lock-free (ODP_NONBLOCKING_LF) implementation capabilities.
+ * The specification is the same as for the blocking
+ * implementation. */
+ struct {
+ /** Maximum number of queues. Lock-free queues are not
+ * supported when zero. */
+ uint32_t max_num;
+
+ /** Maximum queue size. The value of zero means that
+ * there is no size limit. */
+ uint32_t max_size;
+
+ } lockfree;
+
+ /** Wait-free (ODP_NONBLOCKING_WF) implementation capabilities.
+ * The specification is the same as for the blocking
+ * implementation. */
+ struct {
+ /** Maximum number of queues. Wait-free queues are not
+ * supported when zero. */
+ uint32_t max_num;
+
+ /** Maximum queue size. The value of zero means that
+ * there is no size limit. */
+ uint32_t max_size;
+
+ } waitfree;
+
+ } plain;
+
+} odp_queue_capability_t;
+
+/**
+ * ODP Queue parameters
+ */
+typedef struct odp_queue_param_t {
+ /** Queue type
+ *
+ * Valid values for other parameters in this structure depend on
+ * the queue type. The default value is ODP_QUEUE_TYPE_PLAIN. */
+ odp_queue_type_t type;
+
+ /** Enqueue mode
+ *
+ * Default value for both queue types is ODP_QUEUE_OP_MT. Application
+ * may enable performance optimizations by defining MT_UNSAFE or
+ * DISABLED modes when applicable. */
+ odp_queue_op_mode_t enq_mode;
+
+ /** Dequeue mode
+ *
+ * For PLAIN queues, the default value is ODP_QUEUE_OP_MT. Application
+ * may enable performance optimizations by defining MT_UNSAFE or
+ * DISABLED modes when applicable. However, when a plain queue is input
+ * to the implementation (e.g. a queue for packet output), the
+ * parameter is ignored in queue creation and the value is
+ * ODP_QUEUE_OP_DISABLED.
+ *
+ * For SCHED queues, the parameter is ignored in queue creation and
+ * the value is ODP_QUEUE_OP_DISABLED. */
+ odp_queue_op_mode_t deq_mode;
+
+ /** Scheduler parameters
+ *
+ * These parameters are considered only when queue type is
+ * ODP_QUEUE_TYPE_SCHED. */
+ odp_schedule_param_t sched;
+
+ /** Original event order maintenance
+ *
+ * Keep or ignore the original event order of a source queue.
+ * The default value is ODP_QUEUE_ORDER_KEEP. */
+ odp_queue_order_t order;
+
+ /** Non-blocking level
+ *
+ * Queue implementation must guarantee at least this level of block
+ * freedom for queue enqueue and dequeue/schedule operations.
+ * The default value is ODP_BLOCKING. */
+ odp_nonblocking_t nonblocking;
+
+ /** Queue context pointer
+ *
+ * User defined context pointer associated with the queue. The same
+ * pointer can be accessed with odp_queue_context() and
+ * odp_queue_context_set() calls. The implementation may read the
+ * pointer for prefetching the context data. Default value of the
+ * pointer is NULL. */
+ void *context;
+
+ /** Queue context data length
+ *
+ * User defined context data length in bytes for prefetching.
+ * The implementation may use this value as a hint for the number of
+ * context data bytes to prefetch. Default value is zero (no hint). */
+ uint32_t context_len;
+
+ /** Queue size
+ *
+ * The queue must be able to store at minimum this many events
+ * simultaneously. The value must not exceed 'max_size' queue
+ * capability. The value of zero means implementation specific
+ * default size. The default value is 0. */
+ uint32_t size;
+
+} odp_queue_param_t;
+
+/**
+ * Queue information
+ * Retrieve information about a queue with odp_queue_info()
+ */
+typedef struct odp_queue_info_t {
+ const char *name; /**< queue name */
+ odp_queue_param_t param; /**< queue parameters */
+} odp_queue_info_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/random.h b/include/odp/api/spec/random.h
index 4765475c2..10f1026b3 100644
--- a/include/odp/api/spec/random.h
+++ b/include/odp/api/spec/random.h
@@ -1,49 +1,28 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP random number API
*/
-#ifndef ODP_API_RANDOM_H_
-#define ODP_API_RANDOM_H_
+#ifndef ODP_API_SPEC_RANDOM_H_
+#define ODP_API_SPEC_RANDOM_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_random ODP RANDOM
- * @{
- */
+#include <odp/api/random_types.h>
+#include <odp/api/std_types.h>
-/**
- * Random kind selector
- *
- * The kind of random denotes the statistical quality of the random data
- * returned. Basic random simply appears uniformly distributed, Cryptographic
- * random is statistically random and suitable for use by cryptographic
- * functions. True random is generated from a hardware entropy source rather
- * than an algorithm and is thus completely unpredictable. These form a
- * hierarchy where higher quality data is presumably more costly to generate
- * than lower quality data.
+/** @addtogroup odp_random
+ * Random number generation.
+ * @{
*/
-typedef enum {
- /** Basic random, presumably pseudo-random generated by SW. This
- * is the lowest kind of random */
- ODP_RANDOM_BASIC,
- /** Cryptographic quality random */
- ODP_RANDOM_CRYPTO,
- /** True random, generated from a HW entropy source. This is the
- * highest kind of random */
- ODP_RANDOM_TRUE,
-} odp_random_kind_t;
/**
* Query random max kind
@@ -72,7 +51,7 @@ odp_random_kind_t odp_random_max_kind(void);
* is expected to fail if the implementation is unable to
* provide the requested type.
*
- * @return Number of bytes written
+ * @return Number of bytes written (0...len).
* @retval <0 on failure
*/
int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind);
@@ -83,7 +62,7 @@ int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind);
* For testing purposes it is often useful to generate "random" sequences that
* are repeatable. This is accomplished by supplying a seed value that is used
* for pseudo-random data generation. The caller-provided seed value is
- * updated for each call to continue the sequence. Restarting a series of
+ * updated for each call to continue the sequence. Restarting the same series of
* calls with the same initial seed value will generate the same sequence of
* random test data.
*
@@ -96,7 +75,7 @@ int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind);
* variable. Results are undefined if multiple threads
* call this routine with the same seed variable.
*
- * @return Number of bytes written
+ * @return Number of bytes written (always len)
* @retval <0 on failure
*/
int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed);
diff --git a/include/odp/api/spec/random_types.h b/include/odp/api/spec/random_types.h
new file mode 100644
index 000000000..5d5ee9450
--- /dev/null
+++ b/include/odp/api/spec/random_types.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP random number API
+ */
+
+#ifndef ODP_API_SPEC_RANDOM_TYPES_H_
+#define ODP_API_SPEC_RANDOM_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_random ODP RANDOM
+ * @{
+ */
+
+/**
+ * Random kind selector
+ *
+ * The kind of random denotes the statistical quality of the random data
+ * returned. Basic random simply appears uniformly distributed, Cryptographic
+ * random is statistically random and suitable for use by cryptographic
+ * functions. True random is generated from a hardware entropy source rather
+ * than an algorithm and is thus completely unpredictable. These form a
+ * hierarchy where higher quality data is presumably more costly to generate
+ * than lower quality data.
+ */
+typedef enum {
+ /** Basic random, presumably pseudo-random generated by SW. This
+ * is the lowest kind of random */
+ ODP_RANDOM_BASIC,
+ /** Cryptographic quality random */
+ ODP_RANDOM_CRYPTO,
+ /** True random, generated from a HW entropy source. This is the
+ * highest kind of random */
+ ODP_RANDOM_TRUE,
+} odp_random_kind_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/reassembly.h b/include/odp/api/spec/reassembly.h
new file mode 100644
index 000000000..6f1b22390
--- /dev/null
+++ b/include/odp/api/spec/reassembly.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP REASSEMBLY API
+ */
+
+#ifndef ODP_API_SPEC_REASSEMBLY_H_
+#define ODP_API_SPEC_REASSEMBLY_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_reassembly ODP REASSEMBLY
+ * Reassembly
+ * @{
+ */
+
+/**
+ * Reassembly capabilities
+ *
+ */
+typedef struct odp_reass_capability_t {
+ /** Reassembly offload for both IPv4 and IPv6 packets. This capability
+ * does not allow enabling reassembly for only IPv4 or only IPv6.
+ */
+ odp_bool_t ip;
+
+ /** Reassembly offload for IPv4 packets */
+ odp_bool_t ipv4;
+
+ /** Reassembly offload for IPv6 packets */
+ odp_bool_t ipv6;
+
+ /** Maximum time in ns that a fragment can wait in the reassembly
+ * offload for the arrival of further fragments.
+ */
+ uint64_t max_wait_time;
+
+ /** Maximum number of fragments that can be reassembled */
+ uint16_t max_num_frags;
+
+} odp_reass_capability_t;
+
+/**
+ * Fragment reassembly configuration
+ *
+ * Configure inline fragment reassembly offload support. Fragment
+ * reassembly offload can be enabled in IPSEC and PKTIN operations.
+ *
+ * When the offload is enabled, fragments will be delayed for a specified time
+ * period to allow reassembly.
+ *
+ * Reassembly result will be delivered to the application through an ODP packet
+ * with reassembly metadata. odp_packet_reass_status() can be used to query if
+ * reassembly has been attempted and if reassembly was successfully completed.
+ *
+ * In case of successful reassembly, the reassembled packet is delivered
+ * to the receiver as a regular ODP packet as if the reassembled packet
+ * was received as such. When reassembly is enabled in pktio, it will be
+ * attempted before other offloads such as packet parsing, inline IPsec and
+ * classification.
+ *
+ * In case of failed reassembly, the result is delivered to the application
+ * as a special packet that does not contain valid packet data. Such a
+ * packet can be used to get information of the incomplete reassembly
+ * so that the application can try to retry or continue the reassembly.
+ * See odp_packet_reass_partial_state().
+ *
+ * Reassembly may not complete if not all fragments were received in time but
+ * also for packet parsing difficulty, fragment overlap, resource shortage or
+ * other reasons. In such cases, application may receive packets with reassembly
+ * status as either ``ODP_PACKET_REASS_NONE`` or ``ODP_PACKET_REASS_INCOMPLETE``.
+ *
+ * This structure is used only for configuration, not for capability
+ * query even though it is indirectly contained in odp_pktio_capability_t.
+ * The content of odp_pktio_capability_t.config.reassembly written by
+ * odp_pktio_capability() is undefined. Reassembly capabilities of a pktio
+ * can be checked through odp_pktio_capability_t.reassembly.
+ *
+ * @see odp_packet_reass_status(), odp_packet_reass_partial_state()
+ */
+typedef struct odp_reass_config_t {
+ /** Attempt inline reassembly of IPv4 packets. Disabled by default.
+ * This may be set if the relevant odp_reass_capability_t::ipv4
+ * capability is present or if odp_reass_capability_t::ip capability
+ * is present and en_ipv6 is also set.
+ */
+ odp_bool_t en_ipv4;
+
+ /** Attempt inline reassembly of IPv6 packets. Disabled by default.
+ * This may be set if the relevant odp_reass_capability_t::ipv6
+ * capability is present or if odp_reass_capability_t::ip capability
+ * is present and en_ipv4 is also set.
+ */
+ odp_bool_t en_ipv6;
+
+ /** Maximum time in ns that a fragment may wait in the reassembly
+ * offload for the arrival of further fragments. The value may
+ * not exceed the max_wait_time capability. Zero value means
+ * implementation defined maximum wait time.
+ *
+ * Default value is 0.
+ */
+ uint64_t max_wait_time;
+
+ /** Maximum number of fragments that can be reassembled
+ *
+ * Minimum allowed value is 2. Default value is 2.
+ */
+ uint16_t max_num_frags;
+} odp_reass_config_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/rwlock.h b/include/odp/api/spec/rwlock.h
index ff8a3f278..de749add8 100644
--- a/include/odp/api/spec/rwlock.h
+++ b/include/odp/api/spec/rwlock.h
@@ -1,11 +1,9 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
-#ifndef ODP_API_RWLOCK_H_
-#define ODP_API_RWLOCK_H_
+#ifndef ODP_API_SPEC_RWLOCK_H_
+#define ODP_API_SPEC_RWLOCK_H_
#include <odp/visibility_begin.h>
/**
@@ -20,6 +18,8 @@ extern "C" {
/**
* @defgroup odp_locks ODP LOCKS
+ * Various types of locks for thread synchronization.
+ *
* @details
* <b> Reader / writer lock (odp_rwlock_t) </b>
*
@@ -36,7 +36,6 @@ extern "C" {
* ODP reader/writer lock
*/
-
/**
* Initialize a reader/writer lock.
*
diff --git a/include/odp/api/spec/rwlock_recursive.h b/include/odp/api/spec/rwlock_recursive.h
index 1c19c7217..6b4afcbbe 100644
--- a/include/odp/api/spec/rwlock_recursive.h
+++ b/include/odp/api/spec/rwlock_recursive.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP recursive read/write lock
*/
-#ifndef ODP_API_RWLOCK_RECURSIVE_H_
-#define ODP_API_RWLOCK_RECURSIVE_H_
+#ifndef ODP_API_SPEC_RWLOCK_RECURSIVE_H_
+#define ODP_API_SPEC_RWLOCK_RECURSIVE_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/schedule.h b/include/odp/api/spec/schedule.h
index 8244746d7..7226c198b 100644
--- a/include/odp/api/spec/schedule.h
+++ b/include/odp/api/spec/schedule.h
@@ -1,18 +1,15 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP schedule
*/
-#ifndef ODP_API_SCHEDULE_H_
-#define ODP_API_SCHEDULE_H_
+#ifndef ODP_API_SPEC_SCHEDULE_H_
+#define ODP_API_SPEC_SCHEDULE_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,52 +17,17 @@ extern "C" {
#endif
#include <odp/api/std_types.h>
-#include <odp/api/event.h>
-#include <odp/api/queue.h>
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
#include <odp/api/schedule_types.h>
#include <odp/api/thrmask.h>
-/** @defgroup odp_scheduler ODP SCHEDULER
- * Operations on the scheduler.
+/** @addtogroup odp_scheduler
+ * Event scheduler for work load balancing and prioritization.
* @{
*/
/**
- * @def ODP_SCHED_WAIT
- * Wait infinitely
- */
-
-/**
- * @def ODP_SCHED_NO_WAIT
- * Do not wait
- */
-
-/**
- * @def ODP_SCHED_GROUP_NAME_LEN
- * Maximum schedule group name length in chars including null char
- */
-
-/**
- * @def ODP_SCHED_GROUP_INVALID
- * Invalid scheduler group
- */
-
-/**
- * @def ODP_SCHED_GROUP_ALL
- * Predefined scheduler group of all threads
- */
-
-/**
- * @def ODP_SCHED_GROUP_WORKER
- * Predefined scheduler group of all worker threads
- */
-
-/**
- * @def ODP_SCHED_GROUP_CONTROL
- * Predefined scheduler group of all control threads
- */
-
-/**
* Schedule wait time
*
* Converts nanoseconds to wait values for other schedule functions.
@@ -77,20 +39,34 @@ extern "C" {
uint64_t odp_schedule_wait_time(uint64_t ns);
/**
- * Schedule
+ * Schedule an event
*
- * Schedules all queues created with ODP_QUEUE_TYPE_SCHED type. Returns
- * next highest priority event which is available for the calling thread.
- * Outputs the source queue of the event. If there's no event available, waits
+ * Run event scheduler to find the next highest priority event which is
+ * available for the calling thread. Only queues that have been created with
+ * ODP_QUEUE_TYPE_SCHED type are connected to the scheduler. Optionally,
+ * outputs the source queue of the event. If there's no event available, waits
* for an event according to the wait parameter setting. Returns
* ODP_EVENT_INVALID if reaches end of the wait period.
*
* When returns an event, the thread holds the queue synchronization context
- * (atomic or ordered) until the next odp_schedule() or odp_schedule_multi()
- * call. The next call implicitly releases the current context and potentially
- * returns with a new context. User can allow early context release (e.g., see
- * odp_schedule_release_atomic() and odp_schedule_release_ordered()) for
- * performance optimization.
+ * (atomic or ordered) until the next schedule call (e.g. odp_schedule() or
+ * odp_schedule_multi()). The next call implicitly releases the current context
+ * and potentially returns with a new context. User can allow early context
+ * release (e.g., see odp_schedule_release_atomic() and
+ * odp_schedule_release_ordered()) for performance optimization.
+ *
+ * When successful, this function acts as an acquire memory barrier between
+ * the sender and the receiver (the calling thread) of the event. The receiver
+ * sees correctly the memory stores done by the sender before it enqueued
+ * the event.
+ *
+ * When the event was scheduled from an atomic queue, this function acts as
+ * an acquire memory barrier between the previous holder of the same atomic
+ * synchronization context and the calling thread. When the context is released,
+ * a release memory barrier is performed towards the next holder of the context.
+ * This ensures that memory stores done when holding an atomic context are
+ * correctly visible to other threads that will subsequently hold the same
+ * atomic context.
*
* @param from Output parameter for the source queue (where the event was
* dequeued from). Ignored if NULL.
@@ -133,15 +109,58 @@ int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[],
int num);
/**
+ * Schedule, wait for events
+ *
+ * Like odp_schedule_multi(), but waits infinitely for events.
+ *
+ * @param[out] from Output parameter for the source queue (where the event
+ * was dequeued from). Ignored if NULL.
+ * @param[out] events Event array for output
+ * @param num Maximum number of events to output
+ *
+ * @return Number of events outputted (1 ... num)
+ */
+int odp_schedule_multi_wait(odp_queue_t *from, odp_event_t events[], int num);
+
+/**
+ * Schedule, do not wait for events
+ *
+ * Like odp_schedule_multi(), but does not wait for events.
+ *
+ * @param[out] from Output parameter for the source queue (where the event
+ * was dequeued from). Ignored if NULL.
+ * @param[out] events Event array for output
+ * @param num Maximum number of events to output
+ *
+ * @return Number of events outputted (0 ... num)
+ */
+int odp_schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[],
+ int num);
+
+/**
* Pause scheduling
*
- * Pause global scheduling for this thread. After this call, all schedule calls
- * will return only locally pre-scheduled events (if any). User can exit the
- * schedule loop only after the schedule function indicates that there's no more
- * (pre-scheduled) events.
+ * Pause global scheduling for this thread. After this call, only
+ * ODP_SCHED_NO_WAIT schedule calls are allowed and these calls will return only
+ * locally pre-scheduled events (if any). User can exit the schedule loop only
+ * after the schedule function indicates that there's no more (pre-scheduled)
+ * events.
+ *
+ * Example call pattern:
+ *
+ * while (!stop) {
+ * odp_event_t ev = odp_schedule(NULL, ODP_SCHED_WAIT);
+ * // Process event
+ * }
+ *
+ * odp_schedule_pause();
*
- * Must be used with odp_schedule() and odp_schedule_multi() before exiting (or
- * stalling) the schedule loop.
+ * while (1) {
+ * odp_event_t ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ * if (ev == ODP_EVENT_INVALID)
+ * break;
+ * odp_event_free(ev);
+ * }
*/
void odp_schedule_pause(void);
@@ -201,22 +220,113 @@ void odp_schedule_release_ordered(void);
void odp_schedule_prefetch(int num);
/**
+ * Maximum scheduling priority level
+ *
+ * This is the maximum value that can be set to 'prio' field in
+ * odp_schedule_param_t (e.g. odp_queue_create()). Queues with a higher
+ * priority value are served with higher priority than queues with a lower
+ * priority value.
+ *
+ * @return Maximum scheduling priority level
+ */
+int odp_schedule_max_prio(void);
+
+/**
+ * Minimum scheduling priority level
+ *
+ * This is the minimum value that can be set to 'prio' field in
+ * odp_schedule_param_t (e.g. odp_queue_create()). Queues with a higher
+ * priority value are served with higher priority than queues with a lower
+ * priority value.
+ *
+ * @return Minimum scheduling priority level
+ */
+int odp_schedule_min_prio(void);
+
+/**
+ * Default scheduling priority level
+ *
+ * This is the default value of 'prio' field in odp_schedule_param_t
+ * (e.g. odp_queue_param_init()). The default value should be suitable for
+ * an application that uses single priority level for all its queues (uses
+ * scheduler only for load balancing and synchronization). Typically,
+ * the default value is between minimum and maximum values, but with a few
+ * priority levels it may be close or equal to those.
+ *
+ * @return Default scheduling priority level
+ */
+int odp_schedule_default_prio(void);
+
+/**
* Number of scheduling priorities
*
+ * The number of priority levels support by the scheduler. It equals to
+ * odp_schedule_max_prio() - odp_schedule_min_prio() + 1.
+ *
* @return Number of scheduling priorities
*/
int odp_schedule_num_prio(void);
/**
+ * Initialize schedule configuration options
+ *
+ * Initialize an odp_schedule_config_t to its default values.
+ *
+ * @param[out] config Pointer to schedule configuration structure
+ */
+void odp_schedule_config_init(odp_schedule_config_t *config);
+
+/**
+ * Global schedule configuration
+ *
+ * Initialize and configure scheduler with global configuration options
+ * to schedule events across different scheduled queues.
+ * This function must be called only once and before scheduler is used
+ * (any other scheduler function is called except odp_schedule_capability() and
+ * odp_schedule_config_init()) or any queues are created (by application itself
+ * or by other ODP modules).
+ * An application can pass NULL value to use default configuration. It will
+ * have the same result as filling the structure with
+ * odp_schedule_config_init() and then passing it to odp_schedule_config().
+ *
+ * The initialization sequeunce should be,
+ * odp_schedule_capability()
+ * odp_schedule_config_init()
+ * odp_schedule_config()
+ * odp_schedule()
+ *
+ * @param config Pointer to scheduler configuration structure or NULL for the
+ * default configuration
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ *
+ * @see odp_schedule_capability(), odp_schedule_config_init()
+ */
+int odp_schedule_config(const odp_schedule_config_t *config);
+
+/**
+ * Query scheduler capabilities
+ *
+ * Outputs schedule capabilities on success.
+ *
+ * @param[out] capa Pointer to capability structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_schedule_capability(odp_schedule_capability_t *capa);
+
+/**
* Schedule group create
*
* Creates a schedule group with the thread mask. Only threads in the
* mask will receive events from a queue that belongs to the schedule group.
* Thread masks of various schedule groups may overlap. There are predefined
- * groups such as ODP_SCHED_GROUP_ALL and ODP_SCHED_GROUP_WORKER, which are
- * always present and automatically updated. The use of group name is optional.
- * Unique names are not required. However, odp_schedule_group_lookup() returns
- * only a single matching group.
+ * groups, such as ODP_SCHED_GROUP_ALL and ODP_SCHED_GROUP_WORKER, which are
+ * present by default and are automatically updated. The use of group name is
+ * optional. Unique names are not required. However, odp_schedule_group_lookup()
+ * returns only a single matching group.
*
* @param name Name of the schedule group or NULL. Maximum string length is
* ODP_SCHED_GROUP_NAME_LEN.
@@ -300,14 +410,6 @@ int odp_schedule_group_thrmask(odp_schedule_group_t group,
odp_thrmask_t *thrmask);
/**
- * Schedule group information
- */
-typedef struct odp_schedule_group_info_t {
- const char *name; /**< Schedule group name */
- odp_thrmask_t thrmask; /**< Thread mask of the schedule group */
-} odp_schedule_group_info_t;
-
-/**
* Retrieve information about a schedule group
*
* Fills in schedule group information structure with current values.
@@ -347,12 +449,15 @@ int odp_schedule_group_info(odp_schedule_group_t group,
* be protected by its own ordered lock. This promotes maximum parallelism by
* allowing order to maintained on a more granular basis. If an ordered lock
* is used multiple times in the same ordered context results are undefined.
+ * Only one ordered lock can be active in an ordered context at any given time.
+ * Results are undefined when multiple ordered locks are acquired in nested
+ * fashion within the same ordered context.
*
* @param lock_index Index of the ordered lock in the current context to be
* acquired. Must be in the range 0..odp_queue_lock_count()
* - 1
*/
-void odp_schedule_order_lock(unsigned lock_index);
+void odp_schedule_order_lock(uint32_t lock_index);
/**
* Release ordered context lock
@@ -365,7 +470,80 @@ void odp_schedule_order_lock(unsigned lock_index);
* hold this lock. Must be in the range
* 0..odp_queue_lock_count() - 1
*/
-void odp_schedule_order_unlock(unsigned lock_index);
+void odp_schedule_order_unlock(uint32_t lock_index);
+
+/**
+ * Release existing ordered context lock and acquire a new lock
+ *
+ * This call is valid only when holding an ordered synchronization context.
+ * Release a previously locked ordered context lock and acquire a new ordered
+ * context lock. The operation is equivalent to application calling first
+ * odp_schedule_order_unlock(unlock_index) and then
+ * odp_schedule_order_lock(lock_index). The same constraints apply with this
+ * call as with those two.
+ *
+ * @param unlock_index Index of the acquired ordered lock in the current
+ * context to be released.
+ * @param lock_index Index of the ordered lock in the current context to be
+ * acquired. Must be in the range
+ * 0...odp_queue_lock_count() - 1.
+ *
+ * @see odp_schedule_order_lock(), odp_schedule_order_unlock()
+ *
+ */
+void odp_schedule_order_unlock_lock(uint32_t unlock_index, uint32_t lock_index);
+
+/** Asynchronous ordered context lock
+ * Request an ordered context lock to be acquired. Starts an ordered context
+ * lock acquire operation, but does not wait until the lock has been acquired.
+ * Application can use this call to potentially interleave some processing
+ * within waiting for this lock. Each start lock call must be paired with a wait
+ * call that blocks until the lock has been acquired. Locks cannot be acquired
+ * in nested fashion i.e each start call must follow a paring wait and unlock
+ * calls, before using another lock.
+ * The same constraints apply as with odp_schedule_order_lock()
+ *
+ * @param lock_index Index of the ordered lock in the current context to
+ * start acquire operation.
+ * Must be in the range 0..odp_queue_lock_count() - 1.
+ *
+ */
+void odp_schedule_order_lock_start(uint32_t lock_index);
+
+/** Asynchronous ordered context lock wait
+ * Wait for a previously started lock acquire operation to finish.
+ * Lock index must match with the previous start call. Ordered lock acquisition
+ * will be completed during this call.
+ *
+ * @param lock_index Index of the ordered lock in the current context to
+ * complete acquire operation.
+ * Must be in the range 0..odp_queue_lock_count() - 1.
+ */
+void odp_schedule_order_lock_wait(uint32_t lock_index);
+
+/**
+ * Wait until the currently held scheduling context is the first in order
+ *
+ * Wait until there are no other scheduling contexts that precede the
+ * scheduling context of the calling thread in the source queue order.
+ * The context remains the first in order until the thread releases it.
+ *
+ * This function must not be called if the current thread is not holding
+ * an ordered scheduling context or if an ordered lock is being held.
+ *
+ * This functions does nothing if ordered wait is not supported.
+ *
+ * @see odp_schedule_capability()
+ */
+void odp_schedule_order_wait(void);
+
+/**
+ * Print debug info about scheduler
+ *
+ * Print implementation defined information about scheduler to the ODP log.
+ * The information is intended to be used for debugging.
+ */
+void odp_schedule_print(void);
/**
* @}
diff --git a/include/odp/api/spec/schedule_types.h b/include/odp/api/spec/schedule_types.h
index 8a4e42c64..30cb939dc 100644
--- a/include/odp/api/spec/schedule_types.h
+++ b/include/odp/api/spec/schedule_types.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
/**
@@ -10,43 +9,34 @@
* ODP schedule types
*/
-#ifndef ODP_API_SCHEDULE_TYPES_H_
-#define ODP_API_SCHEDULE_TYPES_H_
+#ifndef ODP_API_SPEC_SCHEDULE_TYPES_H_
+#define ODP_API_SPEC_SCHEDULE_TYPES_H_
#include <odp/visibility_begin.h>
+#include <odp/api/std_types.h>
+#include <odp/api/thrmask.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-/** @addtogroup odp_scheduler
+/** @defgroup odp_scheduler ODP SCHEDULER
* @{
*/
/**
- * @typedef odp_schedule_prio_t
- * Scheduler priority level
- */
-
-/**
- * @def ODP_SCHED_PRIO_HIGHEST
- * Highest scheduling priority
- */
-
-/**
- * @def ODP_SCHED_PRIO_NORMAL
- * Normal scheduling priority
+ * @def ODP_SCHED_WAIT
+ * Wait infinitely
*/
/**
- * @def ODP_SCHED_PRIO_LOWEST
- * Lowest scheduling priority
+ * @def ODP_SCHED_NO_WAIT
+ * Do not wait
*/
/**
- * @def ODP_SCHED_PRIO_DEFAULT
- * Default scheduling priority. User does not care about the selected priority
- * level - throughput, load balancing and synchronization features are more
- * important than priority scheduling.
+ * @def ODP_SCHED_GROUP_NAME_LEN
+ * Maximum schedule group name length in chars including null char
*/
/**
@@ -78,7 +68,13 @@ extern "C" {
* The atomic queue synchronization context is dedicated to the thread until it
* requests another event from the scheduler, which implicitly releases the
* context. User may allow the scheduler to release the context earlier than
- * that by calling odp_schedule_release_atomic().
+ * that by calling odp_schedule_release_atomic(). However, this call is just
+ * a hint to the implementation and the context may be held until the next
+ * schedule call.
+ *
+ * When scheduler is enabled as flow-aware, the event flow id value affects
+ * scheduling of the event and synchronization is maintained per flow within
+ * each queue.
*/
/**
@@ -90,21 +86,53 @@ extern "C" {
* enables the user to achieve high single flow throughput by avoiding
* SW synchronization for ordering between threads.
*
- * The source queue (dequeue) ordering is maintained when
- * events are enqueued to their destination queue(s) within the same ordered
- * queue synchronization context. A thread holds the context until it
- * requests another event from the scheduler, which implicitly releases the
- * context. User may allow the scheduler to release the context earlier than
- * that by calling odp_schedule_release_ordered().
+ * When odp_schedule() returns an event, the calling thread is associated
+ * with an ordered scheduling synchronization context. The contexts arising
+ * from the same ordered queue have the same mutual ordering as the
+ * corresponding events had in the queue.
+ *
+ * When odp_schedule_multi() returns more than one event from an ordered
+ * queue, the events returned were consecutive in the queue and the calling
+ * thread is associated with single ordered scheduling synchronization
+ * context that is ordered with respect to other contexts as if just the
+ * first event was returned.
+ *
+ * When threads holding ordered scheduling synchronization contexts, which
+ * arise from the same ordered queue, enqueue events to destination queues,
+ * the order of events in each destination queue will be as follows:
+ *
+ * - Events enqueued by one thread have the order in which the enqueue
+ * calls were made.
*
- * Events from the same (source) queue appear in their original order
- * when dequeued from a destination queue. The destination queue can have any
- * queue type and synchronization method. Event ordering is based on the
- * received event(s), but also other (newly allocated or stored) events are
- * ordered when enqueued within the same ordered context. Events not enqueued
- * (e.g. freed or stored) within the context are considered missing from
- * reordering and are skipped at this time (but can be ordered again within
- * another context).
+ * - Two events enqueued by different threads have the same mutual order
+ * as the scheduling synchronization contexts of the enqueuing threads.
+ *
+ * The ordering rules above apply to all events, not just those that were
+ * scheduled from the ordered queue. For instance, newly allocated events
+ * and previously stored events are ordered in the destination queue based
+ * on the scheduling synchronization context. The ordering rules apply
+ * regarless of the type (scheduled or plain) or schedule type (atomic,
+ * ordered, or parallel) of the destination queue. If the order type of
+ * the destination queue is ODP_QUEUE_ORDER_IGNORE, then the order between
+ * events enqueued by different threads is not guaranteed.
+ *
+ * An ordered scheduling synchronization context is implicitly released when
+ * the thread holding the context requests a new event from the scheduler.
+ * User may allow the scheduler to release the context earlier than that by
+ * calling odp_schedule_release_ordered(). However, this call is just a hint
+ * to the implementation and the context may be held until the next schedule
+ * call.
+ *
+ * Enqueue calls by different threads may return in a different order than
+ * the final order of the enqueued events in the destination queue.
+ *
+ * Unnecessary event re-ordering may be avoided for those destination queues
+ * that do not need to maintain the specified event order by setting 'order'
+ * queue parameter to ODP_QUEUE_ORDER_IGNORE.
+ *
+ * When scheduler is enabled as flow-aware, the event flow id value affects
+ * scheduling of the event and synchronization is maintained and order is
+ * defined per flow within each queue.
*/
/**
@@ -113,6 +141,11 @@ extern "C" {
*/
/**
+ * @def ODP_SCHED_GROUP_INVALID
+ * Invalid scheduler group
+ */
+
+/**
* @def ODP_SCHED_GROUP_ALL
* Group of all threads. All active worker and control threads belong to this
* group. The group is automatically updated when new threads enter or old
@@ -126,11 +159,25 @@ extern "C" {
* old threads exit ODP.
*/
+/**
+ * @def ODP_SCHED_GROUP_CONTROL
+ * Predefined scheduler group of all control threads
+ */
+
+/**
+ * Scheduling priority level
+ *
+ * Priority level is an integer value between odp_schedule_min_prio() and
+ * odp_schedule_max_prio(). Queues with a higher priority value are served with
+ * higher priority than queues with a lower priority value.
+ */
+typedef int odp_schedule_prio_t;
+
/** Scheduler parameters */
typedef struct odp_schedule_param_t {
/** Priority level
*
- * Default value is ODP_SCHED_PRIO_DEFAULT. */
+ * Default value is returned by odp_schedule_default_prio(). */
odp_schedule_prio_t prio;
/** Synchronization method
@@ -146,10 +193,129 @@ typedef struct odp_schedule_param_t {
/** Ordered lock count for this queue
*
* Default value is 0. */
- unsigned lock_count;
+ uint32_t lock_count;
} odp_schedule_param_t;
/**
+ * Scheduler capabilities
+ */
+typedef struct odp_schedule_capability_t {
+ /** Maximum number of ordered locks per queue */
+ uint32_t max_ordered_locks;
+
+ /** Maximum number of scheduling groups. The value includes the enabled
+ * predefined scheduling groups (ODP_SCHED_GROUP_ALL,
+ * ODP_SCHED_GROUP_WORKER, and ODP_SCHED_GROUP_CONTROL). By default, an
+ * application can create 'max_groups' - 3 groups. */
+ uint32_t max_groups;
+
+ /** Number of scheduling priorities */
+ uint32_t max_prios;
+
+ /** Maximum number of scheduled (ODP_BLOCKING) queues of the default
+ * size. */
+ uint32_t max_queues;
+
+ /** Maximum number of events a scheduled (ODP_BLOCKING) queue can store
+ * simultaneously. The value of zero means that scheduled queues do not
+ * have a size limit, but a single queue can store all available
+ * events. */
+ uint32_t max_queue_size;
+
+ /** Maximum flow ID per queue
+ *
+ * Valid flow ID range in flow aware mode of scheduling is from 0 to
+ * this maximum value. So, maximum number of flows per queue is this
+ * value plus one. A value of 0 indicates that flow aware mode is not
+ * supported. */
+ uint32_t max_flow_id;
+
+ /** Lock-free (ODP_NONBLOCKING_LF) queues support.
+ * The specification is the same as for the blocking implementation. */
+ odp_support_t lockfree_queues;
+
+ /** Wait-free (ODP_NONBLOCKING_WF) queues support.
+ * The specification is the same as for the blocking implementation. */
+ odp_support_t waitfree_queues;
+
+ /** Order wait support. If not supported, odp_schedule_order_wait()
+ * does nothing. */
+ odp_support_t order_wait;
+
+} odp_schedule_capability_t;
+
+/**
+ * Schedule configuration
+ */
+typedef struct odp_schedule_config_t {
+ /** Maximum number of scheduled queues to be supported.
+ *
+ * @see odp_schedule_capability_t
+ */
+ uint32_t num_queues;
+
+ /** Maximum number of events required to be stored simultaneously in
+ * scheduled queue. This number must not exceed 'max_queue_size'
+ * capability. A value of 0 configures default queue size supported by
+ * the implementation.
+ */
+ uint32_t queue_size;
+
+ /** Maximum flow ID per queue
+ *
+ * This value must not exceed 'max_flow_id' capability. Flow aware
+ * mode of scheduling is enabled when the value is greater than 0.
+ * The default value is 0.
+ *
+ * Application can assign events to specific flows by calling
+ * odp_event_flow_id_set() before enqueuing events into a scheduled
+ * queue. When in flow aware mode, the event flow id value affects
+ * scheduling of the event and synchronization is maintained per flow
+ * within each queue.
+ *
+ * Depending on the implementation, there may be much more flows
+ * supported than queues, as flows are lightweight entities.
+ *
+ * @see odp_schedule_capability_t, odp_event_flow_id()
+ */
+ uint32_t max_flow_id;
+
+ /** Enable/disable predefined scheduling groups */
+ struct {
+ /** ODP_SCHED_GROUP_ALL
+ *
+ * 0: Disable group
+ * 1: Enable group (default)
+ */
+ odp_bool_t all;
+
+ /** ODP_SCHED_GROUP_CONTROL
+ *
+ * 0: Disable group
+ * 1: Enable group (default)
+ */
+ odp_bool_t control;
+
+ /** ODP_SCHED_GROUP_WORKER
+ *
+ * 0: Disable group
+ * 1: Enable group (default)
+ */
+ odp_bool_t worker;
+
+ } sched_group;
+
+} odp_schedule_config_t;
+
+/**
+ * Schedule group information
+ */
+typedef struct odp_schedule_group_info_t {
+ const char *name; /**< Schedule group name */
+ odp_thrmask_t thrmask; /**< Thread mask of the schedule group */
+} odp_schedule_group_info_t;
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/shared_memory.h b/include/odp/api/spec/shared_memory.h
index 1a9c1299e..3845f6e4d 100644
--- a/include/odp/api/spec/shared_memory.h
+++ b/include/odp/api/spec/shared_memory.h
@@ -1,18 +1,16 @@
-/* Copyright (c) 2013-2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
*/
-
/**
* @file
*
* ODP shared memory
*/
-#ifndef ODP_API_SHARED_MEMORY_H_
-#define ODP_API_SHARED_MEMORY_H_
+#ifndef ODP_API_SPEC_SHARED_MEMORY_H_
+#define ODP_API_SPEC_SHARED_MEMORY_H_
#include <odp/visibility_begin.h>
#include <odp/api/init.h>
@@ -21,7 +19,7 @@ extern "C" {
#endif
/** @defgroup odp_shared_memory ODP SHARED MEMORY
- * Operations on shared memory.
+ * Shared memory blocks.
* @{
*/
@@ -36,20 +34,25 @@ extern "C" {
*/
/**
- * @def ODP_SHM_NULL
- * Synonym for buffer pool use
+ * @def ODP_SHM_NAME_LEN
+ * Maximum shared memory block name length in chars including null char
*/
/**
- * @def ODP_SHM_NAME_LEN
- * Maximum shared memory block name length in chars including null char
+ * @def ODP_SHM_IOVA_INVALID
+ * Invalid IOVA address
*/
-/*
- * Shared memory flags:
+/**
+ * @def ODP_SHM_PA_INVALID
+ * Invalid physical address
*/
-#define ODP_SHM_SW_ONLY 0x1 /**< Application SW only, no HW access */
-#define ODP_SHM_PROC 0x2 /**< Share with external processes */
+
+/**
+ * Share with external processes
+ */
+#define ODP_SHM_PROC 0x2
+
/**
* Single virtual address
*
@@ -58,6 +61,7 @@ extern "C" {
* of ODP thread type (e.g. pthread vs. process (or fork process time)).
*/
#define ODP_SHM_SINGLE_VA 0x4
+
/**
* Export memory
*
@@ -67,17 +71,88 @@ extern "C" {
#define ODP_SHM_EXPORT 0x08
/**
+ * Use huge pages
+ *
+ * When set, this flag guarantees that the memory reserved by odp_shm_reserve()
+ * is allocated from huge pages. The reserve call will return failure if enough
+ * huge page memory is not available.
+ */
+#define ODP_SHM_HP 0x10
+
+/**
+ * Share memory with HW accelerators
+ *
+ * When set, this flag guarantees that the reserved memory is accessible
+ * by both CPUs and HW accelerators of the device. This may require e.g. that
+ * the odp_shm_reserve() call configures the memory to be accessible through
+ * an Input-Output Memory Management Unit (IOMMU).
+ */
+#define ODP_SHM_HW_ACCESS 0x20
+
+/**
+ * Don't use huge pages
+ *
+ * When set, this flag guarantees that the memory reserved by odp_shm_reserve()
+ * is not allocated from huge pages. This flag must not be combined with
+ * ODP_SHM_HP.
+ */
+#define ODP_SHM_NO_HP 0x40
+
+/**
* Shared memory block info
*/
typedef struct odp_shm_info_t {
- const char *name; /**< Block name */
- void *addr; /**< Block address */
- uint64_t size; /**< Block size in bytes */
- uint64_t page_size; /**< Memory page size */
- uint32_t flags; /**< ODP_SHM_* flags */
+ /** Block name */
+ const char *name;
+
+ /** Block address */
+ void *addr;
+
+ /** Block size in bytes */
+ uint64_t size;
+
+ /** Memory page size */
+ uint64_t page_size;
+
+ /** ODP_SHM_* flags */
+ uint32_t flags;
+
+ /**
+ * Number of memory segments
+ *
+ * Number of segments in physical (or IOVA) address space that map memory to
+ * the SHM block. More information about each segment can be retrieved with
+ * odp_shm_segment_info(). Number of segments is always at least one, also when
+ * physical (or IOVA) segmentation information is not available. */
+ uint32_t num_seg;
+
} odp_shm_info_t;
/**
+ * SHM memory segment info
+ */
+typedef struct odp_shm_segment_info_t {
+ /** Segment start address (virtual) */
+ uintptr_t addr;
+
+ /** Segment start address in IO virtual address (IOVA) space
+ *
+ * Value is ODP_SHM_IOVA_INVALID when IOVA address is not available.
+ */
+ uint64_t iova;
+
+ /** Segment start address in physical address space
+ *
+ * Value is ODP_SHM_PA_INVALID when physical address is not available.
+ */
+ uint64_t pa;
+
+ /** Segment length in bytes */
+ uint64_t len;
+
+} odp_shm_segment_info_t;
+
+/**
* Shared memory capabilities
*/
typedef struct odp_shm_capability_t {
@@ -85,7 +160,7 @@ typedef struct odp_shm_capability_t {
*
* This number of separate shared memory blocks can be
* reserved concurrently. */
- unsigned max_blocks;
+ uint32_t max_blocks;
/** Maximum memory block size in bytes
*
@@ -99,6 +174,14 @@ typedef struct odp_shm_capability_t {
* available memory size. */
uint64_t max_align;
+ /** Supported shared memory flags
+ *
+ * A bit mask of supported ODP_SHM_* flags. Depending on the
+ * implementation some flag combinations may not be supported. In this
+ * case odp_shm_reserve() will fail.
+ */
+ uint32_t flags;
+
} odp_shm_capability_t;
/**
@@ -116,25 +199,34 @@ int odp_shm_capability(odp_shm_capability_t *capa);
/**
* Reserve a contiguous block of shared memory
*
- * @param[in] name Name of the block (maximum ODP_SHM_NAME_LEN - 1 chars)
- * @param[in] size Block size in bytes
- * @param[in] align Block alignment in bytes
- * @param[in] flags Shared memory parameter flags (ODP_SHM_*).
- * Default value is 0.
+ * Reserve a contiguous block of shared memory that fulfills size, alignment
+ * and shareability (ODP_SHM_* flags) requirements. By default (without flags), the memory
+ * block can be shared between all threads of the ODP instance. Memory address (odp_shm_addr())
+ * shareability depends on application memory model and #ODP_SHM_SINGLE_VA flag usage.
+ *
+ * Name is optional and does not need to be unique. However, if the block will be
+ * searched with odp_shm_lookup() or odp_shm_import(), a unique name is needed
+ * for correct match.
+ *
+ * @param name Name of the block or NULL. Maximum string length is ODP_SHM_NAME_LEN.
+ * @param size Block size in bytes
+ * @param align Block alignment in bytes
+ * @param flags Shared memory parameter flags (ODP_SHM_*). Default value is 0.
*
* @return Handle of the reserved block
* @retval ODP_SHM_INVALID on failure
+ *
+ * @see odp_mem_model_t
*/
-odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align,
- uint32_t flags);
+odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags);
/**
* Free a contiguous block of shared memory
*
- * Frees a previously reserved block of shared memory.
- * @note Freeing memory that is in use will result in UNDEFINED behavior
+ * Frees a previously reserved block of shared memory. Freeing memory that is
+ * in use will result in UNDEFINED behavior
*
- * @param[in] shm Block handle
+ * @param shm Block handle
*
* @retval 0 on success
* @retval <0 on failure
@@ -144,7 +236,7 @@ int odp_shm_free(odp_shm_t shm);
/**
* Lookup for a block of shared memory
*
- * @param[in] name Name of the block
+ * @param name Name of the block
*
* @return A handle to the block if it is found by name
* @retval ODP_SHM_INVALID on failure
@@ -177,20 +269,19 @@ odp_shm_t odp_shm_import(const char *remote_name,
/**
* Shared memory block address
*
- * @param[in] shm Block handle
+ * @param shm Block handle
*
* @return Memory block address
* @retval NULL on failure
*/
void *odp_shm_addr(odp_shm_t shm);
-
/**
* Shared memory block info
- * @note This is the only shared memory API function which accepts invalid
- * shm handles (any bit value) without causing undefined behavior.
*
- * @param[in] shm Block handle
+ * Get information about the specified shared memory block.
+ *
+ * @param shm Block handle
* @param[out] info Block info pointer for output
*
* @retval 0 on success
@@ -198,6 +289,26 @@ void *odp_shm_addr(odp_shm_t shm);
*/
int odp_shm_info(odp_shm_t shm, odp_shm_info_t *info);
+/**
+ * SHM block segmentation information
+ *
+ * Retrieve information about each memory segment of an SHM block. SHM info call outputs the number
+ * of memory segments (odp_shm_info_t::num_seg). A single segment info call may be used
+ * to request information for all the segments, or for a subset of those. Use 'index' and 'num'
+ * parameters to specify the segments. Segment indexing starts from zero and continues to
+ * odp_shm_info_t.num_seg - 1. Segment infos are written in virtual memory address order and
+ * without address overlaps. Segment info array must have at least 'num' elements.
+ *
+ * @param shm SHM block handle
+ * @param index Index of the first segment to retrieve information
+ * @param num Number of segments to retrieve information
+ * @param[out] info Segment info array for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_shm_segment_info(odp_shm_t shm, uint32_t index, uint32_t num,
+ odp_shm_segment_info_t info[]);
/**
* Print all shared memory blocks
@@ -205,17 +316,27 @@ int odp_shm_info(odp_shm_t shm, odp_shm_info_t *info);
void odp_shm_print_all(void);
/**
+ * Print shared memory block info
+ *
+ * Print implementation defined information about the specified shared memory
+ * block to the ODP log. The information is intended to be used for debugging.
+ *
+ * @param shm Block handle
+ */
+void odp_shm_print(odp_shm_t shm);
+
+/**
* Get printable value for an odp_shm_t
*
- * @param hdl odp_shm_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * This routine is intended to be used for diagnostic purposes to enable
+ * applications to generate a printable value that represents an odp_shm_t
+ * handle.
+ *
+ * @param shm Block handle
*
- * @note This routine is intended to be used for diagnostic purposes
- * to enable applications to generate a printable value that represents
- * an odp_shm_t handle.
+ * @return uint64_t value that can be used to print this handle
*/
-uint64_t odp_shm_to_u64(odp_shm_t hdl);
+uint64_t odp_shm_to_u64(odp_shm_t shm);
/**
* @}
diff --git a/include/odp/api/spec/spinlock.h b/include/odp/api/spec/spinlock.h
index 11b7339b1..a71c83c52 100644
--- a/include/odp/api/spec/spinlock.h
+++ b/include/odp/api/spec/spinlock.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP spinlock
*/
-#ifndef ODP_API_SPINLOCK_H_
-#define ODP_API_SPINLOCK_H_
+#ifndef ODP_API_SPEC_SPINLOCK_H_
+#define ODP_API_SPEC_SPINLOCK_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/spinlock_recursive.h b/include/odp/api/spec/spinlock_recursive.h
index c9c7ddb02..bc42f1ce1 100644
--- a/include/odp/api/spec/spinlock_recursive.h
+++ b/include/odp/api/spec/spinlock_recursive.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP recursive spinlock
*/
-#ifndef ODP_API_SPINLOCK_RECURSIVE_H_
-#define ODP_API_SPINLOCK_RECURSIVE_H_
+#ifndef ODP_API_SPEC_SPINLOCK_RECURSIVE_H_
+#define ODP_API_SPEC_SPINLOCK_RECURSIVE_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/stash.h b/include/odp/api/spec/stash.h
new file mode 100644
index 000000000..61ae58eba
--- /dev/null
+++ b/include/odp/api/spec/stash.h
@@ -0,0 +1,439 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP stash
+ */
+
+#ifndef ODP_API_SPEC_STASH_H_
+#define ODP_API_SPEC_STASH_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/stash_types.h>
+
+/** @addtogroup odp_stash
+ * Stash for storing object handles
+ * @{
+ */
+
+/**
+ * Query stash capabilities
+ *
+ * Outputs capabilities of the given stash type on success. The stash type
+ * is not supported if 'max_stashes' capability is zero. The default stash
+ * type (ODP_STASH_TYPE_DEFAULT) is always supported. The function returns
+ * failure if the given stash type is unknown to the implementation.
+ *
+ * @param[out] capa Pointer to capability structure for output
+ * @param type Stash type
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_stash_capability(odp_stash_capability_t *capa, odp_stash_type_t type);
+
+/**
+ * Initialize stash params
+ *
+ * Initialize an odp_stash_param_t to its default values for all fields.
+ *
+ * @param param Parameter structure to be initialized
+ */
+void odp_stash_param_init(odp_stash_param_t *param);
+
+/**
+ * Create a stash
+ *
+ * This routine is used to create a stash for object handles. Object handle
+ * values are opaque data to ODP implementation. Application may use a stash
+ * to store e.g. pointers, offsets or indexes to arbitrary objects which are
+ * allocated and freed frequently (e.g. per packet) during application
+ * processing. Object handle size is specified in stash parameters.
+ *
+ * It is optional to give a name. Names do not have to be unique. However,
+ * odp_stash_lookup() returns only a single matching stash.
+ *
+ * @param name Name of the stash or NULL. Maximum string length is
+ * ODP_STASH_NAME_LEN.
+ * @param param Stash creation parameters
+ *
+ * @return Handle of the created stash
+ * @retval ODP_STASH_INVALID Stash could not be created
+ */
+odp_stash_t odp_stash_create(const char *name, const odp_stash_param_t *param);
+
+/**
+ * Destroy a stash
+ *
+ * Destroy a previously created stash. Stash must be empty before it is
+ * destroyed. Results are undefined if an attempt is made to destroy a stash
+ * that contains object handles.
+ *
+ * @param stash The stash to be destroyed
+ *
+ * @retval 0 Success
+ * @retval <0 Failure
+ */
+int odp_stash_destroy(odp_stash_t stash);
+
+/**
+ * Find a stash by name
+ *
+ * @param name Name of the stash
+ *
+ * @return Handle of the first matching stash
+ * @retval ODP_STASH_INVALID Stash could not be found
+ */
+odp_stash_t odp_stash_lookup(const char *name);
+
+/**
+ * Get printable value for a stash handle
+ *
+ * @param stash Handle to be converted for debugging
+ * @return uint64_t value that can be used for debugging (e.g. printed)
+ */
+uint64_t odp_stash_to_u64(odp_stash_t stash);
+
+/**
+ * Put object handles into a stash
+ *
+ * Store object handles into the stash. Handle values are opaque data to
+ * ODP implementation and may be e.g. pointers or indexes to arbitrary objects.
+ * Application specifies object handle size and maximum number of handles to be
+ * stored in stash creation parameters.
+ *
+ * A successful operation returns the actual number of object handles stored.
+ * If the return value is less than 'num', the remaining handles at the end of
+ * 'obj' array are not stored.
+ *
+ * In case of ODP_STASH_TYPE_FIFO, object handles are stored into the stash in
+ * the order they are in the array.
+ *
+ * @param stash Stash handle
+ * @param obj Points to an array of object handles to be stored.
+ * Object handle size is specified by 'obj_size' in stash
+ * creation parameters. The array must be 'obj_size' aligned
+ * in memory.
+ * @param num Number of object handles to store
+ *
+ * @return Number of object handles actually stored (0 ... num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put(odp_stash_t stash, const void *obj, int32_t num);
+
+/**
+ * Put batch of object handles into a stash
+ *
+ * Otherwise like odp_stash_put(), except that this function stores either all
+ * 'num' object handles or none. odp_stash_capability_t.max_put_batch defines
+ * the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param obj Points to an array of object handles to be stored.
+ * Object handle size is specified by 'obj_size' in stash
+ * creation parameters. The array must be 'obj_size' aligned
+ * in memory.
+ * @param num Number of object handles to store
+ *
+ * @return Number of object handles actually stored (0 or num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_batch(odp_stash_t stash, const void *obj, int32_t num);
+
+/**
+ * Put 32-bit integers into a stash
+ *
+ * Otherwise like odp_stash_put(), except that this function operates on 32-bit
+ * integers. The stash must have been created with 'obj_size' of 4.
+ *
+ * @param stash Stash handle
+ * @param val Points to an array of 32-bit integers to be stored. The array
+ * must be 32-bit aligned in memory.
+ * @param num Number of integers to store
+ *
+ * @return Number of integers actually stored (0 ... num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_u32(odp_stash_t stash, const uint32_t val[], int32_t num);
+
+/**
+ * Put batch of 32-bit integers into a stash
+ *
+ * Otherwise like odp_stash_put_u32(), except that this function stores either
+ * all 'num' object handles or none. odp_stash_capability_t.max_put_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param val Points to an array of 32-bit integers to be stored. The array
+ * must be 32-bit aligned in memory.
+ * @param num Number of integers to store
+ *
+ * @return Number of integers actually stored (0 or num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_u32_batch(odp_stash_t stash, const uint32_t val[], int32_t num);
+
+/**
+ * Put 64-bit integers into a stash
+ *
+ * Otherwise like odp_stash_put(), except that this function operates on 64-bit
+ * integers. The stash must have been created with 'obj_size' of 8.
+ *
+ * @param stash Stash handle
+ * @param val Points to an array of 64-bit integers to be stored. The array
+ * must be 64-bit aligned in memory.
+ * @param num Number of integers to store
+ *
+ * @return Number of integers actually stored (0 ... num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_u64(odp_stash_t stash, const uint64_t val[], int32_t num);
+
+/**
+ * Put batch of 64-bit integers into a stash
+ *
+ * Otherwise like odp_stash_put_u64(), except that this function stores either
+ * all 'num' object handles or none. odp_stash_capability_t.max_put_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param val Points to an array of 64-bit integers to be stored. The array
+ * must be 64-bit aligned in memory.
+ * @param num Number of integers to store
+ *
+ * @return Number of integers actually stored (0 or num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_u64_batch(odp_stash_t stash, const uint64_t val[], int32_t num);
+
+/**
+ * Put pointers into a stash
+ *
+ * Otherwise like odp_stash_put(), except that this function operates on
+ * pointers. The stash must have been created with 'obj_size' matching to the
+ * size of uintptr_t.
+ *
+ * @param stash Stash handle
+ * @param ptr Points to an array of pointers to be stored. The array must be
+ * pointer size aligned in memory.
+ * @param num Number of pointers to store
+ *
+ * @return Number of pointers actually stored (0 ... num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_ptr(odp_stash_t stash, const uintptr_t ptr[], int32_t num);
+
+/**
+ * Put batch of pointers into a stash
+ *
+ * Otherwise like odp_stash_put_ptr(), except that this function stores either
+ * all 'num' object handles or none. odp_stash_capability_t.max_put_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param ptr Points to an array of pointers to be stored. The array must be
+ * pointer size aligned in memory.
+ * @param num Number of pointers to store
+ *
+ * @return Number of pointers actually stored (0 or num)
+ * @retval <0 on failure
+ */
+int32_t odp_stash_put_ptr_batch(odp_stash_t stash, const uintptr_t ptr[], int32_t num);
+
+/**
+ * Get object handles from a stash
+ *
+ * Get previously stored object handles from the stash. Application specifies
+ * object handle size in stash creation parameters.
+ *
+ * @param stash Stash handle
+ * @param[out] obj Points to an array of object handles for output.
+ * Object handle size is specified by 'obj_size' in stash
+ * creation parameters. The array must be 'obj_size' aligned
+ * in memory.
+ * @param num Maximum number of object handles to get from the stash
+ *
+ * @return Number of object handles actually output (0 ... num) to 'obj' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get(odp_stash_t stash, void *obj, int32_t num);
+
+/**
+ * Get batch of object handles from a stash
+ *
+ * Otherwise like odp_stash_get(), except that this function outputs either
+ * all 'num' object handles or none. odp_stash_capability_t.max_get_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param[out] obj Points to an array of object handles for output.
+ * Object handle size is specified by 'obj_size' in stash
+ * creation parameters. The array must be 'obj_size' aligned
+ * in memory.
+ * @param num Number of object handles to get from the stash
+ *
+ * @return Number of object handles actually output (0 or num) to 'obj' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_batch(odp_stash_t stash, void *obj, int32_t num);
+
+/**
+ * Get 32-bit integers from a stash
+ *
+ * Otherwise like odp_stash_get(), except that this function operates on 32-bit
+ * integers. The stash must have been created with 'obj_size' of 4.
+ *
+ * @param stash Stash handle
+ * @param[out] val Points to an array of 32-bit integers for output. The
+ * array must be 32-bit aligned in memory.
+ * @param num Maximum number of integers to get from the stash
+ *
+ * @return Number of integers actually output (0 ... num) to 'val' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_u32(odp_stash_t stash, uint32_t val[], int32_t num);
+
+/**
+ * Get batch of 32-bit integers from a stash
+ *
+ * Otherwise like odp_stash_get_u32(), except that this function outputs either
+ * all 'num' object handles or none. odp_stash_capability_t.max_get_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param[out] val Points to an array of 32-bit integers for output. The
+ * array must be 32-bit aligned in memory.
+ * @param num Number of integers to get from the stash
+ *
+ * @return Number of integers actually output (0 or num) to 'val' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_u32_batch(odp_stash_t stash, uint32_t val[], int32_t num);
+
+/**
+ * Get 64-bit integers from a stash
+ *
+ * Otherwise like odp_stash_get(), except that this function operates on 64-bit
+ * integers. The stash must have been created with 'obj_size' of 8.
+ *
+ * @param stash Stash handle
+ * @param[out] val Points to an array of 64-bit integers for output. The
+ * array must be 64-bit aligned in memory.
+ * @param num Maximum number of integers to get from the stash
+ *
+ * @return Number of integers actually output (0 ... num) to 'val' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_u64(odp_stash_t stash, uint64_t val[], int32_t num);
+
+/**
+ * Get batch of 64-bit integers from a stash
+ *
+ * Otherwise like odp_stash_get_u64(), except that this function outputs either
+ * all 'num' object handles or none. odp_stash_capability_t.max_get_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param[out] val Points to an array of 64-bit integers for output. The
+ * array must be 64-bit aligned in memory.
+ * @param num Number of integers to get from the stash
+ *
+ * @return Number of integers actually output (0 or num) to 'val' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_u64_batch(odp_stash_t stash, uint64_t val[], int32_t num);
+
+/**
+ * Get pointers from a stash
+ *
+ * Otherwise like odp_stash_get(), except that this function operates on
+ * pointers. The stash must have been created with 'obj_size' matching to the
+ * size of uintptr_t.
+ *
+ * @param stash Stash handle
+ * @param[out] ptr Points to an array of pointers for output. The array must
+ * be pointer size aligned in memory.
+ * @param num Maximum number of pointers to get from the stash
+ *
+ * @return Number of pointers actually output (0 ... num) to 'ptr' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_ptr(odp_stash_t stash, uintptr_t ptr[], int32_t num);
+
+/**
+ * Get batch of pointers from a stash
+ *
+ * Otherwise like odp_stash_get_ptr(), except that this function outputs either
+ * all 'num' object handles or none. odp_stash_capability_t.max_get_batch
+ * defines the maximum supported batch size.
+ *
+ * @param stash Stash handle
+ * @param[out] ptr Points to an array of pointers for output. The array must
+ * be pointer size aligned in memory.
+ * @param num Number of pointers to get from the stash
+ *
+ * @return Number of pointers actually output (0 or num) to 'ptr' array
+ * @retval <0 on failure
+ */
+int32_t odp_stash_get_ptr_batch(odp_stash_t stash, uintptr_t ptr[], int32_t num);
+
+/**
+ * Flush object handles from the thread local cache
+ *
+ * Flushes all object handles from the thread local cache into the stash, so
+ * that those are available to odp_stash_get() calls from other threads. This
+ * call may be used to ensure that thread local cache is empty e.g. before
+ * the calling thread stops using the stash.
+ *
+ * Flush and put operations share 'put_mode' setting in stash creation
+ * parameters. So, application must ensure that flush and put operations are not
+ * used concurrently, when ODP_STASH_OP_ST is selected.
+ *
+ * @param stash Stash handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_stash_flush_cache(odp_stash_t stash);
+
+/**
+ * Print debug information about the stash
+ *
+ * Print implementation defined information about the stash to the ODP log. The information
+ * is intended to be used for debugging.
+ *
+ * @param stash Stash handle
+ */
+void odp_stash_print(odp_stash_t stash);
+
+/**
+ * Read statistics counters of a stash
+ *
+ * Read the statistics counters enabled using odp_stash_stats_opt_t during stash creation.
+ * Inactive counters are set to zero by the implementation.
+ *
+ * @param stash Stash handle
+ * @param[out] stats Points to statistics counters structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_stash_stats(odp_stash_t stash, odp_stash_stats_t *stats);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/stash_types.h b/include/odp/api/spec/stash_types.h
new file mode 100644
index 000000000..48c4b9be8
--- /dev/null
+++ b/include/odp/api/spec/stash_types.h
@@ -0,0 +1,312 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP stash types
+ */
+
+#ifndef ODP_API_SPEC_STASH_TYPES_H_
+#define ODP_API_SPEC_STASH_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_stash ODP STASH
+ * @{
+ */
+
+/**
+ * @typedef odp_stash_t
+ * Stash handle
+ */
+
+/**
+ * @def ODP_STASH_INVALID
+ * Invalid stash handle
+ */
+
+/**
+ * @def ODP_STASH_NAME_LEN
+ * Maximum stash name length in chars including null char
+ */
+
+/**
+ * Stash types
+ */
+typedef enum odp_stash_type_t {
+ /** The default stash type
+ *
+ * It is implementation specific in which order odp_stash_get() calls
+ * return object handles from the stash. The order may be FIFO, LIFO
+ * or something else. Use this for the best performance when any
+ * particular ordering is not required.
+ */
+ ODP_STASH_TYPE_DEFAULT = 0,
+
+ /** Stash type FIFO
+ *
+ * Stash is implemented as a FIFO. A stash maintains object handle
+ * order of consecutive odp_stash_put() calls. Object handles stored
+ * first in the stash are received first by following odp_stash_get()
+ * calls. To maintain (strict) FIFO ordering of object handles,
+ * application needs to ensure that odp_stash_put()
+ * (or odp_stash_get()) operations are not performed concurrently from
+ * multiple threads. When multiple threads put (or get) object handles
+ * concurrently in the stash, object handles from different threads
+ * may be interleaved on output.
+ */
+ ODP_STASH_TYPE_FIFO
+
+} odp_stash_type_t;
+
+/**
+ * Stash operation mode
+ */
+typedef enum odp_stash_op_mode_t {
+ /** Multi-thread safe operation
+ *
+ * Multiple threads operate on the stash. A stash operation
+ * (odp_stash_put() or odp_stash_get()) may be performed concurrently
+ * from multiple threads.
+ */
+ ODP_STASH_OP_MT = 0,
+
+ /** Single thread operation
+ *
+ * Multiple threads operate on the stash, but application ensures that
+ * a stash operation (odp_stash_put() or odp_stash_get()) is not
+ * performed concurrently from multiple threads.
+ */
+ ODP_STASH_OP_ST,
+
+ /** Thread local operation
+ *
+ * Only a single thread operates on the stash. Both stash operations
+ * (odp_stash_put() and odp_stash_get()) are always performed from the
+ * same thread.
+ */
+ ODP_STASH_OP_LOCAL
+
+} odp_stash_op_mode_t;
+
+/**
+ * Stash statistics counters options
+ *
+ * Statistics counters listed in a bit field structure.
+ */
+typedef union odp_stash_stats_opt_t {
+ /** Option flags */
+ struct {
+ /** See odp_stash_stats_t::count */
+ uint64_t count : 1;
+
+ /** See odp_stash_stats_t::cache_count */
+ uint64_t cache_count : 1;
+
+ } bit;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or for bitwise
+ * operations over the entire structure. */
+ uint64_t all;
+
+} odp_stash_stats_opt_t;
+
+/**
+ * Stash statistics counters
+ */
+typedef struct odp_stash_stats_t {
+ /** Object count in the stash
+ *
+ * Number of objects currently stored in the stash. The count does not include objects
+ * stored in thread local caches. When caching is enabled, the total object count
+ * is the sum of 'count' and 'cache_count'.
+ */
+ uint64_t count;
+
+ /** Object count in thread local caches of the stash
+ *
+ * Number of objects stored in all thread local caches of the stash.
+ */
+ uint64_t cache_count;
+
+} odp_stash_stats_t;
+
+/**
+ * Stash capabilities (per stash type)
+ */
+typedef struct odp_stash_capability_t {
+ /** Maximum number of stashes of any type */
+ uint32_t max_stashes_any_type;
+
+ /** Maximum number of stashes of this type
+ *
+ * The value of zero means that the requested stash type is not
+ * supported.
+ */
+ uint32_t max_stashes;
+
+ /** Maximum common number of object handles per stash for any object size
+ *
+ * An application is able to store at least this many objects when using any of the
+ * supported object sizes. Some of the per object size values listed in 'max_num' may be
+ * larger than this common value.
+ */
+ uint64_t max_num_obj;
+
+ /** Maximum number of object handles per stash for each object size
+ *
+ * Values for unsupported object handle sizes are set to zero.
+ */
+ struct {
+ /** Maximum number of 1 byte object handles */
+ uint64_t u8;
+
+ /** Maximum number of 2 byte object handles */
+ uint64_t u16;
+
+ /** Maximum number of 4 byte object handles */
+ uint64_t u32;
+
+ /** Maximum number of 8 byte object handles */
+ uint64_t u64;
+
+ /** Maximum number of 16 byte object handles */
+ uint64_t u128;
+
+ /** Maximum number of 'max_obj_size' object handles */
+ uint64_t max_obj_size;
+ } max_num;
+
+ /** Maximum object handle size in bytes
+ *
+ * At least 4 byte object handle size is always supported.
+ */
+ uint32_t max_obj_size;
+
+ /** Maximum size of thread local cache */
+ uint32_t max_cache_size;
+
+ /** Maximum number of object handles in batch get operations
+ *
+ * At least 1 object batch size is always supported.
+ */
+ uint32_t max_get_batch;
+
+ /** Maximum number of object handles in batch put operations
+ *
+ * At least 1 object batch size is always supported.
+ */
+ uint32_t max_put_batch;
+
+ /** Supported statistics counters */
+ odp_stash_stats_opt_t stats;
+
+} odp_stash_capability_t;
+
+/**
+ * Stash parameters
+ */
+typedef struct odp_stash_param_t {
+ /** Stash type
+ *
+ * Select type of the stash to be created. The default value is
+ * ODP_STASH_TYPE_DEFAULT. Use stash capability to check if additional
+ * types are supported.
+ */
+ odp_stash_type_t type;
+
+ /** Put operation mode
+ *
+ * The default value is ODP_STASH_OP_MT. Usage of ODP_STASH_OP_ST or
+ * ODP_STASH_OP_LOCAL mode may improve performance when applicable.
+ * If ODP_STASH_OP_LOCAL is used, it must be set to both put_mode and
+ * get_mode.
+ */
+ odp_stash_op_mode_t put_mode;
+
+ /** Get operation mode
+ *
+ * The default value is ODP_STASH_OP_MT. Usage of ODP_STASH_OP_ST or
+ * ODP_STASH_OP_LOCAL mode may improve performance when applicable.
+ * If ODP_STASH_OP_LOCAL is used, it must be set to both put_mode and
+ * get_mode.
+ */
+ odp_stash_op_mode_t get_mode;
+
+ /** Number of object handles
+ *
+ * Application must be able to store at least this many object handles
+ * into the stash. An implementation may round up the value. The given
+ * value must not exceed 'max_num' capability.
+ */
+ uint64_t num_obj;
+
+ /** Object handle size in bytes
+ *
+ * Application uses object handles of this size in put and get
+ * operations. Valid values are powers of two (1, 2, 4, 8, ... bytes)
+ * and must not exceed 'max_obj_size' capability.
+ */
+ uint32_t obj_size;
+
+ /** Maximum number of object handles cached locally per thread
+ *
+ * A non-zero value allows implementation to cache object handles
+ * locally per each thread. Thread local caching may improve
+ * performance, but requires application to take into account that
+ * some object handles may be stored locally per thread and thus are
+ * not available to odp_stash_get() calls from other threads.
+ *
+ * Strict FIFO ordering of object handles cannot be maintained with
+ * thread local caching. If application does not require strict
+ * ordering, it may allow caching also with ODP_STASH_TYPE_FIFO type
+ * stashes.
+ *
+ * This is the maximum number of handles to be cached per thread. The
+ * actual cache size and how it is divided between put and get
+ * operations is implementation specific. The value must not exceed
+ * 'max_cache_size' capability. The default value is 0.
+ *
+ * Thread local cache may be emptied with odp_stash_flush_cache().
+ */
+ uint32_t cache_size;
+
+ /**
+ * Configure statistics counters
+ *
+ * See stash capabilities for supported statistics counters. Use odp_stash_stats() to read
+ * the enabled counters. For optimal performance, enable only those counters that are
+ * actually used. All counters are disabled by default.
+ */
+ odp_stash_stats_opt_t stats;
+
+ /**
+ * Strict size
+ *
+ * If true, application never attempts to store more handles into the stash than specified
+ * in the 'num_obj' parameter. Implementation may use this value as a hint for performance
+ * optimizations. The default value is false.
+ */
+ odp_bool_t strict_size;
+
+} odp_stash_param_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/std.h b/include/odp/api/spec/std.h
new file mode 100644
index 000000000..7be543338
--- /dev/null
+++ b/include/odp/api/spec/std.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP standard types and optimized C library functions
+ */
+
+#ifndef ODP_API_SPEC_STD_H_
+#define ODP_API_SPEC_STD_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+/** @addtogroup odp_std
+ * Standard types and performance optimized versions of selected C library
+ * functions.
+ * @{
+ */
+
+/**
+ * Memcpy
+ *
+ * ODP version of C library memcpy function. It copies 'num' bytes from source
+ * to destination address. Source and destination memory blocks must not
+ * overlap.
+ *
+ * @param dst Pointer to destination memory block
+ * @param src Pointer to source memory block
+ * @param num Number of bytes to copy
+ *
+ * @return 'dst' address
+ */
+void *odp_memcpy(void *dst, const void *src, size_t num);
+
+/**
+ * Memset
+ *
+ * ODP version of C library memset function. It sets 'value' to first 'num'
+ * bytes of memory block pointed by 'ptr'.
+ *
+ * @param ptr Pointer to the memory block
+ * @param value Value to be set
+ * @param num Number of bytes to set
+ *
+ * @return 'ptr' address
+ */
+void *odp_memset(void *ptr, int value, size_t num);
+
+/**
+ * Memcmp
+ *
+ * ODP version of C library memcmp function. It compares first 'num' bytes of
+ * memory blocks pointed by 'ptr1' and 'ptr2'.
+ *
+ * @param ptr1 Pointer to a memory block
+ * @param ptr2 Pointer to a memory block
+ * @param num Number of bytes to compare
+ *
+ * @retval 0 when the contents of memory blocks match
+ * @retval <0 when the contents of memory blocks do not match, and
+ * block 'ptr1' is less than block 'ptr2'
+ * @retval >0 when the contents of memory blocks do not match, and
+ * block 'ptr1' is greater than block 'ptr2'
+ */
+int odp_memcmp(const void *ptr1, const void *ptr2, size_t num);
+
+/**
+ * Convert fractional number (u64) to double
+ *
+ * Converts value of the unsigned 64 bit fractional number to a double-precision
+ * floating-point value.
+ *
+ * @param fract Pointer to a fractional number
+ *
+ * @return Value of the fractional number as double
+ */
+double odp_fract_u64_to_dbl(const odp_fract_u64_t *fract);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/std_clib.h b/include/odp/api/spec/std_clib.h
deleted file mode 100644
index 33e9db536..000000000
--- a/include/odp/api/spec/std_clib.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP version of often used C library calls
- */
-
-#ifndef ODP_API_STD_CLIB_H_
-#define ODP_API_STD_CLIB_H_
-#include <odp/visibility_begin.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @defgroup odp_std_clib ODP STD CLIB
- * @details
- * ODP version of often used C library calls
- * @{
- */
-
-/**
- * Memcpy
- *
- * ODP version of C library memcpy function. It copies 'num' bytes from source
- * to destination address. Source and destination memory blocks must not
- * overlap.
- *
- * @param dst Pointer to destination memory block
- * @param src Pointer to source memory block
- * @param num Number of bytes to copy
- *
- * @return 'dst' address
- */
-void *odp_memcpy(void *dst, const void *src, size_t num);
-
-/**
- * Memset
- *
- * ODP version of C library memset function. It sets 'value' to first 'num'
- * bytes of memory block pointed by 'ptr'.
- *
- * @param ptr Pointer to the memory block
- * @param value Value to be set
- * @param num Number of bytes to set
- *
- * @return 'ptr' address
- */
-void *odp_memset(void *ptr, int value, size_t num);
-
-/**
- * Memcmp
- *
- * ODP version of C library memcmp function. It compares first 'num' bytes of
- * memory blocks pointed by 'ptr1' and 'ptr2'.
- *
- * @param ptr1 Pointer to a memory block
- * @param ptr2 Pointer to a memory block
- * @param num Number of bytes to compare
- *
- * @retval 0 when the contents of memory blocks match
- * @retval <0 when the contents of memory blocks do not match, and
- * block 'ptr1' is less than block 'ptr2'
- * @retval >0 when the contents of memory blocks do not match, and
- * block 'ptr1' is greater than block 'ptr2'
- */
-int odp_memcmp(const void *ptr1, const void *ptr2, size_t num);
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <odp/visibility_end.h>
-#endif
diff --git a/include/odp/api/spec/std_types.h b/include/odp/api/spec/std_types.h
index ec6a6df6d..5428631b6 100644
--- a/include/odp/api/spec/std_types.h
+++ b/include/odp/api/spec/std_types.h
@@ -1,26 +1,28 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021 Nokia
*/
-
/**
* @file
*
- * Standard C language types and definitions for ODP.
+ * Common types and definitions for ODP API files.
*
*/
-#ifndef ODP_API_STD_TYPES_H_
-#define ODP_API_STD_TYPES_H_
+#ifndef ODP_API_SPEC_STD_TYPES_H_
+#define ODP_API_SPEC_STD_TYPES_H_
#include <odp/visibility_begin.h>
+/* uint64_t, uint32_t, etc */
+#include <stdint.h>
+#include <odp/api/align.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @addtogroup odp_system ODP SYSTEM
+/** @defgroup odp_std ODP STD
* @{
*/
@@ -32,6 +34,132 @@ extern "C" {
*/
/**
+ * Percentage type
+ * Use odp_percent_t for specifying fields that are percentages. It is a fixed
+ * point integer whose units are expressed as one-hundredth of a percent.
+ * Hence 100% is represented as integer value 10000.
+ */
+typedef uint32_t odp_percent_t;
+
+/** Unaligned uint16_t type */
+typedef uint16_t odp_una_u16_t ODP_ALIGNED(1);
+
+/** Unaligned uint32_t type */
+typedef uint32_t odp_una_u32_t ODP_ALIGNED(1);
+
+/** Unaligned uint64_t type */
+typedef uint64_t odp_una_u64_t ODP_ALIGNED(1);
+
+/**
+ * @typedef odp_u128_t
+ * 128-bit unsigned integer structure
+ */
+
+/**
+ * 128-bit unsigned integer structure
+ */
+typedef struct ODP_ALIGNED(16) odp_u128_s {
+ /** 128 bits in various sizes */
+ union {
+ /** 128 bits as uint64_t words */
+ uint64_t u64[2];
+ /** 128 bits as uint32_t words */
+ uint32_t u32[4];
+ /** 128 bits as uint16_t words */
+ uint16_t u16[8];
+ /** 128 bits as bytes */
+ uint8_t u8[16];
+ };
+} odp_u128_t;
+
+/**
+ * Unsigned 64 bit fractional number
+ *
+ * The number is composed of integer and fraction parts. The fraction part is composed of
+ * two terms: numerator and denominator. Value of the number is sum of the integer and fraction
+ * parts: value = integer + numer/denom. When the fraction part is zero, the numerator is zero and
+ * the denominator may be zero.
+ */
+typedef struct odp_fract_u64_t {
+ /** Integer part */
+ uint64_t integer;
+
+ /** Numerator of the fraction part */
+ uint64_t numer;
+
+ /** Denominator of the fraction part. This may be zero when the numerator
+ * is zero. */
+ uint64_t denom;
+
+} odp_fract_u64_t;
+
+/**
+ * ODP support
+ *
+ * Support levels are specified in the relative order, where ODP_SUPPORT_NO is
+ * the lowest level. E.g. if the examined support level is greater than
+ * ODP_SUPPORT_NO, the feature is supported in some form.
+ */
+typedef enum odp_support_t {
+ /**
+ * Feature is not supported
+ */
+ ODP_SUPPORT_NO = 0,
+ /**
+ * Feature is supported
+ */
+ ODP_SUPPORT_YES,
+ /**
+ * Feature is supported and preferred
+ */
+ ODP_SUPPORT_PREFERRED
+
+} odp_support_t;
+
+/** Definition of ODP features */
+typedef union odp_feature_t {
+ /** All features */
+ uint32_t all_feat;
+
+ /** Individual feature bits */
+ struct {
+ /** Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx() */
+ uint32_t cls:1;
+
+ /** Compression APIs, e.g., odp_comp_xxx() */
+ uint32_t compress:1;
+
+ /** Crypto APIs, e.g., odp_crypto_xxx() */
+ uint32_t crypto:1;
+
+ /** DMA APIs, e.g., odp_dma_xxx() */
+ uint32_t dma:1;
+
+ /** IPsec APIs, e.g., odp_ipsec_xxx() */
+ uint32_t ipsec:1;
+
+ /** Machine Learning APIs, e.g., odp_ml_xxx() */
+ uint32_t ml:1;
+
+ /** Scheduler APIs, e.g., odp_schedule_xxx() */
+ uint32_t schedule:1;
+
+ /** Stash APIs, e.g., odp_stash_xxx() */
+ uint32_t stash:1;
+
+ /** Time APIs, e.g., odp_time_xxx() */
+ uint32_t time:1;
+
+ /** Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx() */
+ uint32_t timer:1;
+
+ /** Traffic Manager APIs, e.g., odp_tm_xxx() */
+ uint32_t tm:1;
+ } feat;
+
+} odp_feature_t;
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/sync.h b/include/odp/api/spec/sync.h
index 6f87db559..18272af88 100644
--- a/include/odp/api/spec/sync.h
+++ b/include/odp/api/spec/sync.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
/**
@@ -10,8 +9,8 @@
* ODP memory barriers
*/
-#ifndef ODP_API_SYNC_H_
-#define ODP_API_SYNC_H_
+#ifndef ODP_API_SPEC_SYNC_H_
+#define ODP_API_SPEC_SYNC_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -23,15 +22,24 @@ extern "C" {
* @details
* <b> Memory barriers </b>
*
- * Memory barriers enforce ordering of memory load and store operations
- * specified before and after the barrier. These barriers may affect both
- * compiler optimizations and CPU out-of-order execution. All ODP
- * synchronization mechanisms (e.g. execution barriers, locks, queues, etc )
- * include all necessary memory barriers, so these calls are not needed when
- * using those. Also ODP atomic operations have memory ordered versions. These
- * explicit barriers may be needed when thread synchronization is based on
- * a non-ODP defined mechanism. Depending on the HW platform, heavy usage of
- * memory barriers may cause significant performance degradation.
+ * A memory barrier enforces the order between memory accesses (loads and/or stores)
+ * specified (in program order) before the barrier with those specified after the barrier.
+ * A barrier may affect both compiler optimizations and CPU out-of-order execution. Depending
+ * on the used HW platform and barrier types, heavy usage of barriers may cause significant
+ * performance degradation.
+ *
+ * An application may use these memory barrier functions e.g. to build a synchronization
+ * mechanism between its threads in shared memory, or when it accesses memory mapped registers
+ * of a device.
+ *
+ * An application does not need to use these memory barriers when using other ODP APIs for thread
+ * synchronization (execution barriers, spinlocks, etc.), or when exchanging data through ODP API
+ * mechanisms (queues, stashes, etc.). Those ODP calls include necessary (acquire and release)
+ * memory barriers to maintain coherency between data producers and consumers.
+ *
+ * Some ODP atomic operations include a memory barrier - see for example odp_atomic_load_acq_u32()
+ * or odp_atomic_store_rel_u32(). Application may use also those (non-relaxed) atomic operations
+ * to enforce memory ordering while using atomic variables.
*
* @{
*/
@@ -80,6 +88,39 @@ void odp_mb_acquire(void);
void odp_mb_full(void);
/**
+ * Memory barrier for load and store synchronization
+ *
+ * This memory barrier ensures that all memory accesses (loads and stores) specified before the
+ * barrier (in program order) are complete prior to any memory access specified after the barrier
+ * begins execution.
+ *
+ * This is a stronger barrier than odp_mb_full(), as in addition to visibility order also memory
+ * access completion is ensured. The barrier may be useful e.g. when synchronizing loads and stores
+ * into memory mapped registers of a device.
+ */
+void odp_mb_sync(void);
+
+/**
+ * Memory barrier for load synchronization
+ *
+ * This memory barrier ensures that all memory loads specified before the barrier (in program
+ * order) are complete prior to any memory load specified after the barrier begins execution.
+ *
+ * The barrier may be useful e.g. when synchronizing loads from memory mapped registers of a device.
+ */
+void odp_mb_sync_load(void);
+
+/**
+ * Memory synchronization barrier for stores
+ *
+ * This memory barrier ensures that all memory stores specified before the barrier (in program
+ * order) are complete prior to any memory store specified after the barrier begins execution.
+ *
+ * The barrier may be useful e.g. when synchronizing stores to memory mapped registers of a device.
+ */
+void odp_mb_sync_store(void);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/system_info.h b/include/odp/api/spec/system_info.h
index ca4dcdc7a..98efe06f7 100644
--- a/include/odp/api/spec/system_info.h
+++ b/include/odp/api/spec/system_info.h
@@ -1,18 +1,16 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2021 Nokia
*/
-
/**
* @file
*
* ODP system information
*/
-#ifndef ODP_API_SYSTEM_INFO_H_
-#define ODP_API_SYSTEM_INFO_H_
+#ifndef ODP_API_SPEC_SYSTEM_INFO_H_
+#define ODP_API_SPEC_SYSTEM_INFO_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -20,17 +18,314 @@ extern "C" {
#endif
/** @defgroup odp_system ODP SYSTEM
+ * System information.
* @{
*/
+/** Maximum memory block name length in chars (including null char) */
+#define ODP_SYSTEM_MEMBLOCK_NAME_LEN 64
+
+/**
+ * CPU instruction set architecture (ISA) families
+ */
+typedef enum odp_cpu_arch_t {
+ /** Unknown CPU architecture */
+ ODP_CPU_ARCH_UNKNOWN = 0,
+
+ /** ARM */
+ ODP_CPU_ARCH_ARM,
+
+ /** MIPS */
+ ODP_CPU_ARCH_MIPS,
+
+ /** PowerPC */
+ ODP_CPU_ARCH_PPC,
+
+ /** RISC-V */
+ ODP_CPU_ARCH_RISCV,
+
+ /** x86 */
+ ODP_CPU_ARCH_X86
+
+} odp_cpu_arch_t;
+
+/**
+ * ARM ISA versions
+ *
+ * ISA versions are defined in ascending order.
+ */
+typedef enum odp_cpu_arch_arm_t {
+ /** Unknown ARM ISA version */
+ ODP_CPU_ARCH_ARM_UNKNOWN = 0,
+
+ /** ARMv6 ISA */
+ ODP_CPU_ARCH_ARMV6,
+
+ /** ARMv7-A ISA */
+ ODP_CPU_ARCH_ARMV7,
+
+ /** ARMv8.0-A ISA */
+ ODP_CPU_ARCH_ARMV8_0,
+
+ /** ARMv8.1-A ISA */
+ ODP_CPU_ARCH_ARMV8_1,
+
+ /** ARMv8.2-A ISA */
+ ODP_CPU_ARCH_ARMV8_2,
+
+ /** ARMv8.3-A ISA */
+ ODP_CPU_ARCH_ARMV8_3,
+
+ /** ARMv8.4-A ISA */
+ ODP_CPU_ARCH_ARMV8_4,
+
+ /** ARMv8.5-A ISA */
+ ODP_CPU_ARCH_ARMV8_5,
+
+ /** ARMv8.6-A ISA */
+ ODP_CPU_ARCH_ARMV8_6,
+
+ /** ARMv8.7-A ISA */
+ ODP_CPU_ARCH_ARMV8_7,
+
+ /** ARMv8.8-A ISA */
+ ODP_CPU_ARCH_ARMV8_8,
+
+ /** ARMv8.9-A ISA */
+ ODP_CPU_ARCH_ARMV8_9,
+
+ /** ARMv9.0-A ISA */
+ ODP_CPU_ARCH_ARMV9_0,
+
+ /** ARMv9.1-A ISA */
+ ODP_CPU_ARCH_ARMV9_1,
+
+ /** ARMv9.2-A ISA */
+ ODP_CPU_ARCH_ARMV9_2,
+
+ /** ARMv9.3-A ISA */
+ ODP_CPU_ARCH_ARMV9_3,
+
+} odp_cpu_arch_arm_t;
+
+/**
+ * MIPS ISA versions
+ */
+typedef enum odp_cpu_arch_mips_t {
+ /** Unknown MIPS ISA version */
+ ODP_CPU_ARCH_MIPS_UNKNOWN = 0
+
+} odp_cpu_arch_mips_t;
+
+/**
+ * PowerPC ISA versions
+ */
+typedef enum odp_cpu_arch_ppc_t {
+ /** Unknown PPC ISA version */
+ ODP_CPU_ARCH_PPC_UNKNOWN = 0
+
+} odp_cpu_arch_ppc_t;
+
+/**
+ * RISC-V ISA versions
+ */
+typedef enum odp_cpu_arch_riscv_t {
+ /** Unknown RISC-V ISA version */
+ ODP_CPU_ARCH_RISCV_UNKNOWN = 0
+
+} odp_cpu_arch_riscv_t;
+
+/**
+ * x86 ISA versions
+ */
+typedef enum odp_cpu_arch_x86_t {
+ /** Unknown x86 ISA version */
+ ODP_CPU_ARCH_X86_UNKNOWN = 0,
+
+ /** x86 32bit ISA */
+ ODP_CPU_ARCH_X86_I686,
+
+ /** x86 64bit ISA */
+ ODP_CPU_ARCH_X86_64
+
+} odp_cpu_arch_x86_t;
+
+/**
+ * CPU ISA versions
+ */
+typedef union odp_cpu_arch_isa_t {
+ /** ARM ISA versions */
+ odp_cpu_arch_arm_t arm;
+
+ /** MIPS ISA versions */
+ odp_cpu_arch_mips_t mips;
+
+ /** PowerPC ISA versions */
+ odp_cpu_arch_ppc_t ppc;
+
+ /** RISC-V ISA versions */
+ odp_cpu_arch_riscv_t riscv;
+
+ /** x86 ISA versions */
+ odp_cpu_arch_x86_t x86;
+
+} odp_cpu_arch_isa_t;
+
+/**
+ * System info
+ */
+typedef struct odp_system_info_t {
+ /**
+ * CPU architecture
+ *
+ * Defines CPU ISA family: ARM, MIPS, PPC, RISC-V, x86 or unknown
+ */
+ odp_cpu_arch_t cpu_arch;
+
+ /**
+ * ISA version of ODP software
+ *
+ * Defines the ISA version that was used to build the ODP library. Depending on compiler
+ * target architecture setting, the value may be lower than the ISA version supported by
+ * the CPU hardware.
+ */
+ odp_cpu_arch_isa_t cpu_isa_sw;
+
+ /**
+ * ISA version of CPU hardware
+ *
+ * Defines the ISA version supported by the CPU hardware. The value is set to
+ * ODP_CPU_ARCH_<arch>_UNKNOWN, when the ISA version cannot be determined.
+ */
+ odp_cpu_arch_isa_t cpu_isa_hw;
+
+} odp_system_info_t;
+
+/**
+ * Memory information
+ */
+typedef struct odp_system_meminfo_t {
+ /**
+ * Total mapped memory
+ *
+ * Total amount of memory (in bytes) in all memory pages that are reserved by
+ * this ODP instance from the system.
+ */
+ uint64_t total_mapped;
+
+ /**
+ * Total memory usage
+ *
+ * Total amount of memory (in bytes) that is currently in use by this ODP instance.
+ * This is a subset of 'total_mapped' bytes.
+ */
+ uint64_t total_used;
+
+ /**
+ * Total memory usage overheads
+ *
+ * Total amount of memory (in bytes) that is currently consumed by roundings to
+ * alignment/block/page size limits, etc. overheads. This is a subset of 'total_used'
+ * bytes.
+ */
+ uint64_t total_overhead;
+
+} odp_system_meminfo_t;
+
+/**
+ * Memory block information
+ */
+typedef struct odp_system_memblock_t {
+ /** Memory block name */
+ char name[ODP_SYSTEM_MEMBLOCK_NAME_LEN];
+
+ /** Start address of the block */
+ uintptr_t addr;
+
+ /**
+ * Memory usage
+ *
+ * Total amount of memory (in bytes) that is used by this block.
+ */
+ uint64_t used;
+
+ /**
+ * Memory usage overheads
+ *
+ * Total amount of memory (in bytes) that is currently consumed by rounding to
+ * alignment/block/page size limits, etc. overheads. This is a subset of 'used' bytes.
+ */
+ uint64_t overhead;
+
+ /** Memory page size in bytes
+ *
+ * Page size used for this block.
+ */
+ uint64_t page_size;
+
+} odp_system_memblock_t;
+
+/**
+ * Retrieve system information
+ *
+ * Fills in system information structure on success. The call is not intended
+ * for fast path use.
+ *
+ * @param[out] info Pointer to system info struct for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_system_info(odp_system_info_t *info);
+
+/**
+ * Retrieve ODP memory usage information
+ *
+ * Retrieves information about ODP memory usage for debugging and monitoring purposes. A successful
+ * call fills in system memory info and outputs up to 'num' elements into memory block info array.
+ * Each array element represents a memory block used due to an API call (SHM reservation, pool
+ * creation, etc) or an implementation internal memory allocation.
+ *
+ * When return value is 'num' or less, it indicates the number of elements written. If return value
+ * is larger than 'num', all 'num' elements were written and the return value indicates the number
+ * of elements that would have been written into a large enough array.
+ *
+ * @param[out] info Pointer to memory info struct for output
+ * @param[out] block Pointer memory block info array for output
+ * @param num Maximum number of array elements to output (0 ... array size)
+ *
+ * @return Number of array elements written / would have been written
+ * @retval <0 on failure
+ */
+int32_t odp_system_meminfo(odp_system_meminfo_t *info, odp_system_memblock_t block[], int32_t num);
+
/**
* Default system huge page size in bytes
*
* @return Default huge page size in bytes
+ * @retval 0 on no huge pages
*/
uint64_t odp_sys_huge_page_size(void);
/**
+ * System huge page sizes in bytes
+ *
+ * Returns the number of huge page sizes supported by the system. Outputs up to
+ * 'num' sizes when the 'size' array pointer is not NULL. If return value is
+ * larger than 'num', there are more supported sizes than the function was
+ * allowed to output. If return value (N) is less than 'num', only sizes
+ * [0 ... N-1] have been written. Returned values are ordered from smallest to
+ * largest.
+ *
+ * @param[out] size Points to an array of huge page sizes for output
+ * @param num Maximum number of huge page sizes to output
+ *
+ * @return Number of supported huge page sizes
+ * @retval <0 on failure
+ */
+int odp_sys_huge_page_size_all(uint64_t size[], int num);
+
+/**
* Page size in bytes
*
* @return Page size in bytes
@@ -54,6 +349,15 @@ int odp_sys_cache_line_size(void);
void odp_sys_info_print(void);
/**
+ * Print configuration
+ *
+ * Print out implementation defined information about selected configuration options. This
+ * information is intended for debugging purposes and may contain e.g. content of various
+ * configuration files, environment variables and configuration options of ODP API.
+ */
+void odp_sys_config_print(void);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/thread.h b/include/odp/api/spec/thread.h
index 689ba59b5..34d3c2b82 100644
--- a/include/odp/api/spec/thread.h
+++ b/include/odp/api/spec/thread.h
@@ -1,70 +1,39 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
*/
-
/**
* @file
*
* ODP thread API
*/
-#ifndef ODP_API_THREAD_H_
-#define ODP_API_THREAD_H_
+#ifndef ODP_API_SPEC_THREAD_H_
+#define ODP_API_SPEC_THREAD_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_thread ODP THREAD
- * @{
- */
+#include <odp/api/thread_types.h>
-/**
- * @def ODP_THREAD_COUNT_MAX
- * Maximum number of threads supported in build time. Use
- * odp_thread_count_max() for maximum number of threads supported in run time,
- * which depend on system configuration and may be lower than this number.
- */
-
-/**
- * Thread type
+/** @addtogroup odp_thread
+ * Thread types, masks and IDs.
+ * @{
*/
-typedef enum odp_thread_type_e {
- /**
- * Worker thread
- *
- * Worker threads do most part of ODP application packet processing.
- * These threads provide high packet and data rates, with low and
- * predictable latency. Typically, worker threads are pinned to isolated
- * CPUs and packets are processed in a run-to-completion loop with very
- * low interference from the operating system.
- */
- ODP_THREAD_WORKER = 0,
-
- /**
- * Control thread
- *
- * Control threads do not participate the main packet flow through the
- * system, but e.g. control or monitor the worker threads, or handle
- * exceptions. These threads may perform general purpose processing,
- * use system calls, share the CPU with other threads and be interrupt
- * driven.
- */
- ODP_THREAD_CONTROL
-} odp_thread_type_t;
-
/**
* Get thread identifier
*
- * Returns the thread identifier of the current thread. Thread ids range from 0
- * to odp_thread_count_max() - 1. The ODP thread id is assigned by
- * odp_init_local() and freed by odp_term_local(). Thread id is unique within
- * the ODP instance.
+ * Returns the ODP thread identifier of current thread. Thread IDs range from 0
+ * to odp_thread_count_max() - 1 and are unique within an ODP instance.
+ *
+ * Thread IDs are assigned by odp_init_local() and freed by odp_term_local().
+ * IDs are assigned sequentially starting from 0 in the same order threads call
+ * odp_init_local(). Thread IDs freed by odp_term_local() may be reused by
+ * following odp_init_local() calls.
*
* @return Thread identifier of the current thread
*/
@@ -74,25 +43,70 @@ int odp_thread_id(void);
* Thread count
*
* Returns the current ODP thread count. This is the number of active threads
- * running the ODP instance. Each odp_init_local() call increments and each
- * odp_term_local() call decrements the count. The count is always between 1 and
- * odp_thread_count_max().
+ * of any type running in the ODP instance. Each odp_init_local() call
+ * increments and each odp_term_local() call decrements the count. The count is
+ * always between 1 and odp_thread_count_max().
*
* @return Current thread count
*/
int odp_thread_count(void);
/**
+ * Control thread count
+ *
+ * Otherwise like odp_thread_count(), but returns the number of active threads
+ * of type #ODP_THREAD_CONTROL. The count is always between 0 and
+ * odp_thread_control_count_max().
+ *
+ * @return Current control thread count
+ */
+int odp_thread_control_count(void);
+
+/**
+ * Worker thread count
+ *
+ * Otherwise like odp_thread_count(), but returns the number of active threads
+ * of type #ODP_THREAD_WORKER. The count is always between 0 and
+ * odp_thread_worker_count_max().
+ *
+ * @return Current worker thread count
+ */
+int odp_thread_worker_count(void);
+
+/**
* Maximum thread count
*
- * Returns the maximum thread count, which is a constant value and set in
- * ODP initialization phase. This may be lower than ODP_THREAD_COUNT_MAX.
+ * Returns the maximum number of threads of any type. This is a constant value
+ * and set in ODP initialization phase. The value may be lower than
+ * #ODP_THREAD_COUNT_MAX.
*
* @return Maximum thread count
*/
int odp_thread_count_max(void);
/**
+ * Maximum control thread count
+ *
+ * Otherwise like odp_thread_count_max(), but returns the maximum number of
+ * control threads (#ODP_THREAD_CONTROL). The returned value is always <=
+ * odp_thread_count_max().
+ *
+ * @return Maximum control thread count
+ */
+int odp_thread_control_count_max(void);
+
+/**
+ * Maximum worker thread count
+ *
+ * Otherwise like odp_thread_count_max(), but returns the maximum number of
+ * worker threads (#ODP_THREAD_WORKER). The returned value is always <=
+ * odp_thread_count_max().
+ *
+ * @return Maximum worker thread count
+ */
+int odp_thread_worker_count_max(void);
+
+/**
* Thread type
*
* Returns the thread type of the current thread.
@@ -101,7 +115,6 @@ int odp_thread_count_max(void);
*/
odp_thread_type_t odp_thread_type(void);
-
/**
* @}
*/
diff --git a/include/odp/api/spec/thread_types.h b/include/odp/api/spec/thread_types.h
new file mode 100644
index 000000000..13846cd8f
--- /dev/null
+++ b/include/odp/api/spec/thread_types.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP thread API types
+ */
+
+#ifndef ODP_API_SPEC_THREAD_TYPES_H_
+#define ODP_API_SPEC_THREAD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_thread ODP THREAD
+ * @{
+ */
+
+/**
+ * @def ODP_THREAD_COUNT_MAX
+ * Maximum number of threads supported in build time. Use odp_thread_count_max()
+ * for maximum number of threads supported in run time, which depends on system
+ * configuration and may be lower than this number.
+ */
+
+/**
+ * Thread type
+ */
+typedef enum odp_thread_type_e {
+ /**
+ * Worker thread
+ *
+ * Worker threads do most part of ODP application packet processing.
+ * These threads provide high packet and data rates, with low and
+ * predictable latency. Typically, worker threads are pinned to isolated
+ * CPUs and packets are processed in a run-to-completion loop with very
+ * low interference from the operating system.
+ */
+ ODP_THREAD_WORKER = 0,
+
+ /**
+ * Control thread
+ *
+ * Control threads do not participate the main packet flow through the
+ * system, but e.g. control or monitor the worker threads, or handle
+ * exceptions. These threads may perform general purpose processing,
+ * use system calls, share the CPU with other threads and be interrupt
+ * driven.
+ */
+ ODP_THREAD_CONTROL
+} odp_thread_type_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/spec/threshold.h b/include/odp/api/spec/threshold.h
new file mode 100644
index 000000000..732da7274
--- /dev/null
+++ b/include/odp/api/spec/threshold.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP threshold descriptor
+ */
+
+#ifndef ODP_API_SPEC_THRESHOLD_H_
+#define ODP_API_SPEC_THRESHOLD_H_
+
+#include <odp/visibility_begin.h>
+#include <odp/api/std_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Supported threshold types
+ *
+ * Supported threshold types in a bit field structure.
+ */
+typedef union odp_threshold_types_t {
+ /** bitfields for different threshold types */
+ struct {
+ /** Percentage of the total size of pool or queue */
+ uint8_t percent:1;
+
+ /** Total number of all transient packets */
+ uint8_t packet:1;
+
+ /** Total size of all transient packets in bytes */
+ uint8_t bytes:1;
+ };
+
+ /** All bits of the bit field structure */
+ uint8_t all_bits;
+} odp_threshold_types_t;
+
+/**
+ * ODP Threshold types
+ *
+ * Different types of threshold measurements
+ */
+typedef enum odp_threshold_type_t {
+ /** Percentage of the total size of pool or queue */
+ ODP_THRESHOLD_PERCENT,
+
+ /** Total number of all transient packets */
+ ODP_THRESHOLD_PACKET,
+
+ /** Total size of all transient packets in bytes */
+ ODP_THRESHOLD_BYTE
+} odp_threshold_type_t;
+
+/**
+ * ODP Threshold
+ *
+ * Threshold configuration
+ */
+typedef struct odp_threshold_t {
+ /** Type of threshold */
+ odp_threshold_type_t type;
+
+ /** Different threshold types */
+ union {
+ /** Percentage */
+ struct {
+ /** Max percentage value */
+ odp_percent_t max;
+
+ /** Min percentage value */
+ odp_percent_t min;
+ } percent;
+
+ /** Packet count */
+ struct {
+ /** Max packet count */
+ uint64_t max;
+
+ /** Min packet count */
+ uint64_t min;
+ } packet;
+
+ /** Sum of all data bytes of all packets */
+ struct {
+ /** Max byte count */
+ uint64_t max;
+
+ /** Min byte count */
+ uint64_t min;
+ } byte;
+ };
+} odp_threshold_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/thrmask.h b/include/odp/api/spec/thrmask.h
index 3986769ac..725690e9e 100644
--- a/include/odp/api/spec/thrmask.h
+++ b/include/odp/api/spec/thrmask.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,8 +8,8 @@
* ODP thread masks
*/
-#ifndef ODP_API_THRMASK_H_
-#define ODP_API_THRMASK_H_
+#ifndef ODP_API_SPEC_THRMASK_H_
+#define ODP_API_SPEC_THRMASK_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
diff --git a/include/odp/api/spec/ticketlock.h b/include/odp/api/spec/ticketlock.h
index b23253b55..2a14273ee 100644
--- a/include/odp/api/spec/ticketlock.h
+++ b/include/odp/api/spec/ticketlock.h
@@ -1,18 +1,15 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
-
/**
* @file
*
* ODP ticketlock
*/
-#ifndef ODP_API_TICKETLOCK_H_
-#define ODP_API_TICKETLOCK_H_
+#ifndef ODP_API_SPEC_TICKETLOCK_H_
+#define ODP_API_SPEC_TICKETLOCK_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -44,7 +41,6 @@ extern "C" {
*/
void odp_ticketlock_init(odp_ticketlock_t *tklock);
-
/**
* Acquire ticket lock.
*
@@ -69,7 +65,6 @@ int odp_ticketlock_trylock(odp_ticketlock_t *tklock);
*/
void odp_ticketlock_unlock(odp_ticketlock_t *tklock);
-
/**
* Check if ticket lock is locked.
*
diff --git a/include/odp/api/spec/time.h b/include/odp/api/spec/time.h
index 29175eb5a..f7e20a6f4 100644
--- a/include/odp/api/spec/time.h
+++ b/include/odp/api/spec/time.h
@@ -1,72 +1,120 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
*/
-
/**
* @file
*
* ODP time
*/
-#ifndef ODP_API_TIME_H_
-#define ODP_API_TIME_H_
+#ifndef ODP_API_SPEC_TIME_H_
+#define ODP_API_SPEC_TIME_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_time ODP TIME
+#include <odp/api/std_types.h>
+#include <odp/api/time_types.h>
+
+/** @addtogroup odp_time
+ * SoC global and CPU local wall clock time
+ *
* @{
*/
-/* Time in nanoseconds */
-#define ODP_TIME_USEC_IN_NS 1000ULL /**< Microsecond in nsec */
-#define ODP_TIME_MSEC_IN_NS 1000000ULL /**< Millisecond in nsec */
-#define ODP_TIME_SEC_IN_NS 1000000000ULL /**< Second in nsec */
+/**
+ * Current local time
+ *
+ * Returns current CPU local time stamp value. The used time source is specific to the calling
+ * thread and the CPU it is running on during the call. Time stamp values from different
+ * time sources cannot be compared or otherwise mixed.
+ *
+ * Local time stamp value advances with a constant rate defined by odp_time_local_res(). The rate
+ * remains constant even during dynamic CPU frequency scaling. Local time stamp and related
+ * nanosecond values may not start from zero, but are guaranteed not to wrap around in at least
+ * 10 years from the ODP instance startup.
+ *
+ * @return CPU local time stamp value
+ */
+odp_time_t odp_time_local(void);
/**
- * @typedef odp_time_t
- * ODP time stamp. Time stamp can represent a time stamp from local or global
- * time source. A local time stamp must not be shared between threads. API calls
- * work correctly only when all time stamps for input are from the same time
- * source.
+ * Current local time in nanoseconds
+ *
+ * Like odp_time_local(), but the time stamp value is converted into nanoseconds.
+ *
+ * @return Local time stamp in nanoseconds
*/
+uint64_t odp_time_local_ns(void);
/**
- * @def ODP_TIME_NULL
- * Zero time stamp
+ * Current local time (strict)
+ *
+ * Like odp_time_local(), but reads the time stamp value more strictly in the program order.
+ * The function may decrease CPU performance around the call, as it may include additional
+ * barrier instructions or otherwise limit out-of-order execution.
+ *
+ * @return Local time stamp
*/
+odp_time_t odp_time_local_strict(void);
/**
- * Current local time
+ * Current local time in nanoseconds (strict)
*
- * Returns current local time stamp value. The local time source provides high
- * resolution time, it is initialized to zero during ODP startup and will not
- * wrap around in at least 10 years.
- * Local time stamps are local to the calling thread and must not be shared
- * with other threads.
+ * Like odp_time_local_strict(), but the time stamp value is converted into nanoseconds.
*
- * @return Local time stamp.
+ * @return Local time stamp in nanoseconds
*/
-odp_time_t odp_time_local(void);
+uint64_t odp_time_local_strict_ns(void);
/**
* Current global time
*
- * Returns current global time stamp value. The global time source provides high
- * resolution time, it is initialized to zero during ODP startup and will not
- * wrap around in at least 10 years.
- * Global time stamps can be shared between threads.
+ * Returns current SoC global time stamp value. Global time stamp values read by different threads
+ * (or CPUs) may be compared or otherwise mixed as those come from the same time source.
*
- * @return Global time stamp.
+ * Global time stamp value advances with a constant rate defined by odp_time_global_res(). The rate
+ * remains constant even during dynamic CPU frequency scaling. Global time stamp and related
+ * nanosecond values may not start from zero, but are guaranteed not to wrap around in at least
+ * 10 years from the ODP instance startup.
+ *
+ * @return SoC global time stamp value
*/
odp_time_t odp_time_global(void);
/**
+ * Current global time in nanoseconds
+ *
+ * Like odp_time_global(), but the time stamp value is converted into nanoseconds.
+ *
+ * @return Global time stamp in nanoseconds
+ */
+uint64_t odp_time_global_ns(void);
+
+/**
+ * Current global time (strict)
+ *
+ * Like odp_time_global(), but reads the time stamp value more strictly (see
+ * odp_time_local_strict() documentation) in the program order.
+ *
+ * @return Global time stamp
+ */
+odp_time_t odp_time_global_strict(void);
+
+/**
+ * Current global time in nanoseconds (strict)
+ *
+ * Like odp_time_global_strict(), but the time stamp value is converted into nanoseconds.
+ *
+ * @return Global time stamp in nanoseconds
+ */
+uint64_t odp_time_global_strict_ns(void);
+
+/**
* Time difference
*
* @param t2 Second time stamp
@@ -77,8 +125,35 @@ odp_time_t odp_time_global(void);
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1);
/**
+ * Time difference in nanoseconds
+ *
+ * @param t2 Second time stamp
+ * @param t1 First time stamp
+ *
+ * @return Difference of time stamps (t2 - t1) in nanoseconds
+ */
+uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1);
+
+/**
+ * Add nanoseconds into time
+ *
+ * Adds 'ns' nanoseconds into the time stamp value. The resulting time may wrap around, if
+ * the sum of 'time' and 'ns' is more than 10 years from the ODP instance startup.
+ *
+ * @param time Time stamp
+ * @param ns Nanoseconds to be added
+ *
+ * @return Time stamp incremented by 'ns' nanoseconds
+ */
+odp_time_t odp_time_add_ns(odp_time_t time, uint64_t ns);
+
+/**
* Time sum
*
+ * Returns the sum of time stamp values. Time stamps must be from the same time source (global or
+ * local). The resulting time may wrap around, if the sum exceeds 10 years from the ODP instance
+ * startup.
+ *
* @param t1 Time stamp
* @param t2 Time stamp
*
@@ -158,6 +233,16 @@ void odp_time_wait_until(odp_time_t time);
void odp_time_wait_ns(uint64_t ns);
/**
+ * Get ODP instance startup time
+ *
+ * Outputs time stamp values captured at ODP instance startup. Application may use those
+ * to calculate time stamp values relative to ODP startup time.
+ *
+ * @param[out] startup Startup time structure for output
+ */
+void odp_time_startup(odp_time_startup_t *startup);
+
+/**
* @}
*/
diff --git a/include/odp/api/spec/time_types.h b/include/odp/api/spec/time_types.h
new file mode 100644
index 000000000..bd8f324a3
--- /dev/null
+++ b/include/odp/api/spec/time_types.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP time
+ */
+
+#ifndef ODP_API_SPEC_TIME_TYPES_H_
+#define ODP_API_SPEC_TIME_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup odp_time ODP TIME
+ * @{
+ */
+
+/** A microsecond in nanoseconds */
+#define ODP_TIME_USEC_IN_NS 1000ULL
+
+/** A millisecond in nanoseconds */
+#define ODP_TIME_MSEC_IN_NS 1000000ULL
+
+/** A second in nanoseconds */
+#define ODP_TIME_SEC_IN_NS 1000000000ULL
+
+/** A minute in nanoseconds */
+#define ODP_TIME_MIN_IN_NS 60000000000ULL
+
+/** An hour in nanoseconds */
+#define ODP_TIME_HOUR_IN_NS 3600000000000ULL
+
+/**
+ * @typedef odp_time_t
+ * ODP time stamp. Time stamp can represent a time stamp from local or global
+ * time source. A local time stamp must not be shared between threads. API calls
+ * work correctly only when all time stamps for input are from the same time
+ * source.
+ */
+
+/**
+ * @def ODP_TIME_NULL
+ * Zero time stamp
+ */
+
+/**
+ * Time stamp values at ODP startup
+ */
+typedef struct odp_time_startup_t {
+ /** Global time at ODP startup */
+ odp_time_t global;
+
+ /** Global time in nanoseconds at ODP startup */
+ uint64_t global_ns;
+
+} odp_time_startup_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/timer.h b/include/odp/api/spec/timer.h
index 75f9db98e..1e7a06ad4 100644
--- a/include/odp/api/spec/timer.h
+++ b/include/odp/api/spec/timer.h
@@ -1,127 +1,137 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
-
/**
* @file
*
* ODP timer service
*/
-#ifndef ODP_API_TIMER_H_
-#define ODP_API_TIMER_H_
+#ifndef ODP_API_SPEC_TIMER_H_
+#define ODP_API_SPEC_TIMER_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
-/** @defgroup odp_timer ODP TIMER
- * @{
- */
-
-/**
- * @typedef odp_timer_pool_t
- * ODP timer pool handle
- */
-
-/**
- * @def ODP_TIMER_POOL_INVALID
- * Invalid timer pool handle
- */
-
-/**
- * Clock sources for timers in timer pool.
- */
-typedef enum {
- /** Use CPU clock as clock source for timers */
- ODP_CLOCK_CPU,
- /** Use external clock as clock source for timers */
- ODP_CLOCK_EXT
- /* Platform dependent which other clock sources exist */
-} odp_timer_clk_src_t;
-
-/**
- * @typedef odp_timer_t
- * ODP timer handle
- */
-
-/**
- * @def ODP_TIMER_INVALID
- * Invalid timer handle
- */
+#include <odp/api/timer_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/queue_types.h>
-/**
- * @typedef odp_timeout_t
- * ODP timeout handle
+/** @addtogroup odp_timer
+ * @{
*/
/**
- * @def ODP_TIMEOUT_INVALID
- * Invalid timeout handle
+ * Query timer capabilities per clock source
+ *
+ * Outputs timer capabilities on success. Returns -1 if the clock source
+ * is not supported.
+ *
+ * @param clk_src Clock source for timers
+ * @param[out] capa Pointer to capability structure for output
+ *
+ * @retval 0 on success
+ * @retval -1 when the clock source is not supported
+ * @retval <-1 on other failures
*/
+int odp_timer_capability(odp_timer_clk_src_t clk_src, odp_timer_capability_t *capa);
/**
- * Return values of timer set calls.
- */
-typedef enum {
-/**
- * Timer set operation succeeded
+ * Timer resolution capability
+ *
+ * This function fills in capability limits for timer pool resolution and
+ * min/max timeout values, based on either resolution or maximum timeout.
+ * Set the required value to a resolution field (res_ns or res_hz) or to the
+ * maximum timeout field (max_tmo), and set other fields to zero. A successful
+ * call fills in the other fields. The call returns a failure, if the user
+ * defined value exceeds capability limits. Outputted values are minimums for
+ * 'res_ns' and 'min_tmo', and maximums for 'res_hz' and 'max_tmo'.
+ *
+ * @param clk_src Clock source for timers
+ * @param[in,out] res_capa Resolution capability pointer for input/output.
+ * Set either a resolution or max timeout field,
+ * a successful call fills in other fields.
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
- ODP_TIMER_SUCCESS = 0,
-/**
- * Timer set operation failed, expiration too early.
- * Either retry with a later expiration time or process the timeout
- * immediately. */
- ODP_TIMER_TOOEARLY = -1,
+int odp_timer_res_capability(odp_timer_clk_src_t clk_src,
+ odp_timer_res_capability_t *res_capa);
/**
- * Timer set operation failed, expiration too late.
- * Truncate the expiration time against the maximum timeout for the
- * timer pool. */
- ODP_TIMER_TOOLATE = -2,
-/**
- * Timer set operation failed because no timeout event specified and no
- * timeout event present in the timer (timer inactive/expired).
+ * Periodic timer capability
+ *
+ * Checks periodic timer capability to support the requested base frequency and other parameters.
+ * Application sets 'capa' with the requested timer pool base frequency, the maximum
+ * frequency multiplier and the minimum timeout resolution. If there is no requirement for timeout
+ * resolution, it is set to zero.
+ *
+ * When the call returns success, 'capa' fields are overwritten in following ways. On return value
+ * of 1, timer supports the requested base frequency exactly, and meets or exceeds other requested
+ * values. The base frequency value is not modified, but other 'capa' fields are updated with
+ * resulting maximum capabilities.
+ *
+ * When the call returns 0, the requested base frequency is not supported exactly, but timer
+ * capabilities meet or exceed all other requested values. In this case, the call overwrites
+ * 'base_freq_hz' with the closest supported frequency and updates other 'capa' fields accordingly.
+ *
+ * Failure is returned when the requirements are not supported or the call fails otherwise.
+ *
+ * @param clk_src Clock source for timer pool
+ * @param[in,out] capa Pointer to periodic timer capability for input/output.
+ *
+ * @retval 1 Success. Capability matches base frequency, and meets or exceeds other requested
+ * values.
+ * @retval 0 Success. Capability does not match base frequency exactly, but meets or exceeds
+ * other requested values.
+ * @retval <0 Failure
*/
- ODP_TIMER_NOEVENT = -3
-} odp_timer_set_t;
+int odp_timer_periodic_capability(odp_timer_clk_src_t clk_src,
+ odp_timer_periodic_capability_t *capa);
/**
- * @def ODP_TIMER_POOL_NAME_LEN
- * Maximum timer pool name length in chars including null char
- */
-
-/** Timer pool parameters
- * Timer pool parameters are used when creating and querying timer pools.
+ * Initialize timer pool parameters
+ *
+ * Initialize an odp_timer_pool_param_t to its default values for all fields.
+ *
+ * @param[out] param Pointer to the odp_timer_pool_param_t structure to be initialized
*/
-typedef struct {
- uint64_t res_ns; /**< Timeout resolution in nanoseconds */
- uint64_t min_tmo; /**< Minimum relative timeout in nanoseconds */
- uint64_t max_tmo; /**< Maximum relative timeout in nanoseconds */
- uint32_t num_timers; /**< (Minimum) number of supported timers */
- int priv; /**< Shared (false) or private (true) timer pool */
- odp_timer_clk_src_t clk_src; /**< Clock source for timers */
-} odp_timer_pool_param_t;
+void odp_timer_pool_param_init(odp_timer_pool_param_t *param);
/**
* Create a timer pool
*
- * The use of pool name is optional. Unique names are not required.
+ * Creates a timer pool according to the parameters. Use 'timer_type' parameter to select if timers
+ * are single shot or periodic. All timers in the pool are of the same type. The selected timer
+ * type defines which other parameters are used or ignored.
+ *
+ * The use of pool name is optional. Unique names are not required. Use odp_timer_pool_param_init()
+ * to initialize timer pool parameters into their default values.
+ *
+ * After creation a timer pool can be either started (see odp_timer_pool_start_multi()) or
+ * destroyed. The returned pool handle cannot be used with any other APIs, except
+ * odp_timer_pool_to_u64(), before the pool is successfully started.
+ *
+ * Periodic timer expiration frequency is a multiple of the timer pool base frequency
+ * (odp_timer_pool_param_t::base_freq_hz). Depending on implementation, the base frequency may need
+ * to be selected carefully with respect to the timer pool source clock frequency. Use
+ * odp_timer_periodic_capability() to check which base frequencies and multipliers are supported.
+ *
+ * The call returns failure when requested parameter values are not supported.
*
* @param name Name of the timer pool or NULL. Maximum string length is
* ODP_TIMER_POOL_NAME_LEN.
* @param params Timer pool parameters. The content will be copied.
*
* @return Timer pool handle on success
- * @retval ODP_TIMER_POOL_INVALID on failure and errno set
+ * @retval ODP_TIMER_POOL_INVALID on failure
*/
-odp_timer_pool_t
-odp_timer_pool_create(const char *name,
- const odp_timer_pool_param_t *params);
+odp_timer_pool_t odp_timer_pool_create(const char *name, const odp_timer_pool_param_t *params);
/**
* Start a timer pool
@@ -130,171 +140,308 @@ odp_timer_pool_create(const char *name,
* The purpose of this call is to coordinate the creation of multiple timer
* pools that may use the same underlying HW resources.
* This function may be called multiple times.
+ *
+ * @deprecated Use odp_timer_pool_start_multi() instead
*/
void odp_timer_pool_start(void);
/**
+ * Start timer pools
+ *
+ * Start given timer pools. After a pool has been successfully started the pool handle can be used
+ * with other APIs. Each timer pool can be started only once.
+ *
+ * Returns 'num' when all given timer pools have been successfully started. If the return value
+ * N < 'num', only the first N pools started successfully and at least some of the remaining ones
+ * failed to start. In case of a negative return value, none of the pools were started. The
+ * unstarted timer pools cannot be used anymore (can only be destroyed).
+ *
+ * @param timer_pool Array of timer pool handles
+ * @param num Number of pools to start
+ *
+ * @retval num on success
+ * @retval <num on failure
+ */
+int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num);
+
+/**
* Destroy a timer pool
*
* Destroy a timer pool, freeing all resources.
* All timers must have been freed.
*
- * @param tpid Timer pool identifier
+ * @param timer_pool Timer pool
*/
-void odp_timer_pool_destroy(odp_timer_pool_t tpid);
+void odp_timer_pool_destroy(odp_timer_pool_t timer_pool);
/**
* Convert timer ticks to nanoseconds
*
- * @param tpid Timer pool identifier
- * @param ticks Timer ticks
+ * @param timer_pool Timer pool
+ * @param ticks Timer ticks
*
* @return Nanoseconds
*/
-uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, uint64_t ticks);
+uint64_t odp_timer_tick_to_ns(odp_timer_pool_t timer_pool, uint64_t ticks);
/**
* Convert nanoseconds to timer ticks
*
- * @param tpid Timer pool identifier
- * @param ns Nanoseconds
+ * @param timer_pool Timer pool
+ * @param ns Nanoseconds
*
* @return Timer ticks
*/
-uint64_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns);
+uint64_t odp_timer_ns_to_tick(odp_timer_pool_t timer_pool, uint64_t ns);
/**
* Current tick value
*
- * @param tpid Timer pool identifier
+ * Returns the current tick value of the timer pool. Timer tick is an implementation defined unit
+ * of time. Ticks and related nanosecond values are timer pool specific. Those may not start from
+ * zero, but are guaranteed not to wrap around in at least 10 years from the ODP instance startup.
+ *
+ * Timer tick value increments with a constant, timer pool specific frequency. Tick frequency may
+ * be equal or higher than the requested timer pool resolution. The frequency can be checked with
+ * odp_timer_pool_info(). Tick value increments with implementation specific step sizes.
+ *
+ * Use odp_timer_sample_ticks() to determine offset between tick values of two or more timer pools.
+ *
+ * @param timer_pool Timer pool
*
* @return Current time in timer ticks
+ *
+ * @see odp_timer_tick_info_t
*/
-uint64_t odp_timer_current_tick(odp_timer_pool_t tpid);
+uint64_t odp_timer_current_tick(odp_timer_pool_t timer_pool);
/**
- * ODP timer pool information and configuration
+ * Sample tick values of timer pools
+ *
+ * Reads timer pool tick values simultaneously (or closely back-to-back) from all requested timer
+ * pools, and outputs those on success. Optionally, outputs corresponding source clock (HW) counter
+ * values, which are implementation specific and may be used for debugging. When a timer pool does
+ * not support reading of the source clock value, zero is written instead. Values are written into
+ * the output arrays in the same order which timer pools were defined. Nothing is written on
+ * failure.
+ *
+ * @param timer_pool Timer pools to sample
+ * @param[out] tick Tick value array for output (one element per timer pool)
+ * @param[out] clk_count Source clock counter value array for output (one element per
+ * timer pool), or NULL when counter values are not requested.
+ * @param num Number of timer pools to sample
+ *
+ * @retval 0 All requested timer pools sampled successfully
+ * @retval -1 Failure
*/
-
-typedef struct {
- odp_timer_pool_param_t param; /**< Parameters specified at creation */
- uint32_t cur_timers; /**< Number of currently allocated timers */
- uint32_t hwm_timers; /**< High watermark of allocated timers */
- const char *name; /**< Name of timer pool */
-} odp_timer_pool_info_t;
+int odp_timer_sample_ticks(odp_timer_pool_t timer_pool[], uint64_t tick[], uint64_t clk_count[],
+ int num);
/**
* Query timer pool configuration and current state
*
- * @param tpid Timer pool identifier
- * @param[out] info Pointer to information buffer
+ * @param timer_pool Timer pool
+ * @param[out] info Pointer to information buffer
*
* @retval 0 on success
* @retval <0 on failure. Info could not be retrieved.
*/
-int odp_timer_pool_info(odp_timer_pool_t tpid,
+int odp_timer_pool_info(odp_timer_pool_t timer_pool,
odp_timer_pool_info_t *info);
/**
* Allocate a timer
*
- * Create a timer (allocating all necessary resources e.g. timeout event) from
- * the timer pool. The user_ptr is copied to timeouts and can be retrieved
- * using the odp_timeout_user_ptr() call.
+ * Allocates a timer from the timer pool. Depending on timer type, the allocated timer is started
+ * with either odp_timer_start() or odp_timer_periodic_start() call. A timer may be reused multiple
+ * times before freeing it back into the timer pool.
+ *
+ * When timer expires, the timeout event defined in timer start parameters (see
+ * odp_timer_start_t::tmo_ev or odp_timer_periodic_start_t::tmo_ev) is sent into the provided
+ * destination queue.
*
- * @param tpid Timer pool identifier
- * @param queue Destination queue for timeout notifications
- * @param user_ptr User defined pointer or NULL to be copied to timeouts
+ * The provided user pointer value is copied into timeout events when the event type is
+ * ODP_EVENT_TIMEOUT. The value can be retrieved from an event with odp_timeout_user_ptr() call.
+ *
+ * @param timer_pool Timer pool
+ * @param queue Destination queue for timeout events
+ * @param user_ptr User defined pointer value or NULL
*
* @return Timer handle on success
- * @retval ODP_TIMER_INVALID on failure and errno set.
+ * @retval ODP_TIMER_INVALID on failure
*/
-odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid,
- odp_queue_t queue,
- void *user_ptr);
+odp_timer_t odp_timer_alloc(odp_timer_pool_t timer_pool, odp_queue_t queue, const void *user_ptr);
/**
* Free a timer
*
- * Free (destroy) a timer, reclaiming associated resources.
- * The timeout event for an active timer will be returned.
- * The timeout event for an expired timer will not be returned. It is the
- * responsibility of the application to handle this timeout when it is received.
+ * Frees a previously allocated timer. The timer must be inactive when calling this function.
+ * In other words, the application must cancel an active single shot timer (odp_timer_cancel())
+ * successfully or wait it to expire before freeing it. Similarly for an active periodic timer, the
+ * application must cancel it (odp_timer_periodic_cancel()) and receive the last event from
+ * the timer (odp_timer_periodic_ack()) before freeing it.
+ *
+ * The call returns failure only on non-recoverable errors. Application must not use the timer
+ * handle anymore after the call, regardless of the return value.
*
- * @param tim Timer handle
- * @return Event handle of timeout event
- * @retval ODP_EVENT_INVALID on failure
+ * @param timer Timer
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-odp_event_t odp_timer_free(odp_timer_t tim);
+int odp_timer_free(odp_timer_t timer);
/**
- * Set a timer (absolute time) with a user-provided timeout event
+ * Start a timer
*
- * Set (arm) the timer to expire at specific time. The timeout
- * event will be enqueued when the timer expires.
+ * Starts a timer with an expiration time and a timeout event. The timer must not be active when
+ * calling this function. After a successful call, the timer remains active until it expires or
+ * is cancelled successfully. An active timer can be restarted with odp_timer_restart().
*
- * @param tim Timer
- * @param abs_tck Expiration time in absolute timer ticks
- * @param[in,out] tmo_ev Reference to an event variable that points to
- * timeout event or NULL to reuse the existing timeout event. Any existing
- * timeout event that is replaced by a successful set operation will be
- * returned here.
+ * The timeout event is sent to the destination queue when the timer expires. The expiration time
+ * may be passed as absolute timer ticks or ticks relative to the current time. Use 'tick_type'
+ * parameter to select between the tick types. Current time of the timer pool can be read with
+ * odp_timer_current_tick().
*
- * @retval ODP_TIMER_SUCCESS Operation succeeded
- * @retval ODP_TIMER_TOOEARLY Operation failed because expiration tick too
- * early
- * @retval ODP_TIMER_TOOLATE Operation failed because expiration tick too
- * late
- * @retval ODP_TIMER_NOEVENT Operation failed because timeout event not
- * specified in odp_timer_set call and not present in timer
+ * The timer is not started when a failure is returned.
+ *
+ * @param timer Timer to be started
+ * @param start_param Timer start parameters
+ *
+ * @retval ODP_TIMER_SUCCESS Success
+ * @retval ODP_TIMER_TOO_NEAR Failure. The expiration time passed already, or is too near to
+ * the current time.
+ * @retval ODP_TIMER_TOO_FAR Failure. The expiration time is too far from the current time.
+ * @retval ODP_TIMER_FAIL Other failure.
*/
-int odp_timer_set_abs(odp_timer_t tim,
- uint64_t abs_tck,
- odp_event_t *tmo_ev);
+int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param);
/**
- * Set a timer with a relative expiration time and user-provided event.
+ * Restart a timer
+ *
+ * A successful restart call updates the expiration time of an active timer. The timeout event
+ * is not changed.
+ *
+ * The timer is not modified when a failure is returned. The call returns #ODP_TIMER_FAIL if
+ * the timer has expired already, or is so close to expire that it cannot be restarted anymore.
+ * A failure is returned also when the new expiration time is too near to the current time
+ * (#ODP_TIMER_TOO_NEAR) or too far from the current time (#ODP_TIMER_TOO_FAR).
*
- * Set (arm) the timer to expire at a relative future time.
+ * The new expiration time is passed the same way as with odp_timer_start() call.
*
- * @param tim Timer
- * @param rel_tck Expiration time in timer ticks relative to current time of
- * the timer pool the timer belongs to
- * @param[in,out] tmo_ev Reference to an event variable that points to
- * timeout event or NULL to reuse the existing timeout event. Any existing
- * timeout event that is replaced by a successful set operation will be
- * returned here.
+ * @param timer Timer to be restarted
+ * @param start_param Timer start parameters. Value of 'tmo_ev' parameter is ignored.
*
- * @retval ODP_TIMER_SUCCESS Operation succeeded
- * @retval ODP_TIMER_TOOEARLY Operation failed because expiration tick too
- * early
- * @retval ODP_TIMER_TOOLATE Operation failed because expiration tick too
- * late
- * @retval ODP_TIMER_NOEVENT Operation failed because timeout event not
- * specified in call and not present in timer
+ * @retval ODP_TIMER_SUCCESS Success
+ * @retval ODP_TIMER_TOO_NEAR Failure. The new expiration time passed already, or is too near to
+ * the current time.
+ * @retval ODP_TIMER_TOO_FAR Failure. The new expiration time is too far from the current time.
+ * @retval ODP_TIMER_FAIL Failure. The timer expired already, or other failure.
*/
-int odp_timer_set_rel(odp_timer_t tim,
- uint64_t rel_tck,
- odp_event_t *tmo_ev);
+int odp_timer_restart(odp_timer_t timer, const odp_timer_start_t *start_param);
+
+/**
+ * Start a periodic timer
+ *
+ * Starts a timer that delivers timeout events periodically to the destination queue starting
+ * from the first expiration time provided. The timer must have been allocated from a pool of
+ * periodic timers. The timer must not be active when calling this function. After a successful
+ * call, the timer remains active until it is cancelled and all its timeout events have been
+ * acknowledged.
+ *
+ * Timer expiration frequency (period) is defined as a multiple of the timer pool base frequency
+ * (odp_timer_pool_param_t::base_freq_hz). The timeout event type must be ODP_EVENT_TIMEOUT
+ * (odp_timeout_t).
+ *
+ * Periodic timers cannot be restarted. If the period needs to be changed, the timer is first
+ * cancelled and then started again with new parameters.
+ *
+ * Application must acknowledge each timeout event with odp_timer_periodic_ack() call. The call
+ * should be made as soon as possible after receiving the event.
+ *
+ * The timer is not started when a failure is returned.
+ *
+ * @param timer Periodic timer to be started
+ * @param start_param Periodic timer start parameters
+ *
+ * @retval ODP_TIMER_SUCCESS Success
+ * @retval ODP_TIMER_TOO_NEAR Failure. The first expiration time passed already, or is too near to
+ * the current time.
+ * @retval ODP_TIMER_TOO_FAR Failure. The first expiration time is too far from the current time.
+ * @retval ODP_TIMER_FAIL Other failure.
+ *
+ * @see odp_timer_periodic_cancel()
+ */
+int odp_timer_periodic_start(odp_timer_t timer, const odp_timer_periodic_start_t *start_param);
+
+/**
+ * Acknowledge timeout from a periodic timer
+ *
+ * This call is valid only for periodic timers. Each timeout event from a periodic timer must be
+ * acknowledged with this call. Acknowledgment should be done as soon as possible after receiving
+ * the event. A late call may affect accuracy of the following period(s). However, a missing
+ * acknowledgment may not stop timeout event delivery to the destination queue, and thus the same
+ * event may appear in the destination queue multiple times (when application falls behind).
+ *
+ * Normally, the acknowledgment call returns zero on success and consumes the timeout event.
+ * Application must not use the event anymore after this. A greater than zero return value
+ * indicates timeout events from a cancelled timer. These events may not arrive at the
+ * requested interval, but are used to finalize the timer cancel request. Return value of 2 marks
+ * the last event from a cancelled timer. After receiving it application may free the timer and
+ * the timeout event.
+ *
+ * @param timer Periodic timer
+ * @param tmo_ev Timeout event that was received from the periodic timer
+ *
+ * @retval 2 Success, the last event from a cancelled timer. The call did not consume
+ * the event.
+ * @retval 1 Success, an event from a cancelled timer. The call consumed the event.
+ * @retval 0 Success, the call consumed the event.
+ * @retval <0 Failure, the call did not consume the event.
+ */
+int odp_timer_periodic_ack(odp_timer_t timer, odp_event_t tmo_ev);
+
+/**
+ * Cancel a periodic timer
+ *
+ * Cancel a previously started periodic timer. A successful operation stops timer expiration.
+ * The timer delivers remaining timeout event(s) into the destination queue. Note that these events
+ * may not arrive at the requested interval. Return value of odp_timer_periodic_ack() call
+ * will indicate the last timeout event from the timer. Application may free the timer and
+ * the timeout event only after receiving the last event.
+ *
+ * @param timer Timer
+ *
+ * @retval 0 Periodic timer expiration stopped. The timeout event will be received through
+ * the destination queue.
+ * @retval <0 Timer cancel failed.
+ */
+int odp_timer_periodic_cancel(odp_timer_t timer);
/**
* Cancel a timer
*
- * Cancel a timer, preventing future expiration and delivery. Return any
- * present timeout event.
+ * Cancels a previously started single shot timer. A successful operation (#ODP_TIMER_SUCCESS)
+ * prevents timer expiration and returns the timeout event back to application. Application may
+ * use or free the event normally.
+ *
+ * When the timer is close to expire or has expired already, the call may not be able cancel it
+ * anymore. In this case, the call returns #ODP_TIMER_TOO_NEAR and the timeout is delivered to
+ * the destination queue.
*
- * A timer that has already expired may be impossible to cancel and the timeout
- * will instead be delivered to the destination queue.
+ * @param timer Timer
+ * @param[out] tmo_ev Pointer to an event handle for output. Event handle is written only
+ * on success.
*
- * @param tim Timer
- * @param[out] tmo_ev Pointer to an event variable
- * @retval 0 Success, active timer cancelled, timeout returned in '*tmo_ev'
- * @retval <0 on failure (timer inactive or already expired)
+ * @retval ODP_TIMER_SUCCESS Timer was cancelled successfully. Timeout event returned in 'tmo_ev'.
+ * @retval ODP_TIMER_TOO_NEAR Timer cannot be cancelled. Timer has expired already, or cannot be
+ * cancelled due to close expiration time.
+ * @retval ODP_TIMER_FAIL Other failure.
*/
-int odp_timer_cancel(odp_timer_t tim, odp_event_t *tmo_ev);
+int odp_timer_cancel(odp_timer_t timer, odp_event_t *tmo_ev);
/**
- * Return timeout handle that is associated with timeout event
+ * Get timeout handle from a ODP_EVENT_TIMEOUT type event
*
* @param ev An event of type ODP_EVENT_TIMEOUT
*
@@ -303,6 +450,17 @@ int odp_timer_cancel(odp_timer_t tim, odp_event_t *tmo_ev);
odp_timeout_t odp_timeout_from_event(odp_event_t ev);
/**
+ * Convert multiple timeout events to timeout handles
+ *
+ * All events must be of type ODP_EVENT_TIMEOUT.
+ *
+ * @param[out] tmo Timeout handle array for output
+ * @param ev Array of event handles to convert
+ * @param num Number of timeouts and events
+ */
+void odp_timeout_from_event_multi(odp_timeout_t tmo[], const odp_event_t ev[], int num);
+
+/**
* Convert timeout handle to event handle
*
* @param tmo Timeout handle
@@ -313,14 +471,17 @@ odp_event_t odp_timeout_to_event(odp_timeout_t tmo);
/**
* Check for fresh timeout
+ *
* If the corresponding timer has been reset or cancelled since this timeout
* was enqueued, the timeout is stale (not fresh).
*
* @param tmo Timeout handle
* @retval 1 Timeout is fresh
* @retval 0 Timeout is stale
+ *
+ * @deprecated The function will be removed in a future API version.
*/
-int odp_timeout_fresh(odp_timeout_t tmo);
+int ODP_DEPRECATE(odp_timeout_fresh)(odp_timeout_t tmo);
/**
* Return timer handle for the timeout
@@ -332,7 +493,15 @@ int odp_timeout_fresh(odp_timeout_t tmo);
odp_timer_t odp_timeout_timer(odp_timeout_t tmo);
/**
- * Return expiration tick for the timeout
+ * Timeout expiration tick
+ *
+ * Returns the absolute expiration time (in timer ticks) that was used to set
+ * (or reset) the timer. For timers set with absolute expiration time this
+ * equals the provided tick value.
+ *
+ * For periodic timers, returns the expiration time of the period, or zero.
+ * When periodic timer implementation cannot return expiration time, it returns zero
+ * for all timeouts from the timer pool.
*
* @param tmo Timeout handle
*
@@ -342,6 +511,7 @@ uint64_t odp_timeout_tick(odp_timeout_t tmo);
/**
* Return user pointer for the timeout
+ *
* The user pointer was specified when the timer was allocated.
*
* @param tmo Timeout handle
@@ -351,6 +521,19 @@ uint64_t odp_timeout_tick(odp_timeout_t tmo);
void *odp_timeout_user_ptr(odp_timeout_t tmo);
/**
+ * Timeout user area
+ *
+ * Returns pointer to the user area associated with the timeout. Size of the area is fixed
+ * and defined in timeout pool parameters.
+ *
+ * @param tmo Timeout handle
+ *
+ * @return Pointer to the user area of the timeout
+ * @retval NULL The timeout does not have user area
+ */
+void *odp_timeout_user_area(odp_timeout_t tmo);
+
+/**
* Timeout alloc
*
* Allocates timeout from pool. Pool must be created with ODP_POOL_TIMEOUT type.
@@ -363,6 +546,20 @@ void *odp_timeout_user_ptr(odp_timeout_t tmo);
odp_timeout_t odp_timeout_alloc(odp_pool_t pool);
/**
+ * Allocate multiple timeouts
+ *
+ * Otherwise like odp_timeout_alloc(), but allocates multiple timeouts from a pool.
+ *
+ * @param pool Pool handle
+ * @param[out] tmo Array of timeout handles for output
+ * @param num Number of timeouts to allocate
+ *
+ * @return Number of timeouts actually allocated (0 ... num)
+ * @retval <0 on failure
+ */
+int odp_timeout_alloc_multi(odp_pool_t pool, odp_timeout_t tmo[], int num);
+
+/**
* Timeout free
*
* Frees the timeout back to the pool it was allocated from.
@@ -372,43 +569,83 @@ odp_timeout_t odp_timeout_alloc(odp_pool_t pool);
void odp_timeout_free(odp_timeout_t tmo);
/**
+ * Free multiple timeouts
+ *
+ * Otherwise like odp_timeout_free(), but frees multiple timeouts to their originating pools.
+ *
+ * @param tmo Array of timeout handles
+ * @param num Number of timeouts to free
+ */
+void odp_timeout_free_multi(odp_timeout_t tmo[], int num);
+
+/**
+ * Print timer pool debug information
+ *
+ * Prints implementation specific debug information about
+ * the timer pool to the ODP log.
+ *
+ * @param timer_pool Timer pool handle
+ */
+void odp_timer_pool_print(odp_timer_pool_t timer_pool);
+
+/**
+ * Print timer debug information
+ *
+ * Prints implementation specific debug information about
+ * the timer to the ODP log.
+ *
+ * @param timer Timer handle
+ */
+void odp_timer_print(odp_timer_t timer);
+
+/**
+ * Print timeout debug information
+ *
+ * Prints implementation specific debug information about
+ * the timeout to the ODP log.
+ *
+ * @param tmo Timeout handle
+ */
+void odp_timeout_print(odp_timeout_t tmo);
+
+/**
* Get printable value for an odp_timer_pool_t
*
- * @param hdl odp_timer_pool_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * @param timer_pool odp_timer_pool_t handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
*
* @note This routine is intended to be used for diagnostic purposes
* to enable applications to generate a printable value that represents
* an odp_timer_pool_t handle.
*/
-uint64_t odp_timer_pool_to_u64(odp_timer_pool_t hdl);
+uint64_t odp_timer_pool_to_u64(odp_timer_pool_t timer_pool);
/**
* Get printable value for an odp_timer_t
*
- * @param hdl odp_timer_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * @param timer odp_timer_t handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
*
* @note This routine is intended to be used for diagnostic purposes
* to enable applications to generate a printable value that represents
* an odp_timer_t handle.
*/
-uint64_t odp_timer_to_u64(odp_timer_t hdl);
+uint64_t odp_timer_to_u64(odp_timer_t timer);
/**
* Get printable value for an odp_timeout_t
*
- * @param hdl odp_timeout_t handle to be printed
- * @return uint64_t value that can be used to print/display this
- * handle
+ * @param tmo odp_timeout_t handle to be printed
+ *
+ * @return uint64_t value that can be used to print/display this handle
*
* @note This routine is intended to be used for diagnostic purposes
* to enable applications to generate a printable value that represents
* an odp_timeout_t handle.
*/
-uint64_t odp_timeout_to_u64(odp_timeout_t hdl);
+uint64_t odp_timeout_to_u64(odp_timeout_t tmo);
/**
* @}
diff --git a/include/odp/api/spec/timer_types.h b/include/odp/api/spec/timer_types.h
new file mode 100644
index 000000000..7db57c340
--- /dev/null
+++ b/include/odp/api/spec/timer_types.h
@@ -0,0 +1,620 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP timer API type definitions
+ */
+
+#ifndef ODP_API_SPEC_TIMER_TYPES_H_
+#define ODP_API_SPEC_TIMER_TYPES_H_
+#include <odp/visibility_begin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/deprecated.h>
+#include <odp/api/event_types.h>
+#include <odp/api/std_types.h>
+
+/** @defgroup odp_timer ODP TIMER
+ * Timer generating timeout events.
+ * @{
+ */
+
+/**
+ * @typedef odp_timer_pool_t
+ * ODP timer pool handle
+ */
+
+/**
+ * @def ODP_TIMER_POOL_INVALID
+ * Invalid timer pool handle
+ */
+
+/**
+ * @typedef odp_timer_t
+ * ODP timer handle
+ */
+
+/**
+ * @def ODP_TIMER_INVALID
+ * Invalid timer handle
+ */
+
+/**
+ * @typedef odp_timeout_t
+ * ODP timeout handle
+ */
+
+/**
+ * @def ODP_TIMEOUT_INVALID
+ * Invalid timeout handle
+ */
+
+/**
+ * @def ODP_TIMER_POOL_NAME_LEN
+ * Maximum timer pool name length in chars including null char
+ */
+
+/**
+ * Timer type
+ *
+ * There are two types of timers. A single shot timer (ODP_TIMER_TYPE_SINGLE) is started with
+ * an expiration time for each timeout. A periodic timer (ODP_TIMER_TYPE_PERIODIC) keeps expiring
+ * and sending timeout events with the given period until it is cancelled.
+ */
+typedef enum {
+ /** Single shot timer */
+ ODP_TIMER_TYPE_SINGLE = 0,
+
+ /** Periodic timer */
+ ODP_TIMER_TYPE_PERIODIC
+
+} odp_timer_type_t;
+
+/**
+ * Timer resolution capability
+ */
+typedef struct {
+ /** Timeout resolution in nanoseconds */
+ uint64_t res_ns;
+
+ /** Timeout resolution in hertz */
+ uint64_t res_hz;
+
+ /** Minimum relative timeout in nanoseconds */
+ uint64_t min_tmo;
+
+ /** Maximum relative timeout in nanoseconds */
+ uint64_t max_tmo;
+
+} odp_timer_res_capability_t;
+
+/**
+ * Periodic timer capability
+ */
+typedef struct {
+ /**
+ * Periodic timer pool base frequency in hertz
+ *
+ * Base frequency is represented as a fractional number where the fraction part is always
+ * less than one. In other words, the integer part specifies whole hertz whereas
+ * the fraction part specifies parts of a hertz (if any). The fraction part does not
+ * need to be reduced to its lowest terms - e.g. 100.5 Hz may be represented as 100 1/2 Hz,
+ * 100 5/10 Hz, or 100 Hz with some other equivalent fraction part.
+ */
+ odp_fract_u64_t base_freq_hz;
+
+ /** Maximum base frequency multiplier */
+ uint64_t max_multiplier;
+
+ /** Timeout resolution in nanoseconds */
+ uint64_t res_ns;
+
+} odp_timer_periodic_capability_t;
+
+/**
+ * Timer capability
+ */
+typedef struct {
+ /** Maximum number of timer pools
+ *
+ * The total number of timer pools that can be created combining both types and
+ * different clock sources.
+ */
+ uint32_t max_pools_combined;
+
+ /** Maximum number of timer pools for single shot timers (per clock source) */
+ uint32_t max_pools;
+
+ /** Maximum number of single shot timers in a pool
+ *
+ * The value of zero means that limited only by the available
+ * memory size for the pool. */
+ uint32_t max_timers;
+
+ /** Highest timer resolution in nanoseconds.
+ *
+ * This defines the highest resolution supported by a timer.
+ * It's the minimum valid value for 'res_ns' timer pool
+ * parameter.
+ *
+ * This value is equal to 'max_res.res_ns' capability.
+ */
+ uint64_t highest_res_ns;
+
+ /**
+ * Maximum resolution
+ *
+ * This defines the highest resolution supported by a timer, with
+ * limits to min/max timeout values. The highest resolution for a timer
+ * pool is defined by 'max_res.res_ns' in nanoseconds and
+ * 'max_res.res_hz' in hertz.
+ * When this resolution is used:
+ * - 'min_tmo' parameter value must be in minimum 'max_res.min_tmo'
+ * - 'max_tmo' parameter value must be in maximum 'max_res.max_tmo'
+ */
+ odp_timer_res_capability_t max_res;
+
+ /**
+ * Maximum timeout length
+ *
+ * This defines the maximum relative timeout value supported by a timer,
+ * with limits to min timeout and max resolution values. The maximum
+ * value for 'max_tmo' timer pool parameter is defined by
+ * 'max_tmo.max_tmo'. When this max timeout value is used:
+ * - 'min_tmo' parameter value must be in minimum 'max_tmo.min_tmo'
+ * - 'res_ns' parameter value must be in minimum 'max_tmo.res_ns' or
+ * - 'res_hz' parameter value must be in maximum 'max_tmo.res_hz'
+ */
+ odp_timer_res_capability_t max_tmo;
+
+ /**
+ * Scheduled queue destination support
+ *
+ * This defines whether schedule queues are supported as timeout
+ * destination queues.
+ * 0: Scheduled queues are not supported as timeout destination queues
+ * 1: Scheduled queues are supported as timeout destination queues
+ * @see odp_timer_alloc()
+ */
+ odp_bool_t queue_type_sched;
+
+ /**
+ * Plain queue destination support
+ *
+ * This defines whether plain queues are supported as timeout
+ * destination queues.
+ * 0: Plain queues are not supported as timeout destination queues
+ * 1: Plain queues are supported as timeout destination queues
+ * @see odp_timer_alloc()
+ */
+ odp_bool_t queue_type_plain;
+
+ /** Periodic timer capabilities */
+ struct {
+ /** Maximum number of timer pools for periodic timers
+ *
+ * When zero, periodic timers (#ODP_TIMER_TYPE_PERIODIC) are not supported.
+ */
+ uint32_t max_pools;
+
+ /** Maximum number of periodic timers in a pool */
+ uint32_t max_timers;
+
+ /** Minimum supported base frequency value */
+ odp_fract_u64_t min_base_freq_hz;
+
+ /** Maximum supported base frequency value */
+ odp_fract_u64_t max_base_freq_hz;
+
+ } periodic;
+
+} odp_timer_capability_t;
+
+/**
+ * Clock sources for timer pools
+ *
+ * ODP_CLOCK_DEFAULT is the default clock source and it is supported always. It is implementation
+ * defined which other clock sources are supported. See from implementation documentation how the
+ * supported clock sources are mapped into these enumerations.
+ */
+typedef enum {
+ /** Clock source number 0 */
+ ODP_CLOCK_SRC_0,
+
+ /** Clock source number 1 */
+ ODP_CLOCK_SRC_1,
+
+ /** Clock source number 2 */
+ ODP_CLOCK_SRC_2,
+
+ /** Clock source number 3 */
+ ODP_CLOCK_SRC_3,
+
+ /** Clock source number 4 */
+ ODP_CLOCK_SRC_4,
+
+ /** Clock source number 5 */
+ ODP_CLOCK_SRC_5,
+
+ /** Number of clock source enumerations */
+ ODP_CLOCK_NUM_SRC
+
+} odp_timer_clk_src_t;
+
+/** The default clock source */
+#define ODP_CLOCK_DEFAULT ODP_CLOCK_SRC_0
+
+/**
+ * Timer expiration mode
+ *
+ * Expiration mode selects how timer expiration tick is interpreted. In ODP_TIMER_EXP_RELAXED mode,
+ * timer implementation may round the requested time up or down within resolution limits, and
+ * therefore application may receive timeouts slightly before or after the requested time.
+ * In ODP_TIMER_EXP_AFTER mode, timers are limited to expire exactly on the requested time or
+ * after it, but never before the time.
+ */
+typedef enum {
+ /**
+ * Expiration after the target time
+ *
+ * Timers expire on the specified time or after it, but never before it. */
+ ODP_TIMER_EXP_AFTER = 0,
+
+ /**
+ * Expiration relaxed
+ *
+ * Timers may expire before or after the specified time (within resolution limits).
+ * Depending on implementation, this may improve expiration accuracy compared to
+ * ODP_TIMER_EXP_AFTER.
+ */
+ ODP_TIMER_EXP_RELAXED
+
+} odp_timer_exp_mode_t;
+
+/**
+ * Timer pool parameters
+ */
+typedef struct {
+ /** Timer type
+ *
+ * Select whether the pool is created for single shot (#ODP_TIMER_TYPE_SINGLE) or
+ * periodic (#ODP_TIMER_TYPE_PERIODIC) timers. All timers in a pool are of the same type.
+ * Timer capabilities specify how many pools of each type are supported.
+ *
+ * The default value is ODP_TIMER_TYPE_SINGLE.
+ */
+ odp_timer_type_t timer_type;
+
+ /** Clock source for timers
+ *
+ * The default value is ODP_CLOCK_DEFAULT. */
+ odp_timer_clk_src_t clk_src;
+
+ /** Timer expiration mode
+ *
+ * The default value is ODP_TIMER_EXP_AFTER. */
+ odp_timer_exp_mode_t exp_mode;
+
+ /** Timeout resolution in nanoseconds. Timer pool must serve timeouts
+ * with this or higher resolution. The minimum valid value (highest
+ * resolution) is defined by timer resolution capability. When this
+ * parameter is used, set 'res_hz' to zero. The default value is zero. */
+ uint64_t res_ns;
+
+ /** Timeout resolution in hertz. This may be used to specify the highest
+ * required resolution in hertz instead of nanoseconds. When this
+ * parameter is used, set 'res_ns' to zero. The default value is zero. */
+ uint64_t res_hz;
+
+ /** Minimum relative timeout in nanoseconds
+ *
+ * All requested timeouts will be at least this many nanoseconds after the current
+ * time of the timer pool. Timer set functions return an error, if too short timeout
+ * was requested. The value may be also smaller than the requested resolution.
+ *
+ * The default value is zero. Ignored when timer type is periodic.
+ */
+ uint64_t min_tmo;
+
+ /** Maximum relative timeout in nanoseconds
+ *
+ * All requested timeouts will be at most this many nanoseconds after the current
+ * time of the timer pool. Timer set functions return an error, if too long timeout was
+ * requested.
+ *
+ * Ignored when timer type is periodic.
+ */
+ uint64_t max_tmo;
+
+ /** Periodic timer parameters
+ *
+ * Additional parameters for periodic timers. Ignored when timer type is single shot.
+ */
+ struct {
+ /** Timer pool base frequency in hertz
+ *
+ * A periodic timer pool has a base frequency. Each timer of the pool has
+ * an expiration frequency that is an integer multiple of the base frequency.
+ * Depending on the implementation, base frequency may need to be selected
+ * carefully to avoid timer periods to drift against the source clock.
+ * Use odp_timer_periodic_capability() to check base frequency support,
+ * and resulting max_multiplier and resolution values.
+ *
+ * Fraction part of the value is always less than one, see
+ * odp_timer_periodic_capability_t::base_freq_hz for details. The default value
+ * is zero.
+ *
+ * An example with two timer frequencies:
+ * base_freq_hz.integer = 33333, .numer = 1, .denom = 3
+ * max_multiplier = 30
+ * timer A: freq_multiplier = 2
+ * timer frequency: 2 * 33333 1/3 Hz = 66.6666..kHz
+ * timer B: freq_multiplier = 30
+ * timer frequency: 30 * 33333 1/3 Hz = 1 MHz
+ */
+ odp_fract_u64_t base_freq_hz;
+
+ /** Maximum base frequency multiplier
+ *
+ * This is the maximum base frequency multiplier value
+ * (odp_timer_periodic_start_t::freq_multiplier) for any timer in the pool.
+ */
+ uint64_t max_multiplier;
+
+ } periodic;
+
+ /** Number of timers in the pool. */
+ uint32_t num_timers;
+
+ /** Thread private timer pool. When zero, multiple thread may use the
+ * timer pool concurrently. When non-zero, only single thread uses the
+ * timer pool (concurrently). The default value is zero. */
+ int priv;
+
+} odp_timer_pool_param_t;
+
+/**
+ * Timer tick type
+ */
+typedef enum {
+ /** Relative ticks
+ *
+ * Timer tick value is relative to the current time (odp_timer_current_tick())
+ * of the timer pool.
+ */
+ ODP_TIMER_TICK_REL = 0,
+
+ /** Absolute ticks
+ *
+ * Timer tick value is absolute timer pool ticks.
+ */
+ ODP_TIMER_TICK_ABS
+
+} odp_timer_tick_type_t;
+
+/**
+ * Timer start parameters
+ */
+typedef struct odp_timer_start_t {
+ /** Tick type
+ *
+ * Defines if expiration time ticks are absolute or relative.
+ */
+ odp_timer_tick_type_t tick_type;
+
+ /** Expiration time in ticks
+ *
+ * New expiration time for the timer to be started/restarted. When 'tick_type' is
+ * ODP_TIMER_TICK_REL, expiration time is odp_timer_current_tick() + 'tick'.*/
+ uint64_t tick;
+
+ /** Timeout event
+ *
+ * When the timer expires, this event is enqueued to the destination queue of the timer.
+ * The event type can be ODP_EVENT_BUFFER, ODP_EVENT_PACKET or ODP_EVENT_TIMEOUT. It is
+ * recommended to use ODP_EVENT_TIMEOUT type events. Those (odp_timeout_t) carry also
+ * timeout specific metadata.
+ *
+ * This field is ignored by odp_timer_restart() calls.
+ */
+ odp_event_t tmo_ev;
+
+} odp_timer_start_t;
+
+/**
+ * Periodic timer start parameters
+ *
+ * A periodic timer pool can be thought as a wall clock, where the base frequency
+ * (odp_timer_pool_param_t::base_freq_hz) defines how many times per second a pointer travels
+ * around the clock face. When a timer (see "Timer A" in the figure) is started with the base
+ * frequency multiplier (odp_timer_periodic_start_t::freq_multiplier) of one, a single reference
+ * to it is placed into the clock face. When a timer (see "Timer B") is started with the multiplier
+ * value of two, two references to it is placed into opposite sides of the clock face, etc. When the
+ * pointer reaches a timer reference, the timer expires and a timeout event is sent to the
+ * destination queue. The maximum base frequency multiplier (odp_timer_pool_param_t::max_multiplier)
+ * defines the maximum number of references a timer can have on the clock face. The first
+ * expiration time parameter (odp_timer_periodic_start_t::first_tick) is used to tune timer
+ * reference placement on the clock face against the current time (the current pointer location).
+ *
+ * @code{.unparsed}
+ *
+ * Periodic timer pool
+ *
+ * o
+ * o o <--- Timer B
+ *
+ * o \ o
+ * \
+ * Timer B ---> o \ o <--- Timer A
+ * o
+ *
+ *
+ * Timer pool: max_multiplier = 8
+ * Timer A: freq_multiplier = 1
+ * Timer B: freq_multiplier = 2
+ *
+ * @endcode
+ *
+ */
+typedef struct odp_timer_periodic_start_t {
+ /** First expiration time
+ *
+ * The first expiration time in absolute timer ticks. When zero, the first expiration time
+ * is one period after the current time, or as close to that as the implementation can
+ * achieve. After the first expiration, timer expiration continues with the defined
+ * frequency. The tick value must be less than one timer period after the current time.
+ */
+ uint64_t first_tick;
+
+ /** Base frequency multiplier
+ *
+ * Periodic timer expiration frequency is defined as a multiple of the timer pool
+ * base frequency: timer frequency (Hz) = base_freq_hz * freq_multiplier. Valid values
+ * range from 1 to timer pool parameter 'max_multiplier'.
+ *
+ * Depending on the implementation, a multiplier value that is a divisor of
+ * 'max_multiplier' may improve timer expiration accuracy:
+ * max_multiplier = k * freq_multiplier, where k is an integer.
+ */
+ uint64_t freq_multiplier;
+
+ /** Timeout event
+ *
+ * This event is enqueued to the destination queue when the timer expires. The event type
+ * must be ODP_EVENT_TIMEOUT.
+ */
+ odp_event_t tmo_ev;
+
+} odp_timer_periodic_start_t;
+
+/**
+ * Return values for timer start, restart and cancel calls
+ */
+typedef enum {
+ /**
+ * Timer operation succeeded
+ *
+ * Timer start, restart and cancel operations may return this value.
+ */
+ ODP_TIMER_SUCCESS = 0,
+
+ /**
+ * Timer operation failed, too near to the current time
+ *
+ * The operation failed because the requested time/timer has expired already, or is too
+ * near to the current time. Timer start, restart and cancel operations may return this
+ * value.
+ */
+ ODP_TIMER_TOO_NEAR = -1,
+
+ /**
+ * Timer operation failed, too far from the current time
+ *
+ * The operation failed because the requested time is too far from the current time.
+ * Only timer start and restart operations may return this value.
+ */
+ ODP_TIMER_TOO_FAR = -2,
+
+ /**
+ * Timer operation failed
+ *
+ * The operation failed due to some other reason than timing of the request. Timer start,
+ * restart and cancel operations may return this value.
+ */
+ ODP_TIMER_FAIL = -3
+
+} odp_timer_retval_t;
+
+/**
+ * For backwards compatibility, odp_timer_set_t is synonym of odp_timer_retval_t
+ *
+ * @deprecated Use odp_timer_retval_t instead.
+ */
+typedef odp_timer_retval_t ODP_DEPRECATE(odp_timer_set_t);
+
+/**
+ * Timer tick information
+ */
+typedef struct odp_timer_tick_info_t {
+ /**
+ * Timer tick frequency in hertz
+ *
+ * Timer tick frequency expressed as a fractional number. The integer part contains
+ * full hertz. The fraction part (numerator / denominator) contains parts of
+ * a hertz to be added with the integer.
+ *
+ * For example, a timer tick frequency of 333 333 and 1/3 Hz could be presented with
+ * these values: integer = 333 333, numer = 1, denom = 3. Implementation may choose numer
+ * and denom values freely.
+ */
+ odp_fract_u64_t freq;
+
+ /**
+ * One timer tick in nanoseconds
+ *
+ * Nanoseconds per tick is expressed as a fractional number. The integer part contains
+ * full nanoseconds. The fraction part (numerator / denominator) contains parts of
+ * a nanosecond to be added with the integer.
+ *
+ * For example, a timer tick period of 3.125 nanoseconds (320MHz) could be presented with
+ * these values: integer = 3, numer = 125 000 000, denom = 1 000 000 000. Implementation
+ * may choose numer and denom values freely.
+ */
+ odp_fract_u64_t nsec;
+
+ /**
+ * One timer tick in source clock cycles
+ *
+ * The clock cycle count is expressed as a fractional number. The integer part contains
+ * full clock cycles. The fraction part (numerator / denominator) contains parts of
+ * a clock cycle to be added with the integer.
+ *
+ * For example, a timer tick period of 42 and 1/3 source clock cycles could be presented
+ * with these values: integer = 42, numer = 1, denom = 3. Implementation may choose numer
+ * and denom values freely.
+ *
+ * The value is zero, when there is no direct connection between tick and the source
+ * clock signal.
+ */
+ odp_fract_u64_t clk_cycle;
+
+} odp_timer_tick_info_t;
+
+/**
+ * ODP timer pool information and configuration
+ */
+typedef struct {
+ /** Parameters specified at creation */
+ odp_timer_pool_param_t param;
+
+ /** Number of currently allocated timers */
+ uint32_t cur_timers;
+
+ /** High watermark of allocated timers */
+ uint32_t hwm_timers;
+
+ /** Name of timer pool */
+ const char *name;
+
+ /** Timer pool tick information */
+ odp_timer_tick_info_t tick_info;
+
+} odp_timer_pool_info_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <odp/visibility_end.h>
+#endif
diff --git a/include/odp/api/spec/traffic_mngr.h b/include/odp/api/spec/traffic_mngr.h
index 3a748cef7..117ed22cd 100644
--- a/include/odp/api/spec/traffic_mngr.h
+++ b/include/odp/api/spec/traffic_mngr.h
@@ -1,19 +1,19 @@
-/** Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ * Copyright (c) 2022 Marvell
*/
-#ifndef ODP_TRAFFIC_MNGR_H_
-#define ODP_TRAFFIC_MNGR_H_
+#ifndef ODP_API_SPEC_TRAFFIC_MNGR_H_
+#define ODP_API_SPEC_TRAFFIC_MNGR_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
extern "C" {
#endif
+#include <odp/api/packet_io_types.h>
#include <odp/api/std_types.h>
-#include <odp/api/packet_io.h>
/**
* @file
@@ -23,7 +23,7 @@ extern "C" {
/** @defgroup odp_traffic_mngr ODP TRAFFIC MNGR
* @{
*
- * An API for configuring and using Traffic Management systems
+ * Traffic management on packet output.
*
* This file forms a simple interface for creating, configuring and using
* Traffic Management (TM) subsystems. By TM subsystem it is meant a general
@@ -37,7 +37,7 @@ extern "C" {
* based systems or one or more hybrid systems - where because of
* hardware constraints some of the packet scheduling is done in hardware
* and some is done in software. In addition, there may also be additional
- * API's beyond those described here for (a) controlling advanced capabilities
+ * APIs beyond those described here for (a) controlling advanced capabilities
* supported by specific hardware, software or hybrid subsystems or (b)
* dealing with constraints and limitations of specific implementations.
*/
@@ -75,7 +75,7 @@ extern "C" {
/**
* @def ODP_TM_MAX_TM_QUEUES
- * The largest number of tm_queues that can handled by any one TM system.
+ * The largest number of tm_queues that can be handled by any one TM system.
*/
/**
@@ -96,23 +96,6 @@ extern "C" {
*/
/**
- * @def ODP_TM_MIN_SHAPER_BW
- * The largest amount of bandwidth that any shaper's peak or commit rate can
- * be set to. It is in units of 1000 bytes/second.
- */
-
-/**
- * @def ODP_TM_MAX_SHAPER_BW
- * The largest amount of bandwidth that any shaper's peak or commit rate can
- * be set to. It is in units of 1000 bytes/second.
- */
-
-/**
- * @def ODP_NUM_SHAPER_COLORS
- * The number of enumeration values defined in the odp_tm_shaper_color_t type.
- */
-
-/**
* @def ODP_TM_INVALID_PRIORITY
* Used to indicate an invalid priority value.
*/
@@ -143,7 +126,7 @@ extern "C" {
/**
* @typedef odp_tm_node_t
- * Each odp_tm_queue_t value is an opaque ODP handle representing a specific
+ * Each odp_tm_node_t value is an opaque ODP handle representing a specific
* tm node within a specific TM system.
*/
@@ -189,6 +172,63 @@ extern "C" {
* tree/hierarchy of nodes.
*/
+/**
+ * TM queue specific statistics counters
+ */
+typedef struct odp_tm_queue_stats_t {
+ /** Number of octets in successfully transmitted packets. In case of
+ * Ethernet, packet size includes MAC header. */
+ uint64_t octets;
+
+ /** Number of successfully transmitted packets. */
+ uint64_t packets;
+
+ /** Number of packets discarded due to other reasons (e.g. aging) than
+ * errors. */
+ uint64_t discards;
+
+ /** Number of octets in packets discarded due to other reasons (e.g.
+ * aging) than errors. */
+ uint64_t discard_octets;
+
+ /** Number of packets with transmission errors. */
+ uint64_t errors;
+
+} odp_tm_queue_stats_t;
+
+/**
+ * TM queue level statistics capabilities
+ */
+typedef struct odp_tm_queue_stats_capability_t {
+ /** Supported counters */
+ union {
+ /** Statistics counters in a bit field structure */
+ struct {
+ /** See odp_tm_queue_stats_t::octets */
+ uint64_t octets : 1;
+
+ /** See odp_tm_queue_stats_t::packets */
+ uint64_t packets : 1;
+
+ /** See odp_tm_queue_stats_t::discards */
+ uint64_t discards : 1;
+
+ /** See odp_tm_queue_stats_t::discard_octets */
+ uint64_t discard_octets : 1;
+
+ /** See odp_tm_queue_stats_t::errors */
+ uint64_t errors : 1;
+
+ } counter;
+
+ /** All bits of the bit field structure
+ *
+ * This field can be used to set/clear all flags, or
+ * for bitwise operations over the entire structure. */
+ uint64_t all_counters;
+ };
+} odp_tm_queue_stats_capability_t;
+
/** Per Level Capabilities
*
* The odp_tm_level_capabilities_t record is used to describe the capabilities
@@ -213,17 +253,71 @@ typedef struct {
/** min_weight only has significance when the weights_supported field
* below is true, in which case it specifies the smallest value
* of the weights allowed at this level. */
- uint8_t min_weight;
+ uint32_t min_weight;
/** max_weight only has significance when the weights_supported field
* below is true, in which case it specifies the largest value
* of the weights allowed at this level. */
- uint8_t max_weight;
-
- /** tm_node_shaper_supported indicates that the tm_nodes at this level
- * all support TM shaping, */
+ uint32_t max_weight;
+
+ /** Minimum allowed value for odp_tm_shaper_params_t::commit_burst and
+ * odp_tm_shaper_params_t::peak_burst when
+ * odp_tm_shaper_params_t::packet_mode is true.
+ */
+ uint32_t min_burst_packets;
+
+ /** Maximum allowed value for odp_tm_shaper_params_t::commit_burst and
+ * odp_tm_shaper_params_t::peak_burst when
+ * odp_tm_shaper_params_t::packet_mode is true.
+ */
+ uint32_t max_burst_packets;
+
+ /** Minimum allowed value for odp_tm_shaper_params_t::commit_rate and
+ * odp_tm_shaper_params_t::peak_rate when
+ * odp_tm_shaper_params_t::packet_mode is true.
+ */
+ uint64_t min_rate_packets;
+
+ /** Maximum allowed value for odp_tm_shaper_params_t::commit_rate and
+ * odp_tm_shaper_params_t::peak_rate when
+ * odp_tm_shaper_params_t::packet_mode is true.
+ */
+ uint64_t max_rate_packets;
+
+ /** Minimum allowed value for odp_tm_shaper_params_t::commit_burst and
+ * odp_tm_shaper_params_t::peak_burst when
+ * odp_tm_shaper_params_t::packet_mode is false.
+ */
+ uint32_t min_burst;
+
+ /** Maximum allowed value for odp_tm_shaper_params_t::commit_burst and
+ * odp_tm_shaper_params_t::peak_burst when
+ * odp_tm_shaper_params_t::packet_mode is false.
+ */
+ uint32_t max_burst;
+
+ /** Minimum allowed value for odp_tm_shaper_params_t::commit_rate and
+ * odp_tm_shaper_params_t::peak_rate when
+ * odp_tm_shaper_params_t::packet_mode is false.
+ */
+ uint64_t min_rate;
+
+ /** Maximum allowed value for odp_tm_shaper_params_t::commit_rate and
+ * odp_tm_shaper_params_t::peak_rate when
+ * odp_tm_shaper_params_t::packet_mode is false.
+ */
+ uint64_t max_rate;
+
+ /** Shaper is supported in rate shape mode */
odp_bool_t tm_node_shaper_supported;
+ /** Shaper is supported in rate limit mode */
+ odp_bool_t tm_node_rate_limiter_supported;
+
+ /** tm_node_shaper_packet_mode indicates that tm_nodes at this level
+ * support shaper in packet mode */
+ odp_bool_t tm_node_shaper_packet_mode;
+
/** tm_node_wred_supported indicates that the tm_nodes at this level
* support some form of Random Early Detection. */
odp_bool_t tm_node_wred_supported;
@@ -244,8 +338,49 @@ typedef struct {
* When true the min_weight and max_weight fields above specify
* the legal range of such weights. */
odp_bool_t weights_supported;
+
+ /** TM node threshold profile support */
+ struct {
+ /** Threshold given as bytes */
+ uint8_t byte : 1;
+
+ /** Threshold given as packets */
+ uint8_t packet : 1;
+
+ /** Threshold given as bytes and packets simultaneously */
+ uint8_t byte_and_packet : 1;
+
+ } tm_node_threshold;
+
} odp_tm_level_capabilities_t;
+/** The tm_pkt_prio_mode_t enumeration type is used to indicate different
+ * modes a tm system supports with respect to assigning priority to a packet
+ * and propagating it across TM system. All the nodes in a TM system can
+ * function only on single mode specified at time of odp_tm_create().
+ */
+typedef enum odp_tm_pkt_prio_mode {
+ /** Indicates Packet priority preserve mode. In this mode, a packet gets
+ * its priority based on a TM queue it gets enqueued to and then it
+ * carries the same priority along with it as long as it is in the TM
+ * system. At every TM node in the topology, that specific pkt is
+ * scheduled as per that priority.
+ */
+ ODP_TM_PKT_PRIO_MODE_PRESERVE,
+
+ /** Indicates Packet priority overwrite mode. In this mode, a packet
+ * gets a new priority every time it passes through a TM queue or a
+ * TM node. All the packets fed by a fan-in node will get the same
+ * priority and that will be valid until overwritten again by another TM
+ * node. This priority is part of the TM fan-in node parameters and is
+ * fixed at node creation time.
+ */
+ ODP_TM_PKT_PRIO_MODE_OVERWRITE,
+
+ /** Max enum of Packet priority mode */
+ ODP_TM_PKT_PRIO_MODE_MAX,
+} odp_tm_pkt_prio_mode_t;
+
/** TM Capabilities Record.
*
* The odp_tm_capabilities_t record type is used to describe the feature set
@@ -275,16 +410,16 @@ typedef struct {
*/
odp_bool_t egress_fcn_supported;
- /** tm_queue_shaper_supported indicates that the tm_queues support
- * proper TM shaping. Note that TM Shaping is NOT the same thing as
- * Ingress Metering/Policing as specified by RFC 2697 (A Single Rate
- * Three Color Marker) or RFC 2698 (A Two Rate Three Color Marker).
- * These RFC's can be used for a Diffserv traffic conditioner, or
- * other ingress policing. They make no mention of and have no
- * algorithms for delaying packets - which is what TM shapers are
- * expected to do. */
+ /** Shaper is supported in rate shape mode */
odp_bool_t tm_queue_shaper_supported;
+ /** Shaper is supported in rate limit mode */
+ odp_bool_t tm_queue_rate_limiter_supported;
+
+ /** tm_queue_shaper_packet_mode indicates that tm_queues support
+ * shaper in packet mode */
+ odp_bool_t tm_queue_shaper_packet_mode;
+
/** tm_queue_wred_supported indicates that the tm_queues support some
* form of Random Early Detection. */
odp_bool_t tm_queue_wred_supported;
@@ -331,13 +466,105 @@ typedef struct {
/** The per_level array specifies the TM system capabilities that
* can vary based upon the tm_node level. */
odp_tm_level_capabilities_t per_level[ODP_TM_MAX_LEVELS];
+
+ /** dynamic_topology_update indicates support for TM system dynamic
+ * topology update. A dynamic topology update is defined as update to
+ * a TM system topology while TM system is not in stopped state.
+ * When TRUE, application can update topology dynamically
+ * without bringing the TM system to stopped state. When FALSE,
+ * application has to call odp_tm_stop() before updating the
+ * topology and odp_tm_start() after completing the update.
+ */
+ odp_bool_t dynamic_topology_update;
+
+ /** dynamic_shaper_update indicates support for TM system's dynamic
+ * shaper profile changes. When TRUE, application can update shaper
+ * profile of a TM queue or TM node dynamically.
+ * When FALSE, it implies that TM system should be brought to
+ * stopped state before changing the shaper profile or updating
+ * the parameters of the shaper profile of any TM node or TM queue.
+ */
+ odp_bool_t dynamic_shaper_update;
+
+ /** dynamic_sched_update indicates support for TM system's dynamic
+ * sched profile changes. When TRUE, application can update sched
+ * profile of a TM queue or TM node dynamically.
+ * When FALSE, it implies that TM system should be brought to
+ * stopped state before changing the sched profile or updating
+ * the parameters of the sched profile of any TM node or TM queue.
+ */
+ odp_bool_t dynamic_sched_update;
+
+ /** dynamic_wred_update indicates support for TM system's dynamic
+ * wred profile changes. When TRUE, application can update wred
+ * profile of a TM queue or TM node dynamically.
+ * When FALSE, it implies that TM system should be brought to
+ * stopped state before changing the wred profile or updating
+ * the parameters of the wred profile of any TM node or TM queue.
+ */
+ odp_bool_t dynamic_wred_update;
+
+ /** dynamic_threshold_update indicates support for TM system's dynamic
+ * threshold profile changes. When TRUE, application can update
+ * threshold profile of a TM queue or TM node dynamically.
+ * When FALSE, it implies that TM system should be brought to
+ * stopped state before changing the threshold profile or updating
+ * the parameters of the threshold profile of any TM node or TM queue.
+ */
+ odp_bool_t dynamic_threshold_update;
+
+ /** TM queue statistics counter capabilities */
+ odp_tm_queue_stats_capability_t queue_stats;
+
+ /** TM queue threshold profile support */
+ struct {
+ /** Threshold given as bytes */
+ uint8_t byte : 1;
+
+ /** Threshold given as packets */
+ uint8_t packet : 1;
+
+ /** Threshold given as bytes and packets simultaneously */
+ uint8_t byte_and_packet : 1;
+
+ } tm_queue_threshold;
+
+ /** tm_queue_query_flags indicates supported types of TM queue query.
+ * Types of TM queue query are same as query_flags that are passed to
+ * odp_tm_queue_query(), odp_tm_priority_query() and
+ * odp_tm_total_query(). When zero, none of the queue query API's are
+ * supported. When non-zero, only the only supported types of passed
+ * query_flags are taken into account and corresponding fields updated.
+ *
+ * @see ODP_TM_QUERY_PKT_CNT, ODP_TM_QUERY_BYTE_CNT,
+ * ODP_TM_QUERY_THRESHOLDS.
+ */
+ uint32_t tm_queue_query_flags;
+
+ /** Indicates the packet priority modes supported by TM systems on a
+ * platform. A platform can support multiple packet priority modes. The
+ * actual mode a TM system runs with is defined by
+ * odp_tm_requirements_t.
+ */
+ odp_bool_t pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_MAX];
+
+ /** Maximum number of schedulers supported by a TM node at any level.
+ * A TM node contains a WFQ/WRR scheduler for each packet priority level
+ * for which the node has more than one possible input. TM topology and
+ * priority configuration must be made so that the resulting number of
+ * WFQ/WRR schedulers does not exceed this capability in any TM node.
+ *
+ * The value can vary between 0 and ODP_TM_MAX_PRIORITIES.
+ */
+ uint8_t max_schedulers_per_node;
} odp_tm_capabilities_t;
/** Per Level Requirements
*
* The odp_tm_level_requirements_t record is used to describe the requirements
* that might vary based upon the tm_node level. It is always used as
- * part of the odp_tm_requirements record. */
+ * part of the odp_tm_requirements record. The default value of all boolean
+ * fields is false. */
typedef struct {
/** max_num_tm_nodes specifies the maximum number of tm_nodes required
* at this level. */
@@ -357,12 +584,12 @@ typedef struct {
/** min_weight only has significance when the weights_supported field
* below is true, in which case it specifies the smallest value
* of the weights that will be used at this level. */
- uint8_t min_weight;
+ uint32_t min_weight;
/** max_weight only has significance when the weights_supported field
* below is true, in which case it specifies the largest value
* of the weights that will be used at this level. */
- uint8_t max_weight;
+ uint32_t max_weight;
/** tm_node_shaper_needed indicates that the tm_nodes at this level
* are expected to do TM shaping, */
@@ -383,17 +610,24 @@ typedef struct {
* disciplines. */
odp_bool_t fair_queuing_needed;
- /** weights_needd indicates that the tm_node schedulers at this
+ /** weights_needed indicates that the tm_node schedulers at this
* level are expected have different weights for their different
* fanins. When true the min_weight and max_weight fields above
* specify the used range of such weights. */
odp_bool_t weights_needed;
+
+ /** tm_node_threshold_needed indicates that the tm_nodes at this
+ * level may use threshold profile support */
+ odp_bool_t tm_node_threshold_needed;
} odp_tm_level_requirements_t;
/** TM Requirements Record.
*
* The odp_tm_requirements_t record type is used to describe the minimum
- * set of features and limits to be actually used by the application. */
+ * set of features and limits to be actually used by the application.
+ *
+ * The default value of all boolean fields is false.
+ **/
typedef struct {
/** max_tm_queues specifies the maximum number of tm_queues that will
* be used for this TM System. */
@@ -401,7 +635,8 @@ typedef struct {
/** num_levels specifies that number of levels of hierarchical
* scheduling that will be used. This is a count of the tm_node
- * stages and does not include tm_queues or tm_egress objects. */
+ * stages and does not include tm_queues or tm_egress objects.
+ * The default value is 0. */
uint8_t num_levels;
/** tm_queue_shaper_needed indicates that the tm_queues are expected
@@ -417,6 +652,10 @@ typedef struct {
* ignored if tm_queue_wred_needed above is false. */
odp_bool_t tm_queue_dual_slope_needed;
+ /** tm_queue_threshold_needed indicates that the tm_queues are
+ * expected to use threshold profile support */
+ odp_bool_t tm_queue_threshold_needed;
+
/** vlan_marking_needed indicates that the ODP application expects
* to use some form of VLAN egress marking using the
* odp_tm_vlan_marking() function. See also comments for
@@ -440,9 +679,15 @@ typedef struct {
* the application will not enable this color for vlan marking,
* ecn marking nor drop precedence marking. A value of TRUE means that
* the application expects to use this color in conjunction with one or
- * more of the marking API's. */
+ * more of the marking APIs. */
odp_bool_t marking_colors_needed[ODP_NUM_PACKET_COLORS];
+ /** Packet priority mode.
+ * TM capabilities indicate which modes are supported.
+ * The default value is ODP_TM_PKT_PRIO_MODE_PRESERVE.
+ */
+ odp_tm_pkt_prio_mode_t pkt_prio_mode;
+
/** The per_level array specifies the TM system requirements that
* can vary based upon the tm_node level. */
odp_tm_level_requirements_t per_level[ODP_TM_MAX_LEVELS];
@@ -478,14 +723,13 @@ typedef struct {
};
} odp_tm_egress_t;
-/** Initialize Requirements record.
+/** Initialize Requirements record fields to their default values.
*
* odp_tm_requirements_init() must be called to initialize any
* odp_tm_requirements_t record before it is first used or assigned to.
- * This is done to allow for vendor specific additions to this record.
*
- * @param[in] requirements A pointer to an odp_tm_requirements_t record which
- * is to be initialized.
+ * @param requirements A pointer to an odp_tm_requirements_t record which
+ * is to be initialized.
*/
void odp_tm_requirements_init(odp_tm_requirements_t *requirements);
@@ -493,54 +737,48 @@ void odp_tm_requirements_init(odp_tm_requirements_t *requirements);
*
* odp_tm_egress_init() must be called to initialize any odp_tm_egress_t
* record before it is first used or assigned to.
- * This is done to allow for vendor specific additions to this record.
*
- * @param[in] egress A pointer to an odp_tm_egress_t record which
- * is to be initialized.
+ * @param egress A pointer to an odp_tm_egress_t record which
+ * is to be initialized.
*/
void odp_tm_egress_init(odp_tm_egress_t *egress);
-/** Query All TM Capabilities
- *
- * The odp_tm_capabilities() function can be used to obtain the complete set of
- * TM limits supported by this implementation. The reason that this returns
- * a SET of capabilities and not just one, is because it is expected that
- * many HW based implementations may have one set of limits for the HW and
- * also support a SW TM implementation with a (presumably larger) different
- * set of limits. There are also cases where there could be more than
- * SW implementation (one supporting say tens of thousands of tm_queues and
- * a variant supporting tens of millions of tm_queues).
- * The caller passes in an array of odp_tm_capabilities_t records and the
- * number of such records. Then the first N of these records will be filled
- * in by the implementation and the number N will be returned. In the event
- * that N is larger than the capabilities_size, N will still be returned,
- * but only capabilities_size records will be filled in.
- *
- * @param[out] capabilities An array of odp_tm_capabilities_t records to
- * be filled in.
- * @param[in] capabilities_size The number of odp_tm_capabilities_t records
- * in the capabilities array.
- * @return Returns < 0 upon failure. Returns N > 0,
- * where N is the maximum number of different
- * odp_tm_capabilities_t records that the
- * implementations supports. *NOTE* that this
- * number can be > capabilities_size!
- */
-int odp_tm_capabilities(odp_tm_capabilities_t capabilities[],
- uint32_t capabilities_size);
+/** Query TM Capabilities specific to an egress
+ *
+ * The function returns the set of TM limits supported by this implementation
+ * for a given egress. Unlike odp_tm_capability() which return's capabilities
+ * of already created TM system which are limited by its requirements, this
+ * function returns maximum TM system limits.
+ *
+ * Lack of TM support in the given egress does not cause this
+ * function to return a failure. Lack of TM support is indicated
+ * by zero max_tm_queues capability.
+ *
+ * If the pktio of an egress of the pktio kind has not been opened
+ * in the ODP_PKTOUT_MODE_TM pktout mode, the capabilities will
+ * indicate that TM is not supported.
+ *
+ * @param[out] capabilities odp_tm_capabilities_t record to be filled in.
+ * @param egress Only capabilities compatible with this egress
+ * are returned.
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_tm_egress_capabilities(odp_tm_capabilities_t *capabilities,
+ const odp_tm_egress_t *egress);
/** Create/instantiate a TM Packet Scheduling system.
*
- * @param[in] name The name to be assigned to this TM system. Cannot
- * be NULL, and also must be unique amongst all other
- * TM system names.
- * @param[in] requirements The minimum required feature set and limits needed
- * by the ODP application.
- * @param[in] egress Describes the single egress "spigot" of this
- * TM system.
- * @return Returns ODP_TM_INVALID upon failure, otherwise the
- * newly created TM system's odp_tm_t handle is
- * returned.
+ * @param name The name to be assigned to this TM system. Cannot
+ * be NULL, and also must be unique amongst all other
+ * TM system names.
+ * @param requirements The minimum required feature set and limits needed
+ * by the ODP application.
+ * @param egress Describes the single egress "spigot" of this
+ * TM system.
+ * @return Returns ODP_TM_INVALID upon failure, otherwise the
+ * newly created TM system's odp_tm_t handle is
+ * returned.
*/
odp_tm_t odp_tm_create(const char *name,
odp_tm_requirements_t *requirements,
@@ -558,21 +796,21 @@ odp_tm_t odp_tm_create(const char *name,
* the existing (built-in or created by odp_tm_create) TM system that best
* matches the requirements is returned.
*
- * @param[in] name If NULL then only uses the requirements parameter to
- * find a closest match, otherwise if the name is
- * matched by an existing TM system it is returned.
- * @param[in] requirements Used when the name is NULL (in which case the
- * closest match is returned) or when the name is
- * not-NULL, but doesn't match any existing TM system
- * in which case the requirements is used to find the
- * FIRST TM system matching exactly these limits.
- * @param[in] egress If a TM system is found, then this specifies the
- * egress "spigot" to be associated with this TM
- * system.
- * @return If an existing TM system (built-in or previously
- * created via odp_tm_create) is found, its
- * odp_tm_t value is returned, otherwise
- * ODP_TM_INVALID is returned.
+ * @param name If NULL then only uses the requirements parameter to
+ * find a closest match, otherwise if the name is
+ * matched by an existing TM system it is returned.
+ * @param requirements Used when the name is NULL (in which case the
+ * closest match is returned) or when the name is
+ * not-NULL, but doesn't match any existing TM system
+ * in which case the requirements is used to find the
+ * FIRST TM system matching exactly these limits.
+ * @param egress If a TM system is found, then this specifies the
+ * egress "spigot" to be associated with this TM
+ * system.
+ * @return If an existing TM system (built-in or previously
+ * created via odp_tm_create) is found, its
+ * odp_tm_t value is returned, otherwise
+ * ODP_TM_INVALID is returned.
*/
odp_tm_t odp_tm_find(const char *name,
odp_tm_requirements_t *requirements,
@@ -591,8 +829,7 @@ odp_tm_t odp_tm_find(const char *name,
* In addition, ODP TM implementations should fail API requests that "exceed"
* the limits or features contracted for in the requirements.
*
- * @param[in] odp_tm The odp_tm_t value of the TM system to be
- * queried.
+ * @param tm TM handle
* @param[out] capabilities A pointer to an odp_tm_capabilities_t record
* where the actual limits used by the TM system are
* copied into. Note that these limits do NOT
@@ -600,10 +837,53 @@ odp_tm_t odp_tm_find(const char *name,
* a TM system was created by odp_tm_create,
* but of course these limits in some cases could
* be larger.
+ *
* @return Returns 0 upon success, < 0 upon failure (which
* indicates that the odp_tm value did not exist).
*/
-int odp_tm_capability(odp_tm_t odp_tm, odp_tm_capabilities_t *capabilities);
+int odp_tm_capability(odp_tm_t tm, odp_tm_capabilities_t *capabilities);
+
+/**
+ * Start a TM system
+ *
+ * odp_tm_start() needs to be used to start an already created or found TM
+ * system. By default, all the TM systems are in stopped state.
+ *
+ * @param tm TM handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_tm_start(odp_tm_t tm);
+
+/**
+ * Stop a TM system
+ *
+ * odp_tm_stop() can to used to stop a TM system that is already started for the
+ * purpose of reconfiguration that cannot be done dynamically.
+ *
+ * When TM is in the stopped state,
+ * - New packets must not be sent to the TM either directly by TM API or indirectly
+ * via IPsec outbound inline API. New packets can only be enqueued after
+ * starting the TM system using odp_tm_start().
+ * - Packets already inflight inside TM or IPSec for transmit may get silently dropped
+ * or may get transmitted with unspecified TM treatment.
+ *
+ * A following call to odp_tm_start() restarts TM system and its scheduling/shaping
+ * on existing and new packets.
+ *
+ * @param tm TM handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ *
+ * @see odp_tm_capabilities_t::dynamic_topology_update
+ * @see odp_tm_capabilities_t::dynamic_shaper_update
+ * @see odp_tm_capabilities_t::dynamic_sched_update
+ * @see odp_tm_capabilities_t::dynamic_wred_update
+ * @see odp_tm_capabilities_t::dynamic_threshold_update
+ */
+int odp_tm_stop(odp_tm_t tm);
/** Destroy a TM system.
*
@@ -619,36 +899,38 @@ int odp_tm_capability(odp_tm_t odp_tm, odp_tm_capabilities_t *capabilities);
* TM system, other than EVENTUALLY these packets will be either sent (in ANY
* order) or freed.
*
- * @param[in] odp_tm The odp_tm_t value of the TM system to be destroyed (and
- * hence destroyed (and hence freed).
- * @return 0 upon success, < 0 upon failure.
+ * @param tm The handle of the TM system to be destroyed (and hence freed).
+ *
+ * @return 0 upon success, < 0 upon failure.
*/
-int odp_tm_destroy(odp_tm_t odp_tm);
+int odp_tm_destroy(odp_tm_t tm);
-/** Marking APIs */
+/* Marking APIs
+ * -------------------------------------------------------- */
/** Vlan Marking.
*
* The odp_tm_vlan_marking() function allows one to configure the TM egress
* so as to have it set the one bit VLAN Drop Eligibility Indicator (DEI)
* field (but only for pkts that already carry a VLAN tag) of a pkt based upon
- * the final pkt (or shaper?) color assigned to the pkt when it reaches the
- * egress node. When drop_eligible_enabled is false, then the given color has
- * no effect on the VLAN fields. See IEEE 802.1q for more details.
+ * the final pkt color assigned to the pkt when it reaches the egress node. When
+ * drop_eligible_enabled is false, then the given color has no effect on the
+ * VLAN fields. See IEEE 802.1q for more details.
*
* Note that ALL ODP implementations are required to SUCCESSFULLY handle all
* calls to this function with drop_eligible_enabled == FALSE - i.e. must
* always return 0 when disabling this feature.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system
- * whose egress behavior is being changed.
- * @param[in] color The packet color whose egress marking is
- * being changed.
- * @param[in] drop_eligible_enabled If true then will set the DEI bit for
- * egressed VLAN tagged pkts with this color.
- * @return 0 upon success, < 0 upon failure.
+ * @param tm Handle of the TM system whose egress behavior
+ * is being changed.
+ * @param color The packet color whose egress marking is
+ * being changed.
+ * @param drop_eligible_enabled If true then will set the DEI bit for
+ * egressed VLAN tagged pkts with this color.
+ *
+ * @return 0 upon success, < 0 upon failure.
*/
-int odp_tm_vlan_marking(odp_tm_t odp_tm,
+int odp_tm_vlan_marking(odp_tm_t tm,
odp_packet_color_t color,
odp_bool_t drop_eligible_enabled);
@@ -669,18 +951,19 @@ int odp_tm_vlan_marking(odp_tm_t odp_tm,
* calls to this function with ecn_ce_enabled == FALSE - i.e. must always
* return 0 when disabling this feature.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system whose
- * egress behavior is being changed.
- * @param[in] color The packet color whose egress marking is
- * being changed.
- * @param[in] ecn_ce_enabled If true then egressed IPv4/IPv6 pkts whose
- * protocol field is TCP AND whose ECN subfield has
- * either one of the two values 1 or 2, will set this
- * subfield to the value ECN_CE - i.e. Congestion
- * Experienced (whose value is 3).
- * @return 0 upon success, < 0 upon failure.
- */
-int odp_tm_ecn_marking(odp_tm_t odp_tm,
+ * @param tm Handle of the TM system whose egress behavior is being
+ * changed.
+ * @param color The packet color whose egress marking is
+ * being changed.
+ * @param ecn_ce_enabled If true then egressed IPv4/IPv6 pkts whose
+ * protocol field is TCP AND whose ECN subfield has
+ * either one of the two values 1 or 2, will set this
+ * subfield to the value ECN_CE - i.e. Congestion
+ * Experienced (whose value is 3).
+ *
+ * @return 0 upon success, < 0 upon failure.
+ */
+int odp_tm_ecn_marking(odp_tm_t tm,
odp_packet_color_t color,
odp_bool_t ecn_ce_enabled);
@@ -709,58 +992,94 @@ int odp_tm_ecn_marking(odp_tm_t odp_tm,
* calls to this function with drop_prec_enabled == FALSE - i.e. must always
* return 0 when disabling this feature.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system whose
- * egress behavior is being changed.
- * @param[in] color The packet color whose egress marking is
- * being changed.
- * @param[in] drop_prec_enabled If true then egressed IPv4/IPv6 pkts with this
- * color will have the pkt's Drop Precedence
- * sub-subfield of the DSCP subfield set to
- * LOW, MEDIUM or HIGH drop precedence.
- * @return 0 upon success, < 0 upon failure.
- */
-int odp_tm_drop_prec_marking(odp_tm_t odp_tm,
+ * @param tm Handle of the TM system whose egress behavior is
+ * being changed.
+ * @param color The packet color whose egress marking is
+ * being changed.
+ * @param drop_prec_enabled If true then egressed IPv4/IPv6 pkts with this
+ * color will have the pkt's Drop Precedence
+ * sub-subfield of the DSCP subfield set to
+ * LOW, MEDIUM or HIGH drop precedence.
+ *
+ * @return 0 upon success, < 0 upon failure.
+ */
+int odp_tm_drop_prec_marking(odp_tm_t tm,
odp_packet_color_t color,
odp_bool_t drop_prec_enabled);
-/** Shaper profile types and functions */
+/* Shaper profile types and functions
+ * -------------------------------------------------------- */
-/** Possible values of running the shaper algorithm. ODP_TM_SHAPER_GREEN
- * means that the traffic is within the commit specification (rate and burst
- * size), ODP_TM_SHAPER_YELLOW means that the traffic is within the peak
- * specification (rate and burst size) and ODP_TM_SHAPER_RED means that the
- * traffic is exceeding both its commit and peak specifications. Note that
- * packets can also have an assigned <b> packet color</b> of ODP_PACKET_GREEN,
- * ODP_PACKET_YELLOW or ODP_PACKET_RED which has a different meaning and
- * purpose than the shaper colors.
- */
+/** Mode selection between rate shaping and rate limiting */
typedef enum {
- ODP_TM_SHAPER_GREEN, ODP_TM_SHAPER_YELLOW, ODP_TM_SHAPER_RED
-} odp_tm_shaper_color_t;
+ /** Rate shape traffic to the specified burst and rate by delaying
+ * packets.
+ *
+ * The shaper does not drop packets in normal operation, but since
+ * it delays packets, it can cause queues to fill up and cause queue
+ * management to drop packets.
+ */
+ ODP_TM_SHAPER_RATE_SHAPE,
+
+ /** Rate limit traffic to the specified burst and rate by dropping
+ * excess packets.
+ *
+ * It is implementation dependent when exactly the limiter state
+ * update and packet drop happens. For example, they may occur
+ * immediately when packets are available from the source or when
+ * the downstream node and scheduler are accepting new packets from
+ * this node/queue. It is possible that in some cases a delayed
+ * packet drop causes queues to fill up.
+ */
+ ODP_TM_SHAPER_RATE_LIMIT
+
+} odp_tm_shaper_mode_t;
-/** The odp_tm_shaper_params_t record type is used to supply the parameters
- * associated with a shaper profile. Since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_shaper_params_init() be
- * called on variables of this type before any of the fields are filled in.
+/**
+ * TM shaper parameters
+ *
+ * Use odp_tm_shaper_params_init() to initialize parameters into their default
+ * values.
*/
typedef struct {
+ /** Shaper mode. The default value is ODP_TM_SHAPER_RATE_SHAPE.
+ *
+ * A shaper profile must not be used in a TM queue or TM node if
+ * the queue/node does not support shaper or if it does not support
+ * the shaper mode set in the profile.
+ *
+ * @see odp_tm_capabilities_t::tm_queue_shaper_supported
+ * @see odp_tm_capabilities_t::tm_queue_rate_limiter_supported
+ * @see odp_tm_level_capabilities_t::tm_node_shaper_supported
+ * @see odp_tm_level_capabilities_t::tm_node_rate_limiter_supported
+ */
+ odp_tm_shaper_mode_t mode;
+
/** The committed information rate for this shaper profile. The units
- * for this integer are always in bits per second. */
- uint64_t commit_bps;
+ * for this integer is in bits per second when packet_mode is
+ * not TRUE while packets per second when packet mode is TRUE.
+ */
+ uint64_t commit_rate;
/** The peak information rate for this shaper profile. The units for
- * this integer are always in bits per second. */
- uint64_t peak_bps;
+ * this integer is in bits per second when packet_mode is
+ * not TRUE while in packets per second when packet mode is TRUE.
+ * This field is ignored when dual_rate is FALSE.
+ */
+ uint64_t peak_rate;
/** The commit burst tolerance for this shaper profile. The units for
- * this field are always bits. This value sets an upper limit for the
+ * this field is bits when packet_mode is not TRUE and packets when
+ * packet_mode is TRUE. This value sets an upper limit for the
* size of the commitCnt. */
uint32_t commit_burst;
/** The peak burst tolerance for this shaper profile. The units for
- * this field are always bits. This value sets an upper limit for the
- * size of the peakCnt. */
+ * this field in bits when packet_mode is not TRUE and packets
+ * when packet_mode is TRUE. This value sets an upper limit for the
+ * size of the peakCnt.
+ * This field is ignored when dual_rate is FALSE.
+ */
uint32_t peak_burst;
/** The shaper_len_adjust is a value between -128 and 127 which is
@@ -771,22 +1090,36 @@ typedef struct {
* to a value approximating the "time" (in units of bytes) taken by
* the Ethernet preamble and Inter Frame Gap. Traditionally this
* would be the value 20 (8 + 12), but in same cases can be as low as
- * 9 (4 + 5). */
+ * 9 (4 + 5).
+ * This field is ignored when packet_mode is TRUE.
+ *
+ * The default value is 0. */
int8_t shaper_len_adjust;
/** If dual_rate is TRUE it indicates the desire for the
* implementation to use dual rate shaping for packets associated with
* this profile. The precise semantics of dual rate shaping are
* implementation specific, but in any case require a non-zero set of
- * both commit and peak parameters. */
+ * both commit and peak parameters.
+ *
+ * The default value is false. */
odp_bool_t dual_rate;
+
+ /** If packet_mode is TRUE it indicates that shaper should work
+ * in packet mode ignoring lengths of packet and hence shaping
+ * traffic in packet's per second as opposed to bits per second.
+ *
+ * The default value is false. */
+ odp_bool_t packet_mode;
+
} odp_tm_shaper_params_t;
-/** odp_tm_shaper_params_init() must be called to initialize any
- * odp_tm_shaper_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM shaper parameters
+ *
+ * Initialize an odp_tm_shaper_params_t to its default values for all fields.
*
- * @param[in] params A pointer to an odp_tm_shaper_params_t record which
- * is to be initialized.
+ * @param params Address of the odp_tm_shaper_params_t to be initialized
*/
void odp_tm_shaper_params_init(odp_tm_shaper_params_t *params);
@@ -794,17 +1127,17 @@ void odp_tm_shaper_params_init(odp_tm_shaper_params_t *params);
* subsequently be attached to any number (including zero) of tm_queues
* or tm_nodes.
*
- * @param[in] name Optional name associated with this shaper profile. Can
- * be NULL. If non-NULL must be unique amongst the set of
- * all other shaper profiles.
- * @param[in] params The profile parameters. See comments associated with
- * the odp_tm_shaper_params_t for more details.
- * @return Returns ODP_TM_INVALID upon failure, or the newly
- * allocated odp_tm_shaper_t value representing this
- * profile object.
+ * @param name Optional name associated with this shaper profile. Can
+ * be NULL. If non-NULL must be unique amongst the set of
+ * all other shaper profiles.
+ * @param params The profile parameters. See comments associated with
+ * the odp_tm_shaper_params_t for more details.
+ * @return Returns ODP_TM_INVALID upon failure, or the newly
+ * allocated odp_tm_shaper_t value representing this
+ * profile object.
*/
odp_tm_shaper_t odp_tm_shaper_create(const char *name,
- odp_tm_shaper_params_t *params);
+ const odp_tm_shaper_params_t *params);
/** Destroy shaper profile object
*
@@ -812,9 +1145,9 @@ odp_tm_shaper_t odp_tm_shaper_create(const char *name,
* profile object. It is an error if this shaper profile is still being
* referenced by an active (connected) tm_node.
*
- * @param[in] shaper_profile Specifies the shaper profile object which is
- * being destroyed.
- * @return Returns < 0 upon failure or 0 upon success.
+ * @param shaper_profile Specifies the shaper profile object which is
+ * being destroyed.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_shaper_destroy(odp_tm_shaper_t shaper_profile);
@@ -822,7 +1155,7 @@ int odp_tm_shaper_destroy(odp_tm_shaper_t shaper_profile);
* with the specified shaper profile object, and copies them into the supplied
* record.
*
- * @param[in] shaper_profile Specifies the shaper profile object whose
+ * @param shaper_profile Specifies the shaper profile object whose
* values are to be read.
* @param[out] params A pointer to an odp_tm_shaper_params_t record
* where the current shaper profile object values
@@ -834,30 +1167,31 @@ int odp_tm_shaper_params_read(odp_tm_shaper_t shaper_profile,
/** odp_tm_shaper_params_update() "sets" the current set of values associated
* with the specified shaper profile object. In addition, this call has the
- * effect that all tm_input's and tm_nodes that are associated (attached?)
- * with this shaper profile object will be updated with the new values.
- *
- * @param[in] shaper_profile Specifies the shaper profile object whose
- * values are to be set.
- * @param[in] params A pointer to an odp_tm_shaper_params_t record
- * where the new shaper profile object values
- * are taken from.
- * @return Returns < 0 upon failure or 0 upon success.
+ * effect that all tm_input's and tm_nodes that are associated with this shaper
+ * profile object will be updated with the new values.
+ *
+ * @param shaper_profile Specifies the shaper profile object whose
+ * values are to be set.
+ * @param params A pointer to an odp_tm_shaper_params_t record
+ * where the new shaper profile object values
+ * are taken from.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_shaper_params_update(odp_tm_shaper_t shaper_profile,
- odp_tm_shaper_params_t *params);
+ const odp_tm_shaper_params_t *params);
/** odp_tm_shaper_lookup() can be used to find the shaper profile object
* created with the specified name.
*
- * @param[in] name Name of a previously created shaper profile. Cannot be
- * NULL.
- * @return Returns ODP_TM_INVALID upon failure, or the shaper
- * profile handle created with this name.
+ * @param name Name of a previously created shaper profile. Cannot be NULL.
+ *
+ * @return Returns ODP_TM_INVALID upon failure, or the shaper
+ * profile handle created with this name.
*/
odp_tm_shaper_t odp_tm_shaper_lookup(const char *name);
-/** Scheduler Profiles - types and functions */
+/* Scheduler Profiles - types and functions
+ * -------------------------------------------------------- */
/** The odp_tm_sched_mode_t type is used to control whether a tm_node
* scheduler takes into account packet lengths (by setting the sched_mode to
@@ -872,49 +1206,51 @@ typedef enum {
ODP_TM_FRAME_BASED_WEIGHTS /**< Ignore the packet length */
} odp_tm_sched_mode_t;
-/** The odp_tm_sched_params_t record type is used to supply the parameters
- * associated with a scheduler profile. Since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_sched_params_init() be
- * called on variables of this type before any of the fields are filled in.
+/**
+ * TM scheduler parameters
+ *
+ * Use odp_tm_sched_params_init() to initialize parameters into their default
+ * values.
*/
typedef struct {
/** sched_modes indicates whether weighted scheduling should be used
- * or not - on a priority basis. */
+ * or not - on a priority basis.
+ * The default value is ODP_TM_BYTE_BASED_WEIGHTS for all priorities */
odp_tm_sched_mode_t sched_modes[ODP_TM_MAX_PRIORITIES];
/** In the case that sched_modes for a given strict priority level
* indicates the use of weighted scheduling, this field supplies the
* weighting factors. The weights - when defined - are used such that
- * the (adjusted) frame lengths are divided by these 8-bit weights
+ * the (adjusted) frame lengths are divided by these weights
* (i.e. they are divisors and not multipliers). Consequently a
* weight of 0 (when sched_mode is ODP_TM_BYTE_BASED_WEIGHTS) is
* illegal. */
- uint8_t sched_weights[ODP_TM_MAX_PRIORITIES];
+ uint32_t sched_weights[ODP_TM_MAX_PRIORITIES];
} odp_tm_sched_params_t;
-/** odp_tm_sched_params_init() must be called to initialize any
- * odp_tm_sched_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM scheduler parameters
+ *
+ * Initialize an odp_tm_sched_params_t to its default values for all fields.
*
- * @param[in] params A pointer to an odp_tm_sched_params_t record which
- * is to be initialized.
+ * @param params Address of the odp_tm_sched_params_t to be initialized
*/
void odp_tm_sched_params_init(odp_tm_sched_params_t *params);
/** odp_tm_sched_create() creates a scheduler profile object, which can
* subsequently be attached to any number (including zero) of tm_nodes.
*
- * @param[in] name Optional name associated with this scheduler profile.
- * Can be NULL. If non-NULL must be unique amongst the
- * set of all other scheduler profiles.
- * @param[in] params The profile parameters. See comments associated with
- * the odp_tm_sched_params_t for more details.
- * @return Returns ODP_TM_INVALID upon failure, or the newly
- * allocated odp_tm_sched_t value representing this profile
- * object.
+ * @param name Optional name associated with this scheduler profile.
+ * Can be NULL. If non-NULL must be unique amongst the
+ * set of all other scheduler profiles.
+ * @param params The profile parameters. See comments associated with
+ * the odp_tm_sched_params_t for more details.
+ * @return Returns ODP_TM_INVALID upon failure, or the newly
+ * allocated odp_tm_sched_t value representing this profile
+ * object.
*/
odp_tm_sched_t odp_tm_sched_create(const char *name,
- odp_tm_sched_params_t *params);
+ const odp_tm_sched_params_t *params);
/** Destroy scheduler profile object
*
@@ -922,9 +1258,9 @@ odp_tm_sched_t odp_tm_sched_create(const char *name,
* profile object. It is an error if this scheduler profile is still being
* referenced by an active (connected) tm_node.
*
- * @param[in] sched_profile Specifies the shaper profile object which is
- * being destroyed.
- * @return Returns < 0 upon failure or 0 upon success.
+ * @param sched_profile Specifies the shaper profile object which is
+ * being destroyed.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_sched_destroy(odp_tm_sched_t sched_profile);
@@ -932,7 +1268,7 @@ int odp_tm_sched_destroy(odp_tm_sched_t sched_profile);
* with the specified scheduler profile object, and copies them into the
* supplied record.
*
- * @param[in] sched_profile Specifies the scheduler profile whose values
+ * @param sched_profile Specifies the scheduler profile whose values
* are to be read.
* @param[out] params A pointer to an odp_tm_sched_params_t record
* where the current scheduler profile object
@@ -944,49 +1280,55 @@ int odp_tm_sched_params_read(odp_tm_sched_t sched_profile,
/** odp_tm_sched_params_update() "sets" the current set of values associated
* with the specified scheduler profile object. In addition, this call has
- * the effect that all tm_nodes that are associated (attached?) with this
- * Scheduler profile object will be updated with the new values.
- *
- * @param[in] sched_profile Specifies the Scheduler profile object whose
- * values are to be set.
- * @param[in] params A pointer to an odp_tm_sched_params_t record
- * where the new scheduler profile object values
- * are taken from.
- * @return Returns < 0 upon failure or 0 upon success.
+ * the effect that all tm_nodes that are associated with this scheduler profile
+ * object will be updated with the new values.
+ *
+ * @param sched_profile Specifies the Scheduler profile object whose
+ * values are to be set.
+ * @param params A pointer to an odp_tm_sched_params_t record
+ * where the new scheduler profile object values
+ * are taken from.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_sched_params_update(odp_tm_sched_t sched_profile,
- odp_tm_sched_params_t *params);
+ const odp_tm_sched_params_t *params);
/** odp_tm_sched_lookup() can be used to find the scheduler profile object
* created with the specified name.
*
- * @param[in] name Name of a previously created scheduler profile. Cannot be
- * NULL.
- * @return Returns ODP_TM_INVALID upon failure, or the scheduler
- * profile handle created with this name.
+ * @param name Name of a previously created scheduler profile. Cannot be NULL.
+ *
+ * @return Returns ODP_TM_INVALID upon failure, or the scheduler
+ * profile handle created with this name.
*/
odp_tm_sched_t odp_tm_sched_lookup(const char *name);
-/** Queue Threshold Profiles - types and functions */
+/* Queue Threshold Profiles - types and functions
+ * -------------------------------------------------------- */
-/** The odp_tm_threshold_params_t record type is used to supply the parameters
- * associated with a queue thresholds profile. Since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_threshold_params_init() be
- * called on variables of this type before any of the fields are filled in
+/**
+ * TM threshold parameters
+ *
+ * Use odp_tm_threshold_params_init() to initialize parameters into their
+ * default values.
*/
typedef struct {
uint64_t max_pkts; /**< max pkt cnt for this threshold profile */
uint64_t max_bytes; /**< max byte cnt for this threshold profile */
- odp_bool_t enable_max_pkts; /**< TRUE if max_pkts is valid */
- odp_bool_t enable_max_bytes; /**< TRUE if max_bytes is valid */
+
+ /** TRUE if max_pkts is valid. The default value is false. */
+ odp_bool_t enable_max_pkts;
+
+ /** TRUE if max_bytes is valid. The default value is false. */
+ odp_bool_t enable_max_bytes;
} odp_tm_threshold_params_t;
-/** odp_tm_threshold_params_init() must be called to initialize any
- * odp_tm_threshold_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM threshold parameters
*
- * @param[in] params A pointer to an odp_tm_threshold_params_t record which
- * is to be initialized.
+ * Initialize an odp_tm_threshold_params_t to its default values for all fields.
+ *
+ * @param params Address of the odp_tm_threshold_params_t to be initialized
*/
void odp_tm_threshold_params_init(odp_tm_threshold_params_t *params);
@@ -994,17 +1336,18 @@ void odp_tm_threshold_params_init(odp_tm_threshold_params_t *params);
* can subsequently be attached to any number (including zero) of tm_queues or
* tm_nodes.
*
- * @param[in] name Optional name associated with this queue threshold
- * profile. Can be NULL. If non-NULL must be unique
- * amongst the set of all other queue threshold profiles.
- * @param[in] params The profile parameters. See comments associated with
- * the odp_tm_threshold_params_t for more details.
- * @return Returns ODP_TM_INVALID upon failure, or the newly
- * allocated odp_tm_threshold_t value representing this
- * profile object.
+ * @param name Optional name associated with this queue threshold
+ * profile. Can be NULL. If non-NULL must be unique
+ * amongst the set of all other queue threshold profiles.
+ * @param params The profile parameters. See comments associated with
+ * the odp_tm_threshold_params_t for more details.
+ * @return Returns ODP_TM_INVALID upon failure, or the newly
+ * allocated odp_tm_threshold_t value representing this
+ * profile object.
*/
odp_tm_threshold_t odp_tm_threshold_create(const char *name,
- odp_tm_threshold_params_t *params);
+ const odp_tm_threshold_params_t
+ *params);
/** Destroy a queue threshold profile object
*
@@ -1012,9 +1355,9 @@ odp_tm_threshold_t odp_tm_threshold_create(const char *name,
* profile object. It is an error if this threshold profile is still being
* referenced by an active (connected) tm_queue or tm_node.
*
- * @param[in] threshold_profile Specifies the queue thresholds profile
- * object which is being destroyed.
- * @return Returns < 0 upon failure or 0 upon success.
+ * @param threshold_profile Specifies the queue thresholds profile
+ * object which is being destroyed.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_threshold_destroy(odp_tm_threshold_t threshold_profile);
@@ -1022,7 +1365,7 @@ int odp_tm_threshold_destroy(odp_tm_threshold_t threshold_profile);
* with the specified queue thresholds profile object, and copies them into the
* supplied record.
*
- * @param[in] threshold_profile Specifies the queue thresholds profile
+ * @param threshold_profile Specifies the queue thresholds profile
* object whose values are to be read.
* @param[out] params A pointer to an odp_tm_threshold_params_t
* record where the current queue thresholds
@@ -1035,36 +1378,37 @@ int odp_tm_thresholds_params_read(odp_tm_threshold_t threshold_profile,
/** odp_tm_thresholds_params_update() "sets" the current set of values
* associated with the specified queue thresholds profile object. In addition,
* this call has the effect that all tm_input's and tm_nodes that are
- * associated (attached?) with this queue thresholds profile object will be
- * updated with the new values.
- *
- * @param[in] threshold_profile Specifies the queue thresholds profile
- * object whose values are to be set.
- * @param[in] params A pointer to an odp_tm_threshold_params_t
- * record where the current queue thresholds
- * profile object values are taken from.
- * @return Returns < 0 upon failure or 0 upon success.
+ * associated with this queue thresholds profile object will be updated with the
+ * new values.
+ *
+ * @param threshold_profile Specifies the queue thresholds profile
+ * object whose values are to be set.
+ * @param params A pointer to an odp_tm_threshold_params_t
+ * record where the current queue thresholds
+ * profile object values are taken from.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_thresholds_params_update(odp_tm_threshold_t threshold_profile,
- odp_tm_threshold_params_t *params);
+ const odp_tm_threshold_params_t *params);
/** odp_tm_thresholds_lookup() can be used to find the queue thresholds
* profile object created with the specified name.
*
- * @param[in] name Name of a previously created queue thresholds profile.
- * Cannot be NULL.
- * @return Returns ODP_TM_INVALID upon failure, or the queue
- * thresholds profile handle created with this name.
+ * @param name Name of a previously created queue thresholds profile.
+ * Cannot be NULL.
+ * @return Returns ODP_TM_INVALID upon failure, or the queue
+ * thresholds profile handle created with this name.
*/
odp_tm_threshold_t odp_tm_thresholds_lookup(const char *name);
-/** WRED Profiles - types and functions */
+/* WRED Profiles - types and functions
+ * -------------------------------------------------------- */
-/** The odp_tm_wred_params_t record type is used to supply the parameters
- * associated with a Random Early Detection profile. Since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_wred_params_init() be called
- * on variables of this type before any of the fields are filled in.
+/**
+ * TM WRED parameters
+ *
+ * Use odp_tm_wred_params_init() to initialize parameters into their default
+ * values.
*/
typedef struct {
/** When min_threshold is set to zero then single-slope WRED is
@@ -1106,7 +1450,7 @@ typedef struct {
/** When enable_wred is false, all tm_queues and tm_nodes that are
* attached to this profile will not take part in a Random Early
- * Detection algorithm. */
+ * Detection algorithm. The default value is false. */
odp_bool_t enable_wred;
/** When use_byte_fullness is true then WRED will use queue memory
@@ -1114,15 +1458,16 @@ typedef struct {
* is false, WRED will use the queue length (i.e. the number of
* packets in the queue) as the fullness criterion. Often will be set
* to true for WRED profiles applied to tm_queues and set to false for
- * WRED profiles applied to tm_nodes. */
+ * WRED profiles applied to tm_nodes. The default value is false. */
odp_bool_t use_byte_fullness;
} odp_tm_wred_params_t;
-/** odp_tm_wred_params_init() must be called to initialize any
- * odp_tm_wred_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM WRED parameters
+ *
+ * Initialize an odp_tm_wred_params_t to its default values for all fields.
*
- * @param[in] params A pointer to an odp_tm_wred_params_t record which
- * is to be initialized.
+ * @param params Address of the odp_tm_wred_params_t to be initialized
*/
void odp_tm_wred_params_init(odp_tm_wred_params_t *params);
@@ -1130,17 +1475,17 @@ void odp_tm_wred_params_init(odp_tm_wred_params_t *params);
* profile object, which can subsequently be attached to any number (including
* zero) of tm_queues or tm_nodes.
*
- * @param[in] name Optional name associated with this WRED profile. Can
- * be NULL. If non-NULL must be unique amongst the set of
- * all other WRED profiles.
- * @param[in] params The profile parameters. See comments associated with the
- * odp_tm_wred_params_t for more details.
- * @return Returns ODP_TM_INVALID upon failure, or the newly
- * allocated odp_tm_wred_t value representing this profile
- * object.
+ * @param name Optional name associated with this WRED profile. Can
+ * be NULL. If non-NULL must be unique amongst the set of
+ * all other WRED profiles.
+ * @param params The profile parameters. See comments associated with the
+ * odp_tm_wred_params_t for more details.
+ * @return Returns ODP_TM_INVALID upon failure, or the newly
+ * allocated odp_tm_wred_t value representing this profile
+ * object.
*/
odp_tm_wred_t odp_tm_wred_create(const char *name,
- odp_tm_wred_params_t *params);
+ const odp_tm_wred_params_t *params);
/** Destroy WRED profile object
*
@@ -1148,9 +1493,9 @@ odp_tm_wred_t odp_tm_wred_create(const char *name,
* profile object. It is an error if this profile object is still being
* referenced by an active (connected) tm_node.
*
- * @param[in] wred_profile Specifies the WRED profile object which is
- * being destroyed.
- * @return Returns < 0 upon failure or 0 upon success.
+ * @param wred_profile Specifies the WRED profile object which is
+ * being destroyed.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_wred_destroy(odp_tm_wred_t wred_profile);
@@ -1158,7 +1503,7 @@ int odp_tm_wred_destroy(odp_tm_wred_t wred_profile);
* with the specified WRED profile object, and copies them into the supplied
* record.
*
- * @param[in] wred_profile Specifies the WRED profile object whose
+ * @param wred_profile Specifies the WRED profile object whose
* values are to be read.
* @param[out] params A pointer to an odp_tm_wred_params_t record
* where the current WRED profile object values
@@ -1170,35 +1515,34 @@ int odp_tm_wred_params_read(odp_tm_wred_t wred_profile,
/** odp_tm_wred_params_update() "sets" the current set of values associated
* with the specified WRED profile object. In addition, this call has the
- * effect that all tm_input's and tm_nodes that are associated (attached?)
- * with this WRED profile object will be updated with the new values.
+ * effect that all tm_input's and tm_nodes that are associated with this WRED
+ * profile object will be updated with the new values.
*
- * @param[in] wred_profile Specifies the WRED profile object whose
- * values are to be set.
- * @param[in] params A pointer to an odp_tm_wred_params_t record
- * where the new WRED profile object values
- * are taken from.
- * @return Returns < 0 upon failure or 0 upon success.
+ * @param wred_profile Specifies the WRED profile object whose
+ * values are to be set.
+ * @param params A pointer to an odp_tm_wred_params_t record
+ * where the new WRED profile object values
+ * are taken from.
+ * @return Returns < 0 upon failure or 0 upon success.
*/
int odp_tm_wred_params_update(odp_tm_wred_t wred_profile,
- odp_tm_wred_params_t *params);
+ const odp_tm_wred_params_t *params);
/** odp_tm_wred_lookup() can be used to find the WRED profile object created
* with the specified name.
*
- * @param[in] name Name of a previously created WRED profile. Cannot be
- * NULL.
- * @return Returns ODP_TM_INVALID upon failure, or the WRED
- * profile handle created with this name.
+ * @param name Name of a previously created WRED profile. Cannot be NULL.
+ *
+ * @return Returns ODP_TM_INVALID upon failure, or the WRED
+ * profile handle created with this name.
*/
odp_tm_wred_t odp_tm_wred_lookup(const char *name);
-/** The odp_tm_node_params_t record type is used to hold extra parameters when
- * calling the odp_tm_node_create() function. Many of these fields are
- * optional EXCEPT for max_fanin and level. Also since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_node_params_init() be called
- * on variables of this type before any of the fields are filled in.
+/**
+ * TM node parameters
+ *
+ * Many of these fields are optional EXCEPT for max_fanin and level. Use
+ * odp_tm_node_params_init() to initialize parameters into their default values.
*/
typedef struct {
/** The user_context field is an generic pointer that the user can
@@ -1213,17 +1557,19 @@ typedef struct {
/** The shaper profile to be associated with this tm_node. Can be
* ODP_TM_INVALID and can also be set and changed post-creation via
- * odp_tm_node_shaper_config(); */
+ * odp_tm_node_shaper_config(); The default value is ODP_TM_INVALID. */
odp_tm_shaper_t shaper_profile;
/** The threshold profile to be used in setting the max queue fullness
- * for WRED and/or tail drop? Can be ODP_TM_INVALID and can also be
- * set and changed post-creation via odp_tm_node_threshold_config(). */
+ * for WRED and/or tail drop. Can be ODP_TM_INVALID and can also be set
+ * and changed post-creation via odp_tm_node_threshold_config(). The
+ * default value is ODP_TM_INVALID. */
odp_tm_threshold_t threshold_profile;
/** The WRED profile(s) to be associated with this tm_node. Any or
* all array elements can be ODP_TM_INVALID and can also be set and
- * changed post-creation via odp_tm_node_wred_config(). */
+ * changed post-creation via odp_tm_node_wred_config().
+ * The default value is ODP_TM_INVALID for every color. */
odp_tm_wred_t wred_profile[ODP_NUM_PACKET_COLORS];
/** The level (or tm_node stage) sets the level for this tm_node It
@@ -1232,13 +1578,30 @@ typedef struct {
* greater levels may be connected to the fan-in of tm_node's with
* numerically smaller levels. */
uint8_t level;
+
+ /** New strict priority level assigned to packets going through this
+ * node when packet priority mode is ODP_TM_PKT_PRIO_MODE_OVERWRITE.
+ * In other packet priority modes this field is ignored. The new
+ * priority does not affect packet processing in this node but in
+ * its destination node.
+ *
+ * The value must be in the range 0..ODP_TM_MAX_PRIORITIES-1.
+ * Additionally, the total number of possible priorities seen by
+ * the destination node must not exceed the max priority configured
+ * for the destination node.
+ *
+ * @see odp_tm_pkt_prio_mode_t
+ * @see odp_tm_level_requirements_t::max_priority
+ */
+ uint8_t priority;
} odp_tm_node_params_t;
-/** odp_tm_node_params_init() must be called to initialize any
- * odp_tm_node_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM node parameters
+ *
+ * Initialize an odp_tm_node_params_t to its default values for all fields.
*
- * @param[in] params A pointer to an odp_tm_node_params_t record which
- * is to be initialized.
+ * @param params Address of the odp_tm_node_params_t to be initialized
*/
void odp_tm_node_params_init(odp_tm_node_params_t *params);
@@ -1249,40 +1612,39 @@ void odp_tm_node_params_init(odp_tm_node_params_t *params);
* strict priority levels for an tm_node cannot be changed after tm_node
* creation. The level parameter MUST be in the range 0..max_level - 1.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system into which this
- * odp_tm_node object is created.
- * @param[in] name Optional name that can be used later later to find this
- * same odp_tm_node_t. Can be NULL, otherwise must be
- * unique across all odp_tm_node objects.
- * @param[in] params A pointer to a record holding (an extensible) set of
- * properties/attributes of this tm_node.
- * @return Returns ODP_TM_INVALID upon failure, otherwise returns
- * a valid odp_tm_node_t handle if successful.
+ * @param tm Handle of the TM system into which this odp_tm_node object is
+ * created.
+ * @param name Optional name that can be used later later to find this
+ * same odp_tm_node_t. Can be NULL, otherwise must be
+ * unique across all odp_tm_node objects.
+ * @param params TM node parameters.
+ *
+ * @return Returns ODP_TM_INVALID upon failure, otherwise returns
+ * a valid odp_tm_node_t handle if successful.
*/
-odp_tm_node_t odp_tm_node_create(odp_tm_t odp_tm,
- const char *name,
- odp_tm_node_params_t *params);
+odp_tm_node_t odp_tm_node_create(odp_tm_t tm, const char *name,
+ const odp_tm_node_params_t *params);
/** Destroy a tm_node object.
*
* The odp_tm_node_destroy frees the resources used by a tm_node_t object.
* The tm_node to be destroyed MUST not have any parent or child entities.
*
- * @param[in] tm_node Specifies the tm_node to be destroyed (freed).
- * @return Returns -1 upon failure, 0 upon success.
+ * @param tm_node Specifies the tm_node to be destroyed (freed).
+ * @return Returns -1 upon failure, 0 upon success.
*/
int odp_tm_node_destroy(odp_tm_node_t tm_node);
/** The odp_tm_node_shaper_config() function is used to dynamically set or
* change the shaper profile associated with this tm_node.
*
- * @param[in] tm_node Specifies the tm_node to be changed.
- * @param[in] shaper_profile Specifies the shaper profile that should
- * now be used for the shaper entity within the
- * given tm_node. Note that it is legal to specify
- * ODP_TM_INVALID indicating that this tm_node
- * no longer implements a shaper function.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_node Specifies the tm_node to be changed.
+ * @param shaper_profile Specifies the shaper profile that should
+ * now be used for the shaper entity within the
+ * given tm_node. Note that it is legal to specify
+ * ODP_TM_INVALID indicating that this tm_node
+ * no longer implements a shaper function.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_node_shaper_config(odp_tm_node_t tm_node,
odp_tm_shaper_t shaper_profile);
@@ -1290,15 +1652,15 @@ int odp_tm_node_shaper_config(odp_tm_node_t tm_node,
/** The odp_tm_node_sched_config() function is used to dynamically set or
* change the scheduler profile associated with a tm_node.
*
- * @param[in] tm_node Specifies the tm_node to be changed.
- * @param[in] tm_fan_in_node Specifies which of the specified tm_node's
- * fan-in's weights etc are to be changed. The
- * fan-in is identified by the "producer"/parent
- * tm_node actually connected to this fan-in.
- * @param[in] sched_profile Specifies the scheduler profile that should
- * now be used for the WFQ/RR entity within the
- * given tm_node.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_node Specifies the tm_node to be changed.
+ * @param tm_fan_in_node Specifies which of the specified tm_node's
+ * fan-in's weights etc are to be changed. The
+ * fan-in is identified by the "producer"/parent
+ * tm_node actually connected to this fan-in.
+ * @param sched_profile Specifies the scheduler profile that should
+ * now be used for the WFQ/RR entity within the
+ * given tm_node.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_node_sched_config(odp_tm_node_t tm_node,
odp_tm_node_t tm_fan_in_node,
@@ -1307,10 +1669,10 @@ int odp_tm_node_sched_config(odp_tm_node_t tm_node,
/** The odp_tm_node_threshold_config() function is used to dynamically set or
* change the queue threshold profile associated with this tm_node.
*
- * @param[in] tm_node Specifies the tm_node to be changed.
- * @param[in] thresholds_profile Specifies the queue threshold profile that
- * should now be used for the given tm_node.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_node Specifies the tm_node to be changed.
+ * @param thresholds_profile Specifies the queue threshold profile that
+ * should now be used for the given tm_node.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_node_threshold_config(odp_tm_node_t tm_node,
odp_tm_threshold_t thresholds_profile);
@@ -1319,16 +1681,16 @@ int odp_tm_node_threshold_config(odp_tm_node_t tm_node,
* change the WRED profile associated with this tm_node or tm_node/pkt_color
* combination.
*
- * @param[in] tm_node Specifies the tm_node to be changed.
- * @param[in] pkt_color Specifies the pkt_color that this profile is to be
- * used with. Can also be the special value
- * ALL_PKT_COLORS.
- * @param[in] wred_profile Specifies the WRED profile that should now be used
- * by this tm_queue, when processing pkts of this
- * pkt_color. It can be the value ODP_TM_INVALID
- * indicating that this tm_queue/pkt_color combination
- * no longer implements WRED.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_node Specifies the tm_node to be changed.
+ * @param pkt_color Specifies the pkt_color that this profile is to be
+ * used with. Can also be the special value
+ * ALL_PKT_COLORS.
+ * @param wred_profile Specifies the WRED profile that should now be used
+ * by this tm_queue, when processing pkts of this
+ * pkt_color. It can be the value ODP_TM_INVALID
+ * indicating that this tm_queue/pkt_color combination
+ * no longer implements WRED.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_node_wred_config(odp_tm_node_t tm_node,
odp_packet_color_t pkt_color,
@@ -1337,43 +1699,40 @@ int odp_tm_node_wred_config(odp_tm_node_t tm_node,
/** odp_tm_node_lookup() can be used to find the tm_node object created with
* the specified name.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system into which this
- * odp_tm_node object is created.
- * @param[in] name Name of a previously created tm_node. Cannot be
- * NULL.
- * @return Returns ODP_TM_INVALID upon failure, or the tm_node
- * handle created with this name.
+ * @param tm TM handle
+ * @param name Name of a previously created tm_node. Cannot be NULL.
+ *
+ * @return Returns ODP_TM_INVALID upon failure, or the tm_node
+ * handle created with this name.
*/
-odp_tm_node_t odp_tm_node_lookup(odp_tm_t odp_tm, const char *name);
+odp_tm_node_t odp_tm_node_lookup(odp_tm_t tm, const char *name);
/** odp_tm_node_context() can be used to get the user_context value that is
* associated with the given tm_node.
*
- * @param[in] tm_node Specifies the tm_node whose user_context is to be gotten.
- * @return Returns the user_context pointer associated with this
- * tm_node. Returns NULL if the tm_node is not valid OR
- * if the user_context was NLL.
- * handle created with this name.
+ * @param tm_node Specifies the tm_node whose user_context is to be gotten.
+ * @return Returns the user_context pointer associated with this
+ * tm_node. Returns NULL if the tm_node is not valid OR
+ * if the user_context was NULL.
*/
void *odp_tm_node_context(odp_tm_node_t tm_node);
/** odp_tm_node_context_set() can be used to set the user_context value that is
* associated with the given tm_node.
*
- * @param[in] tm_node Specifies the tm_node whose user_context is to be set.
- * @param[in] user_context Generic pointer associated with the given tm_node.
- * Does not have any effect on the tm_node semantics.
- * @return Returns 0 upon success and -1 if the given tm_node
- * is not valid.
+ * @param tm_node Specifies the tm_node whose user_context is to be set.
+ * @param user_context Generic pointer associated with the given tm_node.
+ * Does not have any effect on the tm_node semantics.
+ * @return Returns 0 upon success and -1 if the given tm_node
+ * is not valid.
*/
int odp_tm_node_context_set(odp_tm_node_t tm_node, void *user_context);
-/** The odp_tm_queue_params_t record type is used to hold extra parameters
- * when calling the odp_tm_queue_create() function. Many of these fields are
- * optional EXCEPT for priority. Also since it is expected that
- * implementations might augment this record type with platform specific
- * additional fields - it is required that odp_tm_queue_params_init() be
- * called on variables of this type before any of the fields are filled in.
+/**
+ * TM queue parameters
+ *
+ * Use odp_tm_queue_params_init() to initialize parameters into their default
+ * values.
*/
typedef struct {
/** The user_context field is an generic pointer that the user can
@@ -1383,94 +1742,104 @@ typedef struct {
/** The shaper profile to be associated with this tm_queue. Can be
* ODP_TM_INVALID and can also be set and changed post-creation via
- * odp_tm_queue_shaper_config(). */
+ * odp_tm_queue_shaper_config(). The default value is ODP_TM_INVALID. */
odp_tm_shaper_t shaper_profile;
/** The threshold profile to be used in setting the max queue fullness
- * for WRED and/or tail drop? Can be ODP_TM_INVALID and can also be
- * set and changed post-creation via odp_tm_queue_threshold_config(). */
+ * for WRED and/or tail drop. Can be ODP_TM_INVALID and can also be set
+ * and changed post-creation via odp_tm_queue_threshold_config(). The
+ * default value is ODP_TM_INVALID.
+ *
+ * One can specify the maximum queue limits either as a maximum number
+ * of packets in the queue or as a maximum number of bytes in the queue,
+ * or if both are specified, then whichever limit is hit first. */
odp_tm_threshold_t threshold_profile;
/** The WRED profile(s) to be associated with this tm_queue. Any or
* all array elements can be ODP_TM_INVALID and can also be set and
- * changed post-creation via odp_tm_queue_wred_config(). */
+ * changed post-creation via odp_tm_queue_wred_config().
+ * The default value is ODP_TM_INVALID for every color. */
odp_tm_wred_t wred_profile[ODP_NUM_PACKET_COLORS];
/** The strict priority level assigned to packets in this tm_queue -
* in other words all packets associated with a given tm_queue MUST
* have the same single strict priority level and this level must be
- * in the range 0..max_priority. */
+ * in the range 0..max_priority. The default value is 0. */
uint8_t priority;
+
+ /** Maintain original packet order of the source queue when enqueuing
+ * packets to this queue while holding ordered or atomic queue
+ * synchronization context. Default value of this flag is true.
+ */
+ odp_bool_t ordered_enqueue;
} odp_tm_queue_params_t;
-/** odp_tm_queue_params_init() must be called to initialize any
- * odp_tm_queue_params_t record before it is first used or assigned to.
+/**
+ * Initialize TM queue parameters
+ *
+ * Initialize an odp_tm_queue_params_t to its default values for all fields.
*
- * @param[in] params A pointer to an odp_tm_queue_params_t record which
- * is to be initialized.
+ * @param params Address of the odp_tm_queue_params_t to be initialized
*/
void odp_tm_queue_params_init(odp_tm_queue_params_t *params);
-/** Create an tm_queue object. One can specify the maximum queue limits
- * either as a maximum number of packets in the queue OR as a maximum number
- * of bytes in the queue, or if both are specified, then whichever limit is
- * hit first. Note that in the case of specifying the maximum queue memory
- * size as bytes, the system is free to instead convert this byte value into a
- * number of buffers and instead limit the queue memory usage by buffer counts
- * versus strictly using byte counts.
+/**
+ * TM queue create
+ *
+ * Create a TM queue according to the queue parameters. Use
+ * odp_tm_queue_params_init() to initialize parameters into their default values.
+ *
+ * @param tm Handle of the TM system into which this odp_tm_queue object is
+ * created.
+ * @param params TM queue parameters.
*
- * @param[in] odp_tm Odp_tm is used to identify the TM system into which this
- * odp_tm_queue object is created.
- * @param[in] params A pointer to a record holding (an extensible) set of
- * properties/attributes of this tm_queue.
- * @return Returns ODP_TM_INVALID upon failure, otherwise a valid
- * odp_tm_queue_t handle.
+ * @return Returns ODP_TM_INVALID upon failure, otherwise a valid
+ * odp_tm_queue_t handle.
*/
-odp_tm_queue_t odp_tm_queue_create(odp_tm_t odp_tm,
- odp_tm_queue_params_t *params);
+odp_tm_queue_t odp_tm_queue_create(odp_tm_t tm,
+ const odp_tm_queue_params_t *params);
/** Destroy an tm_queue object. The odp_tm_queue_destroy frees the resources
* used by a tm_queue_t object. The tm_queue to be destroyed MUST not be
* connected in a tm system, and consequently cannot contain any pkts.
*
- * @param[in] tm_queue Specifies the tm_queue to be destroyed (freed).
- * @return Returns -1 upon failure, 0 upon success.
+ * @param tm_queue Specifies the tm_queue to be destroyed (freed).
+ * @return Returns -1 upon failure, 0 upon success.
*/
int odp_tm_queue_destroy(odp_tm_queue_t tm_queue);
/** odp_tm_queue_context() can be used to get the user_context value that is
* associated with the given tm_queue.
*
- * @param[in] tm_queue Specifies the tm_queue whose user_context is to be
- * returned.
- * @return Returns the user_context pointer associated with this
- * tm_queue. Returns NULL if the tm_quue is not valid OR
- * if the user_context was NULL.
+ * @param tm_queue Specifies the tm_queue whose user_context is to be
+ * returned.
+ * @return Returns the user_context pointer associated with this
+ * tm_queue. Returns NULL if the tm_queue is not valid OR
+ * if the user_context was NULL.
*/
void *odp_tm_queue_context(odp_tm_queue_t tm_queue);
/** odp_tm_queue_context_set() can be used to set the user_context value that is
* associated with the given tm_queue.
*
- * @param[in] tm_queue Specifies the tm_queue whose user_context is to be
- * set.
- * @param[in] user_context Generic pointer associated with the given tm_queue.
- * Does not have any effect on the tm_queue semantics.
- * @return Returns 0 upon success and -1 if the given tm_queu
- * is not valid.
+ * @param tm_queue Specifies the tm_queue whose user_context is to be set.
+ * @param user_context Generic pointer associated with the given tm_queue.
+ * Does not have any effect on the tm_queue semantics.
+ * @return Returns 0 upon success and -1 if the given tm_queue
+ * is not valid.
*/
int odp_tm_queue_context_set(odp_tm_queue_t tm_queue, void *user_context);
/** The odp_tm_queue_shaper_config() function is used to dynamically set
* or change the shaper profile associated with this tm_queue.
*
- * @param[in] tm_queue Specifies the tm_queue to be changed.
- * @param[in] shaper_profile Specifies the shaper profile that should now be
- * used for shaping the tm_queue's packet stream.
- * Note that it is legal to specify ODP_TM_INVALID
- * indicating that this tm_queue no longer
- * implements a shaper function.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_queue Specifies the tm_queue to be changed.
+ * @param shaper_profile Specifies the shaper profile that should now be
+ * used for shaping the tm_queue's packet stream.
+ * Note that it is legal to specify ODP_TM_INVALID
+ * indicating that this tm_queue no longer
+ * implements a shaper function.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_queue_shaper_config(odp_tm_queue_t tm_queue,
odp_tm_shaper_t shaper_profile);
@@ -1480,15 +1849,15 @@ int odp_tm_queue_shaper_config(odp_tm_queue_t tm_queue,
* the name, this function affects a tm_node scheduler - specifically the
* scheduler fan-in when such fan-in comes from an tm_queue.
*
- * @param[in] tm_node Specifies the tm_node to be changed.
- * @param[in] tm_fan_in_queue Specifies which of the specified tm_node's
- * fan-in's weights etc are to be changed. The
- * fan-in is identified by the "producer"/parent
- * tm_queue actually connected to this fan-in.
- * @param[in] sched_profile Specifies the scheduler profile that should
- * now be used for the WFQ/RR entity within the
- * given tm_node.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_node Specifies the tm_node to be changed.
+ * @param tm_fan_in_queue Specifies which of the specified tm_node's
+ * fan-in's weights etc are to be changed. The
+ * fan-in is identified by the "producer"/parent
+ * tm_queue actually connected to this fan-in.
+ * @param sched_profile Specifies the scheduler profile that should
+ * now be used for the WFQ/RR entity within the
+ * given tm_node.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_queue_sched_config(odp_tm_node_t tm_node,
odp_tm_queue_t tm_fan_in_queue,
@@ -1497,10 +1866,10 @@ int odp_tm_queue_sched_config(odp_tm_node_t tm_node,
/** The odp_tm_queue_threshold_config() function is used to dynamically set or
* change the queue threshold profile associated with this tm_queue.
*
- * @param[in] tm_queue Specifies the tm_queue to be changed.
- * @param[in] thresholds_profile Specifies the queue threshold profile that
- * should now be used for the given tm_queue.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_queue Specifies the tm_queue to be changed.
+ * @param thresholds_profile Specifies the queue threshold profile that
+ * should now be used for the given tm_queue.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_queue_threshold_config(odp_tm_queue_t tm_queue,
odp_tm_threshold_t thresholds_profile);
@@ -1509,22 +1878,23 @@ int odp_tm_queue_threshold_config(odp_tm_queue_t tm_queue,
* the WRED profile associated with this tm_queue or tm_queue/pkt_color
* combination.
*
- * @param[in] tm_queue Specifies the tm_queue to be changed.
- * @param[in] pkt_color Specifies the pkt_color that this profile is to be
- * used with. Can also be the special value
- * ALL_PKT_COLORS.
- * @param[in] wred_profile Specifies the WRED profile that should now be used
- * by this tm_queue, when processing pkts of this
- * pkt_color. It can be the value ODP_TM_INVALID
- * indicating that this tm_queue/pkt_color combination
- * no longer implements WRED.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_queue Specifies the tm_queue to be changed.
+ * @param pkt_color Specifies the pkt_color that this profile is to be
+ * used with. Can also be the special value
+ * ALL_PKT_COLORS.
+ * @param wred_profile Specifies the WRED profile that should now be used
+ * by this tm_queue, when processing pkts of this
+ * pkt_color. It can be the value ODP_TM_INVALID
+ * indicating that this tm_queue/pkt_color combination
+ * no longer implements WRED.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_queue_wred_config(odp_tm_queue_t tm_queue,
odp_packet_color_t pkt_color,
odp_tm_wred_t wred_profile);
-/** Topology setting functions */
+/* Topology setting functions
+ * -------------------------------------------------------- */
/** Connects two tm_nodes
*
@@ -1532,14 +1902,14 @@ int odp_tm_queue_wred_config(odp_tm_queue_t tm_queue,
* dst_tm_node. Note that an ODP_TM_ROOT handle passed in for the
* dst_tm_node implies connection to the egress/root object of this TM system.
*
- * @param[in] src_tm_node odp_tm_node_t handle of the tm_node whose output is
- * to be connected to the fan-in of the next tm_node
- * as represented by the dst_tm_node.
- * @param[in] dst_tm_node odp_tm_node_t handle of the tm_node object that will
- * receive all of the pkt_descs from the src tm_node
- * output. If ODP_TM_ROOT, then attachment is to
- * the root egress object/spigot.
- * @return 0 upon success, < 0 on failure.
+ * @param src_tm_node odp_tm_node_t handle of the tm_node whose output is
+ * to be connected to the fan-in of the next tm_node
+ * as represented by the dst_tm_node.
+ * @param dst_tm_node odp_tm_node_t handle of the tm_node object that will
+ * receive all of the pkt_descs from the src tm_node
+ * output. If ODP_TM_ROOT, then attachment is to
+ * the root egress object/spigot.
+ * @return 0 upon success, < 0 on failure.
*/
int odp_tm_node_connect(odp_tm_node_t src_tm_node, odp_tm_node_t dst_tm_node);
@@ -1550,11 +1920,10 @@ int odp_tm_node_connect(odp_tm_node_t src_tm_node, odp_tm_node_t dst_tm_node);
* tm_queue to be in the fanin tree (directly or indirectly) of this tm_node.
* Note that it is legal for this tm_node to no fanout connection.
*
- * @param[in] src_tm_node odp_tm_node_t handle of the tm_node whose output is
- * to be disconnected from the fan-in of the next
- * tm_node.
+ * @param src_tm_node odp_tm_node_t handle of the tm_node whose output is
+ * to be disconnected from the fan-in of the next tm_node.
*
- * @return 0 upon success, < 0 on failure.
+ * @return 0 upon success, < 0 on failure.
*/
int odp_tm_node_disconnect(odp_tm_node_t src_tm_node);
@@ -1562,12 +1931,12 @@ int odp_tm_node_disconnect(odp_tm_node_t src_tm_node);
* parent tm_node or to the egress/root node. The tm_queue will then become
* one of the dst node's fan-in set.
*
- * @param[in] tm_queue Specifies the tm_queue.
- * @param[in] dst_tm_node odp_tm_node_t handle of the tm_node object that will
- * receive all of the pkt_descs from the src tm_node
- * output. If ODP_TM_ROOT, then attachment is to
- * the root egress object/spigot.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm_queue Specifies the tm_queue.
+ * @param dst_tm_node odp_tm_node_t handle of the tm_node object that will
+ * receive all of the pkt_descs from the src tm_node
+ * output. If ODP_TM_ROOT, then attachment is to
+ * the root egress object/spigot.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_queue_connect(odp_tm_queue_t tm_queue, odp_tm_node_t dst_tm_node);
@@ -1577,45 +1946,82 @@ int odp_tm_queue_connect(odp_tm_queue_t tm_queue, odp_tm_node_t dst_tm_node);
* tm_queue from its fanout. Note that it is legal for this tm_queue to
* have no fanout connection.
*
- * @param[in] tm_queue Specifies the tm_queue.
- * @return 0 upon success, < 0 on failure.
+ * @param tm_queue Specifies the tm_queue.
+ * @return 0 upon success, < 0 on failure.
*/
int odp_tm_queue_disconnect(odp_tm_queue_t tm_queue);
-/** Input API */
+/* Input API
+ * -------------------------------------------------------- */
-/** The odp_tm_enq() function is used to add packets to a given TM system.
- * Note that the System Metadata associated with the pkt needed by the TM
- * system is (a) a drop_eligible bit, (b) a two bit "pkt_color", (c) a 16-bit
- * pkt_len, and MAYBE? (d) a signed 8-bit shaper_len_adjust.
+/** Send packet to TM system
+ *
+ * Note that the packet metadata utilized by the TM system is (a)
+ * drop_eligible, (b) pkt_color, (c) pkt_len, and (d) shaper_len_adjust.
*
* If there is a non-zero shaper_len_adjust, then it is added to the pkt_len
* after any non-zero shaper_len_adjust that is part of the shaper profile.
*
* The pkt_color bits are a result of some earlier Metering/Marking/Policing
- * processing (typically ingress based), and should not be confused with the
- * shaper_color produced from the TM shaper entities within the tm_inputs and
- * tm_nodes.
+ * processing.
*
- * @param[in] tm_queue Specifies the tm_queue (and indirectly the TM system).
- * @param[in] pkt Handle to a packet.
- * @return Returns 0 upon success, < 0 upon failure. One of the
- * more common failure reasons is WRED drop.
+ * @param tm_queue Specifies the tm_queue (and indirectly the TM system).
+ * @param pkt Handle to a packet.
+ * @return Returns 0 upon success, < 0 upon failure. One of the
+ * more common failure reasons is WRED drop.
*/
int odp_tm_enq(odp_tm_queue_t tm_queue, odp_packet_t pkt);
+/** The odp_tm_enq_multi() function is used to add packets to a given TM system.
+ * This function enqueues multiple packets but is otherwise similar to
+ * odp_tm_enq(). Packets dropped by WRED or other queue management action do not
+ * cause this function to return a failure. Such packets get consumed just like
+ * the packets that are not dropped.
+ *
+ * When the return value is less than 'num', the remaining packets at the end of
+ * the array are not consumed, and the caller maintains ownership of those.
+ *
+ * @param tm_queue Specifies the tm_queue (and indirectly the TM system).
+ * @param packets Array of packets to enqueue.
+ * @param num Number of packets to enqueue.
+ *
+ * @return Number of packets consumed (0 ... num)
+ * @retval <0 on failure.
+ */
+int odp_tm_enq_multi(odp_tm_queue_t tm_queue, const odp_packet_t packets[], int num);
+
+/** Send packets with segmentation offload to TM system
+ *
+ * Like odp_tm_enq_multi(), but segments packets according LSO configuration. See e.g.
+ * odp_pktout_send_lso() for documentation how packets are split into smaller packets during
+ * the segmentation offload. Packet segmentation is done first and TM functionality is applied
+ * to the resulting packets.
+ *
+ * @param tm_queue Specifies the tm_queue (and indirectly the TM system)
+ * @param packets Array of packets to enqueue with segmentation offload
+ * @param num Number of packets in the array
+ * @param lso_opt LSO options to be used for all packets. When NULL, LSO options are
+ * read from each packet (see odp_packet_lso_request()).
+ *
+ * @return Number of packets successfully processed and consumed (0 ... num)
+ * @retval <0 on failure
+ */
+int odp_tm_enq_multi_lso(odp_tm_queue_t tm_queue, const odp_packet_t packets[], int num,
+ const odp_packet_lso_opt_t *lso_opt);
+
/** The odp_tm_enq_with_cnt() function behaves identically to odp_tm_enq(),
- * except that it also returns (an approximation to?) the current tm_queue
- * packet queue count.
+ * except that it also returns the current tm_queue packet queue count (may be
+ * an approximation).
*
- * @param[in] tm_queue Specifies the tm_queue (and indirectly the TM system).
- * @param[in] pkt Handle to a packet.
- * @return Returns the number of packets previously enqueued on
- * this tm_queue upon success, < 0 upon failure.
+ * @param tm_queue Specifies the tm_queue (and indirectly the TM system).
+ * @param pkt Handle to a packet.
+ * @return Returns the number of packets previously enqueued on
+ * this tm_queue upon success, < 0 upon failure.
*/
int odp_tm_enq_with_cnt(odp_tm_queue_t tm_queue, odp_packet_t pkt);
-/* Dynamic state query functions */
+/* Dynamic state query functions
+ * -------------------------------------------------------- */
/** The odp_tm_node_info_t record type is used to return various bits of
* information about a given tm_node via the odp_tm_node_info() function.
@@ -1654,9 +2060,10 @@ typedef struct {
/** Get tm_node Info
*
* The odp_tm_node_info() function is used to extract various bits of
- * configuration associated with a given tm_node.
+ * configuration associated with a given tm_node. The info structure is written
+ * only on success.
*
- * @param[in] tm_node Specifies the tm_node to be queried.
+ * @param tm_node Specifies the tm_node to be queried.
* @param[out] info A pointer to an odp_tm_node_info_t record that is to
* be filled in by this call.
* @return Returns < 0 upon failure, 0 upon success.
@@ -1714,13 +2121,13 @@ typedef struct {
* from their fanin list, a reasonable list walk can occur - even while past or
* future entries are being removed or while future entries are being added.
* Note that all new additions to a fanin list always take place at the end of
- * the list.
+ * the list. The info structure is written only on success.
*
- * @param[in] tm_node Specifies the tm_node to be queried.
- * @param[inout] info A pointer to an odp_tm_node_fanin_info_t record that
- * is used to determine which fanin entry is to be
- * next filled in by this call.
- * @return Returns < 0 upon failure, 0 upon success.
+ * @param tm_node Specifies the tm_node to be queried.
+ * @param[in,out] info A pointer to an odp_tm_node_fanin_info_t record that
+ * is used to determine which fanin entry is to be
+ * next filled in by this call.
+ * @return Returns < 0 upon failure, 0 upon success.
*/
int odp_tm_node_fanin_info(odp_tm_node_t tm_node,
odp_tm_node_fanin_info_t *info);
@@ -1746,7 +2153,7 @@ typedef struct {
odp_tm_wred_t wred_profile[ODP_NUM_PACKET_COLORS];
/** The next_tm_node is the "next" node in the tree - i.e. the fanout
- * of this tm_queu. Can be ODP_TM_ROOT if this tm_queue directly
+ * of this tm_queue. Can be ODP_TM_ROOT if this tm_queue directly
* connects to the egress spigot and can be ODP_TM_INVALID if this
* tm_queue is disconnected from the TM system tree. */
odp_tm_node_t next_tm_node;
@@ -1759,9 +2166,10 @@ typedef struct {
/** Get tm_queue Info
*
* The odp_tm_queue_info() function is used to extract various bits of
- * configuration associated with a given tm_queue.
+ * configuration associated with a given tm_queue. The info structure is
+ * written only on success.
*
- * @param[in] tm_queue Specifies the tm_queue to be queried.
+ * @param tm_queue Specifies the tm_queue to be queried.
* @param[out] info A pointer to an odp_tm_queue_info_t record that is to
* be filled in by this call.
* @return Returns < 0 upon failure, 0 upon success.
@@ -1773,7 +2181,7 @@ int odp_tm_queue_info(odp_tm_queue_t tm_queue, odp_tm_queue_info_t *info);
*/
#define ODP_TM_QUERY_PKT_CNT 0x01 /**< The total_pkt_cnt value */
#define ODP_TM_QUERY_BYTE_CNT 0x02 /**< The total_byte_cnt value */
-#define ODP_TM_QUERY_THRESHOLDS 0x04 /**< The thresholds??? */
+#define ODP_TM_QUERY_THRESHOLDS 0x04 /**< The threshold values */
/** The odp_tm_query_info_t record type is used to return the various counts
* as requested by functions like odp_tm_queue_query() and
@@ -1839,15 +2247,14 @@ typedef struct {
} odp_tm_query_info_t;
/** The odp_tm_queue_query() function can be used to check a single tm_queue's
- * queue utilization. The query_flags indicate whether or not packet counts,
- * byte counts or both are being requested. It is an error to request
- * neither. The implementation may still return both sets of counts
- * regardless of query_flags if the cost of returning all the counts is
- * comparable to the cost of checking the query_flags.
+ * queue utilization. The query flags indicate which information is being
+ * requested.
+ * The implementation may choose to return additional information that was not
+ * requested. The info structure is written only on success.
*
- * @param[in] tm_queue Specifies the tm_queue (and indirectly the
+ * @param tm_queue Specifies the tm_queue (and indirectly the
* TM system).
- * @param[out] query_flags A set of flag bits indicating which counters are
+ * @param query_flags A set of flag bits indicating which counters are
* being requested to be returned in the info record.
* @param[out] info Pointer to an odp_tm_query_info_t record where the
* requested queue info is returned.
@@ -1858,17 +2265,15 @@ int odp_tm_queue_query(odp_tm_queue_t tm_queue,
odp_tm_query_info_t *info);
/** The odp_tm_priority_query() function can be used to check the queue
- * utilization of all tm_queue's with the given priority. The query_flags
- * indicate whether or not packet counts, byte counts or both are being
- * requested. It is an error to request neither. The implementation may
- * still return both sets of counts regardless of query_flags if the cost of
- * returning all the counts is comparable to the cost of checking the
- * query_flags.
- *
- * @param[in] odp_tm Specifies the TM system.
- * @param[in] priority Supplies the strict priority level used to specify
+ * utilization of all tm_queue's with the given priority. The query flags
+ * indicate which information is being requested. The implementation may
+ * choose to return additional information that was not requested.
+ * The info structure is written only on success.
+ *
+ * @param odp_tm Specifies the TM system.
+ * @param priority Supplies the strict priority level used to specify
* which tm_queues are included in the info values.
- * @param[out] query_flags A set of flag bits indicating which counters are
+ * @param query_flags A set of flag bits indicating which counters are
* being requested to be returned in the info record.
* @param[out] info Pointer to an odp_tm_query_info_t record where the
* requested queue info is returned.
@@ -1881,14 +2286,12 @@ int odp_tm_priority_query(odp_tm_t odp_tm,
/** The odp_tm_total_query() function can be used to check the queue
* utilization of all tm_queue's in a single TM system. The query_flags
- * indicate whether or not packet counts, byte counts or both are being
- * requested. It is an error to request neither. The implementation may
- * still return both sets of counts regardless of query_flags if the cost of
- * returning all the counts is comparable to the cost of checking the
- * query_flags.
- *
- * @param[in] odp_tm Specifies the TM system.
- * @param[out] query_flags A set of flag bits indicating which counters are
+ * indicate which information is being requested. The implementation may
+ * choose to return additional information that was not requested.
+ * The info structure is written only on success.
+ *
+ * @param odp_tm Specifies the TM system.
+ * @param query_flags A set of flag bits indicating which counters are
* being requested to be returned in the info record.
* @param[out] info Pointer to an odp_tm_query_info_t record where the
* requested queue info is returned.
@@ -1904,14 +2307,14 @@ int odp_tm_total_query(odp_tm_t odp_tm,
* semantic effects other than returning these queue threshold values in the
* odp_tm_query_info_t record.
*
- * @param[in] odp_tm Specifies the TM system.
- * @param[in] priority Supplies the strict priority level that
- * the threshold profile params are associated
- * with.
- * @param[in] thresholds_profile Specifies the queue threshold profile that
- * should now be associated with the supplied
- * strict priority level.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param odp_tm Specifies the TM system.
+ * @param priority Supplies the strict priority level that
+ * the threshold profile params are associated with.
+ *
+ * @param thresholds_profile Specifies the queue threshold profile that
+ * should now be associated with the supplied
+ * strict priority level.
+ * @return Returns 0 upon success and < 0 upon failure.
*/
int odp_tm_priority_threshold_config(odp_tm_t odp_tm,
uint8_t priority,
@@ -1923,13 +2326,14 @@ int odp_tm_priority_threshold_config(odp_tm_t odp_tm,
* other than returning these queue threshold values in the
* odp_tm_query_info_t record.
*
- * @param[in] odp_tm Specifies the TM system.
- * @param[in] thresholds_profile Specifies the queue threshold profile that
- * should now be used for the entire TM
- * system.
- * @return Returns 0 upon success and < 0 upon failure.
+ * @param tm TM handle
+ * @param thresholds_profile Specifies the queue threshold profile that
+ * should now be used for the entire TM
+ * system.
+ *
+ * @return Returns 0 upon success and < 0 upon failure.
*/
-int odp_tm_total_threshold_config(odp_tm_t odp_tm,
+int odp_tm_total_threshold_config(odp_tm_t tm,
odp_tm_threshold_t thresholds_profile);
/** The odp_tm_is_idle function is used to determine if the specified ODP
@@ -1941,18 +2345,127 @@ int odp_tm_total_threshold_config(odp_tm_t odp_tm,
* since for some implementations this call could take a fairly long time
* to execute!
*
- * @param[in] odp_tm Specifies the TM system.
- * @return Returns 1 if the TM system is idle and 0 otherwise.
+ * @param tm TM handle
+ *
+ * @return Returns 1 if the TM system is idle and 0 otherwise.
*/
-odp_bool_t odp_tm_is_idle(odp_tm_t odp_tm);
+odp_bool_t odp_tm_is_idle(odp_tm_t tm);
/** The odp_tm_stats_print function is used to write implementation-defined
* information about the specified TM system to the ODP log. The intended use
* is for debugging.
*
- * @param[in] odp_tm Specifies the TM system.
+ * @param tm TM handle
+ */
+void odp_tm_stats_print(odp_tm_t tm);
+
+/**
+ * Get statistics for a TM queue
+ *
+ * Counters not supported by the queue are set to zero.
+ *
+ * It's implementation defined if odp_pktio_stats_reset() call affects these
+ * counters.
+ *
+ * @param tm_queue TM queue handle
+ * @param[out] stats Statistics structure for output
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int odp_tm_queue_stats(odp_tm_queue_t tm_queue, odp_tm_queue_stats_t *stats);
+
+/**
+ * Get printable value for an odp_tm_t
+ *
+ * @param tm TM handle
+ *
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_t handle.
+ */
+uint64_t odp_tm_to_u64(odp_tm_t tm);
+
+/**
+ * Get printable value for an odp_tm_queue_t
+ *
+ * @param hdl odp_tm_queue_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_queue_t handle.
+ */
+uint64_t odp_tm_queue_to_u64(odp_tm_queue_t hdl);
+
+/**
+ * Get printable value for an odp_tm_node_t
+ *
+ * @param hdl odp_tm_node_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_node_t handle.
+ */
+uint64_t odp_tm_node_to_u64(odp_tm_node_t hdl);
+
+/**
+ * Get printable value for an odp_tm_shaper_t
+ *
+ * @param hdl odp_tm_shaper_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_shaper_t handle.
+ */
+uint64_t odp_tm_shaper_to_u64(odp_tm_shaper_t hdl);
+
+/**
+ * Get printable value for an odp_tm_sched_t
+ *
+ * @param hdl odp_tm_sched_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_sched_t handle.
+ */
+uint64_t odp_tm_sched_to_u64(odp_tm_sched_t hdl);
+
+/**
+ * Get printable value for an odp_tm_threshold_t
+ *
+ * @param hdl odp_tm_threshold_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_threshold_t handle.
+ */
+uint64_t odp_tm_threshold_to_u64(odp_tm_threshold_t hdl);
+
+/**
+ * Get printable value for an odp_tm_wred_t
+ *
+ * @param hdl odp_tm_wred_t handle to be printed
+ * @return uint64_t value that can be used to print/display this
+ * handle
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_tm_wred_t handle.
*/
-void odp_tm_stats_print(odp_tm_t odp_tm);
+uint64_t odp_tm_wred_to_u64(odp_tm_wred_t hdl);
/**
* @}
diff --git a/include/odp/api/spec/version.h.in b/include/odp/api/spec/version.h.in
index f5e9e9c8b..19c5f2ec1 100644
--- a/include/odp/api/spec/version.h.in
+++ b/include/odp/api/spec/version.h.in
@@ -1,18 +1,16 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2021 Nokia
*/
-
/**
* @file
*
* ODP version
*/
-#ifndef ODP_API_VERSION_H_
-#define ODP_API_VERSION_H_
+#ifndef ODP_API_SPEC_VERSION_H_
+#define ODP_API_SPEC_VERSION_H_
#include <odp/visibility_begin.h>
#ifdef __cplusplus
@@ -21,8 +19,7 @@ extern "C" {
/**
* @defgroup odp_version ODP VERSION
- * @details
- * <b> ODP API and implementation versions </b>
+ * API and implementation versions
*
* ODP API version is identified by ODP_VERSION_API_XXX preprocessor macros.
* In addition to these macros, API calls can be used to identify implementation
@@ -57,6 +54,23 @@ extern "C" {
#define ODP_VERSION_API_MINOR @ODP_VERSION_API_MINOR@
/**
+ * ODP API version number macro
+ *
+ * Macro to build a version number for comparisons
+ */
+#define ODP_VERSION_API_NUM(gen, ma, mi) ((gen) << 24 | (ma) << 16 | (mi) << 8)
+
+/**
+ * ODP API version number
+ *
+ * API version number for comparisons against ODP_VERSION_API_NUM()
+ * macro output.
+ */
+#define ODP_VERSION_API ODP_VERSION_API_NUM(ODP_VERSION_API_GENERATION, \
+ ODP_VERSION_API_MAJOR, \
+ ODP_VERSION_API_MINOR)
+
+/**
* ODP API version string
*
* The API version string defines ODP API version in this format:
diff --git a/include/odp/api/spinlock.h b/include/odp/api/spinlock.h
new file mode 100644
index 000000000..de4a86bf7
--- /dev/null
+++ b/include/odp/api/spinlock.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP spinlock
+ */
+
+#ifndef ODP_API_SPINLOCK_H_
+#define ODP_API_SPINLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/spinlock.h>
+
+#include <odp/api/spec/spinlock.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/spinlock_recursive.h b/include/odp/api/spinlock_recursive.h
new file mode 100644
index 000000000..6717a3958
--- /dev/null
+++ b/include/odp/api/spinlock_recursive.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP recursive spinlock
+ */
+
+#ifndef ODP_API_SPINLOCK_RECURSIVE_H_
+#define ODP_API_SPINLOCK_RECURSIVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/spinlock_recursive.h>
+
+#include <odp/api/spec/spinlock_recursive.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/stash.h b/include/odp/api/stash.h
new file mode 100644
index 000000000..e3071ec4e
--- /dev/null
+++ b/include/odp/api/stash.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP stash
+ */
+
+#ifndef ODP_API_STASH_H_
+#define ODP_API_STASH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/stash.h>
+
+#include <odp/api/spec/stash.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/stash_types.h b/include/odp/api/stash_types.h
new file mode 100644
index 000000000..429a47b27
--- /dev/null
+++ b/include/odp/api/stash_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP stash
+ */
+
+#ifndef ODP_API_STASH_TYPES_H_
+#define ODP_API_STASH_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/stash_types.h>
+
+#include <odp/api/spec/stash_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/std.h b/include/odp/api/std.h
new file mode 100644
index 000000000..7a057efc6
--- /dev/null
+++ b/include/odp/api/std.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_STD_H_
+#define ODP_API_STD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/std_types.h>
+#include <odp/api/abi/std.h>
+
+#include <odp/api/spec/std.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/std_types.h b/include/odp/api/std_types.h
new file mode 100644
index 000000000..b94007314
--- /dev/null
+++ b/include/odp/api/std_types.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * Standard C language types and definitions for ODP.
+ */
+
+#ifndef ODP_API_STD_TYPES_H_
+#define ODP_API_STD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <odp/api/abi/std_types.h>
+
+#include <odp/api/spec/std_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/sync.h b/include/odp/api/sync.h
new file mode 100644
index 000000000..c936a1477
--- /dev/null
+++ b/include/odp/api/sync.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP synchronisation
+ */
+
+#ifndef ODP_API_SYNC_H_
+#define ODP_API_SYNC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/sync.h>
+
+#include <odp/api/spec/sync.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/system_info.h b/include/odp/api/system_info.h
new file mode 100644
index 000000000..d830a4f9c
--- /dev/null
+++ b/include/odp/api/system_info.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP system information
+ */
+
+#ifndef ODP_API_SYSTEM_INFO_H_
+#define ODP_API_SYSTEM_INFO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+#include <odp/api/spec/system_info.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/thread.h b/include/odp/api/thread.h
new file mode 100644
index 000000000..24199a166
--- /dev/null
+++ b/include/odp/api/thread.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP thread API
+ */
+
+#ifndef ODP_API_THREAD_H_
+#define ODP_API_THREAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/thread.h>
+
+#include <odp/api/spec/thread.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/thread_types.h b/include/odp/api/thread_types.h
new file mode 100644
index 000000000..54ea5b714
--- /dev/null
+++ b/include/odp/api/thread_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP thread
+ */
+
+#ifndef ODP_API_THREAD_TYPES_H_
+#define ODP_API_THREAD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/thread_types.h>
+
+#include <odp/api/spec/thread_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/threshold.h b/include/odp/api/threshold.h
new file mode 100644
index 000000000..f8e85e206
--- /dev/null
+++ b/include/odp/api/threshold.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP threshold API - platform specific header
+ */
+
+#ifndef ODP_API_THRESHOLD_H_
+#define ODP_API_THRESHOLD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/threshold.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/thrmask.h b/include/odp/api/thrmask.h
new file mode 100644
index 000000000..04cdffb99
--- /dev/null
+++ b/include/odp/api/thrmask.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP thread masks
+ */
+
+#ifndef ODP_API_THRMASK_H_
+#define ODP_API_THRMASK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/thrmask.h>
+
+#include <odp/api/spec/thrmask.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/ticketlock.h b/include/odp/api/ticketlock.h
new file mode 100644
index 000000000..6ca983b3b
--- /dev/null
+++ b/include/odp/api/ticketlock.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP ticketlock
+ */
+
+#ifndef ODP_API_TICKETLOCK_H_
+#define ODP_API_TICKETLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/ticketlock.h>
+
+#include <odp/api/spec/ticketlock.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/time.h b/include/odp/api/time.h
new file mode 100644
index 000000000..24757bbc6
--- /dev/null
+++ b/include/odp/api/time.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP time
+ */
+
+#ifndef ODP_API_TIME_H_
+#define ODP_API_TIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/time.h>
+
+#include <odp/api/spec/time.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/time_types.h b/include/odp/api/time_types.h
new file mode 100644
index 000000000..6cb14ffc7
--- /dev/null
+++ b/include/odp/api/time_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP time
+ */
+
+#ifndef ODP_API_TIME_TYPES_H_
+#define ODP_API_TIME_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/time_types.h>
+
+#include <odp/api/spec/time_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/timer.h b/include/odp/api/timer.h
new file mode 100644
index 000000000..512c9aec0
--- /dev/null
+++ b/include/odp/api/timer.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP timer service
+ */
+
+#ifndef ODP_API_TIMER_H_
+#define ODP_API_TIMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/timer.h>
+
+#include <odp/api/spec/timer.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/timer_types.h b/include/odp/api/timer_types.h
new file mode 100644
index 000000000..0fb0c7e1e
--- /dev/null
+++ b/include/odp/api/timer_types.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP timer service
+ */
+
+#ifndef ODP_API_TIMER_TYPES_H_
+#define ODP_API_TIMER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/timer_types.h>
+
+#include <odp/api/spec/timer_types.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/traffic_mngr.h b/include/odp/api/traffic_mngr.h
new file mode 100644
index 000000000..96ccff170
--- /dev/null
+++ b/include/odp/api/traffic_mngr.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP Traffic manager
+ */
+
+#ifndef ODP_API_TRAFFIC_MNGR_H_
+#define ODP_API_TRAFFIC_MNGR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/traffic_mngr.h>
+
+#include <odp/api/spec/traffic_mngr.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/api/version.h b/include/odp/api/version.h
new file mode 100644
index 000000000..4461ccc32
--- /dev/null
+++ b/include/odp/api/version.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP version
+ */
+
+#ifndef ODP_API_VERSION_H_
+#define ODP_API_VERSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/version.h>
+
+#include <odp/api/spec/version.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/align.h b/include/odp/arch/arm32-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/atomic.h b/include/odp/arch/arm32-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..ab7c3f17b
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/barrier.h b/include/odp/arch/arm32-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..c3388fd2a
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/buffer.h b/include/odp/arch/arm32-linux/odp/api/abi/buffer.h
index d9c31930d..2ffa07d1f 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/buffer.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/buffer.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/buffer.h>
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/buffer_types.h b/include/odp/arch/arm32-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/byteorder.h b/include/odp/arch/arm32-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..9c9728840
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/classification.h b/include/odp/arch/arm32-linux/odp/api/abi/classification.h
index d48a4733f..789f3a92a 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/classification.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/classification.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/classification.h>
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/comp.h b/include/odp/arch/arm32-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/cpu.h b/include/odp/arch/arm32-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..575f4a5ec
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/cpu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/cpumask.h b/include/odp/arch/arm32-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/crypto.h b/include/odp/arch/arm32-linux/odp/api/abi/crypto.h
index 9a9d2dc1b..9d39ba7bc 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/crypto.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/crypto.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/crypto.h>
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/crypto_types.h b/include/odp/arch/arm32-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/debug.h b/include/odp/arch/arm32-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..8a2ef2ade
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/dma.h b/include/odp/arch/arm32-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/dma_types.h b/include/odp/arch/arm32-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/errno.h b/include/odp/arch/arm32-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/event.h b/include/odp/arch/arm32-linux/odp/api/abi/event.h
index 5d2ac75e7..6f7074bb1 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/event.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/event.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/event.h>
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/event_types.h b/include/odp/arch/arm32-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/hash.h b/include/odp/arch/arm32-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/init.h b/include/odp/arch/arm32-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/ipsec.h b/include/odp/arch/arm32-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..33c03cbe2
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/arm32-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/ml_types.h b/include/odp/arch/arm32-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/packet.h b/include/odp/arch/arm32-linux/odp/api/abi/packet.h
index f44cb5365..9d560e6c8 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/packet.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/packet.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/packet.h>
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/packet_flags.h b/include/odp/arch/arm32-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..598047d96
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/packet_io.h b/include/odp/arch/arm32-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..4356eb007
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/arm32-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/packet_types.h b/include/odp/arch/arm32-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/pool.h b/include/odp/arch/arm32-linux/odp/api/abi/pool.h
index b354afbf7..2f2e9164f 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/pool.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/pool.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/pool.h>
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/pool_types.h b/include/odp/arch/arm32-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/proto_stats.h b/include/odp/arch/arm32-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/arm32-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/queue.h b/include/odp/arch/arm32-linux/odp/api/abi/queue.h
index 6027cee2f..2ba9174d0 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/queue.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/queue.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/queue.h>
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/queue_types.h b/include/odp/arch/arm32-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/random.h b/include/odp/arch/arm32-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/rwlock.h b/include/odp/arch/arm32-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..5f953d6f6
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/arm32-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..f10116aeb
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/schedule.h b/include/odp/arch/arm32-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..cd16a1161
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/schedule_types.h b/include/odp/arch/arm32-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/shared_memory.h b/include/odp/arch/arm32-linux/odp/api/abi/shared_memory.h
index d20367859..7ba8a2248 100644
--- a/include/odp/arch/arm32-linux/odp/api/abi/shared_memory.h
+++ b/include/odp/arch/arm32-linux/odp/api/abi/shared_memory.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/shared_memory.h>
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/spinlock.h b/include/odp/arch/arm32-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..c626baae5
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/arm32-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..6794590ca
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/stash.h b/include/odp/arch/arm32-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/stash_types.h b/include/odp/arch/arm32-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/std.h b/include/odp/arch/arm32-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..936009922
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/std_types.h b/include/odp/arch/arm32-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/sync.h b/include/odp/arch/arm32-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..dbe3a95ab
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/thread.h b/include/odp/arch/arm32-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..b6a233c96
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/thread_types.h b/include/odp/arch/arm32-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/thrmask.h b/include/odp/arch/arm32-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/ticketlock.h b/include/odp/arch/arm32-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..4317157b8
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/time.h b/include/odp/arch/arm32-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..0f2425669
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/time_types.h b/include/odp/arch/arm32-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/timer.h b/include/odp/arch/arm32-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/timer_types.h b/include/odp/arch/arm32-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..51b578aa7
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/arm32-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/arm32-linux/odp/api/abi/version.h b/include/odp/arch/arm32-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/include/odp/arch/arm32-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/align.h b/include/odp/arch/arm64-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/atomic.h b/include/odp/arch/arm64-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..ab7c3f17b
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/barrier.h b/include/odp/arch/arm64-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..c3388fd2a
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/buffer.h b/include/odp/arch/arm64-linux/odp/api/abi/buffer.h
index d9c31930d..2ffa07d1f 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/buffer.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/buffer.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/buffer.h>
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/buffer_types.h b/include/odp/arch/arm64-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/byteorder.h b/include/odp/arch/arm64-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..9c9728840
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/classification.h b/include/odp/arch/arm64-linux/odp/api/abi/classification.h
index d48a4733f..789f3a92a 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/classification.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/classification.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/classification.h>
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/comp.h b/include/odp/arch/arm64-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/cpu.h b/include/odp/arch/arm64-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..575f4a5ec
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/cpu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/cpumask.h b/include/odp/arch/arm64-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/crypto.h b/include/odp/arch/arm64-linux/odp/api/abi/crypto.h
index 9a9d2dc1b..9d39ba7bc 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/crypto.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/crypto.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/crypto.h>
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/crypto_types.h b/include/odp/arch/arm64-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/debug.h b/include/odp/arch/arm64-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..8a2ef2ade
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/dma.h b/include/odp/arch/arm64-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/dma_types.h b/include/odp/arch/arm64-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/errno.h b/include/odp/arch/arm64-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/event.h b/include/odp/arch/arm64-linux/odp/api/abi/event.h
index 5d2ac75e7..6f7074bb1 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/event.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/event.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/event.h>
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/event_types.h b/include/odp/arch/arm64-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/hash.h b/include/odp/arch/arm64-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/init.h b/include/odp/arch/arm64-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/ipsec.h b/include/odp/arch/arm64-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..33c03cbe2
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/arm64-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/ml_types.h b/include/odp/arch/arm64-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/packet.h b/include/odp/arch/arm64-linux/odp/api/abi/packet.h
index f44cb5365..9d560e6c8 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/packet.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/packet.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/packet.h>
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/packet_flags.h b/include/odp/arch/arm64-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..598047d96
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/packet_io.h b/include/odp/arch/arm64-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..4356eb007
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/arm64-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/packet_types.h b/include/odp/arch/arm64-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/pool.h b/include/odp/arch/arm64-linux/odp/api/abi/pool.h
index b354afbf7..2f2e9164f 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/pool.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/pool.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/pool.h>
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/pool_types.h b/include/odp/arch/arm64-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/proto_stats.h b/include/odp/arch/arm64-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/arm64-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/queue.h b/include/odp/arch/arm64-linux/odp/api/abi/queue.h
index 6027cee2f..2ba9174d0 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/queue.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/queue.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/queue.h>
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/queue_types.h b/include/odp/arch/arm64-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/random.h b/include/odp/arch/arm64-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/rwlock.h b/include/odp/arch/arm64-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..5f953d6f6
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/arm64-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..f10116aeb
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/schedule.h b/include/odp/arch/arm64-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..cd16a1161
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/schedule_types.h b/include/odp/arch/arm64-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/shared_memory.h b/include/odp/arch/arm64-linux/odp/api/abi/shared_memory.h
index d20367859..7ba8a2248 100644
--- a/include/odp/arch/arm64-linux/odp/api/abi/shared_memory.h
+++ b/include/odp/arch/arm64-linux/odp/api/abi/shared_memory.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/shared_memory.h>
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/spinlock.h b/include/odp/arch/arm64-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..c626baae5
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/arm64-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..6794590ca
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/stash.h b/include/odp/arch/arm64-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/stash_types.h b/include/odp/arch/arm64-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/std.h b/include/odp/arch/arm64-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..936009922
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/std_types.h b/include/odp/arch/arm64-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/sync.h b/include/odp/arch/arm64-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..dbe3a95ab
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/thread.h b/include/odp/arch/arm64-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..b6a233c96
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/thread_types.h b/include/odp/arch/arm64-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/thrmask.h b/include/odp/arch/arm64-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/ticketlock.h b/include/odp/arch/arm64-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..4317157b8
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/time.h b/include/odp/arch/arm64-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..0f2425669
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/time_types.h b/include/odp/arch/arm64-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/timer.h b/include/odp/arch/arm64-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/timer_types.h b/include/odp/arch/arm64-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..51b578aa7
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/arm64-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/arm64-linux/odp/api/abi/version.h b/include/odp/arch/arm64-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/include/odp/arch/arm64-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/align.h b/include/odp/arch/default-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..d5b26ba72
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/atomic.h b/include/odp/arch/default-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..6645faf14
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/barrier.h b/include/odp/arch/default-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..23537289c
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/buffer.h b/include/odp/arch/default-linux/odp/api/abi/buffer.h
new file mode 100644
index 000000000..092054239
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/buffer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/buffer_types.h b/include/odp/arch/default-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/byteorder.h b/include/odp/arch/default-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..479077adb
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/classification.h b/include/odp/arch/default-linux/odp/api/abi/classification.h
new file mode 100644
index 000000000..7eccb6819
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/classification.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/comp.h b/include/odp/arch/default-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/cpu.h b/include/odp/arch/default-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..575f4a5ec
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/cpu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/default-linux/odp/api/abi/cpumask.h b/include/odp/arch/default-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..c14cd36ba
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/crypto.h b/include/odp/arch/default-linux/odp/api/abi/crypto.h
new file mode 100644
index 000000000..1fc496fdd
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/crypto.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/crypto_types.h b/include/odp/arch/default-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/debug.h b/include/odp/arch/default-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..59d18a02d
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/dma.h b/include/odp/arch/default-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/dma_types.h b/include/odp/arch/default-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/errno.h b/include/odp/arch/default-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/event.h b/include/odp/arch/default-linux/odp/api/abi/event.h
new file mode 100644
index 000000000..5e6286ebd
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/event.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/event_types.h b/include/odp/arch/default-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/hash.h b/include/odp/arch/default-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/init.h b/include/odp/arch/default-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..babb44bad
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/ipsec.h b/include/odp/arch/default-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..877515add
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/default-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/ml_types.h b/include/odp/arch/default-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/packet.h b/include/odp/arch/default-linux/odp/api/abi/packet.h
new file mode 100644
index 000000000..8cee03484
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/packet.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/packet_flags.h b/include/odp/arch/default-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..00afba64c
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/packet_io.h b/include/odp/arch/default-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..107a480dd
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/default-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/packet_types.h b/include/odp/arch/default-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/pool.h b/include/odp/arch/default-linux/odp/api/abi/pool.h
new file mode 100644
index 000000000..9c5d5d4fe
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/pool.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/pool_types.h b/include/odp/arch/default-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/proto_stats.h b/include/odp/arch/default-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/default-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/queue.h b/include/odp/arch/default-linux/odp/api/abi/queue.h
new file mode 100644
index 000000000..352885307
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/queue.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/queue_types.h b/include/odp/arch/default-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/random.h b/include/odp/arch/default-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/rwlock.h b/include/odp/arch/default-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..e77c35549
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/default-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..b347e9c87
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/schedule.h b/include/odp/arch/default-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..3c00f79da
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/schedule_types.h b/include/odp/arch/default-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..2731cfbb7
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/shared_memory.h b/include/odp/arch/default-linux/odp/api/abi/shared_memory.h
new file mode 100644
index 000000000..ddf32054c
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/shared_memory.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/spinlock.h b/include/odp/arch/default-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..8fe21357d
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/default-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..366c181ec
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/stash.h b/include/odp/arch/default-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/stash_types.h b/include/odp/arch/default-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/std.h b/include/odp/arch/default-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..ae8d69371
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/std_types.h b/include/odp/arch/default-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..3d011d980
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/sync.h b/include/odp/arch/default-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..e6d26db02
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/thread.h b/include/odp/arch/default-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..26b2f6be0
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/thread_types.h b/include/odp/arch/default-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/thrmask.h b/include/odp/arch/default-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..305be2ca4
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/ticketlock.h b/include/odp/arch/default-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..53d4fa04a
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/time.h b/include/odp/arch/default-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..1b61b9e8d
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/time_types.h b/include/odp/arch/default-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/timer.h b/include/odp/arch/default-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/timer_types.h b/include/odp/arch/default-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..40fd26343
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/default-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..8904e4291
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/default-linux/odp/api/abi/version.h b/include/odp/arch/default-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..3249a2e26
--- /dev/null
+++ b/include/odp/arch/default-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/arch/default/api/abi/buffer.h b/include/odp/arch/default/api/abi/buffer.h
deleted file mode 100644
index d8bfc9131..000000000
--- a/include/odp/arch/default/api/abi/buffer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_BUFFER_H_
-#define ODP_ABI_BUFFER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_buffer_t;
-
-/** @ingroup odp_buffer
- * @{
- */
-
-typedef _odp_abi_buffer_t *odp_buffer_t;
-
-#define ODP_BUFFER_INVALID ((odp_buffer_t)NULL)
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/classification.h b/include/odp/arch/default/api/abi/classification.h
deleted file mode 100644
index 771907aec..000000000
--- a/include/odp/arch/default/api/abi/classification.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_CLASSIFICATION_H_
-#define ODP_ABI_CLASSIFICATION_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_cos_t;
-
-/** Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_pmr_t;
-
-/** @ingroup odp_classification
- * @{
- */
-
-typedef _odp_abi_cos_t *odp_cos_t;
-typedef _odp_abi_pmr_t *odp_pmr_t;
-
-#define ODP_COS_INVALID ((odp_cos_t)~0)
-#define ODP_PMR_INVAL ((odp_pmr_t)~0)
-
-#define ODP_COS_NAME_LEN 32
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/crypto.h b/include/odp/arch/default/api/abi/crypto.h
deleted file mode 100644
index f0793a198..000000000
--- a/include/odp/arch/default/api/abi/crypto.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_CRYPTO_H_
-#define ODP_ABI_CRYPTO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_crypto_compl_t;
-
-/** @ingroup odp_crypto
- * @{
- */
-
-#define ODP_CRYPTO_SESSION_INVALID (0xffffffffffffffffULL)
-
-typedef uint64_t odp_crypto_session_t;
-typedef _odp_abi_crypto_compl_t *odp_crypto_compl_t;
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/event.h b/include/odp/arch/default/api/abi/event.h
deleted file mode 100644
index fd86f25c5..000000000
--- a/include/odp/arch/default/api/abi/event.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_EVENT_H_
-#define ODP_ABI_EVENT_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_event_t;
-
-/** @ingroup odp_event
- * @{
- */
-
-typedef _odp_abi_event_t *odp_event_t;
-
-#define ODP_EVENT_INVALID ((odp_event_t)NULL)
-
-typedef enum odp_event_type_t {
- ODP_EVENT_BUFFER = 1,
- ODP_EVENT_PACKET = 2,
- ODP_EVENT_TIMEOUT = 3,
- ODP_EVENT_CRYPTO_COMPL = 4
-} odp_event_type_t;
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/packet.h b/include/odp/arch/default/api/abi/packet.h
deleted file mode 100644
index 4aac75b9e..000000000
--- a/include/odp/arch/default/api/abi/packet.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_PACKET_H_
-#define ODP_ABI_PACKET_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_packet_t;
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< *internal Dummy */ } _odp_abi_packet_seg_t;
-
-/** @ingroup odp_packet
- * @{
- */
-
-typedef _odp_abi_packet_t *odp_packet_t;
-typedef _odp_abi_packet_seg_t *odp_packet_seg_t;
-
-#define ODP_PACKET_INVALID ((odp_packet_t)0xffffffff)
-#define ODP_PACKET_SEG_INVALID ((odp_packet_seg_t)0xffffffff)
-#define ODP_PACKET_OFFSET_INVALID (0x0fffffff)
-
-typedef enum {
- ODP_PACKET_GREEN = 0,
- ODP_PACKET_YELLOW = 1,
- ODP_PACKET_RED = 2,
- ODP_PACKET_ALL_COLORS = 3,
-} odp_packet_color_t;
-
-#define ODP_NUM_PACKET_COLORS 3
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/pool.h b/include/odp/arch/default/api/abi/pool.h
deleted file mode 100644
index 4637d19fe..000000000
--- a/include/odp/arch/default/api/abi/pool.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_POOL_H_
-#define ODP_ABI_POOL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/abi/event.h>
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_pool_t;
-
-/** @ingroup odp_pool
- * @{
- */
-
-typedef _odp_abi_pool_t *odp_pool_t;
-
-#define ODP_POOL_INVALID ((odp_pool_t)0xffffffff)
-
-#define ODP_POOL_NAME_LEN 32
-
-typedef enum odp_pool_type_t {
- ODP_POOL_BUFFER = ODP_EVENT_BUFFER,
- ODP_POOL_PACKET = ODP_EVENT_PACKET,
- ODP_POOL_TIMEOUT = ODP_EVENT_TIMEOUT
-} odp_pool_type_t;
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/queue.h b/include/odp/arch/default/api/abi/queue.h
deleted file mode 100644
index 378b069ce..000000000
--- a/include/odp/arch/default/api/abi/queue.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_QUEUE_H_
-#define ODP_ABI_QUEUE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_queue_t;
-
-/** @ingroup odp_queue
- * @{
- */
-
-typedef _odp_abi_queue_t *odp_queue_t;
-
-#define ODP_QUEUE_INVALID ((odp_queue_t)0)
-
-#define ODP_QUEUE_NAME_LEN 32
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/default/api/abi/shared_memory.h b/include/odp/arch/default/api/abi/shared_memory.h
deleted file mode 100644
index 5805f957f..000000000
--- a/include/odp/arch/default/api/abi/shared_memory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ABI_SHM_H_
-#define ODP_ABI_SHM_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @internal Dummy type for strong typing */
-typedef struct { char dummy; /**< @internal Dummy */ } _odp_abi_shm_t;
-
-/** @ingroup odp_shared_memory
- * @{
- */
-
-typedef _odp_abi_shm_t *odp_shm_t;
-
-#define ODP_SHM_INVALID ((odp_shm_t)0)
-#define ODP_SHM_NULL ODP_SHM_INVALID
-#define ODP_SHM_NAME_LEN 32
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/buffer.h b/include/odp/arch/mips64-linux/odp/api/abi/buffer.h
deleted file mode 100644
index d9c31930d..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/buffer.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/buffer.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/classification.h b/include/odp/arch/mips64-linux/odp/api/abi/classification.h
deleted file mode 100644
index d48a4733f..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/classification.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/classification.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/crypto.h b/include/odp/arch/mips64-linux/odp/api/abi/crypto.h
deleted file mode 100644
index 9a9d2dc1b..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/crypto.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/crypto.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/event.h b/include/odp/arch/mips64-linux/odp/api/abi/event.h
deleted file mode 100644
index 5d2ac75e7..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/event.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/event.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/packet.h b/include/odp/arch/mips64-linux/odp/api/abi/packet.h
deleted file mode 100644
index f44cb5365..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/packet.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/packet.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/pool.h b/include/odp/arch/mips64-linux/odp/api/abi/pool.h
deleted file mode 100644
index b354afbf7..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/pool.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/pool.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/queue.h b/include/odp/arch/mips64-linux/odp/api/abi/queue.h
deleted file mode 100644
index 6027cee2f..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/queue.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/queue.h>
diff --git a/include/odp/arch/mips64-linux/odp/api/abi/shared_memory.h b/include/odp/arch/mips64-linux/odp/api/abi/shared_memory.h
deleted file mode 100644
index d20367859..000000000
--- a/include/odp/arch/mips64-linux/odp/api/abi/shared_memory.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/arch/default/api/abi/shared_memory.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/align.h b/include/odp/arch/power64-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/atomic.h b/include/odp/arch/power64-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..ab7c3f17b
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/barrier.h b/include/odp/arch/power64-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..c3388fd2a
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/buffer.h b/include/odp/arch/power64-linux/odp/api/abi/buffer.h
index d9c31930d..2ffa07d1f 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/buffer.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/buffer.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/buffer.h>
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/buffer_types.h b/include/odp/arch/power64-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/byteorder.h b/include/odp/arch/power64-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..9c9728840
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/classification.h b/include/odp/arch/power64-linux/odp/api/abi/classification.h
index d48a4733f..789f3a92a 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/classification.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/classification.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/classification.h>
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/comp.h b/include/odp/arch/power64-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/cpu.h b/include/odp/arch/power64-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..55b59b1ab
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/cpu.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 128
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/power64-linux/odp/api/abi/cpumask.h b/include/odp/arch/power64-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/crypto.h b/include/odp/arch/power64-linux/odp/api/abi/crypto.h
index 9a9d2dc1b..9d39ba7bc 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/crypto.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/crypto.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/crypto.h>
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/crypto_types.h b/include/odp/arch/power64-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/debug.h b/include/odp/arch/power64-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..8a2ef2ade
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/dma.h b/include/odp/arch/power64-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/dma_types.h b/include/odp/arch/power64-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/errno.h b/include/odp/arch/power64-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/event.h b/include/odp/arch/power64-linux/odp/api/abi/event.h
index 5d2ac75e7..6f7074bb1 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/event.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/event.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/event.h>
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/event_types.h b/include/odp/arch/power64-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/hash.h b/include/odp/arch/power64-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/init.h b/include/odp/arch/power64-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/ipsec.h b/include/odp/arch/power64-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..33c03cbe2
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/power64-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/ml_types.h b/include/odp/arch/power64-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/packet.h b/include/odp/arch/power64-linux/odp/api/abi/packet.h
index f44cb5365..9d560e6c8 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/packet.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/packet.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/packet.h>
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/packet_flags.h b/include/odp/arch/power64-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..598047d96
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/packet_io.h b/include/odp/arch/power64-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..4356eb007
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/power64-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/packet_types.h b/include/odp/arch/power64-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/pool.h b/include/odp/arch/power64-linux/odp/api/abi/pool.h
index b354afbf7..2f2e9164f 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/pool.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/pool.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/pool.h>
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/pool_types.h b/include/odp/arch/power64-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/proto_stats.h b/include/odp/arch/power64-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/power64-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/queue.h b/include/odp/arch/power64-linux/odp/api/abi/queue.h
index 6027cee2f..2ba9174d0 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/queue.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/queue.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/queue.h>
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/queue_types.h b/include/odp/arch/power64-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/random.h b/include/odp/arch/power64-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/rwlock.h b/include/odp/arch/power64-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..5f953d6f6
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/power64-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..f10116aeb
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/schedule.h b/include/odp/arch/power64-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..cd16a1161
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/schedule_types.h b/include/odp/arch/power64-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/shared_memory.h b/include/odp/arch/power64-linux/odp/api/abi/shared_memory.h
index d20367859..7ba8a2248 100644
--- a/include/odp/arch/power64-linux/odp/api/abi/shared_memory.h
+++ b/include/odp/arch/power64-linux/odp/api/abi/shared_memory.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/shared_memory.h>
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/spinlock.h b/include/odp/arch/power64-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..c626baae5
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/power64-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..6794590ca
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/stash.h b/include/odp/arch/power64-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/stash_types.h b/include/odp/arch/power64-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/std.h b/include/odp/arch/power64-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..936009922
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/std_types.h b/include/odp/arch/power64-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/sync.h b/include/odp/arch/power64-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..dbe3a95ab
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/thread.h b/include/odp/arch/power64-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..b6a233c96
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/thread_types.h b/include/odp/arch/power64-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/thrmask.h b/include/odp/arch/power64-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/ticketlock.h b/include/odp/arch/power64-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..4317157b8
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/time.h b/include/odp/arch/power64-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..0f2425669
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/time_types.h b/include/odp/arch/power64-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/timer.h b/include/odp/arch/power64-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/timer_types.h b/include/odp/arch/power64-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..51b578aa7
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/power64-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/power64-linux/odp/api/abi/version.h b/include/odp/arch/power64-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/include/odp/arch/power64-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/align.h b/include/odp/arch/x86_32-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/atomic.h b/include/odp/arch/x86_32-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..ab7c3f17b
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/barrier.h b/include/odp/arch/x86_32-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..c3388fd2a
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/buffer.h b/include/odp/arch/x86_32-linux/odp/api/abi/buffer.h
index d9c31930d..2ffa07d1f 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/buffer.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/buffer.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/buffer.h>
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/buffer_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/byteorder.h b/include/odp/arch/x86_32-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..9c9728840
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/classification.h b/include/odp/arch/x86_32-linux/odp/api/abi/classification.h
index d48a4733f..789f3a92a 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/classification.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/classification.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/classification.h>
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/comp.h b/include/odp/arch/x86_32-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/cpu.h b/include/odp/arch/x86_32-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..575f4a5ec
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/cpu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/cpumask.h b/include/odp/arch/x86_32-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/crypto.h b/include/odp/arch/x86_32-linux/odp/api/abi/crypto.h
index 9a9d2dc1b..9d39ba7bc 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/crypto.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/crypto.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/crypto.h>
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/crypto_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/debug.h b/include/odp/arch/x86_32-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..8a2ef2ade
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/dma.h b/include/odp/arch/x86_32-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/dma_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/errno.h b/include/odp/arch/x86_32-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/event.h b/include/odp/arch/x86_32-linux/odp/api/abi/event.h
index 5d2ac75e7..6f7074bb1 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/event.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/event.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/event.h>
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/event_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/hash.h b/include/odp/arch/x86_32-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/init.h b/include/odp/arch/x86_32-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/ipsec.h b/include/odp/arch/x86_32-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..33c03cbe2
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/ml_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/packet.h b/include/odp/arch/x86_32-linux/odp/api/abi/packet.h
index f44cb5365..9d560e6c8 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/packet.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/packet.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/packet.h>
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/packet_flags.h b/include/odp/arch/x86_32-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..598047d96
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/packet_io.h b/include/odp/arch/x86_32-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..4356eb007
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/packet_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/pool.h b/include/odp/arch/x86_32-linux/odp/api/abi/pool.h
index b354afbf7..2f2e9164f 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/pool.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/pool.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/pool.h>
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/pool_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats.h b/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/queue.h b/include/odp/arch/x86_32-linux/odp/api/abi/queue.h
index 6027cee2f..2ba9174d0 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/queue.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/queue.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/queue.h>
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/queue_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/random.h b/include/odp/arch/x86_32-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/rwlock.h b/include/odp/arch/x86_32-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..5f953d6f6
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/x86_32-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..f10116aeb
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/schedule.h b/include/odp/arch/x86_32-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..cd16a1161
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/schedule_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/shared_memory.h b/include/odp/arch/x86_32-linux/odp/api/abi/shared_memory.h
index d20367859..7ba8a2248 100644
--- a/include/odp/arch/x86_32-linux/odp/api/abi/shared_memory.h
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/shared_memory.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/shared_memory.h>
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/spinlock.h b/include/odp/arch/x86_32-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..c626baae5
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/x86_32-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..6794590ca
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/stash.h b/include/odp/arch/x86_32-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/stash_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/std.h b/include/odp/arch/x86_32-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..936009922
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/std_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/sync.h b/include/odp/arch/x86_32-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..dbe3a95ab
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/thread.h b/include/odp/arch/x86_32-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..b6a233c96
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/thread_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/thrmask.h b/include/odp/arch/x86_32-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/ticketlock.h b/include/odp/arch/x86_32-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..4317157b8
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/time.h b/include/odp/arch/x86_32-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..0f2425669
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/time_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/timer.h b/include/odp/arch/x86_32-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/timer_types.h b/include/odp/arch/x86_32-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..51b578aa7
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/x86_32-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/x86_32-linux/odp/api/abi/version.h b/include/odp/arch/x86_32-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/include/odp/arch/x86_32-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/align.h b/include/odp/arch/x86_64-linux/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/atomic.h b/include/odp/arch/x86_64-linux/odp/api/abi/atomic.h
new file mode 100644
index 000000000..ab7c3f17b
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/atomic.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/atomic.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/barrier.h b/include/odp/arch/x86_64-linux/odp/api/abi/barrier.h
new file mode 100644
index 000000000..c3388fd2a
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/buffer.h b/include/odp/arch/x86_64-linux/odp/api/abi/buffer.h
index d9c31930d..2ffa07d1f 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/buffer.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/buffer.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/buffer.h>
+#include <odp/api/abi-default/buffer.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/buffer_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..331e123c7
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/buffer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/buffer_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/byteorder.h b/include/odp/arch/x86_64-linux/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..9c9728840
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/byteorder.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/byteorder.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/classification.h b/include/odp/arch/x86_64-linux/odp/api/abi/classification.h
index d48a4733f..789f3a92a 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/classification.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/classification.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/classification.h>
+#include <odp/api/abi-default/classification.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/comp.h b/include/odp/arch/x86_64-linux/odp/api/abi/comp.h
new file mode 100644
index 000000000..7b4be9d3e
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/comp.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/comp.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/cpu.h b/include/odp/arch/x86_64-linux/odp/api/abi/cpu.h
new file mode 100644
index 000000000..575f4a5ec
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/cpu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/cpumask.h b/include/odp/arch/x86_64-linux/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/crypto.h b/include/odp/arch/x86_64-linux/odp/api/abi/crypto.h
index 9a9d2dc1b..9d39ba7bc 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/crypto.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/crypto.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/crypto.h>
+#include <odp/api/abi-default/crypto.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/crypto_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..7e1da759a
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/crypto_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/crypto_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/debug.h b/include/odp/arch/x86_64-linux/odp/api/abi/debug.h
new file mode 100644
index 000000000..8a2ef2ade
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/debug.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/debug.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/dma.h b/include/odp/arch/x86_64-linux/odp/api/abi/dma.h
new file mode 100644
index 000000000..f5e42a076
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/dma.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/dma.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/dma_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..cd96b9933
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/dma_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/dma_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/errno.h b/include/odp/arch/x86_64-linux/odp/api/abi/errno.h
new file mode 100644
index 000000000..9d197a8d4
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/errno.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/errno.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/event.h b/include/odp/arch/x86_64-linux/odp/api/abi/event.h
index 5d2ac75e7..6f7074bb1 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/event.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/event.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/event.h>
+#include <odp/api/abi-default/event.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/event_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/event_types.h
new file mode 100644
index 000000000..ece9cbef0
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/event_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/event_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/hash.h b/include/odp/arch/x86_64-linux/odp/api/abi/hash.h
new file mode 100644
index 000000000..72431f765
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/hash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp/api/abi-default/hash.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/init.h b/include/odp/arch/x86_64-linux/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/ipsec.h b/include/odp/arch/x86_64-linux/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..33c03cbe2
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/ipsec.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ipsec.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/ipsec_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..854e7aa01
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/ipsec_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/ipsec_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/ml_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..4201f92e6
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/ml_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/ml_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/packet.h b/include/odp/arch/x86_64-linux/odp/api/abi/packet.h
index f44cb5365..9d560e6c8 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/packet.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/packet.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/packet.h>
+#include <odp/api/abi-default/packet.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/packet_flags.h b/include/odp/arch/x86_64-linux/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..598047d96
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/packet_flags.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_flags.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/packet_io.h b/include/odp/arch/x86_64-linux/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..4356eb007
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/packet_io.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/packet_io.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/packet_io_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..3e8962f6a
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/packet_io_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/packet_io_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/packet_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..9af00c158
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/packet_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/packet_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/pool.h b/include/odp/arch/x86_64-linux/odp/api/abi/pool.h
index b354afbf7..2f2e9164f 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/pool.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/pool.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/pool.h>
+#include <odp/api/abi-default/pool.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/pool_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..a8366b5d2
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/pool_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/pool_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats.h b/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..81108faa5
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2021 Marvell.
+ */
+
+#include <odp/api/abi-default/proto_stats.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..f6884a40a
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/proto_stats_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/queue.h b/include/odp/arch/x86_64-linux/odp/api/abi/queue.h
index 6027cee2f..2ba9174d0 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/queue.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/queue.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/queue.h>
+#include <odp/api/abi-default/queue.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/queue_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..f1a8e197d
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/queue_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi-default/queue_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/random.h b/include/odp/arch/x86_64-linux/odp/api/abi/random.h
new file mode 100644
index 000000000..703dcf5a0
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/random.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/random.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/rwlock.h b/include/odp/arch/x86_64-linux/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..5f953d6f6
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/rwlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/rwlock_recursive.h b/include/odp/arch/x86_64-linux/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..f10116aeb
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/schedule.h b/include/odp/arch/x86_64-linux/odp/api/abi/schedule.h
new file mode 100644
index 000000000..cd16a1161
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/schedule.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/schedule_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/shared_memory.h b/include/odp/arch/x86_64-linux/odp/api/abi/shared_memory.h
index d20367859..7ba8a2248 100644
--- a/include/odp/arch/x86_64-linux/odp/api/abi/shared_memory.h
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/shared_memory.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
-#include <odp/arch/default/api/abi/shared_memory.h>
+#include <odp/api/abi-default/shared_memory.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/spinlock.h b/include/odp/arch/x86_64-linux/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..c626baae5
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/spinlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/spinlock_recursive.h b/include/odp/arch/x86_64-linux/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..6794590ca
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/stash.h b/include/odp/arch/x86_64-linux/odp/api/abi/stash.h
new file mode 100644
index 000000000..ed2cbbc64
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/stash.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp/api/abi-default/stash.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/stash_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..0f2759726
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/stash_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/stash_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/std.h b/include/odp/arch/x86_64-linux/odp/api/abi/std.h
new file mode 100644
index 000000000..936009922
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/std.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/std_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/sync.h b/include/odp/arch/x86_64-linux/odp/api/abi/sync.h
new file mode 100644
index 000000000..dbe3a95ab
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/sync.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/sync.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/thread.h b/include/odp/arch/x86_64-linux/odp/api/abi/thread.h
new file mode 100644
index 000000000..b6a233c96
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/thread.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thread.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/thread_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/thrmask.h b/include/odp/arch/x86_64-linux/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/ticketlock.h b/include/odp/arch/x86_64-linux/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..4317157b8
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/ticketlock.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/ticketlock.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/time.h b/include/odp/arch/x86_64-linux/odp/api/abi/time.h
new file mode 100644
index 000000000..0f2425669
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/time.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/time.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/time_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/timer.h b/include/odp/arch/x86_64-linux/odp/api/abi/timer.h
new file mode 100644
index 000000000..0d7965bf7
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/timer.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/abi-default/timer.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/timer_types.h b/include/odp/arch/x86_64-linux/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..51b578aa7
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/timer_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/timer_types.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/traffic_mngr.h b/include/odp/arch/x86_64-linux/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/include/odp/arch/x86_64-linux/odp/api/abi/version.h b/include/odp/arch/x86_64-linux/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/include/odp/arch/x86_64-linux/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/include/odp/autoheader_external.h.in b/include/odp/autoheader_external.h.in
new file mode 100644
index 000000000..ebdb9fbc6
--- /dev/null
+++ b/include/odp/autoheader_external.h.in
@@ -0,0 +1,20 @@
+
+#ifndef ODP_AUTOHEADER_EXTERNAL_H_
+#define ODP_AUTOHEADER_EXTERNAL_H_
+
+/* Define to 1 to include additional debug code */
+#undef ODP_DEBUG
+
+/* Define to 1 to display debug information */
+#undef ODP_DEBUG_PRINT
+
+/* Define cache line size */
+#undef _ODP_CACHE_LINE_SIZE
+
+/* Define to 1 or 2 to enable event validation */
+#undef _ODP_EVENT_VALIDATION
+
+/* Define to 1 to enable WFE based lock implementation on aarch64 */
+#undef _ODP_WFE_LOCKS
+
+#endif
diff --git a/include/odp/autoheader_internal.h.in b/include/odp/autoheader_internal.h.in
new file mode 100644
index 000000000..87cefa131
--- /dev/null
+++ b/include/odp/autoheader_internal.h.in
@@ -0,0 +1,38 @@
+
+#ifndef ODP_AUTOHEADER_INTERNAL_H_
+#define ODP_AUTOHEADER_INTERNAL_H_
+
+/* Define to the name of the implementation */
+#undef _ODP_IMPLEMENTATION_NAME
+
+/* Define to name default scheduler */
+#undef _ODP_SCHEDULE_DEFAULT
+
+/* Define to 1 if numa library is usable */
+#undef _ODP_HAVE_NUMA_LIBRARY
+
+/* Define to 1 to enable DPDK packet I/O support */
+#undef _ODP_PKTIO_DPDK
+
+/* Define to 1 to enable DPDK zero copy support */
+#undef _ODP_DPDK_ZERO_COPY
+
+/* Define to 1 to enable pcap packet I/O support */
+#undef _ODP_PKTIO_PCAP
+
+/* Define to 1 to enable pcapng support */
+#undef _ODP_PCAPNG
+
+/* Define to 1 to enable OpenSSL support */
+#undef _ODP_OPENSSL
+
+/* Define to 1 to enable OpenSSL random data */
+#undef _ODP_OPENSSL_RAND
+
+/* Define to 1 to enable XDP support */
+#undef _ODP_PKTIO_XDP
+
+/* Define to 1 to enable IPSec MB crypto support */
+#undef _ODP_IPSECMB
+
+#endif
diff --git a/include/odp/drv/spec/README b/include/odp/drv/spec/README
deleted file mode 100644
index cd017bf9d..000000000
--- a/include/odp/drv/spec/README
+++ /dev/null
@@ -1,4 +0,0 @@
-This directory should contain files (including doxygen documentation) defining
-the ODP Nic driver interface.
-The NIC driver interface is the south interface of ODP, which is used by
-PCI nic drivers.
diff --git a/include/odp/drv/spec/compiler.h b/include/odp/drv/spec/compiler.h
deleted file mode 100644
index 3198d21e8..000000000
--- a/include/odp/drv/spec/compiler.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Compiler related for ODP driver interface
- */
-
-#ifndef ODPDRV_COMPILER_H_
-#define ODPDRV_COMPILER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odpdrv_compiler_optim ODPDRV COMPILER / OPTIMIZATION
- * Macro for old compilers
- * @{
- */
-
-/** @internal GNU compiler version */
-#define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
-
-/**
- * @internal
- * Compiler __builtin_bswap16() is not available on all platforms
- * until GCC 4.8.0 - work around this by offering __odpdrv_builtin_bswap16()
- * Don't use this function directly, instead see odpdrv byteorder.h
- */
-#if GCC_VERSION < 40800
-#define __odpdrv_builtin_bswap16(u16) \
- ((((u16)&0x00ff) << 8) | (((u16)&0xff00) >> 8))
-#else
-#define __odpdrv_builtin_bswap16(u16) __builtin_bswap16(u16)
-#endif
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/drv/spec/std_types.h b/include/odp/drv/spec/std_types.h
deleted file mode 100644
index 904a385b1..000000000
--- a/include/odp/drv/spec/std_types.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Standard C language types and definitions for ODP driver interface.
- *
- */
-
-#ifndef ODPDRV_STD_TYPES_H_
-#define ODPDRV_STD_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odpdrv_system ODPDRV SYSTEM
- * @{
- */
-
-/**
- * @typedef odpdrv_bool_t
- * Use odpdrv boolean type to have it well-defined and known size,
- * regardless which compiler is used as this facilities interoperability
- * between e.g. different compilers.
- */
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/odp/visibility_begin.h b/include/odp/visibility_begin.h
new file mode 100644
index 000000000..25ac005f5
--- /dev/null
+++ b/include/odp/visibility_begin.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/*
+ * @file
+ *
+ * Linker visibility directives
+ */
+
+#if __GNUC__ >= 4
+#pragma GCC visibility push(default)
+#endif
diff --git a/include/odp/visibility_end.h b/include/odp/visibility_end.h
new file mode 100644
index 000000000..4580ff03e
--- /dev/null
+++ b/include/odp/visibility_end.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/*
+ * @file
+ *
+ * Linker visibility directives
+ */
+
+#if __GNUC__ >= 4
+#pragma GCC visibility pop
+#endif
diff --git a/include/odp_api.h b/include/odp_api.h
index 060ec888b..cbd1af691 100644
--- a/include/odp_api.h
+++ b/include/odp_api.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
/**
@@ -21,9 +19,10 @@ extern "C" {
#include <odp/api/deprecated.h>
#include <odp/api/version.h>
#include <odp/api/std_types.h>
-#include <odp/api/compiler.h>
#include <odp/api/align.h>
#include <odp/api/hash.h>
+#include <odp/api/chksum.h>
+#include <odp/api/comp.h>
#include <odp/api/hints.h>
#include <odp/api/debug.h>
#include <odp/api/byteorder.h>
@@ -47,6 +46,7 @@ extern "C" {
#include <odp/api/packet.h>
#include <odp/api/packet_flags.h>
#include <odp/api/packet_io.h>
+#include <odp/api/proto_stats.h>
#include <odp/api/crypto.h>
#include <odp/api/classification.h>
#include <odp/api/rwlock.h>
@@ -55,9 +55,15 @@ extern "C" {
#include <odp/api/errno.h>
#include <odp/api/thrmask.h>
#include <odp/api/traffic_mngr.h>
+#include <odp/api/ml.h>
+#include <odp/api/ml_quantize.h>
#include <odp/api/spinlock_recursive.h>
#include <odp/api/rwlock_recursive.h>
-#include <odp/api/std_clib.h>
+#include <odp/api/std.h>
+#include <odp/api/ipsec.h>
+#include <odp/api/stash.h>
+#include <odp/api/reassembly.h>
+#include <odp/api/dma.h>
#ifdef __cplusplus
}
diff --git a/include/odp_drv.h b/include/odp_drv.h
deleted file mode 100644
index a6d3a4433..000000000
--- a/include/odp_drv.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * The OpenDataPlane nic driver programming interface
- *
- */
-
-#ifndef ODP_DRV_H_
-#define ODP_DRV_H_
-
-#ifdef __cplusplus
-extern C {
-#endif
-
-#include <odp/drv/compiler.h>
-#include <odp/drv/std_types.h>
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
deleted file mode 100644
index c3a8d695a..000000000
--- a/m4/ax_check_compile_flag.m4
+++ /dev/null
@@ -1,72 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
-#
-# DESCRIPTION
-#
-# Check whether the given FLAG works with the current language's compiler
-# or gives an error. (Warnings, however, are ignored)
-#
-# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
-# success/failure.
-#
-# If EXTRA-FLAGS is defined, it is added to the current language's default
-# flags (e.g. CFLAGS) when the check is done. The check is thus made with
-# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
-# force the compiler to issue an error when a bad flag is given.
-#
-# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
-# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 2
-
-AC_DEFUN([AX_CHECK_COMPILE_FLAG],
-[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
-AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
-AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
- ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
- _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
- [AS_VAR_SET(CACHEVAR,[yes])],
- [AS_VAR_SET(CACHEVAR,[no])])
- _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
-AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
- [m4_default([$2], :)],
- [m4_default([$3], :)])
-AS_VAR_POPDEF([CACHEVAR])dnl
-])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4
deleted file mode 100644
index 39ca3c0f3..000000000
--- a/m4/ax_compiler_vendor.m4
+++ /dev/null
@@ -1,87 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_COMPILER_VENDOR
-#
-# DESCRIPTION
-#
-# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun,
-# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft,
-# watcom, etc. The vendor is returned in the cache variable
-# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
-# Copyright (c) 2008 Matteo Frigo
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 15
-
-AC_DEFUN([AX_COMPILER_VENDOR],
-[AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor,
- dnl Please add if possible support to ax_compiler_version.m4
- [# note: don't check for gcc first since some other compilers define __GNUC__
- vendors="intel: __ICC,__ECC,__INTEL_COMPILER
- ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__
- pathscale: __PATHCC__,__PATHSCALE__
- clang: __clang__
- cray: _CRAYC
- fujitsu: __FUJITSU
- gnu: __GNUC__
- sun: __SUNPRO_C,__SUNPRO_CC
- hp: __HP_cc,__HP_aCC
- dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER
- borland: __BORLANDC__,__CODEGEARC__,__TURBOC__
- comeau: __COMO__
- kai: __KCC
- lcc: __LCC__
- sgi: __sgi,sgi
- microsoft: _MSC_VER
- metrowerks: __MWERKS__
- watcom: __WATCOMC__
- portland: __PGI
- tcc: __TINYC__
- unknown: UNKNOWN"
- for ventest in $vendors; do
- case $ventest in
- *:) vendor=$ventest; continue ;;
- *) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;;
- esac
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
- #if !($vencpp)
- thisisanerror;
- #endif
- ])], [break])
- done
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1`
- ])
-])
diff --git a/m4/ax_compiler_version.m4 b/m4/ax_compiler_version.m4
deleted file mode 100644
index 0863118c6..000000000
--- a/m4/ax_compiler_version.m4
+++ /dev/null
@@ -1,492 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_compiler_version.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_COMPILER_VERSION
-#
-# DESCRIPTION
-#
-# This macro retrieves the compiler version and returns it in the cache
-# variable $ax_cv_c_compiler_version for C and $ax_cv_cxx_compiler_version
-# for C++.
-#
-# Version is returned as epoch:major.minor.patchversion
-#
-# Epoch is used in order to have an increasing version number in case of
-# marketing change.
-#
-# Epoch use: * borland compiler use chronologically 0turboc for turboc
-# era,
-#
-# 1borlanc BORLANC++ before 5, 2cppbuilder for cppbuilder era,
-# 3borlancpp for return of BORLANC++ (after version 5.5),
-# 4cppbuilder for cppbuilder with year version,
-# and 5xe for XE era.
-#
-# An empty string is returned otherwise.
-#
-# LICENSE
-#
-# Copyright (c) 2014 Bastien ROUCARIES <roucaries.bastien+autoconf@gmail.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 4
-
-# for intel
-AC_DEFUN([_AX_COMPILER_VERSION_INTEL],
- [ dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [__INTEL_COMPILER/100],,
- AC_MSG_FAILURE([[[$0]] unknown intel compiler version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [(__INTEL_COMPILER%100)/10],,
- AC_MSG_FAILURE([[[$0]] unknown intel compiler version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [(__INTEL_COMPILER%10)],,
- AC_MSG_FAILURE([[[$0]] unknown intel compiler version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# for IBM
-AC_DEFUN([_AX_COMPILER_VERSION_IBM],
- [ dnl
- dnl check between z/OS C/C++ and XL C/C++
- AC_COMPILE_IFELSE([
- AC_LANG_PROGRAM([],
- [
- #if defined(__COMPILER_VER__)
- choke me;
- #endif
- ])],
- [
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [__xlC__/100],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler major version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [__xlC__%100],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__xlC_ver__/0x100],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_build,
- [__xlC_ver__%0x100],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler build version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_build"
- ],
- [
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__xlC__%1000],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [(__xlC__/10000)%10],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [(__xlC__/100000)%10],,
- AC_MSG_FAILURE([[[$0]] unknown IBM compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-])
-
-# for pathscale
-AC_DEFUN([_AX_COMPILER_VERSION_PATHSCALE],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- __PATHCC__,,
- AC_MSG_FAILURE([[[$0]] unknown pathscale major]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- __PATHCC_MINOR__,,
- AC_MSG_FAILURE([[[$0]] unknown pathscale minor]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__PATHCC_PATCHLEVEL__],,
- AC_MSG_FAILURE([[[$0]] unknown pathscale patch level]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# for clang
-AC_DEFUN([_AX_COMPILER_VERSION_CLANG],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- __clang_major__,,
- AC_MSG_FAILURE([[[$0]] unknown clang major]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- __clang_minor__,,
- AC_MSG_FAILURE([[[$0]] unknown clang minor]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__clang_patchlevel__],,0)
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# for crayc
-AC_DEFUN([_AX_COMPILER_VERSION_CRAY],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- _RELEASE,,
- AC_MSG_FAILURE([[[$0]] unknown crayc release]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- _RELEASE_MINOR,,
- AC_MSG_FAILURE([[[$0]] unknown crayc minor]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor"
- ])
-
-# for fujitsu
-AC_DEFUN([_AX_COMPILER_VERSION_FUJITSU],[
- AC_COMPUTE_INT(ax_cv_[]_AC_LANG_ABBREV[]_compiler_version,
- __FCC_VERSION,,
- AC_MSG_FAILURE([[[$0]]unknown fujitsu release]))
- ])
-
-# for GNU
-AC_DEFUN([_AX_COMPILER_VERSION_GNU],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- __GNUC__,,
- AC_MSG_FAILURE([[[$0]] unknown gcc major]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- __GNUC_MINOR__,,
- AC_MSG_FAILURE([[[$0]] unknown gcc minor]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__GNUC_PATCHLEVEL__],,
- AC_MSG_FAILURE([[[$0]] unknown gcc patch level]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# For sun
-AC_DEFUN([_AX_COMPILER_VERSION_SUN],[
- m4_define([_AX_COMPILER_VERSION_SUN_NUMBER],
- [
- #if defined(__SUNPRO_CC)
- __SUNPRO_CC
- #else
- __SUNPRO_C
- #endif
- ])
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_until59,
- !!(_AX_COMPILER_VERSION_SUN_NUMBER < 0x1000),,
- AC_MSG_FAILURE([[[$0]] unknown sun release version]))
- AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_until59" = X1],
- [dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- _AX_COMPILER_VERSION_SUN_NUMBER % 0x10,,
- AC_MSG_FAILURE([[[$0]] unknown sun patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- (_AX_COMPILER_VERSION_SUN_NUMBER / 0x10) % 0x10,,
- AC_MSG_FAILURE([[[$0]] unknown sun minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (_AX_COMPILER_VERSION_SUN_NUMBER / 0x100),,
- AC_MSG_FAILURE([[[$0]] unknown sun major version]))
- ],
- [dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- _AX_COMPILER_VERSION_SUN_NUMBER % 0x10,,
- AC_MSG_FAILURE([[[$0]] unknown sun patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- (_AX_COMPILER_VERSION_SUN_NUMBER / 0x100) % 0x100,,
- AC_MSG_FAILURE([[[$0]] unknown sun minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (_AX_COMPILER_VERSION_SUN_NUMBER / 0x1000),,
- AC_MSG_FAILURE([[[$0]] unknown sun major version]))
- ])
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
-])
-
-AC_DEFUN([_AX_COMPILER_VERSION_HP],[
- m4_define([_AX_COMPILER_VERSION_HP_NUMBER],
- [
- #if defined(__HP_cc)
- __HP_cc
- #else
- __HP_aCC
- #endif
- ])
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_untilA0121,
- !!(_AX_COMPILER_VERSION_HP_NUMBER <= 1),,
- AC_MSG_FAILURE([[[$0]] unknown hp release version]))
- AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_untilA0121" = X1],
- [dnl By default output last version with this behavior.
- dnl it is so old
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="01.21.00"
- ],
- [dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- (_AX_COMPILER_VERSION_HP_NUMBER % 100),,
- AC_MSG_FAILURE([[[$0]] unknown hp release version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- ((_AX_COMPILER_VERSION_HP_NUMBER / 100)%100),,
- AC_MSG_FAILURE([[[$0]] unknown hp minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- ((_AX_COMPILER_VERSION_HP_NUMBER / 10000)%100),,
- AC_MSG_FAILURE([[[$0]] unknown hp major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-])
-
-AC_DEFUN([_AX_COMPILER_VERSION_DEC],[dnl
- m4_define([_AX_COMPILER_VERSION_DEC_NUMBER],
- [
- #if defined(__DECC_VER)
- __DECC_VER
- #else
- __DECCXX_VER
- #endif
- ])
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- (_AX_COMPILER_VERSION_DEC_NUMBER % 10000),,
- AC_MSG_FAILURE([[[$0]] unknown dec release version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- ((_AX_COMPILER_VERSION_DEC_NUMBER / 100000UL)%100),,
- AC_MSG_FAILURE([[[$0]] unknown dec minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- ((_AX_COMPILER_VERSION_DEC_NUMBER / 10000000UL)%100),,
- AC_MSG_FAILURE([[[$0]] unknown dec major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# borland
-AC_DEFUN([_AX_COMPILER_VERSION_BORLAND],[dnl
- m4_define([_AX_COMPILER_VERSION_TURBOC_NUMBER],
- [
- #if defined(__TURBOC__)
- __TURBOC__
- #else
- choke me
- #endif
- ])
- m4_define([_AX_COMPILER_VERSION_BORLANDC_NUMBER],
- [
- #if defined(__BORLANDC__)
- __BORLANDC__
- #else
- __CODEGEARC__
- #endif
- ])
- AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(,
- _AX_COMPILER_VERSION_TURBOC_NUMBER)],
- [dnl TURBOC
- AC_COMPUTE_INT(
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw,
- _AX_COMPILER_VERSION_TURBOC_NUMBER,,
- AC_MSG_FAILURE([[[$0]] unknown turboc version]))
- AS_IF(
- [test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw -lt 661 || test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw -gt 1023],
- [dnl compute normal version
- AC_COMPUTE_INT(
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- _AX_COMPILER_VERSION_TURBOC_NUMBER % 0x100,,
- AC_MSG_FAILURE([[[$0]] unknown turboc minor version]))
- AC_COMPUTE_INT(
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (_AX_COMPILER_VERSION_TURBOC_NUMBER/0x100)%0x100,,
- AC_MSG_FAILURE([[[$0]] unknown turboc major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor"],
- [dnl special version
- AS_CASE([$_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw],
- [661],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:1.00"],
- [662],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:1.01"],
- [663],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:2.00"],
- [
- AC_MSG_WARN([[[$0]] unknown turboc version between 0x295 and 0x400 please report bug])
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version=""
- ])
- ])
- ],
- # borlandc
- [
- AC_COMPUTE_INT(
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw,
- _AX_COMPILER_VERSION_BORLANDC_NUMBER,,
- AC_MSG_FAILURE([[[$0]] unknown borlandc version]))
- AS_CASE([$_ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw],
- dnl BORLANC++ before 5.5
- [512] ,[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:2.00"],
- [1024],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.00"],
- [1024],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.00"],
- [1040],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.1"],
- [1106],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:4.0"],
- [1280],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:5.0"],
- [1312],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:5.02"],
- dnl C++ Builder era
- [1328],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="2cppbuilder:3.0"],
- [1344],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="2cppbuilder:4.0"],
- dnl BORLANC++ after 5.5
- [1360],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.5"],
- [1361],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.51"],
- [1378],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.6.4"],
- dnl C++ Builder with year number
- [1392],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2006"],
- [1424],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2007"],
- [1555],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2009"],
- [1569],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2010"],
- dnl XE version
- [1584],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe"],
- [1600],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:2"],
- [1616],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:3"],
- [1632],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:4"],
- [
- AC_MSG_WARN([[[$0]] Unknown borlanc compiler version $_ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw please report bug])
- ])
- ])
- ])
-
-# COMO
-AC_DEFUN([_AX_COMPILER_VERSION_COMEAU],
- [ dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [__COMO_VERSION__%100],,
- AC_MSG_FAILURE([[[$0]] unknown comeau compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [(__COMO_VERSION__/100)%10],,
- AC_MSG_FAILURE([[[$0]] unknown comeau compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor"
- ])
-
-# KAI
-AC_DEFUN([_AX_COMPILER_VERSION_KAI],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__KCC_VERSION%100],,
- AC_MSG_FAILURE([[[$0]] unknown kay compiler patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [(__KCC_VERSION/100)%10],,
- AC_MSG_FAILURE([[[$0]] unknown kay compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [(__KCC_VERSION/1000)%10],,
- AC_MSG_FAILURE([[[$0]] unknown kay compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-dnl LCC
-dnl LCC does not output version...
-
-# SGI
-AC_DEFUN([_AX_COMPILER_VERSION_SGI],[
- m4_define([_AX_COMPILER_VERSION_SGI_NUMBER],
- [
- #if defined(_COMPILER_VERSION)
- _COMPILER_VERSION
- #else
- _SGI_COMPILER_VERSION
- #endif
- ])
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [_AX_COMPILER_VERSION_SGI_NUMBER%10],,
- AC_MSG_FAILURE([[[$0]] unknown SGI compiler patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- [(_AX_COMPILER_VERSION_SGI_NUMBER/10)%10],,
- AC_MSG_FAILURE([[[$0]] unknown SGI compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- [(_AX_COMPILER_VERSION_SGI_NUMBER/100)%10],,
- AC_MSG_FAILURE([[[$0]] unknown SGI compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# microsoft
-AC_DEFUN([_AX_COMPILER_VERSION_MICROSOFT],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- _MSC_VER%100,,
- AC_MSG_FAILURE([[[$0]] unknown microsoft compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (_MSC_VER/100)%100,,
- AC_MSG_FAILURE([[[$0]] unknown microsoft compiler major version]))
- dnl could be overridden
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_patch=0
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_build=0
- # special case for version 6
- AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major" = "X12"],
- [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- _MSC_FULL_VER%1000,,
- _ax_[]_AC_LANG_ABBREV[]_compiler_version_patch=0)])
- # for version 7
- AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major" = "X13"],
- [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- _MSC_FULL_VER%1000,,
- AC_MSG_FAILURE([[[$0]] unknown microsoft compiler patch version]))
- ])
- # for version > 8
- AS_IF([test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_major -ge 14],
- [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- _MSC_FULL_VER%10000,,
- AC_MSG_FAILURE([[[$0]] unknown microsoft compiler patch version]))
- ])
- AS_IF([test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_major -ge 15],
- [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_build,
- _MSC_BUILD,,
- AC_MSG_FAILURE([[[$0]] unknown microsoft compiler build version]))
- ])
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_build"
- ])
-
-# for metrowerks
-AC_DEFUN([_AX_COMPILER_VERSION_METROWERKS],[dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- __MWERKS__%0x100,,
- AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler patch version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- (__MWERKS__/0x100)%0x10,,
- AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (__MWERKS__/0x1000)%0x10,,
- AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# for watcom
-AC_DEFUN([_AX_COMPILER_VERSION_WATCOM],[dnl
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- __WATCOMC__%100,,
- AC_MSG_FAILURE([[[$0]] unknown watcom compiler minor version]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- (__WATCOMC__/100)%100,,
- AC_MSG_FAILURE([[[$0]] unknown watcom compiler major version]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor"
- ])
-
-# for PGI
-AC_DEFUN([_AX_COMPILER_VERSION_PORTLAND],[
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major,
- __PGIC__,,
- AC_MSG_FAILURE([[[$0]] unknown pgi major]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor,
- __PGIC_MINOR__,,
- AC_MSG_FAILURE([[[$0]] unknown pgi minor]))
- AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch,
- [__PGIC_PATCHLEVEL__],,
- AC_MSG_FAILURE([[[$0]] unknown pgi patch level]))
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch"
- ])
-
-# tcc
-AC_DEFUN([_AX_COMPILER_VERSION_TCC],[
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version=[`tcc -v | $SED 's/^[ ]*tcc[ ]\+version[ ]\+\([0-9.]\+\).*/\1/g'`]
- ])
-# main entry point
-AC_DEFUN([AX_COMPILER_VERSION],[dnl
- AC_REQUIRE([AX_COMPILER_VENDOR])
- AC_REQUIRE([AC_PROG_SED])
- AC_CACHE_CHECK([for _AC_LANG compiler version],
- ax_cv_[]_AC_LANG_ABBREV[]_compiler_version,
- [ dnl
- AS_CASE([$ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor],
- [intel],[_AX_COMPILER_VERSION_INTEL],
- [ibm],[_AX_COMPILER_VERSION_IBM],
- [pathscale],[_AX_COMPILER_VERSION_PATHSCALE],
- [clang],[_AX_COMPILER_VERSION_CLANG],
- [cray],[_AX_COMPILER_VERSION_CRAY],
- [fujitsu],[_AX_COMPILER_VERSION_FUJITSU],
- [gnu],[_AX_COMPILER_VERSION_GNU],
- [sun],[_AX_COMPILER_VERSION_SUN],
- [hp],[_AX_COMPILER_VERSION_HP],
- [dec],[_AX_COMPILER_VERSION_DEC],
- [borland],[_AX_COMPILER_VERSION_BORLAND],
- [comeau],[_AX_COMPILER_VERSION_COMEAU],
- [kai],[_AX_COMPILER_VERSION_KAI],
- [sgi],[_AX_COMPILER_VERSION_SGI],
- [microsoft],[_AX_COMPILER_VERSION_MICROSOFT],
- [metrowerks],[_AX_COMPILER_VERSION_METROWERKS],
- [watcom],[_AX_COMPILER_VERSION_WATCOM],
- [portland],[_AX_COMPILER_VERSION_PORTLAND],
- [tcc],[_AX_COMPILER_VERSION_TCC],
- [ax_cv_[]_AC_LANG_ABBREV[]_compiler_version=""])
- ])
-])
diff --git a/m4/ax_prog_doxygen.m4 b/m4/ax_prog_doxygen.m4
index 426ba0dfb..7ffcbe4c9 100644
--- a/m4/ax_prog_doxygen.m4
+++ b/m4/ax_prog_doxygen.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
+# https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
# ===========================================================================
#
# SYNOPSIS
@@ -21,10 +21,10 @@
# The DX_*_FEATURE macros control the default setting for the given
# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
-# help (for MS users), 'CHI' for generating a seperate .chi file by the
+# help (for MS users), 'CHI' for generating a separate .chi file by the
# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
# output formats. The environment variable DOXYGEN_PAPER_SIZE may be
-# specified to override the default 'a4wide' paper size.
+# specified to override the default 'a4' paper size.
#
# By default, HTML, PDF and PS documentation is generated as this seems to
# be the most popular and portable combination. MAN pages created by
@@ -97,7 +97,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 18
+#serial 24
## ----------##
## Defaults. ##
@@ -164,7 +164,7 @@ AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
AC_DEFUN([DX_CHECK_DEPEND], [
test "$DX_FLAG_$1" = "$2" \
|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
- requires, contradicts) doxygen-DX_CURRENT_FEATURE])
+ requires, contradicts) doxygen-$1])
])
# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
@@ -265,14 +265,11 @@ m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1,
[m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])],
[])))dnl
-# Environment variables used inside Doxyfile:
+# Environment variables used inside doxygen.cfg:
DX_ENV_APPEND(SRCDIR, $srcdir)
-DX_ENV_APPEND(BUILDDIR, $builddir)
-DX_ENV_APPEND(VERSION, $VERSION)
-DX_ENV_APPEND(WITH_PLATFORM, $with_platform)
DX_ENV_APPEND(PROJECT, $DX_PROJECT)
-DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
-DX_ENV_APPEND(WITH_ARCH, $ARCH_DIR)
+DX_ENV_APPEND(ODP_VERSION_API, $ODP_VERSION_API)
+DX_ENV_APPEND(ODPH_VERSION, $ODPH_VERSION)
# Doxygen itself:
DX_ARG_ABLE(doc, [generate any doxygen documentation],
@@ -325,8 +322,8 @@ DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
[DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
-# Seperate CHI file generation.
-DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
+# Separate CHI file generation.
+DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file],
[DX_CHECK_DEPEND(chm, 1)],
[DX_CLEAR_DEPEND(chm, 1)],
[],
@@ -367,13 +364,13 @@ fi
# Paper size for PS and/or PDF:
AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
- [a4wide (default), a4, letter, legal or executive])
+ [a4(default), letter, legal or executive])
case "$DOXYGEN_PAPER_SIZE" in
#(
"")
AC_SUBST(DOXYGEN_PAPER_SIZE, "")
;; #(
-a4wide|a4|letter|legal|executive)
+a4|letter|legal|executive)
DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
;; #(
*)
@@ -382,94 +379,82 @@ a4wide|a4|letter|legal|executive)
esac
# Rules:
-if test $DX_FLAG_html -eq 1; then
- DX_SNIPPET_html="## ------------------------------- ##
+AS_IF([[test $DX_FLAG_html -eq 1]],
+[[DX_SNIPPET_html="## ------------------------------- ##
## Rules specific for HTML output. ##
## ------------------------------- ##
-DX_CLEAN_HTML = \$(DX_DOCDIR)/html[]dnl
+DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/html]])
+ \$(DX_DOCDIR]DX_i[)/html]])[
-"
-else
- DX_SNIPPET_html=""
-fi
-if test $DX_FLAG_chi -eq 1; then
- DX_SNIPPET_chi="
-DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi[]dnl
+"]],
+[[DX_SNIPPET_html=""]])
+AS_IF([[test $DX_FLAG_chi -eq 1]],
+[[DX_SNIPPET_chi="
+DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])"
-else
- DX_SNIPPET_chi=""
-fi
-if test $DX_FLAG_chm -eq 1; then
- DX_SNIPPET_chm="## ------------------------------ ##
+ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]],
+[[DX_SNIPPET_chi=""]])
+AS_IF([[test $DX_FLAG_chm -eq 1]],
+[[DX_SNIPPET_chm="## ------------------------------ ##
## Rules specific for CHM output. ##
## ------------------------------ ##
-DX_CLEAN_CHM = \$(DX_DOCDIR)/chm[]dnl
+DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/chm]])\
+ \$(DX_DOCDIR]DX_i[)/chm]])[\
${DX_SNIPPET_chi}
-"
-else
- DX_SNIPPET_chm=""
-fi
-if test $DX_FLAG_man -eq 1; then
- DX_SNIPPET_man="## ------------------------------ ##
+"]],
+[[DX_SNIPPET_chm=""]])
+AS_IF([[test $DX_FLAG_man -eq 1]],
+[[DX_SNIPPET_man="## ------------------------------ ##
## Rules specific for MAN output. ##
## ------------------------------ ##
-DX_CLEAN_MAN = \$(DX_DOCDIR)/man[]dnl
+DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/man]])
+ \$(DX_DOCDIR]DX_i[)/man]])[
-"
-else
- DX_SNIPPET_man=""
-fi
-if test $DX_FLAG_rtf -eq 1; then
- DX_SNIPPET_rtf="## ------------------------------ ##
+"]],
+[[DX_SNIPPET_man=""]])
+AS_IF([[test $DX_FLAG_rtf -eq 1]],
+[[DX_SNIPPET_rtf="## ------------------------------ ##
## Rules specific for RTF output. ##
## ------------------------------ ##
-DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf[]dnl
+DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/rtf]])
+ \$(DX_DOCDIR]DX_i[)/rtf]])[
-"
-else
- DX_SNIPPET_rtf=""
-fi
-if test $DX_FLAG_xml -eq 1; then
- DX_SNIPPET_xml="## ------------------------------ ##
+"]],
+[[DX_SNIPPET_rtf=""]])
+AS_IF([[test $DX_FLAG_xml -eq 1]],
+[[DX_SNIPPET_xml="## ------------------------------ ##
## Rules specific for XML output. ##
## ------------------------------ ##
-DX_CLEAN_XML = \$(DX_DOCDIR)/xml[]dnl
+DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/xml]])
+ \$(DX_DOCDIR]DX_i[)/xml]])[
-"
-else
- DX_SNIPPET_xml=""
-fi
-if test $DX_FLAG_ps -eq 1; then
- DX_SNIPPET_ps="## ----------------------------- ##
+"]],
+[[DX_SNIPPET_xml=""]])
+AS_IF([[test $DX_FLAG_ps -eq 1]],
+[[DX_SNIPPET_ps="## ----------------------------- ##
## Rules specific for PS output. ##
## ----------------------------- ##
-DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps[]dnl
+DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])
+ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[
DX_PS_GOAL = doxygen-ps
doxygen-ps: \$(DX_CLEAN_PS)
-m4_foreach([DX_i], [DX_loop],
+]m4_foreach([DX_i], [DX_loop],
[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag
\$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\
@@ -485,25 +470,22 @@ m4_foreach([DX_i], [DX_loop],
done; \\
\$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi
-]])dnl
-"
-else
- DX_SNIPPET_ps=""
-fi
-if test $DX_FLAG_pdf -eq 1; then
- DX_SNIPPET_pdf="## ------------------------------ ##
+]])["]],
+[[DX_SNIPPET_ps=""]])
+AS_IF([[test $DX_FLAG_pdf -eq 1]],
+[[DX_SNIPPET_pdf="## ------------------------------ ##
## Rules specific for PDF output. ##
## ------------------------------ ##
-DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf[]dnl
+DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])
+ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[
DX_PDF_GOAL = doxygen-pdf
doxygen-pdf: \$(DX_CLEAN_PDF)
-m4_foreach([DX_i], [DX_loop],
+]m4_foreach([DX_i], [DX_loop],
[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag
\$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\
@@ -519,31 +501,26 @@ m4_foreach([DX_i], [DX_loop],
done; \\
mv refman.pdf ../\$(PACKAGE).pdf
-]])dnl
-"
-else
- DX_SNIPPET_pdf=""
-fi
-if test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1; then
- DX_SNIPPET_latex="## ------------------------------------------------- ##
+]])["]],
+[[DX_SNIPPET_pdf=""]])
+AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]],
+[[DX_SNIPPET_latex="## ------------------------------------------------- ##
## Rules specific for LaTeX (shared for PS and PDF). ##
## ------------------------------------------------- ##
DX_V_LATEX = \$(_DX_v_LATEX_\$(V))
_DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY))
-_DX_v_LATEX_0 = @echo \" LATEX \" \$[]][[]@;
+_DX_v_LATEX_0 = @echo \" LATEX \" \$][@;
-DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex[]dnl
+DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl
m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
- \$(DX_DOCDIR]DX_i[)/latex]])
+ \$(DX_DOCDIR]DX_i[)/latex]])[
-"
-else
- DX_SNIPPET_latex=""
-fi
+"]],
+[[DX_SNIPPET_latex=""]])
-if test $DX_FLAG_doc -eq 1; then
- DX_SNIPPET_doc="## --------------------------------- ##
+AS_IF([[test $DX_FLAG_doc -eq 1]],
+[[DX_SNIPPET_doc="## --------------------------------- ##
## Format-independent Doxygen rules. ##
## --------------------------------- ##
@@ -563,23 +540,24 @@ _DX_v_DXGEN_0 = @echo \" DXGEN \" \$<;
.INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL)
-doxygen-run:[]m4_foreach([DX_i], [DX_loop],
- [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])
+doxygen-run:]m4_foreach([DX_i], [DX_loop],
+ [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[
doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL)
-m4_foreach([DX_i], [DX_loop],
+]m4_foreach([DX_i], [DX_loop],
[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS)
\$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[)
\$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[)
\$(A""M_V_at)echo Timestamp >\$][@
]])dnl
-DX_CLEANFILES = \\
+[DX_CLEANFILES = \\]
m4_foreach([DX_i], [DX_loop],
-[[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\
+[[ \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\
+ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\
]])dnl
- -r \\
+[ -r \\
\$(DX_CLEAN_HTML) \\
\$(DX_CLEAN_CHM) \\
\$(DX_CLEAN_CHI) \\
@@ -588,19 +566,17 @@ m4_foreach([DX_i], [DX_loop],
\$(DX_CLEAN_XML) \\
\$(DX_CLEAN_PS) \\
\$(DX_CLEAN_PDF) \\
- \$(DX_CLEAN_LATEX)"
-else
- DX_SNIPPET_doc=""
-fi
+ \$(DX_CLEAN_LATEX)"]],
+[[DX_SNIPPET_doc=""]])
AC_SUBST([DX_RULES],
["${DX_SNIPPET_doc}"])dnl
AM_SUBST_NOTMAKE([DX_RULES])
#For debugging:
-echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_doc=$DX_FLAG_doc
#echo DX_FLAG_dot=$DX_FLAG_dot
#echo DX_FLAG_man=$DX_FLAG_man
-echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_html=$DX_FLAG_html
#echo DX_FLAG_chm=$DX_FLAG_chm
#echo DX_FLAG_chi=$DX_FLAG_chi
#echo DX_FLAG_rtf=$DX_FLAG_rtf
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
deleted file mode 100644
index c78b87de0..000000000
--- a/m4/ax_pthread.m4
+++ /dev/null
@@ -1,332 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-#
-# DESCRIPTION
-#
-# This macro figures out how to build C programs using POSIX threads. It
-# sets the PTHREAD_LIBS output variable to the threads library and linker
-# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
-# flags that are needed. (The user can also force certain compiler
-# flags/libs to be tested by setting these environment variables.)
-#
-# Also sets PTHREAD_CC to any special C compiler that is needed for
-# multi-threaded programs (defaults to the value of CC otherwise). (This
-# is necessary on AIX to use the special cc_r compiler alias.)
-#
-# NOTE: You are assumed to not only compile your program with these flags,
-# but also link it with them as well. e.g. you should link with
-# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
-#
-# If you are only building threads programs, you may wish to use these
-# variables in your default LIBS, CFLAGS, and CC:
-#
-# LIBS="$PTHREAD_LIBS $LIBS"
-# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-# CC="$PTHREAD_CC"
-#
-# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
-# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
-# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
-#
-# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
-# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
-# PTHREAD_CFLAGS.
-#
-# ACTION-IF-FOUND is a list of shell commands to run if a threads library
-# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
-# is not found. If ACTION-IF-FOUND is not specified, the default action
-# will define HAVE_PTHREAD.
-#
-# Please let the authors know if this macro fails on any platform, or if
-# you have any other suggestions or comments. This macro was based on work
-# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
-# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
-# Alejandro Forero Cuervo to the autoconf macro repository. We are also
-# grateful for the helpful feedback of numerous users.
-#
-# Updated for Autoconf 2.68 by Daniel Richard G.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
-# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 21
-
-AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
-AC_DEFUN([AX_PTHREAD], [
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_PUSH([C])
-ax_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
- AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
- AC_MSG_RESULT([$ax_pthread_ok])
- if test x"$ax_pthread_ok" = xno; then
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
- fi
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try. Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important. Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-# other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-# doesn't hurt to check since this sometimes defines pthreads too;
-# also defines -D_REENTRANT)
-# ... -mt is also the pthreads flag for HP/aCC
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case ${host_os} in
- solaris*)
-
- # On Solaris (at least, for some versions), libc contains stubbed
- # (non-functional) versions of the pthreads routines, so link-based
- # tests will erroneously succeed. (We need to link with -pthreads/-mt/
- # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
- # a function called by this macro, so we could check for that, but
- # who knows whether they'll stub that too in a future libc.) So,
- # we'll just look for -pthreads and -lpthread first:
-
- ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
- ;;
-
- darwin*)
- ax_pthread_flags="-pthread $ax_pthread_flags"
- ;;
-esac
-
-# Clang doesn't consider unrecognized options an error unless we specify
-# -Werror. We throw in some extra Clang-specific options to ensure that
-# this doesn't happen for GCC, which also accepts -Werror.
-
-AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
-save_CFLAGS="$CFLAGS"
-ax_pthread_extra_flags="-Werror"
-CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
- [AC_MSG_RESULT([yes])],
- [ax_pthread_extra_flags=
- AC_MSG_RESULT([no])])
-CFLAGS="$save_CFLAGS"
-
-if test x"$ax_pthread_ok" = xno; then
-for flag in $ax_pthread_flags; do
-
- case $flag in
- none)
- AC_MSG_CHECKING([whether pthreads work without any flags])
- ;;
-
- -*)
- AC_MSG_CHECKING([whether pthreads work with $flag])
- PTHREAD_CFLAGS="$flag"
- ;;
-
- pthread-config)
- AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
- if test x"$ax_pthread_config" = xno; then continue; fi
- PTHREAD_CFLAGS="`pthread-config --cflags`"
- PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
- ;;
-
- *)
- AC_MSG_CHECKING([for the pthreads library -l$flag])
- PTHREAD_LIBS="-l$flag"
- ;;
- esac
-
- save_LIBS="$LIBS"
- save_CFLAGS="$CFLAGS"
- LIBS="$PTHREAD_LIBS $LIBS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
-
- # Check for various functions. We must include pthread.h,
- # since some functions may be macros. (On the Sequent, we
- # need a special flag -Kthread to make this header compile.)
- # We check for pthread_join because it is in -lpthread on IRIX
- # while pthread_create is in libc. We check for pthread_attr_init
- # due to DEC craziness with -lpthreads. We check for
- # pthread_cleanup_push because it is one of the few pthread
- # functions on Solaris that doesn't have a non-functional libc stub.
- # We try pthread_create on general principles.
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
- static void routine(void *a) { if(a) a = 0; }
- static void *start_routine(void *a) { return a; }],
- [pthread_t th; pthread_attr_t attr;
- pthread_create(&th, 0, start_routine, 0);
- pthread_join(th, 0);
- pthread_attr_init(&attr);
- pthread_cleanup_push(routine, 0);
- pthread_cleanup_pop(0) /* ; */])],
- [ax_pthread_ok=yes],
- [])
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- AC_MSG_RESULT([$ax_pthread_ok])
- if test "x$ax_pthread_ok" = xyes; then
- break;
- fi
-
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$ax_pthread_ok" = xyes; then
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
- # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
- AC_MSG_CHECKING([for joinable pthread attribute])
- attr_name=unknown
- for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
- [int attr = $attr; return attr /* ; */])],
- [attr_name=$attr; break],
- [])
- done
- AC_MSG_RESULT([$attr_name])
- if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
- AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
- [Define to necessary symbol if this constant
- uses a non-standard name on your system.])
- fi
-
- AC_MSG_CHECKING([if more special flags are required for pthreads])
- flag=no
- case ${host_os} in
- aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
- osf* | hpux*) flag="-D_REENTRANT";;
- solaris*)
- if test "$GCC" = "yes"; then
- flag="-D_REENTRANT"
- else
- # TODO: What about Clang on Solaris?
- flag="-mt -D_REENTRANT"
- fi
- ;;
- esac
- AC_MSG_RESULT([$flag])
- if test "x$flag" != xno; then
- PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
- fi
-
- AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
- [ax_cv_PTHREAD_PRIO_INHERIT], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
- [[int i = PTHREAD_PRIO_INHERIT;]])],
- [ax_cv_PTHREAD_PRIO_INHERIT=yes],
- [ax_cv_PTHREAD_PRIO_INHERIT=no])
- ])
- AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
- [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- # More AIX lossage: compile with *_r variant
- if test "x$GCC" != xyes; then
- case $host_os in
- aix*)
- AS_CASE(["x/$CC"],
- [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
- [#handle absolute path differently from PATH based program lookup
- AS_CASE(["x$CC"],
- [x/*],
- [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
- [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
- ;;
- esac
- fi
-fi
-
-test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
-
-AC_SUBST([PTHREAD_LIBS])
-AC_SUBST([PTHREAD_CFLAGS])
-AC_SUBST([PTHREAD_CC])
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$ax_pthread_ok" = xyes; then
- ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
- :
-else
- ax_pthread_ok=no
- $2
-fi
-AC_LANG_POP
-])dnl AX_PTHREAD
diff --git a/m4/ax_valgrind_check.m4 b/m4/ax_valgrind_check.m4
index b18babe60..36441a830 100644
--- a/m4/ax_valgrind_check.m4
+++ b/m4/ax_valgrind_check.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
+# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
# ===========================================================================
#
# SYNOPSIS
@@ -67,7 +67,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 13
+#serial 15
dnl Configured tools
m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]])
@@ -79,6 +79,10 @@ AC_DEFUN([AX_VALGRIND_DFLT],[
m4_define([en_dflt_valgrind_$1], [$2])
])dnl
+AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
+m4_foreach([vgtool], [valgrind_tool_list],
+ [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
+
AC_DEFUN([AX_VALGRIND_CHECK],[
dnl Check for --enable-valgrind
AC_ARG_ENABLE([valgrind],
@@ -175,7 +179,7 @@ valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
valgrind_quiet_0 = --quiet
valgrind_v_use = $(valgrind_v_use_$(V))
valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY))
-valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%,%,$''@):;
+valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-am,%,$''@):;
# Support running with and without libtool.
ifneq ($(LIBTOOL),)
@@ -185,11 +189,10 @@ valgrind_lt =
endif
# Use recursive makes in order to ignore errors during check
-check-valgrind:
+check-valgrind-am:
ifeq ($(VALGRIND_ENABLED),yes)
- -$(A''M_V_at)$(foreach tool,$(valgrind_enabled_tools), \
- $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-$(tool); \
- )
+ $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \
+ $(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool))
else
@echo "Need to reconfigure with --enable-valgrind"
endif
@@ -205,14 +208,16 @@ VALGRIND_LOG_COMPILER = \
$(valgrind_lt) \
$(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
-define valgrind_tool_rule =
-check-valgrind-$(1):
+define valgrind_tool_rule
+check-valgrind-$(1)-am:
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
+ifneq ($$(TESTS),)
$$(valgrind_v_use)$$(MAKE) check-TESTS \
TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \
LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \
LOG_FLAGS="$$(valgrind_$(1)_flags)" \
TEST_SUITE_LOG=test-suite-$(1).log
+endif
else ifeq ($$(VALGRIND_ENABLED),yes)
@echo "Need to reconfigure with --enable-valgrind-$(1)"
else
diff --git a/m4/odp_atomic.m4 b/m4/odp_atomic.m4
new file mode 100644
index 000000000..c87319d38
--- /dev/null
+++ b/m4/odp_atomic.m4
@@ -0,0 +1,194 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2017 Linaro Limited
+#
+
+# ODP_ATOMIC
+# ----------
+# Run different atomic-related checks
+AC_DEFUN([ODP_ATOMIC], [dnl
+ODP_ATOMIC_BUILTINS
+
+dnl Check whether -latomic is needed
+use_libatomic=no
+
+ODP_ATOMIC_NEEDED_64BIT([use_libatomic=yes])
+AC_CHECK_TYPE([__int128], [ODP_ATOMIC_NEEDED_128BIT([use_libatomic=yes])])
+
+if test "x$use_libatomic" = "xyes"; then
+ ATOMIC_LIBS="-latomic"
+fi
+AC_SUBST([ATOMIC_LIBS])
+
+# In non-abi-compat mode libatomic is exposed to the application
+if test $ODP_ABI_COMPAT -eq 1; then
+ ATOMIC_LIBS_ABI_COMPAT=$ATOMIC_LIBS
+ AC_SUBST([ATOMIC_LIBS_ABI_COMPAT])
+else
+ ATOMIC_LIBS_NON_ABI_COMPAT=$ATOMIC_LIBS
+ AC_SUBST([ATOMIC_LIBS_NON_ABI_COMPAT])
+fi
+
+# Double wide __atomic_compare_exchange_n is required by ipfragreass example
+use_libatomic_opt=no;
+have_atomic_cmp_exc=yes;
+
+AC_CHECK_SIZEOF([void *])
+AC_PREPROC_IFELSE(
+ [AC_LANG_SOURCE([
+ #if SIZEOF_VOID_P == 8
+ #error
+ #endif
+ ])], [plat64=no], [plat64=yes])
+
+if test "x$plat64" = "xyes"; then
+ ODP_ATOMIC_NEEDED_128BIT_CMP_EXC([use_libatomic_opt=yes], [have_atomic_cmp_exc=no])
+else
+ ODP_ATOMIC_NEEDED_64BIT_CMP_EXC([use_libatomic_opt=yes], [have_atomic_cmp_exc=no])
+fi
+
+if test "x$use_libatomic_opt" = "xyes"; then
+ ATOMIC_LIBS_OPT="-latomic"
+fi
+AC_SUBST([ATOMIC_LIBS_OPT])
+AM_CONDITIONAL([HAVE_DW_ATOMIC_CMP_EXC], [test x$have_atomic_cmp_exc = xyes])
+]) # ODP_ATOMIC
+
+# ODP_ATOMIC_BUILTINS
+# -------------------
+#
+AC_DEFUN([ODP_ATOMIC_BUILTINS], [dnl
+AC_CACHE_CHECK([for GCC atomic builtins], [odp_cv_atomic_builtins], [dnl
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE(
+ [[int main() {
+ int v = 1;
+ __atomic_fetch_add(&v, 1, __ATOMIC_RELAXED);
+ __atomic_fetch_sub(&v, 1, __ATOMIC_RELAXED);
+ __atomic_store_n(&v, 1, __ATOMIC_RELAXED);
+ __atomic_load_n(&v, __ATOMIC_RELAXED);
+ return 0;
+ }
+ ]])],
+ [odp_cv_atomic_builtins=yes],
+ [odp_cv_atomic_builtins=no])])
+
+if test "x$odp_cv_atomic_builtins" != "xyes" ; then
+ AC_MSG_FAILURE([GCC-style __atomic builtins not supported by the compiler, use gcc > 4.7.0])
+fi
+]) # ODP_ATOMIC_BUILTINS
+
+# ODP_ATOMIC_NEEDED_64BIT([ACTION_IF_NEEDED])
+# -------------------------------------------
+#
+AC_DEFUN([ODP_ATOMIC_NEEDED_64BIT], [dnl
+AC_CACHE_CHECK([whether -latomic is needed for 64-bit atomic built-ins],
+ [odp_cv_atomic_needed_64bit], [dnl
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdint.h>
+ static uint64_t loc;
+ int main(void)
+ {
+ uint64_t prev = __atomic_exchange_n(&loc, 7, __ATOMIC_RELAXED);
+ return 0;
+ }
+ ]])],
+ [odp_cv_atomic_needed_64bit=no],
+ [odp_cv_atomic_needed_64bit=yes])])
+
+if test "x$odp_cv_atomic_needed_64bit" = "xyes" ; then
+ AC_CHECK_LIB(
+ [atomic], [__atomic_exchange_8],
+ [m4_default([$1], [:])],
+ [AC_MSG_FAILURE([__atomic_exchange_8 is not available])])
+fi
+]) # ODP_ATOMIC_NEEDED_64BIT
+
+# ODP_ATOMIC_NEEDED_128BIT([ACTION_IF_NEEDED])
+# -------------------------------------------
+#
+AC_DEFUN([ODP_ATOMIC_NEEDED_128BIT], [dnl
+AC_CACHE_CHECK([whether -latomic is needed for 128-bit atomic built-ins],
+ [odp_cv_atomic_needed_128bit], [dnl
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdint.h>
+ static __int128 loc;
+ int main(void)
+ {
+ __int128 prev = __atomic_exchange_n(&loc, 7, __ATOMIC_RELAXED);
+ return 0;
+ }
+ ]])],
+ [odp_cv_atomic_needed_128bit=no],
+ [odp_cv_atomic_needed_128bit=yes])])
+
+if test "x$odp_cv_atomic_needed_128bit" = "xyes" ; then
+ AC_CHECK_LIB(
+ [atomic], [__atomic_exchange_16],
+ [m4_default([$1], [:])],
+ [AC_MSG_FAILURE([__atomic_exchange_16 is not available])])
+fi
+]) # ODP_ATOMIC_NEEDED_128BIT
+
+# ODP_ATOMIC_NEEDED_64BIT_CMP_EXC([ACTION_IF_NEEDED], [ACTION_IF_NOT_AVAILABLE])
+# ------------------------------------------------------------------------------
+#
+AC_DEFUN([ODP_ATOMIC_NEEDED_64BIT_CMP_EXC], [dnl
+AC_CACHE_CHECK([whether -latomic is needed for 64-bit atomic compare exchange],
+ [odp_cv_atomic_needed_64bit_cmp_exc], [dnl
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdbool.h>
+ #include <stdint.h>
+ static uint64_t loc;
+ int main(void)
+ {
+ uint64_t exp = 0;
+ bool res = __atomic_compare_exchange_8(&loc, &exp, 1, 1,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_RELAXED);
+ return 0;
+ }
+ ]])],
+ [odp_cv_atomic_needed_64bit_cmp_exc=no],
+ [odp_cv_atomic_needed_64bit_cmp_exc=yes])])
+
+if test "x$odp_cv_atomic_needed_64bit_cmp_exc" = "xyes" ; then
+ AC_CHECK_LIB(
+ [atomic], [__atomic_compare_exchange_8],
+ [m4_default([$1], [:])],
+ [m4_default([$2], [:])])
+fi
+
+]) # ODP_ATOMIC_NEEDED_64BIT_CMP_EXC
+
+# ODP_ATOMIC_NEEDED_128BIT_CMP_EXC([ACTION_IF_NEEDED], [ACTION_IF_NOT_AVAILABLE])
+# -------------------------------------------------------------------------------
+#
+AC_DEFUN([ODP_ATOMIC_NEEDED_128BIT_CMP_EXC], [dnl
+AC_CACHE_CHECK([whether -latomic is needed for 128-bit atomic compare exchange],
+ [odp_cv_atomic_needed_128bit_cmp_exc], [dnl
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdbool.h>
+ static __int128 loc;
+ int main(void)
+ {
+ __int128 exp = 0;
+ bool res = __atomic_compare_exchange_16(&loc, &exp, 1, 1,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_RELAXED);
+ return 0;
+ }
+ ]])],
+ [odp_cv_atomic_needed_128bit_cmp_exc=no],
+ [odp_cv_atomic_needed_128bit_cmp_exc=yes])])
+
+if test "x$odp_cv_atomic_needed_128bit_cmp_exc" = "xyes" ; then
+ AC_CHECK_LIB(
+ [atomic], [__atomic_compare_exchange_16],
+ [m4_default([$1], [:])],
+ [m4_default([$2], [:])])
+fi
+]) # ODP_ATOMIC_NEEDED_128BIT_CMP_EXC
diff --git a/m4/odp_check_flag.m4 b/m4/odp_check_flag.m4
new file mode 100644
index 000000000..012997ff2
--- /dev/null
+++ b/m4/odp_check_flag.m4
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+dnl Use -Werror in the checks below since Clang emits a warning instead of
+dnl an error when it encounters an unknown warning option.
+
+# ODP_CHECK_CFLAG(FLAG)
+# ---------------------
+# Add FLAG to ODP_CFLAGS if compiler supports that option
+AC_DEFUN([ODP_CHECK_CFLAG],
+[
+ AC_MSG_CHECKING([if $CC supports $1])
+ AC_LANG_PUSH([C])
+ saved_cflags="$CFLAGS"
+ CFLAGS="-W -Wall -Werror $ODP_CFLAGS $1"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [AC_MSG_RESULT([yes])
+ ODP_CFLAGS="$ODP_CFLAGS $1"],
+ [AC_MSG_RESULT([no])])
+ CFLAGS="$saved_cflags"
+ AC_LANG_POP([C])])
+
+# ODP_CHECK_CXXFLAG(FLAG)
+# ---------------------
+AC_DEFUN([ODP_CHECK_CXXFLAG],
+[ AC_MSG_CHECKING([if $CXX supports $1])
+ AC_LANG_PUSH([C++])
+ saved_cxxflags="$CXXFLAGS"
+ CXXFLAGS="-W -Wall -Werror $ODP_CXXFLAGS $1"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [AC_MSG_RESULT([yes])
+ ODP_CXXFLAGS="$ODP_CXXFLAGS $1"],
+ [AC_MSG_RESULT([no])])
+ CXXFLAGS="$saved_cxxflags"
+ AC_LANG_POP([C++])])
diff --git a/m4/odp_dpdk.m4 b/m4/odp_dpdk.m4
new file mode 100644
index 000000000..cfde09c94
--- /dev/null
+++ b/m4/odp_dpdk.m4
@@ -0,0 +1,249 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2017 Linaro Limited
+#
+
+# ODP_DPDK_PMDS(DPDK_DRIVER_PATH)
+# -------------------------------
+# Update DPDK_LIBS to include dependencies.
+AC_DEFUN([ODP_DPDK_PMDS], [dnl
+AC_MSG_NOTICE([Looking for DPDK PMDs at $1])
+for filename in "$1"/librte_pmd_*.a; do
+cur_driver=`basename "$filename" .a | sed -e 's/^lib//'`
+
+# Match pattern is filled to 'filename' once if no matches are found
+AS_IF([test "x$cur_driver" = "xrte_pmd_*"], [break])
+
+AS_CASE([$cur_driver],
+ [rte_pmd_nfp], [AS_VAR_APPEND([DPDK_LIBS], [" -lm"])],
+ [rte_pmd_mlx4], [AS_VAR_APPEND([DPDK_LIBS], [" -lmlx4 -libverbs"])],
+ [rte_pmd_mlx5], [AS_VAR_APPEND([DPDK_LIBS], [" -lmlx5 -libverbs -lmnl"])],
+ [rte_pmd_pcap], [AS_VAR_APPEND([DPDK_LIBS], [" -lpcap"])],
+ [rte_pmd_aesni_gcm], [AS_VAR_APPEND([DPDK_LIBS], [" -lIPSec_MB"])],
+ [rte_pmd_aesni_mb], [AS_VAR_APPEND([DPDK_LIBS], [" -lIPSec_MB"])],
+ [rte_pmd_kasumi], [AS_VAR_APPEND([DPDK_LIBS], [" -lsso_kasumi"])],
+ [rte_pmd_snow3g], [AS_VAR_APPEND([DPDK_LIBS], [" -lsso_snow3g"])],
+ [rte_pmd_zuc], [AS_VAR_APPEND([DPDK_LIBS], [" -lsso_zuc"])],
+ [rte_pmd_qat], [AS_VAR_APPEND([DPDK_LIBS], [" -lcrypto"])],
+ [rte_pmd_octeontx2], [AS_VAR_APPEND([DPDK_LIBS], [" -lm"])],
+ [rte_pmd_openssl], [AS_VAR_APPEND([DPDK_LIBS], [" -lcrypto"])])
+done
+have_pmd_pcap=no
+if [[ -f "$1"/librte_pmd_pcap.a ]]; then
+ have_pmd_pcap=yes
+fi
+])
+
+# _ODP_DPDK_SET_LIBS
+# --------------------
+# Set DPDK_LIBS/DPDK_LIBS_LT/DPDK_LIBS_LIBODP depending on DPDK setup
+AC_DEFUN([_ODP_DPDK_SET_LIBS], [dnl
+ODP_DPDK_PMDS([$DPDK_PMD_PATH])
+DPDK_LIB="-Wl,--whole-archive,-ldpdk,--no-whole-archive"
+AS_IF([test "x$DPDK_SHARED" = "xyes"], [dnl
+ if test x$enable_static_applications != xyes; then
+ if test $ODP_ABI_COMPAT -eq 1; then
+ # applications don't need to be linked to anything, just rpath
+ DPDK_LIBS_LT="$DPDK_RPATH_LT"
+ else
+ # dpdk symbols may be visible to applications
+ DPDK_LIBS_LT="$DPDK_LDFLAGS -ldpdk"
+ fi
+ else
+ # static linking flags will need -ldpdk
+ DPDK_LIBS_LT="$DPDK_LDFLAGS $DPDK_LIB $DPDK_LIBS"
+ fi
+ DPDK_LIBS="-Wl,--no-as-needed,-ldpdk,--as-needed,`echo $DPDK_LIBS | sed -e 's/ /,/g'`"
+ DPDK_LIBS="$DPDK_LDFLAGS $DPDK_RPATH $DPDK_LIBS"
+ # link libodp-linux with -ldpdk
+ DPDK_LIBS_LIBODP="$DPDK_LIBS"
+], [dnl
+ # build long list of libraries for applications, which should not be
+ # rearranged by libtool
+ DPDK_LIBS_LT="`echo $DPDK_LIBS | sed -e 's/^/-Wc,/' -e 's/ /,/g'`"
+ DPDK_LIBS_LT="$DPDK_LDFLAGS $DPDK_LIB $DPDK_LIBS_LT $DPDK_LIBS"
+ # static linking flags follow the suite
+ DPDK_LIBS="$DPDK_LDFLAGS $DPDK_LIB $DPDK_LIBS"
+ # link libodp-linux with libtool linking flags
+ DPDK_LIBS_LIBODP="$DPDK_LIBS_LT"
+])
+
+OLD_LIBS=$LIBS
+LIBS="-lnuma"
+AC_TRY_LINK_FUNC([numa_num_configured_nodes],
+ [AC_DEFINE([_ODP_HAVE_NUMA_LIBRARY], [1],
+ [Define to 1 if numa library is usable])
+ AS_VAR_APPEND([DPDK_LIBS_LIBODP], [" -lnuma"])])
+LIBS=$OLD_LIBS
+
+AC_SUBST([DPDK_LIBS])
+AC_SUBST([DPDK_LIBS_LIBODP])
+AC_SUBST([DPDK_LIBS_LT])
+])
+
+# _ODP_DPDK_CHECK_LIB(LDFLAGS, [LIBS])
+# ----------------------------------
+# Check if one can use -ldpdk with provided set of libs
+AC_DEFUN([_ODP_DPDK_CHECK_LIB], [dnl
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_LDFLAGS=$LDFLAGS
+OLD_LIBS=$LIBS
+LDFLAGS="$1 $LDFLAGS"
+LIBS="$LIBS -ldpdk $2"
+
+AC_MSG_CHECKING([for rte_eal_init in -ldpdk $2])
+AC_LINK_IFELSE([AC_LANG_CALL([], [rte_eal_init])],
+ [AC_MSG_RESULT([yes])
+ DPDK_LIBS="$2"],
+ [AC_MSG_RESULT([no])])
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+LDFLAGS=$OLD_LDFLAGS
+LIBS=$OLD_LIBS
+])
+
+# _ODP_DPDK_CHECK(CPPFLAGS, LDFLAGS, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND)
+# ------------------------------------------------------------------------
+# Check for DPDK availability
+AC_DEFUN([_ODP_DPDK_CHECK], [dnl
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_CPPFLAGS=$CPPFLAGS
+CPPFLAGS="$1 $CPPFLAGS"
+
+dpdk_check_ok=yes
+
+AC_CHECK_HEADERS([rte_config.h], [],
+ [dpdk_check_ok=no])
+
+DPDK_LIBS=""
+_ODP_DPDK_CHECK_LIB([$2], [-lm])
+AS_IF([test "x$DPDK_LIBS" = "x"],
+ [_ODP_DPDK_CHECK_LIB([$2], [-lm -ldl -lpthread])])
+AS_IF([test "x$DPDK_LIBS" = "x"],
+ [_ODP_DPDK_CHECK_LIB([$2], [-lm -ldl -lpthread -lnuma])])
+AS_IF([test "x$DPDK_LIBS" = "x"],
+ [dpdk_check_ok=no])
+AS_IF([test "x$dpdk_check_ok" != "xno"],
+ [_ODP_DPDK_SET_LIBS
+ AC_SUBST([DPDK_CFLAGS])
+ $3],
+ [$4])
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+CPPFLAGS=$OLD_CPPFLAGS
+])
+
+# _ODP_DPDK_LEGACY_SYSTEM(ACTION-IF-FOUND, ACTION-IF-NOT-FOUND)
+# ------------------------------------------------------------------------
+# Locate DPDK installation
+AC_DEFUN([_ODP_DPDK_LEGACY_SYSTEM], [dnl
+ DPDK_CFLAGS="-isystem /usr/include/dpdk"
+ DPDK_LDFLAGS=""
+ DPDK_LIB_PATH="`$CC $AM_CFLAGS $CFLAGS $AM_LDFLAGS $LDFLAGS --print-file-name=libdpdk.so`"
+ if test "$DPDK_LIB_PATH" = "libdpdk.so" ; then
+ DPDK_LIB_PATH="`$CC $AM_CFLAGS $CFLAGS $AM_LDFLAGS $LDFLAGS --print-file-name=libdpdk.a`"
+ AS_IF([test "$DPDK_LIB_PATH" = "libdpdk.a"],
+ [AC_MSG_FAILURE([Could not locate system DPDK library directory])])
+ else
+ DPDK_SHARED=yes
+ fi
+ DPDK_LIB_PATH=`AS_DIRNAME(["$DPDK_LIB_PATH"])`
+ DPDK_PMD_PATH="$DPDK_LIB_PATH"
+ AS_IF([test "x$DPDK_SHARED" = "xyes"],
+ [AC_MSG_NOTICE([Using shared DPDK library found at $DPDK_LIB_PATH])],
+ [AC_MSG_NOTICE([Using static DPDK library found at $DPDK_LIB_PATH])])
+ _ODP_DPDK_CHECK([$DPDK_CFLAGS], [$DPDK_LDFLAGS], [$1], [$2])
+])
+
+# _ODP_DPDK_LEGACY(PATH, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND)
+# ------------------------------------------------------------------------
+# Locate DPDK installation
+AC_DEFUN([_ODP_DPDK_LEGACY], [dnl
+ DPDK_CFLAGS="-isystem $1/include/dpdk"
+ DPDK_LIB_PATH="$1/lib"
+ DPDK_LDFLAGS="-L$DPDK_LIB_PATH"
+ AS_IF([test -r "$DPDK_LIB_PATH"/libdpdk.so], [dnl
+ DPDK_RPATH="-Wl,-rpath,$DPDK_LIB_PATH"
+ DPDK_RPATH_LT="-R$DPDK_LIB_PATH"
+ DPDK_SHARED=yes],
+ [test ! -r "$DPDK_LIB_PATH"/libdpdk.a], [dnl
+ AC_MSG_FAILURE([Could not find DPDK])])
+ DPDK_PMD_PATH="$DPDK_LIB_PATH"
+ AS_IF([test "x$DPDK_SHARED" = "xyes"],
+ [AC_MSG_NOTICE([Using shared DPDK library found at $DPDK_LIB_PATH])],
+ [AC_MSG_NOTICE([Using static DPDK library found at $DPDK_LIB_PATH])])
+ _ODP_DPDK_CHECK([$DPDK_CFLAGS], [$DPDK_LDFLAGS], [$2], [$3])
+])
+
+m4_ifndef([PKG_CHECK_MODULES_STATIC],
+[m4_define([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])])dnl PKG_CHECK_MODULES_STATIC
+
+# _ODP_DPDK_PKGCONFIG (DPDK_SHARED, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND)
+# -----------------------------------------------------------------------
+# Configure DPDK using pkg-config information
+AC_DEFUN([_ODP_DPDK_PKGCONFIG], [dnl
+use_pkg_config=no
+dpdk_shared="$1"
+if test "x$dpdk_shared" = "xyes" ; then
+PKG_CHECK_MODULES([DPDK], [libdpdk],
+ [AC_MSG_NOTICE([Using shared DPDK lib via pkg-config])
+ use_pkg_config=yes
+ m4_default([$2], [:])],
+ [_ODP_DPDK_LEGACY_SYSTEM([m4_default([$2], [:])], [m4_default([$3], [:])])])
+else
+PKG_CHECK_MODULES_STATIC([DPDK], [libdpdk],
+ [AC_MSG_NOTICE([Using static DPDK lib via pkg-config])
+ use_pkg_config=yes
+ m4_default([$2], [:])],
+ [_ODP_DPDK_LEGACY_SYSTEM([m4_default([$2], [:])], [m4_default([$3], [:])])])
+fi
+if test "x$use_pkg_config" = "xyes"; then
+ if test "x$dpdk_shared" = "xyes"; then
+DPDK_LIBS_LIBODP="$DPDK_LIBS"
+ DPDK_LIBS_LT="$DPDK_LIBS"
+ # Set RPATH if library path is found
+ DPDK_LIB_PATH=$(echo "$DPDK_LIBS" | grep -o -- '-L\S*' | sed 's/^-L//')
+ if test -n "$DPDK_LIB_PATH"; then
+ DPDK_LIBS_LIBODP+=" -Wl,-rpath,$DPDK_LIB_PATH"
+ # Debian / Ubuntu has relatively recently made new-dtags the
+ # default, while others (e.g. Fedora) have not changed it. RPATH
+ # is extended recursively when resolving transitive dependencies,
+ # while RUNPATH (new-dtags) is not. We use RPATH to point to rte
+ # libraries so that they can be found when PMDs are loaded in
+ # rte_eal_init(). So we need to explicitly disable new-dtags.
+ DPDK_LIBS_LT+=" -Wl,--disable-new-dtags -R$DPDK_LIB_PATH"
+ fi
+ else
+ # Build a list of libraries, which should not be rearranged by libtool.
+ # This ensures that DPDK constructors are included properly.
+ DPDK_LIBS_LIBODP=$(echo "$DPDK_LIBS" | sed -e 's/\ *$//g' -e 's/ /,/g' -e 's/-Wl,//g')
+ DPDK_LIBS_LIBODP=$(echo "$DPDK_LIBS_LIBODP" | sed 's/-pthread/-lpthread/g')
+ DPDK_LIBS_LIBODP="-Wl,$DPDK_LIBS_LIBODP"
+ DPDK_LIBS_LT="$DPDK_LIBS_LIBODP"
+ fi
+ DPDK_LIBS=$DPDK_LIBS_LIBODP
+fi
+])
+
+# ODP_DPDK(DPDK_PATH, DPDK_SHARED, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# --------------------------------------------------------------------------
+# Check for DPDK availability
+AC_DEFUN([ODP_DPDK], [dnl
+AS_IF([test "x$1" = "xsystem"],
+ [_ODP_DPDK_PKGCONFIG($2, [m4_default([$3], [:])], [m4_default([$4], [:])])],
+ [_ODP_DPDK_LEGACY($1, [m4_default([$3], [:])], [m4_default([$4], [:])])]
+ )
+])
diff --git a/m4/odp_libconfig.m4 b/m4/odp_libconfig.m4
new file mode 100644
index 000000000..f6e4c672b
--- /dev/null
+++ b/m4/odp_libconfig.m4
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+# ODP_LIBCONFIG(PLATFORM, CONFIG-FILE-PATH)
+# -----------------------------------------
+AC_DEFUN([ODP_LIBCONFIG],
+[dnl
+##########################################################################
+# Check for libconfig availability
+##########################################################################
+PKG_CHECK_MODULES([LIBCONFIG], [libconfig], [have_libconfig=yes])
+AM_CONDITIONAL([LIBCONFIG], [test x$have_libconfig = xyes])
+
+##########################################################################
+# Check for od availability
+##########################################################################
+AC_CHECK_PROGS([OD], [od])
+AC_PROG_SED
+AS_IF([test -z "$OD"], [AC_MSG_ERROR([Could not find 'od'])])
+
+##########################################################################
+# Check default configuration file
+##########################################################################
+AS_IF([test -z "$2"] || [test ! -f $2],
+ [AC_MSG_ERROR([Default configuration file not found])], [])
+
+conf_ver=$_ODP_CONFIG_VERSION_GENERATION.$_ODP_CONFIG_VERSION_MAJOR.$_ODP_CONFIG_VERSION_MINOR
+file_ver=`$SED 's/ //g' $2 | $GREP -oP '(?<=config_file_version=").*?(?=")'`
+
+AS_IF([test "x$conf_ver" = "x$file_ver"], [],
+ [AC_MSG_ERROR([Configuration file version mismatch (_ODP_CONFIG_VERSION=$conf_ver config_file_version=$file_ver)])])
+
+odp_use_config=true
+##########################################################################
+# Create a header file odp_libconfig_config.h which containins null
+# terminated hex dump of odp-linux.conf
+##########################################################################
+AC_CONFIG_COMMANDS([platform/$1/include/odp_libconfig_config.h],
+[mkdir -p platform/$1/include
+ (echo "static const char config_builtin[[]] = {"; \
+ $OD -An -v -tx1 < $CONFIG_FILE | \
+ $SED -e 's/[[0-9a-f]]\+/0x\0,/g' ; \
+ echo "0x00 };") > \
+ platform/$1/include/odp_libconfig_config.h],
+ [with_platform=$1 OD=$OD SED=$SED CONFIG_FILE=$2])
+]) # ODP_LIBCONFIG
diff --git a/m4/odp_openssl.m4 b/m4/odp_openssl.m4
new file mode 100644
index 000000000..4f62dba8e
--- /dev/null
+++ b/m4/odp_openssl.m4
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2017 Linaro Limited
+#
+
+# ODP_OPENSSL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# -----------------------------------------------------
+AC_DEFUN([ODP_OPENSSL],
+[dnl
+AC_ARG_VAR([OPENSSL_CPPFLAGS], [C preprocessor flags for OpenSSL])
+AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL crypto library])
+AC_ARG_VAR([OPENSSL_STATIC_LIBS], [static linker flags for OpenSSL crypto library])
+
+##########################################################################
+# Set optional OpenSSL path
+##########################################################################
+AC_ARG_WITH([openssl-path],
+[AS_HELP_STRING([--with-openssl-path=DIR],
+ [path to openssl libs and headers [default=system]])],
+[OPENSSL_CPPFLAGS="-I$withval/include"
+OPENSSL_LIBS="-L$withval/lib -lcrypto"],
+[if test "x$ac_cv_env_OPENSSL_LIBS_set" != "xset" ; then
+ OPENSSL_LIBS="-lcrypto"
+fi])
+if test "x$ac_cv_env_OPENSSL_STATIC_LIBS_set" != "xset" ; then
+ OPENSSL_STATIC_LIBS="$OPENSSL_LIBS -ldl"
+fi
+
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_CPPFLAGS=$CPPFLAGS
+OLD_LIBS=$LIBS
+CPPFLAGS="$OPENSSL_CPPFLAGS $CPPFLAGS"
+LIBS="$OPENSSL_LIBS $LIBS"
+
+##########################################################################
+# Check for OpenSSL availability
+##########################################################################
+odp_openssl_ok=yes
+AC_CHECK_HEADERS([openssl/des.h openssl/rand.h openssl/hmac.h openssl/evp.h], [],
+ [odp_openssl_ok=no])
+AC_CACHE_CHECK([for EVP_EncryptInit in -lcrypto], [odp_cv_openssl_crypto],
+[AC_LINK_IFELSE([AC_LANG_CALL([], [EVP_EncryptInit])],
+ [odp_cv_openssl_crypto=yes],
+ [odp_cv_openssl_crypto=no])])
+if test "x$odp_cv_openssl_crypto" != "xyes" ; then
+ odp_openssl_ok=no
+fi
+
+if test "x$odp_openssl_ok" = "xyes" ; then
+ m4_default([$1], [:])
+else
+ OPENSSL_CPPFLAGS=""
+ OPENSSL_LIBS=""
+ OPENSSL_STATIC_LIBS=""
+ m4_default([$2], [AC_MSG_FAILURE([OpenSSL not found])])
+fi
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+LIBS=$OLD_LIBS
+CPPFLAGS=$OLD_CPPFLAGS
+]) # ODP_OPENSSL
diff --git a/m4/odp_pthread.m4 b/m4/odp_pthread.m4
new file mode 100644
index 000000000..2076a4691
--- /dev/null
+++ b/m4/odp_pthread.m4
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+# ODP_PTHREAD
+# -----------
+# Check for pthreads availability
+AC_DEFUN([ODP_PTHREAD], [
+ AC_MSG_CHECKING([for pthread support in -pthread])
+ AC_LANG_PUSH([C])
+ saved_cflags="$CFLAGS"
+ saved_ldflags="$LDFLAGS"
+ PTHREAD_CFLAGS="-pthread"
+ CFLAGS="$AM_CFLAGS $CFLAGS $PTHREAD_CFLAGS"
+ PTHREAD_LIBS="-pthread"
+ LDFLAGS="$AM_LDFLAGS $LDFLAGS $PTHREAD_LIBS"
+ AC_TRY_LINK_FUNC([pthread_create], [pthread=yes])
+ CFLAGS="$saved_cflags"
+ LDFLAGS="$saved_ldflags"
+ if test x"$pthread" != "xyes"; then
+ AC_MSG_FAILURE([pthread is not supported])
+ fi
+ AC_MSG_RESULT([yes])
+ AC_LANG_POP([C])
+ AC_SUBST([PTHREAD_LIBS])
+ AC_SUBST([PTHREAD_CFLAGS])
+])
diff --git a/m4/odp_timer.m4 b/m4/odp_timer.m4
new file mode 100644
index 000000000..be0ac0013
--- /dev/null
+++ b/m4/odp_timer.m4
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2017 Linaro Limited
+#
+
+# ODP_TIMER([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+##########################################################################
+# Check for POSIX timer functions
+##########################################################################
+AC_DEFUN([ODP_TIMER], [dnl
+AC_CHECK_LIB([rt], [timer_create], [TIMER_LIBS="-lrt"],
+ [AC_CHECK_LIB([posix4], [timer_create], [TIMER_LIBS="-lposix4"],
+ [m4_default([$2], [AC_MSG_FAILURE([timer_create not found])])])])
+m4_default([$1], [:])
+AC_SUBST([TIMER_LIBS])
+]) # ODP_TIMER
diff --git a/m4/odp_visibility.m4 b/m4/odp_visibility.m4
new file mode 100644
index 000000000..9edba1f43
--- /dev/null
+++ b/m4/odp_visibility.m4
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2017 Linaro Limited
+#
+
+# ODP_VISIBILITY
+# --------------
+# Enable -fvisibility=hidden if using a gcc that supports it
+
+AC_DEFUN([ODP_VISIBILITY], [dnl
+VISIBILITY_CFLAGS="-fvisibility=hidden"
+AC_CACHE_CHECK([whether $CC supports -fvisibility=hidden],
+ [odp_cv_visibility_hidden], [dnl
+OLD_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
+AC_LINK_IFELSE([AC_LANG_PROGRAM()], [odp_cv_visibility_hidden=yes],
+ [odp_cv_visibility_hidden=no])
+CFLAGS=$OLD_CFLAGS
+])
+
+if test "x$odp_cv_visibility_hidden" != "xyes" ; then
+ VISIBILITY_CFLAGS=""
+fi
+
+AC_SUBST(VISIBILITY_CFLAGS)
+]) # ODP_VISIBILITY
diff --git a/pkgconfig/libodp-linux.pc.in b/pkgconfig/libodp-linux.pc.in
deleted file mode 100644
index 220dbc9c6..000000000
--- a/pkgconfig/libodp-linux.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libodp-linux
-Description: The ODP packet processing engine
-Version: @PKGCONFIG_VERSION@
-Libs: -L${libdir} -lodp-linux @DPDK_LIBS@
-Libs.private: @OPENSSL_STATIC_LIBS@ @DPDK_PMDS@ @DPDK_LIBS@ @PCAP_LIBS@ @PTHREAD_LIBS@ @TIMER_LIBS@ -lpthread @ATOMIC_LIBS@
-Cflags: -I${includedir}
diff --git a/pkgconfig/libodphelper.pc.in b/pkgconfig/libodphelper.pc.in
deleted file mode 100644
index b14335e5c..000000000
--- a/pkgconfig/libodphelper.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libodphelper
-Description: Helper for the ODP packet processing engine
-Version: @PKGCONFIG_VERSION@
-Libs: -L${libdir} -lodphelper
-Libs.private:
-Cflags: -I${includedir}
diff --git a/platform/Makefile.inc b/platform/Makefile.inc
index 738f0f9a0..ed161d83d 100644
--- a/platform/Makefile.inc
+++ b/platform/Makefile.inc
@@ -1,105 +1,28 @@
-LIB = $(top_builddir)/lib
+include $(top_srcdir)/Makefile.inc
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = $(top_builddir)/pkgconfig/libodp-linux.pc
+pkgconfig_DATA = lib$(ODP_LIB_NAME).pc
-.PHONY: pkgconfig/libodp-linux.pc
+EXTRA_DIST = doc/platform_specific.dox
+
+configdir = $(sysconfdir)/odp
+if ODP_USE_CONFIG
+config_DATA = $(top_builddir)/$(rel_default_config_path)
+EXTRA_DIST += $(top_builddir)/$(rel_default_config_path)
+endif
VPATH = $(srcdir) $(builddir)
-lib_LTLIBRARIES = $(LIB)/libodp-linux.la
+lib_LTLIBRARIES =
AM_LDFLAGS += -version-number '$(ODP_LIBSO_VERSION)'
-AM_CFLAGS += "-DGIT_HASH=$(VERSION)"
-AM_CFLAGS += $(VISIBILITY_CFLAGS)
-
-#The implementation will need to retain the deprecated implementation
-AM_CFLAGS += -Wno-deprecated-declarations
-
-AM_CFLAGS += @PTHREAD_CFLAGS@
+if ODP_ABI_COMPAT
+AM_LDFLAGS += -export-symbols-regex '^(odp_|_deprecated_odp_)'
+else
+AM_LDFLAGS += -export-symbols-regex '^(odp_|_odp_|_deprecated_odp_)'
+endif
-odpapispecincludedir= $(includedir)/odp/api/spec
-odpapispecinclude_HEADERS = \
- $(top_srcdir)/include/odp/api/spec/align.h \
- $(top_srcdir)/include/odp/api/spec/atomic.h \
- $(top_srcdir)/include/odp/api/spec/barrier.h \
- $(top_srcdir)/include/odp/api/spec/buffer.h \
- $(top_srcdir)/include/odp/api/spec/byteorder.h \
- $(top_srcdir)/include/odp/api/spec/classification.h \
- $(top_srcdir)/include/odp/api/spec/compiler.h \
- $(top_srcdir)/include/odp/api/spec/cpu.h \
- $(top_srcdir)/include/odp/api/spec/cpumask.h \
- $(top_srcdir)/include/odp/api/spec/crypto.h \
- $(top_srcdir)/include/odp/api/spec/debug.h \
- $(top_srcdir)/include/odp/api/spec/errno.h \
- $(top_srcdir)/include/odp/api/spec/event.h \
- $(top_srcdir)/include/odp/api/spec/hash.h \
- $(top_srcdir)/include/odp/api/spec/hints.h \
- $(top_srcdir)/include/odp/api/spec/init.h \
- $(top_srcdir)/include/odp/api/spec/packet.h \
- $(top_srcdir)/include/odp/api/spec/packet_flags.h \
- $(top_srcdir)/include/odp/api/spec/packet_io.h \
- $(top_srcdir)/include/odp/api/spec/packet_io_stats.h \
- $(top_srcdir)/include/odp/api/spec/pool.h \
- $(top_srcdir)/include/odp/api/spec/queue.h \
- $(top_srcdir)/include/odp/api/spec/random.h \
- $(top_srcdir)/include/odp/api/spec/rwlock.h \
- $(top_srcdir)/include/odp/api/spec/rwlock_recursive.h \
- $(top_srcdir)/include/odp/api/spec/schedule.h \
- $(top_srcdir)/include/odp/api/spec/schedule_types.h \
- $(top_srcdir)/include/odp/api/spec/shared_memory.h \
- $(top_srcdir)/include/odp/api/spec/spinlock.h \
- $(top_srcdir)/include/odp/api/spec/spinlock_recursive.h \
- $(top_srcdir)/include/odp/api/spec/std_clib.h \
- $(top_srcdir)/include/odp/api/spec/std_types.h \
- $(top_srcdir)/include/odp/api/spec/sync.h \
- $(top_srcdir)/include/odp/api/spec/system_info.h \
- $(top_srcdir)/include/odp/api/spec/thread.h \
- $(top_srcdir)/include/odp/api/spec/thrmask.h \
- $(top_srcdir)/include/odp/api/spec/ticketlock.h \
- $(top_srcdir)/include/odp/api/spec/time.h \
- $(top_srcdir)/include/odp/api/spec/timer.h \
- $(top_srcdir)/include/odp/api/spec/traffic_mngr.h \
- $(top_builddir)/include/odp/api/spec/deprecated.h \
- $(top_builddir)/include/odp/api/spec/version.h
-
-odpapiabidefaultincludedir= $(includedir)/odp/arch/default/api/abi
-odpapiabidefaultinclude_HEADERS = \
- $(top_srcdir)/include/odp/arch/default/api/abi/buffer.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/classification.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/crypto.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/event.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/packet.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/pool.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/queue.h \
- $(top_srcdir)/include/odp/arch/default/api/abi/shared_memory.h
-
-odpapiabiarchincludedir= $(includedir)/odp/arch/@ARCH_ABI@/odp/api/abi
-odpapiabiarchinclude_HEADERS = \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/buffer.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/classification.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/crypto.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/event.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/packet.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/pool.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/queue.h \
- $(top_srcdir)/include/odp/arch/@ARCH_ABI@/odp/api/abi/shared_memory.h
+AM_CFLAGS += "-DODP_VERSION_BUILD=$(VERSION)"
+AM_CFLAGS += $(VISIBILITY_CFLAGS)
-EXTRA_DIST = \
- arch/arm/odp/api/cpu_arch.h \
- arch/arm/odp_cpu_arch.c \
- arch/arm/odp_sysinfo_parse.c \
- arch/default/odp/api/cpu_arch.h \
- arch/default/odp_cpu_arch.c \
- arch/default/odp_sysinfo_parse.c \
- arch/mips64/odp/api/cpu_arch.h \
- arch/mips64/odp_cpu_arch.c \
- arch/mips64/odp_sysinfo_parse.c \
- arch/powerpc/odp/api/cpu_arch.h \
- arch/powerpc/odp_cpu_arch.c \
- arch/powerpc/odp_sysinfo_parse.c \
- arch/x86/odp/api/cpu_arch.h \
- arch/x86/odp_cpu_arch.c \
- arch/x86/odp_sysinfo_parse.c \
- arch/x86/cpu_flags.c \
- arch/x86/cpu_flags.h
+AM_CFLAGS += $(PTHREAD_CFLAGS)
diff --git a/platform/linux-generic/.gitignore b/platform/linux-generic/.gitignore
index 909756a1f..16e788a90 100644
--- a/platform/linux-generic/.gitignore
+++ b/platform/linux-generic/.gitignore
@@ -1 +1,2 @@
-include/odp/api/plat/static_inline.h
+libodp-linux.pc
+odp_libconfig_config.h
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 26eba2846..d1d482849 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -1,279 +1,506 @@
# Uncomment this if you need to change the CUSTOM_STR string
-#export CUSTOM_STR=https://git.linaro.org/lng/odp.git
+#export CUSTOM_STR=https://github.com/OpenDataPlane/odp.git
include $(top_srcdir)/platform/Makefile.inc
-include $(top_srcdir)/platform/@with_platform@/Makefile.inc
+lib_LTLIBRARIES += $(LIB)/libodp-linux.la
-AM_CFLAGS += -I$(srcdir)/include
-AM_CFLAGS += -I$(top_srcdir)/include
-AM_CFLAGS += -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@
-AM_CFLAGS += -I$(top_builddir)/include
-AM_CFLAGS += -Iinclude
-AM_CFLAGS += -D_ODP_PKTIO_IPC
+AM_CPPFLAGS = $(ODP_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/include
+AM_CPPFLAGS += -I$(top_builddir)/platform/$(with_platform)/include
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch/@ARCH_DIR@
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch/default
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch/common
AM_CPPFLAGS += $(OPENSSL_CPPFLAGS)
-AM_CPPFLAGS += $(DPDK_CPPFLAGS)
-AM_CPPFLAGS += $(NETMAP_CPPFLAGS)
+AM_CPPFLAGS += $(ORT_CPPFLAGS)
-include_HEADERS = \
- $(top_srcdir)/include/odp.h \
- $(top_srcdir)/include/odp_api.h
+AM_CFLAGS += $(AARCH64CRYPTO_CFLAGS)
+AM_CFLAGS += $(DPDK_CFLAGS)
+AM_CFLAGS += $(LIBCONFIG_CFLAGS)
+AM_CFLAGS += $(LIBXDP_CFLAGS)
-odpincludedir= $(includedir)/odp
-odpinclude_HEADERS = \
- $(srcdir)/include/odp/visibility_begin.h \
- $(srcdir)/include/odp/visibility_end.h
+DISTCLEANFILES = include/odp_libconfig_config.h
+include/odp_libconfig_config.h: $(top_builddir)/$(rel_default_config_path) $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
-odpapiincludedir= $(includedir)/odp/api
-odpapiinclude_HEADERS = \
- $(srcdir)/include/odp/api/align.h \
- $(srcdir)/include/odp/api/atomic.h \
- $(srcdir)/include/odp/api/barrier.h \
- $(srcdir)/include/odp/api/buffer.h \
- $(srcdir)/include/odp/api/byteorder.h \
- $(srcdir)/include/odp/api/classification.h \
- $(srcdir)/include/odp/api/compiler.h \
- $(srcdir)/include/odp/api/cpu.h \
- $(srcdir)/include/odp/api/cpumask.h \
- $(srcdir)/include/odp/api/crypto.h \
- $(srcdir)/include/odp/api/debug.h \
- $(srcdir)/include/odp/api/deprecated.h \
- $(srcdir)/include/odp/api/errno.h \
- $(srcdir)/include/odp/api/event.h \
- $(srcdir)/include/odp/api/hash.h \
- $(srcdir)/include/odp/api/hints.h \
- $(srcdir)/include/odp/api/init.h \
- $(srcdir)/include/odp/api/packet_flags.h \
- $(srcdir)/include/odp/api/packet.h \
- $(srcdir)/include/odp/api/packet_io.h \
- $(srcdir)/include/odp/api/packet_io_stats.h \
- $(srcdir)/include/odp/api/pool.h \
- $(srcdir)/include/odp/api/queue.h \
- $(srcdir)/include/odp/api/random.h \
- $(srcdir)/include/odp/api/rwlock.h \
- $(srcdir)/include/odp/api/rwlock_recursive.h \
- $(srcdir)/include/odp/api/schedule.h \
- $(srcdir)/include/odp/api/schedule_types.h \
- $(srcdir)/include/odp/api/shared_memory.h \
- $(srcdir)/include/odp/api/spinlock.h \
- $(srcdir)/include/odp/api/spinlock_recursive.h \
- $(srcdir)/include/odp/api/std_clib.h \
- $(srcdir)/include/odp/api/std_types.h \
- $(srcdir)/include/odp/api/sync.h \
- $(srcdir)/include/odp/api/system_info.h \
- $(srcdir)/include/odp/api/thread.h \
- $(srcdir)/include/odp/api/thrmask.h \
- $(srcdir)/include/odp/api/ticketlock.h \
- $(srcdir)/include/odp/api/time.h \
- $(srcdir)/include/odp/api/timer.h \
- $(srcdir)/include/odp/api/traffic_mngr.h \
- $(srcdir)/include/odp/api/version.h
-
-if ARCH_IS_ARM
-odpapiinclude_HEADERS += $(srcdir)/arch/arm/odp/api/cpu_arch.h
-endif
-if ARCH_IS_MIPS64
-odpapiinclude_HEADERS += $(srcdir)/arch/mips64/odp/api/cpu_arch.h
-endif
-if ARCH_IS_POWERPC
-odpapiinclude_HEADERS += $(srcdir)/arch/powerpc/odp/api/cpu_arch.h
-endif
-if ARCH_IS_X86
-odpapiinclude_HEADERS += $(srcdir)/arch/x86/odp/api/cpu_arch.h
-endif
+odpapiabiarchincludedir = $(archincludedir)/odp/api/abi
+odpapiabiarchinclude_HEADERS =
+if !ODP_ABI_COMPAT
odpapiplatincludedir= $(includedir)/odp/api/plat
odpapiplatinclude_HEADERS = \
- $(builddir)/include/odp/api/plat/static_inline.h \
- $(srcdir)/include/odp/api/plat/atomic_inlines.h \
- $(srcdir)/include/odp/api/plat/atomic_types.h \
- $(srcdir)/include/odp/api/plat/barrier_types.h \
- $(srcdir)/include/odp/api/plat/buffer_types.h \
- $(srcdir)/include/odp/api/plat/byteorder_inlines.h \
- $(srcdir)/include/odp/api/plat/byteorder_types.h \
- $(srcdir)/include/odp/api/plat/classification_types.h \
- $(srcdir)/include/odp/api/plat/cpumask_types.h \
- $(srcdir)/include/odp/api/plat/crypto_types.h \
- $(srcdir)/include/odp/api/plat/event_types.h \
- $(srcdir)/include/odp/api/plat/init_types.h \
- $(srcdir)/include/odp/api/plat/packet_flag_inlines.h \
- $(srcdir)/include/odp/api/plat/packet_flag_inlines_api.h \
- $(srcdir)/include/odp/api/plat/packet_inlines.h \
- $(srcdir)/include/odp/api/plat/packet_inlines_api.h \
- $(srcdir)/include/odp/api/plat/packet_types.h \
- $(srcdir)/include/odp/api/plat/packet_io_types.h \
- $(srcdir)/include/odp/api/plat/pool_types.h \
- $(srcdir)/include/odp/api/plat/queue_types.h \
- $(srcdir)/include/odp/api/plat/rwlock_types.h \
- $(srcdir)/include/odp/api/plat/rwlock_recursive_types.h \
- $(srcdir)/include/odp/api/plat/schedule_types.h \
- $(srcdir)/include/odp/api/plat/shared_memory_types.h \
- $(srcdir)/include/odp/api/plat/spinlock_types.h \
- $(srcdir)/include/odp/api/plat/spinlock_recursive_types.h \
- $(srcdir)/include/odp/api/plat/std_clib_inlines.h \
- $(srcdir)/include/odp/api/plat/strong_types.h \
- $(srcdir)/include/odp/api/plat/sync_inlines.h \
- $(srcdir)/include/odp/api/plat/thread_types.h \
- $(srcdir)/include/odp/api/plat/thrmask_types.h \
- $(srcdir)/include/odp/api/plat/ticketlock_inlines.h \
- $(srcdir)/include/odp/api/plat/ticketlock_inlines_api.h \
- $(srcdir)/include/odp/api/plat/ticketlock_types.h \
- $(srcdir)/include/odp/api/plat/time_types.h \
- $(srcdir)/include/odp/api/plat/timer_types.h \
- $(srcdir)/include/odp/api/plat/traffic_mngr_types.h \
- $(srcdir)/include/odp/api/plat/version_types.h
+ include/odp/api/plat/atomic_inlines.h \
+ include/odp/api/plat/buffer_inlines.h \
+ include/odp/api/plat/buffer_inline_types.h \
+ include/odp/api/plat/byteorder_inlines.h \
+ include/odp/api/plat/cpu_inlines.h \
+ include/odp/api/plat/crypto_inlines.h \
+ include/odp/api/plat/dma_inlines.h \
+ include/odp/api/plat/debug_inlines.h \
+ include/odp/api/plat/event_inlines.h \
+ include/odp/api/plat/event_inline_types.h \
+ include/odp/api/plat/event_validation_external.h \
+ include/odp/api/plat/event_vector_inline_types.h \
+ include/odp/api/plat/hash_inlines.h \
+ include/odp/api/plat/ipsec_inlines.h \
+ include/odp/api/plat/packet_flag_inlines.h \
+ include/odp/api/plat/packet_inline_types.h \
+ include/odp/api/plat/packet_inlines.h \
+ include/odp/api/plat/packet_vector_inlines.h \
+ include/odp/api/plat/packet_io_inlines.h \
+ include/odp/api/plat/pool_inlines.h \
+ include/odp/api/plat/pool_inline_types.h \
+ include/odp/api/plat/queue_inlines.h \
+ include/odp/api/plat/queue_inline_types.h \
+ include/odp/api/plat/rwlock_inlines.h \
+ include/odp/api/plat/rwlock_recursive_inlines.h \
+ include/odp/api/plat/schedule_inlines.h \
+ include/odp/api/plat/schedule_inline_types.h \
+ include/odp/api/plat/spinlock_inlines.h \
+ include/odp/api/plat/spinlock_recursive_inlines.h \
+ include/odp/api/plat/std_inlines.h \
+ include/odp/api/plat/strong_types.h \
+ include/odp/api/plat/sync_inlines.h \
+ include/odp/api/plat/thread_inlines.h \
+ include/odp/api/plat/thread_inline_types.h \
+ include/odp/api/plat/ticketlock_inlines.h \
+ include/odp/api/plat/time_inlines.h \
+ include/odp/api/plat/timer_inlines.h \
+ include/odp/api/plat/timer_inline_types.h
-odpdrvincludedir = $(includedir)/odp/drv
-odpdrvinclude_HEADERS = \
- $(srcdir)/include/odp/drv/compiler.h
+odpapiabiarchinclude_HEADERS += \
+ include-abi/odp/api/abi/align.h \
+ include-abi/odp/api/abi/atomic.h \
+ include-abi/odp/api/abi/barrier.h \
+ include-abi/odp/api/abi/buffer.h \
+ include-abi/odp/api/abi/buffer_types.h \
+ include-abi/odp/api/abi/byteorder.h \
+ include-abi/odp/api/abi/classification.h \
+ include-abi/odp/api/abi/comp.h \
+ include-abi/odp/api/abi/cpumask.h \
+ include-abi/odp/api/abi/crypto.h \
+ include-abi/odp/api/abi/crypto_types.h \
+ include-abi/odp/api/abi/debug.h \
+ include-abi/odp/api/abi/dma.h \
+ include-abi/odp/api/abi/dma_types.h \
+ include-abi/odp/api/abi/errno.h \
+ include-abi/odp/api/abi/event.h \
+ include-abi/odp/api/abi/event_types.h \
+ include-abi/odp/api/abi/hash.h \
+ include-abi/odp/api/abi/init.h \
+ include-abi/odp/api/abi/ipsec.h \
+ include-abi/odp/api/abi/ipsec_types.h \
+ include-abi/odp/api/abi/ml_types.h \
+ include-abi/odp/api/abi/packet.h \
+ include-abi/odp/api/abi/packet_types.h \
+ include-abi/odp/api/abi/packet_flags.h \
+ include-abi/odp/api/abi/packet_io.h \
+ include-abi/odp/api/abi/packet_io_types.h \
+ include-abi/odp/api/abi/proto_stats.h \
+ include-abi/odp/api/abi/proto_stats_types.h \
+ include-abi/odp/api/abi/pool.h \
+ include-abi/odp/api/abi/pool_types.h \
+ include-abi/odp/api/abi/queue.h \
+ include-abi/odp/api/abi/queue_types.h \
+ include-abi/odp/api/abi/random.h \
+ include-abi/odp/api/abi/rwlock.h \
+ include-abi/odp/api/abi/rwlock_recursive.h \
+ include-abi/odp/api/abi/schedule.h \
+ include-abi/odp/api/abi/schedule_types.h \
+ include-abi/odp/api/abi/shared_memory.h \
+ include-abi/odp/api/abi/spinlock.h \
+ include-abi/odp/api/abi/spinlock_recursive.h \
+ include-abi/odp/api/abi/stash.h \
+ include-abi/odp/api/abi/stash_types.h \
+ include-abi/odp/api/abi/std.h \
+ include-abi/odp/api/abi/std_types.h \
+ include-abi/odp/api/abi/sync.h \
+ include-abi/odp/api/abi/thread.h \
+ include-abi/odp/api/abi/thread_types.h \
+ include-abi/odp/api/abi/thrmask.h \
+ include-abi/odp/api/abi/ticketlock.h \
+ include-abi/odp/api/abi/time.h \
+ include-abi/odp/api/abi/time_types.h \
+ include-abi/odp/api/abi/timer.h \
+ include-abi/odp/api/abi/timer_types.h \
+ include-abi/odp/api/abi/traffic_mngr.h \
+ include-abi/odp/api/abi/version.h
+endif
noinst_HEADERS = \
- ${srcdir}/include/_fdserver_internal.h \
- ${srcdir}/include/_ishm_internal.h \
- ${srcdir}/include/_ishmphy_internal.h \
- ${srcdir}/include/odp_align_internal.h \
- ${srcdir}/include/odp_atomic_internal.h \
- ${srcdir}/include/odp_buffer_inlines.h \
- ${srcdir}/include/odp_bitmap_internal.h \
- ${srcdir}/include/odp_buffer_internal.h \
- ${srcdir}/include/odp_classification_datamodel.h \
- ${srcdir}/include/odp_classification_inlines.h \
- ${srcdir}/include/odp_classification_internal.h \
- ${srcdir}/include/odp_config_internal.h \
- ${srcdir}/include/odp_crypto_internal.h \
- ${srcdir}/include/odp_debug_internal.h \
- ${srcdir}/include/odp_errno_define.h \
- ${srcdir}/include/odp_forward_typedefs_internal.h \
- ${srcdir}/include/odp_internal.h \
- ${srcdir}/include/odp_name_table_internal.h \
- ${srcdir}/include/odp_packet_internal.h \
- ${srcdir}/include/odp_packet_io_internal.h \
- ${srcdir}/include/odp_packet_io_ipc_internal.h \
- ${srcdir}/include/odp_packet_io_ring_internal.h \
- ${srcdir}/include/odp_packet_netmap.h \
- ${srcdir}/include/odp_packet_dpdk.h \
- ${srcdir}/include/odp_packet_socket.h \
- ${srcdir}/include/odp_packet_tap.h \
- ${srcdir}/include/odp_pkt_queue_internal.h \
- ${srcdir}/include/odp_pool_internal.h \
- ${srcdir}/include/odp_posix_extensions.h \
- ${srcdir}/include/odp_queue_internal.h \
- ${srcdir}/include/odp_ring_internal.h \
- ${srcdir}/include/odp_queue_if.h \
- ${srcdir}/include/odp_schedule_if.h \
- ${srcdir}/include/odp_sorted_list_internal.h \
- ${srcdir}/include/odp_shm_internal.h \
- ${srcdir}/include/odp_time_internal.h \
- ${srcdir}/include/odp_timer_internal.h \
- ${srcdir}/include/odp_timer_wheel_internal.h \
- ${srcdir}/include/odp_traffic_mngr_internal.h \
- ${srcdir}/include/protocols/eth.h \
- ${srcdir}/include/protocols/ip.h \
- ${srcdir}/include/protocols/ipsec.h \
- ${srcdir}/include/protocols/tcp.h \
- ${srcdir}/include/protocols/udp.h \
- ${srcdir}/Makefile.inc
+ include/odp_atomic_internal.h \
+ include/odp_bitset.h \
+ include/odp_buffer_internal.h \
+ include/odp_chksum_internal.h \
+ include/odp_classification_datamodel.h \
+ include/odp_classification_internal.h \
+ include/odp_config_internal.h \
+ include/odp_debug_internal.h \
+ include/odp_errno_define.h \
+ include/odp_event_internal.h \
+ include/odp_event_validation_internal.h \
+ include/odp_fdserver_internal.h \
+ include/odp_forward_typedefs_internal.h \
+ include/odp_ml_fp16.h \
+ include/odp_global_data.h \
+ include/odp_init_internal.h \
+ include/odp_ipsec_internal.h \
+ include/odp_ishmphy_internal.h \
+ include/odp_ishmpool_internal.h \
+ include/odp_libconfig_internal.h \
+ include/odp_llqueue.h \
+ include/odp_macros_internal.h \
+ include/odp_name_table_internal.h \
+ include/odp_packet_dpdk.h \
+ include/odp_packet_internal.h \
+ include/odp_packet_io_internal.h \
+ include/odp_parse_internal.h \
+ include/odp_socket_common.h \
+ include/odp_string_internal.h \
+ include/odp_packet_io_stats_common.h \
+ include/odp_packet_io_stats.h \
+ include/odp_sysfs_stats.h \
+ include/odp_ethtool_stats.h \
+ include/odp_ethtool_rss.h \
+ include/odp_pcapng.h \
+ include/odp_pkt_queue_internal.h \
+ include/odp_pool_internal.h \
+ include/odp_posix_extensions.h \
+ include/odp_queue_if.h \
+ include/odp_queue_basic_internal.h \
+ include/odp_queue_lf.h \
+ include/odp_queue_scalable_internal.h \
+ include/odp_random_std_internal.h \
+ include/odp_random_openssl_internal.h \
+ include/odp_ring_common.h \
+ include/odp_ring_internal.h \
+ include/odp_ring_mpmc_internal.h \
+ include/odp_ring_mpmc_u32_internal.h \
+ include/odp_ring_mpmc_u64_internal.h \
+ include/odp_ring_ptr_internal.h \
+ include/odp_ring_spsc_internal.h \
+ include/odp_ring_st_internal.h \
+ include/odp_ring_u32_internal.h \
+ include/odp_ring_u64_internal.h \
+ include/odp_schedule_if.h \
+ include/odp_schedule_scalable_config.h \
+ include/odp_schedule_scalable.h \
+ include/odp_schedule_scalable_ordered.h \
+ include/odp_shm_internal.h \
+ include/odp_sorted_list_internal.h \
+ include/odp_sysinfo_internal.h \
+ include/odp_timer_internal.h \
+ include/odp_timer_wheel_internal.h \
+ include/odp_traffic_mngr_internal.h \
+ include/odp_types_internal.h \
+ include/odp_event_vector_internal.h \
+ include/protocols/eth.h \
+ include/protocols/ip.h \
+ include/protocols/ipsec.h \
+ include/protocols/sctp.h \
+ include/protocols/tcp.h \
+ include/protocols/thash.h \
+ include/protocols/udp.h
+BUILT_SOURCES = \
+ include/odp_libconfig_config.h
__LIB__libodp_linux_la_SOURCES = \
- _fdserver.c \
- _ishm.c \
- _ishmphy.c \
- odp_atomic.c \
odp_barrier.c \
- odp_bitmap.c \
odp_buffer.c \
- odp_byteorder.c \
+ odp_chksum.c \
odp_classification.c \
- odp_cpu.c \
+ odp_comp.c \
+ miniz/miniz.c miniz/miniz.h miniz/miniz_common.h \
+ miniz/miniz_tdef.c miniz/miniz_tdef.h \
+ miniz/miniz_tinfl.c miniz/miniz_tinfl.h \
odp_cpumask.c \
odp_cpumask_task.c \
- odp_crypto.c \
+ odp_dma.c \
odp_errno.c \
odp_event.c \
- odp_hash.c \
- odp_init.c \
+ odp_event_validation.c \
+ odp_fdserver.c \
+ odp_hash_crc_gen.c \
odp_impl.c \
+ odp_init.c \
+ odp_ipsec.c \
+ odp_ipsec_events.c \
+ odp_ipsec_sad.c \
+ odp_ishm.c \
+ odp_ishmphy.c \
+ odp_ishmpool.c \
+ odp_libconfig.c \
+ odp_ml_fp16.c \
+ odp_ml_quantize.c \
odp_name_table.c \
odp_packet.c \
+ odp_packet_vector.c \
odp_packet_flags.c \
odp_packet_io.c \
- pktio/ethtool.c \
- pktio/io_ops.c \
- pktio/ipc.c \
- pktio/pktio_common.c \
- pktio/loop.c \
- pktio/netmap.c \
- pktio/dpdk.c \
- pktio/socket.c \
- pktio/socket_mmap.c \
- pktio/sysfs.c \
- pktio/tap.c \
- pktio/ring.c \
+ odp_parse.c \
odp_pkt_queue.c \
odp_pool.c \
- odp_queue.c \
+ odp_pool_mem_src_ops.c \
+ odp_queue_basic.c \
odp_queue_if.c \
- odp_rwlock.c \
- odp_rwlock_recursive.c \
- odp_schedule.c \
+ odp_queue_lf.c \
+ odp_queue_scalable.c \
+ odp_queue_spsc.c \
+ odp_random.c \
+ odp_random_std.c \
+ odp_random_openssl.c \
+ odp_schedule_basic.c \
odp_schedule_if.c \
+ odp_schedule_scalable.c \
+ odp_schedule_scalable_ordered.c \
odp_schedule_sp.c \
- odp_schedule_iquery.c \
odp_shared_memory.c \
odp_sorted_list.c \
- odp_spinlock.c \
- odp_spinlock_recursive.c \
- odp_std_clib.c \
- odp_sync.c \
+ odp_stash.c \
+ odp_std.c \
+ odp_string.c \
odp_system_info.c \
+ odp_pcapng.c \
odp_thread.c \
odp_thrmask.c \
- odp_ticketlock.c \
- odp_time.c \
odp_timer.c \
odp_timer_wheel.c \
odp_traffic_mngr.c \
odp_version.c \
- odp_weak.c
+ odp_weak.c \
+ pktio/stats/ethtool_stats.c \
+ pktio/stats/sysfs_stats.c \
+ pktio/stats/packet_io_stats.c \
+ pktio/dpdk.c \
+ pktio/socket_common.c \
+ pktio/ethtool_rss.c \
+ pktio/io_ops.c \
+ pktio/ipc.c \
+ pktio/loop.c \
+ pktio/null.c \
+ pktio/pktio_common.c \
+ pktio/socket.c \
+ pktio/socket_mmap.c \
+ pktio/socket_xdp.c \
+ pktio/tap.c
+
+if WITH_OPENSSL_CRYPTO
+__LIB__libodp_linux_la_SOURCES += \
+ odp_crypto_openssl.c
+else
+if WITH_ARMV8_CRYPTO
+__LIB__libodp_linux_la_SOURCES += \
+ arch/aarch64/odp_crypto_armv8.c
+else
+if WITH_IPSECMB_CRYPTO
+__LIB__libodp_linux_la_SOURCES += \
+ odp_crypto_ipsecmb.c
+else
+__LIB__libodp_linux_la_SOURCES += \
+ odp_crypto_null.c
+endif
+endif
+endif
+
+if WITH_ML
+__LIB__libodp_linux_la_SOURCES += \
+ odp_ml.c
+else
+__LIB__libodp_linux_la_SOURCES += \
+ odp_ml_null.c
+endif
+
+if ODP_ABI_COMPAT
+__LIB__libodp_linux_la_SOURCES += \
+ odp_atomic_api.c \
+ odp_buffer_api.c \
+ odp_byteorder_api.c \
+ odp_cpu_api.c \
+ odp_crypto_api.c \
+ odp_dma_api.c \
+ odp_event_api.c \
+ odp_hash_api.c \
+ odp_ipsec_api.c \
+ odp_packet_api.c \
+ odp_packet_flags_api.c \
+ odp_packet_io_api.c \
+ odp_pool_api.c \
+ odp_queue_api.c \
+ odp_rwlock_api.c \
+ odp_rwlock_recursive_api.c \
+ odp_schedule_api.c \
+ odp_spinlock_api.c \
+ odp_spinlock_recursive_api.c \
+ odp_std_api.c \
+ odp_sync_api.c \
+ odp_thread_api.c \
+ odp_ticketlock_api.c \
+ odp_time_api.c \
+ odp_timer_api.c
+endif
if ARCH_IS_ARM
-__LIB__libodp_linux_la_SOURCES += arch/arm/odp_cpu_arch.c \
- arch/arm/odp_sysinfo_parse.c
+__LIB__libodp_linux_la_SOURCES += arch/default/odp_atomic.c \
+ arch/default/odp_cpu_cycles.c \
+ arch/default/odp_hash_crc32.c \
+ arch/default/odp_random.c \
+ arch/arm/odp_sysinfo_parse.c \
+ arch/default/odp_time.c
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/hash_crc32.h
+if !ODP_ABI_COMPAT
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/atomic_generic.h \
+ arch/default/odp/api/abi/atomic_inlines.h \
+ arch/default/odp/api/abi/cpu_generic.h \
+ arch/arm/odp/api/abi/cpu_inlines.h \
+ arch/arm/odp/api/abi/cpu.h \
+ arch/default/odp/api/abi/sync_inlines.h \
+ arch/default/odp/api/abi/time_inlines.h \
+ arch/default/odp/api/abi/wait_until_generic.h \
+ arch/default/odp/api/abi/wait_until.h
+endif
+noinst_HEADERS += arch/arm/odp_cpu.h \
+ arch/default/odp_atomic.h \
+ arch/default/odp_cpu.h \
+ arch/default/odp_random.h \
+ arch/default/odp_wait_until.h
+endif
+if ARCH_IS_AARCH64
+__LIB__libodp_linux_la_SOURCES += arch/aarch64/odp_atomic.c \
+ arch/aarch64/odp_cpu_cycles.c \
+ arch/aarch64/cpu_flags.c \
+ arch/default/odp_hash_crc32.c \
+ arch/default/odp_random.c \
+ arch/aarch64/odp_sysinfo_parse.c \
+ arch/common/odp_time_cpu.c
+odpapiabiarchinclude_HEADERS += arch/aarch64/odp/api/abi/hash_crc32.h \
+ arch/aarch64/odp/api/abi/time_cpu.h
+if !ODP_ABI_COMPAT
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/atomic_generic.h \
+ arch/aarch64/odp/api/abi/atomic_inlines.h \
+ arch/aarch64/odp/api/abi/atomic.h \
+ arch/default/odp/api/abi/cpu_generic.h \
+ arch/aarch64/odp/api/abi/cpu_inlines.h \
+ arch/aarch64/odp/api/abi/cpu.h \
+ arch/aarch64/odp/api/abi/sync_inlines.h \
+ arch/common/odp/api/abi/time_cpu_inlines.h \
+ arch/aarch64/odp/api/abi/time_inlines.h \
+ arch/default/odp/api/abi/wait_until_generic.h \
+ arch/aarch64/odp/api/abi/wait_until.h
+endif
+noinst_HEADERS += arch/aarch64/odp_atomic.h \
+ arch/aarch64/odp_cpu.h \
+ arch/aarch64/cpu_flags.h \
+ arch/aarch64/odp_random.h \
+ arch/aarch64/odp_wait_until.h
+endif
+if ARCH_IS_DEFAULT
+__LIB__libodp_linux_la_SOURCES += arch/default/odp_atomic.c \
+ arch/default/odp_cpu_cycles.c \
+ arch/default/odp_hash_crc32.c \
+ arch/default/odp_random.c \
+ arch/default/odp_sysinfo_parse.c \
+ arch/default/odp_time.c
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/hash_crc32.h
+if !ODP_ABI_COMPAT
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/atomic_generic.h \
+ arch/default/odp/api/abi/atomic_inlines.h \
+ arch/default/odp/api/abi/cpu_generic.h \
+ arch/default/odp/api/abi/cpu_inlines.h \
+ arch/default/odp/api/abi/cpu.h \
+ arch/default/odp/api/abi/sync_inlines.h \
+ arch/default/odp/api/abi/time_inlines.h \
+ arch/default/odp/api/abi/wait_until_generic.h \
+ arch/default/odp/api/abi/wait_until.h
endif
-if ARCH_IS_MIPS64
-__LIB__libodp_linux_la_SOURCES += arch/mips64/odp_cpu_arch.c \
- arch/mips64/odp_sysinfo_parse.c
+noinst_HEADERS += arch/default/odp_atomic.h \
+ arch/default/odp_cpu.h \
+ arch/default/odp_random.h \
+ arch/default/odp_wait_until.h
endif
if ARCH_IS_POWERPC
-__LIB__libodp_linux_la_SOURCES += arch/powerpc/odp_cpu_arch.c \
- arch/powerpc/odp_sysinfo_parse.c
+__LIB__libodp_linux_la_SOURCES += arch/default/odp_atomic.c \
+ arch/default/odp_cpu_cycles.c \
+ arch/default/odp_hash_crc32.c \
+ arch/default/odp_random.c \
+ arch/powerpc/odp_sysinfo_parse.c \
+ arch/default/odp_time.c
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/hash_crc32.h
+if !ODP_ABI_COMPAT
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/atomic_generic.h \
+ arch/default/odp/api/abi/atomic_inlines.h \
+ arch/default/odp/api/abi/cpu_generic.h \
+ arch/default/odp/api/abi/cpu_inlines.h \
+ arch/powerpc/odp/api/abi/cpu.h \
+ arch/default/odp/api/abi/sync_inlines.h \
+ arch/default/odp/api/abi/time_inlines.h \
+ arch/default/odp/api/abi/wait_until_generic.h \
+ arch/default/odp/api/abi/wait_until.h
+endif
+noinst_HEADERS += arch/default/odp_atomic.h \
+ arch/default/odp_cpu.h \
+ arch/default/odp_random.h \
+ arch/default/odp_wait_until.h
endif
if ARCH_IS_X86
-__LIB__libodp_linux_la_SOURCES += arch/x86/cpu_flags.c \
- arch/x86/odp_cpu_arch.c \
- arch/x86/odp_sysinfo_parse.c
+__LIB__libodp_linux_la_SOURCES += arch/default/odp_atomic.c \
+ arch/x86/cpu_flags.c \
+ arch/x86/odp_cpu_cycles.c \
+ arch/default/odp_hash_crc32.c \
+ arch/default/odp_random.c \
+ arch/x86/odp_sysinfo_parse.c \
+ arch/x86/odp_time_cpu.c \
+ arch/common/odp_time_cpu.c
+odpapiabiarchinclude_HEADERS += arch/x86/odp/api/abi/cpu_rdtsc.h \
+ arch/x86/odp/api/abi/hash_crc32.h \
+ arch/x86/odp/api/abi/time_cpu.h
+if !ODP_ABI_COMPAT
+odpapiabiarchinclude_HEADERS += arch/default/odp/api/abi/atomic_generic.h \
+ arch/default/odp/api/abi/atomic_inlines.h \
+ arch/x86/odp/api/abi/cpu_inlines.h \
+ arch/x86/odp/api/abi/cpu.h \
+ arch/x86/odp/api/abi/sync_inlines.h \
+ arch/common/odp/api/abi/time_cpu_inlines.h \
+ arch/x86/odp/api/abi/time_inlines.h \
+ arch/default/odp/api/abi/wait_until_generic.h \
+ arch/default/odp/api/abi/wait_until.h
+endif
+noinst_HEADERS += arch/x86/cpu_flags.h \
+ arch/x86/odp_cpu.h \
+ arch/x86/odp_random.h \
+ arch/default/odp_atomic.h \
+ arch/default/odp_cpu.h \
+ arch/default/odp_wait_until.h
endif
-if HAVE_PCAP
+if ODP_PKTIO_PCAP
__LIB__libodp_linux_la_SOURCES += pktio/pcap.c
endif
-__LIB__libodp_linux_la_LIBADD = $(ATOMIC_LIBS)
+__LIB__libodp_linux_la_LIBADD = $(AARCH64CRYPTO_LIBS)
+__LIB__libodp_linux_la_LIBADD += $(ATOMIC_LIBS)
__LIB__libodp_linux_la_LIBADD += $(OPENSSL_LIBS)
-__LIB__libodp_linux_la_LIBADD += $(DPDK_LIBS) $(DPDK_PMDS)
+__LIB__libodp_linux_la_LIBADD += $(LIBCONFIG_LIBS)
+__LIB__libodp_linux_la_LIBADD += $(DPDK_LIBS_LIBODP)
__LIB__libodp_linux_la_LIBADD += $(PTHREAD_LIBS)
__LIB__libodp_linux_la_LIBADD += $(TIMER_LIBS)
+__LIB__libodp_linux_la_LIBADD += $(LIBXDP_LIBS)
+__LIB__libodp_linux_la_LIBADD += $(IPSEC_MB_LIBS)
+__LIB__libodp_linux_la_LIBADD += $(ORT_LIBS)
-if HAVE_PCAP
+if ODP_PKTIO_PCAP
__LIB__libodp_linux_la_LIBADD += $(PCAP_LIBS)
endif
-# Create symlink for ABI header files. Application does not need to use the arch
-# specific include path for installed files.
-install-data-hook:
- if [ -h $(DESTDIR)$(prefix)/include/odp/api/abi ]; then \
- : ; \
- else \
- $(LN_S) -rf $(DESTDIR)$(prefix)/include/odp/arch/@ARCH_ABI@/odp/api/abi \
- $(DESTDIR)$(prefix)/include/odp/api/abi; \
- fi
+CHECK_GLOBALS_REGEX = " (odp_|_odp_|_deprecated_odp_|miniz_|mz_|tdefl_|tinfl_|mp_hdlr_init_odp_pool_ops|__odr_asan)"
+
+TESTS_ENVIRONMENT = \
+ LIBTOOL="$(LIBTOOL)" \
+ NM="$(NM)" \
+ LIB="$(LIB)" \
+ lib_LTLIBRARIES="$(lib_LTLIBRARIES)" \
+ CHECK_GLOBALS_REGEX=$(CHECK_GLOBALS_REGEX)
+
+dist_check_SCRIPTS = check-globals.sh
+
+TESTS = $(dist_check_SCRIPTS)
diff --git a/platform/linux-generic/Makefile.inc b/platform/linux-generic/Makefile.inc
deleted file mode 100644
index 876519bef..000000000
--- a/platform/linux-generic/Makefile.inc
+++ /dev/null
@@ -1,2 +0,0 @@
-AM_CFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch/$(ARCH_DIR)
-AM_CXXFLAGS += -I$(top_srcdir)/platform/$(with_platform)/arch/$(ARCH_DIR)
diff --git a/platform/linux-generic/README b/platform/linux-generic/README
index 3e05dabf2..905b06a4a 100644
--- a/platform/linux-generic/README
+++ b/platform/linux-generic/README
@@ -1,17 +1,81 @@
-Copyright (c) 2014, Linaro Limited
-All rights reserved.
-
-SPDX-License-Identifier: BSD-3-Clause
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2014-2018 Linaro Limited
+Copyright (c) 2019-2023 Nokia
1. Intro
-
-OpenDataPlane implementation for Linux generic. Directory linux-generic contains ODP headers and implementation
-for linux-generic target. This drop does not target high
-performance. It is rather proof of ODP API functionality. It still uses
-linux-generic's SW scheduler.
+ OpenDataPlane API generic Linux implementation. Directory linux-generic
+ contains the header and source files and additional platform test scripts
+ for ODP linux-generic implementation.
2. Build
-# To compile ODP
-./bootstrap
-./configure
-make
+ See DEPENDENCIES file about system requirements and dependencies to external
+ libraries/packages. It contains also more detailed build instructions.
+
+ Generally, ODP is built with these three steps:
+ ./bootstrap
+ ./configure
+ make
+
+3. Configuration file
+ See config/README for application runtime configuration options.
+
+4. Packet I/O
+ When passing a packet I/O device name to odp_pktio_open() one can explicitly
+ specify the used implementation internal pktio type. The pktio type can be
+ selected by adding a pktio type prefix to the device name separated by a
+ colon (<pktio_type>:<if_name>).
+
+ E.g.
+ socket:eth1
+ socket_xdp:eth2
+
+ The supported pktio types are:
+ dpdk
+ ipc
+ loop
+ null
+ pcap
+ socket
+ socket_mmap
+ socket_xdp
+ tap
+
+5. Random data
+ On x86 ODP_RANDOM_TRUE type random data is generated using rdseed [1] via
+ compiler builtin functions. If OpenSSL is not available or its use for
+ generating random data is disabled with the --disable-openssl-rand
+ configure option, ODP_RANDOM_CRYPTO type random data is generated using
+ rdrand [1].
+
+ Note that there may be issues with the quality or security of rdrand and
+ rdseed. [2]
+
+6. Event validation
+ ODP linux-generic implementation supports additional fast path event
+ validity checks which are disabled by default to minimize overhead. These
+ checks can be enabled with --enable-event-validation [abort/warn] or
+ --enabled-debug=full configuration options.
+
+ Event validation adds additional endmark data to ODP buffers and packets,
+ which is used to detect data writes outside allowed areas. Endmarks are
+ checked by the implementation each time application calls one the following
+ API functions:
+ - odp_buffer_free() / odp_buffer_free_multi()
+ - odp_buffer_is_valid()
+ - odp_event_free() / odp_event_free_multi() / odp_event_free_sp()
+ - odp_event_is_valid()
+ - odp_packet_free() / odp_packet_free_multi() / odp_packet_free_sp()
+ - odp_packet_is_valid()
+ - odp_queue_enq() / odp_queue_enq_multi()
+
+ Event validation can function in two modes: abort (default) and warn. In
+ abort mode the application is terminated immediately if an event validity
+ check fails. In warn mode only an error log message is printed.
+
+7. References
+ [1] Intel Digital Random Number Generator (DRNG) Software Implementation
+ Guide. John P Mechalas, 17 October 2018.
+ https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html
+
+ [2] RDRAND. Wikipedia, 29 September 2021.
+ https://en.wikipedia.org/wiki/RDRAND#Reception
diff --git a/platform/linux-generic/_fdserver.c b/platform/linux-generic/_fdserver.c
deleted file mode 100644
index d1b16f622..000000000
--- a/platform/linux-generic/_fdserver.c
+++ /dev/null
@@ -1,694 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * This file implements a file descriptor sharing server enabling
- * sharing of file descriptors between processes, regardless of fork time.
- *
- * File descriptors are process scoped, but they can be "sent and converted
- * on the fly" between processes using special unix domain socket ancillary
- * data.
- * The receiving process gets a file descriptor "pointing" to the same thing
- * as the one sent (but the value of the file descriptor itself may be different
- * from the one sent).
- * Because ODP applications are responsible for creating ODP threads (i.e.
- * pthreads or linux processes), ODP has no control on the order things happen:
- * Nothing prevent a thread A to fork B and C, and then C creating a pktio
- * which will be used by A and B to send/receive packets.
- * Assuming this pktio uses a file descriptor, the latter will need to be
- * shared between the processes, despite the "non convenient" fork time.
- * The shared memory allocator is likely to use this as well to be able to
- * share memory regardless of fork() time.
- * This server handles a table of {(context,key)<-> fd} pair, and is
- * interfaced by the following functions:
- *
- * _odp_fdserver_register_fd(context, key, fd_to_send);
- * _odp_fdserver_deregister_fd(context, key);
- * _odp_fdserver_lookup_fd(context, key);
- *
- * which are used to register/deregister or querry for file descriptor based
- * on a context and key value couple, which has to be unique.
- *
- * Note again that the file descriptors stored here are local to this server
- * process and get converted both when registered or looked up.
- */
-
-#include <odp_posix_extensions.h>
-#include <odp/api/spinlock.h>
-#include <odp_internal.h>
-#include <odp_debug_internal.h>
-#include <_fdserver_internal.h>
-#include <sys/prctl.h>
-#include <signal.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <sys/mman.h>
-#include <sys/wait.h>
-
-#define FDSERVER_SOCKPATH_MAXLEN 255
-#define FDSERVER_SOCK_FORMAT "%s/%s/odp-%d-fdserver"
-#define FDSERVER_SOCKDIR_FORMAT "%s/%s"
-#define FDSERVER_DEFAULT_DIR "/dev/shm"
-#define FDSERVER_BACKLOG 5
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-/* when accessing the client functions, clients should be mutexed: */
-static odp_spinlock_t *client_lock;
-
-/* define the tables of file descriptors handled by this server: */
-#define FDSERVER_MAX_ENTRIES 256
-typedef struct fdentry_s {
- fd_server_context_e context;
- uint64_t key;
- int fd;
-} fdentry_t;
-static fdentry_t *fd_table;
-static int fd_table_nb_entries;
-
-/*
- * define the message struct used for communication between client and server
- * (this single message is used in both direction)
- * The file descriptors are sent out of band as ancillary data for conversion.
- */
-typedef struct fd_server_msg {
- int command;
- fd_server_context_e context;
- uint64_t key;
-} fdserver_msg_t;
-/* possible commands are: */
-#define FD_REGISTER_REQ 1 /* client -> server */
-#define FD_REGISTER_ACK 2 /* server -> client */
-#define FD_REGISTER_NACK 3 /* server -> client */
-#define FD_LOOKUP_REQ 4 /* client -> server */
-#define FD_LOOKUP_ACK 5 /* server -> client */
-#define FD_LOOKUP_NACK 6 /* server -> client */
-#define FD_DEREGISTER_REQ 7 /* client -> server */
-#define FD_DEREGISTER_ACK 8 /* server -> client */
-#define FD_DEREGISTER_NACK 9 /* server -> client */
-#define FD_SERVERSTOP_REQ 10 /* client -> server (stops) */
-
-/*
- * Client and server function:
- * Send a fdserver_msg, possibly including a file descriptor, on the socket
- * This function is used both by:
- * -the client (sending a FD_REGISTER_REQ with a file descriptor to be shared,
- * or FD_LOOKUP_REQ/FD_DEREGISTER_REQ without a file descriptor)
- * -the server (sending FD_REGISTER_ACK/NACK, FD_LOOKUP_NACK,
- * FD_DEREGISTER_ACK/NACK... without a fd or a
- * FD_LOOKUP_ACK with a fd)
- * This function make use of the ancillary data (control data) to pass and
- * convert file descriptors over UNIX sockets
- * Return -1 on error, 0 on success.
- */
-static int send_fdserver_msg(int sock, int command,
- fd_server_context_e context, uint64_t key,
- int fd_to_send)
-{
- struct msghdr socket_message;
- struct iovec io_vector[1]; /* one msg frgmt only */
- struct cmsghdr *control_message = NULL;
- int *fd_location;
- fdserver_msg_t msg;
- int res;
-
- char ancillary_data[CMSG_SPACE(sizeof(int))];
-
- /* prepare the register request body (single framgent): */
- msg.command = command;
- msg.context = context;
- msg.key = key;
- io_vector[0].iov_base = &msg;
- io_vector[0].iov_len = sizeof(fdserver_msg_t);
-
- /* initialize socket message */
- memset(&socket_message, 0, sizeof(struct msghdr));
- socket_message.msg_iov = io_vector;
- socket_message.msg_iovlen = 1;
-
- if (fd_to_send >= 0) {
- /* provide space for the ancillary data */
- memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
- socket_message.msg_control = ancillary_data;
- socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
-
- /* initialize a single ancillary data element for fd passing */
- control_message = CMSG_FIRSTHDR(&socket_message);
- control_message->cmsg_level = SOL_SOCKET;
- control_message->cmsg_type = SCM_RIGHTS;
- control_message->cmsg_len = CMSG_LEN(sizeof(int));
- fd_location = (int *)(void *)CMSG_DATA(control_message);
- *fd_location = fd_to_send;
- }
- res = sendmsg(sock, &socket_message, 0);
- if (res < 0) {
- ODP_ERR("send_fdserver_msg: %s\n", strerror(errno));
- return(-1);
- }
-
- return 0;
-}
-
-/*
- * Client and server function
- * Receive a fdserver_msg, possibly including a file descriptor, on the
- * given socket.
- * This function is used both by:
- * -the server (receiving a FD_REGISTER_REQ with a file descriptor to be shared,
- * or FD_LOOKUP_REQ, FD_DEREGISTER_REQ without a file descriptor)
- * -the client (receiving FD_REGISTER_ACK...without a fd or a FD_LOOKUP_ACK with
- * a fd)
- * This function make use of the ancillary data (control data) to pass and
- * convert file descriptors over UNIX sockets.
- * Return -1 on error, 0 on success.
- */
-static int recv_fdserver_msg(int sock, int *command,
- fd_server_context_e *context, uint64_t *key,
- int *recvd_fd)
-{
- struct msghdr socket_message;
- struct iovec io_vector[1]; /* one msg frgmt only */
- struct cmsghdr *control_message = NULL;
- int *fd_location;
- fdserver_msg_t msg;
- char ancillary_data[CMSG_SPACE(sizeof(int))];
-
- memset(&socket_message, 0, sizeof(struct msghdr));
- memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
-
- /* setup a place to fill in message contents */
- io_vector[0].iov_base = &msg;
- io_vector[0].iov_len = sizeof(fdserver_msg_t);
- socket_message.msg_iov = io_vector;
- socket_message.msg_iovlen = 1;
-
- /* provide space for the ancillary data */
- socket_message.msg_control = ancillary_data;
- socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
-
- /* receive the message */
- if (recvmsg(sock, &socket_message, MSG_CMSG_CLOEXEC) < 0) {
- ODP_ERR("recv_fdserver_msg: %s\n", strerror(errno));
- return(-1);
- }
-
- *command = msg.command;
- *context = msg.context;
- *key = msg.key;
-
- /* grab the converted file descriptor (if any) */
- *recvd_fd = -1;
-
- if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
- return 0;
-
- /* iterate ancillary elements to find the file descriptor: */
- for (control_message = CMSG_FIRSTHDR(&socket_message);
- control_message != NULL;
- control_message = CMSG_NXTHDR(&socket_message, control_message)) {
- if ((control_message->cmsg_level == SOL_SOCKET) &&
- (control_message->cmsg_type == SCM_RIGHTS)) {
- fd_location = (int *)(void *)CMSG_DATA(control_message);
- *recvd_fd = *fd_location;
- break;
- }
- }
-
- return 0;
-}
-
-/* opens and returns a connected socket to the server */
-static int get_socket(void)
-{
- char sockpath[FDSERVER_SOCKPATH_MAXLEN];
- int s_sock; /* server socket */
- struct sockaddr_un remote;
- int len;
-
- /* construct the named socket path: */
- snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid,
- odp_global_data.main_pid);
-
- s_sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s_sock == -1) {
- ODP_ERR("cannot connect to server: %s\n", strerror(errno));
- return(-1);
- }
-
- remote.sun_family = AF_UNIX;
- strcpy(remote.sun_path, sockpath);
- len = strlen(remote.sun_path) + sizeof(remote.sun_family);
- if (connect(s_sock, (struct sockaddr *)&remote, len) == -1) {
- ODP_ERR("cannot connect to server: %s\n", strerror(errno));
- close(s_sock);
- return(-1);
- }
-
- return s_sock;
-}
-
-/*
- * Client function:
- * Register a file descriptor to the server. Return -1 on error.
- */
-int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
- int fd_to_send)
-{
- int s_sock; /* server socket */
- int res;
- int command;
- int fd;
-
- odp_spinlock_lock(client_lock);
-
- ODP_DBG("FD client register: pid=%d key=%" PRIu64 ", fd=%d\n",
- getpid(), key, fd_to_send);
-
- s_sock = get_socket();
- if (s_sock < 0) {
- odp_spinlock_unlock(client_lock);
- return(-1);
- }
-
- res = send_fdserver_msg(s_sock, FD_REGISTER_REQ, context, key,
- fd_to_send);
- if (res < 0) {
- ODP_ERR("fd registration failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
-
- if ((res < 0) || (command != FD_REGISTER_ACK)) {
- ODP_ERR("fd registration failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- close(s_sock);
-
- odp_spinlock_unlock(client_lock);
- return 0;
-}
-
-/*
- * Client function:
- * Deregister a file descriptor from the server. Return -1 on error.
- */
-int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key)
-{
- int s_sock; /* server socket */
- int res;
- int command;
- int fd;
-
- odp_spinlock_lock(client_lock);
-
- ODP_DBG("FD client deregister: pid=%d key=%" PRIu64 "\n",
- getpid(), key);
-
- s_sock = get_socket();
- if (s_sock < 0) {
- odp_spinlock_unlock(client_lock);
- return(-1);
- }
-
- res = send_fdserver_msg(s_sock, FD_DEREGISTER_REQ, context, key, -1);
- if (res < 0) {
- ODP_ERR("fd de-registration failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
-
- if ((res < 0) || (command != FD_DEREGISTER_ACK)) {
- ODP_ERR("fd de-registration failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- close(s_sock);
-
- odp_spinlock_unlock(client_lock);
- return 0;
-}
-
-/*
- * client function:
- * lookup a file descriptor from the server. return -1 on error,
- * or the file descriptor on success (>=0).
- */
-int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key)
-{
- int s_sock; /* server socket */
- int res;
- int command;
- int fd;
-
- odp_spinlock_lock(client_lock);
-
- s_sock = get_socket();
- if (s_sock < 0) {
- odp_spinlock_unlock(client_lock);
- return(-1);
- }
-
- res = send_fdserver_msg(s_sock, FD_LOOKUP_REQ, context, key, -1);
- if (res < 0) {
- ODP_ERR("fd lookup failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
-
- if ((res < 0) || (command != FD_LOOKUP_ACK)) {
- ODP_ERR("fd lookup failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- close(s_sock);
- ODP_DBG("FD client lookup: pid=%d, key=%" PRIu64 ", fd=%d\n",
- getpid(), key, fd);
-
- odp_spinlock_unlock(client_lock);
- return fd;
-}
-
-/*
- * request server terminaison:
- */
-static int stop_server(void)
-{
- int s_sock; /* server socket */
- int res;
-
- odp_spinlock_lock(client_lock);
-
- ODP_DBG("FD sending server stop request\n");
-
- s_sock = get_socket();
- if (s_sock < 0) {
- odp_spinlock_unlock(client_lock);
- return(-1);
- }
-
- res = send_fdserver_msg(s_sock, FD_SERVERSTOP_REQ, 0, 0, -1);
- if (res < 0) {
- ODP_ERR("fd stop request failure\n");
- close(s_sock);
- odp_spinlock_unlock(client_lock);
- return -1;
- }
-
- close(s_sock);
-
- odp_spinlock_unlock(client_lock);
- return 0;
-}
-
-/*
- * server function
- * receive a client request and handle it.
- * Always returns 0 unless a stop request is received.
- */
-static int handle_request(int client_sock)
-{
- int command;
- fd_server_context_e context;
- uint64_t key;
- int fd;
- int i;
-
- /* get a client request: */
- recv_fdserver_msg(client_sock, &command, &context, &key, &fd);
- switch (command) {
- case FD_REGISTER_REQ:
- if ((fd < 0) || (context >= FD_SRV_CTX_END)) {
- ODP_ERR("Invalid register fd or context\n");
- send_fdserver_msg(client_sock, FD_REGISTER_NACK,
- FD_SRV_CTX_NA, 0, -1);
- return 0;
- }
-
- /* store the file descriptor in table: */
- if (fd_table_nb_entries < FDSERVER_MAX_ENTRIES) {
- fd_table[fd_table_nb_entries].context = context;
- fd_table[fd_table_nb_entries].key = key;
- fd_table[fd_table_nb_entries++].fd = fd;
- ODP_DBG("storing {ctx=%d, key=%" PRIu64 "}->fd=%d\n",
- context, key, fd);
- } else {
- ODP_ERR("FD table full\n");
- send_fdserver_msg(client_sock, FD_REGISTER_NACK,
- FD_SRV_CTX_NA, 0, -1);
- return 0;
- }
-
- send_fdserver_msg(client_sock, FD_REGISTER_ACK,
- FD_SRV_CTX_NA, 0, -1);
- break;
-
- case FD_LOOKUP_REQ:
- if (context >= FD_SRV_CTX_END) {
- ODP_ERR("invalid lookup context\n");
- send_fdserver_msg(client_sock, FD_LOOKUP_NACK,
- FD_SRV_CTX_NA, 0, -1);
- return 0;
- }
-
- /* search key in table and sent reply: */
- for (i = 0; i < fd_table_nb_entries; i++) {
- if ((fd_table[i].context == context) &&
- (fd_table[i].key == key)) {
- fd = fd_table[i].fd;
- ODP_DBG("lookup {ctx=%d,"
- " key=%" PRIu64 "}->fd=%d\n",
- context, key, fd);
- send_fdserver_msg(client_sock,
- FD_LOOKUP_ACK, context, key,
- fd);
- return 0;
- }
- }
-
- /* context+key not found... send nack */
- send_fdserver_msg(client_sock, FD_LOOKUP_NACK, context, key,
- -1);
- break;
-
- case FD_DEREGISTER_REQ:
- if (context >= FD_SRV_CTX_END) {
- ODP_ERR("invalid deregister context\n");
- send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
- FD_SRV_CTX_NA, 0, -1);
- return 0;
- }
-
- /* search key in table and remove it if found, and reply: */
- for (i = 0; i < fd_table_nb_entries; i++) {
- if ((fd_table[i].context == context) &&
- (fd_table[i].key == key)) {
- ODP_DBG("drop {ctx=%d,"
- " key=%" PRIu64 "}->fd=%d\n",
- context, key, fd_table[i].fd);
- close(fd_table[i].fd);
- fd_table[i] = fd_table[--fd_table_nb_entries];
- send_fdserver_msg(client_sock,
- FD_DEREGISTER_ACK,
- context, key, -1);
- return 0;
- }
- }
-
- /* key not found... send nack */
- send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
- context, key, -1);
- break;
-
- case FD_SERVERSTOP_REQ:
- ODP_DBG("Stoping FD server\n");
- return 1;
-
- default:
- ODP_ERR("Unexpected request\n");
- break;
- }
- return 0;
-}
-
-/*
- * server function
- * loop forever, handling client requests one by one
- */
-static void wait_requests(int sock)
-{
- int c_socket; /* client connection */
- unsigned int addr_sz;
- struct sockaddr_un remote;
-
- for (;;) {
- addr_sz = sizeof(remote);
- c_socket = accept(sock, (struct sockaddr *)&remote, &addr_sz);
- if (c_socket == -1) {
- ODP_ERR("wait_requests: %s\n", strerror(errno));
- return;
- }
-
- if (handle_request(c_socket))
- break;
- close(c_socket);
- }
- close(c_socket);
-}
-
-/*
- * Create a unix domain socket and fork a process to listen to incoming
- * requests.
- */
-int _odp_fdserver_init_global(void)
-{
- char sockpath[FDSERVER_SOCKPATH_MAXLEN];
- int sock;
- struct sockaddr_un local;
- pid_t server_pid;
- int res;
-
- /* create the client spinlock that any client can see: */
- client_lock = mmap(NULL, sizeof(odp_spinlock_t), PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-
- odp_spinlock_init(client_lock);
-
- snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKDIR_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid);
-
- mkdir(sockpath, 0744);
-
- /* construct the server named socket path: */
- snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid,
- odp_global_data.main_pid);
-
- /* create UNIX domain socket: */
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock == -1) {
- ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
- return(-1);
- }
-
- /* remove previous named socket if it already exists: */
- unlink(sockpath);
-
- /* bind to new named socket: */
- local.sun_family = AF_UNIX;
- strncpy(local.sun_path, sockpath, sizeof(local.sun_path));
- res = bind(sock, (struct sockaddr *)&local, sizeof(struct sockaddr_un));
- if (res == -1) {
- ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
- close(sock);
- return(-1);
- }
-
- /* listen for incoming conections: */
- if (listen(sock, FDSERVER_BACKLOG) == -1) {
- ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
- close(sock);
- return(-1);
- }
-
- /* fork a server process: */
- server_pid = fork();
- if (server_pid == -1) {
- ODP_ERR("Could not fork!\n");
- close(sock);
- return(-1);
- }
-
- if (server_pid == 0) { /*child */
- /* TODO: pin the server on appropriate service cpu mask */
- /* when (if) we can agree on the usage of service mask */
-
- /* request to be killed if parent dies, hence avoiding */
- /* orphans being "adopted" by the init process... */
- prctl(PR_SET_PDEATHSIG, SIGTERM);
-
- /* allocate the space for the file descriptor<->key table: */
- fd_table = malloc(FDSERVER_MAX_ENTRIES * sizeof(fdentry_t));
- if (!fd_table) {
- ODP_ERR("maloc failed!\n");
- exit(1);
- }
-
- /* wait for clients requests */
- wait_requests(sock); /* Returns when server is stopped */
- close(sock);
-
- /* release the file descriptor table: */
- free(fd_table);
-
- exit(0);
- }
-
- /* parent */
- close(sock);
- return 0;
-}
-
-/*
- * Terminate the server
- */
-int _odp_fdserver_term_global(void)
-{
- int status;
- char sockpath[FDSERVER_SOCKPATH_MAXLEN];
-
- /* close the server and wait for child terminaison*/
- stop_server();
- wait(&status);
-
- /* construct the server named socket path: */
- snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid,
- odp_global_data.main_pid);
-
- /* delete the UNIX domain socket: */
- unlink(sockpath);
-
- /* delete shm files directory */
- snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKDIR_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid);
- rmdir(sockpath);
-
- return 0;
-}
diff --git a/platform/linux-generic/_ishm.c b/platform/linux-generic/_ishm.c
deleted file mode 100644
index 892e889bf..000000000
--- a/platform/linux-generic/_ishm.c
+++ /dev/null
@@ -1,1810 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* This file handles the internal shared memory: internal shared memory
- * is memory which is sharable by all ODP threads regardless of how the
- * ODP thread is implemented (pthread or process) and regardless of fork()
- * time.
- * Moreover, when reserved with the _ODP_ISHM_SINGLE_VA flag,
- * internal shared memory is guaranteed to always be located at the same virtual
- * address, i.e. pointers to internal shared memory are fully shareable
- * between odp threads (regardless of thread type or fork time) in that case.
- * Internal shared memory is mainly meant to be used internaly within ODP
- * (hence its name), but may also be allocated by odp applications and drivers,
- * in the future (through these interfaces).
- * To guarrentee this full pointer shareability (when reserved with the
- * _ODP_ISHM_SINGLE_VA flag) internal shared memory is handled as follows:
- * At global_init time, a huge virtual address space reservation is performed.
- * Note that this is just reserving virtual space, not physical memory.
- * Because all ODP threads (pthreads or processes) are descendants of the ODP
- * instantiation process, this VA space is inherited by all ODP threads.
- * When internal shmem reservation actually occurs, and
- * when reserved with the _ODP_ISHM_SINGLE_VA flag, physical memory is
- * allocated, and mapped (MAP_FIXED) to some part in the huge preallocated
- * address space area:
- * because this virtual address space is common to all ODP threads, we
- * know this mapping will succeed, and not clash with anything else.
- * Hence, an ODP threads which perform a lookup for the same ishm block
- * can map it at the same VA address.
- * When internal shared memory is released, the physical memory is released
- * and the corresponding virtual space returned to its "pool" of preallocated
- * virtual space (assuming it was allocated from there).
- * Note, though, that, if 2 linux processes share the same ishm block,
- * the virtual space is marked as released as soon as one of the processes
- * releases the ishm block, but the physical memory space is actually released
- * by the kernel once all processes have done a ishm operation (i,e. a sync).
- * This is due to the fact that linux does not contain any syscall to unmap
- * memory from a different process.
- *
- * This file contains functions to handle the VA area (handling fragmentation
- * and defragmentation resulting from different allocs/release) and also
- * define the functions to allocate, release and lookup internal shared
- * memory:
- * _odp_ishm_reserve(), _odp_ishm_free*() and _odp_ishm_lookup*()...
- */
-#include <odp_posix_extensions.h>
-#include <odp_config_internal.h>
-#include <odp_internal.h>
-#include <odp/api/spinlock.h>
-#include <odp/api/align.h>
-#include <odp/api/system_info.h>
-#include <odp/api/debug.h>
-#include <odp_shm_internal.h>
-#include <odp_debug_internal.h>
-#include <odp_align_internal.h>
-#include <_fdserver_internal.h>
-#include <_ishm_internal.h>
-#include <_ishmphy_internal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <inttypes.h>
-#include <sys/wait.h>
-#include <libgen.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-/*
- * Maximum number of internal shared memory blocks.
- *
- * This is the number of separate ISHM areas that can be reserved concurrently
- * (Note that freeing such blocks may take time, or possibly never happen
- * if some of the block ownwers never procsync() after free). This number
- * should take that into account)
- */
-#define ISHM_MAX_NB_BLOCKS 128
-
-/*
- * Maximum internal shared memory block name length in chars
- * probably taking the same number as SHM name size make sense at this stage
- */
-#define ISHM_NAME_MAXLEN 128
-
-/*
- * Linux underlying file name: <directory>/odp-<odp_pid>-ishm-<name>
- * The <name> part may be replaced by a sequence number if no specific
- * name is given at reserve time
- * <directory> is either /dev/shm or the hugepagefs mount point for default
- * size.
- * (searched at init time)
- */
-#define ISHM_FILENAME_MAXLEN (ISHM_NAME_MAXLEN + 64)
-#define ISHM_FILENAME_FORMAT "%s/odp-%d-ishm-%s"
-#define ISHM_FILENAME_NORMAL_PAGE_DIR "/dev/shm"
-#define _ODP_FILES_FMT "odp-%d-"
-
-/*
- * when the memory is to be shared with an external entity (such as another
- * ODP instance or an OS process not part of this ODP instance) then a
- * export file is created describing the exported memory: this defines the
- * location and the filename format of this description file
- */
-#define ISHM_EXPTNAME_FORMAT "%s/%s/odp-%d-shm-%s"
-
-/*
- * At worse case the virtual space gets so fragmented that there is
- * a unallocated fragment between each allocated fragment:
- * In that case, the number of fragments to take care of is twice the
- * number of ISHM blocks + 1.
- */
-#define ISHM_NB_FRAGMNTS (ISHM_MAX_NB_BLOCKS * 2 + 1)
-
-/*
- * when a memory block is to be exported outside its ODP instance,
- * an block 'attribute file' is created in /dev/shm/odp-<pid>-shm-<name>.
- * The information given in this file is according to the following:
- */
-#define EXPORT_FILE_LINE1_FMT "ODP exported shm block info:"
-#define EXPORT_FILE_LINE2_FMT "ishm_blockname: %s"
-#define EXPORT_FILE_LINE3_FMT "file: %s"
-#define EXPORT_FILE_LINE4_FMT "length: %" PRIu64
-#define EXPORT_FILE_LINE5_FMT "flags: %" PRIu32
-#define EXPORT_FILE_LINE6_FMT "user_length: %" PRIu64
-#define EXPORT_FILE_LINE7_FMT "user_flags: %" PRIu32
-#define EXPORT_FILE_LINE8_FMT "align: %" PRIu32
-/*
- * A fragment describes a piece of the shared virtual address space,
- * and is allocated only when allocation is done with the _ODP_ISHM_SINGLE_VA
- * flag:
- * A fragment is said to be used when it actually does represent some
- * portion of the virtual address space, and is said to be unused when
- * it does not (so at start, one single fragment is used -describing the
- * whole address space as unallocated-, and all others are unused).
- * Fragments get used as address space fragmentation increases.
- * A fragment is allocated if the piece of address space it
- * describes is actually used by a shared memory block.
- * Allocated fragments get their block_index set >=0.
- */
-typedef struct ishm_fragment {
- struct ishm_fragment *prev; /* not used when the fragment is unused */
- struct ishm_fragment *next;
- void *start; /* start of segment (VA) */
- uintptr_t len; /* length of segment. multiple of page size */
- int block_index; /* -1 for unallocated fragments */
-} ishm_fragment_t;
-
-/*
- * A block describes a piece of reserved memory: Any successful ishm_reserve()
- * will allocate a block. A ishm_reserve() with the _ODP_ISHM_SINGLE_VA flag set
- * will allocate both a block and a fragment.
- * Blocks contain only global data common to all processes.
- */
-typedef enum {UNKNOWN, HUGE, NORMAL, EXTERNAL} huge_flag_t;
-typedef struct ishm_block {
- char name[ISHM_NAME_MAXLEN]; /* name for the ishm block (if any) */
- char filename[ISHM_FILENAME_MAXLEN]; /* name of the .../odp-* file */
- char exptname[ISHM_FILENAME_MAXLEN]; /* name of the export file */
- uint32_t user_flags; /* any flags the user want to remember. */
- uint32_t flags; /* block creation flags. */
- uint32_t external_fd:1; /* block FD was externally provided */
- uint64_t user_len; /* length, as requested at reserve time. */
- void *start; /* only valid if _ODP_ISHM_SINGLE_VA is set*/
- uint64_t len; /* length. multiple of page size. 0 if free*/
- ishm_fragment_t *fragment; /* used when _ODP_ISHM_SINGLE_VA is used */
- huge_flag_t huge; /* page type: external means unknown here. */
- uint64_t seq; /* sequence number, incremented on alloc and free */
- uint64_t refcnt;/* number of linux processes mapping this block */
-} ishm_block_t;
-
-/*
- * Table of blocks describing allocated internal shared memory
- * This table is visible to every ODP thread (linux process or pthreads).
- * (it is allocated shared at odp init time and is therefore inherited by all)
- * Table index is used as handle, so it cannot move!. Entry is regarded as
- * free when len==0
- */
-typedef struct {
- odp_spinlock_t lock;
- uint64_t dev_seq; /* used when creating device names */
- uint32_t odpthread_cnt; /* number of running ODP threads */
- ishm_block_t block[ISHM_MAX_NB_BLOCKS];
-} ishm_table_t;
-static ishm_table_t *ishm_tbl;
-
-/*
- * Process local table containing the list of (believed) allocated blocks seen
- * from the current process. There is one such table per linux process. linux
- * threads within a process shares this table.
- * The contents within this table may become obsolete when other processes
- * reserve/free ishm blocks. This is what the procsync() function
- * catches by comparing the block sequence number with the one in this table.
- * This table is filled at ishm_reserve and ishm_lookup time.
- * Entries are removed at ishm_free or procsync time.
- * Note that flags and len are present in this table and seems to be redundant
- * with those present in the ishm block table: but this is not fully true:
- * When ishm_sync() detects obsolete mappings and tries to remove them,
- * the entry in the ishm block table is then obsolete, and the values which are
- * found in this table must be used to perform the ummap.
- * (and the values in the block tables are needed at lookup time...)
- */
-typedef struct {
- int thrd_refcnt; /* number of pthreads in this process, really */
- struct {
- int block_index; /* entry in the ishm_tbl */
- uint32_t flags; /* flags used at creation time */
- uint64_t seq;
- void *start; /* start of block (VA) */
- uint64_t len; /* length of block. multiple of page size */
- int fd; /* file descriptor used for this block */
- } entry[ISHM_MAX_NB_BLOCKS];
- int nb_entries;
-} ishm_proctable_t;
-static ishm_proctable_t *ishm_proctable;
-
-/*
- * Table of fragments describing the common virtual address space:
- * This table is visible to every ODP thread (linux process or pthreads).
- * (it is allocated at odp init time and is therefore inherited by all)
- */
-typedef struct {
- ishm_fragment_t fragment[ISHM_NB_FRAGMNTS];
- ishm_fragment_t *used_fragmnts; /* ordered by increasing start addr */
- ishm_fragment_t *unused_fragmnts;
-} ishm_ftable_t;
-static ishm_ftable_t *ishm_ftbl;
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-/* prototypes: */
-static void procsync(void);
-
-/*
- * Take a piece of the preallocated virtual space to fit "size" bytes.
- * (best fit). Size must be rounded up to an integer number of pages size.
- * Possibly split the fragment to keep track of remaining space.
- * Returns the allocated fragment (best_fragment) and the corresponding address.
- * External caller must ensure mutex before the call!
- */
-static void *alloc_fragment(uintptr_t size, int block_index, intptr_t align,
- ishm_fragment_t **best_fragmnt)
-{
- ishm_fragment_t *fragmnt;
- *best_fragmnt = NULL;
- ishm_fragment_t *rem_fragmnt;
- uintptr_t border;/* possible start of new fragment (next alignement) */
- intptr_t left; /* room remaining after, if the segment is allocated */
- uintptr_t remainder = ODP_CONFIG_ISHM_VA_PREALLOC_SZ;
-
- /*
- * search for the best bit, i.e. search for the unallocated fragment
- * would give less remainder if the new fragment was allocated within
- * it:
- */
- for (fragmnt = ishm_ftbl->used_fragmnts;
- fragmnt; fragmnt = fragmnt->next) {
- /* skip allocated segment: */
- if (fragmnt->block_index >= 0)
- continue;
- /* skip too short segment: */
- border = ((uintptr_t)fragmnt->start + align - 1) & (-align);
- left =
- ((uintptr_t)fragmnt->start + fragmnt->len) - (border + size);
- if (left < 0)
- continue;
- /* remember best fit: */
- if ((uintptr_t)left < remainder) {
- remainder = left; /* best, so far */
- *best_fragmnt = fragmnt;
- }
- }
-
- if (!(*best_fragmnt)) {
- ODP_ERR("unable to get virtual address for shmem block!\n.");
- return NULL;
- }
-
- (*best_fragmnt)->block_index = block_index;
- border = ((uintptr_t)(*best_fragmnt)->start + align - 1) & (-align);
-
- /*
- * if there is room between previous fragment and new one, (due to
- * alignment requirement) then fragment (split) the space between
- * the end of the previous fragment and the beginning of the new one:
- */
- if (border - (uintptr_t)(*best_fragmnt)->start > 0) {
- /* fragment space, i.e. take a new fragment descriptor... */
- rem_fragmnt = ishm_ftbl->unused_fragmnts;
- if (!rem_fragmnt) {
- ODP_ERR("unable to get shmem fragment descriptor!\n.");
- return NULL;
- }
- ishm_ftbl->unused_fragmnts = rem_fragmnt->next;
-
- /* and link it between best_fragmnt->prev and best_fragmnt */
- if ((*best_fragmnt)->prev)
- (*best_fragmnt)->prev->next = rem_fragmnt;
- else
- ishm_ftbl->used_fragmnts = rem_fragmnt;
- rem_fragmnt->prev = (*best_fragmnt)->prev;
- (*best_fragmnt)->prev = rem_fragmnt;
- rem_fragmnt->next = (*best_fragmnt);
-
- /* update length: rem_fragmnt getting space before border */
- rem_fragmnt->block_index = -1;
- rem_fragmnt->start = (*best_fragmnt)->start;
- rem_fragmnt->len = border - (uintptr_t)(*best_fragmnt)->start;
- (*best_fragmnt)->start =
- (void *)((uintptr_t)rem_fragmnt->start + rem_fragmnt->len);
- (*best_fragmnt)->len -= rem_fragmnt->len;
- }
-
- /* if this was a perfect fit, i.e. no free space follows, we are done */
- if (remainder == 0)
- return (*best_fragmnt)->start;
-
- /* otherwise, fragment space, i.e. take a new fragment descriptor... */
- rem_fragmnt = ishm_ftbl->unused_fragmnts;
- if (!rem_fragmnt) {
- ODP_ERR("unable to get shmem fragment descriptor!\n.");
- return (*best_fragmnt)->start;
- }
- ishm_ftbl->unused_fragmnts = rem_fragmnt->next;
-
- /* ... double link it... */
- rem_fragmnt->next = (*best_fragmnt)->next;
- rem_fragmnt->prev = (*best_fragmnt);
- if ((*best_fragmnt)->next)
- (*best_fragmnt)->next->prev = rem_fragmnt;
- (*best_fragmnt)->next = rem_fragmnt;
-
- /* ... and keep track of the remainder */
- (*best_fragmnt)->len = size;
- rem_fragmnt->len = remainder;
- rem_fragmnt->start = (void *)((char *)(*best_fragmnt)->start + size);
- rem_fragmnt->block_index = -1;
-
- return (*best_fragmnt)->start;
-}
-
-/*
- * Free a portion of virtual space.
- * Possibly defragment, if the freed fragment is adjacent to another
- * free virtual fragment.
- * External caller must ensure mutex before the call!
- */
-static void free_fragment(ishm_fragment_t *fragmnt)
-{
- ishm_fragment_t *prev_f;
- ishm_fragment_t *next_f;
-
- /* sanity check */
- if (!fragmnt)
- return;
-
- prev_f = fragmnt->prev;
- next_f = fragmnt->next;
-
- /* free the fragment */
- fragmnt->block_index = -1;
-
- /* check if the previous fragment is also free: if so, defragment */
- if (prev_f && (prev_f->block_index < 0)) {
- fragmnt->start = prev_f->start;
- fragmnt->len += prev_f->len;
- if (prev_f->prev) {
- prev_f->prev->next = fragmnt;
- } else {
- if (ishm_ftbl->used_fragmnts == prev_f)
- ishm_ftbl->used_fragmnts = fragmnt;
- else
- ODP_ERR("corrupted fragment list!.\n");
- }
- fragmnt->prev = prev_f->prev;
-
- /* put removed fragment in free list */
- prev_f->prev = NULL;
- prev_f->next = ishm_ftbl->unused_fragmnts;
- ishm_ftbl->unused_fragmnts = prev_f;
- }
-
- /* check if the next fragment is also free: if so, defragment */
- if (next_f && (next_f->block_index < 0)) {
- fragmnt->len += next_f->len;
- if (next_f->next)
- next_f->next->prev = fragmnt;
- fragmnt->next = next_f->next;
-
- /* put removed fragment in free list */
- next_f->prev = NULL;
- next_f->next = ishm_ftbl->unused_fragmnts;
- ishm_ftbl->unused_fragmnts = next_f;
- }
-}
-
-/*
- * Create file with size len. returns -1 on error
- * Creates a file to /dev/shm/odp-<pid>-<sequence_or_name> (for normal pages)
- * or /mnt/huge/odp-<pid>-<sequence_or_name> (for huge pages)
- * Return the new file descriptor, or -1 on error.
- */
-static int create_file(int block_index, huge_flag_t huge, uint64_t len,
- uint32_t flags, uint32_t align)
-{
- char *name;
- int fd;
- ishm_block_t *new_block; /* entry in the main block table */
- char seq_string[ISHM_FILENAME_MAXLEN]; /* used to construct filename*/
- char filename[ISHM_FILENAME_MAXLEN]; /* filename in /dev/shm or
- * /mnt/huge */
- int oflag = O_RDWR | O_CREAT | O_TRUNC; /* flags for open */
- FILE *export_file;
- char dir[ISHM_FILENAME_MAXLEN];
-
- new_block = &ishm_tbl->block[block_index];
- name = new_block->name;
-
- /* create the filename: */
- snprintf(seq_string, ISHM_FILENAME_MAXLEN, "%08" PRIu64,
- ishm_tbl->dev_seq++);
-
- /* huge dir must be known to create files there!: */
- if ((huge == HUGE) &&
- (!odp_global_data.hugepage_info.default_huge_page_dir))
- return -1;
-
- if (huge == HUGE)
- snprintf(dir, ISHM_FILENAME_MAXLEN, "%s/%s",
- odp_global_data.hugepage_info.default_huge_page_dir,
- odp_global_data.uid);
- else
- snprintf(dir, ISHM_FILENAME_MAXLEN, "%s/%s",
- odp_global_data.shm_dir,
- odp_global_data.uid);
-
- snprintf(filename, ISHM_FILENAME_MAXLEN,
- ISHM_FILENAME_FORMAT,
- dir,
- odp_global_data.main_pid,
- (name && name[0]) ? name : seq_string);
-
- mkdir(dir, 0744);
-
- fd = open(filename, oflag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd < 0) {
- if (huge == HUGE)
- ODP_DBG("open failed for %s: %s.\n",
- filename, strerror(errno));
- else
- ODP_ERR("open failed for %s: %s.\n",
- filename, strerror(errno));
- return -1;
- }
-
- if (ftruncate(fd, len) == -1) {
- ODP_ERR("ftruncate failed: fd=%d, err=%s.\n",
- fd, strerror(errno));
- close(fd);
- unlink(filename);
- return -1;
- }
-
-
- /* if _ODP_ISHM_EXPORT is set, create a description file for
- * external ref:
- */
- if (flags & _ODP_ISHM_EXPORT) {
- strncpy(new_block->filename, filename,
- ISHM_FILENAME_MAXLEN - 1);
- snprintf(new_block->exptname, ISHM_FILENAME_MAXLEN,
- ISHM_EXPTNAME_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid,
- odp_global_data.main_pid,
- (name && name[0]) ? name : seq_string);
- export_file = fopen(new_block->exptname, "w");
- if (export_file == NULL) {
- ODP_ERR("open failed: err=%s.\n",
- strerror(errno));
- new_block->exptname[0] = 0;
- } else {
- fprintf(export_file, EXPORT_FILE_LINE1_FMT "\n");
- fprintf(export_file, EXPORT_FILE_LINE2_FMT "\n", name);
- fprintf(export_file, EXPORT_FILE_LINE3_FMT "\n",
- new_block->filename);
- fprintf(export_file, EXPORT_FILE_LINE4_FMT "\n", len);
- fprintf(export_file, EXPORT_FILE_LINE5_FMT "\n", flags);
- fprintf(export_file, EXPORT_FILE_LINE6_FMT "\n",
- new_block->user_len);
- fprintf(export_file, EXPORT_FILE_LINE7_FMT "\n",
- new_block->user_flags);
- fprintf(export_file, EXPORT_FILE_LINE8_FMT "\n", align);
-
- fclose(export_file);
- }
- } else {
- new_block->exptname[0] = 0;
- /* remove the file from the filesystem, keeping its fd open */
- unlink(filename);
- }
-
- return fd;
-}
-
-/* delete the files related to a given ishm block: */
-static void delete_file(ishm_block_t *block)
-{
- /* remove the .../odp-* file, unless fd was external: */
- if (block->filename[0] != 0)
- unlink(block->filename);
- /* also remove possible description file (if block was exported): */
- if (block->exptname[0] != 0)
- unlink(block->exptname);
-}
-
-/*
- * performs the mapping, possibly allocating a fragment of the pre-reserved
- * VA space if the _ODP_ISHM_SINGLE_VA flag was given.
- * Sets fd, and returns the mapping address.
- * This function will also set the _ODP_ISHM_SINGLE_VA flag if the alignment
- * requires it
- * Mutex must be assured by the caller.
- */
-static void *do_map(int block_index, uint64_t len, uint32_t align,
- uint32_t flags, huge_flag_t huge, int *fd)
-{
- ishm_block_t *new_block; /* entry in the main block table */
- void *addr = NULL;
- void *mapped_addr;
- ishm_fragment_t *fragment = NULL;
-
- new_block = &ishm_tbl->block[block_index];
-
- /*
- * Creates a file to /dev/shm/odp-<pid>-<sequence> (for normal pages)
- * or /mnt/huge/odp-<pid>-<sequence> (for huge pages)
- * unless a fd was already given
- */
- if (*fd < 0) {
- *fd = create_file(block_index, huge, len, flags, align);
- if (*fd < 0)
- return NULL;
- } else {
- new_block->filename[0] = 0;
- }
-
- /* allocate an address range in the prebooked VA area if needed */
- if (flags & _ODP_ISHM_SINGLE_VA) {
- addr = alloc_fragment(len, block_index, align, &fragment);
- if (!addr) {
- ODP_ERR("alloc_fragment failed.\n");
- if (!new_block->external_fd) {
- close(*fd);
- *fd = -1;
- delete_file(new_block);
- }
- return NULL;
- }
- ishm_tbl->block[block_index].fragment = fragment;
- }
-
- /* try to mmap: */
- mapped_addr = _odp_ishmphy_map(*fd, addr, len, flags);
- if (mapped_addr == NULL) {
- if (flags & _ODP_ISHM_SINGLE_VA)
- free_fragment(fragment);
- if (!new_block->external_fd) {
- close(*fd);
- *fd = -1;
- delete_file(new_block);
- }
- return NULL;
- }
-
- return mapped_addr;
-}
-
-/*
- * Performs an extra mapping (for a process trying to see an existing block
- * i.e. performing a lookup).
- * Mutex must be assured by the caller.
- */
-static void *do_remap(int block_index, int fd)
-{
- void *mapped_addr;
- ishm_fragment_t *fragment;
- uint64_t len;
- uint32_t flags;
-
- len = ishm_tbl->block[block_index].len;
- flags = ishm_tbl->block[block_index].flags;
-
- if (flags & _ODP_ISHM_SINGLE_VA) {
- fragment = ishm_tbl->block[block_index].fragment;
- if (!fragment) {
- ODP_ERR("invalid fragment failure.\n");
- return NULL;
- }
-
- /* try to mmap: */
- mapped_addr = _odp_ishmphy_map(fd, fragment->start, len, flags);
- if (mapped_addr == NULL)
- return NULL;
- return mapped_addr;
- }
-
- /* try to mmap: */
- mapped_addr = _odp_ishmphy_map(fd, NULL, len, flags);
- if (mapped_addr == NULL)
- return NULL;
-
- return mapped_addr;
-}
-
-/*
- * Performs unmapping, possibly freeing a prereserved VA space fragment,
- * if the _ODP_ISHM_SINGLE_VA flag was set at alloc time
- * Mutex must be assured by the caller.
- */
-static int do_unmap(void *start, uint64_t size, uint32_t flags,
- int block_index)
-{
- int ret;
-
- if (start)
- ret = _odp_ishmphy_unmap(start, size, flags);
- else
- ret = 0;
-
- if ((block_index >= 0) && (flags & _ODP_ISHM_SINGLE_VA)) {
- /* mark reserved address space as free */
- free_fragment(ishm_tbl->block[block_index].fragment);
- }
-
- return ret;
-}
-
-/*
- * Search for a given used and allocated block name.
- * (search is performed in the global ishm table)
- * Returns the index of the found block (if any) or -1 if none.
- * Mutex must be assured by the caller.
- */
-static int find_block_by_name(const char *name)
-{
- int i;
-
- if (name == NULL || name[0] == 0)
- return -1;
-
- for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
- if ((ishm_tbl->block[i].len) &&
- (strcmp(name, ishm_tbl->block[i].name) == 0))
- return i;
- }
-
- return -1;
-}
-
-/*
- * Search for a block by address (only works when flag _ODP_ISHM_SINGLE_VA
- * was set at reserve() time, or if the block is already known by this
- * process).
- * Search is performed in the process table and in the global ishm table.
- * The provided address does not have to be at start: any address
- * within the fragment is OK.
- * Returns the index to the found block (if any) or -1 if none.
- * Mutex must be assured by the caller.
- */
-static int find_block_by_address(void *addr)
-{
- int block_index;
- int i;
- ishm_fragment_t *fragmnt;
-
- /*
- * first check if there is already a process known block for this
- * address
- */
- for (i = 0; i < ishm_proctable->nb_entries; i++) {
- block_index = ishm_proctable->entry[i].block_index;
- if ((addr > ishm_proctable->entry[i].start) &&
- ((char *)addr < ((char *)ishm_proctable->entry[i].start +
- ishm_tbl->block[block_index].len)))
- return block_index;
- }
-
- /*
- * then check if there is a existing single VA block known by some other
- * process and containing the given address
- */
- for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
- if ((!ishm_tbl->block[i].len) ||
- (!(ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA)))
- continue;
- fragmnt = ishm_tbl->block[i].fragment;
- if (!fragmnt) {
- ODP_ERR("find_fragment: invalid NULL fragment\n");
- return -1;
- }
- if ((addr >= fragmnt->start) &&
- ((char *)addr < ((char *)fragmnt->start + fragmnt->len)))
- return i;
- }
-
- /* address does not belong to any accessible block: */
- return -1;
-}
-
-/*
- * Search a given ishm block in the process local table. Return its index
- * in the process table or -1 if not found (meaning that the ishm table
- * block index was not referenced in the process local table, i.e. the
- * block is known by some other process, but not by the current process).
- * Caller must assure mutex.
- */
-static int procfind_block(int block_index)
-{
- int i;
-
- for (i = 0; i < ishm_proctable->nb_entries; i++) {
- if (ishm_proctable->entry[i].block_index == block_index)
- return i;
- }
- return -1;
-}
-
-/*
- * Release the physical memory mapping for blocks which have been freed
- * by other processes. Caller must ensure mutex.
- * Mutex must be assured by the caller.
- */
-static void procsync(void)
-{
- int i = 0;
- int last;
- ishm_block_t *block;
-
- last = ishm_proctable->nb_entries;
- while (i < last) {
- /* if the process sequence number doesn't match the main
- * table seq number, this entry is obsolete
- */
- block = &ishm_tbl->block[ishm_proctable->entry[i].block_index];
- if (ishm_proctable->entry[i].seq != block->seq) {
- /* obsolete entry: free memory and remove proc entry */
- close(ishm_proctable->entry[i].fd);
- _odp_ishmphy_unmap(ishm_proctable->entry[i].start,
- ishm_proctable->entry[i].len,
- ishm_proctable->entry[i].flags);
- ishm_proctable->entry[i] =
- ishm_proctable->entry[--last];
- } else {
- i++;
- }
- }
- ishm_proctable->nb_entries = last;
-}
-
-/*
- * Allocate and map internal shared memory, or other objects:
- * If a name is given, check that this name is not already in use.
- * If ok, allocate a new shared memory block and map the
- * provided fd in it (if fd >=0 was given).
- * If no fd is provided, a shared memory file desc named
- * /dev/shm/odp-<pid>-ishm-<name_or_sequence> is created and mapped.
- * (the name is different for huge page file as they must be on hugepagefs)
- * The function returns the index of the newly created block in the
- * main block table (>=0) or -1 on error.
- */
-int _odp_ishm_reserve(const char *name, uint64_t size, int fd,
- uint32_t align, uint32_t flags, uint32_t user_flags)
-{
- int new_index; /* index in the main block table*/
- ishm_block_t *new_block; /* entry in the main block table*/
- uint64_t page_sz; /* normal page size. usually 4K*/
- uint64_t page_hp_size; /* huge page size */
- uint32_t hp_align;
- uint64_t len; /* mapped length */
- void *addr = NULL; /* mapping address */
- int new_proc_entry;
- struct stat statbuf;
- static int huge_error_printed; /* to avoid millions of error...*/
-
- odp_spinlock_lock(&ishm_tbl->lock);
-
- /* update this process view... */
- procsync();
-
- /* Get system page sizes: page_hp_size is 0 if no huge page available*/
- page_sz = odp_sys_page_size();
- page_hp_size = odp_sys_huge_page_size();
-
- /* grab a new entry: */
- for (new_index = 0; new_index < ISHM_MAX_NB_BLOCKS; new_index++) {
- if (ishm_tbl->block[new_index].len == 0) {
- /* Found free block */
- break;
- }
- }
-
- /* check if we have reached the maximum number of allocation: */
- if (new_index >= ISHM_MAX_NB_BLOCKS) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- ODP_ERR("ISHM_MAX_NB_BLOCKS limit reached!\n");
- return -1;
- }
-
- new_block = &ishm_tbl->block[new_index];
-
- /* save block name (if any given): */
- if (name)
- strncpy(new_block->name, name, ISHM_NAME_MAXLEN - 1);
- else
- new_block->name[0] = 0;
-
- /* save user data: */
- new_block->user_flags = user_flags;
- new_block->user_len = size;
-
- /* If a file descriptor is provided, get the real size and map: */
- if (fd >= 0) {
- if (fstat(fd, &statbuf) < 0) {
- close(fd);
- odp_spinlock_unlock(&ishm_tbl->lock);
- ODP_ERR("_ishm_reserve failed (fstat failed: %s).\n",
- strerror(errno));
- __odp_errno = errno;
- return -1;
- }
- len = statbuf.st_size;
- /* note that the huge page flag is meningless here as huge
- * page is determined by the provided file descriptor: */
- addr = do_map(new_index, len, align, flags, EXTERNAL, &fd);
- if (addr == NULL) {
- close(fd);
- odp_spinlock_unlock(&ishm_tbl->lock);
- ODP_ERR("_ishm_reserve failed.\n");
- return -1;
- }
- new_block->huge = EXTERNAL;
- new_block->external_fd = 1;
- } else {
- new_block->external_fd = 0;
- }
-
- /* Otherwise, Try first huge pages when possible and needed: */
- if ((fd < 0) && page_hp_size && (size > page_sz)) {
- /* at least, alignment in VA should match page size, but user
- * can request more: If the user requirement exceeds the page
- * size then we have to make sure the block will be mapped at
- * the same address every where, otherwise alignment may be
- * be wrong for some process */
- hp_align = align;
- if (hp_align <= odp_sys_huge_page_size())
- hp_align = odp_sys_huge_page_size();
- else
- flags |= _ODP_ISHM_SINGLE_VA;
-
- /* roundup to page size */
- len = (size + (page_hp_size - 1)) & (-page_hp_size);
- addr = do_map(new_index, len, hp_align, flags, HUGE, &fd);
-
- if (addr == NULL) {
- if (!huge_error_printed) {
- ODP_ERR("No huge pages, fall back to normal "
- "pages. "
- "check: /proc/sys/vm/nr_hugepages.\n");
- huge_error_printed = 1;
- }
- } else {
- new_block->huge = HUGE;
- }
- }
-
- /* Try normal pages if huge pages failed */
- if (fd < 0) {
- /* at least, alignment in VA should match page size, but user
- * can request more: If the user requirement exceeds the page
- * size then we have to make sure the block will be mapped at
- * the same address every where, otherwise alignment may be
- * be wrong for some process */
- if (align <= odp_sys_page_size())
- align = odp_sys_page_size();
- else
- flags |= _ODP_ISHM_SINGLE_VA;
-
- /* roundup to page size */
- len = (size + (page_sz - 1)) & (-page_sz);
- addr = do_map(new_index, len, align, flags, NORMAL, &fd);
- new_block->huge = NORMAL;
- }
-
- /* if neither huge pages or normal pages works, we cannot proceed: */
- if ((fd < 0) || (addr == NULL) || (len == 0)) {
- if ((!new_block->external_fd) && (fd >= 0))
- close(fd);
- delete_file(new_block);
- odp_spinlock_unlock(&ishm_tbl->lock);
- ODP_ERR("_ishm_reserve failed.\n");
- return -1;
- }
-
- /* remember block data and increment block seq number to mark change */
- new_block->len = len;
- new_block->user_len = size;
- new_block->flags = flags;
- new_block->user_flags = user_flags;
- new_block->seq++;
- new_block->refcnt = 1;
- new_block->start = addr; /* only for SINGLE_VA*/
-
- /* the allocation succeeded: update the process local view */
- new_proc_entry = ishm_proctable->nb_entries++;
- ishm_proctable->entry[new_proc_entry].block_index = new_index;
- ishm_proctable->entry[new_proc_entry].flags = flags;
- ishm_proctable->entry[new_proc_entry].seq = new_block->seq;
- ishm_proctable->entry[new_proc_entry].start = addr;
- ishm_proctable->entry[new_proc_entry].len = len;
- ishm_proctable->entry[new_proc_entry].fd = fd;
-
- /* register the file descriptor to the file descriptor server. */
- _odp_fdserver_register_fd(FD_SRV_CTX_ISHM, new_index, fd);
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return new_index;
-}
-
-/*
- * Try to map an memory block mapped by another ODP instance into the
- * current ODP instance.
- * returns 0 on success.
- */
-int _odp_ishm_find_exported(const char *remote_name, pid_t external_odp_pid,
- const char *local_name)
-{
- char export_filename[ISHM_FILENAME_MAXLEN];
- char blockname[ISHM_FILENAME_MAXLEN];
- char filename[ISHM_FILENAME_MAXLEN];
- FILE *export_file;
- uint64_t len;
- uint32_t flags;
- uint64_t user_len;
- uint32_t user_flags;
- uint32_t align;
- int fd;
- int block_index;
-
- /* try to read the block description file: */
- snprintf(export_filename, ISHM_FILENAME_MAXLEN,
- ISHM_EXPTNAME_FORMAT,
- odp_global_data.shm_dir,
- odp_global_data.uid,
- external_odp_pid,
- remote_name);
-
- export_file = fopen(export_filename, "r");
-
- if (export_file == NULL) {
- ODP_ERR("Error opening %s.\n", export_filename);
- return -1;
- }
-
- if (fscanf(export_file, EXPORT_FILE_LINE1_FMT " ") != 0)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE2_FMT " ", blockname) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE3_FMT " ", filename) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE4_FMT " ", &len) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE5_FMT " ", &flags) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE6_FMT " ", &user_len) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE7_FMT " ", &user_flags) != 1)
- goto error_exp_file;
-
- if (fscanf(export_file, EXPORT_FILE_LINE8_FMT " ", &align) != 1)
- goto error_exp_file;
-
- fclose(export_file);
-
- /* now open the filename given in the description file: */
- fd = open(filename, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd == -1) {
- ODP_ERR("open failed for %s: %s.\n",
- filename, strerror(errno));
- return -1;
- }
-
- /* clear the _ODP_ISHM_EXPORT flag so we don't export that again*/
- flags &= ~(uint32_t)_ODP_ISHM_EXPORT;
-
- /* reserve the memory, providing the opened file descriptor: */
- block_index = _odp_ishm_reserve(local_name, 0, fd, align, flags, 0);
- if (block_index < 0) {
- close(fd);
- return block_index;
- }
-
- /* set inherited info: */
- ishm_tbl->block[block_index].user_flags = user_flags;
- ishm_tbl->block[block_index].user_len = user_len;
-
- return block_index;
-
-error_exp_file:
- fclose(export_file);
- ODP_ERR("Error reading %s.\n", export_filename);
- return -1;
-}
-
-/*
- * Free and unmap internal shared memory:
- * The file descriptor is closed and the .../odp-* file deleted,
- * unless fd was externally provided at reserve() time.
- * return 0 if OK, and -1 on error.
- * Mutex must be assured by the caller.
- */
-static int block_free(int block_index)
-{
- int proc_index;
- ishm_block_t *block; /* entry in the main block table*/
- int last;
-
- if ((block_index < 0) ||
- (block_index >= ISHM_MAX_NB_BLOCKS) ||
- (ishm_tbl->block[block_index].len == 0)) {
- ODP_ERR("Request to free an invalid block\n");
- return -1;
- }
-
- block = &ishm_tbl->block[block_index];
-
- proc_index = procfind_block(block_index);
- if (proc_index >= 0) {
- /* close the related fd */
- close(ishm_proctable->entry[proc_index].fd);
-
- /* remove the mapping and possible fragment */
- do_unmap(ishm_proctable->entry[proc_index].start,
- block->len,
- ishm_proctable->entry[proc_index].flags,
- block_index);
-
- /* remove entry from process local table: */
- last = ishm_proctable->nb_entries - 1;
- ishm_proctable->entry[proc_index] =
- ishm_proctable->entry[last];
- ishm_proctable->nb_entries = last;
- } else {
- /* just possibly free the fragment as no mapping exist here: */
- do_unmap(NULL, 0, block->flags, block_index);
- }
-
- /* remove all files related to this block: */
- delete_file(block);
-
- /* deregister the file descriptor from the file descriptor server. */
- _odp_fdserver_deregister_fd(FD_SRV_CTX_ISHM, block_index);
-
- /* mark the block as free in the main block table: */
- block->len = 0;
-
- /* mark the change so other processes see this entry as obsolete: */
- block->seq++;
-
- return 0;
-}
-
-/*
- * Free and unmap internal shared memory, identified by its block number:
- * return -1 on error. 0 if OK.
- */
-int _odp_ishm_free_by_index(int block_index)
-{
- int ret;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- ret = block_free(block_index);
- odp_spinlock_unlock(&ishm_tbl->lock);
- return ret;
-}
-
-/*
- * free and unmap internal shared memory, identified by its block name:
- * return -1 on error. 0 if OK.
- */
-int _odp_ishm_free_by_name(const char *name)
-{
- int block_index;
- int ret;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- /* search the block in main ishm table */
- block_index = find_block_by_name(name);
- if (block_index < 0) {
- ODP_ERR("Request to free an non existing block..."
- " (double free?)\n");
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
-
- ret = block_free(block_index);
- odp_spinlock_unlock(&ishm_tbl->lock);
- return ret;
-}
-
-/*
- * Free and unmap internal shared memory identified by address:
- * return -1 on error. 0 if OK.
- */
-int _odp_ishm_free_by_address(void *addr)
-{
- int block_index;
- int ret;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- /* search the block in main ishm table */
- block_index = find_block_by_address(addr);
- if (block_index < 0) {
- ODP_ERR("Request to free an non existing block..."
- " (double free?)\n");
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
-
- ret = block_free(block_index);
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return ret;
-}
-
-/*
- * Lookup for an ishm shared memory, identified by its block index
- * in the main ishm block table.
- * Map this ishm area in the process VA (if not already present).
- * Returns the block user address or NULL on error.
- * Mutex must be assured by the caller.
- */
-static void *block_lookup(int block_index)
-{
- int proc_index;
- int fd = -1;
- ishm_block_t *block;
- void *mapped_addr;
- int new_entry;
-
- if ((block_index < 0) ||
- (block_index >= ISHM_MAX_NB_BLOCKS) ||
- (ishm_tbl->block[block_index].len == 0)) {
- ODP_ERR("Request to lookup an invalid block\n");
- return NULL;
- }
-
- /* search it in process table: if there, this process knows it already*/
- proc_index = procfind_block(block_index);
- if (proc_index >= 0)
- return ishm_proctable->entry[proc_index].start;
-
- /* this ishm is not known by this process, yet: we create the mapping.*/
- fd = _odp_fdserver_lookup_fd(FD_SRV_CTX_ISHM, block_index);
- if (fd < 0) {
- ODP_ERR("Could not find ishm file descriptor (BUG!)\n");
- return NULL;
- }
-
- /* perform the mapping */
- block = &ishm_tbl->block[block_index];
-
- mapped_addr = do_remap(block_index, fd);
- if (mapped_addr == NULL) {
- ODP_ERR(" lookup: Could not map existing shared memory!\n");
- return NULL;
- }
-
- /* the mapping succeeded: update the process local view */
- new_entry = ishm_proctable->nb_entries++;
- ishm_proctable->entry[new_entry].block_index = block_index;
- ishm_proctable->entry[new_entry].flags = block->flags;
- ishm_proctable->entry[new_entry].seq = block->seq;
- ishm_proctable->entry[new_entry].start = mapped_addr;
- ishm_proctable->entry[new_entry].len = block->len;
- ishm_proctable->entry[new_entry].fd = fd;
- block->refcnt++;
-
- return mapped_addr;
-}
-
-/*
- * Lookup for an ishm shared memory, identified by its block_index.
- * Maps this ishmem area in the process VA (if not already present).
- * Returns the block user address, or NULL if the index
- * does not match any known ishm blocks.
- */
-void *_odp_ishm_lookup_by_index(int block_index)
-{
- void *ret;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- ret = block_lookup(block_index);
- odp_spinlock_unlock(&ishm_tbl->lock);
- return ret;
-}
-
-/*
- * Lookup for an ishm shared memory, identified by its block name.
- * Map this ishm area in the process VA (if not already present).
- * Return the block index, or -1 if the index
- * does not match any known ishm blocks.
- */
-int _odp_ishm_lookup_by_name(const char *name)
-{
- int block_index;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- /* search the block in main ishm table: return -1 if not found: */
- block_index = find_block_by_name(name);
- if ((block_index < 0) || (!block_lookup(block_index))) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return block_index;
-}
-
-/*
- * Lookup for an ishm shared memory block, identified by its VA address.
- * This works only if the block has already been looked-up (mapped) by the
- * current process or it it was created with the _ODP_ISHM_SINGLE_VA flag.
- * Map this ishm area in the process VA (if not already present).
- * Return the block index, or -1 if the address
- * does not match any known ishm blocks.
- */
-int _odp_ishm_lookup_by_address(void *addr)
-{
- int block_index;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- /* search the block in main ishm table: return -1 if not found: */
- block_index = find_block_by_address(addr);
- if ((block_index < 0) || (!block_lookup(block_index))) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return block_index;
-}
-
-/*
- * Returns the VA address of a given block (which has to be known in the current
- * process). Returns NULL if the block is unknown.
- */
-void *_odp_ishm_address(int block_index)
-{
- int proc_index;
- void *addr;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- if ((block_index < 0) ||
- (block_index >= ISHM_MAX_NB_BLOCKS) ||
- (ishm_tbl->block[block_index].len == 0)) {
- ODP_ERR("Request for address on an invalid block\n");
- odp_spinlock_unlock(&ishm_tbl->lock);
- return NULL;
- }
-
- proc_index = procfind_block(block_index);
- if (proc_index < 0) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return NULL;
- }
-
- addr = ishm_proctable->entry[proc_index].start;
- odp_spinlock_unlock(&ishm_tbl->lock);
- return addr;
-}
-
-int _odp_ishm_info(int block_index, _odp_ishm_info_t *info)
-{
- int proc_index;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- if ((block_index < 0) ||
- (block_index >= ISHM_MAX_NB_BLOCKS) ||
- (ishm_tbl->block[block_index].len == 0)) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- ODP_ERR("Request for info on an invalid block\n");
- return -1;
- }
-
- /* search it in process table: if not there, need to map*/
- proc_index = procfind_block(block_index);
- if (proc_index < 0) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
-
- info->name = ishm_tbl->block[block_index].name;
- info->addr = ishm_proctable->entry[proc_index].start;
- info->size = ishm_tbl->block[block_index].user_len;
- info->page_size = (ishm_tbl->block[block_index].huge == HUGE) ?
- odp_sys_huge_page_size() : odp_sys_page_size();
- info->flags = ishm_tbl->block[block_index].flags;
- info->user_flags = ishm_tbl->block[block_index].user_flags;
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return 0;
-}
-
-static int do_odp_ishm_init_local(void)
-{
- int i;
- int block_index;
-
- /*
- * the ishm_process table is local to each linux process
- * Check that no other linux threads (of same or ancestor processes)
- * have already created the table, and create it if needed.
- * We protect this with the general ishm lock to avoid
- * init race condition of different running threads.
- */
- odp_spinlock_lock(&ishm_tbl->lock);
- ishm_tbl->odpthread_cnt++; /* count ODPthread (pthread or process) */
- if (!ishm_proctable) {
- ishm_proctable = malloc(sizeof(ishm_proctable_t));
- if (!ishm_proctable) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return -1;
- }
- memset(ishm_proctable, 0, sizeof(ishm_proctable_t));
- }
- if (syscall(SYS_gettid) != getpid())
- ishm_proctable->thrd_refcnt++; /* new linux thread */
- else
- ishm_proctable->thrd_refcnt = 1;/* new linux process */
-
- /*
- * if this ODP thread is actually a new linux process, (as opposed
- * to a pthread), i.e, we just forked, then all shmem blocks
- * of the parent process are mapped into this child by inheritance.
- * (The process local table is inherited as well). We hence have to
- * increase the process refcount for each of the inherited mappings:
- */
- if (syscall(SYS_gettid) == getpid()) {
- for (i = 0; i < ishm_proctable->nb_entries; i++) {
- block_index = ishm_proctable->entry[i].block_index;
- ishm_tbl->block[block_index].refcnt++;
- }
- }
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return 0;
-}
-
-/* remove all files staring with "odp-<pid>" from a directory "dir" */
-int _odp_ishm_cleanup_files(const char *dirpath)
-{
- struct dirent *e;
- DIR *dir;
- char userdir[PATH_MAX];
- char prefix[PATH_MAX];
- char *fullpath;
- int d_len = strlen(dirpath);
- int p_len;
- int f_len;
-
- snprintf(userdir, PATH_MAX, "%s/%s", dirpath, odp_global_data.uid);
-
- dir = opendir(userdir);
- if (!dir) {
- /* ok if the dir does not exist. no much to delete then! */
- ODP_DBG("opendir failed for %s: %s\n",
- dirpath, strerror(errno));
- return 0;
- }
- snprintf(prefix, PATH_MAX, _ODP_FILES_FMT, odp_global_data.main_pid);
- p_len = strlen(prefix);
- while ((e = readdir(dir)) != NULL) {
- if (strncmp(e->d_name, prefix, p_len) == 0) {
- f_len = strlen(e->d_name);
- fullpath = malloc(d_len + f_len + 2);
- if (fullpath == NULL) {
- closedir(dir);
- return -1;
- }
- snprintf(fullpath, PATH_MAX, "%s/%s",
- dirpath, e->d_name);
- ODP_DBG("deleting obsolete file: %s\n", fullpath);
- if (unlink(fullpath))
- ODP_ERR("unlink failed for %s: %s\n",
- fullpath, strerror(errno));
- free(fullpath);
- }
- }
- closedir(dir);
-
- return 0;
-}
-
-int _odp_ishm_init_global(void)
-{
- void *addr;
- void *spce_addr;
- int i;
- uid_t uid;
-
- odp_global_data.main_pid = getpid();
- odp_global_data.shm_dir = getenv("ODP_SHM_DIR");
- if (odp_global_data.shm_dir) {
- odp_global_data.shm_dir_from_env = 1;
- } else {
- odp_global_data.shm_dir =
- calloc(1, sizeof(ISHM_FILENAME_NORMAL_PAGE_DIR));
- sprintf(odp_global_data.shm_dir, "%s",
- ISHM_FILENAME_NORMAL_PAGE_DIR);
- odp_global_data.shm_dir_from_env = 0;
- }
-
- ODP_DBG("ishm: using dir %s\n", odp_global_data.shm_dir);
-
- uid = getuid();
- snprintf(odp_global_data.uid, UID_MAXLEN, "%d",
- uid);
-
- if ((syscall(SYS_gettid)) != odp_global_data.main_pid) {
- ODP_ERR("ishm init must be performed by the main "
- "ODP process!\n.");
- return -1;
- }
-
- if (!odp_global_data.hugepage_info.default_huge_page_dir)
- ODP_DBG("NOTE: No support for huge pages\n");
- else {
- ODP_DBG("Huge pages mount point is: %s\n",
- odp_global_data.hugepage_info.default_huge_page_dir);
- _odp_ishm_cleanup_files(
- odp_global_data.hugepage_info.default_huge_page_dir);
- }
-
- _odp_ishm_cleanup_files(odp_global_data.shm_dir);
-
- /* allocate space for the internal shared mem block table: */
- addr = mmap(NULL, sizeof(ishm_table_t),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- if (addr == MAP_FAILED) {
- ODP_ERR("unable to mmap the main block table\n.");
- goto init_glob_err1;
- }
- ishm_tbl = addr;
- memset(ishm_tbl, 0, sizeof(ishm_table_t));
- ishm_tbl->dev_seq = 0;
- ishm_tbl->odpthread_cnt = 0;
- odp_spinlock_init(&ishm_tbl->lock);
-
- /* allocate space for the internal shared mem fragment table: */
- addr = mmap(NULL, sizeof(ishm_ftable_t),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- if (addr == MAP_FAILED) {
- ODP_ERR("unable to mmap the main fragment table\n.");
- goto init_glob_err2;
- }
- ishm_ftbl = addr;
- memset(ishm_ftbl, 0, sizeof(ishm_ftable_t));
-
- /*
- *reserve the address space for _ODP_ISHM_SINGLE_VA reserved blocks,
- * only address space!
- */
- spce_addr = _odp_ishmphy_book_va(ODP_CONFIG_ISHM_VA_PREALLOC_SZ,
- odp_sys_huge_page_size());
- if (!spce_addr) {
- ODP_ERR("unable to reserve virtual space\n.");
- goto init_glob_err3;
- }
-
- /* use the first fragment descriptor to describe to whole VA space: */
- ishm_ftbl->fragment[0].block_index = -1;
- ishm_ftbl->fragment[0].start = spce_addr;
- ishm_ftbl->fragment[0].len = ODP_CONFIG_ISHM_VA_PREALLOC_SZ;
- ishm_ftbl->fragment[0].prev = NULL;
- ishm_ftbl->fragment[0].next = NULL;
- ishm_ftbl->used_fragmnts = &ishm_ftbl->fragment[0];
-
- /* and put all other fragment descriptors in the unused list: */
- for (i = 1; i < ISHM_NB_FRAGMNTS - 1; i++) {
- ishm_ftbl->fragment[i].prev = NULL;
- ishm_ftbl->fragment[i].next = &ishm_ftbl->fragment[i + 1];
- }
- ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].prev = NULL;
- ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].next = NULL;
- ishm_ftbl->unused_fragmnts = &ishm_ftbl->fragment[1];
-
- /*
- * We run _odp_ishm_init_local() directely here to give the
- * possibility to run shm_reserve() before the odp_init_local()
- * is performed for the main thread... Many init_global() functions
- * indeed assume the availability of odp_shm_reserve()...:
- */
- return do_odp_ishm_init_local();
-
-init_glob_err3:
- if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0)
- ODP_ERR("unable to munmap main fragment table\n.");
-init_glob_err2:
- if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0)
- ODP_ERR("unable to munmap main block table\n.");
-init_glob_err1:
- return -1;
-}
-
-int _odp_ishm_init_local(void)
-{
- /*
- * Do not re-run this for the main ODP process, as it has already
- * been done in advance at _odp_ishm_init_global() time:
- */
- if ((getpid() == odp_global_data.main_pid) &&
- (syscall(SYS_gettid) == getpid()))
- return 0;
-
- return do_odp_ishm_init_local();
-}
-
-static int do_odp_ishm_term_local(void)
-{
- int i;
- int proc_table_refcnt = 0;
- int block_index;
- ishm_block_t *block;
-
- procsync();
-
- ishm_tbl->odpthread_cnt--; /* decount ODPthread (pthread or process) */
-
- /*
- * The ishm_process table is local to each linux process
- * Check that no other linux threads (of this linux process)
- * still needs the table, and free it if so.
- * We protect this with the general ishm lock to avoid
- * term race condition of different running threads.
- */
- proc_table_refcnt = --ishm_proctable->thrd_refcnt;
- if (!proc_table_refcnt) {
- /*
- * this is the last thread of this process...
- * All mappings for this process are about to be lost...
- * Go through the table of visible blocks for this process,
- * decreasing the refcnt of each visible blocks, and issuing
- * warning for those no longer referenced by any process.
- * Note that non-referenced blocks are not freed: this is
- * deliberate as this would imply that the semantic of the
- * freeing function would differ depending on whether we run
- * with odp_thread as processes or pthreads. With this approach,
- * the user should always free the blocks manually, which is
- * more consistent
- */
- for (i = 0; i < ishm_proctable->nb_entries; i++) {
- block_index = ishm_proctable->entry[i].block_index;
- block = &ishm_tbl->block[block_index];
- if ((--block->refcnt) <= 0) {
- block->refcnt = 0;
- ODP_DBG("Warning: block %d: name:%s "
- "no longer referenced\n",
- i,
- ishm_tbl->block[i].name[0] ?
- ishm_tbl->block[i].name : "<no name>");
- }
- }
-
- free(ishm_proctable);
- ishm_proctable = NULL;
- }
-
- return 0;
-}
-
-int _odp_ishm_term_local(void)
-{
- int ret;
-
- odp_spinlock_lock(&ishm_tbl->lock);
-
- /* postpone last thread term to allow free() by global term functions:*/
- if (ishm_tbl->odpthread_cnt == 1) {
- odp_spinlock_unlock(&ishm_tbl->lock);
- return 0;
- }
-
- ret = do_odp_ishm_term_local();
- odp_spinlock_unlock(&ishm_tbl->lock);
- return ret;
-}
-
-int _odp_ishm_term_global(void)
-{
- int ret = 0;
- int index;
- ishm_block_t *block;
-
- if ((getpid() != odp_global_data.main_pid) ||
- (syscall(SYS_gettid) != getpid()))
- ODP_ERR("odp_term_global() must be performed by the main "
- "ODP process!\n.");
-
- /* cleanup possibly non freed memory (and complain a bit): */
- for (index = 0; index < ISHM_MAX_NB_BLOCKS; index++) {
- block = &ishm_tbl->block[index];
- if (block->len != 0) {
- ODP_ERR("block '%s' (file %s) was never freed "
- "(cleaning up...).\n",
- block->name, block->filename);
- delete_file(block);
- }
- }
-
- /* perform the last thread terminate which was postponed: */
- ret = do_odp_ishm_term_local();
-
- /* free the fragment table */
- if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) {
- ret |= -1;
- ODP_ERR("unable to munmap fragment table\n.");
- }
- /* free the block table */
- if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) {
- ret |= -1;
- ODP_ERR("unable to munmap main table\n.");
- }
-
- /* free the reserved VA space */
- if (_odp_ishmphy_unbook_va())
- ret |= -1;
-
- if (!odp_global_data.shm_dir_from_env)
- free(odp_global_data.shm_dir);
-
- return ret;
-}
-
-/*
- * Print the current ishm status (allocated blocks and VA space map)
- * Return the number of allocated blocks (including those not mapped
- * by the current odp thread). Also perform a number of sanity check.
- * For debug.
- */
-int _odp_ishm_status(const char *title)
-{
- int i;
- char flags[3];
- char huge;
- int proc_index;
- ishm_fragment_t *fragmnt;
- int consecutive_unallocated = 0; /* should never exceed 1 */
- uintptr_t last_address = 0;
- ishm_fragment_t *previous = NULL;
- int nb_used_frgments = 0;
- int nb_unused_frgments = 0; /* nb frag describing a VA area */
- int nb_allocated_frgments = 0; /* nb frag describing an allocated VA */
- int nb_blocks = 0;
- int single_va_blocks = 0;
-
- odp_spinlock_lock(&ishm_tbl->lock);
- procsync();
-
- ODP_DBG("ishm blocks allocated at: %s\n", title);
-
- /* display block table: 1 line per entry +1 extra line if mapped here */
- for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
- if (ishm_tbl->block[i].len <= 0)
- continue; /* unused block */
-
- nb_blocks++;
- if (ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA)
- single_va_blocks++;
-
- flags[0] = (ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA) ?
- 'S' : '.';
- flags[1] = (ishm_tbl->block[i].flags & _ODP_ISHM_LOCK) ?
- 'L' : '.';
- flags[2] = 0;
- switch (ishm_tbl->block[i].huge) {
- case HUGE:
- huge = 'H';
- break;
- case NORMAL:
- huge = 'N';
- break;
- case EXTERNAL:
- huge = 'E';
- break;
- default:
- huge = '?';
- }
- proc_index = procfind_block(i);
- ODP_DBG("%-3d: name:%-.24s file:%-.24s"
- " flags:%s,%c len:0x%-08lx"
- " user_len:%-8ld seq:%-3ld refcnt:%-4d\n",
- i,
- ishm_tbl->block[i].name,
- ishm_tbl->block[i].filename,
- flags, huge,
- ishm_tbl->block[i].len,
- ishm_tbl->block[i].user_len,
- ishm_tbl->block[i].seq,
- ishm_tbl->block[i].refcnt);
-
- if (proc_index < 0)
- continue;
-
- ODP_DBG(" start:%-08lx fd:%-3d\n",
- ishm_proctable->entry[proc_index].start,
- ishm_proctable->entry[proc_index].fd);
- }
-
- /* display the virtual space allocations... : */
- ODP_DBG("ishm virtual space:\n");
- for (fragmnt = ishm_ftbl->used_fragmnts;
- fragmnt; fragmnt = fragmnt->next) {
- if (fragmnt->block_index >= 0) {
- nb_allocated_frgments++;
- ODP_DBG(" %08p - %08p: ALLOCATED by block:%d\n",
- (uintptr_t)fragmnt->start,
- (uintptr_t)fragmnt->start + fragmnt->len - 1,
- fragmnt->block_index);
- consecutive_unallocated = 0;
- } else {
- ODP_DBG(" %08p - %08p: NOT ALLOCATED\n",
- (uintptr_t)fragmnt->start,
- (uintptr_t)fragmnt->start + fragmnt->len - 1);
- if (consecutive_unallocated++)
- ODP_ERR("defragmentation error\n");
- }
-
- /* some other sanity checks: */
- if (fragmnt->prev != previous)
- ODP_ERR("chaining error\n");
-
- if (fragmnt != ishm_ftbl->used_fragmnts) {
- if ((uintptr_t)fragmnt->start != last_address + 1)
- ODP_ERR("lost space error\n");
- }
-
- last_address = (uintptr_t)fragmnt->start + fragmnt->len - 1;
- previous = fragmnt;
- nb_used_frgments++;
- }
-
- /*
- * the number of blocks with the single_VA flag set should match
- * the number of used fragments:
- */
- if (single_va_blocks != nb_allocated_frgments)
- ODP_ERR("single_va_blocks != nb_allocated_fragments!\n");
-
- /* compute the number of unused fragments*/
- for (fragmnt = ishm_ftbl->unused_fragmnts;
- fragmnt; fragmnt = fragmnt->next)
- nb_unused_frgments++;
-
- ODP_DBG("ishm: %d fragment used. %d fragments unused. (total=%d)\n",
- nb_used_frgments, nb_unused_frgments,
- nb_used_frgments + nb_unused_frgments);
-
- if ((nb_used_frgments + nb_unused_frgments) != ISHM_NB_FRAGMNTS)
- ODP_ERR("lost fragments!\n");
-
- if (nb_blocks < ishm_proctable->nb_entries)
- ODP_ERR("process known block cannot exceed main total sum!\n");
-
- ODP_DBG("\n");
-
- odp_spinlock_unlock(&ishm_tbl->lock);
- return nb_blocks;
-}
diff --git a/platform/linux-generic/_ishmphy.c b/platform/linux-generic/_ishmphy.c
deleted file mode 100644
index d519af60c..000000000
--- a/platform/linux-generic/_ishmphy.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * This file handles the lower end of the ishm memory allocator:
- * It performs the physical mappings.
- */
-#include <odp_posix_extensions.h>
-#include <odp_config_internal.h>
-#include <odp_internal.h>
-#include <odp/api/align.h>
-#include <odp/api/system_info.h>
-#include <odp/api/debug.h>
-#include <odp_debug_internal.h>
-#include <odp_align_internal.h>
-#include <_ishm_internal.h>
-#include <_ishmphy_internal.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <_ishmphy_internal.h>
-
-static void *common_va_address;
-static uint64_t common_va_len;
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-/* Book some virtual address space
- * This function is called at odp_init_global() time to pre-book some
- * virtual address space inherited by all odpthreads (i.e. descendant
- * processes and threads) and later used to guarantee the unicity the
- * the mapping VA address when memory is reserver with the _ODP_ISHM_SINGLE_VA
- * flag.
- * returns the address of the mapping or NULL on error.
- */
-void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align)
-{
- void *addr;
-
- addr = mmap(NULL, len + align, PROT_NONE,
- MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
- if (addr == MAP_FAILED) {
- ODP_ERR("_ishmphy_book_va failure\n");
- return NULL;
- }
-
- if (mprotect(addr, len, PROT_NONE))
- ODP_ERR("failure for protect\n");
-
- ODP_DBG("VA Reserved: %p, len=%p\n", addr, len + align);
-
- common_va_address = addr;
- common_va_len = len;
-
- /* return the nearest aligned address: */
- return (void *)(((uintptr_t)addr + align - 1) & (-align));
-}
-
-/* Un-book some virtual address space
- * This function is called at odp_term_global() time to unbook
- * the virtual address space booked by _ishmphy_book_va()
- */
-int _odp_ishmphy_unbook_va(void)
-{
- int ret;
-
- ret = munmap(common_va_address, common_va_len);
- if (ret)
- ODP_ERR("_unishmphy_book_va failure\n");
- return ret;
-}
-
-/*
- * do a mapping:
- * Performs a mapping of the provided file descriptor to the process VA
- * space. If the _ODP_ISHM_SINGLE_VA flag is set, 'start' is assumed to be
- * the VA address where the mapping is to be done.
- * If the flag is not set, a new VA address is taken.
- * returns the address of the mapping or NULL on error.
- */
-void *_odp_ishmphy_map(int fd, void *start, uint64_t size,
- int flags)
-{
- void *mapped_addr_tmp, *mapped_addr;
- int mmap_flags = 0;
-
- if (flags & _ODP_ISHM_SINGLE_VA) {
- if (!start) {
- ODP_ERR("failure: missing address\n");
- return NULL;
- }
- /* maps over fragment of reserved VA: */
- /* first, try a normal map. If that works, remap it where it
- * should (on the prereverved space), and remove the initial
- * normal mapping:
- * This is because it turned out that if a mapping fails
- * on a the prereserved virtual address space, then
- * the prereserved address space which was tried to be mapped
- * on becomes available to the kernel again! This was not
- * according to expectations: the assumption was that if a
- * mapping fails, the system should remain unchanged, but this
- * is obvioulsy not true (at least for huge pages when
- * exhausted).
- * So the strategy is to first map at a non reserved place
- * (which can then be freed and returned to the kernel on
- * failure) and peform a new map to the prereserved space on
- * success (which is then guaranteed to work).
- * The initial free maping can then be removed.
- */
- mapped_addr = MAP_FAILED;
- mapped_addr_tmp = mmap(NULL, size, PROT_READ | PROT_WRITE,
- MAP_SHARED | mmap_flags, fd, 0);
- if (mapped_addr_tmp != MAP_FAILED) {
- /* If OK, do new map at right fixed location... */
- mapped_addr = mmap(start,
- size, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_FIXED | mmap_flags,
- fd, 0);
- if (mapped_addr != start)
- ODP_ERR("new map failed:%s\n", strerror(errno));
- /* ... and remove initial mapping: */
- if (munmap(mapped_addr_tmp, size))
- ODP_ERR("munmap failed:%s\n", strerror(errno));
- }
- } else {
- /* just do a new mapping in the VA space: */
- mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
- MAP_SHARED | mmap_flags, fd, 0);
- if ((mapped_addr >= common_va_address) &&
- ((char *)mapped_addr <
- (char *)common_va_address + common_va_len)) {
- ODP_ERR("VA SPACE OVERLAP!\n");
- }
- }
-
- if (mapped_addr == MAP_FAILED) {
- ODP_ERR("mmap failed:%s\n", strerror(errno));
- return NULL;
- }
-
- /* if locking is requested, lock it...*/
- if (flags & _ODP_ISHM_LOCK) {
- if (mlock(mapped_addr, size)) {
- if (munmap(mapped_addr, size))
- ODP_ERR("munmap failed:%s\n", strerror(errno));
- ODP_ERR("mlock failed:%s\n", strerror(errno));
- return NULL;
- }
- }
- return mapped_addr;
-}
-
-/* free a mapping:
- * If the _ODP_ISHM_SINGLE_VA flag was given at creation time the virtual
- * address range must be returned to the preoallocated "pool". this is
- * done by mapping non accessibly memory there (hence blocking the VA but
- * releasing the physical memory).
- * If the _ODP_ISHM_SINGLE_VA flag was not given, both physical memory and
- * virtual address space are realeased by calling the normal munmap.
- * return 0 on success or -1 on error.
- */
-int _odp_ishmphy_unmap(void *start, uint64_t len, int flags)
-{
- void *addr;
- int ret;
- int mmap_flgs;
-
- mmap_flgs = MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | MAP_NORESERVE;
-
- /* if locking was requested, unlock...*/
- if (flags & _ODP_ISHM_LOCK)
- munlock(start, len);
-
- if (flags & _ODP_ISHM_SINGLE_VA) {
- /* map unnaccessible memory overwrites previous mapping
- * and free the physical memory, but guarantees to block
- * the VA range from other mappings
- */
- addr = mmap(start, len, PROT_NONE, mmap_flgs, -1, 0);
- if (addr == MAP_FAILED) {
- ODP_ERR("_ishmphy_free failure for ISHM_SINGLE_VA\n");
- return -1;
- }
- if (mprotect(start, len, PROT_NONE))
- ODP_ERR("_ishmphy_free failure for protect\n");
- return 0;
- }
-
- /* just release the mapping */
- ret = munmap(start, len);
- if (ret)
- ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno));
- return ret;
-}
diff --git a/platform/linux-generic/arch/aarch64/cpu_flags.c b/platform/linux-generic/arch/aarch64/cpu_flags.c
new file mode 100644
index 000000000..35ef7c1f0
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/cpu_flags.c
@@ -0,0 +1,1050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp/api/hints.h>
+
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+
+#include "cpu_flags.h"
+
+#include <asm/hwcap.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+
+typedef struct {
+ const char *name;
+ const uint64_t bit_mask;
+} hwcap_feat_flag_t;
+
+/* Linux HWCAP and HWCAP2 flags
+ *
+ * See https://docs.kernel.org/arch/arm64/elf_hwcaps.html for meaning of each flag.
+ */
+static hwcap_feat_flag_t hwcap_flags[] = {
+ {
+ /* Floating-point support for single-precision and double-precision types */
+ .name = "FEAT_FP",
+#ifdef HWCAP_FP
+ .bit_mask = HWCAP_FP,
+#endif
+ },
+
+ {
+ /* Advanced SIMD support for:
+ * - integer byte, halfword, word and doubleword element operations
+ * - single-precision and double-precision floating-point arithmetic */
+ .name = "ASIMD",
+#ifdef HWCAP_ASIMD
+ .bit_mask = HWCAP_ASIMD,
+#endif
+ },
+
+ {
+ /* Generic Timer is configured to generate events at approx. 10KHz */
+ .name = "EVTSTRM",
+#ifdef HWCAP_EVTSTRM
+ .bit_mask = HWCAP_EVTSTRM,
+#endif
+ },
+
+ {
+ /* Advanced SIMD AES Instructions */
+ .name = "FEAT_AES",
+#ifdef HWCAP_AES
+ .bit_mask = HWCAP_AES,
+#endif
+ },
+
+ {
+ /* Advanced SIMD PMULL Instructions */
+ .name = "FEAT_PMULL",
+#ifdef HWCAP_PMULL
+ .bit_mask = HWCAP_PMULL,
+#endif
+ },
+
+ {
+ /* Advanced SIMD SHA1 Instructions */
+ .name = "FEAT_SHA1",
+#ifdef HWCAP_SHA1
+ .bit_mask = HWCAP_SHA1,
+#endif
+ },
+
+ {
+ /* Advanced SIMD SHA256 Instructions */
+ .name = "FEAT_SHA256",
+#ifdef HWCAP_SHA2
+ .bit_mask = HWCAP_SHA2,
+#endif
+ },
+
+ {
+ /* CRC32 Instructions */
+ .name = "FEAT_CRC32",
+#ifdef HWCAP_CRC32
+ .bit_mask = HWCAP_CRC32,
+#endif
+ },
+
+ {
+ /* Large System Extensions */
+ .name = "FEAT_LSE",
+#ifdef HWCAP_ATOMICS
+ .bit_mask = HWCAP_ATOMICS,
+#endif
+ },
+
+ {
+ /* Half-precision Floating-point Data Processing Instructions */
+ .name = "FEAT_FP16",
+#ifdef HWCAP_FPHP
+ .bit_mask = HWCAP_FPHP,
+#endif
+ },
+
+ {
+ /* Advanced SIMD support with half-precision floating-point arithmetic */
+ .name = "ASIMDHP",
+#ifdef HWCAP_ASIMDHP
+ .bit_mask = HWCAP_ASIMDHP,
+#endif
+ },
+
+ {
+ /* Availability of EL0 Access to certain ID Registers */
+ .name = "CPUID",
+#ifdef HWCAP_CPUID
+ .bit_mask = HWCAP_CPUID,
+#endif
+ },
+
+ {
+ /* Rounding Double Multiply Accumulate Extensions */
+ .name = "FEAT_RDM",
+#ifdef HWCAP_ASIMDRDM
+ .bit_mask = HWCAP_ASIMDRDM,
+#endif
+ },
+
+ {
+ /* JavaScript FJCVTS Conversion Instructions */
+ .name = "FEAT_JSCVT",
+#ifdef HWCAP_JSCVT
+ .bit_mask = HWCAP_JSCVT,
+#endif
+ },
+
+ {
+ /* Floating-point FCMLA and FCADD Instructions */
+ .name = "FEAT_FCMA",
+#ifdef HWCAP_FCMA
+ .bit_mask = HWCAP_FCMA,
+#endif
+ },
+
+ {
+ /* Load-acquire RCpc Instructions */
+ .name = "FEAT_LRCPC",
+#ifdef HWCAP_LRCPC
+ .bit_mask = HWCAP_LRCPC,
+#endif
+ },
+
+ {
+ /* DC CVAP Instructions */
+ .name = "FEAT_DPB",
+#ifdef HWCAP_DCPOP
+ .bit_mask = HWCAP_DCPOP,
+#endif
+ },
+
+ {
+ /* Advanced SIMD EOR3, RAX1, XAR, and BCAX Instructions */
+ .name = "FEAT_SHA3",
+#ifdef HWCAP_SHA3
+ .bit_mask = HWCAP_SHA3,
+#endif
+ },
+
+ {
+ /* Advanced SIMD SM3 Instructions */
+ .name = "FEAT_SM3",
+#ifdef HWCAP_SM3
+ .bit_mask = HWCAP_SM3,
+#endif
+ },
+
+ {
+ /* Advanced SIMD SM4 Instructions */
+ .name = "FEAT_SM4",
+#ifdef HWCAP_SM4
+ .bit_mask = HWCAP_SM4,
+#endif
+ },
+
+ {
+ /* Advanced SIMD Int8 Dot Product Instructions */
+ .name = "FEAT_DotProd",
+#ifdef HWCAP_ASIMDDP
+ .bit_mask = HWCAP_ASIMDDP,
+#endif
+ },
+
+ {
+ /* Advanced SIMD SHA512 Instructions */
+ .name = "FEAT_SHA512",
+#ifdef HWCAP_SHA512
+ .bit_mask = HWCAP_SHA512,
+#endif
+ },
+
+ {
+ /* Scalable Vector Extensions */
+ .name = "FEAT_SVE",
+#ifdef HWCAP_SVE
+ .bit_mask = HWCAP_SVE,
+#endif
+ },
+
+ {
+ /* Half-precision Floating-point FMLAL Instructions */
+ .name = "FEAT_FHM",
+#ifdef HWCAP_ASIMDFHM
+ .bit_mask = HWCAP_ASIMDFHM,
+#endif
+ },
+
+ {
+ /* Data Independent Timing Instructions */
+ .name = "FEAT_DIT",
+#ifdef HWCAP_DIT
+ .bit_mask = HWCAP_DIT,
+#endif
+ },
+
+ {
+ /* Large System Extensions Version 2 */
+ .name = "FEAT_LSE2",
+#ifdef HWCAP_USCAT
+ .bit_mask = HWCAP_USCAT,
+#endif
+ },
+
+ {
+ /* Load-acquire RCpc Instructions Version 2 */
+ .name = "FEAT_LRCPC2",
+#ifdef HWCAP_ILRCPC
+ .bit_mask = HWCAP_ILRCPC,
+#endif
+ },
+
+ {
+ /* Condition Flag Manipulation Extensions */
+ .name = "FEAT_FlagM",
+#ifdef HWCAP_FLAGM
+ .bit_mask = HWCAP_FLAGM,
+#endif
+ },
+
+ {
+ /* Speculative Store Bypass Safe Instructions */
+ .name = "FEAT_SSBS2",
+#ifdef HWCAP_SSBS
+ .bit_mask = HWCAP_SSBS,
+#endif
+ },
+
+ {
+ /* Speculation Barrier Instructions */
+ .name = "FEAT_SB",
+#ifdef HWCAP_SB
+ .bit_mask = HWCAP_SB,
+#endif
+ },
+
+ {
+ /* Pointer Authentication Extensions */
+ .name = "FEAT_PAuth",
+#ifdef HWCAP_PACA
+ .bit_mask = HWCAP_PACA,
+#endif
+ },
+
+ {
+ /* Generic Authentication Extensions */
+ .name = "PACG",
+#ifdef HWCAP_PACG
+ .bit_mask = HWCAP_PACG,
+#endif
+ }
+};
+
+static hwcap_feat_flag_t hwcap2_flags[] = {
+ {
+ /* DC CVADP instructions */
+ .name = "FEAT_DPB2",
+#ifdef HWCAP2_DCPODP
+ .bit_mask = HWCAP2_DCPODP,
+#endif
+ },
+
+ {
+ /* Scalable Vector Extensions Version 2 */
+ .name = "FEAT_SVE2",
+#ifdef HWCAP2_SVE2
+ .bit_mask = HWCAP2_SVE2,
+#endif
+ },
+
+ {
+ /* SVE AES Instructions */
+ .name = "FEAT_SVE_AES",
+#ifdef HWCAP2_SVEAES
+ .bit_mask = HWCAP2_SVEAES,
+#endif
+ },
+
+ {
+ /* SVE PMULL Instructions */
+ .name = "FEAT_SVE_PMULL128",
+#ifdef HWCAP2_SVEPMULL
+ .bit_mask = HWCAP2_SVEPMULL,
+#endif
+ },
+
+ {
+ /* SVE Bit Permute Instructions */
+ .name = "FEAT_SVE_BitPerm",
+#ifdef HWCAP2_SVEBITPERM
+ .bit_mask = HWCAP2_SVEBITPERM,
+#endif
+ },
+
+ {
+ /* SVE SHA-3 Instructions */
+ .name = "FEAT_SVE_SHA3",
+#ifdef HWCAP2_SVESHA3
+ .bit_mask = HWCAP2_SVESHA3,
+#endif
+ },
+
+ {
+ /* SVE SM4 Instructions */
+ .name = "FEAT_SVE_SM4",
+#ifdef HWCAP2_SVESM4
+ .bit_mask = HWCAP2_SVESM4,
+#endif
+ },
+
+ {
+ /* Condition Flag Manipulation Extensions Version 2 */
+ .name = "FEAT_FlagM2",
+#ifdef HWCAP2_FLAGM2
+ .bit_mask = HWCAP2_FLAGM2,
+#endif
+ },
+
+ {
+ /* FRINT32Z, FRINT32X, FRINT64Z, and FRINT64X instructions */
+ .name = "FEAT_FRINTTS",
+#ifdef HWCAP2_FRINT
+ .bit_mask = HWCAP2_FRINT,
+#endif
+ },
+
+ {
+ /* SVE Int8 Matrix Multiplication Instructions */
+ .name = "SVEI8MM",
+#ifdef HWCAP2_SVEI8MM
+ .bit_mask = HWCAP2_SVEI8MM,
+#endif
+ },
+
+ {
+ /* SVE Single-precision Floating-point Matrix Multiply Instructions */
+ .name = "FEAT_F32MM",
+#ifdef HWCAP2_SVEF32MM
+ .bit_mask = HWCAP2_SVEF32MM,
+#endif
+ },
+
+ {
+ /* SVE Double-precision Floating-point Matrix Multiply Instructions */
+ .name = "FEAT_F64MM",
+#ifdef HWCAP2_SVEF64MM
+ .bit_mask = HWCAP2_SVEF64MM,
+#endif
+ },
+
+ {
+ /* SVE BFloat16 Instructions */
+ .name = "SVEBF16",
+#ifdef HWCAP2_SVEBF16
+ .bit_mask = HWCAP2_SVEBF16,
+#endif
+ },
+
+ {
+ /* Advanced SIMD and Floating-point Int8 Matrix Multiplication Instructions */
+ .name = "FEAT_I8MM",
+#ifdef HWCAP2_I8MM
+ .bit_mask = HWCAP2_I8MM,
+#endif
+ },
+
+ {
+ /* Advanced SIMD and Floating-point BFloat16 Instructions */
+ .name = "FEAT_BF16",
+#ifdef HWCAP2_BF16
+ .bit_mask = HWCAP2_BF16,
+#endif
+ },
+
+ {
+ /* Data Gathering Hint Extensions */
+ .name = "FEAT_DGH",
+#ifdef HWCAP2_DGH
+ .bit_mask = HWCAP2_DGH,
+#endif
+ },
+
+ {
+ /* Random Number Generation Extensions */
+ .name = "FEAT_RNG",
+#ifdef HWCAP2_RNG
+ .bit_mask = HWCAP2_RNG,
+#endif
+ },
+
+ {
+ /* Branch Target Identification Extensions */
+ .name = "FEAT_BTI",
+#ifdef HWCAP2_BTI
+ .bit_mask = HWCAP2_BTI,
+#endif
+ },
+
+ {
+ /* Full Memory Tagging Extensions */
+ .name = "FEAT_MTE2",
+#ifdef HWCAP2_MTE
+ .bit_mask = HWCAP2_MTE,
+#endif
+ },
+
+ {
+ .name = "ECV",
+#ifdef HWCAP2_ECV
+ .bit_mask = HWCAP2_ECV,
+#endif
+ },
+
+ {
+ .name = "AFP",
+#ifdef HWCAP2_AFP
+ .bit_mask = HWCAP2_AFP,
+#endif
+ },
+
+ {
+ .name = "RPRES",
+#ifdef HWCAP2_RPRES
+ .bit_mask = HWCAP2_RPRES,
+#endif
+ },
+
+ {
+ .name = "MTE3",
+#ifdef HWCAP2_MTE3
+ .bit_mask = HWCAP2_MTE3,
+#endif
+ },
+
+ {
+ .name = "SME",
+#ifdef HWCAP2_SME
+ .bit_mask = HWCAP2_SME,
+#endif
+ },
+
+ {
+ .name = "SME_I16I64",
+#ifdef HWCAP2_SME_I16I64
+ .bit_mask = HWCAP2_SME_I16I64,
+#endif
+ },
+
+ {
+ .name = "SME_F64F64",
+#ifdef HWCAP2_SME_F64F64
+ .bit_mask = HWCAP2_SME_F64F64,
+#endif
+ },
+
+ {
+ .name = "SME_I8I32",
+#ifdef HWCAP2_SME_I8I32
+ .bit_mask = HWCAP2_SME_I8I32,
+#endif
+ },
+
+ {
+ .name = "SME_F16F32",
+#ifdef HWCAP2_SME_F16F32
+ .bit_mask = HWCAP2_SME_F16F32,
+#endif
+ },
+
+ {
+ .name = "SME_B16F32",
+#ifdef HWCAP2_SME_B16F32
+ .bit_mask = HWCAP2_SME_B16F32,
+#endif
+ },
+
+ {
+ .name = "SME_F32F32",
+#ifdef HWCAP2_SME_F32F32
+ .bit_mask = HWCAP2_SME_F32F32,
+#endif
+ },
+
+ {
+ .name = "SME_FA64",
+#ifdef HWCAP2_SME_FA64
+ .bit_mask = HWCAP2_SME_FA64,
+#endif
+ },
+
+ {
+ .name = "WFXT",
+#ifdef HWCAP2_WFXT
+ .bit_mask = HWCAP2_WFXT,
+#endif
+ },
+
+ {
+ .name = "EBF16",
+#ifdef HWCAP2_EBF16
+ .bit_mask = HWCAP2_EBF16,
+#endif
+ },
+
+ {
+ .name = "SVE_EBF16",
+#ifdef HWCAP2_SVE_EBF16
+ .bit_mask = HWCAP2_SVE_EBF16,
+#endif
+ },
+
+ {
+ .name = "CSSC",
+#ifdef HWCAP2_CSSC
+ .bit_mask = HWCAP2_CSSC,
+#endif
+ },
+
+ {
+ .name = "RPRFM",
+#ifdef HWCAP2_RPRFM
+ .bit_mask = HWCAP2_RPRFM,
+#endif
+ },
+
+ {
+ .name = "SVE2P1",
+#ifdef HWCAP2_SVE2P1
+ .bit_mask = HWCAP2_SVE2P1,
+#endif
+ },
+
+ {
+ .name = "SME2",
+#ifdef HWCAP2_SME2
+ .bit_mask = HWCAP2_SME2,
+#endif
+ },
+
+ {
+ .name = "SME2P1",
+#ifdef HWCAP2_SME2P1
+ .bit_mask = HWCAP2_SME2P1,
+#endif
+ },
+
+ {
+ .name = "SME_I16I32",
+#ifdef HWCAP2_SME_I16I32
+ .bit_mask = HWCAP2_SME_I16I32,
+#endif
+ },
+
+ {
+ .name = "SME_BI32I32",
+#ifdef HWCAP2_SME_BI32I32
+ .bit_mask = HWCAP2_SME_BI32I32,
+#endif
+ },
+
+ {
+ .name = "SME_B16B16",
+#ifdef HWCAP2_SME_B16B16
+ .bit_mask = HWCAP2_SME_B16B16,
+#endif
+ },
+
+ {
+ .name = "SME_F16F16",
+#ifdef HWCAP2_SME_F16F16
+ .bit_mask = HWCAP2_SME_F16F16,
+#endif
+ },
+
+ {
+ .name = "MOPS",
+#ifdef HWCAP2_MOPS
+ .bit_mask = HWCAP2_MOPS,
+#endif
+ },
+};
+
+static void _odp_sys_info_print_acle_flags(void)
+{
+ const char *ndef = "n/a";
+
+ /* Avoid compiler warning about unused variable */
+ (void)ndef;
+
+ /* See ARM C Language Extensions documentation for details */
+ _ODP_PRINT("ARM FEATURES:\n");
+
+ _ODP_PRINT(" __ARM_ALIGN_MAX_PWR ");
+#ifdef __ARM_ALIGN_MAX_PWR
+ _ODP_PRINT("%i\n", __ARM_ALIGN_MAX_PWR);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_ALIGN_MAX_STACK_PWR ");
+#ifdef __ARM_ALIGN_MAX_STACK_PWR
+ _ODP_PRINT("%i\n", __ARM_ALIGN_MAX_STACK_PWR);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_ARCH ");
+#ifdef __ARM_ARCH
+ _ODP_PRINT("%i\n", __ARM_ARCH);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_ARCH_ISA_A64 ");
+#ifdef __ARM_ARCH_ISA_A64
+ _ODP_PRINT("%i\n", __ARM_ARCH_ISA_A64);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_BIG_ENDIAN ");
+#ifdef __ARM_BIG_ENDIAN
+ _ODP_PRINT("%i\n", __ARM_BIG_ENDIAN);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_BF16_FORMAT_ALTERNATIVE ");
+#ifdef __ARM_BF16_FORMAT_ALTERNATIVE
+ _ODP_PRINT("%i\n", __ARM_BF16_FORMAT_ALTERNATIVE);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_ATOMICS ");
+#ifdef __ARM_FEATURE_ATOMICS
+ _ODP_PRINT("%i\n", __ARM_FEATURE_ATOMICS);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_BF16 ");
+#ifdef __ARM_FEATURE_BF16
+ _ODP_PRINT("%i\n", __ARM_FEATURE_BF16);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_BTI_DEFAULT ");
+#ifdef __ARM_FEATURE_BTI_DEFAULT
+ _ODP_PRINT("%i\n", __ARM_FEATURE_BTI_DEFAULT);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_CDE ");
+#ifdef __ARM_FEATURE_CDE
+ _ODP_PRINT("%i\n", __ARM_FEATURE_CDE);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_CDE_COPROC ");
+#ifdef __ARM_FEATURE_CDE_COPROC
+ _ODP_PRINT("0x%X\n", __ARM_FEATURE_CDE_COPROC);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_CLZ ");
+#ifdef __ARM_FEATURE_CLZ
+ _ODP_PRINT("%i\n", __ARM_FEATURE_CLZ);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_COMPLEX ");
+#ifdef __ARM_FEATURE_COMPLEX
+ _ODP_PRINT("%i\n", __ARM_FEATURE_COMPLEX);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_COPROC ");
+#ifdef __ARM_FEATURE_COPROC
+ _ODP_PRINT("0x%X\n", __ARM_FEATURE_COPROC);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_CRC32 ");
+#ifdef __ARM_FEATURE_CRC32
+ _ODP_PRINT("%i\n", __ARM_FEATURE_CRC32);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_CRYPTO ");
+#ifdef __ARM_FEATURE_CRYPTO
+ _ODP_PRINT("%i\n", __ARM_FEATURE_CRYPTO);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_DIRECTED_ROUNDING ");
+#ifdef __ARM_FEATURE_DIRECTED_ROUNDING
+ _ODP_PRINT("%i\n", __ARM_FEATURE_DIRECTED_ROUNDING);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_DOTPROD ");
+#ifdef __ARM_FEATURE_DOTPROD
+ _ODP_PRINT("%i\n", __ARM_FEATURE_DOTPROD);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_FMA ");
+#ifdef __ARM_FEATURE_FMA
+ _ODP_PRINT("%i\n", __ARM_FEATURE_FMA);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_FP16_FML ");
+#ifdef __ARM_FEATURE_FP16_FML
+ _ODP_PRINT("%i\n", __ARM_FEATURE_FP16_FML);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_FRINT ");
+#ifdef __ARM_FEATURE_FRINT
+ _ODP_PRINT("%i\n", __ARM_FEATURE_FRINT);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_IDIV ");
+#ifdef __ARM_FEATURE_IDIV
+ _ODP_PRINT("%i\n", __ARM_FEATURE_IDIV);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_JCVT ");
+#ifdef __ARM_FEATURE_JCVT
+ _ODP_PRINT("%i\n", __ARM_FEATURE_JCVT);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_MATMUL_INT8 ");
+#ifdef __ARM_FEATURE_MATMUL_INT8
+ _ODP_PRINT("%i\n", __ARM_FEATURE_MATMUL_INT8);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_MEMORY_TAGGING ");
+#ifdef __ARM_FEATURE_MEMORY_TAGGING
+ _ODP_PRINT("%i\n", __ARM_FEATURE_MEMORY_TAGGING);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_MVE ");
+#ifdef __ARM_FEATURE_MVE
+ _ODP_PRINT("0x%X\n", __ARM_FEATURE_MVE);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_NUMERIC_MAXMIN ");
+#ifdef __ARM_FEATURE_NUMERIC_MAXMIN
+ _ODP_PRINT("%i\n", __ARM_FEATURE_NUMERIC_MAXMIN);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_PAC_DEFAULT ");
+#ifdef __ARM_FEATURE_PAC_DEFAULT
+ _ODP_PRINT("0x%X\n", __ARM_FEATURE_PAC_DEFAULT);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_QRDMX ");
+#ifdef __ARM_FEATURE_QRDMX
+ _ODP_PRINT("%i\n", __ARM_FEATURE_QRDMX);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_RNG ");
+#ifdef __ARM_FEATURE_RNG
+ _ODP_PRINT("%i\n", __ARM_FEATURE_RNG);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_SHA3 ");
+#ifdef __ARM_FEATURE_SHA3
+ _ODP_PRINT("%i\n", __ARM_FEATURE_SHA3);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_SHA512 ");
+#ifdef __ARM_FEATURE_SHA512
+ _ODP_PRINT("%i\n", __ARM_FEATURE_SHA512);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_SM3 ");
+#ifdef __ARM_FEATURE_SM3
+ _ODP_PRINT("%i\n", __ARM_FEATURE_SM3);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_SM4 ");
+#ifdef __ARM_FEATURE_SM4
+ _ODP_PRINT("%i\n", __ARM_FEATURE_SM4);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_TME ");
+#ifdef __ARM_FEATURE_TME
+ _ODP_PRINT("%i\n", __ARM_FEATURE_TME);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FEATURE_UNALIGNED ");
+#ifdef __ARM_FEATURE_UNALIGNED
+ _ODP_PRINT("%i\n", __ARM_FEATURE_UNALIGNED);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP ");
+#ifdef __ARM_FP
+ _ODP_PRINT("0x%X\n", __ARM_FP);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP_FAST ");
+#ifdef __ARM_FP_FAST
+ _ODP_PRINT("%i\n", __ARM_FP_FAST);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP_FENV_ROUNDING ");
+#ifdef __ARM_FP_FENV_ROUNDING
+ _ODP_PRINT("%i\n", __ARM_FP_FENV_ROUNDING);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP16_ARGS ");
+#ifdef __ARM_FP16_ARGS
+ _ODP_PRINT("%i\n", __ARM_FP16_ARGS);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP16_FORMAT_ALTERNATIVE ");
+#ifdef __ARM_FP16_FORMAT_ALTERNATIVE
+ _ODP_PRINT("%i\n", __ARM_FP16_FORMAT_ALTERNATIVE);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_FP16_FORMAT_IEEE ");
+#ifdef __ARM_FP16_FORMAT_IEEE
+ _ODP_PRINT("%i\n", __ARM_FP16_FORMAT_IEEE);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_NEON ");
+#ifdef __ARM_NEON
+ _ODP_PRINT("%i\n", __ARM_NEON);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_NEON_FP ");
+#ifdef __ARM_NEON_FP
+ _ODP_PRINT("0x%X\n", __ARM_NEON_FP);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_PCS_AAPCS64 ");
+#ifdef __ARM_PCS_AAPCS64
+ _ODP_PRINT("%i\n", __ARM_PCS_AAPCS64);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_ROPI ");
+#ifdef __ARM_ROPI
+ _ODP_PRINT("%i\n", __ARM_ROPI);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_RWPI ");
+#ifdef __ARM_RWPI
+ _ODP_PRINT("%i\n", __ARM_RWPI);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_SIZEOF_MINIMAL_ENUM ");
+#ifdef __ARM_SIZEOF_MINIMAL_ENUM
+ _ODP_PRINT("%i\n", __ARM_SIZEOF_MINIMAL_ENUM);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" __ARM_SIZEOF_WCHAR_T ");
+#ifdef __ARM_SIZEOF_WCHAR_T
+ _ODP_PRINT("%i\n", __ARM_SIZEOF_WCHAR_T);
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT(" ARM ISA version: ");
+#if defined(__ARM_ARCH)
+ if (__ARM_ARCH < 8) {
+ _ODP_PRINT("v%i\n", __ARM_ARCH);
+ } else if (__ARM_ARCH == 8) {
+ /* Actually, this checks for new NEON instructions in
+ * v8.1, but is currently the only way to distinguish
+ * v8.0 and >=v8.1. */
+ #ifdef __ARM_FEATURE_QRDMX
+ _ODP_PRINT("v8.1 or higher\n");
+ #else
+ _ODP_PRINT("v8.0\n");
+ #endif
+ } else {
+ /* ACLE 2018 defines that from v8.1 onwards the value includes
+ * the minor version number: __ARM_ARCH = X * 100 + Y
+ * E.g. for Armv8.1 __ARM_ARCH = 801 */
+ int major = __ARM_ARCH / 100;
+ int minor = __ARM_ARCH - (major * 100);
+
+ _ODP_PRINT("v%i.%i\n", major, minor);
+ }
+#else
+ _ODP_PRINT("%s\n", ndef);
+#endif
+
+ _ODP_PRINT("\n");
+}
+
+static void _odp_sys_info_print_hwcap_flags(void)
+{
+ uint64_t hwcap, hwcap2;
+ uint32_t size, size2, i;
+
+ hwcap = getauxval(AT_HWCAP);
+ hwcap2 = getauxval(AT_HWCAP2);
+ size = _ODP_ARRAY_SIZE(hwcap_flags);
+ size2 = _ODP_ARRAY_SIZE(hwcap2_flags);
+
+ _ODP_PRINT("ARM FEATURES SUPPORTED BY HARDWARE:\n");
+
+ /* Supported HWCAP flags */
+ for (i = 0; i < size; i++)
+ if (hwcap & hwcap_flags[i].bit_mask)
+ _ODP_PRINT("%s ", hwcap_flags[i].name);
+
+ /* Supported HWCAP2 flags */
+ for (i = 0; i < size2; i++)
+ if (hwcap2 & hwcap2_flags[i].bit_mask)
+ _ODP_PRINT("%s ", hwcap2_flags[i].name);
+
+ _ODP_PRINT("\n\nARM FEATURES NOT SUPPORTED BY HARDWARE:\n");
+
+ /* Unsupported HWCAP flags */
+ for (i = 0; i < size; i++)
+ if (hwcap_flags[i].bit_mask && (hwcap & hwcap_flags[i].bit_mask) == 0)
+ _ODP_PRINT("%s ", hwcap_flags[i].name);
+
+ /* Unsupported HWCAP2 flags */
+ for (i = 0; i < size2; i++)
+ if (hwcap2_flags[i].bit_mask && (hwcap2 & hwcap2_flags[i].bit_mask) == 0)
+ _ODP_PRINT("%s ", hwcap2_flags[i].name);
+
+ _ODP_PRINT("\n\nARM FEATURES UNKNOWN TO LINUX VERSION:\n");
+ /* Unknown HWCAP flags */
+ for (i = 0; i < size; i++)
+ if (hwcap_flags[i].bit_mask == 0)
+ _ODP_PRINT("%s ", hwcap_flags[i].name);
+
+ /* Unknown HWCAP2 flags */
+ for (i = 0; i < size2; i++)
+ if (hwcap2_flags[i].bit_mask == 0)
+ _ODP_PRINT("%s ", hwcap2_flags[i].name);
+
+ _ODP_PRINT("\n\n");
+}
+
+void _odp_cpu_flags_print_all(void)
+{
+ _odp_sys_info_print_acle_flags();
+ _odp_sys_info_print_hwcap_flags();
+}
diff --git a/platform/linux-generic/arch/aarch64/cpu_flags.h b/platform/linux-generic/arch/aarch64/cpu_flags.h
new file mode 100644
index 000000000..f520306ac
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/cpu_flags.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#ifndef ODP_PLAT_CPU_FLAGS_H_
+#define ODP_PLAT_CPU_FLAGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void _odp_cpu_flags_print_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/atomic.h b/platform/linux-generic/arch/aarch64/odp/api/abi/atomic.h
new file mode 100644
index 000000000..32a763ee6
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/atomic.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#ifdef __ARM_FEATURE_ATOMICS
+#define _ODP_LOCK_FREE_128BIT_ATOMICS
+#endif
+
+#include <odp/api/abi-default/atomic.h>
+#include <odp/api/plat/atomic_inlines.h>
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/atomic_inlines.h b/platform/linux-generic/arch/aarch64/odp/api/abi/atomic_inlines.h
new file mode 100644
index 000000000..19180ffee
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/atomic_inlines.h
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_ATOMIC_INLINES_H_
+#define ODP_API_ABI_ATOMIC_INLINES_H_
+
+#include <odp/api/atomic.h>
+
+#ifdef _ODP_LOCK_FREE_128BIT_ATOMICS
+
+/**
+ * @internal
+ * Helper macro for lockless atomic CAS operations on 128-bit integers
+ * @param[in,out] atom Pointer to the 128-bit atomic variable
+ * @param oper CAS operation
+ * @param old_val Old value
+ * @param new_val New value to be swapped
+ * @return 1 for success and 0 for fail
+ */
+#define ATOMIC_CAS_OP_128(atom, oper, old_val, new_val, val) \
+__extension__ ({ \
+ odp_u128_t _val; \
+ odp_atomic_u128_t *_atom = atom; \
+ odp_u128_t *_old_val = old_val; \
+ odp_u128_t _new_val = new_val; \
+ odp_u128_t *ptr = (odp_u128_t *)(_atom); \
+ register uint64_t old0 __asm__ ("x0"); \
+ register uint64_t old1 __asm__ ("x1"); \
+ register uint64_t new0 __asm__ ("x2"); \
+ register uint64_t new1 __asm__ ("x3"); \
+ old0 = (uint64_t)(_old_val)->u64[0]; \
+ old1 = (uint64_t)(_old_val)->u64[1]; \
+ new0 = (uint64_t)(_new_val).u64[0]; \
+ new1 = (uint64_t)(_new_val).u64[1]; \
+ __asm__ volatile(oper " %[old0], %[old1], %[new0], %[new1], [%[ptr]]" \
+ : [old0] "+r" (old0), [old1] "+r" (old1) \
+ : [new0] "r" (new0), [new1] "r" (new1), \
+ [ptr] "r" (ptr) \
+ : "memory"); \
+ _val.u64[0] = old0; \
+ _val.u64[1] = old1; \
+ val = _val; \
+})
+
+#define ATOMIC_CAS_OP_128_NO_ORDER(atom, old_value, new_value, val) \
+ ATOMIC_CAS_OP_128(atom, "casp", old_value, new_value, val)
+
+#define ATOMIC_CAS_OP_128_ACQ(atom, old_value, new_value, val) \
+ ATOMIC_CAS_OP_128(atom, "caspa", old_value, new_value, val)
+
+#define ATOMIC_CAS_OP_128_REL(atom, old_value, new_value, val) \
+ ATOMIC_CAS_OP_128(atom, "caspl", old_value, new_value, val)
+
+#define ATOMIC_CAS_OP_128_ACQ_REL(atom, old_value, new_value, val) \
+ ATOMIC_CAS_OP_128(atom, "caspal", old_value, new_value, val)
+
+static inline void _odp_atomic_init_u128(odp_atomic_u128_t *atom, odp_u128_t new_val)
+{
+ atom->v = new_val;
+}
+
+static inline odp_u128_t _odp_atomic_load_u128(odp_atomic_u128_t *atom)
+{
+ odp_u128_t val, exp;
+
+ exp.u64[0] = 0;
+ exp.u64[1] = 0;
+ ATOMIC_CAS_OP_128_NO_ORDER(atom, &exp, exp, val);
+ return val;
+}
+
+static inline void _odp_atomic_store_u128(odp_atomic_u128_t *atom, odp_u128_t new_val)
+{
+ odp_u128_t old, val;
+
+ old = atom->v;
+
+ while (1) {
+ ATOMIC_CAS_OP_128_NO_ORDER(atom, &old, new_val, val);
+
+ if ((val.u64[0] == old.u64[0]) && (val.u64[1] == old.u64[1]))
+ return;
+
+ old = val;
+ }
+}
+
+static inline int _odp_atomic_cas_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ odp_u128_t val;
+
+ ATOMIC_CAS_OP_128_NO_ORDER(atom, old_val, new_val, val);
+
+ if ((val.u64[0] == old_val->u64[0]) && (val.u64[1] == old_val->u64[1]))
+ return 1;
+
+ old_val->u64[0] = val.u64[0];
+ old_val->u64[1] = val.u64[1];
+
+ return 0;
+}
+
+static inline int _odp_atomic_cas_acq_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ odp_u128_t val;
+
+ ATOMIC_CAS_OP_128_ACQ(atom, old_val, new_val, val);
+
+ if ((val.u64[0] == old_val->u64[0]) && (val.u64[1] == old_val->u64[1]))
+ return 1;
+
+ old_val->u64[0] = val.u64[0];
+ old_val->u64[1] = val.u64[1];
+
+ return 0;
+}
+
+static inline int _odp_atomic_cas_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ odp_u128_t val;
+
+ ATOMIC_CAS_OP_128_REL(atom, old_val, new_val, val);
+
+ if ((val.u64[0] == old_val->u64[0]) && (val.u64[1] == old_val->u64[1]))
+ return 1;
+
+ old_val->u64[0] = val.u64[0];
+ old_val->u64[1] = val.u64[1];
+
+ return 0;
+}
+
+static inline int _odp_atomic_cas_acq_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ odp_u128_t val;
+
+ ATOMIC_CAS_OP_128_ACQ_REL(atom, old_val, new_val, val);
+
+ if ((val.u64[0] == old_val->u64[0]) && (val.u64[1] == old_val->u64[1]))
+ return 1;
+
+ old_val->u64[0] = val.u64[0];
+ old_val->u64[1] = val.u64[1];
+
+ return 0;
+}
+
+static inline void _odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ __asm__ volatile("stadd %w[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_sub_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ int32_t neg_val = (int32_t)-val;
+
+ __asm__ volatile("stadd %w[neg_val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [neg_val] "r" (neg_val));
+}
+
+static inline void _odp_atomic_inc_u32(odp_atomic_u32_t *atom)
+{
+ _odp_atomic_add_u32(atom, 1);
+}
+
+static inline void _odp_atomic_dec_u32(odp_atomic_u32_t *atom)
+{
+ _odp_atomic_sub_u32(atom, 1);
+}
+
+static inline void _odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ __asm__ volatile("stadd %[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_sub_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ int64_t neg_val = (int64_t)-val;
+
+ __asm__ volatile("stadd %[neg_val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [neg_val] "r" (neg_val));
+}
+
+static inline void _odp_atomic_inc_u64(odp_atomic_u64_t *atom)
+{
+ _odp_atomic_add_u64(atom, 1);
+}
+
+static inline void _odp_atomic_dec_u64(odp_atomic_u64_t *atom)
+{
+ _odp_atomic_sub_u64(atom, 1);
+}
+
+static inline void _odp_atomic_max_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ __asm__ volatile("stumax %w[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ __asm__ volatile("stumin %w[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_max_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ __asm__ volatile("stumax %[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ __asm__ volatile("stumin %[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val));
+}
+
+static inline void _odp_atomic_add_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ __asm__ volatile("staddl %w[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val)
+ : "memory");
+}
+
+static inline void _odp_atomic_sub_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ int32_t neg_val = (int32_t)-val;
+
+ __asm__ volatile("staddl %w[neg_val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [neg_val] "r" (neg_val)
+ : "memory");
+}
+
+static inline void _odp_atomic_add_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ __asm__ volatile("staddl %[val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [val] "r" (val)
+ : "memory");
+}
+
+static inline void _odp_atomic_sub_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ int64_t neg_val = (int64_t)-val;
+
+ __asm__ volatile("staddl %[neg_val], %[atom]"
+ : [atom] "+Q" (atom->v)
+ : [neg_val] "r" (neg_val)
+ : "memory");
+}
+
+#else /* !_ODP_LOCK_FREE_128BIT_ATOMICS */
+
+/* Use generic implementation */
+#include <odp/api/abi/atomic_generic.h>
+
+#endif
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/cpu.h b/platform/linux-generic/arch/aarch64/odp/api/abi/cpu.h
new file mode 100644
index 000000000..0e7cbf86b
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/cpu.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/autoheader_external.h>
+
+#ifndef ODP_CACHE_LINE_SIZE
+ #define ODP_CACHE_LINE_SIZE _ODP_CACHE_LINE_SIZE
+#endif
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/cpu_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/cpu_inlines.h b/platform/linux-generic/arch/aarch64/odp/api/abi/cpu_inlines.h
new file mode 100644
index 000000000..5890fc7a4
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/cpu_inlines.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef ODP_ARCH_CPU_INLINES_H_
+#define ODP_ARCH_CPU_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/time_cpu.h>
+
+#include <stdint.h>
+
+/* CPU frequency is shifted to decrease integer division error */
+#define _ODP_CPU_FREQ_SHIFT 16
+
+typedef struct _odp_cpu_cycles_global_t {
+ uint64_t res;
+ uint64_t res_shifted;
+ uint64_t max;
+
+} _odp_cpu_cycles_global_t;
+
+extern _odp_cpu_cycles_global_t _odp_cpu_cycles_glob;
+
+static inline void _odp_cpu_pause(void)
+{
+ /* YIELD hints the CPU to switch to another thread if possible
+ * and executes as a NOP otherwise.
+ * ISB flushes the pipeline, then restarts. This is guaranteed to
+ * stall the CPU a number of cycles.
+ */
+ __asm volatile("isb" ::: "memory");
+}
+
+static inline uint64_t _odp_cpu_cycles(void)
+{
+ return (_odp_time_cpu_global() * _odp_cpu_cycles_glob.res_shifted) >> _ODP_CPU_FREQ_SHIFT;
+}
+
+static inline uint64_t _odp_cpu_cycles_resolution(void)
+{
+ return _odp_cpu_cycles_glob.res;
+}
+
+static inline uint64_t _odp_cpu_cycles_max(void)
+{
+ return _odp_cpu_cycles_glob.max;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/hash_crc32.h b/platform/linux-generic/arch/aarch64/odp/api/abi/hash_crc32.h
new file mode 100644
index 000000000..56bdd2812
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/hash_crc32.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#ifndef ODP_API_ABI_HASH_CRC32_H_
+#define ODP_API_ABI_HASH_CRC32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+uint32_t _odp_hash_crc32_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+uint32_t _odp_hash_crc32c_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+
+#ifdef __ARM_FEATURE_CRC32
+
+#include <arm_acle.h>
+
+static inline uint32_t _odp_hash_crc32(const void *data_ptr, uint32_t data_len,
+ uint32_t init_val)
+{
+ uint32_t i;
+ uintptr_t pd = (uintptr_t)data_ptr;
+
+ for (i = 0; i < data_len / 8; i++) {
+ init_val = __crc32d(init_val, *(const uint64_t *)pd);
+ pd += 8;
+ }
+
+ if (data_len & 0x4) {
+ init_val = __crc32w(init_val, *(const uint32_t *)pd);
+ pd += 4;
+ }
+
+ if (data_len & 0x2) {
+ init_val = __crc32h(init_val, *(const uint16_t *)pd);
+ pd += 2;
+ }
+
+ if (data_len & 0x1)
+ init_val = __crc32b(init_val, *(const uint8_t *)pd);
+
+ return init_val;
+}
+
+static inline uint32_t _odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ uint32_t i;
+ uintptr_t pd = (uintptr_t)data;
+
+ for (i = 0; i < data_len / 8; i++) {
+ init_val = __crc32cd(init_val, *(const uint64_t *)pd);
+ pd += 8;
+ }
+
+ if (data_len & 0x4) {
+ init_val = __crc32cw(init_val, *(const uint32_t *)pd);
+ pd += 4;
+ }
+
+ if (data_len & 0x2) {
+ init_val = __crc32ch(init_val, *(const uint16_t *)pd);
+ pd += 2;
+ }
+
+ if (data_len & 0x1)
+ init_val = __crc32cb(init_val, *(const uint8_t *)pd);
+
+ return init_val;
+}
+
+#else /* __ARM_FEATURE_CRC32 */
+
+/*
+ * Fall back to software implementation
+ */
+
+static inline uint32_t _odp_hash_crc32(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32_generic(data, data_len, init_val);
+}
+
+static inline uint32_t _odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32c_generic(data, data_len, init_val);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/sync_inlines.h b/platform/linux-generic/arch/aarch64/odp/api/abi/sync_inlines.h
new file mode 100644
index 000000000..3d42e7dd8
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/sync_inlines.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ARCH_SYNC_INLINES_H_
+#define ODP_ARCH_SYNC_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void _odp_mb_sync(void)
+{
+ __asm__ volatile("dsb sy" ::: "memory");
+}
+
+static inline void _odp_mb_sync_load(void)
+{
+ __asm__ volatile("dsb ld" ::: "memory");
+}
+
+static inline void _odp_mb_sync_store(void)
+{
+ __asm__ volatile("dsb st" ::: "memory");
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/time_cpu.h b/platform/linux-generic/arch/aarch64/odp/api/abi/time_cpu.h
new file mode 100644
index 000000000..29e92da2a
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/time_cpu.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_TIME_CPU_H_
+#define ODP_API_ABI_TIME_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+static inline uint64_t _odp_time_cpu_global(void)
+{
+ uint64_t cntvct;
+
+ __asm__ volatile("mrs %0, cntvct_el0" : "=r"(cntvct) : : "memory");
+
+ return cntvct;
+}
+
+static inline uint64_t _odp_time_cpu_global_strict(void)
+{
+ uint64_t cntvct;
+
+ __asm__ volatile("isb" ::: "memory");
+ __asm__ volatile("mrs %0, cntvct_el0" : "=r"(cntvct) : : "memory");
+
+ return cntvct;
+}
+
+static inline uint64_t _odp_time_cpu_global_freq(void)
+{
+ uint64_t cntfrq;
+
+ __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq) : : );
+
+ return cntfrq;
+}
+
+static inline int _odp_time_cpu_global_freq_is_const(void)
+{
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/time_inlines.h b/platform/linux-generic/arch/aarch64/odp/api/abi/time_inlines.h
new file mode 100644
index 000000000..eed571027
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/time_inlines.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi/time_cpu_inlines.h>
diff --git a/platform/linux-generic/arch/aarch64/odp/api/abi/wait_until.h b/platform/linux-generic/arch/aarch64/odp/api/abi/wait_until.h
new file mode 100644
index 000000000..73a3d476a
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp/api/abi/wait_until.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 ARM Limited
+ */
+
+#ifndef ODP_API_ABI_WAIT_UNTIL_H_
+#define ODP_API_ABI_WAIT_UNTIL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/autoheader_external.h>
+
+#ifdef _ODP_WFE_LOCKS
+
+#include <stdint.h>
+
+#include <odp/api/atomic.h>
+
+static inline void
+_odp_wait_until_equal_acq_u32(odp_atomic_u32_t *addr, uint32_t expected)
+{
+ uint32_t value;
+ uint32_t *var = &addr->v;
+
+ __asm__ volatile("sevl" : : : "memory");
+ do {
+ __asm__ volatile("wfe" : : : "memory");
+ __asm__ volatile("ldaxr %w0, [%1]"
+ : "=&r" (value)
+ : "r" (var)
+ : "memory");
+ } while (expected != value);
+}
+
+#else /* !_ODP_WFE_LOCKS*/
+
+/* Use generic implementation */
+#include <odp/api/abi/wait_until_generic.h>
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp_atomic.c b/platform/linux-generic/arch/aarch64/odp_atomic.c
new file mode 100644
index 000000000..56604f7f0
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_atomic.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#include <odp/api/atomic.h>
+
+int odp_atomic_lock_free_u64(odp_atomic_op_t *atomic_op)
+{
+#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
+ /* All operations have locks */
+ if (atomic_op)
+ atomic_op->all_bits = 0;
+
+ return 0;
+#else
+ /* All operations are lock-free */
+ if (atomic_op) {
+ atomic_op->all_bits = ~((uint32_t)0);
+ atomic_op->op.init = 0;
+ }
+
+ return 2;
+#endif
+}
+
+int odp_atomic_lock_free_u128(odp_atomic_op_t *atomic_op)
+{
+#ifdef _ODP_LOCK_FREE_128BIT_ATOMICS
+ if (atomic_op) {
+ atomic_op->all_bits = 0;
+ atomic_op->op.load = 1;
+ atomic_op->op.store = 1;
+ atomic_op->op.cas = 1;
+ }
+
+ return 2;
+#elif defined(__SIZEOF_INT128__)
+ if (__atomic_is_lock_free(16, NULL)) {
+ if (atomic_op) {
+ atomic_op->all_bits = 0;
+ atomic_op->op.load = 1;
+ atomic_op->op.store = 1;
+ atomic_op->op.cas = 1;
+ }
+ return 2;
+ }
+#endif
+ /* All operations have locks */
+ if (atomic_op)
+ atomic_op->all_bits = 0;
+
+ return 0;
+}
diff --git a/platform/linux-generic/arch/aarch64/odp_atomic.h b/platform/linux-generic/arch/aarch64/odp_atomic.h
new file mode 100644
index 000000000..5bbaa9ca8
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_atomic.h
@@ -0,0 +1,323 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2021 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_ATOMIC_H
+#define PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_ATOMIC_H
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+#error This file should not be included directly, please include odp_cpu.h
+#endif
+
+#include <odp_types_internal.h>
+#include <limits.h>
+
+#ifdef CONFIG_DMBSTR
+
+#define atomic_store_release(loc, val, ro) \
+do { \
+ _odp_release_barrier(ro); \
+ __atomic_store_n(loc, val, __ATOMIC_RELAXED); \
+} while (0)
+
+#else
+
+#define atomic_store_release(loc, val, ro) \
+ __atomic_store_n(loc, val, __ATOMIC_RELEASE)
+
+#endif /* CONFIG_DMBSTR */
+
+#define HAS_ACQ(mo) ((mo) != __ATOMIC_RELAXED && (mo) != __ATOMIC_RELEASE)
+#define HAS_RLS(mo) ((mo) == __ATOMIC_RELEASE || (mo) == __ATOMIC_ACQ_REL || \
+ (mo) == __ATOMIC_SEQ_CST)
+
+#define LL_MO(mo) (HAS_ACQ((mo)) ? __ATOMIC_ACQUIRE : __ATOMIC_RELAXED)
+#define SC_MO(mo) (HAS_RLS((mo)) ? __ATOMIC_RELEASE : __ATOMIC_RELAXED)
+
+#ifndef __ARM_FEATURE_QRDMX /* Feature only available in v8.1a and beyond */
+static inline bool
+__lockfree_compare_exchange_16(register _odp_u128_t *var, _odp_u128_t *exp,
+ register _odp_u128_t neu, bool weak, int mo_success,
+ int mo_failure)
+{
+ (void)weak; /* Always do strong CAS or we can't perform atomic read */
+ /* Ignore memory ordering for failure, memory order for
+ * success must be stronger or equal. */
+ (void)mo_failure;
+ register _odp_u128_t old;
+ register _odp_u128_t expected;
+ int ll_mo = LL_MO(mo_success);
+ int sc_mo = SC_MO(mo_success);
+
+ expected = *exp;
+ __asm__ volatile("" ::: "memory");
+ do {
+ /* Atomicity of LLD is not guaranteed */
+ old = lld(var, ll_mo);
+ /* Must write back neu or old to verify atomicity of LLD */
+ } while (odp_unlikely(scd(var, old == expected ? neu : old, sc_mo)));
+ *exp = old; /* Always update, atomically read value */
+ return old == expected;
+}
+
+static inline _odp_u128_t __lockfree_exchange_16(_odp_u128_t *var,
+ _odp_u128_t neu, int mo)
+{
+ register _odp_u128_t old;
+ int ll_mo = LL_MO(mo);
+ int sc_mo = SC_MO(mo);
+
+ do {
+ /* Atomicity of LLD is not guaranteed */
+ old = lld(var, ll_mo);
+ /* Must successfully write back to verify atomicity of LLD */
+ } while (odp_unlikely(scd(var, neu, sc_mo)));
+ return old;
+}
+
+static inline _odp_u128_t __lockfree_fetch_and_16(_odp_u128_t *var,
+ _odp_u128_t mask, int mo)
+{
+ register _odp_u128_t old;
+ int ll_mo = LL_MO(mo);
+ int sc_mo = SC_MO(mo);
+
+ do {
+ /* Atomicity of LLD is not guaranteed */
+ old = lld(var, ll_mo);
+ /* Must successfully write back to verify atomicity of LLD */
+ } while (odp_unlikely(scd(var, old & mask, sc_mo)));
+ return old;
+}
+
+static inline _odp_u128_t __lockfree_fetch_or_16(_odp_u128_t *var,
+ _odp_u128_t mask, int mo)
+{
+ register _odp_u128_t old;
+ int ll_mo = LL_MO(mo);
+ int sc_mo = SC_MO(mo);
+
+ do {
+ /* Atomicity of LLD is not guaranteed */
+ old = lld(var, ll_mo);
+ /* Must successfully write back to verify atomicity of LLD */
+ } while (odp_unlikely(scd(var, old | mask, sc_mo)));
+ return old;
+}
+
+#else
+
+static inline _odp_u128_t cas_u128(_odp_u128_t *ptr, _odp_u128_t old_val,
+ _odp_u128_t new_val, int mo)
+{
+ /* CASP instructions require that the first register number is paired */
+ register uint64_t old0 __asm__ ("x0");
+ register uint64_t old1 __asm__ ("x1");
+ register uint64_t new0 __asm__ ("x2");
+ register uint64_t new1 __asm__ ("x3");
+
+ old0 = (uint64_t)old_val;
+ old1 = (uint64_t)(old_val >> 64);
+ new0 = (uint64_t)new_val;
+ new1 = (uint64_t)(new_val >> 64);
+
+ if (mo == __ATOMIC_RELAXED) {
+ __asm__ volatile("casp %[old0], %[old1], %[new0], %[new1], [%[ptr]]"
+ : [old0] "+r" (old0), [old1] "+r" (old1)
+ : [new0] "r" (new0), [new1] "r" (new1), [ptr] "r" (ptr)
+ : "memory");
+ } else if (mo == __ATOMIC_ACQUIRE) {
+ __asm__ volatile("caspa %[old0], %[old1], %[new0], %[new1], [%[ptr]]"
+ : [old0] "+r" (old0), [old1] "+r" (old1)
+ : [new0] "r" (new0), [new1] "r" (new1), [ptr] "r" (ptr)
+ : "memory");
+ } else if (mo == __ATOMIC_ACQ_REL) {
+ __asm__ volatile("caspal %[old0], %[old1], %[new0], %[new1], [%[ptr]]"
+ : [old0] "+r" (old0), [old1] "+r" (old1)
+ : [new0] "r" (new0), [new1] "r" (new1), [ptr] "r" (ptr)
+ : "memory");
+ } else if (mo == __ATOMIC_RELEASE) {
+ __asm__ volatile("caspl %[old0], %[old1], %[new0], %[new1], [%[ptr]]"
+ : [old0] "+r" (old0), [old1] "+r" (old1)
+ : [new0] "r" (new0), [new1] "r" (new1), [ptr] "r" (ptr)
+ : "memory");
+ } else {
+ abort();
+ }
+
+ return ((_odp_u128_t)old0) | (((_odp_u128_t)old1) << 64);
+}
+
+static inline bool
+__lockfree_compare_exchange_16(register _odp_u128_t *var, _odp_u128_t *exp,
+ register _odp_u128_t neu, bool weak, int mo_success,
+ int mo_failure)
+{
+ (void)weak;
+ (void)mo_failure;
+ _odp_u128_t old;
+ _odp_u128_t expected;
+
+ expected = *exp;
+ old = cas_u128(var, expected, neu, mo_success);
+ *exp = old; /* Always update, atomically read value */
+ return old == expected;
+}
+
+static inline _odp_u128_t __lockfree_exchange_16(_odp_u128_t *var,
+ _odp_u128_t neu, int mo)
+{
+ _odp_u128_t old;
+ _odp_u128_t expected;
+
+ do {
+ expected = *var;
+ old = cas_u128(var, expected, neu, mo);
+ } while (old != expected);
+ return old;
+}
+
+static inline _odp_u128_t __lockfree_fetch_and_16(_odp_u128_t *var,
+ _odp_u128_t mask, int mo)
+{
+ _odp_u128_t old;
+ _odp_u128_t expected;
+
+ do {
+ expected = *var;
+ old = cas_u128(var, expected, expected & mask, mo);
+ } while (old != expected);
+ return old;
+}
+
+static inline _odp_u128_t __lockfree_fetch_or_16(_odp_u128_t *var,
+ _odp_u128_t mask, int mo)
+{
+ _odp_u128_t old;
+ _odp_u128_t expected;
+
+ do {
+ expected = *var;
+ old = cas_u128(var, expected, expected | mask, mo);
+ } while (old != expected);
+ return old;
+}
+
+#endif /* __ARM_FEATURE_QRDMX */
+
+static inline _odp_u128_t __lockfree_load_16(_odp_u128_t *var, int mo)
+{
+ _odp_u128_t old = *var; /* Possibly torn read */
+
+ /* Do CAS to ensure atomicity
+ * Either CAS succeeds (writing back the same value)
+ * Or CAS fails and returns the old value (atomic read)
+ */
+ (void)__lockfree_compare_exchange_16(var, &old, old, false, mo, mo);
+ return old;
+}
+
+static inline _odp_u128_t lockfree_load_u128(_odp_u128_t *atomic)
+{
+ return __lockfree_load_16((_odp_u128_t *)atomic, __ATOMIC_RELAXED);
+}
+
+static inline int lockfree_cas_acq_rel_u128(_odp_u128_t *atomic,
+ _odp_u128_t old_val,
+ _odp_u128_t new_val)
+{
+ return __lockfree_compare_exchange_16((_odp_u128_t *)atomic,
+ (_odp_u128_t *)&old_val,
+ new_val,
+ 0,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_RELAXED);
+}
+
+static inline int lockfree_check_u128(void)
+{
+ return 1;
+}
+
+/** Atomic bit set operations with memory ordering */
+#if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16
+typedef _odp_u128_t bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_INT128__)
+
+#elif __GCC_ATOMIC_LLONG_LOCK_FREE == 2 && \
+ __SIZEOF_LONG_LONG__ != __SIZEOF_LONG__
+typedef unsigned long long bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_LONG_LONG__)
+
+#elif __GCC_ATOMIC_LONG_LOCK_FREE == 2 && __SIZEOF_LONG__ != __SIZEOF_INT__
+typedef unsigned long bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_LONG__)
+
+#elif __GCC_ATOMIC_INT_LOCK_FREE == 2
+typedef unsigned int bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_INT__)
+
+#else
+/* Target does not support lock-free atomic operations */
+typedef unsigned int bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_INT__)
+#endif
+
+#if ATOM_BITSET_SIZE <= 32
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ return 1UL << bit;
+}
+
+#elif ATOM_BITSET_SIZE <= 64
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ return 1ULL << bit;
+}
+
+#elif ATOM_BITSET_SIZE <= 128
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ if (bit < 64)
+ return 1ULL << bit;
+ else
+ return (_odp_u128_t)(1ULL << (bit - 64)) << 64;
+}
+
+#else
+#error Unsupported size of bit sets (ATOM_BITSET_SIZE)
+#endif
+
+static inline bitset_t atom_bitset_load(bitset_t *bs, int mo)
+{
+ return __lockfree_load_16(bs, mo);
+}
+
+static inline void atom_bitset_set(bitset_t *bs, uint32_t bit, int mo)
+{
+ (void)__lockfree_fetch_or_16(bs, bitset_mask(bit), mo);
+}
+
+static inline void atom_bitset_clr(bitset_t *bs, uint32_t bit, int mo)
+{
+ (void)__lockfree_fetch_and_16(bs, ~bitset_mask(bit), mo);
+}
+
+static inline bitset_t atom_bitset_xchg(bitset_t *bs, bitset_t neu, int mo)
+{
+ return __lockfree_exchange_16(bs, neu, mo);
+}
+
+static inline bitset_t atom_bitset_cmpxchg(bitset_t *bs, bitset_t *old,
+ bitset_t neu, bool weak,
+ int mo_success, int mo_failure)
+{
+ return __lockfree_compare_exchange_16(bs, old, neu, weak, mo_success,
+ mo_failure);
+}
+
+#endif /* PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_ATOMIC_H */
diff --git a/platform/linux-generic/arch/aarch64/odp_cpu.h b/platform/linux-generic/arch/aarch64/odp_cpu.h
new file mode 100644
index 000000000..cd15cda2d
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_cpu.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+#define PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+
+#if !defined(__aarch64__)
+#error Use this file only when compiling for ARMv8 architecture
+#endif
+
+#include <odp_debug_internal.h>
+#include <odp_types_internal.h>
+
+/*
+ * Use LLD/SCD atomic primitives instead of lock-based code path in llqueue
+ * LLD/SCD is on ARM the fastest way to enqueue and dequeue elements from a
+ * linked list queue.
+ */
+#define CONFIG_LLDSCD
+
+/*
+ * Use DMB;STR instead of STRL on ARM
+ * On early ARMv8 implementations (e.g. Cortex-A57) this is noticeably more
+ * performant than using store-release.
+ * This also allows for load-only barriers (DMB ISHLD) which are much cheaper
+ * than a full barrier
+ */
+#define CONFIG_DMBSTR
+
+/* Only ARMv8 supports DMB ISHLD */
+/* A load only barrier is much cheaper than full barrier */
+#define _odp_release_barrier(ro) \
+do { \
+ if (ro) \
+ __asm__ volatile("dmb ishld" ::: "memory"); \
+ else \
+ __asm__ volatile("dmb ish" ::: "memory"); \
+} while (0)
+
+static inline uint16_t ll8(uint8_t *var, int mm)
+{
+ uint16_t old;
+
+ _ODP_ASSERT(mm == __ATOMIC_ACQUIRE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_ACQUIRE)
+ __asm__ volatile("ldaxrb %w0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("ldxrb %w0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : );
+ return old;
+}
+
+static inline uint32_t ll32(uint32_t *var, int mm)
+{
+ uint32_t old;
+
+ _ODP_ASSERT(mm == __ATOMIC_ACQUIRE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_ACQUIRE)
+ __asm__ volatile("ldaxr %w0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("ldxr %w0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : );
+ return old;
+}
+
+/* Return 0 on success, 1 on failure */
+static inline uint32_t sc32(uint32_t *var, uint32_t neu, int mm)
+{
+ uint32_t ret;
+
+ _ODP_ASSERT(mm == __ATOMIC_RELEASE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_RELEASE)
+ __asm__ volatile("stlxr %w0, %w1, [%2]"
+ : "=&r" (ret)
+ : "r" (neu), "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("stxr %w0, %w1, [%2]"
+ : "=&r" (ret)
+ : "r" (neu), "r" (var)
+ : );
+ return ret;
+}
+
+static inline uint64_t ll64(uint64_t *var, int mm)
+{
+ uint64_t old;
+
+ _ODP_ASSERT(mm == __ATOMIC_ACQUIRE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_ACQUIRE)
+ __asm__ volatile("ldaxr %0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("ldxr %0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : );
+ return old;
+}
+
+/* Return 0 on success, 1 on failure */
+static inline uint32_t sc64(uint64_t *var, uint64_t neu, int mm)
+{
+ uint32_t ret;
+
+ _ODP_ASSERT(mm == __ATOMIC_RELEASE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_RELEASE)
+ __asm__ volatile("stlxr %w0, %1, [%2]"
+ : "=&r" (ret)
+ : "r" (neu), "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("stxr %w0, %1, [%2]"
+ : "=&r" (ret)
+ : "r" (neu), "r" (var)
+ : );
+ return ret;
+}
+
+union i128 {
+ _odp_u128_t i128;
+ int64_t i64[2];
+};
+
+static inline _odp_u128_t lld(_odp_u128_t *var, int mm)
+{
+ union i128 old;
+
+ _ODP_ASSERT(mm == __ATOMIC_ACQUIRE || mm == __ATOMIC_RELAXED);
+
+ if (mm == __ATOMIC_ACQUIRE)
+ __asm__ volatile("ldaxp %0, %1, [%2]"
+ : "=&r" (old.i64[0]), "=&r" (old.i64[1])
+ : "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("ldxp %0, %1, [%2]"
+ : "=&r" (old.i64[0]), "=&r" (old.i64[1])
+ : "r" (var)
+ : );
+ return old.i128;
+}
+
+/* Return 0 on success, 1 on failure */
+static inline uint32_t scd(_odp_u128_t *var, _odp_u128_t neu, int mm)
+{
+ uint32_t ret;
+
+ _ODP_ASSERT(mm == __ATOMIC_RELEASE || mm == __ATOMIC_RELAXED);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ if (mm == __ATOMIC_RELEASE)
+ __asm__ volatile("stlxp %w0, %1, %2, [%3]"
+ : "=&r" (ret)
+ : "r" (((*(union i128 *)&neu)).i64[0]),
+ "r" (((*(union i128 *)&neu)).i64[1]),
+ "r" (var)
+ : "memory");
+ else
+ __asm__ volatile("stxp %w0, %1, %2, [%3]"
+ : "=&r" (ret)
+ : "r" (((*(union i128 *)&neu)).i64[0]),
+ "r" (((*(union i128 *)&neu)).i64[1]),
+ "r" (var)
+ : );
+#pragma GCC diagnostic pop
+ return ret;
+}
+
+#include "odp_atomic.h"
+#include "odp_wait_until.h"
+
+#ifdef __ARM_FEATURE_UNALIGNED
+#define _ODP_UNALIGNED 1
+#else
+#define _ODP_UNALIGNED 0
+#endif
+
+#endif /* PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H */
diff --git a/platform/linux-generic/arch/aarch64/odp_cpu_cycles.c b/platform/linux-generic/arch/aarch64/odp_cpu_cycles.c
new file mode 100644
index 000000000..fba263ee4
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_cpu_cycles.c
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/cpu.h>
+
+#include <odp/api/abi/cpu_inlines.h>
+#include <odp/api/abi/time_cpu.h>
+
+#include <odp_debug_internal.h>
+#include <odp_init_internal.h>
+
+#include <string.h>
+
+#include <odp/visibility_begin.h>
+
+_odp_cpu_cycles_global_t _odp_cpu_cycles_glob;
+
+#include <odp/visibility_end.h>
+
+int _odp_cpu_cycles_init_global(void)
+{
+ uint64_t cpu_hz, cpu_time_hz;
+
+ memset(&_odp_cpu_cycles_glob, 0, sizeof(_odp_cpu_cycles_global_t));
+
+ cpu_time_hz = _odp_time_cpu_global_freq();
+ if (cpu_time_hz == 0) {
+ _ODP_ERR("CPU time counter frequency not available\n");
+ return -1;
+ }
+
+ cpu_hz = odp_cpu_hz_max_id(0);
+ if (cpu_hz == 0) {
+ _ODP_ERR("CPU frequency not available\n");
+ return -1;
+ }
+
+ _odp_cpu_cycles_glob.res_shifted = (cpu_hz << _ODP_CPU_FREQ_SHIFT) / cpu_time_hz;
+
+ _odp_cpu_cycles_glob.res = cpu_hz > cpu_time_hz ?
+ (_odp_cpu_cycles_glob.res_shifted >> _ODP_CPU_FREQ_SHIFT) : 1;
+
+ _odp_cpu_cycles_glob.max = (UINT64_MAX >> _ODP_CPU_FREQ_SHIFT) -
+ (UINT64_MAX % _odp_cpu_cycles_glob.res);
+
+ return 0;
+}
diff --git a/platform/linux-generic/arch/aarch64/odp_crypto_armv8.c b/platform/linux-generic/arch/aarch64/odp_crypto_armv8.c
new file mode 100644
index 000000000..3d1d7bf40
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_crypto_armv8.c
@@ -0,0 +1,894 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/crypto.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/sync.h>
+#include <odp/api/debug.h>
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/hints.h>
+#include <odp/api/random.h>
+
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp/api/plat/thread_inlines.h>
+
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_packet_internal.h>
+
+#include "AArch64cryptolib.h"
+
+#define MAX_SESSIONS 4000
+/* Length in bytes */
+#define ARM_CRYPTO_MAX_CIPHER_KEY_LENGTH 32
+#define ARM_CRYPTO_MAX_AUTH_KEY_LENGTH 32
+#define ARM_CRYPTO_MAX_IV_LENGTH 16
+#define ARM_CRYPTO_MAX_AAD_LENGTH 16
+#define ARM_CRYPTO_MAX_DATA_LENGTH 65536
+#define ARM_CRYPTO_MAX_DIGEST_LENGTH 16
+
+#define AES_GCM_IV_LEN 12
+ODP_STATIC_ASSERT(AES_GCM_IV_LEN <= ARM_CRYPTO_MAX_IV_LENGTH,
+ "AES_GCM_IV_LEN exceeds ARM_CRYPTO_MAX_IV_LENGTH");
+
+/*
+ * ARM crypto library may read up to 15 bytes past the end of input
+ * data and AAD and write up to 15 bytes past the end of output data.
+ */
+#define OOB_WRITE_LEN 16 /* rounded up to 16 bytes for efficiency */
+
+/*
+ * Data buffer size must be a multiple of 16, because the ARM crypto
+ * library will write full 16 byte blocks even if the last data block
+ * is not a full block.
+ */
+ODP_STATIC_ASSERT(ARM_CRYPTO_MAX_DATA_LENGTH % 16 == 0,
+ "Data buffer size not a multiple of 16");
+
+/*
+ * IV buffer size must be a multiple of 16, because the ARM crypto
+ * library will read in 16 byte blocks even if the last data block
+ * is not a full block.
+ */
+ODP_STATIC_ASSERT(ARM_CRYPTO_MAX_IV_LENGTH % 16 == 0,
+ "IV buffer size not a multiple of 16");
+
+/*
+ * Cipher algorithm capabilities
+ *
+ * Keep sorted: first by key length, then by IV length
+ */
+static const odp_crypto_cipher_capability_t cipher_capa_null[] = {
+{.key_len = 0, .iv_len = 0} };
+
+#ifdef __ARM_FEATURE_AES
+static const odp_crypto_cipher_capability_t cipher_capa_aes_gcm[] = {
+{.key_len = 16, .iv_len = AES_GCM_IV_LEN},
+{.key_len = 24, .iv_len = AES_GCM_IV_LEN},
+{.key_len = 32, .iv_len = AES_GCM_IV_LEN} };
+#endif
+
+/*
+ * Authentication algorithm capabilities
+ *
+ * Keep sorted: first by digest length, then by key length
+ */
+static const odp_crypto_auth_capability_t auth_capa_null[] = {
+{.digest_len = 0, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+#define AES_GCM_TAG_LEN 16
+
+#ifdef __ARM_FEATURE_AES
+static const odp_crypto_auth_capability_t auth_capa_aes_gcm[] = {
+{.digest_len = AES_GCM_TAG_LEN, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
+#endif
+
+/** Forward declaration of session structure */
+typedef struct odp_crypto_generic_session_t odp_crypto_generic_session_t;
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef
+void (*crypto_func_t)(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session);
+
+/**
+ * Per crypto session data structure
+ */
+struct odp_crypto_generic_session_t {
+ odp_crypto_generic_session_t *next;
+
+ /* Session creation parameters */
+ odp_crypto_session_param_t p;
+
+ struct {
+ uint8_t key_data[ARM_CRYPTO_MAX_CIPHER_KEY_LENGTH];
+ } cipher;
+
+ struct {
+ uint8_t key[ARM_CRYPTO_MAX_AUTH_KEY_LENGTH];
+ } auth;
+
+ crypto_func_t func;
+ unsigned int idx;
+ armv8_cipher_constants_t cc;
+};
+
+typedef struct odp_crypto_global_s odp_crypto_global_t;
+
+struct odp_crypto_global_s {
+ odp_spinlock_t lock;
+ odp_crypto_generic_session_t *free;
+ odp_crypto_generic_session_t sessions[MAX_SESSIONS];
+};
+
+static odp_crypto_global_t *global;
+
+typedef struct crypto_local_t {
+ uint8_t buffer[ARM_CRYPTO_MAX_DATA_LENGTH];
+} crypto_local_t;
+
+static __thread crypto_local_t local;
+
+static
+odp_crypto_generic_session_t *alloc_session(void)
+{
+ odp_crypto_generic_session_t *session = NULL;
+
+ odp_spinlock_lock(&global->lock);
+ session = global->free;
+ if (session) {
+ global->free = session->next;
+ session->next = NULL;
+ }
+ odp_spinlock_unlock(&global->lock);
+
+ if (!session)
+ return NULL;
+
+ session->idx = session - global->sessions;
+
+ return session;
+}
+
+static
+void free_session(odp_crypto_generic_session_t *session)
+{
+ odp_spinlock_lock(&global->lock);
+ session->next = global->free;
+ global->free = session;
+ odp_spinlock_unlock(&global->lock);
+}
+
+static inline void set_crypto_op_result(odp_packet_t pkt,
+ odp_crypto_alg_err_t cipher_err,
+ odp_crypto_alg_err_t auth_err)
+{
+ odp_crypto_packet_result_t *op_result;
+
+ op_result = &packet_hdr(pkt)->crypto_op_result;
+ op_result->cipher_status.alg_err = cipher_err;
+ op_result->auth_status.alg_err = auth_err;
+}
+
+static inline void set_crypto_op_result_ok(odp_packet_t pkt)
+{
+ set_crypto_op_result(pkt,
+ ODP_CRYPTO_ALG_ERR_NONE,
+ ODP_CRYPTO_ALG_ERR_NONE);
+}
+
+static void
+null_crypto_routine(odp_packet_t pkt ODP_UNUSED,
+ const odp_crypto_packet_op_param_t *param ODP_UNUSED,
+ odp_crypto_generic_session_t *session ODP_UNUSED)
+{
+ set_crypto_op_result_ok(pkt);
+}
+
+static inline void copy_aad(uint8_t *dst, const uint8_t *src, uint32_t len)
+{
+ _ODP_ASSERT(len == 8 || len == 12);
+
+ /* Use constant length memcpy for better optimization result */
+ if (len == 8)
+ memcpy(dst, src, 8);
+ else
+ memcpy(dst, src, 12);
+}
+
+static
+void aes_gcm_encrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ armv8_cipher_state_t cs = {
+ .counter = {
+ .d = {0, 0}
+ }
+ };
+ uint8_t iv_data[ARM_CRYPTO_MAX_IV_LENGTH];
+ uint64_t iv_bit_length = AES_GCM_IV_LEN * 8;
+ uint64_t plaintext_bit_length = param->cipher_range.length * 8;
+ uint64_t aad_bit_length = session->p.auth_aad_len * 8;
+ uint32_t in_pos = param->cipher_range.offset;
+ uint32_t in_len = param->cipher_range.length;
+ odp_bool_t continuous_data;
+ uint16_t saved_tail[OOB_WRITE_LEN];
+ uint8_t tag[AES_GCM_TAG_LEN];
+ int rc;
+
+ /* Fail early if cipher_range is too large */
+ if (odp_unlikely(in_len > ARM_CRYPTO_MAX_DATA_LENGTH)) {
+ _ODP_DBG("ARM Crypto: Packet size too large for requested operation\n");
+ goto err;
+ }
+
+ /* The crypto lib may read 16 bytes. Copy to a big enough buffer */
+ _ODP_ASSERT(param->cipher_iv_ptr != NULL);
+ memcpy(iv_data, param->cipher_iv_ptr, AES_GCM_IV_LEN);
+
+ cs.constants = &session->cc;
+
+ rc = armv8_aes_gcm_set_counter(iv_data, iv_bit_length, &cs);
+ if (odp_unlikely(rc)) {
+ _ODP_DBG("ARM Crypto: Failure while setting nonce\n");
+ goto err;
+ }
+
+ /* Copy AAD in a stack to make sure that the ARM crypto library can
+ * read it in 16 byte chunks. */
+ uint8_t aad[ARM_CRYPTO_MAX_AAD_LENGTH];
+
+ copy_aad(aad, param->aad_ptr, session->p.auth_aad_len);
+
+ uint32_t seg_len = 0;
+ uint8_t *data = odp_packet_offset(pkt, in_pos, &seg_len, NULL);
+
+ if (odp_unlikely(odp_packet_is_segmented(pkt)) ||
+ odp_unlikely(odp_packet_tailroom(pkt) < OOB_WRITE_LEN)) {
+ /* Packet is segmented or it may not be safe to read and write
+ * beyond the end of packet data. Copy the cipher range to a
+ * contiguous buffer. */
+ odp_packet_copy_to_mem(pkt, in_pos, in_len, local.buffer);
+
+ data = local.buffer;
+ continuous_data = false;
+ } else {
+ /* Save data that might get overwritten */
+ memcpy(saved_tail, data + in_len, OOB_WRITE_LEN);
+ continuous_data = true;
+ }
+
+ rc = armv8_enc_aes_gcm_from_state(&cs,
+ aad, aad_bit_length,
+ data, plaintext_bit_length,
+ data,
+ tag);
+ if (odp_unlikely(rc)) {
+ _ODP_DBG("ARM Crypto: AES GCM Encoding failed\n");
+ goto err;
+ }
+
+ if (odp_likely(continuous_data)) {
+ memcpy(data + in_len, saved_tail, OOB_WRITE_LEN);
+ memcpy(data - in_pos + param->hash_result_offset,
+ tag, AES_GCM_TAG_LEN);
+ } else {
+ odp_packet_copy_from_mem(pkt, in_pos, in_len, data);
+ odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+ AES_GCM_TAG_LEN, tag);
+ }
+
+ set_crypto_op_result_ok(pkt);
+ return;
+
+err:
+ set_crypto_op_result(pkt,
+ ODP_CRYPTO_ALG_ERR_DATA_SIZE,
+ ODP_CRYPTO_ALG_ERR_NONE);
+}
+
+static
+void aes_gcm_decrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ armv8_cipher_state_t cs = {
+ .counter = {
+ .d = {0, 0}
+ }
+ };
+ uint8_t iv_data[ARM_CRYPTO_MAX_IV_LENGTH];
+ uint8_t tag[AES_GCM_TAG_LEN];
+ uint64_t iv_bit_length = AES_GCM_IV_LEN * 8;
+ uint64_t plaintext_bit_length = param->cipher_range.length * 8;
+ uint64_t aad_bit_length = session->p.auth_aad_len * 8;
+ uint32_t in_pos = param->cipher_range.offset;
+ uint32_t in_len = param->cipher_range.length;
+ odp_bool_t continuous_data;
+ uint16_t saved_tail[OOB_WRITE_LEN];
+ int rc;
+
+ /* Fail early if cipher_range is too large */
+ if (odp_unlikely(in_len > ARM_CRYPTO_MAX_DATA_LENGTH)) {
+ _ODP_DBG("ARM Crypto: Packet size too large for requested operation\n");
+ goto err;
+ }
+
+ /* The crypto lib may read 16 bytes. Copy to a big enough buffer */
+ _ODP_ASSERT(param->cipher_iv_ptr != NULL);
+ memcpy(iv_data, param->cipher_iv_ptr, AES_GCM_IV_LEN);
+
+ cs.constants = &session->cc;
+
+ rc = armv8_aes_gcm_set_counter(iv_data, iv_bit_length, &cs);
+ if (odp_unlikely(rc)) {
+ _ODP_DBG("ARM Crypto: Failure while setting nonce\n");
+ goto err;
+ }
+
+ /* Copy AAD in a stack to make sure that the ARM crypto library can
+ * read it in 16 byte chunks. */
+ uint8_t aad[ARM_CRYPTO_MAX_AAD_LENGTH];
+
+ copy_aad(aad, param->aad_ptr, session->p.auth_aad_len);
+
+ uint32_t seg_len = 0;
+ uint8_t *data = odp_packet_offset(pkt, in_pos, &seg_len, NULL);
+
+ if (odp_unlikely(odp_packet_is_segmented(pkt)) ||
+ odp_unlikely(odp_packet_tailroom(pkt) < OOB_WRITE_LEN)) {
+ /* Packet is segmented or it may not be safe to read and write
+ * beyond the end of packet data. Copy the cipher range to a
+ * contiguous buffer. */
+ odp_packet_copy_to_mem(pkt, in_pos, in_len, local.buffer);
+ data = local.buffer;
+ /* Copy tag from the packet to a buffer */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ AES_GCM_TAG_LEN, tag);
+ continuous_data = false;
+ } else {
+ /* Save data that might get overwritten */
+ memcpy(saved_tail, data + in_len, OOB_WRITE_LEN);
+ /* Copy tag from the packet to a buffer */
+ memcpy(tag, data - in_pos + param->hash_result_offset, AES_GCM_TAG_LEN);
+ continuous_data = true;
+ }
+
+ rc = armv8_dec_aes_gcm_from_state(&cs,
+ aad, aad_bit_length,
+ data, plaintext_bit_length,
+ tag,
+ data);
+ if (odp_unlikely(rc)) {
+ _ODP_DBG("ARM Crypto: AES GCM Decoding failed\n");
+ goto err;
+ }
+
+ if (odp_likely(continuous_data))
+ memcpy(data + in_len, saved_tail, OOB_WRITE_LEN);
+ else
+ odp_packet_copy_from_mem(pkt, in_pos, in_len, data);
+
+ set_crypto_op_result_ok(pkt);
+ return;
+
+err:
+ set_crypto_op_result(pkt,
+ ODP_CRYPTO_ALG_ERR_NONE,
+ ODP_CRYPTO_ALG_ERR_ICV_CHECK);
+}
+
+static int process_aes_gcm_param(odp_crypto_generic_session_t *session)
+{
+ /* Verify Key len is valid */
+ if (16 != session->p.cipher_key.length &&
+ 24 != session->p.cipher_key.length &&
+ 32 != session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if (session->p.cipher_iv_len != AES_GCM_IV_LEN)
+ return -1;
+
+ if (ARM_CRYPTO_MAX_CIPHER_KEY_LENGTH < session->p.cipher_key.length)
+ return -1;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->func = aes_gcm_encrypt;
+ else
+ session->func = aes_gcm_decrypt;
+
+ return 0;
+}
+
+int odp_crypto_capability(odp_crypto_capability_t *capa)
+{
+ if (NULL == capa)
+ return -1;
+
+ /* Initialize crypto capability structure */
+ memset(capa, 0, sizeof(odp_crypto_capability_t));
+
+ capa->sync_mode = ODP_SUPPORT_PREFERRED;
+ capa->async_mode = ODP_SUPPORT_YES;
+ capa->queue_type_plain = 1;
+ capa->queue_type_sched = 1;
+
+ capa->ciphers.bit.null = 1;
+ capa->auths.bit.null = 1;
+
+#ifdef __ARM_FEATURE_AES
+ capa->ciphers.bit.aes_gcm = 1;
+ capa->auths.bit.aes_gcm = 1;
+#endif
+
+ capa->max_sessions = MAX_SESSIONS;
+
+ return 0;
+}
+
+int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
+ odp_crypto_cipher_capability_t dst[],
+ int num_copy)
+{
+ const odp_crypto_cipher_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_cipher_capability_t);
+
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ src = cipher_capa_null;
+ num = sizeof(cipher_capa_null) / size;
+ break;
+#ifdef __ARM_FEATURE_AES
+ case ODP_CIPHER_ALG_AES_GCM:
+ src = cipher_capa_aes_gcm;
+ num = sizeof(cipher_capa_aes_gcm) / size;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int odp_crypto_auth_capability(odp_auth_alg_t auth,
+ odp_crypto_auth_capability_t dst[], int num_copy)
+{
+ const odp_crypto_auth_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_auth_capability_t);
+
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ src = auth_capa_null;
+ num = sizeof(auth_capa_null) / size;
+ break;
+#ifdef __ARM_FEATURE_AES
+ case ODP_AUTH_ALG_AES_GCM:
+ src = auth_capa_aes_gcm;
+ num = sizeof(auth_capa_aes_gcm) / size;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int
+odp_crypto_session_create(const odp_crypto_session_param_t *param,
+ odp_crypto_session_t *session_out,
+ odp_crypto_ses_create_err_t *status)
+{
+ int rc = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ /* Dummy output to avoid compiler warning about uninitialized
+ * variables */
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ if (param->cipher_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->auth_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP ||
+ param->op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ *status = ODP_CRYPTO_SES_ERR_PARAMS;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ /* Allocate memory for this session */
+ session = alloc_session();
+ if (NULL == session) {
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ goto err;
+ }
+
+ /* Copy parameters */
+ session->p = *param;
+
+ if (session->p.cipher_iv_len > ARM_CRYPTO_MAX_IV_LENGTH) {
+ _ODP_DBG("Maximum IV length exceeded\n");
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ if (session->p.auth_iv_len > ARM_CRYPTO_MAX_IV_LENGTH) {
+ _ODP_DBG("Maximum auth IV length exceeded\n");
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Process based on cipher */
+ switch (param->cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ session->func = null_crypto_routine;
+ rc = 0;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ {
+ /* Set cipher mode for AES-GCM */
+ armv8_cipher_mode_t mode = 0;
+
+ switch (param->cipher_key.length) {
+ case 16:
+ mode = AES_GCM_128;
+ break;
+ case 24:
+ mode = AES_GCM_192;
+ break;
+ case 32:
+ mode = AES_GCM_256;
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ /* AES-GCM requires to do both auth and
+ * cipher at the same time */
+ if (param->auth_alg != ODP_AUTH_ALG_AES_GCM) {
+ rc = -1;
+ } else if (mode == AES_GCM_128 || mode == AES_GCM_192 ||
+ mode == AES_GCM_256) {
+ if (armv8_aes_gcm_set_constants(mode,
+ session->p.auth_digest_len,
+ session->p.cipher_key.data,
+ &session->cc) != 0) {
+ _ODP_DBG("ARM Crypto: Failure in setting constants\n");
+ rc = -1;
+ }
+ rc = process_aes_gcm_param(session);
+ } else {
+ rc = -1;
+ }
+ break;
+ }
+ default:
+ rc = -1;
+ }
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Process based on auth */
+ switch (param->auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ if (param->cipher_alg == ODP_CIPHER_ALG_NULL)
+ rc = 0;
+ else
+ rc = -1;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ /* AES-GCM requires to do both auth and
+ * cipher at the same time */
+ if (param->cipher_alg == ODP_CIPHER_ALG_AES_GCM) {
+ rc = 0;
+ } else {
+ rc = -1;
+ }
+ break;
+ default:
+ rc = -1;
+ }
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ goto err;
+ }
+
+ /* We're happy */
+ *session_out = (intptr_t)session;
+ *status = ODP_CRYPTO_SES_ERR_NONE;
+ return 0;
+
+err:
+ /* error status should be set at this moment */
+ if (session != NULL)
+ free_session(session);
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+}
+
+int odp_crypto_session_destroy(odp_crypto_session_t session)
+{
+ odp_crypto_generic_session_t *generic;
+
+ generic = (odp_crypto_generic_session_t *)(intptr_t)session;
+ memset(generic, 0, sizeof(*generic));
+ free_session(generic);
+ return 0;
+}
+
+int _odp_crypto_init_global(void)
+{
+ size_t mem_size;
+ odp_shm_t shm;
+ int idx;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_PRINT("\nODP crypto is DISABLED\n");
+ return 0;
+ }
+
+ /* Calculate the memory size we need */
+ mem_size = sizeof(odp_crypto_global_t);
+
+ /* Allocate our globally shared memory */
+ shm = odp_shm_reserve("_odp_crypto_pool_armv8crypto", mem_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (ODP_SHM_INVALID == shm) {
+ _ODP_ERR("unable to allocate crypto pool\n");
+ return -1;
+ }
+
+ global = odp_shm_addr(shm);
+
+ /* Clear it out */
+ memset(global, 0, mem_size);
+
+ /* Initialize free list and lock */
+ for (idx = 0; idx < MAX_SESSIONS; idx++) {
+ global->sessions[idx].next = global->free;
+ global->free = &global->sessions[idx];
+ }
+ odp_spinlock_init(&global->lock);
+
+ return 0;
+}
+
+int _odp_crypto_term_global(void)
+{
+ int rc = 0;
+ int ret;
+ int count = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (session = global->free; session != NULL; session = session->next)
+ count++;
+ if (count != MAX_SESSIONS) {
+ _ODP_ERR("crypto sessions still active\n");
+ rc = -1;
+ }
+
+ ret = odp_shm_free(odp_shm_lookup("_odp_crypto_pool_armv8crypto"));
+ if (ret < 0) {
+ _ODP_ERR("shm free failed for _odp_crypto_pool_armv8crypto\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int _odp_crypto_init_local(void)
+{
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ memset(&local, 0, sizeof(local));
+
+ return 0;
+}
+
+int _odp_crypto_term_local(void)
+{
+ return 0;
+}
+
+void odp_crypto_session_param_init(odp_crypto_session_param_t *param)
+{
+ memset(param, 0, sizeof(odp_crypto_session_param_t));
+}
+
+uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl)
+{
+ return (uint64_t)hdl;
+}
+
+static int copy_data_and_metadata(odp_packet_t dst, odp_packet_t src)
+{
+ int md_copy;
+ int rc;
+
+ md_copy = _odp_packet_copy_md_possible(odp_packet_pool(dst),
+ odp_packet_pool(src));
+ if (odp_unlikely(md_copy < 0)) {
+ _ODP_ERR("Unable to copy packet metadata\n");
+ return -1;
+ }
+
+ rc = odp_packet_copy_from_pkt(dst, 0, src, 0, odp_packet_len(src));
+ if (odp_unlikely(rc < 0)) {
+ _ODP_ERR("Unable to copy packet data\n");
+ return -1;
+ }
+
+ _odp_packet_copy_md(packet_hdr(dst), packet_hdr(src), md_copy);
+ return 0;
+}
+
+static odp_packet_t get_output_packet(const odp_crypto_generic_session_t *session,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out)
+{
+ int rc;
+
+ if (odp_likely(pkt_in == pkt_out))
+ return pkt_out;
+
+ if (pkt_out == ODP_PACKET_INVALID) {
+ odp_pool_t pool = session->p.output_pool;
+
+ _ODP_ASSERT(pool != ODP_POOL_INVALID);
+ if (pool == odp_packet_pool(pkt_in)) {
+ pkt_out = pkt_in;
+ } else {
+ pkt_out = odp_packet_copy(pkt_in, pool);
+ if (odp_likely(pkt_out != ODP_PACKET_INVALID))
+ odp_packet_free(pkt_in);
+ }
+ return pkt_out;
+ }
+ rc = copy_data_and_metadata(pkt_out, pkt_in);
+ if (odp_unlikely(rc < 0))
+ return ODP_PACKET_INVALID;
+
+ odp_packet_free(pkt_in);
+ return pkt_out;
+}
+
+static
+int crypto_int(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_generic_session_t *session;
+ odp_packet_t out_pkt;
+
+ session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ out_pkt = pkt_in;
+ } else {
+ out_pkt = get_output_packet(session, pkt_in, *pkt_out);
+ if (odp_unlikely(out_pkt == ODP_PACKET_INVALID))
+ return -1;
+ }
+
+ if (odp_unlikely(session->p.null_crypto_enable &&
+ param->null_crypto))
+ goto out;
+
+ /* Invoke the crypto function */
+ session->func(out_pkt, param, session);
+
+out:
+ packet_subtype_set(out_pkt, ODP_EVENT_PACKET_CRYPTO);
+
+ /* Synchronous, simply return results */
+ *pkt_out = out_pkt;
+
+ return 0;
+}
+
+int odp_crypto_op(const odp_packet_t pkt_in[],
+ odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ int i, rc;
+ odp_crypto_generic_session_t *session;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_SYNC == session->p.op_mode);
+
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ if (rc < 0)
+ break;
+ }
+
+ return i;
+}
+
+int odp_crypto_op_enq(const odp_packet_t pkt_in[],
+ const odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ odp_packet_t pkt;
+ odp_event_t event;
+ odp_crypto_generic_session_t *session;
+ int i, rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_ASYNC == session->p.op_mode);
+ _ODP_ASSERT(ODP_QUEUE_INVALID != session->p.compl_queue);
+
+ if (session->p.op_type != ODP_CRYPTO_OP_TYPE_BASIC)
+ pkt = pkt_out[i];
+
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ if (rc < 0)
+ break;
+
+ event = odp_packet_to_event(pkt);
+ if (odp_queue_enq(session->p.compl_queue, event)) {
+ odp_event_free(event);
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/platform/linux-generic/arch/aarch64/odp_random.h b/platform/linux-generic/arch/aarch64/odp_random.h
new file mode 100644
index 000000000..1f6187c31
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_random.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#ifndef ODP_AARCH64_RANDOM_H_
+#define ODP_AARCH64_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/random.h>
+#include <odp/autoheader_internal.h>
+
+#include <stdint.h>
+
+odp_random_kind_t _odp_random_max_kind_generic(void);
+int32_t _odp_random_true_data_generic(uint8_t *buf, uint32_t len);
+int32_t _odp_random_crypto_data_generic(uint8_t *buf, uint32_t len);
+
+#ifdef __ARM_FEATURE_RNG
+
+#if __ARM_FEATURE_UNALIGNED != 1
+#error This implementation requires unaligned access
+#endif
+
+static inline int _odp_random_max_kind(void)
+{
+ return ODP_RANDOM_TRUE;
+}
+
+static inline int _odp_rndr(uint64_t *v)
+{
+ int pass;
+
+ /* Return a 64-bit random number which is reseeded from the True Random
+ * Number source. If the hardware returns a genuine random number,
+ * PSTATE.NZCV is set to 0b0000. The NZCV condition flag is checked via
+ * the CSET instruction. If the hardware cannot return a genuine random
+ * number in a reasonable period of time, PSTATE.NZCV is set to 0b0100
+ * and the data value returned is 0. */
+ __asm__ volatile("mrs %0, s3_3_c2_c4_0\n\t"
+ "cset %w[pass], ne"
+ : "=r" (*v), [pass] "=r" (pass)
+ :
+ : "cc");
+
+ return pass;
+}
+
+static inline int _odp_rndrrs(uint64_t *v)
+{
+ int pass;
+
+ /* Return a 64-bit random number which is reseeded from the True Random
+ * Number source immediately before the read of the random number.
+ * If the hardware returns a genuine random number, PSTATE.NZCV is
+ * set to 0b0000. The NZCV condition flag is checked via the CSET
+ * instruction. If the hardware cannot return a genuine random number
+ * in a reasonable period of time, PSTATE.NZCV is set to 0b0100 and the
+ * data value returned is 0. */
+ __asm__ volatile("mrs %0, s3_3_c2_c4_1\n\t"
+ "cset %w[pass], ne"
+ : "=r" (*v), [pass] "=r" (pass)
+ :
+ : "cc");
+
+ return pass;
+}
+
+static inline int32_t _odp_random_crypto_data(uint8_t *buf, uint32_t len)
+{
+ uint64_t temp;
+
+ for (uint32_t i = 0; i < len / 8; i++) {
+ while (!_odp_rndr(&temp))
+ ;
+
+ *(uint64_t *)(uintptr_t)buf = temp;
+ buf += 8;
+ }
+
+ if (len & 7) {
+ while (!_odp_rndr(&temp))
+ ;
+
+ if (len & 4) {
+ *(uint32_t *)(uintptr_t)buf = temp & 0xffffffff;
+ temp >>= 32;
+ buf += 4;
+ }
+
+ if (len & 2) {
+ *(uint16_t *)(uintptr_t)buf = temp & 0xffff;
+ temp >>= 16;
+ buf += 2;
+ }
+
+ if (len & 1)
+ *buf = temp & 0xff;
+ }
+
+ return len;
+}
+
+static inline int32_t _odp_random_true_data(uint8_t *buf, uint32_t len)
+{
+ uint64_t temp;
+
+ for (uint32_t i = 0; i < len / 8; i++) {
+ while (!_odp_rndrrs(&temp))
+ ;
+
+ *(uint64_t *)(uintptr_t)buf = temp;
+ buf += 8;
+ }
+
+ if (len & 7) {
+ while (!_odp_rndrrs(&temp))
+ ;
+
+ if (len & 4) {
+ *(uint32_t *)(uintptr_t)buf = temp & 0xffffffff;
+ temp >>= 32;
+ buf += 4;
+ }
+
+ if (len & 2) {
+ *(uint16_t *)(uintptr_t)buf = temp & 0xffff;
+ temp >>= 16;
+ buf += 2;
+ }
+
+ if (len & 1)
+ *buf = temp & 0xff;
+ }
+
+ return len;
+}
+
+#else
+
+static inline int _odp_random_max_kind(void)
+{
+ return _odp_random_max_kind_generic();
+}
+
+static inline int32_t _odp_random_crypto_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_crypto_data_generic(buf, len);
+}
+
+static inline int32_t _odp_random_true_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_true_data_generic(buf, len);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/aarch64/odp_sysinfo_parse.c b/platform/linux-generic/arch/aarch64/odp_sysinfo_parse.c
new file mode 100644
index 000000000..b1bbb0305
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_sysinfo_parse.c
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2020-2021 Nokia
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <odp/api/hints.h>
+#include <odp_global_data.h>
+#include <odp_sysinfo_internal.h>
+#include <odp_debug_internal.h>
+#include "cpu_flags.h"
+
+#define TMP_STR_LEN 64
+
+static void aarch64_impl_str(char *str, int maxlen, int implementer)
+{
+ switch (implementer) {
+ case 0x41:
+ snprintf(str, maxlen, "ARM Limited");
+ return;
+ case 0x42:
+ snprintf(str, maxlen, "Broadcom Corporation");
+ return;
+ case 0x43:
+ snprintf(str, maxlen, "Marvell (Cavium) Inc.");
+ return;
+ case 0x44:
+ snprintf(str, maxlen, "Digital Equipment Corporation");
+ return;
+ case 0x46:
+ snprintf(str, maxlen, "Fujitsu Ltd.");
+ return;
+ case 0x49:
+ snprintf(str, maxlen, "Infineon Technologies AG");
+ return;
+ case 0x4d:
+ snprintf(str, maxlen, "Freescale Semiconductor Inc.");
+ return;
+ case 0x4e:
+ snprintf(str, maxlen, "NVIDIA Corporation");
+ return;
+ case 0x50:
+ snprintf(str, maxlen, "Applied Micro Circuits Corporation");
+ return;
+ case 0x51:
+ snprintf(str, maxlen, "Qualcomm Inc.");
+ return;
+ case 0x56:
+ snprintf(str, maxlen, "Marvell International Ltd.");
+ return;
+ case 0x69:
+ snprintf(str, maxlen, "Intel Corporation");
+ return;
+ case 0xc0:
+ snprintf(str, maxlen, "Ampere Computing");
+ return;
+ default:
+ break;
+ }
+
+ snprintf(str, maxlen, "UNKNOWN (0x%x)", implementer);
+}
+
+static void aarch64_part_info(char *str, int maxlen, odp_cpu_arch_arm_t *cpu_isa, int implementer,
+ int part, int variant, int revision)
+{
+ *cpu_isa = ODP_CPU_ARCH_ARM_UNKNOWN;
+
+ if (implementer == 0x41) {
+ /* Part numbers are specified in Main ID Register (MIDR_EL1) documentation */
+ switch (part) {
+ case 0xd02:
+ snprintf(str, maxlen, "Cortex-A34");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd04:
+ snprintf(str, maxlen, "Cortex-A35");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd03:
+ snprintf(str, maxlen, "Cortex-A53");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd05:
+ snprintf(str, maxlen, "Cortex-A55");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd07:
+ snprintf(str, maxlen, "Cortex-A57");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd06:
+ snprintf(str, maxlen, "Cortex-A65");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd08:
+ snprintf(str, maxlen, "Cortex-A72");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd09:
+ snprintf(str, maxlen, "Cortex-A73");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_0;
+ return;
+ case 0xd0a:
+ snprintf(str, maxlen, "Cortex-A75");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd0b:
+ snprintf(str, maxlen, "Cortex-A76");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd0c:
+ snprintf(str, maxlen, "Neoverse N1");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd0e:
+ snprintf(str, maxlen, "Cortex-A76AE");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd0d:
+ snprintf(str, maxlen, "Cortex-A77");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd40:
+ snprintf(str, maxlen, "Neoverse V1");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_4;
+ return;
+ case 0xd41:
+ snprintf(str, maxlen, "Cortex-A78");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd42:
+ snprintf(str, maxlen, "Cortex-A78AE");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd44:
+ snprintf(str, maxlen, "Cortex-X1");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd46:
+ snprintf(str, maxlen, "Cortex-A510");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_0;
+ return;
+ case 0xd47:
+ snprintf(str, maxlen, "Cortex-A710");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_0;
+ return;
+ case 0xd48:
+ snprintf(str, maxlen, "Cortex-X2");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_0;
+ return;
+ case 0xd49:
+ snprintf(str, maxlen, "Neoverse N2");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_0;
+ return;
+ case 0xd4a:
+ snprintf(str, maxlen, "Neoverse E1");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd4b:
+ snprintf(str, maxlen, "Cortex-A78C");
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xd4d:
+ snprintf(str, maxlen, "Cortex-A715");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_0;
+ return;
+ case 0xd80:
+ snprintf(str, maxlen, "Cortex-A520");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_2;
+ return;
+ case 0xd81:
+ snprintf(str, maxlen, "Cortex-A720");
+ *cpu_isa = ODP_CPU_ARCH_ARMV9_2;
+ return;
+ default:
+ break;
+ }
+ } else if (implementer == 0x43) {
+ switch (part) {
+ case 0xa1:
+ snprintf(str, maxlen, "CN88XX, Pass %i.%i", variant + 1, revision);
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_1;
+ return;
+ case 0xa2:
+ snprintf(str, maxlen, "CN81XX, Pass %i.%i", variant + 1, revision);
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_1;
+ return;
+ case 0xa3:
+ snprintf(str, maxlen, "CN83XX, Pass %i.%i", variant + 1, revision);
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_1;
+ return;
+ case 0xaf:
+ snprintf(str, maxlen, "CN99XX, Rev %c%i", 'A' + variant, revision);
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_1;
+ return;
+ case 0xb1:
+ snprintf(str, maxlen, "CN98XX, Rev %c%i", 'A' + variant, revision);
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ case 0xb2:
+ /* Handle B0 errata: variant and revision numbers show up as A1 */
+ if (variant == 0 && revision == 1)
+ snprintf(str, maxlen, "CN96XX, Rev B0");
+ else
+ snprintf(str, maxlen, "CN96XX, Rev %c%i", 'A' + variant, revision);
+
+ *cpu_isa = ODP_CPU_ARCH_ARMV8_2;
+ return;
+ default:
+ break;
+ }
+ }
+
+ snprintf(str, maxlen, "part 0x%x, var 0x%x, rev 0x%x",
+ part, variant, revision);
+}
+
+static odp_cpu_arch_arm_t arm_isa_version(void)
+{
+#if defined(__ARM_ARCH)
+ if (__ARM_ARCH == 8) {
+ #ifdef __ARM_FEATURE_QRDMX
+ /* v8.1 or higher */
+ return ODP_CPU_ARCH_ARMV8_1;
+ #else
+ return ODP_CPU_ARCH_ARMV8_0;
+ #endif
+ }
+
+ if (__ARM_ARCH == 9) {
+ /* v9.0 or higher */
+ return ODP_CPU_ARCH_ARMV9_0;
+ }
+
+ if (__ARM_ARCH >= 800) {
+ /* ACLE 2018 defines that from v8.1 onwards the value includes
+ * the minor version number: __ARM_ARCH = X * 100 + Y
+ * E.g. for Armv8.1 __ARM_ARCH = 801 */
+ int major = __ARM_ARCH / 100;
+ int minor = __ARM_ARCH - (major * 100);
+
+ if (major == 8) {
+ switch (minor) {
+ case 0:
+ return ODP_CPU_ARCH_ARMV8_0;
+ case 1:
+ return ODP_CPU_ARCH_ARMV8_1;
+ case 2:
+ return ODP_CPU_ARCH_ARMV8_2;
+ case 3:
+ return ODP_CPU_ARCH_ARMV8_3;
+ case 4:
+ return ODP_CPU_ARCH_ARMV8_4;
+ case 5:
+ return ODP_CPU_ARCH_ARMV8_5;
+ case 6:
+ return ODP_CPU_ARCH_ARMV8_6;
+ case 7:
+ return ODP_CPU_ARCH_ARMV8_7;
+ case 8:
+ return ODP_CPU_ARCH_ARMV8_8;
+ case 9:
+ return ODP_CPU_ARCH_ARMV8_9;
+ default:
+ return ODP_CPU_ARCH_ARM_UNKNOWN;
+ }
+ } else if (major == 9) {
+ switch (minor) {
+ case 0:
+ return ODP_CPU_ARCH_ARMV9_0;
+ case 1:
+ return ODP_CPU_ARCH_ARMV9_1;
+ case 2:
+ return ODP_CPU_ARCH_ARMV9_2;
+ case 3:
+ return ODP_CPU_ARCH_ARMV9_3;
+ default:
+ return ODP_CPU_ARCH_ARM_UNKNOWN;
+ }
+ }
+ }
+#endif
+ return ODP_CPU_ARCH_ARM_UNKNOWN;
+}
+
+int _odp_cpuinfo_parser(FILE *file, system_info_t *sysinfo)
+{
+ char str[1024];
+ char impl_str[TMP_STR_LEN];
+ char part_str[TMP_STR_LEN];
+ const char *cur;
+ long int impl, arch, var, part, rev;
+ int id;
+
+ sysinfo->cpu_arch = ODP_CPU_ARCH_ARM;
+ sysinfo->cpu_isa_sw.arm = arm_isa_version();
+ /* Linux cpuinfo does not have detailed ISA version number (CPU architecture: 8) */
+ sysinfo->cpu_isa_hw.arm = ODP_CPU_ARCH_ARM_UNKNOWN;
+
+ strcpy(sysinfo->cpu_arch_str, "aarch64");
+
+ memset(impl_str, 0, sizeof(impl_str));
+ memset(part_str, 0, sizeof(part_str));
+
+ impl = 0;
+ arch = 0;
+ var = 0;
+ part = 0;
+ rev = 0;
+ id = 0;
+
+ while (fgets(str, sizeof(str), file) != NULL && id < CONFIG_NUM_CPU_IDS) {
+ /* Parse line by line a block of cpuinfo */
+ cur = strstr(str, "CPU implementer");
+
+ if (cur) {
+ cur = strchr(cur, ':');
+ impl = strtol(cur + 1, NULL, 16);
+ aarch64_impl_str(impl_str, TMP_STR_LEN, impl);
+ continue;
+ }
+
+ cur = strstr(str, "CPU architecture");
+
+ if (cur) {
+ cur = strchr(cur, ':');
+ arch = strtol(cur + 1, NULL, 10);
+ continue;
+ }
+
+ cur = strstr(str, "CPU variant");
+
+ if (cur) {
+ cur = strchr(cur, ':');
+ var = strtol(cur + 1, NULL, 16);
+ continue;
+ }
+
+ cur = strstr(str, "CPU part");
+
+ if (cur) {
+ cur = strchr(cur, ':');
+ part = strtol(cur + 1, NULL, 16);
+ continue;
+ }
+
+ cur = strstr(str, "CPU revision");
+
+ if (cur) {
+ odp_cpu_arch_arm_t cpu_isa;
+
+ cur = strchr(cur, ':');
+ rev = strtol(cur + 1, NULL, 10);
+
+ aarch64_part_info(part_str, TMP_STR_LEN, &cpu_isa, impl, part, var, rev);
+ sysinfo->cpu_isa_hw.arm = cpu_isa;
+
+ /* This is the last line about this cpu, update
+ * model string. */
+ snprintf(sysinfo->model_str[id],
+ sizeof(sysinfo->model_str[id]),
+ "%s, %s, arch %li",
+ impl_str, part_str, arch);
+
+ /* Some CPUs do not support cpufreq, use a dummy
+ * max freq. */
+ if (sysinfo->cpu_hz_max[id] == 0) {
+ uint64_t hz = sysinfo->default_cpu_hz_max;
+
+ _ODP_WARN("CPU[%i] uses default max frequency of %" PRIu64 " "
+ "Hz from config file\n", id, hz);
+ sysinfo->cpu_hz_max[id] = hz;
+ }
+
+ id++;
+ }
+ }
+
+ return 0;
+}
+
+void _odp_sys_info_print_arch(void)
+{
+ _odp_cpu_flags_print_all();
+}
+
+uint64_t odp_cpu_arch_hz_current(int id ODP_UNUSED)
+{
+ return odp_global_ro.system_info.default_cpu_hz;
+}
diff --git a/platform/linux-generic/arch/aarch64/odp_wait_until.h b/platform/linux-generic/arch/aarch64/odp_wait_until.h
new file mode 100644
index 000000000..eca3f9ce5
--- /dev/null
+++ b/platform/linux-generic/arch/aarch64/odp_wait_until.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2024 Nokia
+ */
+
+#ifndef ODP_AARCH64_WAIT_UNTIL_H_
+#define ODP_AARCH64_WAIT_UNTIL_H_
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+#error This file should not be included directly, please include odp_cpu.h
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/cpu.h>
+
+#include <odp_cpu.h>
+
+#include <stdint.h>
+
+static inline void _odp_sevl(void)
+{
+ __asm__ volatile("sevl" : : : );
+}
+
+static inline int _odp_wfe(void)
+{
+ __asm__ volatile("wfe" : : : "memory");
+ return 1;
+}
+
+#define _odp_monitor_u8(addr, mo) ll8((addr), (mo))
+#define _odp_monitor_u32(addr, mo) ll32((addr), (mo))
+#define _odp_monitor_u64(addr, mo) ll64((addr), (mo))
+#define _odp_monitor_u128(addr, mo) lld((addr), (mo))
+
+#if ATOM_BITSET_SIZE <= 32
+static inline bitset_t _odp_bitset_monitor(bitset_t *bs, int mo)
+{
+ return _odp_monitor_u32(bs, mo);
+}
+#elif ATOM_BITSET_SIZE <= 64
+static inline bitset_t _odp_bitset_monitor(bitset_t *bs, int mo)
+{
+ return _odp_monitor_u64(bs, mo);
+}
+#elif ATOM_BITSET_SIZE <= 128
+static inline bitset_t _odp_bitset_monitor(bitset_t *bs, int mo)
+{
+ return _odp_monitor_u128(bs, mo);
+}
+#else
+#error Unsupported size of bit sets (ATOM_BITSET_SIZE)
+#endif
+
+/**
+ * The _odp_wait_until_eq_*() functions defined in this header are intended to
+ * be used only with the scalable scheduler and queue implementations. Even
+ * though these functions use standard non-atomic parameter types, the
+ * parameters must only be operated using atomic operations. If new functions
+ * are added to this file, they should use _odp_wait_until_equal_*() prefix and
+ * atomic parameter types.
+ */
+
+static inline void _odp_wait_until_eq_u32(uint32_t *val, uint32_t expected)
+{
+ _odp_sevl();
+ while (_odp_wfe() && _odp_monitor_u32(val, __ATOMIC_RELAXED) != expected)
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_bitset(bitset_t *val, bitset_t expected)
+{
+ _odp_sevl();
+ while (_odp_wfe() && _odp_bitset_monitor(val, __ATOMIC_RELAXED != expected))
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_acq_u8(uint8_t *val, uint8_t expected)
+{
+ _odp_sevl();
+ while (_odp_wfe() && _odp_monitor_u8(val, __ATOMIC_ACQUIRE) != expected)
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_acq_u32(uint32_t *val, uint32_t expected)
+{
+ _odp_sevl();
+ while (_odp_wfe() && _odp_monitor_u32(val, __ATOMIC_ACQUIRE) != expected)
+ odp_cpu_pause();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/arm/odp/api/abi/cpu.h b/platform/linux-generic/arch/arm/odp/api/abi/cpu.h
new file mode 100644
index 000000000..ad93cc816
--- /dev/null
+++ b/platform/linux-generic/arch/arm/odp/api/abi/cpu.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/cpu_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/arm/odp/api/abi/cpu_inlines.h b/platform/linux-generic/arch/arm/odp/api/abi/cpu_inlines.h
new file mode 100644
index 000000000..298953d95
--- /dev/null
+++ b/platform/linux-generic/arch/arm/odp/api/abi/cpu_inlines.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ARCH_CPU_INLINES_H_
+#define ODP_ARCH_CPU_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void _odp_cpu_pause(void)
+{
+ /* YIELD hints the CPU to switch to another thread if possible
+ * and executes as a NOP otherwise.
+ * ISB flushes the pipeline, then restarts. This is guaranteed to
+ * stall the CPU a number of cycles.
+ */
+ __asm volatile("isb" ::: "memory");
+}
+
+/* Use generic implementations for the rest of the functions */
+#include <odp/api/abi/cpu_generic.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h
deleted file mode 100644
index 7c75a690e..000000000
--- a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_CPU_ARCH_H_
-#define ODP_PLAT_CPU_ARCH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _ODP_CACHE_LINE_SIZE 64
-
-static inline void odp_cpu_pause(void)
-{
- /* YIELD hints the CPU to switch to another thread if possible
- * and executes as a NOP otherwise.
- * ISB flushes the pipeline, then restarts. This is guaranteed to
- * stall the CPU a number of cycles.
- */
- __asm volatile("isb" ::: "memory");
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/arch/arm/odp_cpu.h b/platform/linux-generic/arch/arm/odp_cpu.h
new file mode 100644
index 000000000..f8af54800
--- /dev/null
+++ b/platform/linux-generic/arch/arm/odp_cpu.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+#define PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H
+
+#if !defined(__arm__)
+#error Use this file only when compiling for ARM architecture
+#endif
+
+#include <odp_debug_internal.h>
+
+/*
+ * Use LLD/SCD atomic primitives instead of lock-based code path in llqueue
+ * LLD/SCD is on ARM the fastest way to enqueue and dequeue elements from a
+ * linked list queue.
+ */
+#define CONFIG_LLDSCD
+
+/*
+ * Use DMB;STR instead of STRL on ARM
+ * On early ARMv8 implementations (e.g. Cortex-A57) this is noticeably more
+ * performant than using store-release.
+ * This also allows for load-only barriers (DMB ISHLD) which are much cheaper
+ * than a full barrier
+ */
+#define CONFIG_DMBSTR
+
+static inline uint64_t lld(uint64_t *var, int mm)
+{
+ uint64_t old;
+
+ __asm__ volatile("ldrexd %0, %H0, [%1]"
+ : "=&r" (old)
+ : "r" (var)
+ : );
+ /* Barrier after an acquiring load */
+ if (mm == __ATOMIC_ACQUIRE)
+ __asm__ volatile("dmb" : : : "memory");
+ return old;
+}
+
+/* Return 0 on success, 1 on failure */
+static inline uint32_t scd(uint64_t *var, uint64_t neu, int mm)
+{
+ uint32_t ret;
+
+ /* Barrier before a releasing store */
+ if (mm == __ATOMIC_RELEASE)
+ __asm__ volatile("dmb" : : : "memory");
+ __asm__ volatile("strexd %0, %1, %H1, [%2]"
+ : "=&r" (ret)
+ : "r" (neu), "r" (var)
+ : );
+ return ret;
+}
+
+#ifdef CONFIG_DMBSTR
+
+#define atomic_store_release(loc, val, ro) \
+do { \
+ __atomic_thread_fence(__ATOMIC_RELEASE); \
+ __atomic_store_n(loc, val, __ATOMIC_RELAXED); \
+} while (0)
+
+#else
+
+#define atomic_store_release(loc, val, ro) \
+ __atomic_store_n(loc, val, __ATOMIC_RELEASE)
+
+#endif /* CONFIG_DMBSTR */
+
+#include "../default/odp_atomic.h"
+#include "../default/odp_wait_until.h"
+
+#ifdef __ARM_FEATURE_UNALIGNED
+#define _ODP_UNALIGNED 1
+#else
+#define _ODP_UNALIGNED 0
+#endif
+
+#endif /* PLATFORM_LINUXGENERIC_ARCH_ARM_ODP_CPU_H */
diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c b/platform/linux-generic/arch/arm/odp_cpu_arch.c
deleted file mode 100644
index 28ee508bd..000000000
--- a/platform/linux-generic/arch/arm/odp_cpu_arch.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-
-#include <stdlib.h>
-#include <time.h>
-
-#include <odp/api/cpu.h>
-#include <odp/api/hints.h>
-#include <odp/api/system_info.h>
-#include <odp_debug_internal.h>
-#include <odp_time_internal.h>
-
-#define GIGA 1000000000
-
-uint64_t odp_cpu_cycles(void)
-{
- struct timespec time;
- uint64_t sec, ns, hz, cycles;
- int ret;
-
- ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
-
- if (ret != 0)
- ODP_ABORT("clock_gettime failed\n");
-
- hz = odp_cpu_hz_max();
- sec = (uint64_t)time.tv_sec;
- ns = (uint64_t)time.tv_nsec;
-
- cycles = sec * hz;
- cycles += (ns * hz) / GIGA;
-
- return cycles;
-}
-
-uint64_t odp_cpu_cycles_max(void)
-{
- return UINT64_MAX;
-}
-
-uint64_t odp_cpu_cycles_resolution(void)
-{
- return 1;
-}
-
-int cpu_has_global_time(void)
-{
- uint64_t hz = cpu_global_time_freq();
-
- /*
- * The system counter portion of the architected timer must
- * provide a uniform view of system time to all processing
- * elements in the system. This should hold true even for
- * heterogeneous SoCs.
- *
- * Determine whether the system has 'global time' by checking
- * whether a read of the architected timer frequency sys reg
- * returns a sane value. Sane is considered to be within
- * 1MHz and 6GHz (1us and .1667ns period).
- */
- return hz >= 1000000 && hz <= 6000000000;
-}
-
-uint64_t cpu_global_time(void)
-{
-#ifdef __aarch64__
- uint64_t cntvct;
-
- /*
- * To be consistent with other architectures, do not issue a
- * serializing instruction, e.g. ISB, before reading this
- * sys reg.
- */
-
- /* Memory clobber to minimize optimization around load from sys reg. */
- __asm__ volatile("mrs %0, cntvct_el0" : "=r"(cntvct) : : "memory");
-
- return cntvct;
-#else
- return 0;
-#endif
-}
-
-uint64_t cpu_global_time_freq(void)
-{
-#ifdef __aarch64__
- uint64_t cntfrq;
-
- __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq) : : );
-
- return cntfrq;
-#else
- return 0;
-#endif
-}
diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c
index 8ae2022ac..57c17d8ee 100644
--- a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c
+++ b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c
@@ -1,31 +1,31 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
*/
-#include <odp_internal.h>
-#include <odp_debug_internal.h>
-#include <string.h>
+#include <odp_global_data.h>
+#include <odp_sysinfo_internal.h>
-int cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo)
+int _odp_cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo)
{
- int i;
+ sysinfo->cpu_arch = ODP_CPU_ARCH_ARM;
+ sysinfo->cpu_isa_sw.arm = ODP_CPU_ARCH_ARM_UNKNOWN;
+ sysinfo->cpu_isa_hw.arm = ODP_CPU_ARCH_ARM_UNKNOWN;
- ODP_DBG("Warning: use dummy values for freq and model string\n");
- for (i = 0; i < MAX_CPU_NUMBER; i++) {
- sysinfo->cpu_hz_max[i] = 1400000000;
- strcpy(sysinfo->model_str[i], "UNKNOWN");
- }
+#if defined(__ARM_ARCH)
+ if (__ARM_ARCH == 6)
+ sysinfo->cpu_isa_sw.arm = ODP_CPU_ARCH_ARMV6;
+ else if (__ARM_ARCH == 7)
+ sysinfo->cpu_isa_sw.arm = ODP_CPU_ARCH_ARMV7;
+#endif
- return 0;
+ return _odp_dummy_cpuinfo(sysinfo);
}
-uint64_t odp_cpu_hz_current(int id ODP_UNUSED)
+void _odp_sys_info_print_arch(void)
{
- return 0;
}
-void sys_info_print_arch(void)
+uint64_t odp_cpu_arch_hz_current(int id ODP_UNUSED)
{
+ return odp_global_ro.system_info.default_cpu_hz;
}
diff --git a/platform/linux-generic/arch/common/odp/api/abi/time_cpu_inlines.h b/platform/linux-generic/arch/common/odp/api/abi/time_cpu_inlines.h
new file mode 100644
index 000000000..3b0808072
--- /dev/null
+++ b/platform/linux-generic/arch/common/odp/api/abi/time_cpu_inlines.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#ifndef ODP_ARCH_TIME_CPU_INLINES_H_
+#define ODP_ARCH_TIME_CPU_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/time_types.h>
+
+#include <odp/api/abi/time_cpu.h>
+
+#include <stdint.h>
+
+#define _ODP_TIME_GIGA_HZ 1000000000ULL
+
+typedef struct _odp_time_global_t {
+ uint64_t freq_hz;
+ uint64_t start_time;
+ uint64_t start_time_ns;
+
+} _odp_time_global_t;
+
+extern _odp_time_global_t _odp_time_glob;
+
+static inline odp_time_t _odp_time_cur(void)
+{
+ odp_time_t time;
+
+ time.count = _odp_time_cpu_global();
+ return time;
+}
+
+static inline odp_time_t _odp_time_cur_strict(void)
+{
+ odp_time_t time;
+
+ time.count = _odp_time_cpu_global_strict();
+ return time;
+}
+
+static inline uint64_t _odp_time_to_ns(odp_time_t time)
+{
+ uint64_t nsec;
+ uint64_t freq_hz = _odp_time_glob.freq_hz;
+ uint64_t count = time.count;
+ uint64_t sec = 0;
+
+ if (count >= freq_hz) {
+ sec = count / freq_hz;
+ count = count - sec * freq_hz;
+ }
+
+ nsec = (_ODP_TIME_GIGA_HZ * count) / freq_hz;
+
+ return (sec * _ODP_TIME_GIGA_HZ) + nsec;
+}
+
+static inline odp_time_t _odp_time_from_ns(uint64_t ns)
+{
+ odp_time_t time;
+ uint64_t count;
+ uint64_t freq_hz = _odp_time_glob.freq_hz;
+ uint64_t sec = 0;
+
+ if (ns >= ODP_TIME_SEC_IN_NS) {
+ sec = ns / ODP_TIME_SEC_IN_NS;
+ ns = ns - sec * ODP_TIME_SEC_IN_NS;
+ }
+
+ count = sec * freq_hz;
+ count += (ns * freq_hz) / ODP_TIME_SEC_IN_NS;
+
+ time.count = count;
+
+ return time;
+}
+
+static inline uint64_t _odp_time_res(void)
+{
+ return _odp_time_glob.freq_hz;
+}
+
+static inline void _odp_time_startup(odp_time_startup_t *startup)
+{
+ startup->global.count = _odp_time_glob.start_time;
+ startup->global_ns = _odp_time_glob.start_time_ns;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/common/odp_time_cpu.c b/platform/linux-generic/arch/common/odp_time_cpu.c
new file mode 100644
index 000000000..6416f5575
--- /dev/null
+++ b/platform/linux-generic/arch/common/odp_time_cpu.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp/api/time_types.h>
+
+#include <odp/api/abi/time_cpu.h>
+#include <odp/api/abi/time_cpu_inlines.h>
+
+#include <odp_debug_internal.h>
+#include <odp_init_internal.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#define YEAR_IN_SEC (365 * 24 * 3600)
+
+#include <odp/visibility_begin.h>
+
+_odp_time_global_t _odp_time_glob;
+
+#include <odp/visibility_end.h>
+
+int _odp_time_init_global(void)
+{
+ uint64_t count, diff, years;
+ odp_time_t time;
+ _odp_time_global_t *global = &_odp_time_glob;
+
+ memset(global, 0, sizeof(_odp_time_global_t));
+
+ if (!_odp_time_cpu_global_freq_is_const())
+ return -1;
+
+ global->freq_hz = _odp_time_cpu_global_freq();
+ if (global->freq_hz == 0)
+ return -1;
+
+ _ODP_PRINT("HW time counter freq: %" PRIu64 " hz\n\n", global->freq_hz);
+
+ count = _odp_time_cpu_global();
+ time.count = count;
+ global->start_time = count;
+ global->start_time_ns = _odp_time_to_ns(time);
+
+ /* Make sure that counters will not wrap */
+ diff = UINT64_MAX - count;
+ years = (diff / global->freq_hz) / YEAR_IN_SEC;
+
+ if (years < 10) {
+ _ODP_ERR("Time counter would wrap in 10 years: %" PRIu64 "\n", count);
+ return -1;
+ }
+
+ diff = UINT64_MAX - global->start_time_ns;
+ years = (diff / ODP_TIME_SEC_IN_NS) / YEAR_IN_SEC;
+
+ if (years < 10) {
+ _ODP_ERR("Time in nsec would wrap in 10 years: %" PRIu64 "\n",
+ global->start_time_ns);
+ return -1;
+ }
+
+ return 0;
+}
+
+int _odp_time_term_global(void)
+{
+ return 0;
+}
diff --git a/platform/linux-generic/arch/default/odp/api/abi/atomic_generic.h b/platform/linux-generic/arch/default/odp/api/abi/atomic_generic.h
new file mode 100644
index 000000000..b2029777c
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/atomic_generic.h
@@ -0,0 +1,274 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#ifndef ODP_API_ABI_ATOMIC_GENERIC_H_
+#define ODP_API_ABI_ATOMIC_GENERIC_H_
+
+#include <odp/api/atomic.h>
+
+static inline void _odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_sub_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_inc_u32(odp_atomic_u32_t *atom)
+{
+ (void)__atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_dec_u32(odp_atomic_u32_t *atom)
+{
+ (void)__atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_max_u32(odp_atomic_u32_t *atom, uint32_t new_val)
+{
+ uint32_t old_val;
+
+ old_val = __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
+
+ while (new_val > old_val) {
+ if (__atomic_compare_exchange_n(&atom->v, &old_val, new_val, 0 /* strong */,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ break;
+ }
+}
+
+static inline void _odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t new_val)
+{
+ uint32_t old_val;
+
+ old_val = __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
+
+ while (new_val < old_val) {
+ if (__atomic_compare_exchange_n(&atom->v, &old_val, new_val, 0 /* strong */,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ break;
+ }
+}
+
+static inline void _odp_atomic_add_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELEASE);
+}
+
+static inline void _odp_atomic_sub_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELEASE);
+}
+
+static inline void _odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_sub_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_inc_u64(odp_atomic_u64_t *atom)
+{
+ (void)__atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_dec_u64(odp_atomic_u64_t *atom)
+{
+ (void)__atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+static inline void _odp_atomic_max_u64(odp_atomic_u64_t *atom, uint64_t new_val)
+{
+ uint64_t old_val;
+
+ old_val = __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
+
+ while (new_val > old_val) {
+ if (__atomic_compare_exchange_n(&atom->v, &old_val, new_val, 0 /* strong */,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ break;
+ }
+}
+
+static inline void _odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t new_val)
+{
+ uint64_t old_val;
+
+ old_val = __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
+
+ while (new_val < old_val) {
+ if (__atomic_compare_exchange_n(&atom->v, &old_val, new_val, 0 /* strong */,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ break;
+ }
+}
+
+#ifndef ODP_ATOMIC_U64_LOCK
+static inline void _odp_atomic_add_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELEASE);
+}
+
+static inline void _odp_atomic_sub_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELEASE);
+}
+#endif
+
+#ifdef __SIZEOF_INT128__
+
+static inline void _odp_atomic_init_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ atom->v = val;
+}
+
+static inline odp_u128_t _odp_atomic_load_u128(odp_atomic_u128_t *atom)
+{
+ union {
+ odp_u128_t val;
+ __int128_t i;
+ } u;
+
+ u.i = __atomic_load_n((__int128_t *)&atom->v, __ATOMIC_RELAXED);
+ return u.val;
+}
+
+static inline void _odp_atomic_store_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ __atomic_store_n((__int128_t *)&atom->v, *(__int128_t *)&val, __ATOMIC_RELAXED);
+}
+
+static inline int _odp_atomic_cas_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return __atomic_compare_exchange_n((__int128_t *)&atom->v, (__int128_t *)old_val,
+ *(__int128_t *)&new_val, 0 /* strong */,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED);
+}
+
+static inline int _odp_atomic_cas_acq_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return __atomic_compare_exchange_n((__int128_t *)&atom->v, (__int128_t *)old_val,
+ *(__int128_t *)&new_val, 0 /* strong */,
+ __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
+}
+
+static inline int _odp_atomic_cas_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return __atomic_compare_exchange_n((__int128_t *)&atom->v, (__int128_t *)old_val,
+ *(__int128_t *)&new_val, 0 /* strong */,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED);
+}
+
+static inline int _odp_atomic_cas_acq_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return __atomic_compare_exchange_n((__int128_t *)&atom->v, (__int128_t *)old_val,
+ *(__int128_t *)&new_val, 0 /* strong */,
+ __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
+}
+
+#else /* Lock-based implementation */
+
+/**
+ * @internal
+ * 128 bit store operation expression for the ATOMIC_OP macro
+ */
+#define ATOMIC_STORE_OP_128(new_val) \
+({ \
+ (_atom)->v = (new_val); \
+})
+
+/**
+ * @internal
+ * 128 bit CAS operation expression for the ATOMIC_OP macro
+ */
+#define ATOMIC_CAS_OP_128(ret_ptr, old_val, new_val) \
+__extension__ ({ \
+ int *_ret_ptr = ret_ptr; \
+ odp_u128_t *_cas_old = old_val; \
+ odp_u128_t _cas_new = new_val; \
+ if (((_atom)->v.u64[0] == (_cas_old)->u64[0]) && \
+ ((_atom)->v.u64[1] == (_cas_old)->u64[1])) { \
+ (_atom)->v = (_cas_new); \
+ *(_ret_ptr) = 1; \
+ } else { \
+ *(_ret_ptr) = 0; \
+ } \
+})
+
+/**
+ * @internal
+ * Helper macro for lock-based atomic operations on 128-bit integers
+ * @param[in,out] atom Pointer to the 128-bit atomic variable
+ * @param expr Expression used update the variable.
+ * @return The old value of the variable.
+ */
+#define ATOMIC_OP_128(atom, expr) \
+__extension__ ({ \
+ odp_u128_t _old_val; \
+ odp_atomic_u128_t *_atom = atom; \
+ /* Loop while lock is already taken, stop when lock becomes clear */ \
+ while (__atomic_test_and_set(&(_atom)->lock, __ATOMIC_ACQUIRE)) \
+ (void)0; \
+ _old_val = (_atom)->v; \
+ (expr); /* Perform whatever update is desired */ \
+ __atomic_clear(&(_atom)->lock, __ATOMIC_RELEASE); \
+ _old_val; /* Return old value */ \
+})
+
+static inline void _odp_atomic_init_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ atom->v.u64[0] = val.u64[0];
+ atom->v.u64[1] = val.u64[1];
+ atom->lock = 0;
+}
+
+static inline odp_u128_t _odp_atomic_load_u128(odp_atomic_u128_t *atom)
+{
+ return ATOMIC_OP_128(atom, (void)0);
+}
+
+static inline void _odp_atomic_store_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ ATOMIC_OP_128(atom, ATOMIC_STORE_OP_128(val));
+}
+
+static inline int _odp_atomic_cas_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ int ret;
+
+ *old_val = ATOMIC_OP_128(atom, ATOMIC_CAS_OP_128(&ret, old_val, new_val));
+ return ret;
+}
+
+static inline int _odp_atomic_cas_acq_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return _odp_atomic_cas_u128(atom, old_val, new_val);
+}
+
+static inline int _odp_atomic_cas_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return _odp_atomic_cas_u128(atom, old_val, new_val);
+}
+
+static inline int _odp_atomic_cas_acq_rel_u128(odp_atomic_u128_t *atom, odp_u128_t *old_val,
+ odp_u128_t new_val)
+{
+ return _odp_atomic_cas_u128(atom, old_val, new_val);
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/atomic_inlines.h b/platform/linux-generic/arch/default/odp/api/abi/atomic_inlines.h
new file mode 100644
index 000000000..8da9b73d1
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/atomic_inlines.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/abi/atomic_generic.h>
diff --git a/platform/linux-generic/arch/default/odp/api/abi/cpu.h b/platform/linux-generic/arch/default/odp/api/abi/cpu.h
new file mode 100644
index 000000000..402655dfc
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/cpu.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/cpu_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/cpu_generic.h b/platform/linux-generic/arch/default/odp/api/abi/cpu_generic.h
new file mode 100644
index 000000000..b2ba3972a
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/cpu_generic.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_CPU_GENERIC_H_
+#define ODP_API_ABI_CPU_GENERIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+uint64_t _odp_cpu_cycles(void);
+int _odp_cpu_cycles_init_global(void);
+
+static inline uint64_t _odp_cpu_cycles_max(void)
+{
+ return UINT64_MAX;
+}
+
+static inline uint64_t _odp_cpu_cycles_resolution(void)
+{
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/cpu_inlines.h b/platform/linux-generic/arch/default/odp/api/abi/cpu_inlines.h
new file mode 100644
index 000000000..6a35e93eb
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/cpu_inlines.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ARCH_CPU_INLINES_H_
+#define ODP_ARCH_CPU_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void _odp_cpu_pause(void)
+{
+}
+
+#include <odp/api/abi/cpu_generic.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/hash_crc32.h b/platform/linux-generic/arch/default/odp/api/abi/hash_crc32.h
new file mode 100644
index 000000000..07da45621
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/hash_crc32.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_HASH_CRC32_H_
+#define ODP_API_ABI_HASH_CRC32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+uint32_t _odp_hash_crc32_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+uint32_t _odp_hash_crc32c_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+
+static inline uint32_t _odp_hash_crc32(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32_generic(data, data_len, init_val);
+}
+
+static inline uint32_t _odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32c_generic(data, data_len, init_val);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/sync_inlines.h b/platform/linux-generic/arch/default/odp/api/abi/sync_inlines.h
new file mode 100644
index 000000000..bfbb3039f
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/sync_inlines.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ARCH_SYNC_INLINES_H_
+#define ODP_ARCH_SYNC_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void _odp_mb_sync(void)
+{
+ __sync_synchronize();
+}
+
+static inline void _odp_mb_sync_load(void)
+{
+ __sync_synchronize();
+}
+
+static inline void _odp_mb_sync_store(void)
+{
+ __sync_synchronize();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/time_inlines.h b/platform/linux-generic/arch/default/odp/api/abi/time_inlines.h
new file mode 100644
index 000000000..6e592d7a8
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/time_inlines.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#ifndef ODP_ARCH_TIME_INLINES_H_
+#define ODP_ARCH_TIME_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/time_types.h>
+
+#include <stdint.h>
+
+odp_time_t _odp_time_cur(void);
+uint64_t _odp_time_res(void);
+void _odp_time_startup(odp_time_startup_t *startup);
+
+static inline odp_time_t _odp_time_cur_strict(void)
+{
+ return _odp_time_cur();
+}
+
+static inline uint64_t _odp_time_to_ns(odp_time_t time)
+{
+ return time.nsec;
+}
+
+static inline odp_time_t _odp_time_from_ns(uint64_t ns)
+{
+ odp_time_t time;
+
+ time.nsec = ns;
+
+ return time;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/abi/wait_until.h b/platform/linux-generic/arch/default/odp/api/abi/wait_until.h
new file mode 100644
index 000000000..35e8d2566
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/wait_until.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 ARM Limited
+ */
+
+#include <odp/api/abi/wait_until_generic.h>
diff --git a/platform/linux-generic/arch/default/odp/api/abi/wait_until_generic.h b/platform/linux-generic/arch/default/odp/api/abi/wait_until_generic.h
new file mode 100644
index 000000000..3d3fce175
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp/api/abi/wait_until_generic.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 ARM Limited
+ */
+
+#ifndef ODP_API_ABI_WAIT_UNTIL_GENERIC_H_
+#define ODP_API_ABI_WAIT_UNTIL_GENERIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/atomic.h>
+
+static inline void
+_odp_wait_until_equal_acq_u32(odp_atomic_u32_t *addr, uint32_t expected)
+{
+ while (odp_atomic_load_acq_u32(addr) != expected)
+ odp_cpu_pause();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp/api/cpu_arch.h b/platform/linux-generic/arch/default/odp/api/cpu_arch.h
deleted file mode 100644
index 22b1da2dd..000000000
--- a/platform/linux-generic/arch/default/odp/api/cpu_arch.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_CPU_ARCH_H_
-#define ODP_PLAT_CPU_ARCH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _ODP_CACHE_LINE_SIZE 64
-
-static inline void odp_cpu_pause(void)
-{
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/arch/default/odp_atomic.c b/platform/linux-generic/arch/default/odp_atomic.c
new file mode 100644
index 000000000..a2a3bd2f2
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_atomic.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#include <odp/api/atomic.h>
+
+int odp_atomic_lock_free_u64(odp_atomic_op_t *atomic_op)
+{
+#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
+ /* All operations have locks */
+ if (atomic_op)
+ atomic_op->all_bits = 0;
+
+ return 0;
+#else
+ /* All operations are lock-free */
+ if (atomic_op) {
+ atomic_op->all_bits = ~((uint32_t)0);
+ atomic_op->op.init = 0;
+ }
+
+ return 2;
+#endif
+}
+
+int odp_atomic_lock_free_u128(odp_atomic_op_t *atomic_op)
+{
+#ifdef __SIZEOF_INT128__
+ if (__atomic_is_lock_free(16, NULL)) {
+ if (atomic_op) {
+ atomic_op->all_bits = 0;
+ atomic_op->op.load = 1;
+ atomic_op->op.store = 1;
+ atomic_op->op.cas = 1;
+ }
+ return 2;
+ }
+#endif
+ /* All operations have locks */
+ if (atomic_op)
+ atomic_op->all_bits = 0;
+
+ return 0;
+}
diff --git a/platform/linux-generic/arch/default/odp_atomic.h b/platform/linux-generic/arch/default/odp_atomic.h
new file mode 100644
index 000000000..eef88c7b3
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_atomic.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 ARM Limited
+ */
+
+#ifndef ODP_DEFAULT_ATOMIC_H_
+#define ODP_DEFAULT_ATOMIC_H_
+
+#include <odp_types_internal.h>
+
+#ifdef __SIZEOF_INT128__
+
+static inline _odp_u128_t lockfree_load_u128(_odp_u128_t *atomic)
+{
+ return __atomic_load_n(atomic, __ATOMIC_RELAXED);
+}
+
+static inline int lockfree_cas_acq_rel_u128(_odp_u128_t *atomic,
+ _odp_u128_t old_val,
+ _odp_u128_t new_val)
+{
+ return __atomic_compare_exchange_n(atomic, &old_val, new_val,
+ 0 /* strong */,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_RELAXED);
+}
+
+static inline int lockfree_check_u128(void)
+{
+ return __atomic_is_lock_free(16, NULL);
+}
+
+#endif
+
+#include <limits.h>
+
+/** Atomic bit set operations with memory ordering */
+#if __GCC_ATOMIC_LLONG_LOCK_FREE == 2 && \
+ __SIZEOF_LONG_LONG__ != __SIZEOF_LONG__
+typedef unsigned long long bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_LONG_LONG__)
+
+#elif __GCC_ATOMIC_LONG_LOCK_FREE == 2 && __SIZEOF_LONG__ != __SIZEOF_INT__
+typedef unsigned long bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_LONG__)
+
+#elif __GCC_ATOMIC_INT_LOCK_FREE == 2
+typedef unsigned int bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_INT__)
+
+#else
+/* Target does not support lock-free atomic operations */
+typedef unsigned int bitset_t;
+#define ATOM_BITSET_SIZE (CHAR_BIT * __SIZEOF_INT__)
+#endif
+
+#if ATOM_BITSET_SIZE <= 32
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ return 1UL << bit;
+}
+
+#elif ATOM_BITSET_SIZE <= 64
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ return 1ULL << bit;
+}
+
+#elif ATOM_BITSET_SIZE <= 128
+
+static inline bitset_t bitset_mask(uint32_t bit)
+{
+ if (bit < 64)
+ return 1ULL << bit;
+ else
+ return (_odp_u128_t)(1ULL << (bit - 64)) << 64;
+}
+
+#else
+#error Unsupported size of bit sets (ATOM_BITSET_SIZE)
+#endif
+
+static inline bitset_t atom_bitset_load(bitset_t *bs, int mo)
+{
+ return __atomic_load_n(bs, mo);
+}
+
+static inline void atom_bitset_set(bitset_t *bs, uint32_t bit, int mo)
+{
+ (void)__atomic_fetch_or(bs, bitset_mask(bit), mo);
+}
+
+static inline void atom_bitset_clr(bitset_t *bs, uint32_t bit, int mo)
+{
+ (void)__atomic_fetch_and(bs, ~bitset_mask(bit), mo);
+}
+
+static inline bitset_t atom_bitset_xchg(bitset_t *bs, bitset_t neu, int mo)
+{
+ return __atomic_exchange_n(bs, neu, mo);
+}
+
+static inline bitset_t atom_bitset_cmpxchg(bitset_t *bs, bitset_t *old,
+ bitset_t neu, bool weak,
+ int mo_success, int mo_failure)
+{
+ return __atomic_compare_exchange_n(bs, old, neu, weak, mo_success,
+ mo_failure);
+}
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp_cpu.h b/platform/linux-generic/arch/default/odp_cpu.h
new file mode 100644
index 000000000..e9d88c791
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_cpu.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_DEFAULT_CPU_H_
+#define ODP_DEFAULT_CPU_H_
+
+#ifndef _ODP_UNALIGNED
+#define _ODP_UNALIGNED 0
+#endif
+
+/******************************************************************************
+ * Atomics
+ *****************************************************************************/
+
+#define atomic_store_release(loc, val, ro) \
+ __atomic_store_n(loc, val, __ATOMIC_RELEASE)
+
+#include "odp_atomic.h"
+#include "odp_wait_until.h"
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp_cpu_arch.c b/platform/linux-generic/arch/default/odp_cpu_arch.c
deleted file mode 100644
index c31f90844..000000000
--- a/platform/linux-generic/arch/default/odp_cpu_arch.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-
-#include <stdlib.h>
-#include <time.h>
-
-#include <odp/api/cpu.h>
-#include <odp/api/hints.h>
-#include <odp/api/system_info.h>
-#include <odp_debug_internal.h>
-#include <odp_time_internal.h>
-
-#define GIGA 1000000000
-
-uint64_t odp_cpu_cycles(void)
-{
- struct timespec time;
- uint64_t sec, ns, hz, cycles;
- int ret;
-
- ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
-
- if (ret != 0)
- ODP_ABORT("clock_gettime failed\n");
-
- hz = odp_cpu_hz_max();
- sec = (uint64_t)time.tv_sec;
- ns = (uint64_t)time.tv_nsec;
-
- cycles = sec * hz;
- cycles += (ns * hz) / GIGA;
-
- return cycles;
-}
-
-uint64_t odp_cpu_cycles_max(void)
-{
- return UINT64_MAX;
-}
-
-uint64_t odp_cpu_cycles_resolution(void)
-{
- return 1;
-}
-
-int cpu_has_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time_freq(void)
-{
- return 0;
-}
diff --git a/platform/linux-generic/arch/default/odp_cpu_cycles.c b/platform/linux-generic/arch/default/odp_cpu_cycles.c
new file mode 100644
index 000000000..97c1da93c
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_cpu_cycles.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+
+#define GIGA 1000000000
+
+#include <odp/api/abi/cpu_generic.h>
+
+uint64_t _odp_cpu_cycles(void)
+{
+ struct timespec time;
+ uint64_t sec, ns, hz, cycles;
+ int ret;
+
+ ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+
+ if (ret != 0)
+ _ODP_ABORT("clock_gettime failed\n");
+
+ hz = odp_global_ro.system_info.cpu_hz_max[0];
+
+ sec = (uint64_t)time.tv_sec;
+ ns = (uint64_t)time.tv_nsec;
+
+ cycles = sec * hz;
+ cycles += (ns * hz) / GIGA;
+
+ return cycles;
+}
+
+int _odp_cpu_cycles_init_global(void)
+{
+ return 0;
+}
diff --git a/platform/linux-generic/arch/default/odp_hash_crc32.c b/platform/linux-generic/arch/default/odp_hash_crc32.c
new file mode 100644
index 000000000..c6956787f
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_hash_crc32.c
@@ -0,0 +1,464 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ *
+ * Copyright(c) 2010-2014 Intel Corporation
+ * - lib/hash/rte_crc_sw.h
+ */
+
+#include <odp/api/align.h>
+#include <odp/api/std_types.h>
+
+#include <odp/api/abi/hash_crc32.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Table generated with odp_hash_crc_gen64() */
+static const uint32_t crc32_table[256] ODP_ALIGNED_CACHE = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static const uint32_t crc32c_tables[8][256] = {{
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
+},
+{
+ 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB, 0x69CF5132, 0x7A6DC945,
+ 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD,
+ 0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,
+ 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C,
+ 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47,
+ 0xE29F20BA, 0xF13DB8CD, 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF,
+ 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6,
+ 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2, 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E,
+ 0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41,
+ 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17, 0x0BCC548E, 0x186ECCF9,
+ 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0,
+ 0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,
+ 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43,
+ 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB,
+ 0xBF59D487, 0xACFB4CF0, 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2,
+ 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A,
+ 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260, 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC,
+ 0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004,
+ 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3, 0xADC95A4A, 0xBE6BC23D,
+ 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185,
+ 0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,
+ 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306,
+ 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3, 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F,
+ 0x26992BC2, 0x353BB3B5, 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287,
+ 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8,
+ 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC, 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600,
+ 0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439,
+ 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F, 0xCFCA5FF6, 0xDC68C781,
+ 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA,
+ 0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,
+ 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B,
+ 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F, 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483
+},
+{
+ 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664, 0xD1B1F617, 0x74F06469,
+ 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC,
+ 0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,
+ 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726,
+ 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67, 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D,
+ 0xD915C5D1, 0x7C5457AF, 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8,
+ 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7,
+ 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828, 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32,
+ 0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0,
+ 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878, 0x2E85480B, 0x8BC4DA75,
+ 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A,
+ 0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,
+ 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4,
+ 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B, 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161,
+ 0x56830647, 0xF3C29439, 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E,
+ 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB,
+ 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730, 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A,
+ 0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF,
+ 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD, 0x2A34FCDE, 0x8F756EA0,
+ 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065,
+ 0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,
+ 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB,
+ 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4,
+ 0x2290CF18, 0x87D15D66, 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71,
+ 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3,
+ 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C, 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36,
+ 0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79,
+ 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1, 0xD50042C2, 0x7041D0BC,
+ 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7,
+ 0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,
+ 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D,
+ 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8
+},
+{
+ 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B, 0xC4451272, 0x1900B8CA,
+ 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C,
+ 0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,
+ 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11,
+ 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2, 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41,
+ 0x2161776D, 0xFC24DDD5, 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7,
+ 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C,
+ 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69, 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A,
+ 0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D,
+ 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A, 0x99A72E73, 0x44E284CB,
+ 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610,
+ 0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,
+ 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6,
+ 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3, 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040,
+ 0x95E7FA51, 0x48A250E9, 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B,
+ 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D,
+ 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006, 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5,
+ 0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213,
+ 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349, 0x7F816A70, 0xA2C4C0C8,
+ 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E,
+ 0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,
+ 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698,
+ 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443,
+ 0x9AA50F6F, 0x47E0A5D7, 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5,
+ 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12,
+ 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07, 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4,
+ 0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F,
+ 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48, 0x22635671, 0xFF26FCC9,
+ 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99,
+ 0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,
+ 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4,
+ 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1, 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842
+},
+{
+ 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C, 0x906761E8, 0xA8760E44,
+ 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5,
+ 0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,
+ 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406,
+ 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13,
+ 0xDECFBEC6, 0xE6DED16A, 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082,
+ 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0,
+ 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1, 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151,
+ 0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA,
+ 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023, 0x625100D7, 0x5A406F7B,
+ 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539,
+ 0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,
+ 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD,
+ 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C,
+ 0xA3DBBE2A, 0x9BCAD186, 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E,
+ 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF,
+ 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8, 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18,
+ 0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089,
+ 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593, 0x71E7D567, 0x49F6BACB,
+ 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A,
+ 0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,
+ 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE,
+ 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C, 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C,
+ 0x3F4F0A49, 0x075E65E5, 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D,
+ 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6,
+ 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497, 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27,
+ 0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065,
+ 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC, 0x83D1B458, 0xBBC0DBF4,
+ 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1,
+ 0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,
+ 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532,
+ 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013, 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3
+},
+{
+ 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E, 0x697997B4, 0x8649FCAD,
+ 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2,
+ 0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,
+ 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C,
+ 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57, 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20,
+ 0xE5F20E92, 0x0AC2658B, 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F,
+ 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E,
+ 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576, 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201,
+ 0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746,
+ 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A, 0x077BD440, 0xE84BBF59,
+ 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778,
+ 0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,
+ 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB,
+ 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4,
+ 0x4BFC7D58, 0xA4CC1641, 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5,
+ 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA,
+ 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C, 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B,
+ 0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364,
+ 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86, 0xB57D105C, 0x5A4D7B45,
+ 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A,
+ 0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,
+ 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9,
+ 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8,
+ 0x39F6897A, 0xD6C6E263, 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7,
+ 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090,
+ 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8, 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F,
+ 0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE,
+ 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872, 0xDB7F53A8, 0x344F38B1,
+ 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D,
+ 0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,
+ 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623,
+ 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B, 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C
+},
+{
+ 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919, 0x75E69C41, 0x1DE5B089,
+ 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA,
+ 0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,
+ 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C,
+ 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5, 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334,
+ 0x73767EEE, 0x1B755226, 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67,
+ 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992,
+ 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110, 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1,
+ 0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3,
+ 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330, 0x417C6668, 0x297F4AA0,
+ 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55,
+ 0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,
+ 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E,
+ 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC, 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D,
+ 0xDBB77E61, 0xB3B452A9, 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8,
+ 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB,
+ 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC, 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D,
+ 0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E,
+ 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B, 0x1CD36813, 0x74D044DB,
+ 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988,
+ 0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,
+ 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093,
+ 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766,
+ 0x1A438ABC, 0x7240A674, 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35,
+ 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907,
+ 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185, 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454,
+ 0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1,
+ 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762, 0x2849923A, 0x404ABEF2,
+ 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA,
+ 0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,
+ 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C,
+ 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F
+},
+{
+ 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A, 0xB3657823, 0xFA590504,
+ 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE,
+ 0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,
+ 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A,
+ 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0, 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D,
+ 0x4F3B6143, 0x06071C64, 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447,
+ 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929,
+ 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E, 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3,
+ 0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36,
+ 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2, 0xEB5EB7CB, 0xA262CAEC,
+ 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782,
+ 0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,
+ 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF,
+ 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18, 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75,
+ 0x9376A71F, 0xDA4ADA38, 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B,
+ 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1,
+ 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D, 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360,
+ 0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA,
+ 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A, 0x0312E7F3, 0x4A2E9AD4,
+ 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E,
+ 0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,
+ 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223,
+ 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20, 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D,
+ 0xFF4CFE93, 0xB67083B4, 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97,
+ 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852,
+ 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5, 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88,
+ 0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6,
+ 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72, 0x5B29281B, 0x1215553C,
+ 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB,
+ 0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,
+ 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F,
+ 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8, 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5
+} };
+
+#define CRC32_UPD(crc, n) \
+ (crc32c_tables[(n)][(crc) & 0xff] ^ \
+ crc32c_tables[(n) - 1][((crc) >> 8) & 0xff])
+
+static inline uint32_t crc32c_u8(uint8_t data, uint32_t init_val)
+{
+ uint32_t crc;
+
+ crc = init_val;
+ crc ^= data;
+
+ return crc32c_tables[0][crc & 0xff] ^ (crc >> 8);
+}
+
+static inline uint32_t crc32c_u16(uint16_t data, uint32_t init_val)
+{
+ uint32_t crc;
+
+ crc = init_val;
+ crc ^= data;
+
+ crc = CRC32_UPD(crc, 1) ^ (crc >> 16);
+
+ return crc;
+}
+
+static inline uint32_t crc32c_u32(uint32_t data, uint32_t init_val)
+{
+ uint32_t crc, term1, term2;
+
+ crc = init_val;
+ crc ^= data;
+
+ term1 = CRC32_UPD(crc, 3);
+ term2 = crc >> 16;
+ crc = term1 ^ CRC32_UPD(term2, 1);
+
+ return crc;
+}
+
+static inline uint32_t crc32c_u64(uint64_t data, uint32_t init_val)
+{
+ uint32_t crc, term1, term2;
+
+ union {
+ uint64_t u64;
+ uint32_t u32[2];
+ } d;
+
+ d.u64 = data;
+
+ crc = init_val;
+ crc ^= d.u32[0];
+
+ term1 = CRC32_UPD(crc, 7);
+ term2 = crc >> 16;
+ crc = term1 ^ CRC32_UPD(term2, 5);
+ term1 = CRC32_UPD(d.u32[1], 3);
+ term2 = d.u32[1] >> 16;
+ crc ^= term1 ^ CRC32_UPD(term2, 1);
+
+ return crc;
+}
+
+#include <odp/visibility_begin.h>
+
+uint32_t _odp_hash_crc32_generic(const void *data_ptr, uint32_t data_len,
+ uint32_t init_val)
+{
+ uint32_t i, crc;
+ const uint8_t *byte = data_ptr;
+
+ crc = init_val;
+
+ for (i = 0; i < data_len; i++)
+ crc = crc32_table[(crc ^ byte[i]) & 0xff] ^ (crc >> 8);
+
+ return crc;
+}
+
+uint32_t _odp_hash_crc32c_generic(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ uint32_t i;
+ uintptr_t pd = (uintptr_t)data;
+
+ for (i = 0; i < data_len / 8; i++) {
+ init_val = crc32c_u64(*(const uint64_t *)pd, init_val);
+ pd += 8;
+ }
+
+ if (data_len & 0x4) {
+ init_val = crc32c_u32(*(const uint32_t *)pd, init_val);
+ pd += 4;
+ }
+
+ if (data_len & 0x2) {
+ init_val = crc32c_u16(*(const uint16_t *)pd, init_val);
+ pd += 2;
+ }
+
+ if (data_len & 0x1)
+ init_val = crc32c_u8(*(const uint8_t *)pd, init_val);
+
+ return init_val;
+}
+
+#include <odp/visibility_end.h>
diff --git a/platform/linux-generic/arch/default/odp_random.c b/platform/linux-generic/arch/default/odp_random.c
new file mode 100644
index 000000000..7c8b33c42
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_random.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_random.h>
+#include <odp/api/spec/random.h>
+
+#include <odp/visibility_begin.h>
+
+odp_random_kind_t _odp_random_max_kind_generic(void)
+{
+ return ODP_RANDOM_BASIC;
+}
+
+int32_t _odp_random_true_data_generic(uint8_t *buf, uint32_t len)
+{
+ (void)buf;
+ (void)len;
+
+ return -1;
+}
+
+int32_t _odp_random_crypto_data_generic(uint8_t *buf, uint32_t len)
+{
+ (void)buf;
+ (void)len;
+
+ return -1;
+}
+
+#include <odp/visibility_end.h>
diff --git a/platform/linux-generic/arch/default/odp_random.h b/platform/linux-generic/arch/default/odp_random.h
new file mode 100644
index 000000000..7b6c5a2f3
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_random.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_DEFAULT_RANDOM_H_
+#define ODP_DEFAULT_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/random.h>
+
+#include <stdint.h>
+
+odp_random_kind_t _odp_random_max_kind_generic(void);
+int32_t _odp_random_true_data_generic(uint8_t *buf, uint32_t len);
+int32_t _odp_random_crypto_data_generic(uint8_t *buf, uint32_t len);
+
+static inline odp_random_kind_t _odp_random_max_kind(void)
+{
+ return _odp_random_max_kind_generic();
+}
+
+static inline int32_t _odp_random_true_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_true_data_generic(buf, len);
+}
+
+static inline int32_t _odp_random_crypto_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_crypto_data_generic(buf, len);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/default/odp_sysinfo_parse.c b/platform/linux-generic/arch/default/odp_sysinfo_parse.c
index 8ae2022ac..024bc8217 100644
--- a/platform/linux-generic/arch/default/odp_sysinfo_parse.c
+++ b/platform/linux-generic/arch/default/odp_sysinfo_parse.c
@@ -1,31 +1,20 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
-#include <odp_internal.h>
-#include <odp_debug_internal.h>
-#include <string.h>
+#include <odp_global_data.h>
+#include <odp_sysinfo_internal.h>
-int cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo)
+int _odp_cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo)
{
- int i;
-
- ODP_DBG("Warning: use dummy values for freq and model string\n");
- for (i = 0; i < MAX_CPU_NUMBER; i++) {
- sysinfo->cpu_hz_max[i] = 1400000000;
- strcpy(sysinfo->model_str[i], "UNKNOWN");
- }
-
- return 0;
+ return _odp_dummy_cpuinfo(sysinfo);
}
-uint64_t odp_cpu_hz_current(int id ODP_UNUSED)
+void _odp_sys_info_print_arch(void)
{
- return 0;
}
-void sys_info_print_arch(void)
+uint64_t odp_cpu_arch_hz_current(int id ODP_UNUSED)
{
+ return odp_global_ro.system_info.default_cpu_hz;
}
diff --git a/platform/linux-generic/arch/default/odp_time.c b/platform/linux-generic/arch/default/odp_time.c
new file mode 100644
index 000000000..ec9da0896
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_time.c
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/time_types.h>
+
+#include <odp/api/abi/time_inlines.h>
+
+#include <odp_debug_internal.h>
+#include <odp_init_internal.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+
+#define YEAR_IN_SEC (365 * 24 * 3600)
+
+typedef struct _odp_time_global_t {
+ struct timespec start_time;
+ uint64_t start_time_ns;
+
+} _odp_time_global_t;
+
+_odp_time_global_t _odp_time_glob;
+
+static inline uint64_t time_nsec(struct timespec *t)
+{
+ uint64_t nsec = (t->tv_sec * ODP_TIME_SEC_IN_NS) + t->tv_nsec;
+
+ return nsec;
+}
+
+#include <odp/visibility_begin.h>
+
+odp_time_t _odp_time_cur(void)
+{
+ int ret;
+ odp_time_t time;
+ struct timespec sys_time;
+
+ ret = clock_gettime(CLOCK_MONOTONIC_RAW, &sys_time);
+ if (odp_unlikely(ret != 0))
+ _ODP_ABORT("clock_gettime() failed\n");
+
+ time.nsec = time_nsec(&sys_time);
+
+ return time;
+}
+
+uint64_t _odp_time_res(void)
+{
+ int ret;
+ struct timespec tres;
+
+ ret = clock_getres(CLOCK_MONOTONIC_RAW, &tres);
+ if (odp_unlikely(ret != 0))
+ _ODP_ABORT("clock_getres() failed\n");
+
+ return ODP_TIME_SEC_IN_NS / (uint64_t)tres.tv_nsec;
+}
+
+void _odp_time_startup(odp_time_startup_t *startup)
+{
+ startup->global.nsec = _odp_time_glob.start_time_ns;
+ startup->global_ns = _odp_time_glob.start_time_ns;
+}
+
+#include <odp/visibility_end.h>
+
+int _odp_time_init_global(void)
+{
+ struct timespec *start_time;
+ uint64_t diff, years;
+ int ret = 0;
+ _odp_time_global_t *global = &_odp_time_glob;
+
+ memset(global, 0, sizeof(_odp_time_global_t));
+
+ start_time = &global->start_time;
+ start_time->tv_sec = 0;
+ start_time->tv_nsec = 0;
+
+ ret = clock_gettime(CLOCK_MONOTONIC_RAW, start_time);
+ if (ret)
+ _ODP_ERR("clock_gettime() failed: %d\n", ret);
+
+ global->start_time_ns = time_nsec(start_time);
+
+ diff = UINT64_MAX - global->start_time_ns;
+ years = (diff / ODP_TIME_SEC_IN_NS) / YEAR_IN_SEC;
+
+ if (years < 10) {
+ _ODP_ERR("Time in nsec would wrap in 10 years: %" PRIu64 "\n",
+ global->start_time_ns);
+ return -1;
+ }
+
+ return ret;
+}
+
+int _odp_time_term_global(void)
+{
+ return 0;
+}
diff --git a/platform/linux-generic/arch/default/odp_wait_until.h b/platform/linux-generic/arch/default/odp_wait_until.h
new file mode 100644
index 000000000..c51f4355e
--- /dev/null
+++ b/platform/linux-generic/arch/default/odp_wait_until.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+#ifndef ODP_DEFAULT_WAIT_UNTIL_H_
+#define ODP_DEFAULT_WAIT_UNTIL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/cpu_inlines.h>
+
+#include <stdint.h>
+
+/**
+ * The _odp_wait_until_eq_*() functions defined in this header are intended to
+ * be used only with the scalable scheduler and queue implementations. Even
+ * though these functions use standard non-atomic parameter types, the
+ * parameters must only be operated using atomic operations. If new functions
+ * are added to this file, they should use _odp_wait_until_equal_*() prefix and
+ * atomic parameter types.
+ */
+
+static inline void _odp_wait_until_eq_u32(uint32_t *val, uint32_t expected)
+{
+ while (__atomic_load_n(val, __ATOMIC_RELAXED) != expected)
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_bitset(bitset_t *val, bitset_t expected)
+{
+ while (__atomic_load_n(val, __ATOMIC_RELAXED) != expected)
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_acq_u8(uint8_t *val, uint8_t expected)
+{
+ while (__atomic_load_n(val, __ATOMIC_ACQUIRE) != expected)
+ odp_cpu_pause();
+}
+
+static inline void _odp_wait_until_eq_acq_u32(uint32_t *val, uint32_t expected)
+{
+ while (__atomic_load_n(val, __ATOMIC_ACQUIRE) != expected)
+ odp_cpu_pause();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/mips64/odp/api/cpu_arch.h b/platform/linux-generic/arch/mips64/odp/api/cpu_arch.h
deleted file mode 100644
index 3582b129b..000000000
--- a/platform/linux-generic/arch/mips64/odp/api/cpu_arch.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_CPU_ARCH_H_
-#define ODP_PLAT_CPU_ARCH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined __OCTEON__
-#define _ODP_CACHE_LINE_SIZE 128
-#else
-#error Please add support for your arch in cpu_arch.h
-#endif
-
-static inline void odp_cpu_pause(void)
-{
- __asm__ __volatile__ ("nop");
- __asm__ __volatile__ ("nop");
- __asm__ __volatile__ ("nop");
- __asm__ __volatile__ ("nop");
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/arch/mips64/odp_cpu_arch.c b/platform/linux-generic/arch/mips64/odp_cpu_arch.c
deleted file mode 100644
index f7eafa0fb..000000000
--- a/platform/linux-generic/arch/mips64/odp_cpu_arch.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/cpu.h>
-#include <odp/api/hints.h>
-#include <odp/api/system_info.h>
-#include <odp_time_internal.h>
-
-uint64_t odp_cpu_cycles(void)
-{
- #define CVMX_TMP_STR(x) CVMX_TMP_STR2(x)
- #define CVMX_TMP_STR2(x) #x
- uint64_t cycle;
-
- __asm__ __volatile__ ("rdhwr %[rt],$" CVMX_TMP_STR(31) :
- [rt] "=d" (cycle) : : "memory");
-
- return cycle;
-}
-
-uint64_t odp_cpu_cycles_max(void)
-{
- return UINT64_MAX;
-}
-
-uint64_t odp_cpu_cycles_resolution(void)
-{
- return 1;
-}
-
-int cpu_has_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time_freq(void)
-{
- return 0;
-}
diff --git a/platform/linux-generic/arch/mips64/odp_sysinfo_parse.c b/platform/linux-generic/arch/mips64/odp_sysinfo_parse.c
deleted file mode 100644
index d6f75f283..000000000
--- a/platform/linux-generic/arch/mips64/odp_sysinfo_parse.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_internal.h>
-#include <string.h>
-
-int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
-{
- char str[1024];
- char *pos;
- double mhz = 0.0;
- uint64_t hz;
- int model = 0;
- int count = 2;
- int id = 0;
-
- strcpy(sysinfo->cpu_arch_str, "mips64");
- while (fgets(str, sizeof(str), file) != NULL && id < MAX_CPU_NUMBER) {
- if (!mhz) {
- pos = strstr(str, "BogoMIPS");
-
- if (pos)
- if (sscanf(pos, "BogoMIPS : %lf", &mhz) == 1) {
- /* bogomips seems to be 2x freq */
- hz = (uint64_t)(mhz * 1000000.0 / 2.0);
- sysinfo->cpu_hz_max[id] = hz;
- count--;
- }
- }
-
- if (!model) {
- pos = strstr(str, "cpu model");
-
- if (pos) {
- int len;
-
- pos = strchr(str, ':');
- strncpy(sysinfo->model_str[id], pos + 2,
- sizeof(sysinfo->model_str[id]) - 1);
- len = strlen(sysinfo->model_str[id]);
- sysinfo->model_str[id][len - 1] = 0;
- model = 1;
- count--;
- }
- }
-
- if (count == 0) {
- mhz = 0.0;
- model = 0;
- count = 2;
- id++;
- }
- }
-
- return 0;
-}
-
-uint64_t odp_cpu_hz_current(int id ODP_UNUSED)
-{
- return 0;
-}
-
-void sys_info_print_arch(void)
-{
-}
diff --git a/platform/linux-generic/arch/powerpc/odp/api/abi/cpu.h b/platform/linux-generic/arch/powerpc/odp/api/abi/cpu.h
new file mode 100644
index 000000000..dc128e1a8
--- /dev/null
+++ b/platform/linux-generic/arch/powerpc/odp/api/abi/cpu.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 128
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/cpu_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/platform/linux-generic/arch/powerpc/odp/api/cpu_arch.h b/platform/linux-generic/arch/powerpc/odp/api/cpu_arch.h
deleted file mode 100644
index 22b1da2dd..000000000
--- a/platform/linux-generic/arch/powerpc/odp/api/cpu_arch.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_CPU_ARCH_H_
-#define ODP_PLAT_CPU_ARCH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _ODP_CACHE_LINE_SIZE 64
-
-static inline void odp_cpu_pause(void)
-{
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c
deleted file mode 100644
index c31f90844..000000000
--- a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-
-#include <stdlib.h>
-#include <time.h>
-
-#include <odp/api/cpu.h>
-#include <odp/api/hints.h>
-#include <odp/api/system_info.h>
-#include <odp_debug_internal.h>
-#include <odp_time_internal.h>
-
-#define GIGA 1000000000
-
-uint64_t odp_cpu_cycles(void)
-{
- struct timespec time;
- uint64_t sec, ns, hz, cycles;
- int ret;
-
- ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
-
- if (ret != 0)
- ODP_ABORT("clock_gettime failed\n");
-
- hz = odp_cpu_hz_max();
- sec = (uint64_t)time.tv_sec;
- ns = (uint64_t)time.tv_nsec;
-
- cycles = sec * hz;
- cycles += (ns * hz) / GIGA;
-
- return cycles;
-}
-
-uint64_t odp_cpu_cycles_max(void)
-{
- return UINT64_MAX;
-}
-
-uint64_t odp_cpu_cycles_resolution(void)
-{
- return 1;
-}
-
-int cpu_has_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time(void)
-{
- return 0;
-}
-
-uint64_t cpu_global_time_freq(void)
-{
- return 0;
-}
diff --git a/platform/linux-generic/arch/powerpc/odp_sysinfo_parse.c b/platform/linux-generic/arch/powerpc/odp_sysinfo_parse.c
index bd4b9b429..519ca62ed 100644
--- a/platform/linux-generic/arch/powerpc/odp_sysinfo_parse.c
+++ b/platform/linux-generic/arch/powerpc/odp_sysinfo_parse.c
@@ -1,13 +1,13 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
-#include <odp_internal.h>
+#include <odp_global_data.h>
+#include <odp_sysinfo_internal.h>
+#include <odp_string_internal.h>
#include <string.h>
-int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
+int _odp_cpuinfo_parser(FILE *file, system_info_t *sysinfo)
{
char str[1024];
char *pos;
@@ -17,8 +17,12 @@ int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
int count = 2;
int id = 0;
+ sysinfo->cpu_arch = ODP_CPU_ARCH_PPC;
+ sysinfo->cpu_isa_sw.ppc = ODP_CPU_ARCH_PPC_UNKNOWN;
+ sysinfo->cpu_isa_hw.ppc = ODP_CPU_ARCH_PPC_UNKNOWN;
+
strcpy(sysinfo->cpu_arch_str, "powerpc");
- while (fgets(str, sizeof(str), file) != NULL && id < MAX_CPU_NUMBER) {
+ while (fgets(str, sizeof(str), file) != NULL && id < CONFIG_NUM_CPU_IDS) {
if (!mhz) {
pos = strstr(str, "clock");
@@ -34,13 +38,9 @@ int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
pos = strstr(str, "cpu");
if (pos) {
- int len;
-
pos = strchr(str, ':');
- strncpy(sysinfo->model_str[id], pos + 2,
- sizeof(sysinfo->model_str[id]) - 1);
- len = strlen(sysinfo->model_str[id]);
- sysinfo->model_str[id][len - 1] = 0;
+ _odp_strcpy(sysinfo->model_str[id], pos + 2,
+ MODEL_STR_SIZE);
model = 1;
count--;
}
@@ -57,11 +57,11 @@ int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
return 0;
}
-uint64_t odp_cpu_hz_current(int id ODP_UNUSED)
+void _odp_sys_info_print_arch(void)
{
- return 0;
}
-void sys_info_print_arch(void)
+uint64_t odp_cpu_arch_hz_current(int id ODP_UNUSED)
{
+ return odp_global_ro.system_info.default_cpu_hz;
}
diff --git a/platform/linux-generic/arch/x86/cpu_flags.c b/platform/linux-generic/arch/x86/cpu_flags.c
index a492a35bf..124f1d184 100644
--- a/platform/linux-generic/arch/x86/cpu_flags.c
+++ b/platform/linux-generic/arch/x86/cpu_flags.c
@@ -1,45 +1,22 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*
- * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2015 Intel Corporation
+ * - lib/eal/x86/include/rte_cpuflags.h
+ * - lib/eal/x86/rte_cpuflags.c
+ * - lib/eal/x86/rte_cpuid.h
*/
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+#include "cpu_flags.h"
+
+#include <odp/api/abi/time_cpu.h>
-#include <arch/x86/cpu_flags.h>
#include <odp_debug_internal.h>
-#include <odp_time_internal.h>
+#include <odp_global_data.h>
+
+#include <cpuid.h>
+#include <errno.h>
#include <stdio.h>
#include <stdint.h>
@@ -74,6 +51,7 @@ enum rte_cpu_flag_t {
RTE_CPUFLAG_AVX, /**< AVX */
RTE_CPUFLAG_F16C, /**< F16C */
RTE_CPUFLAG_RDRAND, /**< RDRAND */
+ RTE_CPUFLAG_HYPERVISOR, /**< Running in a VM */
/* (EAX 01h) EDX features */
RTE_CPUFLAG_FPU, /**< FPU */
@@ -130,6 +108,7 @@ enum rte_cpu_flag_t {
RTE_CPUFLAG_INVPCID, /**< INVPCID */
RTE_CPUFLAG_RTM, /**< Transactional memory */
RTE_CPUFLAG_AVX512F, /**< AVX512F */
+ RTE_CPUFLAG_RDSEED, /**< RDSEED instruction */
/* (EAX 80000001h) ECX features */
RTE_CPUFLAG_LAHF_SAHF, /**< LAHF_SAHF */
@@ -145,8 +124,29 @@ enum rte_cpu_flag_t {
/* (EAX 80000007h) EDX features */
RTE_CPUFLAG_INVTSC, /**< INVTSC */
+ RTE_CPUFLAG_AVX512DQ, /**< AVX512 Doubleword and Quadword */
+ RTE_CPUFLAG_AVX512IFMA, /**< AVX512 Integer Fused Multiply-Add */
+ RTE_CPUFLAG_AVX512CD, /**< AVX512 Conflict Detection*/
+ RTE_CPUFLAG_AVX512BW, /**< AVX512 Byte and Word */
+ RTE_CPUFLAG_AVX512VL, /**< AVX512 Vector Length */
+ RTE_CPUFLAG_AVX512VBMI, /**< AVX512 Vector Bit Manipulation */
+ RTE_CPUFLAG_AVX512VBMI2, /**< AVX512 Vector Bit Manipulation 2 */
+ RTE_CPUFLAG_GFNI, /**< Galois Field New Instructions */
+ RTE_CPUFLAG_VAES, /**< Vector AES */
+ RTE_CPUFLAG_VPCLMULQDQ, /**< Vector Carry-less Multiply */
+ RTE_CPUFLAG_AVX512VNNI,
+ /**< AVX512 Vector Neural Network Instructions */
+ RTE_CPUFLAG_AVX512BITALG, /**< AVX512 Bit Algorithms */
+ RTE_CPUFLAG_AVX512VPOPCNTDQ, /**< AVX512 Vector Popcount */
+ RTE_CPUFLAG_CLDEMOTE, /**< Cache Line Demote */
+ RTE_CPUFLAG_MOVDIRI, /**< Direct Store Instructions */
+ RTE_CPUFLAG_MOVDIR64B, /**< Direct Store Instructions 64B */
+ RTE_CPUFLAG_AVX512VP2INTERSECT, /**< AVX512 Two Register Intersection */
+
+ RTE_CPUFLAG_WAITPKG, /**< UMONITOR/UMWAIT/TPAUSE */
+
/* The last item */
- RTE_CPUFLAG_NUMFLAGS, /**< This should always be the last! */
+ RTE_CPUFLAG_NUMFLAGS, /**< This should always be the last! */
};
enum cpu_register_t {
@@ -203,6 +203,7 @@ static const struct feature_entry cpu_feature_table[] = {
FEAT_DEF(AVX, 0x00000001, 0, RTE_REG_ECX, 28)
FEAT_DEF(F16C, 0x00000001, 0, RTE_REG_ECX, 29)
FEAT_DEF(RDRAND, 0x00000001, 0, RTE_REG_ECX, 30)
+ FEAT_DEF(HYPERVISOR, 0x00000001, 0, RTE_REG_ECX, 31)
FEAT_DEF(FPU, 0x00000001, 0, RTE_REG_EDX, 0)
FEAT_DEF(VME, 0x00000001, 0, RTE_REG_EDX, 1)
@@ -246,15 +247,36 @@ static const struct feature_entry cpu_feature_table[] = {
FEAT_DEF(ENERGY_EFF, 0x00000006, 0, RTE_REG_ECX, 3)
FEAT_DEF(FSGSBASE, 0x00000007, 0, RTE_REG_EBX, 0)
- FEAT_DEF(BMI1, 0x00000007, 0, RTE_REG_EBX, 2)
+ FEAT_DEF(BMI1, 0x00000007, 0, RTE_REG_EBX, 3)
FEAT_DEF(HLE, 0x00000007, 0, RTE_REG_EBX, 4)
FEAT_DEF(AVX2, 0x00000007, 0, RTE_REG_EBX, 5)
- FEAT_DEF(SMEP, 0x00000007, 0, RTE_REG_EBX, 6)
- FEAT_DEF(BMI2, 0x00000007, 0, RTE_REG_EBX, 7)
- FEAT_DEF(ERMS, 0x00000007, 0, RTE_REG_EBX, 8)
+ FEAT_DEF(SMEP, 0x00000007, 0, RTE_REG_EBX, 7)
+ FEAT_DEF(BMI2, 0x00000007, 0, RTE_REG_EBX, 8)
+ FEAT_DEF(ERMS, 0x00000007, 0, RTE_REG_EBX, 9)
FEAT_DEF(INVPCID, 0x00000007, 0, RTE_REG_EBX, 10)
FEAT_DEF(RTM, 0x00000007, 0, RTE_REG_EBX, 11)
FEAT_DEF(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16)
+ FEAT_DEF(AVX512DQ, 0x00000007, 0, RTE_REG_EBX, 17)
+ FEAT_DEF(RDSEED, 0x00000007, 0, RTE_REG_EBX, 18)
+ FEAT_DEF(AVX512IFMA, 0x00000007, 0, RTE_REG_EBX, 21)
+ FEAT_DEF(AVX512CD, 0x00000007, 0, RTE_REG_EBX, 28)
+ FEAT_DEF(AVX512BW, 0x00000007, 0, RTE_REG_EBX, 30)
+ FEAT_DEF(AVX512VL, 0x00000007, 0, RTE_REG_EBX, 31)
+
+ FEAT_DEF(AVX512VBMI, 0x00000007, 0, RTE_REG_ECX, 1)
+ FEAT_DEF(WAITPKG, 0x00000007, 0, RTE_REG_ECX, 5)
+ FEAT_DEF(AVX512VBMI2, 0x00000007, 0, RTE_REG_ECX, 6)
+ FEAT_DEF(GFNI, 0x00000007, 0, RTE_REG_ECX, 8)
+ FEAT_DEF(VAES, 0x00000007, 0, RTE_REG_ECX, 9)
+ FEAT_DEF(VPCLMULQDQ, 0x00000007, 0, RTE_REG_ECX, 10)
+ FEAT_DEF(AVX512VNNI, 0x00000007, 0, RTE_REG_ECX, 11)
+ FEAT_DEF(AVX512BITALG, 0x00000007, 0, RTE_REG_ECX, 12)
+ FEAT_DEF(AVX512VPOPCNTDQ, 0x00000007, 0, RTE_REG_ECX, 14)
+ FEAT_DEF(CLDEMOTE, 0x00000007, 0, RTE_REG_ECX, 25)
+ FEAT_DEF(MOVDIRI, 0x00000007, 0, RTE_REG_ECX, 27)
+ FEAT_DEF(MOVDIR64B, 0x00000007, 0, RTE_REG_ECX, 28)
+
+ FEAT_DEF(AVX512VP2INTERSECT, 0x00000007, 0, RTE_REG_EDX, 8)
FEAT_DEF(LAHF_SAHF, 0x80000001, 0, RTE_REG_ECX, 0)
FEAT_DEF(LZCNT, 0x80000001, 0, RTE_REG_ECX, 4)
@@ -268,55 +290,30 @@ static const struct feature_entry cpu_feature_table[] = {
FEAT_DEF(INVTSC, 0x80000007, 0, RTE_REG_EDX, 8)
};
-/*
- * Execute CPUID instruction and get contents of a specific register
- *
- * This function, when compiled with GCC, will generate architecture-neutral
- * code, as per GCC manual.
- */
-static void cpu_get_features(uint32_t leaf, uint32_t subleaf,
- cpuid_registers_t out)
-{
-#if defined(__i386__) && defined(__PIC__)
- /* %ebx is a forbidden register if we compile with -fPIC or -fPIE */
- __asm__ __volatile__("movl %%ebx,%0 ; cpuid ; xchgl %%ebx,%0"
- : "=r" (out[RTE_REG_EBX]),
- "=a" (out[RTE_REG_EAX]),
- "=c" (out[RTE_REG_ECX]),
- "=d" (out[RTE_REG_EDX])
- : "a" (leaf), "c" (subleaf));
-#else
- __asm__ __volatile__("cpuid"
- : "=a" (out[RTE_REG_EAX]),
- "=b" (out[RTE_REG_EBX]),
- "=c" (out[RTE_REG_ECX]),
- "=d" (out[RTE_REG_EDX])
- : "a" (leaf), "c" (subleaf));
-#endif
-}
-
static int cpu_get_flag_enabled(enum rte_cpu_flag_t feature)
{
const struct feature_entry *feat;
cpuid_registers_t regs;
+ unsigned int maxleaf;
if (feature >= RTE_CPUFLAG_NUMFLAGS)
/* Flag does not match anything in the feature tables */
- return -1;
+ return -ENOENT;
feat = &cpu_feature_table[feature];
if (!feat->leaf)
/* This entry in the table wasn't filled out! */
- return -1;
+ return -EFAULT;
- cpu_get_features(feat->leaf & 0xffff0000, 0, regs);
- if (((regs[RTE_REG_EAX] ^ feat->leaf) & 0xffff0000) ||
- regs[RTE_REG_EAX] < feat->leaf)
+ maxleaf = __get_cpuid_max(feat->leaf & 0x80000000, NULL);
+
+ if (maxleaf < feat->leaf)
return 0;
- /* get the cpuid leaf containing the desired feature */
- cpu_get_features(feat->leaf, feat->subleaf, regs);
+ __cpuid_count(feat->leaf, feat->subleaf,
+ regs[RTE_REG_EAX], regs[RTE_REG_EBX],
+ regs[RTE_REG_ECX], regs[RTE_REG_EDX]);
/* check if the feature is enabled */
return (regs[feat->reg] >> feat->bit) & 1;
@@ -329,7 +326,7 @@ static const char *cpu_get_flag_name(enum rte_cpu_flag_t feature)
return cpu_feature_table[feature].name;
}
-void cpu_flags_print_all(void)
+void _odp_cpu_flags_print_all(void)
{
int len, i;
int max_str = 1024;
@@ -356,12 +353,24 @@ void cpu_flags_print_all(void)
len += snprintf(&str[len], max_len - len, "\n\n");
str[len] = '\0';
- ODP_PRINT("%s", str);
+ _ODP_PRINT("%s", str);
+}
+
+int _odp_time_cpu_global_freq_is_const(void)
+{
+ if (odp_global_ro.system_info.cpu_constant_tsc ||
+ cpu_get_flag_enabled(RTE_CPUFLAG_INVTSC) > 0)
+ return 1;
+
+ _ODP_ERR("WARN: assuming constant TSC based on CPU arch, but could not confirm from CPU "
+ "flags\n");
+
+ return 1;
}
-int cpu_has_global_time(void)
+int _odp_cpu_flags_has_rdtsc(void)
{
- if (cpu_get_flag_enabled(RTE_CPUFLAG_INVTSC) > 0)
+ if (cpu_get_flag_enabled(RTE_CPUFLAG_TSC) > 0)
return 1;
return 0;
diff --git a/platform/linux-generic/arch/x86/cpu_flags.h b/platform/linux-generic/arch/x86/cpu_flags.h
index f709ca08b..8f6be6813 100644
--- a/platform/linux-generic/arch/x86/cpu_flags.h
+++ b/platform/linux-generic/arch/x86/cpu_flags.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
#ifndef ODP_PLAT_CPU_FLAGS_H_
@@ -11,7 +9,8 @@
extern "C" {
#endif
-void cpu_flags_print_all(void);
+void _odp_cpu_flags_print_all(void);
+int _odp_cpu_flags_has_rdtsc(void);
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/cpu.h b/platform/linux-generic/arch/x86/odp/api/abi/cpu.h
new file mode 100644
index 000000000..ad93cc816
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/cpu.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_API_ABI_CPU_H_
+#define ODP_API_ABI_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ODP_CACHE_LINE_SIZE 64
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/cpu_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/cpu_inlines.h b/platform/linux-generic/arch/x86/odp/api/abi/cpu_inlines.h
new file mode 100644
index 000000000..066945e7b
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/cpu_inlines.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ARCH_CPU_INLINES_H_
+#define ODP_ARCH_CPU_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <odp/api/abi/cpu_rdtsc.h>
+
+static inline void _odp_cpu_pause(void)
+{
+#ifdef __SSE2__
+ __asm__ __volatile__ ("pause");
+#else
+ __asm__ __volatile__ ("rep; nop");
+#endif
+}
+
+static inline uint64_t _odp_cpu_cycles(void)
+{
+ return _odp_cpu_rdtsc();
+}
+
+static inline uint64_t _odp_cpu_cycles_max(void)
+{
+ return UINT64_MAX;
+}
+
+static inline uint64_t _odp_cpu_cycles_resolution(void)
+{
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/cpu_rdtsc.h b/platform/linux-generic/arch/x86/odp/api/abi/cpu_rdtsc.h
new file mode 100644
index 000000000..9e6d9f278
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/cpu_rdtsc.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ARCH_CPU_RDTSC_H_
+#define ODP_ARCH_CPU_RDTSC_H_
+
+#include <stdint.h>
+
+static inline uint64_t _odp_cpu_rdtsc(void)
+{
+ union {
+ uint64_t tsc_64;
+ struct {
+ uint32_t lo_32;
+ uint32_t hi_32;
+ };
+ } tsc;
+
+ __asm__ __volatile__ ("rdtsc" :
+ "=a" (tsc.lo_32),
+ "=d" (tsc.hi_32) : : "memory");
+
+ return tsc.tsc_64;
+}
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/hash_crc32.h b/platform/linux-generic/arch/x86/odp/api/abi/hash_crc32.h
new file mode 100644
index 000000000..576b68f20
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/hash_crc32.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_HASH_CRC32_H_
+#define ODP_API_ABI_HASH_CRC32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <stdint.h>
+
+uint32_t _odp_hash_crc32_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+uint32_t _odp_hash_crc32c_generic(const void *data, uint32_t data_len,
+ uint32_t init_val);
+
+static inline uint32_t _odp_hash_crc32(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32_generic(data, data_len, init_val);
+}
+
+#ifdef __SSE4_2__
+
+static inline uint32_t _odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ uint32_t i;
+ uintptr_t pd = (uintptr_t)data;
+
+#ifdef __x86_64__
+ for (i = 0; i < data_len / 8; i++) {
+ init_val = (uint32_t)__builtin_ia32_crc32di(init_val, *(const odp_una_u64_t *)pd);
+ pd += 8;
+ }
+
+ if (data_len & 0x4) {
+ init_val = __builtin_ia32_crc32si(init_val, *(const odp_una_u32_t *)pd);
+ pd += 4;
+ }
+#else
+ for (i = 0; i < data_len / 4; i++) {
+ init_val = __builtin_ia32_crc32si(init_val, *(const odp_una_u32_t *)pd);
+ pd += 4;
+ }
+#endif
+
+ if (data_len & 0x2) {
+ init_val = __builtin_ia32_crc32hi(init_val, *(const odp_una_u16_t *)pd);
+ pd += 2;
+ }
+
+ if (data_len & 0x1)
+ init_val = __builtin_ia32_crc32qi(init_val, *(const uint8_t *)pd);
+
+ return init_val;
+}
+
+#else
+
+static inline uint32_t _odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32c_generic(data, data_len, init_val);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/sync_inlines.h b/platform/linux-generic/arch/x86/odp/api/abi/sync_inlines.h
new file mode 100644
index 000000000..bebe6b571
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/sync_inlines.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ARCH_SYNC_INLINES_H_
+#define ODP_ARCH_SYNC_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void _odp_mb_sync(void)
+{
+ __asm__ volatile("mfence" ::: "memory");
+}
+
+static inline void _odp_mb_sync_load(void)
+{
+ __asm__ volatile("lfence" ::: "memory");
+}
+
+static inline void _odp_mb_sync_store(void)
+{
+ __asm__ volatile("sfence" ::: "memory");
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/time_cpu.h b/platform/linux-generic/arch/x86/odp/api/abi/time_cpu.h
new file mode 100644
index 000000000..74b6eb740
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/time_cpu.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ARCH_TIME_CPU_H_
+#define ODP_ARCH_TIME_CPU_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <odp/api/abi/cpu_rdtsc.h>
+
+static inline uint64_t _odp_time_cpu_global(void)
+{
+ return _odp_cpu_rdtsc();
+}
+
+static inline uint64_t _odp_time_cpu_global_strict(void)
+{
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+ return _odp_cpu_rdtsc();
+}
+
+int _odp_time_cpu_global_freq_is_const(void);
+uint64_t _odp_time_cpu_global_freq(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp/api/abi/time_inlines.h b/platform/linux-generic/arch/x86/odp/api/abi/time_inlines.h
new file mode 100644
index 000000000..eed571027
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp/api/abi/time_inlines.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi/time_cpu_inlines.h>
diff --git a/platform/linux-generic/arch/x86/odp/api/cpu_arch.h b/platform/linux-generic/arch/x86/odp/api/cpu_arch.h
deleted file mode 100644
index 44e6b30ed..000000000
--- a/platform/linux-generic/arch/x86/odp/api/cpu_arch.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_CPU_ARCH_H_
-#define ODP_PLAT_CPU_ARCH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _ODP_CACHE_LINE_SIZE 64
-
-static inline void odp_cpu_pause(void)
-{
-#ifdef __SSE2__
- __asm__ __volatile__ ("pause");
-#else
- __asm__ __volatile__ ("rep; nop");
-#endif
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/arch/x86/odp_cpu.h b/platform/linux-generic/arch/x86/odp_cpu.h
new file mode 100644
index 000000000..2282285fc
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp_cpu.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_X86_CPU_H_
+#define ODP_X86_CPU_H_
+
+#define _ODP_UNALIGNED 1
+
+#include <default/odp_cpu.h>
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp_cpu_arch.c b/platform/linux-generic/arch/x86/odp_cpu_arch.c
deleted file mode 100644
index b1da428b9..000000000
--- a/platform/linux-generic/arch/x86/odp_cpu_arch.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-
-#include <odp/api/cpu.h>
-#include <odp_time_internal.h>
-#include <odp_debug_internal.h>
-
-#include <time.h>
-
-uint64_t odp_cpu_cycles(void)
-{
- union {
- uint64_t tsc_64;
- struct {
- uint32_t lo_32;
- uint32_t hi_32;
- };
- } tsc;
-
- __asm__ __volatile__ ("rdtsc" :
- "=a" (tsc.lo_32),
- "=d" (tsc.hi_32) : : "memory");
-
- return tsc.tsc_64;
-}
-
-uint64_t odp_cpu_cycles_max(void)
-{
- return UINT64_MAX;
-}
-
-uint64_t odp_cpu_cycles_resolution(void)
-{
- return 1;
-}
-
-uint64_t cpu_global_time(void)
-{
- return odp_cpu_cycles();
-}
-
-#define SEC_IN_NS 1000000000ULL
-
-/* Measure TSC frequency. Frequency information registers are defined for x86,
- * but those are often not enumerated. */
-uint64_t cpu_global_time_freq(void)
-{
- struct timespec sleep, ts1, ts2;
- uint64_t t1, t2, ts_nsec, cycles, hz;
- int i;
- uint64_t avg = 0;
- int rounds = 3;
- int warm_up = 1;
-
- for (i = 0; i < rounds; i++) {
- sleep.tv_sec = 0;
-
- if (warm_up)
- sleep.tv_nsec = SEC_IN_NS / 1000;
- else
- sleep.tv_nsec = SEC_IN_NS / 4;
-
- if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts1)) {
- ODP_DBG("clock_gettime failed\n");
- return 0;
- }
-
- t1 = cpu_global_time();
-
- if (nanosleep(&sleep, NULL) < 0) {
- ODP_DBG("nanosleep failed\n");
- return 0;
- }
-
- if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts2)) {
- ODP_DBG("clock_gettime failed\n");
- return 0;
- }
-
- t2 = cpu_global_time();
-
- ts_nsec = (ts2.tv_sec - ts1.tv_sec) * SEC_IN_NS;
- ts_nsec += ts2.tv_nsec - ts1.tv_nsec;
-
- cycles = t2 - t1;
-
- hz = (cycles * SEC_IN_NS) / ts_nsec;
-
- if (warm_up)
- warm_up = 0;
- else
- avg += hz;
- }
-
- return avg / (rounds - 1);
-}
diff --git a/platform/linux-generic/arch/x86/odp_cpu_cycles.c b/platform/linux-generic/arch/x86/odp_cpu_cycles.c
new file mode 100644
index 000000000..4950f0bad
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp_cpu_cycles.c
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp/api/cpu.h>
+
+#include "cpu_flags.h"
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+
+int _odp_cpu_cycles_init_global(void)
+{
+ if (_odp_cpu_flags_has_rdtsc() == 0) {
+ _ODP_ERR("RDTSC instruction not supported\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/arch/x86/odp_random.h b/platform/linux-generic/arch/x86/odp_random.h
new file mode 100644
index 000000000..14551f47e
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp_random.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/*
+ * These functions implement ODP_RANDOM_CRYPTO random data using rdrand [1],
+ * and ODP_RANDOM_TRUE random data using rdseed [1], via compiler builtin
+ * functions.
+ *
+ * Note that there may be issues with the quality or security of rdrand and
+ * rdseed. [2]
+ *
+ * [1] Intel Digital Random Number Generator (DRNG) Software Implementation
+ * Guide. John P Mechalas, 17 October 2018.
+ * https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html
+ *
+ * [2] RDRAND. Wikipedia, 29 September 2021.
+ * https://en.wikipedia.org/wiki/RDRAND#Reception
+ */
+
+#ifndef ODP_X86_RANDOM_H_
+#define ODP_X86_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/spec/random.h>
+
+#include <stdint.h>
+
+odp_random_kind_t _odp_random_max_kind_generic(void);
+int32_t _odp_random_true_data_generic(uint8_t *buf, uint32_t len);
+int32_t _odp_random_crypto_data_generic(uint8_t *buf, uint32_t len);
+
+#ifdef __RDRND__
+
+static inline int _odp_random_max_kind(void)
+{
+#ifdef __RDSEED__
+ return ODP_RANDOM_TRUE;
+#else
+ return ODP_RANDOM_CRYPTO;
+#endif
+}
+
+#else
+
+static inline int _odp_random_max_kind(void)
+{
+ return _odp_random_max_kind_generic();
+}
+
+#endif
+
+#ifdef __RDSEED__
+
+static inline int32_t _odp_random_true_data(uint8_t *buf, uint32_t len)
+{
+#ifdef __x86_64__
+ for (uint32_t i = 0; i < len / 8; i++) {
+ while (!__builtin_ia32_rdseed_di_step((unsigned long long *)buf))
+ ;
+ buf += 8;
+ }
+
+ if (len & 4) {
+ while (!__builtin_ia32_rdseed_si_step((unsigned int *)buf))
+ ;
+ buf += 4;
+ }
+#else
+ for (uint32_t i = 0; i < len / 4; i++) {
+ while (!__builtin_ia32_rdseed_si_step((unsigned int *)buf))
+ ;
+ buf += 4;
+ }
+#endif
+ if (len & 2) {
+ while (!__builtin_ia32_rdseed_hi_step((unsigned short int *)buf))
+ ;
+ buf += 2;
+ }
+
+ if (len & 1) {
+ uint16_t w;
+
+ while (!__builtin_ia32_rdseed_hi_step(&w))
+ ;
+ *((uint8_t *)buf) = w & 0xff;
+ }
+
+ return len;
+}
+
+#else
+
+static inline int32_t _odp_random_true_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_true_data_generic(buf, len);
+}
+
+#endif
+
+#ifdef __RDRND__
+
+static inline int32_t _odp_random_crypto_data(uint8_t *buf, uint32_t len)
+{
+#ifdef __x86_64__
+ for (uint32_t i = 0; i < len / 8; i++) {
+ while (!__builtin_ia32_rdrand64_step((unsigned long long *)buf))
+ ;
+ buf += 8;
+ }
+
+ if (len & 4) {
+ while (!__builtin_ia32_rdrand32_step((unsigned int *)buf))
+ ;
+ buf += 4;
+ }
+#else
+ for (uint32_t i = 0; i < len / 4; i++) {
+ while (!__builtin_ia32_rdrand32_step((unsigned int *)buf))
+ ;
+ buf += 4;
+ }
+#endif
+ if (len & 2) {
+ while (!__builtin_ia32_rdrand16_step((unsigned short int *)buf))
+ ;
+ buf += 2;
+ }
+
+ if (len & 1) {
+ uint16_t w;
+
+ while (!__builtin_ia32_rdrand16_step(&w))
+ ;
+ *((uint8_t *)buf) = w & 0xff;
+ }
+
+ return len;
+}
+
+#else
+
+static inline int32_t _odp_random_crypto_data(uint8_t *buf, uint32_t len)
+{
+ return _odp_random_crypto_data_generic(buf, len);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/arch/x86/odp_sysinfo_parse.c b/platform/linux-generic/arch/x86/odp_sysinfo_parse.c
index d77165a41..0a5aedfcc 100644
--- a/platform/linux-generic/arch/x86/odp_sysinfo_parse.c
+++ b/platform/linux-generic/arch/x86/odp_sysinfo_parse.c
@@ -1,45 +1,93 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
-#include <odp_internal.h>
-#include <arch/x86/cpu_flags.h>
+#include <odp_sysinfo_internal.h>
+#include <odp_string_internal.h>
+#include "cpu_flags.h"
#include <string.h>
-int cpuinfo_parser(FILE *file, system_info_t *sysinfo)
+int _odp_cpuinfo_parser(FILE *file, system_info_t *sysinfo)
{
char str[1024];
- char *pos;
+ char *pos, *pos_end;
double ghz = 0.0;
+ double mhz = 0.0;
uint64_t hz;
int id = 0;
+ bool freq_set = false;
+
+ sysinfo->cpu_arch = ODP_CPU_ARCH_X86;
+ sysinfo->cpu_isa_sw.x86 = ODP_CPU_ARCH_X86_UNKNOWN;
+ sysinfo->cpu_isa_hw.x86 = ODP_CPU_ARCH_X86_UNKNOWN;
+
+ #if defined __x86_64 || defined __x86_64__
+ sysinfo->cpu_isa_sw.x86 = ODP_CPU_ARCH_X86_64;
+ #elif defined __i686 || defined __i686__
+ sysinfo->cpu_isa_sw.x86 = ODP_CPU_ARCH_X86_I686;
+ #endif
strcpy(sysinfo->cpu_arch_str, "x86");
- while (fgets(str, sizeof(str), file) != NULL && id < MAX_CPU_NUMBER) {
+ while (fgets(str, sizeof(str), file) != NULL && id < CONFIG_NUM_CPU_IDS) {
+ if (strstr(str, "flags") && strstr(str, "constant_tsc")) {
+ sysinfo->cpu_constant_tsc = 1;
+ continue;
+ }
+
pos = strstr(str, "model name");
if (pos) {
- pos = strchr(str, ':');
- strncpy(sysinfo->model_str[id], pos + 2,
- sizeof(sysinfo->model_str[id]) - 1);
-
- pos = strchr(sysinfo->model_str[id], '@');
- if (pos) {
- *(pos - 1) = '\0';
- if (sscanf(pos, "@ %lfGHz", &ghz) == 1) {
- hz = (uint64_t)(ghz * 1000000000.0);
- sysinfo->cpu_hz_max[id] = hz;
- }
+ freq_set = false;
+
+ /* Copy model name between : and @ characters */
+ pos = strchr(str, ':');
+ pos_end = strchr(str, '@');
+ if (pos == NULL)
+ continue;
+
+ if (pos_end != NULL)
+ *(pos_end - 1) = '\0';
+
+ _odp_strcpy(sysinfo->model_str[id], pos + 2,
+ MODEL_STR_SIZE);
+
+ if (sysinfo->cpu_hz_max[id]) {
+ freq_set = true;
+ id++;
+ continue;
+ }
+
+ /* max frequency needs to be set */
+ if (pos_end != NULL &&
+ sscanf(pos_end, "@ %lfGHz", &ghz) == 1) {
+ hz = (uint64_t)(ghz * 1000000000.0);
+ sysinfo->cpu_hz_max[id++] = hz;
+ freq_set = true;
+ }
+ } else if (!freq_set &&
+ strstr(str, "bogomips") != NULL) {
+ pos = strchr(str, ':');
+ if (pos == NULL)
+ continue;
+
+ if (sscanf(pos + 2, "%lf", &mhz) == 1) {
+ /* On typical x86 BogoMIPS is freq * 2 */
+ hz = (uint64_t)(mhz * 1000000.0 / 2);
+ sysinfo->cpu_hz_max[id++] = hz;
+ freq_set = true;
}
- id++;
}
}
return 0;
}
-uint64_t odp_cpu_hz_current(int id)
+void _odp_sys_info_print_arch(void)
+{
+ _odp_cpu_flags_print_all();
+}
+
+uint64_t odp_cpu_arch_hz_current(int id)
{
char str[1024];
FILE *file;
@@ -48,6 +96,8 @@ uint64_t odp_cpu_hz_current(int id)
double mhz = 0.0;
file = fopen("/proc/cpuinfo", "rt");
+ if (!file)
+ return 0;
/* find the correct processor instance */
while (fgets(str, sizeof(str), file) != NULL) {
@@ -74,8 +124,3 @@ uint64_t odp_cpu_hz_current(int id)
return 0;
}
-
-void sys_info_print_arch(void)
-{
- cpu_flags_print_all();
-}
diff --git a/platform/linux-generic/arch/x86/odp_time_cpu.c b/platform/linux-generic/arch/x86/odp_time_cpu.c
new file mode 100644
index 000000000..ab897296d
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp_time_cpu.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2024 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/hints.h>
+#include <odp/api/time_types.h>
+
+#include <odp/api/abi/time_cpu.h>
+
+#include <odp_debug_internal.h>
+
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+
+static int nwait(uint64_t nsec)
+{
+ struct timespec ts1, ts2;
+ uint64_t diff;
+
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts1))
+ return 1;
+
+ do {
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts2))
+ return 1;
+
+ diff = (ts2.tv_sec - ts1.tv_sec) * ODP_TIME_SEC_IN_NS +
+ ts2.tv_nsec - ts1.tv_nsec;
+ } while (diff < nsec);
+
+ return 0;
+}
+
+static void sort(uint64_t values[], int num)
+{
+ for (int n = 0; n < num; n++) {
+ for (int i = n + 1; i < num; i++) {
+ if (values[i] < values[n]) {
+ uint64_t tmp = values[i];
+
+ values[i] = values[n];
+ values[n] = tmp;
+ }
+ }
+ }
+}
+
+static uint64_t median(uint64_t values[], int num)
+{
+ sort(values, num);
+ if (num % 2 == 0)
+ return (values[num / 2 - 1] + values[num / 2]) / 2;
+ else
+ return values[num / 2];
+}
+
+/* Measure TSC frequency. */
+uint64_t _odp_time_cpu_global_freq(void)
+{
+ struct timespec ts1, ts2;
+ uint64_t t1, t2, ts_nsec, cycles;
+ int i;
+ const int rounds = 6; /* first round is warmup */
+ int warm_up = 1;
+ uint64_t hz[rounds];
+
+ for (i = 0; i < rounds; i++) {
+ uint64_t wait_nsec = ODP_TIME_SEC_IN_NS / 50;
+
+ if (warm_up)
+ wait_nsec = ODP_TIME_SEC_IN_NS / 1000;
+
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts1))
+ goto err_out;
+
+ t1 = _odp_time_cpu_global();
+
+ if (nwait(wait_nsec))
+ goto err_out;
+
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts2))
+ goto err_out;
+
+ t2 = _odp_time_cpu_global();
+
+ ts_nsec = (ts2.tv_sec - ts1.tv_sec) * ODP_TIME_SEC_IN_NS;
+ ts_nsec += ts2.tv_nsec - ts1.tv_nsec;
+
+ cycles = t2 - t1;
+
+ hz[i] = (cycles * ODP_TIME_SEC_IN_NS) / ts_nsec;
+
+ if (warm_up)
+ warm_up = 0;
+ }
+
+ return median(&hz[1], rounds - 1);
+
+err_out:
+ _ODP_ERR("clock_gettime() failed (%s)\n", strerror(errno));
+ return 0;
+}
diff --git a/platform/linux-generic/check-globals.sh b/platform/linux-generic/check-globals.sh
new file mode 120000
index 000000000..c999a29ef
--- /dev/null
+++ b/platform/linux-generic/check-globals.sh
@@ -0,0 +1 @@
+../../scripts/check-globals.sh \ No newline at end of file
diff --git a/platform/linux-generic/doc/platform_specific.dox b/platform/linux-generic/doc/platform_specific.dox
index e116ec617..197e6de10 100644
--- a/platform/linux-generic/doc/platform_specific.dox
+++ b/platform/linux-generic/doc/platform_specific.dox
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -28,7 +26,7 @@
* to odp_init_local() is actually fully defined by these
* requirements: It has to be the value returned by the
* unique call to odp_init_global() made by one single
- * acsendant of the current process.
+ * ancestor of the current process.
*/
/**
diff --git a/platform/linux-generic/dumpconfig/.gitignore b/platform/linux-generic/dumpconfig/.gitignore
new file mode 100644
index 000000000..44752b565
--- /dev/null
+++ b/platform/linux-generic/dumpconfig/.gitignore
@@ -0,0 +1 @@
+odp_linuxgen_dumpconfig
diff --git a/platform/linux-generic/dumpconfig/Makefile.am b/platform/linux-generic/dumpconfig/Makefile.am
new file mode 100644
index 000000000..933424f0a
--- /dev/null
+++ b/platform/linux-generic/dumpconfig/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.inc
+
+AM_CPPFLAGS = -I$(top_builddir)/platform/$(with_platform)/include
+AM_CPPFLAGS += -I$(top_srcdir)/platform/$(with_platform)/include
+
+bin_PROGRAMS = odp_linuxgen_dumpconfig
+
+odp_linuxgen_dumpconfig_SOURCES = dumpconfig.c
+
+TESTS = odp_linuxgen_dumpconfig
diff --git a/platform/linux-generic/dumpconfig/dumpconfig.c b/platform/linux-generic/dumpconfig/dumpconfig.c
new file mode 100644
index 000000000..47ad48b70
--- /dev/null
+++ b/platform/linux-generic/dumpconfig/dumpconfig.c
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <odp_libconfig_config.h>
+
+int main(void)
+{
+ unsigned int i;
+ const char *filename;
+ FILE *f;
+ char c;
+
+ printf("# Builtin platform config\n\n");
+ for (i = 0; i < sizeof(config_builtin); i++)
+ printf("%c", config_builtin[i]);
+
+ filename = getenv("ODP_CONFIG_FILE");
+ if (filename == NULL)
+ return 0;
+
+ printf("# Overridden section with ODP_CONFIG_FILE=%s\n\n", filename);
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Error: open file %s\n", filename);
+ return -1;
+ }
+
+ while (1) {
+ c = fgetc(f);
+ if (feof(f))
+ break;
+ printf("%c", c);
+ }
+
+ fclose(f);
+ return 0;
+}
diff --git a/platform/linux-generic/example/Makefile.am b/platform/linux-generic/example/Makefile.am
new file mode 100644
index 000000000..84f337387
--- /dev/null
+++ b/platform/linux-generic/example/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS =
+
+if WITH_ML
+SUBDIRS += ml
+endif
diff --git a/platform/linux-generic/example/ml/.gitignore b/platform/linux-generic/example/ml/.gitignore
new file mode 100644
index 000000000..d845f6bb5
--- /dev/null
+++ b/platform/linux-generic/example/ml/.gitignore
@@ -0,0 +1,5 @@
+model_explorer
+simple_linear
+mnist
+*.log
+*.trs
diff --git a/platform/linux-generic/example/ml/Makefile.am b/platform/linux-generic/example/ml/Makefile.am
new file mode 100644
index 000000000..3692b704e
--- /dev/null
+++ b/platform/linux-generic/example/ml/Makefile.am
@@ -0,0 +1,46 @@
+include $(top_srcdir)/example/Makefile.inc
+
+LDADD += -lm
+
+bin_PROGRAMS = model_explorer simple_linear mnist
+
+simple_linear_SOURCES = simple_linear.c model_read.c model_read.h
+model_explorer_SOURCES = model_explorer.c model_read.c model_read.h
+mnist_SOURCES = mnist.c model_read.c model_read.h
+
+EXTRA_DIST = \
+ odp_ml_run_mnist.sh \
+ example_digit.csv \
+ mnist-12.onnx \
+ odp_ml_run_model_explorer.sh \
+ odp_ml_run_simple_linear.sh \
+ simple_linear.onnx \
+ README.md
+
+if test_example
+TESTS = \
+ odp_ml_run_mnist.sh \
+ odp_ml_run_model_explorer.sh \
+ odp_ml_run_simple_linear.sh
+endif
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/example/ml/README.md b/platform/linux-generic/example/ml/README.md
new file mode 100644
index 000000000..fc6a57c0a
--- /dev/null
+++ b/platform/linux-generic/example/ml/README.md
@@ -0,0 +1,94 @@
+# ML examples
+
+Machine Learning API examples demonstrate how to use ODP ML API in different tasks:
+for example simple linear computation and predicting a handwritten digit in
+a given image.
+
+## Simple Linear
+
+This example runs on a very simple model of form y = 3 * x + 4 where x is given
+as the second argument.
+
+### Generate model
+
+```bash
+python3 <odp_directory>/platform/linux-generic/test/validation/api/ml/simple_linear_gen.py
+```
+
+### Run simple linear
+
+```bash
+$ ./simple_linear 3
+.
+.
+.
+y = 3 * 3 + 4: 13
+.
+```
+
+Or run the program with multiple threads, each thread inferences on one x given in
+the input. Thus, the number of threads is the number of numbers in the second argument.
+
+```bash
+$ ./simple_linear [2,4,5]
+.
+.
+.
+y = 3 * 2 + 4: 10
+y = 3 * 5 + 4: 19
+y = 3 * 4 + 4: 16
+.
+```
+
+## MNIST
+
+This example predicts a handwritten digit in a given image. Refer to
+https://github.com/onnx/models/tree/main/validated/vision/classification/mnist
+for more information. The model file is from
+https://github.com/onnx/models/raw/main/validated/vision/classification/mnist/model/mnist-12.onnx
+(SPDX-License-Identifier: MIT).
+
+### Prepare input data
+
+The input image is stored in a csv file which contains, comma separated, the
+digit label (a number from 0 to 9) and the 784 pixel values (a number from 0 to
+255). Pixel order is left to right and then top down. The MNIST dataset is
+available in this format at https://www.kaggle.com/oddrationale/mnist-in-csv.
+
+### Run mnist
+
+```bash
+$ ./mnist mnist-12.onnx example_digit.csv
+.
+.
+.
+predicted_digit: 4, expected_digit: 4
+.
+```
+
+## Model Explorer
+
+The example prints basic model information.
+
+### Run model_explorer
+
+```bash
+$ ./model_explorer simple_linear.onnx
+.
+.
+.
+Model info
+----------
+ Model handle: 0x7fe8426ce1d8
+ Name: model-explorer
+ Model version: 1
+ Model interface version: 0
+ Index: 0
+ Number of inputs: 1
+ Input[0]: Name: x, Data_type: int32, Shape: static [1], Size: 4
+ Number of outputs: 1
+ Output[0]: Name: y, Data_type: int32, Shape: static [1], Size: 4
+.
+.
+.
+```
diff --git a/platform/linux-generic/example/ml/example_digit.csv b/platform/linux-generic/example/ml/example_digit.csv
new file mode 100644
index 000000000..2ab0f4a0c
--- /dev/null
+++ b/platform/linux-generic/example/ml/example_digit.csv
@@ -0,0 +1 @@
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,55,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,215,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,249,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,246,148,0,0,0,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,255,139,0,0,0,0,0,0,2,95,117,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,255,97,0,0,0,0,0,0,8,203,211,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,255,58,0,0,0,0,0,0,13,238,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,255,23,0,0,0,0,0,0,24,255,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,209,222,1,0,0,0,0,0,0,62,255,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,255,125,0,0,0,0,0,0,0,117,255,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,164,255,60,0,0,0,0,0,0,0,171,230,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,235,255,178,120,89,74,72,72,72,74,246,241,121,141,153,148,83,1,0,0,0,0,0,0,0,0,0,6,121,231,255,255,255,255,255,255,255,255,255,255,255,255,255,253,173,14,0,0,0,0,0,0,0,0,0,0,1,19,44,63,76,83,83,83,83,100,255,192,66,52,45,46,34,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,255,138,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,255,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,255,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,147,255,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,190,255,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,229,210,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,255,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,255,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,120,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
diff --git a/platform/linux-generic/example/ml/mnist-12.onnx b/platform/linux-generic/example/ml/mnist-12.onnx
new file mode 100644
index 000000000..6661bfe3c
--- /dev/null
+++ b/platform/linux-generic/example/ml/mnist-12.onnx
Binary files differ
diff --git a/platform/linux-generic/example/ml/mnist.c b/platform/linux-generic/example/ml/mnist.c
new file mode 100644
index 000000000..28720a3fd
--- /dev/null
+++ b/platform/linux-generic/example/ml/mnist.c
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include "model_read.h"
+
+/**
+ * About MNIST model used in this example.
+ *
+ * The model predicts handwritten digits. It has one input and one output whose
+ * detailed information is as follows:
+ *
+ * Input:
+ * Name: Input3, type: float32, shape: [1, 1, 28, 28]
+ *
+ * Output:
+ * Name: Plus214_Output_0, type: float32, shape: [1, 10]
+ *
+ * Refer https://github.com/onnx/models/tree/main/validated/vision/classification/mnist
+ * for more information about the model.
+ *
+ * The model outputs the likelihood of each number before softmax, so we need to
+ * map the output to probabilities across the 10 classes with softmax function.
+ *
+ * In this example, the input image is stored in example_digit.csv file, which
+ * contains, comma separated, the digit label (a number from 0 to 9) and the 784
+ * pixel values (a number from 0 to 255). Pixel order is first left to right and
+ * then top down. The MNIST dataset is available in this format at
+ * https://www.kaggle.com/oddrationale/mnist-in-csv.
+ */
+
+#define MAX_MODEL_SIZE 30000
+#define INPUT_NUM_ELEMS 784 /* Total shape for input: 1 * 1 * 28 * 28 */
+#define OUTPUT_NUM_ELEMS 10 /* Total shape for output: 1 * 10 */
+
+static int read_digit_csv(const char *file_name, uint8_t *expected_digit, float *pixels)
+{
+ char *tmp;
+ char *token;
+ char *end;
+ FILE *digit_file;
+ size_t size, num_elem;
+ const char *delim = ","; /* Delimiter */
+ size_t num_pixel = 0;
+
+ /* Get the model file size in bytes */
+ digit_file = fopen(file_name, "rb");
+ fseek(digit_file, 0, SEEK_END);
+ size = ftell(digit_file);
+ rewind(digit_file);
+
+ tmp = malloc(size + 1);
+ memset(tmp, 0, size + 1);
+ num_elem = fread(tmp, size, 1, digit_file);
+
+ fclose(digit_file);
+ if (num_elem != 1) {
+ printf("Read digit file failed\n");
+ free(tmp);
+ return -1;
+ }
+
+ /* Get the first token which is the expected digit */
+ token = strtok(tmp, delim);
+ *expected_digit = (uint8_t)strtol(token, &end, 10);
+ if ((*expected_digit > 9) || (end == token)/*No numeric character*/) {
+ printf("Invalid digit %u or no numeric character available\n",
+ *expected_digit);
+ free(tmp);
+ return -1;
+ }
+
+ /* The rest 784 numbers are pixel values */
+ token = strtok(NULL, delim);
+ while (token != NULL) {
+ pixels[num_pixel] = strtof(token, NULL);
+ num_pixel++;
+ token = strtok(NULL, delim);
+ }
+
+ if (num_pixel != INPUT_NUM_ELEMS) {
+ printf("Wrong number of pixels: %zu (expected:784)\n", num_pixel);
+ free(tmp);
+ return -1;
+ }
+
+ free(tmp);
+ return 0;
+}
+
+static int prepare_run_params(const char *file_name, uint8_t *expected_digit,
+ odp_ml_data_seg_t *input, odp_ml_data_seg_t *output)
+{
+ input->size = INPUT_NUM_ELEMS * sizeof(float);
+ input->addr = malloc(input->size);
+ memset(input->addr, 0, input->size);
+
+ if (read_digit_csv(file_name, expected_digit, input->addr)) {
+ free(input->addr);
+ return -1;
+ }
+
+ output->size = OUTPUT_NUM_ELEMS * sizeof(float);
+ output->addr = malloc(output->size);
+ memset(output->addr, 0, output->size);
+
+ return 0;
+}
+
+static float array_max(float *arr, uint8_t arr_len)
+{
+ float max = arr[0];
+
+ for (size_t i = 1; i < arr_len; i++) {
+ if (arr[i] > max)
+ max = arr[i];
+ }
+
+ return max;
+}
+
+static void softmax(float *input, uint8_t input_len)
+{
+ float rowmax = array_max(input, input_len);
+
+ float input_exp[input_len];
+ float sum = 0.0f;
+
+ for (size_t i = 0; i != input_len; ++i) {
+ input_exp[i] = exp(input[i] - rowmax);
+ sum += input_exp[i];
+ }
+
+ for (size_t i = 0; i != input_len; ++i)
+ input[i] = input_exp[i] / sum;
+}
+
+static uint8_t index_of_max(float *arr, uint8_t arr_len)
+{
+ uint8_t i = 0;
+ uint8_t max_index = 0;
+ float max = arr[0];
+
+ for (i = 1; i < arr_len; i++) {
+ if (arr[i] > max) {
+ max = arr[i];
+ max_index = i;
+ }
+ }
+
+ return max_index;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *model_file;
+ const char *input_file;
+ float *probabilities;
+ uint8_t expected_digit;
+ uint8_t predicted_digit;
+ odp_instance_t inst;
+ odp_ml_data_t data;
+ odp_ml_model_t ml_model;
+ odp_ml_data_seg_t input;
+ odp_ml_data_seg_t output;
+ odp_ml_capability_t capa;
+ odp_ml_config_t ml_config;
+ odp_ml_model_param_t model_param;
+ int ret = 0;
+
+ if (argc != 3) {
+ printf("Please provide an input image file for classification.\n"
+ "\nUsage:\n"
+ " %s model_file input_image\n"
+ "\nThis example classifies digit written on the input image.\n\n",
+ argv[0]);
+ return -1;
+ }
+
+ model_file = argv[1];
+ input_file = argv[2];
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ printf("Global init failed.\n");
+ return -1;
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ printf("Local init failed.\n");
+ return -1;
+ }
+
+ if (odp_ml_capability(&capa)) {
+ printf("odp_ml_capability() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ if (MAX_MODEL_SIZE > capa.max_model_size) {
+ printf("Configured max model size %d exceeds max mode size %" PRIu64 " in capa\n",
+ MAX_MODEL_SIZE, capa.max_model_size);
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_config_init(&ml_config);
+ ml_config.max_model_size = MAX_MODEL_SIZE;
+ ml_config.load_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+ ml_config.run_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+
+ if (odp_ml_config(&ml_config)) {
+ printf("odp_ml_config() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_param_init(&model_param);
+ if (read_model_from_file(model_file, &model_param)) {
+ printf("Read model file failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ ml_model = odp_ml_model_create("mnist", &model_param);
+ free(model_param.model);
+ if (ml_model == ODP_ML_MODEL_INVALID) {
+ printf("odp_ml_model_create() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_print(ml_model);
+
+ if (odp_ml_model_load(ml_model, NULL)) {
+ printf("odp_ml_model_load() failed\n");
+ ret = -1;
+ goto destroy_model;
+ }
+
+ data.num_input_seg = 1;
+ data.num_output_seg = 1;
+ data.input_seg = &input;
+ data.output_seg = &output;
+ if (prepare_run_params(input_file, &expected_digit, &input, &output)) {
+ printf("prepare_run_params() failed\n");
+ ret = -1;
+ goto unload;
+ }
+
+ if (odp_ml_run(ml_model, &data, NULL) != 1) {
+ printf("odp_ml_model_run() failed\n");
+ ret = -1;
+ goto free_model_io;
+ }
+
+ probabilities = output.addr;
+
+ /* Post-process the model output */
+ softmax(probabilities, OUTPUT_NUM_ELEMS);
+ predicted_digit = index_of_max(probabilities, OUTPUT_NUM_ELEMS);
+ printf("predicted_digit: %u, expected_digit: %u\n", predicted_digit, expected_digit);
+
+free_model_io:
+ free(input.addr);
+ free(output.addr);
+
+unload:
+ if (odp_ml_model_unload(ml_model, NULL)) {
+ printf("odp_ml_model_unload() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+destroy_model:
+ /* Destroy the model */
+ if (odp_ml_model_destroy(ml_model)) {
+ printf("odp_ml_model_destroy() failed\n");
+ ret = -1;
+ }
+
+odp_term:
+ if (odp_term_local()) {
+ printf("Local term failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(inst)) {
+ printf("Global term failed.\n");
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/platform/linux-generic/example/ml/model_explorer.c b/platform/linux-generic/example/ml/model_explorer.c
new file mode 100644
index 000000000..408b96c03
--- /dev/null
+++ b/platform/linux-generic/example/ml/model_explorer.c
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "model_read.h"
+
+/**
+ * Read basic model information, e.g. inputs/outputs.
+ */
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t inst;
+ odp_ml_model_t ml_model;
+ odp_ml_capability_t capa;
+ odp_ml_config_t ml_config;
+ odp_ml_model_param_t model_param;
+ int ret = 0;
+
+ if (argc != 2) {
+ printf("Please specify model path\n"
+ "\nUsage:\n"
+ " %s model_path\n"
+ "\nThis example prints model information\n\n",
+ argv[0]);
+ return -1;
+ }
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ printf("Global init failed.\n");
+ return -1;
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ printf("Local init failed.\n");
+ return -1;
+ }
+
+ if (odp_ml_capability(&capa)) {
+ printf("odp_ml_capability() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_config_init(&ml_config);
+ ml_config.max_model_size = capa.max_model_size;
+ ml_config.load_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+ ml_config.run_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+
+ if (odp_ml_config(&ml_config)) {
+ printf("odp_ml_config() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_param_init(&model_param);
+ if (read_model_from_file(argv[1], &model_param)) {
+ ret = -1;
+ goto odp_term;
+ }
+
+ ml_model = odp_ml_model_create("model-explorer", &model_param);
+ free(model_param.model);
+ if (ml_model == ODP_ML_MODEL_INVALID) {
+ printf("odp_ml_model_create failed.\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_print(ml_model);
+
+ if (odp_ml_model_destroy(ml_model)) {
+ printf("odp_ml_model_destroy failed.\n");
+ ret = -1;
+ }
+
+odp_term:
+ if (odp_term_local()) {
+ printf("Local term failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(inst)) {
+ printf("Global term failed.\n");
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/platform/linux-generic/example/ml/model_read.c b/platform/linux-generic/example/ml/model_read.c
new file mode 100644
index 000000000..7aa20bf35
--- /dev/null
+++ b/platform/linux-generic/example/ml/model_read.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <odp_api.h>
+
+#include "model_read.h"
+
+int read_model_from_file(const char *file_name, odp_ml_model_param_t *model_param)
+{
+ FILE *model_file;
+ /* Number of elements successfully read */
+ size_t num_elem;
+
+ /* Get the model file size in bytes */
+ model_file = fopen(file_name, "rb");
+ if (model_file == NULL) {
+ perror("Failed to open model file");
+ return -1;
+ }
+
+ fseek(model_file, 0, SEEK_END);
+ model_param->size = ftell(model_file);
+ rewind(model_file);
+
+ /* Allocate memory for model buffer */
+ model_param->model = malloc(model_param->size);
+ memset(model_param->model, 0, model_param->size);
+ if (!model_param->model) {
+ printf("Allocating memory for model buffer failed\n");
+ return -1;
+ }
+
+ /* Read the model file */
+ num_elem = fread(model_param->model, model_param->size, 1, model_file);
+ fclose(model_file);
+ if (num_elem != 1) {
+ printf("Read model file failed\n");
+ free(model_param->model);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/example/ml/model_read.h b/platform/linux-generic/example/ml/model_read.h
new file mode 100644
index 000000000..df2062d5f
--- /dev/null
+++ b/platform/linux-generic/example/ml/model_read.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_MODEL_READ_H_
+#define ODP_MODEL_READ_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+
+/**
+ * Read model binaries from model file
+ *
+ * @param file_name The name of model file
+ * @param model_param Model parameter where model content and size are read to
+ *
+ * @retval 0 on success
+ * @retval < 0 on failure
+ */
+int read_model_from_file(const char *file_name, odp_ml_model_param_t *model_param);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/example/ml/odp_ml_run_mnist.sh b/platform/linux-generic/example/ml/odp_ml_run_mnist.sh
new file mode 100755
index 000000000..f83d6f60d
--- /dev/null
+++ b/platform/linux-generic/example/ml/odp_ml_run_mnist.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+set -e
+
+# wget https://github.com/onnx/models/raw/main/validated/vision/classification/mnist/model/mnist-12.onnx
+./mnist${EXEEXT} mnist-12.onnx example_digit.csv
diff --git a/platform/linux-generic/example/ml/odp_ml_run_model_explorer.sh b/platform/linux-generic/example/ml/odp_ml_run_model_explorer.sh
new file mode 100755
index 000000000..7f9fed5a6
--- /dev/null
+++ b/platform/linux-generic/example/ml/odp_ml_run_model_explorer.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+set -e
+
+./model_explorer${EXEEXT} simple_linear.onnx
diff --git a/platform/linux-generic/example/ml/odp_ml_run_simple_linear.sh b/platform/linux-generic/example/ml/odp_ml_run_simple_linear.sh
new file mode 100755
index 000000000..b394b61a8
--- /dev/null
+++ b/platform/linux-generic/example/ml/odp_ml_run_simple_linear.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+set -e
+
+./simple_linear${EXEEXT} [2,4,5]
diff --git a/platform/linux-generic/example/ml/simple_linear.c b/platform/linux-generic/example/ml/simple_linear.c
new file mode 100644
index 000000000..3417219c7
--- /dev/null
+++ b/platform/linux-generic/example/ml/simple_linear.c
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "model_read.h"
+
+/**
+ * About model simple_linear.onnx used in this example.
+ *
+ * Model info:
+ * Inputs: name: x, type: int32, shape: [1]
+ * Outputs: name: y, type: int32, shape: [1]
+ *
+ * The model is of form y = 3 * x + 4 where x is given as the second argument.
+ * Thus when x = 5, the output y should be 19.
+ */
+
+#define NUM_INPUTS 1
+#define NUM_OUTPUTS 1
+#define MAX_NUM_WORKERS 10
+#define MAX_MODEL_SIZE 500
+
+typedef struct infer_param_t {
+ int32_t x;
+ odp_ml_model_t ml_model;
+} infer_param_t;
+
+typedef struct {
+ odp_shm_t shm;
+ /* Thread specific arguments */
+ infer_param_t infer_param[MAX_NUM_WORKERS];
+} thread_args_t;
+
+/* Global pointer to thread_args */
+static thread_args_t *thread_args;
+
+static int run_inference(void *infer_param)
+{
+ int32_t y;
+ odp_ml_data_t data;
+ odp_ml_data_seg_t input;
+ odp_ml_data_seg_t output;
+ infer_param_t *param = (infer_param_t *)infer_param;
+
+ data.num_input_seg = NUM_INPUTS;
+ data.input_seg = &input;
+ input.addr = &param->x;
+ input.size = sizeof(int32_t);
+
+ data.num_output_seg = NUM_OUTPUTS;
+ data.output_seg = &output;
+ output.addr = &y;
+ output.size = sizeof(int32_t);
+
+ while (1) {
+ int ret = odp_ml_run(param->ml_model, &data, NULL);
+
+ if (ret == 1)
+ break;
+
+ if (ret < 0) {
+ ODPH_ERR("odp_ml_model_run() failed: %d\n", ret);
+ return -1;
+ }
+ }
+
+ printf("y = 3 * %d + 4: %d\n", param->x, y);
+
+ return 0;
+}
+
+static int parse_argv1(char *argv1, uint32_t *num, int32_t *x)
+{
+ char *token;
+ int i;
+
+ if (!strstr(argv1, "[")) {
+ *num = 1;
+ *x = strtol(argv1, NULL, 10);
+ return 0;
+ }
+
+ token = strtok(argv1, "[,]");
+ if (token == NULL) {
+ ODPH_ERR("Invalid argv[1]\n");
+ return -1;
+ }
+ x[0] = strtol(token, NULL, 10);
+
+ for (i = 0; i < MAX_NUM_WORKERS; i++) {
+ token = strtok(NULL, "[,]");
+ if (token == NULL)
+ break;
+
+ x[i + 1] = strtol(token, NULL, 10);
+ }
+
+ if (i == MAX_NUM_WORKERS) {
+ ODPH_ERR("Too much xs, maximum number is: %d\n", MAX_NUM_WORKERS);
+ return -1;
+ }
+
+ *num = i + 1;
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_shm_t shm;
+ int num_workers;
+ odp_instance_t inst;
+ odp_cpumask_t cpumask;
+ odp_ml_model_t ml_model;
+ odp_ml_capability_t capa;
+ odp_ml_config_t ml_config;
+ int32_t x[MAX_NUM_WORKERS];
+ odp_ml_model_param_t model_param;
+ odph_thread_t thread_tbl[MAX_NUM_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_NUM_WORKERS];
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ int ret = 0;
+ uint32_t num = 0;
+
+ if (argc != 2) {
+ ODPH_ERR("Please specify x\n"
+ "\nUsage:\n"
+ " %s x\n"
+ "\nThis example runs inference on model y = 3x + 4\n\n",
+ argv[0]);
+ return -1;
+ }
+
+ if (parse_argv1(argv[1], &num, x))
+ return -1;
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ return -1;
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ return -1;
+ }
+
+ if (odp_ml_capability(&capa)) {
+ ODPH_ERR("odp_ml_capability() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ if (MAX_MODEL_SIZE > capa.max_model_size) {
+ ODPH_ERR("Configured max model size %d exceeds max mode size %" PRIu64 " in capa\n",
+ MAX_MODEL_SIZE, capa.max_model_size);
+ ret = -1;
+ goto odp_term;
+ }
+
+ /* Set ML configuration parameter */
+ odp_ml_config_init(&ml_config);
+ ml_config.max_model_size = MAX_MODEL_SIZE;
+ ml_config.load_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+ ml_config.run_mode_mask = ODP_ML_COMPL_MODE_SYNC;
+
+ if (odp_ml_config(&ml_config)) {
+ ODPH_ERR("odp_ml_config() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_param_init(&model_param);
+ if (read_model_from_file("simple_linear.onnx", &model_param)) {
+ ret = -1;
+ goto odp_term;
+ }
+
+ ml_model = odp_ml_model_create("simple linear", &model_param);
+ free(model_param.model);
+ if (ml_model == ODP_ML_MODEL_INVALID) {
+ ODPH_ERR("odp_ml_model_create() failed\n");
+ ret = -1;
+ goto odp_term;
+ }
+
+ odp_ml_model_print(ml_model);
+ odp_ml_print();
+
+ if (odp_ml_model_load(ml_model, NULL)) {
+ ODPH_ERR("odp_ml_model_load() failed\n");
+ ret = -1;
+ goto destroy_model;
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("_thread_args", sizeof(thread_args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ ret = -1;
+ goto unload;
+ }
+
+ thread_args = odp_shm_addr(shm);
+ if (thread_args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ ret = -1;
+ goto free_shm;
+ }
+ thread_args->shm = shm;
+ memset(thread_args, 0, sizeof(thread_args_t));
+
+ /* Prepare inference parameter */
+ for (uint32_t i = 0; i < num; i++) {
+ thread_args->infer_param[i].x = x[i];
+ thread_args->infer_param[i].ml_model = ml_model;
+ }
+
+ num_workers = odp_cpumask_default_worker(&cpumask, num);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ /* Create and init worker threads */
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = inst;
+ thr_common.cpumask = &cpumask;
+
+ for (int i = 0; i < num_workers; ++i) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = run_inference;
+ thr_param[i].arg = &thread_args->infer_param[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
+
+ odph_thread_join(thread_tbl, num_workers);
+
+free_shm:
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shm free global data\n");
+ return -1;
+ }
+
+unload:
+ /* Unload a model */
+ if (odp_ml_model_unload(ml_model, NULL)) {
+ ODPH_ERR("odp_ml_model_load() failed\n");
+ ret = -1;
+ }
+
+destroy_model:
+ if (odp_ml_model_destroy(ml_model)) {
+ ODPH_ERR("odp_ml_model_destroy() failed\n");
+ ret = -1;
+ }
+
+odp_term:
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("Global term failed.\n");
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/platform/linux-generic/example/ml/simple_linear.onnx b/platform/linux-generic/example/ml/simple_linear.onnx
new file mode 100644
index 000000000..45c4b95b9
--- /dev/null
+++ b/platform/linux-generic/example/ml/simple_linear.onnx
Binary files differ
diff --git a/platform/linux-generic/include-abi/odp/api/abi/align.h b/platform/linux-generic/include-abi/odp/api/abi/align.h
new file mode 100644
index 000000000..aa8f2eab7
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/align.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/align.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/atomic.h b/platform/linux-generic/include-abi/odp/api/abi/atomic.h
new file mode 100644
index 000000000..cb5d6fcc9
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/atomic.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP atomic operations
+ */
+
+#ifndef ODP_API_ABI_ATOMIC_H_
+#define ODP_API_ABI_ATOMIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/align.h>
+
+/**
+ * @internal
+ * Atomic 32-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint32_t)) odp_atomic_u32_s {
+ uint32_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u32_t;
+
+#if __GCC_ATOMIC_LLONG_LOCK_FREE >= 2
+
+/**
+ * @internal
+ * Atomic 64-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) odp_atomic_u64_s {
+ uint64_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u64_t;
+
+#else
+
+#define ODP_ATOMIC_U64_LOCK 1
+
+/**
+ * @internal
+ * Atomic 64-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) odp_atomic_u64_s {
+ uint64_t v; /**< Actual storage for the atomic variable */
+ /* Some architectures do not support lock-free operations on 64-bit
+ * data types. We use a spin lock to ensure atomicity. */
+ char lock; /**< Spin lock (if needed) used to ensure atomic access */
+} odp_atomic_u64_t;
+
+#endif
+
+#if defined(__SIZEOF_INT128__) || defined(_ODP_LOCK_FREE_128BIT_ATOMICS)
+
+/**
+ * @internal
+ * Atomic 128-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(odp_u128_t)) odp_atomic_u128_s {
+ odp_u128_t v; /**< Actual storage for the atomic variable */
+} odp_atomic_u128_t;
+
+#else
+
+/**
+ * @internal
+ * Atomic 128-bit unsigned integer
+ */
+typedef struct ODP_ALIGNED(sizeof(odp_u128_t)) odp_atomic_u128_s {
+ odp_u128_t v; /**< Actual storage for the atomic variable */
+ /* Some architectures do not support lock-free operations on 128-bit
+ * data types. We use a spin lock to ensure atomicity. */
+ char lock; /**< Spin lock (if needed) used to ensure atomic access */
+} odp_atomic_u128_t;
+
+#endif
+
+/** @addtogroup odp_atomic
+ * @{
+ */
+
+#include <odp/api/plat/atomic_inlines.h>
+
+/**
+ * @}
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/barrier.h b/platform/linux-generic/include-abi/odp/api/abi/barrier.h
new file mode 100644
index 000000000..6e804ce31
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/barrier.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/barrier.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/buffer.h b/platform/linux-generic/include-abi/odp/api/abi/buffer.h
new file mode 100644
index 000000000..51759e974
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/buffer.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP buffer
+ */
+
+#ifndef ODP_API_ABI_BUFFER_H_
+#define ODP_API_ABI_BUFFER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined API functions */
+#include <odp/api/plat/buffer_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/buffer_types.h b/platform/linux-generic/include-abi/odp/api/abi/buffer_types.h
new file mode 100644
index 000000000..da7a6532d
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/buffer_types.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP buffer types
+ */
+
+#ifndef ODP_API_ABI_BUFFER_TYPES_H_
+#define ODP_API_ABI_BUFFER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_buffer
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_buffer_t);
+
+#define ODP_BUFFER_INVALID _odp_cast_scalar(odp_buffer_t, 0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/byteorder.h b/platform/linux-generic/include-abi/odp/api/abi/byteorder.h
new file mode 100644
index 000000000..e9cebff02
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/byteorder.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP byteorder
+ */
+
+#ifndef ODP_API_ABI_BYTEORDER_H_
+#define ODP_API_ABI_BYTEORDER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+#ifndef __BYTE_ORDER__
+#error __BYTE_ORDER__ not defined!
+#endif
+
+#ifndef __ORDER_BIG_ENDIAN__
+#error __ORDER_BIG_ENDIAN__ not defined!
+#endif
+
+#ifndef __ORDER_LITTLE_ENDIAN__
+#error __ORDER_LITTLE_ENDIAN__ not defined!
+#endif
+
+/* for use with type checkers such as sparse */
+#ifdef __CHECKER__
+/** @internal bitwise attribute */
+#define __odp_bitwise __attribute__((bitwise))
+/** @internal force attribute */
+#define __odp_force __attribute__((force))
+#else
+/** @internal bitwise attribute */
+#define __odp_bitwise
+/** @internal force attribute */
+#define __odp_force
+#endif
+
+/** @addtogroup odp_compiler_optim
+ * @{
+ */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define ODP_LITTLE_ENDIAN 1
+ #define ODP_BIG_ENDIAN 0
+ #define ODP_BYTE_ORDER ODP_LITTLE_ENDIAN
+ #define ODP_LITTLE_ENDIAN_BITFIELD 1
+ #define ODP_BIG_ENDIAN_BITFIELD 0
+ #define ODP_BITFIELD_ORDER ODP_LITTLE_ENDIAN_BITFIELD
+#else
+ #define ODP_LITTLE_ENDIAN 0
+ #define ODP_BIG_ENDIAN 1
+ #define ODP_BYTE_ORDER ODP_BIG_ENDIAN
+ #define ODP_LITTLE_ENDIAN_BITFIELD 0
+ #define ODP_BIG_ENDIAN_BITFIELD 1
+ #define ODP_BITFIELD_ORDER ODP_BIG_ENDIAN_BITFIELD
+#endif
+
+typedef uint16_t __odp_bitwise odp_u16le_t;
+typedef uint16_t __odp_bitwise odp_u16be_t;
+
+typedef uint32_t __odp_bitwise odp_u32le_t;
+typedef uint32_t __odp_bitwise odp_u32be_t;
+
+typedef uint64_t __odp_bitwise odp_u64le_t;
+typedef uint64_t __odp_bitwise odp_u64be_t;
+
+typedef uint16_t __odp_bitwise odp_u16sum_t;
+typedef uint32_t __odp_bitwise odp_u32sum_t;
+
+#include <odp/api/plat/byteorder_inlines.h>
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/classification.h b/platform/linux-generic/include-abi/odp/api/abi/classification.h
new file mode 100644
index 000000000..e67f585ac
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/classification.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP classification descriptor
+ */
+
+#ifndef ODP_API_ABI_CLASSIFICATION_H_
+#define ODP_API_ABI_CLASSIFICATION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_classification
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_cos_t);
+#define ODP_COS_INVALID _odp_cast_scalar(odp_cos_t, 0)
+
+typedef ODP_HANDLE_T(odp_pmr_t);
+#define ODP_PMR_INVALID _odp_cast_scalar(odp_pmr_t, 0)
+
+#define ODP_COS_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/comp.h b/platform/linux-generic/include-abi/odp/api/abi/comp.h
new file mode 100644
index 000000000..590950aef
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/comp.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ABI_COMP_H_
+#define ODP_ABI_COMP_H_
+
+#include <odp/api/plat/strong_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @addtogroup odp_compression
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_comp_session_t);
+
+#define ODP_COMP_SESSION_INVALID _odp_cast_scalar(odp_comp_session_t, 0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/cpumask.h b/platform/linux-generic/include-abi/odp/api/abi/cpumask.h
new file mode 100644
index 000000000..696f8664f
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/cpumask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/cpumask.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/crypto.h b/platform/linux-generic/include-abi/odp/api/abi/crypto.h
new file mode 100644
index 000000000..3be9b138c
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/crypto.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_API_ABI_CRYPTO_H_
+#define ODP_API_ABI_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined API functions */
+#include <odp/api/plat/crypto_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/crypto_types.h b/platform/linux-generic/include-abi/odp/api/abi/crypto_types.h
new file mode 100644
index 000000000..b37a78f86
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/crypto_types.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_API_ABI_CRYPTO_TYPES_H_
+#define ODP_API_ABI_CRYPTO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_crypto
+ * @{
+ */
+
+#define ODP_CRYPTO_SESSION_INVALID (0xffffffffffffffffULL)
+
+typedef uint64_t odp_crypto_session_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/debug.h b/platform/linux-generic/include-abi/odp/api/abi/debug.h
new file mode 100644
index 000000000..b82e2d171
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/debug.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP atomic operations
+ */
+
+#ifndef ODP_API_ABI_DEBUG_H_
+#define ODP_API_ABI_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal _Static_assert was only added in GCC 4.6 and the C++ version
+ * static_assert for g++ 6 and above. Provide a weak replacement for previous
+ * versions.
+ */
+#define _odp_merge(a, b) a##b
+/** @internal */
+#define _odp_label(a) _odp_merge(_ODP_SASSERT_, a)
+/** @internal */
+#define _ODP_SASSERT _odp_label(__COUNTER__)
+/** @internal */
+#define _ODP_SASSERT_ENUM(e) { _ODP_SASSERT = 1 / !!(e) }
+/** @internal */
+#define _odp_static_assert(e, s) enum _ODP_SASSERT_ENUM(e)
+
+#if defined(__clang__)
+#if defined(__cplusplus)
+#if !__has_feature(cxx_static_assert) && !defined(static_assert)
+/** @internal */
+#define static_assert(e, s) _odp_static_assert(e, s)
+#endif
+#elif !__has_feature(c_static_assert) && !defined(_Static_assert)
+/** @internal */
+#define _Static_assert(e, s) _odp_static_assert(e, s)
+#endif
+
+#elif defined(__GNUC__)
+#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6)) || \
+ (__GNUC__ < 6 && defined(__cplusplus))
+#if defined(__cplusplus)
+#if !defined(static_assert)
+/** @intenral */
+#define static_assert(e, s) _odp_static_assert(e, s)
+#endif
+#elif !defined(_Static_assert)
+/** @internal */
+#define _Static_assert(e, s) _odp_static_assert(e, s)
+#endif
+#endif
+
+#endif
+
+#include <odp/api/abi-default/debug.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/dma.h b/platform/linux-generic/include-abi/odp/api/abi/dma.h
new file mode 100644
index 000000000..fb40d38c3
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/dma.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP DMA
+ */
+
+#ifndef ODP_API_ABI_DMA_H_
+#define ODP_API_ABI_DMA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/dma_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/dma_types.h b/platform/linux-generic/include-abi/odp/api/abi/dma_types.h
new file mode 100644
index 000000000..4ad113ddd
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/dma_types.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_API_ABI_DMA_TYPES_H_
+#define ODP_API_ABI_DMA_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_dma
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_dma_t);
+
+#define ODP_DMA_INVALID _odp_cast_scalar(odp_dma_t, 0)
+
+typedef uint32_t odp_dma_transfer_id_t;
+
+#define ODP_DMA_TRANSFER_ID_INVALID ((odp_dma_transfer_id_t)0)
+
+typedef ODP_HANDLE_T(odp_dma_compl_t);
+
+#define ODP_DMA_COMPL_INVALID _odp_cast_scalar(odp_dma_compl_t, 0)
+
+#define ODP_DMA_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/errno.h b/platform/linux-generic/include-abi/odp/api/abi/errno.h
new file mode 100644
index 000000000..fdfd03e3a
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/errno.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP errno
+ */
+
+#ifndef ODP_API_ABI_ERRNO_H_
+#define ODP_API_ABI_ERRNO_H_
+
+#include <odp/api/abi-default/errno.h>
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/event.h b/platform/linux-generic/include-abi/odp/api/abi/event.h
new file mode 100644
index 000000000..ca2901c87
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/event.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event
+ */
+
+#ifndef ODP_API_ABI_EVENT_H_
+#define ODP_API_ABI_EVENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/event_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/event_types.h b/platform/linux-generic/include-abi/odp/api/abi/event_types.h
new file mode 100644
index 000000000..4beafa6b7
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/event_types.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event type definitions
+ */
+
+#ifndef ODP_API_ABI_EVENT_TYPES_H_
+#define ODP_API_ABI_EVENT_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_event
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_event_t);
+
+#define ODP_EVENT_INVALID _odp_cast_scalar(odp_event_t, 0)
+
+typedef enum odp_event_type_t {
+ ODP_EVENT_BUFFER = 1,
+ ODP_EVENT_PACKET = 2,
+ ODP_EVENT_TIMEOUT = 3,
+ ODP_EVENT_IPSEC_STATUS = 5,
+ ODP_EVENT_PACKET_VECTOR = 6,
+ ODP_EVENT_PACKET_TX_COMPL = 7,
+ ODP_EVENT_DMA_COMPL = 8,
+ ODP_EVENT_ML_COMPL = 9
+} odp_event_type_t;
+
+typedef enum odp_event_subtype_t {
+ ODP_EVENT_NO_SUBTYPE = 0,
+ ODP_EVENT_PACKET_BASIC = 1,
+ ODP_EVENT_PACKET_CRYPTO = 2,
+ ODP_EVENT_PACKET_IPSEC = 3,
+ ODP_EVENT_PACKET_COMP = 4,
+ ODP_EVENT_ML_COMPL_LOAD = 5,
+ ODP_EVENT_ML_COMPL_RUN = 6
+} odp_event_subtype_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/hash.h b/platform/linux-generic/include-abi/odp/api/abi/hash.h
new file mode 100644
index 000000000..fe61a2111
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/hash.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Marvell
+ */
+
+/**
+ * @file
+ *
+ * ODP hash
+ */
+
+#ifndef ODP_API_ABI_HASH_H_
+#define ODP_API_ABI_HASH_H_
+
+#include <odp/api/abi-default/hash.h>
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/hash_inlines.h>
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/init.h b/platform/linux-generic/include-abi/odp/api/abi/init.h
new file mode 100644
index 000000000..936644f46
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/init.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/ipsec.h b/platform/linux-generic/include-abi/odp/api/abi/ipsec.h
new file mode 100644
index 000000000..40ae34b17
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/ipsec.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP IPsec
+ */
+
+#ifndef ODP_API_ABI_IPSEC_H_
+#define ODP_API_ABI_IPSEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined API functions */
+#include <odp/api/plat/ipsec_inlines.h>
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/ipsec_types.h b/platform/linux-generic/include-abi/odp/api/abi/ipsec_types.h
new file mode 100644
index 000000000..50a859d3c
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/ipsec_types.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP IPsec platform specific types
+ */
+
+#ifndef ODP_API_ABI_IPSEC_TYPES_H_
+#define ODP_API_ABI_IPSEC_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_ipsec
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_ipsec_sa_t);
+
+#define ODP_IPSEC_SA_INVALID _odp_cast_scalar(odp_ipsec_sa_t, 0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/ml_types.h b/platform/linux-generic/include-abi/odp/api/abi/ml_types.h
new file mode 100644
index 000000000..0fdb7a8dc
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/ml_types.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef ODP_API_ABI_ML_TYPES_H_
+#define ODP_API_ABI_ML_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @internal Implementation specific ML parameters */
+struct _odp_ml_model_extra_param_t {
+ /** @internal Dummy field to avoid empty struct */
+ char dummy;
+};
+
+/** @addtogroup odp_ml
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_ml_model_t);
+typedef ODP_HANDLE_T(odp_ml_compl_t);
+typedef struct _odp_ml_model_extra_param_t odp_ml_model_extra_param_t;
+
+#define ODP_ML_MODEL_INVALID _odp_cast_scalar(odp_ml_model_t, 0)
+#define ODP_ML_COMPL_INVALID _odp_cast_scalar(odp_ml_compl_t, 0)
+
+#define ODP_ML_MODEL_NAME_LEN 64
+#define ODP_ML_MODEL_IO_NAME_LEN 64
+#define ODP_ML_SHAPE_NAME_LEN 16
+#define ODP_ML_EXTRA_STAT_NAME_LEN 64
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/packet.h b/platform/linux-generic/include-abi/odp/api/abi/packet.h
new file mode 100644
index 000000000..e3ada64a0
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/packet.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_API_ABI_PACKET_H_
+#define ODP_API_ABI_PACKET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_vector_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/packet_flags.h b/platform/linux-generic/include-abi/odp/api/abi/packet_flags.h
new file mode 100644
index 000000000..22cae8e1b
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/packet_flags.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_API_ABI_PACKET_FLAGS_H_
+#define ODP_API_ABI_PACKET_FLAGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/packet_flag_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/packet_io.h b/platform/linux-generic/include-abi/odp/api/abi/packet_io.h
new file mode 100644
index 000000000..d60d29d3c
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/packet_io.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO
+ */
+
+#ifndef ODP_API_ABI_PACKET_IO_H_
+#define ODP_API_ABI_PACKET_IO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/packet_io_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/packet_io_types.h b/platform/linux-generic/include-abi/odp/api/abi/packet_io_types.h
new file mode 100644
index 000000000..7d66e9da5
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/packet_io_types.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO types
+ */
+
+#ifndef ODP_API_ABI_PACKET_IO_TYPES_H_
+#define ODP_API_ABI_PACKET_IO_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_packet_io
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_pktio_t);
+typedef ODP_HANDLE_T(odp_lso_profile_t);
+
+/** @internal */
+typedef struct odp_pktin_queue_t {
+ odp_pktio_t pktio; /**< @internal pktio handle */
+ int index; /**< @internal pktio queue index */
+} odp_pktin_queue_t;
+
+/** @internal */
+typedef struct odp_pktout_queue_t {
+ odp_pktio_t pktio; /**< @internal pktio handle */
+ int index; /**< @internal pktio queue index */
+} odp_pktout_queue_t;
+
+#define ODP_PKTIO_INVALID _odp_cast_scalar(odp_pktio_t, 0)
+#define ODP_LSO_PROFILE_INVALID _odp_cast_scalar(odp_lso_profile_t, 0)
+
+#define ODP_PKTIO_MAX_INDEX 63
+
+#define ODP_PKTIO_MACADDR_MAXSIZE 16
+
+#define ODP_PKTIN_NO_WAIT 0
+
+#define ODP_PKTIN_MAX_QUEUES 64
+
+#define ODP_PKTOUT_MAX_QUEUES 64
+
+#define ODP_PKTIO_STATS_EXTRA_NAME_LEN 64
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/packet_types.h b/platform/linux-generic/include-abi/odp/api/abi/packet_types.h
new file mode 100644
index 000000000..7ffdec678
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/packet_types.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_API_ABI_PACKET_TYPES_H_
+#define ODP_API_ABI_PACKET_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_packet
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_packet_t);
+
+#define ODP_PACKET_INVALID _odp_cast_scalar(odp_packet_t, 0)
+
+#define ODP_PACKET_OFFSET_INVALID 0xffff
+
+typedef ODP_HANDLE_T(odp_packet_seg_t);
+
+#define ODP_PACKET_SEG_INVALID _odp_cast_scalar(odp_packet_seg_t, 0)
+
+typedef ODP_HANDLE_T(odp_packet_buf_t);
+
+#define ODP_PACKET_BUF_INVALID _odp_cast_scalar(odp_packet_buf_t, 0)
+
+typedef ODP_HANDLE_T(odp_packet_vector_t);
+
+#define ODP_PACKET_VECTOR_INVALID _odp_cast_scalar(odp_packet_vector_t, 0)
+
+typedef ODP_HANDLE_T(odp_packet_tx_compl_t);
+
+#define ODP_PACKET_TX_COMPL_INVALID _odp_cast_scalar(odp_packet_tx_compl_t, 0)
+
+#define ODP_PACKET_OFFSET_INVALID 0xffff
+
+typedef enum {
+ ODP_PACKET_GREEN = 0,
+ ODP_PACKET_YELLOW = 1,
+ ODP_PACKET_RED = 2,
+ ODP_PACKET_ALL_COLORS = 3,
+} odp_packet_color_t;
+
+typedef enum {
+ ODP_PACKET_CHKSUM_UNKNOWN = 0,
+ ODP_PACKET_CHKSUM_BAD,
+ ODP_PACKET_CHKSUM_OK
+} odp_packet_chksum_status_t;
+
+typedef struct odp_packet_parse_result_flag_t {
+ union {
+ uint64_t all;
+
+ struct {
+ uint64_t has_error : 1;
+ uint64_t has_l2_error : 1;
+ uint64_t has_l3_error : 1;
+ uint64_t has_l4_error : 1;
+ uint64_t has_l2 : 1;
+ uint64_t has_l3 : 1;
+ uint64_t has_l4 : 1;
+ uint64_t has_eth : 1;
+ uint64_t has_eth_bcast : 1;
+ uint64_t has_eth_mcast : 1;
+ uint64_t has_jumbo : 1;
+ uint64_t has_vlan : 1;
+ uint64_t has_vlan_qinq : 1;
+ uint64_t has_arp : 1;
+ uint64_t has_ipv4 : 1;
+ uint64_t has_ipv6 : 1;
+ uint64_t has_ip_bcast : 1;
+ uint64_t has_ip_mcast : 1;
+ uint64_t has_ipfrag : 1;
+ uint64_t has_ipopt : 1;
+ uint64_t has_ipsec : 1;
+ uint64_t has_udp : 1;
+ uint64_t has_tcp : 1;
+ uint64_t has_sctp : 1;
+ uint64_t has_icmp : 1;
+ };
+ };
+
+} odp_packet_parse_result_flag_t;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/pool.h b/platform/linux-generic/include-abi/odp/api/abi/pool.h
new file mode 100644
index 000000000..fd5cd04ec
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/pool.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP pool
+ */
+
+#ifndef ODP_API_ABI_POOL_H_
+#define ODP_API_ABI_POOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined API functions */
+#include <odp/api/plat/pool_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/pool_types.h b/platform/linux-generic/include-abi/odp/api/abi/pool_types.h
new file mode 100644
index 000000000..143a1c4ee
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/pool_types.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP pool
+ */
+
+#ifndef ODP_API_ABI_POOL_TYPES_H_
+#define ODP_API_ABI_POOL_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_pool
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_pool_t);
+
+#define ODP_POOL_INVALID _odp_cast_scalar(odp_pool_t, 0)
+
+#define ODP_POOL_NAME_LEN 32
+
+#define ODP_POOL_MAX_THREAD_STATS 128
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/proto_stats.h b/platform/linux-generic/include-abi/odp/api/abi/proto_stats.h
new file mode 100644
index 000000000..f9399407d
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/proto_stats.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP proto stats
+ */
+
+#ifndef ODP_API_ABI_PROTO_STATS_H_
+#define ODP_API_ABI_PROTO_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Placeholder for inlined functions for non-ABI compat mode */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/proto_stats_types.h b/platform/linux-generic/include-abi/odp/api/abi/proto_stats_types.h
new file mode 100644
index 000000000..0404ace05
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/proto_stats_types.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP proto stats types
+ */
+
+#ifndef ODP_API_ABI_PROTO_STATS_TYPES_H_
+#define ODP_API_ABI_PROTO_STATS_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_proto_stats
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_proto_stats_t);
+
+#define ODP_PROTO_STATS_INVALID _odp_cast_scalar(odp_proto_stats_t, 0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/queue.h b/platform/linux-generic/include-abi/odp/api/abi/queue.h
new file mode 100644
index 000000000..59cbe9bb6
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/queue.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP queue
+ */
+
+#ifndef ODP_API_ABI_QUEUE_H_
+#define ODP_API_ABI_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/queue_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/queue_types.h b/platform/linux-generic/include-abi/odp/api/abi/queue_types.h
new file mode 100644
index 000000000..18ab36286
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/queue_types.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP queue
+ */
+
+#ifndef ODP_API_ABI_QUEUE_TYPES_H_
+#define ODP_API_ABI_QUEUE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_queue
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_queue_t);
+
+#define ODP_QUEUE_INVALID _odp_cast_scalar(odp_queue_t, 0)
+
+#define ODP_QUEUE_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/random.h b/platform/linux-generic/include-abi/odp/api/abi/random.h
new file mode 100644
index 000000000..89745378b
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/random.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP random
+ */
+
+#ifndef ODP_API_ABI_RANDOM_H_
+#define ODP_API_ABI_RANDOM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty placeholder header for function inlining */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/rwlock.h b/platform/linux-generic/include-abi/odp/api/abi/rwlock.h
new file mode 100644
index 000000000..8194aa649
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/rwlock.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/rwlock_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/rwlock_recursive.h b/platform/linux-generic/include-abi/odp/api/abi/rwlock_recursive.h
new file mode 100644
index 000000000..fb9142332
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/rwlock_recursive.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/rwlock_recursive.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/rwlock_recursive_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/schedule.h b/platform/linux-generic/include-abi/odp/api/abi/schedule.h
new file mode 100644
index 000000000..c65aa6a22
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/schedule.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Schedule
+ */
+
+#ifndef ODP_API_ABI_SCHEDULE_H_
+#define ODP_API_ABI_SCHEDULE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Inlined API functions */
+#include <odp/api/plat/schedule_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/schedule_types.h b/platform/linux-generic/include-abi/odp/api/abi/schedule_types.h
new file mode 100644
index 000000000..1b2990c2b
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/schedule_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/schedule_types.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/shared_memory.h b/platform/linux-generic/include-abi/odp/api/abi/shared_memory.h
new file mode 100644
index 000000000..a7ef9c5a9
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/shared_memory.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+
+/**
+ * @file
+ *
+ * ODP shared memory
+ */
+
+#ifndef ODP_API_ABI_SHARED_MEMORY_H_
+#define ODP_API_ABI_SHARED_MEMORY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_shared_memory
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_shm_t);
+
+#define ODP_SHM_INVALID _odp_cast_scalar(odp_shm_t, 0)
+
+#define ODP_SHM_NAME_LEN 32
+
+#define ODP_SHM_IOVA_INVALID ((uint64_t)-1)
+#define ODP_SHM_PA_INVALID ODP_SHM_IOVA_INVALID
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/spinlock.h b/platform/linux-generic/include-abi/odp/api/abi/spinlock.h
new file mode 100644
index 000000000..22f093ab5
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/spinlock.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock.h>
+
+/* Include inlined versions of API functions */
+#include <odp/api/plat/spinlock_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/spinlock_recursive.h b/platform/linux-generic/include-abi/odp/api/abi/spinlock_recursive.h
new file mode 100644
index 000000000..f7d2e5212
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/spinlock_recursive.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/spinlock_recursive.h>
+
+/* Include inlined versions of API functions */
+#include <odp/api/plat/spinlock_recursive_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/stash.h b/platform/linux-generic/include-abi/odp/api/abi/stash.h
new file mode 100644
index 000000000..f28f8ee96
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/stash.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+/**
+ * @file
+ */
+
+#ifndef ODP_API_ABI_STASH_H_
+#define ODP_API_ABI_STASH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Empty placeholder header for inline functions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/stash_types.h b/platform/linux-generic/include-abi/odp/api/abi/stash_types.h
new file mode 100644
index 000000000..140f5a2ef
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/stash_types.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @file
+ */
+
+#ifndef ODP_API_ABI_STASH_TYPES_H_
+#define ODP_API_ABI_STASH_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_stash
+ * @{
+ */
+
+typedef ODP_HANDLE_T(odp_stash_t);
+
+#define ODP_STASH_INVALID _odp_cast_scalar(odp_stash_t, 0)
+
+#define ODP_STASH_NAME_LEN 32
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/std.h b/platform/linux-generic/include-abi/odp/api/abi/std.h
new file mode 100644
index 000000000..639e730b4
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/std.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP barrier
+ */
+
+#ifndef ODP_API_ABI_STD_H_
+#define ODP_API_ABI_STD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/std_inlines.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/std_types.h b/platform/linux-generic/include-abi/odp/api/abi/std_types.h
new file mode 100644
index 000000000..c0d930e1f
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/std_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/std_types.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/sync.h b/platform/linux-generic/include-abi/odp/api/abi/sync.h
new file mode 100644
index 000000000..b1053f478
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/sync.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP barrier
+ */
+
+#ifndef ODP_API_ABI_SYNC_H_
+#define ODP_API_ABI_SYNC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @addtogroup odp_barrier
+ * @{
+ */
+
+#include <odp/api/plat/sync_inlines.h>
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/thread.h b/platform/linux-generic/include-abi/odp/api/abi/thread.h
new file mode 100644
index 000000000..31145dade
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/thread.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/* Inlined API functions */
+#include <odp/api/plat/thread_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/thread_types.h b/platform/linux-generic/include-abi/odp/api/abi/thread_types.h
new file mode 100644
index 000000000..e695c233b
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/thread_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/thread_types.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/thrmask.h b/platform/linux-generic/include-abi/odp/api/abi/thrmask.h
new file mode 100644
index 000000000..2bb61d233
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/thrmask.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/thrmask.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/ticketlock.h b/platform/linux-generic/include-abi/odp/api/abi/ticketlock.h
new file mode 100644
index 000000000..1a9322264
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/ticketlock.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP ticketlock
+ */
+
+#ifndef ODP_API_ABI_TICKETLOCK_H_
+#define ODP_API_ABI_TICKETLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/atomic.h>
+
+/** @addtogroup odp_locks
+ * @{
+ */
+
+/** @internal */
+typedef struct odp_ticketlock_s {
+ odp_atomic_u32_t next_ticket; /**< Next ticket */
+ odp_atomic_u32_t cur_ticket; /**< Current ticket */
+} odp_ticketlock_t;
+
+/* Include inlined versions of API functions */
+#include <odp/api/plat/ticketlock_inlines.h>
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/time.h b/platform/linux-generic/include-abi/odp/api/abi/time.h
new file mode 100644
index 000000000..d641ddda5
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/time.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/* Inlined API functions */
+#include <odp/api/plat/time_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/time_types.h b/platform/linux-generic/include-abi/odp/api/abi/time_types.h
new file mode 100644
index 000000000..a415200a0
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/time_types.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/abi-default/time_types.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/timer.h b/platform/linux-generic/include-abi/odp/api/abi/timer.h
new file mode 100644
index 000000000..f9209591f
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/timer.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/* Inlined functions for non-ABI compat mode */
+#include <odp/api/plat/timer_inlines.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/timer_types.h b/platform/linux-generic/include-abi/odp/api/abi/timer_types.h
new file mode 100644
index 000000000..efcf25fcc
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/timer_types.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+
+/**
+ * @file
+ *
+ * ODP timer service
+ */
+
+#ifndef ODP_API_ABI_TIMER_TYPES_H_
+#define ODP_API_ABI_TIMER_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+
+/** @addtogroup odp_timer
+ * @{
+ **/
+
+typedef ODP_HANDLE_T(odp_timer_pool_t);
+
+#define ODP_TIMER_POOL_INVALID _odp_cast_scalar(odp_timer_pool_t, 0)
+
+#define ODP_TIMER_POOL_NAME_LEN 32
+
+typedef ODP_HANDLE_T(odp_timer_t);
+
+#define ODP_TIMER_INVALID _odp_cast_scalar(odp_timer_t, 0)
+
+typedef ODP_HANDLE_T(odp_timeout_t);
+
+#define ODP_TIMEOUT_INVALID _odp_cast_scalar(odp_timeout_t, 0)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include-abi/odp/api/abi/traffic_mngr.h b/platform/linux-generic/include-abi/odp/api/abi/traffic_mngr.h
new file mode 100644
index 000000000..9a457c370
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/traffic_mngr.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/traffic_mngr.h>
diff --git a/platform/linux-generic/include-abi/odp/api/abi/version.h b/platform/linux-generic/include-abi/odp/api/abi/version.h
new file mode 100644
index 000000000..cf4b9a128
--- /dev/null
+++ b/platform/linux-generic/include-abi/odp/api/abi/version.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/abi-default/version.h>
diff --git a/platform/linux-generic/include/_fdserver_internal.h b/platform/linux-generic/include/_fdserver_internal.h
deleted file mode 100644
index 22b280287..000000000
--- a/platform/linux-generic/include/_fdserver_internal.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _FD_SERVER_INTERNAL_H
-#define _FD_SERVER_INTERNAL_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * the following enum defines the different contextes by which the
- * FD server may be used: In the FD server, the keys used to store/retrieve
- * a file descriptor are actually context based:
- * Both the context and the key are stored at fd registration time,
- * and both the context and the key are used to retrieve a fd.
- * In other words a context identifies a FD server usage, so that different
- * unrelated fd server users do not have to guarantee key unicity between
- * them.
- */
-typedef enum fd_server_context {
- FD_SRV_CTX_NA, /* Not Applicable */
- FD_SRV_CTX_ISHM,
- FD_SRV_CTX_END, /* upper enum limit */
-} fd_server_context_e;
-
-int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
- int fd);
-int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key);
-int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/_ishm_internal.h b/platform/linux-generic/include/_ishm_internal.h
deleted file mode 100644
index 005d6b551..000000000
--- a/platform/linux-generic/include/_ishm_internal.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_ISHM_INTERNAL_H_
-#define ODP_ISHM_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <sys/types.h>
-
-/* flags available at ishm_reserve: */
-#define _ODP_ISHM_SINGLE_VA 1
-#define _ODP_ISHM_LOCK 2
-#define _ODP_ISHM_EXPORT 4 /*create export descr file in /tmp */
-
-/**
- * Shared memory block info
- */
-typedef struct _odp_ishm_info_t {
- const char *name; /**< Block name */
- void *addr; /**< Block address */
- uint64_t size; /**< Block size in bytes */
- uint64_t page_size; /**< Memory page size */
- uint32_t flags; /**< _ODP_ISHM_* flags */
- uint32_t user_flags;/**< user specific flags */
-} _odp_ishm_info_t;
-
-int _odp_ishm_reserve(const char *name, uint64_t size, int fd, uint32_t align,
- uint32_t flags, uint32_t user_flags);
-int _odp_ishm_free_by_index(int block_index);
-int _odp_ishm_free_by_name(const char *name);
-int _odp_ishm_free_by_address(void *addr);
-void *_odp_ishm_lookup_by_index(int block_index);
-int _odp_ishm_lookup_by_name(const char *name);
-int _odp_ishm_lookup_by_address(void *addr);
-int _odp_ishm_find_exported(const char *remote_name,
- pid_t external_odp_pid,
- const char *local_name);
-void *_odp_ishm_address(int block_index);
-int _odp_ishm_info(int block_index, _odp_ishm_info_t *info);
-int _odp_ishm_status(const char *title);
-int _odp_ishm_cleanup_files(const char *dirpath);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/_ishmphy_internal.h b/platform/linux-generic/include/_ishmphy_internal.h
deleted file mode 100644
index 4fe560fd2..000000000
--- a/platform/linux-generic/include/_ishmphy_internal.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ISHMPHY_INTERNAL_H
-#define _ISHMPHY_INTERNAL_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align);
-int _odp_ishmphy_unbook_va(void);
-void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags);
-int _odp_ishmphy_unmap(void *start, uint64_t len, int flags);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/ishmphy_internal.h b/platform/linux-generic/include/ishmphy_internal.h
index 0bc4207af..f979825c5 100644
--- a/platform/linux-generic/include/ishmphy_internal.h
+++ b/platform/linux-generic/include/ishmphy_internal.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#ifndef _ISHMPHY_INTERNAL_H_
diff --git a/platform/linux-generic/include/odp/api/align.h b/platform/linux-generic/include/odp/api/align.h
deleted file mode 100644
index c238b80af..000000000
--- a/platform/linux-generic/include/odp/api/align.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP alignments
- */
-
-#ifndef ODP_PLAT_ALIGN_H_
-#define ODP_PLAT_ALIGN_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/cpu_arch.h>
-
-/** @ingroup odp_compiler_optim
- * @{
- */
-
-#ifdef __GNUC__
-
-#define ODP_ALIGNED(x) __attribute__((__aligned__(x)))
-
-#define ODP_PACKED __attribute__((__packed__))
-
-#define ODP_OFFSETOF(type, member) __builtin_offsetof(type, member)
-
-#define ODP_FIELD_SIZEOF(type, member) sizeof(((type *)0)->member)
-
-#else
-#error Non-gcc compatible compiler
-#endif
-
-#define ODP_CACHE_LINE_SIZE _ODP_CACHE_LINE_SIZE
-
-#define ODP_PAGE_SIZE 4096
-
-#define ODP_ALIGNED_CACHE ODP_ALIGNED(ODP_CACHE_LINE_SIZE)
-
-#define ODP_ALIGNED_PAGE ODP_ALIGNED(ODP_PAGE_SIZE)
-
-/**
- * @}
- */
-
-#include <odp/api/spec/align.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/atomic.h b/platform/linux-generic/include/odp/api/atomic.h
deleted file mode 100644
index 7886cb4ea..000000000
--- a/platform/linux-generic/include/odp/api/atomic.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP atomic operations
- */
-
-#ifndef ODP_PLAT_ATOMIC_H_
-#define ODP_PLAT_ATOMIC_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/align.h>
-#include <odp/api/plat/atomic_types.h>
-
-/** @ingroup odp_atomic
- * @{
- */
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/atomic_inlines.h>
-#endif
-
-/**
- * @}
- */
-
-#include <odp/api/spec/atomic.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/barrier.h b/platform/linux-generic/include/odp/api/barrier.h
deleted file mode 100644
index ab1b77562..000000000
--- a/platform/linux-generic/include/odp/api/barrier.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP execution barriers
- */
-
-#ifndef ODP_PLAT_BARRIER_H_
-#define ODP_PLAT_BARRIER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/atomic.h>
-#include <odp/api/plat/shared_memory_types.h>
-#include <odp/api/plat/barrier_types.h>
-
-#include <odp/api/spec/barrier.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/buffer.h b/platform/linux-generic/include/odp/api/buffer.h
deleted file mode 100644
index 81dbf1280..000000000
--- a/platform/linux-generic/include/odp/api/buffer.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP buffer descriptor
- */
-
-#ifndef ODP_PLAT_BUFFER_H_
-#define ODP_PLAT_BUFFER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/event_types.h>
-#include <odp/api/plat/buffer_types.h>
-#include <odp/api/plat/pool_types.h>
-
-/** @ingroup odp_buffer
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/buffer.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/byteorder.h b/platform/linux-generic/include/odp/api/byteorder.h
deleted file mode 100644
index ec3d0eef7..000000000
--- a/platform/linux-generic/include/odp/api/byteorder.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP byteorder
- */
-
-#ifndef ODP_PLAT_BYTEORDER_H_
-#define ODP_PLAT_BYTEORDER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/byteorder_types.h>
-#include <odp/api/compiler.h>
-
-/** @ingroup odp_compiler_optim
- * @{
- */
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/byteorder_inlines.h>
-#endif
-
-/**
- * @}
- */
-
-#include <odp/api/spec/byteorder.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/classification.h b/platform/linux-generic/include/odp/api/classification.h
deleted file mode 100644
index 2ba6eb0eb..000000000
--- a/platform/linux-generic/include/odp/api/classification.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP classification descriptor
- */
-
-#ifndef ODP_PLAT_CLASSIFICATION_H_
-#define ODP_PLAT_CLASSIFICATION_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/pool_types.h>
-#include <odp/api/plat/classification_types.h>
-#include <odp/api/plat/packet_types.h>
-#include <odp/api/plat/packet_io_types.h>
-#include <odp/api/plat/queue_types.h>
-
-/** @ingroup odp_classification
- * @{
- */
-
-/* REMOVE THESE FROM API SPEC. Typedefs needed only for suppressing Doxygen
- * warning. */
-typedef void odp_flowsig_t;
-typedef void odp_cos_flow_set_t;
-
-/**
- * @}
- */
-
-#include <odp/api/spec/classification.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/compiler.h b/platform/linux-generic/include/odp/api/compiler.h
deleted file mode 100644
index 5249d5d62..000000000
--- a/platform/linux-generic/include/odp/api/compiler.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Compiler related
- */
-
-#ifndef ODP_PLAT_COMPILER_H_
-#define ODP_PLAT_COMPILER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_compiler_optim
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/compiler.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/cpu.h b/platform/linux-generic/include/odp/api/cpu.h
deleted file mode 100644
index d49c782b1..000000000
--- a/platform/linux-generic/include/odp/api/cpu.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP CPU
- */
-
-#ifndef ODP_PLAT_CPU_H_
-#define ODP_PLAT_CPU_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/cpu_arch.h>
-
-#include <odp/api/spec/cpu.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/cpumask.h b/platform/linux-generic/include/odp/api/cpumask.h
deleted file mode 100644
index 325ea52ed..000000000
--- a/platform/linux-generic/include/odp/api/cpumask.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP CPU masks and enumeration
- */
-
-#ifndef ODP_PLAT_CPUMASK_H_
-#define ODP_PLAT_CPUMASK_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/cpumask_types.h>
-
-#include <odp/api/spec/cpumask.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/crypto.h b/platform/linux-generic/include/odp/api/crypto.h
deleted file mode 100644
index 4f65932aa..000000000
--- a/platform/linux-generic/include/odp/api/crypto.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP crypto
- */
-
-#ifndef ODP_PLAT_CRYPTO_H_
-#define ODP_PLAT_CRYPTO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/packet_types.h>
-#include <odp/api/plat/crypto_types.h>
-#include <odp/api/plat/buffer_types.h>
-#include <odp/api/plat/pool_types.h>
-#include <odp/api/queue.h>
-
-/** @ingroup odp_crypto
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/crypto.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/debug.h b/platform/linux-generic/include/odp/api/debug.h
deleted file mode 100644
index bef2fd0eb..000000000
--- a/platform/linux-generic/include/odp/api/debug.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP debug
- */
-
-#ifndef ODP_PLAT_DEBUG_H_
-#define ODP_PLAT_DEBUG_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spec/debug.h>
-
-/**
- * @internal _Static_assert was only added in GCC 4.6 and the C++ version
- * static_assert for g++ 6 and above. Provide a weak replacement for previous
- * versions.
- */
-#define _odp_merge(a, b) a##b
-/** @internal */
-#define _odp_label(a) _odp_merge(_ODP_SASSERT_, a)
-/** @internal */
-#define _ODP_SASSERT _odp_label(__COUNTER__)
-/** @internal */
-#define _ODP_SASSERT_ENUM(e) { _ODP_SASSERT = 1 / !!(e) }
-/** @internal */
-#define _odp_static_assert(e, s) enum _ODP_SASSERT_ENUM(e)
-
-#if defined(__clang__)
-#if defined(__cplusplus)
-#if !__has_feature(cxx_static_assert) && !defined(static_assert)
-/** @internal */
-#define static_assert(e, s) _odp_static_assert(e, s)
-#endif
-#elif !__has_feature(c_static_assert) && !defined(_Static_assert)
-/** @internal */
-#define _Static_assert(e, s) _odp_static_assert(e, s)
-#endif
-
-#elif defined(__GNUC__)
-#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6)) || \
- (__GNUC__ < 6 && defined(__cplusplus))
-#if defined(__cplusplus)
-#if !defined(static_assert)
-/** @intenral */
-#define static_assert(e, s) _odp_static_assert(e, s)
-#endif
-#elif !defined(_Static_assert)
-/** @internal */
-#define _Static_assert(e, s) _odp_static_assert(e, s)
-#endif
-#endif
-
-#endif
-
-/**
- * @internal Compile time assertion macro. Fails compilation and outputs 'msg'
- * if condition 'cond' is false. Macro definition is empty when compiler is not
- * supported or the compiler does not support static assertion.
- */
-#ifndef __cplusplus
-#define ODP_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
-
-#else
-#define ODP_STATIC_ASSERT(cond, msg) static_assert(cond, msg)
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/deprecated.h b/platform/linux-generic/include/odp/api/deprecated.h
deleted file mode 100644
index 82797ebc4..000000000
--- a/platform/linux-generic/include/odp/api/deprecated.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Control deprecated API definitions
- */
-
-#ifndef ODP_PLAT_DEPRECATED_H_
-#define ODP_PLAT_DEPRECATED_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spec/deprecated.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/errno.h b/platform/linux-generic/include/odp/api/errno.h
deleted file mode 100644
index f70d84df0..000000000
--- a/platform/linux-generic/include/odp/api/errno.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP errno API
- */
-
-#ifndef ODP_PLAT_ERRNO_H_
-#define ODP_PLAT_ERRNO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-#include <odp/api/spec/errno.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/event.h b/platform/linux-generic/include/odp/api/event.h
deleted file mode 100644
index 55931b625..000000000
--- a/platform/linux-generic/include/odp/api/event.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP event
- */
-
-#ifndef ODP_PLAT_EVENT_H_
-#define ODP_PLAT_EVENT_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/event_types.h>
-
-/** @ingroup odp_event
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/event.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/hash.h b/platform/linux-generic/include/odp/api/hash.h
deleted file mode 100644
index 332029ed0..000000000
--- a/platform/linux-generic/include/odp/api/hash.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP Hash function
- */
-
-#ifndef ODP_PLAT_HASH_H_
-#define ODP_PLAT_HASH_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_hash
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/hash.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/hints.h b/platform/linux-generic/include/odp/api/hints.h
deleted file mode 100644
index 3ba1ba95e..000000000
--- a/platform/linux-generic/include/odp/api/hints.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP compiler hints
- */
-
-#ifndef ODP_PLAT_HINTS_H_
-#define ODP_PLAT_HINTS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_compiler_optim
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/hints.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/init.h b/platform/linux-generic/include/odp/api/init.h
deleted file mode 100644
index 1d9f59a6f..000000000
--- a/platform/linux-generic/include/odp/api/init.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP initialization.
- */
-
-#ifndef ODP_PLAT_INIT_H_
-#define ODP_PLAT_INIT_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/init_types.h>
-
-/** @ingroup odp_initialization
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/init.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/packet.h b/platform/linux-generic/include/odp/api/packet.h
deleted file mode 100644
index eff408035..000000000
--- a/platform/linux-generic/include/odp/api/packet.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP packet descriptor
- */
-
-#ifndef ODP_PLAT_PACKET_H_
-#define ODP_PLAT_PACKET_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/event_types.h>
-#include <odp/api/plat/packet_io_types.h>
-#include <odp/api/plat/packet_types.h>
-#include <odp/api/plat/buffer_types.h>
-#include <odp/api/plat/pool_types.h>
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/packet_inlines.h>
-#endif
-
-#include <odp/api/spec/packet.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/packet_flags.h b/platform/linux-generic/include/odp/api/packet_flags.h
deleted file mode 100644
index 1e55af823..000000000
--- a/platform/linux-generic/include/odp/api/packet_flags.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP packet flags
- */
-
-#ifndef ODP_PLAT_PACKET_FLAGS_H_
-#define ODP_PLAT_PACKET_FLAGS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/packet_flag_inlines.h>
-#endif
-
-#include <odp/api/spec/packet_flags.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/packet_io.h b/platform/linux-generic/include/odp/api/packet_io.h
deleted file mode 100644
index 76c7dfeda..000000000
--- a/platform/linux-generic/include/odp/api/packet_io.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP Packet IO
- */
-
-#ifndef ODP_PLAT_PACKET_IO_H_
-#define ODP_PLAT_PACKET_IO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/pool_types.h>
-#include <odp/api/plat/classification_types.h>
-#include <odp/api/plat/packet_types.h>
-#include <odp/api/plat/packet_io_types.h>
-#include <odp/api/plat/queue_types.h>
-
-/** @ingroup odp_packet_io
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/packet_io.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/packet_io_stats.h b/platform/linux-generic/include/odp/api/packet_io_stats.h
deleted file mode 100644
index a9cd9535e..000000000
--- a/platform/linux-generic/include/odp/api/packet_io_stats.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP packet IO stats
- */
-
-#ifndef ODP_PLAT_PACKET_IO_STATS_H_
-#define ODP_PLAT_PACKET_IO_STATS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spec/packet_io_stats.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/atomic_inlines.h b/platform/linux-generic/include/odp/api/plat/atomic_inlines.h
index 03b2884fd..fabf2eb04 100644
--- a/platform/linux-generic/include/odp/api/plat/atomic_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/atomic_inlines.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
*/
/**
@@ -13,6 +12,69 @@
#ifndef _ODP_PLAT_ATOMIC_INLINES_H_
#define _ODP_PLAT_ATOMIC_INLINES_H_
+#include <odp/api/abi/atomic_inlines.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_atomic_init_u32 __odp_atomic_init_u32
+ #define odp_atomic_load_u32 __odp_atomic_load_u32
+ #define odp_atomic_store_u32 __odp_atomic_store_u32
+ #define odp_atomic_fetch_add_u32 __odp_atomic_fetch_add_u32
+ #define odp_atomic_add_u32 __odp_atomic_add_u32
+ #define odp_atomic_fetch_sub_u32 __odp_atomic_fetch_sub_u32
+ #define odp_atomic_sub_u32 __odp_atomic_sub_u32
+ #define odp_atomic_fetch_inc_u32 __odp_atomic_fetch_inc_u32
+ #define odp_atomic_inc_u32 __odp_atomic_inc_u32
+ #define odp_atomic_fetch_dec_u32 __odp_atomic_fetch_dec_u32
+ #define odp_atomic_dec_u32 __odp_atomic_dec_u32
+ #define odp_atomic_cas_u32 __odp_atomic_cas_u32
+ #define odp_atomic_xchg_u32 __odp_atomic_xchg_u32
+ #define odp_atomic_load_acq_u32 __odp_atomic_load_acq_u32
+ #define odp_atomic_store_rel_u32 __odp_atomic_store_rel_u32
+ #define odp_atomic_add_rel_u32 __odp_atomic_add_rel_u32
+ #define odp_atomic_sub_rel_u32 __odp_atomic_sub_rel_u32
+ #define odp_atomic_cas_acq_u32 __odp_atomic_cas_acq_u32
+ #define odp_atomic_cas_rel_u32 __odp_atomic_cas_rel_u32
+ #define odp_atomic_cas_acq_rel_u32 __odp_atomic_cas_acq_rel_u32
+ #define odp_atomic_max_u32 __odp_atomic_max_u32
+ #define odp_atomic_min_u32 __odp_atomic_min_u32
+ #define odp_atomic_init_u64 __odp_atomic_init_u64
+ #define odp_atomic_load_u64 __odp_atomic_load_u64
+ #define odp_atomic_store_u64 __odp_atomic_store_u64
+ #define odp_atomic_fetch_add_u64 __odp_atomic_fetch_add_u64
+ #define odp_atomic_add_u64 __odp_atomic_add_u64
+ #define odp_atomic_fetch_sub_u64 __odp_atomic_fetch_sub_u64
+ #define odp_atomic_sub_u64 __odp_atomic_sub_u64
+ #define odp_atomic_fetch_inc_u64 __odp_atomic_fetch_inc_u64
+ #define odp_atomic_inc_u64 __odp_atomic_inc_u64
+ #define odp_atomic_fetch_dec_u64 __odp_atomic_fetch_dec_u64
+ #define odp_atomic_dec_u64 __odp_atomic_dec_u64
+ #define odp_atomic_cas_u64 __odp_atomic_cas_u64
+ #define odp_atomic_xchg_u64 __odp_atomic_xchg_u64
+ #define odp_atomic_load_acq_u64 __odp_atomic_load_acq_u64
+ #define odp_atomic_store_rel_u64 __odp_atomic_store_rel_u64
+ #define odp_atomic_add_rel_u64 __odp_atomic_add_rel_u64
+ #define odp_atomic_sub_rel_u64 __odp_atomic_sub_rel_u64
+ #define odp_atomic_cas_acq_u64 __odp_atomic_cas_acq_u64
+ #define odp_atomic_cas_rel_u64 __odp_atomic_cas_rel_u64
+ #define odp_atomic_cas_acq_rel_u64 __odp_atomic_cas_acq_rel_u64
+ #define odp_atomic_max_u64 __odp_atomic_max_u64
+ #define odp_atomic_min_u64 __odp_atomic_min_u64
+ #define odp_atomic_init_u128 __odp_atomic_init_u128
+ #define odp_atomic_load_u128 __odp_atomic_load_u128
+ #define odp_atomic_store_u128 __odp_atomic_store_u128
+ #define odp_atomic_cas_u128 __odp_atomic_cas_u128
+ #define odp_atomic_cas_acq_u128 __odp_atomic_cas_acq_u128
+ #define odp_atomic_cas_rel_u128 __odp_atomic_cas_rel_u128
+ #define odp_atomic_cas_acq_rel_u128 __odp_atomic_cas_acq_rel_u128
+
+#else
+ #define _ODP_INLINE
+#endif
+
_ODP_INLINE void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
{
__atomic_store_n(&atom->v, val, __ATOMIC_RELAXED);
@@ -36,7 +98,7 @@ _ODP_INLINE uint32_t odp_atomic_fetch_add_u32(odp_atomic_u32_t *atom,
_ODP_INLINE void odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
{
- (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
+ _odp_atomic_add_u32(atom, val);
}
_ODP_INLINE uint32_t odp_atomic_fetch_sub_u32(odp_atomic_u32_t *atom,
@@ -47,7 +109,7 @@ _ODP_INLINE uint32_t odp_atomic_fetch_sub_u32(odp_atomic_u32_t *atom,
_ODP_INLINE void odp_atomic_sub_u32(odp_atomic_u32_t *atom, uint32_t val)
{
- (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
+ _odp_atomic_sub_u32(atom, val);
}
_ODP_INLINE uint32_t odp_atomic_fetch_inc_u32(odp_atomic_u32_t *atom)
@@ -57,7 +119,7 @@ _ODP_INLINE uint32_t odp_atomic_fetch_inc_u32(odp_atomic_u32_t *atom)
_ODP_INLINE void odp_atomic_inc_u32(odp_atomic_u32_t *atom)
{
- (void)__atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
+ _odp_atomic_inc_u32(atom);
}
_ODP_INLINE uint32_t odp_atomic_fetch_dec_u32(odp_atomic_u32_t *atom)
@@ -67,7 +129,7 @@ _ODP_INLINE uint32_t odp_atomic_fetch_dec_u32(odp_atomic_u32_t *atom)
_ODP_INLINE void odp_atomic_dec_u32(odp_atomic_u32_t *atom)
{
- (void)__atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
+ _odp_atomic_dec_u32(atom);
}
_ODP_INLINE int odp_atomic_cas_u32(odp_atomic_u32_t *atom, uint32_t *old_val,
@@ -85,307 +147,413 @@ _ODP_INLINE uint32_t odp_atomic_xchg_u32(odp_atomic_u32_t *atom,
return __atomic_exchange_n(&atom->v, new_val, __ATOMIC_RELAXED);
}
-_ODP_INLINE void odp_atomic_max_u32(odp_atomic_u32_t *atom, uint32_t new_max)
+_ODP_INLINE void odp_atomic_max_u32(odp_atomic_u32_t *atom, uint32_t val)
{
- uint32_t old_val;
-
- old_val = odp_atomic_load_u32(atom);
-
- while (new_max > old_val) {
- if (odp_atomic_cas_u32(atom, &old_val, new_max))
- break;
- }
+ _odp_atomic_max_u32(atom, val);
}
-_ODP_INLINE void odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t new_min)
+_ODP_INLINE void odp_atomic_min_u32(odp_atomic_u32_t *atom, uint32_t val)
{
- uint32_t old_val;
+ _odp_atomic_min_u32(atom, val);
+}
- old_val = odp_atomic_load_u32(atom);
+#ifdef ODP_ATOMIC_U64_LOCK
- while (new_min < old_val) {
- if (odp_atomic_cas_u32(atom, &old_val, new_min))
- break;
- }
-}
+/**
+ * @internal
+ * CAS operation expression for the ATOMIC_OP macro
+ */
+#define ATOMIC_CAS_OP(ret_ptr, old_val, new_val) \
+__extension__ ({ \
+ if (atom->v == (old_val)) { \
+ atom->v = (new_val); \
+ *(ret_ptr) = 1; \
+ } else { \
+ *(ret_ptr) = 0; \
+ } \
+})
+
+/**
+ * @internal
+ * Helper macro for lock-based atomic operations on 64-bit integers
+ * @param[in,out] atom Pointer to the 64-bit atomic variable
+ * @param expr Expression used update the variable.
+ * @return The old value of the variable.
+ */
+#define ATOMIC_OP(atom, expr) \
+__extension__ ({ \
+ uint64_t _old_val; \
+ /* Loop while lock is already taken, stop when lock becomes clear */ \
+ while (__atomic_test_and_set(&(atom)->lock, __ATOMIC_ACQUIRE)) \
+ (void)0; \
+ _old_val = (atom)->v; \
+ (expr); /* Perform whatever update is desired */ \
+ __atomic_clear(&(atom)->lock, __ATOMIC_RELEASE); \
+ _old_val; /* Return old value */ \
+})
_ODP_INLINE void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
{
atom->v = val;
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
__atomic_clear(&atom->lock, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, (void)0);
-#else
- return __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE void odp_atomic_store_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
(void)ATOMIC_OP(atom, atom->v = val);
-#else
- __atomic_store_n(&atom->v, val, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_fetch_add_u64(odp_atomic_u64_t *atom,
uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, atom->v += val);
-#else
- return __atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE void odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
(void)ATOMIC_OP(atom, atom->v += val);
-#else
- (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_fetch_sub_u64(odp_atomic_u64_t *atom,
uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, atom->v -= val);
-#else
- return __atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE void odp_atomic_sub_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
(void)ATOMIC_OP(atom, atom->v -= val);
-#else
- (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_fetch_inc_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, atom->v++);
-#else
- return __atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE void odp_atomic_inc_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
(void)ATOMIC_OP(atom, atom->v++);
-#else
- (void)__atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_fetch_dec_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, atom->v--);
-#else
- return __atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE void odp_atomic_dec_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
(void)ATOMIC_OP(atom, atom->v--);
-#else
- (void)__atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val,
uint64_t new_val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
int ret;
*old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
return ret;
-#else
- return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
- 0 /* strong */,
- __ATOMIC_RELAXED,
- __ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE uint64_t odp_atomic_xchg_u64(odp_atomic_u64_t *atom,
uint64_t new_val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
return ATOMIC_OP(atom, atom->v = new_val);
-#else
- return __atomic_exchange_n(&atom->v, new_val, __ATOMIC_RELAXED);
-#endif
}
-_ODP_INLINE void odp_atomic_max_u64(odp_atomic_u64_t *atom, uint64_t new_max)
+_ODP_INLINE uint64_t odp_atomic_load_acq_u64(odp_atomic_u64_t *atom)
+{
+ return ATOMIC_OP(atom, (void)0);
+}
+
+_ODP_INLINE void odp_atomic_store_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)ATOMIC_OP(atom, atom->v = val);
+}
+
+_ODP_INLINE void odp_atomic_add_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)ATOMIC_OP(atom, atom->v += val);
+}
+
+_ODP_INLINE void odp_atomic_sub_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ (void)ATOMIC_OP(atom, atom->v -= val);
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_u64(odp_atomic_u64_t *atom,
+ uint64_t *old_val, uint64_t new_val)
+{
+ int ret;
+ *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
+ return ret;
+}
+
+_ODP_INLINE int odp_atomic_cas_rel_u64(odp_atomic_u64_t *atom,
+ uint64_t *old_val, uint64_t new_val)
+{
+ int ret;
+ *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
+ return ret;
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_rel_u64(odp_atomic_u64_t *atom,
+ uint64_t *old_val,
+ uint64_t new_val)
+{
+ int ret;
+ *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
+ return ret;
+}
+
+_ODP_INLINE void odp_atomic_max_u64(odp_atomic_u64_t *atom, uint64_t new_val)
{
uint64_t old_val;
old_val = odp_atomic_load_u64(atom);
- while (new_max > old_val) {
- if (odp_atomic_cas_u64(atom, &old_val, new_max))
+ while (new_val > old_val) {
+ if (odp_atomic_cas_u64(atom, &old_val, new_val))
break;
}
}
-_ODP_INLINE void odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t new_min)
+_ODP_INLINE void odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t new_val)
{
uint64_t old_val;
old_val = odp_atomic_load_u64(atom);
- while (new_min < old_val) {
- if (odp_atomic_cas_u64(atom, &old_val, new_min))
+ while (new_val < old_val) {
+ if (odp_atomic_cas_u64(atom, &old_val, new_val))
break;
}
}
-_ODP_INLINE uint32_t odp_atomic_load_acq_u32(odp_atomic_u32_t *atom)
+#else /* !ODP_ATOMIC_U64_LOCK */
+
+_ODP_INLINE void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
{
- return __atomic_load_n(&atom->v, __ATOMIC_ACQUIRE);
+ atom->v = val;
}
-_ODP_INLINE void odp_atomic_store_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+_ODP_INLINE uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
{
- __atomic_store_n(&atom->v, val, __ATOMIC_RELEASE);
+ return __atomic_load_n(&atom->v, __ATOMIC_RELAXED);
}
-_ODP_INLINE void odp_atomic_add_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+_ODP_INLINE void odp_atomic_store_u64(odp_atomic_u64_t *atom, uint64_t val)
{
- (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELEASE);
+ __atomic_store_n(&atom->v, val, __ATOMIC_RELAXED);
}
-_ODP_INLINE void odp_atomic_sub_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+_ODP_INLINE uint64_t odp_atomic_fetch_add_u64(odp_atomic_u64_t *atom,
+ uint64_t val)
{
- (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELEASE);
+ return __atomic_fetch_add(&atom->v, val, __ATOMIC_RELAXED);
}
-_ODP_INLINE int odp_atomic_cas_acq_u32(odp_atomic_u32_t *atom,
- uint32_t *old_val, uint32_t new_val)
+_ODP_INLINE void odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
{
- return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
- 0 /* strong */,
- __ATOMIC_ACQUIRE,
- __ATOMIC_RELAXED);
+ _odp_atomic_add_u64(atom, val);
}
-_ODP_INLINE int odp_atomic_cas_rel_u32(odp_atomic_u32_t *atom,
- uint32_t *old_val, uint32_t new_val)
+_ODP_INLINE uint64_t odp_atomic_fetch_sub_u64(odp_atomic_u64_t *atom,
+ uint64_t val)
{
- return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
- 0 /* strong */,
- __ATOMIC_RELEASE,
- __ATOMIC_RELAXED);
+ return __atomic_fetch_sub(&atom->v, val, __ATOMIC_RELAXED);
}
-_ODP_INLINE int odp_atomic_cas_acq_rel_u32(odp_atomic_u32_t *atom,
- uint32_t *old_val,
- uint32_t new_val)
+_ODP_INLINE void odp_atomic_sub_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ _odp_atomic_sub_u64(atom, val);
+}
+
+_ODP_INLINE uint64_t odp_atomic_fetch_inc_u64(odp_atomic_u64_t *atom)
+{
+ return __atomic_fetch_add(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE void odp_atomic_inc_u64(odp_atomic_u64_t *atom)
+{
+ _odp_atomic_inc_u64(atom);
+}
+
+_ODP_INLINE uint64_t odp_atomic_fetch_dec_u64(odp_atomic_u64_t *atom)
+{
+ return __atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE void odp_atomic_dec_u64(odp_atomic_u64_t *atom)
+{
+ _odp_atomic_dec_u64(atom);
+}
+
+_ODP_INLINE int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val,
+ uint64_t new_val)
{
return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
0 /* strong */,
- __ATOMIC_ACQ_REL,
+ __ATOMIC_RELAXED,
__ATOMIC_RELAXED);
}
+_ODP_INLINE uint64_t odp_atomic_xchg_u64(odp_atomic_u64_t *atom,
+ uint64_t new_val)
+{
+ return __atomic_exchange_n(&atom->v, new_val, __ATOMIC_RELAXED);
+}
+
_ODP_INLINE uint64_t odp_atomic_load_acq_u64(odp_atomic_u64_t *atom)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- return ATOMIC_OP(atom, (void)0);
-#else
return __atomic_load_n(&atom->v, __ATOMIC_ACQUIRE);
-#endif
}
_ODP_INLINE void odp_atomic_store_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP(atom, atom->v = val);
-#else
__atomic_store_n(&atom->v, val, __ATOMIC_RELEASE);
-#endif
}
_ODP_INLINE void odp_atomic_add_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP(atom, atom->v += val);
-#else
- (void)__atomic_fetch_add(&atom->v, val, __ATOMIC_RELEASE);
-#endif
+ _odp_atomic_add_rel_u64(atom, val);
}
_ODP_INLINE void odp_atomic_sub_rel_u64(odp_atomic_u64_t *atom, uint64_t val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP(atom, atom->v -= val);
-#else
- (void)__atomic_fetch_sub(&atom->v, val, __ATOMIC_RELEASE);
-#endif
+ _odp_atomic_sub_rel_u64(atom, val);
}
_ODP_INLINE int odp_atomic_cas_acq_u64(odp_atomic_u64_t *atom,
uint64_t *old_val, uint64_t new_val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- int ret;
- *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
- return ret;
-#else
return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
0 /* strong */,
__ATOMIC_ACQUIRE,
__ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE int odp_atomic_cas_rel_u64(odp_atomic_u64_t *atom,
uint64_t *old_val, uint64_t new_val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- int ret;
- *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
- return ret;
-#else
return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
0 /* strong */,
__ATOMIC_RELEASE,
__ATOMIC_RELAXED);
-#endif
}
_ODP_INLINE int odp_atomic_cas_acq_rel_u64(odp_atomic_u64_t *atom,
uint64_t *old_val,
uint64_t new_val)
{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- int ret;
- *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val));
- return ret;
-#else
return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
0 /* strong */,
__ATOMIC_ACQ_REL,
__ATOMIC_RELAXED);
-#endif
}
+_ODP_INLINE void odp_atomic_max_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ _odp_atomic_max_u64(atom, val);
+}
+
+_ODP_INLINE void odp_atomic_min_u64(odp_atomic_u64_t *atom, uint64_t val)
+{
+ _odp_atomic_min_u64(atom, val);
+}
+
+#endif /* !ODP_ATOMIC_U64_LOCK */
+
+_ODP_INLINE uint32_t odp_atomic_load_acq_u32(odp_atomic_u32_t *atom)
+{
+ return __atomic_load_n(&atom->v, __ATOMIC_ACQUIRE);
+}
+
+_ODP_INLINE void odp_atomic_store_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ __atomic_store_n(&atom->v, val, __ATOMIC_RELEASE);
+}
+
+_ODP_INLINE void odp_atomic_add_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ _odp_atomic_add_rel_u32(atom, val);
+}
+
+_ODP_INLINE void odp_atomic_sub_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
+{
+ _odp_atomic_sub_rel_u32(atom, val);
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_u32(odp_atomic_u32_t *atom,
+ uint32_t *old_val, uint32_t new_val)
+{
+ return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
+ 0 /* strong */,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE int odp_atomic_cas_rel_u32(odp_atomic_u32_t *atom,
+ uint32_t *old_val, uint32_t new_val)
+{
+ return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
+ 0 /* strong */,
+ __ATOMIC_RELEASE,
+ __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_rel_u32(odp_atomic_u32_t *atom,
+ uint32_t *old_val,
+ uint32_t new_val)
+{
+ return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
+ 0 /* strong */,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE void odp_atomic_init_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ _odp_atomic_init_u128(atom, val);
+}
+
+_ODP_INLINE odp_u128_t odp_atomic_load_u128(odp_atomic_u128_t *atom)
+{
+ return _odp_atomic_load_u128(atom);
+}
+
+_ODP_INLINE void odp_atomic_store_u128(odp_atomic_u128_t *atom, odp_u128_t val)
+{
+ _odp_atomic_store_u128(atom, val);
+}
+
+_ODP_INLINE int odp_atomic_cas_u128(odp_atomic_u128_t *atom,
+ odp_u128_t *old_val, odp_u128_t new_val)
+{
+ return _odp_atomic_cas_u128(atom, old_val, new_val);
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_u128(odp_atomic_u128_t *atom,
+ odp_u128_t *old_val, odp_u128_t new_val)
+{
+ return _odp_atomic_cas_acq_u128(atom, old_val, new_val);
+}
+
+_ODP_INLINE int odp_atomic_cas_rel_u128(odp_atomic_u128_t *atom,
+ odp_u128_t *old_val, odp_u128_t new_val)
+{
+ return _odp_atomic_cas_rel_u128(atom, old_val, new_val);
+}
+
+_ODP_INLINE int odp_atomic_cas_acq_rel_u128(odp_atomic_u128_t *atom,
+ odp_u128_t *old_val, odp_u128_t new_val)
+{
+ return _odp_atomic_cas_acq_rel_u128(atom, old_val, new_val);
+}
+
+/** @endcond */
+
#endif
diff --git a/platform/linux-generic/include/odp/api/plat/atomic_types.h b/platform/linux-generic/include/odp/api/plat/atomic_types.h
deleted file mode 100644
index a674ac997..000000000
--- a/platform/linux-generic/include/odp/api/plat/atomic_types.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP atomic operations
- */
-
-#ifndef ODP_ATOMIC_TYPES_H_
-#define ODP_ATOMIC_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/align.h>
-
-/**
- * @internal
- * Atomic 64-bit unsigned integer
- */
-struct odp_atomic_u64_s {
- uint64_t v; /**< Actual storage for the atomic variable */
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- /* Some architectures do not support lock-free operations on 64-bit
- * data types. We use a spin lock to ensure atomicity. */
- char lock; /**< Spin lock (if needed) used to ensure atomic access */
-#endif
-} ODP_ALIGNED(sizeof(uint64_t)); /* Enforce alignement! */
-
-/**
- * @internal
- * Atomic 32-bit unsigned integer
- */
-struct odp_atomic_u32_s {
- uint32_t v; /**< Actual storage for the atomic variable */
-} ODP_ALIGNED(sizeof(uint32_t)); /* Enforce alignement! */
-
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
-
-/**
- * @internal
- * CAS operation expression for the ATOMIC_OP macro
- */
-#define ATOMIC_CAS_OP(ret_ptr, old_val, new_val) \
-({ \
- if (atom->v == (old_val)) { \
- atom->v = (new_val); \
- *(ret_ptr) = 1; \
- } else { \
- *(ret_ptr) = 0; \
- } \
-})
-
-/**
- * @internal
- * Helper macro for lock-based atomic operations on 64-bit integers
- * @param[in,out] atom Pointer to the 64-bit atomic variable
- * @param expr Expression used update the variable.
- * @return The old value of the variable.
- */
-#define ATOMIC_OP(atom, expr) \
-({ \
- uint64_t _old_val; \
- /* Loop while lock is already taken, stop when lock becomes clear */ \
- while (__atomic_test_and_set(&(atom)->lock, __ATOMIC_ACQUIRE)) \
- (void)0; \
- _old_val = (atom)->v; \
- (expr); /* Perform whatever update is desired */ \
- __atomic_clear(&(atom)->lock, __ATOMIC_RELEASE); \
- _old_val; /* Return old value */ \
-})
-#endif
-
-typedef struct odp_atomic_u64_s odp_atomic_u64_t;
-
-typedef struct odp_atomic_u32_s odp_atomic_u32_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/barrier_types.h b/platform/linux-generic/include/odp/api/plat/barrier_types.h
deleted file mode 100644
index 00b383cc6..000000000
--- a/platform/linux-generic/include/odp/api/plat/barrier_types.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP barrier
- */
-
-#ifndef ODP_BARRIER_TYPES_H_
-#define ODP_BARRIER_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/atomic.h>
-
-/**
- * @internal
- * ODP thread synchronization barrier
- */
-struct odp_barrier_s {
- uint32_t count; /**< Thread count */
- odp_atomic_u32_t bar; /**< Barrier counter */
-};
-
-typedef struct odp_barrier_s odp_barrier_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/buffer_inline_types.h b/platform/linux-generic/include/odp/api/plat/buffer_inline_types.h
new file mode 100644
index 000000000..3b0307cea
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/buffer_inline_types.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_BUFFER_INLINE_TYPES_H_
+#define ODP_PLAT_BUFFER_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Buffer header field accessors */
+#define _odp_buffer_get(buffer_hdr, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)buffer_hdr + \
+ _odp_buffer_inline_offset.field))
+
+/* Buffer header field offsets for inline functions */
+typedef struct _odp_buffer_inline_offset_t {
+ uint16_t uarea_addr;
+
+} _odp_buffer_inline_offset_t;
+
+extern const _odp_buffer_inline_offset_t _odp_buffer_inline_offset;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/buffer_inlines.h b/platform/linux-generic/include/odp/api/plat/buffer_inlines.h
new file mode 100644
index 000000000..4ef9687e7
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/buffer_inlines.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+#ifndef ODP_PLAT_BUFFER_INLINES_H_
+#define ODP_PLAT_BUFFER_INLINES_H_
+
+#include <odp/api/buffer_types.h>
+#include <odp/api/event.h>
+#include <odp/api/pool_types.h>
+
+#include <odp/api/plat/buffer_inline_types.h>
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/event_inline_types.h>
+#include <odp/api/plat/pool_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_buffer_from_event __odp_buffer_from_event
+ #define odp_buffer_from_event_multi __odp_buffer_from_event_multi
+ #define odp_buffer_to_event __odp_buffer_to_event
+ #define odp_buffer_to_event_multi __odp_buffer_to_event_multi
+ #define odp_buffer_addr __odp_buffer_addr
+ #define odp_buffer_size __odp_buffer_size
+ #define odp_buffer_pool __odp_buffer_pool
+ #define odp_buffer_user_area __odp_buffer_user_area
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_buffer_t odp_buffer_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_BUFFER);
+
+ return (odp_buffer_t)ev;
+}
+
+_ODP_INLINE void odp_buffer_from_event_multi(odp_buffer_t buf[], const odp_event_t ev[], int num)
+{
+ for (int i = 0; i < num; i++)
+ buf[i] = odp_buffer_from_event(ev[i]);
+}
+
+_ODP_INLINE odp_event_t odp_buffer_to_event(odp_buffer_t buf)
+{
+ return (odp_event_t)buf;
+}
+
+_ODP_INLINE void odp_buffer_to_event_multi(const odp_buffer_t buf[], odp_event_t ev[], int num)
+{
+ for (int i = 0; i < num; i++)
+ ev[i] = odp_buffer_to_event(buf[i]);
+}
+
+_ODP_INLINE void *odp_buffer_addr(odp_buffer_t buf)
+{
+ return _odp_event_hdr_field((odp_event_t)buf, void *, base_data);
+}
+
+_ODP_INLINE uint32_t odp_buffer_size(odp_buffer_t buf)
+{
+ odp_pool_t pool = _odp_event_hdr_field(buf, odp_pool_t, pool);
+
+ return _odp_pool_get(pool, uint32_t, seg_len);
+}
+
+_ODP_INLINE odp_pool_t odp_buffer_pool(odp_buffer_t buf)
+{
+ return _odp_event_hdr_field(buf, odp_pool_t, pool);
+}
+
+_ODP_INLINE void *odp_buffer_user_area(odp_buffer_t buf)
+{
+ return _odp_buffer_get(buf, void *, uarea_addr);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/buffer_types.h b/platform/linux-generic/include/odp/api/plat/buffer_types.h
deleted file mode 100644
index 8b79bb52e..000000000
--- a/platform/linux-generic/include/odp/api/plat/buffer_types.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP buffer descriptor
- */
-
-#ifndef ODP_BUFFER_TYPES_H_
-#define ODP_BUFFER_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/buffer.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_buffer
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_buffer_t);
-
-#define ODP_BUFFER_INVALID _odp_cast_scalar(odp_buffer_t, NULL)
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/byteorder_inlines.h b/platform/linux-generic/include/odp/api/plat/byteorder_inlines.h
index e7818904b..692c66c74 100644
--- a/platform/linux-generic/include/odp/api/plat/byteorder_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/byteorder_inlines.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -17,9 +15,54 @@
extern "C" {
#endif
-/** @ingroup odp_compiler_optim
- * @{
+#include <odp/api/abi/byteorder.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef __odp_force
+#define __odp_force
+#endif
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_be_to_cpu_16 __odp_be_to_cpu_16
+ #define odp_be_to_cpu_32 __odp_be_to_cpu_32
+ #define odp_be_to_cpu_64 __odp_be_to_cpu_64
+ #define odp_cpu_to_be_16 __odp_cpu_to_be_16
+ #define odp_cpu_to_be_32 __odp_cpu_to_be_32
+ #define odp_cpu_to_be_64 __odp_cpu_to_be_64
+ #define odp_le_to_cpu_16 __odp_le_to_cpu_16
+ #define odp_le_to_cpu_32 __odp_le_to_cpu_32
+ #define odp_le_to_cpu_64 __odp_le_to_cpu_64
+ #define odp_cpu_to_le_16 __odp_cpu_to_le_16
+ #define odp_cpu_to_le_32 __odp_cpu_to_le_32
+ #define odp_cpu_to_le_64 __odp_cpu_to_le_64
+#else
+ #define _ODP_INLINE
+#endif
+
+/** @internal GNU compiler version */
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+/**
+ * @internal
+ * Compiler __builtin_bswap16() is not available on all platforms
+ * until GCC 4.8.0 - work around this by offering __odp_builtin_bswap16()
+ * Don't use this function directly, instead see odp_byteorder.h
*/
+#if GCC_VERSION < 40800
+/*
+ * We have to explicitly cast back to uint16_t because clang promotes the
+ * left side of << operator to int.
+ */
+#define __odp_builtin_bswap16(u16) ((uint16_t)(((u16)&0x00ff) << 8) | \
+ (((u16)&0xff00) >> 8))
+#else
+#define __odp_builtin_bswap16(u16) __builtin_bswap16(u16)
+#endif
_ODP_INLINE uint16_t odp_be_to_cpu_16(odp_u16be_t be16)
{
@@ -129,9 +172,7 @@ _ODP_INLINE odp_u64le_t odp_cpu_to_le_64(uint64_t cpu64)
#endif
}
-/**
- * @}
- */
+/** @endcond */
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp/api/plat/byteorder_types.h b/platform/linux-generic/include/odp/api/plat/byteorder_types.h
deleted file mode 100644
index 20d52bf8f..000000000
--- a/platform/linux-generic/include/odp/api/plat/byteorder_types.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP byteorder
- */
-
-#ifndef ODP_BYTEORDER_TYPES_H_
-#define ODP_BYTEORDER_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __BYTE_ORDER__
-#error __BYTE_ORDER not defined!
-#endif
-
-#ifndef __ORDER_BIG_ENDIAN__
-#error __BIG_ENDIAN not defined!
-#endif
-
-#ifndef __ORDER_LITTLE_ENDIAN__
-#error __LITTLE_ENDIAN not defined!
-#endif
-
-/* for use with type checkers such as sparse */
-#ifdef __CHECKER__
-/** @internal bitwise attribute */
-#define __odp_bitwise __attribute__((bitwise))
-/** @internal force attribute */
-#define __odp_force __attribute__((force))
-#else
-/** @internal bitwise attribute */
-#define __odp_bitwise
-/** @internal force attribute */
-#define __odp_force
-#endif
-
-/** @addtogroup odp_compiler_optim
- * @{
- */
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- #define ODP_LITTLE_ENDIAN 1
- #define ODP_BIG_ENDIAN 0
- #define ODP_BYTE_ORDER ODP_LITTLE_ENDIAN
- #define ODP_LITTLE_ENDIAN_BITFIELD 1
- #define ODP_BIG_ENDIAN_BITFIELD 0
- #define ODP_BITFIELD_ORDER ODP_LITTLE_ENDIAN_BITFIELD
-#else
- #define ODP_LITTLE_ENDIAN 0
- #define ODP_BIG_ENDIAN 1
- #define ODP_BYTE_ORDER ODP_BIG_ENDIAN
- #define ODP_LITTLE_ENDIAN_BITFIELD 0
- #define ODP_BIG_ENDIAN_BITFIELD 1
- #define ODP_BITFIELD_ORDER ODP_BIG_ENDIAN_BITFIELD
-#endif
-
-typedef uint16_t __odp_bitwise odp_u16le_t;
-typedef uint16_t __odp_bitwise odp_u16be_t;
-
-typedef uint32_t __odp_bitwise odp_u32le_t;
-typedef uint32_t __odp_bitwise odp_u32be_t;
-
-typedef uint64_t __odp_bitwise odp_u64le_t;
-typedef uint64_t __odp_bitwise odp_u64be_t;
-
-typedef uint16_t __odp_bitwise odp_u16sum_t;
-typedef uint32_t __odp_bitwise odp_u32sum_t;
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/classification_types.h b/platform/linux-generic/include/odp/api/plat/classification_types.h
deleted file mode 100644
index d210feb0c..000000000
--- a/platform/linux-generic/include/odp/api/plat/classification_types.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP classification descriptor
- */
-
-#ifndef ODP_CLASSIFICATION_TYPES_H_
-#define ODP_CLASSIFICATION_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/classification.h>
-#else
-
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_classification
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_cos_t);
-#define ODP_COS_INVALID _odp_cast_scalar(odp_cos_t, ~0)
-
-typedef ODP_HANDLE_T(odp_pmr_t);
-#define ODP_PMR_INVAL _odp_cast_scalar(odp_pmr_t, ~0)
-
-#define ODP_COS_NAME_LEN 32
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/cpu_inlines.h b/platform/linux-generic/include/odp/api/plat/cpu_inlines.h
new file mode 100644
index 000000000..0a1627b79
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/cpu_inlines.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef ODP_PLAT_CPU_INLINES_H_
+#define ODP_PLAT_CPU_INLINES_H_
+
+#include <odp/api/hints.h>
+
+#include <odp/api/abi/cpu_inlines.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_cpu_pause __odp_cpu_pause
+ #define odp_cpu_cycles __odp_cpu_cycles
+ #define odp_cpu_cycles_max __odp_cpu_cycles_max
+ #define odp_cpu_cycles_resolution __odp_cpu_cycles_resolution
+ #define odp_cpu_cycles_diff __odp_cpu_cycles_diff
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void odp_cpu_pause(void)
+{
+ _odp_cpu_pause();
+}
+
+_ODP_INLINE uint64_t odp_cpu_cycles_max(void)
+{
+ return _odp_cpu_cycles_max();
+}
+
+_ODP_INLINE uint64_t odp_cpu_cycles_resolution(void)
+{
+ return _odp_cpu_cycles_resolution();
+}
+
+_ODP_INLINE uint64_t odp_cpu_cycles(void)
+{
+ return _odp_cpu_cycles();
+}
+
+_ODP_INLINE uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
+{
+ if (odp_likely(c2 >= c1))
+ return c2 - c1;
+
+ return c2 + (odp_cpu_cycles_max() - c1) + _odp_cpu_cycles_resolution();
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/cpumask_types.h b/platform/linux-generic/include/odp/api/plat/cpumask_types.h
deleted file mode 100644
index c2727a46c..000000000
--- a/platform/linux-generic/include/odp/api/plat/cpumask_types.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP CPU masks and enumeration
- */
-
-#ifndef ODP_CPUMASK_TYPES_H_
-#define ODP_CPUMASK_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_cpumask
- * @{
- */
-
-#include <odp/api/std_types.h>
-#include <odp/api/align.h>
-
-#define ODP_CPUMASK_SIZE 1024
-
-#define ODP_CPUMASK_STR_SIZE ((ODP_CPUMASK_SIZE + 3) / 4 + 3)
-
-/**
- * CPU mask
- *
- * Don't access directly, use access functions.
- */
-typedef struct odp_cpumask_t {
- /** @private CPU mask storage
- *
- * This is private to the implementation.
- * Don't access directly, use access functions.
- */
- uint8_t _u8[ODP_CPUMASK_SIZE / 8];
-} odp_cpumask_t ODP_ALIGNED(8);
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/crypto_inlines.h b/platform/linux-generic/include/odp/api/plat/crypto_inlines.h
new file mode 100644
index 000000000..c8b6ae5ba
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/crypto_inlines.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_CRYPTO_INLINES_H_
+#define ODP_PLAT_CRYPTO_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/crypto_types.h>
+#include <odp/api/event.h>
+#include <odp/api/packet.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/packet_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_crypto_packet_from_event __odp_crypto_packet_from_event
+ #define odp_crypto_packet_to_event __odp_crypto_packet_to_event
+ #define odp_crypto_result __odp_crypto_result
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_packet_t odp_crypto_packet_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+ _ODP_ASSERT(odp_event_subtype(ev) == ODP_EVENT_PACKET_CRYPTO);
+
+ return odp_packet_from_event(ev);
+}
+
+_ODP_INLINE odp_event_t odp_crypto_packet_to_event(odp_packet_t pkt)
+{
+ return odp_packet_to_event(pkt);
+}
+
+_ODP_INLINE int odp_crypto_result(odp_crypto_packet_result_t *result, odp_packet_t pkt)
+{
+ odp_crypto_packet_result_t *op_result;
+ odp_bool_t ok;
+
+ _ODP_ASSERT(odp_packet_subtype(pkt) == ODP_EVENT_PACKET_CRYPTO);
+
+ op_result = _odp_pkt_get_ptr(pkt, odp_crypto_packet_result_t, crypto_op);
+
+ ok = op_result->cipher_status.alg_err == ODP_CRYPTO_ALG_ERR_NONE &&
+ op_result->auth_status.alg_err == ODP_CRYPTO_ALG_ERR_NONE;
+
+ if (result)
+ *result = *op_result;
+
+ return ok ? 0 : -1;
+}
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/crypto_types.h b/platform/linux-generic/include/odp/api/plat/crypto_types.h
deleted file mode 100644
index 2cc747eb2..000000000
--- a/platform/linux-generic/include/odp/api/plat/crypto_types.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP crypto
- */
-
-#ifndef ODP_CRYPTO_TYPES_H_
-#define ODP_CRYPTO_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/crypto.h>
-#else
-
-/** @ingroup odp_crypto
- * @{
- */
-
-#define ODP_CRYPTO_SESSION_INVALID (0xffffffffffffffffULL)
-
-typedef uint64_t odp_crypto_session_t;
-typedef ODP_HANDLE_T(odp_crypto_compl_t);
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/debug_inlines.h b/platform/linux-generic/include/odp/api/plat/debug_inlines.h
new file mode 100644
index 000000000..8502cb05e
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/debug_inlines.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP Debug inlines
+ *
+ * @warning These definitions are not part of ODP API, they are for
+ * implementation internal use only.
+ */
+
+#ifndef ODP_DEBUG_INLINES_H_
+#define ODP_DEBUG_INLINES_H_
+
+#include <odp/autoheader_external.h>
+
+#include <odp/api/hints.h>
+#include <odp/api/init.h>
+
+#include <odp/api/plat/thread_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma GCC diagnostic push
+
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+extern odp_log_func_t ODP_PRINTF_FORMAT(2, 3) _odp_log_fn;
+extern odp_abort_func_t _odp_abort_fn;
+
+#define _ODP_LOG_FN(level, ...) \
+ do { \
+ if (_odp_this_thread && _odp_this_thread->log_fn) \
+ _odp_this_thread->log_fn(level, ##__VA_ARGS__); \
+ else \
+ _odp_log_fn(level, ##__VA_ARGS__); \
+ } while (0)
+
+/**
+ * ODP LOG macro.
+ */
+#define _ODP_LOG(level, prefix, fmt, ...) \
+ _ODP_LOG_FN(level, "%s: %s:%d:%s(): " fmt, prefix, \
+ __FILE__, __LINE__, __func__, ##__VA_ARGS__)
+
+/**
+ * Runtime assertion-macro - aborts if 'cond' is false.
+ */
+#define _ODP_ASSERT(cond) \
+ do { if ((ODP_DEBUG == 1) && (!(cond))) { \
+ _ODP_ERR("%s\n", #cond); \
+ _odp_abort_fn(); } \
+ } while (0)
+
+/*
+ * Print debug message to log, if ODP_DEBUG_PRINT flag is set (ignores CONFIG_DEBUG_LEVEL).
+ */
+#define _ODP_DBG(...) \
+ do { \
+ if (ODP_DEBUG_PRINT == 1) \
+ __extension__ ({ \
+ _ODP_LOG(ODP_LOG_DBG, "DBG", ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * Log warning message.
+ */
+#define _ODP_WARN(...) \
+ do { \
+ __extension__ ({ \
+ _ODP_LOG(ODP_LOG_WARN, "WARN", ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * Log error message.
+ */
+#define _ODP_ERR(...) \
+ do { \
+ __extension__ ({ \
+ _ODP_LOG(ODP_LOG_ERR, "ERR", ##__VA_ARGS__); \
+ }); \
+ } while (0)
+
+/**
+ * Log abort message and then stop execution (by default call abort()).
+ * This function should not return.
+ */
+#define _ODP_ABORT(...) \
+ do { \
+ __extension__ ({ \
+ _ODP_LOG(ODP_LOG_ABORT, "ABORT", ##__VA_ARGS__); \
+ }); \
+ _odp_abort_fn(); \
+ } while (0)
+
+/**
+ * Log print message when the application calls one of the ODP APIs
+ * specifically for dumping internal data.
+ */
+#define _ODP_PRINT(...) \
+ _ODP_LOG_FN(ODP_LOG_PRINT, ##__VA_ARGS__)
+
+#pragma GCC diagnostic pop
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/dma_inlines.h b/platform/linux-generic/include/odp/api/plat/dma_inlines.h
new file mode 100644
index 000000000..da3b25c0d
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/dma_inlines.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_PLAT_DMA_INLINES_H_
+#define ODP_PLAT_DMA_INLINES_H_
+
+#include <odp/api/buffer.h>
+#include <odp/api/dma_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/hints.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/queue_types.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/event_inline_types.h>
+
+#include <stdint.h>
+#include <string.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_dma_compl_from_event __odp_dma_compl_from_event
+ #define odp_dma_compl_to_event __odp_dma_compl_to_event
+ #define odp_dma_compl_user_area __odp_dma_compl_user_area
+ #define odp_dma_compl_result __odp_dma_compl_result
+ #define odp_dma_transfer_param_init __odp_dma_transfer_param_init
+ #define odp_dma_compl_param_init __odp_dma_compl_param_init
+ #define odp_dma_compl_alloc __odp_dma_compl_alloc
+ #define odp_dma_compl_free __odp_dma_compl_free
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_dma_compl_t odp_dma_compl_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(_odp_event_hdr_field(ev, int8_t, event_type) == ODP_EVENT_DMA_COMPL);
+
+ return (odp_dma_compl_t)(uintptr_t)ev;
+}
+
+_ODP_INLINE odp_event_t odp_dma_compl_to_event(odp_dma_compl_t dma_compl)
+{
+ return (odp_event_t)(uintptr_t)dma_compl;
+}
+
+_ODP_INLINE void *odp_dma_compl_user_area(odp_dma_compl_t dma_compl)
+{
+ return odp_buffer_user_area((odp_buffer_t)(uintptr_t)dma_compl);
+}
+
+_ODP_INLINE int odp_dma_compl_result(odp_dma_compl_t dma_compl, odp_dma_result_t *result_out)
+{
+ odp_dma_result_t *result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)dma_compl;
+
+ if (odp_unlikely(dma_compl == ODP_DMA_COMPL_INVALID)) {
+ _ODP_ERR("Bad DMA compl handle\n");
+ return -1;
+ }
+
+ result = (odp_dma_result_t *)odp_buffer_addr(buf);
+
+ if (result_out)
+ *result_out = *result;
+
+ return result->success ? 0 : -1;
+}
+
+_ODP_INLINE void odp_dma_transfer_param_init(odp_dma_transfer_param_t *trs_param)
+{
+ memset(trs_param, 0, sizeof(odp_dma_transfer_param_t));
+
+ trs_param->src_format = ODP_DMA_FORMAT_ADDR;
+ trs_param->dst_format = ODP_DMA_FORMAT_ADDR;
+ trs_param->num_src = 1;
+ trs_param->num_dst = 1;
+}
+
+_ODP_INLINE void odp_dma_compl_param_init(odp_dma_compl_param_t *compl_param)
+{
+ memset(compl_param, 0, sizeof(odp_dma_compl_param_t));
+
+ compl_param->queue = ODP_QUEUE_INVALID;
+ compl_param->event = ODP_EVENT_INVALID;
+ compl_param->transfer_id = ODP_DMA_TRANSFER_ID_INVALID;
+}
+
+_ODP_INLINE odp_dma_compl_t odp_dma_compl_alloc(odp_pool_t pool)
+{
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_dma_result_t *result;
+ int8_t *ev_type;
+
+ buf = odp_buffer_alloc(pool);
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+ return ODP_DMA_COMPL_INVALID;
+
+ result = (odp_dma_result_t *)odp_buffer_addr(buf);
+ memset(result, 0, sizeof(odp_dma_result_t));
+
+ ev = odp_buffer_to_event(buf);
+ ev_type = _odp_event_hdr_ptr(ev, int8_t, event_type);
+ *ev_type = ODP_EVENT_DMA_COMPL;
+
+ return (odp_dma_compl_t)(uintptr_t)buf;
+}
+
+_ODP_INLINE void odp_dma_compl_free(odp_dma_compl_t dma_compl)
+{
+ int8_t *ev_type;
+ odp_event_t ev;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)dma_compl;
+
+ if (odp_unlikely(dma_compl == ODP_DMA_COMPL_INVALID)) {
+ _ODP_ERR("Bad DMA compl handle\n");
+ return;
+ }
+
+ ev = odp_buffer_to_event(buf);
+ ev_type = _odp_event_hdr_ptr(ev, int8_t, event_type);
+ *ev_type = ODP_EVENT_BUFFER;
+
+ odp_buffer_free(buf);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/event_inline_types.h b/platform/linux-generic/include/odp/api/plat/event_inline_types.h
new file mode 100644
index 000000000..641b2a290
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/event_inline_types.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_EVENT_INLINE_TYPES_H_
+#define ODP_PLAT_EVENT_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Event header field accessors */
+#define _odp_event_hdr_field(event_hdr, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)event_hdr + \
+ _odp_event_inline_offset.field))
+#define _odp_event_hdr_ptr(event_hdr, cast, field) \
+ ((cast *)(uintptr_t)((uint8_t *)event_hdr + \
+ _odp_event_inline_offset.field))
+
+/* Event header field offsets for inline functions */
+typedef struct _odp_event_inline_offset_t {
+ uint16_t event_type;
+ uint16_t base_data;
+ uint16_t subtype;
+ uint16_t flow_id;
+ uint16_t pool;
+
+} _odp_event_inline_offset_t;
+
+extern const _odp_event_inline_offset_t _odp_event_inline_offset;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/event_inlines.h b/platform/linux-generic/include/odp/api/plat/event_inlines.h
new file mode 100644
index 000000000..8977e67fe
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/event_inlines.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#ifndef ODP_PLAT_EVENT_INLINES_H_
+#define ODP_PLAT_EVENT_INLINES_H_
+
+#include <odp/api/buffer_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/timer_types.h>
+
+#include <odp/api/plat/buffer_inline_types.h>
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/event_inline_types.h>
+#include <odp/api/plat/event_vector_inline_types.h>
+#include <odp/api/plat/packet_inline_types.h>
+#include <odp/api/plat/timer_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_event_type __odp_event_type
+ #define odp_event_type_multi __odp_event_type_multi
+ #define odp_event_pool __odp_event_pool
+ #define odp_event_user_area __odp_event_user_area
+ #define odp_event_user_area_and_flag __odp_event_user_area_and_flag
+ #define odp_event_subtype __odp_event_subtype
+ #define odp_event_types __odp_event_types
+ #define odp_event_types_multi __odp_event_types_multi
+ #define odp_event_flow_id __odp_event_flow_id
+ #define odp_event_flow_id_set __odp_event_flow_id_set
+#else
+ #define _ODP_INLINE
+#endif
+
+static inline odp_event_type_t __odp_event_type_get(odp_event_t event)
+{
+ int8_t type;
+
+ type = _odp_event_hdr_field(event, int8_t, event_type);
+
+ return (odp_event_type_t)type;
+}
+
+static inline odp_event_subtype_t __odp_event_subtype_get(odp_event_t event)
+{
+ int8_t type;
+
+ type = _odp_event_hdr_field(event, int8_t, subtype);
+
+ return (odp_event_subtype_t)type;
+}
+
+_ODP_INLINE odp_event_type_t odp_event_type(odp_event_t event)
+{
+ return __odp_event_type_get(event);
+}
+
+_ODP_INLINE int odp_event_type_multi(const odp_event_t event[], int num,
+ odp_event_type_t *type_out)
+{
+ int i;
+ odp_event_type_t type = __odp_event_type_get(event[0]);
+
+ for (i = 1; i < num; i++) {
+ if (__odp_event_type_get(event[i]) != type)
+ break;
+ }
+
+ *type_out = type;
+
+ return i;
+}
+
+_ODP_INLINE odp_pool_t odp_event_pool(odp_event_t event)
+{
+ const odp_event_type_t type = __odp_event_type_get(event);
+
+ switch (type) {
+ case ODP_EVENT_BUFFER:
+ case ODP_EVENT_PACKET:
+ case ODP_EVENT_PACKET_VECTOR:
+ return _odp_event_hdr_field(event, odp_pool_t, pool);
+ default:
+ return ODP_POOL_INVALID;
+ }
+}
+
+_ODP_INLINE void *odp_event_user_area(odp_event_t event)
+{
+ const odp_event_type_t type = __odp_event_type_get(event);
+
+ switch (type) {
+ case ODP_EVENT_BUFFER:
+ case ODP_EVENT_ML_COMPL:
+ case ODP_EVENT_DMA_COMPL:
+ return _odp_buffer_get((odp_buffer_t)event, void *, uarea_addr);
+ case ODP_EVENT_PACKET:
+ return _odp_pkt_get((odp_packet_t)event, void *, user_area);
+ case ODP_EVENT_PACKET_VECTOR:
+ return _odp_event_vect_get((odp_packet_vector_t)event, void *, uarea_addr);
+ case ODP_EVENT_TIMEOUT:
+ return _odp_timeout_hdr_field((odp_timeout_t)event, void *, uarea_addr);
+ default:
+ return NULL;
+ }
+}
+
+_ODP_INLINE void *odp_event_user_area_and_flag(odp_event_t event, int *flag)
+{
+ const odp_event_type_t type = __odp_event_type_get(event);
+
+ _ODP_ASSERT(flag != NULL);
+
+ switch (type) {
+ case ODP_EVENT_BUFFER:
+ case ODP_EVENT_DMA_COMPL:
+ case ODP_EVENT_ML_COMPL:
+ *flag = -1;
+ return _odp_buffer_get((odp_buffer_t)event, void *, uarea_addr);
+ case ODP_EVENT_PACKET:
+ {
+ _odp_packet_flags_t pkt_flags;
+ odp_packet_t pkt = (odp_packet_t)event;
+
+ pkt_flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+ *flag = pkt_flags.user_flag;
+
+ return _odp_pkt_get(pkt, void *, user_area);
+ }
+ case ODP_EVENT_PACKET_VECTOR:
+ {
+ _odp_event_vector_flags_t pktv_flags;
+ odp_packet_vector_t pktv = (odp_packet_vector_t)event;
+
+ pktv_flags.all_flags = _odp_event_vect_get(pktv, uint32_t, flags);
+ *flag = pktv_flags.user_flag;
+
+ return _odp_event_vect_get(pktv, void *, uarea_addr);
+ }
+ case ODP_EVENT_TIMEOUT:
+ *flag = -1;
+ return _odp_timeout_hdr_field((odp_timeout_t)event, void *, uarea_addr);
+ default:
+ *flag = -1;
+ return NULL;
+ }
+}
+
+_ODP_INLINE odp_event_subtype_t odp_event_subtype(odp_event_t event)
+{
+ return __odp_event_subtype_get(event);
+}
+
+_ODP_INLINE odp_event_type_t odp_event_types(odp_event_t event,
+ odp_event_subtype_t *subtype)
+{
+ odp_event_type_t event_type = __odp_event_type_get(event);
+
+ *subtype = __odp_event_subtype_get(event);
+
+ return event_type;
+}
+
+_ODP_INLINE void odp_event_types_multi(const odp_event_t event[], odp_event_type_t type[],
+ odp_event_subtype_t subtype[], int num)
+{
+ for (int i = 0; i < num; i++)
+ type[i] = __odp_event_type_get(event[i]);
+
+ if (subtype == NULL)
+ return;
+
+ for (int i = 0; i < num; i++)
+ subtype[i] = __odp_event_subtype_get(event[i]);
+}
+
+_ODP_INLINE uint32_t odp_event_flow_id(odp_event_t event)
+{
+ return _odp_event_hdr_field(event, uint8_t, flow_id);
+}
+
+_ODP_INLINE void odp_event_flow_id_set(odp_event_t event, uint32_t id)
+{
+ uint8_t *flow_id = _odp_event_hdr_ptr(event, uint8_t, flow_id);
+
+ *flow_id = (uint8_t)id;
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/event_types.h b/platform/linux-generic/include/odp/api/plat/event_types.h
deleted file mode 100644
index 559a2fa00..000000000
--- a/platform/linux-generic/include/odp/api/plat/event_types.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP event
- */
-
-#ifndef ODP_EVENT_TYPES_H_
-#define ODP_EVENT_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/event.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_event
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_event_t);
-
-#define ODP_EVENT_INVALID _odp_cast_scalar(odp_event_t, NULL)
-
-typedef enum odp_event_type_t {
- ODP_EVENT_BUFFER = 1,
- ODP_EVENT_PACKET = 2,
- ODP_EVENT_TIMEOUT = 3,
- ODP_EVENT_CRYPTO_COMPL = 4,
-} odp_event_type_t;
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/event_validation_external.h b/platform/linux-generic/include/odp/api/plat/event_validation_external.h
new file mode 100644
index 000000000..05d98bcf2
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/event_validation_external.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event validation
+ *
+ * @warning These definitions are not part of ODP API, they are for
+ * implementation internal use only.
+ */
+
+#ifndef ODP_EVENT_VALIDATION_EXTERNAL_H_
+#define ODP_EVENT_VALIDATION_EXTERNAL_H_
+
+#include <odp/autoheader_external.h>
+
+#include <odp/api/buffer_types.h>
+#include <odp/api/event_types.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Enumerations for identifying ODP API functions */
+typedef enum {
+ _ODP_EV_BUFFER_FREE = 0,
+ _ODP_EV_BUFFER_FREE_MULTI,
+ _ODP_EV_BUFFER_IS_VALID,
+ _ODP_EV_EVENT_FREE,
+ _ODP_EV_EVENT_FREE_MULTI,
+ _ODP_EV_EVENT_FREE_SP,
+ _ODP_EV_EVENT_IS_VALID,
+ _ODP_EV_PACKET_FREE,
+ _ODP_EV_PACKET_FREE_MULTI,
+ _ODP_EV_PACKET_FREE_SP,
+ _ODP_EV_PACKET_IS_VALID,
+ _ODP_EV_QUEUE_ENQ,
+ _ODP_EV_QUEUE_ENQ_MULTI,
+ _ODP_EV_MAX
+} _odp_ev_id_t;
+
+/* Implementation internal event validation functions */
+#if _ODP_EVENT_VALIDATION
+
+int _odp_event_validate(odp_event_t event, _odp_ev_id_t id);
+
+int _odp_event_validate_multi(const odp_event_t event[], int num, _odp_ev_id_t id);
+
+int _odp_buffer_validate(odp_buffer_t buf, _odp_ev_id_t ev_id);
+
+int _odp_buffer_validate_multi(const odp_buffer_t buf[], int num, _odp_ev_id_t ev_id);
+
+int _odp_packet_validate(odp_packet_t pkt, _odp_ev_id_t ev_id);
+
+int _odp_packet_validate_multi(const odp_packet_t pkt[], int num, _odp_ev_id_t ev_id);
+
+#else
+
+static inline int _odp_event_validate(odp_event_t event ODP_UNUSED, _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+static inline int _odp_event_validate_multi(const odp_event_t event[] ODP_UNUSED,
+ int num ODP_UNUSED,
+ _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+static inline int _odp_buffer_validate(odp_buffer_t buf ODP_UNUSED, _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+static inline int _odp_buffer_validate_multi(const odp_buffer_t buf[] ODP_UNUSED,
+ int num ODP_UNUSED,
+ _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+static inline int _odp_packet_validate(odp_packet_t pkt ODP_UNUSED, _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+static inline int _odp_packet_validate_multi(const odp_packet_t pkt[] ODP_UNUSED,
+ int num ODP_UNUSED,
+ _odp_ev_id_t ev_id ODP_UNUSED)
+{
+ return 0;
+}
+
+#endif /* _ODP_EVENT_VALIDATION */
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/event_vector_inline_types.h b/platform/linux-generic/include/odp/api/plat/event_vector_inline_types.h
new file mode 100644
index 000000000..7b956be5d
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/event_vector_inline_types.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef ODP_PLAT_EVENT_VECTOR_INLINE_TYPES_H_
+#define ODP_PLAT_EVENT_VECTOR_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+typedef union {
+ uint32_t all_flags;
+
+ struct {
+ uint32_t user_flag : 1;
+ };
+
+} _odp_event_vector_flags_t;
+
+/* Event vector field accessors */
+#define _odp_event_vect_get(vect, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)vect + _odp_event_vector_inline.field))
+#define _odp_event_vect_get_ptr(vect, cast, field) \
+ ((cast *)(uintptr_t)((uint8_t *)vect + _odp_event_vector_inline.field))
+
+/* Event vector header field offsets for inline functions */
+typedef struct _odp_event_vector_inline_offset_t {
+ uint16_t packet;
+ uint16_t pool;
+ uint16_t size;
+ uint16_t uarea_addr;
+ uint16_t flags;
+
+} _odp_event_vector_inline_offset_t;
+
+extern const _odp_event_vector_inline_offset_t _odp_event_vector_inline;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_PLAT_EVENT_VECTOR_INLINE_TYPES_H_ */
diff --git a/platform/linux-generic/include/odp/api/plat/hash_inlines.h b/platform/linux-generic/include/odp/api/plat/hash_inlines.h
new file mode 100644
index 000000000..db781597f
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/hash_inlines.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_PLAT_HASH_INLINES_H_
+#define ODP_PLAT_HASH_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/hash_crc32.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_hash_crc32 __odp_hash_crc32
+ #define odp_hash_crc32c __odp_hash_crc32c
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE uint32_t odp_hash_crc32(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32(data, data_len, init_val);
+}
+
+_ODP_INLINE uint32_t odp_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ return _odp_hash_crc32c(data, data_len, init_val);
+}
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/init_types.h b/platform/linux-generic/include/odp/api/plat/init_types.h
deleted file mode 100644
index 888b04a70..000000000
--- a/platform/linux-generic/include/odp/api/plat/init_types.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP initialization.
- */
-
-#ifndef ODP_INIT_TYPES_H_
-#define ODP_INIT_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-
-typedef uint64_t odp_instance_t;
-
-/**
- * @internal platform specific data
- */
-typedef struct odp_platform_init_t {
- int ipc_ns; /**< Name space for ipc shared objects. */
-} odp_platform_init_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/ipsec_inlines.h b/platform/linux-generic/include/odp/api/plat/ipsec_inlines.h
new file mode 100644
index 000000000..924708371
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/ipsec_inlines.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_IPSEC_INLINES_H_
+#define ODP_PLAT_IPSEC_INLINES_H_
+
+#include <odp/api/event.h>
+#include <odp/api/ipsec_types.h>
+#include <odp/api/packet.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/packet_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_ipsec_packet_from_event __odp_ipsec_packet_from_event
+ #define odp_ipsec_packet_to_event __odp_ipsec_packet_to_event
+ #define odp_ipsec_result __odp_ipsec_result
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_packet_t odp_ipsec_packet_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+ _ODP_ASSERT(odp_event_subtype(ev) == ODP_EVENT_PACKET_IPSEC);
+
+ return odp_packet_from_event(ev);
+}
+
+_ODP_INLINE odp_event_t odp_ipsec_packet_to_event(odp_packet_t pkt)
+{
+ return odp_packet_to_event(pkt);
+}
+
+_ODP_INLINE int odp_ipsec_result(odp_ipsec_packet_result_t *result, odp_packet_t pkt)
+{
+ odp_ipsec_packet_result_t *res;
+
+ _ODP_ASSERT(result != NULL);
+ _ODP_ASSERT(odp_packet_subtype(pkt) == ODP_EVENT_PACKET_IPSEC);
+
+ res = _odp_pkt_get_ptr(pkt, odp_ipsec_packet_result_t, ipsec_ctx);
+
+ *result = *res;
+
+ return 0;
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_flag_inlines.h b/platform/linux-generic/include/odp/api/plat/packet_flag_inlines.h
index 42003e317..00ca1e694 100644
--- a/platform/linux-generic/include/odp/api/plat/packet_flag_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/packet_flag_inlines.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
*/
/**
@@ -13,20 +12,53 @@
#ifndef _ODP_PLAT_PACKET_FLAG_INLINES_H_
#define _ODP_PLAT_PACKET_FLAG_INLINES_H_
-#include <odp/api/plat/packet_types.h>
+#include <odp/api/abi/packet_types.h>
+#include <odp/api/plat/packet_inline_types.h>
#include <odp/api/hints.h>
-/** @internal Inline function offsets */
-extern const _odp_packet_inline_offset_t _odp_packet_inline;
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
-/** @internal Inline function @param pkt @return */
static inline uint64_t _odp_packet_input_flags(odp_packet_t pkt)
{
return _odp_pkt_get(pkt, uint64_t, input_flags);
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_has_l2(odp_packet_t pkt)
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_packet_has_l2 __odp_packet_has_l2
+ #define odp_packet_has_l3 __odp_packet_has_l3
+ #define odp_packet_has_l4 __odp_packet_has_l4
+ #define odp_packet_has_eth __odp_packet_has_eth
+ #define odp_packet_has_jumbo __odp_packet_has_jumbo
+ #define odp_packet_has_flow_hash __odp_packet_has_flow_hash
+ #define odp_packet_has_ts __odp_packet_has_ts
+ #define odp_packet_has_ipsec __odp_packet_has_ipsec
+ #define odp_packet_has_eth_bcast __odp_packet_has_eth_bcast
+ #define odp_packet_has_eth_mcast __odp_packet_has_eth_mcast
+ #define odp_packet_has_vlan __odp_packet_has_vlan
+ #define odp_packet_has_vlan_qinq __odp_packet_has_vlan_qinq
+ #define odp_packet_has_arp __odp_packet_has_arp
+ #define odp_packet_has_ipv4 __odp_packet_has_ipv4
+ #define odp_packet_has_ipv6 __odp_packet_has_ipv6
+ #define odp_packet_has_ip_bcast __odp_packet_has_ip_bcast
+ #define odp_packet_has_ip_mcast __odp_packet_has_ip_mcast
+ #define odp_packet_has_ipfrag __odp_packet_has_ipfrag
+ #define odp_packet_has_ipopt __odp_packet_has_ipopt
+ #define odp_packet_has_udp __odp_packet_has_udp
+ #define odp_packet_has_tcp __odp_packet_has_tcp
+ #define odp_packet_has_sctp __odp_packet_has_sctp
+ #define odp_packet_has_icmp __odp_packet_has_icmp
+ #define odp_packet_has_error __odp_packet_has_error
+ #define odp_packet_has_l2_error __odp_packet_has_l2_error
+ #define odp_packet_has_l3_error __odp_packet_has_l3_error
+ #define odp_packet_has_l4_error __odp_packet_has_l4_error
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE int odp_packet_has_l2(odp_packet_t pkt)
{
_odp_packet_input_flags_t flags;
@@ -34,8 +66,23 @@ static inline int _odp_packet_has_l2(odp_packet_t pkt)
return flags.l2;
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_has_eth(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_has_l3(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.l3;
+}
+
+_ODP_INLINE int odp_packet_has_l4(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.l4;
+}
+
+_ODP_INLINE int odp_packet_has_eth(odp_packet_t pkt)
{
_odp_packet_input_flags_t flags;
@@ -43,8 +90,7 @@ static inline int _odp_packet_has_eth(odp_packet_t pkt)
return flags.eth;
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_has_jumbo(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_has_jumbo(odp_packet_t pkt)
{
_odp_packet_input_flags_t flags;
@@ -52,8 +98,7 @@ static inline int _odp_packet_has_jumbo(odp_packet_t pkt)
return flags.jumbo;
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_has_flow_hash(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_has_flow_hash(odp_packet_t pkt)
{
_odp_packet_input_flags_t flags;
@@ -61,8 +106,7 @@ static inline int _odp_packet_has_flow_hash(odp_packet_t pkt)
return flags.flow_hash;
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_has_ts(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_has_ts(odp_packet_t pkt)
{
_odp_packet_input_flags_t flags;
@@ -70,20 +114,171 @@ static inline int _odp_packet_has_ts(odp_packet_t pkt)
return flags.timestamp;
}
-/* Include inlined versions of API functions */
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
+_ODP_INLINE int odp_packet_has_ipsec(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
-/** @ingroup odp_packet
- * @{
- */
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ipsec;
+}
-#include <odp/api/plat/packet_flag_inlines_api.h>
+_ODP_INLINE int odp_packet_has_eth_bcast(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
-/**
- * @}
- */
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.eth_bcast;
+}
-#endif
+_ODP_INLINE int odp_packet_has_eth_mcast(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.eth_mcast;
+}
+
+_ODP_INLINE int odp_packet_has_vlan(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.vlan;
+}
+
+_ODP_INLINE int odp_packet_has_vlan_qinq(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.vlan_qinq;
+}
+
+_ODP_INLINE int odp_packet_has_arp(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.arp;
+}
+
+_ODP_INLINE int odp_packet_has_ipv4(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ipv4;
+}
+
+_ODP_INLINE int odp_packet_has_ipv6(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ipv6;
+}
+
+_ODP_INLINE int odp_packet_has_ip_bcast(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ip_bcast;
+}
+
+_ODP_INLINE int odp_packet_has_ip_mcast(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ip_mcast;
+}
+
+_ODP_INLINE int odp_packet_has_ipfrag(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ipfrag;
+}
+
+_ODP_INLINE int odp_packet_has_ipopt(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.ipopt;
+}
+
+_ODP_INLINE int odp_packet_has_udp(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.udp;
+}
+
+_ODP_INLINE int odp_packet_has_tcp(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.tcp;
+}
+
+_ODP_INLINE int odp_packet_has_sctp(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.sctp;
+}
+
+_ODP_INLINE int odp_packet_has_icmp(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t flags;
+
+ flags.all = _odp_packet_input_flags(pkt);
+ return flags.icmp;
+}
+
+_ODP_INLINE int odp_packet_has_error(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+ return flags.all.error != 0;
+}
+
+_ODP_INLINE int odp_packet_has_l2_error(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ /* L2 parsing is always done by default and hence
+ no additional check is required. */
+ return flags.snap_len_err;
+}
+
+_ODP_INLINE int odp_packet_has_l3_error(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ return flags.ip_err;
+}
+
+_ODP_INLINE int odp_packet_has_l4_error(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ return flags.tcp_err | flags.udp_err;
+}
+
+/** @endcond */
#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_flag_inlines_api.h b/platform/linux-generic/include/odp/api/plat/packet_flag_inlines_api.h
deleted file mode 100644
index f4e143aa0..000000000
--- a/platform/linux-generic/include/odp/api/plat/packet_flag_inlines_api.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Packet inline functions
- */
-
-#ifndef _ODP_PLAT_PACKET_FLAG_INLINES_API_H_
-#define _ODP_PLAT_PACKET_FLAG_INLINES_API_H_
-
-_ODP_INLINE int odp_packet_has_l2(odp_packet_t pkt)
-{
- return _odp_packet_has_l2(pkt);
-}
-
-_ODP_INLINE int odp_packet_has_eth(odp_packet_t pkt)
-{
- return _odp_packet_has_eth(pkt);
-}
-
-_ODP_INLINE int odp_packet_has_jumbo(odp_packet_t pkt)
-{
- return _odp_packet_has_jumbo(pkt);
-}
-
-_ODP_INLINE int odp_packet_has_flow_hash(odp_packet_t pkt)
-{
- return _odp_packet_has_flow_hash(pkt);
-}
-
-_ODP_INLINE int odp_packet_has_ts(odp_packet_t pkt)
-{
- return _odp_packet_has_ts(pkt);
-}
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_inline_types.h b/platform/linux-generic/include/odp/api/plat/packet_inline_types.h
new file mode 100644
index 000000000..e819c48a3
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/packet_inline_types.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_PACKET_INLINE_TYPES_H_
+#define ODP_PACKET_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Packet field accessor */
+#define _odp_pkt_get(pkt, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)pkt + _odp_packet_inline.field))
+
+#define _odp_pkt_get_ptr(pkt, cast, field) \
+ ((cast *)(uintptr_t)((uint8_t *)pkt + _odp_packet_inline.field))
+
+/* Packet header field offsets for inline functions */
+typedef struct _odp_packet_inline_offset_t {
+ uint16_t seg_data;
+ uint16_t seg_len;
+ uint16_t seg_next;
+ uint16_t frame_len;
+ uint16_t headroom;
+ uint16_t tailroom;
+ uint16_t pool;
+ uint16_t input;
+ uint16_t seg_count;
+ uint16_t user_ptr;
+ uint16_t user_area;
+ uint16_t l2_offset;
+ uint16_t l3_offset;
+ uint16_t l4_offset;
+ uint16_t flow_hash;
+ uint16_t timestamp;
+ uint16_t input_flags;
+ uint16_t flags;
+ uint16_t cls_mark;
+ uint16_t ipsec_ctx;
+ uint16_t crypto_op;
+
+} _odp_packet_inline_offset_t;
+
+extern const _odp_packet_inline_offset_t _odp_packet_inline;
+
+/* Packet input & protocol flags */
+typedef union {
+ /* All input flags */
+ uint64_t all;
+
+ /* Individual input flags */
+ struct {
+ uint64_t dst_queue:1; /* Dst queue present */
+ uint64_t cls_mark: 1; /* Classifier mark value present*/
+
+ uint64_t flow_hash:1; /* Flow hash present */
+ uint64_t timestamp:1; /* Timestamp present */
+
+ uint64_t l2:1; /* known L2 protocol present */
+ uint64_t l3:1; /* known L3 protocol present */
+ uint64_t l4:1; /* known L4 protocol present */
+
+ uint64_t eth:1; /* Ethernet */
+ uint64_t eth_bcast:1; /* Ethernet broadcast */
+ uint64_t eth_mcast:1; /* Ethernet multicast */
+ uint64_t jumbo:1; /* Jumbo frame */
+ uint64_t vlan:1; /* VLAN hdr found */
+ uint64_t vlan_qinq:1; /* Stacked VLAN found, QinQ */
+
+ uint64_t snap:1; /* SNAP */
+ uint64_t arp:1; /* ARP */
+
+ uint64_t ipv4:1; /* IPv4 */
+ uint64_t ipv6:1; /* IPv6 */
+ uint64_t ip_bcast:1; /* IP broadcast */
+ uint64_t ip_mcast:1; /* IP multicast */
+ uint64_t ipfrag:1; /* IP fragment */
+ uint64_t ipopt:1; /* IP optional headers */
+
+ uint64_t ipsec:1; /* IPSec packet. Required by the
+ odp_packet_has_ipsec_set() func. */
+ uint64_t ipsec_ah:1; /* IPSec authentication header */
+ uint64_t ipsec_esp:1; /* IPSec encapsulating security
+ payload */
+ uint64_t udp:1; /* UDP */
+ uint64_t tcp:1; /* TCP */
+ uint64_t sctp:1; /* SCTP */
+ uint64_t icmp:1; /* ICMP */
+ uint64_t no_next_hdr:1; /* No Next Header */
+
+ uint64_t color:2; /* Packet color for traffic mgmt */
+ uint64_t nodrop:1; /* Drop eligibility status */
+
+ uint64_t l3_chksum_done:1; /* L3 checksum validation done */
+ uint64_t l4_chksum_done:1; /* L4 checksum validation done */
+ uint64_t ipsec_udp:1; /* UDP-encapsulated IPsec packet */
+ uint64_t udp_chksum_zero:1; /* UDP header had 0 as chksum */
+ };
+
+} _odp_packet_input_flags_t;
+
+/*
+ * Additional packet flags
+ */
+typedef union {
+ /* All flags */
+ uint32_t all_flags;
+
+ struct {
+ uint32_t reserved1: 4;
+
+ /*
+ * Init flags
+ */
+ uint32_t user_ptr_set: 1; /* User has set a non-NULL value */
+ uint32_t user_flag: 1;
+
+ /*
+ * Packet output flags
+ */
+ uint32_t lso: 1; /* LSO requested */
+ uint32_t payload_off: 1; /* Payload offset is valid */
+ uint32_t l3_chksum_set: 1; /* L3 chksum bit is valid */
+ uint32_t l3_chksum: 1; /* L3 chksum override */
+ uint32_t l4_chksum_set: 1; /* L4 chksum bit is valid */
+ uint32_t l4_chksum: 1; /* L4 chksum override */
+ uint32_t ts_set: 1; /* Set Tx timestamp */
+ uint32_t tx_compl_ev: 1; /* Tx completion event requested */
+ uint32_t tx_compl_poll: 1; /* Tx completion poll requested */
+ uint32_t free_ctrl: 1; /* Don't free option */
+ uint32_t tx_aging: 1; /* Packet aging at Tx requested */
+ uint32_t shaper_len_adj: 8; /* Adjustment for traffic mgr */
+
+ /*
+ * Error flags
+ */
+ uint32_t snap_len_err: 1; /* Snap length error */
+ uint32_t ip_err: 1; /* IP error */
+ uint32_t l3_chksum_err: 1; /* L3 checksum error */
+ uint32_t tcp_err: 1; /* TCP error */
+ uint32_t udp_err: 1; /* UDP error */
+ uint32_t sctp_err: 1; /* SCTP error */
+ uint32_t l4_chksum_err: 1; /* L4 checksum error */
+ };
+
+ /* Flag groups */
+ struct {
+ uint32_t reserved2: 4;
+ uint32_t other: 21; /* All other flags */
+ uint32_t error: 7; /* All error flags */
+ } all;
+
+} _odp_packet_flags_t;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_inlines.h b/platform/linux-generic/include/odp/api/plat/packet_inlines.h
index 06b049fcb..fcd1836b2 100644
--- a/platform/linux-generic/include/odp/api/plat/packet_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/packet_inlines.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
/**
@@ -13,154 +12,671 @@
#ifndef _ODP_PLAT_PACKET_INLINES_H_
#define _ODP_PLAT_PACKET_INLINES_H_
-#include <odp/api/plat/packet_types.h>
-#include <odp/api/pool.h>
-#include <odp/api/packet_io.h>
+#include <odp/api/event.h>
#include <odp/api/hints.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/time_types.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/packet_io_inlines.h>
+#include <odp/api/plat/packet_inline_types.h>
+#include <odp/api/plat/pool_inline_types.h>
+#include <odp/api/plat/event_inline_types.h>
+
+#include <stdint.h>
+#include <string.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_packet_data __odp_packet_data
+ #define odp_packet_seg_len __odp_packet_seg_len
+ #define odp_packet_data_seg_len __odp_packet_data_seg_len
+ #define odp_packet_len __odp_packet_len
+ #define odp_packet_headroom __odp_packet_headroom
+ #define odp_packet_tailroom __odp_packet_tailroom
+ #define odp_packet_pool __odp_packet_pool
+ #define odp_packet_input __odp_packet_input
+ #define odp_packet_input_set __odp_packet_input_set
+ #define odp_packet_input_index __odp_packet_input_index
+ #define odp_packet_num_segs __odp_packet_num_segs
+ #define odp_packet_user_ptr __odp_packet_user_ptr
+ #define odp_packet_user_ptr_set __odp_packet_user_ptr_set
+ #define odp_packet_user_area __odp_packet_user_area
+ #define odp_packet_user_area_size __odp_packet_user_area_size
+ #define odp_packet_user_flag __odp_packet_user_flag
+ #define odp_packet_user_flag_set __odp_packet_user_flag_set
+ #define odp_packet_l2_offset __odp_packet_l2_offset
+ #define odp_packet_l3_offset __odp_packet_l3_offset
+ #define odp_packet_l4_offset __odp_packet_l4_offset
+ #define odp_packet_l2_offset_set __odp_packet_l2_offset_set
+ #define odp_packet_l3_offset_set __odp_packet_l3_offset_set
+ #define odp_packet_l4_offset_set __odp_packet_l4_offset_set
+ #define odp_packet_l2_ptr __odp_packet_l2_ptr
+ #define odp_packet_l3_ptr __odp_packet_l3_ptr
+ #define odp_packet_l4_ptr __odp_packet_l4_ptr
+ #define odp_packet_l2_type __odp_packet_l2_type
+ #define odp_packet_l3_type __odp_packet_l3_type
+ #define odp_packet_l4_type __odp_packet_l4_type
+ #define odp_packet_l3_chksum_status __odp_packet_l3_chksum_status
+ #define odp_packet_l4_chksum_status __odp_packet_l4_chksum_status
+ #define odp_packet_l3_chksum_insert __odp_packet_l3_chksum_insert
+ #define odp_packet_l4_chksum_insert __odp_packet_l4_chksum_insert
+ #define odp_packet_flow_hash __odp_packet_flow_hash
+ #define odp_packet_ts __odp_packet_ts
+ #define odp_packet_ts_set __odp_packet_ts_set
+ #define odp_packet_ts_request __odp_packet_ts_request
+ #define odp_packet_head __odp_packet_head
+ #define odp_packet_is_segmented __odp_packet_is_segmented
+ #define odp_packet_first_seg __odp_packet_first_seg
+ #define odp_packet_seg_data __odp_packet_seg_data
+ #define odp_packet_seg_data_len __odp_packet_seg_data_len
+ #define odp_packet_next_seg __odp_packet_next_seg
+ #define odp_packet_prefetch __odp_packet_prefetch
+ #define odp_packet_copy_from_mem __odp_packet_copy_from_mem
+ #define odp_packet_copy_to_mem __odp_packet_copy_to_mem
+ #define odp_packet_from_event __odp_packet_from_event
+ #define odp_packet_to_event __odp_packet_to_event
+ #define odp_packet_from_event_multi __odp_packet_from_event_multi
+ #define odp_packet_to_event_multi __odp_packet_to_event_multi
+ #define odp_packet_subtype __odp_packet_subtype
+ #define odp_packet_tx_compl_from_event __odp_packet_tx_compl_from_event
+ #define odp_packet_tx_compl_to_event __odp_packet_tx_compl_to_event
+ #define odp_packet_color __odp_packet_color
+ #define odp_packet_drop_eligible __odp_packet_drop_eligible
+ #define odp_packet_shaper_len_adjust __odp_packet_shaper_len_adjust
+ #define odp_packet_cls_mark __odp_packet_cls_mark
+ #define odp_packet_buf_data_len __odp_packet_buf_data_len
+ #define odp_packet_buf_size __odp_packet_buf_size
+ #define odp_packet_buf_head __odp_packet_buf_head
+ #define odp_packet_buf_data_offset __odp_packet_buf_data_offset
+ #define odp_packet_buf_data_set __odp_packet_buf_data_set
+ #define odp_packet_buf_from_head __odp_packet_buf_from_head
+
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+void *_odp_packet_map(void *pkt_ptr, uint32_t offset, uint32_t *seg_len,
+ odp_packet_seg_t *seg);
+
+int _odp_packet_copy_from_mem_seg(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, const void *src);
-/** @internal Inline function offsets */
-extern const _odp_packet_inline_offset_t _odp_packet_inline;
+int _odp_packet_copy_to_mem_seg(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, void *dst);
-#if ODP_ABI_COMPAT == 1
-/** @internal Inline function @param seg @return */
-static inline uint32_t _odp_packet_seg_to_ndx(odp_packet_seg_t seg)
+_ODP_INLINE void *odp_packet_data(odp_packet_t pkt)
{
- return _odp_typeval(seg);
+ return _odp_pkt_get(pkt, void *, seg_data);
}
-/** @internal Inline function @param ndx @return */
-static inline odp_packet_seg_t _odp_packet_seg_from_ndx(uint32_t ndx)
+_ODP_INLINE uint32_t odp_packet_seg_len(odp_packet_t pkt)
{
- return _odp_cast_scalar(odp_packet_seg_t, ndx);
+ return _odp_pkt_get(pkt, uint32_t, seg_len);
}
-#endif
-/** @internal Inline function @param pkt @return */
-static inline void *_odp_packet_data(odp_packet_t pkt)
+_ODP_INLINE void *odp_packet_data_seg_len(odp_packet_t pkt,
+ uint32_t *seg_len)
{
- return _odp_pkt_get(pkt, void *, data);
+ *seg_len = odp_packet_seg_len(pkt);
+ return odp_packet_data(pkt);
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_seg_len(odp_packet_t pkt)
+_ODP_INLINE uint32_t odp_packet_len(odp_packet_t pkt)
{
- return _odp_pkt_get(pkt, uint32_t, seg_len);
+ return _odp_pkt_get(pkt, uint32_t, frame_len);
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_len(odp_packet_t pkt)
+_ODP_INLINE uint32_t odp_packet_headroom(odp_packet_t pkt)
{
- uint32_t pkt_len = _odp_pkt_get(pkt, uint32_t, frame_len);
- void *ref_nxt = _odp_pkt_get(pkt, void *, ref_hdr);
- void *ref_pkt = (void *)pkt;
-
- while (ref_nxt) {
- pkt_len += _odp_pkt_get(ref_pkt, uint32_t, ref_len) -
- _odp_pkt_get(ref_pkt, uint32_t, ref_offset);
+ return _odp_pkt_get(pkt, uint16_t, headroom);
+}
- ref_pkt = ref_nxt;
- ref_nxt = _odp_pkt_get(ref_nxt, void *, ref_hdr);
- }
+_ODP_INLINE uint32_t odp_packet_tailroom(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, uint16_t, tailroom);
+}
- return pkt_len;
+_ODP_INLINE odp_pool_t odp_packet_pool(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, odp_pool_t, pool);
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_headroom(odp_packet_t pkt)
+_ODP_INLINE odp_pktio_t odp_packet_input(odp_packet_t pkt)
{
- return _odp_pkt_get(pkt, uint32_t, headroom);
+ return _odp_pkt_get(pkt, odp_pktio_t, input);
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_tailroom(odp_packet_t pkt)
+_ODP_INLINE void odp_packet_input_set(odp_packet_t pkt, odp_pktio_t pktio)
{
- return _odp_pkt_get(pkt, uint32_t, tailroom);
+ odp_pktio_t *pktio_ptr = _odp_pkt_get_ptr(pkt, odp_pktio_t, input);
+
+ *pktio_ptr = pktio;
}
-/** @internal Inline function @param pkt @return */
-static inline odp_pool_t _odp_packet_pool(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_input_index(odp_packet_t pkt)
{
- return _odp_pkt_get(pkt, odp_pool_t, pool);
+ odp_pktio_t pktio = odp_packet_input(pkt);
+
+ return odp_pktio_index(pktio);
}
-/** @internal Inline function @param pkt @return */
-static inline odp_pktio_t _odp_packet_input(odp_packet_t pkt)
+_ODP_INLINE int odp_packet_num_segs(odp_packet_t pkt)
{
- return _odp_pkt_get(pkt, odp_pktio_t, input);
+ return _odp_pkt_get(pkt, uint8_t, seg_count);
}
-/** @internal Inline function @param pkt @return */
-static inline void *_odp_packet_user_ptr(odp_packet_t pkt)
+_ODP_INLINE void *odp_packet_user_ptr(odp_packet_t pkt)
{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ if (flags.user_ptr_set == 0)
+ return NULL;
+
return _odp_pkt_get(pkt, void *, user_ptr);
}
-/** @internal Inline function @param pkt @return */
-static inline void *_odp_packet_user_area(odp_packet_t pkt)
+_ODP_INLINE void odp_packet_user_ptr_set(odp_packet_t pkt, const void *ptr)
+{
+ _odp_packet_flags_t *flags = _odp_pkt_get_ptr(pkt, _odp_packet_flags_t, flags);
+ const void **user_ptr = _odp_pkt_get_ptr(pkt, const void *, user_ptr);
+
+ if (odp_unlikely(ptr == NULL)) {
+ flags->user_ptr_set = 0;
+ return;
+ }
+
+ *user_ptr = ptr;
+ flags->user_ptr_set = 1;
+}
+
+_ODP_INLINE void *odp_packet_user_area(odp_packet_t pkt)
{
return _odp_pkt_get(pkt, void *, user_area);
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_user_area_size(odp_packet_t pkt)
+_ODP_INLINE uint32_t odp_packet_user_area_size(odp_packet_t pkt)
+{
+ void *pool = _odp_pkt_get(pkt, void *, pool);
+
+ return _odp_pool_get(pool, uint32_t, uarea_size);
+}
+
+_ODP_INLINE int odp_packet_user_flag(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ return flags.user_flag;
+}
+
+_ODP_INLINE void odp_packet_user_flag_set(odp_packet_t pkt, int val)
+{
+ _odp_packet_flags_t *flags = _odp_pkt_get_ptr(pkt, _odp_packet_flags_t, flags);
+
+ flags->user_flag = !!val;
+}
+
+_ODP_INLINE uint32_t odp_packet_l2_offset(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, uint16_t, l2_offset);
+}
+
+_ODP_INLINE uint32_t odp_packet_l3_offset(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, uint16_t, l3_offset);
+}
+
+_ODP_INLINE uint32_t odp_packet_l4_offset(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, uint16_t, l4_offset);
+}
+
+_ODP_INLINE int odp_packet_l2_offset_set(odp_packet_t pkt, uint32_t offset)
+{
+ uint16_t *l2_offset = _odp_pkt_get_ptr(pkt, uint16_t, l2_offset);
+ _odp_packet_input_flags_t *input_flags = _odp_pkt_get_ptr(pkt, _odp_packet_input_flags_t,
+ input_flags);
+
+ if (odp_unlikely(offset >= odp_packet_len(pkt)))
+ return -1;
+
+ input_flags->l2 = 1;
+ *l2_offset = (uint16_t)offset;
+ return 0;
+}
+
+_ODP_INLINE int odp_packet_l3_offset_set(odp_packet_t pkt, uint32_t offset)
+{
+ uint16_t *l3_offset = _odp_pkt_get_ptr(pkt, uint16_t, l3_offset);
+
+ if (odp_unlikely(offset >= odp_packet_len(pkt)))
+ return -1;
+
+ *l3_offset = (uint16_t)offset;
+ return 0;
+}
+
+_ODP_INLINE int odp_packet_l4_offset_set(odp_packet_t pkt, uint32_t offset)
+{
+ uint16_t *l4_offset = _odp_pkt_get_ptr(pkt, uint16_t, l4_offset);
+
+ if (odp_unlikely(offset >= odp_packet_len(pkt)))
+ return -1;
+
+ *l4_offset = (uint16_t)offset;
+ return 0;
+}
+
+_ODP_INLINE void *odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
+{
+ uint32_t offset = odp_packet_l2_offset(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
+
+ if (odp_unlikely(offset >= seg_len)) {
+ void *pkt_hdr = (void *)pkt;
+
+ return _odp_packet_map(pkt_hdr, offset, len, NULL);
+ }
+
+ if (len)
+ *len = seg_len - offset;
+
+ return data + offset;
+}
+
+_ODP_INLINE void *odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len)
+{
+ uint32_t offset = odp_packet_l3_offset(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
+
+ if (odp_unlikely(offset >= seg_len)) {
+ void *pkt_hdr = (void *)pkt;
+
+ return _odp_packet_map(pkt_hdr, offset, len, NULL);
+ }
+
+ if (len)
+ *len = seg_len - offset;
+
+ return data + offset;
+}
+
+_ODP_INLINE void *odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
+{
+ uint32_t offset = odp_packet_l4_offset(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
+
+ if (odp_unlikely(offset >= seg_len)) {
+ void *pkt_hdr = (void *)pkt;
+
+ return _odp_packet_map(pkt_hdr, offset, len, NULL);
+ }
+
+ if (len)
+ *len = seg_len - offset;
+
+ return data + offset;
+}
+
+_ODP_INLINE odp_proto_l2_type_t odp_packet_l2_type(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ return input_flags.eth ? ODP_PROTO_L2_TYPE_ETH : ODP_PROTO_L2_TYPE_NONE;
+}
+
+_ODP_INLINE odp_proto_l3_type_t odp_packet_l3_type(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ if (input_flags.ipv4)
+ return ODP_PROTO_L3_TYPE_IPV4;
+ else if (input_flags.ipv6)
+ return ODP_PROTO_L3_TYPE_IPV6;
+ else if (input_flags.arp)
+ return ODP_PROTO_L3_TYPE_ARP;
+
+ return ODP_PROTO_L3_TYPE_NONE;
+}
+
+_ODP_INLINE odp_proto_l4_type_t odp_packet_l4_type(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ if (input_flags.tcp)
+ return ODP_PROTO_L4_TYPE_TCP;
+ else if (input_flags.udp)
+ return ODP_PROTO_L4_TYPE_UDP;
+ else if (input_flags.sctp)
+ return ODP_PROTO_L4_TYPE_SCTP;
+ else if (input_flags.ipsec_ah)
+ return ODP_PROTO_L4_TYPE_AH;
+ else if (input_flags.ipsec_esp)
+ return ODP_PROTO_L4_TYPE_ESP;
+ else if (input_flags.icmp && input_flags.ipv4)
+ return ODP_PROTO_L4_TYPE_ICMPV4;
+ else if (input_flags.icmp && input_flags.ipv6)
+ return ODP_PROTO_L4_TYPE_ICMPV6;
+ else if (input_flags.no_next_hdr)
+ return ODP_PROTO_L4_TYPE_NO_NEXT;
+
+ return ODP_PROTO_L4_TYPE_NONE;
+}
+
+_ODP_INLINE odp_packet_chksum_status_t odp_packet_l3_chksum_status(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+ _odp_packet_input_flags_t input_flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ if (!input_flags.l3_chksum_done)
+ return ODP_PACKET_CHKSUM_UNKNOWN;
+
+ if (flags.l3_chksum_err)
+ return ODP_PACKET_CHKSUM_BAD;
+
+ return ODP_PACKET_CHKSUM_OK;
+}
+
+_ODP_INLINE odp_packet_chksum_status_t odp_packet_l4_chksum_status(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+ _odp_packet_input_flags_t input_flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ if (!input_flags.l4_chksum_done)
+ return ODP_PACKET_CHKSUM_UNKNOWN;
+
+ if (flags.l4_chksum_err)
+ return ODP_PACKET_CHKSUM_BAD;
+
+ return ODP_PACKET_CHKSUM_OK;
+}
+
+_ODP_INLINE void odp_packet_l3_chksum_insert(odp_packet_t pkt, int insert)
+{
+ _odp_packet_flags_t *flags = _odp_pkt_get_ptr(pkt, _odp_packet_flags_t, flags);
+
+ flags->l3_chksum_set = 1;
+ flags->l3_chksum = !!insert;
+}
+
+_ODP_INLINE void odp_packet_l4_chksum_insert(odp_packet_t pkt, int insert)
{
- return _odp_pkt_get(pkt, uint32_t, user_area_size);
+ _odp_packet_flags_t *flags = _odp_pkt_get_ptr(pkt, _odp_packet_flags_t, flags);
+
+ flags->l4_chksum_set = 1;
+ flags->l4_chksum = !!insert;
}
-/** @internal Inline function @param pkt @return */
-static inline uint32_t _odp_packet_flow_hash(odp_packet_t pkt)
+_ODP_INLINE uint32_t odp_packet_flow_hash(odp_packet_t pkt)
{
return _odp_pkt_get(pkt, uint32_t, flow_hash);
}
-/** @internal Inline function @param pkt @return */
-static inline odp_time_t _odp_packet_ts(odp_packet_t pkt)
+_ODP_INLINE odp_time_t odp_packet_ts(odp_packet_t pkt)
{
return _odp_pkt_get(pkt, odp_time_t, timestamp);
}
-/** @internal Inline function @param pkt @return */
-static inline void *_odp_packet_head(odp_packet_t pkt)
+_ODP_INLINE void odp_packet_ts_set(odp_packet_t pkt, odp_time_t timestamp)
+{
+ odp_time_t *ts = _odp_pkt_get_ptr(pkt, odp_time_t, timestamp);
+ _odp_packet_input_flags_t *input_flags = _odp_pkt_get_ptr(pkt, _odp_packet_input_flags_t,
+ input_flags);
+
+ *ts = timestamp;
+ input_flags->timestamp = 1;
+}
+
+_ODP_INLINE void odp_packet_ts_request(odp_packet_t pkt, int enable)
{
- return (uint8_t *)_odp_packet_data(pkt) - _odp_packet_headroom(pkt);
+ _odp_packet_flags_t *flags = _odp_pkt_get_ptr(pkt, _odp_packet_flags_t, flags);
+
+ flags->ts_set = !!enable;
+}
+
+_ODP_INLINE void *odp_packet_head(odp_packet_t pkt)
+{
+ return (uint8_t *)odp_packet_data(pkt) - odp_packet_headroom(pkt);
+}
+
+_ODP_INLINE int odp_packet_is_segmented(odp_packet_t pkt)
+{
+ return _odp_pkt_get(pkt, uint8_t, seg_count) > 1;
}
-/** @internal Inline function @param pkt @return */
-static inline int _odp_packet_is_segmented(odp_packet_t pkt)
+_ODP_INLINE odp_packet_seg_t odp_packet_first_seg(odp_packet_t pkt)
{
- return _odp_pkt_get(pkt, uint8_t, segcount) > 1 ||
- _odp_pkt_get(pkt, void *, ref_hdr) != NULL;
+ return (odp_packet_seg_t)pkt;
}
-/** @internal Inline function @param pkt @return */
-static inline odp_packet_seg_t _odp_packet_first_seg(odp_packet_t pkt)
+_ODP_INLINE void *odp_packet_seg_data(odp_packet_t pkt ODP_UNUSED,
+ odp_packet_seg_t seg)
{
- (void)pkt;
+ return _odp_pkt_get((odp_packet_t)seg, void *, seg_data);
+}
+
+_ODP_INLINE uint32_t odp_packet_seg_data_len(odp_packet_t pkt ODP_UNUSED,
+ odp_packet_seg_t seg)
+{
+ return _odp_pkt_get((odp_packet_t)seg, uint32_t, seg_len);
+}
+
+_ODP_INLINE odp_packet_seg_t odp_packet_next_seg(odp_packet_t pkt ODP_UNUSED,
+ odp_packet_seg_t seg)
+{
+ void *next_seg = _odp_pkt_get((odp_packet_t)seg, void *, seg_next);
+
+ if (odp_unlikely(next_seg == NULL))
+ return ODP_PACKET_SEG_INVALID;
- return _odp_packet_seg_from_ndx(0);
+ return (odp_packet_seg_t)next_seg;
}
-/** @internal Inline function @param pkt @param offset @param len */
-static inline void _odp_packet_prefetch(odp_packet_t pkt, uint32_t offset,
+_ODP_INLINE void odp_packet_prefetch(odp_packet_t pkt, uint32_t offset,
uint32_t len)
{
- (void)pkt; (void)offset; (void)len;
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
+ (void)len;
+
+ if (odp_unlikely(offset >= seg_len))
+ return;
+
+ odp_prefetch(data + offset);
}
-/* Include inlined versions of API functions */
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
+_ODP_INLINE int odp_packet_copy_from_mem(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, const void *src)
+{
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
-/** @ingroup odp_packet
- * @{
- */
+ if (odp_unlikely(offset + len > seg_len))
+ return _odp_packet_copy_from_mem_seg(pkt, offset, len, src);
-#include <odp/api/plat/packet_inlines_api.h>
+ memcpy(data + offset, src, len);
-/**
- * @}
- */
+ return 0;
+}
-#endif
+_ODP_INLINE int odp_packet_copy_to_mem(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, void *dst)
+{
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+ uint8_t *data = (uint8_t *)odp_packet_data(pkt);
+
+ if (odp_unlikely(offset + len > seg_len))
+ return _odp_packet_copy_to_mem_seg(pkt, offset, len, dst);
+
+ memcpy(dst, data + offset, len);
+
+ return 0;
+}
+
+_ODP_INLINE odp_packet_t odp_packet_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+
+ return (odp_packet_t)ev;
+}
+
+_ODP_INLINE odp_event_t odp_packet_to_event(odp_packet_t pkt)
+{
+ return (odp_event_t)pkt;
+}
+
+_ODP_INLINE void odp_packet_from_event_multi(odp_packet_t pkt[],
+ const odp_event_t ev[],
+ int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ pkt[i] = odp_packet_from_event(ev[i]);
+}
+
+_ODP_INLINE void odp_packet_to_event_multi(const odp_packet_t pkt[],
+ odp_event_t ev[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ ev[i] = odp_packet_to_event(pkt[i]);
+}
+
+_ODP_INLINE odp_event_subtype_t odp_packet_subtype(odp_packet_t pkt)
+{
+ return (odp_event_subtype_t)_odp_event_hdr_field((odp_event_t)(uintptr_t)pkt,
+ int8_t, subtype);
+}
+
+_ODP_INLINE odp_packet_tx_compl_t odp_packet_tx_compl_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET_TX_COMPL);
+
+ return (odp_packet_tx_compl_t)(uintptr_t)ev;
+}
+
+_ODP_INLINE odp_event_t odp_packet_tx_compl_to_event(odp_packet_tx_compl_t tx_compl)
+{
+ return (odp_event_t)(uintptr_t)tx_compl;
+}
+
+_ODP_INLINE odp_packet_color_t odp_packet_color(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ return (odp_packet_color_t)input_flags.color;
+}
+
+_ODP_INLINE odp_bool_t odp_packet_drop_eligible(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ return !input_flags.nodrop;
+}
+
+_ODP_INLINE int8_t odp_packet_shaper_len_adjust(odp_packet_t pkt)
+{
+ _odp_packet_flags_t flags;
+
+ flags.all_flags = _odp_pkt_get(pkt, uint32_t, flags);
+
+ return (int8_t)flags.shaper_len_adj;
+}
+
+_ODP_INLINE uint64_t odp_packet_cls_mark(odp_packet_t pkt)
+{
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = _odp_pkt_get(pkt, uint64_t, input_flags);
+
+ return input_flags.cls_mark ? _odp_pkt_get(pkt, uint16_t, cls_mark) : 0;
+}
+
+_ODP_INLINE uint32_t odp_packet_buf_data_len(odp_packet_buf_t pkt_buf)
+{
+ return _odp_pkt_get(pkt_buf, uint32_t, seg_len);
+}
+
+_ODP_INLINE uint32_t odp_packet_buf_size(odp_packet_buf_t pkt_buf)
+{
+ odp_pool_t pool = _odp_pkt_get(pkt_buf, odp_pool_t, pool);
+
+ return _odp_pool_get(pool, uint32_t, ext_pkt_buf_size) -
+ _odp_pool_get(pool, uint32_t, ext_head_offset) -
+ _odp_pool_get(pool, uint32_t, trailer_size);
+}
+
+_ODP_INLINE void *odp_packet_buf_head(odp_packet_buf_t pkt_buf)
+{
+ odp_pool_t pool = _odp_pkt_get(pkt_buf, odp_pool_t, pool);
+ const uint32_t head_offset = _odp_pool_get(pool, uint32_t, ext_head_offset);
+
+ /* Check that pool is external */
+ if (odp_unlikely(!head_offset))
+ return NULL;
+
+ return (uint8_t *)(uintptr_t)pkt_buf + head_offset;
+}
+
+_ODP_INLINE uint32_t odp_packet_buf_data_offset(odp_packet_buf_t pkt_buf)
+{
+ void *buf_head = odp_packet_buf_head(pkt_buf);
+
+ return (uint32_t)((uintptr_t)_odp_pkt_get(pkt_buf, void *, seg_data) - (uintptr_t)buf_head);
+}
+
+_ODP_INLINE void odp_packet_buf_data_set(odp_packet_buf_t pkt_buf, uint32_t data_offset,
+ uint32_t data_len)
+{
+ uint8_t *head = (uint8_t *)odp_packet_buf_head(pkt_buf);
+ uint32_t *seg_len = _odp_pkt_get_ptr(pkt_buf, uint32_t, seg_len);
+ void **seg_data = _odp_pkt_get_ptr(pkt_buf, void *, seg_data);
+
+ *seg_len = data_len;
+ *seg_data = head + data_offset;
+}
+
+_ODP_INLINE odp_packet_buf_t odp_packet_buf_from_head(odp_pool_t pool, void *head)
+{
+ const uint32_t head_offset = _odp_pool_get(pool, uint32_t, ext_head_offset);
+
+ /* Check that pool is external */
+ if (odp_unlikely(!head_offset))
+ return ODP_PACKET_BUF_INVALID;
+
+ return (odp_packet_buf_t)((uintptr_t)head - head_offset);
+}
+
+/** @endcond */
#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_inlines_api.h b/platform/linux-generic/include/odp/api/plat/packet_inlines_api.h
deleted file mode 100644
index f818f820e..000000000
--- a/platform/linux-generic/include/odp/api/plat/packet_inlines_api.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Packet inline functions
- */
-
-#ifndef _ODP_PLAT_PACKET_INLINES_API_H_
-#define _ODP_PLAT_PACKET_INLINES_API_H_
-
-_ODP_INLINE void *odp_packet_data(odp_packet_t pkt)
-{
- return _odp_packet_data(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_seg_len(odp_packet_t pkt)
-{
- return _odp_packet_seg_len(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_len(odp_packet_t pkt)
-{
- return _odp_packet_len(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_headroom(odp_packet_t pkt)
-{
- return _odp_packet_headroom(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_tailroom(odp_packet_t pkt)
-{
- return _odp_packet_tailroom(pkt);
-}
-
-_ODP_INLINE odp_pool_t odp_packet_pool(odp_packet_t pkt)
-{
- return _odp_packet_pool(pkt);
-}
-
-_ODP_INLINE odp_pktio_t odp_packet_input(odp_packet_t pkt)
-{
- return _odp_packet_input(pkt);
-}
-
-_ODP_INLINE void *odp_packet_user_ptr(odp_packet_t pkt)
-{
- return _odp_packet_user_ptr(pkt);
-}
-
-_ODP_INLINE void *odp_packet_user_area(odp_packet_t pkt)
-{
- return _odp_packet_user_area(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_user_area_size(odp_packet_t pkt)
-{
- return _odp_packet_user_area_size(pkt);
-}
-
-_ODP_INLINE uint32_t odp_packet_flow_hash(odp_packet_t pkt)
-{
- return _odp_packet_flow_hash(pkt);
-}
-
-_ODP_INLINE odp_time_t odp_packet_ts(odp_packet_t pkt)
-{
- return _odp_packet_ts(pkt);
-}
-
-_ODP_INLINE void *odp_packet_head(odp_packet_t pkt)
-{
- return _odp_packet_head(pkt);
-}
-
-_ODP_INLINE int odp_packet_is_segmented(odp_packet_t pkt)
-{
- return _odp_packet_is_segmented(pkt);
-}
-
-_ODP_INLINE odp_packet_seg_t odp_packet_first_seg(odp_packet_t pkt)
-{
- return _odp_packet_first_seg(pkt);
-}
-
-_ODP_INLINE void odp_packet_prefetch(odp_packet_t pkt, uint32_t offset,
- uint32_t len)
-{
- return _odp_packet_prefetch(pkt, offset, len);
-}
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_io_inlines.h b/platform/linux-generic/include/odp/api/plat/packet_io_inlines.h
new file mode 100644
index 000000000..5c7a815b5
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/packet_io_inlines.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_PACKET_IO_INLINES_H_
+#define ODP_PLAT_PACKET_IO_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/abi/packet_io_types.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_pktio_index __odp_pktio_index
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE int odp_pktio_index(odp_pktio_t pktio)
+{
+ return (int)(uintptr_t)pktio - 1;
+}
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_io_types.h b/platform/linux-generic/include/odp/api/plat/packet_io_types.h
deleted file mode 100644
index 5a45321fb..000000000
--- a/platform/linux-generic/include/odp/api/plat/packet_io_types.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP Packet IO
- */
-
-#ifndef ODP_PACKET_IO_TYPES_H_
-#define ODP_PACKET_IO_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @addtogroup odp_packet_io
- * Operations on a packet.
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_pktio_t);
-
-/** @internal */
-typedef struct odp_pktin_queue_t {
- odp_pktio_t pktio; /**< @internal pktio handle */
- int index; /**< @internal pktio queue index */
-} odp_pktin_queue_t;
-
-/** @internal */
-typedef struct odp_pktout_queue_t {
- odp_pktio_t pktio; /**< @internal pktio handle */
- int index; /**< @internal pktio queue index */
-} odp_pktout_queue_t;
-
-#define ODP_PKTIO_INVALID _odp_cast_scalar(odp_pktio_t, 0)
-
-#define ODP_PKTIO_MACADDR_MAXSIZE 16
-
-#define ODP_PKTIN_NO_WAIT 0
-#define ODP_PKTIN_WAIT UINT64_MAX
-
-/** Get printable format of odp_pktio_t */
-static inline uint64_t odp_pktio_to_u64(odp_pktio_t hdl)
-{
- return _odp_pri(hdl);
-}
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_types.h b/platform/linux-generic/include/odp/api/plat/packet_types.h
deleted file mode 100644
index 1554c3af3..000000000
--- a/platform/linux-generic/include/odp/api/plat/packet_types.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP packet descriptor
- */
-
-#ifndef ODP_PACKET_TYPES_H_
-#define ODP_PACKET_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stddef.h>
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/packet.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_packet
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_packet_t);
-
-#define ODP_PACKET_INVALID _odp_cast_scalar(odp_packet_t, 0)
-
-#define ODP_PACKET_OFFSET_INVALID (0x0fffffff)
-
-typedef uint8_t odp_packet_seg_t;
-
-static inline uint8_t _odp_packet_seg_to_ndx(odp_packet_seg_t seg)
-{
- return (uint8_t)seg;
-}
-
-static inline odp_packet_seg_t _odp_packet_seg_from_ndx(uint8_t ndx)
-{
- return (odp_packet_seg_t)ndx;
-}
-
-#define ODP_PACKET_SEG_INVALID ((odp_packet_seg_t)-1)
-
-typedef enum {
- ODP_PACKET_GREEN = 0,
- ODP_PACKET_YELLOW = 1,
- ODP_PACKET_RED = 2,
- ODP_PACKET_ALL_COLORS = 3,
-} odp_packet_color_t;
-
-#define ODP_NUM_PACKET_COLORS 3
-
-/**
- * @}
- */
-
-#endif
-
-/** @internal Packet field accessor */
-#define _odp_pkt_get(pkt, cast, field) \
- (*(cast *)(uintptr_t)((uint8_t *)pkt + _odp_packet_inline.field))
-
-/** @internal Packet header field offsets for inline functions */
-typedef struct _odp_packet_inline_offset_t {
- /** @internal field offset */
- size_t data;
- /** @internal field offset */
- size_t seg_len;
- /** @internal field offset */
- size_t frame_len;
- /** @internal field offset */
- size_t headroom;
- /** @internal field offset */
- size_t tailroom;
- /** @internal field offset */
- size_t unshared_len;
- /** @internal field offset */
- size_t ref_hdr;
- /** @internal field offset */
- size_t ref_offset;
- /** *internal field offset */
- size_t ref_len;
- /** @internal field offset */
- size_t pool;
- /** @internal field offset */
- size_t input;
- /** @internal field offset */
- size_t segcount;
- /** @internal field offset */
- size_t user_ptr;
- /** @internal field offset */
- size_t user_area;
- /** @internal field offset */
- size_t user_area_size;
- /** @internal field offset */
- size_t flow_hash;
- /** @internal field offset */
- size_t timestamp;
- /** @internal field offset */
- size_t input_flags;
-
-} _odp_packet_inline_offset_t;
-
-/** @internal Packet input & protocol flags */
-typedef union {
- /** All input flags */
- uint64_t all;
-
- /** Individual input flags */
- struct {
- uint64_t dst_queue:1; /**< Dst queue present */
-
- uint64_t flow_hash:1; /**< Flow hash present */
- uint64_t timestamp:1; /**< Timestamp present */
-
- uint64_t l2:1; /**< known L2 protocol present */
- uint64_t l3:1; /**< known L3 protocol present */
- uint64_t l4:1; /**< known L4 protocol present */
-
- uint64_t eth:1; /**< Ethernet */
- uint64_t eth_bcast:1; /**< Ethernet broadcast */
- uint64_t eth_mcast:1; /**< Ethernet multicast */
- uint64_t jumbo:1; /**< Jumbo frame */
- uint64_t vlan:1; /**< VLAN hdr found */
- uint64_t vlan_qinq:1; /**< Stacked VLAN found, QinQ */
-
- uint64_t snap:1; /**< SNAP */
- uint64_t arp:1; /**< ARP */
-
- uint64_t ipv4:1; /**< IPv4 */
- uint64_t ipv6:1; /**< IPv6 */
- uint64_t ip_bcast:1; /**< IP broadcast */
- uint64_t ip_mcast:1; /**< IP multicast */
- uint64_t ipfrag:1; /**< IP fragment */
- uint64_t ipopt:1; /**< IP optional headers */
-
- uint64_t ipsec:1; /**< IPSec packet. Required by the
- odp_packet_has_ipsec_set() func. */
- uint64_t ipsec_ah:1; /**< IPSec authentication header */
- uint64_t ipsec_esp:1; /**< IPSec encapsulating security
- payload */
- uint64_t udp:1; /**< UDP */
- uint64_t tcp:1; /**< TCP */
- uint64_t tcpopt:1; /**< TCP options present */
- uint64_t sctp:1; /**< SCTP */
- uint64_t icmp:1; /**< ICMP */
-
- uint64_t color:2; /**< Packet color for traffic mgmt */
- uint64_t nodrop:1; /**< Drop eligibility status */
- };
-
-} _odp_packet_input_flags_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/packet_vector_inlines.h b/platform/linux-generic/include/odp/api/plat/packet_vector_inlines.h
new file mode 100644
index 000000000..9aa15ae60
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/packet_vector_inlines.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * Packet vector inline functions
+ */
+
+#ifndef _ODP_PLAT_PACKET_VECTOR_INLINES_H_
+#define _ODP_PLAT_PACKET_VECTOR_INLINES_H_
+
+#include <odp/api/event.h>
+#include <odp/api/packet_types.h>
+#include <odp/api/pool_types.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/event_vector_inline_types.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_packet_vector_from_event __odp_packet_vector_from_event
+ #define odp_packet_vector_to_event __odp_packet_vector_to_event
+ #define odp_packet_vector_tbl __odp_packet_vector_tbl
+ #define odp_packet_vector_pool __odp_packet_vector_pool
+ #define odp_packet_vector_size __odp_packet_vector_size
+ #define odp_packet_vector_size_set __odp_packet_vector_size_set
+ #define odp_packet_vector_user_area __odp_packet_vector_user_area
+ #define odp_packet_vector_user_flag __odp_packet_vector_user_flag
+ #define odp_packet_vector_user_flag_set __odp_packet_vector_user_flag_set
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_packet_vector_t odp_packet_vector_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET_VECTOR);
+
+ return (odp_packet_vector_t)ev;
+}
+
+_ODP_INLINE odp_event_t odp_packet_vector_to_event(odp_packet_vector_t pktv)
+{
+ return (odp_event_t)pktv;
+}
+
+_ODP_INLINE uint32_t odp_packet_vector_tbl(odp_packet_vector_t pktv, odp_packet_t **pkt_tbl)
+{
+ *pkt_tbl = _odp_event_vect_get_ptr(pktv, odp_packet_t, packet);
+
+ return _odp_event_vect_get(pktv, uint32_t, size);
+}
+
+_ODP_INLINE odp_pool_t odp_packet_vector_pool(odp_packet_vector_t pktv)
+{
+ return _odp_event_vect_get(pktv, odp_pool_t, pool);
+}
+
+_ODP_INLINE uint32_t odp_packet_vector_size(odp_packet_vector_t pktv)
+{
+ return _odp_event_vect_get(pktv, uint32_t, size);
+}
+
+_ODP_INLINE void odp_packet_vector_size_set(odp_packet_vector_t pktv, uint32_t size)
+{
+ uint32_t *vector_size = _odp_event_vect_get_ptr(pktv, uint32_t, size);
+
+ *vector_size = size;
+}
+
+_ODP_INLINE void *odp_packet_vector_user_area(odp_packet_vector_t pktv)
+{
+ return _odp_event_vect_get(pktv, void *, uarea_addr);
+}
+
+_ODP_INLINE int odp_packet_vector_user_flag(odp_packet_vector_t pktv)
+{
+ _odp_event_vector_flags_t flags;
+
+ flags.all_flags = _odp_event_vect_get(pktv, uint32_t, flags);
+
+ return flags.user_flag;
+}
+
+_ODP_INLINE void odp_packet_vector_user_flag_set(odp_packet_vector_t pktv, int val)
+{
+ _odp_event_vector_flags_t *flags = _odp_event_vect_get_ptr(pktv, _odp_event_vector_flags_t,
+ flags);
+
+ flags->user_flag = !!val;
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/pool_inline_types.h b/platform/linux-generic/include/odp/api/plat/pool_inline_types.h
new file mode 100644
index 000000000..b1c078c6a
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/pool_inline_types.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP packet descriptor
+ */
+
+#ifndef ODP_POOL_INLINE_TYPES_H_
+#define ODP_POOL_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/** Pool field accessor */
+#define _odp_pool_get(pool, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)pool + _odp_pool_inline.field))
+
+/** Pool header field offsets for inline functions */
+typedef struct _odp_pool_inline_offset_t {
+ uint16_t index;
+ uint16_t seg_len;
+ uint16_t uarea_size;
+ uint16_t trailer_size;
+ uint16_t ext_head_offset;
+ uint16_t ext_pkt_buf_size;
+
+} _odp_pool_inline_offset_t;
+
+extern const _odp_pool_inline_offset_t _odp_pool_inline;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/pool_inlines.h b/platform/linux-generic/include/odp/api/plat/pool_inlines.h
new file mode 100644
index 000000000..9b206b9f7
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/pool_inlines.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_POOL_INLINES_H_
+#define ODP_PLAT_POOL_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/pool_types.h>
+
+#include <odp/api/plat/pool_inline_types.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_pool_index __odp_pool_index
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE int odp_pool_index(odp_pool_t pool)
+{
+ return (int)_odp_pool_get(pool, uint32_t, index);
+}
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/pool_types.h b/platform/linux-generic/include/odp/api/plat/pool_types.h
deleted file mode 100644
index 8bc816d4e..000000000
--- a/platform/linux-generic/include/odp/api/plat/pool_types.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP pool
- */
-
-#ifndef ODP_POOL_TYPES_H_
-#define ODP_POOL_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/pool.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-#include <odp/api/plat/event_types.h>
-
-/** @ingroup odp_pool
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_pool_t);
-
-#define ODP_POOL_INVALID _odp_cast_scalar(odp_pool_t, 0xffffffff)
-
-#define ODP_POOL_NAME_LEN 32
-
-typedef enum odp_pool_type_t {
- ODP_POOL_BUFFER = ODP_EVENT_BUFFER,
- ODP_POOL_PACKET = ODP_EVENT_PACKET,
- ODP_POOL_TIMEOUT = ODP_EVENT_TIMEOUT,
-} odp_pool_type_t;
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/queue_inline_types.h b/platform/linux-generic/include/odp/api/plat/queue_inline_types.h
new file mode 100644
index 000000000..c224ae092
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/queue_inline_types.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_PLAT_QUEUE_INLINE_TYPES_H_
+#define ODP_PLAT_QUEUE_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Queue entry field accessor */
+#define _odp_qentry_field(qentry, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)qentry + \
+ _odp_queue_inline_offset.field))
+
+/* Queue entry field offsets for inline functions */
+typedef struct _odp_queue_inline_offset_t {
+ uint16_t context;
+
+} _odp_queue_inline_offset_t;
+
+/* Queue API functions */
+typedef struct {
+ odp_queue_t (*queue_create)(const char *name,
+ const odp_queue_param_t *param);
+ int (*queue_create_multi)(const char *name[],
+ const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[],
+ int num);
+ int (*queue_destroy)(odp_queue_t queue);
+ int (*queue_destroy_multi)(odp_queue_t queue[], int num);
+ odp_queue_t (*queue_lookup)(const char *name);
+ int (*queue_capability)(odp_queue_capability_t *capa);
+ int (*queue_context_set)(odp_queue_t queue, void *context,
+ uint32_t len);
+ int (*queue_enq)(odp_queue_t queue, odp_event_t ev);
+ int (*queue_enq_multi)(odp_queue_t queue, const odp_event_t events[],
+ int num);
+ odp_event_t (*queue_deq)(odp_queue_t queue);
+ int (*queue_deq_multi)(odp_queue_t queue, odp_event_t events[],
+ int num);
+ odp_queue_type_t (*queue_type)(odp_queue_t queue);
+ odp_schedule_sync_t (*queue_sched_type)(odp_queue_t queue);
+ odp_schedule_prio_t (*queue_sched_prio)(odp_queue_t queue);
+ odp_schedule_group_t (*queue_sched_group)(odp_queue_t queue);
+ uint32_t (*queue_lock_count)(odp_queue_t queue);
+ uint64_t (*queue_to_u64)(odp_queue_t queue);
+ void (*queue_param_init)(odp_queue_param_t *param);
+ int (*queue_info)(odp_queue_t queue, odp_queue_info_t *info);
+ void (*queue_print)(odp_queue_t queue);
+ void (*queue_print_all)(void);
+
+} _odp_queue_api_fn_t;
+
+extern _odp_queue_inline_offset_t _odp_queue_inline_offset;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/queue_inlines.h b/platform/linux-generic/include/odp/api/plat/queue_inlines.h
new file mode 100644
index 000000000..1c8d369c3
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/queue_inlines.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_PLAT_QUEUE_INLINES_H_
+#define ODP_PLAT_QUEUE_INLINES_H_
+
+#include <odp/api/hints.h>
+
+#include <odp/api/plat/event_validation_external.h>
+#include <odp/api/plat/queue_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+extern const _odp_queue_api_fn_t *_odp_queue_api;
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_queue_context __odp_queue_context
+ #define odp_queue_enq __odp_queue_enq
+ #define odp_queue_enq_multi __odp_queue_enq_multi
+ #define odp_queue_deq __odp_queue_deq
+ #define odp_queue_deq_multi __odp_queue_deq_multi
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void *odp_queue_context(odp_queue_t handle)
+{
+ void *context;
+ void *qentry = (void *)handle;
+
+ context = _odp_qentry_field(qentry, void *, context);
+
+ return context;
+}
+
+_ODP_INLINE int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
+{
+ if (odp_unlikely(_odp_event_validate(ev, _ODP_EV_QUEUE_ENQ)))
+ return -1;
+
+ return _odp_queue_api->queue_enq(queue, ev);
+}
+
+_ODP_INLINE int odp_queue_enq_multi(odp_queue_t queue,
+ const odp_event_t events[], int num)
+{
+ if (odp_unlikely(_odp_event_validate_multi(events, num, _ODP_EV_QUEUE_ENQ_MULTI)))
+ return -1;
+
+ return _odp_queue_api->queue_enq_multi(queue, events, num);
+}
+
+_ODP_INLINE odp_event_t odp_queue_deq(odp_queue_t queue)
+{
+ return _odp_queue_api->queue_deq(queue);
+}
+
+_ODP_INLINE int odp_queue_deq_multi(odp_queue_t queue,
+ odp_event_t events[], int num)
+{
+ return _odp_queue_api->queue_deq_multi(queue, events, num);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/queue_types.h b/platform/linux-generic/include/odp/api/plat/queue_types.h
deleted file mode 100644
index 1561e2239..000000000
--- a/platform/linux-generic/include/odp/api/plat/queue_types.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP queue
- */
-
-#ifndef ODP_QUEUE_TYPES_H_
-#define ODP_QUEUE_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/queue.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_queue
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_queue_t);
-
-#define ODP_QUEUE_INVALID _odp_cast_scalar(odp_queue_t, 0)
-
-#define ODP_QUEUE_NAME_LEN 32
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/rwlock_inlines.h b/platform/linux-generic/include/odp/api/plat/rwlock_inlines.h
new file mode 100644
index 000000000..7df3727dd
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/rwlock_inlines.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_RWLOCK_INLINES_H_
+#define ODP_PLAT_RWLOCK_INLINES_H_
+
+#include <odp/api/atomic.h>
+#include <odp/api/cpu.h>
+
+#include <odp/api/abi/rwlock.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_rwlock_init __odp_rwlock_init
+ #define odp_rwlock_read_lock __odp_rwlock_read_lock
+ #define odp_rwlock_read_trylock __odp_rwlock_read_trylock
+ #define odp_rwlock_read_unlock __odp_rwlock_read_unlock
+ #define odp_rwlock_write_lock __odp_rwlock_write_lock
+ #define odp_rwlock_write_trylock __odp_rwlock_write_trylock
+ #define odp_rwlock_write_unlock __odp_rwlock_write_unlock
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void odp_rwlock_init(odp_rwlock_t *rwlock)
+{
+ odp_atomic_init_u32(&rwlock->cnt, 0);
+}
+
+_ODP_INLINE void odp_rwlock_read_lock(odp_rwlock_t *rwlock)
+{
+ uint32_t cnt;
+ int is_locked = 0;
+
+ while (is_locked == 0) {
+ cnt = odp_atomic_load_u32(&rwlock->cnt);
+ /* waiting for read lock */
+ if ((int32_t)cnt < 0) {
+ odp_cpu_pause();
+ continue;
+ }
+ is_locked = odp_atomic_cas_acq_u32(&rwlock->cnt, &cnt, cnt + 1);
+ }
+}
+
+_ODP_INLINE int odp_rwlock_read_trylock(odp_rwlock_t *rwlock)
+{
+ uint32_t cnt = odp_atomic_load_u32(&rwlock->cnt);
+
+ while (cnt != (uint32_t)-1) {
+ if (odp_atomic_cas_acq_u32(&rwlock->cnt, &cnt, cnt + 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+_ODP_INLINE void odp_rwlock_read_unlock(odp_rwlock_t *rwlock)
+{
+ odp_atomic_sub_rel_u32(&rwlock->cnt, 1);
+}
+
+_ODP_INLINE void odp_rwlock_write_lock(odp_rwlock_t *rwlock)
+{
+ uint32_t cnt;
+ int is_locked = 0;
+
+ while (is_locked == 0) {
+ uint32_t zero = 0;
+
+ cnt = odp_atomic_load_u32(&rwlock->cnt);
+ /* lock acquired, wait */
+ if (cnt != 0) {
+ odp_cpu_pause();
+ continue;
+ }
+ is_locked = odp_atomic_cas_acq_u32(&rwlock->cnt, &zero, (uint32_t)-1);
+ }
+}
+
+_ODP_INLINE int odp_rwlock_write_trylock(odp_rwlock_t *rwlock)
+{
+ uint32_t zero = 0;
+
+ return odp_atomic_cas_acq_u32(&rwlock->cnt, &zero, (uint32_t)-1);
+}
+
+_ODP_INLINE void odp_rwlock_write_unlock(odp_rwlock_t *rwlock)
+{
+ odp_atomic_store_rel_u32(&rwlock->cnt, 0);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/rwlock_recursive_inlines.h b/platform/linux-generic/include/odp/api/plat/rwlock_recursive_inlines.h
new file mode 100644
index 000000000..ab9a03e21
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/rwlock_recursive_inlines.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_RWLOCK_RECURSIVE_INLINES_H_
+#define ODP_PLAT_RWLOCK_RECURSIVE_INLINES_H_
+
+#include <odp/api/rwlock.h>
+#include <odp/api/thread.h>
+
+#include <odp/api/abi/rwlock_recursive.h>
+
+#include <odp/api/plat/debug_inlines.h>
+
+#include <stdint.h>
+#include <string.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_rwlock_recursive_init __odp_rwlock_recursive_init
+ #define odp_rwlock_recursive_read_lock __odp_rwlock_recursive_read_lock
+ #define odp_rwlock_recursive_read_trylock __odp_rwlock_recursive_read_trylock
+ #define odp_rwlock_recursive_read_unlock __odp_rwlock_recursive_read_unlock
+ #define odp_rwlock_recursive_write_lock __odp_rwlock_recursive_write_lock
+ #define odp_rwlock_recursive_write_trylock __odp_rwlock_recursive_write_trylock
+ #define odp_rwlock_recursive_write_unlock __odp_rwlock_recursive_write_unlock
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void odp_rwlock_recursive_init(odp_rwlock_recursive_t *rlock)
+{
+ memset(rlock, 0, sizeof(odp_rwlock_recursive_t));
+ odp_rwlock_init(&rlock->lock);
+ rlock->wr_owner = -1;
+}
+
+/* Multiple readers can recurse the lock concurrently */
+_ODP_INLINE void odp_rwlock_recursive_read_lock(odp_rwlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->rd_cnt[thr]) {
+ _ODP_ASSERT(rlock->rd_cnt[thr] < UINT8_MAX);
+ rlock->rd_cnt[thr]++;
+ return;
+ }
+
+ odp_rwlock_read_lock(&rlock->lock);
+ rlock->rd_cnt[thr] = 1;
+}
+
+/* Multiple readers can recurse the lock concurrently */
+_ODP_INLINE int odp_rwlock_recursive_read_trylock(odp_rwlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->rd_cnt[thr]) {
+ _ODP_ASSERT(rlock->rd_cnt[thr] < UINT8_MAX);
+ rlock->rd_cnt[thr]++;
+ return 1;
+ }
+
+ if (odp_rwlock_read_trylock(&rlock->lock)) {
+ rlock->rd_cnt[thr] = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+_ODP_INLINE void odp_rwlock_recursive_read_unlock(odp_rwlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ _ODP_ASSERT(rlock->rd_cnt[thr]);
+ rlock->rd_cnt[thr]--;
+
+ if (rlock->rd_cnt[thr] > 0)
+ return;
+
+ odp_rwlock_read_unlock(&rlock->lock);
+}
+
+/* Only one writer can recurse the lock */
+_ODP_INLINE void odp_rwlock_recursive_write_lock(odp_rwlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->wr_owner == thr) {
+ _ODP_ASSERT(rlock->wr_cnt < UINT32_MAX);
+ rlock->wr_cnt++;
+ return;
+ }
+
+ odp_rwlock_write_lock(&rlock->lock);
+ rlock->wr_owner = thr;
+ rlock->wr_cnt = 1;
+}
+
+/* Only one writer can recurse the lock */
+_ODP_INLINE int odp_rwlock_recursive_write_trylock(odp_rwlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->wr_owner == thr) {
+ _ODP_ASSERT(rlock->wr_cnt < UINT32_MAX);
+ rlock->wr_cnt++;
+ return 1;
+ }
+
+ if (odp_rwlock_write_trylock(&rlock->lock)) {
+ rlock->wr_owner = thr;
+ rlock->wr_cnt = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+_ODP_INLINE void odp_rwlock_recursive_write_unlock(odp_rwlock_recursive_t *rlock)
+{
+ _ODP_ASSERT(rlock->wr_cnt);
+ rlock->wr_cnt--;
+
+ if (rlock->wr_cnt > 0)
+ return;
+
+ rlock->wr_owner = -1;
+ odp_rwlock_write_unlock(&rlock->lock);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/rwlock_recursive_types.h b/platform/linux-generic/include/odp/api/plat/rwlock_recursive_types.h
deleted file mode 100644
index 36f9204ac..000000000
--- a/platform/linux-generic/include/odp/api/plat/rwlock_recursive_types.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP recursive read/write lock
- */
-
-#ifndef ODP_RWLOCK_RECURSIVE_TYPES_H_
-#define ODP_RWLOCK_RECURSIVE_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/rwlock.h>
-#include <odp/api/std_types.h>
-#include <odp/api/thread.h>
-
-/** @internal */
-struct odp_rwlock_recursive_s {
- odp_rwlock_t lock; /**< the lock */
- int wr_owner; /**< write owner thread */
- uint32_t wr_cnt; /**< write recursion count */
- uint8_t rd_cnt[ODP_THREAD_COUNT_MAX]; /**< read recursion count */
-};
-
-typedef struct odp_rwlock_recursive_s odp_rwlock_recursive_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/rwlock_types.h b/platform/linux-generic/include/odp/api/plat/rwlock_types.h
deleted file mode 100644
index f7dc04496..000000000
--- a/platform/linux-generic/include/odp/api/plat/rwlock_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP rwlock
- */
-
-#ifndef ODP_RWLOCK_TYPES_H_
-#define ODP_RWLOCK_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/atomic.h>
-
-/** @internal */
-struct odp_rwlock_s {
- odp_atomic_u32_t cnt; /**< lock count
- 0 lock not taken
- -1 write lock taken
- >0 read lock(s) taken */
-};
-
-typedef struct odp_rwlock_s odp_rwlock_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/schedule_inline_types.h b/platform/linux-generic/include/odp/api/plat/schedule_inline_types.h
new file mode 100644
index 000000000..17bad6488
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/schedule_inline_types.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_SCHEDULE_INLINE_TYPES_H_
+#define ODP_PLAT_SCHEDULE_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
+#include <odp/api/schedule_types.h>
+#include <odp/api/thrmask.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Schedule API functions */
+typedef struct {
+ uint64_t (*schedule_wait_time)(uint64_t ns);
+ int (*schedule_capability)(odp_schedule_capability_t *capa);
+ void (*schedule_config_init)(odp_schedule_config_t *config);
+ int (*schedule_config)(const odp_schedule_config_t *config);
+ odp_event_t (*schedule)(odp_queue_t *from, uint64_t wait);
+ int (*schedule_multi)(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num);
+ int (*schedule_multi_wait)(odp_queue_t *from, odp_event_t events[], int num);
+ int (*schedule_multi_no_wait)(odp_queue_t *from, odp_event_t events[], int num);
+ void (*schedule_pause)(void);
+ void (*schedule_resume)(void);
+ void (*schedule_release_atomic)(void);
+ void (*schedule_release_ordered)(void);
+ void (*schedule_prefetch)(int num);
+ int (*schedule_min_prio)(void);
+ int (*schedule_max_prio)(void);
+ int (*schedule_default_prio)(void);
+ int (*schedule_num_prio)(void);
+ odp_schedule_group_t (*schedule_group_create)(const char *name, const odp_thrmask_t *mask);
+ int (*schedule_group_destroy)(odp_schedule_group_t group);
+ odp_schedule_group_t (*schedule_group_lookup)(const char *name);
+ int (*schedule_group_join)(odp_schedule_group_t group, const odp_thrmask_t *mask);
+ int (*schedule_group_leave)(odp_schedule_group_t group, const odp_thrmask_t *mask);
+ int (*schedule_group_thrmask)(odp_schedule_group_t group, odp_thrmask_t *mask);
+ int (*schedule_group_info)(odp_schedule_group_t group, odp_schedule_group_info_t *info);
+ void (*schedule_order_lock)(uint32_t lock_index);
+ void (*schedule_order_unlock)(uint32_t lock_index);
+ void (*schedule_order_unlock_lock)(uint32_t unlock_index, uint32_t lock_index);
+ void (*schedule_order_lock_start)(uint32_t lock_index);
+ void (*schedule_order_lock_wait)(uint32_t lock_index);
+ void (*schedule_order_wait)(void);
+ void (*schedule_print)(void);
+
+} _odp_schedule_api_fn_t;
+
+/* Scheduler configuration status */
+int _odp_schedule_configured(void);
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/schedule_inlines.h b/platform/linux-generic/include/odp/api/plat/schedule_inlines.h
new file mode 100644
index 000000000..2162bd8e6
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/schedule_inlines.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_SCHEDULE_INLINES_H_
+#define ODP_PLAT_SCHEDULE_INLINES_H_
+
+#include <odp/api/event_types.h>
+#include <odp/api/queue_types.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/schedule_inline_types.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+extern const _odp_schedule_api_fn_t *_odp_sched_api;
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_schedule __odp_schedule
+ #define odp_schedule_multi __odp_schedule_multi
+ #define odp_schedule_multi_wait __odp_schedule_multi_wait
+ #define odp_schedule_multi_no_wait __odp_schedule_multi_no_wait
+ #define odp_schedule_wait_time __odp_schedule_wait_time
+ #define odp_schedule_pause __odp_schedule_pause
+ #define odp_schedule_resume __odp_schedule_resume
+ #define odp_schedule_release_atomic __odp_schedule_release_atomic
+ #define odp_schedule_release_ordered __odp_schedule_release_ordered
+ #define odp_schedule_prefetch __odp_schedule_prefetch
+ #define odp_schedule_order_lock __odp_schedule_order_lock
+ #define odp_schedule_order_unlock __odp_schedule_order_unlock
+ #define odp_schedule_order_unlock_lock __odp_schedule_order_unlock_lock
+ #define odp_schedule_order_lock_start __odp_schedule_order_lock_start
+ #define odp_schedule_order_lock_wait __odp_schedule_order_lock_wait
+ #define odp_schedule_order_wait __odp_schedule_order_wait
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
+{
+ _ODP_ASSERT(_odp_schedule_configured());
+
+ return _odp_sched_api->schedule(from, wait);
+}
+
+_ODP_INLINE int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
+{
+ _ODP_ASSERT(_odp_schedule_configured());
+
+ return _odp_sched_api->schedule_multi(from, wait, events, num);
+}
+
+_ODP_INLINE int odp_schedule_multi_wait(odp_queue_t *from, odp_event_t events[], int num)
+{
+ _ODP_ASSERT(_odp_schedule_configured());
+
+ return _odp_sched_api->schedule_multi_wait(from, events, num);
+}
+
+_ODP_INLINE int odp_schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[], int num)
+{
+ _ODP_ASSERT(_odp_schedule_configured());
+
+ return _odp_sched_api->schedule_multi_no_wait(from, events, num);
+}
+
+_ODP_INLINE uint64_t odp_schedule_wait_time(uint64_t ns)
+{
+ return _odp_sched_api->schedule_wait_time(ns);
+}
+
+_ODP_INLINE void odp_schedule_pause(void)
+{
+ _odp_sched_api->schedule_pause();
+}
+
+_ODP_INLINE void odp_schedule_resume(void)
+{
+ _odp_sched_api->schedule_resume();
+}
+
+_ODP_INLINE void odp_schedule_release_atomic(void)
+{
+ _odp_sched_api->schedule_release_atomic();
+}
+
+_ODP_INLINE void odp_schedule_release_ordered(void)
+{
+ _odp_sched_api->schedule_release_ordered();
+}
+
+_ODP_INLINE void odp_schedule_prefetch(int num)
+{
+ _odp_sched_api->schedule_prefetch(num);
+}
+
+_ODP_INLINE void odp_schedule_order_lock(uint32_t lock_index)
+{
+ _odp_sched_api->schedule_order_lock(lock_index);
+}
+
+_ODP_INLINE void odp_schedule_order_unlock(uint32_t lock_index)
+{
+ _odp_sched_api->schedule_order_unlock(lock_index);
+}
+
+_ODP_INLINE void odp_schedule_order_unlock_lock(uint32_t unlock_index, uint32_t lock_index)
+{
+ _odp_sched_api->schedule_order_unlock_lock(unlock_index, lock_index);
+}
+
+_ODP_INLINE void odp_schedule_order_lock_start(uint32_t lock_index)
+{
+ _odp_sched_api->schedule_order_lock_start(lock_index);
+}
+
+_ODP_INLINE void odp_schedule_order_lock_wait(uint32_t lock_index)
+{
+ _odp_sched_api->schedule_order_lock_wait(lock_index);
+}
+
+_ODP_INLINE void odp_schedule_order_wait(void)
+{
+ _odp_sched_api->schedule_order_wait();
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/schedule_types.h b/platform/linux-generic/include/odp/api/plat/schedule_types.h
deleted file mode 100644
index 535fd6d05..000000000
--- a/platform/linux-generic/include/odp/api/plat/schedule_types.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP schedule
- */
-
-#ifndef ODP_SCHEDULE_TYPES_H_
-#define ODP_SCHEDULE_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_scheduler
- * @{
- */
-
-#define ODP_SCHED_WAIT UINT64_MAX
-#define ODP_SCHED_NO_WAIT 0
-
-typedef int odp_schedule_prio_t;
-
-#define ODP_SCHED_PRIO_HIGHEST 0
-
-#define ODP_SCHED_PRIO_NORMAL 4
-
-#define ODP_SCHED_PRIO_LOWEST 7
-
-#define ODP_SCHED_PRIO_DEFAULT ODP_SCHED_PRIO_NORMAL
-
-typedef int odp_schedule_sync_t;
-
-#define ODP_SCHED_SYNC_PARALLEL 0
-#define ODP_SCHED_SYNC_ATOMIC 1
-#define ODP_SCHED_SYNC_ORDERED 2
-
-typedef int odp_schedule_group_t;
-
-/* These must be kept in sync with thread_globals_t in odp_thread.c */
-#define ODP_SCHED_GROUP_INVALID -1
-#define ODP_SCHED_GROUP_ALL 0
-#define ODP_SCHED_GROUP_WORKER 1
-#define ODP_SCHED_GROUP_CONTROL 2
-
-#define ODP_SCHED_GROUP_NAME_LEN 32
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/shared_memory_types.h b/platform/linux-generic/include/odp/api/plat/shared_memory_types.h
deleted file mode 100644
index 2c5b4ed2e..000000000
--- a/platform/linux-generic/include/odp/api/plat/shared_memory_types.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP shared memory
- */
-
-#ifndef ODP_SHARED_MEMORY_TYPES_H_
-#define ODP_SHARED_MEMORY_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/abi/shared_memory.h>
-#else
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @ingroup odp_shared_memory
- * @{
- */
-
-typedef ODP_HANDLE_T(odp_shm_t);
-
-#define ODP_SHM_INVALID _odp_cast_scalar(odp_shm_t, 0)
-#define ODP_SHM_NULL ODP_SHM_INVALID
-
-#define ODP_SHM_NAME_LEN 32
-
-/**
- * @}
- */
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/spinlock_inlines.h b/platform/linux-generic/include/odp/api/plat/spinlock_inlines.h
new file mode 100644
index 000000000..6a07350de
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/spinlock_inlines.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_SPINLOCK_INLINES_H_
+#define ODP_PLAT_SPINLOCK_INLINES_H_
+
+#include <odp/api/cpu.h>
+
+#include <odp/api/abi/spinlock.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_spinlock_init __odp_spinlock_init
+ #define odp_spinlock_lock __odp_spinlock_lock
+ #define odp_spinlock_trylock __odp_spinlock_trylock
+ #define odp_spinlock_unlock __odp_spinlock_unlock
+ #define odp_spinlock_is_locked __odp_spinlock_is_locked
+
+ #include <odp/api/plat/cpu_inlines.h>
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void odp_spinlock_init(odp_spinlock_t *spinlock)
+{
+ __atomic_clear(&spinlock->lock, __ATOMIC_RELAXED);
+}
+
+_ODP_INLINE void odp_spinlock_lock(odp_spinlock_t *spinlock)
+{
+ /* While the lock is already taken... */
+ while (__atomic_test_and_set(&spinlock->lock, __ATOMIC_ACQUIRE))
+ /* ...spin reading the flag (relaxed MM),
+ * the loop will exit when the lock becomes available
+ * and we will retry the TAS operation above */
+ while (__atomic_load_n(&spinlock->lock, __ATOMIC_RELAXED))
+ odp_cpu_pause();
+}
+
+_ODP_INLINE int odp_spinlock_trylock(odp_spinlock_t *spinlock)
+{
+ return (__atomic_test_and_set(&spinlock->lock, __ATOMIC_ACQUIRE) == 0);
+}
+
+_ODP_INLINE void odp_spinlock_unlock(odp_spinlock_t *spinlock)
+{
+ __atomic_clear(&spinlock->lock, __ATOMIC_RELEASE);
+}
+
+_ODP_INLINE int odp_spinlock_is_locked(odp_spinlock_t *spinlock)
+{
+ return __atomic_load_n(&spinlock->lock, __ATOMIC_RELAXED) != 0;
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/spinlock_recursive_inlines.h b/platform/linux-generic/include/odp/api/plat/spinlock_recursive_inlines.h
new file mode 100644
index 000000000..04d3ee11d
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/spinlock_recursive_inlines.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_SPINLOCK_RECURSIVE_INLINES_H_
+#define ODP_PLAT_SPINLOCK_RECURSIVE_INLINES_H_
+
+#include <odp/api/spinlock.h>
+#include <odp/api/thread.h>
+
+#include <odp/api/abi/spinlock_recursive.h>
+
+#include <odp/api/plat/debug_inlines.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_spinlock_recursive_init __odp_spinlock_recursive_init
+ #define odp_spinlock_recursive_lock __odp_spinlock_recursive_lock
+ #define odp_spinlock_recursive_trylock __odp_spinlock_recursive_trylock
+ #define odp_spinlock_recursive_unlock __odp_spinlock_recursive_unlock
+ #define odp_spinlock_recursive_is_locked __odp_spinlock_recursive_is_locked
+
+ #include <odp/api/plat/spinlock_inlines.h>
+ #include <odp/api/plat/thread_inlines.h>
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void odp_spinlock_recursive_init(odp_spinlock_recursive_t *rlock)
+{
+ odp_spinlock_init(&rlock->lock);
+ rlock->owner = -1;
+ rlock->cnt = 0;
+}
+
+_ODP_INLINE void odp_spinlock_recursive_lock(odp_spinlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->owner == thr) {
+ _ODP_ASSERT(rlock->cnt < UINT32_MAX);
+ rlock->cnt++;
+ return;
+ }
+
+ odp_spinlock_lock(&rlock->lock);
+ rlock->owner = thr;
+ rlock->cnt = 1;
+}
+
+_ODP_INLINE int odp_spinlock_recursive_trylock(odp_spinlock_recursive_t *rlock)
+{
+ int thr = odp_thread_id();
+
+ if (rlock->owner == thr) {
+ _ODP_ASSERT(rlock->cnt < UINT32_MAX);
+ rlock->cnt++;
+ return 1;
+ }
+
+ if (odp_spinlock_trylock(&rlock->lock)) {
+ rlock->owner = thr;
+ rlock->cnt = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+_ODP_INLINE void odp_spinlock_recursive_unlock(odp_spinlock_recursive_t *rlock)
+{
+ _ODP_ASSERT(rlock->cnt);
+ rlock->cnt--;
+
+ if (rlock->cnt > 0)
+ return;
+
+ rlock->owner = -1;
+ odp_spinlock_unlock(&rlock->lock);
+}
+
+_ODP_INLINE int odp_spinlock_recursive_is_locked(odp_spinlock_recursive_t *rlock)
+{
+ return odp_thread_id() == rlock->owner ? 1 : odp_spinlock_is_locked(&rlock->lock);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/spinlock_recursive_types.h b/platform/linux-generic/include/odp/api/plat/spinlock_recursive_types.h
deleted file mode 100644
index c5a1adff9..000000000
--- a/platform/linux-generic/include/odp/api/plat/spinlock_recursive_types.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP recursive spinlock
- */
-
-#ifndef ODP_SPINLOCK_RECURSIVE_TYPES_H_
-#define ODP_SPINLOCK_RECURSIVE_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spinlock.h>
-#include <odp/api/std_types.h>
-
-/** @internal */
-struct odp_spinlock_recursive_s {
- odp_spinlock_t lock; /**< the lock */
- int owner; /**< thread owning the lock */
- uint32_t cnt; /**< recursion count */
-};
-
-typedef struct odp_spinlock_recursive_s odp_spinlock_recursive_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/spinlock_types.h b/platform/linux-generic/include/odp/api/plat/spinlock_types.h
deleted file mode 100644
index f38ece6af..000000000
--- a/platform/linux-generic/include/odp/api/plat/spinlock_types.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP spinlock
- */
-
-#ifndef ODP_SPINLOCK_TYPES_H_
-#define ODP_SPINLOCK_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-
-/** @internal */
-struct odp_spinlock_s {
- char lock; /**< lock flag, should match odp_atomic_flag_t */
-};
-
-typedef struct odp_spinlock_s odp_spinlock_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/static_inline.h.in b/platform/linux-generic/include/odp/api/plat/static_inline.h.in
deleted file mode 100644
index 3cf004347..000000000
--- a/platform/linux-generic/include/odp/api/plat/static_inline.h.in
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Macro for static inline functions
- */
-
-#ifndef ODP_PLAT_STATIC_INLINE_H_
-#define ODP_PLAT_STATIC_INLINE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @internal
- * @def ODP_ABI_COMPAT
- * Control ABI compatibility
- */
-
-/**
- * @internal
- * @def _ODP_INLINE
- * Define a function as inlined or not inlined (for ABI compatibility)
- */
-#if @ODP_ABI_COMPAT@
-#define ODP_ABI_COMPAT 1
-#define _ODP_INLINE
-#else
-#define ODP_ABI_COMPAT 0
-#define _ODP_INLINE static inline
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/std_clib_inlines.h b/platform/linux-generic/include/odp/api/plat/std_clib_inlines.h
deleted file mode 100644
index 8f505d079..000000000
--- a/platform/linux-generic/include/odp/api/plat/std_clib_inlines.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_STD_CLIB_INLINE_H_
-#define ODP_PLAT_STD_CLIB_INLINE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spec/std_types.h>
-#include <string.h>
-
-_ODP_INLINE void *odp_memcpy(void *dst, const void *src, size_t num)
-{
- return memcpy(dst, src, num);
-}
-
-_ODP_INLINE void *odp_memset(void *ptr, int value, size_t num)
-{
- return memset(ptr, value, num);
-}
-
-_ODP_INLINE int odp_memcmp(const void *ptr1, const void *ptr2, size_t num)
-{
- return memcmp(ptr1, ptr2, num);
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/std_inlines.h b/platform/linux-generic/include/odp/api/plat/std_inlines.h
new file mode 100644
index 000000000..0e875258b
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/std_inlines.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef ODP_PLAT_STD_INLINE_H_
+#define ODP_PLAT_STD_INLINE_H_
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <string.h>
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_memcpy __odp_memcpy
+ #define odp_memset __odp_memset
+ #define odp_memcmp __odp_memcmp
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE void *odp_memcpy(void *dst, const void *src, size_t num)
+{
+ return memcpy(dst, src, num);
+}
+
+_ODP_INLINE void *odp_memset(void *ptr, int value, size_t num)
+{
+ return memset(ptr, value, num);
+}
+
+_ODP_INLINE int odp_memcmp(const void *ptr1, const void *ptr2, size_t num)
+{
+ return memcmp(ptr1, ptr2, num);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/strong_types.h b/platform/linux-generic/include/odp/api/plat/strong_types.h
index a53d76352..3866c8080 100644
--- a/platform/linux-generic/include/odp/api/plat/strong_types.h
+++ b/platform/linux-generic/include/odp/api/plat/strong_types.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
@@ -15,9 +13,13 @@
#ifndef STRONG_TYPES_H_
#define STRONG_TYPES_H_
+#include <odp/api/std_types.h>
+
/** Use strong typing for ODP types */
#ifdef __cplusplus
-#define ODP_HANDLE_T(type) struct _##type { uint8_t unused_dummy_var; } *type
+/* Allow type to be expanded before concatenation with underscore */
+#define _ODP_HANDLE_T(type) struct _##type { uint8_t unused_dummy_var; } *type
+#define ODP_HANDLE_T(type) _ODP_HANDLE_T(type)
#else
#define odp_handle_t struct { uint8_t unused_dummy_var; } *
/** C/C++ helper macro for strong typing */
@@ -25,10 +27,10 @@
#endif
/** Internal macro to get value of an ODP handle */
-#define _odp_typeval(handle) ((uint32_t)(uintptr_t)(handle))
+#define _odp_typeval(handle) ((uintptr_t)(handle))
/** Internal macro to get printable value of an ODP handle */
-#define _odp_pri(handle) ((uint64_t)_odp_typeval(handle))
+#define _odp_pri(handle) ((uint64_t)(uintptr_t)(handle))
/** Internal macro to convert a scalar to a typed handle */
#define _odp_cast_scalar(type, val) ((type)(uintptr_t)(val))
diff --git a/platform/linux-generic/include/odp/api/plat/sync_inlines.h b/platform/linux-generic/include/odp/api/plat/sync_inlines.h
index 76eb68107..b3a88b629 100644
--- a/platform/linux-generic/include/odp/api/plat/sync_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/sync_inlines.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
*/
/**
@@ -13,13 +12,26 @@
#ifndef ODP_PLAT_SYNC_INLINE_H_
#define ODP_PLAT_SYNC_INLINE_H_
+#include <odp/api/abi/sync_inlines.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-/** @ingroup odp_barrier
- * @{
- */
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_mb_release __odp_mb_release
+ #define odp_mb_acquire __odp_mb_acquire
+ #define odp_mb_full __odp_mb_full
+ #define odp_mb_sync __odp_mb_sync
+ #define odp_mb_sync_load __odp_mb_sync_load
+ #define odp_mb_sync_store __odp_mb_sync_store
+#else
+ #define _ODP_INLINE
+#endif
_ODP_INLINE void odp_mb_release(void)
{
@@ -36,9 +48,22 @@ _ODP_INLINE void odp_mb_full(void)
__atomic_thread_fence(__ATOMIC_SEQ_CST);
}
-/**
- * @}
- */
+_ODP_INLINE void odp_mb_sync(void)
+{
+ _odp_mb_sync();
+}
+
+_ODP_INLINE void odp_mb_sync_load(void)
+{
+ _odp_mb_sync_load();
+}
+
+_ODP_INLINE void odp_mb_sync_store(void)
+{
+ _odp_mb_sync_store();
+}
+
+/** @endcond */
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp/api/plat/thread_inline_types.h b/platform/linux-generic/include/odp/api/plat/thread_inline_types.h
new file mode 100644
index 000000000..e594a37ff
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/thread_inline_types.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_THREAD_INLINE_TYPES_H_
+#define ODP_PLAT_THREAD_INLINE_TYPES_H_
+
+#include <odp/api/init.h>
+#include <odp/api/thread_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+typedef struct {
+ odp_log_func_t log_fn;
+ odp_thread_type_t type;
+ int thr;
+ int cpu;
+
+} _odp_thread_state_t;
+
+extern __thread _odp_thread_state_t *_odp_this_thread;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/thread_inlines.h b/platform/linux-generic/include/odp/api/plat/thread_inlines.h
new file mode 100644
index 000000000..be8ee9725
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/thread_inlines.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_PLAT_THREAD_INLINES_H_
+#define ODP_PLAT_THREAD_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/thread_types.h>
+
+#include <odp/api/plat/thread_inline_types.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_thread_id __odp_thread_id
+ #define odp_thread_type __odp_thread_type
+ #define odp_cpu_id __odp_cpu_id
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE int odp_thread_id(void)
+{
+ return _odp_this_thread->thr;
+}
+
+_ODP_INLINE odp_thread_type_t odp_thread_type(void)
+{
+ return _odp_this_thread->type;
+}
+
+_ODP_INLINE int odp_cpu_id(void)
+{
+ return _odp_this_thread->cpu;
+}
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/thread_types.h b/platform/linux-generic/include/odp/api/plat/thread_types.h
deleted file mode 100644
index 33af45983..000000000
--- a/platform/linux-generic/include/odp/api/plat/thread_types.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP thread
- */
-
-#ifndef ODP_THREAD_TYPES_H_
-#define ODP_THREAD_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_thread
- * @{
- */
-
-#define ODP_THREAD_COUNT_MAX 128
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/thrmask_types.h b/platform/linux-generic/include/odp/api/plat/thrmask_types.h
deleted file mode 100644
index 5d93890c1..000000000
--- a/platform/linux-generic/include/odp/api/plat/thrmask_types.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP thread masks
- */
-
-#ifndef ODP_THRMASK_TYPES_H_
-#define ODP_THRMASK_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_thread
- * @{
- */
-
-#include <odp/api/cpumask.h>
-
-/**
- * Minimum size of output buffer for odp_thrmask_to_str()
- */
-#define ODP_THRMASK_STR_SIZE ODP_CPUMASK_STR_SIZE
-
-/**
- * Thread mask
- *
- * Don't access directly, use access functions.
- */
-typedef struct odp_thrmask_t {
- odp_cpumask_t m; /**< @private Mask*/
-} odp_thrmask_t;
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/ticketlock_inlines.h b/platform/linux-generic/include/odp/api/plat/ticketlock_inlines.h
index ecbea7c4d..24eddf195 100644
--- a/platform/linux-generic/include/odp/api/plat/ticketlock_inlines.h
+++ b/platform/linux-generic/include/odp/api/plat/ticketlock_inlines.h
@@ -1,30 +1,40 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Ticketlock inline functions
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#ifndef _ODP_PLAT_TICKETLOCK_INLINES_H_
#define _ODP_PLAT_TICKETLOCK_INLINES_H_
#include <odp/api/atomic.h>
-#include <odp/api/sync.h>
#include <odp/api/cpu.h>
-#include <odp/api/plat/ticketlock_types.h>
+#include <odp/api/abi/ticketlock.h>
+#include <odp/api/abi/wait_until.h>
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_ticketlock_init __odp_ticketlock_init
+ #define odp_ticketlock_lock __odp_ticketlock_lock
+ #define odp_ticketlock_trylock __odp_ticketlock_trylock
+ #define odp_ticketlock_unlock __odp_ticketlock_unlock
+ #define odp_ticketlock_is_locked __odp_ticketlock_is_locked
+ /* Inline atomic functions */
+ #include <odp/api/plat/atomic_inlines.h>
+ #include <odp/api/plat/cpu_inlines.h>
+#else
+ #undef _ODP_INLINE
+ #define _ODP_INLINE
+#endif
-/** @internal
- * Acquire ticket lock.
- *
- * @param ticketlock Pointer to a ticket lock
- */
-static inline void _odp_ticketlock_lock(odp_ticketlock_t *ticketlock)
+_ODP_INLINE void odp_ticketlock_init(odp_ticketlock_t *ticketlock)
+{
+ odp_atomic_init_u32(&ticketlock->next_ticket, 0);
+ odp_atomic_init_u32(&ticketlock->cur_ticket, 0);
+}
+
+_ODP_INLINE void odp_ticketlock_lock(odp_ticketlock_t *ticketlock)
{
uint32_t ticket;
@@ -35,19 +45,10 @@ static inline void _odp_ticketlock_lock(odp_ticketlock_t *ticketlock)
/* Spin waiting for our turn. Use load-acquire so that we acquire
* all stores from the previous lock owner */
- while (ticket != odp_atomic_load_acq_u32(&ticketlock->cur_ticket))
- odp_cpu_pause();
+ _odp_wait_until_equal_acq_u32(&ticketlock->cur_ticket, ticket);
}
-/** @internal
- * Try to acquire ticket lock.
- *
- * @param tklock Pointer to a ticket lock
- *
- * @retval 1 lock acquired
- * @retval 0 lock not acquired
- */
-static inline int _odp_ticketlock_trylock(odp_ticketlock_t *tklock)
+_ODP_INLINE int odp_ticketlock_trylock(odp_ticketlock_t *tklock)
{
/* We read 'next_ticket' and 'cur_ticket' non-atomically which should
* not be a problem as they are not independent of each other.
@@ -75,12 +76,7 @@ static inline int _odp_ticketlock_trylock(odp_ticketlock_t *tklock)
return 0;
}
-/** @internal
- * Release ticket lock
- *
- * @param ticketlock Pointer to a ticket lock
- */
-static inline void _odp_ticketlock_unlock(odp_ticketlock_t *ticketlock)
+_ODP_INLINE void odp_ticketlock_unlock(odp_ticketlock_t *ticketlock)
{
/* Release the lock by incrementing 'cur_ticket'. As we are the
* lock owner and thus the only thread that is allowed to write
@@ -92,15 +88,7 @@ static inline void _odp_ticketlock_unlock(odp_ticketlock_t *ticketlock)
odp_atomic_store_rel_u32(&ticketlock->cur_ticket, cur + 1);
}
-/** @internal
- * Check if ticket lock is locked
- *
- * @param ticketlock Pointer to a ticket lock
- *
- * @retval 1 the lock is busy (locked)
- * @retval 0 the lock is available (unlocked)
- */
-static inline int _odp_ticketlock_is_locked(odp_ticketlock_t *ticketlock)
+_ODP_INLINE int odp_ticketlock_is_locked(odp_ticketlock_t *ticketlock)
{
/* Compare 'cur_ticket' with 'next_ticket'. Ideally we should read
* both variables atomically but the information can become stale
@@ -111,20 +99,6 @@ static inline int _odp_ticketlock_is_locked(odp_ticketlock_t *ticketlock)
odp_atomic_load_u32(&ticketlock->next_ticket);
}
-/* Include inlined versions of API functions */
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-
-/** @ingroup odp_locks
- * @{
- */
-
-#include <odp/api/plat/ticketlock_inlines_api.h>
-
-/**
- * @}
- */
-
-#endif
+/** @endcond */
#endif
diff --git a/platform/linux-generic/include/odp/api/plat/ticketlock_inlines_api.h b/platform/linux-generic/include/odp/api/plat/ticketlock_inlines_api.h
deleted file mode 100644
index 5efe696ff..000000000
--- a/platform/linux-generic/include/odp/api/plat/ticketlock_inlines_api.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Ticketlock inline functions
- */
-
-#ifndef _ODP_PLAT_TICKETLOCK_INLINES_API_H_
-#define _ODP_PLAT_TICKETLOCK_INLINES_API_H_
-
-_ODP_INLINE void odp_ticketlock_lock(odp_ticketlock_t *lock)
-{
- return _odp_ticketlock_lock(lock);
-}
-
-_ODP_INLINE int odp_ticketlock_trylock(odp_ticketlock_t *lock)
-{
- return _odp_ticketlock_trylock(lock);
-}
-
-_ODP_INLINE void odp_ticketlock_unlock(odp_ticketlock_t *lock)
-{
- _odp_ticketlock_unlock(lock);
-}
-
-_ODP_INLINE int odp_ticketlock_is_locked(odp_ticketlock_t *lock)
-{
- return _odp_ticketlock_is_locked(lock);
-}
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/ticketlock_types.h b/platform/linux-generic/include/odp/api/plat/ticketlock_types.h
deleted file mode 100644
index 81d479d61..000000000
--- a/platform/linux-generic/include/odp/api/plat/ticketlock_types.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP ticketlock
- */
-
-#ifndef ODP_TICKETLOCK_TYPES_H_
-#define ODP_TICKETLOCK_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/atomic.h>
-
-/** @internal */
-struct odp_ticketlock_s {
- odp_atomic_u32_t next_ticket; /**< Next ticket */
- odp_atomic_u32_t cur_ticket; /**< Current ticket */
-};
-
-typedef struct odp_ticketlock_s odp_ticketlock_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/time_inlines.h b/platform/linux-generic/include/odp/api/plat/time_inlines.h
new file mode 100644
index 000000000..d7295c9d0
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/time_inlines.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#ifndef ODP_PLAT_TIME_INLINES_H_
+#define ODP_PLAT_TIME_INLINES_H_
+
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/time_types.h>
+
+#include <odp/api/abi/time_inlines.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_time_local __odp_time_local
+ #define odp_time_global __odp_time_global
+ #define odp_time_to_ns __odp_time_to_ns
+ #define odp_time_local_ns __odp_time_local_ns
+ #define odp_time_global_ns __odp_time_global_ns
+
+ #define odp_time_local_strict __odp_time_local_strict
+ #define odp_time_global_strict __odp_time_global_strict
+ #define odp_time_local_strict_ns __odp_time_local_strict_ns
+ #define odp_time_global_strict_ns __odp_time_global_strict_ns
+
+ #define odp_time_cmp __odp_time_cmp
+ #define odp_time_diff __odp_time_diff
+ #define odp_time_diff_ns __odp_time_diff_ns
+ #define odp_time_add_ns __odp_time_add_ns
+ #define odp_time_sum __odp_time_sum
+
+ #define odp_time_local_from_ns __odp_time_local_from_ns
+ #define odp_time_global_from_ns __odp_time_global_from_ns
+
+ #define odp_time_local_res __odp_time_local_res
+ #define odp_time_global_res __odp_time_global_res
+
+ #define odp_time_wait_ns __odp_time_wait_ns
+ #define odp_time_wait_until __odp_time_wait_until
+ #define odp_time_startup __odp_time_startup
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_time_t odp_time_local(void)
+{
+ return _odp_time_cur();
+}
+
+_ODP_INLINE odp_time_t odp_time_global(void)
+{
+ return _odp_time_cur();
+}
+
+_ODP_INLINE odp_time_t odp_time_local_strict(void)
+{
+ return _odp_time_cur_strict();
+}
+
+_ODP_INLINE odp_time_t odp_time_global_strict(void)
+{
+ return _odp_time_cur_strict();
+}
+
+_ODP_INLINE uint64_t odp_time_local_ns(void)
+{
+ return _odp_time_to_ns(_odp_time_cur());
+}
+
+_ODP_INLINE uint64_t odp_time_global_ns(void)
+{
+ return _odp_time_to_ns(_odp_time_cur());
+}
+
+_ODP_INLINE uint64_t odp_time_local_strict_ns(void)
+{
+ return _odp_time_to_ns(_odp_time_cur_strict());
+}
+
+_ODP_INLINE uint64_t odp_time_global_strict_ns(void)
+{
+ return _odp_time_to_ns(_odp_time_cur_strict());
+}
+
+_ODP_INLINE uint64_t odp_time_to_ns(odp_time_t time)
+{
+ return _odp_time_to_ns(time);
+}
+
+_ODP_INLINE int odp_time_cmp(odp_time_t t2, odp_time_t t1)
+{
+ if (odp_likely(t2.u64 > t1.u64))
+ return 1;
+
+ if (t2.u64 < t1.u64)
+ return -1;
+
+ return 0;
+}
+
+_ODP_INLINE odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
+{
+ odp_time_t time;
+
+ time.u64 = t2.u64 - t1.u64;
+
+ return time;
+}
+
+_ODP_INLINE uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
+{
+ odp_time_t time;
+
+ time.u64 = t2.u64 - t1.u64;
+
+ return odp_time_to_ns(time);
+}
+
+_ODP_INLINE odp_time_t odp_time_add_ns(odp_time_t time, uint64_t ns)
+{
+ odp_time_t t = _odp_time_from_ns(ns);
+
+ t.u64 += time.u64;
+
+ return t;
+}
+
+_ODP_INLINE odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2)
+{
+ odp_time_t time;
+
+ time.u64 = t1.u64 + t2.u64;
+
+ return time;
+}
+
+_ODP_INLINE odp_time_t odp_time_local_from_ns(uint64_t ns)
+{
+ return _odp_time_from_ns(ns);
+}
+
+_ODP_INLINE odp_time_t odp_time_global_from_ns(uint64_t ns)
+{
+ return _odp_time_from_ns(ns);
+}
+
+_ODP_INLINE uint64_t odp_time_local_res(void)
+{
+ return _odp_time_res();
+}
+
+_ODP_INLINE uint64_t odp_time_global_res(void)
+{
+ return _odp_time_res();
+}
+
+_ODP_INLINE void odp_time_wait_until(odp_time_t time)
+{
+ odp_time_t cur;
+
+ do {
+ cur = _odp_time_cur();
+ } while (odp_time_cmp(time, cur) > 0);
+}
+
+_ODP_INLINE void odp_time_wait_ns(uint64_t ns)
+{
+ odp_time_t cur = _odp_time_cur();
+ odp_time_t wait = _odp_time_from_ns(ns);
+ odp_time_t end_time = odp_time_sum(cur, wait);
+
+ odp_time_wait_until(end_time);
+}
+
+_ODP_INLINE void odp_time_startup(odp_time_startup_t *startup)
+{
+ _odp_time_startup(startup);
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/time_types.h b/platform/linux-generic/include/odp/api/plat/time_types.h
deleted file mode 100644
index e7111c8c5..000000000
--- a/platform/linux-generic/include/odp/api/plat/time_types.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP time service
- */
-
-#ifndef ODP_TIME_TYPES_H_
-#define ODP_TIME_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odp_time
- * @{
- **/
-
-/**
- * @internal Time structure used for both POSIX timespec and HW counter
- * implementations.
- */
-typedef struct odp_time_t {
- /** @internal Variant mappings for time type */
- union {
- /** @internal Used with generic 64 bit operations */
- uint64_t u64;
-
- /** @internal Nanoseconds */
- uint64_t nsec;
-
- /** @internal HW timer counter value */
- uint64_t count;
-
- };
-} odp_time_t;
-
-#define ODP_TIME_NULL ((odp_time_t){.u64 = 0})
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/timer_inline_types.h b/platform/linux-generic/include/odp/api/plat/timer_inline_types.h
new file mode 100644
index 000000000..a89660bc2
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/timer_inline_types.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_PLAT_TIMER_INLINE_TYPES_H_
+#define ODP_PLAT_TIMER_INLINE_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/* Timeout header field accessor */
+#define _odp_timeout_hdr_field(hdr, cast, field) \
+ (*(cast *)(uintptr_t)((uint8_t *)hdr + \
+ _odp_timeout_inline_offset.field))
+
+/* Timeout header field offsets for inline functions */
+typedef struct _odp_timeout_inline_offset_t {
+ uint16_t expiration;
+ uint16_t timer;
+ uint16_t user_ptr;
+ uint16_t uarea_addr;
+
+} _odp_timeout_inline_offset_t;
+
+extern const _odp_timeout_inline_offset_t _odp_timeout_inline_offset;
+
+/** @endcond */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/timer_inlines.h b/platform/linux-generic/include/odp/api/plat/timer_inlines.h
new file mode 100644
index 000000000..c5ba380e5
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/timer_inlines.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#ifndef ODP_PLAT_TIMER_INLINES_H_
+#define ODP_PLAT_TIMER_INLINES_H_
+
+#include <odp/api/event.h>
+#include <odp/api/timer_types.h>
+
+#include <odp/api/abi/time_inlines.h>
+
+#include <odp/api/plat/debug_inlines.h>
+#include <odp/api/plat/timer_inline_types.h>
+
+#include <stdint.h>
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef _ODP_NO_INLINE
+ /* Inline functions by default */
+ #define _ODP_INLINE static inline
+ #define odp_timeout_timer __odp_timeout_timer
+ #define odp_timeout_tick __odp_timeout_tick
+ #define odp_timeout_user_ptr __odp_timeout_user_ptr
+ #define odp_timeout_user_area __odp_timeout_user_area
+ #define odp_timer_current_tick __odp_timer_current_tick
+ #define odp_timer_tick_to_ns __odp_timer_tick_to_ns
+ #define odp_timer_ns_to_tick __odp_timer_ns_to_tick
+ #define odp_timeout_from_event __odp_timeout_from_event
+ #define odp_timeout_from_event_multi __odp_timeout_from_event_multi
+ #define odp_timeout_to_event __odp_timeout_to_event
+#else
+ #define _ODP_INLINE
+#endif
+
+_ODP_INLINE odp_timer_t odp_timeout_timer(odp_timeout_t tmo)
+{
+ return _odp_timeout_hdr_field(tmo, odp_timer_t, timer);
+}
+
+_ODP_INLINE uint64_t odp_timeout_tick(odp_timeout_t tmo)
+{
+ return _odp_timeout_hdr_field(tmo, uint64_t, expiration);
+}
+
+_ODP_INLINE void *odp_timeout_user_ptr(odp_timeout_t tmo)
+{
+ return _odp_timeout_hdr_field(tmo, void *, user_ptr);
+}
+
+_ODP_INLINE void *odp_timeout_user_area(odp_timeout_t tmo)
+{
+ return _odp_timeout_hdr_field(tmo, void *, uarea_addr);
+}
+
+_ODP_INLINE uint64_t odp_timer_current_tick(odp_timer_pool_t tpid)
+{
+ (void)tpid;
+
+ /* This is equal to odp_time_global_ns(). Cannot call inlined API function from here. */
+ return _odp_time_to_ns(_odp_time_cur());
+}
+
+_ODP_INLINE uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tp, uint64_t ticks)
+{
+ (void)tp;
+
+ /* Timer ticks in API are nsec */
+ return ticks;
+}
+
+_ODP_INLINE uint64_t odp_timer_ns_to_tick(odp_timer_pool_t tp, uint64_t ns)
+{
+ (void)tp;
+
+ /* Timer ticks in API are nsec */
+ return ns;
+}
+
+_ODP_INLINE odp_timeout_t odp_timeout_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_TIMEOUT);
+
+ return (odp_timeout_t)ev;
+}
+
+_ODP_INLINE void odp_timeout_from_event_multi(odp_timeout_t tmo[], const odp_event_t ev[], int num)
+{
+ for (int i = 0; i < num; i++) {
+ _ODP_ASSERT(odp_event_type(ev[i]) == ODP_EVENT_TIMEOUT);
+
+ tmo[i] = odp_timeout_from_event(ev[i]);
+ }
+}
+
+_ODP_INLINE odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
+{
+ return (odp_event_t)tmo;
+}
+
+/** @endcond */
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/timer_types.h b/platform/linux-generic/include/odp/api/plat/timer_types.h
deleted file mode 100644
index a8891f11f..000000000
--- a/platform/linux-generic/include/odp/api/plat/timer_types.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP timer service
- */
-
-#ifndef ODP_TIMER_TYPES_H_
-#define ODP_TIMER_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/strong_types.h>
-
-/** @addtogroup odp_timer
- * @{
- **/
-
-struct odp_timer_pool_s; /**< Forward declaration */
-
-typedef struct odp_timer_pool_s *odp_timer_pool_t;
-
-#define ODP_TIMER_POOL_INVALID NULL
-
-#define ODP_TIMER_POOL_NAME_LEN 32
-
-typedef ODP_HANDLE_T(odp_timer_t);
-
-#define ODP_TIMER_INVALID _odp_cast_scalar(odp_timer_t, 0xffffffff)
-
-typedef ODP_HANDLE_T(odp_timeout_t);
-
-#define ODP_TIMEOUT_INVALID _odp_cast_scalar(odp_timeout_t, NULL)
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/traffic_mngr_types.h b/platform/linux-generic/include/odp/api/plat/traffic_mngr_types.h
deleted file mode 100644
index f47a13f6f..000000000
--- a/platform/linux-generic/include/odp/api/plat/traffic_mngr_types.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP traffic mngr
- */
-
-#ifndef ODP_TRAFFIC_MNGR_TYPES_H_
-#define ODP_TRAFFIC_MNGR_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/strong_types.h>
-
-/** @addtogroup odp_traffic_mngr
- * Macros and operations on a TM system.
- * @{
- */
-
-/** The ODP_TM_MAX_NUM_SYSTEMS constant specifies the maximum number of TM
- * systems that may be created. On some platforms this might be much more
- * limited to as little as one hardware TM system.
- */
-#define ODP_TM_MAX_NUM_SYSTEMS 64
-
-/** The ODP_TM_MAX_PRIORITIES constant specifies the largest range of
- * priorities that any TM system can support. All strict priority values MUST
- * in the range 0..ODP_TM_MAX_PRIORITIES-1.
- */
-#define ODP_TM_MAX_PRIORITIES 16
-
-/** The ODP_TM MAX_LEVELS constant specifies the largest range of
- * tm_node levels that any TM system can support. Hence all tm_node level
- * values MUST be in the range 0..ODP_TM_MAX_LEVELS-1. Smaller tm_node
- * levels are associated with tm_nodes closer to the TM system egress.
- */
-#define ODP_TM_MAX_LEVELS 8
-
-/**
- * The smallest SCHED weight is 1 (i.e. 0 is not a legal WFQ/WRR value).
- */
-#define ODP_TM_MIN_SCHED_WEIGHT 1
-
-/** The ODP_TM_MAX_SCHED_WEIGHT constant is the largest weight any TM system
- * can support (at least from a configuration standpoint). A given TM system
- * could have a smaller value.
- */
-#define ODP_TM_MAX_SCHED_WEIGHT 255
-
-/** The ODP_TM_MAX_TM_QUEUES constant is the largest number of tm_queues
- * that can handled by any one TM system.
- */
-#define ODP_TM_MAX_TM_QUEUES (16 * 1024 * 1024)
-
-/** The ODP_TM_MAX_NUM_OUTPUTS constant is the largest number of outputs that
- * can be configured for any one TM system.
- */
-#define ODP_TM_MAX_NUM_OUTPUTS 256
-
-/** The ODP_TM_MAX_NUM_TM_NODES constant is the largest number of tm_nodes that
- * can be in existence for any one TM system.
- */
-#define ODP_TM_MAX_NUM_TM_NODES (1024 * 1024)
-
-/** The ODP_TM_MAX_TM_NODE_FANIN constant is the largest number of fan-in
- * "inputs" that can be simultaneously connected to a single tm_node.
- * *TBD* Does this need to be as large as ODP_TM_MAX_TM_QUEUES? *TBD*
- */
-#define ODP_TM_MAX_TM_NODE_FANIN (1024 * 1024)
-
-/** The ODP_TM_MIN_SHAPER_BW constant is the smallest amount of bandwidth that
- * can a shaper's peak or commit rate can be set to. It is in units of
- * 1000 bytes/second so that it and the ODP_TM_MAX_SHAPER_BW can both fit in
- * 32 bits.
- */
-#define ODP_TM_MIN_SHAPER_BW 1
-
-/** The ODP_TM_MAX_SHAPER_BW constant is the largest amound of bandwidth that
- * any shaper's peak or commit rate can be set to. It is in units of
- * 1000 bytes/second so that it and the ODP_TM_MIN_SHAPER_BW can both fit in
- * 32 bits.
- */
-#define ODP_TM_MAX_SHAPER_BW 12500000
-
-/** The ODP_NUM_SHAPER_COLORS constant just counts the number of enumeration
- * values defined in the odp_tm_shaper_color_t type.
- */
-#define ODP_NUM_SHAPER_COLORS 3
-
-/** The INVALID_PRIORITY constant is used when one needs to indicate an
- * invalid priority value.
- */
-#define ODP_TM_INVALID_PRIORITY 255
-
-/** The odp_tm_percent_t type is used when specifying fields that are
- * percentages. It is a fixed point integer whose units are 1/100 of a
- * percent. Hence 100% is represented as the integer value 10000. Note
- * that because it is often used as a ratio of the current queue value and
- * maximum queue threshold, it can be > 100%, but in any event will never
- * be larger than 500% (i.e. it MUST be capped at 50000).
- */
-typedef uint16_t odp_tm_percent_t;
-
-/** The odp_tm_handle_t type is a generic type that can stand for any of the
- * other ODP_TM handle types.
- */
-typedef uint64_t odp_tm_handle_t;
-
-/** Each odp_tm_t value represents a specific TM system. Almost all
- * functions in this API require a odp_tm_t value - either directly
- * as a function parameter or indirectly by having another ODP TM handle value
- * as a function parameter.
- */
-typedef odp_tm_handle_t odp_tm_t;
-
-/** Each odp_tm_queue_t value is an opaque ODP handle representing a specific
- * tm_queue within a specific TM system.
- */
-typedef odp_tm_handle_t odp_tm_queue_t;
-
-/** Each odp_tm_node_t value is an opaque ODP handle representing a specific
- * tm_node within a specific TM system.
- */
-typedef odp_tm_handle_t odp_tm_node_t;
-
-/** Each odp_tm_shaper_t value is an opaque ODP handle representing a specific
- * shaper profile usable across all TM systems described by this API. A given
- * shaper profile can then be attached to any tm_queue or tm_node.
- */
-typedef odp_tm_handle_t odp_tm_shaper_t;
-
-/** Each odp_tm_sched_t value is an opaque ODP handle representing a specific
- * tm_node scheduler profile usable across all TM systems described by this
- * API. A given tm_node scheduler profile can then be attached to any tm_node.
- */
-typedef odp_tm_handle_t odp_tm_sched_t;
-
-/** Each odp_tm_threshold_t value is an opaque ODP handle representing a
- * specific queue threshold profile usable across all TM systems described by
- * this API. A given queue threshold profile can then be attached to any
- * tm_queue or tm_node.
- */
-typedef odp_tm_handle_t odp_tm_threshold_t;
-
-/** Each odp_tm_wred_t value is an opaque ODP handle representing a specific
- * WRED profile usable across all TM systems described by this API. A given
- * WRED profile can then be attached to any tm_queue or tm_node.
- */
-typedef odp_tm_handle_t odp_tm_wred_t;
-
-/** The ODP_TM_INVALID constant can be used with any ODP TM handle type and
- * indicates that this value does NOT represent a valid TM object.
- */
-#define ODP_TM_INVALID 0
-
-/**
- * @def ODP_TM_ROOT
- * Constant that is used to refer to the egress/root node of the TM subsystem's
- * tree/hierarchy of nodes.
- */
-#define ODP_TM_ROOT ((odp_tm_handle_t)-1)
-
-/** @internal Get printable format of odp_tm_handle_t @param hdl @return */
-static inline uint64_t odp_tm_handle_to_u64(odp_tm_handle_t hdl)
-{
- return hdl;
-}
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/plat/version_types.h b/platform/linux-generic/include/odp/api/plat/version_types.h
deleted file mode 100644
index e3327eb33..000000000
--- a/platform/linux-generic/include/odp/api/plat/version_types.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_VERSION_TYPESH_
-#define ODP_VERSION_TYPESH_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @internal Version string expand */
-#define ODP_VERSION_STR_EXPAND(x) #x
-
-/** @internal Version to string */
-#define ODP_VERSION_TO_STR(x) ODP_VERSION_STR_EXPAND(x)
-
-/** @internal API version string */
-#define ODP_VERSION_API_STR \
-ODP_VERSION_TO_STR(ODP_VERSION_API_GENERATION) "." \
-ODP_VERSION_TO_STR(ODP_VERSION_API_MAJOR) "." \
-ODP_VERSION_TO_STR(ODP_VERSION_API_MINOR)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/pool.h b/platform/linux-generic/include/odp/api/pool.h
deleted file mode 100644
index d712b6501..000000000
--- a/platform/linux-generic/include/odp/api/pool.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP pool
- */
-
-#ifndef ODP_PLAT_POOL_H_
-#define ODP_PLAT_POOL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_buffer
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/plat/pool_types.h>
-#include <odp/api/plat/shared_memory_types.h>
-#include <odp/api/plat/event_types.h>
-#include <odp/api/spec/pool.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/queue.h b/platform/linux-generic/include/odp/api/queue.h
deleted file mode 100644
index adceafbd1..000000000
--- a/platform/linux-generic/include/odp/api/queue.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP queue
- */
-
-#ifndef ODP_PLAT_QUEUE_H_
-#define ODP_PLAT_QUEUE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/event_types.h>
-#include <odp/api/plat/queue_types.h>
-#include <odp/api/plat/buffer_types.h>
-#include <odp/api/plat/pool_types.h>
-
-/** @ingroup odp_queue
- * @{
- */
-
-/* REMOVE FROM API SPEC. Typedef needed only for suppressing Doxygen
- * warning. */
-typedef void odp_queue_group_t;
-
-/**
- * @}
- */
-
-#include <odp/api/spec/queue.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/random.h b/platform/linux-generic/include/odp/api/random.h
deleted file mode 100644
index c8529b3f9..000000000
--- a/platform/linux-generic/include/odp/api/random.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP random number API
- */
-
-#ifndef ODP_PLAT_RANDOM_H_
-#define ODP_PLAT_RANDOM_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_random ODP RANDOM
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/random.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/rwlock.h b/platform/linux-generic/include/odp/api/rwlock.h
deleted file mode 100644
index 4a86173f5..000000000
--- a/platform/linux-generic/include/odp/api/rwlock.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP RW Locks
- */
-
-#ifndef ODP_PLAT_RWLOCK_H_
-#define ODP_PLAT_RWLOCK_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/rwlock_types.h>
-
-#include <odp/api/spec/rwlock.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ODP_RWLOCK_H_ */
diff --git a/platform/linux-generic/include/odp/api/rwlock_recursive.h b/platform/linux-generic/include/odp/api/rwlock_recursive.h
deleted file mode 100644
index 4a081532d..000000000
--- a/platform/linux-generic/include/odp/api/rwlock_recursive.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP resursive read/write lock
- */
-
-#ifndef ODP_PLAT_RWLOCK_RECURSIVE_H_
-#define ODP_PLAT_RWLOCK_RECURSIVE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/rwlock_recursive_types.h>
-
-#include <odp/api/spec/rwlock_recursive.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/schedule.h b/platform/linux-generic/include/odp/api/schedule.h
deleted file mode 100644
index 002648517..000000000
--- a/platform/linux-generic/include/odp/api/schedule.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP schedule
- */
-
-#ifndef ODP_PLAT_SCHEDULE_H_
-#define ODP_PLAT_SCHEDULE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/schedule_types.h>
-
-/** @ingroup odp_scheduler
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/schedule.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/schedule_types.h b/platform/linux-generic/include/odp/api/schedule_types.h
deleted file mode 100644
index 536007d66..000000000
--- a/platform/linux-generic/include/odp/api/schedule_types.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP schedule
- */
-
-#ifndef ODP_PLAT_SCHEDULE_TYPES_H_
-#define ODP_PLAT_SCHEDULE_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/schedule_types.h>
-
-#include <odp/api/spec/schedule_types.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/shared_memory.h b/platform/linux-generic/include/odp/api/shared_memory.h
deleted file mode 100644
index affc290e7..000000000
--- a/platform/linux-generic/include/odp/api/shared_memory.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2013-2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP shared memory
- */
-
-#ifndef ODP_PLAT_SHARED_MEMORY_H_
-#define ODP_PLAT_SHARED_MEMORY_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/shared_memory_types.h>
-
-/** @ingroup odp_shared_memory
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/shared_memory.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/spinlock.h b/platform/linux-generic/include/odp/api/spinlock.h
deleted file mode 100644
index 830f4edd2..000000000
--- a/platform/linux-generic/include/odp/api/spinlock.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP spinlock
- */
-
-#ifndef ODP_PLAT_SPINLOCK_H_
-#define ODP_PLAT_SPINLOCK_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/spinlock_types.h>
-
-#include <odp/api/spec/spinlock.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/spinlock_recursive.h b/platform/linux-generic/include/odp/api/spinlock_recursive.h
deleted file mode 100644
index d97b0173a..000000000
--- a/platform/linux-generic/include/odp/api/spinlock_recursive.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP resursive spinlock
- */
-
-#ifndef ODP_PLAT_SPINLOCK_RECURSIVE_H_
-#define ODP_PLAT_SPINLOCK_RECURSIVE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/spinlock_recursive_types.h>
-
-#include <odp/api/spec/spinlock_recursive.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/std_clib.h b/platform/linux-generic/include/odp/api/std_clib.h
deleted file mode 100644
index fea472543..000000000
--- a/platform/linux-generic/include/odp/api/std_clib.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PLAT_STD_CLIB_H_
-#define ODP_PLAT_STD_CLIB_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/spec/std_types.h>
-#include <string.h>
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/std_clib_inlines.h>
-#endif
-
-#include <odp/api/spec/std_clib.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/std_types.h b/platform/linux-generic/include/odp/api/std_types.h
deleted file mode 100644
index b61f33f4c..000000000
--- a/platform/linux-generic/include/odp/api/std_types.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Standard C language types and definitions for ODP.
- */
-
-#ifndef ODP_PLAT_STD_TYPES_H_
-#define ODP_PLAT_STD_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* uint64_t, uint32_t, etc */
-#include <stdint.h>
-
-/* true and false for odp_bool_t */
-#include <stdbool.h>
-
-/** @addtogroup odp_system ODP SYSTEM
- * @{
- */
-
-typedef int odp_bool_t;
-
-/**
- * @}
- */
-
-#include <odp/api/spec/std_types.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/sync.h b/platform/linux-generic/include/odp/api/sync.h
deleted file mode 100644
index e1afcc722..000000000
--- a/platform/linux-generic/include/odp/api/sync.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP synchronisation
- */
-
-#ifndef ODP_PLAT_SYNC_H_
-#define ODP_PLAT_SYNC_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_barrier
- * @{
- */
-
-#include <odp/api/plat/static_inline.h>
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/sync_inlines.h>
-#endif
-
-/**
- * @}
- */
-
-#include <odp/api/spec/sync.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/system_info.h b/platform/linux-generic/include/odp/api/system_info.h
deleted file mode 100644
index 36ddc814b..000000000
--- a/platform/linux-generic/include/odp/api/system_info.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP system information
- */
-
-#ifndef ODP_PLAT_SYSTEM_INFO_H_
-#define ODP_PLAT_SYSTEM_INFO_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-
-
-#include <odp/api/spec/system_info.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/thread.h b/platform/linux-generic/include/odp/api/thread.h
deleted file mode 100644
index c54abc890..000000000
--- a/platform/linux-generic/include/odp/api/thread.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP thread API
- */
-
-#ifndef ODP_PLAT_THREAD_H_
-#define ODP_PLAT_THREAD_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/thread_types.h>
-
-#include <odp/api/spec/thread.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/thrmask.h b/platform/linux-generic/include/odp/api/thrmask.h
deleted file mode 100644
index b1c207775..000000000
--- a/platform/linux-generic/include/odp/api/thrmask.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP thread masks
- */
-
-#ifndef ODP_PLAT_THRMASK_H_
-#define ODP_PLAT_THRMASK_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/thrmask_types.h>
-
-/** @ingroup odp_thread
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/thrmask.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/ticketlock.h b/platform/linux-generic/include/odp/api/ticketlock.h
deleted file mode 100644
index e0f5d81fd..000000000
--- a/platform/linux-generic/include/odp/api/ticketlock.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP ticketlock
- */
-
-#ifndef ODP_PLAT_TICKETLOCK_H_
-#define ODP_PLAT_TICKETLOCK_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/ticketlock_types.h>
-
-#if ODP_ABI_COMPAT == 0
-#include <odp/api/plat/ticketlock_inlines.h>
-#endif
-
-#include <odp/api/spec/ticketlock.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/time.h b/platform/linux-generic/include/odp/api/time.h
deleted file mode 100644
index 8d1c33e68..000000000
--- a/platform/linux-generic/include/odp/api/time.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP time
- */
-
-#ifndef ODP_PLAT_TIME_H_
-#define ODP_PLAT_TIME_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-
-
-
-#include <odp/api/plat/time_types.h>
-#include <odp/api/spec/time.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/timer.h b/platform/linux-generic/include/odp/api/timer.h
deleted file mode 100644
index 1450727f8..000000000
--- a/platform/linux-generic/include/odp/api/timer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP timer service
- */
-
-#ifndef ODP_PLAT_TIMER_H_
-#define ODP_PLAT_TIMER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/plat/event_types.h>
-#include <odp/api/plat/pool_types.h>
-#include <odp/api/plat/queue_types.h>
-#include <odp/api/plat/timer_types.h>
-
-/** @ingroup odp_timer
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/spec/timer.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/traffic_mngr.h b/platform/linux-generic/include/odp/api/traffic_mngr.h
deleted file mode 100644
index 3e6f5fbbe..000000000
--- a/platform/linux-generic/include/odp/api/traffic_mngr.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP Traffic manager
- */
-
-#ifndef ODP_PLAT_TRAFFIC_MNGR_H_
-#define ODP_PLAT_TRAFFIC_MNGR_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @ingroup odp_traffic_mngr
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/api/plat/traffic_mngr_types.h>
-#include <odp/api/spec/traffic_mngr.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/api/version.h b/platform/linux-generic/include/odp/api/version.h
deleted file mode 100644
index fc4ea5865..000000000
--- a/platform/linux-generic/include/odp/api/version.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP version
- */
-
-#ifndef ODP_PLAT_VERSION_H_
-#define ODP_PLAT_VERSION_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/plat/version_types.h>
-#include <odp/api/spec/version.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/drv/README b/platform/linux-generic/include/odp/drv/README
deleted file mode 100644
index fd38e8e57..000000000
--- a/platform/linux-generic/include/odp/drv/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains the files defining the ODP driver interface,
-for linux-generic.
diff --git a/platform/linux-generic/include/odp/drv/compiler.h b/platform/linux-generic/include/odp/drv/compiler.h
deleted file mode 100644
index 24e84c554..000000000
--- a/platform/linux-generic/include/odp/drv/compiler.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Compiler related
- */
-
-#ifndef ODPDRV_PLAT_COMPILER_H_
-#define ODPDRV_PLAT_COMPILER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** @addtogroup odpdrv_compiler_optim ODPDRV COMPILER / OPTIMIZATION
- * @{
- */
-
-/**
- * @}
- */
-
-#include <odp/drv/spec/compiler.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/drv/std_types.h b/platform/linux-generic/include/odp/drv/std_types.h
deleted file mode 100644
index 4fe4affda..000000000
--- a/platform/linux-generic/include/odp/drv/std_types.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Standard C language types and definitions for ODP driver interface.
- */
-
-#ifndef ODPDRV_PLAT_STD_TYPES_H_
-#define ODPDRV_PLAT_STD_TYPES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <limits.h>
-
-/** @addtogroup odpdrv_system ODPDRV SYSTEM
- * @{
- */
-
-typedef int odpdrv_bool_t;
-
-/**
- * @}
- */
-
-#include <odp/drv/spec/std_types.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp/visibility_begin.h b/platform/linux-generic/include/odp/visibility_begin.h
deleted file mode 100644
index 1bbb43def..000000000
--- a/platform/linux-generic/include/odp/visibility_begin.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * @file
- *
- * Linker visibility directives
- */
-
-#if __GNUC__ >= 4
-#pragma GCC visibility push(default)
-#endif
diff --git a/platform/linux-generic/include/odp/visibility_end.h b/platform/linux-generic/include/odp/visibility_end.h
deleted file mode 100644
index 748af5103..000000000
--- a/platform/linux-generic/include/odp/visibility_end.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * @file
- *
- * Linker visibility directives
- */
-
-#if __GNUC__ >= 4
-#pragma GCC visibility pop
-#endif
diff --git a/platform/linux-generic/include/odp_align_internal.h b/platform/linux-generic/include/odp_align_internal.h
deleted file mode 100644
index 61ff200c5..000000000
--- a/platform/linux-generic/include/odp_align_internal.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP internal alignments
- */
-
-#ifndef ODP_ALIGN_INTERNAL_H_
-#define ODP_ALIGN_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/align.h>
-#include <stdint.h>
-
-/* Macros to calculate ODP_ROUNDUP_POWER2_U32() in five rounds of shift
- * and OR operations. */
-#define _RSHIFT_U32(x, y) (((uint32_t)(x)) >> (y))
-#define _POW2_U32_R1(x) (((uint32_t)(x)) | _RSHIFT_U32(x, 1))
-#define _POW2_U32_R2(x) (_POW2_U32_R1(x) | _RSHIFT_U32(_POW2_U32_R1(x), 2))
-#define _POW2_U32_R3(x) (_POW2_U32_R2(x) | _RSHIFT_U32(_POW2_U32_R2(x), 4))
-#define _POW2_U32_R4(x) (_POW2_U32_R3(x) | _RSHIFT_U32(_POW2_U32_R3(x), 8))
-#define _POW2_U32_R5(x) (_POW2_U32_R4(x) | _RSHIFT_U32(_POW2_U32_R4(x), 16))
-
-/* Round up a uint32_t value 'x' to the next power of two.
- *
- * The value is not round up, if it's already a power of two (including 1).
- * The value must be larger than 0 and not exceed 0x80000000.
- */
-#define ROUNDUP_POWER2_U32(x) \
- ((((uint32_t)(x)) > 0x80000000) ? 0 : (_POW2_U32_R5(x - 1) + 1))
-
-/*
- * Round up 'x' to alignment 'align'
- */
-#define ROUNDUP_ALIGN(x, align)\
- ((align) * (((x) + (align) - 1) / (align)))
-
-/*
- * Round up 'x' to cache line size alignment
- */
-#define ROUNDUP_CACHE_LINE(x)\
- ROUNDUP_ALIGN(x, ODP_CACHE_LINE_SIZE)
-
-/*
- * Round down 'x' to 'align' alignment, which is a power of two
- */
-#define ROUNDDOWN_POWER2(x, align)\
- ((x) & (~((align) - 1)))
-
-/*
- * Check if value is a power of two
- */
-#define CHECK_IS_POWER2(x) ((((x) - 1) & (x)) == 0)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_atomic_internal.h b/platform/linux-generic/include/odp_atomic_internal.h
index dca2175ce..3e6c45a27 100644
--- a/platform/linux-generic/include/odp_atomic_internal.h
+++ b/platform/linux-generic/include/odp_atomic_internal.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
/**
@@ -20,6 +18,7 @@
#include <odp/api/align.h>
#include <odp/api/hints.h>
#include <odp/api/atomic.h>
+#include <odp_types_internal.h>
#include <stdbool.h>
#ifdef __cplusplus
@@ -27,14 +26,6 @@ extern "C" {
#endif
/**
- * Pointer atomic type
- */
-typedef struct {
- void *v; /**< Actual storage for the atomic variable */
-} _odp_atomic_ptr_t
-ODP_ALIGNED(sizeof(void *)); /* Enforce alignement! */
-
-/**
* Atomic flag (boolean) type
* @Note this is not the same as a plain boolean type.
* _odp_atomic_flag_t is guaranteed to be able to operate on atomically.
@@ -59,469 +50,9 @@ typedef enum {
_ODP_MEMMODEL_RLS = __ATOMIC_RELEASE,
/** Acquire&release memory ordering, synchronize with acquire loads and release
* stores in another (one other) thread */
- _ODP_MEMMODEL_ACQ_RLS = __ATOMIC_ACQ_REL,
-/** Sequential consistent memory ordering, synchronize with acquire loads and
- * release stores in all threads */
- _ODP_MEMMODEL_SC = __ATOMIC_SEQ_CST
-} _odp_memmodel_t;
-
-/*****************************************************************************
- * Operations on 32-bit atomics
- * _odp_atomic_u32_load_mm - return current value
- * _odp_atomic_u32_store_mm - no return value
- * _odp_atomic_u32_xchg_mm - return old value
- * _odp_atomic_u32_cmp_xchg_strong_mm - return bool
- * _odp_atomic_u32_fetch_add_mm - return old value
- * _odp_atomic_u32_add_mm - no return value
- * _odp_atomic_u32_fetch_sub_mm - return old value
- * _odp_atomic_u32_sub_mm - no return value
- *****************************************************************************/
-
-/**
- * Atomic load of 32-bit atomic variable
- *
- * @param atom Pointer to a 32-bit atomic variable
- * @param mmodel Memory ordering associated with the load operation
- *
- * @return Value of the variable
- */
-static inline uint32_t _odp_atomic_u32_load_mm(const odp_atomic_u32_t *atom,
- _odp_memmodel_t mmodel)
-{
- return __atomic_load_n(&atom->v, mmodel);
-}
-
-/**
- * Atomic store to 32-bit atomic variable
- *
- * @param[out] atom Pointer to a 32-bit atomic variable
- * @param val Value to store in the atomic variable
- * @param mmodel Memory order associated with the store operation
- */
-static inline void _odp_atomic_u32_store_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-{
- __atomic_store_n(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic exchange (swap) of 32-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param val New value to store in the atomic variable
- * @param mmodel Memory order associated with the exchange operation
- *
- * @return Old value of the variable
- */
-static inline uint32_t _odp_atomic_u32_xchg_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-
-{
- return __atomic_exchange_n(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic compare and exchange (swap) of 32-bit atomic variable
- * "Strong" semantics, will not fail spuriously.
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param[in,out] exp Pointer to expected value (updated on failure)
- * @param val New value to write
- * @param success Memory order associated with a successful compare-and-swap
- * operation
- * @param failure Memory order associated with a failed compare-and-swap
- * operation
- *
- * @retval 1 exchange successul
- * @retval 0 exchange failed and '*exp' updated with current value
- */
-static inline int _odp_atomic_u32_cmp_xchg_strong_mm(
- odp_atomic_u32_t *atom,
- uint32_t *exp,
- uint32_t val,
- _odp_memmodel_t success,
- _odp_memmodel_t failure)
-{
- return __atomic_compare_exchange_n(&atom->v, exp, val,
- false/*strong*/, success, failure);
-}
-
-/**
- * Atomic fetch and add of 32-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param val Value to add to the atomic variable
- * @param mmodel Memory order associated with the add operation
- *
- * @return Value of the atomic variable before the addition
- */
-static inline uint32_t _odp_atomic_u32_fetch_add_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-{
- return __atomic_fetch_add(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic add of 32-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param val Value to add to the atomic variable
- * @param mmodel Memory order associated with the add operation
- */
-static inline void _odp_atomic_u32_add_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-
-{
- (void)__atomic_fetch_add(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic fetch and subtract of 32-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param val Value to subtract from the atomic variable
- * @param mmodel Memory order associated with the subtract operation
- *
- * @return Value of the atomic variable before the subtraction
- */
-static inline uint32_t _odp_atomic_u32_fetch_sub_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-{
- return __atomic_fetch_sub(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic subtract of 32-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 32-bit atomic variable
- * @param val Value to subtract from the atomic variable
- * @param mmodel Memory order associated with the subtract operation
- */
-static inline void _odp_atomic_u32_sub_mm(odp_atomic_u32_t *atom,
- uint32_t val,
- _odp_memmodel_t mmodel)
-
-{
- (void)__atomic_fetch_sub(&atom->v, val, mmodel);
-}
-
-/*****************************************************************************
- * Operations on 64-bit atomics
- * _odp_atomic_u64_load_mm - return current value
- * _odp_atomic_u64_store_mm - no return value
- * _odp_atomic_u64_xchg_mm - return old value
- * _odp_atomic_u64_cmp_xchg_strong_mm - return bool
- * _odp_atomic_u64_fetch_add_mm - return old value
- * _odp_atomic_u64_add_mm - no return value
- * _odp_atomic_u64_fetch_sub_mm - return old value
- * _odp_atomic_u64_sub_mm - no return value
- *****************************************************************************/
-
-/* Check if the compiler support lock-less atomic operations on 64-bit types */
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
-/**
- * @internal
- * Helper macro for lock-based atomic operations on 64-bit integers
- * @param[in,out] atom Pointer to the 64-bit atomic variable
- * @param expr Expression used update the variable.
- * @param mm Memory order to use.
- * @return The old value of the variable.
- */
-#define ATOMIC_OP_MM(atom, expr, mm) \
-({ \
- uint64_t old_val; \
- /* Loop while lock is already taken, stop when lock becomes clear */ \
- while (__atomic_test_and_set(&(atom)->lock, \
- (mm) == _ODP_MEMMODEL_SC ? \
- __ATOMIC_SEQ_CST : __ATOMIC_ACQUIRE)) \
- (void)0; \
- old_val = (atom)->v; \
- (expr); /* Perform whatever update is desired */ \
- __atomic_clear(&(atom)->lock, \
- (mm) == _ODP_MEMMODEL_SC ? \
- __ATOMIC_SEQ_CST : __ATOMIC_RELEASE); \
- old_val; /* Return old value */ \
-})
-#endif
-
-/**
- * Atomic load of 64-bit atomic variable
- *
- * @param atom Pointer to a 64-bit atomic variable
- * @param mmodel Memory order associated with the load operation
- *
- * @return Value of the variable
- */
-static inline uint64_t _odp_atomic_u64_load_mm(odp_atomic_u64_t *atom,
- _odp_memmodel_t mmodel)
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- return ATOMIC_OP_MM(atom, (void)0, mmodel);
-#else
- return __atomic_load_n(&atom->v, mmodel);
-#endif
-}
-
-/**
- * Atomic store to 64-bit atomic variable
- *
- * @param[out] atom Pointer to a 64-bit atomic variable
- * @param val Value to write to the atomic variable
- * @param mmodel Memory order associated with the store operation
- */
-static inline void _odp_atomic_u64_store_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP_MM(atom, atom->v = val, mmodel);
-#else
- __atomic_store_n(&atom->v, val, mmodel);
-#endif
-}
-
-/**
- * Atomic exchange (swap) of 64-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param val New value to write to the atomic variable
- * @param mmodel Memory order associated with the exchange operation
- *
- * @return Old value of variable
- */
-static inline uint64_t _odp_atomic_u64_xchg_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
+ _ODP_MEMMODEL_ACQ_RLS = __ATOMIC_ACQ_REL
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- return ATOMIC_OP_MM(atom, atom->v = val, mmodel);
-#else
- return __atomic_exchange_n(&atom->v, val, mmodel);
-#endif
-}
-
-/**
- * Atomic compare and exchange (swap) of 64-bit atomic variable
- * "Strong" semantics, will not fail spuriously.
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param[in,out] exp Pointer to expected value (updated on failure)
- * @param val New value to write
- * @param success Memory order associated with a successful compare-and-swap
- * operation
- * @param failure Memory order associated with a failed compare-and-swap
- * operation
- *
- * @retval 1 exchange successful
- * @retval 0 exchange failed and '*exp' updated with current value
- */
-static inline int _odp_atomic_u64_cmp_xchg_strong_mm(odp_atomic_u64_t *atom,
- uint64_t *exp,
- uint64_t val,
- _odp_memmodel_t success,
- _odp_memmodel_t failure)
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- /* Possibly we are a bit pessimistic with the memory models */
- odp_bool_t ret_succ;
- /* Loop while lock is already taken, stop when lock becomes clear */
- while (__atomic_test_and_set(&(atom)->lock,
- (success) == _ODP_MEMMODEL_SC ?
- __ATOMIC_SEQ_CST : __ATOMIC_ACQUIRE))
- (void)0;
- if (atom->v == *exp) {
- atom->v = val;
- ret_succ = 1;
- } else {
- *exp = atom->v;
- ret_succ = 0;
- }
- __atomic_clear(&(atom)->lock,
- (ret_succ ? success : failure) == _ODP_MEMMODEL_SC ?
- __ATOMIC_SEQ_CST : __ATOMIC_RELEASE);
- return ret_succ;
-#else
- return __atomic_compare_exchange_n(&atom->v, exp, val,
- false/*strong*/, success, failure);
-#endif
-}
-
-/**
- * Atomic fetch and add of 64-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param val Value to add to the atomic variable
- * @param mmodel Memory order associated with the add operation
- *
- * @return Value of the atomic variable before the addition
- */
-static inline uint64_t _odp_atomic_u64_fetch_add_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- return ATOMIC_OP_MM(atom, atom->v += val, mmodel);
-#else
- return __atomic_fetch_add(&atom->v, val, mmodel);
-#endif
-}
-
-/**
- * Atomic add of 64-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param val Value to add to the atomic variable
- * @param mmodel Memory order associated with the add operation.
- */
-static inline void _odp_atomic_u64_add_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
-
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP_MM(atom, atom->v += val, mmodel);
-#else
- (void)__atomic_fetch_add(&atom->v, val, mmodel);
-#endif
-}
-
-/**
- * Atomic fetch and subtract of 64-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param val Value to subtract from the atomic variable
- * @param mmodel Memory order associated with the subtract operation
- *
- * @return Value of the atomic variable before the subtraction
- */
-static inline uint64_t _odp_atomic_u64_fetch_sub_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- return ATOMIC_OP_MM(atom, atom->v -= val, mmodel);
-#else
- return __atomic_fetch_sub(&atom->v, val, mmodel);
-#endif
-}
-
-/**
- * Atomic subtract of 64-bit atomic variable
- *
- * @param[in,out] atom Pointer to a 64-bit atomic variable
- * @param val Value to subtract from the atomic variable
- * @param mmodel Memory order associated with the subtract operation
- */
-static inline void _odp_atomic_u64_sub_mm(odp_atomic_u64_t *atom,
- uint64_t val,
- _odp_memmodel_t mmodel)
-
-{
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- (void)ATOMIC_OP_MM(atom, atom->v -= val, mmodel);
-#else
- (void)__atomic_fetch_sub(&atom->v, val, mmodel);
-#endif
-}
-
-#if !defined __GCC_ATOMIC_LLONG_LOCK_FREE || __GCC_ATOMIC_LLONG_LOCK_FREE < 2
-#undef ATOMIC_OP_MM
-#endif
-
-/*****************************************************************************
- * Operations on pointer atomics
- * _odp_atomic_ptr_init - no return value
- * _odp_atomic_ptr_load - return current value
- * _odp_atomic_ptr_store - no return value
- * _odp_atomic_ptr_xchg - return old value
- *****************************************************************************/
-
-/**
- * Initialization of pointer atomic variable
- *
- * @param[out] atom Pointer to a pointer atomic variable
- * @param val Value to initialize the variable with
- */
-static inline void _odp_atomic_ptr_init(_odp_atomic_ptr_t *atom, void *val)
-{
- __atomic_store_n(&atom->v, val, __ATOMIC_RELAXED);
-}
-
-/**
- * Atomic load of pointer atomic variable
- *
- * @param atom Pointer to a pointer atomic variable
- * @param mmodel Memory order associated with the load operation
- *
- * @return Value of the variable
- */
-static inline void *_odp_atomic_ptr_load(const _odp_atomic_ptr_t *atom,
- _odp_memmodel_t mmodel)
-{
- return __atomic_load_n(&atom->v, mmodel);
-}
-
-/**
- * Atomic store to pointer atomic variable
- *
- * @param[out] atom Pointer to a pointer atomic variable
- * @param val Value to write to the atomic variable
- * @param mmodel Memory order associated with the store operation
- */
-static inline void _odp_atomic_ptr_store(_odp_atomic_ptr_t *atom,
- void *val,
- _odp_memmodel_t mmodel)
-{
- __atomic_store_n(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic exchange (swap) of pointer atomic variable
- *
- * @param[in,out] atom Pointer to a pointer atomic variable
- * @param val New value to write
- * @param mmodel Memory order associated with the exchange operation
- *
- * @return Old value of variable
- */
-static inline void *_odp_atomic_ptr_xchg(_odp_atomic_ptr_t *atom,
- void *val,
- _odp_memmodel_t mmodel)
-{
- return __atomic_exchange_n(&atom->v, val, mmodel);
-}
-
-/**
- * Atomic compare and exchange (swap) of pointer atomic variable
- * "Strong" semantics, will not fail spuriously.
- *
- * @param[in,out] atom Pointer to a pointer atomic variable
- * @param[in,out] exp Pointer to expected value (updated on failure)
- * @param val New value to write
- * @param success Memory order associated with a successful compare-and-swap
- * operation
- * @param failure Memory order associated with a failed compare-and-swap
- * operation
- *
- * @retval 1 exchange successful
- * @retval 0 exchange failed and '*exp' updated with current value
- */
-static inline int _odp_atomic_ptr_cmp_xchg_strong(
- _odp_atomic_ptr_t *atom,
- void **exp,
- void *val,
- _odp_memmodel_t success,
- _odp_memmodel_t failure)
-{
- return __atomic_compare_exchange_n(&atom->v, exp, val,
- false/*strong*/, success, failure);
-}
+} _odp_memmodel_t;
/*****************************************************************************
* Operations on flag atomics
@@ -541,7 +72,7 @@ static inline int _odp_atomic_ptr_cmp_xchg_strong(
* @param val The initial value of the variable
*/
static inline void _odp_atomic_flag_init(_odp_atomic_flag_t *flag,
- odp_bool_t val)
+ odp_bool_t val)
{
__atomic_clear(flag, __ATOMIC_RELAXED);
if (val)
@@ -605,13 +136,11 @@ static inline void _odp_atomic_flag_clear(_odp_atomic_flag_t *flag)
#endif
#ifdef ODP_ATOMIC_U128
-/** An unsigned 128-bit (16-byte) scalar type */
-typedef __int128 _uint128_t;
/** Atomic 128-bit type */
-typedef struct {
- _uint128_t v; /**< Actual storage for the atomic variable */
-} _odp_atomic_u128_t ODP_ALIGNED(16);
+typedef struct ODP_ALIGNED(16) {
+ _odp_u128_t v; /**< Actual storage for the atomic variable */
+} _odp_atomic_u128_t;
/**
* 16-byte atomic exchange operation
@@ -622,37 +151,12 @@ typedef struct {
* @param mmodel Memory model associated with the exchange operation
*/
static inline void _odp_atomic_u128_xchg_mm(_odp_atomic_u128_t *ptr,
- _uint128_t *val,
- _uint128_t *old,
+ _odp_u128_t *val,
+ _odp_u128_t *old,
_odp_memmodel_t mm)
{
__atomic_exchange(&ptr->v, val, old, mm);
}
-
-/**
- * Atomic compare and exchange (swap) of 16-byte atomic variable
- * "Strong" semantics, will not fail spuriously.
- *
- * @param ptr Pointer to a 16-byte atomic variable
- * @param exp Pointer to expected value (updated on failure)
- * @param val Pointer to new value to write
- * @param succ Memory model associated with a successful compare-and-swap
- * operation
- * @param fail Memory model associated with a failed compare-and-swap
- * operation
- *
- * @retval 1 exchange successul
- * @retval 0 exchange failed and '*exp' updated with current value
- */
-static inline int _odp_atomic_u128_cmp_xchg_mm(_odp_atomic_u128_t *ptr,
- _uint128_t *exp,
- _uint128_t *val,
- _odp_memmodel_t succ,
- _odp_memmodel_t fail)
-{
- return __atomic_compare_exchange(&ptr->v, exp, val,
- false/*strong*/, succ, fail);
-}
#endif
/**
diff --git a/platform/linux-generic/include/odp_bitmap_internal.h b/platform/linux-generic/include/odp_bitmap_internal.h
deleted file mode 100644
index 1be4d0287..000000000
--- a/platform/linux-generic/include/odp_bitmap_internal.h
+++ /dev/null
@@ -1,317 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP generic bitmap types and operations.
- */
-
-#ifndef ODP_BITMAP_INTERNAL_H_
-#define ODP_BITMAP_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <odp/api/hints.h>
-
-/* Generate unique identifier for instantiated class */
-#define TOKENIZE(template, line) \
- template ## _ ## line ## _ ## __COUNTER__
-
-/* Array size in general */
-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
-
-#define BITS_PER_BYTE (8)
-#define BITS_PER_LONG __WORDSIZE
-#define BYTES_PER_LONG (BITS_PER_LONG / BITS_PER_BYTE)
-
-#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
-#define BITS_TO_LONGS(nr) BIT_WORD(nr + BITS_PER_LONG - 1)
-
-#define BITMAP_FIRST_WORD_MASK(start) \
- (~0UL << ((start) & (BITS_PER_LONG - 1)))
-#define BITMAP_LAST_WORD_MASK(nbits) \
- (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
-
-/* WAPL bitmap base class */
-typedef struct {
- unsigned int nwords;
- unsigned int *pl;
- unsigned long *ul;
-} wapl_bitmap_t;
-
-/*
- * Word-Aligned Position List (WAPL) bitmap, which actually
- * is not a compression, but with an extra list of non-empty
- * word positions.
- *
- * WAPL accelerates bitwise operations and iterations by
- * applying only to non-empty positions instead of walking
- * through the whole bitmap.
- *
- * WAPL uses [1 ~ N] instead of [0 ~ N - 1] as position
- * values and an extra 0 as end indicator for position list.
- * This is the reason to allocate one extra room below.
- */
-#define instantiate_wapl_bitmap(line, nbits) \
- struct TOKENIZE(wapl_bitmap, line) { \
- unsigned int pl[BITS_TO_LONGS(nbits) + 1]; \
- unsigned long ul[BITS_TO_LONGS(nbits) + 1]; \
- }
-
-#define WAPL_BITMAP(nbits) instantiate_wapl_bitmap(__LINE__, nbits)
-
-/*
- * Upcast any derived WAPL bitmap class to its base class
- */
-#define __wapl_upcast(base, derived) \
- do { \
- __typeof__(derived) p = derived; \
- base.pl = p->pl; \
- base.ul = p->ul; \
- base.nwords = ARRAY_SIZE(p->ul) - 1; \
- } while (0)
-
-/*
- * WAPL base class bitmap operations
- */
-void __wapl_bitmap_and(wapl_bitmap_t *dst,
- wapl_bitmap_t *src, wapl_bitmap_t *and);
-
-void __wapl_bitmap_or(wapl_bitmap_t *dst, wapl_bitmap_t *or);
-
-void __wapl_bitmap_set(wapl_bitmap_t *map, unsigned int bit);
-
-void __wapl_bitmap_clear(wapl_bitmap_t *map, unsigned int bit);
-
-/*
- * Generic WAPL bitmap operations
- */
-#define wapl_bitmap_zero(map) \
- ({ \
- __typeof__(map) p = map; \
- memset((void *)p, 0, sizeof(__typeof__(*p))); \
- })
-
-#define wapl_bitmap_copy(dst, src) \
- ({ \
- __typeof__(dst) d = dst; \
- __typeof__(src) s = src; \
- if (d != s) \
- memcpy((void *)d, (void *)s, \
- sizeof(__typeof__(*d))); \
- })
-
-#define wapl_bitmap_and(dst, src, and) \
- ({ \
- wapl_bitmap_t d, s, a; \
- __wapl_upcast(d, dst); \
- __wapl_upcast(s, src); \
- __wapl_upcast(a, and); \
- __wapl_bitmap_and(&d, &s, &a); \
- })
-
-#define wapl_bitmap_or(dst, src, or) \
- ({ \
- wapl_bitmap_t d, o; \
- wapl_bitmap_copy(dst, src); \
- __wapl_upcast(d, dst); \
- __wapl_upcast(o, or); \
- __wapl_bitmap_or(&d, &o); \
- })
-
-#define wapl_bitmap_set(map, bit) \
- ({ \
- wapl_bitmap_t b; \
- __wapl_upcast(b, map); \
- __wapl_bitmap_set(&b, bit); \
- })
-
-#define wapl_bitmap_clear(map, bit) \
- ({ \
- wapl_bitmap_t b; \
- __wapl_upcast(b, map); \
- __wapl_bitmap_clear(&b, bit); \
- })
-
-/*
- * Round robin iterator runs upon a WAPL bitmap:
- *
- * wapl_bitmap_iterator(iterator, WAPL bitmap);
- * for (iterator->start(); iterator->has_next(); ) {
- * unsigned int bit_index = iterator->next();
- * ...operations on this bit index...
- * }
- */
-typedef struct wapl_bitmap_iterator {
- int _start, _next, _nbits;
- wapl_bitmap_t _base;
-
- void (*start)(struct wapl_bitmap_iterator *this);
- bool (*has_next)(struct wapl_bitmap_iterator *this);
- unsigned int (*next)(struct wapl_bitmap_iterator *this);
-} wapl_bitmap_iterator_t;
-
-/*
- * WAPL bitmap iterator constructor
- */
-void __wapl_bitmap_iterator(wapl_bitmap_iterator_t *this);
-
-/*
- * Generic constructor accepts any derived WAPL bitmap class
- */
-#define wapl_bitmap_iterator(iterator, map) \
- ({ \
- __typeof__(iterator) __it = iterator; \
- __wapl_upcast(__it->_base, map); \
- __wapl_bitmap_iterator(__it); \
- })
-
-/* Sparse bitmap base class */
-typedef struct {
- unsigned int nbits;
- unsigned int *last, *pl, *il;
-} sparse_bitmap_t;
-
-/*
- * Sparse bitmap, lists all bit indexes directly as an array.
- * Expected to be significantly straightforward iteration.
- */
-#define instantiate_sparse_bitmap(line, nbits) \
- struct TOKENIZE(sparse_bitmap, line) { \
- unsigned int last; \
- unsigned int pl[nbits]; \
- unsigned int il[nbits]; \
- }
-
-#define SPARSE_BITMAP(nbits) instantiate_sparse_bitmap(__LINE__, nbits)
-
-/*
- * Upcast any derived sparse bitmap class to its base class
- */
-#define __sparse_upcast(base, derived) \
- do { \
- __typeof__(derived) p = derived; \
- base.pl = p->pl; \
- base.il = p->il; \
- base.last = &p->last; \
- base.nbits = ARRAY_SIZE(p->il); \
- } while (0)
-
-/*
- * Sparse base class bitmap operations
- */
-void __sparse_bitmap_set(sparse_bitmap_t *map, unsigned int bit);
-
-void __sparse_bitmap_clear(sparse_bitmap_t *map, unsigned int bit);
-
-/*
- * Generic sparse bitmap operations
- */
-#define sparse_bitmap_zero(map) \
- ({ \
- __typeof__(map) p = map; \
- memset((void *)p, 0, sizeof(__typeof__(*p))); \
- })
-
-#define sparse_bitmap_set(map, bit) \
- ({ \
- sparse_bitmap_t b; \
- __sparse_upcast(b, map); \
- __sparse_bitmap_set(&b, bit); \
- })
-
-#define sparse_bitmap_clear(map, bit) \
- ({ \
- sparse_bitmap_t b; \
- __sparse_upcast(b, map); \
- __sparse_bitmap_clear(&b, bit); \
- })
-
-/*
- * Round robin iterator runs upon a sparse bitmap:
- *
- * sparse_bitmap_iterator(iterator, SPARSE bitmap);
- * for (iterator->start(); iterator->has_next(); ) {
- * unsigned int bit_index = iterator->next();
- * ...operations on this bit index...
- * }
- */
-typedef struct sparse_bitmap_iterator {
- int _start, _next, _nbits;
- sparse_bitmap_t _base;
-
- void (*start)(struct sparse_bitmap_iterator *this);
- bool (*has_next)(struct sparse_bitmap_iterator *this);
- unsigned int (*next)(struct sparse_bitmap_iterator *this);
-} sparse_bitmap_iterator_t;
-
-/*
- * Sparse bitmap iterator constructor
- */
-void __sparse_bitmap_iterator(sparse_bitmap_iterator_t *this);
-
-/*
- * Generic constructor accepts any derived sparse bitmap class.
- */
-#define sparse_bitmap_iterator(iterator, map) \
- ({ \
- __typeof__(iterator) __it = iterator; \
- __sparse_upcast(__it->_base, map); \
- __sparse_bitmap_iterator(__it); \
- })
-
-/*
- * Raw bitmap atomic set and clear.
- */
-void raw_bitmap_set(unsigned long *map, unsigned int bit);
-
-void raw_bitmap_clear(unsigned long *map, unsigned int bit);
-
-/*
- * It will enter infinite loop incase that all bits are zero,
- * so please make sure the bitmap at least has one set.
- */
-static inline int __bitmap_wraparound_next(
- unsigned long *addr, unsigned int nbits, int start)
-{
- unsigned long tmp;
-
- if (start >= (int)nbits)
- start = 0;
-
- tmp = addr[BIT_WORD(start)];
-
- /* Handle 1st word. */
- tmp &= BITMAP_FIRST_WORD_MASK(start);
- start = start & ~(BITS_PER_LONG - 1);
-
- while (!tmp) {
- start += BITS_PER_LONG;
- if (start >= (int)nbits)
- start = 0;
-
- tmp = addr[BIT_WORD(start)];
- }
-
- start += __builtin_ffsl(tmp) - 1;
- return start;
-}
-
-/**
- * @}
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_bitset.h b/platform/linux-generic/include/odp_bitset.h
new file mode 100644
index 000000000..92212330b
--- /dev/null
+++ b/platform/linux-generic/include/odp_bitset.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef _ODP_BITSET_H_
+#define _ODP_BITSET_H_
+
+#include <odp_cpu.h>
+
+#include <limits.h>
+
+/******************************************************************************
+ * bitset abstract data type
+ *****************************************************************************/
+/* This could be a struct of scalars to support larger bit sets */
+
+/*
+ * Size of atomic bit set. This limits the max number of threads,
+ * scheduler groups and reorder windows. On ARMv8/64-bit and x86-64, the
+ * (lock-free) max is 128
+ */
+
+#if ATOM_BITSET_SIZE <= 32
+
+/* Return first-bit-set with StdC ffs() semantics */
+static inline uint32_t bitset_ffs(bitset_t b)
+{
+ return __builtin_ffsl(b);
+}
+
+#elif ATOM_BITSET_SIZE <= 64
+
+/* Return first-bit-set with StdC ffs() semantics */
+static inline uint32_t bitset_ffs(bitset_t b)
+{
+ return __builtin_ffsll(b);
+}
+
+#elif ATOM_BITSET_SIZE <= 128
+
+/* Return first-bit-set with StdC ffs() semantics */
+static inline uint32_t bitset_ffs(bitset_t b)
+{
+ if ((uint64_t)b != 0)
+ return __builtin_ffsll((uint64_t)b);
+ else if ((b >> 64) != 0)
+ return __builtin_ffsll((uint64_t)(b >> 64)) + 64;
+ else
+ return 0;
+}
+
+#else
+#error Unsupported size of bit sets (ATOM_BITSET_SIZE)
+#endif
+
+/* Return a & ~b */
+static inline bitset_t bitset_andn(bitset_t a, bitset_t b)
+{
+ return a & ~b;
+}
+
+static inline bool bitset_is_eql(bitset_t a, bitset_t b)
+{
+ return a == b;
+}
+
+static inline bitset_t bitset_clr(bitset_t bs, uint32_t bit)
+{
+ return bs & ~bitset_mask(bit);
+}
+
+static inline bitset_t bitset_set(bitset_t bs, uint32_t bit)
+{
+ return bs | bitset_mask(bit);
+}
+
+static inline bitset_t bitset_null(void)
+{
+ return 0U;
+}
+
+static inline bool bitset_is_null(bitset_t a)
+{
+ return a == 0U;
+}
+
+static inline bool bitset_is_set(bitset_t a, uint32_t bit)
+{
+ return (a & bitset_mask(bit)) != 0;
+}
+
+#endif
diff --git a/platform/linux-generic/include/odp_buffer_inlines.h b/platform/linux-generic/include/odp_buffer_inlines.h
deleted file mode 100644
index a5658e815..000000000
--- a/platform/linux-generic/include/odp_buffer_inlines.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * Inline functions for ODP buffer mgmt routines - implementation internal
- */
-
-#ifndef ODP_BUFFER_INLINES_H_
-#define ODP_BUFFER_INLINES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_buffer_internal.h>
-
-odp_event_type_t _odp_buffer_event_type(odp_buffer_t buf);
-void _odp_buffer_event_type_set(odp_buffer_t buf, int ev);
-int odp_buffer_snprint(char *str, uint32_t n, odp_buffer_t buf);
-
-static inline odp_buffer_t buf_from_buf_hdr(odp_buffer_hdr_t *hdr)
-{
- return (odp_buffer_t)hdr;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_buffer_internal.h b/platform/linux-generic/include/odp_buffer_internal.h
index a580c82d5..66b2a454c 100644
--- a/platform/linux-generic/include/odp_buffer_internal.h
+++ b/platform/linux-generic/include/odp_buffer_internal.h
@@ -1,10 +1,8 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
*/
-
/**
* @file
*
@@ -24,88 +22,41 @@ extern "C" {
#include <odp/api/buffer.h>
#include <odp/api/debug.h>
#include <odp/api/align.h>
-#include <odp_align_internal.h>
#include <odp_config_internal.h>
#include <odp/api/byteorder.h>
#include <odp/api/thread.h>
#include <odp/api/event.h>
-#include <odp_forward_typedefs_internal.h>
-#include <odp_schedule_if.h>
+#include <odp_event_internal.h>
#include <stddef.h>
-#define BUFFER_BURST_SIZE CONFIG_BURST_SIZE
-
-/* Common buffer header */
-struct odp_buffer_hdr_t {
- /* Buffer index in the pool */
- uint32_t index;
-
- /* Initial buffer data pointer and length */
- uint8_t *base_data;
- uint8_t *buf_end;
-
- /* Max data size */
- uint32_t size;
-
- /* Pool type */
- int8_t type;
-
- /* Burst counts */
- uint8_t burst_num;
- uint8_t burst_first;
-
- /* Segment count */
- uint8_t segcount;
-
- /* Segments */
- struct {
- void *hdr;
- uint8_t *data;
- uint32_t len;
- } seg[CONFIG_PACKET_MAX_SEGS];
-
- /* Next buf in a list */
- struct odp_buffer_hdr_t *next;
-
- /* User context pointer or u64 */
- union {
- uint64_t buf_u64;
- void *buf_ctx;
- const void *buf_cctx; /* const alias for ctx */
- };
+/* Internal buffer header */
+typedef struct ODP_ALIGNED_CACHE odp_buffer_hdr_t {
+ /* Common event header */
+ _odp_event_hdr_t event_hdr;
/* User area pointer */
- void *uarea_addr;
-
- /* User area size */
- uint32_t uarea_size;
-
- /* Event type. Maybe different than pool type (crypto compl event) */
- int8_t event_type;
+ void *uarea_addr;
- /* Burst table */
- struct odp_buffer_hdr_t *burst[BUFFER_BURST_SIZE];
+ /* Data */
+ uint8_t data[];
+} odp_buffer_hdr_t;
- /* Used only if _ODP_PKTIO_IPC is set.
- * ipc mapped process can not walk over pointers,
- * offset has to be used */
- uint64_t ipc_data_offset;
+/* Buffer header size is critical for performance. Ensure that it does not accidentally
+ * grow over cache line size. Note that ODP_ALIGNED_CACHE rounds up struct size to a multiple of
+ * ODP_CACHE_LINE_SIZE. */
+ODP_STATIC_ASSERT(sizeof(odp_buffer_hdr_t) <= ODP_CACHE_LINE_SIZE, "BUFFER_HDR_SIZE_ERROR");
- /* Pool handle: will be removed, used only for odp_packet_pool()
- * inlining */
- odp_pool_t pool_hdl;
-
- /* Pool pointer */
- void *pool_ptr;
-
- /* Data or next header */
- uint8_t data[0];
-} ODP_ALIGNED_CACHE;
+static inline odp_buffer_hdr_t *_odp_buf_hdr(odp_buffer_t buf)
+{
+ return (odp_buffer_hdr_t *)(uintptr_t)buf;
+}
-ODP_STATIC_ASSERT(CONFIG_PACKET_MAX_SEGS < 256,
- "CONFIG_PACKET_MAX_SEGS_TOO_LARGE");
+static inline void _odp_buffer_subtype_set(odp_buffer_t buffer, int subtype)
+{
+ odp_buffer_hdr_t *buf_hdr = _odp_buf_hdr(buffer);
-ODP_STATIC_ASSERT(BUFFER_BURST_SIZE < 256, "BUFFER_BURST_SIZE_TOO_LARGE");
+ buf_hdr->event_hdr.subtype = subtype;
+}
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_chksum_internal.h b/platform/linux-generic/include/odp_chksum_internal.h
new file mode 100644
index 000000000..04f79771c
--- /dev/null
+++ b/platform/linux-generic/include/odp_chksum_internal.h
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#ifndef ODP_CHKSUM_INTERNAL_H_
+#define ODP_CHKSUM_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/hints.h>
+#include <odp/api/byteorder.h>
+#include <odp_cpu.h>
+#include <stdint.h>
+
+/*
+ * Compute the final Internet checksum (RFC 1071) based on a partial
+ * sum. A partial sum can be obtained e.g. by calling
+ * chksum_partial().
+ */
+static inline uint16_t chksum_finalize(uint64_t sum)
+{
+ sum = (sum >> 32) + (sum & 0xffffffff);
+ sum = (sum >> 16) + (sum & 0xffff);
+ /*
+ * The final & 0xffff is intentionally omitted, the extra bits
+ * are discarded by the implicit cast to the return type.
+ */
+ return (sum >> 16) + sum;
+}
+
+/*
+ * Compute a partial checksum. Several partial checksums may be summed
+ * together. The final checksum may be obtained by calling
+ * chksum_finalize(). Parameter offset is the offset of this segment
+ * of data from the start of IP header.
+ *
+ * This implementation
+ *
+ * - Accepts unaligned data.
+ *
+ * - Accepts data at any byte offset from the start of IP header,
+ * including odd offsets.
+ *
+ * - Uses unaligned memory access only if available.
+ *
+ * - Is optimized (for skylake, cn96, a53) by trial and error.
+ *
+ * The following did not improve performance (in synthetic tests):
+ *
+ * - 2 or 4 sub-sums in the main loop (to break dependency chains).
+ *
+ * - Aligning to 8 bytes instead of 4 (for ldp instruction). This
+ * makes the main loop faster on a53 (only), but the extra
+ * conditional branch has its cost.
+ *
+ * - __builtin_assume_aligned().
+ */
+static uint64_t chksum_partial(const void *addr, uint32_t len, uint32_t offset)
+{
+ const uint8_t *b;
+#if _ODP_UNALIGNED
+ /*
+ * _ODP_UNALIGNED does not guarantee that all possible ways of
+ * accessing memory can be unaligned. Make the compiler aware
+ * of the possible unalignment so that it does not generate
+ * instructions (such as LDM of AArch32) that require higher
+ * alignment than one byte.
+ */
+ typedef uint32_t x_uint32_t ODP_ALIGNED(1);
+ typedef uint16_t x_uint16_t ODP_ALIGNED(1);
+#else
+ /* In this case we can use normal types as we align manually. */
+ typedef uint32_t x_uint32_t;
+ typedef uint16_t x_uint16_t;
+#endif
+ const x_uint16_t *w;
+ const x_uint32_t *d;
+ uint64_t sum = 0;
+
+ /*
+ * Offset is either even or odd, the rest of it doesn't
+ * matter.
+ */
+ offset &= 1;
+
+ if (_ODP_UNALIGNED) {
+ /*
+ * We have efficient unaligned access. Just read
+ * dwords starting at the given address.
+ */
+ d = (const x_uint32_t *)addr;
+ } else {
+ /*
+ * We must avoid unaligned access, so align to 4 bytes
+ * by summing up the first up to 3 bytes.
+ */
+ b = (const uint8_t *)addr;
+
+ if (odp_unlikely((uintptr_t)b & 1) && len >= 1) {
+ /*
+ * Align to 2 bytes by handling an odd
+ * byte. Since addr is unaligned, the first
+ * byte goes into the second byte of the sum.
+ */
+ sum += odp_cpu_to_be_16(*b++);
+ len -= 1;
+
+ /* An odd byte negates the effect of offset. */
+ offset ^= 1;
+ }
+
+ /*
+ * This cast increases alignment, but it's OK, since
+ * we've made sure that the pointer value is aligned.
+ */
+ w = (const x_uint16_t *)(uintptr_t)b;
+
+ if ((uintptr_t)w & 2 && len >= 2) {
+ /* Align bytes by handling an odd word. */
+ sum += *w++;
+ len -= 2;
+ }
+
+ /* Increases alignment. */
+ d = (const x_uint32_t *)(uintptr_t)w;
+ }
+
+ while (len >= 32) {
+ /* 8 dwords or 32 bytes per round. */
+
+ sum += *d++;
+ sum += *d++;
+ sum += *d++;
+ sum += *d++;
+
+ sum += *d++;
+ sum += *d++;
+ sum += *d++;
+ sum += *d++;
+
+ len -= 32;
+ }
+
+ /* Last up to 7 dwords. */
+ switch (len >> 2) {
+ case 7:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 6:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 5:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 4:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 3:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 2:
+ sum += *d++;
+ /* FALLTHROUGH */
+ case 1:
+ sum += *d++;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+
+ len &= 3;
+
+ w = (const x_uint16_t *)d;
+ if (len > 1) {
+ /* Last word. */
+ sum += *w++;
+ len -= 2;
+ }
+
+ if (len) {
+ /* Last byte. */
+ b = (const uint8_t *)w;
+ sum += odp_cpu_to_be_16((uint16_t)*b << 8);
+ }
+
+ /*
+ * If offset is odd, our sum is byte-flipped and we need to
+ * flip odd and even bytes.
+ */
+ if (odp_unlikely(offset))
+ sum = ((sum & 0xff00ff00ff00ff) << 8) | ((sum & 0xff00ff00ff00ff00) >> 8);
+
+ return sum;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_classification_datamodel.h b/platform/linux-generic/include/odp_classification_datamodel.h
index fbe10cb4d..1da4ed1f7 100644
--- a/platform/linux-generic/include/odp_classification_datamodel.h
+++ b/platform/linux-generic/include/odp_classification_datamodel.h
@@ -1,10 +1,7 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
-
/**
* @file
*
@@ -21,128 +18,147 @@ extern "C" {
#include <odp/api/spinlock.h>
#include <odp/api/classification.h>
+#include <odp/api/debug.h>
+
+#include <odp_macros_internal.h>
#include <odp_pool_internal.h>
#include <odp_packet_internal.h>
#include <odp_packet_io_internal.h>
#include <odp_queue_if.h>
+
#include <protocols/ip.h>
/* Maximum Class Of Service Entry */
-#define ODP_COS_MAX_ENTRY 64
+#define CLS_COS_MAX_ENTRY 64
+/* Invalid CoS index */
+#define CLS_COS_IDX_NONE CLS_COS_MAX_ENTRY
/* Maximum PMR Entry */
-#define ODP_PMR_MAX_ENTRY 256
+#define CLS_PMR_MAX_ENTRY 256
/* Maximum PMR Terms in a PMR Set */
-#define ODP_PMRTERM_MAX 8
+#define CLS_PMRTERM_MAX 8
/* Maximum PMRs attached in PKTIO Level */
-#define ODP_PMR_PER_COS_MAX 8
-/* L2 Priority Bits */
-#define ODP_COS_L2_QOS_BITS 3
-/* Max L2 QoS value */
-#define ODP_COS_MAX_L2_QOS (1 << ODP_COS_L2_QOS_BITS)
-/* L2 DSCP Bits */
-#define ODP_COS_L3_QOS_BITS 6
-/* Max L3 QoS Value */
-#define ODP_COS_MAX_L3_QOS (1 << ODP_COS_L3_QOS_BITS)
-/* Max PMR Term bits */
-#define ODP_PMR_TERM_BYTES_MAX 16
-
-/**
-Packet Matching Rule Term Value
+#define CLS_PMR_PER_COS_MAX 8
+/* Max PMR Term size */
+#define MAX_PMR_TERM_SIZE 16
+/* Max queue per Class of service */
+#define CLS_COS_QUEUE_MAX 32
+/* Max number of implementation created queues */
+#define CLS_QUEUE_GROUP_MAX (CLS_COS_MAX_ENTRY * CLS_COS_QUEUE_MAX)
+
+/* CoS index is stored in odp_packet_hdr_t */
+ODP_STATIC_ASSERT(CLS_COS_MAX_ENTRY <= UINT16_MAX, "CoS_does_not_fit_16_bits");
+
+typedef union {
+ /* All proto fields */
+ uint32_t all;
+
+ struct {
+ uint32_t ipv4:1;
+ uint32_t ipv6:1;
+ uint32_t udp:1;
+ uint32_t tcp:1;
+ };
+} odp_cls_hash_proto_t;
-Stores the Term and Value mapping for a PMR.
-The maximum size of value currently supported in 64 bits
-**/
+/*
+ * Term and value mapping for a PMR
+ */
typedef struct pmr_term_value {
- odp_cls_pmr_term_t term; /* PMR Term */
- odp_bool_t range_term; /* True if range, false if match */
+ /* PMR Term */
+ odp_cls_pmr_term_t term;
+
+ /* True if range, false if match */
+ odp_bool_t range_term;
+
union {
+ /* Match value and mask */
struct {
- /** Value to be matched */
- uint64_t value;
- /** Masked set of bits to be matched */
- uint64_t mask;
+ /* Value to be matched. Arrays are used with custom and
+ * IPv6 address terms. */
+ union {
+ uint64_t value;
+ uint8_t value_u8[MAX_PMR_TERM_SIZE];
+ uint64_t value_u64[2];
+ };
+
+ /* Mask for the data to be matched */
+ union {
+ uint64_t mask;
+ uint8_t mask_u8[MAX_PMR_TERM_SIZE];
+ uint64_t mask_u64[2];
+ };
+
} match;
+
+ /* Range values */
struct {
- /** Start value of the range */
- uint64_t val_start;
- /** End value of the range */
- uint64_t val_end;
+ /* Start value of the range */
+ union {
+ uint64_t start;
+ uint8_t start_u8[MAX_PMR_TERM_SIZE];
+ uint64_t start_u64[2];
+ };
+
+ /* End value of the range */
+ union {
+ uint64_t end;
+ uint8_t end_u8[MAX_PMR_TERM_SIZE];
+ uint64_t end_u64[2];
+ };
+
} range;
- struct {
- _odp_ipv6_addr_t addr;
- _odp_ipv6_addr_t mask;
- } match_ipv6;
- struct {
- _odp_ipv6_addr_t addr_start;
- _odp_ipv6_addr_t addr_end;
- } range_ipv6;
+
};
- uint32_t offset; /**< Offset if term == ODP_PMR_CUSTOM_FRAME */
- uint32_t val_sz; /**< Size of the value to be matched */
+
+ /* Offset used with custom PMR */
+ uint32_t offset;
+
+ /* Size of the value to be matched */
+ uint32_t val_sz;
+
} pmr_term_value_t;
/*
Class Of Service
*/
-struct cos_s {
- queue_t queue; /* Associated Queue */
- odp_pool_t pool; /* Associated Buffer pool */
- union pmr_u *pmr[ODP_PMR_PER_COS_MAX]; /* Chained PMR */
- union cos_u *linked_cos[ODP_PMR_PER_COS_MAX]; /* Chained CoS with PMR*/
+typedef struct ODP_ALIGNED_CACHE cos_s {
uint32_t valid; /* validity Flag */
- odp_cls_drop_t drop_policy; /* Associated Drop Policy */
+ odp_atomic_u32_t num_rule; /* num of PMRs attached with this CoS */
+ struct pmr_s *pmr[CLS_PMR_PER_COS_MAX]; /* Chained PMR */
+ struct cos_s *linked_cos[CLS_PMR_PER_COS_MAX]; /* Chained CoS with PMR*/
+ odp_bool_t stats_enable;
+ odp_cos_action_t action; /* Action */
+ odp_queue_t queue; /* Associated Queue */
+ uint32_t num_queue;
+ odp_pool_t pool; /* Associated Buffer pool */
+ uint8_t index;
+ bool queue_group;
+ odp_cls_hash_proto_t hash_proto;
+ odp_pktin_vector_config_t vector; /* Packet vector config */
size_t headroom; /* Headroom for this CoS */
odp_spinlock_t lock; /* cos lock */
- odp_atomic_u32_t num_rule; /* num of PMRs attached with this CoS */
+ odp_queue_param_t queue_param;
char name[ODP_COS_NAME_LEN]; /* name */
-};
-
-typedef union cos_u {
- struct cos_s s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct cos_s))];
+ struct {
+ odp_atomic_u64_t discards;
+ odp_atomic_u64_t packets;
+ } stats, queue_stats[CLS_COS_QUEUE_MAX];
} cos_t;
-
-/**
-Packet Matching Rule
-
-**/
-struct pmr_s {
+/* Pattern Matching Rule */
+typedef struct ODP_ALIGNED_CACHE pmr_s {
uint32_t valid; /* Validity Flag */
- odp_atomic_u32_t count; /* num of packets matching this rule */
uint32_t num_pmr; /* num of PMR Term Values*/
+ uint16_t mark;
+ pmr_term_value_t pmr_term_value[CLS_PMRTERM_MAX];
+ /* List of associated PMR Terms */
odp_spinlock_t lock; /* pmr lock*/
cos_t *src_cos; /* source CoS where PMR is attached */
- pmr_term_value_t pmr_term_value[ODP_PMRTERM_MAX];
- /* List of associated PMR Terms */
-};
-
-typedef union pmr_u {
- struct pmr_s s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct pmr_s))];
} pmr_t;
-/**
-L2 QoS and CoS Map
-
-This structure holds the mapping between L2 QoS value and
-corresponding cos_t object
-**/
-typedef struct pmr_l2_cos {
- odp_spinlock_t lock; /* pmr_l2_cos lock */
- cos_t *cos[ODP_COS_MAX_L2_QOS]; /* Array of CoS objects */
-} pmr_l2_cos_t;
-
-/**
-L3 QoS and CoS Map
-
-This structure holds the mapping between L3 QoS value and
-corresponding cos_t object
-**/
-typedef struct pmr_l3_cos {
- odp_spinlock_t lock; /* pmr_l3_cos lock */
- cos_t *cos[ODP_COS_MAX_L3_QOS]; /* Array of CoS objects */
-} pmr_l3_cos_t;
+typedef struct ODP_ALIGNED_CACHE {
+ odp_queue_t queue[CLS_QUEUE_GROUP_MAX];
+} _cls_queue_grp_tbl_t;
/**
Linux Generic Classifier
@@ -153,9 +169,6 @@ the classifier configuration value.
typedef struct classifier {
cos_t *error_cos; /* Associated Error CoS */
cos_t *default_cos; /* Associated Default CoS */
- uint32_t l3_precedence; /* L3 QoS precedence */
- pmr_l2_cos_t l2_cos_table; /* L2 QoS-CoS table map */
- pmr_l3_cos_t l3_cos_table; /* L3 Qos-CoS table map */
size_t headroom; /* Pktio Headroom */
size_t skip; /* Pktio Skip Offset */
} classifier_t;
@@ -164,16 +177,27 @@ typedef struct classifier {
Class of Service Table
**/
typedef struct odp_cos_table {
- cos_t cos_entry[ODP_COS_MAX_ENTRY];
+ cos_t cos_entry[CLS_COS_MAX_ENTRY];
} cos_tbl_t;
/**
PMR table
**/
typedef struct pmr_tbl {
- pmr_t pmr[ODP_PMR_MAX_ENTRY];
+ pmr_t pmr[CLS_PMR_MAX_ENTRY];
} pmr_tbl_t;
+/**
+Classifier global data
+**/
+typedef struct cls_global_t {
+ cos_tbl_t cos_tbl;
+ pmr_tbl_t pmr_tbl;
+ _cls_queue_grp_tbl_t queue_grp_tbl;
+ odp_shm_t shm;
+
+} cls_global_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/platform/linux-generic/include/odp_classification_inlines.h b/platform/linux-generic/include/odp_classification_inlines.h
deleted file mode 100644
index 2747db8cc..000000000
--- a/platform/linux-generic/include/odp_classification_inlines.h
+++ /dev/null
@@ -1,377 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP Classification Inlines
- * Classification Inlines Functions
- */
-#ifndef __ODP_CLASSIFICATION_INLINES_H_
-#define __ODP_CLASSIFICATION_INLINES_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/debug.h>
-#include <protocols/eth.h>
-#include <protocols/ip.h>
-#include <protocols/ipsec.h>
-#include <protocols/udp.h>
-#include <protocols/tcp.h>
-#include <odp_packet_internal.h>
-#include <stdio.h>
-#include <inttypes.h>
-
-/* PMR term value verification function
-These functions verify the given PMR term value with the value in the packet
-These following functions return 1 on success and 0 on failure
-*/
-
-static inline int verify_pmr_packet_len(odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- if (term_value->match.value == (packet_len(pkt_hdr) &
- term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ip_proto(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ipv4hdr_t *ip;
- uint8_t proto;
- if (!pkt_hdr->p.input_flags.ipv4)
- return 0;
- ip = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
- proto = ip->proto;
- if (term_value->match.value == (proto & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ipv4_saddr(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ipv4hdr_t *ip;
- uint32_t ipaddr;
- if (!pkt_hdr->p.input_flags.ipv4)
- return 0;
- ip = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
- ipaddr = odp_be_to_cpu_32(ip->src_addr);
- if (term_value->match.value == (ipaddr & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ipv4_daddr(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ipv4hdr_t *ip;
- uint32_t ipaddr;
- if (!pkt_hdr->p.input_flags.ipv4)
- return 0;
- ip = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
- ipaddr = odp_be_to_cpu_32(ip->dst_addr);
- if (term_value->match.value == (ipaddr & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_tcp_sport(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint16_t sport;
- const _odp_tcphdr_t *tcp;
- if (!pkt_hdr->p.input_flags.tcp)
- return 0;
- tcp = (const _odp_tcphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
- sport = odp_be_to_cpu_16(tcp->src_port);
- if (term_value->match.value == (sport & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_tcp_dport(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint16_t dport;
- const _odp_tcphdr_t *tcp;
- if (!pkt_hdr->p.input_flags.tcp)
- return 0;
- tcp = (const _odp_tcphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
- dport = odp_be_to_cpu_16(tcp->dst_port);
- if (term_value->match.value == (dport & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_udp_dport(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint16_t dport;
- const _odp_udphdr_t *udp;
- if (!pkt_hdr->p.input_flags.udp)
- return 0;
- udp = (const _odp_udphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
- dport = odp_be_to_cpu_16(udp->dst_port);
- if (term_value->match.value == (dport & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_udp_sport(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint16_t sport;
- const _odp_udphdr_t *udp;
-
- if (!pkt_hdr->p.input_flags.udp)
- return 0;
- udp = (const _odp_udphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
- sport = odp_be_to_cpu_16(udp->src_port);
- if (term_value->match.value == (sport & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_dmac(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint64_t dmac = 0;
- uint64_t dmac_be = 0;
- const _odp_ethhdr_t *eth;
-
- if (!packet_hdr_has_eth(pkt_hdr))
- return 0;
-
- eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
- memcpy(&dmac_be, eth->dst.addr, _ODP_ETHADDR_LEN);
- dmac = odp_be_to_cpu_64(dmac_be);
- /* since we are converting a 48 bit ethernet address from BE to cpu
- format using odp_be_to_cpu_64() the last 16 bits needs to be right
- shifted */
- if (dmac_be != dmac)
- dmac = dmac >> (64 - (_ODP_ETHADDR_LEN * 8));
-
- if (term_value->match.value == (dmac & term_value->match.mask))
- return 1;
- return 0;
-}
-
-static inline int verify_pmr_ipv6_saddr(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ipv6hdr_t *ipv6;
- uint64_t addr[2];
-
- if (!packet_hdr_has_ipv6(pkt_hdr))
- return 0;
-
- ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
-
- addr[0] = ipv6->src_addr.u64[0];
- addr[1] = ipv6->src_addr.u64[1];
-
- /* 128 bit address is processed as two 64 bit value
- * for bitwise AND operation */
- addr[0] = addr[0] & term_value->match_ipv6.mask.u64[0];
- addr[1] = addr[1] & term_value->match_ipv6.mask.u64[1];
-
- if (!memcmp(addr, term_value->match_ipv6.addr.u8, _ODP_IPV6ADDR_LEN))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ipv6_daddr(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ipv6hdr_t *ipv6;
- uint64_t addr[2];
-
- if (!packet_hdr_has_ipv6(pkt_hdr))
- return 0;
- ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
- addr[0] = ipv6->dst_addr.u64[0];
- addr[1] = ipv6->dst_addr.u64[1];
-
- /* 128 bit address is processed as two 64 bit value
- * for bitwise AND operation */
- addr[0] = addr[0] & term_value->match_ipv6.mask.u64[0];
- addr[1] = addr[1] & term_value->match_ipv6.mask.u64[1];
-
- if (!memcmp(addr, term_value->match_ipv6.addr.u8, _ODP_IPV6ADDR_LEN))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_vlan_id_0(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ethhdr_t *eth;
- const _odp_vlanhdr_t *vlan;
- uint16_t tci;
- uint16_t vlan_id;
-
- if (!pkt_hdr->p.input_flags.vlan_qinq)
- return 0;
-
- eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
- vlan = (const _odp_vlanhdr_t *)(eth + 1);
- tci = odp_be_to_cpu_16(vlan->tci);
- vlan_id = tci & 0x0fff;
-
- if (term_value->match.value == (vlan_id & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_vlan_id_x(const uint8_t *pkt_addr ODP_UNUSED,
- odp_packet_hdr_t *pkt_hdr ODP_UNUSED,
- pmr_term_value_t *term_value ODP_UNUSED)
-{
- const _odp_ethhdr_t *eth;
- const _odp_vlanhdr_t *vlan;
- uint16_t tci;
- uint16_t vlan_id;
-
- if (!pkt_hdr->p.input_flags.vlan_qinq)
- return 0;
-
- eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
- vlan = (const _odp_vlanhdr_t *)(eth + 1);
- vlan++;
- tci = odp_be_to_cpu_16(vlan->tci);
- vlan_id = tci & 0x0fff;
-
- if (term_value->match.value == (vlan_id & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ipsec_spi(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint32_t spi;
-
- pkt_addr += pkt_hdr->p.l4_offset;
-
- if (pkt_hdr->p.input_flags.ipsec_ah) {
- const _odp_ahhdr_t *ahhdr = (const _odp_ahhdr_t *)pkt_addr;
-
- spi = odp_be_to_cpu_32(ahhdr->spi);
- } else if (pkt_hdr->p.input_flags.ipsec_esp) {
- const _odp_esphdr_t *esphdr = (const _odp_esphdr_t *)pkt_addr;
-
- spi = odp_be_to_cpu_32(esphdr->spi);
- } else {
- return 0;
- }
-
- if (term_value->match.value == (spi & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_ld_vni(const uint8_t *pkt_addr ODP_UNUSED,
- odp_packet_hdr_t *pkt_hdr ODP_UNUSED,
- pmr_term_value_t *term_value ODP_UNUSED)
-{
- ODP_UNIMPLEMENTED();
- return 0;
-}
-
-static inline int verify_pmr_custom_frame(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- uint64_t val = 0;
- uint32_t offset = term_value->offset;
- uint32_t val_sz = term_value->val_sz;
-
- ODP_ASSERT(val_sz <= ODP_PMR_TERM_BYTES_MAX);
-
- if (packet_len(pkt_hdr) <= offset + val_sz)
- return 0;
-
- memcpy(&val, pkt_addr + offset, val_sz);
- if (term_value->match.value == (val & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_eth_type_0(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ethhdr_t *eth;
- uint16_t ethtype;
-
- if (!pkt_hdr->p.input_flags.vlan_qinq)
- return 0;
-
- eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
- ethtype = odp_be_to_cpu_16(eth->type);
-
- if (term_value->match.value == (ethtype & term_value->match.mask))
- return 1;
-
- return 0;
-}
-
-static inline int verify_pmr_eth_type_x(const uint8_t *pkt_addr,
- odp_packet_hdr_t *pkt_hdr,
- pmr_term_value_t *term_value)
-{
- const _odp_ethhdr_t *eth;
- uint16_t ethtype;
- const _odp_vlanhdr_t *vlan;
-
- if (!pkt_hdr->p.input_flags.vlan_qinq)
- return 0;
-
- eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
- vlan = (const _odp_vlanhdr_t *)(eth + 1);
- ethtype = odp_be_to_cpu_16(vlan->type);
-
- if (term_value->match.value == (ethtype & term_value->match.mask))
- return 1;
-
- return 0;
-}
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/platform/linux-generic/include/odp_classification_internal.h b/platform/linux-generic/include/odp_classification_internal.h
index 369636d5a..4223af1b4 100644
--- a/platform/linux-generic/include/odp_classification_internal.h
+++ b/platform/linux-generic/include/odp_classification_internal.h
@@ -1,10 +1,8 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
-
/**
* @file
*
@@ -19,13 +17,213 @@
extern "C" {
#endif
+#include <odp/api/atomic.h>
#include <odp/api/classification.h>
+#include <odp/api/event.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/pool.h>
#include <odp/api/queue.h>
+#include <odp/api/std_types.h>
+
+#include <odp_debug_internal.h>
#include <odp_packet_internal.h>
-#include <odp/api/packet_io.h>
#include <odp_packet_io_internal.h>
#include <odp_classification_datamodel.h>
+#include <stdint.h>
+
+extern cls_global_t *_odp_cls_global;
+
+static inline cos_t *_odp_cos_entry_from_idx(uint32_t ndx)
+{
+ return &_odp_cls_global->cos_tbl.cos_entry[ndx];
+}
+
+static inline int _odp_cos_queue_idx(const cos_t *cos, odp_queue_t queue)
+{
+ uint32_t i, tbl_idx;
+ int queue_idx = -1;
+
+ if (cos->num_queue == 1) {
+ if (odp_unlikely(cos->queue != queue))
+ return -1;
+ return 0;
+ }
+
+ tbl_idx = cos->index * CLS_COS_QUEUE_MAX;
+ for (i = 0; i < cos->num_queue; i++) {
+ if (_odp_cls_global->queue_grp_tbl.queue[tbl_idx + i] == queue) {
+ queue_idx = i;
+ break;
+ }
+ }
+ return queue_idx;
+}
+
+static inline void _odp_cos_queue_stats_add(cos_t *cos, odp_queue_t queue,
+ uint64_t packets, uint64_t discards)
+{
+ int queue_idx = _odp_cos_queue_idx(cos, queue);
+
+ if (odp_unlikely(queue_idx < 0)) {
+ _ODP_ERR("Queue not attached to the CoS\n");
+ return;
+ }
+
+ if (packets)
+ odp_atomic_add_u64(&cos->queue_stats[queue_idx].packets, packets);
+ if (discards)
+ odp_atomic_add_u64(&cos->queue_stats[queue_idx].discards, discards);
+}
+
+static inline void _odp_cos_vector_enq(odp_queue_t queue, odp_event_t events[], uint32_t num,
+ cos_t *cos)
+{
+ odp_packet_vector_t pktv;
+ const odp_pool_t pool = cos->vector.pool;
+ const uint32_t max_size = cos->vector.max_size;
+ uint32_t num_enq;
+ int num_pktv = (num + max_size - 1) / max_size;
+ int ret;
+ int i;
+ odp_packet_vector_t pktv_tbl[num_pktv];
+ odp_event_t event_tbl[num_pktv];
+
+ for (i = 0; i < num_pktv; i++) {
+ pktv = odp_packet_vector_alloc(pool);
+ if (odp_unlikely(pktv == ODP_PACKET_VECTOR_INVALID))
+ break;
+ pktv_tbl[i] = pktv;
+ event_tbl[i] = odp_packet_vector_to_event(pktv);
+ }
+ if (odp_unlikely(i == 0)) {
+ odp_event_free_multi(events, num);
+ _odp_cos_queue_stats_add(cos, queue, 0, num);
+ return;
+ }
+ num_pktv = i;
+ num_enq = 0;
+ for (i = 0; i < num_pktv; i++) {
+ odp_packet_t *pkt_tbl;
+ int pktv_size = max_size;
+
+ pktv = pktv_tbl[i];
+
+ if (num_enq + max_size > num)
+ pktv_size = num - num_enq;
+
+ odp_packet_vector_tbl(pktv, &pkt_tbl);
+ odp_packet_from_event_multi(pkt_tbl, &events[num_enq], pktv_size);
+ odp_packet_vector_size_set(pktv, pktv_size);
+ num_enq += pktv_size;
+ }
+
+ ret = odp_queue_enq_multi(queue, event_tbl, num_pktv);
+ if (odp_likely(ret == num_pktv)) {
+ _odp_cos_queue_stats_add(cos, queue, num_enq, num - num_enq);
+ } else {
+ uint32_t enqueued;
+
+ if (ret < 0)
+ ret = 0;
+ enqueued = max_size * ret;
+ _odp_cos_queue_stats_add(cos, queue, enqueued, num - enqueued);
+ odp_event_free_multi(&event_tbl[ret], num_pktv - ret);
+ }
+}
+
+/**
+ * Enqueue packets into destination CoS
+ */
+static inline void _odp_cos_enq(uint16_t cos_id, odp_queue_t dst, odp_packet_t packets[], int num)
+{
+ _ODP_ASSERT(cos_id != CLS_COS_IDX_NONE);
+ _ODP_ASSERT(dst != ODP_QUEUE_INVALID);
+
+ cos_t *cos = _odp_cos_entry_from_idx(cos_id);
+
+ if (num < 2 || !cos->vector.enable) {
+ int ret = odp_queue_enq_multi(dst, (odp_event_t *)packets, num);
+
+ if (odp_unlikely(ret != num)) {
+ if (ret < 0)
+ ret = 0;
+
+ odp_packet_free_multi(&packets[ret], num - ret);
+ }
+ _odp_cos_queue_stats_add(cos, dst, ret, num - ret);
+ } else {
+ _odp_cos_vector_enq(dst, (odp_event_t *)packets, num, cos);
+ }
+}
+
+/**
+ * Enqueue all remaining packets in 'packets' array
+ */
+static inline void _odp_cls_enq_all(odp_packet_t packets[], int num)
+{
+ odp_packet_hdr_t *prev_hdr;
+ odp_packet_hdr_t *latest_hdr = packet_hdr(packets[num - 1]);
+
+ if (num < 2) {
+ _odp_cos_enq(latest_hdr->cos, latest_hdr->dst_queue, packets, 1);
+ return;
+ }
+
+ prev_hdr = packet_hdr(packets[num - 2]);
+
+ if (prev_hdr->dst_queue == latest_hdr->dst_queue && prev_hdr->cos == latest_hdr->cos) {
+ _odp_cos_enq(prev_hdr->cos, prev_hdr->dst_queue, packets, num);
+ } else {
+ _odp_cos_enq(prev_hdr->cos, prev_hdr->dst_queue, packets, num - 1);
+ _odp_cos_enq(latest_hdr->cos, latest_hdr->dst_queue, &packets[num - 1], 1);
+ }
+}
+
+/**
+ * Enqueue packets into classifier destination queue
+ *
+ * Called by pktio devices for each received packet when classifier has been enabled. Postpones the
+ * actual enqueue operation and stores packets in 'packets' array until destination queue or CoS
+ * change, or 'last' flag is set.
+ *
+ * @param[out] packets Packet array to be enqueued
+ * @param num Number of handles in 'packets' array
+ * @param last Enqueue all packets
+ *
+ * @return Number of packets remaining in 'packets' array
+ */
+static inline int _odp_cls_enq(odp_packet_t packets[], int num, odp_bool_t last)
+{
+ odp_packet_hdr_t *prev_hdr, *latest_hdr;
+
+ _ODP_ASSERT(num > 0);
+
+ if (last) {
+ _odp_cls_enq_all(packets, num);
+ return 0;
+ }
+
+ /* Only one packet, so postpone enqueue */
+ if (num < 2)
+ return num;
+
+ prev_hdr = packet_hdr(packets[num - 2]);
+ latest_hdr = packet_hdr(packets[num - 1]);
+
+ /* Postpone enqueue if destination queue and CoS have not changed */
+ if (prev_hdr->dst_queue == latest_hdr->dst_queue && prev_hdr->cos == latest_hdr->cos)
+ return num;
+
+ /* Perform previously postponed enqueue operation and move the last packet (different
+ * destination) in 'packets' array to be the first entry */
+ _odp_cos_enq(prev_hdr->cos, prev_hdr->dst_queue, packets, num - 1);
+ packets[0] = packets[num - 1];
+
+ return 1;
+}
+
/** Classification Internal function **/
/**
@@ -37,9 +235,8 @@ Start function for Packet Classifier
This function calls Classifier module internal functions for a given packet and
selects destination queue and packet pool based on selected PMR and CoS.
**/
-int cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,
- uint16_t pkt_len, uint32_t seg_len, odp_pool_t *pool,
- odp_packet_hdr_t *pkt_hdr);
+int _odp_cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,
+ odp_pool_t *pool, odp_packet_hdr_t *pkt_hdr);
/**
Packet IO classifier init
@@ -47,16 +244,7 @@ Packet IO classifier init
This function does initialization of classifier object associated with pktio.
This function should be called during pktio initialization.
**/
-int pktio_classifier_init(pktio_entry_t *pktio);
-
-/**
-@internal
-Flow Signature Calculation
-
-This function calculates the Flow Signature for a packet based on
-CoS and updates in Packet Meta Data
-**/
-int update_flow_signature(uint8_t *pkt_addr, cos_t *cos);
+int _odp_pktio_classifier_init(pktio_entry_t *pktio);
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_config_internal.h b/platform/linux-generic/include/odp_config_internal.h
index a798851fb..c7147acaf 100644
--- a/platform/linux-generic/include/odp_config_internal.h
+++ b/platform/linux-generic/include/odp_config_internal.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
#ifndef ODP_CONFIG_INTERNAL_H_
@@ -12,14 +11,51 @@ extern "C" {
#endif
/*
- * Maximum number of pools
+ * Maximum number of supported CPU identifiers. The maximum supported CPU ID is
+ * CONFIG_NUM_CPU_IDS - 1. Note that the maximum number of ODP threads is
+ * defined by ODP_THREAD_COUNT_MAX.
+ */
+#define CONFIG_NUM_CPU_IDS 256
+
+/*
+ * Maximum number of packet IO resources
+ */
+#define CONFIG_PKTIO_ENTRIES 64
+
+/*
+ * Pools reserved for internal usage, 1 for IPsec status events and one per packet
+ * I/O for TX completion
+ */
+#define CONFIG_INTERNAL_POOLS (1 + CONFIG_PKTIO_ENTRIES)
+
+/*
+ * Maximum number of pools.
*/
-#define ODP_CONFIG_POOLS 64
+#define CONFIG_POOLS 128
+
+/*
+ * Queues reserved for ODP internal use
+ */
+#define CONFIG_INTERNAL_QUEUES 64
+
+/*
+ * Maximum number of plain ODP queues
+ */
+#define CONFIG_MAX_PLAIN_QUEUES 1024
+
+/*
+ * Maximum number of scheduled ODP queues
+ *
+ * Must be a power of two.
+ */
+#define CONFIG_MAX_SCHED_QUEUES 1024
/*
* Maximum number of queues
*/
-#define ODP_CONFIG_QUEUES 1024
+#define CONFIG_MAX_QUEUES (CONFIG_INTERNAL_QUEUES + \
+ CONFIG_MAX_PLAIN_QUEUES + \
+ CONFIG_MAX_SCHED_QUEUES)
/*
* Maximum number of ordered locks per queue
@@ -27,17 +63,19 @@ extern "C" {
#define CONFIG_QUEUE_MAX_ORD_LOCKS 2
/*
- * Maximum number of packet IO resources
+ * Maximum number of DMA sessions
*/
-#define ODP_CONFIG_PKTIO_ENTRIES 64
+#define CONFIG_MAX_DMA_SESSIONS 32
/*
- * Minimum buffer alignment
- *
- * This defines the minimum supported buffer alignment. Requests for values
- * below this will be rounded up to this value.
+ * Stashes reserved for internal usage
+ */
+#define CONFIG_INTERNAL_STASHES CONFIG_MAX_DMA_SESSIONS
+
+/*
+ * Maximum number of stashes
*/
-#define ODP_CONFIG_BUFFER_ALIGN_MIN 64
+#define CONFIG_MAX_STASHES 2048
/*
* Maximum buffer alignment
@@ -45,7 +83,7 @@ extern "C" {
* This defines the maximum supported buffer alignment. Requests for values
* above this will fail.
*/
-#define ODP_CONFIG_BUFFER_ALIGN_MAX (4 * 1024)
+#define CONFIG_BUFFER_ALIGN_MAX (4 * 1024)
/*
* Default packet headroom
@@ -73,14 +111,9 @@ extern "C" {
#define CONFIG_PACKET_TAILROOM 0
/*
- * Maximum number of segments per packet
- */
-#define CONFIG_PACKET_MAX_SEGS 6
-
-/*
* Maximum packet segment size including head- and tailrooms
*/
-#define CONFIG_PACKET_SEG_SIZE (8 * 1024)
+#define CONFIG_PACKET_SEG_SIZE (60 * 1024)
/* Maximum data length in a segment
*
@@ -98,13 +131,26 @@ extern "C" {
* defined segment length (seg_len in odp_pool_param_t) will be rounded up into
* this value.
*/
-#define CONFIG_PACKET_SEG_LEN_MIN CONFIG_PACKET_MAX_SEG_LEN
+#define CONFIG_PACKET_SEG_LEN_MIN ((2 * 1024) + \
+ CONFIG_PACKET_HEADROOM + \
+ CONFIG_PACKET_TAILROOM)
-/* Maximum number of shared memory blocks.
+/*
+ * Number of shared memory blocks reserved for implementation internal use.
*
- * This the the number of separate SHM areas that can be reserved concurrently
+ * Each pool requires three blocks (buffers, ring, user area), 20 blocks
+ * are reserved for per ODP module global data and one block per packet I/O is
+ * reserved for TX completion usage.
*/
-#define ODP_CONFIG_SHM_BLOCKS (ODP_CONFIG_POOLS + 48)
+#define CONFIG_INTERNAL_SHM_BLOCKS ((CONFIG_POOLS * 3) + 20 + CONFIG_PKTIO_ENTRIES)
+
+/*
+ * Maximum number of shared memory blocks.
+ *
+ * This is the number of separate SHM blocks that an application can reserve
+ * concurrently.
+ */
+#define CONFIG_SHM_BLOCKS 64
/*
* Maximum event burst size
@@ -112,27 +158,53 @@ extern "C" {
* This controls the burst size on various enqueue, dequeue, etc calls. Large
* burst size improves throughput, but may degrade QoS (increase latency).
*/
-#define CONFIG_BURST_SIZE 16
+#define CONFIG_BURST_SIZE 32
/*
- * Maximum number of events in a pool
+ * Maximum number of events in a pool. Power of two minus one results optimal
+ * memory usage for the ring.
*/
-#define CONFIG_POOL_MAX_NUM (1 * 1024 * 1024)
+#define CONFIG_POOL_MAX_NUM ((1024 * 1024) - 1)
/*
* Maximum number of events in a thread local pool cache
*/
-#define CONFIG_POOL_CACHE_SIZE 256
+#define CONFIG_POOL_CACHE_MAX_SIZE 256
+
+/* Maximum packet vector size */
+#define CONFIG_PACKET_VECTOR_MAX_SIZE 256
+
+/* Enable pool statistics collection */
+#define CONFIG_POOL_STATISTICS 1
+
+/*
+ * Maximum number of IPsec SAs. The actual maximum number can be further
+ * limited by the number of sessions supported by the crypto subsystem and
+ * is reported by odp_ipsec_capability().
+ */
+#define CONFIG_IPSEC_MAX_NUM_SA 4000
/*
- * Size of the virtual address space pre-reserver for ISHM
+ * Use 128-bit atomics for timer implementation (if available)
*
- * This is just virtual space preallocation size, not memory allocation.
- * This address space is used by ISHM to map things at a common address in
- * all ODP threads (when the _ODP_ISHM_SINGLE_VA flag is used).
- * In bytes.
+ * On some platforms 128-bit atomic operations may be available, but the
+ * implementation of used 128-bit GCC built-in functions (e.g.
+ * __atomic_compare_exchange_n) utilizes expensive locking. Set to zero to use
+ * ODP lock based implementation instead.
*/
-#define ODP_CONFIG_ISHM_VA_PREALLOC_SZ (536870912L)
+#define CONFIG_TIMER_128BIT_ATOMICS 1
+
+/* Enable timer scan performance benchmark. This works with inline enabled. */
+#define CONFIG_TIMER_PROFILE_INLINE 0
+
+/* Maximum number of ML models that can be created or loaded. */
+#define CONFIG_ML_MAX_MODELS 4
+
+/* Maximum number of inputs for a ML model. */
+#define CONFIG_ML_MAX_INPUTS 4
+
+/* Maximum number of outputs for a ML model. */
+#define CONFIG_ML_MAX_OUTPUTS 4
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h
deleted file mode 100644
index c3b70b231..000000000
--- a/platform/linux-generic/include/odp_crypto_internal.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_CRYPTO_INTERNAL_H_
-#define ODP_CRYPTO_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <openssl/evp.h>
-
-#define MAX_IV_LEN 64
-#define OP_RESULT_MAGIC 0x91919191
-
-/** Forward declaration of session structure */
-typedef struct odp_crypto_generic_session odp_crypto_generic_session_t;
-
-/**
- * Algorithm handler function prototype
- */
-typedef
-odp_crypto_alg_err_t (*crypto_func_t)(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session);
-
-/**
- * Per crypto session data structure
- */
-struct odp_crypto_generic_session {
- struct odp_crypto_generic_session *next;
-
- /* Session creation parameters */
- odp_crypto_session_param_t p;
-
- odp_bool_t do_cipher_first;
-
- struct {
- /* Copy of session IV data */
- uint8_t iv_data[MAX_IV_LEN];
- uint8_t key_data[EVP_MAX_KEY_LENGTH];
-
- const EVP_CIPHER *evp_cipher;
- crypto_func_t func;
- } cipher;
-
- struct {
- uint8_t key[EVP_MAX_KEY_LENGTH];
- uint32_t key_length;
- uint32_t bytes;
- const EVP_MD *evp_md;
- crypto_func_t func;
- } auth;
-};
-
-/**
- * Per packet operation result
- */
-typedef struct odp_crypto_generic_op_result {
- uint32_t magic;
- odp_crypto_op_result_t result;
-} odp_crypto_generic_op_result_t;
-
-/**
- * Per session creation operation result
- */
-typedef struct odp_crypto_generic_session_result {
- odp_crypto_ses_create_err_t rc;
- odp_crypto_session_t session;
-} odp_crypto_generic_session_result_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_debug_internal.h b/platform/linux-generic/include/odp_debug_internal.h
index 02ae87a90..456612aab 100644
--- a/platform/linux-generic/include/odp_debug_internal.h
+++ b/platform/linux-generic/include/odp_debug_internal.h
@@ -1,8 +1,8 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020-2022 Nokia
*/
+
/**
* @file
*
@@ -16,72 +16,58 @@
#ifndef ODP_DEBUG_INTERNAL_H_
#define ODP_DEBUG_INTERNAL_H_
+#include <odp/autoheader_external.h>
+
+#include <odp/api/debug.h>
+
+#include <odp/api/plat/debug_inlines.h>
+
#include <stdio.h>
#include <stdlib.h>
-#include <odp/api/debug.h>
-#include <odp_internal.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-/** @addtogroup odp_ver_abt_log_dbg
- * @{
- */
+#pragma GCC diagnostic push
-/**
- * Runtime assertion-macro - aborts if 'cond' is false.
- */
-#define ODP_ASSERT(cond) \
- do { if ((ODP_DEBUG == 1) && (!(cond))) { \
- ODP_ERR("%s\n", #cond); \
- odp_global_data.abort_fn(); } \
- } while (0)
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/* Debug level configure option. Zero is the highest level. Value of N prints debug messages from
+ * level 0 to N. */
+#define CONFIG_DEBUG_LEVEL 0
/**
* This macro is used to indicate when a given function is not implemented
*/
#define ODP_UNIMPLEMENTED() \
- odp_global_data.log_fn(ODP_LOG_UNIMPLEMENTED, \
+ _ODP_LOG_FN(ODP_LOG_UNIMPLEMENTED, \
"%s:%d:The function %s() is not implemented\n", \
__FILE__, __LINE__, __func__)
-/**
- * Log debug message if ODP_DEBUG_PRINT flag is set.
+
+/*
+ * Print debug message to log, if ODP_DEBUG_PRINT flag is set and CONFIG_DEBUG_LEVEL is high enough.
*/
-#define ODP_DBG(fmt, ...) \
+#define ODP_DBG_LVL(level, ...) \
do { \
- if (ODP_DEBUG_PRINT == 1) \
- ODP_LOG(ODP_LOG_DBG, fmt, ##__VA_ARGS__);\
+ if (ODP_DEBUG_PRINT == 1 && CONFIG_DEBUG_LEVEL >= (level)) \
+ __extension__ ({ \
+ _ODP_LOG(ODP_LOG_DBG, "DBG", ##__VA_ARGS__); \
+ }); \
} while (0)
-/**
- * Log error message.
- */
-#define ODP_ERR(fmt, ...) \
- ODP_LOG(ODP_LOG_ERR, fmt, ##__VA_ARGS__)
-
-/**
- * Log abort message and then stop execution (by default call abort()).
- * This function should not return.
+/*
+ * Same as ODP_DBG_LVL() but does not add file/line/function name prefix
*/
-#define ODP_ABORT(fmt, ...) \
+#define ODP_DBG_RAW(level, ...) \
do { \
- ODP_LOG(ODP_LOG_ABORT, fmt, ##__VA_ARGS__); \
- odp_global_data.abort_fn(); \
+ if (ODP_DEBUG_PRINT == 1 && CONFIG_DEBUG_LEVEL >= (level)) \
+ _ODP_LOG_FN(ODP_LOG_DBG, ##__VA_ARGS__); \
} while (0)
-/**
- * ODP LOG macro.
- */
-#define ODP_LOG(level, fmt, ...) \
- odp_global_data.log_fn(level, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__)
-
-/**
- * Log print message when the application calls one of the ODP APIs
- * specifically for dumping internal data.
- */
-#define ODP_PRINT(fmt, ...) \
- odp_global_data.log_fn(ODP_LOG_PRINT, " " fmt, ##__VA_ARGS__)
+#pragma GCC diagnostic pop
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_errno_define.h b/platform/linux-generic/include/odp_errno_define.h
index 94c30e793..7303c70c7 100644
--- a/platform/linux-generic/include/odp_errno_define.h
+++ b/platform/linux-generic/include/odp_errno_define.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
*/
/**
@@ -17,7 +15,7 @@
extern "C" {
#endif
-extern __thread int __odp_errno;
+extern __thread int _odp_errno;
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_ethtool_rss.h b/platform/linux-generic/include/odp_ethtool_rss.h
new file mode 100644
index 000000000..2dfb30a3b
--- /dev/null
+++ b/platform/linux-generic/include/odp_ethtool_rss.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_ETHTOOL_RSS_H_
+#define ODP_ETHTOOL_RSS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/packet_io.h>
+
+/**
+ * Get enabled RSS hash protocols of a packet socket
+ *
+ * @param fd Socket file descriptor
+ * @param name Interface name
+ * @param hash_proto[out] Hash protocols
+ *
+ * @returns Number enabled hash protocols
+ */
+int _odp_rss_conf_get_fd(int fd, const char *name,
+ odp_pktin_hash_proto_t *hash_proto);
+
+/**
+ * Get supported RSS hash protocols of a packet socket
+ *
+ * Can be both read and modified.
+ *
+ * @param fd Socket file descriptor
+ * @param name Interface name
+ * @param hash_proto[out] Hash protocols
+ *
+ * @returns Number of supported hash protocols
+ */
+int _odp_rss_conf_get_supported_fd(int fd, const char *name,
+ odp_pktin_hash_proto_t *hash_proto);
+
+/**
+ * Set RSS hash protocols of a packet socket
+ *
+ * @param fd Socket file descriptor
+ * @param name Interface name
+ * @param hash_proto Hash protocols
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int _odp_rss_conf_set_fd(int fd, const char *name,
+ const odp_pktin_hash_proto_t *proto);
+
+/**
+ * Print enabled RSS hash protocols
+ *
+ * @param hash_proto Hash protocols
+ */
+void _odp_rss_conf_print(const odp_pktin_hash_proto_t *hash_proto);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_ETHTOOL_RSS_H_ */
diff --git a/platform/linux-generic/include/odp_ethtool_stats.h b/platform/linux-generic/include/odp_ethtool_stats.h
new file mode 100644
index 000000000..d3de8ce88
--- /dev/null
+++ b/platform/linux-generic/include/odp_ethtool_stats.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_ETHTOOL_H_
+#define ODP_ETHTOOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+#include <odp/api/packet_io_stats.h>
+
+/**
+ * Get ethtool statistics of a packet socket
+ */
+int _odp_ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
+
+int _odp_ethtool_extra_stat_info(int fd, const char *name, odp_pktio_extra_stat_info_t info[],
+ int num);
+int _odp_ethtool_extra_stats(int fd, const char *name, uint64_t stats[], int num);
+int _odp_ethtool_extra_stat_counter(int fd, const char *name, uint32_t id, uint64_t *stat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_ETHTOOL_H_ */
diff --git a/platform/linux-generic/include/odp_event_internal.h b/platform/linux-generic/include/odp_event_internal.h
new file mode 100644
index 000000000..33e6fa927
--- /dev/null
+++ b/platform/linux-generic/include/odp_event_internal.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event descriptor - implementation internal
+ */
+
+#ifndef ODP_EVENT_INTERNAL_H_
+#define ODP_EVENT_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/debug.h>
+#include <odp/api/event.h>
+#include <odp/api/pool_types.h>
+#include <odp/api/std_types.h>
+
+#include <odp_config_internal.h>
+
+/* Combined pool and event index */
+typedef union _odp_event_index_t {
+ uint32_t u32;
+
+ struct {
+ uint32_t pool :8;
+ uint32_t event :24;
+ };
+} _odp_event_index_t;
+
+/* Check that pool index fit into bit field */
+ODP_STATIC_ASSERT(CONFIG_POOLS <= (0xFF + 1), "TOO_MANY_POOLS");
+
+/* Check that buffer index fit into bit field */
+ODP_STATIC_ASSERT(CONFIG_POOL_MAX_NUM <= (0xFFFFFF + 1), "TOO_LARGE_POOL");
+
+/* Type size limits number of flow IDs supported */
+#define BUF_HDR_MAX_FLOW_ID 255
+
+/* Common header for all event types without alignment constraints. */
+typedef struct _odp_event_hdr_t {
+ /* Initial buffer data pointer */
+ uint8_t *base_data;
+
+ /* Pool handle */
+ odp_pool_t pool;
+
+ /* --- Mostly read only data --- */
+
+ /* Initial buffer tail pointer and endmark location (if enabled) */
+ uint8_t *buf_end;
+
+ /* Combined pool and event index */
+ _odp_event_index_t index;
+
+ /* Pool type */
+ int8_t type;
+
+ /* Event type. Maybe different than pool type (crypto compl event) */
+ int8_t event_type;
+
+ /* Event subtype */
+ int8_t subtype;
+
+ /* Event flow id */
+ uint8_t flow_id;
+
+} _odp_event_hdr_t;
+
+static inline odp_event_t _odp_event_from_hdr(_odp_event_hdr_t *hdr)
+{
+ return (odp_event_t)hdr;
+}
+
+static inline _odp_event_hdr_t *_odp_event_hdr(odp_event_t event)
+{
+ return (_odp_event_hdr_t *)(uintptr_t)event;
+}
+
+static inline void _odp_event_type_set(odp_event_t event, int ev)
+{
+ _odp_event_hdr(event)->event_type = ev;
+}
+
+static inline uint64_t *_odp_event_endmark_get_ptr(odp_event_t event)
+{
+ return (uint64_t *)(uintptr_t)_odp_event_hdr(event)->buf_end;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_event_validation_internal.h b/platform/linux-generic/include/odp_event_validation_internal.h
new file mode 100644
index 000000000..06be79e7a
--- /dev/null
+++ b/platform/linux-generic/include/odp_event_validation_internal.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_EVENT_VALIDATION_INTERNAL_H_
+#define ODP_EVENT_VALIDATION_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/autoheader_external.h>
+
+#include <odp/api/event.h>
+#include <odp/api/hints.h>
+
+#include <odp/api/plat/event_validation_external.h>
+
+#include <odp_event_internal.h>
+
+#include <stdint.h>
+
+#if _ODP_EVENT_VALIDATION
+
+#define _ODP_EV_ENDMARK_VAL 0xDEADBEEFDEADBEEF
+#define _ODP_EV_ENDMARK_SIZE (sizeof(uint64_t))
+
+static inline void _odp_event_endmark_set(odp_event_t event)
+{
+ uint64_t *endmark_ptr;
+
+ endmark_ptr = _odp_event_endmark_get_ptr(event);
+ *endmark_ptr = _ODP_EV_ENDMARK_VAL;
+}
+
+#else
+
+#define _ODP_EV_ENDMARK_VAL 0
+#define _ODP_EV_ENDMARK_SIZE 0
+
+static inline void _odp_event_endmark_set(odp_event_t event ODP_UNUSED)
+{
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform/linux-generic/include/odp_event_vector_internal.h b/platform/linux-generic/include/odp_event_vector_internal.h
new file mode 100644
index 000000000..eab64dc9d
--- /dev/null
+++ b/platform/linux-generic/include/odp_event_vector_internal.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP event vector descriptor - implementation internal
+ */
+
+#ifndef ODP_EVENT_VECTOR_INTERNAL_H_
+#define ODP_EVENT_VECTOR_INTERNAL_H_
+
+#include <odp/api/align.h>
+#include <odp/api/debug.h>
+#include <odp/api/packet.h>
+
+#include <odp/api/plat/event_vector_inline_types.h>
+
+#include <odp_event_internal.h>
+
+#include <stdint.h>
+
+/**
+ * Internal event vector header
+ */
+typedef struct ODP_ALIGNED_CACHE odp_event_vector_hdr_t {
+ /* Common event header */
+ _odp_event_hdr_t event_hdr;
+
+ /* User area pointer */
+ void *uarea_addr;
+
+ /* Event vector size */
+ uint32_t size;
+
+ /* Flags */
+ _odp_event_vector_flags_t flags;
+
+ /* Vector of packet handles */
+ odp_packet_t packet[];
+
+} odp_event_vector_hdr_t;
+
+/* Vector header size is critical for performance. Ensure that it does not accidentally
+ * grow over cache line size. */
+ODP_STATIC_ASSERT(sizeof(odp_event_vector_hdr_t) <= ODP_CACHE_LINE_SIZE,
+ "EVENT_VECTOR_HDR_SIZE_ERROR");
+
+/**
+ * Return the vector header
+ */
+static inline odp_event_vector_hdr_t *_odp_packet_vector_hdr(odp_packet_vector_t pktv)
+{
+ return (odp_event_vector_hdr_t *)(uintptr_t)pktv;
+}
+
+/**
+ * Return the event header
+ */
+static inline _odp_event_hdr_t *_odp_packet_vector_to_event_hdr(odp_packet_vector_t pktv)
+{
+ return (_odp_event_hdr_t *)(uintptr_t)&_odp_packet_vector_hdr(pktv)->event_hdr;
+}
+
+/**
+ * Free packet vector and contained packets
+ */
+static inline void _odp_packet_vector_free_full(odp_packet_vector_t pktv)
+{
+ odp_event_vector_hdr_t *pktv_hdr = _odp_packet_vector_hdr(pktv);
+
+ if (pktv_hdr->size)
+ odp_packet_free_multi(pktv_hdr->packet, pktv_hdr->size);
+
+ odp_packet_vector_free(pktv);
+}
+
+#endif /* ODP_EVENT_VECTOR_INTERNAL_H_ */
diff --git a/platform/linux-generic/include/odp_fdserver_internal.h b/platform/linux-generic/include/odp_fdserver_internal.h
new file mode 100644
index 000000000..8637b53be
--- /dev/null
+++ b/platform/linux-generic/include/odp_fdserver_internal.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef _FD_SERVER_INTERNAL_H
+#define _FD_SERVER_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * the following enum defines the different contexts by which the
+ * FD server may be used: In the FD server, the keys used to store/retrieve
+ * a file descriptor are actually context based:
+ * Both the context and the key are stored at fd registration time,
+ * and both the context and the key are used to retrieve a fd.
+ * In other words a context identifies a FD server usage, so that different
+ * unrelated fd server users do not have to guarantee key unicity between
+ * them.
+ */
+typedef enum fd_server_context {
+ FD_SRV_CTX_NA, /* Not Applicable */
+ FD_SRV_CTX_ISHM,
+ FD_SRV_CTX_END, /* upper enum limit */
+} fd_server_context_e;
+
+int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
+ int fd);
+int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key);
+int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_forward_typedefs_internal.h b/platform/linux-generic/include/odp_forward_typedefs_internal.h
index f8832f777..9212281fe 100644
--- a/platform/linux-generic/include/odp_forward_typedefs_internal.h
+++ b/platform/linux-generic/include/odp_forward_typedefs_internal.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -10,7 +8,7 @@
* ODP forward typedefs - implementation internal
*
* This needs to be a separate file because it is needed by both
- * odp_queue_internal.h and odp_buffer_internal.h and clang prohibits forward
+ * odp_queue_internal.h and odp_queue_lf.h and clang prohibits forward
* "redefining" typedefs. Note that this file can be extended with additional
* forward typedefs as needed.
*/
@@ -22,8 +20,7 @@
extern "C" {
#endif
-typedef struct odp_buffer_hdr_t odp_buffer_hdr_t;
-typedef union queue_entry_u queue_entry_t;
+typedef struct queue_entry_s queue_entry_t;
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_global_data.h b/platform/linux-generic/include/odp_global_data.h
new file mode 100644
index 000000000..e532cf0e7
--- /dev/null
+++ b/platform/linux-generic/include/odp_global_data.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_GLOBAL_DATA_H_
+#define ODP_GLOBAL_DATA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/cpumask.h>
+#include <odp/api/init.h>
+#include <odp/api/random.h>
+#include <odp/api/system_info.h>
+#include <odp/api/std_types.h>
+
+#include <odp_config_internal.h>
+
+#include <libconfig.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define MODEL_STR_SIZE 128
+#define UID_MAXLEN 30
+
+typedef struct {
+ uint64_t cpu_hz_max[CONFIG_NUM_CPU_IDS];
+ uint64_t cpu_hz[CONFIG_NUM_CPU_IDS];
+ uint64_t default_cpu_hz_max;
+ uint64_t default_cpu_hz;
+ uint64_t page_size;
+ int cache_line_size;
+ uint8_t cpu_hz_static;
+ uint8_t cpu_constant_tsc;
+ odp_cpu_arch_t cpu_arch;
+ odp_cpu_arch_isa_t cpu_isa_sw;
+ odp_cpu_arch_isa_t cpu_isa_hw;
+ char cpu_arch_str[128];
+ char model_str[CONFIG_NUM_CPU_IDS][MODEL_STR_SIZE];
+} system_info_t;
+
+typedef struct {
+ uint64_t default_huge_page_size;
+ char *default_huge_page_dir;
+} hugepage_info_t;
+
+/* Read-only global data. Members should not be modified after global init
+ * to enable process more support. */
+typedef struct odp_global_data_ro_t {
+ odp_init_t init_param;
+ /* directory for odp mapped files */
+ char *shm_dir;
+ /* overload default with env */
+ int shm_dir_from_env;
+ uint64_t shm_max_memory;
+ uint64_t shm_max_size;
+ int shm_single_va;
+ pid_t main_pid;
+ pid_t fdserver_pid;
+ char uid[UID_MAXLEN];
+ system_info_t system_info;
+ hugepage_info_t hugepage_info;
+ odp_cpumask_t all_cpus;
+ odp_cpumask_t control_cpus;
+ odp_cpumask_t worker_cpus;
+ int num_cpus_installed;
+ uint8_t has_config_rt;
+ config_t libconfig_default;
+ config_t libconfig_runtime;
+
+ /* Disabled features during global init */
+ struct {
+ uint8_t compress;
+ uint8_t crypto;
+ uint8_t dma;
+ uint8_t ipsec;
+ uint8_t stash;
+ uint8_t traffic_mngr;
+ uint8_t ml;
+
+ } disable;
+
+} odp_global_data_ro_t;
+
+/* Modifiable global data. Memory region is shared and synchronized amongst all
+ * worker processes. */
+typedef struct odp_global_data_rw_t {
+ odp_bool_t dpdk_initialized;
+ odp_bool_t inline_timers;
+ odp_bool_t schedule_configured;
+
+} odp_global_data_rw_t;
+
+extern odp_global_data_ro_t odp_global_ro;
+extern odp_global_data_rw_t *odp_global_rw;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_init_internal.h b/platform/linux-generic/include/odp_init_internal.h
new file mode 100644
index 000000000..4ee80114a
--- /dev/null
+++ b/platform/linux-generic/include/odp_init_internal.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+#ifndef ODP_INIT_INTERNAL_H_
+#define ODP_INIT_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/init.h>
+#include <odp/api/thread.h>
+
+int _odp_cpumask_init_global(const odp_init_t *params);
+int _odp_cpumask_term_global(void);
+
+int _odp_system_info_init(void);
+int _odp_system_info_term(void);
+
+int _odp_thread_init_global(void);
+int _odp_thread_init_local(odp_thread_type_t type);
+int _odp_thread_term_local(void);
+int _odp_thread_term_global(void);
+
+int _odp_pcapng_init_global(void);
+int _odp_pcapng_term_global(void);
+
+int _odp_pool_init_global(void);
+int _odp_pool_init_local(void);
+int _odp_pool_term_global(void);
+int _odp_pool_term_local(void);
+
+int _odp_event_validation_init_global(void);
+int _odp_event_validation_term_global(void);
+
+int _odp_queue_init_global(void);
+int _odp_queue_term_global(void);
+
+int _odp_schedule_init_global(void);
+int _odp_schedule_term_global(void);
+
+int _odp_pktio_init_global(void);
+int _odp_pktio_term_global(void);
+int _odp_pktio_init_local(void);
+
+int _odp_classification_init_global(void);
+int _odp_classification_term_global(void);
+
+int _odp_queue_init_global(void);
+int _odp_queue_term_global(void);
+
+int _odp_random_init_local(void);
+int _odp_random_term_local(void);
+
+int _odp_crypto_init_global(void);
+int _odp_crypto_term_global(void);
+int _odp_crypto_init_local(void);
+int _odp_crypto_term_local(void);
+
+int _odp_comp_init_global(void);
+int _odp_comp_term_global(void);
+
+int _odp_timer_init_global(const odp_init_t *params);
+int _odp_timer_init_local(void);
+int _odp_timer_term_global(void);
+int _odp_timer_term_local(void);
+
+int _odp_time_init_global(void);
+int _odp_time_term_global(void);
+
+int _odp_tm_init_global(void);
+int _odp_tm_term_global(void);
+
+int _odp_int_name_tbl_init_global(void);
+int _odp_int_name_tbl_term_global(void);
+
+int _odp_fdserver_init_global(void);
+int _odp_fdserver_term_global(void);
+
+int _odp_ishm_init_global(const odp_init_t *init);
+int _odp_ishm_init_local(void);
+int _odp_ishm_term_global(void);
+int _odp_ishm_term_local(void);
+
+int _odp_ipsec_init_global(void);
+int _odp_ipsec_term_global(void);
+
+int _odp_ipsec_sad_init_global(void);
+int _odp_ipsec_sad_term_global(void);
+
+int _odp_ipsec_events_init_global(void);
+int _odp_ipsec_events_term_global(void);
+
+int _odp_cpu_cycles_init_global(void);
+
+int _odp_hash_init_global(void);
+int _odp_hash_term_global(void);
+
+int _odp_stash_init_global(void);
+int _odp_stash_term_global(void);
+
+int _odp_dma_init_global(void);
+int _odp_dma_term_global(void);
+
+int _odp_ml_init_global(void);
+int _odp_ml_term_global(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
deleted file mode 100644
index 355e25f73..000000000
--- a/platform/linux-generic/include/odp_internal.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP HW system information
- */
-
-#ifndef ODP_INTERNAL_H_
-#define ODP_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/init.h>
-#include <odp/api/cpumask.h>
-#include <odp/api/thread.h>
-#include <odp_errno_define.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-#define MAX_CPU_NUMBER 128
-#define UID_MAXLEN 30
-
-typedef struct {
- uint64_t cpu_hz_max[MAX_CPU_NUMBER];
- uint64_t page_size;
- int cache_line_size;
- int cpu_count;
- char cpu_arch_str[128];
- char model_str[MAX_CPU_NUMBER][128];
-} system_info_t;
-
-typedef struct {
- uint64_t default_huge_page_size;
- char *default_huge_page_dir;
-} hugepage_info_t;
-
-struct odp_global_data_s {
- char *shm_dir; /*< directory for odp mmaped files */
- int shm_dir_from_env; /*< overload default with env */
- pid_t main_pid;
- char uid[UID_MAXLEN];
- odp_log_func_t log_fn;
- odp_abort_func_t abort_fn;
- system_info_t system_info;
- hugepage_info_t hugepage_info;
- odp_cpumask_t control_cpus;
- odp_cpumask_t worker_cpus;
- int num_cpus_installed;
-};
-
-enum init_stage {
- NO_INIT = 0, /* No init stages completed */
- CPUMASK_INIT,
- TIME_INIT,
- SYSINFO_INIT,
- ISHM_INIT,
- FDSERVER_INIT,
- THREAD_INIT,
- POOL_INIT,
- QUEUE_INIT,
- SCHED_INIT,
- PKTIO_INIT,
- TIMER_INIT,
- CRYPTO_INIT,
- CLASSIFICATION_INIT,
- TRAFFIC_MNGR_INIT,
- NAME_TABLE_INIT,
- ALL_INIT /* All init stages completed */
-};
-
-extern struct odp_global_data_s odp_global_data;
-
-int _odp_term_global(enum init_stage stage);
-int _odp_term_local(enum init_stage stage);
-
-int odp_cpumask_init_global(const odp_init_t *params);
-int odp_cpumask_term_global(void);
-
-int odp_system_info_init(void);
-int odp_system_info_term(void);
-
-int odp_thread_init_global(void);
-int odp_thread_init_local(odp_thread_type_t type);
-int odp_thread_term_local(void);
-int odp_thread_term_global(void);
-
-int odp_pool_init_global(void);
-int odp_pool_init_local(void);
-int odp_pool_term_global(void);
-int odp_pool_term_local(void);
-
-int odp_pktio_init_global(void);
-int odp_pktio_term_global(void);
-int odp_pktio_init_local(void);
-
-int odp_classification_init_global(void);
-int odp_classification_term_global(void);
-
-int odp_queue_init_global(void);
-int odp_queue_term_global(void);
-
-int odp_crypto_init_global(void);
-int odp_crypto_term_global(void);
-
-int odp_timer_init_global(void);
-int odp_timer_term_global(void);
-int odp_timer_disarm_all(void);
-
-int odp_time_init_global(void);
-int odp_time_term_global(void);
-
-int odp_tm_init_global(void);
-int odp_tm_term_global(void);
-
-int _odp_int_name_tbl_init_global(void);
-int _odp_int_name_tbl_term_global(void);
-
-int _odp_fdserver_init_global(void);
-int _odp_fdserver_term_global(void);
-
-int _odp_ishm_init_global(void);
-int _odp_ishm_init_local(void);
-int _odp_ishm_term_global(void);
-int _odp_ishm_term_local(void);
-
-int cpuinfo_parser(FILE *file, system_info_t *sysinfo);
-uint64_t odp_cpu_hz_current(int id);
-void sys_info_print_arch(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h
new file mode 100644
index 000000000..030cb6bf3
--- /dev/null
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2018-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP internal IPsec routines
+ */
+
+#ifndef ODP_IPSEC_INTERNAL_H_
+#define ODP_IPSEC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/atomic.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/event.h>
+#include <odp/api/ipsec.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/std_types.h>
+
+#include <odp/api/plat/strong_types.h>
+
+#include <protocols/ip.h>
+#include <stdint.h>
+
+/** @addtogroup odp_ipsec
+ * @{
+ */
+
+typedef ODP_HANDLE_T(ipsec_status_t);
+
+#define ODP_IPSEC_STATUS_INVALID \
+ _odp_cast_scalar(ipsec_status_t, 0xffffffff)
+
+typedef struct ipsec_sa_s ipsec_sa_t;
+
+/**
+ * @internal Get ipsec_status handle from event
+ *
+ * Converts an ODP_EVENT_IPSEC_STATUS type event to an IPsec status event.
+ *
+ * @param ev Event handle
+ *
+ * @return IPsec status handle
+ *
+ * @see odp_event_type()
+ */
+ipsec_status_t _odp_ipsec_status_from_event(odp_event_t ev);
+
+/**
+ * @internal Free IPsec status event
+ *
+ * Frees the ipsec_status into the ipsec_status pool it was allocated from.
+ *
+ * @param res IPsec status handle
+ */
+void _odp_ipsec_status_free(ipsec_status_t status);
+
+/**
+ * @internal Send ODP_IPSEC_STATUS event
+ *
+ * Sends the ipsec_status event using provided information
+ *
+ * @param queue destination queue
+ * @param id status id
+ * @param sa SA respective to the operation
+ * @param result status value
+ * @param warn generated warning
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int _odp_ipsec_status_send(odp_queue_t queue,
+ odp_ipsec_status_id_t id,
+ odp_ipsec_sa_t sa,
+ int result,
+ odp_ipsec_warn_t warn);
+
+#define IPSEC_MAX_IV_LEN 16 /**< Maximum cipher IV length in bytes */
+
+#define IPSEC_MAX_SALT_LEN 4 /**< Maximum salt length in bytes */
+
+#define CBC_SALT_LEN 8
+#define CBC_IV_LEN (CBC_SALT_LEN + sizeof(uint64_t))
+
+#define IPSEC_SEQ_HI_LEN 4 /**< ESN Higher bits length in bytes */
+
+/* The minimum supported AR window size */
+#define IPSEC_AR_WIN_SIZE_MIN 32
+
+/* The maximum supported AR window size */
+#define IPSEC_AR_WIN_SIZE_MAX 4096
+
+/* For a 64-bit bucket size */
+#define IPSEC_AR_WIN_BUCKET_BITS 6
+#define IPSEC_AR_WIN_BUCKET_SIZE (1 << IPSEC_AR_WIN_BUCKET_BITS)
+#define IPSEC_AR_WIN_BITLOC_MASK (IPSEC_AR_WIN_BUCKET_SIZE - 1)
+
+/*
+ * We need one extra bucket in addition to the buckets that contain
+ * part of the window.
+ */
+#define IPSEC_AR_WIN_NUM_BUCKETS(window_size) \
+ (((window_size) - 1) / IPSEC_AR_WIN_BUCKET_SIZE + 2)
+
+/* Maximum number of buckets */
+#define IPSEC_AR_WIN_BUCKET_MAX \
+ IPSEC_AR_WIN_NUM_BUCKETS(IPSEC_AR_WIN_SIZE_MAX)
+
+struct ipsec_sa_s {
+ odp_atomic_u32_t state ODP_ALIGNED_CACHE;
+
+ /*
+ * State that gets updated very frequently. Grouped separately
+ * to avoid false cache line sharing with other data.
+ */
+ struct ODP_ALIGNED_CACHE {
+ /* Statistics for soft/hard expiration */
+ odp_atomic_u64_t bytes;
+ odp_atomic_u64_t packets;
+
+ union {
+ struct {
+ /* AR window lock */
+ odp_spinlock_t lock;
+
+ /* AR window top sequence number */
+ odp_atomic_u64_t wintop_seq;
+
+ /* AR window bucket array */
+ uint64_t bucket_arr[IPSEC_AR_WIN_BUCKET_MAX];
+ } in;
+
+ struct {
+ /*
+ * 64-bit sequence number that is also used as
+ * CTR/GCM IV
+ */
+ odp_atomic_u64_t seq;
+ } out;
+ };
+ } hot;
+
+ uint32_t ipsec_sa_idx;
+ odp_ipsec_sa_t ipsec_sa_hdl;
+
+ odp_ipsec_protocol_t proto;
+ uint32_t spi;
+
+ odp_ipsec_mode_t mode;
+
+ /* Limits */
+ uint64_t soft_limit_bytes;
+ uint64_t soft_limit_packets;
+ uint64_t hard_limit_bytes;
+ uint64_t hard_limit_packets;
+
+ odp_crypto_session_t session;
+ void *context;
+ odp_queue_t queue;
+
+ uint32_t icv_len;
+ uint32_t esp_iv_len;
+ uint32_t esp_pad_mask;
+
+ union {
+ uint8_t salt[IPSEC_MAX_SALT_LEN];
+ uint8_t cbc_salt[CBC_SALT_LEN];
+ };
+ uint32_t salt_length;
+ odp_ipsec_lookup_mode_t lookup_mode;
+
+ union {
+ unsigned flags;
+ struct {
+ unsigned inbound : 1;
+ unsigned dec_ttl : 1;
+ unsigned copy_dscp : 1;
+ unsigned copy_df : 1;
+ unsigned copy_flabel : 1;
+ unsigned aes_ctr_iv : 1;
+ unsigned udp_encap : 1;
+ unsigned esn : 1;
+ unsigned insert_seq_hi : 1;
+
+ /* Only for outbound */
+ unsigned use_counter_iv : 1;
+ unsigned use_cbc_iv : 1;
+ unsigned tun_ipv4 : 1;
+
+ /* Only for inbound */
+ unsigned antireplay : 1;
+ };
+ };
+
+ union {
+ struct {
+ odp_ipsec_ip_version_t lookup_ver;
+
+ /* Anti-replay window management. */
+ struct {
+ /* Number of buckets for AR window */
+ uint16_t num_buckets;
+
+ /* AR window size */
+ uint32_t win_size;
+ } ar;
+
+ union {
+ odp_u32be_t lookup_dst_ipv4;
+ uint8_t lookup_dst_ipv6[_ODP_IPV6ADDR_LEN];
+ };
+ } in;
+
+ struct {
+ odp_ipsec_frag_mode_t frag_mode;
+ odp_atomic_u32_t mtu;
+
+ union {
+ struct {
+ odp_ipsec_ipv4_param_t param;
+ odp_u32be_t src_ip;
+ odp_u32be_t dst_ip;
+ } tun_ipv4;
+ struct {
+ odp_ipsec_ipv6_param_t param;
+ uint8_t src_ip[_ODP_IPV6ADDR_LEN];
+ uint8_t dst_ip[_ODP_IPV6ADDR_LEN];
+ } tun_ipv6;
+ };
+ } out;
+ };
+
+ struct {
+ odp_atomic_u64_t proto_err;
+ odp_atomic_u64_t auth_err;
+ odp_atomic_u64_t antireplay_err;
+ odp_atomic_u64_t alg_err;
+ odp_atomic_u64_t mtu_err;
+ odp_atomic_u64_t hard_exp_bytes_err;
+ odp_atomic_u64_t hard_exp_pkts_err;
+
+ /*
+ * Track error packets and bytes after lifetime check is done.
+ * Required since, the stats tracking lifetime is being
+ * used for SA success packets stats.
+ */
+ odp_atomic_u64_t post_lifetime_err_pkts;
+ odp_atomic_u64_t post_lifetime_err_bytes;
+ } stats;
+
+ uint32_t next_sa;
+
+ /* Data stored solely for odp_ipsec_sa_info() */
+ struct {
+ odp_cipher_alg_t cipher_alg;
+ uint32_t cipher_key_len;
+ uint32_t cipher_key_extra_len;
+
+ odp_auth_alg_t auth_alg;
+ uint32_t auth_key_len;
+ uint32_t auth_key_extra_len;
+
+ uint32_t icv_len;
+ uint32_t context_len;
+ union {
+ struct {
+ uint32_t antireplay_ws;
+ } in;
+ struct{
+ uint32_t mtu;
+ } out;
+ };
+ } sa_info;
+
+ /*
+ * Flag to check if the SA soft expiry status event was already
+ * sent. This field is applicable only for the soft expiry status
+ * event that gets generated for IPsec SAs configured in inline
+ * outbound mode.
+ */
+ odp_atomic_u32_t soft_expiry_notified;
+};
+
+/**
+ * IPSEC Security Association (SA) lookup parameters
+ */
+typedef struct odp_ipsec_sa_lookup_s {
+ /** IPSEC protocol: ESP or AH */
+ odp_ipsec_protocol_t proto;
+
+ /** SPI value */
+ uint32_t spi;
+
+ /** IP protocol version */
+ odp_ipsec_ip_version_t ver;
+
+ /** IP destination address (NETWORK ENDIAN) */
+ void *dst_addr;
+} ipsec_sa_lookup_t;
+
+/** IPSEC AAD */
+typedef struct ODP_PACKED {
+ /**< Security Parameter Index */
+ odp_u32be_t spi;
+
+ /**< Sequence Number */
+ union {
+ odp_u32be_t seq_no;
+ odp_u64be_t seq_no64;
+ };
+} ipsec_aad_t;
+
+/* Return IV length required for the cipher for IPsec use */
+uint32_t _odp_ipsec_cipher_iv_len(odp_cipher_alg_t cipher);
+
+/* Return digest length required for the cipher for IPsec use */
+uint32_t _odp_ipsec_auth_digest_len(odp_auth_alg_t auth);
+
+/* Return the maximum number of SAs supported by the implementation */
+uint32_t _odp_ipsec_max_num_sa(void);
+
+/*
+ * Get SA entry from handle without obtaining a reference
+ */
+ipsec_sa_t *_odp_ipsec_sa_entry_from_hdl(odp_ipsec_sa_t sa);
+
+/**
+ * Obtain SA reference
+ */
+ipsec_sa_t *_odp_ipsec_sa_use(odp_ipsec_sa_t sa);
+
+/**
+ * Release SA reference
+ */
+void _odp_ipsec_sa_unuse(ipsec_sa_t *ipsec_sa);
+
+/**
+ * Lookup SA corresponding to inbound packet pkt
+ */
+ipsec_sa_t *_odp_ipsec_sa_lookup(const ipsec_sa_lookup_t *lookup);
+
+/**
+ * Run pre-check on SA usage statistics.
+ *
+ * @retval <0 if hard limits were breached
+ */
+int _odp_ipsec_sa_stats_precheck(ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status);
+
+/**
+ * Update SA lifetime counters, filling respective status for the packet.
+ *
+ * @retval <0 if hard limits were breached
+ */
+int _odp_ipsec_sa_lifetime_update(ipsec_sa_t *ipsec_sa, uint32_t len,
+ odp_ipsec_op_status_t *status);
+
+/* Run pre-check on sequence number of the packet.
+ *
+ * @retval <0 if the packet falls out of window
+ */
+int _odp_ipsec_sa_replay_precheck(ipsec_sa_t *ipsec_sa, uint64_t seq,
+ odp_ipsec_op_status_t *status);
+
+/* Run check on sequence number of the packet and update window if necessary.
+ *
+ * @retval <0 if the packet falls out of window
+ */
+int _odp_ipsec_sa_replay_update(ipsec_sa_t *ipsec_sa, uint64_t seq,
+ odp_ipsec_op_status_t *status);
+
+/**
+ * Allocate an IPv4 ID for an outgoing packet.
+ */
+uint16_t _odp_ipsec_sa_alloc_ipv4_id(ipsec_sa_t *ipsec_sa);
+
+/**
+ * Try inline IPsec processing of provided packet.
+ *
+ * @retval 0 if packet was processed and will be queue using IPsec inline
+ * processing
+ */
+int _odp_ipsec_try_inline(odp_packet_t *pkt);
+
+/**
+ * Populate number of packets and bytes of data successfully processed by the SA
+ * in the odp_ipsec_stats_t structure passed.
+ *
+ */
+void _odp_ipsec_sa_stats_pkts(ipsec_sa_t *sa, odp_ipsec_stats_t *stats);
+
+/**
+ * Return true if IPsec operates in sync mode in the given direction.
+ */
+odp_bool_t _odp_ipsec_is_sync_mode(odp_ipsec_dir_t dir);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ishmphy_internal.h b/platform/linux-generic/include/odp_ishmphy_internal.h
new file mode 100644
index 000000000..89178bd4e
--- /dev/null
+++ b/platform/linux-generic/include/odp_ishmphy_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef _ISHMPHY_INTERNAL_H
+#define _ISHMPHY_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+void *_odp_ishmphy_reserve_single_va(uint64_t len, int fd);
+int _odp_ishmphy_free_single_va(void);
+void *_odp_ishmphy_map(int fd, uint64_t size, uint64_t offset, int flags);
+int _odp_ishmphy_unmap(void *start, uint64_t len, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ishmpool_internal.h b/platform/linux-generic/include/odp_ishmpool_internal.h
new file mode 100644
index 000000000..4c1e071f5
--- /dev/null
+++ b/platform/linux-generic/include/odp_ishmpool_internal.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_ISHMBUDDY_INTERNAL_H_
+#define ODP_ISHMBUDDY_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <odp/api/spinlock.h>
+
+typedef struct _odp_ishm_pool_ctrl_t {
+ uint32_t element_sz; /* 0 for buddy pools, >0 for slab. */
+ int ishm_blk_idx; /* the block index returned by _ishm_resrve()*/
+ odp_spinlock_t lock; /* for pool access mutex */
+ void *user_addr; /* user pool area ('real user pool') */
+ union {
+ struct { /* things needed for buddy pools: */
+ uint8_t order; /* pool is 2^order bytes long */
+ uint8_t min_order; /*alloc won't go below 2^min_order*/
+ void **free_heads; /* 'order' free list heads. */
+ uint8_t *alloced_order; /* size of blocks, 0=free */
+ };
+ struct { /* things needed for slab pools: */
+ void *free_head; /* free element list head */
+ uint64_t nb_elem;/* total number of elements in pool */
+ };
+ };
+} _odp_ishm_pool_ctrl_t;
+
+typedef struct _odp_ishm_pool_t {
+ _odp_ishm_pool_ctrl_t ctrl; /* control part */
+ uint8_t mem[1]; /* area for heads, saved alloc'd orders, data*/
+} _odp_ishm_pool_t;
+
+_odp_ishm_pool_t *_odp_ishm_pool_create(const char *pool_name,
+ uint64_t size,
+ uint64_t min_alloc,
+ uint64_t max_alloc, int flags);
+int _odp_ishm_pool_destroy(_odp_ishm_pool_t *pool);
+void *_odp_ishm_pool_alloc(_odp_ishm_pool_t *pool, uint64_t size);
+int _odp_ishm_pool_free(_odp_ishm_pool_t *pool, void *addr);
+void _odp_ishm_pool_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_libconfig_internal.h b/platform/linux-generic/include/odp_libconfig_internal.h
new file mode 100644
index 000000000..98320d823
--- /dev/null
+++ b/platform/linux-generic/include/odp_libconfig_internal.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * Common libconfig functions
+ */
+
+#ifndef ODP_LIBCONFIG_INTERNAL_H_
+#define ODP_LIBCONFIG_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int _odp_libconfig_init_global(void);
+int _odp_libconfig_term_global(void);
+
+int _odp_libconfig_lookup_int(const char *path, int *value);
+int _odp_libconfig_lookup_str(const char *path, char *value,
+ unsigned int str_size);
+int _odp_libconfig_lookup_array(const char *path, int value[], int max_num);
+int _odp_libconfig_lookup_array_str(const char *path, char **value,
+ int max_count, unsigned int max_str);
+
+int _odp_libconfig_lookup_ext_int(const char *base_path,
+ const char *local_path,
+ const char *name,
+ int *value);
+
+int _odp_libconfig_print(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_llqueue.h b/platform/linux-generic/include/odp_llqueue.h
new file mode 100644
index 000000000..40e2b2dda
--- /dev/null
+++ b/platform/linux-generic/include/odp_llqueue.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_LLQUEUE_H_
+#define ODP_LLQUEUE_H_
+
+#include <odp/api/cpu.h>
+#include <odp/api/hints.h>
+#include <odp/api/spinlock.h>
+
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_types_internal.h>
+#include <odp_cpu.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/******************************************************************************
+ * Linked list queues
+ *****************************************************************************/
+
+struct llqueue;
+struct llnode;
+
+static struct llnode *llq_head(struct llqueue *llq);
+static void llqueue_init(struct llqueue *llq);
+static void llq_enqueue(struct llqueue *llq, struct llnode *node);
+static struct llnode *llq_dequeue(struct llqueue *llq);
+static odp_bool_t llq_dequeue_cond(struct llqueue *llq, struct llnode *exp);
+static odp_bool_t llq_cond_rotate(struct llqueue *llq, struct llnode *node);
+static odp_bool_t llq_on_queue(struct llnode *node);
+
+/******************************************************************************
+ * The implementation(s)
+ *****************************************************************************/
+
+#define SENTINEL ((void *)~(uintptr_t)0)
+#define MAX_SPIN_COUNT 1000
+
+#ifdef CONFIG_LLDSCD
+/* Implement queue operations using double-word LL/SC */
+
+/* The scalar equivalent of a double pointer */
+#if __SIZEOF_PTRDIFF_T__ == 4
+typedef uint64_t dintptr_t;
+#endif
+#if __SIZEOF_PTRDIFF_T__ == 8
+typedef _odp_u128_t dintptr_t;
+#endif
+
+struct llnode {
+ struct llnode *next;
+};
+
+union llht {
+ struct {
+ struct llnode *head, *tail;
+ } st;
+ dintptr_t ui;
+};
+
+struct llqueue {
+ union llht u;
+};
+
+static inline struct llnode *llq_head(struct llqueue *llq)
+{
+ return __atomic_load_n(&llq->u.st.head, __ATOMIC_RELAXED);
+}
+
+static inline void llqueue_init(struct llqueue *llq)
+{
+ llq->u.st.head = NULL;
+ llq->u.st.tail = NULL;
+}
+
+static inline void llq_enqueue(struct llqueue *llq, struct llnode *node)
+{
+ union llht old, neu;
+
+ _ODP_ASSERT(node->next == NULL);
+ node->next = SENTINEL;
+ do {
+ old.ui = lld(&llq->u.ui, __ATOMIC_RELAXED);
+ neu.st.head = old.st.head == NULL ? node : old.st.head;
+ neu.st.tail = node;
+ } while (odp_unlikely(scd(&llq->u.ui, neu.ui, __ATOMIC_RELEASE)));
+ if (old.st.tail != NULL) {
+ /* List was not empty */
+ _ODP_ASSERT(old.st.tail->next == SENTINEL);
+ old.st.tail->next = node;
+ }
+}
+
+static inline struct llnode *llq_dequeue(struct llqueue *llq)
+{
+ struct llnode *head;
+ union llht old, neu;
+
+ /* llq_dequeue() may be used in a busy-waiting fashion
+ * Read head using plain load to avoid disturbing remote LL/SC
+ */
+ head = __atomic_load_n(&llq->u.st.head, __ATOMIC_ACQUIRE);
+ if (head == NULL)
+ return NULL;
+ /* Read head->next before LL to minimize cache miss latency
+ * in LL/SC below
+ */
+ (void)__atomic_load_n(&head->next, __ATOMIC_RELAXED);
+
+ do {
+restart_loop:
+ old.ui = lld(&llq->u.ui, __ATOMIC_RELAXED);
+ if (odp_unlikely(old.st.head == NULL)) {
+ /* Empty list */
+ return NULL;
+ } else if (odp_unlikely(old.st.head == old.st.tail)) {
+ /* Single-element in list */
+ neu.st.head = NULL;
+ neu.st.tail = NULL;
+ } else {
+ /* Multi-element list, dequeue head */
+ struct llnode *next;
+ int spin_count = 0;
+
+ /* Wait until llq_enqueue() has written true next
+ * pointer
+ */
+ while ((next = __atomic_load_n(&old.st.head->next,
+ __ATOMIC_RELAXED)) ==
+ SENTINEL) {
+ odp_cpu_pause();
+ if (++spin_count >= MAX_SPIN_COUNT)
+ goto restart_loop;
+ }
+ neu.st.head = next;
+ neu.st.tail = old.st.tail;
+ }
+ } while (odp_unlikely(scd(&llq->u.ui, neu.ui, __ATOMIC_RELAXED)));
+ old.st.head->next = NULL;
+ return old.st.head;
+}
+
+static inline odp_bool_t llq_dequeue_cond(struct llqueue *llq,
+ struct llnode *exp)
+{
+ union llht old, neu;
+
+ do {
+restart_loop:
+ old.ui = lld(&llq->u.ui, __ATOMIC_ACQUIRE);
+ if (odp_unlikely(old.st.head == NULL || old.st.head != exp)) {
+ /* Empty list or wrong head */
+ return false;
+ } else if (odp_unlikely(old.st.head == old.st.tail)) {
+ /* Single-element in list */
+ neu.st.head = NULL;
+ neu.st.tail = NULL;
+ } else {
+ /* Multi-element list, dequeue head */
+ struct llnode *next;
+ int spin_count = 0;
+
+ /* Wait until llq_enqueue() has written true next
+ * pointer */
+ while ((next = __atomic_load_n(&old.st.head->next,
+ __ATOMIC_RELAXED)) ==
+ SENTINEL) {
+ odp_cpu_pause();
+ if (++spin_count >= MAX_SPIN_COUNT)
+ goto restart_loop;
+ }
+
+ neu.st.head = next;
+ neu.st.tail = old.st.tail;
+ }
+ } while (odp_unlikely(scd(&llq->u.ui, neu.ui, __ATOMIC_RELAXED)));
+ old.st.head->next = NULL;
+ return true;
+}
+
+/* If 'node' is a head of llq then move it to tail */
+static inline odp_bool_t llq_cond_rotate(struct llqueue *llq,
+ struct llnode *node)
+{
+ /* Difficult to make this into a single atomic operation
+ * Instead use existing primitives.
+ */
+ if (odp_likely(llq_dequeue_cond(llq, node))) {
+ llq_enqueue(llq, node);
+ return true;
+ }
+ return false;
+}
+
+static inline odp_bool_t llq_on_queue(struct llnode *node)
+{
+ return node->next != NULL;
+}
+
+#else
+/* Implement queue operations protected by a spin lock */
+
+struct llnode {
+ struct llnode *next;
+};
+
+struct llqueue {
+ struct llnode *head, *tail;
+ odp_spinlock_t lock;
+};
+
+static inline struct llnode *llq_head(struct llqueue *llq)
+{
+ return __atomic_load_n(&llq->head, __ATOMIC_RELAXED);
+}
+
+static inline void llqueue_init(struct llqueue *llq)
+{
+ llq->head = NULL;
+ llq->tail = NULL;
+ odp_spinlock_init(&llq->lock);
+}
+
+static inline void llq_enqueue(struct llqueue *llq, struct llnode *node)
+{
+ _ODP_ASSERT(node->next == NULL);
+ node->next = SENTINEL;
+
+ odp_spinlock_lock(&llq->lock);
+ if (llq->head == NULL) {
+ llq->head = node;
+ llq->tail = node;
+ } else {
+ llq->tail->next = node;
+ llq->tail = node;
+ }
+ odp_spinlock_unlock(&llq->lock);
+}
+
+static inline struct llnode *llq_dequeue(struct llqueue *llq)
+{
+ struct llnode *head;
+ struct llnode *node = NULL;
+
+ head = __atomic_load_n(&llq->head, __ATOMIC_RELAXED);
+ if (head == NULL)
+ return NULL;
+
+ odp_spinlock_lock(&llq->lock);
+ if (llq->head != NULL) {
+ node = llq->head;
+ if (llq->head == llq->tail) {
+ _ODP_ASSERT(node->next == SENTINEL);
+ llq->head = NULL;
+ llq->tail = NULL;
+ } else {
+ _ODP_ASSERT(node->next != SENTINEL);
+ llq->head = node->next;
+ }
+ node->next = NULL;
+ }
+ odp_spinlock_unlock(&llq->lock);
+ return node;
+}
+
+static inline odp_bool_t llq_dequeue_cond(struct llqueue *llq,
+ struct llnode *node)
+{
+ odp_bool_t success = false;
+
+ odp_spinlock_lock(&llq->lock);
+ if (odp_likely(llq->head != NULL && llq->head == node)) {
+ success = true;
+ if (llq->head == llq->tail) {
+ _ODP_ASSERT(node->next == SENTINEL);
+ llq->head = NULL;
+ llq->tail = NULL;
+ } else {
+ _ODP_ASSERT(node->next != SENTINEL);
+ llq->head = node->next;
+ }
+ node->next = NULL;
+ }
+ odp_spinlock_unlock(&llq->lock);
+ return success;
+}
+
+/* If 'node' is a head of llq then move it to tail */
+static inline odp_bool_t llq_cond_rotate(struct llqueue *llq,
+ struct llnode *node)
+{
+ odp_bool_t success = false;
+
+ odp_spinlock_lock(&llq->lock);
+ if (odp_likely(llq->head == node)) {
+ success = true;
+ if (llq->tail != node) {
+ _ODP_ASSERT(node->next != SENTINEL);
+ llq->head = node->next;
+ llq->tail->next = node;
+ llq->tail = node;
+ node->next = SENTINEL;
+ }
+ /* Else 'node' is only element on list => nothing to do */
+ }
+ odp_spinlock_unlock(&llq->lock);
+ return success;
+}
+
+static inline odp_bool_t llq_on_queue(struct llnode *node)
+{
+ return node->next != NULL;
+}
+
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_macros_internal.h b/platform/linux-generic/include/odp_macros_internal.h
new file mode 100644
index 000000000..88251d627
--- /dev/null
+++ b/platform/linux-generic/include/odp_macros_internal.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2022-2024 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP miscellaneous macros
+ */
+
+#ifndef ODP_MACROS_INTERNAL_H_
+#define ODP_MACROS_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/align.h>
+
+#include <stdint.h>
+
+#define _ODP_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define _ODP_MIN(a, b) \
+ __extension__ ({ \
+ __typeof__(a) min_a = (a); \
+ __typeof__(b) min_b = (b); \
+ min_a < min_b ? min_a : min_b; \
+ })
+
+#define _ODP_MAX(a, b) \
+ __extension__ ({ \
+ __typeof__(a) max_a = (a); \
+ __typeof__(b) max_b = (b); \
+ max_a > max_b ? max_a : max_b; \
+ })
+
+#define _ODP_MIN3(a, b, c) \
+__extension__ ({ \
+ __typeof__(a) min3_a = (a); \
+ __typeof__(b) min3_b = (b); \
+ __typeof__(c) min3_c = (c); \
+ (min3_a < min3_b ? (min3_a < min3_c ? min3_a : min3_c) : \
+ (min3_b < min3_c ? min3_b : min3_c)); \
+})
+
+#define _ODP_MAX3(a, b, c) \
+__extension__ ({ \
+ __typeof__(a) max3_a = (a); \
+ __typeof__(b) max3_b = (b); \
+ __typeof__(c) max3_c = (c); \
+ (max3_a > max3_b ? (max3_a > max3_c ? max3_a : max3_c) : \
+ (max3_b > max3_c ? max3_b : max3_c)); \
+})
+
+/* Macros to calculate ODP_ROUNDUP_POWER2_U32() in five rounds of shift
+ * and OR operations. */
+#define __ODP_RSHIFT_U32(x, y) (((uint32_t)(x)) >> (y))
+#define __ODP_POW2_U32_R1(x) (((uint32_t)(x)) | __ODP_RSHIFT_U32(x, 1))
+#define __ODP_POW2_U32_R2(x) (__ODP_POW2_U32_R1(x) | __ODP_RSHIFT_U32(__ODP_POW2_U32_R1(x), 2))
+#define __ODP_POW2_U32_R3(x) (__ODP_POW2_U32_R2(x) | __ODP_RSHIFT_U32(__ODP_POW2_U32_R2(x), 4))
+#define __ODP_POW2_U32_R4(x) (__ODP_POW2_U32_R3(x) | __ODP_RSHIFT_U32(__ODP_POW2_U32_R3(x), 8))
+#define __ODP_POW2_U32_R5(x) (__ODP_POW2_U32_R4(x) | __ODP_RSHIFT_U32(__ODP_POW2_U32_R4(x), 16))
+
+/* Round up a uint32_t value 'x' to the next power of two.
+ *
+ * The value is not round up, if it's already a power of two (including 1).
+ * The value must be larger than 0 and not exceed 0x80000000.
+ */
+#define _ODP_ROUNDUP_POWER2_U32(x) \
+ ((((uint32_t)(x)) > 0x80000000) ? 0 : (__ODP_POW2_U32_R5(x - 1) + 1))
+
+/*
+ * Round up 'x' to alignment 'align'
+ */
+#define _ODP_ROUNDUP_ALIGN(x, align)\
+ ((align) * (((x) + (align) - 1) / (align)))
+
+/*
+ * Round up 'x' to cache line size alignment
+ */
+#define _ODP_ROUNDUP_CACHE_LINE(x)\
+ _ODP_ROUNDUP_ALIGN(x, ODP_CACHE_LINE_SIZE)
+
+/*
+ * Round down 'x' to 'align' alignment, which is a power of two
+ */
+#define _ODP_ROUNDDOWN_POWER2(x, align)\
+ ((x) & (~((align) - 1)))
+
+/*
+ * Check if value is a power of two
+ */
+#define _ODP_CHECK_IS_POWER2(x) ((((x) - 1) & (x)) == 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ml_fp16.h b/platform/linux-generic/include/odp_ml_fp16.h
new file mode 100644
index 000000000..476028cb4
--- /dev/null
+++ b/platform/linux-generic/include/odp_ml_fp16.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_ML_FP16_H_
+#define ODP_ML_FP16_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+uint16_t _odp_float32_to_float16(float x);
+float _odp_float16_to_float32(uint16_t f16);
+uint16_t _odp_float32_to_bfloat16(float x);
+float _odp_bfloat16_to_float32(uint16_t f16);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_ML_FP16_H_ */
diff --git a/platform/linux-generic/include/odp_name_table_internal.h b/platform/linux-generic/include/odp_name_table_internal.h
index 52b202ca2..40ae40d01 100644
--- a/platform/linux-generic/include/odp_name_table_internal.h
+++ b/platform/linux-generic/include/odp_name_table_internal.h
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#ifndef ODP_INT_NAME_TABLE_H_
@@ -14,7 +11,6 @@ extern "C" {
#endif
#include <stdint.h>
-#include <odp_api.h>
typedef enum {
ODP_COS_HANDLE,
diff --git a/platform/linux-generic/include/odp_packet_dpdk.h b/platform/linux-generic/include/odp_packet_dpdk.h
index 5d80d84a3..d40cf7d95 100644
--- a/platform/linux-generic/include/odp_packet_dpdk.h
+++ b/platform/linux-generic/include/odp_packet_dpdk.h
@@ -1,68 +1,78 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
#ifndef ODP_PACKET_DPDK_H
#define ODP_PACKET_DPDK_H
+#include <stdint.h>
+
#include <odp/api/packet_io.h>
-#include <odp/api/pool.h>
-#include <odp/api/ticketlock.h>
-#include <net/if.h>
+#include <odp_packet_internal.h>
+#include <odp_parse_internal.h>
-#ifdef ODP_PKTIO_DPDK
-#include <rte_config.h>
#include <rte_mbuf.h>
+#include <rte_version.h>
-#define DPDK_MEMORY_MB 512
-#define DPDK_NB_MBUF 16384
-#define DPDK_MBUF_BUF_SIZE RTE_MBUF_DEFAULT_BUF_SIZE
-#define DPDK_MEMPOOL_CACHE_SIZE 64
-#define DPDK_NM_RX_DESC 128
-#define DPDK_NM_TX_DESC 512
-
-ODP_STATIC_ASSERT((DPDK_NB_MBUF % DPDK_MEMPOOL_CACHE_SIZE == 0) &&
- (DPDK_MEMPOOL_CACHE_SIZE <= RTE_MEMPOOL_CACHE_MAX_SIZE) &&
- (DPDK_MEMPOOL_CACHE_SIZE <= DPDK_MBUF_BUF_SIZE * 10 / 15)
- , "DPDK mempool cache size failure");
+#if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0)
+ #define RTE_MBUF_F_RX_IP_CKSUM_MASK PKT_RX_IP_CKSUM_MASK
+ #define RTE_MBUF_F_RX_IP_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD
+ #define RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN PKT_RX_IP_CKSUM_UNKNOWN
+ #define RTE_MBUF_F_RX_L4_CKSUM_MASK PKT_RX_L4_CKSUM_MASK
+ #define RTE_MBUF_F_RX_L4_CKSUM_GOOD PKT_RX_L4_CKSUM_GOOD
+ #define RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN PKT_RX_L4_CKSUM_UNKNOWN
#endif
-#define DPDK_IXGBE_MIN_RX_BURST 4
-
-/** Cache for storing packets */
-struct pkt_cache_t {
- /** array for storing extra RX packets */
- struct rte_mbuf *pkt[DPDK_IXGBE_MIN_RX_BURST];
- unsigned idx; /**< head of cache */
- unsigned count; /**< packets in cache */
-};
-
-typedef union {
- struct pkt_cache_t s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct pkt_cache_t))];
-} pkt_cache_t ODP_ALIGNED_CACHE;
-
-/** Packet IO using DPDK interface */
-typedef struct {
- odp_pool_t pool; /**< pool to alloc packets from */
- struct rte_mempool *pkt_pool; /**< DPDK packet pool */
- odp_pktio_capability_t capa; /**< interface capabilities */
- uint32_t data_room; /**< maximum packet length */
- uint16_t mtu; /**< maximum transmission unit */
- /** Use system call to get/set vdev promisc mode */
- odp_bool_t vdev_sysc_promisc;
- uint8_t port_id; /**< DPDK port identifier */
- unsigned min_rx_burst; /**< minimum RX burst size */
- odp_pktin_hash_proto_t hash; /**< Packet input hash protocol */
- odp_bool_t lockless_rx; /**< no locking for rx */
- odp_bool_t lockless_tx; /**< no locking for tx */
- odp_ticketlock_t rx_lock[PKTIO_MAX_QUEUES]; /**< RX queue locks */
- odp_ticketlock_t tx_lock[PKTIO_MAX_QUEUES]; /**< TX queue locks */
- /** cache for storing extra RX packets */
- pkt_cache_t rx_cache[PKTIO_MAX_QUEUES];
-} pkt_dpdk_t;
+#define IP4_CSUM_RESULT(ol_flags) ((ol_flags) & RTE_MBUF_F_RX_IP_CKSUM_MASK)
+#define L4_CSUM_RESULT(ol_flags) ((ol_flags) & RTE_MBUF_F_RX_L4_CKSUM_MASK)
+
+/** Packet parser using DPDK interface */
+static inline
+int _odp_dpdk_packet_parse_common(odp_packet_hdr_t *pkt_hdr, const uint8_t *ptr,
+ uint32_t frame_len, uint32_t seg_len,
+ struct rte_mbuf *mbuf, int layer,
+ odp_pktin_config_opt_t pktin_cfg)
+{
+ packet_parser_t *prs = &pkt_hdr->p;
+ uint64_t mbuf_ol = mbuf->ol_flags;
+
+ if (odp_unlikely(layer == ODP_PROTO_LAYER_NONE))
+ return 0;
+
+ /* Assume valid L2 header, no CRC/FCS check in SW */
+ prs->l2_offset = 0;
+
+ if (layer >= ODP_PROTO_LAYER_L3) {
+ int ip_chksum = IP4_CSUM_RESULT(mbuf_ol);
+
+ if (ip_chksum == RTE_MBUF_F_RX_IP_CKSUM_GOOD) {
+ prs->input_flags.l3_chksum_done = 1;
+ } else if (ip_chksum != RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN) {
+ prs->input_flags.l3_chksum_done = 1;
+ prs->flags.l3_chksum_err = 1;
+ }
+ }
+
+ if (layer >= ODP_PROTO_LAYER_L4) {
+ int l4_chksum = L4_CSUM_RESULT(mbuf_ol);
+
+ if (l4_chksum == RTE_MBUF_F_RX_L4_CKSUM_GOOD) {
+ prs->input_flags.l4_chksum_done = 1;
+ } else if (l4_chksum != RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN) {
+ prs->input_flags.l4_chksum_done = 1;
+ prs->flags.l4_chksum_err = 1;
+ }
+ }
+
+ pktin_cfg.bit.ipv4_chksum = 0;
+ pktin_cfg.bit.udp_chksum = 0;
+ pktin_cfg.bit.tcp_chksum = 0;
+ pktin_cfg.bit.sctp_chksum = 0;
+
+ return _odp_packet_parse_common(pkt_hdr, ptr, frame_len, seg_len, layer,
+ pktin_cfg);
+}
#endif
diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index d923ee233..3cea245ae 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
/**
@@ -18,87 +17,57 @@ extern "C" {
#endif
#include <odp/api/align.h>
+#include <odp/api/atomic.h>
#include <odp/api/debug.h>
-#include <odp_debug_internal.h>
-#include <odp_buffer_internal.h>
-#include <odp_pool_internal.h>
-#include <odp_buffer_inlines.h>
+#include <odp/api/hints.h>
#include <odp/api/packet.h>
#include <odp/api/packet_io.h>
#include <odp/api/crypto.h>
-#include <odp_crypto_internal.h>
-#include <odp/api/plat/packet_types.h>
-#include <odp_queue_if.h>
+#include <odp/api/comp.h>
+#include <odp/api/std.h>
-/** Minimum segment length expected by packet_parse_common() */
-#define PACKET_PARSE_SEG_LEN 96
+#include <odp/api/plat/packet_inline_types.h>
+
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_queue_if.h>
+#include <odp_config_internal.h>
+#include <stdint.h>
+#include <string.h>
ODP_STATIC_ASSERT(sizeof(_odp_packet_input_flags_t) == sizeof(uint64_t),
"INPUT_FLAGS_SIZE_ERROR");
-/**
- * Packet error flags
- */
-typedef union {
- /* All error flags */
- uint32_t all;
-
- struct {
- /* Bitfield flags for each detected error */
- uint32_t app_error:1; /**< Error bit for application use */
- uint32_t frame_len:1; /**< Frame length error */
- uint32_t snap_len:1; /**< Snap length error */
- uint32_t l2_chksum:1; /**< L2 checksum error, checks TBD */
- uint32_t ip_err:1; /**< IP error, checks TBD */
- uint32_t tcp_err:1; /**< TCP error, checks TBD */
- uint32_t udp_err:1; /**< UDP error, checks TBD */
- };
-} error_flags_t;
-
-ODP_STATIC_ASSERT(sizeof(error_flags_t) == sizeof(uint32_t),
- "ERROR_FLAGS_SIZE_ERROR");
+ODP_STATIC_ASSERT(sizeof(_odp_packet_flags_t) == sizeof(uint32_t),
+ "PACKET_FLAGS_SIZE_ERROR");
-/**
- * Packet output flags
- */
-typedef union {
- /* All output flags */
- uint32_t all;
-
- struct {
- /** adjustment for traffic mgr */
- uint32_t shaper_len_adj:8;
-
- /* Bitfield flags for each output option */
- uint32_t l3_chksum_set:1; /**< L3 chksum bit is valid */
- uint32_t l3_chksum:1; /**< L3 chksum override */
- uint32_t l4_chksum_set:1; /**< L3 chksum bit is valid */
- uint32_t l4_chksum:1; /**< L4 chksum override */
- };
-} output_flags_t;
+/* Maximum number of segments per packet */
+#define PKT_MAX_SEGS 255
-ODP_STATIC_ASSERT(sizeof(output_flags_t) == sizeof(uint32_t),
- "OUTPUT_FLAGS_SIZE_ERROR");
+ODP_STATIC_ASSERT(PKT_MAX_SEGS < UINT16_MAX, "PACKET_MAX_SEGS_ERROR");
/**
* Packet parser metadata
*/
typedef struct {
+ /* Packet input flags */
_odp_packet_input_flags_t input_flags;
- error_flags_t error_flags;
- output_flags_t output_flags;
- uint32_t l2_offset; /**< offset to L2 hdr, e.g. Eth */
- uint32_t l3_offset; /**< offset to L3 hdr, e.g. IPv4, IPv6 */
- uint32_t l4_offset; /**< offset to L4 hdr (TCP, UDP, SCTP, also ICMP) */
-} packet_parser_t;
+ /* Other flags */
+ _odp_packet_flags_t flags;
-/* Packet extra data length */
-#define PKT_EXTRA_LEN 128
+ /* offset to L2 hdr, e.g. Eth */
+ uint16_t l2_offset;
-/* Packet extra data types */
-#define PKT_EXTRA_TYPE_DPDK 1
+ /* offset to L3 hdr, e.g. IPv4, IPv6 */
+ uint16_t l3_offset;
+
+ /* offset to L4 hdr (TCP, UDP, SCTP, also ICMP) */
+ uint16_t l4_offset;
+} packet_parser_t;
/**
* Internal Packet header
@@ -107,127 +76,144 @@ typedef struct {
* packet_init(). Because of this any new fields added must be reviewed for
* initialization requirements.
*/
-typedef struct odp_packet_hdr_t {
- /* common buffer header */
- odp_buffer_hdr_t buf_hdr;
+typedef struct ODP_ALIGNED_CACHE odp_packet_hdr_t {
+ /* Common event header */
+ _odp_event_hdr_t event_hdr;
- /*
- * Following members are initialized by packet_init()
- */
+ /* Segment data start */
+ uint8_t *seg_data;
packet_parser_t p;
+ /* --- 64-byte cache line boundary --- */
+
odp_pktio_t input;
+ /* Next header which continues the segment list */
+ struct odp_packet_hdr_t *seg_next;
+
+ /* Total packet length */
uint32_t frame_len;
- uint32_t headroom;
- uint32_t tailroom;
-
- /* Fields used to support packet references */
- uint32_t unshared_len;
- /* Next pkt_hdr in reference chain */
- struct odp_packet_hdr_t *ref_hdr;
- /* Offset into next pkt_hdr that ref was created at */
- uint32_t ref_offset;
- /* frame_len in next pkt_hdr at time ref was created. This
- * allows original offset to be maintained when base pkt len
- * is changed */
- uint32_t ref_len;
- /* Incremented on refs, decremented on frees. */
- odp_atomic_u32_t ref_count;
-
- /*
- * Members below are not initialized by packet_init()
- */
+
+ /* Segment data length */
+ uint32_t seg_len;
+
+ /* Total segment count */
+ uint16_t seg_count;
+
+ uint16_t headroom;
+
+ uint16_t tailroom;
+
+ /* Classifier handle index */
+ uint16_t cos;
+
+ /* Used as classifier destination queue, in IPsec inline input processing and as Tx
+ * completion event queue. */
+ odp_queue_t dst_queue;
+
+ /* Reference count */
+ odp_atomic_u32_t ref_cnt;
/* Flow hash value */
uint32_t flow_hash;
+ /* User area pointer */
+ void *uarea_addr;
+
+ /* User context pointer */
+ const void *user_ptr;
+
+ /* --- 64-byte cache line boundary --- */
+
/* Timestamp value */
odp_time_t timestamp;
- /* Classifier destination queue */
- queue_t dst_queue;
+ /* Classifier mark */
+ uint16_t cls_mark;
- /* Result for crypto */
- odp_crypto_generic_op_result_t op_result;
+ /* Offset to payload start */
+ uint16_t payload_offset;
-#ifdef ODP_PKTIO_DPDK
- /* Type of extra data */
- uint8_t extra_type;
- /* Extra space for packet descriptors. E.g. DPDK mbuf */
- uint8_t extra[PKT_EXTRA_LEN] ODP_ALIGNED_CACHE;
-#endif
+ /* Max payload size in a LSO segment */
+ uint16_t lso_max_payload;
+
+ /* Packet aging drop timeout before enqueue. Once enqueued holds the maximum age (time of
+ * request + requested drop timeout). */
+ uint64_t tx_aging_ns;
+
+ /* Tx completion poll completion identifier */
+ uint32_t tx_compl_id;
+
+ /* LSO profile index */
+ uint8_t lso_profile_idx;
+
+ /* Pktio where packet is used as a memory source */
+ uint8_t ms_pktio_idx;
+
+ union {
+ /* Result for crypto packet op */
+ odp_crypto_packet_result_t crypto_op_result;
+
+ /* Context for IPsec */
+ odp_ipsec_packet_result_t ipsec_ctx;
+
+ /* Result for comp packet op */
+ odp_comp_packet_result_t comp_op_result;
+ };
/* Packet data storage */
- uint8_t data[0];
+ uint8_t data[];
+
} odp_packet_hdr_t;
+/* Packet header size is critical for performance. Ensure that it does not accidentally
+ * grow over 256 bytes. */
+ODP_STATIC_ASSERT(sizeof(odp_packet_hdr_t) <= 256, "PACKET_HDR_SIZE_ERROR");
+
+ODP_STATIC_ASSERT(CONFIG_PKTIO_ENTRIES < UINT8_MAX, "MS_PKTIO_IDX_SIZE_ERROR");
+
/**
* Return the packet header
*/
-static inline odp_packet_hdr_t *odp_packet_hdr(odp_packet_t pkt)
+static inline odp_packet_hdr_t *packet_hdr(odp_packet_t pkt)
{
return (odp_packet_hdr_t *)(uintptr_t)pkt;
}
-static inline odp_packet_hdr_t *packet_last_hdr(odp_packet_t pkt,
- uint32_t *offset)
+static inline odp_packet_t packet_handle(odp_packet_hdr_t *pkt_hdr)
{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
- odp_packet_hdr_t *prev_hdr = pkt_hdr;
- uint32_t ref_offset = 0;
-
- while (pkt_hdr->ref_hdr) {
- ref_offset = pkt_hdr->ref_offset;
- prev_hdr = pkt_hdr;
- pkt_hdr = pkt_hdr->ref_hdr;
- }
-
- if (offset) {
- if (prev_hdr != pkt_hdr)
- ref_offset += pkt_hdr->frame_len - prev_hdr->ref_len;
- *offset = ref_offset;
- }
-
- return pkt_hdr;
+ return (odp_packet_t)pkt_hdr;
}
-static inline odp_packet_hdr_t *packet_prev_hdr(odp_packet_hdr_t *pkt_hdr,
- odp_packet_hdr_t *cur_hdr,
- uint32_t *offset)
+static inline _odp_event_hdr_t *packet_to_event_hdr(odp_packet_t pkt)
{
- uint32_t ref_offset = 0;
- odp_packet_hdr_t *prev_hdr = pkt_hdr;
-
- while (pkt_hdr->ref_hdr != cur_hdr) {
- ref_offset = pkt_hdr->ref_offset;
- prev_hdr = pkt_hdr;
- pkt_hdr = pkt_hdr->ref_hdr;
- }
-
- if (offset) {
- if (prev_hdr != pkt_hdr)
- ref_offset += pkt_hdr->frame_len - prev_hdr->ref_len;
- *offset = ref_offset;
- }
+ return (_odp_event_hdr_t *)(uintptr_t)&packet_hdr(pkt)->event_hdr;
+}
- return pkt_hdr;
+static inline odp_packet_t packet_from_event_hdr(_odp_event_hdr_t *event_hdr)
+{
+ return (odp_packet_t)(uintptr_t)event_hdr;
}
-static inline odp_packet_t packet_handle(odp_packet_hdr_t *pkt_hdr)
+static inline uint32_t packet_first_seg_len(odp_packet_hdr_t *pkt_hdr)
{
- return (odp_packet_t)pkt_hdr;
+ return pkt_hdr->seg_len;
}
-static inline odp_buffer_hdr_t *packet_to_buf_hdr(odp_packet_t pkt)
+static inline odp_packet_hdr_t *packet_last_seg(odp_packet_hdr_t *hdr)
{
- return &odp_packet_hdr(pkt)->buf_hdr;
+ while (hdr->seg_next != NULL)
+ hdr = hdr->seg_next;
+
+ return hdr;
}
-static inline odp_packet_t packet_from_buf_hdr(odp_buffer_hdr_t *buf_hdr)
+static inline void packet_subtype_set(odp_packet_t pkt, int subtype)
{
- return (odp_packet_t)(odp_packet_hdr_t *)buf_hdr;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ pkt_hdr->event_hdr.subtype = subtype;
}
/**
@@ -235,22 +221,26 @@ static inline odp_packet_t packet_from_buf_hdr(odp_buffer_hdr_t *buf_hdr)
*/
static inline void packet_init(odp_packet_hdr_t *pkt_hdr, uint32_t len)
{
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
uint32_t seg_len;
- int num = pkt_hdr->buf_hdr.segcount;
+ int num = pkt_hdr->seg_count;
- if (odp_likely(CONFIG_PACKET_MAX_SEGS == 1 || num == 1)) {
+ if (odp_likely(num == 1)) {
seg_len = len;
- pkt_hdr->buf_hdr.seg[0].len = len;
+ pkt_hdr->seg_len = len;
} else {
- seg_len = len - ((num - 1) * CONFIG_PACKET_MAX_SEG_LEN);
+ odp_packet_hdr_t *last;
+
+ seg_len = len - ((num - 1) * pool->seg_len);
/* Last segment data length */
- pkt_hdr->buf_hdr.seg[num - 1].len = seg_len;
+ last = packet_last_seg(pkt_hdr);
+ last->seg_len = seg_len;
}
- pkt_hdr->p.input_flags.all = 0;
- pkt_hdr->p.output_flags.all = 0;
- pkt_hdr->p.error_flags.all = 0;
+ /* Clear all flags. Resets also return value of cls_mark, user_ptr, etc. */
+ pkt_hdr->p.input_flags.all = 0;
+ pkt_hdr->p.flags.all_flags = 0;
pkt_hdr->p.l2_offset = 0;
pkt_hdr->p.l3_offset = ODP_PACKET_OFFSET_INVALID;
@@ -262,109 +252,192 @@ static inline void packet_init(odp_packet_hdr_t *pkt_hdr, uint32_t len)
* segment occupied by the allocated length.
*/
pkt_hdr->frame_len = len;
- pkt_hdr->headroom = CONFIG_PACKET_HEADROOM;
- pkt_hdr->tailroom = CONFIG_PACKET_MAX_SEG_LEN - seg_len +
- CONFIG_PACKET_TAILROOM;
+ pkt_hdr->headroom = pool->headroom;
+ pkt_hdr->tailroom = pool->seg_len - seg_len + pool->tailroom;
+
+ if (odp_unlikely(pkt_hdr->event_hdr.subtype != ODP_EVENT_PACKET_BASIC))
+ pkt_hdr->event_hdr.subtype = ODP_EVENT_PACKET_BASIC;
pkt_hdr->input = ODP_PKTIO_INVALID;
+}
+
+/**
+ * Check if copying packet metadata between pools is possible
+ *
+ * @retval 0 when possible without user area copy
+ * @retval >0 when possible with user area copy
+ * @retval <0 when not possible
+ */
+static inline int _odp_packet_copy_md_possible(odp_pool_t dst_pool,
+ odp_pool_t src_pool)
+{
+ const pool_t *src_hdr;
+ const pool_t *dst_hdr;
+
+ if (src_pool == dst_pool)
+ return 0;
+
+ src_hdr = _odp_pool_entry(src_pool);
+ dst_hdr = _odp_pool_entry(dst_pool);
- /* By default packet has no references */
- pkt_hdr->unshared_len = len;
- pkt_hdr->ref_hdr = NULL;
+ if (dst_hdr->param_uarea_size < src_hdr->param_uarea_size)
+ return -1;
+
+ return 1;
}
-static inline void copy_packet_parser_metadata(odp_packet_hdr_t *src_hdr,
- odp_packet_hdr_t *dst_hdr)
+/**
+ * Copy packet metadata
+ *
+ * This function is assumed to never fail. Use _odp_packet_copy_md_possible() to
+ * check beforehand that copying packet metadata between source and destination
+ * packet pools is possible.
+ *
+ * @param uarea_copy Copy user area data. If false, user area pointers
+ * are swapped between the packet headers (allowed
+ * only when packets are from the same pool).
+ */
+static inline void _odp_packet_copy_md(odp_packet_hdr_t *dst_hdr,
+ odp_packet_hdr_t *src_hdr,
+ odp_bool_t uarea_copy)
{
+ int8_t subtype = src_hdr->event_hdr.subtype;
+
+ /* Lengths and segmentation data are not copied:
+ * .frame_len
+ * .headroom
+ * .tailroom
+ * .seg_data
+ * .seg_next
+ * .seg_len
+ * .seg_count
+ */
+ dst_hdr->input = src_hdr->input;
+ dst_hdr->event_hdr.subtype = subtype;
+ dst_hdr->dst_queue = src_hdr->dst_queue;
+ dst_hdr->cos = src_hdr->cos;
+ dst_hdr->cls_mark = src_hdr->cls_mark;
+ dst_hdr->user_ptr = src_hdr->user_ptr;
+
+ if (src_hdr->p.input_flags.flow_hash)
+ dst_hdr->flow_hash = src_hdr->flow_hash;
+
+ if (src_hdr->p.input_flags.timestamp)
+ dst_hdr->timestamp = src_hdr->timestamp;
+
+ if (src_hdr->p.flags.lso) {
+ dst_hdr->lso_max_payload = src_hdr->lso_max_payload;
+ dst_hdr->lso_profile_idx = src_hdr->lso_profile_idx;
+ }
+
+ if (src_hdr->p.flags.payload_off)
+ dst_hdr->payload_offset = src_hdr->payload_offset;
+
dst_hdr->p = src_hdr->p;
+
+ if (src_hdr->uarea_addr) {
+ if (uarea_copy) {
+ const pool_t *src_pool = _odp_pool_entry(src_hdr->event_hdr.pool);
+ const pool_t *dst_pool = _odp_pool_entry(dst_hdr->event_hdr.pool);
+ const uint32_t src_uarea_size = src_pool->param_uarea_size;
+ const uint32_t dst_uarea_size = dst_pool->param_uarea_size;
+
+ _ODP_ASSERT(dst_hdr->uarea_addr != NULL);
+ _ODP_ASSERT(dst_uarea_size >= src_uarea_size);
+
+ memcpy(dst_hdr->uarea_addr, src_hdr->uarea_addr, src_uarea_size);
+ } else {
+ void *src_uarea = src_hdr->uarea_addr;
+
+ /* If user area exists, packets should always be from the same pool, so
+ * user area pointers can simply be swapped. */
+ _ODP_ASSERT(dst_hdr->event_hdr.pool == src_hdr->event_hdr.pool);
+
+ src_hdr->uarea_addr = dst_hdr->uarea_addr;
+ dst_hdr->uarea_addr = src_uarea;
+ }
+ }
+
+ if (odp_unlikely(subtype != ODP_EVENT_PACKET_BASIC)) {
+ if (subtype == ODP_EVENT_PACKET_IPSEC)
+ dst_hdr->ipsec_ctx = src_hdr->ipsec_ctx;
+ else if (subtype == ODP_EVENT_PACKET_CRYPTO)
+ dst_hdr->crypto_op_result = src_hdr->crypto_op_result;
+ else if (subtype == ODP_EVENT_PACKET_COMP)
+ dst_hdr->comp_op_result = src_hdr->comp_op_result;
+ }
}
-static inline void copy_packet_cls_metadata(odp_packet_hdr_t *src_hdr,
- odp_packet_hdr_t *dst_hdr)
+static inline void _odp_packet_copy_cls_md(odp_packet_hdr_t *dst_hdr,
+ odp_packet_hdr_t *src_hdr)
{
dst_hdr->p = src_hdr->p;
dst_hdr->dst_queue = src_hdr->dst_queue;
- dst_hdr->flow_hash = src_hdr->flow_hash;
- dst_hdr->timestamp = src_hdr->timestamp;
- dst_hdr->op_result = src_hdr->op_result;
+ dst_hdr->cos = src_hdr->cos;
+ dst_hdr->cls_mark = src_hdr->cls_mark;
}
-static inline void pull_tail(odp_packet_hdr_t *pkt_hdr, uint32_t len)
+static inline void *packet_data(odp_packet_hdr_t *pkt_hdr)
{
- int last = pkt_hdr->buf_hdr.segcount - 1;
-
- pkt_hdr->tailroom += len;
- pkt_hdr->frame_len -= len;
- pkt_hdr->unshared_len -= len;
- pkt_hdr->buf_hdr.seg[last].len -= len;
+ return pkt_hdr->seg_data;
}
-static inline uint32_t packet_len(odp_packet_hdr_t *pkt_hdr)
+static inline void push_head(odp_packet_hdr_t *pkt_hdr, uint32_t len)
{
- uint32_t pkt_len = pkt_hdr->frame_len;
- odp_packet_hdr_t *ref_hdr = pkt_hdr->ref_hdr;
-
- while (ref_hdr) {
- pkt_len += (pkt_hdr->ref_len - pkt_hdr->ref_offset);
- pkt_hdr = ref_hdr;
- ref_hdr = ref_hdr->ref_hdr;
- }
+ pkt_hdr->headroom -= len;
+ pkt_hdr->frame_len += len;
+ pkt_hdr->seg_data -= len;
+ pkt_hdr->seg_len += len;
+}
- return pkt_len;
+static inline void pull_head(odp_packet_hdr_t *pkt_hdr, uint32_t len)
+{
+ pkt_hdr->headroom += len;
+ pkt_hdr->frame_len -= len;
+ pkt_hdr->seg_data += len;
+ pkt_hdr->seg_len -= len;
}
-static inline uint32_t packet_ref_count(odp_packet_hdr_t *pkt_hdr)
+static inline void pull_tail(odp_packet_hdr_t *pkt_hdr, uint32_t len)
{
- /* Breach the atomic type to do a peek at the ref count. This
- * is used to bypass atomic operations if ref_count == 1 for
- * performance reasons.
- */
- return pkt_hdr->ref_count.v;
+ odp_packet_hdr_t *last = packet_last_seg(pkt_hdr);
+
+ pkt_hdr->tailroom += len;
+ pkt_hdr->frame_len -= len;
+ last->seg_len -= len;
}
-static inline void packet_ref_count_set(odp_packet_hdr_t *pkt_hdr, uint32_t n)
+static inline uint32_t packet_len(odp_packet_hdr_t *pkt_hdr)
{
- /* Only used during init when there are no other possible
- * references to this pkt, so avoid the "atomic" overhead by
- * a controlled breach of the atomic type here. This saves
- * over 10% of the pathlength in routines like packet_alloc().
- */
- pkt_hdr->ref_count.v = n;
+ return pkt_hdr->frame_len;
}
static inline void packet_set_len(odp_packet_hdr_t *pkt_hdr, uint32_t len)
{
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
-
pkt_hdr->frame_len = len;
- pkt_hdr->unshared_len = len;
}
-/* Forward declarations */
-int _odp_packet_copy_md_to_packet(odp_packet_t srcpkt, odp_packet_t dstpkt);
-
/* Packet alloc of pktios */
-int packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
- odp_packet_t pkt[], int max_num);
-
-/* Perform packet parse up to a given protocol layer */
-int packet_parse_layer(odp_packet_hdr_t *pkt_hdr,
- odp_pktio_parser_layer_t layer);
+int _odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
+ odp_packet_t pkt[], int max_num);
/* Reset parser metadata for a new parse */
-void packet_parse_reset(odp_packet_hdr_t *pkt_hdr);
-
-/* Convert a buffer handle to a packet handle */
-odp_packet_t _odp_packet_from_buf_hdr(odp_buffer_hdr_t *buf_hdr);
-
-static inline int packet_hdr_has_l2(odp_packet_hdr_t *pkt_hdr)
+static inline void packet_parse_reset(odp_packet_hdr_t *pkt_hdr, int all)
{
- return pkt_hdr->p.input_flags.l2;
+ pkt_hdr->p.input_flags.all = 0;
+ pkt_hdr->p.l2_offset = ODP_PACKET_OFFSET_INVALID;
+ pkt_hdr->p.l3_offset = ODP_PACKET_OFFSET_INVALID;
+ pkt_hdr->p.l4_offset = ODP_PACKET_OFFSET_INVALID;
+
+ if (all)
+ pkt_hdr->p.flags.all_flags = 0;
+ else /* Keep user ptr and pktout flags */
+ pkt_hdr->p.flags.all.error = 0;
}
-static inline void packet_hdr_has_l2_set(odp_packet_hdr_t *pkt_hdr, int val)
+static inline int packet_hdr_has_l2(odp_packet_hdr_t *pkt_hdr)
{
- pkt_hdr->p.input_flags.l2 = val;
+ return pkt_hdr->p.input_flags.l2;
}
static inline int packet_hdr_has_eth(odp_packet_hdr_t *pkt_hdr)
@@ -377,6 +450,13 @@ static inline int packet_hdr_has_ipv6(odp_packet_hdr_t *pkt_hdr)
return pkt_hdr->p.input_flags.ipv6;
}
+static inline void packet_set_flow_hash(odp_packet_hdr_t *pkt_hdr,
+ uint32_t flow_hash)
+{
+ pkt_hdr->flow_hash = flow_hash;
+ pkt_hdr->p.input_flags.flow_hash = 1;
+}
+
static inline void packet_set_ts(odp_packet_hdr_t *pkt_hdr, odp_time_t *ts)
{
if (ts != NULL) {
@@ -385,18 +465,20 @@ static inline void packet_set_ts(odp_packet_hdr_t *pkt_hdr, odp_time_t *ts)
}
}
-int packet_parse_common(packet_parser_t *pkt_hdr, const uint8_t *ptr,
- uint32_t pkt_len, uint32_t seg_len,
- odp_pktio_parser_layer_t layer);
-
-int _odp_cls_parse(odp_packet_hdr_t *pkt_hdr, const uint8_t *parseptr);
-
int _odp_packet_set_data(odp_packet_t pkt, uint32_t offset,
uint8_t c, uint32_t len);
int _odp_packet_cmp_data(odp_packet_t pkt, uint32_t offset,
const void *s, uint32_t len);
+int _odp_packet_ipv4_chksum_insert(odp_packet_t pkt);
+int _odp_packet_tcp_chksum_insert(odp_packet_t pkt);
+int _odp_packet_udp_chksum_insert(odp_packet_t pkt);
+int _odp_packet_sctp_chksum_insert(odp_packet_t pkt);
+
+int _odp_packet_l4_chksum(odp_packet_hdr_t *pkt_hdr,
+ odp_pktin_config_opt_t opt, uint64_t l4_part_sum);
+
#ifdef __cplusplus
}
#endif
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index 1a4e345f5..d8fb77c74 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -1,10 +1,8 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
-
/**
* @file
*
@@ -18,23 +16,36 @@
extern "C" {
#endif
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
#include <odp/api/spinlock.h>
#include <odp/api/ticketlock.h>
+#include <odp/api/time.h>
+
+#include <odp/api/plat/packet_io_inlines.h>
+
+#include <odp/autoheader_internal.h>
#include <odp_classification_datamodel.h>
-#include <odp_align_internal.h>
+#include <odp_config_internal.h>
#include <odp_debug_internal.h>
-#include <odp_packet_io_ring_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_io_stats_common.h>
#include <odp_queue_if.h>
-#include <odp_config_internal.h>
-#include <odp/api/hints.h>
+#include <inttypes.h>
+#include <linux/if_ether.h>
#include <net/if.h>
+#include <string.h>
+#include <sys/select.h>
-#define PKTIO_MAX_QUEUES 64
-#include <odp_packet_socket.h>
-#include <odp_packet_netmap.h>
-#include <odp_packet_tap.h>
-#include <odp_packet_dpdk.h>
+#define PKTIO_LSO_PROFILES 16
+/* Assume at least Ethernet header per each segment */
+#define PKTIO_LSO_MIN_PAYLOAD_OFFSET 14
+#define PKTIO_LSO_MAX_PAYLOAD_OFFSET 128
+/* Allow 64 kB packet to be split into about 1kB segments */
+#define PKTIO_LSO_MAX_SEGMENTS 64
+
+ODP_STATIC_ASSERT(PKTIO_LSO_PROFILES < UINT8_MAX, "PKTIO_LSO_PROFILES_ERROR");
#define PKTIO_NAME_LEN 256
@@ -50,84 +61,43 @@ extern "C" {
/* Forward declaration */
struct pktio_if_ops;
-typedef struct {
- odp_queue_t loopq; /**< loopback queue for "loop" device */
- odp_bool_t promisc; /**< promiscuous mode state */
-} pkt_loop_t;
-
-#ifdef HAVE_PCAP
-typedef struct {
- char *fname_rx; /**< name of pcap file for rx */
- char *fname_tx; /**< name of pcap file for tx */
- void *rx; /**< rx pcap handle */
- void *tx; /**< tx pcap handle */
- void *tx_dump; /**< tx pcap dumper handle */
- odp_pool_t pool; /**< rx pool */
- unsigned char *buf; /**< per-pktio temp buffer */
- int loops; /**< number of times to loop rx pcap */
- int loop_cnt; /**< number of loops completed */
- odp_bool_t promisc; /**< promiscuous mode state */
-} pkt_pcap_t;
+#if defined(_ODP_PKTIO_XDP) && ODP_CACHE_LINE_SIZE == 128
+#define PKTIO_PRIVATE_SIZE 33792
+#elif defined(_ODP_PKTIO_XDP)
+#define PKTIO_PRIVATE_SIZE 29696
+#else
+#define PKTIO_PRIVATE_SIZE 9216
#endif
-typedef struct {
- /* TX */
- struct {
- _ring_t *send; /**< ODP ring for IPC msg packets
- indexes transmitted to shared
- memory */
- _ring_t *free; /**< ODP ring for IPC msg packets
- indexes already processed by remote
- process */
- } tx;
- /* RX */
- struct {
- _ring_t *recv; /**< ODP ring for IPC msg packets
- indexes received from shared
- memory (from remote process) */
- _ring_t *free; /**< odp ring for ipc msg packets
- indexes already processed by
- current process */
- _ring_t *cache; /**< local cache to keep packet order right */
- } rx; /* slave */
- void *pool_base; /**< Remote pool base addr */
- void *pool_mdata_base; /**< Remote pool mdata base addr */
- uint64_t pkt_size; /**< Packet size in remote pool */
- odp_pool_t pool; /**< Pool of main process */
- enum {
- PKTIO_TYPE_IPC_MASTER = 0, /**< Master is the process which
- creates shm */
- PKTIO_TYPE_IPC_SLAVE /**< Slave is the process which
- connects to shm */
- } type; /**< define if it's master or slave process */
- odp_atomic_u32_t ready; /**< 1 - pktio is ready and can recv/send
- packet, 0 - not yet ready */
- void *pinfo;
- odp_shm_t pinfo_shm;
- odp_shm_t remote_pool_shm; /**< shm of remote pool get with
- _ipc_map_remote_pool() */
-} _ipc_pktio_t;
-
-struct pktio_entry {
+typedef struct ODP_ALIGNED_CACHE {
const struct pktio_if_ops *ops; /**< Implementation specific methods */
/* These two locks together lock the whole pktio device */
odp_ticketlock_t rxl; /**< RX ticketlock */
odp_ticketlock_t txl; /**< TX ticketlock */
- int cls_enabled; /**< is classifier enabled */
+ odp_proto_layer_t parse_layer;
+ uint16_t pktin_frame_offset;
+
+ struct {
+ union {
+ uint8_t all_flags;
+
+ struct {
+ /* Pktout checksum offload */
+ uint8_t chksum_insert : 1;
+ /* Classifier */
+ uint8_t cls : 1;
+ /* Tx timestamp */
+ uint8_t tx_ts : 1;
+ /* Tx completion events */
+ uint8_t tx_compl : 1;
+ /* Packet aging */
+ uint8_t tx_aging : 1;
+ };
+ };
+ } enabled;
+
odp_pktio_t handle; /**< pktio handle */
- union {
- pkt_loop_t pkt_loop; /**< Using loopback for IO */
- pkt_sock_t pkt_sock; /**< using socket API for IO */
- pkt_sock_mmap_t pkt_sock_mmap; /**< using socket mmap
- * API for IO */
- pkt_netmap_t pkt_nm; /**< using netmap API for IO */
- pkt_dpdk_t pkt_dpdk; /**< using DPDK for IO */
-#ifdef HAVE_PCAP
- pkt_pcap_t pkt_pcap; /**< Using pcap for IO */
-#endif
- pkt_tap_t pkt_tap; /**< using TAP for IO */
- _ipc_pktio_t ipc; /**< IPC pktio data */
- };
+ unsigned char pkt_priv[PKTIO_PRIVATE_SIZE] ODP_ALIGNED_CACHE;
enum {
/* Not allocated */
PKTIO_STATE_FREE = 0,
@@ -148,44 +118,76 @@ struct pktio_entry {
} state;
odp_pktio_config_t config; /**< Device configuration */
classifier_t cls; /**< classifier linked with this pktio*/
- odp_pktio_stats_t stats; /**< statistic counters for pktio */
- enum {
- STATS_SYSFS = 0,
- STATS_ETHTOOL,
- STATS_UNSUPPORTED
- } stats_type;
+ /* Driver level statistics counters */
+ odp_pktio_stats_t stats;
+ /* Statistics counters used also outside drivers */
+ struct {
+ odp_atomic_u64_t in_discards;
+ odp_atomic_u64_t in_errors;
+ odp_atomic_u64_t out_discards;
+ } stats_extra;
+ /* Latest Tx timestamp */
+ odp_atomic_u64_t tx_ts;
+ pktio_stats_type_t stats_type;
char name[PKTIO_NAME_LEN]; /**< name of pktio provided to
- pktio_open() */
-
+ internal pktio_open() calls */
+ char full_name[PKTIO_NAME_LEN]; /**< original pktio name passed to
+ odp_pktio_open() and returned by
+ odp_pktio_info() */
odp_pool_t pool;
odp_pktio_param_t param;
+ odp_pktio_capability_t capa; /**< Packet IO capabilities */
+
+ /* Pool for Tx completion events */
+ odp_pool_t tx_compl_pool;
+ /* Status map SHM handle */
+ odp_shm_t tx_compl_status_shm;
+ /* Status map for Tx completion identifiers */
+ odp_atomic_u32_t *tx_compl_status;
/* Storage for queue handles
* Multi-queue support is pktio driver specific */
- unsigned num_in_queue;
- unsigned num_out_queue;
+ uint32_t num_in_queue;
+ uint32_t num_out_queue;
struct {
odp_queue_t queue;
- queue_t queue_int;
odp_pktin_queue_t pktin;
- } in_queue[PKTIO_MAX_QUEUES];
+ odp_pktin_vector_config_t vector;
+ } in_queue[ODP_PKTIN_MAX_QUEUES];
struct {
odp_queue_t queue;
odp_pktout_queue_t pktout;
- } out_queue[PKTIO_MAX_QUEUES];
-};
+ } out_queue[ODP_PKTOUT_MAX_QUEUES];
-typedef union {
- struct pktio_entry s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct pktio_entry))];
} pktio_entry_t;
typedef struct {
+ odp_lso_profile_param_t param;
+ int used;
+ uint8_t index;
+
+} lso_profile_t;
+
+/* Global variables */
+typedef struct {
odp_spinlock_t lock;
- pktio_entry_t entries[ODP_CONFIG_PKTIO_ENTRIES];
-} pktio_table_t;
+ odp_shm_t shm;
+
+ struct {
+ /* Frame start offset from base pointer at packet input */
+ uint16_t pktin_frame_offset;
+ /* Pool size for potential completion events */
+ uint32_t tx_compl_pool_size;
+ } config;
+
+ pktio_entry_t entries[CONFIG_PKTIO_ENTRIES];
+
+ lso_profile_t lso_profile[PKTIO_LSO_PROFILES];
+ int num_lso_profiles;
+
+} pktio_global_t;
typedef struct pktio_if_ops {
const char *name;
@@ -200,17 +202,36 @@ typedef struct pktio_if_ops {
int (*stop)(pktio_entry_t *pktio_entry);
int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
int (*stats_reset)(pktio_entry_t *pktio_entry);
- uint64_t (*pktin_ts_res)(pktio_entry_t *pktio_entry);
- odp_time_t (*pktin_ts_from_ns)(pktio_entry_t *pktio_entry, uint64_t ns);
+ int (*pktin_queue_stats)(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktin_queue_stats_t *pktin_stats);
+ int (*pktout_queue_stats)(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktout_queue_stats_t *pktout_stats);
+ int (*extra_stat_info)(pktio_entry_t *pktio_entry, odp_pktio_extra_stat_info_t info[],
+ int num);
+ int (*extra_stats)(pktio_entry_t *pktio_entry, uint64_t stats[], int num);
+ int (*extra_stat_counter)(pktio_entry_t *pktio_entry, uint32_t id, uint64_t *stat);
+ uint64_t (*pktio_ts_res)(pktio_entry_t *pktio_entry);
+ odp_time_t (*pktio_ts_from_ns)(pktio_entry_t *pktio_entry, uint64_t ns);
+ odp_time_t (*pktio_time)(pktio_entry_t *pktio_entry, odp_time_t *global_ts);
int (*recv)(pktio_entry_t *entry, int index, odp_packet_t packets[],
int num);
+ int (*recv_tmo)(pktio_entry_t *entry, int index, odp_packet_t packets[],
+ int num, uint64_t wait_usecs);
+ int (*recv_mq_tmo)(pktio_entry_t *entry[], int index[], uint32_t num_q,
+ odp_packet_t packets[], int num, uint32_t *from,
+ uint64_t wait_usecs);
+ int (*fd_set)(pktio_entry_t *entry, int index, fd_set *readfds);
int (*send)(pktio_entry_t *entry, int index,
const odp_packet_t packets[], int num);
- uint32_t (*mtu_get)(pktio_entry_t *pktio_entry);
+ uint32_t (*maxlen_get)(pktio_entry_t *pktio_entry);
+ int (*maxlen_set)(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output);
int (*promisc_mode_set)(pktio_entry_t *pktio_entry, int enable);
int (*promisc_mode_get)(pktio_entry_t *pktio_entry);
int (*mac_get)(pktio_entry_t *pktio_entry, void *mac_addr);
+ int (*mac_set)(pktio_entry_t *pktio_entry, const void *mac_addr);
int (*link_status)(pktio_entry_t *pktio_entry);
+ int (*link_info)(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info);
int (*capability)(pktio_entry_t *pktio_entry,
odp_pktio_capability_t *capa);
int (*config)(pktio_entry_t *pktio_entry,
@@ -221,55 +242,127 @@ typedef struct pktio_if_ops {
const odp_pktout_queue_param_t *p);
} pktio_if_ops_t;
-extern void *pktio_entry_ptr[];
+typedef struct {
+ const void *user_ptr;
+} _odp_pktio_tx_compl_t;
-static inline int pktio_to_id(odp_pktio_t pktio)
-{
- return _odp_typeval(pktio) - 1;
-}
+extern void *_odp_pktio_entry_ptr[];
static inline pktio_entry_t *get_pktio_entry(odp_pktio_t pktio)
{
+ int idx;
+
if (odp_unlikely(pktio == ODP_PKTIO_INVALID))
return NULL;
- if (odp_unlikely(_odp_typeval(pktio) > ODP_CONFIG_PKTIO_ENTRIES)) {
- ODP_DBG("pktio limit %d/%d exceed\n",
- _odp_typeval(pktio), ODP_CONFIG_PKTIO_ENTRIES);
+ if (odp_unlikely(_odp_typeval(pktio) > CONFIG_PKTIO_ENTRIES)) {
+ _ODP_DBG("pktio limit %" PRIuPTR "/%d exceed\n",
+ _odp_typeval(pktio), CONFIG_PKTIO_ENTRIES);
return NULL;
}
- return pktio_entry_ptr[pktio_to_id(pktio)];
+ idx = odp_pktio_index(pktio);
+
+ return _odp_pktio_entry_ptr[idx];
}
static inline int pktio_cls_enabled(pktio_entry_t *entry)
{
- return entry->s.cls_enabled;
+ return entry->enabled.cls;
+}
+
+static inline int _odp_pktio_tx_ts_enabled(pktio_entry_t *entry)
+{
+ return entry->enabled.tx_ts;
+}
+
+static inline int _odp_pktio_tx_compl_enabled(const pktio_entry_t *entry)
+{
+ return entry->enabled.tx_compl;
+}
+
+static inline int _odp_pktio_tx_aging_enabled(pktio_entry_t *entry)
+{
+ return entry->enabled.tx_aging;
}
-static inline void pktio_cls_enabled_set(pktio_entry_t *entry, int ena)
+static inline void _odp_pktio_tx_ts_set(pktio_entry_t *entry)
{
- entry->s.cls_enabled = ena;
+ odp_time_t ts_val = odp_time_global();
+
+ odp_atomic_store_u64(&entry->tx_ts, ts_val.u64);
}
-extern const pktio_if_ops_t netmap_pktio_ops;
-extern const pktio_if_ops_t dpdk_pktio_ops;
-extern const pktio_if_ops_t sock_mmsg_pktio_ops;
-extern const pktio_if_ops_t sock_mmap_pktio_ops;
-extern const pktio_if_ops_t loopback_pktio_ops;
-#ifdef HAVE_PCAP
-extern const pktio_if_ops_t pcap_pktio_ops;
+extern const pktio_if_ops_t _odp_dpdk_pktio_ops;
+extern const pktio_if_ops_t _odp_sock_xdp_pktio_ops;
+extern const pktio_if_ops_t _odp_sock_mmsg_pktio_ops;
+extern const pktio_if_ops_t _odp_sock_mmap_pktio_ops;
+extern const pktio_if_ops_t _odp_loopback_pktio_ops;
+#ifdef _ODP_PKTIO_PCAP
+extern const pktio_if_ops_t _odp_pcap_pktio_ops;
#endif
-extern const pktio_if_ops_t tap_pktio_ops;
-extern const pktio_if_ops_t ipc_pktio_ops;
-extern const pktio_if_ops_t * const pktio_if_ops[];
-
-int sysfs_stats(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats);
-int sock_stats_fd(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats,
- int fd);
-int sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd);
+extern const pktio_if_ops_t _odp_tap_pktio_ops;
+extern const pktio_if_ops_t _odp_null_pktio_ops;
+extern const pktio_if_ops_t _odp_ipc_pktio_ops;
+extern const pktio_if_ops_t * const _odp_pktio_if_ops[];
+
+/**
+ * Try interrupt-driven receive
+ *
+ * @param queues Pktin queues
+ * @param num_q Number of queues
+ * @param packets Output packet slots
+ * @param num Number of output packet slots
+ * @param from Queue from which the call received packets
+ * @param usecs Microseconds to wait
+ * @param trial_successful Will receive information whether trial was successful
+ *
+ * @return >=0 on success, number of packets received
+ * @return <0 on failure
+ */
+int _odp_sock_recv_mq_tmo_try_int_driven(const struct odp_pktin_queue_t queues[],
+ uint32_t num_q, uint32_t *from,
+ odp_packet_t packets[], int num,
+ uint64_t usecs,
+ int *trial_successful);
+
+/* Setup PKTOUT with single queue for TM */
+int _odp_pktio_pktout_tm_config(odp_pktio_t pktio_hdl,
+ odp_pktout_queue_t *queue, bool reconf);
+
+/* LSO functions shared with TM */
+odp_lso_profile_t _odp_lso_prof_from_idx(uint8_t idx);
+
+int _odp_lso_num_packets(odp_packet_t packet, const odp_packet_lso_opt_t *lso_opt,
+ uint32_t *len_out, uint32_t *left_over_out);
+
+int _odp_lso_create_packets(odp_packet_t packet, const odp_packet_lso_opt_t *lso_opt,
+ uint32_t payload_len, uint32_t left_over_len,
+ odp_packet_t pkt_out[], int num_pkt);
+
+void _odp_pktio_process_tx_compl(const pktio_entry_t *entry, const odp_packet_t packets[],
+ int num);
+
+static inline int _odp_pktio_packet_to_pool(odp_packet_t *pkt,
+ odp_packet_hdr_t **pkt_hdr,
+ odp_pool_t new_pool)
+{
+ odp_packet_t new_pkt;
+
+ if (odp_likely(new_pool == odp_packet_pool(*pkt)))
+ return 0;
+
+ new_pkt = odp_packet_copy(*pkt, new_pool);
+
+ if (odp_unlikely(new_pkt == ODP_PACKET_INVALID))
+ return 1;
+
+ odp_packet_free(*pkt);
+ *pkt = new_pkt;
+ *pkt_hdr = packet_hdr(new_pkt);
+
+ return 0;
+}
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_packet_io_ipc_internal.h b/platform/linux-generic/include/odp_packet_io_ipc_internal.h
deleted file mode 100644
index 9d8943a6c..000000000
--- a/platform/linux-generic/include/odp_packet_io_ipc_internal.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/packet_io.h>
-#include <odp_packet_io_internal.h>
-#include <odp/api/packet.h>
-#include <odp_packet_internal.h>
-#include <odp_internal.h>
-#include <odp/api/shared_memory.h>
-
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-/* IPC packet I/O over shared memory ring */
-#include <odp_packet_io_ring_internal.h>
-
-/* number of odp buffers in odp ring queue */
-#define PKTIO_IPC_ENTRIES 4096
-
-/* that struct is exported to shared memory, so that processes can find
- * each other.
- */
-struct pktio_info {
- struct {
- /* number of buffer*/
- int num;
- /* size of packet/segment in remote pool */
- uint32_t block_size;
- char pool_name[ODP_POOL_NAME_LEN];
- /* 1 if master finished creation of all shared objects */
- int init_done;
- } master;
- struct {
- void *base_addr;
- uint32_t block_size;
- char pool_name[ODP_POOL_NAME_LEN];
- /* pid of the slave process written to shm and
- * used by master to look up memory created by
- * slave
- */
- int pid;
- int init_done;
- } slave;
-} ODP_PACKED;
diff --git a/platform/linux-generic/include/odp_packet_io_ring_internal.h b/platform/linux-generic/include/odp_packet_io_ring_internal.h
deleted file mode 100644
index d044f9319..000000000
--- a/platform/linux-generic/include/odp_packet_io_ring_internal.h
+++ /dev/null
@@ -1,589 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Derived from FreeBSD's bufring.c
- *
- **************************************************************************
- *
- * Copyright (c) 2007,2008 Kip Macy kmacy@freebsd.org
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. The name of Kip Macy nor the names of other
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ***************************************************************************/
-
-/**
- * ODP Ring
- *
- * The Ring Manager is a fixed-size queue, implemented as a table of
- * pointers. Head and tail pointers are modified atomically, allowing
- * concurrent access to it. It has the following features:
- *
- * - FIFO (First In First Out)
- * - Maximum size is fixed; the pointers are stored in a table.
- * - Lockless implementation.
- * - Multi- or single-consumer dequeue.
- * - Multi- or single-producer enqueue.
- * - Bulk dequeue.
- * - Bulk enqueue.
- *
- * Note: the ring implementation is not preemptable. A lcore must not
- * be interrupted by another task that uses the same ring.
- *
- */
-
-#ifndef _RING_H_
-#define _RING_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/std_types.h>
-#include <odp/api/hints.h>
-#include <odp/api/atomic.h>
-#include <errno.h>
-#include <sys/queue.h>
-#include <odp_debug_internal.h>
-
-enum _ring_queue_behavior {
- _RING_QUEUE_FIXED = 0, /**< Enq/Deq a fixed number
- of items from a ring */
- _RING_QUEUE_VARIABLE /**< Enq/Deq as many items
- a possible from ring */
-};
-
-#define _RING_NAMESIZE 32 /**< The maximum length of a ring name. */
-
-/**
- * An ODP ring structure.
- *
- * The producer and the consumer have a head and a tail index. The particularity
- * of these index is that they are not between 0 and size(ring). These indexes
- * are between 0 and 2^32, and we mask their value when we access the ring[]
- * field. Thanks to this assumption, we can do subtractions between 2 index
- * values in a modulo-32bit base: that's why the overflow of the indexes is not
- * a problem.
- */
-typedef struct _ring {
- /** @private Next in list. */
- TAILQ_ENTRY(_ring) next;
-
- /** @private Name of the ring. */
- char name[_RING_NAMESIZE];
- /** @private Flags supplied at creation. */
- int flags;
-
- /** @private Producer */
- struct _prod {
- uint32_t watermark; /* Maximum items */
- uint32_t sp_enqueue; /* True, if single producer. */
- uint32_t size; /* Size of ring. */
- uint32_t mask; /* Mask (size-1) of ring. */
- volatile uint32_t head; /* Producer head. */
- volatile uint32_t tail; /* Producer tail. */
- } prod ODP_ALIGNED_CACHE;
-
- /** @private Consumer */
- struct _cons {
- uint32_t sc_dequeue; /* True, if single consumer. */
- uint32_t size; /* Size of the ring. */
- uint32_t mask; /* Mask (size-1) of ring. */
- volatile uint32_t head; /* Consumer head. */
- volatile uint32_t tail; /* Consumer tail. */
- } cons ODP_ALIGNED_CACHE;
-
- /** @private Memory space of ring starts here. */
- void *ring[0] ODP_ALIGNED_CACHE;
-} _ring_t;
-
-/* The default enqueue is "single-producer".*/
-#define _RING_F_SP_ENQ (1 << 0)
-/* The default dequeue is "single-consumer".*/
-#define _RING_F_SC_DEQ (1 << 1)
-/* If set - ring is visible from different processes.
- * Default is thread visible.*/
-#define _RING_SHM_PROC (1 << 2)
- /* Do not link ring to linked list. */
-#define _RING_NO_LIST (1 << 3)
-/* Quota exceed for burst ops */
-#define _RING_QUOT_EXCEED (1 << 31)
-/* Ring size mask */
-#define _RING_SZ_MASK (unsigned)(0x0fffffff)
-
-/**
- * Create a new ring named *name* in memory.
- *
- * This function uses odp_shm_reserve() to allocate memory. Its size is
- * set to *count*, which must be a power of two. Water marking is
- * disabled by default. Note that the real usable ring size is count-1
- * instead of count.
- *
- * @param name
- * The name of the ring.
- * @param count
- * The size of the ring (must be a power of 2).
- * @param flags
- * An OR of the following:
- * - RING_F_SP_ENQ: If this flag is set, the default behavior when
- * using ``odph_ring_enqueue()`` or ``odph_ring_enqueue_bulk()``
- * is "single-producer". Otherwise, it is "multi-producers".
- * - RING_F_SC_DEQ: If this flag is set, the default behavior when
- * using ``odph_ring_dequeue()`` or ``odph_ring_dequeue_bulk()``
- * is "single-consumer". Otherwise, it is "multi-consumers".
- * @return
- * On success, the pointer to the new allocated ring. NULL on error with
- * odp_errno set appropriately. Possible errno values include:
- * - EINVAL - count provided is not a power of 2
- * - ENOSPC - the maximum number of memzones has already been allocated
- * - EEXIST - a memzone with the same name already exists
- * - ENOMEM - no appropriate memory area found in which to create memzone
- */
-_ring_t *_ring_create(const char *name, unsigned count,
- unsigned flags);
-
-/**
- * Destroy the ring created with *name*.
- *
- * @param name name of the ring to be destroyed.
- * @return 0 on success and negative value on error.
- */
-int _ring_destroy(const char *name);
-
-/**
- * Change the high water mark.
- *
- * If *count* is 0, water marking is disabled. Otherwise, it is set to the
- * *count* value. The *count* value must be greater than 0 and less
- * than the ring size.
- *
- * This function can be called at any time (not necessarily at
- * initialization).
- *
- * @param r Pointer to the ring structure.
- * @param count New water mark value.
- * @return 0: Success; water mark changed.
- * -EINVAL: Invalid water mark value.
- */
-int _ring_set_water_mark(_ring_t *r, unsigned count);
-
-/**
- * Dump the status of the ring to the console.
- *
- * @param r A pointer to the ring structure.
- */
-void _ring_dump(const _ring_t *r);
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- *
- * This function uses a "compare and set" instruction to move the
- * producer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @param behavior
- * ODPH_RING_QUEUE_FIXED: Enqueue a fixed number of items from a ring
- * ODPH_RING_QUEUE_VARIABLE: Enqueue as many items a possible from ring
- * @return
- * Depend on the behavior value
- * if behavior = ODPH_RING_QUEUE_FIXED
- * - 0: Success; objects enqueue.
- * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the
- * high water mark is exceeded.
- * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued.
- * if behavior = ODPH_RING_QUEUE_VARIABLE
- * - n: Actual number of objects enqueued.
- */
-int ___ring_mp_do_enqueue(_ring_t *r, void * const *obj_table,
- unsigned n,
- enum _ring_queue_behavior behavior);
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @param behavior
- * ODPH_RING_QUEUE_FIXED: Enqueue a fixed number of items from a ring
- * ODPH_RING_QUEUE_VARIABLE: Enqueue as many items a possible from ring
- * @return
- * Depend on the behavior value
- * if behavior = ODPH_RING_QUEUE_FIXED
- * - 0: Success; objects enqueue.
- * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the
- * high water mark is exceeded.
- * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued.
- * if behavior = ODPH_RING_QUEUE_VARIABLE
- * - n: Actual number of objects enqueued.
- */
-int ___ring_sp_do_enqueue(_ring_t *r, void * const *obj_table,
- unsigned n,
- enum _ring_queue_behavior behavior);
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe). When
- * the request objects are more than the available objects, only dequeue the
- * actual number of objects
- *
- * This function uses a "compare and set" instruction to move the
- * consumer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @param behavior
- * ODPH_RING_QUEUE_FIXED: Dequeue a fixed number of items from a ring
- * ODPH_RING_QUEUE_VARIABLE: Dequeue as many items a possible from ring
- * @return
- * Depend on the behavior value
- * if behavior = ODPH_RING_QUEUE_FIXED
- * - 0: Success; objects dequeued.
- * - -ENOENT: Not enough entries in the ring to dequeue; no object is
- * dequeued.
- * if behavior = ODPH_RING_QUEUE_VARIABLE
- * - n: Actual number of objects dequeued.
- */
-
-int ___ring_mc_do_dequeue(_ring_t *r, void **obj_table,
- unsigned n,
- enum _ring_queue_behavior behavior);
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).
- * When the request objects are more than the available objects, only dequeue
- * the actual number of objects
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @param behavior
- * ODPH_RING_QUEUE_FIXED: Dequeue a fixed number of items from a ring
- * ODPH_RING_QUEUE_VARIABLE: Dequeue as many items a possible from ring
- * @return
- * Depend on the behavior value
- * if behavior = ODPH_RING_QUEUE_FIXED
- * - 0: Success; objects dequeued.
- * - -ENOENT: Not enough entries in the ring to dequeue; no object is
- * dequeued.
- * if behavior = ODPH_RING_QUEUE_VARIABLE
- * - n: Actual number of objects dequeued.
- */
-int ___ring_sc_do_dequeue(_ring_t *r, void **obj_table,
- unsigned n,
- enum _ring_queue_behavior behavior);
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- *
- * This function uses a "compare and set" instruction to move the
- * producer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @return
- * - 0: Success; objects enqueue.
- * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the
- * high water mark is exceeded.
- * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued.
- */
-int _ring_mp_enqueue_bulk(_ring_t *r, void * const *obj_table,
- unsigned n);
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @return
- * - 0: Success; objects enqueued.
- * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the
- * high water mark is exceeded.
- * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued.
- */
-int _ring_sp_enqueue_bulk(_ring_t *r, void * const *obj_table,
- unsigned n);
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe).
- *
- * This function uses a "compare and set" instruction to move the
- * consumer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @return
- * - 0: Success; objects dequeued.
- * - -ENOENT: Not enough entries in the ring to dequeue; no object is
- * dequeued.
- */
-int _ring_mc_dequeue_bulk(_ring_t *r, void **obj_table, unsigned n);
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table,
- * must be strictly positive.
- * @return
- * - 0: Success; objects dequeued.
- * - -ENOENT: Not enough entries in the ring to dequeue; no object is
- * dequeued.
- */
-int _ring_sc_dequeue_bulk(_ring_t *r, void **obj_table, unsigned n);
-
-/**
- * Test if a ring is full.
- *
- * @param r
- * A pointer to the ring structure.
- * @return
- * - 1: The ring is full.
- * - 0: The ring is not full.
- */
-int _ring_full(const _ring_t *r);
-
-/**
- * Test if a ring is empty.
- *
- * @param r
- * A pointer to the ring structure.
- * @return
- * - 1: The ring is empty.
- * - 0: The ring is not empty.
- */
-int _ring_empty(const _ring_t *r);
-
-/**
- * Return the number of entries in a ring.
- *
- * @param r
- * A pointer to the ring structure.
- * @return
- * The number of entries in the ring.
- */
-unsigned _ring_count(const _ring_t *r);
-
-/**
- * Return the number of free entries in a ring.
- *
- * @param r
- * A pointer to the ring structure.
- * @return
- * The number of free entries in the ring.
- */
-unsigned _ring_free_count(const _ring_t *r);
-
-/**
- * search ring by name
- * @param name ring name to search
- * @return pointer to ring otherwise NULL
- */
-_ring_t *_ring_lookup(const char *name);
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- *
- * This function uses a "compare and set" instruction to move the
- * producer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @return
- * - n: Actual number of objects enqueued.
- */
-int _ring_mp_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n);
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @return
- * - n: Actual number of objects enqueued.
- */
-int _ring_sp_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n);
-/**
- * Enqueue several objects on a ring.
- *
- * This function calls the multi-producer or the single-producer
- * version depending on the default behavior that was specified at
- * ring creation time (see flags).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects).
- * @param n
- * The number of objects to add in the ring from the obj_table.
- * @return
- * - n: Actual number of objects enqueued.
- */
-int _ring_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n);
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe). When the request
- * objects are more than the available objects, only dequeue the actual number
- * of objects
- *
- * This function uses a "compare and set" instruction to move the
- * consumer index atomically.
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @return
- * - n: Actual number of objects dequeued, 0 if ring is empty
- */
-int _ring_mc_dequeue_burst(_ring_t *r, void **obj_table, unsigned n);
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).When the
- * request objects are more than the available objects, only dequeue the
- * actual number of objects
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @return
- * - n: Actual number of objects dequeued, 0 if ring is empty
- */
-int _ring_sc_dequeue_burst(_ring_t *r, void **obj_table, unsigned n);
-
-/**
- * Dequeue multiple objects from a ring up to a maximum number.
- *
- * This function calls the multi-consumers or the single-consumer
- * version, depending on the default behaviour that was specified at
- * ring creation time (see flags).
- *
- * @param r
- * A pointer to the ring structure.
- * @param obj_table
- * A pointer to a table of void * pointers (objects) that will be filled.
- * @param n
- * The number of objects to dequeue from the ring to the obj_table.
- * @return
- * - Number of objects dequeued, or a negative error code on error
- */
-int _ring_dequeue_burst(_ring_t *r, void **obj_table, unsigned n);
-
-/**
- * dump the status of all rings on the console
- */
-void _ring_list_dump(void);
-
-/**
- * initialise ring tailq
- */
-void _ring_tailq_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_packet_io_stats.h b/platform/linux-generic/include/odp_packet_io_stats.h
new file mode 100644
index 000000000..b86e7b845
--- /dev/null
+++ b/platform/linux-generic/include/odp_packet_io_stats.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_PACKET_IO_STATS_H_
+#define ODP_PACKET_IO_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/packet_io_stats.h>
+#include <odp_packet_io_internal.h>
+#include <odp_packet_io_stats_common.h>
+
+int _odp_sock_stats_fd(pktio_entry_t *pktio_entry,
+ odp_pktio_stats_t *stats,
+ int fd);
+int _odp_sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd);
+
+void _odp_sock_stats_capa(pktio_entry_t *pktio_entry,
+ odp_pktio_capability_t *capa);
+
+int _odp_sock_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[], int num,
+ int fd);
+int _odp_sock_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[], int num,
+ int fd);
+int _odp_sock_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat, int fd);
+
+pktio_stats_type_t _odp_sock_stats_type_fd(pktio_entry_t *pktio_entry, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_PACKET_IO_STATS_H_ */
diff --git a/platform/linux-generic/include/odp_packet_io_stats_common.h b/platform/linux-generic/include/odp_packet_io_stats_common.h
new file mode 100644
index 000000000..7e7ef9723
--- /dev/null
+++ b/platform/linux-generic/include/odp_packet_io_stats_common.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_PACKET_IO_STATS_COMMON_H_
+#define ODP_PACKET_IO_STATS_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ STATS_SYSFS = 0,
+ STATS_ETHTOOL,
+ STATS_UNSUPPORTED
+} pktio_stats_type_t;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_PACKET_IO_STATS_COMMON_H_ */
diff --git a/platform/linux-generic/include/odp_packet_netmap.h b/platform/linux-generic/include/odp_packet_netmap.h
deleted file mode 100644
index a6f68d569..000000000
--- a/platform/linux-generic/include/odp_packet_netmap.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PACKET_NETMAP_H
-#define ODP_PACKET_NETMAP_H
-
-#include <odp/api/align.h>
-#include <odp/api/debug.h>
-#include <odp/api/packet_io.h>
-#include <odp/api/pool.h>
-#include <odp/api/ticketlock.h>
-#include <odp_align_internal.h>
-
-#include <linux/if_ether.h>
-#include <net/if.h>
-
-#define NM_MAX_DESC 64
-
-/** Ring for mapping pktin/pktout queues to netmap descriptors */
-struct netmap_ring_t {
- unsigned first; /**< Index of first netmap descriptor */
- unsigned last; /**< Index of last netmap descriptor */
- unsigned num; /**< Number of netmap descriptors */
- /** Netmap metadata for the device */
- struct nm_desc *desc[NM_MAX_DESC];
- unsigned cur; /**< Index of current netmap descriptor */
- odp_ticketlock_t lock; /**< Queue lock */
-};
-
-typedef union {
- struct netmap_ring_t s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct netmap_ring_t))];
-} netmap_ring_t ODP_ALIGNED_CACHE;
-
-/** Netmap ring slot */
-typedef struct {
- char *buf; /**< Slot buffer pointer */
- uint16_t len; /**< Slot length */
-} netmap_slot_t;
-
-/** Packet socket using netmap mmaped rings for both Rx and Tx */
-typedef struct {
- odp_pool_t pool; /**< pool to alloc packets from */
- size_t max_frame_len; /**< buf_size - sizeof(pkt_hdr) */
- uint32_t if_flags; /**< interface flags */
- uint32_t mtu; /**< maximum transmission unit */
- int sockfd; /**< control socket */
- unsigned char if_mac[ETH_ALEN]; /**< eth mac address */
- char nm_name[IF_NAMESIZE + 7]; /**< netmap:<ifname> */
- char if_name[IF_NAMESIZE]; /**< interface name used in ioctl */
- odp_bool_t is_virtual; /**< nm virtual port (VALE/pipe) */
- odp_pktio_capability_t capa; /**< interface capabilities */
- uint32_t num_rx_rings; /**< number of nm rx rings */
- uint32_t num_tx_rings; /**< number of nm tx rings */
- unsigned num_rx_desc_rings; /**< number of rx descriptor rings */
- unsigned num_tx_desc_rings; /**< number of tx descriptor rings */
- odp_bool_t lockless_rx; /**< no locking for rx */
- odp_bool_t lockless_tx; /**< no locking for tx */
- /** mapping of pktin queues to netmap rx descriptors */
- netmap_ring_t rx_desc_ring[PKTIO_MAX_QUEUES];
- /** mapping of pktout queues to netmap tx descriptors */
- netmap_ring_t tx_desc_ring[PKTIO_MAX_QUEUES];
-} pkt_netmap_t;
-
-#endif
diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
deleted file mode 100644
index 0e61f6f0c..000000000
--- a/platform/linux-generic/include/odp_packet_socket.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * Copyright (c) 2013, Nokia Solutions and Networks
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PACKET_SOCKET_H
-#define ODP_PACKET_SOCKET_H
-
-#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <stddef.h>
-
-#include <odp/api/align.h>
-#include <odp/api/buffer.h>
-#include <odp/api/debug.h>
-#include <odp/api/pool.h>
-#include <odp/api/packet.h>
-#include <odp/api/packet_io.h>
-
-#include <linux/version.h>
-
-/*
- * Packet socket config:
- */
-
-/*
- * This makes sure that building for kernels older than 3.1 works
- * and a fanout requests fails (for invalid packet socket option)
- * in runtime if requested
- */
-#ifndef PACKET_FANOUT
-#define PACKET_FANOUT 18
-#define PACKET_FANOUT_HASH 0
-#endif /* PACKET_FANOUT */
-
-typedef struct {
- int sockfd; /**< socket descriptor */
- odp_pool_t pool; /**< pool to alloc packets from */
- uint32_t mtu; /**< maximum transmission unit */
- unsigned char if_mac[ETH_ALEN]; /**< IF eth mac addr */
-} pkt_sock_t;
-
-/** packet mmap ring */
-struct ring {
- struct iovec *rd;
- unsigned frame_num;
- int rd_num;
-
- int sock;
- int type;
- int version;
- uint8_t *mm_space;
- size_t mm_len;
- size_t rd_len;
- int flen;
-
- struct tpacket_req req;
-};
-
-ODP_STATIC_ASSERT(offsetof(struct ring, mm_space) <= ODP_CACHE_LINE_SIZE,
- "ERR_STRUCT_RING");
-
-/** Packet socket using mmap rings for both Rx and Tx */
-typedef struct {
- /** Packet mmap ring for Rx */
- struct ring rx_ring ODP_ALIGNED_CACHE;
- /** Packet mmap ring for Tx */
- struct ring tx_ring ODP_ALIGNED_CACHE;
-
- int sockfd ODP_ALIGNED_CACHE;
- odp_pool_t pool;
- size_t frame_offset; /**< frame start offset from start of pkt buf */
- uint8_t *mmap_base;
- unsigned mmap_len;
- unsigned char if_mac[ETH_ALEN];
- struct sockaddr_ll ll;
- int fanout;
-} pkt_sock_mmap_t;
-
-static inline void
-ethaddr_copy(unsigned char mac_dst[], unsigned char mac_src[])
-{
- memcpy(mac_dst, mac_src, ETH_ALEN);
-}
-
-static inline int
-ethaddrs_equal(unsigned char mac_a[], unsigned char mac_b[])
-{
- return !memcmp(mac_a, mac_b, ETH_ALEN);
-}
-
-/**
- * Read the MAC address from a packet socket
- */
-int mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[]);
-
-/**
- * Read the MTU from a packet socket
- */
-uint32_t mtu_get_fd(int fd, const char *name);
-
-/**
- * Enable/Disable promisc mode for a packet socket
- */
-int promisc_mode_set_fd(int fd, const char *name, int enable);
-
-/**
- * Return promisc mode of a packet socket
- */
-int promisc_mode_get_fd(int fd, const char *name);
-
-/**
- * Return link status of a packet socket (up/down)
- */
-int link_status_fd(int fd, const char *name);
-
-/**
- * Get enabled RSS hash protocols of a packet socket
- *
- * @param fd Socket file descriptor
- * @param name Interface name
- * @param hash_proto[out] Hash protocols
- *
- * @returns Number enabled hash protocols
- */
-int rss_conf_get_fd(int fd, const char *name,
- odp_pktin_hash_proto_t *hash_proto);
-
-/**
- * Get supported RSS hash protocols of a packet socket
- *
- * Can be both read and modified.
- *
- * @param fd Socket file descriptor
- * @param name Interface name
- * @param hash_proto[out] Hash protocols
- *
- * @returns Number of supported hash protocols
- */
-int rss_conf_get_supported_fd(int fd, const char *name,
- odp_pktin_hash_proto_t *hash_proto);
-
-/**
- * Set RSS hash protocols of a packet socket
- *
- * @param fd Socket file descriptor
- * @param name Interface name
- * @param hash_proto Hash protocols
- *
- * @retval 0 on success
- * @retval <0 on failure
- */
-int rss_conf_set_fd(int fd, const char *name,
- const odp_pktin_hash_proto_t *proto);
-
-/**
- * Print enabled RSS hash protocols
- *
- * @param hash_proto Hash protocols
- */
-void rss_conf_print(const odp_pktin_hash_proto_t *hash_proto);
-
-/**
- * Get ethtool statistics of a packet socket
- */
-int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
-
-#endif
diff --git a/platform/linux-generic/include/odp_packet_tap.h b/platform/linux-generic/include/odp_packet_tap.h
deleted file mode 100644
index a90bfbce0..000000000
--- a/platform/linux-generic/include/odp_packet_tap.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright (c) 2015, Ilya Maximets <i.maximets@samsung.com>
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_PACKET_TAP_H_
-#define ODP_PACKET_TAP_H_
-
-#include <odp/api/pool.h>
-
-typedef struct {
- int fd; /**< file descriptor for tap interface*/
- int skfd; /**< socket descriptor */
- uint32_t mtu; /**< cached mtu */
- unsigned char if_mac[ETH_ALEN]; /**< MAC address of pktio side (not a
- MAC address of kernel interface)*/
- odp_pool_t pool; /**< pool to alloc packets from */
-} pkt_tap_t;
-
-#endif
diff --git a/platform/linux-generic/include/odp_parse_internal.h b/platform/linux-generic/include/odp_parse_internal.h
new file mode 100644
index 000000000..28a401c25
--- /dev/null
+++ b/platform/linux-generic/include/odp_parse_internal.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+#ifndef ODP_PARSE_INTERNAL_H_
+#define ODP_PARSE_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_packet_internal.h>
+#include <odp_macros_internal.h>
+#include <protocols/eth.h>
+#include <protocols/ip.h>
+#include <protocols/sctp.h>
+#include <protocols/tcp.h>
+#include <protocols/udp.h>
+#include <odp/api/plat/packet_inline_types.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/packet_types.h>
+#include <stdint.h>
+
+/*
+ * In the worst case we look at the Ethernet header, 8 bytes of LLC/SNAP
+ * header and two VLAN tags in the same packet.
+ */
+#define PARSE_ETH_BYTES (sizeof(_odp_ethhdr_t) + 8 + 2 * sizeof(_odp_vlanhdr_t))
+#define PARSE_IPV4_BYTES (0xfU * 4) /* max IPv4 header length with options */
+/*
+ * Peek 2 bytes beyond IPv6 base header without length check if there are
+ * extension headers.
+ */
+#define PARSE_IPV6_BYTES (sizeof(_odp_ipv6hdr_t) + 2)
+#define PARSE_TCP_BYTES (sizeof(_odp_tcphdr_t))
+/*
+ * In the worst case we look at the UDP header and 4 bytes of the UDP
+ * payload (the non-ESP marker to distinguish IKE packets from ESP packets).
+ */
+#define PARSE_UDP_BYTES (sizeof(_odp_udphdr_t) + 4)
+#define PARSE_SCTP_BYTES (sizeof(_odp_sctphdr_t))
+
+/* _odp_packet_parse_common_l3_l4() requires up to this many bytes. */
+#define PARSE_L3_L4_BYTES (_ODP_MAX(PARSE_IPV4_BYTES, PARSE_IPV6_BYTES) + \
+ _ODP_MAX3(PARSE_TCP_BYTES, PARSE_UDP_BYTES, PARSE_SCTP_BYTES))
+
+/* _odp_packet_parse_common() requires up to this many bytes. */
+#define PARSE_BYTES (PARSE_ETH_BYTES + PARSE_L3_L4_BYTES)
+
+uint16_t _odp_parse_eth(packet_parser_t *prs, const uint8_t **parseptr,
+ uint32_t *offset, uint32_t frame_len);
+
+/*
+ * Parse common L3 and L4 packet headers up to given layer
+ *
+ * See _odp_packet_parse_common(). Requires up to PARSE_L3_L4_BYTES bytes of
+ * contiguous packet data.
+ *
+ * - offset is the offset of the first byte of the data pointed to by parseptr
+ * - seg_end is the maximum offset that can be accessed plus one
+ */
+int _odp_packet_parse_common_l3_l4(packet_parser_t *prs,
+ const uint8_t *parseptr, uint32_t offset,
+ uint32_t frame_len, uint32_t seg_end,
+ int layer, uint16_t ethtype,
+ uint64_t *l4_part_sum,
+ odp_pktin_config_opt_t opt);
+
+/**
+ * Parse common packet headers up to given layer
+ *
+ * Requires up to PARSE_BYTES bytes of contiguous packet data. Also parse
+ * metadata must be already initialized.
+ *
+ * Returns 0 on success, 1 on packet errors, and -1 if the packet should be
+ * dropped.
+ */
+static inline int _odp_packet_parse_common(odp_packet_hdr_t *pkt_hdr,
+ const uint8_t *ptr,
+ uint32_t frame_len, uint32_t seg_len,
+ int layer,
+ odp_pktin_config_opt_t opt)
+{
+ int r;
+ uint32_t offset;
+ uint16_t ethtype;
+ const uint8_t *parseptr;
+ packet_parser_t *prs = &pkt_hdr->p;
+ uint64_t l4_part_sum = 0;
+
+ parseptr = ptr;
+ offset = 0;
+
+ if (odp_unlikely(layer == ODP_PROTO_LAYER_NONE))
+ return 0;
+
+ /* Assume valid L2 header, no CRC/FCS check in SW */
+ prs->l2_offset = offset;
+
+ ethtype = _odp_parse_eth(prs, &parseptr, &offset, frame_len);
+
+ r = _odp_packet_parse_common_l3_l4(prs, parseptr, offset, frame_len,
+ seg_len, layer, ethtype,
+ &l4_part_sum, opt);
+
+ if (!r && layer >= ODP_PROTO_LAYER_L4)
+ r = _odp_packet_l4_chksum(pkt_hdr, opt, l4_part_sum);
+
+ return r;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_pcapng.h b/platform/linux-generic/include/odp_pcapng.h
new file mode 100644
index 000000000..1afe96867
--- /dev/null
+++ b/platform/linux-generic/include/odp_pcapng.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
+ */
+
+#ifndef ODP_PCAPNG_H_
+#define ODP_PCAPNG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/packet.h>
+#include <odp_packet_io_internal.h>
+
+#include <stdint.h>
+
+int _odp_pcapng_start(pktio_entry_t *entry);
+void _odp_pcapng_stop(pktio_entry_t *entry);
+int _odp_pcapng_dump_pkts(pktio_entry_t *entry, int qidx,
+ const odp_packet_t packets[], int num);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ODP_PCAPNG_H_ */
diff --git a/platform/linux-generic/include/odp_pkt_queue_internal.h b/platform/linux-generic/include/odp_pkt_queue_internal.h
index 83375d02c..3c6ac7230 100644
--- a/platform/linux-generic/include/odp_pkt_queue_internal.h
+++ b/platform/linux-generic/include/odp_pkt_queue_internal.h
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#ifndef _ODP_INT_PKT_QUEUE_H_
@@ -13,8 +10,9 @@
extern "C" {
#endif
+#include <odp/api/packet.h>
+
#include <stdint.h>
-#include <odp_api.h>
typedef uint64_t _odp_int_queue_pool_t;
typedef uint32_t _odp_int_pkt_queue_t;
@@ -43,6 +41,9 @@ _odp_int_queue_pool_t _odp_queue_pool_create(uint32_t max_num_queues,
_odp_int_pkt_queue_t _odp_pkt_queue_create(_odp_int_queue_pool_t queue_pool);
+void _odp_pkt_queue_destroy(_odp_int_queue_pool_t queue_pool,
+ _odp_int_pkt_queue_t pkt_queue);
+
int _odp_pkt_queue_append(_odp_int_queue_pool_t queue_pool,
_odp_int_pkt_queue_t pkt_queue,
odp_packet_t pkt);
diff --git a/platform/linux-generic/include/odp_pool_internal.h b/platform/linux-generic/include/odp_pool_internal.h
index edf75d6e0..b98e7e334 100644
--- a/platform/linux-generic/include/odp_pool_internal.h
+++ b/platform/linux-generic/include/odp_pool_internal.h
@@ -1,10 +1,8 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
-
/**
* @file
*
@@ -18,62 +16,95 @@
extern "C" {
#endif
+#include <odp/api/atomic.h>
#include <odp/api/shared_memory.h>
#include <odp/api/ticketlock.h>
+#include <odp/api/align.h>
#include <odp_buffer_internal.h>
+#include <odp_event_internal.h>
#include <odp_config_internal.h>
-#include <odp_ring_internal.h>
+#include <odp_ring_ptr_internal.h>
#include <odp/api/plat/strong_types.h>
-typedef struct pool_cache_t {
- uint32_t num;
- uint32_t buf_index[CONFIG_POOL_CACHE_SIZE];
+#define _ODP_POOL_MEM_SRC_DATA_SIZE 128
+
+typedef struct ODP_ALIGNED_CACHE pool_cache_t {
+ /* Number of buffers in cache */
+ odp_atomic_u32_t cache_num;
+ /* Cached buffers */
+ _odp_event_hdr_t *event_hdr[CONFIG_POOL_CACHE_MAX_SIZE];
-} pool_cache_t ODP_ALIGNED_CACHE;
+} pool_cache_t;
-/* Buffer header ring */
-typedef struct {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+/* Event header ring */
+typedef struct ODP_ALIGNED_CACHE {
/* Ring header */
- ring_t hdr;
+ ring_ptr_t hdr;
/* Ring data: buffer handles */
- uint32_t buf[CONFIG_POOL_MAX_NUM];
+ _odp_event_hdr_t *event_hdr[CONFIG_POOL_MAX_NUM + 1];
-} pool_ring_t ODP_ALIGNED_CACHE;
+ /* Index to pointer look-up table for external memory pool */
+ _odp_event_hdr_t *event_hdr_by_index[];
-/* Callback function for pool destroy */
-typedef void (*pool_destroy_cb_fn)(void *pool);
+} pool_ring_t;
+#pragma GCC diagnostic pop
+
+struct _odp_pool_mem_src_ops_t;
typedef struct pool_t {
odp_ticketlock_t lock ODP_ALIGNED_CACHE;
+ uint32_t pool_idx;
+ uint8_t reserved;
+ /* Everything under this mark are memset() to zero on pool create */
+ uint8_t memset_mark;
+ uint8_t type;
+ uint8_t pool_ext;
char name[ODP_POOL_NAME_LEN];
odp_pool_param_t params;
- odp_pool_t pool_hdl;
- uint32_t pool_idx;
uint32_t ring_mask;
+ uint32_t cache_size;
+ uint32_t burst_size;
odp_shm_t shm;
odp_shm_t uarea_shm;
- int reserved;
+ uint64_t shm_size;
+ uint64_t uarea_shm_size;
uint32_t num;
uint32_t align;
uint32_t headroom;
uint32_t tailroom;
- uint32_t data_size;
- uint32_t max_len;
+ uint32_t seg_len;
uint32_t max_seg_len;
+ uint32_t max_len;
+ uint32_t param_uarea_size;
uint32_t uarea_size;
uint32_t block_size;
- uint32_t shm_size;
- uint32_t uarea_shm_size;
+ uint32_t block_offset;
+ uint32_t num_populated;
+ uint32_t trailer_size;
uint8_t *base_addr;
+ uint8_t *max_addr;
uint8_t *uarea_base_addr;
-
- /* Used by DPDK zero-copy pktio */
- uint8_t mem_from_huge_pages;
- pool_destroy_cb_fn ext_destroy;
- void *ext_desc;
+ odp_pool_type_t type_2;
+ odp_pool_ext_param_t ext_param;
+ uint32_t ext_head_offset;
+ uint32_t skipped_blocks;
+ uint8_t mem_from_huge_pages;
+ const struct _odp_pool_mem_src_ops_t *mem_src_ops;
+ /* Private area for memory source operations */
+ uint8_t mem_src_data[_ODP_POOL_MEM_SRC_DATA_SIZE] ODP_ALIGNED_CACHE;
+
+ struct ODP_ALIGNED_CACHE {
+ odp_atomic_u64_t alloc_ops;
+ odp_atomic_u64_t alloc_fails;
+ odp_atomic_u64_t free_ops;
+ odp_atomic_u64_t cache_alloc_ops;
+ odp_atomic_u64_t cache_free_ops;
+ } stats;
pool_cache_t local_cache[ODP_THREAD_COUNT_MAX];
@@ -82,30 +113,98 @@ typedef struct pool_t {
} pool_t;
-typedef struct pool_table_t {
- pool_t pool[ODP_CONFIG_POOLS];
+typedef struct pool_global_t {
+ pool_t pool[CONFIG_POOLS];
odp_shm_t shm;
-} pool_table_t;
-extern pool_table_t *pool_tbl;
+ struct {
+ uint32_t pkt_max_len;
+ uint32_t pkt_max_num;
+ uint32_t local_cache_size;
+ uint32_t burst_size;
+ uint32_t pkt_base_align;
+ uint32_t buf_min_align;
+ } config;
+
+} pool_global_t;
+
+/* Operations for when ODP packet pool is used as a memory source for e.g. zero-copy packet IO
+ * purposes */
+typedef struct _odp_pool_mem_src_ops_t {
+ /* Name of the ops provider */
+ const char *name;
+ /* Signal if ops provider is an active user for the pool as a memory source */
+ odp_bool_t (*is_active)(void);
+ /* Force disable for the ops provider (for now, if one active memory source user is found,
+ * others are disabled) */
+ void (*force_disable)(void);
+ /* Adjust pool block sizes as required by memory consumer */
+ void (*adjust_size)(uint8_t *data, uint32_t *block_size, uint32_t *block_offset,
+ uint32_t *flags);
+ /* Bind the pool as a memory source */
+ int (*bind)(uint8_t *data, pool_t *pool);
+ /* Unbind the pool as a memory source */
+ void (*unbind)(uint8_t *data);
+} _odp_pool_mem_src_ops_t;
+
+extern pool_global_t *_odp_pool_glb;
+
+static inline pool_t *_odp_pool_entry_from_idx(uint32_t pool_idx)
+{
+ return &_odp_pool_glb->pool[pool_idx];
+}
-static inline pool_t *pool_entry(uint32_t pool_idx)
+static inline pool_t *_odp_pool_entry(odp_pool_t pool_hdl)
{
- return &pool_tbl->pool[pool_idx];
+ return (pool_t *)(uintptr_t)pool_hdl;
}
-static inline pool_t *pool_entry_from_hdl(odp_pool_t pool_hdl)
+static inline odp_pool_t _odp_pool_handle(pool_t *pool)
{
- return &pool_tbl->pool[_odp_typeval(pool_hdl)];
+ return (odp_pool_t)(uintptr_t)pool;
}
-static inline odp_buffer_hdr_t *buf_hdl_to_hdr(odp_buffer_t buf)
+static inline _odp_event_hdr_t *event_hdr_from_index(pool_t *pool,
+ uint32_t event_idx)
+{
+ uint64_t block_offset;
+ _odp_event_hdr_t *event_hdr;
+
+ block_offset = (event_idx * (uint64_t)pool->block_size) +
+ pool->block_offset;
+
+ /* clang requires cast to uintptr_t */
+ event_hdr = (_odp_event_hdr_t *)(uintptr_t)&pool->base_addr[block_offset];
+
+ return event_hdr;
+}
+
+static inline _odp_event_hdr_t *_odp_event_hdr_from_index_u32(uint32_t u32)
+{
+ _odp_event_index_t index;
+ uint32_t pool_idx, event_idx;
+ pool_t *pool;
+
+ index.u32 = u32;
+ pool_idx = index.pool;
+ event_idx = index.event;
+ pool = _odp_pool_entry_from_idx(pool_idx);
+
+ return event_hdr_from_index(pool, event_idx);
+}
+
+odp_event_t _odp_event_alloc(pool_t *pool);
+int _odp_event_alloc_multi(pool_t *pool, _odp_event_hdr_t *event_hdr[], int num);
+void _odp_event_free_multi(_odp_event_hdr_t *event_hdr[], int num_free);
+int _odp_event_is_valid(odp_event_t event);
+
+static inline void _odp_event_free(odp_event_t event)
{
- return (odp_buffer_hdr_t *)(uintptr_t)buf;
+ _odp_event_free_multi((_odp_event_hdr_t **)&event, 1);
}
-int buffer_alloc_multi(pool_t *pool, odp_buffer_hdr_t *buf_hdr[], int num);
-void buffer_free_multi(odp_buffer_hdr_t *buf_hdr[], int num_free);
+odp_pool_t _odp_pool_create(const char *name, const odp_pool_param_t *params,
+ odp_pool_type_t type_2);
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_posix_extensions.h b/platform/linux-generic/include/odp_posix_extensions.h
index 2c468b4b6..cda257d1d 100644
--- a/platform/linux-generic/include/odp_posix_extensions.h
+++ b/platform/linux-generic/include/odp_posix_extensions.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
#ifndef ODP_POSIX_EXTENSIONS_H
diff --git a/platform/linux-generic/include/odp_queue_basic_internal.h b/platform/linux-generic/include/odp_queue_basic_internal.h
new file mode 100644
index 000000000..e6edec1cd
--- /dev/null
+++ b/platform/linux-generic/include/odp_queue_basic_internal.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_QUEUE_BASIC_INTERNAL_H_
+#define ODP_QUEUE_BASIC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+#include <odp/api/queue.h>
+#include <odp/api/shared_memory.h>
+#include <odp_forward_typedefs_internal.h>
+#include <odp_queue_if.h>
+#include <odp_buffer_internal.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/ticketlock.h>
+#include <odp_config_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ring_mpmc_u32_internal.h>
+#include <odp_ring_st_internal.h>
+#include <odp_ring_spsc_internal.h>
+#include <odp_queue_lf.h>
+
+#define QUEUE_STATUS_FREE 0
+#define QUEUE_STATUS_DESTROYED 1
+#define QUEUE_STATUS_READY 2
+#define QUEUE_STATUS_NOTSCHED 3
+#define QUEUE_STATUS_SCHED 4
+
+typedef struct ODP_ALIGNED_CACHE queue_entry_s {
+ /* The first cache line is read only */
+ queue_enq_fn_t enqueue ODP_ALIGNED_CACHE;
+ queue_deq_fn_t dequeue;
+ queue_enq_multi_fn_t enqueue_multi;
+ queue_deq_multi_fn_t dequeue_multi;
+ uint32_t *ring_data;
+ uint32_t ring_mask;
+ uint32_t index;
+ odp_queue_t handle;
+ odp_queue_type_t type;
+
+ /* MPMC ring (2 cache lines). */
+ ring_mpmc_u32_t ring_mpmc;
+
+ odp_ticketlock_t lock;
+ union {
+ ring_st_t ring_st;
+ ring_spsc_t ring_spsc;
+ };
+
+ odp_atomic_u64_t num_timers;
+ int status;
+
+ queue_deq_multi_fn_t orig_dequeue_multi;
+ odp_queue_param_t param;
+ odp_pktin_queue_t pktin;
+ odp_pktout_queue_t pktout;
+ void *queue_lf;
+ int spsc;
+ char name[ODP_QUEUE_NAME_LEN];
+} queue_entry_t;
+
+typedef struct queue_global_t {
+ queue_entry_t queue[CONFIG_MAX_QUEUES];
+ uint32_t *ring_data;
+ uint32_t queue_lf_num;
+ uint32_t queue_lf_size;
+ queue_lf_func_t queue_lf_func;
+ odp_shm_t queue_gbl_shm;
+ odp_shm_t queue_ring_shm;
+
+ struct {
+ uint32_t max_queue_size;
+ uint32_t default_queue_size;
+ } config;
+
+} queue_global_t;
+
+extern queue_global_t *_odp_queue_glb;
+
+static inline uint32_t queue_to_index(odp_queue_t handle)
+{
+ queue_entry_t *qentry = (queue_entry_t *)(uintptr_t)handle;
+
+ return qentry->index;
+}
+
+static inline queue_entry_t *qentry_from_index(uint32_t queue_id)
+{
+ return &_odp_queue_glb->queue[queue_id];
+}
+
+static inline odp_queue_t queue_from_index(uint32_t queue_id)
+{
+ return (odp_queue_t)qentry_from_index(queue_id);
+}
+
+static inline queue_entry_t *qentry_from_handle(odp_queue_t handle)
+{
+ return (queue_entry_t *)(uintptr_t)handle;
+}
+
+void _odp_queue_spsc_init(queue_entry_t *queue, uint32_t queue_size);
+
+/* Functions for schedulers */
+void _odp_sched_queue_set_status(uint32_t queue_index, int status);
+int _odp_sched_queue_deq(uint32_t queue_index, odp_event_t ev[], int num,
+ int update_status);
+int _odp_sched_queue_empty(uint32_t queue_index);
+
+/* Functions by schedulers */
+int _odp_sched_basic_get_spread(uint32_t queue_index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_queue_if.h b/platform/linux-generic/include/odp_queue_if.h
index 410c6b796..48be24b0a 100644
--- a/platform/linux-generic/include/odp_queue_if.h
+++ b/platform/linux-generic/include/odp_queue_if.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2017, ARM Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2021 Nokia
*/
#ifndef ODP_QUEUE_IF_H_
@@ -14,63 +13,35 @@ extern "C" {
#include <odp/api/queue.h>
#include <odp/api/schedule.h>
#include <odp/api/packet_io.h>
+
+#include <odp_event_internal.h>
#include <odp_forward_typedefs_internal.h>
#define QUEUE_MULTI_MAX CONFIG_BURST_SIZE
-/* Queue API functions */
-typedef struct {
- odp_queue_t (*queue_create)(const char *name,
- const odp_queue_param_t *param);
- int (*queue_destroy)(odp_queue_t queue);
- odp_queue_t (*queue_lookup)(const char *name);
- int (*queue_capability)(odp_queue_capability_t *capa);
- int (*queue_context_set)(odp_queue_t queue, void *context,
- uint32_t len);
- void *(*queue_context)(odp_queue_t queue);
- int (*queue_enq)(odp_queue_t queue, odp_event_t ev);
- int (*queue_enq_multi)(odp_queue_t queue, const odp_event_t events[],
- int num);
- odp_event_t (*queue_deq)(odp_queue_t queue);
- int (*queue_deq_multi)(odp_queue_t queue, odp_event_t events[],
- int num);
- odp_queue_type_t (*queue_type)(odp_queue_t queue);
- odp_schedule_sync_t (*queue_sched_type)(odp_queue_t queue);
- odp_schedule_prio_t (*queue_sched_prio)(odp_queue_t queue);
- odp_schedule_group_t (*queue_sched_group)(odp_queue_t queue);
- int (*queue_lock_count)(odp_queue_t queue);
- uint64_t (*queue_to_u64)(odp_queue_t hdl);
- void (*queue_param_init)(odp_queue_param_t *param);
- int (*queue_info)(odp_queue_t queue, odp_queue_info_t *info);
-} queue_api_t;
-
-/* Internal abstract queue handle */
-typedef struct { char dummy; } _queue_t;
-typedef _queue_t *queue_t;
-
-#define QUEUE_NULL ((queue_t)NULL)
-
typedef int (*queue_init_global_fn_t)(void);
typedef int (*queue_term_global_fn_t)(void);
typedef int (*queue_init_local_fn_t)(void);
typedef int (*queue_term_local_fn_t)(void);
-typedef queue_t (*queue_from_ext_fn_t)(odp_queue_t handle);
-typedef odp_queue_t (*queue_to_ext_fn_t)(queue_t q_int);
-typedef int (*queue_enq_fn_t)(queue_t q_int, odp_buffer_hdr_t *);
-typedef int (*queue_enq_multi_fn_t)(queue_t q_int, odp_buffer_hdr_t **, int);
-typedef odp_buffer_hdr_t *(*queue_deq_fn_t)(queue_t q_int);
-typedef int (*queue_deq_multi_fn_t)(queue_t q_int, odp_buffer_hdr_t **, int);
-typedef odp_pktout_queue_t (*queue_get_pktout_fn_t)(queue_t q_int);
-typedef void (*queue_set_pktout_fn_t)(queue_t q_int, odp_pktio_t pktio,
+typedef int (*queue_enq_fn_t)(odp_queue_t queue, _odp_event_hdr_t *event_hdr);
+typedef int (*queue_enq_multi_fn_t)(odp_queue_t queue,
+ _odp_event_hdr_t **event_hdr, int num);
+typedef _odp_event_hdr_t *(*queue_deq_fn_t)(odp_queue_t queue);
+typedef int (*queue_deq_multi_fn_t)(odp_queue_t queue,
+ _odp_event_hdr_t **event_hdr, int num);
+typedef odp_pktout_queue_t (*queue_get_pktout_fn_t)(odp_queue_t queue);
+typedef void (*queue_set_pktout_fn_t)(odp_queue_t queue, odp_pktio_t pktio,
int index);
-typedef odp_pktin_queue_t (*queue_get_pktin_fn_t)(queue_t q_int);
-typedef void (*queue_set_pktin_fn_t)(queue_t q_int, odp_pktio_t pktio,
+typedef odp_pktin_queue_t (*queue_get_pktin_fn_t)(odp_queue_t queue);
+typedef void (*queue_set_pktin_fn_t)(odp_queue_t queue, odp_pktio_t pktio,
int index);
-typedef void (*queue_set_enq_deq_fn_t)(queue_t q_int,
+typedef void (*queue_set_enq_deq_fn_t)(odp_queue_t queue,
queue_enq_fn_t enq,
queue_enq_multi_fn_t enq_multi,
queue_deq_fn_t deq,
queue_deq_multi_fn_t deq_multi);
+typedef void (*queue_timer_add_fn_t)(odp_queue_t queue);
+typedef void (*queue_timer_rem_fn_t)(odp_queue_t queue);
/* Queue functions towards other internal components */
typedef struct {
@@ -78,20 +49,20 @@ typedef struct {
queue_term_global_fn_t term_global;
queue_init_local_fn_t init_local;
queue_term_local_fn_t term_local;
- queue_from_ext_fn_t from_ext;
- queue_to_ext_fn_t to_ext;
- queue_enq_fn_t enq;
- queue_enq_multi_fn_t enq_multi;
- queue_deq_fn_t deq;
- queue_deq_multi_fn_t deq_multi;
queue_get_pktout_fn_t get_pktout;
queue_set_pktout_fn_t set_pktout;
queue_get_pktin_fn_t get_pktin;
queue_set_pktin_fn_t set_pktin;
queue_set_enq_deq_fn_t set_enq_deq_fn;
+ queue_timer_add_fn_t timer_add;
+ queue_timer_rem_fn_t timer_rem;
+
+ /* Original queue dequeue multi function (before override). May be used
+ * by an overriding dequeue function. */
+ queue_deq_multi_fn_t orig_deq_multi;
} queue_fn_t;
-extern const queue_fn_t *queue_fn;
+extern const queue_fn_t *_odp_queue_fn;
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_queue_internal.h b/platform/linux-generic/include/odp_queue_internal.h
deleted file mode 100644
index dd846d592..000000000
--- a/platform/linux-generic/include/odp_queue_internal.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-
-/**
- * @file
- *
- * ODP queue - implementation internal
- */
-
-#ifndef ODP_QUEUE_INTERNAL_H_
-#define ODP_QUEUE_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp/api/queue.h>
-#include <odp_forward_typedefs_internal.h>
-#include <odp_queue_if.h>
-#include <odp_buffer_internal.h>
-#include <odp_align_internal.h>
-#include <odp/api/packet_io.h>
-#include <odp/api/align.h>
-#include <odp/api/hints.h>
-#include <odp/api/ticketlock.h>
-#include <odp_config_internal.h>
-
-#define QUEUE_STATUS_FREE 0
-#define QUEUE_STATUS_DESTROYED 1
-#define QUEUE_STATUS_READY 2
-#define QUEUE_STATUS_NOTSCHED 3
-#define QUEUE_STATUS_SCHED 4
-
-struct queue_entry_s {
- odp_ticketlock_t lock ODP_ALIGNED_CACHE;
-
- odp_buffer_hdr_t *head;
- odp_buffer_hdr_t *tail;
- int status;
-
- queue_enq_fn_t enqueue ODP_ALIGNED_CACHE;
- queue_deq_fn_t dequeue;
- queue_enq_multi_fn_t enqueue_multi;
- queue_deq_multi_fn_t dequeue_multi;
-
- uint32_t index;
- odp_queue_t handle;
- odp_queue_type_t type;
- odp_queue_param_t param;
- odp_pktin_queue_t pktin;
- odp_pktout_queue_t pktout;
- char name[ODP_QUEUE_NAME_LEN];
-};
-
-union queue_entry_u {
- struct queue_entry_s s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct queue_entry_s))];
-};
-
-static inline uint32_t queue_to_id(odp_queue_t handle)
-{
- return _odp_typeval(handle) - 1;
-}
-
-static inline queue_entry_t *qentry_from_int(queue_t q_int)
-{
- return (queue_entry_t *)(void *)(q_int);
-}
-
-static inline queue_t qentry_to_int(queue_entry_t *qentry)
-{
- return (queue_t)(qentry);
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_queue_lf.h b/platform/linux-generic/include/odp_queue_lf.h
new file mode 100644
index 000000000..f58327520
--- /dev/null
+++ b/platform/linux-generic/include/odp_queue_lf.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_QUEUE_LF_H_
+#define ODP_QUEUE_LF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_queue_if.h>
+
+/* Lock-free queue functions */
+typedef struct {
+ queue_enq_fn_t enq;
+ queue_enq_multi_fn_t enq_multi;
+ queue_deq_fn_t deq;
+ queue_deq_multi_fn_t deq_multi;
+
+} queue_lf_func_t;
+
+uint32_t _odp_queue_lf_init_global(uint32_t *queue_lf_size,
+ queue_lf_func_t *lf_func);
+void _odp_queue_lf_term_global(void);
+void *_odp_queue_lf_create(queue_entry_t *queue);
+void _odp_queue_lf_destroy(void *queue_lf);
+uint32_t _odp_queue_lf_length(void *queue_lf);
+uint32_t _odp_queue_lf_max_length(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_queue_scalable_internal.h b/platform/linux-generic/include/odp_queue_scalable_internal.h
new file mode 100644
index 000000000..7ba668f5f
--- /dev/null
+++ b/platform/linux-generic/include/odp_queue_scalable_internal.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_QUEUE_SCALABLE_INTERNAL_H_
+#define ODP_QUEUE_SCALABLE_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/plat/strong_types.h>
+#include <odp/api/queue.h>
+#include <odp_forward_typedefs_internal.h>
+#include <odp_queue_if.h>
+#include <odp_event_internal.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/ticketlock.h>
+#include <odp_config_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_schedule_scalable.h>
+#include <odp_schedule_scalable_ordered.h>
+
+#define QUEUE_STATUS_FREE 0
+#define QUEUE_STATUS_DESTROYED 1
+#define QUEUE_STATUS_READY 2
+
+struct ODP_ALIGNED_CACHE queue_entry_s {
+ sched_elem_t sched_elem;
+
+ odp_ticketlock_t lock ODP_ALIGNED_CACHE;
+ odp_atomic_u64_t num_timers;
+ int status;
+
+ queue_enq_fn_t enqueue ODP_ALIGNED_CACHE;
+ queue_deq_fn_t dequeue;
+ queue_enq_multi_fn_t enqueue_multi;
+ queue_deq_multi_fn_t dequeue_multi;
+ queue_deq_multi_fn_t orig_dequeue_multi;
+
+ uint32_t index;
+ odp_queue_t handle;
+ odp_queue_type_t type;
+ odp_queue_param_t param;
+ odp_pktin_queue_t pktin;
+ odp_pktout_queue_t pktout;
+ char name[ODP_QUEUE_NAME_LEN];
+};
+
+int _odp_queue_deq(sched_elem_t *q, _odp_event_hdr_t *event_hdr[], int num);
+int _odp_queue_deq_sc(sched_elem_t *q, odp_event_t *evp, int num);
+int _odp_queue_deq_mc(sched_elem_t *q, odp_event_t *evp, int num);
+int _odp_queue_enq_sp(sched_elem_t *q, _odp_event_hdr_t *event_hdr[], int num);
+queue_entry_t *_odp_qentry_from_ext(odp_queue_t handle);
+
+/* Round up memory size to next cache line size to
+ * align all memory addresses on cache line boundary.
+ */
+static inline void *shm_pool_alloc_align(_odp_ishm_pool_t *pool, uint32_t size)
+{
+ void *addr;
+
+ addr = _odp_ishm_pool_alloc(pool, _ODP_ROUNDUP_CACHE_LINE(size));
+ _ODP_ASSERT(((uintptr_t)addr & (ODP_CACHE_LINE_SIZE - 1)) == 0);
+
+ return addr;
+}
+
+static inline uint32_t queue_to_id(odp_queue_t handle)
+{
+ return _odp_qentry_from_ext(handle)->index;
+}
+
+static inline queue_entry_t *qentry_from_int(odp_queue_t handle)
+{
+ return (queue_entry_t *)(uintptr_t)handle;
+}
+
+static inline odp_queue_t qentry_to_int(queue_entry_t *qentry)
+{
+ return (odp_queue_t)qentry;
+}
+
+static inline odp_queue_t queue_get_handle(queue_entry_t *queue)
+{
+ return queue->handle;
+}
+
+static inline reorder_window_t *queue_get_rwin(queue_entry_t *queue)
+{
+ return queue->sched_elem.rwin;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_random_openssl_internal.h b/platform/linux-generic/include/odp_random_openssl_internal.h
new file mode 100644
index 000000000..fb4b78e81
--- /dev/null
+++ b/platform/linux-generic/include/odp_random_openssl_internal.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef ODP_RANDOM_OPENSSL_INTERNAL_H_
+#define ODP_RANDOM_OPENSSL_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+int32_t _odp_random_openssl_data(uint8_t *buf, uint32_t len);
+int _odp_random_openssl_init_local(void);
+int _odp_random_openssl_term_local(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_RANDOM_OPENSSL_INTERNAL_H_ */
diff --git a/platform/linux-generic/include/odp_random_std_internal.h b/platform/linux-generic/include/odp_random_std_internal.h
new file mode 100644
index 000000000..22583089c
--- /dev/null
+++ b/platform/linux-generic/include/odp_random_std_internal.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef ODP_RANDOM_STD_INTERNAL_H_
+#define ODP_RANDOM_STD_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+int32_t _odp_random_std_test_data(uint8_t *buf, uint32_t len, uint64_t *seed);
+int32_t _odp_random_std_data(uint8_t *buf, uint32_t len);
+int _odp_random_std_init_local(void);
+int _odp_random_std_term_local(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_RANDOM_STD_INTERNAL_H_ */
diff --git a/platform/linux-generic/include/odp_ring_common.h b/platform/linux-generic/include/odp_ring_common.h
new file mode 100644
index 000000000..7c28abde8
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_common.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019-2021 Nokia
+ */
+
+#ifndef ODP_RING_COMMON_H_
+#define ODP_RING_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _ODP_RING_TYPE_U32 1
+#define _ODP_RING_TYPE_U64 2
+#define _ODP_RING_TYPE_PTR 3
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_internal.h b/platform/linux-generic/include/odp_ring_internal.h
index 44b83c603..53c5e944c 100644
--- a/platform/linux-generic/include/odp_ring_internal.h
+++ b/platform/linux-generic/include/odp_ring_internal.h
@@ -1,9 +1,11 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
+/* This header should NOT be included directly. There are no include guards for
+ * the function definitions! */
+
#ifndef ODP_RING_INTERNAL_H_
#define ODP_RING_INTERNAL_H_
@@ -11,86 +13,174 @@
extern "C" {
#endif
+#include <odp/api/align.h>
#include <odp/api/atomic.h>
+#include <odp/api/cpu.h>
#include <odp/api/hints.h>
-#include <odp_align_internal.h>
-/* Ring empty, not a valid data value. */
-#define RING_EMPTY ((uint32_t)-1)
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/plat/cpu_inlines.h>
+
+#include <odp_ring_common.h>
-/* Ring of uint32_t data
+/* Generic ring implementation
*
* Ring stores head and tail counters. Ring indexes are formed from these
* counters with a mask (mask = ring_size - 1), which requires that ring size
* must be a power of two. Also ring size must be larger than the maximum
- * number of data items that will be stored on it (there's no check against
- * overwriting). */
-typedef struct {
+ * number of data items that will be stored on it as write operations are
+ * assumed to succeed eventually (after readers complete their current
+ * operations). */
+
+struct ring_common {
/* Writer head and tail */
- odp_atomic_u32_t w_head;
- odp_atomic_u32_t w_tail;
- uint8_t pad[ODP_CACHE_LINE_SIZE - (2 * sizeof(odp_atomic_u32_t))];
+ struct ODP_ALIGNED_CACHE {
+ odp_atomic_u32_t w_head;
+ odp_atomic_u32_t w_tail;
+ };
/* Reader head and tail */
- odp_atomic_u32_t r_head;
- odp_atomic_u32_t r_tail;
+ struct ODP_ALIGNED_CACHE {
+ odp_atomic_u32_t r_head;
+ odp_atomic_u32_t r_tail;
+ };
+};
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct ring_common r;
+ uint32_t data[];
+} ring_u32_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct ring_common r;
+ uint64_t data[];
+} ring_u64_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct ring_common r;
+ void *data[];
+} ring_ptr_t;
+
+/* 32-bit CAS with memory order selection */
+static inline int cas_mo_u32(odp_atomic_u32_t *atom, uint32_t *old_val,
+ uint32_t new_val, int mo_success, int mo_failure)
+{
+ return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
+ 0 /* strong */,
+ mo_success,
+ mo_failure);
+}
- uint32_t data[0];
-} ring_t ODP_ALIGNED_CACHE;
+#endif /* End of include guards */
+
+#undef _ring_gen_t
+#undef _ring_data_t
+#undef _RING_INIT
+#undef _RING_DEQ
+#undef _RING_DEQ_MULTI
+#undef _RING_DEQ_BATCH
+#undef _RING_ENQ
+#undef _RING_ENQ_MULTI
+#undef _RING_LEN
+
+/* Remap generic types and function names to ring data type specific ones. One
+ * should never use the generic names (e.g. _RING_INIT) directly. */
+
+#if _ODP_RING_TYPE == _ODP_RING_TYPE_U32
+ #define _ring_gen_t ring_u32_t
+ #define _ring_data_t uint32_t
+
+ #define _RING_INIT ring_u32_init
+ #define _RING_DEQ ring_u32_deq
+ #define _RING_DEQ_MULTI ring_u32_deq_multi
+ #define _RING_DEQ_BATCH ring_u32_deq_batch
+ #define _RING_ENQ ring_u32_enq
+ #define _RING_ENQ_MULTI ring_u32_enq_multi
+ #define _RING_LEN ring_u32_len
+#elif _ODP_RING_TYPE == _ODP_RING_TYPE_U64
+ #define _ring_gen_t ring_u64_t
+ #define _ring_data_t uint64_t
+
+ #define _RING_INIT ring_u64_init
+ #define _RING_DEQ ring_u64_deq
+ #define _RING_DEQ_MULTI ring_u64_deq_multi
+ #define _RING_DEQ_BATCH ring_u64_deq_batch
+ #define _RING_ENQ ring_u64_enq
+ #define _RING_ENQ_MULTI ring_u64_enq_multi
+ #define _RING_LEN ring_u64_len
+#elif _ODP_RING_TYPE == _ODP_RING_TYPE_PTR
+ #define _ring_gen_t ring_ptr_t
+ #define _ring_data_t void *
+
+ #define _RING_INIT ring_ptr_init
+ #define _RING_DEQ ring_ptr_deq
+ #define _RING_DEQ_MULTI ring_ptr_deq_multi
+ #define _RING_DEQ_BATCH ring_ptr_deq_batch
+ #define _RING_ENQ ring_ptr_enq
+ #define _RING_ENQ_MULTI ring_ptr_enq_multi
+ #define _RING_LEN ring_ptr_len
+#endif
/* Initialize ring */
-static inline void ring_init(ring_t *ring)
+static inline void _RING_INIT(_ring_gen_t *ring)
{
- odp_atomic_init_u32(&ring->w_head, 0);
- odp_atomic_init_u32(&ring->w_tail, 0);
- odp_atomic_init_u32(&ring->r_head, 0);
- odp_atomic_init_u32(&ring->r_tail, 0);
+ odp_atomic_init_u32(&ring->r.w_head, 0);
+ odp_atomic_init_u32(&ring->r.w_tail, 0);
+ odp_atomic_init_u32(&ring->r.r_head, 0);
+ odp_atomic_init_u32(&ring->r.r_tail, 0);
}
/* Dequeue data from the ring head */
-static inline uint32_t ring_deq(ring_t *ring, uint32_t mask)
+static inline uint32_t _RING_DEQ(_ring_gen_t *ring, uint32_t mask,
+ _ring_data_t *data)
{
uint32_t head, tail, new_head;
- uint32_t data;
- head = odp_atomic_load_u32(&ring->r_head);
+ /* Load/CAS acquire of r_head ensures that w_tail load happens after
+ * r_head load, and thus head value is always behind or equal to tail
+ * value. */
+ head = odp_atomic_load_acq_u32(&ring->r.r_head);
/* Move reader head. This thread owns data at the new head. */
do {
- tail = odp_atomic_load_acq_u32(&ring->w_tail);
+ tail = odp_atomic_load_acq_u32(&ring->r.w_tail);
if (head == tail)
- return RING_EMPTY;
+ return 0;
new_head = head + 1;
- } while (odp_unlikely(odp_atomic_cas_acq_u32(&ring->r_head, &head,
- new_head) == 0));
+ } while (odp_unlikely(cas_mo_u32(&ring->r.r_head, &head, new_head,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_ACQUIRE) == 0));
- /* Read queue index */
- data = ring->data[new_head & mask];
+ /* Read data. */
+ *data = ring->data[new_head & mask];
/* Wait until other readers have updated the tail */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->r_tail) != head))
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.r_tail) != head))
odp_cpu_pause();
- /* Now update the reader tail */
- odp_atomic_store_rel_u32(&ring->r_tail, new_head);
+ /* Update the tail. Writers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.r_tail, new_head);
- return data;
+ return 1;
}
/* Dequeue multiple data from the ring head. Num is smaller than ring size. */
-static inline uint32_t ring_deq_multi(ring_t *ring, uint32_t mask,
- uint32_t data[], uint32_t num)
+static inline uint32_t _RING_DEQ_MULTI(_ring_gen_t *ring, uint32_t mask,
+ _ring_data_t data[], uint32_t num)
{
uint32_t head, tail, new_head, i;
- head = odp_atomic_load_u32(&ring->r_head);
+ /* Load/CAS acquire of r_head ensures that w_tail load happens after
+ * r_head load, and thus head value is always behind or equal to tail
+ * value. */
+ head = odp_atomic_load_acq_u32(&ring->r.r_head);
/* Move reader head. This thread owns data at the new head. */
do {
- tail = odp_atomic_load_acq_u32(&ring->w_tail);
+ tail = odp_atomic_load_acq_u32(&ring->r.w_tail);
/* Ring is empty */
if (head == tail)
@@ -102,59 +192,108 @@ static inline uint32_t ring_deq_multi(ring_t *ring, uint32_t mask,
new_head = head + num;
- } while (odp_unlikely(odp_atomic_cas_acq_u32(&ring->r_head, &head,
- new_head) == 0));
+ } while (odp_unlikely(cas_mo_u32(&ring->r.r_head, &head, new_head,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_ACQUIRE) == 0));
- /* Read queue index */
+ /* Read data. */
for (i = 0; i < num; i++)
data[i] = ring->data[(head + 1 + i) & mask];
/* Wait until other readers have updated the tail */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->r_tail) != head))
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.r_tail) != head))
odp_cpu_pause();
- /* Now update the reader tail */
- odp_atomic_store_rel_u32(&ring->r_tail, new_head);
+ /* Update the tail. Writers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.r_tail, new_head);
+
+ return num;
+}
+
+/* Dequeue batch of data (0 or num) from the ring head. Num is smaller than ring size. */
+static inline uint32_t _RING_DEQ_BATCH(_ring_gen_t *ring, uint32_t mask,
+ _ring_data_t data[], uint32_t num)
+{
+ uint32_t head, tail, new_head, i;
+
+ /* Load/CAS acquire of r_head ensures that w_tail load happens after
+ * r_head load, and thus head value is always behind or equal to tail
+ * value. */
+ head = odp_atomic_load_acq_u32(&ring->r.r_head);
+
+ /* Move reader head. This thread owns data at the new head. */
+ do {
+ tail = odp_atomic_load_acq_u32(&ring->r.w_tail);
+
+ /* Not enough data available */
+ if ((tail - head) < num)
+ return 0;
+
+ new_head = head + num;
+
+ } while (odp_unlikely(cas_mo_u32(&ring->r.r_head, &head, new_head,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_ACQUIRE) == 0));
+
+ /* Read data. */
+ for (i = 0; i < num; i++)
+ data[i] = ring->data[(head + 1 + i) & mask];
+
+ /* Wait until other readers have updated the tail */
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.r_tail) != head))
+ odp_cpu_pause();
+
+ /* Update the tail. Writers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.r_tail, new_head);
return num;
}
/* Enqueue data into the ring tail */
-static inline void ring_enq(ring_t *ring, uint32_t mask, uint32_t data)
+static inline void _RING_ENQ(_ring_gen_t *ring, uint32_t mask,
+ _ring_data_t data)
{
uint32_t old_head, new_head;
+ uint32_t size = mask + 1;
/* Reserve a slot in the ring for writing */
- old_head = odp_atomic_fetch_inc_u32(&ring->w_head);
+ old_head = odp_atomic_fetch_inc_u32(&ring->r.w_head);
new_head = old_head + 1;
- /* Ring is full. Wait for the last reader to finish. */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->r_tail) == new_head))
+ /* Wait for the last reader to finish. This prevents overwrite when
+ * a reader has been left behind (e.g. due to an interrupt) and is
+ * still reading the same slot. */
+ while (odp_unlikely(new_head - odp_atomic_load_acq_u32(&ring->r.r_tail)
+ >= size))
odp_cpu_pause();
/* Write data */
ring->data[new_head & mask] = data;
/* Wait until other writers have updated the tail */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->w_tail) != old_head))
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.w_tail) != old_head))
odp_cpu_pause();
- /* Now update the writer tail */
- odp_atomic_store_rel_u32(&ring->w_tail, new_head);
+ /* Release the new writer tail, readers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.w_tail, new_head);
}
/* Enqueue multiple data into the ring tail. Num is smaller than ring size. */
-static inline void ring_enq_multi(ring_t *ring, uint32_t mask, uint32_t data[],
- uint32_t num)
+static inline void _RING_ENQ_MULTI(_ring_gen_t *ring, uint32_t mask,
+ _ring_data_t data[], uint32_t num)
{
uint32_t old_head, new_head, i;
+ uint32_t size = mask + 1;
/* Reserve a slot in the ring for writing */
- old_head = odp_atomic_fetch_add_u32(&ring->w_head, num);
+ old_head = odp_atomic_fetch_add_u32(&ring->r.w_head, num);
new_head = old_head + 1;
- /* Ring is full. Wait for the last reader to finish. */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->r_tail) == new_head))
+ /* Wait for the last reader to finish. This prevents overwrite when
+ * a reader has been left behind (e.g. due to an interrupt) and is
+ * still reading these slots. */
+ while (odp_unlikely(new_head - odp_atomic_load_acq_u32(&ring->r.r_tail)
+ >= size))
odp_cpu_pause();
/* Write data */
@@ -162,15 +301,21 @@ static inline void ring_enq_multi(ring_t *ring, uint32_t mask, uint32_t data[],
ring->data[(new_head + i) & mask] = data[i];
/* Wait until other writers have updated the tail */
- while (odp_unlikely(odp_atomic_load_acq_u32(&ring->w_tail) != old_head))
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.w_tail) != old_head))
odp_cpu_pause();
- /* Now update the writer tail */
- odp_atomic_store_rel_u32(&ring->w_tail, old_head + num);
+ /* Release the new writer tail, readers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.w_tail, old_head + num);
}
-#ifdef __cplusplus
+static inline uint32_t _RING_LEN(_ring_gen_t *ring)
+{
+ uint32_t head = odp_atomic_load_u32(&ring->r.r_head);
+ uint32_t tail = odp_atomic_load_u32(&ring->r.w_tail);
+
+ return tail - head;
}
-#endif
+#ifdef __cplusplus
+}
#endif
diff --git a/platform/linux-generic/include/odp_ring_mpmc_internal.h b/platform/linux-generic/include/odp_ring_mpmc_internal.h
new file mode 100644
index 000000000..63e86baca
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_mpmc_internal.h
@@ -0,0 +1,348 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_RING_MPMC_INTERNAL_H_
+#define ODP_RING_MPMC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/align.h>
+#include <odp/api/atomic.h>
+#include <odp/api/cpu.h>
+#include <odp/api/hints.h>
+
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/plat/cpu_inlines.h>
+
+#include <odp_ring_common.h>
+
+/* Ring of uint32_t/uint64_t data
+ *
+ * Ring stores head and tail counters. Ring indexes are formed from these
+ * counters with a mask (mask = ring_size - 1), which requires that ring size
+ * must be a power of two.
+ *
+ * The following figures depict an example where a ring is being simultaneously
+ * enqueued to and dequeued from. Ring slots containing data are marked with
+ * letter D, empty slots with E, and slots being modified with X.
+ *
+ * Ring status before enq/deq operations.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | E | E | D | D | D | D | D | D | E | E | E | E | E | E | E | E |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * ^ ^
+ * | |
+ * r_head w_head
+ * r_tail w_tail
+ *
+ * Ring status while being enqueued and dequeued.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | E | E | X | X | D | D | D | D | X | X | X | E | E | E | E | E |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * ^ ^ ^ ^
+ * | | | |
+ * r_tail r_head w_tail w_head
+ *
+ * Ring status after enq/deq operations.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | E | E | E | E | D | D | D | D | D | D | D | E | E | E | E | E |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * ^ ^
+ * | |
+ * r_head w_head
+ * r_tail w_tail
+ */
+
+struct ring_mpmc_common {
+ odp_atomic_u32_t r_head ODP_ALIGNED_CACHE;
+ odp_atomic_u32_t r_tail;
+
+ odp_atomic_u32_t w_head ODP_ALIGNED_CACHE;
+ odp_atomic_u32_t w_tail;
+};
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct ring_mpmc_common r;
+} ring_mpmc_u32_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct ring_mpmc_common r;
+} ring_mpmc_u64_t;
+
+static inline int ring_mpmc_cas_u32(odp_atomic_u32_t *atom,
+ uint32_t *old_val, uint32_t new_val)
+{
+ return __atomic_compare_exchange_n(&atom->v, old_val, new_val,
+ 0 /* strong */,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED);
+}
+
+#endif /* End of include guards */
+
+#undef _ring_mpmc_gen_t
+#undef _ring_mpmc_data_t
+#undef _RING_MPMC_INIT
+#undef _RING_MPMC_DEQ_MULTI
+#undef _RING_MPMC_ENQ_MULTI
+#undef _RING_MPMC_DEQ_BATCH
+#undef _RING_MPMC_ENQ_BATCH
+#undef _RING_MPMC_IS_EMPTY
+#undef _RING_MPMC_LEN
+
+/* This header should NOT be included directly. There are no include guards for
+ * the following types and function definitions! */
+#ifndef _ODP_RING_TYPE
+#error Include type specific (u32/u64) ring header instead of this common file.
+#endif
+
+#if _ODP_RING_TYPE == _ODP_RING_TYPE_U32
+ #define _ring_mpmc_gen_t ring_mpmc_u32_t
+ #define _ring_mpmc_data_t uint32_t
+
+ #define _RING_MPMC_INIT ring_mpmc_u32_init
+ #define _RING_MPMC_DEQ_MULTI ring_mpmc_u32_deq_multi
+ #define _RING_MPMC_ENQ_MULTI ring_mpmc_u32_enq_multi
+ #define _RING_MPMC_DEQ_BATCH ring_mpmc_u32_deq_batch
+ #define _RING_MPMC_ENQ_BATCH ring_mpmc_u32_enq_batch
+ #define _RING_MPMC_IS_EMPTY ring_mpmc_u32_is_empty
+ #define _RING_MPMC_LEN ring_mpmc_u32_len
+#elif _ODP_RING_TYPE == _ODP_RING_TYPE_U64
+ #define _ring_mpmc_gen_t ring_mpmc_u64_t
+ #define _ring_mpmc_data_t uint64_t
+
+ #define _RING_MPMC_INIT ring_mpmc_u64_init
+ #define _RING_MPMC_DEQ_MULTI ring_mpmc_u64_deq_multi
+ #define _RING_MPMC_ENQ_MULTI ring_mpmc_u64_enq_multi
+ #define _RING_MPMC_DEQ_BATCH ring_mpmc_u64_deq_batch
+ #define _RING_MPMC_ENQ_BATCH ring_mpmc_u64_enq_batch
+ #define _RING_MPMC_IS_EMPTY ring_mpmc_u64_is_empty
+ #define _RING_MPMC_LEN ring_mpmc_u64_len
+#endif
+
+/* Initialize ring */
+static inline void _RING_MPMC_INIT(_ring_mpmc_gen_t *ring)
+{
+ odp_atomic_init_u32(&ring->r.w_head, 0);
+ odp_atomic_init_u32(&ring->r.w_tail, 0);
+ odp_atomic_init_u32(&ring->r.r_head, 0);
+ odp_atomic_init_u32(&ring->r.r_tail, 0);
+}
+
+/* Dequeue data from the ring head */
+static inline uint32_t _RING_MPMC_DEQ_MULTI(_ring_mpmc_gen_t *ring,
+ _ring_mpmc_data_t *ring_data,
+ uint32_t ring_mask,
+ _ring_mpmc_data_t data[],
+ uint32_t num)
+{
+ uint32_t old_head, new_head, w_tail, num_data, i;
+
+ /* Load acquires ensure that w_tail load happens after r_head load,
+ * and thus r_head value is always behind or equal to w_tail value.
+ * When CAS operation succeeds, this thread owns data between old
+ * and new r_head. */
+ do {
+ old_head = odp_atomic_load_acq_u32(&ring->r.r_head);
+ odp_prefetch(&ring_data[(old_head + 1) & ring_mask]);
+ w_tail = odp_atomic_load_acq_u32(&ring->r.w_tail);
+ num_data = w_tail - old_head;
+
+ /* Ring is empty */
+ if (num_data == 0)
+ return 0;
+
+ /* Try to take all available */
+ if (num > num_data)
+ num = num_data;
+
+ new_head = old_head + num;
+
+ } while (odp_unlikely(ring_mpmc_cas_u32(&ring->r.r_head, &old_head,
+ new_head) == 0));
+
+ /* Read data. This will not move above load acquire of r_head. */
+ for (i = 0; i < num; i++)
+ data[i] = ring_data[(old_head + 1 + i) & ring_mask];
+
+ /* Wait until other readers have updated the tail */
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.r_tail) != old_head))
+ odp_cpu_pause();
+
+ /* Release the new reader tail, writers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.r_tail, new_head);
+
+ return num;
+}
+
+/* Dequeue num or 0 data from the ring head */
+static inline uint32_t _RING_MPMC_DEQ_BATCH(_ring_mpmc_gen_t *ring,
+ _ring_mpmc_data_t *ring_data,
+ uint32_t ring_mask,
+ _ring_mpmc_data_t data[],
+ uint32_t num)
+{
+ uint32_t old_head, new_head, w_tail, num_data, i;
+
+ /* Load acquires ensure that w_tail load happens after r_head load,
+ * and thus r_head value is always behind or equal to w_tail value.
+ * When CAS operation succeeds, this thread owns data between old
+ * and new r_head. */
+ do {
+ old_head = odp_atomic_load_acq_u32(&ring->r.r_head);
+ odp_prefetch(&ring_data[(old_head + 1) & ring_mask]);
+ w_tail = odp_atomic_load_acq_u32(&ring->r.w_tail);
+ num_data = w_tail - old_head;
+
+ /* Not enough data available */
+ if (num_data < num)
+ return 0;
+
+ new_head = old_head + num;
+
+ } while (odp_unlikely(ring_mpmc_cas_u32(&ring->r.r_head, &old_head,
+ new_head) == 0));
+
+ /* Read data. This will not move above load acquire of r_head. */
+ for (i = 0; i < num; i++)
+ data[i] = ring_data[(old_head + 1 + i) & ring_mask];
+
+ /* Wait until other readers have updated the tail */
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.r_tail) != old_head))
+ odp_cpu_pause();
+
+ /* Release the new reader tail, writers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.r_tail, new_head);
+
+ return num;
+}
+
+/* Enqueue multiple data into the ring tail */
+static inline uint32_t _RING_MPMC_ENQ_MULTI(_ring_mpmc_gen_t *ring,
+ _ring_mpmc_data_t *ring_data,
+ uint32_t ring_mask,
+ const _ring_mpmc_data_t data[],
+ uint32_t num)
+{
+ uint32_t old_head, new_head, r_tail, num_free, i;
+ uint32_t size = ring_mask + 1;
+
+ /* The CAS operation guarantees that w_head value is up to date. Load
+ * acquire is used to ensure that r_tail is read after w_head. This
+ * guarantees that w_head - r_tail <= size. Any additional delay in
+ * reading r_tail makes the subtraction result only smaller. This
+ * avoids returning zero when the ring is not actually full.
+ *
+ * When CAS operation succeeds, this thread owns data between old and
+ * new w_head. */
+ do {
+ old_head = odp_atomic_load_acq_u32(&ring->r.w_head);
+ r_tail = odp_atomic_load_acq_u32(&ring->r.r_tail);
+
+ num_free = size - (old_head - r_tail);
+
+ /* Ring is full */
+ if (num_free == 0)
+ return 0;
+
+ /* Try to use all available */
+ if (num > num_free)
+ num = num_free;
+
+ new_head = old_head + num;
+
+ } while (odp_unlikely(ring_mpmc_cas_u32(&ring->r.w_head, &old_head,
+ new_head) == 0));
+
+ /* Write data. This will not move above load acquire of w_head. */
+ for (i = 0; i < num; i++)
+ ring_data[(old_head + 1 + i) & ring_mask] = data[i];
+
+ /* Wait until other writers have updated the tail */
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.w_tail) != old_head))
+ odp_cpu_pause();
+
+ /* Release the new writer tail, readers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.w_tail, new_head);
+
+ return num;
+}
+
+/* Enqueue num or 0 data into the ring tail */
+static inline uint32_t _RING_MPMC_ENQ_BATCH(_ring_mpmc_gen_t *ring,
+ _ring_mpmc_data_t *ring_data,
+ uint32_t ring_mask,
+ const _ring_mpmc_data_t data[],
+ uint32_t num)
+{
+ uint32_t old_head, new_head, r_tail, num_free, i;
+ uint32_t size = ring_mask + 1;
+
+ /* The CAS operation guarantees that w_head value is up to date. Load
+ * acquire is used to ensure that r_tail is read after w_head. This
+ * guarantees that w_head - r_tail <= size. Any additional delay in
+ * reading r_tail makes the subtraction result only smaller. This
+ * avoids returning zero when the ring is not actually full.
+ *
+ * When CAS operation succeeds, this thread owns data between old and
+ * new w_head. */
+ do {
+ old_head = odp_atomic_load_acq_u32(&ring->r.w_head);
+ r_tail = odp_atomic_load_acq_u32(&ring->r.r_tail);
+
+ num_free = size - (old_head - r_tail);
+
+ /* Not enough free space available */
+ if (num_free < num)
+ return 0;
+
+ new_head = old_head + num;
+
+ } while (odp_unlikely(ring_mpmc_cas_u32(&ring->r.w_head, &old_head,
+ new_head) == 0));
+
+ /* Write data. This will not move above load acquire of w_head. */
+ for (i = 0; i < num; i++)
+ ring_data[(old_head + 1 + i) & ring_mask] = data[i];
+
+ /* Wait until other writers have updated the tail */
+ while (odp_unlikely(odp_atomic_load_u32(&ring->r.w_tail) != old_head))
+ odp_cpu_pause();
+
+ /* Release the new writer tail, readers acquire it. */
+ odp_atomic_store_rel_u32(&ring->r.w_tail, new_head);
+
+ return num;
+}
+
+/* Check if ring is empty */
+static inline int _RING_MPMC_IS_EMPTY(_ring_mpmc_gen_t *ring)
+{
+ uint32_t head = odp_atomic_load_u32(&ring->r.r_head);
+ uint32_t tail = odp_atomic_load_u32(&ring->r.w_tail);
+
+ return head == tail;
+}
+
+/* Return current ring length */
+static inline uint32_t _RING_MPMC_LEN(_ring_mpmc_gen_t *ring)
+{
+ uint32_t head = odp_atomic_load_u32(&ring->r.r_head);
+ uint32_t tail = odp_atomic_load_u32(&ring->r.w_tail);
+
+ return tail - head;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/include/odp_ring_mpmc_u32_internal.h b/platform/linux-generic/include/odp_ring_mpmc_u32_internal.h
new file mode 100644
index 000000000..b5896f9d4
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_mpmc_u32_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_RING_MPMC_U32_INTERNAL_H_
+#define ODP_RING_MPMC_U32_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ring_common.h>
+
+#undef _ODP_RING_TYPE
+#define _ODP_RING_TYPE _ODP_RING_TYPE_U32
+
+#include <odp_ring_mpmc_internal.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_mpmc_u64_internal.h b/platform/linux-generic/include/odp_ring_mpmc_u64_internal.h
new file mode 100644
index 000000000..a77b38c3f
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_mpmc_u64_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef ODP_RING_MPMC_U64_INTERNAL_H_
+#define ODP_RING_MPMC_U64_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ring_common.h>
+
+#undef _ODP_RING_TYPE
+#define _ODP_RING_TYPE _ODP_RING_TYPE_U64
+
+#include <odp_ring_mpmc_internal.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_ptr_internal.h b/platform/linux-generic/include/odp_ring_ptr_internal.h
new file mode 100644
index 000000000..ee3c29ff0
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_ptr_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#ifndef ODP_RING_PTR_INTERNAL_H_
+#define ODP_RING_PTR_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ring_common.h>
+
+#undef _ODP_RING_TYPE
+#define _ODP_RING_TYPE _ODP_RING_TYPE_PTR
+
+#include <odp_ring_internal.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_spsc_internal.h b/platform/linux-generic/include/odp_ring_spsc_internal.h
new file mode 100644
index 000000000..0b774f197
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_spsc_internal.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_RING_SPSC_INTERNAL_H_
+#define ODP_RING_SPSC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <odp/api/atomic.h>
+#include <odp/api/plat/atomic_inlines.h>
+
+/* Lock-free ring for single-producer / single-consumer usage.
+ *
+ * Thread doing an operation may be different each time, but the same operation
+ * (enq- or dequeue) must not be called concurrently. The next thread may call
+ * the same operation only when it's sure that the previous thread have returned
+ * from the call, or will never return back to finish the call when interrupted
+ * during the call.
+ *
+ * Enqueue and dequeue operations can be done concurrently.
+ */
+typedef struct {
+ odp_atomic_u32_t head;
+ odp_atomic_u32_t tail;
+
+} ring_spsc_t;
+
+/* Initialize ring. Ring size must be a power of two. */
+static inline void ring_spsc_init(ring_spsc_t *ring)
+{
+ odp_atomic_init_u32(&ring->head, 0);
+ odp_atomic_init_u32(&ring->tail, 0);
+}
+
+/* Dequeue data from the ring head. Max_num is smaller than ring size.*/
+static inline uint32_t ring_spsc_deq_multi(ring_spsc_t *ring,
+ uint32_t *ring_data,
+ uint32_t ring_mask, uint32_t data[],
+ uint32_t max_num)
+{
+ uint32_t head, tail, idx;
+ uint32_t num, i;
+
+ tail = odp_atomic_load_acq_u32(&ring->tail);
+ head = odp_atomic_load_u32(&ring->head);
+ num = tail - head;
+
+ /* Empty */
+ if (num == 0)
+ return 0;
+
+ if (num > max_num)
+ num = max_num;
+
+ idx = head & ring_mask;
+
+ for (i = 0; i < num; i++) {
+ data[i] = ring_data[idx];
+ idx = (idx + 1) & ring_mask;
+ }
+
+ odp_atomic_store_rel_u32(&ring->head, head + num);
+
+ return num;
+}
+
+/* Enqueue data into the ring tail. Num_data is smaller than ring size. */
+static inline uint32_t ring_spsc_enq_multi(ring_spsc_t *ring,
+ uint32_t *ring_data,
+ uint32_t ring_mask,
+ const uint32_t data[],
+ uint32_t num_data)
+{
+ uint32_t head, tail, size, idx;
+ uint32_t num, i;
+
+ head = odp_atomic_load_acq_u32(&ring->head);
+ tail = odp_atomic_load_u32(&ring->tail);
+ size = ring_mask + 1;
+ num = size - (tail - head);
+
+ /* Full */
+ if (num == 0)
+ return 0;
+
+ if (num > num_data)
+ num = num_data;
+
+ idx = tail & ring_mask;
+
+ for (i = 0; i < num; i++) {
+ ring_data[idx] = data[i];
+ idx = (idx + 1) & ring_mask;
+ }
+
+ odp_atomic_store_rel_u32(&ring->tail, tail + num);
+
+ return num;
+}
+
+/* Check if ring is empty */
+static inline int ring_spsc_is_empty(ring_spsc_t *ring)
+{
+ uint32_t head = odp_atomic_load_u32(&ring->head);
+ uint32_t tail = odp_atomic_load_u32(&ring->tail);
+
+ return head == tail;
+}
+
+/* Return current ring length */
+static inline uint32_t ring_spsc_length(ring_spsc_t *ring)
+{
+ uint32_t head = odp_atomic_load_u32(&ring->head);
+ uint32_t tail = odp_atomic_load_u32(&ring->tail);
+
+ return tail - head;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_st_internal.h b/platform/linux-generic/include/odp_ring_st_internal.h
new file mode 100644
index 000000000..543ca9cb2
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_st_internal.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef ODP_RING_ST_INTERNAL_H_
+#define ODP_RING_ST_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+
+/* Basic ring for single thread usage. Operations must be synchronized by using
+ * locks (or other means), when multiple threads use the same ring. */
+typedef struct {
+ uint32_t head;
+ uint32_t tail;
+} ring_st_t;
+
+/* Initialize ring. Ring size must be a power of two. */
+static inline void ring_st_init(ring_st_t *ring)
+{
+ ring->head = 0;
+ ring->tail = 0;
+}
+
+/* Dequeue data from the ring head. Max_num is smaller than ring size.*/
+static inline uint32_t ring_st_deq_multi(ring_st_t *ring, uint32_t *ring_data,
+ uint32_t ring_mask, uint32_t data[],
+ uint32_t max_num)
+{
+ uint32_t head, tail, idx;
+ uint32_t num, i;
+
+ head = ring->head;
+ tail = ring->tail;
+ num = tail - head;
+
+ /* Empty */
+ if (num == 0)
+ return 0;
+
+ if (num > max_num)
+ num = max_num;
+
+ idx = head & ring_mask;
+
+ for (i = 0; i < num; i++) {
+ data[i] = ring_data[idx];
+ idx = (idx + 1) & ring_mask;
+ }
+
+ ring->head = head + num;
+
+ return num;
+}
+
+/* Enqueue data into the ring tail. Num_data is smaller than ring size. */
+static inline uint32_t ring_st_enq_multi(ring_st_t *ring, uint32_t *ring_data,
+ uint32_t ring_mask,
+ const uint32_t data[],
+ uint32_t num_data)
+{
+ uint32_t head, tail, size, idx;
+ uint32_t num, i;
+
+ head = ring->head;
+ tail = ring->tail;
+ size = ring_mask + 1;
+ num = size - (tail - head);
+
+ /* Full */
+ if (num == 0)
+ return 0;
+
+ if (num > num_data)
+ num = num_data;
+
+ idx = tail & ring_mask;
+
+ for (i = 0; i < num; i++) {
+ ring_data[idx] = data[i];
+ idx = (idx + 1) & ring_mask;
+ }
+
+ ring->tail = tail + num;
+
+ return num;
+}
+
+/* Check if ring is empty */
+static inline int ring_st_is_empty(ring_st_t *ring)
+{
+ return ring->head == ring->tail;
+}
+
+/* Return current ring length */
+static inline uint32_t ring_st_length(ring_st_t *ring)
+{
+ return ring->tail - ring->head;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_u32_internal.h b/platform/linux-generic/include/odp_ring_u32_internal.h
new file mode 100644
index 000000000..2e91e1d88
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_u32_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#ifndef ODP_RING_U32_INTERNAL_H_
+#define ODP_RING_U32_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ring_common.h>
+
+#undef _ODP_RING_TYPE
+#define _ODP_RING_TYPE _ODP_RING_TYPE_U32
+
+#include <odp_ring_internal.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_ring_u64_internal.h b/platform/linux-generic/include/odp_ring_u64_internal.h
new file mode 100644
index 000000000..1de098d45
--- /dev/null
+++ b/platform/linux-generic/include/odp_ring_u64_internal.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_RING_U64_INTERNAL_H_
+#define ODP_RING_U64_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_ring_common.h>
+
+#undef _ODP_RING_TYPE
+#define _ODP_RING_TYPE _ODP_RING_TYPE_U64
+
+#include <odp_ring_internal.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_schedule_if.h b/platform/linux-generic/include/odp_schedule_if.h
index e842fb464..7a61b1dc0 100644
--- a/platform/linux-generic/include/odp_schedule_if.h
+++ b/platform/linux-generic/include/odp_schedule_if.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
*/
#ifndef ODP_SCHEDULE_IF_H_
@@ -12,38 +11,61 @@ extern "C" {
#endif
#include <odp/api/queue.h>
-#include <odp_queue_internal.h>
#include <odp/api/schedule.h>
+#include <odp/api/plat/schedule_inline_types.h>
-typedef void (*schedule_pktio_start_fn_t)(int pktio_index, int num_in_queue,
- int in_queue_idx[]);
+#include <odp_event_internal.h>
+#include <odp_queue_if.h>
+
+#define _ODP_SCHED_ID_BASIC 0
+#define _ODP_SCHED_ID_SP 1
+#define _ODP_SCHED_ID_SCALABLE 2
+
+/* Scheduler identifier */
+extern int _odp_sched_id;
+
+typedef struct schedule_config_t {
+ struct {
+ int all;
+ int worker;
+ int control;
+ } group_enable;
+
+} schedule_config_t;
+
+typedef void (*schedule_pktio_start_fn_t)(int pktio_index,
+ int num_in_queue,
+ int in_queue_idx[],
+ odp_queue_t odpq[]);
typedef int (*schedule_thr_add_fn_t)(odp_schedule_group_t group, int thr);
typedef int (*schedule_thr_rem_fn_t)(odp_schedule_group_t group, int thr);
typedef int (*schedule_num_grps_fn_t)(void);
-typedef int (*schedule_init_queue_fn_t)(uint32_t queue_index,
- const odp_schedule_param_t *sched_param
- );
+typedef int (*schedule_create_queue_fn_t)(uint32_t queue_index,
+ const odp_schedule_param_t *param);
typedef void (*schedule_destroy_queue_fn_t)(uint32_t queue_index);
typedef int (*schedule_sched_queue_fn_t)(uint32_t queue_index);
typedef int (*schedule_unsched_queue_fn_t)(uint32_t queue_index);
-typedef int (*schedule_ord_enq_multi_fn_t)(queue_t q_int,
- void *buf_hdr[], int num, int *ret);
+typedef int (*schedule_ord_enq_multi_fn_t)(odp_queue_t queue, void *event_hdr[],
+ int num, int *ret);
typedef int (*schedule_init_global_fn_t)(void);
typedef int (*schedule_term_global_fn_t)(void);
typedef int (*schedule_init_local_fn_t)(void);
typedef int (*schedule_term_local_fn_t)(void);
typedef void (*schedule_order_lock_fn_t)(void);
typedef void (*schedule_order_unlock_fn_t)(void);
-typedef unsigned (*schedule_max_ordered_locks_fn_t)(void);
-typedef void (*schedule_save_context_fn_t)(uint32_t queue_index);
+typedef void (*schedule_order_unlock_lock_fn_t)(void);
+typedef void (*schedule_order_lock_start_fn_t)(void);
+typedef void (*schedule_order_lock_wait_fn_t)(void);
+typedef uint32_t (*schedule_max_ordered_locks_fn_t)(void);
+typedef void (*schedule_get_config_fn_t)(schedule_config_t *config);
+typedef const _odp_schedule_api_fn_t *(*schedule_sched_api_fn_t)(void);
typedef struct schedule_fn_t {
- int status_sync;
schedule_pktio_start_fn_t pktio_start;
schedule_thr_add_fn_t thr_add;
schedule_thr_rem_fn_t thr_rem;
schedule_num_grps_fn_t num_grps;
- schedule_init_queue_fn_t init_queue;
+ schedule_create_queue_fn_t create_queue;
schedule_destroy_queue_fn_t destroy_queue;
schedule_sched_queue_fn_t sched_queue;
schedule_ord_enq_multi_fn_t ord_enq_multi;
@@ -53,50 +75,22 @@ typedef struct schedule_fn_t {
schedule_term_local_fn_t term_local;
schedule_order_lock_fn_t order_lock;
schedule_order_unlock_fn_t order_unlock;
+ schedule_order_lock_start_fn_t start_order_lock;
+ schedule_order_lock_wait_fn_t wait_order_lock;
+ schedule_order_unlock_lock_fn_t order_unlock_lock;
schedule_max_ordered_locks_fn_t max_ordered_locks;
-
- /* Called only when status_sync is set */
- schedule_unsched_queue_fn_t unsched_queue;
- schedule_save_context_fn_t save_context;
+ schedule_get_config_fn_t get_config;
+ schedule_sched_api_fn_t sched_api;
} schedule_fn_t;
/* Interface towards the scheduler */
-extern const schedule_fn_t *sched_fn;
+extern const schedule_fn_t *_odp_sched_fn;
/* Interface for the scheduler */
-int sched_cb_pktin_poll(int pktio_index, int num_queue, int index[]);
-void sched_cb_pktio_stop_finalize(int pktio_index);
-odp_queue_t sched_cb_queue_handle(uint32_t queue_index);
-void sched_cb_queue_destroy_finalize(uint32_t queue_index);
-int sched_cb_queue_deq_multi(uint32_t queue_index, odp_event_t ev[], int num);
-int sched_cb_queue_empty(uint32_t queue_index);
-
-/* API functions */
-typedef struct {
- uint64_t (*schedule_wait_time)(uint64_t);
- odp_event_t (*schedule)(odp_queue_t *, uint64_t);
- int (*schedule_multi)(odp_queue_t *, uint64_t, odp_event_t [], int);
- void (*schedule_pause)(void);
- void (*schedule_resume)(void);
- void (*schedule_release_atomic)(void);
- void (*schedule_release_ordered)(void);
- void (*schedule_prefetch)(int);
- int (*schedule_num_prio)(void);
- odp_schedule_group_t (*schedule_group_create)(const char *,
- const odp_thrmask_t *);
- int (*schedule_group_destroy)(odp_schedule_group_t);
- odp_schedule_group_t (*schedule_group_lookup)(const char *);
- int (*schedule_group_join)(odp_schedule_group_t, const odp_thrmask_t *);
- int (*schedule_group_leave)(odp_schedule_group_t,
- const odp_thrmask_t *);
- int (*schedule_group_thrmask)(odp_schedule_group_t, odp_thrmask_t *);
- int (*schedule_group_info)(odp_schedule_group_t,
- odp_schedule_group_info_t *);
- void (*schedule_order_lock)(unsigned);
- void (*schedule_order_unlock)(unsigned);
-
-} schedule_api_t;
+int _odp_sched_cb_pktin_poll(int pktio_index, int pktin_index,
+ _odp_event_hdr_t *hdr_tbl[], int num);
+void _odp_sched_cb_pktio_stop_finalize(int pktio_index);
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_schedule_scalable.h b/platform/linux-generic/include/odp_schedule_scalable.h
new file mode 100644
index 000000000..a2db327df
--- /dev/null
+++ b/platform/linux-generic/include/odp_schedule_scalable.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_SCHEDULE_SCALABLE_H
+#define ODP_SCHEDULE_SCALABLE_H
+
+#include <odp/api/align.h>
+#include <odp/api/schedule.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp_event_internal.h>
+#include <odp_schedule_scalable_config.h>
+#include <odp_schedule_scalable_ordered.h>
+#include <odp_llqueue.h>
+
+/*
+ * Define scalable scheduler internal maximum priority count
+ * ODP_SCHED_PRIO_NUM as it is not compile-time constant at API
+ * level. The current API for this is odp_schedule_num_prio().
+ * The other schedulers also define this internally as NUM_PRIO.
+ *
+ * One additional priority level for idle pktin queues.
+ * This is only for internal use and not visible to the user.
+ */
+#define ODP_SCHED_PRIO_PKTIN 8
+#define ODP_SCHED_PRIO_NUM 9
+
+typedef struct ODP_ALIGNED_CACHE {
+ union {
+ struct {
+ struct llqueue llq;
+ uint32_t prio;
+ };
+ char line[ODP_CACHE_LINE_SIZE];
+ };
+} sched_queue_t;
+
+#define TICKET_INVALID (uint16_t)(~0U)
+
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) {
+ int32_t numevts;
+ uint16_t wrr_budget;
+ uint8_t cur_ticket;
+ uint8_t nxt_ticket;
+} qschedstate_t;
+
+typedef uint32_t ringidx_t;
+
+#ifdef CONFIG_SPLIT_PRODCONS
+#define SPLIT_PC ODP_ALIGNED_CACHE
+#else
+#define SPLIT_PC
+#endif
+
+#define ODP_NO_SCHED_QUEUE (ODP_SCHED_SYNC_ORDERED + 1)
+
+typedef struct ODP_ALIGNED_CACHE {
+ struct llnode node;
+ sched_queue_t *schedq;
+#ifdef CONFIG_QSCHST_LOCK
+ odp_ticketlock_t qschlock;
+#endif
+ qschedstate_t qschst;
+ uint8_t pop_deficit;
+ uint8_t qschst_type;
+ uint8_t pktio_idx;
+ uint8_t rx_queue;
+ uint16_t xoffset;
+ uint8_t sched_prio;
+ ringidx_t prod_read SPLIT_PC;
+ ringidx_t prod_write;
+ ringidx_t prod_mask;
+ _odp_event_hdr_t **prod_ring;
+ ringidx_t cons_write SPLIT_PC;
+ ringidx_t cons_read;
+ reorder_window_t *rwin;
+ void *user_ctx;
+#ifdef CONFIG_SPLIT_PRODCONS
+ _odp_event_hdr_t **cons_ring;
+ ringidx_t cons_mask;
+ uint16_t cons_type;
+#else
+#define cons_mask prod_mask
+#define cons_ring prod_ring
+#define cons_type qschst_type
+#endif
+ odp_schedule_group_t sched_grp;
+ uint32_t loop_check[CONFIG_NUM_CPU_IDS];
+} sched_elem_t;
+
+/* Number of scheduling groups */
+#define MAX_SCHED_GROUP (sizeof(sched_group_mask_t) * CHAR_BIT)
+
+typedef bitset_t sched_group_mask_t;
+
+typedef struct {
+ /* Threads currently associated with the sched group */
+ bitset_t thr_actual[ODP_SCHED_PRIO_NUM] ODP_ALIGNED_CACHE;
+ bitset_t thr_wanted;
+ /* Used to spread queues over schedq's */
+ uint32_t xcount[ODP_SCHED_PRIO_NUM];
+ /* Number of schedq's per prio */
+ uint32_t xfactor;
+ char name[ODP_SCHED_GROUP_NAME_LEN];
+ /* ODP_SCHED_PRIO_NUM * xfactor. Must be last. */
+ sched_queue_t schedq[1] ODP_ALIGNED_CACHE;
+} sched_group_t;
+
+/* Number of reorder contexts per thread */
+#define TS_RVEC_SIZE 16
+
+typedef struct ODP_ALIGNED_CACHE {
+ /* Atomic queue currently being processed or NULL */
+ sched_elem_t *atomq;
+ /* Schedq the currently processed queue was popped from */
+ sched_queue_t *src_schedq;
+ /* Current reorder context or NULL */
+ reorder_context_t *rctx;
+ uint8_t pause;
+ uint8_t out_of_order;
+ uint8_t tidx;
+ uint8_t pad;
+ uint32_t dequeued; /* Number of events dequeued from atomic queue */
+ uint16_t ticket; /* Ticket for atomic queue or TICKET_INVALID */
+ uint16_t num_schedq;
+ uint16_t sg_sem; /* Set when sg_wanted is modified by other thread */
+#define SCHEDQ_PER_THREAD (MAX_SCHED_GROUP * ODP_SCHED_PRIO_NUM)
+ sched_queue_t *schedq_list[SCHEDQ_PER_THREAD];
+ /* Current sched_group membership */
+ sched_group_mask_t sg_actual[ODP_SCHED_PRIO_NUM];
+ /* Future sched_group membership. */
+ sched_group_mask_t sg_wanted[ODP_SCHED_PRIO_NUM];
+ bitset_t priv_rvec_free;
+ /* Bitset of free entries in rvec[] */
+ bitset_t rvec_free ODP_ALIGNED_CACHE;
+ /* Reordering contexts to allocate from */
+ reorder_context_t rvec[TS_RVEC_SIZE] ODP_ALIGNED_CACHE;
+ uint32_t loop_cnt; /*Counter to check pktio ingress queue dead loop */
+} sched_scalable_thread_state_t;
+
+void _odp_sched_update_enq(sched_elem_t *q, uint32_t actual);
+void _odp_sched_update_enq_sp(sched_elem_t *q, uint32_t actual);
+sched_queue_t *_odp_sched_queue_add(odp_schedule_group_t grp, uint32_t prio);
+void _odp_sched_queue_rem(odp_schedule_group_t grp, uint32_t prio);
+
+#endif /* ODP_SCHEDULE_SCALABLE_H */
diff --git a/platform/linux-generic/include/odp_schedule_scalable_config.h b/platform/linux-generic/include/odp_schedule_scalable_config.h
new file mode 100644
index 000000000..1133d49aa
--- /dev/null
+++ b/platform/linux-generic/include/odp_schedule_scalable_config.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_SCHEDULE_SCALABLE_CONFIG_H_
+#define ODP_SCHEDULE_SCALABLE_CONFIG_H_
+
+/* Maximum number of events that can be stored in a queue */
+#define CONFIG_SCAL_QUEUE_SIZE 4096
+
+/*
+ * Default scaling factor for the scheduler group
+ *
+ * This scaling factor is used when the application creates a scheduler
+ * group with no worker threads.
+ */
+#define CONFIG_DEFAULT_XFACTOR 4
+
+/*
+ * Default weight (in events) for WRR in scalable scheduler
+ *
+ * This controls the per-queue weight for WRR between queues of the same
+ * priority in the scalable scheduler
+ * A higher value improves throughput while a lower value increases fairness
+ * and thus likely decreases latency
+ *
+ * If WRR is undesired, set the value to ~0 which will use the largest possible
+ * weight
+ *
+ * Note: an API for specifying this on a per-queue basis would be useful but is
+ * not yet available
+ */
+#define CONFIG_WRR_WEIGHT 64
+
+/*
+ * Split queue producer/consumer metadata into separate cache lines.
+ * This is beneficial on e.g. Cortex-A57 but not so much on A53.
+ */
+#define CONFIG_SPLIT_PRODCONS
+
+/*
+ * Use locks to protect queue (ring buffer) and scheduler state updates
+ * On x86, this decreases overhead noticeably.
+ */
+#if !defined(__arm__) && !defined(__aarch64__)
+#define CONFIG_QSCHST_LOCK
+/* Keep all ring buffer/qschst data together when using locks */
+#undef CONFIG_SPLIT_PRODCONS
+#endif
+
+#endif /* ODP_SCHEDULE_SCALABLE_CONFIG_H_ */
diff --git a/platform/linux-generic/include/odp_schedule_scalable_ordered.h b/platform/linux-generic/include/odp_schedule_scalable_ordered.h
new file mode 100644
index 000000000..ad2dfc8ee
--- /dev/null
+++ b/platform/linux-generic/include/odp_schedule_scalable_ordered.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef ODP_SCHEDULE_SCALABLE_ORDERED_H
+#define ODP_SCHEDULE_SCALABLE_ORDERED_H
+
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_bitset.h>
+#include <odp_event_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ishmpool_internal.h>
+
+/* High level functioning of reordering
+ * Datastructures -
+ * Reorder Window - Every ordered queue is associated with a reorder window.
+ * Reorder window stores reorder contexts from threads that
+ * have completed processing out-of-order.
+ * Reorder Context - Reorder context consists of events that a thread
+ * wants to enqueue while processing a batch of events
+ * from an ordered queue.
+ *
+ * Algorithm -
+ * 1) Thread identifies the ordered queue.
+ * 2) It 'reserves a slot in the reorder window and dequeues the
+ * events' atomically. Atomicity is achieved by using a ticket-lock
+ * like design where the reorder window slot is the ticket.
+ * 3a) Upon order-release/next schedule call, the thread
+ * checks if it's slot (ticket) equals the head of the reorder window.
+ * If yes, enqueues the events to the destination queue till
+ * i) the reorder window is empty or
+ * ii) there is a gap in the reorder window
+ * If no, the reorder context is stored in the reorder window at
+ * the reserved slot.
+ * 3b) Upon the first enqueue, the thread checks if it's slot (ticket)
+ * equals the head of the reorder window.
+ * If yes, enqueues the events immediately to the destination queue
+ * If no, these (and subsequent) events are stored in the reorder context
+ * (in the application given order)
+ */
+
+/* Head and change indicator variables are used to synchronise between
+ * concurrent insert operations in the reorder window. A thread performing
+ * an in-order insertion must be notified about the newly inserted
+ * reorder contexts so that it doesn’t halt the retire process too early.
+ * A thread performing an out-of-order insertion must correspondingly
+ * notify the thread doing in-order insertion of the new waiting reorder
+ * context, which may need to be handled by that thread.
+ *
+ * Also, an out-of-order insertion may become an in-order insertion if the
+ * thread doing an in-order insertion completes before this thread completes.
+ * We need a point of synchronisation where this knowledge and potential state
+ * change can be transferred between threads.
+ */
+typedef struct ODP_ALIGNED(sizeof(uint64_t)) hc {
+ /* First missing context */
+ uint32_t head;
+ /* Change indicator */
+ uint32_t chgi;
+} hc_t;
+
+/* Number of reorder contects in the reorder window.
+ * Should be at least one per CPU.
+ */
+#define RWIN_SIZE 32
+ODP_STATIC_ASSERT(_ODP_CHECK_IS_POWER2(RWIN_SIZE), "RWIN_SIZE is not a power of 2");
+
+typedef struct reorder_context reorder_context_t;
+
+typedef struct reorder_window {
+ /* head and change indicator */
+ hc_t hc;
+ uint32_t winmask;
+ uint32_t tail;
+ uint32_t turn;
+ uint32_t olock[CONFIG_QUEUE_MAX_ORD_LOCKS];
+ uint32_t lock_count;
+ /* Reorder contexts in this window */
+ reorder_context_t *ring[RWIN_SIZE];
+} reorder_window_t;
+
+/* Number of events that can be stored in a reorder context.
+ * This size is chosen so that there is no space left unused at the end
+ * of the last cache line (for 64b architectures and 64b handles).
+ */
+#define RC_EVT_SIZE 18
+
+struct ODP_ALIGNED_CACHE reorder_context {
+ /* Reorder window to which this context belongs */
+ reorder_window_t *rwin;
+ /* Pointer to TS->rvec_free */
+ bitset_t *rvec_free;
+ /* Our slot number in the reorder window */
+ uint32_t sn;
+ uint8_t olock_flags;
+ /* Our index in thread_state rvec array */
+ uint8_t idx;
+ /* Use to link reorder contexts together */
+ uint8_t next_idx;
+ /* Current reorder context to save events in */
+ uint8_t cur_idx;
+ /* Number of events stored in this reorder context */
+ uint8_t numevts;
+ /* Events stored in this context */
+ _odp_event_hdr_t *events[RC_EVT_SIZE];
+ queue_entry_t *destq[RC_EVT_SIZE];
+};
+
+reorder_window_t *_odp_rwin_alloc(_odp_ishm_pool_t *pool,
+ unsigned int lock_count);
+int _odp_rwin_free(_odp_ishm_pool_t *pool, reorder_window_t *rwin);
+bool _odp_rwin_reserve(reorder_window_t *rwin, uint32_t *sn);
+bool _odp_rwin_reserve_sc(reorder_window_t *rwin, uint32_t *sn);
+void _odp_rwin_unreserve_sc(reorder_window_t *rwin, uint32_t sn);
+void _odp_rctx_init(reorder_context_t *rctx, uint16_t idx,
+ reorder_window_t *rwin, uint32_t sn);
+void _odp_rctx_release(reorder_context_t *rctx);
+int _odp_rctx_save(queue_entry_t *queue, _odp_event_hdr_t *event_hdr[], int num);
+
+#endif /* ODP_SCHEDULE_SCALABLE_ORDERED_H */
diff --git a/platform/linux-generic/include/odp_shm_internal.h b/platform/linux-generic/include/odp_shm_internal.h
index 8bd105d9f..bd9566e74 100644
--- a/platform/linux-generic/include/odp_shm_internal.h
+++ b/platform/linux-generic/include/odp_shm_internal.h
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
*/
#ifndef ODP_SHM_INTERNAL_H_
@@ -11,13 +10,40 @@
extern "C" {
#endif
+#include <sys/types.h>
+#include <inttypes.h>
+
#include <odp/api/shared_memory.h>
-#define SHM_DEVNAME_MAXLEN (ODP_SHM_NAME_LEN + 16)
-#define SHM_DEVNAME_FORMAT "/odp-%d-%s" /* /dev/shm/odp-<pid>-<name> */
+/* flags available at ishm_reserve: */
+#define _ODP_ISHM_SINGLE_VA 1
+#define _ODP_ISHM_LOCK 2
+#define _ODP_ISHM_EXPORT 4 /* create export descr file in /tmp */
+
+/**
+ * Shared memory block info
+ */
+typedef struct _odp_ishm_info_t {
+ const char *name; /**< Block name */
+ void *addr; /**< Block address */
+ uint64_t size; /**< Block size in bytes */
+ uint64_t page_size; /**< Memory page size */
+ uint32_t flags; /**< _ODP_ISHM_* flags */
+ uint32_t user_flags;/**< user specific flags */
+} _odp_ishm_info_t;
-#define _ODP_SHM_PROC_NOCREAT 0x40 /**< Do not create shm if not exist */
-#define _ODP_SHM_O_EXCL 0x80 /**< Do not create shm if exist */
+int _odp_ishm_reserve(const char *name, uint64_t size, int fd, uint32_t align,
+ uint64_t offset, uint32_t flags, uint32_t user_flags);
+int _odp_ishm_free_by_index(int block_index);
+int _odp_ishm_lookup_by_name(const char *name);
+int _odp_ishm_find_exported(const char *remote_name,
+ pid_t external_odp_pid,
+ const char *local_name);
+void *_odp_ishm_address(int block_index);
+int _odp_ishm_info(int block_index, _odp_ishm_info_t *info);
+int _odp_ishm_status(const char *title);
+int _odp_ishm_cleanup_files(const char *dirpath);
+void _odp_ishm_print(int block_index);
#ifdef __cplusplus
}
diff --git a/platform/linux-generic/include/odp_socket_common.h b/platform/linux-generic/include/odp_socket_common.h
new file mode 100644
index 000000000..bb89133a7
--- /dev/null
+++ b/platform/linux-generic/include/odp_socket_common.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
+ */
+
+#ifndef ODP_SOCKET_COMMON_H_
+#define ODP_SOCKET_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/packet_io.h>
+#include <protocols/eth.h>
+
+#include <string.h>
+
+#define _ODP_SOCKET_MTU_MIN (68 + _ODP_ETHHDR_LEN)
+#define _ODP_SOCKET_MTU_MAX (9000 + _ODP_ETHHDR_LEN)
+
+static inline void
+ethaddr_copy(unsigned char mac_dst[], unsigned char mac_src[])
+{
+ memcpy(mac_dst, mac_src, _ODP_ETHADDR_LEN);
+}
+
+static inline int
+ethaddrs_equal(unsigned char mac_a[], unsigned char mac_b[])
+{
+ return !memcmp(mac_a, mac_b, _ODP_ETHADDR_LEN);
+}
+
+/**
+ * Read the MAC address from a packet socket
+ */
+int _odp_mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[]);
+
+/**
+ * Read the MTU from a packet socket
+ */
+uint32_t _odp_mtu_get_fd(int fd, const char *name);
+
+/**
+ * Set a packet socket MTU
+ */
+int _odp_mtu_set_fd(int fd, const char *name, int mtu);
+
+/**
+ * Enable/Disable promisc mode for a packet socket
+ */
+int _odp_promisc_mode_set_fd(int fd, const char *name, int enable);
+
+/**
+ * Return promisc mode of a packet socket
+ */
+int _odp_promisc_mode_get_fd(int fd, const char *name);
+
+/**
+ * Return link status of a packet socket (up/down)
+ */
+int _odp_link_status_fd(int fd, const char *name);
+
+/**
+ * Read link information from a packet socket
+ */
+int _odp_link_info_fd(int fd, const char *name, odp_pktio_link_info_t *info);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_SOCKET_COMMON_H_ */
diff --git a/platform/linux-generic/include/odp_sorted_list_internal.h b/platform/linux-generic/include/odp_sorted_list_internal.h
index 3266c4337..feb1fc7d6 100644
--- a/platform/linux-generic/include/odp_sorted_list_internal.h
+++ b/platform/linux-generic/include/odp_sorted_list_internal.h
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#ifndef _ODP_INT_SORTED_LIST_H_
diff --git a/platform/linux-generic/include/odp_string_internal.h b/platform/linux-generic/include/odp_string_internal.h
new file mode 100644
index 000000000..37e9361af
--- /dev/null
+++ b/platform/linux-generic/include/odp_string_internal.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_STRING_INTERNAL_H_
+#define ODP_STRING_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+/* Helps with snprintf() return value checking
+ *
+ * Otherwise like snprintf(), but returns always the number of characters
+ * printed (without the end mark) or zero on error. Terminates the string
+ * always with the end mark. */
+int _odp_snprint(char *str, size_t size, const char *format, ...);
+
+/*
+ * Copy a string
+ *
+ * Like strncpy(), but additionally ensures that the destination string is null
+ * terminated, unless sz is zero in which case returns dst without doing
+ * anything else.
+ */
+char *_odp_strcpy(char *restrict dst, const char *restrict src, size_t sz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_sysfs_stats.h b/platform/linux-generic/include/odp_sysfs_stats.h
new file mode 100644
index 000000000..8d7ac2327
--- /dev/null
+++ b/platform/linux-generic/include/odp_sysfs_stats.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef ODP_SYSFS_STATS_H_
+#define ODP_SYSFS_STATS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/packet_io_stats.h>
+#include <odp_packet_io_internal.h>
+
+int _odp_sysfs_stats(pktio_entry_t *pktio_entry,
+ odp_pktio_stats_t *stats);
+
+int _odp_sysfs_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[], int num);
+int _odp_sysfs_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[],
+ int num);
+int _odp_sysfs_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ODP_SYSFS_STATS_H_ */
diff --git a/platform/linux-generic/include/odp_sysinfo_internal.h b/platform/linux-generic/include/odp_sysinfo_internal.h
new file mode 100644
index 000000000..793d7986e
--- /dev/null
+++ b/platform/linux-generic/include/odp_sysinfo_internal.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+#ifndef ODP_SYSINFO_INTERNAL_H_
+#define ODP_SYSINFO_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_global_data.h>
+#include <odp_debug_internal.h>
+#include <inttypes.h>
+#include <string.h>
+
+int _odp_cpuinfo_parser(FILE *file, system_info_t *sysinfo);
+uint64_t odp_cpu_arch_hz_current(int id);
+void _odp_sys_info_print_arch(void);
+
+static inline int _odp_dummy_cpuinfo(system_info_t *sysinfo)
+{
+ uint64_t cpu_hz_max = sysinfo->default_cpu_hz_max;
+ int i;
+
+ sysinfo->cpu_arch = ODP_CPU_ARCH_UNKNOWN;
+
+ _ODP_WARN("Use dummy values for freq and model string\n");
+ for (i = 0; i < CONFIG_NUM_CPU_IDS; i++) {
+ _ODP_WARN("CPU[%i] uses default max frequency of "
+ "%" PRIu64 " Hz from config file\n", i, cpu_hz_max);
+ sysinfo->cpu_hz_max[i] = cpu_hz_max;
+ strcpy(sysinfo->model_str[i], "UNKNOWN");
+ }
+
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_time_internal.h b/platform/linux-generic/include/odp_time_internal.h
deleted file mode 100644
index 99ac79772..000000000
--- a/platform/linux-generic/include/odp_time_internal.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_TIME_INTERNAL_H_
-#define ODP_TIME_INTERNAL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-int cpu_has_global_time(void);
-uint64_t cpu_global_time(void);
-uint64_t cpu_global_time_freq(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platform/linux-generic/include/odp_timer_internal.h b/platform/linux-generic/include/odp_timer_internal.h
index 91b12c545..2351def95 100644
--- a/platform/linux-generic/include/odp_timer_internal.h
+++ b/platform/linux-generic/include/odp_timer_internal.h
@@ -1,10 +1,8 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
*/
-
/**
* @file
*
@@ -16,23 +14,53 @@
#include <odp/api/align.h>
#include <odp/api/debug.h>
-#include <odp_buffer_internal.h>
-#include <odp_pool_internal.h>
#include <odp/api/timer.h>
+#include <odp_event_internal.h>
+#include <odp_global_data.h>
+#include <odp_pool_internal.h>
+
+/*
+ * Use as the argument to timer_run() to force a scan and to ignore rate
+ * limit.
+ */
+#define TIMER_SCAN_FORCE INT32_MAX
+
/**
* Internal Timeout header
*/
-typedef struct {
- /* common buffer header */
- odp_buffer_hdr_t buf_hdr;
+typedef struct ODP_ALIGNED_CACHE odp_timeout_hdr_t {
+ /* Common event header */
+ _odp_event_hdr_t event_hdr;
/* Requested expiration time */
uint64_t expiration;
+
/* User ptr inherited from parent timer */
- void *user_ptr;
+ const void *user_ptr;
+
+ /* User area pointer */
+ void *uarea_addr;
+
/* Parent timer */
odp_timer_t timer;
+
} odp_timeout_hdr_t;
+ODP_STATIC_ASSERT(sizeof(odp_timeout_hdr_t) <= ODP_CACHE_LINE_SIZE,
+ "TIMEOUT_HDR_SIZE_ERROR");
+
+/* A larger decrement value should be used after receiving events compared to
+ * an 'empty' call. */
+uint64_t _odp_timer_run_inline(int dec);
+
+/* Static inline wrapper to minimize modification of schedulers. */
+static inline uint64_t timer_run(int dec)
+{
+ if (odp_global_rw->inline_timers)
+ return _odp_timer_run_inline(dec);
+
+ return UINT64_MAX;
+}
+
#endif
diff --git a/platform/linux-generic/include/odp_timer_wheel_internal.h b/platform/linux-generic/include/odp_timer_wheel_internal.h
index a60ab516c..f54c058d7 100644
--- a/platform/linux-generic/include/odp_timer_wheel_internal.h
+++ b/platform/linux-generic/include/odp_timer_wheel_internal.h
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#ifndef _ODP_INT_TIMER_WHEEL_H_
diff --git a/platform/linux-generic/include/odp_traffic_mngr_internal.h b/platform/linux-generic/include/odp_traffic_mngr_internal.h
index e8254f5ed..960756340 100644
--- a/platform/linux-generic/include/odp_traffic_mngr_internal.h
+++ b/platform/linux-generic/include/odp_traffic_mngr_internal.h
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
/**
@@ -19,23 +16,22 @@
extern "C" {
#endif
-#include <pthread.h>
-#include <odp/api/traffic_mngr.h>
+#include <odp/api/barrier.h>
#include <odp/api/packet_io.h>
+#include <odp/api/traffic_mngr.h>
+
#include <odp_name_table_internal.h>
#include <odp_timer_wheel_internal.h>
#include <odp_pkt_queue_internal.h>
#include <odp_sorted_list_internal.h>
-#include <odp_internal.h>
#include <odp_debug_internal.h>
#include <odp_buffer_internal.h>
#include <odp_queue_if.h>
#include <odp_packet_internal.h>
-typedef struct stat file_stat_t;
+#include <pthread.h>
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+typedef struct stat file_stat_t;
#define INPUT_WORK_RING_SIZE (16 * 1024)
@@ -87,13 +83,6 @@ typedef uint64_t tm_handle_t;
#define PF_REACHED_EGRESS 0x40
#define PF_ERROR 0x80
-typedef struct {
- uint32_t num_allocd;
- uint32_t num_used;
- uint32_t num_freed;
- void **array_ptrs; /* Ptr to an array of num_allocd void * ptrs. */
-} dynamic_tbl_t;
-
#define ODP_TM_NUM_PROFILES 4
typedef enum {
@@ -103,6 +92,11 @@ typedef enum {
TM_WRED_PROFILE
} profile_kind_t;
+typedef enum {
+ TM_STATUS_FREE = 0,
+ TM_STATUS_RESERVED
+} tm_status_t;
+
typedef struct tm_queue_obj_s tm_queue_obj_t;
typedef struct tm_node_obj_s tm_node_obj_t;
@@ -114,6 +108,7 @@ typedef struct {
_odp_int_name_t name_tbl_id;
odp_tm_threshold_t thresholds_profile;
uint32_t ref_cnt;
+ tm_status_t status;
} tm_queue_thresholds_t;
typedef struct {
@@ -126,6 +121,7 @@ typedef struct {
odp_tm_percent_t max_drop_prob;
odp_bool_t enable_wred;
odp_bool_t use_byte_fullness;
+ tm_status_t status;
} tm_wred_params_t;
typedef struct {
@@ -164,6 +160,7 @@ typedef struct {
uint32_t ref_cnt;
odp_tm_sched_mode_t sched_modes[ODP_TM_MAX_PRIORITIES];
uint16_t inverted_weights[ODP_TM_MAX_PRIORITIES];
+ tm_status_t status;
} tm_sched_params_t;
typedef enum {
@@ -199,6 +196,7 @@ typedef struct {
int8_t len_adjust;
odp_bool_t dual_rate;
odp_bool_t enabled;
+ tm_status_t status;
} tm_shaper_params_t;
typedef enum { NO_CALLBACK, UNDELAY_PKT } tm_shaper_callback_reason_t;
@@ -257,7 +255,7 @@ typedef struct {
uint8_t num_priorities;
uint8_t highest_priority;
uint8_t locked;
- tm_sched_state_t sched_states[0];
+ tm_sched_state_t sched_states[ODP_TM_MAX_PRIORITIES];
} tm_schedulers_obj_t;
struct tm_queue_obj_s {
@@ -268,7 +266,7 @@ struct tm_queue_obj_s {
uint32_t pkts_dequeued_cnt;
uint32_t pkts_consumed_cnt;
_odp_int_pkt_queue_t _odp_int_pkt_queue;
- tm_wred_node_t *tm_wred_node;
+ tm_wred_node_t tm_wred_node;
odp_packet_t pkt;
odp_packet_t sent_pkt;
uint32_t timer_seq;
@@ -286,13 +284,20 @@ struct tm_queue_obj_s {
uint8_t tm_idx;
uint8_t delayed_cnt;
uint8_t blocked_cnt;
- queue_t tm_qentry;
+ odp_bool_t ordered_enqueue;
+ tm_status_t status;
+ /* Statistics for odp_tm_queue_stats_t */
+ struct {
+ odp_atomic_u64_t discards;
+ odp_atomic_u64_t errors;
+ odp_atomic_u64_t packets;
+ } stats;
};
struct tm_node_obj_s {
void *user_context;
- tm_wred_node_t *tm_wred_node;
- tm_schedulers_obj_t *schedulers_obj;
+ tm_wred_node_t tm_wred_node;
+ tm_schedulers_obj_t schedulers_obj;
tm_shaper_obj_t *fanin_list_head;
tm_shaper_obj_t *fanin_list_tail;
tm_shaper_obj_t shaper_obj;
@@ -305,6 +310,7 @@ struct tm_node_obj_s {
uint8_t level; /* Primarily for debugging */
uint8_t tm_idx;
uint8_t marked;
+ tm_status_t status;
};
typedef struct {
@@ -372,9 +378,8 @@ struct tm_system_s {
_odp_int_name_t name_tbl_id;
void *trace_buffer;
- uint32_t next_queue_num;
- tm_queue_obj_t **queue_num_tbl;
- input_work_queue_t *input_work_queue;
+ tm_queue_obj_t *queue_num_tbl[ODP_TM_MAX_TM_QUEUES];
+ input_work_queue_t input_work_queue;
tm_queue_cnts_t priority_queue_cnts;
tm_queue_cnts_t total_queue_cnts;
pkt_desc_t egress_pkt_desc;
@@ -383,7 +388,7 @@ struct tm_system_s {
_odp_timer_wheel_t _odp_int_timer_wheel;
_odp_int_sorted_pool_t _odp_int_sorted_pool;
- tm_node_obj_t *root_node;
+ tm_node_obj_t root_node;
odp_tm_egress_t egress;
odp_tm_requirements_t requirements;
odp_tm_capabilities_t capabilities;
@@ -399,7 +404,8 @@ struct tm_system_s {
uint64_t current_time;
uint8_t tm_idx;
uint8_t first_enq;
- odp_bool_t is_idle;
+ odp_atomic_u32_t is_idle;
+ tm_status_t status;
uint64_t shaper_green_cnt;
uint64_t shaper_yellow_cnt;
@@ -412,15 +418,13 @@ struct tm_system_s {
* while the input work queue is shared - timers are not. */
struct tm_system_group_s {
- tm_system_group_t *prev;
- tm_system_group_t *next;
-
odp_barrier_t tm_group_barrier;
tm_system_t *first_tm_system;
uint32_t num_tm_systems;
uint32_t first_enq;
pthread_t thread;
pthread_attr_t attr;
+ tm_status_t status;
};
#ifdef __cplusplus
diff --git a/platform/linux-generic/include/odp_types_internal.h b/platform/linux-generic/include/odp_types_internal.h
new file mode 100644
index 000000000..d69db19f6
--- /dev/null
+++ b/platform/linux-generic/include/odp_types_internal.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef ODP_TYPES_INTERNAL_H_
+#define ODP_TYPES_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __SIZEOF_INT128__
+
+__extension__ typedef unsigned __int128 _odp_u128_t;
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/protocols/eth.h b/platform/linux-generic/include/protocols/eth.h
index 6d00e7fb0..d9ea4f704 100644
--- a/platform/linux-generic/include/protocols/eth.h
+++ b/platform/linux-generic/include/protocols/eth.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -17,7 +15,11 @@
extern "C" {
#endif
-#include <odp_api.h>
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
+
+#include <stdint.h>
/** @addtogroup odp_header ODP HEADER
* @{
diff --git a/platform/linux-generic/include/protocols/ip.h b/platform/linux-generic/include/protocols/ip.h
index 2b34a753f..97652d02c 100644
--- a/platform/linux-generic/include/protocols/ip.h
+++ b/platform/linux-generic/include/protocols/ip.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -17,7 +15,9 @@
extern "C" {
#endif
-#include <odp_api.h>
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
/** @addtogroup odp_header ODP HEADER
* @{
@@ -62,8 +62,8 @@ extern "C" {
/** @internal Returns IPv4 Don't fragment */
#define _ODP_IPV4HDR_FLAGS_DONT_FRAG(frag_offset) ((frag_offset) & 0x4000)
-/** @internal Returns IPv4 more fragments */
-#define _ODP_IPV4HDR_FLAGS_MORE_FRAGS(frag_offset) ((frag_offset) & 0x2000)
+/* IPv4 more fragments flag in the frag_offset field */
+#define _ODP_IPV4HDR_FRAG_OFFSET_MORE_FRAGS 0x2000
/** @internal Returns IPv4 fragment offset */
#define _ODP_IPV4HDR_FRAG_OFFSET(frag_offset) ((frag_offset) & 0x1fff)
@@ -157,14 +157,18 @@ typedef struct ODP_PACKED {
* IP protocol values (IPv4:'proto' or IPv6:'next_hdr')
* @{*/
#define _ODP_IPPROTO_HOPOPTS 0x00 /**< IPv6 hop-by-hop options */
-#define _ODP_IPPROTO_ICMPv4 0x01 /**< Internet Control Message Protocol (1) */
+#define _ODP_IPPROTO_ICMPV4 0x01 /**< Internet Control Message Protocol (1) */
+#define _ODP_IPPROTO_IPIP 0x04 /**< IP Encapsulation within IP (4) */
#define _ODP_IPPROTO_TCP 0x06 /**< Transmission Control Protocol (6) */
#define _ODP_IPPROTO_UDP 0x11 /**< User Datagram Protocol (17) */
+#define _ODP_IPPROTO_IPV6 0x29 /**< IPv6 Routing header (41) */
#define _ODP_IPPROTO_ROUTE 0x2B /**< IPv6 Routing header (43) */
#define _ODP_IPPROTO_FRAG 0x2C /**< IPv6 Fragment (44) */
#define _ODP_IPPROTO_AH 0x33 /**< Authentication Header (51) */
#define _ODP_IPPROTO_ESP 0x32 /**< Encapsulating Security Payload (50) */
-#define _ODP_IPPROTO_ICMPv6 0x3A /**< Internet Control Message Protocol (58) */
+#define _ODP_IPPROTO_ICMPV6 0x3A /**< Internet Control Message Protocol (58) */
+#define _ODP_IPPROTO_NO_NEXT 0x3B /**< No Next Header (59) */
+#define _ODP_IPPROTO_DEST 0x3C /**< IPv6 Destination header (60) */
#define _ODP_IPPROTO_SCTP 0x84 /**< Stream Control Transmission protocol
(132) */
#define _ODP_IPPROTO_INVALID 0xFF /**< Reserved invalid by IANA */
diff --git a/platform/linux-generic/include/protocols/ipsec.h b/platform/linux-generic/include/protocols/ipsec.h
index 093177fba..5bb0d2169 100644
--- a/platform/linux-generic/include/protocols/ipsec.h
+++ b/platform/linux-generic/include/protocols/ipsec.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
@@ -18,7 +16,11 @@
extern "C" {
#endif
-#include <odp_api.h>
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
+
+#include <stdint.h>
/** @addtogroup odp_header ODP HEADER
* @{
@@ -34,7 +36,7 @@ extern "C" {
typedef struct ODP_PACKED {
odp_u32be_t spi; /**< Security Parameter Index */
odp_u32be_t seq_no; /**< Sequence Number */
- uint8_t iv[0]; /**< Initialization vector */
+ uint8_t iv[]; /**< Initialization vector */
} _odp_esphdr_t;
/** @internal Compile time assert */
@@ -47,7 +49,7 @@ ODP_STATIC_ASSERT(sizeof(_odp_esphdr_t) == _ODP_ESPHDR_LEN,
typedef struct ODP_PACKED {
uint8_t pad_len; /**< Padding length (0-255) */
uint8_t next_header; /**< Next header protocol */
- uint8_t icv[0]; /**< Integrity Check Value (optional) */
+ uint8_t icv[]; /**< Integrity Check Value (optional) */
} _odp_esptrl_t;
/** @internal Compile time assert */
@@ -63,7 +65,7 @@ typedef struct ODP_PACKED {
odp_u16be_t pad; /**< Padding (must be 0) */
odp_u32be_t spi; /**< Security Parameter Index */
odp_u32be_t seq_no; /**< Sequence Number */
- uint8_t icv[0]; /**< Integrity Check Value */
+ uint8_t icv[]; /**< Integrity Check Value */
} _odp_ahhdr_t;
/** @internal Compile time assert */
diff --git a/platform/linux-generic/include/protocols/sctp.h b/platform/linux-generic/include/protocols/sctp.h
new file mode 100644
index 000000000..e8c4b0c03
--- /dev/null
+++ b/platform/linux-generic/include/protocols/sctp.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP SCTP header
+ */
+
+#ifndef ODP_SCTP_H_
+#define ODP_SCTP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
+
+/** @addtogroup odp_header ODP HEADER
+ * @{
+ */
+
+/** SCTP header length */
+#define _ODP_SCTPHDR_LEN 12
+
+/** SCTP header */
+typedef struct ODP_PACKED {
+ odp_u16be_t src_port; /**< Source port */
+ odp_u16be_t dst_port; /**< Destination port */
+ odp_u32be_t tag; /**< Verification tag */
+ odp_u32be_t chksum; /**< SCTP header and data checksum */
+} _odp_sctphdr_t;
+
+/** @internal Compile time assert */
+ODP_STATIC_ASSERT(sizeof(_odp_sctphdr_t) == _ODP_SCTPHDR_LEN,
+ "_ODP_SCTPHDR_T__SIZE_ERROR");
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/protocols/tcp.h b/platform/linux-generic/include/protocols/tcp.h
index 114262e97..04620d571 100644
--- a/platform/linux-generic/include/protocols/tcp.h
+++ b/platform/linux-generic/include/protocols/tcp.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
@@ -18,7 +16,8 @@
extern "C" {
#endif
-#include <odp_api.h>
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
/** @addtogroup odp_header ODP HEADER
* @{
diff --git a/platform/linux-generic/include/protocols/thash.h b/platform/linux-generic/include/protocols/thash.h
new file mode 100644
index 000000000..f8539f524
--- /dev/null
+++ b/platform/linux-generic/include/protocols/thash.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/**
+ * @file
+ *
+ * ODP Toeplitz hash function
+ */
+
+#ifndef ODPH_THASH_H_
+#define ODPH_THASH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
+
+#include <protocols/ip.h>
+
+#include <stdint.h>
+
+/** rss data type */
+typedef union {
+ uint8_t u8[40];
+ uint32_t u32[10];
+} rss_key;
+
+/** IPv4 tuple
+ *
+ */
+typedef struct thash_ipv4_tuple {
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ union {
+ struct {
+ uint16_t sport;
+ uint16_t dport;
+ };
+ uint32_t sctp_tag;
+ };
+} thash_ipv4_tuple_t;
+
+/** IPv6 tuple */
+typedef struct thash_ipv6_tuple {
+ _odp_ipv6_addr_t src_addr;
+ _odp_ipv6_addr_t dst_addr;
+ union {
+ struct {
+ uint16_t sport;
+ uint16_t dport;
+ };
+ uint32_t sctp_tag;
+ };
+} thash_ipv6_tuple_t;
+
+/** Thash tuple union */
+typedef union {
+ thash_ipv4_tuple_t v4;
+ thash_ipv6_tuple_t v6;
+} thash_tuple_t;
+
+static inline
+void thash_load_ipv6_addr(const _odp_ipv6hdr_t *ipv6,
+ thash_tuple_t *tuple)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ *(tuple->v6.src_addr.u32 + i) =
+ odp_be_to_cpu_32(*(ipv6->src_addr.u32 + i));
+
+ *(tuple->v6.dst_addr.u32 + i) =
+ odp_be_to_cpu_32(*(ipv6->dst_addr.u32 + i));
+ }
+}
+
+static inline
+uint32_t thash_softrss(uint32_t *tuple, uint8_t len,
+ const rss_key key)
+{
+ uint32_t i, j, ret = 0;
+
+ for (j = 0; j < len; j++) {
+ for (i = 0; i < 32; i++) {
+ if (tuple[j] & (1 << (31 - i))) {
+ ret ^= odp_cpu_to_be_32(((const uint32_t *)
+ key.u32)[j]) << i | (uint32_t)((uint64_t)
+ (odp_cpu_to_be_32(((const uint32_t *)key.u32)
+ [j + 1])) >> (32 - i));
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/protocols/udp.h b/platform/linux-generic/include/protocols/udp.h
index 535aba855..0eccbd44d 100644
--- a/platform/linux-generic/include/protocols/udp.h
+++ b/platform/linux-generic/include/protocols/udp.h
@@ -1,7 +1,5 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
*/
/**
@@ -17,7 +15,9 @@
extern "C" {
#endif
-#include <odp_api.h>
+#include <odp/api/align.h>
+#include <odp/api/byteorder.h>
+#include <odp/api/debug.h>
/** @addtogroup odp_header ODP HEADER
* @{
@@ -38,6 +38,8 @@ typedef struct ODP_PACKED {
ODP_STATIC_ASSERT(sizeof(_odp_udphdr_t) == _ODP_UDPHDR_LEN,
"_ODP_UDPHDR_T__SIZE_ERROR");
+#define _ODP_UDP_IPSEC_PORT 4500
+
/**
* @}
*/
diff --git a/platform/linux-generic/libodp-linux.pc.in b/platform/linux-generic/libodp-linux.pc.in
new file mode 100644
index 000000000..62589c1a3
--- /dev/null
+++ b/platform/linux-generic/libodp-linux.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: lib@ODP_LIB_NAME@
+Description: The ODP packet processing engine
+Version: @PKGCONFIG_VERSION@
+Requires.private: libconfig@AARCH64CRYPTO_PKG@
+Libs: -L${libdir} -l@ODP_LIB_NAME@ @ATOMIC_LIBS_NON_ABI_COMPAT@
+Libs.private: @OPENSSL_STATIC_LIBS@ @DPDK_LIBS@ @PCAP_LIBS@ @PTHREAD_LIBS@ @TIMER_LIBS@ @LIBXDP_LIBS@ -lpthread @ATOMIC_LIBS_ABI_COMPAT@ @IPSEC_MB_LIBS@ @ORT_LIBS@
+Cflags: -I${includedir}
diff --git a/platform/linux-generic/m4/configure.m4 b/platform/linux-generic/m4/configure.m4
index 8e008de44..d662ecfcd 100644
--- a/platform/linux-generic/m4/configure.m4
+++ b/platform/linux-generic/m4/configure.m4
@@ -1,86 +1,87 @@
-# Enable -fvisibility=hidden if using a gcc that supports it
-OLD_CFLAGS="$CFLAGS"
-AC_MSG_CHECKING([whether $CC supports -fvisibility=hidden])
-VISIBILITY_CFLAGS="-fvisibility=hidden"
-CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
-AC_LINK_IFELSE([AC_LANG_PROGRAM()], AC_MSG_RESULT([yes]),
- [VISIBILITY_CFLAGS=""; AC_MSG_RESULT([no])]);
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2014 Linaro Limited
+#
-AC_SUBST(VISIBILITY_CFLAGS)
-# Restore CFLAGS; VISIBILITY_CFLAGS are added to it where needed.
-CFLAGS=$OLD_CFLAGS
+ODP_IMPLEMENTATION_NAME="odp-linux"
+ODP_LIB_NAME="odp-linux"
-AC_MSG_CHECKING(for GCC atomic builtins)
-AC_LINK_IFELSE(
- [AC_LANG_SOURCE(
- [[int main() {
- int v = 1;
- __atomic_fetch_add(&v, 1, __ATOMIC_RELAXED);
- __atomic_fetch_sub(&v, 1, __ATOMIC_RELAXED);
- __atomic_store_n(&v, 1, __ATOMIC_RELAXED);
- __atomic_load_n(&v, __ATOMIC_RELAXED);
- return 0;
- }
- ]])],
- AC_MSG_RESULT(yes),
- AC_MSG_RESULT(no)
- echo "GCC-style __atomic builtins not supported by the compiler."
- echo "Use newer version. For gcc > 4.7.0"
- exit -1)
+ODP_VISIBILITY
+ODP_ATOMIC
-dnl Check whether -latomic is needed
-use_libatomic=no
-
-AC_MSG_CHECKING(whether -latomic is needed for 64-bit atomic built-ins)
-AC_LINK_IFELSE(
- [AC_LANG_SOURCE([[
- #include <stdint.h>
- static uint64_t loc;
- int main(void)
- {
- uint64_t prev = __atomic_exchange_n(&loc, 7, __ATOMIC_RELAXED);
- return 0;
- }
- ]])],
- [AC_MSG_RESULT(no)],
- [AC_MSG_RESULT(yes)
- AC_CHECK_LIB(
- [atomic], [__atomic_exchange_8],
- [use_libatomic=yes],
- [AC_MSG_FAILURE([__atomic_exchange_8 is not available])])
- ])
-
-AC_MSG_CHECKING(whether -latomic is needed for 128-bit atomic built-ins)
-AC_LINK_IFELSE(
- [AC_LANG_SOURCE([[
- static __int128 loc;
- int main(void)
- {
- __int128 prev;
- prev = __atomic_exchange_n(&loc, 7, __ATOMIC_RELAXED);
- return 0;
- }
- ]])],
- [AC_MSG_RESULT(no)],
- [AC_MSG_RESULT(yes)
- AC_CHECK_LIB(
- [atomic], [__atomic_exchange_16],
- [use_libatomic=yes],
- [AC_MSG_CHECKING([cannot detect support for 128-bit atomics])])
- ])
+ODP_PTHREAD
+ODP_TIMER
+m4_include([platform/linux-generic/m4/odp_cpu.m4])
+m4_include([platform/linux-generic/m4/odp_event_validation.m4])
+m4_include([platform/linux-generic/m4/odp_pcap.m4])
+m4_include([platform/linux-generic/m4/odp_scheduler.m4])
-if test "x$use_libatomic" = "xyes"; then
- ATOMIC_LIBS="-latomic"
-fi
-AC_SUBST([ATOMIC_LIBS])
+AC_ARG_WITH([pcap],
+ [AS_HELP_STRING([--without-pcap],
+ [compile without PCAP [default=with] (linux-generic)])],
+ [],
+ [with_pcap=yes])
+have_pcap=no
+AS_IF([test "x$with_pcap" != xno],
+ [ODP_PCAP([with_pcap=yes]‚[with_pcap=no])])
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([ODP_PKTIO_PCAP], [test x$have_pcap = xyes])
+])
-m4_include([platform/linux-generic/m4/odp_pthread.m4])
-m4_include([platform/linux-generic/m4/odp_timer.m4])
+m4_include([platform/linux-generic/m4/odp_libconfig.m4])
m4_include([platform/linux-generic/m4/odp_openssl.m4])
-m4_include([platform/linux-generic/m4/odp_pcap.m4])
-m4_include([platform/linux-generic/m4/odp_netmap.m4])
+m4_include([platform/linux-generic/m4/odp_crypto.m4])
+m4_include([platform/linux-generic/m4/odp_ipsec_mb.m4])
+m4_include([platform/linux-generic/m4/odp_pcapng.m4])
m4_include([platform/linux-generic/m4/odp_dpdk.m4])
-m4_include([platform/linux-generic/m4/odp_schedule.m4])
+m4_include([platform/linux-generic/m4/odp_wfe.m4])
+m4_include([platform/linux-generic/m4/odp_xdp.m4])
+m4_include([platform/linux-generic/m4/odp_ml.m4])
+ODP_EVENT_VALIDATION
+ODP_SCHEDULER
+
+AS_VAR_APPEND([PLAT_DEP_LIBS], ["${ATOMIC_LIBS} ${AARCH64CRYPTO_LIBS} ${LIBCONFIG_LIBS} ${OPENSSL_LIBS} ${IPSEC_MB_LIBS} ${DPDK_LIBS_LT} ${LIBCLI_LIBS} ${LIBXDP_LIBS} ${ORT_LIBS}"])
+
+# Add text to the end of configure with platform specific settings.
+# Make sure it's aligned same as other lines in configure.ac.
+AS_VAR_APPEND([PLAT_CFG_TEXT], ["
+ event_validation: ${enable_event_validation}
+ openssl: ${with_openssl}
+ openssl_rand: ${openssl_rand}
+ crypto: ${with_crypto}
+ pcap: ${have_pcap}
+ pcapng: ${have_pcapng}
+ wfe_locks: ${use_wfe_locks}
+ ml_support: ${ml_support}
+ default_config_path: ${default_config_path}"])
+
+# Ignore Clang specific errors about fields with variable sized type not at the
+# end of a struct. This style is used by e.g. odp_packet_hdr_t and
+# odp_timeout_hdr_t.
+ODP_CHECK_CFLAG([-Wno-error=gnu-variable-sized-type-not-at-end])
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([PLATFORM_IS_LINUX_GENERIC],
+ [test "${with_platform}" = "linux-generic"])
AC_CONFIG_FILES([platform/linux-generic/Makefile
- platform/linux-generic/include/odp/api/plat/static_inline.h])
+ platform/linux-generic/libodp-linux.pc
+ platform/linux-generic/dumpconfig/Makefile
+ platform/linux-generic/example/Makefile
+ platform/linux-generic/example/ml/Makefile
+ platform/linux-generic/test/Makefile
+ platform/linux-generic/test/example/Makefile
+ platform/linux-generic/test/example/classifier/Makefile
+ platform/linux-generic/test/example/ipsec_api/Makefile
+ platform/linux-generic/test/example/ipsec_crypto/Makefile
+ platform/linux-generic/test/example/l2fwd_simple/Makefile
+ platform/linux-generic/test/example/l3fwd/Makefile
+ platform/linux-generic/test/example/packet/Makefile
+ platform/linux-generic/test/example/ping/Makefile
+ platform/linux-generic/test/example/simple_pipeline/Makefile
+ platform/linux-generic/test/example/switch/Makefile
+ platform/linux-generic/test/validation/api/shmem/Makefile
+ platform/linux-generic/test/validation/api/pktio/Makefile
+ platform/linux-generic/test/validation/api/ml/Makefile
+ platform/linux-generic/test/performance/Makefile
+ platform/linux-generic/test/performance/dmafwd/Makefile
+ platform/linux-generic/test/pktio_ipc/Makefile])
+])
diff --git a/platform/linux-generic/m4/odp_cpu.m4 b/platform/linux-generic/m4/odp_cpu.m4
new file mode 100644
index 000000000..f363813f1
--- /dev/null
+++ b/platform/linux-generic/m4/odp_cpu.m4
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 Nokia
+#
+
+##########################################################################
+# Set ODP_CACHE_LINE_SIZE define
+##########################################################################
+# Currently used only for aarch64
+if test "${ARCH_DIR}" = "aarch64"; then
+ cache_line_size=64
+ # Use default cache size if cross-compiling
+ if test $build = $host; then
+ cpu_implementer=""
+ cpu_part=""
+
+ AC_PROG_GREP
+ AC_PROG_SED
+ while read line; do
+ if echo $line | $GREP -q "CPU implementer"; then
+ cpu_implementer=`echo $line | $SED 's/.*\:\s*//'`
+ fi
+ if echo $line | $GREP -q "CPU part"; then
+ cpu_part=`echo $line | $SED 's/.*\:\s*//'`
+ fi
+ done < /proc/cpuinfo
+
+ # Cavium
+ if test "$cpu_implementer" == "0x43"; then
+ # ThunderX2 (0x0af) 64B, others 128B
+ if test "$cpu_part" == "0x0af"; then
+ cache_line_size=64;
+ else
+ cache_line_size=128;
+ fi
+ fi
+ fi
+ AC_DEFINE_UNQUOTED([_ODP_CACHE_LINE_SIZE], [$cache_line_size],
+ [Define cache line size])
+fi
diff --git a/platform/linux-generic/m4/odp_crypto.m4 b/platform/linux-generic/m4/odp_crypto.m4
new file mode 100644
index 000000000..e7cfa8865
--- /dev/null
+++ b/platform/linux-generic/m4/odp_crypto.m4
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 ARM Limited
+# Copyright (c) 2022 Nokia
+#
+
+# ODP_CRYPTO
+# ----------
+# Select default crypto implementation
+AC_ARG_WITH([crypto],
+ [AS_HELP_STRING([--with-crypto],
+ [Choose crypto implementation (openssl/armv8crypto/ipsecmb/null)]
+ [[default=openssl] (linux-generic)])],
+ [], [with_crypto=openssl])
+
+# Default to OpenSSL implementation if crypto is enabled
+AS_IF([test "x$with_crypto" = "xyes"], [with_crypto=openssl])
+
+# Default to Null implementation if crypto is disabled
+AS_IF([test "x$with_crypto" = "xno"], [with_crypto=null])
+AS_IF([test "x$with_crypto" = "xopenssl" -a "x$with_openssl" = "xno"], [with_crypto=null])
+
+AS_IF([test "x$with_crypto" != "xopenssl" -a "x$with_crypto" != "xarmv8crypto" -a "x$with_crypto" != "xipsecmb" -a "x$with_crypto" != "xnull"],
+ [AC_MSG_ERROR([Invalid crypto implementation name])])
+
+##########################################################################
+# OpenSSL implementation
+##########################################################################
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([WITH_OPENSSL_CRYPTO], [test "x$with_crypto" == "xopenssl"])
+])
+
+##########################################################################
+# ARMv8 Crypto library implementation
+##########################################################################
+AS_IF([test "x$with_crypto" == "xarmv8crypto"],
+ [PKG_CHECK_MODULES([AARCH64CRYPTO], [libAArch64crypto])
+ AARCH64CRYPTO_PKG=", libAArch64crypto"
+ AC_SUBST([AARCH64CRYPTO_PKG])])
+
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([WITH_ARMV8_CRYPTO], [test "x$with_crypto" == "xarmv8crypto"])
+])
+
+##########################################################################
+# Multi-buffer IPSec library implementation
+##########################################################################
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([WITH_IPSECMB_CRYPTO], [test "x$with_crypto" == "xipsecmb"])
+])
+
+##########################################################################
+# Null implementation
+##########################################################################
+AS_IF([test "x$with_crypto" == "xnull"],
+ [AC_MSG_WARN([Using null crypto. Strong cryptography is not available])])
diff --git a/platform/linux-generic/m4/odp_dpdk.m4 b/platform/linux-generic/m4/odp_dpdk.m4
index cebf10287..2e101df30 100644
--- a/platform/linux-generic/m4/odp_dpdk.m4
+++ b/platform/linux-generic/m4/odp_dpdk.m4
@@ -1,64 +1,72 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016 Linaro Limited
+#
+
##########################################################################
# Enable DPDK support
##########################################################################
pktio_dpdk_support=no
+
+AC_ARG_ENABLE([dpdk],
+ [AS_HELP_STRING([--enable-dpdk],
+ [enable DPDK support for Packet I/O [default=disabled] (linux-generic)])],
+ [pktio_dpdk_support=$enableval
+ DPDK_PATH=system])
+
AC_ARG_WITH([dpdk-path],
-AC_HELP_STRING([--with-dpdk-path=DIR path to dpdk build directory]),
+[AS_HELP_STRING([--with-dpdk-path=DIR],
+ [path to DPDK build directory [default=system] (linux-generic)])],
[DPDK_PATH="$withval"
- DPDK_CPPFLAGS="-msse4.2 -isystem $DPDK_PATH/include"
- pktio_dpdk_support=yes],[])
+ pktio_dpdk_support=yes],[])
+
+##########################################################################
+# Use shared DPDK library
+##########################################################################
+dpdk_shared=no
+AC_ARG_ENABLE([dpdk-shared],
+ [AS_HELP_STRING([--enable-dpdk-shared],
+ [use shared DPDK library [default=disabled] (linux-generic)])],
+ [if test x$enableval = xyes; then
+ dpdk_shared=yes
+ fi])
##########################################################################
# Enable zero-copy DPDK pktio
##########################################################################
zero_copy=0
AC_ARG_ENABLE([dpdk-zero-copy],
- [ --enable-dpdk-zero-copy enable experimental zero-copy DPDK pktio mode],
+ [AS_HELP_STRING([--enable-dpdk-zero-copy],
+ [enable experimental zero-copy DPDK pktio mode [default=disabled] (linux-generic)])],
[if test x$enableval = xyes; then
zero_copy=1
fi])
##########################################################################
-# Save and set temporary compilation flags
-##########################################################################
-OLD_CPPFLAGS="$CPPFLAGS"
-CPPFLAGS="$DPDK_CPPFLAGS $CPPFLAGS"
-
-##########################################################################
# Check for DPDK availability
#
# DPDK pmd drivers are not linked unless the --whole-archive option is
-# used. No spaces are allowed between the --whole-arhive flags.
+# used. No spaces are allowed between the --whole-archive flags.
##########################################################################
if test x$pktio_dpdk_support = xyes
then
- AC_CHECK_HEADERS([rte_config.h], [],
- [AC_MSG_FAILURE(["can't find DPDK header"])])
+ ODP_DPDK([$DPDK_PATH], [$dpdk_shared], [],
+ [AC_MSG_FAILURE([can't find DPDK])])
- AS_VAR_SET([DPDK_PMDS], [-Wl,--whole-archive,])
- for filename in "$DPDK_PATH"/lib/librte_pmd_*.a; do
- cur_driver=`basename "$filename" .a | sed -e 's/^lib//'`
- # rte_pmd_nfp has external dependencies which break linking
- if test "$cur_driver" = "rte_pmd_nfp"; then
- echo "skip linking rte_pmd_nfp"
- else
- AS_VAR_APPEND([DPDK_PMDS], [-l$cur_driver,])
- fi
- done
- AS_VAR_APPEND([DPDK_PMDS], [--no-whole-archive])
+ case "${host}" in
+ i?86* | x86*)
+ DPDK_CFLAGS="${DPDK_CFLAGS} -msse4.2"
+ ;;
+ esac
- ODP_CFLAGS="$ODP_CFLAGS -DODP_PKTIO_DPDK -DODP_DPDK_ZERO_COPY=$zero_copy"
- DPDK_LIBS="-L$DPDK_PATH/lib -ldpdk -lpthread -ldl -lpcap -lm"
- AC_SUBST([DPDK_CPPFLAGS])
- AC_SUBST([DPDK_LIBS])
- AC_SUBST([DPDK_PMDS])
+ ODP_CHECK_CFLAG([-Wno-error=cast-align])
+ AC_DEFINE([_ODP_PKTIO_DPDK], [1],
+ [Define to 1 to enable DPDK packet I/O support])
+ AC_DEFINE_UNQUOTED([_ODP_DPDK_ZERO_COPY], [$zero_copy],
+ [Define to 1 to enable DPDK zero copy support])
else
pktio_dpdk_support=no
fi
-##########################################################################
-# Restore old saved variables
-##########################################################################
-CPPFLAGS=$OLD_CPPFLAGS
-
+AC_CONFIG_COMMANDS_PRE([dnl
AM_CONDITIONAL([PKTIO_DPDK], [test x$pktio_dpdk_support = xyes ])
+])
diff --git a/platform/linux-generic/m4/odp_event_validation.m4 b/platform/linux-generic/m4/odp_event_validation.m4
new file mode 100644
index 000000000..df466d4ee
--- /dev/null
+++ b/platform/linux-generic/m4/odp_event_validation.m4
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 Nokia
+#
+
+# ODP_EVENT_VALIDATION
+# --------------------
+# Select event validation level
+AC_DEFUN([ODP_EVENT_VALIDATION], [dnl
+AC_ARG_ENABLE([event-validation],
+ [AS_HELP_STRING([--enable-event-validation],
+ [enable event validation (warn/abort)
+ [default=disabled] (linux-generic)])],
+ [], [AS_IF([test "x$enable_debug" = "xfull"],
+ [enable_event_validation=yes], [enable_event_validation=no])])
+
+# Default to abort mode if validation is enabled
+AS_IF([test "x$enable_event_validation" = "xyes"],
+ [enable_event_validation="abort"])
+
+validation_level=0
+AS_IF([test "x$enable_event_validation" = "xwarn"], [validation_level=1])
+AS_IF([test "x$enable_event_validation" = "xyes" -o "x$enable_event_validation" = "xabort"],
+ [validation_level=2])
+
+AC_DEFINE_UNQUOTED([_ODP_EVENT_VALIDATION], [$validation_level],
+ [Define to 1 or 2 to enable event validation])
+]) # ODP_EVENT_VALIDATION
diff --git a/platform/linux-generic/m4/odp_ipsec_mb.m4 b/platform/linux-generic/m4/odp_ipsec_mb.m4
new file mode 100644
index 000000000..d85d179a3
--- /dev/null
+++ b/platform/linux-generic/m4/odp_ipsec_mb.m4
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 ARM Limited
+#
+
+#########################################################################
+# Check for libIPSec_MB availability
+#########################################################################
+ipsecmb_support=no
+AC_CHECK_HEADERS([ipsec-mb.h],
+ [AC_CHECK_LIB([IPSec_MB], [init_mb_mgr_auto], [ipsecmb_support=yes],
+ [ipsecmb_support=no])],
+ [ipsecmb_support=no])
+
+AS_IF([test "x$with_crypto" = "xipsecmb" -a "x$ipsecmb_support" = "xno"],
+ [AC_MSG_ERROR([IPSec MB library not found on this platform])])
+
+if test "x$with_crypto" = "xipsecmb"; then
+ IPSEC_MB_LIBS="-lIPSec_MB"
+else
+ IPSEC_MB_LIBS=""
+fi
+
+AC_SUBST([IPSEC_MB_LIBS])
diff --git a/platform/linux-generic/m4/odp_libconfig.m4 b/platform/linux-generic/m4/odp_libconfig.m4
new file mode 100644
index 000000000..25ffd87a4
--- /dev/null
+++ b/platform/linux-generic/m4/odp_libconfig.m4
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Nokia
+#
+
+##########################################################################
+# Configuration file version
+##########################################################################
+m4_define([_odp_config_version_generation], [0])
+m4_define([_odp_config_version_major], [1])
+m4_define([_odp_config_version_minor], [28])
+
+m4_define([_odp_config_version],
+ [_odp_config_version_generation._odp_config_version_major._odp_config_version_minor])
+
+_ODP_CONFIG_VERSION_GENERATION=_odp_config_version_generation
+AC_SUBST(_ODP_CONFIG_VERSION_GENERATION)
+_ODP_CONFIG_VERSION_MAJOR=_odp_config_version_major
+AC_SUBST(_ODP_CONFIG_VERSION_MAJOR)
+_ODP_CONFIG_VERSION_MINOR=_odp_config_version_minor
+AC_SUBST(_ODP_CONFIG_VERSION_MINOR)
+
+##########################################################################
+# Set optional path for the default configuration file
+##########################################################################
+default_config_path="${srcdir}/config/odp-linux-generic.conf"
+
+AC_CHECK_PROGS([REALPATH], [realpath])
+AS_IF([test -z "$REALPATH"], [AC_MSG_ERROR([Could not find 'realpath'])])
+
+AC_ARG_WITH([config-file],
+AS_HELP_STRING([--with-config-file=FILE], [path to the default configuration file]
+ [(this file must include all configuration options)]
+ [[default=SRCDIR/config/odp-<platform>.conf]]),
+ [default_config_path=$withval], [])
+
+rel_default_config_path=`realpath --relative-to=$(pwd) ${default_config_path}`
+AC_SUBST(default_config_path)
+AC_SUBST(rel_default_config_path)
+
+ODP_LIBCONFIG([linux-generic], [$rel_default_config_path])
diff --git a/platform/linux-generic/m4/odp_ml.m4 b/platform/linux-generic/m4/odp_ml.m4
new file mode 100644
index 000000000..ab424ab39
--- /dev/null
+++ b/platform/linux-generic/m4/odp_ml.m4
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+##########################################################################
+# Onnxruntime library path and name
+##########################################################################
+# Optional configure parameter for a non-standard install prefix of onnxruntime
+AC_ARG_WITH([ort-path],
+ [AS_HELP_STRING([--with-ort-path=DIR],
+ [path to onnxruntime libs and headers [default=system]])],
+ [ort_path_given=yes
+ ORT_CPPFLAGS="-I$withval/include"
+ ORT_LIBS="-L$withval/lib"
+ ORT_RPATH="-R$withval/lib"],
+ [])
+
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_CPPFLAGS=$CPPFLAGS
+OLD_LIBS=$LIBS
+CPPFLAGS="$ORT_CPPFLAGS $CPPFLAGS"
+LIBS="$ORT_LIBS $LIBS"
+
+#########################################################################
+# If ort is available, enable ML API
+#########################################################################
+ml_support=no
+AC_CHECK_HEADERS([onnxruntime_c_api.h],
+ [AC_CHECK_LIB(onnxruntime, OrtGetApiBase, [ml_support=yes], [], [])],
+ [AS_IF([test "x$ort_path_given" = "xyes"],
+ [AC_MSG_ERROR([ort not found at the specified path (--with-ort-path)])])])
+
+AS_IF([test "x$ml_support" != "xno"],
+ [ORT_LIBS="$ORT_RPATH $ORT_LIBS -lonnxruntime -lm"],
+ [ORT_CPPFLAGS="" ORT_LIBS="-lm"])
+
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([WITH_ML], [test x$ml_support = xyes ])
+])
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+LIBS=$OLD_LIBS
+CPPFLAGS=$OLD_CPPFLAGS
+
+AC_SUBST([ORT_CPPFLAGS])
+AC_SUBST([ORT_LIBS])
diff --git a/platform/linux-generic/m4/odp_netmap.m4 b/platform/linux-generic/m4/odp_netmap.m4
deleted file mode 100644
index dea80626b..000000000
--- a/platform/linux-generic/m4/odp_netmap.m4
+++ /dev/null
@@ -1,45 +0,0 @@
-##########################################################################
-# Enable netmap support
-##########################################################################
-netmap_support=no
-AC_ARG_ENABLE([netmap_support],
- [ --enable-netmap-support include netmap IO support],
- [if test x$enableval = xyes; then
- netmap_support=yes
- fi])
-
-##########################################################################
-# Set optional netmap path
-##########################################################################
-AC_ARG_WITH([netmap-path],
-AC_HELP_STRING([--with-netmap-path=DIR path to netmap root directory],
- [(or in the default path if not specified).]),
- [NETMAP_PATH=$withval
- NETMAP_CPPFLAGS="-isystem $NETMAP_PATH/sys"
- netmap_support=yes],[])
-
-##########################################################################
-# Save and set temporary compilation flags
-##########################################################################
-OLD_CPPFLAGS=$CPPFLAGS
-CPPFLAGS="$NETMAP_CPPFLAGS $CPPFLAGS"
-
-##########################################################################
-# Check for netmap availability
-##########################################################################
-if test x$netmap_support = xyes
-then
- AC_CHECK_HEADERS([net/netmap_user.h], [],
- [AC_MSG_FAILURE(["can't find netmap header"])])
- ODP_CFLAGS="$ODP_CFLAGS -DODP_NETMAP"
- AC_SUBST([NETMAP_CPPFLAGS])
-else
- netmap_support=no
-fi
-
-##########################################################################
-# Restore old saved variables
-##########################################################################
-CPPFLAGS=$OLD_CPPFLAGS
-
-AM_CONDITIONAL([netmap_support], [test x$netmap_support = xyes ])
diff --git a/platform/linux-generic/m4/odp_openssl.m4 b/platform/linux-generic/m4/odp_openssl.m4
index 1d520077c..4f51b6b01 100644
--- a/platform/linux-generic/m4/odp_openssl.m4
+++ b/platform/linux-generic/m4/odp_openssl.m4
@@ -1,37 +1,37 @@
-##########################################################################
-# Set optional OpenSSL path
-##########################################################################
-AC_ARG_WITH([openssl-path],
-AC_HELP_STRING([--with-openssl-path=DIR path to openssl libs and headers],
- [(or in the default path if not specified).]),
- [OPENSSL_PATH=$withval
- OPENSSL_CPPFLAGS="-I$OPENSSL_PATH/include"
- OPENSSL_LIBS="-L$OPENSSL_PATH/lib"
- ],[])
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015 Linaro Limited
+#
##########################################################################
-# Save and set temporary compilation flags
-##########################################################################
-OLD_LDFLAGS=$LDFLAGS
-OLD_CPPFLAGS=$CPPFLAGS
-LIBS="$OPENSSL_LIBS $LIBS"
-CPPFLAGS="$OPENSSL_CPPFLAGS $CPPFLAGS"
+# Enable/disable usage of OpenSSL library
+##########################################################################
+AC_ARG_WITH([openssl],
+ [AS_HELP_STRING([--without-openssl],
+ [compile without OpenSSL (may result in disabled crypto and random support)]
+ [[default=with] (linux-generic)])],
+ [],
+ [with_openssl=yes])
+AS_IF([test "$with_openssl" != "no"],
+ [ODP_OPENSSL
+ have_openssl=1], [have_openssl=0])
+AM_CONDITIONAL([WITH_OPENSSL], [test x$with_openssl != xno])
+AC_DEFINE_UNQUOTED([_ODP_OPENSSL], [$have_openssl],
+ [Define to 1 to enable OpenSSL support])
##########################################################################
-# Check for OpenSSL availability
+# Enable/disable usage of OpenSSL for random data
##########################################################################
-AC_CHECK_LIB([crypto], [EVP_EncryptInit], [OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto"
- OPENSSL_STATIC_LIBS="$OPENSSL_LIBS -ldl"],
- [AC_MSG_FAILURE([OpenSSL libraries required])])
-AC_CHECK_HEADERS([openssl/des.h openssl/rand.h openssl/hmac.h openssl/evp.h], [],
- [AC_MSG_ERROR([OpenSSL headers required])])
+have_openssl_rand=1
+AC_ARG_ENABLE([openssl-rand],
+ [AS_HELP_STRING([--disable-openssl-rand],
+ [disable OpenSSL random data (use arch-specific instead)]
+ [[default=enabled] (linux-generic)])],
+ [if test "x$enableval" = "xno"; then
+ have_openssl_rand=0
+ fi])
-AC_SUBST([OPENSSL_CPPFLAGS])
-AC_SUBST([OPENSSL_LIBS])
-AC_SUBST([OPENSSL_STATIC_LIBS])
+AS_IF([test "$have_openssl" != "1"], [have_openssl_rand=0])
+AS_IF([test "$have_openssl_rand" = "1"], [openssl_rand=yes], [openssl_rand=no])
-##########################################################################
-# Restore old saved variables
-##########################################################################
-LIBS=$OLD_LIBS
-CPPFLAGS=$OLD_CPPFLAGS
+AC_DEFINE_UNQUOTED([_ODP_OPENSSL_RAND], [$have_openssl_rand],
+ [Define to 1 to enable OpenSSL support])
diff --git a/platform/linux-generic/m4/odp_pcap.m4 b/platform/linux-generic/m4/odp_pcap.m4
index d7295f21b..cc74d0ca3 100644
--- a/platform/linux-generic/m4/odp_pcap.m4
+++ b/platform/linux-generic/m4/odp_pcap.m4
@@ -1,3 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015 Linaro Limited
+#
+
+# ODP_PCAP([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# --------------------------------------------------
+AC_DEFUN([ODP_PCAP],
+[dnl
#########################################################################
# Check for libpcap availability
#########################################################################
@@ -9,10 +17,18 @@ AC_CHECK_HEADER(pcap/pcap.h,
[])
if test "$have_pcap" = "yes"; then
- ODP_CFLAGS="$AM_CFLAGS -DHAVE_PCAP"
+ AC_DEFINE([_ODP_PKTIO_PCAP], 1,
+ [Define to 1 to enable pcap packet I/O support])
PCAP_LIBS="-lpcap"
+else
+ PCAP_LIBS=""
fi
AC_SUBST([PCAP_LIBS])
-AM_CONDITIONAL([HAVE_PCAP], [test $have_pcap = yes])
+if test "x$have_pcap" = "xyes" ; then
+ m4_default([$1], [:])
+else
+ m4_default([$2], [:])
+fi
+]) # ODP_PCAP
diff --git a/platform/linux-generic/m4/odp_pcapng.m4 b/platform/linux-generic/m4/odp_pcapng.m4
new file mode 100644
index 000000000..58e537478
--- /dev/null
+++ b/platform/linux-generic/m4/odp_pcapng.m4
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+##########################################################################
+# Enable PCAPNG support
+##########################################################################
+have_pcapng=no
+pcapng_support=0
+
+AC_ARG_ENABLE([pcapng-support],
+ [AS_HELP_STRING([--enable-pcapng-support],
+ [enable experimental tcpdump for pktios [default=disabled] (linux-generic)])],
+ have_pcapng=$enableval
+ [if test x$enableval = xyes; then
+ pcapng_support=1
+ fi])
+
+AC_DEFINE_UNQUOTED([_ODP_PCAPNG], [$pcapng_support],
+ [Define to 1 to enable pcapng support])
+
+AC_CONFIG_COMMANDS_PRE([dnl
+AM_CONDITIONAL([have_pcapng], [test x$have_pcapng = xyes])
+])
diff --git a/platform/linux-generic/m4/odp_pthread.m4 b/platform/linux-generic/m4/odp_pthread.m4
deleted file mode 100644
index 486158942..000000000
--- a/platform/linux-generic/m4/odp_pthread.m4
+++ /dev/null
@@ -1,8 +0,0 @@
-##########################################################################
-# Check for pthreads availability
-##########################################################################
-
-AX_PTHREAD([CC="$PTHREAD_CC"], [
- echo "Error! We require pthreads to be available"
- exit -1
- ])
diff --git a/platform/linux-generic/m4/odp_schedule.m4 b/platform/linux-generic/m4/odp_schedule.m4
deleted file mode 100644
index 91c19f21a..000000000
--- a/platform/linux-generic/m4/odp_schedule.m4
+++ /dev/null
@@ -1,13 +0,0 @@
-AC_ARG_ENABLE([schedule-sp],
- [ --enable-schedule-sp enable strict priority scheduler],
- [if test x$enableval = xyes; then
- schedule_sp_enabled=yes
- ODP_CFLAGS="$ODP_CFLAGS -DODP_SCHEDULE_SP"
- fi])
-
-AC_ARG_ENABLE([schedule-iquery],
- [ --enable-schedule-iquery enable interests query (sparse bitmap) scheduler],
- [if test x$enableval = xyes; then
- schedule_iquery_enabled=yes
- ODP_CFLAGS="$ODP_CFLAGS -DODP_SCHEDULE_IQUERY"
- fi])
diff --git a/platform/linux-generic/m4/odp_scheduler.m4 b/platform/linux-generic/m4/odp_scheduler.m4
new file mode 100644
index 000000000..d6f1c6e5c
--- /dev/null
+++ b/platform/linux-generic/m4/odp_scheduler.m4
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016 Linaro Limited
+#
+
+# ODP_SCHEDULER
+# -------------
+# Select default scheduler
+AC_DEFUN([ODP_SCHEDULER], [dnl
+AC_ARG_ENABLE([scheduler-default],
+ [AS_HELP_STRING([--enable-scheduler-default],
+ [choose default scheduler [default=basic] (linux-generic)])],
+ [], [enable_scheduler_default=basic])
+AC_DEFINE_UNQUOTED([_ODP_SCHEDULE_DEFAULT], ["$enable_scheduler_default"],
+ [Define to name default scheduler])
+]) # ODP_SCHEDULER
diff --git a/platform/linux-generic/m4/odp_timer.m4 b/platform/linux-generic/m4/odp_timer.m4
deleted file mode 100644
index 3122c92b4..000000000
--- a/platform/linux-generic/m4/odp_timer.m4
+++ /dev/null
@@ -1,8 +0,0 @@
-##########################################################################
-# Check for POSIX timer functions
-##########################################################################
-
-AC_CHECK_LIB([rt], [timer_create], [TIMER_LIBS="-lrt"],
- [AC_CHECK_LIB([posix4], [timer_create], [TIMER_LIBS="-lposix4"],
- [AC_MSG_FAILURE([timer_create not found])])])
-AC_SUBST([TIMER_LIBS])
diff --git a/platform/linux-generic/m4/odp_wfe.m4 b/platform/linux-generic/m4/odp_wfe.m4
new file mode 100644
index 000000000..678e6229b
--- /dev/null
+++ b/platform/linux-generic/m4/odp_wfe.m4
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+##########################################################################
+# Enable/disable WFE based lock implementations
+##########################################################################
+use_wfe_locks=no
+AC_ARG_ENABLE([wfe-locks],
+ [AS_HELP_STRING([--enable-wfe-locks],
+ [enable WFE based lock implementations on aarch64]
+ [[default=disabled] (linux-generic)])],
+ [use_wfe_locks=$enableval])
+
+if test x$use_wfe_locks = xyes; then
+ AC_DEFINE([_ODP_WFE_LOCKS], [1],
+ [Define to 1 to enable WFE based lock implementations on aarch64])
+fi
diff --git a/platform/linux-generic/m4/odp_xdp.m4 b/platform/linux-generic/m4/odp_xdp.m4
new file mode 100644
index 000000000..6820088af
--- /dev/null
+++ b/platform/linux-generic/m4/odp_xdp.m4
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 Nokia
+#
+
+##########################################################################
+# Check for libxdp availability
+##########################################################################
+AC_ARG_ENABLE([xdp], AS_HELP_STRING([--enable-xdp],
+ [enable experimental XDP support for Packet I/O [default=disabled] (linux-generic)]))
+
+AS_IF([test "x$enable_xdp" = "xyes"], [
+ PKG_CHECK_MODULES([LIBXDP], [libxdp >= 1.2.3],
+ [
+ AC_DEFINE(_ODP_PKTIO_XDP, [1], [Define to 1 to enable xdp packet I/O support])
+ ],
+ [
+ AS_IF([test "x$enable_xdp" == "xyes"], [AC_MSG_ERROR([libxdp not found])])
+ ])
+])
diff --git a/platform/linux-generic/miniz/miniz.c b/platform/linux-generic/miniz/miniz.c
new file mode 100644
index 000000000..d0e39ec4a
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz.c
@@ -0,0 +1,619 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "miniz.h"
+
+typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];
+typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
+typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- zlib-style API's */
+
+mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
+{
+ mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16);
+ size_t block_len = buf_len % 5552;
+ if (!ptr)
+ return MZ_ADLER32_INIT;
+ while (buf_len)
+ {
+ for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
+ {
+ s1 += ptr[0], s2 += s1;
+ s1 += ptr[1], s2 += s1;
+ s1 += ptr[2], s2 += s1;
+ s1 += ptr[3], s2 += s1;
+ s1 += ptr[4], s2 += s1;
+ s1 += ptr[5], s2 += s1;
+ s1 += ptr[6], s2 += s1;
+ s1 += ptr[7], s2 += s1;
+ }
+ for (; i < block_len; ++i)
+ s1 += *ptr++, s2 += s1;
+ s1 %= 65521U, s2 %= 65521U;
+ buf_len -= block_len;
+ block_len = 5552;
+ }
+ return (s2 << 16) + s1;
+}
+
+/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */
+#if 0
+ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
+ {
+ static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
+ mz_uint32 crcu32 = (mz_uint32)crc;
+ if (!ptr)
+ return MZ_CRC32_INIT;
+ crcu32 = ~crcu32;
+ while (buf_len--)
+ {
+ mz_uint8 b = *ptr++;
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
+ }
+ return ~crcu32;
+ }
+#else
+/* Faster, but larger CPU cache footprint.
+ */
+mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
+{
+ static const mz_uint32 s_crc_table[256] =
+ {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
+ 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
+ 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
+ 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
+ 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
+ 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
+ 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
+ 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB,
+ 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
+ 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE,
+ 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
+ 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409,
+ 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739,
+ 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268,
+ 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,
+ 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,
+ 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
+ 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703,
+ 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,
+ 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE,
+ 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,
+ 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,
+ 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5,
+ 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
+ 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ };
+
+ mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF;
+ const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr;
+
+ while (buf_len >= 4)
+ {
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF];
+ pByte_buf += 4;
+ buf_len -= 4;
+ }
+
+ while (buf_len)
+ {
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
+ ++pByte_buf;
+ --buf_len;
+ }
+
+ return ~crc32;
+}
+#endif
+
+void mz_free(void *p)
+{
+ MZ_FREE(p);
+}
+
+void *miniz_def_alloc_func(void *opaque, size_t items, size_t size)
+{
+ (void)opaque, (void)items, (void)size;
+ return MZ_MALLOC(items * size);
+}
+void miniz_def_free_func(void *opaque, void *address)
+{
+ (void)opaque, (void)address;
+ MZ_FREE(address);
+}
+void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size)
+{
+ (void)opaque, (void)address, (void)items, (void)size;
+ return MZ_REALLOC(address, items * size);
+}
+
+const char *mz_version(void)
+{
+ return MZ_VERSION;
+}
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+int mz_deflateInit(mz_streamp pStream, int level)
+{
+ return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
+}
+
+int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
+{
+ tdefl_compressor *pComp;
+ mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
+
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)))
+ return MZ_PARAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = MZ_ADLER32_INIT;
+ pStream->msg = NULL;
+ pStream->reserved = 0;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ if (!pStream->zalloc)
+ pStream->zalloc = miniz_def_alloc_func;
+ if (!pStream->zfree)
+ pStream->zfree = miniz_def_free_func;
+
+ pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
+ if (!pComp)
+ return MZ_MEM_ERROR;
+
+ pStream->state = (struct mz_internal_state *)pComp;
+
+ if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
+ {
+ mz_deflateEnd(pStream);
+ return MZ_PARAM_ERROR;
+ }
+
+ return MZ_OK;
+}
+
+int mz_deflateReset(mz_streamp pStream)
+{
+ if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree))
+ return MZ_STREAM_ERROR;
+ pStream->total_in = pStream->total_out = 0;
+ tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags);
+ return MZ_OK;
+}
+
+int mz_deflate(mz_streamp pStream, int flush)
+{
+ size_t in_bytes, out_bytes;
+ mz_ulong orig_total_in, orig_total_out;
+ int mz_status = MZ_OK;
+
+ if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out))
+ return MZ_STREAM_ERROR;
+ if (!pStream->avail_out)
+ return MZ_BUF_ERROR;
+
+ if (flush == MZ_PARTIAL_FLUSH)
+ flush = MZ_SYNC_FLUSH;
+
+ if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
+ return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
+
+ orig_total_in = pStream->total_in;
+ orig_total_out = pStream->total_out;
+ for (;;)
+ {
+ tdefl_status defl_status;
+ in_bytes = pStream->avail_in;
+ out_bytes = pStream->avail_out;
+
+ defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state);
+
+ pStream->next_out += (mz_uint)out_bytes;
+ pStream->avail_out -= (mz_uint)out_bytes;
+ pStream->total_out += (mz_uint)out_bytes;
+
+ if (defl_status < 0)
+ {
+ mz_status = MZ_STREAM_ERROR;
+ break;
+ }
+ else if (defl_status == TDEFL_STATUS_DONE)
+ {
+ mz_status = MZ_STREAM_END;
+ break;
+ }
+ else if (!pStream->avail_out)
+ break;
+ else if ((!pStream->avail_in) && (flush != MZ_FINISH))
+ {
+ if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
+ break;
+ return MZ_BUF_ERROR; /* Can't make forward progress without some input.
+ */
+ }
+ }
+ return mz_status;
+}
+
+int mz_deflateEnd(mz_streamp pStream)
+{
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if (pStream->state)
+ {
+ pStream->zfree(pStream->opaque, pStream->state);
+ pStream->state = NULL;
+ }
+ return MZ_OK;
+}
+
+mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
+{
+ (void)pStream;
+ /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */
+ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
+}
+
+int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
+{
+ int status;
+ mz_stream stream;
+ memset(&stream, 0, sizeof(stream));
+
+ /* In case mz_ulong is 64-bits (argh I hate longs). */
+ if ((source_len | *pDest_len) > 0xFFFFFFFFU)
+ return MZ_PARAM_ERROR;
+
+ stream.next_in = pSource;
+ stream.avail_in = (mz_uint32)source_len;
+ stream.next_out = pDest;
+ stream.avail_out = (mz_uint32)*pDest_len;
+
+ status = mz_deflateInit(&stream, level);
+ if (status != MZ_OK)
+ return status;
+
+ status = mz_deflate(&stream, MZ_FINISH);
+ if (status != MZ_STREAM_END)
+ {
+ mz_deflateEnd(&stream);
+ return (status == MZ_OK) ? MZ_BUF_ERROR : status;
+ }
+
+ *pDest_len = stream.total_out;
+ return mz_deflateEnd(&stream);
+}
+
+int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+ return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
+}
+
+mz_ulong mz_compressBound(mz_ulong source_len)
+{
+ return mz_deflateBound(NULL, source_len);
+}
+
+int mz_inflateInit2(mz_streamp pStream, int window_bits)
+{
+ inflate_state *pDecomp;
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))
+ return MZ_PARAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = 0;
+ pStream->msg = NULL;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ pStream->reserved = 0;
+ if (!pStream->zalloc)
+ pStream->zalloc = miniz_def_alloc_func;
+ if (!pStream->zfree)
+ pStream->zfree = miniz_def_free_func;
+
+ pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
+ if (!pDecomp)
+ return MZ_MEM_ERROR;
+
+ pStream->state = (struct mz_internal_state *)pDecomp;
+
+ tinfl_init(&pDecomp->m_decomp);
+ pDecomp->m_dict_ofs = 0;
+ pDecomp->m_dict_avail = 0;
+ pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
+ pDecomp->m_first_call = 1;
+ pDecomp->m_has_flushed = 0;
+ pDecomp->m_window_bits = window_bits;
+
+ return MZ_OK;
+}
+
+int mz_inflateInit(mz_streamp pStream)
+{
+ return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
+}
+
+int mz_inflateReset(mz_streamp pStream)
+{
+ inflate_state *pDecomp;
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = 0;
+ pStream->msg = NULL;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ pStream->reserved = 0;
+
+ pDecomp = (inflate_state *)pStream->state;
+
+ tinfl_init(&pDecomp->m_decomp);
+ pDecomp->m_dict_ofs = 0;
+ pDecomp->m_dict_avail = 0;
+ pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
+ pDecomp->m_first_call = 1;
+ pDecomp->m_has_flushed = 0;
+ /* pDecomp->m_window_bits = window_bits */;
+
+ return MZ_OK;
+}
+
+int mz_inflate(mz_streamp pStream, int flush)
+{
+ inflate_state *pState;
+ mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
+ size_t in_bytes, out_bytes, orig_avail_in;
+ tinfl_status status;
+
+ if ((!pStream) || (!pStream->state))
+ return MZ_STREAM_ERROR;
+ if (flush == MZ_PARTIAL_FLUSH)
+ flush = MZ_SYNC_FLUSH;
+ if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH))
+ return MZ_STREAM_ERROR;
+
+ pState = (inflate_state *)pStream->state;
+ if (pState->m_window_bits > 0)
+ decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
+ orig_avail_in = pStream->avail_in;
+
+ first_call = pState->m_first_call;
+ pState->m_first_call = 0;
+ if (pState->m_last_status < 0)
+ return MZ_DATA_ERROR;
+
+ if (pState->m_has_flushed && (flush != MZ_FINISH))
+ return MZ_STREAM_ERROR;
+ pState->m_has_flushed |= (flush == MZ_FINISH);
+
+ if ((flush == MZ_FINISH) && (first_call))
+ {
+ /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */
+ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
+ in_bytes = pStream->avail_in;
+ out_bytes = pStream->avail_out;
+ status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
+ pState->m_last_status = status;
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+ pStream->next_out += (mz_uint)out_bytes;
+ pStream->avail_out -= (mz_uint)out_bytes;
+ pStream->total_out += (mz_uint)out_bytes;
+
+ if (status < 0)
+ return MZ_DATA_ERROR;
+ else if (status != TINFL_STATUS_DONE)
+ {
+ pState->m_last_status = TINFL_STATUS_FAILED;
+ return MZ_BUF_ERROR;
+ }
+ return MZ_STREAM_END;
+ }
+ /* flush != MZ_FINISH then we must assume there's more input. */
+ if (flush != MZ_FINISH)
+ decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
+
+ if (pState->m_dict_avail)
+ {
+ n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+ memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+ pStream->next_out += n;
+ pStream->avail_out -= n;
+ pStream->total_out += n;
+ pState->m_dict_avail -= n;
+ pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+ return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+ }
+
+ for (;;)
+ {
+ in_bytes = pStream->avail_in;
+ out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
+
+ status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
+ pState->m_last_status = status;
+
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+
+ pState->m_dict_avail = (mz_uint)out_bytes;
+
+ n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+ memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+ pStream->next_out += n;
+ pStream->avail_out -= n;
+ pStream->total_out += n;
+ pState->m_dict_avail -= n;
+ pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+
+ if (status < 0)
+ return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */
+ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
+ return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */
+ else if (flush == MZ_FINISH)
+ {
+ /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */
+ if (status == TINFL_STATUS_DONE)
+ return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
+ /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */
+ else if (!pStream->avail_out)
+ return MZ_BUF_ERROR;
+ }
+ else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
+ break;
+ }
+
+ return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+}
+
+int mz_inflateEnd(mz_streamp pStream)
+{
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if (pStream->state)
+ {
+ pStream->zfree(pStream->opaque, pStream->state);
+ pStream->state = NULL;
+ }
+ return MZ_OK;
+}
+
+int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+ mz_stream stream;
+ int status;
+ memset(&stream, 0, sizeof(stream));
+
+ /* In case mz_ulong is 64-bits (argh I hate longs). */
+ if ((source_len | *pDest_len) > 0xFFFFFFFFU)
+ return MZ_PARAM_ERROR;
+
+ stream.next_in = pSource;
+ stream.avail_in = (mz_uint32)source_len;
+ stream.next_out = pDest;
+ stream.avail_out = (mz_uint32)*pDest_len;
+
+ status = mz_inflateInit(&stream);
+ if (status != MZ_OK)
+ return status;
+
+ status = mz_inflate(&stream, MZ_FINISH);
+ if (status != MZ_STREAM_END)
+ {
+ mz_inflateEnd(&stream);
+ return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
+ }
+ *pDest_len = stream.total_out;
+
+ return mz_inflateEnd(&stream);
+}
+
+const char *mz_error(int err)
+{
+ static struct
+ {
+ int m_err;
+ const char *m_pDesc;
+ } s_error_descs[] =
+ {
+ { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
+ };
+ mz_uint i;
+ for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i)
+ if (s_error_descs[i].m_err == err)
+ return s_error_descs[i].m_pDesc;
+ return NULL;
+}
+
+#endif /*MINIZ_NO_ZLIB_APIS */
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ For more information, please refer to <http://unlicense.org/>
+*/
diff --git a/platform/linux-generic/miniz/miniz.h b/platform/linux-generic/miniz/miniz.h
new file mode 100644
index 000000000..a2f90d907
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz.h
@@ -0,0 +1,363 @@
+/* miniz.c 2.1.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
+ See "unlicense" statement at the end of this file.
+ Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
+ Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
+
+ Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
+ MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
+
+ * Low-level Deflate/Inflate implementation notes:
+
+ Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
+ greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
+ approximately as well as zlib.
+
+ Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
+ coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
+ block large enough to hold the entire file.
+
+ The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
+
+ * zlib-style API notes:
+
+ miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
+ zlib replacement in many apps:
+ The z_stream struct, optional memory allocation callbacks
+ deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
+ inflateInit/inflateInit2/inflate/inflateReset/inflateEnd
+ compress, compress2, compressBound, uncompress
+ CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
+ Supports raw deflate streams or standard zlib streams with adler-32 checking.
+
+ Limitations:
+ The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
+ I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
+ there are no guarantees that miniz.c pulls this off perfectly.
+
+ * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
+ Alex Evans. Supports 1-4 bytes/pixel images.
+
+ * ZIP archive API notes:
+
+ The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
+ get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
+ existing archives, create new archives, append new files to existing archives, or clone archive data from
+ one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
+ or you can specify custom file read/write callbacks.
+
+ - Archive reading: Just call this function to read a single file from a disk archive:
+
+ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
+ size_t *pSize, mz_uint zip_flags);
+
+ For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
+ directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
+
+ - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
+
+ int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
+
+ The locate operation can optionally check file comments too, which (as one example) can be used to identify
+ multiple versions of the same file in an archive. This function uses a simple linear search through the central
+ directory, so it's not very fast.
+
+ Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
+ retrieve detailed info on each file by calling mz_zip_reader_file_stat().
+
+ - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
+ to disk and builds an exact image of the central directory in memory. The central directory image is written
+ all at once at the end of the archive file when the archive is finalized.
+
+ The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
+ which can be useful when the archive will be read from optical media. Also, the writer supports placing
+ arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
+ readable by any ZIP tool.
+
+ - Archive appending: The simple way to add a single file to an archive is to call this function:
+
+ mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
+ const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+
+ The archive will be created if it doesn't already exist, otherwise it'll be appended to.
+ Note the appending is done in-place and is not an atomic operation, so if something goes wrong
+ during the operation it's possible the archive could be left without a central directory (although the local
+ file headers and file data will be fine, so the archive will be recoverable).
+
+ For more complex archive modification scenarios:
+ 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
+ preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
+ compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
+ you're done. This is safe but requires a bunch of temporary disk space or heap memory.
+
+ 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
+ append new files as needed, then finalize the archive which will write an updated central directory to the
+ original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
+ possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
+
+ - ZIP archive support limitations:
+ No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
+ Requires streams capable of seeking.
+
+ * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
+ below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
+
+ * Important: For best perf. be sure to customize the below macros for your target platform:
+ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
+ #define MINIZ_LITTLE_ENDIAN 1
+ #define MINIZ_HAS_64BIT_REGISTERS 1
+
+ * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
+ uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
+ (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
+*/
+#pragma once
+
+/* Defines to completely disable specific portions of miniz.c:
+ If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */
+
+/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */
+/*#define MINIZ_NO_ZLIB_APIS */
+
+#include <stddef.h>
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
+/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */
+#define MINIZ_X86_OR_X64_CPU 1
+#else
+#define MINIZ_X86_OR_X64_CPU 0
+#endif
+
+#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
+/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
+#define MINIZ_LITTLE_ENDIAN 1
+#else
+#define MINIZ_LITTLE_ENDIAN 0
+#endif
+
+/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
+/* #define MINIZ_UNALIGNED_USE_MEMCPY */
+
+#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) || defined (__arch64__)
+/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */
+#define MINIZ_HAS_64BIT_REGISTERS 1
+#else
+#define MINIZ_HAS_64BIT_REGISTERS 0
+#endif
+
+#include "miniz_common.h"
+#include "miniz_tdef.h"
+#include "miniz_tinfl.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- zlib-style API Definitions. */
+
+/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */
+typedef unsigned long mz_ulong;
+
+/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */
+void mz_free(void *p);
+
+#define MZ_ADLER32_INIT (1)
+/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */
+mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
+
+#define MZ_CRC32_INIT (0)
+/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */
+mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
+
+/* Compression strategies. */
+enum
+{
+ MZ_DEFAULT_STRATEGY = 0,
+ MZ_FILTERED = 1,
+ MZ_HUFFMAN_ONLY = 2,
+ MZ_RLE = 3,
+ MZ_FIXED = 4
+};
+
+/* Method */
+#define MZ_DEFLATED 8
+
+/* Heap allocation callbacks.
+Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */
+typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
+typedef void (*mz_free_func)(void *opaque, void *address);
+typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
+
+/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */
+enum
+{
+ MZ_NO_COMPRESSION = 0,
+ MZ_BEST_SPEED = 1,
+ MZ_BEST_COMPRESSION = 9,
+ MZ_UBER_COMPRESSION = 10,
+ MZ_DEFAULT_LEVEL = 6,
+ MZ_DEFAULT_COMPRESSION = -1
+};
+
+#define MZ_VERSION "10.1.0"
+#define MZ_VERNUM 0xA100
+#define MZ_VER_MAJOR 10
+#define MZ_VER_MINOR 1
+#define MZ_VER_REVISION 0
+#define MZ_VER_SUBREVISION 0
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */
+enum
+{
+ MZ_NO_FLUSH = 0,
+ MZ_PARTIAL_FLUSH = 1,
+ MZ_SYNC_FLUSH = 2,
+ MZ_FULL_FLUSH = 3,
+ MZ_FINISH = 4,
+ MZ_BLOCK = 5
+};
+
+/* Return status codes. MZ_PARAM_ERROR is non-standard. */
+enum
+{
+ MZ_OK = 0,
+ MZ_STREAM_END = 1,
+ MZ_NEED_DICT = 2,
+ MZ_ERRNO = -1,
+ MZ_STREAM_ERROR = -2,
+ MZ_DATA_ERROR = -3,
+ MZ_MEM_ERROR = -4,
+ MZ_BUF_ERROR = -5,
+ MZ_VERSION_ERROR = -6,
+ MZ_PARAM_ERROR = -10000
+};
+
+/* Window bits */
+#define MZ_DEFAULT_WINDOW_BITS 15
+
+struct mz_internal_state;
+
+/* Compression/decompression stream struct. */
+typedef struct mz_stream_s
+{
+ const unsigned char *next_in; /* pointer to next byte to read */
+ unsigned int avail_in; /* number of bytes available at next_in */
+ mz_ulong total_in; /* total number of bytes consumed so far */
+
+ unsigned char *next_out; /* pointer to next byte to write */
+ unsigned int avail_out; /* number of bytes that can be written to next_out */
+ mz_ulong total_out; /* total number of bytes produced so far */
+
+ char *msg; /* error msg (unused) */
+ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */
+
+ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */
+ mz_free_func zfree; /* optional heap free function (defaults to free) */
+ void *opaque; /* heap alloc function user pointer */
+
+ int data_type; /* data_type (unused) */
+ mz_ulong adler; /* adler32 of the source or uncompressed data */
+ mz_ulong reserved; /* not used */
+} mz_stream;
+
+typedef mz_stream *mz_streamp;
+
+/* Returns the version string of miniz.c. */
+const char *mz_version(void);
+
+/* mz_deflateInit() initializes a compressor with default options: */
+/* Parameters: */
+/* pStream must point to an initialized mz_stream struct. */
+/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */
+/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */
+/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */
+/* Return values: */
+/* MZ_OK on success. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_PARAM_ERROR if the input parameters are bogus. */
+/* MZ_MEM_ERROR on out of memory. */
+int mz_deflateInit(mz_streamp pStream, int level);
+
+/* mz_deflateInit2() is like mz_deflate(), except with more control: */
+/* Additional parameters: */
+/* method must be MZ_DEFLATED */
+/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */
+/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */
+int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
+
+/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */
+int mz_deflateReset(mz_streamp pStream);
+
+/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */
+/* Parameters: */
+/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */
+/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */
+/* Return values: */
+/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */
+/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_PARAM_ERROR if one of the parameters is invalid. */
+/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */
+int mz_deflate(mz_streamp pStream, int flush);
+
+/* mz_deflateEnd() deinitializes a compressor: */
+/* Return values: */
+/* MZ_OK on success. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+int mz_deflateEnd(mz_streamp pStream);
+
+/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */
+mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
+
+/* Single-call compression functions mz_compress() and mz_compress2(): */
+/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */
+int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
+
+/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */
+mz_ulong mz_compressBound(mz_ulong source_len);
+
+/* Initializes a decompressor. */
+int mz_inflateInit(mz_streamp pStream);
+
+/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */
+/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */
+int mz_inflateInit2(mz_streamp pStream, int window_bits);
+
+/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */
+int mz_inflateReset(mz_streamp pStream);
+
+/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */
+/* Parameters: */
+/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */
+/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */
+/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */
+/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */
+/* Return values: */
+/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */
+/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_DATA_ERROR if the deflate stream is invalid. */
+/* MZ_PARAM_ERROR if one of the parameters is invalid. */
+/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */
+/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */
+int mz_inflate(mz_streamp pStream, int flush);
+
+/* Deinitializes a decompressor. */
+int mz_inflateEnd(mz_streamp pStream);
+
+/* Single-call decompression. */
+/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */
+int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+
+/* Returns a string description of the specified error code, or NULL if the error code is invalid. */
+const char *mz_error(int err);
+
+#endif /* MINIZ_NO_ZLIB_APIS */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/miniz/miniz_common.h b/platform/linux-generic/miniz/miniz_common.h
new file mode 100644
index 000000000..0945775c8
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz_common.h
@@ -0,0 +1,68 @@
+#pragma once
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* ------------------- Types and macros */
+typedef unsigned char mz_uint8;
+typedef signed short mz_int16;
+typedef unsigned short mz_uint16;
+typedef unsigned int mz_uint32;
+typedef unsigned int mz_uint;
+typedef int64_t mz_int64;
+typedef uint64_t mz_uint64;
+typedef int mz_bool;
+
+#define MZ_FALSE (0)
+#define MZ_TRUE (1)
+
+/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */
+#ifdef _MSC_VER
+#define MZ_MACRO_END while (0, 0)
+#else
+#define MZ_MACRO_END while (0)
+#endif
+
+#define MZ_ASSERT(x) assert(x)
+
+#define MZ_MALLOC(x) NULL
+#define MZ_FREE(x) (void)x, ((void)0)
+#define MZ_REALLOC(p, x) NULL
+
+#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
+#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
+#else
+#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
+#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
+#endif
+
+#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U))
+
+#ifdef _MSC_VER
+#define MZ_FORCEINLINE __forceinline
+#elif defined(__GNUC__)
+#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__))
+#else
+#define MZ_FORCEINLINE inline
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size);
+extern void miniz_def_free_func(void *opaque, void *address);
+extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size);
+
+#define MZ_UINT16_MAX (0xFFFFU)
+#define MZ_UINT32_MAX (0xFFFFFFFFU)
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/miniz/miniz_tdef.c b/platform/linux-generic/miniz/miniz_tdef.c
new file mode 100644
index 000000000..477a1c5df
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz_tdef.c
@@ -0,0 +1,1564 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "miniz_tdef.h"
+#include "miniz.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- Low-level Compression (independent from all decompression API's) */
+
+/* Purposely making these tables static for faster init and thread safety. */
+static const mz_uint16 s_tdefl_len_sym[256] =
+ {
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272,
+ 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276,
+ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
+ 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,
+ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285
+ };
+
+static const mz_uint8 s_tdefl_len_extra[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0
+ };
+
+static const mz_uint8 s_tdefl_small_dist_sym[512] =
+ {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
+ };
+
+static const mz_uint8 s_tdefl_small_dist_extra[512] =
+ {
+ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7
+ };
+
+static const mz_uint8 s_tdefl_large_dist_sym[128] =
+ {
+ 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+ };
+
+static const mz_uint8 s_tdefl_large_dist_extra[128] =
+ {
+ 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13
+ };
+
+/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */
+typedef struct
+{
+ mz_uint16 m_key, m_sym_index;
+} tdefl_sym_freq;
+static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1)
+{
+ mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2];
+ tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1;
+ MZ_CLEAR_OBJ(hist);
+ for (i = 0; i < num_syms; i++)
+ {
+ mz_uint freq = pSyms0[i].m_key;
+ hist[freq & 0xFF]++;
+ hist[256 + ((freq >> 8) & 0xFF)]++;
+ }
+ while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256]))
+ total_passes--;
+ for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
+ {
+ const mz_uint32 *pHist = &hist[pass << 8];
+ mz_uint offsets[256], cur_ofs = 0;
+ for (i = 0; i < 256; i++)
+ {
+ offsets[i] = cur_ofs;
+ cur_ofs += pHist[i];
+ }
+ for (i = 0; i < num_syms; i++)
+ pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
+ {
+ tdefl_sym_freq *t = pCur_syms;
+ pCur_syms = pNew_syms;
+ pNew_syms = t;
+ }
+ }
+ return pCur_syms;
+}
+
+/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */
+static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
+{
+ int root, leaf, next, avbl, used, dpth;
+ if (n == 0)
+ return;
+ else if (n == 1)
+ {
+ A[0].m_key = 1;
+ return;
+ }
+ A[0].m_key += A[1].m_key;
+ root = 0;
+ leaf = 2;
+ for (next = 1; next < n - 1; next++)
+ {
+ if (leaf >= n || A[root].m_key < A[leaf].m_key)
+ {
+ A[next].m_key = A[root].m_key;
+ A[root++].m_key = (mz_uint16)next;
+ }
+ else
+ A[next].m_key = A[leaf++].m_key;
+ if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key))
+ {
+ A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key);
+ A[root++].m_key = (mz_uint16)next;
+ }
+ else
+ A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
+ }
+ A[n - 2].m_key = 0;
+ for (next = n - 3; next >= 0; next--)
+ A[next].m_key = A[A[next].m_key].m_key + 1;
+ avbl = 1;
+ used = dpth = 0;
+ root = n - 2;
+ next = n - 1;
+ while (avbl > 0)
+ {
+ while (root >= 0 && (int)A[root].m_key == dpth)
+ {
+ used++;
+ root--;
+ }
+ while (avbl > used)
+ {
+ A[next--].m_key = (mz_uint16)(dpth);
+ avbl--;
+ }
+ avbl = 2 * used;
+ dpth++;
+ used = 0;
+ }
+}
+
+/* Limits canonical Huffman code table's max code size. */
+enum
+{
+ TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32
+};
+static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
+{
+ int i;
+ mz_uint32 total = 0;
+ if (code_list_len <= 1)
+ return;
+ for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++)
+ pNum_codes[max_code_size] += pNum_codes[i];
+ for (i = max_code_size; i > 0; i--)
+ total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
+ while (total != (1UL << max_code_size))
+ {
+ pNum_codes[max_code_size]--;
+ for (i = max_code_size - 1; i > 0; i--)
+ if (pNum_codes[i])
+ {
+ pNum_codes[i]--;
+ pNum_codes[i + 1] += 2;
+ break;
+ }
+ total--;
+ }
+}
+
+static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
+{
+ int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE];
+ mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1];
+ MZ_CLEAR_OBJ(num_codes);
+ if (static_table)
+ {
+ for (i = 0; i < table_len; i++)
+ num_codes[d->m_huff_code_sizes[table_num][i]]++;
+ }
+ else
+ {
+ tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
+ int num_used_syms = 0;
+ const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
+ for (i = 0; i < table_len; i++)
+ if (pSym_count[i])
+ {
+ syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i];
+ syms0[num_used_syms++].m_sym_index = (mz_uint16)i;
+ }
+
+ pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1);
+ tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
+
+ for (i = 0; i < num_used_syms; i++)
+ num_codes[pSyms[i].m_key]++;
+
+ tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
+
+ MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]);
+ MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
+ for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
+ for (l = num_codes[i]; l > 0; l--)
+ d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
+ }
+
+ next_code[1] = 0;
+ for (j = 0, i = 2; i <= code_size_limit; i++)
+ next_code[i] = j = ((j + num_codes[i - 1]) << 1);
+
+ for (i = 0; i < table_len; i++)
+ {
+ mz_uint rev_code = 0, code, code_size;
+ if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0)
+ continue;
+ code = next_code[code_size]++;
+ for (l = code_size; l > 0; l--, code >>= 1)
+ rev_code = (rev_code << 1) | (code & 1);
+ d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
+ }
+}
+
+#define TDEFL_PUT_BITS(b, l) \
+ do \
+ { \
+ mz_uint bits = b; \
+ mz_uint len = l; \
+ MZ_ASSERT(bits <= ((1U << len) - 1U)); \
+ d->m_bit_buffer |= (bits << d->m_bits_in); \
+ d->m_bits_in += len; \
+ while (d->m_bits_in >= 8) \
+ { \
+ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
+ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
+ d->m_bit_buffer >>= 8; \
+ d->m_bits_in -= 8; \
+ } \
+ } \
+ MZ_MACRO_END
+
+#define TDEFL_RLE_PREV_CODE_SIZE() \
+ { \
+ if (rle_repeat_count) \
+ { \
+ if (rle_repeat_count < 3) \
+ { \
+ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
+ while (rle_repeat_count--) \
+ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
+ } \
+ else \
+ { \
+ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 16; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
+ } \
+ rle_repeat_count = 0; \
+ } \
+ }
+
+#define TDEFL_RLE_ZERO_CODE_SIZE() \
+ { \
+ if (rle_z_count) \
+ { \
+ if (rle_z_count < 3) \
+ { \
+ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \
+ while (rle_z_count--) \
+ packed_code_sizes[num_packed_code_sizes++] = 0; \
+ } \
+ else if (rle_z_count <= 10) \
+ { \
+ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 17; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
+ } \
+ else \
+ { \
+ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 18; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
+ } \
+ rle_z_count = 0; \
+ } \
+ }
+
+static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+static void tdefl_start_dynamic_block(tdefl_compressor *d)
+{
+ int num_lit_codes, num_dist_codes, num_bit_lengths;
+ mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
+ mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
+
+ d->m_huff_count[0][256] = 1;
+
+ tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
+ tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
+
+ for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--)
+ if (d->m_huff_code_sizes[0][num_lit_codes - 1])
+ break;
+ for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--)
+ if (d->m_huff_code_sizes[1][num_dist_codes - 1])
+ break;
+
+ memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
+ memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
+ total_code_sizes_to_pack = num_lit_codes + num_dist_codes;
+ num_packed_code_sizes = 0;
+ rle_z_count = 0;
+ rle_repeat_count = 0;
+
+ memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
+ for (i = 0; i < total_code_sizes_to_pack; i++)
+ {
+ mz_uint8 code_size = code_sizes_to_pack[i];
+ if (!code_size)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ if (++rle_z_count == 138)
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ }
+ }
+ else
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ if (code_size != prev_code_size)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1);
+ packed_code_sizes[num_packed_code_sizes++] = code_size;
+ }
+ else if (++rle_repeat_count == 6)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ }
+ }
+ prev_code_size = code_size;
+ }
+ if (rle_repeat_count)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ }
+ else
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ }
+
+ tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
+
+ TDEFL_PUT_BITS(2, 2);
+
+ TDEFL_PUT_BITS(num_lit_codes - 257, 5);
+ TDEFL_PUT_BITS(num_dist_codes - 1, 5);
+
+ for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--)
+ if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]])
+ break;
+ num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1));
+ TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
+ for (i = 0; (int)i < num_bit_lengths; i++)
+ TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
+
+ for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;)
+ {
+ mz_uint code = packed_code_sizes[packed_code_sizes_index++];
+ MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
+ TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
+ if (code >= 16)
+ TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
+ }
+}
+
+static void tdefl_start_static_block(tdefl_compressor *d)
+{
+ mz_uint i;
+ mz_uint8 *p = &d->m_huff_code_sizes[0][0];
+
+ for (i = 0; i <= 143; ++i)
+ *p++ = 8;
+ for (; i <= 255; ++i)
+ *p++ = 9;
+ for (; i <= 279; ++i)
+ *p++ = 7;
+ for (; i <= 287; ++i)
+ *p++ = 8;
+
+ memset(d->m_huff_code_sizes[1], 5, 32);
+
+ tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
+ tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
+
+ TDEFL_PUT_BITS(1, 2);
+}
+
+static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+ mz_uint flags;
+ mz_uint8 *pLZ_codes;
+ mz_uint8 *pOutput_buf = d->m_pOutput_buf;
+ mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
+ mz_uint64 bit_buffer = d->m_bit_buffer;
+ mz_uint bits_in = d->m_bits_in;
+
+#define TDEFL_PUT_BITS_FAST(b, l) \
+ { \
+ bit_buffer |= (((mz_uint64)(b)) << bits_in); \
+ bits_in += (l); \
+ }
+
+ flags = 1;
+ for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
+ {
+ if (flags == 1)
+ flags = *pLZ_codes++ | 0x100;
+
+ if (flags & 1)
+ {
+ mz_uint s0, s1, n0, n1, sym, num_extra_bits;
+ mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1);
+ pLZ_codes += 3;
+
+ MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+ /* This sequence coaxes MSVC into using cmov's vs. jmp's. */
+ s0 = s_tdefl_small_dist_sym[match_dist & 511];
+ n0 = s_tdefl_small_dist_extra[match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[match_dist >> 8];
+ n1 = s_tdefl_large_dist_extra[match_dist >> 8];
+ sym = (match_dist < 512) ? s0 : s1;
+ num_extra_bits = (match_dist < 512) ? n0 : n1;
+
+ MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+ }
+ else
+ {
+ mz_uint lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+ if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+ {
+ flags >>= 1;
+ lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+ if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+ {
+ flags >>= 1;
+ lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+ }
+ }
+ }
+
+ if (pOutput_buf >= d->m_pOutput_buf_end)
+ return MZ_FALSE;
+
+ *(mz_uint64 *)pOutput_buf = bit_buffer;
+ pOutput_buf += (bits_in >> 3);
+ bit_buffer >>= (bits_in & ~7);
+ bits_in &= 7;
+ }
+
+#undef TDEFL_PUT_BITS_FAST
+
+ d->m_pOutput_buf = pOutput_buf;
+ d->m_bits_in = 0;
+ d->m_bit_buffer = 0;
+
+ while (bits_in)
+ {
+ mz_uint32 n = MZ_MIN(bits_in, 16);
+ TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
+ bit_buffer >>= n;
+ bits_in -= n;
+ }
+
+ TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+ return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#else
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+ mz_uint flags;
+ mz_uint8 *pLZ_codes;
+
+ flags = 1;
+ for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
+ {
+ if (flags == 1)
+ flags = *pLZ_codes++ | 0x100;
+ if (flags & 1)
+ {
+ mz_uint sym, num_extra_bits;
+ mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));
+ pLZ_codes += 3;
+
+ MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+ if (match_dist < 512)
+ {
+ sym = s_tdefl_small_dist_sym[match_dist];
+ num_extra_bits = s_tdefl_small_dist_extra[match_dist];
+ }
+ else
+ {
+ sym = s_tdefl_large_dist_sym[match_dist >> 8];
+ num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
+ }
+ MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+ }
+ else
+ {
+ mz_uint lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+ }
+ }
+
+ TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+ return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */
+
+static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
+{
+ if (static_block)
+ tdefl_start_static_block(d);
+ else
+ tdefl_start_dynamic_block(d);
+ return tdefl_compress_lz_codes(d);
+}
+
+static int tdefl_flush_block(tdefl_compressor *d, int flush)
+{
+ mz_uint saved_bit_buf, saved_bits_in;
+ mz_uint8 *pSaved_output_buf;
+ mz_bool comp_block_succeeded = MZ_FALSE;
+ int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
+ mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
+
+ d->m_pOutput_buf = pOutput_buf_start;
+ d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
+
+ MZ_ASSERT(!d->m_output_flush_remaining);
+ d->m_output_flush_ofs = 0;
+ d->m_output_flush_remaining = 0;
+
+ *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
+ d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
+
+ if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
+ {
+ TDEFL_PUT_BITS(0x78, 8);
+ TDEFL_PUT_BITS(0x01, 8);
+ }
+
+ TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
+
+ pSaved_output_buf = d->m_pOutput_buf;
+ saved_bit_buf = d->m_bit_buffer;
+ saved_bits_in = d->m_bits_in;
+
+ if (!use_raw_block)
+ comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
+
+ /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */
+ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
+ ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size))
+ {
+ mz_uint i;
+ d->m_pOutput_buf = pSaved_output_buf;
+ d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+ TDEFL_PUT_BITS(0, 2);
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
+ {
+ TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
+ }
+ for (i = 0; i < d->m_total_lz_bytes; ++i)
+ {
+ TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
+ }
+ }
+ /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */
+ else if (!comp_block_succeeded)
+ {
+ d->m_pOutput_buf = pSaved_output_buf;
+ d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+ tdefl_compress_block(d, MZ_TRUE);
+ }
+
+ if (flush)
+ {
+ if (flush == TDEFL_FINISH)
+ {
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER)
+ {
+ mz_uint i, a = d->m_adler32;
+ for (i = 0; i < 4; i++)
+ {
+ TDEFL_PUT_BITS((a >> 24) & 0xFF, 8);
+ a <<= 8;
+ }
+ }
+ }
+ else
+ {
+ mz_uint i, z = 0;
+ TDEFL_PUT_BITS(0, 3);
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ for (i = 2; i; --i, z ^= 0xFFFF)
+ {
+ TDEFL_PUT_BITS(z & 0xFFFF, 16);
+ }
+ }
+ }
+
+ MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
+
+ memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+ memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+
+ d->m_pLZ_code_buf = d->m_lz_code_buf + 1;
+ d->m_pLZ_flags = d->m_lz_code_buf;
+ d->m_num_flags_left = 8;
+ d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes;
+ d->m_total_lz_bytes = 0;
+ d->m_block_index++;
+
+ if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
+ {
+ if (d->m_pPut_buf_func)
+ {
+ *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+ if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
+ return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
+ }
+ else if (pOutput_buf_start == d->m_output_buf)
+ {
+ int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
+ memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
+ d->m_out_buf_ofs += bytes_to_copy;
+ if ((n -= bytes_to_copy) != 0)
+ {
+ d->m_output_flush_ofs = bytes_to_copy;
+ d->m_output_flush_remaining = n;
+ }
+ }
+ else
+ {
+ d->m_out_buf_ofs += n;
+ }
+ }
+
+ return d->m_output_flush_remaining;
+}
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p)
+{
+ mz_uint16 ret;
+ memcpy(&ret, p, sizeof(mz_uint16));
+ return ret;
+}
+static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p)
+{
+ mz_uint16 ret;
+ memcpy(&ret, p, sizeof(mz_uint16));
+ return ret;
+}
+#else
+#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p)
+#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p)
+#endif
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+ mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+ mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+ const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q;
+ mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s);
+ MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);
+ if (max_match_len <= match_len)
+ return;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (--num_probes_left == 0)
+ return;
+#define TDEFL_PROBE \
+ next_probe_pos = d->m_next[probe_pos]; \
+ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \
+ return; \
+ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \
+ break;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ }
+ if (!dist)
+ break;
+ q = (const mz_uint16 *)(d->m_dict + probe_pos);
+ if (TDEFL_READ_UNALIGNED_WORD2(q) != s01)
+ continue;
+ p = s;
+ probe_len = 32;
+ do
+ {
+ } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&
+ (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));
+ if (!probe_len)
+ {
+ *pMatch_dist = dist;
+ *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN);
+ break;
+ }
+ else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len)
+ {
+ *pMatch_dist = dist;
+ if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len)
+ break;
+ c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
+ }
+ }
+}
+#else
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+ mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+ mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+ const mz_uint8 *s = d->m_dict + pos, *p, *q;
+ mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
+ MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);
+ if (max_match_len <= match_len)
+ return;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (--num_probes_left == 0)
+ return;
+#define TDEFL_PROBE \
+ next_probe_pos = d->m_next[probe_pos]; \
+ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \
+ return; \
+ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \
+ break;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ }
+ if (!dist)
+ break;
+ p = s;
+ q = d->m_dict + probe_pos;
+ for (probe_len = 0; probe_len < max_match_len; probe_len++)
+ if (*p++ != *q++)
+ break;
+ if (probe_len > match_len)
+ {
+ *pMatch_dist = dist;
+ if ((*pMatch_len = match_len = probe_len) == max_match_len)
+ return;
+ c0 = d->m_dict[pos + match_len];
+ c1 = d->m_dict[pos + match_len - 1];
+ }
+ }
+}
+#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p)
+{
+ mz_uint32 ret;
+ memcpy(&ret, p, sizeof(mz_uint32));
+ return ret;
+}
+#else
+#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p)
+#endif
+static mz_bool tdefl_compress_fast(tdefl_compressor *d)
+{
+ /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */
+ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
+ mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
+ mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+
+ while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
+ {
+ const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
+ mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+ mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
+ d->m_src_buf_left -= num_bytes_to_process;
+ lookahead_size += num_bytes_to_process;
+
+ while (num_bytes_to_process)
+ {
+ mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
+ memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
+ d->m_pSrc += n;
+ dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
+ num_bytes_to_process -= n;
+ }
+
+ dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
+ if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE))
+ break;
+
+ while (lookahead_size >= 4)
+ {
+ mz_uint cur_match_dist, cur_match_len = 1;
+ mz_uint8 *pCur_dict = d->m_dict + cur_pos;
+ mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF;
+ mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
+ mz_uint probe_pos = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)lookahead_pos;
+
+ if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
+ {
+ const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
+ const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
+ mz_uint32 probe_len = 32;
+ do
+ {
+ } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&
+ (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));
+ cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
+ if (!probe_len)
+ cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
+
+ if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)))
+ {
+ cur_match_len = 1;
+ *pLZ_code_buf++ = (mz_uint8)first_trigram;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ d->m_huff_count[0][(mz_uint8)first_trigram]++;
+ }
+ else
+ {
+ mz_uint32 s0, s1;
+ cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
+
+ MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
+
+ cur_match_dist--;
+
+ pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+ memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist));
+#else
+ *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
+#endif
+ pLZ_code_buf += 3;
+ *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
+
+ s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
+ d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
+
+ d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
+ }
+ }
+ else
+ {
+ *pLZ_code_buf++ = (mz_uint8)first_trigram;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ d->m_huff_count[0][(mz_uint8)first_trigram]++;
+ }
+
+ if (--num_flags_left == 0)
+ {
+ num_flags_left = 8;
+ pLZ_flags = pLZ_code_buf++;
+ }
+
+ total_lz_bytes += cur_match_len;
+ lookahead_pos += cur_match_len;
+ dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
+ MZ_ASSERT(lookahead_size >= cur_match_len);
+ lookahead_size -= cur_match_len;
+
+ if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+ {
+ int n;
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ total_lz_bytes = d->m_total_lz_bytes;
+ pLZ_code_buf = d->m_pLZ_code_buf;
+ pLZ_flags = d->m_pLZ_flags;
+ num_flags_left = d->m_num_flags_left;
+ }
+ }
+
+ while (lookahead_size)
+ {
+ mz_uint8 lit = d->m_dict[cur_pos];
+
+ total_lz_bytes++;
+ *pLZ_code_buf++ = lit;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ if (--num_flags_left == 0)
+ {
+ num_flags_left = 8;
+ pLZ_flags = pLZ_code_buf++;
+ }
+
+ d->m_huff_count[0][lit]++;
+
+ lookahead_pos++;
+ dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
+ lookahead_size--;
+
+ if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+ {
+ int n;
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ total_lz_bytes = d->m_total_lz_bytes;
+ pLZ_code_buf = d->m_pLZ_code_buf;
+ pLZ_flags = d->m_pLZ_flags;
+ num_flags_left = d->m_num_flags_left;
+ }
+ }
+ }
+
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ return MZ_TRUE;
+}
+#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */
+
+static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
+{
+ d->m_total_lz_bytes++;
+ *d->m_pLZ_code_buf++ = lit;
+ *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1);
+ if (--d->m_num_flags_left == 0)
+ {
+ d->m_num_flags_left = 8;
+ d->m_pLZ_flags = d->m_pLZ_code_buf++;
+ }
+ d->m_huff_count[0][lit]++;
+}
+
+static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
+{
+ mz_uint32 s0, s1;
+
+ MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
+
+ d->m_total_lz_bytes += match_len;
+
+ d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
+
+ match_dist -= 1;
+ d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
+ d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8);
+ d->m_pLZ_code_buf += 3;
+
+ *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80);
+ if (--d->m_num_flags_left == 0)
+ {
+ d->m_num_flags_left = 8;
+ d->m_pLZ_flags = d->m_pLZ_code_buf++;
+ }
+
+ s0 = s_tdefl_small_dist_sym[match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
+ d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
+
+ if (match_len >= TDEFL_MIN_MATCH_LEN)
+ d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
+}
+
+static mz_bool tdefl_compress_normal(tdefl_compressor *d)
+{
+ const mz_uint8 *pSrc = d->m_pSrc;
+ size_t src_buf_left = d->m_src_buf_left;
+ tdefl_flush flush = d->m_flush;
+
+ while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
+ {
+ mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
+ /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */
+ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
+ {
+ mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
+ mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
+ mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
+ const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
+ src_buf_left -= num_bytes_to_process;
+ d->m_lookahead_size += num_bytes_to_process;
+ while (pSrc != pSrc_end)
+ {
+ mz_uint8 c = *pSrc++;
+ d->m_dict[dst_pos] = c;
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+ hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+ d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)(ins_pos);
+ dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
+ ins_pos++;
+ }
+ }
+ else
+ {
+ while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+ {
+ mz_uint8 c = *pSrc++;
+ mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+ src_buf_left--;
+ d->m_dict[dst_pos] = c;
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+ if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
+ {
+ mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
+ mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+ d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)(ins_pos);
+ }
+ }
+ }
+ d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
+ if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+ break;
+
+ /* Simple lazy/greedy parsing state machine. */
+ len_to_move = 1;
+ cur_match_dist = 0;
+ cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1);
+ cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+ if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
+ {
+ if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
+ {
+ mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
+ cur_match_len = 0;
+ while (cur_match_len < d->m_lookahead_size)
+ {
+ if (d->m_dict[cur_pos + cur_match_len] != c)
+ break;
+ cur_match_len++;
+ }
+ if (cur_match_len < TDEFL_MIN_MATCH_LEN)
+ cur_match_len = 0;
+ else
+ cur_match_dist = 1;
+ }
+ }
+ else
+ {
+ tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
+ }
+ if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
+ {
+ cur_match_dist = cur_match_len = 0;
+ }
+ if (d->m_saved_match_len)
+ {
+ if (cur_match_len > d->m_saved_match_len)
+ {
+ tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
+ if (cur_match_len >= 128)
+ {
+ tdefl_record_match(d, cur_match_len, cur_match_dist);
+ d->m_saved_match_len = 0;
+ len_to_move = cur_match_len;
+ }
+ else
+ {
+ d->m_saved_lit = d->m_dict[cur_pos];
+ d->m_saved_match_dist = cur_match_dist;
+ d->m_saved_match_len = cur_match_len;
+ }
+ }
+ else
+ {
+ tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
+ len_to_move = d->m_saved_match_len - 1;
+ d->m_saved_match_len = 0;
+ }
+ }
+ else if (!cur_match_dist)
+ tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
+ else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
+ {
+ tdefl_record_match(d, cur_match_len, cur_match_dist);
+ len_to_move = cur_match_len;
+ }
+ else
+ {
+ d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)];
+ d->m_saved_match_dist = cur_match_dist;
+ d->m_saved_match_len = cur_match_len;
+ }
+ /* Move the lookahead forward by len_to_move bytes. */
+ d->m_lookahead_pos += len_to_move;
+ MZ_ASSERT(d->m_lookahead_size >= len_to_move);
+ d->m_lookahead_size -= len_to_move;
+ d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ /* Check if it's time to flush the current LZ codes to the internal output buffer. */
+ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
+ ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))))
+ {
+ int n;
+ d->m_pSrc = pSrc;
+ d->m_src_buf_left = src_buf_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ }
+ }
+
+ d->m_pSrc = pSrc;
+ d->m_src_buf_left = src_buf_left;
+ return MZ_TRUE;
+}
+
+static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
+{
+ if (d->m_pIn_buf_size)
+ {
+ *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+ }
+
+ if (d->m_pOut_buf_size)
+ {
+ size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
+ memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
+ d->m_output_flush_ofs += (mz_uint)n;
+ d->m_output_flush_remaining -= (mz_uint)n;
+ d->m_out_buf_ofs += n;
+
+ *d->m_pOut_buf_size = d->m_out_buf_ofs;
+ }
+
+ return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
+{
+ if (!d)
+ {
+ if (pIn_buf_size)
+ *pIn_buf_size = 0;
+ if (pOut_buf_size)
+ *pOut_buf_size = 0;
+ return TDEFL_STATUS_BAD_PARAM;
+ }
+
+ d->m_pIn_buf = pIn_buf;
+ d->m_pIn_buf_size = pIn_buf_size;
+ d->m_pOut_buf = pOut_buf;
+ d->m_pOut_buf_size = pOut_buf_size;
+ d->m_pSrc = (const mz_uint8 *)(pIn_buf);
+ d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
+ d->m_out_buf_ofs = 0;
+ d->m_flush = flush;
+
+ if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
+ (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf))
+ {
+ if (pIn_buf_size)
+ *pIn_buf_size = 0;
+ if (pOut_buf_size)
+ *pOut_buf_size = 0;
+ return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
+ }
+ d->m_wants_to_finish |= (flush == TDEFL_FINISH);
+
+ if ((d->m_output_flush_remaining) || (d->m_finished))
+ return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+ if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
+ ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
+ ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
+ {
+ if (!tdefl_compress_fast(d))
+ return d->m_prev_return_status;
+ }
+ else
+#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */
+ {
+ if (!tdefl_compress_normal(d))
+ return d->m_prev_return_status;
+ }
+
+ if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
+ d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
+
+ if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
+ {
+ if (tdefl_flush_block(d, flush) < 0)
+ return d->m_prev_return_status;
+ d->m_finished = (flush == TDEFL_FINISH);
+ if (flush == TDEFL_FULL_FLUSH)
+ {
+ MZ_CLEAR_OBJ(d->m_hash);
+ MZ_CLEAR_OBJ(d->m_next);
+ d->m_dict_size = 0;
+ }
+ }
+
+ return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+}
+
+tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
+{
+ MZ_ASSERT(d->m_pPut_buf_func);
+ return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
+}
+
+tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ d->m_pPut_buf_func = pPut_buf_func;
+ d->m_pPut_buf_user = pPut_buf_user;
+ d->m_flags = (mz_uint)(flags);
+ d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3;
+ d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
+ d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
+ if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
+ MZ_CLEAR_OBJ(d->m_hash);
+ d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
+ d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
+ d->m_pLZ_code_buf = d->m_lz_code_buf + 1;
+ d->m_pLZ_flags = d->m_lz_code_buf;
+ d->m_num_flags_left = 8;
+ d->m_pOutput_buf = d->m_output_buf;
+ d->m_pOutput_buf_end = d->m_output_buf;
+ d->m_prev_return_status = TDEFL_STATUS_OKAY;
+ d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0;
+ d->m_adler32 = 1;
+ d->m_pIn_buf = NULL;
+ d->m_pOut_buf = NULL;
+ d->m_pIn_buf_size = NULL;
+ d->m_pOut_buf_size = NULL;
+ d->m_flush = TDEFL_NO_FLUSH;
+ d->m_pSrc = NULL;
+ d->m_src_buf_left = 0;
+ d->m_out_buf_ofs = 0;
+ if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
+ MZ_CLEAR_OBJ(d->m_dict);
+ memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+ memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+ return TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
+{
+ return d->m_prev_return_status;
+}
+
+mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
+{
+ return d->m_adler32;
+}
+
+mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ tdefl_compressor *pComp;
+ mz_bool succeeded;
+ if (((buf_len) && (!pBuf)) || (!pPut_buf_func))
+ return MZ_FALSE;
+ pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
+ if (!pComp)
+ return MZ_FALSE;
+ succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
+ succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
+ MZ_FREE(pComp);
+ return succeeded;
+}
+
+typedef struct
+{
+ size_t m_size, m_capacity;
+ mz_uint8 *m_pBuf;
+ mz_bool m_expandable;
+} tdefl_output_buffer;
+
+static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
+{
+ tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
+ size_t new_size = p->m_size + len;
+ if (new_size > p->m_capacity)
+ {
+ size_t new_capacity = p->m_capacity;
+ mz_uint8 *pNew_buf;
+ if (!p->m_expandable)
+ return MZ_FALSE;
+ do
+ {
+ new_capacity = MZ_MAX(128U, new_capacity << 1U);
+ } while (new_size > new_capacity);
+ pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity);
+ if (!pNew_buf)
+ return MZ_FALSE;
+ p->m_pBuf = pNew_buf;
+ p->m_capacity = new_capacity;
+ }
+ memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len);
+ p->m_size = new_size;
+ return MZ_TRUE;
+}
+
+void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+ tdefl_output_buffer out_buf;
+ MZ_CLEAR_OBJ(out_buf);
+ if (!pOut_len)
+ return MZ_FALSE;
+ else
+ *pOut_len = 0;
+ out_buf.m_expandable = MZ_TRUE;
+ if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))
+ return NULL;
+ *pOut_len = out_buf.m_size;
+ return out_buf.m_pBuf;
+}
+
+size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+ tdefl_output_buffer out_buf;
+ MZ_CLEAR_OBJ(out_buf);
+ if (!pOut_buf)
+ return 0;
+ out_buf.m_pBuf = (mz_uint8 *)pOut_buf;
+ out_buf.m_capacity = out_buf_len;
+ if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))
+ return 0;
+ return out_buf.m_size;
+}
+
+static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
+
+/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */
+mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
+{
+ mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+ if (window_bits > 0)
+ comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
+
+ if (!level)
+ comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+ else if (strategy == MZ_FILTERED)
+ comp_flags |= TDEFL_FILTER_MATCHES;
+ else if (strategy == MZ_HUFFMAN_ONLY)
+ comp_flags &= ~TDEFL_MAX_PROBES_MASK;
+ else if (strategy == MZ_FIXED)
+ comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
+ else if (strategy == MZ_RLE)
+ comp_flags |= TDEFL_RLE_MATCHES;
+
+ return comp_flags;
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */
+#endif
+
+/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
+ http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
+ This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */
+void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
+{
+ /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */
+ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
+ tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
+ tdefl_output_buffer out_buf;
+ int i, bpl = w * num_chans, y, z;
+ mz_uint32 c;
+ *pLen_out = 0;
+ if (!pComp)
+ return NULL;
+ MZ_CLEAR_OBJ(out_buf);
+ out_buf.m_expandable = MZ_TRUE;
+ out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h);
+ if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity)))
+ {
+ MZ_FREE(pComp);
+ return NULL;
+ }
+ /* write dummy header */
+ for (z = 41; z; --z)
+ tdefl_output_buffer_putter(&z, 1, &out_buf);
+ /* compress image data */
+ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
+ for (y = 0; y < h; ++y)
+ {
+ tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH);
+ tdefl_compress_buffer(pComp, (const mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH);
+ }
+ if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE)
+ {
+ MZ_FREE(pComp);
+ MZ_FREE(out_buf.m_pBuf);
+ return NULL;
+ }
+ /* write real header */
+ *pLen_out = out_buf.m_size - 41;
+ {
+ static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 };
+ mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x1a, 0x0a, 0x00, 0x00,
+ 0x00, 0x0d, 0x49, 0x48, 0x44,
+ 0x52, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x44, 0x41,
+ 0x54 };
+ pnghdr[18] = (mz_uint8)(w >> 8);
+ pnghdr[19] = (mz_uint8)w;
+ pnghdr[22] = (mz_uint8)(h >> 8);
+ pnghdr[23] = (mz_uint8)h;
+ pnghdr[25] = chans[num_chans];
+ pnghdr[33] = (mz_uint8)(*pLen_out >> 24);
+ pnghdr[34] = (mz_uint8)(*pLen_out >> 16);
+ pnghdr[35] = (mz_uint8)(*pLen_out >> 8);
+ pnghdr[36] = (mz_uint8)*pLen_out;
+ c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17);
+ for (i = 0; i < 4; ++i, c <<= 8)
+ ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24);
+ memcpy(out_buf.m_pBuf, pnghdr, 41);
+ }
+ /* write footer (IDAT CRC-32, followed by IEND chunk) */
+ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf))
+ {
+ *pLen_out = 0;
+ MZ_FREE(pComp);
+ MZ_FREE(out_buf.m_pBuf);
+ return NULL;
+ }
+ c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4);
+ for (i = 0; i < 4; ++i, c <<= 8)
+ (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24);
+ /* compute final size of file, grab compressed data buffer and return */
+ *pLen_out += 57;
+ MZ_FREE(pComp);
+ return out_buf.m_pBuf;
+}
+void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
+{
+ /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */
+ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/miniz/miniz_tdef.h b/platform/linux-generic/miniz/miniz_tdef.h
new file mode 100644
index 000000000..25448b6fa
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz_tdef.h
@@ -0,0 +1,183 @@
+#pragma once
+#include "miniz.h"
+#include "miniz_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* ------------------- Low-level Compression API Definitions */
+
+/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */
+#define TDEFL_LESS_MEMORY 0
+
+/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */
+/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */
+enum
+{
+ TDEFL_HUFFMAN_ONLY = 0,
+ TDEFL_DEFAULT_MAX_PROBES = 128,
+ TDEFL_MAX_PROBES_MASK = 0xFFF
+};
+
+/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */
+/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */
+/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */
+/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */
+/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */
+/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */
+/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */
+/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */
+/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */
+enum
+{
+ TDEFL_WRITE_ZLIB_HEADER = 0x01000,
+ TDEFL_COMPUTE_ADLER32 = 0x02000,
+ TDEFL_GREEDY_PARSING_FLAG = 0x04000,
+ TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
+ TDEFL_RLE_MATCHES = 0x10000,
+ TDEFL_FILTER_MATCHES = 0x20000,
+ TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
+ TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
+};
+
+/* High level compression functions: */
+/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */
+/* On entry: */
+/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */
+/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */
+/* On return: */
+/* Function returns a pointer to the compressed data, or NULL on failure. */
+/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */
+/* The caller must free() the returned block when it's no longer needed. */
+void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */
+/* Returns 0 on failure. */
+size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+/* Compresses an image to a compressed PNG file in memory. */
+/* On entry: */
+/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */
+/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */
+/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */
+/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */
+/* On return: */
+/* Function returns a pointer to the compressed data, or NULL on failure. */
+/* *pLen_out will be set to the size of the PNG image file. */
+/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */
+void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
+void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
+
+/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */
+typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
+
+/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */
+mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+enum
+{
+ TDEFL_MAX_HUFF_TABLES = 3,
+ TDEFL_MAX_HUFF_SYMBOLS_0 = 288,
+ TDEFL_MAX_HUFF_SYMBOLS_1 = 32,
+ TDEFL_MAX_HUFF_SYMBOLS_2 = 19,
+ TDEFL_LZ_DICT_SIZE = 32768,
+ TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1,
+ TDEFL_MIN_MATCH_LEN = 3,
+ TDEFL_MAX_MATCH_LEN = 258
+};
+
+/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */
+#if TDEFL_LESS_MEMORY
+enum
+{
+ TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024,
+ TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,
+ TDEFL_MAX_HUFF_SYMBOLS = 288,
+ TDEFL_LZ_HASH_BITS = 12,
+ TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,
+ TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,
+ TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS
+};
+#else
+enum
+{
+ TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024,
+ TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,
+ TDEFL_MAX_HUFF_SYMBOLS = 288,
+ TDEFL_LZ_HASH_BITS = 15,
+ TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,
+ TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,
+ TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS
+};
+#endif
+
+/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */
+typedef enum {
+ TDEFL_STATUS_BAD_PARAM = -2,
+ TDEFL_STATUS_PUT_BUF_FAILED = -1,
+ TDEFL_STATUS_OKAY = 0,
+ TDEFL_STATUS_DONE = 1
+} tdefl_status;
+
+/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */
+typedef enum {
+ TDEFL_NO_FLUSH = 0,
+ TDEFL_SYNC_FLUSH = 2,
+ TDEFL_FULL_FLUSH = 3,
+ TDEFL_FINISH = 4
+} tdefl_flush;
+
+/* tdefl's compression state structure. */
+typedef struct
+{
+ tdefl_put_buf_func_ptr m_pPut_buf_func;
+ void *m_pPut_buf_user;
+ mz_uint m_flags, m_max_probes[2];
+ int m_greedy_parsing;
+ mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
+ mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
+ mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
+ mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
+ tdefl_status m_prev_return_status;
+ const void *m_pIn_buf;
+ void *m_pOut_buf;
+ size_t *m_pIn_buf_size, *m_pOut_buf_size;
+ tdefl_flush m_flush;
+ const mz_uint8 *m_pSrc;
+ size_t m_src_buf_left, m_out_buf_ofs;
+ mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
+ mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
+ mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
+ mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
+ mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
+} tdefl_compressor;
+
+/* Initializes the compressor. */
+/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */
+/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */
+/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */
+/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */
+tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */
+tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
+
+/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */
+/* tdefl_compress_buffer() always consumes the entire input buffer. */
+tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
+
+tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
+mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
+
+/* Create tdefl_compress() flags given zlib-style compression parameters. */
+/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */
+/* window_bits may be -15 (raw deflate) or 15 (zlib) */
+/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */
+mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/miniz/miniz_tinfl.c b/platform/linux-generic/miniz/miniz_tinfl.c
new file mode 100644
index 000000000..3dfa1d550
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz_tinfl.c
@@ -0,0 +1,725 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "miniz_tinfl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- Low-level Decompression (completely independent from all compression API's) */
+
+#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
+#define TINFL_MEMSET(p, c, l) memset(p, c, l)
+
+#define TINFL_CR_BEGIN \
+ switch (r->m_state) \
+ { \
+ case 0:
+#define TINFL_CR_RETURN(state_index, result) \
+ do \
+ { \
+ status = result; \
+ r->m_state = state_index; \
+ goto common_exit; \
+ case state_index:; \
+ } \
+ MZ_MACRO_END
+#define TINFL_CR_RETURN_FOREVER(state_index, result) \
+ do \
+ { \
+ for (;;) \
+ { \
+ TINFL_CR_RETURN(state_index, result); \
+ } \
+ } \
+ MZ_MACRO_END
+#define TINFL_CR_FINISH }
+
+#define TINFL_GET_BYTE(state_index, c) \
+ do \
+ { \
+ while (pIn_buf_cur >= pIn_buf_end) \
+ { \
+ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \
+ } \
+ c = *pIn_buf_cur++; \
+ } \
+ MZ_MACRO_END
+
+#define TINFL_NEED_BITS(state_index, n) \
+ do \
+ { \
+ mz_uint c; \
+ TINFL_GET_BYTE(state_index, c); \
+ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
+ num_bits += 8; \
+ } while (num_bits < (mz_uint)(n))
+#define TINFL_SKIP_BITS(state_index, n) \
+ do \
+ { \
+ if (num_bits < (mz_uint)(n)) \
+ { \
+ TINFL_NEED_BITS(state_index, n); \
+ } \
+ bit_buf >>= (n); \
+ num_bits -= (n); \
+ } \
+ MZ_MACRO_END
+#define TINFL_GET_BITS(state_index, b, n) \
+ do \
+ { \
+ if (num_bits < (mz_uint)(n)) \
+ { \
+ TINFL_NEED_BITS(state_index, n); \
+ } \
+ b = bit_buf & ((1 << (n)) - 1); \
+ bit_buf >>= (n); \
+ num_bits -= (n); \
+ } \
+ MZ_MACRO_END
+
+/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
+/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
+/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
+/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
+#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
+ do \
+ { \
+ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
+ if (temp >= 0) \
+ { \
+ code_len = temp >> 9; \
+ if ((code_len) && (num_bits >= code_len)) \
+ break; \
+ } \
+ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \
+ { \
+ code_len = TINFL_FAST_LOOKUP_BITS; \
+ do \
+ { \
+ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
+ } while ((temp < 0) && (num_bits >= (code_len + 1))); \
+ if (temp >= 0) \
+ break; \
+ } \
+ TINFL_GET_BYTE(state_index, c); \
+ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
+ num_bits += 8; \
+ } while (num_bits < 15);
+
+/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
+/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
+/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
+/* The slow path is only executed at the very end of the input buffer. */
+/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */
+/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */
+#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \
+ do \
+ { \
+ int temp; \
+ mz_uint code_len, c; \
+ if (num_bits < 15) \
+ { \
+ if ((pIn_buf_end - pIn_buf_cur) < 2) \
+ { \
+ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
+ } \
+ else \
+ { \
+ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \
+ pIn_buf_cur += 2; \
+ num_bits += 16; \
+ } \
+ } \
+ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
+ code_len = temp >> 9, temp &= 511; \
+ else \
+ { \
+ code_len = TINFL_FAST_LOOKUP_BITS; \
+ do \
+ { \
+ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
+ } while (temp < 0); \
+ } \
+ sym = temp; \
+ bit_buf >>= code_len; \
+ num_bits -= code_len; \
+ } \
+ MZ_MACRO_END
+
+tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
+{
+ static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
+ static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
+ static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
+ static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
+ static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+ static const int s_min_table_sizes[3] = { 257, 1, 4 };
+
+ tinfl_status status = TINFL_STATUS_FAILED;
+ mz_uint32 num_bits, dist, counter, num_extra;
+ tinfl_bit_buf_t bit_buf;
+ const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
+ mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
+ size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
+
+ /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
+ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start))
+ {
+ *pIn_buf_size = *pOut_buf_size = 0;
+ return TINFL_STATUS_BAD_PARAM;
+ }
+
+ num_bits = r->m_num_bits;
+ bit_buf = r->m_bit_buf;
+ dist = r->m_dist;
+ counter = r->m_counter;
+ num_extra = r->m_num_extra;
+ dist_from_out_buf_start = r->m_dist_from_out_buf_start;
+ TINFL_CR_BEGIN
+
+ bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0;
+ r->m_z_adler32 = r->m_check_adler32 = 1;
+ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+ {
+ TINFL_GET_BYTE(1, r->m_zhdr0);
+ TINFL_GET_BYTE(2, r->m_zhdr1);
+ counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
+ if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
+ counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
+ if (counter)
+ {
+ TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);
+ }
+ }
+
+ do
+ {
+ TINFL_GET_BITS(3, r->m_final, 3);
+ r->m_type = r->m_final >> 1;
+ if (r->m_type == 0)
+ {
+ TINFL_SKIP_BITS(5, num_bits & 7);
+ for (counter = 0; counter < 4; ++counter)
+ {
+ if (num_bits)
+ TINFL_GET_BITS(6, r->m_raw_header[counter], 8);
+ else
+ TINFL_GET_BYTE(7, r->m_raw_header[counter]);
+ }
+ if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8))))
+ {
+ TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED);
+ }
+ while ((counter) && (num_bits))
+ {
+ TINFL_GET_BITS(51, dist, 8);
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = (mz_uint8)dist;
+ counter--;
+ }
+ while (counter)
+ {
+ size_t n;
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ while (pIn_buf_cur >= pIn_buf_end)
+ {
+ TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS);
+ }
+ n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
+ TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n);
+ pIn_buf_cur += n;
+ pOut_buf_cur += n;
+ counter -= (mz_uint)n;
+ }
+ }
+ else if (r->m_type == 3)
+ {
+ TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
+ }
+ else
+ {
+ if (r->m_type == 1)
+ {
+ mz_uint8 *p = r->m_tables[0].m_code_size;
+ mz_uint i;
+ r->m_table_sizes[0] = 288;
+ r->m_table_sizes[1] = 32;
+ TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
+ for (i = 0; i <= 143; ++i)
+ *p++ = 8;
+ for (; i <= 255; ++i)
+ *p++ = 9;
+ for (; i <= 279; ++i)
+ *p++ = 7;
+ for (; i <= 287; ++i)
+ *p++ = 8;
+ }
+ else
+ {
+ for (counter = 0; counter < 3; counter++)
+ {
+ TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]);
+ r->m_table_sizes[counter] += s_min_table_sizes[counter];
+ }
+ MZ_CLEAR_OBJ(r->m_tables[2].m_code_size);
+ for (counter = 0; counter < r->m_table_sizes[2]; counter++)
+ {
+ mz_uint s;
+ TINFL_GET_BITS(14, s, 3);
+ r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s;
+ }
+ r->m_table_sizes[2] = 19;
+ }
+ for (; (int)r->m_type >= 0; r->m_type--)
+ {
+ int tree_next, tree_cur;
+ tinfl_huff_table *pTable;
+ mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];
+ pTable = &r->m_tables[r->m_type];
+ MZ_CLEAR_OBJ(total_syms);
+ MZ_CLEAR_OBJ(pTable->m_look_up);
+ MZ_CLEAR_OBJ(pTable->m_tree);
+ for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)
+ total_syms[pTable->m_code_size[i]]++;
+ used_syms = 0, total = 0;
+ next_code[0] = next_code[1] = 0;
+ for (i = 1; i <= 15; ++i)
+ {
+ used_syms += total_syms[i];
+ next_code[i + 1] = (total = ((total + total_syms[i]) << 1));
+ }
+ if ((65536 != total) && (used_syms > 1))
+ {
+ TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
+ }
+ for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
+ {
+ mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index];
+ if (!code_size)
+ continue;
+ cur_code = next_code[code_size]++;
+ for (l = code_size; l > 0; l--, cur_code >>= 1)
+ rev_code = (rev_code << 1) | (cur_code & 1);
+ if (code_size <= TINFL_FAST_LOOKUP_BITS)
+ {
+ mz_int16 k = (mz_int16)((code_size << 9) | sym_index);
+ while (rev_code < TINFL_FAST_LOOKUP_SIZE)
+ {
+ pTable->m_look_up[rev_code] = k;
+ rev_code += (1 << code_size);
+ }
+ continue;
+ }
+ if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
+ {
+ pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
+ tree_cur = tree_next;
+ tree_next -= 2;
+ }
+ rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
+ for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
+ {
+ tree_cur -= ((rev_code >>= 1) & 1);
+ if (!pTable->m_tree[-tree_cur - 1])
+ {
+ pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next;
+ tree_cur = tree_next;
+ tree_next -= 2;
+ }
+ else
+ tree_cur = pTable->m_tree[-tree_cur - 1];
+ }
+ tree_cur -= ((rev_code >>= 1) & 1);
+ pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
+ }
+ if (r->m_type == 2)
+ {
+ for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)
+ {
+ mz_uint s;
+ TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]);
+ if (dist < 16)
+ {
+ r->m_len_codes[counter++] = (mz_uint8)dist;
+ continue;
+ }
+ if ((dist == 16) && (!counter))
+ {
+ TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
+ }
+ num_extra = "\02\03\07"[dist - 16];
+ TINFL_GET_BITS(18, s, num_extra);
+ s += "\03\03\013"[dist - 16];
+ TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s);
+ counter += s;
+ }
+ if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
+ {
+ TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
+ }
+ TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]);
+ TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
+ }
+ }
+ for (;;)
+ {
+ mz_uint8 *pSrc;
+ for (;;)
+ {
+ if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
+ {
+ TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
+ if (counter >= 256)
+ break;
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = (mz_uint8)counter;
+ }
+ else
+ {
+ int sym2;
+ mz_uint code_len;
+#if TINFL_USE_64BIT_BITBUF
+ if (num_bits < 30)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 4;
+ num_bits += 32;
+ }
+#else
+ if (num_bits < 15)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 2;
+ num_bits += 16;
+ }
+#endif
+ if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+ code_len = sym2 >> 9;
+ else
+ {
+ code_len = TINFL_FAST_LOOKUP_BITS;
+ do
+ {
+ sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];
+ } while (sym2 < 0);
+ }
+ counter = sym2;
+ bit_buf >>= code_len;
+ num_bits -= code_len;
+ if (counter & 256)
+ break;
+
+#if !TINFL_USE_64BIT_BITBUF
+ if (num_bits < 15)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 2;
+ num_bits += 16;
+ }
+#endif
+ if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+ code_len = sym2 >> 9;
+ else
+ {
+ code_len = TINFL_FAST_LOOKUP_BITS;
+ do
+ {
+ sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];
+ } while (sym2 < 0);
+ }
+ bit_buf >>= code_len;
+ num_bits -= code_len;
+
+ pOut_buf_cur[0] = (mz_uint8)counter;
+ if (sym2 & 256)
+ {
+ pOut_buf_cur++;
+ counter = sym2;
+ break;
+ }
+ pOut_buf_cur[1] = (mz_uint8)sym2;
+ pOut_buf_cur += 2;
+ }
+ }
+ if ((counter &= 511) == 256)
+ break;
+
+ num_extra = s_length_extra[counter - 257];
+ counter = s_length_base[counter - 257];
+ if (num_extra)
+ {
+ mz_uint extra_bits;
+ TINFL_GET_BITS(25, extra_bits, num_extra);
+ counter += extra_bits;
+ }
+
+ TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
+ num_extra = s_dist_extra[dist];
+ dist = s_dist_base[dist];
+ if (num_extra)
+ {
+ mz_uint extra_bits;
+ TINFL_GET_BITS(27, extra_bits, num_extra);
+ dist += extra_bits;
+ }
+
+ dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
+ if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
+ {
+ TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
+ }
+
+ pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
+
+ if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
+ {
+ while (counter--)
+ {
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
+ }
+ continue;
+ }
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+ else if ((counter >= 9) && (counter <= dist))
+ {
+ const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
+ do
+ {
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+ memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2);
+#else
+ ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
+ ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
+#endif
+ pOut_buf_cur += 8;
+ } while ((pSrc += 8) < pSrc_end);
+ if ((counter &= 7) < 3)
+ {
+ if (counter)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ if (counter > 1)
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur += counter;
+ }
+ continue;
+ }
+ }
+#endif
+ while(counter>2)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur[2] = pSrc[2];
+ pOut_buf_cur += 3;
+ pSrc += 3;
+ counter -= 3;
+ }
+ if (counter > 0)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ if (counter > 1)
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur += counter;
+ }
+ }
+ }
+ } while (!(r->m_final & 1));
+
+ /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
+ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */
+ TINFL_SKIP_BITS(32, num_bits & 7);
+ while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
+ {
+ --pIn_buf_cur;
+ num_bits -= 8;
+ }
+ bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);
+ MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */
+
+ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+ {
+ for (counter = 0; counter < 4; ++counter)
+ {
+ mz_uint s;
+ if (num_bits)
+ TINFL_GET_BITS(41, s, 8);
+ else
+ TINFL_GET_BYTE(42, s);
+ r->m_z_adler32 = (r->m_z_adler32 << 8) | s;
+ }
+ }
+ TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
+
+ TINFL_CR_FINISH
+
+common_exit:
+ /* As long as we aren't telling the caller that we NEED more input to make forward progress: */
+ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
+ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */
+ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS))
+ {
+ while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
+ {
+ --pIn_buf_cur;
+ num_bits -= 8;
+ }
+ }
+ r->m_num_bits = num_bits;
+ r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);
+ r->m_dist = dist;
+ r->m_counter = counter;
+ r->m_num_extra = num_extra;
+ r->m_dist_from_out_buf_start = dist_from_out_buf_start;
+ *pIn_buf_size = pIn_buf_cur - pIn_buf_next;
+ *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
+ if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
+ {
+ const mz_uint8 *ptr = pOut_buf_next;
+ size_t buf_len = *pOut_buf_size;
+ mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16;
+ size_t block_len = buf_len % 5552;
+ while (buf_len)
+ {
+ for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
+ {
+ s1 += ptr[0], s2 += s1;
+ s1 += ptr[1], s2 += s1;
+ s1 += ptr[2], s2 += s1;
+ s1 += ptr[3], s2 += s1;
+ s1 += ptr[4], s2 += s1;
+ s1 += ptr[5], s2 += s1;
+ s1 += ptr[6], s2 += s1;
+ s1 += ptr[7], s2 += s1;
+ }
+ for (; i < block_len; ++i)
+ s1 += *ptr++, s2 += s1;
+ s1 %= 65521U, s2 %= 65521U;
+ buf_len -= block_len;
+ block_len = 5552;
+ }
+ r->m_check_adler32 = (s2 << 16) + s1;
+ if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
+ status = TINFL_STATUS_ADLER32_MISMATCH;
+ }
+ return status;
+}
+
+/* Higher level helper functions. */
+void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+ tinfl_decompressor decomp;
+ void *pBuf = NULL, *pNew_buf;
+ size_t src_buf_ofs = 0, out_buf_capacity = 0;
+ *pOut_len = 0;
+ tinfl_init(&decomp);
+ for (;;)
+ {
+ size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
+ tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size,
+ (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+ if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
+ {
+ MZ_FREE(pBuf);
+ *pOut_len = 0;
+ return NULL;
+ }
+ src_buf_ofs += src_buf_size;
+ *pOut_len += dst_buf_size;
+ if (status == TINFL_STATUS_DONE)
+ break;
+ new_out_buf_capacity = out_buf_capacity * 2;
+ if (new_out_buf_capacity < 128)
+ new_out_buf_capacity = 128;
+ pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
+ if (!pNew_buf)
+ {
+ MZ_FREE(pBuf);
+ *pOut_len = 0;
+ return NULL;
+ }
+ pBuf = pNew_buf;
+ out_buf_capacity = new_out_buf_capacity;
+ }
+ return pBuf;
+}
+
+size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+ tinfl_decompressor decomp;
+ tinfl_status status;
+ tinfl_init(&decomp);
+ status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+ return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
+}
+
+int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ int result = 0;
+ tinfl_decompressor decomp;
+ mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE);
+ size_t in_buf_ofs = 0, dict_ofs = 0;
+ if (!pDict)
+ return TINFL_STATUS_FAILED;
+ tinfl_init(&decomp);
+ for (;;)
+ {
+ size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
+ tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
+ (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
+ in_buf_ofs += in_buf_size;
+ if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
+ break;
+ if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
+ {
+ result = (status == TINFL_STATUS_DONE);
+ break;
+ }
+ dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
+ }
+ MZ_FREE(pDict);
+ *pIn_buf_size = in_buf_ofs;
+ return result;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/miniz/miniz_tinfl.h b/platform/linux-generic/miniz/miniz_tinfl.h
new file mode 100644
index 000000000..28ca15fcb
--- /dev/null
+++ b/platform/linux-generic/miniz/miniz_tinfl.h
@@ -0,0 +1,146 @@
+#pragma once
+#include "miniz.h"
+#include "miniz_common.h"
+/* ------------------- Low-level Decompression API Definitions */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Decompression flags used by tinfl_decompress(). */
+/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */
+/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */
+/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */
+/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */
+enum
+{
+ TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
+ TINFL_FLAG_HAS_MORE_INPUT = 2,
+ TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
+ TINFL_FLAG_COMPUTE_ADLER32 = 8
+};
+
+/* High level decompression functions: */
+/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */
+/* On entry: */
+/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */
+/* On return: */
+/* Function returns a pointer to the decompressed data, or NULL on failure. */
+/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */
+/* The caller must call mz_free() on the returned block when it's no longer needed. */
+void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */
+/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */
+#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
+size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */
+/* Returns 1 on success or 0 on failure. */
+typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
+int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+struct tinfl_decompressor_tag;
+typedef struct tinfl_decompressor_tag tinfl_decompressor;
+
+/* Max size of LZ dictionary. */
+#define TINFL_LZ_DICT_SIZE 32768
+
+/* Return status. */
+typedef enum {
+ /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */
+ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */
+ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */
+ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4,
+
+ /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */
+ TINFL_STATUS_BAD_PARAM = -3,
+
+ /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */
+ TINFL_STATUS_ADLER32_MISMATCH = -2,
+
+ /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */
+ TINFL_STATUS_FAILED = -1,
+
+ /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */
+
+ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */
+ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */
+ TINFL_STATUS_DONE = 0,
+
+ /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */
+ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */
+ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */
+ TINFL_STATUS_NEEDS_MORE_INPUT = 1,
+
+ /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */
+ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */
+ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */
+ /* so I may need to add some code to address this. */
+ TINFL_STATUS_HAS_MORE_OUTPUT = 2
+} tinfl_status;
+
+/* Initializes the decompressor to its initial state. */
+#define tinfl_init(r) \
+ do \
+ { \
+ (r)->m_state = 0; \
+ } \
+ MZ_MACRO_END
+#define tinfl_get_adler32(r) (r)->m_check_adler32
+
+/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
+/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
+tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
+
+/* Internal/private bits follow. */
+enum
+{
+ TINFL_MAX_HUFF_TABLES = 3,
+ TINFL_MAX_HUFF_SYMBOLS_0 = 288,
+ TINFL_MAX_HUFF_SYMBOLS_1 = 32,
+ TINFL_MAX_HUFF_SYMBOLS_2 = 19,
+ TINFL_FAST_LOOKUP_BITS = 10,
+ TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
+};
+
+typedef struct
+{
+ mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
+ mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
+} tinfl_huff_table;
+
+#if MINIZ_HAS_64BIT_REGISTERS
+#define TINFL_USE_64BIT_BITBUF 1
+#else
+#define TINFL_USE_64BIT_BITBUF 0
+#endif
+
+#if TINFL_USE_64BIT_BITBUF
+typedef mz_uint64 tinfl_bit_buf_t;
+#define TINFL_BITBUF_SIZE (64)
+#else
+typedef mz_uint32 tinfl_bit_buf_t;
+#define TINFL_BITBUF_SIZE (32)
+#endif
+
+struct tinfl_decompressor_tag
+{
+ mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
+ tinfl_bit_buf_t m_bit_buf;
+ size_t m_dist_from_out_buf_start;
+ tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
+ mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
+};
+
+typedef struct
+{
+ tinfl_decompressor m_decomp;
+ mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed;
+ int m_window_bits;
+ mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
+ tinfl_status m_last_status;
+} inflate_state;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/platform/linux-generic/odp_atomic.c b/platform/linux-generic/odp_atomic.c
deleted file mode 100644
index 0e40cda51..000000000
--- a/platform/linux-generic/odp_atomic.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/atomic.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/atomic_inlines.h>
-#endif
-
-int odp_atomic_lock_free_u64(odp_atomic_op_t *atomic_op)
-{
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- /* All operations have locks */
- if (atomic_op)
- atomic_op->all_bits = 0;
-
- return 0;
-#else
- /* All operations are lock-free */
- if (atomic_op) {
- atomic_op->all_bits = ~((uint32_t)0);
- atomic_op->op.init = 0;
- }
-
- return 2;
-#endif
-}
diff --git a/platform/linux-generic/odp_atomic_api.c b/platform/linux-generic/odp_atomic_api.c
new file mode 100644
index 000000000..f887f5c1c
--- /dev/null
+++ b/platform/linux-generic/odp_atomic_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp/api/atomic.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/atomic_inlines.h>
diff --git a/platform/linux-generic/odp_barrier.c b/platform/linux-generic/odp_barrier.c
index a2c62676b..634f1fe4e 100644
--- a/platform/linux-generic/odp_barrier.c
+++ b/platform/linux-generic/odp_barrier.c
@@ -1,13 +1,14 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
#include <odp/api/barrier.h>
#include <odp/api/sync.h>
#include <odp/api/cpu.h>
#include <odp/api/atomic.h>
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/plat/sync_inlines.h>
+#include <odp/api/plat/cpu_inlines.h>
void odp_barrier_init(odp_barrier_t *barrier, int count)
{
@@ -32,7 +33,7 @@ void odp_barrier_wait(odp_barrier_t *barrier)
uint32_t count;
int wasless;
- odp_mb_full();
+ odp_mb_release();
count = odp_atomic_fetch_inc_u32(&barrier->bar);
wasless = count < barrier->count;
@@ -46,5 +47,5 @@ void odp_barrier_wait(odp_barrier_t *barrier)
odp_cpu_pause();
}
- odp_mb_full();
+ odp_mb_acquire();
}
diff --git a/platform/linux-generic/odp_bitmap.c b/platform/linux-generic/odp_bitmap.c
deleted file mode 100644
index a29b9ef28..000000000
--- a/platform/linux-generic/odp_bitmap.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <string.h>
-#include <unistd.h>
-#include <odp/api/std_types.h>
-#include <odp/api/byteorder.h>
-#include <odp_bitmap_internal.h>
-
-/*
- * WAPL base class bitmap operations
- */
-static inline void __wapl_add_pos(
- wapl_bitmap_t *map, unsigned int p)
-{
- unsigned int s, k = 0;
- unsigned int *pl = map->pl;
-
- while (pl[k] && p > pl[k])
- k++;
-
- if (p == pl[k])
- return;
-
- /* sorted insertion */
- for (; pl[k] && p < pl[k]; k++) {
- s = pl[k];
- pl[k] = p;
- p = s;
- }
-
- if (k < map->nwords)
- pl[k++] = p;
-
- pl[k] = 0;
-}
-
-static inline void __wapl_remove_pos(
- wapl_bitmap_t *map, unsigned int p)
-{
- unsigned int k = 0;
- unsigned int *pl = map->pl;
-
- while (pl[k] && p != pl[k])
- k++;
-
- for (; pl[k]; k++)
- pl[k] = pl[k + 1];
-}
-
-void __wapl_bitmap_and(wapl_bitmap_t *dst,
- wapl_bitmap_t *src, wapl_bitmap_t *and)
-{
- unsigned int k = 0, p;
- unsigned int *pl = src->pl;
-
- while ((p = *pl++) != 0) {
- dst->ul[p] = src->ul[p] & and->ul[p];
- if (dst->ul[p])
- dst->pl[k++] = p;
- }
-
- dst->pl[k] = 0;
-}
-
-void __wapl_bitmap_or(wapl_bitmap_t *dst, wapl_bitmap_t *or)
-{
- unsigned int p;
- unsigned int *pl = or->pl;
-
- while ((p = *pl++) != 0) {
- if (dst->ul[p] == 0)
- __wapl_add_pos(dst, p);
-
- dst->ul[p] |= or->ul[p];
- }
-}
-
-void __wapl_bitmap_set(wapl_bitmap_t *map, unsigned int bit)
-{
- unsigned int p = BIT_WORD(bit) + 1;
- unsigned long set = 1UL << (bit & (BITS_PER_LONG - 1));
-
- if (p > map->nwords)
- return;
-
- if (map->ul[p] == 0)
- __wapl_add_pos(map, p);
-
- map->ul[p] |= set;
-}
-
-void __wapl_bitmap_clear(wapl_bitmap_t *map, unsigned int bit)
-{
- unsigned int p = BIT_WORD(bit) + 1;
- unsigned long clear = 1UL << (bit & (BITS_PER_LONG - 1));
-
- if (p > map->nwords)
- return;
-
- map->ul[p] &= ~clear;
-
- if (map->ul[p] == 0)
- __wapl_remove_pos(map, p);
-}
-
-/*
- * WAPL bitmap iterator implementation
- */
-static void __wapl_iterator_start(wapl_bitmap_iterator_t *this)
-{
- this->_nbits = this->_base.nwords * BITS_PER_LONG;
-
- /* Advance to next queue index to start this
- * new round iteration.
- */
- if (this->_base.pl[0] == 0)
- this->_start = -1;
- else
- this->_start = __bitmap_wraparound_next(
- &this->_base.ul[1], this->_nbits, this->_start + 1);
-
- this->_next = this->_start;
-}
-
-static bool __wapl_iterator_has_next(wapl_bitmap_iterator_t *this)
-{
- return (this->_next != -1);
-}
-
-static unsigned int __wapl_iterator_next(wapl_bitmap_iterator_t *this)
-{
- int next = this->_next;
-
- this->_next = __bitmap_wraparound_next(
- &this->_base.ul[1], this->_nbits, this->_next + 1);
-
- if (this->_next == this->_start)
- this->_next = -1;
-
- return next;
-}
-
-void __wapl_bitmap_iterator(wapl_bitmap_iterator_t *this)
-{
- this->start = __wapl_iterator_start;
- this->has_next = __wapl_iterator_has_next;
- this->next = __wapl_iterator_next;
-
- this->_start = -1;
- this->_next = this->_start;
-}
-
-/*
- * Sparse base class bitmap operations
- */
-void __sparse_bitmap_set(sparse_bitmap_t *map, unsigned int bit)
-{
- unsigned int last = *map->last;
-
- /* Index exceeds */
- if (bit >= map->nbits)
- return;
-
- /* Full bitmap */
- if (last >= map->nbits)
- return;
-
- /* Bit was not set previously,
- * also record where we set the bit
- */
- if (!map->pl[bit]) {
- map->il[last++] = bit;
- map->pl[bit] = last;
-
- *map->last = last;
- }
-}
-
-void __sparse_bitmap_clear(sparse_bitmap_t *map, unsigned int bit)
-{
- unsigned int p, i;
- unsigned int last = *map->last;
-
- /* Index exceeds */
- if (bit >= map->nbits)
- return;
-
- /* Empty bitmap */
- if (last == 0)
- return;
-
- /* Bit was set previously */
- if (map->pl[bit]) {
- p = map->pl[bit] - 1;
- map->pl[bit] = 0;
-
- last--;
- *map->last = last;
-
- /* Fill the hole with the latest index */
- if (p < last) {
- i = map->il[last];
- map->pl[i] = p + 1;
- map->il[p] = i;
- }
- }
-}
-
-/*
- * Sparse bitmap iterator implementation
- */
-static void __sparse_iterator_start(sparse_bitmap_iterator_t *this)
-{
- this->_nbits = (int)*this->_base.last;
-
- /* Advance to next queue index to start this
- * new round iteration.
- */
- if (this->_nbits == 0)
- this->_start = -1;
- else
- this->_start = (this->_start + 1) & (this->_nbits - 1);
-
- this->_next = this->_start;
-}
-
-static bool __sparse_iterator_has_next(sparse_bitmap_iterator_t *this)
-{
- return (this->_next != -1);
-}
-
-static unsigned int __sparse_iterator_next(sparse_bitmap_iterator_t *this)
-{
- int next = this->_next;
-
- this->_next = (this->_next + 1) & (this->_nbits - 1);
- if (this->_next == this->_start)
- this->_next = -1;
-
- return this->_base.il[next];
-}
-
-void __sparse_bitmap_iterator(sparse_bitmap_iterator_t *this)
-{
- this->start = __sparse_iterator_start;
- this->has_next = __sparse_iterator_has_next;
- this->next = __sparse_iterator_next;
-
- this->_start = -1;
- this->_next = this->_start;
-}
-
-/*
- * Generic byte-width atomic set/clear
- */
-static inline void atomic_byte_set(
- unsigned char *addr, unsigned int bit)
-{
- unsigned char load, store;
- unsigned char set = 1 << (bit & (BITS_PER_BYTE - 1));
-
- do {
- load = *addr;
- store = load | set;
- } while (!__atomic_compare_exchange_n(addr, &load, store,
- 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
-}
-
-static inline void atomic_byte_clear(
- unsigned char *addr, unsigned int bit)
-{
- unsigned char load, store;
- unsigned char clear = 1 << (bit & (BITS_PER_BYTE - 1));
-
- do {
- load = *addr;
- store = load & ~clear;
- } while (!__atomic_compare_exchange_n(addr, &load, store,
- 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
-}
-
-static inline unsigned char *__bit_byte(
- unsigned long *word, unsigned int bit)
-{
- unsigned int i;
- unsigned char *b;
-
- b = (unsigned char *)word;
-
- i = bit & (BITS_PER_LONG - 1);
- i = i / BITS_PER_BYTE;
-
-#if (ODP_BYTE_ORDER == ODP_BIG_ENDIAN)
- i = BYTES_PER_LONG - 1 - i;
-#endif
- return &b[i];
-}
-
-void raw_bitmap_set(unsigned long *map, unsigned int bit)
-{
- unsigned long *p = map + BIT_WORD(bit);
-
- atomic_byte_set(__bit_byte(p, bit), bit);
-}
-
-void raw_bitmap_clear(unsigned long *map, unsigned int bit)
-{
- unsigned long *p = map + BIT_WORD(bit);
-
- atomic_byte_clear(__bit_byte(p, bit), bit);
-}
diff --git a/platform/linux-generic/odp_buffer.c b/platform/linux-generic/odp_buffer.c
index 9c7dc1f59..af9b85edc 100644
--- a/platform/linux-generic/odp_buffer.c
+++ b/platform/linux-generic/odp_buffer.c
@@ -1,82 +1,60 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
+#include <odp/api/align.h>
#include <odp/api/buffer.h>
+
+#include <odp/api/plat/buffer_inline_types.h>
+
#include <odp_pool_internal.h>
#include <odp_buffer_internal.h>
-#include <odp_buffer_inlines.h>
#include <odp_debug_internal.h>
+#include <odp_string_internal.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
-odp_buffer_t odp_buffer_from_event(odp_event_t ev)
-{
- return (odp_buffer_t)ev;
-}
+#include <odp/visibility_begin.h>
-odp_event_t odp_buffer_to_event(odp_buffer_t buf)
-{
- return (odp_event_t)buf;
-}
+/* Buffer header field offsets for inline functions */
+const _odp_buffer_inline_offset_t _odp_buffer_inline_offset ODP_ALIGNED_CACHE = {
+ .uarea_addr = offsetof(odp_buffer_hdr_t, uarea_addr)
+};
-void *odp_buffer_addr(odp_buffer_t buf)
-{
- odp_buffer_hdr_t *hdr = buf_hdl_to_hdr(buf);
-
- return hdr->seg[0].data;
-}
-
-uint32_t odp_buffer_size(odp_buffer_t buf)
-{
- odp_buffer_hdr_t *hdr = buf_hdl_to_hdr(buf);
-
- return hdr->size;
-}
+#include <odp/visibility_end.h>
-int odp_buffer_snprint(char *str, uint32_t n, odp_buffer_t buf)
+void odp_buffer_print(odp_buffer_t buf)
{
odp_buffer_hdr_t *hdr;
- pool_t *pool;
int len = 0;
+ int max_len = 512;
+ int n = max_len - 1;
+ char str[max_len];
if (!odp_buffer_is_valid(buf)) {
- ODP_PRINT("Buffer is not valid.\n");
- return len;
+ _ODP_ERR("Buffer is not valid.\n");
+ return;
}
- hdr = buf_hdl_to_hdr(buf);
- pool = hdr->pool_ptr;
-
- len += snprintf(&str[len], n-len,
- "Buffer\n");
- len += snprintf(&str[len], n-len,
- " pool %" PRIu64 "\n",
- odp_pool_to_u64(pool->pool_hdl));
- len += snprintf(&str[len], n-len,
- " addr %p\n", hdr->seg[0].data);
- len += snprintf(&str[len], n-len,
- " size %" PRIu32 "\n", hdr->size);
- len += snprintf(&str[len], n-len,
- " type %i\n", hdr->type);
-
- return len;
-}
-
-void odp_buffer_print(odp_buffer_t buf)
-{
- int max_len = 512;
- char str[max_len];
- int len;
-
- len = odp_buffer_snprint(str, max_len-1, buf);
+ hdr = _odp_buf_hdr(buf);
+
+ len += _odp_snprint(&str[len], n - len, "Buffer info\n");
+ len += _odp_snprint(&str[len], n - len, "-----------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_buffer_to_u64(buf));
+ len += _odp_snprint(&str[len], n - len, " pool index %u\n", hdr->event_hdr.index.pool);
+ len += _odp_snprint(&str[len], n - len, " buffer index %u\n",
+ hdr->event_hdr.index.event);
+ len += _odp_snprint(&str[len], n - len, " addr %p\n",
+ (void *)hdr->event_hdr.base_data);
+ len += _odp_snprint(&str[len], n - len, " size %u\n", odp_buffer_size(buf));
+ len += _odp_snprint(&str[len], n - len, " user area %p\n", hdr->uarea_addr);
str[len] = 0;
- ODP_PRINT("\n%s\n", str);
+ _ODP_PRINT("%s\n", str);
}
uint64_t odp_buffer_to_u64(odp_buffer_t hdl)
diff --git a/platform/linux-generic/odp_buffer_api.c b/platform/linux-generic/odp_buffer_api.c
new file mode 100644
index 000000000..71f0e6d4e
--- /dev/null
+++ b/platform/linux-generic/odp_buffer_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019 Nokia
+ */
+
+#include <odp/api/buffer.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/buffer_inlines.h>
diff --git a/platform/linux-generic/odp_byteorder.c b/platform/linux-generic/odp_byteorder.c
deleted file mode 100644
index a344c53f7..000000000
--- a/platform/linux-generic/odp_byteorder.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/byteorder.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/byteorder_inlines.h>
-#endif
diff --git a/platform/linux-generic/odp_byteorder_api.c b/platform/linux-generic/odp_byteorder_api.c
new file mode 100644
index 000000000..ebd945ef5
--- /dev/null
+++ b/platform/linux-generic/odp_byteorder_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#include <odp/api/byteorder.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/byteorder_inlines.h>
diff --git a/platform/linux-generic/odp_chksum.c b/platform/linux-generic/odp_chksum.c
new file mode 100644
index 000000000..33997fcb7
--- /dev/null
+++ b/platform/linux-generic/odp_chksum.c
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/chksum.h>
+#include <odp/api/std_types.h>
+#include <odp_chksum_internal.h>
+
+uint16_t odp_chksum_ones_comp16(const void *p, uint32_t len)
+{
+ return chksum_finalize(chksum_partial(p, len, 0));
+}
diff --git a/platform/linux-generic/odp_classification.c b/platform/linux-generic/odp_classification.c
index 5e9173dc7..83a676ce5 100644
--- a/platform/linux-generic/odp_classification.c
+++ b/platform/linux-generic/odp_classification.c
@@ -1,28 +1,39 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
#include <odp/api/classification.h>
#include <odp/api/align.h>
-#include <odp/api/queue.h>
#include <odp/api/debug.h>
-#include <odp_internal.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/pool.h>
+#include <odp/api/queue.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/spinlock.h>
+
+#include <odp_init_internal.h>
#include <odp_debug_internal.h>
#include <odp_packet_internal.h>
-#include <odp/api/packet_io.h>
#include <odp_packet_io_internal.h>
#include <odp_classification_datamodel.h>
-#include <odp_classification_inlines.h>
#include <odp_classification_internal.h>
-#include <odp/api/shared_memory.h>
+#include <odp_string_internal.h>
#include <protocols/eth.h>
#include <protocols/ip.h>
+#include <protocols/ipsec.h>
+#include <protocols/udp.h>
+#include <protocols/tcp.h>
+#include <protocols/thash.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
-#include <odp/api/spinlock.h>
+#include <inttypes.h>
+
+/* Debug level for per packet classification operations */
+#define CLS_DBG 3
+#define MAX_MARK UINT16_MAX
#define LOCK(a) odp_spinlock_lock(a)
#define UNLOCK(a) odp_spinlock_unlock(a)
@@ -30,102 +41,106 @@
static cos_tbl_t *cos_tbl;
static pmr_tbl_t *pmr_tbl;
+static _cls_queue_grp_tbl_t *queue_grp_tbl;
+
+cls_global_t *_odp_cls_global;
+
+static const rss_key default_rss = {
+ .u8 = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+ 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+ 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+ 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+ 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+ }
+};
+
+static inline uint32_t _odp_cos_to_ndx(odp_cos_t cos)
+{
+ return _odp_typeval(cos) - 1;
+}
+
+static inline odp_cos_t _odp_cos_from_ndx(uint32_t ndx)
+{
+ return _odp_cast_scalar(odp_cos_t, ndx + 1);
+}
+
+static inline uint32_t _odp_pmr_to_ndx(odp_pmr_t pmr)
+{
+ return _odp_typeval(pmr) - 1;
+}
+
+static inline odp_pmr_t _odp_pmr_from_ndx(uint32_t ndx)
+{
+ return _odp_cast_scalar(odp_pmr_t, ndx + 1);
+}
static
-cos_t *get_cos_entry_internal(odp_cos_t cos_id)
+cos_t *get_cos_entry_internal(odp_cos_t cos)
{
- return &cos_tbl->cos_entry[_odp_typeval(cos_id)];
+ return &cos_tbl->cos_entry[_odp_cos_to_ndx(cos)];
}
static
-pmr_t *get_pmr_entry_internal(odp_pmr_t pmr_id)
+pmr_t *get_pmr_entry_internal(odp_pmr_t pmr)
{
- return &pmr_tbl->pmr[_odp_typeval(pmr_id)];
+ return &pmr_tbl->pmr[_odp_pmr_to_ndx(pmr)];
}
-int odp_classification_init_global(void)
+int _odp_classification_init_global(void)
{
- odp_shm_t cos_shm;
- odp_shm_t pmr_shm;
+ odp_shm_t shm;
int i;
- cos_shm = odp_shm_reserve("shm_odp_cos_tbl",
- sizeof(cos_tbl_t),
- sizeof(cos_t), 0);
+ shm = odp_shm_reserve("_odp_cls_global", sizeof(cls_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
- if (cos_shm == ODP_SHM_INVALID) {
- ODP_ERR("shm allocation failed for shm_odp_cos_tbl");
- goto error;
- }
+ _odp_cls_global = odp_shm_addr(shm);
+ memset(_odp_cls_global, 0, sizeof(cls_global_t));
- cos_tbl = odp_shm_addr(cos_shm);
- if (cos_tbl == NULL)
- goto error_cos;
+ _odp_cls_global->shm = shm;
+ cos_tbl = &_odp_cls_global->cos_tbl;
+ pmr_tbl = &_odp_cls_global->pmr_tbl;
+ queue_grp_tbl = &_odp_cls_global->queue_grp_tbl;
- memset(cos_tbl, 0, sizeof(cos_tbl_t));
- for (i = 0; i < ODP_COS_MAX_ENTRY; i++) {
+ for (i = 0; i < CLS_COS_MAX_ENTRY; i++) {
/* init locks */
- cos_t *cos =
- get_cos_entry_internal(_odp_cast_scalar(odp_cos_t, i));
- LOCK_INIT(&cos->s.lock);
- }
+ cos_t *cos = get_cos_entry_internal(_odp_cos_from_ndx(i));
- pmr_shm = odp_shm_reserve("shm_odp_pmr_tbl",
- sizeof(pmr_tbl_t),
- sizeof(pmr_t), 0);
-
- if (pmr_shm == ODP_SHM_INVALID) {
- ODP_ERR("shm allocation failed for shm_odp_pmr_tbl");
- goto error_cos;
+ LOCK_INIT(&cos->lock);
}
- pmr_tbl = odp_shm_addr(pmr_shm);
- if (pmr_tbl == NULL)
- goto error_pmr;
-
- memset(pmr_tbl, 0, sizeof(pmr_tbl_t));
- for (i = 0; i < ODP_PMR_MAX_ENTRY; i++) {
+ for (i = 0; i < CLS_PMR_MAX_ENTRY; i++) {
/* init locks */
- pmr_t *pmr =
- get_pmr_entry_internal(_odp_cast_scalar(odp_pmr_t, i));
- LOCK_INIT(&pmr->s.lock);
+ pmr_t *pmr = get_pmr_entry_internal(_odp_pmr_from_ndx(i));
+
+ LOCK_INIT(&pmr->lock);
}
return 0;
-
-error_pmr:
- odp_shm_free(pmr_shm);
-error_cos:
- odp_shm_free(cos_shm);
-error:
- return -1;
}
-int odp_classification_term_global(void)
+int _odp_classification_term_global(void)
{
- int ret = 0;
- int rc = 0;
-
- ret = odp_shm_free(odp_shm_lookup("shm_odp_cos_tbl"));
- if (ret < 0) {
- ODP_ERR("shm free failed for shm_odp_cos_tbl");
- rc = -1;
- }
-
- ret = odp_shm_free(odp_shm_lookup("shm_odp_pmr_tbl"));
- if (ret < 0) {
- ODP_ERR("shm free failed for shm_odp_pmr_tbl");
- rc = -1;
+ if (_odp_cls_global && odp_shm_free(_odp_cls_global->shm)) {
+ _ODP_ERR("shm free failed\n");
+ return -1;
}
- return rc;
+ return 0;
}
void odp_cls_cos_param_init(odp_cls_cos_param_t *param)
{
+ memset(param, 0, sizeof(odp_cls_cos_param_t));
+
param->queue = ODP_QUEUE_INVALID;
param->pool = ODP_POOL_INVALID;
- param->drop_policy = ODP_COS_DROP_NEVER;
+ param->num_queue = 1;
+ param->vector.enable = false;
+ odp_queue_param_init(&param->queue_param);
}
void odp_cls_pmr_param_init(odp_pmr_param_t *param)
@@ -135,73 +150,220 @@ void odp_cls_pmr_param_init(odp_pmr_param_t *param)
int odp_cls_capability(odp_cls_capability_t *capability)
{
- unsigned count = 0;
-
- for (int i = 0; i < ODP_PMR_MAX_ENTRY; i++)
- if (!pmr_tbl->pmr[i].s.valid)
- count++;
-
- capability->max_pmr_terms = ODP_PMR_MAX_ENTRY;
- capability->available_pmr_terms = count;
- capability->max_cos = ODP_COS_MAX_ENTRY;
+ memset(capability, 0, sizeof(odp_cls_capability_t));
+ capability->max_pmr = CLS_PMR_MAX_ENTRY;
+ capability->max_pmr_per_cos = CLS_PMR_PER_COS_MAX;
+ capability->max_terms_per_pmr = CLS_PMRTERM_MAX;
+ capability->max_cos = CLS_COS_MAX_ENTRY;
+ capability->max_cos_stats = capability->max_cos;
capability->pmr_range_supported = false;
capability->supported_terms.all_bits = 0;
+ capability->supported_terms.bit.len = 1;
+ capability->supported_terms.bit.ethtype_0 = 1;
+ capability->supported_terms.bit.ethtype_x = 1;
+ capability->supported_terms.bit.vlan_id_0 = 1;
+ capability->supported_terms.bit.vlan_id_x = 1;
+ capability->supported_terms.bit.vlan_pcp_0 = 1;
+ capability->supported_terms.bit.dmac = 1;
capability->supported_terms.bit.ip_proto = 1;
+ capability->supported_terms.bit.ip_dscp = 1;
capability->supported_terms.bit.udp_dport = 1;
capability->supported_terms.bit.udp_sport = 1;
capability->supported_terms.bit.tcp_dport = 1;
capability->supported_terms.bit.tcp_sport = 1;
capability->supported_terms.bit.sip_addr = 1;
capability->supported_terms.bit.dip_addr = 1;
+ capability->supported_terms.bit.sip6_addr = 1;
+ capability->supported_terms.bit.dip6_addr = 1;
+ capability->supported_terms.bit.ipsec_spi = 1;
+ capability->supported_terms.bit.custom_frame = 1;
+ capability->supported_terms.bit.custom_l3 = 1;
+ capability->random_early_detection = ODP_SUPPORT_NO;
+ capability->back_pressure = ODP_SUPPORT_NO;
+ capability->threshold_red.all_bits = 0;
+ capability->threshold_bp.all_bits = 0;
+ capability->max_hash_queues = CLS_COS_QUEUE_MAX;
+ capability->hash_protocols.proto.ipv4_udp = 1;
+ capability->hash_protocols.proto.ipv4_tcp = 1;
+ capability->hash_protocols.proto.ipv4 = 1;
+ capability->hash_protocols.proto.ipv6_udp = 1;
+ capability->hash_protocols.proto.ipv6_tcp = 1;
+ capability->hash_protocols.proto.ipv6 = 1;
+ capability->max_mark = MAX_MARK;
+ capability->stats.cos.counter.discards = 1;
+ capability->stats.cos.counter.packets = 1;
+ capability->stats.queue.counter.discards = 1;
+ capability->stats.queue.counter.packets = 1;
+
return 0;
}
-odp_cos_t odp_cls_cos_create(const char *name, odp_cls_cos_param_t *param)
+void odp_cls_pmr_create_opt_init(odp_pmr_create_opt_t *opt)
{
- int i, j;
- queue_t queue;
- odp_cls_drop_t drop_policy;
+ opt->terms = NULL;
+ opt->num_terms = 0;
+ opt->mark = 0;
+}
- /* Packets are dropped if Queue or Pool is invalid*/
- if (param->queue == ODP_QUEUE_INVALID)
- queue = QUEUE_NULL;
- else
- queue = queue_fn->from_ext(param->queue);
+static void _odp_cls_update_hash_proto(cos_t *cos,
+ odp_pktin_hash_proto_t hash_proto)
+{
+ if (hash_proto.proto.ipv4 || hash_proto.proto.ipv4_tcp ||
+ hash_proto.proto.ipv4_udp)
+ cos->hash_proto.ipv4 = 1;
+ if (hash_proto.proto.ipv6 || hash_proto.proto.ipv6_tcp ||
+ hash_proto.proto.ipv6_udp)
+ cos->hash_proto.ipv6 = 1;
+ if (hash_proto.proto.ipv4_tcp || hash_proto.proto.ipv6_tcp)
+ cos->hash_proto.tcp = 1;
+ if (hash_proto.proto.ipv4_udp || hash_proto.proto.ipv6_udp)
+ cos->hash_proto.udp = 1;
+}
+
+static inline void _cls_queue_unwind(uint32_t tbl_index, uint32_t j)
+{
+ while (j > 0)
+ odp_queue_destroy(queue_grp_tbl->queue[tbl_index + --j]);
+}
+
+odp_cos_t odp_cls_cos_create(const char *name, const odp_cls_cos_param_t *param_in)
+{
+ uint32_t i, j;
+ odp_queue_t queue;
+ cos_t *cos;
+ uint32_t tbl_index;
+ odp_cls_cos_param_t param = *param_in;
+
+ if (param.action == ODP_COS_ACTION_DROP) {
+ param.num_queue = 1;
+ param.queue = ODP_QUEUE_INVALID;
+ param.pool = ODP_POOL_INVALID;
+ param.vector.enable = false;
+ } else {
+ if (param.num_queue == 1 && param.queue == ODP_QUEUE_INVALID)
+ return ODP_COS_INVALID;
+ }
+
+ /* num_queue should not be zero */
+ if (param.num_queue > CLS_COS_QUEUE_MAX || param.num_queue < 1)
+ return ODP_COS_INVALID;
- drop_policy = param->drop_policy;
+ /* Validate packet vector parameters */
+ if (param.vector.enable) {
+ odp_pool_t pool = param.vector.pool;
+ odp_pool_info_t pool_info;
+
+ if (pool == ODP_POOL_INVALID || odp_pool_info(pool, &pool_info)) {
+ _ODP_ERR("invalid packet vector pool\n");
+ return ODP_COS_INVALID;
+ }
+ if (pool_info.params.type != ODP_POOL_VECTOR) {
+ _ODP_ERR("wrong pool type\n");
+ return ODP_COS_INVALID;
+ }
+ if (param.vector.max_size == 0) {
+ _ODP_ERR("vector.max_size is zero\n");
+ return ODP_COS_INVALID;
+ }
+ if (param.vector.max_size > pool_info.params.vector.max_size) {
+ _ODP_ERR("vector.max_size larger than pool max vector size\n");
+ return ODP_COS_INVALID;
+ }
+ }
- for (i = 0; i < ODP_COS_MAX_ENTRY; i++) {
- LOCK(&cos_tbl->cos_entry[i].s.lock);
- if (0 == cos_tbl->cos_entry[i].s.valid) {
- char *cos_name = cos_tbl->cos_entry[i].s.name;
+ for (i = 0; i < CLS_COS_MAX_ENTRY; i++) {
+ cos = &cos_tbl->cos_entry[i];
+ LOCK(&cos->lock);
+ if (0 == cos->valid) {
+ char *cos_name = cos->name;
- if (name == NULL) {
+ if (name == NULL)
cos_name[0] = 0;
+ else
+ _odp_strcpy(cos_name, name, ODP_COS_NAME_LEN);
+
+ for (j = 0; j < CLS_PMR_PER_COS_MAX; j++) {
+ cos->pmr[j] = NULL;
+ cos->linked_cos[j] = NULL;
+ }
+
+ cos->num_queue = param.num_queue;
+
+ if (param.num_queue > 1) {
+ cos->queue_param = param.queue_param;
+ cos->queue_group = true;
+ cos->queue = ODP_QUEUE_INVALID;
+ _odp_cls_update_hash_proto(cos,
+ param.hash_proto);
+ tbl_index = i * CLS_COS_QUEUE_MAX;
+ for (j = 0; j < param.num_queue; j++) {
+ char hq_name[ODP_QUEUE_NAME_LEN];
+
+ snprintf(hq_name, sizeof(hq_name), "_odp_cos_hq_%u_%u",
+ i, j);
+ queue = odp_queue_create(hq_name, &cos->queue_param);
+ if (queue == ODP_QUEUE_INVALID) {
+ /* unwind the queues */
+ _cls_queue_unwind(tbl_index, j);
+ UNLOCK(&cos->lock);
+ return ODP_COS_INVALID;
+ }
+ queue_grp_tbl->queue[tbl_index + j] =
+ queue;
+ }
+
} else {
- strncpy(cos_name, name, ODP_COS_NAME_LEN - 1);
- cos_name[ODP_COS_NAME_LEN - 1] = 0;
+ cos->queue_group = false;
+ cos->queue = param.queue;
}
- for (j = 0; j < ODP_PMR_PER_COS_MAX; j++) {
- cos_tbl->cos_entry[i].s.pmr[j] = NULL;
- cos_tbl->cos_entry[i].s.linked_cos[j] = NULL;
+
+ odp_atomic_init_u64(&cos->stats.discards, 0);
+ odp_atomic_init_u64(&cos->stats.packets, 0);
+
+ /* Initialize statistics counters */
+ for (j = 0; j < cos->num_queue; j++) {
+ odp_atomic_init_u64(&cos->queue_stats[j].discards, 0);
+ odp_atomic_init_u64(&cos->queue_stats[j].packets, 0);
}
- cos_tbl->cos_entry[i].s.queue = queue;
- cos_tbl->cos_entry[i].s.pool = param->pool;
- cos_tbl->cos_entry[i].s.headroom = 0;
- cos_tbl->cos_entry[i].s.valid = 1;
- cos_tbl->cos_entry[i].s.drop_policy = drop_policy;
- odp_atomic_init_u32(&cos_tbl->cos_entry[i]
- .s.num_rule, 0);
- UNLOCK(&cos_tbl->cos_entry[i].s.lock);
- return _odp_cast_scalar(odp_cos_t, i);
+
+ cos->action = param.action;
+ cos->pool = param.pool;
+ cos->headroom = 0;
+ cos->valid = 1;
+ odp_atomic_init_u32(&cos->num_rule, 0);
+ cos->index = i;
+ cos->vector = param.vector;
+ cos->stats_enable = param.stats_enable;
+ UNLOCK(&cos->lock);
+ return _odp_cos_from_ndx(i);
}
- UNLOCK(&cos_tbl->cos_entry[i].s.lock);
+ UNLOCK(&cos->lock);
}
- ODP_ERR("ODP_COS_MAX_ENTRY reached");
+ _ODP_ERR("CLS_COS_MAX_ENTRY reached\n");
return ODP_COS_INVALID;
}
+int odp_cls_cos_create_multi(const char *name[], const odp_cls_cos_param_t param[],
+ odp_cos_t cos[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(param != NULL);
+ _ODP_ASSERT(cos != NULL);
+
+ for (i = 0; i < num; i++) {
+ const char *cur_name = name != NULL ? name[i] : NULL;
+ odp_cos_t new_cos = odp_cls_cos_create(cur_name, &param[i]);
+
+ if (odp_unlikely(new_cos == ODP_COS_INVALID))
+ return (i == 0) ? -1 : i;
+
+ cos[i] = new_cos;
+ }
+ return i;
+}
+
/*
* Allocate an odp_pmr_t Handle
*/
@@ -210,42 +372,44 @@ odp_pmr_t alloc_pmr(pmr_t **pmr)
{
int i;
- for (i = 0; i < ODP_PMR_MAX_ENTRY; i++) {
- LOCK(&pmr_tbl->pmr[i].s.lock);
- if (0 == pmr_tbl->pmr[i].s.valid) {
- pmr_tbl->pmr[i].s.valid = 1;
- odp_atomic_init_u32(&pmr_tbl->pmr[i].s.count, 0);
- pmr_tbl->pmr[i].s.num_pmr = 0;
+ for (i = 0; i < CLS_PMR_MAX_ENTRY; i++) {
+ LOCK(&pmr_tbl->pmr[i].lock);
+ if (0 == pmr_tbl->pmr[i].valid) {
+ pmr_tbl->pmr[i].valid = 1;
+ pmr_tbl->pmr[i].num_pmr = 0;
*pmr = &pmr_tbl->pmr[i];
/* return as locked */
- return _odp_cast_scalar(odp_pmr_t, i);
+ return _odp_pmr_from_ndx(i);
}
- UNLOCK(&pmr_tbl->pmr[i].s.lock);
+ UNLOCK(&pmr_tbl->pmr[i].lock);
}
- ODP_ERR("ODP_PMR_MAX_ENTRY reached");
- return ODP_PMR_INVAL;
+ _ODP_ERR("CLS_PMR_MAX_ENTRY reached\n");
+ return ODP_PMR_INVALID;
}
static
-cos_t *get_cos_entry(odp_cos_t cos_id)
+cos_t *get_cos_entry(odp_cos_t cos)
{
- if (_odp_typeval(cos_id) >= ODP_COS_MAX_ENTRY ||
- cos_id == ODP_COS_INVALID)
+ uint32_t cos_id = _odp_cos_to_ndx(cos);
+
+ if (cos_id >= CLS_COS_MAX_ENTRY || cos == ODP_COS_INVALID)
return NULL;
- if (cos_tbl->cos_entry[_odp_typeval(cos_id)].s.valid == 0)
+ if (cos_tbl->cos_entry[cos_id].valid == 0)
return NULL;
- return &cos_tbl->cos_entry[_odp_typeval(cos_id)];
+ return &cos_tbl->cos_entry[cos_id];
}
static
-pmr_t *get_pmr_entry(odp_pmr_t pmr_id)
+pmr_t *get_pmr_entry(odp_pmr_t pmr)
{
- if (_odp_typeval(pmr_id) >= ODP_PMR_MAX_ENTRY ||
- pmr_id == ODP_PMR_INVAL)
+ uint32_t pmr_id = _odp_pmr_to_ndx(pmr);
+
+ if (pmr_id >= CLS_PMR_MAX_ENTRY ||
+ pmr == ODP_PMR_INVALID)
return NULL;
- if (pmr_tbl->pmr[_odp_typeval(pmr_id)].s.valid == 0)
+ if (pmr_tbl->pmr[pmr_id].valid == 0)
return NULL;
- return &pmr_tbl->pmr[_odp_typeval(pmr_id)];
+ return &pmr_tbl->pmr[pmr_id];
}
int odp_cos_destroy(odp_cos_t cos_id)
@@ -253,28 +417,56 @@ int odp_cos_destroy(odp_cos_t cos_id)
cos_t *cos = get_cos_entry(cos_id);
if (NULL == cos) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
return -1;
}
- cos->s.valid = 0;
+ if (cos->queue_group)
+ _cls_queue_unwind(cos->index * CLS_COS_QUEUE_MAX, cos->num_queue);
+
+ cos->valid = 0;
return 0;
}
+int odp_cos_destroy_multi(odp_cos_t cos[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(cos != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ int ret = odp_cos_destroy(cos[i]);
+
+ if (ret)
+ return (i == 0) ? ret : i;
+ }
+
+ return i;
+}
+
int odp_cos_queue_set(odp_cos_t cos_id, odp_queue_t queue_id)
{
cos_t *cos = get_cos_entry(cos_id);
if (cos == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return -1;
+ }
+
+ if (queue_id == ODP_QUEUE_INVALID) {
+ _ODP_ERR("Invalid queue\n");
+ return -1;
+ }
+
+ if (cos->num_queue != 1) {
+ _ODP_ERR("Hashing enabled, cannot set queue\n");
return -1;
}
+
/* Locking is not required as intermittent stale
data during CoS modification is acceptable*/
- if (queue_id == ODP_QUEUE_INVALID)
- cos->s.queue = QUEUE_NULL;
- else
- cos->s.queue = queue_fn->from_ext(queue_id);
+ cos->queue = queue_id;
return 0;
}
@@ -283,59 +475,79 @@ odp_queue_t odp_cos_queue(odp_cos_t cos_id)
cos_t *cos = get_cos_entry(cos_id);
if (!cos) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
return ODP_QUEUE_INVALID;
}
- if (cos->s.queue == QUEUE_NULL)
- return ODP_QUEUE_INVALID;
-
- return queue_fn->to_ext(cos->s.queue);
+ return cos->queue;
}
-int odp_cos_drop_set(odp_cos_t cos_id, odp_cls_drop_t drop_policy)
+uint32_t odp_cls_cos_num_queue(odp_cos_t cos_id)
{
cos_t *cos = get_cos_entry(cos_id);
if (!cos) {
- ODP_ERR("Invalid odp_cos_t handle");
- return -1;
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return 0;
}
- /*Drop policy is not supported in v1.0*/
- cos->s.drop_policy = drop_policy;
- return 0;
+ return cos->num_queue;
}
-odp_cls_drop_t odp_cos_drop(odp_cos_t cos_id)
+uint32_t odp_cls_cos_queues(odp_cos_t cos_id, odp_queue_t queue[],
+ uint32_t num)
{
- cos_t *cos = get_cos_entry(cos_id);
+ uint32_t num_queues;
+ cos_t *cos;
+ uint32_t tbl_index;
+ uint32_t i;
+ cos = get_cos_entry(cos_id);
if (!cos) {
- ODP_ERR("Invalid odp_cos_t handle");
- return -1;
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return 0;
}
- return cos->s.drop_policy;
+ if (cos->num_queue == 1) {
+ if (num == 0)
+ return 1;
+
+ queue[0] = cos->queue;
+ return 1;
+ }
+
+ if (num < cos->num_queue)
+ num_queues = num;
+ else
+ num_queues = cos->num_queue;
+
+ tbl_index = cos->index * CLS_COS_QUEUE_MAX;
+ for (i = 0; i < num_queues; i++)
+ queue[i] = queue_grp_tbl->queue[tbl_index + i];
+
+ return cos->num_queue;
}
int odp_pktio_default_cos_set(odp_pktio_t pktio_in, odp_cos_t default_cos)
{
pktio_entry_t *entry;
- cos_t *cos;
+ cos_t *cos = NULL;
entry = get_pktio_entry(pktio_in);
if (entry == NULL) {
- ODP_ERR("Invalid odp_pktio_t handle");
+ _ODP_ERR("Invalid odp_pktio_t handle\n");
return -1;
}
- cos = get_cos_entry(default_cos);
- if (cos == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
- return -1;
+
+ if (default_cos != ODP_COS_INVALID) {
+ cos = get_cos_entry(default_cos);
+ if (cos == NULL) {
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return -1;
+ }
}
- entry->s.cls.default_cos = cos;
+ entry->cls.default_cos = cos;
return 0;
}
@@ -346,31 +558,27 @@ int odp_pktio_error_cos_set(odp_pktio_t pktio_in, odp_cos_t error_cos)
entry = get_pktio_entry(pktio_in);
if (entry == NULL) {
- ODP_ERR("Invalid odp_pktio_t handle");
+ _ODP_ERR("Invalid odp_pktio_t handle\n");
return -1;
}
cos = get_cos_entry(error_cos);
if (cos == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
return -1;
}
- entry->s.cls.error_cos = cos;
+ entry->cls.error_cos = cos;
return 0;
}
int odp_pktio_skip_set(odp_pktio_t pktio_in, uint32_t offset)
{
- pktio_entry_t *entry = get_pktio_entry(pktio_in);
+ (void)pktio_in;
+ (void)offset;
- if (entry == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
- return -1;
- }
-
- entry->s.cls.skip = offset;
- return 0;
+ /* Skipping bytes before parsing is not supported */
+ return -ENOTSUP;
}
int odp_pktio_headroom_set(odp_pktio_t pktio_in, uint32_t headroom)
@@ -378,120 +586,103 @@ int odp_pktio_headroom_set(odp_pktio_t pktio_in, uint32_t headroom)
pktio_entry_t *entry = get_pktio_entry(pktio_in);
if (entry == NULL) {
- ODP_ERR("Invalid odp_pktio_t handle");
+ _ODP_ERR("Invalid odp_pktio_t handle\n");
return -1;
}
- entry->s.cls.headroom = headroom;
+ entry->cls.headroom = headroom;
return 0;
}
-int odp_cos_with_l2_priority(odp_pktio_t pktio_in,
- uint8_t num_qos,
- uint8_t qos_table[],
- odp_cos_t cos_table[])
+static int pmr_create_term(pmr_term_value_t *value,
+ const odp_pmr_param_t *param)
{
- pmr_l2_cos_t *l2_cos;
- uint32_t i;
- cos_t *cos;
- pktio_entry_t *entry = get_pktio_entry(pktio_in);
+ uint32_t size;
+ uint8_t i;
+ int custom = 0;
+ odp_cls_pmr_term_t term = param->term;
- if (entry == NULL) {
- ODP_ERR("Invalid odp_pktio_t handle");
+ if (param->range_term) {
+ _ODP_ERR("PMR value range not supported\n");
return -1;
}
- l2_cos = &entry->s.cls.l2_cos_table;
- LOCK(&l2_cos->lock);
- /* Update the L2 QoS table*/
- for (i = 0; i < num_qos; i++) {
- cos = get_cos_entry(cos_table[i]);
- if (cos != NULL) {
- if (ODP_COS_MAX_L2_QOS > qos_table[i])
- l2_cos->cos[qos_table[i]] = cos;
- }
- }
- UNLOCK(&l2_cos->lock);
- return 0;
-}
+ value->term = term;
+ value->range_term = param->range_term;
-int odp_cos_with_l3_qos(odp_pktio_t pktio_in,
- uint32_t num_qos,
- uint8_t qos_table[],
- odp_cos_t cos_table[],
- odp_bool_t l3_preference)
-{
- pmr_l3_cos_t *l3_cos;
- uint32_t i;
- pktio_entry_t *entry = get_pktio_entry(pktio_in);
- cos_t *cos;
+ switch (term) {
+ case ODP_PMR_VLAN_PCP_0:
+ /* Fall through */
+ case ODP_PMR_IPPROTO:
+ /* Fall through */
+ case ODP_PMR_IP_DSCP:
+ size = 1;
+ break;
+
+ case ODP_PMR_ETHTYPE_0:
+ /* Fall through */
+ case ODP_PMR_ETHTYPE_X:
+ /* Fall through */
+ case ODP_PMR_VLAN_ID_0:
+ /* Fall through */
+ case ODP_PMR_VLAN_ID_X:
+ /* Fall through */
+ case ODP_PMR_UDP_DPORT:
+ /* Fall through */
+ case ODP_PMR_TCP_DPORT:
+ /* Fall through */
+ case ODP_PMR_UDP_SPORT:
+ /* Fall through */
+ case ODP_PMR_TCP_SPORT:
+ size = 2;
+ break;
+
+ case ODP_PMR_LEN:
+ /* Fall through */
+ case ODP_PMR_SIP_ADDR:
+ /* Fall through */
+ case ODP_PMR_DIP_ADDR:
+ /* Fall through */
+ case ODP_PMR_IPSEC_SPI:
+ /* Fall through */
+ case ODP_PMR_LD_VNI:
+ size = 4;
+ break;
+
+ case ODP_PMR_DMAC:
+ size = 6;
+ break;
- if (entry == NULL) {
- ODP_ERR("Invalid odp_pktio_t handle");
+ case ODP_PMR_SIP6_ADDR:
+ /* Fall through */
+ case ODP_PMR_DIP6_ADDR:
+ size = 16;
+ break;
+
+ case ODP_PMR_CUSTOM_FRAME:
+ /* Fall through */
+ case ODP_PMR_CUSTOM_L3:
+ custom = 1;
+ size = MAX_PMR_TERM_SIZE;
+ break;
+
+ default:
+ _ODP_ERR("Bad PMR term\n");
return -1;
}
- entry->s.cls.l3_precedence = l3_preference;
- l3_cos = &entry->s.cls.l3_cos_table;
-
- LOCK(&l3_cos->lock);
- /* Update the L3 QoS table*/
- for (i = 0; i < num_qos; i++) {
- cos = get_cos_entry(cos_table[i]);
- if (cos != NULL) {
- if (ODP_COS_MAX_L3_QOS > qos_table[i])
- l3_cos->cos[qos_table[i]] = cos;
- }
+ if ((!custom && param->val_sz != size) ||
+ (custom && param->val_sz > size)) {
+ _ODP_ERR("Bad PMR value size: %u\n", param->val_sz);
+ return -1;
}
- UNLOCK(&l3_cos->lock);
- return 0;
-}
-static int odp_pmr_create_term(pmr_term_value_t *value,
- const odp_pmr_param_t *param)
-{
- value->term = param->term;
- value->range_term = param->range_term;
- uint8_t i;
+ memset(&value->match, 0, sizeof(value->match));
+ memcpy(&value->match.value, param->match.value, param->val_sz);
+ memcpy(&value->match.mask, param->match.mask, param->val_sz);
- switch (value->term) {
- case ODP_PMR_SIP6_ADDR:
- case ODP_PMR_DIP6_ADDR:
- if (!value->range_term) {
- memset(value->match_ipv6.addr.u8, 0, 16);
- memset(value->match_ipv6.mask.u8, 0, 16);
- memcpy(&value->match_ipv6.addr.u8, param->match.value,
- param->val_sz);
- memcpy(&value->match_ipv6.mask.u8, param->match.mask,
- param->val_sz);
- for (i = 0; i < 2; i++)
- value->match_ipv6.addr.u64[i] &=
- value->match_ipv6.mask.u64[i];
- } else {
- memset(value->range_ipv6.addr_start.u8, 0, 16);
- memset(value->range_ipv6.addr_end.u8, 0, 16);
- memcpy(&value->range_ipv6.addr_start.u8, param->range.val_start,
- param->val_sz);
- memcpy(&value->range_ipv6.addr_end.u8, param->range.val_end,
- param->val_sz);
- }
+ for (i = 0; i < param->val_sz; i++)
+ value->match.value_u8[i] &= value->match.mask_u8[i];
- break;
- default:
- if (!value->range_term) {
- value->match.value = 0;
- value->match.mask = 0;
- memcpy(&value->match.value, param->match.value, param->val_sz);
- memcpy(&value->match.mask, param->match.mask, param->val_sz);
- value->match.value &= value->match.mask;
- } else {
- value->range.val_start = 0;
- value->range.val_end = 0;
- memcpy(&value->range.val_start, param->range.val_start,
- param->val_sz);
- memcpy(&value->range.val_end, param->range.val_end,
- param->val_sz);
- }
- }
value->offset = param->offset;
value->val_sz = param->val_sz;
return 0;
@@ -505,91 +696,147 @@ int odp_cls_pmr_destroy(odp_pmr_t pmr_id)
uint8_t i;
pmr = get_pmr_entry(pmr_id);
- if (pmr == NULL || pmr->s.src_cos == NULL)
+ if (pmr == NULL || pmr->src_cos == NULL)
return -1;
- src_cos = pmr->s.src_cos;
- LOCK(&src_cos->s.lock);
- loc = odp_atomic_load_u32(&src_cos->s.num_rule);
+ src_cos = pmr->src_cos;
+ LOCK(&src_cos->lock);
+ loc = odp_atomic_load_u32(&src_cos->num_rule);
if (loc == 0)
goto no_rule;
loc -= 1;
for (i = 0; i <= loc; i++)
- if (src_cos->s.pmr[i] == pmr) {
- src_cos->s.pmr[i] = src_cos->s.pmr[loc];
- src_cos->s.linked_cos[i] = src_cos->s.linked_cos[loc];
+ if (src_cos->pmr[i] == pmr) {
+ src_cos->pmr[i] = src_cos->pmr[loc];
+ src_cos->linked_cos[i] = src_cos->linked_cos[loc];
}
- odp_atomic_dec_u32(&src_cos->s.num_rule);
+ odp_atomic_dec_u32(&src_cos->num_rule);
no_rule:
- pmr->s.valid = 0;
- UNLOCK(&src_cos->s.lock);
+ pmr->valid = 0;
+ UNLOCK(&src_cos->lock);
return 0;
}
-odp_pmr_t odp_cls_pmr_create(const odp_pmr_param_t *terms, int num_terms,
- odp_cos_t src_cos, odp_cos_t dst_cos)
+int odp_cls_pmr_destroy_multi(odp_pmr_t pmr[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(pmr != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ int ret = odp_cls_pmr_destroy(pmr[i]);
+
+ if (ret)
+ return (i == 0) ? ret : i;
+ }
+
+ return i;
+}
+
+static odp_pmr_t cls_pmr_create(const odp_pmr_param_t *terms, int num_terms, uint16_t mark,
+ odp_cos_t src_cos, odp_cos_t dst_cos)
{
pmr_t *pmr;
int i;
odp_pmr_t id;
- int val_sz;
uint32_t loc;
cos_t *cos_src = get_cos_entry(src_cos);
cos_t *cos_dst = get_cos_entry(dst_cos);
if (NULL == cos_src || NULL == cos_dst) {
- ODP_ERR("Invalid input handle");
- return ODP_PMR_INVAL;
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return ODP_PMR_INVALID;
}
- if (num_terms > ODP_PMRTERM_MAX) {
- ODP_ERR("no of terms greater than supported ODP_PMRTERM_MAX");
- return ODP_PMR_INVAL;
+ if (num_terms > CLS_PMRTERM_MAX) {
+ _ODP_ERR("no of terms greater than supported CLS_PMRTERM_MAX\n");
+ return ODP_PMR_INVALID;
}
- if (ODP_PMR_PER_COS_MAX == odp_atomic_load_u32(&cos_src->s.num_rule))
- return ODP_PMR_INVAL;
+ if (CLS_PMR_PER_COS_MAX == odp_atomic_load_u32(&cos_src->num_rule))
+ return ODP_PMR_INVALID;
id = alloc_pmr(&pmr);
/*if alloc_pmr is successful it returns with the acquired lock*/
- if (id == ODP_PMR_INVAL)
+ if (id == ODP_PMR_INVALID)
return id;
- pmr->s.num_pmr = num_terms;
+ pmr->num_pmr = num_terms;
for (i = 0; i < num_terms; i++) {
- val_sz = terms[i].val_sz;
- if (val_sz > ODP_PMR_TERM_BYTES_MAX) {
- pmr->s.valid = 0;
- return ODP_PMR_INVAL;
- }
- if (0 > odp_pmr_create_term(&pmr->s.pmr_term_value[i],
- &terms[i])) {
- UNLOCK(&pmr->s.lock);
- return ODP_PMR_INVAL;
+ if (pmr_create_term(&pmr->pmr_term_value[i], &terms[i])) {
+ pmr->valid = 0;
+ UNLOCK(&pmr->lock);
+ return ODP_PMR_INVALID;
}
}
- loc = odp_atomic_fetch_inc_u32(&cos_src->s.num_rule);
- cos_src->s.pmr[loc] = pmr;
- cos_src->s.linked_cos[loc] = cos_dst;
- pmr->s.src_cos = cos_src;
+ pmr->mark = mark;
+
+ loc = odp_atomic_fetch_inc_u32(&cos_src->num_rule);
+ cos_src->pmr[loc] = pmr;
+ cos_src->linked_cos[loc] = cos_dst;
+ pmr->src_cos = cos_src;
- UNLOCK(&pmr->s.lock);
+ UNLOCK(&pmr->lock);
return id;
}
+odp_pmr_t odp_cls_pmr_create(const odp_pmr_param_t *terms, int num_terms,
+ odp_cos_t src_cos, odp_cos_t dst_cos)
+{
+ return cls_pmr_create(terms, num_terms, 0, src_cos, dst_cos);
+}
+
+odp_pmr_t odp_cls_pmr_create_opt(const odp_pmr_create_opt_t *opt,
+ odp_cos_t src_cos, odp_cos_t dst_cos)
+{
+ if (opt == NULL) {
+ _ODP_ERR("Bad parameter\n");
+ return ODP_PMR_INVALID;
+ }
+
+ if (opt->mark > MAX_MARK) {
+ _ODP_ERR("Too large mark value: %" PRIu64 "\n", opt->mark);
+ return ODP_PMR_INVALID;
+ }
+
+ return cls_pmr_create(opt->terms, opt->num_terms, opt->mark, src_cos, dst_cos);
+}
+
+int odp_cls_pmr_create_multi(const odp_pmr_create_opt_t opt[], odp_cos_t src_cos[],
+ odp_cos_t dst_cos[], odp_pmr_t pmr[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(opt != NULL);
+ _ODP_ASSERT(src_cos != NULL);
+ _ODP_ASSERT(dst_cos != NULL);
+ _ODP_ASSERT(pmr != NULL);
+
+ for (i = 0; i < num; i++) {
+ odp_pmr_t new_pmr = odp_cls_pmr_create_opt(&opt[i], src_cos[i], dst_cos[i]);
+
+ if (odp_unlikely(new_pmr == ODP_PMR_INVALID))
+ return (i == 0) ? -1 : i;
+
+ pmr[i] = new_pmr;
+ }
+ return i;
+}
+
int odp_cls_cos_pool_set(odp_cos_t cos_id, odp_pool_t pool)
{
cos_t *cos;
cos = get_cos_entry(cos_id);
if (cos == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
return -1;
}
- cos->s.pool = pool;
+ cos->pool = pool;
return 0;
}
@@ -600,35 +847,470 @@ odp_pool_t odp_cls_cos_pool(odp_cos_t cos_id)
cos = get_cos_entry(cos_id);
if (cos == NULL) {
- ODP_ERR("Invalid odp_cos_t handle");
+ _ODP_ERR("Invalid odp_cos_t handle\n");
return ODP_POOL_INVALID;
}
- return cos->s.pool;
+ return cos->pool;
+}
+
+static inline int verify_pmr_packet_len(odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ if (term_value->match.value == (packet_len(pkt_hdr) &
+ term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv4_proto(const _odp_ipv4hdr_t *ipv4, pmr_term_value_t *term_value)
+{
+ uint8_t proto;
+
+ proto = ipv4->proto;
+ if (term_value->match.value == (proto & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv6_next_hdr(const _odp_ipv6hdr_t *ipv6, pmr_term_value_t *term_value)
+{
+ uint8_t next_hdr;
+
+ next_hdr = ipv6->next_hdr;
+ if (term_value->match.value == (next_hdr & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv4_dscp(const _odp_ipv4hdr_t *ipv4, pmr_term_value_t *term_value)
+{
+ uint8_t dscp;
+
+ dscp = _ODP_IPV4HDR_DSCP(ipv4->tos);
+ if (term_value->match.value == (dscp & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv6_dscp(const _odp_ipv6hdr_t *ipv6, pmr_term_value_t *term_value)
+{
+ uint8_t dscp;
+
+ dscp = _ODP_IPV6HDR_DSCP(odp_be_to_cpu_32(ipv6->ver_tc_flow));
+ if (term_value->match.value == (dscp & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv4_saddr(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ipv4hdr_t *ip;
+ uint32_t ipaddr;
+
+ if (!pkt_hdr->p.input_flags.ipv4)
+ return 0;
+ ip = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
+ ipaddr = ip->src_addr;
+ if (term_value->match.value == (ipaddr & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv4_daddr(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ipv4hdr_t *ip;
+ uint32_t ipaddr;
+
+ if (!pkt_hdr->p.input_flags.ipv4)
+ return 0;
+ ip = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
+ ipaddr = ip->dst_addr;
+ if (term_value->match.value == (ipaddr & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_tcp_sport(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint16_t sport;
+ const _odp_tcphdr_t *tcp;
+
+ if (!pkt_hdr->p.input_flags.tcp)
+ return 0;
+ tcp = (const _odp_tcphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
+ sport = tcp->src_port;
+ if (term_value->match.value == (sport & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_tcp_dport(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint16_t dport;
+ const _odp_tcphdr_t *tcp;
+
+ if (!pkt_hdr->p.input_flags.tcp)
+ return 0;
+ tcp = (const _odp_tcphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
+ dport = tcp->dst_port;
+ if (term_value->match.value == (dport & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_udp_dport(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint16_t dport;
+ const _odp_udphdr_t *udp;
+
+ if (!pkt_hdr->p.input_flags.udp)
+ return 0;
+ udp = (const _odp_udphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
+ dport = udp->dst_port;
+ if (term_value->match.value == (dport & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_udp_sport(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint16_t sport;
+ const _odp_udphdr_t *udp;
+
+ if (!pkt_hdr->p.input_flags.udp)
+ return 0;
+ udp = (const _odp_udphdr_t *)(pkt_addr + pkt_hdr->p.l4_offset);
+ sport = udp->src_port;
+ if (term_value->match.value == (sport & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_dmac(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ uint16_t dmac[3];
+ uint16_t *mask = (uint16_t *)&term_value->match.mask;
+ uint16_t *value = (uint16_t *)&term_value->match.value;
+
+ if (!packet_hdr_has_eth(pkt_hdr))
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ memcpy(dmac, eth->dst.addr, _ODP_ETHADDR_LEN);
+ dmac[0] &= mask[0];
+ dmac[1] &= mask[1];
+ dmac[2] &= mask[2];
+
+ if (value[0] == dmac[0] && value[1] == dmac[1] && value[2] == dmac[2])
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv6_saddr(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ipv6hdr_t *ipv6;
+ uint64_t addr[2];
+
+ if (!packet_hdr_has_ipv6(pkt_hdr))
+ return 0;
+
+ ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
+ memcpy(addr, ipv6->src_addr.u64, _ODP_IPV6ADDR_LEN);
+
+ addr[0] = addr[0] & term_value->match.mask_u64[0];
+ addr[1] = addr[1] & term_value->match.mask_u64[1];
+
+ if (addr[0] == term_value->match.value_u64[0] &&
+ addr[1] == term_value->match.value_u64[1])
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipv6_daddr(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ipv6hdr_t *ipv6;
+ uint64_t addr[2];
+
+ if (!packet_hdr_has_ipv6(pkt_hdr))
+ return 0;
+
+ ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
+ memcpy(addr, ipv6->dst_addr.u64, _ODP_IPV6ADDR_LEN);
+
+ addr[0] = addr[0] & term_value->match.mask_u64[0];
+ addr[1] = addr[1] & term_value->match.mask_u64[1];
+
+ if (addr[0] == term_value->match.value_u64[0] &&
+ addr[1] == term_value->match.value_u64[1])
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_vlan_id_0(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ const _odp_vlanhdr_t *vlan;
+ uint16_t tci;
+ uint16_t vlan_id;
+
+ if (!packet_hdr_has_eth(pkt_hdr) || !pkt_hdr->p.input_flags.vlan)
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ vlan = (const _odp_vlanhdr_t *)(eth + 1);
+ tci = vlan->tci;
+ vlan_id = tci & odp_cpu_to_be_16(0x0fff);
+
+ if (term_value->match.value == (vlan_id & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_vlan_id_x(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ const _odp_vlanhdr_t *vlan;
+ uint16_t tci;
+ uint16_t vlan_id;
+
+ if (!pkt_hdr->p.input_flags.vlan && !pkt_hdr->p.input_flags.vlan_qinq)
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ vlan = (const _odp_vlanhdr_t *)(eth + 1);
+
+ if (pkt_hdr->p.input_flags.vlan_qinq)
+ vlan++;
+
+ tci = vlan->tci;
+ vlan_id = tci & odp_cpu_to_be_16(0x0fff);
+
+ if (term_value->match.value == (vlan_id & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_vlan_pcp_0(const uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ const _odp_vlanhdr_t *vlan;
+ uint16_t tci;
+ uint8_t pcp;
+
+ if (!packet_hdr_has_eth(pkt_hdr) || !pkt_hdr->p.input_flags.vlan)
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ vlan = (const _odp_vlanhdr_t *)(eth + 1);
+ tci = odp_be_to_cpu_16(vlan->tci);
+ pcp = tci >> _ODP_VLANHDR_PCP_SHIFT;
+
+ if (term_value->match.value == (pcp & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ipsec_spi(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint32_t spi;
+
+ pkt_addr += pkt_hdr->p.l4_offset;
+
+ if (pkt_hdr->p.input_flags.ipsec_ah)
+ spi = ((const _odp_ahhdr_t *)pkt_addr)->spi;
+ else if (pkt_hdr->p.input_flags.ipsec_esp)
+ spi = ((const _odp_esphdr_t *)pkt_addr)->spi;
+ else
+ return 0;
+
+ if (term_value->match.value == (spi & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_ld_vni(const uint8_t *pkt_addr ODP_UNUSED,
+ odp_packet_hdr_t *pkt_hdr ODP_UNUSED,
+ pmr_term_value_t *term_value ODP_UNUSED)
+{
+ ODP_UNIMPLEMENTED();
+ return 0;
+}
+
+static inline int verify_pmr_custom_frame(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint32_t i;
+ uint8_t val;
+ uint32_t offset = term_value->offset;
+ uint32_t val_sz = term_value->val_sz;
+
+ _ODP_ASSERT(val_sz <= MAX_PMR_TERM_SIZE);
+
+ if (packet_len(pkt_hdr) <= offset + val_sz)
+ return 0;
+
+ pkt_addr += offset;
+
+ for (i = 0; i < val_sz; i++) {
+ val = pkt_addr[i] & term_value->match.mask_u8[i];
+
+ if (val != term_value->match.value_u8[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int verify_pmr_custom_l3(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ uint32_t i;
+ uint8_t val;
+ uint32_t l3_offset = pkt_hdr->p.l3_offset;
+ uint32_t offset = l3_offset + term_value->offset;
+ uint32_t val_sz = term_value->val_sz;
+
+ _ODP_ASSERT(val_sz <= MAX_PMR_TERM_SIZE);
+
+ if (pkt_hdr->p.input_flags.l2 == 0 ||
+ l3_offset == ODP_PACKET_OFFSET_INVALID)
+ return 0;
+
+ if (packet_len(pkt_hdr) <= offset + val_sz)
+ return 0;
+
+ pkt_addr += offset;
+
+ for (i = 0; i < val_sz; i++) {
+ val = pkt_addr[i] & term_value->match.mask_u8[i];
+
+ if (val != term_value->match.value_u8[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int verify_pmr_eth_type_0(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ uint16_t ethtype;
+
+ if (!packet_hdr_has_eth(pkt_hdr))
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ ethtype = eth->type;
+
+ if (term_value->match.value == (ethtype & term_value->match.mask))
+ return 1;
+
+ return 0;
+}
+
+static inline int verify_pmr_eth_type_x(const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr,
+ pmr_term_value_t *term_value)
+{
+ const _odp_ethhdr_t *eth;
+ uint16_t ethtype;
+ const _odp_vlanhdr_t *vlan;
+
+ if (!pkt_hdr->p.input_flags.vlan && !pkt_hdr->p.input_flags.vlan_qinq)
+ return 0;
+
+ eth = (const _odp_ethhdr_t *)(pkt_addr + pkt_hdr->p.l2_offset);
+ vlan = (const _odp_vlanhdr_t *)(eth + 1);
+
+ if (pkt_hdr->p.input_flags.vlan_qinq)
+ vlan++;
+
+ ethtype = vlan->type;
+
+ if (term_value->match.value == (ethtype & term_value->match.mask))
+ return 1;
+
+ return 0;
}
/*
* This function goes through each PMR_TERM value in pmr_t structure and calls
* verification function for each term.Returns 1 if PMR matches or 0 otherwise.
*/
-static
-int verify_pmr(pmr_t *pmr, const uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr)
+static int verify_pmr(pmr_t *pmr, const uint8_t *pkt_addr,
+ odp_packet_hdr_t *pkt_hdr)
{
int pmr_failure = 0;
int num_pmr;
int i;
pmr_term_value_t *term_value;
+ const _odp_ipv4hdr_t *ipv4 = NULL;
+ const _odp_ipv6hdr_t *ipv6 = NULL;
/* Locking is not required as PMR rules for in-flight packets
delivery during a PMR change is indeterminate*/
- if (!pmr->s.valid)
+ if (!pmr->valid)
return 0;
- num_pmr = pmr->s.num_pmr;
+ num_pmr = pmr->num_pmr;
+
+ if (pkt_hdr->p.input_flags.ipv4)
+ ipv4 = (const _odp_ipv4hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
+ if (pkt_hdr->p.input_flags.ipv6)
+ ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + pkt_hdr->p.l3_offset);
/* Iterate through list of PMR Term values in a pmr_t */
for (i = 0; i < num_pmr; i++) {
- term_value = &pmr->s.pmr_term_value[i];
+ term_value = &pmr->pmr_term_value[i];
switch (term_value->term) {
case ODP_PMR_LEN:
if (!verify_pmr_packet_len(pkt_hdr, term_value))
@@ -654,15 +1336,36 @@ int verify_pmr(pmr_t *pmr, const uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr)
term_value))
pmr_failure = 1;
break;
+ case ODP_PMR_VLAN_PCP_0:
+ if (!verify_pmr_vlan_pcp_0(pkt_addr, pkt_hdr, term_value))
+ pmr_failure = 1;
+ break;
case ODP_PMR_DMAC:
if (!verify_pmr_dmac(pkt_addr, pkt_hdr,
term_value))
pmr_failure = 1;
break;
case ODP_PMR_IPPROTO:
- if (!verify_pmr_ip_proto(pkt_addr, pkt_hdr,
- term_value))
+ if (ipv4) {
+ if (!verify_pmr_ipv4_proto(ipv4, term_value))
+ pmr_failure = 1;
+ } else if (ipv6) {
+ if (!verify_pmr_ipv6_next_hdr(ipv6, term_value))
+ pmr_failure = 1;
+ } else {
pmr_failure = 1;
+ }
+ break;
+ case ODP_PMR_IP_DSCP:
+ if (ipv4) {
+ if (!verify_pmr_ipv4_dscp(ipv4, term_value))
+ pmr_failure = 1;
+ } else if (ipv6) {
+ if (!verify_pmr_ipv6_dscp(ipv6, term_value))
+ pmr_failure = 1;
+ } else {
+ pmr_failure = 1;
+ }
break;
case ODP_PMR_UDP_DPORT:
if (!verify_pmr_udp_dport(pkt_addr, pkt_hdr,
@@ -719,61 +1422,184 @@ int verify_pmr(pmr_t *pmr, const uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr)
term_value))
pmr_failure = 1;
break;
+ case ODP_PMR_CUSTOM_L3:
+ if (!verify_pmr_custom_l3(pkt_addr, pkt_hdr,
+ term_value))
+ pmr_failure = 1;
+ break;
case ODP_PMR_INNER_HDR_OFF:
break;
+ default:
+ pmr_failure = 1;
+ break;
}
if (pmr_failure)
- return false;
+ return 0;
+ }
+ return 1;
+}
+
+static const char *format_pmr_name(odp_cls_pmr_term_t pmr_term)
+{
+ const char *name;
+
+ switch (pmr_term) {
+ case ODP_PMR_LEN:
+ name = "PMR_LEN";
+ break;
+ case ODP_PMR_ETHTYPE_0:
+ name = "PMR_ETHTYPE_0";
+ break;
+ case ODP_PMR_ETHTYPE_X:
+ name = "PMR_ETHTYPE_X";
+ break;
+ case ODP_PMR_VLAN_ID_0:
+ name = "PMR_VLAN_ID_0";
+ break;
+ case ODP_PMR_VLAN_ID_X:
+ name = "PMR_VLAN_ID_X";
+ break;
+ case ODP_PMR_VLAN_PCP_0:
+ name = "PMR_VLAN_PCP_0";
+ break;
+ case ODP_PMR_DMAC:
+ name = "PMR_DMAC";
+ break;
+ case ODP_PMR_IPPROTO:
+ name = "PMR_IPPROTO";
+ break;
+ case ODP_PMR_IP_DSCP:
+ name = "PMR_IP_DSCP";
+ break;
+ case ODP_PMR_UDP_DPORT:
+ name = "PMR_UDP_DPORT";
+ break;
+ case ODP_PMR_TCP_DPORT:
+ name = "PMR_TCP_DPORT";
+ break;
+ case ODP_PMR_UDP_SPORT:
+ name = "PMR_UDP_SPORT";
+ break;
+ case ODP_PMR_TCP_SPORT:
+ name = "PMR_TCP_SPORT";
+ break;
+ case ODP_PMR_SIP_ADDR:
+ name = "PMR_SIP_ADDR";
+ break;
+ case ODP_PMR_DIP_ADDR:
+ name = "PMR_DIP_ADDR";
+ break;
+ case ODP_PMR_SIP6_ADDR:
+ name = "PMR_SIP6_ADDR";
+ break;
+ case ODP_PMR_DIP6_ADDR:
+ name = "PMR_DIP6_ADDR";
+ break;
+ case ODP_PMR_IPSEC_SPI:
+ name = "PMR_IPSEC_SPI";
+ break;
+ case ODP_PMR_LD_VNI:
+ name = "PMR_LD_VNI";
+ break;
+ case ODP_PMR_CUSTOM_FRAME:
+ name = "PMR_CUSTOM_FRAME";
+ break;
+ case ODP_PMR_CUSTOM_L3:
+ name = "PMR_CUSTOM_L3";
+ break;
+ default:
+ name = "unknown";
+ break;
+ }
+
+ return name;
+}
+
+static inline void pmr_debug_print(pmr_t *pmr, cos_t *cos)
+{
+ uint32_t i;
+ const char *pmr_name;
+ const char *cos_name = cos->name;
+ uint32_t cos_index = cos->index;
+ uint32_t num_pmr = pmr->num_pmr;
+
+ if (ODP_DEBUG_PRINT == 0)
+ return;
+
+ if (num_pmr == 1) {
+ pmr_name = format_pmr_name(pmr->pmr_term_value[0].term);
+ ODP_DBG_RAW(CLS_DBG, " PMR matched: %s -> cos: %s(%u)\n", pmr_name, cos_name,
+ cos_index);
+ return;
+ }
+
+ ODP_DBG_RAW(CLS_DBG, " PMRs matched:");
+ for (i = 0; i < num_pmr; i++) {
+ pmr_name = format_pmr_name(pmr->pmr_term_value[i].term);
+ ODP_DBG_RAW(CLS_DBG, " %s", pmr_name);
}
- odp_atomic_inc_u32(&pmr->s.count);
- return true;
+
+ ODP_DBG_RAW(CLS_DBG, " -> cos: %s(%u)\n", cos_name, cos_index);
}
/*
* Match a PMR chain with a Packet and return matching CoS
- * This function gets called recursively to check the chained PMR Term value
- * with the packet.
+ * This function performs a depth-first search in the CoS tree.
*/
-static
-cos_t *match_pmr_cos(cos_t *cos, const uint8_t *pkt_addr, pmr_t *pmr,
- odp_packet_hdr_t *hdr)
+static cos_t *match_pmr_cos(cos_t *cos, const uint8_t *pkt_addr, odp_packet_hdr_t *hdr)
{
- cos_t *retcos;
- uint32_t i;
+ pmr_t *pmr_match = NULL;
- retcos = NULL;
+ while (1) {
+ uint32_t i, num_rule = odp_atomic_load_u32(&cos->num_rule);
- if (cos == NULL || pmr == NULL)
- return NULL;
+ for (i = 0; i < num_rule; i++) {
+ pmr_t *pmr = cos->pmr[i];
+ struct cos_s *linked_cos = cos->linked_cos[i];
- if (!cos->s.valid)
- return NULL;
+ if (odp_unlikely(!linked_cos->valid))
+ continue;
- if (verify_pmr(pmr, pkt_addr, hdr)) {
- /** This gets called recursively to check all the PMRs in
- * a PMR chain */
- if (0 == odp_atomic_load_u32(&cos->s.num_rule))
- return cos;
+ if (verify_pmr(pmr, pkt_addr, hdr)) {
+ /* PMR matched */
+
+ pmr_match = pmr;
+ cos = linked_cos;
+
+ pmr_debug_print(pmr, cos);
+
+ if (cos->stats_enable)
+ odp_atomic_inc_u64(&cos->stats.packets);
- for (i = 0; i < odp_atomic_load_u32(&cos->s.num_rule); i++) {
- retcos = match_pmr_cos(cos->s.linked_cos[i], pkt_addr,
- cos->s.pmr[i], hdr);
- if (!retcos)
- return cos;
+ break;
+ }
+ }
+
+ /* If no PMR matched, the current CoS is the best match. */
+ if (i == num_rule)
+ break;
+ }
+
+ if (pmr_match) {
+ hdr->p.input_flags.cls_mark = 0;
+ if (pmr_match->mark) {
+ hdr->p.input_flags.cls_mark = 1;
+ hdr->cls_mark = pmr_match->mark;
}
}
- return retcos;
+
+ return cos;
}
-int pktio_classifier_init(pktio_entry_t *entry)
+int _odp_pktio_classifier_init(pktio_entry_t *entry)
{
classifier_t *cls;
/* classifier lock should be acquired by the calling function */
if (entry == NULL)
return -1;
- cls = &entry->s.cls;
+ cls = &entry->cls;
cls->error_cos = NULL;
cls->default_cos = NULL;
cls->headroom = 0;
@@ -782,17 +1608,11 @@ int pktio_classifier_init(pktio_entry_t *entry)
return 0;
}
-static
-cos_t *match_qos_cos(pktio_entry_t *entry, const uint8_t *pkt_addr,
- odp_packet_hdr_t *hdr);
-
/**
Select a CoS for the given Packet based on pktio
This function will call all the PMRs associated with a pktio for
a given packet and will return the matched COS object.
-This function will check PMR, L2 and L3 QoS COS object associated
-with the PKTIO interface.
Returns the default cos if the packet does not match any PMR
Returns the error_cos if the packet has an error
@@ -801,34 +1621,40 @@ static inline cos_t *cls_select_cos(pktio_entry_t *entry,
const uint8_t *pkt_addr,
odp_packet_hdr_t *pkt_hdr)
{
- pmr_t *pmr;
cos_t *cos;
cos_t *default_cos;
- uint32_t i;
classifier_t *cls;
- cls = &entry->s.cls;
+ cls = &entry->cls;
default_cos = cls->default_cos;
/* Return error cos for error packet */
- if (pkt_hdr->p.error_flags.all)
- return cls->error_cos;
+ if (pkt_hdr->p.flags.all.error) {
+ cos = cls->error_cos;
+ goto done;
+ }
+
/* Calls all the PMRs attached at the PKTIO level*/
- for (i = 0; i < odp_atomic_load_u32(&default_cos->s.num_rule); i++) {
- pmr = default_cos->s.pmr[i];
- cos = default_cos->s.linked_cos[i];
- cos = match_pmr_cos(cos, pkt_addr, pmr, pkt_hdr);
- if (cos)
+ if (default_cos && default_cos->valid) {
+ cos = match_pmr_cos(default_cos, pkt_addr, pkt_hdr);
+ if (cos && cos != default_cos)
return cos;
}
- cos = match_qos_cos(entry, pkt_addr, pkt_hdr);
- if (cos)
- return cos;
+ ODP_DBG_RAW(CLS_DBG, " No match -> default cos\n");
+ cos = cls->default_cos;
+
+done:
+ if (cos && cos->stats_enable)
+ odp_atomic_inc_u64(&cos->stats.packets);
- return cls->default_cos;
+ return cos;
}
+static uint32_t packet_rss_hash(odp_packet_hdr_t *pkt_hdr,
+ odp_cls_hash_proto_t hash_proto,
+ const uint8_t *base);
+
/**
* Classify packet
*
@@ -839,121 +1665,276 @@ static inline cos_t *cls_select_cos(pktio_entry_t *entry,
* @param pool[out] Packet pool
* @param pkt_hdr[out] Packet header
*
- * @retval 0 on success
- * @retval -EFAULT Bug
- * @retval -EINVAL Config error
+ * @retval 0 success
+ * @retval -1 drop packet and increment in_discards
+ * @retval 1 drop packet
*
* @note *base is not released
*/
-int cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,
- uint16_t pkt_len, uint32_t seg_len, odp_pool_t *pool,
- odp_packet_hdr_t *pkt_hdr)
+int _odp_cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,
+ odp_pool_t *pool, odp_packet_hdr_t *pkt_hdr)
{
cos_t *cos;
+ uint32_t tbl_index;
+ uint32_t hash;
- packet_parse_reset(pkt_hdr);
- packet_set_len(pkt_hdr, pkt_len);
+ ODP_DBG_LVL(CLS_DBG, "Classify packet from %s\n", entry->full_name);
- packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len,
- ODP_PKTIO_PARSER_LAYER_ALL);
cos = cls_select_cos(entry, base, pkt_hdr);
if (cos == NULL)
- return -EINVAL;
+ return -1;
+
+ if (cos->action == ODP_COS_ACTION_DROP)
+ return 1;
- if (cos->s.queue == QUEUE_NULL || cos->s.pool == ODP_POOL_INVALID)
- return -EFAULT;
+ *pool = cos->pool;
+ if (*pool == ODP_POOL_INVALID)
+ *pool = entry->pool;
- *pool = cos->s.pool;
pkt_hdr->p.input_flags.dst_queue = 1;
- pkt_hdr->dst_queue = cos->s.queue;
+ pkt_hdr->cos = cos->index;
+
+ if (!cos->queue_group) {
+ pkt_hdr->dst_queue = cos->queue;
+ return 0;
+ }
+ hash = packet_rss_hash(pkt_hdr, cos->hash_proto, base);
+ /* CLS_COS_QUEUE_MAX is a power of 2 */
+ hash = hash & (CLS_COS_QUEUE_MAX - 1);
+ tbl_index = (cos->index * CLS_COS_QUEUE_MAX) + (hash %
+ cos->num_queue);
+ pkt_hdr->dst_queue = queue_grp_tbl->queue[tbl_index];
return 0;
}
-static
-cos_t *match_qos_l3_cos(pmr_l3_cos_t *l3_cos, const uint8_t *pkt_addr,
- odp_packet_hdr_t *hdr)
+static uint32_t packet_rss_hash(odp_packet_hdr_t *pkt_hdr,
+ odp_cls_hash_proto_t hash_proto,
+ const uint8_t *base)
{
- uint8_t dscp;
- cos_t *cos = NULL;
+ thash_tuple_t tuple;
const _odp_ipv4hdr_t *ipv4;
+ const _odp_udphdr_t *udp;
+ const _odp_tcphdr_t *tcp;
const _odp_ipv6hdr_t *ipv6;
+ uint32_t hash;
+ uint32_t tuple_len;
+
+ tuple_len = 0;
+ hash = 0;
+ if (pkt_hdr->p.input_flags.ipv4) {
+ if (hash_proto.ipv4) {
+ /* add ipv4 */
+ ipv4 = (const _odp_ipv4hdr_t *)(base +
+ pkt_hdr->p.l3_offset);
+ tuple.v4.src_addr = ipv4->src_addr;
+ tuple.v4.dst_addr = ipv4->dst_addr;
+ tuple_len += 2;
+ }
- if (hdr->p.input_flags.l3 && hdr->p.input_flags.ipv4) {
- ipv4 = (const _odp_ipv4hdr_t *)(pkt_addr + hdr->p.l3_offset);
- dscp = _ODP_IPV4HDR_DSCP(ipv4->tos);
- cos = l3_cos->cos[dscp];
- } else if (hdr->p.input_flags.l3 && hdr->p.input_flags.ipv6) {
- ipv6 = (const _odp_ipv6hdr_t *)(pkt_addr + hdr->p.l3_offset);
- dscp = _ODP_IPV6HDR_DSCP(ipv6->ver_tc_flow);
- cos = l3_cos->cos[dscp];
+ if (pkt_hdr->p.input_flags.tcp && hash_proto.tcp) {
+ /* add tcp */
+ tcp = (const _odp_tcphdr_t *)(base +
+ pkt_hdr->p.l4_offset);
+ tuple.v4.sport = tcp->src_port;
+ tuple.v4.dport = tcp->dst_port;
+ tuple_len += 1;
+ } else if (pkt_hdr->p.input_flags.udp && hash_proto.udp) {
+ /* add udp */
+ udp = (const _odp_udphdr_t *)(base +
+ pkt_hdr->p.l4_offset);
+ tuple.v4.sport = udp->src_port;
+ tuple.v4.dport = udp->dst_port;
+ tuple_len += 1;
+ }
+ } else if (pkt_hdr->p.input_flags.ipv6) {
+ if (hash_proto.ipv6) {
+ /* add ipv6 */
+ ipv6 = (const _odp_ipv6hdr_t *)(base +
+ pkt_hdr->p.l3_offset);
+ thash_load_ipv6_addr(ipv6, &tuple);
+ tuple_len += 8;
+ }
+ if (pkt_hdr->p.input_flags.tcp && hash_proto.tcp) {
+ tcp = (const _odp_tcphdr_t *)(base +
+ pkt_hdr->p.l4_offset);
+ tuple.v6.sport = tcp->src_port;
+ tuple.v6.dport = tcp->dst_port;
+ tuple_len += 1;
+ } else if (pkt_hdr->p.input_flags.udp && hash_proto.udp) {
+ /* add udp */
+ udp = (const _odp_udphdr_t *)(base +
+ pkt_hdr->p.l4_offset);
+ tuple.v6.sport = udp->src_port;
+ tuple.v6.dport = udp->dst_port;
+ tuple_len += 1;
+ }
}
+ if (tuple_len)
+ hash = thash_softrss((uint32_t *)&tuple,
+ tuple_len, default_rss);
+ return hash;
+}
- return cos;
+uint64_t odp_cos_to_u64(odp_cos_t hdl)
+{
+ return _odp_pri(hdl);
}
-static
-cos_t *match_qos_l2_cos(pmr_l2_cos_t *l2_cos, const uint8_t *pkt_addr,
- odp_packet_hdr_t *hdr)
+uint64_t odp_pmr_to_u64(odp_pmr_t hdl)
{
- cos_t *cos = NULL;
- const _odp_ethhdr_t *eth;
- const _odp_vlanhdr_t *vlan;
- uint16_t qos;
+ return _odp_pri(hdl);
+}
+
+int odp_cls_cos_stats(odp_cos_t hdl, odp_cls_cos_stats_t *stats)
+{
+ cos_t *cos = get_cos_entry(hdl);
- if (packet_hdr_has_l2(hdr) && hdr->p.input_flags.vlan &&
- packet_hdr_has_eth(hdr)) {
- eth = (const _odp_ethhdr_t *)(pkt_addr + hdr->p.l2_offset);
- vlan = (const _odp_vlanhdr_t *)(eth + 1);
- qos = odp_be_to_cpu_16(vlan->tci);
- qos = ((qos >> 13) & 0x07);
- cos = l2_cos->cos[qos];
+ if (odp_unlikely(cos == NULL)) {
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return -1;
}
- return cos;
+
+ if (odp_unlikely(stats == NULL)) {
+ _ODP_ERR("Output structure NULL\n");
+ return -1;
+ }
+
+ memset(stats, 0, sizeof(*stats));
+ stats->discards = odp_atomic_load_u64(&cos->stats.discards);
+ stats->packets = odp_atomic_load_u64(&cos->stats.packets);
+
+ return 0;
}
-/*
- * Select a CoS for the given Packet based on QoS values
- * This function returns the COS object matching the L2 and L3 QoS
- * based on the l3_preference value of the pktio
-*/
-static
-cos_t *match_qos_cos(pktio_entry_t *entry, const uint8_t *pkt_addr,
- odp_packet_hdr_t *hdr)
+int odp_cls_queue_stats(odp_cos_t hdl, odp_queue_t queue,
+ odp_cls_queue_stats_t *stats)
{
- classifier_t *cls = &entry->s.cls;
- pmr_l2_cos_t *l2_cos;
- pmr_l3_cos_t *l3_cos;
- cos_t *cos;
+ cos_t *cos = get_cos_entry(hdl);
+ int queue_idx;
- l2_cos = &cls->l2_cos_table;
- l3_cos = &cls->l3_cos_table;
+ if (odp_unlikely(cos == NULL)) {
+ _ODP_ERR("Invalid odp_cos_t handle\n");
+ return -1;
+ }
- if (cls->l3_precedence) {
- cos = match_qos_l3_cos(l3_cos, pkt_addr, hdr);
- if (cos)
- return cos;
- cos = match_qos_l2_cos(l2_cos, pkt_addr, hdr);
- if (cos)
- return cos;
- } else {
- cos = match_qos_l2_cos(l2_cos, pkt_addr, hdr);
- if (cos)
- return cos;
- cos = match_qos_l3_cos(l3_cos, pkt_addr, hdr);
- if (cos)
- return cos;
+ if (odp_unlikely(stats == NULL)) {
+ _ODP_ERR("Output structure NULL\n");
+ return -1;
}
- return NULL;
+
+ queue_idx = _odp_cos_queue_idx(cos, queue);
+ if (odp_unlikely(queue_idx < 0)) {
+ _ODP_ERR("Invalid odp_queue_t handle\n");
+ return -1;
+ }
+
+ memset(stats, 0, sizeof(odp_cls_queue_stats_t));
+ stats->discards = odp_atomic_load_u64(&cos->queue_stats[queue_idx].discards);
+ stats->packets = odp_atomic_load_u64(&cos->queue_stats[queue_idx].packets);
+
+ return 0;
}
-uint64_t odp_cos_to_u64(odp_cos_t hdl)
+static
+void print_cos_ident(cos_t *cos)
{
- return _odp_pri(hdl);
+ if (strlen(cos->name))
+ _ODP_PRINT("%s", cos->name);
+
+ _ODP_PRINT("(%" PRIu64 ")\n", odp_cos_to_u64(_odp_cos_from_ndx(cos->index)));
}
-uint64_t odp_pmr_to_u64(odp_pmr_t hdl)
+static
+void print_queue_ident(odp_queue_t q)
{
- return _odp_pri(hdl);
+ odp_queue_info_t info;
+
+ if (!odp_queue_info(q, &info) && strlen(info.name))
+ _ODP_PRINT(" %s\n", info.name);
+ else
+ _ODP_PRINT(" %" PRIx64 "\n", odp_queue_to_u64(q));
+}
+
+static
+void print_hex(const void *vp, int len)
+{
+ const uint8_t *p = vp;
+
+ for (int i = 0; i < len; i++)
+ _ODP_PRINT("%02x", *p++);
+}
+
+static
+void cls_print_cos(cos_t *cos)
+{
+ uint32_t tbl_index = cos->index * CLS_COS_QUEUE_MAX;
+ uint32_t num_rule = odp_atomic_load_u32(&cos->num_rule);
+ bool first = true;
+
+ _ODP_PRINT("cos: ");
+ print_cos_ident(cos);
+ _ODP_PRINT(" queues:\n");
+
+ if (!cos->queue_group) {
+ print_queue_ident(cos->queue);
+ } else {
+ for (uint32_t i = 0; i < cos->num_queue; i++)
+ print_queue_ident(queue_grp_tbl->queue[tbl_index + i]);
+ }
+
+ for (uint32_t j = 0; j < num_rule; j++) {
+ pmr_t *pmr = cos->pmr[j];
+
+ LOCK(&pmr->lock);
+ for (uint32_t k = 0; k < pmr->num_pmr; k++) {
+ pmr_term_value_t *v = &pmr->pmr_term_value[k];
+
+ if (first)
+ _ODP_PRINT(" rules: ");
+ else
+ _ODP_PRINT(" ");
+
+ first = false;
+
+ _ODP_PRINT("%s: ", format_pmr_name(v->term));
+
+ if (v->term == ODP_PMR_CUSTOM_FRAME ||
+ v->term == ODP_PMR_CUSTOM_L3)
+ _ODP_PRINT("offset:%" PRIu32 " ", v->offset);
+
+ if (v->range_term) {
+ _ODP_PRINT("<range>");
+ } else {
+ print_hex(v->match.value_u8, v->val_sz);
+ _ODP_PRINT(" ");
+ print_hex(v->match.mask_u8, v->val_sz);
+ }
+
+ _ODP_PRINT(" -> ");
+
+ if (pmr->mark)
+ _ODP_PRINT("mark:%" PRIu16 " ", pmr->mark);
+
+ print_cos_ident(cos->linked_cos[j]);
+ }
+ UNLOCK(&pmr->lock);
+ }
+}
+
+void odp_cls_print_all(void)
+{
+ _ODP_PRINT("\n"
+ "Classifier info\n"
+ "---------------\n\n");
+
+ for (uint32_t i = 0; i < CLS_COS_MAX_ENTRY; i++) {
+ cos_t *cos = &cos_tbl->cos_entry[i];
+
+ LOCK(&cos->lock);
+ if (cos->valid)
+ cls_print_cos(cos);
+ UNLOCK(&cos->lock);
+ }
}
diff --git a/platform/linux-generic/odp_comp.c b/platform/linux-generic/odp_comp.c
new file mode 100644
index 000000000..21403bda3
--- /dev/null
+++ b/platform/linux-generic/odp_comp.c
@@ -0,0 +1,678 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <string.h>
+
+#include <odp/api/comp.h>
+#include <odp/api/event.h>
+#include <odp/api/packet.h>
+#include <odp/api/queue.h>
+
+#include <odp/api/plat/strong_types.h>
+
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_packet_internal.h>
+
+#include "miniz/miniz.h"
+
+#define MAX_SESSIONS 16
+#define MEM_LEVEL 8
+
+/** Forward declaration of session structure */
+typedef struct odp_comp_generic_session odp_comp_generic_session_t;
+
+#define to_gen_session(s) ((odp_comp_generic_session_t *)(intptr_t)(s))
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef
+int (*comp_func_t)(odp_packet_t pkt_in,
+ odp_packet_t pkt_out,
+ const odp_comp_packet_op_param_t *params,
+ odp_comp_generic_session_t *session);
+
+/**
+ * Per session data structure
+ */
+struct odp_comp_generic_session {
+ struct odp_comp_generic_session *next;
+ odp_comp_session_param_t params;
+ struct {
+ comp_func_t func;
+ mz_stream stream;
+ union {
+ tdefl_compressor comp;
+ inflate_state inflate;
+ } data;
+ } comp;
+};
+
+typedef struct odp_comp_global_s {
+ odp_spinlock_t lock;
+ odp_shm_t global_shm;
+ odp_comp_generic_session_t *free;
+ odp_comp_generic_session_t sessions[MAX_SESSIONS];
+} odp_comp_global_t;
+
+static odp_comp_global_t *global;
+
+static
+odp_comp_generic_session_t *alloc_session(void)
+{
+ odp_comp_generic_session_t *session = NULL;
+
+ odp_spinlock_lock(&global->lock);
+ session = global->free;
+ if (session) {
+ global->free = session->next;
+ session->next = NULL;
+ }
+ odp_spinlock_unlock(&global->lock);
+
+ return session;
+}
+
+static
+void free_session(odp_comp_generic_session_t *session)
+{
+ odp_spinlock_lock(&global->lock);
+ session->next = global->free;
+ global->free = session;
+ odp_spinlock_unlock(&global->lock);
+}
+
+static int
+null_comp_routine(odp_packet_t pkt_in ODP_UNUSED,
+ odp_packet_t pkt_out ODP_UNUSED,
+ const odp_comp_packet_op_param_t *params ODP_UNUSED,
+ odp_comp_generic_session_t *session ODP_UNUSED)
+{
+ return 0;
+}
+
+static
+odp_comp_packet_result_t *get_op_result_from_packet(odp_packet_t pkt)
+{
+ return &(packet_hdr(pkt)->comp_op_result);
+}
+
+void odp_comp_session_param_init(odp_comp_session_param_t *param)
+{
+ memset(param, 0, sizeof(odp_comp_session_param_t));
+}
+
+static void process_input(odp_packet_t pkt_out,
+ const odp_comp_packet_op_param_t *params,
+ odp_comp_generic_session_t *session,
+ odp_comp_packet_result_t *result,
+ odp_bool_t sync)
+{
+ mz_streamp streamp = &session->comp.stream;
+ int ret = 0;
+ uint8_t *out_data = NULL;
+ uint32_t out_len = 0;
+ uint32_t written = 0;
+ uint32_t start = 0;
+ uint32_t output_end = 0;
+ uint32_t space_avail = 0;
+ odp_packet_seg_t cur_seg = ODP_PACKET_SEG_INVALID;
+ odp_packet_data_range_t *res_data_range;
+ int finish = 0;
+
+ res_data_range = &result->output_data_range;
+
+ start = res_data_range->offset + res_data_range->length;
+ space_avail = params->out_data_range.length -
+ res_data_range->length;
+ output_end = space_avail + start;
+
+ do {
+ out_data =
+ odp_packet_offset(pkt_out, start, &out_len, &cur_seg);
+ _ODP_DBG("out_data %p seg_data_ptr %p out_len %d seg %p\n",
+ (void *)out_data, odp_packet_seg_data(pkt_out, cur_seg),
+ out_len, (void *)cur_seg);
+
+ if (0 == out_len) {
+ /* there are no more segments */
+ _ODP_DBG("Ran out of space. (streamp->avail_out) %d\n",
+ (streamp->avail_out));
+ result->status = ODP_COMP_STATUS_OUT_OF_SPACE_TERM;
+ break;
+ }
+
+ /* if segment length is greater than user given available
+ * space, then adjust output len
+ */
+ if (out_len > space_avail)
+ out_len = space_avail;
+
+ streamp->next_out = out_data;
+ streamp->avail_out = out_len;
+
+ _ODP_DBG("next_in %p, avail_in %d next_out %p avail_out %d, sync %d\n",
+ (const void *)streamp->next_in, streamp->avail_in,
+ (void *)streamp->next_out, streamp->avail_out, sync);
+
+ if (session->params.op == ODP_COMP_OP_COMPRESS)
+ ret = mz_deflate(streamp,
+ sync ? MZ_FINISH : MZ_NO_FLUSH);
+ else
+ ret = mz_inflate(streamp, MZ_NO_FLUSH);
+
+ _ODP_DBG("ret %d streamp->avail_out %d avail_in %d\n",
+ ret, streamp->avail_out, streamp->avail_in);
+
+ out_len = out_len - streamp->avail_out;
+ written += out_len;
+
+ /* increase next offset by amount of data written into
+ * output buffer and decrease available space by amount
+ * of space consumed.
+ */
+ start += out_len;
+ space_avail -= out_len;
+
+ _ODP_DBG("ret %d,written %d\n", ret, out_len);
+
+ if (ret == MZ_STREAM_END) {
+ if (session->params.op == ODP_COMP_OP_COMPRESS) {
+ /* required to continue processing of next pkt
+ with same stream */
+ mz_deflateReset(streamp);
+ } else {
+ mz_inflateReset(streamp);
+ }
+ finish = 1;
+ break;
+ }
+ if ((ret != MZ_BUF_ERROR) && (ret != MZ_OK)) {
+ _ODP_DBG("deflate failed. Err %s,ret %d"
+ "(streamp->avail_out) %d\n",
+ streamp->msg, ret, (streamp->avail_out));
+ result->status = ODP_COMP_STATUS_FAILURE;
+ return;
+ }
+ } while (!streamp->avail_out && (start < output_end));
+
+ res_data_range->length += written;
+
+ if ((!finish) && !(streamp->avail_out)) {
+ /* if write stopped as output exhausted,
+ return OUT_OF_SPACE_ERR
+ */
+ _ODP_DBG("Ran out of space. (out avail) %d,"
+ "to process %d\n", streamp->avail_out,
+ streamp->avail_in);
+ result->status = ODP_COMP_STATUS_OUT_OF_SPACE_TERM;
+ } else {
+ result->status = ODP_COMP_STATUS_SUCCESS;
+ }
+}
+
+/*
+ * Deflate routine to perform deflate based compression/decompression
+ *
+ * NOTE: Current implementation does not support in-place
+ */
+static int deflate_comp(odp_packet_t pkt_in,
+ odp_packet_t pkt_out,
+ const odp_comp_packet_op_param_t *params,
+ odp_comp_generic_session_t *session)
+{
+ mz_streamp streamp;
+ uint8_t *data = NULL;
+ uint32_t len;
+ uint32_t in_len = 0;
+ uint32_t read = 0;
+ uint32_t consumed = 0;
+ odp_bool_t sync = false;
+ odp_packet_seg_t in_seg = ODP_PACKET_SEG_INVALID;
+ odp_comp_packet_result_t *result = get_op_result_from_packet(pkt_out);
+
+ _ODP_ASSERT(session != NULL);
+ _ODP_ASSERT(params != NULL);
+ _ODP_ASSERT(pkt_in != ODP_PACKET_INVALID);
+ _ODP_ASSERT(pkt_out != ODP_PACKET_INVALID);
+
+ streamp = &session->comp.stream;
+
+ /* Adjust pointer for beginning of area to compress.
+ Since we need to pass phys cont area so we need to deal with segments
+ here as packet inherently are segmented and segments may not be
+ contiguous.
+ */
+
+ read = params->in_data_range.offset;
+ len = params->in_data_range.length;
+
+ while (read < (len + params->in_data_range.offset)) {
+ data = odp_packet_offset(pkt_in,
+ read,
+ &in_len,
+ &in_seg);
+ _ODP_DBG("data %p in_len %d seg %p len %d\n",
+ (void *)data, in_len, (void *)in_seg, len);
+
+ if (in_len > len)
+ in_len = len;
+
+ /* tracker for data consumed from input */
+ consumed += in_len;
+ streamp->next_in = data;
+ streamp->avail_in = in_len;
+
+ if (consumed >= len) {
+ _ODP_DBG("This is last chunk\n");
+ sync = true;
+ }
+
+ process_input(pkt_out, params, session, result, sync);
+
+ if (result->status != ODP_COMP_STATUS_SUCCESS)
+ return -1;
+
+ read += in_len;
+ }
+
+ _ODP_DBG("Read %d Written %d\n", read, result->output_data_range.length);
+
+ return 0;
+}
+
+static void *comp_zalloc(void *opaque, size_t items, size_t size)
+{
+ odp_comp_generic_session_t *session = opaque;
+
+ if (items * size > sizeof(session->comp.data))
+ return NULL;
+ else
+ return &session->comp.data;
+}
+
+static void comp_zfree(void *opaque ODP_UNUSED, void *data ODP_UNUSED)
+{
+ /* Do nothing */
+}
+
+static int deflate_init(odp_comp_generic_session_t *session)
+{
+ mz_streamp streamp = &session->comp.stream;
+ uint32_t level;
+ uint32_t strategy;
+ int32_t window_bits;
+ uint32_t cl;
+ odp_comp_huffman_code_t cc;
+
+ /* optional check as such may not required */
+ _ODP_ASSERT(strcmp(mz_version(), MZ_VERSION) == 0);
+
+ memset(&session->comp.stream, 0, sizeof(mz_stream));
+
+ /* let zlib handles required memory allocations
+ we will identify if there any memory allocations issues that
+ may come b/w odp and zlib allocated memory
+ */
+ streamp->zalloc = comp_zalloc;
+ streamp->zfree = comp_zfree;
+ streamp->opaque = session;
+
+ switch (session->params.comp_algo) {
+ case ODP_COMP_ALG_ZLIB:
+ cl = session->params.alg_param.zlib.deflate.comp_level;
+ cc = session->params.alg_param.zlib.deflate.huffman_code;
+ window_bits = MZ_DEFAULT_WINDOW_BITS;
+ break;
+ case ODP_COMP_ALG_DEFLATE:
+ cl = session->params.alg_param.deflate.comp_level;
+ cc = session->params.alg_param.deflate.huffman_code;
+ window_bits = -MZ_DEFAULT_WINDOW_BITS;
+ break;
+ default:
+ return -1;
+ }
+
+ level = MZ_DEFAULT_COMPRESSION; /* Z_BEST_COMPRESSION; */
+ if (cl)
+ level = cl;
+
+ switch (cc) {
+ case ODP_COMP_HUFFMAN_DEFAULT:
+ case ODP_COMP_HUFFMAN_DYNAMIC:/*Z_HUFFMAN_ONLY */
+ strategy = MZ_DEFAULT_STRATEGY;
+ break;
+ case ODP_COMP_HUFFMAN_FIXED:
+ strategy = MZ_FIXED;
+ break;
+ default:
+ return -1;
+ }
+ _ODP_DBG(" level %d strategy %d window %d\n", level, strategy, window_bits);
+
+ if (ODP_COMP_OP_COMPRESS == session->params.op) {
+ if (mz_deflateInit2(streamp, level, MZ_DEFLATED, window_bits,
+ MEM_LEVEL, strategy) != MZ_OK) {
+ _ODP_DBG("Err in Deflate Initialization %s\n", streamp->msg);
+ return -1;
+ }
+ } else {
+ if (mz_inflateInit2(streamp, window_bits) != MZ_OK) {
+ _ODP_DBG("Err in Inflate Initialization %s\n", streamp->msg);
+ return -1;
+ }
+ }
+
+ session->comp.func = deflate_comp;
+
+ return 0;
+}
+
+static int term_def(odp_comp_generic_session_t *session)
+{
+ int rc = 0;
+ mz_streamp streamp = &session->comp.stream;
+
+ if (ODP_COMP_OP_COMPRESS == session->params.op) {
+ rc = mz_deflateEnd(streamp);
+
+ if (rc != MZ_OK) {
+ _ODP_ERR("deflateEnd failed. Err %s,rc %d\n", streamp->msg, rc);
+ /* we choose to just return 0 with error info */
+ }
+ } else {
+ rc = mz_inflateEnd(streamp);
+ if (rc != MZ_OK) {
+ _ODP_ERR("inflateEnd failed. Err %s\n", streamp->msg);
+ /* we choose to just return 0 with error info */
+ }
+ }
+
+ return 0;
+}
+
+odp_comp_session_t
+odp_comp_session_create(const odp_comp_session_param_t *params)
+{
+ odp_comp_generic_session_t *session;
+ int rc;
+
+ /* Allocate memory for this session */
+ session = alloc_session();
+ if (NULL == session)
+ return ODP_COMP_SESSION_INVALID;
+
+ /* Copy stuff over */
+ memcpy(&session->params, params, sizeof(*params));
+
+ /* Process based on compress */
+ switch (params->comp_algo) {
+ case ODP_COMP_ALG_NULL:
+ session->comp.func = null_comp_routine;
+ break;
+ case ODP_COMP_ALG_DEFLATE:
+ case ODP_COMP_ALG_ZLIB:
+ rc = deflate_init(session);
+ if (rc < 0)
+ goto cleanup;
+ break;
+ default:
+ rc = -1;
+ goto cleanup;
+ }
+
+ return (odp_comp_session_t)session;
+
+cleanup:
+ free_session(session);
+
+ return ODP_COMP_SESSION_INVALID;
+}
+
+int odp_comp_session_destroy(odp_comp_session_t session)
+{
+ odp_comp_generic_session_t *generic;
+ int32_t rc = 0;
+
+ generic = (odp_comp_generic_session_t *)(intptr_t)session;
+
+ switch (generic->params.comp_algo) {
+ case ODP_COMP_ALG_DEFLATE:
+ case ODP_COMP_ALG_ZLIB:
+ rc = term_def(generic);
+ break;
+ default:
+ break;
+ }
+ if (rc < 0) {
+ _ODP_ERR("Compression Unit could not be terminated\n");
+ return -1;
+ }
+
+ memset(generic, 0, sizeof(*generic));
+ free_session(generic);
+ return 0;
+}
+
+int odp_comp_capability(odp_comp_capability_t *capa)
+{
+ if (NULL == capa)
+ return -1;
+
+ /* Initialize comp capability structure */
+ memset(capa, 0, sizeof(odp_comp_capability_t));
+
+ capa->comp_algos.bit.null = 1;
+ capa->comp_algos.bit.deflate = 1;
+ capa->comp_algos.bit.zlib = 1;
+ capa->hash_algos.bit.none = 1;
+ capa->sync = ODP_SUPPORT_YES;
+ capa->async = ODP_SUPPORT_YES;
+ capa->max_sessions = MAX_SESSIONS;
+ return 0;
+}
+
+int
+odp_comp_alg_capability(odp_comp_alg_t comp,
+ odp_comp_alg_capability_t *capa)
+{
+ switch (comp) {
+ case ODP_COMP_ALG_ZLIB:
+ capa->hash_algo.all_bits = 0;
+ capa->hash_algo.bit.none = 1;
+ capa->max_level = MZ_BEST_COMPRESSION;
+ capa->compression_ratio = 50;
+ return 0;
+ case ODP_COMP_ALG_DEFLATE:
+ capa->hash_algo.all_bits = 0;
+ capa->hash_algo.bit.none = 1;
+ capa->max_level = MZ_BEST_COMPRESSION;
+ capa->compression_ratio = 50;
+ return 0;
+ default:
+ /* Error unsupported enum */
+ return -1;
+ }
+ return -1;
+}
+
+int
+odp_comp_hash_alg_capability(odp_comp_hash_alg_t hash,
+ odp_comp_hash_alg_capability_t *capa)
+{
+ (void)capa;
+ switch (hash) {
+ case ODP_COMP_HASH_ALG_NONE:
+ capa[0].digest_len = 0;
+ return 0;
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+static int _odp_comp_single(odp_packet_t pkt_in, odp_packet_t pkt_out,
+ const odp_comp_packet_op_param_t *param)
+{
+ odp_comp_generic_session_t *session;
+ odp_comp_packet_result_t *result;
+ int rc;
+
+ session = to_gen_session(param->session);
+ _ODP_ASSERT(session);
+ _ODP_ASSERT(pkt_in != ODP_PACKET_INVALID);
+ _ODP_ASSERT(pkt_out != ODP_PACKET_INVALID);
+
+ result = get_op_result_from_packet(pkt_out);
+ _ODP_ASSERT(result);
+
+ result->pkt_in = pkt_in;
+ result->output_data_range.offset = param->out_data_range.offset;
+ result->output_data_range.length = 0;
+
+ packet_subtype_set(pkt_out, ODP_EVENT_PACKET_COMP);
+
+ rc = session->comp.func(pkt_in, pkt_out, param, session);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+int odp_comp_op(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[])
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ rc = _odp_comp_single(pkt_in[i], pkt_out[i], &param[i]);
+ if (rc < 0)
+ break;
+ }
+
+ return i;
+}
+
+int odp_comp_op_enq(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_pkt, const odp_comp_packet_op_param_t param[])
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ odp_event_t event;
+ odp_comp_generic_session_t *session;
+
+ rc = _odp_comp_single(pkt_in[i], pkt_out[i], &param[i]);
+ if (rc < 0)
+ break;
+
+ event = odp_packet_to_event(pkt_out[i]);
+ session = to_gen_session(param[i].session);
+ if (odp_queue_enq(session->params.compl_queue, event)) {
+ odp_event_free(event);
+ break;
+ }
+ }
+
+ return i;
+}
+
+int odp_comp_result(odp_comp_packet_result_t *result,
+ odp_packet_t packet)
+{
+ odp_comp_packet_result_t *op_result;
+
+ _ODP_ASSERT(odp_event_subtype(odp_packet_to_event(packet))
+ == ODP_EVENT_PACKET_COMP);
+
+ op_result = get_op_result_from_packet(packet);
+ _ODP_DBG("Copy operational result back\n");
+ memcpy(result, op_result, sizeof(*result));
+ return 0;
+}
+
+int _odp_comp_init_global(void)
+{
+ size_t mem_size;
+ odp_shm_t shm;
+ int idx;
+
+ if (odp_global_ro.disable.compress) {
+ _ODP_PRINT("\nODP compress is DISABLED\n");
+ return 0;
+ }
+
+ /* Calculate the memory size we need */
+ mem_size = sizeof(*global);
+
+ /* Allocate our globally shared memory */
+ shm = odp_shm_reserve("_odp_comp_global", mem_size,
+ ODP_CACHE_LINE_SIZE, 0);
+
+ global = odp_shm_addr(shm);
+
+ /* Clear it out */
+ memset(global, 0, mem_size);
+ global->global_shm = shm;
+
+ /* Initialize free list and lock */
+ for (idx = 0; idx < MAX_SESSIONS; idx++) {
+ global->sessions[idx].next = global->free;
+ global->free = &global->sessions[idx];
+ }
+ odp_spinlock_init(&global->lock);
+
+ return 0;
+}
+
+int _odp_comp_term_global(void)
+{
+ int rc = 0;
+ int ret;
+ int count = 0;
+ odp_comp_generic_session_t *session;
+
+ if (odp_global_ro.disable.compress)
+ return 0;
+
+ for (session = global->free; session != NULL; session = session->next)
+ count++;
+
+ if (count != MAX_SESSIONS) {
+ _ODP_ERR("comp sessions still active\n");
+ rc = -1;
+ }
+
+ ret = odp_shm_free(global->global_shm);
+ if (ret < 0) {
+ _ODP_ERR("shm free failed for comp_pool\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+odp_packet_t odp_comp_packet_from_event(odp_event_t ev)
+{
+ /* This check not mandated by the API specification */
+ _ODP_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+ _ODP_ASSERT(odp_event_subtype(ev) == ODP_EVENT_PACKET_COMP);
+
+ return odp_packet_from_event(ev);
+}
+
+odp_event_t odp_comp_packet_to_event(odp_packet_t pkt)
+{
+ return odp_packet_to_event(pkt);
+}
+
+/** Get printable format of odp_comp_session_t */
+uint64_t odp_comp_session_to_u64(odp_comp_session_t hdl)
+{
+ return _odp_pri(hdl);
+}
diff --git a/platform/linux-generic/odp_cpu.c b/platform/linux-generic/odp_cpu.c
deleted file mode 100644
index 282defd44..000000000
--- a/platform/linux-generic/odp_cpu.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/cpu.h>
-#include <odp/api/hints.h>
-
-uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
-{
- if (odp_likely(c2 >= c1))
- return c2 - c1;
-
- return c2 + (odp_cpu_cycles_max() - c1) + 1;
-}
diff --git a/platform/linux-generic/odp_cpu_api.c b/platform/linux-generic/odp_cpu_api.c
new file mode 100644
index 000000000..16107e701
--- /dev/null
+++ b/platform/linux-generic/odp_cpu_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/cpu.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/cpu_inlines.h>
diff --git a/platform/linux-generic/odp_cpumask.c b/platform/linux-generic/odp_cpumask.c
index 64559a6d5..778a851a3 100644
--- a/platform/linux-generic/odp_cpumask.c
+++ b/platform/linux-generic/odp_cpumask.c
@@ -1,17 +1,16 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
#include <odp_posix_extensions.h>
#include <sched.h>
-#include <pthread.h>
#include <odp/api/cpumask.h>
#include <odp/api/init.h>
#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
#include <stdlib.h>
#include <string.h>
@@ -227,33 +226,30 @@ int odp_cpumask_next(const odp_cpumask_t *mask, int cpu)
*/
static int get_available_cpus(void)
{
- int cpu_idnum;
cpu_set_t cpuset;
- int ret;
+ int cpu, ret;
- /* Clear the global cpumasks for control and worker CPUs */
- odp_cpumask_zero(&odp_global_data.control_cpus);
- odp_cpumask_zero(&odp_global_data.worker_cpus);
+ odp_cpumask_zero(&odp_global_ro.all_cpus);
CPU_ZERO(&cpuset);
ret = sched_getaffinity(0, sizeof(cpuset), &cpuset);
if (ret < 0) {
- ODP_ERR("Failed to get cpu affinity");
+ _ODP_ERR("Failed to get cpu affinity");
return -1;
}
- for (cpu_idnum = 0; cpu_idnum < CPU_SETSIZE - 1; cpu_idnum++) {
- if (CPU_ISSET(cpu_idnum, &cpuset)) {
- odp_global_data.num_cpus_installed++;
- /* Add the CPU to our default cpumasks */
- odp_cpumask_set(&odp_global_data.control_cpus,
- (int)cpu_idnum);
- odp_cpumask_set(&odp_global_data.worker_cpus,
- (int)cpu_idnum);
+ for (cpu = 0; cpu < CPU_SETSIZE - 1; cpu++) {
+ if (CPU_ISSET(cpu, &cpuset)) {
+ odp_global_ro.num_cpus_installed++;
+ odp_cpumask_set(&odp_global_ro.all_cpus, cpu);
}
}
+ /* Initialize control and worker masks with all CPUs */
+ odp_cpumask_copy(&odp_global_ro.control_cpus, &odp_global_ro.all_cpus);
+ odp_cpumask_copy(&odp_global_ro.worker_cpus, &odp_global_ro.all_cpus);
+
return 0;
}
@@ -265,8 +261,8 @@ static int get_available_cpus(void)
*/
static void init_default_control_cpumask(int worker_cpus_default)
{
- odp_cpumask_t *control_mask = &odp_global_data.control_cpus;
- odp_cpumask_t *worker_mask = &odp_global_data.worker_cpus;
+ odp_cpumask_t *control_mask = &odp_global_ro.control_cpus;
+ odp_cpumask_t *worker_mask = &odp_global_ro.worker_cpus;
int i;
/* (Bits for all available CPUs are SET in control cpumask) */
@@ -277,7 +273,7 @@ static void init_default_control_cpumask(int worker_cpus_default)
* If only one or two CPUs installed, use CPU 0 for control.
* Otherwise leave it for the kernel and start with CPU 1.
*/
- if (odp_global_data.num_cpus_installed < 3) {
+ if (odp_global_ro.num_cpus_installed < 3) {
/*
* If only two CPUS, use CPU 0 for control and
* use CPU 1 for workers.
@@ -290,7 +286,7 @@ static void init_default_control_cpumask(int worker_cpus_default)
* reserve remaining CPUs for workers
*/
odp_cpumask_clr(control_mask, 0);
- for (i = 2; i < odp_global_data.num_cpus_installed; i++)
+ for (i = 2; i < odp_global_ro.num_cpus_installed; i++)
if (odp_cpumask_isset(worker_mask, i))
odp_cpumask_clr(control_mask, i);
}
@@ -299,7 +295,7 @@ static void init_default_control_cpumask(int worker_cpus_default)
* The worker cpumask was specified so first ensure
* the control cpumask does not overlap any worker CPUs
*/
- for (i = 0; i < odp_global_data.num_cpus_installed; i++)
+ for (i = 0; i < odp_global_ro.num_cpus_installed; i++)
if (odp_cpumask_isset(worker_mask, i))
odp_cpumask_clr(control_mask, i);
@@ -307,7 +303,7 @@ static void init_default_control_cpumask(int worker_cpus_default)
* If only one or two CPUs installed,
* ensure availability of CPU 0 for control threads
*/
- if (odp_global_data.num_cpus_installed < 3) {
+ if (odp_global_ro.num_cpus_installed < 3) {
odp_cpumask_set(control_mask, 0);
odp_cpumask_clr(control_mask, 1);
} else {
@@ -333,8 +329,8 @@ static void init_default_control_cpumask(int worker_cpus_default)
*/
static void init_default_worker_cpumask(int control_cpus_default)
{
- odp_cpumask_t *control_mask = &odp_global_data.control_cpus;
- odp_cpumask_t *worker_mask = &odp_global_data.worker_cpus;
+ odp_cpumask_t *control_mask = &odp_global_ro.control_cpus;
+ odp_cpumask_t *worker_mask = &odp_global_ro.worker_cpus;
int i;
/* (Bits for all available CPUs are SET in worker cpumask) */
@@ -344,10 +340,10 @@ static void init_default_worker_cpumask(int control_cpus_default)
* The control cpumask was also unspecified...
* CPU 0 is only used for workers on uniprocessor systems
*/
- if (odp_global_data.num_cpus_installed > 1)
+ if (odp_global_ro.num_cpus_installed > 1)
odp_cpumask_clr(worker_mask, 0);
- if (odp_global_data.num_cpus_installed > 2)
+ if (odp_global_ro.num_cpus_installed > 2)
/*
* If three or more CPUs, reserve CPU 0 for kernel,
* reserve CPU 1 for control, and
@@ -359,7 +355,7 @@ static void init_default_worker_cpumask(int control_cpus_default)
* The control cpumask was specified so first ensure
* the worker cpumask does not overlap any control CPUs
*/
- for (i = 0; i < odp_global_data.num_cpus_installed; i++)
+ for (i = 0; i < odp_global_ro.num_cpus_installed; i++)
if (odp_cpumask_isset(control_mask, i))
odp_cpumask_clr(worker_mask, i);
@@ -367,7 +363,7 @@ static void init_default_worker_cpumask(int control_cpus_default)
* If only one CPU installed, use CPU 0 for workers
* even though it is used for control as well.
*/
- if (odp_global_data.num_cpus_installed < 2)
+ if (odp_global_ro.num_cpus_installed < 2)
odp_cpumask_set(worker_mask, 0);
else
odp_cpumask_clr(worker_mask, 0);
@@ -380,10 +376,10 @@ static void init_default_worker_cpumask(int control_cpus_default)
* It also allows the default cpumasks to be overridden by
* externally specified cpumasks passed in as initialization parameters.
*/
-int odp_cpumask_init_global(const odp_init_t *params)
+int _odp_cpumask_init_global(const odp_init_t *params)
{
- odp_cpumask_t *control_mask = &odp_global_data.control_cpus;
- odp_cpumask_t *worker_mask = &odp_global_data.worker_cpus;
+ odp_cpumask_t *control_mask = &odp_global_ro.control_cpus;
+ odp_cpumask_t *worker_mask = &odp_global_ro.worker_cpus;
odp_cpumask_t check_mask;
int control_cpus_default = 1;
int worker_cpus_default = 1;
@@ -449,7 +445,7 @@ int odp_cpumask_init_global(const odp_init_t *params)
}
}
-int odp_cpumask_term_global(void)
+int _odp_cpumask_term_global(void)
{
return 0;
}
diff --git a/platform/linux-generic/odp_cpumask_task.c b/platform/linux-generic/odp_cpumask_task.c
index 10885ce6b..b0819bbf0 100644
--- a/platform/linux-generic/odp_cpumask_task.c
+++ b/platform/linux-generic/odp_cpumask_task.c
@@ -1,88 +1,83 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
*/
#include <odp_posix_extensions.h>
-#include <sched.h>
-#include <pthread.h>
-
#include <odp/api/cpumask.h>
+
#include <odp_debug_internal.h>
+#include <odp_global_data.h>
-int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
+#include <sched.h>
+
+int odp_cpumask_default_worker(odp_cpumask_t *mask, int max_num)
{
- odp_cpumask_t overlap;
- int cpu, i;
-
- /*
- * If no user supplied number or it's too large, then attempt
- * to use all CPUs
- */
- cpu = odp_cpumask_count(&odp_global_data.worker_cpus);
- if (0 == num || cpu < num)
- num = cpu;
-
- /* build the mask, allocating down from highest numbered CPU */
+ int num, cpu, ret;
+ odp_cpumask_t *worker_cpus = &odp_global_ro.worker_cpus;
+
+ num = odp_cpumask_count(worker_cpus);
+
+ if (max_num && num > max_num)
+ num = max_num;
+
+ if (mask == NULL)
+ return num;
+
odp_cpumask_zero(mask);
- for (cpu = 0, i = CPU_SETSIZE - 1; i >= 0 && cpu < num; --i) {
- if (odp_cpumask_isset(&odp_global_data.worker_cpus, i)) {
- odp_cpumask_set(mask, i);
- cpu++;
+
+ /* Allocate down from the highest numbered CPU */
+ cpu = odp_cpumask_last(worker_cpus);
+ ret = num;
+
+ while (cpu >= 0 && num > 0) {
+ if (odp_cpumask_isset(worker_cpus, cpu)) {
+ odp_cpumask_set(mask, cpu);
+ num--;
}
- }
- odp_cpumask_and(&overlap, mask, &odp_global_data.control_cpus);
- if (odp_cpumask_count(&overlap))
- ODP_DBG("\n\tWorker CPUs overlap with control CPUs...\n"
- "\tthis will likely have a performance impact on the worker threads.\n");
+ cpu--;
+ }
- return cpu;
+ return ret;
}
-int odp_cpumask_default_control(odp_cpumask_t *mask, int num)
+int odp_cpumask_default_control(odp_cpumask_t *mask, int max_num)
{
- odp_cpumask_t overlap;
- int cpu, i;
-
- /*
- * If no user supplied number then default to one control CPU.
- */
- if (0 == num) {
- num = 1;
- } else {
- /*
- * If user supplied number is too large, then attempt
- * to use all installed control CPUs
- */
- cpu = odp_cpumask_count(&odp_global_data.control_cpus);
- if (cpu < num)
- num = cpu;
- }
+ int num, cpu, last, ret;
+ odp_cpumask_t *control_cpus = &odp_global_ro.control_cpus;
+
+ num = odp_cpumask_count(control_cpus);
+
+ if (max_num && num > max_num)
+ num = max_num;
+
+ if (mask == NULL)
+ return num;
- /* build the mask, allocating upwards from lowest numbered CPU */
odp_cpumask_zero(mask);
- for (cpu = 0, i = 0; i < CPU_SETSIZE && cpu < num; i++) {
- if (odp_cpumask_isset(&odp_global_data.control_cpus, i)) {
- odp_cpumask_set(mask, i);
- cpu++;
+
+ /* Allocate up from the lowest numbered CPU */
+ cpu = odp_cpumask_first(control_cpus);
+ last = odp_cpumask_last(control_cpus);
+ ret = num;
+
+ while (cpu <= last && num > 0) {
+ if (odp_cpumask_isset(control_cpus, cpu)) {
+ odp_cpumask_set(mask, cpu);
+ num--;
}
- }
- odp_cpumask_and(&overlap, mask, &odp_global_data.worker_cpus);
- if (odp_cpumask_count(&overlap))
- ODP_DBG("\n\tControl CPUs overlap with worker CPUs...\n"
- "\tthis will likely have a performance impact on the worker threads.\n");
+ cpu++;
+ }
- return cpu;
+ return ret;
}
int odp_cpumask_all_available(odp_cpumask_t *mask)
{
- odp_cpumask_or(mask, &odp_global_data.worker_cpus,
- &odp_global_data.control_cpus);
+ odp_cpumask_copy(mask, &odp_global_ro.all_cpus);
- return odp_cpumask_count(mask);
+ return odp_global_ro.num_cpus_installed;
}
diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c
deleted file mode 100644
index ab42132a6..000000000
--- a/platform/linux-generic/odp_crypto.c
+++ /dev/null
@@ -1,1131 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-#include <odp/api/crypto.h>
-#include <odp_internal.h>
-#include <odp/api/atomic.h>
-#include <odp/api/spinlock.h>
-#include <odp/api/sync.h>
-#include <odp/api/debug.h>
-#include <odp/api/align.h>
-#include <odp/api/shared_memory.h>
-#include <odp_crypto_internal.h>
-#include <odp_debug_internal.h>
-#include <odp/api/hints.h>
-#include <odp/api/random.h>
-#include <odp_packet_internal.h>
-
-#include <string.h>
-#include <stdlib.h>
-
-#include <openssl/des.h>
-#include <openssl/rand.h>
-#include <openssl/hmac.h>
-#include <openssl/evp.h>
-
-#define MAX_SESSIONS 32
-
-/*
- * Cipher algorithm capabilities
- *
- * Keep sorted: first by key length, then by IV length
- */
-static const odp_crypto_cipher_capability_t cipher_capa_null[] = {
-{.key_len = 0, .iv_len = 0} };
-
-static const odp_crypto_cipher_capability_t cipher_capa_des[] = {
-{.key_len = 24, .iv_len = 8} };
-
-static const odp_crypto_cipher_capability_t cipher_capa_trides_cbc[] = {
-{.key_len = 24, .iv_len = 8} };
-
-static const odp_crypto_cipher_capability_t cipher_capa_aes_cbc[] = {
-{.key_len = 16, .iv_len = 16} };
-
-static const odp_crypto_cipher_capability_t cipher_capa_aes_gcm[] = {
-{.key_len = 16, .iv_len = 12} };
-
-/*
- * Authentication algorithm capabilities
- *
- * Keep sorted: first by digest length, then by key length
- */
-static const odp_crypto_auth_capability_t auth_capa_null[] = {
-{.digest_len = 0, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
-
-static const odp_crypto_auth_capability_t auth_capa_md5_hmac[] = {
-{.digest_len = 12, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} },
-{.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
-
-static const odp_crypto_auth_capability_t auth_capa_sha1_hmac[] = {
-{.digest_len = 12, .key_len = 20, .aad_len = {.min = 0, .max = 0, .inc = 0} },
-{.digest_len = 20, .key_len = 20, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
-
-static const odp_crypto_auth_capability_t auth_capa_sha256_hmac[] = {
-{.digest_len = 16, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} },
-{.digest_len = 32, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
-
-static const odp_crypto_auth_capability_t auth_capa_sha512_hmac[] = {
-{.digest_len = 32, .key_len = 64, .aad_len = {.min = 0, .max = 0, .inc = 0} },
-{.digest_len = 64, .key_len = 64, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
-
-static const odp_crypto_auth_capability_t auth_capa_aes_gcm[] = {
-{.digest_len = 16, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
-
-typedef struct odp_crypto_global_s odp_crypto_global_t;
-
-struct odp_crypto_global_s {
- odp_spinlock_t lock;
- odp_ticketlock_t **openssl_lock;
- odp_crypto_generic_session_t *free;
- odp_crypto_generic_session_t sessions[0];
-};
-
-static odp_crypto_global_t *global;
-
-static
-odp_crypto_generic_op_result_t *get_op_result_from_event(odp_event_t ev)
-{
- odp_packet_hdr_t *hdr = odp_packet_hdr(odp_packet_from_event(ev));
-
- return &hdr->op_result;
-}
-
-static
-odp_crypto_generic_session_t *alloc_session(void)
-{
- odp_crypto_generic_session_t *session = NULL;
-
- odp_spinlock_lock(&global->lock);
- session = global->free;
- if (session) {
- global->free = session->next;
- session->next = NULL;
- }
- odp_spinlock_unlock(&global->lock);
-
- return session;
-}
-
-static
-void free_session(odp_crypto_generic_session_t *session)
-{
- odp_spinlock_lock(&global->lock);
- session->next = global->free;
- global->free = session;
- odp_spinlock_unlock(&global->lock);
-}
-
-static odp_crypto_alg_err_t
-null_crypto_routine(odp_crypto_op_param_t *param ODP_UNUSED,
- odp_crypto_generic_session_t *session ODP_UNUSED)
-{
- return ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static
-void packet_hmac_calculate(HMAC_CTX *ctx,
- odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session,
- uint8_t *hash)
-{
- odp_packet_t pkt = param->out_pkt;
- uint32_t offset = param->auth_range.offset;
- uint32_t len = param->auth_range.length;
-
- ODP_ASSERT(offset + len <= odp_packet_len(pkt));
-
- HMAC_Init_ex(ctx,
- session->auth.key,
- session->auth.key_length,
- session->auth.evp_md,
- NULL);
-
- while (len > 0) {
- uint32_t seglen = 0; /* GCC */
- void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
- uint32_t maclen = len > seglen ? seglen : len;
-
- HMAC_Update(ctx, mapaddr, maclen);
- offset += maclen;
- len -= maclen;
- }
-
- HMAC_Final(ctx, hash, NULL);
-}
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static
-void packet_hmac(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session,
- uint8_t *hash)
-{
- HMAC_CTX ctx;
-
- /* Hash it */
- HMAC_CTX_init(&ctx);
- packet_hmac_calculate(&ctx, param, session, hash);
- HMAC_CTX_cleanup(&ctx);
-}
-#else
-static
-void packet_hmac(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session,
- uint8_t *hash)
-{
- HMAC_CTX *ctx;
-
- /* Hash it */
- ctx = HMAC_CTX_new();
- packet_hmac_calculate(ctx, param, session, hash);
- HMAC_CTX_free(ctx);
-}
-#endif
-
-static
-odp_crypto_alg_err_t auth_gen(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- uint8_t hash[EVP_MAX_MD_SIZE];
-
- /* Hash it */
- packet_hmac(param, session, hash);
-
- /* Copy to the output location */
- odp_packet_copy_from_mem(param->out_pkt,
- param->hash_result_offset,
- session->auth.bytes,
- hash);
-
- return ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static
-odp_crypto_alg_err_t auth_check(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- uint32_t bytes = session->auth.bytes;
- uint8_t hash_in[EVP_MAX_MD_SIZE];
- uint8_t hash_out[EVP_MAX_MD_SIZE];
-
- /* Copy current value out and clear it before authentication */
- odp_packet_copy_to_mem(param->out_pkt, param->hash_result_offset,
- bytes, hash_in);
-
- _odp_packet_set_data(param->out_pkt, param->hash_result_offset,
- 0, bytes);
-
- /* Hash it */
- packet_hmac(param, session, hash_out);
-
- /* Verify match */
- if (0 != memcmp(hash_in, hash_out, bytes))
- return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
-
- /* Matched */
- return ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static
-int internal_encrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
-{
- odp_packet_t pkt = param->out_pkt;
- unsigned in_pos = param->cipher_range.offset;
- unsigned out_pos = param->cipher_range.offset;
- unsigned in_len = param->cipher_range.length;
- uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
- unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
- int cipher_len;
- int ret;
-
- ODP_ASSERT(in_pos + in_len <= odp_packet_len(pkt));
-
- while (in_len > 0) {
- uint32_t seglen = 0; /* GCC */
- uint8_t *insegaddr = odp_packet_offset(pkt, in_pos,
- &seglen, NULL);
- unsigned inseglen = in_len < seglen ? in_len : seglen;
-
- /* There should be at least 1 additional block in out buffer */
- if (inseglen > block_len) {
- unsigned part = inseglen - block_len;
-
- EVP_EncryptUpdate(ctx, insegaddr, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- out_pos += cipher_len;
- }
-
- /* Use temporal storage */
- if (inseglen > 0) {
- unsigned part = inseglen;
-
- EVP_EncryptUpdate(ctx, block, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- odp_packet_copy_from_mem(pkt, out_pos,
- cipher_len, block);
- out_pos += cipher_len;
- }
- }
-
- ret = EVP_EncryptFinal_ex(ctx, block, &cipher_len);
- odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
-
- return ret;
-}
-
-static
-int internal_decrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
-{
- odp_packet_t pkt = param->out_pkt;
- unsigned in_pos = param->cipher_range.offset;
- unsigned out_pos = param->cipher_range.offset;
- unsigned in_len = param->cipher_range.length;
- uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
- unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
- int cipher_len;
- int ret;
-
- ODP_ASSERT(in_pos + in_len <= odp_packet_len(pkt));
-
- while (in_len > 0) {
- uint32_t seglen = 0; /* GCC */
- uint8_t *insegaddr = odp_packet_offset(pkt, in_pos,
- &seglen, NULL);
- unsigned inseglen = in_len < seglen ? in_len : seglen;
-
- /* There should be at least 1 additional block in out buffer */
- if (inseglen > block_len) {
- unsigned part = inseglen - block_len;
-
- EVP_DecryptUpdate(ctx, insegaddr, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- out_pos += cipher_len;
- }
-
- /* Use temporal storage */
- if (inseglen > 0) {
- unsigned part = inseglen;
-
- EVP_DecryptUpdate(ctx, block, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- odp_packet_copy_from_mem(pkt, out_pos,
- cipher_len, block);
- out_pos += cipher_len;
- }
- }
-
- ret = EVP_DecryptFinal_ex(ctx, block, &cipher_len);
- odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
-
- return ret;
-}
-
-static
-odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- EVP_CIPHER_CTX *ctx;
- void *iv_ptr;
- int ret;
-
- if (param->override_iv_ptr)
- iv_ptr = param->override_iv_ptr;
- else if (session->p.iv.data)
- iv_ptr = session->cipher.iv_data;
- else
- return ODP_CRYPTO_ALG_ERR_IV_INVALID;
-
- /* Encrypt it */
- ctx = EVP_CIPHER_CTX_new();
- EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
- session->cipher.key_data, NULL);
- EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- EVP_CIPHER_CTX_set_padding(ctx, 0);
-
- ret = internal_encrypt(ctx, param);
-
- EVP_CIPHER_CTX_free(ctx);
-
- return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
- ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static
-odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- EVP_CIPHER_CTX *ctx;
- void *iv_ptr;
- int ret;
-
- if (param->override_iv_ptr)
- iv_ptr = param->override_iv_ptr;
- else if (session->p.iv.data)
- iv_ptr = session->cipher.iv_data;
- else
- return ODP_CRYPTO_ALG_ERR_IV_INVALID;
-
- /* Decrypt it */
- ctx = EVP_CIPHER_CTX_new();
- EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
- session->cipher.key_data, NULL);
- EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- EVP_CIPHER_CTX_set_padding(ctx, 0);
-
- ret = internal_decrypt(ctx, param);
-
- EVP_CIPHER_CTX_free(ctx);
-
- return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
- ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static int process_cipher_param(odp_crypto_generic_session_t *session,
- const EVP_CIPHER *cipher)
-{
- /* Verify Key len is valid */
- if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
- session->p.cipher_key.length)
- return -1;
-
- /* Verify IV len is correct */
- if (!((0 == session->p.iv.length) ||
- ((uint32_t)EVP_CIPHER_iv_length(cipher) == session->p.iv.length)))
- return -1;
-
- session->cipher.evp_cipher = cipher;
-
- memcpy(session->cipher.key_data, session->p.cipher_key.data,
- session->p.cipher_key.length);
-
- /* Set function */
- if (ODP_CRYPTO_OP_ENCODE == session->p.op)
- session->cipher.func = cipher_encrypt;
- else
- session->cipher.func = cipher_decrypt;
-
- return 0;
-}
-
-static
-odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- EVP_CIPHER_CTX *ctx;
- const uint8_t *aad_head = param->aad.ptr;
- uint32_t aad_len = param->aad.length;
- void *iv_ptr;
- int dummy_len = 0;
- uint8_t block[EVP_MAX_MD_SIZE];
- int ret;
-
- if (param->override_iv_ptr)
- iv_ptr = param->override_iv_ptr;
- else if (session->p.iv.data)
- iv_ptr = session->cipher.iv_data;
- else
- return ODP_CRYPTO_ALG_ERR_IV_INVALID;
-
- /* Encrypt it */
- ctx = EVP_CIPHER_CTX_new();
- EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
- session->cipher.key_data, NULL);
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
- session->p.iv.length, NULL);
- EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- EVP_CIPHER_CTX_set_padding(ctx, 0);
-
- /* Authenticate header data (if any) without encrypting them */
- if (aad_len > 0)
- EVP_EncryptUpdate(ctx, NULL, &dummy_len,
- aad_head, aad_len);
-
- ret = internal_encrypt(ctx, param);
-
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
- session->p.auth_digest_len, block);
- odp_packet_copy_from_mem(param->out_pkt, param->hash_result_offset,
- session->p.auth_digest_len, block);
-
- EVP_CIPHER_CTX_free(ctx);
-
- return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
- ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static
-odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
- odp_crypto_generic_session_t *session)
-{
- EVP_CIPHER_CTX *ctx;
- const uint8_t *aad_head = param->aad.ptr;
- uint32_t aad_len = param->aad.length;
- int dummy_len = 0;
- void *iv_ptr;
- uint8_t block[EVP_MAX_MD_SIZE];
- int ret;
-
- if (param->override_iv_ptr)
- iv_ptr = param->override_iv_ptr;
- else if (session->p.iv.data)
- iv_ptr = session->cipher.iv_data;
- else
- return ODP_CRYPTO_ALG_ERR_IV_INVALID;
-
- /* Decrypt it */
- ctx = EVP_CIPHER_CTX_new();
- EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
- session->cipher.key_data, NULL);
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
- session->p.iv.length, NULL);
- EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- EVP_CIPHER_CTX_set_padding(ctx, 0);
-
- odp_packet_copy_to_mem(param->out_pkt, param->hash_result_offset,
- session->p.auth_digest_len, block);
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
- session->p.auth_digest_len, block);
-
- /* Authenticate header data (if any) without encrypting them */
- if (aad_len > 0)
- EVP_DecryptUpdate(ctx, NULL, &dummy_len,
- aad_head, aad_len);
-
- ret = internal_decrypt(ctx, param);
-
- EVP_CIPHER_CTX_free(ctx);
-
- return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
- ODP_CRYPTO_ALG_ERR_NONE;
-}
-
-static int process_aes_gcm_param(odp_crypto_generic_session_t *session,
- const EVP_CIPHER *cipher)
-{
- /* Verify Key len is valid */
- if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
- session->p.cipher_key.length)
- return -1;
-
- memcpy(session->cipher.key_data, session->p.cipher_key.data,
- session->p.cipher_key.length);
-
- session->cipher.evp_cipher = cipher;
-
- /* Set function */
- if (ODP_CRYPTO_OP_ENCODE == session->p.op)
- session->cipher.func = aes_gcm_encrypt;
- else
- session->cipher.func = aes_gcm_decrypt;
-
- return 0;
-}
-
-static int process_auth_param(odp_crypto_generic_session_t *session,
- uint32_t key_length,
- const EVP_MD *evp_md)
-{
- /* Set function */
- if (ODP_CRYPTO_OP_ENCODE == session->p.op)
- session->auth.func = auth_gen;
- else
- session->auth.func = auth_check;
-
- session->auth.evp_md = evp_md;
-
- /* Number of valid bytes */
- session->auth.bytes = session->p.auth_digest_len;
- if (session->auth.bytes < (unsigned)EVP_MD_size(evp_md) / 2)
- return -1;
-
- /* Convert keys */
- session->auth.key_length = key_length;
- memcpy(session->auth.key, session->p.auth_key.data,
- session->auth.key_length);
-
- return 0;
-}
-
-int odp_crypto_capability(odp_crypto_capability_t *capa)
-{
- if (NULL == capa)
- return -1;
-
- /* Initialize crypto capability structure */
- memset(capa, 0, sizeof(odp_crypto_capability_t));
-
- capa->ciphers.bit.null = 1;
- capa->ciphers.bit.des = 1;
- capa->ciphers.bit.trides_cbc = 1;
- capa->ciphers.bit.aes_cbc = 1;
- capa->ciphers.bit.aes_gcm = 1;
-
- capa->auths.bit.null = 1;
- capa->auths.bit.md5_hmac = 1;
- capa->auths.bit.sha1_hmac = 1;
- capa->auths.bit.sha256_hmac = 1;
- capa->auths.bit.sha512_hmac = 1;
- capa->auths.bit.aes_gcm = 1;
-
-#if ODP_DEPRECATED_API
- capa->ciphers.bit.aes128_cbc = 1;
- capa->ciphers.bit.aes128_gcm = 1;
- capa->auths.bit.md5_96 = 1;
- capa->auths.bit.sha256_128 = 1;
- capa->auths.bit.aes128_gcm = 1;
-#endif
-
- capa->max_sessions = MAX_SESSIONS;
-
- return 0;
-}
-
-int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
- odp_crypto_cipher_capability_t dst[],
- int num_copy)
-{
- const odp_crypto_cipher_capability_t *src;
- int num;
- int size = sizeof(odp_crypto_cipher_capability_t);
-
- switch (cipher) {
- case ODP_CIPHER_ALG_NULL:
- src = cipher_capa_null;
- num = sizeof(cipher_capa_null) / size;
- break;
- case ODP_CIPHER_ALG_DES:
- src = cipher_capa_des;
- num = sizeof(cipher_capa_des) / size;
- break;
- case ODP_CIPHER_ALG_3DES_CBC:
- src = cipher_capa_trides_cbc;
- num = sizeof(cipher_capa_trides_cbc) / size;
- break;
- case ODP_CIPHER_ALG_AES_CBC:
- src = cipher_capa_aes_cbc;
- num = sizeof(cipher_capa_aes_cbc) / size;
- break;
- case ODP_CIPHER_ALG_AES_GCM:
- src = cipher_capa_aes_gcm;
- num = sizeof(cipher_capa_aes_gcm) / size;
- break;
- default:
- return -1;
- }
-
- if (num < num_copy)
- num_copy = num;
-
- memcpy(dst, src, num_copy * size);
-
- return num;
-}
-
-int odp_crypto_auth_capability(odp_auth_alg_t auth,
- odp_crypto_auth_capability_t dst[], int num_copy)
-{
- const odp_crypto_auth_capability_t *src;
- int num;
- int size = sizeof(odp_crypto_auth_capability_t);
-
- switch (auth) {
- case ODP_AUTH_ALG_NULL:
- src = auth_capa_null;
- num = sizeof(auth_capa_null) / size;
- break;
- case ODP_AUTH_ALG_MD5_HMAC:
- src = auth_capa_md5_hmac;
- num = sizeof(auth_capa_md5_hmac) / size;
- break;
- case ODP_AUTH_ALG_SHA1_HMAC:
- src = auth_capa_sha1_hmac;
- num = sizeof(auth_capa_sha1_hmac) / size;
- break;
- case ODP_AUTH_ALG_SHA256_HMAC:
- src = auth_capa_sha256_hmac;
- num = sizeof(auth_capa_sha256_hmac) / size;
- break;
- case ODP_AUTH_ALG_SHA512_HMAC:
- src = auth_capa_sha512_hmac;
- num = sizeof(auth_capa_sha512_hmac) / size;
- break;
- case ODP_AUTH_ALG_AES_GCM:
- src = auth_capa_aes_gcm;
- num = sizeof(auth_capa_aes_gcm) / size;
- break;
- default:
- return -1;
- }
-
- if (num < num_copy)
- num_copy = num;
-
- memcpy(dst, src, num_copy * size);
-
- return num;
-}
-
-int
-odp_crypto_session_create(odp_crypto_session_param_t *param,
- odp_crypto_session_t *session_out,
- odp_crypto_ses_create_err_t *status)
-{
- int rc;
- odp_crypto_generic_session_t *session;
- int aes_gcm = 0;
-
- /* Default to successful result */
- *status = ODP_CRYPTO_SES_CREATE_ERR_NONE;
-
- /* Allocate memory for this session */
- session = alloc_session();
- if (NULL == session) {
- *status = ODP_CRYPTO_SES_CREATE_ERR_ENOMEM;
- return -1;
- }
-
- /* Copy parameters */
- session->p = *param;
-
- if (session->p.iv.length > MAX_IV_LEN) {
- ODP_DBG("Maximum IV length exceeded\n");
- free_session(session);
- return -1;
- }
-
- /* Copy IV data */
- if (session->p.iv.data)
- memcpy(session->cipher.iv_data, session->p.iv.data,
- session->p.iv.length);
-
- /* Derive order */
- if (ODP_CRYPTO_OP_ENCODE == param->op)
- session->do_cipher_first = param->auth_cipher_text;
- else
- session->do_cipher_first = !param->auth_cipher_text;
-
- /* Process based on cipher */
- switch (param->cipher_alg) {
- case ODP_CIPHER_ALG_NULL:
- session->cipher.func = null_crypto_routine;
- rc = 0;
- break;
- case ODP_CIPHER_ALG_DES:
- case ODP_CIPHER_ALG_3DES_CBC:
- rc = process_cipher_param(session, EVP_des_ede3_cbc());
- break;
- case ODP_CIPHER_ALG_AES_CBC:
-#if ODP_DEPRECATED_API
- case ODP_CIPHER_ALG_AES128_CBC:
-#endif
- rc = process_cipher_param(session, EVP_aes_128_cbc());
- break;
-#if ODP_DEPRECATED_API
- case ODP_CIPHER_ALG_AES128_GCM:
- if (param->auth_alg == ODP_AUTH_ALG_AES128_GCM)
- aes_gcm = 1;
- /* Fallthrough */
-#endif
- case ODP_CIPHER_ALG_AES_GCM:
- /* AES-GCM requires to do both auth and
- * cipher at the same time */
- if (param->auth_alg == ODP_AUTH_ALG_AES_GCM || aes_gcm)
- rc = process_aes_gcm_param(session, EVP_aes_128_gcm());
- else
- rc = -1;
- break;
- default:
- rc = -1;
- }
-
- /* Check result */
- if (rc) {
- *status = ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER;
- free_session(session);
- return -1;
- }
-
- aes_gcm = 0;
-
- /* Process based on auth */
- switch (param->auth_alg) {
- case ODP_AUTH_ALG_NULL:
- session->auth.func = null_crypto_routine;
- rc = 0;
- break;
-#if ODP_DEPRECATED_API
- case ODP_AUTH_ALG_MD5_96:
- /* Fixed digest tag length with deprecated algo */
- session->p.auth_digest_len = 96 / 8;
- /* Fallthrough */
-#endif
- case ODP_AUTH_ALG_MD5_HMAC:
- rc = process_auth_param(session, 16, EVP_md5());
- break;
- case ODP_AUTH_ALG_SHA1_HMAC:
- rc = process_auth_param(session, 20, EVP_sha1());
- break;
-#if ODP_DEPRECATED_API
- case ODP_AUTH_ALG_SHA256_128:
- /* Fixed digest tag length with deprecated algo */
- session->p.auth_digest_len = 128 / 8;
- /* Fallthrough */
-#endif
- case ODP_AUTH_ALG_SHA256_HMAC:
- rc = process_auth_param(session, 32, EVP_sha256());
- break;
- case ODP_AUTH_ALG_SHA512_HMAC:
- rc = process_auth_param(session, 64, EVP_sha512());
- break;
-#if ODP_DEPRECATED_API
- case ODP_AUTH_ALG_AES128_GCM:
- if (param->cipher_alg == ODP_CIPHER_ALG_AES128_GCM)
- aes_gcm = 1;
- /* Fixed digest tag length with deprecated algo */
- session->p.auth_digest_len = 16;
- /* Fallthrough */
-#endif
- case ODP_AUTH_ALG_AES_GCM:
- /* AES-GCM requires to do both auth and
- * cipher at the same time */
- if (param->cipher_alg == ODP_CIPHER_ALG_AES_GCM || aes_gcm) {
- session->auth.func = null_crypto_routine;
- rc = 0;
- } else {
- rc = -1;
- }
- break;
- default:
- rc = -1;
- }
-
- /* Check result */
- if (rc) {
- *status = ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH;
- free_session(session);
- return -1;
- }
-
- /* We're happy */
- *session_out = (intptr_t)session;
- return 0;
-}
-
-int odp_crypto_session_destroy(odp_crypto_session_t session)
-{
- odp_crypto_generic_session_t *generic;
-
- generic = (odp_crypto_generic_session_t *)(intptr_t)session;
- memset(generic, 0, sizeof(*generic));
- free_session(generic);
- return 0;
-}
-
-int
-odp_crypto_operation(odp_crypto_op_param_t *param,
- odp_bool_t *posted,
- odp_crypto_op_result_t *result)
-{
- odp_crypto_alg_err_t rc_cipher = ODP_CRYPTO_ALG_ERR_NONE;
- odp_crypto_alg_err_t rc_auth = ODP_CRYPTO_ALG_ERR_NONE;
- odp_crypto_generic_session_t *session;
- odp_crypto_op_result_t local_result;
- odp_bool_t allocated = false;
-
- session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
-
- /* Resolve output buffer */
- if (ODP_PACKET_INVALID == param->out_pkt &&
- ODP_POOL_INVALID != session->p.output_pool) {
- param->out_pkt = odp_packet_alloc(session->p.output_pool,
- odp_packet_len(param->pkt));
- allocated = true;
- }
-
- if (odp_unlikely(ODP_PACKET_INVALID == param->out_pkt)) {
- ODP_DBG("Alloc failed.\n");
- return -1;
- }
-
- if (param->pkt != param->out_pkt) {
- int ret;
-
- ret = odp_packet_copy_from_pkt(param->out_pkt,
- 0,
- param->pkt,
- 0,
- odp_packet_len(param->pkt));
- if (odp_unlikely(ret < 0))
- goto err;
-
- _odp_packet_copy_md_to_packet(param->pkt, param->out_pkt);
- odp_packet_free(param->pkt);
- param->pkt = ODP_PACKET_INVALID;
- }
-
- /* Invoke the functions */
- if (session->do_cipher_first) {
- rc_cipher = session->cipher.func(param, session);
- rc_auth = session->auth.func(param, session);
- } else {
- rc_auth = session->auth.func(param, session);
- rc_cipher = session->cipher.func(param, session);
- }
-
- /* Fill in result */
- local_result.ctx = param->ctx;
- local_result.pkt = param->out_pkt;
- local_result.cipher_status.alg_err = rc_cipher;
- local_result.cipher_status.hw_err = ODP_CRYPTO_HW_ERR_NONE;
- local_result.auth_status.alg_err = rc_auth;
- local_result.auth_status.hw_err = ODP_CRYPTO_HW_ERR_NONE;
- local_result.ok =
- (rc_cipher == ODP_CRYPTO_ALG_ERR_NONE) &&
- (rc_auth == ODP_CRYPTO_ALG_ERR_NONE);
-
- /* If specified during creation post event to completion queue */
- if (ODP_QUEUE_INVALID != session->p.compl_queue) {
- odp_event_t completion_event;
- odp_crypto_generic_op_result_t *op_result;
-
- /* Linux generic will always use packet for completion event */
- completion_event = odp_packet_to_event(param->out_pkt);
- _odp_buffer_event_type_set(
- odp_buffer_from_event(completion_event),
- ODP_EVENT_CRYPTO_COMPL);
- /* Asynchronous, build result (no HW so no errors) and send it*/
- op_result = get_op_result_from_event(completion_event);
- op_result->magic = OP_RESULT_MAGIC;
- op_result->result = local_result;
- if (odp_queue_enq(session->p.compl_queue, completion_event)) {
- odp_event_free(completion_event);
- goto err;
- }
-
- /* Indicate to caller operation was async */
- *posted = 1;
- } else {
- /* Synchronous, simply return results */
- if (!result)
- goto err;
- *result = local_result;
-
- /* Indicate to caller operation was sync */
- *posted = 0;
- }
- return 0;
-
-err:
- if (allocated) {
- odp_packet_free(param->out_pkt);
- param->out_pkt = ODP_PACKET_INVALID;
- }
-
- return -1;
-}
-
-static void ODP_UNUSED openssl_thread_id(CRYPTO_THREADID ODP_UNUSED *id)
-{
- CRYPTO_THREADID_set_numeric(id, odp_thread_id());
-}
-
-static void ODP_UNUSED openssl_lock(int mode, int n,
- const char *file ODP_UNUSED,
- int line ODP_UNUSED)
-{
- if (mode & CRYPTO_LOCK)
- odp_ticketlock_lock((odp_ticketlock_t *)
- &global->openssl_lock[n]);
- else
- odp_ticketlock_unlock((odp_ticketlock_t *)
- &global->openssl_lock[n]);
-}
-
-int
-odp_crypto_init_global(void)
-{
- size_t mem_size;
- odp_shm_t shm;
- int idx;
- int nlocks = CRYPTO_num_locks();
-
- /* Calculate the memory size we need */
- mem_size = sizeof(*global);
- mem_size += (MAX_SESSIONS * sizeof(odp_crypto_generic_session_t));
- mem_size += nlocks * sizeof(odp_ticketlock_t);
-
- /* Allocate our globally shared memory */
- shm = odp_shm_reserve("crypto_pool", mem_size,
- ODP_CACHE_LINE_SIZE, 0);
-
- global = odp_shm_addr(shm);
-
- /* Clear it out */
- memset(global, 0, mem_size);
-
- /* Initialize free list and lock */
- for (idx = 0; idx < MAX_SESSIONS; idx++) {
- global->sessions[idx].next = global->free;
- global->free = &global->sessions[idx];
- }
- odp_spinlock_init(&global->lock);
-
- if (nlocks > 0) {
- global->openssl_lock =
- (odp_ticketlock_t **)&global->sessions[MAX_SESSIONS];
-
- for (idx = 0; idx < nlocks; idx++)
- odp_ticketlock_init((odp_ticketlock_t *)
- &global->openssl_lock[idx]);
-
- CRYPTO_THREADID_set_callback(openssl_thread_id);
- CRYPTO_set_locking_callback(openssl_lock);
- }
-
- return 0;
-}
-
-int odp_crypto_term_global(void)
-{
- int rc = 0;
- int ret;
- int count = 0;
- odp_crypto_generic_session_t *session;
-
- for (session = global->free; session != NULL; session = session->next)
- count++;
- if (count != MAX_SESSIONS) {
- ODP_ERR("crypto sessions still active\n");
- rc = -1;
- }
-
- CRYPTO_set_locking_callback(NULL);
- CRYPTO_set_id_callback(NULL);
-
- ret = odp_shm_free(odp_shm_lookup("crypto_pool"));
- if (ret < 0) {
- ODP_ERR("shm free failed for crypto_pool\n");
- rc = -1;
- }
-
- return rc;
-}
-
-odp_random_kind_t odp_random_max_kind(void)
-{
- return ODP_RANDOM_CRYPTO;
-}
-
-int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind)
-{
- int rc;
-
- switch (kind) {
- case ODP_RANDOM_BASIC:
- RAND_pseudo_bytes(buf, len);
- return len;
-
- case ODP_RANDOM_CRYPTO:
- rc = RAND_bytes(buf, len);
- return (1 == rc) ? (int)len /*success*/: -1 /*failure*/;
-
- case ODP_RANDOM_TRUE:
- default:
- return -1;
- }
-}
-
-int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
-{
- union {
- uint32_t rand_word;
- uint8_t rand_byte[4];
- } u;
- uint32_t i = 0, j;
- uint32_t seed32 = (*seed) & 0xffffffff;
-
- while (i < len) {
- u.rand_word = rand_r(&seed32);
-
- for (j = 0; j < 4 && i < len; j++, i++)
- *buf++ = u.rand_byte[j];
- }
-
- *seed = seed32;
- return len;
-}
-
-odp_crypto_compl_t odp_crypto_compl_from_event(odp_event_t ev)
-{
- /* This check not mandated by the API specification */
- if (odp_event_type(ev) != ODP_EVENT_CRYPTO_COMPL)
- ODP_ABORT("Event not a crypto completion");
- return (odp_crypto_compl_t)ev;
-}
-
-odp_event_t odp_crypto_compl_to_event(odp_crypto_compl_t completion_event)
-{
- return (odp_event_t)completion_event;
-}
-
-void
-odp_crypto_compl_result(odp_crypto_compl_t completion_event,
- odp_crypto_op_result_t *result)
-{
- odp_event_t ev = odp_crypto_compl_to_event(completion_event);
- odp_crypto_generic_op_result_t *op_result;
-
- op_result = get_op_result_from_event(ev);
-
- if (OP_RESULT_MAGIC != op_result->magic)
- ODP_ABORT();
-
- memcpy(result, &op_result->result, sizeof(*result));
-}
-
-void
-odp_crypto_compl_free(odp_crypto_compl_t completion_event)
-{
- _odp_buffer_event_type_set(
- odp_buffer_from_event((odp_event_t)completion_event),
- ODP_EVENT_PACKET);
-}
-
-void odp_crypto_session_param_init(odp_crypto_session_param_t *param)
-{
- memset(param, 0, sizeof(odp_crypto_session_param_t));
-}
-
-uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl)
-{
- return (uint64_t)hdl;
-}
-
-uint64_t odp_crypto_compl_to_u64(odp_crypto_compl_t hdl)
-{
- return _odp_pri(hdl);
-}
diff --git a/platform/linux-generic/odp_crypto_api.c b/platform/linux-generic/odp_crypto_api.c
new file mode 100644
index 000000000..aaa167044
--- /dev/null
+++ b/platform/linux-generic/odp_crypto_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/crypto.h>
+
+/* Non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/crypto_inlines.h>
diff --git a/platform/linux-generic/odp_crypto_ipsecmb.c b/platform/linux-generic/odp_crypto_ipsecmb.c
new file mode 100644
index 000000000..917b5ff14
--- /dev/null
+++ b/platform/linux-generic/odp_crypto_ipsecmb.c
@@ -0,0 +1,893 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+#include <odp/autoheader_internal.h>
+
+#include <odp/api/crypto.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/debug.h>
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/hints.h>
+
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp/api/plat/thread_inlines.h>
+
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_packet_internal.h>
+
+#include <ipsec-mb.h>
+
+#define MAX_SESSIONS 4000
+/* Length in bytes */
+#define IPSEC_MB_CRYPTO_MAX_CIPHER_KEY_LENGTH 32
+#define IPSEC_MB_CRYPTO_MAX_AUTH_KEY_LENGTH 32
+#define IPSEC_MB_CRYPTO_MAX_DATA_LENGTH 65536
+#define ZUC_DIGEST_LENGTH 4
+#define SNOW3G_DIGEST_LENGTH 4
+
+#define ODP_CRYPTO_IPSEC_MB_SHM_NAME "_odp_crypto_ipsecmb"
+/*
+ * Cipher algorithm capabilities
+ *
+ * Keep sorted: first by key length, then by IV length
+ */
+static const odp_crypto_cipher_capability_t cipher_capa_null[] = {
+{.key_len = 0, .iv_len = 0} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_zuc_eea3[] = {
+{.key_len = 16, .iv_len = 16},
+{.key_len = 32, .iv_len = 25} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_snow3g_uea2[] = {
+{.key_len = 16, .iv_len = 16} };
+
+/*
+ * Authentication algorithm capabilities
+ *
+ * Keep sorted: first by digest length, then by key length
+ */
+static const odp_crypto_auth_capability_t auth_capa_null[] = {
+{.digest_len = 0, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_zuc_eia3[] = {
+{.digest_len = 4, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 16},
+{.digest_len = 4, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 25} };
+
+static const odp_crypto_auth_capability_t auth_capa_snow3g_uia2[] = {
+{.digest_len = SNOW3G_DIGEST_LENGTH, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 16} };
+
+/** Forward declaration of session structure */
+typedef struct odp_crypto_generic_session_t odp_crypto_generic_session_t;
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef odp_crypto_alg_err_t (*crypto_func_t)(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session);
+
+/**
+ * Per crypto session data structure
+ */
+struct odp_crypto_generic_session_t {
+ odp_crypto_generic_session_t *next;
+
+ /* Session creation parameters */
+ odp_crypto_session_param_t p;
+
+ odp_bool_t do_cipher_first;
+ uint8_t null_crypto_enable :1;
+
+ struct {
+ union {
+ uint8_t key_data[IPSEC_MB_CRYPTO_MAX_CIPHER_KEY_LENGTH];
+ snow3g_key_schedule_t key_sched;
+ };
+ crypto_func_t func;
+ } cipher;
+
+ struct {
+ union {
+ uint8_t key[IPSEC_MB_CRYPTO_MAX_AUTH_KEY_LENGTH];
+ snow3g_key_schedule_t key_sched;
+ };
+ crypto_func_t func;
+ } auth;
+
+ unsigned int idx;
+};
+
+typedef struct odp_crypto_global_s odp_crypto_global_t;
+
+struct odp_crypto_global_s {
+ odp_spinlock_t lock;
+ odp_crypto_generic_session_t *free;
+ odp_crypto_generic_session_t sessions[MAX_SESSIONS];
+};
+
+static odp_crypto_global_t *global;
+
+typedef struct crypto_local_t {
+ uint8_t buffer[IPSEC_MB_CRYPTO_MAX_DATA_LENGTH];
+ IMB_MGR *mb_mgr;
+} crypto_local_t;
+
+static __thread crypto_local_t local;
+
+static
+odp_crypto_generic_session_t *alloc_session(void)
+{
+ odp_crypto_generic_session_t *session = NULL;
+
+ odp_spinlock_lock(&global->lock);
+ session = global->free;
+ if (session) {
+ global->free = session->next;
+ session->next = NULL;
+ }
+ odp_spinlock_unlock(&global->lock);
+
+ if (!session)
+ return NULL;
+
+ session->idx = session - global->sessions;
+
+ return session;
+}
+
+static
+void free_session(odp_crypto_generic_session_t *session)
+{
+ odp_spinlock_lock(&global->lock);
+ session->next = global->free;
+ global->free = session;
+ odp_spinlock_unlock(&global->lock);
+}
+
+static odp_crypto_alg_err_t
+null_crypto_routine(odp_packet_t pkt ODP_UNUSED,
+ const odp_crypto_packet_op_param_t *param ODP_UNUSED,
+ odp_crypto_generic_session_t *session ODP_UNUSED)
+{
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t ipsec_mb_cipher_op(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ IMB_MGR *mb_mgr = local.mb_mgr;
+ const uint8_t *iv_ptr = param->cipher_iv_ptr;
+ uint32_t in_pos = param->cipher_range.offset;
+ uint32_t in_len = param->cipher_range.length;
+
+ _ODP_ASSERT(iv_ptr != NULL);
+
+ uint32_t seg_len = 0;
+ uint8_t *data = odp_packet_offset(pkt, in_pos, &seg_len, NULL);
+
+ if (odp_unlikely(seg_len < in_len)) {
+ if (odp_unlikely(in_len > IPSEC_MB_CRYPTO_MAX_DATA_LENGTH))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ /* Packet is segmented within the cipher range. Copy the cipher
+ * range to a contiguous buffer. */
+ odp_packet_copy_to_mem(pkt, in_pos, in_len, local.buffer);
+
+ data = local.buffer;
+ }
+
+ if (session->p.cipher_alg == ODP_CIPHER_ALG_ZUC_EEA3) {
+ if (session->p.cipher_key.length == 16) {
+ /* ZUC128 EEA3 */
+ IMB_ZUC_EEA3_1_BUFFER(mb_mgr, session->cipher.key_data,
+ iv_ptr,
+ data,
+ data,
+ in_len);
+ } else {
+ /* Only 16 and 32 byte keys are supported
+ * ZUC256 EEA3 */
+ IMB_ZUC256_EEA3_1_BUFFER(mb_mgr, session->cipher.key_data,
+ iv_ptr,
+ data,
+ data,
+ in_len);
+ }
+ } else {
+ /* Only ODP_CIPHER_ALG_SNOW3G_UEA2 */
+ IMB_SNOW3G_F8_1_BUFFER(mb_mgr, &session->cipher.key_sched,
+ iv_ptr,
+ data,
+ data,
+ in_len);
+ }
+
+ if (odp_unlikely(imb_get_errno(mb_mgr) != 0))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ if (odp_unlikely(seg_len < in_len))
+ odp_packet_copy_from_mem(pkt, in_pos, in_len, data);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_zuc_eea3_param(odp_crypto_generic_session_t *session)
+{
+ if (!((16 == session->p.cipher_key.length &&
+ 16 == session->p.cipher_iv_len) ||
+ (32 == session->p.cipher_key.length &&
+ 25 == session->p.cipher_iv_len)))
+ return -1;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ session->cipher.func = ipsec_mb_cipher_op;
+
+ return 0;
+}
+
+static
+odp_crypto_alg_err_t auth_ipsec_mb_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ IMB_MGR *mb_mgr = local.mb_mgr;
+ const uint8_t *iv_ptr = param->auth_iv_ptr;
+ uint32_t in_pos = param->auth_range.offset;
+ uint32_t in_len = param->auth_range.length;
+ uint32_t auth_tag;
+
+ _ODP_ASSERT(iv_ptr != NULL);
+
+ uint32_t seg_len = 0;
+ uint8_t *data = odp_packet_offset(pkt, in_pos, &seg_len, NULL);
+
+ if (odp_unlikely(seg_len < in_len)) {
+ if (odp_unlikely(in_len > IPSEC_MB_CRYPTO_MAX_DATA_LENGTH))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ /* Packet is segmented within the auth range. Copy the auth
+ * range to a contiguous buffer. */
+ odp_packet_copy_to_mem(pkt, in_pos, in_len, local.buffer);
+
+ data = local.buffer;
+ }
+
+ if (session->p.auth_alg == ODP_AUTH_ALG_ZUC_EIA3) {
+ if (session->p.auth_key.length == 16) {
+ /* ZUC128 EIA3 */
+ IMB_ZUC_EIA3_1_BUFFER(mb_mgr, session->auth.key,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &auth_tag);
+ } else {
+ /* Only 16 and 32 byte keys are supported
+ * ZUC256 EIA3 */
+ IMB_ZUC256_EIA3_1_BUFFER(mb_mgr, session->auth.key,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &auth_tag);
+ }
+ } else {
+ /* Only ODP_AUTH_ALG_SNOW3G_UIA2 */
+ IMB_SNOW3G_F9_1_BUFFER(mb_mgr, &session->auth.key_sched,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &auth_tag);
+ }
+
+ if (odp_unlikely(imb_get_errno(mb_mgr) != 0))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len,
+ &auth_tag);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_ipsec_mb_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ IMB_MGR *mb_mgr = local.mb_mgr;
+ const uint8_t *iv_ptr = param->auth_iv_ptr;
+ uint32_t in_pos = param->auth_range.offset;
+ uint32_t in_len = param->auth_range.length;
+ uint32_t bytes = ZUC_DIGEST_LENGTH;
+ uint32_t hash_in;
+ uint32_t hash_out;
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, &hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ _ODP_ASSERT(iv_ptr != NULL);
+
+ uint32_t seg_len = 0;
+ uint8_t *data = odp_packet_offset(pkt, in_pos, &seg_len, NULL);
+
+ if (odp_unlikely(seg_len < in_len)) {
+ if (odp_unlikely(in_len > IPSEC_MB_CRYPTO_MAX_DATA_LENGTH))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ /* Packet is segmented within the auth range. Copy the auth
+ * range to a contiguous buffer. */
+ odp_packet_copy_to_mem(pkt, in_pos, in_len, local.buffer);
+
+ data = local.buffer;
+ }
+
+ if (session->p.auth_alg == ODP_AUTH_ALG_ZUC_EIA3) {
+ if (session->p.auth_key.length == 16) {
+ /* ZUC128 EIA3 */
+ IMB_ZUC_EIA3_1_BUFFER(mb_mgr, session->auth.key,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &hash_out);
+ } else {
+ /* Only 16 and 32 byte keys are supported
+ * ZUC256 EIA3 */
+ IMB_ZUC256_EIA3_1_BUFFER(mb_mgr, session->auth.key,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &hash_out);
+ }
+ } else {
+ /* Only ODP_AUTH_ALG_SNOW3G_UIA2 */
+ IMB_SNOW3G_F9_1_BUFFER(mb_mgr, &session->auth.key_sched,
+ iv_ptr,
+ data,
+ param->auth_range.length * 8,
+ &hash_out);
+ }
+
+ if (odp_unlikely(imb_get_errno(mb_mgr) != 0))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+
+ /* Verify match */
+ if (hash_in != hash_out)
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_auth_zuc_eia3_param(odp_crypto_generic_session_t *session)
+{
+ if (!((16 == session->p.auth_key.length &&
+ 16 == session->p.auth_iv_len) ||
+ (32 == session->p.auth_key.length &&
+ 25 == session->p.auth_iv_len)))
+ return -1;
+
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_ipsec_mb_gen;
+ else
+ session->auth.func = auth_ipsec_mb_check;
+
+ if (session->p.auth_digest_len != ZUC_DIGEST_LENGTH)
+ return -1;
+
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ return 0;
+}
+
+static int process_snow3g_uea2_param(odp_crypto_generic_session_t *session)
+{
+ if (!(16 == session->p.cipher_key.length &&
+ 16 == session->p.cipher_iv_len))
+ return -1;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ session->cipher.func = ipsec_mb_cipher_op;
+
+ return IMB_SNOW3G_INIT_KEY_SCHED(local.mb_mgr, session->p.cipher_key.data,
+ &session->cipher.key_sched);
+}
+
+static int process_auth_snow3g_uia2_param(odp_crypto_generic_session_t *session)
+{
+ if (!(16 == session->p.auth_key.length &&
+ 16 == session->p.auth_iv_len))
+ return -1;
+
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_ipsec_mb_gen;
+ else
+ session->auth.func = auth_ipsec_mb_check;
+
+ if (session->p.auth_digest_len != SNOW3G_DIGEST_LENGTH)
+ return -1;
+
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ return IMB_SNOW3G_INIT_KEY_SCHED(local.mb_mgr, session->p.auth_key.data,
+ &session->auth.key_sched);
+}
+
+int odp_crypto_capability(odp_crypto_capability_t *capa)
+{
+ if (NULL == capa)
+ return -1;
+
+ memset(capa, 0, sizeof(odp_crypto_capability_t));
+
+ capa->sync_mode = ODP_SUPPORT_PREFERRED;
+ capa->async_mode = ODP_SUPPORT_YES;
+ capa->queue_type_plain = 1;
+ capa->queue_type_sched = 1;
+
+ capa->ciphers.bit.null = 1;
+ capa->auths.bit.null = 1;
+
+ capa->ciphers.bit.zuc_eea3 = 1;
+ capa->auths.bit.zuc_eia3 = 1;
+
+ capa->ciphers.bit.snow3g_uea2 = 1;
+ capa->auths.bit.snow3g_uia2 = 1;
+
+ capa->max_sessions = MAX_SESSIONS;
+
+ return 0;
+}
+
+int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
+ odp_crypto_cipher_capability_t dst[],
+ int num_copy)
+{
+ const odp_crypto_cipher_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_cipher_capability_t);
+
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ src = cipher_capa_null;
+ num = sizeof(cipher_capa_null) / size;
+ break;
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ src = cipher_capa_zuc_eea3;
+ num = sizeof(cipher_capa_zuc_eea3) / size;
+ break;
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ src = cipher_capa_snow3g_uea2;
+ num = sizeof(cipher_capa_snow3g_uea2) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int odp_crypto_auth_capability(odp_auth_alg_t auth,
+ odp_crypto_auth_capability_t dst[], int num_copy)
+{
+ const odp_crypto_auth_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_auth_capability_t);
+
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ src = auth_capa_null;
+ num = sizeof(auth_capa_null) / size;
+ break;
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ src = auth_capa_zuc_eia3;
+ num = sizeof(auth_capa_zuc_eia3) / size;
+ break;
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ src = auth_capa_snow3g_uia2;
+ num = sizeof(auth_capa_snow3g_uia2) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int
+odp_crypto_session_create(const odp_crypto_session_param_t *param,
+ odp_crypto_session_t *session_out,
+ odp_crypto_ses_create_err_t *status)
+{
+ int rc = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ /* Dummy output to avoid compiler warning about uninitialized
+ * variables */
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ if (param->cipher_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->auth_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP ||
+ param->op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ *status = ODP_CRYPTO_SES_ERR_PARAMS;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ session = alloc_session();
+ if (NULL == session) {
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ goto err;
+ }
+
+ session->p = *param;
+
+ /* Derive order */
+ if (ODP_CRYPTO_OP_ENCODE == param->op)
+ session->do_cipher_first = param->auth_cipher_text;
+ else
+ session->do_cipher_first = !param->auth_cipher_text;
+
+ /* Process based on cipher */
+ switch (param->cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ session->cipher.func = null_crypto_routine;
+ rc = 0;
+ break;
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ rc = process_zuc_eea3_param(session);
+ break;
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ rc = process_snow3g_uea2_param(session);
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (param->null_crypto_enable && param->op_mode == ODP_CRYPTO_SYNC)
+ rc = -1;
+ session->null_crypto_enable = !!param->null_crypto_enable;
+
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Process based on auth */
+ switch (param->auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ session->auth.func = null_crypto_routine;
+ rc = 0;
+ break;
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ rc = process_auth_zuc_eia3_param(session);
+ break;
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ rc = process_auth_snow3g_uia2_param(session);
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ goto err;
+ }
+
+ *session_out = (intptr_t)session;
+ *status = ODP_CRYPTO_SES_ERR_NONE;
+ return 0;
+
+err:
+ /* error status should be set at this moment */
+ if (session != NULL)
+ free_session(session);
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+}
+
+int odp_crypto_session_destroy(odp_crypto_session_t session)
+{
+ odp_crypto_generic_session_t *generic;
+
+ generic = (odp_crypto_generic_session_t *)(intptr_t)session;
+ memset(generic, 0, sizeof(*generic));
+ free_session(generic);
+ return 0;
+}
+
+int _odp_crypto_init_global(void)
+{
+ size_t mem_size;
+ odp_shm_t shm;
+ int idx;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_PRINT("\nODP crypto is DISABLED\n");
+ return 0;
+ }
+
+ /* Calculate the memory size we need */
+ mem_size = sizeof(odp_crypto_global_t);
+
+ /* Allocate our globally shared memory */
+ shm = odp_shm_reserve(ODP_CRYPTO_IPSEC_MB_SHM_NAME, mem_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (ODP_SHM_INVALID == shm) {
+ _ODP_ERR("unable to allocate crypto pool\n");
+ return -1;
+ }
+
+ global = odp_shm_addr(shm);
+
+ /* Clear it out */
+ memset(global, 0, mem_size);
+
+ /* Initialize free list and lock */
+ for (idx = 0; idx < MAX_SESSIONS; idx++) {
+ global->sessions[idx].next = global->free;
+ global->free = &global->sessions[idx];
+ }
+ odp_spinlock_init(&global->lock);
+
+ return 0;
+}
+
+int _odp_crypto_term_global(void)
+{
+ int rc = 0;
+ int ret;
+ int count = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (session = global->free; session != NULL; session = session->next)
+ count++;
+ if (count != MAX_SESSIONS) {
+ _ODP_ERR("crypto sessions still active\n");
+ rc = -1;
+ }
+
+ ret = odp_shm_free(odp_shm_lookup(ODP_CRYPTO_IPSEC_MB_SHM_NAME));
+ if (ret < 0) {
+ _ODP_ERR("shm free failed for %s\n", ODP_CRYPTO_IPSEC_MB_SHM_NAME);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int _odp_crypto_init_local(void)
+{
+ uint64_t flags = 0;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ memset(&local, 0, sizeof(local));
+
+ local.mb_mgr = alloc_mb_mgr(flags);
+ if (local.mb_mgr == NULL)
+ return -1;
+
+ init_mb_mgr_auto(local.mb_mgr, NULL);
+
+ return 0;
+}
+
+int _odp_crypto_term_local(void)
+{
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ free_mb_mgr(local.mb_mgr);
+ return 0;
+}
+
+void odp_crypto_session_param_init(odp_crypto_session_param_t *param)
+{
+ memset(param, 0, sizeof(odp_crypto_session_param_t));
+}
+
+uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl)
+{
+ return (uint64_t)hdl;
+}
+
+static int copy_data_and_metadata(odp_packet_t dst, odp_packet_t src)
+{
+ int md_copy;
+ int rc;
+
+ md_copy = _odp_packet_copy_md_possible(odp_packet_pool(dst),
+ odp_packet_pool(src));
+ if (odp_unlikely(md_copy < 0)) {
+ _ODP_ERR("Unable to copy packet metadata\n");
+ return -1;
+ }
+
+ rc = odp_packet_copy_from_pkt(dst, 0, src, 0, odp_packet_len(src));
+ if (odp_unlikely(rc < 0)) {
+ _ODP_ERR("Unable to copy packet data\n");
+ return -1;
+ }
+
+ _odp_packet_copy_md(packet_hdr(dst), packet_hdr(src), md_copy);
+ return 0;
+}
+
+static odp_packet_t get_output_packet(const odp_crypto_generic_session_t *session,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out)
+{
+ int rc;
+
+ if (odp_likely(pkt_in == pkt_out))
+ return pkt_out;
+
+ if (pkt_out == ODP_PACKET_INVALID) {
+ odp_pool_t pool = session->p.output_pool;
+
+ _ODP_ASSERT(pool != ODP_POOL_INVALID);
+ if (pool == odp_packet_pool(pkt_in)) {
+ pkt_out = pkt_in;
+ } else {
+ pkt_out = odp_packet_copy(pkt_in, pool);
+ if (odp_likely(pkt_out != ODP_PACKET_INVALID))
+ odp_packet_free(pkt_in);
+ }
+ return pkt_out;
+ }
+ rc = copy_data_and_metadata(pkt_out, pkt_in);
+ if (odp_unlikely(rc < 0))
+ return ODP_PACKET_INVALID;
+
+ odp_packet_free(pkt_in);
+ return pkt_out;
+}
+
+static
+int crypto_int(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_alg_err_t rc_cipher = ODP_CRYPTO_ALG_ERR_NONE;
+ odp_crypto_alg_err_t rc_auth = ODP_CRYPTO_ALG_ERR_NONE;
+ odp_crypto_generic_session_t *session;
+ odp_packet_t out_pkt;
+ odp_crypto_packet_result_t *op_result;
+
+ session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ out_pkt = pkt_in;
+ } else {
+ out_pkt = get_output_packet(session, pkt_in, *pkt_out);
+ if (odp_unlikely(out_pkt == ODP_PACKET_INVALID))
+ return -1;
+ }
+
+ if (odp_unlikely(session->null_crypto_enable && param->null_crypto))
+ goto out;
+
+ /* Invoke the crypto function */
+ if (session->do_cipher_first) {
+ rc_cipher = session->cipher.func(out_pkt, param, session);
+ rc_auth = session->auth.func(out_pkt, param, session);
+ } else {
+ rc_auth = session->auth.func(out_pkt, param, session);
+ rc_cipher = session->cipher.func(out_pkt, param, session);
+ }
+
+out:
+ packet_subtype_set(out_pkt, ODP_EVENT_PACKET_CRYPTO);
+ op_result = &packet_hdr(out_pkt)->crypto_op_result;
+ op_result->cipher_status.alg_err = rc_cipher;
+ op_result->auth_status.alg_err = rc_auth;
+
+ /* Synchronous, simply return results */
+ *pkt_out = out_pkt;
+
+ return 0;
+}
+
+int odp_crypto_op(const odp_packet_t pkt_in[],
+ odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ int i, rc;
+ odp_crypto_generic_session_t *session;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_SYNC == session->p.op_mode);
+
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ if (rc < 0)
+ break;
+ }
+
+ return i;
+}
+
+int odp_crypto_op_enq(const odp_packet_t pkt_in[],
+ const odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ odp_packet_t pkt;
+ odp_event_t event;
+ odp_crypto_generic_session_t *session;
+ int i, rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_ASYNC == session->p.op_mode);
+ _ODP_ASSERT(ODP_QUEUE_INVALID != session->p.compl_queue);
+
+ if (session->p.op_type != ODP_CRYPTO_OP_TYPE_BASIC)
+ pkt = pkt_out[i];
+
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ if (rc < 0)
+ break;
+
+ event = odp_packet_to_event(pkt);
+ if (odp_queue_enq(session->p.compl_queue, event)) {
+ odp_event_free(event);
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/platform/linux-generic/odp_crypto_null.c b/platform/linux-generic/odp_crypto_null.c
new file mode 100644
index 000000000..91e65a5a8
--- /dev/null
+++ b/platform/linux-generic/odp_crypto_null.c
@@ -0,0 +1,508 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+#include <odp/api/crypto.h>
+#include <odp_init_internal.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/sync.h>
+#include <odp/api/debug.h>
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp/api/hints.h>
+#include <odp/api/random.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp_packet_internal.h>
+#include <odp/api/plat/queue_inlines.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/event_inlines.h>
+
+#define MAX_SESSIONS 32
+
+/*
+ * Cipher algorithm capabilities
+ *
+ * Keep sorted: first by key length, then by IV length
+ */
+static const odp_crypto_cipher_capability_t cipher_capa_null[] = {
+{.key_len = 0, .iv_len = 0} };
+
+/*
+ * Authentication algorithm capabilities
+ *
+ * Keep sorted: first by digest length, then by key length
+ */
+static const odp_crypto_auth_capability_t auth_capa_null[] = {
+{.digest_len = 0, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+/** Forward declaration of session structure */
+typedef struct odp_crypto_generic_session_t odp_crypto_generic_session_t;
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef
+odp_crypto_alg_err_t (*crypto_func_t)(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session);
+typedef void (*crypto_init_func_t)(odp_crypto_generic_session_t *session);
+
+/**
+ * Per crypto session data structure
+ */
+struct odp_crypto_generic_session_t {
+ odp_crypto_generic_session_t *next;
+
+ /* Session creation parameters */
+ odp_crypto_session_param_t p;
+
+ unsigned int idx;
+};
+
+typedef struct odp_crypto_global_s odp_crypto_global_t;
+
+struct odp_crypto_global_s {
+ odp_spinlock_t lock;
+ odp_crypto_generic_session_t *free;
+ odp_crypto_generic_session_t sessions[MAX_SESSIONS];
+
+ /* These flags are cleared at alloc_session() */
+ uint8_t ctx_valid[ODP_THREAD_COUNT_MAX][MAX_SESSIONS];
+
+ odp_ticketlock_t openssl_lock[];
+};
+
+static odp_crypto_global_t *global;
+
+static
+odp_crypto_generic_session_t *alloc_session(void)
+{
+ odp_crypto_generic_session_t *session = NULL;
+ unsigned int i;
+
+ odp_spinlock_lock(&global->lock);
+ session = global->free;
+ if (session) {
+ global->free = session->next;
+ session->next = NULL;
+ }
+ odp_spinlock_unlock(&global->lock);
+
+ if (!session)
+ return NULL;
+
+ session->idx = session - global->sessions;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ global->ctx_valid[i][session->idx] = 0;
+
+ return session;
+}
+
+static
+void free_session(odp_crypto_generic_session_t *session)
+{
+ odp_spinlock_lock(&global->lock);
+ session->next = global->free;
+ global->free = session;
+ odp_spinlock_unlock(&global->lock);
+}
+
+int odp_crypto_capability(odp_crypto_capability_t *capa)
+{
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ return -1;
+ }
+
+ if (NULL == capa)
+ return -1;
+
+ /* Initialize crypto capability structure */
+ memset(capa, 0, sizeof(odp_crypto_capability_t));
+
+ capa->sync_mode = ODP_SUPPORT_PREFERRED;
+ capa->async_mode = ODP_SUPPORT_YES;
+ capa->queue_type_plain = 1;
+ capa->queue_type_sched = 1;
+
+ capa->ciphers.bit.null = 1;
+
+ capa->auths.bit.null = 1;
+
+ capa->max_sessions = MAX_SESSIONS;
+
+ return 0;
+}
+
+int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
+ odp_crypto_cipher_capability_t dst[],
+ int num_copy)
+{
+ const odp_crypto_cipher_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_cipher_capability_t);
+
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ src = cipher_capa_null;
+ num = sizeof(cipher_capa_null) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int odp_crypto_auth_capability(odp_auth_alg_t auth,
+ odp_crypto_auth_capability_t dst[], int num_copy)
+{
+ const odp_crypto_auth_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_auth_capability_t);
+
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ src = auth_capa_null;
+ num = sizeof(auth_capa_null) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int
+odp_crypto_session_create(const odp_crypto_session_param_t *param,
+ odp_crypto_session_t *session_out,
+ odp_crypto_ses_create_err_t *status)
+{
+ int rc;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ /* Dummy output to avoid compiler warning about uninitialized
+ * variables */
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ if (param->cipher_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->auth_range_in_bits) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP ||
+ param->op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ *status = ODP_CRYPTO_SES_ERR_PARAMS;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ /* Allocate memory for this session */
+ session = alloc_session();
+ if (NULL == session) {
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ goto err;
+ }
+
+ /* Copy parameters */
+ session->p = *param;
+
+ /* Process based on cipher */
+ switch (param->cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ rc = 0;
+ break;
+ default:
+ rc = -1;
+ }
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Process based on auth */
+ switch (param->auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ rc = 0;
+ break;
+ default:
+ rc = -1;
+ }
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ goto err;
+ }
+
+ /* We're happy */
+ *session_out = (intptr_t)session;
+ *status = ODP_CRYPTO_SES_ERR_NONE;
+ return 0;
+
+err:
+ /* error status should be set at this moment */
+ if (session != NULL)
+ free_session(session);
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+}
+
+int odp_crypto_session_destroy(odp_crypto_session_t session)
+{
+ odp_crypto_generic_session_t *generic;
+
+ generic = (odp_crypto_generic_session_t *)(intptr_t)session;
+ memset(generic, 0, sizeof(*generic));
+ free_session(generic);
+ return 0;
+}
+
+int
+_odp_crypto_init_global(void)
+{
+ size_t mem_size;
+ odp_shm_t shm;
+ int idx;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_PRINT("\nODP crypto is DISABLED\n");
+ return 0;
+ }
+
+ /* Calculate the memory size we need */
+ mem_size = sizeof(odp_crypto_global_t);
+
+ /* Allocate our globally shared memory */
+ shm = odp_shm_reserve("_odp_crypto_null_global", mem_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (ODP_SHM_INVALID == shm) {
+ _ODP_ERR("unable to allocate crypto pool\n");
+ return -1;
+ }
+
+ global = odp_shm_addr(shm);
+
+ /* Clear it out */
+ memset(global, 0, mem_size);
+
+ /* Initialize free list and lock */
+ for (idx = 0; idx < MAX_SESSIONS; idx++) {
+ global->sessions[idx].next = global->free;
+ global->free = &global->sessions[idx];
+ }
+ odp_spinlock_init(&global->lock);
+
+ return 0;
+}
+
+int _odp_crypto_term_global(void)
+{
+ int rc = 0;
+ int ret;
+ int count = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (session = global->free; session != NULL; session = session->next)
+ count++;
+ if (count != MAX_SESSIONS) {
+ _ODP_ERR("crypto sessions still active\n");
+ rc = -1;
+ }
+
+ ret = odp_shm_free(odp_shm_lookup("_odp_crypto_null_global"));
+ if (ret < 0) {
+ _ODP_ERR("shm free failed for _odp_crypto_pool_null\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int _odp_crypto_init_local(void)
+{
+ return 0;
+}
+
+int _odp_crypto_term_local(void)
+{
+ return 0;
+}
+
+void odp_crypto_session_param_init(odp_crypto_session_param_t *param)
+{
+ memset(param, 0, sizeof(odp_crypto_session_param_t));
+}
+
+uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl)
+{
+ return (uint64_t)hdl;
+}
+
+static int copy_data_and_metadata(odp_packet_t dst, odp_packet_t src)
+{
+ int md_copy;
+ int rc;
+
+ md_copy = _odp_packet_copy_md_possible(odp_packet_pool(dst),
+ odp_packet_pool(src));
+ if (odp_unlikely(md_copy < 0)) {
+ _ODP_ERR("Unable to copy packet metadata\n");
+ return -1;
+ }
+
+ rc = odp_packet_copy_from_pkt(dst, 0, src, 0, odp_packet_len(src));
+ if (odp_unlikely(rc < 0)) {
+ _ODP_ERR("Unable to copy packet data\n");
+ return -1;
+ }
+
+ _odp_packet_copy_md(packet_hdr(dst), packet_hdr(src), md_copy);
+ return 0;
+}
+
+static odp_packet_t get_output_packet(const odp_crypto_generic_session_t *session,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out)
+{
+ int rc;
+
+ if (odp_likely(pkt_in == pkt_out))
+ return pkt_out;
+
+ if (pkt_out == ODP_PACKET_INVALID) {
+ odp_pool_t pool = session->p.output_pool;
+
+ _ODP_ASSERT(pool != ODP_POOL_INVALID);
+ if (pool == odp_packet_pool(pkt_in)) {
+ pkt_out = pkt_in;
+ } else {
+ pkt_out = odp_packet_copy(pkt_in, pool);
+ if (odp_likely(pkt_out != ODP_PACKET_INVALID))
+ odp_packet_free(pkt_in);
+ }
+ return pkt_out;
+ }
+ rc = copy_data_and_metadata(pkt_out, pkt_in);
+ if (odp_unlikely(rc < 0))
+ return ODP_PACKET_INVALID;
+
+ odp_packet_free(pkt_in);
+ return pkt_out;
+}
+
+static
+int crypto_int(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_generic_session_t *session;
+ odp_packet_t out_pkt;
+ odp_crypto_packet_result_t *op_result;
+
+ session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ out_pkt = pkt_in;
+ } else {
+ out_pkt = get_output_packet(session, pkt_in, *pkt_out);
+ if (odp_unlikely(out_pkt == ODP_PACKET_INVALID))
+ return -1;
+ }
+
+ /* Fill in result */
+ packet_subtype_set(out_pkt, ODP_EVENT_PACKET_CRYPTO);
+ op_result = &packet_hdr(out_pkt)->crypto_op_result;
+ op_result->cipher_status.alg_err = ODP_CRYPTO_ALG_ERR_NONE;
+ op_result->auth_status.alg_err = ODP_CRYPTO_ALG_ERR_NONE;
+
+ /* Synchronous, simply return results */
+ *pkt_out = out_pkt;
+
+ return 0;
+}
+
+int odp_crypto_op(const odp_packet_t pkt_in[],
+ odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ int i, rc;
+ odp_crypto_generic_session_t *session;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_SYNC == session->p.op_mode);
+
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ if (rc < 0)
+ break;
+ }
+
+ return i;
+}
+
+int odp_crypto_op_enq(const odp_packet_t pkt_in[],
+ const odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+ odp_event_t event;
+ odp_crypto_generic_session_t *session;
+ int i, rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_ASYNC == session->p.op_mode);
+ _ODP_ASSERT(ODP_QUEUE_INVALID != session->p.compl_queue);
+
+ if (session->p.op_type != ODP_CRYPTO_OP_TYPE_BASIC)
+ pkt = pkt_out[i];
+
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ if (rc < 0)
+ break;
+
+ event = odp_packet_to_event(pkt);
+ if (odp_queue_enq(session->p.compl_queue, event)) {
+ odp_event_free(event);
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/platform/linux-generic/odp_crypto_openssl.c b/platform/linux-generic/odp_crypto_openssl.c
new file mode 100644
index 000000000..b67038280
--- /dev/null
+++ b/platform/linux-generic/odp_crypto_openssl.c
@@ -0,0 +1,2830 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+#include <odp/api/crypto.h>
+#include <odp_init_internal.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/sync.h>
+#include <odp/api/debug.h>
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp_debug_internal.h>
+#include <odp/api/hints.h>
+#include <odp/api/random.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_internal.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp_global_data.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/event_inlines.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <openssl/hmac.h>
+#include <openssl/cmac.h>
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_POLY1305)
+#define _ODP_HAVE_CHACHA20_POLY1305 1
+#else
+#define _ODP_HAVE_CHACHA20_POLY1305 0
+#endif
+
+/* Ignore warnings about APIs deprecated in OpenSSL 3.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#define MAX_SESSIONS 4000
+#define AES_BLOCK_SIZE 16
+#define AES_KEY_LENGTH 16
+
+/*
+ * Cipher algorithm capabilities
+ *
+ * Keep sorted: first by key length, then by IV length
+ */
+static const odp_crypto_cipher_capability_t cipher_capa_null[] = {
+{.key_len = 0, .iv_len = 0, .bit_mode = 1} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_trides_cbc[] = {
+{.key_len = 24, .iv_len = 8} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_trides_ecb[] = {
+{.key_len = 24} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_cbc[] = {
+{.key_len = 16, .iv_len = 16},
+{.key_len = 24, .iv_len = 16},
+{.key_len = 32, .iv_len = 16} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_ctr[] = {
+{.key_len = 16, .iv_len = 16},
+{.key_len = 24, .iv_len = 16},
+{.key_len = 32, .iv_len = 16} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_ecb[] = {
+{.key_len = 16},
+{.key_len = 24},
+{.key_len = 32} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_cfb128[] = {
+{.key_len = 16, .iv_len = 16},
+{.key_len = 24, .iv_len = 16},
+{.key_len = 32, .iv_len = 16} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_xts[] = {
+{.key_len = 32, .iv_len = 16},
+{.key_len = 64, .iv_len = 16} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_gcm[] = {
+{.key_len = 16, .iv_len = 12},
+{.key_len = 24, .iv_len = 12},
+{.key_len = 32, .iv_len = 12} };
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_ccm[] = {
+{.key_len = 16, .iv_len = 11},
+{.key_len = 16, .iv_len = 13},
+{.key_len = 24, .iv_len = 11},
+{.key_len = 24, .iv_len = 13},
+{.key_len = 32, .iv_len = 11},
+{.key_len = 32, .iv_len = 13} };
+
+#if _ODP_HAVE_CHACHA20_POLY1305
+static const odp_crypto_cipher_capability_t cipher_capa_chacha20_poly1305[] = {
+{.key_len = 32, .iv_len = 12} };
+#endif
+
+static const odp_crypto_cipher_capability_t cipher_capa_aes_eea2[] = {
+{.key_len = 16, .iv_len = 16, .bit_mode = 1} };
+
+/*
+ * Authentication algorithm capabilities
+ *
+ * Keep sorted: first by digest length, then by key length
+ */
+static const odp_crypto_auth_capability_t auth_capa_null[] = {
+{.digest_len = 0, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0}, .bit_mode = 1} };
+
+static const odp_crypto_auth_capability_t auth_capa_md5_hmac[] = {
+{.digest_len = 12, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha1_hmac[] = {
+{.digest_len = 12, .key_len = 20, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 20, .key_len = 20, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha224_hmac[] = {
+{.digest_len = 28, .key_len = 28, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha256_hmac[] = {
+{.digest_len = 16, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 32, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha384_hmac[] = {
+{.digest_len = 24, .key_len = 48, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 48, .key_len = 48, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha512_hmac[] = {
+{.digest_len = 32, .key_len = 64, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 64, .key_len = 64, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_aes_xcbc[] = {
+{.digest_len = 12, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_aes_gcm[] = {
+{.digest_len = 16, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
+
+static const odp_crypto_auth_capability_t auth_capa_aes_ccm[] = {
+{.digest_len = 8, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
+
+static const odp_crypto_auth_capability_t auth_capa_aes_gmac[] = {
+{.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 12 },
+{.digest_len = 16, .key_len = 24, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 12 },
+{.digest_len = 16, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 12 } };
+
+static const odp_crypto_auth_capability_t auth_capa_aes_cmac[] = {
+{.digest_len = 12, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 12, .key_len = 24, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 12, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 16, .key_len = 24, .aad_len = {.min = 0, .max = 0, .inc = 0} },
+{.digest_len = 16, .key_len = 32, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+#if _ODP_HAVE_CHACHA20_POLY1305
+static const odp_crypto_auth_capability_t auth_capa_chacha20_poly1305[] = {
+{.digest_len = 16, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
+#endif
+
+static const odp_crypto_auth_capability_t auth_capa_aes_eia2[] = {
+{.digest_len = 4, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0},
+ .iv_len = 8} };
+
+static const odp_crypto_auth_capability_t auth_capa_md5[] = {
+{.digest_len = 16, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha1[] = {
+{.digest_len = 20, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha224[] = {
+{.digest_len = 28, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha256[] = {
+{.digest_len = 32, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha384[] = {
+{.digest_len = 48, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+static const odp_crypto_auth_capability_t auth_capa_sha512[] = {
+{.digest_len = 64, .key_len = 0, .aad_len = {.min = 0, .max = 0, .inc = 0} } };
+
+/** Forward declaration of session structure */
+typedef struct odp_crypto_generic_session_t odp_crypto_generic_session_t;
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef
+odp_crypto_alg_err_t (*crypto_func_t)(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session);
+typedef void (*crypto_init_func_t)(odp_crypto_generic_session_t *session);
+
+/**
+ * Per crypto session data structure
+ */
+struct odp_crypto_generic_session_t {
+ odp_crypto_generic_session_t *next;
+
+ /* Session creation parameters */
+ odp_crypto_session_param_t p;
+
+ odp_bool_t do_cipher_first;
+ uint8_t cipher_range_in_bits : 1;
+ uint8_t auth_range_in_bits : 1;
+ uint8_t auth_range_used : 1;
+ uint8_t null_crypto_enable : 1;
+
+ struct {
+ uint8_t key_data[EVP_MAX_KEY_LENGTH];
+ const EVP_CIPHER *evp_cipher;
+ crypto_func_t func;
+ crypto_init_func_t init;
+ } cipher;
+
+ struct {
+ uint8_t key[EVP_MAX_KEY_LENGTH];
+ union {
+ const EVP_MD *evp_md;
+ const EVP_CIPHER *evp_cipher;
+ };
+ crypto_func_t func;
+ crypto_init_func_t init;
+ } auth;
+
+ unsigned idx;
+};
+
+typedef struct odp_crypto_global_s odp_crypto_global_t;
+
+struct odp_crypto_global_s {
+ odp_spinlock_t lock;
+ odp_crypto_generic_session_t *free;
+ odp_crypto_generic_session_t sessions[MAX_SESSIONS];
+
+ /* These flags are cleared at alloc_session() */
+ uint8_t ctx_valid[ODP_THREAD_COUNT_MAX][MAX_SESSIONS];
+
+ odp_ticketlock_t openssl_lock[];
+};
+
+static odp_crypto_global_t *global;
+
+typedef struct crypto_local_t {
+ EVP_MD_CTX *md_ctx[MAX_SESSIONS];
+ HMAC_CTX *hmac_ctx[MAX_SESSIONS];
+ CMAC_CTX *cmac_ctx[MAX_SESSIONS];
+ EVP_CIPHER_CTX *cipher_ctx[MAX_SESSIONS];
+ EVP_CIPHER_CTX *mac_cipher_ctx[MAX_SESSIONS];
+ uint8_t *ctx_valid;
+} crypto_local_t;
+
+static __thread crypto_local_t local;
+
+static inline void crypto_init(odp_crypto_generic_session_t *session)
+{
+ if (local.ctx_valid[session->idx])
+ return;
+
+ session->cipher.init(session);
+ session->auth.init(session);
+
+ local.ctx_valid[session->idx] = 1;
+}
+
+static
+odp_crypto_generic_session_t *alloc_session(void)
+{
+ odp_crypto_generic_session_t *session = NULL;
+ unsigned i;
+
+ odp_spinlock_lock(&global->lock);
+ session = global->free;
+ if (session) {
+ global->free = session->next;
+ session->next = NULL;
+ }
+ odp_spinlock_unlock(&global->lock);
+
+ if (!session)
+ return NULL;
+
+ session->idx = session - global->sessions;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ global->ctx_valid[i][session->idx] = 0;
+
+ return session;
+}
+
+static
+void free_session(odp_crypto_generic_session_t *session)
+{
+ odp_spinlock_lock(&global->lock);
+ session->next = global->free;
+ global->free = session;
+ odp_spinlock_unlock(&global->lock);
+}
+
+static odp_crypto_alg_err_t
+null_crypto_routine(odp_packet_t pkt ODP_UNUSED,
+ const odp_crypto_packet_op_param_t *param ODP_UNUSED,
+ odp_crypto_generic_session_t *session ODP_UNUSED)
+{
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+null_crypto_init_routine(odp_crypto_generic_session_t *session)
+{
+ (void)session;
+}
+
+/* Mimic new OpenSSL 1.1.y API */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = malloc(sizeof(*ctx));
+
+ HMAC_CTX_init(ctx);
+ return ctx;
+}
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ HMAC_CTX_cleanup(ctx);
+ free(ctx);
+}
+
+static EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+ EVP_MD_CTX *ctx = malloc(sizeof(*ctx));
+
+ EVP_MD_CTX_init(ctx);
+ return ctx;
+}
+
+static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(ctx);
+ free(ctx);
+}
+#endif
+
+static void
+auth_hmac_init(odp_crypto_generic_session_t *session)
+{
+ HMAC_CTX *ctx = local.hmac_ctx[session->idx];
+
+ HMAC_Init_ex(ctx,
+ session->auth.key,
+ session->p.auth_key.length,
+ session->auth.evp_md,
+ NULL);
+}
+
+static
+void packet_hmac(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session,
+ uint8_t *hash)
+{
+ HMAC_CTX *ctx = local.hmac_ctx[session->idx];
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+
+ /* Reinitialize HMAC calculation without resetting the key */
+ HMAC_Init_ex(ctx, NULL, 0, NULL, NULL);
+
+ /* Hash it */
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ uint32_t maclen = len > seglen ? seglen : len;
+
+ HMAC_Update(ctx, mapaddr, maclen);
+ offset += maclen;
+ len -= maclen;
+ }
+
+ HMAC_Final(ctx, hash, NULL);
+}
+
+static void xor_block(uint8_t *res, const uint8_t *op)
+{
+ int i;
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ res[i] ^= op[i];
+}
+
+static void memxor(uint8_t *res, const uint8_t *op, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ res[i] ^= op[i];
+}
+
+static
+void packet_aes_xcbc_mac(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session,
+ uint8_t *hash)
+{
+ uint8_t e[AES_BLOCK_SIZE] = {0};
+ size_t eoff = 0;
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+ uint32_t seglen = 0;
+ uint32_t datalen = 0;
+ int dummy_len = 0;
+ EVP_CIPHER_CTX *ctx;
+ void *mapaddr;
+ uint8_t *data = NULL;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+ _ODP_ASSERT(session != NULL);
+ _ODP_ASSERT(sizeof(session->auth.key) >= 3 * AES_KEY_LENGTH);
+
+ ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, session->auth.evp_cipher,
+ NULL, session->auth.key, NULL);
+
+ while (len > 0) {
+ mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ datalen = seglen >= len ? len : seglen;
+ data = (uint8_t *)mapaddr;
+ offset += datalen;
+ len -= datalen;
+ if (eoff != 0) {
+ if (eoff + datalen > AES_BLOCK_SIZE) {
+ /* bytes needed to fill the partial block */
+ uint32_t remaining_len = AES_BLOCK_SIZE - eoff;
+
+ memxor(e + eoff, data, remaining_len);
+ datalen -= remaining_len;
+ data += remaining_len;
+ eoff = 0;
+ EVP_EncryptUpdate(ctx,
+ e, &dummy_len, e, sizeof(e));
+ } else {
+ memxor(e + eoff, data, datalen);
+ eoff += datalen;
+ continue;
+ }
+ }
+ while (datalen > AES_BLOCK_SIZE) {
+ xor_block(e, data);
+ EVP_EncryptUpdate(ctx, e, &dummy_len, e, sizeof(e));
+ data += AES_BLOCK_SIZE;
+ datalen -= AES_BLOCK_SIZE;
+ }
+ /* Segmentation handle */
+ if (datalen > 0) {
+ memxor(e, data, datalen);
+ eoff = datalen;
+ }
+ }
+
+ if (eoff == AES_BLOCK_SIZE) {
+ xor_block(e, session->auth.key + AES_KEY_LENGTH);
+ } else {
+ e[eoff] ^= 0x80;
+ xor_block(e, session->auth.key + AES_KEY_LENGTH * 2);
+ }
+ EVP_EncryptUpdate(ctx, hash, &dummy_len, e, sizeof(e));
+ EVP_CIPHER_CTX_free(ctx);
+}
+
+static
+odp_crypto_alg_err_t auth_xcbcmac_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+
+ /* Hash it */
+ packet_aes_xcbc_mac(pkt, param, session, hash);
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt,
+ param->hash_result_offset,
+ session->p.auth_digest_len,
+ hash);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static odp_crypto_alg_err_t
+auth_xcbcmac_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint32_t bytes = session->p.auth_digest_len;
+ uint8_t hash_in[EVP_MAX_MD_SIZE];
+ uint8_t hash_out[EVP_MAX_MD_SIZE];
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ /* Hash it */
+ packet_aes_xcbc_mac(pkt, param, session, hash_out);
+
+ /* Verify match */
+ if (0 != memcmp(hash_in, hash_out, bytes))
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ /* Matched */
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_aesxcbc_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ uint32_t k1[4] = { 0x01010101, 0x01010101, 0x01010101, 0x01010101 };
+ uint32_t k2[4] = { 0x02020202, 0x02020202, 0x02020202, 0x02020202 };
+ uint32_t k3[4] = { 0x03030303, 0x03030303, 0x03030303, 0x03030303 };
+ EVP_CIPHER_CTX *ctx;
+ int dummy_len = 0;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_xcbcmac_gen;
+ else
+ session->auth.func = auth_xcbcmac_check;
+ session->auth.init = null_crypto_init_routine;
+
+ session->auth.evp_cipher = cipher;
+ ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, session->auth.evp_cipher, NULL,
+ session->p.auth_key.data, NULL);
+ /* K1 = 0x01010101010101010101010101010101 encrypted with Key K */
+ EVP_EncryptUpdate(ctx, session->auth.key,
+ &dummy_len, (uint8_t *)k1, AES_BLOCK_SIZE);
+
+ /* K2 = 0x02020202020202020202020202020202 encrypted with Key K */
+ EVP_EncryptUpdate(ctx, session->auth.key + AES_KEY_LENGTH,
+ &dummy_len, (uint8_t *)k2, AES_BLOCK_SIZE);
+
+ /* K3 = 0x03030303030303030303030303030303 encrypted with Key K */
+ EVP_EncryptUpdate(ctx, session->auth.key + AES_KEY_LENGTH * 2,
+ &dummy_len, (uint8_t *)k3, AES_BLOCK_SIZE);
+
+ EVP_CIPHER_CTX_free(ctx);
+ return 0;
+}
+
+static
+odp_crypto_alg_err_t auth_hmac_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+
+ /* Hash it */
+ packet_hmac(pkt, param, session, hash);
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt,
+ param->hash_result_offset,
+ session->p.auth_digest_len,
+ hash);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_hmac_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint32_t bytes = session->p.auth_digest_len;
+ uint8_t hash_in[EVP_MAX_MD_SIZE];
+ uint8_t hash_out[EVP_MAX_MD_SIZE];
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ /* Hash it */
+ packet_hmac(pkt, param, session, hash_out);
+
+ /* Verify match */
+ if (0 != memcmp(hash_in, hash_out, bytes))
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ /* Matched */
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+auth_cmac_init(odp_crypto_generic_session_t *session)
+{
+ CMAC_CTX *ctx = local.cmac_ctx[session->idx];
+
+ CMAC_Init(ctx,
+ session->auth.key,
+ session->p.auth_key.length,
+ session->auth.evp_cipher,
+ NULL);
+}
+
+static
+void packet_cmac(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session,
+ uint8_t *hash)
+{
+ CMAC_CTX *ctx = local.cmac_ctx[session->idx];
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+ size_t outlen;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+
+ /* Reinitialize CMAC calculation without resetting the key */
+ CMAC_Init(ctx, NULL, 0, NULL, NULL);
+
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ uint32_t maclen = len > seglen ? seglen : len;
+
+ CMAC_Update(ctx, mapaddr, maclen);
+ offset += maclen;
+ len -= maclen;
+ }
+
+ CMAC_Final(ctx, hash, &outlen);
+}
+
+static
+odp_crypto_alg_err_t auth_cmac_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+
+ /* Hash it */
+ packet_cmac(pkt, param, session, hash);
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt,
+ param->hash_result_offset,
+ session->p.auth_digest_len,
+ hash);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_cmac_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint32_t bytes = session->p.auth_digest_len;
+ uint8_t hash_in[EVP_MAX_MD_SIZE];
+ uint8_t hash_out[EVP_MAX_MD_SIZE];
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ /* Hash it */
+ packet_cmac(pkt, param, session, hash_out);
+
+ /* Verify match */
+ if (0 != memcmp(hash_in, hash_out, bytes))
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ /* Matched */
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+int packet_cmac_eia2(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session,
+ uint8_t *hash)
+{
+ CMAC_CTX *ctx = local.cmac_ctx[session->idx];
+ const void *iv_ptr = param->auth_iv_ptr;
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+ size_t outlen;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+
+ /* Reinitialize CMAC calculation without resetting the key */
+ CMAC_Init(ctx, NULL, 0, NULL, NULL);
+
+ CMAC_Update(ctx, iv_ptr, session->p.auth_iv_len);
+
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ uint32_t maclen = len > seglen ? seglen : len;
+
+ CMAC_Update(ctx, mapaddr, maclen);
+ offset += maclen;
+ len -= maclen;
+ }
+
+ if (1 != CMAC_Final(ctx, hash, &outlen))
+ return ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+ else
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_cmac_eia2_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t
+ *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ int ret;
+
+ /* Hash it */
+ ret = packet_cmac_eia2(pkt, param, session, hash);
+ if (ret != ODP_CRYPTO_ALG_ERR_NONE)
+ return ret;
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt,
+ param->hash_result_offset,
+ session->p.auth_digest_len,
+ hash);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_cmac_eia2_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t
+ *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint32_t bytes = session->p.auth_digest_len;
+ uint8_t hash_in[EVP_MAX_MD_SIZE];
+ uint8_t hash_out[EVP_MAX_MD_SIZE];
+ int ret;
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ /* Hash it */
+ ret = packet_cmac_eia2(pkt, param, session, hash_out);
+ if (ret != ODP_CRYPTO_ALG_ERR_NONE)
+ return ret;
+
+ /* Verify match */
+ if (0 != memcmp(hash_in, hash_out, bytes))
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ /* Matched */
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+void packet_digest(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session,
+ uint8_t *hash)
+{
+ EVP_MD_CTX *ctx = local.md_ctx[session->idx];
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+
+ EVP_DigestInit_ex(ctx,
+ session->auth.evp_md,
+ NULL);
+
+ /* Hash it */
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ uint32_t maclen = len > seglen ? seglen : len;
+
+ EVP_DigestUpdate(ctx, mapaddr, maclen);
+ offset += maclen;
+ len -= maclen;
+ }
+
+ EVP_DigestFinal_ex(ctx, hash, NULL);
+}
+
+static
+odp_crypto_alg_err_t auth_digest_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+
+ /* Hash it */
+ packet_digest(pkt, param, session, hash);
+
+ /* Copy to the output location */
+ odp_packet_copy_from_mem(pkt,
+ param->hash_result_offset,
+ session->p.auth_digest_len,
+ hash);
+
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t auth_digest_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t
+ *param,
+ odp_crypto_generic_session_t *session)
+{
+ uint32_t bytes = session->p.auth_digest_len;
+ uint8_t hash_in[EVP_MAX_MD_SIZE];
+ uint8_t hash_out[EVP_MAX_MD_SIZE];
+
+ /* Copy current value out and clear it before authentication */
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ bytes, hash_in);
+
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset, 0, bytes);
+
+ /* Hash it */
+ packet_digest(pkt, param, session, hash_out);
+
+ /* Verify match */
+ if (0 != memcmp(hash_in, hash_out, bytes))
+ return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+ /* Matched */
+ return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+int internal_aad(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_bool_t encrypt)
+{
+ uint32_t offset = param->auth_range.offset;
+ uint32_t len = param->auth_range.length;
+ int dummy_len;
+ int ret;
+
+ _ODP_ASSERT(offset + len <= odp_packet_len(pkt));
+
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = odp_packet_offset(pkt, offset, &seglen, NULL);
+ uint32_t maclen = len > seglen ? seglen : len;
+
+ if (encrypt)
+ EVP_EncryptUpdate(ctx, NULL, &dummy_len, mapaddr, maclen);
+ else
+ EVP_DecryptUpdate(ctx, NULL, &dummy_len, mapaddr, maclen);
+ offset += maclen;
+ len -= maclen;
+ }
+
+ if (encrypt)
+ ret = EVP_EncryptFinal_ex(ctx, NULL, &dummy_len);
+ else
+ ret = EVP_DecryptFinal_ex(ctx, NULL, &dummy_len);
+
+ return ret;
+}
+
+typedef int (*evp_update_t)(EVP_CIPHER_CTX *, unsigned char *,
+ int *, const unsigned char *, int);
+
+typedef int (*evp_final_t)(EVP_CIPHER_CTX *, unsigned char *, int *);
+
+static inline int internal_crypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ evp_update_t EVP_update,
+ evp_final_t EVP_final)
+{
+ uint32_t in_pos = param->cipher_range.offset;
+ uint32_t out_pos = in_pos;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t block[EVP_MAX_BLOCK_LENGTH];
+ uint32_t buffered = 0;
+ uint32_t block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
+ int out_len;
+ int rc;
+
+ _ODP_ASSERT(in_pos + in_len <= odp_packet_len(pkt));
+
+ /*
+ * In the following loop we process one packet segment per iteration.
+ * We rely on the following properties of the encrypt/decrypt update
+ * function with the algorithms that we use:
+ *
+ * - The function processes (and writes to output) only whole blocks.
+ * - Input data beyond the last full block is buffered inside OpenSSL.
+ * - The amount of buffered data is always less than one block.
+ * - Total amount of output data does not exceed the total amount
+ * of input data at any point.
+ */
+ while (in_len > 0) {
+ uint32_t seglen = 0;
+ uint8_t *in_addr = odp_packet_offset(pkt, in_pos,
+ &seglen, NULL);
+ uint32_t len = in_len < seglen ? in_len : seglen;
+
+ if (odp_unlikely(buffered > 0)) {
+ /*
+ * Leftover data from the previous segment is
+ * in the buffer inside OpenSSL.
+ */
+ uint32_t remaining_len = block_len - buffered;
+
+ if (odp_likely(len >= remaining_len)) {
+ /*
+ * Let's fill the buffered input data to a
+ * full block and get the output block to
+ * a memory buffer. The buffer is then copied
+ * to the packet, crossing segment boundary.
+ */
+ rc = EVP_update(ctx, block, &out_len,
+ in_addr, remaining_len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ if (odp_unlikely(out_len != (int)block_len))
+ goto err;
+ in_addr += remaining_len;
+ in_pos += remaining_len;
+ len -= remaining_len;
+ in_len -= remaining_len;
+ buffered = 0;
+ rc = odp_packet_copy_from_mem(pkt, out_pos,
+ block_len, block);
+ if (odp_unlikely(rc))
+ goto err;
+ out_pos += block_len;
+ } else {
+ /*
+ * Not enough data in this segment to fill
+ * the buffer to a full block. Fill the buffer
+ * a bit more and go to the next segment.
+ */
+ rc = EVP_update(ctx, block, &out_len,
+ in_addr, len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ if (odp_unlikely(out_len > 0))
+ goto err;
+ in_pos += len;
+ in_len -= len;
+ buffered += len;
+ continue;
+ }
+ }
+ _ODP_ASSERT(buffered == 0);
+
+ if (in_len > 0) {
+ /*
+ * No input is buffered inside OpenSSL. We pass the
+ * whole remaining segment to OpenSSL and expect to
+ * get a multiple of block size of data processed,
+ * with the rest left in the buffer.
+ */
+ rc = EVP_update(ctx, in_addr, &out_len, in_addr, len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ _ODP_ASSERT(_ODP_CHECK_IS_POWER2(block_len));
+ buffered = len & (block_len - 1);
+ if (odp_unlikely(out_len + buffered != len))
+ goto err;
+ in_pos += len;
+ in_len -= len;
+ out_pos += len - buffered;
+ }
+ }
+ if (odp_unlikely(buffered > 0))
+ goto err;
+ /*
+ * We do not expect any more data out since the cipher range is
+ * supposed to be a multiple of the block size.
+ */
+ rc = EVP_final(ctx, block, &out_len);
+ if (odp_unlikely(out_len != 0))
+ return 0;
+ return rc;
+err:
+ _ODP_ERR("internal error\n");
+ (void)EVP_final(ctx, block, &out_len);
+ return 0;
+}
+
+static int internal_encrypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param)
+{
+ return internal_crypt(ctx, pkt, param,
+ EVP_EncryptUpdate,
+ EVP_EncryptFinal_ex);
+}
+
+static int internal_decrypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param)
+{
+ return internal_crypt(ctx, pkt, param,
+ EVP_DecryptUpdate,
+ EVP_DecryptFinal_ex);
+}
+
+static void
+cipher_encrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ session->cipher.key_data, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t cipher_encrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int ret;
+
+ EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ ret = internal_encrypt(ctx, pkt, param);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+cipher_decrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ session->cipher.key_data, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t cipher_decrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int ret;
+
+ EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ ret = internal_decrypt(ctx, pkt, param);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_cipher_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if ((uint32_t)EVP_CIPHER_iv_length(cipher) !=
+ session->p.cipher_iv_len)
+ return -1;
+
+ session->cipher.evp_cipher = cipher;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->cipher.func = cipher_encrypt;
+ session->cipher.init = cipher_encrypt_init;
+ } else {
+ session->cipher.func = cipher_decrypt;
+ session->cipher.init = cipher_decrypt_init;
+ }
+
+ return 0;
+}
+
+static odp_crypto_alg_err_t cipher_encrypt_bytes(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint32_t offset = param->cipher_range.offset;
+ uint8_t data[in_len];
+ int ret;
+
+ EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+ odp_packet_copy_to_mem(pkt, offset, in_len, data);
+ EVP_EncryptUpdate(ctx, data, &cipher_len, data, in_len);
+ ret = EVP_EncryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+ odp_packet_copy_from_mem(pkt, offset, in_len, data);
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static odp_crypto_alg_err_t cipher_decrypt_bytes(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint32_t offset = param->cipher_range.offset;
+ uint8_t data[in_len];
+ int ret;
+
+ EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+ odp_packet_copy_to_mem(pkt, offset, in_len, data);
+ EVP_DecryptUpdate(ctx, data, &cipher_len, data, in_len);
+ ret = EVP_DecryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+ odp_packet_copy_from_mem(pkt, offset, in_len, data);
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static odp_crypto_alg_err_t cipher_encrypt_bits(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ odp_crypto_packet_op_param_t new_param = *param;
+
+ new_param.cipher_range.offset /= 8;
+ new_param.cipher_range.length = (new_param.cipher_range.length + 7) / 8;
+ return cipher_encrypt_bytes(pkt, &new_param, session);
+}
+
+static odp_crypto_alg_err_t cipher_decrypt_bits(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ odp_crypto_packet_op_param_t new_param = *param;
+
+ new_param.cipher_range.offset /= 8;
+ new_param.cipher_range.length = (new_param.cipher_range.length + 7) / 8;
+ return cipher_decrypt_bytes(pkt, &new_param, session);
+}
+
+static int process_cipher_param_bits(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if ((uint32_t)EVP_CIPHER_iv_length(cipher) !=
+ session->p.cipher_iv_len)
+ return -1;
+
+ session->cipher.evp_cipher = cipher;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->cipher.init = cipher_encrypt_init;
+ session->cipher.func = session->cipher_range_in_bits ? cipher_encrypt_bits
+ : cipher_encrypt_bytes;
+
+ } else {
+ session->cipher.init = cipher_decrypt_init;
+ session->cipher.func = session->cipher_range_in_bits ? cipher_decrypt_bits
+ : cipher_decrypt_bytes;
+ }
+
+ return 0;
+}
+
+static void
+aes_gcm_encrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ session->cipher.key_data, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
+ session->p.cipher_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_gcm_encrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ const uint8_t *aad_head = param->aad_ptr;
+ uint32_t aad_len = session->p.auth_aad_len;
+ int dummy_len = 0;
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ /* Authenticate header data (if any) without encrypting them */
+ if (aad_len > 0)
+ EVP_EncryptUpdate(ctx, NULL, &dummy_len,
+ aad_head, aad_len);
+
+ ret = internal_encrypt(ctx, pkt, param);
+
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
+ session->p.auth_digest_len, block);
+ odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+aes_gcm_decrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ session->cipher.key_data, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
+ session->p.cipher_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_gcm_decrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ const uint8_t *aad_head = param->aad_ptr;
+ uint32_t aad_len = session->p.auth_aad_len;
+ int dummy_len = 0;
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
+ session->p.auth_digest_len, block);
+
+ /* Authenticate header data (if any) without encrypting them */
+ if (aad_len > 0)
+ EVP_DecryptUpdate(ctx, NULL, &dummy_len,
+ aad_head, aad_len);
+
+ ret = internal_decrypt(ctx, pkt, param);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_aes_gcm_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if (12 != session->p.cipher_iv_len)
+ return -1;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ session->cipher.evp_cipher = cipher;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->cipher.func = aes_gcm_encrypt;
+ session->cipher.init = aes_gcm_encrypt_init;
+ } else {
+ session->cipher.func = aes_gcm_decrypt;
+ session->cipher.init = aes_gcm_decrypt_init;
+ }
+
+ return 0;
+}
+
+static void
+aes_gmac_gen_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.mac_cipher_ctx[session->idx];
+
+ EVP_EncryptInit_ex(ctx, session->auth.evp_cipher, NULL,
+ session->auth.key, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
+ session->p.auth_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_gmac_gen(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.mac_cipher_ctx[session->idx];
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, param->auth_iv_ptr);
+
+ ret = internal_aad(ctx, pkt, param, true);
+
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
+ session->p.auth_digest_len, block);
+ odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+aes_gmac_check_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.mac_cipher_ctx[session->idx];
+
+ EVP_DecryptInit_ex(ctx, session->auth.evp_cipher, NULL,
+ session->auth.key, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
+ session->p.auth_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_gmac_check(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.mac_cipher_ctx[session->idx];
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, param->auth_iv_ptr);
+
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
+ session->p.auth_digest_len, block);
+ if (odp_unlikely(session->p.hash_result_in_auth_range))
+ _odp_packet_set_data(pkt, param->hash_result_offset,
+ 0, session->p.auth_digest_len);
+
+ ret = internal_aad(ctx, pkt, param, false);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_aes_gmac_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.auth_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if (12 != session->p.auth_iv_len)
+ return -1;
+
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ session->auth.evp_cipher = cipher;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->auth.func = aes_gmac_gen;
+ session->auth.init = aes_gmac_gen_init;
+ } else {
+ session->auth.func = aes_gmac_check;
+ session->auth.init = aes_gmac_check_init;
+ }
+
+ return 0;
+}
+
+static void
+aes_ccm_encrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ NULL, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN,
+ session->p.cipher_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_ccm_encrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ const uint8_t *aad_head = param->aad_ptr;
+ uint32_t aad_len = session->p.auth_aad_len;
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t data[in_len];
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG,
+ session->p.auth_digest_len, NULL);
+ EVP_EncryptInit_ex(ctx, NULL, NULL, session->cipher.key_data, param->cipher_iv_ptr);
+
+ /* Set len */
+ EVP_EncryptUpdate(ctx, NULL, &dummy_len, NULL, in_len);
+
+ /* Authenticate header data (if any) without encrypting them */
+ if (aad_len > 0)
+ EVP_EncryptUpdate(ctx, NULL, &dummy_len,
+ aad_head, aad_len);
+
+ odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ EVP_EncryptUpdate(ctx, data, &cipher_len, data, in_len);
+
+ ret = EVP_EncryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+
+ odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG,
+ session->p.auth_digest_len, block);
+ odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static void
+aes_ccm_decrypt_init(odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+
+ EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+ session->cipher.key_data, NULL);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
+ session->p.cipher_iv_len, NULL);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+}
+
+static
+odp_crypto_alg_err_t aes_ccm_decrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ const uint8_t *aad_head = param->aad_ptr;
+ uint32_t aad_len = session->p.auth_aad_len;
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t data[in_len];
+ uint8_t block[EVP_MAX_MD_SIZE];
+ int ret;
+
+ odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+ session->p.auth_digest_len, block);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG,
+ session->p.auth_digest_len, block);
+ EVP_DecryptInit_ex(ctx, NULL, NULL, session->cipher.key_data, param->cipher_iv_ptr);
+
+ /* Set len */
+ EVP_DecryptUpdate(ctx, NULL, &dummy_len, NULL, in_len);
+
+ /* Authenticate header data (if any) without encrypting them */
+ if (aad_len > 0)
+ EVP_DecryptUpdate(ctx, NULL, &dummy_len,
+ aad_head, aad_len);
+
+ odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ ret = EVP_DecryptUpdate(ctx, data, &cipher_len, data, in_len);
+
+ EVP_DecryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+
+ odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_aes_ccm_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if (11 != session->p.cipher_iv_len &&
+ 13 != session->p.cipher_iv_len)
+ return -1;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ session->cipher.evp_cipher = cipher;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->cipher.func = aes_ccm_encrypt;
+ session->cipher.init = aes_ccm_encrypt_init;
+ } else {
+ session->cipher.func = aes_ccm_decrypt;
+ session->cipher.init = aes_ccm_decrypt_init;
+ }
+
+ return 0;
+}
+
+static
+odp_crypto_alg_err_t xts_encrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t data[in_len];
+ int ret;
+
+ EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ EVP_EncryptUpdate(ctx, data, &cipher_len, data, in_len);
+
+ ret = EVP_EncryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+
+ odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t xts_decrypt(odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ odp_crypto_generic_session_t *session)
+{
+ EVP_CIPHER_CTX *ctx = local.cipher_ctx[session->idx];
+ int dummy_len = 0;
+ int cipher_len;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t data[in_len];
+ int ret;
+
+ EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, param->cipher_iv_ptr);
+
+ odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ EVP_DecryptUpdate(ctx, data, &cipher_len, data, in_len);
+
+ ret = EVP_DecryptFinal_ex(ctx, data + cipher_len, &dummy_len);
+ cipher_len += dummy_len;
+
+ odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
+ data);
+
+ return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+ ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_xts_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.cipher_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if ((uint32_t)EVP_CIPHER_iv_length(cipher) !=
+ session->p.cipher_iv_len)
+ return -1;
+
+ session->cipher.evp_cipher = cipher;
+
+ memcpy(session->cipher.key_data, session->p.cipher_key.data,
+ session->p.cipher_key.length);
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op) {
+ session->cipher.func = xts_encrypt;
+ session->cipher.init = cipher_encrypt_init;
+ } else {
+ session->cipher.func = xts_decrypt;
+ session->cipher.init = cipher_decrypt_init;
+ }
+
+ return 0;
+}
+
+static int process_auth_hmac_param(odp_crypto_generic_session_t *session,
+ const EVP_MD *evp_md)
+{
+ /* Verify IV len is correct */
+ if (0 != session->p.auth_iv_len)
+ return -1;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_hmac_gen;
+ else
+ session->auth.func = auth_hmac_check;
+ session->auth.init = auth_hmac_init;
+
+ session->auth.evp_md = evp_md;
+
+ /* Number of valid bytes */
+ if (session->p.auth_digest_len < (unsigned)EVP_MD_size(evp_md) / 2)
+ return -1;
+
+ /* Convert keys */
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ return 0;
+}
+
+static int process_auth_cmac_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.auth_key.length)
+ return -1;
+
+ if (0 != session->p.auth_iv_len)
+ return -1;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_cmac_gen;
+ else
+ session->auth.func = auth_cmac_check;
+ session->auth.init = auth_cmac_init;
+
+ session->auth.evp_cipher = cipher;
+
+ /* Number of valid bytes */
+ if (session->p.auth_digest_len <
+ (unsigned)EVP_CIPHER_block_size(cipher) / 2)
+ return -1;
+
+ /* Convert keys */
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ return 0;
+}
+
+static int process_auth_cmac_eia2_param(odp_crypto_generic_session_t *session,
+ const EVP_CIPHER *cipher)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+ session->p.auth_key.length)
+ return -1;
+
+ /* Verify IV len is correct */
+ if (8 != session->p.auth_iv_len)
+ return -1;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_cmac_eia2_gen;
+ else
+ session->auth.func = auth_cmac_eia2_check;
+ session->auth.init = auth_cmac_init;
+
+ session->auth.evp_cipher = cipher;
+
+ /* Number of valid bytes */
+ if (session->p.auth_digest_len != 4)
+ return -1;
+
+ /* Convert keys */
+ memcpy(session->auth.key, session->p.auth_key.data,
+ session->p.auth_key.length);
+
+ return 0;
+}
+
+static int process_digest_param(odp_crypto_generic_session_t *session,
+ const EVP_MD *md)
+{
+ /* Verify Key len is valid */
+ if ((uint32_t)EVP_MD_size(md) !=
+ session->p.auth_digest_len)
+ return -1;
+
+ /* Set function */
+ if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+ session->auth.func = auth_digest_gen;
+ else
+ session->auth.func = auth_digest_check;
+ session->auth.init = null_crypto_init_routine;
+
+ session->auth.evp_md = md;
+
+ return 0;
+}
+
+int odp_crypto_capability(odp_crypto_capability_t *capa)
+{
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ return -1;
+ }
+
+ if (NULL == capa)
+ return -1;
+
+ /* Initialize crypto capability structure */
+ memset(capa, 0, sizeof(odp_crypto_capability_t));
+
+ capa->max_sessions = MAX_SESSIONS;
+ capa->sync_mode = ODP_SUPPORT_PREFERRED;
+ capa->async_mode = ODP_SUPPORT_YES;
+ capa->queue_type_plain = 1;
+ capa->queue_type_sched = 1;
+
+ /* Memory allocation in libssl is not compatible with process mode */
+ if (odp_global_ro.init_param.mem_model == ODP_MEM_MODEL_PROCESS) {
+ capa->ciphers.bit.null = 1;
+ capa->auths.bit.null = 1;
+ return 0;
+ }
+
+ capa->ciphers.bit.null = 1;
+ capa->ciphers.bit.trides_cbc = 1;
+ capa->ciphers.bit.trides_ecb = 1;
+ capa->ciphers.bit.aes_cbc = 1;
+ capa->ciphers.bit.aes_ctr = 1;
+ capa->ciphers.bit.aes_ecb = 1;
+ capa->ciphers.bit.aes_cfb128 = 1;
+ capa->ciphers.bit.aes_xts = 1;
+ capa->ciphers.bit.aes_gcm = 1;
+ capa->ciphers.bit.aes_ccm = 1;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ capa->ciphers.bit.chacha20_poly1305 = 1;
+#endif
+ capa->ciphers.bit.aes_eea2 = 1;
+
+ capa->auths.bit.null = 1;
+ capa->auths.bit.md5_hmac = 1;
+ capa->auths.bit.sha1_hmac = 1;
+ capa->auths.bit.sha224_hmac = 1;
+ capa->auths.bit.sha256_hmac = 1;
+ capa->auths.bit.sha384_hmac = 1;
+ capa->auths.bit.sha512_hmac = 1;
+ capa->auths.bit.aes_xcbc_mac = 1;
+ capa->auths.bit.aes_gcm = 1;
+ capa->auths.bit.aes_ccm = 1;
+ capa->auths.bit.aes_gmac = 1;
+ capa->auths.bit.aes_cmac = 1;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ capa->auths.bit.chacha20_poly1305 = 1;
+#endif
+ capa->auths.bit.aes_eia2 = 1;
+
+ capa->auths.bit.md5 = 1;
+ capa->auths.bit.sha1 = 1;
+ capa->auths.bit.sha224 = 1;
+ capa->auths.bit.sha256 = 1;
+ capa->auths.bit.sha384 = 1;
+ capa->auths.bit.sha512 = 1;
+
+ return 0;
+}
+
+int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
+ odp_crypto_cipher_capability_t dst[],
+ int num_copy)
+{
+ const odp_crypto_cipher_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_cipher_capability_t);
+
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ src = cipher_capa_null;
+ num = sizeof(cipher_capa_null) / size;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ src = cipher_capa_trides_cbc;
+ num = sizeof(cipher_capa_trides_cbc) / size;
+ break;
+ case ODP_CIPHER_ALG_3DES_ECB:
+ src = cipher_capa_trides_ecb;
+ num = sizeof(cipher_capa_trides_ecb) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ src = cipher_capa_aes_cbc;
+ num = sizeof(cipher_capa_aes_cbc) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ src = cipher_capa_aes_ctr;
+ num = sizeof(cipher_capa_aes_ctr) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_ECB:
+ src = cipher_capa_aes_ecb;
+ num = sizeof(cipher_capa_aes_ecb) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_CFB128:
+ src = cipher_capa_aes_cfb128;
+ num = sizeof(cipher_capa_aes_cfb128) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_XTS:
+ src = cipher_capa_aes_xts;
+ num = sizeof(cipher_capa_aes_xts) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ src = cipher_capa_aes_gcm;
+ num = sizeof(cipher_capa_aes_gcm) / size;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ src = cipher_capa_aes_ccm;
+ num = sizeof(cipher_capa_aes_ccm) / size;
+ break;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ src = cipher_capa_chacha20_poly1305;
+ num = sizeof(cipher_capa_chacha20_poly1305) / size;
+ break;
+#endif
+ case ODP_CIPHER_ALG_AES_EEA2:
+ src = cipher_capa_aes_eea2;
+ num = sizeof(cipher_capa_aes_eea2) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ if (dst)
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int odp_crypto_auth_capability(odp_auth_alg_t auth,
+ odp_crypto_auth_capability_t dst[], int num_copy)
+{
+ const odp_crypto_auth_capability_t *src;
+ int num;
+ int size = sizeof(odp_crypto_auth_capability_t);
+
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ src = auth_capa_null;
+ num = sizeof(auth_capa_null) / size;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ src = auth_capa_md5_hmac;
+ num = sizeof(auth_capa_md5_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ src = auth_capa_sha1_hmac;
+ num = sizeof(auth_capa_sha1_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_SHA224_HMAC:
+ src = auth_capa_sha224_hmac;
+ num = sizeof(auth_capa_sha224_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ src = auth_capa_sha256_hmac;
+ num = sizeof(auth_capa_sha256_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ src = auth_capa_sha384_hmac;
+ num = sizeof(auth_capa_sha384_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ src = auth_capa_sha512_hmac;
+ num = sizeof(auth_capa_sha512_hmac) / size;
+ break;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ src = auth_capa_aes_xcbc;
+ num = sizeof(auth_capa_aes_xcbc) / size;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ src = auth_capa_aes_gcm;
+ num = sizeof(auth_capa_aes_gcm) / size;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ src = auth_capa_aes_gmac;
+ num = sizeof(auth_capa_aes_gmac) / size;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ src = auth_capa_aes_ccm;
+ num = sizeof(auth_capa_aes_ccm) / size;
+ break;
+ case ODP_AUTH_ALG_AES_CMAC:
+ src = auth_capa_aes_cmac;
+ num = sizeof(auth_capa_aes_cmac) / size;
+ break;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ src = auth_capa_chacha20_poly1305;
+ num = sizeof(auth_capa_chacha20_poly1305) / size;
+ break;
+#endif
+ case ODP_AUTH_ALG_AES_EIA2:
+ src = auth_capa_aes_eia2;
+ num = sizeof(auth_capa_aes_eia2) / size;
+ break;
+ case ODP_AUTH_ALG_MD5:
+ src = auth_capa_md5;
+ num = sizeof(auth_capa_md5) / size;
+ break;
+ case ODP_AUTH_ALG_SHA1:
+ src = auth_capa_sha1;
+ num = sizeof(auth_capa_sha1) / size;
+ break;
+ case ODP_AUTH_ALG_SHA224:
+ src = auth_capa_sha224;
+ num = sizeof(auth_capa_sha224) / size;
+ break;
+ case ODP_AUTH_ALG_SHA256:
+ src = auth_capa_sha256;
+ num = sizeof(auth_capa_sha256) / size;
+ break;
+ case ODP_AUTH_ALG_SHA384:
+ src = auth_capa_sha384;
+ num = sizeof(auth_capa_sha384) / size;
+ break;
+ case ODP_AUTH_ALG_SHA512:
+ src = auth_capa_sha512;
+ num = sizeof(auth_capa_sha512) / size;
+ break;
+ default:
+ return -1;
+ }
+
+ if (num < num_copy)
+ num_copy = num;
+
+ if (dst)
+ memcpy(dst, src, num_copy * size);
+
+ return num;
+}
+
+int
+odp_crypto_session_create(const odp_crypto_session_param_t *param,
+ odp_crypto_session_t *session_out,
+ odp_crypto_ses_create_err_t *status)
+{
+ int rc;
+ odp_crypto_generic_session_t *session;
+ int cipher_bit_mode_supported = 0;
+ int auth_bit_mode_supported = 0;
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_ERR("Crypto is disabled\n");
+ /* Dummy output to avoid compiler warning about uninitialized
+ * variables */
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ /* Process mode is not supported with libssl based algos */
+ if (odp_global_ro.init_param.mem_model == ODP_MEM_MODEL_PROCESS &&
+ (param->cipher_alg != ODP_CIPHER_ALG_NULL ||
+ param->auth_alg != ODP_AUTH_ALG_NULL)) {
+ *status = param->cipher_alg != ODP_CIPHER_ALG_NULL ?
+ ODP_CRYPTO_SES_ERR_CIPHER : ODP_CRYPTO_SES_ERR_AUTH;
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+ }
+
+ /* Allocate memory for this session */
+ session = alloc_session();
+ if (NULL == session) {
+ *status = ODP_CRYPTO_SES_ERR_ENOMEM;
+ goto err;
+ }
+
+ /* Copy parameters */
+ session->p = *param;
+
+ session->cipher_range_in_bits = !!param->cipher_range_in_bits;
+ session->auth_range_in_bits = !!param->auth_range_in_bits;
+ session->auth_range_used = 1;
+ session->null_crypto_enable = !!param->null_crypto_enable;
+
+ if (session->null_crypto_enable && param->op_mode == ODP_CRYPTO_SYNC) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ if (session->p.cipher_iv_len > EVP_MAX_IV_LENGTH) {
+ _ODP_DBG("Maximum IV length exceeded\n");
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ if (session->p.auth_iv_len > EVP_MAX_IV_LENGTH) {
+ _ODP_DBG("Maximum auth IV length exceeded\n");
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Derive order */
+ if (ODP_CRYPTO_OP_ENCODE == param->op)
+ session->do_cipher_first = param->auth_cipher_text;
+ else
+ session->do_cipher_first = !param->auth_cipher_text;
+
+ /* Process based on cipher */
+ switch (param->cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ session->cipher.func = null_crypto_routine;
+ session->cipher.init = null_crypto_init_routine;
+ cipher_bit_mode_supported = 1;
+ rc = 0;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ rc = process_cipher_param(session, EVP_des_ede3_cbc());
+ break;
+ case ODP_CIPHER_ALG_3DES_ECB:
+ rc = process_cipher_param(session, EVP_des_ede3_ecb());
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ if (param->cipher_key.length == 16)
+ rc = process_cipher_param(session, EVP_aes_128_cbc());
+ else if (param->cipher_key.length == 24)
+ rc = process_cipher_param(session, EVP_aes_192_cbc());
+ else if (param->cipher_key.length == 32)
+ rc = process_cipher_param(session, EVP_aes_256_cbc());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ if (param->cipher_key.length == 16)
+ rc = process_cipher_param(session, EVP_aes_128_ctr());
+ else if (param->cipher_key.length == 24)
+ rc = process_cipher_param(session, EVP_aes_192_ctr());
+ else if (param->cipher_key.length == 32)
+ rc = process_cipher_param(session, EVP_aes_256_ctr());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_ECB:
+ if (param->cipher_key.length == 16)
+ rc = process_cipher_param(session, EVP_aes_128_ecb());
+ else if (param->cipher_key.length == 24)
+ rc = process_cipher_param(session, EVP_aes_192_ecb());
+ else if (param->cipher_key.length == 32)
+ rc = process_cipher_param(session, EVP_aes_256_ecb());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CFB128:
+ if (param->cipher_key.length == 16)
+ rc = process_cipher_param(session,
+ EVP_aes_128_cfb128());
+ else if (param->cipher_key.length == 24)
+ rc = process_cipher_param(session,
+ EVP_aes_192_cfb128());
+ else if (param->cipher_key.length == 32)
+ rc = process_cipher_param(session,
+ EVP_aes_256_cfb128());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_XTS:
+ if (param->cipher_key.length == 32)
+ rc = process_xts_param(session, EVP_aes_128_xts());
+ else if (param->cipher_key.length == 64)
+ rc = process_xts_param(session, EVP_aes_256_xts());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ /* AES-GCM requires to do both auth and
+ * cipher at the same time */
+ if (param->auth_alg != ODP_AUTH_ALG_AES_GCM)
+ rc = -1;
+ else if (param->cipher_key.length == 16)
+ rc = process_aes_gcm_param(session, EVP_aes_128_gcm());
+ else if (param->cipher_key.length == 24)
+ rc = process_aes_gcm_param(session, EVP_aes_192_gcm());
+ else if (param->cipher_key.length == 32)
+ rc = process_aes_gcm_param(session, EVP_aes_256_gcm());
+ else
+ rc = -1;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ /* AES-CCM requires to do both auth and
+ * cipher at the same time */
+ if (param->auth_alg != ODP_AUTH_ALG_AES_CCM)
+ rc = -1;
+ else if (param->cipher_key.length == 16)
+ rc = process_aes_ccm_param(session, EVP_aes_128_ccm());
+ else if (param->cipher_key.length == 24)
+ rc = process_aes_ccm_param(session, EVP_aes_192_ccm());
+ else if (param->cipher_key.length == 32)
+ rc = process_aes_ccm_param(session, EVP_aes_256_ccm());
+ else
+ rc = -1;
+ break;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ /* ChaCha20_Poly1305 requires to do both auth and
+ * cipher at the same time */
+ if (param->auth_alg != ODP_AUTH_ALG_CHACHA20_POLY1305)
+ rc = -1;
+ else
+ rc = process_aes_gcm_param(session,
+ EVP_chacha20_poly1305());
+ break;
+#endif
+ case ODP_CIPHER_ALG_AES_EEA2:
+ if (param->cipher_key.length == 16)
+ rc = process_cipher_param_bits(session,
+ EVP_aes_128_ctr());
+ else
+ rc = -1;
+ cipher_bit_mode_supported = 1;
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (session->cipher_range_in_bits && !cipher_bit_mode_supported)
+ rc = -1;
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_CIPHER;
+ goto err;
+ }
+
+ /* Process based on auth */
+ switch (param->auth_alg) {
+ case ODP_AUTH_ALG_NULL:
+ session->auth.func = null_crypto_routine;
+ session->auth.init = null_crypto_init_routine;
+ auth_bit_mode_supported = 1;
+ rc = 0;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ rc = process_auth_hmac_param(session, EVP_md5());
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ rc = process_auth_hmac_param(session, EVP_sha1());
+ break;
+ case ODP_AUTH_ALG_SHA224_HMAC:
+ rc = process_auth_hmac_param(session, EVP_sha224());
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ rc = process_auth_hmac_param(session, EVP_sha256());
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ rc = process_auth_hmac_param(session, EVP_sha384());
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ rc = process_auth_hmac_param(session, EVP_sha512());
+ break;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ rc = process_aesxcbc_param(session, EVP_aes_128_ecb());
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ /* AES-GCM requires to do both auth and
+ * cipher at the same time */
+ if (param->cipher_alg == ODP_CIPHER_ALG_AES_GCM) {
+ session->auth.func = null_crypto_routine;
+ session->auth.init = null_crypto_init_routine;
+ rc = 0;
+ } else {
+ rc = -1;
+ }
+ session->auth_range_used = 0;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ if (param->auth_key.length == 16)
+ rc = process_aes_gmac_param(session, EVP_aes_128_gcm());
+ else if (param->auth_key.length == 24)
+ rc = process_aes_gmac_param(session, EVP_aes_192_gcm());
+ else if (param->auth_key.length == 32)
+ rc = process_aes_gmac_param(session, EVP_aes_256_gcm());
+ else
+ rc = -1;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ /* AES-CCM requires to do both auth and
+ * cipher at the same time */
+ if (param->cipher_alg == ODP_CIPHER_ALG_AES_CCM) {
+ session->auth.func = null_crypto_routine;
+ session->auth.init = null_crypto_init_routine;
+ rc = 0;
+ } else {
+ rc = -1;
+ }
+ session->auth_range_used = 0;
+ break;
+ case ODP_AUTH_ALG_AES_CMAC:
+ if (param->auth_key.length == 16)
+ rc = process_auth_cmac_param(session,
+ EVP_aes_128_cbc());
+ else if (param->auth_key.length == 24)
+ rc = process_auth_cmac_param(session,
+ EVP_aes_192_cbc());
+ else if (param->auth_key.length == 32)
+ rc = process_auth_cmac_param(session,
+ EVP_aes_256_cbc());
+ else
+ rc = -1;
+ break;
+#if _ODP_HAVE_CHACHA20_POLY1305
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ /* ChaCha20_Poly1305 requires to do both auth and
+ * cipher at the same time */
+ if (param->cipher_alg == ODP_CIPHER_ALG_CHACHA20_POLY1305) {
+ session->auth.func = null_crypto_routine;
+ session->auth.init = null_crypto_init_routine;
+ rc = 0;
+ } else {
+ rc = -1;
+ }
+ session->auth_range_used = 0;
+ break;
+#endif
+ case ODP_AUTH_ALG_AES_EIA2:
+ if (param->auth_key.length == 16)
+ rc = process_auth_cmac_eia2_param(session,
+ EVP_aes_128_cbc());
+ else
+ rc = -1;
+ break;
+ case ODP_AUTH_ALG_MD5:
+ rc = process_digest_param(session, EVP_md5());
+ break;
+ case ODP_AUTH_ALG_SHA1:
+ rc = process_digest_param(session, EVP_sha1());
+ break;
+ case ODP_AUTH_ALG_SHA224:
+ rc = process_digest_param(session, EVP_sha224());
+ break;
+ case ODP_AUTH_ALG_SHA256:
+ rc = process_digest_param(session, EVP_sha256());
+ break;
+ case ODP_AUTH_ALG_SHA384:
+ rc = process_digest_param(session, EVP_sha384());
+ break;
+ case ODP_AUTH_ALG_SHA512:
+ rc = process_digest_param(session, EVP_sha512());
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (session->auth_range_in_bits && !auth_bit_mode_supported)
+ rc = -1;
+
+ /* Check result */
+ if (rc) {
+ *status = ODP_CRYPTO_SES_ERR_AUTH;
+ goto err;
+ }
+
+ /* We're happy */
+ *session_out = (intptr_t)session;
+ *status = ODP_CRYPTO_SES_ERR_NONE;
+ return 0;
+
+err:
+ /* error status should be set at this moment */
+ if (session != NULL)
+ free_session(session);
+ *session_out = ODP_CRYPTO_SESSION_INVALID;
+ return -1;
+}
+
+int odp_crypto_session_destroy(odp_crypto_session_t session)
+{
+ odp_crypto_generic_session_t *generic;
+
+ generic = (odp_crypto_generic_session_t *)(intptr_t)session;
+ memset(generic, 0, sizeof(*generic));
+ free_session(generic);
+ return 0;
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static void ODP_UNUSED openssl_thread_id(CRYPTO_THREADID ODP_UNUSED *id)
+{
+ CRYPTO_THREADID_set_numeric(id, odp_thread_id());
+}
+
+static void ODP_UNUSED openssl_lock(int mode, int n,
+ const char *file ODP_UNUSED,
+ int line ODP_UNUSED)
+{
+ if (mode & CRYPTO_LOCK)
+ odp_ticketlock_lock(&global->openssl_lock[n]);
+ else
+ odp_ticketlock_unlock(&global->openssl_lock[n]);
+}
+#endif
+
+int _odp_crypto_init_global(void)
+{
+ size_t mem_size;
+ odp_shm_t shm;
+ int idx;
+ int nlocks = CRYPTO_num_locks();
+
+ if (odp_global_ro.disable.crypto) {
+ _ODP_PRINT("\nODP crypto is DISABLED\n");
+ return 0;
+ }
+
+ /* Calculate the memory size we need */
+ mem_size = sizeof(odp_crypto_global_t);
+ mem_size += nlocks * sizeof(odp_ticketlock_t);
+
+ /* Allocate our globally shared memory */
+ shm = odp_shm_reserve("_odp_crypto_ssl_global", mem_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (ODP_SHM_INVALID == shm) {
+ _ODP_ERR("unable to allocate crypto pool\n");
+ return -1;
+ }
+
+ global = odp_shm_addr(shm);
+
+ /* Clear it out */
+ memset(global, 0, mem_size);
+
+ /* Initialize free list and lock */
+ for (idx = 0; idx < MAX_SESSIONS; idx++) {
+ global->sessions[idx].next = global->free;
+ global->free = &global->sessions[idx];
+ }
+ odp_spinlock_init(&global->lock);
+
+ if (nlocks > 0) {
+ for (idx = 0; idx < nlocks; idx++)
+ odp_ticketlock_init(&global->openssl_lock[idx]);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_THREADID_set_callback(openssl_thread_id);
+ CRYPTO_set_locking_callback(openssl_lock);
+#endif
+ }
+
+ return 0;
+}
+
+int _odp_crypto_term_global(void)
+{
+ int rc = 0;
+ int ret;
+ int count = 0;
+ odp_crypto_generic_session_t *session;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (session = global->free; session != NULL; session = session->next)
+ count++;
+ if (count != MAX_SESSIONS) {
+ _ODP_ERR("crypto sessions still active\n");
+ rc = -1;
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_id_callback(NULL);
+#endif
+
+ ret = odp_shm_free(odp_shm_lookup("_odp_crypto_ssl_global"));
+ if (ret < 0) {
+ _ODP_ERR("shm free failed for crypto_pool\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int _odp_crypto_init_local(void)
+{
+ unsigned i;
+ int id;
+
+ memset(&local, 0, sizeof(local));
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ local.hmac_ctx[i] = HMAC_CTX_new();
+ local.cmac_ctx[i] = CMAC_CTX_new();
+ local.cipher_ctx[i] = EVP_CIPHER_CTX_new();
+ local.mac_cipher_ctx[i] = EVP_CIPHER_CTX_new();
+ local.md_ctx[i] = EVP_MD_CTX_new();
+
+ if (local.hmac_ctx[i] == NULL ||
+ local.cmac_ctx[i] == NULL ||
+ local.md_ctx[i] == NULL ||
+ local.cipher_ctx[i] == NULL ||
+ local.mac_cipher_ctx[i] == NULL) {
+ _odp_crypto_term_local();
+ return -1;
+ }
+ }
+
+ id = odp_thread_id();
+ local.ctx_valid = global->ctx_valid[id];
+ /* No need to clear flags here, alloc_session did the job for us */
+
+ return 0;
+}
+
+int _odp_crypto_term_local(void)
+{
+ unsigned i;
+
+ if (odp_global_ro.disable.crypto)
+ return 0;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ if (local.cmac_ctx[i] != NULL)
+ CMAC_CTX_free(local.cmac_ctx[i]);
+ if (local.hmac_ctx[i] != NULL)
+ HMAC_CTX_free(local.hmac_ctx[i]);
+ if (local.cipher_ctx[i] != NULL)
+ EVP_CIPHER_CTX_free(local.cipher_ctx[i]);
+ if (local.mac_cipher_ctx[i] != NULL)
+ EVP_CIPHER_CTX_free(local.mac_cipher_ctx[i]);
+ if (local.md_ctx[i] != NULL)
+ EVP_MD_CTX_free(local.md_ctx[i]);
+ }
+
+ return 0;
+}
+
+void odp_crypto_session_param_init(odp_crypto_session_param_t *param)
+{
+ memset(param, 0, sizeof(odp_crypto_session_param_t));
+}
+
+uint64_t odp_crypto_session_to_u64(odp_crypto_session_t hdl)
+{
+ return (uint64_t)hdl;
+}
+
+static int copy_data_and_metadata(odp_packet_t dst, odp_packet_t src)
+{
+ int md_copy;
+ int rc;
+
+ md_copy = _odp_packet_copy_md_possible(odp_packet_pool(dst),
+ odp_packet_pool(src));
+ if (odp_unlikely(md_copy < 0)) {
+ _ODP_ERR("Unable to copy packet metadata\n");
+ return -1;
+ }
+
+ rc = odp_packet_copy_from_pkt(dst, 0, src, 0, odp_packet_len(src));
+ if (odp_unlikely(rc < 0)) {
+ _ODP_ERR("Unable to copy packet data\n");
+ return -1;
+ }
+
+ _odp_packet_copy_md(packet_hdr(dst), packet_hdr(src), md_copy);
+ return 0;
+}
+
+static odp_packet_t get_output_packet(const odp_crypto_generic_session_t *session,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out)
+{
+ int rc;
+
+ if (odp_likely(pkt_in == pkt_out))
+ return pkt_out;
+
+ if (pkt_out == ODP_PACKET_INVALID) {
+ odp_pool_t pool = session->p.output_pool;
+
+ _ODP_ASSERT(pool != ODP_POOL_INVALID);
+ if (pool == odp_packet_pool(pkt_in)) {
+ pkt_out = pkt_in;
+ } else {
+ pkt_out = odp_packet_copy(pkt_in, pool);
+ if (odp_likely(pkt_out != ODP_PACKET_INVALID))
+ odp_packet_free(pkt_in);
+ }
+ return pkt_out;
+ }
+ rc = copy_data_and_metadata(pkt_out, pkt_in);
+ if (odp_unlikely(rc < 0))
+ return ODP_PACKET_INVALID;
+
+ odp_packet_free(pkt_in);
+ return pkt_out;
+}
+
+static
+int crypto_int(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_alg_err_t rc_cipher = ODP_CRYPTO_ALG_ERR_NONE;
+ odp_crypto_alg_err_t rc_auth = ODP_CRYPTO_ALG_ERR_NONE;
+ odp_crypto_generic_session_t *session;
+ odp_packet_t out_pkt;
+ odp_crypto_packet_result_t *op_result;
+
+ session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ out_pkt = pkt_in;
+ } else if (session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP &&
+ *pkt_out == ODP_PACKET_INVALID) {
+ out_pkt = pkt_in;
+ } else {
+ out_pkt = get_output_packet(session, pkt_in, *pkt_out);
+ if (odp_unlikely(out_pkt == ODP_PACKET_INVALID))
+ return -1;
+ }
+
+ if (odp_unlikely(session->null_crypto_enable && param->null_crypto))
+ goto out;
+
+ if (ODP_DEBUG) {
+ if (session->p.auth_alg != ODP_AUTH_ALG_NULL &&
+ param->hash_result_offset + session->p.auth_digest_len
+ > odp_packet_len(out_pkt)) {
+ _ODP_ERR("Invalid hash result offset\n");
+ rc_cipher = ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+ rc_auth = ODP_CRYPTO_ALG_ERR_DATA_SIZE;
+ goto out;
+ }
+ }
+ _ODP_ASSERT(session->p.cipher_iv_len == 0 || param->cipher_iv_ptr != NULL);
+ _ODP_ASSERT(session->p.auth_iv_len == 0 || param->auth_iv_ptr != NULL);
+
+ crypto_init(session);
+
+ /* Invoke the functions */
+ if (session->do_cipher_first) {
+ rc_cipher = session->cipher.func(out_pkt, param, session);
+ rc_auth = session->auth.func(out_pkt, param, session);
+ } else {
+ rc_auth = session->auth.func(out_pkt, param, session);
+ rc_cipher = session->cipher.func(out_pkt, param, session);
+ }
+
+out:
+ /* Fill in result */
+ packet_subtype_set(out_pkt, ODP_EVENT_PACKET_CRYPTO);
+ op_result = &packet_hdr(out_pkt)->crypto_op_result;
+ op_result->cipher_status.alg_err = rc_cipher;
+ op_result->auth_status.alg_err = rc_auth;
+
+ /* Synchronous, simply return results */
+ *pkt_out = out_pkt;
+
+ return 0;
+}
+
+/*
+ * Copy cipher range and auth range from src to dst,
+ * with shifting by dst_offset_shift.
+ */
+static void copy_ranges(odp_packet_t dst,
+ odp_packet_t src,
+ const odp_crypto_generic_session_t *session,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_packet_data_range_t c_range = param->cipher_range;
+ odp_packet_data_range_t a_range = param->auth_range;
+ int32_t shift = param->dst_offset_shift;
+ int rc;
+
+ if (session->cipher_range_in_bits) {
+ c_range.offset /= 8;
+ c_range.length = (c_range.length + 7) / 8;
+ }
+ if (session->auth_range_in_bits) {
+ a_range.offset /= 8;
+ a_range.length = (a_range.length + 7) / 8;
+ }
+
+ if (c_range.length > 0) {
+ rc = odp_packet_copy_from_pkt(dst, c_range.offset + shift,
+ src, c_range.offset,
+ c_range.length);
+ if (rc) {
+ _ODP_ERR("cipher range copying failed\n");
+ return;
+ }
+ }
+ if (session->auth_range_used && a_range.length > 0) {
+ rc = odp_packet_copy_from_pkt(dst, a_range.offset + shift,
+ src, a_range.offset,
+ a_range.length);
+ if (rc) {
+ _ODP_ERR("auth range copying failed\n");
+ return;
+ }
+ }
+}
+
+static int crypto_int_oop_encode(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_generic_session_t *session,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_packet_op_param_t new_param = *param;
+ const uint32_t c_scale = session->cipher_range_in_bits ? 8 : 1;
+ const uint32_t a_scale = session->auth_range_in_bits ? 8 : 1;
+
+ copy_ranges(*pkt_out, pkt_in, session, param);
+
+ new_param.cipher_range.offset += param->dst_offset_shift * c_scale;
+ new_param.auth_range.offset += param->dst_offset_shift * a_scale;
+
+ return crypto_int(*pkt_out, pkt_out, &new_param);
+}
+
+static int crypto_int_oop_decode(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_generic_session_t *session,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_packet_t copy;
+ int rc;
+
+ copy = odp_packet_copy(pkt_in, odp_packet_pool(pkt_in));
+ if (copy == ODP_PACKET_INVALID)
+ return -1;
+
+ rc = crypto_int(copy, &copy, param);
+ if (rc < 0) {
+ odp_packet_free(copy);
+ return rc;
+ }
+
+ copy_ranges(*pkt_out, copy, session, param);
+
+ packet_subtype_set(*pkt_out, ODP_EVENT_PACKET_CRYPTO);
+ packet_hdr(*pkt_out)->crypto_op_result = packet_hdr(copy)->crypto_op_result;
+ odp_packet_free(copy);
+
+ return 0;
+}
+
+/*
+ * Slow out-of-place operation implemented using copying and in-place operation
+ */
+static int crypto_int_oop(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ const odp_crypto_packet_op_param_t *param)
+{
+ odp_crypto_generic_session_t *session;
+ int rc;
+
+ session = (odp_crypto_generic_session_t *)(intptr_t)param->session;
+
+ if (session->p.op == ODP_CRYPTO_OP_ENCODE)
+ rc = crypto_int_oop_encode(pkt_in, pkt_out, session, param);
+ else
+ rc = crypto_int_oop_decode(pkt_in, pkt_out, session, param);
+ if (rc)
+ return rc;
+
+ if (session->p.op_mode == ODP_CRYPTO_ASYNC)
+ packet_hdr(*pkt_out)->crypto_op_result.pkt_in = pkt_in;
+
+ return 0;
+}
+
+int odp_crypto_op(const odp_packet_t pkt_in[],
+ odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ int i, rc;
+ odp_crypto_generic_session_t *session;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_SYNC == session->p.op_mode);
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ } else if (session->p.op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ rc = crypto_int_oop(pkt_in[i], &pkt_out[i], &param[i]);
+ } else if (session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ if (pkt_out[i] == ODP_PACKET_INVALID) /* basic */
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ else /* oop */
+ rc = crypto_int_oop(pkt_in[i], &pkt_out[i], &param[i]);
+ } else {
+ _ODP_ASSERT(session->p.op_type == ODP_CRYPTO_OP_TYPE_LEGACY);
+ rc = crypto_int(pkt_in[i], &pkt_out[i], &param[i]);
+ }
+ if (rc < 0)
+ break;
+ }
+
+ return i;
+}
+
+int odp_crypto_op_enq(const odp_packet_t pkt_in[],
+ const odp_packet_t pkt_out[],
+ const odp_crypto_packet_op_param_t param[],
+ int num_pkt)
+{
+ odp_packet_t pkt;
+ odp_event_t event;
+ odp_crypto_generic_session_t *session;
+ int i, rc;
+
+ for (i = 0; i < num_pkt; i++) {
+ session = (odp_crypto_generic_session_t *)(intptr_t)param[i].session;
+ _ODP_ASSERT(ODP_CRYPTO_ASYNC == session->p.op_mode);
+ _ODP_ASSERT(ODP_QUEUE_INVALID != session->p.compl_queue);
+
+ if (odp_likely(session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC)) {
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ } else if (session->p.op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ pkt = pkt_out[i];
+ rc = crypto_int_oop(pkt_in[i], &pkt, &param[i]);
+ } else if (session->p.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ pkt = pkt_out[i];
+ if (pkt_out[i] == ODP_PACKET_INVALID) /* basic */
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ else /* oop */
+ rc = crypto_int_oop(pkt_in[i], &pkt, &param[i]);
+ } else {
+ _ODP_ASSERT(session->p.op_type == ODP_CRYPTO_OP_TYPE_LEGACY);
+ pkt = pkt_out[i];
+ rc = crypto_int(pkt_in[i], &pkt, &param[i]);
+ }
+ if (rc < 0)
+ break;
+
+ event = odp_packet_to_event(pkt);
+ if (odp_queue_enq(session->p.compl_queue, event)) {
+ odp_event_free(event);
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/platform/linux-generic/odp_dma.c b/platform/linux-generic/odp_dma.c
new file mode 100644
index 000000000..879a2a59b
--- /dev/null
+++ b/platform/linux-generic/odp_dma.c
@@ -0,0 +1,861 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp/api/dma.h>
+#include <odp/api/event.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/ticketlock.h>
+#include <odp/api/align.h>
+#include <odp/api/buffer.h>
+#include <odp/api/stash.h>
+#include <odp/api/packet.h>
+#include <odp/api/pool.h>
+#include <odp/api/queue.h>
+
+#include <odp/api/plat/std_inlines.h>
+#include <odp/api/plat/strong_types.h>
+
+#include <odp_global_data.h>
+#include <odp_debug_internal.h>
+#include <odp_init_internal.h>
+#include <odp_event_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_string_internal.h>
+
+#include <string.h>
+#include <inttypes.h>
+
+#define MAX_SESSIONS CONFIG_MAX_DMA_SESSIONS
+#define MAX_TRANSFERS 256
+#define MAX_SEGS 16
+#define MAX_SEG_LEN (128 * 1024)
+
+ODP_STATIC_ASSERT(MAX_TRANSFERS < UINT32_MAX, "Too many inflight transfers");
+
+typedef struct segment_t {
+ void *addr;
+ uint32_t len;
+
+} segment_t;
+
+typedef struct transfer_t {
+ void *dst;
+ void *src;
+ uint32_t len;
+
+} transfer_t;
+
+typedef struct result_t {
+ void *user_ptr;
+
+} result_t;
+
+typedef struct ODP_ALIGNED_CACHE dma_session_t {
+ odp_ticketlock_t lock;
+ odp_dma_param_t dma_param;
+ uint8_t active;
+ char name[ODP_DMA_NAME_LEN];
+ odp_stash_t stash;
+ result_t result[MAX_TRANSFERS];
+
+} dma_session_t;
+
+typedef struct dma_global_t {
+ odp_shm_t shm;
+
+ /* Buffer pool capability and default parameters */
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+
+ dma_session_t session[MAX_SESSIONS];
+
+} dma_global_t;
+
+static dma_global_t *_odp_dma_glb;
+
+static inline dma_session_t *dma_session_from_handle(odp_dma_t dma)
+{
+ return (dma_session_t *)(uintptr_t)dma;
+}
+
+int odp_dma_capability(odp_dma_capability_t *capa)
+{
+ if (odp_global_ro.disable.dma) {
+ _ODP_ERR("DMA is disabled\n");
+ return -1;
+ }
+
+ memset(capa, 0, sizeof(odp_dma_capability_t));
+
+ capa->max_sessions = MAX_SESSIONS;
+ capa->max_transfers = MAX_TRANSFERS;
+ capa->max_src_segs = MAX_SEGS;
+ capa->max_dst_segs = MAX_SEGS;
+ capa->max_segs = 2 * MAX_SEGS;
+ capa->max_seg_len = MAX_SEG_LEN;
+
+ capa->compl_mode_mask = ODP_DMA_COMPL_SYNC | ODP_DMA_COMPL_NONE |
+ ODP_DMA_COMPL_EVENT | ODP_DMA_COMPL_POLL;
+
+ capa->queue_type_sched = 1;
+ capa->queue_type_plain = 1;
+
+ capa->pool.max_pools = _odp_dma_glb->pool_capa.buf.max_pools;
+ capa->pool.max_num = _odp_dma_glb->pool_capa.buf.max_num;
+ capa->pool.max_uarea_size = _odp_dma_glb->pool_capa.buf.max_uarea_size;
+ capa->pool.uarea_persistence = _odp_dma_glb->pool_capa.buf.uarea_persistence;
+ capa->pool.min_cache_size = _odp_dma_glb->pool_capa.buf.min_cache_size;
+ capa->pool.max_cache_size = _odp_dma_glb->pool_capa.buf.max_cache_size;
+
+ return 0;
+}
+
+void odp_dma_param_init(odp_dma_param_t *param)
+{
+ memset(param, 0, sizeof(odp_dma_param_t));
+
+ param->direction = ODP_DMA_MAIN_TO_MAIN;
+ param->type = ODP_DMA_TYPE_COPY;
+ param->mt_mode = ODP_DMA_MT_SAFE;
+ param->order = ODP_DMA_ORDER_NONE;
+}
+
+static odp_stash_t create_stash(void)
+{
+ odp_stash_param_t stash_param;
+ odp_stash_t stash;
+ uint32_t id, tmp, i;
+ int32_t ret;
+
+ odp_stash_param_init(&stash_param);
+ stash_param.num_obj = MAX_TRANSFERS;
+ stash_param.obj_size = sizeof(uint32_t);
+ stash_param.cache_size = 0;
+
+ stash = odp_stash_create("_odp_dma_transfer_id", &stash_param);
+
+ if (stash == ODP_STASH_INVALID) {
+ _ODP_ERR("Stash create failed\n");
+ return ODP_STASH_INVALID;
+ }
+
+ /* Zero is invalid ID */
+ for (id = 1; id < MAX_TRANSFERS + 1; id++) {
+ ret = odp_stash_put_u32(stash, &id, 1);
+ if (ret != 1) {
+ _ODP_ERR("Stash put failed: %i, %u\n", ret, id);
+ break;
+ }
+ }
+
+ if (ret != 1) {
+ for (i = 0; i < id; i++) {
+ if (odp_stash_get_u32(stash, &tmp, 1) != 1) {
+ _ODP_ERR("Stash get failed: %u\n", i);
+ break;
+ }
+ }
+
+ if (odp_stash_destroy(stash))
+ _ODP_ERR("Stash destroy failed\n");
+
+ return ODP_STASH_INVALID;
+ }
+
+ return stash;
+}
+
+static int destroy_stash(odp_stash_t stash)
+{
+ uint32_t tmp;
+ int32_t num;
+ int ret = 0;
+
+ while (1) {
+ num = odp_stash_get_u32(stash, &tmp, 1);
+
+ if (num == 1)
+ continue;
+
+ if (num == 0)
+ break;
+
+ _ODP_ERR("Stash get failed: %i\n", num);
+ ret = -1;
+ break;
+ }
+
+ if (odp_stash_destroy(stash)) {
+ _ODP_ERR("Stash destroy failed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+odp_dma_t odp_dma_create(const char *name, const odp_dma_param_t *param)
+{
+ odp_dma_capability_t dma_capa;
+ int i;
+ dma_session_t *session = NULL;
+
+ if (odp_global_ro.disable.dma) {
+ _ODP_ERR("DMA is disabled\n");
+ return ODP_DMA_INVALID;
+ }
+
+ if ((param->direction != ODP_DMA_MAIN_TO_MAIN) ||
+ (param->type != ODP_DMA_TYPE_COPY)) {
+ _ODP_ERR("Bad DMA parameter\n");
+ return ODP_DMA_INVALID;
+ }
+
+ if (param->compl_mode_mask == 0) {
+ _ODP_ERR("Empty compl mode mask\n");
+ return ODP_DMA_INVALID;
+ }
+
+ if (odp_dma_capability(&dma_capa)) {
+ _ODP_ERR("DMA capa failed\n");
+ return ODP_DMA_INVALID;
+ }
+
+ if (param->compl_mode_mask & ~dma_capa.compl_mode_mask) {
+ _ODP_ERR("Compl mode not supported\n");
+ return ODP_DMA_INVALID;
+ }
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ if (_odp_dma_glb->session[i].active)
+ continue;
+
+ odp_ticketlock_lock(&_odp_dma_glb->session[i].lock);
+
+ if (_odp_dma_glb->session[i].active) {
+ odp_ticketlock_unlock(&_odp_dma_glb->session[i].lock);
+ continue;
+ }
+
+ session = &_odp_dma_glb->session[i];
+ session->active = 1;
+ odp_ticketlock_unlock(&_odp_dma_glb->session[i].lock);
+ break;
+ }
+
+ if (session == NULL) {
+ _ODP_DBG("Out of DMA sessions\n");
+ return ODP_DMA_INVALID;
+ }
+
+ session->stash = ODP_STASH_INVALID;
+
+ /* Create stash for transfer IDs */
+ if (param->compl_mode_mask & ODP_DMA_COMPL_POLL) {
+ session->stash = create_stash();
+
+ if (session->stash == ODP_STASH_INVALID) {
+ session->active = 0;
+ return ODP_DMA_INVALID;
+ }
+ }
+
+ session->name[0] = 0;
+
+ if (name)
+ _odp_strcpy(session->name, name, ODP_DMA_NAME_LEN);
+
+ session->dma_param = *param;
+
+ return (odp_dma_t)session;
+}
+
+int odp_dma_destroy(odp_dma_t dma)
+{
+ dma_session_t *session = dma_session_from_handle(dma);
+ int ret = 0;
+
+ if (dma == ODP_DMA_INVALID) {
+ _ODP_ERR("Bad DMA handle\n");
+ return -1;
+ }
+
+ if (session->stash != ODP_STASH_INVALID)
+ if (destroy_stash(session->stash))
+ ret = -1;
+
+ odp_ticketlock_lock(&session->lock);
+
+ if (session->active == 0) {
+ _ODP_ERR("Session not created\n");
+ odp_ticketlock_unlock(&session->lock);
+ return -1;
+ }
+
+ session->active = 0;
+ odp_ticketlock_unlock(&session->lock);
+
+ return ret;
+}
+
+odp_dma_t odp_dma_lookup(const char *name)
+{
+ dma_session_t *session;
+ int i;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ session = &_odp_dma_glb->session[i];
+
+ odp_ticketlock_lock(&session->lock);
+
+ if (session->active == 0) {
+ odp_ticketlock_unlock(&session->lock);
+ continue;
+ }
+
+ if (strcmp(session->name, name) == 0) {
+ /* found it */
+ odp_ticketlock_unlock(&session->lock);
+ return (odp_dma_t)session;
+ }
+ odp_ticketlock_unlock(&session->lock);
+ }
+
+ return ODP_DMA_INVALID;
+}
+
+static uint32_t transfer_len(const odp_dma_transfer_param_t *trs_param)
+{
+ uint32_t i;
+ uint32_t src_len = 0;
+ uint32_t dst_len = 0;
+
+ for (i = 0; i < trs_param->num_src; i++)
+ src_len += trs_param->src_seg[i].len;
+
+ for (i = 0; i < trs_param->num_dst; i++)
+ dst_len += trs_param->dst_seg[i].len;
+
+ if (src_len != dst_len)
+ return 0;
+
+ return src_len;
+}
+
+static inline void segment_raw(segment_t seg[], int num, const odp_dma_seg_t *dma_seg)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ seg[i].addr = dma_seg[i].addr;
+ seg[i].len = dma_seg[i].len;
+ }
+}
+
+static inline int segment_pkt(segment_t seg[], int num_seg, const odp_dma_seg_t *dma_seg)
+{
+ odp_packet_t pkt;
+ uint32_t offset;
+ void *addr;
+ uint32_t seg_len, tot_len, len;
+ int i;
+ int num = 0;
+
+ for (i = 0; i < num_seg; i++) {
+ pkt = dma_seg[i].packet;
+ offset = dma_seg[i].offset;
+ tot_len = dma_seg[i].len;
+
+ if (odp_unlikely(offset + tot_len > odp_packet_len(pkt))) {
+ _ODP_ERR("Bad packet segment len/offset (%u/%u)\n", tot_len, offset);
+ return 0;
+ }
+
+ while (tot_len) {
+ addr = odp_packet_offset(pkt, offset, &seg_len, NULL);
+
+ if (odp_unlikely(addr == NULL)) {
+ _ODP_ERR("Bad packet offset %u\n", offset);
+ return 0;
+ }
+
+ seg[num].addr = addr;
+ len = tot_len;
+ if (tot_len > seg_len)
+ len = seg_len;
+
+ seg[num].len = len;
+
+ tot_len -= len;
+ offset += len;
+ num++;
+
+ if (odp_unlikely(num > MAX_SEGS)) {
+ _ODP_ERR("Too many packet segments\n");
+ return 0;
+ }
+ }
+ }
+
+ return num;
+}
+
+static int transfer_table(transfer_t *trs, const segment_t src_seg[], const segment_t dst_seg[],
+ int max_num, uint32_t tot_len)
+{
+ uint32_t len, src_len, dst_len;
+ uint8_t *src_ptr, *dst_ptr;
+ int i;
+ int src = 0;
+ int dst = 0;
+
+ src_ptr = src_seg[0].addr;
+ dst_ptr = dst_seg[0].addr;
+ src_len = src_seg[0].len;
+ dst_len = dst_seg[0].len;
+
+ len = src_len;
+ if (dst_len < src_len)
+ len = dst_len;
+
+ for (i = 0; i < max_num; i++) {
+ trs[i].src = src_ptr;
+ trs[i].dst = dst_ptr;
+ trs[i].len = len;
+ tot_len -= len;
+
+ if (tot_len == 0)
+ break;
+
+ if (dst_len < src_len) {
+ dst++;
+ dst_ptr = dst_seg[dst].addr;
+ dst_len = dst_seg[dst].len;
+ src_ptr += len;
+ src_len -= len;
+ } else if (src_len < dst_len) {
+ src++;
+ src_ptr = src_seg[src].addr;
+ src_len = src_seg[src].len;
+ dst_ptr += len;
+ dst_len -= len;
+ } else { /* equal lengths */
+ dst++;
+ src++;
+ dst_ptr = dst_seg[dst].addr;
+ dst_len = dst_seg[dst].len;
+ src_ptr = src_seg[src].addr;
+ src_len = src_seg[src].len;
+ }
+
+ len = src_len;
+ if (dst_len < src_len)
+ len = dst_len;
+ }
+
+ return i + 1;
+}
+
+int odp_dma_transfer(odp_dma_t dma, const odp_dma_transfer_param_t *transfer,
+ odp_dma_result_t *result)
+{
+ int num, i;
+ uint32_t tot_len;
+ dma_session_t *session = dma_session_from_handle(dma);
+ int num_src, num_dst;
+ const int max_num = 2 * MAX_SEGS;
+ transfer_t trs[max_num];
+ segment_t src[MAX_SEGS];
+ segment_t dst[MAX_SEGS];
+
+ if (odp_unlikely(dma == ODP_DMA_INVALID)) {
+ _ODP_ERR("Bad DMA handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(session->active == 0)) {
+ _ODP_ERR("Session not created\n");
+ return -1;
+ }
+
+ if (odp_unlikely(transfer->num_src == 0 || transfer->num_src > MAX_SEGS)) {
+ _ODP_ERR("Bad number of src segments\n");
+ return -1;
+ }
+
+ if (odp_unlikely(transfer->num_dst == 0 || transfer->num_dst > MAX_SEGS)) {
+ _ODP_ERR("Bad number of dst segments\n");
+ return -1;
+ }
+
+ tot_len = transfer_len(transfer);
+
+ if (odp_unlikely(tot_len == 0)) {
+ _ODP_ERR("Bad transfer length\n");
+ return -1;
+ }
+
+ if (transfer->src_format == ODP_DMA_FORMAT_ADDR) {
+ num_src = transfer->num_src;
+ segment_raw(src, num_src, transfer->src_seg);
+ } else {
+ num_src = segment_pkt(src, transfer->num_src, transfer->src_seg);
+
+ if (odp_unlikely(num_src == 0))
+ return -1;
+ }
+
+ if (transfer->dst_format == ODP_DMA_FORMAT_ADDR) {
+ num_dst = transfer->num_dst;
+ segment_raw(dst, num_dst, transfer->dst_seg);
+ } else {
+ num_dst = segment_pkt(dst, transfer->num_dst, transfer->dst_seg);
+
+ if (odp_unlikely(num_dst == 0))
+ return -1;
+ }
+
+ num = transfer_table(trs, src, dst, max_num, tot_len);
+
+ if (odp_unlikely(num > max_num)) {
+ _ODP_ERR("Segment table error\n");
+ return -1;
+ }
+
+ for (i = 0; i < num; i++)
+ memcpy(trs[i].dst, trs[i].src, trs[i].len);
+
+ if (result) {
+ memset(result, 0, sizeof(odp_dma_result_t));
+ result->success = 1;
+ }
+
+ return 1;
+}
+
+int odp_dma_transfer_multi(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param[],
+ odp_dma_result_t *result[], int num)
+{
+ int i;
+ odp_dma_result_t *res = NULL;
+ int ret = 0;
+
+ if (odp_unlikely(num < 1)) {
+ _ODP_ERR("Bad number of transfers\n");
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (result)
+ res = result[i];
+
+ ret = odp_dma_transfer(dma, trs_param[i], res);
+
+ if (odp_unlikely(ret != 1))
+ break;
+ }
+
+ if (odp_unlikely(i == 0))
+ return ret;
+
+ return i;
+}
+
+odp_dma_transfer_id_t odp_dma_transfer_id_alloc(odp_dma_t dma)
+{
+ int32_t num;
+ uint32_t id;
+ dma_session_t *session = dma_session_from_handle(dma);
+
+ num = odp_stash_get_u32(session->stash, &id, 1);
+
+ if (odp_unlikely(num != 1))
+ return ODP_DMA_TRANSFER_ID_INVALID;
+
+ return id;
+}
+
+void odp_dma_transfer_id_free(odp_dma_t dma, odp_dma_transfer_id_t transfer_id)
+{
+ int32_t num;
+ dma_session_t *session = dma_session_from_handle(dma);
+ uint32_t id = transfer_id;
+
+ num = odp_stash_put_u32(session->stash, &id, 1);
+
+ if (odp_unlikely(num != 1))
+ _ODP_ERR("Stash put failed\n");
+}
+
+static inline uint32_t index_from_transfer_id(odp_dma_transfer_id_t transfer_id)
+{
+ return transfer_id - 1;
+}
+
+int odp_dma_transfer_start(odp_dma_t dma, const odp_dma_transfer_param_t *transfer,
+ const odp_dma_compl_param_t *compl)
+{
+ int ret;
+ dma_session_t *session = dma_session_from_handle(dma);
+ const uint32_t transfer_id = compl->transfer_id;
+
+ if (odp_unlikely(dma == ODP_DMA_INVALID)) {
+ _ODP_ERR("Bad DMA handle\n");
+ return -1;
+ }
+
+ /* Check completion mode */
+ switch (compl->compl_mode) {
+ case ODP_DMA_COMPL_NONE:
+ break;
+ case ODP_DMA_COMPL_POLL:
+ if (transfer_id == ODP_DMA_TRANSFER_ID_INVALID || transfer_id > MAX_TRANSFERS) {
+ _ODP_ERR("Bad transfer ID: %u\n", transfer_id);
+ return -1;
+ }
+ break;
+ case ODP_DMA_COMPL_EVENT:
+ if (compl->event == ODP_EVENT_INVALID ||
+ compl->queue == ODP_QUEUE_INVALID) {
+ _ODP_ERR("Bad event or queue\n");
+ return -1;
+ }
+ break;
+ default:
+ _ODP_ERR("Bad completion mode %u\n", compl->compl_mode);
+ return -1;
+ }
+
+ ret = odp_dma_transfer(dma, transfer, NULL);
+
+ if (odp_unlikely(ret < 1))
+ return ret;
+
+ if (compl->compl_mode == ODP_DMA_COMPL_POLL) {
+ uint32_t index = index_from_transfer_id(transfer_id);
+
+ session->result[index].user_ptr = compl->user_ptr;
+
+ } else if (compl->compl_mode == ODP_DMA_COMPL_EVENT) {
+ odp_dma_result_t *result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)compl->event;
+
+ if (odp_unlikely(odp_event_type(compl->event) != ODP_EVENT_DMA_COMPL)) {
+ _ODP_ERR("Bad completion event type\n");
+ return -1;
+ }
+
+ result = odp_buffer_addr(buf);
+ result->success = 1;
+ result->user_ptr = compl->user_ptr;
+
+ if (odp_unlikely(odp_queue_enq(compl->queue, compl->event))) {
+ _ODP_ERR("Completion event enqueue failed %" PRIu64 "\n",
+ odp_queue_to_u64(compl->queue));
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int odp_dma_transfer_start_multi(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param[],
+ const odp_dma_compl_param_t *compl_param[], int num)
+{
+ int i;
+ int ret = 0;
+
+ if (odp_unlikely(num < 1)) {
+ _ODP_ERR("Bad number of transfers\n");
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ ret = odp_dma_transfer_start(dma, trs_param[i], compl_param[i]);
+
+ if (odp_unlikely(ret != 1))
+ break;
+ }
+
+ if (odp_unlikely(i == 0))
+ return ret;
+
+ return i;
+}
+
+int odp_dma_transfer_done(odp_dma_t dma, odp_dma_transfer_id_t transfer_id,
+ odp_dma_result_t *result)
+{
+ dma_session_t *session = dma_session_from_handle(dma);
+ const uint32_t id = transfer_id;
+
+ if (odp_unlikely(dma == ODP_DMA_INVALID)) {
+ _ODP_ERR("Bad DMA handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(id == ODP_DMA_TRANSFER_ID_INVALID || id > MAX_TRANSFERS)) {
+ _ODP_ERR("Bad transfer ID: %u\n", id);
+ return -1;
+ }
+
+ if (result) {
+ uint32_t index = index_from_transfer_id(id);
+
+ result->success = 1;
+ result->user_ptr = session->result[index].user_ptr;
+ }
+
+ return 1;
+}
+
+void odp_dma_pool_param_init(odp_dma_pool_param_t *pool_param)
+{
+ memset(pool_param, 0, sizeof(odp_dma_pool_param_t));
+
+ pool_param->cache_size = _odp_dma_glb->pool_param.buf.cache_size;
+}
+
+odp_pool_t odp_dma_pool_create(const char *name, const odp_dma_pool_param_t *dma_pool_param)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ uint32_t num = dma_pool_param->num;
+ uint32_t uarea_size = dma_pool_param->uarea_size;
+ uint32_t cache_size = dma_pool_param->cache_size;
+
+ if (num > _odp_dma_glb->pool_capa.buf.max_num) {
+ _ODP_ERR("Too many DMA completion events: %u\n", num);
+ return ODP_POOL_INVALID;
+ }
+
+ if (uarea_size > _odp_dma_glb->pool_capa.buf.max_uarea_size) {
+ _ODP_ERR("Bad uarea size: %u\n", uarea_size);
+ return ODP_POOL_INVALID;
+ }
+
+ if (cache_size < _odp_dma_glb->pool_capa.buf.min_cache_size ||
+ cache_size > _odp_dma_glb->pool_capa.buf.max_cache_size) {
+ _ODP_ERR("Bad cache size: %u\n", cache_size);
+ return ODP_POOL_INVALID;
+ }
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.uarea_init.init_fn = dma_pool_param->uarea_init.init_fn;
+ pool_param.uarea_init.args = dma_pool_param->uarea_init.args;
+ pool_param.buf.num = num;
+ pool_param.buf.uarea_size = uarea_size;
+ pool_param.buf.cache_size = cache_size;
+ pool_param.buf.size = sizeof(odp_dma_result_t);
+
+ pool = _odp_pool_create(name, &pool_param, ODP_POOL_DMA_COMPL);
+
+ return pool;
+}
+
+uint64_t odp_dma_to_u64(odp_dma_t dma)
+{
+ return _odp_pri(dma);
+}
+
+uint64_t odp_dma_compl_to_u64(odp_dma_compl_t dma_compl)
+{
+ return _odp_pri(dma_compl);
+}
+
+void odp_dma_print(odp_dma_t dma)
+{
+ dma_session_t *session = dma_session_from_handle(dma);
+
+ if (dma == ODP_DMA_INVALID) {
+ _ODP_ERR("Bad DMA handle\n");
+ return;
+ }
+
+ _ODP_PRINT("\nDMA info\n");
+ _ODP_PRINT("--------\n");
+ _ODP_PRINT(" DMA handle 0x%" PRIx64 "\n", odp_dma_to_u64(dma));
+ _ODP_PRINT(" name %s\n", session->name);
+ _ODP_PRINT("\n");
+}
+
+void odp_dma_compl_print(odp_dma_compl_t dma_compl)
+{
+ odp_dma_result_t result;
+ int ret;
+
+ if (dma_compl == ODP_DMA_COMPL_INVALID) {
+ _ODP_ERR("Bad DMA compl handle\n");
+ return;
+ }
+
+ ret = odp_dma_compl_result(dma_compl, &result);
+
+ _ODP_PRINT("\nDMA completion\n");
+ _ODP_PRINT("--------------\n");
+ _ODP_PRINT(" Compl event handle: 0x%" PRIx64 "\n", _odp_pri(dma_compl));
+
+ if (ret == 0) {
+ _ODP_PRINT(" Result: %s\n", result.success ? "success" : "fail");
+ _ODP_PRINT(" User pointer: 0x%" PRIx64 "\n", _odp_pri(result.user_ptr));
+ } else {
+ _ODP_PRINT(" No result metadata\n");
+ }
+
+ _ODP_PRINT("\n");
+}
+
+int _odp_dma_init_global(void)
+{
+ odp_shm_t shm;
+ int i;
+
+ if (odp_global_ro.disable.dma) {
+ _ODP_PRINT("DMA is DISABLED\n");
+ return 0;
+ }
+
+ shm = odp_shm_reserve("_odp_dma_global", sizeof(dma_global_t), ODP_CACHE_LINE_SIZE, 0);
+ _odp_dma_glb = odp_shm_addr(shm);
+
+ if (_odp_dma_glb == NULL) {
+ _ODP_ERR("SHM reserve failed\n");
+ return -1;
+ }
+
+ memset(_odp_dma_glb, 0, sizeof(dma_global_t));
+ _odp_dma_glb->shm = shm;
+
+ odp_pool_param_init(&_odp_dma_glb->pool_param);
+
+ if (odp_pool_capability(&_odp_dma_glb->pool_capa)) {
+ _ODP_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < MAX_SESSIONS; i++)
+ odp_ticketlock_init(&_odp_dma_glb->session[i].lock);
+
+ return 0;
+}
+
+int _odp_dma_term_global(void)
+{
+ odp_shm_t shm;
+
+ if (odp_global_ro.disable.dma)
+ return 0;
+
+ if (_odp_dma_glb == NULL)
+ return 0;
+
+ shm = _odp_dma_glb->shm;
+
+ if (odp_shm_free(shm)) {
+ _ODP_ERR("SHM free failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_dma_api.c b/platform/linux-generic/odp_dma_api.c
new file mode 100644
index 000000000..96b3f7ec3
--- /dev/null
+++ b/platform/linux-generic/odp_dma_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/dma.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/dma_inlines.h>
diff --git a/platform/linux-generic/odp_errno.c b/platform/linux-generic/odp_errno.c
index 408a4b95f..6e9119a5f 100644
--- a/platform/linux-generic/odp_errno.c
+++ b/platform/linux-generic/odp_errno.c
@@ -1,33 +1,30 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <odp/api/errno.h>
-#include <odp_internal.h>
#include <string.h>
#include <stdio.h>
#include <odp_debug_internal.h>
-__thread int __odp_errno;
+__thread int _odp_errno;
int odp_errno(void)
{
- return __odp_errno;
+ return _odp_errno;
}
void odp_errno_zero(void)
{
- __odp_errno = 0;
+ _odp_errno = 0;
}
void odp_errno_print(const char *str)
{
if (str != NULL)
- printf("%s ", str);
-
- ODP_PRINT("%s\n", strerror(__odp_errno));
+ _ODP_PRINT("%s %s\n", str, strerror(_odp_errno));
+ else
+ _ODP_PRINT("%s\n", strerror(_odp_errno));
}
const char *odp_errno_str(int errnum)
diff --git a/platform/linux-generic/odp_event.c b/platform/linux-generic/odp_event.c
index d71f4464a..ce2745497 100644
--- a/platform/linux-generic/odp_event.c
+++ b/platform/linux-generic/odp_event.c
@@ -1,45 +1,131 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
*/
#include <odp/api/event.h>
#include <odp/api/buffer.h>
#include <odp/api/crypto.h>
+#include <odp/api/dma.h>
#include <odp/api/packet.h>
#include <odp/api/timer.h>
#include <odp/api/pool.h>
+#include <odp/api/ml.h>
+
#include <odp_buffer_internal.h>
-#include <odp_buffer_inlines.h>
+#include <odp_ipsec_internal.h>
#include <odp_debug_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_event_internal.h>
+#include <odp_event_validation_internal.h>
+#include <odp_event_vector_internal.h>
-odp_event_type_t odp_event_type(odp_event_t event)
-{
- return _odp_buffer_event_type(odp_buffer_from_event(event));
-}
+/* Inlined API functions */
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_vector_inlines.h>
+#include <odp/api/plat/timer_inlines.h>
-void odp_event_free(odp_event_t event)
+#include <odp/api/plat/event_inline_types.h>
+
+#include <odp/visibility_begin.h>
+
+/* Fill in event header field offsets for inline functions */
+const _odp_event_inline_offset_t
+_odp_event_inline_offset ODP_ALIGNED_CACHE = {
+ .event_type = offsetof(_odp_event_hdr_t, event_type),
+ .base_data = offsetof(_odp_event_hdr_t, base_data),
+ .subtype = offsetof(_odp_event_hdr_t, subtype),
+ .flow_id = offsetof(_odp_event_hdr_t, flow_id),
+ .pool = offsetof(_odp_event_hdr_t, pool),
+};
+
+#include <odp/visibility_end.h>
+
+static inline void event_free(odp_event_t event, _odp_ev_id_t id)
{
switch (odp_event_type(event)) {
case ODP_EVENT_BUFFER:
+ _odp_buffer_validate(odp_buffer_from_event(event), id);
odp_buffer_free(odp_buffer_from_event(event));
break;
case ODP_EVENT_PACKET:
+ _odp_packet_validate(odp_packet_from_event(event), id);
odp_packet_free(odp_packet_from_event(event));
break;
+ case ODP_EVENT_PACKET_VECTOR:
+ _odp_packet_vector_free_full(odp_packet_vector_from_event(event));
+ break;
case ODP_EVENT_TIMEOUT:
odp_timeout_free(odp_timeout_from_event(event));
break;
- case ODP_EVENT_CRYPTO_COMPL:
- odp_crypto_compl_free(odp_crypto_compl_from_event(event));
+ case ODP_EVENT_IPSEC_STATUS:
+ _odp_ipsec_status_free(_odp_ipsec_status_from_event(event));
+ break;
+ case ODP_EVENT_PACKET_TX_COMPL:
+ odp_packet_tx_compl_free(odp_packet_tx_compl_from_event(event));
+ break;
+ case ODP_EVENT_DMA_COMPL:
+ odp_dma_compl_free(odp_dma_compl_from_event(event));
+ break;
+ case ODP_EVENT_ML_COMPL:
+ odp_ml_compl_free(odp_ml_compl_from_event(event));
break;
default:
- ODP_ABORT("Invalid event type: %d\n", odp_event_type(event));
+ _ODP_ABORT("Invalid event type: %d\n", odp_event_type(event));
}
}
+void odp_event_free(odp_event_t event)
+{
+ event_free(event, _ODP_EV_EVENT_FREE);
+}
+
+void odp_event_free_multi(const odp_event_t event[], int num)
+{
+ for (int i = 0; i < num; i++)
+ event_free(event[i], _ODP_EV_EVENT_FREE_MULTI);
+}
+
+void odp_event_free_sp(const odp_event_t event[], int num)
+{
+ for (int i = 0; i < num; i++)
+ event_free(event[i], _ODP_EV_EVENT_FREE_SP);
+}
+
uint64_t odp_event_to_u64(odp_event_t hdl)
{
return _odp_pri(hdl);
}
+
+int odp_event_is_valid(odp_event_t event)
+{
+ if (event == ODP_EVENT_INVALID)
+ return 0;
+
+ if (_odp_event_is_valid(event) == 0)
+ return 0;
+
+ switch (odp_event_type(event)) {
+ case ODP_EVENT_BUFFER:
+ return !_odp_buffer_validate(odp_buffer_from_event(event), _ODP_EV_EVENT_IS_VALID);
+ case ODP_EVENT_PACKET:
+ return !_odp_packet_validate(odp_packet_from_event(event), _ODP_EV_EVENT_IS_VALID);
+ case ODP_EVENT_TIMEOUT:
+ /* Fall through */
+ case ODP_EVENT_IPSEC_STATUS:
+ /* Fall through */
+ case ODP_EVENT_PACKET_VECTOR:
+ /* Fall through */
+ case ODP_EVENT_DMA_COMPL:
+ /* Fall through */
+ case ODP_EVENT_ML_COMPL:
+ /* Fall through */
+ case ODP_EVENT_PACKET_TX_COMPL:
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/platform/linux-generic/odp_event_api.c b/platform/linux-generic/odp_event_api.c
new file mode 100644
index 000000000..bd4b1a2d5
--- /dev/null
+++ b/platform/linux-generic/odp_event_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/event.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/event_inlines.h>
diff --git a/platform/linux-generic/odp_event_validation.c b/platform/linux-generic/odp_event_validation.c
new file mode 100644
index 000000000..f3cb98f7f
--- /dev/null
+++ b/platform/linux-generic/odp_event_validation.c
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/atomic.h>
+#include <odp/api/buffer.h>
+#include <odp/api/debug.h>
+#include <odp/api/event.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_buffer_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_event_validation_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_string_internal.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#define EVENT_VALIDATION_NONE 0
+#define EVENT_VALIDATION_WARN 1
+#define EVENT_VALIDATION_ABORT 2
+
+#define EVENT_DATA_PRINT_MAX_LEN 128
+
+typedef struct {
+ odp_atomic_u64_t err_count[_ODP_EV_MAX];
+ odp_shm_t shm;
+
+} event_validation_global_t;
+
+typedef struct {
+ const char *str;
+} _odp_ev_info_t;
+
+static event_validation_global_t *_odp_ev_glb;
+
+#if _ODP_EVENT_VALIDATION
+
+/* Table for mapping function IDs to API function names */
+static const _odp_ev_info_t ev_info_tbl[] = {
+ [_ODP_EV_BUFFER_FREE] = {.str = "odp_buffer_free()"},
+ [_ODP_EV_BUFFER_FREE_MULTI] = {.str = "odp_buffer_free_multi()"},
+ [_ODP_EV_BUFFER_IS_VALID] = {.str = "odp_buffer_is_valid()"},
+ [_ODP_EV_EVENT_FREE] = {.str = "odp_event_free()"},
+ [_ODP_EV_EVENT_FREE_MULTI] = {.str = "odp_event_free_multi()"},
+ [_ODP_EV_EVENT_FREE_SP] = {.str = "odp_event_free()_sp"},
+ [_ODP_EV_EVENT_IS_VALID] = {.str = "odp_event_is_valid()"},
+ [_ODP_EV_PACKET_FREE] = {.str = "odp_packet_free()"},
+ [_ODP_EV_PACKET_FREE_MULTI] = {.str = "odp_packet_free_multi()"},
+ [_ODP_EV_PACKET_FREE_SP] = {.str = "odp_packet_free_sp()"},
+ [_ODP_EV_PACKET_IS_VALID] = {.str = "odp_packet_is_valid()"},
+ [_ODP_EV_QUEUE_ENQ] = {.str = "odp_queue_enq()"},
+ [_ODP_EV_QUEUE_ENQ_MULTI] = {.str = "odp_queue_enq_multi()"}
+};
+
+ODP_STATIC_ASSERT(_ODP_ARRAY_SIZE(ev_info_tbl) == _ODP_EV_MAX, "ev_info_tbl missing entries");
+
+static void print_event_data(odp_event_t event, odp_event_type_t type)
+{
+ const char *type_str;
+ const uint32_t bytes_per_row = 16;
+ uint32_t byte_len;
+ int num_rows, max_len, n;
+ int len = 0;
+ uint8_t *data;
+
+ if (type == ODP_EVENT_PACKET) {
+ odp_packet_t pkt = odp_packet_from_event(event);
+
+ data = odp_packet_data(pkt);
+ byte_len = odp_packet_seg_len(pkt);
+ type_str = "Packet";
+ } else {
+ odp_buffer_t buf = odp_buffer_from_event(event);
+
+ data = odp_buffer_addr(buf);
+ byte_len = odp_buffer_size(buf);
+ type_str = "Buffer";
+ }
+
+ if (byte_len > EVENT_DATA_PRINT_MAX_LEN)
+ byte_len = EVENT_DATA_PRINT_MAX_LEN;
+
+ num_rows = (byte_len + bytes_per_row - 1) / bytes_per_row;
+ max_len = 256 + (3 * byte_len) + (3 * num_rows);
+ n = max_len - 1;
+
+ char str[max_len];
+
+ len += _odp_snprint(&str[len], n - len, "%s %p data %p:\n", type_str, event, data);
+ while (byte_len) {
+ uint32_t row_len = byte_len > bytes_per_row ? bytes_per_row : byte_len;
+
+ len += _odp_snprint(&str[len], n - len, " ");
+
+ for (uint32_t i = 0; i < row_len; i++)
+ len += _odp_snprint(&str[len], n - len, " %02x", data[i]);
+
+ len += _odp_snprint(&str[len], n - len, "\n");
+
+ byte_len -= row_len;
+ data += row_len;
+ }
+
+ _ODP_PRINT("%s\n", str);
+}
+
+static inline int validate_event_endmark(odp_event_t event, _odp_ev_id_t id, odp_event_type_t type)
+{
+ uint64_t err_count;
+ uint64_t *endmark_ptr = _odp_event_endmark_get_ptr(event);
+
+ if (odp_likely(*endmark_ptr == _ODP_EV_ENDMARK_VAL))
+ return 0;
+
+ err_count = odp_atomic_fetch_inc_u64(&_odp_ev_glb->err_count[id]) + 1;
+
+ _ODP_ERR("Event %p endmark mismatch in %s: endmark=0x%" PRIx64 " (expected 0x%" PRIx64 ") "
+ "err_count=%" PRIu64 "\n", event, ev_info_tbl[id].str, *endmark_ptr,
+ _ODP_EV_ENDMARK_VAL, err_count);
+
+ print_event_data(event, type);
+
+ if (_ODP_EVENT_VALIDATION == EVENT_VALIDATION_ABORT)
+ _ODP_ABORT("Abort due to event %p endmark mismatch\n", event);
+
+ /* Fix endmark value */
+ _odp_event_endmark_set(event);
+
+ return -1;
+}
+
+static inline int buffer_validate(odp_buffer_t buf, _odp_ev_id_t id)
+{
+ return validate_event_endmark(odp_buffer_to_event(buf), id, ODP_EVENT_BUFFER);
+}
+
+static inline int packet_validate(odp_packet_t pkt, _odp_ev_id_t id)
+{
+ return validate_event_endmark(odp_packet_to_event(pkt), id, ODP_EVENT_PACKET);
+}
+
+static inline int event_validate(odp_event_t event, int id)
+{
+ if (odp_event_type(event) == ODP_EVENT_BUFFER)
+ return buffer_validate(odp_buffer_from_event(event), id);
+ if (odp_event_type(event) == ODP_EVENT_PACKET)
+ return packet_validate(odp_packet_from_event(event), id);
+ return 0;
+}
+
+/* Enable usage from API inline files */
+#include <odp/visibility_begin.h>
+
+int _odp_buffer_validate(odp_buffer_t buf, _odp_ev_id_t id)
+{
+ return buffer_validate(buf, id);
+}
+
+int _odp_buffer_validate_multi(const odp_buffer_t buf[], int num,
+ _odp_ev_id_t id)
+{
+ for (int i = 0; i < num; i++) {
+ if (odp_unlikely(buffer_validate(buf[i], id)))
+ return -1;
+ }
+ return 0;
+}
+
+int _odp_packet_validate(odp_packet_t pkt, _odp_ev_id_t id)
+{
+ return packet_validate(pkt, id);
+}
+
+int _odp_packet_validate_multi(const odp_packet_t pkt[], int num,
+ _odp_ev_id_t id)
+{
+ for (int i = 0; i < num; i++) {
+ if (odp_unlikely(packet_validate(pkt[i], id)))
+ return -1;
+ }
+ return 0;
+}
+
+int _odp_event_validate(odp_event_t event, _odp_ev_id_t id)
+{
+ return event_validate(event, id);
+}
+
+int _odp_event_validate_multi(const odp_event_t event[], int num,
+ _odp_ev_id_t id)
+{
+ for (int i = 0; i < num; i++) {
+ if (odp_unlikely(event_validate(event[i], id)))
+ return -1;
+ }
+ return 0;
+}
+
+#include <odp/visibility_end.h>
+
+#endif /* _ODP_EVENT_VALIDATION */
+
+int _odp_event_validation_init_global(void)
+{
+ odp_shm_t shm;
+
+ _ODP_PRINT("\nEvent validation mode: %s\n\n",
+ _ODP_EVENT_VALIDATION == EVENT_VALIDATION_NONE ? "none" :
+ _ODP_EVENT_VALIDATION == EVENT_VALIDATION_WARN ? "warn" : "abort");
+
+ if (_ODP_EVENT_VALIDATION == EVENT_VALIDATION_NONE)
+ return 0;
+
+ shm = odp_shm_reserve("_odp_event_validation_global",
+ sizeof(event_validation_global_t),
+ ODP_CACHE_LINE_SIZE, ODP_SHM_EXPORT);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ _odp_ev_glb = odp_shm_addr(shm);
+ if (_odp_ev_glb == NULL)
+ return -1;
+
+ memset(_odp_ev_glb, 0, sizeof(event_validation_global_t));
+ _odp_ev_glb->shm = shm;
+
+ for (int i = 0; i < _ODP_EV_MAX; i++)
+ odp_atomic_init_u64(&_odp_ev_glb->err_count[i], 0);
+
+ return 0;
+}
+
+int _odp_event_validation_term_global(void)
+{
+ int ret;
+
+ if (_ODP_EVENT_VALIDATION == EVENT_VALIDATION_NONE)
+ return 0;
+
+ if (_odp_ev_glb == NULL)
+ return 0;
+
+ ret = odp_shm_free(_odp_ev_glb->shm);
+ if (ret) {
+ _ODP_ERR("SHM free failed: %d\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_fdserver.c b/platform/linux-generic/odp_fdserver.c
new file mode 100644
index 000000000..9d866f6a1
--- /dev/null
+++ b/platform/linux-generic/odp_fdserver.c
@@ -0,0 +1,719 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/*
+ * This file implements a file descriptor sharing server enabling
+ * sharing of file descriptors between processes, regardless of fork time.
+ *
+ * File descriptors are process scoped, but they can be "sent and converted
+ * on the fly" between processes using special unix domain socket ancillary
+ * data.
+ * The receiving process gets a file descriptor "pointing" to the same thing
+ * as the one sent (but the value of the file descriptor itself may be different
+ * from the one sent).
+ * Because ODP applications are responsible for creating ODP threads (i.e.
+ * pthreads or linux processes), ODP has no control on the order things happen:
+ * Nothing prevent a thread A to fork B and C, and then C creating a pktio
+ * which will be used by A and B to send/receive packets.
+ * Assuming this pktio uses a file descriptor, the latter will need to be
+ * shared between the processes, despite the "non convenient" fork time.
+ * The shared memory allocator is likely to use this as well to be able to
+ * share memory regardless of fork() time.
+ * This server handles a table of {(context,key)<-> fd} pair, and is
+ * interfaced by the following functions:
+ *
+ * _odp_fdserver_register_fd(context, key, fd_to_send);
+ * _odp_fdserver_deregister_fd(context, key);
+ * _odp_fdserver_lookup_fd(context, key);
+ *
+ * which are used to register/deregister or query for file descriptor based
+ * on a context and key value couple, which has to be unique.
+ *
+ * Note again that the file descriptors stored here are local to this server
+ * process and get converted both when registered or looked up.
+ */
+
+#include <odp_posix_extensions.h>
+#include <odp_config_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_fdserver_internal.h>
+#include <sys/prctl.h>
+#include <signal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#define FDSERVER_SOCKPATH_MAXLEN 255
+#define FDSERVER_SOCK_FORMAT "%s/%s/odp-%d-fdserver"
+#define FDSERVER_SOCKDIR_FORMAT "%s/%s"
+#define FDSERVER_DEFAULT_DIR "/dev/shm"
+#define FDSERVER_BACKLOG 5
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* Debug level for the FD server */
+#define FD_DBG 3
+
+/* define the tables of file descriptors handled by this server: */
+#define FDSERVER_MAX_ENTRIES (CONFIG_SHM_BLOCKS + CONFIG_INTERNAL_SHM_BLOCKS)
+typedef struct fdentry_s {
+ fd_server_context_e context;
+ uint64_t key;
+ int fd;
+} fdentry_t;
+static fdentry_t *fd_table;
+static int fd_table_nb_entries;
+
+/*
+ * define the message struct used for communication between client and server
+ * (this single message is used in both direction)
+ * The file descriptors are sent out of band as ancillary data for conversion.
+ */
+typedef struct fd_server_msg {
+ int command;
+ fd_server_context_e context;
+ uint64_t key;
+} fdserver_msg_t;
+/* possible commands are: */
+#define FD_REGISTER_REQ 1 /* client -> server */
+#define FD_REGISTER_ACK 2 /* server -> client */
+#define FD_REGISTER_NACK 3 /* server -> client */
+#define FD_LOOKUP_REQ 4 /* client -> server */
+#define FD_LOOKUP_ACK 5 /* server -> client */
+#define FD_LOOKUP_NACK 6 /* server -> client */
+#define FD_DEREGISTER_REQ 7 /* client -> server */
+#define FD_DEREGISTER_ACK 8 /* server -> client */
+#define FD_DEREGISTER_NACK 9 /* server -> client */
+#define FD_SERVERSTOP_REQ 10 /* client -> server (stops) */
+
+/*
+ * Client and server function:
+ * Send a fdserver_msg, possibly including a file descriptor, on the socket
+ * This function is used both by:
+ * -the client (sending a FD_REGISTER_REQ with a file descriptor to be shared,
+ * or FD_LOOKUP_REQ/FD_DEREGISTER_REQ without a file descriptor)
+ * -the server (sending FD_REGISTER_ACK/NACK, FD_LOOKUP_NACK,
+ * FD_DEREGISTER_ACK/NACK... without a fd or a
+ * FD_LOOKUP_ACK with a fd)
+ * This function make use of the ancillary data (control data) to pass and
+ * convert file descriptors over UNIX sockets
+ * Return -1 on error, 0 on success.
+ */
+static int send_fdserver_msg(int sock, int command,
+ fd_server_context_e context, uint64_t key,
+ int fd_to_send)
+{
+ struct msghdr socket_message;
+ struct iovec io_vector[1]; /* one msg frgmt only */
+ struct cmsghdr *control_message = NULL;
+ int *fd_location;
+ fdserver_msg_t msg;
+ int res;
+
+ char ancillary_data[CMSG_SPACE(sizeof(int))];
+
+ /* prepare the register request body (single fragment): */
+ msg.command = command;
+ msg.context = context;
+ msg.key = key;
+ io_vector[0].iov_base = &msg;
+ io_vector[0].iov_len = sizeof(fdserver_msg_t);
+
+ /* initialize socket message */
+ memset(&socket_message, 0, sizeof(struct msghdr));
+ socket_message.msg_iov = io_vector;
+ socket_message.msg_iovlen = 1;
+
+ if (fd_to_send >= 0) {
+ /* provide space for the ancillary data */
+ memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
+ socket_message.msg_control = ancillary_data;
+ socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+ /* initialize a single ancillary data element for fd passing */
+ control_message = CMSG_FIRSTHDR(&socket_message);
+ control_message->cmsg_level = SOL_SOCKET;
+ control_message->cmsg_type = SCM_RIGHTS;
+ control_message->cmsg_len = CMSG_LEN(sizeof(int));
+ fd_location = (int *)(void *)CMSG_DATA(control_message);
+ *fd_location = fd_to_send;
+ }
+ res = sendmsg(sock, &socket_message, 0);
+ if (res < 0) {
+ _ODP_ERR("sendmsg() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Client and server function
+ * Receive a fdserver_msg, possibly including a file descriptor, on the
+ * given socket.
+ * This function is used both by:
+ * -the server (receiving a FD_REGISTER_REQ with a file descriptor to be shared,
+ * or FD_LOOKUP_REQ, FD_DEREGISTER_REQ without a file descriptor)
+ * -the client (receiving FD_REGISTER_ACK...without a fd or a FD_LOOKUP_ACK with
+ * a fd)
+ * This function make use of the ancillary data (control data) to pass and
+ * convert file descriptors over UNIX sockets.
+ * Return -1 on error, 0 on success.
+ */
+static int recv_fdserver_msg(int sock, int *command,
+ fd_server_context_e *context, uint64_t *key,
+ int *recvd_fd)
+{
+ struct msghdr socket_message;
+ struct iovec io_vector[1]; /* one msg frgmt only */
+ struct cmsghdr *control_message = NULL;
+ int *fd_location;
+ fdserver_msg_t msg;
+ char ancillary_data[CMSG_SPACE(sizeof(int))];
+
+ memset(&socket_message, 0, sizeof(struct msghdr));
+ memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
+
+ /* setup a place to fill in message contents */
+ io_vector[0].iov_base = &msg;
+ io_vector[0].iov_len = sizeof(fdserver_msg_t);
+ socket_message.msg_iov = io_vector;
+ socket_message.msg_iovlen = 1;
+
+ /* provide space for the ancillary data */
+ socket_message.msg_control = ancillary_data;
+ socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+ /* receive the message */
+ if (recvmsg(sock, &socket_message, MSG_CMSG_CLOEXEC) < 0) {
+ _ODP_ERR("recvmsg() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ *command = msg.command;
+ *context = msg.context;
+ *key = msg.key;
+
+ /* grab the converted file descriptor (if any) */
+ *recvd_fd = -1;
+
+ if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
+ return 0;
+
+ /* iterate ancillary elements to find the file descriptor: */
+ for (control_message = CMSG_FIRSTHDR(&socket_message);
+ control_message != NULL;
+ control_message = CMSG_NXTHDR(&socket_message, control_message)) {
+ if ((control_message->cmsg_level == SOL_SOCKET) &&
+ (control_message->cmsg_type == SCM_RIGHTS)) {
+ fd_location = (int *)(void *)CMSG_DATA(control_message);
+ *recvd_fd = *fd_location;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* opens and returns a connected socket to the server */
+static int get_socket(void)
+{
+ char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+ int s_sock; /* server socket */
+ struct sockaddr_un remote;
+ int len;
+
+ /* construct the named socket path: */
+ len = snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
+ odp_global_ro.shm_dir, odp_global_ro.uid,
+ odp_global_ro.main_pid);
+
+ if (len >= FDSERVER_SOCKPATH_MAXLEN || len >= (int)sizeof(remote.sun_path)) {
+ _ODP_ERR("path too long\n");
+ return -1;
+ }
+
+ s_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s_sock == -1) {
+ _ODP_ERR("cannot connect to server: %s\n", strerror(errno));
+ return -1;
+ }
+
+ remote.sun_family = AF_UNIX;
+ strcpy(remote.sun_path, sockpath);
+ len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+ while (connect(s_sock, (struct sockaddr *)&remote, len) == -1) {
+ if (errno == EINTR)
+ continue;
+ _ODP_ERR("cannot connect to server: %s\n", strerror(errno));
+ close(s_sock);
+ return -1;
+ }
+
+ return s_sock;
+}
+
+/*
+ * Client function:
+ * Register a file descriptor to the server. Return -1 on error.
+ */
+int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
+ int fd_to_send)
+{
+ int s_sock; /* server socket */
+ int res;
+ int command;
+ int fd;
+
+ ODP_DBG_LVL(FD_DBG, "FD client register: pid=%d key=%" PRIu64 ", fd=%d\n",
+ getpid(), key, fd_to_send);
+
+ s_sock = get_socket();
+ if (s_sock < 0)
+ return -1;
+
+ res = send_fdserver_msg(s_sock, FD_REGISTER_REQ, context, key,
+ fd_to_send);
+ if (res < 0) {
+ _ODP_ERR("fd registration failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+ if ((res < 0) || (command != FD_REGISTER_ACK)) {
+ _ODP_ERR("fd registration failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ close(s_sock);
+
+ return 0;
+}
+
+/*
+ * Client function:
+ * Deregister a file descriptor from the server. Return -1 on error.
+ */
+int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key)
+{
+ int s_sock; /* server socket */
+ int res;
+ int command;
+ int fd;
+
+ ODP_DBG_LVL(FD_DBG, "FD client deregister: pid=%d key=%" PRIu64 "\n",
+ getpid(), key);
+
+ s_sock = get_socket();
+ if (s_sock < 0)
+ return -1;
+
+ res = send_fdserver_msg(s_sock, FD_DEREGISTER_REQ, context, key, -1);
+ if (res < 0) {
+ _ODP_ERR("fd de-registration failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+ if ((res < 0) || (command != FD_DEREGISTER_ACK)) {
+ _ODP_ERR("fd de-registration failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ close(s_sock);
+
+ return 0;
+}
+
+/*
+ * client function:
+ * lookup a file descriptor from the server. return -1 on error,
+ * or the file descriptor on success (>=0).
+ */
+int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key)
+{
+ int s_sock; /* server socket */
+ int res;
+ int command;
+ int fd;
+
+ s_sock = get_socket();
+ if (s_sock < 0)
+ return -1;
+
+ res = send_fdserver_msg(s_sock, FD_LOOKUP_REQ, context, key, -1);
+ if (res < 0) {
+ _ODP_ERR("fd lookup failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+ if ((res < 0) || (command != FD_LOOKUP_ACK)) {
+ _ODP_ERR("fd lookup failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ close(s_sock);
+ _ODP_DBG("FD client lookup: pid=%d, key=%" PRIu64 ", fd=%d\n",
+ getpid(), key, fd);
+
+ return fd;
+}
+
+/*
+ * request server termination:
+ */
+static int stop_server(void)
+{
+ int s_sock; /* server socket */
+ int res;
+
+ ODP_DBG_LVL(FD_DBG, "FD sending server stop request\n");
+
+ s_sock = get_socket();
+ if (s_sock < 0)
+ return -1;
+
+ res = send_fdserver_msg(s_sock, FD_SERVERSTOP_REQ, 0, 0, -1);
+ if (res < 0) {
+ _ODP_ERR("fd stop request failure\n");
+ close(s_sock);
+ return -1;
+ }
+
+ close(s_sock);
+
+ return 0;
+}
+
+/*
+ * server function
+ * receive a client request and handle it.
+ * Always returns 0 unless a stop request is received.
+ */
+static int handle_request(int client_sock)
+{
+ int command;
+ fd_server_context_e context;
+ uint64_t key;
+ int fd;
+ int i;
+
+ /* get a client request: */
+ recv_fdserver_msg(client_sock, &command, &context, &key, &fd);
+ switch (command) {
+ case FD_REGISTER_REQ:
+ if ((fd < 0) || (context >= FD_SRV_CTX_END)) {
+ _ODP_ERR("Invalid register fd or context\n");
+ send_fdserver_msg(client_sock, FD_REGISTER_NACK,
+ FD_SRV_CTX_NA, 0, -1);
+ return 0;
+ }
+
+ /* store the file descriptor in table: */
+ if (fd_table_nb_entries < FDSERVER_MAX_ENTRIES) {
+ fd_table[fd_table_nb_entries].context = context;
+ fd_table[fd_table_nb_entries].key = key;
+ fd_table[fd_table_nb_entries++].fd = fd;
+ ODP_DBG_LVL(FD_DBG, "storing {ctx=%d, key=%" PRIu64 "}->fd=%d\n",
+ context, key, fd);
+ } else {
+ _ODP_ERR("FD table full\n");
+ send_fdserver_msg(client_sock, FD_REGISTER_NACK,
+ FD_SRV_CTX_NA, 0, -1);
+ return 0;
+ }
+
+ send_fdserver_msg(client_sock, FD_REGISTER_ACK,
+ FD_SRV_CTX_NA, 0, -1);
+ break;
+
+ case FD_LOOKUP_REQ:
+ if (context >= FD_SRV_CTX_END) {
+ _ODP_ERR("invalid lookup context\n");
+ send_fdserver_msg(client_sock, FD_LOOKUP_NACK,
+ FD_SRV_CTX_NA, 0, -1);
+ return 0;
+ }
+
+ /* search key in table and sent reply: */
+ for (i = 0; i < fd_table_nb_entries; i++) {
+ if ((fd_table[i].context == context) &&
+ (fd_table[i].key == key)) {
+ fd = fd_table[i].fd;
+ _ODP_DBG("lookup {ctx=%d,"
+ " key=%" PRIu64 "}->fd=%d\n",
+ context, key, fd);
+ send_fdserver_msg(client_sock,
+ FD_LOOKUP_ACK, context, key,
+ fd);
+ return 0;
+ }
+ }
+
+ /* context+key not found... send nack */
+ send_fdserver_msg(client_sock, FD_LOOKUP_NACK, context, key,
+ -1);
+ break;
+
+ case FD_DEREGISTER_REQ:
+ if (context >= FD_SRV_CTX_END) {
+ _ODP_ERR("invalid deregister context\n");
+ send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
+ FD_SRV_CTX_NA, 0, -1);
+ return 0;
+ }
+
+ /* search key in table and remove it if found, and reply: */
+ for (i = 0; i < fd_table_nb_entries; i++) {
+ if ((fd_table[i].context == context) &&
+ (fd_table[i].key == key)) {
+ ODP_DBG_LVL(FD_DBG, "drop {ctx=%d,"
+ " key=%" PRIu64 "}->fd=%d\n",
+ context, key, fd_table[i].fd);
+ close(fd_table[i].fd);
+ fd_table[i] = fd_table[--fd_table_nb_entries];
+ send_fdserver_msg(client_sock,
+ FD_DEREGISTER_ACK,
+ context, key, -1);
+ return 0;
+ }
+ }
+
+ /* key not found... send nack */
+ send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
+ context, key, -1);
+ break;
+
+ case FD_SERVERSTOP_REQ:
+ ODP_DBG_LVL(FD_DBG, "Stopping FD server\n");
+ return 1;
+
+ default:
+ _ODP_ERR("Unexpected request\n");
+ break;
+ }
+ return 0;
+}
+
+/*
+ * server function
+ * loop forever, handling client requests one by one
+ */
+static void wait_requests(int sock)
+{
+ int c_socket; /* client connection */
+ unsigned int addr_sz;
+ struct sockaddr_un remote;
+
+ for (;;) {
+ addr_sz = sizeof(remote);
+ c_socket = accept(sock, (struct sockaddr *)&remote, &addr_sz);
+ if (c_socket == -1) {
+ if (errno == EINTR)
+ continue;
+
+ _ODP_ERR("accept() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (handle_request(c_socket))
+ break;
+ close(c_socket);
+ }
+ close(c_socket);
+}
+
+/*
+ * Create a unix domain socket and fork a process to listen to incoming
+ * requests.
+ */
+int _odp_fdserver_init_global(void)
+{
+ char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+ int sock;
+ struct sockaddr_un local;
+ pid_t server_pid;
+ int len, res;
+
+ snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKDIR_FORMAT,
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid);
+
+ mkdir(sockpath, 0744);
+
+ /* construct the server named socket path: */
+ len = snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
+ odp_global_ro.shm_dir, odp_global_ro.uid,
+ odp_global_ro.main_pid);
+
+ if (len >= FDSERVER_SOCKPATH_MAXLEN || len >= (int)sizeof(local.sun_path)) {
+ _ODP_ERR("path too long\n");
+ return -1;
+ }
+
+ /* create UNIX domain socket: */
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ _ODP_ERR("socket() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* remove previous named socket if it already exists: */
+ unlink(sockpath);
+
+ /* bind to new named socket: */
+ local.sun_family = AF_UNIX;
+ memcpy(local.sun_path, sockpath, sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+ res = bind(sock, (struct sockaddr *)&local, sizeof(struct sockaddr_un));
+ if (res == -1) {
+ _ODP_ERR("bind() failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /* listen for incoming connections: */
+ if (listen(sock, FDSERVER_BACKLOG) == -1) {
+ _ODP_ERR("listen() failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /* fork a server process: */
+ server_pid = fork();
+ if (server_pid == -1) {
+ _ODP_ERR("Could not fork!\n");
+ close(sock);
+ return -1;
+ }
+
+ if (server_pid == 0) { /*child */
+ sigset_t sigset;
+ struct sigaction action;
+
+ sigfillset(&sigset);
+ /* undefined if these are ignored, as per POSIX */
+ sigdelset(&sigset, SIGFPE);
+ sigdelset(&sigset, SIGILL);
+ sigdelset(&sigset, SIGSEGV);
+ /* can not be masked */
+ sigdelset(&sigset, SIGKILL);
+ sigdelset(&sigset, SIGSTOP);
+ /* these we want to handle */
+ sigdelset(&sigset, SIGTERM);
+ if (sigprocmask(SIG_SETMASK, &sigset, NULL) == -1) {
+ _ODP_ERR("Could not set signal mask");
+ exit(1);
+ }
+
+ /* set default handlers for those signals we can handle */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ sigaction(SIGFPE, &action, NULL);
+ sigaction(SIGILL, &action, NULL);
+ sigaction(SIGSEGV, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+
+ /* TODO: pin the server on appropriate service cpu mask */
+ /* when (if) we can agree on the usage of service mask */
+
+ /* request to be killed if parent dies, hence avoiding */
+ /* orphans being "adopted" by the init process... */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ res = setsid();
+ if (res == -1) {
+ _ODP_ERR("Could not setsid()");
+ exit(1);
+ }
+
+ /* allocate the space for the file descriptor<->key table: */
+ fd_table = malloc(FDSERVER_MAX_ENTRIES * sizeof(fdentry_t));
+ if (!fd_table) {
+ _ODP_ERR("maloc failed!\n");
+ exit(1);
+ }
+
+ /* wait for clients requests */
+ wait_requests(sock); /* Returns when server is stopped */
+ close(sock);
+
+ /* release the file descriptor table: */
+ free(fd_table);
+
+ exit(0);
+ }
+
+ /* parent */
+ odp_global_ro.fdserver_pid = server_pid;
+ close(sock);
+ return 0;
+}
+
+/*
+ * Terminate the server
+ */
+int _odp_fdserver_term_global(void)
+{
+ int status;
+ pid_t pid;
+ char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+
+ /* close fdserver and wait for it to terminate */
+ if (stop_server()) {
+ _ODP_ERR("Server stop failed\n");
+ return -1;
+ }
+
+ _ODP_DBG("Waiting for fdserver (%i) to stop\n", odp_global_ro.fdserver_pid);
+ pid = waitpid(odp_global_ro.fdserver_pid, &status, 0);
+
+ if (pid != odp_global_ro.fdserver_pid)
+ _ODP_ERR("Failed to wait for fdserver\n");
+
+ /* construct the server named socket path: */
+ snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCK_FORMAT,
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid,
+ odp_global_ro.main_pid);
+
+ /* delete the UNIX domain socket: */
+ unlink(sockpath);
+
+ /* delete shm files directory */
+ snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKDIR_FORMAT,
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid);
+ rmdir(sockpath);
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_hash.c b/platform/linux-generic/odp_hash.c
deleted file mode 100644
index 55876c338..000000000
--- a/platform/linux-generic/odp_hash.c
+++ /dev/null
@@ -1,489 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <odp/api/hash.h>
-#include <odp/api/std_types.h>
-
-#include <stddef.h>
-
-static const uint32_t crc32c_tables[8][256] = {{
- 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C,
- 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
- 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C,
- 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
- 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC,
- 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
- 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512,
- 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
- 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD,
- 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
- 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF,
- 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
- 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F,
- 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
- 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F,
- 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
- 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E,
- 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
- 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E,
- 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
- 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE,
- 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
- 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4,
- 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
- 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B,
- 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
- 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5,
- 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
- 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975,
- 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
- 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905,
- 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
- 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8,
- 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
- 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8,
- 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
- 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78,
- 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
- 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6,
- 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
- 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69,
- 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
- 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
-},
-{
- 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB,
- 0x69CF5132, 0x7A6DC945, 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21,
- 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, 0x3FC5F181, 0x2C6769F6,
- 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,
- 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92,
- 0xCB1E630B, 0xD8BCFB7C, 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B,
- 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, 0xE29F20BA, 0xF13DB8CD,
- 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF,
- 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28,
- 0x298143B1, 0x3A23DBC6, 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2,
- 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, 0xFF17C604, 0xECB55E73,
- 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41,
- 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17,
- 0x0BCC548E, 0x186ECCF9, 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C,
- 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, 0x5DC6F43D, 0x4E646C4A,
- 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,
- 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD,
- 0xE9537434, 0xFAF1EC43, 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27,
- 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, 0xBF59D487, 0xACFB4CF0,
- 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2,
- 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94,
- 0x4B82460D, 0x5820DE7A, 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260,
- 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, 0x66D73941, 0x7575A136,
- 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004,
- 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3,
- 0xADC95A4A, 0xBE6BC23D, 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059,
- 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, 0x844819FB, 0x97EA818C,
- 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,
- 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8,
- 0x70938B71, 0x63311306, 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3,
- 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, 0x26992BC2, 0x353BB3B5,
- 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287,
- 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556,
- 0x6D1B6DCF, 0x7EB9F5B8, 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC,
- 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, 0x3B11CD7C, 0x28B3550B,
- 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439,
- 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F,
- 0xCFCA5FF6, 0xDC68C781, 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766,
- 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, 0xE64B1C47, 0xF5E98430,
- 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,
- 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5,
- 0x2D557F4C, 0x3EF7E73B, 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F,
- 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483,
-},
-{
- 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664,
- 0xD1B1F617, 0x74F06469, 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6,
- 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, 0x70A27D8A, 0xD5E3EFF4,
- 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,
- 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B,
- 0x9942B558, 0x3C032726, 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67,
- 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, 0xD915C5D1, 0x7C5457AF,
- 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8,
- 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA,
- 0x40577089, 0xE516E2F7, 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828,
- 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, 0xC76580D9, 0x622412A7,
- 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0,
- 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878,
- 0x2E85480B, 0x8BC4DA75, 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20,
- 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, 0x8F96C396, 0x2AD751E8,
- 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,
- 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9,
- 0xF7908DDA, 0x52D11FA4, 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B,
- 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, 0x56830647, 0xF3C29439,
- 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E,
- 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6,
- 0xBF63CE95, 0x1A225CEB, 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730,
- 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, 0xB3764986, 0x1637DBF8,
- 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF,
- 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD,
- 0x2A34FCDE, 0x8F756EA0, 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F,
- 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, 0x6A638C57, 0xCF221E29,
- 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,
- 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6,
- 0x83834485, 0x26C2D6FB, 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE,
- 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, 0x2290CF18, 0x87D15D66,
- 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71,
- 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE,
- 0x9DF3018D, 0x38B293F3, 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C,
- 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, 0x3CE08A10, 0x99A1186E,
- 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79,
- 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1,
- 0xD50042C2, 0x7041D0BC, 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD,
- 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, 0x9557324B, 0x3016A035,
- 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,
- 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760,
- 0x0C158713, 0xA954156D, 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2,
- 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8,
-},
-{
- 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B,
- 0xC4451272, 0x1900B8CA, 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF,
- 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, 0xE964B13D, 0x34211B85,
- 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,
- 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990,
- 0xDB65C0A9, 0x06206A11, 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2,
- 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, 0x2161776D, 0xFC24DDD5,
- 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7,
- 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD,
- 0xFA04B7C4, 0x27411D7C, 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69,
- 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, 0xABA65FE7, 0x76E3F55F,
- 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D,
- 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A,
- 0x99A72E73, 0x44E284CB, 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3,
- 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, 0xB4868D3C, 0x69C32784,
- 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,
- 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027,
- 0xB8C6591E, 0x6583F3A6, 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3,
- 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, 0x95E7FA51, 0x48A250E9,
- 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B,
- 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC,
- 0xA7E68BC5, 0x7AA3217D, 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006,
- 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, 0xA4E4AAD9, 0x79A10061,
- 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213,
- 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349,
- 0x7F816A70, 0xA2C4C0C8, 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD,
- 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, 0x8585DDB4, 0x58C0770C,
- 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,
- 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519,
- 0xB784AC20, 0x6AC10698, 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0,
- 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, 0x9AA50F6F, 0x47E0A5D7,
- 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5,
- 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93,
- 0x3D4384AA, 0xE0062E12, 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07,
- 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, 0x106227E5, 0xCD278D5D,
- 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F,
- 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48,
- 0x22635671, 0xFF26FCC9, 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A,
- 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, 0xD867E1B5, 0x05224B0D,
- 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,
- 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825,
- 0x0302211C, 0xDE478BA4, 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1,
- 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842,
-},
-{
- 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C,
- 0x906761E8, 0xA8760E44, 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65,
- 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, 0x8F2261D3, 0xB7330E7F,
- 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,
- 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E,
- 0xDA220BAA, 0xE2336406, 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3,
- 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, 0xDECFBEC6, 0xE6DED16A,
- 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082,
- 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598,
- 0x04EDB56C, 0x3CFCDAC0, 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1,
- 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, 0x37516AAE, 0x0F400502,
- 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA,
- 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023,
- 0x625100D7, 0x5A406F7B, 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89,
- 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, 0x7D1400EC, 0x45056F40,
- 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,
- 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5,
- 0xBC9EBE11, 0x848FD1BD, 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C,
- 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, 0xA3DBBE2A, 0x9BCAD186,
- 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E,
- 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7,
- 0xF6DBD453, 0xCECABBFF, 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8,
- 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, 0xABC5DECD, 0x93D4B161,
- 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089,
- 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593,
- 0x71E7D567, 0x49F6BACB, 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA,
- 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, 0x750A600B, 0x4D1B0FA7,
- 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,
- 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86,
- 0x200A0A72, 0x181B65DE, 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C,
- 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, 0x3F4F0A49, 0x075E65E5,
- 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D,
- 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE,
- 0xC994DE1A, 0xF185B1B6, 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497,
- 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, 0xD6D1DE21, 0xEEC0B18D,
- 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065,
- 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC,
- 0x83D1B458, 0xBBC0DBF4, 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51,
- 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, 0x873C0134, 0xBF2D6E98,
- 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,
- 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A,
- 0x5D1E0A9E, 0x650F6532, 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013,
- 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3,
-},
-{
- 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E,
- 0x697997B4, 0x8649FCAD, 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5,
- 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, 0xC00C303E, 0x2F3C5B27,
- 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,
- 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F,
- 0xC973BF95, 0x2643D48C, 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57,
- 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, 0xE5F20E92, 0x0AC2658B,
- 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F,
- 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD,
- 0x2C81B107, 0xC3B1DA1E, 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576,
- 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, 0x0E045BEB, 0xE13430F2,
- 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746,
- 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A,
- 0x077BD440, 0xE84BBF59, 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F,
- 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, 0xAE0E73CA, 0x413E18D3,
- 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,
- 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108,
- 0xE289DAD2, 0x0DB9B1CB, 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3,
- 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, 0x4BFC7D58, 0xA4CC1641,
- 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5,
- 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929,
- 0x4283F2F3, 0xADB399EA, 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C,
- 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, 0x7C0EAFC9, 0x933EC4D0,
- 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364,
- 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86,
- 0xB57D105C, 0x5A4D7B45, 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D,
- 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, 0x99FCA15B, 0x76CCCA42,
- 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,
- 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A,
- 0x90832EF0, 0x7FB345E9, 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF,
- 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, 0x39F6897A, 0xD6C6E263,
- 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7,
- 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053,
- 0x7B757B89, 0x94451090, 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8,
- 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, 0xD200DC03, 0x3D30B71A,
- 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE,
- 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872,
- 0xDB7F53A8, 0x344F38B1, 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A,
- 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, 0xF7FEE2AF, 0x18CE89B6,
- 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,
- 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0,
- 0x3E8D5D3A, 0xD1BD3623, 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B,
- 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C,
-},
-{
- 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919,
- 0x75E69C41, 0x1DE5B089, 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B,
- 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, 0x9C5BFAA6, 0xF458D66E,
- 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,
- 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC,
- 0xA7909BB4, 0xCF93B77C, 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5,
- 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, 0x73767EEE, 0x1B755226,
- 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67,
- 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002,
- 0xD4E6E55A, 0xBCE5C992, 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110,
- 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, 0x7AB7077A, 0x12B42BB2,
- 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3,
- 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330,
- 0x417C6668, 0x297F4AA0, 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884,
- 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, 0xA8C1008F, 0xC0C22C47,
- 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,
- 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE,
- 0x320A1886, 0x5A09344E, 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC,
- 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, 0xDBB77E61, 0xB3B452A9,
- 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8,
- 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B,
- 0xE07C1F73, 0x887F33BB, 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC,
- 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, 0xBB43F3A7, 0xD340DF6F,
- 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E,
- 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B,
- 0x1CD36813, 0x74D044DB, 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59,
- 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, 0xC8358D49, 0xA036A181,
- 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,
- 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903,
- 0xF3FEEC5B, 0x9BFDC093, 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7,
- 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, 0x1A438ABC, 0x7240A674,
- 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35,
- 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097,
- 0xFA3F95CF, 0x923CB907, 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185,
- 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, 0x1382F328, 0x7B81DFE0,
- 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1,
- 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762,
- 0x2849923A, 0x404ABEF2, 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B,
- 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, 0xFCAF7760, 0x94AC5BA8,
- 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,
- 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C,
- 0x5B3FECD4, 0x333CC01C, 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E,
- 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F,
-},
-{
- 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A,
- 0xB3657823, 0xFA590504, 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3,
- 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, 0x847609B4, 0xCD4A7493,
- 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,
- 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224,
- 0x7528754D, 0x3C14086A, 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0,
- 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, 0x4F3B6143, 0x06071C64,
- 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447,
- 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367,
- 0x3A13140E, 0x732F6929, 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E,
- 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, 0x1A00CB32, 0x533CB615,
- 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36,
- 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2,
- 0xEB5EB7CB, 0xA262CAEC, 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF,
- 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, 0xDC4DC65C, 0x9571BB7B,
- 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,
- 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1,
- 0xA465D688, 0xED59ABAF, 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18,
- 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, 0x9376A71F, 0xDA4ADA38,
- 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B,
- 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F,
- 0x6228DBE6, 0x2B14A6C1, 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D,
- 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, 0x763A92BE, 0x3F06EF99,
- 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA,
- 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A,
- 0x0312E7F3, 0x4A2E9AD4, 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63,
- 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, 0x3901F3FD, 0x703D8EDA,
- 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,
- 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D,
- 0xC85F8F04, 0x8163F223, 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20,
- 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, 0xFF4CFE93, 0xB67083B4,
- 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97,
- 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C,
- 0x9D642575, 0xD4585852, 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5,
- 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, 0xAA7754E2, 0xE34B29C5,
- 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6,
- 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72,
- 0x5B29281B, 0x1215553C, 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6,
- 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, 0x613A3C15, 0x28064132,
- 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,
- 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31,
- 0x14124958, 0x5D2E347F, 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8,
- 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5,
-} };
-
-#define CRC32_UPD(crc, n) \
- (crc32c_tables[(n)][(crc) & 0xff] ^ \
- crc32c_tables[(n) - 1][((crc) >> 8) & 0xff])
-
-static inline uint32_t crc32c_u32(uint32_t data, uint32_t init_val)
-{
- uint32_t crc, term1, term2;
-
- crc = init_val;
- crc ^= data;
-
- term1 = CRC32_UPD(crc, 3);
- term2 = crc >> 16;
- crc = term1 ^ CRC32_UPD(term2, 1);
-
- return crc;
-}
-
-static inline uint32_t crc32c_u64(uint64_t data, uint32_t init_val)
-{
- union {
- uint64_t u64;
- uint32_t u32[2];
- } d;
- d.u64 = data;
-
- uint32_t crc, term1, term2;
-
- crc = init_val;
- crc ^= d.u32[0];
-
- term1 = CRC32_UPD(crc, 7);
- term2 = crc >> 16;
- crc = term1 ^ CRC32_UPD(term2, 5);
- term1 = CRC32_UPD(d.u32[1], 3);
- term2 = d.u32[1] >> 16;
- crc ^= term1 ^ CRC32_UPD(term2, 1);
-
- return crc;
-}
-
-uint32_t odp_hash_crc32c(const void *data, uint32_t data_len,
- uint32_t init_val)
-{
- size_t i;
- uint64_t temp = 0;
- uintptr_t pd = (uintptr_t)data;
-
- for (i = 0; i < data_len / 8; i++) {
- init_val = crc32c_u64(*(const uint64_t *)pd, init_val);
- pd += 8;
- }
-
- switch (7 - (data_len & 0x07)) {
- case 0:
- temp |= (uint64_t)*((const uint8_t *)pd + 6) << 48;
- /* Fallthrough */
- case 1:
- temp |= (uint64_t)*((const uint8_t *)pd + 5) << 40;
- /* Fallthrough */
- case 2:
- temp |= (uint64_t)*((const uint8_t *)pd + 4) << 32;
- temp |= *(const uint32_t *)pd;
- init_val = crc32c_u64(temp, init_val);
- break;
- case 3:
- init_val = crc32c_u32(*(const uint32_t *)pd, init_val);
- break;
- case 4:
- temp |= *((const uint8_t *)pd + 2) << 16;
- /* Fallthrough */
- case 5:
- temp |= *((const uint8_t *)pd + 1) << 8;
- /* Fallthrough */
- case 6:
- temp |= *(const uint8_t *)pd;
- init_val = crc32c_u32(temp, init_val);
- /* Fallthrough */
- default:
- break;
- }
-
- return init_val;
-}
diff --git a/platform/linux-generic/odp_hash_api.c b/platform/linux-generic/odp_hash_api.c
new file mode 100644
index 000000000..620ea0dfc
--- /dev/null
+++ b/platform/linux-generic/odp_hash_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/hash.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/hash_inlines.h>
diff --git a/platform/linux-generic/odp_hash_crc_gen.c b/platform/linux-generic/odp_hash_crc_gen.c
new file mode 100644
index 000000000..4090d3863
--- /dev/null
+++ b/platform/linux-generic/odp_hash_crc_gen.c
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <odp/api/hash.h>
+#include <odp/api/hints.h>
+#include <odp/api/rwlock.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_debug_internal.h>
+#include <odp_init_internal.h>
+
+typedef struct crc_table_t {
+ uint32_t crc[256];
+ uint32_t width;
+ uint32_t poly;
+ int reflect;
+ odp_rwlock_t rwlock;
+ odp_shm_t shm;
+
+} crc_table_t;
+
+static crc_table_t *crc_table;
+
+int _odp_hash_init_global(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("_odp_hash_crc_gen", sizeof(crc_table_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ crc_table = odp_shm_addr(shm);
+
+ if (crc_table == NULL) {
+ _ODP_ERR("Shm reserve failed for odp_hash_crc_gen\n");
+ return -1;
+ }
+
+ memset(crc_table, 0, sizeof(crc_table_t));
+
+ crc_table->shm = shm;
+ odp_rwlock_init(&crc_table->rwlock);
+
+ return 0;
+}
+
+int _odp_hash_term_global(void)
+{
+ if (odp_shm_free(crc_table->shm)) {
+ _ODP_ERR("Shm free failed for odp_hash_crc_gen\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Reflect bits in a byte */
+static inline uint8_t reflect_u8(uint8_t byte)
+{
+ uint8_t u8[8];
+
+ u8[0] = (byte & (0x1u << 7)) >> 7;
+ u8[1] = (byte & (0x1u << 6)) >> 5;
+ u8[2] = (byte & (0x1u << 5)) >> 3;
+ u8[3] = (byte & (0x1u << 4)) >> 1;
+
+ u8[4] = (byte & (0x1u << 3)) << 1;
+ u8[5] = (byte & (0x1u << 2)) << 3;
+ u8[6] = (byte & (0x1u << 1)) << 5;
+ u8[7] = (byte & 0x1u) << 7;
+
+ return u8[0] | u8[1] | u8[2] | u8[3] | u8[4] | u8[5] | u8[6] | u8[7];
+}
+
+/* Reflect 32 bits */
+static inline uint32_t reflect_u32(uint32_t u32)
+{
+ uint32_t r[4];
+
+ r[0] = reflect_u8((u32 & 0xff000000u) >> 24);
+ r[1] = reflect_u8((u32 & 0x00ff0000u) >> 16);
+ r[2] = reflect_u8((u32 & 0x0000ff00u) >> 8);
+ r[3] = reflect_u8(u32 & 0xffu);
+
+ return (r[3] << 24) | (r[2] << 16) | (r[1] << 8) | r[0];
+}
+
+/* Reflect 24 bits */
+static inline uint32_t reflect_u24(uint32_t u32)
+{
+ uint32_t r[4];
+
+ r[0] = reflect_u8((u32 & 0xff0000u) >> 16);
+ r[1] = reflect_u8((u32 & 0x00ff00u) >> 8);
+ r[2] = reflect_u8(u32 & 0xffu);
+
+ return (r[2] << 16) | (r[1] << 8) | r[0];
+}
+
+/* Reflect 16 bits */
+static inline uint32_t reflect_u16(uint32_t u32)
+{
+ uint32_t r[4];
+
+ r[0] = reflect_u8((u32 & 0xff00u) >> 8);
+ r[1] = reflect_u8(u32 & 0xffu);
+
+ return (r[1] << 8) | r[0];
+}
+
+/* Generate table for a 32/24/16 bit CRCs.
+ *
+ * Based on an example in RFC 1952.
+ */
+static inline void crc_table_gen(uint32_t poly, int reflect, int width)
+{
+ uint32_t i, crc, bit, shift, msb, mask;
+
+ crc_table->width = width;
+ crc_table->poly = poly;
+ crc_table->reflect = reflect;
+
+ shift = width - 8;
+ mask = 0xffffffffu >> (32 - width);
+ msb = 0x1u << (width - 1);
+
+ if (reflect) {
+ if (width == 32)
+ poly = reflect_u32(poly);
+ else if (width == 24)
+ poly = reflect_u24(poly);
+ else
+ poly = reflect_u16(poly);
+ }
+
+ for (i = 0; i < 256; i++) {
+ if (reflect) {
+ crc = i;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (crc & 0x1u)
+ crc = poly ^ (crc >> 1);
+ else
+ crc = crc >> 1;
+ }
+ } else {
+ crc = i << shift;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (crc & msb)
+ crc = poly ^ (crc << 1);
+ else
+ crc = crc << 1;
+ }
+ }
+
+ crc_table->crc[i] = crc & mask;
+ }
+}
+
+static inline uint32_t crc_calc(const uint8_t *data, uint32_t data_len,
+ uint32_t init_val, int reflect, int width)
+{
+ uint32_t i, crc, shift;
+ uint8_t byte;
+ uint32_t mask;
+
+ shift = width - 8;
+ mask = 0xffffffffu >> (32 - width);
+
+ crc = init_val;
+
+ for (i = 0; i < data_len; i++) {
+ byte = data[i];
+
+ if (reflect) {
+ crc = crc_table->crc[(crc ^ byte) & 0xffu] ^ (crc >> 8);
+ } else {
+ crc = crc_table->crc[(crc >> shift) ^ byte] ^
+ (crc << 8);
+ crc = crc & mask;
+ }
+ }
+
+ return crc;
+}
+
+int odp_hash_crc_gen64(const void *data_ptr, uint32_t data_len,
+ uint64_t init_val, odp_hash_crc_param_t *crc_param,
+ uint64_t *crc_out)
+{
+ uint32_t crc;
+ int update_table;
+ uint32_t poly = crc_param->poly;
+ uint32_t width = crc_param->width;
+ int reflect = crc_param->reflect_in;
+
+ if (odp_unlikely(crc_param->reflect_in != crc_param->reflect_out)) {
+ _ODP_ERR("Odd reflection setting not supported.\n");
+ return -1;
+ }
+
+ if (odp_unlikely(width != 32 && width != 24 && width != 16)) {
+ _ODP_ERR("CRC width %" PRIu32 " bits not supported.\n", width);
+ return -1;
+ }
+
+ /* TODO: fix implementation of 24 bit CRC with reflection */
+ if (odp_unlikely(width == 24 && reflect)) {
+ _ODP_ERR("24 bit CRC with reflection not supported.\n");
+ return -1;
+ }
+
+ odp_rwlock_read_lock(&crc_table->rwlock);
+
+ update_table = (crc_table->width != width) ||
+ (crc_table->poly != poly) ||
+ (crc_table->reflect != reflect);
+
+ /* Generate CRC table if not yet generated. */
+ if (odp_unlikely(update_table)) {
+ odp_rwlock_read_unlock(&crc_table->rwlock);
+ odp_rwlock_write_lock(&crc_table->rwlock);
+
+ crc_table_gen(poly, reflect, width);
+ }
+
+ crc = crc_calc(data_ptr, data_len, init_val, reflect, width);
+
+ if (odp_unlikely(update_table))
+ odp_rwlock_write_unlock(&crc_table->rwlock);
+ else
+ odp_rwlock_read_unlock(&crc_table->rwlock);
+
+ if (crc_param->xor_out)
+ crc = crc ^ (uint32_t)crc_param->xor_out;
+
+ *crc_out = crc;
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_impl.c b/platform/linux-generic/odp_impl.c
index 3fc2d6ab3..0b192a82f 100644
--- a/platform/linux-generic/odp_impl.c
+++ b/platform/linux-generic/odp_impl.c
@@ -1,28 +1,13 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
-
-/**
- * @file
- *
- * ODP Implementation information
- */
-
-#ifndef ODP_IMPL_H_
-#define ODP_IMPL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+#include <odp/autoheader_internal.h>
#include <odp/api/version.h>
#define ODP_VERSION_IMPL 0
#define ODP_VERSION_IMPL_STR \
- ODP_VERSION_TO_STR(IMPLEMENTATION_NAME) " " \
+ _ODP_IMPLEMENTATION_NAME " " \
ODP_VERSION_TO_STR(ODP_VERSION_API_GENERATION) "." \
ODP_VERSION_TO_STR(ODP_VERSION_API_MAJOR) "." \
ODP_VERSION_TO_STR(ODP_VERSION_API_MINOR) "-" \
@@ -30,10 +15,7 @@ extern "C" {
ODP_VERSION_TO_STR(ODP_VERSION_API_GENERATION) "." \
ODP_VERSION_TO_STR(ODP_VERSION_API_MAJOR) "." \
ODP_VERSION_TO_STR(ODP_VERSION_API_MINOR) ") " \
- ODP_VERSION_TO_STR(GIT_HASH)
-
-#define ODP_VERSION_IMPL_NAME \
- ODP_VERSION_TO_STR(IMPLEMENTATION_NAME)
+ ODP_VERSION_TO_STR(ODP_VERSION_BUILD)
const char *odp_version_impl_str(void)
{
@@ -42,11 +24,5 @@ const char *odp_version_impl_str(void)
const char *odp_version_impl_name(void)
{
- return ODP_VERSION_IMPL_NAME;
-}
-
-#ifdef __cplusplus
+ return _ODP_IMPLEMENTATION_NAME;
}
-#endif
-
-#endif
diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
index 7d0299508..6c9c12860 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -1,265 +1,615 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
+
+#include <odp_posix_extensions.h>
+
#include <odp/api/init.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp/api/plat/thread_inlines.h>
+
#include <odp_debug_internal.h>
-#include <odp/api/debug.h>
-#include <unistd.h>
-#include <odp_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
#include <odp_schedule_if.h>
+#include <odp_libconfig_internal.h>
+
#include <string.h>
#include <stdio.h>
-#include <linux/limits.h>
-#include <dirent.h>
#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <pwd.h>
-struct odp_global_data_s odp_global_data;
+enum init_stage {
+ NO_INIT = 0, /* No init stages completed */
+ LIBCONFIG_INIT,
+ CPUMASK_INIT,
+ SYSINFO_INIT,
+ CPU_CYCLES_INIT,
+ TIME_INIT,
+ ISHM_INIT,
+ FDSERVER_INIT,
+ GLOBAL_RW_DATA_INIT,
+ HASH_INIT,
+ THREAD_INIT,
+ POOL_INIT,
+ EVENT_VALIDATION_INIT,
+ STASH_INIT,
+ QUEUE_INIT,
+ SCHED_INIT,
+ PKTIO_INIT,
+ TIMER_INIT,
+ RANDOM_INIT,
+ CRYPTO_INIT,
+ COMP_INIT,
+ CLASSIFICATION_INIT,
+ TRAFFIC_MNGR_INIT,
+ NAME_TABLE_INIT,
+ IPSEC_EVENTS_INIT,
+ IPSEC_SAD_INIT,
+ IPSEC_INIT,
+ DMA_INIT,
+ ML_INIT,
+ ALL_INIT /* All init stages completed */
+};
+
+odp_global_data_ro_t odp_global_ro;
+odp_global_data_rw_t *odp_global_rw;
+
+/* Global function pointers for inline header usage. The values are written
+ * during odp_init_global() (enables process mode support). */
+#include <odp/visibility_begin.h>
+
+odp_log_func_t ODP_PRINTF_FORMAT(2, 3) _odp_log_fn;
+odp_abort_func_t _odp_abort_fn;
+
+#include <odp/visibility_end.h>
+
+/* odp_init_local() call status */
+static __thread uint8_t init_local_called;
+
+static void disable_features(odp_global_data_ro_t *global_ro,
+ const odp_init_t *init_param)
+{
+ int disable_ipsec, disable_crypto;
+ int disable_dma;
+
+ if (init_param == NULL)
+ return;
+
+ disable_ipsec = init_param->not_used.feat.ipsec;
+ global_ro->disable.ipsec = disable_ipsec;
+
+ disable_crypto = init_param->not_used.feat.crypto;
+ /* Crypto can be disabled only if IPSec is disabled */
+ if (disable_ipsec && disable_crypto)
+ global_ro->disable.crypto = 1;
+
+ disable_dma = init_param->not_used.feat.dma;
+ global_ro->disable.dma = disable_dma;
+
+ /* DMA uses stash. Disable stash only when both are disabled. */
+ if (disable_dma && init_param->not_used.feat.stash)
+ global_ro->disable.stash = 1;
+
+ global_ro->disable.traffic_mngr = init_param->not_used.feat.tm;
+ global_ro->disable.compress = init_param->not_used.feat.compress;
+ global_ro->disable.ml = init_param->not_used.feat.ml;
+}
+
+void odp_init_param_init(odp_init_t *param)
+{
+ memset(param, 0, sizeof(odp_init_t));
+}
+
+static int global_rw_data_init(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("_odp_global_rw_data",
+ sizeof(odp_global_data_rw_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ odp_global_rw = odp_shm_addr(shm);
+ if (odp_global_rw == NULL) {
+ _ODP_ERR("Global RW data shm reserve failed.\n");
+ return -1;
+ }
+
+ memset(odp_global_rw, 0, sizeof(odp_global_data_rw_t));
+
+ return 0;
+}
+
+static int global_rw_data_term(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup("_odp_global_rw_data");
+ if (shm == ODP_SHM_INVALID) {
+ _ODP_ERR("Unable to find global RW data shm.\n");
+ return -1;
+ }
+
+ if (odp_shm_free(shm)) {
+ _ODP_ERR("Global RW data shm free failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int term_global(enum init_stage stage)
+{
+ int rc = 0;
+
+ switch (stage) {
+ case ALL_INIT:
+ case ML_INIT:
+ if (_odp_ml_term_global()) {
+ _ODP_ERR("ODP ML term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case DMA_INIT:
+ if (_odp_dma_term_global()) {
+ _ODP_ERR("ODP DMA term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case IPSEC_INIT:
+ if (_odp_ipsec_term_global()) {
+ _ODP_ERR("ODP IPsec term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case IPSEC_SAD_INIT:
+ if (_odp_ipsec_sad_term_global()) {
+ _ODP_ERR("ODP IPsec SAD term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case IPSEC_EVENTS_INIT:
+ if (_odp_ipsec_events_term_global()) {
+ _ODP_ERR("ODP IPsec events term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case NAME_TABLE_INIT:
+ if (_odp_int_name_tbl_term_global()) {
+ _ODP_ERR("Name table term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case TRAFFIC_MNGR_INIT:
+ if (_odp_tm_term_global()) {
+ _ODP_ERR("TM term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case CLASSIFICATION_INIT:
+ if (_odp_classification_term_global()) {
+ _ODP_ERR("ODP classification term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case COMP_INIT:
+ if (_odp_comp_term_global()) {
+ _ODP_ERR("ODP comp term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case CRYPTO_INIT:
+ if (_odp_crypto_term_global()) {
+ _ODP_ERR("ODP crypto term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case TIMER_INIT:
+ if (_odp_timer_term_global()) {
+ _ODP_ERR("ODP timer term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case PKTIO_INIT:
+ if (_odp_pktio_term_global()) {
+ _ODP_ERR("ODP pktio term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case SCHED_INIT:
+ if (_odp_schedule_term_global()) {
+ _ODP_ERR("ODP schedule term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case QUEUE_INIT:
+ if (_odp_queue_term_global()) {
+ _ODP_ERR("ODP queue term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case STASH_INIT:
+ if (_odp_stash_term_global()) {
+ _ODP_ERR("ODP stash term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case EVENT_VALIDATION_INIT:
+ if (_odp_event_validation_term_global()) {
+ _ODP_ERR("ODP event validation term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case POOL_INIT:
+ if (_odp_pool_term_global()) {
+ _ODP_ERR("ODP buffer pool term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case THREAD_INIT:
+ if (_odp_thread_term_global()) {
+ _ODP_ERR("ODP thread term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case HASH_INIT:
+ if (_odp_hash_term_global()) {
+ _ODP_ERR("ODP hash term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case GLOBAL_RW_DATA_INIT:
+ if (global_rw_data_term()) {
+ _ODP_ERR("ODP global RW data term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case FDSERVER_INIT:
+ if (_odp_fdserver_term_global()) {
+ _ODP_ERR("ODP fdserver term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case ISHM_INIT:
+ if (_odp_ishm_term_global()) {
+ _ODP_ERR("ODP ishm term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case TIME_INIT:
+ if (_odp_time_term_global()) {
+ _ODP_ERR("ODP time term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case CPU_CYCLES_INIT:
+ case SYSINFO_INIT:
+ if (_odp_system_info_term()) {
+ _ODP_ERR("ODP system info term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case CPUMASK_INIT:
+ if (_odp_cpumask_term_global()) {
+ _ODP_ERR("ODP cpumask term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ case LIBCONFIG_INIT:
+ if (_odp_libconfig_term_global()) {
+ _ODP_ERR("ODP runtime config term failed.\n");
+ rc = -1;
+ }
+ /* Fall through */
+
+ default:
+ break;
+ }
+
+ return rc;
+}
int odp_init_global(odp_instance_t *instance,
const odp_init_t *params,
const odp_platform_init_t *platform_params ODP_UNUSED)
{
- memset(&odp_global_data, 0, sizeof(struct odp_global_data_s));
- odp_global_data.main_pid = getpid();
-
enum init_stage stage = NO_INIT;
- odp_global_data.log_fn = odp_override_log;
- odp_global_data.abort_fn = odp_override_abort;
+ memset(&odp_global_ro, 0, sizeof(odp_global_data_ro_t));
+ odp_global_ro.main_pid = getpid();
+ _odp_log_fn = odp_override_log;
+ _odp_abort_fn = odp_override_abort;
+
+ odp_init_param_init(&odp_global_ro.init_param);
if (params != NULL) {
+ odp_global_ro.init_param = *params;
+
if (params->log_fn != NULL)
- odp_global_data.log_fn = params->log_fn;
+ _odp_log_fn = params->log_fn;
if (params->abort_fn != NULL)
- odp_global_data.abort_fn = params->abort_fn;
+ _odp_abort_fn = params->abort_fn;
+ if (params->mem_model == ODP_MEM_MODEL_PROCESS)
+ odp_global_ro.shm_single_va = 1;
}
- if (odp_cpumask_init_global(params)) {
- ODP_ERR("ODP cpumask init failed.\n");
+ if (_odp_libconfig_init_global()) {
+ _ODP_ERR("ODP runtime config init failed.\n");
goto init_failed;
}
- stage = CPUMASK_INIT;
+ stage = LIBCONFIG_INIT;
+
+ disable_features(&odp_global_ro, params);
- if (odp_time_init_global()) {
- ODP_ERR("ODP time init failed.\n");
+ if (_odp_cpumask_init_global(params)) {
+ _ODP_ERR("ODP cpumask init failed.\n");
goto init_failed;
}
- stage = TIME_INIT;
+ stage = CPUMASK_INIT;
- if (odp_system_info_init()) {
- ODP_ERR("ODP system_info init failed.\n");
+ if (_odp_system_info_init()) {
+ _ODP_ERR("ODP system_info init failed.\n");
goto init_failed;
}
stage = SYSINFO_INIT;
- if (_odp_ishm_init_global()) {
- ODP_ERR("ODP ishm init failed.\n");
+ if (_odp_cpu_cycles_init_global()) {
+ _ODP_ERR("ODP cpu cycle init failed.\n");
+ goto init_failed;
+ }
+ stage = CPU_CYCLES_INIT;
+
+ if (_odp_time_init_global()) {
+ _ODP_ERR("ODP time init failed.\n");
+ goto init_failed;
+ }
+ stage = TIME_INIT;
+
+ if (_odp_ishm_init_global(params)) {
+ _ODP_ERR("ODP ishm init failed.\n");
goto init_failed;
}
stage = ISHM_INIT;
if (_odp_fdserver_init_global()) {
- ODP_ERR("ODP fdserver init failed.\n");
+ _ODP_ERR("ODP fdserver init failed.\n");
goto init_failed;
}
stage = FDSERVER_INIT;
- if (odp_thread_init_global()) {
- ODP_ERR("ODP thread init failed.\n");
+ if (global_rw_data_init()) {
+ _ODP_ERR("ODP global RW data init failed.\n");
+ goto init_failed;
+ }
+ stage = GLOBAL_RW_DATA_INIT;
+
+ if (_odp_hash_init_global()) {
+ _ODP_ERR("ODP hash init failed.\n");
+ goto init_failed;
+ }
+ stage = HASH_INIT;
+
+ if (_odp_thread_init_global()) {
+ _ODP_ERR("ODP thread init failed.\n");
goto init_failed;
}
stage = THREAD_INIT;
- if (odp_pool_init_global()) {
- ODP_ERR("ODP pool init failed.\n");
+ if (_odp_pool_init_global()) {
+ _ODP_ERR("ODP pool init failed.\n");
goto init_failed;
}
stage = POOL_INIT;
- if (queue_fn->init_global()) {
- ODP_ERR("ODP queue init failed.\n");
+ if (_odp_event_validation_init_global()) {
+ _ODP_ERR("ODP event validation init failed.\n");
+ goto init_failed;
+ }
+ stage = EVENT_VALIDATION_INIT;
+
+ if (_odp_stash_init_global()) {
+ _ODP_ERR("ODP stash init failed.\n");
+ goto init_failed;
+ }
+ stage = STASH_INIT;
+
+ if (_odp_queue_init_global()) {
+ _ODP_ERR("ODP queue init failed.\n");
goto init_failed;
}
stage = QUEUE_INIT;
- if (sched_fn->init_global()) {
- ODP_ERR("ODP schedule init failed.\n");
+ if (_odp_schedule_init_global()) {
+ _ODP_ERR("ODP schedule init failed.\n");
goto init_failed;
}
stage = SCHED_INIT;
- if (odp_pktio_init_global()) {
- ODP_ERR("ODP packet io init failed.\n");
+ if (_odp_pktio_init_global()) {
+ _ODP_ERR("ODP packet io init failed.\n");
goto init_failed;
}
stage = PKTIO_INIT;
- if (odp_timer_init_global()) {
- ODP_ERR("ODP timer init failed.\n");
+ if (_odp_timer_init_global(params)) {
+ _ODP_ERR("ODP timer init failed.\n");
goto init_failed;
}
stage = TIMER_INIT;
- if (odp_crypto_init_global()) {
- ODP_ERR("ODP crypto init failed.\n");
+ if (_odp_crypto_init_global()) {
+ _ODP_ERR("ODP crypto init failed.\n");
goto init_failed;
}
stage = CRYPTO_INIT;
- if (odp_classification_init_global()) {
- ODP_ERR("ODP classification init failed.\n");
+ if (_odp_comp_init_global()) {
+ _ODP_ERR("ODP comp init failed.\n");
+ goto init_failed;
+ }
+ stage = COMP_INIT;
+
+ if (_odp_classification_init_global()) {
+ _ODP_ERR("ODP classification init failed.\n");
goto init_failed;
}
stage = CLASSIFICATION_INIT;
- if (odp_tm_init_global()) {
- ODP_ERR("ODP traffic manager init failed\n");
+ if (_odp_tm_init_global()) {
+ _ODP_ERR("ODP traffic manager init failed\n");
goto init_failed;
}
stage = TRAFFIC_MNGR_INIT;
if (_odp_int_name_tbl_init_global()) {
- ODP_ERR("ODP name table init failed\n");
+ _ODP_ERR("ODP name table init failed\n");
goto init_failed;
}
+ stage = NAME_TABLE_INIT;
- *instance = (odp_instance_t)odp_global_data.main_pid;
+ if (_odp_ipsec_events_init_global()) {
+ _ODP_ERR("ODP IPsec events init failed.\n");
+ goto init_failed;
+ }
+ stage = IPSEC_EVENTS_INIT;
+
+ if (_odp_ipsec_sad_init_global()) {
+ _ODP_ERR("ODP IPsec SAD init failed.\n");
+ goto init_failed;
+ }
+ stage = IPSEC_SAD_INIT;
+
+ if (_odp_ipsec_init_global()) {
+ _ODP_ERR("ODP IPsec init failed.\n");
+ goto init_failed;
+ }
+ stage = IPSEC_INIT;
+
+ if (_odp_dma_init_global()) {
+ _ODP_ERR("ODP DMA init failed.\n");
+ goto init_failed;
+ }
+ stage = DMA_INIT;
+
+ if (_odp_ml_init_global()) {
+ _ODP_ERR("ODP ML init failed.\n");
+ goto init_failed;
+ }
+ stage = ML_INIT;
+
+ *instance = (odp_instance_t)odp_global_ro.main_pid;
return 0;
init_failed:
- _odp_term_global(stage);
+ term_global(stage);
return -1;
}
int odp_term_global(odp_instance_t instance)
{
- if (instance != (odp_instance_t)odp_global_data.main_pid) {
- ODP_ERR("Bad instance.\n");
+ if (instance != (odp_instance_t)odp_global_ro.main_pid) {
+ _ODP_ERR("Bad instance.\n");
return -1;
}
- return _odp_term_global(ALL_INIT);
+ return term_global(ALL_INIT);
}
-int _odp_term_global(enum init_stage stage)
+static int term_local(enum init_stage stage)
{
int rc = 0;
+ int rc_thd = 0;
switch (stage) {
case ALL_INIT:
- case NAME_TABLE_INIT:
- if (_odp_int_name_tbl_term_global()) {
- ODP_ERR("Name table term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case TRAFFIC_MNGR_INIT:
- if (odp_tm_term_global()) {
- ODP_ERR("TM term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case CLASSIFICATION_INIT:
- if (odp_classification_term_global()) {
- ODP_ERR("ODP classification term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case CRYPTO_INIT:
- if (odp_crypto_term_global()) {
- ODP_ERR("ODP crypto term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case TIMER_INIT:
- if (odp_timer_term_global()) {
- ODP_ERR("ODP timer term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case PKTIO_INIT:
- if (odp_pktio_term_global()) {
- ODP_ERR("ODP pktio term failed.\n");
- rc = -1;
- }
- /* Fall through */
case SCHED_INIT:
- if (sched_fn->term_global()) {
- ODP_ERR("ODP schedule term failed.\n");
+ if (_odp_sched_fn->term_local()) {
+ _ODP_ERR("ODP schedule local term failed.\n");
rc = -1;
}
/* Fall through */
case QUEUE_INIT:
- if (queue_fn->term_global()) {
- ODP_ERR("ODP queue term failed.\n");
+ if (_odp_queue_fn->term_local()) {
+ _ODP_ERR("ODP queue local term failed.\n");
rc = -1;
}
/* Fall through */
case POOL_INIT:
- if (odp_pool_term_global()) {
- ODP_ERR("ODP buffer pool term failed.\n");
+ if (_odp_pool_term_local()) {
+ _ODP_ERR("ODP buffer pool local term failed.\n");
rc = -1;
}
/* Fall through */
- case THREAD_INIT:
- if (odp_thread_term_global()) {
- ODP_ERR("ODP thread term failed.\n");
- rc = -1;
- }
- /* Fall through */
-
- case FDSERVER_INIT:
- if (_odp_fdserver_term_global()) {
- ODP_ERR("ODP fdserver term failed.\n");
+ case CRYPTO_INIT:
+ if (_odp_crypto_term_local()) {
+ _ODP_ERR("ODP crypto local term failed.\n");
rc = -1;
}
/* Fall through */
- case ISHM_INIT:
- if (_odp_ishm_term_global()) {
- ODP_ERR("ODP ishm term failed.\n");
+ case RANDOM_INIT:
+ if (_odp_random_term_local()) {
+ _ODP_ERR("ODP random local term failed.\n");
rc = -1;
}
/* Fall through */
- case SYSINFO_INIT:
- if (odp_system_info_term()) {
- ODP_ERR("ODP system info term failed.\n");
+ case TIMER_INIT:
+ if (_odp_timer_term_local()) {
+ _ODP_ERR("ODP timer local term failed.\n");
rc = -1;
}
/* Fall through */
- case TIME_INIT:
- if (odp_time_term_global()) {
- ODP_ERR("ODP time term failed.\n");
+ case THREAD_INIT:
+ rc_thd = _odp_thread_term_local();
+ if (rc_thd < 0) {
+ _ODP_ERR("ODP thread local term failed.\n");
rc = -1;
+ } else {
+ if (!rc)
+ rc = (rc_thd == 0) ? 0 : 1;
}
/* Fall through */
- case CPUMASK_INIT:
- if (odp_cpumask_term_global()) {
- ODP_ERR("ODP cpumask term failed.\n");
+ case ISHM_INIT:
+ if (_odp_ishm_term_local()) {
+ _ODP_ERR("ODP ishm local term failed.\n");
rc = -1;
}
/* Fall through */
- case NO_INIT:
- ;
+ default:
+ break;
}
return rc;
@@ -269,43 +619,68 @@ int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
{
enum init_stage stage = NO_INIT;
- if (instance != (odp_instance_t)odp_global_data.main_pid) {
- ODP_ERR("Bad instance.\n");
+ if (instance != (odp_instance_t)odp_global_ro.main_pid) {
+ _ODP_ERR("Bad instance.\n");
goto init_fail;
}
+ /* Detect if odp_init_local() has been already called from this thread */
+ if (getpid() == odp_global_ro.main_pid && init_local_called) {
+ _ODP_ERR("%s() called multiple times by the same thread\n", __func__);
+ goto init_fail;
+ }
+ init_local_called = 1;
+
if (_odp_ishm_init_local()) {
- ODP_ERR("ODP ishm local init failed.\n");
+ _ODP_ERR("ODP ishm local init failed.\n");
goto init_fail;
}
stage = ISHM_INIT;
- if (odp_thread_init_local(thr_type)) {
- ODP_ERR("ODP thread local init failed.\n");
+ if (_odp_thread_init_local(thr_type)) {
+ _ODP_ERR("ODP thread local init failed.\n");
goto init_fail;
}
stage = THREAD_INIT;
- if (odp_pktio_init_local()) {
- ODP_ERR("ODP packet io local init failed.\n");
+ if (_odp_pktio_init_local()) {
+ _ODP_ERR("ODP packet io local init failed.\n");
goto init_fail;
}
stage = PKTIO_INIT;
- if (odp_pool_init_local()) {
- ODP_ERR("ODP pool local init failed.\n");
+ if (_odp_timer_init_local()) {
+ _ODP_ERR("ODP timer local init failed.\n");
+ goto init_fail;
+ }
+ stage = TIMER_INIT;
+
+ if (_odp_random_init_local()) {
+ _ODP_ERR("ODP random local init failed.\n");
+ goto init_fail;
+ }
+ stage = RANDOM_INIT;
+
+ if (_odp_crypto_init_local()) {
+ _ODP_ERR("ODP crypto local init failed.\n");
+ goto init_fail;
+ }
+ stage = CRYPTO_INIT;
+
+ if (_odp_pool_init_local()) {
+ _ODP_ERR("ODP pool local init failed.\n");
goto init_fail;
}
stage = POOL_INIT;
- if (queue_fn->init_local()) {
- ODP_ERR("ODP queue local init failed.\n");
+ if (_odp_queue_fn->init_local()) {
+ _ODP_ERR("ODP queue local init failed.\n");
goto init_fail;
}
stage = QUEUE_INIT;
- if (sched_fn->init_local()) {
- ODP_ERR("ODP schedule local init failed.\n");
+ if (_odp_sched_fn->init_local()) {
+ _ODP_ERR("ODP schedule local init failed.\n");
goto init_fail;
}
/* stage = SCHED_INIT; */
@@ -313,65 +688,50 @@ int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
return 0;
init_fail:
- _odp_term_local(stage);
+ term_local(stage);
return -1;
}
int odp_term_local(void)
{
- return _odp_term_local(ALL_INIT);
+ /* Check that odp_init_local() has been called by this thread */
+ if (!init_local_called) {
+ _ODP_ERR("%s() called by a non-initialized thread\n", __func__);
+ return -1;
+ }
+ init_local_called = 0;
+
+ return term_local(ALL_INIT);
}
-int _odp_term_local(enum init_stage stage)
+int odp_term_abnormal(odp_instance_t instance, uint64_t flags, void *data ODP_UNUSED)
{
- int rc = 0;
- int rc_thd = 0;
-
- switch (stage) {
- case ALL_INIT:
-
- case SCHED_INIT:
- if (sched_fn->term_local()) {
- ODP_ERR("ODP schedule local term failed.\n");
- rc = -1;
- }
- /* Fall through */
+ if (flags & ODP_TERM_FROM_SIGH)
+ /* Called from signal handler, not safe to terminate with local/global,
+ * return with failure as not able to perform all actions */
+ return -1;
- case QUEUE_INIT:
- if (queue_fn->term_local()) {
- ODP_ERR("ODP queue local term failed.\n");
- rc = -1;
- }
- /* Fall through */
+ if (odp_term_local() < 0) {
+ _ODP_ERR("ODP local terminate failed.\n");
+ return -2;
+ }
- case POOL_INIT:
- if (odp_pool_term_local()) {
- ODP_ERR("ODP buffer pool local term failed.\n");
- rc = -1;
- }
- /* Fall through */
+ if (odp_term_global(instance) < 0) {
+ _ODP_ERR("ODP global terminate failed.\n");
+ return -3;
+ }
- case THREAD_INIT:
- rc_thd = odp_thread_term_local();
- if (rc_thd < 0) {
- ODP_ERR("ODP thread local term failed.\n");
- rc = -1;
- } else {
- if (!rc)
- rc = rc_thd;
- }
- /* Fall through */
+ return 0;
+}
- case ISHM_INIT:
- if (_odp_ishm_term_local()) {
- ODP_ERR("ODP ishm local term failed.\n");
- rc = -1;
- }
- /* Fall through */
+void odp_log_thread_fn_set(odp_log_func_t func)
+{
+ _odp_this_thread->log_fn = func;
+}
- default:
- break;
- }
+int odp_instance(odp_instance_t *instance)
+{
+ *instance = (odp_instance_t)odp_global_ro.main_pid;
- return rc;
+ return 0;
}
diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c
new file mode 100644
index 000000000..033d7706d
--- /dev/null
+++ b/platform/linux-generic/odp_ipsec.c
@@ -0,0 +1,2723 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2018-2022 Nokia
+ */
+
+#include <odp/api/byteorder.h>
+#include <odp/api/ipsec.h>
+#include <odp/api/chksum.h>
+
+#include <odp/api/plat/byteorder_inlines.h>
+#include <odp/api/plat/ipsec_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_classification_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_schedule_if.h>
+
+#include <protocols/eth.h>
+#include <protocols/ip.h>
+#include <protocols/ipsec.h>
+#include <protocols/udp.h>
+
+#include <errno.h>
+#include <string.h>
+
+typedef enum {
+ IPSEC_ORDERING_NONE = 0,
+ IPSEC_ORDERING_SIMPLE,
+} ordering_mode_t;
+
+typedef struct {
+ ordering_mode_t inbound_ordering_mode;
+ ordering_mode_t outbound_ordering_mode;
+ odp_ipsec_config_t ipsec_config;
+} ipsec_global_t;
+
+static ipsec_global_t *ipsec_global;
+
+static odp_ipsec_config_t *ipsec_config;
+
+/*
+ * Wait until the ordered scheduling context of this thread corresponds
+ * to the head of its input queue. Do nothing if ordering is not requested
+ * or if not holding an ordered context.
+ */
+static void wait_for_order(ordering_mode_t mode)
+{
+ if (mode == IPSEC_ORDERING_NONE)
+ return;
+ _odp_sched_fn->order_lock();
+ /*
+ * We rely on the unlock being no-op, so let's not even bother
+ * calling it. Unlock cannot really be anything but a no-op since
+ * the scheduler cannot let other threads to continue until at
+ * scheduling context release time.
+ *
+ * _odp_sched_fn->order_unlock();
+ */
+}
+
+/*
+ * Set cabability bits for algorithms that are defined for use with IPsec
+ * and for which the IPsec crypto or auth capability function returns
+ * at least one supported instance.
+ */
+static int set_ipsec_crypto_capa(odp_ipsec_capability_t *capa)
+{
+ odp_crypto_capability_t crypto_capa;
+
+ crypto_capa.ciphers.all_bits = 0;
+ crypto_capa.auths.all_bits = 0;
+
+ if (odp_crypto_capability(&crypto_capa))
+ return -1;
+
+#define CHECK_CIPHER(field, alg) do { \
+ if (crypto_capa.ciphers.bit.field && \
+ odp_ipsec_cipher_capability(alg, NULL, 0) > 0) \
+ capa->ciphers.bit.field = 1; \
+} while (0)
+
+ CHECK_CIPHER(null, ODP_CIPHER_ALG_NULL);
+ CHECK_CIPHER(des, ODP_CIPHER_ALG_DES);
+ CHECK_CIPHER(trides_cbc, ODP_CIPHER_ALG_3DES_CBC);
+ CHECK_CIPHER(aes_cbc, ODP_CIPHER_ALG_AES_CBC);
+ CHECK_CIPHER(aes_ctr, ODP_CIPHER_ALG_AES_CTR);
+ CHECK_CIPHER(aes_gcm, ODP_CIPHER_ALG_AES_GCM);
+ CHECK_CIPHER(aes_ccm, ODP_CIPHER_ALG_AES_CCM);
+ CHECK_CIPHER(chacha20_poly1305, ODP_CIPHER_ALG_CHACHA20_POLY1305);
+
+#define CHECK_AUTH(field, alg) do { \
+ if (crypto_capa.auths.bit.field && \
+ odp_ipsec_auth_capability(alg, NULL, 0) > 0) \
+ capa->auths.bit.field = 1; \
+} while (0)
+
+ CHECK_AUTH(null, ODP_AUTH_ALG_NULL);
+ CHECK_AUTH(md5_hmac, ODP_AUTH_ALG_MD5_HMAC);
+ CHECK_AUTH(sha1_hmac, ODP_AUTH_ALG_SHA1_HMAC);
+ CHECK_AUTH(sha256_hmac, ODP_AUTH_ALG_SHA256_HMAC);
+ CHECK_AUTH(sha384_hmac, ODP_AUTH_ALG_SHA384_HMAC);
+ CHECK_AUTH(sha512_hmac, ODP_AUTH_ALG_SHA512_HMAC);
+ CHECK_AUTH(aes_gcm, ODP_AUTH_ALG_AES_GCM);
+ CHECK_AUTH(aes_gmac, ODP_AUTH_ALG_AES_GMAC);
+ CHECK_AUTH(aes_ccm, ODP_AUTH_ALG_AES_CCM);
+ CHECK_AUTH(aes_cmac, ODP_AUTH_ALG_AES_CMAC);
+ CHECK_AUTH(aes_xcbc_mac, ODP_AUTH_ALG_AES_XCBC_MAC);
+ CHECK_AUTH(chacha20_poly1305, ODP_AUTH_ALG_CHACHA20_POLY1305);
+
+ /*
+ * Certain combined mode algorithms are configured by setting
+ * both cipher and auth to the corresponding algorithm when
+ * creating an SA. Since such algorithms cannot be combined
+ * with anything else, clear both capability fields if the
+ * cipher and auth check did not both succeed.
+ *
+ * Although AES-GMAC is a combined mode algorithm, it does
+ * not appear here because it is configured by setting cipher
+ * to null.
+ */
+#define REQUIRE_BOTH(field) do { \
+ if (!capa->ciphers.bit.field) \
+ capa->auths.bit.field = 0; \
+ if (!capa->auths.bit.field) \
+ capa->ciphers.bit.field = 0; \
+ } while (0)
+
+ REQUIRE_BOTH(aes_gcm);
+ REQUIRE_BOTH(aes_ccm);
+ REQUIRE_BOTH(chacha20_poly1305);
+
+ return 0;
+}
+
+int odp_ipsec_capability(odp_ipsec_capability_t *capa)
+{
+ int rc;
+ odp_queue_capability_t queue_capa;
+
+ if (odp_global_ro.disable.ipsec) {
+ _ODP_ERR("IPSec is disabled\n");
+ return -1;
+ }
+
+ memset(capa, 0, sizeof(odp_ipsec_capability_t));
+
+ capa->op_mode_sync = ODP_SUPPORT_PREFERRED;
+ capa->op_mode_async = ODP_SUPPORT_PREFERRED;
+ capa->op_mode_inline_in = ODP_SUPPORT_PREFERRED;
+ capa->op_mode_inline_out = ODP_SUPPORT_PREFERRED;
+
+ capa->proto_ah = ODP_SUPPORT_YES;
+
+ capa->max_num_sa = _odp_ipsec_max_num_sa();
+
+ capa->max_antireplay_ws = IPSEC_AR_WIN_SIZE_MAX;
+
+ rc = set_ipsec_crypto_capa(capa);
+ if (rc < 0)
+ return rc;
+
+ capa->queue_type_plain = true;
+ capa->queue_type_sched = true;
+
+ rc = odp_queue_capability(&queue_capa);
+ if (rc < 0)
+ return rc;
+
+ capa->max_queues = queue_capa.max_queues;
+ capa->inline_ipsec_tm = ODP_SUPPORT_NO;
+
+ capa->test.sa_operations.seq_num = 1;
+
+ capa->reassembly.ip = false;
+ capa->reassembly.ipv4 = false;
+ capa->reassembly.ipv6 = false;
+ capa->reass_async = false;
+ capa->reass_inline = false;
+
+ return 0;
+}
+
+static int cipher_requires_randomness(odp_cipher_alg_t cipher)
+{
+ int ret;
+
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ case ODP_CIPHER_ALG_AES_CTR:
+ case ODP_CIPHER_ALG_AES_GCM:
+ case ODP_CIPHER_ALG_AES_CCM:
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ ret = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+int odp_ipsec_cipher_capability(odp_cipher_alg_t cipher,
+ odp_ipsec_cipher_capability_t capa[], int num)
+{
+ uint32_t req_iv_len;
+ int rc, i, out, max_capa;
+
+ if (odp_random_max_kind() < ODP_RANDOM_CRYPTO &&
+ cipher_requires_randomness(cipher))
+ return 0;
+
+ max_capa = odp_crypto_cipher_capability(cipher, NULL, 0);
+ if (max_capa <= 0)
+ return max_capa;
+
+ odp_crypto_cipher_capability_t crypto_capa[max_capa];
+
+ rc = odp_crypto_cipher_capability(cipher, crypto_capa, max_capa);
+ if (rc <= 0)
+ return rc;
+
+ req_iv_len = _odp_ipsec_cipher_iv_len(cipher);
+ for (i = 0, out = 0; i < rc; i++) {
+ if (crypto_capa[i].iv_len != req_iv_len)
+ continue;
+
+ if (out < num)
+ capa[out].key_len = crypto_capa[i].key_len;
+ out++;
+ }
+
+ return out;
+}
+
+int odp_ipsec_auth_capability(odp_auth_alg_t auth,
+ odp_ipsec_auth_capability_t capa[], int num)
+{
+ uint32_t req_digest_len;
+ int rc, i, out, max_capa;
+
+ max_capa = odp_crypto_auth_capability(auth, NULL, 0);
+ if (max_capa <= 0)
+ return max_capa;
+
+ odp_crypto_auth_capability_t crypto_capa[max_capa];
+
+ rc = odp_crypto_auth_capability(auth, crypto_capa, max_capa);
+ if (rc <= 0)
+ return rc;
+
+ req_digest_len = _odp_ipsec_auth_digest_len(auth);
+ for (i = 0, out = 0; i < rc; i++) {
+ if (crypto_capa[i].digest_len != req_digest_len)
+ continue;
+
+ if (ODP_AUTH_ALG_AES_GCM == auth ||
+ ODP_AUTH_ALG_CHACHA20_POLY1305 == auth) {
+ uint8_t aad_len = 12;
+
+ if (aad_len < crypto_capa[i].aad_len.min ||
+ aad_len > crypto_capa[i].aad_len.max ||
+ 0 != (aad_len - crypto_capa[i].aad_len.min) %
+ crypto_capa[i].aad_len.inc)
+ continue;
+ }
+
+ if (out < num) {
+ capa[out].key_len = crypto_capa[i].key_len;
+ capa[out].icv_len = crypto_capa[i].digest_len;
+ }
+ out++;
+ }
+
+ return out;
+}
+
+void odp_ipsec_config_init(odp_ipsec_config_t *config)
+{
+ memset(config, 0, sizeof(odp_ipsec_config_t));
+ config->inbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ config->outbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ config->max_num_sa = _odp_ipsec_max_num_sa();
+ config->inbound.default_queue = ODP_QUEUE_INVALID;
+ config->inbound.lookup.min_spi = 0;
+ config->inbound.lookup.max_spi = UINT32_MAX;
+ config->inbound.reassembly.max_num_frags = 2;
+ config->stats_en = false;
+}
+
+int odp_ipsec_config(const odp_ipsec_config_t *config)
+{
+ if (config->max_num_sa > _odp_ipsec_max_num_sa())
+ return -1;
+
+ *ipsec_config = *config;
+
+ return 0;
+}
+
+odp_bool_t _odp_ipsec_is_sync_mode(odp_ipsec_dir_t dir)
+{
+ return ((dir == ODP_IPSEC_DIR_INBOUND &&
+ ipsec_config->inbound_mode == ODP_IPSEC_OP_MODE_SYNC) ||
+ (dir == ODP_IPSEC_DIR_OUTBOUND &&
+ ipsec_config->outbound_mode == ODP_IPSEC_OP_MODE_SYNC));
+}
+
+static odp_ipsec_packet_result_t *ipsec_pkt_result(odp_packet_t packet)
+{
+ _ODP_ASSERT(ODP_EVENT_PACKET_IPSEC ==
+ odp_event_subtype(odp_packet_to_event(packet)));
+
+ return &packet_hdr(packet)->ipsec_ctx;
+}
+
+#define _ODP_IPV4HDR_PROTO_OFFSET ODP_OFFSETOF(_odp_ipv4hdr_t, proto)
+#define _ODP_IPV6HDR_NHDR_OFFSET ODP_OFFSETOF(_odp_ipv6hdr_t, next_hdr)
+#define _ODP_IPV6HDREXT_NHDR_OFFSET ODP_OFFSETOF(_odp_ipv6hdr_ext_t, next_hdr)
+
+#define ipv4_hdr_len(ip) (_ODP_IPV4HDR_IHL((ip)->ver_ihl) * 4)
+
+static const uint8_t ipsec_padding[255] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
+
+typedef struct {
+ void *ip;
+ unsigned stats_length;
+ uint16_t ip_offset;
+ uint16_t ip_hdr_len;
+ uint16_t ip_tot_len;
+ uint16_t ip_next_hdr_offset;
+ uint8_t ip_next_hdr;
+ unsigned is_ipv4 : 1;
+ unsigned is_ipv6 : 1;
+ union {
+ struct {
+ uint32_t ip_flabel;
+ uint16_t ip_df;
+ uint8_t ip_tos;
+ } out_tunnel;
+ struct {
+ uint16_t hdr_len;
+ uint16_t trl_len;
+ uint64_t seq_no;
+ } in;
+ odp_u32be_t ipv4_addr;
+ uint8_t ipv6_addr[_ODP_IPV6ADDR_LEN];
+ };
+ union {
+ struct {
+ uint8_t tos;
+ uint8_t ttl;
+ odp_u16be_t frag_offset;
+ } ah_ipv4;
+ struct {
+ odp_u32be_t ver_tc_flow;
+ uint8_t hop_limit;
+ } ah_ipv6;
+ struct {
+ ipsec_aad_t aad;
+ } esp;
+ };
+ uint8_t iv[IPSEC_MAX_IV_LEN];
+} ipsec_state_t;
+
+#define MAX_BURST 32
+
+typedef struct {
+ ipsec_state_t state;
+ odp_ipsec_op_status_t status;
+ ipsec_sa_t *sa;
+ odp_ipsec_sa_t sa_hdl;
+ uint32_t orig_ip_len;
+} ipsec_op_t;
+
+#define MAX_HDR_LEN 100 /* Enough for VxLAN over IPv6 */
+
+typedef struct {
+ ipsec_op_t op;
+ uint8_t hdr_buf[MAX_HDR_LEN];
+} ipsec_inline_op_t;
+
+/*
+ * Computes 64-bit seq number according to RFC4303 A2
+ */
+static inline uint64_t ipsec_compute_esn(ipsec_sa_t *ipsec_sa, uint32_t seq)
+{
+ uint32_t wintop_h, wintop_l, winbot_l, ws;
+ uint64_t seq64 = 0, wintop = 0;
+
+ wintop = odp_atomic_load_u64(&ipsec_sa->hot.in.wintop_seq);
+ wintop_l = wintop & 0xffffffff;
+ wintop_h = wintop >> 32;
+
+ ws = ipsec_sa->in.ar.win_size;
+ winbot_l = wintop_l - ws + 1;
+
+ /* case A: window is within one sequence number subspace */
+ if (wintop_l >= (ws - 1)) {
+ if (seq < winbot_l)
+ wintop_h++;
+ /* case B: window spans two sequence number subspaces */
+ } else {
+ if (seq >= winbot_l)
+ wintop_h--;
+ }
+
+ seq64 = ((uint64_t)wintop_h << 32) | seq;
+ return seq64;
+}
+
+static inline uint32_t ipsec_get_seqh_len(ipsec_sa_t *ipsec_sa)
+{
+ return ipsec_sa->insert_seq_hi * IPSEC_SEQ_HI_LEN;
+}
+
+static int ipsec_parse_ipv4(ipsec_state_t *state, odp_packet_t pkt)
+{
+ _odp_ipv4hdr_t ipv4hdr;
+
+ odp_packet_copy_to_mem(pkt, state->ip_offset,
+ _ODP_IPV4HDR_LEN, &ipv4hdr);
+
+ if (_ODP_IPV4HDR_IS_FRAGMENT(odp_be_to_cpu_16(ipv4hdr.frag_offset)))
+ return -1;
+
+ state->ip_hdr_len = ipv4_hdr_len(&ipv4hdr);
+ state->ip_tot_len = odp_be_to_cpu_16(ipv4hdr.tot_len);
+ state->ip_next_hdr = ipv4hdr.proto;
+ state->ip_next_hdr_offset = state->ip_offset +
+ _ODP_IPV4HDR_PROTO_OFFSET;
+ state->ipv4_addr = ipv4hdr.dst_addr;
+
+ return 0;
+}
+
+static int ipsec_parse_ipv6(ipsec_state_t *state, odp_packet_t pkt)
+{
+ _odp_ipv6hdr_t ipv6hdr;
+ _odp_ipv6hdr_ext_t ipv6hdrext;
+
+ odp_packet_copy_to_mem(pkt, state->ip_offset,
+ _ODP_IPV6HDR_LEN, &ipv6hdr);
+
+ state->ip_hdr_len = _ODP_IPV6HDR_LEN;
+ state->ip_next_hdr = ipv6hdr.next_hdr;
+ state->ip_next_hdr_offset = state->ip_offset + _ODP_IPV6HDR_NHDR_OFFSET;
+ /* FIXME: Jumbo frames */
+ state->ip_tot_len = odp_be_to_cpu_16(ipv6hdr.payload_len) +
+ _ODP_IPV6HDR_LEN;
+ memcpy(state->ipv6_addr, &ipv6hdr.dst_addr, _ODP_IPV6ADDR_LEN);
+
+ while (state->ip_next_hdr == _ODP_IPPROTO_HOPOPTS ||
+ state->ip_next_hdr == _ODP_IPPROTO_DEST ||
+ state->ip_next_hdr == _ODP_IPPROTO_ROUTE) {
+ odp_packet_copy_to_mem(pkt,
+ state->ip_offset + state->ip_hdr_len,
+ sizeof(ipv6hdrext),
+ &ipv6hdrext);
+ state->ip_next_hdr = ipv6hdrext.next_hdr;
+ state->ip_next_hdr_offset = state->ip_offset +
+ state->ip_hdr_len +
+ _ODP_IPV6HDREXT_NHDR_OFFSET;
+ state->ip_hdr_len += (ipv6hdrext.ext_len + 1) * 8;
+ }
+
+ if (_ODP_IPPROTO_FRAG == state->ip_next_hdr)
+ return -1;
+
+ return 0;
+}
+
+static inline ipsec_sa_t *ipsec_get_sa(odp_ipsec_sa_t sa,
+ odp_ipsec_protocol_t proto,
+ uint32_t spi,
+ odp_ipsec_ip_version_t ver,
+ void *dst_addr,
+ odp_ipsec_op_status_t *status)
+{
+ ipsec_sa_t *ipsec_sa;
+
+ if (ODP_IPSEC_SA_INVALID == sa) {
+ ipsec_sa_lookup_t lookup;
+
+ lookup.proto = proto;
+ lookup.spi = spi;
+ lookup.ver = ver;
+ lookup.dst_addr = dst_addr;
+
+ ipsec_sa = _odp_ipsec_sa_lookup(&lookup);
+ if (NULL == ipsec_sa) {
+ status->error.sa_lookup = 1;
+ return NULL;
+ }
+ } else {
+ ipsec_sa = _odp_ipsec_sa_entry_from_hdl(sa);
+ _ODP_ASSERT(NULL != ipsec_sa);
+ if (ipsec_sa->proto != proto ||
+ ipsec_sa->spi != spi) {
+ status->error.proto = 1;
+ return ipsec_sa;
+ }
+ }
+
+ return ipsec_sa;
+}
+
+static int ipsec_in_iv(odp_packet_t pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ uint16_t iv_offset)
+{
+ if (ipsec_sa->salt_length > 0) {
+ /* It is faster to just copy MAX_SALT_LEN bytes than the exact length */
+ ODP_STATIC_ASSERT(IPSEC_MAX_SALT_LEN <= IPSEC_MAX_IV_LEN,
+ "IPSEC_MAX_SALT_LEN too large");
+ memcpy(state->iv, ipsec_sa->salt, IPSEC_MAX_SALT_LEN);
+ }
+ _ODP_ASSERT(ipsec_sa->salt_length + ipsec_sa->esp_iv_len <= IPSEC_MAX_IV_LEN);
+ if (odp_packet_copy_to_mem(pkt,
+ iv_offset,
+ ipsec_sa->esp_iv_len,
+ state->iv + ipsec_sa->salt_length) < 0)
+ return -1;
+
+ if (ipsec_sa->aes_ctr_iv) {
+ ODP_STATIC_ASSERT(IPSEC_MAX_IV_LEN >= 16, "IPSEC_MAX_IV_LEN too small");
+ state->iv[12] = 0;
+ state->iv[13] = 0;
+ state->iv[14] = 0;
+ state->iv[15] = 1;
+ }
+
+ return 0;
+}
+
+static int ipsec_in_esp(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t **_ipsec_sa,
+ odp_ipsec_sa_t sa,
+ odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status)
+{
+ _odp_esphdr_t esp;
+ uint16_t ipsec_offset;
+ ipsec_sa_t *ipsec_sa;
+ odp_bool_t udp_encap = false;
+
+ ipsec_offset = state->ip_offset + state->ip_hdr_len;
+
+ if (_ODP_IPPROTO_UDP == state->ip_next_hdr) {
+ _odp_udphdr_t udp;
+ uint16_t ip_data_len = state->ip_tot_len -
+ state->ip_hdr_len;
+
+ odp_packet_copy_to_mem(*pkt, ipsec_offset,
+ _ODP_UDPHDR_LEN, &udp);
+
+ if (udp.dst_port != odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT) ||
+ udp.length != odp_cpu_to_be_16(ip_data_len)) {
+ status->error.proto = 1;
+ return -1;
+ }
+
+ ipsec_offset += _ODP_UDPHDR_LEN;
+ state->ip_hdr_len += _ODP_UDPHDR_LEN;
+ udp_encap = true;
+ }
+
+ if (odp_packet_copy_to_mem(*pkt, ipsec_offset,
+ sizeof(esp), &esp) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ ipsec_sa = ipsec_get_sa(sa, ODP_IPSEC_ESP,
+ odp_be_to_cpu_32(esp.spi),
+ state->is_ipv4 ? ODP_IPSEC_IPV4 :
+ ODP_IPSEC_IPV6,
+ &state->ipv4_addr, status);
+ *_ipsec_sa = ipsec_sa;
+ if (status->error.all)
+ return -1;
+
+ if (!!ipsec_sa->udp_encap != udp_encap) {
+ status->error.proto = 1;
+ return -1;
+ }
+
+ if (ipsec_in_iv(*pkt, state, ipsec_sa,
+ ipsec_offset + _ODP_ESPHDR_LEN) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ state->in.hdr_len = _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;
+ state->in.trl_len = _ODP_ESPTRL_LEN + ipsec_sa->icv_len;
+
+ if (odp_unlikely(state->ip_tot_len <
+ state->ip_hdr_len + state->in.hdr_len + ipsec_sa->icv_len)) {
+ status->error.proto = 1;
+ return -1;
+ }
+
+ param->cipher_range.offset = ipsec_offset + state->in.hdr_len;
+ param->cipher_range.length = state->ip_tot_len -
+ state->ip_hdr_len -
+ state->in.hdr_len -
+ ipsec_sa->icv_len;
+ param->cipher_iv_ptr = state->iv;
+ param->auth_iv_ptr = state->iv;
+
+ state->esp.aad.spi = esp.spi;
+ state->in.seq_no = odp_be_to_cpu_32(esp.seq_no);
+
+ if (ipsec_sa->esn) {
+ state->in.seq_no = ipsec_compute_esn(ipsec_sa, state->in.seq_no);
+ state->esp.aad.seq_no64 = odp_cpu_to_be_64(state->in.seq_no);
+ } else {
+ state->esp.aad.seq_no = esp.seq_no;
+ }
+ param->aad_ptr = (uint8_t *)&state->esp.aad;
+
+ /* Insert high-order bits of ESN before the ICV for ICV check
+ * with non-combined mode algorithms.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ uint32_t inb_seqh = odp_cpu_to_be_32(state->in.seq_no >> 32);
+ uint32_t icv_offset = odp_packet_len(*pkt) - ipsec_sa->icv_len;
+
+ if (odp_packet_extend_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ _ODP_ERR("odp_packet_extend_tail failed\n");
+ return -1;
+ }
+ odp_packet_move_data(*pkt, icv_offset + IPSEC_SEQ_HI_LEN, icv_offset,
+ ipsec_sa->icv_len);
+ odp_packet_copy_from_mem(*pkt, icv_offset, IPSEC_SEQ_HI_LEN, &inb_seqh);
+ }
+
+ param->auth_range.offset = ipsec_offset;
+ param->auth_range.length = state->ip_tot_len -
+ state->ip_hdr_len +
+ ipsec_get_seqh_len(ipsec_sa) -
+ ipsec_sa->icv_len;
+ param->hash_result_offset = state->ip_offset +
+ state->ip_tot_len +
+ ipsec_get_seqh_len(ipsec_sa) -
+ ipsec_sa->icv_len;
+
+ state->stats_length = param->cipher_range.length;
+ param->session = ipsec_sa->session;
+
+ return 0;
+}
+
+static int ipsec_in_esp_post(odp_packet_t pkt,
+ ipsec_state_t *state)
+{
+ _odp_esptrl_t esptrl;
+ uint32_t esptrl_offset = state->ip_offset +
+ state->ip_tot_len -
+ state->in.trl_len;
+
+ if (odp_packet_copy_to_mem(pkt, esptrl_offset,
+ sizeof(esptrl), &esptrl) < 0 ||
+ state->ip_offset + esptrl.pad_len > esptrl_offset ||
+ _odp_packet_cmp_data(pkt, esptrl_offset - esptrl.pad_len,
+ ipsec_padding, esptrl.pad_len) != 0)
+ return -1;
+
+ if (_ODP_IPPROTO_UDP == state->ip_next_hdr) {
+ state->ip_hdr_len -= _ODP_UDPHDR_LEN;
+ state->in.hdr_len += _ODP_UDPHDR_LEN;
+ }
+
+ odp_packet_copy_from_mem(pkt, state->ip_next_hdr_offset,
+ 1, &esptrl.next_header);
+ state->in.trl_len += esptrl.pad_len;
+ state->ip_next_hdr = esptrl.next_header;
+
+ return 0;
+}
+
+static int ipsec_in_ah(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t **_ipsec_sa,
+ odp_ipsec_sa_t sa,
+ odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status)
+{
+ _odp_ahhdr_t ah;
+ uint16_t ipsec_offset;
+ ipsec_sa_t *ipsec_sa;
+
+ ipsec_offset = state->ip_offset + state->ip_hdr_len;
+
+ if (odp_packet_copy_to_mem(*pkt, ipsec_offset,
+ sizeof(ah), &ah) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ ipsec_sa = ipsec_get_sa(sa, ODP_IPSEC_AH,
+ odp_be_to_cpu_32(ah.spi),
+ state->is_ipv4 ? ODP_IPSEC_IPV4 :
+ ODP_IPSEC_IPV6,
+ &state->ipv4_addr, status);
+ *_ipsec_sa = ipsec_sa;
+ if (status->error.all)
+ return -1;
+
+ if (ipsec_in_iv(*pkt, state, ipsec_sa,
+ ipsec_offset + _ODP_AHHDR_LEN) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ param->auth_iv_ptr = state->iv;
+
+ state->in.hdr_len = (ah.ah_len + 2) * 4;
+ state->in.trl_len = 0;
+
+ if (state->is_ipv4) {
+ _odp_ipv4hdr_t *ipv4hdr = state->ip;
+
+ /* Save everything to context */
+ state->ah_ipv4.tos = ipv4hdr->tos;
+ state->ah_ipv4.frag_offset = ipv4hdr->frag_offset;
+ state->ah_ipv4.ttl = ipv4hdr->ttl;
+
+ /* FIXME: zero copy of header, passing it to crypto! */
+ /*
+ * If authenticating, zero the mutable fields build the request
+ */
+ ipv4hdr->chksum = 0;
+ ipv4hdr->tos = 0;
+ ipv4hdr->frag_offset = 0;
+ ipv4hdr->ttl = 0;
+ } else {
+ _odp_ipv6hdr_t *ipv6hdr = state->ip;
+
+ state->ah_ipv6.ver_tc_flow = ipv6hdr->ver_tc_flow;
+ state->ah_ipv6.hop_limit = ipv6hdr->hop_limit;
+ ipv6hdr->ver_tc_flow =
+ odp_cpu_to_be_32(6 << _ODP_IPV6HDR_VERSION_SHIFT);
+ ipv6hdr->hop_limit = 0;
+ }
+
+ state->in.seq_no = odp_be_to_cpu_32(ah.seq_no);
+ if (ipsec_sa->esn)
+ state->in.seq_no = ipsec_compute_esn(ipsec_sa, state->in.seq_no);
+
+ /* ESN higher 32 bits are included at the end of the packet data
+ * for inbound ICV computation.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ uint32_t inb_seqh = odp_cpu_to_be_32(state->in.seq_no >> 32);
+ uint32_t seqh_offset = odp_packet_len(*pkt);
+
+ if (odp_packet_extend_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ _ODP_ERR("odp_packet_extend_tail failed\n");
+ return -1;
+ }
+ odp_packet_copy_from_mem(*pkt, seqh_offset, IPSEC_SEQ_HI_LEN, &inb_seqh);
+ }
+
+ param->auth_range.offset = state->ip_offset;
+ param->auth_range.length = state->ip_tot_len;
+ param->hash_result_offset = ipsec_offset + _ODP_AHHDR_LEN +
+ ipsec_sa->esp_iv_len;
+
+ state->stats_length = param->auth_range.length;
+ param->auth_range.length += ipsec_get_seqh_len(ipsec_sa);
+ param->session = ipsec_sa->session;
+
+ return 0;
+}
+
+static int ipsec_in_ah_post(odp_packet_t pkt,
+ ipsec_state_t *state)
+{
+ _odp_ahhdr_t ah;
+ uint16_t ipsec_offset;
+
+ ipsec_offset = state->ip_offset + state->ip_hdr_len;
+
+ if (odp_packet_copy_to_mem(pkt, ipsec_offset,
+ sizeof(ah), &ah) < 0)
+ return -1;
+
+ odp_packet_copy_from_mem(pkt, state->ip_next_hdr_offset,
+ 1, &ah.next_header);
+
+ /* Restore mutable fields */
+ if (state->is_ipv4) {
+ _odp_ipv4hdr_t *ipv4hdr = state->ip;
+
+ ipv4hdr->ttl = state->ah_ipv4.ttl;
+ ipv4hdr->tos = state->ah_ipv4.tos;
+ ipv4hdr->frag_offset = state->ah_ipv4.frag_offset;
+ } else {
+ _odp_ipv6hdr_t *ipv6hdr = odp_packet_l3_ptr(pkt, NULL);
+
+ ipv6hdr->ver_tc_flow = state->ah_ipv6.ver_tc_flow;
+ ipv6hdr->hop_limit = state->ah_ipv6.hop_limit;
+ }
+ state->ip_next_hdr = ah.next_header;
+
+ return 0;
+}
+
+static void
+ipsec_sa_err_stats_update(ipsec_sa_t *sa, odp_ipsec_op_status_t *status)
+{
+ odp_ipsec_op_status_t err_status;
+
+ if (odp_likely(ODP_IPSEC_OK == status->error.all))
+ return;
+
+ if (NULL == sa)
+ return;
+
+ err_status = *status;
+
+ if (err_status.error.proto)
+ odp_atomic_inc_u64(&sa->stats.proto_err);
+
+ if (err_status.error.auth)
+ odp_atomic_inc_u64(&sa->stats.auth_err);
+
+ if (err_status.error.antireplay)
+ odp_atomic_inc_u64(&sa->stats.antireplay_err);
+
+ if (err_status.error.alg)
+ odp_atomic_inc_u64(&sa->stats.alg_err);
+
+ if (err_status.error.mtu)
+ odp_atomic_inc_u64(&sa->stats.mtu_err);
+
+ if (err_status.error.hard_exp_bytes)
+ odp_atomic_inc_u64(&sa->stats.hard_exp_bytes_err);
+
+ if (err_status.error.hard_exp_packets)
+ odp_atomic_inc_u64(&sa->stats.hard_exp_pkts_err);
+}
+
+static int ipsec_in_parse_encap_packet(odp_packet_t pkt, ipsec_state_t *state,
+ odp_ipsec_op_status_t *status, uint32_t *orig_ip_len)
+{
+ int (*op)(ipsec_state_t *state, odp_packet_t pkt);
+
+ state->ip_offset = odp_packet_l3_offset(pkt);
+ _ODP_ASSERT(ODP_PACKET_OFFSET_INVALID != state->ip_offset);
+ state->ip = odp_packet_l3_ptr(pkt, NULL);
+ _ODP_ASSERT(NULL != state->ip);
+ state->is_ipv4 = (((uint8_t *)state->ip)[0] >> 4) == 0x4;
+ state->is_ipv6 = (((uint8_t *)state->ip)[0] >> 4) == 0x6;
+
+ if (odp_unlikely(!(state->is_ipv4 || state->is_ipv6)))
+ goto err;
+
+ op = state->is_ipv4 ? ipsec_parse_ipv4 : ipsec_parse_ipv6;
+
+ if (odp_unlikely(op(state, pkt) ||
+ state->ip_tot_len + state->ip_offset > odp_packet_len(pkt)))
+ goto err;
+
+ *orig_ip_len = state->ip_tot_len;
+
+ return 0;
+
+err:
+ status->error.alg = 1;
+
+ return -1;
+}
+
+static int ipsec_in_prepare_op(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t **ipsec_sa,
+ odp_ipsec_sa_t sa, odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status)
+{
+ int (*op)(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t **ipsec_sa,
+ odp_ipsec_sa_t sa, odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status);
+
+ memset(param, 0, sizeof(*param));
+
+ if (odp_unlikely(!(_ODP_IPPROTO_ESP == state->ip_next_hdr ||
+ _ODP_IPPROTO_UDP == state->ip_next_hdr ||
+ _ODP_IPPROTO_AH == state->ip_next_hdr))) {
+ status->error.proto = 1;
+
+ return -1;
+ }
+
+ op = _ODP_IPPROTO_ESP == state->ip_next_hdr || _ODP_IPPROTO_UDP == state->ip_next_hdr ?
+ ipsec_in_esp : ipsec_in_ah;
+
+ return op(pkt, state, ipsec_sa, sa, param, status);
+}
+
+static int ipsec_in_prepare_packet(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t **ipsec_sa,
+ odp_ipsec_sa_t sa, odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status, uint32_t *orig_ip_len)
+{
+ return ipsec_in_parse_encap_packet(*pkt, state, status, orig_ip_len) ||
+ ipsec_in_prepare_op(pkt, state, ipsec_sa, sa, param, status) ||
+ _odp_ipsec_sa_replay_precheck(*ipsec_sa, state->in.seq_no, status) < 0 ||
+ _odp_ipsec_sa_stats_precheck(*ipsec_sa, status) < 0;
+}
+
+static int ipsec_in_do_crypto(odp_packet_t *pkt, odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status)
+{
+ odp_crypto_packet_result_t result;
+ int rc;
+
+ if (odp_unlikely(odp_crypto_op(pkt, pkt, param, 1) < 0)) {
+ _ODP_DBG("Crypto failed\n");
+ goto alg_err;
+ }
+
+ rc = odp_crypto_result(&result, *pkt);
+
+ if (odp_likely(rc == 0))
+ return 0;
+
+ if (odp_unlikely(rc < -1)) {
+ _ODP_DBG("Crypto failed\n");
+ goto alg_err;
+ }
+
+ if (result.cipher_status.alg_err == ODP_CRYPTO_ALG_ERR_ICV_CHECK ||
+ result.auth_status.alg_err == ODP_CRYPTO_ALG_ERR_ICV_CHECK)
+ goto auth_err;
+
+alg_err:
+ status->error.alg = 1;
+
+ return -1;
+
+auth_err:
+ status->error.auth = 1;
+
+ return -1;
+}
+
+static int ipsec_in_finalize_op(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status)
+{
+ int (*op)(odp_packet_t pkt, ipsec_state_t *state);
+
+ state->ip = odp_packet_l3_ptr(*pkt, NULL);
+
+ if (odp_unlikely(!(ODP_IPSEC_ESP == ipsec_sa->proto || ODP_IPSEC_AH == ipsec_sa->proto)))
+ goto proto_err;
+
+ op = ODP_IPSEC_ESP == ipsec_sa->proto ? ipsec_in_esp_post : ipsec_in_ah_post;
+
+ if (odp_unlikely(op(*pkt, state)))
+ goto proto_err;
+
+ if (odp_unlikely(odp_packet_trunc_tail(pkt,
+ state->in.trl_len + ipsec_get_seqh_len(ipsec_sa),
+ NULL, NULL) < 0))
+ goto alg_err;
+
+ state->ip_tot_len -= state->in.trl_len;
+
+ return 0;
+
+proto_err:
+ status->error.proto = 1;
+
+ return -1;
+
+alg_err:
+ status->error.alg = 1;
+
+ return -1;
+}
+
+static int ipsec_in_strip_tunnel(odp_packet_t *pkt, ipsec_state_t *state,
+ odp_ipsec_op_status_t *status)
+{
+ odp_packet_move_data(*pkt, state->ip_hdr_len + state->in.hdr_len, 0, state->ip_offset);
+
+ if (odp_unlikely(odp_packet_trunc_head(pkt, state->ip_hdr_len + state->in.hdr_len, NULL,
+ NULL) < 0)) {
+ status->error.alg = 1;
+
+ return -1;
+ }
+
+ state->ip_tot_len -= state->ip_hdr_len + state->in.hdr_len;
+
+ if (odp_unlikely(!(_ODP_IPPROTO_IPIP == state->ip_next_hdr ||
+ _ODP_IPPROTO_IPV6 == state->ip_next_hdr ||
+ _ODP_IPPROTO_NO_NEXT == state->ip_next_hdr))) {
+ status->error.proto = 1;
+
+ return -1;
+ }
+
+ state->is_ipv4 = _ODP_IPPROTO_IPIP == state->ip_next_hdr;
+ state->is_ipv6 = _ODP_IPPROTO_IPV6 == state->ip_next_hdr;
+
+ return 0;
+}
+
+static int ipsec_in_strip_tp(odp_packet_t *pkt, ipsec_state_t *state,
+ odp_ipsec_op_status_t *status)
+{
+ odp_packet_move_data(*pkt, state->in.hdr_len, 0, state->ip_offset + state->ip_hdr_len);
+
+ if (odp_unlikely(odp_packet_trunc_head(pkt, state->in.hdr_len, NULL, NULL) < 0)) {
+ status->error.alg = 1;
+
+ return -1;
+ }
+
+ state->ip_tot_len -= state->in.hdr_len;
+
+ return 0;
+}
+
+static int ipsec_in_strip_headers(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status)
+{
+ int (*op)(odp_packet_t *pkt, ipsec_state_t *state, odp_ipsec_op_status_t *status);
+
+ op = ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode ? ipsec_in_strip_tunnel : ipsec_in_strip_tp;
+
+ return op(pkt, state, status);
+}
+
+static int ipsec_in_finalize_decap_header(odp_packet_t pkt, ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa, odp_ipsec_op_status_t *status)
+{
+ _odp_ipv4hdr_t *ipv4hdr;
+ _odp_ipv6hdr_t *ipv6hdr;
+
+ if (state->is_ipv4 && odp_packet_len(pkt) > _ODP_IPV4HDR_LEN) {
+ ipv4hdr = odp_packet_l3_ptr(pkt, NULL);
+
+ if (ODP_IPSEC_MODE_TRANSPORT == ipsec_sa->mode)
+ ipv4hdr->tot_len = odp_cpu_to_be_16(state->ip_tot_len);
+ else
+ ipv4hdr->ttl -= ipsec_sa->dec_ttl;
+
+ _odp_packet_ipv4_chksum_insert(pkt);
+ } else if (state->is_ipv6 && odp_packet_len(pkt) > _ODP_IPV6HDR_LEN) {
+ ipv6hdr = odp_packet_l3_ptr(pkt, NULL);
+
+ if (ODP_IPSEC_MODE_TRANSPORT == ipsec_sa->mode)
+ ipv6hdr->payload_len = odp_cpu_to_be_16(state->ip_tot_len -
+ _ODP_IPV6HDR_LEN);
+ else
+ ipv6hdr->hop_limit -= ipsec_sa->dec_ttl;
+ } else if (state->ip_next_hdr != _ODP_IPPROTO_NO_NEXT) {
+ status->error.proto = 1;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipsec_in_finalize_packet(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status)
+{
+ return _odp_ipsec_sa_lifetime_update(ipsec_sa, state->stats_length, status) < 0 ||
+ ipsec_in_finalize_op(pkt, state, ipsec_sa, status) ||
+ ipsec_in_strip_headers(pkt, state, ipsec_sa, status) ||
+ ipsec_in_finalize_decap_header(*pkt, state, ipsec_sa, status);
+}
+
+static void ipsec_in_reset_parse_data(odp_packet_t pkt, ipsec_state_t *state)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ packet_parse_reset(pkt_hdr, 0);
+ pkt_hdr->p.l3_offset = state->ip_offset;
+}
+
+static void ipsec_in_parse_packet(odp_packet_t pkt, ipsec_state_t *state)
+{
+ odp_packet_parse_param_t parse_param;
+
+ parse_param.proto = state->is_ipv4 ? ODP_PROTO_IPV4 :
+ state->is_ipv6 ? ODP_PROTO_IPV6 :
+ ODP_PROTO_NONE;
+ parse_param.last_layer = ipsec_config->inbound.parse_level;
+ parse_param.chksums = ipsec_config->inbound.chksums;
+ /* We do not care about return code here. Parsing error should not result in IPsec
+ * error. */
+ odp_packet_parse(pkt, state->ip_offset, &parse_param);
+}
+
+static void ipsec_in_parse_decap_packet(odp_packet_t pkt, ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa)
+{
+ void (*op)(odp_packet_t pkt, ipsec_state_t *state);
+
+ op = _ODP_IPPROTO_NO_NEXT == state->ip_next_hdr &&
+ ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode ? ipsec_in_reset_parse_data :
+ ipsec_in_parse_packet;
+
+ op(pkt, state);
+}
+
+static ipsec_sa_t *ipsec_in_single(odp_packet_t pkt,
+ odp_ipsec_sa_t sa,
+ odp_packet_t *pkt_out,
+ odp_bool_t enqueue_op,
+ odp_ipsec_op_status_t *status,
+ uint32_t *orig_ip_len)
+{
+ ipsec_state_t state;
+ ipsec_sa_t *ipsec_sa = NULL;
+ odp_crypto_packet_op_param_t param;
+
+ if (odp_unlikely(ipsec_in_prepare_packet(&pkt, &state, &ipsec_sa, sa, &param, status,
+ orig_ip_len)))
+ goto exit;
+
+ if (ipsec_in_do_crypto(&pkt, &param, status))
+ goto exit;
+
+ if (ipsec_sa->antireplay) {
+ if (enqueue_op)
+ wait_for_order(ipsec_global->inbound_ordering_mode);
+
+ if (_odp_ipsec_sa_replay_update(ipsec_sa, state.in.seq_no, status) < 0)
+ goto exit;
+ }
+
+ if (odp_unlikely(ipsec_in_finalize_packet(&pkt, &state, ipsec_sa, status)))
+ goto post_lifetime_err_cnt_update;
+
+ ipsec_in_parse_decap_packet(pkt, &state, ipsec_sa);
+
+ goto exit;
+
+post_lifetime_err_cnt_update:
+ if (ipsec_config->stats_en) {
+ odp_atomic_inc_u64(&ipsec_sa->stats.post_lifetime_err_pkts);
+ odp_atomic_add_u64(&ipsec_sa->stats.post_lifetime_err_bytes, state.stats_length);
+ }
+
+exit:
+ *pkt_out = pkt;
+
+ if (ipsec_config->stats_en)
+ ipsec_sa_err_stats_update(ipsec_sa, status);
+
+ return ipsec_sa;
+}
+
+/* Generate sequence number */
+static inline
+uint64_t ipsec_seq_no(ipsec_sa_t *ipsec_sa)
+{
+ return odp_atomic_fetch_add_u64(&ipsec_sa->hot.out.seq, 1);
+}
+
+/* Helper for calculating encode length using data length and block size */
+#define IPSEC_PAD_LEN(x, b) ((((x) + ((b) - 1)) / (b)) * (b))
+
+/*
+ * Round len up to next multiple of pad_mask + 1.
+ * pad_mask + 1 must be a power of 2.
+ */
+static inline uint32_t ipsec_padded_len(uint32_t len, uint32_t pad_mask)
+{
+ _ODP_ASSERT(_ODP_CHECK_IS_POWER2(pad_mask + 1));
+
+ return (len + pad_mask) & ~pad_mask;
+}
+
+static int ipsec_out_tunnel_parse_ipv4(ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa)
+{
+ _odp_ipv4hdr_t *ipv4hdr = state->ip;
+ uint16_t flags = odp_be_to_cpu_16(ipv4hdr->frag_offset);
+
+ ipv4hdr->ttl -= ipsec_sa->dec_ttl;
+ state->out_tunnel.ip_tos = ipv4hdr->tos;
+ state->out_tunnel.ip_df = _ODP_IPV4HDR_FLAGS_DONT_FRAG(flags);
+ state->out_tunnel.ip_flabel = 0;
+ state->ip_next_hdr = ipv4hdr->proto;
+
+ return 0;
+}
+
+static int ipsec_out_tunnel_parse_ipv6(ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa)
+{
+ _odp_ipv6hdr_t *ipv6hdr = state->ip;
+ uint32_t ver_tc_flow = odp_be_to_cpu_32(ipv6hdr->ver_tc_flow);
+
+ ipv6hdr->hop_limit -= ipsec_sa->dec_ttl;
+ state->out_tunnel.ip_tos = (ver_tc_flow &
+ _ODP_IPV6HDR_TC_MASK) >>
+ _ODP_IPV6HDR_TC_SHIFT;
+ state->out_tunnel.ip_df = 0;
+ state->out_tunnel.ip_flabel = (ver_tc_flow &
+ _ODP_IPV6HDR_FLOW_LABEL_MASK) >>
+ _ODP_IPV6HDR_FLOW_LABEL_SHIFT;
+ state->ip_next_hdr = ipv6hdr->next_hdr;
+
+ return 0;
+}
+
+static int ipsec_out_tunnel_ipv4(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_ipv4_param_t *ipv4_param)
+{
+ _odp_ipv4hdr_t out_ip;
+ uint16_t flags;
+
+ out_ip.ver_ihl = 0x45;
+ if (ipsec_sa->copy_dscp)
+ out_ip.tos = state->out_tunnel.ip_tos;
+ else
+ out_ip.tos = (state->out_tunnel.ip_tos &
+ ~_ODP_IP_TOS_DSCP_MASK) |
+ (ipv4_param->dscp <<
+ _ODP_IP_TOS_DSCP_SHIFT);
+ state->ip_tot_len = odp_packet_len(*pkt) - state->ip_offset;
+ state->ip_tot_len += _ODP_IPV4HDR_LEN;
+
+ out_ip.tot_len = odp_cpu_to_be_16(state->ip_tot_len);
+ if (ipsec_sa->copy_df)
+ flags = state->out_tunnel.ip_df;
+ else
+ flags = ((uint16_t)ipv4_param->df) << 14;
+ out_ip.frag_offset = odp_cpu_to_be_16(flags);
+
+ /* Allocate unique IP ID only for non-atomic datagrams */
+ if (out_ip.frag_offset == 0)
+ out_ip.id = _odp_ipsec_sa_alloc_ipv4_id(ipsec_sa);
+ else
+ out_ip.id = 0;
+
+ out_ip.ttl = ipv4_param->ttl;
+ /* Will be filled later by packet checksum update */
+ out_ip.chksum = 0;
+ memcpy(&out_ip.src_addr, ipv4_param->src_addr,
+ _ODP_IPV4ADDR_LEN);
+ memcpy(&out_ip.dst_addr, ipv4_param->dst_addr,
+ _ODP_IPV4ADDR_LEN);
+
+ if (odp_packet_extend_head(pkt, _ODP_IPV4HDR_LEN,
+ NULL, NULL) < 0)
+ return -1;
+
+ odp_packet_move_data(*pkt, 0, _ODP_IPV4HDR_LEN, state->ip_offset);
+
+ odp_packet_copy_from_mem(*pkt, state->ip_offset,
+ _ODP_IPV4HDR_LEN, &out_ip);
+
+ state->ip = odp_packet_l3_ptr(*pkt, NULL);
+ state->ip_hdr_len = _ODP_IPV4HDR_LEN;
+ if (state->is_ipv4)
+ state->ip_next_hdr = _ODP_IPPROTO_IPIP;
+ else if (state->is_ipv6)
+ state->ip_next_hdr = _ODP_IPPROTO_IPV6;
+ else
+ state->ip_next_hdr = _ODP_IPPROTO_NO_NEXT;
+ state->ip_next_hdr_offset = state->ip_offset +
+ _ODP_IPV4HDR_PROTO_OFFSET;
+
+ state->is_ipv4 = 1;
+ state->is_ipv6 = 0;
+
+ return 0;
+}
+
+static int ipsec_out_tunnel_ipv6(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_ipv6_param_t *ipv6_param)
+{
+ _odp_ipv6hdr_t out_ip;
+ uint32_t ver;
+
+ ver = 6 << _ODP_IPV6HDR_VERSION_SHIFT;
+ if (ipsec_sa->copy_dscp)
+ ver |= state->out_tunnel.ip_tos << _ODP_IPV6HDR_TC_SHIFT;
+ else
+ ver |= ((state->out_tunnel.ip_tos &
+ ~_ODP_IP_TOS_DSCP_MASK) |
+ (ipv6_param->dscp <<
+ _ODP_IP_TOS_DSCP_SHIFT)) <<
+ _ODP_IPV6HDR_TC_SHIFT;
+ if (ipsec_sa->copy_flabel)
+ ver |= state->out_tunnel.ip_flabel;
+ else
+ ver |= ipv6_param->flabel;
+ out_ip.ver_tc_flow = odp_cpu_to_be_32(ver);
+
+ state->ip_tot_len = odp_packet_len(*pkt) - state->ip_offset;
+ out_ip.payload_len = odp_cpu_to_be_16(state->ip_tot_len);
+ state->ip_tot_len += _ODP_IPV6HDR_LEN;
+
+ out_ip.hop_limit = ipv6_param->hlimit;
+ memcpy(&out_ip.src_addr, ipv6_param->src_addr,
+ _ODP_IPV6ADDR_LEN);
+ memcpy(&out_ip.dst_addr, ipv6_param->dst_addr,
+ _ODP_IPV6ADDR_LEN);
+
+ if (odp_packet_extend_head(pkt, _ODP_IPV6HDR_LEN,
+ NULL, NULL) < 0)
+ return -1;
+
+ odp_packet_move_data(*pkt, 0, _ODP_IPV6HDR_LEN, state->ip_offset);
+
+ odp_packet_copy_from_mem(*pkt, state->ip_offset,
+ sizeof(out_ip), &out_ip);
+
+ state->ip = odp_packet_l3_ptr(*pkt, NULL);
+ state->ip_hdr_len = _ODP_IPV6HDR_LEN;
+ if (state->is_ipv4)
+ state->ip_next_hdr = _ODP_IPPROTO_IPIP;
+ else if (state->is_ipv6)
+ state->ip_next_hdr = _ODP_IPPROTO_IPV6;
+ else
+ state->ip_next_hdr = _ODP_IPPROTO_NO_NEXT;
+ state->ip_next_hdr_offset = state->ip_offset + _ODP_IPV6HDR_NHDR_OFFSET;
+
+ state->is_ipv4 = 0;
+ state->is_ipv6 = 1;
+
+ return 0;
+}
+
+#define IPSEC_RANDOM_BUF_SIZE 256
+
+static int ipsec_random_data(uint8_t *data, uint32_t len)
+{
+ static __thread uint8_t buffer[IPSEC_RANDOM_BUF_SIZE];
+ static __thread uint32_t buffer_used = IPSEC_RANDOM_BUF_SIZE;
+
+ if (odp_likely(buffer_used + len <= IPSEC_RANDOM_BUF_SIZE)) {
+ memcpy(data, &buffer[buffer_used], len);
+ buffer_used += len;
+ } else if (odp_likely(len <= IPSEC_RANDOM_BUF_SIZE)) {
+ uint32_t rnd_len;
+
+ rnd_len = odp_random_data(buffer, IPSEC_RANDOM_BUF_SIZE,
+ ODP_RANDOM_CRYPTO);
+ if (odp_unlikely(rnd_len != IPSEC_RANDOM_BUF_SIZE))
+ return -1;
+ memcpy(data, &buffer[0], len);
+ buffer_used = len;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Generate cipher IV for outbound processing.
+ */
+static int ipsec_out_iv(ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ uint64_t seq_no)
+{
+ if (ipsec_sa->use_counter_iv) {
+ /* Both GCM and CTR use 8-bit counters */
+ _ODP_ASSERT(sizeof(seq_no) == ipsec_sa->esp_iv_len);
+
+ /* It is faster to just copy MAX_SALT_LEN bytes than the exact length */
+ ODP_STATIC_ASSERT(IPSEC_MAX_SALT_LEN <= IPSEC_MAX_IV_LEN,
+ "IPSEC_MAX_SALT_LEN too large");
+ memcpy(state->iv, ipsec_sa->salt, IPSEC_MAX_SALT_LEN);
+
+ _ODP_ASSERT(ipsec_sa->salt_length + sizeof(seq_no) <= IPSEC_MAX_IV_LEN);
+ memcpy(state->iv + ipsec_sa->salt_length, &seq_no, sizeof(seq_no));
+
+ if (ipsec_sa->aes_ctr_iv) {
+ ODP_STATIC_ASSERT(IPSEC_MAX_IV_LEN >= 16, "IPSEC_MAX_IV_LEN too small");
+ state->iv[12] = 0;
+ state->iv[13] = 0;
+ state->iv[14] = 0;
+ state->iv[15] = 1;
+ }
+ } else if (ipsec_sa->use_cbc_iv) {
+ /*
+ * For CBC mode ciphers with 16 byte IV we generate the cipher
+ * IV by concatenating a per-session random salt value and
+ * 64-bit sequence number. The ESP IV will be generated at
+ * ciphering time by CBC-encrypting a zero block using the
+ * cipher IV.
+ *
+ * This way each packet of an SA will have an unpredictable
+ * IV and different SAs (e.g. manually keyed SAs across
+ * restarts) will have different IV sequences (so one cannot
+ * predict IVs of an SA by observing the IVs of another SA
+ * with the same key).
+ */
+ _ODP_ASSERT(CBC_SALT_LEN + sizeof(seq_no) == ipsec_sa->esp_iv_len);
+ ODP_STATIC_ASSERT(CBC_SALT_LEN + sizeof(seq_no) <= IPSEC_MAX_IV_LEN,
+ "IPSEC_MAX_IV_LEN too small for CBC IV construction");
+ memcpy(state->iv, ipsec_sa->cbc_salt, CBC_SALT_LEN);
+ memcpy(state->iv + CBC_SALT_LEN, &seq_no, sizeof(seq_no));
+ } else if (odp_unlikely(ipsec_sa->esp_iv_len)) {
+ _ODP_ASSERT(ipsec_sa->esp_iv_len <= IPSEC_MAX_IV_LEN);
+ if (ipsec_random_data(state->iv, ipsec_sa->esp_iv_len))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipsec_out_esp(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status,
+ uint32_t mtu,
+ odp_bool_t enqueue_op,
+ const odp_ipsec_out_opt_t *opt)
+{
+ _odp_esphdr_t esp;
+ _odp_esptrl_t esptrl;
+ _odp_udphdr_t udphdr;
+ uint32_t encrypt_len;
+ uint16_t ip_data_len = state->ip_tot_len -
+ state->ip_hdr_len;
+ uint16_t tfc_len = (opt->flag.tfc_pad || opt->flag.tfc_dummy) ?
+ opt->tfc_pad_len : 0;
+ uint16_t ipsec_offset = state->ip_offset + state->ip_hdr_len;
+ unsigned hdr_len;
+ unsigned trl_len;
+ unsigned pkt_len, new_len;
+ uint8_t proto = _ODP_IPPROTO_ESP;
+ uint64_t seq_no;
+
+ if (odp_unlikely(opt->flag.tfc_dummy)) {
+ ip_data_len = 0;
+ state->ip_tot_len = state->ip_offset + state->ip_hdr_len;
+ }
+
+ encrypt_len = ipsec_padded_len(ip_data_len + tfc_len + _ODP_ESPTRL_LEN,
+ ipsec_sa->esp_pad_mask);
+
+ hdr_len = _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;
+ trl_len = encrypt_len -
+ ip_data_len +
+ ipsec_sa->icv_len;
+
+ if (ipsec_sa->udp_encap) {
+ hdr_len += _ODP_UDPHDR_LEN;
+ proto = _ODP_IPPROTO_UDP;
+ udphdr.src_port = odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT);
+ udphdr.dst_port = odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT);
+ udphdr.length = odp_cpu_to_be_16(ip_data_len +
+ hdr_len + trl_len);
+ udphdr.chksum = 0; /* should be 0 by RFC */
+ }
+
+ if (state->ip_tot_len + hdr_len + trl_len > mtu) {
+ status->error.mtu = 1;
+ return -1;
+ }
+
+ if (enqueue_op)
+ wait_for_order(ipsec_global->outbound_ordering_mode);
+ seq_no = ipsec_seq_no(ipsec_sa);
+
+ if (ipsec_out_iv(state, ipsec_sa, seq_no) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ param->cipher_iv_ptr = state->iv;
+ param->auth_iv_ptr = state->iv;
+
+ memset(&esp, 0, sizeof(esp));
+ esp.spi = odp_cpu_to_be_32(ipsec_sa->spi);
+ state->esp.aad.spi = esp.spi;
+ esp.seq_no = odp_cpu_to_be_32(seq_no & 0xffffffff);
+
+ if (ipsec_sa->esn)
+ state->esp.aad.seq_no64 = odp_cpu_to_be_64(seq_no);
+ else
+ state->esp.aad.seq_no = esp.seq_no;
+
+ param->aad_ptr = (uint8_t *)&state->esp.aad;
+
+ memset(&esptrl, 0, sizeof(esptrl));
+ esptrl.pad_len = encrypt_len - ip_data_len - tfc_len - _ODP_ESPTRL_LEN;
+ esptrl.next_header = state->ip_next_hdr;
+
+ odp_packet_copy_from_mem(*pkt, state->ip_next_hdr_offset, 1, &proto);
+ state->ip_tot_len += hdr_len + trl_len;
+ if (state->is_ipv4) {
+ _odp_ipv4hdr_t *ipv4hdr = state->ip;
+
+ ipv4hdr->tot_len = odp_cpu_to_be_16(state->ip_tot_len);
+ } else if (state->is_ipv6) {
+ _odp_ipv6hdr_t *ipv6hdr = state->ip;
+
+ ipv6hdr->payload_len = odp_cpu_to_be_16(state->ip_tot_len -
+ _ODP_IPV6HDR_LEN);
+ }
+
+ if (odp_packet_extend_head(pkt, hdr_len, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ pkt_len = odp_packet_len(*pkt);
+ new_len = state->ip_offset + state->ip_tot_len;
+ if (pkt_len >= new_len) {
+ if (odp_packet_trunc_tail(pkt, pkt_len - new_len,
+ NULL, NULL) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+ } else {
+ if (odp_packet_extend_tail(pkt, new_len - pkt_len,
+ NULL, NULL) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+ }
+
+ odp_packet_move_data(*pkt, 0, hdr_len, ipsec_offset);
+
+ uint32_t esptrl_offset = state->ip_offset +
+ state->ip_hdr_len +
+ hdr_len +
+ encrypt_len -
+ _ODP_ESPTRL_LEN;
+
+ if (ipsec_sa->udp_encap) {
+ odp_packet_copy_from_mem(*pkt, ipsec_offset, _ODP_UDPHDR_LEN,
+ &udphdr);
+ ipsec_offset += _ODP_UDPHDR_LEN;
+ hdr_len -= _ODP_UDPHDR_LEN;
+ state->ip_hdr_len += _ODP_UDPHDR_LEN;
+ }
+
+ odp_packet_copy_from_mem(*pkt,
+ ipsec_offset, _ODP_ESPHDR_LEN,
+ &esp);
+ if (!ipsec_sa->use_cbc_iv) {
+ /* copy the relevant part of cipher IV to ESP IV */
+ odp_packet_copy_from_mem(*pkt,
+ ipsec_offset + _ODP_ESPHDR_LEN,
+ ipsec_sa->esp_iv_len,
+ state->iv + ipsec_sa->salt_length);
+ }
+ /* 0xa5 is a good value to fill data instead of generating random data
+ * to create TFC padding */
+ _odp_packet_set_data(*pkt, esptrl_offset - esptrl.pad_len - tfc_len,
+ 0xa5, tfc_len);
+ odp_packet_copy_from_mem(*pkt,
+ esptrl_offset - esptrl.pad_len,
+ esptrl.pad_len, ipsec_padding);
+ odp_packet_copy_from_mem(*pkt,
+ esptrl_offset, _ODP_ESPTRL_LEN,
+ &esptrl);
+
+ /* Outbound ICV computation includes ESN higher 32 bits as part of ESP
+ * implicit trailer for individual algo's.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ uint32_t outb_seqh = odp_cpu_to_be_32(seq_no >> 32);
+
+ if (odp_packet_extend_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ _ODP_ERR("odp_packet_extend_tail failed\n");
+ return -1;
+ }
+ odp_packet_copy_from_mem(*pkt,
+ esptrl_offset + _ODP_ESPTRL_LEN,
+ IPSEC_SEQ_HI_LEN, &outb_seqh);
+ }
+
+ if (odp_unlikely(state->ip_tot_len <
+ state->ip_hdr_len + hdr_len + ipsec_sa->icv_len)) {
+ status->error.proto = 1;
+ return -1;
+ }
+
+ param->cipher_range.offset = ipsec_offset + hdr_len;
+ param->cipher_range.length = state->ip_tot_len -
+ state->ip_hdr_len -
+ hdr_len -
+ ipsec_sa->icv_len;
+
+ param->auth_range.offset = ipsec_offset;
+ param->auth_range.length = state->ip_tot_len -
+ state->ip_hdr_len +
+ ipsec_get_seqh_len(ipsec_sa) -
+ ipsec_sa->icv_len;
+ param->hash_result_offset = state->ip_offset +
+ state->ip_tot_len +
+ ipsec_get_seqh_len(ipsec_sa) -
+ ipsec_sa->icv_len;
+
+ state->stats_length = param->cipher_range.length;
+
+ if (ipsec_sa->use_cbc_iv) {
+ /*
+ * Encrypt zeroed ESP IV field using the special cipher IV
+ * to create the final unpredictable ESP IV
+ */
+ _ODP_ASSERT(ipsec_sa->esp_iv_len == CBC_IV_LEN);
+ param->cipher_range.offset -= CBC_IV_LEN;
+ param->cipher_range.length += CBC_IV_LEN;
+ _odp_packet_set_data(*pkt,
+ ipsec_offset + _ODP_ESPHDR_LEN,
+ 0,
+ CBC_IV_LEN);
+ }
+
+ param->session = ipsec_sa->session;
+
+ return 0;
+}
+
+static int ipsec_out_esp_post(ipsec_state_t *state, odp_packet_t *pkt,
+ ipsec_sa_t *ipsec_sa)
+{
+ if (state->is_ipv4)
+ _odp_packet_ipv4_chksum_insert(*pkt);
+
+ /* Remove the high order ESN bits that were added in the packet for ICV
+ * computation.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ uint32_t icv_offset = odp_packet_len(*pkt) - ipsec_sa->icv_len;
+
+ odp_packet_move_data(*pkt, icv_offset - IPSEC_SEQ_HI_LEN, icv_offset,
+ ipsec_sa->icv_len);
+ if (odp_packet_trunc_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ _ODP_ERR("odp_packet_trunc_tail failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int ipsec_out_ah(odp_packet_t *pkt,
+ ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa,
+ odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status,
+ uint32_t mtu,
+ odp_bool_t enqueue_op)
+{
+ _odp_ahhdr_t ah;
+ unsigned hdr_len = _ODP_AHHDR_LEN + ipsec_sa->esp_iv_len +
+ ipsec_sa->icv_len;
+ uint16_t ipsec_offset = state->ip_offset + state->ip_hdr_len;
+ uint8_t proto = _ODP_IPPROTO_AH;
+ uint64_t seq_no;
+
+ if (state->ip_tot_len + hdr_len > mtu) {
+ status->error.mtu = 1;
+ return -1;
+ }
+
+ if (enqueue_op)
+ wait_for_order(ipsec_global->outbound_ordering_mode);
+ seq_no = ipsec_seq_no(ipsec_sa);
+
+ memset(&ah, 0, sizeof(ah));
+ ah.spi = odp_cpu_to_be_32(ipsec_sa->spi);
+ ah.seq_no = odp_cpu_to_be_32(seq_no & 0xffffffff);
+ ah.next_header = state->ip_next_hdr;
+
+ odp_packet_copy_from_mem(*pkt, state->ip_next_hdr_offset, 1, &proto);
+ /* Save IP stuff */
+ if (state->is_ipv4) {
+ _odp_ipv4hdr_t *ipv4hdr = state->ip;
+
+ state->ah_ipv4.tos = ipv4hdr->tos;
+ state->ah_ipv4.frag_offset = ipv4hdr->frag_offset;
+ state->ah_ipv4.ttl = ipv4hdr->ttl;
+ ipv4hdr->chksum = 0;
+ ipv4hdr->tos = 0;
+ ipv4hdr->frag_offset = 0;
+ ipv4hdr->ttl = 0;
+ hdr_len = IPSEC_PAD_LEN(hdr_len, 4);
+ state->ip_tot_len += hdr_len;
+ ipv4hdr->tot_len = odp_cpu_to_be_16(state->ip_tot_len);
+ } else {
+ _odp_ipv6hdr_t *ipv6hdr = state->ip;
+
+ state->ah_ipv6.ver_tc_flow = ipv6hdr->ver_tc_flow;
+ state->ah_ipv6.hop_limit = ipv6hdr->hop_limit;
+ ipv6hdr->ver_tc_flow =
+ odp_cpu_to_be_32(6 << _ODP_IPV6HDR_VERSION_SHIFT);
+ ipv6hdr->hop_limit = 0;
+
+ hdr_len = IPSEC_PAD_LEN(hdr_len, 8);
+ state->ip_tot_len += hdr_len;
+ ipv6hdr->payload_len = odp_cpu_to_be_16(state->ip_tot_len -
+ _ODP_IPV6HDR_LEN);
+ }
+
+ ah.ah_len = hdr_len / 4 - 2;
+
+ /* For GMAC */
+ if (ipsec_out_iv(state, ipsec_sa, seq_no) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ param->auth_iv_ptr = state->iv;
+
+ if (odp_packet_extend_head(pkt, hdr_len, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ return -1;
+ }
+
+ odp_packet_move_data(*pkt, 0, hdr_len, ipsec_offset);
+
+ odp_packet_copy_from_mem(*pkt,
+ ipsec_offset, _ODP_AHHDR_LEN,
+ &ah);
+ odp_packet_copy_from_mem(*pkt,
+ ipsec_offset + _ODP_AHHDR_LEN,
+ ipsec_sa->esp_iv_len,
+ state->iv + ipsec_sa->salt_length);
+ _odp_packet_set_data(*pkt,
+ ipsec_offset + _ODP_AHHDR_LEN +
+ ipsec_sa->esp_iv_len,
+ 0,
+ hdr_len - _ODP_AHHDR_LEN - ipsec_sa->esp_iv_len);
+
+ /* ESN higher 32 bits are included at the end of the packet data
+ * for outbound ICV computation.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ uint32_t outb_seqh = odp_cpu_to_be_32(seq_no >> 32);
+ uint32_t seqh_offset = odp_packet_len(*pkt);
+
+ if (odp_packet_extend_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ status->error.alg = 1;
+ _ODP_ERR("odp_packet_extend_tail failed\n");
+ return -1;
+ }
+ odp_packet_copy_from_mem(*pkt,
+ seqh_offset, IPSEC_SEQ_HI_LEN, &outb_seqh);
+ }
+
+ param->auth_range.offset = state->ip_offset;
+ param->auth_range.length = state->ip_tot_len;
+ param->hash_result_offset = ipsec_offset + _ODP_AHHDR_LEN +
+ ipsec_sa->esp_iv_len;
+
+ state->stats_length = param->auth_range.length;
+ param->auth_range.length += ipsec_get_seqh_len(ipsec_sa);
+ param->session = ipsec_sa->session;
+
+ return 0;
+}
+
+static int ipsec_out_ah_post(ipsec_state_t *state, odp_packet_t *pkt,
+ ipsec_sa_t *ipsec_sa)
+{
+ if (state->is_ipv4) {
+ _odp_ipv4hdr_t *ipv4hdr = odp_packet_l3_ptr(*pkt, NULL);
+
+ ipv4hdr->ttl = state->ah_ipv4.ttl;
+ ipv4hdr->tos = state->ah_ipv4.tos;
+ ipv4hdr->frag_offset = state->ah_ipv4.frag_offset;
+
+ _odp_packet_ipv4_chksum_insert(*pkt);
+ } else {
+ _odp_ipv6hdr_t *ipv6hdr = odp_packet_l3_ptr(*pkt, NULL);
+
+ ipv6hdr->ver_tc_flow = state->ah_ipv6.ver_tc_flow;
+ ipv6hdr->hop_limit = state->ah_ipv6.hop_limit;
+ }
+
+ /* Remove the high order ESN bits that were added in the packet for ICV
+ * computation.
+ */
+ if (ipsec_sa->insert_seq_hi) {
+ if (odp_packet_trunc_tail(pkt, IPSEC_SEQ_HI_LEN, NULL, NULL) < 0) {
+ _ODP_ERR("odp_packet_trunc_tail failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#define OL_TX_CHKSUM_PKT(_cfg, _proto, _ovr_set, _ovr) \
+ (_proto && (_ovr_set ? _ovr : _cfg))
+
+static void ipsec_out_checksums(odp_packet_t pkt,
+ ipsec_state_t *state)
+{
+ odp_bool_t ipv4_chksum_pkt, udp_chksum_pkt, tcp_chksum_pkt,
+ sctp_chksum_pkt;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ odp_ipsec_outbound_config_t outbound = ipsec_config->outbound;
+
+ ipv4_chksum_pkt = OL_TX_CHKSUM_PKT(outbound.chksum.inner_ipv4,
+ state->is_ipv4,
+ pkt_hdr->p.flags.l3_chksum_set,
+ pkt_hdr->p.flags.l3_chksum);
+ udp_chksum_pkt = OL_TX_CHKSUM_PKT(outbound.chksum.inner_udp,
+ state->ip_next_hdr ==
+ _ODP_IPPROTO_UDP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+ tcp_chksum_pkt = OL_TX_CHKSUM_PKT(outbound.chksum.inner_tcp,
+ state->ip_next_hdr ==
+ _ODP_IPPROTO_TCP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+
+ sctp_chksum_pkt = OL_TX_CHKSUM_PKT(outbound.chksum.inner_sctp,
+ state->ip_next_hdr ==
+ _ODP_IPPROTO_SCTP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+
+ if (ipv4_chksum_pkt)
+ _odp_packet_ipv4_chksum_insert(pkt);
+
+ if (tcp_chksum_pkt)
+ _odp_packet_tcp_chksum_insert(pkt);
+
+ if (udp_chksum_pkt)
+ _odp_packet_udp_chksum_insert(pkt);
+
+ if (sctp_chksum_pkt)
+ _odp_packet_sctp_chksum_insert(pkt);
+}
+
+static int ipsec_out_tp_encap(odp_packet_t pkt, ipsec_state_t *state)
+{
+ int (*op)(ipsec_state_t *state, odp_packet_t pkt);
+
+ if (odp_unlikely(!(state->is_ipv4 || state->is_ipv6)))
+ return -1;
+
+ op = state->is_ipv4 ? ipsec_parse_ipv4 : ipsec_parse_ipv6;
+
+ if (odp_unlikely(op(state, pkt) ||
+ state->ip_tot_len + state->ip_offset != odp_packet_len(pkt)))
+ return -1;
+
+ ipsec_out_checksums(pkt, state);
+
+ return 0;
+}
+
+static int ipsec_out_tunnel_encap(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_out_opt_t *opt)
+{
+ int ret;
+
+ if (odp_unlikely(!(state->is_ipv4 || state->is_ipv6 || opt->flag.tfc_dummy)))
+ return -1;
+
+ if (state->is_ipv4) {
+ if (odp_unlikely(ipsec_out_tunnel_parse_ipv4(state, ipsec_sa)))
+ return -1;
+ } else if (state->is_ipv6) {
+ if (odp_unlikely(ipsec_out_tunnel_parse_ipv6(state, ipsec_sa)))
+ return -1;
+ } else {
+ state->out_tunnel.ip_tos = 0;
+ state->out_tunnel.ip_df = 0;
+ state->out_tunnel.ip_flabel = 0;
+ state->ip_next_hdr = _ODP_IPPROTO_NO_NEXT;
+ }
+
+ ipsec_out_checksums(*pkt, state);
+
+ if (ipsec_sa->tun_ipv4)
+ ret = ipsec_out_tunnel_ipv4(pkt, state, ipsec_sa,
+ opt->flag.ip_param ? &opt->ipv4 :
+ &ipsec_sa->out.tun_ipv4.param);
+ else
+ ret = ipsec_out_tunnel_ipv6(pkt, state, ipsec_sa,
+ opt->flag.ip_param ? &opt->ipv6 :
+ &ipsec_sa->out.tun_ipv6.param);
+
+ return ret;
+}
+
+static int ipsec_out_parse_encap_packet(odp_packet_t *pkt, ipsec_state_t *state,
+ ipsec_sa_t *ipsec_sa, const odp_ipsec_out_opt_t *opt,
+ odp_ipsec_op_status_t *status)
+{
+ odp_packet_hdr_t *pkt_hdr;
+ int ret;
+
+ if (opt->flag.tfc_dummy) {
+ pkt_hdr = packet_hdr(*pkt);
+ _ODP_ASSERT(ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode);
+ pkt_hdr->p.l2_offset = ODP_PACKET_OFFSET_INVALID;
+ pkt_hdr->p.l3_offset = 0;
+ state->ip_offset = 0;
+ state->ip = NULL;
+ state->is_ipv4 = 0;
+ state->is_ipv6 = 0;
+ } else {
+ state->ip_offset = odp_packet_l3_offset(*pkt);
+ _ODP_ASSERT(ODP_PACKET_OFFSET_INVALID != state->ip_offset);
+ state->ip = odp_packet_l3_ptr(*pkt, NULL);
+ _ODP_ASSERT(NULL != state->ip);
+ state->is_ipv4 = (((uint8_t *)state->ip)[0] >> 4) == 0x4;
+ state->is_ipv6 = (((uint8_t *)state->ip)[0] >> 4) == 0x6;
+ }
+
+ if (ODP_IPSEC_MODE_TRANSPORT == ipsec_sa->mode)
+ ret = ipsec_out_tp_encap(*pkt, state);
+ else
+ ret = ipsec_out_tunnel_encap(pkt, state, ipsec_sa, opt);
+
+ if (odp_unlikely(ret))
+ status->error.alg = 1;
+
+ return ret;
+}
+
+static int ipsec_out_prepare_op(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_out_opt_t *opt, odp_bool_t is_enqueue_op,
+ odp_crypto_packet_op_param_t *param, odp_ipsec_op_status_t *status)
+{
+ odp_ipsec_frag_mode_t frag_mode;
+ uint32_t mtu;
+ int ret;
+
+ memset(param, 0, sizeof(*param));
+
+ frag_mode = opt->flag.frag_mode ? opt->frag_mode : ipsec_sa->out.frag_mode;
+ mtu = frag_mode == ODP_IPSEC_FRAG_CHECK ? odp_atomic_load_u32(&ipsec_sa->out.mtu) :
+ UINT32_MAX;
+
+ if (odp_unlikely(!(ODP_IPSEC_ESP == ipsec_sa->proto || ODP_IPSEC_AH == ipsec_sa->proto))) {
+ status->error.alg = 1;
+
+ return -1;
+ }
+
+ if (ODP_IPSEC_ESP == ipsec_sa->proto)
+ ret = ipsec_out_esp(pkt, state, ipsec_sa, param, status, mtu, is_enqueue_op, opt);
+ else
+ ret = ipsec_out_ah(pkt, state, ipsec_sa, param, status, mtu, is_enqueue_op);
+
+ return ret;
+}
+
+static int ipsec_out_prepare_packet(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_out_opt_t *opt, odp_bool_t is_enqueue_op,
+ odp_crypto_packet_op_param_t *param,
+ odp_ipsec_op_status_t *status)
+{
+ return ipsec_out_parse_encap_packet(pkt, state, ipsec_sa, opt, status) ||
+ ipsec_out_prepare_op(pkt, state, ipsec_sa, opt, is_enqueue_op, param, status);
+}
+
+static int ipsec_out_finalize_packet(odp_packet_t *pkt, ipsec_state_t *state, ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status)
+{
+ int (*op)(ipsec_state_t *state, odp_packet_t *pkt, ipsec_sa_t *ipsec_sa);
+
+ op = ODP_IPSEC_ESP == ipsec_sa->proto ? ipsec_out_esp_post :
+ ODP_IPSEC_AH == ipsec_sa->proto ? ipsec_out_ah_post : NULL;
+
+ if (odp_unlikely(op && op(state, pkt, ipsec_sa))) {
+ status->error.alg = 1;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ipsec_in_prepare(const odp_packet_t pkt_in[], odp_packet_t pkt_out[], int num_in,
+ const odp_ipsec_in_param_t *param, ipsec_op_t ops[],
+ odp_packet_t crypto_pkts[],
+ odp_crypto_packet_op_param_t crypto_param[], ipsec_op_t *crypto_ops[],
+ int *num_crypto)
+{
+ unsigned int sa_idx = 0, sa_inc = (param->num_sa > 1) ? 1 : 0;
+
+ *num_crypto = 0;
+
+ for (int i = 0; i < num_in; i++) {
+ pkt_out[i] = pkt_in[i];
+ ipsec_op_t *op = &ops[i];
+ odp_packet_t *pkt = &pkt_out[i];
+ odp_crypto_packet_op_param_t c_p;
+
+ memset(op, 0, sizeof(*op));
+
+ if (0 == param->num_sa) {
+ op->sa_hdl = ODP_IPSEC_SA_INVALID;
+ } else {
+ op->sa_hdl = param->sa[sa_idx];
+ _ODP_ASSERT(ODP_IPSEC_SA_INVALID != op->sa_hdl);
+ }
+
+ sa_idx += sa_inc;
+
+ if (odp_likely(ipsec_in_prepare_packet(pkt, &op->state, &op->sa, op->sa_hdl, &c_p,
+ &op->status, &op->orig_ip_len) == 0)) {
+ crypto_pkts[*num_crypto] = *pkt;
+ crypto_param[*num_crypto] = c_p;
+ crypto_ops[*num_crypto] = op;
+ (*num_crypto)++;
+ }
+ }
+}
+
+static void ipsec_do_crypto_burst(odp_packet_t pkts[], odp_crypto_packet_op_param_t param[],
+ ipsec_op_t *ops[], int num)
+{
+ int num_procd = 0;
+
+ while (num_procd < num) {
+ int ret = odp_crypto_op(&pkts[num_procd], &pkts[num_procd], &param[num_procd],
+ num - num_procd);
+
+ if (odp_unlikely(ret <= 0))
+ break;
+
+ num_procd += ret;
+ }
+
+ for (int i = num_procd; i < num; i++)
+ ops[i]->status.error.alg = 1;
+}
+
+static int ipsec_in_check_crypto_result(odp_packet_t pkt, odp_ipsec_op_status_t *status)
+{
+ odp_crypto_packet_result_t result;
+ int rc = odp_crypto_result(&result, pkt);
+
+ if (odp_likely(rc == 0))
+ return 0;
+
+ if (odp_unlikely(rc < -1)) {
+ _ODP_DBG("Crypto failed\n");
+ status->error.alg = 1;
+ return -1;
+ }
+
+ if (result.cipher_status.alg_err == ODP_CRYPTO_ALG_ERR_ICV_CHECK ||
+ result.auth_status.alg_err == ODP_CRYPTO_ALG_ERR_ICV_CHECK)
+ status->error.auth = 1;
+ else
+ status->error.alg = 1;
+
+ return -1;
+}
+
+static inline void update_post_lifetime_stats(ipsec_sa_t *sa, ipsec_state_t *state)
+{
+ if (ipsec_config->stats_en) {
+ odp_atomic_inc_u64(&sa->stats.post_lifetime_err_pkts);
+ odp_atomic_add_u64(&sa->stats.post_lifetime_err_bytes, state->stats_length);
+ }
+}
+
+static inline void finish_packet_proc(odp_packet_t pkt, ipsec_op_t *op, odp_queue_t queue)
+{
+ odp_ipsec_packet_result_t *res;
+
+ if (ipsec_config->stats_en)
+ ipsec_sa_err_stats_update(op->sa, &op->status);
+
+ packet_subtype_set(pkt, ODP_EVENT_PACKET_IPSEC);
+ res = ipsec_pkt_result(pkt);
+ memset(res, 0, sizeof(*res));
+ res->status = op->status;
+ res->sa = NULL != op->sa ? op->sa->ipsec_sa_hdl : ODP_IPSEC_SA_INVALID;
+ /* We need to decrease SA use count only if the SA was not provided to us by the caller but
+ * was found through our own SA lookup that increased the use count. */
+ if (op->sa_hdl == ODP_IPSEC_SA_INVALID && op->sa)
+ _odp_ipsec_sa_unuse(op->sa);
+
+ if (queue != ODP_QUEUE_INVALID) {
+ res->orig_ip_len = op->orig_ip_len;
+ /* What should be done if enqueue fails? */
+ if (odp_unlikely(odp_queue_enq(queue, odp_ipsec_packet_to_event(pkt)) < 0))
+ odp_packet_free(pkt);
+ }
+}
+
+static void ipsec_in_finalize(odp_packet_t pkt_in[], ipsec_op_t ops[], int num, odp_bool_t is_enq)
+{
+ for (int i = 0; i < num; i++) {
+ ipsec_op_t *op = &ops[i];
+ odp_packet_t *pkt = &pkt_in[i];
+ odp_queue_t q = ODP_QUEUE_INVALID;
+
+ if (odp_unlikely(op->status.error.all))
+ goto finish;
+
+ if (odp_unlikely(ipsec_in_check_crypto_result(*pkt, &op->status)))
+ goto finish;
+
+ if (op->sa->antireplay) {
+ if (is_enq)
+ wait_for_order(ipsec_global->inbound_ordering_mode);
+
+ if (odp_unlikely(_odp_ipsec_sa_replay_update(op->sa, op->state.in.seq_no,
+ &op->status) < 0))
+ goto finish;
+ }
+
+ if (odp_unlikely(ipsec_in_finalize_packet(pkt, &op->state, op->sa,
+ &op->status))) {
+ update_post_lifetime_stats(op->sa, &op->state);
+ goto finish;
+ }
+
+ ipsec_in_parse_decap_packet(*pkt, &op->state, op->sa);
+
+finish:
+ if (is_enq)
+ q = NULL != op->sa ? op->sa->queue : ipsec_config->inbound.default_queue;
+
+ finish_packet_proc(*pkt, op, q);
+ }
+}
+
+int odp_ipsec_in(const odp_packet_t pkt_in[], int num_in, odp_packet_t pkt_out[], int *num_out,
+ const odp_ipsec_in_param_t *param)
+{
+ int max_out = _ODP_MIN3(num_in, *num_out, MAX_BURST), num_crypto;
+ odp_packet_t crypto_pkts[MAX_BURST];
+ odp_crypto_packet_op_param_t crypto_param[MAX_BURST];
+ ipsec_op_t ops[MAX_BURST], *crypto_ops[MAX_BURST];
+
+ ipsec_in_prepare(pkt_in, pkt_out, max_out, param, ops, crypto_pkts, crypto_param,
+ crypto_ops, &num_crypto);
+ ipsec_do_crypto_burst(crypto_pkts, crypto_param, crypto_ops, num_crypto);
+ ipsec_in_finalize(pkt_out, ops, max_out, false);
+ *num_out = max_out;
+
+ return max_out;
+}
+
+static odp_ipsec_out_opt_t default_out_opt;
+
+static void ipsec_out_prepare(const odp_packet_t pkt_in[], odp_packet_t pkt_out[], int num_in,
+ const odp_ipsec_out_param_t *param, ipsec_op_t ops[],
+ odp_packet_t crypto_pkts[],
+ odp_crypto_packet_op_param_t crypto_param[],
+ ipsec_op_t *crypto_ops[], int *num_crypto, odp_bool_t is_enq)
+{
+ unsigned int sa_idx = 0, opt_idx = 0, sa_inc = (param->num_sa > 1) ? 1 : 0,
+ opt_inc = (param->num_opt > 1) ? 1 : 0;
+ /* No need to do _odp_ipsec_sa_use() here since an ODP application is not allowed to do
+ * call IPsec output before SA creation has completed nor call odp_ipsec_sa_disable()
+ * before IPsec output has completed. IOW, the needed synchronization between threads is
+ * done by the application. */
+ *num_crypto = 0;
+
+ for (int i = 0; i < num_in; i++) {
+ pkt_out[i] = pkt_in[i];
+ ipsec_op_t *op = &ops[i];
+ const odp_ipsec_out_opt_t *opt;
+ odp_packet_t *pkt = &pkt_out[i];
+ odp_crypto_packet_op_param_t c_p;
+
+ memset(op, 0, sizeof(*op));
+ op->sa_hdl = param->sa[sa_idx];
+ _ODP_ASSERT(ODP_IPSEC_SA_INVALID != op->sa_hdl);
+ op->sa = _odp_ipsec_sa_entry_from_hdl(op->sa_hdl);
+ _ODP_ASSERT(NULL != op->sa);
+
+ if (0 == param->num_opt)
+ opt = &default_out_opt;
+ else
+ opt = &param->opt[opt_idx];
+
+ sa_idx += sa_inc;
+ opt_idx += opt_inc;
+
+ if (odp_unlikely(ipsec_out_prepare_packet(pkt, &op->state, op->sa, opt, is_enq,
+ &c_p, &op->status)))
+ continue;
+
+ if (odp_unlikely(_odp_ipsec_sa_lifetime_update(op->sa, op->state.stats_length,
+ &op->status))) {
+ update_post_lifetime_stats(op->sa, &op->state);
+ continue;
+ }
+
+ crypto_pkts[*num_crypto] = *pkt;
+ crypto_param[*num_crypto] = c_p;
+ crypto_ops[*num_crypto] = op;
+ (*num_crypto)++;
+ }
+}
+
+static int ipsec_out_check_crypto_result(odp_packet_t pkt, odp_ipsec_op_status_t *status)
+{
+ if (odp_unlikely(odp_crypto_result(NULL, pkt) != 0)) {
+ _ODP_DBG("Crypto failed\n");
+ status->error.alg = 1;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ipsec_out_finalize(odp_packet_t pkt_in[], ipsec_op_t ops[], int num, odp_bool_t is_enq)
+{
+ for (int i = 0; i < num; i++) {
+ ipsec_op_t *op = &ops[i];
+ odp_packet_t *pkt = &pkt_in[i];
+ odp_queue_t q = ODP_QUEUE_INVALID;
+
+ if (odp_unlikely(op->status.error.all))
+ goto finish;
+
+ if (odp_unlikely(ipsec_out_check_crypto_result(*pkt, &op->status))) {
+ update_post_lifetime_stats(op->sa, &op->state);
+ goto finish;
+ }
+
+ if (odp_unlikely(ipsec_out_finalize_packet(pkt, &op->state, op->sa, &op->status)))
+ update_post_lifetime_stats(op->sa, &op->state);
+
+finish:
+ if (is_enq)
+ q = NULL != op->sa ? op->sa->queue : ipsec_config->inbound.default_queue;
+
+ finish_packet_proc(*pkt, op, q);
+ }
+}
+
+int odp_ipsec_out(const odp_packet_t pkt_in[], int num_in, odp_packet_t pkt_out[], int *num_out,
+ const odp_ipsec_out_param_t *param)
+{
+ int max_out = _ODP_MIN3(num_in, *num_out, MAX_BURST), num_crypto;
+ odp_packet_t crypto_pkts[MAX_BURST];
+ odp_crypto_packet_op_param_t crypto_param[MAX_BURST];
+ ipsec_op_t ops[MAX_BURST], *crypto_ops[MAX_BURST];
+
+ ipsec_out_prepare(pkt_in, pkt_out, max_out, param, ops, crypto_pkts, crypto_param,
+ crypto_ops, &num_crypto, false);
+ ipsec_do_crypto_burst(crypto_pkts, crypto_param, crypto_ops, num_crypto);
+ ipsec_out_finalize(pkt_out, ops, max_out, false);
+ *num_out = max_out;
+
+ return max_out;
+}
+
+/* Do not change to an asynchronous design without thinking concurrency and what changes are
+ * required to guarantee that used SAs are not destroyed when asynchronous operations are in
+ * progress.
+ *
+ * The containing code does not hold a reference to the SA but completes processing synchronously
+ * and makes use of the fact that the application may not disable (and then destroy) the SA before
+ * these routines return (and all side effects are visible to the disabling thread). */
+int odp_ipsec_in_enq(const odp_packet_t pkt_in[], int num_in, const odp_ipsec_in_param_t *param)
+{
+ int max_out = _ODP_MIN(num_in, MAX_BURST), num_crypto;
+ odp_packet_t pkt_out[MAX_BURST], crypto_pkts[MAX_BURST];
+ odp_crypto_packet_op_param_t crypto_param[MAX_BURST];
+ ipsec_op_t ops[MAX_BURST], *crypto_ops[MAX_BURST];
+
+ ipsec_in_prepare(pkt_in, pkt_out, max_out, param, ops, crypto_pkts, crypto_param,
+ crypto_ops, &num_crypto);
+ ipsec_do_crypto_burst(crypto_pkts, crypto_param, crypto_ops, num_crypto);
+ ipsec_in_finalize(pkt_out, ops, max_out, true);
+
+ return max_out;
+}
+
+int odp_ipsec_out_enq(const odp_packet_t pkt_in[], int num_in, const odp_ipsec_out_param_t *param)
+{
+ int max_out = _ODP_MIN(num_in, MAX_BURST), num_crypto;
+ odp_packet_t pkt_out[MAX_BURST], crypto_pkts[MAX_BURST];
+ odp_crypto_packet_op_param_t crypto_param[MAX_BURST];
+ ipsec_op_t ops[MAX_BURST], *crypto_ops[MAX_BURST];
+
+ ipsec_out_prepare(pkt_in, pkt_out, max_out, param, ops, crypto_pkts, crypto_param,
+ crypto_ops, &num_crypto, true);
+ ipsec_do_crypto_burst(crypto_pkts, crypto_param, crypto_ops, num_crypto);
+ ipsec_out_finalize(pkt_out, ops, max_out, true);
+
+ return max_out;
+}
+
+int _odp_ipsec_try_inline(odp_packet_t *pkt)
+{
+ odp_ipsec_op_status_t status;
+ ipsec_sa_t *ipsec_sa;
+ uint32_t orig_ip_len = 0;
+ odp_ipsec_packet_result_t *result;
+ odp_packet_hdr_t *pkt_hdr;
+
+ if (odp_global_ro.disable.ipsec)
+ return -1;
+
+ memset(&status, 0, sizeof(status));
+
+ ipsec_sa = ipsec_in_single(*pkt, ODP_IPSEC_SA_INVALID, pkt, false,
+ &status, &orig_ip_len);
+ /*
+ * Route packet back in case of lookup failure or early error before
+ * lookup
+ */
+ if (NULL == ipsec_sa)
+ return -1;
+
+ packet_subtype_set(*pkt, ODP_EVENT_PACKET_IPSEC);
+ result = ipsec_pkt_result(*pkt);
+ memset(result, 0, sizeof(*result));
+ result->status = status;
+ result->orig_ip_len = orig_ip_len;
+ result->sa = ipsec_sa->ipsec_sa_hdl;
+ result->flag.inline_mode = 1;
+
+ pkt_hdr = packet_hdr(*pkt);
+ pkt_hdr->p.input_flags.dst_queue = 1;
+ pkt_hdr->dst_queue = ipsec_sa->queue;
+ /* Distinguish inline IPsec packets from classifier packets */
+ pkt_hdr->cos = CLS_COS_IDX_NONE;
+
+ /* Last thing */
+ _odp_ipsec_sa_unuse(ipsec_sa);
+
+ return 0;
+}
+
+static inline int ipsec_out_inline_check_out_hdrs(odp_packet_t pkt,
+ const odp_ipsec_out_inline_param_t *param,
+ ipsec_inline_op_t *op)
+{
+ uint32_t l2_offset, hdr_len = param->outer_hdr.len;
+
+ if (!param->outer_hdr.ptr) {
+ l2_offset = odp_packet_l2_offset(pkt);
+ _ODP_ASSERT(hdr_len == odp_packet_l3_offset(pkt) - l2_offset);
+
+ if (odp_unlikely(hdr_len > MAX_HDR_LEN ||
+ odp_packet_copy_to_mem(pkt, l2_offset, hdr_len, op->hdr_buf)
+ < 0)) {
+ op->op.status.error.proto = 1;
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void ipsec_out_inline_prepare(const odp_packet_t pkt_in[], odp_packet_t pkt_out[],
+ int num_in, const odp_ipsec_out_param_t *param,
+ const odp_ipsec_out_inline_param_t *inline_param,
+ ipsec_inline_op_t ops[], odp_packet_t crypto_pkts[],
+ odp_crypto_packet_op_param_t crypto_param[],
+ ipsec_op_t *crypto_ops[], int *num_crypto)
+{
+ unsigned int sa_idx = 0, opt_idx = 0, sa_inc = (param->num_sa > 1) ? 1 : 0,
+ opt_inc = (param->num_opt > 1) ? 1 : 0;
+
+ *num_crypto = 0;
+
+ for (int i = 0; i < num_in; i++) {
+ pkt_out[i] = pkt_in[i];
+ ipsec_inline_op_t *op = &ops[i];
+ const odp_ipsec_out_opt_t *opt;
+ odp_packet_t *pkt = &pkt_out[i];
+ odp_crypto_packet_op_param_t c_p;
+
+ memset(op, 0, sizeof(*op));
+ op->op.sa_hdl = param->sa[sa_idx];
+ _ODP_ASSERT(ODP_IPSEC_SA_INVALID != op->op.sa_hdl);
+ op->op.sa = _odp_ipsec_sa_entry_from_hdl(op->op.sa_hdl);
+ _ODP_ASSERT(NULL != op->op.sa);
+
+ if (0 == param->num_opt)
+ opt = &default_out_opt;
+ else
+ opt = &param->opt[opt_idx];
+
+ sa_idx += sa_inc;
+ opt_idx += opt_inc;
+
+ if (odp_unlikely(ipsec_out_inline_check_out_hdrs(*pkt, &inline_param[i], op) ||
+ ipsec_out_prepare_packet(pkt, &op->op.state, op->op.sa, opt,
+ true, &c_p, &op->op.status)))
+ continue;
+
+ if (odp_unlikely(_odp_ipsec_sa_lifetime_update(op->op.sa,
+ op->op.state.stats_length,
+ &op->op.status))) {
+ update_post_lifetime_stats(op->op.sa, &op->op.state);
+ continue;
+ }
+
+ crypto_pkts[*num_crypto] = *pkt;
+ crypto_param[*num_crypto] = c_p;
+ crypto_ops[*num_crypto] = &op->op;
+ (*num_crypto)++;
+ }
+}
+
+static void ipsec_out_inline_finish_packet_proc(odp_packet_t *pkt,
+ const odp_ipsec_out_inline_param_t *param,
+ ipsec_inline_op_t *op)
+{
+ uint32_t offset = odp_packet_l3_offset(*pkt), hdr_len = param->outer_hdr.len;
+ odp_pktout_queue_t pkqueue;
+
+ _ODP_ASSERT(NULL != op->op.sa);
+
+ if (odp_unlikely(offset == ODP_PACKET_OFFSET_INVALID))
+ offset = 0;
+
+ if (offset >= hdr_len) {
+ if (odp_packet_trunc_head(pkt, offset - hdr_len, NULL, NULL) < 0)
+ op->op.status.error.alg = 1;
+ } else {
+ if (odp_packet_extend_head(pkt, hdr_len - offset, NULL, NULL) < 0)
+ op->op.status.error.alg = 1;
+ }
+
+ odp_packet_l3_offset_set(*pkt, hdr_len);
+
+ if (odp_packet_copy_from_mem(*pkt, 0, hdr_len,
+ param->outer_hdr.ptr ? param->outer_hdr.ptr : op->hdr_buf)
+ < 0)
+ op->op.status.error.alg = 1;
+
+ if (!op->op.status.error.all) {
+ if (odp_pktout_queue(param->pktio, &pkqueue, 1) <= 0)
+ op->op.status.error.alg = 1;
+
+ if (odp_pktout_send(pkqueue, pkt, 1) < 0)
+ op->op.status.error.alg = 1;
+ }
+}
+
+static void ipsec_out_inline_handle_err(odp_packet_t pkt, ipsec_inline_op_t *op)
+{
+ odp_ipsec_packet_result_t *res;
+
+ if (odp_likely(!op->op.status.error.all))
+ return;
+
+ if (ipsec_config->stats_en)
+ ipsec_sa_err_stats_update(op->op.sa, &op->op.status);
+
+ packet_subtype_set(pkt, ODP_EVENT_PACKET_IPSEC);
+ res = ipsec_pkt_result(pkt);
+ memset(res, 0, sizeof(*res));
+ res->sa = op->op.sa_hdl;
+ res->status = op->op.status;
+
+ if (odp_unlikely(odp_queue_enq(op->op.sa->queue, odp_ipsec_packet_to_event(pkt)) < 0))
+ odp_packet_free(pkt);
+}
+
+static void ipsec_out_inline_finalize(odp_packet_t pkt_in[],
+ const odp_ipsec_out_inline_param_t *inline_param,
+ ipsec_inline_op_t ops[], int num)
+{
+ for (int i = 0; i < num; i++) {
+ ipsec_inline_op_t *op = &ops[i];
+ odp_packet_t *pkt = &pkt_in[i];
+
+ if (op->op.status.warn.soft_exp_packets || op->op.status.warn.soft_exp_bytes) {
+ if (!odp_atomic_load_u32(&op->op.sa->soft_expiry_notified)) {
+ int rc;
+
+ /*
+ * Another thread may have sent the notification by now but we do
+ * not care since sending duplicate expiry notifications is allowed.
+ */
+ rc = _odp_ipsec_status_send(op->op.sa->queue,
+ ODP_IPSEC_STATUS_WARN,
+ op->op.sa->ipsec_sa_hdl,
+ 0, op->op.status.warn);
+ if (rc == 0)
+ odp_atomic_store_u32(&op->op.sa->soft_expiry_notified, 1);
+ else
+ _ODP_DBG("IPsec status event submission failed\n");
+ }
+ }
+
+ if (odp_unlikely(op->op.status.error.all))
+ goto handle_err;
+
+ if (odp_unlikely(ipsec_out_check_crypto_result(*pkt, &op->op.status))) {
+ update_post_lifetime_stats(op->op.sa, &op->op.state);
+ goto finish;
+ }
+
+ if (odp_unlikely(ipsec_out_finalize_packet(pkt, &op->op.state, op->op.sa,
+ &op->op.status)))
+ update_post_lifetime_stats(op->op.sa, &op->op.state);
+
+finish:
+ ipsec_out_inline_finish_packet_proc(pkt, &inline_param[i], op);
+
+handle_err:
+ ipsec_out_inline_handle_err(*pkt, op);
+ }
+}
+
+int odp_ipsec_out_inline(const odp_packet_t pkt_in[], int num_in,
+ const odp_ipsec_out_param_t *param,
+ const odp_ipsec_out_inline_param_t *inline_param)
+{
+ int max_out = _ODP_MIN(num_in, MAX_BURST), num_crypto;
+ odp_packet_t pkt_out[MAX_BURST], crypto_pkts[MAX_BURST];
+ odp_crypto_packet_op_param_t crypto_param[MAX_BURST];
+ ipsec_inline_op_t ops[MAX_BURST];
+ ipsec_op_t *crypto_ops[MAX_BURST];
+
+ ipsec_out_inline_prepare(pkt_in, pkt_out, max_out, param, inline_param, ops, crypto_pkts,
+ crypto_param, crypto_ops, &num_crypto);
+ ipsec_do_crypto_burst(crypto_pkts, crypto_param, crypto_ops, num_crypto);
+ ipsec_out_inline_finalize(pkt_out, inline_param, ops, max_out);
+
+ return max_out;
+}
+
+int odp_ipsec_test_sa_update(odp_ipsec_sa_t sa,
+ odp_ipsec_test_sa_operation_t sa_op,
+ const odp_ipsec_test_sa_param_t *sa_param)
+{
+ ipsec_sa_t *ipsec_sa;
+
+ ipsec_sa = _odp_ipsec_sa_entry_from_hdl(sa);
+ _ODP_ASSERT(NULL != ipsec_sa);
+
+ switch (sa_op) {
+ case ODP_IPSEC_TEST_SA_UPDATE_SEQ_NUM:
+ odp_atomic_store_u64(&ipsec_sa->hot.out.seq, sa_param->seq_num);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int odp_ipsec_stats(odp_ipsec_sa_t sa, odp_ipsec_stats_t *stats)
+{
+ ipsec_sa_t *ipsec_sa;
+
+ if (ODP_IPSEC_SA_INVALID == sa)
+ return -EINVAL;
+
+ if (!ipsec_config->stats_en)
+ return -ENOTSUP;
+
+ _ODP_ASSERT(NULL != stats);
+
+ ipsec_sa = _odp_ipsec_sa_entry_from_hdl(sa);
+ _ODP_ASSERT(NULL != ipsec_sa);
+
+ _odp_ipsec_sa_stats_pkts(ipsec_sa, stats);
+ stats->proto_err = odp_atomic_load_u64(&ipsec_sa->stats.proto_err);
+ stats->auth_err = odp_atomic_load_u64(&ipsec_sa->stats.auth_err);
+ stats->antireplay_err = odp_atomic_load_u64(&ipsec_sa->stats.antireplay_err);
+ stats->alg_err = odp_atomic_load_u64(&ipsec_sa->stats.alg_err);
+ stats->mtu_err = odp_atomic_load_u64(&ipsec_sa->stats.mtu_err);
+ stats->hard_exp_bytes_err = odp_atomic_load_u64(&ipsec_sa->stats.hard_exp_bytes_err);
+ stats->hard_exp_pkts_err = odp_atomic_load_u64(&ipsec_sa->stats.hard_exp_pkts_err);
+
+ return 0;
+}
+
+int odp_ipsec_stats_multi(odp_ipsec_sa_t sa[], odp_ipsec_stats_t stats[], int num)
+{
+ int ret, i;
+
+ _ODP_ASSERT(NULL != stats);
+
+ for (i = 0; i < num; i++) {
+ ret = odp_ipsec_stats(sa[i], &stats[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int read_config_file(ipsec_global_t *global)
+{
+ const char *str_i = "ipsec.ordering.async_inbound";
+ const char *str_o = "ipsec.ordering.async_outbound";
+ int val;
+
+ if (!_odp_libconfig_lookup_int(str_i, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str_i);
+ return -1;
+ }
+ global->inbound_ordering_mode = val;
+
+ if (!_odp_libconfig_lookup_int(str_o, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str_o);
+ return -1;
+ }
+ global->outbound_ordering_mode = val;
+
+ return 0;
+}
+
+int _odp_ipsec_init_global(void)
+{
+ odp_shm_t shm;
+
+ if (odp_global_ro.disable.ipsec)
+ return 0;
+
+ shm = odp_shm_reserve("_odp_ipsec_global", sizeof(*ipsec_global),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ _ODP_ERR("Shm reserve failed for odp_ipsec\n");
+ return -1;
+ }
+ ipsec_global = odp_shm_addr(shm);
+ if (ipsec_global == NULL) {
+ _ODP_ERR("ipsec: odp_shm_addr() failed\n");
+ odp_shm_free(shm);
+ return -1;
+ }
+ memset(ipsec_global, 0, sizeof(*ipsec_global));
+ ipsec_config = &ipsec_global->ipsec_config;
+
+ if (read_config_file(ipsec_global)) {
+ odp_shm_free(shm);
+ return -1;
+ }
+
+ memset(&default_out_opt, 0, sizeof(default_out_opt));
+
+ return 0;
+}
+
+int _odp_ipsec_term_global(void)
+{
+ odp_shm_t shm;
+
+ if (odp_global_ro.disable.ipsec)
+ return 0;
+
+ shm = odp_shm_lookup("_odp_ipsec_global");
+
+ if (shm == ODP_SHM_INVALID || odp_shm_free(shm)) {
+ _ODP_ERR("Shm free failed for odp_ipsec");
+ return -1;
+ }
+
+ return 0;
+}
+
+void odp_ipsec_print(void)
+{
+ _ODP_PRINT("\nIPSEC print\n");
+ _ODP_PRINT("-----------\n");
+ _ODP_PRINT(" max number of SA %u\n\n", ipsec_config->max_num_sa);
+}
+
+void odp_ipsec_sa_print(odp_ipsec_sa_t sa)
+{
+ ipsec_sa_t *ipsec_sa = _odp_ipsec_sa_entry_from_hdl(sa);
+
+ _ODP_PRINT("\nIPSEC SA print\n");
+ _ODP_PRINT("--------------\n");
+ _ODP_PRINT(" SPI %u\n\n", ipsec_sa->spi);
+}
diff --git a/platform/linux-generic/odp_ipsec_api.c b/platform/linux-generic/odp_ipsec_api.c
new file mode 100644
index 000000000..b92cc9225
--- /dev/null
+++ b/platform/linux-generic/odp_ipsec_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/ipsec.h>
+
+/* Non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/ipsec_inlines.h>
diff --git a/platform/linux-generic/odp_ipsec_events.c b/platform/linux-generic/odp_ipsec_events.c
new file mode 100644
index 000000000..c88089d98
--- /dev/null
+++ b/platform/linux-generic/odp_ipsec_events.c
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/ipsec.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_init_internal.h>
+#include <odp_buffer_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_global_data.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+typedef struct {
+ /* common buffer header */
+ odp_buffer_hdr_t buf_hdr;
+
+ odp_ipsec_status_t status;
+} ipsec_status_hdr_t;
+#pragma GCC diagnostic pop
+
+static odp_pool_t ipsec_status_pool = ODP_POOL_INVALID;
+
+#define IPSEC_EVENTS_POOL_BUF_COUNT 1024
+
+int _odp_ipsec_events_init_global(void)
+{
+ odp_pool_param_t param;
+
+ if (odp_global_ro.disable.ipsec) {
+ _ODP_PRINT("\nODP IPSec is DISABLED\n");
+ return 0;
+ }
+
+ odp_pool_param_init(&param);
+
+ param.buf.size = sizeof(ipsec_status_hdr_t);
+ param.buf.align = 0;
+ param.buf.num = IPSEC_EVENTS_POOL_BUF_COUNT;
+ param.type = ODP_POOL_BUFFER;
+
+ ipsec_status_pool = odp_pool_create("_odp_ipsec_status_pool", &param);
+ if (ODP_POOL_INVALID == ipsec_status_pool) {
+ _ODP_ERR("Error: status pool create failed.\n");
+ goto err_status;
+ }
+
+ return 0;
+
+err_status:
+ return -1;
+}
+
+int _odp_ipsec_events_term_global(void)
+{
+ int ret;
+
+ if (odp_global_ro.disable.ipsec)
+ return 0;
+
+ ret = odp_pool_destroy(ipsec_status_pool);
+ if (ret < 0) {
+ _ODP_ERR("status pool destroy failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+ipsec_status_t _odp_ipsec_status_from_event(odp_event_t ev)
+{
+ _ODP_ASSERT(ODP_EVENT_INVALID != ev);
+ _ODP_ASSERT(ODP_EVENT_IPSEC_STATUS == odp_event_type(ev));
+
+ return (ipsec_status_t)ev;
+}
+
+static odp_event_t ipsec_status_to_event(ipsec_status_t status)
+{
+ _ODP_ASSERT(ODP_IPSEC_STATUS_INVALID != status);
+
+ return (odp_event_t)status;
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr_from_buf(odp_buffer_t buf)
+{
+ return (ipsec_status_hdr_t *)(void *)_odp_buf_hdr(buf);
+}
+
+static inline odp_buffer_t buffer_from_event(odp_event_t ev)
+{
+ return (odp_buffer_t)ev;
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr(ipsec_status_t status)
+{
+ odp_buffer_t buf = buffer_from_event(ipsec_status_to_event(status));
+
+ return ipsec_status_hdr_from_buf(buf);
+}
+
+static ipsec_status_t odp_ipsec_status_alloc(void)
+{
+ odp_buffer_t buf = odp_buffer_alloc(ipsec_status_pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+ return ODP_IPSEC_STATUS_INVALID;
+
+ _odp_event_type_set(odp_buffer_to_event(buf), ODP_EVENT_IPSEC_STATUS);
+
+ return _odp_ipsec_status_from_event(odp_buffer_to_event(buf));
+}
+
+void _odp_ipsec_status_free(ipsec_status_t status)
+{
+ odp_event_t ev = ipsec_status_to_event(status);
+
+ _odp_event_type_set(ev, ODP_EVENT_BUFFER);
+ odp_buffer_free(buffer_from_event(ev));
+}
+
+int _odp_ipsec_status_send(odp_queue_t queue,
+ odp_ipsec_status_id_t id,
+ odp_ipsec_sa_t sa,
+ int result,
+ odp_ipsec_warn_t warn)
+{
+ ipsec_status_t ipsec_ev = odp_ipsec_status_alloc();
+ ipsec_status_hdr_t *status_hdr;
+
+ if (ODP_IPSEC_STATUS_INVALID == ipsec_ev)
+ return -1;
+
+ status_hdr = ipsec_status_hdr(ipsec_ev);
+
+ status_hdr->status.id = id;
+ status_hdr->status.sa = sa;
+ status_hdr->status.result = result;
+ status_hdr->status.warn = warn;
+
+ if (odp_queue_enq(queue, ipsec_status_to_event(ipsec_ev))) {
+ _odp_ipsec_status_free(ipsec_ev);
+ return -1;
+ }
+
+ return 0;
+}
+
+int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event)
+{
+ ipsec_status_t ipsec_ev;
+ ipsec_status_hdr_t *status_hdr;
+
+ if (odp_unlikely(ODP_EVENT_INVALID == event))
+ return -1;
+
+ ipsec_ev = _odp_ipsec_status_from_event(event);
+ if (odp_unlikely(ODP_IPSEC_STATUS_INVALID == ipsec_ev))
+ return -1;
+
+ status_hdr = ipsec_status_hdr(ipsec_ev);
+
+ *status = status_hdr->status;
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c
new file mode 100644
index 000000000..34320522b
--- /dev/null
+++ b/platform/linux-generic/odp_ipsec_sad.c
@@ -0,0 +1,1305 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2018-2023 Nokia
+ */
+
+#include <odp/api/atomic.h>
+#include <odp/api/crypto.h>
+#include <odp/api/ipsec.h>
+#include <odp/api/random.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/plat/cpu_inlines.h>
+
+#include <odp_config_internal.h>
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ring_mpmc_u32_internal.h>
+#include <odp_global_data.h>
+
+#include <string.h>
+#include <inttypes.h>
+
+/*
+ * SA state consists of state value in the high order bits of ipsec_sa_t::state
+ * and use counter in the low order bits.
+ *
+ * An SA cannot be destroyed if its use count is higher than one. Use counter
+ * is needed for the case SA lookup is done by us and not the application.
+ * In the latter case we rely on the fact that the application may not pass
+ * the SA as a parameter to an IPsec operation concurrently with a call
+ * to odp_ipsec_sa_disable().
+ *
+ * SAs that are free or being disabled cannot be found in SA lookup by ODP.
+ */
+#define IPSEC_SA_STATE_ACTIVE 0x00000000 /* SA is in use */
+#define IPSEC_SA_STATE_DISABLE 0x40000000 /* SA is being disabled */
+#define IPSEC_SA_STATE_FREE 0xc0000000 /* SA is unused and free */
+#define IPSEC_SA_STATE_MASK 0xc0000000 /* mask of state bits */
+
+#define SA_IDX_NONE UINT32_MAX
+
+/*
+ * We do not have global IPv4 ID counter that is accessed for every outbound
+ * packet. Instead, we split IPv4 ID space to fixed size blocks that we
+ * allocate to threads on demand. When a thread has used its block of IDs,
+ * it frees it and allocates a new block. Free blocks are kept in a ring so
+ * that the block last freed is the one to be allocated last to maximize
+ * the time before IPv4 ID reuse.
+ */
+#define IPV4_ID_BLOCK_SIZE 64 /* must be power of 2 */
+#define IPV4_ID_RING_SIZE (UINT16_MAX / IPV4_ID_BLOCK_SIZE)
+#define IPV4_ID_RING_MASK (IPV4_ID_RING_SIZE - 1)
+
+#if IPV4_ID_RING_SIZE <= ODP_THREAD_COUNT_MAX
+#warning IPV4_ID_RING_SIZE is too small for the maximum number of threads.
+#endif
+
+/*
+ * To avoid checking and updating the packet and byte counters in the
+ * SA for every packet, we increment the global counters once for several
+ * packets. We decrement a preallocated thread-local quota for every
+ * packet. When the quota runs out, we get a new quota by incementing the
+ * global counter.
+ *
+ * This improves performance but the looser synchronization between
+ * threads makes life time warnings and errors somewhat inaccurate.
+ * The warnings and errors may get triggered a bit too early since
+ * some threads may still have unused quota when the first thread
+ * hits the limit.
+ */
+#define SA_LIFE_PACKETS_PREALLOC 64
+#define SA_LIFE_BYTES_PREALLOC 4000
+
+typedef struct sa_thread_local_s {
+ /*
+ * Packets that can be processed in this thread before looking at
+ * the SA-global packet counter and checking hard and soft limits.
+ */
+ odp_atomic_u32_t packet_quota;
+ /*
+ * Bytes that can be processed in this thread before looking at
+ * the SA-global byte counter and checking hard and soft limits.
+ */
+ odp_atomic_u32_t byte_quota;
+ /*
+ * Life time status when this thread last checked the global
+ * counter(s).
+ */
+ odp_ipsec_op_status_t lifetime_status;
+} sa_thread_local_t;
+
+typedef struct ODP_ALIGNED_CACHE ipsec_thread_local_s {
+ sa_thread_local_t sa[CONFIG_IPSEC_MAX_NUM_SA];
+ uint16_t first_ipv4_id; /* first ID of current block of IDs */
+ uint16_t next_ipv4_id; /* next ID to be used */
+} ipsec_thread_local_t;
+
+typedef struct ipsec_sa_table_t {
+ ipsec_sa_t ipsec_sa[CONFIG_IPSEC_MAX_NUM_SA];
+ struct ODP_ALIGNED_CACHE {
+ ring_mpmc_u32_t ipv4_id_ring;
+ uint32_t ipv4_id_data[IPV4_ID_RING_SIZE] ODP_ALIGNED_CACHE;
+ } hot;
+ struct {
+ uint32_t head;
+ odp_spinlock_t lock;
+ } sa_freelist;
+ uint32_t max_num_sa;
+ odp_shm_t shm;
+ ipsec_thread_local_t per_thread[];
+} ipsec_sa_table_t;
+
+static ipsec_sa_table_t *ipsec_sa_tbl;
+
+static inline ipsec_sa_t *ipsec_sa_entry(uint32_t ipsec_sa_idx)
+{
+ return &ipsec_sa_tbl->ipsec_sa[ipsec_sa_idx];
+}
+
+static inline ipsec_sa_t *ipsec_sa_entry_from_hdl(odp_ipsec_sa_t ipsec_sa_hdl)
+{
+ return ipsec_sa_entry(_odp_typeval(ipsec_sa_hdl) - 1);
+}
+
+static inline odp_ipsec_sa_t ipsec_sa_index_to_handle(uint32_t ipsec_sa_idx)
+{
+ return _odp_cast_scalar(odp_ipsec_sa_t, ipsec_sa_idx + 1);
+}
+
+ipsec_sa_t *_odp_ipsec_sa_entry_from_hdl(odp_ipsec_sa_t sa)
+{
+ _ODP_ASSERT(ODP_IPSEC_SA_INVALID != sa);
+ return ipsec_sa_entry_from_hdl(sa);
+}
+
+static inline sa_thread_local_t *ipsec_sa_thread_local(ipsec_sa_t *sa)
+{
+ return &ipsec_sa_tbl->per_thread[odp_thread_id()].sa[sa->ipsec_sa_idx];
+}
+
+static void init_sa_thread_local(ipsec_sa_t *sa)
+{
+ sa_thread_local_t *sa_tl;
+ int n;
+ int thread_count_max = odp_thread_count_max();
+
+ for (n = 0; n < thread_count_max; n++) {
+ sa_tl = &ipsec_sa_tbl->per_thread[n].sa[sa->ipsec_sa_idx];
+ odp_atomic_init_u32(&sa_tl->packet_quota, 0);
+ odp_atomic_init_u32(&sa_tl->byte_quota, 0);
+ sa_tl->lifetime_status.all = 0;
+ }
+}
+
+int _odp_ipsec_sad_init_global(void)
+{
+ odp_crypto_capability_t crypto_capa;
+ uint32_t max_num_sa = CONFIG_IPSEC_MAX_NUM_SA;
+ uint64_t shm_size;
+ unsigned int thread_count_max = odp_thread_count_max();
+ odp_shm_t shm;
+ unsigned i;
+
+ if (odp_global_ro.disable.ipsec)
+ return 0;
+
+ crypto_capa.max_sessions = 0;
+
+ if (odp_crypto_capability(&crypto_capa)) {
+ _ODP_ERR("odp_crypto_capability() failed\n");
+ return -1;
+ }
+ if (max_num_sa > crypto_capa.max_sessions)
+ max_num_sa = crypto_capa.max_sessions;
+
+ shm_size = sizeof(ipsec_sa_table_t) +
+ sizeof(ipsec_thread_local_t) * thread_count_max;
+
+ shm = odp_shm_reserve("_odp_ipsec_sa_table",
+ shm_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ ipsec_sa_tbl = odp_shm_addr(shm);
+ memset(ipsec_sa_tbl, 0, sizeof(ipsec_sa_table_t));
+ ipsec_sa_tbl->shm = shm;
+ ipsec_sa_tbl->max_num_sa = max_num_sa;
+
+ ring_mpmc_u32_init(&ipsec_sa_tbl->hot.ipv4_id_ring);
+ for (i = 0; i < thread_count_max; i++) {
+ /*
+ * Make the current ID block fully used, forcing allocation
+ * of a fresh block at first use.
+ */
+ ipsec_sa_tbl->per_thread[i].first_ipv4_id = 0;
+ ipsec_sa_tbl->per_thread[i].next_ipv4_id = IPV4_ID_BLOCK_SIZE;
+ }
+ /*
+ * Initialize IPv4 ID ring with ID blocks.
+ *
+ * The last ID block is left unused since the ring can hold
+ * only IPV4_ID_RING_SIZE - 1 entries.
+ */
+ for (i = 0; i < IPV4_ID_RING_SIZE - 1; i++) {
+ uint32_t data = i * IPV4_ID_BLOCK_SIZE;
+
+ ring_mpmc_u32_enq_multi(&ipsec_sa_tbl->hot.ipv4_id_ring,
+ ipsec_sa_tbl->hot.ipv4_id_data,
+ IPV4_ID_RING_MASK,
+ &data,
+ 1);
+ }
+
+ for (i = 0; i < ipsec_sa_tbl->max_num_sa; i++) {
+ ipsec_sa_t *ipsec_sa = ipsec_sa_entry(i);
+
+ ipsec_sa->ipsec_sa_hdl = ipsec_sa_index_to_handle(i);
+ ipsec_sa->ipsec_sa_idx = i;
+ ipsec_sa->next_sa = i + 1;
+ if (i == ipsec_sa_tbl->max_num_sa - 1)
+ ipsec_sa->next_sa = SA_IDX_NONE;
+ odp_atomic_init_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE);
+ odp_atomic_init_u64(&ipsec_sa->hot.bytes, 0);
+ odp_atomic_init_u64(&ipsec_sa->hot.packets, 0);
+ }
+ ipsec_sa_tbl->sa_freelist.head = 0;
+ odp_spinlock_init(&ipsec_sa_tbl->sa_freelist.lock);
+
+ return 0;
+}
+
+int _odp_ipsec_sad_term_global(void)
+{
+ uint32_t i;
+ ipsec_sa_t *ipsec_sa;
+ int ret = 0;
+ int rc = 0;
+
+ if (odp_global_ro.disable.ipsec)
+ return 0;
+
+ for (i = 0; i < ipsec_sa_tbl->max_num_sa; i++) {
+ ipsec_sa = ipsec_sa_entry(i);
+
+ if (odp_atomic_load_u32(&ipsec_sa->state) !=
+ IPSEC_SA_STATE_FREE) {
+ _ODP_ERR("Not destroyed ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+ rc = -1;
+ }
+ odp_atomic_store_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE);
+ }
+
+ ret = odp_shm_free(ipsec_sa_tbl->shm);
+ if (ret < 0) {
+ _ODP_ERR("shm free failed");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+uint32_t _odp_ipsec_max_num_sa(void)
+{
+ return ipsec_sa_tbl->max_num_sa;
+}
+
+static ipsec_sa_t *ipsec_sa_reserve(void)
+{
+ ipsec_sa_t *ipsec_sa = NULL;
+ uint32_t sa_idx;
+
+ odp_spinlock_lock(&ipsec_sa_tbl->sa_freelist.lock);
+ sa_idx = ipsec_sa_tbl->sa_freelist.head;
+ if (sa_idx != SA_IDX_NONE) {
+ ipsec_sa = ipsec_sa_entry(sa_idx);
+ ipsec_sa_tbl->sa_freelist.head = ipsec_sa->next_sa;
+ }
+ odp_spinlock_unlock(&ipsec_sa_tbl->sa_freelist.lock);
+ return ipsec_sa;
+}
+
+static void ipsec_sa_release(ipsec_sa_t *ipsec_sa)
+{
+ odp_spinlock_lock(&ipsec_sa_tbl->sa_freelist.lock);
+ ipsec_sa->next_sa = ipsec_sa_tbl->sa_freelist.head;
+ ipsec_sa_tbl->sa_freelist.head = ipsec_sa->ipsec_sa_idx;
+ odp_atomic_store_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE);
+ odp_spinlock_unlock(&ipsec_sa_tbl->sa_freelist.lock);
+}
+
+/* Mark reserved SA as available now */
+static void ipsec_sa_publish(ipsec_sa_t *ipsec_sa)
+{
+ odp_atomic_store_rel_u32(&ipsec_sa->state, IPSEC_SA_STATE_ACTIVE);
+}
+
+static int ipsec_sa_lock(ipsec_sa_t *ipsec_sa)
+{
+ int cas = 0;
+ uint32_t state = odp_atomic_load_u32(&ipsec_sa->state);
+
+ while (0 == cas) {
+ /*
+ * This can be called from lookup path, so we really need this
+ * check.
+ */
+ if ((state & IPSEC_SA_STATE_MASK) != IPSEC_SA_STATE_ACTIVE)
+ return -1;
+
+ cas = odp_atomic_cas_acq_u32(&ipsec_sa->state, &state,
+ state + 1);
+ }
+
+ return 0;
+}
+
+/* Do not call directly, use _odp_ipsec_sa_unuse */
+static odp_bool_t ipsec_sa_unlock(ipsec_sa_t *ipsec_sa)
+{
+ int cas = 0;
+ uint32_t state = odp_atomic_load_u32(&ipsec_sa->state);
+
+ while (0 == cas)
+ cas = odp_atomic_cas_rel_u32(&ipsec_sa->state, &state,
+ state - 1);
+
+ return state == IPSEC_SA_STATE_DISABLE;
+}
+
+ipsec_sa_t *_odp_ipsec_sa_use(odp_ipsec_sa_t sa)
+{
+ ipsec_sa_t *ipsec_sa;
+
+ _ODP_ASSERT(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+
+ if (ipsec_sa_lock(ipsec_sa) < 0)
+ return NULL;
+
+ return ipsec_sa;
+}
+
+void _odp_ipsec_sa_unuse(ipsec_sa_t *ipsec_sa)
+{
+ odp_queue_t queue;
+ odp_ipsec_sa_t sa;
+ odp_ipsec_warn_t warn = { .all = 0 };
+
+ _ODP_ASSERT(NULL != ipsec_sa);
+
+ queue = ipsec_sa->queue;
+ sa = ipsec_sa->ipsec_sa_hdl;
+
+ if (ipsec_sa_unlock(ipsec_sa) && ODP_QUEUE_INVALID != queue)
+ _odp_ipsec_status_send(queue,
+ ODP_IPSEC_STATUS_SA_DISABLE,
+ sa, 0, warn);
+}
+
+void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param)
+{
+ memset(param, 0, sizeof(odp_ipsec_sa_param_t));
+ param->dest_queue = ODP_QUEUE_INVALID;
+ param->outbound.tunnel.ipv4.ttl = 255;
+ param->outbound.tunnel.ipv6.hlimit = 255;
+}
+
+/* Return IV length required for the cipher for IPsec use */
+uint32_t _odp_ipsec_cipher_iv_len(odp_cipher_alg_t cipher)
+{
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ return 0;
+ case ODP_CIPHER_ALG_DES:
+ case ODP_CIPHER_ALG_3DES_CBC:
+ return 8;
+ case ODP_CIPHER_ALG_AES_CBC:
+ case ODP_CIPHER_ALG_AES_CTR:
+ return 16;
+ case ODP_CIPHER_ALG_AES_GCM:
+ return 12;
+ case ODP_CIPHER_ALG_AES_CCM:
+ return 11;
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ return 12;
+ default:
+ return (uint32_t)-1;
+ }
+}
+
+/* Return digest length required for the cipher for IPsec use */
+uint32_t _odp_ipsec_auth_digest_len(odp_auth_alg_t auth)
+{
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ return 0;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ return 12;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ return 16;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ return 24;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ return 32;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ return 12;
+ case ODP_AUTH_ALG_AES_GCM:
+ case ODP_AUTH_ALG_AES_GMAC:
+ return 16;
+ case ODP_AUTH_ALG_AES_CCM:
+ return 16;
+ case ODP_AUTH_ALG_AES_CMAC:
+ return 12;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ return 16;
+ default:
+ return (uint32_t)-1;
+ }
+}
+
+static uint32_t esp_block_len_to_mask(uint32_t block_len)
+{
+ /* ESP trailer should be 32-bit right aligned */
+ if (block_len < 4)
+ block_len = 4;
+
+ _ODP_ASSERT(_ODP_CHECK_IS_POWER2(block_len));
+ return block_len - 1;
+}
+
+/* AR window management initialization */
+static int ipsec_antireplay_init(ipsec_sa_t *ipsec_sa,
+ const odp_ipsec_sa_param_t *param)
+{
+ uint16_t num_bkts = 0;
+
+ if (param->inbound.antireplay_ws > IPSEC_AR_WIN_SIZE_MAX) {
+ _ODP_ERR("Anti-replay window size %" PRIu32 " is not supported.\n",
+ param->inbound.antireplay_ws);
+ return -1;
+ }
+
+ ipsec_sa->antireplay = (param->inbound.antireplay_ws != 0);
+ if (!ipsec_sa->antireplay)
+ return 0;
+
+ ipsec_sa->in.ar.win_size = param->inbound.antireplay_ws;
+ /* Window size should be at least IPSEC_AR_WIN_SIZE_MIN */
+ if (ipsec_sa->in.ar.win_size < IPSEC_AR_WIN_SIZE_MIN)
+ ipsec_sa->in.ar.win_size = IPSEC_AR_WIN_SIZE_MIN;
+
+ num_bkts = IPSEC_AR_WIN_NUM_BUCKETS(ipsec_sa->in.ar.win_size);
+ ipsec_sa->in.ar.num_buckets = num_bkts;
+ odp_atomic_init_u64(&ipsec_sa->hot.in.wintop_seq, 0);
+ memset(ipsec_sa->hot.in.bucket_arr, 0, sizeof(uint64_t) * num_bkts);
+
+ odp_spinlock_init(&ipsec_sa->hot.in.lock);
+
+ return 0;
+}
+
+static void store_sa_info(ipsec_sa_t *ipsec_sa, const odp_ipsec_sa_param_t *p)
+{
+ ipsec_sa->sa_info.cipher_alg = p->crypto.cipher_alg;
+ ipsec_sa->sa_info.cipher_key_len = p->crypto.cipher_key.length;
+ ipsec_sa->sa_info.cipher_key_extra_len = p->crypto.cipher_key.length;
+ ipsec_sa->sa_info.auth_alg = p->crypto.auth_alg;
+ ipsec_sa->sa_info.auth_key_len = p->crypto.auth_key.length;
+ ipsec_sa->sa_info.auth_key_extra_len = p->crypto.auth_key_extra.length;
+
+ ipsec_sa->sa_info.icv_len = p->crypto.icv_len;
+ ipsec_sa->sa_info.context_len = p->context_len;
+
+ if (p->dir == ODP_IPSEC_DIR_INBOUND)
+ ipsec_sa->sa_info.in.antireplay_ws = p->inbound.antireplay_ws;
+ else
+ ipsec_sa->sa_info.out.mtu = p->outbound.mtu;
+}
+
+static int init_cbc_salt(ipsec_sa_t *ipsec_sa)
+{
+ int filled = 0;
+ int rc;
+
+ if (!ipsec_sa->use_cbc_iv)
+ return 0;
+
+ while (filled < CBC_SALT_LEN) {
+ rc = odp_random_data(&ipsec_sa->cbc_salt[filled],
+ CBC_SALT_LEN - filled,
+ ODP_RANDOM_CRYPTO);
+ if (rc < 0)
+ return -1;
+ filled += rc;
+ }
+ return 0;
+}
+
+odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
+{
+ ipsec_sa_t *ipsec_sa;
+ odp_crypto_session_param_t crypto_param;
+ odp_crypto_ses_create_err_t ses_create_rc;
+ const odp_crypto_key_t *salt_param = NULL;
+
+ if (!odp_ipsec_cipher_capability(param->crypto.cipher_alg, NULL, 0) ||
+ !odp_ipsec_auth_capability(param->crypto.auth_alg, NULL, 0))
+ return ODP_IPSEC_SA_INVALID;
+
+ ipsec_sa = ipsec_sa_reserve();
+ if (NULL == ipsec_sa) {
+ _ODP_ERR("No more free SA\n");
+ return ODP_IPSEC_SA_INVALID;
+ }
+
+ store_sa_info(ipsec_sa, param);
+
+ ipsec_sa->proto = param->proto;
+ ipsec_sa->spi = param->spi;
+ ipsec_sa->context = param->context;
+ ipsec_sa->queue = param->dest_queue;
+ if (_odp_ipsec_is_sync_mode(param->dir)) {
+ /* Invalid queue indicates sync mode */
+ ipsec_sa->queue = ODP_QUEUE_INVALID;
+ }
+ ipsec_sa->mode = param->mode;
+ ipsec_sa->flags = 0;
+ ipsec_sa->esn = param->opt.esn;
+
+ if (ODP_IPSEC_DIR_INBOUND == param->dir) {
+ ipsec_sa->inbound = 1;
+ ipsec_sa->lookup_mode = param->inbound.lookup_mode;
+ if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->lookup_mode) {
+ ipsec_sa->in.lookup_ver =
+ param->inbound.lookup_param.ip_version;
+ if (ODP_IPSEC_IPV4 == ipsec_sa->in.lookup_ver)
+ memcpy(&ipsec_sa->in.lookup_dst_ipv4,
+ param->inbound.lookup_param.dst_addr,
+ sizeof(ipsec_sa->in.lookup_dst_ipv4));
+ else
+ memcpy(&ipsec_sa->in.lookup_dst_ipv6,
+ param->inbound.lookup_param.dst_addr,
+ sizeof(ipsec_sa->in.lookup_dst_ipv6));
+ }
+
+ if (ipsec_antireplay_init(ipsec_sa, param))
+ goto error;
+ } else {
+ ipsec_sa->lookup_mode = ODP_IPSEC_LOOKUP_DISABLED;
+ odp_atomic_init_u64(&ipsec_sa->hot.out.seq, 1);
+ ipsec_sa->out.frag_mode = param->outbound.frag_mode;
+ odp_atomic_init_u32(&ipsec_sa->out.mtu, param->outbound.mtu);
+ }
+ ipsec_sa->dec_ttl = param->opt.dec_ttl;
+ ipsec_sa->copy_dscp = param->opt.copy_dscp;
+ ipsec_sa->copy_df = param->opt.copy_df;
+ ipsec_sa->copy_flabel = param->opt.copy_flabel;
+ ipsec_sa->udp_encap = param->opt.udp_encap;
+
+ odp_atomic_init_u64(&ipsec_sa->hot.bytes, 0);
+ odp_atomic_init_u64(&ipsec_sa->hot.packets, 0);
+ ipsec_sa->soft_limit_bytes = param->lifetime.soft_limit.bytes;
+ ipsec_sa->soft_limit_packets = param->lifetime.soft_limit.packets;
+ ipsec_sa->hard_limit_bytes = param->lifetime.hard_limit.bytes;
+ ipsec_sa->hard_limit_packets = param->lifetime.hard_limit.packets;
+
+ odp_atomic_init_u64(&ipsec_sa->stats.proto_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.auth_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.antireplay_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.alg_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.mtu_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.hard_exp_bytes_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.hard_exp_pkts_err, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.post_lifetime_err_pkts, 0);
+ odp_atomic_init_u64(&ipsec_sa->stats.post_lifetime_err_bytes, 0);
+ odp_atomic_init_u32(&ipsec_sa->soft_expiry_notified, 0);
+
+ if (ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode &&
+ ODP_IPSEC_DIR_OUTBOUND == param->dir) {
+ if (ODP_IPSEC_TUNNEL_IPV4 == param->outbound.tunnel.type) {
+ ipsec_sa->tun_ipv4 = 1;
+ memcpy(&ipsec_sa->out.tun_ipv4.src_ip,
+ param->outbound.tunnel.ipv4.src_addr,
+ sizeof(ipsec_sa->out.tun_ipv4.src_ip));
+ memcpy(&ipsec_sa->out.tun_ipv4.dst_ip,
+ param->outbound.tunnel.ipv4.dst_addr,
+ sizeof(ipsec_sa->out.tun_ipv4.dst_ip));
+ ipsec_sa->out.tun_ipv4.param.src_addr =
+ &ipsec_sa->out.tun_ipv4.src_ip;
+ ipsec_sa->out.tun_ipv4.param.dst_addr =
+ &ipsec_sa->out.tun_ipv4.dst_ip;
+ ipsec_sa->out.tun_ipv4.param.ttl =
+ param->outbound.tunnel.ipv4.ttl;
+ ipsec_sa->out.tun_ipv4.param.dscp =
+ param->outbound.tunnel.ipv4.dscp;
+ ipsec_sa->out.tun_ipv4.param.df =
+ param->outbound.tunnel.ipv4.df;
+ } else {
+ ipsec_sa->tun_ipv4 = 0;
+ memcpy(&ipsec_sa->out.tun_ipv6.src_ip,
+ param->outbound.tunnel.ipv6.src_addr,
+ sizeof(ipsec_sa->out.tun_ipv6.src_ip));
+ memcpy(&ipsec_sa->out.tun_ipv6.dst_ip,
+ param->outbound.tunnel.ipv6.dst_addr,
+ sizeof(ipsec_sa->out.tun_ipv6.dst_ip));
+ ipsec_sa->out.tun_ipv4.param.src_addr =
+ &ipsec_sa->out.tun_ipv6.src_ip;
+ ipsec_sa->out.tun_ipv4.param.dst_addr =
+ &ipsec_sa->out.tun_ipv6.dst_ip;
+ ipsec_sa->out.tun_ipv6.param.hlimit =
+ param->outbound.tunnel.ipv6.hlimit;
+ ipsec_sa->out.tun_ipv6.param.dscp =
+ param->outbound.tunnel.ipv6.dscp;
+ ipsec_sa->out.tun_ipv6.param.flabel =
+ param->outbound.tunnel.ipv6.flabel;
+ }
+ }
+
+ odp_crypto_session_param_init(&crypto_param);
+
+ /* Setup parameters and call crypto library to create session */
+ crypto_param.op = (ODP_IPSEC_DIR_INBOUND == param->dir) ?
+ ODP_CRYPTO_OP_DECODE :
+ ODP_CRYPTO_OP_ENCODE;
+ crypto_param.op_type = ODP_CRYPTO_OP_TYPE_BASIC;
+ crypto_param.auth_cipher_text = 1;
+ if (param->proto == ODP_IPSEC_AH)
+ crypto_param.hash_result_in_auth_range = 1;
+
+ crypto_param.op_mode = ODP_CRYPTO_SYNC;
+ crypto_param.compl_queue = ODP_QUEUE_INVALID;
+ crypto_param.output_pool = ODP_POOL_INVALID;
+
+ crypto_param.cipher_alg = param->crypto.cipher_alg;
+ crypto_param.cipher_key = param->crypto.cipher_key;
+ crypto_param.auth_alg = param->crypto.auth_alg;
+ crypto_param.auth_key = param->crypto.auth_key;
+
+ crypto_param.cipher_iv_len =
+ _odp_ipsec_cipher_iv_len(crypto_param.cipher_alg);
+
+ crypto_param.auth_digest_len =
+ _odp_ipsec_auth_digest_len(crypto_param.auth_alg);
+
+ if (param->crypto.icv_len != 0 &&
+ param->crypto.icv_len != crypto_param.auth_digest_len)
+ goto error;
+
+ if ((uint32_t)-1 == crypto_param.cipher_iv_len ||
+ (uint32_t)-1 == crypto_param.auth_digest_len)
+ goto error;
+
+ ipsec_sa->salt_length = 0;
+ /* ESN higher 32 bits flag.
+ * This flag is set for individual algo's.
+ * This flag is reset for combined mode algo's and ODP_AUTH_ALG_NULL.
+ */
+ ipsec_sa->insert_seq_hi = (ipsec_sa->esn) ? 1 : 0;
+
+ switch (crypto_param.cipher_alg) {
+ case ODP_CIPHER_ALG_NULL:
+ ipsec_sa->esp_iv_len = 0;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(1);
+ break;
+ case ODP_CIPHER_ALG_DES:
+ case ODP_CIPHER_ALG_3DES_CBC:
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(8);
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ ipsec_sa->use_cbc_iv = 1;
+ ipsec_sa->esp_iv_len = 16;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(16);
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ ipsec_sa->use_counter_iv = 1;
+ ipsec_sa->aes_ctr_iv = 1;
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(1);
+ /* 4 byte nonse */
+ ipsec_sa->salt_length = 4;
+ salt_param = &param->crypto.cipher_key_extra;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ ipsec_sa->use_counter_iv = 1;
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(1);
+ ipsec_sa->salt_length = 4;
+ salt_param = &param->crypto.cipher_key_extra;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ ipsec_sa->use_counter_iv = 1;
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(16);
+ ipsec_sa->salt_length = 3;
+ salt_param = &param->crypto.cipher_key_extra;
+ break;
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ ipsec_sa->use_counter_iv = 1;
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(1);
+ ipsec_sa->salt_length = 4;
+ salt_param = &param->crypto.cipher_key_extra;
+ break;
+ default:
+ goto error;
+ }
+
+ switch (crypto_param.auth_alg) {
+ case ODP_AUTH_ALG_AES_GCM:
+ case ODP_AUTH_ALG_AES_CCM:
+ if (ipsec_sa->esn) {
+ crypto_param.auth_aad_len = 12;
+ ipsec_sa->insert_seq_hi = 0;
+ } else {
+ crypto_param.auth_aad_len = 8;
+ }
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ if ((ODP_CIPHER_ALG_NULL != crypto_param.cipher_alg) ||
+ ipsec_sa->esn)
+ goto error;
+ ipsec_sa->use_counter_iv = 1;
+ ipsec_sa->esp_iv_len = 8;
+ ipsec_sa->esp_pad_mask = esp_block_len_to_mask(1);
+ crypto_param.auth_iv_len = 12;
+ ipsec_sa->salt_length = 4;
+ salt_param = &param->crypto.auth_key_extra;
+ break;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ if (ipsec_sa->esn) {
+ crypto_param.auth_aad_len = 12;
+ ipsec_sa->insert_seq_hi = 0;
+ } else {
+ crypto_param.auth_aad_len = 8;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ipsec_sa->icv_len = crypto_param.auth_digest_len;
+
+ /* For ODP_AUTH_ALG_NULL */
+ if (!ipsec_sa->icv_len)
+ ipsec_sa->insert_seq_hi = 0;
+
+ if (ipsec_sa->salt_length) {
+ if (ipsec_sa->salt_length > IPSEC_MAX_SALT_LEN) {
+ _ODP_ERR("IPSEC_MAX_SALT_LEN too small\n");
+ goto error;
+ }
+
+ if (ipsec_sa->salt_length != salt_param->length) {
+ _ODP_ERR("Bad extra keying material length: %i\n", salt_param->length);
+ goto error;
+ }
+
+ memcpy(ipsec_sa->salt, salt_param->data, ipsec_sa->salt_length);
+ }
+
+ if (init_cbc_salt(ipsec_sa))
+ goto error;
+
+ if (odp_crypto_session_create(&crypto_param, &ipsec_sa->session,
+ &ses_create_rc))
+ goto error;
+
+ init_sa_thread_local(ipsec_sa);
+
+ ipsec_sa_publish(ipsec_sa);
+
+ return ipsec_sa->ipsec_sa_hdl;
+
+error:
+ ipsec_sa_release(ipsec_sa);
+
+ return ODP_IPSEC_SA_INVALID;
+}
+
+int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)
+{
+ ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+ uint32_t state;
+ int cas = 0;
+
+ /* This is a custom rwlock implementation. It is not possible to use
+ * original rwlock, because there is no way to test if current code is
+ * the last reader when disable operation is pending. */
+ state = odp_atomic_load_u32(&ipsec_sa->state);
+
+ while (0 == cas) {
+ if (state & IPSEC_SA_STATE_DISABLE)
+ return -1;
+
+ cas = odp_atomic_cas_acq_u32(&ipsec_sa->state, &state,
+ state | IPSEC_SA_STATE_DISABLE);
+ }
+
+ if (ODP_QUEUE_INVALID != ipsec_sa->queue) {
+ odp_ipsec_warn_t warn = { .all = 0 };
+
+ /*
+ * If there were not active state when we disabled SA,
+ * send the event.
+ */
+ if (0 == state)
+ _odp_ipsec_status_send(ipsec_sa->queue,
+ ODP_IPSEC_STATUS_SA_DISABLE,
+ ipsec_sa->ipsec_sa_hdl,
+ 0, warn);
+
+ return 0;
+ }
+
+ while (IPSEC_SA_STATE_DISABLE != state) {
+ odp_cpu_pause();
+ state = odp_atomic_load_u32(&ipsec_sa->state);
+ }
+
+ return 0;
+}
+
+int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa)
+{
+ ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+ int rc = 0;
+ uint32_t state = odp_atomic_load_u32(&ipsec_sa->state);
+
+ if (IPSEC_SA_STATE_DISABLE != state) {
+ _ODP_ERR("Destroying not disabled ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+ return -1;
+ }
+
+ if (odp_crypto_session_destroy(ipsec_sa->session) < 0) {
+ _ODP_ERR("Error destroying crypto session for ipsec_sa: %u\n",
+ ipsec_sa->ipsec_sa_idx);
+ rc = -1;
+ }
+
+ ipsec_sa_release(ipsec_sa);
+
+ return rc;
+}
+
+void *odp_ipsec_sa_context(odp_ipsec_sa_t sa)
+{
+ ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+
+ return ipsec_sa->context;
+}
+
+uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa)
+{
+ return _odp_pri(sa);
+}
+
+int odp_ipsec_sa_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu)
+{
+ ipsec_sa_t *ipsec_sa;
+
+ ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+ _ODP_ASSERT(NULL != ipsec_sa);
+ odp_atomic_store_u32(&ipsec_sa->out.mtu, mtu);
+ return 0;
+}
+
+ipsec_sa_t *_odp_ipsec_sa_lookup(const ipsec_sa_lookup_t *lookup)
+{
+ uint32_t i;
+ ipsec_sa_t *best = NULL;
+
+ for (i = 0; i < ipsec_sa_tbl->max_num_sa; i++) {
+ ipsec_sa_t *ipsec_sa = ipsec_sa_entry(i);
+
+ if (ipsec_sa_lock(ipsec_sa) < 0)
+ continue;
+
+ if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->lookup_mode &&
+ lookup->proto == ipsec_sa->proto &&
+ lookup->spi == ipsec_sa->spi &&
+ lookup->ver == ipsec_sa->in.lookup_ver &&
+ !memcmp(lookup->dst_addr,
+ lookup->ver == ODP_IPSEC_IPV4 ?
+ (void *)&ipsec_sa->in.lookup_dst_ipv4 :
+ (void *)&ipsec_sa->in.lookup_dst_ipv6,
+ lookup->ver == ODP_IPSEC_IPV4 ?
+ _ODP_IPV4ADDR_LEN :
+ _ODP_IPV6ADDR_LEN)) {
+ if (NULL != best)
+ _odp_ipsec_sa_unuse(best);
+ return ipsec_sa;
+ } else if (NULL == best &&
+ ODP_IPSEC_LOOKUP_SPI == ipsec_sa->lookup_mode &&
+ lookup->proto == ipsec_sa->proto &&
+ lookup->spi == ipsec_sa->spi) {
+ best = ipsec_sa;
+ } else {
+ _odp_ipsec_sa_unuse(ipsec_sa);
+ }
+ }
+
+ return best;
+}
+
+int _odp_ipsec_sa_stats_precheck(ipsec_sa_t *ipsec_sa,
+ odp_ipsec_op_status_t *status)
+{
+ int rc = 0;
+ sa_thread_local_t *sa_tl = ipsec_sa_thread_local(ipsec_sa);
+
+ if (sa_tl->lifetime_status.error.hard_exp_packets ||
+ sa_tl->lifetime_status.error.hard_exp_bytes) {
+ status->all |= sa_tl->lifetime_status.all;
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int _odp_ipsec_sa_lifetime_update(ipsec_sa_t *ipsec_sa, uint32_t len,
+ odp_ipsec_op_status_t *status)
+{
+ sa_thread_local_t *sa_tl = ipsec_sa_thread_local(ipsec_sa);
+ uint64_t packets, bytes;
+ uint32_t tl_byte_quota;
+ uint32_t tl_pkt_quota;
+
+ tl_pkt_quota = odp_atomic_load_u32(&sa_tl->packet_quota);
+ if (odp_unlikely(tl_pkt_quota == 0)) {
+ packets = odp_atomic_fetch_add_u64(&ipsec_sa->hot.packets,
+ SA_LIFE_PACKETS_PREALLOC);
+ packets += SA_LIFE_PACKETS_PREALLOC;
+ tl_pkt_quota += SA_LIFE_PACKETS_PREALLOC;
+
+ if (ipsec_sa->soft_limit_packets > 0 &&
+ packets >= ipsec_sa->soft_limit_packets)
+ sa_tl->lifetime_status.warn.soft_exp_packets = 1;
+
+ if (ipsec_sa->hard_limit_packets > 0 &&
+ packets >= ipsec_sa->hard_limit_packets)
+ sa_tl->lifetime_status.error.hard_exp_packets = 1;
+ }
+ tl_pkt_quota--;
+ odp_atomic_store_u32(&sa_tl->packet_quota, tl_pkt_quota);
+
+ tl_byte_quota = odp_atomic_load_u32(&sa_tl->byte_quota);
+ if (odp_unlikely(tl_byte_quota < len)) {
+ bytes = odp_atomic_fetch_add_u64(&ipsec_sa->hot.bytes,
+ len + SA_LIFE_BYTES_PREALLOC);
+ bytes += len + SA_LIFE_BYTES_PREALLOC;
+ tl_byte_quota += len + SA_LIFE_BYTES_PREALLOC;
+
+ if (ipsec_sa->soft_limit_bytes > 0 &&
+ bytes >= ipsec_sa->soft_limit_bytes)
+ sa_tl->lifetime_status.warn.soft_exp_bytes = 1;
+
+ if (ipsec_sa->hard_limit_bytes > 0 &&
+ bytes >= ipsec_sa->hard_limit_bytes)
+ sa_tl->lifetime_status.error.hard_exp_bytes = 1;
+ }
+ tl_byte_quota -= len;
+ odp_atomic_store_u32(&sa_tl->byte_quota, tl_byte_quota);
+
+
+ status->all |= sa_tl->lifetime_status.all;
+
+ if (sa_tl->lifetime_status.error.hard_exp_packets ||
+ sa_tl->lifetime_status.error.hard_exp_bytes)
+ return -1;
+ return 0;
+}
+
+static uint64_t ipsec_sa_antireplay_max_seq(ipsec_sa_t *ipsec_sa)
+{
+ uint64_t max_seq = 0;
+
+ max_seq = odp_atomic_load_u64(&ipsec_sa->hot.in.wintop_seq);
+ if (!ipsec_sa->esn)
+ max_seq &= 0xffffffff;
+
+ return max_seq;
+}
+
+int _odp_ipsec_sa_replay_precheck(ipsec_sa_t *ipsec_sa, uint64_t seq,
+ odp_ipsec_op_status_t *status)
+{
+ /* Try to be as quick as possible, we will discard packets later */
+ if (ipsec_sa->antireplay) {
+ uint64_t wintop = odp_atomic_load_u64(&ipsec_sa->hot.in.wintop_seq);
+
+ if (!ipsec_sa->esn)
+ wintop &= 0xffffffff;
+
+ if ((seq + ipsec_sa->in.ar.win_size) <= wintop) {
+ status->error.antireplay = 1;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline int ipsec_wslarge_replay_update(ipsec_sa_t *ipsec_sa, uint64_t seq,
+ odp_ipsec_op_status_t *status)
+{
+ uint64_t bucket, wintop_bucket, new_bucket;
+ uint64_t bkt_diff, bkt_cnt;
+ uint64_t bit = 0, top_seq;
+
+ odp_spinlock_lock(&ipsec_sa->hot.in.lock);
+
+ top_seq = odp_atomic_load_u64(&ipsec_sa->hot.in.wintop_seq);
+ if ((seq + ipsec_sa->in.ar.win_size) <= top_seq)
+ goto ar_err;
+
+ bucket = (seq >> IPSEC_AR_WIN_BUCKET_BITS);
+
+ /* Check if the seq is within the range */
+ if (seq > top_seq) {
+ wintop_bucket = top_seq >> IPSEC_AR_WIN_BUCKET_BITS;
+ bkt_diff = bucket - wintop_bucket;
+
+ /* Seq is way after the range of AR window size */
+ if (bkt_diff > ipsec_sa->in.ar.num_buckets)
+ bkt_diff = ipsec_sa->in.ar.num_buckets;
+
+ for (bkt_cnt = 0; bkt_cnt < bkt_diff; bkt_cnt++) {
+ new_bucket = (bkt_cnt + wintop_bucket + 1) %
+ ipsec_sa->in.ar.num_buckets;
+ ipsec_sa->hot.in.bucket_arr[new_bucket] = 0;
+ }
+
+ /* AR window top sequence number */
+ odp_atomic_store_u64(&ipsec_sa->hot.in.wintop_seq, seq);
+ }
+
+ bucket %= ipsec_sa->in.ar.num_buckets;
+ bit = (uint64_t)1 << (seq & IPSEC_AR_WIN_BITLOC_MASK);
+
+ /* Already seen the packet, discard it */
+ if (ipsec_sa->hot.in.bucket_arr[bucket] & bit)
+ goto ar_err;
+
+ /* Packet is new, mark it as seen */
+ ipsec_sa->hot.in.bucket_arr[bucket] |= bit;
+ odp_spinlock_unlock(&ipsec_sa->hot.in.lock);
+
+ return 0;
+ar_err:
+ status->error.antireplay = 1;
+ odp_spinlock_unlock(&ipsec_sa->hot.in.lock);
+ return -1;
+}
+
+static inline int ipsec_ws32_replay_update(ipsec_sa_t *ipsec_sa, uint32_t seq,
+ odp_ipsec_op_status_t *status)
+{
+ uint64_t state, new_state;
+ int cas = 0;
+
+ state = odp_atomic_load_u64(&ipsec_sa->hot.in.wintop_seq);
+ while (0 == cas) {
+ uint32_t max_seq = state & 0xffffffff;
+ uint32_t mask = state >> 32;
+
+ if (seq + IPSEC_AR_WIN_SIZE_MIN <= max_seq) {
+ status->error.antireplay = 1;
+ return -1;
+ } else if (seq >= max_seq + IPSEC_AR_WIN_SIZE_MIN) {
+ mask = 1;
+ max_seq = seq;
+ } else if (seq > max_seq) {
+ mask <<= seq - max_seq;
+ mask |= 1;
+ max_seq = seq;
+ } else if (mask & (1U << (max_seq - seq))) {
+ status->error.antireplay = 1;
+ return -1;
+ } else {
+ mask |= (1U << (max_seq - seq));
+ }
+
+ new_state = (((uint64_t)mask) << 32) | max_seq;
+ cas = odp_atomic_cas_acq_rel_u64(&ipsec_sa->hot.in.wintop_seq,
+ &state, new_state);
+ }
+ return 0;
+}
+
+int _odp_ipsec_sa_replay_update(ipsec_sa_t *ipsec_sa, uint64_t seq,
+ odp_ipsec_op_status_t *status)
+{
+ int ret;
+
+ /* Window update for ws equal to 32 */
+ if ((!ipsec_sa->esn) && (ipsec_sa->in.ar.win_size == IPSEC_AR_WIN_SIZE_MIN))
+ ret = ipsec_ws32_replay_update(ipsec_sa, (seq & 0xffffffff), status);
+ else
+ ret = ipsec_wslarge_replay_update(ipsec_sa, seq, status);
+
+ return ret;
+}
+
+uint16_t _odp_ipsec_sa_alloc_ipv4_id(ipsec_sa_t *ipsec_sa)
+{
+ (void)ipsec_sa;
+ ipsec_thread_local_t *tl = &ipsec_sa_tbl->per_thread[odp_thread_id()];
+ uint32_t data;
+
+ if (odp_unlikely(tl->next_ipv4_id ==
+ tl->first_ipv4_id + IPV4_ID_BLOCK_SIZE)) {
+ /* Return used ID block to the ring */
+ data = tl->first_ipv4_id;
+ ring_mpmc_u32_enq_multi(&ipsec_sa_tbl->hot.ipv4_id_ring,
+ ipsec_sa_tbl->hot.ipv4_id_data,
+ IPV4_ID_RING_MASK,
+ &data,
+ 1);
+ /* Get new ID block */
+ ring_mpmc_u32_deq_multi(&ipsec_sa_tbl->hot.ipv4_id_ring,
+ ipsec_sa_tbl->hot.ipv4_id_data,
+ IPV4_ID_RING_MASK,
+ &data,
+ 1);
+ tl->first_ipv4_id = data;
+ tl->next_ipv4_id = data;
+ }
+
+ /* No need to convert to BE: ID just should not be duplicated */
+ return tl->next_ipv4_id++;
+}
+
+void _odp_ipsec_sa_stats_pkts(ipsec_sa_t *sa, odp_ipsec_stats_t *stats)
+{
+ int thread_count_max = odp_thread_count_max();
+ uint64_t tl_byte_quota = 0;
+ uint64_t tl_pkt_quota = 0;
+ sa_thread_local_t *sa_tl;
+ int n;
+
+ /*
+ * Field 'hot.packets' tracks SA lifetime. The same field is being used
+ * to track the number of success packets.
+ *
+ * SA lifetime tracking implements a per thread packet quota to allow
+ * less frequent updates to the hot field. The per thread quota need
+ * to be decremented. In addition, SA lifetime gets consumed for any
+ * errors occurring after lifetime check is done. Those packets also
+ * need to be accounted for.
+ */
+
+ for (n = 0; n < thread_count_max; n++) {
+ sa_tl = &ipsec_sa_tbl->per_thread[n].sa[sa->ipsec_sa_idx];
+ tl_pkt_quota += odp_atomic_load_u32(&sa_tl->packet_quota);
+ tl_byte_quota += odp_atomic_load_u32(&sa_tl->byte_quota);
+ }
+
+ stats->success =
+ odp_atomic_load_u64(&sa->hot.packets)
+ - odp_atomic_load_u64(&sa->stats.post_lifetime_err_pkts)
+ - tl_pkt_quota;
+
+ stats->success_bytes =
+ odp_atomic_load_u64(&sa->hot.bytes)
+ - odp_atomic_load_u64(&sa->stats.post_lifetime_err_bytes)
+ - tl_byte_quota;
+
+ return;
+}
+
+static void ipsec_out_sa_info(ipsec_sa_t *ipsec_sa, odp_ipsec_sa_info_t *sa_info)
+{
+ odp_ipsec_tunnel_param_t *tun_param = &sa_info->param.outbound.tunnel;
+
+ tun_param->type = ipsec_sa->tun_ipv4 ? ODP_IPSEC_TUNNEL_IPV4 :
+ ODP_IPSEC_TUNNEL_IPV6;
+ tun_param->ipv4.dscp = ipsec_sa->out.tun_ipv4.param.dscp;
+ tun_param->ipv4.df = ipsec_sa->out.tun_ipv4.param.df;
+ tun_param->ipv4.ttl = ipsec_sa->out.tun_ipv4.param.ttl;
+ tun_param->ipv6.flabel = ipsec_sa->out.tun_ipv6.param.flabel;
+ tun_param->ipv6.dscp = ipsec_sa->out.tun_ipv6.param.dscp;
+ tun_param->ipv6.hlimit = ipsec_sa->out.tun_ipv6.param.hlimit;
+
+ sa_info->param.outbound.frag_mode = ipsec_sa->out.frag_mode;
+ sa_info->param.outbound.mtu = ipsec_sa->sa_info.out.mtu;
+
+ sa_info->outbound.seq_num =
+ (uint64_t)odp_atomic_load_u64(&ipsec_sa->hot.out.seq) - 1;
+
+ if (ipsec_sa->mode == ODP_IPSEC_MODE_TUNNEL) {
+ uint8_t *src, *dst;
+
+ if (ipsec_sa->tun_ipv4) {
+ src = sa_info->outbound.tunnel.ipv4.src_addr;
+ dst = sa_info->outbound.tunnel.ipv4.dst_addr;
+ memcpy(src, &ipsec_sa->out.tun_ipv4.src_ip,
+ ODP_IPV4_ADDR_SIZE);
+ memcpy(dst, &ipsec_sa->out.tun_ipv4.dst_ip,
+ ODP_IPV4_ADDR_SIZE);
+ tun_param->ipv4.src_addr = src;
+ tun_param->ipv4.dst_addr = dst;
+ } else {
+ src = sa_info->outbound.tunnel.ipv6.src_addr;
+ dst = sa_info->outbound.tunnel.ipv6.dst_addr;
+ memcpy(src, &ipsec_sa->out.tun_ipv6.src_ip,
+ ODP_IPV6_ADDR_SIZE);
+ memcpy(dst, &ipsec_sa->out.tun_ipv6.dst_ip,
+ ODP_IPV6_ADDR_SIZE);
+ tun_param->ipv6.src_addr = src;
+ tun_param->ipv6.dst_addr = dst;
+ }
+ }
+}
+
+static void ipsec_in_sa_info(ipsec_sa_t *ipsec_sa, odp_ipsec_sa_info_t *sa_info)
+{
+ uint8_t *dst = sa_info->inbound.lookup_param.dst_addr;
+
+ sa_info->param.inbound.lookup_mode = ipsec_sa->lookup_mode;
+ sa_info->param.inbound.lookup_param.ip_version = ipsec_sa->in.lookup_ver;
+ sa_info->param.inbound.lookup_param.dst_addr = dst;
+ sa_info->param.inbound.antireplay_ws = ipsec_sa->sa_info.in.antireplay_ws;
+ sa_info->param.inbound.pipeline = ODP_IPSEC_PIPELINE_NONE;
+ sa_info->param.inbound.dest_cos = ODP_COS_INVALID;
+ sa_info->param.inbound.reassembly_en = false;
+
+ if (ipsec_sa->lookup_mode == ODP_IPSEC_LOOKUP_DSTADDR_SPI) {
+ if (ipsec_sa->in.lookup_ver == ODP_IPSEC_IPV4)
+ memcpy(dst, &ipsec_sa->in.lookup_dst_ipv4,
+ ODP_IPV4_ADDR_SIZE);
+ else
+ memcpy(dst, &ipsec_sa->in.lookup_dst_ipv6,
+ ODP_IPV6_ADDR_SIZE);
+ }
+
+ sa_info->param.inbound.lookup_param.dst_addr = dst;
+
+ if (ipsec_sa->antireplay) {
+ sa_info->inbound.antireplay_ws = ipsec_sa->in.ar.win_size;
+ sa_info->inbound.antireplay_window_top =
+ ipsec_sa_antireplay_max_seq(ipsec_sa);
+ }
+}
+
+int odp_ipsec_sa_info(odp_ipsec_sa_t sa, odp_ipsec_sa_info_t *sa_info)
+{
+ ipsec_sa_t *ipsec_sa;
+ odp_ipsec_sa_param_t *param;
+
+ ipsec_sa = _odp_ipsec_sa_entry_from_hdl(sa);
+
+ _ODP_ASSERT(ipsec_sa != NULL);
+ _ODP_ASSERT(sa_info != NULL);
+
+ memset(sa_info, 0, sizeof(*sa_info));
+ param = &sa_info->param;
+
+ param->dir = ipsec_sa->inbound ? ODP_IPSEC_DIR_INBOUND :
+ ODP_IPSEC_DIR_OUTBOUND;
+ param->proto = ipsec_sa->proto;
+ param->mode = ipsec_sa->mode;
+
+ param->crypto.cipher_alg = ipsec_sa->sa_info.cipher_alg;
+ param->crypto.cipher_key.data = NULL;
+ param->crypto.cipher_key.length = ipsec_sa->sa_info.cipher_key_len;
+ param->crypto.cipher_key_extra.data = NULL;
+ param->crypto.cipher_key_extra.length = ipsec_sa->sa_info.cipher_key_extra_len;
+ param->crypto.auth_alg = ipsec_sa->sa_info.auth_alg;
+ param->crypto.auth_key.data = NULL;
+ param->crypto.auth_key.length = ipsec_sa->sa_info.auth_key_len;
+ param->crypto.auth_key_extra.data = NULL;
+ param->crypto.auth_key_extra.length = ipsec_sa->sa_info.auth_key_extra_len;
+ param->crypto.icv_len = ipsec_sa->sa_info.icv_len;
+
+ param->opt.esn = ipsec_sa->esn;
+ param->opt.udp_encap = ipsec_sa->udp_encap;
+ param->opt.copy_dscp = ipsec_sa->copy_dscp;
+ param->opt.copy_flabel = ipsec_sa->copy_flabel;
+ param->opt.copy_df = ipsec_sa->copy_df;
+ param->opt.dec_ttl = ipsec_sa->dec_ttl;
+
+ param->lifetime.soft_limit.bytes = ipsec_sa->soft_limit_bytes;
+ param->lifetime.soft_limit.packets = ipsec_sa->soft_limit_packets;
+ param->lifetime.hard_limit.bytes = ipsec_sa->hard_limit_bytes;
+ param->lifetime.hard_limit.packets = ipsec_sa->hard_limit_packets;
+
+ param->spi = ipsec_sa->spi;
+ param->dest_queue = ipsec_sa->queue;
+ param->context = ipsec_sa->context;
+ param->context_len = ipsec_sa->sa_info.context_len;
+
+ if (ipsec_sa->inbound)
+ ipsec_in_sa_info(ipsec_sa, sa_info);
+ else
+ ipsec_out_sa_info(ipsec_sa, sa_info);
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_ishm.c b/platform/linux-generic/odp_ishm.c
new file mode 100644
index 000000000..d93e54fdc
--- /dev/null
+++ b/platform/linux-generic/odp_ishm.c
@@ -0,0 +1,2241 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019 Nokia
+ */
+
+/* This file handles the internal shared memory: internal shared memory
+ * is memory which is sharable by all ODP threads regardless of how the
+ * ODP thread is implemented (pthread or process) and regardless of fork()
+ * time.
+ * Moreover, when reserved with the _ODP_ISHM_SINGLE_VA flag,
+ * internal shared memory is guaranteed to always be located at the same virtual
+ * address, i.e. pointers to internal shared memory are fully shareable
+ * between odp threads (regardless of thread type or fork time) in that case.
+ * Internal shared memory is mainly meant to be used internally within ODP
+ * (hence its name), but may also be allocated by odp applications and drivers,
+ * in the future (through these interfaces).
+ * To guarantee this full pointer shareability (when reserved with the
+ * _ODP_ISHM_SINGLE_VA flag) the whole internal shared memory area is reserved
+ * at global_init time.
+ * Because all ODP threads (pthreads or processes) are descendants of the ODP
+ * instantiation process, this address space is inherited by all ODP threads.
+ * When internal shmem reservation actually occurs, and when reserved with the
+ * _ODP_ISHM_SINGLE_VA flag, memory is allocated from the pre-reserved single
+ * VA memory.
+ * When an internal shared memory block is released, the memory is returned to
+ * its "pool" of pre-reserved memory (assuming it was allocated from there). The
+ * memory is not returned back to kernel until odp_term_global().
+ *
+ * This file contains functions to handle the VA area (handling fragmentation
+ * and defragmentation resulting from different allocs/release) and also
+ * define the functions to allocate, release and lookup internal shared
+ * memory:
+ * _odp_ishm_reserve(), _odp_ishm_free*() and _odp_ishm_lookup*()...
+ */
+#include <odp_posix_extensions.h>
+#include <odp_config_internal.h>
+#include <odp_global_data.h>
+#include <odp/api/spinlock.h>
+#include <odp/api/align.h>
+#include <odp/api/system_info.h>
+#include <odp/api/debug.h>
+#include <odp_init_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_fdserver_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_ishmphy_internal.h>
+#include <odp_ishmpool_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_string_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+/*
+ * Maximum number of internal shared memory blocks.
+ *
+ * This is the number of separate ISHM areas that can be reserved concurrently
+ * (Note that freeing such blocks may take time, or possibly never happen
+ * if some of the block owners never procsync() after free). This number
+ * should take that into account)
+ */
+#define ISHM_MAX_NB_BLOCKS (CONFIG_INTERNAL_SHM_BLOCKS + CONFIG_SHM_BLOCKS)
+
+/*
+ * Maximum internal shared memory block name length in chars
+ * probably taking the same number as SHM name size make sense at this stage
+ */
+#define ISHM_NAME_MAXLEN 128
+
+/*
+ * Linux underlying file name: <directory>/odp-<odp_pid>-ishm-<name>
+ * The <name> part may be replaced by a sequence number if no specific
+ * name is given at reserve time
+ * <directory> is either /dev/shm or the hugepagefs mount point for default
+ * size.
+ * (searched at init time)
+ */
+#define ISHM_FILENAME_MAXLEN (ISHM_NAME_MAXLEN + 64)
+#define ISHM_FILENAME_FORMAT "%s/odp-%d-ishm-%s"
+#define ISHM_FILENAME_NORMAL_PAGE_DIR "/dev/shm"
+#define _ODP_FILES_FMT "odp-%d-"
+
+/*
+ * when the memory is to be shared with an external entity (such as another
+ * ODP instance or an OS process not part of this ODP instance) then a
+ * export file is created describing the exported memory: this defines the
+ * location and the filename format of this description file
+ */
+#define ISHM_EXPTNAME_FORMAT "%s/%s/odp-%d-shm-%s"
+
+/*
+ * At worse case the virtual space gets so fragmented that there is
+ * a unallocated fragment between each allocated fragment:
+ * In that case, the number of fragments to take care of is twice the
+ * number of ISHM blocks + 1.
+ */
+#define ISHM_NB_FRAGMNTS (ISHM_MAX_NB_BLOCKS * 2 + 1)
+
+/*
+ * when a memory block is to be exported outside its ODP instance,
+ * an block 'attribute file' is created in /dev/shm/odp-<pid>-shm-<name>.
+ * The information given in this file is according to the following:
+ */
+#define EXPORT_FILE_LINE1_FMT "ODP exported shm block info:"
+#define EXPORT_FILE_LINE2_FMT "ishm_blockname: %s"
+#define EXPORT_FILE_LINE3_FMT "file: %s"
+#define EXPORT_FILE_LINE4_FMT "length: %" PRIu64
+#define EXPORT_FILE_LINE5_FMT "flags: %" PRIu32
+#define EXPORT_FILE_LINE6_FMT "user_length: %" PRIu64
+#define EXPORT_FILE_LINE7_FMT "user_flags: %" PRIu32
+#define EXPORT_FILE_LINE8_FMT "align: %" PRIu32
+#define EXPORT_FILE_LINE9_FMT "offset: %" PRIu64
+
+/*
+ * A fragment describes a piece of the shared virtual address space,
+ * and is allocated only when allocation is done with the _ODP_ISHM_SINGLE_VA
+ * flag:
+ * A fragment is said to be used when it actually does represent some
+ * portion of the virtual address space, and is said to be unused when
+ * it does not (so at start, one single fragment is used -describing the
+ * whole address space as unallocated-, and all others are unused).
+ * Fragments get used as address space fragmentation increases.
+ * A fragment is allocated if the piece of address space it
+ * describes is actually used by a shared memory block.
+ * Allocated fragments get their block_index set >=0.
+ */
+typedef struct ishm_fragment {
+ struct ishm_fragment *prev; /* not used when the fragment is unused */
+ struct ishm_fragment *next;
+ void *start; /* start of segment (VA) */
+ uintptr_t len; /* length of segment. multiple of page size */
+ int block_index; /* -1 for unallocated fragments */
+} ishm_fragment_t;
+
+/*
+ * A block describes a piece of reserved memory: Any successful ishm_reserve()
+ * will allocate a block. A ishm_reserve() with the _ODP_ISHM_SINGLE_VA flag set
+ * will allocate both a block and a fragment.
+ * Blocks contain only global data common to all processes.
+ */
+typedef enum {UNKNOWN, HUGE, NORMAL, EXTERNAL, CACHED} huge_flag_t;
+typedef struct ishm_block {
+ char name[ISHM_NAME_MAXLEN]; /* name for the ishm block (if any) */
+ char filename[ISHM_FILENAME_MAXLEN]; /* name of the .../odp-* file */
+ char exptname[ISHM_FILENAME_MAXLEN]; /* name of the export file */
+ uint32_t user_flags; /* any flags the user want to remember. */
+ uint32_t flags; /* block creation flags. */
+ uint32_t external_fd:1; /* block FD was externally provided */
+ uint64_t user_len; /* length, as requested at reserve time. */
+ uint64_t offset; /* offset from beginning of the fd */
+ void *start; /* only valid if _ODP_ISHM_SINGLE_VA is set*/
+ uint64_t len; /* length. multiple of page size. 0 if free*/
+ ishm_fragment_t *fragment; /* used when _ODP_ISHM_SINGLE_VA is used */
+ huge_flag_t huge; /* page type: external means unknown here. */
+ uint64_t seq; /* sequence number, incremented on alloc and free */
+ uint64_t refcnt;/* number of linux processes mapping this block */
+} ishm_block_t;
+
+/*
+ * Table of blocks describing allocated internal shared memory
+ * This table is visible to every ODP thread (linux process or pthreads).
+ * (it is allocated shared at odp init time and is therefore inherited by all)
+ * Table index is used as handle, so it cannot move!. Entry is regarded as
+ * free when len==0
+ */
+typedef struct {
+ odp_spinlock_t lock;
+ uint64_t dev_seq; /* used when creating device names */
+ /* limit for reserving memory using huge pages */
+ uint64_t huge_page_limit;
+ uint32_t odpthread_cnt; /* number of running ODP threads */
+ ishm_block_t block[ISHM_MAX_NB_BLOCKS];
+ void *single_va_start; /* start of single VA memory */
+ int single_va_fd; /* single VA memory file descriptor */
+ odp_bool_t single_va_huge; /* single VA memory from huge pages */
+ char single_va_filename[ISHM_FILENAME_MAXLEN];
+} ishm_table_t;
+static ishm_table_t *ishm_tbl;
+
+/*
+ * Process local table containing the list of (believed) allocated blocks seen
+ * from the current process. There is one such table per linux process. linux
+ * threads within a process shares this table.
+ * The contents within this table may become obsolete when other processes
+ * reserve/free ishm blocks. This is what the procsync() function
+ * catches by comparing the block sequence number with the one in this table.
+ * This table is filled at ishm_reserve and ishm_lookup time.
+ * Entries are removed at ishm_free or procsync time.
+ * Note that flags and len are present in this table and seems to be redundant
+ * with those present in the ishm block table: but this is not fully true:
+ * When ishm_sync() detects obsolete mappings and tries to remove them,
+ * the entry in the ishm block table is then obsolete, and the values which are
+ * found in this table must be used to perform the ummap.
+ * (and the values in the block tables are needed at lookup time...)
+ */
+typedef struct {
+ int thrd_refcnt; /* number of pthreads in this process, really */
+ struct {
+ int block_index; /* entry in the ishm_tbl */
+ uint32_t flags; /* flags used at creation time */
+ uint64_t seq;
+ void *start; /* start of block (VA) */
+ uint64_t len; /* length of block. multiple of page size */
+ int fd; /* file descriptor used for this block */
+ } entry[ISHM_MAX_NB_BLOCKS];
+ int nb_entries;
+} ishm_proctable_t;
+static ishm_proctable_t *ishm_proctable;
+
+/*
+ * Table of fragments describing the common virtual address space:
+ * This table is visible to every ODP thread (linux process or pthreads).
+ * (it is allocated at odp init time and is therefore inherited by all)
+ */
+typedef struct {
+ ishm_fragment_t fragment[ISHM_NB_FRAGMNTS];
+ ishm_fragment_t *used_fragmnts; /* ordered by increasing start addr */
+ ishm_fragment_t *unused_fragmnts;
+} ishm_ftable_t;
+static ishm_ftable_t *ishm_ftbl;
+
+struct huge_page_cache {
+ uint64_t len;
+ int max_fds; /* maximum amount requested of pre-allocated huge pages */
+ int total; /* amount of actually pre-allocated huge pages */
+ int idx; /* retrieve fd[idx] to get a free file descriptor */
+ int fd[]; /* list of file descriptors */
+};
+
+static struct huge_page_cache *hpc;
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* prototypes: */
+static void procsync(void);
+
+static int hp_create_file(uint64_t len, const char *filename)
+{
+ int fd;
+ int ret;
+ void *addr;
+
+ if (len <= 0) {
+ _ODP_ERR("Length is wrong\n");
+ return -1;
+ }
+
+ fd = open(filename, O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ _ODP_ERR("Could not create cache file %s\n", filename);
+ return -1;
+ }
+
+ /* remove file from file system */
+ unlink(filename);
+
+ ret = fallocate(fd, 0, 0, len);
+ if (ret == -1) {
+ if (errno == ENOTSUP) {
+ _ODP_DBG("fallocate() not supported\n");
+ ret = ftruncate(fd, len);
+ }
+
+ if (ret == -1) {
+ _ODP_ERR("memory allocation failed: fd=%d, err=%s.\n",
+ fd, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ /* commit huge page */
+ addr = _odp_ishmphy_map(fd, len, 0, 0);
+ if (addr == NULL) {
+ /* no more pages available */
+ close(fd);
+ return -1;
+ }
+ _odp_ishmphy_unmap(addr, len, 0);
+
+ _ODP_DBG("Created HP cache file %s, fd: %d\n", filename, fd);
+
+ return fd;
+}
+
+static void hp_init(void)
+{
+ char filename[ISHM_FILENAME_MAXLEN];
+ char dir[ISHM_FILENAME_MAXLEN];
+ int count;
+ void *addr;
+
+ if (!_odp_libconfig_lookup_ext_int("shm", NULL, "num_cached_hp",
+ &count)) {
+ return;
+ }
+
+ if (count <= 0)
+ return;
+
+ _ODP_DBG("Init HP cache with up to %d pages\n", count);
+
+ if (!odp_global_ro.hugepage_info.default_huge_page_dir) {
+ _ODP_ERR("No huge page dir\n");
+ return;
+ }
+
+ snprintf(dir, ISHM_FILENAME_MAXLEN, "%s/%s",
+ odp_global_ro.hugepage_info.default_huge_page_dir,
+ odp_global_ro.uid);
+
+ if (mkdir(dir, 0744) != 0) {
+ if (errno != EEXIST) {
+ _ODP_ERR("Failed to create dir: %s\n", strerror(errno));
+ return;
+ }
+ }
+
+ snprintf(filename, ISHM_FILENAME_MAXLEN,
+ "%s/odp-%d-ishm_cached",
+ dir,
+ odp_global_ro.main_pid);
+
+ addr = mmap(NULL,
+ sizeof(struct huge_page_cache) + sizeof(int) * count,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ _ODP_ERR("Unable to mmap memory for huge page cache\n.");
+ return;
+ }
+
+ hpc = addr;
+
+ hpc->max_fds = count;
+ hpc->total = 0;
+ hpc->idx = -1;
+ hpc->len = odp_sys_huge_page_size();
+
+ for (int i = 0; i < count; ++i) {
+ int fd;
+
+ fd = hp_create_file(hpc->len, filename);
+ if (fd == -1) {
+ do {
+ hpc->fd[i++] = -1;
+ } while (i < count);
+ break;
+ }
+ hpc->total++;
+ hpc->fd[i] = fd;
+ }
+ hpc->idx = hpc->total - 1;
+
+ _ODP_DBG("HP cache has %d huge pages of size 0x%08" PRIx64 "\n",
+ hpc->total, hpc->len);
+}
+
+static void hp_term(void)
+{
+ if (NULL == hpc)
+ return;
+
+ for (int i = 0; i < hpc->total; i++) {
+ if (hpc->fd[i] != -1)
+ close(hpc->fd[i]);
+ }
+
+ hpc->total = 0;
+ hpc->idx = -1;
+ hpc->len = 0;
+}
+
+static int hp_get_cached(uint64_t len)
+{
+ int fd;
+
+ if (hpc == NULL)
+ return -1;
+
+ if (hpc->idx < 0 || len != hpc->len)
+ return -1;
+
+ fd = hpc->fd[hpc->idx];
+ hpc->fd[hpc->idx--] = -1;
+
+ return fd;
+}
+
+static int hp_put_cached(int fd)
+{
+ if (hpc == NULL) {
+ _ODP_ERR("Bad hpc state\n");
+ return -1;
+ }
+
+ if (odp_unlikely((hpc->idx + 1) >= hpc->total)) {
+ _ODP_ERR("Trying to put more FD than allowed: %d\n", fd);
+ return -1;
+ }
+
+ hpc->idx++;
+ hpc->fd[hpc->idx] = fd;
+
+ return 0;
+}
+
+/*
+ * Take a piece of the preallocated virtual space to fit "size" bytes.
+ * (best fit). Size must be rounded up to an integer number of pages size.
+ * Possibly split the fragment to keep track of remaining space.
+ * Returns the allocated fragment (best_fragment) and the corresponding address.
+ * External caller must ensure mutex before the call!
+ */
+static void *alloc_fragment(uintptr_t size, int block_index, intptr_t align,
+ ishm_fragment_t **best_fragmnt)
+{
+ ishm_fragment_t *fragmnt;
+ *best_fragmnt = NULL;
+ ishm_fragment_t *rem_fragmnt;
+ uintptr_t border;/* possible start of new fragment (next alignment) */
+ intptr_t left; /* room remaining after, if the segment is allocated */
+ uintptr_t remainder = odp_global_ro.shm_max_memory;
+
+ /*
+ * search for the best bit, i.e. search for the unallocated fragment
+ * would give less remainder if the new fragment was allocated within
+ * it:
+ */
+ for (fragmnt = ishm_ftbl->used_fragmnts;
+ fragmnt; fragmnt = fragmnt->next) {
+ /* skip allocated segment: */
+ if (fragmnt->block_index >= 0)
+ continue;
+ /* skip too short segment: */
+ border = ((uintptr_t)fragmnt->start + align - 1) & (-align);
+ left =
+ ((uintptr_t)fragmnt->start + fragmnt->len) - (border + size);
+ if (left < 0)
+ continue;
+ /* remember best fit: */
+ if ((uintptr_t)left < remainder) {
+ remainder = left; /* best, so far */
+ *best_fragmnt = fragmnt;
+ }
+ }
+
+ if (!(*best_fragmnt)) {
+ _ODP_ERR("Out of single VA memory. Try increasing "
+ "'shm.single_va_size_kb' in ODP config.\n");
+ return NULL;
+ }
+
+ (*best_fragmnt)->block_index = block_index;
+ border = ((uintptr_t)(*best_fragmnt)->start + align - 1) & (-align);
+
+ /*
+ * if there is room between previous fragment and new one, (due to
+ * alignment requirement) then fragment (split) the space between
+ * the end of the previous fragment and the beginning of the new one:
+ */
+ if (border - (uintptr_t)(*best_fragmnt)->start > 0) {
+ /* fragment space, i.e. take a new fragment descriptor... */
+ rem_fragmnt = ishm_ftbl->unused_fragmnts;
+ if (!rem_fragmnt) {
+ _ODP_ERR("unable to get shmem fragment descriptor!\n.");
+ return NULL;
+ }
+ ishm_ftbl->unused_fragmnts = rem_fragmnt->next;
+
+ /* and link it between best_fragmnt->prev and best_fragmnt */
+ if ((*best_fragmnt)->prev)
+ (*best_fragmnt)->prev->next = rem_fragmnt;
+ else
+ ishm_ftbl->used_fragmnts = rem_fragmnt;
+ rem_fragmnt->prev = (*best_fragmnt)->prev;
+ (*best_fragmnt)->prev = rem_fragmnt;
+ rem_fragmnt->next = (*best_fragmnt);
+
+ /* update length: rem_fragmnt getting space before border */
+ rem_fragmnt->block_index = -1;
+ rem_fragmnt->start = (*best_fragmnt)->start;
+ rem_fragmnt->len = border - (uintptr_t)(*best_fragmnt)->start;
+ (*best_fragmnt)->start =
+ (void *)((uintptr_t)rem_fragmnt->start + rem_fragmnt->len);
+ (*best_fragmnt)->len -= rem_fragmnt->len;
+ }
+
+ /* if this was a perfect fit, i.e. no free space follows, we are done */
+ if (remainder == 0)
+ return (*best_fragmnt)->start;
+
+ /* otherwise, fragment space, i.e. take a new fragment descriptor... */
+ rem_fragmnt = ishm_ftbl->unused_fragmnts;
+ if (!rem_fragmnt) {
+ _ODP_ERR("unable to get shmem fragment descriptor!\n.");
+ return (*best_fragmnt)->start;
+ }
+ ishm_ftbl->unused_fragmnts = rem_fragmnt->next;
+
+ /* ... double link it... */
+ rem_fragmnt->next = (*best_fragmnt)->next;
+ rem_fragmnt->prev = (*best_fragmnt);
+ if ((*best_fragmnt)->next)
+ (*best_fragmnt)->next->prev = rem_fragmnt;
+ (*best_fragmnt)->next = rem_fragmnt;
+
+ /* ... and keep track of the remainder */
+ (*best_fragmnt)->len = size;
+ rem_fragmnt->len = remainder;
+ rem_fragmnt->start = (void *)((char *)(*best_fragmnt)->start + size);
+ rem_fragmnt->block_index = -1;
+
+ return (*best_fragmnt)->start;
+}
+
+/*
+ * Free a portion of virtual space.
+ * Possibly defragment, if the freed fragment is adjacent to another
+ * free virtual fragment.
+ * External caller must ensure mutex before the call!
+ */
+static void free_fragment(ishm_fragment_t *fragmnt)
+{
+ ishm_fragment_t *prev_f;
+ ishm_fragment_t *next_f;
+
+ /* sanity check */
+ if (!fragmnt)
+ return;
+
+ prev_f = fragmnt->prev;
+ next_f = fragmnt->next;
+
+ /* free the fragment */
+ fragmnt->block_index = -1;
+
+ /* check if the previous fragment is also free: if so, defragment */
+ if (prev_f && (prev_f->block_index < 0)) {
+ fragmnt->start = prev_f->start;
+ fragmnt->len += prev_f->len;
+ if (prev_f->prev) {
+ prev_f->prev->next = fragmnt;
+ } else {
+ if (ishm_ftbl->used_fragmnts == prev_f)
+ ishm_ftbl->used_fragmnts = fragmnt;
+ else
+ _ODP_ERR("corrupted fragment list!.\n");
+ }
+ fragmnt->prev = prev_f->prev;
+
+ /* put removed fragment in free list */
+ prev_f->prev = NULL;
+ prev_f->next = ishm_ftbl->unused_fragmnts;
+ ishm_ftbl->unused_fragmnts = prev_f;
+ }
+
+ /* check if the next fragment is also free: if so, defragment */
+ if (next_f && (next_f->block_index < 0)) {
+ fragmnt->len += next_f->len;
+ if (next_f->next)
+ next_f->next->prev = fragmnt;
+ fragmnt->next = next_f->next;
+
+ /* put removed fragment in free list */
+ next_f->prev = NULL;
+ next_f->next = ishm_ftbl->unused_fragmnts;
+ ishm_ftbl->unused_fragmnts = next_f;
+ }
+}
+
+static char *create_seq_string(char *output, size_t size)
+{
+ snprintf(output, size, "%08" PRIu64, ishm_tbl->dev_seq++);
+
+ return output;
+}
+
+static int create_export_file(ishm_block_t *new_block, const char *name,
+ uint64_t len, uint32_t flags, uint32_t align,
+ odp_bool_t single_va, uint64_t offset)
+{
+ FILE *export_file;
+
+ snprintf(new_block->exptname, ISHM_FILENAME_MAXLEN,
+ ISHM_EXPTNAME_FORMAT,
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid,
+ odp_global_ro.main_pid,
+ name);
+ export_file = fopen(new_block->exptname, "w");
+ if (export_file == NULL) {
+ _ODP_ERR("open failed: err=%s.\n", strerror(errno));
+ new_block->exptname[0] = 0;
+ return -1;
+ }
+
+ fprintf(export_file, EXPORT_FILE_LINE1_FMT "\n");
+ fprintf(export_file, EXPORT_FILE_LINE2_FMT "\n", new_block->name);
+ if (single_va)
+ fprintf(export_file, EXPORT_FILE_LINE3_FMT "\n",
+ ishm_tbl->single_va_filename);
+ else
+ fprintf(export_file, EXPORT_FILE_LINE3_FMT "\n",
+ new_block->filename);
+
+ fprintf(export_file, EXPORT_FILE_LINE4_FMT "\n", len);
+ fprintf(export_file, EXPORT_FILE_LINE5_FMT "\n", flags);
+ fprintf(export_file, EXPORT_FILE_LINE6_FMT "\n",
+ new_block->user_len);
+ fprintf(export_file, EXPORT_FILE_LINE7_FMT "\n",
+ new_block->user_flags);
+ fprintf(export_file, EXPORT_FILE_LINE8_FMT "\n", align);
+ fprintf(export_file, EXPORT_FILE_LINE9_FMT "\n", offset);
+
+ fclose(export_file);
+
+ return 0;
+}
+
+/*
+ * Create file with size len. returns -1 on error
+ * Creates a file to /dev/shm/odp-<pid>-<sequence_or_name> (for normal pages)
+ * or /mnt/huge/odp-<pid>-<sequence_or_name> (for huge pages).
+ * Return the new file descriptor, or -1 on error.
+ */
+static int create_file(int block_index, huge_flag_t huge, uint64_t len,
+ uint32_t flags, uint32_t align, odp_bool_t single_va)
+{
+ char *name;
+ int fd;
+ ishm_block_t *new_block = NULL; /* entry in the main block table */
+ char seq_string[ISHM_FILENAME_MAXLEN]; /* used to construct filename*/
+ char filename[ISHM_FILENAME_MAXLEN]; /* filename in /dev/shm or
+ * /mnt/huge */
+ int oflag = O_RDWR | O_CREAT | O_TRUNC; /* flags for open */
+ char dir[ISHM_FILENAME_MAXLEN];
+ int ret;
+
+ /* No ishm_block_t for the master single VA memory file */
+ if (single_va) {
+ name = (char *)(uintptr_t)"single_va";
+ } else {
+ new_block = &ishm_tbl->block[block_index];
+ name = new_block->name;
+ if (!name || !name[0])
+ name = create_seq_string(seq_string,
+ ISHM_FILENAME_MAXLEN);
+ }
+
+ /* huge dir must be known to create files there!: */
+ if ((huge == HUGE) &&
+ (!odp_global_ro.hugepage_info.default_huge_page_dir))
+ return -1;
+
+ if (huge == HUGE)
+ snprintf(dir, ISHM_FILENAME_MAXLEN, "%s/%s",
+ odp_global_ro.hugepage_info.default_huge_page_dir,
+ odp_global_ro.uid);
+ else
+ snprintf(dir, ISHM_FILENAME_MAXLEN, "%s/%s",
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid);
+
+ snprintf(filename, ISHM_FILENAME_MAXLEN, ISHM_FILENAME_FORMAT, dir,
+ odp_global_ro.main_pid, name);
+
+ mkdir(dir, 0744);
+
+ fd = open(filename, oflag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ if (huge != HUGE)
+ _ODP_ERR("Normal page open failed: file=%s, "
+ "err=\"%s\"\n", filename, strerror(errno));
+ return -1;
+ }
+
+ ret = fallocate(fd, 0, 0, len);
+ if (ret == -1) {
+ if (errno == ENOTSUP) {
+ _ODP_DBG("fallocate() not supported\n");
+ ret = ftruncate(fd, len);
+ }
+
+ if (ret == -1) {
+ _ODP_ERR("%s memory allocation failed: fd=%d, file=%s, "
+ "err=\"%s\"\n", (huge == HUGE) ? "Huge page" :
+ "Normal page", fd, filename, strerror(errno));
+ close(fd);
+ unlink(filename);
+ return -1;
+ }
+ }
+
+ /* No export file is created since this is only for internal use.*/
+ if (single_va) {
+ snprintf(ishm_tbl->single_va_filename, ISHM_FILENAME_MAXLEN,
+ "%s", filename);
+ return fd;
+ }
+
+ /* if _ODP_ISHM_EXPORT is set, create a description file for
+ * external ref:
+ */
+ if (flags & _ODP_ISHM_EXPORT) {
+ memcpy(new_block->filename, filename, ISHM_FILENAME_MAXLEN);
+
+ create_export_file(new_block, name, len, flags, align, false,
+ 0);
+ } else {
+ new_block->exptname[0] = 0;
+ /* remove the file from the filesystem, keeping its fd open */
+ unlink(filename);
+ }
+
+ return fd;
+}
+
+/* delete the files related to a given ishm block: */
+static void delete_file(ishm_block_t *block)
+{
+ /* remove the .../odp-* file, unless fd was external or single va */
+ if (block->filename[0] != 0 &&
+ strcmp(block->filename, ishm_tbl->single_va_filename))
+ unlink(block->filename);
+ /* also remove possible description file (if block was exported): */
+ if (block->exptname[0] != 0)
+ unlink(block->exptname);
+}
+
+/*
+ * Performs the mapping.
+ * Sets fd, and returns the mapping address. Not to be used with
+ * _ODP_ISHM_SINGLE_VA blocks.
+ * Mutex must be assured by the caller.
+ */
+static void *do_map(int block_index, uint64_t len, uint32_t align,
+ uint64_t offset, uint32_t flags, huge_flag_t huge, int *fd)
+{
+ ishm_block_t *new_block; /* entry in the main block table */
+ void *mapped_addr;
+
+ _ODP_ASSERT(!(flags & _ODP_ISHM_SINGLE_VA));
+
+ new_block = &ishm_tbl->block[block_index];
+
+ /*
+ * Creates a file to /dev/shm/odp-<pid>-<sequence> (for normal pages)
+ * or /mnt/huge/odp-<pid>-<sequence> (for huge pages)
+ * unless a fd was already given
+ */
+ if (*fd < 0) {
+ *fd = create_file(block_index, huge, len, flags, align, false);
+ if (*fd < 0)
+ return NULL;
+ } else {
+ new_block->filename[0] = 0;
+ }
+
+ /* try to mmap: */
+ mapped_addr = _odp_ishmphy_map(*fd, len, offset, flags);
+ if (mapped_addr == NULL) {
+ if (!new_block->external_fd) {
+ close(*fd);
+ *fd = -1;
+ delete_file(new_block);
+ }
+ return NULL;
+ }
+
+ return mapped_addr;
+}
+
+/*
+ * Allocate block from pre-reserved single VA memory
+ */
+static void *alloc_single_va(const char *name, int new_index, uint64_t size,
+ uint32_t align, uint32_t flags, int *fd,
+ uint64_t *len_out)
+{
+ uint64_t len;
+ uint64_t page_sz;
+ char *file_name = (char *)(uintptr_t)name;
+ void *addr;
+ ishm_block_t *new_block = &ishm_tbl->block[new_index];
+ ishm_fragment_t *fragment = NULL;
+ char seq_string[ISHM_FILENAME_MAXLEN];
+
+ if (!file_name || !file_name[0])
+ file_name = create_seq_string(seq_string, ISHM_FILENAME_MAXLEN);
+
+ /* Common fd for all single VA blocks */
+ *fd = ishm_tbl->single_va_fd;
+
+ if (ishm_tbl->single_va_huge) {
+ page_sz = odp_sys_huge_page_size();
+ new_block->huge = HUGE;
+ } else {
+ page_sz = odp_sys_page_size();
+ new_block->huge = NORMAL;
+ }
+ new_block->filename[0] = 0;
+
+ len = (size + (page_sz - 1)) & (-page_sz);
+
+ if (align < page_sz)
+ align = page_sz;
+
+ /* Allocate memory from the pre-reserved single VA space */
+ addr = alloc_fragment(len, new_index, align, &fragment);
+ if (!addr) {
+ _ODP_ERR("alloc_fragment failed.\n");
+ return NULL;
+ }
+ new_block->fragment = fragment;
+
+ /* Create export info file */
+ if (flags & _ODP_ISHM_EXPORT) {
+ uint64_t offset = (uintptr_t)addr -
+ (uintptr_t)ishm_tbl->single_va_start;
+ memcpy(new_block->filename, ishm_tbl->single_va_filename,
+ ISHM_FILENAME_MAXLEN);
+
+ create_export_file(new_block, file_name, len, flags, align,
+ true, offset);
+ } else {
+ new_block->exptname[0] = 0;
+ }
+
+ *len_out = len;
+ return addr;
+}
+
+/*
+ * Performs an extra mapping (for a process trying to see an existing block
+ * i.e. performing a lookup). Not to be used with _ODP_ISHM_SINGLE_VA blocks.
+ * Mutex must be assured by the caller.
+ */
+static void *do_remap(int block_index, int fd)
+{
+ void *mapped_addr;
+ uint64_t len;
+ uint64_t offset;
+ uint32_t flags;
+
+ len = ishm_tbl->block[block_index].len;
+ offset = ishm_tbl->block[block_index].offset;
+ flags = ishm_tbl->block[block_index].flags;
+
+ _ODP_ASSERT(!(flags & _ODP_ISHM_SINGLE_VA));
+
+ /* try to mmap: */
+ mapped_addr = _odp_ishmphy_map(fd, len, offset, flags);
+
+ if (mapped_addr == NULL)
+ return NULL;
+
+ return mapped_addr;
+}
+
+/*
+ * Performs unmapping, possibly freeing a pre-reserved single VA memory
+ * fragment, if the _ODP_ISHM_SINGLE_VA flag was set at alloc time.
+ * Mutex must be assured by the caller.
+ */
+static int do_unmap(void *start, uint64_t size, uint32_t flags,
+ int block_index)
+{
+ int ret;
+
+ if (start)
+ ret = _odp_ishmphy_unmap(start, size, flags);
+ else
+ ret = 0;
+
+ if ((block_index >= 0) && (flags & _ODP_ISHM_SINGLE_VA)) {
+ /* mark reserved address space as free */
+ free_fragment(ishm_tbl->block[block_index].fragment);
+ }
+
+ return ret;
+}
+
+/*
+ * Search for a given used and allocated block name.
+ * (search is performed in the global ishm table)
+ * Returns the index of the found block (if any) or -1 if none.
+ * Mutex must be assured by the caller.
+ */
+static int find_block_by_name(const char *name)
+{
+ int i;
+
+ if (name == NULL || name[0] == 0)
+ return -1;
+
+ for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
+ if ((ishm_tbl->block[i].len) &&
+ (strcmp(name, ishm_tbl->block[i].name) == 0))
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Search a given ishm block in the process local table. Return its index
+ * in the process table or -1 if not found (meaning that the ishm table
+ * block index was not referenced in the process local table, i.e. the
+ * block is known by some other process, but not by the current process).
+ * Caller must assure mutex.
+ */
+static int procfind_block(int block_index)
+{
+ int i;
+
+ for (i = 0; i < ishm_proctable->nb_entries; i++) {
+ if (ishm_proctable->entry[i].block_index == block_index)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * Release the physical memory mapping for blocks which have been freed
+ * by other processes. Caller must ensure mutex.
+ * Mutex must be assured by the caller.
+ */
+static void procsync(void)
+{
+ int i = 0;
+ int last;
+ ishm_block_t *block;
+
+ last = ishm_proctable->nb_entries;
+ while (i < last) {
+ /* if the process sequence number doesn't match the main
+ * table seq number, this entry is obsolete
+ */
+ block = &ishm_tbl->block[ishm_proctable->entry[i].block_index];
+ if (ishm_proctable->entry[i].seq != block->seq) {
+ /* obsolete entry: free memory and remove proc entry */
+ if (ishm_proctable->entry[i].fd !=
+ ishm_tbl->single_va_fd)
+ close(ishm_proctable->entry[i].fd);
+ _odp_ishmphy_unmap(ishm_proctable->entry[i].start,
+ ishm_proctable->entry[i].len,
+ ishm_proctable->entry[i].flags);
+ ishm_proctable->entry[i] =
+ ishm_proctable->entry[--last];
+ } else {
+ i++;
+ }
+ }
+ ishm_proctable->nb_entries = last;
+}
+
+/*
+ * Free a block as described in block_free(), but
+ * considering whether to close the file descriptor or not, and
+ * whether to deregister from the fdserver.
+ */
+static int block_free_internal(int block_index, int close_fd, int deregister)
+{
+ int proc_index;
+ ishm_block_t *block; /* entry in the main block table*/
+ int last;
+ int ret = 0;
+
+ if ((block_index < 0) ||
+ (block_index >= ISHM_MAX_NB_BLOCKS) ||
+ (ishm_tbl->block[block_index].len == 0)) {
+ _ODP_ERR("Request to free an invalid block\n");
+ return -1;
+ }
+
+ block = &ishm_tbl->block[block_index];
+
+ proc_index = procfind_block(block_index);
+ if (proc_index >= 0) {
+ int fd = ishm_proctable->entry[proc_index].fd;
+
+ /* remove the mapping and possible fragment */
+ do_unmap(ishm_proctable->entry[proc_index].start,
+ block->len,
+ ishm_proctable->entry[proc_index].flags,
+ block_index);
+
+ /* close the related fd */
+ if (close_fd && (fd != ishm_tbl->single_va_fd)) {
+ if (block->huge == CACHED)
+ hp_put_cached(fd);
+ else
+ close(fd);
+ }
+
+ /* remove entry from process local table: */
+ last = ishm_proctable->nb_entries - 1;
+ ishm_proctable->entry[proc_index] = ishm_proctable->entry[last];
+ ishm_proctable->nb_entries = last;
+ } else {
+ /* just possibly free the fragment as no mapping exist here: */
+ do_unmap(NULL, 0, block->flags, block_index);
+ }
+
+ /* remove all files related to this block: */
+ if (close_fd)
+ delete_file(block);
+
+ /* deregister the file descriptor from the file descriptor server. */
+ if (deregister)
+ ret = _odp_fdserver_deregister_fd(FD_SRV_CTX_ISHM, block_index);
+
+ /* mark the block as free in the main block table: */
+ block->len = 0;
+
+ /* mark the change so other processes see this entry as obsolete: */
+ block->seq++;
+
+ return ret;
+}
+
+/*
+ * Allocate and map internal shared memory, or other objects:
+ * If a name is given, check that this name is not already in use.
+ * If ok, allocate a new shared memory block and map the
+ * provided fd in it (if fd >=0 was given).
+ * If no fd is provided, a shared memory file desc named
+ * /dev/shm/odp-<pid>-ishm-<name_or_sequence> is created and mapped.
+ * (the name is different for huge page file as they must be on hugepagefs)
+ * The function returns the index of the newly created block in the
+ * main block table (>=0) or -1 on error.
+ */
+int _odp_ishm_reserve(const char *name, uint64_t size, int fd,
+ uint32_t align, uint64_t offset, uint32_t flags,
+ uint32_t user_flags)
+{
+ int new_index; /* index in the main block table*/
+ ishm_block_t *new_block; /* entry in the main block table*/
+ uint64_t page_sz; /* normal page size. usually 4K*/
+ uint64_t page_hp_size; /* huge page size */
+ uint32_t hp_align;
+ uint64_t len = 0; /* mapped length */
+ void *addr = NULL; /* mapping address */
+ int new_proc_entry;
+ static int huge_error_printed; /* to avoid millions of error...*/
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+
+ /* update this process view... */
+ procsync();
+
+ /* Get system page sizes: page_hp_size is 0 if no huge page available*/
+ page_sz = odp_sys_page_size();
+ /* Use normal pages if ODP_SHM_NO_HP was used */
+ page_hp_size = (user_flags & ODP_SHM_NO_HP) ? 0 : odp_sys_huge_page_size();
+
+ /* grab a new entry: */
+ for (new_index = 0; new_index < ISHM_MAX_NB_BLOCKS; new_index++) {
+ if (ishm_tbl->block[new_index].len == 0) {
+ /* Found free block */
+ break;
+ }
+ }
+
+ /* check if we have reached the maximum number of allocation: */
+ if (new_index >= ISHM_MAX_NB_BLOCKS) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("ISHM_MAX_NB_BLOCKS limit reached!\n");
+ return -1;
+ }
+
+ new_block = &ishm_tbl->block[new_index];
+
+ /* save block name (if any given): */
+ if (name)
+ _odp_strcpy(new_block->name, name, ISHM_NAME_MAXLEN);
+ else
+ new_block->name[0] = 0;
+
+ new_block->offset = 0;
+
+ /* save user data: */
+ new_block->user_flags = user_flags;
+ new_block->user_len = size;
+
+ /* If a file descriptor is provided, get the real size and map: */
+ if (fd >= 0) {
+ new_block->external_fd = 1;
+ len = size;
+ /* note that the huge page flag is meaningless here as huge
+ * page is determined by the provided file descriptor: */
+ addr = do_map(new_index, len, align, offset, flags, EXTERNAL,
+ &fd);
+ if (addr == NULL) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("_ishm_reserve failed.\n");
+ return -1;
+ }
+ new_block->huge = EXTERNAL;
+ } else {
+ new_block->external_fd = 0;
+ new_block->huge = UNKNOWN;
+ }
+
+ /* Otherwise, Try first huge pages when possible and needed: */
+ if ((fd < 0) && page_hp_size && ((user_flags & ODP_SHM_HP) ||
+ size > ishm_tbl->huge_page_limit)) {
+ /* at least, alignment in VA should match page size, but user
+ * can request more: If the user requirement exceeds the page
+ * size then we have to make sure the block will be mapped at
+ * the same address every where, otherwise alignment may be
+ * wrong for some process */
+ hp_align = align;
+ if (hp_align <= page_hp_size)
+ hp_align = page_hp_size;
+ else
+ flags |= _ODP_ISHM_SINGLE_VA;
+
+ if (flags & _ODP_ISHM_SINGLE_VA)
+ goto use_single_va;
+
+ /* roundup to page size */
+ len = (size + (page_hp_size - 1)) & (-page_hp_size);
+
+ /* try pre-allocated pages */
+ fd = hp_get_cached(len);
+ if (fd != -1) {
+ /* do as if user provided a fd */
+ new_block->external_fd = 1;
+ addr = do_map(new_index, len, hp_align, 0, flags,
+ CACHED, &fd);
+ if (addr == NULL) {
+ _ODP_ERR("Could not use cached hp %d\n", fd);
+ hp_put_cached(fd);
+ fd = -1;
+ } else {
+ new_block->huge = CACHED;
+ }
+ }
+ if (fd == -1) {
+ addr = do_map(new_index, len, hp_align, 0, flags, HUGE,
+ &fd);
+
+ if (addr == NULL) {
+ if (!huge_error_printed) {
+ _ODP_WARN("No huge pages, fall back to normal pages. "
+ "Check: /proc/sys/vm/nr_hugepages.\n");
+ huge_error_printed = 1;
+ }
+ } else {
+ new_block->huge = HUGE;
+ }
+ }
+ }
+
+ /* Try normal pages if huge pages failed */
+ if (fd < 0) {
+ if (user_flags & ODP_SHM_HP) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("Unable to allocate memory from huge pages\n");
+ return -1;
+ }
+ /* at least, alignment in VA should match page size, but user
+ * can request more: If the user requirement exceeds the page
+ * size then we have to make sure the block will be mapped at
+ * the same address every where, otherwise alignment may be
+ * wrong for some process */
+ if (align <= odp_sys_page_size())
+ align = odp_sys_page_size();
+ else
+ flags |= _ODP_ISHM_SINGLE_VA;
+
+ if (flags & _ODP_ISHM_SINGLE_VA)
+ goto use_single_va;
+
+ /* roundup to page size */
+ len = (size + (page_sz - 1)) & (-page_sz);
+ addr = do_map(new_index, len, align, 0, flags, NORMAL, &fd);
+ new_block->huge = NORMAL;
+ }
+
+use_single_va:
+ /* Reserve memory from single VA space */
+ if (fd < 0 && (flags & _ODP_ISHM_SINGLE_VA))
+ addr = alloc_single_va(name, new_index, size, align, flags, &fd,
+ &len);
+
+ /* if neither huge pages or normal pages works, we cannot proceed: */
+ if ((fd < 0) || (addr == NULL) || (len == 0)) {
+ if (new_block->external_fd) {
+ if (new_block->huge == CACHED)
+ hp_put_cached(fd);
+ } else if (fd >= 0 && (fd != ishm_tbl->single_va_fd)) {
+ close(fd);
+ }
+ delete_file(new_block);
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("_ishm_reserve failed.\n");
+ return -1;
+ }
+
+ /* remember block data and increment block seq number to mark change */
+ new_block->len = len;
+ new_block->user_len = size;
+ new_block->flags = flags;
+ new_block->user_flags = user_flags;
+ new_block->seq++;
+ new_block->refcnt = 1;
+ new_block->start = addr; /* only for SINGLE_VA*/
+
+ /* the allocation succeeded: update the process local view */
+ new_proc_entry = ishm_proctable->nb_entries++;
+ ishm_proctable->entry[new_proc_entry].block_index = new_index;
+ ishm_proctable->entry[new_proc_entry].flags = flags;
+ ishm_proctable->entry[new_proc_entry].seq = new_block->seq;
+ ishm_proctable->entry[new_proc_entry].start = addr;
+ ishm_proctable->entry[new_proc_entry].len = len;
+ ishm_proctable->entry[new_proc_entry].fd = fd;
+
+ /* register the file descriptor to the file descriptor server. */
+ if (_odp_fdserver_register_fd(FD_SRV_CTX_ISHM, new_index, fd) == -1) {
+ block_free_internal(new_index, !new_block->external_fd, 0);
+ new_index = -1;
+ }
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return new_index;
+}
+
+/*
+ * Pre-reserve all single VA memory. Called only in global init.
+ */
+static void *reserve_single_va(uint64_t size, int *fd_out)
+{
+ uint64_t page_sz; /* normal page size. usually 4K*/
+ uint64_t page_hp_size; /* huge page size */
+ uint64_t len; /* mapped length */
+ int fd = -1;
+ void *addr = NULL;
+
+ /* Get system page sizes: page_hp_size is 0 if no huge page available*/
+ page_sz = odp_sys_page_size();
+ page_hp_size = odp_sys_huge_page_size();
+
+ /* Try first huge pages when possible and needed: */
+ if (page_hp_size && (size > page_sz)) {
+ /* roundup to page size */
+ len = (size + (page_hp_size - 1)) & (-page_hp_size);
+ fd = create_file(-1, HUGE, len, 0, 0, true);
+ if (fd >= 0) {
+ addr = _odp_ishmphy_reserve_single_va(len, fd);
+ if (!addr) {
+ close(fd);
+ unlink(ishm_tbl->single_va_filename);
+ fd = -1;
+ }
+ }
+ if (fd < 0)
+ _ODP_WARN("No huge pages, fall back to normal pages. "
+ "Check: /proc/sys/vm/nr_hugepages.\n");
+ ishm_tbl->single_va_huge = true;
+ }
+
+ /* Fall back to normal pages if necessary */
+ if (fd < 0) {
+ /* roundup to page size */
+ len = (size + (page_sz - 1)) & (-page_sz);
+
+ fd = create_file(-1, NORMAL, len, 0, 0, true);
+ if (fd >= 0)
+ addr = _odp_ishmphy_reserve_single_va(len, fd);
+ ishm_tbl->single_va_huge = false;
+ }
+
+ /* If neither huge pages or normal pages works, we cannot proceed: */
+ if ((fd < 0) || (len == 0) || !addr) {
+ if (fd >= 0) {
+ close(fd);
+ unlink(ishm_tbl->single_va_filename);
+ }
+ _ODP_ERR("Reserving single VA memory failed.\n");
+ return NULL;
+ }
+
+ *fd_out = fd;
+ return addr;
+}
+
+/*
+ * Try to map an memory block mapped by another ODP instance into the
+ * current ODP instance.
+ * returns 0 on success.
+ */
+int _odp_ishm_find_exported(const char *remote_name, pid_t external_odp_pid,
+ const char *local_name)
+{
+ char export_filename[ISHM_FILENAME_MAXLEN];
+ char blockname[ISHM_FILENAME_MAXLEN];
+ char filename[ISHM_FILENAME_MAXLEN];
+ FILE *export_file;
+ uint64_t len;
+ uint32_t flags;
+ uint64_t user_len;
+ uint64_t offset;
+ uint32_t user_flags;
+ uint32_t align;
+ int fd;
+ int block_index;
+
+ /* try to read the block description file: */
+ snprintf(export_filename, ISHM_FILENAME_MAXLEN,
+ ISHM_EXPTNAME_FORMAT,
+ odp_global_ro.shm_dir,
+ odp_global_ro.uid,
+ external_odp_pid,
+ remote_name);
+
+ export_file = fopen(export_filename, "r");
+
+ if (export_file == NULL) {
+ _ODP_ERR("Error opening %s.\n", export_filename);
+ return -1;
+ }
+
+ if (fscanf(export_file, EXPORT_FILE_LINE1_FMT " ") != 0)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE2_FMT " ", blockname) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE3_FMT " ", filename) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE4_FMT " ", &len) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE5_FMT " ", &flags) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE6_FMT " ", &user_len) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE7_FMT " ", &user_flags) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE8_FMT " ", &align) != 1)
+ goto error_exp_file;
+
+ if (fscanf(export_file, EXPORT_FILE_LINE9_FMT " ", &offset) != 1)
+ goto error_exp_file;
+
+ fclose(export_file);
+
+ /* now open the filename given in the description file: */
+ fd = open(filename, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd == -1) {
+ _ODP_ERR("open failed for %s: %s.\n", filename, strerror(errno));
+ return -1;
+ }
+
+ /* Clear the _ODP_ISHM_EXPORT flag so we don't export again. Single
+ * VA doesn't hold up after export. */
+ flags &= ~(uint32_t)_ODP_ISHM_EXPORT;
+ flags &= ~(uint32_t)_ODP_ISHM_SINGLE_VA;
+
+ /* reserve the memory, providing the opened file descriptor: */
+ block_index = _odp_ishm_reserve(local_name, len, fd, align, offset,
+ flags, 0);
+ if (block_index < 0) {
+ close(fd);
+ return block_index;
+ }
+
+ /* Offset is required to remap the block to other processes */
+ ishm_tbl->block[block_index].offset = offset;
+
+ /* set inherited info: */
+ ishm_tbl->block[block_index].user_flags = user_flags;
+ ishm_tbl->block[block_index].user_len = user_len;
+
+ return block_index;
+
+error_exp_file:
+ fclose(export_file);
+ _ODP_ERR("Error reading %s.\n", export_filename);
+ return -1;
+}
+
+/*
+ * Free and unmap internal shared memory:
+ * The file descriptor is closed and the .../odp-* file deleted,
+ * unless fd was externally provided at reserve() time.
+ * return 0 if OK, and -1 on error.
+ * Mutex must be assured by the caller.
+ */
+static int block_free(int block_index)
+{
+ return block_free_internal(block_index, 1, 1);
+}
+
+/*
+ * Free and unmap internal shared memory, identified by its block number:
+ * return -1 on error. 0 if OK.
+ */
+int _odp_ishm_free_by_index(int block_index)
+{
+ int ret;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ ret = block_free(block_index);
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return ret;
+}
+
+/*
+ * Lookup for an ishm shared memory, identified by its block index
+ * in the main ishm block table.
+ * Map this ishm area in the process VA (if not already present).
+ * Returns the block user address or NULL on error.
+ * Mutex must be assured by the caller.
+ */
+static void *block_lookup(int block_index)
+{
+ int proc_index;
+ int fd = -1;
+ ishm_block_t *block;
+ void *mapped_addr;
+ int new_entry;
+
+ if ((block_index < 0) ||
+ (block_index >= ISHM_MAX_NB_BLOCKS) ||
+ (ishm_tbl->block[block_index].len == 0)) {
+ _ODP_ERR("Request to lookup an invalid block\n");
+ return NULL;
+ }
+
+ /* search it in process table: if there, this process knows it already*/
+ proc_index = procfind_block(block_index);
+ if (proc_index >= 0)
+ return ishm_proctable->entry[proc_index].start;
+
+ /* this ishm is not known by this process, yet: we create the mapping.*/
+ fd = _odp_fdserver_lookup_fd(FD_SRV_CTX_ISHM, block_index);
+ if (fd < 0) {
+ _ODP_ERR("Could not find ishm file descriptor (BUG!)\n");
+ return NULL;
+ }
+
+ /* perform the mapping */
+ block = &ishm_tbl->block[block_index];
+
+ /* No need to remap single VA */
+ if (block->flags & _ODP_ISHM_SINGLE_VA)
+ mapped_addr = block->start;
+ else
+ mapped_addr = do_remap(block_index, fd);
+
+ if (mapped_addr == NULL) {
+ _ODP_ERR(" lookup: Could not map existing shared memory!\n");
+ return NULL;
+ }
+
+ /* the mapping succeeded: update the process local view */
+ new_entry = ishm_proctable->nb_entries++;
+ ishm_proctable->entry[new_entry].block_index = block_index;
+ ishm_proctable->entry[new_entry].flags = block->flags;
+ ishm_proctable->entry[new_entry].seq = block->seq;
+ ishm_proctable->entry[new_entry].start = mapped_addr;
+ ishm_proctable->entry[new_entry].len = block->len;
+ ishm_proctable->entry[new_entry].fd = fd;
+ block->refcnt++;
+
+ return mapped_addr;
+}
+
+/*
+ * Lookup for an ishm shared memory, identified by its block name.
+ * Return the block index, or -1 if the index does not match any known ishm
+ * blocks.
+ */
+int _odp_ishm_lookup_by_name(const char *name)
+{
+ int block_index;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ /* search the block in main ishm table: return -1 if not found: */
+ block_index = find_block_by_name(name);
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return block_index;
+}
+
+/*
+ * Returns the VA address of a given block. Maps this ishm area in the process
+ * VA (if not already present).
+ * Returns NULL if the block is unknown.
+ */
+void *_odp_ishm_address(int block_index)
+{
+ void *addr;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ addr = block_lookup(block_index);
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return addr;
+}
+
+int _odp_ishm_info(int block_index, _odp_ishm_info_t *info)
+{
+ int proc_index;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ if ((block_index < 0) ||
+ (block_index >= ISHM_MAX_NB_BLOCKS) ||
+ (ishm_tbl->block[block_index].len == 0)) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("Request for info on an invalid block\n");
+ return -1;
+ }
+
+ /* search it in process table: if not there, need to map*/
+ proc_index = procfind_block(block_index);
+ if (proc_index < 0) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return -1;
+ }
+
+ info->name = ishm_tbl->block[block_index].name;
+ info->addr = ishm_proctable->entry[proc_index].start;
+ info->size = ishm_tbl->block[block_index].user_len;
+ info->page_size = (ishm_tbl->block[block_index].huge == HUGE) ?
+ odp_sys_huge_page_size() : odp_sys_page_size();
+ info->flags = ishm_tbl->block[block_index].flags;
+ info->user_flags = ishm_tbl->block[block_index].user_flags;
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return 0;
+}
+
+static int do_odp_ishm_init_local(void)
+{
+ int i;
+ int block_index;
+
+ /*
+ * the ishm_process table is local to each linux process
+ * Check that no other linux threads (of same or ancestor processes)
+ * have already created the table, and create it if needed.
+ * We protect this with the general ishm lock to avoid
+ * init race condition of different running threads.
+ */
+ odp_spinlock_lock(&ishm_tbl->lock);
+ ishm_tbl->odpthread_cnt++; /* count ODPthread (pthread or process) */
+ if (!ishm_proctable) {
+ ishm_proctable = malloc(sizeof(ishm_proctable_t));
+ if (!ishm_proctable) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return -1;
+ }
+ memset(ishm_proctable, 0, sizeof(ishm_proctable_t));
+ }
+ if (syscall(SYS_gettid) != getpid())
+ ishm_proctable->thrd_refcnt++; /* new linux thread */
+ else
+ ishm_proctable->thrd_refcnt = 1;/* new linux process */
+
+ /*
+ * if this ODP thread is actually a new linux process, (as opposed
+ * to a pthread), i.e, we just forked, then all shmem blocks
+ * of the parent process are mapped into this child by inheritance.
+ * (The process local table is inherited as well). We hence have to
+ * increase the process refcount for each of the inherited mappings:
+ */
+ if (syscall(SYS_gettid) == getpid()) {
+ for (i = 0; i < ishm_proctable->nb_entries; i++) {
+ block_index = ishm_proctable->entry[i].block_index;
+ ishm_tbl->block[block_index].refcnt++;
+ }
+ }
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return 0;
+}
+
+/* remove all files staring with "odp-<pid>" from a directory "dir" */
+int _odp_ishm_cleanup_files(const char *dirpath)
+{
+ struct dirent *e;
+ DIR *dir;
+ char userdir[PATH_MAX];
+ char prefix[PATH_MAX];
+ char *fullpath;
+ int d_len;
+ int p_len;
+ int f_len;
+
+ snprintf(userdir, PATH_MAX, "%s/%s", dirpath, odp_global_ro.uid);
+ d_len = strlen(userdir);
+
+ dir = opendir(userdir);
+ if (!dir) {
+ /* ok if the dir does not exist. no much to delete then! */
+ _ODP_DBG("opendir failed for %s: %s\n", userdir, strerror(errno));
+ return 0;
+ }
+ snprintf(prefix, PATH_MAX, _ODP_FILES_FMT, odp_global_ro.main_pid);
+ p_len = strlen(prefix);
+ while ((e = readdir(dir)) != NULL) {
+ if (strncmp(e->d_name, prefix, p_len) == 0) {
+ f_len = strlen(e->d_name);
+ fullpath = malloc(d_len + f_len + 2);
+ if (fullpath == NULL) {
+ closedir(dir);
+ return -1;
+ }
+ snprintf(fullpath, PATH_MAX, "%s/%s",
+ userdir, e->d_name);
+ _ODP_DBG("deleting obsolete file: %s\n", fullpath);
+ if (unlink(fullpath))
+ _ODP_ERR("unlink failed for %s: %s\n", fullpath, strerror(errno));
+ free(fullpath);
+ }
+ }
+ closedir(dir);
+
+ return 0;
+}
+
+int _odp_ishm_init_global(const odp_init_t *init)
+{
+ void *addr;
+ void *spce_addr = NULL;
+ int i;
+ int val_kb;
+ uid_t uid;
+ char *hp_dir = odp_global_ro.hugepage_info.default_huge_page_dir;
+ uint64_t max_memory;
+ uint64_t internal;
+ uint64_t huge_page_limit;
+
+ if (!_odp_libconfig_lookup_ext_int("shm", NULL, "single_va_size_kb",
+ &val_kb)) {
+ _ODP_ERR("Unable to read single VA size from config\n");
+ return -1;
+ }
+
+ _ODP_DBG("Shm single VA size: %dkB\n", val_kb);
+
+ max_memory = (uint64_t)val_kb * 1024;
+ internal = max_memory / 8;
+
+ if (!_odp_libconfig_lookup_ext_int("shm", NULL, "huge_page_limit_kb",
+ &val_kb)) {
+ _ODP_ERR("Unable to read huge page usage limit from config\n");
+ return -1;
+ }
+ huge_page_limit = (uint64_t)val_kb * 1024;
+
+ _ODP_DBG("Shm huge page usage limit: %dkB\n", val_kb);
+
+ /* user requested memory size + some extra for internal use */
+ if (init && init->shm.max_memory)
+ max_memory = init->shm.max_memory + internal;
+
+ odp_global_ro.shm_max_memory = max_memory;
+ odp_global_ro.shm_max_size = max_memory - internal;
+ odp_global_ro.main_pid = getpid();
+ odp_global_ro.shm_dir = getenv("ODP_SHM_DIR");
+ if (odp_global_ro.shm_dir) {
+ odp_global_ro.shm_dir_from_env = 1;
+ } else {
+ odp_global_ro.shm_dir =
+ calloc(1, sizeof(ISHM_FILENAME_NORMAL_PAGE_DIR));
+ sprintf(odp_global_ro.shm_dir, "%s",
+ ISHM_FILENAME_NORMAL_PAGE_DIR);
+ odp_global_ro.shm_dir_from_env = 0;
+ }
+
+ _ODP_DBG("ishm: using dir %s\n", odp_global_ro.shm_dir);
+
+ uid = getuid();
+ snprintf(odp_global_ro.uid, UID_MAXLEN, "%d",
+ uid);
+
+ if ((syscall(SYS_gettid)) != odp_global_ro.main_pid) {
+ _ODP_ERR("ishm init must be performed by the main "
+ "ODP process!\n.");
+ return -1;
+ }
+
+ if (!hp_dir) {
+ _ODP_DBG("NOTE: No support for huge pages\n");
+ } else {
+ _ODP_DBG("Huge pages mount point is: %s\n", hp_dir);
+ _odp_ishm_cleanup_files(hp_dir);
+ }
+
+ _odp_ishm_cleanup_files(odp_global_ro.shm_dir);
+
+ /* allocate space for the internal shared mem block table: */
+ addr = mmap(NULL, sizeof(ishm_table_t),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ _ODP_ERR("unable to mmap the main block table\n.");
+ goto init_glob_err1;
+ }
+ ishm_tbl = addr;
+ memset(ishm_tbl, 0, sizeof(ishm_table_t));
+ ishm_tbl->dev_seq = 0;
+ ishm_tbl->odpthread_cnt = 0;
+ ishm_tbl->huge_page_limit = huge_page_limit;
+ odp_spinlock_init(&ishm_tbl->lock);
+
+ /* allocate space for the internal shared mem fragment table: */
+ addr = mmap(NULL, sizeof(ishm_ftable_t),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ _ODP_ERR("unable to mmap the main fragment table\n.");
+ goto init_glob_err2;
+ }
+ ishm_ftbl = addr;
+ memset(ishm_ftbl, 0, sizeof(ishm_ftable_t));
+
+ /* Reserve memory for _ODP_ISHM_SINGLE_VA reserved blocks */
+ ishm_tbl->single_va_fd = -1;
+ if (max_memory) {
+ spce_addr = reserve_single_va(max_memory,
+ &ishm_tbl->single_va_fd);
+ if (!spce_addr) {
+ _ODP_ERR("unable to reserve single VA memory\n.");
+ goto init_glob_err3;
+ }
+ ishm_tbl->single_va_start = spce_addr;
+ }
+
+ /* use the first fragment descriptor to describe to whole VA space: */
+ ishm_ftbl->fragment[0].block_index = -1;
+ ishm_ftbl->fragment[0].start = spce_addr;
+ ishm_ftbl->fragment[0].len = max_memory;
+ ishm_ftbl->fragment[0].prev = NULL;
+ ishm_ftbl->fragment[0].next = NULL;
+ ishm_ftbl->used_fragmnts = &ishm_ftbl->fragment[0];
+
+ /* and put all other fragment descriptors in the unused list: */
+ for (i = 1; i < ISHM_NB_FRAGMNTS - 1; i++) {
+ ishm_ftbl->fragment[i].prev = NULL;
+ ishm_ftbl->fragment[i].next = &ishm_ftbl->fragment[i + 1];
+ }
+ ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].prev = NULL;
+ ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].next = NULL;
+ ishm_ftbl->unused_fragmnts = &ishm_ftbl->fragment[1];
+
+ /*
+ * We run _odp_ishm_init_local() directly here to give the
+ * possibility to run shm_reserve() before the odp_init_local()
+ * is performed for the main thread... Many init_global() functions
+ * indeed assume the availability of odp_shm_reserve()...:
+ */
+ if (do_odp_ishm_init_local()) {
+ _ODP_ERR("unable to init the main thread\n.");
+ goto init_glob_err4;
+ }
+
+ /* get ready to create pools: */
+ _odp_ishm_pool_init();
+
+ /* init cache files */
+ hp_init();
+
+ return 0;
+
+init_glob_err4:
+ if (_odp_ishmphy_free_single_va())
+ _ODP_ERR("unable to free single VA memory\n.");
+init_glob_err3:
+ if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0)
+ _ODP_ERR("unable to munmap main fragment table\n.");
+init_glob_err2:
+ if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0)
+ _ODP_ERR("unable to munmap main block table\n.");
+init_glob_err1:
+ return -1;
+}
+
+int _odp_ishm_init_local(void)
+{
+ /*
+ * Do not re-run this for the main ODP process, as it has already
+ * been done in advance at _odp_ishm_init_global() time:
+ */
+ if ((getpid() == odp_global_ro.main_pid) &&
+ (syscall(SYS_gettid) == getpid()))
+ return 0;
+
+ return do_odp_ishm_init_local();
+}
+
+static int do_odp_ishm_term_local(void)
+{
+ int i;
+ int proc_table_refcnt = 0;
+ int block_index;
+ ishm_block_t *block;
+
+ procsync();
+
+ ishm_tbl->odpthread_cnt--; /* decount ODPthread (pthread or process) */
+
+ /*
+ * The ishm_process table is local to each linux process
+ * Check that no other linux threads (of this linux process)
+ * still needs the table, and free it if so.
+ * We protect this with the general ishm lock to avoid
+ * term race condition of different running threads.
+ */
+ proc_table_refcnt = --ishm_proctable->thrd_refcnt;
+ if (!proc_table_refcnt) {
+ /*
+ * this is the last thread of this process...
+ * All mappings for this process are about to be lost...
+ * Go through the table of visible blocks for this process,
+ * decreasing the refcnt of each visible blocks, and issuing
+ * warning for those no longer referenced by any process.
+ * Note that non-referenced blocks are not freed: this is
+ * deliberate as this would imply that the semantic of the
+ * freeing function would differ depending on whether we run
+ * with odp_thread as processes or pthreads. With this approach,
+ * the user should always free the blocks manually, which is
+ * more consistent
+ */
+ for (i = 0; i < ishm_proctable->nb_entries; i++) {
+ block_index = ishm_proctable->entry[i].block_index;
+ block = &ishm_tbl->block[block_index];
+ if ((--block->refcnt) <= 0) {
+ block->refcnt = 0;
+ _ODP_DBG("Warning: block %d: name:%s "
+ "no longer referenced\n",
+ i,
+ ishm_tbl->block[i].name[0] ?
+ ishm_tbl->block[i].name : "<no name>");
+ }
+ }
+
+ free(ishm_proctable);
+ ishm_proctable = NULL;
+ }
+
+ return 0;
+}
+
+int _odp_ishm_term_local(void)
+{
+ int ret;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+
+ /* postpone last thread term to allow free() by global term functions:*/
+ if (ishm_tbl->odpthread_cnt == 1) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return 0;
+ }
+
+ ret = do_odp_ishm_term_local();
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return ret;
+}
+
+int _odp_ishm_term_global(void)
+{
+ int ret = 0;
+ int index;
+ int fd = ishm_tbl->single_va_fd;
+ ishm_block_t *block;
+
+ if ((getpid() != odp_global_ro.main_pid) ||
+ (syscall(SYS_gettid) != getpid()))
+ _ODP_ERR("odp_term_global() must be performed by the main "
+ "ODP process!\n.");
+
+ /* cleanup possibly non freed memory (and complain a bit): */
+ for (index = 0; index < ISHM_MAX_NB_BLOCKS; index++) {
+ block = &ishm_tbl->block[index];
+ if (block->len != 0) {
+ _ODP_ERR("block '%s' (file %s) was never freed "
+ "(cleaning up...).\n",
+ block->name, block->filename);
+ delete_file(block);
+ }
+ }
+
+ /* perform the last thread terminate which was postponed: */
+ ret = do_odp_ishm_term_local();
+
+ /* remove the file from the filesystem, keeping its fd open */
+ unlink(ishm_tbl->single_va_filename);
+
+ /* free the fragment table */
+ if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) {
+ ret |= -1;
+ _ODP_ERR("unable to munmap fragment table\n.");
+ }
+ /* free the block table */
+ if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) {
+ ret |= -1;
+ _ODP_ERR("unable to munmap main table\n.");
+ }
+
+ /* free the reserved single VA memory */
+ if (_odp_ishmphy_free_single_va())
+ ret |= -1;
+ if ((fd >= 0) && close(fd)) {
+ ret |= -1;
+ _ODP_ERR("unable to close single VA\n.");
+ }
+
+ if (!odp_global_ro.shm_dir_from_env)
+ free(odp_global_ro.shm_dir);
+
+ hp_term();
+
+ return ret;
+}
+
+/*
+ * Print the current ishm status (allocated blocks and VA space map)
+ * Return the number of allocated blocks (including those not mapped
+ * by the current odp thread). Also perform a number of sanity check.
+ * For debug.
+ */
+int _odp_ishm_status(const char *title)
+{
+ int i;
+ char flags[3];
+ char huge;
+ int proc_index;
+ ishm_fragment_t *fragmnt;
+ int consecutive_unallocated = 0; /* should never exceed 1 */
+ uintptr_t last_address = 0;
+ ishm_fragment_t *previous = NULL;
+ int nb_used_frgments = 0;
+ int nb_unused_frgments = 0; /* nb frag describing a VA area */
+ int nb_allocated_frgments = 0; /* nb frag describing an allocated VA */
+ int nb_blocks = 0;
+ int single_va_blocks = 0;
+ int max_name_len = 0;
+ uint64_t lost_total = 0; /* statistics for total unused memory */
+ uint64_t len_total = 0; /* statistics for total allocated memory */
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ /* find longest block name */
+ for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
+ int str_len;
+
+ if (ishm_tbl->block[i].len <= 0)
+ continue;
+
+ str_len = strlen(ishm_tbl->block[i].name);
+
+ if (max_name_len < str_len)
+ max_name_len = str_len;
+ }
+
+ _ODP_PRINT("%s\n", title);
+ _ODP_PRINT(" %-*s flag %-29s %-8s %-8s %-3s %-3s %-3s file\n",
+ max_name_len, "name", "range", "user_len", "unused",
+ "seq", "ref", "fd");
+
+ /* display block table: 1 line per entry +1 extra line if mapped here */
+ for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
+ void *start_addr = NULL;
+ void *end_addr = NULL;
+ int entry_fd = -1;
+
+ if (ishm_tbl->block[i].len <= 0)
+ continue; /* unused block */
+
+ nb_blocks++;
+ if (ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA)
+ single_va_blocks++;
+
+ flags[0] = (ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA) ?
+ 'S' : '.';
+ flags[1] = (ishm_tbl->block[i].flags & _ODP_ISHM_LOCK) ?
+ 'L' : '.';
+ flags[2] = 0;
+ switch (ishm_tbl->block[i].huge) {
+ case HUGE:
+ huge = 'H';
+ break;
+ case NORMAL:
+ huge = 'N';
+ break;
+ case EXTERNAL:
+ huge = 'E';
+ break;
+ case CACHED:
+ huge = 'C';
+ break;
+ default:
+ huge = '?';
+ }
+ proc_index = procfind_block(i);
+ lost_total += ishm_tbl->block[i].len -
+ ishm_tbl->block[i].user_len;
+ len_total += ishm_tbl->block[i].len;
+
+ if (proc_index >= 0) {
+ start_addr = ishm_proctable->entry[proc_index].start;
+ end_addr = (void *)(uintptr_t)((uintptr_t)start_addr +
+ ishm_tbl->block[i].len);
+ entry_fd = ishm_proctable->entry[proc_index].fd;
+ }
+
+ _ODP_PRINT("%2i %-*s %s%c %p-%p %-8" PRIu64 " "
+ "%-8" PRIu64 " %-3" PRIu64 " %-3" PRIu64 " "
+ "%-3d %s\n",
+ i, max_name_len, ishm_tbl->block[i].name,
+ flags, huge, start_addr, end_addr,
+ ishm_tbl->block[i].user_len,
+ ishm_tbl->block[i].len - ishm_tbl->block[i].user_len,
+ ishm_tbl->block[i].seq,
+ ishm_tbl->block[i].refcnt,
+ entry_fd,
+ ishm_tbl->block[i].filename[0] ?
+ ishm_tbl->block[i].filename :
+ "(none)");
+ }
+ _ODP_PRINT("TOTAL: %58s%-8" PRIu64 " %2s%-8" PRIu64 "\n",
+ "", len_total,
+ "", lost_total);
+ _ODP_PRINT("%65s(%" PRIu64 "MB) %4s(%" PRIu64 "MB)\n",
+ "", len_total / 1024 / 1024,
+ "", lost_total / 1024 / 1024);
+
+ /* display the virtual space allocations... : */
+ _ODP_PRINT("\nishm virtual space:\n");
+ for (fragmnt = ishm_ftbl->used_fragmnts;
+ fragmnt; fragmnt = fragmnt->next) {
+ if (fragmnt->block_index >= 0) {
+ nb_allocated_frgments++;
+ _ODP_PRINT(" %8p - %8p: ALLOCATED by block:%d\n",
+ fragmnt->start,
+ (void *)((uintptr_t)fragmnt->start + fragmnt->len - 1),
+ fragmnt->block_index);
+ consecutive_unallocated = 0;
+ } else {
+ _ODP_PRINT(" %8p - %8p: NOT ALLOCATED\n",
+ fragmnt->start,
+ (void *)((uintptr_t)fragmnt->start + fragmnt->len - 1));
+ if (consecutive_unallocated++)
+ _ODP_ERR("defragmentation error\n");
+ }
+
+ /* some other sanity checks: */
+ if (fragmnt->prev != previous)
+ _ODP_ERR("chaining error\n");
+
+ if (fragmnt != ishm_ftbl->used_fragmnts) {
+ if ((uintptr_t)fragmnt->start != last_address + 1)
+ _ODP_ERR("lost space error\n");
+ }
+
+ last_address = (uintptr_t)fragmnt->start + fragmnt->len - 1;
+ previous = fragmnt;
+ nb_used_frgments++;
+ }
+
+ /*
+ * the number of blocks with the single_VA flag set should match
+ * the number of used fragments:
+ */
+ if (single_va_blocks != nb_allocated_frgments)
+ _ODP_ERR("single_va_blocks != nb_allocated_fragments!\n");
+
+ /* compute the number of unused fragments*/
+ for (fragmnt = ishm_ftbl->unused_fragmnts;
+ fragmnt; fragmnt = fragmnt->next)
+ nb_unused_frgments++;
+
+ _ODP_PRINT("ishm: %d fragment used. %d fragments unused. (total=%d)\n",
+ nb_used_frgments, nb_unused_frgments,
+ nb_used_frgments + nb_unused_frgments);
+
+ if ((nb_used_frgments + nb_unused_frgments) != ISHM_NB_FRAGMNTS)
+ _ODP_ERR("lost fragments!\n");
+
+ if (nb_blocks < ishm_proctable->nb_entries)
+ _ODP_ERR("process known block cannot exceed main total sum!\n");
+
+ _ODP_PRINT("\n");
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ return nb_blocks;
+}
+
+void _odp_ishm_print(int block_index)
+{
+ ishm_block_t *block;
+ const char *str;
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+
+ if ((block_index < 0) ||
+ (block_index >= ISHM_MAX_NB_BLOCKS) ||
+ (ishm_tbl->block[block_index].len == 0)) {
+ odp_spinlock_unlock(&ishm_tbl->lock);
+ _ODP_ERR("Request for info on an invalid block\n");
+ return;
+ }
+
+ block = &ishm_tbl->block[block_index];
+
+ _ODP_PRINT("\nSHM block info\n--------------\n");
+ _ODP_PRINT(" name: %s\n", block->name);
+ _ODP_PRINT(" file: %s\n", block->filename);
+ _ODP_PRINT(" expt: %s\n", block->exptname);
+ _ODP_PRINT(" user_flags: 0x%x\n", block->user_flags);
+ _ODP_PRINT(" flags: 0x%x\n", block->flags);
+ _ODP_PRINT(" user_len: %" PRIu64 "\n", block->user_len);
+ _ODP_PRINT(" start: %p\n", block->start);
+ _ODP_PRINT(" len: %" PRIu64 "\n", block->len);
+
+ switch (block->huge) {
+ case HUGE:
+ str = "huge";
+ break;
+ case NORMAL:
+ str = "normal";
+ break;
+ case EXTERNAL:
+ str = "external";
+ break;
+ case CACHED:
+ str = "cached";
+ break;
+ default:
+ str = "??";
+ }
+
+ _ODP_PRINT(" page type: %s\n", str);
+ _ODP_PRINT(" seq: %" PRIu64 "\n", block->seq);
+ _ODP_PRINT(" refcnt: %" PRIu64 "\n", block->refcnt);
+ _ODP_PRINT("\n");
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+}
+
+int32_t odp_system_meminfo(odp_system_meminfo_t *info, odp_system_memblock_t memblock[],
+ int32_t max_num)
+{
+ ishm_block_t *block;
+ int name_len, proc_index;
+ int32_t i;
+ uintptr_t addr;
+ uint64_t len, lost, page_size;
+ uint64_t lost_total = 0;
+ uint64_t len_total = 0;
+ int32_t num = 0;
+ const uint64_t huge_sz = odp_sys_huge_page_size();
+ const uint64_t normal_sz = odp_sys_page_size();
+
+ odp_spinlock_lock(&ishm_tbl->lock);
+ procsync();
+
+ for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) {
+ block = &ishm_tbl->block[i];
+
+ len = block->len;
+ if (len == 0)
+ continue;
+
+ lost = len - block->user_len;
+
+ if (num < max_num) {
+ odp_system_memblock_t *mb = &memblock[num];
+
+ name_len = strlen(block->name);
+ if (name_len >= ODP_SYSTEM_MEMBLOCK_NAME_LEN)
+ name_len = ODP_SYSTEM_MEMBLOCK_NAME_LEN - 1;
+
+ memcpy(mb->name, block->name, name_len);
+ mb->name[name_len] = 0;
+
+ addr = 0;
+ proc_index = procfind_block(i);
+ if (proc_index >= 0)
+ addr = (uintptr_t)ishm_proctable->entry[proc_index].start;
+
+ page_size = 0;
+ if (block->huge == HUGE)
+ page_size = huge_sz;
+ else if (block->huge == NORMAL)
+ page_size = normal_sz;
+
+ mb->addr = addr;
+ mb->used = len;
+ mb->overhead = lost;
+ mb->page_size = page_size;
+ }
+
+ len_total += len;
+ lost_total += lost;
+
+ num++;
+ }
+
+ odp_spinlock_unlock(&ishm_tbl->lock);
+
+ info->total_mapped = len_total;
+ info->total_used = len_total;
+ info->total_overhead = lost_total;
+
+ return num;
+}
diff --git a/platform/linux-generic/odp_ishmphy.c b/platform/linux-generic/odp_ishmphy.c
new file mode 100644
index 000000000..046b254c3
--- /dev/null
+++ b/platform/linux-generic/odp_ishmphy.c
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/*
+ * This file handles the lower end of the ishm memory allocator:
+ * It performs the physical mappings.
+ */
+#include <odp_posix_extensions.h>
+#include <odp_config_internal.h>
+#include <odp/api/align.h>
+#include <odp/api/system_info.h>
+#include <odp/api/debug.h>
+#include <odp_debug_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_ishmphy_internal.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+#include <odp_ishmphy_internal.h>
+
+static void *common_va_address;
+static uint64_t common_va_len;
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* Reserve single VA memory
+ * This function is called at odp_init_global() time to pre-reserve some memory
+ * which is inherited by all odpthreads (i.e. descendant processes and threads).
+ * This memory block is later used when memory is reserved with
+ * _ODP_ISHM_SINGLE_VA flag.
+ * returns the address of the mapping or NULL on error.
+ */
+void *_odp_ishmphy_reserve_single_va(uint64_t len, int fd)
+{
+ void *addr;
+
+ addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_POPULATE, fd, 0);
+ if (addr == MAP_FAILED) {
+ _ODP_ERR("mmap failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (mprotect(addr, len, PROT_READ | PROT_WRITE))
+ _ODP_ERR("mprotect failed: %s\n", strerror(errno));
+
+ _ODP_DBG("VA Reserved: %p, len=%" PRIu64 "\n", addr, len);
+
+ common_va_address = addr;
+ common_va_len = len;
+
+ return addr;
+}
+
+/* Free single VA memory
+ * This function is called at odp_term_global() time to free the memory reserved
+ * by _odp_ishmphy_reserve_single_va()
+ */
+int _odp_ishmphy_free_single_va(void)
+{
+ int ret;
+
+ if (!common_va_address)
+ return 0;
+
+ ret = munmap(common_va_address, common_va_len);
+ if (ret)
+ _ODP_ERR("munmap failed: %s\n", strerror(errno));
+ return ret;
+}
+
+/*
+ * do a mapping:
+ * Performs a mapping of the provided file descriptor to the process VA
+ * space. Not to be used with _ODP_ISHM_SINGLE_VA blocks.
+ * returns the address of the mapping or NULL on error.
+ */
+void *_odp_ishmphy_map(int fd, uint64_t size, uint64_t offset, int flags)
+{
+ void *mapped_addr;
+ int mmap_flags = MAP_POPULATE;
+
+ _ODP_ASSERT(!(flags & _ODP_ISHM_SINGLE_VA));
+
+ /* do a new mapping in the VA space: */
+ mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | mmap_flags, fd, offset);
+ if ((mapped_addr >= common_va_address) &&
+ ((char *)mapped_addr <
+ (char *)common_va_address + common_va_len)) {
+ _ODP_ERR("VA SPACE OVERLAP!\n");
+ }
+
+ if (mapped_addr == MAP_FAILED)
+ return NULL;
+
+ /* if locking is requested, lock it...*/
+ if (flags & _ODP_ISHM_LOCK) {
+ if (mlock(mapped_addr, size)) {
+ _ODP_ERR("mlock failed: %s\n", strerror(errno));
+ if (munmap(mapped_addr, size))
+ _ODP_ERR("munmap failed: %s\n", strerror(errno));
+ return NULL;
+ }
+ }
+ return mapped_addr;
+}
+
+/* free a mapping:
+ * _ODP_ISHM_SINGLE_VA memory is not returned back to linux until global
+ * terminate. If the _ODP_ISHM_SINGLE_VA flag was not given, both physical
+ * memory and virtual address space are released by calling the normal munmap.
+ * return 0 on success or -1 on error.
+ */
+int _odp_ishmphy_unmap(void *start, uint64_t len, int flags)
+{
+ int ret;
+
+ /* if locking was requested, unlock...*/
+ if (flags & _ODP_ISHM_LOCK)
+ munlock(start, len);
+
+ if (flags & _ODP_ISHM_SINGLE_VA)
+ return 0;
+
+ /* just release the mapping */
+ ret = munmap(start, len);
+ if (ret)
+ _ODP_ERR("munmap failed: %s\n", strerror(errno));
+ return ret;
+}
diff --git a/platform/linux-generic/odp_ishmpool.c b/platform/linux-generic/odp_ishmpool.c
new file mode 100644
index 000000000..fb2cf43c4
--- /dev/null
+++ b/platform/linux-generic/odp_ishmpool.c
@@ -0,0 +1,657 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+/* This file gathers the buddy and slab allocation functionality provided
+ * by _ishm.
+ * _odp_ishmpool_create() can be used to create a pool for buddy/slab
+ * allocation. _odp_ishmpool_create() will allocate a memory area using
+ * ishm_reserve() for both the control part (needed for tracking
+ * allocation/free...) and the user memory itself (part of which will be given
+ * at each ishmpool_alloc()).
+ * The element size provided at pool creation time determines whether
+ * to pool will of type buddy or slab.
+ * For buddy, all allocations are rounded to the nearest power of 2.
+ *
+ * The implementation of the buddy allocator is very traditional: it
+ * maintains N lists of free buffers.
+ * The control part actually contains these N queue heads, (N-M are actually
+ * used), the free buffers themselves being used for chaining (the chaining info
+ * is in the buffers: as they are "free" they should not be touched by the
+ * user). The control part also contains a array of bytes for remembering
+ * the size (actually the order) of the allocated buffers:
+ * There are 2^(N-M) such bytes, this number being the maximum number of
+ * allocated buffers (when all allocation are <= 2^M bytes)
+ * Buddy allocators handle fragmentation by splitting or merging blocks by 2.
+ * They guarantee a minimum efficiency of 50%, at worse case fragmentation.
+ *
+ * Slab implementation is even simpler, all free elements being queued in
+ * one single queue at init, taken from this queue when allocated and
+ * returned to this same queue when freed.
+ *
+ * The reason for not using malloc() is that malloc does not guarantee
+ * memory sharability between ODP threads (regardless of their implementation)
+ * which ishm_reserve() can do. see the comments around
+ * _odp_ishmbud_pool_create() and ishm_reserve() for more details.
+ *
+ * This file is divided in 3 sections: the first one regroups functions
+ * needed by the buddy allocation.
+ * The second one regroups the functions needed by the slab allocator.
+ * The third section regroups the common functions exported externally.
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/spinlock.h>
+#include <odp/api/align.h>
+#include <odp/api/debug.h>
+
+#include <odp_shm_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_ishmpool_internal.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define BUDDY_MIN_SIZE 32 /* minimal buddy allocation size */
+
+typedef _odp_ishm_pool_t pool_t; /* for shorter writing */
+
+/* array of ishm block index used for pools. only used for pool
+ * lookup by name */
+#define MAX_NB_POOL 100
+static int pool_blk_idx[MAX_NB_POOL];
+
+/* section 1: functions for buddy allocation: */
+
+/* free buddy blocks contains the following structure, used to link the
+ * free blocks together.
+ */
+typedef struct bblock_t {
+ struct bblock_t *next;
+ uint32_t order;
+} bblock_t;
+
+/* value set in the 'order' table when the block is not allocated: */
+#define BBLOCK_FREE 0
+
+/* compute ceil(log2(size)) */
+static uint8_t clog2(uint64_t size)
+{
+ uint64_t sz;
+ uint32_t bit;
+ uint8_t res;
+
+ sz = size; /* we start by computing res = log2(sz)... */
+ res = 0;
+ for (bit = 32; bit ; bit >>= 1) {
+ if (sz >= ((uint64_t)1 << bit)) {
+ sz >>= bit;
+ res += bit;
+ }
+ }
+ if (((uint64_t)1 << res) < size) /* ...and then ceil(x) */
+ res++;
+
+ return res;
+}
+
+/*
+ * given a bblock address, and an order value, returns the address
+ * of the buddy bblock (the other "half")
+ */
+static inline bblock_t *get_bblock_buddy(pool_t *bpool, bblock_t *addr,
+ uint8_t order)
+{
+ uintptr_t b;
+
+ b = ((uintptr_t)addr - (uintptr_t)bpool->ctrl.user_addr);
+ b ^= 1 << order;
+ return (void *)(b + (uintptr_t)bpool->ctrl.user_addr);
+}
+
+/*
+ * given a buddy block address, return its number (used for busy flags):
+ */
+static inline uintptr_t get_bblock_nr(pool_t *bpool, void *addr)
+{
+ uintptr_t b;
+ uint8_t min_order;
+
+ min_order = bpool->ctrl.min_order;
+ b = ((uintptr_t)addr - (uintptr_t)bpool->ctrl.user_addr) >> min_order;
+ return b;
+}
+
+/* remove bblock from the list for bblocks of rank order. The bblock to be
+ * removed is really expected to be on the list: not finding it is an error */
+static inline void remove_from_list(pool_t *bpool, uint8_t order,
+ bblock_t *bblock)
+{
+ bblock_t *curr;
+ bblock_t *prev = NULL;
+
+ curr = bpool->ctrl.free_heads[order];
+ if (!curr)
+ goto remove_from_list_error;
+
+ if (curr == bblock) {
+ bpool->ctrl.free_heads[order] = curr->next;
+ return;
+ }
+
+ while (curr) {
+ if (curr == bblock) {
+ prev->next = curr->next;
+ return;
+ }
+ prev = curr;
+ curr = curr->next;
+ }
+
+remove_from_list_error:
+ _ODP_ERR("List corrupted\n");
+}
+
+/*
+ * create a buddy memory pool of given size (actually nearest power of 2),
+ * where allocation will never be smaller than min_alloc.
+ * returns a pointer to the created buddy_pool
+ * The allocated area contains:
+ * - The _odp_ishm_pool_ctrl_t structure
+ * - The array of ((order - min_order) of free list heads
+ * - The array of 'order' values, remembering sizes of allocated bblocks
+ * - alignment to cache line
+ * - The user memory
+ */
+static pool_t *_odp_ishmbud_pool_create(const char *pool_name, int store_idx,
+ uint64_t size,
+ uint64_t min_alloc, int flags)
+{
+ uint8_t order; /* pool order = ceil(log2(size)) */
+ uint8_t min_order; /* pool min_order = ceil(log2(min_alloc))*/
+ uint32_t max_nb_bblock; /* max number of bblock, when smallest */
+ uint32_t control_sz; /* size of control area */
+ uint32_t free_head_sz; /* mem area needed for list heads */
+ uint32_t saved_order_sz; /* mem area to remember given sizes */
+ uint64_t user_sz; /* 2^order bytes */
+ uint64_t total_sz; /* total size to request */
+ int blk_idx; /* as returned by _ishm_resrve() */
+ pool_t *bpool;
+ int i;
+ bblock_t *first_block;
+
+ /* a bblock_t must fit in the buffers for linked chain! */
+ if (min_alloc < sizeof(bblock_t))
+ min_alloc = sizeof(bblock_t);
+
+ /* pool order is such that 2^order = size. same for min_order */
+ order = clog2(size);
+ min_order = clog2(min_alloc);
+
+ /* check parameters obvious wishes: */
+ if (order >= 64)
+ return NULL;
+ if (order < min_order)
+ return NULL;
+
+ /* at worst case, all bblocks have smallest (2^min_order) size */
+ max_nb_bblock = (1 << (order - min_order));
+
+ /* space needed for the control area (padded to cache line size)*/
+ control_sz = _ODP_ROUNDUP_CACHE_LINE(sizeof(_odp_ishm_pool_ctrl_t));
+
+ /* space needed for 'order' free bblock list heads: */
+ /* Note that only lists from min_order to order are really used.*/
+ free_head_sz = _ODP_ROUNDUP_CACHE_LINE(sizeof(void *) * (order + 1));
+
+ /* space needed for order -i.e. size- storage of alloc'd bblock:*/
+ saved_order_sz = _ODP_ROUNDUP_CACHE_LINE(max_nb_bblock * sizeof(uint8_t));
+
+ /* space needed for user area is 2^order bytes: */
+ user_sz = 1ULL << order;
+
+ total_sz = control_sz +
+ free_head_sz +
+ saved_order_sz +
+ user_sz;
+
+ /* allocate required memory: */
+ blk_idx = _odp_ishm_reserve(pool_name, total_sz, -1,
+ ODP_CACHE_LINE_SIZE, 0, flags, 0);
+ if (blk_idx < 0) {
+ _ODP_ERR("_odp_ishm_reserve failed.");
+ return NULL;
+ }
+
+ bpool = _odp_ishm_address(blk_idx);
+ if (bpool == NULL) {
+ _ODP_ERR("_odp_ishm_address failed.");
+ return NULL;
+ }
+
+ /* store in pool array (needed for look up): */
+ pool_blk_idx[store_idx] = blk_idx;
+
+ /* remember block index, needed when pool is destroyed */
+ bpool->ctrl.ishm_blk_idx = blk_idx;
+
+ /* remember element size: 0 means unknown size, i.e. buddy alloation*/
+ bpool->ctrl.element_sz = 0;
+
+ /* prepare mutex: */
+ odp_spinlock_init(&bpool->ctrl.lock);
+
+ /* initialise pointers and things... */
+ bpool->ctrl.order = order;
+ bpool->ctrl.min_order = min_order;
+ bpool->ctrl.free_heads =
+ (void *)((uintptr_t)bpool + control_sz);
+ bpool->ctrl.alloced_order =
+ (uint8_t *)((uintptr_t)bpool->ctrl.free_heads + free_head_sz);
+ bpool->ctrl.user_addr =
+ (void *)((uintptr_t)bpool->ctrl.alloced_order + saved_order_sz);
+
+ /* initialize all free list to NULL, except the top biggest element:*/
+ for (i = 0; i < (order - min_order); i++)
+ bpool->ctrl.free_heads[i] = NULL;
+ bpool->ctrl.free_heads[order] = bpool->ctrl.user_addr;
+ first_block = (bblock_t *)bpool->ctrl.user_addr;
+ first_block->next = NULL;
+ first_block->order = order;
+
+ /* set all 'order' of allocated bblocks to free: */
+ memset(bpool->ctrl.alloced_order, BBLOCK_FREE, saved_order_sz);
+
+ return bpool;
+}
+
+/* allocated memory from the given buddy pool */
+static void *_odp_ishmbud_alloc(pool_t *bpool, uint64_t size)
+{
+ uint32_t rq_order; /* requested order */
+ uint32_t try_order;
+ bblock_t *bblock;
+ bblock_t *buddy;
+ uintptr_t nr;
+
+ /* if size is zero or too big reject: */
+ if ((!size) && (size > (1ULL << bpool->ctrl.order))) {
+ _ODP_ERR("Invalid alloc size (0 or larger than whole pool)\n");
+ return NULL;
+ }
+
+ /* compute ceil(log2(size)), to get the requested block order: */
+ rq_order = clog2(size);
+
+ /* make sure the requested order is bigger (or same) as minimum! */
+ if (rq_order < bpool->ctrl.min_order)
+ rq_order = bpool->ctrl.min_order;
+
+ /* mutex from here: */
+ odp_spinlock_lock(&bpool->ctrl.lock);
+
+ /* now, start trying to allocate a bblock of rq_order. If that
+ * fails keep trying larger orders until pool order is reached */
+ bblock = NULL;
+ for (try_order = rq_order; try_order <= bpool->ctrl.order;
+ try_order++) {
+ if (bpool->ctrl.free_heads[try_order]) {
+ /* remove from list: */
+ bblock =
+ (bblock_t *)(bpool->ctrl.free_heads[try_order]);
+ bpool->ctrl.free_heads[try_order] = bblock->next;
+ break;
+ }
+ }
+
+ if (!bblock) {
+ odp_spinlock_unlock(&bpool->ctrl.lock);
+ _ODP_ERR("Out of memory. (Buddy pool full)\n");
+ return NULL;
+ }
+
+ /* OK: we got a block, but possibbly too large (if try_order>rq_order)
+ * return the extra halves to the pool hence splitting the bblock at
+ * each 'extra' order: */
+ while (try_order-- > rq_order) {
+ /* split: */
+ buddy = (bblock_t *)((uintptr_t)bblock + (1 << try_order));
+ buddy->order = try_order;
+ /* add to list: */
+ buddy->next = bpool->ctrl.free_heads[try_order];
+ bpool->ctrl.free_heads[try_order] = buddy;
+ /* mark as free (non allocated block get size 0): */
+ nr = get_bblock_nr(bpool, buddy);
+ bpool->ctrl.alloced_order[nr] = BBLOCK_FREE;
+ }
+
+ /* remember the size if the allocated block: */
+ nr = get_bblock_nr(bpool, bblock);
+ bpool->ctrl.alloced_order[nr] = rq_order;
+
+ /* and return the allocated block! */
+ odp_spinlock_unlock(&bpool->ctrl.lock);
+ return (void *)bblock;
+}
+
+/* free a previously allocated buffer from a given buddy pool */
+static int _odp_ishmbud_free(pool_t *bpool, void *addr)
+{
+ uintptr_t user_start; /* start of user area */
+ uintptr_t user_stop; /* stop of user area */
+ uintptr_t mask; /* 2^min_order - 1 */
+ bblock_t *bblock; /* bblock being freed */
+ bblock_t *buddy; /* buddy bblock of bblock being freed */
+ uint8_t order; /* order of block being freed */
+ uintptr_t nr; /* block number */
+
+ /* freeing NULL is regarded as OK, though without any effect: */
+ if (!addr)
+ return 0;
+
+ user_start = (uintptr_t)bpool->ctrl.user_addr;
+ user_stop = user_start + ((uintptr_t)1 << bpool->ctrl.order);
+ mask = ((uintptr_t)1 << bpool->ctrl.min_order) - 1;
+
+ /* some sanity checks: check that given address is within pool and
+ * that relative address has 2^min_order granularity: */
+ if (((uintptr_t)addr < user_start) ||
+ ((uintptr_t)addr > user_stop) ||
+ (((uintptr_t)addr - user_start) & mask)) {
+ _ODP_ERR("Invalid address to be freed\n");
+ return -1;
+ }
+
+ /* mutex from here: */
+ odp_spinlock_lock(&bpool->ctrl.lock);
+
+ /* collect saved block order and make sure bblock was allocated */
+ bblock = (bblock_t *)addr;
+ nr = get_bblock_nr(bpool, bblock);
+ order = bpool->ctrl.alloced_order[nr];
+ if (order == BBLOCK_FREE) {
+ _ODP_ERR("Double free error\n");
+ odp_spinlock_unlock(&bpool->ctrl.lock);
+ return -1;
+ }
+
+ /* this looks like a valid free, mark at least this as free: */
+ bpool->ctrl.alloced_order[nr] = BBLOCK_FREE;
+
+ /* go up in orders, trying to merge buddies... */
+ while (order < bpool->ctrl.order) {
+ buddy = get_bblock_buddy(bpool, bblock, order);
+ /*if buddy is not free: no further merge possible */
+ nr = get_bblock_nr(bpool, buddy);
+ if (bpool->ctrl.alloced_order[nr] != BBLOCK_FREE)
+ break;
+ /*merge only bblock of same order:*/
+ if (buddy->order != order)
+ break;
+ /*merge: remove buddy from free list: */
+ remove_from_list(bpool, order, buddy);
+ /*merge: make sure we point at start of block: */
+ if (bblock > buddy)
+ bblock = buddy;
+ /*merge: size of block has doubled: increase order: */
+ order++;
+ }
+
+ /* insert the bblock into its correct free block list: */
+ bblock->next = bpool->ctrl.free_heads[order];
+ bpool->ctrl.free_heads[order] = bblock;
+
+ /* remember the (possibly now merged) block order: */
+ bblock->order = order;
+
+ odp_spinlock_unlock(&bpool->ctrl.lock);
+ return 0;
+}
+
+/* section 2: functions for slab allocation: */
+
+/* free slab blocks contains the following structure, used to link the
+ * free blocks together.
+ */
+typedef struct sblock_t {
+ struct sblock_t *next;
+} sblock_t;
+
+/*
+ * create a slab memory pool of given size (rounded up to the nearest integer
+ * number of element, where each element has size 'elt_size').
+ * returns a pointer to the created slab pool.
+ * The allocated area contains:
+ * - The _odp_ishm_pool_ctrl_t structure
+ * - alignment to cache line
+ * - The user memory
+ */
+static pool_t *_odp_ishmslab_pool_create(const char *pool_name, int store_idx,
+ uint64_t size,
+ uint64_t elt_size, int flags)
+{
+ uint32_t nb_sblock; /* number of elements in the pool */
+ uint32_t control_sz; /* size of control area */
+ uint64_t total_sz; /* total size to request */
+ uint64_t user_sz; /* 2^order bytes */
+ int blk_idx; /* as returned by _ishm_reserve() */
+ pool_t *spool;
+ unsigned int i;
+ sblock_t *block;
+
+ /* a sblock_t must fit in the buffers for linked chain! */
+ if (elt_size < sizeof(bblock_t)) {
+ elt_size = sizeof(bblock_t);
+ size = size * (sizeof(bblock_t) / elt_size +
+ ((sizeof(bblock_t) % elt_size) ? 1 : 0));
+ }
+
+ /* nb of element fitting in the pool is just ceil(size/elt_size)*/
+ nb_sblock = (size / elt_size) + ((size % elt_size) ? 1 : 0);
+
+ /* space needed for the control area (padded to cache line size)*/
+ control_sz = _ODP_ROUNDUP_CACHE_LINE(sizeof(_odp_ishm_pool_ctrl_t));
+
+ /* space needed for user area is : */
+ user_sz = nb_sblock * elt_size;
+
+ total_sz = control_sz +
+ user_sz;
+
+ /* allocate required memory: */
+ blk_idx = _odp_ishm_reserve(pool_name, total_sz, -1,
+ ODP_CACHE_LINE_SIZE, 0, flags, 0);
+ if (blk_idx < 0) {
+ _ODP_ERR("_odp_ishm_reserve failed.");
+ return NULL;
+ }
+
+ spool = _odp_ishm_address(blk_idx);
+ if (spool == NULL) {
+ _ODP_ERR("_odp_ishm_address failed.");
+ return NULL;
+ }
+
+ /* store in pool array (needed for look up): */
+ pool_blk_idx[store_idx] = blk_idx;
+
+ /* remember block index, needed when pool is destroyed */
+ spool->ctrl.ishm_blk_idx = blk_idx;
+
+ /* remember element (sblock) size and their number: */
+ spool->ctrl.element_sz = elt_size;
+ spool->ctrl.nb_elem = nb_sblock;
+
+ /* prepare mutex: */
+ odp_spinlock_init(&spool->ctrl.lock);
+
+ /* initialise pointers and things... */
+ spool->ctrl.user_addr =
+ (void *)((uintptr_t)spool + control_sz);
+
+ /* initialise the free list with the list of all elements:*/
+ spool->ctrl.free_head = spool->ctrl.user_addr;
+ for (i = 0; i < nb_sblock - 1; i++) {
+ block = (sblock_t *)((uintptr_t)spool->ctrl.user_addr +
+ i * (uintptr_t)elt_size);
+ block->next = (sblock_t *)((uintptr_t)block +
+ (uintptr_t)elt_size);
+ }
+ block = (sblock_t *)((uintptr_t)spool->ctrl.user_addr +
+ (nb_sblock - 1) * (uintptr_t)elt_size);
+ block->next = NULL;
+
+ return spool;
+}
+
+/* allocated memory from the given slab pool */
+static void *_odp_ishmslab_alloc(pool_t *spool, uint64_t size)
+{
+ void *ret;
+ sblock_t *block;
+
+ if (size > spool->ctrl.element_sz)
+ return NULL;
+
+ odp_spinlock_lock(&spool->ctrl.lock);
+ ret = spool->ctrl.free_head;
+ if (!ret) {
+ odp_spinlock_unlock(&spool->ctrl.lock);
+ _ODP_ERR("Out of memory. (Slab pool full)\n");
+ return NULL;
+ }
+
+ block = (sblock_t *)ret;
+ spool->ctrl.free_head = block->next;
+
+ odp_spinlock_unlock(&spool->ctrl.lock);
+ return ret;
+}
+
+/* free a previously allocated buffer from a given slab pool */
+static int _odp_ishmslab_free(pool_t *spool, void *addr)
+{
+ uintptr_t user_start; /* start of user area */
+ uintptr_t user_stop; /* stop of user area */
+ sblock_t *block;
+
+ /* freeing NULL is regarded as OK, though without any effect: */
+ if (!addr)
+ return 0;
+
+ user_start = (uintptr_t)spool->ctrl.user_addr;
+ user_stop = user_start + spool->ctrl.element_sz * spool->ctrl.nb_elem;
+
+ /* some sanity checks: check that given address is within pool and
+ * that relative address has element_sz granularity: */
+ if (((uintptr_t)addr < user_start) ||
+ ((uintptr_t)addr > user_stop) ||
+ (((uintptr_t)addr - user_start) % spool->ctrl.element_sz)) {
+ _ODP_ERR("Invalid address to be freed\n");
+ return -1;
+ }
+
+ odp_spinlock_lock(&spool->ctrl.lock);
+ block = (sblock_t *)addr;
+ block->next = (sblock_t *)spool->ctrl.free_head;
+ spool->ctrl.free_head = addr;
+ odp_spinlock_unlock(&spool->ctrl.lock);
+
+ return 0;
+}
+
+/* section 3: common, external functions: */
+
+/* create a pool: either with fixed alloc size (if max_alloc/min_alloc<2) or
+ * of variable block size (if max_alloc == 0) */
+pool_t *_odp_ishm_pool_create(const char *pool_name, uint64_t size,
+ uint64_t min_alloc, uint64_t max_alloc, int flags)
+{
+ int store_idx;
+ uint64_t real_pool_sz;
+
+ if (min_alloc > max_alloc) {
+ _ODP_ERR("invalid parameter: min_alloc > max_alloc");
+ return NULL;
+ }
+
+ /* search for a free index in pool_blk_idx for the pool */
+ for (store_idx = 0; store_idx < MAX_NB_POOL; store_idx++) {
+ if (pool_blk_idx[store_idx] < 0)
+ break;
+ }
+ if (store_idx == MAX_NB_POOL) {
+ _ODP_ERR("Max number of pool reached (MAX_NB_POOL)");
+ return NULL;
+ }
+
+ if ((min_alloc == 0) || ((max_alloc / min_alloc) > 2)) {
+ /* alloc variation is not constant enough: we go for a buddy
+ * allocator. The pool efficiency may go as low as 50%
+ * so we double the required size to make sure we can satisfy
+ * the user request */
+ real_pool_sz = 2 * size;
+ return _odp_ishmbud_pool_create(pool_name, store_idx,
+ real_pool_sz,
+ BUDDY_MIN_SIZE, flags);
+ } else {
+ /* min and max are close enough so we go for constant size
+ * allocator:
+ * make sure the pool can fit the required size, even when
+ * only min_alloc allocation are performed: */
+ real_pool_sz = ((size / min_alloc) +
+ ((size % min_alloc) ? 1 : 0))
+ * max_alloc;
+ return _odp_ishmslab_pool_create(pool_name, store_idx,
+ real_pool_sz,
+ max_alloc, flags);
+ }
+}
+
+/* destroy a pool. everything goes away. no operation on the pool should
+ * follow. */
+int _odp_ishm_pool_destroy(pool_t *pool)
+{
+ int store_idx;
+
+ for (store_idx = 0; store_idx < MAX_NB_POOL; store_idx++) {
+ if (pool_blk_idx[store_idx] == pool->ctrl.ishm_blk_idx) {
+ pool_blk_idx[store_idx] = -1;
+ break;
+ }
+ }
+
+ return _odp_ishm_free_by_index(pool->ctrl.ishm_blk_idx);
+}
+
+/* allocated a buffer from a pool */
+void *_odp_ishm_pool_alloc(_odp_ishm_pool_t *pool, uint64_t size)
+{
+ if (!pool->ctrl.element_sz)
+ return _odp_ishmbud_alloc(pool, size);
+ else
+ return _odp_ishmslab_alloc(pool, size);
+}
+
+/* free a previously allocated buffer from a pool */
+int _odp_ishm_pool_free(_odp_ishm_pool_t *pool, void *addr)
+{
+ if (!pool->ctrl.element_sz)
+ return _odp_ishmbud_free(pool, addr);
+ else
+ return _odp_ishmslab_free(pool, addr);
+}
+
+void _odp_ishm_pool_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NB_POOL; i++)
+ pool_blk_idx[i] = -1;
+}
diff --git a/platform/linux-generic/odp_libconfig.c b/platform/linux-generic/odp_libconfig.c
new file mode 100644
index 000000000..7c362a755
--- /dev/null
+++ b/platform/linux-generic/odp_libconfig.c
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2020 Nokia
+ * Copyright (c) 2021 Marvell
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libconfig.h>
+
+#include <odp/api/version.h>
+#include <odp_global_data.h>
+#include <odp_debug_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_libconfig_config.h>
+
+int _odp_libconfig_init_global(void)
+{
+ const char *filename;
+ const char *vers;
+ const char *vers_rt;
+ const char *impl;
+ const char *impl_rt;
+ config_t *config = &odp_global_ro.libconfig_default;
+ config_t *config_rt = &odp_global_ro.libconfig_runtime;
+ const char *impl_field = "odp_implementation";
+ const char *vers_field = "config_file_version";
+
+ config_init(config);
+ config_init(config_rt);
+ odp_global_ro.has_config_rt = 0;
+
+ if (!config_read_string(config, config_builtin)) {
+ _ODP_ERR("Failed to read default config: %s(%d): %s\n",
+ config_error_file(config), config_error_line(config),
+ config_error_text(config));
+ goto fail;
+ }
+
+ filename = getenv("ODP_CONFIG_FILE");
+ if (filename == NULL)
+ return 0;
+
+ _ODP_PRINT("ODP CONFIG FILE: %s\n", filename);
+
+ if (!config_read_file(config_rt, filename)) {
+ _ODP_PRINT(" ERROR: failed to read config file: %s(%d): %s\n\n",
+ config_error_file(config_rt),
+ config_error_line(config_rt),
+ config_error_text(config_rt));
+ goto fail;
+ }
+
+ /* Check runtime configuration's implementation name and version */
+ if (!config_lookup_string(config, impl_field, &impl) ||
+ !config_lookup_string(config_rt, impl_field, &impl_rt)) {
+ _ODP_PRINT(" ERROR: missing mandatory field: %s\n\n", impl_field);
+ goto fail;
+ }
+ if (!config_lookup_string(config, vers_field, &vers) ||
+ !config_lookup_string(config_rt, vers_field, &vers_rt)) {
+ _ODP_PRINT(" ERROR: missing mandatory field: %s\n\n", vers_field);
+ goto fail;
+ }
+ if (strcmp(impl, impl_rt)) {
+ _ODP_PRINT(" ERROR: ODP implementation name mismatch:\n"
+ " Expected: \"%s\"\n"
+ " Found: \"%s\"\n\n", impl, impl_rt);
+ goto fail;
+ }
+ if (strcmp(vers, vers_rt)) {
+ _ODP_PRINT(" ERROR: config file version number mismatch:\n"
+ " Expected: \"%s\"\n"
+ " Found: \"%s\"\n\n", vers, vers_rt);
+ goto fail;
+ }
+
+ odp_global_ro.has_config_rt = 1;
+ return 0;
+fail:
+ _ODP_ERR("Config file failure\n");
+ config_destroy(config);
+ config_destroy(config_rt);
+ return -1;
+}
+
+int _odp_libconfig_term_global(void)
+{
+ config_destroy(&odp_global_ro.libconfig_default);
+ config_destroy(&odp_global_ro.libconfig_runtime);
+
+ return 0;
+}
+
+/**
+ * String setting value
+ *
+ * Returns a string from setting. A valid runtime setting overrides
+ * default even if the string is empty.
+ *
+ * @param path Path of the setting
+ * @param[out] value String to be copied from the setting
+ * @param str_size Maximum string length to be copied
+ *
+ * @return Size of the string copied
+ * @retval <0 on failure
+*/
+int _odp_libconfig_lookup_str(const char *path, char *value,
+ unsigned int str_size)
+{
+ const config_t *config;
+ unsigned int length, i;
+ const char *str;
+
+ for (i = 0; i < 2; i++) {
+ if (i == 0)
+ config = &odp_global_ro.libconfig_runtime;
+ else
+ config = &odp_global_ro.libconfig_default;
+
+ if (config_lookup_string(config, path, &str) == CONFIG_FALSE)
+ continue;
+
+ length = strlen(str);
+
+ /* Runtime config overrides even if it's empty string */
+ if (value == NULL || str_size == 0 || length == 0)
+ return length;
+
+ if (length > str_size) {
+ _ODP_ERR("libconfig: length of %d bigger than size %u\n", length, str_size);
+ return -1;
+ }
+
+ strcpy(value, str);
+ return length;
+ }
+
+ _ODP_ERR("libconfig: %s is not defined in config files\n", path);
+ return -1;
+}
+
+int _odp_libconfig_lookup_int(const char *path, int *value)
+{
+ int ret_def = CONFIG_FALSE;
+ int ret_rt = CONFIG_FALSE;
+
+ ret_def = config_lookup_int(&odp_global_ro.libconfig_default, path,
+ value);
+
+ /* Runtime option overrides default value */
+ ret_rt = config_lookup_int(&odp_global_ro.libconfig_runtime, path,
+ value);
+
+ return (ret_def == CONFIG_TRUE || ret_rt == CONFIG_TRUE) ? 1 : 0;
+}
+
+/**
+ * String array setting values
+ *
+ * Returns the number of strings in a string array setting. A valid runtime
+ * setting overrides default even if the array is empty. Outputs upto
+ * 'count' strings when the 'value' array pointer is not NULL. If return
+ * value is larger than 'count', there are more strings than the function was
+ * allowed to output. If return value (N) is less than 'count', only
+ * strings[0 ... N-1] have been written.
+ *
+ * @param path Path of the setting
+ * @param[out] value Array of strings to be copied from the setting
+ * @param count Number of strings to be copied
+ * @param str_size Maximum string length to be copied
+ *
+ * @return Number of strings in the setting
+ * @retval <0 on failure
+*/
+int _odp_libconfig_lookup_array_str(const char *path, char **value,
+ int count, unsigned int str_size)
+{
+ config_setting_t *setting, *elem;
+ const config_t *config;
+ int num, i, j;
+
+ for (i = 0; i < 2; i++) {
+ if (i == 0)
+ config = &odp_global_ro.libconfig_runtime;
+ else
+ config = &odp_global_ro.libconfig_default;
+
+ setting = config_lookup(config, path);
+
+ if (setting == NULL)
+ continue;
+
+ /* invalid config if element is not an array */
+ if (config_setting_is_array(setting) == CONFIG_FALSE) {
+ _ODP_ERR("libconfig: %s is not an array\n", path);
+ return -1;
+ }
+ num = config_setting_length(setting);
+
+ if (num == 0 || count == 0 || value == NULL)
+ return num;
+
+ elem = config_setting_get_elem(setting, 0);
+ if (config_setting_type(elem) != CONFIG_TYPE_STRING) {
+ _ODP_ERR("libconfig: %s array is not of type string\n", path);
+ return -1;
+ }
+
+ for (j = 0; j < num; j++) {
+ elem = config_setting_get_elem(setting, j);
+ if (strlen(elem->value.sval) > str_size) {
+ _ODP_ERR("libconfig: length of %s bigger than size %u\n",
+ elem->value.sval, str_size);
+ return -1;
+ }
+ strcpy(value[j], elem->value.sval);
+ }
+
+ return num;
+ }
+
+ _ODP_ERR("libconfig: %s is not defined in config files\n", path);
+ return -1;
+}
+
+int _odp_libconfig_lookup_array(const char *path, int value[], int max_num)
+{
+ const config_t *config;
+ config_setting_t *setting;
+ int num, i, j;
+ int num_out = 0;
+
+ for (j = 0; j < 2; j++) {
+ if (j == 0)
+ config = &odp_global_ro.libconfig_default;
+ else
+ config = &odp_global_ro.libconfig_runtime;
+
+ setting = config_lookup(config, path);
+
+ /* Runtime config may not define the array, whereas
+ * the default config has it always defined. When the array
+ * is defined, it must be correctly formatted. */
+ if (setting == NULL)
+ continue;
+
+ if (config_setting_is_array(setting) == CONFIG_FALSE)
+ return 0;
+
+ num = config_setting_length(setting);
+
+ if (num <= 0 || num > max_num)
+ return 0;
+
+ for (i = 0; i < num; i++)
+ value[i] = config_setting_get_int_elem(setting, i);
+
+ num_out = num;
+ }
+
+ /* Number of elements copied */
+ return num_out;
+}
+
+static int lookup_int(config_t *cfg,
+ const char *base_path,
+ const char *local_path,
+ const char *name,
+ int *value)
+{
+ char path[256];
+
+ if (local_path) {
+ snprintf(path, sizeof(path), "%s.%s.%s", base_path,
+ local_path, name);
+ if (config_lookup_int(cfg, path, value) == CONFIG_TRUE)
+ return 1;
+ }
+
+ snprintf(path, sizeof(path), "%s.%s", base_path, name);
+ if (config_lookup_int(cfg, path, value) == CONFIG_TRUE)
+ return 1;
+
+ return 0;
+}
+
+int _odp_libconfig_lookup_ext_int(const char *base_path,
+ const char *local_path,
+ const char *name,
+ int *value)
+{
+ if (lookup_int(&odp_global_ro.libconfig_runtime,
+ base_path, local_path, name, value))
+ return 1;
+
+ if (lookup_int(&odp_global_ro.libconfig_default,
+ base_path, local_path, name, value))
+ return 1;
+
+ return 0;
+}
+
+int _odp_libconfig_print(void)
+{
+ int c;
+ /* Temp file for config_write() output. Suppress Coverity warning about tmpfile() usage. */
+ /* coverity[secure_temp] */
+ FILE *file = tmpfile();
+
+ if (file == NULL)
+ return -1;
+
+ if (fprintf(file,
+ "\nODP_CONFIG_FILE default values:\n"
+ "-------------------------------\n\n") < 0)
+ goto fail;
+
+ config_write(&odp_global_ro.libconfig_default, file);
+
+ if (odp_global_ro.has_config_rt) {
+ if (fprintf(file,
+ "\nODP_CONFIG_FILE override values:\n"
+ "--------------------------------\n\n") < 0)
+ goto fail;
+
+ config_write(&odp_global_ro.libconfig_runtime, file);
+ }
+
+ /* Print temp file to the log */
+ rewind(file);
+ while ((c = fgetc(file)) != EOF)
+ _ODP_PRINT("%c", (char)c);
+
+ fclose(file);
+ return 0;
+
+fail:
+ fclose(file);
+ return -1;
+}
diff --git a/platform/linux-generic/odp_ml.c b/platform/linux-generic/odp_ml.c
new file mode 100644
index 000000000..205cc26b5
--- /dev/null
+++ b/platform/linux-generic/odp_ml.c
@@ -0,0 +1,2643 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/autoheader_external.h>
+
+#include <odp/api/atomic.h>
+#include <odp/api/buffer.h>
+#include <odp/api/event.h>
+#include <odp/api/hints.h>
+#include <odp/api/ml.h>
+#include <odp/api/pool.h>
+#include <odp/api/queue.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/std_types.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/event_inline_types.h>
+#include <odp/api/plat/strong_types.h>
+
+#include <odp_buffer_internal.h>
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_string_internal.h>
+
+#include <onnxruntime_c_api.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ML_MAX_IO_SEGS UINT32_MAX
+#define ML_MAX_COMPL_ID 32
+#define ML_MAX_CONFIG_STR_LEN 65
+#define ML_MAX_MODEL_SIZE (1024 * 1024 * 1024)
+#define ML_MAX_MODELS_CREATED CONFIG_ML_MAX_MODELS
+#define ML_MAX_MODELS_LOADED CONFIG_ML_MAX_MODELS
+
+/* Error codes */
+enum {
+ /* Feature not supported */
+ ML_FEATURE_NOT_SUPPORTED = 1,
+
+ /* Model is not created */
+ ML_NOT_CREATED,
+
+ /* Model was not loaded */
+ ML_NOT_LOADED,
+
+ /* Model has already loaded */
+ ML_LOADED,
+
+ /* Bad input */
+ ML_BAD_INPUT,
+
+ /* Fail from underlying library onnxruntime */
+ ML_LIB_FAILED,
+
+ /* Bad output */
+ ML_BAD_OUTPUT,
+
+ /* Bad handle */
+ ML_BAD_HDL
+};
+
+typedef struct ort_run_opts_t {
+ int enable_profiling;
+
+ ExecutionMode execution_mode;
+
+ int inter_op_num_threads;
+
+ int intra_op_num_threads;
+
+ GraphOptimizationLevel graph_opt_level;
+
+ char opt_model_filepath[ML_MAX_CONFIG_STR_LEN];
+} ort_run_opts_t;
+
+typedef struct ml_input_t {
+ /* Combined input start address */
+ void *addr;
+ /* Data size in bytes */
+ uint64_t size;
+} ml_input_t;
+
+/* Onnxruntime model info */
+typedef struct ml_model_t {
+ /* Guards state, which must be accessed atomically */
+ odp_ticketlock_t lock;
+
+ enum {
+ ML_STATE_FREE = 0, /* Not allocated */
+ ML_STATE_CREATED, /* Model is created */
+ ML_STATE_LOADED, /* Model is loaded */
+ ML_STATE_INFERENCING, /* Model is inferencing */
+ } state;
+
+ OrtSession *session;
+ OrtSessionOptions *session_opts;
+ uint32_t max_compl_id;
+ odp_atomic_u32_t compl_status[ML_MAX_COMPL_ID];
+
+ odp_ml_model_info_t info;
+ odp_ml_input_info_t input_info[CONFIG_ML_MAX_INPUTS];
+ uint64_t input_sizes[CONFIG_ML_MAX_INPUTS];
+ odp_ml_output_info_t output_info[CONFIG_ML_MAX_OUTPUTS];
+ uint64_t output_sizes[CONFIG_ML_MAX_OUTPUTS];
+
+ struct {
+ void *user_ptr;
+ } result[ML_MAX_COMPL_ID];
+} ml_model_t;
+
+typedef struct ml_global_t {
+ odp_shm_t shm;
+
+ odp_ml_capability_t capa;
+ odp_ml_config_t ml_config;
+
+ odp_pool_param_t pool_param;
+
+ const OrtApi *ort_api;
+ OrtEnv *env;
+ ort_run_opts_t ort_run_opts;
+
+ ml_model_t models[ML_MAX_MODELS_CREATED];
+
+} ml_global_t;
+
+static ml_global_t *_odp_ml_glb;
+
+static inline ml_model_t *ml_model_from_handle(odp_ml_model_t model)
+{
+ return (ml_model_t *)(uintptr_t)model;
+}
+
+int odp_ml_capability(odp_ml_capability_t *capa)
+{
+ odp_pool_capability_t pool_capa;
+
+ memset(capa, 0, sizeof(odp_ml_capability_t));
+
+ if (odp_global_ro.disable.ml) {
+ _ODP_PRINT("ML is disabled\n");
+ return 0;
+ }
+
+ capa->max_model_size = ML_MAX_MODEL_SIZE;
+ capa->max_models = ML_MAX_MODELS_CREATED;
+ capa->max_models_loaded = ML_MAX_MODELS_LOADED;
+ capa->max_compl_id = ML_MAX_COMPL_ID;
+ capa->max_inputs = CONFIG_ML_MAX_INPUTS;
+ capa->max_outputs = CONFIG_ML_MAX_OUTPUTS;
+ capa->max_segs_per_input = ML_MAX_IO_SEGS;
+ capa->max_segs_per_output = ML_MAX_IO_SEGS;
+ capa->min_input_align = 1;
+ capa->min_output_align = 1;
+
+ capa->load.compl_mode_mask = ODP_ML_COMPL_MODE_SYNC |
+ ODP_ML_COMPL_MODE_POLL |
+ ODP_ML_COMPL_MODE_EVENT;
+ capa->load.compl_queue_plain = 1;
+ capa->load.compl_queue_sched = 1;
+
+ capa->run.compl_mode_mask = ODP_ML_COMPL_MODE_SYNC |
+ ODP_ML_COMPL_MODE_POLL |
+ ODP_ML_COMPL_MODE_EVENT;
+ capa->run.compl_queue_plain = 1;
+ capa->run.compl_queue_sched = 1;
+
+ if (odp_pool_capability(&pool_capa)) {
+ _ODP_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ capa->pool.max_pools = pool_capa.buf.max_pools;
+ capa->pool.max_num = pool_capa.buf.max_num;
+ capa->pool.max_uarea_size = pool_capa.buf.max_uarea_size;
+ capa->pool.uarea_persistence = pool_capa.buf.uarea_persistence;
+ capa->pool.max_cache_size = pool_capa.buf.max_cache_size;
+ capa->pool.min_cache_size = pool_capa.buf.min_cache_size;
+
+ return 0;
+}
+
+void odp_ml_config_init(odp_ml_config_t *config)
+{
+ memset(config, 0, sizeof(odp_ml_config_t));
+ config->max_models_created = 1;
+ config->max_models_loaded = 1;
+}
+
+int odp_ml_config(const odp_ml_config_t *config)
+{
+ if (!config) {
+ _ODP_ERR("Error: config must not be NULL\n");
+ return -1;
+ }
+
+ if (config->max_model_size == 0 || config->max_models_created == 0 ||
+ config->max_models_loaded == 0) {
+ _ODP_ERR("Error: max_model_size, max_models_created and max_models_loaded"
+ " must be bigger than 0\n");
+ return -1;
+ }
+
+ if (config->max_models_loaded > config->max_models_created) {
+ _ODP_ERR("Error: max_models_loaded %d exceeds max_models_created %d\n",
+ config->max_models_loaded, config->max_models_created);
+ return -1;
+ }
+
+ if (config->max_models_created > ML_MAX_MODELS_CREATED) {
+ _ODP_ERR("Error: max_models_created %d exceeds maximum number"
+ " of models that can be created in this driver %d\n",
+ config->max_models_created, ML_MAX_MODELS_CREATED);
+ return -1;
+ }
+
+ if (config->max_models_loaded > ML_MAX_MODELS_LOADED) {
+ _ODP_ERR("Error: max_models_loaded %d exceeds maximum number"
+ " of models that can be loaded in this driver %d\n",
+ config->max_models_loaded, ML_MAX_MODELS_LOADED);
+ return -1;
+ }
+
+ if (config->max_model_size > ML_MAX_MODEL_SIZE) {
+ _ODP_ERR("max_model_size %" PRIu64 " exceeds supported maximum model size %d\n",
+ config->max_model_size, ML_MAX_MODEL_SIZE);
+ return -1;
+ }
+
+ _odp_ml_glb->ml_config = *config;
+ return 0;
+}
+
+void odp_ml_model_param_init(odp_ml_model_param_t *param)
+{
+ memset(param, 0, sizeof(odp_ml_model_param_t));
+}
+
+static int check_ortstatus(OrtStatus * const status)
+{
+ if (status != NULL) {
+ const char *msg = _odp_ml_glb->ort_api->GetErrorMessage(status);
+
+ _ODP_ERR("%s\n", msg);
+ _odp_ml_glb->ort_api->ReleaseStatus(status);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Get model input and output count */
+static int get_model_io_count(OrtSession *model, uint32_t *num_inputs, uint32_t *num_outputs)
+{
+ size_t num = 0;
+ OrtStatus *status = NULL;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ status = ort_api->SessionGetInputCount(model, &num);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Get model input count failed\n");
+ return -1;
+ }
+
+ *num_inputs = num;
+ _ODP_DBG("num_inputs: %u\n", *num_inputs);
+
+ status = ort_api->SessionGetOutputCount(model, &num);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Get model output count failed\n");
+ return -1;
+ }
+
+ *num_outputs = num;
+ _ODP_DBG("num_outputs: %u\n", *num_outputs);
+
+ return 0;
+}
+
+static odp_ml_data_type_t onnx_dtype_to_odp_dtype(ONNXTensorElementDataType onnx_dtype)
+{
+ switch (onnx_dtype) {
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT:
+ return ODP_ML_DATA_TYPE_FP32;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
+ return ODP_ML_DATA_TYPE_UINT8;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
+ return ODP_ML_DATA_TYPE_INT8;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
+ return ODP_ML_DATA_TYPE_UINT16;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
+ return ODP_ML_DATA_TYPE_INT16;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
+ return ODP_ML_DATA_TYPE_INT32;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
+ return ODP_ML_DATA_TYPE_UINT32;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
+ return ODP_ML_DATA_TYPE_INT64;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
+ return ODP_ML_DATA_TYPE_UINT64;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
+ return ODP_ML_DATA_TYPE_FP16;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16:
+ return ODP_ML_DATA_TYPE_BFP16;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE:
+ return ODP_ML_DATA_TYPE_FP64;
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
+ /* Fall through */
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64:
+ /* Fall through */
+ case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128:
+ /* Fall through */
+ default:
+ _ODP_ERR("onnx_dtype %d not supported by odp_ml\n", onnx_dtype);
+ return ODP_ML_DATA_TYPE_NONE;
+ }
+}
+
+/* Get the size of given odp_ml_data_type_t in bytes */
+static uint32_t size_of_odp_ml_data_type(odp_ml_data_type_t data_type)
+{
+ switch (data_type) {
+ case ODP_ML_DATA_TYPE_NONE:
+ return 0;
+
+ case ODP_ML_DATA_TYPE_INT8:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_UINT8:
+ return 1;
+
+ case ODP_ML_DATA_TYPE_INT16:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_UINT16:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_FP16:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_BFP16:
+ return 2;
+
+ case ODP_ML_DATA_TYPE_INT24:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_UINT24:
+ return 3;
+
+ case ODP_ML_DATA_TYPE_INT32:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_UINT32:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_FP32:
+ return 4;
+
+ case ODP_ML_DATA_TYPE_INT64:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_UINT64:
+ /* Fall through */
+ case ODP_ML_DATA_TYPE_FP64:
+ return 8;
+
+ default:
+ return 0;
+ }
+}
+
+static int get_shape(int64_t dims[], odp_ml_shape_info_t *shape)
+{
+ uint32_t dyn_cnt = 0;
+
+ for (uint32_t i = 0; i < shape->num_dim; i++) {
+ if (dims[i] == 0) {
+ _ODP_ERR("Dimension value: %" PRId64 " must be at least 1\n", dims[i]);
+ return -1;
+ } else if (dims[i] == -1) { /* Symbolic dimension */
+ dyn_cnt++;
+ shape->dim[i] = ODP_ML_DIM_DYNAMIC;
+ shape->dim_min[i] = 0; /*unknown*/
+ shape->dim_max[i] = 0; /*unknown*/
+ } else if (dims[i] > 0 && dims[i] < UINT32_MAX) {
+ shape->dim[i] = dims[i];
+ shape->dim_min[i] = dims[i];
+ shape->dim_max[i] = dims[i];
+ } else {
+ _ODP_ERR("Dimension value: %" PRId64 " invalid\n", dims[i]);
+ return -1;
+ }
+ }
+
+ if (dyn_cnt == 0) {
+ shape->type = ODP_ML_SHAPE_STATIC;
+ } else if (dyn_cnt == 1) {
+ shape->type = ODP_ML_SHAPE_BATCH;
+ } else {
+ _ODP_ERR("Data shape type not supported by ODP\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void calculate_model_io_size(const odp_ml_shape_info_t *shape, uint64_t *size)
+{
+ /* Calculate the data size in bytes of this tensor, 0 for tensors with
+ * dynamic batch sizes */
+ for (size_t i = 0; i < shape->num_dim; i++) {
+ /* Skip dynamic dimension size */
+ if (shape->dim[i] == ODP_ML_DIM_DYNAMIC) {
+ *size = 0;
+ break;
+ }
+ (*size) *= shape->dim[i];
+ }
+}
+
+static int get_model_io_type_shape_size(OrtTypeInfo *type_info, odp_ml_shape_info_t *shape,
+ odp_ml_data_type_t *data_type, uint32_t *data_type_size,
+ uint64_t *size)
+{
+ ONNXTensorElementDataType tensor_type;
+ const OrtTensorTypeAndShapeInfo *tensor_info;
+ size_t num_dim = 0;
+ OrtStatus *status = NULL;
+ int64_t dims[ODP_ML_MAX_DIMS] = {0};
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ status = ort_api->CastTypeInfoToTensorInfo(type_info, &tensor_info);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("CastTypeInfoToTensorInfo failed\n");
+ return -1;
+ }
+
+ status = ort_api->GetTensorElementType(tensor_info, &tensor_type);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetTensorElementType failed\n");
+ return -1;
+ }
+
+ *data_type = onnx_dtype_to_odp_dtype(tensor_type);
+ if (*data_type == ODP_ML_DATA_TYPE_NONE) /* Type not supported by odp */
+ return -1;
+
+ status = ort_api->GetDimensionsCount(tensor_info, &num_dim);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetDimensionsCount failed\n");
+ return -1;
+ }
+
+ if (num_dim > ODP_ML_MAX_DIMS) {
+ _ODP_ERR("Number of dimensions: %zu exceeds supported maximum number"
+ " of dimensions: %d\n", num_dim, ODP_ML_MAX_DIMS);
+ return -1;
+ }
+ shape->num_dim = num_dim;
+
+ status = ort_api->GetDimensions(tensor_info, dims, num_dim);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetDimensions failed\n");
+ return -1;
+ }
+
+ if (get_shape(dims, shape))
+ return -1;
+
+ *data_type_size = size_of_odp_ml_data_type(*data_type);
+
+ *size = *data_type_size;
+ calculate_model_io_size(shape, size);
+
+ return 0;
+}
+
+/* Get model input and output info */
+static int get_model_io_info(OrtSession *session, ml_model_t *mdl,
+ const odp_ml_model_param_t *param)
+{
+ char *name;
+ OrtTypeInfo *type_info;
+ const odp_ml_data_format_t *data_format;
+ OrtStatus *status = NULL;
+ OrtAllocator *allocator = NULL;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+ odp_ml_input_info_t *input_info = mdl->input_info;
+ odp_ml_output_info_t *output_info = mdl->output_info;
+
+ status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetAllocatorWithDefaultOptions failed\n");
+ return -1;
+ }
+
+ /* Retrieve info about input array. */
+ memset(input_info, 0, sizeof(mdl->input_info));
+ for (uint32_t i = 0; i < mdl->info.num_inputs; i++) {
+ name = NULL;
+ status = ort_api->SessionGetInputName(session, i, allocator, &name);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Get %uth input name failed\n", i);
+ return -1;
+ }
+
+ _odp_strcpy(input_info[i].name, name, ODP_ML_MODEL_IO_NAME_LEN);
+
+ /* Free memory allocated by SessionGetInputName */
+ status = ort_api->AllocatorFree(allocator, name);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("AllocatorFree %uth input_name failed\n", i);
+ return -1;
+ }
+
+ if (param->extra_info.num_inputs) {
+ data_format = &param->extra_info.input_format[i];
+
+ input_info[i].shape = data_format->shape;
+ input_info[i].data_type = data_format->data_type;
+ input_info[i].data_type_size = data_format->data_type_size;
+
+ mdl->input_sizes[i] = input_info[i].data_type_size;
+ calculate_model_io_size(&data_format->shape, &mdl->input_sizes[i]);
+ continue;
+ }
+
+ type_info = NULL;
+ status = ort_api->SessionGetInputTypeInfo(session, i, &type_info);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SessionGetInputTypeInfo failed\n");
+ return -1;
+ }
+
+ if (get_model_io_type_shape_size(type_info, &input_info[i].shape,
+ &input_info[i].data_type,
+ &input_info[i].data_type_size,
+ &mdl->input_sizes[i])) {
+ _ODP_ERR("get_model_io_type_shape_size() for input failed\n");
+ ort_api->ReleaseTypeInfo(type_info);
+ return -1;
+ }
+
+ ort_api->ReleaseTypeInfo(type_info);
+ }
+
+ /* Retrieve info about output array. */
+ memset(output_info, 0, sizeof(mdl->output_info));
+ for (uint32_t i = 0; i < mdl->info.num_outputs; i++) {
+ name = NULL;
+ status = ort_api->SessionGetOutputName(session, i, allocator, &name);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Get %uth output name failed\n", i);
+ return -1;
+ }
+
+ _odp_strcpy(output_info[i].name, name, ODP_ML_MODEL_IO_NAME_LEN);
+
+ /* Free memory allocated by SessionGetOutputName */
+ status = ort_api->AllocatorFree(allocator, name);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("AllocatorFree %uth output_name failed\n", i);
+ return -1;
+ }
+
+ if (param->extra_info.num_outputs) {
+ data_format = &param->extra_info.output_format[i];
+
+ output_info[i].shape = data_format->shape;
+ output_info[i].data_type = data_format->data_type;
+ output_info[i].data_type_size = data_format->data_type_size;
+
+ mdl->output_sizes[i] = output_info[i].data_type_size;
+ calculate_model_io_size(&data_format->shape, &mdl->output_sizes[i]);
+ continue;
+ }
+
+ type_info = NULL;
+ status = ort_api->SessionGetOutputTypeInfo(session, i, &type_info);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SessionGetOutputTypeInfo failed\n");
+ return -1;
+ }
+
+ if (get_model_io_type_shape_size(type_info, &output_info[i].shape,
+ &output_info[i].data_type,
+ &output_info[i].data_type_size,
+ &mdl->output_sizes[i])) {
+ _ODP_ERR("get_model_io_type_shape_size() for output failed\n");
+ ort_api->ReleaseTypeInfo(type_info);
+ return -1;
+ }
+
+ ort_api->ReleaseTypeInfo(type_info);
+ }
+
+ return 0;
+}
+
+static inline int check_model_io_num(const odp_ml_model_param_t *param,
+ uint32_t num_inputs, uint32_t num_outputs)
+{
+ /* Make sure the number of inputs/outputs not exceeding the supported
+ * model max inputs/outputs */
+ if (num_inputs > CONFIG_ML_MAX_INPUTS) {
+ _ODP_ERR("The model's number of inputs %u exceeds the maximum "
+ "number of inputs supported in a model %u\n",
+ num_inputs, CONFIG_ML_MAX_INPUTS);
+ return -1;
+ }
+
+ if (num_outputs > CONFIG_ML_MAX_OUTPUTS) {
+ _ODP_ERR("The model's number of outputs %u exceeds the maximum "
+ "number of outputs supported in a model %u\n",
+ num_outputs, CONFIG_ML_MAX_OUTPUTS);
+
+ return -1;
+ }
+
+ /* Make sure the numbers of inputs/outputs provided in the extra_info of
+ * param match the numbers defined in model metadata. */
+ if (param->extra_info.num_inputs &&
+ param->extra_info.num_inputs != num_inputs) {
+ _ODP_ERR("Provided param->extra_info.num_inputs %u does not match the"
+ " number of inputs defined in model metadata: %u\n",
+ param->extra_info.num_inputs, num_inputs);
+ return -1;
+ }
+
+ if (param->extra_info.num_outputs && param->extra_info.num_outputs != num_outputs) {
+ _ODP_ERR("Provided param->extra_info.num_outputs %u does not match the"
+ " number of outputs defined in model metadata: %u\n",
+ param->extra_info.num_outputs, num_outputs);
+ return -1;
+ }
+
+ if (param->extra_info.num_inputs && !param->extra_info.input_format) {
+ _ODP_ERR("num_inputs is provided but not input_format in param->extra_info\n");
+ return -1;
+ }
+
+ if (param->extra_info.num_outputs && !param->extra_info.output_format) {
+ _ODP_ERR("num_outputs is provided but not output_format in param->extra_info\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_ort_model(const odp_ml_model_param_t *param, OrtSession **session,
+ ml_model_t *mdl, OrtSessionOptions *session_opts)
+{
+ OrtStatus *status;
+ int64_t model_version;
+ uint32_t num_inputs = 0;
+ uint32_t num_outputs = 0;
+ OrtModelMetadata *metadata = {0};
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ status = ort_api->CreateSessionFromArray(_odp_ml_glb->env,
+ param->model,
+ param->size,
+ session_opts,
+ session);
+ if (check_ortstatus(status) || !(*session)) {
+ _ODP_ERR("CreateSessionFromArray failed\n");
+ return -1;
+ }
+
+ if (get_model_io_count(*session, &num_inputs, &num_outputs)) {
+ _ODP_ERR("get_model_io_count() failed\n");
+ ort_api->ReleaseSession(*session);
+ return -1;
+ }
+
+ if (check_model_io_num(param, num_inputs, num_outputs)) {
+ ort_api->ReleaseSession(*session);
+ return -1;
+ }
+
+ mdl->max_compl_id = param->max_compl_id;
+ mdl->info.num_inputs = num_inputs;
+ mdl->info.num_outputs = num_outputs;
+
+ /* Get metadata */
+ status = ort_api->SessionGetModelMetadata(*session, &metadata);
+ if (check_ortstatus(status) || !metadata) {
+ _ODP_ERR("SessionGetModelMetadata failed\n");
+ ort_api->ReleaseSession(*session);
+ return -1;
+ }
+
+ /* Get model version */
+ status = ort_api->ModelMetadataGetVersion(metadata, &model_version);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("ModelMetadataGetVersion failed\n");
+ ort_api->ReleaseModelMetadata(metadata);
+ ort_api->ReleaseSession(*session);
+ return -1;
+ }
+ mdl->info.model_version = model_version;
+ mdl->info.interface_version = 0;
+
+ if (get_model_io_info(*session, mdl, param)) {
+ _ODP_ERR("get_model_io_info() failed\n");
+ ort_api->ReleaseModelMetadata(metadata);
+ ort_api->ReleaseSession(*session);
+ return -1;
+ }
+
+ ort_api->ReleaseModelMetadata(metadata);
+ return 0;
+}
+
+static int set_ort_run_opts(const char *name, OrtSessionOptions *se_opts)
+{
+ OrtStatus *status;
+ ort_run_opts_t *opts = &_odp_ml_glb->ort_run_opts;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ if (opts->enable_profiling) {
+ status = ort_api->EnableProfiling(se_opts, name);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Enable profiling failed\n");
+ return -1;
+ }
+ }
+
+ status = ort_api->SetSessionExecutionMode(se_opts, opts->execution_mode);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SetSessionExecutionMode failed\n");
+ return -1;
+ }
+
+ if (opts->intra_op_num_threads) {
+ status = ort_api->SetIntraOpNumThreads(se_opts, opts->intra_op_num_threads);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SetIntraOpNumThreads failed\n");
+ return -1;
+ }
+ }
+
+ if (opts->inter_op_num_threads) {
+ status = ort_api->SetInterOpNumThreads(se_opts, opts->inter_op_num_threads);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SetInterOpNumThreads failed\n");
+ return -1;
+ }
+ }
+
+ status = ort_api->SetSessionGraphOptimizationLevel(se_opts, opts->graph_opt_level);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SetSessionGraphOptimizationLevel failed\n");
+ return -1;
+ }
+
+ /* Optimized model file path is not provided */
+ if (opts->opt_model_filepath[0] == '\0')
+ return 0;
+
+ status = ort_api->SetOptimizedModelFilePath(se_opts, opts->opt_model_filepath);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("SetOptimizedModelFilePath failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void reset_mdl_info_sizes(ml_model_t *mdl)
+{
+ memset(&mdl->info, 0, sizeof(odp_ml_model_info_t));
+ memset(mdl->input_info, 0, sizeof(mdl->input_info));
+ memset(mdl->output_info, 0, sizeof(mdl->output_info));
+ memset(mdl->input_sizes, 0, sizeof(mdl->input_sizes));
+ memset(mdl->output_sizes, 0, sizeof(mdl->output_sizes));
+}
+
+static int check_io_shape(ml_model_t *mdl)
+{
+ odp_ml_shape_info_t *shape;
+
+ for (uint32_t i = 0; i < mdl->info.num_inputs; i++) {
+ shape = &mdl->input_info[i].shape;
+
+ if (shape->type == ODP_ML_SHAPE_NONE) {
+ _ODP_ERR("Undefined shape type for model input[%u]\n", i);
+ return -1;
+ }
+
+ if (shape->type == ODP_ML_SHAPE_STATIC)
+ continue;
+
+ /* shape->type == ODP_ML_SHAPE_BATCH */
+ for (uint32_t j = 0; j < shape->num_dim; j++) {
+ if (shape->dim[j] == ODP_ML_DIM_DYNAMIC && !shape->dim_max[j]) {
+ _ODP_ERR("Missing dim_max[%u] for dynamic sized input[%u], please"
+ " provide via the extra_info of model param\n", j, i);
+ return -1;
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < mdl->info.num_outputs; i++) {
+ if (mdl->output_info[i].shape.type == ODP_ML_SHAPE_NONE) {
+ _ODP_ERR("Undefined shape type for model output[%u]\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+odp_ml_model_t odp_ml_model_create(const char *name, const odp_ml_model_param_t *param)
+{
+ OrtStatus *status;
+ odp_ml_model_info_t *info;
+ OrtSessionOptions *session_opts;
+ uint32_t i = 0;
+ ml_model_t *mdl = NULL;
+ OrtSession *session = NULL;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ if (odp_unlikely(odp_global_ro.disable.ml)) {
+ _ODP_ERR("ML is disabled\n");
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ if (odp_unlikely(param->size > _odp_ml_glb->ml_config.max_model_size)) {
+ _ODP_ERR("Model size %" PRIu64 " exceeds maximum model size configured %" PRIu64 "\n",
+ param->size, _odp_ml_glb->ml_config.max_model_size);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ if (odp_unlikely(!param->size || !param->model)) {
+ _ODP_ERR("Invalid model param: param->model: %p, param->size: %" PRIu64 "\n",
+ param->model, param->size);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ if (odp_unlikely(param->max_compl_id > ML_MAX_COMPL_ID)) {
+ _ODP_ERR("param->max_compl_id: %u exceeds maximum completion id supported: %d\n",
+ param->max_compl_id, ML_MAX_COMPL_ID);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ /* Find an emtpy slot to store the new model */
+ for (i = 0; i < ML_MAX_MODELS_CREATED; i++) {
+ if (_odp_ml_glb->models[i].state)
+ continue;
+
+ odp_ticketlock_lock(&_odp_ml_glb->models[i].lock);
+
+ if (_odp_ml_glb->models[i].state) {
+ odp_ticketlock_unlock(&_odp_ml_glb->models[i].lock);
+ continue;
+ }
+
+ mdl = &_odp_ml_glb->models[i];
+ break;
+ }
+
+ if (i == ML_MAX_MODELS_CREATED) {
+ _ODP_ERR("Maximum number of models has already been created!\n");
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ /* Free model entry was found and is now locked */
+ mdl->state = ML_STATE_CREATED;
+
+ status = ort_api->CreateSessionOptions(&session_opts);
+ if (check_ortstatus(status) || !session_opts) {
+ _ODP_ERR("Error: CreateSessionOptions failed.\n");
+ mdl->state = ML_STATE_FREE;
+ odp_ticketlock_unlock(&mdl->lock);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ if (set_ort_run_opts(name, session_opts)) {
+ _odp_ml_glb->ort_api->ReleaseSessionOptions(session_opts);
+ mdl->state = ML_STATE_FREE;
+ odp_ticketlock_unlock(&mdl->lock);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ /* Store model info */
+ info = &mdl->info;
+ memset(info, 0, sizeof(odp_ml_model_info_t));
+
+ if (create_ort_model(param, &session, mdl, session_opts)) {
+ mdl->state = ML_STATE_FREE;
+
+ /* Initialize info back to 0 when some fields have been filled
+ * while later failed */
+ reset_mdl_info_sizes(mdl);
+ odp_ticketlock_unlock(&mdl->lock);
+
+ _odp_ml_glb->ort_api->ReleaseSessionOptions(session_opts);
+ _ODP_ERR("create_ort_model() failed\n");
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ if (check_io_shape(mdl)) {
+ mdl->state = ML_STATE_FREE;
+ reset_mdl_info_sizes(mdl);
+ odp_ticketlock_unlock(&mdl->lock);
+
+ ort_api->ReleaseSession(session);
+ _odp_ml_glb->ort_api->ReleaseSessionOptions(session_opts);
+ return ODP_ML_MODEL_INVALID;
+ }
+
+ mdl->session = session;
+ mdl->session_opts = session_opts;
+ info->index = i;
+
+ if (name)
+ _odp_strcpy(info->name, name, ODP_ML_MODEL_NAME_LEN);
+
+ mdl->max_compl_id = param->max_compl_id;
+ for (uint32_t j = 0; j < ML_MAX_COMPL_ID; j++)
+ odp_atomic_init_u32(&mdl->compl_status[j], 1);
+
+ odp_ticketlock_unlock(&mdl->lock);
+ return (odp_ml_model_t)mdl;
+}
+
+int odp_ml_model_destroy(odp_ml_model_t model)
+{
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (model == ODP_ML_MODEL_INVALID) {
+ _ODP_ERR("Bad ML model handle\n");
+ return -1;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+
+ if (mdl->state != ML_STATE_CREATED) {
+ _ODP_ERR("Model not created\n");
+ odp_ticketlock_unlock(&mdl->lock);
+ return -1;
+ }
+
+ _odp_ml_glb->ort_api->ReleaseSessionOptions(mdl->session_opts);
+ _odp_ml_glb->ort_api->ReleaseSession(mdl->session);
+ mdl->state = ML_STATE_FREE;
+ mdl->session = NULL;
+ odp_ticketlock_unlock(&mdl->lock);
+
+ return 0;
+}
+
+int odp_ml_model_info(odp_ml_model_t model, odp_ml_model_info_t *info)
+{
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(!info)) {
+ _ODP_ERR("info must not be NULL\n");
+ return -1;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ if (odp_unlikely(mdl->state == ML_STATE_FREE)) {
+ _ODP_ERR("Model not created\n");
+ odp_ticketlock_unlock(&mdl->lock);
+ return -1;
+ }
+
+ *info = mdl->info;
+
+ odp_ticketlock_unlock(&mdl->lock);
+ return 0;
+}
+
+uint32_t odp_ml_model_input_info(odp_ml_model_t model, odp_ml_input_info_t info[], uint32_t num)
+{
+ uint32_t num_model_inputs;
+ uint32_t num_written;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return 0;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ num_model_inputs = mdl->info.num_inputs;
+ num_written = num_model_inputs >= num ? num : num_model_inputs;
+
+ if (num == 0) {
+ odp_ticketlock_unlock(&mdl->lock);
+ return num_model_inputs;
+ }
+
+ for (uint32_t i = 0; i < num_written; i++)
+ info[i] = mdl->input_info[i];
+
+ odp_ticketlock_unlock(&mdl->lock);
+ return num_model_inputs;
+}
+
+uint32_t odp_ml_model_output_info(odp_ml_model_t model, odp_ml_output_info_t info[], uint32_t num)
+{
+ uint32_t num_model_outputs;
+ uint32_t num_written;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return 0;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ num_model_outputs = mdl->info.num_outputs;
+ num_written = num_model_outputs >= num ? num : num_model_outputs;
+
+ if (num == 0) {
+ odp_ticketlock_unlock(&mdl->lock);
+ return num_model_outputs;
+ }
+
+ for (uint32_t i = 0; i < num_written; i++)
+ info[i] = mdl->output_info[i];
+
+ odp_ticketlock_unlock(&mdl->lock);
+ return num_model_outputs;
+}
+
+odp_ml_model_t odp_ml_model_lookup(const char *name)
+{
+ uint32_t i;
+ ml_model_t *mdl;
+
+ for (i = 0; i < ML_MAX_MODELS_CREATED; i++) {
+ mdl = &_odp_ml_glb->models[i];
+
+ odp_ticketlock_lock(&mdl->lock);
+
+ if (mdl->state == ML_STATE_FREE) {
+ odp_ticketlock_unlock(&mdl->lock);
+ continue;
+ }
+
+ if (!strcmp(mdl->info.name, name)) {
+ /* found it */
+ odp_ticketlock_unlock(&mdl->lock);
+ return (odp_ml_model_t)mdl;
+ }
+ odp_ticketlock_unlock(&mdl->lock);
+ }
+
+ return ODP_ML_MODEL_INVALID;
+}
+
+uint64_t odp_ml_model_to_u64(odp_ml_model_t model)
+{
+ return _odp_pri(model);
+}
+
+static const char *data_type_str(odp_ml_data_type_t data_type)
+{
+ switch (data_type) {
+ case ODP_ML_DATA_TYPE_INT8:
+ return "int8";
+ case ODP_ML_DATA_TYPE_UINT8:
+ return "uint8";
+ case ODP_ML_DATA_TYPE_UINT16:
+ return "uint16";
+ case ODP_ML_DATA_TYPE_INT16:
+ return "int16";
+ case ODP_ML_DATA_TYPE_INT32:
+ return "int32";
+ case ODP_ML_DATA_TYPE_UINT32:
+ return "uint32";
+ case ODP_ML_DATA_TYPE_INT64:
+ return "int64";
+ case ODP_ML_DATA_TYPE_UINT64:
+ return "uint64";
+ case ODP_ML_DATA_TYPE_FP16:
+ return "fp16";
+ case ODP_ML_DATA_TYPE_FP32:
+ return "fp32";
+ case ODP_ML_DATA_TYPE_BFP16:
+ return "bfp16";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *shape_type_str(odp_ml_shape_type_t shape_type)
+{
+ switch (shape_type) {
+ case ODP_ML_SHAPE_NONE:
+ return "none";
+ case ODP_ML_SHAPE_STATIC:
+ return "static";
+ case ODP_ML_SHAPE_BATCH:
+ return "batch";
+ default:
+ return "Unknown";
+ }
+}
+
+static void print_shape(const odp_ml_shape_info_t *shape)
+{
+ /* Print shape */
+ _ODP_PRINT("Shape: %s [", shape_type_str(shape->type));
+
+ for (uint32_t i = 0; i < shape->num_dim; i++) {
+ if (shape->dim[i] == ODP_ML_DIM_DYNAMIC)
+ _ODP_PRINT("Dyn");
+ else
+ _ODP_PRINT("%" PRIu32, shape->dim[i]);
+
+ if (i == (shape->num_dim - 1))
+ _ODP_PRINT("]\n");
+ else
+ _ODP_PRINT(", ");
+ }
+
+ /* The number of dimensions for a scalar input is 0, in which case did not
+ * go into above for loop */
+ if (shape->num_dim == 0)
+ _ODP_PRINT("]\n");
+}
+
+void odp_ml_model_print(odp_ml_model_t model)
+{
+ ml_model_t *mdl = ml_model_from_handle(model);
+ const odp_ml_model_info_t * const info = &mdl->info;
+ const odp_ml_input_info_t * const input_info = mdl->input_info;
+ const odp_ml_output_info_t * const output_info = mdl->output_info;
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ if (odp_unlikely(mdl->state == ML_STATE_FREE)) {
+ odp_ticketlock_unlock(&mdl->lock);
+ _ODP_ERR("Model not created\n");
+ return;
+ }
+
+ _ODP_PRINT("\nModel info\n");
+ _ODP_PRINT("----------\n");
+ _ODP_PRINT(" Model handle: 0x%" PRIx64 "\n", odp_ml_model_to_u64(model));
+ _ODP_PRINT(" Name: %s\n", info->name);
+ _ODP_PRINT(" Model version: %" PRIu64 "\n", info->model_version);
+ _ODP_PRINT(" Model interface version: %" PRIu64 "\n", info->interface_version);
+ _ODP_PRINT(" Index: %u\n", info->index);
+ _ODP_PRINT(" Number of inputs: %u\n", info->num_inputs);
+
+ for (uint32_t i = 0; i < info->num_inputs; i++) {
+ _ODP_PRINT(" Input[%u]: ", i);
+ _ODP_PRINT("Name: %s, ", input_info[i].name);
+ _ODP_PRINT("Data_type: %s, ", data_type_str(input_info[i].data_type));
+ print_shape(&input_info[i].shape);
+ }
+
+ _ODP_PRINT(" Number of outputs: %u\n", info->num_outputs);
+ for (uint32_t i = 0; i < info->num_outputs; i++) {
+ _ODP_PRINT(" Output[%u]: ", i);
+ _ODP_PRINT("Name: %s, ", output_info[i].name);
+ _ODP_PRINT("Data_type: %s, ", data_type_str(output_info[i].data_type));
+ print_shape(&output_info[i].shape);
+ }
+
+ odp_ticketlock_unlock(&mdl->lock);
+
+ _ODP_PRINT("\n");
+}
+
+static inline void mode_print(odp_ml_compl_mode_t compl_mode_mask)
+{
+ if (compl_mode_mask & ODP_ML_COMPL_MODE_SYNC)
+ _ODP_PRINT(" syn");
+
+ if (compl_mode_mask & ODP_ML_COMPL_MODE_POLL)
+ _ODP_PRINT(" poll");
+
+ if (compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ _ODP_PRINT(" event");
+}
+
+void odp_ml_print(void)
+{
+ _ODP_PRINT("\nML info\n");
+ _ODP_PRINT("-----------\n");
+ _ODP_PRINT(" max_model_size: %u\n", ML_MAX_MODEL_SIZE);
+ _ODP_PRINT(" max_compl_id: %u\n", ML_MAX_COMPL_ID);
+ _ODP_PRINT(" max_models_created: %u\n", ML_MAX_MODELS_CREATED);
+ _ODP_PRINT(" max_models_loaded: %u\n", ML_MAX_MODELS_LOADED);
+ _ODP_PRINT(" model_max_inputs: %u\n", CONFIG_ML_MAX_INPUTS);
+ _ODP_PRINT(" model_max_outputs: %u\n", CONFIG_ML_MAX_OUTPUTS);
+
+ _ODP_PRINT(" load:\n");
+ _ODP_PRINT(" completion mode: ");
+ mode_print(_odp_ml_glb->capa.load.compl_mode_mask);
+ _ODP_PRINT(", plain queue: %c, schedule queue: %c\n",
+ _odp_ml_glb->capa.load.compl_queue_plain ? 'Y' : 'N',
+ _odp_ml_glb->capa.load.compl_queue_sched ? 'Y' : 'N');
+
+ _ODP_PRINT(" run:\n");
+ _ODP_PRINT(" completion mode:");
+ mode_print(_odp_ml_glb->capa.run.compl_mode_mask);
+ _ODP_PRINT(", plain queue: %c, schedule queue: %c\n",
+ _odp_ml_glb->capa.run.compl_queue_plain ? 'Y' : 'N',
+ _odp_ml_glb->capa.run.compl_queue_sched ? 'Y' : 'N');
+ _ODP_PRINT("\n");
+}
+
+int odp_ml_model_extra_stat_info(odp_ml_model_t model,
+ odp_ml_extra_stat_info_t info[] ODP_UNUSED,
+ int num ODP_UNUSED)
+{
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int odp_ml_model_extra_stats(odp_ml_model_t model, uint64_t stats[] ODP_UNUSED, int num ODP_UNUSED)
+{
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void odp_ml_compl_pool_param_init(odp_ml_compl_pool_param_t *pool_param)
+{
+ if (odp_unlikely(!pool_param)) {
+ _ODP_ERR("Param 'pool_param' must not NULL\n");
+ return;
+ }
+
+ memset(pool_param, 0, sizeof(odp_ml_compl_pool_param_t));
+
+ pool_param->cache_size = _odp_ml_glb->pool_param.buf.cache_size;
+}
+
+odp_pool_t odp_ml_compl_pool_create(const char *name, const odp_ml_compl_pool_param_t *pool_param)
+{
+ odp_pool_t pool;
+ odp_pool_param_t ml_pool_param;
+ uint32_t num = pool_param->num;
+ uint32_t uarea_size = pool_param->uarea_size;
+ uint32_t cache_size = pool_param->cache_size;
+ uint32_t buf_size = _ODP_MAX(sizeof(odp_ml_run_result_t),
+ sizeof(odp_ml_load_result_t));
+
+ if (num > _odp_ml_glb->capa.pool.max_num) {
+ _ODP_ERR("Too many ML completion events: %u\n", num);
+ return ODP_POOL_INVALID;
+ }
+
+ if (uarea_size > _odp_ml_glb->capa.pool.max_uarea_size) {
+ _ODP_ERR("Bad uarea size: %u\n", uarea_size);
+ return ODP_POOL_INVALID;
+ }
+
+ if (cache_size < _odp_ml_glb->capa.pool.min_cache_size ||
+ cache_size > _odp_ml_glb->capa.pool.max_cache_size) {
+ _ODP_ERR("Bad cache size: %u\n", cache_size);
+ return ODP_POOL_INVALID;
+ }
+
+ odp_pool_param_init(&ml_pool_param);
+ ml_pool_param.type = ODP_POOL_BUFFER;
+ ml_pool_param.uarea_init.init_fn = pool_param->uarea_init.init_fn;
+ ml_pool_param.uarea_init.args = pool_param->uarea_init.args;
+ ml_pool_param.buf.num = num;
+ ml_pool_param.buf.cache_size = cache_size;
+ ml_pool_param.buf.size = buf_size;
+ ml_pool_param.buf.uarea_size = uarea_size;
+
+ pool = _odp_pool_create(name, &ml_pool_param, ODP_POOL_ML_COMPL);
+
+ return pool;
+}
+
+odp_ml_compl_t odp_ml_compl_alloc(odp_pool_t pool)
+{
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_ml_run_result_t *result;
+ uint32_t buf_size = _ODP_MAX(sizeof(odp_ml_run_result_t),
+ sizeof(odp_ml_load_result_t));
+
+ buf = odp_buffer_alloc(pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+ return ODP_ML_COMPL_INVALID;
+
+ result = odp_buffer_addr(buf);
+ memset(result, 0, buf_size);
+
+ ev = odp_buffer_to_event(buf);
+ _odp_event_type_set(ev, ODP_EVENT_ML_COMPL);
+
+ return (odp_ml_compl_t)(uintptr_t)buf;
+}
+
+void odp_ml_compl_free(odp_ml_compl_t ml_compl)
+{
+ odp_event_t ev;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)ml_compl;
+
+ if (odp_unlikely(ml_compl == ODP_ML_COMPL_INVALID)) {
+ _ODP_ERR("Bad ML job completion handle\n");
+ return;
+ }
+
+ ev = odp_buffer_to_event(buf);
+ _odp_event_type_set(ev, ODP_EVENT_BUFFER);
+
+ odp_buffer_free(buf);
+}
+
+int odp_ml_compl_run_result(odp_ml_compl_t ml_compl, odp_ml_run_result_t *result)
+{
+ odp_event_subtype_t subtype;
+ odp_ml_run_result_t *run_result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)ml_compl;
+ odp_event_t ev = odp_buffer_to_event(buf);
+
+ if (odp_unlikely(ml_compl == ODP_ML_COMPL_INVALID)) {
+ _ODP_ERR("Given ML completion event is invalid\n");
+ return -2;
+ }
+
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_ML_COMPL ||
+ subtype != ODP_EVENT_ML_COMPL_RUN) {
+ _ODP_ERR("Given completion event has wrong event type or subtype\n");
+ return -2;
+ }
+
+ run_result = odp_buffer_addr(buf);
+ if (result)
+ *result = *run_result;
+
+ return run_result->error_code ? -1 : 0;
+}
+
+int odp_ml_compl_load_result(odp_ml_compl_t ml_compl, odp_ml_load_result_t *result)
+{
+ odp_event_subtype_t subtype;
+ odp_ml_load_result_t *load_result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)ml_compl;
+ odp_event_t ev = odp_buffer_to_event(buf);
+
+ if (odp_unlikely(ml_compl == ODP_ML_COMPL_INVALID)) {
+ _ODP_ERR("Given ML completion event is invalid\n");
+ return -2;
+ }
+
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_ML_COMPL ||
+ subtype != ODP_EVENT_ML_COMPL_LOAD) {
+ _ODP_ERR("Given completion event has wrong event type or subtype\n");
+ return -2;
+ }
+
+ load_result = odp_buffer_addr(buf);
+ if (result)
+ *result = *load_result;
+
+ return load_result->error_code ? -1 : 0;
+}
+
+void *odp_ml_compl_user_area(odp_ml_compl_t ml_compl)
+{
+ return odp_buffer_user_area((odp_buffer_t)(uintptr_t)ml_compl);
+}
+
+odp_ml_compl_t odp_ml_compl_from_event(odp_event_t event)
+{
+ _ODP_ASSERT(_odp_event_hdr_field(event, int8_t, event_type) == ODP_EVENT_ML_COMPL);
+
+ return (odp_ml_compl_t)(uintptr_t)event;
+}
+
+odp_event_t odp_ml_compl_to_event(odp_ml_compl_t ml_compl)
+{
+ return (odp_event_t)(uintptr_t)ml_compl;
+}
+
+uint64_t odp_ml_compl_to_u64(odp_ml_compl_t ml_compl)
+{
+ return (uint64_t)(uintptr_t)ml_compl;
+}
+
+void odp_ml_compl_param_init(odp_ml_compl_param_t *compl_param)
+{
+ memset(compl_param, 0, sizeof(odp_ml_compl_param_t));
+
+ compl_param->queue = ODP_QUEUE_INVALID;
+ compl_param->event = ODP_EVENT_INVALID;
+}
+
+int odp_ml_model_load(odp_ml_model_t model, odp_ml_load_result_t *result)
+{
+ odp_ml_load_result_t result_local;
+ int ret = -1;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ memset(&result_local, 0, sizeof(result_local));
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ result_local.error_code = ML_BAD_HDL;
+ goto load_fail;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ if (odp_unlikely(mdl->state != ML_STATE_CREATED)) {
+ _ODP_ERR("Model has not been created yet or is already loaded\n");
+ odp_ticketlock_unlock(&mdl->lock);
+ result_local.error_code = ML_NOT_CREATED;
+ goto load_fail;
+ }
+
+ mdl->state = ML_STATE_LOADED;
+ odp_ticketlock_unlock(&mdl->lock);
+ ret = 0;
+
+load_fail:
+ if (result)
+ *result = result_local;
+
+ return ret;
+}
+
+static inline int check_compl_param(const odp_ml_compl_param_t *compl_param,
+ uint32_t max_compl_id, odp_bool_t is_load)
+{
+ odp_ml_config_t *config = &_odp_ml_glb->ml_config;
+
+ switch (compl_param->mode) {
+ case ODP_ML_COMPL_MODE_POLL:
+ if (is_load && !(config->load_mode_mask & ODP_ML_COMPL_MODE_POLL)) {
+ _ODP_ERR("Poll mode loading/unloading is not configured\n");
+ return -1;
+ }
+
+ if (!is_load && !(config->run_mode_mask & ODP_ML_COMPL_MODE_POLL)) {
+ _ODP_ERR("Poll mode run is not configured\n");
+ return -1;
+ }
+
+ if (compl_param->compl_id > max_compl_id) {
+ _ODP_ERR("Bad compl_id: %u, exceeding model max completion id %u\n",
+ compl_param->compl_id, max_compl_id);
+ return -1;
+ }
+ break;
+ case ODP_ML_COMPL_MODE_EVENT:
+ if (is_load && !(config->load_mode_mask & ODP_ML_COMPL_MODE_EVENT)) {
+ _ODP_ERR("Event mode loading/unloading is not configured\n");
+ return -1;
+ }
+
+ if (!is_load && !(config->run_mode_mask & ODP_ML_COMPL_MODE_EVENT)) {
+ _ODP_ERR("Event mode run is not configured\n");
+ return -1;
+ }
+
+ if (compl_param->event == ODP_EVENT_INVALID ||
+ compl_param->queue == ODP_QUEUE_INVALID) {
+ _ODP_ERR("Bad event or queue\n");
+ return -1;
+ }
+
+ if (odp_event_type(compl_param->event) != ODP_EVENT_ML_COMPL) {
+ _ODP_ERR("Bad completion event type\n");
+ return -1;
+ }
+ break;
+ default:
+ /* Including ODP_ML_COMPL_MODE_SYNC, which is not supported by
+ * asynchrous functions (e.g. *_start()) either.
+ */
+ _ODP_ERR("Invalid completion mode %u\n", compl_param->mode);
+ return -1;
+ }
+
+ return 0;
+}
+
+int odp_ml_model_load_start(odp_ml_model_t model, const odp_ml_compl_param_t *compl_param)
+{
+ int ret;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad model handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(check_compl_param(compl_param, mdl->max_compl_id, true)))
+ return -1;
+
+ if (compl_param->mode == ODP_ML_COMPL_MODE_POLL)
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 0);
+
+ ret = odp_ml_model_load(model, NULL);
+
+ if (odp_unlikely(ret))
+ return -1;
+
+ /* Send a completion event to the given queue */
+ if (compl_param->mode == ODP_ML_COMPL_MODE_EVENT) {
+ odp_ml_load_result_t *result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)compl_param->event;
+
+ _odp_buffer_subtype_set(buf, ODP_EVENT_ML_COMPL_LOAD);
+
+ result = odp_buffer_addr(buf);
+ result->error_code = 0;
+ result->user_ptr = compl_param->user_ptr;
+
+ if (odp_unlikely(odp_queue_enq(compl_param->queue, compl_param->event))) {
+ _ODP_ERR("Completion event enqueue failed %" PRIu64 "\n",
+ odp_queue_to_u64(compl_param->queue));
+ if (odp_ml_model_unload(model, NULL))
+ _ODP_ERR("Failed to unload model\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ mdl->result[compl_param->compl_id].user_ptr = compl_param->user_ptr;
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 1);
+ return 0;
+}
+
+int odp_ml_model_load_status(odp_ml_model_t model, uint32_t compl_id, odp_ml_load_result_t *result)
+{
+ int ret;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID || compl_id > mdl->max_compl_id)) {
+ _ODP_ERR("Invalid model or compl_id: %u\n", compl_id);
+ return -2;
+ }
+
+ ret = odp_atomic_load_acq_u32(&mdl->compl_status[compl_id]);
+
+ if (ret && result) {
+ result->error_code = 0;
+ result->user_ptr = mdl->result[compl_id].user_ptr;
+ }
+
+ return ret;
+}
+
+int odp_ml_model_unload(odp_ml_model_t model, odp_ml_load_result_t *result)
+{
+ odp_ml_load_result_t result_local;
+ int ret = -1;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ memset(&result_local, 0, sizeof(result_local));
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ result_local.error_code = ML_BAD_HDL;
+ _ODP_ERR("Bad ML model handle\n");
+ goto unload_fail;
+ }
+
+ odp_ticketlock_lock(&mdl->lock);
+ /* mdl->state == ML_STATE_FREE, ML_STATE_CREATED, ML_STATE_INFERENCING */
+ if (odp_unlikely(mdl->state != ML_STATE_LOADED)) {
+ _ODP_ERR("Model has not been created/loaded or inferencing has not finished yet\n");
+ odp_ticketlock_unlock(&mdl->lock);
+ result_local.error_code = ML_NOT_LOADED;
+ goto unload_fail;
+ }
+
+ mdl->state = ML_STATE_CREATED;
+ odp_ticketlock_unlock(&mdl->lock);
+
+ ret = 0;
+
+unload_fail:
+ if (result)
+ *result = result_local;
+
+ return ret;
+}
+
+int odp_ml_model_unload_start(odp_ml_model_t model, const odp_ml_compl_param_t *compl_param)
+{
+ int ret;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad model handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(check_compl_param(compl_param, mdl->max_compl_id, true)))
+ return -1;
+
+ if (compl_param->mode == ODP_ML_COMPL_MODE_POLL)
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 0);
+
+ ret = odp_ml_model_unload(model, NULL);
+
+ if (odp_unlikely(ret))
+ return -1;
+
+ /* Upon successful unloading, send a completion event to the given queue */
+ if (compl_param->mode == ODP_ML_COMPL_MODE_EVENT) {
+ odp_ml_load_result_t *result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)compl_param->event;
+
+ _odp_buffer_subtype_set(buf, ODP_EVENT_ML_COMPL_LOAD);
+
+ result = odp_buffer_addr(buf);
+ result->error_code = 0;
+ result->user_ptr = compl_param->user_ptr;
+
+ if (odp_unlikely(odp_queue_enq(compl_param->queue, compl_param->event))) {
+ _ODP_ERR("Completion event enqueue failed %" PRIu64 "\n",
+ odp_queue_to_u64(compl_param->queue));
+ return -1;
+ }
+
+ return 0;
+ }
+
+ mdl->result[compl_param->compl_id].user_ptr = compl_param->user_ptr;
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 1);
+ return 0;
+}
+
+int odp_ml_model_unload_status(odp_ml_model_t model, uint32_t compl_id,
+ odp_ml_load_result_t *result)
+{
+ return odp_ml_model_load_status(model, compl_id, result);
+}
+
+void odp_ml_run_param_init(odp_ml_run_param_t *param)
+{
+ memset(param, 0, sizeof(odp_ml_run_param_t));
+}
+
+static void ml_shape_to_int64(const odp_ml_shape_info_t *shape, uint32_t batch_size, int64_t *array)
+{
+ for (uint32_t i = 0; i < shape->num_dim; i++) {
+ /* Replace dynamic dimension size with provided batch_size */
+ if (shape->dim[i] == ODP_ML_DIM_DYNAMIC)
+ array[i] = batch_size;
+ else
+ array[i] = shape->dim[i];
+ }
+}
+
+/* Get the number of elements in given shape */
+static inline uint64_t get_num_elem(uint32_t batch_size, const odp_ml_shape_info_t *shape)
+{
+ uint64_t num_elements = 1;
+ int64_t dim[ODP_ML_MAX_DIMS] = {0};
+
+ ml_shape_to_int64(shape, batch_size, dim);
+
+ for (uint32_t i = 0; i < shape->num_dim; i++)
+ num_elements *= (uint64_t)dim[i];
+
+ return num_elements;
+}
+
+static inline uint32_t dyn_io_size(const odp_ml_shape_info_t *shape, uint32_t data_type_size,
+ const odp_ml_run_param_t *param)
+{
+ uint32_t size;
+
+ if (!param || !param->batch_size) {
+ _ODP_ERR("Parameter 'param' must not be NULL and batch_size must be "
+ "provided when a input/output has dynamic dimension size\n");
+ return 0;
+ }
+
+ size = get_num_elem(param->batch_size, shape);
+ size *= data_type_size;
+
+ return size;
+}
+
+static int verify_run_params(odp_ml_model_t model, const odp_ml_data_t *data,
+ const odp_ml_run_param_t *param)
+{
+ const ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad ML model handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(!data)) {
+ _ODP_ERR("Parameter 'data' must not be NULL\n");
+ return -1;
+ }
+
+ /* Make sure that the number of input data segments equals or bigger than
+ * the number of model inputs. */
+ if (mdl->info.num_inputs > data->num_input_seg) {
+ _ODP_ERR("The num of input data segments %u must not less than "
+ "the number of model inputs %u\n", data->num_input_seg,
+ mdl->info.num_inputs);
+ return -1;
+ }
+
+ if (mdl->info.num_outputs > data->num_output_seg) {
+ _ODP_ERR("The num of output data segments %u must not less than "
+ "the number of model outputs %u\n", data->num_output_seg,
+ mdl->info.num_outputs);
+ return -1;
+ }
+
+ if (data->num_input_seg > mdl->info.num_inputs &&
+ (_odp_ml_glb->capa.max_segs_per_input == 1)) {
+ _ODP_ERR("Segmented input data is not supported\n");
+ return -1;
+ }
+
+ if (data->num_output_seg > mdl->info.num_outputs &&
+ (_odp_ml_glb->capa.max_segs_per_output == 1)) {
+ _ODP_ERR("Segmented output data is not supported");
+ return -1;
+ }
+
+ uint32_t size = 0;
+ uint32_t input_index = 0;
+ uint32_t seg_size_sum = 0;
+ odp_bool_t index_new = true;
+ uint32_t segs_per_input = 1;
+
+ for (uint32_t i = 0; i < data->num_input_seg; i++) {
+ if (data->input_seg[i].addr == NULL) {
+ _ODP_ERR("data->input_seg[%u].addr must not NULL\n", i);
+ return -1;
+ };
+
+ if (index_new) {
+ if (input_index > mdl->info.num_inputs - 1) {
+ _ODP_ERR("Too much number of input segments given\n");
+ return -1;
+ }
+
+ /* Input with dynamic batch size */
+ if (mdl->input_info[input_index].shape.type == ODP_ML_SHAPE_BATCH)
+ size = dyn_io_size(&mdl->input_info[input_index].shape,
+ mdl->input_info[input_index].data_type_size,
+ param);
+ else
+ size = mdl->input_sizes[input_index];
+
+ if (!size) {
+ _ODP_ERR("Size for %uth input is 0\n", input_index);
+ return -1;
+ }
+ }
+
+ seg_size_sum += data->input_seg[i].size;
+
+ if (seg_size_sum > size) {
+ _ODP_ERR("Sum of segment sizes %u exceeds %uth input data size %u\n",
+ seg_size_sum, input_index, size);
+ return -1;
+ }
+
+ if (seg_size_sum == size) {
+ if (segs_per_input > _odp_ml_glb->capa.max_segs_per_input) {
+ _ODP_ERR("Number of segments %u for input[%u] exceeds maximum"
+ " number of data segments per model input %u\n",
+ segs_per_input, input_index,
+ _odp_ml_glb->capa.max_segs_per_input);
+ return -1;
+ }
+ input_index++;
+ index_new = true;
+ seg_size_sum = 0;
+ segs_per_input = 1;
+ } else {
+ segs_per_input++;
+ index_new = false;
+ }
+ }
+
+ if (input_index != mdl->info.num_inputs) {
+ _ODP_ERR("Data is not provided for all model inputs\n");
+ return -1;
+ }
+
+ seg_size_sum = 0;
+ index_new = true;
+ uint32_t output_index = 0;
+ uint32_t segs_per_output = 1;
+
+ for (uint32_t i = 0; i < data->num_output_seg; i++) {
+ if (data->output_seg[i].addr == NULL) {
+ _ODP_ERR("data->output_seg[%u].addr must not NULL\n", i);
+ return -1;
+ }
+
+ if (index_new) {
+ if (output_index > mdl->info.num_outputs - 1) {
+ _ODP_ERR("Too much number of output segments given\n");
+ return -1;
+ }
+
+ /* Output with dynamic batch size */
+ if (mdl->output_info[output_index].shape.type == ODP_ML_SHAPE_BATCH)
+ size = dyn_io_size(&mdl->output_info[output_index].shape,
+ mdl->output_info[output_index].data_type_size,
+ param);
+ else
+ size = mdl->output_sizes[output_index];
+
+ if (!size) {
+ _ODP_ERR("Size for %uth output is 0\n", output_index);
+ return -1;
+ }
+ }
+
+ seg_size_sum += data->output_seg[i].size;
+
+ if (seg_size_sum > size) {
+ _ODP_ERR("Sum of segment sizes %u exceeds %uth output data size %u\n",
+ seg_size_sum, output_index, size);
+ return -1;
+ }
+
+ if (seg_size_sum >= size) {
+ if (segs_per_output > _odp_ml_glb->capa.max_segs_per_output) {
+ _ODP_ERR("Number of segments %u for output[%u] exceeds maximum"
+ " number of data segments per model output %u\n",
+ segs_per_output, output_index,
+ _odp_ml_glb->capa.max_segs_per_output);
+ return -1;
+ }
+ output_index++;
+ index_new = true;
+ seg_size_sum = 0;
+ segs_per_output = 1;
+ } else {
+ segs_per_output++;
+ index_new = false;
+ }
+ }
+
+ if (output_index != mdl->info.num_outputs) {
+ _ODP_ERR("Not enough output_segs to hold all output data\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static ONNXTensorElementDataType onnx_dtype_from_odp_dtype(odp_ml_data_type_t data_type)
+{
+ switch (data_type) {
+ case ODP_ML_DATA_TYPE_NONE:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
+ case ODP_ML_DATA_TYPE_INT8:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8;
+ case ODP_ML_DATA_TYPE_UINT8:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8;
+ case ODP_ML_DATA_TYPE_INT16:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16;
+ case ODP_ML_DATA_TYPE_UINT16:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16;
+ case ODP_ML_DATA_TYPE_INT24:
+ /* Fall through*/
+ case ODP_ML_DATA_TYPE_UINT24:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
+ case ODP_ML_DATA_TYPE_FP64:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE;
+ case ODP_ML_DATA_TYPE_INT32:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32;
+ case ODP_ML_DATA_TYPE_UINT32:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32;
+ case ODP_ML_DATA_TYPE_INT64:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
+ case ODP_ML_DATA_TYPE_UINT64:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64;
+ case ODP_ML_DATA_TYPE_FP16:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16;
+ case ODP_ML_DATA_TYPE_FP32:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT;
+ case ODP_ML_DATA_TYPE_BFP16:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16;
+ default:
+ return ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
+ }
+}
+
+static int verify_tensor(const OrtValue *tensor, odp_ml_data_type_t expected_type,
+ const odp_ml_shape_info_t *expected_shape, uint32_t batch_size)
+{
+ OrtTensorTypeAndShapeInfo *tensor_info;
+ ONNXTensorElementDataType tensor_type;
+ size_t dim_count;
+ OrtStatus *status = NULL;
+ int64_t dims[ODP_ML_MAX_DIMS] = {0};
+ int64_t shape_arr[ODP_ML_MAX_DIMS] = {0};
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ status = ort_api->GetTensorTypeAndShape(tensor, &tensor_info);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetTensorTypeAndShape() failed\n");
+ return -1;
+ }
+
+ status = ort_api->GetTensorElementType(tensor_info, &tensor_type);
+ if (check_ortstatus(status)) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("GetTensorElementType() failed\n");
+ return -1;
+ }
+
+ if (onnx_dtype_to_odp_dtype(tensor_type) != expected_type) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("Tensor type does not match model type\n");
+ return -1;
+ }
+
+ status = ort_api->GetDimensionsCount(tensor_info, &dim_count);
+ if (check_ortstatus(status)) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("GetDimensionsCount() failed\n");
+ return -1;
+ }
+
+ if (dim_count != expected_shape->num_dim) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("Tensor dimension does not match shape_dim\n");
+ return -1;
+ }
+
+ status = ort_api->GetDimensions(tensor_info, dims, dim_count);
+ if (check_ortstatus(status)) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("GetDimensions() failed\n");
+ return -1;
+ }
+
+ ml_shape_to_int64(expected_shape, batch_size, shape_arr);
+
+ for (uint32_t i = 0; i < dim_count; i++) {
+ if (dims[i] != shape_arr[i]) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("Shape[%u]: %" PRIu64 " does not match expected: %" PRIu64 "\n",
+ i, dims[i], shape_arr[i]);
+ return -1;
+ }
+ }
+
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ return 0;
+}
+
+static int input_data_to_tensor(const odp_ml_input_info_t *input_info, uint32_t num_seg,
+ const odp_ml_data_seg_t *input_seg, uint32_t *seg_idx,
+ uint32_t batch_size, OrtValue **input_tensor)
+{
+ int is_tensor;
+ uint64_t input_size;
+ OrtAllocator *allocator;
+ void *data = NULL;
+ OrtStatus *status = NULL;
+ int64_t shape[ODP_ML_MAX_DIMS] = {0};
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+ ONNXTensorElementDataType onnx_dtype = ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
+
+ ml_shape_to_int64(&input_info->shape, batch_size, shape);
+
+ onnx_dtype = onnx_dtype_from_odp_dtype(input_info->data_type);
+ _ODP_ASSERT(onnx_dtype != ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED);
+
+ status = ort_api->GetAllocatorWithDefaultOptions(&allocator);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetAllocatorWithDefaultOptions() failed\n");
+ return -1;
+ }
+
+ status = ort_api->CreateTensorAsOrtValue(allocator,
+ shape,
+ input_info->shape.num_dim,
+ onnx_dtype,
+ input_tensor);
+ if (check_ortstatus(status) || !input_tensor[0]) {
+ _ODP_ERR("CreateTensorWithDataAsOrtValue() failed\n");
+ return -1;
+ }
+
+ input_size = input_info->data_type_size * get_num_elem(batch_size, &input_info->shape);
+
+ status = ort_api->GetTensorMutableData(input_tensor[0], &data);
+ if (check_ortstatus(status) || !data) {
+ _ODP_ERR("GetTensorMutableData() failed\n");
+ return -1;
+ }
+
+ for (uint64_t i = 0; i < input_size; ) {
+ if (*seg_idx >= num_seg) {
+ _ODP_ERR("Insufficient input data\n");
+ return -1;
+ }
+
+ uint64_t seg_size = input_seg[*seg_idx].size;
+
+ if (i + seg_size > input_size) {
+ _ODP_ERR("Excess input data in segment %" PRIu32 "\n", *seg_idx);
+ return -1;
+ }
+
+ memcpy((uint8_t *)data + i, input_seg[(*seg_idx)++].addr, seg_size);
+ i += seg_size;
+ }
+
+ if (!ODP_DEBUG)
+ return 0;
+
+ status = ort_api->IsTensor(input_tensor[0], &is_tensor);
+ if (check_ortstatus(status) || !is_tensor) {
+ _ODP_ERR("input_tensor IsTensor failed\n");
+ return -1;
+ }
+
+ /* Make sure tensor shape matches input_shape */
+ if (verify_tensor(input_tensor[0], input_info->data_type,
+ &input_info->shape, batch_size)) {
+ _ODP_ERR("Verify input_tensor failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_output_tensor(OrtValue *output_tensor, odp_ml_data_type_t expected_type,
+ const odp_ml_shape_info_t *expected_shape, uint32_t batch_size)
+{
+ int is_tensor = 0;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+ OrtStatus *status = ort_api->IsTensor(output_tensor, &is_tensor);
+
+ if (check_ortstatus(status) || !is_tensor) {
+ _ODP_ERR("output_tensor IsTensor failed\n");
+ return -1;
+ }
+
+ /* Make sure tensor shape matches output_shape */
+ if (verify_tensor(output_tensor, expected_type, expected_shape, batch_size)) {
+ _ODP_ERR("Verify output_tensor failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_tensor_data_size(OrtValue *tensor, uint32_t *size, uint32_t data_type_size)
+{
+ size_t num_elem;
+ OrtStatus *status;
+ OrtTensorTypeAndShapeInfo *tensor_info;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+
+ status = ort_api->GetTensorTypeAndShape(tensor, &tensor_info);
+ if (check_ortstatus(status)) {
+ _ODP_ERR("GetTensorTypeAndShape() failed\n");
+ return -1;
+ }
+
+ status = ort_api->GetTensorShapeElementCount(tensor_info, &num_elem);
+ if (check_ortstatus(status)) {
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ _ODP_ERR("GetTensorShapeElementCount() failed\n");
+ return -1;
+ }
+ *size = data_type_size * num_elem;
+
+ ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info);
+ return 0;
+}
+
+static int check_output_size(odp_bool_t is_segmented, uint32_t output_idx, uint32_t seg_idx,
+ uint64_t out_tensor_data_size, const odp_ml_data_t data[])
+{
+ uint64_t output_size = 0;
+
+ /* Output is not segmented */
+ if (!is_segmented) {
+ /* Make sure tensor data size does not exceed size allocated for
+ * data->output_seg[seg_idx].addr */
+ if (out_tensor_data_size > data->output_seg[seg_idx].size) {
+ _ODP_ERR("Malloc at least %" PRIu64 " bytes for %dth output tensor\n",
+ out_tensor_data_size, output_idx);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /* Output is segmented, first calculate total size for one tensor */
+ for (; seg_idx < data->num_output_seg; seg_idx++) {
+ output_size += data->output_seg[seg_idx].size;
+ if (output_size >= out_tensor_data_size)
+ break;
+ }
+
+ if (0 == output_size) {
+ _ODP_ERR("No output data segments for %uth output tensor\n", output_idx);
+ return -1;
+ }
+
+ if (out_tensor_data_size > output_size) {
+ _ODP_ERR("Output segments (%" PRIu64 " bytes in total) for %uth output"
+ " is expected to be at least %" PRIu64 " bytes\n",
+ output_size, output_idx, out_tensor_data_size);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int output_tensors_to_data(OrtValue **output_tensors,
+ uint32_t model_num_outputs,
+ const odp_ml_run_param_t *param,
+ const odp_ml_output_info_t *output_info,
+ const odp_ml_data_t *data,
+ odp_ml_run_result_t *result_local)
+{
+ uint32_t seg_idx;
+ uint64_t seg_size;
+ uint64_t cpy_size;
+ uint64_t left_size;
+ uint64_t output_val_offset;
+ uint32_t out_tensor_data_size;
+ void *output_val = NULL; /* Pointer to store one raw output value */
+ OrtStatus *status = NULL;
+ uint32_t batch_size = (param && param->batch_size) ? param->batch_size : 0;
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+ odp_bool_t is_segmented = (data->num_output_seg != model_num_outputs);
+
+ seg_idx = 0;
+ for (uint32_t i = 0; i < model_num_outputs; i++) {
+ if (ODP_DEBUG &&
+ verify_output_tensor(output_tensors[i], output_info[i].data_type,
+ &output_info[i].shape, batch_size)){
+ result_local->error_code = ML_BAD_OUTPUT;
+ return -1;
+ }
+
+ /* Get tensor data size */
+ if (get_tensor_data_size(output_tensors[i], &out_tensor_data_size,
+ output_info[i].data_type_size)) {
+ result_local->error_code = ML_LIB_FAILED;
+ return -1;
+ }
+
+ /* When output_tensor is an empty tensor [], skip getting data */
+ if (out_tensor_data_size == 0)
+ continue;
+
+ if (ODP_DEBUG && check_output_size(is_segmented, i, seg_idx,
+ out_tensor_data_size, data)) {
+ result_local->error_code = ML_BAD_OUTPUT;
+ return -1;
+ }
+
+ /* Following assumes param and data->output_seg are valid */
+ /* Get tensor data */
+ output_val = NULL;
+ status = ort_api->GetTensorMutableData(output_tensors[i], &output_val);
+ if (check_ortstatus(status) || !output_val) {
+ result_local->error_code = ML_LIB_FAILED;
+ return -1;
+ }
+
+ /* Output is not segmented */
+ if (!is_segmented) {
+ /* Store output data to data->output_seg[i].addr */
+ memcpy(data->output_seg[i].addr, output_val, out_tensor_data_size);
+ seg_idx++;
+ continue;
+ }
+
+ /* Output is segmented */
+ output_val_offset = 0;
+ left_size = out_tensor_data_size;
+ for (; seg_idx < data->num_output_seg; seg_idx++) {
+ seg_size = data->output_seg[seg_idx].size;
+ cpy_size = left_size > seg_size ? seg_size : left_size;
+ memcpy(data->output_seg[seg_idx].addr,
+ ((char *)output_val) + output_val_offset, cpy_size);
+
+ output_val_offset += cpy_size;
+ left_size = out_tensor_data_size - output_val_offset;
+
+ if (!left_size) {
+ seg_idx++;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int odp_ml_run(odp_ml_model_t model, const odp_ml_data_t *data, const odp_ml_run_param_t *param)
+{
+ odp_ml_run_result_t result_local;
+
+ int retval = -1; /* Return value of this function */
+ int ret = 0;
+ OrtStatus *status = NULL;
+ uint32_t batch_size = 0;
+
+ OrtValue *input_tensor[CONFIG_ML_MAX_INPUTS] = {0};
+ OrtValue *output_tensors[CONFIG_ML_MAX_OUTPUTS] = {0};
+ const char *input_names[CONFIG_ML_MAX_INPUTS] = {0};
+ const char *output_names[CONFIG_ML_MAX_OUTPUTS] = {0};
+
+ const OrtApi *ort_api = _odp_ml_glb->ort_api;
+ ml_model_t *mdl = ml_model_from_handle(model);
+ const odp_ml_model_info_t *ml_info = &mdl->info;
+ const odp_ml_input_info_t *input_info = mdl->input_info;
+ const odp_ml_output_info_t *output_info = mdl->output_info;
+ OrtSession *session = mdl->session;
+
+ odp_ticketlock_lock(&mdl->lock);
+ if (odp_unlikely(mdl->state == ML_STATE_INFERENCING)) {
+ odp_ticketlock_unlock(&mdl->lock);
+ return 0;
+ }
+ if (odp_unlikely(mdl->state != ML_STATE_LOADED)) {
+ _ODP_ERR("Wrong model state: not created or not loaded\n");
+ odp_ticketlock_unlock(&mdl->lock);
+ return -1;
+ }
+ mdl->state = ML_STATE_INFERENCING;
+ odp_ticketlock_unlock(&mdl->lock);
+
+ memset(&result_local, 0, sizeof(result_local));
+
+ if (ODP_DEBUG && verify_run_params(model, data, param)) {
+ result_local.error_code = ML_BAD_INPUT;
+ goto init_fail;
+ }
+
+ if (param && param->batch_size)
+ batch_size = param->batch_size;
+
+ uint32_t seg_idx = 0;
+
+ /* Transfer input data to tensor */
+ for (uint32_t i = 0; i < ml_info->num_inputs; i++) {
+ ret = input_data_to_tensor(&input_info[i],
+ data->num_input_seg,
+ data->input_seg,
+ &seg_idx,
+ batch_size,
+ &input_tensor[i]);
+ if (ret) {
+ _ODP_ERR("%uth input data to tensor failed\n", i);
+ result_local.error_code = ML_LIB_FAILED;
+ goto release_input_tensors;
+ }
+
+ _ODP_DBG("input_tensor[%u]: %p\n", i, input_tensor[i]);
+
+ /* Model input names */
+ input_names[i] = input_info[i].name;
+ }
+
+ if (seg_idx < data->num_input_seg) {
+ _ODP_ERR("Excess input segments\n");
+ ret = -1;
+ }
+
+ for (uint32_t i = 0; i < ml_info->num_outputs; i++)
+ output_names[i] = output_info[i].name;
+
+ /* Run inference */
+ status = ort_api->Run(session,
+ NULL,
+ (const char * const *)input_names,
+ (const OrtValue * const*)input_tensor,
+ ml_info->num_inputs,
+ (const char * const *)output_names,
+ ml_info->num_outputs,
+ output_tensors);
+
+ if (check_ortstatus(status)) {
+ _ODP_ERR("Run inference failed\n");
+ result_local.error_code = ML_LIB_FAILED;
+ goto release_all_tensors;
+ }
+
+ /* Verify output tensors and store them to output */
+ if (output_tensors_to_data(output_tensors, ml_info->num_outputs, param,
+ output_info, data, &result_local)) {
+ _ODP_ERR("Output tensors to data failed\n");
+ goto release_all_tensors;
+ }
+
+ retval = 1;
+
+release_all_tensors:
+ for (uint32_t i = 0; i < ml_info->num_outputs; i++)
+ ort_api->ReleaseValue(output_tensors[i]);
+
+release_input_tensors:
+ for (uint32_t i = 0; i < ml_info->num_inputs; i++)
+ ort_api->ReleaseValue(input_tensor[i]);
+
+init_fail:
+ if (param && param->result)
+ *param->result = result_local;
+
+ odp_ticketlock_lock(&mdl->lock);
+ mdl->state = ML_STATE_LOADED;
+ odp_ticketlock_unlock(&mdl->lock);
+
+ return retval;
+}
+
+int odp_ml_run_multi(odp_ml_model_t model, const odp_ml_data_t data[],
+ const odp_ml_run_param_t param[], int num)
+{
+ int i;
+ int ret;
+
+ if (odp_unlikely(num < 1)) {
+ _ODP_ERR("Bad number of runs\n");
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (param)
+ ret = odp_ml_run(model, &data[i], &param[i]);
+ else
+ ret = odp_ml_run(model, &data[i], NULL);
+
+ if (odp_unlikely(ret != 1))
+ break;
+ }
+
+ if (odp_unlikely(i == 0))
+ return ret;
+
+ return i;
+}
+
+int odp_ml_run_start(odp_ml_model_t model, const odp_ml_data_t *data,
+ const odp_ml_compl_param_t *compl_param,
+ const odp_ml_run_param_t *run_param)
+{
+ int ret;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID)) {
+ _ODP_ERR("Bad model handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(!compl_param)) {
+ _ODP_ERR("Completion parameter is NULL\n");
+ return -1;
+ }
+
+ /* Check completion mode */
+ if (odp_unlikely(check_compl_param(compl_param, mdl->max_compl_id, false))) {
+ _ODP_ERR("Bad ML job completion parameter\n");
+ return -1;
+ }
+
+ if (compl_param->mode == ODP_ML_COMPL_MODE_POLL)
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 0);
+
+ ret = odp_ml_run(model, data, run_param);
+
+ if (odp_unlikely(ret < 1))
+ return ret;
+
+ /* Send a completion event to the given queue */
+ if (compl_param->mode == ODP_ML_COMPL_MODE_EVENT) {
+ odp_ml_run_result_t *result;
+ odp_buffer_t buf = (odp_buffer_t)(uintptr_t)compl_param->event;
+
+ _odp_buffer_subtype_set(buf, ODP_EVENT_ML_COMPL_RUN);
+
+ result = odp_buffer_addr(buf);
+ result->error_code = 0;
+ result->user_ptr = compl_param->user_ptr;
+
+ if (odp_unlikely(odp_queue_enq(compl_param->queue, compl_param->event))) {
+ _ODP_ERR("Completion event enqueue failed %" PRIu64 "\n",
+ odp_queue_to_u64(compl_param->queue));
+ return -1;
+ }
+
+ return 1;
+ }
+
+ /* compl_param->mode == ODP_ML_COMPL_MODE_POLL */
+ mdl->result[compl_param->compl_id].user_ptr = compl_param->user_ptr;
+ odp_atomic_store_rel_u32(&mdl->compl_status[compl_param->compl_id], 1);
+
+ return 1;
+}
+
+int odp_ml_run_start_multi(odp_ml_model_t model, const odp_ml_data_t data[],
+ const odp_ml_compl_param_t compl_param[],
+ const odp_ml_run_param_t run_param[], int num)
+{
+ int i;
+ int ret = 0;
+
+ if (odp_unlikely(num < 1)) {
+ _ODP_ERR("Bad number of runs\n");
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (run_param)
+ ret = odp_ml_run_start(model, &data[i], &compl_param[i], &run_param[i]);
+ else
+ ret = odp_ml_run_start(model, &data[i], &compl_param[i], NULL);
+
+ if (odp_unlikely(ret != 1))
+ break;
+ }
+
+ if (odp_unlikely(i == 0))
+ return ret;
+
+ return i;
+}
+
+int odp_ml_run_status(odp_ml_model_t model, uint32_t compl_id, odp_ml_run_result_t *result)
+{
+ int ret;
+ ml_model_t *mdl = ml_model_from_handle(model);
+
+ if (odp_unlikely(model == ODP_ML_MODEL_INVALID ||
+ compl_id > mdl->max_compl_id)) {
+ _ODP_ERR("Invalid model handle or completion id: %u\n", compl_id);
+ return -2;
+ }
+
+ ret = odp_atomic_load_acq_u32(&mdl->compl_status[compl_id]);
+
+ if (result) {
+ result->error_code = 0;
+ result->user_ptr = mdl->result[compl_id].user_ptr;
+ }
+
+ return ret;
+}
+
+static int opt_level_from_str(const char *level_str, GraphOptimizationLevel *level)
+{
+ if (strcmp(level_str, "DISABLE_ALL") == 0)
+ *level = ORT_DISABLE_ALL;
+ else if (strcmp(level_str, "ENABLE_BASIC") == 0)
+ *level = ORT_ENABLE_BASIC;
+ else if (strcmp(level_str, "ENABLE_EXTENDED") == 0)
+ *level = ORT_ENABLE_EXTENDED;
+ else if (strcmp(level_str, "ENABLE_ALL") == 0)
+ *level = ORT_ENABLE_ALL;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int execution_mode_from_str(const char *mode_str, ExecutionMode *mode)
+{
+ if (strcmp(mode_str, "SEQUENTIAL") == 0)
+ *mode = ORT_SEQUENTIAL;
+ else if (strcmp(mode_str, "PARALLEL") == 0)
+ *mode = ORT_PARALLEL;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int read_config_file(ort_run_opts_t *opts)
+{
+ const char *conf_str;
+ char mode_str[ML_MAX_CONFIG_STR_LEN];
+ char opt_level_str[ML_MAX_CONFIG_STR_LEN];
+
+ _ODP_PRINT("ML config:\n");
+
+ conf_str = "ml.enable_profiling";
+ if (!_odp_libconfig_lookup_int(conf_str, &opts->enable_profiling)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", conf_str, opts->enable_profiling);
+
+ conf_str = "ml.execution_mode";
+ if (_odp_libconfig_lookup_str(conf_str, mode_str, ML_MAX_CONFIG_STR_LEN - 1) < 0) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+
+ if (execution_mode_from_str(mode_str, &opts->execution_mode)) {
+ _ODP_ERR("Unsupported execution mode: %s\n", mode_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %s\n", conf_str, mode_str);
+
+ conf_str = "ml.inter_op_num_threads";
+ if (!_odp_libconfig_lookup_int(conf_str, &opts->inter_op_num_threads)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", conf_str, opts->inter_op_num_threads);
+
+ conf_str = "ml.intra_op_num_threads";
+ if (!_odp_libconfig_lookup_int(conf_str, &opts->intra_op_num_threads)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", conf_str, opts->intra_op_num_threads);
+
+ conf_str = "ml.graph_optimization_level";
+ if (_odp_libconfig_lookup_str(conf_str, opt_level_str,
+ ML_MAX_CONFIG_STR_LEN - 1) < 0) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+
+ if (opt_level_from_str(opt_level_str, &opts->graph_opt_level)) {
+ _ODP_ERR("Graph optimize level %s not supported\n", opt_level_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %s\n", conf_str, opt_level_str);
+
+ conf_str = "ml.optimized_model_filepath";
+ if (_odp_libconfig_lookup_str(conf_str, opts->opt_model_filepath,
+ ML_MAX_CONFIG_STR_LEN - 1) < 0) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %s\n", conf_str, opts->opt_model_filepath);
+
+ return 0;
+}
+
+int _odp_ml_init_global(void)
+{
+ int i;
+ OrtEnv *env;
+ odp_shm_t shm;
+ OrtStatus *status;
+ const OrtApi *ort_api;
+
+ if (odp_global_ro.disable.ml) {
+ _ODP_ERR("ML is disabled\n");
+ return 0;
+ }
+
+ shm = odp_shm_reserve("_odp_ml_global", sizeof(ml_global_t), ODP_CACHE_LINE_SIZE, 0);
+ _odp_ml_glb = odp_shm_addr(shm);
+
+ if (_odp_ml_glb == NULL) {
+ _ODP_ERR("SHM reserve failed for odp_ml\n");
+ return -1;
+ }
+
+ memset(_odp_ml_glb, 0, sizeof(ml_global_t));
+ _odp_ml_glb->shm = shm;
+
+ if (odp_ml_capability(&_odp_ml_glb->capa)) {
+ _ODP_ERR("ML capability failed\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&_odp_ml_glb->pool_param);
+
+ if (read_config_file(&_odp_ml_glb->ort_run_opts))
+ return -1;
+
+ ort_api = OrtGetApiBase()->GetApi(ORT_API_VERSION);
+ if (!ort_api) {
+ _ODP_ERR("Failed to init ONNX Runtime engine.\n");
+ return -1;
+ }
+ _odp_ml_glb->ort_api = ort_api;
+
+ status = ort_api->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "Default", &env);
+ if (check_ortstatus(status) || !env) {
+ _ODP_ERR("ort_api->CreateEnv() failed.\n");
+ return -1;
+ }
+ _odp_ml_glb->env = env;
+
+ for (i = 0; i < ML_MAX_MODELS_CREATED; i++)
+ odp_ticketlock_init(&_odp_ml_glb->models[i].lock);
+
+ return 0;
+}
+
+int _odp_ml_term_global(void)
+{
+ if (odp_global_ro.disable.ml)
+ return 0;
+
+ if (_odp_ml_glb == NULL)
+ return 0;
+
+ if (_odp_ml_glb->env)
+ _odp_ml_glb->ort_api->ReleaseEnv(_odp_ml_glb->env);
+
+ if (odp_shm_free(_odp_ml_glb->shm)) {
+ _ODP_ERR("Shm free failed for odp_ml\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_ml_fp16.c b/platform/linux-generic/odp_ml_fp16.c
new file mode 100644
index 000000000..47b10f841
--- /dev/null
+++ b/platform/linux-generic/odp_ml_fp16.c
@@ -0,0 +1,425 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022-2023 Marvell.
+ * Copyright (c) 2023 Nokia
+ *
+ * Based on
+ * - dpdk/lib/mldev/mldev_utils_scalar.h
+ * - dpdk/lib/mldev/mldev_utils_scalar.c
+ * - dpdk/lib/mldev/mldev_utils_scalar_bfloat16.c
+ */
+
+#include <odp_ml_fp16.h>
+
+#include <errno.h>
+#include <stdint.h>
+
+#ifndef BIT
+#define BIT(nr) (1UL << (nr))
+#endif
+
+#ifndef BITS_PER_LONG
+#define BITS_PER_LONG (__SIZEOF_LONG__ * 8)
+#endif
+
+#ifndef GENMASK_U32
+#define GENMASK_U32(h, l) (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+#endif
+
+/* float32: bit index of MSB & LSB of sign, exponent and mantissa */
+#define FP32_LSB_M 0
+#define FP32_MSB_M 22
+#define FP32_LSB_E 23
+#define FP32_MSB_E 30
+#define FP32_LSB_S 31
+#define FP32_MSB_S 31
+
+/* float32: bitmask for sign, exponent and mantissa */
+#define FP32_MASK_S GENMASK_U32(FP32_MSB_S, FP32_LSB_S)
+#define FP32_MASK_E GENMASK_U32(FP32_MSB_E, FP32_LSB_E)
+#define FP32_MASK_M GENMASK_U32(FP32_MSB_M, FP32_LSB_M)
+
+/* float16: bit index of MSB & LSB of sign, exponent and mantissa */
+#define FP16_LSB_M 0
+#define FP16_MSB_M 9
+#define FP16_LSB_E 10
+#define FP16_MSB_E 14
+#define FP16_LSB_S 15
+#define FP16_MSB_S 15
+
+/* float16: bitmask for sign, exponent and mantissa */
+#define FP16_MASK_S GENMASK_U32(FP16_MSB_S, FP16_LSB_S)
+#define FP16_MASK_E GENMASK_U32(FP16_MSB_E, FP16_LSB_E)
+#define FP16_MASK_M GENMASK_U32(FP16_MSB_M, FP16_LSB_M)
+
+/* bfloat16: bit index of MSB & LSB of sign, exponent and mantissa */
+#define BF16_LSB_M 0
+#define BF16_MSB_M 6
+#define BF16_LSB_E 7
+#define BF16_MSB_E 14
+#define BF16_LSB_S 15
+#define BF16_MSB_S 15
+
+/* bfloat16: bitmask for sign, exponent and mantissa */
+#define BF16_MASK_S GENMASK_U32(BF16_MSB_S, BF16_LSB_S)
+#define BF16_MASK_E GENMASK_U32(BF16_MSB_E, BF16_LSB_E)
+#define BF16_MASK_M GENMASK_U32(BF16_MSB_M, BF16_LSB_M)
+
+/* Exponent bias */
+#define FP32_BIAS_E 127
+#define FP16_BIAS_E 15
+#define BF16_BIAS_E 127
+
+#define FP32_PACK(sign, exponent, mantissa) \
+ (((sign) << FP32_LSB_S) | ((exponent) << FP32_LSB_E) | (mantissa))
+
+#define FP16_PACK(sign, exponent, mantissa) \
+ (((sign) << FP16_LSB_S) | ((exponent) << FP16_LSB_E) | (mantissa))
+
+#define BF16_PACK(sign, exponent, mantissa) \
+ (((sign) << BF16_LSB_S) | ((exponent) << BF16_LSB_E) | (mantissa))
+
+/* Represent float32 as float and uint32_t */
+union float32 {
+ float f;
+ uint32_t u;
+};
+
+/* Convert a single precision floating point number (float32) into a half precision
+ * floating point number (float16) using round to nearest rounding mode.
+ */
+static uint16_t
+__float32_to_float16_scalar_rtn(float x)
+{
+ union float32 f32; /* float32 input */
+ uint32_t f32_s; /* float32 sign */
+ uint32_t f32_e; /* float32 exponent */
+ uint32_t f32_m; /* float32 mantissa */
+ uint16_t f16_s; /* float16 sign */
+ uint16_t f16_e; /* float16 exponent */
+ uint16_t f16_m; /* float16 mantissa */
+ uint32_t tbits; /* number of truncated bits */
+ uint32_t tmsb; /* MSB position of truncated bits */
+ uint32_t m_32; /* temporary float32 mantissa */
+ uint16_t m_16; /* temporary float16 mantissa */
+ uint16_t u16; /* float16 output */
+ int be_16; /* float16 biased exponent, signed */
+
+ f32.f = x;
+ f32_s = (f32.u & FP32_MASK_S) >> FP32_LSB_S;
+ f32_e = (f32.u & FP32_MASK_E) >> FP32_LSB_E;
+ f32_m = (f32.u & FP32_MASK_M) >> FP32_LSB_M;
+
+ f16_s = f32_s;
+ f16_e = 0;
+ f16_m = 0;
+
+ switch (f32_e) {
+ case (0): /* float32: zero or subnormal number */
+ f16_e = 0;
+ f16_m = 0; /* convert to zero */
+ break;
+ case (FP32_MASK_E >> FP32_LSB_E): /* float32: infinity or nan */
+ f16_e = FP16_MASK_E >> FP16_LSB_E;
+ if (f32_m == 0) { /* infinity */
+ f16_m = 0;
+ } else { /* nan, propagate mantissa and set MSB of mantissa to 1 */
+ f16_m = f32_m >> (FP32_MSB_M - FP16_MSB_M);
+ f16_m |= BIT(FP16_MSB_M);
+ }
+ break;
+ default: /* float32: normal number */
+ /* compute biased exponent for float16 */
+ be_16 = (int)f32_e - FP32_BIAS_E + FP16_BIAS_E;
+
+ /* overflow, be_16 = [31-INF], set to infinity */
+ if (be_16 >= (int)(FP16_MASK_E >> FP16_LSB_E)) {
+ f16_e = FP16_MASK_E >> FP16_LSB_E;
+ f16_m = 0;
+ } else if ((be_16 >= 1) && (be_16 < (int)(FP16_MASK_E >> FP16_LSB_E))) {
+ /* normal float16, be_16 = [1:30]*/
+ f16_e = be_16;
+ m_16 = f32_m >> (FP32_LSB_E - FP16_LSB_E);
+ tmsb = FP32_MSB_M - FP16_MSB_M - 1;
+ if ((f32_m & GENMASK_U32(tmsb, 0)) > BIT(tmsb)) {
+ /* round: non-zero truncated bits except MSB */
+ m_16++;
+
+ /* overflow into exponent */
+ if (((m_16 & FP16_MASK_E) >> FP16_LSB_E) == 0x1)
+ f16_e++;
+ } else if ((f32_m & GENMASK_U32(tmsb, 0)) == BIT(tmsb)) {
+ /* round: MSB of truncated bits and LSB of m_16 is set */
+ if ((m_16 & 0x1) == 0x1) {
+ m_16++;
+
+ /* overflow into exponent */
+ if (((m_16 & FP16_MASK_E) >> FP16_LSB_E) == 0x1)
+ f16_e++;
+ }
+ }
+ f16_m = m_16 & FP16_MASK_M;
+ } else if ((be_16 >= -(int)(FP16_MSB_M)) && (be_16 < 1)) {
+ /* underflow: zero / subnormal, be_16 = [-9:0] */
+ f16_e = 0;
+
+ /* add implicit leading zero */
+ m_32 = f32_m | BIT(FP32_LSB_E);
+ tbits = FP32_LSB_E - FP16_LSB_E - be_16 + 1;
+ m_16 = m_32 >> tbits;
+
+ /* if non-leading truncated bits are set */
+ if ((f32_m & GENMASK_U32(tbits - 1, 0)) > BIT(tbits - 1)) {
+ m_16++;
+
+ /* overflow into exponent */
+ if (((m_16 & FP16_MASK_E) >> FP16_LSB_E) == 0x1)
+ f16_e++;
+ } else if ((f32_m & GENMASK_U32(tbits - 1, 0)) == BIT(tbits - 1)) {
+ /* if leading truncated bit is set */
+ if ((m_16 & 0x1) == 0x1) {
+ m_16++;
+
+ /* overflow into exponent */
+ if (((m_16 & FP16_MASK_E) >> FP16_LSB_E) == 0x1)
+ f16_e++;
+ }
+ }
+ f16_m = m_16 & FP16_MASK_M;
+ } else if (be_16 == -(int)(FP16_MSB_M + 1)) {
+ /* underflow: zero, be_16 = [-10] */
+ f16_e = 0;
+ if (f32_m != 0)
+ f16_m = 1;
+ else
+ f16_m = 0;
+ } else {
+ /* underflow: zero, be_16 = [-INF:-11] */
+ f16_e = 0;
+ f16_m = 0;
+ }
+
+ break;
+ }
+
+ u16 = FP16_PACK(f16_s, f16_e, f16_m);
+
+ return u16;
+}
+
+/* Convert a half precision floating point number (float16) into a single precision
+ * floating point number (float32).
+ */
+static float
+__float16_to_float32_scalar_rtx(uint16_t f16)
+{
+ union float32 f32; /* float32 output */
+ uint16_t f16_s; /* float16 sign */
+ uint16_t f16_e; /* float16 exponent */
+ uint16_t f16_m; /* float16 mantissa */
+ uint32_t f32_s; /* float32 sign */
+ uint32_t f32_e; /* float32 exponent */
+ uint32_t f32_m; /* float32 mantissa*/
+ uint8_t shift; /* number of bits to be shifted */
+ uint32_t clz; /* count of leading zeroes */
+ int e_16; /* float16 exponent unbiased */
+
+ f16_s = (f16 & FP16_MASK_S) >> FP16_LSB_S;
+ f16_e = (f16 & FP16_MASK_E) >> FP16_LSB_E;
+ f16_m = (f16 & FP16_MASK_M) >> FP16_LSB_M;
+
+ f32_s = f16_s;
+ switch (f16_e) {
+ case (FP16_MASK_E >> FP16_LSB_E): /* float16: infinity or nan */
+ f32_e = FP32_MASK_E >> FP32_LSB_E;
+ if (f16_m == 0x0) { /* infinity */
+ f32_m = f16_m;
+ } else { /* nan, propagate mantissa, set MSB of mantissa to 1 */
+ f32_m = f16_m;
+ shift = FP32_MSB_M - FP16_MSB_M;
+ f32_m = (f32_m << shift) & FP32_MASK_M;
+ f32_m |= BIT(FP32_MSB_M);
+ }
+ break;
+ case 0: /* float16: zero or sub-normal */
+ f32_m = f16_m;
+ if (f16_m == 0) { /* zero signed */
+ f32_e = 0;
+ } else { /* subnormal numbers */
+ clz = __builtin_clz((uint32_t)f16_m) - sizeof(uint32_t) * 8 + FP16_LSB_E;
+ e_16 = (int)f16_e - clz;
+ f32_e = FP32_BIAS_E + e_16 - FP16_BIAS_E;
+
+ shift = clz + (FP32_MSB_M - FP16_MSB_M) + 1;
+ f32_m = (f32_m << shift) & FP32_MASK_M;
+ }
+ break;
+ default: /* normal numbers */
+ f32_m = f16_m;
+ e_16 = (int)f16_e;
+ f32_e = FP32_BIAS_E + e_16 - FP16_BIAS_E;
+
+ shift = (FP32_MSB_M - FP16_MSB_M);
+ f32_m = (f32_m << shift) & FP32_MASK_M;
+ }
+
+ f32.u = FP32_PACK(f32_s, f32_e, f32_m);
+
+ return f32.f;
+}
+
+/* Convert a single precision floating point number (float32) into a
+ * brain float number (bfloat16) using round to nearest rounding mode.
+ */
+static uint16_t
+__float32_to_bfloat16_scalar_rtn(float x)
+{
+ union float32 f32; /* float32 input */
+ uint32_t f32_s; /* float32 sign */
+ uint32_t f32_e; /* float32 exponent */
+ uint32_t f32_m; /* float32 mantissa */
+ uint16_t b16_s; /* float16 sign */
+ uint16_t b16_e; /* float16 exponent */
+ uint16_t b16_m; /* float16 mantissa */
+ uint32_t tbits; /* number of truncated bits */
+ uint16_t u16; /* float16 output */
+
+ f32.f = x;
+ f32_s = (f32.u & FP32_MASK_S) >> FP32_LSB_S;
+ f32_e = (f32.u & FP32_MASK_E) >> FP32_LSB_E;
+ f32_m = (f32.u & FP32_MASK_M) >> FP32_LSB_M;
+
+ b16_s = f32_s;
+ b16_e = 0;
+ b16_m = 0;
+
+ switch (f32_e) {
+ case (0): /* float32: zero or subnormal number */
+ b16_e = 0;
+ if (f32_m == 0) /* zero */
+ b16_m = 0;
+ else /* subnormal float32 number, normal bfloat16 */
+ goto bf16_normal;
+ break;
+ case (FP32_MASK_E >> FP32_LSB_E): /* float32: infinity or nan */
+ b16_e = BF16_MASK_E >> BF16_LSB_E;
+ if (f32_m == 0) { /* infinity */
+ b16_m = 0;
+ } else { /* nan, propagate mantissa and set MSB of mantissa to 1 */
+ b16_m = f32_m >> (FP32_MSB_M - BF16_MSB_M);
+ b16_m |= BIT(BF16_MSB_M);
+ }
+ break;
+ default: /* float32: normal number, normal bfloat16 */
+ goto bf16_normal;
+ }
+
+ goto bf16_pack;
+
+bf16_normal:
+ b16_e = f32_e;
+ tbits = FP32_MSB_M - BF16_MSB_M;
+ b16_m = f32_m >> tbits;
+
+ /* if non-leading truncated bits are set */
+ if ((f32_m & GENMASK_U32(tbits - 1, 0)) > BIT(tbits - 1)) {
+ b16_m++;
+
+ /* if overflow into exponent */
+ if (((b16_m & BF16_MASK_E) >> BF16_LSB_E) == 0x1)
+ b16_e++;
+ } else if ((f32_m & GENMASK_U32(tbits - 1, 0)) == BIT(tbits - 1)) {
+ /* if only leading truncated bit is set */
+ if ((b16_m & 0x1) == 0x1) {
+ b16_m++;
+
+ /* if overflow into exponent */
+ if (((b16_m & BF16_MASK_E) >> BF16_LSB_E) == 0x1)
+ b16_e++;
+ }
+ }
+ b16_m = b16_m & BF16_MASK_M;
+
+bf16_pack:
+ u16 = BF16_PACK(b16_s, b16_e, b16_m);
+
+ return u16;
+}
+
+/* Convert a brain float number (bfloat16) into a
+ * single precision floating point number (float32).
+ */
+static float
+__bfloat16_to_float32_scalar_rtx(uint16_t f16)
+{
+ union float32 f32; /* float32 output */
+ uint16_t b16_s; /* float16 sign */
+ uint16_t b16_e; /* float16 exponent */
+ uint16_t b16_m; /* float16 mantissa */
+ uint32_t f32_s; /* float32 sign */
+ uint32_t f32_e; /* float32 exponent */
+ uint32_t f32_m; /* float32 mantissa*/
+ uint8_t shift; /* number of bits to be shifted */
+
+ b16_s = (f16 & BF16_MASK_S) >> BF16_LSB_S;
+ b16_e = (f16 & BF16_MASK_E) >> BF16_LSB_E;
+ b16_m = (f16 & BF16_MASK_M) >> BF16_LSB_M;
+
+ f32_s = b16_s;
+ switch (b16_e) {
+ case (BF16_MASK_E >> BF16_LSB_E): /* bfloat16: infinity or nan */
+ f32_e = FP32_MASK_E >> FP32_LSB_E;
+ if (b16_m == 0x0) { /* infinity */
+ f32_m = 0;
+ } else { /* nan, propagate mantissa, set MSB of mantissa to 1 */
+ f32_m = b16_m;
+ shift = FP32_MSB_M - BF16_MSB_M;
+ f32_m = (f32_m << shift) & FP32_MASK_M;
+ f32_m |= BIT(FP32_MSB_M);
+ }
+ break;
+ case 0: /* bfloat16: zero or subnormal */
+ f32_m = b16_m;
+ if (b16_m == 0) { /* zero signed */
+ f32_e = 0;
+ } else { /* subnormal numbers */
+ goto fp32_normal;
+ }
+ break;
+ default: /* bfloat16: normal number */
+ goto fp32_normal;
+ }
+
+ goto fp32_pack;
+
+fp32_normal:
+ f32_m = b16_m;
+ f32_e = FP32_BIAS_E + b16_e - BF16_BIAS_E;
+
+ shift = (FP32_MSB_M - BF16_MSB_M);
+ f32_m = (f32_m << shift) & FP32_MASK_M;
+
+fp32_pack:
+ f32.u = FP32_PACK(f32_s, f32_e, f32_m);
+
+ return f32.f;
+}
+
+uint16_t _odp_float32_to_float16(float x)
+{
+ return __float32_to_float16_scalar_rtn(x);
+}
+
+float _odp_float16_to_float32(uint16_t f16)
+{
+ return __float16_to_float32_scalar_rtx(f16);
+}
+
+uint16_t _odp_float32_to_bfloat16(float x)
+{
+ return __float32_to_bfloat16_scalar_rtn(x);
+}
+
+float _odp_bfloat16_to_float32(uint16_t f16)
+{
+ return __bfloat16_to_float32_scalar_rtx(f16);
+}
diff --git a/platform/linux-generic/odp_ml_null.c b/platform/linux-generic/odp_ml_null.c
new file mode 100644
index 000000000..718e80d76
--- /dev/null
+++ b/platform/linux-generic/odp_ml_null.c
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/hints.h>
+#include <odp/api/ml.h>
+
+#include <odp_init_internal.h>
+
+#include <stdint.h>
+#include <string.h>
+
+/* Dummy ML API implementation, no capability and just return error for
+ * other functions.
+ */
+int _odp_ml_init_global(void)
+{
+ return 0;
+}
+
+int _odp_ml_term_global(void)
+{
+ return 0;
+}
+
+int odp_ml_capability(odp_ml_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_ml_capability_t));
+ return 0;
+}
+
+void odp_ml_config_init(odp_ml_config_t *config ODP_UNUSED)
+{
+}
+
+int odp_ml_config(const odp_ml_config_t *config ODP_UNUSED)
+{
+ return -1;
+}
+
+void odp_ml_model_param_init(odp_ml_model_param_t *param ODP_UNUSED)
+{
+}
+
+odp_ml_model_t odp_ml_model_create(const char *name ODP_UNUSED,
+ const odp_ml_model_param_t *param ODP_UNUSED)
+{
+ return ODP_ML_MODEL_INVALID;
+}
+
+int odp_ml_model_destroy(odp_ml_model_t model ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_info(odp_ml_model_t model ODP_UNUSED, odp_ml_model_info_t *info ODP_UNUSED)
+{
+ return -1;
+}
+
+uint32_t odp_ml_model_input_info(odp_ml_model_t model ODP_UNUSED,
+ odp_ml_input_info_t info[] ODP_UNUSED,
+ uint32_t num ODP_UNUSED)
+{
+ return 0;
+}
+
+uint32_t odp_ml_model_output_info(odp_ml_model_t model ODP_UNUSED,
+ odp_ml_output_info_t info[] ODP_UNUSED,
+ uint32_t num ODP_UNUSED)
+{
+ return 0;
+}
+
+odp_ml_model_t odp_ml_model_lookup(const char *name ODP_UNUSED)
+{
+ return ODP_ML_MODEL_INVALID;
+}
+
+uint64_t odp_ml_model_to_u64(odp_ml_model_t model ODP_UNUSED)
+{
+ return 0;
+}
+
+void odp_ml_model_print(odp_ml_model_t model ODP_UNUSED)
+{
+}
+
+void odp_ml_print(void)
+{
+}
+
+void odp_ml_compl_pool_param_init(odp_ml_compl_pool_param_t *pool_param)
+{
+ memset(pool_param, 0, sizeof(odp_ml_compl_pool_param_t));
+}
+
+odp_pool_t odp_ml_compl_pool_create(const char *name ODP_UNUSED,
+ const odp_ml_compl_pool_param_t *pool_param ODP_UNUSED)
+{
+ return ODP_POOL_INVALID;
+}
+
+odp_ml_compl_t odp_ml_compl_alloc(odp_pool_t pool ODP_UNUSED)
+{
+ return ODP_ML_COMPL_INVALID;
+}
+
+void odp_ml_compl_free(odp_ml_compl_t ml_compl ODP_UNUSED)
+{
+}
+
+int odp_ml_compl_run_result(odp_ml_compl_t ml_compl ODP_UNUSED,
+ odp_ml_run_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_compl_load_result(odp_ml_compl_t ml_compl ODP_UNUSED,
+ odp_ml_load_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+void *odp_ml_compl_user_area(odp_ml_compl_t ml_compl ODP_UNUSED)
+{
+ return NULL;
+}
+
+odp_ml_compl_t odp_ml_compl_from_event(odp_event_t event ODP_UNUSED)
+{
+ return ODP_ML_COMPL_INVALID;
+}
+
+odp_event_t odp_ml_compl_to_event(odp_ml_compl_t ml_compl ODP_UNUSED)
+{
+ return ODP_EVENT_INVALID;
+}
+
+uint64_t odp_ml_compl_to_u64(odp_ml_compl_t ml_compl ODP_UNUSED)
+{
+ return 0;
+}
+
+void odp_ml_compl_param_init(odp_ml_compl_param_t *compl_param ODP_UNUSED)
+{
+}
+
+int odp_ml_model_load(odp_ml_model_t model ODP_UNUSED, odp_ml_load_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_load_start(odp_ml_model_t model ODP_UNUSED,
+ const odp_ml_compl_param_t *compl_param ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_load_status(odp_ml_model_t model ODP_UNUSED, uint32_t compl_id ODP_UNUSED,
+ odp_ml_load_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_unload(odp_ml_model_t model ODP_UNUSED, odp_ml_load_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_unload_start(odp_ml_model_t model ODP_UNUSED,
+ const odp_ml_compl_param_t *compl_param ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_unload_status(odp_ml_model_t model ODP_UNUSED, uint32_t compl_id ODP_UNUSED,
+ odp_ml_load_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+void odp_ml_run_param_init(odp_ml_run_param_t *param ODP_UNUSED)
+{
+}
+
+int odp_ml_run(odp_ml_model_t model ODP_UNUSED, const odp_ml_data_t *data ODP_UNUSED,
+ const odp_ml_run_param_t *param ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_run_multi(odp_ml_model_t model ODP_UNUSED, const odp_ml_data_t data[] ODP_UNUSED,
+ const odp_ml_run_param_t param[] ODP_UNUSED, int num ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_run_start(odp_ml_model_t model ODP_UNUSED, const odp_ml_data_t *data ODP_UNUSED,
+ const odp_ml_compl_param_t *compl_param ODP_UNUSED,
+ const odp_ml_run_param_t *run_param ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_run_start_multi(odp_ml_model_t model ODP_UNUSED,
+ const odp_ml_data_t data[] ODP_UNUSED,
+ const odp_ml_compl_param_t compl_param[] ODP_UNUSED,
+ const odp_ml_run_param_t run_param[] ODP_UNUSED,
+ int num ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_run_status(odp_ml_model_t model ODP_UNUSED, uint32_t compl_id ODP_UNUSED,
+ odp_ml_run_result_t *result ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_extra_stat_info(odp_ml_model_t model ODP_UNUSED,
+ odp_ml_extra_stat_info_t info[] ODP_UNUSED,
+ int num ODP_UNUSED)
+{
+ return -1;
+}
+
+int odp_ml_model_extra_stats(odp_ml_model_t model ODP_UNUSED,
+ uint64_t stats[] ODP_UNUSED, int num ODP_UNUSED)
+{
+ return -1;
+}
diff --git a/platform/linux-generic/odp_ml_quantize.c b/platform/linux-generic/odp_ml_quantize.c
new file mode 100644
index 000000000..d3f3601e3
--- /dev/null
+++ b/platform/linux-generic/odp_ml_quantize.c
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp/api/ml_quantize.h>
+
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ml_fp16.h>
+
+#include <math.h>
+#include <stdint.h>
+
+void odp_ml_fp32_to_uint8(uint8_t *u8, const float *fp32, uint32_t num, float scale,
+ uint8_t zerop)
+{
+ float fval;
+
+ _ODP_ASSERT(scale != 0);
+
+ for (uint32_t i = 0; i < num; i++) {
+ /* Range mapping: map real values to signed integer */
+ fval = nearbyintf(fp32[i] / scale) + (float)zerop;
+
+ /* clip */
+ fval = _ODP_MAX(fval, 0.f);
+ fval = _ODP_MIN(fval, 255.f);
+ u8[i] = (uint8_t)(int32_t)fval;
+ }
+}
+
+void odp_ml_fp32_from_uint8(float *fp32, const uint8_t *u8, uint32_t num, float scale,
+ uint8_t zerop)
+{
+ for (uint32_t i = 0; i < num; i++)
+ fp32[i] = (float)(u8[i] - zerop) * scale;
+}
+
+void odp_ml_fp32_to_int8(int8_t *i8, const float *fp32, uint32_t num, float scale, int8_t zerop)
+{
+ float fval;
+
+ _ODP_ASSERT(scale != 0);
+
+ for (uint32_t i = 0; i < num; i++) {
+ /* Range mapping: map real values to signed integer */
+ fval = nearbyintf(fp32[i] / scale) + (float)zerop;
+
+ /* NOTE: Clamps signed quantization values to [-127,127] instead of [-128,127].
+ * This is to ensure that symmetric quantization results in a zero
+ * point of exactly 0 for signed 8 bit ints.
+ */
+ fval = _ODP_MAX(fval, -127.f);
+ fval = _ODP_MIN(fval, 127.f);
+ i8[i] = (int8_t)(int32_t)fval;
+ }
+}
+
+void odp_ml_fp32_from_int8(float *fp32, const int8_t *i8, uint32_t num, float scale, int8_t zerop)
+{
+ for (uint32_t i = 0; i < num; i++)
+ fp32[i] = (float)(i8[i] - zerop) * scale;
+}
+
+void odp_ml_fp32_to_fp16(uint16_t *fp16, const float *fp32, uint32_t num)
+{
+ uint32_t i;
+
+ for (i = 0; i < num; i++)
+ fp16[i] = _odp_float32_to_float16(fp32[i]);
+}
+
+void odp_ml_fp32_from_fp16(float *fp32, const uint16_t *fp16, uint32_t num)
+{
+ uint32_t i;
+
+ for (i = 0; i < num; i++)
+ fp32[i] = _odp_float16_to_float32(fp16[i]);
+}
diff --git a/platform/linux-generic/odp_name_table.c b/platform/linux-generic/odp_name_table.c
index b116904da..46a038ac7 100644
--- a/platform/linux-generic/odp_name_table.c
+++ b/platform/linux-generic/odp_name_table.c
@@ -1,20 +1,20 @@
- /* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
-
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+ /* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
+#include <odp/api/hash.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp_name_table_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
-#include <odp_name_table_internal.h>
-#include <odp_debug_internal.h>
-
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#include <inttypes.h>
/* The following constants define some tunable parameters of this module.
* They are set to fairly reasonable values (perhaps somewhat biased toward
@@ -54,7 +54,7 @@ typedef struct name_tbl_entry_s name_tbl_entry_t;
/* It is important for most platforms that the following struct fit within
* one cacheline.
*/
-struct name_tbl_entry_s {
+struct ODP_ALIGNED_CACHE name_tbl_entry_s {
name_tbl_entry_t *next_entry;
uint64_t user_data;
_odp_int_name_t name_tbl_id;
@@ -62,17 +62,17 @@ struct name_tbl_entry_s {
uint8_t name_kind;
uint8_t name_len;
char name[_ODP_INT_NAME_LEN + 1];
-} ODP_ALIGNED_CACHE;
+};
-typedef struct {
+typedef struct ODP_ALIGNED_CACHE {
uint32_t num_allocd;
uint32_t num_used;
uint32_t num_added_to_free_list;
uint32_t num_avail_to_add;
uint32_t base_id;
name_tbl_entry_t *free_list_head;
- name_tbl_entry_t entries[0];
-} ODP_ALIGNED_CACHE name_tbl_t;
+ name_tbl_entry_t entries[];
+} name_tbl_t;
typedef struct {
name_tbl_t *tbls[NUM_NAME_TBLS];
@@ -220,20 +220,19 @@ static void secondary_hash_dump(secondary_hash_tbl_t *secondary_hash_tbl)
entry_cnt = hash_tbl_entry & 0x3F;
list_cnt = linked_list_len(name_tbl_entry);
if (entry_cnt != list_cnt)
- ODP_DBG("%s idx=%u entry_cnt=%u "
+ _ODP_DBG("%s idx=%u entry_cnt=%u "
"list_cnt=%u\n",
__func__,
idx, entry_cnt, list_cnt);
count += entry_cnt;
} else {
- ODP_DBG("%s inner secondary tbl\n",
- __func__);
+ _ODP_DBG("%s inner secondary tbl\n", __func__);
}
}
}
- ODP_DBG("%s count=%u\n", __func__, count);
+ _ODP_DBG("%s count=%u\n", __func__, count);
}
static uint32_t name_tbl_free_list_add(name_tbl_t *name_tbl,
@@ -245,7 +244,7 @@ static uint32_t name_tbl_free_list_add(name_tbl_t *name_tbl,
name_tbl_id = name_tbl->base_id | first_idx;
entry_idx = first_idx;
- num_added = MIN(num_to_add, name_tbl->num_avail_to_add);
+ num_added = _ODP_MIN(num_to_add, name_tbl->num_avail_to_add);
if (num_added == 0)
return 0;
@@ -296,7 +295,7 @@ static int new_name_tbl_add(void)
name_tbls_idx = name_tbls.num_name_tbls;
num_entries = INITIAL_NAME_TBL_SIZE << name_tbls_idx;
new_name_tbl = name_tbl_alloc(name_tbls_idx, num_entries);
- name_tbl_free_list_add(new_name_tbl, MIN(num_entries, 256));
+ name_tbl_free_list_add(new_name_tbl, _ODP_MIN(num_entries, UINT32_C(256)));
name_tbls.tbls[name_tbls_idx] = new_name_tbl;
name_tbls.avail_space_bit_mask |= 1 << name_tbls_idx;
@@ -377,6 +376,7 @@ static void name_tbl_entry_free(name_tbl_entry_t *name_tbl_entry)
memset(name_tbl_entry, 0, sizeof(name_tbl_entry_t));
name_tbl_entry->next_entry = name_tbl->free_list_head;
name_tbl->free_list_head = name_tbl_entry;
+ name_tbl_entry->name_tbl_id = name_tbl_id;
}
static hash_tbl_entry_t make_hash_tbl_entry(name_tbl_entry_t *name_tbl_entry,
@@ -385,7 +385,7 @@ static hash_tbl_entry_t make_hash_tbl_entry(name_tbl_entry_t *name_tbl_entry,
hash_tbl_entry_t hash_tbl_entry;
uint32_t new_entry_cnt;
- new_entry_cnt = MIN(entry_cnt + 1, 0x3F);
+ new_entry_cnt = _ODP_MIN(entry_cnt + 1, UINT32_C(0x3F));
hash_tbl_entry = (hash_tbl_entry_t)(uintptr_t)name_tbl_entry;
hash_tbl_entry &= ~0x3F;
hash_tbl_entry |= new_entry_cnt;
@@ -1004,7 +1004,7 @@ static uint32_t level2_hash_histo(secondary_hash_tbl_t *hash_tbl,
collisions = linked_list_len(name_tbl_entry);
}
- level2_histo[MIN(collisions, 256)]++;
+ level2_histo[_ODP_MIN(collisions, UINT32_C(256))]++;
total_collisions += collisions;
}
@@ -1036,7 +1036,7 @@ static uint32_t level1_hash_histo(secondary_hash_tbl_t *hash_tbl,
level2_histo);
}
- level1_histo[MIN(collisions, 256)]++;
+ level1_histo[_ODP_MIN(collisions, UINT32_C(256))]++;
total_collisions += collisions;
}
@@ -1069,7 +1069,7 @@ static void secondary_hash_histo_print(void)
if (name_hash_tbl.num_secondary_tbls[0] == 0)
return;
- ODP_DBG(" level1 secondary hash histogram:\n");
+ _ODP_DBG(" level1 secondary hash histogram:\n");
total_count = 0;
for (idx = 0; idx < 256; idx++) {
count = level1_histo[idx];
@@ -1077,24 +1077,23 @@ static void secondary_hash_histo_print(void)
total_count += count * idx;
if (count != 0)
- ODP_DBG(" num collisions=%02u count=%u\n",
- idx, count);
+ _ODP_DBG(" num collisions=%02u count=%u\n", idx, count);
}
count = level1_histo[256];
total_count += count;
if (count != 0)
- ODP_DBG(" num collisions >=256 count=%u\n", count);
+ _ODP_DBG(" num collisions >=256 count=%u\n", count);
avg = (100 * total_count) / name_hash_tbl.num_secondary_tbls[0];
avg = avg / SECONDARY_HASH_TBL_SIZE;
- ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
- avg / 100, avg % 100, total_count);
+ _ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
+ avg / 100, avg % 100, total_count);
if (name_hash_tbl.num_secondary_tbls[1] == 0)
return;
- ODP_DBG(" level2 secondary hash histogram:\n");
+ _ODP_DBG(" level2 secondary hash histogram:\n");
total_count = 0;
for (idx = 0; idx < 256; idx++) {
count = level2_histo[idx];
@@ -1102,19 +1101,18 @@ static void secondary_hash_histo_print(void)
total_count += count * idx;
if (count != 0)
- ODP_DBG(" num collisions=%02u count=%u\n",
- idx, count);
+ _ODP_DBG(" num collisions=%02u count=%u\n", idx, count);
}
count = level2_histo[256];
total_count += count;
if (count != 0)
- ODP_DBG(" num collisions >=256 count=%u\n", count);
+ _ODP_DBG(" num collisions >=256 count=%u\n", count);
avg = (100 * total_count) / name_hash_tbl.num_secondary_tbls[1];
avg = avg / SECONDARY_HASH_TBL_SIZE;
- ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
- avg / 100, avg % 100, total_count);
+ _ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
+ avg / 100, avg % 100, total_count);
}
#endif
@@ -1126,30 +1124,31 @@ void _odp_int_name_tbl_stats_print(void)
count, total_count;
uint32_t avg;
- ODP_DBG("\nname table stats:\n");
- ODP_DBG(" num_names=%u num_adds=%lu "
- "num_deletes=%lu num_name_tbls=%u\n",
- name_tbls.current_num_names, name_tbls.num_adds,
- name_tbls.num_deletes, name_tbls.num_name_tbls);
+ _ODP_DBG("\nname table stats:\n");
+ _ODP_DBG(" num_names=%" PRIu32 " num_adds=%" PRIu64 " "
+ "num_deletes=%" PRIu64 " num_name_tbls=%" PRIu8 "\n",
+ name_tbls.current_num_names, name_tbls.num_adds,
+ name_tbls.num_deletes, name_tbls.num_name_tbls);
for (idx = 0; idx < NUM_NAME_TBLS; idx++) {
name_tbl = name_tbls.tbls[idx];
if ((name_tbl) && (name_tbl->num_used != 0))
- ODP_DBG(" name_tbl %u num_allocd=%7u "
- "num_added_to_free_list=%7u "
- "num_used=%7u num_avail_to_add=%7u\n", idx,
- name_tbl->num_allocd,
- name_tbl->num_added_to_free_list,
- name_tbl->num_used,
- name_tbl->num_avail_to_add);
+ _ODP_DBG(" name_tbl %u num_allocd=%7u "
+ "num_added_to_free_list=%7u "
+ "num_used=%7u num_avail_to_add=%7u\n", idx,
+ name_tbl->num_allocd,
+ name_tbl->num_added_to_free_list,
+ name_tbl->num_used,
+ name_tbl->num_avail_to_add);
}
memset(primary_hash_histo, 0, sizeof(primary_hash_histo));
for (idx = 0; idx < PRIMARY_HASH_TBL_SIZE; idx++) {
- collisions = MIN(name_hash_tbl.hash_collisions[idx], 256);
+ collisions =
+ _ODP_MIN(name_hash_tbl.hash_collisions[idx], UINT32_C(256));
primary_hash_histo[collisions]++;
}
- ODP_DBG(" name_tbl primary hash histogram:\n");
+ _ODP_DBG(" name_tbl primary hash histogram:\n");
total_count = 0;
for (idx = 0; idx < 256; idx++) {
count = primary_hash_histo[idx];
@@ -1157,23 +1156,20 @@ void _odp_int_name_tbl_stats_print(void)
total_count += count * idx;
if (count != 0)
- ODP_DBG(" num collisions=%02u count=%u\n",
- idx, count);
+ _ODP_DBG(" num collisions=%02u count=%u\n", idx, count);
}
count = primary_hash_histo[256];
total_count += count;
if (count != 0)
- ODP_DBG(" num collisions >=256 count=%u\n", count);
+ _ODP_DBG(" num collisions >=256 count=%u\n", count);
avg = (100 * total_count) / PRIMARY_HASH_TBL_SIZE;
- ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
- avg / 100, avg % 100, total_count);
+ _ODP_DBG(" avg collisions=%02u.%02u total=%u\n\n",
+ avg / 100, avg % 100, total_count);
- ODP_DBG(" num of first level secondary hash tbls=%u "
- "second level tbls=%u\n",
- name_hash_tbl.num_secondary_tbls[0],
- name_hash_tbl.num_secondary_tbls[1]);
+ _ODP_DBG(" num of first level secondary hash tbls=%u second level tbls=%u\n",
+ name_hash_tbl.num_secondary_tbls[0], name_hash_tbl.num_secondary_tbls[1]);
#ifdef SECONDARY_HASH_HISTO_PRINT
if (name_hash_tbl.num_secondary_tbls[0] != 0)
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index f18bd4dd5..364992761 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -1,18 +1,39 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
-#include <odp/api/plat/packet_inlines.h>
+#include <odp/autoheader_external.h>
+
+#include <odp/api/byteorder.h>
+#include <odp/api/hash.h>
+#include <odp/api/hints.h>
#include <odp/api/packet.h>
-#include <odp_packet_internal.h>
+#include <odp/api/packet_flags.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/proto_stats.h>
+#include <odp/api/timer.h>
+
+#include <odp_parse_internal.h>
+#include <odp_chksum_internal.h>
#include <odp_debug_internal.h>
-#include <odp/api/hints.h>
-#include <odp/api/byteorder.h>
+#include <odp_event_internal.h>
+#include <odp_event_validation_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_string_internal.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/byteorder_inlines.h>
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_io_inlines.h>
#include <protocols/eth.h>
#include <protocols/ip.h>
+#include <protocols/sctp.h>
#include <protocols/tcp.h>
#include <protocols/udp.h>
@@ -21,224 +42,152 @@
#include <stdio.h>
#include <inttypes.h>
-/* Initial packet segment data length */
-#define BASE_LEN CONFIG_PACKET_MAX_SEG_LEN
-
#include <odp/visibility_begin.h>
/* Fill in packet header field offsets for inline functions */
const _odp_packet_inline_offset_t _odp_packet_inline ODP_ALIGNED_CACHE = {
- .data = offsetof(odp_packet_hdr_t, buf_hdr.seg[0].data),
- .seg_len = offsetof(odp_packet_hdr_t, buf_hdr.seg[0].len),
+ .seg_data = offsetof(odp_packet_hdr_t, seg_data),
+ .seg_len = offsetof(odp_packet_hdr_t, seg_len),
+ .seg_next = offsetof(odp_packet_hdr_t, seg_next),
.frame_len = offsetof(odp_packet_hdr_t, frame_len),
.headroom = offsetof(odp_packet_hdr_t, headroom),
.tailroom = offsetof(odp_packet_hdr_t, tailroom),
- .unshared_len = offsetof(odp_packet_hdr_t, unshared_len),
- .ref_hdr = offsetof(odp_packet_hdr_t, ref_hdr),
- .ref_offset = offsetof(odp_packet_hdr_t, ref_offset),
- .ref_len = offsetof(odp_packet_hdr_t, ref_len),
- .pool = offsetof(odp_packet_hdr_t, buf_hdr.pool_hdl),
+ .pool = offsetof(odp_packet_hdr_t, event_hdr.pool),
.input = offsetof(odp_packet_hdr_t, input),
- .segcount = offsetof(odp_packet_hdr_t, buf_hdr.segcount),
- .user_ptr = offsetof(odp_packet_hdr_t, buf_hdr.buf_ctx),
- .user_area = offsetof(odp_packet_hdr_t, buf_hdr.uarea_addr),
- .user_area_size = offsetof(odp_packet_hdr_t, buf_hdr.uarea_size),
+ .seg_count = offsetof(odp_packet_hdr_t, seg_count),
+ .user_ptr = offsetof(odp_packet_hdr_t, user_ptr),
+ .user_area = offsetof(odp_packet_hdr_t, uarea_addr),
+ .l2_offset = offsetof(odp_packet_hdr_t, p.l2_offset),
+ .l3_offset = offsetof(odp_packet_hdr_t, p.l3_offset),
+ .l4_offset = offsetof(odp_packet_hdr_t, p.l4_offset),
.flow_hash = offsetof(odp_packet_hdr_t, flow_hash),
.timestamp = offsetof(odp_packet_hdr_t, timestamp),
- .input_flags = offsetof(odp_packet_hdr_t, p.input_flags)
-
+ .input_flags = offsetof(odp_packet_hdr_t, p.input_flags),
+ .flags = offsetof(odp_packet_hdr_t, p.flags),
+ .cls_mark = offsetof(odp_packet_hdr_t, cls_mark),
+ .ipsec_ctx = offsetof(odp_packet_hdr_t, ipsec_ctx),
+ .crypto_op = offsetof(odp_packet_hdr_t, crypto_op_result),
};
#include <odp/visibility_end.h>
-static inline odp_packet_hdr_t *packet_hdr(odp_packet_t pkt)
-{
- return (odp_packet_hdr_t *)(uintptr_t)pkt;
-}
-
-static inline odp_buffer_t buffer_handle(odp_packet_hdr_t *pkt_hdr)
-{
- return (odp_buffer_t)pkt_hdr;
-}
+/* Check that invalid values are the same. Some versions of Clang and pedantic
+ * build have trouble with the strong type casting, and complain that these
+ * invalid values are not integral constants.
+ *
+ * Invalid values are required to be equal for _odp_buffer_is_valid() to work
+ * properly. */
+#ifndef __clang__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ODP_STATIC_ASSERT(ODP_PACKET_INVALID == 0, "Packet invalid not 0");
+ODP_STATIC_ASSERT(ODP_BUFFER_INVALID == 0, "Buffer invalid not 0");
+ODP_STATIC_ASSERT(ODP_EVENT_INVALID == 0, "Event invalid not 0");
+ODP_STATIC_ASSERT(ODP_PACKET_VECTOR_INVALID == 0, "Packet vector invalid not 0");
+ODP_STATIC_ASSERT(ODP_PACKET_TX_COMPL_INVALID == 0, "Packet TX completion invalid not 0");
+ODP_STATIC_ASSERT(ODP_TIMEOUT_INVALID == 0, "Timeout invalid not 0");
+#pragma GCC diagnostic pop
+#endif
-static inline void packet_ref_inc(odp_packet_hdr_t *pkt_hdr)
+static inline odp_packet_hdr_t *packet_seg_to_hdr(odp_packet_seg_t seg)
{
- odp_atomic_inc_u32(&pkt_hdr->ref_count);
+ return (odp_packet_hdr_t *)(uintptr_t)seg;
}
-static inline uint32_t packet_ref_dec(odp_packet_hdr_t *pkt_hdr)
+static inline odp_packet_seg_t packet_hdr_to_seg(odp_packet_hdr_t *pkt_hdr)
{
- return odp_atomic_fetch_dec_u32(&pkt_hdr->ref_count);
+ return (odp_packet_seg_t)pkt_hdr;
}
-static inline odp_packet_hdr_t *buf_to_packet_hdr(odp_buffer_t buf)
+/*
+ * Return pointer to the current segment and step cur_hdr forward.
+ */
+static inline odp_packet_hdr_t *packet_seg_step(odp_packet_hdr_t **cur_hdr)
{
- return (odp_packet_hdr_t *)buf_hdl_to_hdr(buf);
-}
+ odp_packet_hdr_t *hdr = *cur_hdr;
-odp_packet_t _odp_packet_from_buf_hdr(odp_buffer_hdr_t *buf_hdr)
-{
- return (odp_packet_t)buf_hdr;
-}
+ *cur_hdr = hdr->seg_next;
-static inline odp_buffer_t packet_to_buffer(odp_packet_t pkt)
-{
- return (odp_buffer_t)pkt;
+ return hdr;
}
-static inline uint32_t packet_seg_len(odp_packet_hdr_t *pkt_hdr,
- uint32_t seg_idx)
+static inline void packet_seg_find_idx(odp_packet_hdr_t **pkt_hdr,
+ uint32_t find_idx)
{
- return pkt_hdr->buf_hdr.seg[seg_idx].len;
-}
+ odp_packet_hdr_t *hdr = *pkt_hdr;
+ uint32_t idx = 0;
-static inline uint8_t *packet_seg_data(odp_packet_hdr_t *pkt_hdr,
- uint32_t seg_idx)
-{
- return pkt_hdr->buf_hdr.seg[seg_idx].data;
-}
+ while (odp_unlikely(idx < find_idx)) {
+ idx++;
+ hdr = hdr->seg_next;
+ }
-static inline uint32_t packet_last_seg(odp_packet_hdr_t *pkt_hdr)
-{
- if (CONFIG_PACKET_MAX_SEGS == 1)
- return 0;
- else
- return pkt_hdr->buf_hdr.segcount - 1;
+ *pkt_hdr = hdr;
}
-static inline uint32_t packet_first_seg_len(odp_packet_hdr_t *pkt_hdr)
-{
- return packet_seg_len(pkt_hdr, 0);
-}
-
-static inline uint32_t packet_last_seg_len(odp_packet_hdr_t *pkt_hdr)
+static inline uint32_t packet_seg_len(odp_packet_hdr_t *pkt_hdr,
+ uint32_t seg_idx)
{
- int last = packet_last_seg(pkt_hdr);
+ packet_seg_find_idx(&pkt_hdr, seg_idx);
- return packet_seg_len(pkt_hdr, last);
-}
-
-static inline void *packet_data(odp_packet_hdr_t *pkt_hdr)
-{
- return pkt_hdr->buf_hdr.seg[0].data;
+ return pkt_hdr->seg_len;
}
static inline void *packet_tail(odp_packet_hdr_t *pkt_hdr)
{
- int last = packet_last_seg(pkt_hdr);
- uint32_t seg_len = pkt_hdr->buf_hdr.seg[last].len;
+ odp_packet_hdr_t *last_seg = packet_last_seg(pkt_hdr);
- return pkt_hdr->buf_hdr.seg[last].data + seg_len;
+ return last_seg->seg_data + last_seg->seg_len;
}
-static inline uint32_t seg_headroom(odp_packet_hdr_t *pkt_hdr, int seg)
+static inline uint32_t seg_headroom(odp_packet_hdr_t *pkt_seg)
{
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[seg].hdr;
+ _odp_event_hdr_t *hdr = &pkt_seg->event_hdr;
+ pool_t *pool = _odp_pool_entry(hdr->pool);
uint8_t *base = hdr->base_data;
- uint8_t *head = pkt_hdr->buf_hdr.seg[seg].data;
+ uint8_t *head = pkt_seg->seg_data;
- return CONFIG_PACKET_HEADROOM + (head - base);
+ return pool->headroom + (head - base);
}
-static inline uint32_t seg_tailroom(odp_packet_hdr_t *pkt_hdr, int seg)
+static inline uint32_t seg_tailroom(odp_packet_hdr_t *pkt_seg)
{
- uint32_t seg_len = pkt_hdr->buf_hdr.seg[seg].len;
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[seg].hdr;
- uint8_t *tail = pkt_hdr->buf_hdr.seg[seg].data + seg_len;
+ _odp_event_hdr_t *hdr = &pkt_seg->event_hdr;
+ uint8_t *tail = pkt_seg->seg_data + pkt_seg->seg_len;
return hdr->buf_end - tail;
}
-static inline void push_head(odp_packet_hdr_t *pkt_hdr, uint32_t len)
-{
- pkt_hdr->headroom -= len;
- pkt_hdr->frame_len += len;
- pkt_hdr->unshared_len += len;
- pkt_hdr->buf_hdr.seg[0].data -= len;
- pkt_hdr->buf_hdr.seg[0].len += len;
-}
-
-static inline void pull_head(odp_packet_hdr_t *pkt_hdr, uint32_t len)
-{
- pkt_hdr->headroom += len;
- pkt_hdr->frame_len -= len;
- pkt_hdr->unshared_len -= len;
- pkt_hdr->buf_hdr.seg[0].data += len;
- pkt_hdr->buf_hdr.seg[0].len -= len;
-}
-
static inline void push_tail(odp_packet_hdr_t *pkt_hdr, uint32_t len)
{
- int last = packet_last_seg(pkt_hdr);
+ odp_packet_hdr_t *last_seg = packet_last_seg(pkt_hdr);
pkt_hdr->tailroom -= len;
pkt_hdr->frame_len += len;
- pkt_hdr->unshared_len += len;
- pkt_hdr->buf_hdr.seg[last].len += len;
+ last_seg->seg_len += len;
}
-/* Copy all metadata for segmentation modification. Segment data and lengths
- * are not copied. */
-static inline void packet_seg_copy_md(odp_packet_hdr_t *dst,
- odp_packet_hdr_t *src)
-{
- dst->p = src->p;
-
- /* lengths are not copied:
- * .frame_len
- * .headroom
- * .tailroom
- */
-
- dst->input = src->input;
- dst->dst_queue = src->dst_queue;
- dst->flow_hash = src->flow_hash;
- dst->timestamp = src->timestamp;
- dst->op_result = src->op_result;
-
- /* buffer header side packet metadata */
- dst->buf_hdr.buf_u64 = src->buf_hdr.buf_u64;
- dst->buf_hdr.uarea_addr = src->buf_hdr.uarea_addr;
- dst->buf_hdr.uarea_size = src->buf_hdr.uarea_size;
-
- /* reference related metadata */
- dst->ref_len = src->ref_len;
- dst->unshared_len = src->unshared_len;
-
- /* segmentation data is not copied:
- * buf_hdr.seg[]
- * buf_hdr.segcount
- */
-}
-
-static inline void *packet_map(odp_packet_hdr_t *pkt_hdr,
- uint32_t offset, uint32_t *seg_len, int *seg_idx)
+static inline void *packet_map(void *pkt_ptr, uint32_t offset,
+ uint32_t *seg_len, odp_packet_seg_t *seg)
{
void *addr;
uint32_t len;
- int seg = 0;
- int seg_count = pkt_hdr->buf_hdr.segcount;
-
- /* Special processing for references */
- while (offset >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
- offset -= (pkt_hdr->frame_len - pkt_hdr->ref_offset);
- offset += (pkt_hdr->ref_hdr->frame_len - pkt_hdr->ref_len);
- pkt_hdr = pkt_hdr->ref_hdr;
- seg_count = pkt_hdr->buf_hdr.segcount;
- }
+ odp_packet_hdr_t *pkt_hdr = pkt_ptr;
+ int seg_count = pkt_hdr->seg_count;
- if (odp_unlikely(offset > pkt_hdr->frame_len))
+ if (odp_unlikely(offset >= pkt_hdr->frame_len))
return NULL;
- if (odp_likely(CONFIG_PACKET_MAX_SEGS == 1 || seg_count == 1)) {
- addr = pkt_hdr->buf_hdr.seg[0].data + offset;
- len = pkt_hdr->buf_hdr.seg[0].len - offset;
+ if (odp_likely(seg_count == 1)) {
+ addr = pkt_hdr->seg_data + offset;
+ len = pkt_hdr->seg_len - offset;
} else {
- int i;
+ odp_packet_hdr_t *next_hdr = pkt_hdr;
uint32_t seg_start = 0, seg_end = 0;
- for (i = 0; i < seg_count; i++) {
- seg_end += pkt_hdr->buf_hdr.seg[i].len;
+ while (next_hdr != NULL) {
+ pkt_hdr = packet_seg_step(&next_hdr);
+ seg_end += pkt_hdr->seg_len;
if (odp_likely(offset < seg_end))
break;
@@ -246,94 +195,168 @@ static inline void *packet_map(odp_packet_hdr_t *pkt_hdr,
seg_start = seg_end;
}
- addr = pkt_hdr->buf_hdr.seg[i].data + (offset - seg_start);
- len = pkt_hdr->buf_hdr.seg[i].len - (offset - seg_start);
- seg = i;
+ addr = pkt_hdr->seg_data + (offset - seg_start);
+ len = pkt_hdr->seg_len - (offset - seg_start);
}
if (seg_len)
*seg_len = len;
- if (seg_idx)
- *seg_idx = seg;
+ if (seg)
+ *seg = packet_hdr_to_seg(pkt_hdr);
return addr;
}
-void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)
+#include <odp/visibility_begin.h>
+
+/* This file uses the inlined version directly. Inlined API calls use this when
+ * offset does not point to the first segment. */
+void *_odp_packet_map(void *pkt_ptr, uint32_t offset, uint32_t *seg_len,
+ odp_packet_seg_t *seg)
+{
+ return packet_map(pkt_ptr, offset, seg_len, seg);
+}
+
+int _odp_packet_copy_from_mem_seg(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, const void *src)
+{
+ void *mapaddr;
+ uint32_t seglen = 0; /* GCC */
+ uint32_t cpylen;
+ const uint8_t *srcaddr = (const uint8_t *)src;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (offset + len > pkt_hdr->frame_len)
+ return -1;
+
+ while (len > 0) {
+ mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
+ cpylen = len > seglen ? seglen : len;
+ memcpy(mapaddr, srcaddr, cpylen);
+ offset += cpylen;
+ srcaddr += cpylen;
+ len -= cpylen;
+ }
+
+ return 0;
+}
+
+int _odp_packet_copy_to_mem_seg(odp_packet_t pkt, uint32_t offset,
+ uint32_t len, void *dst)
{
- /* Reset parser metadata before new parse */
- pkt_hdr->p.error_flags.all = 0;
- pkt_hdr->p.input_flags.all = 0;
- pkt_hdr->p.output_flags.all = 0;
- pkt_hdr->p.l2_offset = 0;
- pkt_hdr->p.l3_offset = ODP_PACKET_OFFSET_INVALID;
- pkt_hdr->p.l4_offset = ODP_PACKET_OFFSET_INVALID;
+ void *mapaddr;
+ uint32_t seglen = 0; /* GCC */
+ uint32_t cpylen;
+ uint8_t *dstaddr = (uint8_t *)dst;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (offset + len > pkt_hdr->frame_len)
+ return -1;
+
+ while (len > 0) {
+ mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
+ cpylen = len > seglen ? seglen : len;
+ memcpy(dstaddr, mapaddr, cpylen);
+ offset += cpylen;
+ dstaddr += cpylen;
+ len -= cpylen;
+ }
+
+ return 0;
+}
+
+#include <odp/visibility_end.h>
+
+static inline void link_segments(odp_packet_hdr_t *pkt_hdr[], int num)
+{
+ int cur = 0;
+ odp_packet_hdr_t *hdr;
+ odp_packet_hdr_t *head = pkt_hdr[0];
+ uint32_t seg_len = _odp_pool_entry(head->event_hdr.pool)->seg_len;
+
+ while (1) {
+ _odp_event_hdr_t *event_hdr = &pkt_hdr[cur]->event_hdr;
+
+ hdr = pkt_hdr[cur];
+ hdr->seg_data = event_hdr->base_data;
+ hdr->seg_len = seg_len;
+
+ /* init_segments() handles first seg ref_cnt init */
+ if (ODP_DEBUG == 1 && cur > 0) {
+ uint32_t prev_ref;
+ odp_atomic_u32_t *ref_cnt;
- /* Ensure dummy pkt hdrs used in I/O recv classification are valid */
- pkt_hdr->ref_hdr = NULL;
+ ref_cnt = &pkt_hdr[cur]->ref_cnt;
+ prev_ref = odp_atomic_fetch_inc_u32(ref_cnt);
+
+ _ODP_ASSERT(prev_ref == 0);
+ }
+
+ cur++;
+
+ if (cur == num) {
+ /* Last segment */
+ hdr->seg_next = NULL;
+ return;
+ }
+
+ hdr->seg_next = pkt_hdr[cur];
+ }
}
static inline void init_segments(odp_packet_hdr_t *pkt_hdr[], int num)
{
odp_packet_hdr_t *hdr;
- int i;
+ uint32_t seg_len;
/* First segment is the packet descriptor */
hdr = pkt_hdr[0];
+ seg_len = _odp_pool_entry(hdr->event_hdr.pool)->seg_len;
- hdr->buf_hdr.seg[0].data = hdr->buf_hdr.base_data;
- hdr->buf_hdr.seg[0].len = BASE_LEN;
- packet_ref_count_set(hdr, 1);
+ /* Defaults for single segment packet */
+ hdr->seg_data = hdr->event_hdr.base_data;
+ hdr->seg_len = seg_len;
+ hdr->seg_next = NULL;
- /* Link segments */
- if (CONFIG_PACKET_MAX_SEGS != 1) {
- hdr->buf_hdr.segcount = num;
-
- if (odp_unlikely(num > 1)) {
- for (i = 1; i < num; i++) {
- odp_buffer_hdr_t *buf_hdr;
-
- packet_ref_count_set(pkt_hdr[i], 1);
- buf_hdr = &pkt_hdr[i]->buf_hdr;
- hdr->buf_hdr.seg[i].hdr = buf_hdr;
- hdr->buf_hdr.seg[i].data = buf_hdr->base_data;
- hdr->buf_hdr.seg[i].len = BASE_LEN;
- }
- }
+ hdr->seg_count = num;
+
+ if (ODP_DEBUG == 1) {
+ uint32_t prev_ref =
+ odp_atomic_fetch_inc_u32(&hdr->ref_cnt);
+
+ _ODP_ASSERT(prev_ref == 0);
}
+
+ /* Link segments */
+ if (odp_unlikely(num > 1))
+ link_segments(pkt_hdr, num);
}
-static inline void reset_seg(odp_packet_hdr_t *pkt_hdr, int first, int num)
+static inline void reset_segments(odp_packet_hdr_t *pkt_hdr)
{
- odp_buffer_hdr_t *hdr;
void *base;
- int i;
+ uint32_t seg_len = _odp_pool_entry(pkt_hdr->event_hdr.pool)->seg_len;
+
+ while (pkt_hdr != NULL) {
+ base = pkt_hdr->event_hdr.base_data;
+
+ pkt_hdr->seg_len = seg_len;
+ pkt_hdr->seg_data = base;
- for (i = first; i < first + num; i++) {
- hdr = pkt_hdr->buf_hdr.seg[i].hdr;
- base = hdr->base_data;
- pkt_hdr->buf_hdr.seg[i].len = BASE_LEN;
- pkt_hdr->buf_hdr.seg[i].data = base;
+ pkt_hdr = pkt_hdr->seg_next;
}
}
/* Calculate the number of segments */
-static inline int num_segments(uint32_t len)
+static inline int num_segments(uint32_t len, uint32_t seg_len)
{
- uint32_t max_seg_len;
- int num;
+ int num = 1;
- if (CONFIG_PACKET_MAX_SEGS == 1)
- return 1;
+ if (odp_unlikely(len > seg_len)) {
+ num = len / seg_len;
- num = 1;
- max_seg_len = CONFIG_PACKET_MAX_SEG_LEN;
-
- if (odp_unlikely(len > max_seg_len)) {
- num = len / max_seg_len;
-
- if (odp_likely((num * max_seg_len) != len))
+ if (odp_likely((num * seg_len) != len))
num += 1;
}
@@ -342,31 +365,10 @@ static inline int num_segments(uint32_t len)
static inline void add_all_segs(odp_packet_hdr_t *to, odp_packet_hdr_t *from)
{
- int i;
- int n = to->buf_hdr.segcount;
- int num = from->buf_hdr.segcount;
+ odp_packet_hdr_t *last = packet_last_seg(to);
- for (i = 0; i < num; i++) {
- to->buf_hdr.seg[n + i].hdr = from->buf_hdr.seg[i].hdr;
- to->buf_hdr.seg[n + i].data = from->buf_hdr.seg[i].data;
- to->buf_hdr.seg[n + i].len = from->buf_hdr.seg[i].len;
- }
-
- to->buf_hdr.segcount = n + num;
-}
-
-static inline void copy_num_segs(odp_packet_hdr_t *to, odp_packet_hdr_t *from,
- int first, int num)
-{
- int i;
-
- for (i = 0; i < num; i++) {
- to->buf_hdr.seg[i].hdr = from->buf_hdr.seg[first + i].hdr;
- to->buf_hdr.seg[i].data = from->buf_hdr.seg[first + i].data;
- to->buf_hdr.seg[i].len = from->buf_hdr.seg[first + i].len;
- }
-
- to->buf_hdr.segcount = num;
+ last->seg_next = from;
+ to->seg_count += from->seg_count;
}
static inline odp_packet_hdr_t *alloc_segments(pool_t *pool, int num)
@@ -374,11 +376,11 @@ static inline odp_packet_hdr_t *alloc_segments(pool_t *pool, int num)
odp_packet_hdr_t *pkt_hdr[num];
int ret;
- ret = buffer_alloc_multi(pool, (odp_buffer_hdr_t **)pkt_hdr, num);
+ ret = _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)pkt_hdr, num);
if (odp_unlikely(ret != num)) {
if (ret > 0)
- buffer_free_multi((odp_buffer_hdr_t **)pkt_hdr, ret);
+ _odp_event_free_multi((_odp_event_hdr_t **)pkt_hdr, ret);
return NULL;
}
@@ -400,110 +402,173 @@ static inline odp_packet_hdr_t *add_segments(odp_packet_hdr_t *pkt_hdr,
if (new_hdr == NULL)
return NULL;
- seg_len = len - ((num - 1) * pool->max_seg_len);
- offset = pool->max_seg_len - seg_len;
+ seg_len = len - ((num - 1) * pool->seg_len);
+ offset = pool->seg_len - seg_len;
if (head) {
/* add into the head*/
add_all_segs(new_hdr, pkt_hdr);
/* adjust first segment length */
- new_hdr->buf_hdr.seg[0].data += offset;
- new_hdr->buf_hdr.seg[0].len = seg_len;
+ new_hdr->seg_data += offset;
+ new_hdr->seg_len = seg_len;
- packet_seg_copy_md(new_hdr, pkt_hdr);
- new_hdr->frame_len = pkt_hdr->frame_len + len;
- new_hdr->unshared_len = pkt_hdr->unshared_len + len;
- new_hdr->headroom = pool->headroom + offset;
- new_hdr->tailroom = pkt_hdr->tailroom;
+ _odp_packet_copy_md(new_hdr, pkt_hdr, 0);
+ new_hdr->frame_len = pkt_hdr->frame_len + len;
+ new_hdr->headroom = pool->headroom + offset;
+ new_hdr->tailroom = pkt_hdr->tailroom;
pkt_hdr = new_hdr;
} else {
- int last;
+ odp_packet_hdr_t *last_seg;
/* add into the tail */
add_all_segs(pkt_hdr, new_hdr);
/* adjust last segment length */
- last = packet_last_seg(pkt_hdr);
- pkt_hdr->buf_hdr.seg[last].len = seg_len;
+ last_seg = packet_last_seg(pkt_hdr);
+ last_seg->seg_len = seg_len;
- pkt_hdr->frame_len += len;
- pkt_hdr->unshared_len += len;
- pkt_hdr->tailroom = pool->tailroom + offset;
+ pkt_hdr->frame_len += len;
+ pkt_hdr->tailroom = pool->tailroom + offset;
}
return pkt_hdr;
}
-static inline void free_bufs(odp_packet_hdr_t *pkt_hdr, int first, int num)
+static inline void segment_ref_inc(odp_packet_hdr_t *seg_hdr)
{
- int i, nfree;
- odp_buffer_hdr_t *buf_hdr[num];
+ uint32_t ref_cnt = odp_atomic_load_u32(&seg_hdr->ref_cnt);
+
+ /* First count increment after alloc */
+ if (odp_likely(ref_cnt == 0))
+ odp_atomic_store_u32(&seg_hdr->ref_cnt, 2);
+ else
+ odp_atomic_inc_u32(&seg_hdr->ref_cnt);
+}
- for (i = 0, nfree = 0; i < num; i++) {
- odp_packet_hdr_t *hdr = pkt_hdr->buf_hdr.seg[first + i].hdr;
+static inline uint32_t segment_ref_dec(odp_packet_hdr_t *seg_hdr)
+{
+ return odp_atomic_fetch_dec_u32(&seg_hdr->ref_cnt);
+}
- if (packet_ref_count(hdr) == 1 || packet_ref_dec(hdr) == 1) {
- ODP_ASSERT((packet_ref_count_set(hdr, 0), 1));
- buf_hdr[nfree++] = &hdr->buf_hdr;
+static inline uint32_t segment_ref(odp_packet_hdr_t *seg_hdr)
+{
+ return odp_atomic_load_u32(&seg_hdr->ref_cnt);
+}
+
+static inline int is_multi_ref(uint32_t ref_cnt)
+{
+ return (ref_cnt > 1);
+}
+
+static inline void packet_free_multi(odp_packet_hdr_t *hdr[], int num)
+{
+ int i;
+ uint32_t ref_cnt;
+ int num_ref = 0;
+
+ for (i = 0; i < num; i++) {
+ /* Zero when reference API has not been used */
+ ref_cnt = segment_ref(hdr[i]);
+
+ if (odp_unlikely(ref_cnt)) {
+ ref_cnt = segment_ref_dec(hdr[i]);
+
+ if (is_multi_ref(ref_cnt)) {
+ num_ref++;
+ continue;
+ }
}
+
+ /* Skip references and pack to be freed headers to array head */
+ if (odp_unlikely(num_ref))
+ hdr[i - num_ref] = hdr[i];
}
- if (nfree > 0)
- buffer_free_multi(buf_hdr, nfree);
+ num -= num_ref;
+
+ if (odp_likely(num))
+ _odp_event_free_multi((_odp_event_hdr_t **)(uintptr_t)hdr, num);
+}
+
+static inline void free_all_segments(odp_packet_hdr_t *pkt_hdr, int num)
+{
+ int i;
+ odp_packet_hdr_t *pkt_hdrs[num];
+ odp_packet_hdr_t *seg_hdr = pkt_hdr;
+
+ for (i = 0; i < num; i++) {
+ pkt_hdrs[i] = seg_hdr;
+ seg_hdr = seg_hdr->seg_next;
+ }
+
+ packet_free_multi(pkt_hdrs, num);
}
static inline odp_packet_hdr_t *free_segments(odp_packet_hdr_t *pkt_hdr,
int num, uint32_t free_len,
uint32_t pull_len, int head)
{
- int num_remain = pkt_hdr->buf_hdr.segcount - num;
+ odp_packet_hdr_t *seg_hdr;
+ int i;
+ int num_remain = pkt_hdr->seg_count - num;
+ odp_packet_hdr_t *hdr = pkt_hdr;
+ odp_packet_hdr_t *last_hdr = packet_last_seg(pkt_hdr);
+ odp_packet_hdr_t *pkt_hdrs[num];
if (head) {
odp_packet_hdr_t *new_hdr;
- int i, nfree;
- odp_buffer_hdr_t *buf_hdr[num];
-
- for (i = 0, nfree = 0; i < num; i++) {
- new_hdr = pkt_hdr->buf_hdr.seg[i].hdr;
- if (packet_ref_count(new_hdr) == 1 ||
- packet_ref_dec(new_hdr) == 1) {
- ODP_ASSERT((packet_ref_count_set(new_hdr, 0),
- 1));
- buf_hdr[nfree++] = &new_hdr->buf_hdr;
- }
+ for (i = 0; i < num; i++) {
+ seg_hdr = packet_seg_step(&hdr);
+ pkt_hdrs[i] = seg_hdr;
}
- /* First remaining segment is the new packet descriptor */
- new_hdr = pkt_hdr->buf_hdr.seg[num].hdr;
+ /* The first remaining header is the new packet descriptor.
+ * Copy remaining segments from the last to-be-removed header
+ * to the new header. */
+ new_hdr = hdr;
+
+ new_hdr->seg_next = hdr->seg_next;
+ new_hdr->seg_count = num_remain;
- copy_num_segs(new_hdr, pkt_hdr, num, num_remain);
- packet_seg_copy_md(new_hdr, pkt_hdr);
+ _odp_packet_copy_md(new_hdr, pkt_hdr, 0);
/* Tailroom not changed */
- new_hdr->tailroom = pkt_hdr->tailroom;
- new_hdr->headroom = seg_headroom(new_hdr, 0);
- new_hdr->frame_len = pkt_hdr->frame_len - free_len;
- new_hdr->unshared_len = pkt_hdr->unshared_len - free_len;
+ new_hdr->tailroom = pkt_hdr->tailroom;
+
+ new_hdr->headroom = seg_headroom(new_hdr);
+
+ new_hdr->frame_len = pkt_hdr->frame_len - free_len;
pull_head(new_hdr, pull_len);
pkt_hdr = new_hdr;
- if (nfree > 0)
- buffer_free_multi(buf_hdr, nfree);
+ packet_free_multi(pkt_hdrs, num);
} else {
- /* Free last 'num' bufs */
- free_bufs(pkt_hdr, num_remain, num);
+ /* Free last 'num' bufs.
+ * First, find the last remaining header. */
+ packet_seg_find_idx(&hdr, num_remain - 1);
+ last_hdr = hdr;
+
+ packet_seg_step(&hdr);
+
+ for (i = 0; i < num; i++) {
+ seg_hdr = packet_seg_step(&hdr);
+ pkt_hdrs[i] = seg_hdr;
+ }
+
+ packet_free_multi(pkt_hdrs, num);
/* Head segment remains, no need to copy or update majority
* of the metadata. */
- pkt_hdr->buf_hdr.segcount = num_remain;
+ last_hdr->seg_next = NULL;
+
+ pkt_hdr->seg_count = num_remain;
pkt_hdr->frame_len -= free_len;
- pkt_hdr->unshared_len -= free_len;
- pkt_hdr->tailroom = seg_tailroom(pkt_hdr, num_remain - 1);
+ pkt_hdr->tailroom = seg_tailroom(pkt_hdr);
pull_tail(pkt_hdr, pull_len);
}
@@ -518,9 +583,11 @@ static inline int packet_alloc(pool_t *pool, uint32_t len, int max_pkt,
int num = max_pkt;
int max_buf = max_pkt * num_seg;
odp_packet_hdr_t *pkt_hdr[max_buf];
+ odp_packet_hdr_t *hdr_next;
+ odp_packet_hdr_t *hdr;
- num_buf = buffer_alloc_multi(pool, (odp_buffer_hdr_t **)pkt_hdr,
- max_buf);
+ num_buf = _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)pkt_hdr,
+ max_buf);
/* Failed to allocate all segments */
if (odp_unlikely(num_buf != max_buf)) {
@@ -530,37 +597,49 @@ static inline int packet_alloc(pool_t *pool, uint32_t len, int max_pkt,
num_free = num_buf - (num * num_seg);
if (num_free > 0) {
- odp_buffer_hdr_t **p;
+ _odp_event_hdr_t **p;
- p = (odp_buffer_hdr_t **)&pkt_hdr[num_buf - num_free];
- buffer_free_multi(p, num_free);
+ p = (_odp_event_hdr_t **)&pkt_hdr[num_buf - num_free];
+ _odp_event_free_multi(p, num_free);
}
if (num == 0)
return 0;
}
- for (i = 0; i < num; i++) {
- odp_packet_hdr_t *hdr;
+ hdr_next = pkt_hdr[0];
+ odp_prefetch(hdr_next);
+ odp_prefetch((uint8_t *)hdr_next + ODP_CACHE_LINE_SIZE);
+
+ for (i = 0; i < num - 1; i++) {
+ hdr = hdr_next;
+ hdr_next = pkt_hdr[(i + 1) * num_seg];
+
+ odp_prefetch(hdr_next);
+ odp_prefetch((uint8_t *)hdr_next + ODP_CACHE_LINE_SIZE);
/* First buffer is the packet descriptor */
- hdr = pkt_hdr[i * num_seg];
pkt[i] = packet_handle(hdr);
init_segments(&pkt_hdr[i * num_seg], num_seg);
packet_init(hdr, len);
}
+ /* Last packet */
+ pkt[i] = packet_handle(hdr_next);
+ init_segments(&pkt_hdr[i * num_seg], num_seg);
+ packet_init(hdr_next, len);
+
return num;
}
-int packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
- odp_packet_t pkt[], int max_num)
+int _odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
+ odp_packet_t pkt[], int max_num)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
int num, num_seg;
- num_seg = num_segments(len);
+ num_seg = num_segments(len, pool->seg_len);
num = packet_alloc(pool, len, max_num, num_seg, pkt);
return num;
@@ -568,19 +647,16 @@ int packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
odp_packet_t pkt;
int num, num_seg;
- if (odp_unlikely(pool->params.type != ODP_POOL_PACKET)) {
- __odp_errno = EINVAL;
- return ODP_PACKET_INVALID;
- }
+ _ODP_ASSERT(pool->type == ODP_POOL_PACKET);
- if (odp_unlikely(len > pool->max_len))
+ if (odp_unlikely(len > pool->max_len || len == 0))
return ODP_PACKET_INVALID;
- num_seg = num_segments(len);
+ num_seg = num_segments(len, pool->seg_len);
num = packet_alloc(pool, len, 1, num_seg, &pkt);
if (odp_unlikely(num == 0))
@@ -592,132 +668,115 @@ odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
int odp_packet_alloc_multi(odp_pool_t pool_hdl, uint32_t len,
odp_packet_t pkt[], int max_num)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
int num, num_seg;
- if (odp_unlikely(pool->params.type != ODP_POOL_PACKET)) {
- __odp_errno = EINVAL;
- return -1;
- }
+ _ODP_ASSERT(pool->type == ODP_POOL_PACKET);
- if (odp_unlikely(len > pool->max_len))
+ if (odp_unlikely(len > pool->max_len || len == 0))
return -1;
- num_seg = num_segments(len);
+ num_seg = num_segments(len, pool->seg_len);
num = packet_alloc(pool, len, max_num, num_seg, pkt);
return num;
}
-static inline void packet_free(odp_packet_hdr_t *pkt_hdr)
+void odp_packet_free(odp_packet_t pkt)
{
- odp_packet_hdr_t *ref_hdr;
- odp_buffer_hdr_t *buf_hdr;
- uint32_t ref_count;
- int num_seg;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ int num_seg = pkt_hdr->seg_count;
- do {
- buf_hdr = &pkt_hdr->buf_hdr;
- ref_count = packet_ref_count(pkt_hdr);
- num_seg = pkt_hdr->buf_hdr.segcount;
- ref_hdr = pkt_hdr->ref_hdr;
- ODP_ASSERT(ref_count >= 1);
+ _odp_packet_validate(pkt, _ODP_EV_PACKET_FREE);
- if (odp_likely((CONFIG_PACKET_MAX_SEGS == 1 || num_seg == 1) &&
- ref_count == 1)) {
- ODP_ASSERT((packet_ref_count_set(pkt_hdr, 0), 1));
- buffer_free_multi(&buf_hdr, 1);
- } else {
- free_bufs(pkt_hdr, 0, num_seg);
- }
+ _ODP_ASSERT(segment_ref(pkt_hdr) > 0);
- pkt_hdr = ref_hdr;
- } while (pkt_hdr);
+ if (odp_likely(num_seg == 1))
+ packet_free_multi(&pkt_hdr, 1);
+ else
+ free_all_segments(pkt_hdr, num_seg);
}
-void odp_packet_free(odp_packet_t pkt)
+static inline void packet_free_multi_ev(const odp_packet_t pkt[], int num, _odp_ev_id_t id)
{
- packet_free(packet_hdr(pkt));
-}
+ if (odp_unlikely(!num))
+ return;
-void odp_packet_free_multi(const odp_packet_t pkt[], int num)
-{
- odp_packet_hdr_t *pkt_hdr, *ref_hdr, *hdr;
- int nbufs = num * CONFIG_PACKET_MAX_SEGS * 2;
- odp_buffer_hdr_t *buf_hdr[nbufs];
- int num_seg;
- int i, j;
- uint32_t ref_count;
- int nfree = 0;
+ odp_packet_hdr_t *pkt_hdrs[num];
+ int i;
+ int num_freed = 0;
- for (i = 0; i < num; i++) {
- pkt_hdr = packet_hdr(pkt[i]);
+ _odp_packet_validate_multi(pkt, num, id);
- do {
- num_seg = pkt_hdr->buf_hdr.segcount;
- ref_hdr = pkt_hdr->ref_hdr;
+ for (i = 0; i < num; i++) {
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt[i]);
+ int num_seg = pkt_hdr->seg_count;
- /* Make sure we have enough space for this pkt's segs */
- if (nfree + num_seg > nbufs) {
- buffer_free_multi(buf_hdr, nfree);
- nfree = 0;
- }
+ _ODP_ASSERT(segment_ref(pkt_hdr) > 0);
- for (j = 0; j < num_seg; j++) {
- hdr = pkt_hdr->buf_hdr.seg[j].hdr;
- ref_count = packet_ref_count(hdr);
- ODP_ASSERT(ref_count >= 1);
-
- if (ref_count == 1 ||
- packet_ref_dec(hdr) == 1) {
- ODP_ASSERT
- ((packet_ref_count_set(hdr, 0),
- 1));
- buf_hdr[nfree++] = &hdr->buf_hdr;
- }
- }
+ if (odp_unlikely(num_seg > 1)) {
+ free_all_segments(pkt_hdr, num_seg);
+ num_freed++;
+ continue;
+ }
- pkt_hdr = ref_hdr;
- } while (pkt_hdr);
+ pkt_hdrs[i - num_freed] = pkt_hdr;
}
- if (nfree > 0)
- buffer_free_multi(buf_hdr, nfree);
+ if (odp_likely(num - num_freed))
+ packet_free_multi(pkt_hdrs, num - num_freed);
+}
+
+void odp_packet_free_multi(const odp_packet_t pkt[], int num)
+{
+ packet_free_multi_ev(pkt, num, _ODP_EV_PACKET_FREE_MULTI);
+}
+
+void odp_packet_free_sp(const odp_packet_t pkt[], int num)
+{
+ packet_free_multi_ev(pkt, num, _ODP_EV_PACKET_FREE_SP);
}
int odp_packet_reset(odp_packet_t pkt, uint32_t len)
{
odp_packet_hdr_t *const pkt_hdr = packet_hdr(pkt);
- pool_t *pool = pkt_hdr->buf_hdr.pool_ptr;
- int num = pkt_hdr->buf_hdr.segcount;
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
+ int num = pkt_hdr->seg_count;
+ int num_req;
- if (odp_unlikely(len > (pool->max_seg_len * num)))
+ if (odp_unlikely(len > (pool->seg_len * num)) || len == 0)
return -1;
- if (pkt_hdr->ref_hdr)
- packet_free(pkt_hdr->ref_hdr);
-
- reset_seg(pkt_hdr, 0, num);
+ /* Free possible extra segments */
+ num_req = num_segments(len, pool->seg_len);
+ if (odp_unlikely(num_req < num))
+ free_segments(pkt_hdr, num - num_req, 0, 0, 0);
+ reset_segments(pkt_hdr);
packet_init(pkt_hdr, len);
return 0;
}
-odp_packet_t odp_packet_from_event(odp_event_t ev)
+int odp_event_filter_packet(const odp_event_t event[],
+ odp_packet_t packet[],
+ odp_event_t remain[], int num)
{
- if (odp_unlikely(ev == ODP_EVENT_INVALID))
- return ODP_PACKET_INVALID;
-
- return (odp_packet_t)buf_to_packet_hdr((odp_buffer_t)ev);
-}
+ int i;
+ int num_pkt = 0;
+ int num_rem = 0;
-odp_event_t odp_packet_to_event(odp_packet_t pkt)
-{
- if (odp_unlikely(pkt == ODP_PACKET_INVALID))
- return ODP_EVENT_INVALID;
+ for (i = 0; i < num; i++) {
+ if (odp_event_type(event[i]) == ODP_EVENT_PACKET) {
+ packet[num_pkt] = odp_packet_from_event(event[i]);
+ num_pkt++;
+ } else {
+ remain[num_rem] = event[i];
+ num_rem++;
+ }
+ }
- return (odp_event_t)buffer_handle(packet_hdr(pkt));
+ return num_pkt;
}
/*
@@ -730,47 +789,14 @@ odp_event_t odp_packet_to_event(odp_packet_t pkt)
uint32_t odp_packet_buf_len(odp_packet_t pkt)
{
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t buf_len = 0;
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
- do {
- buf_len += pkt_hdr->buf_hdr.size * pkt_hdr->buf_hdr.segcount;
- pkt_hdr = pkt_hdr->ref_hdr;
- } while (pkt_hdr);
-
- return buf_len;
-}
-
-uint32_t odp_packet_unshared_len(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t pkt_len = 0, offset = 0;
-
- if (packet_ref_count(pkt_hdr) == 1)
- pkt_hdr->unshared_len = pkt_hdr->frame_len;
-
- do {
- if (packet_ref_count(pkt_hdr) > 1) {
- if (offset == 0)
- pkt_len += pkt_hdr->unshared_len;
- break;
- }
-
- pkt_len += pkt_hdr->frame_len - offset;
- offset = pkt_hdr->ref_offset;
-
- if (pkt_hdr->ref_hdr)
- offset += (pkt_hdr->ref_hdr->frame_len -
- pkt_hdr->ref_len);
-
- pkt_hdr = pkt_hdr->ref_hdr;
- } while (pkt_hdr);
-
- return pkt_len;
+ return pool->max_seg_len * pkt_hdr->seg_count;
}
void *odp_packet_tail(odp_packet_t pkt)
{
- odp_packet_hdr_t *pkt_hdr = packet_last_hdr(pkt, NULL);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
return packet_tail(pkt_hdr);
}
@@ -786,166 +812,6 @@ void *odp_packet_push_head(odp_packet_t pkt, uint32_t len)
return packet_data(pkt_hdr);
}
-static inline uint32_t pack_seg_head(odp_packet_hdr_t *pkt_hdr, int seg)
-{
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[seg].hdr;
- uint32_t len = pkt_hdr->buf_hdr.seg[seg].len;
- uint8_t *src = pkt_hdr->buf_hdr.seg[seg].data;
- uint8_t *dst = hdr->base_data;
-
- if (dst != src) {
- memmove(dst, src, len);
- pkt_hdr->buf_hdr.seg[seg].data = dst;
- }
-
- return len;
-}
-
-static inline uint32_t pack_seg_tail(odp_packet_hdr_t *pkt_hdr, int seg)
-{
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[seg].hdr;
- uint32_t len = pkt_hdr->buf_hdr.seg[seg].len;
- uint8_t *src = pkt_hdr->buf_hdr.seg[seg].data;
- uint8_t *dst = hdr->base_data + BASE_LEN - len;
-
- if (dst != src) {
- memmove(dst, src, len);
- pkt_hdr->buf_hdr.seg[seg].data = dst;
- }
-
- return len;
-}
-
-static inline uint32_t fill_seg_head(odp_packet_hdr_t *pkt_hdr, int dst_seg,
- int src_seg, uint32_t max_len)
-{
- uint32_t len = pkt_hdr->buf_hdr.seg[src_seg].len;
- uint8_t *src = pkt_hdr->buf_hdr.seg[src_seg].data;
- uint32_t offset = pkt_hdr->buf_hdr.seg[dst_seg].len;
- uint8_t *dst = pkt_hdr->buf_hdr.seg[dst_seg].data + offset;
-
- if (len > max_len)
- len = max_len;
-
- memmove(dst, src, len);
-
- pkt_hdr->buf_hdr.seg[dst_seg].len += len;
- pkt_hdr->buf_hdr.seg[src_seg].len -= len;
- pkt_hdr->buf_hdr.seg[src_seg].data += len;
-
- if (pkt_hdr->buf_hdr.seg[src_seg].len == 0) {
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[src_seg].hdr;
-
- pkt_hdr->buf_hdr.seg[src_seg].data = hdr->base_data;
- }
-
- return len;
-}
-
-static inline uint32_t fill_seg_tail(odp_packet_hdr_t *pkt_hdr, int dst_seg,
- int src_seg, uint32_t max_len)
-{
- uint32_t src_len = pkt_hdr->buf_hdr.seg[src_seg].len;
- uint8_t *src = pkt_hdr->buf_hdr.seg[src_seg].data;
- uint8_t *dst = pkt_hdr->buf_hdr.seg[dst_seg].data;
- uint32_t len = src_len;
-
- if (len > max_len)
- len = max_len;
-
- src += src_len - len;
- dst -= len;
-
- memmove(dst, src, len);
-
- pkt_hdr->buf_hdr.seg[dst_seg].data -= len;
- pkt_hdr->buf_hdr.seg[dst_seg].len += len;
- pkt_hdr->buf_hdr.seg[src_seg].len -= len;
-
- if (pkt_hdr->buf_hdr.seg[src_seg].len == 0) {
- odp_buffer_hdr_t *hdr = pkt_hdr->buf_hdr.seg[src_seg].hdr;
-
- pkt_hdr->buf_hdr.seg[src_seg].data = hdr->base_data;
- }
-
- return len;
-}
-
-static inline int move_data_to_head(odp_packet_hdr_t *pkt_hdr, int segs)
-{
- int dst_seg, src_seg;
- uint32_t len, free_len;
- uint32_t moved = 0;
-
- for (dst_seg = 0; dst_seg < segs; dst_seg++) {
- len = pack_seg_head(pkt_hdr, dst_seg);
- moved += len;
-
- if (len == BASE_LEN)
- continue;
-
- free_len = BASE_LEN - len;
-
- for (src_seg = dst_seg + 1; CONFIG_PACKET_MAX_SEGS > 1 &&
- src_seg < segs; src_seg++) {
- len = fill_seg_head(pkt_hdr, dst_seg, src_seg,
- free_len);
- moved += len;
-
- if (len == free_len) {
- /* dst seg is full */
- break;
- }
-
- /* src seg is empty */
- free_len -= len;
- }
-
- if (moved == pkt_hdr->frame_len)
- break;
- }
-
- /* last segment which have data */
- return dst_seg;
-}
-
-static inline int move_data_to_tail(odp_packet_hdr_t *pkt_hdr, int segs)
-{
- int dst_seg, src_seg;
- uint32_t len, free_len;
- uint32_t moved = 0;
-
- for (dst_seg = segs - 1; dst_seg >= 0; dst_seg--) {
- len = pack_seg_tail(pkt_hdr, dst_seg);
- moved += len;
-
- if (len == BASE_LEN)
- continue;
-
- free_len = BASE_LEN - len;
-
- for (src_seg = dst_seg - 1; src_seg >= 0; src_seg--) {
- len = fill_seg_tail(pkt_hdr, dst_seg, src_seg,
- free_len);
- moved += len;
-
- if (len == free_len) {
- /* dst seg is full */
- break;
- }
-
- /* src seg is empty */
- free_len -= len;
- }
-
- if (moved == pkt_hdr->frame_len)
- break;
- }
-
- /* first segment which have data */
- return dst_seg;
-}
-
int odp_packet_extend_head(odp_packet_t *pkt, uint32_t len,
void **data_ptr, uint32_t *seg_len)
{
@@ -955,111 +821,37 @@ int odp_packet_extend_head(odp_packet_t *pkt, uint32_t len,
int ret = 0;
if (len > headroom) {
- pool_t *pool = pkt_hdr->buf_hdr.pool_ptr;
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
int num;
- int segs;
+ void *ptr;
if (odp_unlikely((frame_len + len) > pool->max_len))
return -1;
- num = num_segments(len - headroom);
- segs = pkt_hdr->buf_hdr.segcount;
-
- if (odp_unlikely((segs + num) > CONFIG_PACKET_MAX_SEGS)) {
- /* Corner case: fail request if packet has
- * references since we cannot shuffle segments
- * since another thread may be accessing them
- * concurrently */
- if (packet_ref_count(pkt_hdr) > 1)
- return -1;
-
- /* Cannot directly add new segments */
- odp_packet_hdr_t *new_hdr;
- int new_segs = 0;
- int free_segs = 0;
- uint32_t offset;
-
- num = num_segments(frame_len + len);
-
- if (num > segs) {
- /* Allocate additional segments */
- new_segs = num - segs;
- new_hdr = alloc_segments(pool, new_segs);
-
- if (new_hdr == NULL)
- return -1;
-
- } else if (num < segs) {
- free_segs = segs - num;
- }
-
- /* Pack all data to packet tail */
- move_data_to_tail(pkt_hdr, segs);
- reset_seg(pkt_hdr, 0, segs);
-
- if (new_segs) {
- add_all_segs(new_hdr, pkt_hdr);
- packet_seg_copy_md(new_hdr, pkt_hdr);
- segs += new_segs;
-
- pkt_hdr = new_hdr;
- *pkt = packet_handle(pkt_hdr);
- } else if (CONFIG_PACKET_MAX_SEGS > 1 && free_segs) {
- new_hdr = pkt_hdr->buf_hdr.seg[free_segs].hdr;
- packet_seg_copy_md(new_hdr, pkt_hdr);
-
- /* Free extra segs */
- free_bufs(pkt_hdr, 0, free_segs);
-
- segs -= free_segs;
- pkt_hdr = new_hdr;
- *pkt = packet_handle(pkt_hdr);
- }
-
- frame_len += len;
- offset = (segs * BASE_LEN) - frame_len;
-
- pkt_hdr->buf_hdr.seg[0].data += offset;
- pkt_hdr->buf_hdr.seg[0].len -= offset;
-
- pkt_hdr->buf_hdr.segcount = segs;
- pkt_hdr->frame_len = frame_len;
- pkt_hdr->unshared_len = frame_len;
- pkt_hdr->headroom = offset + pool->headroom;
- pkt_hdr->tailroom = pool->tailroom;
-
- /* Data was moved */
- ret = 1;
- } else {
- void *ptr;
+ num = num_segments(len - headroom, pool->seg_len);
+ if (odp_unlikely(pkt_hdr->seg_count + num > PKT_MAX_SEGS))
+ return -1;
- push_head(pkt_hdr, headroom);
- ptr = add_segments(pkt_hdr, pool, len - headroom,
- num, 1);
+ push_head(pkt_hdr, headroom);
+ ptr = add_segments(pkt_hdr, pool, len - headroom, num, 1);
- if (ptr == NULL) {
- /* segment alloc failed, rollback changes */
- pull_head(pkt_hdr, headroom);
- return -1;
- }
-
- *pkt = packet_handle(ptr);
- pkt_hdr = ptr;
+ if (ptr == NULL) {
+ /* segment alloc failed, rollback changes */
+ pull_head(pkt_hdr, headroom);
+ return -1;
}
+
+ *pkt = packet_handle(ptr);
+ pkt_hdr = ptr;
} else {
push_head(pkt_hdr, len);
}
- if (data_ptr || seg_len) {
- uint32_t seg_ln = 0;
- void *data = packet_map(pkt_hdr, 0, &seg_ln, NULL);
-
- if (data_ptr)
- *data_ptr = data;
+ if (data_ptr)
+ *data_ptr = packet_data(pkt_hdr);
- if (seg_len)
- *seg_len = seg_ln;
- }
+ if (seg_len)
+ *seg_len = packet_first_seg_len(pkt_hdr);
return ret;
}
@@ -1068,11 +860,9 @@ void *odp_packet_pull_head(odp_packet_t pkt, uint32_t len)
{
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- if (len > pkt_hdr->frame_len)
+ if (len >= pkt_hdr->seg_len)
return NULL;
- ODP_ASSERT(len <= pkt_hdr->unshared_len);
-
pull_head(pkt_hdr, len);
return packet_data(pkt_hdr);
}
@@ -1080,33 +870,13 @@ void *odp_packet_pull_head(odp_packet_t pkt, uint32_t len)
int odp_packet_trunc_head(odp_packet_t *pkt, uint32_t len,
void **data_ptr, uint32_t *seg_len_out)
{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(*pkt), *nxt_hdr;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(*pkt);
uint32_t seg_len = packet_first_seg_len(pkt_hdr);
- int ret = 0;
- if (len > packet_len(pkt_hdr))
+ if (len >= pkt_hdr->frame_len)
return -1;
- ODP_ASSERT(len <= odp_packet_unshared_len(*pkt));
-
- /* Special processing for references */
- while (len >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
- nxt_hdr = pkt_hdr->ref_hdr;
- len -= pkt_hdr->frame_len;
- len += pkt_hdr->ref_offset +
- (nxt_hdr->frame_len - pkt_hdr->ref_len);
- pkt_hdr->ref_hdr = NULL;
- packet_free(pkt_hdr);
- pkt_hdr = nxt_hdr;
- seg_len = packet_first_seg_len(pkt_hdr);
- *pkt = packet_handle(pkt_hdr);
- ret = 1;
- }
-
- if (CONFIG_PACKET_MAX_SEGS == 1 ||
- len < seg_len ||
- pkt_hdr->buf_hdr.segcount == 1) {
+ if (len < seg_len) {
pull_head(pkt_hdr, len);
} else {
int num = 0;
@@ -1123,28 +893,24 @@ int odp_packet_trunc_head(odp_packet_t *pkt, uint32_t len,
*pkt = packet_handle(pkt_hdr);
}
- if (data_ptr || seg_len_out) {
- void *data_head = packet_map(pkt_hdr, 0, &seg_len, NULL);
-
- if (data_ptr)
- *data_ptr = data_head;
+ if (data_ptr)
+ *data_ptr = packet_data(pkt_hdr);
- if (seg_len_out)
- *seg_len_out = seg_len;
- }
+ if (seg_len_out)
+ *seg_len_out = packet_first_seg_len(pkt_hdr);
- return ret;
+ return 0;
}
void *odp_packet_push_tail(odp_packet_t pkt, uint32_t len)
{
- odp_packet_hdr_t *pkt_hdr = packet_last_hdr(pkt, NULL);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
void *old_tail;
if (len > pkt_hdr->tailroom)
return NULL;
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
+ _ODP_ASSERT(odp_packet_has_ref(pkt) == 0);
old_tail = packet_tail(pkt_hdr);
push_tail(pkt_hdr, len);
@@ -1155,86 +921,33 @@ void *odp_packet_push_tail(odp_packet_t pkt, uint32_t len)
int odp_packet_extend_tail(odp_packet_t *pkt, uint32_t len,
void **data_ptr, uint32_t *seg_len_out)
{
- odp_packet_hdr_t *pkt_hdr = packet_last_hdr(*pkt, NULL);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(*pkt);
uint32_t frame_len = pkt_hdr->frame_len;
uint32_t tailroom = pkt_hdr->tailroom;
uint32_t tail_off = frame_len;
int ret = 0;
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
+ _ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
if (len > tailroom) {
- pool_t *pool = pkt_hdr->buf_hdr.pool_ptr;
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
int num;
- int segs;
+ void *ptr;
if (odp_unlikely((frame_len + len) > pool->max_len))
return -1;
- num = num_segments(len - tailroom);
- segs = pkt_hdr->buf_hdr.segcount;
-
- if (odp_unlikely((segs + num) > CONFIG_PACKET_MAX_SEGS)) {
- /* Cannot directly add new segments */
- odp_packet_hdr_t *new_hdr;
- int new_segs = 0;
- int free_segs = 0;
- uint32_t offset;
-
- num = num_segments(frame_len + len);
-
- if (num > segs) {
- /* Allocate additional segments */
- new_segs = num - segs;
- new_hdr = alloc_segments(pool, new_segs);
-
- if (new_hdr == NULL)
- return -1;
-
- } else if (num < segs) {
- free_segs = segs - num;
- }
-
- /* Pack all data to packet head */
- move_data_to_head(pkt_hdr, segs);
- reset_seg(pkt_hdr, 0, segs);
-
- if (new_segs) {
- /* Add new segs */
- add_all_segs(pkt_hdr, new_hdr);
- segs += new_segs;
- } else if (free_segs) {
- /* Free extra segs */
- free_bufs(pkt_hdr, segs - free_segs, free_segs);
-
- segs -= free_segs;
- }
-
- frame_len += len;
- offset = (segs * BASE_LEN) - frame_len;
-
- pkt_hdr->buf_hdr.seg[segs - 1].len -= offset;
-
- pkt_hdr->buf_hdr.segcount = segs;
- pkt_hdr->frame_len = frame_len;
- pkt_hdr->headroom = pool->headroom;
- pkt_hdr->tailroom = offset + pool->tailroom;
-
- /* Data was moved */
- ret = 1;
- } else {
- void *ptr;
-
- push_tail(pkt_hdr, tailroom);
+ num = num_segments(len - tailroom, pool->seg_len);
+ if (odp_unlikely(pkt_hdr->seg_count + num > PKT_MAX_SEGS))
+ return -1;
- ptr = add_segments(pkt_hdr, pool, len - tailroom,
- num, 0);
+ push_tail(pkt_hdr, tailroom);
+ ptr = add_segments(pkt_hdr, pool, len - tailroom, num, 0);
- if (ptr == NULL) {
- /* segment alloc failed, rollback changes */
- pull_tail(pkt_hdr, tailroom);
- return -1;
- }
+ if (ptr == NULL) {
+ /* segment alloc failed, rollback changes */
+ pull_tail(pkt_hdr, tailroom);
+ return -1;
}
} else {
push_tail(pkt_hdr, len);
@@ -1249,11 +962,13 @@ int odp_packet_extend_tail(odp_packet_t *pkt, uint32_t len,
void *odp_packet_pull_tail(odp_packet_t pkt, uint32_t len)
{
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ odp_packet_hdr_t *last_seg = packet_last_seg(pkt_hdr);
- if (len > packet_last_seg_len(pkt_hdr))
+ _ODP_ASSERT(odp_packet_has_ref(pkt) == 0);
+
+ if (len >= last_seg->seg_len)
return NULL;
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
pull_tail(pkt_hdr, len);
return packet_tail(pkt_hdr);
@@ -1264,37 +979,25 @@ int odp_packet_trunc_tail(odp_packet_t *pkt, uint32_t len,
{
int last;
uint32_t seg_len;
- uint32_t offset;
- odp_packet_hdr_t *first_hdr = packet_hdr(*pkt);
- odp_packet_hdr_t *pkt_hdr, *prev_hdr;
+ odp_packet_hdr_t *last_seg;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(*pkt);
- if (len > packet_len(first_hdr))
+ if (len >= pkt_hdr->frame_len)
return -1;
- pkt_hdr = packet_last_hdr(*pkt, &offset);
+ _ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
- /* Special processing for references */
- while (len >= pkt_hdr->frame_len - offset && first_hdr->ref_hdr) {
- len -= (pkt_hdr->frame_len - offset);
- prev_hdr = packet_prev_hdr(first_hdr, pkt_hdr, &offset);
- ODP_ASSERT(packet_ref_count(prev_hdr) == 1);
- prev_hdr->ref_hdr = NULL;
- packet_free(pkt_hdr);
- pkt_hdr = prev_hdr;
- }
-
- ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
- last = packet_last_seg(pkt_hdr);
- seg_len = packet_seg_len(pkt_hdr, last);
+ last = pkt_hdr->seg_count - 1;
+ last_seg = packet_last_seg(pkt_hdr);
+ seg_len = last_seg->seg_len;
- if (CONFIG_PACKET_MAX_SEGS == 1 ||
- len < seg_len ||
- pkt_hdr->buf_hdr.segcount == 1) {
+ if (len < seg_len) {
pull_tail(pkt_hdr, len);
} else {
int num = 0;
uint32_t pull_len = 0;
+ /* Reverse order */
while (seg_len <= len) {
pull_len = len - seg_len;
num++;
@@ -1315,12 +1018,9 @@ int odp_packet_trunc_tail(odp_packet_t *pkt, uint32_t len,
void *odp_packet_offset(odp_packet_t pkt, uint32_t offset, uint32_t *len,
odp_packet_seg_t *seg)
{
- int seg_idx;
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- void *addr = packet_map(pkt_hdr, offset, len, &seg_idx);
- if (addr != NULL && seg != NULL)
- *seg = _odp_packet_seg_from_ndx(seg_idx);
+ void *addr = packet_map(pkt_hdr, offset, len, seg);
return addr;
}
@@ -1332,93 +1032,11 @@ void *odp_packet_offset(odp_packet_t pkt, uint32_t offset, uint32_t *len,
*
*/
-int odp_packet_input_index(odp_packet_t pkt)
-{
- return odp_pktio_index(packet_hdr(pkt)->input);
-}
-
-void odp_packet_user_ptr_set(odp_packet_t pkt, const void *ctx)
-{
- packet_hdr(pkt)->buf_hdr.buf_cctx = ctx;
-}
-
-void *odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (!packet_hdr_has_l2(pkt_hdr))
- return NULL;
- return packet_map(pkt_hdr, pkt_hdr->p.l2_offset, len, NULL);
-}
-
-uint32_t odp_packet_l2_offset(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (!packet_hdr_has_l2(pkt_hdr))
- return ODP_PACKET_OFFSET_INVALID;
- return pkt_hdr->p.l2_offset;
-}
-
-int odp_packet_l2_offset_set(odp_packet_t pkt, uint32_t offset)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (offset >= pkt_hdr->frame_len)
- return -1;
-
- packet_hdr_has_l2_set(pkt_hdr, 1);
- pkt_hdr->p.l2_offset = offset;
- return 0;
-}
-
-void *odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- return packet_map(pkt_hdr, pkt_hdr->p.l3_offset, len, NULL);
-}
-
-uint32_t odp_packet_l3_offset(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- return pkt_hdr->p.l3_offset;
-}
-
-int odp_packet_l3_offset_set(odp_packet_t pkt, uint32_t offset)
+uint16_t odp_packet_ones_comp(odp_packet_t pkt, odp_packet_data_range_t *range)
{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (offset >= pkt_hdr->frame_len)
- return -1;
-
- pkt_hdr->p.l3_offset = offset;
- return 0;
-}
-
-void *odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- return packet_map(pkt_hdr, pkt_hdr->p.l4_offset, len, NULL);
-}
-
-uint32_t odp_packet_l4_offset(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- return pkt_hdr->p.l4_offset;
-}
-
-int odp_packet_l4_offset_set(odp_packet_t pkt, uint32_t offset)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (offset >= pkt_hdr->frame_len)
- return -1;
-
- pkt_hdr->p.l4_offset = offset;
+ (void)pkt;
+ range->length = 0;
+ range->offset = 0;
return 0;
}
@@ -1426,56 +1044,7 @@ void odp_packet_flow_hash_set(odp_packet_t pkt, uint32_t flow_hash)
{
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- pkt_hdr->flow_hash = flow_hash;
- pkt_hdr->p.input_flags.flow_hash = 1;
-}
-
-void odp_packet_ts_set(odp_packet_t pkt, odp_time_t timestamp)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- pkt_hdr->timestamp = timestamp;
- pkt_hdr->p.input_flags.timestamp = 1;
-}
-
-int odp_packet_num_segs(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t segcount = 0, i;
- uint32_t seg_offset = 0, offset;
-
- do {
- segcount += pkt_hdr->buf_hdr.segcount - seg_offset;
- offset = pkt_hdr->ref_offset;
- pkt_hdr = pkt_hdr->ref_hdr;
- if (pkt_hdr) {
- for (i = 0, seg_offset = 0;
- i < pkt_hdr->buf_hdr.segcount;
- i++, seg_offset++) {
- if (offset < pkt_hdr->buf_hdr.seg[i].len)
- break;
- offset -= pkt_hdr->buf_hdr.seg[i].len;
- }
- }
- } while (pkt_hdr);
-
- return segcount;
-}
-
-odp_packet_seg_t odp_packet_last_seg(odp_packet_t pkt)
-{
- return _odp_packet_seg_from_ndx(odp_packet_num_segs(pkt) - 1);
-}
-
-odp_packet_seg_t odp_packet_next_seg(odp_packet_t pkt, odp_packet_seg_t seg)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (odp_unlikely(_odp_packet_seg_to_ndx(seg) >=
- packet_last_seg(pkt_hdr)))
- return ODP_PACKET_SEG_INVALID;
-
- return seg + 1;
+ packet_set_flow_hash(pkt_hdr, flow_hash);
}
/*
@@ -1485,56 +1054,9 @@ odp_packet_seg_t odp_packet_next_seg(odp_packet_t pkt, odp_packet_seg_t seg)
*
*/
-void *odp_packet_seg_data(odp_packet_t pkt, odp_packet_seg_t seg)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t seg_offset = 0, offset = 0, i;
- uint32_t seg_ndx = _odp_packet_seg_to_ndx(seg);
-
- while (seg_ndx >= pkt_hdr->buf_hdr.segcount - seg_offset &&
- pkt_hdr->ref_hdr) {
- seg_ndx -= (pkt_hdr->buf_hdr.segcount - seg_offset);
- offset = pkt_hdr->ref_offset;
- pkt_hdr = pkt_hdr->ref_hdr;
- for (i = 0, seg_offset = 0;
- i < pkt_hdr->buf_hdr.segcount;
- i++, seg_offset++) {
- if (offset < pkt_hdr->buf_hdr.seg[i].len)
- break;
- offset -= pkt_hdr->buf_hdr.seg[i].len;
- }
- }
-
- if (odp_unlikely(seg_ndx + seg_offset >= pkt_hdr->buf_hdr.segcount))
- return NULL;
-
- return packet_seg_data(pkt_hdr, seg_ndx + seg_offset) + offset;
-}
-
-uint32_t odp_packet_seg_data_len(odp_packet_t pkt, odp_packet_seg_t seg)
+odp_packet_seg_t odp_packet_last_seg(odp_packet_t pkt)
{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t seg_offset = 0, offset = 0, i;
- uint32_t seg_ndx = _odp_packet_seg_to_ndx(seg);
-
- while (seg_ndx >= pkt_hdr->buf_hdr.segcount - seg_offset &&
- pkt_hdr->ref_hdr) {
- seg_ndx -= (pkt_hdr->buf_hdr.segcount - seg_offset);
- offset = pkt_hdr->ref_offset;
- pkt_hdr = pkt_hdr->ref_hdr;
- for (i = 0, seg_offset = 0;
- i < pkt_hdr->buf_hdr.segcount;
- i++, seg_offset++) {
- if (offset < pkt_hdr->buf_hdr.seg[i].len)
- break;
- offset -= pkt_hdr->buf_hdr.seg[i].len;
- }
- }
-
- if (odp_unlikely(seg_ndx + seg_offset >= pkt_hdr->buf_hdr.segcount))
- return 0;
-
- return packet_seg_len(pkt_hdr, seg_ndx + seg_offset) - offset;
+ return (odp_packet_seg_t)packet_last_seg(packet_hdr(pkt));
}
/*
@@ -1549,15 +1071,13 @@ int odp_packet_add_data(odp_packet_t *pkt_ptr, uint32_t offset, uint32_t len)
odp_packet_t pkt = *pkt_ptr;
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
uint32_t pktlen = pkt_hdr->frame_len;
- pool_t *pool = pkt_hdr->buf_hdr.pool_ptr;
+ odp_pool_t pool = pkt_hdr->event_hdr.pool;
odp_packet_t newpkt;
if (offset > pktlen)
return -1;
- ODP_ASSERT(odp_packet_unshared_len(*pkt_ptr) >= offset);
-
- newpkt = odp_packet_alloc(pool->pool_hdl, pktlen + len);
+ newpkt = odp_packet_alloc(pool, pktlen + len);
if (newpkt == ODP_PACKET_INVALID)
return -1;
@@ -1569,7 +1089,7 @@ int odp_packet_add_data(odp_packet_t *pkt_ptr, uint32_t offset, uint32_t len)
return -1;
}
- _odp_packet_copy_md_to_packet(pkt, newpkt);
+ _odp_packet_copy_md(packet_hdr(newpkt), pkt_hdr, 0);
odp_packet_free(pkt);
*pkt_ptr = newpkt;
@@ -1580,16 +1100,14 @@ int odp_packet_rem_data(odp_packet_t *pkt_ptr, uint32_t offset, uint32_t len)
{
odp_packet_t pkt = *pkt_ptr;
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- uint32_t pktlen = packet_len(pkt_hdr);
- pool_t *pool = pkt_hdr->buf_hdr.pool_ptr;
+ uint32_t pktlen = pkt_hdr->frame_len;
+ odp_pool_t pool = pkt_hdr->event_hdr.pool;
odp_packet_t newpkt;
- if (offset + len > pktlen)
+ if (offset + len >= pktlen)
return -1;
- ODP_ASSERT(odp_packet_unshared_len(*pkt_ptr) >= offset + len);
-
- newpkt = odp_packet_alloc(pool->pool_hdl, pktlen - len);
+ newpkt = odp_packet_alloc(pool, pktlen - len);
if (newpkt == ODP_PACKET_INVALID)
return -1;
@@ -1601,7 +1119,7 @@ int odp_packet_rem_data(odp_packet_t *pkt_ptr, uint32_t offset, uint32_t len)
return -1;
}
- _odp_packet_copy_md_to_packet(pkt, newpkt);
+ _odp_packet_copy_md(packet_hdr(newpkt), pkt_hdr, 0);
odp_packet_free(pkt);
*pkt_ptr = newpkt;
@@ -1615,6 +1133,7 @@ int odp_packet_align(odp_packet_t *pkt, uint32_t offset, uint32_t len,
uint32_t shift;
uint32_t seglen = 0; /* GCC */
odp_packet_hdr_t *pkt_hdr = packet_hdr(*pkt);
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
void *addr = packet_map(pkt_hdr, offset, &seglen, NULL);
uint64_t uaddr = (uint64_t)(uintptr_t)addr;
uint64_t misalign;
@@ -1622,21 +1141,21 @@ int odp_packet_align(odp_packet_t *pkt, uint32_t offset, uint32_t len,
if (align > ODP_CACHE_LINE_SIZE)
return -1;
- ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
+ _ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
if (seglen >= len) {
misalign = align <= 1 ? 0 :
- ROUNDUP_ALIGN(uaddr, align) - uaddr;
+ _ODP_ROUNDUP_ALIGN(uaddr, align) - uaddr;
if (misalign == 0)
return 0;
shift = align - misalign;
} else {
- if (len > pkt_hdr->buf_hdr.size)
+ if (len > pool->max_seg_len)
return -1;
shift = len - seglen;
uaddr -= shift;
misalign = align <= 1 ? 0 :
- ROUNDUP_ALIGN(uaddr, align) - uaddr;
+ _ODP_ROUNDUP_ALIGN(uaddr, align) - uaddr;
if (misalign)
shift += align - misalign;
}
@@ -1646,7 +1165,7 @@ int odp_packet_align(odp_packet_t *pkt, uint32_t offset, uint32_t len,
return rc;
(void)odp_packet_move_data(*pkt, 0, shift,
- _odp_packet_len(*pkt) - shift);
+ odp_packet_len(*pkt) - shift);
(void)odp_packet_trunc_tail(pkt, shift, NULL, NULL);
return 1;
@@ -1656,20 +1175,20 @@ int odp_packet_concat(odp_packet_t *dst, odp_packet_t src)
{
odp_packet_hdr_t *dst_hdr = packet_hdr(*dst);
odp_packet_hdr_t *src_hdr = packet_hdr(src);
- int dst_segs = dst_hdr->buf_hdr.segcount;
- int src_segs = src_hdr->buf_hdr.segcount;
- pool_t *dst_pool = dst_hdr->buf_hdr.pool_ptr;
- pool_t *src_pool = src_hdr->buf_hdr.pool_ptr;
+ pool_t *dst_pool = _odp_pool_entry(dst_hdr->event_hdr.pool);
+ pool_t *src_pool = _odp_pool_entry(src_hdr->event_hdr.pool);
uint32_t dst_len = dst_hdr->frame_len;
uint32_t src_len = src_hdr->frame_len;
- ODP_ASSERT(packet_ref_count(dst_hdr) == 1);
+ _ODP_ASSERT(odp_packet_has_ref(*dst) == 0);
- /* Do a copy if resulting packet would be out of segments or packets
- * are from different pools or src is a reference. */
- if (odp_unlikely((dst_segs + src_segs) > CONFIG_PACKET_MAX_SEGS) ||
- odp_unlikely(dst_pool != src_pool) ||
- odp_unlikely(packet_ref_count(src_hdr) > 1)) {
+ if (odp_unlikely(dst_len + src_len > dst_pool->max_len)) {
+ _ODP_ERR("concat would result oversized packet\n");
+ return -1;
+ }
+
+ /* Do a copy if packets are from different pools. */
+ if (odp_unlikely(dst_pool != src_pool)) {
if (odp_packet_extend_tail(dst, src_len, NULL, NULL) >= 0) {
(void)odp_packet_copy_from_pkt(*dst, dst_len,
src, 0, src_len);
@@ -1682,11 +1201,14 @@ int odp_packet_concat(odp_packet_t *dst, odp_packet_t src)
return -1;
}
+ if (odp_unlikely(dst_hdr->seg_count + src_hdr->seg_count >
+ PKT_MAX_SEGS))
+ return -1;
+
add_all_segs(dst_hdr, src_hdr);
- dst_hdr->frame_len = dst_len + src_len;
- dst_hdr->unshared_len = dst_len + src_len;
- dst_hdr->tailroom = src_hdr->tailroom;
+ dst_hdr->frame_len = dst_len + src_len;
+ dst_hdr->tailroom = src_hdr->tailroom;
/* Data was not moved in memory */
return 0;
@@ -1694,12 +1216,13 @@ int odp_packet_concat(odp_packet_t *dst, odp_packet_t src)
int odp_packet_split(odp_packet_t *pkt, uint32_t len, odp_packet_t *tail)
{
- uint32_t pktlen = _odp_packet_len(*pkt);
+ uint32_t pktlen = odp_packet_len(*pkt);
if (len >= pktlen || tail == NULL)
return -1;
- ODP_ASSERT(odp_packet_unshared_len(*pkt) >= len);
+ _ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
+
*tail = odp_packet_copy_part(*pkt, len, pktlen - len,
odp_packet_pool(*pkt));
@@ -1710,109 +1233,6 @@ int odp_packet_split(odp_packet_t *pkt, uint32_t len, odp_packet_t *tail)
}
/*
- * References
- */
-
-static inline void packet_ref(odp_packet_hdr_t *pkt_hdr)
-{
- uint32_t i;
- odp_packet_hdr_t *hdr;
-
- do {
- for (i = 0; i < pkt_hdr->buf_hdr.segcount; i++) {
- hdr = pkt_hdr->buf_hdr.seg[i].hdr;
- packet_ref_inc(hdr);
- }
-
- pkt_hdr = pkt_hdr->ref_hdr;
- } while (pkt_hdr);
-}
-
-static inline odp_packet_t packet_splice(odp_packet_hdr_t *pkt_hdr,
- uint32_t offset,
- odp_packet_hdr_t *ref_hdr)
-{
- /* Catch attempted references to stale handles in debug builds */
- ODP_ASSERT(packet_ref_count(pkt_hdr) > 0);
-
- /* Splicing is from the last section of src pkt */
- while (ref_hdr->ref_hdr)
- ref_hdr = ref_hdr->ref_hdr;
-
- /* Find section where splice begins */
- while (offset >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
- offset -= (pkt_hdr->frame_len - pkt_hdr->ref_offset);
- offset += (pkt_hdr->ref_hdr->frame_len - pkt_hdr->ref_len);
- pkt_hdr = pkt_hdr->ref_hdr;
- }
-
- ref_hdr->ref_hdr = pkt_hdr;
- ref_hdr->ref_offset = offset;
- ref_hdr->ref_len = pkt_hdr->frame_len;
-
- if (packet_ref_count(pkt_hdr) == 1 || offset < pkt_hdr->unshared_len)
- pkt_hdr->unshared_len = offset;
-
- packet_ref(pkt_hdr);
- return packet_handle(ref_hdr);
-}
-
-odp_packet_t odp_packet_ref_static(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- packet_ref(pkt_hdr);
- pkt_hdr->unshared_len = 0;
- return pkt;
-}
-
-odp_packet_t odp_packet_ref(odp_packet_t pkt, uint32_t offset)
-{
- odp_packet_t hdr;
- odp_packet_hdr_t *pkt_hdr;
-
- if (pkt == ODP_PACKET_INVALID)
- return ODP_PACKET_INVALID;
-
- pkt_hdr = packet_hdr(pkt);
- if (offset >= packet_len(pkt_hdr))
- return ODP_PACKET_INVALID;
-
- hdr = odp_packet_alloc(odp_packet_pool(pkt), 0);
-
- if (hdr == ODP_PACKET_INVALID)
- return ODP_PACKET_INVALID;
-
- return packet_splice(pkt_hdr, offset, packet_hdr(hdr));
-}
-
-odp_packet_t odp_packet_ref_pkt(odp_packet_t pkt, uint32_t offset,
- odp_packet_t hdr)
-{
- odp_packet_hdr_t *pkt_hdr;
-
- if (pkt == ODP_PACKET_INVALID ||
- hdr == ODP_PACKET_INVALID ||
- pkt == hdr)
- return ODP_PACKET_INVALID;
-
- ODP_ASSERT(odp_packet_has_ref(hdr) == 0);
-
- pkt_hdr = packet_hdr(pkt);
- if (offset >= packet_len(pkt_hdr))
- return ODP_PACKET_INVALID;
-
- return packet_splice(pkt_hdr, offset, packet_hdr(hdr));
-}
-
-int odp_packet_has_ref(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- return pkt_hdr->ref_hdr != NULL || packet_ref_count(pkt_hdr) > 1;
-}
-
-/*
*
* Copy
* ********************************************************
@@ -1821,24 +1241,35 @@ int odp_packet_has_ref(odp_packet_t pkt)
odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
{
- uint32_t pktlen = odp_packet_len(pkt);
- odp_packet_t newpkt = odp_packet_alloc(pool, pktlen);
+ odp_packet_hdr_t *srchdr = packet_hdr(pkt);
+ uint32_t pktlen = srchdr->frame_len;
+ odp_packet_t newpkt;
+ int md_copy;
- if (newpkt != ODP_PACKET_INVALID) {
- if (_odp_packet_copy_md_to_packet(pkt, newpkt) ||
- odp_packet_copy_from_pkt(newpkt, 0, pkt, 0, pktlen)) {
- odp_packet_free(newpkt);
- newpkt = ODP_PACKET_INVALID;
- }
+ md_copy = _odp_packet_copy_md_possible(pool, odp_packet_pool(pkt));
+ if (odp_unlikely(md_copy < 0)) {
+ _ODP_ERR("Unable to copy packet metadata\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ newpkt = odp_packet_alloc(pool, pktlen);
+ if (odp_unlikely(newpkt == ODP_PACKET_INVALID))
+ return ODP_PACKET_INVALID;
+
+ if (odp_unlikely(odp_packet_copy_from_pkt(newpkt, 0, pkt, 0, pktlen))) {
+ odp_packet_free(newpkt);
+ return ODP_PACKET_INVALID;
}
+ _odp_packet_copy_md(packet_hdr(newpkt), srchdr, 1);
+
return newpkt;
}
odp_packet_t odp_packet_copy_part(odp_packet_t pkt, uint32_t offset,
uint32_t len, odp_pool_t pool)
{
- uint32_t pktlen = _odp_packet_len(pkt);
+ uint32_t pktlen = odp_packet_len(pkt);
odp_packet_t newpkt;
if (offset >= pktlen || offset + len > pktlen)
@@ -1851,56 +1282,6 @@ odp_packet_t odp_packet_copy_part(odp_packet_t pkt, uint32_t offset,
return newpkt;
}
-int odp_packet_copy_to_mem(odp_packet_t pkt, uint32_t offset,
- uint32_t len, void *dst)
-{
- void *mapaddr;
- uint32_t seglen = 0; /* GCC */
- uint32_t cpylen;
- uint8_t *dstaddr = (uint8_t *)dst;
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (offset + len > packet_len(pkt_hdr))
- return -1;
-
- while (len > 0) {
- mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
- cpylen = len > seglen ? seglen : len;
- memcpy(dstaddr, mapaddr, cpylen);
- offset += cpylen;
- dstaddr += cpylen;
- len -= cpylen;
- }
-
- return 0;
-}
-
-int odp_packet_copy_from_mem(odp_packet_t pkt, uint32_t offset,
- uint32_t len, const void *src)
-{
- void *mapaddr;
- uint32_t seglen = 0; /* GCC */
- uint32_t cpylen;
- const uint8_t *srcaddr = (const uint8_t *)src;
- odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
-
- if (offset + len > packet_len(pkt_hdr))
- return -1;
-
- ODP_ASSERT(odp_packet_unshared_len(pkt) >= offset + len);
-
- while (len > 0) {
- mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
- cpylen = len > seglen ? seglen : len;
- memcpy(mapaddr, srcaddr, cpylen);
- offset += cpylen;
- srcaddr += cpylen;
- len -= cpylen;
- }
-
- return 0;
-}
-
int odp_packet_copy_from_pkt(odp_packet_t dst, uint32_t dst_offset,
odp_packet_t src, uint32_t src_offset,
uint32_t len)
@@ -1914,12 +1295,10 @@ int odp_packet_copy_from_pkt(odp_packet_t dst, uint32_t dst_offset,
uint32_t src_seglen = 0; /* GCC */
int overlap;
- if (dst_offset + len > packet_len(dst_hdr) ||
- src_offset + len > packet_len(src_hdr))
+ if (dst_offset + len > dst_hdr->frame_len ||
+ src_offset + len > src_hdr->frame_len)
return -1;
- ODP_ASSERT(odp_packet_unshared_len(dst) >= dst_offset + len);
-
overlap = (dst_hdr == src_hdr &&
((dst_offset <= src_offset &&
dst_offset + len >= src_offset) ||
@@ -2003,7 +1382,7 @@ int _odp_packet_cmp_data(odp_packet_t pkt, uint32_t offset,
int ret;
odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- ODP_ASSERT(offset + len <= pkt_hdr->frame_len);
+ _ODP_ASSERT(offset + len <= pkt_hdr->frame_len);
while (len > 0) {
mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
@@ -2025,69 +1404,210 @@ int _odp_packet_cmp_data(odp_packet_t pkt, uint32_t offset,
* ********************************************************
*
*/
+static int packet_print_input_flags(odp_packet_hdr_t *hdr, char *str, int max)
+{
+ int len = 0;
+
+ if (hdr->p.input_flags.l2)
+ len += _odp_snprint(&str[len], max - len, "l2 ");
+ if (hdr->p.input_flags.l3)
+ len += _odp_snprint(&str[len], max - len, "l3 ");
+ if (hdr->p.input_flags.l4)
+ len += _odp_snprint(&str[len], max - len, "l4 ");
+ if (hdr->p.input_flags.eth)
+ len += _odp_snprint(&str[len], max - len, "eth ");
+ if (hdr->p.input_flags.vlan)
+ len += _odp_snprint(&str[len], max - len, "vlan ");
+ if (hdr->p.input_flags.arp)
+ len += _odp_snprint(&str[len], max - len, "arp ");
+ if (hdr->p.input_flags.ipv4)
+ len += _odp_snprint(&str[len], max - len, "ipv4 ");
+ if (hdr->p.input_flags.ipv6)
+ len += _odp_snprint(&str[len], max - len, "ipv6 ");
+ if (hdr->p.input_flags.ipsec)
+ len += _odp_snprint(&str[len], max - len, "ipsec ");
+ if (hdr->p.input_flags.udp)
+ len += _odp_snprint(&str[len], max - len, "udp ");
+ if (hdr->p.input_flags.tcp)
+ len += _odp_snprint(&str[len], max - len, "tcp ");
+ if (hdr->p.input_flags.sctp)
+ len += _odp_snprint(&str[len], max - len, "sctp ");
+ if (hdr->p.input_flags.icmp)
+ len += _odp_snprint(&str[len], max - len, "icmp ");
+
+ return len;
+}
void odp_packet_print(odp_packet_t pkt)
{
odp_packet_seg_t seg;
- int max_len = 512;
+ int max_len = 4096;
char str[max_len];
int len = 0;
int n = max_len - 1;
odp_packet_hdr_t *hdr = packet_hdr(pkt);
- odp_buffer_t buf = packet_to_buffer(pkt);
-
- len += snprintf(&str[len], n - len, "Packet ");
- len += odp_buffer_snprint(&str[len], n - len, buf);
- len += snprintf(&str[len], n - len, " input_flags 0x%" PRIx64 "\n",
- hdr->p.input_flags.all);
- len += snprintf(&str[len], n - len, " error_flags 0x%" PRIx32 "\n",
- hdr->p.error_flags.all);
- len += snprintf(&str[len], n - len,
- " output_flags 0x%" PRIx32 "\n",
- hdr->p.output_flags.all);
- len += snprintf(&str[len], n - len,
- " l2_offset %" PRIu32 "\n", hdr->p.l2_offset);
- len += snprintf(&str[len], n - len,
- " l3_offset %" PRIu32 "\n", hdr->p.l3_offset);
- len += snprintf(&str[len], n - len,
- " l4_offset %" PRIu32 "\n", hdr->p.l4_offset);
- len += snprintf(&str[len], n - len,
- " frame_len %" PRIu32 "\n", packet_len(hdr));
- len += snprintf(&str[len], n - len,
- " input %" PRIu64 "\n",
- odp_pktio_to_u64(hdr->input));
- len += snprintf(&str[len], n - len,
- " headroom %" PRIu32 "\n",
- odp_packet_headroom(pkt));
- len += snprintf(&str[len], n - len,
- " tailroom %" PRIu32 "\n",
- odp_packet_tailroom(pkt));
- len += snprintf(&str[len], n - len,
- " num_segs %i\n", odp_packet_num_segs(pkt));
+
+ len += _odp_snprint(&str[len], n - len, "Packet info\n");
+ len += _odp_snprint(&str[len], n - len, "-----------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_packet_to_u64(pkt));
+ len += _odp_snprint(&str[len], n - len, " pool index %u\n", hdr->event_hdr.index.pool);
+ len += _odp_snprint(&str[len], n - len, " buf index %u\n",
+ hdr->event_hdr.index.event);
+ len += _odp_snprint(&str[len], n - len, " ev subtype %i\n", hdr->event_hdr.subtype);
+ len += _odp_snprint(&str[len], n - len, " input_flags 0x%" PRIx64 "\n",
+ hdr->p.input_flags.all);
+ if (hdr->p.input_flags.all) {
+ len += _odp_snprint(&str[len], n - len, " ");
+ len += packet_print_input_flags(hdr, &str[len], n - len);
+ len += _odp_snprint(&str[len], n - len, "\n");
+ }
+ len += _odp_snprint(&str[len], n - len,
+ " flags 0x%" PRIx32 "\n", hdr->p.flags.all_flags);
+ len += _odp_snprint(&str[len], n - len,
+ " cls_mark %" PRIu64 "\n", odp_packet_cls_mark(pkt));
+ len += _odp_snprint(&str[len], n - len,
+ " user ptr %p\n", hdr->user_ptr);
+ len += _odp_snprint(&str[len], n - len,
+ " user area %p\n", hdr->uarea_addr);
+ len += _odp_snprint(&str[len], n - len,
+ " l2_offset %" PRIu32 "\n", hdr->p.l2_offset);
+ len += _odp_snprint(&str[len], n - len,
+ " l3_offset %" PRIu32 "\n", hdr->p.l3_offset);
+ len += _odp_snprint(&str[len], n - len,
+ " l4_offset %" PRIu32 "\n", hdr->p.l4_offset);
+ len += _odp_snprint(&str[len], n - len,
+ " frame_len %" PRIu32 "\n", hdr->frame_len);
+ len += _odp_snprint(&str[len], n - len,
+ " input %" PRIu64 "\n", odp_pktio_to_u64(hdr->input));
+ len += _odp_snprint(&str[len], n - len,
+ " headroom %" PRIu32 "\n", odp_packet_headroom(pkt));
+ len += _odp_snprint(&str[len], n - len,
+ " tailroom %" PRIu32 "\n", odp_packet_tailroom(pkt));
+ len += _odp_snprint(&str[len], n - len,
+ " num_segs %i\n", odp_packet_num_segs(pkt));
seg = odp_packet_first_seg(pkt);
- while (seg != ODP_PACKET_SEG_INVALID) {
- len += snprintf(&str[len], n - len,
- " seg_len %" PRIu32 "\n",
- odp_packet_seg_data_len(pkt, seg));
+ for (int seg_idx = 0; seg != ODP_PACKET_SEG_INVALID; seg_idx++) {
+ odp_packet_hdr_t *seg_hdr = packet_seg_to_hdr(seg);
+ char seg_str[max_len];
+ int str_len;
+
+ str_len = _odp_snprint(&seg_str[0], max_len,
+ " [%d] seg_len %-4" PRIu32 " seg_data %p ref_cnt %u\n",
+ seg_idx, odp_packet_seg_data_len(pkt, seg),
+ odp_packet_seg_data(pkt, seg), segment_ref(seg_hdr));
+
+ /* Prevent print buffer overflow */
+ if (n - len - str_len < 10) {
+ len += _odp_snprint(&str[len], n - len, " ...\n");
+ break;
+ }
+ len += _odp_snprint(&str[len], n - len, "%s", seg_str);
seg = odp_packet_next_seg(pkt, seg);
}
- str[len] = '\0';
+ _ODP_PRINT("%s\n", str);
+}
- ODP_PRINT("\n%s\n", str);
+void odp_packet_print_data(odp_packet_t pkt, uint32_t offset,
+ uint32_t byte_len)
+{
+ odp_packet_hdr_t *hdr = packet_hdr(pkt);
+ uint32_t bytes_per_row = 16;
+ int num_rows = (byte_len + bytes_per_row - 1) / bytes_per_row;
+ int max_len = 256 + (3 * byte_len) + (3 * num_rows);
+ char str[max_len];
+ int len = 0;
+ int n = max_len - 1;
+ uint32_t data_len = odp_packet_len(pkt);
+ pool_t *pool = _odp_pool_entry(hdr->event_hdr.pool);
+
+ len += _odp_snprint(&str[len], n - len, "Packet data\n");
+ len += _odp_snprint(&str[len], n - len, "-----------\n");
+ len += _odp_snprint(&str[len], n - len,
+ " handle 0x%" PRIx64 "\n", odp_packet_to_u64(pkt));
+ len += _odp_snprint(&str[len], n - len,
+ " pool index %" PRIu32 "\n", pool->pool_idx);
+ len += _odp_snprint(&str[len], n - len,
+ " buf index %" PRIu32 "\n", hdr->event_hdr.index.event);
+ len += _odp_snprint(&str[len], n - len,
+ " seg_count %" PRIu16 "\n", hdr->seg_count);
+ len += _odp_snprint(&str[len], n - len,
+ " data len %" PRIu32 "\n", data_len);
+ len += _odp_snprint(&str[len], n - len,
+ " data ptr %p\n", odp_packet_data(pkt));
+ len += _odp_snprint(&str[len], n - len,
+ " print offset %" PRIu32 "\n", offset);
+ len += _odp_snprint(&str[len], n - len,
+ " print length %" PRIu32 "\n", byte_len);
+
+ if (offset + byte_len > data_len) {
+ len += _odp_snprint(&str[len], n - len, " BAD OFFSET OR LEN\n");
+ _ODP_PRINT("%s\n", str);
+ return;
+ }
+
+ while (byte_len) {
+ uint32_t copy_len;
+ uint8_t data[bytes_per_row];
+ uint32_t i;
+
+ if (byte_len > bytes_per_row)
+ copy_len = bytes_per_row;
+ else
+ copy_len = byte_len;
+
+ odp_packet_copy_to_mem(pkt, offset, copy_len, data);
+
+ len += _odp_snprint(&str[len], n - len, " ");
+
+ for (i = 0; i < copy_len; i++)
+ len += _odp_snprint(&str[len], n - len, " %02x", data[i]);
+
+ len += _odp_snprint(&str[len], n - len, "\n");
+
+ byte_len -= copy_len;
+ offset += copy_len;
+ }
+
+ _ODP_PRINT("%s\n", str);
}
int odp_packet_is_valid(odp_packet_t pkt)
{
- if (odp_buffer_is_valid(packet_to_buffer(pkt)) == 0)
+ odp_event_t ev;
+
+ if (pkt == ODP_PACKET_INVALID)
+ return 0;
+
+ ev = odp_packet_to_event(pkt);
+
+ if (_odp_event_is_valid(ev) == 0)
+ return 0;
+
+ if (odp_event_type(ev) != ODP_EVENT_PACKET)
return 0;
- if (odp_event_type(odp_packet_to_event(pkt)) != ODP_EVENT_PACKET)
+ if (odp_unlikely(_odp_packet_validate(pkt, _ODP_EV_PACKET_IS_VALID)))
return 0;
+ switch (odp_event_subtype(ev)) {
+ case ODP_EVENT_PACKET_BASIC:
+ /* Fall through */
+ case ODP_EVENT_PACKET_COMP:
+ /* Fall through */
+ case ODP_EVENT_PACKET_CRYPTO:
+ /* Fall through */
+ case ODP_EVENT_PACKET_IPSEC:
+ /* Fall through */
+ break;
+ default:
+ return 0;
+ }
+
return 1;
}
@@ -2098,365 +1618,805 @@ int odp_packet_is_valid(odp_packet_t pkt)
*
*/
-int _odp_packet_copy_md_to_packet(odp_packet_t srcpkt, odp_packet_t dstpkt)
+static uint64_t packet_sum_partial(odp_packet_hdr_t *pkt_hdr,
+ uint32_t l3_offset,
+ uint32_t offset,
+ uint32_t len)
{
- odp_packet_hdr_t *srchdr = packet_hdr(srcpkt);
- odp_packet_hdr_t *dsthdr = packet_hdr(dstpkt);
+ uint64_t sum = 0;
- dsthdr->input = srchdr->input;
- dsthdr->dst_queue = srchdr->dst_queue;
- dsthdr->buf_hdr.buf_u64 = srchdr->buf_hdr.buf_u64;
- if (dsthdr->buf_hdr.uarea_addr != NULL &&
- srchdr->buf_hdr.uarea_addr != NULL)
- memcpy(dsthdr->buf_hdr.uarea_addr,
- srchdr->buf_hdr.uarea_addr,
- dsthdr->buf_hdr.uarea_size <=
- srchdr->buf_hdr.uarea_size ?
- dsthdr->buf_hdr.uarea_size :
- srchdr->buf_hdr.uarea_size);
+ if (offset + len > pkt_hdr->frame_len)
+ return 0;
+
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
- copy_packet_parser_metadata(srchdr, dsthdr);
+ if (seglen > len)
+ seglen = len;
- /* Metadata copied, but return indication of whether the packet
- * user area was truncated in the process. Note this can only
- * happen when copying between different pools.
- */
- return dsthdr->buf_hdr.uarea_size < srchdr->buf_hdr.uarea_size;
+ sum += chksum_partial(mapaddr, seglen, offset - l3_offset);
+ len -= seglen;
+ offset += seglen;
+ }
+
+ return sum;
}
-/**
- * Parser helper function for IPv4
- */
-static inline uint8_t parse_ipv4(packet_parser_t *prs, const uint8_t **parseptr,
- uint32_t *offset, uint32_t frame_len)
-{
- const _odp_ipv4hdr_t *ipv4 = (const _odp_ipv4hdr_t *)*parseptr;
- uint8_t ver = _ODP_IPV4HDR_VER(ipv4->ver_ihl);
- uint8_t ihl = _ODP_IPV4HDR_IHL(ipv4->ver_ihl);
- uint16_t frag_offset;
- uint32_t dstaddr = odp_be_to_cpu_32(ipv4->dst_addr);
- uint32_t l3_len = odp_be_to_cpu_16(ipv4->tot_len);
-
- if (odp_unlikely(ihl < _ODP_IPV4HDR_IHL_MIN) ||
- odp_unlikely(ver != 4) ||
- (l3_len > frame_len - *offset)) {
- prs->error_flags.ip_err = 1;
- return 0;
+static inline uint16_t packet_sum(odp_packet_hdr_t *pkt_hdr,
+ uint32_t l3_offset,
+ uint32_t offset,
+ uint32_t len,
+ uint64_t sum)
+{
+ sum += packet_sum_partial(pkt_hdr, l3_offset, offset, len);
+ return chksum_finalize(sum);
+}
+
+static uint32_t packet_sum_crc32c(odp_packet_hdr_t *pkt_hdr,
+ uint32_t offset,
+ uint32_t len,
+ uint32_t init_val)
+{
+ uint32_t sum = init_val;
+
+ if (offset + len > pkt_hdr->frame_len)
+ return sum;
+
+ while (len > 0) {
+ uint32_t seglen = 0; /* GCC */
+ void *mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
+
+ if (seglen > len)
+ seglen = len;
+
+ sum = odp_hash_crc32c(mapaddr, seglen, sum);
+ len -= seglen;
+ offset += seglen;
}
- *offset += ihl * 4;
- *parseptr += ihl * 4;
+ return sum;
+}
- if (odp_unlikely(ihl > _ODP_IPV4HDR_IHL_MIN))
- prs->input_flags.ipopt = 1;
+static inline int packet_ipv4_chksum(odp_packet_t pkt,
+ uint32_t offset,
+ _odp_ipv4hdr_t *ip,
+ odp_u16sum_t *chksum)
+{
+ unsigned int nleft = _ODP_IPV4HDR_IHL(ip->ver_ihl) * 4;
+ uint16_t buf[nleft / 2];
+ int res;
- /* A packet is a fragment if:
- * "more fragments" flag is set (all fragments except the last)
- * OR
- * "fragment offset" field is nonzero (all fragments except the first)
- */
- frag_offset = odp_be_to_cpu_16(ipv4->frag_offset);
- if (odp_unlikely(_ODP_IPV4HDR_IS_FRAGMENT(frag_offset)))
- prs->input_flags.ipfrag = 1;
+ if (odp_unlikely(nleft < sizeof(*ip)))
+ return -1;
+ ip->chksum = 0;
+ memcpy(buf, ip, sizeof(*ip));
+ res = odp_packet_copy_to_mem(pkt, offset + sizeof(*ip),
+ nleft - sizeof(*ip),
+ buf + sizeof(*ip) / 2);
+ if (odp_unlikely(res < 0))
+ return res;
- /* Handle IPv4 broadcast / multicast */
- prs->input_flags.ip_bcast = (dstaddr == 0xffffffff);
- prs->input_flags.ip_mcast = (dstaddr >> 28) == 0xd;
+ *chksum = ~chksum_finalize(chksum_partial(buf, nleft, 0));
- return ipv4->proto;
+ return 0;
}
+#define _ODP_IPV4HDR_CSUM_OFFSET ODP_OFFSETOF(_odp_ipv4hdr_t, chksum)
+#define _ODP_IPV4ADDR_OFFSSET ODP_OFFSETOF(_odp_ipv4hdr_t, src_addr)
+#define _ODP_IPV6ADDR_OFFSSET ODP_OFFSETOF(_odp_ipv6hdr_t, src_addr)
+#define _ODP_IPV4HDR_CSUM_OFFSET ODP_OFFSETOF(_odp_ipv4hdr_t, chksum)
+#define _ODP_UDP_LEN_OFFSET ODP_OFFSETOF(_odp_udphdr_t, length)
+#define _ODP_UDP_CSUM_OFFSET ODP_OFFSETOF(_odp_udphdr_t, chksum)
+
/**
- * Parser helper function for IPv6
+ * Calculate and fill in IPv4 checksum
+ *
+ * @param pkt ODP packet
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-static inline uint8_t parse_ipv6(packet_parser_t *prs, const uint8_t **parseptr,
- uint32_t *offset, uint32_t frame_len,
- uint32_t seg_len)
-{
- const _odp_ipv6hdr_t *ipv6 = (const _odp_ipv6hdr_t *)*parseptr;
- const _odp_ipv6hdr_ext_t *ipv6ext;
- uint32_t dstaddr0 = odp_be_to_cpu_32(ipv6->dst_addr.u8[0]);
- uint32_t l3_len = odp_be_to_cpu_16(ipv6->payload_len) +
- _ODP_IPV6HDR_LEN;
-
- /* Basic sanity checks on IPv6 header */
- if ((odp_be_to_cpu_32(ipv6->ver_tc_flow) >> 28) != 6 ||
- l3_len > frame_len - *offset) {
- prs->error_flags.ip_err = 1;
- return 0;
- }
+int _odp_packet_ipv4_chksum_insert(odp_packet_t pkt)
+{
+ uint32_t offset;
+ _odp_ipv4hdr_t ip;
+ odp_u16sum_t chksum;
+ int res;
- /* IPv6 broadcast / multicast flags */
- prs->input_flags.ip_mcast = (dstaddr0 & 0xff000000) == 0xff000000;
- prs->input_flags.ip_bcast = 0;
+ offset = odp_packet_l3_offset(pkt);
+ if (offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
- /* Skip past IPv6 header */
- *offset += sizeof(_odp_ipv6hdr_t);
- *parseptr += sizeof(_odp_ipv6hdr_t);
+ res = odp_packet_copy_to_mem(pkt, offset, sizeof(ip), &ip);
+ if (odp_unlikely(res < 0))
+ return res;
- /* Skip past any IPv6 extension headers */
- if (ipv6->next_hdr == _ODP_IPPROTO_HOPOPTS ||
- ipv6->next_hdr == _ODP_IPPROTO_ROUTE) {
- prs->input_flags.ipopt = 1;
+ res = packet_ipv4_chksum(pkt, offset, &ip, &chksum);
+ if (odp_unlikely(res < 0))
+ return res;
- do {
- ipv6ext = (const _odp_ipv6hdr_ext_t *)*parseptr;
- uint16_t extlen = 8 + ipv6ext->ext_len * 8;
+ return odp_packet_copy_from_mem(pkt,
+ offset + _ODP_IPV4HDR_CSUM_OFFSET,
+ 2, &chksum);
+}
- *offset += extlen;
- *parseptr += extlen;
- } while ((ipv6ext->next_hdr == _ODP_IPPROTO_HOPOPTS ||
- ipv6ext->next_hdr == _ODP_IPPROTO_ROUTE) &&
- *offset < seg_len);
+static int _odp_packet_tcp_udp_chksum_insert(odp_packet_t pkt, uint16_t proto)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ uint32_t zero = 0;
+ uint64_t sum;
+ uint16_t l3_ver;
+ uint16_t chksum;
+ uint32_t chksum_offset;
- if (*offset >= prs->l3_offset +
- odp_be_to_cpu_16(ipv6->payload_len)) {
- prs->error_flags.ip_err = 1;
- return 0;
- }
+ if (pkt_hdr->p.l3_offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
+ if (pkt_hdr->p.l4_offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
- if (ipv6ext->next_hdr == _ODP_IPPROTO_FRAG)
- prs->input_flags.ipfrag = 1;
+ odp_packet_copy_to_mem(pkt, pkt_hdr->p.l3_offset, 2, &l3_ver);
- return ipv6ext->next_hdr;
- }
+ if (_ODP_IPV4HDR_VER(l3_ver) == _ODP_IPV4)
+ sum = packet_sum_partial(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l3_offset +
+ _ODP_IPV4ADDR_OFFSSET,
+ 2 * _ODP_IPV4ADDR_LEN);
+ else
+ sum = packet_sum_partial(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l3_offset +
+ _ODP_IPV6ADDR_OFFSSET,
+ 2 * _ODP_IPV6ADDR_LEN);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+ sum += proto;
+#else
+ sum += proto << 8;
+#endif
- if (odp_unlikely(ipv6->next_hdr == _ODP_IPPROTO_FRAG)) {
- prs->input_flags.ipopt = 1;
- prs->input_flags.ipfrag = 1;
+ if (proto == _ODP_IPPROTO_TCP) {
+ sum += odp_cpu_to_be_16(pkt_hdr->frame_len -
+ pkt_hdr->p.l4_offset);
+ chksum_offset = pkt_hdr->p.l4_offset + _ODP_UDP_CSUM_OFFSET;
+ } else {
+ sum += packet_sum_partial(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l4_offset +
+ _ODP_UDP_LEN_OFFSET,
+ 2);
+ chksum_offset = pkt_hdr->p.l4_offset + _ODP_UDP_CSUM_OFFSET;
}
+ odp_packet_copy_from_mem(pkt, chksum_offset, 2, &zero);
+
+ sum += packet_sum_partial(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l4_offset,
+ pkt_hdr->frame_len -
+ pkt_hdr->p.l4_offset);
+
+ chksum = ~chksum_finalize(sum);
- return ipv6->next_hdr;
+ if (proto == _ODP_IPPROTO_UDP && chksum == 0)
+ chksum = 0xffff;
+
+ return odp_packet_copy_from_mem(pkt,
+ chksum_offset,
+ 2, &chksum);
}
/**
- * Parser helper function for TCP
+ * Calculate and fill in TCP checksum
+ *
+ * @param pkt ODP packet
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-static inline void parse_tcp(packet_parser_t *prs,
- const uint8_t **parseptr, uint32_t *offset)
+int _odp_packet_tcp_chksum_insert(odp_packet_t pkt)
{
- const _odp_tcphdr_t *tcp = (const _odp_tcphdr_t *)*parseptr;
-
- if (tcp->hl < sizeof(_odp_tcphdr_t) / sizeof(uint32_t))
- prs->error_flags.tcp_err = 1;
- else if ((uint32_t)tcp->hl * 4 > sizeof(_odp_tcphdr_t))
- prs->input_flags.tcpopt = 1;
-
- if (offset)
- *offset += (uint32_t)tcp->hl * 4;
- *parseptr += (uint32_t)tcp->hl * 4;
+ return _odp_packet_tcp_udp_chksum_insert(pkt, _ODP_IPPROTO_TCP);
}
/**
- * Parser helper function for UDP
+ * Calculate and fill in UDP checksum
+ *
+ * @param pkt ODP packet
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-static inline void parse_udp(packet_parser_t *prs,
- const uint8_t **parseptr, uint32_t *offset)
+int _odp_packet_udp_chksum_insert(odp_packet_t pkt)
{
- const _odp_udphdr_t *udp = (const _odp_udphdr_t *)*parseptr;
- uint32_t udplen = odp_be_to_cpu_16(udp->length);
-
- if (odp_unlikely(udplen < sizeof(_odp_udphdr_t)))
- prs->error_flags.udp_err = 1;
-
- if (offset)
- *offset += sizeof(_odp_udphdr_t);
- *parseptr += sizeof(_odp_udphdr_t);
+ return _odp_packet_tcp_udp_chksum_insert(pkt, _ODP_IPPROTO_UDP);
}
/**
- * Parse common packet headers up to given layer
+ * Calculate and fill in SCTP checksum
*
- * The function expects at least PACKET_PARSE_SEG_LEN bytes of data to be
- * available from the ptr.
+ * @param pkt ODP packet
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
*/
-int packet_parse_common(packet_parser_t *prs, const uint8_t *ptr,
- uint32_t frame_len, uint32_t seg_len,
- odp_pktio_parser_layer_t layer)
+int _odp_packet_sctp_chksum_insert(odp_packet_t pkt)
{
- uint32_t offset;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ uint32_t sum;
+
+ if (pkt_hdr->p.l4_offset == ODP_PACKET_OFFSET_INVALID)
+ return -1;
+
+ sum = 0;
+ odp_packet_copy_from_mem(pkt, pkt_hdr->p.l4_offset + 8, 4, &sum);
+ sum = ~packet_sum_crc32c(pkt_hdr, pkt_hdr->p.l4_offset,
+ pkt_hdr->frame_len - pkt_hdr->p.l4_offset,
+ ~0);
+ return odp_packet_copy_from_mem(pkt, pkt_hdr->p.l4_offset + 8, 4, &sum);
+}
+
+int _odp_packet_l4_chksum(odp_packet_hdr_t *pkt_hdr,
+ odp_pktin_config_opt_t opt, uint64_t l4_part_sum)
+{
+ /* UDP chksum == 0 case is covered in parse_udp() */
+ if (opt.bit.udp_chksum &&
+ pkt_hdr->p.input_flags.udp &&
+ !pkt_hdr->p.input_flags.ipfrag &&
+ !pkt_hdr->p.input_flags.udp_chksum_zero) {
+ uint16_t sum = ~packet_sum(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l4_offset,
+ pkt_hdr->frame_len -
+ pkt_hdr->p.l4_offset,
+ l4_part_sum);
+
+ pkt_hdr->p.input_flags.l4_chksum_done = 1;
+ if (sum != 0) {
+ pkt_hdr->p.flags.l4_chksum_err = 1;
+ pkt_hdr->p.flags.udp_err = 1;
+ _ODP_DBG("UDP chksum fail (%x)!\n", sum);
+ if (opt.bit.drop_udp_err)
+ return -1;
+ }
+ }
+
+ if (opt.bit.tcp_chksum &&
+ pkt_hdr->p.input_flags.tcp &&
+ !pkt_hdr->p.input_flags.ipfrag) {
+ uint16_t sum = ~packet_sum(pkt_hdr,
+ pkt_hdr->p.l3_offset,
+ pkt_hdr->p.l4_offset,
+ pkt_hdr->frame_len -
+ pkt_hdr->p.l4_offset,
+ l4_part_sum);
+
+ pkt_hdr->p.input_flags.l4_chksum_done = 1;
+ if (sum != 0) {
+ pkt_hdr->p.flags.l4_chksum_err = 1;
+ pkt_hdr->p.flags.tcp_err = 1;
+ _ODP_DBG("TCP chksum fail (%x)!\n", sum);
+ if (opt.bit.drop_tcp_err)
+ return -1;
+ }
+ }
+
+ if (opt.bit.sctp_chksum &&
+ pkt_hdr->p.input_flags.sctp &&
+ !pkt_hdr->p.input_flags.ipfrag) {
+ uint32_t seg_len = 0;
+ _odp_sctphdr_t hdr_copy;
+ uint32_t sum = ~packet_sum_crc32c(pkt_hdr,
+ pkt_hdr->p.l4_offset +
+ _ODP_SCTPHDR_LEN,
+ pkt_hdr->frame_len -
+ pkt_hdr->p.l4_offset -
+ _ODP_SCTPHDR_LEN,
+ l4_part_sum);
+ _odp_sctphdr_t *sctp = packet_map(pkt_hdr,
+ pkt_hdr->p.l4_offset,
+ &seg_len, NULL);
+ if (odp_unlikely(seg_len < sizeof(*sctp))) {
+ odp_packet_t pkt = packet_handle(pkt_hdr);
+
+ sctp = &hdr_copy;
+ odp_packet_copy_to_mem(pkt, pkt_hdr->p.l4_offset,
+ sizeof(*sctp), sctp);
+ }
+ pkt_hdr->p.input_flags.l4_chksum_done = 1;
+ if (sum != sctp->chksum) {
+ pkt_hdr->p.flags.l4_chksum_err = 1;
+ pkt_hdr->p.flags.sctp_err = 1;
+ _ODP_DBG("SCTP chksum fail (%x/%x)!\n", sum, sctp->chksum);
+ if (opt.bit.drop_sctp_err)
+ return -1;
+ }
+ }
+
+ return pkt_hdr->p.flags.all.error != 0;
+}
+
+int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
+ const odp_packet_parse_param_t *param)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ const uint8_t *data;
+ uint32_t seg_len;
+ uint32_t seg_end;
+ uint32_t packet_len = pkt_hdr->frame_len;
+ odp_proto_t proto = param->proto;
+ odp_proto_layer_t layer = param->last_layer;
+ int ret;
uint16_t ethtype;
- const uint8_t *parseptr;
- uint8_t ip_proto;
- const _odp_ethhdr_t *eth;
- uint16_t macaddr0, macaddr2, macaddr4;
- const _odp_vlanhdr_t *vlan;
+ uint64_t l4_part_sum = 0;
+ const uint32_t min_seglen = PARSE_ETH_BYTES + PARSE_L3_L4_BYTES;
+ uint8_t buf[min_seglen];
+ odp_pktin_config_opt_t opt;
- if (layer == ODP_PKTIO_PARSER_LAYER_NONE)
- return 0;
+ if (proto == ODP_PROTO_NONE || layer == ODP_PROTO_LAYER_NONE)
+ return -1;
+
+ data = packet_map(pkt_hdr, offset, &seg_len, NULL);
+
+ if (data == NULL)
+ return -1;
+
+ /*
+ * We must not have a packet segment boundary within the parsed
+ * packet data range. Copy enough data to a temporary buffer for
+ * parsing if necessary.
+ */
+ if (odp_unlikely(pkt_hdr->seg_count > 1) &&
+ odp_unlikely(seg_len < min_seglen)) {
+ seg_len = min_seglen;
+ if (seg_len > packet_len - offset)
+ seg_len = packet_len - offset;
+ odp_packet_copy_to_mem(pkt, offset, seg_len, buf);
+ data = buf;
+ }
+
+ seg_end = offset + seg_len; /* one past the maximum offset */
+
+ /* Reset parser flags, keep other flags */
+ packet_parse_reset(pkt_hdr, 0);
- /* We only support Ethernet for now */
- prs->input_flags.eth = 1;
- /* Assume valid L2 header, no CRC/FCS check in SW */
- prs->input_flags.l2 = 1;
- /* Detect jumbo frames */
- if (frame_len > _ODP_ETH_LEN_MAX)
- prs->input_flags.jumbo = 1;
-
- offset = sizeof(_odp_ethhdr_t);
- eth = (const _odp_ethhdr_t *)ptr;
-
- /* Handle Ethernet broadcast/multicast addresses */
- macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)(const void *)eth));
- prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;
-
- if (macaddr0 == 0xffff) {
- macaddr2 =
- odp_be_to_cpu_16(*((const uint16_t *)
- (const void *)eth + 1));
- macaddr4 =
- odp_be_to_cpu_16(*((const uint16_t *)
- (const void *)eth + 2));
- prs->input_flags.eth_bcast =
- (macaddr2 == 0xffff) && (macaddr4 == 0xffff);
+ if (proto == ODP_PROTO_ETH) {
+ /* Assume valid L2 header, no CRC/FCS check in SW */
+ pkt_hdr->p.l2_offset = offset;
+
+ ethtype = _odp_parse_eth(&pkt_hdr->p, &data, &offset, packet_len);
+ } else if (proto == ODP_PROTO_IPV4) {
+ ethtype = _ODP_ETHTYPE_IPV4;
+ } else if (proto == ODP_PROTO_IPV6) {
+ ethtype = _ODP_ETHTYPE_IPV6;
} else {
- prs->input_flags.eth_bcast = 0;
+ ethtype = 0; /* Invalid */
}
- /* Get Ethertype */
- ethtype = odp_be_to_cpu_16(eth->type);
- parseptr = (const uint8_t *)(eth + 1);
+ opt.all_bits = 0;
+ opt.bit.ipv4_chksum = param->chksums.chksum.ipv4;
+ opt.bit.udp_chksum = param->chksums.chksum.udp;
+ opt.bit.tcp_chksum = param->chksums.chksum.tcp;
+ opt.bit.sctp_chksum = param->chksums.chksum.sctp;
- /* Check for SNAP vs. DIX */
- if (ethtype < _ODP_ETH_LEN_MAX) {
- prs->input_flags.snap = 1;
- if (ethtype > frame_len - offset) {
- prs->error_flags.snap_len = 1;
- goto parse_exit;
- }
- ethtype = odp_be_to_cpu_16(*((const uint16_t *)(uintptr_t)
- (parseptr + 6)));
- offset += 8;
- parseptr += 8;
+ ret = _odp_packet_parse_common_l3_l4(&pkt_hdr->p, data, offset,
+ packet_len, seg_end, layer,
+ ethtype, &l4_part_sum, opt);
+
+ if (ret)
+ return -1;
+
+ if (layer >= ODP_PROTO_LAYER_L4) {
+ ret = _odp_packet_l4_chksum(pkt_hdr, opt, l4_part_sum);
+ if (ret)
+ return -1;
}
- /* Parse the VLAN header(s), if present */
- if (ethtype == _ODP_ETHTYPE_VLAN_OUTER) {
- prs->input_flags.vlan_qinq = 1;
- prs->input_flags.vlan = 1;
+ return 0;
+}
+
+int odp_packet_parse_multi(const odp_packet_t pkt[], const uint32_t offset[],
+ int num, const odp_packet_parse_param_t *param)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ if (odp_packet_parse(pkt[i], offset[i], param))
+ return i;
+
+ return num;
+}
- vlan = (const _odp_vlanhdr_t *)parseptr;
- ethtype = odp_be_to_cpu_16(vlan->type);
- offset += sizeof(_odp_vlanhdr_t);
- parseptr += sizeof(_odp_vlanhdr_t);
+void odp_packet_parse_result(odp_packet_t pkt,
+ odp_packet_parse_result_t *result)
+{
+ /* TODO: optimize to single word copy when packet header stores bits
+ * directly into odp_packet_parse_result_flag_t */
+ result->flag.all = 0;
+ result->flag.has_error = odp_packet_has_error(pkt);
+ result->flag.has_l2_error = odp_packet_has_l2_error(pkt);
+ result->flag.has_l3_error = odp_packet_has_l3_error(pkt);
+ result->flag.has_l4_error = odp_packet_has_l4_error(pkt);
+ result->flag.has_l2 = odp_packet_has_l2(pkt);
+ result->flag.has_l3 = odp_packet_has_l3(pkt);
+ result->flag.has_l4 = odp_packet_has_l4(pkt);
+ result->flag.has_eth = odp_packet_has_eth(pkt);
+ result->flag.has_eth_bcast = odp_packet_has_eth_bcast(pkt);
+ result->flag.has_eth_mcast = odp_packet_has_eth_mcast(pkt);
+ result->flag.has_jumbo = odp_packet_has_jumbo(pkt);
+ result->flag.has_vlan = odp_packet_has_vlan(pkt);
+ result->flag.has_vlan_qinq = odp_packet_has_vlan_qinq(pkt);
+ result->flag.has_arp = odp_packet_has_arp(pkt);
+ result->flag.has_ipv4 = odp_packet_has_ipv4(pkt);
+ result->flag.has_ipv6 = odp_packet_has_ipv6(pkt);
+ result->flag.has_ip_bcast = odp_packet_has_ip_bcast(pkt);
+ result->flag.has_ip_mcast = odp_packet_has_ip_mcast(pkt);
+ result->flag.has_ipfrag = odp_packet_has_ipfrag(pkt);
+ result->flag.has_ipopt = odp_packet_has_ipopt(pkt);
+ result->flag.has_ipsec = odp_packet_has_ipsec(pkt);
+ result->flag.has_udp = odp_packet_has_udp(pkt);
+ result->flag.has_tcp = odp_packet_has_tcp(pkt);
+ result->flag.has_sctp = odp_packet_has_sctp(pkt);
+ result->flag.has_icmp = odp_packet_has_icmp(pkt);
+
+ result->packet_len = odp_packet_len(pkt);
+ result->l2_offset = odp_packet_l2_offset(pkt);
+ result->l3_offset = odp_packet_l3_offset(pkt);
+ result->l4_offset = odp_packet_l4_offset(pkt);
+ result->l3_chksum_status = odp_packet_l3_chksum_status(pkt);
+ result->l4_chksum_status = odp_packet_l4_chksum_status(pkt);
+ result->l2_type = odp_packet_l2_type(pkt);
+ result->l3_type = odp_packet_l3_type(pkt);
+ result->l4_type = odp_packet_l4_type(pkt);
+}
+
+void odp_packet_parse_result_multi(const odp_packet_t pkt[],
+ odp_packet_parse_result_t *result[],
+ int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ odp_packet_parse_result(pkt[i], result[i]);
+}
+
+uint64_t odp_packet_to_u64(odp_packet_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_packet_seg_to_u64(odp_packet_seg_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+odp_packet_t odp_packet_ref_static(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ while (pkt_hdr != NULL) {
+ segment_ref_inc(pkt_hdr);
+ pkt_hdr = pkt_hdr->seg_next;
}
- if (ethtype == _ODP_ETHTYPE_VLAN) {
- prs->input_flags.vlan = 1;
- vlan = (const _odp_vlanhdr_t *)parseptr;
- ethtype = odp_be_to_cpu_16(vlan->type);
- offset += sizeof(_odp_vlanhdr_t);
- parseptr += sizeof(_odp_vlanhdr_t);
+ return pkt;
+}
+
+odp_packet_t odp_packet_ref(odp_packet_t pkt, uint32_t offset)
+{
+ odp_packet_t new;
+ int ret;
+
+ new = odp_packet_copy(pkt, odp_packet_pool(pkt));
+
+ if (new == ODP_PACKET_INVALID) {
+ _ODP_ERR("copy failed\n");
+ return ODP_PACKET_INVALID;
}
- if (layer == ODP_PKTIO_PARSER_LAYER_L2)
- return prs->error_flags.all != 0;
+ ret = odp_packet_trunc_head(&new, offset, NULL, NULL);
- /* Set l3_offset+flag only for known ethtypes */
- prs->l3_offset = offset;
- prs->input_flags.l3 = 1;
+ if (ret < 0) {
+ _ODP_ERR("trunk_head failed\n");
+ odp_packet_free(new);
+ return ODP_PACKET_INVALID;
+ }
- /* Parse Layer 3 headers */
- switch (ethtype) {
- case _ODP_ETHTYPE_IPV4:
- prs->input_flags.ipv4 = 1;
- ip_proto = parse_ipv4(prs, &parseptr, &offset, frame_len);
- break;
+ return new;
+}
- case _ODP_ETHTYPE_IPV6:
- prs->input_flags.ipv6 = 1;
- ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,
- seg_len);
- break;
+odp_packet_t odp_packet_ref_pkt(odp_packet_t pkt, uint32_t offset,
+ odp_packet_t hdr)
+{
+ odp_packet_t ref;
+ int ret;
- case _ODP_ETHTYPE_ARP:
- prs->input_flags.arp = 1;
- ip_proto = 255; /* Reserved invalid by IANA */
- break;
+ ref = odp_packet_ref(pkt, offset);
- default:
- prs->input_flags.l3 = 0;
- prs->l3_offset = ODP_PACKET_OFFSET_INVALID;
- ip_proto = 255; /* Reserved invalid by IANA */
+ if (ref == ODP_PACKET_INVALID) {
+ _ODP_DBG("reference create failed\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ ret = odp_packet_concat(&hdr, ref);
+
+ if (ret < 0) {
+ _ODP_DBG("concat failed\n");
+ odp_packet_free(ref);
+ return ODP_PACKET_INVALID;
}
- if (layer == ODP_PKTIO_PARSER_LAYER_L3)
- return prs->error_flags.all != 0;
+ return hdr;
+}
- /* Set l4_offset+flag only for known ip_proto */
- prs->l4_offset = offset;
- prs->input_flags.l4 = 1;
+int odp_packet_has_ref(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ uint32_t ref_cnt;
- /* Parse Layer 4 headers */
- switch (ip_proto) {
- case _ODP_IPPROTO_ICMPv4:
- /* Fall through */
+ while (pkt_hdr != NULL) {
+ ref_cnt = segment_ref(pkt_hdr);
- case _ODP_IPPROTO_ICMPv6:
- prs->input_flags.icmp = 1;
- break;
+ if (is_multi_ref(ref_cnt))
+ return 1;
- case _ODP_IPPROTO_TCP:
- if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))
- return -1;
- prs->input_flags.tcp = 1;
- parse_tcp(prs, &parseptr, NULL);
- break;
+ pkt_hdr = pkt_hdr->seg_next;
+ }
- case _ODP_IPPROTO_UDP:
- if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))
- return -1;
- prs->input_flags.udp = 1;
- parse_udp(prs, &parseptr, NULL);
- break;
+ return 0;
+}
- case _ODP_IPPROTO_AH:
- prs->input_flags.ipsec = 1;
- prs->input_flags.ipsec_ah = 1;
- break;
+void odp_packet_lso_request_clr(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- case _ODP_IPPROTO_ESP:
- prs->input_flags.ipsec = 1;
- prs->input_flags.ipsec_esp = 1;
- break;
+ pkt_hdr->p.flags.lso = 0;
+}
- case _ODP_IPPROTO_SCTP:
- prs->input_flags.sctp = 1;
- break;
+int odp_packet_has_lso_request(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- default:
- prs->input_flags.l4 = 0;
- prs->l4_offset = ODP_PACKET_OFFSET_INVALID;
+ return pkt_hdr->p.flags.lso;
+}
+
+uint32_t odp_packet_payload_offset(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (pkt_hdr->p.flags.payload_off)
+ return pkt_hdr->payload_offset;
+
+ return ODP_PACKET_OFFSET_INVALID;
+}
+
+int odp_packet_payload_offset_set(odp_packet_t pkt, uint32_t offset)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ pkt_hdr->p.flags.payload_off = 1;
+ pkt_hdr->payload_offset = offset;
+
+ return 0;
+}
+
+void odp_packet_aging_tmo_set(odp_packet_t pkt, uint64_t tmo_ns)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ pkt_hdr->p.flags.tx_aging = tmo_ns ? 1 : 0;
+ pkt_hdr->tx_aging_ns = tmo_ns;
+}
+
+uint64_t odp_packet_aging_tmo(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ return pkt_hdr->p.flags.tx_aging ? pkt_hdr->tx_aging_ns : 0;
+}
+
+int odp_packet_tx_compl_request(odp_packet_t pkt, const odp_packet_tx_compl_opt_t *opt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ switch (opt->mode) {
+ case ODP_PACKET_TX_COMPL_DISABLED:
+ pkt_hdr->p.flags.tx_compl_ev = 0;
+ pkt_hdr->p.flags.tx_compl_poll = 0;
break;
+ case ODP_PACKET_TX_COMPL_EVENT:
+ _ODP_ASSERT(opt->queue != ODP_QUEUE_INVALID);
+ pkt_hdr->p.flags.tx_compl_ev = 1;
+ pkt_hdr->p.flags.tx_compl_poll = 0;
+ pkt_hdr->dst_queue = opt->queue;
+ break;
+ case ODP_PACKET_TX_COMPL_POLL:
+ pkt_hdr->p.flags.tx_compl_ev = 0;
+ pkt_hdr->p.flags.tx_compl_poll = 1;
+ pkt_hdr->tx_compl_id = opt->compl_id;
+ break;
+ default:
+ _ODP_ERR("Bad TX completion mode: %i\n", opt->mode);
+ return -1;
}
-parse_exit:
- return prs->error_flags.all != 0;
+
+ return 0;
}
-/**
- * Simple packet parser
- */
-int packet_parse_layer(odp_packet_hdr_t *pkt_hdr,
- odp_pktio_parser_layer_t layer)
+int odp_packet_has_tx_compl_request(odp_packet_t pkt)
{
- uint32_t seg_len = packet_first_seg_len(pkt_hdr);
- void *base = packet_data(pkt_hdr);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
- seg_len, layer);
+ return pkt_hdr->p.flags.tx_compl_ev || pkt_hdr->p.flags.tx_compl_poll;
}
-uint64_t odp_packet_to_u64(odp_packet_t hdl)
+void odp_packet_tx_compl_free(odp_packet_tx_compl_t tx_compl)
{
- return _odp_pri(hdl);
+ if (odp_unlikely(tx_compl == ODP_PACKET_TX_COMPL_INVALID)) {
+ _ODP_ERR("Bad TX completion event handle\n");
+ return;
+ }
+
+ odp_buffer_free((odp_buffer_t)tx_compl);
}
-uint64_t odp_packet_seg_to_u64(odp_packet_seg_t hdl)
+void *odp_packet_tx_compl_user_ptr(odp_packet_tx_compl_t tx_compl)
{
- return _odp_pri(hdl);
+ if (odp_unlikely(tx_compl == ODP_PACKET_TX_COMPL_INVALID)) {
+ _ODP_ERR("Bad TX completion event handle\n");
+ return NULL;
+ }
+
+ _odp_pktio_tx_compl_t *data = odp_buffer_addr((odp_buffer_t)tx_compl);
+
+ return (void *)(uintptr_t)data->user_ptr;
}
-/* Include non-inlined versions of API functions */
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/packet_inlines_api.h>
-#endif
+int odp_packet_tx_compl_done(odp_pktio_t pktio, uint32_t compl_id)
+{
+ return odp_atomic_load_acq_u32(&get_pktio_entry(pktio)->tx_compl_status[compl_id]);
+}
+
+void odp_packet_free_ctrl_set(odp_packet_t pkt, odp_packet_free_ctrl_t ctrl)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (ctrl == ODP_PACKET_FREE_CTRL_DONT_FREE)
+ pkt_hdr->p.flags.free_ctrl = 1;
+ else
+ pkt_hdr->p.flags.free_ctrl = 0;
+}
+
+odp_packet_free_ctrl_t odp_packet_free_ctrl(odp_packet_t pkt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (pkt_hdr->p.flags.free_ctrl)
+ return ODP_PACKET_FREE_CTRL_DONT_FREE;
+
+ return ODP_PACKET_FREE_CTRL_DISABLED;
+}
+
+odp_packet_reass_status_t
+odp_packet_reass_status(odp_packet_t pkt)
+{
+ (void)pkt;
+ return ODP_PACKET_REASS_NONE;
+}
+
+int odp_packet_reass_info(odp_packet_t pkt, odp_packet_reass_info_t *info)
+{
+ (void)pkt;
+ (void)info;
+ return -1;
+}
+
+int
+odp_packet_reass_partial_state(odp_packet_t pkt, odp_packet_t frags[],
+ odp_packet_reass_partial_state_t *res)
+{
+ (void)pkt;
+ (void)frags;
+ (void)res;
+ return -ENOTSUP;
+}
+
+uint32_t odp_packet_disassemble(odp_packet_t pkt, odp_packet_buf_t pkt_buf[], uint32_t num)
+{
+ uint32_t i;
+ odp_packet_seg_t seg;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ pool_t *pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
+ uint32_t num_segs = odp_packet_num_segs(pkt);
+
+ if (odp_unlikely(pool->type != ODP_POOL_PACKET)) {
+ _ODP_ERR("Not a packet pool\n");
+ return 0;
+ }
+
+ if (odp_unlikely(pool->pool_ext == 0)) {
+ _ODP_ERR("Not an external memory pool\n");
+ return 0;
+ }
+
+ if (odp_unlikely(num < num_segs)) {
+ _ODP_ERR("Not enough buffer handles %u. Packet has %u segments.\n", num, num_segs);
+ return 0;
+ }
+
+ seg = odp_packet_first_seg(pkt);
+
+ for (i = 0; i < num_segs; i++) {
+ pkt_buf[i] = (odp_packet_buf_t)(uintptr_t)packet_seg_to_hdr(seg);
+ seg = odp_packet_next_seg(pkt, seg);
+ }
+
+ return num_segs;
+}
+
+odp_packet_t odp_packet_reassemble(odp_pool_t pool_hdl, odp_packet_buf_t pkt_buf[], uint32_t num)
+{
+ uint32_t i, data_len, tailroom;
+ odp_packet_hdr_t *cur_seg, *next_seg;
+ odp_packet_hdr_t *pkt_hdr = (odp_packet_hdr_t *)(uintptr_t)pkt_buf[0];
+ uint32_t headroom = odp_packet_buf_data_offset(pkt_buf[0]);
+
+ pool_t *pool = _odp_pool_entry(pool_hdl);
+
+ if (odp_unlikely(pool->type != ODP_POOL_PACKET)) {
+ _ODP_ERR("Not a packet pool\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ if (odp_unlikely(pool->pool_ext == 0)) {
+ _ODP_ERR("Not an external memory pool\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ if (odp_unlikely(num == 0)) {
+ _ODP_ERR("Bad number of buffers: %u\n", num);
+ return ODP_PACKET_INVALID;
+ }
+
+ cur_seg = pkt_hdr;
+ data_len = 0;
+
+ for (i = 0; i < num; i++) {
+ next_seg = NULL;
+ if (i < num - 1)
+ next_seg = (odp_packet_hdr_t *)(uintptr_t)pkt_buf[i + 1];
+
+ data_len += cur_seg->seg_len;
+ cur_seg->seg_next = next_seg;
+ cur_seg = next_seg;
+ }
+
+ tailroom = pool->ext_param.pkt.buf_size - sizeof(odp_packet_hdr_t);
+ tailroom -= pool->ext_param.pkt.app_header_size;
+ tailroom -= odp_packet_buf_data_len(pkt_buf[num - 1]);
+ tailroom -= pool->trailer_size;
+
+ pkt_hdr->seg_count = num;
+ pkt_hdr->frame_len = data_len;
+ pkt_hdr->headroom = headroom;
+ pkt_hdr->tailroom = tailroom;
+
+ /* Reset metadata */
+ pkt_hdr->event_hdr.subtype = ODP_EVENT_PACKET_BASIC;
+ pkt_hdr->input = ODP_PKTIO_INVALID;
+ packet_parse_reset(pkt_hdr, 1);
+
+ return packet_handle(pkt_hdr);
+}
+
+void odp_packet_proto_stats_request(odp_packet_t pkt, odp_packet_proto_stats_opt_t *opt)
+{
+ (void)pkt;
+ (void)opt;
+}
+
+odp_proto_stats_t odp_packet_proto_stats(odp_packet_t pkt)
+{
+ (void)pkt;
+
+ return ODP_PROTO_STATS_INVALID;
+}
diff --git a/platform/linux-generic/odp_packet_api.c b/platform/linux-generic/odp_packet_api.c
new file mode 100644
index 000000000..0309c4e5a
--- /dev/null
+++ b/platform/linux-generic/odp_packet_api.c
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+#include <odp/api/packet.h>
+
+/* Prevent this header from being included again later */
+#include <odp/api/plat/packet_io_inlines.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_vector_inlines.h>
diff --git a/platform/linux-generic/odp_packet_flags.c b/platform/linux-generic/odp_packet_flags.c
index 72df1ecfe..e119d136f 100644
--- a/platform/linux-generic/odp_packet_flags.c
+++ b/platform/linux-generic/odp_packet_flags.c
@@ -1,180 +1,33 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
#include <odp/api/plat/packet_flag_inlines.h>
#include <odp/api/packet_flags.h>
#include <odp_packet_internal.h>
-#define retflag(pkt, x) do { \
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt); \
- return pkt_hdr->p.x; \
- } while (0)
-
#define setflag(pkt, x, v) do { \
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt); \
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt); \
pkt_hdr->p.x = (v) & 1; \
} while (0)
-int odp_packet_has_error(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
-
- return pkt_hdr->p.error_flags.all != 0;
-}
-
-/* Get Input Flags */
-
-int odp_packet_has_l2_error(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
- /* L2 parsing is always done by default and hence
- no additional check is required */
- return pkt_hdr->p.error_flags.frame_len
- | pkt_hdr->p.error_flags.snap_len
- | pkt_hdr->p.error_flags.l2_chksum;
-}
-
-int odp_packet_has_l3(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.l3);
-}
-
-int odp_packet_has_l3_error(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
-
- return pkt_hdr->p.error_flags.ip_err;
-}
-
-int odp_packet_has_l4(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.l4);
-}
-
-int odp_packet_has_l4_error(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
-
- return pkt_hdr->p.error_flags.tcp_err | pkt_hdr->p.error_flags.udp_err;
-}
-
-int odp_packet_has_eth_bcast(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.eth_bcast);
-}
-
-int odp_packet_has_eth_mcast(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.eth_mcast);
-}
-
-int odp_packet_has_vlan(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.vlan);
-}
-
-int odp_packet_has_vlan_qinq(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.vlan_qinq);
-}
-
-int odp_packet_has_arp(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.arp);
-}
-
-int odp_packet_has_ipv4(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ipv4);
-}
-
-int odp_packet_has_ipv6(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ipv6);
-}
-
-int odp_packet_has_ip_bcast(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ip_bcast);
-}
-
-int odp_packet_has_ip_mcast(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ip_mcast);
-}
-
-int odp_packet_has_ipfrag(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ipfrag);
-}
-
-int odp_packet_has_ipopt(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ipopt);
-}
-
-int odp_packet_has_ipsec(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.ipsec);
-}
-
-int odp_packet_has_udp(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.udp);
-}
-
-int odp_packet_has_tcp(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.tcp);
-}
-
-int odp_packet_has_sctp(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.sctp);
-}
-
-int odp_packet_has_icmp(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.icmp);
-}
-
-odp_packet_color_t odp_packet_color(odp_packet_t pkt)
-{
- retflag(pkt, input_flags.color);
-}
-
void odp_packet_color_set(odp_packet_t pkt, odp_packet_color_t color)
{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
pkt_hdr->p.input_flags.color = color;
}
-odp_bool_t odp_packet_drop_eligible(odp_packet_t pkt)
-{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
-
- return !pkt_hdr->p.input_flags.nodrop;
-}
-
void odp_packet_drop_eligible_set(odp_packet_t pkt, odp_bool_t drop)
{
setflag(pkt, input_flags.nodrop, !drop);
}
-int8_t odp_packet_shaper_len_adjust(odp_packet_t pkt)
-{
- retflag(pkt, output_flags.shaper_len_adj);
-}
-
void odp_packet_shaper_len_adjust_set(odp_packet_t pkt, int8_t adj)
{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
- pkt_hdr->p.output_flags.shaper_len_adj = adj;
+ pkt_hdr->p.flags.shaper_len_adj = adj;
}
/* Set Input Flags */
@@ -217,10 +70,12 @@ void odp_packet_has_jumbo_set(odp_packet_t pkt, int val)
void odp_packet_has_vlan_set(odp_packet_t pkt, int val)
{
setflag(pkt, input_flags.vlan, val);
+ setflag(pkt, input_flags.vlan_qinq, 0);
}
void odp_packet_has_vlan_qinq_set(odp_packet_t pkt, int val)
{
+ setflag(pkt, input_flags.vlan, val);
setflag(pkt, input_flags.vlan_qinq, val);
}
@@ -286,19 +141,14 @@ void odp_packet_has_icmp_set(odp_packet_t pkt, int val)
void odp_packet_has_flow_hash_clr(odp_packet_t pkt)
{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
pkt_hdr->p.input_flags.flow_hash = 0;
}
void odp_packet_has_ts_clr(odp_packet_t pkt)
{
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
pkt_hdr->p.input_flags.timestamp = 0;
}
-
-/* Include non-inlined versions of API functions */
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/packet_flag_inlines_api.h>
-#endif
diff --git a/platform/linux-generic/odp_packet_flags_api.c b/platform/linux-generic/odp_packet_flags_api.c
new file mode 100644
index 000000000..91457504c
--- /dev/null
+++ b/platform/linux-generic/odp_packet_flags_api.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+#include <odp/api/packet_flags.h>
+#include <odp_packet_internal.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/packet_flag_inlines.h>
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index 9419adf6f..f523d6de5 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -1,102 +1,185 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
+
#include <odp_posix_extensions.h>
-#include <odp/api/packet_io.h>
-#include <odp_packet_io_internal.h>
+#include <odp/api/buffer.h>
+#include <odp/api/debug.h>
#include <odp/api/packet.h>
-#include <odp_packet_internal.h>
-#include <odp_internal.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/proto_stats.h>
+#include <odp/api/shared_memory.h>
#include <odp/api/spinlock.h>
#include <odp/api/ticketlock.h>
-#include <odp/api/shared_memory.h>
-#include <odp_packet_socket.h>
+#include <odp/api/time.h>
+
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_io_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp/api/plat/time_inlines.h>
+
+#include <odp/autoheader_internal.h>
+#include <odp_classification_internal.h>
#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_event_vector_internal.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_string_internal.h>
+#include <odp_pcapng.h>
#include <odp_queue_if.h>
#include <odp_schedule_if.h>
-#include <odp_classification_internal.h>
-#include <odp_debug_internal.h>
-#include <odp_packet_io_ipc_internal.h>
-#include <odp/api/time.h>
-#include <string.h>
+#include <ifaddrs.h>
#include <inttypes.h>
+#include <string.h>
#include <sys/ioctl.h>
-#include <ifaddrs.h>
-#include <errno.h>
#include <time.h>
-/* Sleep this many nanoseconds between pktin receive calls */
-#define SLEEP_NSEC 1000
+/* Sleep this many microseconds between pktin receive calls. Must be smaller
+ * than 1000000 (a million), i.e. smaller than a second. */
+#define SLEEP_USEC 1
-/* Check total sleep time about every SLEEP_CHECK * SLEEP_NSEC nanoseconds.
+/* Check total sleep time about every SLEEP_CHECK * SLEEP_USEC microseconds.
* Must be power of two. */
#define SLEEP_CHECK 32
-static pktio_table_t *pktio_tbl;
+/* Max wait time supported to avoid potential overflow */
+#define MAX_WAIT_TIME (UINT64_MAX / 1024)
+
+/* One hour maximum aging timeout, no real limitations imposed by the implementation other than
+ * integer width, so just use some value. */
+#define MAX_TX_AGING_TMO_NS 3600000000000ULL
+
+typedef struct {
+ union {
+ struct {
+ odp_buffer_t buf;
+ const void *user_ptr;
+ odp_queue_t queue;
+ };
+
+ odp_atomic_u32_t *status;
+ };
+ uint16_t idx;
+ uint8_t mode;
+} tx_compl_info_t;
+
+/* Global variables */
+static pktio_global_t *pktio_global;
/* pktio pointer entries ( for inlines) */
-void *pktio_entry_ptr[ODP_CONFIG_PKTIO_ENTRIES];
+void *_odp_pktio_entry_ptr[CONFIG_PKTIO_ENTRIES];
static inline pktio_entry_t *pktio_entry_by_index(int index)
{
- return pktio_entry_ptr[index];
+ return _odp_pktio_entry_ptr[index];
+}
+
+static int read_config_file(pktio_global_t *pktio_glb)
+{
+ const char *str;
+ int val = 0;
+
+ _ODP_PRINT("Packet IO config:\n");
+
+ str = "pktio.pktin_frame_offset";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val < 0 || val > UINT16_MAX) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+
+ pktio_glb->config.pktin_frame_offset = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "pktio.tx_compl_pool_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val < 0) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+
+ pktio_glb->config.tx_compl_pool_size = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ _ODP_PRINT("\n");
+
+ return 0;
}
-int odp_pktio_init_global(void)
+int _odp_pktio_init_global(void)
{
pktio_entry_t *pktio_entry;
int i;
odp_shm_t shm;
int pktio_if;
- shm = odp_shm_reserve("odp_pktio_entries",
- sizeof(pktio_table_t),
- sizeof(pktio_entry_t), 0);
- pktio_tbl = odp_shm_addr(shm);
-
- if (pktio_tbl == NULL)
+ shm = odp_shm_reserve("_odp_pktio_global", sizeof(pktio_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
return -1;
- memset(pktio_tbl, 0, sizeof(pktio_table_t));
+ pktio_global = odp_shm_addr(shm);
+ memset(pktio_global, 0, sizeof(pktio_global_t));
+ pktio_global->shm = shm;
- odp_spinlock_init(&pktio_tbl->lock);
+ odp_spinlock_init(&pktio_global->lock);
- for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
- pktio_entry = &pktio_tbl->entries[i];
+ if (read_config_file(pktio_global)) {
+ odp_shm_free(shm);
+ pktio_global = NULL;
+ return -1;
+ }
- odp_ticketlock_init(&pktio_entry->s.rxl);
- odp_ticketlock_init(&pktio_entry->s.txl);
- odp_spinlock_init(&pktio_entry->s.cls.l2_cos_table.lock);
- odp_spinlock_init(&pktio_entry->s.cls.l3_cos_table.lock);
+ for (i = 0; i < CONFIG_PKTIO_ENTRIES; ++i) {
+ pktio_entry = &pktio_global->entries[i];
- pktio_entry_ptr[i] = pktio_entry;
+ pktio_entry->handle = _odp_cast_scalar(odp_pktio_t, i + 1);
+ odp_ticketlock_init(&pktio_entry->rxl);
+ odp_ticketlock_init(&pktio_entry->txl);
+
+ _odp_pktio_entry_ptr[i] = pktio_entry;
}
- for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
- if (pktio_if_ops[pktio_if]->init_global)
- if (pktio_if_ops[pktio_if]->init_global()) {
- ODP_ERR("failed to initialized pktio type %d",
- pktio_if);
+ for (pktio_if = 0; _odp_pktio_if_ops[pktio_if]; ++pktio_if) {
+ if (_odp_pktio_if_ops[pktio_if]->init_global)
+ if (_odp_pktio_if_ops[pktio_if]->init_global()) {
+ _ODP_ERR("failed to initialized pktio type %d", pktio_if);
return -1;
}
}
+ if (_ODP_PCAPNG) {
+ if (_odp_pcapng_init_global()) {
+ _ODP_ERR("Failed to initialize pcapng\n");
+ return -1;
+ }
+ }
+
return 0;
}
-int odp_pktio_init_local(void)
+int _odp_pktio_init_local(void)
{
int pktio_if;
- for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
- if (pktio_if_ops[pktio_if]->init_local)
- if (pktio_if_ops[pktio_if]->init_local()) {
- ODP_ERR("failed to initialized pktio type %d",
- pktio_if);
+ for (pktio_if = 0; _odp_pktio_if_ops[pktio_if]; ++pktio_if) {
+ if (_odp_pktio_if_ops[pktio_if]->init_local)
+ if (_odp_pktio_if_ops[pktio_if]->init_local()) {
+ _ODP_ERR("failed to initialized pktio type %d", pktio_if);
return -1;
}
}
@@ -106,74 +189,103 @@ int odp_pktio_init_local(void)
static inline int is_free(pktio_entry_t *entry)
{
- return (entry->s.state == PKTIO_STATE_FREE);
+ return (entry->state == PKTIO_STATE_FREE);
}
static void lock_entry(pktio_entry_t *entry)
{
- odp_ticketlock_lock(&entry->s.rxl);
- odp_ticketlock_lock(&entry->s.txl);
+ odp_ticketlock_lock(&entry->rxl);
+ odp_ticketlock_lock(&entry->txl);
}
static void unlock_entry(pktio_entry_t *entry)
{
- odp_ticketlock_unlock(&entry->s.txl);
- odp_ticketlock_unlock(&entry->s.rxl);
+ odp_ticketlock_unlock(&entry->txl);
+ odp_ticketlock_unlock(&entry->rxl);
}
-static void init_in_queues(pktio_entry_t *entry)
+/**
+ * Strip optional pktio type from device name by moving start pointer
+ *
+ * @param name Packet IO device name
+ * @param[out] type_out Optional char array (len = PKTIO_NAME_LEN) for storing
+ * pktio type. Ignored when NULL.
+ *
+ * @return Pointer to the beginning of device name
+ */
+static const char *strip_pktio_type(const char *name, char *type_out)
{
- int i;
+ const char *if_name;
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
- entry->s.in_queue[i].queue = ODP_QUEUE_INVALID;
- entry->s.in_queue[i].queue_int = QUEUE_NULL;
- entry->s.in_queue[i].pktin = PKTIN_INVALID;
+ if (type_out)
+ type_out[0] = '\0';
+
+ /* Strip pktio type prefix <pktio_type>:<if_name> */
+ if_name = strchr(name, ':');
+
+ if (if_name) {
+ int pktio_if;
+ int type_len = if_name - name;
+ char pktio_type[type_len + 1];
+
+ _odp_strcpy(pktio_type, name, type_len + 1);
+
+ /* Remove colon */
+ if_name++;
+
+ /* Match if_type to enabled pktio devices */
+ for (pktio_if = 0; _odp_pktio_if_ops[pktio_if]; pktio_if++) {
+ if (!strcmp(pktio_type, _odp_pktio_if_ops[pktio_if]->name)) {
+ if (type_out)
+ strcpy(type_out, pktio_type);
+ /* Some pktio devices expect device names to
+ * begin with pktio type */
+ if (!strcmp(pktio_type, "ipc") ||
+ !strcmp(pktio_type, "null") ||
+ !strcmp(pktio_type, "pcap") ||
+ !strcmp(pktio_type, "tap"))
+ return name;
+
+ return if_name;
+ }
+ }
}
+ return name;
}
static void init_out_queues(pktio_entry_t *entry)
{
int i;
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
- entry->s.out_queue[i].queue = ODP_QUEUE_INVALID;
- entry->s.out_queue[i].pktout = PKTOUT_INVALID;
+ for (i = 0; i < ODP_PKTOUT_MAX_QUEUES; i++) {
+ entry->out_queue[i].queue = ODP_QUEUE_INVALID;
+ entry->out_queue[i].pktout = PKTOUT_INVALID;
}
}
static void init_pktio_entry(pktio_entry_t *entry)
{
- pktio_cls_enabled_set(entry, 0);
+ int i;
- init_in_queues(entry);
- init_out_queues(entry);
+ /* Clear all flags */
+ entry->enabled.all_flags = 0;
- pktio_classifier_init(entry);
-}
+ entry->tx_compl_pool = ODP_POOL_INVALID;
+ entry->tx_compl_status_shm = ODP_SHM_INVALID;
-static odp_pktio_t alloc_lock_pktio_entry(void)
-{
- pktio_entry_t *entry;
- int i;
+ odp_atomic_init_u64(&entry->stats_extra.in_discards, 0);
+ odp_atomic_init_u64(&entry->stats_extra.in_errors, 0);
+ odp_atomic_init_u64(&entry->stats_extra.out_discards, 0);
+ odp_atomic_init_u64(&entry->tx_ts, 0);
- for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
- entry = &pktio_tbl->entries[i];
- if (is_free(entry)) {
- lock_entry(entry);
- if (is_free(entry)) {
- odp_pktio_t hdl;
-
- entry->s.state = PKTIO_STATE_ACTIVE;
- init_pktio_entry(entry);
- hdl = _odp_cast_scalar(odp_pktio_t, i + 1);
- return hdl; /* return with entry locked! */
- }
- unlock_entry(entry);
- }
+ for (i = 0; i < ODP_PKTIN_MAX_QUEUES; i++) {
+ entry->in_queue[i].queue = ODP_QUEUE_INVALID;
+ entry->in_queue[i].pktin = PKTIN_INVALID;
}
- return ODP_PKTIO_INVALID;
+ init_out_queues(entry);
+
+ _odp_pktio_classifier_init(entry);
}
static odp_pktio_t setup_pktio_entry(const char *name, odp_pool_t pool,
@@ -181,55 +293,71 @@ static odp_pktio_t setup_pktio_entry(const char *name, odp_pool_t pool,
{
odp_pktio_t hdl;
pktio_entry_t *pktio_entry;
+ int i, pktio_if;
+ char pktio_type[PKTIO_NAME_LEN];
+ const char *if_name;
+ uint16_t pktin_frame_offset = pktio_global->config.pktin_frame_offset;
int ret = -1;
- int pktio_if;
- if (strlen(name) >= PKTIO_NAME_LEN - 1) {
+ if (strlen(name) >= PKTIO_NAME_LEN) {
/* ioctl names limitation */
- ODP_ERR("pktio name %s is too big, limit is %d bytes\n",
- name, PKTIO_NAME_LEN - 1);
+ _ODP_ERR("pktio name %s is too long (max: %d chars)\n", name, PKTIO_NAME_LEN - 1);
return ODP_PKTIO_INVALID;
}
- hdl = alloc_lock_pktio_entry();
- if (hdl == ODP_PKTIO_INVALID) {
- ODP_ERR("No resources available.\n");
- return ODP_PKTIO_INVALID;
+ if_name = strip_pktio_type(name, pktio_type);
+
+ for (i = 0; i < CONFIG_PKTIO_ENTRIES; ++i) {
+ pktio_entry = &pktio_global->entries[i];
+ if (is_free(pktio_entry)) {
+ lock_entry(pktio_entry);
+ if (is_free(pktio_entry))
+ break;
+
+ unlock_entry(pktio_entry);
+ }
}
- /* if successful, alloc_pktio_entry() returns with the entry locked */
- pktio_entry = get_pktio_entry(hdl);
- if (!pktio_entry)
+ if (i == CONFIG_PKTIO_ENTRIES) {
+ _ODP_ERR("All pktios used already\n");
return ODP_PKTIO_INVALID;
+ }
- pktio_entry->s.pool = pool;
- memcpy(&pktio_entry->s.param, param, sizeof(odp_pktio_param_t));
- pktio_entry->s.handle = hdl;
+ /* Entry was found and is now locked */
+ pktio_entry->state = PKTIO_STATE_ACTIVE;
+ hdl = pktio_entry->handle;
- odp_pktio_config_init(&pktio_entry->s.config);
+ init_pktio_entry(pktio_entry);
- for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
- ret = pktio_if_ops[pktio_if]->open(hdl, pktio_entry, name,
- pool);
+ snprintf(pktio_entry->name, sizeof(pktio_entry->name), "%s", if_name);
+ snprintf(pktio_entry->full_name, sizeof(pktio_entry->full_name), "%s", name);
+ pktio_entry->pool = pool;
+ memcpy(&pktio_entry->param, param, sizeof(odp_pktio_param_t));
+ pktio_entry->pktin_frame_offset = pktin_frame_offset;
- if (!ret) {
- pktio_entry->s.ops = pktio_if_ops[pktio_if];
- ODP_DBG("%s uses %s\n",
- name, pktio_if_ops[pktio_if]->name);
+ odp_pktio_config_init(&pktio_entry->config);
+
+ for (pktio_if = 0; _odp_pktio_if_ops[pktio_if]; ++pktio_if) {
+ /* Only use explicitly defined pktio type */
+ if (strlen(pktio_type) &&
+ strcmp(_odp_pktio_if_ops[pktio_if]->name, pktio_type))
+ continue;
+
+ ret = _odp_pktio_if_ops[pktio_if]->open(hdl, pktio_entry, if_name, pool);
+
+ if (!ret)
break;
- }
}
if (ret != 0) {
- pktio_entry->s.state = PKTIO_STATE_FREE;
- hdl = ODP_PKTIO_INVALID;
- ODP_ERR("Unable to init any I/O type.\n");
- } else {
- snprintf(pktio_entry->s.name,
- sizeof(pktio_entry->s.name), "%s", name);
- pktio_entry->s.state = PKTIO_STATE_OPENED;
+ pktio_entry->state = PKTIO_STATE_FREE;
+ unlock_entry(pktio_entry);
+ _ODP_ERR("Unable to init any I/O type.\n");
+ return ODP_PKTIO_INVALID;
}
+ pktio_entry->state = PKTIO_STATE_OPENED;
+ pktio_entry->ops = _odp_pktio_if_ops[pktio_if];
unlock_entry(pktio_entry);
return hdl;
@@ -248,6 +376,19 @@ static int pool_type_is_packet(odp_pool_t pool)
return pool_info.params.type == ODP_POOL_PACKET;
}
+static const char *driver_name(odp_pktio_t hdl)
+{
+ pktio_entry_t *entry;
+
+ entry = get_pktio_entry(hdl);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
+ return "bad handle";
+ }
+
+ return entry->ops->name;
+}
+
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool,
const odp_pktio_param_t *param)
{
@@ -259,18 +400,19 @@ odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool,
param = &default_param;
}
- ODP_ASSERT(pool_type_is_packet(pool));
+ _ODP_ASSERT(pool_type_is_packet(pool));
hdl = odp_pktio_lookup(name);
if (hdl != ODP_PKTIO_INVALID) {
- /* interface is already open */
- __odp_errno = EEXIST;
+ _ODP_ERR("pktio device %s already opened\n", name);
return ODP_PKTIO_INVALID;
}
- odp_spinlock_lock(&pktio_tbl->lock);
+ odp_spinlock_lock(&pktio_global->lock);
hdl = setup_pktio_entry(name, pool, param);
- odp_spinlock_unlock(&pktio_tbl->lock);
+ odp_spinlock_unlock(&pktio_global->lock);
+
+ _ODP_DBG("interface: %s, driver: %s\n", name, driver_name(hdl));
return hdl;
}
@@ -278,21 +420,21 @@ odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool,
static int _pktio_close(pktio_entry_t *entry)
{
int ret;
- int state = entry->s.state;
+ int state = entry->state;
if (state != PKTIO_STATE_OPENED &&
state != PKTIO_STATE_STOPPED &&
state != PKTIO_STATE_STOP_PENDING)
return -1;
- ret = entry->s.ops->close(entry);
+ ret = entry->ops->close(entry);
if (ret)
return -1;
if (state == PKTIO_STATE_STOP_PENDING)
- entry->s.state = PKTIO_STATE_CLOSE_PENDING;
+ entry->state = PKTIO_STATE_CLOSE_PENDING;
else
- entry->s.state = PKTIO_STATE_FREE;
+ entry->state = PKTIO_STATE_FREE;
return 0;
}
@@ -302,10 +444,9 @@ static void destroy_in_queues(pktio_entry_t *entry, int num)
int i;
for (i = 0; i < num; i++) {
- if (entry->s.in_queue[i].queue != ODP_QUEUE_INVALID) {
- odp_queue_destroy(entry->s.in_queue[i].queue);
- entry->s.in_queue[i].queue = ODP_QUEUE_INVALID;
- entry->s.in_queue[i].queue_int = QUEUE_NULL;
+ if (entry->in_queue[i].queue != ODP_QUEUE_INVALID) {
+ odp_queue_destroy(entry->in_queue[i].queue);
+ entry->in_queue[i].queue = ODP_QUEUE_INVALID;
}
}
}
@@ -315,10 +456,10 @@ static void destroy_out_queues(pktio_entry_t *entry, int num)
int i, rc;
for (i = 0; i < num; i++) {
- if (entry->s.out_queue[i].queue != ODP_QUEUE_INVALID) {
- rc = odp_queue_destroy(entry->s.out_queue[i].queue);
- ODP_ASSERT(rc == 0);
- entry->s.out_queue[i].queue = ODP_QUEUE_INVALID;
+ if (entry->out_queue[i].queue != ODP_QUEUE_INVALID) {
+ rc = odp_queue_destroy(entry->out_queue[i].queue);
+ _ODP_ASSERT(rc == 0);
+ entry->out_queue[i].queue = ODP_QUEUE_INVALID;
}
}
}
@@ -330,18 +471,18 @@ static void flush_in_queues(pktio_entry_t *entry)
int max_pkts = 16;
odp_packet_t packets[max_pkts];
- mode = entry->s.param.in_mode;
- num = entry->s.num_in_queue;
+ mode = entry->param.in_mode;
+ num = entry->num_in_queue;
if (mode == ODP_PKTIN_MODE_DIRECT) {
for (i = 0; i < num; i++) {
int ret;
- odp_pktin_queue_t pktin = entry->s.in_queue[i].pktin;
+ odp_pktin_queue_t pktin = entry->in_queue[i].pktin;
while ((ret = odp_pktin_recv(pktin, packets,
max_pkts))) {
if (ret < 0) {
- ODP_ERR("Queue flush failed\n");
+ _ODP_ERR("Queue flush failed\n");
return;
}
@@ -357,33 +498,103 @@ int odp_pktio_close(odp_pktio_t hdl)
int res;
entry = get_pktio_entry(hdl);
- if (entry == NULL)
+ if (entry == NULL) {
+ _ODP_ERR("Bad handle\n");
return -1;
+ }
- if (entry->s.state == PKTIO_STATE_STARTED) {
- ODP_DBG("Missing odp_pktio_stop() before close.\n");
+ if (entry->state == PKTIO_STATE_STARTED) {
+ _ODP_DBG("Missing odp_pktio_stop() before close.\n");
return -1;
}
- if (entry->s.state == PKTIO_STATE_STOPPED)
+ if (entry->state == PKTIO_STATE_STOPPED)
flush_in_queues(entry);
lock_entry(entry);
- destroy_in_queues(entry, entry->s.num_in_queue);
- destroy_out_queues(entry, entry->s.num_out_queue);
+ destroy_in_queues(entry, entry->num_in_queue);
+ destroy_out_queues(entry, entry->num_out_queue);
- entry->s.num_in_queue = 0;
- entry->s.num_out_queue = 0;
+ entry->num_in_queue = 0;
+ entry->num_out_queue = 0;
- odp_spinlock_lock(&pktio_tbl->lock);
+ if (entry->tx_compl_pool != ODP_POOL_INVALID) {
+ if (odp_pool_destroy(entry->tx_compl_pool) == -1) {
+ unlock_entry(entry);
+ _ODP_ERR("Unable to destroy Tx event completion pool\n");
+ return -1;
+ }
+ }
+
+ if (entry->tx_compl_status_shm != ODP_SHM_INVALID) {
+ if (odp_shm_free(entry->tx_compl_status_shm) < 0) {
+ unlock_entry(entry);
+ _ODP_ERR("Unable to destroy Tx poll completion SHM\n");
+ return -1;
+ }
+ }
+
+ odp_spinlock_lock(&pktio_global->lock);
res = _pktio_close(entry);
- odp_spinlock_unlock(&pktio_tbl->lock);
+ odp_spinlock_unlock(&pktio_global->lock);
if (res)
- ODP_ABORT("unable to close pktio\n");
+ _ODP_ABORT("unable to close pktio\n");
unlock_entry(entry);
+ _ODP_DBG("interface: %s\n", entry->name);
+
+ return 0;
+}
+
+static int configure_tx_event_compl(pktio_entry_t *entry)
+{
+ odp_pool_param_t params;
+ const char *name_base = "_odp_pktio_tx_compl_";
+ char pool_name[ODP_POOL_NAME_LEN];
+
+ if (entry->tx_compl_pool != ODP_POOL_INVALID)
+ return 0;
+
+ snprintf(pool_name, sizeof(pool_name), "%s%d", name_base, odp_pktio_index(entry->handle));
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_BUFFER;
+ params.buf.num = pktio_global->config.tx_compl_pool_size;
+ params.buf.size = sizeof(_odp_pktio_tx_compl_t);
+ entry->tx_compl_pool = odp_pool_create(pool_name, &params);
+
+ if (entry->tx_compl_pool == ODP_POOL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+static int configure_tx_poll_compl(pktio_entry_t *entry, uint32_t count)
+{
+ odp_shm_t shm;
+ const char *name_base = "_odp_pktio_tx_compl_";
+ char shm_name[ODP_SHM_NAME_LEN];
+
+ if (entry->tx_compl_status_shm != ODP_SHM_INVALID)
+ return 0;
+
+ snprintf(shm_name, sizeof(shm_name), "%s%d", name_base, odp_pktio_index(entry->handle));
+ shm = odp_shm_reserve(shm_name, sizeof(odp_atomic_u32_t) * count, ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ entry->tx_compl_status_shm = shm;
+ entry->tx_compl_status = odp_shm_addr(shm);
+
+ if (entry->tx_compl_status == NULL)
+ return -1;
+
+ for (uint32_t i = 0; i < count; i++)
+ odp_atomic_init_u32(&entry->tx_compl_status[i], 0);
+
return 0;
}
@@ -395,8 +606,10 @@ int odp_pktio_config(odp_pktio_t hdl, const odp_pktio_config_t *config)
int res = 0;
entry = get_pktio_entry(hdl);
- if (!entry)
+ if (!entry) {
+ _ODP_ERR("Bad handle\n");
return -1;
+ }
if (config == NULL) {
odp_pktio_config_init(&default_config);
@@ -408,30 +621,59 @@ int odp_pktio_config(odp_pktio_t hdl, const odp_pktio_config_t *config)
/* Check config for invalid values */
if (config->pktin.all_bits & ~capa.config.pktin.all_bits) {
- ODP_ERR("Unsupported input configuration option\n");
+ _ODP_ERR("Unsupported input configuration option\n");
return -1;
}
if (config->pktout.all_bits & ~capa.config.pktout.all_bits) {
- ODP_ERR("Unsupported output configuration option\n");
+ _ODP_ERR("Unsupported output configuration option\n");
return -1;
}
- if (config->enable_loop && !capa.loop_supported) {
- ODP_ERR("Loopback mode not supported\n");
+ if (config->enable_loop && !capa.config.enable_loop) {
+ _ODP_ERR("Loopback mode not supported\n");
+ return -1;
+ }
+
+ if (config->flow_control.pause_rx != ODP_PKTIO_LINK_PAUSE_OFF ||
+ config->flow_control.pause_tx != ODP_PKTIO_LINK_PAUSE_OFF) {
+ _ODP_ERR("Link flow control is not supported\n");
return -1;
}
lock_entry(entry);
- if (entry->s.state == PKTIO_STATE_STARTED) {
+ if (entry->state == PKTIO_STATE_STARTED) {
unlock_entry(entry);
- ODP_DBG("pktio %s: not stopped\n", entry->s.name);
+ _ODP_DBG("pktio %s: not stopped\n", entry->name);
return -1;
}
- entry->s.config = *config;
+ entry->config = *config;
+
+ entry->enabled.tx_ts = config->pktout.bit.ts_ena;
+ entry->enabled.tx_compl = (config->pktout.bit.tx_compl_ena ||
+ config->tx_compl.mode_event ||
+ config->tx_compl.mode_poll);
+
+ if (entry->enabled.tx_compl) {
+ if ((config->pktout.bit.tx_compl_ena || config->tx_compl.mode_event) &&
+ configure_tx_event_compl(entry)) {
+ unlock_entry(entry);
+ _ODP_ERR("Unable to configure Tx event completion\n");
+ return -1;
+ }
+
+ if (config->tx_compl.mode_poll &&
+ configure_tx_poll_compl(entry, config->tx_compl.max_compl_id + 1)) {
+ unlock_entry(entry);
+ _ODP_ERR("Unable to configure Tx poll completion\n");
+ return -1;
+ }
+ }
+
+ entry->enabled.tx_aging = config->pktout.bit.aging_ena;
- if (entry->s.ops->config)
- res = entry->s.ops->config(entry, config);
+ if (entry->ops->config)
+ res = entry->ops->config(entry, config);
unlock_entry(entry);
@@ -445,38 +687,61 @@ int odp_pktio_start(odp_pktio_t hdl)
int res = 0;
entry = get_pktio_entry(hdl);
- if (!entry)
+ if (!entry) {
+ _ODP_ERR("Bad handle\n");
return -1;
+ }
lock_entry(entry);
- if (entry->s.state == PKTIO_STATE_STARTED) {
+ if (entry->state == PKTIO_STATE_STARTED) {
unlock_entry(entry);
+ _ODP_ERR("Already started\n");
return -1;
}
- if (entry->s.ops->start)
- res = entry->s.ops->start(entry);
+
+ if (entry->state == PKTIO_STATE_STOP_PENDING) {
+ unlock_entry(entry);
+ _ODP_ERR("Scheduled pktio stop pending\n");
+ return -1;
+ }
+
+ entry->parse_layer = pktio_cls_enabled(entry) ?
+ ODP_PROTO_LAYER_ALL :
+ entry->config.parser.layer;
+ if (entry->ops->start)
+ res = entry->ops->start(entry);
if (!res)
- entry->s.state = PKTIO_STATE_STARTED;
+ entry->state = PKTIO_STATE_STARTED;
unlock_entry(entry);
- mode = entry->s.param.in_mode;
+ mode = entry->param.in_mode;
if (mode == ODP_PKTIN_MODE_SCHED) {
- unsigned i;
- unsigned num = entry->s.num_in_queue;
+ uint32_t i;
+ uint32_t num = entry->num_in_queue;
int index[num];
+ odp_queue_t odpq[num];
for (i = 0; i < num; i++) {
index[i] = i;
+ odpq[i] = entry->in_queue[i].queue;
- if (entry->s.in_queue[i].queue == ODP_QUEUE_INVALID) {
- ODP_ERR("No input queue\n");
+ if (entry->in_queue[i].queue == ODP_QUEUE_INVALID) {
+ _ODP_ERR("No input queue\n");
return -1;
}
}
- sched_fn->pktio_start(pktio_to_id(hdl), num, index);
+ _odp_sched_fn->pktio_start(odp_pktio_index(hdl), num, index, odpq);
+ }
+
+ _ODP_DBG("interface: %s, input queues: %u, output queues: %u\n",
+ entry->name, entry->num_in_queue, entry->num_out_queue);
+
+ if (_ODP_PCAPNG) {
+ if (_odp_pcapng_start(entry))
+ _ODP_ERR("pcapng start failed, won't capture\n");
}
return res;
@@ -485,21 +750,26 @@ int odp_pktio_start(odp_pktio_t hdl)
static int _pktio_stop(pktio_entry_t *entry)
{
int res = 0;
- odp_pktin_mode_t mode = entry->s.param.in_mode;
+ odp_pktin_mode_t mode = entry->param.in_mode;
- if (entry->s.state != PKTIO_STATE_STARTED)
+ if (entry->state != PKTIO_STATE_STARTED) {
+ _ODP_ERR("Not started\n");
return -1;
+ }
- if (entry->s.ops->stop)
- res = entry->s.ops->stop(entry);
+ if (entry->ops->stop)
+ res = entry->ops->stop(entry);
if (res)
return -1;
if (mode == ODP_PKTIN_MODE_SCHED)
- entry->s.state = PKTIO_STATE_STOP_PENDING;
+ entry->state = PKTIO_STATE_STOP_PENDING;
else
- entry->s.state = PKTIO_STATE_STOPPED;
+ entry->state = PKTIO_STATE_STOPPED;
+
+ if (_ODP_PCAPNG)
+ _odp_pcapng_stop(entry);
return res;
}
@@ -510,13 +780,17 @@ int odp_pktio_stop(odp_pktio_t hdl)
int res;
entry = get_pktio_entry(hdl);
- if (!entry)
+ if (!entry) {
+ _ODP_ERR("Bad handle\n");
return -1;
+ }
lock_entry(entry);
res = _pktio_stop(entry);
unlock_entry(entry);
+ _ODP_DBG("interface: %s\n", entry->name);
+
return res;
}
@@ -524,19 +798,22 @@ odp_pktio_t odp_pktio_lookup(const char *name)
{
odp_pktio_t hdl = ODP_PKTIO_INVALID;
pktio_entry_t *entry;
+ const char *ifname;
int i;
- odp_spinlock_lock(&pktio_tbl->lock);
+ ifname = strip_pktio_type(name, NULL);
+
+ odp_spinlock_lock(&pktio_global->lock);
- for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
+ for (i = 0; i < CONFIG_PKTIO_ENTRIES; ++i) {
entry = pktio_entry_by_index(i);
if (!entry || is_free(entry))
continue;
lock_entry(entry);
- if (entry->s.state >= PKTIO_STATE_ACTIVE &&
- strncmp(entry->s.name, name, sizeof(entry->s.name)) == 0)
+ if (entry->state >= PKTIO_STATE_ACTIVE &&
+ strncmp(entry->name, ifname, sizeof(entry->name)) == 0)
hdl = _odp_cast_scalar(odp_pktio_t, i + 1);
unlock_entry(entry);
@@ -545,196 +822,237 @@ odp_pktio_t odp_pktio_lookup(const char *name)
break;
}
- odp_spinlock_unlock(&pktio_tbl->lock);
+ odp_spinlock_unlock(&pktio_global->lock);
return hdl;
}
-static inline int pktin_recv_buf(odp_pktin_queue_t queue,
- odp_buffer_hdr_t *buffer_hdrs[], int num)
+static inline odp_packet_vector_t packet_vector_create(odp_packet_t packets[], uint32_t num,
+ odp_pool_t pool)
{
- odp_packet_t pkt;
- odp_packet_t packets[num];
- odp_packet_hdr_t *pkt_hdr;
- odp_buffer_hdr_t *buf_hdr;
- int i;
- int pkts;
- int num_rx = 0;
+ odp_packet_vector_t pktv;
+ odp_packet_t *pkt_tbl;
+ uint32_t i;
- pkts = odp_pktin_recv(queue, packets, num);
+ pktv = odp_packet_vector_alloc(pool);
+ if (odp_unlikely(pktv == ODP_PACKET_VECTOR_INVALID)) {
+ odp_packet_free_multi(packets, num);
+ return ODP_PACKET_VECTOR_INVALID;
+ }
- for (i = 0; i < pkts; i++) {
- pkt = packets[i];
- pkt_hdr = odp_packet_hdr(pkt);
- buf_hdr = packet_to_buf_hdr(pkt);
+ odp_packet_vector_tbl(pktv, &pkt_tbl);
+ for (i = 0; i < num; i++)
+ pkt_tbl[i] = packets[i];
+ odp_packet_vector_size_set(pktv, num);
- if (pkt_hdr->p.input_flags.dst_queue) {
- int ret;
+ return pktv;
+}
- ret = queue_fn->enq(pkt_hdr->dst_queue, buf_hdr);
- if (ret < 0)
- odp_packet_free(pkt);
- continue;
- }
- buffer_hdrs[num_rx++] = buf_hdr;
+static inline int pktin_recv_buf(pktio_entry_t *entry, int pktin_index,
+ _odp_event_hdr_t *event_hdrs[], int num)
+{
+ const odp_bool_t vector_enabled = entry->in_queue[pktin_index].vector.enable;
+ odp_pool_t pool = ODP_POOL_INVALID;
+ int num_rx;
+
+ if (vector_enabled) {
+ /* Make sure all packets will fit into a single packet vector */
+ if ((int)entry->in_queue[pktin_index].vector.max_size < num)
+ num = entry->in_queue[pktin_index].vector.max_size;
+ pool = entry->in_queue[pktin_index].vector.pool;
}
- return num_rx;
+
+ num_rx = entry->ops->recv(entry, pktin_index, (odp_packet_t *)event_hdrs, num);
+
+ if (!vector_enabled || num_rx < 2)
+ return num_rx;
+
+ odp_packet_vector_t pktv = packet_vector_create((odp_packet_t *)event_hdrs, num_rx, pool);
+
+ if (odp_unlikely(pktv == ODP_PACKET_VECTOR_INVALID))
+ return 0;
+
+ event_hdrs[0] = _odp_packet_vector_to_event_hdr(pktv);
+ return 1;
}
-static int pktout_enqueue(queue_t q_int, odp_buffer_hdr_t *buf_hdr)
+static int pktout_enqueue(odp_queue_t queue, _odp_event_hdr_t *event_hdr)
{
- odp_packet_t pkt = packet_from_buf_hdr(buf_hdr);
- int len = 1;
+ odp_packet_t pkt = packet_from_event_hdr(event_hdr);
int nbr;
- if (sched_fn->ord_enq_multi(q_int, (void **)buf_hdr, len, &nbr))
- return (nbr == len ? 0 : -1);
+ _ODP_ASSERT(odp_event_type(_odp_event_from_hdr(event_hdr)) == ODP_EVENT_PACKET);
+
+ if (_odp_sched_fn->ord_enq_multi(queue, (void **)event_hdr, 1, &nbr))
+ return (nbr == 1 ? 0 : -1);
- nbr = odp_pktout_send(queue_fn->get_pktout(q_int), &pkt, len);
- return (nbr == len ? 0 : -1);
+ nbr = odp_pktout_send(_odp_queue_fn->get_pktout(queue), &pkt, 1);
+ return (nbr == 1 ? 0 : -1);
}
-static int pktout_enq_multi(queue_t q_int, odp_buffer_hdr_t *buf_hdr[], int num)
+static int pktout_enq_multi(odp_queue_t queue, _odp_event_hdr_t *event_hdr[],
+ int num)
{
- odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
int nbr;
- int i;
- if (sched_fn->ord_enq_multi(q_int, (void **)buf_hdr, num, &nbr))
- return nbr;
+ if (ODP_DEBUG) {
+ for (int i = 0; i < num; i++)
+ _ODP_ASSERT(odp_event_type(_odp_event_from_hdr(event_hdr[i])) ==
+ ODP_EVENT_PACKET);
+ }
- for (i = 0; i < num; ++i)
- pkt_tbl[i] = packet_from_buf_hdr(buf_hdr[i]);
+ if (_odp_sched_fn->ord_enq_multi(queue, (void **)event_hdr, num, &nbr))
+ return nbr;
- nbr = odp_pktout_send(queue_fn->get_pktout(q_int), pkt_tbl, num);
- return nbr;
+ return odp_pktout_send(_odp_queue_fn->get_pktout(queue), (odp_packet_t *)event_hdr, num);
}
-static odp_buffer_hdr_t *pktin_dequeue(queue_t q_int)
+static _odp_event_hdr_t *pktin_dequeue(odp_queue_t queue)
{
- odp_buffer_hdr_t *buf_hdr;
- odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
+ _odp_event_hdr_t *event_hdr;
+ _odp_event_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
int pkts;
+ odp_pktin_queue_t pktin_queue = _odp_queue_fn->get_pktin(queue);
+ odp_pktio_t pktio = pktin_queue.pktio;
+ int pktin_index = pktin_queue.index;
+ pktio_entry_t *entry = get_pktio_entry(pktio);
+
+ _ODP_ASSERT(entry != NULL);
- buf_hdr = queue_fn->deq(q_int);
- if (buf_hdr != NULL)
- return buf_hdr;
+ if (_odp_queue_fn->orig_deq_multi(queue, &event_hdr, 1) == 1)
+ return event_hdr;
- pkts = pktin_recv_buf(queue_fn->get_pktin(q_int),
- hdr_tbl, QUEUE_MULTI_MAX);
+ if (odp_unlikely(entry->state != PKTIO_STATE_STARTED))
+ return 0;
+
+ pkts = pktin_recv_buf(entry, pktin_index, hdr_tbl, QUEUE_MULTI_MAX);
if (pkts <= 0)
return NULL;
- if (pkts > 1)
- queue_fn->enq_multi(q_int, &hdr_tbl[1], pkts - 1);
- buf_hdr = hdr_tbl[0];
- return buf_hdr;
+ if (pkts > 1) {
+ int num_enq;
+ int num = pkts - 1;
+
+ num_enq = odp_queue_enq_multi(queue,
+ (odp_event_t *)&hdr_tbl[1], num);
+
+ if (odp_unlikely(num_enq < num)) {
+ if (odp_unlikely(num_enq < 0))
+ num_enq = 0;
+
+ _ODP_DBG("Interface %s dropped %i packets\n", entry->name, num - num_enq);
+ _odp_event_free_multi(&hdr_tbl[num_enq + 1], num - num_enq);
+ }
+ }
+
+ event_hdr = hdr_tbl[0];
+ return event_hdr;
}
-static int pktin_deq_multi(queue_t q_int, odp_buffer_hdr_t *buf_hdr[], int num)
+static int pktin_deq_multi(odp_queue_t queue, _odp_event_hdr_t *event_hdr[],
+ int num)
{
int nbr;
- odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
+ _odp_event_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
int pkts, i, j;
+ odp_pktin_queue_t pktin_queue = _odp_queue_fn->get_pktin(queue);
+ odp_pktio_t pktio = pktin_queue.pktio;
+ int pktin_index = pktin_queue.index;
+ pktio_entry_t *entry = get_pktio_entry(pktio);
+
+ _ODP_ASSERT(entry != NULL);
- nbr = queue_fn->deq_multi(q_int, buf_hdr, num);
+ nbr = _odp_queue_fn->orig_deq_multi(queue, event_hdr, num);
if (odp_unlikely(nbr > num))
- ODP_ABORT("queue_deq_multi req: %d, returned %d\n", num, nbr);
+ _ODP_ABORT("queue_deq_multi req: %d, returned %d\n", num, nbr);
- /** queue already has number of requsted buffers,
+ /** queue already has number of requested buffers,
* do not do receive in that case.
*/
- if (nbr == num)
+ if (nbr == num || odp_unlikely(entry->state != PKTIO_STATE_STARTED))
return nbr;
- pkts = pktin_recv_buf(queue_fn->get_pktin(q_int),
- hdr_tbl, QUEUE_MULTI_MAX);
+ pkts = pktin_recv_buf(entry, pktin_index, hdr_tbl, QUEUE_MULTI_MAX);
+
if (pkts <= 0)
return nbr;
for (i = 0; i < pkts && nbr < num; i++, nbr++)
- buf_hdr[nbr] = hdr_tbl[i];
+ event_hdr[nbr] = hdr_tbl[i];
/* Queue the rest for later */
for (j = 0; i < pkts; i++, j++)
hdr_tbl[j] = hdr_tbl[i];
- if (j)
- queue_fn->enq_multi(q_int, hdr_tbl, j);
+ if (j) {
+ int num_enq;
+
+ num_enq = odp_queue_enq_multi(queue, (odp_event_t *)hdr_tbl, j);
+
+ if (odp_unlikely(num_enq < j)) {
+ if (odp_unlikely(num_enq < 0))
+ num_enq = 0;
+
+ _ODP_DBG("Interface %s dropped %i packets\n",
+ entry->name, j - num_enq);
+ _odp_event_free_multi(&event_hdr[num_enq], j - num_enq);
+ }
+ }
+
return nbr;
}
-int sched_cb_pktin_poll(int pktio_index, int num_queue, int index[])
+int _odp_sched_cb_pktin_poll(int pktio_index, int pktin_index,
+ _odp_event_hdr_t *hdr_tbl[], int num)
{
- odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
- int num, idx;
- pktio_entry_t *entry;
- entry = pktio_entry_by_index(pktio_index);
- int state = entry->s.state;
+ pktio_entry_t *entry = pktio_entry_by_index(pktio_index);
+ int state = entry->state;
if (odp_unlikely(state != PKTIO_STATE_STARTED)) {
if (state < PKTIO_STATE_ACTIVE ||
state == PKTIO_STATE_STOP_PENDING)
return -1;
- ODP_DBG("interface not started\n");
+ _ODP_DBG("Interface %s not started\n", entry->name);
return 0;
}
- for (idx = 0; idx < num_queue; idx++) {
- queue_t q_int;
- odp_pktin_queue_t pktin = entry->s.in_queue[index[idx]].pktin;
-
- num = pktin_recv_buf(pktin, hdr_tbl, QUEUE_MULTI_MAX);
-
- if (num == 0)
- continue;
-
- if (num < 0) {
- ODP_ERR("Packet recv error\n");
- return -1;
- }
-
- q_int = entry->s.in_queue[index[idx]].queue_int;
- queue_fn->enq_multi(q_int, hdr_tbl, num);
- }
-
- return 0;
+ return pktin_recv_buf(entry, pktin_index, hdr_tbl, num);
}
-void sched_cb_pktio_stop_finalize(int pktio_index)
+void _odp_sched_cb_pktio_stop_finalize(int pktio_index)
{
int state;
pktio_entry_t *entry = pktio_entry_by_index(pktio_index);
lock_entry(entry);
- state = entry->s.state;
+ state = entry->state;
if (state != PKTIO_STATE_STOP_PENDING &&
state != PKTIO_STATE_CLOSE_PENDING) {
unlock_entry(entry);
- ODP_ERR("Not in a pending state %i\n", state);
+ _ODP_ERR("Not in a pending state %i\n", state);
return;
}
if (state == PKTIO_STATE_STOP_PENDING)
- entry->s.state = PKTIO_STATE_STOPPED;
+ entry->state = PKTIO_STATE_STOPPED;
else
- entry->s.state = PKTIO_STATE_FREE;
+ entry->state = PKTIO_STATE_FREE;
unlock_entry(entry);
}
-uint32_t odp_pktio_mtu(odp_pktio_t hdl)
+static inline uint32_t pktio_maxlen(odp_pktio_t hdl)
{
pktio_entry_t *entry;
uint32_t ret = 0;
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return 0;
}
@@ -742,13 +1060,89 @@ uint32_t odp_pktio_mtu(odp_pktio_t hdl)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return 0;
}
- if (entry->s.ops->mtu_get)
- ret = entry->s.ops->mtu_get(entry);
+ if (entry->ops->maxlen_get)
+ ret = entry->ops->maxlen_get(entry);
+
+ unlock_entry(entry);
+ return ret;
+}
+
+uint32_t odp_pktin_maxlen(odp_pktio_t pktio)
+{
+ return pktio_maxlen(pktio);
+}
+
+uint32_t odp_pktout_maxlen(odp_pktio_t pktio)
+{
+ return pktio_maxlen(pktio);
+}
+
+int odp_pktio_maxlen_set(odp_pktio_t hdl, uint32_t maxlen_input,
+ uint32_t maxlen_output)
+{
+ odp_pktio_capability_t capa;
+ pktio_entry_t *entry;
+ int ret = 0;
+
+ entry = get_pktio_entry(hdl);
+ if (entry == NULL) {
+ _ODP_ERR("Pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
+ return -1;
+ }
+
+ ret = odp_pktio_capability(hdl, &capa);
+ if (ret) {
+ _ODP_ERR("Reading pktio capability failed\n");
+ goto fail;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ _ODP_ERR("Pktio already freed\n");
+ ret = -1;
+ goto fail;
+ }
+ if (entry->state == PKTIO_STATE_STARTED) {
+ _ODP_ERR("Pktio not stopped\n");
+ ret = -1;
+ goto fail;
+ }
+
+ if (capa.set_op.op.maxlen == 0) {
+ _ODP_ERR("Setting maximum frame length not supported\n");
+ ret = -1;
+ goto fail;
+ }
+
+ if (capa.maxlen.equal && (maxlen_input != maxlen_output)) {
+ _ODP_ERR("Max input and output lengths don't match\n");
+ ret = -1;
+ goto fail;
+ }
+
+ if (maxlen_input < capa.maxlen.min_input ||
+ maxlen_input > capa.maxlen.max_input) {
+ _ODP_ERR("Invalid max input length value: %" PRIu32 "\n", maxlen_input);
+ ret = -1;
+ goto fail;
+ }
+ if (maxlen_output < capa.maxlen.min_output ||
+ maxlen_output > capa.maxlen.max_output) {
+ _ODP_ERR("Invalid max output length value: %" PRIu32 "\n", maxlen_output);
+ ret = -1;
+ goto fail;
+ }
+
+ if (entry->ops->maxlen_set)
+ ret = entry->ops->maxlen_set(entry, maxlen_input, maxlen_output);
+
+fail:
unlock_entry(entry);
return ret;
}
@@ -760,7 +1154,7 @@ int odp_pktio_promisc_mode_set(odp_pktio_t hdl, odp_bool_t enable)
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
}
@@ -768,16 +1162,16 @@ int odp_pktio_promisc_mode_set(odp_pktio_t hdl, odp_bool_t enable)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return -1;
}
- if (entry->s.state == PKTIO_STATE_STARTED) {
+ if (entry->state == PKTIO_STATE_STARTED) {
unlock_entry(entry);
return -1;
}
- if (entry->s.ops->promisc_mode_set)
- ret = entry->s.ops->promisc_mode_set(entry, enable);
+ if (entry->ops->promisc_mode_set)
+ ret = entry->ops->promisc_mode_set(entry, enable);
unlock_entry(entry);
return ret;
@@ -790,7 +1184,7 @@ int odp_pktio_promisc_mode(odp_pktio_t hdl)
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
}
@@ -798,12 +1192,12 @@ int odp_pktio_promisc_mode(odp_pktio_t hdl)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return -1;
}
- if (entry->s.ops->promisc_mode_get)
- ret = entry->s.ops->promisc_mode_get(entry);
+ if (entry->ops->promisc_mode_get)
+ ret = entry->ops->promisc_mode_get(entry);
unlock_entry(entry);
return ret;
@@ -821,7 +1215,7 @@ int odp_pktio_mac_addr(odp_pktio_t hdl, void *mac_addr, int addr_size)
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
}
@@ -829,14 +1223,14 @@ int odp_pktio_mac_addr(odp_pktio_t hdl, void *mac_addr, int addr_size)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return -1;
}
- if (entry->s.ops->mac_get) {
- ret = entry->s.ops->mac_get(entry, mac_addr);
+ if (entry->ops->mac_get) {
+ ret = entry->ops->mac_get(entry, mac_addr);
} else {
- ODP_DBG("pktio does not support mac addr get\n");
+ _ODP_DBG("pktio does not support mac addr get\n");
ret = -1;
}
unlock_entry(entry);
@@ -844,14 +1238,19 @@ int odp_pktio_mac_addr(odp_pktio_t hdl, void *mac_addr, int addr_size)
return ret;
}
-int odp_pktio_link_status(odp_pktio_t hdl)
+int odp_pktio_mac_addr_set(odp_pktio_t hdl, const void *mac_addr, int addr_size)
{
pktio_entry_t *entry;
int ret = -1;
+ if (addr_size < ETH_ALEN) {
+ /* Input buffer too small */
+ return -1;
+ }
+
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
}
@@ -859,12 +1258,43 @@ int odp_pktio_link_status(odp_pktio_t hdl)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
+ return -1;
+ }
+
+ if (entry->state == PKTIO_STATE_STARTED) {
+ unlock_entry(entry);
return -1;
}
- if (entry->s.ops->link_status)
- ret = entry->s.ops->link_status(entry);
+ if (entry->ops->mac_set)
+ ret = entry->ops->mac_set(entry, mac_addr);
+
+ unlock_entry(entry);
+ return ret;
+}
+
+odp_pktio_link_status_t odp_pktio_link_status(odp_pktio_t hdl)
+{
+ pktio_entry_t *entry;
+ int ret = ODP_PKTIO_LINK_STATUS_UNKNOWN;
+
+ entry = get_pktio_entry(hdl);
+ if (entry == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
+ return ODP_PKTIO_LINK_STATUS_UNKNOWN;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_DBG("already freed pktio\n");
+ return ODP_PKTIO_LINK_STATUS_UNKNOWN;
+ }
+
+ if (entry->ops->link_status)
+ ret = entry->ops->link_status(entry);
unlock_entry(entry);
return ret;
@@ -897,7 +1327,10 @@ void odp_pktio_config_init(odp_pktio_config_t *config)
{
memset(config, 0, sizeof(odp_pktio_config_t));
- config->parser.layer = ODP_PKTIO_PARSER_LAYER_ALL;
+ config->parser.layer = ODP_PROTO_LAYER_ALL;
+ config->reassembly.max_num_frags = 2;
+ config->flow_control.pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ config->flow_control.pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
}
int odp_pktio_info(odp_pktio_t hdl, odp_pktio_info_t *info)
@@ -907,63 +1340,93 @@ int odp_pktio_info(odp_pktio_t hdl, odp_pktio_info_t *info)
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
}
memset(info, 0, sizeof(odp_pktio_info_t));
- info->name = entry->s.name;
- info->drv_name = entry->s.ops->name;
- info->pool = entry->s.pool;
- memcpy(&info->param, &entry->s.param, sizeof(odp_pktio_param_t));
+ info->name = entry->full_name;
+ info->drv_name = entry->ops->name;
+ info->pool = entry->pool;
+ memcpy(&info->param, &entry->param, sizeof(odp_pktio_param_t));
return 0;
}
-int odp_pktio_index(odp_pktio_t pktio)
+int odp_pktio_link_info(odp_pktio_t hdl, odp_pktio_link_info_t *info)
{
- pktio_entry_t *entry = get_pktio_entry(pktio);
+ pktio_entry_t *entry;
+
+ entry = get_pktio_entry(hdl);
- if (!entry || is_free(entry))
+ if (entry == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return -1;
+ }
+
+ if (entry->ops->link_info)
+ return entry->ops->link_info(entry, info);
- return pktio_to_id(pktio);
+ return -1;
}
-uint64_t odp_pktin_ts_res(odp_pktio_t hdl)
+uint64_t odp_pktio_ts_res(odp_pktio_t hdl)
{
pktio_entry_t *entry;
entry = get_pktio_entry(hdl);
-
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return 0;
}
- if (entry->s.ops->pktin_ts_res)
- return entry->s.ops->pktin_ts_res(entry);
+ if (entry->ops->pktio_ts_res)
+ return entry->ops->pktio_ts_res(entry);
return odp_time_global_res();
}
-odp_time_t odp_pktin_ts_from_ns(odp_pktio_t hdl, uint64_t ns)
+odp_time_t odp_pktio_ts_from_ns(odp_pktio_t hdl, uint64_t ns)
{
pktio_entry_t *entry;
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return ODP_TIME_NULL;
}
- if (entry->s.ops->pktin_ts_from_ns)
- return entry->s.ops->pktin_ts_from_ns(entry, ns);
+ if (entry->ops->pktio_ts_from_ns)
+ return entry->ops->pktio_ts_from_ns(entry, ns);
return odp_time_global_from_ns(ns);
}
+odp_time_t odp_pktio_time(odp_pktio_t hdl, odp_time_t *global_ts)
+{
+ pktio_entry_t *entry;
+ odp_time_t ts;
+
+ entry = get_pktio_entry(hdl);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
+ return ODP_TIME_NULL;
+ }
+
+ /* Callback if present */
+ if (entry->ops->pktio_time)
+ return entry->ops->pktio_time(entry, global_ts);
+
+ /* By default both Packet IO time source and
+ * global time source are same.
+ */
+ ts = odp_time_global();
+ if (global_ts)
+ *global_ts = ts;
+ return ts;
+}
+
void odp_pktio_print(odp_pktio_t hdl)
{
pktio_entry_t *entry;
@@ -976,35 +1439,42 @@ void odp_pktio_print(odp_pktio_t hdl)
entry = get_pktio_entry(hdl);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", hdl);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
return;
}
+ len += snprintf(&str[len], n - len, "Pktio info\n----------\n");
len += snprintf(&str[len], n - len,
- "pktio\n");
+ " name %s\n", entry->name);
len += snprintf(&str[len], n - len,
- " handle %" PRIu64 "\n",
- odp_pktio_to_u64(hdl));
+ " type %s\n", entry->ops->name);
len += snprintf(&str[len], n - len,
- " name %s\n", entry->s.name);
+ " index %i\n", odp_pktio_index(hdl));
len += snprintf(&str[len], n - len,
- " type %s\n", entry->s.ops->name);
+ " handle 0x%" PRIx64 "\n",
+ odp_pktio_to_u64(hdl));
+ len += snprintf(&str[len], n - len,
+ " pool handle 0x%" PRIx64 "\n",
+ odp_pool_to_u64(entry->pool));
len += snprintf(&str[len], n - len,
" state %s\n",
- entry->s.state == PKTIO_STATE_STARTED ? "start" :
- (entry->s.state == PKTIO_STATE_STOPPED ? "stop" :
- (entry->s.state == PKTIO_STATE_STOP_PENDING ?
+ entry->state == PKTIO_STATE_STARTED ? "start" :
+ (entry->state == PKTIO_STATE_STOPPED ? "stop" :
+ (entry->state == PKTIO_STATE_STOP_PENDING ?
"stop pending" :
- (entry->s.state == PKTIO_STATE_OPENED ? "opened" :
- "unknown"))));
+ (entry->state == PKTIO_STATE_OPENED ? "opened" :
+ "unknown"))));
memset(addr, 0, sizeof(addr));
odp_pktio_mac_addr(hdl, addr, ETH_ALEN);
len += snprintf(&str[len], n - len,
" mac %02x:%02x:%02x:%02x:%02x:%02x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
len += snprintf(&str[len], n - len,
- " mtu %" PRIu32 "\n",
- odp_pktio_mtu(hdl));
+ " pktin maxlen %" PRIu32 "\n",
+ odp_pktin_maxlen(hdl));
+ len += snprintf(&str[len], n - len,
+ " pktout maxlen %" PRIu32 "\n",
+ odp_pktout_maxlen(hdl));
len += snprintf(&str[len], n - len,
" promisc %s\n",
odp_pktio_promisc_mode(hdl) ? "yes" : "no");
@@ -1018,95 +1488,146 @@ void odp_pktio_print(odp_pktio_t hdl)
str[len] = '\0';
- ODP_PRINT("\n%s", str);
+ _ODP_PRINT("\n%s", str);
- if (entry->s.ops->print)
- entry->s.ops->print(entry);
+ if (entry->ops->print)
+ entry->ops->print(entry);
- ODP_PRINT("\n");
+ _ODP_PRINT("\n");
}
-int odp_pktio_term_global(void)
+int _odp_pktio_term_global(void)
{
+ odp_shm_t shm;
+ int i, pktio_if;
int ret = 0;
- int i;
- int pktio_if;
- for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
+ if (pktio_global == NULL)
+ return 0;
+
+ for (i = 0; i < CONFIG_PKTIO_ENTRIES; ++i) {
pktio_entry_t *pktio_entry;
- pktio_entry = &pktio_tbl->entries[i];
+ pktio_entry = &pktio_global->entries[i];
if (is_free(pktio_entry))
continue;
lock_entry(pktio_entry);
- if (pktio_entry->s.state == PKTIO_STATE_STARTED) {
+ if (pktio_entry->state == PKTIO_STATE_STARTED) {
ret = _pktio_stop(pktio_entry);
if (ret)
- ODP_ABORT("unable to stop pktio %s\n",
- pktio_entry->s.name);
+ _ODP_ABORT("unable to stop pktio %s\n", pktio_entry->name);
}
- if (pktio_entry->s.state != PKTIO_STATE_CLOSE_PENDING)
+ if (pktio_entry->state != PKTIO_STATE_CLOSE_PENDING)
ret = _pktio_close(pktio_entry);
if (ret)
- ODP_ABORT("unable to close pktio %s\n",
- pktio_entry->s.name);
+ _ODP_ABORT("unable to close pktio %s\n", pktio_entry->name);
unlock_entry(pktio_entry);
}
- for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
- if (pktio_if_ops[pktio_if]->term)
- if (pktio_if_ops[pktio_if]->term())
- ODP_ABORT("failed to terminate pktio type %d",
- pktio_if);
+ for (pktio_if = 0; _odp_pktio_if_ops[pktio_if]; ++pktio_if) {
+ if (_odp_pktio_if_ops[pktio_if]->term)
+ if (_odp_pktio_if_ops[pktio_if]->term())
+ _ODP_ABORT("failed to terminate pktio type %d", pktio_if);
}
- ret = odp_shm_free(odp_shm_lookup("odp_pktio_entries"));
+ if (_ODP_PCAPNG) {
+ ret = _odp_pcapng_term_global();
+ if (ret)
+ _ODP_ERR("Failed to terminate pcapng\n");
+ }
+
+ shm = pktio_global->shm;
+ ret = odp_shm_free(shm);
if (ret != 0)
- ODP_ERR("shm free failed for odp_pktio_entries");
+ _ODP_ERR("shm free failed\n");
return ret;
}
-static
-int single_capability(odp_pktio_capability_t *capa)
-{
- memset(capa, 0, sizeof(odp_pktio_capability_t));
- capa->max_input_queues = 1;
- capa->max_output_queues = 1;
- capa->set_op.op.promisc_mode = 1;
-
- return 0;
-}
-
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
{
pktio_entry_t *entry;
int ret;
+ uint32_t mtu;
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- if (entry->s.ops->capability)
- ret = entry->s.ops->capability(entry, capa);
- else
- ret = single_capability(capa);
+ ret = entry->ops->capability(entry, capa);
+
+ if (ret) {
+ _ODP_ERR("Driver specific capa query failed: %s\n", entry->name);
+ return -1;
+ }
+
+ mtu = pktio_maxlen(pktio);
+
+ if (mtu == 0) {
+ _ODP_ERR("MTU query failed: %s\n", entry->name);
+ return -1;
+ }
/* The same parser is used for all pktios */
- if (ret == 0)
- capa->config.parser.layer = ODP_PKTIO_PARSER_LAYER_ALL;
+ capa->config.parser.layer = ODP_PROTO_LAYER_ALL;
+ /* Header skip is not supported */
+ capa->set_op.op.skip_offset = 0;
+ /* Irrespective of whether we optimize the fast path or not,
+ * we can report that it is supported.
+ */
+ capa->config.pktout.bit.no_packet_refs = 1;
+
+ /* LSO implementation is common to all pktios */
+ capa->lso.max_profiles = PKTIO_LSO_PROFILES;
+ capa->lso.max_profiles_per_pktio = PKTIO_LSO_PROFILES;
+ capa->lso.max_packet_segments = PKT_MAX_SEGS;
+ capa->lso.max_segments = PKTIO_LSO_MAX_SEGMENTS;
+ capa->lso.max_payload_len = mtu - PKTIO_LSO_MIN_PAYLOAD_OFFSET;
+ capa->lso.max_payload_offset = PKTIO_LSO_MAX_PAYLOAD_OFFSET;
+ capa->lso.max_num_custom = ODP_LSO_MAX_CUSTOM;
+ capa->lso.proto.ipv4 = 1;
+ capa->lso.proto.custom = 1;
+ capa->lso.mod_op.add_segment_num = 1;
+
+ capa->tx_compl.queue_type_sched = 1;
+ capa->tx_compl.queue_type_plain = 1;
+ capa->tx_compl.max_compl_id = UINT32_MAX - 1;
+
+ capa->config.pktout.bit.aging_ena = 1;
+ capa->max_tx_aging_tmo_ns = MAX_TX_AGING_TMO_NS;
+
+ /* Packet vector generation is common for all pktio types */
+ if (entry->param.in_mode == ODP_PKTIN_MODE_QUEUE ||
+ entry->param.in_mode == ODP_PKTIN_MODE_SCHED) {
+ capa->vector.supported = ODP_SUPPORT_YES;
+ capa->vector.max_size = CONFIG_PACKET_VECTOR_MAX_SIZE;
+ capa->vector.min_size = 1;
+ capa->vector.max_tmo_ns = 0;
+ capa->vector.min_tmo_ns = 0;
+ }
+
+ capa->reassembly.ip = false;
+ capa->reassembly.ipv4 = false;
+ capa->reassembly.ipv6 = false;
+ capa->flow_control.pause_rx = 0;
+ capa->flow_control.pfc_rx = 0;
+ capa->flow_control.pause_tx = 0;
+ capa->flow_control.pfc_tx = 0;
- return ret;
+ return 0;
}
-unsigned odp_pktio_max_index(void)
+ODP_STATIC_ASSERT(CONFIG_PKTIO_ENTRIES - 1 <= ODP_PKTIO_MAX_INDEX,
+ "CONFIG_PKTIO_ENTRIES larger than ODP_PKTIO_MAX_INDEX");
+
+unsigned int odp_pktio_max_index(void)
{
- return ODP_CONFIG_PKTIO_ENTRIES - 1;
+ return CONFIG_PKTIO_ENTRIES - 1;
}
int odp_pktio_stats(odp_pktio_t pktio,
@@ -1117,7 +1638,7 @@ int odp_pktio_stats(odp_pktio_t pktio,
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
@@ -1125,12 +1646,17 @@ int odp_pktio_stats(odp_pktio_t pktio,
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return -1;
}
- if (entry->s.ops->stats)
- ret = entry->s.ops->stats(entry, stats);
+ if (entry->ops->stats)
+ ret = entry->ops->stats(entry, stats);
+ if (odp_likely(ret == 0)) {
+ stats->in_discards += odp_atomic_load_u64(&entry->stats_extra.in_discards);
+ stats->in_errors += odp_atomic_load_u64(&entry->stats_extra.in_errors);
+ stats->out_discards += odp_atomic_load_u64(&entry->stats_extra.out_discards);
+ }
unlock_entry(entry);
return ret;
@@ -1143,7 +1669,7 @@ int odp_pktio_stats_reset(odp_pktio_t pktio)
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
@@ -1151,58 +1677,300 @@ int odp_pktio_stats_reset(odp_pktio_t pktio)
if (odp_unlikely(is_free(entry))) {
unlock_entry(entry);
- ODP_DBG("already freed pktio\n");
+ _ODP_DBG("already freed pktio\n");
return -1;
}
- if (entry->s.ops->stats)
- ret = entry->s.ops->stats_reset(entry);
+ odp_atomic_store_u64(&entry->stats_extra.in_discards, 0);
+ odp_atomic_store_u64(&entry->stats_extra.in_errors, 0);
+ odp_atomic_store_u64(&entry->stats_extra.out_discards, 0);
+ if (entry->ops->stats)
+ ret = entry->ops->stats_reset(entry);
unlock_entry(entry);
return ret;
}
-static int abort_pktin_enqueue(queue_t q_int ODP_UNUSED,
- odp_buffer_hdr_t *buf_hdr ODP_UNUSED)
+int odp_pktin_queue_stats(odp_pktin_queue_t queue,
+ odp_pktin_queue_stats_t *stats)
{
- ODP_ABORT("attempted enqueue to a pktin queue");
- return -1;
+ pktio_entry_t *entry;
+ odp_pktin_mode_t mode;
+ int ret = -1;
+
+ entry = get_pktio_entry(queue.pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)queue.pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("pktio entry already freed\n");
+ return -1;
+ }
+
+ mode = entry->param.in_mode;
+ if (odp_unlikely(mode != ODP_PKTIN_MODE_DIRECT)) {
+ unlock_entry(entry);
+ _ODP_ERR("invalid packet input mode: %d\n", mode);
+ return -1;
+ }
+
+ if (entry->ops->pktin_queue_stats)
+ ret = entry->ops->pktin_queue_stats(entry, queue.index, stats);
+
+ unlock_entry(entry);
+
+ return ret;
}
-static int abort_pktin_enq_multi(queue_t q_int ODP_UNUSED,
- odp_buffer_hdr_t *buf_hdr[] ODP_UNUSED,
- int num ODP_UNUSED)
+int odp_pktin_event_queue_stats(odp_pktio_t pktio, odp_queue_t queue,
+ odp_pktin_queue_stats_t *stats)
{
- ODP_ABORT("attempted enqueue to a pktin queue");
- return 0;
+ pktio_entry_t *entry;
+ odp_pktin_mode_t mode;
+ odp_pktin_queue_t pktin_queue;
+ int ret = -1;
+
+ entry = get_pktio_entry(pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("pktio entry already freed\n");
+ return -1;
+ }
+
+ mode = entry->param.in_mode;
+ if (odp_unlikely(mode != ODP_PKTIN_MODE_SCHED && mode != ODP_PKTIN_MODE_QUEUE)) {
+ unlock_entry(entry);
+ _ODP_ERR("invalid packet input mode: %d\n", mode);
+ return -1;
+ }
+
+ pktin_queue = _odp_queue_fn->get_pktin(queue);
+
+ if (entry->ops->pktin_queue_stats)
+ ret = entry->ops->pktin_queue_stats(entry, pktin_queue.index, stats);
+
+ unlock_entry(entry);
+
+ return ret;
}
-static odp_buffer_hdr_t *abort_pktout_dequeue(queue_t q_int ODP_UNUSED)
+int odp_pktout_queue_stats(odp_pktout_queue_t queue,
+ odp_pktout_queue_stats_t *stats)
{
- ODP_ABORT("attempted dequeue from a pktout queue");
- return NULL;
+ pktio_entry_t *entry;
+ odp_pktout_mode_t mode;
+ int ret = -1;
+
+ entry = get_pktio_entry(queue.pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)queue.pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("pktio entry already freed\n");
+ return -1;
+ }
+
+ mode = entry->param.out_mode;
+ if (odp_unlikely(mode != ODP_PKTOUT_MODE_DIRECT)) {
+ unlock_entry(entry);
+ _ODP_ERR("invalid packet output mode: %d\n", mode);
+ return -1;
+ }
+
+ if (entry->ops->pktout_queue_stats)
+ ret = entry->ops->pktout_queue_stats(entry, queue.index, stats);
+
+ unlock_entry(entry);
+
+ return ret;
}
-static int abort_pktout_deq_multi(queue_t q_int ODP_UNUSED,
- odp_buffer_hdr_t *buf_hdr[] ODP_UNUSED,
- int num ODP_UNUSED)
+int odp_pktout_event_queue_stats(odp_pktio_t pktio, odp_queue_t queue,
+ odp_pktout_queue_stats_t *stats)
{
- ODP_ABORT("attempted dequeue from a pktout queue");
- return 0;
+ pktio_entry_t *entry;
+ odp_pktout_mode_t mode;
+ odp_pktout_queue_t pktout_queue;
+ int ret = -1;
+
+ entry = get_pktio_entry(pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("pktio entry already freed\n");
+ return -1;
+ }
+
+ mode = entry->param.out_mode;
+ if (odp_unlikely(mode != ODP_PKTOUT_MODE_QUEUE)) {
+ unlock_entry(entry);
+ _ODP_ERR("invalid packet output mode: %d\n", mode);
+ return -1;
+ }
+
+ pktout_queue = _odp_queue_fn->get_pktout(queue);
+
+ if (entry->ops->pktout_queue_stats)
+ ret = entry->ops->pktout_queue_stats(entry, pktout_queue.index, stats);
+
+ unlock_entry(entry);
+
+ return ret;
+}
+
+int odp_pktio_extra_stat_info(odp_pktio_t pktio,
+ odp_pktio_extra_stat_info_t info[], int num)
+{
+ pktio_entry_t *entry;
+ int ret = 0;
+
+ entry = get_pktio_entry(pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("already freed pktio\n");
+ return -1;
+ }
+
+ if (entry->ops->extra_stat_info)
+ ret = entry->ops->extra_stat_info(entry, info, num);
+
+ unlock_entry(entry);
+
+ return ret;
+}
+
+int odp_pktio_extra_stats(odp_pktio_t pktio, uint64_t stats[], int num)
+{
+ pktio_entry_t *entry;
+ int ret = 0;
+
+ entry = get_pktio_entry(pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("already freed pktio\n");
+ return -1;
+ }
+
+ if (entry->ops->extra_stats)
+ ret = entry->ops->extra_stats(entry, stats, num);
+
+ unlock_entry(entry);
+
+ return ret;
+}
+
+int odp_pktio_extra_stat_counter(odp_pktio_t pktio, uint32_t id, uint64_t *stat)
+{
+ pktio_entry_t *entry;
+ int ret = -1;
+
+ entry = get_pktio_entry(pktio);
+ if (entry == NULL) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ lock_entry(entry);
+
+ if (odp_unlikely(is_free(entry))) {
+ unlock_entry(entry);
+ _ODP_ERR("already freed pktio\n");
+ return -1;
+ }
+
+ if (entry->ops->extra_stat_counter)
+ ret = entry->ops->extra_stat_counter(entry, id, stat);
+
+ unlock_entry(entry);
+
+ return ret;
+}
+
+void odp_pktio_extra_stats_print(odp_pktio_t pktio)
+{
+ int num_info, num_stats, i;
+
+ num_info = odp_pktio_extra_stat_info(pktio, NULL, 0);
+ if (num_info <= 0)
+ return;
+
+ num_stats = odp_pktio_extra_stats(pktio, NULL, 0);
+ if (num_stats <= 0)
+ return;
+
+ if (num_info != num_stats) {
+ _ODP_ERR("extra statistics info counts not matching\n");
+ return;
+ }
+
+ odp_pktio_extra_stat_info_t stats_info[num_stats];
+ uint64_t extra_stats[num_stats];
+
+ num_info = odp_pktio_extra_stat_info(pktio, stats_info, num_stats);
+ if (num_info <= 0)
+ return;
+
+ num_stats = odp_pktio_extra_stats(pktio, extra_stats, num_stats);
+ if (num_stats <= 0)
+ return;
+
+ if (num_info != num_stats) {
+ _ODP_ERR("extra statistics info counts not matching\n");
+ return;
+ }
+
+ _ODP_PRINT("Pktio extra statistics\n----------------------\n");
+ for (i = 0; i < num_stats; i++)
+ _ODP_PRINT(" %s=%" PRIu64 "\n", stats_info[i].name, extra_stats[i]);
+ _ODP_PRINT("\n");
}
-int odp_pktin_queue_config(odp_pktio_t pktio,
- const odp_pktin_queue_param_t *param)
+int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
{
pktio_entry_t *entry;
odp_pktin_mode_t mode;
odp_pktio_capability_t capa;
- unsigned num_queues;
- unsigned i;
+ uint32_t num_queues, i;
int rc;
odp_queue_t queue;
- queue_t q_int;
- odp_pktin_queue_param_t default_param;
+ odp_pktin_queue_param_t default_param, local_param;
if (param == NULL) {
odp_pktin_queue_param_init(&default_param);
@@ -1211,58 +1979,141 @@ int odp_pktin_queue_config(odp_pktio_t pktio,
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- if (entry->s.state == PKTIO_STATE_STARTED) {
- ODP_DBG("pktio %s: not stopped\n", entry->s.name);
+ if (entry->state == PKTIO_STATE_STARTED) {
+ _ODP_ERR("pktio %s: not stopped\n", entry->name);
return -1;
}
- mode = entry->s.param.in_mode;
+ mode = entry->param.in_mode;
/* Ignore the call when packet input is disabled. */
if (mode == ODP_PKTIN_MODE_DISABLED)
return 0;
- num_queues = param->num_queues;
-
- if (num_queues == 0) {
- ODP_DBG("pktio %s: zero input queues\n", entry->s.name);
+ if (!param->classifier_enable && param->num_queues == 0) {
+ _ODP_ERR("invalid num_queues for operation mode\n");
return -1;
}
+ if (param->classifier_enable) {
+ num_queues = 1;
+
+ if (param->num_queues != num_queues) {
+ /* When classifier is enabled, ensure that only one input queue will be
+ * configured by driver. */
+ memcpy(&local_param, param, sizeof(odp_pktin_queue_param_t));
+ local_param.num_queues = num_queues;
+ param = &local_param;
+ }
+ } else {
+ num_queues = param->num_queues;
+ }
+
rc = odp_pktio_capability(pktio, &capa);
if (rc) {
- ODP_DBG("pktio %s: unable to read capabilities\n",
- entry->s.name);
+ _ODP_ERR("pktio %s: unable to read capabilities\n", entry->name);
return -1;
}
- pktio_cls_enabled_set(entry, param->classifier_enable);
+ entry->enabled.cls = !!param->classifier_enable;
if (num_queues > capa.max_input_queues) {
- ODP_DBG("pktio %s: too many input queues\n", entry->s.name);
+ _ODP_ERR("pktio %s: too many input queues\n", entry->name);
return -1;
}
+ /* Check input queue sizes in direct mode */
+ for (i = 0; i < num_queues && mode == ODP_PKTIN_MODE_DIRECT; i++) {
+ uint32_t queue_size = param->queue_size[i];
+
+ if (queue_size == 0)
+ continue;
+
+ if (capa.max_input_queue_size == 0) {
+ _ODP_ERR("pktio %s: configuring input queue size not supported\n",
+ entry->name);
+ return -1;
+ }
+ if (queue_size < capa.min_input_queue_size) {
+ _ODP_ERR("pktio %s: input queue size too small\n", entry->name);
+ return -1;
+ }
+ if (queue_size > capa.max_input_queue_size) {
+ _ODP_ERR("pktio %s: input queue size too large\n", entry->name);
+ return -1;
+ }
+ }
+
+ /* Validate packet vector parameters */
+ if (param->vector.enable) {
+ odp_pool_t pool = param->vector.pool;
+ odp_pool_info_t pool_info;
+
+ if (mode == ODP_PKTIN_MODE_DIRECT) {
+ _ODP_ERR("packet vectors not supported with ODP_PKTIN_MODE_DIRECT\n");
+ return -1;
+ }
+ if (param->vector.max_size < capa.vector.min_size) {
+ _ODP_ERR("vector.max_size too small %" PRIu32 "\n",
+ param->vector.max_size);
+ return -1;
+ }
+ if (param->vector.max_size > capa.vector.max_size) {
+ _ODP_ERR("vector.max_size too large %" PRIu32 "\n",
+ param->vector.max_size);
+ return -1;
+ }
+ if (param->vector.max_tmo_ns > capa.vector.max_tmo_ns) {
+ _ODP_ERR("vector.max_tmo_ns too large %" PRIu64 "\n",
+ param->vector.max_tmo_ns);
+ return -1;
+ }
+
+ if (pool == ODP_POOL_INVALID || odp_pool_info(pool, &pool_info)) {
+ _ODP_ERR("invalid packet vector pool\n");
+ return -1;
+ }
+ if (pool_info.params.type != ODP_POOL_VECTOR) {
+ _ODP_ERR("wrong pool type\n");
+ return -1;
+ }
+ if (param->vector.max_size > pool_info.params.vector.max_size) {
+ _ODP_ERR("vector.max_size larger than pool max vector size\n");
+ return -1;
+ }
+ }
+
/* If re-configuring, destroy old queues */
- if (entry->s.num_in_queue)
- destroy_in_queues(entry, entry->s.num_in_queue);
+ if (entry->num_in_queue)
+ destroy_in_queues(entry, entry->num_in_queue);
for (i = 0; i < num_queues; i++) {
if (mode == ODP_PKTIN_MODE_QUEUE ||
mode == ODP_PKTIN_MODE_SCHED) {
odp_queue_param_t queue_param;
char name[ODP_QUEUE_NAME_LEN];
- int pktio_id = pktio_to_id(pktio);
+ int pktio_id = odp_pktio_index(pktio);
+ odp_pktin_queue_param_ovr_t *queue_param_ovr = NULL;
+
+ if (param->queue_param_ovr)
+ queue_param_ovr = param->queue_param_ovr + i;
snprintf(name, sizeof(name), "odp-pktin-%i-%i",
pktio_id, i);
- memcpy(&queue_param, &param->queue_param,
- sizeof(odp_queue_param_t));
+ if (param->classifier_enable) {
+ odp_queue_param_init(&queue_param);
+ } else {
+ memcpy(&queue_param, &param->queue_param,
+ sizeof(odp_queue_param_t));
+ if (queue_param_ovr)
+ queue_param.sched.group =
+ queue_param_ovr->group;
+ }
queue_param.type = ODP_QUEUE_TYPE_PLAIN;
@@ -1272,51 +2123,115 @@ int odp_pktin_queue_config(odp_pktio_t pktio,
queue = odp_queue_create(name, &queue_param);
if (queue == ODP_QUEUE_INVALID) {
- ODP_DBG("pktio %s: event queue create failed\n",
- entry->s.name);
+ _ODP_ERR("pktio %s: event queue create failed\n", entry->name);
destroy_in_queues(entry, i + 1);
return -1;
}
- q_int = queue_fn->from_ext(queue);
+ _odp_queue_fn->set_pktin(queue, pktio, i);
+ if (mode == ODP_PKTIN_MODE_QUEUE)
+ _odp_queue_fn->set_enq_deq_fn(queue,
+ NULL,
+ NULL,
+ pktin_dequeue,
+ pktin_deq_multi);
- if (mode == ODP_PKTIN_MODE_QUEUE) {
- queue_fn->set_pktin(q_int, pktio, i);
- queue_fn->set_enq_deq_fn(q_int,
- abort_pktin_enqueue,
- abort_pktin_enq_multi,
- pktin_dequeue,
- pktin_deq_multi);
- }
-
- entry->s.in_queue[i].queue = queue;
- entry->s.in_queue[i].queue_int = q_int;
+ entry->in_queue[i].queue = queue;
} else {
- entry->s.in_queue[i].queue = ODP_QUEUE_INVALID;
- entry->s.in_queue[i].queue_int = QUEUE_NULL;
+ entry->in_queue[i].queue = ODP_QUEUE_INVALID;
}
- entry->s.in_queue[i].pktin.index = i;
- entry->s.in_queue[i].pktin.pktio = entry->s.handle;
+ entry->in_queue[i].pktin.index = i;
+ entry->in_queue[i].pktin.pktio = entry->handle;
+ entry->in_queue[i].vector = param->vector;
}
- entry->s.num_in_queue = num_queues;
+ entry->num_in_queue = num_queues;
- if (entry->s.ops->input_queues_config)
- return entry->s.ops->input_queues_config(entry, param);
+ if (entry->ops->input_queues_config)
+ return entry->ops->input_queues_config(entry, param);
return 0;
}
+int _odp_pktio_pktout_tm_config(odp_pktio_t pktio_hdl,
+ odp_pktout_queue_t *queue, bool reconf)
+{
+ odp_pktout_queue_param_t param;
+ bool pktio_started = false;
+ odp_pktout_mode_t mode;
+ pktio_entry_t *entry;
+ uint32_t i;
+ int rc;
+
+ odp_pktout_queue_param_init(&param);
+ param.num_queues = 1;
+
+ entry = get_pktio_entry(pktio_hdl);
+ if (entry == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio_hdl);
+ return -1;
+ }
+
+ mode = entry->param.out_mode;
+ /* Don't proceed further if mode is not TM */
+ if (mode != ODP_PKTOUT_MODE_TM)
+ return -1;
+
+ /* Don't reconfigure unless requested */
+ if (entry->num_out_queue && !reconf) {
+ *queue = entry->out_queue[0].pktout;
+ return 0;
+ }
+
+ if (entry->state == PKTIO_STATE_STARTED) {
+ pktio_started = true;
+ rc = odp_pktio_stop(pktio_hdl);
+ if (rc) {
+ _ODP_ERR("Unable to stop pktio, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* If re-configuring, destroy old queues */
+ if (entry->num_out_queue) {
+ destroy_out_queues(entry, entry->num_out_queue);
+ entry->num_out_queue = 0;
+ }
+
+ init_out_queues(entry);
+ for (i = 0; i < param.num_queues; i++) {
+ entry->out_queue[i].pktout.index = i;
+ entry->out_queue[i].pktout.pktio = pktio_hdl;
+ }
+
+ entry->num_out_queue = param.num_queues;
+
+ rc = 0;
+ if (entry->ops->output_queues_config) {
+ rc = entry->ops->output_queues_config(entry, &param);
+ if (rc)
+ _ODP_ERR("Unable to setup output queues, rc=%d\n", rc);
+ }
+
+ /* Return pktout queue on success */
+ if (!rc)
+ *queue = entry->out_queue[0].pktout;
+
+ /* Take pktio back to its previous state */
+ if (pktio_started)
+ rc |= odp_pktio_start(pktio_hdl);
+ return rc;
+}
+
int odp_pktout_queue_config(odp_pktio_t pktio,
const odp_pktout_queue_param_t *param)
{
pktio_entry_t *entry;
odp_pktout_mode_t mode;
odp_pktio_capability_t capa;
- unsigned num_queues;
- unsigned i;
+ uint32_t num_queues, i;
int rc;
odp_pktout_queue_param_t default_param;
@@ -1327,16 +2242,16 @@ int odp_pktout_queue_config(odp_pktio_t pktio,
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- if (entry->s.state == PKTIO_STATE_STARTED) {
- ODP_DBG("pktio %s: not stopped\n", entry->s.name);
+ if (entry->state == PKTIO_STATE_STARTED) {
+ _ODP_ERR("pktio %s: not stopped\n", entry->name);
return -1;
}
- mode = entry->s.param.out_mode;
+ mode = entry->param.out_mode;
/* Ignore the call when packet output is disabled, or routed through
* traffic manager. */
@@ -1344,51 +2259,71 @@ int odp_pktout_queue_config(odp_pktio_t pktio,
return 0;
if (mode != ODP_PKTOUT_MODE_DIRECT && mode != ODP_PKTOUT_MODE_QUEUE) {
- ODP_DBG("pktio %s: bad packet output mode\n", entry->s.name);
+ _ODP_ERR("pktio %s: bad packet output mode\n", entry->name);
return -1;
}
num_queues = param->num_queues;
if (num_queues == 0) {
- ODP_DBG("pktio %s: zero output queues\n", entry->s.name);
+ _ODP_ERR("pktio %s: zero output queues\n", entry->name);
return -1;
}
rc = odp_pktio_capability(pktio, &capa);
if (rc) {
- ODP_DBG("pktio %s: unable to read capabilities\n",
- entry->s.name);
+ _ODP_ERR("pktio %s: unable to read capabilities\n", entry->name);
return -1;
}
if (num_queues > capa.max_output_queues) {
- ODP_DBG("pktio %s: too many output queues\n", entry->s.name);
+ _ODP_ERR("pktio %s: too many output queues\n", entry->name);
return -1;
}
+ /* Check output queue sizes */
+ for (i = 0; i < num_queues; i++) {
+ uint32_t queue_size = param->queue_size[i];
+
+ if (queue_size == 0)
+ continue;
+
+ if (capa.max_output_queue_size == 0) {
+ _ODP_ERR("pktio %s: configuring output queue size not supported\n",
+ entry->name);
+ return -1;
+ }
+ if (queue_size < capa.min_output_queue_size) {
+ _ODP_ERR("pktio %s: output queue size too small\n", entry->name);
+ return -1;
+ }
+ if (queue_size > capa.max_output_queue_size) {
+ _ODP_ERR("pktio %s: output queue size too large\n", entry->name);
+ return -1;
+ }
+ }
+
/* If re-configuring, destroy old queues */
- if (entry->s.num_out_queue) {
- destroy_out_queues(entry, entry->s.num_out_queue);
- entry->s.num_out_queue = 0;
+ if (entry->num_out_queue) {
+ destroy_out_queues(entry, entry->num_out_queue);
+ entry->num_out_queue = 0;
}
init_out_queues(entry);
for (i = 0; i < num_queues; i++) {
- entry->s.out_queue[i].pktout.index = i;
- entry->s.out_queue[i].pktout.pktio = pktio;
+ entry->out_queue[i].pktout.index = i;
+ entry->out_queue[i].pktout.pktio = pktio;
}
- entry->s.num_out_queue = num_queues;
+ entry->num_out_queue = num_queues;
if (mode == ODP_PKTOUT_MODE_QUEUE) {
for (i = 0; i < num_queues; i++) {
odp_queue_t queue;
odp_queue_param_t queue_param;
- queue_t q_int;
char name[ODP_QUEUE_NAME_LEN];
- int pktio_id = pktio_to_id(pktio);
+ int pktio_id = odp_pktio_index(pktio);
snprintf(name, sizeof(name), "odp-pktout-%i-%i",
pktio_id, i);
@@ -1400,28 +2335,26 @@ int odp_pktout_queue_config(odp_pktio_t pktio,
queue = odp_queue_create(name, &queue_param);
if (queue == ODP_QUEUE_INVALID) {
- ODP_DBG("pktout %s: event queue create failed\n",
- entry->s.name);
+ _ODP_ERR("pktout %s: event queue create failed\n", entry->name);
destroy_out_queues(entry, i + 1);
return -1;
}
- q_int = queue_fn->from_ext(queue);
- queue_fn->set_pktout(q_int, pktio, i);
+ _odp_queue_fn->set_pktout(queue, pktio, i);
/* Override default enqueue / dequeue functions */
- queue_fn->set_enq_deq_fn(q_int,
- pktout_enqueue,
- pktout_enq_multi,
- abort_pktout_dequeue,
- abort_pktout_deq_multi);
+ _odp_queue_fn->set_enq_deq_fn(queue,
+ pktout_enqueue,
+ pktout_enq_multi,
+ NULL,
+ NULL);
- entry->s.out_queue[i].queue = queue;
+ entry->out_queue[i].queue = queue;
}
}
- if (entry->s.ops->output_queues_config)
- return entry->s.ops->output_queues_config(entry, param);
+ if (entry->ops->output_queues_config)
+ return entry->ops->output_queues_config(entry, param);
return 0;
}
@@ -1435,11 +2368,16 @@ int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- mode = entry->s.param.in_mode;
+ if (num < 0) {
+ _ODP_DBG("Bad param: num %i\n", num);
+ return -1;
+ }
+
+ mode = entry->param.in_mode;
if (mode == ODP_PKTIN_MODE_DISABLED)
return 0;
@@ -1448,11 +2386,14 @@ int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
mode != ODP_PKTIN_MODE_SCHED)
return -1;
- num_queues = entry->s.num_in_queue;
+ num_queues = entry->num_in_queue;
- if (queues && num > 0) {
- for (i = 0; i < num && i < num_queues; i++)
- queues[i] = entry->s.in_queue[i].queue;
+ if (queues) {
+ if (num_queues < num)
+ num = num_queues;
+
+ for (i = 0; i < num; i++)
+ queues[i] = entry->in_queue[i].queue;
}
return num_queues;
@@ -1467,11 +2408,16 @@ int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ if (num < 0) {
+ _ODP_DBG("Bad param: num %i\n", num);
return -1;
}
- mode = entry->s.param.in_mode;
+ mode = entry->param.in_mode;
if (mode == ODP_PKTIN_MODE_DISABLED)
return 0;
@@ -1479,11 +2425,14 @@ int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
if (mode != ODP_PKTIN_MODE_DIRECT)
return -1;
- num_queues = entry->s.num_in_queue;
+ num_queues = entry->num_in_queue;
- if (queues && num > 0) {
- for (i = 0; i < num && i < num_queues; i++)
- queues[i] = entry->s.in_queue[i].pktin;
+ if (queues) {
+ if (num_queues < num)
+ num = num_queues;
+
+ for (i = 0; i < num; i++)
+ queues[i] = entry->in_queue[i].pktin;
}
return num_queues;
@@ -1498,11 +2447,11 @@ int odp_pktout_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- mode = entry->s.param.out_mode;
+ mode = entry->param.out_mode;
if (mode == ODP_PKTOUT_MODE_DISABLED)
return 0;
@@ -1510,11 +2459,11 @@ int odp_pktout_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
if (mode != ODP_PKTOUT_MODE_QUEUE)
return -1;
- num_queues = entry->s.num_out_queue;
+ num_queues = entry->num_out_queue;
if (queues && num > 0) {
for (i = 0; i < num && i < num_queues; i++)
- queues[i] = entry->s.out_queue[i].queue;
+ queues[i] = entry->out_queue[i].queue;
}
return num_queues;
@@ -1529,11 +2478,11 @@ int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- mode = entry->s.param.out_mode;
+ mode = entry->param.out_mode;
if (mode == ODP_PKTOUT_MODE_DISABLED)
return 0;
@@ -1541,11 +2490,11 @@ int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
if (mode != ODP_PKTOUT_MODE_DIRECT)
return -1;
- num_queues = entry->s.num_out_queue;
+ num_queues = entry->num_out_queue;
if (queues && num > 0) {
for (i = 0; i < num && i < num_queues; i++)
- queues[i] = entry->s.out_queue[i].pktout;
+ queues[i] = entry->out_queue[i].pktout;
}
return num_queues;
@@ -1555,14 +2504,22 @@ int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
{
pktio_entry_t *entry;
odp_pktio_t pktio = queue.pktio;
+ int ret;
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
return -1;
}
- return entry->s.ops->recv(entry, queue.index, packets, num);
+ if (odp_unlikely(entry->state != PKTIO_STATE_STARTED))
+ return 0;
+
+ ret = entry->ops->recv(entry, queue.index, packets, num);
+ if (_ODP_PCAPNG)
+ _odp_pcapng_dump_pkts(entry, queue.index, packets, ret);
+
+ return ret;
}
int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num,
@@ -1572,58 +2529,107 @@ int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num,
odp_time_t t1, t2;
struct timespec ts;
int started = 0;
+ uint64_t sleep_round = 0;
+ pktio_entry_t *entry;
ts.tv_sec = 0;
- ts.tv_nsec = SLEEP_NSEC;
+ ts.tv_nsec = 1000 * SLEEP_USEC;
- while (1) {
- ret = odp_pktin_recv(queue, packets, num);
+ entry = get_pktio_entry(queue.pktio);
+ if (entry == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)queue.pktio);
+ return -1;
+ }
- if (ret != 0)
- return ret;
+ if (odp_unlikely(entry->state != PKTIO_STATE_STARTED))
+ return 0;
- if (wait == 0)
- return 0;
+ if (entry->ops->recv_tmo && wait != ODP_PKTIN_NO_WAIT) {
+ ret = entry->ops->recv_tmo(entry, queue.index, packets, num,
+ wait);
+ if (_ODP_PCAPNG)
+ _odp_pcapng_dump_pkts(entry, queue.index, packets, ret);
- if (wait != ODP_PKTIN_WAIT) {
- /* Avoid unnecessary system calls. Record the start time
- * only when needed and after the first call to recv. */
- if (odp_unlikely(!started)) {
- odp_time_t t;
+ return ret;
+ }
- t = odp_time_local_from_ns(wait * SLEEP_NSEC);
- started = 1;
- t1 = odp_time_sum(odp_time_local(), t);
- }
+ while (1) {
+ ret = entry->ops->recv(entry, queue.index, packets, num);
+ if (_ODP_PCAPNG)
+ _odp_pcapng_dump_pkts(entry, queue.index, packets, ret);
- /* Check every SLEEP_CHECK rounds if total wait time
- * has been exceeded. */
- if ((wait & (SLEEP_CHECK - 1)) == 0) {
- t2 = odp_time_local();
+ if (ret != 0 || wait == 0)
+ return ret;
- if (odp_time_cmp(t2, t1) > 0)
- return 0;
- }
+ /* Avoid unnecessary system calls. Record the start time
+ * only when needed and after the first call to recv. */
+ if (odp_unlikely(!started)) {
+ /* Avoid overflow issues for large wait times */
+ if (wait > MAX_WAIT_TIME)
+ wait = MAX_WAIT_TIME;
+
+ started = 1;
+ t1 = odp_time_add_ns(odp_time_local(), wait * 1000);
+ }
- wait--;
+ /* Check every SLEEP_CHECK rounds if total wait time
+ * has been exceeded. */
+ if ((++sleep_round & (SLEEP_CHECK - 1)) == 0) {
+ t2 = odp_time_local();
+
+ if (odp_time_cmp(t2, t1) > 0)
+ return 0;
}
+ wait = wait > SLEEP_USEC ? wait - SLEEP_USEC : 0;
nanosleep(&ts, NULL);
}
}
-int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q,
- unsigned *from, odp_packet_t packets[], int num,
- uint64_t wait)
+int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], uint32_t num_q, uint32_t *from,
+ odp_packet_t packets[], int num, uint64_t wait)
{
- unsigned i;
+ uint32_t i;
int ret;
odp_time_t t1, t2;
struct timespec ts;
int started = 0;
+ uint64_t sleep_round = 0;
+ int trial_successful = 0;
+ uint32_t lfrom = 0;
+
+ for (i = 0; i < num_q; i++) {
+ ret = odp_pktin_recv(queues[i], packets, num);
+
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret != 0)
+ return ret;
+ }
+
+ if (wait == 0)
+ return 0;
+
+ ret = _odp_sock_recv_mq_tmo_try_int_driven(queues, num_q, &lfrom,
+ packets, num, wait,
+ &trial_successful);
+ if (ret > 0 && from)
+ *from = lfrom;
+ if (trial_successful) {
+ if (_ODP_PCAPNG) {
+ pktio_entry_t *entry;
+
+ entry = get_pktio_entry(queues[lfrom].pktio);
+ if (entry)
+ _odp_pcapng_dump_pkts(entry, lfrom, packets, ret);
+ }
+
+ return ret;
+ }
ts.tv_sec = 0;
- ts.tv_nsec = SLEEP_NSEC;
+ ts.tv_nsec = 1000 * SLEEP_USEC;
while (1) {
for (i = 0; i < num_q; i++) {
@@ -1639,24 +2645,24 @@ int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q,
if (wait == 0)
return 0;
- if (wait != ODP_PKTIN_WAIT) {
- if (odp_unlikely(!started)) {
- odp_time_t t;
-
- t = odp_time_local_from_ns(wait * SLEEP_NSEC);
- started = 1;
- t1 = odp_time_sum(odp_time_local(), t);
- }
+ if (odp_unlikely(!started)) {
+ /* Avoid overflow issues for large wait times */
+ if (wait > MAX_WAIT_TIME)
+ wait = MAX_WAIT_TIME;
- if ((wait & (SLEEP_CHECK - 1)) == 0) {
- t2 = odp_time_local();
+ started = 1;
+ t1 = odp_time_add_ns(odp_time_local(), wait * 1000);
+ }
- if (odp_time_cmp(t2, t1) > 0)
- return 0;
- }
+ /* Check every SLEEP_CHECK rounds if total wait time
+ * has been exceeded. */
+ if ((++sleep_round & (SLEEP_CHECK - 1)) == 0) {
+ t2 = odp_time_local();
- wait--;
+ if (odp_time_cmp(t2, t1) > 0)
+ return 0;
}
+ wait = wait > SLEEP_USEC ? wait - SLEEP_USEC : 0;
nanosleep(&ts, NULL);
}
@@ -1667,9 +2673,90 @@ uint64_t odp_pktin_wait_time(uint64_t nsec)
if (nsec == 0)
return 0;
- /* number of nanosleep calls rounded up by one, so that
+ /* number of microseconds rounded up by one, so that
* recv_mq_tmo call waits at least 'nsec' nanoseconds. */
- return (nsec / SLEEP_NSEC) + 1;
+ return (nsec / (1000)) + 1;
+}
+
+static inline odp_bool_t check_tx_compl(const odp_packet_hdr_t *hdr, int pkt_idx,
+ tx_compl_info_t *info, odp_pool_t pool,
+ odp_atomic_u32_t *status_map, uint16_t *num)
+{
+ tx_compl_info_t *i;
+
+ if (odp_likely(hdr->p.flags.tx_compl_ev == 0 && hdr->p.flags.tx_compl_poll == 0))
+ return true;
+
+ i = &info[*num];
+ i->idx = pkt_idx;
+
+ if (hdr->p.flags.tx_compl_ev) {
+ i->buf = odp_buffer_alloc(pool);
+
+ if (i->buf == ODP_BUFFER_INVALID)
+ return false;
+
+ i->user_ptr = hdr->user_ptr;
+ i->queue = hdr->dst_queue;
+ i->mode = ODP_PACKET_TX_COMPL_EVENT;
+ } else {
+ i->status = &status_map[hdr->tx_compl_id];
+ odp_atomic_store_rel_u32(i->status, 0);
+ i->mode = ODP_PACKET_TX_COMPL_POLL;
+ }
+
+ (*num)++;
+
+ return true;
+}
+
+static inline int prepare_tx_compl(const odp_packet_t packets[], int num, tx_compl_info_t *info,
+ odp_pool_t pool, odp_atomic_u32_t *status_map,
+ uint16_t *num_tx_c)
+{
+ int num_to_send = num;
+
+ for (int i = 0; i < num; i++)
+ if (!check_tx_compl(packet_hdr(packets[i]), i, info, pool, status_map, num_tx_c)) {
+ num_to_send = info[*num_tx_c].idx;
+ break;
+ }
+
+ return num_to_send;
+}
+
+static inline void send_tx_compl_event(odp_buffer_t buf, const void *user_ptr, odp_queue_t queue)
+{
+ _odp_pktio_tx_compl_t *data;
+ odp_event_t ev;
+
+ data = odp_buffer_addr(buf);
+ data->user_ptr = user_ptr;
+ ev = odp_buffer_to_event(buf);
+ _odp_event_type_set(ev, ODP_EVENT_PACKET_TX_COMPL);
+
+ if (odp_unlikely(odp_queue_enq(queue, ev))) {
+ _ODP_ERR("Failed to enqueue Tx completion event\n");
+ odp_event_free(ev);
+ }
+}
+
+static inline void finish_tx_compl(tx_compl_info_t *info, uint16_t num, int num_sent)
+{
+ tx_compl_info_t *i;
+
+ for (int j = 0; j < num; j++) {
+ i = &info[j];
+
+ if (i->idx < num_sent) {
+ if (i->mode == ODP_PACKET_TX_COMPL_EVENT)
+ send_tx_compl_event(i->buf, i->user_ptr, i->queue);
+ else
+ odp_atomic_store_rel_u32(i->status, 1);
+ } else if (i->mode == ODP_PACKET_TX_COMPL_EVENT) {
+ odp_buffer_free(i->buf);
+ }
+ }
}
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[],
@@ -1677,12 +2764,635 @@ int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[],
{
pktio_entry_t *entry;
odp_pktio_t pktio = queue.pktio;
+ tx_compl_info_t tx_compl_info[num];
+ uint16_t num_tx_c = 0;
+ int num_to_send = num, num_sent;
entry = get_pktio_entry(pktio);
if (entry == NULL) {
- ODP_DBG("pktio entry %d does not exist\n", pktio);
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)pktio);
+ return -1;
+ }
+
+ if (odp_unlikely(entry->state != PKTIO_STATE_STARTED))
+ return 0;
+
+ if (_ODP_PCAPNG)
+ _odp_pcapng_dump_pkts(entry, queue.index, packets, num);
+
+ if (odp_unlikely(_odp_pktio_tx_compl_enabled(entry))) {
+ odp_pool_t tx_compl_pool = entry->tx_compl_pool;
+ odp_atomic_u32_t *tx_compl_status = entry->tx_compl_status;
+
+ num_to_send = prepare_tx_compl(packets, num, tx_compl_info, tx_compl_pool,
+ tx_compl_status, &num_tx_c);
+ }
+
+ num_sent = entry->ops->send(entry, queue.index, packets, num_to_send);
+
+ if (odp_unlikely(num_tx_c))
+ finish_tx_compl(tx_compl_info, num_tx_c, num_sent);
+
+ return num_sent;
+}
+
+/** Get printable format of odp_pktio_t */
+uint64_t odp_pktio_to_u64(odp_pktio_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+int odp_pktout_ts_read(odp_pktio_t hdl, odp_time_t *ts)
+{
+ pktio_entry_t *entry;
+ uint64_t ts_val;
+
+ entry = get_pktio_entry(hdl);
+ if (odp_unlikely(entry == NULL)) {
+ _ODP_ERR("pktio entry %" PRIuPTR " does not exist\n", (uintptr_t)hdl);
+ return -1;
+ }
+
+ if (odp_atomic_load_u64(&entry->tx_ts) == 0)
+ return 1;
+
+ ts_val = odp_atomic_xchg_u64(&entry->tx_ts, 0);
+ if (odp_unlikely(ts_val == 0))
+ return 1;
+
+ ts->u64 = ts_val;
+ return 0;
+}
+
+void odp_lso_profile_param_init(odp_lso_profile_param_t *param)
+{
+ memset(param, 0, sizeof(odp_lso_profile_param_t));
+
+ param->lso_proto = ODP_LSO_PROTO_NONE;
+}
+
+odp_lso_profile_t odp_lso_profile_create(odp_pktio_t pktio, const odp_lso_profile_param_t *param)
+{
+ uint32_t i, num_custom, mod_op, offset, size;
+ lso_profile_t *lso_prof = NULL;
+ (void)pktio;
+
+ /* Currently only IPv4 and custom implemented */
+ if (param->lso_proto != ODP_LSO_PROTO_IPV4 &&
+ param->lso_proto != ODP_LSO_PROTO_CUSTOM) {
+ _ODP_ERR("Protocol not supported\n");
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ if (param->lso_proto == ODP_LSO_PROTO_CUSTOM) {
+ num_custom = param->custom.num_custom;
+ if (num_custom > ODP_LSO_MAX_CUSTOM) {
+ _ODP_ERR("Too many custom fields\n");
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ for (i = 0; i < num_custom; i++) {
+ mod_op = param->custom.field[i].mod_op;
+ offset = param->custom.field[i].offset;
+ size = param->custom.field[i].size;
+
+ if (offset > PKTIO_LSO_MAX_PAYLOAD_OFFSET) {
+ _ODP_ERR("Too large custom field offset %u\n", offset);
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ /* Currently only segment number supported */
+ if (mod_op != ODP_LSO_ADD_SEGMENT_NUM) {
+ _ODP_ERR("Custom modify operation %u not supported\n", mod_op);
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ if (size != 1 && size != 2 && size != 4 && size != 8) {
+ _ODP_ERR("Bad custom field size %u\n", size);
+ return ODP_LSO_PROFILE_INVALID;
+ }
+ }
+ }
+
+ odp_spinlock_lock(&pktio_global->lock);
+
+ if (pktio_global->num_lso_profiles >= PKTIO_LSO_PROFILES) {
+ odp_spinlock_unlock(&pktio_global->lock);
+ _ODP_ERR("All LSO profiles used already: %u\n", PKTIO_LSO_PROFILES);
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ for (i = 0; i < PKTIO_LSO_PROFILES; i++) {
+ if (pktio_global->lso_profile[i].used == 0) {
+ lso_prof = &pktio_global->lso_profile[i];
+ lso_prof->used = 1;
+ pktio_global->num_lso_profiles++;
+ break;
+ }
+ }
+
+ odp_spinlock_unlock(&pktio_global->lock);
+
+ if (lso_prof == NULL) {
+ _ODP_ERR("Did not find free LSO profile\n");
+ return ODP_LSO_PROFILE_INVALID;
+ }
+
+ lso_prof->param = *param;
+ lso_prof->index = i;
+
+ return (odp_lso_profile_t)(uintptr_t)lso_prof;
+}
+
+odp_lso_profile_t _odp_lso_prof_from_idx(uint8_t idx)
+{
+ return (odp_lso_profile_t)(uintptr_t)&pktio_global->lso_profile[idx];
+}
+
+static inline lso_profile_t *lso_profile_ptr(odp_lso_profile_t handle)
+{
+ return (lso_profile_t *)(uintptr_t)handle;
+}
+
+int odp_lso_profile_destroy(odp_lso_profile_t lso_profile)
+{
+ lso_profile_t *lso_prof = lso_profile_ptr(lso_profile);
+
+ if (lso_profile == ODP_LSO_PROFILE_INVALID || lso_prof->used == 0) {
+ _ODP_ERR("Bad handle\n");
+ return -1;
+ }
+
+ odp_spinlock_lock(&pktio_global->lock);
+ lso_prof->used = 0;
+ pktio_global->num_lso_profiles--;
+ odp_spinlock_unlock(&pktio_global->lock);
+
+ return 0;
+}
+
+int odp_packet_lso_request(odp_packet_t pkt, const odp_packet_lso_opt_t *lso_opt)
+{
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ lso_profile_t *lso_prof = lso_profile_ptr(lso_opt->lso_profile);
+ uint32_t payload_offset = lso_opt->payload_offset;
+
+ if (odp_unlikely(lso_opt->lso_profile == ODP_LSO_PROFILE_INVALID || lso_prof->used == 0)) {
+ _ODP_ERR("Bad LSO profile handle\n");
+ return -1;
+ }
+
+ if (odp_unlikely(payload_offset > PKTIO_LSO_MAX_PAYLOAD_OFFSET)) {
+ _ODP_ERR("Too large LSO payload offset\n");
+ return -1;
+ }
+
+ if (odp_unlikely(payload_offset > packet_len(pkt_hdr))) {
+ _ODP_ERR("LSO payload offset larger than packet data length\n");
+ return -1;
+ }
+
+ if (odp_packet_payload_offset_set(pkt, payload_offset)) {
+ _ODP_ERR("Payload offset set failed\n");
+ return -1;
+ }
+
+ pkt_hdr->p.flags.lso = 1;
+ pkt_hdr->lso_max_payload = lso_opt->max_payload_len;
+ pkt_hdr->lso_profile_idx = lso_prof->index;
+
+ return 0;
+}
+
+static int lso_update_ipv4(odp_packet_t pkt, int index, int num_pkt,
+ uint32_t l3_offset, uint32_t payload_len)
+{
+ _odp_ipv4hdr_t *ipv4;
+ uint32_t pkt_len = odp_packet_len(pkt);
+ uint16_t tot_len = pkt_len - l3_offset;
+ int ret = 0;
+ uint16_t frag_offset;
+
+ odp_packet_l3_offset_set(pkt, l3_offset);
+ ipv4 = odp_packet_l3_ptr(pkt, NULL);
+ ipv4->tot_len = odp_cpu_to_be_16(tot_len);
+
+ /* IP payload offset in 8 byte blocks */
+ frag_offset = ((uint32_t)index * payload_len) / 8;
+
+ /* More fragments flag */
+ if (index < (num_pkt - 1))
+ frag_offset |= _ODP_IPV4HDR_FRAG_OFFSET_MORE_FRAGS;
+
+ ipv4->frag_offset = odp_cpu_to_be_16(frag_offset);
+ ret = _odp_packet_ipv4_chksum_insert(pkt);
+
+ return ret;
+}
+
+static int lso_update_custom(lso_profile_t *lso_prof, odp_packet_t pkt, int segnum)
+{
+ void *ptr;
+ int i, mod_op;
+ uint32_t offset;
+ uint8_t size;
+ int num_custom = lso_prof->param.custom.num_custom;
+ uint64_t u64 = 0;
+ uint32_t u32 = 0;
+ uint16_t u16 = 0;
+ uint8_t u8 = 0;
+
+ for (i = 0; i < num_custom; i++) {
+ mod_op = lso_prof->param.custom.field[i].mod_op;
+ offset = lso_prof->param.custom.field[i].offset;
+ size = lso_prof->param.custom.field[i].size;
+
+ if (size == 8)
+ ptr = &u64;
+ else if (size == 4)
+ ptr = &u32;
+ else if (size == 2)
+ ptr = &u16;
+ else {
+ /*
+ * odp_lso_profile_create() ensures that size is one of the allowed values.
+ * But compiler doesn't know that, so set it here to avoid possibility of
+ * out of bounds warnings.
+ */
+ size = 1;
+ ptr = &u8;
+ }
+
+ if (odp_packet_copy_to_mem(pkt, offset, size, ptr)) {
+ _ODP_ERR("Read from packet failed at offset %u\n", offset);
+ return -1;
+ }
+
+ if (mod_op == ODP_LSO_ADD_SEGMENT_NUM) {
+ if (size == 8)
+ u64 = odp_cpu_to_be_64(segnum + odp_be_to_cpu_64(u64));
+ else if (size == 4)
+ u32 = odp_cpu_to_be_32(segnum + odp_be_to_cpu_32(u32));
+ else if (size == 2)
+ u16 = odp_cpu_to_be_16(segnum + odp_be_to_cpu_16(u16));
+ else
+ u8 += segnum;
+ }
+
+ if (odp_packet_copy_from_mem(pkt, offset, size, ptr)) {
+ _ODP_ERR("Write to packet failed at offset %u\n", offset);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int _odp_lso_num_packets(odp_packet_t packet, const odp_packet_lso_opt_t *lso_opt,
+ uint32_t *len_out, uint32_t *left_over_out)
+{
+ uint32_t num_pkt, left_over, l3_offset, iphdr_len;
+ odp_lso_profile_t lso_profile = lso_opt->lso_profile;
+ lso_profile_t *lso_prof = lso_profile_ptr(lso_profile);
+ uint32_t payload_len = lso_opt->max_payload_len;
+ uint32_t hdr_len = lso_opt->payload_offset;
+ uint32_t pkt_len = odp_packet_len(packet);
+ uint32_t pkt_payload = pkt_len - hdr_len;
+
+ if (odp_unlikely(hdr_len > PKTIO_LSO_MAX_PAYLOAD_OFFSET)) {
+ _ODP_ERR("Too large LSO payload offset\n");
+ return -1;
+ }
+
+ if (odp_unlikely(hdr_len > pkt_len)) {
+ _ODP_ERR("LSO payload offset larger than packet data length\n");
+ return -1;
+ }
+
+ if (odp_unlikely(hdr_len + payload_len > odp_packet_len(packet))) {
+ /* Packet does not need segmentation */
+ *len_out = payload_len;
+ *left_over_out = 0;
+
+ return 1;
+ }
+
+ if (lso_prof->param.lso_proto == ODP_LSO_PROTO_IPV4) {
+ l3_offset = odp_packet_l3_offset(packet);
+ iphdr_len = hdr_len - l3_offset;
+
+ if (l3_offset == ODP_PACKET_OFFSET_INVALID) {
+ _ODP_ERR("Invalid L3 offset\n");
+ return -1;
+ }
+
+ if (hdr_len < l3_offset || iphdr_len < _ODP_IPV4HDR_LEN) {
+ _ODP_ERR("Bad payload or L3 offset\n");
+ return -1;
+ }
+
+ /* Round down payload len to a multiple of 8 (on other than the last fragment). */
+ payload_len = (payload_len / 8) * 8;
+ }
+
+ num_pkt = pkt_payload / payload_len;
+
+ left_over = pkt_payload - (num_pkt * payload_len);
+ if (left_over)
+ num_pkt++;
+
+ if (num_pkt > PKTIO_LSO_MAX_SEGMENTS) {
+ _ODP_ERR("Too many LSO segments %i. Maximum is %i\n", num_pkt,
+ PKTIO_LSO_MAX_SEGMENTS);
+ return -1;
+ }
+
+ *len_out = payload_len;
+ *left_over_out = left_over;
+
+ return num_pkt;
+}
+
+int _odp_lso_create_packets(odp_packet_t packet, const odp_packet_lso_opt_t *lso_opt,
+ uint32_t payload_len, uint32_t left_over_len,
+ odp_packet_t pkt_out[], int num_pkt)
+{
+ int i, num;
+ uint32_t offset;
+ odp_packet_t pkt;
+ odp_lso_profile_t lso_profile = lso_opt->lso_profile;
+ lso_profile_t *lso_prof = lso_profile_ptr(lso_profile);
+ const uint32_t hdr_len = lso_opt->payload_offset;
+ const uint32_t pkt_len = hdr_len + payload_len;
+ odp_pool_t pool = odp_packet_pool(packet);
+ int num_free = 0;
+ int num_full = num_pkt;
+
+ if (left_over_len)
+ num_full = num_pkt - 1;
+
+ num = odp_packet_alloc_multi(pool, pkt_len, pkt_out, num_full);
+ if (odp_unlikely(num < num_full)) {
+ _ODP_DBG("Alloc failed %i\n", num);
+ if (num > 0) {
+ num_free = num;
+ goto error;
+ }
+ }
+
+ if (left_over_len) {
+ pkt = odp_packet_alloc(pool, hdr_len + left_over_len);
+ if (pkt == ODP_PACKET_INVALID) {
+ _ODP_DBG("Alloc failed\n");
+ num_free = num_full;
+ goto error;
+ }
+
+ pkt_out[num_pkt - 1] = pkt;
+ }
+
+ num_free = num_pkt;
+
+ /* Copy headers */
+ for (i = 0; i < num_pkt; i++) {
+ if (odp_packet_copy_from_pkt(pkt_out[i], 0, packet, 0, hdr_len)) {
+ _ODP_ERR("Header copy failed\n");
+ goto error;
+ }
+ }
+
+ /* Copy payload */
+ for (i = 0; i < num_full; i++) {
+ offset = hdr_len + (i * payload_len);
+ if (odp_packet_copy_from_pkt(pkt_out[i], hdr_len, packet, offset, payload_len)) {
+ _ODP_ERR("Payload copy failed\n");
+ goto error;
+ }
+ }
+
+ /* Copy left over payload */
+ if (left_over_len) {
+ offset = hdr_len + (num_full * payload_len);
+ if (odp_packet_copy_from_pkt(pkt_out[num_pkt - 1], hdr_len, packet, offset,
+ left_over_len)){
+ _ODP_ERR("Payload copy failed\n");
+ goto error;
+ }
+ }
+
+ if (lso_prof->param.lso_proto == ODP_LSO_PROTO_IPV4) {
+ offset = odp_packet_l3_offset(packet);
+
+ if (offset == ODP_PACKET_OFFSET_INVALID) {
+ _ODP_ERR("Invalid L3 offset\n");
+ goto error;
+ }
+
+ for (i = 0; i < num_pkt; i++) {
+ if (lso_update_ipv4(pkt_out[i], i, num_pkt, offset, payload_len)) {
+ _ODP_ERR("IPv4 header update failed. Packet %i.\n", i);
+ goto error;
+ }
+ }
+ } else {
+ /* Update custom fields */
+ int num_custom = lso_prof->param.custom.num_custom;
+
+ for (i = 0; num_custom && i < num_pkt; i++) {
+ if (lso_update_custom(lso_prof, pkt_out[i], i)) {
+ _ODP_ERR("Custom field update failed. Segment %i\n", i);
+ goto error;
+ }
+ }
+ }
+
+ return 0;
+
+error:
+ odp_packet_free_multi(pkt_out, num_free);
+ return -1;
+}
+
+static int pktout_send_lso(odp_pktout_queue_t queue, odp_packet_t packet,
+ const odp_packet_lso_opt_t *lso_opt)
+{
+ int ret, num_pkt;
+ uint32_t payload_len, left_over_len;
+
+ /* Calculate number of packets */
+ num_pkt = _odp_lso_num_packets(packet, lso_opt, &payload_len, &left_over_len);
+ if (odp_unlikely(num_pkt <= 0))
+ return -1;
+
+ if (odp_unlikely(num_pkt == 1)) {
+ /* Segmentation not needed */
+ if (odp_pktout_send(queue, &packet, 1) != 1)
+ return -1;
+
+ return 0;
+ }
+
+ /* Create packets */
+ odp_packet_t pkt_out[num_pkt];
+
+ ret = _odp_lso_create_packets(packet, lso_opt, payload_len, left_over_len, pkt_out,
+ num_pkt);
+
+ if (odp_unlikely(ret))
+ return -1;
+
+ /* Send LSO packets */
+ ret = odp_pktout_send(queue, pkt_out, num_pkt);
+
+ if (ret < num_pkt) {
+ int first_free = 0;
+ int num_free = num_pkt;
+
+ _ODP_DBG("Packet send failed %i\n", ret);
+
+ if (ret > 0) {
+ first_free = ret;
+ num_free = num_pkt - ret;
+ }
+
+ odp_packet_free_multi(&pkt_out[first_free], num_free);
+ return -1;
+ }
+
+ /* Free original packet */
+ odp_packet_free(packet);
+
+ return 0;
+}
+
+int odp_pktout_send_lso(odp_pktout_queue_t queue, const odp_packet_t packet[], int num,
+ const odp_packet_lso_opt_t *opt)
+{
+ int i;
+ odp_packet_t pkt;
+ odp_packet_lso_opt_t lso_opt;
+ const odp_packet_lso_opt_t *opt_ptr = &lso_opt;
+
+ if (odp_unlikely(num <= 0)) {
+ _ODP_ERR("No packets\n");
return -1;
}
- return entry->s.ops->send(entry, queue.index, packets, num);
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ if (opt)
+ opt_ptr = opt;
+
+ for (i = 0; i < num; i++) {
+ pkt = packet[i];
+
+ if (opt == NULL) {
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (pkt_hdr->p.flags.lso == 0) {
+ _ODP_ERR("No LSO options on packet %i\n", i);
+ if (i == 0)
+ return -1;
+
+ return i;
+ }
+
+ lso_opt.lso_profile = _odp_lso_prof_from_idx(pkt_hdr->lso_profile_idx);
+ lso_opt.payload_offset = odp_packet_payload_offset(pkt);
+ lso_opt.max_payload_len = pkt_hdr->lso_max_payload;
+ }
+
+ if (odp_unlikely(pktout_send_lso(queue, pkt, opt_ptr))) {
+ _ODP_DBG("LSO output failed on packet %i\n", i);
+ return i;
+ }
+ }
+
+ return i;
+}
+
+void _odp_pktio_process_tx_compl(const pktio_entry_t *entry, const odp_packet_t packets[], int num)
+{
+ odp_packet_hdr_t *hdr;
+ odp_pool_t pool = entry->tx_compl_pool;
+ odp_buffer_t buf;
+ odp_atomic_u32_t *status_map = entry->tx_compl_status;
+
+ for (int i = 0; i < num; i++) {
+ hdr = packet_hdr(packets[i]);
+
+ if (odp_likely(hdr->p.flags.tx_compl_ev == 0 && hdr->p.flags.tx_compl_poll == 0))
+ continue;
+
+ if (hdr->p.flags.tx_compl_ev) {
+ buf = odp_buffer_alloc(pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+ continue;
+
+ send_tx_compl_event(buf, hdr->user_ptr, hdr->dst_queue);
+ } else {
+ odp_atomic_store_rel_u32(&status_map[hdr->tx_compl_id], 1);
+ }
+ }
+}
+
+void
+odp_proto_stats_param_init(odp_proto_stats_param_t *param)
+{
+ if (param)
+ memset(param, 0, sizeof(*param));
+}
+
+int
+odp_proto_stats_capability(odp_pktio_t pktio, odp_proto_stats_capability_t *capa)
+{
+ (void)pktio;
+
+ if (capa == NULL)
+ return -1;
+
+ memset(capa, 0, sizeof(*capa));
+
+ return 0;
+}
+
+odp_proto_stats_t
+odp_proto_stats_lookup(const char *name)
+{
+ (void)name;
+
+ return ODP_PROTO_STATS_INVALID;
+}
+
+odp_proto_stats_t
+odp_proto_stats_create(const char *name, const odp_proto_stats_param_t *param)
+{
+ (void)name;
+ (void)param;
+
+ return ODP_PROTO_STATS_INVALID;
+}
+
+int
+odp_proto_stats_destroy(odp_proto_stats_t stat)
+{
+ (void)stat;
+
+ return 0;
+}
+
+int
+odp_proto_stats(odp_proto_stats_t stat, odp_proto_stats_data_t *data)
+{
+ (void)stat;
+
+ memset(data, 0, sizeof(odp_proto_stats_data_t));
+
+ return 0;
+}
+
+void
+odp_proto_stats_print(odp_proto_stats_t stat)
+{
+ (void)stat;
}
diff --git a/platform/linux-generic/odp_packet_io_api.c b/platform/linux-generic/odp_packet_io_api.c
new file mode 100644
index 000000000..196d8ce01
--- /dev/null
+++ b/platform/linux-generic/odp_packet_io_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/packet_io.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/packet_io_inlines.h>
diff --git a/platform/linux-generic/odp_packet_vector.c b/platform/linux-generic/odp_packet_vector.c
new file mode 100644
index 000000000..80533da25
--- /dev/null
+++ b/platform/linux-generic/odp_packet_vector.c
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/pool.h>
+#include <odp/api/plat/packet_vector_inlines.h>
+#include <odp/api/plat/strong_types.h>
+
+#include <odp_debug_internal.h>
+#include <odp_event_vector_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_string_internal.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <odp/visibility_begin.h>
+
+/* Packet vector header field offsets for inline functions */
+const _odp_event_vector_inline_offset_t _odp_event_vector_inline ODP_ALIGNED_CACHE = {
+ .packet = offsetof(odp_event_vector_hdr_t, packet),
+ .pool = offsetof(odp_event_vector_hdr_t, event_hdr.pool),
+ .size = offsetof(odp_event_vector_hdr_t, size),
+ .uarea_addr = offsetof(odp_event_vector_hdr_t, uarea_addr),
+ .flags = offsetof(odp_event_vector_hdr_t, flags)
+};
+
+#include <odp/visibility_end.h>
+
+static inline odp_event_vector_hdr_t *event_vector_hdr_from_event(odp_event_t event)
+{
+ return (odp_event_vector_hdr_t *)(uintptr_t)event;
+}
+
+odp_packet_vector_t odp_packet_vector_alloc(odp_pool_t pool_hdl)
+{
+ odp_event_t event;
+ pool_t *pool;
+
+ _ODP_ASSERT(pool_hdl != ODP_POOL_INVALID);
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ _ODP_ASSERT(pool->type == ODP_POOL_VECTOR);
+
+ event = _odp_event_alloc(pool);
+ if (odp_unlikely(event == ODP_EVENT_INVALID))
+ return ODP_PACKET_VECTOR_INVALID;
+
+ _ODP_ASSERT(event_vector_hdr_from_event(event)->size == 0);
+
+ return odp_packet_vector_from_event(event);
+}
+
+void odp_packet_vector_free(odp_packet_vector_t pktv)
+{
+ odp_event_vector_hdr_t *pktv_hdr = _odp_packet_vector_hdr(pktv);
+
+ pktv_hdr->size = 0;
+ pktv_hdr->flags.all_flags = 0;
+
+ _odp_event_free(odp_packet_vector_to_event(pktv));
+}
+
+int odp_packet_vector_valid(odp_packet_vector_t pktv)
+{
+ odp_event_vector_hdr_t *pktv_hdr;
+ odp_event_t ev;
+ pool_t *pool;
+ uint32_t i;
+
+ if (odp_unlikely(pktv == ODP_PACKET_VECTOR_INVALID))
+ return 0;
+
+ ev = odp_packet_vector_to_event(pktv);
+
+ if (_odp_event_is_valid(ev) == 0)
+ return 0;
+
+ if (odp_event_type(ev) != ODP_EVENT_PACKET_VECTOR)
+ return 0;
+
+ pktv_hdr = _odp_packet_vector_hdr(pktv);
+ pool = _odp_pool_entry(pktv_hdr->event_hdr.pool);
+
+ if (odp_unlikely(pktv_hdr->size > pool->params.vector.max_size))
+ return 0;
+
+ for (i = 0; i < pktv_hdr->size; i++) {
+ if (pktv_hdr->packet[i] == ODP_PACKET_INVALID)
+ return 0;
+ }
+
+ return 1;
+}
+
+void odp_packet_vector_print(odp_packet_vector_t pktv)
+{
+ int max_len = 4096;
+ char str[max_len];
+ int len = 0;
+ int n = max_len - 1;
+ uint32_t i;
+ odp_event_vector_hdr_t *pktv_hdr = _odp_packet_vector_hdr(pktv);
+
+ len += _odp_snprint(&str[len], n - len, "Packet vector info\n");
+ len += _odp_snprint(&str[len], n - len, "------------------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_packet_vector_to_u64(pktv));
+ len += _odp_snprint(&str[len], n - len, " size %" PRIu32 "\n", pktv_hdr->size);
+ len += _odp_snprint(&str[len], n - len, " flags 0x%" PRIx32 "\n",
+ pktv_hdr->flags.all_flags);
+ len += _odp_snprint(&str[len], n - len, " user area %p\n", pktv_hdr->uarea_addr);
+
+ for (i = 0; i < pktv_hdr->size; i++) {
+ odp_packet_t pkt = pktv_hdr->packet[i];
+ char seg_str[max_len];
+ int str_len;
+
+ str_len = _odp_snprint(seg_str, max_len, " packet %p len %" PRIu32 "\n",
+ (void *)pkt, odp_packet_len(pkt));
+
+ /* Prevent print buffer overflow */
+ if (n - len - str_len < 10) {
+ len += _odp_snprint(&str[len], n - len, " ...\n");
+ break;
+ }
+ len += _odp_snprint(&str[len], n - len, "%s", seg_str);
+ }
+
+ _ODP_PRINT("%s\n", str);
+}
+
+uint64_t odp_packet_vector_to_u64(odp_packet_vector_t pktv)
+{
+ return _odp_pri(pktv);
+}
diff --git a/platform/linux-generic/odp_parse.c b/platform/linux-generic/odp_parse.c
new file mode 100644
index 000000000..60e8e5922
--- /dev/null
+++ b/platform/linux-generic/odp_parse.c
@@ -0,0 +1,475 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+#include <odp_parse_internal.h>
+#include <odp_chksum_internal.h>
+#include <protocols/eth.h>
+#include <protocols/ip.h>
+#include <protocols/sctp.h>
+#include <protocols/tcp.h>
+#include <protocols/udp.h>
+#include <odp/api/hash.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/packet_types.h>
+#include <stdint.h>
+#include <string.h>
+
+/** Parser helper function for Ethernet packets
+ *
+ * Requires up to PARSE_ETH_BYTES bytes of contiguous packet data.
+ */
+uint16_t _odp_parse_eth(packet_parser_t *prs, const uint8_t **parseptr,
+ uint32_t *offset, uint32_t frame_len)
+{
+ uint16_t ethtype;
+ const _odp_ethhdr_t *eth;
+ uint16_t macaddr0, macaddr2, macaddr4;
+ const _odp_vlanhdr_t *vlan;
+ _odp_packet_input_flags_t input_flags;
+
+ input_flags.all = 0;
+ input_flags.l2 = 1;
+ input_flags.eth = 1;
+
+ eth = (const _odp_ethhdr_t *)*parseptr;
+
+ /* Detect jumbo frames */
+ if (odp_unlikely(frame_len - *offset > _ODP_ETH_LEN_MAX))
+ input_flags.jumbo = 1;
+
+ /* Handle Ethernet broadcast/multicast addresses */
+ macaddr0 = odp_be_to_cpu_16(*((const odp_una_u16_t *)eth));
+ if (odp_unlikely((macaddr0 & 0x0100) == 0x0100))
+ input_flags.eth_mcast = 1;
+
+ if (odp_unlikely(macaddr0 == 0xffff)) {
+ macaddr2 = odp_be_to_cpu_16(*((const odp_una_u16_t *)eth + 1));
+ macaddr4 = odp_be_to_cpu_16(*((const odp_una_u16_t *)eth + 2));
+
+ if ((macaddr2 == 0xffff) && (macaddr4 == 0xffff))
+ input_flags.eth_bcast = 1;
+ }
+
+ /* Get Ethertype */
+ ethtype = odp_be_to_cpu_16(eth->type);
+ *offset += sizeof(*eth);
+ *parseptr += sizeof(*eth);
+
+ /* Check for SNAP vs. DIX */
+ if (odp_unlikely(ethtype < _ODP_ETH_LEN_MAX)) {
+ input_flags.snap = 1;
+ if (ethtype > frame_len - *offset) {
+ prs->flags.snap_len_err = 1;
+ ethtype = 0;
+ goto error;
+ }
+ ethtype = odp_be_to_cpu_16(*((const odp_una_u16_t *)(*parseptr + 6)));
+ *offset += 8;
+ *parseptr += 8;
+ }
+
+ /* Parse the VLAN header(s), if present */
+ if (odp_unlikely(ethtype == _ODP_ETHTYPE_VLAN_OUTER)) {
+ input_flags.vlan_qinq = 1;
+ input_flags.vlan = 1;
+
+ vlan = (const _odp_vlanhdr_t *)*parseptr;
+ ethtype = odp_be_to_cpu_16(vlan->type);
+ *offset += sizeof(_odp_vlanhdr_t);
+ *parseptr += sizeof(_odp_vlanhdr_t);
+ }
+
+ if (ethtype == _ODP_ETHTYPE_VLAN) {
+ input_flags.vlan = 1;
+ vlan = (const _odp_vlanhdr_t *)*parseptr;
+ ethtype = odp_be_to_cpu_16(vlan->type);
+ *offset += sizeof(_odp_vlanhdr_t);
+ *parseptr += sizeof(_odp_vlanhdr_t);
+ }
+
+ /*
+ * The packet was too short for what we parsed. We just give up
+ * entirely without trying to parse what fits in the packet.
+ */
+ if (odp_unlikely(*offset > frame_len)) {
+ input_flags.all = 0;
+ input_flags.l2 = 1;
+ ethtype = 0;
+ }
+
+error:
+ prs->input_flags.all |= input_flags.all;
+
+ return ethtype;
+}
+
+/**
+ * Parser helper function for IPv4
+ *
+ * Requires up to PARSE_IPV4_BYTES bytes of contiguous packet data.
+ */
+static inline uint8_t parse_ipv4(packet_parser_t *prs, const uint8_t **parseptr,
+ uint32_t *offset, uint32_t frame_len,
+ odp_pktin_config_opt_t opt,
+ uint64_t *l4_part_sum)
+{
+ const _odp_ipv4hdr_t *ipv4 = (const _odp_ipv4hdr_t *)*parseptr;
+ uint32_t dstaddr = odp_be_to_cpu_32(ipv4->dst_addr);
+ uint32_t l3_len = odp_be_to_cpu_16(ipv4->tot_len);
+ uint16_t frag_offset = odp_be_to_cpu_16(ipv4->frag_offset);
+ uint8_t ver = _ODP_IPV4HDR_VER(ipv4->ver_ihl);
+ uint8_t ihl = _ODP_IPV4HDR_IHL(ipv4->ver_ihl);
+
+ if (odp_unlikely(prs->flags.l3_chksum_err ||
+ ihl < _ODP_IPV4HDR_IHL_MIN ||
+ ver != 4 ||
+ sizeof(*ipv4) > frame_len - *offset ||
+ (l3_len > frame_len - *offset))) {
+ prs->flags.ip_err = 1;
+ return 0;
+ }
+
+ if (opt.bit.ipv4_chksum) {
+ prs->input_flags.l3_chksum_done = 1;
+ if (chksum_finalize(chksum_partial(ipv4, ihl * 4, 0)) != 0xffff) {
+ prs->flags.ip_err = 1;
+ prs->flags.l3_chksum_err = 1;
+ return 0;
+ }
+ }
+
+ *offset += ihl * 4;
+ *parseptr += ihl * 4;
+
+ if (opt.bit.udp_chksum || opt.bit.tcp_chksum)
+ *l4_part_sum = chksum_partial((const uint8_t *)&ipv4->src_addr,
+ 2 * _ODP_IPV4ADDR_LEN, 0);
+
+ if (odp_unlikely(ihl > _ODP_IPV4HDR_IHL_MIN))
+ prs->input_flags.ipopt = 1;
+
+ /* A packet is a fragment if:
+ * "more fragments" flag is set (all fragments except the last)
+ * OR
+ * "fragment offset" field is nonzero (all fragments except the first)
+ */
+ if (odp_unlikely(_ODP_IPV4HDR_IS_FRAGMENT(frag_offset)))
+ prs->input_flags.ipfrag = 1;
+
+ /* Handle IPv4 broadcast / multicast */
+ if (odp_unlikely(dstaddr == 0xffffffff))
+ prs->input_flags.ip_bcast = 1;
+
+ if (odp_unlikely((dstaddr >> 28) == 0xe))
+ prs->input_flags.ip_mcast = 1;
+
+ return ipv4->proto;
+}
+
+/**
+ * Parser helper function for IPv6
+ *
+ * Requires at least PARSE_IPV6_BYTES bytes of contiguous packet data.
+ *
+ * - offset is the offset of the first byte of the data pointed to by parseptr
+ * - seg_end is the maximum offset that can be accessed plus one
+ */
+static inline uint8_t parse_ipv6(packet_parser_t *prs, const uint8_t **parseptr,
+ uint32_t *offset, uint32_t frame_len,
+ uint32_t seg_end,
+ odp_pktin_config_opt_t opt,
+ uint64_t *l4_part_sum)
+{
+ const _odp_ipv6hdr_t *ipv6 = (const _odp_ipv6hdr_t *)*parseptr;
+ const _odp_ipv6hdr_ext_t *ipv6ext;
+ uint32_t dstaddr0 = odp_be_to_cpu_32(ipv6->dst_addr.u8[0]);
+ uint32_t l3_len = odp_be_to_cpu_16(ipv6->payload_len) +
+ _ODP_IPV6HDR_LEN;
+
+ /* Basic sanity checks on IPv6 header */
+ if (odp_unlikely(prs->flags.l3_chksum_err ||
+ (odp_be_to_cpu_32(ipv6->ver_tc_flow) >> 28) != 6 ||
+ sizeof(*ipv6) > frame_len - *offset ||
+ l3_len > frame_len - *offset)) {
+ prs->flags.ip_err = 1;
+ return 0;
+ }
+
+ /* IPv6 broadcast / multicast flags */
+ prs->input_flags.ip_mcast = (dstaddr0 & 0xff000000) == 0xff000000;
+ prs->input_flags.ip_bcast = 0;
+
+ /* Skip past IPv6 header */
+ *offset += sizeof(_odp_ipv6hdr_t);
+ *parseptr += sizeof(_odp_ipv6hdr_t);
+
+ if (opt.bit.udp_chksum || opt.bit.tcp_chksum)
+ *l4_part_sum = chksum_partial((const uint8_t *)&ipv6->src_addr,
+ 2 * _ODP_IPV6ADDR_LEN, 0);
+
+ /* Skip past any IPv6 extension headers */
+ if (ipv6->next_hdr == _ODP_IPPROTO_HOPOPTS ||
+ ipv6->next_hdr == _ODP_IPPROTO_ROUTE) {
+ prs->input_flags.ipopt = 1;
+
+ do {
+ ipv6ext = (const _odp_ipv6hdr_ext_t *)*parseptr;
+ uint16_t extlen = 8 + ipv6ext->ext_len * 8;
+
+ *offset += extlen;
+ *parseptr += extlen;
+ } while ((ipv6ext->next_hdr == _ODP_IPPROTO_HOPOPTS ||
+ ipv6ext->next_hdr == _ODP_IPPROTO_ROUTE) &&
+ *offset < seg_end);
+
+ if (*offset >= prs->l3_offset +
+ odp_be_to_cpu_16(ipv6->payload_len)) {
+ prs->flags.ip_err = 1;
+ return 0;
+ }
+
+ if (ipv6ext->next_hdr == _ODP_IPPROTO_FRAG)
+ prs->input_flags.ipfrag = 1;
+
+ return ipv6ext->next_hdr;
+ }
+
+ if (odp_unlikely(ipv6->next_hdr == _ODP_IPPROTO_FRAG)) {
+ prs->input_flags.ipopt = 1;
+ prs->input_flags.ipfrag = 1;
+ }
+
+ return ipv6->next_hdr;
+}
+
+/**
+ * Parser helper function for TCP
+ *
+ * Requires PARSE_TCP_BYTES bytes of contiguous packet data.
+ */
+static inline void parse_tcp(packet_parser_t *prs, const uint8_t **parseptr,
+ uint16_t tcp_len,
+ odp_pktin_config_opt_t opt,
+ uint64_t *l4_part_sum)
+{
+ const _odp_tcphdr_t *tcp = (const _odp_tcphdr_t *)*parseptr;
+ uint32_t len = tcp->hl * 4;
+
+ if (odp_unlikely(tcp->hl < sizeof(_odp_tcphdr_t) / sizeof(uint32_t)))
+ prs->flags.tcp_err = 1;
+
+ if (opt.bit.tcp_chksum &&
+ !prs->input_flags.ipfrag) {
+ *l4_part_sum += odp_cpu_to_be_16(tcp_len);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+ *l4_part_sum += _ODP_IPPROTO_TCP;
+#else
+ *l4_part_sum += _ODP_IPPROTO_TCP << 8;
+#endif
+ }
+
+ *parseptr += len;
+}
+
+/**
+ * Parser helper function for UDP
+ *
+ * Requires PARSE_UDP_BYTES bytes of contiguous packet data.
+ */
+static inline void parse_udp(packet_parser_t *prs, const uint8_t **parseptr,
+ odp_pktin_config_opt_t opt,
+ uint64_t *l4_part_sum)
+{
+ const _odp_udphdr_t *udp = (const _odp_udphdr_t *)*parseptr;
+ uint32_t udplen = odp_be_to_cpu_16(udp->length);
+ uint16_t ipsec_port = odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT);
+
+ if (odp_unlikely(udplen < sizeof(_odp_udphdr_t))) {
+ prs->flags.udp_err = 1;
+ return;
+ }
+
+ if (opt.bit.udp_chksum &&
+ !prs->input_flags.ipfrag) {
+ if (udp->chksum == 0) {
+ prs->input_flags.l4_chksum_done = 1;
+ prs->flags.l4_chksum_err =
+ (prs->input_flags.ipv4 != 1);
+ } else {
+ *l4_part_sum += udp->length;
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+ *l4_part_sum += _ODP_IPPROTO_UDP;
+#else
+ *l4_part_sum += _ODP_IPPROTO_UDP << 8;
+#endif
+ }
+ prs->input_flags.udp_chksum_zero = (udp->chksum == 0);
+ }
+
+ if (odp_unlikely(ipsec_port == udp->dst_port && udplen > 4)) {
+ uint32_t val;
+
+ memcpy(&val, udp + 1, 4);
+ if (val != 0) {
+ prs->input_flags.ipsec = 1;
+ prs->input_flags.ipsec_udp = 1;
+ }
+ }
+
+ *parseptr += sizeof(_odp_udphdr_t);
+}
+
+/**
+ * Parser helper function for SCTP
+ *
+ * Requires PARSE_SCTP_BYTES bytes of contiguous packet data.
+ */
+static inline void parse_sctp(packet_parser_t *prs, const uint8_t **parseptr,
+ uint16_t sctp_len,
+ odp_pktin_config_opt_t opt,
+ uint64_t *l4_part_sum)
+{
+ if (odp_unlikely(sctp_len < sizeof(_odp_sctphdr_t))) {
+ prs->flags.sctp_err = 1;
+ return;
+ }
+
+ if (opt.bit.sctp_chksum &&
+ !prs->input_flags.ipfrag) {
+ const _odp_sctphdr_t *sctp =
+ (const _odp_sctphdr_t *)*parseptr;
+ uint32_t crc = ~0;
+ uint32_t zero = 0;
+
+ crc = odp_hash_crc32c(sctp, sizeof(*sctp) - 4, crc);
+ crc = odp_hash_crc32c(&zero, 4, crc);
+ *l4_part_sum = crc;
+ }
+
+ *parseptr += sizeof(_odp_sctphdr_t);
+}
+
+/*
+ * Requires up to PARSE_L3_L4_BYTES bytes of contiguous packet data.
+ *
+ * - offset is the offset of the first byte of the data pointed to by parseptr
+ * - seg_end is the maximum offset that can be accessed plus one
+ */
+int _odp_packet_parse_common_l3_l4(packet_parser_t *prs,
+ const uint8_t *parseptr, uint32_t offset,
+ uint32_t frame_len, uint32_t seg_end,
+ int layer, uint16_t ethtype,
+ uint64_t *l4_part_sum,
+ odp_pktin_config_opt_t opt)
+{
+ uint8_t ip_proto;
+
+ prs->l3_offset = offset;
+
+ if (odp_unlikely(layer <= ODP_PROTO_LAYER_L2))
+ return prs->flags.all.error != 0;
+
+ /* Set l3 flag only for known ethtypes */
+ prs->input_flags.l3 = 1;
+
+ /* Parse Layer 3 headers */
+ switch (ethtype) {
+ case _ODP_ETHTYPE_IPV4:
+ prs->input_flags.ipv4 = 1;
+ ip_proto = parse_ipv4(prs, &parseptr, &offset, frame_len,
+ opt, l4_part_sum);
+ if (odp_likely(!prs->flags.ip_err))
+ prs->l4_offset = offset;
+ else if (opt.bit.drop_ipv4_err)
+ return -1; /* drop */
+ break;
+
+ case _ODP_ETHTYPE_IPV6:
+ prs->input_flags.ipv6 = 1;
+ ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,
+ seg_end, opt, l4_part_sum);
+ if (odp_likely(!prs->flags.ip_err))
+ prs->l4_offset = offset;
+ else if (opt.bit.drop_ipv6_err)
+ return -1; /* drop */
+ break;
+
+ case _ODP_ETHTYPE_ARP:
+ prs->input_flags.arp = 1;
+ ip_proto = 255; /* Reserved invalid by IANA */
+ break;
+
+ default:
+ prs->input_flags.l3 = 0;
+ ip_proto = 255; /* Reserved invalid by IANA */
+ }
+
+ if (layer == ODP_PROTO_LAYER_L3)
+ return prs->flags.all.error != 0;
+
+ /* Set l4 flag only for known ip_proto */
+ prs->input_flags.l4 = 1;
+
+ /* Parse Layer 4 headers */
+ switch (ip_proto) {
+ case _ODP_IPPROTO_ICMPV4:
+ /* Fall through */
+
+ case _ODP_IPPROTO_ICMPV6:
+ prs->input_flags.icmp = 1;
+ break;
+
+ case _ODP_IPPROTO_IPIP:
+ /* Do nothing */
+ break;
+
+ case _ODP_IPPROTO_TCP:
+ if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_end))
+ return -1;
+ prs->input_flags.tcp = 1;
+ parse_tcp(prs, &parseptr, frame_len - prs->l4_offset, opt,
+ l4_part_sum);
+ if (prs->flags.tcp_err && opt.bit.drop_tcp_err)
+ return -1; /* drop */
+ break;
+
+ case _ODP_IPPROTO_UDP:
+ if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_end))
+ return -1;
+ prs->input_flags.udp = 1;
+ parse_udp(prs, &parseptr, opt, l4_part_sum);
+ if (prs->flags.udp_err && opt.bit.drop_udp_err)
+ return -1; /* drop */
+ break;
+
+ case _ODP_IPPROTO_AH:
+ prs->input_flags.ipsec = 1;
+ prs->input_flags.ipsec_ah = 1;
+ break;
+
+ case _ODP_IPPROTO_ESP:
+ prs->input_flags.ipsec = 1;
+ prs->input_flags.ipsec_esp = 1;
+ break;
+
+ case _ODP_IPPROTO_SCTP:
+ prs->input_flags.sctp = 1;
+ parse_sctp(prs, &parseptr, frame_len - prs->l4_offset, opt,
+ l4_part_sum);
+ if (prs->flags.sctp_err && opt.bit.drop_sctp_err)
+ return -1; /* drop */
+ break;
+
+ case _ODP_IPPROTO_NO_NEXT:
+ prs->input_flags.no_next_hdr = 1;
+ break;
+
+ default:
+ prs->input_flags.l4 = 0;
+ break;
+ }
+
+ return prs->flags.all.error != 0;
+}
diff --git a/platform/linux-generic/odp_pcapng.c b/platform/linux-generic/odp_pcapng.c
new file mode 100644
index 000000000..db9bf1af7
--- /dev/null
+++ b/platform/linux-generic/odp_pcapng.c
@@ -0,0 +1,604 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/autoheader_internal.h>
+
+#if defined(_ODP_PCAPNG) && _ODP_PCAPNG == 1
+
+#include <odp/api/hints.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/spinlock.h>
+
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/packet_io_inlines.h>
+
+#include <odp_config_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_pcapng.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
+
+#define PCAPNG_BLOCK_TYPE_EPB 0x00000006UL
+#define PCAPNG_BLOCK_TYPE_SHB 0x0A0D0D0AUL
+#define PCAPNG_BLOCK_TYPE_IDB 0x00000001UL
+#define PCAPNG_ENDIAN_MAGIC 0x1A2B3C4DUL
+#define PCAPNG_DATA_ALIGN 4
+#define PCAPNG_LINKTYPE_ETHERNET 0x1
+
+/* inotify */
+#define INOTIFY_BUF_LEN (16 * (sizeof(struct inotify_event)))
+#define PCAPNG_WATCH_DIR "/var/run/odp/"
+
+#define PKTIO_MAX_QUEUES (ODP_PKTIN_MAX_QUEUES > ODP_PKTOUT_MAX_QUEUES ? \
+ ODP_PKTIN_MAX_QUEUES : ODP_PKTOUT_MAX_QUEUES)
+
+/* pcapng: enhanced packet block file encoding */
+typedef struct ODP_PACKED pcapng_section_hdr_block_s {
+ uint32_t block_type;
+ uint32_t block_total_length;
+ uint32_t magic;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int64_t section_len;
+ uint32_t block_total_length2;
+} pcapng_section_hdr_block_t;
+
+typedef struct pcapng_interface_description_block {
+ uint32_t block_type;
+ uint32_t block_total_length;
+ uint16_t linktype;
+ uint16_t reserved;
+ uint32_t snaplen;
+ uint32_t block_total_length2;
+} pcapng_interface_description_block_t;
+
+typedef struct pcapng_enhanced_packet_block_s {
+ uint32_t block_type;
+ uint32_t block_total_length;
+ uint32_t interface_idx;
+ uint32_t timestamp_high;
+ uint32_t timestamp_low;
+ uint32_t captured_len;
+ uint32_t packet_len;
+} pcapng_enhanced_packet_block_t;
+
+/** Pktio entry specific data */
+typedef struct {
+ pktio_entry_t *pktio_entry;
+
+ /* inotify instances for pcapng fifos */
+ enum {
+ PCAPNG_WR_STOP = 0,
+ PCAPNG_WR_PKT,
+ } state[PKTIO_MAX_QUEUES];
+ int fd[PKTIO_MAX_QUEUES];
+} pcapng_entry_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ odp_shm_t shm;
+ int num_entries;
+ pthread_t inotify_thread;
+ int inotify_fd;
+ int inotify_watch_fd;
+ int inotify_is_running;
+ odp_spinlock_t lock;
+ pcapng_entry_t entry[CONFIG_PKTIO_ENTRIES];
+} pcapng_global_t;
+
+static pcapng_global_t *pcapng_gbl;
+
+static inline pcapng_entry_t *pcapng_entry(pktio_entry_t *pktio_entry)
+{
+ return &pcapng_gbl->entry[odp_pktio_index(pktio_entry->handle)];
+}
+
+int write_pcapng_hdr(pktio_entry_t *entry, int qidx);
+
+int _odp_pcapng_init_global(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("_odp_pcapng_global", sizeof(pcapng_global_t),
+ ODP_PAGE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ pcapng_gbl = odp_shm_addr(shm);
+
+ memset(pcapng_gbl, 0, sizeof(pcapng_global_t));
+ pcapng_gbl->shm = shm;
+
+ odp_spinlock_init(&pcapng_gbl->lock);
+
+ return 0;
+}
+
+int _odp_pcapng_term_global(void)
+{
+ int ret = 0;
+
+ if (odp_shm_free(pcapng_gbl->shm)) {
+ _ODP_ERR("shm free failed");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void pcapng_drain_fifo(int fd)
+{
+ char buffer[4096];
+ ssize_t len;
+
+ do {
+ len = read(fd, buffer, sizeof(buffer));
+ } while (len > 0);
+}
+
+static void inotify_event_handle(pktio_entry_t *entry, int qidx,
+ struct inotify_event *event)
+{
+ pcapng_entry_t *pcapng = pcapng_entry(entry);
+ int mtu = _ODP_MAX(odp_pktin_maxlen(entry->handle), odp_pktout_maxlen(entry->handle));
+
+ if (event->mask & IN_OPEN) {
+ int ret;
+
+ if (PIPE_BUF < mtu + sizeof(pcapng_enhanced_packet_block_t) +
+ sizeof(uint32_t)) {
+ _ODP_ERR("PIPE_BUF:%d too small. Disabling pcap\n", PIPE_BUF);
+ pcapng->state[qidx] = PCAPNG_WR_STOP;
+
+ return;
+ }
+
+ ret = write_pcapng_hdr(entry, qidx);
+ if (ret) {
+ pcapng->state[qidx] = PCAPNG_WR_STOP;
+ } else {
+ pcapng->state[qidx] = PCAPNG_WR_PKT;
+ _ODP_DBG("Open %s for pcap tracing\n", event->name);
+ }
+ } else if (event->mask & IN_CLOSE) {
+ int fd = pcapng->fd[qidx];
+
+ pcapng_drain_fifo(fd);
+ pcapng->state[qidx] = PCAPNG_WR_STOP;
+ _ODP_DBG("Close %s for pcap tracing\n", event->name);
+ } else {
+ _ODP_ERR("Unknown inotify event 0x%08x\n", event->mask);
+ }
+}
+
+static void get_pcapng_fifo_name(char *pcapng_entry, size_t len,
+ char *pktio_name, int qidx)
+{
+ snprintf(pcapng_entry, len, "%d-%s-flow-%d",
+ odp_global_ro.main_pid, pktio_name, qidx);
+ pcapng_entry[len - 1] = 0;
+}
+
+static int get_qidx_from_fifo(pktio_entry_t *entry, char *name)
+{
+ unsigned int max_queue = _ODP_MAX(entry->num_in_queue, entry->num_out_queue);
+ unsigned int i;
+
+ for (i = 0; i < max_queue; i++) {
+ char pcapng_entry[256];
+
+ get_pcapng_fifo_name(pcapng_entry, sizeof(pcapng_entry),
+ entry->name, i);
+ /*
+ * verify we still talk to a fifo before returning a valid
+ * queue number
+ */
+ if (strcmp(name, pcapng_entry) == 0) {
+ struct stat fstat;
+ char pcapng_path[256];
+
+ snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+ PCAPNG_WATCH_DIR, name);
+ stat(pcapng_path, &fstat);
+
+ return S_ISFIFO(fstat.st_mode) ? (int)i : -1;
+ }
+ }
+
+ return -1;
+}
+
+static pktio_entry_t *pktio_from_event(struct inotify_event *event)
+{
+ int i;
+
+ odp_spinlock_lock(&pcapng_gbl->lock);
+
+ for (i = 0; i < CONFIG_PKTIO_ENTRIES; i++) {
+ pktio_entry_t *entry = pcapng_gbl->entry[i].pktio_entry;
+
+ if (entry == NULL)
+ continue;
+
+ if (get_qidx_from_fifo(entry, event->name) != -1) {
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+ return entry;
+ }
+ }
+
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+
+ return NULL;
+}
+
+static void *inotify_update(void *arg)
+{
+ struct timeval time;
+ ssize_t rdlen;
+ int offset;
+ char buffer[INOTIFY_BUF_LEN];
+ fd_set rfds;
+ int inotify_fd = *(int *)arg;
+
+ while (1) {
+ offset = 0;
+ FD_ZERO(&rfds);
+ FD_SET(inotify_fd, &rfds);
+ time.tv_sec = 2;
+ time.tv_usec = 0;
+ select(inotify_fd + 1, &rfds, NULL, NULL, &time);
+ if (FD_ISSET(inotify_fd, &rfds)) {
+ rdlen = read(inotify_fd, buffer, INOTIFY_BUF_LEN);
+ while (offset < rdlen) {
+ int qidx;
+ struct inotify_event *event =
+ (struct inotify_event *)(void *)
+ &buffer[offset];
+ pktio_entry_t *entry;
+
+ offset += sizeof(struct inotify_event) +
+ event->len;
+
+ entry = pktio_from_event(event);
+ if (entry == NULL)
+ continue;
+
+ qidx = get_qidx_from_fifo(entry, event->name);
+ if (qidx == -1)
+ continue;
+
+ inotify_event_handle(entry, qidx, event);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int get_fifo_max_size(void)
+{
+ FILE *file;
+ char buf[128];
+ int ret = -1;
+
+ file = fopen("/proc/sys/fs/pipe-max-size", "r");
+ if (file == NULL)
+ return ret;
+
+ if (fgets(buf, sizeof(buf), file))
+ ret = atoi(buf);
+
+ fclose(file);
+
+ return ret;
+}
+
+int _odp_pcapng_start(pktio_entry_t *entry)
+{
+ pcapng_entry_t *pcapng = pcapng_entry(entry);
+ int ret = -1, fd;
+ pthread_attr_t attr;
+ unsigned int i;
+ unsigned int max_queue = _ODP_MAX(entry->num_in_queue, entry->num_out_queue);
+ int fifo_sz;
+
+ fifo_sz = get_fifo_max_size();
+ if (fifo_sz < 0)
+ _ODP_DBG("failed to read max fifo size\n");
+
+ for (i = 0; i < max_queue; i++) {
+ char pcapng_name[128];
+ char pcapng_path[256];
+
+ pcapng->fd[i] = -1;
+ pcapng->state[i] = PCAPNG_WR_STOP;
+
+ get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name),
+ entry->name, i);
+ snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+ PCAPNG_WATCH_DIR, pcapng_name);
+ if (mkfifo(pcapng_path, O_RDWR)) {
+ _ODP_ERR("pcap not available for %s %s\n", pcapng_path, strerror(errno));
+ continue;
+ }
+
+ if (chmod(pcapng_path, S_IRUSR | S_IRGRP))
+ _ODP_ERR("Failed to change file permission for %s %s\n",
+ pcapng_path, strerror(errno));
+
+ fd = open(pcapng_path, O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ _ODP_ERR("Fail to open fifo\n");
+ pcapng->state[i] = PCAPNG_WR_STOP;
+ if (remove(pcapng_path) == -1)
+ _ODP_ERR("Can't remove fifo %s\n", pcapng_path);
+ continue;
+ }
+
+ if (fifo_sz > 0) {
+ if (fcntl(fd, F_SETPIPE_SZ, fifo_sz) != fifo_sz)
+ _ODP_DBG("Failed to set max fifo size\n");
+ else
+ _ODP_DBG("set pcap fifo size %i\n", fifo_sz);
+ }
+
+ pcapng->fd[i] = fd;
+ }
+
+ odp_spinlock_lock(&pcapng_gbl->lock);
+
+ /* already running from a previous pktio */
+ if (pcapng_gbl->inotify_is_running == 1) {
+ pcapng->pktio_entry = entry;
+ pcapng_gbl->num_entries++;
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+ return 0;
+ }
+
+ pcapng_gbl->inotify_fd = -1;
+ pcapng_gbl->inotify_watch_fd = -1;
+
+ pcapng_gbl->inotify_fd = inotify_init();
+ if (pcapng_gbl->inotify_fd == -1) {
+ _ODP_ERR("can't init inotify. pcap disabled\n");
+ goto out_destroy;
+ }
+
+ pcapng_gbl->inotify_watch_fd = inotify_add_watch(pcapng_gbl->inotify_fd,
+ PCAPNG_WATCH_DIR,
+ IN_CLOSE | IN_OPEN);
+
+ if (pcapng_gbl->inotify_watch_fd == -1) {
+ _ODP_ERR("can't register inotify for %s. pcap disabled\n", strerror(errno));
+ goto out_destroy;
+ }
+
+ /* create a thread to poll inotify triggers */
+ pthread_attr_init(&attr);
+ ret = pthread_create(&pcapng_gbl->inotify_thread, &attr, inotify_update,
+ &pcapng_gbl->inotify_fd);
+ if (ret) {
+ _ODP_ERR("Can't start inotify thread (ret=%d). pcapng disabled.\n", ret);
+ } else {
+ pcapng->pktio_entry = entry;
+ pcapng_gbl->num_entries++;
+ pcapng_gbl->inotify_is_running = 1;
+ }
+
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+
+ return ret;
+
+out_destroy:
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+
+ _odp_pcapng_stop(entry);
+
+ return ret;
+}
+
+void _odp_pcapng_stop(pktio_entry_t *entry)
+{
+ pcapng_entry_t *pcapng = pcapng_entry(entry);
+ int ret;
+ unsigned int i;
+ unsigned int max_queue = _ODP_MAX(entry->num_in_queue, entry->num_out_queue);
+
+ odp_spinlock_lock(&pcapng_gbl->lock);
+
+ pcapng->pktio_entry = NULL;
+ pcapng_gbl->num_entries--;
+
+ if (pcapng_gbl->inotify_is_running == 1 &&
+ pcapng_gbl->num_entries == 0) {
+ ret = pthread_cancel(pcapng_gbl->inotify_thread);
+ if (ret)
+ _ODP_ERR("can't cancel inotify thread %s\n", strerror(errno));
+ pcapng_gbl->inotify_is_running = 0;
+ }
+
+ if (pcapng_gbl->num_entries == 0) {
+ /* fd's will be -1 in case of any failure */
+ ret = inotify_rm_watch(pcapng_gbl->inotify_fd,
+ pcapng_gbl->inotify_watch_fd);
+ if (ret)
+ _ODP_ERR("can't deregister inotify %s\n", strerror(errno));
+
+ if (pcapng_gbl->inotify_fd != -1)
+ close(pcapng_gbl->inotify_fd);
+
+ if (pcapng_gbl->inotify_watch_fd != -1)
+ close(pcapng_gbl->inotify_watch_fd);
+ }
+
+ odp_spinlock_unlock(&pcapng_gbl->lock);
+
+ for (i = 0; i < max_queue; i++) {
+ char pcapng_name[128];
+ char pcapng_path[256];
+
+ pcapng->state[i] = PCAPNG_WR_STOP;
+ close(pcapng->fd[i]);
+
+ get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name),
+ entry->name, i);
+ snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+ PCAPNG_WATCH_DIR, pcapng_name);
+
+ if (remove(pcapng_path))
+ _ODP_ERR("can't delete fifo %s\n", pcapng_path);
+ }
+}
+
+int write_pcapng_hdr(pktio_entry_t *entry, int qidx)
+{
+ pcapng_entry_t *pcapng = pcapng_entry(entry);
+ size_t len;
+ pcapng_section_hdr_block_t shb;
+ pcapng_interface_description_block_t idb;
+ int fd = pcapng->fd[qidx];
+
+ memset(&shb, 0, sizeof(shb));
+ memset(&idb, 0, sizeof(idb));
+
+ shb.block_type = PCAPNG_BLOCK_TYPE_SHB;
+ shb.block_total_length = sizeof(shb);
+ shb.block_total_length2 = sizeof(shb);
+ shb.magic = PCAPNG_ENDIAN_MAGIC;
+ shb.version_major = 0x1;
+ shb.version_minor = 0x0;
+ shb.section_len = -1;
+
+ len = write(fd, &shb, sizeof(shb));
+ /* fail to write shb/idb means the pcapng is unreadable */
+ if (len != sizeof(shb)) {
+ _ODP_ERR("Failed to write pcapng section hdr\n");
+ return -1;
+ }
+ fsync(fd);
+
+ idb.block_type = PCAPNG_BLOCK_TYPE_IDB;
+ idb.block_total_length = sizeof(idb);
+ idb.block_total_length2 = sizeof(idb);
+ idb.linktype = PCAPNG_LINKTYPE_ETHERNET;
+ idb.snaplen = 0x0; /* unlimited */
+ len = write(fd, &idb, sizeof(idb));
+ if (len != sizeof(idb)) {
+ _ODP_ERR("Failed to write pcapng interface description\n");
+ return -1;
+ }
+ fsync(fd);
+
+ return 0;
+}
+
+/*
+ * make sure that each fifo write is less than PIPE_BUF
+ * this will make sure writes are atomic (on non blocking mode).
+ * writev() transfers all the data and returns the number of bytes requested or
+ * -EAGAIN
+ */
+static ssize_t write_fifo(int fd, struct iovec *iov, int iovcnt)
+{
+ ssize_t len = 0;
+
+ len = writev(fd, iov, iovcnt);
+ /*
+ * we don't care if a writev fails, we asynchronously read the fifo
+ * so the next block of packets might be successful. This error only
+ * means that some packets failed to append on the pcap file
+ */
+ if (len > 0)
+ fsync(fd);
+
+ return len;
+}
+
+int _odp_pcapng_dump_pkts(pktio_entry_t *entry, int qidx,
+ const odp_packet_t packets[], int num)
+{
+ pcapng_entry_t *pcapng = pcapng_entry(entry);
+ int i = 0;
+ struct iovec packet_iov[3 * num];
+ pcapng_enhanced_packet_block_t epb[num];
+ int iovcnt = 0;
+ ssize_t block_len = 0;
+ int fd = pcapng->fd[qidx];
+ ssize_t len = 0, wlen;
+
+ if (odp_likely(pcapng->state[qidx] != PCAPNG_WR_PKT))
+ return 0;
+
+ for (i = 0; i < num; i++) {
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(packets[i]);
+ uint32_t seg_len;
+ char *buf = (char *)odp_packet_offset(packets[i], 0, &seg_len,
+ NULL);
+
+ if (block_len + sizeof(epb[i]) +
+ _ODP_ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) +
+ sizeof(uint32_t) > PIPE_BUF) {
+ wlen = write_fifo(fd, packet_iov, iovcnt);
+ if (wlen > 0) {
+ len += wlen;
+ block_len = 0;
+ iovcnt = 0;
+ }
+ }
+ epb[i].block_type = PCAPNG_BLOCK_TYPE_EPB;
+ epb[i].block_total_length = sizeof(epb[i]) +
+ _ODP_ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) +
+ PCAPNG_DATA_ALIGN;
+ epb[i].interface_idx = 0;
+ epb[i].timestamp_high =
+ (uint32_t)(pkt_hdr->timestamp.u64 >> 32);
+ epb[i].timestamp_low = (uint32_t)(pkt_hdr->timestamp.u64);
+ epb[i].captured_len = seg_len;
+ epb[i].packet_len = seg_len;
+
+ /* epb */
+ packet_iov[iovcnt].iov_base = &epb[i];
+ packet_iov[iovcnt].iov_len = sizeof(epb[i]);
+ block_len += packet_iov[iovcnt].iov_len;
+ iovcnt++;
+
+ /* data */
+ packet_iov[iovcnt].iov_base = buf;
+ packet_iov[iovcnt].iov_len = _ODP_ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN);
+ block_len += packet_iov[iovcnt].iov_len;
+ iovcnt++;
+
+ /* trailing */
+ packet_iov[iovcnt].iov_base = &epb[i].block_total_length;
+ packet_iov[iovcnt].iov_len = sizeof(uint32_t);
+ block_len += packet_iov[iovcnt].iov_len;
+ iovcnt++;
+ }
+
+ if (iovcnt) {
+ wlen = write_fifo(fd, packet_iov, iovcnt);
+ if (wlen > 0)
+ len += wlen;
+ }
+
+ return len;
+}
+
+#else /* _ODP_PCAPNG */
+/* Avoid warning about empty translation unit */
+typedef int _odp_dummy;
+#endif
diff --git a/platform/linux-generic/odp_pkt_queue.c b/platform/linux-generic/odp_pkt_queue.c
index f1086096d..1a5c9bd3c 100644
--- a/platform/linux-generic/odp_pkt_queue.c
+++ b/platform/linux-generic/odp_pkt_queue.c
@@ -1,44 +1,37 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
-
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
+#include <odp/api/packet.h>
+
+#include <odp_pkt_queue_internal.h>
+#include <odp_traffic_mngr_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
+
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <inttypes.h>
-#include <odp_api.h>
-#include <odp_pkt_queue_internal.h>
-#include <odp_debug_internal.h>
-
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#include <stdalign.h>
#define NUM_PKTS 7
-typedef struct /* Must be exactly 64 bytes long AND cacheline aligned! */ {
+/* Must be exactly 64 bytes long AND cacheline aligned! */
+typedef struct ODP_ALIGNED_CACHE {
uint32_t next_queue_blk_idx;
uint32_t tail_queue_blk_idx;
odp_packet_t pkts[NUM_PKTS];
-} ODP_ALIGNED_CACHE queue_blk_t;
+} queue_blk_t;
-typedef struct {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+typedef struct ODP_ALIGNED_CACHE {
queue_blk_t blks[0];
-} ODP_ALIGNED_CACHE queue_blks_t;
-
-/* The queue_num_tbl is used to map from a queue_num to a queue_num_desc.
- * The reason is based on the assumption that usually only a small fraction
- * of the max_num_queues will have more than 1 pkt associated with it. This
- * way the active queue_desc's can be dynamically allocated and freed according
- * to the actual usage pattern.
- */
-typedef struct {
- uint32_t queue_num_to_blk_idx[0];
-} queue_num_tbl_t;
+} queue_blks_t;
+#pragma GCC diagnostic pop
typedef struct {
uint32_t num_blks;
@@ -47,6 +40,7 @@ typedef struct {
} queue_region_desc_t;
typedef struct {
+ uint8_t queue_status[ODP_TM_MAX_TM_QUEUES];
uint64_t total_pkt_appends;
uint64_t total_pkt_removes;
uint64_t total_bad_removes;
@@ -56,7 +50,7 @@ typedef struct {
uint32_t free_list_head_idx;
uint32_t max_queue_num;
uint32_t max_queued_pkts;
- uint32_t next_queue_num;
+ uint32_t num_queues;
queue_region_desc_t queue_region_descs[16];
uint32_t *queue_num_tbl;
uint8_t current_region;
@@ -113,7 +107,7 @@ static int pkt_queue_free_list_add(queue_pool_t *pool,
queue_blks = region_desc->queue_blks;
if (!queue_blks) {
malloc_len = num_blks * sizeof(queue_blk_t);
- queue_blks = malloc(malloc_len);
+ queue_blks = aligned_alloc(alignof(queue_blk_t), malloc_len);
if (!queue_blks) {
free_alloced_queue_blks(alloc_cnt,
alloced_queue_blks);
@@ -129,7 +123,7 @@ static int pkt_queue_free_list_add(queue_pool_t *pool,
}
/* Now add as many queue_blks to the free list as... */
- blks_to_add = MIN(num_blks - start_idx, num_queue_blks);
+ blks_to_add = _ODP_MIN(num_blks - start_idx, num_queue_blks);
queue_blk = &queue_blks->blks[start_idx];
for (cnt = 1; cnt <= blks_to_add; cnt++) {
queue_blk->next_queue_blk_idx = start_idx + cnt;
@@ -227,7 +221,7 @@ _odp_int_queue_pool_t _odp_queue_pool_create(uint32_t max_num_queues,
/* Initialize the queue_blk_tbl_sizes array based upon the
* max_queued_pkts.
*/
- max_queued_pkts = MAX(max_queued_pkts, 64 * 1024);
+ max_queued_pkts = _ODP_MAX(max_queued_pkts, 64 * UINT32_C(1024));
queue_region_desc_init(pool, 0, max_queued_pkts / 4);
queue_region_desc_init(pool, 1, max_queued_pkts / 64);
queue_region_desc_init(pool, 2, max_queued_pkts / 64);
@@ -239,7 +233,7 @@ _odp_int_queue_pool_t _odp_queue_pool_create(uint32_t max_num_queues,
/* Now allocate the first queue_blk_tbl and add its blks to the free
* list. Replenish the queue_blk_t free list.
*/
- initial_free_list_size = MIN(64 * 1024, max_queued_pkts / 4);
+ initial_free_list_size = _ODP_MIN(64 * UINT32_C(1024), max_queued_pkts / 4);
rc = pkt_queue_free_list_add(pool, initial_free_list_size);
if (rc < 0) {
free(pool->queue_num_tbl);
@@ -252,7 +246,7 @@ _odp_int_queue_pool_t _odp_queue_pool_create(uint32_t max_num_queues,
pool->max_queue_num = max_num_queues;
pool->max_queued_pkts = max_queued_pkts;
- pool->next_queue_num = 1;
+ pool->num_queues = 0;
pool->min_free_list_size = pool->free_list_size;
pool->peak_free_list_size = pool->free_list_size;
@@ -261,15 +255,35 @@ _odp_int_queue_pool_t _odp_queue_pool_create(uint32_t max_num_queues,
_odp_int_pkt_queue_t _odp_pkt_queue_create(_odp_int_queue_pool_t queue_pool)
{
- queue_pool_t *pool;
- uint32_t queue_num;
+ queue_pool_t *pool = (queue_pool_t *)(uintptr_t)queue_pool;
+ uint32_t i;
- pool = (queue_pool_t *)(uintptr_t)queue_pool;
- queue_num = pool->next_queue_num++;
- if (pool->max_queue_num < queue_num)
+ if (pool->num_queues >= pool->max_queue_num)
return _ODP_INT_PKT_QUEUE_INVALID;
- return (_odp_int_pkt_queue_t)queue_num;
+ for (i = 0; i < pool->max_queue_num; i++) {
+ if (pool->queue_status[i] == TM_STATUS_FREE) {
+ pool->queue_status[i] = TM_STATUS_RESERVED;
+ pool->num_queues++;
+ return (_odp_int_pkt_queue_t)(i + 1);
+ }
+ }
+ return _ODP_INT_PKT_QUEUE_INVALID;
+}
+
+void _odp_pkt_queue_destroy(_odp_int_queue_pool_t queue_pool,
+ _odp_int_pkt_queue_t pkt_queue)
+{
+ queue_pool_t *pool = (queue_pool_t *)(uintptr_t)queue_pool;
+ uint32_t queue_num = (uint32_t)pkt_queue;
+
+ if ((queue_num == 0) || (queue_num > pool->max_queue_num)) {
+ _ODP_ERR("Invalid TM packet queue ID\n");
+ return;
+ }
+
+ pool->queue_status[queue_num - 1] = TM_STATUS_FREE;
+ pool->num_queues--;
}
int _odp_pkt_queue_append(_odp_int_queue_pool_t queue_pool,
@@ -390,17 +404,17 @@ void _odp_pkt_queue_stats_print(_odp_int_queue_pool_t queue_pool)
queue_pool_t *pool;
pool = (queue_pool_t *)(uintptr_t)queue_pool;
- ODP_DBG("pkt_queue_stats - queue_pool=0x%" PRIX64 "\n", queue_pool);
- ODP_DBG(" max_queue_num=%u max_queued_pkts=%u next_queue_num=%u\n",
- pool->max_queue_num, pool->max_queued_pkts,
- pool->next_queue_num);
- ODP_DBG(" total pkt appends=%" PRIu64 " total pkt removes=%" PRIu64
- " bad removes=%" PRIu64 "\n",
- pool->total_pkt_appends, pool->total_pkt_removes,
- pool->total_bad_removes);
- ODP_DBG(" free_list size=%u min size=%u peak size=%u\n",
- pool->free_list_size, pool->min_free_list_size,
- pool->peak_free_list_size);
+ _ODP_PRINT(" pkt_queue_stats - queue_pool=0x%" PRIX64 "\n", queue_pool);
+ _ODP_PRINT(" max_queue_num=%" PRIu32 " max_queued_pkts=%" PRIu32 " "
+ "num_queues=%" PRIu32 "\n", pool->max_queue_num,
+ pool->max_queued_pkts, pool->num_queues);
+ _ODP_PRINT(" total pkt appends=%" PRIu64 " total pkt removes=%" PRIu64
+ " bad removes=%" PRIu64 "\n",
+ pool->total_pkt_appends, pool->total_pkt_removes,
+ pool->total_bad_removes);
+ _ODP_PRINT(" free_list size=%u min size=%u peak size=%u\n",
+ pool->free_list_size, pool->min_free_list_size,
+ pool->peak_free_list_size);
}
void _odp_queue_pool_destroy(_odp_int_queue_pool_t queue_pool)
diff --git a/platform/linux-generic/odp_pool.c b/platform/linux-generic/odp_pool.c
index 35e39f5d3..1345705df 100644
--- a/platform/linux-generic/odp_pool.c
+++ b/platform/linux-generic/odp_pool.c
@@ -1,139 +1,400 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
+#include <odp/api/align.h>
+#include <odp/api/atomic.h>
+#include <odp/api/hints.h>
#include <odp/api/pool.h>
#include <odp/api/shared_memory.h>
-#include <odp/api/align.h>
-#include <odp/api/ticketlock.h>
#include <odp/api/system_info.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/pool_inline_types.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp/api/plat/ticketlock_inlines.h>
#include <odp_pool_internal.h>
-#include <odp_internal.h>
-#include <odp_buffer_inlines.h>
+#include <odp_init_internal.h>
#include <odp_packet_internal.h>
#include <odp_config_internal.h>
#include <odp_debug_internal.h>
-#include <odp_ring_internal.h>
+#include <odp_event_internal.h>
+#include <odp_event_validation_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ring_ptr_internal.h>
+#include <odp_global_data.h>
+#include <odp_libconfig_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_timer_internal.h>
+#include <odp_event_vector_internal.h>
+#include <odp_buffer_internal.h>
+#include <odp_string_internal.h>
#include <string.h>
#include <stdio.h>
+#include <stddef.h>
#include <inttypes.h>
-#include <odp/api/plat/ticketlock_inlines.h>
-#define LOCK(a) _odp_ticketlock_lock(a)
-#define UNLOCK(a) _odp_ticketlock_unlock(a)
+#define LOCK(a) odp_ticketlock_lock(a)
+#define UNLOCK(a) odp_ticketlock_unlock(a)
#define LOCK_INIT(a) odp_ticketlock_init(a)
-#define CACHE_BURST 32
-#define RING_SIZE_MIN (2 * CACHE_BURST)
+#define RING_SIZE_MIN 64
+#define POOL_MAX_NUM_MIN RING_SIZE_MIN
+
+/* Make sure packet buffers don't cross huge page boundaries starting from this
+ * page size. 2MB is typically the smallest used huge page size. */
+#define FIRST_HP_SIZE (2 * 1024 * 1024)
/* Define a practical limit for contiguous memory allocations */
#define MAX_SIZE (10 * 1024 * 1024)
-ODP_STATIC_ASSERT(CONFIG_POOL_CACHE_SIZE > (2 * CACHE_BURST),
- "cache_burst_size_too_large_compared_to_cache_size");
+/* Maximum packet user area size */
+#define MAX_UAREA_SIZE 2048
ODP_STATIC_ASSERT(CONFIG_PACKET_SEG_LEN_MIN >= 256,
"ODP Segment size must be a minimum of 256 bytes");
+ODP_STATIC_ASSERT(CONFIG_PACKET_SEG_SIZE < 0xffff,
+ "Segment size must be less than 64k (16 bit offsets)");
+
+ODP_STATIC_ASSERT(CONFIG_INTERNAL_POOLS < CONFIG_POOLS,
+ "Internal pool count needs to be less than total configured pool count");
+
/* Thread local variables */
typedef struct pool_local_t {
- pool_cache_t *cache[ODP_CONFIG_POOLS];
+ pool_cache_t *cache[CONFIG_POOLS];
int thr_id;
+
} pool_local_t;
-pool_table_t *pool_tbl;
+extern const _odp_pool_mem_src_ops_t * const _odp_pool_mem_src_ops[];
+
+pool_global_t *_odp_pool_glb;
static __thread pool_local_t local;
-static inline odp_pool_t pool_index_to_handle(uint32_t pool_idx)
+#include <odp/visibility_begin.h>
+
+/* Fill in pool header field offsets for inline functions */
+const _odp_pool_inline_offset_t _odp_pool_inline ODP_ALIGNED_CACHE = {
+ .index = offsetof(pool_t, pool_idx),
+ .seg_len = offsetof(pool_t, seg_len),
+ .uarea_size = offsetof(pool_t, param_uarea_size),
+ .trailer_size = offsetof(pool_t, trailer_size),
+ .ext_head_offset = offsetof(pool_t, ext_head_offset),
+ .ext_pkt_buf_size = offsetof(pool_t, ext_param.pkt.buf_size)
+};
+
+#include <odp/visibility_end.h>
+
+static inline void cache_init(pool_cache_t *cache)
{
- return _odp_cast_scalar(odp_pool_t, pool_idx);
+ memset(cache, 0, sizeof(pool_cache_t));
+ odp_atomic_init_u32(&cache->cache_num, 0);
}
-static inline pool_t *pool_from_buf(odp_buffer_t buf)
+static inline uint32_t cache_pop(pool_cache_t *cache,
+ _odp_event_hdr_t *event_hdr[], int max_num)
{
- odp_buffer_hdr_t *buf_hdr = buf_hdl_to_hdr(buf);
+ uint32_t cache_num = odp_atomic_load_u32(&cache->cache_num);
+ uint32_t num_ch = max_num;
+ uint32_t cache_begin;
+ uint32_t i;
+
+ /* Cache does not have enough buffers */
+ if (odp_unlikely(cache_num < (uint32_t)max_num))
+ num_ch = cache_num;
+
+ /* Get buffers from the cache */
+ cache_begin = cache_num - num_ch;
+ for (i = 0; i < num_ch; i++)
+ event_hdr[i] = cache->event_hdr[cache_begin + i];
+
+ odp_atomic_store_u32(&cache->cache_num, cache_num - num_ch);
- return buf_hdr->pool_ptr;
+ return num_ch;
}
-static inline odp_buffer_hdr_t *buf_hdr_from_index(pool_t *pool,
- uint32_t buffer_idx)
+static inline void cache_push(pool_cache_t *cache, _odp_event_hdr_t *event_hdr[],
+ uint32_t num)
{
- uint32_t block_offset;
- odp_buffer_hdr_t *buf_hdr;
+ uint32_t cache_num = odp_atomic_load_u32(&cache->cache_num);
+ uint32_t i;
+
+ for (i = 0; i < num; i++)
+ cache->event_hdr[cache_num + i] = event_hdr[i];
+
+ odp_atomic_store_u32(&cache->cache_num, cache_num + num);
+}
+
+static void cache_flush(pool_cache_t *cache, pool_t *pool)
+{
+ _odp_event_hdr_t *event_hdr;
+ ring_ptr_t *ring;
+ uint32_t mask;
+
+ if (!pool->ring)
+ return;
+
+ ring = &pool->ring->hdr;
+ mask = pool->ring_mask;
+
+ while (cache_pop(cache, &event_hdr, 1))
+ ring_ptr_enq(ring, mask, event_hdr);
+}
+
+static inline int cache_available(pool_t *pool, odp_pool_stats_t *stats)
+{
+ uint64_t cached = 0;
+ const uint16_t first = stats->thread.first;
+ const uint16_t last = stats->thread.last;
+ const odp_bool_t cache_available = pool->params.stats.bit.cache_available;
+ const odp_bool_t per_thread = pool->params.stats.bit.thread_cache_available;
+ const int max_threads = odp_thread_count_max();
+ uint16_t out_idx = 0;
+ int i, idx_limit;
+
+ if (per_thread) {
+ if (first > last || last >= max_threads) {
+ _ODP_ERR("Bad thread ids: first=%" PRIu16 " last=%" PRIu16 "\n",
+ first, last);
+ return -1;
+ }
+
+ if (last - first + 1 > ODP_POOL_MAX_THREAD_STATS) {
+ _ODP_ERR("Too many thread ids: max=%d\n", ODP_POOL_MAX_THREAD_STATS);
+ return -1;
+ }
+ }
+
+ if (cache_available) {
+ i = 0;
+ idx_limit = max_threads;
+ } else {
+ i = first;
+ idx_limit = last + 1;
+ }
+
+ for (; i < idx_limit; i++) {
+ uint32_t cur = odp_atomic_load_u32(&pool->local_cache[i].cache_num);
+
+ if (per_thread && i >= first && i <= last)
+ stats->thread.cache_available[out_idx++] = cur;
+
+ cached += cur;
+ }
+
+ if (cache_available)
+ stats->cache_available = cached;
+
+ return 0;
+}
+
+static inline uint64_t cache_total_available(pool_t *pool)
+{
+ uint64_t cached = 0;
+ const int max_threads = odp_thread_count_max();
+
+ for (int i = 0; i < max_threads; i++)
+ cached += odp_atomic_load_u32(&pool->local_cache[i].cache_num);
+
+ return cached;
+}
+
+static int read_config_file(pool_global_t *pool_glb)
+{
+ uint32_t local_cache_size, burst_size, align;
+ const char *str;
+ int val = 0;
+
+ _ODP_PRINT("Pool config:\n");
+
+ str = "pool.local_cache_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > CONFIG_POOL_CACHE_MAX_SIZE || val < 0) {
+ _ODP_ERR("Bad value %s = %i, max %i\n", str, val, CONFIG_POOL_CACHE_MAX_SIZE);
+ return -1;
+ }
+
+ local_cache_size = val;
+ pool_glb->config.local_cache_size = local_cache_size;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "pool.burst_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val <= 0) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+
+ burst_size = val;
+ pool_glb->config.burst_size = burst_size;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ /* Check local cache size and burst size relation */
+ if (local_cache_size % burst_size) {
+ _ODP_ERR("Pool cache size not multiple of burst size\n");
+ return -1;
+ }
+
+ if (local_cache_size && (local_cache_size / burst_size < 2)) {
+ _ODP_ERR("Cache burst size too large compared to cache size\n");
+ return -1;
+ }
+
+ str = "pool.pkt.max_num";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > CONFIG_POOL_MAX_NUM || val < POOL_MAX_NUM_MIN) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+
+ pool_glb->config.pkt_max_num = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "pool.pkt.max_len";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
- block_offset = buffer_idx * pool->block_size;
+ if (val <= 0) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+
+ pool_glb->config.pkt_max_len = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
- /* clang requires cast to uintptr_t */
- buf_hdr = (odp_buffer_hdr_t *)(uintptr_t)&pool->base_addr[block_offset];
+ str = "pool.pkt.base_align";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
- return buf_hdr;
+ align = val;
+ if (val == 0)
+ align = ODP_CACHE_LINE_SIZE;
+
+ if (!_ODP_CHECK_IS_POWER2(align)) {
+ _ODP_ERR("Not a power of two: %s = %i\n", str, val);
+ return -1;
+ }
+
+ pool_glb->config.pkt_base_align = align;
+ _ODP_PRINT(" %s: %u\n", str, align);
+
+ str = "pool.buf.min_align";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ align = val;
+ if (val == 0)
+ align = ODP_CACHE_LINE_SIZE;
+
+ if (!_ODP_CHECK_IS_POWER2(align)) {
+ _ODP_ERR("Not a power of two: %s = %i\n", str, val);
+ return -1;
+ }
+
+ pool_glb->config.buf_min_align = align;
+ _ODP_PRINT(" %s: %u\n", str, align);
+
+ _ODP_PRINT("\n");
+
+ return 0;
}
-int odp_pool_init_global(void)
+int _odp_pool_init_global(void)
{
uint32_t i;
odp_shm_t shm;
- shm = odp_shm_reserve("_odp_pool_table",
- sizeof(pool_table_t),
- ODP_CACHE_LINE_SIZE, 0);
+ shm = odp_shm_reserve("_odp_pool_global",
+ sizeof(pool_global_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
- pool_tbl = odp_shm_addr(shm);
+ _odp_pool_glb = odp_shm_addr(shm);
- if (pool_tbl == NULL)
+ if (_odp_pool_glb == NULL)
return -1;
- memset(pool_tbl, 0, sizeof(pool_table_t));
- pool_tbl->shm = shm;
+ memset(_odp_pool_glb, 0, sizeof(pool_global_t));
+ _odp_pool_glb->shm = shm;
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool_t *pool = pool_entry(i);
+ if (read_config_file(_odp_pool_glb)) {
+ odp_shm_free(shm);
+ _odp_pool_glb = NULL;
+ return -1;
+ }
+
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool_t *pool = _odp_pool_entry_from_idx(i);
LOCK_INIT(&pool->lock);
- pool->pool_hdl = pool_index_to_handle(i);
pool->pool_idx = i;
}
- ODP_DBG("\nPool init global\n");
- ODP_DBG(" odp_buffer_hdr_t size %zu\n", sizeof(odp_buffer_hdr_t));
- ODP_DBG(" odp_packet_hdr_t size %zu\n", sizeof(odp_packet_hdr_t));
- ODP_DBG("\n");
+ _ODP_DBG("\nPool init global\n");
+ _ODP_DBG(" event_hdr_t size %zu\n", sizeof(_odp_event_hdr_t));
+ _ODP_DBG(" buffer_hdr_t size %zu\n", sizeof(odp_buffer_hdr_t));
+ _ODP_DBG(" packet_hdr_t size %zu\n", sizeof(odp_packet_hdr_t));
+ _ODP_DBG(" timeout_hdr_t size %zu\n", sizeof(odp_timeout_hdr_t));
+ _ODP_DBG(" event_vector_hdr_t size %zu\n", sizeof(odp_event_vector_hdr_t));
+ _ODP_DBG(" packet_hdr_t::seg_data offset %zu\n", offsetof(odp_packet_hdr_t, seg_data));
+ _ODP_DBG(" packet_hdr_t::timestamp offset %zu\n", offsetof(odp_packet_hdr_t, timestamp));
+ _ODP_DBG("\n");
return 0;
}
-int odp_pool_term_global(void)
+int _odp_pool_term_global(void)
{
int i;
pool_t *pool;
int ret = 0;
int rc = 0;
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool = pool_entry(i);
+ if (_odp_pool_glb == NULL)
+ return 0;
+
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool = _odp_pool_entry_from_idx(i);
LOCK(&pool->lock);
if (pool->reserved) {
- ODP_ERR("Not destroyed pool: %s\n", pool->name);
+ _ODP_ERR("Not destroyed pool: %s\n", pool->name);
rc = -1;
}
UNLOCK(&pool->lock);
}
- ret = odp_shm_free(pool_tbl->shm);
+ ret = odp_shm_free(_odp_pool_glb->shm);
if (ret < 0) {
- ODP_ERR("shm free failed");
+ _ODP_ERR("SHM free failed\n");
rc = -1;
}
return rc;
}
-int odp_pool_init_local(void)
+int _odp_pool_init_local(void)
{
pool_t *pool;
int i;
@@ -141,71 +402,68 @@ int odp_pool_init_local(void)
memset(&local, 0, sizeof(pool_local_t));
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool = pool_entry(i);
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool = _odp_pool_entry_from_idx(i);
local.cache[i] = &pool->local_cache[thr_id];
- local.cache[i]->num = 0;
+ cache_init(local.cache[i]);
}
local.thr_id = thr_id;
return 0;
}
-static void flush_cache(pool_cache_t *cache, pool_t *pool)
-{
- ring_t *ring;
- uint32_t mask;
- uint32_t cache_num, i;
-
- ring = &pool->ring->hdr;
- mask = pool->ring_mask;
- cache_num = cache->num;
-
- for (i = 0; i < cache_num; i++)
- ring_enq(ring, mask, cache->buf_index[i]);
-
- cache->num = 0;
-}
-
-int odp_pool_term_local(void)
+int _odp_pool_term_local(void)
{
int i;
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool_t *pool = pool_entry(i);
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool_t *pool = _odp_pool_entry_from_idx(i);
- flush_cache(local.cache[i], pool);
+ cache_flush(local.cache[i], pool);
}
return 0;
}
-static pool_t *reserve_pool(void)
+static pool_t *reserve_pool(uint32_t shmflags, uint8_t pool_ext, uint32_t num)
{
int i;
+ odp_shm_t shm;
+ uint32_t mem_size;
pool_t *pool;
char ring_name[ODP_POOL_NAME_LEN];
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool = pool_entry(i);
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool = _odp_pool_entry_from_idx(i);
LOCK(&pool->lock);
if (pool->reserved == 0) {
pool->reserved = 1;
UNLOCK(&pool->lock);
- sprintf(ring_name, "pool_ring_%d", i);
- pool->ring_shm =
- odp_shm_reserve(ring_name,
- sizeof(pool_ring_t),
- ODP_CACHE_LINE_SIZE, 0);
- if (odp_unlikely(pool->ring_shm == ODP_SHM_INVALID)) {
- ODP_ERR("Unable to alloc pool ring %d\n", i);
+
+ memset(&pool->memset_mark, 0,
+ sizeof(pool_t) - offsetof(pool_t, memset_mark));
+ sprintf(ring_name, "_odp_pool_ring_%d", i);
+
+ /* Reserve memory for the ring, and for lookup table in case of pool ext */
+ mem_size = sizeof(pool_ring_t);
+ if (pool_ext)
+ mem_size += num * sizeof(_odp_event_hdr_t *);
+
+ shm = odp_shm_reserve(ring_name, mem_size, ODP_CACHE_LINE_SIZE, shmflags);
+
+ if (odp_unlikely(shm == ODP_SHM_INVALID)) {
+ _ODP_ERR("Unable to alloc pool ring %d\n", i);
LOCK(&pool->lock);
pool->reserved = 0;
UNLOCK(&pool->lock);
break;
}
- pool->ring = odp_shm_addr(pool->ring_shm);
+
+ pool->ring_shm = shm;
+ pool->ring = odp_shm_addr(shm);
+ pool->pool_ext = pool_ext;
+
return pool;
}
UNLOCK(&pool->lock);
@@ -214,71 +472,162 @@ static pool_t *reserve_pool(void)
return NULL;
}
+static void init_event_hdr(pool_t *pool, _odp_event_hdr_t *event_hdr, uint32_t event_index,
+ uint8_t *data_ptr, void *uarea)
+{
+ uint32_t hdr_len;
+ odp_pool_type_t type = pool->type;
+
+ if (type == ODP_POOL_BUFFER)
+ hdr_len = sizeof(odp_buffer_hdr_t);
+ else if (type == ODP_POOL_PACKET)
+ hdr_len = sizeof(odp_packet_hdr_t);
+ else if (type == ODP_POOL_VECTOR)
+ hdr_len = sizeof(odp_event_vector_hdr_t);
+ else if (type == ODP_POOL_TIMEOUT)
+ hdr_len = sizeof(odp_timeout_hdr_t);
+ else
+ hdr_len = sizeof(_odp_event_hdr_t);
+
+ /* Zero all event and type specific header fields */
+ memset(event_hdr, 0, hdr_len);
+
+ /* Initialize common event metadata */
+ event_hdr->index.pool = pool->pool_idx;
+ event_hdr->index.event = event_index;
+ event_hdr->type = type;
+ event_hdr->event_type = type;
+ event_hdr->subtype = ODP_EVENT_NO_SUBTYPE;
+ event_hdr->pool = _odp_pool_handle(pool);
+
+ /* Store base values for fast init */
+ if (type == ODP_POOL_BUFFER || type == ODP_POOL_PACKET) {
+ event_hdr->base_data = data_ptr;
+ event_hdr->buf_end = data_ptr + pool->seg_len + pool->tailroom;
+ _odp_event_endmark_set(_odp_event_from_hdr(event_hdr));
+ }
+
+ if (type == ODP_POOL_BUFFER) {
+ odp_buffer_hdr_t *buf_hdr = (void *)event_hdr;
+
+ buf_hdr->uarea_addr = uarea;
+ }
+
+ /* Initialize segmentation metadata */
+ if (type == ODP_POOL_PACKET) {
+ odp_packet_hdr_t *pkt_hdr = (void *)event_hdr;
+
+ pkt_hdr->user_ptr = NULL;
+ pkt_hdr->uarea_addr = uarea;
+ pkt_hdr->seg_data = data_ptr;
+ pkt_hdr->seg_len = pool->seg_len;
+ pkt_hdr->seg_count = 1;
+ pkt_hdr->seg_next = NULL;
+
+ odp_atomic_init_u32(&pkt_hdr->ref_cnt, 0);
+ }
+
+ /* Initialize event vector metadata */
+ if (type == ODP_POOL_VECTOR) {
+ odp_event_vector_hdr_t *vect_hdr = (void *)event_hdr;
+
+ event_hdr->event_type = ODP_EVENT_PACKET_VECTOR;
+ vect_hdr->uarea_addr = uarea;
+ }
+
+ /* Initialize timeout metadata */
+ if (type == ODP_POOL_TIMEOUT) {
+ odp_timeout_hdr_t *tmo_hdr = (void *)event_hdr;
+
+ tmo_hdr->uarea_addr = uarea;
+ }
+}
+
static void init_buffers(pool_t *pool)
{
- uint32_t i;
+ _odp_event_hdr_t *event_hdr;
odp_buffer_hdr_t *buf_hdr;
odp_packet_hdr_t *pkt_hdr;
+ odp_shm_info_t shm_info;
void *addr;
void *uarea = NULL;
- uint8_t *data;
+ uint8_t *data = NULL;
+ uint8_t *data_ptr = NULL;
uint32_t offset;
- ring_t *ring;
+ ring_ptr_t *ring;
uint32_t mask;
- int type;
- uint32_t seg_size;
+ odp_pool_type_t type;
+ uint64_t page_size;
+ int skipped_blocks = 0;
+ if (odp_shm_info(pool->shm, &shm_info))
+ _ODP_ABORT("Shm info failed\n");
+
+ page_size = shm_info.page_size;
ring = &pool->ring->hdr;
mask = pool->ring_mask;
- type = pool->params.type;
+ type = pool->type;
+
+ for (uint64_t i = 0; i < pool->num + skipped_blocks ; i++) {
+ int skip = 0;
+ addr = &pool->base_addr[i * pool->block_size];
+
+ /* Skip packet buffers which cross huge page boundaries. Some
+ * NICs cannot handle buffers which cross page boundaries. */
+ if (type == ODP_POOL_PACKET && page_size >= FIRST_HP_SIZE) {
+ uint64_t first_page;
+ uint64_t last_page;
+
+ first_page = ((uint64_t)(uintptr_t)addr &
+ ~(page_size - 1));
+ last_page = (((uint64_t)(uintptr_t)addr +
+ pool->block_size - 1) &
+ ~(page_size - 1));
+ if (last_page != first_page) {
+ skipped_blocks++;
+ skip = 1;
+ }
+ }
- for (i = 0; i < pool->num; i++) {
- addr = &pool->base_addr[i * pool->block_size];
+ addr = (uint8_t *)addr + pool->block_offset;
+ event_hdr = addr;
buf_hdr = addr;
pkt_hdr = addr;
if (pool->uarea_size)
- uarea = &pool->uarea_base_addr[i * pool->uarea_size];
-
- data = buf_hdr->data;
-
- if (type == ODP_POOL_PACKET)
- data = pkt_hdr->data;
-
- offset = pool->headroom;
+ uarea = &pool->uarea_base_addr[(i - skipped_blocks) *
+ pool->uarea_size];
- /* move to correct align */
- while (((uintptr_t)&data[offset]) % pool->align != 0)
- offset++;
+ /* Only buffers and packets have data pointer */
+ if (type == ODP_POOL_BUFFER || type == ODP_POOL_PACKET) {
+ if (type == ODP_POOL_BUFFER)
+ data = buf_hdr->data;
+ else
+ data = pkt_hdr->data;
- memset(buf_hdr, 0, (uintptr_t)data - (uintptr_t)buf_hdr);
+ offset = pool->headroom;
- seg_size = pool->headroom + pool->data_size + pool->tailroom;
+ /* Move to correct align */
+ while (((uintptr_t)&data[offset]) % pool->align != 0)
+ offset++;
- /* Initialize buffer metadata */
- buf_hdr->index = i;
- buf_hdr->size = seg_size;
- buf_hdr->type = type;
- buf_hdr->event_type = type;
- buf_hdr->pool_hdl = pool->pool_hdl;
- buf_hdr->pool_ptr = pool;
- buf_hdr->uarea_addr = uarea;
- /* Show user requested size through API */
- buf_hdr->uarea_size = pool->params.pkt.uarea_size;
- buf_hdr->segcount = 1;
+ data_ptr = &data[offset];
+ }
- /* Pointer to data start (of the first segment) */
- buf_hdr->seg[0].hdr = buf_hdr;
- buf_hdr->seg[0].data = &data[offset];
- buf_hdr->seg[0].len = pool->data_size;
+ init_event_hdr(pool, event_hdr, i, data_ptr, uarea);
- /* Store base values for fast init */
- buf_hdr->base_data = buf_hdr->seg[0].data;
- buf_hdr->buf_end = &data[offset + pool->data_size +
- pool->tailroom];
+ /* Store buffer into the global pool */
+ if (!skip)
+ ring_ptr_enq(ring, mask, event_hdr);
+ }
+ pool->skipped_blocks = skipped_blocks;
- /* Store buffer index into the global pool */
- ring_enq(ring, mask, i);
+ if (pool->uarea_size && pool->params.uarea_init.init_fn) {
+ for (uint32_t i = 0; i < pool->num; i++) {
+ uarea = &pool->uarea_base_addr[i * pool->uarea_size];
+ pool->params.uarea_init.init_fn(uarea, pool->param_uarea_size,
+ pool->params.uarea_init.args, i);
+ }
}
}
@@ -291,161 +640,353 @@ static bool shm_is_from_huge_pages(odp_shm_t shm)
return 0;
if (odp_shm_info(shm, &info)) {
- ODP_ERR("Failed to fetch shm info\n");
+ _ODP_ERR("Failed to fetch shm info\n");
return 0;
}
return (info.page_size >= huge_page_size);
}
-static odp_pool_t pool_create(const char *name, odp_pool_param_t *params,
- uint32_t shmflags)
+static void set_pool_name(pool_t *pool, const char *name)
+{
+ if (name == NULL)
+ pool->name[0] = 0;
+ else
+ _odp_strcpy(pool->name, name, ODP_POOL_NAME_LEN);
+}
+
+static void set_pool_cache_size(pool_t *pool, uint32_t cache_size)
+{
+ uint32_t burst_size;
+
+ pool->cache_size = 0;
+ pool->burst_size = 1;
+
+ if (cache_size > 1) {
+ cache_size = (cache_size / 2) * 2;
+ burst_size = _odp_pool_glb->config.burst_size;
+
+ if ((cache_size / burst_size) < 2)
+ burst_size = cache_size / 2;
+
+ pool->cache_size = cache_size;
+ pool->burst_size = burst_size;
+ }
+}
+
+static int reserve_uarea(pool_t *pool, uint32_t uarea_size, uint32_t num_pkt, uint32_t shmflags)
+{
+ odp_shm_t shm;
+ const char *max_prefix = "pool_000_uarea_";
+ int max_prefix_len = strlen(max_prefix);
+ char uarea_name[ODP_POOL_NAME_LEN + max_prefix_len];
+
+ pool->uarea_shm = ODP_SHM_INVALID;
+
+ if (uarea_size == 0) {
+ pool->param_uarea_size = 0;
+ pool->uarea_size = 0;
+ pool->uarea_shm_size = 0;
+ return 0;
+ }
+
+ sprintf(uarea_name, "pool_%03i_uarea_%s", pool->pool_idx, pool->name);
+
+ pool->param_uarea_size = uarea_size;
+ pool->uarea_size = _ODP_ROUNDUP_CACHE_LINE(uarea_size);
+ pool->uarea_shm_size = num_pkt * (uint64_t)pool->uarea_size;
+
+ shm = odp_shm_reserve(uarea_name, pool->uarea_shm_size, ODP_PAGE_SIZE, shmflags);
+
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ pool->uarea_shm = shm;
+ pool->uarea_base_addr = odp_shm_addr(shm);
+ return 0;
+}
+
+static void set_mem_src_ops(pool_t *pool)
+{
+ odp_bool_t is_active_found = false;
+
+ pool->mem_src_ops = NULL;
+
+ for (int i = 0; _odp_pool_mem_src_ops[i]; i++) {
+ if (!is_active_found) {
+ if (_odp_pool_mem_src_ops[i]->is_active()) {
+ is_active_found = true;
+ pool->mem_src_ops = _odp_pool_mem_src_ops[i];
+ _ODP_DBG("Packet pool as a memory source for: %s\n",
+ pool->mem_src_ops->name);
+ }
+ } else if (_odp_pool_mem_src_ops[i]->is_active()) {
+ _odp_pool_mem_src_ops[i]->force_disable();
+ }
+ }
+}
+
+/* Create pool according to params. Actual type of the pool is type_2, which is recorded for pool
+ * info calls. */
+odp_pool_t _odp_pool_create(const char *name, const odp_pool_param_t *params,
+ odp_pool_type_t type_2)
{
pool_t *pool;
uint32_t uarea_size, headroom, tailroom;
odp_shm_t shm;
- uint32_t data_size, align, num, hdr_size, block_size;
- uint32_t max_len, max_seg_len;
+ uint32_t seg_len, align, num, hdr_size, block_size;
+ uint32_t max_len, cache_size, trailer_size;
uint32_t ring_size;
- int name_len;
- const char *postfix = "_uarea";
- char uarea_name[ODP_POOL_NAME_LEN + sizeof(postfix)];
-
- if (params == NULL) {
- ODP_ERR("No params");
- return ODP_POOL_INVALID;
- }
+ odp_pool_type_t type = params->type;
+ uint32_t shmflags = 0;
+ uint32_t num_extra = 0;
+ const char *max_prefix = "pool_000_";
+ int max_prefix_len = strlen(max_prefix);
+ char shm_name[ODP_POOL_NAME_LEN + max_prefix_len];
+
+ if (type == ODP_POOL_PACKET)
+ shmflags = ODP_SHM_PROC;
+ if (odp_global_ro.shm_single_va)
+ shmflags |= ODP_SHM_SINGLE_VA;
align = 0;
- if (params->type == ODP_POOL_BUFFER)
- align = params->buf.align;
+ if (type == ODP_POOL_PACKET) {
+ uint32_t align_req = params->pkt.align;
+
+ if (align_req &&
+ (!_ODP_CHECK_IS_POWER2(align_req) ||
+ align_req > _odp_pool_glb->config.pkt_base_align)) {
+ _ODP_ERR("Bad align requirement\n");
+ return ODP_POOL_INVALID;
+ }
+
+ align = _odp_pool_glb->config.pkt_base_align;
+ } else {
+ if (type == ODP_POOL_BUFFER)
+ align = params->buf.align;
- if (align < ODP_CONFIG_BUFFER_ALIGN_MIN)
- align = ODP_CONFIG_BUFFER_ALIGN_MIN;
+ if (align < _odp_pool_glb->config.buf_min_align)
+ align = _odp_pool_glb->config.buf_min_align;
+ }
/* Validate requested buffer alignment */
- if (align > ODP_CONFIG_BUFFER_ALIGN_MAX ||
- align != ROUNDDOWN_POWER2(align, align)) {
- ODP_ERR("Bad align requirement");
+ if (align > CONFIG_BUFFER_ALIGN_MAX ||
+ align != _ODP_ROUNDDOWN_POWER2(align, align)) {
+ _ODP_ERR("Bad align requirement\n");
return ODP_POOL_INVALID;
}
headroom = 0;
tailroom = 0;
- data_size = 0;
+ seg_len = 0;
max_len = 0;
- max_seg_len = 0;
+ trailer_size = 0;
uarea_size = 0;
+ cache_size = 0;
- switch (params->type) {
+ switch (type) {
case ODP_POOL_BUFFER:
num = params->buf.num;
- data_size = params->buf.size;
+ seg_len = params->buf.size;
+ uarea_size = params->buf.uarea_size;
+ cache_size = params->buf.cache_size;
+ trailer_size = _ODP_EV_ENDMARK_SIZE;
break;
case ODP_POOL_PACKET:
+ if (params->pkt.headroom > CONFIG_PACKET_HEADROOM) {
+ _ODP_ERR("Packet headroom size not supported\n");
+ return ODP_POOL_INVALID;
+ }
+
+ num = params->pkt.num;
+ seg_len = CONFIG_PACKET_MAX_SEG_LEN;
+ max_len = _odp_pool_glb->config.pkt_max_len;
+ trailer_size = _ODP_EV_ENDMARK_SIZE;
+
+ if (params->pkt.len &&
+ params->pkt.len < CONFIG_PACKET_MAX_SEG_LEN)
+ seg_len = params->pkt.len;
+ if (params->pkt.seg_len && params->pkt.seg_len > seg_len)
+ seg_len = params->pkt.seg_len;
+ if (seg_len < CONFIG_PACKET_SEG_LEN_MIN)
+ seg_len = CONFIG_PACKET_SEG_LEN_MIN;
+
+ /* Make sure that at least one 'max_len' packet can fit in the
+ * pool. */
+ if (params->pkt.max_len != 0)
+ max_len = params->pkt.max_len;
+ if ((max_len + seg_len - 1) / seg_len > PKT_MAX_SEGS)
+ seg_len = (max_len + PKT_MAX_SEGS - 1) / PKT_MAX_SEGS;
+ if (seg_len > CONFIG_PACKET_MAX_SEG_LEN) {
+ _ODP_ERR("Pool unable to store 'max_len' packet\n");
+ return ODP_POOL_INVALID;
+ }
+
+ /* Multiple segments required per 'params->pkt.len' packet */
+ if (params->pkt.len > seg_len)
+ num *= (params->pkt.len + seg_len - 1) / seg_len;
+
+ /* Make sure 'params->pkt.max_num' limitation holds */
+ if (params->pkt.max_num && num > params->pkt.max_num) {
+ _ODP_ERR("Pool 'max_num' parameter too small (%u/%u)\n",
+ params->pkt.max_num, num);
+ return ODP_POOL_INVALID;
+ }
+
headroom = CONFIG_PACKET_HEADROOM;
tailroom = CONFIG_PACKET_TAILROOM;
- num = params->pkt.num;
uarea_size = params->pkt.uarea_size;
- data_size = CONFIG_PACKET_MAX_SEG_LEN;
- max_seg_len = CONFIG_PACKET_MAX_SEG_LEN;
- max_len = CONFIG_PACKET_MAX_SEGS * max_seg_len;
+ cache_size = params->pkt.cache_size;
break;
case ODP_POOL_TIMEOUT:
num = params->tmo.num;
+ uarea_size = params->tmo.uarea_size;
+ cache_size = params->tmo.cache_size;
+ break;
+
+ case ODP_POOL_VECTOR:
+ num = params->vector.num;
+ uarea_size = params->vector.uarea_size;
+ cache_size = params->vector.cache_size;
+ seg_len = params->vector.max_size * sizeof(odp_packet_t);
break;
default:
- ODP_ERR("Bad pool type");
+ _ODP_ERR("Bad pool type\n");
return ODP_POOL_INVALID;
}
- if (uarea_size)
- uarea_size = ROUNDUP_CACHE_LINE(uarea_size);
-
- pool = reserve_pool();
+ pool = reserve_pool(shmflags, 0, num);
if (pool == NULL) {
- ODP_ERR("No more free pools");
+ _ODP_ERR("No more free pools\n");
return ODP_POOL_INVALID;
}
- if (name == NULL) {
- pool->name[0] = 0;
- } else {
- strncpy(pool->name, name,
- ODP_POOL_NAME_LEN - 1);
- pool->name[ODP_POOL_NAME_LEN - 1] = 0;
- }
+ set_pool_name(pool, name);
- name_len = strlen(pool->name);
- memcpy(uarea_name, pool->name, name_len);
- strcpy(&uarea_name[name_len], postfix);
+ /* Format SHM names from prefix, pool index and pool name. */
+ sprintf(shm_name, "pool_%03i_%s", pool->pool_idx, pool->name);
+ pool->type = type;
+ pool->type_2 = type_2;
pool->params = *params;
+ pool->block_offset = 0;
+ set_mem_src_ops(pool);
- hdr_size = sizeof(odp_packet_hdr_t);
- hdr_size = ROUNDUP_CACHE_LINE(hdr_size);
+ if (type == ODP_POOL_PACKET) {
+ uint32_t adj_size;
- block_size = ROUNDUP_CACHE_LINE(hdr_size + align + headroom +
- data_size + tailroom);
+ hdr_size = _ODP_ROUNDUP_CACHE_LINE(sizeof(odp_packet_hdr_t));
+ block_size = hdr_size + align + headroom + seg_len + tailroom + trailer_size;
+ adj_size = block_size;
+
+ if (pool->mem_src_ops && pool->mem_src_ops->adjust_size) {
+ pool->mem_src_ops->adjust_size(pool->mem_src_data, &adj_size,
+ &pool->block_offset, &shmflags);
+
+ if (!adj_size) {
+ _ODP_ERR("Calculating adjusted block size failed\n");
+ goto error;
+ }
+ }
+
+ if (adj_size != block_size)
+ block_size = adj_size;
+ else
+ block_size = _ODP_ROUNDUP_CACHE_LINE(block_size);
+ } else {
+ /* Header size is rounded up to cache line size, so the
+ * following data can be cache line aligned without extra
+ * padding. */
+ uint32_t align_pad = (align > ODP_CACHE_LINE_SIZE) ?
+ align - ODP_CACHE_LINE_SIZE : 0;
+
+ if (type == ODP_POOL_BUFFER)
+ hdr_size = _ODP_ROUNDUP_CACHE_LINE(sizeof(odp_buffer_hdr_t));
+ else if (type == ODP_POOL_TIMEOUT)
+ hdr_size = _ODP_ROUNDUP_CACHE_LINE(sizeof(odp_timeout_hdr_t));
+ else
+ hdr_size = _ODP_ROUNDUP_CACHE_LINE(sizeof(odp_event_vector_hdr_t));
+
+ block_size = _ODP_ROUNDUP_CACHE_LINE(hdr_size + align_pad + seg_len + trailer_size);
+ }
+
+ /* Allocate extra memory for skipping packet buffers which cross huge
+ * page boundaries. */
+ if (type == ODP_POOL_PACKET) {
+ num_extra = ((((uint64_t)num * block_size) +
+ FIRST_HP_SIZE - 1) / FIRST_HP_SIZE);
+ num_extra += ((((uint64_t)num_extra * block_size) +
+ FIRST_HP_SIZE - 1) / FIRST_HP_SIZE);
+ }
- if (num <= RING_SIZE_MIN)
+ /* Ring size must be larger than the number of items stored */
+ if (num + 1 <= RING_SIZE_MIN)
ring_size = RING_SIZE_MIN;
else
- ring_size = ROUNDUP_POWER2_U32(num);
+ ring_size = _ODP_ROUNDUP_POWER2_U32(num + 1);
pool->ring_mask = ring_size - 1;
pool->num = num;
pool->align = align;
pool->headroom = headroom;
- pool->data_size = data_size;
+ pool->seg_len = seg_len;
+ pool->trailer_size = trailer_size;
+ pool->max_seg_len = headroom + seg_len + tailroom;
pool->max_len = max_len;
- pool->max_seg_len = max_seg_len;
pool->tailroom = tailroom;
pool->block_size = block_size;
- pool->uarea_size = uarea_size;
- pool->shm_size = num * block_size;
- pool->uarea_shm_size = num * uarea_size;
- pool->ext_desc = NULL;
- pool->ext_destroy = NULL;
+ pool->shm_size = (num + num_extra) * (uint64_t)block_size;
- shm = odp_shm_reserve(pool->name, pool->shm_size,
- ODP_PAGE_SIZE, shmflags);
+ set_pool_cache_size(pool, cache_size);
+
+ shm = odp_shm_reserve(shm_name, pool->shm_size, ODP_PAGE_SIZE,
+ shmflags);
pool->shm = shm;
if (shm == ODP_SHM_INVALID) {
- ODP_ERR("Shm reserve failed");
+ _ODP_ERR("SHM reserve failed\n");
goto error;
}
pool->mem_from_huge_pages = shm_is_from_huge_pages(pool->shm);
pool->base_addr = odp_shm_addr(pool->shm);
+ pool->max_addr = pool->base_addr + pool->shm_size - 1;
- pool->uarea_shm = ODP_SHM_INVALID;
- if (uarea_size) {
- shm = odp_shm_reserve(uarea_name, pool->uarea_shm_size,
- ODP_PAGE_SIZE, shmflags);
+ if (reserve_uarea(pool, uarea_size, num, shmflags)) {
+ _ODP_ERR("User area SHM reserve failed\n");
+ goto error;
+ }
- pool->uarea_shm = shm;
+ ring_ptr_init(&pool->ring->hdr);
+ init_buffers(pool);
- if (shm == ODP_SHM_INVALID) {
- ODP_ERR("Shm reserve failed (uarea)");
- goto error;
- }
+ if (type == ODP_POOL_PACKET && pool->mem_src_ops && pool->mem_src_ops->bind &&
+ pool->mem_src_ops->bind(pool->mem_src_data, pool)) {
+ _ODP_ERR("Binding pool as memory source failed\n");
+ goto error;
+ }
- pool->uarea_base_addr = odp_shm_addr(pool->uarea_shm);
+ /* Total ops utilizes alloc_ops and free_ops counters */
+ if (pool->params.stats.bit.total_ops) {
+ pool->params.stats.bit.alloc_ops = 1;
+ pool->params.stats.bit.free_ops = 1;
}
- ring_init(&pool->ring->hdr);
- init_buffers(pool);
+ /* Reset pool stats */
+ odp_atomic_init_u64(&pool->stats.alloc_ops, 0);
+ odp_atomic_init_u64(&pool->stats.alloc_fails, 0);
+ odp_atomic_init_u64(&pool->stats.free_ops, 0);
+ odp_atomic_init_u64(&pool->stats.cache_alloc_ops, 0);
+ odp_atomic_init_u64(&pool->stats.cache_free_ops, 0);
- return pool->pool_hdl;
+ return _odp_pool_handle(pool);
error:
if (pool->shm != ODP_SHM_INVALID)
@@ -454,97 +995,198 @@ error:
if (pool->uarea_shm != ODP_SHM_INVALID)
odp_shm_free(pool->uarea_shm);
+ if (pool->ring_shm != ODP_SHM_INVALID)
+ odp_shm_free(pool->ring_shm);
+
+ pool->ring = NULL;
+
LOCK(&pool->lock);
pool->reserved = 0;
UNLOCK(&pool->lock);
return ODP_POOL_INVALID;
}
-static int check_params(odp_pool_param_t *params)
+static int check_params(const odp_pool_param_t *params)
{
odp_pool_capability_t capa;
+ uint32_t cache_size, num;
+ int num_threads = odp_global_ro.init_param.num_control +
+ odp_global_ro.init_param.num_worker;
+ int cur_threads = odp_thread_count();
- if (odp_pool_capability(&capa) < 0)
+ if (!params || odp_pool_capability(&capa) < 0)
return -1;
+ num = 0;
+ cache_size = 0;
+ if (num_threads < cur_threads)
+ num_threads = cur_threads;
+
switch (params->type) {
case ODP_POOL_BUFFER:
+ num = params->buf.num;
+ cache_size = params->buf.cache_size;
+
if (params->buf.num > capa.buf.max_num) {
- printf("buf.num too large %u\n", params->buf.num);
+ _ODP_ERR("buf.num too large %u\n", params->buf.num);
return -1;
}
if (params->buf.size > capa.buf.max_size) {
- printf("buf.size too large %u\n", params->buf.size);
+ _ODP_ERR("buf.size too large %u\n", params->buf.size);
return -1;
}
if (params->buf.align > capa.buf.max_align) {
- printf("buf.align too large %u\n", params->buf.align);
+ _ODP_ERR("buf.align too large %u\n", params->buf.align);
+ return -1;
+ }
+
+ if (params->buf.uarea_size > capa.buf.max_uarea_size) {
+ _ODP_ERR("buf.uarea_size too large %u\n", params->buf.uarea_size);
+ return -1;
+ }
+
+ if (params->stats.all & ~capa.buf.stats.all) {
+ _ODP_ERR("Unsupported pool statistics counter\n");
return -1;
}
break;
case ODP_POOL_PACKET:
+ num = params->pkt.num;
+ cache_size = params->pkt.cache_size;
+
+ if (params->pkt.num > capa.pkt.max_num) {
+ _ODP_ERR("pkt.num too large %u\n", params->pkt.num);
+ return -1;
+ }
+
+ if (params->pkt.max_num > capa.pkt.max_num) {
+ _ODP_ERR("pkt.max_num too large %u\n", params->pkt.max_num);
+ return -1;
+ }
+
if (params->pkt.len > capa.pkt.max_len) {
- printf("pkt.len too large %u\n", params->pkt.len);
+ _ODP_ERR("pkt.len too large %u\n", params->pkt.len);
return -1;
}
if (params->pkt.max_len > capa.pkt.max_len) {
- printf("pkt.max_len too large %u\n",
- params->pkt.max_len);
+ _ODP_ERR("pkt.max_len too large %u\n", params->pkt.max_len);
return -1;
}
if (params->pkt.seg_len > capa.pkt.max_seg_len) {
- printf("pkt.seg_len too large %u\n",
- params->pkt.seg_len);
+ _ODP_ERR("pkt.seg_len too large %u\n", params->pkt.seg_len);
return -1;
}
if (params->pkt.uarea_size > capa.pkt.max_uarea_size) {
- printf("pkt.uarea_size too large %u\n",
- params->pkt.uarea_size);
+ _ODP_ERR("pkt.uarea_size too large %u\n", params->pkt.uarea_size);
+ return -1;
+ }
+
+ if (params->pkt.headroom > capa.pkt.max_headroom) {
+ _ODP_ERR("pkt.headroom too large %u\n", params->pkt.headroom);
+ return -1;
+ }
+
+ if (params->stats.all & ~capa.pkt.stats.all) {
+ _ODP_ERR("Unsupported pool statistics counter\n");
return -1;
}
break;
case ODP_POOL_TIMEOUT:
+ num = params->tmo.num;
+ cache_size = params->tmo.cache_size;
+
if (params->tmo.num > capa.tmo.max_num) {
- printf("tmo.num too large %u\n", params->tmo.num);
+ _ODP_ERR("tmo.num too large %u\n", params->tmo.num);
return -1;
}
+
+ if (params->tmo.uarea_size > capa.tmo.max_uarea_size) {
+ _ODP_ERR("tmo.uarea_size too large %u\n", params->tmo.uarea_size);
+ return -1;
+ }
+
+ if (params->stats.all & ~capa.tmo.stats.all) {
+ _ODP_ERR("Unsupported pool statistics counter\n");
+ return -1;
+ }
+
+ break;
+
+ case ODP_POOL_VECTOR:
+ num = params->vector.num;
+ cache_size = params->vector.cache_size;
+
+ if (params->vector.num == 0) {
+ _ODP_ERR("vector.num zero\n");
+ return -1;
+ }
+
+ if (params->vector.num > capa.vector.max_num) {
+ _ODP_ERR("vector.num too large %u\n", params->vector.num);
+ return -1;
+ }
+
+ if (params->vector.max_size == 0) {
+ _ODP_ERR("vector.max_size zero\n");
+ return -1;
+ }
+
+ if (params->vector.max_size > capa.vector.max_size) {
+ _ODP_ERR("vector.max_size too large %u\n", params->vector.max_size);
+ return -1;
+ }
+
+ if (params->vector.uarea_size > capa.vector.max_uarea_size) {
+ _ODP_ERR("vector.uarea_size too large %u\n", params->vector.uarea_size);
+ return -1;
+ }
+
+ if (params->stats.all & ~capa.vector.stats.all) {
+ _ODP_ERR("Unsupported pool statistics counter\n");
+ return -1;
+ }
+
break;
default:
- printf("bad pool type %i\n", params->type);
+ _ODP_ERR("bad pool type %i\n", params->type);
return -1;
}
+ if (cache_size > CONFIG_POOL_CACHE_MAX_SIZE) {
+ _ODP_ERR("Too large cache size %u\n", cache_size);
+ return -1;
+ }
+
+ if (num <= (num_threads * cache_size))
+ _ODP_DBG("Entire pool fits into thread local caches. Pool "
+ "starvation may occur if the pool is used by multiple "
+ "threads.\n");
+
return 0;
}
-odp_pool_t odp_pool_create(const char *name, odp_pool_param_t *params)
+odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *params)
{
- uint32_t shm_flags = 0;
-
if (check_params(params))
return ODP_POOL_INVALID;
-#ifdef _ODP_PKTIO_IPC
- if (params && (params->type == ODP_POOL_PACKET))
- shm_flags = ODP_SHM_PROC;
-#endif
-
- return pool_create(name, params, shm_flags);
+ return _odp_pool_create(name, params, params->type);
}
int odp_pool_destroy(odp_pool_t pool_hdl)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
+ const int max_threads = odp_thread_count_max();
int i;
if (pool == NULL)
@@ -554,22 +1196,19 @@ int odp_pool_destroy(odp_pool_t pool_hdl)
if (pool->reserved == 0) {
UNLOCK(&pool->lock);
- ODP_ERR("Pool not created\n");
+ _ODP_ERR("Pool not created\n");
return -1;
}
- /* Destroy external DPDK mempool */
- if (pool->ext_destroy) {
- pool->ext_destroy(pool->ext_desc);
- pool->ext_destroy = NULL;
- pool->ext_desc = NULL;
- }
+ if (pool->type == ODP_POOL_PACKET && pool->mem_src_ops && pool->mem_src_ops->unbind)
+ pool->mem_src_ops->unbind(pool->mem_src_data);
/* Make sure local caches are empty */
- for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
- flush_cache(&pool->local_cache[i], pool);
+ for (i = 0; i < max_threads; i++)
+ cache_flush(&pool->local_cache[i], pool);
- odp_shm_free(pool->shm);
+ if (pool->pool_ext == 0)
+ odp_shm_free(pool->shm);
if (pool->uarea_shm != ODP_SHM_INVALID)
odp_shm_free(pool->uarea_shm);
@@ -582,29 +1221,19 @@ int odp_pool_destroy(odp_pool_t pool_hdl)
return 0;
}
-odp_event_type_t _odp_buffer_event_type(odp_buffer_t buf)
-{
- return buf_hdl_to_hdr(buf)->event_type;
-}
-
-void _odp_buffer_event_type_set(odp_buffer_t buf, int ev)
-{
- buf_hdl_to_hdr(buf)->event_type = ev;
-}
-
odp_pool_t odp_pool_lookup(const char *name)
{
uint32_t i;
pool_t *pool;
- for (i = 0; i < ODP_CONFIG_POOLS; i++) {
- pool = pool_entry(i);
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool = _odp_pool_entry_from_idx(i);
LOCK(&pool->lock);
if (strcmp(name, pool->name) == 0) {
/* found it */
UNLOCK(&pool->lock);
- return pool->pool_hdl;
+ return _odp_pool_handle(pool);
}
UNLOCK(&pool->lock);
}
@@ -614,59 +1243,82 @@ odp_pool_t odp_pool_lookup(const char *name)
int odp_pool_info(odp_pool_t pool_hdl, odp_pool_info_t *info)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
if (pool == NULL || info == NULL)
return -1;
+ memset(info, 0, sizeof(odp_pool_info_t));
+
+ info->type = pool->type_2;
info->name = pool->name;
- info->params = pool->params;
+
+ if (pool->pool_ext) {
+ info->pool_ext = 1;
+ info->pool_ext_param = pool->ext_param;
+
+ } else if (pool->type_2 == ODP_POOL_DMA_COMPL) {
+ info->dma_pool_param.num = pool->params.buf.num;
+ info->dma_pool_param.uarea_size = pool->params.buf.uarea_size;
+ info->dma_pool_param.cache_size = pool->params.buf.cache_size;
+
+ } else if (pool->type_2 == ODP_POOL_ML_COMPL) {
+ info->ml_pool_param.num = pool->params.buf.num;
+ info->ml_pool_param.uarea_size = pool->params.buf.uarea_size;
+ info->ml_pool_param.cache_size = pool->params.buf.cache_size;
+ } else {
+ info->params = pool->params;
+ }
+
+ if (pool->type == ODP_POOL_PACKET)
+ info->pkt.max_num = pool->num;
+
+ info->min_data_addr = (uintptr_t)pool->base_addr;
+ info->max_data_addr = (uintptr_t)pool->max_addr;
return 0;
}
-int buffer_alloc_multi(pool_t *pool, odp_buffer_hdr_t *buf_hdr[], int max_num)
+int _odp_event_alloc_multi(pool_t *pool, _odp_event_hdr_t *event_hdr[], int max_num)
{
- ring_t *ring;
- uint32_t mask, i;
- pool_cache_t *cache;
- uint32_t cache_num, num_ch, num_deq, burst;
- odp_buffer_hdr_t *hdr;
+ uint32_t pool_idx = pool->pool_idx;
+ pool_cache_t *cache = local.cache[pool_idx];
+ ring_ptr_t *ring;
+ _odp_event_hdr_t *hdr;
+ uint32_t mask, num_ch, num_alloc, i;
+ uint32_t num_deq = 0;
+ uint32_t burst_size = pool->burst_size;
- cache = local.cache[pool->pool_idx];
+ /* First pull packets from local cache */
+ num_ch = cache_pop(cache, event_hdr, max_num);
- cache_num = cache->num;
- num_ch = max_num;
- num_deq = 0;
- burst = CACHE_BURST;
+ if (CONFIG_POOL_STATISTICS && pool->params.stats.bit.cache_alloc_ops && num_ch)
+ odp_atomic_inc_u64(&pool->stats.cache_alloc_ops);
- if (odp_unlikely(cache_num < (uint32_t)max_num)) {
- /* Cache does not have enough buffers */
- num_ch = cache_num;
- num_deq = max_num - cache_num;
+ /* If needed, get more from the global pool */
+ if (odp_unlikely(num_ch != (uint32_t)max_num)) {
+ uint32_t burst = burst_size;
+ uint32_t cache_num;
- if (odp_unlikely(num_deq > CACHE_BURST))
+ num_deq = max_num - num_ch;
+ if (odp_unlikely(num_deq > burst_size))
burst = num_deq;
- }
-
- /* Get buffers from the cache */
- for (i = 0; i < num_ch; i++) {
- uint32_t j = cache_num - num_ch + i;
-
- buf_hdr[i] = buf_hdr_from_index(pool, cache->buf_index[j]);
- }
- /* If needed, get more from the global pool */
- if (odp_unlikely(num_deq)) {
- /* Temporary copy needed since odp_buffer_t is uintptr_t
- * and not uint32_t. */
- uint32_t data[burst];
+ _odp_event_hdr_t *hdr_tmp[burst];
ring = &pool->ring->hdr;
mask = pool->ring_mask;
- burst = ring_deq_multi(ring, mask, data, burst);
+ burst = ring_ptr_deq_multi(ring, mask, (void **)hdr_tmp,
+ burst);
cache_num = burst - num_deq;
+ if (CONFIG_POOL_STATISTICS) {
+ if (pool->params.stats.bit.alloc_ops)
+ odp_atomic_inc_u64(&pool->stats.alloc_ops);
+ if (odp_unlikely(pool->params.stats.bit.alloc_fails && burst == 0))
+ odp_atomic_inc_u64(&pool->stats.alloc_fails);
+ }
+
if (odp_unlikely(burst < num_deq)) {
num_deq = burst;
cache_num = 0;
@@ -675,88 +1327,74 @@ int buffer_alloc_multi(pool_t *pool, odp_buffer_hdr_t *buf_hdr[], int max_num)
for (i = 0; i < num_deq; i++) {
uint32_t idx = num_ch + i;
- hdr = buf_hdr_from_index(pool, data[i]);
+ hdr = hdr_tmp[i];
odp_prefetch(hdr);
- buf_hdr[idx] = hdr;
+ event_hdr[idx] = hdr;
}
- /* Cache extra buffers. Cache is currently empty. */
- for (i = 0; i < cache_num; i++)
- cache->buf_index[i] = data[num_deq + i];
-
- cache->num = cache_num;
- } else {
- cache->num = cache_num - num_ch;
+ /* Cache possible extra buffers. Cache is currently empty. */
+ if (cache_num)
+ cache_push(cache, &hdr_tmp[num_deq], cache_num);
}
- return num_ch + num_deq;
+ num_alloc = num_ch + num_deq;
+
+ return num_alloc;
}
-static inline void buffer_free_to_pool(pool_t *pool,
- odp_buffer_hdr_t *buf_hdr[], int num)
+static inline void event_free_to_pool(pool_t *pool,
+ _odp_event_hdr_t *event_hdr[], int num)
{
- int i;
- ring_t *ring;
- uint32_t mask;
- pool_cache_t *cache;
- uint32_t cache_num;
-
- cache = local.cache[pool->pool_idx];
+ uint32_t pool_idx = pool->pool_idx;
+ pool_cache_t *cache = local.cache[pool_idx];
+ ring_ptr_t *ring;
+ uint32_t cache_num, mask;
+ uint32_t cache_size = pool->cache_size;
/* Special case of a very large free. Move directly to
* the global pool. */
- if (odp_unlikely(num > CONFIG_POOL_CACHE_SIZE)) {
- uint32_t buf_index[num];
-
+ if (odp_unlikely(num > (int)cache_size)) {
ring = &pool->ring->hdr;
mask = pool->ring_mask;
- for (i = 0; i < num; i++)
- buf_index[i] = buf_hdr[i]->index;
- ring_enq_multi(ring, mask, buf_index, num);
+ ring_ptr_enq_multi(ring, mask, (void **)event_hdr, num);
+
+ if (CONFIG_POOL_STATISTICS && pool->params.stats.bit.free_ops)
+ odp_atomic_inc_u64(&pool->stats.free_ops);
return;
}
/* Make room into local cache if needed. Do at least burst size
* transfer. */
- cache_num = cache->num;
+ cache_num = odp_atomic_load_u32(&cache->cache_num);
- if (odp_unlikely((int)(CONFIG_POOL_CACHE_SIZE - cache_num) < num)) {
- uint32_t index;
- int burst = CACHE_BURST;
+ if (odp_unlikely((int)(cache_size - cache_num) < num)) {
+ int burst = pool->burst_size;
ring = &pool->ring->hdr;
mask = pool->ring_mask;
- if (odp_unlikely(num > CACHE_BURST))
+ if (odp_unlikely(num > burst))
burst = num;
if (odp_unlikely((uint32_t)num > cache_num))
burst = cache_num;
- {
- /* Temporary copy needed since odp_buffer_t is
- * uintptr_t and not uint32_t. */
- uint32_t data[burst];
-
- index = cache_num - burst;
+ _odp_event_hdr_t *ev_hdr[burst];
- for (i = 0; i < burst; i++)
- data[i] = cache->buf_index[index + i];
+ cache_pop(cache, ev_hdr, burst);
- ring_enq_multi(ring, mask, data, burst);
- }
-
- cache_num -= burst;
+ ring_ptr_enq_multi(ring, mask, (void **)ev_hdr, burst);
+ if (CONFIG_POOL_STATISTICS && pool->params.stats.bit.free_ops)
+ odp_atomic_inc_u64(&pool->stats.free_ops);
}
- for (i = 0; i < num; i++)
- cache->buf_index[cache_num + i] = buf_hdr[i]->index;
-
- cache->num = cache_num + num;
+ cache_push(cache, event_hdr, num);
+ if (CONFIG_POOL_STATISTICS && pool->params.stats.bit.cache_free_ops)
+ odp_atomic_inc_u64(&pool->stats.cache_free_ops);
}
-void buffer_free_multi(odp_buffer_hdr_t *buf_hdr[], int num_total)
+void _odp_event_free_multi(_odp_event_hdr_t *event_hdr[], int num_total)
{
pool_t *pool;
int num;
@@ -766,18 +1404,18 @@ void buffer_free_multi(odp_buffer_hdr_t *buf_hdr[], int num_total)
while (1) {
num = 1;
i = 1;
- pool = buf_hdr[first]->pool_ptr;
+ pool = _odp_pool_entry(event_hdr[first]->pool);
/* 'num' buffers are from the same pool */
if (num_total > 1) {
for (i = first; i < num_total; i++)
- if (pool != buf_hdr[i]->pool_ptr)
+ if (pool != _odp_pool_entry(event_hdr[i]->pool))
break;
num = i - first;
}
- buffer_free_to_pool(pool, &buf_hdr[first], num);
+ event_free_to_pool(pool, &event_hdr[first], num);
if (i == num_total)
return;
@@ -792,10 +1430,13 @@ odp_buffer_t odp_buffer_alloc(odp_pool_t pool_hdl)
pool_t *pool;
int ret;
- ODP_ASSERT(ODP_POOL_INVALID != pool_hdl);
+ _ODP_ASSERT(ODP_POOL_INVALID != pool_hdl);
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ _ODP_ASSERT(pool->type == ODP_POOL_BUFFER);
- pool = pool_entry_from_hdl(pool_hdl);
- ret = buffer_alloc_multi(pool, (odp_buffer_hdr_t **)&buf, 1);
+ ret = _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)&buf, 1);
if (odp_likely(ret == 1))
return buf;
@@ -803,105 +1444,248 @@ odp_buffer_t odp_buffer_alloc(odp_pool_t pool_hdl)
return ODP_BUFFER_INVALID;
}
+odp_event_t _odp_event_alloc(pool_t *pool)
+{
+ odp_event_t event;
+ int ret;
+
+ ret = _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)&event, 1);
+
+ if (odp_likely(ret == 1))
+ return event;
+
+ return ODP_EVENT_INVALID;
+}
+
int odp_buffer_alloc_multi(odp_pool_t pool_hdl, odp_buffer_t buf[], int num)
{
pool_t *pool;
- ODP_ASSERT(ODP_POOL_INVALID != pool_hdl);
+ _ODP_ASSERT(ODP_POOL_INVALID != pool_hdl);
+
+ pool = _odp_pool_entry(pool_hdl);
- pool = pool_entry_from_hdl(pool_hdl);
+ _ODP_ASSERT(pool->type == ODP_POOL_BUFFER);
- return buffer_alloc_multi(pool, (odp_buffer_hdr_t **)buf, num);
+ return _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)buf, num);
}
void odp_buffer_free(odp_buffer_t buf)
{
- buffer_free_multi((odp_buffer_hdr_t **)&buf, 1);
+ _odp_buffer_validate(buf, _ODP_EV_BUFFER_FREE);
+
+ _odp_event_free_multi((_odp_event_hdr_t **)&buf, 1);
}
void odp_buffer_free_multi(const odp_buffer_t buf[], int num)
{
- buffer_free_multi((odp_buffer_hdr_t **)(uintptr_t)buf, num);
+ _odp_buffer_validate_multi(buf, num, _ODP_EV_BUFFER_FREE_MULTI);
+
+ _odp_event_free_multi((_odp_event_hdr_t **)(uintptr_t)buf, num);
}
int odp_pool_capability(odp_pool_capability_t *capa)
{
+ odp_pool_stats_opt_t supported_stats;
uint32_t max_seg_len = CONFIG_PACKET_MAX_SEG_LEN;
+ /* Reserve pools for internal usage */
+ unsigned int max_pools = CONFIG_POOLS - CONFIG_INTERNAL_POOLS;
memset(capa, 0, sizeof(odp_pool_capability_t));
- capa->max_pools = ODP_CONFIG_POOLS;
+ capa->max_pools = max_pools;
+
+ supported_stats.all = 0;
+ supported_stats.bit.available = 1;
+ supported_stats.bit.alloc_ops = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.alloc_fails = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.free_ops = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.total_ops = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.cache_available = 1;
+ supported_stats.bit.cache_alloc_ops = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.cache_free_ops = CONFIG_POOL_STATISTICS;
+ supported_stats.bit.thread_cache_available = 1;
/* Buffer pools */
- capa->buf.max_pools = ODP_CONFIG_POOLS;
- capa->buf.max_align = ODP_CONFIG_BUFFER_ALIGN_MAX;
+ capa->buf.max_pools = max_pools;
+ capa->buf.max_align = CONFIG_BUFFER_ALIGN_MAX;
capa->buf.max_size = MAX_SIZE;
capa->buf.max_num = CONFIG_POOL_MAX_NUM;
+ capa->buf.max_uarea_size = MAX_UAREA_SIZE;
+ capa->buf.uarea_persistence = true;
+ capa->buf.min_cache_size = 0;
+ capa->buf.max_cache_size = CONFIG_POOL_CACHE_MAX_SIZE;
+ capa->buf.stats.all = supported_stats.all;
/* Packet pools */
- capa->pkt.max_pools = ODP_CONFIG_POOLS;
- capa->pkt.max_len = CONFIG_PACKET_MAX_SEGS * max_seg_len;
- capa->pkt.max_num = CONFIG_POOL_MAX_NUM;
+ capa->pkt.max_pools = max_pools;
+ capa->pkt.max_len = _odp_pool_glb->config.pkt_max_len;
+ capa->pkt.max_num = _odp_pool_glb->config.pkt_max_num;
+ capa->pkt.max_align = _odp_pool_glb->config.pkt_base_align;
capa->pkt.min_headroom = CONFIG_PACKET_HEADROOM;
+ capa->pkt.max_headroom = CONFIG_PACKET_HEADROOM;
capa->pkt.min_tailroom = CONFIG_PACKET_TAILROOM;
- capa->pkt.max_segs_per_pkt = CONFIG_PACKET_MAX_SEGS;
- capa->pkt.min_seg_len = max_seg_len;
+ capa->pkt.max_segs_per_pkt = PKT_MAX_SEGS;
+ capa->pkt.min_seg_len = CONFIG_PACKET_SEG_LEN_MIN;
capa->pkt.max_seg_len = max_seg_len;
- capa->pkt.max_uarea_size = MAX_SIZE;
+ capa->pkt.max_uarea_size = MAX_UAREA_SIZE;
+ capa->pkt.uarea_persistence = true;
+ capa->pkt.min_cache_size = 0;
+ capa->pkt.max_cache_size = CONFIG_POOL_CACHE_MAX_SIZE;
+ capa->pkt.stats.all = supported_stats.all;
/* Timeout pools */
- capa->tmo.max_pools = ODP_CONFIG_POOLS;
+ capa->tmo.max_pools = max_pools;
capa->tmo.max_num = CONFIG_POOL_MAX_NUM;
-
+ capa->tmo.max_uarea_size = MAX_UAREA_SIZE;
+ capa->tmo.uarea_persistence = true;
+ capa->tmo.min_cache_size = 0;
+ capa->tmo.max_cache_size = CONFIG_POOL_CACHE_MAX_SIZE;
+ capa->tmo.stats.all = supported_stats.all;
+
+ /* Vector pools */
+ capa->vector.max_pools = max_pools;
+ capa->vector.max_num = CONFIG_POOL_MAX_NUM;
+ capa->vector.max_size = CONFIG_PACKET_VECTOR_MAX_SIZE;
+ capa->vector.max_uarea_size = MAX_UAREA_SIZE;
+ capa->vector.uarea_persistence = true;
+ capa->vector.min_cache_size = 0;
+ capa->vector.max_cache_size = CONFIG_POOL_CACHE_MAX_SIZE;
+ capa->vector.stats.all = supported_stats.all;
return 0;
}
+static const char *get_long_type_str(odp_pool_type_t type)
+{
+ switch (type) {
+ case ODP_POOL_BUFFER:
+ return "buffer";
+ case ODP_POOL_PACKET:
+ return "packet";
+ case ODP_POOL_TIMEOUT:
+ return "timeout";
+ case ODP_POOL_VECTOR:
+ return "vector";
+ case ODP_POOL_DMA_COMPL:
+ return "dma completion";
+ case ODP_POOL_ML_COMPL:
+ return "ml completion";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *get_short_type_str(odp_pool_type_t type)
+{
+ switch (type) {
+ case ODP_POOL_BUFFER:
+ return "B";
+ case ODP_POOL_PACKET:
+ return "P";
+ case ODP_POOL_TIMEOUT:
+ return "T";
+ case ODP_POOL_VECTOR:
+ return "V";
+ case ODP_POOL_DMA_COMPL:
+ return "D";
+ case ODP_POOL_ML_COMPL:
+ return "M";
+ default:
+ return "-";
+ }
+}
+
void odp_pool_print(odp_pool_t pool_hdl)
{
pool_t *pool;
- pool = pool_entry_from_hdl(pool_hdl);
-
- printf("\nPool info\n");
- printf("---------\n");
- printf(" pool %" PRIu64 "\n",
- odp_pool_to_u64(pool->pool_hdl));
- printf(" name %s\n", pool->name);
- printf(" pool type %s\n",
- pool->params.type == ODP_POOL_BUFFER ? "buffer" :
- (pool->params.type == ODP_POOL_PACKET ? "packet" :
- (pool->params.type == ODP_POOL_TIMEOUT ? "timeout" :
- "unknown")));
- printf(" pool shm %" PRIu64 "\n",
- odp_shm_to_u64(pool->shm));
- printf(" user area shm %" PRIu64 "\n",
- odp_shm_to_u64(pool->uarea_shm));
- printf(" num %u\n", pool->num);
- printf(" align %u\n", pool->align);
- printf(" headroom %u\n", pool->headroom);
- printf(" data size %u\n", pool->data_size);
- printf(" max data len %u\n", pool->max_len);
- printf(" max seg len %u\n", pool->max_seg_len);
- printf(" tailroom %u\n", pool->tailroom);
- printf(" block size %u\n", pool->block_size);
- printf(" uarea size %u\n", pool->uarea_size);
- printf(" shm size %u\n", pool->shm_size);
- printf(" base addr %p\n", pool->base_addr);
- printf(" uarea shm size %u\n", pool->uarea_shm_size);
- printf(" uarea base addr %p\n", pool->uarea_base_addr);
- printf("\n");
-}
-
-odp_pool_t odp_buffer_pool(odp_buffer_t buf)
-{
- pool_t *pool = pool_from_buf(buf);
-
- return pool->pool_hdl;
+ pool = _odp_pool_entry(pool_hdl);
+
+ _ODP_PRINT("\nPool info\n");
+ _ODP_PRINT("---------\n");
+ _ODP_PRINT(" pool %" PRIu64 "\n",
+ odp_pool_to_u64(_odp_pool_handle(pool)));
+ _ODP_PRINT(" name %s\n", pool->name);
+ _ODP_PRINT(" pool type %s\n", get_long_type_str(pool->type_2));
+ _ODP_PRINT(" pool shm %" PRIu64 "\n", odp_shm_to_u64(pool->shm));
+ _ODP_PRINT(" user area shm %" PRIu64 "\n", odp_shm_to_u64(pool->uarea_shm));
+ _ODP_PRINT(" num %u\n", pool->num);
+ _ODP_PRINT(" align %u\n", pool->align);
+ _ODP_PRINT(" headroom %u\n", pool->headroom);
+ _ODP_PRINT(" seg len %u\n", pool->seg_len);
+ _ODP_PRINT(" max data len %u\n", pool->max_len);
+ _ODP_PRINT(" tailroom %u\n", pool->tailroom);
+ _ODP_PRINT(" block size %u\n", pool->block_size);
+ _ODP_PRINT(" uarea size %u\n", pool->uarea_size);
+ _ODP_PRINT(" shm size %" PRIu64 "\n", pool->shm_size);
+ _ODP_PRINT(" base addr %p\n", (void *)pool->base_addr);
+ _ODP_PRINT(" max addr %p\n", (void *)pool->max_addr);
+ _ODP_PRINT(" uarea shm size %" PRIu64 "\n", pool->uarea_shm_size);
+ _ODP_PRINT(" uarea base addr %p\n", (void *)pool->uarea_base_addr);
+ _ODP_PRINT(" cache size %u\n", pool->cache_size);
+ _ODP_PRINT(" burst size %u\n", pool->burst_size);
+ _ODP_PRINT(" mem src %s\n",
+ pool->mem_src_ops ? pool->mem_src_ops->name : "(none)");
+ _ODP_PRINT(" event valid. %d\n", _ODP_EVENT_VALIDATION);
+ _ODP_PRINT("\n");
+}
+
+void odp_pool_print_all(void)
+{
+ uint64_t available;
+ uint32_t i, index, tot, cache_size, seg_len;
+ uint32_t buf_len = 0;
+ uint8_t type, ext;
+ const int col_width = 24;
+ const char *name, *type_c;
+
+ _ODP_PRINT("\nList of all pools\n");
+ _ODP_PRINT("-----------------\n");
+ _ODP_PRINT(" idx %-*s type free tot cache buf_len ext\n", col_width, "name");
+
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool_t *pool = _odp_pool_entry_from_idx(i);
+
+ LOCK(&pool->lock);
+
+ if (!pool->reserved) {
+ UNLOCK(&pool->lock);
+ continue;
+ }
+
+ available = ring_ptr_len(&pool->ring->hdr);
+ cache_size = pool->cache_size;
+ ext = pool->pool_ext;
+ index = pool->pool_idx;
+ name = pool->name;
+ tot = pool->num;
+ type = pool->type;
+ seg_len = pool->seg_len;
+
+ UNLOCK(&pool->lock);
+
+ if (type == ODP_POOL_BUFFER || type == ODP_POOL_PACKET)
+ buf_len = seg_len;
+
+ type_c = get_short_type_str(pool->type_2);
+
+ _ODP_PRINT("%4u %-*s %s %6" PRIu64 " %6" PRIu32 " %6" PRIu32 " %8" PRIu32 " "
+ "%" PRIu8 "\n", index, col_width, name, type_c, available, tot,
+ cache_size, buf_len, ext);
+ }
+ _ODP_PRINT("\n");
}
void odp_pool_param_init(odp_pool_param_t *params)
{
+ uint32_t default_cache_size = _odp_pool_glb->config.local_cache_size;
+
memset(params, 0, sizeof(odp_pool_param_t));
+ params->pkt.headroom = CONFIG_PACKET_HEADROOM;
+ params->buf.cache_size = default_cache_size;
+ params->pkt.cache_size = default_cache_size;
+ params->tmo.cache_size = default_cache_size;
+ params->vector.cache_size = default_cache_size;
}
uint64_t odp_pool_to_u64(odp_pool_t hdl)
@@ -909,20 +1693,473 @@ uint64_t odp_pool_to_u64(odp_pool_t hdl)
return _odp_pri(hdl);
}
-int odp_buffer_is_valid(odp_buffer_t buf)
+unsigned int odp_pool_max_index(void)
+{
+ return CONFIG_POOLS - 1;
+}
+
+int odp_pool_stats(odp_pool_t pool_hdl, odp_pool_stats_t *stats)
{
pool_t *pool;
- if (buf == ODP_BUFFER_INVALID)
+ if (odp_unlikely(pool_hdl == ODP_POOL_INVALID)) {
+ _ODP_ERR("Invalid pool handle\n");
+ return -1;
+ }
+ if (odp_unlikely(stats == NULL)) {
+ _ODP_ERR("Output buffer NULL\n");
+ return -1;
+ }
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ /* Zero everything else but per thread statistics */
+ memset(stats, 0, offsetof(odp_pool_stats_t, thread));
+
+ if (pool->params.stats.bit.available)
+ stats->available = ring_ptr_len(&pool->ring->hdr);
+
+ if (pool->params.stats.bit.alloc_ops)
+ stats->alloc_ops = odp_atomic_load_u64(&pool->stats.alloc_ops);
+
+ if (pool->params.stats.bit.alloc_fails)
+ stats->alloc_fails = odp_atomic_load_u64(&pool->stats.alloc_fails);
+
+ if (pool->params.stats.bit.free_ops)
+ stats->free_ops = odp_atomic_load_u64(&pool->stats.free_ops);
+
+ if (pool->params.stats.bit.total_ops)
+ stats->total_ops = stats->alloc_ops + stats->free_ops;
+
+ if (pool->params.stats.bit.cache_available ||
+ pool->params.stats.bit.thread_cache_available) {
+ if (cache_available(pool, stats))
+ return -1;
+ }
+
+ if (pool->params.stats.bit.cache_alloc_ops)
+ stats->cache_alloc_ops = odp_atomic_load_u64(&pool->stats.cache_alloc_ops);
+
+ if (pool->params.stats.bit.cache_free_ops)
+ stats->cache_free_ops = odp_atomic_load_u64(&pool->stats.cache_free_ops);
+
+ return 0;
+}
+
+int odp_pool_stats_selected(odp_pool_t pool_hdl, odp_pool_stats_selected_t *stats,
+ const odp_pool_stats_opt_t *opt)
+{
+ pool_t *pool;
+
+ if (odp_unlikely(pool_hdl == ODP_POOL_INVALID)) {
+ _ODP_ERR("Invalid pool handle\n");
+ return -1;
+ }
+ if (odp_unlikely(stats == NULL)) {
+ _ODP_ERR("Output buffer NULL\n");
+ return -1;
+ }
+ if (odp_unlikely(opt == NULL)) {
+ _ODP_ERR("Pool counters NULL\n");
+ return -1;
+ }
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ if (odp_unlikely(opt->all & ~pool->params.stats.all)) {
+ _ODP_ERR("Trying to read disabled counter\n");
+ return -1;
+ }
+
+ if (opt->bit.available)
+ stats->available = ring_ptr_len(&pool->ring->hdr);
+
+ if (opt->bit.alloc_ops || opt->bit.total_ops)
+ stats->alloc_ops = odp_atomic_load_u64(&pool->stats.alloc_ops);
+
+ if (opt->bit.alloc_fails)
+ stats->alloc_fails = odp_atomic_load_u64(&pool->stats.alloc_fails);
+
+ if (opt->bit.free_ops || opt->bit.total_ops)
+ stats->free_ops = odp_atomic_load_u64(&pool->stats.free_ops);
+
+ if (opt->bit.total_ops)
+ stats->total_ops = stats->alloc_ops + stats->free_ops;
+
+ if (opt->bit.cache_available)
+ stats->cache_available = cache_total_available(pool);
+
+ if (opt->bit.cache_alloc_ops)
+ stats->cache_alloc_ops = odp_atomic_load_u64(&pool->stats.cache_alloc_ops);
+
+ if (opt->bit.cache_free_ops)
+ stats->cache_free_ops = odp_atomic_load_u64(&pool->stats.cache_free_ops);
+
+ return 0;
+}
+
+int odp_pool_stats_reset(odp_pool_t pool_hdl)
+{
+ pool_t *pool;
+
+ if (odp_unlikely(pool_hdl == ODP_POOL_INVALID)) {
+ _ODP_ERR("Invalid pool handle\n");
+ return -1;
+ }
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ odp_atomic_store_u64(&pool->stats.alloc_ops, 0);
+ odp_atomic_store_u64(&pool->stats.alloc_fails, 0);
+ odp_atomic_store_u64(&pool->stats.free_ops, 0);
+ odp_atomic_store_u64(&pool->stats.cache_alloc_ops, 0);
+ odp_atomic_store_u64(&pool->stats.cache_free_ops, 0);
+
+ return 0;
+}
+
+static pool_t *find_pool(_odp_event_hdr_t *event_hdr)
+{
+ int i;
+ uint8_t *ptr = (uint8_t *)event_hdr;
+
+ for (i = 0; i < CONFIG_POOLS; i++) {
+ pool_t *pool = _odp_pool_entry_from_idx(i);
+
+ if (pool->reserved == 0)
+ continue;
+
+ if (ptr >= pool->base_addr && ptr < pool->max_addr)
+ return pool;
+ }
+
+ return NULL;
+}
+
+int _odp_event_is_valid(odp_event_t event)
+{
+ pool_t *pool;
+ _odp_event_hdr_t *event_hdr = _odp_event_hdr(event);
+
+ if (event == ODP_EVENT_INVALID)
+ return 0;
+
+ /* Check that buffer header is from a known pool */
+ pool = find_pool(event_hdr);
+ if (pool == NULL)
+ return 0;
+
+ if (pool != _odp_pool_entry(event_hdr->pool))
+ return 0;
+
+ if (event_hdr->index.event >= (pool->num + pool->skipped_blocks))
return 0;
- pool = pool_from_buf(buf);
+ return 1;
+}
+
+int odp_buffer_is_valid(odp_buffer_t buf)
+{
+ if (_odp_event_is_valid(odp_buffer_to_event(buf)) == 0)
+ return 0;
- if (pool->pool_idx >= ODP_CONFIG_POOLS)
+ if (odp_event_type(odp_buffer_to_event(buf)) != ODP_EVENT_BUFFER)
return 0;
- if (pool->reserved == 0)
+ if (odp_unlikely(_odp_buffer_validate(buf, _ODP_EV_BUFFER_IS_VALID)))
return 0;
return 1;
}
+
+/* No actual head pointer alignment requirement. Anyway, require even byte address. */
+#define MIN_HEAD_ALIGN 2
+
+int odp_pool_ext_capability(odp_pool_type_t type, odp_pool_ext_capability_t *capa)
+{
+ odp_pool_stats_opt_t supported_stats;
+
+ _ODP_ASSERT(capa != NULL);
+
+ switch (type) {
+ case ODP_POOL_PACKET:
+ break;
+ case ODP_POOL_BUFFER:
+ case ODP_POOL_TIMEOUT:
+ case ODP_POOL_VECTOR:
+ case ODP_POOL_DMA_COMPL:
+ case ODP_POOL_ML_COMPL:
+ memset(capa, 0, sizeof(odp_pool_ext_capability_t));
+ return 0;
+ default:
+ _ODP_ERR("Invalid pool type: %d\n", type);
+ return -1;
+ }
+
+ supported_stats.all = 0;
+
+ memset(capa, 0, sizeof(odp_pool_ext_capability_t));
+
+ capa->type = type;
+ capa->max_pools = CONFIG_POOLS - CONFIG_INTERNAL_POOLS;
+ capa->min_cache_size = 0;
+ capa->max_cache_size = CONFIG_POOL_CACHE_MAX_SIZE;
+ capa->stats.all = supported_stats.all;
+
+ capa->pkt.max_num_buf = _odp_pool_glb->config.pkt_max_num;
+ capa->pkt.max_buf_size = MAX_SIZE;
+ capa->pkt.odp_header_size = sizeof(odp_packet_hdr_t);
+ capa->pkt.odp_trailer_size = _ODP_EV_ENDMARK_SIZE;
+ capa->pkt.min_mem_align = ODP_CACHE_LINE_SIZE;
+ capa->pkt.min_buf_align = ODP_CACHE_LINE_SIZE;
+ capa->pkt.min_head_align = MIN_HEAD_ALIGN;
+ capa->pkt.buf_size_aligned = 0;
+ capa->pkt.max_headroom = CONFIG_PACKET_HEADROOM;
+ capa->pkt.max_headroom_size = CONFIG_PACKET_HEADROOM;
+ capa->pkt.max_segs_per_pkt = PKT_MAX_SEGS;
+ capa->pkt.max_uarea_size = MAX_UAREA_SIZE;
+ capa->pkt.uarea_persistence = true;
+
+ return 0;
+}
+
+void odp_pool_ext_param_init(odp_pool_type_t type, odp_pool_ext_param_t *param)
+{
+ uint32_t default_cache_size = _odp_pool_glb->config.local_cache_size;
+
+ memset(param, 0, sizeof(odp_pool_ext_param_t));
+
+ if (type != ODP_POOL_PACKET)
+ return;
+
+ param->type = ODP_POOL_PACKET;
+ param->cache_size = default_cache_size;
+ param->pkt.headroom = CONFIG_PACKET_HEADROOM;
+}
+
+static int check_pool_ext_param(const odp_pool_ext_param_t *param)
+{
+ odp_pool_ext_capability_t capa;
+ uint32_t head_offset = sizeof(odp_packet_hdr_t) + param->pkt.app_header_size;
+
+ if (param->type != ODP_POOL_PACKET) {
+ _ODP_ERR("Pool type not supported\n");
+ return -1;
+ }
+
+ if (odp_pool_ext_capability(param->type, &capa)) {
+ _ODP_ERR("Capa failed\n");
+ return -1;
+ }
+
+ if (param->cache_size > capa.max_cache_size) {
+ _ODP_ERR("Too large cache size %u\n", param->cache_size);
+ return -1;
+ }
+
+ if (param->stats.all != capa.stats.all) {
+ _ODP_ERR("Pool statistics not supported\n");
+ return -1;
+ }
+
+ if (param->pkt.num_buf > capa.pkt.max_num_buf) {
+ _ODP_ERR("Too many packet buffers\n");
+ return -1;
+ }
+
+ if (param->pkt.buf_size > capa.pkt.max_buf_size) {
+ _ODP_ERR("Too large packet buffer size %u\n", param->pkt.buf_size);
+ return -1;
+ }
+
+ if (param->pkt.uarea_size > capa.pkt.max_uarea_size) {
+ _ODP_ERR("Too large user area size %u\n", param->pkt.uarea_size);
+ return -1;
+ }
+
+ if (param->pkt.headroom > capa.pkt.max_headroom) {
+ _ODP_ERR("Too large headroom size\n");
+ return -1;
+ }
+
+ if (head_offset % capa.pkt.min_head_align) {
+ _ODP_ERR("Head pointer not %u byte aligned\n", capa.pkt.min_head_align);
+ return -1;
+ }
+
+ return 0;
+}
+
+odp_pool_t odp_pool_ext_create(const char *name, const odp_pool_ext_param_t *param)
+{
+ pool_t *pool;
+ uint32_t ring_size;
+ uint32_t num_buf = param->pkt.num_buf;
+ uint32_t buf_size = param->pkt.buf_size;
+ uint32_t head_offset = sizeof(odp_packet_hdr_t) + param->pkt.app_header_size;
+ uint32_t headroom = param->pkt.headroom;
+ uint32_t shm_flags = 0;
+
+ if (check_pool_ext_param(param)) {
+ _ODP_ERR("Bad pool ext param\n");
+ return ODP_POOL_INVALID;
+ }
+
+ if (odp_global_ro.shm_single_va)
+ shm_flags |= ODP_SHM_SINGLE_VA;
+
+ pool = reserve_pool(shm_flags, 1, num_buf);
+
+ if (pool == NULL) {
+ _ODP_ERR("No more free pools\n");
+ return ODP_POOL_INVALID;
+ }
+
+ pool->ext_param = *param;
+ set_pool_name(pool, name);
+ set_pool_cache_size(pool, param->cache_size);
+
+ if (reserve_uarea(pool, param->pkt.uarea_size, num_buf, shm_flags)) {
+ _ODP_ERR("User area SHM reserve failed\n");
+ goto error;
+ }
+
+ /* Ring size must be larger than the number of items stored */
+ if (num_buf + 1 <= RING_SIZE_MIN)
+ ring_size = RING_SIZE_MIN;
+ else
+ ring_size = _ODP_ROUNDUP_POWER2_U32(num_buf + 1);
+
+ pool->ring_mask = ring_size - 1;
+ pool->type = param->type;
+ pool->num = num_buf;
+ pool->headroom = headroom;
+ pool->tailroom = 0;
+ pool->trailer_size = _ODP_EV_ENDMARK_SIZE;
+ pool->seg_len = buf_size - head_offset - headroom - pool->tailroom -
+ pool->trailer_size;
+ pool->max_seg_len = headroom + pool->seg_len + pool->tailroom;
+ pool->max_len = PKT_MAX_SEGS * pool->seg_len;
+ pool->ext_head_offset = head_offset;
+ pool->base_addr = (uint8_t *)(uintptr_t)UINT64_MAX;
+ pool->max_addr = 0;
+
+ ring_ptr_init(&pool->ring->hdr);
+
+ return _odp_pool_handle(pool);
+
+error:
+ if (pool->ring_shm != ODP_SHM_INVALID)
+ odp_shm_free(pool->ring_shm);
+
+ pool->ring = NULL;
+
+ LOCK(&pool->lock);
+ pool->reserved = 0;
+ UNLOCK(&pool->lock);
+
+ return ODP_POOL_INVALID;
+}
+
+int odp_pool_ext_populate(odp_pool_t pool_hdl, void *buf[], uint32_t buf_size, uint32_t num,
+ uint32_t flags)
+{
+ pool_t *pool;
+ _odp_event_hdr_t *event_hdr;
+ ring_ptr_t *ring;
+ uint32_t i, ring_mask, buf_index, head_offset;
+ uint32_t num_populated;
+ uint8_t *data_ptr, *min_addr, *max_addr;
+ void *uarea = NULL;
+
+ if (pool_hdl == ODP_POOL_INVALID) {
+ _ODP_ERR("Bad pool handle\n");
+ return -1;
+ }
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ if (pool->type != ODP_POOL_PACKET || pool->pool_ext == 0) {
+ _ODP_ERR("Bad pool type\n");
+ return -1;
+ }
+
+ min_addr = pool->base_addr;
+ max_addr = pool->max_addr;
+
+ if (buf_size != pool->ext_param.pkt.buf_size) {
+ _ODP_ERR("Bad buffer size\n");
+ return -1;
+ }
+
+ num_populated = pool->num_populated;
+
+ if (num_populated + num > pool->num) {
+ _ODP_ERR("Trying to over populate the pool\n");
+ return -1;
+ }
+
+ if ((num_populated + num == pool->num) && !(flags & ODP_POOL_POPULATE_DONE)) {
+ _ODP_ERR("Missing ODP_POOL_POPULATE_DONE flag\n");
+ return -1;
+ }
+
+ if ((num_populated + num < pool->num) && flags) {
+ _ODP_ERR("Unexpected flags: 0x%x\n", flags);
+ return -1;
+ }
+
+ ring = &pool->ring->hdr;
+ ring_mask = pool->ring_mask;
+ buf_index = pool->num_populated;
+ head_offset = pool->ext_head_offset;
+
+ for (i = 0; i < num; i++) {
+ event_hdr = buf[i];
+
+ if ((uint8_t *)event_hdr < min_addr)
+ min_addr = (uint8_t *)event_hdr;
+
+ if ((uint8_t *)event_hdr > max_addr)
+ max_addr = (uint8_t *)event_hdr;
+
+ if ((uintptr_t)event_hdr & (ODP_CACHE_LINE_SIZE - 1)) {
+ _ODP_ERR("Bad packet buffer align: buf[%u]\n", i);
+ return -1;
+ }
+
+ if (((uintptr_t)event_hdr + head_offset) & (MIN_HEAD_ALIGN - 1)) {
+ _ODP_ERR("Bad head pointer align: buf[%u]\n", i);
+ return -1;
+ }
+
+ if (pool->uarea_size)
+ uarea = &pool->uarea_base_addr[buf_index * pool->uarea_size];
+
+ data_ptr = (uint8_t *)event_hdr + head_offset + pool->headroom;
+ init_event_hdr(pool, event_hdr, buf_index, data_ptr, uarea);
+ pool->ring->event_hdr_by_index[buf_index] = event_hdr;
+ buf_index++;
+
+ ring_ptr_enq(ring, ring_mask, event_hdr);
+ }
+
+ pool->num_populated += num;
+ pool->base_addr = min_addr;
+ pool->max_addr = max_addr;
+
+ if (flags & ODP_POOL_POPULATE_DONE) {
+ pool->max_addr = max_addr + buf_size - 1;
+
+ if (pool->uarea_size && pool->ext_param.uarea_init.init_fn) {
+ for (i = 0; i < pool->num_populated; i++) {
+ uarea = &pool->uarea_base_addr[i * pool->uarea_size];
+ pool->ext_param.uarea_init.init_fn(uarea, pool->param_uarea_size,
+ pool->ext_param.uarea_init.args,
+ i);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_pool_api.c b/platform/linux-generic/odp_pool_api.c
new file mode 100644
index 000000000..f81e864aa
--- /dev/null
+++ b/platform/linux-generic/odp_pool_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/pool.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/pool_inlines.h>
diff --git a/platform/linux-generic/odp_pool_mem_src_ops.c b/platform/linux-generic/odp_pool_mem_src_ops.c
new file mode 100644
index 000000000..c07abc78e
--- /dev/null
+++ b/platform/linux-generic/odp_pool_mem_src_ops.c
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/autoheader_internal.h>
+#include <odp_pool_internal.h>
+
+extern const _odp_pool_mem_src_ops_t _odp_pool_dpdk_mem_src_ops;
+extern const _odp_pool_mem_src_ops_t _odp_pool_sock_xdp_mem_src_ops;
+
+/* List of available ODP packet pool memory source operations. Array must be NULL terminated */
+const _odp_pool_mem_src_ops_t * const _odp_pool_mem_src_ops[] = {
+#ifdef _ODP_PKTIO_DPDK
+ &_odp_pool_dpdk_mem_src_ops,
+#endif
+#ifdef _ODP_PKTIO_XDP
+ &_odp_pool_sock_xdp_mem_src_ops,
+#endif
+ NULL
+};
diff --git a/platform/linux-generic/odp_queue.c b/platform/linux-generic/odp_queue.c
deleted file mode 100644
index 7ad346ad4..000000000
--- a/platform/linux-generic/odp_queue.c
+++ /dev/null
@@ -1,785 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/queue.h>
-#include <odp_queue_internal.h>
-#include <odp_queue_if.h>
-#include <odp/api/std_types.h>
-#include <odp/api/align.h>
-#include <odp/api/buffer.h>
-#include <odp_buffer_internal.h>
-#include <odp_pool_internal.h>
-#include <odp_buffer_inlines.h>
-#include <odp_internal.h>
-#include <odp/api/shared_memory.h>
-#include <odp/api/schedule.h>
-#include <odp_schedule_if.h>
-#include <odp_config_internal.h>
-#include <odp_packet_io_internal.h>
-#include <odp_debug_internal.h>
-#include <odp/api/hints.h>
-#include <odp/api/sync.h>
-#include <odp/api/traffic_mngr.h>
-
-#define NUM_INTERNAL_QUEUES 64
-
-#include <odp/api/plat/ticketlock_inlines.h>
-#define LOCK(a) _odp_ticketlock_lock(a)
-#define UNLOCK(a) _odp_ticketlock_unlock(a)
-#define LOCK_INIT(a) odp_ticketlock_init(a)
-
-#include <string.h>
-#include <inttypes.h>
-
-static int queue_init(queue_entry_t *queue, const char *name,
- const odp_queue_param_t *param);
-
-typedef struct queue_table_t {
- queue_entry_t queue[ODP_CONFIG_QUEUES];
-} queue_table_t;
-
-static queue_table_t *queue_tbl;
-
-static
-queue_entry_t *get_qentry(uint32_t queue_id);
-
-static inline queue_entry_t *handle_to_qentry(odp_queue_t handle)
-{
- uint32_t queue_id;
-
- queue_id = queue_to_id(handle);
- return get_qentry(queue_id);
-}
-
-static inline odp_queue_t queue_from_id(uint32_t queue_id)
-{
- return _odp_cast_scalar(odp_queue_t, queue_id + 1);
-}
-
-static
-queue_entry_t *get_qentry(uint32_t queue_id)
-{
- return &queue_tbl->queue[queue_id];
-}
-
-static int queue_init_global(void)
-{
- uint32_t i;
- odp_shm_t shm;
-
- ODP_DBG("Queue init ... ");
-
- shm = odp_shm_reserve("odp_queues",
- sizeof(queue_table_t),
- sizeof(queue_entry_t), 0);
-
- queue_tbl = odp_shm_addr(shm);
-
- if (queue_tbl == NULL)
- return -1;
-
- memset(queue_tbl, 0, sizeof(queue_table_t));
-
- for (i = 0; i < ODP_CONFIG_QUEUES; i++) {
- /* init locks */
- queue_entry_t *queue = get_qentry(i);
- LOCK_INIT(&queue->s.lock);
- queue->s.index = i;
- queue->s.handle = queue_from_id(i);
- }
-
- ODP_DBG("done\n");
- ODP_DBG("Queue init global\n");
- ODP_DBG(" struct queue_entry_s size %zu\n",
- sizeof(struct queue_entry_s));
- ODP_DBG(" queue_entry_t size %zu\n",
- sizeof(queue_entry_t));
- ODP_DBG("\n");
-
- return 0;
-}
-
-static int queue_init_local(void)
-{
- return 0;
-}
-
-static int queue_term_local(void)
-{
- return 0;
-}
-
-static int queue_term_global(void)
-{
- int ret = 0;
- int rc = 0;
- queue_entry_t *queue;
- int i;
-
- for (i = 0; i < ODP_CONFIG_QUEUES; i++) {
- queue = &queue_tbl->queue[i];
- LOCK(&queue->s.lock);
- if (queue->s.status != QUEUE_STATUS_FREE) {
- ODP_ERR("Not destroyed queue: %s\n", queue->s.name);
- rc = -1;
- }
- UNLOCK(&queue->s.lock);
- }
-
- ret = odp_shm_free(odp_shm_lookup("odp_queues"));
- if (ret < 0) {
- ODP_ERR("shm free failed for odp_queues");
- rc = -1;
- }
-
- return rc;
-}
-
-static int queue_capability(odp_queue_capability_t *capa)
-{
- memset(capa, 0, sizeof(odp_queue_capability_t));
-
- /* Reserve some queues for internal use */
- capa->max_queues = ODP_CONFIG_QUEUES - NUM_INTERNAL_QUEUES;
- capa->max_ordered_locks = sched_fn->max_ordered_locks();
- capa->max_sched_groups = sched_fn->num_grps();
- capa->sched_prios = odp_schedule_num_prio();
- capa->plain.max_num = capa->max_queues;
- capa->sched.max_num = capa->max_queues;
-
- return 0;
-}
-
-static odp_queue_type_t queue_type(odp_queue_t handle)
-{
- return handle_to_qentry(handle)->s.type;
-}
-
-static odp_schedule_sync_t queue_sched_type(odp_queue_t handle)
-{
- return handle_to_qentry(handle)->s.param.sched.sync;
-}
-
-static odp_schedule_prio_t queue_sched_prio(odp_queue_t handle)
-{
- return handle_to_qentry(handle)->s.param.sched.prio;
-}
-
-static odp_schedule_group_t queue_sched_group(odp_queue_t handle)
-{
- return handle_to_qentry(handle)->s.param.sched.group;
-}
-
-static int queue_lock_count(odp_queue_t handle)
-{
- queue_entry_t *queue = handle_to_qentry(handle);
-
- return queue->s.param.sched.sync == ODP_SCHED_SYNC_ORDERED ?
- (int)queue->s.param.sched.lock_count : -1;
-}
-
-static odp_queue_t queue_create(const char *name,
- const odp_queue_param_t *param)
-{
- uint32_t i;
- queue_entry_t *queue;
- odp_queue_t handle = ODP_QUEUE_INVALID;
- odp_queue_type_t type = ODP_QUEUE_TYPE_PLAIN;
- odp_queue_param_t default_param;
-
- if (param == NULL) {
- odp_queue_param_init(&default_param);
- param = &default_param;
- }
-
- for (i = 0; i < ODP_CONFIG_QUEUES; i++) {
- queue = &queue_tbl->queue[i];
-
- if (queue->s.status != QUEUE_STATUS_FREE)
- continue;
-
- LOCK(&queue->s.lock);
- if (queue->s.status == QUEUE_STATUS_FREE) {
- if (queue_init(queue, name, param)) {
- UNLOCK(&queue->s.lock);
- return handle;
- }
-
- type = queue->s.type;
-
- if (type == ODP_QUEUE_TYPE_SCHED)
- queue->s.status = QUEUE_STATUS_NOTSCHED;
- else
- queue->s.status = QUEUE_STATUS_READY;
-
- handle = queue->s.handle;
- UNLOCK(&queue->s.lock);
- break;
- }
- UNLOCK(&queue->s.lock);
- }
-
- if (handle != ODP_QUEUE_INVALID && type == ODP_QUEUE_TYPE_SCHED) {
- if (sched_fn->init_queue(queue->s.index,
- &queue->s.param.sched)) {
- queue->s.status = QUEUE_STATUS_FREE;
- ODP_ERR("schedule queue init failed\n");
- return ODP_QUEUE_INVALID;
- }
- }
-
- return handle;
-}
-
-void sched_cb_queue_destroy_finalize(uint32_t queue_index)
-{
- queue_entry_t *queue = get_qentry(queue_index);
-
- LOCK(&queue->s.lock);
-
- if (queue->s.status == QUEUE_STATUS_DESTROYED) {
- queue->s.status = QUEUE_STATUS_FREE;
- sched_fn->destroy_queue(queue_index);
- }
- UNLOCK(&queue->s.lock);
-}
-
-static int queue_destroy(odp_queue_t handle)
-{
- queue_entry_t *queue;
- queue = handle_to_qentry(handle);
-
- if (handle == ODP_QUEUE_INVALID)
- return -1;
-
- LOCK(&queue->s.lock);
- if (queue->s.status == QUEUE_STATUS_FREE) {
- UNLOCK(&queue->s.lock);
- ODP_ERR("queue \"%s\" already free\n", queue->s.name);
- return -1;
- }
- if (queue->s.status == QUEUE_STATUS_DESTROYED) {
- UNLOCK(&queue->s.lock);
- ODP_ERR("queue \"%s\" already destroyed\n", queue->s.name);
- return -1;
- }
- if (queue->s.head != NULL) {
- UNLOCK(&queue->s.lock);
- ODP_ERR("queue \"%s\" not empty\n", queue->s.name);
- return -1;
- }
-
- switch (queue->s.status) {
- case QUEUE_STATUS_READY:
- queue->s.status = QUEUE_STATUS_FREE;
- break;
- case QUEUE_STATUS_NOTSCHED:
- queue->s.status = QUEUE_STATUS_FREE;
- sched_fn->destroy_queue(queue->s.index);
- break;
- case QUEUE_STATUS_SCHED:
- /* Queue is still in scheduling */
- queue->s.status = QUEUE_STATUS_DESTROYED;
- break;
- default:
- ODP_ABORT("Unexpected queue status\n");
- }
- UNLOCK(&queue->s.lock);
-
- return 0;
-}
-
-static int queue_context_set(odp_queue_t handle, void *context,
- uint32_t len ODP_UNUSED)
-{
- odp_mb_full();
- handle_to_qentry(handle)->s.param.context = context;
- odp_mb_full();
- return 0;
-}
-
-static void *queue_context(odp_queue_t handle)
-{
- return handle_to_qentry(handle)->s.param.context;
-}
-
-static odp_queue_t queue_lookup(const char *name)
-{
- uint32_t i;
-
- for (i = 0; i < ODP_CONFIG_QUEUES; i++) {
- queue_entry_t *queue = &queue_tbl->queue[i];
-
- if (queue->s.status == QUEUE_STATUS_FREE ||
- queue->s.status == QUEUE_STATUS_DESTROYED)
- continue;
-
- LOCK(&queue->s.lock);
- if (strcmp(name, queue->s.name) == 0) {
- /* found it */
- UNLOCK(&queue->s.lock);
- return queue->s.handle;
- }
- UNLOCK(&queue->s.lock);
- }
-
- return ODP_QUEUE_INVALID;
-}
-
-static inline int enq_multi(queue_t q_int, odp_buffer_hdr_t *buf_hdr[],
- int num)
-{
- int sched = 0;
- int i, ret;
- queue_entry_t *queue;
- odp_buffer_hdr_t *hdr, *tail, *next_hdr;
-
- queue = qentry_from_int(q_int);
- if (sched_fn->ord_enq_multi(q_int, (void **)buf_hdr, num, &ret))
- return ret;
-
- /* Optimize the common case of single enqueue */
- if (num == 1) {
- tail = buf_hdr[0];
- hdr = tail;
- hdr->burst_num = 0;
- hdr->next = NULL;
- } else {
- int next;
-
- /* Start from the last buffer header */
- tail = buf_hdr[num - 1];
- hdr = tail;
- hdr->next = NULL;
- next = num - 2;
-
- while (1) {
- /* Build a burst. The buffer header carrying
- * a burst is the last buffer of the burst. */
- for (i = 0; next >= 0 && i < BUFFER_BURST_SIZE;
- i++, next--)
- hdr->burst[BUFFER_BURST_SIZE - 1 - i] =
- buf_hdr[next];
-
- hdr->burst_num = i;
- hdr->burst_first = BUFFER_BURST_SIZE - i;
-
- if (odp_likely(next < 0))
- break;
-
- /* Get another header and link it */
- next_hdr = hdr;
- hdr = buf_hdr[next];
- hdr->next = next_hdr;
- next--;
- }
- }
-
- LOCK(&queue->s.lock);
- if (odp_unlikely(queue->s.status < QUEUE_STATUS_READY)) {
- UNLOCK(&queue->s.lock);
- ODP_ERR("Bad queue status\n");
- return -1;
- }
-
- /* Empty queue */
- if (queue->s.head == NULL)
- queue->s.head = hdr;
- else
- queue->s.tail->next = hdr;
-
- queue->s.tail = tail;
-
- if (queue->s.status == QUEUE_STATUS_NOTSCHED) {
- queue->s.status = QUEUE_STATUS_SCHED;
- sched = 1; /* retval: schedule queue */
- }
- UNLOCK(&queue->s.lock);
-
- /* Add queue to scheduling */
- if (sched && sched_fn->sched_queue(queue->s.index))
- ODP_ABORT("schedule_queue failed\n");
-
- return num; /* All events enqueued */
-}
-
-static int queue_int_enq_multi(queue_t q_int, odp_buffer_hdr_t *buf_hdr[],
- int num)
-{
- return enq_multi(q_int, buf_hdr, num);
-}
-
-static int queue_int_enq(queue_t q_int, odp_buffer_hdr_t *buf_hdr)
-{
- int ret;
-
- ret = enq_multi(q_int, &buf_hdr, 1);
-
- if (ret == 1)
- return 0;
- else
- return -1;
-}
-
-static int queue_enq_multi(odp_queue_t handle, const odp_event_t ev[], int num)
-{
- queue_entry_t *queue = handle_to_qentry(handle);
-
- if (odp_unlikely(num == 0))
- return 0;
-
- if (num > QUEUE_MULTI_MAX)
- num = QUEUE_MULTI_MAX;
-
- return queue->s.enqueue_multi(qentry_to_int(queue),
- (odp_buffer_hdr_t **)(uintptr_t)ev, num);
-}
-
-static int queue_enq(odp_queue_t handle, odp_event_t ev)
-{
- queue_entry_t *queue = handle_to_qentry(handle);
-
- return queue->s.enqueue(qentry_to_int(queue),
- (odp_buffer_hdr_t *)(uintptr_t)ev);
-}
-
-static inline int deq_multi(queue_entry_t *queue, odp_buffer_hdr_t *buf_hdr[],
- int num)
-{
- odp_buffer_hdr_t *hdr, *next;
- int i, j;
- int updated = 0;
- int status_sync = sched_fn->status_sync;
-
- LOCK(&queue->s.lock);
- if (odp_unlikely(queue->s.status < QUEUE_STATUS_READY)) {
- /* Bad queue, or queue has been destroyed.
- * Scheduler finalizes queue destroy after this. */
- UNLOCK(&queue->s.lock);
- return -1;
- }
-
- hdr = queue->s.head;
-
- if (hdr == NULL) {
- /* Already empty queue */
- if (queue->s.status == QUEUE_STATUS_SCHED) {
- queue->s.status = QUEUE_STATUS_NOTSCHED;
-
- if (status_sync)
- sched_fn->unsched_queue(queue->s.index);
- }
-
- UNLOCK(&queue->s.lock);
- return 0;
- }
-
- for (i = 0; i < num && hdr; ) {
- int burst_num = hdr->burst_num;
- int first = hdr->burst_first;
-
- /* First, get bursted buffers */
- for (j = 0; j < burst_num && i < num; j++, i++) {
- buf_hdr[i] = hdr->burst[first + j];
- odp_prefetch(buf_hdr[i]);
- }
-
- if (burst_num) {
- hdr->burst_num = burst_num - j;
- hdr->burst_first = first + j;
- }
-
- if (i == num)
- break;
-
- /* When burst is empty, consume the current buffer header and
- * move to the next header */
- buf_hdr[i] = hdr;
- next = hdr->next;
- hdr->next = NULL;
- hdr = next;
- updated++;
- i++;
- }
-
- /* Write head only if updated */
- if (updated)
- queue->s.head = hdr;
-
- /* Queue is empty */
- if (hdr == NULL)
- queue->s.tail = NULL;
-
- if (status_sync && queue->s.type == ODP_QUEUE_TYPE_SCHED)
- sched_fn->save_context(queue->s.index);
-
- UNLOCK(&queue->s.lock);
-
- return i;
-}
-
-static int queue_int_deq_multi(queue_t q_int, odp_buffer_hdr_t *buf_hdr[],
- int num)
-{
- queue_entry_t *queue = qentry_from_int(q_int);
-
- return deq_multi(queue, buf_hdr, num);
-}
-
-static odp_buffer_hdr_t *queue_int_deq(queue_t q_int)
-{
- queue_entry_t *queue = qentry_from_int(q_int);
- odp_buffer_hdr_t *buf_hdr = NULL;
- int ret;
-
- ret = deq_multi(queue, &buf_hdr, 1);
-
- if (ret == 1)
- return buf_hdr;
- else
- return NULL;
-}
-
-static int queue_deq_multi(odp_queue_t handle, odp_event_t ev[], int num)
-{
- queue_entry_t *queue = handle_to_qentry(handle);
-
- if (num > QUEUE_MULTI_MAX)
- num = QUEUE_MULTI_MAX;
-
- return queue->s.dequeue_multi(qentry_to_int(queue),
- (odp_buffer_hdr_t **)ev, num);
-}
-
-static odp_event_t queue_deq(odp_queue_t handle)
-{
- queue_entry_t *queue = handle_to_qentry(handle);
-
- return (odp_event_t)queue->s.dequeue(qentry_to_int(queue));
-}
-
-static int queue_init(queue_entry_t *queue, const char *name,
- const odp_queue_param_t *param)
-{
- if (name == NULL) {
- queue->s.name[0] = 0;
- } else {
- strncpy(queue->s.name, name, ODP_QUEUE_NAME_LEN - 1);
- queue->s.name[ODP_QUEUE_NAME_LEN - 1] = 0;
- }
- memcpy(&queue->s.param, param, sizeof(odp_queue_param_t));
- if (queue->s.param.sched.lock_count > sched_fn->max_ordered_locks())
- return -1;
-
- if (param->type == ODP_QUEUE_TYPE_SCHED)
- queue->s.param.deq_mode = ODP_QUEUE_OP_DISABLED;
-
- queue->s.type = queue->s.param.type;
-
- queue->s.enqueue = queue_int_enq;
- queue->s.dequeue = queue_int_deq;
- queue->s.enqueue_multi = queue_int_enq_multi;
- queue->s.dequeue_multi = queue_int_deq_multi;
-
- queue->s.pktin = PKTIN_INVALID;
- queue->s.pktout = PKTOUT_INVALID;
-
- queue->s.head = NULL;
- queue->s.tail = NULL;
-
- return 0;
-}
-
-static void queue_param_init(odp_queue_param_t *params)
-{
- memset(params, 0, sizeof(odp_queue_param_t));
- params->type = ODP_QUEUE_TYPE_PLAIN;
- params->enq_mode = ODP_QUEUE_OP_MT;
- params->deq_mode = ODP_QUEUE_OP_MT;
- params->sched.prio = ODP_SCHED_PRIO_DEFAULT;
- params->sched.sync = ODP_SCHED_SYNC_PARALLEL;
- params->sched.group = ODP_SCHED_GROUP_ALL;
-}
-
-static int queue_info(odp_queue_t handle, odp_queue_info_t *info)
-{
- uint32_t queue_id;
- queue_entry_t *queue;
- int status;
-
- if (odp_unlikely(info == NULL)) {
- ODP_ERR("Unable to store info, NULL ptr given\n");
- return -1;
- }
-
- queue_id = queue_to_id(handle);
-
- if (odp_unlikely(queue_id >= ODP_CONFIG_QUEUES)) {
- ODP_ERR("Invalid queue handle:%" PRIu64 "\n",
- odp_queue_to_u64(handle));
- return -1;
- }
-
- queue = get_qentry(queue_id);
-
- LOCK(&queue->s.lock);
- status = queue->s.status;
-
- if (odp_unlikely(status == QUEUE_STATUS_FREE ||
- status == QUEUE_STATUS_DESTROYED)) {
- UNLOCK(&queue->s.lock);
- ODP_ERR("Invalid queue status:%d\n", status);
- return -1;
- }
-
- info->name = queue->s.name;
- info->param = queue->s.param;
-
- UNLOCK(&queue->s.lock);
-
- return 0;
-}
-
-odp_queue_t sched_cb_queue_handle(uint32_t queue_index)
-{
- return queue_from_id(queue_index);
-}
-
-int sched_cb_queue_deq_multi(uint32_t queue_index, odp_event_t ev[], int num)
-{
- queue_entry_t *qe = get_qentry(queue_index);
-
- return deq_multi(qe, (odp_buffer_hdr_t **)ev, num);
-}
-
-int sched_cb_queue_empty(uint32_t queue_index)
-{
- queue_entry_t *queue = get_qentry(queue_index);
- int ret = 0;
-
- LOCK(&queue->s.lock);
-
- if (odp_unlikely(queue->s.status < QUEUE_STATUS_READY)) {
- /* Bad queue, or queue has been destroyed. */
- UNLOCK(&queue->s.lock);
- return -1;
- }
-
- if (queue->s.head == NULL) {
- /* Already empty queue. Update status. */
- if (queue->s.status == QUEUE_STATUS_SCHED)
- queue->s.status = QUEUE_STATUS_NOTSCHED;
-
- ret = 1;
- }
-
- UNLOCK(&queue->s.lock);
-
- return ret;
-}
-
-static uint64_t queue_to_u64(odp_queue_t hdl)
-{
- return _odp_pri(hdl);
-}
-
-static odp_pktout_queue_t queue_get_pktout(queue_t q_int)
-{
- return qentry_from_int(q_int)->s.pktout;
-}
-
-static void queue_set_pktout(queue_t q_int, odp_pktio_t pktio, int index)
-{
- queue_entry_t *qentry = qentry_from_int(q_int);
-
- qentry->s.pktout.pktio = pktio;
- qentry->s.pktout.index = index;
-}
-
-static odp_pktin_queue_t queue_get_pktin(queue_t q_int)
-{
- return qentry_from_int(q_int)->s.pktin;
-}
-
-static void queue_set_pktin(queue_t q_int, odp_pktio_t pktio, int index)
-{
- queue_entry_t *qentry = qentry_from_int(q_int);
-
- qentry->s.pktin.pktio = pktio;
- qentry->s.pktin.index = index;
-}
-
-static void queue_set_enq_deq_func(queue_t q_int,
- queue_enq_fn_t enq,
- queue_enq_multi_fn_t enq_multi,
- queue_deq_fn_t deq,
- queue_deq_multi_fn_t deq_multi)
-{
- queue_entry_t *qentry = qentry_from_int(q_int);
-
- if (enq)
- qentry->s.enqueue = enq;
-
- if (enq_multi)
- qentry->s.enqueue_multi = enq_multi;
-
- if (deq)
- qentry->s.dequeue = deq;
-
- if (deq_multi)
- qentry->s.dequeue_multi = deq_multi;
-}
-
-static queue_t queue_from_ext(odp_queue_t handle)
-{
- return qentry_to_int(handle_to_qentry(handle));
-}
-
-static odp_queue_t queue_to_ext(queue_t q_int)
-{
- return qentry_from_int(q_int)->s.handle;
-}
-
-/* API functions */
-queue_api_t queue_default_api = {
- .queue_create = queue_create,
- .queue_destroy = queue_destroy,
- .queue_lookup = queue_lookup,
- .queue_capability = queue_capability,
- .queue_context_set = queue_context_set,
- .queue_context = queue_context,
- .queue_enq = queue_enq,
- .queue_enq_multi = queue_enq_multi,
- .queue_deq = queue_deq,
- .queue_deq_multi = queue_deq_multi,
- .queue_type = queue_type,
- .queue_sched_type = queue_sched_type,
- .queue_sched_prio = queue_sched_prio,
- .queue_sched_group = queue_sched_group,
- .queue_lock_count = queue_lock_count,
- .queue_to_u64 = queue_to_u64,
- .queue_param_init = queue_param_init,
- .queue_info = queue_info
-};
-
-/* Functions towards internal components */
-queue_fn_t queue_default_fn = {
- .init_global = queue_init_global,
- .term_global = queue_term_global,
- .init_local = queue_init_local,
- .term_local = queue_term_local,
- .from_ext = queue_from_ext,
- .to_ext = queue_to_ext,
- .enq = queue_int_enq,
- .enq_multi = queue_int_enq_multi,
- .deq = queue_int_deq,
- .deq_multi = queue_int_deq_multi,
- .get_pktout = queue_get_pktout,
- .set_pktout = queue_set_pktout,
- .get_pktin = queue_get_pktin,
- .set_pktin = queue_set_pktin,
- .set_enq_deq_fn = queue_set_enq_deq_func
-};
diff --git a/platform/linux-generic/odp_queue_api.c b/platform/linux-generic/odp_queue_api.c
new file mode 100644
index 000000000..e1beeeb58
--- /dev/null
+++ b/platform/linux-generic/odp_queue_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/queue.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/queue_inlines.h>
diff --git a/platform/linux-generic/odp_queue_basic.c b/platform/linux-generic/odp_queue_basic.c
new file mode 100644
index 000000000..ec51b65f2
--- /dev/null
+++ b/platform/linux-generic/odp_queue_basic.c
@@ -0,0 +1,1298 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp/api/align.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/queue.h>
+#include <odp/api/schedule.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/std_types.h>
+#include <odp/api/sync.h>
+#include <odp/api/ticketlock.h>
+#include <odp/api/traffic_mngr.h>
+
+#include <odp/api/plat/queue_inline_types.h>
+#include <odp/api/plat/sync_inlines.h>
+#include <odp/api/plat/ticketlock_inlines.h>
+
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_queue_basic_internal.h>
+#include <odp_queue_if.h>
+#include <odp_schedule_if.h>
+#include <odp_timer_internal.h>
+#include <odp_string_internal.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#define LOCK(queue_ptr) odp_ticketlock_lock(&((queue_ptr)->lock))
+#define UNLOCK(queue_ptr) odp_ticketlock_unlock(&((queue_ptr)->lock))
+#define LOCK_INIT(queue_ptr) odp_ticketlock_init(&((queue_ptr)->lock))
+
+#define MIN_QUEUE_SIZE 32
+#define MAX_QUEUE_SIZE (1 * 1024 * 1024)
+
+static int queue_init(queue_entry_t *queue, const char *name,
+ const odp_queue_param_t *param);
+
+queue_global_t *_odp_queue_glb;
+extern _odp_queue_inline_offset_t _odp_queue_inline_offset;
+
+static int queue_capa(odp_queue_capability_t *capa, int sched ODP_UNUSED)
+{
+ memset(capa, 0, sizeof(odp_queue_capability_t));
+
+ /* Reserve some queues for internal use */
+ capa->max_queues = CONFIG_MAX_QUEUES - CONFIG_INTERNAL_QUEUES;
+ capa->plain.max_num = CONFIG_MAX_PLAIN_QUEUES;
+ capa->plain.max_size = _odp_queue_glb->config.max_queue_size;
+ capa->plain.lockfree.max_num = _odp_queue_glb->queue_lf_num;
+ capa->plain.lockfree.max_size = _odp_queue_glb->queue_lf_size;
+
+ return 0;
+}
+
+static int read_config_file(queue_global_t *_odp_queue_glb)
+{
+ const char *str;
+ uint32_t val_u32;
+ int val = 0;
+
+ _ODP_PRINT("Queue config:\n");
+
+ str = "queue_basic.max_queue_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ val_u32 = val;
+
+ if (val_u32 > MAX_QUEUE_SIZE || val_u32 < MIN_QUEUE_SIZE ||
+ !_ODP_CHECK_IS_POWER2(val_u32)) {
+ _ODP_ERR("Bad value %s = %u\n", str, val_u32);
+ return -1;
+ }
+
+ _odp_queue_glb->config.max_queue_size = val_u32;
+ _ODP_PRINT(" %s: %u\n", str, val_u32);
+
+ str = "queue_basic.default_queue_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ val_u32 = val;
+
+ if (val_u32 > _odp_queue_glb->config.max_queue_size ||
+ val_u32 < MIN_QUEUE_SIZE ||
+ !_ODP_CHECK_IS_POWER2(val_u32)) {
+ _ODP_ERR("Bad value %s = %u\n", str, val_u32);
+ return -1;
+ }
+
+ _odp_queue_glb->config.default_queue_size = val_u32;
+ _ODP_PRINT(" %s: %u\n\n", str, val_u32);
+
+ return 0;
+}
+
+static int queue_init_global(void)
+{
+ uint32_t i;
+ odp_shm_t shm;
+ uint32_t lf_size = 0;
+ queue_lf_func_t *lf_func;
+ odp_queue_capability_t capa;
+ uint64_t mem_size;
+
+ _ODP_DBG("Starts...\n");
+
+ /* Fill in queue entry field offsets for inline functions */
+ memset(&_odp_queue_inline_offset, 0,
+ sizeof(_odp_queue_inline_offset_t));
+ _odp_queue_inline_offset.context = offsetof(queue_entry_t,
+ param.context);
+
+ shm = odp_shm_reserve("_odp_queue_basic_global",
+ sizeof(queue_global_t),
+ sizeof(queue_entry_t),
+ 0);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ _odp_queue_glb = odp_shm_addr(shm);
+
+ memset(_odp_queue_glb, 0, sizeof(queue_global_t));
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ /* init locks */
+ queue_entry_t *queue = qentry_from_index(i);
+
+ LOCK_INIT(queue);
+ queue->index = i;
+ queue->handle = (odp_queue_t)queue;
+ }
+
+ if (read_config_file(_odp_queue_glb)) {
+ odp_shm_free(shm);
+ return -1;
+ }
+
+ _odp_queue_glb->queue_gbl_shm = shm;
+ mem_size = sizeof(uint32_t) * CONFIG_MAX_QUEUES *
+ (uint64_t)_odp_queue_glb->config.max_queue_size;
+
+ shm = odp_shm_reserve("_odp_queue_basic_rings", mem_size,
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ odp_shm_free(_odp_queue_glb->queue_gbl_shm);
+ return -1;
+ }
+
+ _odp_queue_glb->queue_ring_shm = shm;
+ _odp_queue_glb->ring_data = odp_shm_addr(shm);
+
+ lf_func = &_odp_queue_glb->queue_lf_func;
+ _odp_queue_glb->queue_lf_num = _odp_queue_lf_init_global(&lf_size, lf_func);
+ _odp_queue_glb->queue_lf_size = lf_size;
+
+ queue_capa(&capa, 0);
+
+ _ODP_DBG("... done.\n");
+ _ODP_DBG(" queue_entry_t size %zu\n", sizeof(queue_entry_t));
+ _ODP_DBG(" max num queues %u\n", capa.max_queues);
+ _ODP_DBG(" max queue size %u\n", capa.plain.max_size);
+ _ODP_DBG(" max num lockfree %u\n", capa.plain.lockfree.max_num);
+ _ODP_DBG(" max lockfree size %u\n\n", capa.plain.lockfree.max_size);
+
+ return 0;
+}
+
+static int queue_init_local(void)
+{
+ return 0;
+}
+
+static int queue_term_local(void)
+{
+ return 0;
+}
+
+static int queue_term_global(void)
+{
+ int ret = 0;
+ queue_entry_t *queue;
+ int i;
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue = qentry_from_index(i);
+ LOCK(queue);
+ if (queue->status != QUEUE_STATUS_FREE) {
+ _ODP_ERR("Not destroyed queue: %s\n", queue->name);
+ ret = -1;
+ }
+ UNLOCK(queue);
+ }
+
+ _odp_queue_lf_term_global();
+
+ if (odp_shm_free(_odp_queue_glb->queue_ring_shm)) {
+ _ODP_ERR("shm free failed");
+ ret = -1;
+ }
+
+ if (odp_shm_free(_odp_queue_glb->queue_gbl_shm)) {
+ _ODP_ERR("shm free failed");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int queue_capability(odp_queue_capability_t *capa)
+{
+ return queue_capa(capa, 1);
+}
+
+static odp_queue_type_t queue_type(odp_queue_t handle)
+{
+ return qentry_from_handle(handle)->type;
+}
+
+static odp_schedule_sync_t queue_sched_type(odp_queue_t handle)
+{
+ return qentry_from_handle(handle)->param.sched.sync;
+}
+
+static odp_schedule_prio_t queue_sched_prio(odp_queue_t handle)
+{
+ return qentry_from_handle(handle)->param.sched.prio;
+}
+
+static odp_schedule_group_t queue_sched_group(odp_queue_t handle)
+{
+ return qentry_from_handle(handle)->param.sched.group;
+}
+
+static uint32_t queue_lock_count(odp_queue_t handle)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ return queue->param.sched.sync == ODP_SCHED_SYNC_ORDERED ?
+ queue->param.sched.lock_count : 0;
+}
+
+static odp_queue_t queue_create(const char *name,
+ const odp_queue_param_t *param)
+{
+ uint32_t i;
+ uint32_t max_idx;
+ queue_entry_t *queue;
+ void *queue_lf;
+ odp_queue_type_t type;
+ odp_queue_param_t default_param;
+ odp_queue_t handle = ODP_QUEUE_INVALID;
+
+ if (param == NULL) {
+ odp_queue_param_init(&default_param);
+ param = &default_param;
+ }
+
+ type = param->type;
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ if (param->sched.prio < odp_schedule_min_prio() ||
+ param->sched.prio > odp_schedule_max_prio()) {
+ _ODP_ERR("Bad queue priority: %i\n", param->sched.prio);
+ return ODP_QUEUE_INVALID;
+ }
+ }
+
+ if (param->nonblocking == ODP_BLOCKING) {
+ if (param->size > _odp_queue_glb->config.max_queue_size)
+ return ODP_QUEUE_INVALID;
+ } else if (param->nonblocking == ODP_NONBLOCKING_LF) {
+ /* Only plain type lock-free queues supported */
+ if (type != ODP_QUEUE_TYPE_PLAIN)
+ return ODP_QUEUE_INVALID;
+ if (param->size > _odp_queue_glb->queue_lf_size)
+ return ODP_QUEUE_INVALID;
+ } else {
+ /* Wait-free queues not supported */
+ return ODP_QUEUE_INVALID;
+ }
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ /* Start scheduled queue indices from zero to enable direct
+ * mapping to scheduler implementation indices. */
+ i = 0;
+ max_idx = CONFIG_MAX_SCHED_QUEUES;
+ } else {
+ i = CONFIG_MAX_SCHED_QUEUES;
+ /* All internal queues are of type plain */
+ max_idx = CONFIG_MAX_QUEUES;
+ }
+
+ for (; i < max_idx; i++) {
+ queue = qentry_from_index(i);
+
+ if (queue->status != QUEUE_STATUS_FREE)
+ continue;
+
+ LOCK(queue);
+ if (queue->status == QUEUE_STATUS_FREE) {
+ if (queue_init(queue, name, param)) {
+ UNLOCK(queue);
+ return ODP_QUEUE_INVALID;
+ }
+
+ if (!queue->spsc &&
+ param->nonblocking == ODP_NONBLOCKING_LF) {
+ queue_lf_func_t *lf_fn;
+
+ lf_fn = &_odp_queue_glb->queue_lf_func;
+
+ queue_lf = _odp_queue_lf_create(queue);
+
+ if (queue_lf == NULL) {
+ UNLOCK(queue);
+ return ODP_QUEUE_INVALID;
+ }
+ queue->queue_lf = queue_lf;
+
+ queue->enqueue = lf_fn->enq;
+ queue->enqueue_multi = lf_fn->enq_multi;
+ queue->dequeue = lf_fn->deq;
+ queue->dequeue_multi = lf_fn->deq_multi;
+ queue->orig_dequeue_multi = lf_fn->deq_multi;
+ }
+
+ if (type == ODP_QUEUE_TYPE_SCHED)
+ queue->status = QUEUE_STATUS_NOTSCHED;
+ else
+ queue->status = QUEUE_STATUS_READY;
+
+ handle = queue->handle;
+ UNLOCK(queue);
+ break;
+ }
+ UNLOCK(queue);
+ }
+
+ if (handle == ODP_QUEUE_INVALID)
+ return ODP_QUEUE_INVALID;
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ if (_odp_sched_fn->create_queue(queue->index,
+ &queue->param.sched)) {
+ queue->status = QUEUE_STATUS_FREE;
+ _ODP_ERR("schedule queue init failed\n");
+ return ODP_QUEUE_INVALID;
+ }
+ }
+
+ return handle;
+}
+
+static int queue_create_multi(const char *name[], const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(param != NULL);
+ _ODP_ASSERT(queue != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ odp_queue_t cur_queue;
+ const char *cur_name = name != NULL ? name[i] : NULL;
+ const odp_queue_param_t *cur_param = share_param ? &param[0] : &param[i];
+
+ cur_queue = queue_create(cur_name, cur_param);
+ if (cur_queue == ODP_QUEUE_INVALID)
+ return (i == 0) ? -1 : i;
+
+ queue[i] = cur_queue;
+ }
+ return i;
+}
+
+void _odp_sched_queue_set_status(uint32_t queue_index, int status)
+{
+ queue_entry_t *queue = qentry_from_index(queue_index);
+
+ LOCK(queue);
+
+ queue->status = status;
+
+ UNLOCK(queue);
+}
+
+static int queue_destroy(odp_queue_t handle)
+{
+ int empty;
+ queue_entry_t *queue;
+
+ queue = qentry_from_handle(handle);
+
+ if (handle == ODP_QUEUE_INVALID)
+ return -1;
+
+ LOCK(queue);
+ if (queue->status == QUEUE_STATUS_FREE) {
+ UNLOCK(queue);
+ _ODP_ERR("queue \"%s\" already free\n", queue->name);
+ return -1;
+ }
+ if (queue->status == QUEUE_STATUS_DESTROYED) {
+ UNLOCK(queue);
+ _ODP_ERR("queue \"%s\" already destroyed\n", queue->name);
+ return -1;
+ }
+
+ if (queue->spsc)
+ empty = ring_spsc_is_empty(&queue->ring_spsc);
+ else if (queue->type == ODP_QUEUE_TYPE_SCHED)
+ empty = ring_st_is_empty(&queue->ring_st);
+ else
+ empty = ring_mpmc_u32_is_empty(&queue->ring_mpmc);
+
+ if (!empty) {
+ UNLOCK(queue);
+ _ODP_ERR("queue \"%s\" not empty\n", queue->name);
+ return -1;
+ }
+
+ switch (queue->status) {
+ case QUEUE_STATUS_READY:
+ queue->status = QUEUE_STATUS_FREE;
+ break;
+ case QUEUE_STATUS_NOTSCHED:
+ queue->status = QUEUE_STATUS_FREE;
+ _odp_sched_fn->destroy_queue(queue->index);
+ break;
+ case QUEUE_STATUS_SCHED:
+ /* Queue is still in scheduling */
+ queue->status = QUEUE_STATUS_DESTROYED;
+ break;
+ default:
+ _ODP_ABORT("Unexpected queue status\n");
+ }
+
+ if (queue->queue_lf)
+ _odp_queue_lf_destroy(queue->queue_lf);
+
+ UNLOCK(queue);
+
+ return 0;
+}
+
+static int queue_destroy_multi(odp_queue_t handle[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(handle != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ int ret = queue_destroy(handle[i]);
+
+ if (ret)
+ return (i == 0) ? ret : i;
+ }
+
+ return i;
+}
+
+static int queue_context_set(odp_queue_t handle, void *context,
+ uint32_t len ODP_UNUSED)
+{
+ odp_mb_full();
+ qentry_from_handle(handle)->param.context = context;
+ odp_mb_full();
+ return 0;
+}
+
+static odp_queue_t queue_lookup(const char *name)
+{
+ uint32_t i;
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue_entry_t *queue = qentry_from_index(i);
+
+ if (queue->status == QUEUE_STATUS_FREE ||
+ queue->status == QUEUE_STATUS_DESTROYED)
+ continue;
+
+ LOCK(queue);
+ if (strcmp(name, queue->name) == 0) {
+ /* found it */
+ UNLOCK(queue);
+ return queue->handle;
+ }
+ UNLOCK(queue);
+ }
+
+ return ODP_QUEUE_INVALID;
+}
+
+static inline void event_index_from_hdr(uint32_t event_index[],
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ event_index[i] = event_hdr[i]->index.u32;
+}
+
+static inline void event_index_to_hdr(_odp_event_hdr_t *event_hdr[],
+ uint32_t event_index[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ event_hdr[i] = _odp_event_hdr_from_index_u32(event_index[i]);
+ odp_prefetch(event_hdr[i]);
+ }
+}
+
+static inline int _plain_queue_enq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ queue_entry_t *queue;
+ int ret, num_enq;
+ ring_mpmc_u32_t *ring_mpmc;
+ uint32_t event_idx[num];
+
+ queue = qentry_from_handle(handle);
+ ring_mpmc = &queue->ring_mpmc;
+
+ if (_odp_sched_fn->ord_enq_multi(handle, (void **)event_hdr, num, &ret))
+ return ret;
+
+ event_index_from_hdr(event_idx, event_hdr, num);
+
+ num_enq = ring_mpmc_u32_enq_multi(ring_mpmc, queue->ring_data,
+ queue->ring_mask, event_idx, num);
+
+ return num_enq;
+}
+
+static inline int _plain_queue_deq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ int num_deq;
+ queue_entry_t *queue;
+ ring_mpmc_u32_t *ring_mpmc;
+ uint32_t event_idx[num];
+
+ queue = qentry_from_handle(handle);
+ ring_mpmc = &queue->ring_mpmc;
+
+ num_deq = ring_mpmc_u32_deq_multi(ring_mpmc, queue->ring_data,
+ queue->ring_mask, event_idx, num);
+
+ if (num_deq == 0)
+ return 0;
+
+ event_index_to_hdr(event_hdr, event_idx, num_deq);
+
+ return num_deq;
+}
+
+static int plain_queue_enq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ return _plain_queue_enq_multi(handle, event_hdr, num);
+}
+
+static int plain_queue_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ int ret;
+
+ ret = _plain_queue_enq_multi(handle, &event_hdr, 1);
+
+ if (ret == 1)
+ return 0;
+ else
+ return -1;
+}
+
+static int plain_queue_deq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ return _plain_queue_deq_multi(handle, event_hdr, num);
+}
+
+static _odp_event_hdr_t *plain_queue_deq(odp_queue_t handle)
+{
+ _odp_event_hdr_t *event_hdr = NULL;
+ int ret;
+
+ ret = _plain_queue_deq_multi(handle, &event_hdr, 1);
+
+ if (ret == 1)
+ return event_hdr;
+ else
+ return NULL;
+}
+
+static int error_enqueue(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ (void)event_hdr;
+
+ _ODP_ERR("Enqueue not supported (0x%" PRIx64 ")\n", odp_queue_to_u64(handle));
+
+ return -1;
+}
+
+static int error_enqueue_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ (void)event_hdr;
+ (void)num;
+
+ _ODP_ERR("Enqueue multi not supported (0x%" PRIx64 ")\n", odp_queue_to_u64(handle));
+
+ return -1;
+}
+
+static _odp_event_hdr_t *error_dequeue(odp_queue_t handle)
+{
+ _ODP_ERR("Dequeue not supported (0x%" PRIx64 ")\n", odp_queue_to_u64(handle));
+
+ return NULL;
+}
+
+static int error_dequeue_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ (void)event_hdr;
+ (void)num;
+
+ _ODP_ERR("Dequeue multi not supported (0x%" PRIx64 ")\n", odp_queue_to_u64(handle));
+
+ return -1;
+}
+
+static void queue_param_init(odp_queue_param_t *params)
+{
+ memset(params, 0, sizeof(odp_queue_param_t));
+ params->type = ODP_QUEUE_TYPE_PLAIN;
+ params->enq_mode = ODP_QUEUE_OP_MT;
+ params->deq_mode = ODP_QUEUE_OP_MT;
+ params->nonblocking = ODP_BLOCKING;
+ params->order = ODP_QUEUE_ORDER_KEEP;
+ params->sched.prio = odp_schedule_default_prio();
+ params->sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ params->sched.group = ODP_SCHED_GROUP_ALL;
+}
+
+static int queue_info(odp_queue_t handle, odp_queue_info_t *info)
+{
+ uint32_t queue_id;
+ queue_entry_t *queue;
+ int status;
+
+ if (odp_unlikely(info == NULL)) {
+ _ODP_ERR("Unable to store info, NULL ptr given\n");
+ return -1;
+ }
+
+ queue_id = queue_to_index(handle);
+
+ if (odp_unlikely(queue_id >= CONFIG_MAX_QUEUES)) {
+ _ODP_ERR("Invalid queue handle: 0x%" PRIx64 "\n", odp_queue_to_u64(handle));
+ return -1;
+ }
+
+ queue = qentry_from_index(queue_id);
+
+ LOCK(queue);
+ status = queue->status;
+
+ if (odp_unlikely(status == QUEUE_STATUS_FREE ||
+ status == QUEUE_STATUS_DESTROYED)) {
+ UNLOCK(queue);
+ _ODP_ERR("Invalid queue status:%d\n", status);
+ return -1;
+ }
+
+ info->name = queue->name;
+ info->param = queue->param;
+
+ UNLOCK(queue);
+
+ return 0;
+}
+
+static void queue_print(odp_queue_t handle)
+{
+ odp_pktio_info_t pktio_info;
+ queue_entry_t *queue;
+ uint32_t queue_id;
+ int status, prio;
+ int max_prio = odp_schedule_max_prio();
+
+ queue_id = queue_to_index(handle);
+
+ if (odp_unlikely(queue_id >= CONFIG_MAX_QUEUES)) {
+ _ODP_ERR("Invalid queue handle: 0x%" PRIx64 "\n", odp_queue_to_u64(handle));
+ return;
+ }
+
+ queue = qentry_from_index(queue_id);
+
+ LOCK(queue);
+ status = queue->status;
+
+ if (odp_unlikely(status == QUEUE_STATUS_FREE ||
+ status == QUEUE_STATUS_DESTROYED)) {
+ UNLOCK(queue);
+ _ODP_ERR("Invalid queue status:%d\n", status);
+ return;
+ }
+ _ODP_PRINT("\nQueue info\n");
+ _ODP_PRINT("----------\n");
+ _ODP_PRINT(" handle %p\n", (void *)queue->handle);
+ _ODP_PRINT(" index %" PRIu32 "\n", queue_id);
+ _ODP_PRINT(" name %s\n", queue->name);
+ _ODP_PRINT(" enq mode %s\n",
+ queue->param.enq_mode == ODP_QUEUE_OP_MT ? "ODP_QUEUE_OP_MT" :
+ (queue->param.enq_mode == ODP_QUEUE_OP_MT_UNSAFE ? "ODP_QUEUE_OP_MT_UNSAFE" :
+ (queue->param.enq_mode == ODP_QUEUE_OP_DISABLED ? "ODP_QUEUE_OP_DISABLED" :
+ "unknown")));
+ _ODP_PRINT(" deq mode %s\n",
+ queue->param.deq_mode == ODP_QUEUE_OP_MT ? "ODP_QUEUE_OP_MT" :
+ (queue->param.deq_mode == ODP_QUEUE_OP_MT_UNSAFE ? "ODP_QUEUE_OP_MT_UNSAFE" :
+ (queue->param.deq_mode == ODP_QUEUE_OP_DISABLED ? "ODP_QUEUE_OP_DISABLED" :
+ "unknown")));
+ _ODP_PRINT(" non-blocking %s\n",
+ queue->param.nonblocking == ODP_BLOCKING ? "ODP_BLOCKING" :
+ (queue->param.nonblocking == ODP_NONBLOCKING_LF ? "ODP_NONBLOCKING_LF" :
+ (queue->param.nonblocking == ODP_NONBLOCKING_WF ? "ODP_NONBLOCKING_WF" :
+ "unknown")));
+ _ODP_PRINT(" type %s\n",
+ queue->type == ODP_QUEUE_TYPE_PLAIN ? "ODP_QUEUE_TYPE_PLAIN" :
+ (queue->type == ODP_QUEUE_TYPE_SCHED ? "ODP_QUEUE_TYPE_SCHED" : "unknown"));
+ if (queue->type == ODP_QUEUE_TYPE_SCHED) {
+ _ODP_PRINT(" sync %s\n",
+ queue->param.sched.sync == ODP_SCHED_SYNC_PARALLEL ?
+ "ODP_SCHED_SYNC_PARALLEL" :
+ (queue->param.sched.sync == ODP_SCHED_SYNC_ATOMIC ?
+ "ODP_SCHED_SYNC_ATOMIC" :
+ (queue->param.sched.sync == ODP_SCHED_SYNC_ORDERED ?
+ "ODP_SCHED_SYNC_ORDERED" : "unknown")));
+ prio = queue->param.sched.prio;
+ _ODP_PRINT(" priority %i (%i in API)\n", max_prio - prio, prio);
+ _ODP_PRINT(" group %i\n", queue->param.sched.group);
+ if (_odp_sched_id == _ODP_SCHED_ID_BASIC)
+ _ODP_PRINT(" spread %i\n", _odp_sched_basic_get_spread(queue_id));
+ }
+ if (queue->pktin.pktio != ODP_PKTIO_INVALID) {
+ if (!odp_pktio_info(queue->pktin.pktio, &pktio_info))
+ _ODP_PRINT(" pktin %s\n", pktio_info.name);
+ }
+ if (queue->pktout.pktio != ODP_PKTIO_INVALID) {
+ if (!odp_pktio_info(queue->pktout.pktio, &pktio_info))
+ _ODP_PRINT(" pktout %s\n", pktio_info.name);
+ }
+ _ODP_PRINT(" timers %" PRIu64 "\n", odp_atomic_load_u64(&queue->num_timers));
+ _ODP_PRINT(" status %s\n",
+ queue->status == QUEUE_STATUS_READY ? "ready" :
+ (queue->status == QUEUE_STATUS_NOTSCHED ? "not scheduled" :
+ (queue->status == QUEUE_STATUS_SCHED ? "scheduled" : "unknown")));
+ _ODP_PRINT(" param.size %" PRIu32 "\n", queue->param.size);
+ if (queue->queue_lf) {
+ _ODP_PRINT(" implementation queue_lf\n");
+ _ODP_PRINT(" length %" PRIu32 "/%" PRIu32 "\n",
+ _odp_queue_lf_length(queue->queue_lf), _odp_queue_lf_max_length());
+ } else if (queue->spsc) {
+ _ODP_PRINT(" implementation ring_spsc\n");
+ _ODP_PRINT(" length %" PRIu32 "/%" PRIu32 "\n",
+ ring_spsc_length(&queue->ring_spsc), queue->ring_mask + 1);
+ } else if (queue->type == ODP_QUEUE_TYPE_SCHED) {
+ _ODP_PRINT(" implementation ring_st\n");
+ _ODP_PRINT(" length %" PRIu32 "/%" PRIu32 "\n",
+ ring_st_length(&queue->ring_st), queue->ring_mask + 1);
+ } else {
+ _ODP_PRINT(" implementation ring_mpmc\n");
+ _ODP_PRINT(" length %" PRIu32 "/%" PRIu32 "\n",
+ ring_mpmc_u32_len(&queue->ring_mpmc), queue->ring_mask + 1);
+ }
+ _ODP_PRINT("\n");
+
+ UNLOCK(queue);
+}
+
+static void queue_print_all(void)
+{
+ uint32_t i, index, len, max_len;
+ const char *name;
+ int status;
+ odp_queue_type_t type;
+ odp_nonblocking_t blocking;
+ odp_queue_op_mode_t enq_mode;
+ odp_queue_op_mode_t deq_mode;
+ odp_queue_order_t order;
+ const char *status_str;
+ const char *bl_str;
+ char type_c, enq_c, deq_c, order_c, sync_c;
+ const int col_width = 24;
+ int prio = 0;
+ int spr = 0;
+ odp_schedule_sync_t sync = ODP_SCHED_SYNC_PARALLEL;
+ odp_schedule_group_t grp = ODP_SCHED_GROUP_INVALID;
+
+ _ODP_PRINT("\nList of all queues\n");
+ _ODP_PRINT("------------------\n");
+ _ODP_PRINT(" idx %-*s type stat blk enq deq ord len max_len sync prio grp", col_width, "name");
+ if (_odp_sched_id == _ODP_SCHED_ID_BASIC)
+ _ODP_PRINT(" spr\n");
+ else
+ _ODP_PRINT("\n");
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue_entry_t *queue = qentry_from_index(i);
+
+ if (queue->status < QUEUE_STATUS_READY)
+ continue;
+
+ LOCK(queue);
+
+ status = queue->status;
+ index = queue->index;
+ name = queue->name;
+ type = queue->type;
+ blocking = queue->param.nonblocking;
+ enq_mode = queue->param.enq_mode;
+ deq_mode = queue->param.deq_mode;
+ order = queue->param.order;
+
+ if (queue->queue_lf) {
+ len = _odp_queue_lf_length(queue->queue_lf);
+ max_len = _odp_queue_lf_max_length();
+ } else if (queue->spsc) {
+ len = ring_spsc_length(&queue->ring_spsc);
+ max_len = queue->ring_mask + 1;
+ } else if (type == ODP_QUEUE_TYPE_SCHED) {
+ len = ring_st_length(&queue->ring_st);
+ max_len = queue->ring_mask + 1;
+ prio = queue->param.sched.prio;
+ grp = queue->param.sched.group;
+ sync = queue->param.sched.sync;
+ if (_odp_sched_id == _ODP_SCHED_ID_BASIC)
+ spr = _odp_sched_basic_get_spread(index);
+ } else {
+ len = ring_mpmc_u32_len(&queue->ring_mpmc);
+ max_len = queue->ring_mask + 1;
+ }
+
+ UNLOCK(queue);
+
+ if (status < QUEUE_STATUS_READY)
+ continue;
+
+ status_str = (status == QUEUE_STATUS_READY) ? "R" :
+ ((status == QUEUE_STATUS_SCHED) ? "S" : "NS");
+
+ type_c = (type == ODP_QUEUE_TYPE_PLAIN) ? 'P' : 'S';
+
+ bl_str = (blocking == ODP_BLOCKING) ? "B" :
+ ((blocking == ODP_NONBLOCKING_LF) ? "LF" : "WF");
+
+ enq_c = (enq_mode == ODP_QUEUE_OP_MT) ? 'S' :
+ ((enq_mode == ODP_QUEUE_OP_MT_UNSAFE) ? 'U' : 'D');
+
+ deq_c = (deq_mode == ODP_QUEUE_OP_MT) ? 'S' :
+ ((deq_mode == ODP_QUEUE_OP_MT_UNSAFE) ? 'U' : 'D');
+
+ order_c = (order == ODP_QUEUE_ORDER_KEEP) ? 'K' : 'I';
+
+ _ODP_PRINT("%4u %-*s %c %2s %2s", index, col_width, name, type_c,
+ status_str, bl_str);
+ _ODP_PRINT(" %c %c %c %6u %6u", enq_c, deq_c, order_c, len, max_len);
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ sync_c = (sync == ODP_SCHED_SYNC_PARALLEL) ? 'P' :
+ ((sync == ODP_SCHED_SYNC_ATOMIC) ? 'A' : 'O');
+ /* Print prio level matching odp_schedule_print() output */
+ prio = odp_schedule_max_prio() - prio;
+
+ _ODP_PRINT(" %c %4i %3i", sync_c, prio, grp);
+
+ if (_odp_sched_id == _ODP_SCHED_ID_BASIC)
+ _ODP_PRINT(" %3i", spr);
+ }
+
+ _ODP_PRINT("\n");
+ }
+
+ _ODP_PRINT("\n");
+}
+
+static inline int _sched_queue_enq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ int sched = 0;
+ int ret;
+ queue_entry_t *queue;
+ int num_enq;
+ ring_st_t *ring_st;
+ uint32_t event_idx[num];
+
+ queue = qentry_from_handle(handle);
+ ring_st = &queue->ring_st;
+
+ if (_odp_sched_fn->ord_enq_multi(handle, (void **)event_hdr, num, &ret))
+ return ret;
+
+ event_index_from_hdr(event_idx, event_hdr, num);
+
+ LOCK(queue);
+
+ num_enq = ring_st_enq_multi(ring_st, queue->ring_data,
+ queue->ring_mask, event_idx, num);
+
+ if (odp_unlikely(num_enq == 0)) {
+ UNLOCK(queue);
+ return 0;
+ }
+
+ if (queue->status == QUEUE_STATUS_NOTSCHED) {
+ queue->status = QUEUE_STATUS_SCHED;
+ sched = 1;
+ }
+
+ UNLOCK(queue);
+
+ /* Add queue to scheduling */
+ if (sched && _odp_sched_fn->sched_queue(queue->index))
+ _ODP_ABORT("schedule_queue failed\n");
+
+ return num_enq;
+}
+
+int _odp_sched_queue_deq(uint32_t queue_index, odp_event_t ev[], int max_num,
+ int update_status)
+{
+ int num_deq, status;
+ ring_st_t *ring_st;
+ queue_entry_t *queue = qentry_from_index(queue_index);
+ uint32_t event_idx[max_num];
+
+ ring_st = &queue->ring_st;
+
+ LOCK(queue);
+
+ status = queue->status;
+
+ if (odp_unlikely(status < QUEUE_STATUS_READY)) {
+ /* Bad queue, or queue has been destroyed.
+ * Inform scheduler about a destroyed queue. */
+ if (queue->status == QUEUE_STATUS_DESTROYED) {
+ queue->status = QUEUE_STATUS_FREE;
+ _odp_sched_fn->destroy_queue(queue_index);
+ }
+
+ UNLOCK(queue);
+ return -1;
+ }
+
+ num_deq = ring_st_deq_multi(ring_st, queue->ring_data,
+ queue->ring_mask, event_idx, max_num);
+
+ if (num_deq == 0) {
+ /* Already empty queue */
+ if (update_status && status == QUEUE_STATUS_SCHED)
+ queue->status = QUEUE_STATUS_NOTSCHED;
+
+ UNLOCK(queue);
+
+ return 0;
+ }
+
+ UNLOCK(queue);
+
+ event_index_to_hdr((_odp_event_hdr_t **)ev, event_idx, num_deq);
+
+ return num_deq;
+}
+
+static int sched_queue_enq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ return _sched_queue_enq_multi(handle, event_hdr, num);
+}
+
+static int sched_queue_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ int ret;
+
+ ret = _sched_queue_enq_multi(handle, &event_hdr, 1);
+
+ if (ret == 1)
+ return 0;
+ else
+ return -1;
+}
+
+int _odp_sched_queue_empty(uint32_t queue_index)
+{
+ queue_entry_t *queue = qentry_from_index(queue_index);
+ int ret = 0;
+
+ LOCK(queue);
+
+ if (odp_unlikely(queue->status < QUEUE_STATUS_READY)) {
+ /* Bad queue, or queue has been destroyed. */
+ UNLOCK(queue);
+ return -1;
+ }
+
+ if (ring_st_is_empty(&queue->ring_st)) {
+ /* Already empty queue. Update status. */
+ if (queue->status == QUEUE_STATUS_SCHED)
+ queue->status = QUEUE_STATUS_NOTSCHED;
+
+ ret = 1;
+ }
+
+ UNLOCK(queue);
+
+ return ret;
+}
+
+static int queue_init(queue_entry_t *queue, const char *name,
+ const odp_queue_param_t *param)
+{
+ uint64_t offset;
+ uint32_t queue_size;
+ odp_queue_type_t queue_type;
+ int spsc;
+
+ queue_type = param->type;
+
+ if (name == NULL)
+ queue->name[0] = 0;
+ else
+ _odp_strcpy(queue->name, name, ODP_QUEUE_NAME_LEN);
+ memcpy(&queue->param, param, sizeof(odp_queue_param_t));
+ if (queue->param.sched.lock_count > _odp_sched_fn->max_ordered_locks())
+ return -1;
+
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ queue->param.deq_mode = ODP_QUEUE_OP_DISABLED;
+
+ queue->type = queue_type;
+ odp_atomic_init_u64(&queue->num_timers, 0);
+
+ queue->pktin = PKTIN_INVALID;
+ queue->pktout = PKTOUT_INVALID;
+
+ queue_size = param->size;
+ if (queue_size == 0)
+ queue_size = _odp_queue_glb->config.default_queue_size;
+
+ if (queue_size < MIN_QUEUE_SIZE)
+ queue_size = MIN_QUEUE_SIZE;
+
+ /* Round up if not already a power of two */
+ queue_size = _ODP_ROUNDUP_POWER2_U32(queue_size);
+
+ if (queue_size > _odp_queue_glb->config.max_queue_size) {
+ _ODP_ERR("Too large queue size %u\n", queue_size);
+ return -1;
+ }
+
+ offset = queue->index * (uint64_t)_odp_queue_glb->config.max_queue_size;
+
+ /* Single-producer / single-consumer plain queue has simple and
+ * lock-free implementation */
+ spsc = (queue_type == ODP_QUEUE_TYPE_PLAIN) &&
+ (param->enq_mode == ODP_QUEUE_OP_MT_UNSAFE) &&
+ (param->deq_mode == ODP_QUEUE_OP_MT_UNSAFE);
+
+ queue->spsc = spsc;
+ queue->queue_lf = NULL;
+
+ /* Default to error functions */
+ queue->enqueue = error_enqueue;
+ queue->enqueue_multi = error_enqueue_multi;
+ queue->dequeue = error_dequeue;
+ queue->dequeue_multi = error_dequeue_multi;
+ queue->orig_dequeue_multi = error_dequeue_multi;
+
+ if (spsc) {
+ _odp_queue_spsc_init(queue, queue_size);
+ } else {
+ if (queue_type == ODP_QUEUE_TYPE_PLAIN) {
+ queue->enqueue = plain_queue_enq;
+ queue->enqueue_multi = plain_queue_enq_multi;
+ queue->dequeue = plain_queue_deq;
+ queue->dequeue_multi = plain_queue_deq_multi;
+ queue->orig_dequeue_multi = plain_queue_deq_multi;
+
+ queue->ring_data = &_odp_queue_glb->ring_data[offset];
+ queue->ring_mask = queue_size - 1;
+ ring_mpmc_u32_init(&queue->ring_mpmc);
+
+ } else {
+ queue->enqueue = sched_queue_enq;
+ queue->enqueue_multi = sched_queue_enq_multi;
+
+ queue->ring_data = &_odp_queue_glb->ring_data[offset];
+ queue->ring_mask = queue_size - 1;
+ ring_st_init(&queue->ring_st);
+ }
+ }
+
+ return 0;
+}
+
+static uint64_t queue_to_u64(odp_queue_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+static odp_pktout_queue_t queue_get_pktout(odp_queue_t handle)
+{
+ queue_entry_t *qentry = qentry_from_handle(handle);
+
+ return qentry->pktout;
+}
+
+static void queue_set_pktout(odp_queue_t handle, odp_pktio_t pktio, int index)
+{
+ queue_entry_t *qentry = qentry_from_handle(handle);
+
+ qentry->pktout.pktio = pktio;
+ qentry->pktout.index = index;
+}
+
+static odp_pktin_queue_t queue_get_pktin(odp_queue_t handle)
+{
+ queue_entry_t *qentry = qentry_from_handle(handle);
+
+ return qentry->pktin;
+}
+
+static void queue_set_pktin(odp_queue_t handle, odp_pktio_t pktio, int index)
+{
+ queue_entry_t *qentry = qentry_from_handle(handle);
+
+ qentry->pktin.pktio = pktio;
+ qentry->pktin.index = index;
+}
+
+static void queue_set_enq_deq_func(odp_queue_t handle,
+ queue_enq_fn_t enq,
+ queue_enq_multi_fn_t enq_multi,
+ queue_deq_fn_t deq,
+ queue_deq_multi_fn_t deq_multi)
+{
+ queue_entry_t *qentry = qentry_from_handle(handle);
+
+ if (enq)
+ qentry->enqueue = enq;
+
+ if (enq_multi)
+ qentry->enqueue_multi = enq_multi;
+
+ if (deq)
+ qentry->dequeue = deq;
+
+ if (deq_multi)
+ qentry->dequeue_multi = deq_multi;
+}
+
+static int queue_orig_multi(odp_queue_t handle,
+ _odp_event_hdr_t **event_hdr, int num)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ return queue->orig_dequeue_multi(handle, event_hdr, num);
+}
+
+static int queue_api_enq_multi(odp_queue_t handle,
+ const odp_event_t ev[], int num)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ if (odp_unlikely(num == 0))
+ return 0;
+
+ if (num > QUEUE_MULTI_MAX)
+ num = QUEUE_MULTI_MAX;
+
+ return queue->enqueue_multi(handle,
+ (_odp_event_hdr_t **)(uintptr_t)ev, num);
+}
+
+static void queue_timer_add(odp_queue_t handle)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ odp_atomic_inc_u64(&queue->num_timers);
+}
+
+static void queue_timer_rem(odp_queue_t handle)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ odp_atomic_dec_u64(&queue->num_timers);
+}
+
+static int queue_api_enq(odp_queue_t handle, odp_event_t ev)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+
+ return queue->enqueue(handle,
+ (_odp_event_hdr_t *)(uintptr_t)ev);
+}
+
+static int queue_api_deq_multi(odp_queue_t handle, odp_event_t ev[], int num)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+ int ret;
+
+ if (num > QUEUE_MULTI_MAX)
+ num = QUEUE_MULTI_MAX;
+
+ ret = queue->dequeue_multi(handle, (_odp_event_hdr_t **)ev, num);
+
+ if (odp_global_rw->inline_timers &&
+ odp_atomic_load_u64(&queue->num_timers))
+ timer_run(ret ? 2 : 1);
+
+ return ret;
+}
+
+static odp_event_t queue_api_deq(odp_queue_t handle)
+{
+ queue_entry_t *queue = qentry_from_handle(handle);
+ odp_event_t ev = (odp_event_t)queue->dequeue(handle);
+
+ if (odp_global_rw->inline_timers &&
+ odp_atomic_load_u64(&queue->num_timers))
+ timer_run(ev != ODP_EVENT_INVALID ? 2 : 1);
+
+ return ev;
+}
+
+/* API functions */
+_odp_queue_api_fn_t _odp_queue_basic_api = {
+ .queue_create = queue_create,
+ .queue_create_multi = queue_create_multi,
+ .queue_destroy = queue_destroy,
+ .queue_destroy_multi = queue_destroy_multi,
+ .queue_lookup = queue_lookup,
+ .queue_capability = queue_capability,
+ .queue_context_set = queue_context_set,
+ .queue_enq = queue_api_enq,
+ .queue_enq_multi = queue_api_enq_multi,
+ .queue_deq = queue_api_deq,
+ .queue_deq_multi = queue_api_deq_multi,
+ .queue_type = queue_type,
+ .queue_sched_type = queue_sched_type,
+ .queue_sched_prio = queue_sched_prio,
+ .queue_sched_group = queue_sched_group,
+ .queue_lock_count = queue_lock_count,
+ .queue_to_u64 = queue_to_u64,
+ .queue_param_init = queue_param_init,
+ .queue_info = queue_info,
+ .queue_print = queue_print,
+ .queue_print_all = queue_print_all
+
+};
+
+/* Functions towards internal components */
+queue_fn_t _odp_queue_basic_fn = {
+ .init_global = queue_init_global,
+ .term_global = queue_term_global,
+ .init_local = queue_init_local,
+ .term_local = queue_term_local,
+ .get_pktout = queue_get_pktout,
+ .set_pktout = queue_set_pktout,
+ .get_pktin = queue_get_pktin,
+ .set_pktin = queue_set_pktin,
+ .set_enq_deq_fn = queue_set_enq_deq_func,
+ .orig_deq_multi = queue_orig_multi,
+ .timer_add = queue_timer_add,
+ .timer_rem = queue_timer_rem
+};
diff --git a/platform/linux-generic/odp_queue_if.c b/platform/linux-generic/odp_queue_if.c
index c91f00eb2..248e7743a 100644
--- a/platform/linux-generic/odp_queue_if.c
+++ b/platform/linux-generic/odp_queue_if.c
@@ -1,103 +1,144 @@
-/* Copyright (c) 2017, ARM Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2023 Nokia
*/
+#include <odp/autoheader_internal.h>
+
#include <odp_queue_if.h>
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp/api/align.h>
+#include <odp/api/plat/queue_inline_types.h>
+
+#include <odp/visibility_begin.h>
-extern const queue_api_t queue_default_api;
-extern const queue_fn_t queue_default_fn;
+_odp_queue_inline_offset_t _odp_queue_inline_offset ODP_ALIGNED_CACHE;
+const _odp_queue_api_fn_t *_odp_queue_api;
-const queue_api_t *queue_api = &queue_default_api;
-const queue_fn_t *queue_fn = &queue_default_fn;
+#include <odp/visibility_end.h>
+
+extern const _odp_queue_api_fn_t _odp_queue_scalable_api;
+extern const queue_fn_t _odp_queue_scalable_fn;
+
+extern const _odp_queue_api_fn_t _odp_queue_basic_api;
+extern const queue_fn_t _odp_queue_basic_fn;
+
+const queue_fn_t *_odp_queue_fn;
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
{
- return queue_api->queue_create(name, param);
+ return _odp_queue_api->queue_create(name, param);
+}
+
+int odp_queue_create_multi(const char *name[], const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[], int num)
+{
+ return _odp_queue_api->queue_create_multi(name, param, share_param,
+ queue, num);
}
int odp_queue_destroy(odp_queue_t queue)
{
- return queue_api->queue_destroy(queue);
+ return _odp_queue_api->queue_destroy(queue);
+}
+
+int odp_queue_destroy_multi(odp_queue_t queue[], int num)
+{
+ return _odp_queue_api->queue_destroy_multi(queue, num);
}
odp_queue_t odp_queue_lookup(const char *name)
{
- return queue_api->queue_lookup(name);
+ return _odp_queue_api->queue_lookup(name);
}
int odp_queue_capability(odp_queue_capability_t *capa)
{
- return queue_api->queue_capability(capa);
+ return _odp_queue_api->queue_capability(capa);
}
int odp_queue_context_set(odp_queue_t queue, void *context, uint32_t len)
{
- return queue_api->queue_context_set(queue, context, len);
+ return _odp_queue_api->queue_context_set(queue, context, len);
}
-void *odp_queue_context(odp_queue_t queue)
+odp_queue_type_t odp_queue_type(odp_queue_t queue)
{
- return queue_api->queue_context(queue);
+ return _odp_queue_api->queue_type(queue);
}
-int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
+odp_schedule_sync_t odp_queue_sched_type(odp_queue_t queue)
{
- return queue_api->queue_enq(queue, ev);
+ return _odp_queue_api->queue_sched_type(queue);
}
-int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
+odp_schedule_prio_t odp_queue_sched_prio(odp_queue_t queue)
{
- return queue_api->queue_enq_multi(queue, events, num);
+ return _odp_queue_api->queue_sched_prio(queue);
}
-odp_event_t odp_queue_deq(odp_queue_t queue)
+odp_schedule_group_t odp_queue_sched_group(odp_queue_t queue)
{
- return queue_api->queue_deq(queue);
+ return _odp_queue_api->queue_sched_group(queue);
}
-int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
+uint32_t odp_queue_lock_count(odp_queue_t queue)
{
- return queue_api->queue_deq_multi(queue, events, num);
+ return _odp_queue_api->queue_lock_count(queue);
}
-odp_queue_type_t odp_queue_type(odp_queue_t queue)
+uint64_t odp_queue_to_u64(odp_queue_t hdl)
{
- return queue_api->queue_type(queue);
+ return _odp_queue_api->queue_to_u64(hdl);
}
-odp_schedule_sync_t odp_queue_sched_type(odp_queue_t queue)
+void odp_queue_param_init(odp_queue_param_t *param)
{
- return queue_api->queue_sched_type(queue);
+ _odp_queue_api->queue_param_init(param);
}
-odp_schedule_prio_t odp_queue_sched_prio(odp_queue_t queue)
+int odp_queue_info(odp_queue_t queue, odp_queue_info_t *info)
{
- return queue_api->queue_sched_prio(queue);
+ return _odp_queue_api->queue_info(queue, info);
}
-odp_schedule_group_t odp_queue_sched_group(odp_queue_t queue)
+void odp_queue_print(odp_queue_t queue)
{
- return queue_api->queue_sched_group(queue);
+ _odp_queue_api->queue_print(queue);
}
-int odp_queue_lock_count(odp_queue_t queue)
+void odp_queue_print_all(void)
{
- return queue_api->queue_lock_count(queue);
+ _odp_queue_api->queue_print_all();
}
-uint64_t odp_queue_to_u64(odp_queue_t hdl)
+int _odp_queue_init_global(void)
{
- return queue_api->queue_to_u64(hdl);
-}
+ const char *sched = getenv("ODP_SCHEDULER");
-void odp_queue_param_init(odp_queue_param_t *param)
-{
- return queue_api->queue_param_init(param);
+ if (sched == NULL || !strcmp(sched, "default"))
+ sched = _ODP_SCHEDULE_DEFAULT;
+
+ if (!strcmp(sched, "basic") || !strcmp(sched, "sp")) {
+ _odp_queue_fn = &_odp_queue_basic_fn;
+ _odp_queue_api = &_odp_queue_basic_api;
+ } else if (!strcmp(sched, "scalable")) {
+ _odp_queue_fn = &_odp_queue_scalable_fn;
+ _odp_queue_api = &_odp_queue_scalable_api;
+ } else {
+ _ODP_ABORT("Unknown scheduler specified via ODP_SCHEDULER\n");
+ return -1;
+ }
+
+ return _odp_queue_fn->init_global();
}
-int odp_queue_info(odp_queue_t queue, odp_queue_info_t *info)
+int _odp_queue_term_global(void)
{
- return queue_api->queue_info(queue, info);
+ return _odp_queue_fn->term_global();
}
diff --git a/platform/linux-generic/odp_queue_lf.c b/platform/linux-generic/odp_queue_lf.c
new file mode 100644
index 000000000..e7f8fa435
--- /dev/null
+++ b/platform/linux-generic/odp_queue_lf.c
@@ -0,0 +1,368 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/queue.h>
+#include <odp/api/atomic.h>
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_queue_basic_internal.h>
+#include <odp_types_internal.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#define RING_LF_SIZE 32
+#define QUEUE_LF_NUM 128
+#define ENQ_RETRIES (RING_LF_SIZE / 4)
+#define DEQ_RETRIES (RING_LF_SIZE / 8)
+
+#ifdef __SIZEOF_INT128__
+
+static inline void lockfree_zero_u128(_odp_u128_t *atomic)
+{
+ __atomic_store_n(atomic, 0, __ATOMIC_RELAXED);
+}
+
+#include <odp_cpu.h>
+
+#else
+
+/* These definitions enable build in non 128 bit compatible systems.
+ * Implementation is active only when 128 bit lockfree atomics are available.
+ * So, these are never actually used. */
+typedef struct ODP_ALIGNED(16) {
+ uint64_t u64[2];
+} _odp_u128_t;
+
+static inline _odp_u128_t lockfree_load_u128(_odp_u128_t *atomic)
+{
+ return *atomic;
+}
+
+static inline void lockfree_zero_u128(_odp_u128_t *atomic)
+{
+ atomic->u64[0] = 0;
+ atomic->u64[1] = 0;
+}
+
+static inline int lockfree_cas_acq_rel_u128(_odp_u128_t *atomic, _odp_u128_t old_val,
+ _odp_u128_t new_val)
+{
+ if (atomic->u64[0] == old_val.u64[0] &&
+ atomic->u64[1] == old_val.u64[1]) {
+ atomic->u64[0] = new_val.u64[0];
+ atomic->u64[1] = new_val.u64[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int lockfree_check_u128(void)
+{
+ return 0;
+}
+
+#endif
+
+/* Node in lock-free ring */
+typedef union {
+ _odp_u128_t u128;
+
+ struct {
+ /* Data with lowest counter value is the head. Empty node has
+ * counter value 0. */
+ uint64_t counter;
+
+ /* Data pointer */
+ uint64_t ptr;
+ } s;
+
+} ring_lf_node_t;
+
+/* Lock-free ring */
+typedef struct ODP_ALIGNED_CACHE {
+ ring_lf_node_t node[RING_LF_SIZE];
+ int used;
+ odp_atomic_u64_t enq_counter;
+
+} queue_lf_t;
+
+/* Lock-free queue globals */
+typedef struct ODP_ALIGNED_CACHE {
+ queue_lf_t queue_lf[QUEUE_LF_NUM];
+ odp_shm_t shm;
+
+} queue_lf_global_t;
+
+static queue_lf_global_t *queue_lf_glb;
+
+static inline int next_idx(int idx)
+{
+ int next = idx + 1;
+
+ if (next == RING_LF_SIZE)
+ next = 0;
+
+ return next;
+}
+
+static int queue_lf_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ queue_entry_t *queue;
+ queue_lf_t *queue_lf;
+ int i, j, idx;
+ int found;
+ ring_lf_node_t node_val;
+ ring_lf_node_t new_val;
+ ring_lf_node_t *node;
+
+ queue = qentry_from_handle(handle);
+ queue_lf = queue->queue_lf;
+
+ new_val.s.ptr = (uintptr_t)event_hdr;
+ new_val.s.counter = odp_atomic_fetch_inc_u64(&queue_lf->enq_counter);
+
+ idx = 0;
+
+ for (j = 0; j < ENQ_RETRIES; j++) {
+ found = 0;
+
+ /* Find empty node */
+ for (i = 0; i < RING_LF_SIZE; i++) {
+ node = &queue_lf->node[idx];
+ idx = next_idx(idx);
+
+ node_val.u128 = lockfree_load_u128(&node->u128);
+
+ if (node_val.s.counter == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* Queue is full */
+ if (found == 0)
+ return -1;
+
+ /* Try to insert data */
+ if (lockfree_cas_acq_rel_u128(&node->u128, node_val.u128,
+ new_val.u128))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int queue_lf_enq_multi(odp_queue_t handle, _odp_event_hdr_t **event_hdr,
+ int num)
+{
+ (void)num;
+
+ if (queue_lf_enq(handle, event_hdr[0]) == 0)
+ return 1;
+
+ return 0;
+}
+
+static _odp_event_hdr_t *queue_lf_deq(odp_queue_t handle)
+{
+ queue_entry_t *queue;
+ queue_lf_t *queue_lf;
+ int i, j, i_lowest = 0;
+ int found;
+ ring_lf_node_t node_val, old_val, new_val;
+ ring_lf_node_t *node, *old;
+ uint64_t lowest, counter;
+ _odp_event_hdr_t *event_hdr;
+
+ queue = qentry_from_handle(handle);
+ queue_lf = queue->queue_lf;
+ new_val.s.counter = 0;
+ new_val.s.ptr = 0;
+ old = NULL;
+
+ for (j = 0; j < DEQ_RETRIES; j++) {
+ found = 0;
+ lowest = -1;
+
+ /* Find the head node. The one with data and
+ * the lowest counter. */
+ for (i = 0; i < RING_LF_SIZE; i++) {
+ node = &queue_lf->node[i];
+ node_val.u128 = lockfree_load_u128(&node->u128);
+ counter = node_val.s.counter;
+
+ if (counter && counter < lowest) {
+ old = node;
+ old_val.u128 = node_val.u128;
+ lowest = counter;
+ i_lowest = i;
+ found = 1;
+ }
+ }
+
+ /* Queue is empty */
+ if (found == 0)
+ return NULL;
+
+ /* New data may have been written to the area we searched before
+ * finding the current lowest. Check that there are no lower
+ * values. */
+ for (i = 0; i < i_lowest; i++) {
+ node = &queue_lf->node[i];
+ node_val.u128 = lockfree_load_u128(&node->u128);
+ counter = node_val.s.counter;
+
+ if (counter && counter < lowest) {
+ old = node;
+ old_val.u128 = node_val.u128;
+ lowest = counter;
+ }
+ }
+
+ event_hdr = (void *)(uintptr_t)old_val.s.ptr;
+
+ /* Try to remove data */
+ if (lockfree_cas_acq_rel_u128(&old->u128, old_val.u128,
+ new_val.u128))
+ return event_hdr;
+ }
+
+ return NULL;
+}
+
+static int queue_lf_deq_multi(odp_queue_t handle, _odp_event_hdr_t **event_hdr,
+ int num)
+{
+ _odp_event_hdr_t *buf;
+
+ (void)num;
+
+ buf = queue_lf_deq(handle);
+
+ if (buf == NULL)
+ return 0;
+
+ event_hdr[0] = buf;
+ return 1;
+}
+
+uint32_t _odp_queue_lf_init_global(uint32_t *queue_lf_size,
+ queue_lf_func_t *lf_func)
+{
+ odp_shm_t shm;
+ int lockfree;
+
+ /* 16 byte lockfree CAS operation is needed. */
+ lockfree = lockfree_check_u128();
+
+ _ODP_DBG("\nLock-free queue init\n");
+ _ODP_DBG(" u128 lock-free: %i\n\n", lockfree);
+
+ if (!lockfree)
+ return 0;
+
+ shm = odp_shm_reserve("_odp_queues_lf_global", sizeof(queue_lf_global_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (shm == ODP_SHM_INVALID)
+ return 0;
+
+ queue_lf_glb = odp_shm_addr(shm);
+ memset(queue_lf_glb, 0, sizeof(queue_lf_global_t));
+
+ queue_lf_glb->shm = shm;
+
+ memset(lf_func, 0, sizeof(queue_lf_func_t));
+ lf_func->enq = queue_lf_enq;
+ lf_func->enq_multi = queue_lf_enq_multi;
+ lf_func->deq = queue_lf_deq;
+ lf_func->deq_multi = queue_lf_deq_multi;
+
+ *queue_lf_size = RING_LF_SIZE;
+
+ return QUEUE_LF_NUM;
+}
+
+void _odp_queue_lf_term_global(void)
+{
+ odp_shm_t shm;
+
+ if (queue_lf_glb == NULL)
+ return;
+
+ shm = queue_lf_glb->shm;
+
+ if (odp_shm_free(shm) < 0)
+ _ODP_ERR("shm free failed");
+}
+
+static void init_queue(queue_lf_t *queue_lf)
+{
+ int i;
+
+ odp_atomic_init_u64(&queue_lf->enq_counter, 1);
+
+ for (i = 0; i < RING_LF_SIZE; i++)
+ lockfree_zero_u128(&queue_lf->node[i].u128);
+}
+
+void *_odp_queue_lf_create(queue_entry_t *queue)
+{
+ int i;
+ queue_lf_t *queue_lf = NULL;
+
+ if (queue_lf_glb == NULL) {
+ _ODP_ERR("No lock-free queues available\n");
+ return NULL;
+ }
+
+ if (queue->type != ODP_QUEUE_TYPE_PLAIN)
+ return NULL;
+
+ for (i = 0; i < QUEUE_LF_NUM; i++) {
+ if (queue_lf_glb->queue_lf[i].used == 0) {
+ queue_lf = &queue_lf_glb->queue_lf[i];
+ memset(queue_lf, 0, sizeof(queue_lf_t));
+ init_queue(queue_lf);
+ queue_lf->used = 1;
+ break;
+ }
+ }
+
+ return queue_lf;
+}
+
+void _odp_queue_lf_destroy(void *queue_lf_ptr)
+{
+ queue_lf_t *queue_lf = queue_lf_ptr;
+
+ queue_lf->used = 0;
+}
+
+uint32_t _odp_queue_lf_length(void *queue_lf_ptr)
+{
+ queue_lf_t *queue_lf = queue_lf_ptr;
+ ring_lf_node_t node_val;
+ uint32_t i;
+ uint32_t num = 0;
+
+ for (i = 0; i < RING_LF_SIZE; i++) {
+ node_val.u128 = lockfree_load_u128(&queue_lf->node[i].u128);
+ if (node_val.s.counter)
+ num++;
+ }
+ return num;
+}
+
+uint32_t _odp_queue_lf_max_length(void)
+{
+ return RING_LF_SIZE;
+}
+
diff --git a/platform/linux-generic/odp_queue_scalable.c b/platform/linux-generic/odp_queue_scalable.c
new file mode 100644
index 000000000..f2eb82d23
--- /dev/null
+++ b/platform/linux-generic/odp_queue_scalable.c
@@ -0,0 +1,1199 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/hints.h>
+#include <odp/api/ticketlock.h>
+#include <odp/api/plat/ticketlock_inlines.h>
+#include <odp/api/queue.h>
+#include <odp/api/schedule.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/sync.h>
+#include <odp/api/plat/sync_inlines.h>
+#include <odp/api/traffic_mngr.h>
+#include <odp/api/cpu.h>
+
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_queue_scalable_internal.h>
+#include <odp_schedule_if.h>
+#include <odp_timer_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_ishmpool_internal.h>
+#include <odp/api/plat/queue_inline_types.h>
+#include <odp_global_data.h>
+#include <odp_macros_internal.h>
+#include <odp_string_internal.h>
+
+#include <string.h>
+#include <inttypes.h>
+
+#define LOCK(a) odp_ticketlock_lock(a)
+#define UNLOCK(a) odp_ticketlock_unlock(a)
+#define LOCK_INIT(a) odp_ticketlock_init(a)
+
+extern __thread sched_scalable_thread_state_t *_odp_sched_ts;
+extern _odp_queue_inline_offset_t _odp_queue_inline_offset;
+
+typedef struct queue_table_t {
+ queue_entry_t queue[CONFIG_MAX_QUEUES];
+} queue_table_t;
+
+static queue_table_t *queue_tbl;
+static _odp_ishm_pool_t *queue_shm_pool;
+
+static int _queue_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr);
+static _odp_event_hdr_t *_queue_deq(odp_queue_t handle);
+static int _queue_enq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num);
+static int _queue_deq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num);
+
+static queue_entry_t *get_qentry(uint32_t queue_id)
+{
+ return &queue_tbl->queue[queue_id];
+}
+
+queue_entry_t *_odp_qentry_from_ext(odp_queue_t handle)
+{
+ return (queue_entry_t *)(uintptr_t)handle;
+}
+
+static int _odp_queue_disable_enq(sched_elem_t *q)
+{
+ ringidx_t old_read, old_write, new_write;
+ uint32_t size;
+
+ old_write = q->prod_write;
+ size = q->prod_mask + 1;
+ do {
+ /* Need __atomic_load to avoid compiler reordering */
+ old_read = __atomic_load_n(&q->prod_read, __ATOMIC_ACQUIRE);
+ if (old_write != old_read) {
+ /* Queue is not empty, cannot claim all elements
+ * Cannot disable enqueue.
+ */
+ return -1;
+ }
+ /* Claim all elements in ring */
+ new_write = old_write + size;
+ } while (!__atomic_compare_exchange_n(&q->prod_write,
+ &old_write, /* Updated on failure */
+ new_write,
+ true,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED));
+ /* All remaining elements claimed, no one else can enqueue */
+ return 0;
+}
+
+static int queue_init(queue_entry_t *queue, const char *name,
+ const odp_queue_param_t *param)
+{
+ ringidx_t ring_idx;
+ sched_elem_t *sched_elem;
+ uint32_t ring_size;
+ _odp_event_hdr_t **ring;
+ uint32_t size;
+
+ sched_elem = &queue->sched_elem;
+ ring_size = param->size > 0 ?
+ _ODP_ROUNDUP_POWER2_U32(param->size) : CONFIG_SCAL_QUEUE_SIZE;
+ _odp_strcpy(queue->name, name ? name : "", ODP_QUEUE_NAME_LEN);
+ memcpy(&queue->param, param, sizeof(odp_queue_param_t));
+
+ size = ring_size * sizeof(_odp_event_hdr_t *);
+ ring = (_odp_event_hdr_t **)shm_pool_alloc_align(queue_shm_pool, size);
+ if (NULL == ring)
+ return -1;
+
+ for (ring_idx = 0; ring_idx < ring_size; ring_idx++)
+ ring[ring_idx] = NULL;
+
+ queue->type = queue->param.type;
+
+ if (queue->type == ODP_QUEUE_TYPE_SCHED)
+ queue->param.deq_mode = ODP_QUEUE_OP_DISABLED;
+
+ odp_atomic_init_u64(&queue->num_timers, 0);
+
+ queue->enqueue = _queue_enq;
+ queue->dequeue = _queue_deq;
+ queue->enqueue_multi = _queue_enq_multi;
+ queue->dequeue_multi = _queue_deq_multi;
+ queue->orig_dequeue_multi = _queue_deq_multi;
+ queue->pktin = PKTIN_INVALID;
+
+ sched_elem->node.next = NULL;
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK_INIT(&sched_elem->qschlock);
+#endif
+ sched_elem->qschst.numevts = 0;
+ sched_elem->qschst.wrr_budget = CONFIG_WRR_WEIGHT;
+ sched_elem->qschst.cur_ticket = 0;
+ sched_elem->qschst.nxt_ticket = 0;
+ sched_elem->pop_deficit = 0;
+ if (queue->type == ODP_QUEUE_TYPE_SCHED)
+ sched_elem->qschst_type = queue->param.sched.sync;
+ else
+ sched_elem->qschst_type = ODP_NO_SCHED_QUEUE;
+ /* 2nd cache line - enqueue */
+ sched_elem->prod_read = 0;
+ sched_elem->prod_write = 0;
+ sched_elem->prod_ring = ring;
+ sched_elem->prod_mask = ring_size - 1;
+ /* 3rd cache line - dequeue */
+ sched_elem->cons_read = 0;
+ sched_elem->cons_write = 0;
+ sched_elem->rwin = NULL;
+ sched_elem->schedq = NULL;
+ sched_elem->user_ctx = queue->param.context;
+#ifdef CONFIG_SPLIT_PRODCONS
+ sched_elem->cons_ring = ring;
+ sched_elem->cons_mask = ring_size - 1;
+ sched_elem->cons_type = sched_elem->qschst_type;
+#endif
+
+ /* Queue initialized successfully, add it to the sched group */
+ if (queue->type == ODP_QUEUE_TYPE_SCHED) {
+ int prio = odp_schedule_max_prio() - param->sched.prio;
+
+ if (queue->param.sched.sync == ODP_SCHED_SYNC_ORDERED) {
+ sched_elem->rwin =
+ _odp_rwin_alloc(queue_shm_pool,
+ queue->param.sched.lock_count);
+ if (sched_elem->rwin == NULL) {
+ _ODP_ERR("Reorder window not created\n");
+ goto rwin_create_failed;
+ }
+ }
+ sched_elem->sched_grp = param->sched.group;
+ sched_elem->sched_prio = prio;
+ sched_elem->schedq =
+ _odp_sched_queue_add(param->sched.group, prio);
+ _ODP_ASSERT(sched_elem->schedq != NULL);
+ }
+
+ return 0;
+
+rwin_create_failed:
+ _odp_ishm_pool_free(queue_shm_pool, ring);
+
+ return -1;
+}
+
+static int queue_init_global(void)
+{
+ uint32_t i;
+ uint64_t pool_size;
+ uint64_t min_alloc;
+ uint64_t max_alloc;
+
+ _ODP_DBG("Queue init ... ");
+
+ /* Fill in queue entry field offsets for inline functions */
+ memset(&_odp_queue_inline_offset, 0,
+ sizeof(_odp_queue_inline_offset_t));
+ _odp_queue_inline_offset.context = offsetof(queue_entry_t,
+ param.context);
+
+ /* Create shared memory pool to allocate shared memory for the
+ * queues. Use the default queue size.
+ */
+ /* Add size of the array holding the queues */
+ pool_size = sizeof(queue_table_t);
+ /* Add storage required for queues */
+ pool_size += (CONFIG_SCAL_QUEUE_SIZE *
+ sizeof(_odp_event_hdr_t *)) * CONFIG_MAX_QUEUES;
+
+ /* Add the reorder window size */
+ pool_size += sizeof(reorder_window_t) * CONFIG_MAX_QUEUES;
+ /* Choose min_alloc and max_alloc such that buddy allocator is selected. */
+ min_alloc = 0;
+ max_alloc = CONFIG_SCAL_QUEUE_SIZE * sizeof(_odp_event_hdr_t *);
+ queue_shm_pool = _odp_ishm_pool_create("queue_shm_pool",
+ pool_size,
+ min_alloc, max_alloc, 0);
+ if (queue_shm_pool == NULL) {
+ _ODP_ERR("Failed to allocate shared memory pool for"
+ " queues\n");
+ goto queue_shm_pool_create_failed;
+ }
+
+ queue_tbl = (queue_table_t *)
+ shm_pool_alloc_align(queue_shm_pool,
+ sizeof(queue_table_t));
+ if (queue_tbl == NULL) {
+ _ODP_ERR("Failed to reserve shared memory for queue table\n");
+ goto queue_tbl_ishm_alloc_failed;
+ }
+
+ memset(queue_tbl, 0, sizeof(queue_table_t));
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ /* init locks */
+ queue_entry_t *queue;
+
+ queue = get_qentry(i);
+ LOCK_INIT(&queue->lock);
+ queue->index = i;
+ queue->handle = (odp_queue_t)queue;
+ }
+
+ _ODP_DBG("done\n");
+ _ODP_DBG("Queue init global\n");
+ _ODP_DBG(" struct queue_entry_s size %zu\n", sizeof(struct queue_entry_s));
+ _ODP_DBG(" queue_entry_t size %zu\n", sizeof(queue_entry_t));
+ _ODP_DBG("\n");
+
+ return 0;
+
+queue_shm_pool_create_failed:
+
+queue_tbl_ishm_alloc_failed:
+ _odp_ishm_pool_destroy(queue_shm_pool);
+
+ return -1;
+}
+
+static int queue_term_global(void)
+{
+ int ret = 0;
+ int rc = 0;
+ queue_entry_t *queue;
+ int i;
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue = &queue_tbl->queue[i];
+ if (__atomic_load_n(&queue->status,
+ __ATOMIC_RELAXED) != QUEUE_STATUS_FREE) {
+ _ODP_ERR("Not destroyed queue: %s\n", queue->name);
+ rc = -1;
+ }
+ }
+
+ _odp_ishm_pool_free(queue_shm_pool, queue_tbl);
+
+ ret = _odp_ishm_pool_destroy(queue_shm_pool);
+ if (ret < 0) {
+ _ODP_ERR("Failed to destroy shared memory pool for queues\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int queue_init_local(void)
+{
+ return 0;
+}
+
+static int queue_term_local(void)
+{
+ return 0;
+}
+
+static int queue_capability(odp_queue_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_queue_capability_t));
+
+ /* Reserve some queues for internal use */
+ capa->max_queues = CONFIG_MAX_QUEUES - CONFIG_INTERNAL_QUEUES;
+
+ capa->plain.max_num = CONFIG_MAX_PLAIN_QUEUES;
+ capa->plain.max_size = 0;
+
+ return 0;
+}
+
+static odp_queue_type_t queue_type(odp_queue_t handle)
+{
+ return _odp_qentry_from_ext(handle)->type;
+}
+
+static odp_schedule_sync_t queue_sched_type(odp_queue_t handle)
+{
+ return _odp_qentry_from_ext(handle)->param.sched.sync;
+}
+
+static odp_schedule_prio_t queue_sched_prio(odp_queue_t handle)
+{
+ return _odp_qentry_from_ext(handle)->param.sched.prio;
+}
+
+static odp_schedule_group_t queue_sched_group(odp_queue_t handle)
+{
+ return _odp_qentry_from_ext(handle)->param.sched.group;
+}
+
+static uint32_t queue_lock_count(odp_queue_t handle)
+{
+ queue_entry_t *queue = _odp_qentry_from_ext(handle);
+
+ return queue->param.sched.sync == ODP_SCHED_SYNC_ORDERED ?
+ queue->param.sched.lock_count : 0;
+}
+
+static odp_queue_t queue_create(const char *name,
+ const odp_queue_param_t *param)
+{
+ int queue_idx;
+ int max_idx;
+ queue_entry_t *queue;
+ odp_queue_type_t type;
+ odp_queue_param_t default_param;
+ odp_queue_t handle = ODP_QUEUE_INVALID;
+
+ if (param == NULL) {
+ odp_queue_param_init(&default_param);
+ param = &default_param;
+ }
+
+ type = param->type;
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ if (param->sched.prio < odp_schedule_min_prio() ||
+ param->sched.prio > odp_schedule_max_prio()) {
+ _ODP_ERR("Bad queue priority: %i\n", param->sched.prio);
+ return ODP_QUEUE_INVALID;
+ }
+ }
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ /* Start scheduled queue indices from zero to enable direct
+ * mapping to scheduler implementation indices. */
+ queue_idx = 0;
+ max_idx = CONFIG_MAX_SCHED_QUEUES;
+ } else {
+ queue_idx = CONFIG_MAX_SCHED_QUEUES;
+ /* All internal queues are of type plain */
+ max_idx = CONFIG_MAX_QUEUES;
+ }
+
+ for (; queue_idx < max_idx; queue_idx++) {
+ queue = &queue_tbl->queue[queue_idx];
+
+ if (queue->status != QUEUE_STATUS_FREE)
+ continue;
+
+ LOCK(&queue->lock);
+ if (queue->status == QUEUE_STATUS_FREE) {
+ if (queue_init(queue, name, param)) {
+ UNLOCK(&queue->lock);
+ return handle;
+ }
+ queue->status = QUEUE_STATUS_READY;
+ handle = queue->handle;
+ UNLOCK(&queue->lock);
+ break;
+ }
+ UNLOCK(&queue->lock);
+ }
+ return handle;
+}
+
+static int queue_create_multi(const char *name[], const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(param != NULL);
+ _ODP_ASSERT(queue != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ odp_queue_t cur_queue;
+ const char *cur_name = name != NULL ? name[i] : NULL;
+ const odp_queue_param_t *cur_param = share_param ? &param[0] : &param[i];
+
+ cur_queue = queue_create(cur_name, cur_param);
+ if (cur_queue == ODP_QUEUE_INVALID)
+ return (i == 0) ? -1 : i;
+
+ queue[i] = cur_queue;
+ }
+ return i;
+}
+
+static int queue_destroy(odp_queue_t handle)
+{
+ queue_entry_t *queue;
+ sched_elem_t *q;
+
+ if (handle == ODP_QUEUE_INVALID)
+ return -1;
+
+ queue = _odp_qentry_from_ext(handle);
+ LOCK(&queue->lock);
+ if (queue->status != QUEUE_STATUS_READY) {
+ UNLOCK(&queue->lock);
+ return -1;
+ }
+ q = &queue->sched_elem;
+
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&q->qschlock);
+#endif
+ if (_odp_queue_disable_enq(q)) {
+ /* Producer side not empty */
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&q->qschlock);
+#endif
+ UNLOCK(&queue->lock);
+ return -1;
+ }
+ /* Enqueue is now disabled */
+ if (q->cons_read != q->cons_write) {
+ /* Consumer side is not empty
+ * Roll back previous change, enable enqueue again.
+ */
+ uint32_t size;
+
+ size = q->prod_mask + 1;
+ __atomic_fetch_sub(&q->prod_write, size, __ATOMIC_RELAXED);
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&q->qschlock);
+#endif
+ UNLOCK(&queue->lock);
+ return -1;
+ }
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&q->qschlock);
+#endif
+ /* Producer and consumer sides empty, enqueue disabled
+ * Now wait until schedq state is empty and no outstanding tickets
+ */
+ while (__atomic_load_n(&q->qschst.numevts, __ATOMIC_RELAXED) != 0 ||
+ __atomic_load_n(&q->qschst.cur_ticket, __ATOMIC_RELAXED) !=
+ __atomic_load_n(&q->qschst.nxt_ticket, __ATOMIC_RELAXED))
+ _odp_wait_until_eq_u32((uint32_t *)&q->qschst.numevts, 0);
+
+ if (q->schedq != NULL) {
+ _odp_sched_queue_rem(q->sched_grp, q->sched_prio);
+ q->schedq = NULL;
+ }
+
+ _odp_ishm_pool_free(queue_shm_pool, q->prod_ring);
+
+ if (q->rwin != NULL) {
+ if (_odp_rwin_free(queue_shm_pool, q->rwin) < 0) {
+ _ODP_ERR("Failed to free reorder window\n");
+ UNLOCK(&queue->lock);
+ return -1;
+ }
+ q->rwin = NULL;
+ }
+ queue->status = QUEUE_STATUS_FREE;
+ UNLOCK(&queue->lock);
+ return 0;
+}
+
+static int queue_destroy_multi(odp_queue_t handle[], int num)
+{
+ int i;
+
+ _ODP_ASSERT(handle != NULL);
+ _ODP_ASSERT(num > 0);
+
+ for (i = 0; i < num; i++) {
+ int ret = queue_destroy(handle[i]);
+
+ if (ret)
+ return (i == 0) ? ret : i;
+ }
+
+ return i;
+}
+
+static int queue_context_set(odp_queue_t handle, void *context,
+ uint32_t len ODP_UNUSED)
+{
+ odp_mb_full();
+ _odp_qentry_from_ext(handle)->param.context = context;
+ odp_mb_full();
+ return 0;
+}
+
+static odp_queue_t queue_lookup(const char *name)
+{
+ uint32_t i;
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue_entry_t *queue = &queue_tbl->queue[i];
+
+ if (queue->status == QUEUE_STATUS_FREE ||
+ queue->status == QUEUE_STATUS_DESTROYED)
+ continue;
+
+ LOCK(&queue->lock);
+ if (strcmp(name, queue->name) == 0) {
+ /* found it */
+ UNLOCK(&queue->lock);
+ return queue->handle;
+ }
+ UNLOCK(&queue->lock);
+ }
+
+ return ODP_QUEUE_INVALID;
+}
+
+#ifndef CONFIG_QSCHST_LOCK
+static inline int _odp_queue_enq(sched_elem_t *q,
+ _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ ringidx_t old_read;
+ ringidx_t old_write;
+ ringidx_t new_write;
+ int actual;
+ uint32_t mask;
+ _odp_event_hdr_t **ring;
+
+ mask = q->prod_mask;
+ ring = q->prod_ring;
+
+ /* Load producer ring state (read & write index) */
+ old_write = __atomic_load_n(&q->prod_write, __ATOMIC_RELAXED);
+ do {
+ /* Consumer does store-release prod_read, we need
+ * load-acquire.
+ */
+ old_read = __atomic_load_n(&q->prod_read, __ATOMIC_ACQUIRE);
+
+ actual = _ODP_MIN(num, (int)((mask + 1) - (old_write - old_read)));
+ if (odp_unlikely(actual <= 0))
+ return 0;
+
+ new_write = old_write + actual;
+ } while (!__atomic_compare_exchange_n(&q->prod_write,
+ &old_write, /* Updated on failure */
+ new_write,
+ true,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED));
+
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->cons_write, 0, 0);
+#endif
+ /* Store our event(s) in the ring */
+ do {
+ ring[old_write & mask] = *event_hdr++;
+ } while (++old_write != new_write);
+ old_write -= actual;
+
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->node, 1, 0);
+#endif
+ /* Wait for our turn to signal consumers */
+ if (odp_unlikely(__atomic_load_n(&q->cons_write, __ATOMIC_RELAXED) != old_write))
+ _odp_wait_until_eq_u32(&q->cons_write, old_write);
+
+ /* Signal consumers that events are available (release events)
+ * Enable other producers to continue
+ */
+ /* Wait for writes (to ring slots) to complete */
+ atomic_store_release(&q->cons_write, new_write, /*readonly=*/false);
+
+ return actual;
+}
+
+#endif
+
+int _odp_queue_enq_sp(sched_elem_t *q,
+ _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ ringidx_t old_read;
+ ringidx_t old_write;
+ ringidx_t new_write;
+ int actual;
+ uint32_t mask;
+ _odp_event_hdr_t **ring;
+
+ mask = q->prod_mask;
+ ring = q->prod_ring;
+
+ /* Load producer ring state (read & write index) */
+ old_write = q->prod_write;
+ /* Consumer does store-release prod_read, we need load-acquire */
+ old_read = __atomic_load_n(&q->prod_read, __ATOMIC_ACQUIRE);
+ actual = _ODP_MIN(num, (int)((mask + 1) - (old_write - old_read)));
+ if (odp_unlikely(actual <= 0))
+ return 0;
+
+ new_write = old_write + actual;
+ q->prod_write = new_write;
+
+ /* Store our event(s) in the ring */
+ do {
+ ring[old_write & mask] = *event_hdr++;
+ } while (++old_write != new_write);
+ old_write -= actual;
+
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->node, 1, 0);
+#endif
+
+ /* Signal consumers that events are available (release events)
+ * Enable other producers to continue
+ */
+#ifdef CONFIG_QSCHST_LOCK
+ q->cons_write = new_write;
+#else
+ atomic_store_release(&q->cons_write, new_write, /*readonly=*/false);
+#endif
+
+ return actual;
+}
+
+static int _queue_enq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ int actual;
+ queue_entry_t *queue;
+ sched_scalable_thread_state_t *ts;
+
+ queue = qentry_from_int(handle);
+ ts = _odp_sched_ts;
+ if (ts && odp_unlikely(ts->out_of_order) &&
+ (queue->param.order == ODP_QUEUE_ORDER_KEEP)) {
+ actual = _odp_rctx_save(queue, event_hdr, num);
+ return actual;
+ }
+
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&queue->sched_elem.qschlock);
+ actual = _odp_queue_enq_sp(&queue->sched_elem, event_hdr, num);
+#else
+ actual = _odp_queue_enq(&queue->sched_elem, event_hdr, num);
+#endif
+
+ if (odp_likely(queue->sched_elem.schedq != NULL && actual != 0)) {
+ /* Perform scheduler related updates. */
+#ifdef CONFIG_QSCHST_LOCK
+ _odp_sched_update_enq_sp(&queue->sched_elem, actual);
+#else
+ _odp_sched_update_enq(&queue->sched_elem, actual);
+#endif
+ }
+
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&queue->sched_elem.qschlock);
+#endif
+ return actual;
+}
+
+static int _queue_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ return odp_likely(_queue_enq_multi(handle, &event_hdr, 1) == 1) ? 0 : -1;
+}
+
+static int queue_enq_multi(odp_queue_t handle, const odp_event_t ev[], int num)
+{
+ _odp_event_hdr_t *event_hdr[QUEUE_MULTI_MAX];
+ queue_entry_t *queue;
+ int i;
+
+ if (num > QUEUE_MULTI_MAX)
+ num = QUEUE_MULTI_MAX;
+
+ queue = _odp_qentry_from_ext(handle);
+
+ for (i = 0; i < num; i++)
+ event_hdr[i] = _odp_event_hdr(ev[i]);
+
+ return queue->enqueue_multi(handle, event_hdr, num);
+}
+
+static int queue_enq(odp_queue_t handle, odp_event_t ev)
+{
+ _odp_event_hdr_t *event_hdr;
+ queue_entry_t *queue;
+
+ queue = _odp_qentry_from_ext(handle);
+ event_hdr = _odp_event_hdr(ev);
+
+ return queue->enqueue(handle, event_hdr);
+}
+
+/* Single-consumer dequeue. */
+int _odp_queue_deq_sc(sched_elem_t *q, odp_event_t *evp, int num)
+{
+ int actual;
+ ringidx_t old_read;
+ ringidx_t old_write;
+ ringidx_t new_read;
+ uint32_t mask;
+ _odp_event_hdr_t **ring;
+
+ /* Load consumer ring state (read & write index). */
+ old_read = q->cons_read;
+ /* Producer does store-release cons_write, we need load-acquire */
+ old_write = __atomic_load_n(&q->cons_write, __ATOMIC_ACQUIRE);
+ actual = _ODP_MIN(num, (int)(old_write - old_read));
+
+ if (odp_unlikely(actual <= 0))
+ return 0;
+
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->node, 1, 0);
+#endif
+ new_read = old_read + actual;
+ q->cons_read = new_read;
+
+ mask = q->cons_mask;
+ ring = q->cons_ring;
+ do {
+ *evp++ = _odp_event_from_hdr(ring[old_read & mask]);
+ } while (++old_read != new_read);
+
+ /* Signal producers that empty slots are available
+ * (release ring slots). Enable other consumers to continue.
+ */
+#ifdef CONFIG_QSCHST_LOCK
+ q->prod_read = new_read;
+#else
+ /* Wait for loads (from ring slots) to complete. */
+ atomic_store_release(&q->prod_read, new_read, /*readonly=*/true);
+#endif
+ return actual;
+}
+
+int _odp_queue_deq(sched_elem_t *q, _odp_event_hdr_t *event_hdr[], int num)
+{
+ int actual;
+ ringidx_t old_read;
+ ringidx_t old_write;
+ ringidx_t new_read;
+ uint32_t mask;
+ _odp_event_hdr_t **ring;
+ _odp_event_hdr_t **p_event_hdr;
+
+ mask = q->cons_mask;
+ ring = q->cons_ring;
+
+ /* Load consumer ring state (read & write index) */
+ old_read = __atomic_load_n(&q->cons_read, __ATOMIC_RELAXED);
+ do {
+ /* Need __atomic_load to avoid compiler reordering
+ * Producer does store-release cons_write, we need
+ * load-acquire.
+ */
+ old_write = __atomic_load_n(&q->cons_write, __ATOMIC_ACQUIRE);
+ /* Prefetch ring buffer array */
+ __builtin_prefetch(&q->cons_ring[old_read & mask], 0, 0);
+
+ actual = _ODP_MIN(num, (int)(old_write - old_read));
+ if (odp_unlikely(actual <= 0))
+ return 0;
+
+ /* Attempt to free ring slot(s) */
+ new_read = old_read + actual;
+ } while (!__atomic_compare_exchange_n(&q->cons_read,
+ &old_read, /* Updated on failure */
+ new_read,
+ true,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED));
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->prod_read, 0, 0);
+#endif
+ p_event_hdr = event_hdr;
+ do {
+ *p_event_hdr++ = ring[old_read & mask];
+ } while (++old_read != new_read);
+ old_read -= actual;
+
+#ifdef CONFIG_SPLIT_PRODCONS
+ __builtin_prefetch(&q->node, 1, 0);
+#endif
+ /* Wait for our turn to signal producers */
+ if (odp_unlikely(__atomic_load_n(&q->prod_read, __ATOMIC_RELAXED) != old_read))
+ _odp_wait_until_eq_u32(&q->prod_read, old_read);
+
+ /* Signal producers that empty slots are available
+ * (release ring slots)
+ * Enable other consumers to continue
+ */
+ /* Wait for loads (from ring slots) to complete */
+ atomic_store_release(&q->prod_read, new_read, /*readonly=*/true);
+
+ return actual;
+}
+
+int _odp_queue_deq_mc(sched_elem_t *q, odp_event_t *evp, int num)
+{
+ int ret, evt_idx;
+ _odp_event_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
+
+ if (num > QUEUE_MULTI_MAX)
+ num = QUEUE_MULTI_MAX;
+
+ ret = _odp_queue_deq(q, hdr_tbl, num);
+ if (odp_likely(ret != 0)) {
+ for (evt_idx = 0; evt_idx < num; evt_idx++)
+ evp[evt_idx] = _odp_event_from_hdr(hdr_tbl[evt_idx]);
+ }
+
+ return ret;
+}
+
+static int _queue_deq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ sched_elem_t *q;
+ queue_entry_t *queue;
+
+ queue = qentry_from_int(handle);
+ q = &queue->sched_elem;
+ return _odp_queue_deq(q, event_hdr, num);
+}
+
+static _odp_event_hdr_t *_queue_deq(odp_queue_t handle)
+{
+ sched_elem_t *q;
+ _odp_event_hdr_t *event_hdr;
+ queue_entry_t *queue;
+
+ queue = qentry_from_int(handle);
+ q = &queue->sched_elem;
+ if (_odp_queue_deq(q, &event_hdr, 1) == 1)
+ return event_hdr;
+ else
+ return NULL;
+}
+
+static int queue_deq_multi(odp_queue_t handle, odp_event_t ev[], int num)
+{
+ queue_entry_t *queue;
+ int ret;
+
+ if (num > QUEUE_MULTI_MAX)
+ num = QUEUE_MULTI_MAX;
+
+ queue = _odp_qentry_from_ext(handle);
+
+ ret = queue->dequeue_multi(handle, (_odp_event_hdr_t **)ev, num);
+
+ if (odp_global_rw->inline_timers &&
+ odp_atomic_load_u64(&queue->num_timers))
+ timer_run(ret ? 2 : 1);
+
+ return ret;
+}
+
+static odp_event_t queue_deq(odp_queue_t handle)
+{
+ queue_entry_t *queue = _odp_qentry_from_ext(handle);
+ odp_event_t ev = (odp_event_t)queue->dequeue(handle);
+
+ if (odp_global_rw->inline_timers &&
+ odp_atomic_load_u64(&queue->num_timers))
+ timer_run(ev != ODP_EVENT_INVALID ? 2 : 1);
+
+ return ev;
+}
+
+static void queue_param_init(odp_queue_param_t *params)
+{
+ memset(params, 0, sizeof(odp_queue_param_t));
+ params->type = ODP_QUEUE_TYPE_PLAIN;
+ params->enq_mode = ODP_QUEUE_OP_MT;
+ params->deq_mode = ODP_QUEUE_OP_MT;
+ params->nonblocking = ODP_BLOCKING;
+ params->sched.prio = odp_schedule_default_prio();
+ params->sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ params->sched.group = ODP_SCHED_GROUP_ALL;
+ params->order = ODP_QUEUE_ORDER_KEEP;
+}
+
+static int queue_info(odp_queue_t handle, odp_queue_info_t *info)
+{
+ uint32_t queue_id;
+ queue_entry_t *queue;
+ int status;
+
+ if (odp_unlikely(info == NULL)) {
+ _ODP_ERR("Unable to store info, NULL ptr given\n");
+ return -1;
+ }
+
+ queue_id = queue_to_id(handle);
+
+ if (odp_unlikely(queue_id >= CONFIG_MAX_QUEUES)) {
+ _ODP_ERR("Invalid queue handle:%" PRIu64 "\n", odp_queue_to_u64(handle));
+ return -1;
+ }
+
+ queue = get_qentry(queue_id);
+
+ LOCK(&queue->lock);
+ status = queue->status;
+
+ if (odp_unlikely(status == QUEUE_STATUS_FREE ||
+ status == QUEUE_STATUS_DESTROYED)) {
+ UNLOCK(&queue->lock);
+ _ODP_ERR("Invalid queue status:%d\n", status);
+ return -1;
+ }
+
+ info->name = queue->name;
+ info->param = queue->param;
+
+ UNLOCK(&queue->lock);
+
+ return 0;
+}
+
+static void queue_print(odp_queue_t handle)
+{
+ odp_pktio_info_t pktio_info;
+ queue_entry_t *queue;
+ uint32_t queue_id;
+ int status;
+
+ queue_id = queue_to_id(handle);
+
+ if (odp_unlikely(queue_id >= CONFIG_MAX_QUEUES)) {
+ _ODP_ERR("Invalid queue handle: 0x%" PRIx64 "\n", odp_queue_to_u64(handle));
+ return;
+ }
+
+ queue = get_qentry(queue_id);
+
+ LOCK(&queue->lock);
+ status = queue->status;
+
+ if (odp_unlikely(status == QUEUE_STATUS_FREE ||
+ status == QUEUE_STATUS_DESTROYED)) {
+ UNLOCK(&queue->lock);
+ _ODP_ERR("Invalid queue status:%d\n", status);
+ return;
+ }
+ _ODP_PRINT("\nQueue info\n");
+ _ODP_PRINT("----------\n");
+ _ODP_PRINT(" handle %p\n", (void *)queue->handle);
+ _ODP_PRINT(" index %" PRIu32 "\n", queue->index);
+ _ODP_PRINT(" name %s\n", queue->name);
+ _ODP_PRINT(" enq mode %s\n",
+ queue->param.enq_mode == ODP_QUEUE_OP_MT ? "ODP_QUEUE_OP_MT" :
+ (queue->param.enq_mode == ODP_QUEUE_OP_MT_UNSAFE ? "ODP_QUEUE_OP_MT_UNSAFE" :
+ (queue->param.enq_mode == ODP_QUEUE_OP_DISABLED ? "ODP_QUEUE_OP_DISABLED" :
+ "unknown")));
+ _ODP_PRINT(" deq mode %s\n",
+ queue->param.deq_mode == ODP_QUEUE_OP_MT ? "ODP_QUEUE_OP_MT" :
+ (queue->param.deq_mode == ODP_QUEUE_OP_MT_UNSAFE ? "ODP_QUEUE_OP_MT_UNSAFE" :
+ (queue->param.deq_mode == ODP_QUEUE_OP_DISABLED ? "ODP_QUEUE_OP_DISABLED" :
+ "unknown")));
+ _ODP_PRINT(" type %s\n",
+ queue->type == ODP_QUEUE_TYPE_PLAIN ? "ODP_QUEUE_TYPE_PLAIN" :
+ (queue->type == ODP_QUEUE_TYPE_SCHED ? "ODP_QUEUE_TYPE_SCHED" : "unknown"));
+ if (queue->type == ODP_QUEUE_TYPE_SCHED) {
+ _ODP_PRINT(" sync %s\n",
+ queue->param.sched.sync == ODP_SCHED_SYNC_PARALLEL ?
+ "ODP_SCHED_SYNC_PARALLEL" :
+ (queue->param.sched.sync == ODP_SCHED_SYNC_ATOMIC ?
+ "ODP_SCHED_SYNC_ATOMIC" :
+ (queue->param.sched.sync == ODP_SCHED_SYNC_ORDERED ?
+ "ODP_SCHED_SYNC_ORDERED" : "unknown")));
+ _ODP_PRINT(" priority %d\n", queue->param.sched.prio);
+ _ODP_PRINT(" group %d\n", queue->param.sched.group);
+ }
+ if (queue->pktin.pktio != ODP_PKTIO_INVALID) {
+ if (!odp_pktio_info(queue->pktin.pktio, &pktio_info))
+ _ODP_PRINT(" pktin %s\n", pktio_info.name);
+ }
+ if (queue->pktout.pktio != ODP_PKTIO_INVALID) {
+ if (!odp_pktio_info(queue->pktout.pktio, &pktio_info))
+ _ODP_PRINT(" pktout %s\n", pktio_info.name);
+ }
+ _ODP_PRINT(" timers %" PRIu64 "\n", odp_atomic_load_u64(&queue->num_timers));
+ _ODP_PRINT(" param.size %" PRIu32 "\n", queue->param.size);
+ _ODP_PRINT("\n");
+
+ UNLOCK(&queue->lock);
+}
+
+static void queue_print_all(void)
+{
+ uint32_t i, index;
+ const char *name;
+ int status;
+ odp_queue_type_t type;
+ odp_nonblocking_t blocking;
+ odp_queue_op_mode_t enq_mode;
+ odp_queue_op_mode_t deq_mode;
+ odp_queue_order_t order;
+ odp_schedule_sync_t sync;
+ int prio;
+ const char *bl_str;
+ char type_c, enq_c, deq_c, order_c, sync_c;
+ const int col_width = 24;
+
+ _ODP_PRINT("\nList of all queues\n");
+ _ODP_PRINT("------------------\n");
+ _ODP_PRINT(" idx %-*s type blk enq deq ord sync prio\n", col_width, "name");
+
+ for (i = 0; i < CONFIG_MAX_QUEUES; i++) {
+ queue_entry_t *queue = &queue_tbl->queue[i];
+
+ if (queue->status != QUEUE_STATUS_READY)
+ continue;
+
+ LOCK(&queue->lock);
+
+ status = queue->status;
+ index = queue->index;
+ name = queue->name;
+ type = queue->type;
+ blocking = queue->param.nonblocking;
+ enq_mode = queue->param.enq_mode;
+ deq_mode = queue->param.deq_mode;
+ order = queue->param.order;
+ prio = queue->param.sched.prio;
+ sync = queue->param.sched.sync;
+
+ UNLOCK(&queue->lock);
+
+ if (status != QUEUE_STATUS_READY)
+ continue;
+
+ type_c = (type == ODP_QUEUE_TYPE_PLAIN) ? 'P' : 'S';
+
+ bl_str = (blocking == ODP_BLOCKING) ? "B" :
+ ((blocking == ODP_NONBLOCKING_LF) ? "LF" : "WF");
+
+ enq_c = (enq_mode == ODP_QUEUE_OP_MT) ? 'S' :
+ ((enq_mode == ODP_QUEUE_OP_MT_UNSAFE) ? 'U' : 'D');
+
+ deq_c = (deq_mode == ODP_QUEUE_OP_MT) ? 'S' :
+ ((deq_mode == ODP_QUEUE_OP_MT_UNSAFE) ? 'U' : 'D');
+
+ order_c = (order == ODP_QUEUE_ORDER_KEEP) ? 'K' : 'I';
+
+ _ODP_PRINT("%4u %-*s %c %2s", index, col_width, name, type_c, bl_str);
+ _ODP_PRINT(" %c %c %c", enq_c, deq_c, order_c);
+
+ if (type == ODP_QUEUE_TYPE_SCHED) {
+ sync_c = (sync == ODP_SCHED_SYNC_PARALLEL) ? 'P' :
+ ((sync == ODP_SCHED_SYNC_ATOMIC) ? 'A' : 'O');
+ _ODP_PRINT(" %c %4i", sync_c, prio);
+ }
+
+ _ODP_PRINT("\n");
+ }
+
+ _ODP_PRINT("\n");
+}
+
+static uint64_t queue_to_u64(odp_queue_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+static odp_pktout_queue_t queue_get_pktout(odp_queue_t handle)
+{
+ return qentry_from_int(handle)->pktout;
+}
+
+static void queue_set_pktout(odp_queue_t handle, odp_pktio_t pktio, int index)
+{
+ qentry_from_int(handle)->pktout.pktio = pktio;
+ qentry_from_int(handle)->pktout.index = index;
+}
+
+static odp_pktin_queue_t queue_get_pktin(odp_queue_t handle)
+{
+ return qentry_from_int(handle)->pktin;
+}
+
+static void queue_set_pktin(odp_queue_t handle, odp_pktio_t pktio, int index)
+{
+ qentry_from_int(handle)->pktin.pktio = pktio;
+ qentry_from_int(handle)->pktin.index = index;
+}
+
+static void queue_set_enq_deq_func(odp_queue_t handle,
+ queue_enq_fn_t enq,
+ queue_enq_multi_fn_t enq_multi,
+ queue_deq_fn_t deq,
+ queue_deq_multi_fn_t deq_multi)
+{
+ if (enq)
+ qentry_from_int(handle)->enqueue = enq;
+
+ if (enq_multi)
+ qentry_from_int(handle)->enqueue_multi = enq_multi;
+
+ if (deq)
+ qentry_from_int(handle)->dequeue = deq;
+
+ if (deq_multi)
+ qentry_from_int(handle)->dequeue_multi = deq_multi;
+}
+
+static int queue_orig_multi(odp_queue_t handle,
+ _odp_event_hdr_t **event_hdr, int num)
+{
+ return qentry_from_int(handle)->orig_dequeue_multi(handle,
+ event_hdr, num);
+}
+
+static void queue_timer_add(odp_queue_t handle)
+{
+ queue_entry_t *queue = _odp_qentry_from_ext(handle);
+
+ odp_atomic_inc_u64(&queue->num_timers);
+}
+
+static void queue_timer_rem(odp_queue_t handle)
+{
+ queue_entry_t *queue = _odp_qentry_from_ext(handle);
+
+ odp_atomic_dec_u64(&queue->num_timers);
+}
+
+/* API functions */
+_odp_queue_api_fn_t _odp_queue_scalable_api = {
+ .queue_create = queue_create,
+ .queue_create_multi = queue_create_multi,
+ .queue_destroy = queue_destroy,
+ .queue_destroy_multi = queue_destroy_multi,
+ .queue_lookup = queue_lookup,
+ .queue_capability = queue_capability,
+ .queue_context_set = queue_context_set,
+ .queue_enq = queue_enq,
+ .queue_enq_multi = queue_enq_multi,
+ .queue_deq = queue_deq,
+ .queue_deq_multi = queue_deq_multi,
+ .queue_type = queue_type,
+ .queue_sched_type = queue_sched_type,
+ .queue_sched_prio = queue_sched_prio,
+ .queue_sched_group = queue_sched_group,
+ .queue_lock_count = queue_lock_count,
+ .queue_to_u64 = queue_to_u64,
+ .queue_param_init = queue_param_init,
+ .queue_info = queue_info,
+ .queue_print = queue_print,
+ .queue_print_all = queue_print_all
+};
+
+/* Functions towards internal components */
+queue_fn_t _odp_queue_scalable_fn = {
+ .init_global = queue_init_global,
+ .term_global = queue_term_global,
+ .init_local = queue_init_local,
+ .term_local = queue_term_local,
+ .get_pktout = queue_get_pktout,
+ .set_pktout = queue_set_pktout,
+ .get_pktin = queue_get_pktin,
+ .set_pktin = queue_set_pktin,
+ .set_enq_deq_fn = queue_set_enq_deq_func,
+ .orig_deq_multi = queue_orig_multi,
+ .timer_add = queue_timer_add,
+ .timer_rem = queue_timer_rem
+};
diff --git a/platform/linux-generic/odp_queue_spsc.c b/platform/linux-generic/odp_queue_spsc.c
new file mode 100644
index 000000000..bb2a7ce9f
--- /dev/null
+++ b/platform/linux-generic/odp_queue_spsc.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_queue_basic_internal.h>
+
+#include <string.h>
+#include <stdio.h>
+
+static inline void event_index_from_hdr(uint32_t event_index[],
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ event_index[i] = event_hdr[i]->index.u32;
+}
+
+static inline void event_index_to_hdr(_odp_event_hdr_t *event_hdr[],
+ uint32_t event_index[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ event_hdr[i] = _odp_event_hdr_from_index_u32(event_index[i]);
+ odp_prefetch(event_hdr[i]);
+ }
+}
+
+static inline int spsc_enq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ queue_entry_t *queue;
+ ring_spsc_t *ring_spsc;
+ uint32_t buf_idx[num];
+
+ queue = qentry_from_handle(handle);
+ ring_spsc = &queue->ring_spsc;
+
+ event_index_from_hdr(buf_idx, event_hdr, num);
+
+ if (odp_unlikely(queue->status < QUEUE_STATUS_READY)) {
+ _ODP_ERR("Bad queue status\n");
+ return -1;
+ }
+
+ return ring_spsc_enq_multi(ring_spsc, queue->ring_data,
+ queue->ring_mask, buf_idx, num);
+}
+
+static inline int spsc_deq_multi(odp_queue_t handle,
+ _odp_event_hdr_t *event_hdr[], int num)
+{
+ queue_entry_t *queue;
+ int num_deq;
+ ring_spsc_t *ring_spsc;
+ uint32_t buf_idx[num];
+
+ queue = qentry_from_handle(handle);
+ ring_spsc = &queue->ring_spsc;
+
+ if (odp_unlikely(queue->status < QUEUE_STATUS_READY)) {
+ /* Bad queue, or queue has been destroyed. */
+ return -1;
+ }
+
+ num_deq = ring_spsc_deq_multi(ring_spsc, queue->ring_data,
+ queue->ring_mask, buf_idx, num);
+
+ if (num_deq == 0)
+ return 0;
+
+ event_index_to_hdr(event_hdr, buf_idx, num_deq);
+
+ return num_deq;
+}
+
+static int queue_spsc_enq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ return spsc_enq_multi(handle, event_hdr, num);
+}
+
+static int queue_spsc_enq(odp_queue_t handle, _odp_event_hdr_t *event_hdr)
+{
+ int ret;
+
+ ret = spsc_enq_multi(handle, &event_hdr, 1);
+
+ if (ret == 1)
+ return 0;
+ else
+ return -1;
+}
+
+static int queue_spsc_deq_multi(odp_queue_t handle, _odp_event_hdr_t *event_hdr[],
+ int num)
+{
+ return spsc_deq_multi(handle, event_hdr, num);
+}
+
+static _odp_event_hdr_t *queue_spsc_deq(odp_queue_t handle)
+{
+ _odp_event_hdr_t *event_hdr = NULL;
+ int ret;
+
+ ret = spsc_deq_multi(handle, &event_hdr, 1);
+
+ if (ret == 1)
+ return event_hdr;
+ else
+ return NULL;
+}
+
+void _odp_queue_spsc_init(queue_entry_t *queue, uint32_t queue_size)
+{
+ uint64_t offset;
+
+ queue->enqueue = queue_spsc_enq;
+ queue->dequeue = queue_spsc_deq;
+ queue->enqueue_multi = queue_spsc_enq_multi;
+ queue->dequeue_multi = queue_spsc_deq_multi;
+ queue->orig_dequeue_multi = queue_spsc_deq_multi;
+
+ offset = queue->index * (uint64_t)_odp_queue_glb->config.max_queue_size;
+
+ queue->ring_data = &_odp_queue_glb->ring_data[offset];
+ queue->ring_mask = queue_size - 1;
+ ring_spsc_init(&queue->ring_spsc);
+}
diff --git a/platform/linux-generic/odp_random.c b/platform/linux-generic/odp_random.c
new file mode 100644
index 000000000..d96f03f39
--- /dev/null
+++ b/platform/linux-generic/odp_random.c
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <stdint.h>
+
+#include <odp/api/random.h>
+
+#include <odp/autoheader_internal.h>
+#include <odp_init_internal.h>
+#include <odp_random_std_internal.h>
+#include <odp_random_openssl_internal.h>
+#include <odp_random.h>
+
+odp_random_kind_t odp_random_max_kind(void)
+{
+ odp_random_kind_t kind, max_kind = ODP_RANDOM_BASIC;
+
+ if (_ODP_OPENSSL_RAND)
+ max_kind = ODP_RANDOM_CRYPTO;
+
+ kind = _odp_random_max_kind();
+ if (kind > max_kind)
+ max_kind = kind;
+
+ return max_kind;
+}
+
+int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind)
+{
+ switch (kind) {
+ case ODP_RANDOM_BASIC:
+ if (_ODP_OPENSSL_RAND)
+ return _odp_random_openssl_data(buf, len);
+ return _odp_random_std_data(buf, len);
+ case ODP_RANDOM_CRYPTO:
+ if (_ODP_OPENSSL_RAND)
+ return _odp_random_openssl_data(buf, len);
+ return _odp_random_crypto_data(buf, len);
+ case ODP_RANDOM_TRUE:
+ return _odp_random_true_data(buf, len);
+ }
+
+ return -1;
+}
+
+int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
+{
+ return _odp_random_std_test_data(buf, len, seed);
+}
+
+int _odp_random_init_local(void)
+{
+ if (_ODP_OPENSSL_RAND)
+ return _odp_random_openssl_init_local();
+ return _odp_random_std_init_local();
+}
+
+int _odp_random_term_local(void)
+{
+ if (_ODP_OPENSSL_RAND)
+ return _odp_random_openssl_term_local();
+ return _odp_random_std_term_local();
+}
diff --git a/platform/linux-generic/odp_random_openssl.c b/platform/linux-generic/odp_random_openssl.c
new file mode 100644
index 000000000..c45cb470b
--- /dev/null
+++ b/platform/linux-generic/odp_random_openssl.c
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+#include <stdint.h>
+#include <odp/autoheader_internal.h>
+#include <odp_init_internal.h>
+#include <odp_random_openssl_internal.h>
+
+#if _ODP_OPENSSL_RAND
+#include <openssl/rand.h>
+
+int32_t _odp_random_openssl_data(uint8_t *buf, uint32_t len)
+{
+ int rc;
+
+ rc = RAND_bytes(buf, len);
+ return (1 == rc) ? (int)len /*success*/: -1 /*failure*/;
+}
+#else
+/* Dummy functions for building without OpenSSL support */
+int32_t _odp_random_openssl_data(uint8_t *buf ODP_UNUSED,
+ uint32_t len ODP_UNUSED)
+{
+ return -1;
+}
+#endif /* _ODP_OPENSSL_RAND */
+
+int _odp_random_openssl_init_local(void)
+{
+ return 0;
+}
+
+int _odp_random_openssl_term_local(void)
+{
+ return 0;
+}
diff --git a/platform/linux-generic/odp_random_std.c b/platform/linux-generic/odp_random_std.c
new file mode 100644
index 000000000..5ae47d596
--- /dev/null
+++ b/platform/linux-generic/odp_random_std.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+#include <odp/api/byteorder.h>
+#include <odp/api/cpu.h>
+#include <odp/api/debug.h>
+#include <odp_init_internal.h>
+#include <odp_random_std_internal.h>
+#include <odp_cpu.h>
+
+#include <stdint.h>
+#include <time.h>
+
+/*
+ * Xorshift64*, adapted from [1], and modified to return only the high 32 bits.
+ *
+ * [1] An experimental exploration of Marsaglia's xorshift generators, scrambled
+ * Sebastiano Vigna, July 2016.
+ * http://vigna.di.unimi.it/ftp/papers/xorshift.pdf
+ */
+static inline uint32_t xorshift64s32(uint64_t *x)
+{
+ /* The variable x should be initialized to a nonzero seed. [1] */
+ if (!*x)
+ /*
+ * 2^64 / phi. As far away as possible from any small integer
+ * fractions, which the caller might be likely to use for the
+ * next seed after 0.
+ */
+ *x = 11400714819323198485ull;
+
+ *x ^= *x >> 12; /* a */
+ *x ^= *x << 25; /* b */
+ *x ^= *x >> 27; /* c */
+ return (*x * 2685821657736338717ull) >> 32;
+}
+
+static int32_t _random_data(uint8_t *buf, uint32_t len, uint64_t *seed)
+{
+ const uint32_t ret = len;
+
+ if (!_ODP_UNALIGNED) {
+ uint32_t r = xorshift64s32(seed);
+
+ if (((uintptr_t)buf & 1) && len) {
+ *(uint8_t *)buf = r & 0xff;
+ r >>= 8;
+ buf += 1;
+ len -= 1;
+ }
+
+ if (((uintptr_t)buf & 2) && len >= 2) {
+ *(uint16_t *)(uintptr_t)buf = r & 0xffff;
+ buf += 2;
+ len -= 2;
+ }
+
+ for (uint32_t i = 0; i < len / 4; i++) {
+ *(uint32_t *)(uintptr_t)buf = xorshift64s32(seed);
+ buf += 4;
+ }
+ } else {
+ for (uint32_t i = 0; i < len / 4; i++) {
+ *(odp_una_u32_t *)buf = xorshift64s32(seed);
+ buf += 4;
+ }
+ }
+
+ if (len & 3) {
+ uint32_t r = xorshift64s32(seed);
+
+ if (len & 2) {
+ *(odp_una_u16_t *)buf = r & 0xffff;
+ r >>= 16;
+ buf += 2;
+ }
+
+ if (len & 1)
+ *(uint8_t *)buf = r & 0xff;
+ }
+
+ return ret;
+}
+
+int32_t _odp_random_std_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
+{
+ return _random_data(buf, len, seed);
+}
+
+static __thread uint64_t this_seed;
+
+int32_t _odp_random_std_data(uint8_t *buf, uint32_t len)
+{
+ return _random_data(buf, len, &this_seed);
+}
+
+int _odp_random_std_init_local(void)
+{
+ this_seed = time(NULL);
+ this_seed ^= (uint64_t)odp_cpu_id() << 32;
+
+ return 0;
+}
+
+int _odp_random_std_term_local(void)
+{
+ return 0;
+}
diff --git a/platform/linux-generic/odp_rwlock.c b/platform/linux-generic/odp_rwlock.c
deleted file mode 100644
index 5bef13a45..000000000
--- a/platform/linux-generic/odp_rwlock.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdbool.h>
-#include <odp/api/atomic.h>
-#include <odp/api/rwlock.h>
-#include <odp/api/cpu.h>
-
-void odp_rwlock_init(odp_rwlock_t *rwlock)
-{
- odp_atomic_init_u32(&rwlock->cnt, 0);
-}
-
-void odp_rwlock_read_lock(odp_rwlock_t *rwlock)
-{
- uint32_t cnt;
- int is_locked = 0;
-
- while (is_locked == 0) {
- cnt = odp_atomic_load_u32(&rwlock->cnt);
- /* waiting for read lock */
- if ((int32_t)cnt < 0) {
- odp_cpu_pause();
- continue;
- }
- is_locked = odp_atomic_cas_acq_u32(&rwlock->cnt,
- &cnt, cnt + 1);
- }
-}
-
-int odp_rwlock_read_trylock(odp_rwlock_t *rwlock)
-{
- uint32_t cnt = odp_atomic_load_u32(&rwlock->cnt);
-
- while (cnt != (uint32_t)-1) {
- if (odp_atomic_cas_acq_u32(&rwlock->cnt, &cnt, cnt + 1))
- return 1;
- }
-
- return 0;
-}
-
-void odp_rwlock_read_unlock(odp_rwlock_t *rwlock)
-{
- odp_atomic_sub_rel_u32(&rwlock->cnt, 1);
-}
-
-void odp_rwlock_write_lock(odp_rwlock_t *rwlock)
-{
- uint32_t cnt;
- int is_locked = 0;
-
- while (is_locked == 0) {
- uint32_t zero = 0;
- cnt = odp_atomic_load_u32(&rwlock->cnt);
- /* lock acquired, wait */
- if (cnt != 0) {
- odp_cpu_pause();
- continue;
- }
- is_locked = odp_atomic_cas_acq_u32(&rwlock->cnt,
- &zero, (uint32_t)-1);
- }
-}
-
-int odp_rwlock_write_trylock(odp_rwlock_t *rwlock)
-{
- uint32_t zero = 0;
-
- return odp_atomic_cas_acq_u32(&rwlock->cnt, &zero, (uint32_t)-1);
-}
-
-void odp_rwlock_write_unlock(odp_rwlock_t *rwlock)
-{
- odp_atomic_store_rel_u32(&rwlock->cnt, 0);
-}
diff --git a/platform/linux-generic/odp_rwlock_api.c b/platform/linux-generic/odp_rwlock_api.c
new file mode 100644
index 000000000..9969473d1
--- /dev/null
+++ b/platform/linux-generic/odp_rwlock_api.c
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/rwlock.h>
+
+#define _ODP_NO_INLINE
+#include <odp/api/plat/rwlock_inlines.h>
diff --git a/platform/linux-generic/odp_rwlock_recursive.c b/platform/linux-generic/odp_rwlock_recursive.c
deleted file mode 100644
index 6b0228143..000000000
--- a/platform/linux-generic/odp_rwlock_recursive.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/rwlock_recursive.h>
-#include <odp/api/thread.h>
-#include <string.h>
-
-#define NO_OWNER (-1)
-
-void odp_rwlock_recursive_init(odp_rwlock_recursive_t *rlock)
-{
- memset(rlock, 0, sizeof(odp_rwlock_recursive_t));
- odp_rwlock_init(&rlock->lock);
- rlock->wr_owner = NO_OWNER;
-}
-
-/* Multiple readers can recurse the lock concurrently */
-void odp_rwlock_recursive_read_lock(odp_rwlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->rd_cnt[thr]) {
- rlock->rd_cnt[thr]++;
- return;
- }
-
- odp_rwlock_read_lock(&rlock->lock);
- rlock->rd_cnt[thr] = 1;
-}
-
-/* Multiple readers can recurse the lock concurrently */
-int odp_rwlock_recursive_read_trylock(odp_rwlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->rd_cnt[thr]) {
- rlock->rd_cnt[thr]++;
- return 1;
- }
-
- if (odp_rwlock_read_trylock(&rlock->lock)) {
- rlock->rd_cnt[thr] = 1;
- return 1;
- }
-
- return 0;
-}
-
-void odp_rwlock_recursive_read_unlock(odp_rwlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- rlock->rd_cnt[thr]--;
-
- if (rlock->rd_cnt[thr] > 0)
- return;
-
- odp_rwlock_read_unlock(&rlock->lock);
-}
-
-/* Only one writer can recurse the lock */
-void odp_rwlock_recursive_write_lock(odp_rwlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->wr_owner == thr) {
- rlock->wr_cnt++;
- return;
- }
-
- odp_rwlock_write_lock(&rlock->lock);
- rlock->wr_owner = thr;
- rlock->wr_cnt = 1;
-}
-
-/* Only one writer can recurse the lock */
-int odp_rwlock_recursive_write_trylock(odp_rwlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->wr_owner == thr) {
- rlock->wr_cnt++;
- return 1;
- }
-
- if (odp_rwlock_write_trylock(&rlock->lock)) {
- rlock->wr_owner = thr;
- rlock->wr_cnt = 1;
- return 1;
- }
-
- return 0;
-}
-
-void odp_rwlock_recursive_write_unlock(odp_rwlock_recursive_t *rlock)
-{
- rlock->wr_cnt--;
-
- if (rlock->wr_cnt > 0)
- return;
-
- rlock->wr_owner = NO_OWNER;
- odp_rwlock_write_unlock(&rlock->lock);
-}
diff --git a/platform/linux-generic/odp_rwlock_recursive_api.c b/platform/linux-generic/odp_rwlock_recursive_api.c
new file mode 100644
index 000000000..b13dd5522
--- /dev/null
+++ b/platform/linux-generic/odp_rwlock_recursive_api.c
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/rwlock_recursive.h>
+
+#define _ODP_NO_INLINE
+#include <odp/api/plat/rwlock_recursive_inlines.h>
diff --git a/platform/linux-generic/odp_schedule.c b/platform/linux-generic/odp_schedule.c
deleted file mode 100644
index 79b21ac76..000000000
--- a/platform/linux-generic/odp_schedule.c
+++ /dev/null
@@ -1,1429 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <string.h>
-#include <odp/api/schedule.h>
-#include <odp_schedule_if.h>
-#include <odp/api/align.h>
-#include <odp/api/shared_memory.h>
-#include <odp_internal.h>
-#include <odp_debug_internal.h>
-#include <odp/api/thread.h>
-#include <odp/api/time.h>
-#include <odp/api/spinlock.h>
-#include <odp/api/hints.h>
-#include <odp/api/cpu.h>
-#include <odp/api/thrmask.h>
-#include <odp_config_internal.h>
-#include <odp_align_internal.h>
-#include <odp/api/sync.h>
-#include <odp/api/packet_io.h>
-#include <odp_ring_internal.h>
-#include <odp_timer_internal.h>
-
-/* Should remove this dependency */
-#include <odp_queue_internal.h>
-
-/* Number of priority levels */
-#define NUM_PRIO 8
-
-ODP_STATIC_ASSERT(ODP_SCHED_PRIO_LOWEST == (NUM_PRIO - 1),
- "lowest_prio_does_not_match_with_num_prios");
-
-ODP_STATIC_ASSERT((ODP_SCHED_PRIO_NORMAL > 0) &&
- (ODP_SCHED_PRIO_NORMAL < (NUM_PRIO - 1)),
- "normal_prio_is_not_between_highest_and_lowest");
-
-/* Number of scheduling groups */
-#define NUM_SCHED_GRPS 32
-
-/* Priority queues per priority */
-#define QUEUES_PER_PRIO 4
-
-/* A thread polls a non preferred sched queue every this many polls
- * of the prefer queue. */
-#define PREFER_RATIO 64
-
-/* Size of poll weight table */
-#define WEIGHT_TBL_SIZE ((QUEUES_PER_PRIO - 1) * PREFER_RATIO)
-
-/* Packet input poll cmd queues */
-#define PKTIO_CMD_QUEUES 4
-
-/* Mask for wrapping command queues */
-#define PKTIO_CMD_QUEUE_MASK (PKTIO_CMD_QUEUES - 1)
-
-/* Maximum number of packet input queues per command */
-#define MAX_PKTIN 16
-
-/* Maximum number of packet IO interfaces */
-#define NUM_PKTIO ODP_CONFIG_PKTIO_ENTRIES
-
-/* Maximum number of pktio poll commands */
-#define NUM_PKTIO_CMD (MAX_PKTIN * NUM_PKTIO)
-
-/* Not a valid index */
-#define NULL_INDEX ((uint32_t)-1)
-
-/* Not a valid poll command */
-#define PKTIO_CMD_INVALID NULL_INDEX
-
-/* Pktio command is free */
-#define PKTIO_CMD_FREE PKTIO_CMD_INVALID
-
-/* Packet IO poll queue ring size. In worst case, all pktios have all pktins
- * enabled and one poll command is created per pktin queue. The ring size must
- * be larger than or equal to NUM_PKTIO_CMD / PKTIO_CMD_QUEUES, so that it can
- * hold all poll commands in the worst case. */
-#define PKTIO_RING_SIZE (NUM_PKTIO_CMD / PKTIO_CMD_QUEUES)
-
-/* Mask for wrapping around pktio poll command index */
-#define PKTIO_RING_MASK (PKTIO_RING_SIZE - 1)
-
-/* Priority queue ring size. In worst case, all event queues are scheduled
- * queues and have the same priority. The ring size must be larger than or
- * equal to ODP_CONFIG_QUEUES / QUEUES_PER_PRIO, so that it can hold all
- * queues in the worst case. */
-#define PRIO_QUEUE_RING_SIZE (ODP_CONFIG_QUEUES / QUEUES_PER_PRIO)
-
-/* Mask for wrapping around priority queue index */
-#define PRIO_QUEUE_MASK (PRIO_QUEUE_RING_SIZE - 1)
-
-/* Priority queue empty, not a valid queue index. */
-#define PRIO_QUEUE_EMPTY NULL_INDEX
-
-/* For best performance, the number of queues should be a power of two. */
-ODP_STATIC_ASSERT(CHECK_IS_POWER2(ODP_CONFIG_QUEUES),
- "Number_of_queues_is_not_power_of_two");
-
-/* Ring size must be power of two, so that MAX_QUEUE_IDX_MASK can be used. */
-ODP_STATIC_ASSERT(CHECK_IS_POWER2(PRIO_QUEUE_RING_SIZE),
- "Ring_size_is_not_power_of_two");
-
-/* Ring size must be power of two, so that PKTIO_RING_MASK can be used. */
-ODP_STATIC_ASSERT(CHECK_IS_POWER2(PKTIO_RING_SIZE),
- "pktio_ring_size_is_not_power_of_two");
-
-/* Number of commands queues must be power of two, so that PKTIO_CMD_QUEUE_MASK
- * can be used. */
-ODP_STATIC_ASSERT(CHECK_IS_POWER2(PKTIO_CMD_QUEUES),
- "pktio_cmd_queues_is_not_power_of_two");
-
-/* Mask of queues per priority */
-typedef uint8_t pri_mask_t;
-
-ODP_STATIC_ASSERT((8 * sizeof(pri_mask_t)) >= QUEUES_PER_PRIO,
- "pri_mask_t_is_too_small");
-
-/* Start of named groups in group mask arrays */
-#define SCHED_GROUP_NAMED (ODP_SCHED_GROUP_CONTROL + 1)
-
-/* Maximum number of dequeues */
-#define MAX_DEQ CONFIG_BURST_SIZE
-
-/* Ordered stash size */
-#define MAX_ORDERED_STASH 512
-
-/* Storage for stashed enqueue operation arguments */
-typedef struct {
- odp_buffer_hdr_t *buf_hdr[QUEUE_MULTI_MAX];
- queue_entry_t *queue_entry;
- int num;
-} ordered_stash_t;
-
-/* Ordered lock states */
-typedef union {
- uint8_t u8[CONFIG_QUEUE_MAX_ORD_LOCKS];
- uint32_t all;
-} lock_called_t;
-
-ODP_STATIC_ASSERT(sizeof(lock_called_t) == sizeof(uint32_t),
- "Lock_called_values_do_not_fit_in_uint32");
-
-/* Scheduler local data */
-typedef struct {
- int thr;
- int num;
- int index;
- int pause;
- uint16_t round;
- uint16_t pktin_polls;
- uint32_t queue_index;
- odp_queue_t queue;
- odp_event_t ev_stash[MAX_DEQ];
- struct {
- /* Source queue index */
- uint32_t src_queue;
- uint64_t ctx; /**< Ordered context id */
- int stash_num; /**< Number of stashed enqueue operations */
- uint8_t in_order; /**< Order status */
- lock_called_t lock_called; /**< States of ordered locks */
- /** Storage for stashed enqueue operations */
- ordered_stash_t stash[MAX_ORDERED_STASH];
- } ordered;
-
- uint32_t grp_epoch;
- int num_grp;
- uint8_t grp[NUM_SCHED_GRPS];
- uint8_t weight_tbl[WEIGHT_TBL_SIZE];
- uint8_t grp_weight[WEIGHT_TBL_SIZE];
-
-} sched_local_t;
-
-/* Priority queue */
-typedef struct {
- /* Ring header */
- ring_t ring;
-
- /* Ring data: queue indexes */
- uint32_t queue_index[PRIO_QUEUE_RING_SIZE];
-
-} prio_queue_t ODP_ALIGNED_CACHE;
-
-/* Packet IO queue */
-typedef struct {
- /* Ring header */
- ring_t ring;
-
- /* Ring data: pktio poll command indexes */
- uint32_t cmd_index[PKTIO_RING_SIZE];
-
-} pktio_queue_t ODP_ALIGNED_CACHE;
-
-/* Packet IO poll command */
-typedef struct {
- int pktio_index;
- int num_pktin;
- int pktin[MAX_PKTIN];
- uint32_t cmd_index;
-} pktio_cmd_t;
-
-/* Order context of a queue */
-typedef struct {
- /* Current ordered context id */
- odp_atomic_u64_t ctx ODP_ALIGNED_CACHE;
-
- /* Next unallocated context id */
- odp_atomic_u64_t next_ctx;
-
- /* Array of ordered locks */
- odp_atomic_u64_t lock[CONFIG_QUEUE_MAX_ORD_LOCKS];
-
-} order_context_t ODP_ALIGNED_CACHE;
-
-typedef struct {
- pri_mask_t pri_mask[NUM_PRIO];
- odp_spinlock_t mask_lock;
-
- prio_queue_t prio_q[NUM_SCHED_GRPS][NUM_PRIO][QUEUES_PER_PRIO];
-
- odp_spinlock_t poll_cmd_lock;
- /* Number of commands in a command queue */
- uint16_t num_pktio_cmd[PKTIO_CMD_QUEUES];
-
- /* Packet IO command queues */
- pktio_queue_t pktio_q[PKTIO_CMD_QUEUES];
-
- /* Packet IO poll commands */
- pktio_cmd_t pktio_cmd[NUM_PKTIO_CMD];
-
- odp_shm_t shm;
- uint32_t pri_count[NUM_PRIO][QUEUES_PER_PRIO];
-
- odp_thrmask_t mask_all;
- odp_spinlock_t grp_lock;
- odp_atomic_u32_t grp_epoch;
-
- struct {
- char name[ODP_SCHED_GROUP_NAME_LEN];
- odp_thrmask_t mask;
- int allocated;
- } sched_grp[NUM_SCHED_GRPS];
-
- struct {
- int grp;
- int prio;
- int queue_per_prio;
- int sync;
- unsigned order_lock_count;
- } queue[ODP_CONFIG_QUEUES];
-
- struct {
- /* Number of active commands for a pktio interface */
- int num_cmd;
- } pktio[NUM_PKTIO];
-
- order_context_t order[ODP_CONFIG_QUEUES];
-
-} sched_global_t;
-
-/* Global scheduler context */
-static sched_global_t *sched;
-
-/* Thread local scheduler context */
-static __thread sched_local_t sched_local;
-
-/* Function prototypes */
-static inline void schedule_release_context(void);
-
-static void sched_local_init(void)
-{
- int i;
- uint8_t id;
- uint8_t offset = 0;
-
- memset(&sched_local, 0, sizeof(sched_local_t));
-
- sched_local.thr = odp_thread_id();
- sched_local.queue = ODP_QUEUE_INVALID;
- sched_local.queue_index = PRIO_QUEUE_EMPTY;
- sched_local.ordered.src_queue = NULL_INDEX;
-
- id = sched_local.thr & (QUEUES_PER_PRIO - 1);
-
- for (i = 0; i < WEIGHT_TBL_SIZE; i++) {
- sched_local.weight_tbl[i] = id;
-
- if (i % PREFER_RATIO == 0) {
- offset++;
- sched_local.weight_tbl[i] = (id + offset) &
- (QUEUES_PER_PRIO - 1);
- if (offset == QUEUES_PER_PRIO - 1)
- offset = 0;
- }
- }
-}
-
-static int schedule_init_global(void)
-{
- odp_shm_t shm;
- int i, j, grp;
-
- ODP_DBG("Schedule init ... ");
-
- shm = odp_shm_reserve("odp_scheduler",
- sizeof(sched_global_t),
- ODP_CACHE_LINE_SIZE, 0);
-
- sched = odp_shm_addr(shm);
-
- if (sched == NULL) {
- ODP_ERR("Schedule init: Shm reserve failed.\n");
- return -1;
- }
-
- memset(sched, 0, sizeof(sched_global_t));
-
- sched->shm = shm;
- odp_spinlock_init(&sched->mask_lock);
-
- for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
- for (i = 0; i < NUM_PRIO; i++) {
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- prio_queue_t *prio_q;
- int k;
-
- prio_q = &sched->prio_q[grp][i][j];
- ring_init(&prio_q->ring);
-
- for (k = 0; k < PRIO_QUEUE_RING_SIZE; k++) {
- prio_q->queue_index[k] =
- PRIO_QUEUE_EMPTY;
- }
- }
- }
- }
-
- odp_spinlock_init(&sched->poll_cmd_lock);
- for (i = 0; i < PKTIO_CMD_QUEUES; i++) {
- ring_init(&sched->pktio_q[i].ring);
-
- for (j = 0; j < PKTIO_RING_SIZE; j++)
- sched->pktio_q[i].cmd_index[j] = PKTIO_CMD_INVALID;
- }
-
- for (i = 0; i < NUM_PKTIO_CMD; i++)
- sched->pktio_cmd[i].cmd_index = PKTIO_CMD_FREE;
-
- odp_spinlock_init(&sched->grp_lock);
- odp_atomic_init_u32(&sched->grp_epoch, 0);
-
- for (i = 0; i < NUM_SCHED_GRPS; i++) {
- memset(sched->sched_grp[i].name, 0, ODP_SCHED_GROUP_NAME_LEN);
- odp_thrmask_zero(&sched->sched_grp[i].mask);
- }
-
- sched->sched_grp[ODP_SCHED_GROUP_ALL].allocated = 1;
- sched->sched_grp[ODP_SCHED_GROUP_WORKER].allocated = 1;
- sched->sched_grp[ODP_SCHED_GROUP_CONTROL].allocated = 1;
-
- odp_thrmask_setall(&sched->mask_all);
-
- ODP_DBG("done\n");
-
- return 0;
-}
-
-static inline void queue_destroy_finalize(uint32_t qi)
-{
- sched_cb_queue_destroy_finalize(qi);
-}
-
-static int schedule_term_global(void)
-{
- int ret = 0;
- int rc = 0;
- int i, j, grp;
-
- for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
- for (i = 0; i < NUM_PRIO; i++) {
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- ring_t *ring = &sched->prio_q[grp][i][j].ring;
- uint32_t qi;
-
- while ((qi = ring_deq(ring, PRIO_QUEUE_MASK)) !=
- RING_EMPTY) {
- odp_event_t events[1];
- int num;
-
- num = sched_cb_queue_deq_multi(qi,
- events,
- 1);
-
- if (num < 0)
- queue_destroy_finalize(qi);
-
- if (num > 0)
- ODP_ERR("Queue not empty\n");
- }
- }
- }
- }
-
- ret = odp_shm_free(sched->shm);
- if (ret < 0) {
- ODP_ERR("Shm free failed for odp_scheduler");
- rc = -1;
- }
-
- return rc;
-}
-
-static int schedule_init_local(void)
-{
- sched_local_init();
- return 0;
-}
-
-static int schedule_term_local(void)
-{
- if (sched_local.num) {
- ODP_ERR("Locally pre-scheduled events exist.\n");
- return -1;
- }
-
- schedule_release_context();
- return 0;
-}
-
-static inline void grp_update_mask(int grp, const odp_thrmask_t *new_mask)
-{
- odp_thrmask_copy(&sched->sched_grp[grp].mask, new_mask);
- odp_atomic_add_rel_u32(&sched->grp_epoch, 1);
-}
-
-static inline int grp_update_tbl(void)
-{
- int i;
- int num = 0;
- int thr = sched_local.thr;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- for (i = 0; i < NUM_SCHED_GRPS; i++) {
- if (sched->sched_grp[i].allocated == 0)
- continue;
-
- if (odp_thrmask_isset(&sched->sched_grp[i].mask, thr)) {
- sched_local.grp[num] = i;
- num++;
- }
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
-
- /* Update group weights. Round robin over all thread's groups. */
- for (i = 0; i < WEIGHT_TBL_SIZE; i++)
- sched_local.grp_weight[i] = i % num;
-
- sched_local.num_grp = num;
- return num;
-}
-
-static unsigned schedule_max_ordered_locks(void)
-{
- return CONFIG_QUEUE_MAX_ORD_LOCKS;
-}
-
-static inline int queue_per_prio(uint32_t queue_index)
-{
- return ((QUEUES_PER_PRIO - 1) & queue_index);
-}
-
-static void pri_set(int id, int prio)
-{
- odp_spinlock_lock(&sched->mask_lock);
- sched->pri_mask[prio] |= 1 << id;
- sched->pri_count[prio][id]++;
- odp_spinlock_unlock(&sched->mask_lock);
-}
-
-static void pri_clr(int id, int prio)
-{
- odp_spinlock_lock(&sched->mask_lock);
-
- /* Clear mask bit when last queue is removed*/
- sched->pri_count[prio][id]--;
-
- if (sched->pri_count[prio][id] == 0)
- sched->pri_mask[prio] &= (uint8_t)(~(1 << id));
-
- odp_spinlock_unlock(&sched->mask_lock);
-}
-
-static void pri_set_queue(uint32_t queue_index, int prio)
-{
- int id = queue_per_prio(queue_index);
-
- return pri_set(id, prio);
-}
-
-static void pri_clr_queue(uint32_t queue_index, int prio)
-{
- int id = queue_per_prio(queue_index);
- pri_clr(id, prio);
-}
-
-static int schedule_init_queue(uint32_t queue_index,
- const odp_schedule_param_t *sched_param)
-{
- int i;
- int prio = sched_param->prio;
-
- pri_set_queue(queue_index, prio);
- sched->queue[queue_index].grp = sched_param->group;
- sched->queue[queue_index].prio = prio;
- sched->queue[queue_index].queue_per_prio = queue_per_prio(queue_index);
- sched->queue[queue_index].sync = sched_param->sync;
- sched->queue[queue_index].order_lock_count = sched_param->lock_count;
-
- odp_atomic_init_u64(&sched->order[queue_index].ctx, 0);
- odp_atomic_init_u64(&sched->order[queue_index].next_ctx, 0);
-
- for (i = 0; i < CONFIG_QUEUE_MAX_ORD_LOCKS; i++)
- odp_atomic_init_u64(&sched->order[queue_index].lock[i], 0);
-
- return 0;
-}
-
-static inline int queue_is_atomic(uint32_t queue_index)
-{
- return sched->queue[queue_index].sync == ODP_SCHED_SYNC_ATOMIC;
-}
-
-static inline int queue_is_ordered(uint32_t queue_index)
-{
- return sched->queue[queue_index].sync == ODP_SCHED_SYNC_ORDERED;
-}
-
-static void schedule_destroy_queue(uint32_t queue_index)
-{
- int prio = sched->queue[queue_index].prio;
-
- pri_clr_queue(queue_index, prio);
- sched->queue[queue_index].grp = 0;
- sched->queue[queue_index].prio = 0;
- sched->queue[queue_index].queue_per_prio = 0;
-
- if (queue_is_ordered(queue_index) &&
- odp_atomic_load_u64(&sched->order[queue_index].ctx) !=
- odp_atomic_load_u64(&sched->order[queue_index].next_ctx))
- ODP_ERR("queue reorder incomplete\n");
-}
-
-static int poll_cmd_queue_idx(int pktio_index, int pktin_idx)
-{
- return PKTIO_CMD_QUEUE_MASK & (pktio_index ^ pktin_idx);
-}
-
-static inline pktio_cmd_t *alloc_pktio_cmd(void)
-{
- int i;
- pktio_cmd_t *cmd = NULL;
-
- odp_spinlock_lock(&sched->poll_cmd_lock);
-
- /* Find next free command */
- for (i = 0; i < NUM_PKTIO_CMD; i++) {
- if (sched->pktio_cmd[i].cmd_index == PKTIO_CMD_FREE) {
- cmd = &sched->pktio_cmd[i];
- cmd->cmd_index = i;
- break;
- }
- }
-
- odp_spinlock_unlock(&sched->poll_cmd_lock);
-
- return cmd;
-}
-
-static inline void free_pktio_cmd(pktio_cmd_t *cmd)
-{
- odp_spinlock_lock(&sched->poll_cmd_lock);
-
- cmd->cmd_index = PKTIO_CMD_FREE;
-
- odp_spinlock_unlock(&sched->poll_cmd_lock);
-}
-
-static void schedule_pktio_start(int pktio_index, int num_pktin,
- int pktin_idx[])
-{
- int i, idx;
- pktio_cmd_t *cmd;
-
- if (num_pktin > MAX_PKTIN)
- ODP_ABORT("Too many input queues for scheduler\n");
-
- sched->pktio[pktio_index].num_cmd = num_pktin;
-
- /* Create a pktio poll command per queue */
- for (i = 0; i < num_pktin; i++) {
-
- cmd = alloc_pktio_cmd();
-
- if (cmd == NULL)
- ODP_ABORT("Scheduler out of pktio commands\n");
-
- idx = poll_cmd_queue_idx(pktio_index, pktin_idx[i]);
-
- odp_spinlock_lock(&sched->poll_cmd_lock);
- sched->num_pktio_cmd[idx]++;
- odp_spinlock_unlock(&sched->poll_cmd_lock);
-
- cmd->pktio_index = pktio_index;
- cmd->num_pktin = 1;
- cmd->pktin[0] = pktin_idx[i];
- ring_enq(&sched->pktio_q[idx].ring, PKTIO_RING_MASK,
- cmd->cmd_index);
- }
-}
-
-static int schedule_pktio_stop(int pktio_index, int first_pktin)
-{
- int num;
- int idx = poll_cmd_queue_idx(pktio_index, first_pktin);
-
- odp_spinlock_lock(&sched->poll_cmd_lock);
- sched->num_pktio_cmd[idx]--;
- sched->pktio[pktio_index].num_cmd--;
- num = sched->pktio[pktio_index].num_cmd;
- odp_spinlock_unlock(&sched->poll_cmd_lock);
-
- return num;
-}
-
-static void schedule_release_atomic(void)
-{
- uint32_t qi = sched_local.queue_index;
-
- if (qi != PRIO_QUEUE_EMPTY && sched_local.num == 0) {
- int grp = sched->queue[qi].grp;
- int prio = sched->queue[qi].prio;
- int queue_per_prio = sched->queue[qi].queue_per_prio;
- ring_t *ring = &sched->prio_q[grp][prio][queue_per_prio].ring;
-
- /* Release current atomic queue */
- ring_enq(ring, PRIO_QUEUE_MASK, qi);
- sched_local.queue_index = PRIO_QUEUE_EMPTY;
- }
-}
-
-static inline int ordered_own_turn(uint32_t queue_index)
-{
- uint64_t ctx;
-
- ctx = odp_atomic_load_acq_u64(&sched->order[queue_index].ctx);
-
- return ctx == sched_local.ordered.ctx;
-}
-
-static inline void wait_for_order(uint32_t queue_index)
-{
- /* Busy loop to synchronize ordered processing */
- while (1) {
- if (ordered_own_turn(queue_index))
- break;
- odp_cpu_pause();
- }
-}
-
-/**
- * Perform stashed enqueue operations
- *
- * Should be called only when already in order.
- */
-static inline void ordered_stash_release(void)
-{
- int i;
-
- for (i = 0; i < sched_local.ordered.stash_num; i++) {
- queue_entry_t *queue_entry;
- odp_buffer_hdr_t **buf_hdr;
- int num;
-
- queue_entry = sched_local.ordered.stash[i].queue_entry;
- buf_hdr = sched_local.ordered.stash[i].buf_hdr;
- num = sched_local.ordered.stash[i].num;
-
- queue_fn->enq_multi(qentry_to_int(queue_entry), buf_hdr, num);
- }
- sched_local.ordered.stash_num = 0;
-}
-
-static inline void release_ordered(void)
-{
- uint32_t qi;
- unsigned i;
-
- qi = sched_local.ordered.src_queue;
-
- wait_for_order(qi);
-
- /* Release all ordered locks */
- for (i = 0; i < sched->queue[qi].order_lock_count; i++) {
- if (!sched_local.ordered.lock_called.u8[i])
- odp_atomic_store_rel_u64(&sched->order[qi].lock[i],
- sched_local.ordered.ctx + 1);
- }
-
- sched_local.ordered.lock_called.all = 0;
- sched_local.ordered.src_queue = NULL_INDEX;
- sched_local.ordered.in_order = 0;
-
- ordered_stash_release();
-
- /* Next thread can continue processing */
- odp_atomic_add_rel_u64(&sched->order[qi].ctx, 1);
-}
-
-static void schedule_release_ordered(void)
-{
- uint32_t queue_index;
-
- queue_index = sched_local.ordered.src_queue;
-
- if (odp_unlikely((queue_index == NULL_INDEX) || sched_local.num))
- return;
-
- release_ordered();
-}
-
-static inline void schedule_release_context(void)
-{
- if (sched_local.ordered.src_queue != NULL_INDEX)
- release_ordered();
- else
- schedule_release_atomic();
-}
-
-static inline int copy_events(odp_event_t out_ev[], unsigned int max)
-{
- int i = 0;
-
- while (sched_local.num && max) {
- out_ev[i] = sched_local.ev_stash[sched_local.index];
- sched_local.index++;
- sched_local.num--;
- max--;
- i++;
- }
-
- return i;
-}
-
-static int schedule_ord_enq_multi(queue_t q_int, void *buf_hdr[],
- int num, int *ret)
-{
- int i;
- uint32_t stash_num = sched_local.ordered.stash_num;
- queue_entry_t *dst_queue = qentry_from_int(q_int);
- uint32_t src_queue = sched_local.ordered.src_queue;
-
- if ((src_queue == NULL_INDEX) || sched_local.ordered.in_order)
- return 0;
-
- if (ordered_own_turn(src_queue)) {
- /* Own turn, so can do enqueue directly. */
- sched_local.ordered.in_order = 1;
- ordered_stash_release();
- return 0;
- }
-
- /* Pktout may drop packets, so the operation cannot be stashed. */
- if (dst_queue->s.pktout.pktio != ODP_PKTIO_INVALID ||
- odp_unlikely(stash_num >= MAX_ORDERED_STASH)) {
- /* If the local stash is full, wait until it is our turn and
- * then release the stash and do enqueue directly. */
- wait_for_order(src_queue);
-
- sched_local.ordered.in_order = 1;
-
- ordered_stash_release();
- return 0;
- }
-
- sched_local.ordered.stash[stash_num].queue_entry = dst_queue;
- sched_local.ordered.stash[stash_num].num = num;
- for (i = 0; i < num; i++)
- sched_local.ordered.stash[stash_num].buf_hdr[i] = buf_hdr[i];
-
- sched_local.ordered.stash_num++;
-
- *ret = num;
- return 1;
-}
-
-static inline int do_schedule_grp(odp_queue_t *out_queue, odp_event_t out_ev[],
- unsigned int max_num, int grp, int first)
-{
- int prio, i;
- int ret;
- int id;
- unsigned int max_deq = MAX_DEQ;
- uint32_t qi;
-
- /* Schedule events */
- for (prio = 0; prio < NUM_PRIO; prio++) {
-
- if (sched->pri_mask[prio] == 0)
- continue;
-
- /* Select the first ring based on weights */
- id = first;
-
- for (i = 0; i < QUEUES_PER_PRIO;) {
- int num;
- int ordered;
- odp_queue_t handle;
- ring_t *ring;
-
- if (id >= QUEUES_PER_PRIO)
- id = 0;
-
- /* No queues created for this priority queue */
- if (odp_unlikely((sched->pri_mask[prio] & (1 << id))
- == 0)) {
- i++;
- id++;
- continue;
- }
-
- /* Get queue index from the priority queue */
- ring = &sched->prio_q[grp][prio][id].ring;
- qi = ring_deq(ring, PRIO_QUEUE_MASK);
-
- /* Priority queue empty */
- if (qi == RING_EMPTY) {
- i++;
- id++;
- continue;
- }
-
- /* Low priorities have smaller batch size to limit
- * head of line blocking latency. */
- if (odp_unlikely(MAX_DEQ > 1 &&
- prio > ODP_SCHED_PRIO_DEFAULT))
- max_deq = MAX_DEQ / 2;
-
- ordered = queue_is_ordered(qi);
-
- /* Do not cache ordered events locally to improve
- * parallelism. Ordered context can only be released
- * when the local cache is empty. */
- if (ordered && max_num < MAX_DEQ)
- max_deq = max_num;
-
- num = sched_cb_queue_deq_multi(qi, sched_local.ev_stash,
- max_deq);
-
- if (num < 0) {
- /* Destroyed queue. Continue scheduling the same
- * priority queue. */
- sched_cb_queue_destroy_finalize(qi);
- continue;
- }
-
- if (num == 0) {
- /* Remove empty queue from scheduling. Continue
- * scheduling the same priority queue. */
- continue;
- }
-
- handle = sched_cb_queue_handle(qi);
- sched_local.num = num;
- sched_local.index = 0;
- sched_local.queue = handle;
- ret = copy_events(out_ev, max_num);
-
- if (ordered) {
- uint64_t ctx;
- odp_atomic_u64_t *next_ctx;
-
- next_ctx = &sched->order[qi].next_ctx;
- ctx = odp_atomic_fetch_inc_u64(next_ctx);
-
- sched_local.ordered.ctx = ctx;
- sched_local.ordered.src_queue = qi;
-
- /* Continue scheduling ordered queues */
- ring_enq(ring, PRIO_QUEUE_MASK, qi);
-
- } else if (queue_is_atomic(qi)) {
- /* Hold queue during atomic access */
- sched_local.queue_index = qi;
- } else {
- /* Continue scheduling the queue */
- ring_enq(ring, PRIO_QUEUE_MASK, qi);
- }
-
- /* Output the source queue handle */
- if (out_queue)
- *out_queue = handle;
-
- return ret;
- }
- }
-
- return 0;
-}
-
-/*
- * Schedule queues
- */
-static inline int do_schedule(odp_queue_t *out_queue, odp_event_t out_ev[],
- unsigned int max_num)
-{
- int i, num_grp;
- int ret;
- int id, first, grp_id;
- uint16_t round;
- uint32_t epoch;
-
- if (sched_local.num) {
- ret = copy_events(out_ev, max_num);
-
- if (out_queue)
- *out_queue = sched_local.queue;
-
- return ret;
- }
-
- schedule_release_context();
-
- if (odp_unlikely(sched_local.pause))
- return 0;
-
- /* Each thread prefers a priority queue. Poll weight table avoids
- * starvation of other priority queues on low thread counts. */
- round = sched_local.round + 1;
-
- if (odp_unlikely(round == WEIGHT_TBL_SIZE))
- round = 0;
-
- sched_local.round = round;
- first = sched_local.weight_tbl[round];
-
- epoch = odp_atomic_load_acq_u32(&sched->grp_epoch);
- num_grp = sched_local.num_grp;
-
- if (odp_unlikely(sched_local.grp_epoch != epoch)) {
- num_grp = grp_update_tbl();
- sched_local.grp_epoch = epoch;
- }
-
- grp_id = sched_local.grp_weight[round];
-
- /* Schedule queues per group and priority */
- for (i = 0; i < num_grp; i++) {
- int grp;
-
- grp = sched_local.grp[grp_id];
- ret = do_schedule_grp(out_queue, out_ev, max_num, grp, first);
-
- if (odp_likely(ret))
- return ret;
-
- grp_id++;
- if (odp_unlikely(grp_id >= num_grp))
- grp_id = 0;
- }
-
- /*
- * Poll packet input when there are no events
- * * Each thread starts the search for a poll command from its
- * preferred command queue. If the queue is empty, it moves to other
- * queues.
- * * Most of the times, the search stops on the first command found to
- * optimize multi-threaded performance. A small portion of polls
- * have to do full iteration to avoid packet input starvation when
- * there are less threads than command queues.
- */
- id = sched_local.thr & PKTIO_CMD_QUEUE_MASK;
-
- for (i = 0; i < PKTIO_CMD_QUEUES; i++, id = ((id + 1) &
- PKTIO_CMD_QUEUE_MASK)) {
- ring_t *ring;
- uint32_t cmd_index;
- pktio_cmd_t *cmd;
-
- if (odp_unlikely(sched->num_pktio_cmd[id] == 0))
- continue;
-
- ring = &sched->pktio_q[id].ring;
- cmd_index = ring_deq(ring, PKTIO_RING_MASK);
-
- if (odp_unlikely(cmd_index == RING_EMPTY))
- continue;
-
- cmd = &sched->pktio_cmd[cmd_index];
-
- /* Poll packet input */
- if (odp_unlikely(sched_cb_pktin_poll(cmd->pktio_index,
- cmd->num_pktin,
- cmd->pktin))){
- /* Pktio stopped or closed. Remove poll command and call
- * stop_finalize when all commands of the pktio has
- * been removed. */
- if (schedule_pktio_stop(cmd->pktio_index,
- cmd->pktin[0]) == 0)
- sched_cb_pktio_stop_finalize(cmd->pktio_index);
-
- free_pktio_cmd(cmd);
- } else {
- /* Continue scheduling the pktio */
- ring_enq(ring, PKTIO_RING_MASK, cmd_index);
-
- /* Do not iterate through all pktin poll command queues
- * every time. */
- if (odp_likely(sched_local.pktin_polls & 0xf))
- break;
- }
- }
-
- sched_local.pktin_polls++;
- return 0;
-}
-
-
-static int schedule_loop(odp_queue_t *out_queue, uint64_t wait,
- odp_event_t out_ev[],
- unsigned int max_num)
-{
- odp_time_t next, wtime;
- int first = 1;
- int ret;
-
- while (1) {
- ret = do_schedule(out_queue, out_ev, max_num);
-
- if (ret)
- break;
-
- if (wait == ODP_SCHED_WAIT)
- continue;
-
- if (wait == ODP_SCHED_NO_WAIT)
- break;
-
- if (first) {
- wtime = odp_time_local_from_ns(wait);
- next = odp_time_sum(odp_time_local(), wtime);
- first = 0;
- continue;
- }
-
- if (odp_time_cmp(next, odp_time_local()) < 0)
- break;
- }
-
- return ret;
-}
-
-static odp_event_t schedule(odp_queue_t *out_queue, uint64_t wait)
-{
- odp_event_t ev;
-
- ev = ODP_EVENT_INVALID;
-
- schedule_loop(out_queue, wait, &ev, 1);
-
- return ev;
-}
-
-static int schedule_multi(odp_queue_t *out_queue, uint64_t wait,
- odp_event_t events[], int num)
-{
- return schedule_loop(out_queue, wait, events, num);
-}
-
-static inline void order_lock(void)
-{
- uint32_t queue_index;
-
- queue_index = sched_local.ordered.src_queue;
-
- if (queue_index == NULL_INDEX)
- return;
-
- wait_for_order(queue_index);
-}
-
-static void order_unlock(void)
-{
-}
-
-static void schedule_order_lock(unsigned lock_index)
-{
- odp_atomic_u64_t *ord_lock;
- uint32_t queue_index;
-
- queue_index = sched_local.ordered.src_queue;
-
- ODP_ASSERT(queue_index != NULL_INDEX &&
- lock_index <= sched->queue[queue_index].order_lock_count &&
- !sched_local.ordered.lock_called.u8[lock_index]);
-
- ord_lock = &sched->order[queue_index].lock[lock_index];
-
- /* Busy loop to synchronize ordered processing */
- while (1) {
- uint64_t lock_seq;
-
- lock_seq = odp_atomic_load_acq_u64(ord_lock);
-
- if (lock_seq == sched_local.ordered.ctx) {
- sched_local.ordered.lock_called.u8[lock_index] = 1;
- return;
- }
- odp_cpu_pause();
- }
-}
-
-static void schedule_order_unlock(unsigned lock_index)
-{
- odp_atomic_u64_t *ord_lock;
- uint32_t queue_index;
-
- queue_index = sched_local.ordered.src_queue;
-
- ODP_ASSERT(queue_index != NULL_INDEX &&
- lock_index <= sched->queue[queue_index].order_lock_count);
-
- ord_lock = &sched->order[queue_index].lock[lock_index];
-
- ODP_ASSERT(sched_local.ordered.ctx == odp_atomic_load_u64(ord_lock));
-
- odp_atomic_store_rel_u64(ord_lock, sched_local.ordered.ctx + 1);
-}
-
-static void schedule_pause(void)
-{
- sched_local.pause = 1;
-}
-
-static void schedule_resume(void)
-{
- sched_local.pause = 0;
-}
-
-static uint64_t schedule_wait_time(uint64_t ns)
-{
- return ns;
-}
-
-static int schedule_num_prio(void)
-{
- return NUM_PRIO;
-}
-
-static odp_schedule_group_t schedule_group_create(const char *name,
- const odp_thrmask_t *mask)
-{
- odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
- int i;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- for (i = SCHED_GROUP_NAMED; i < NUM_SCHED_GRPS; i++) {
- if (!sched->sched_grp[i].allocated) {
- char *grp_name = sched->sched_grp[i].name;
-
- if (name == NULL) {
- grp_name[0] = 0;
- } else {
- strncpy(grp_name, name,
- ODP_SCHED_GROUP_NAME_LEN - 1);
- grp_name[ODP_SCHED_GROUP_NAME_LEN - 1] = 0;
- }
-
- grp_update_mask(i, mask);
- group = (odp_schedule_group_t)i;
- sched->sched_grp[i].allocated = 1;
- break;
- }
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return group;
-}
-
-static int schedule_group_destroy(odp_schedule_group_t group)
-{
- odp_thrmask_t zero;
- int ret;
-
- odp_thrmask_zero(&zero);
-
- odp_spinlock_lock(&sched->grp_lock);
-
- if (group < NUM_SCHED_GRPS && group >= SCHED_GROUP_NAMED &&
- sched->sched_grp[group].allocated) {
- grp_update_mask(group, &zero);
- memset(sched->sched_grp[group].name, 0,
- ODP_SCHED_GROUP_NAME_LEN);
- sched->sched_grp[group].allocated = 0;
- ret = 0;
- } else {
- ret = -1;
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return ret;
-}
-
-static odp_schedule_group_t schedule_group_lookup(const char *name)
-{
- odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
- int i;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- for (i = SCHED_GROUP_NAMED; i < NUM_SCHED_GRPS; i++) {
- if (strcmp(name, sched->sched_grp[i].name) == 0) {
- group = (odp_schedule_group_t)i;
- break;
- }
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return group;
-}
-
-static int schedule_group_join(odp_schedule_group_t group,
- const odp_thrmask_t *mask)
-{
- int ret;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- if (group < NUM_SCHED_GRPS && group >= SCHED_GROUP_NAMED &&
- sched->sched_grp[group].allocated) {
- odp_thrmask_t new_mask;
-
- odp_thrmask_or(&new_mask, &sched->sched_grp[group].mask, mask);
- grp_update_mask(group, &new_mask);
-
- ret = 0;
- } else {
- ret = -1;
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return ret;
-}
-
-static int schedule_group_leave(odp_schedule_group_t group,
- const odp_thrmask_t *mask)
-{
- odp_thrmask_t new_mask;
- int ret;
-
- odp_thrmask_xor(&new_mask, mask, &sched->mask_all);
-
- odp_spinlock_lock(&sched->grp_lock);
-
- if (group < NUM_SCHED_GRPS && group >= SCHED_GROUP_NAMED &&
- sched->sched_grp[group].allocated) {
- odp_thrmask_and(&new_mask, &sched->sched_grp[group].mask,
- &new_mask);
- grp_update_mask(group, &new_mask);
-
- ret = 0;
- } else {
- ret = -1;
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return ret;
-}
-
-static int schedule_group_thrmask(odp_schedule_group_t group,
- odp_thrmask_t *thrmask)
-{
- int ret;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- if (group < NUM_SCHED_GRPS && group >= SCHED_GROUP_NAMED &&
- sched->sched_grp[group].allocated) {
- *thrmask = sched->sched_grp[group].mask;
- ret = 0;
- } else {
- ret = -1;
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return ret;
-}
-
-static int schedule_group_info(odp_schedule_group_t group,
- odp_schedule_group_info_t *info)
-{
- int ret;
-
- odp_spinlock_lock(&sched->grp_lock);
-
- if (group < NUM_SCHED_GRPS && group >= SCHED_GROUP_NAMED &&
- sched->sched_grp[group].allocated) {
- info->name = sched->sched_grp[group].name;
- info->thrmask = sched->sched_grp[group].mask;
- ret = 0;
- } else {
- ret = -1;
- }
-
- odp_spinlock_unlock(&sched->grp_lock);
- return ret;
-}
-
-static int schedule_thr_add(odp_schedule_group_t group, int thr)
-{
- odp_thrmask_t mask;
- odp_thrmask_t new_mask;
-
- if (group < 0 || group >= SCHED_GROUP_NAMED)
- return -1;
-
- odp_thrmask_zero(&mask);
- odp_thrmask_set(&mask, thr);
-
- odp_spinlock_lock(&sched->grp_lock);
-
- odp_thrmask_or(&new_mask, &sched->sched_grp[group].mask, &mask);
- grp_update_mask(group, &new_mask);
-
- odp_spinlock_unlock(&sched->grp_lock);
-
- return 0;
-}
-
-static int schedule_thr_rem(odp_schedule_group_t group, int thr)
-{
- odp_thrmask_t mask;
- odp_thrmask_t new_mask;
-
- if (group < 0 || group >= SCHED_GROUP_NAMED)
- return -1;
-
- odp_thrmask_zero(&mask);
- odp_thrmask_set(&mask, thr);
- odp_thrmask_xor(&new_mask, &mask, &sched->mask_all);
-
- odp_spinlock_lock(&sched->grp_lock);
-
- odp_thrmask_and(&new_mask, &sched->sched_grp[group].mask, &new_mask);
- grp_update_mask(group, &new_mask);
-
- odp_spinlock_unlock(&sched->grp_lock);
-
- return 0;
-}
-
-/* This function is a no-op */
-static void schedule_prefetch(int num ODP_UNUSED)
-{
-}
-
-static int schedule_sched_queue(uint32_t queue_index)
-{
- int grp = sched->queue[queue_index].grp;
- int prio = sched->queue[queue_index].prio;
- int queue_per_prio = sched->queue[queue_index].queue_per_prio;
- ring_t *ring = &sched->prio_q[grp][prio][queue_per_prio].ring;
-
- ring_enq(ring, PRIO_QUEUE_MASK, queue_index);
- return 0;
-}
-
-static int schedule_num_grps(void)
-{
- return NUM_SCHED_GRPS;
-}
-
-/* Fill in scheduler interface */
-const schedule_fn_t schedule_default_fn = {
- .status_sync = 0,
- .pktio_start = schedule_pktio_start,
- .thr_add = schedule_thr_add,
- .thr_rem = schedule_thr_rem,
- .num_grps = schedule_num_grps,
- .init_queue = schedule_init_queue,
- .destroy_queue = schedule_destroy_queue,
- .sched_queue = schedule_sched_queue,
- .ord_enq_multi = schedule_ord_enq_multi,
- .init_global = schedule_init_global,
- .term_global = schedule_term_global,
- .init_local = schedule_init_local,
- .term_local = schedule_term_local,
- .order_lock = order_lock,
- .order_unlock = order_unlock,
- .max_ordered_locks = schedule_max_ordered_locks,
- .unsched_queue = NULL,
- .save_context = NULL
-};
-
-/* Fill in scheduler API calls */
-const schedule_api_t schedule_default_api = {
- .schedule_wait_time = schedule_wait_time,
- .schedule = schedule,
- .schedule_multi = schedule_multi,
- .schedule_pause = schedule_pause,
- .schedule_resume = schedule_resume,
- .schedule_release_atomic = schedule_release_atomic,
- .schedule_release_ordered = schedule_release_ordered,
- .schedule_prefetch = schedule_prefetch,
- .schedule_num_prio = schedule_num_prio,
- .schedule_group_create = schedule_group_create,
- .schedule_group_destroy = schedule_group_destroy,
- .schedule_group_lookup = schedule_group_lookup,
- .schedule_group_join = schedule_group_join,
- .schedule_group_leave = schedule_group_leave,
- .schedule_group_thrmask = schedule_group_thrmask,
- .schedule_group_info = schedule_group_info,
- .schedule_order_lock = schedule_order_lock,
- .schedule_order_unlock = schedule_order_unlock
-};
diff --git a/platform/linux-generic/odp_schedule_api.c b/platform/linux-generic/odp_schedule_api.c
new file mode 100644
index 000000000..1a758405a
--- /dev/null
+++ b/platform/linux-generic/odp_schedule_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/schedule.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/schedule_inlines.h>
diff --git a/platform/linux-generic/odp_schedule_basic.c b/platform/linux-generic/odp_schedule_basic.c
new file mode 100644
index 000000000..69fbf7d6f
--- /dev/null
+++ b/platform/linux-generic/odp_schedule_basic.c
@@ -0,0 +1,2407 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+/*
+ * Suppress bounds warnings about interior zero length arrays. Such an array
+ * is used intentionally in prio_queue_t.
+ */
+#if __GNUC__ >= 10
+#pragma GCC diagnostic ignored "-Wzero-length-bounds"
+#endif
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/schedule.h>
+#include <odp_schedule_if.h>
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp_debug_internal.h>
+#include <odp/api/thread.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp/api/time.h>
+#include <odp/api/plat/time_inlines.h>
+#include <odp/api/ticketlock.h>
+#include <odp/api/hints.h>
+#include <odp/api/cpu.h>
+#include <odp/api/thrmask.h>
+#include <odp_config_internal.h>
+#include <odp/api/sync.h>
+#include <odp/api/packet_io.h>
+#include <odp_ring_u32_internal.h>
+#include <odp_timer_internal.h>
+#include <odp_queue_basic_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp/api/plat/schedule_inline_types.h>
+#include <odp_global_data.h>
+#include <odp_event_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_string_internal.h>
+
+#include <string.h>
+#include <time.h>
+#include <inttypes.h>
+
+/* No synchronization context */
+#define NO_SYNC_CONTEXT ODP_SCHED_SYNC_PARALLEL
+
+/* Number of priority levels */
+#define NUM_PRIO 8
+
+/* Group mask (prio_grp_mask) size in bits */
+#define GRP_MASK_BITS 64
+
+/* Number of scheduling groups. Maximum value is GRP_MASK_BITS. */
+#define NUM_SCHED_GRPS GRP_MASK_BITS
+
+/* Spread balancing frequency. Balance every BALANCE_ROUNDS_M1 + 1 scheduling rounds. */
+#define BALANCE_ROUNDS_M1 0xfffff
+
+/* Number of scheduled queue synchronization types */
+#define NUM_SCHED_SYNC 3
+
+/* Queue types used as array indices */
+ODP_STATIC_ASSERT(ODP_SCHED_SYNC_PARALLEL == 0, "ODP_SCHED_SYNC_PARALLEL_value_changed");
+ODP_STATIC_ASSERT(ODP_SCHED_SYNC_ATOMIC == 1, "ODP_SCHED_SYNC_ATOMIC_value_changed");
+ODP_STATIC_ASSERT(ODP_SCHED_SYNC_ORDERED == 2, "ODP_SCHED_SYNC_ORDERED_value_changed");
+
+/* Load of a queue */
+#define QUEUE_LOAD 256
+
+/* Margin for load balance hysteresis */
+#define QUEUE_LOAD_MARGIN 8
+
+/* Ensure that load calculation does not wrap around */
+ODP_STATIC_ASSERT((QUEUE_LOAD * CONFIG_MAX_SCHED_QUEUES) < UINT32_MAX, "Load_value_too_large");
+
+/* Maximum priority queue spread */
+#define MAX_SPREAD 8
+
+/* Minimum priority queue spread */
+#define MIN_SPREAD 1
+
+/* A thread polls a non preferred sched queue every this many polls
+ * of the prefer queue. */
+#define MAX_PREFER_WEIGHT 127
+#define MIN_PREFER_WEIGHT 1
+#define MAX_PREFER_RATIO (MAX_PREFER_WEIGHT + 1)
+
+/* Spread weight table */
+#define SPREAD_TBL_SIZE ((MAX_SPREAD - 1) * MAX_PREFER_RATIO)
+
+/* Random data table size */
+#define RANDOM_TBL_SIZE 128
+
+/* Maximum number of packet IO interfaces */
+#define NUM_PKTIO CONFIG_PKTIO_ENTRIES
+
+/* Maximum pktin index. Needs to fit into 8 bits. */
+#define MAX_PKTIN_INDEX 255
+
+/* Maximum priority queue ring size. A ring must be large enough to store all
+ * queues in the worst case (all queues are scheduled, have the same priority
+ * and no spreading). */
+#define MAX_RING_SIZE CONFIG_MAX_SCHED_QUEUES
+
+/* For best performance, the number of queues should be a power of two. */
+ODP_STATIC_ASSERT(_ODP_CHECK_IS_POWER2(CONFIG_MAX_SCHED_QUEUES),
+ "Number_of_queues_is_not_power_of_two");
+
+/* Ring size must be power of two, so that mask can be used. */
+ODP_STATIC_ASSERT(_ODP_CHECK_IS_POWER2(MAX_RING_SIZE),
+ "Ring_size_is_not_power_of_two");
+
+/* Thread ID is saved into uint16_t variable */
+ODP_STATIC_ASSERT(ODP_THREAD_COUNT_MAX < (64 * 1024),
+ "Max_64k_threads_supported");
+
+/* Mask of queues per priority */
+typedef uint8_t prio_q_mask_t;
+
+ODP_STATIC_ASSERT((8 * sizeof(prio_q_mask_t)) >= MAX_SPREAD,
+ "prio_q_mask_t_is_too_small");
+
+/* Start of named groups in group mask arrays */
+#define SCHED_GROUP_NAMED (ODP_SCHED_GROUP_CONTROL + 1)
+
+/* Limits for burst size configuration */
+#define BURST_MAX 255
+#define STASH_SIZE CONFIG_BURST_SIZE
+
+/* Ordered stash size */
+#define MAX_ORDERED_STASH 512
+
+/* Storage for stashed enqueue operation arguments */
+typedef struct {
+ _odp_event_hdr_t *event_hdr[QUEUE_MULTI_MAX];
+ odp_queue_t queue;
+ int num;
+} ordered_stash_t;
+
+/* Ordered lock states */
+typedef union {
+ uint8_t u8[CONFIG_QUEUE_MAX_ORD_LOCKS];
+ uint32_t all;
+} lock_called_t;
+
+ODP_STATIC_ASSERT(sizeof(lock_called_t) == sizeof(uint32_t),
+ "Lock_called_values_do_not_fit_in_uint32");
+
+/* Shuffled values from 0 to 127 */
+static uint8_t sched_random_u8[] = {
+ 0x5B, 0x56, 0x21, 0x28, 0x77, 0x2C, 0x7E, 0x10,
+ 0x29, 0x73, 0x39, 0x74, 0x60, 0x2B, 0x2D, 0x3E,
+ 0x6C, 0x4C, 0x1B, 0x79, 0x14, 0x76, 0x7B, 0x5A,
+ 0x4F, 0x3B, 0x0B, 0x16, 0x66, 0x0D, 0x05, 0x27,
+ 0x3F, 0x7F, 0x67, 0x3C, 0x41, 0x6F, 0x4E, 0x7A,
+ 0x04, 0x26, 0x11, 0x7C, 0x43, 0x38, 0x30, 0x2A,
+ 0x03, 0x22, 0x17, 0x75, 0x08, 0x71, 0x6D, 0x6B,
+ 0x0A, 0x4B, 0x52, 0x1D, 0x63, 0x59, 0x1C, 0x50,
+ 0x15, 0x1A, 0x64, 0x42, 0x47, 0x62, 0x1F, 0x37,
+ 0x46, 0x5D, 0x19, 0x35, 0x78, 0x68, 0x57, 0x7D,
+ 0x3A, 0x31, 0x4A, 0x45, 0x09, 0x49, 0x00, 0x01,
+ 0x65, 0x13, 0x48, 0x70, 0x5E, 0x69, 0x36, 0x58,
+ 0x1E, 0x5C, 0x23, 0x12, 0x18, 0x25, 0x55, 0x32,
+ 0x33, 0x61, 0x2F, 0x02, 0x06, 0x53, 0x24, 0x6E,
+ 0x2E, 0x5F, 0x54, 0x6A, 0x20, 0x07, 0x0F, 0x51,
+ 0x3D, 0x34, 0x44, 0x0C, 0x4D, 0x40, 0x72, 0x0E
+};
+
+ODP_STATIC_ASSERT(sizeof(sched_random_u8) == RANDOM_TBL_SIZE, "Bad_random_table_size");
+
+/* Scheduler local data */
+typedef struct ODP_ALIGNED_CACHE {
+ uint32_t sched_round;
+ uint16_t thr;
+ uint8_t pause;
+ uint8_t sync_ctx;
+ uint8_t balance_on;
+ uint16_t balance_start;
+ uint16_t spread_round;
+
+ struct {
+ uint16_t num_ev;
+ uint16_t ev_index;
+ uint32_t qi;
+ odp_queue_t queue;
+ ring_u32_t *ring;
+ odp_event_t ev[STASH_SIZE];
+ } stash;
+
+ uint64_t grp_mask;
+ uint32_t grp_epoch;
+ uint16_t num_grp;
+ uint8_t grp_idx;
+ uint8_t grp[NUM_SCHED_GRPS];
+ uint8_t spread_tbl[SPREAD_TBL_SIZE];
+
+ struct {
+ /* Source queue index */
+ uint32_t src_queue;
+ uint64_t ctx; /**< Ordered context id */
+ int stash_num; /**< Number of stashed enqueue operations */
+ uint8_t in_order; /**< Order status */
+ lock_called_t lock_called; /**< States of ordered locks */
+ /** Storage for stashed enqueue operations */
+ ordered_stash_t stash[MAX_ORDERED_STASH];
+ } ordered;
+
+} sched_local_t;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+/* Priority queue */
+typedef struct ODP_ALIGNED_CACHE {
+ /* Ring header */
+ ring_u32_t ring;
+
+ /* Ring data: queue indexes */
+ uint32_t queue_index[MAX_RING_SIZE]; /* overlaps with ring.data[] */
+
+} prio_queue_t;
+#pragma GCC diagnostic pop
+
+/* Order context of a queue */
+typedef struct ODP_ALIGNED_CACHE {
+ /* Current ordered context id */
+ odp_atomic_u64_t ctx ODP_ALIGNED_CACHE;
+
+ /* Next unallocated context id */
+ odp_atomic_u64_t next_ctx;
+
+ /* Array of ordered locks */
+ odp_atomic_u64_t lock[CONFIG_QUEUE_MAX_ORD_LOCKS];
+
+} order_context_t;
+
+typedef struct {
+ struct {
+ uint8_t burst_default[NUM_SCHED_SYNC][NUM_PRIO];
+ uint8_t burst_max[NUM_SCHED_SYNC][NUM_PRIO];
+ uint16_t order_stash_size;
+ uint8_t num_spread;
+ uint8_t prefer_ratio;
+ } config;
+ uint32_t ring_mask;
+ uint16_t max_spread;
+ uint8_t load_balance;
+ odp_atomic_u32_t grp_epoch;
+ odp_shm_t shm;
+ odp_ticketlock_t mask_lock[NUM_SCHED_GRPS];
+ prio_q_mask_t prio_q_mask[NUM_SCHED_GRPS][NUM_PRIO];
+
+ /* Groups on a priority level that have queues created */
+ odp_atomic_u64_t prio_grp_mask[NUM_PRIO];
+
+ struct {
+ uint8_t grp;
+ /* Inverted prio value (max = 0) vs API (min = 0)*/
+ uint8_t prio;
+ uint8_t spread;
+ uint8_t sync;
+ uint8_t order_lock_count;
+ uint8_t poll_pktin;
+ uint8_t pktio_index;
+ uint8_t pktin_index;
+ } queue[CONFIG_MAX_SCHED_QUEUES];
+
+ /* Scheduler priority queues */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ prio_queue_t prio_q[NUM_SCHED_GRPS][NUM_PRIO][MAX_SPREAD];
+#pragma GCC diagnostic pop
+ uint32_t prio_q_count[NUM_SCHED_GRPS][NUM_PRIO][MAX_SPREAD];
+
+ /* Number of queues per group and priority */
+ uint32_t prio_grp_count[NUM_PRIO][NUM_SCHED_GRPS];
+
+ odp_thrmask_t mask_all;
+ odp_ticketlock_t grp_lock;
+
+ struct {
+ char name[ODP_SCHED_GROUP_NAME_LEN];
+ odp_thrmask_t mask;
+ uint16_t spread_thrs[MAX_SPREAD];
+ uint8_t allocated;
+ } sched_grp[NUM_SCHED_GRPS];
+
+ struct {
+ int num_pktin;
+ } pktio[NUM_PKTIO];
+ odp_ticketlock_t pktio_lock;
+
+ order_context_t order[CONFIG_MAX_SCHED_QUEUES];
+
+ struct {
+ uint32_t poll_time;
+ uint64_t sleep_time;
+ } powersave;
+
+ /* Scheduler interface config options (not used in fast path) */
+ schedule_config_t config_if;
+ uint32_t max_queues;
+ odp_atomic_u32_t next_rand;
+
+} sched_global_t;
+
+/* Check that queue[] variables are large enough */
+ODP_STATIC_ASSERT(NUM_SCHED_GRPS <= GRP_MASK_BITS, "Groups do not fit into group mask");
+ODP_STATIC_ASSERT(NUM_PRIO <= 256, "Prio_does_not_fit_8_bits");
+ODP_STATIC_ASSERT(MAX_SPREAD <= 256, "Spread_does_not_fit_8_bits");
+ODP_STATIC_ASSERT(CONFIG_QUEUE_MAX_ORD_LOCKS <= 256,
+ "Ordered_lock_count_does_not_fit_8_bits");
+ODP_STATIC_ASSERT(NUM_PKTIO <= 256, "Pktio_index_does_not_fit_8_bits");
+
+/* Global scheduler context */
+static sched_global_t *sched;
+
+/* Thread local scheduler context */
+static __thread sched_local_t sched_local;
+
+static void prio_grp_mask_init(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_PRIO; i++)
+ odp_atomic_init_u64(&sched->prio_grp_mask[i], 0);
+}
+
+static inline void prio_grp_mask_set(int prio, int grp)
+{
+ uint64_t grp_mask = (uint64_t)1 << grp;
+ uint64_t mask = odp_atomic_load_u64(&sched->prio_grp_mask[prio]);
+
+ odp_atomic_store_u64(&sched->prio_grp_mask[prio], mask | grp_mask);
+
+ sched->prio_grp_count[prio][grp]++;
+}
+
+static inline void prio_grp_mask_clear(int prio, int grp)
+{
+ uint64_t grp_mask = (uint64_t)1 << grp;
+ uint64_t mask = odp_atomic_load_u64(&sched->prio_grp_mask[prio]);
+
+ sched->prio_grp_count[prio][grp]--;
+
+ if (sched->prio_grp_count[prio][grp] == 0)
+ odp_atomic_store_u64(&sched->prio_grp_mask[prio], mask &= (~grp_mask));
+}
+
+static inline uint64_t prio_grp_mask_check(int prio, uint64_t grp_mask)
+{
+ return odp_atomic_load_u64(&sched->prio_grp_mask[prio]) & grp_mask;
+}
+
+static int read_burst_size_conf(uint8_t out_tbl[], const char *conf_str,
+ int min_val, int max_val, int print)
+{
+ int burst_val[NUM_PRIO];
+ const int max_len = 256;
+ const int n = max_len - 1;
+ char line[max_len];
+ int len = 0;
+
+ if (_odp_libconfig_lookup_array(conf_str, burst_val, NUM_PRIO) !=
+ NUM_PRIO) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ return -1;
+ }
+
+ char str[strlen(conf_str) + 4];
+
+ snprintf(str, sizeof(str), "%s[]:", conf_str);
+ len += snprintf(&line[len], n - len, " %-38s", str);
+
+ for (int i = 0; i < NUM_PRIO; i++) {
+ int val = burst_val[i];
+
+ if (val > max_val || val < min_val) {
+ _ODP_ERR("Bad value for %s: %i\n", conf_str, val);
+ return -1;
+ }
+ len += snprintf(&line[len], n - len, " %3i", val);
+ if (val > 0)
+ out_tbl[i] = val;
+ }
+ if (print)
+ _ODP_PRINT("%s\n", line);
+
+ return 0;
+}
+
+static int read_config_file(sched_global_t *sched)
+{
+ const char *str;
+ int val = 0;
+
+ _ODP_PRINT("Scheduler config:\n");
+
+ str = "sched_basic.prio_spread";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > MAX_SPREAD || val < MIN_SPREAD) {
+ _ODP_ERR("Bad value %s = %u [min: %u, max: %u]\n", str, val,
+ MIN_SPREAD, MAX_SPREAD);
+ return -1;
+ }
+
+ sched->config.num_spread = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.prio_spread_weight";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > MAX_PREFER_WEIGHT || val < MIN_PREFER_WEIGHT) {
+ _ODP_ERR("Bad value %s = %u [min: %u, max: %u]\n", str, val,
+ MIN_PREFER_WEIGHT, MAX_PREFER_WEIGHT);
+ return -1;
+ }
+
+ sched->config.prefer_ratio = val + 1;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.load_balance";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > 1 || val < 0) {
+ _ODP_ERR("Bad value %s = %i\n", str, val);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ sched->load_balance = 1;
+ if (val == 0 || sched->config.num_spread == 1)
+ sched->load_balance = 0;
+
+ str = "sched_basic.order_stash_size";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ if (val > MAX_ORDERED_STASH || val < 0) {
+ _ODP_ERR("Bad value %s = %i [min: 0, max: %u]\n", str, val, MAX_ORDERED_STASH);
+ return -1;
+ }
+
+ sched->config.order_stash_size = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ /* Initialize default values for all queue types */
+ str = "sched_basic.burst_size_default";
+ if (read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_ATOMIC], str, 1,
+ STASH_SIZE, 1) ||
+ read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_PARALLEL], str, 1,
+ STASH_SIZE, 0) ||
+ read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_ORDERED], str, 1,
+ STASH_SIZE, 0))
+ return -1;
+
+ str = "sched_basic.burst_size_max";
+ if (read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_ATOMIC], str, 1,
+ BURST_MAX, 1) ||
+ read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_PARALLEL], str, 1,
+ BURST_MAX, 0) ||
+ read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_ORDERED], str, 1,
+ BURST_MAX, 0))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_ATOMIC],
+ "sched_basic.burst_size_atomic", 0, STASH_SIZE, 1))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_ATOMIC],
+ "sched_basic.burst_size_max_atomic", 0, BURST_MAX, 1))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_PARALLEL],
+ "sched_basic.burst_size_parallel", 0, STASH_SIZE, 1))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_PARALLEL],
+ "sched_basic.burst_size_max_parallel", 0, BURST_MAX, 1))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_default[ODP_SCHED_SYNC_ORDERED],
+ "sched_basic.burst_size_ordered", 0, STASH_SIZE, 1))
+ return -1;
+
+ if (read_burst_size_conf(sched->config.burst_max[ODP_SCHED_SYNC_ORDERED],
+ "sched_basic.burst_size_max_ordered", 0, BURST_MAX, 1))
+ return -1;
+
+ str = "sched_basic.group_enable.all";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ sched->config_if.group_enable.all = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.group_enable.worker";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ sched->config_if.group_enable.worker = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.group_enable.control";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ sched->config_if.group_enable.control = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.powersave.poll_time_nsec";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ sched->powersave.poll_time = _ODP_MAX(0, val);
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ str = "sched_basic.powersave.sleep_time_nsec";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+
+ val = _ODP_MAX(0, val);
+ val = _ODP_MIN((int)ODP_TIME_SEC_IN_NS - 1, val);
+ sched->powersave.sleep_time = val;
+ _ODP_PRINT(" %s: %i\n", str, val);
+
+ _ODP_PRINT(" dynamic load balance: %s\n", sched->load_balance ? "ON" : "OFF");
+
+ _ODP_PRINT("\n");
+
+ return 0;
+}
+
+/* Spread from thread or other index */
+static inline uint8_t spread_from_index(uint32_t index)
+{
+ return index % sched->config.num_spread;
+}
+
+static void sched_local_init(void)
+{
+ int i;
+ uint8_t spread, prefer_ratio;
+ uint8_t num_spread = sched->config.num_spread;
+ uint8_t offset = 1;
+
+ memset(&sched_local, 0, sizeof(sched_local_t));
+
+ sched_local.thr = odp_thread_id();
+ sched_local.sync_ctx = NO_SYNC_CONTEXT;
+ sched_local.stash.queue = ODP_QUEUE_INVALID;
+
+ spread = spread_from_index(sched_local.thr);
+ prefer_ratio = sched->config.prefer_ratio;
+
+ for (i = 0; i < SPREAD_TBL_SIZE; i++) {
+ sched_local.spread_tbl[i] = spread;
+
+ if (num_spread > 1 && (i % prefer_ratio) == 0) {
+ sched_local.spread_tbl[i] = spread_from_index(spread + offset);
+ offset++;
+ if (offset == num_spread)
+ offset = 1;
+ }
+ }
+}
+
+static int schedule_init_global(void)
+{
+ odp_shm_t shm;
+ int i, j, grp;
+ int prefer_ratio;
+ uint32_t ring_size, num_rings;
+
+ _ODP_DBG("Schedule init ... ");
+
+ shm = odp_shm_reserve("_odp_sched_basic_global",
+ sizeof(sched_global_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+ if (shm == ODP_SHM_INVALID) {
+ _ODP_ERR("Schedule init: Shm reserve failed.\n");
+ return -1;
+ }
+
+ sched = odp_shm_addr(shm);
+ memset(sched, 0, sizeof(sched_global_t));
+
+ if (read_config_file(sched)) {
+ odp_shm_free(shm);
+ return -1;
+ }
+
+ sched->shm = shm;
+ prefer_ratio = sched->config.prefer_ratio;
+
+ /* When num_spread == 1, only spread_tbl[0] is used. */
+ sched->max_spread = (sched->config.num_spread - 1) * prefer_ratio;
+
+ /* Dynamic load balance may move all queues into a single ring.
+ * Ring size can be smaller with fixed spreading. */
+ if (sched->load_balance) {
+ ring_size = MAX_RING_SIZE;
+ num_rings = 1;
+ } else {
+ ring_size = MAX_RING_SIZE / sched->config.num_spread;
+ num_rings = sched->config.num_spread;
+ }
+
+ ring_size = _ODP_ROUNDUP_POWER2_U32(ring_size);
+ _ODP_ASSERT(ring_size <= MAX_RING_SIZE);
+ sched->ring_mask = ring_size - 1;
+
+ /* Each ring can hold in maximum ring_size-1 queues. Due to ring size round up,
+ * total capacity of rings may be larger than CONFIG_MAX_SCHED_QUEUES. */
+ sched->max_queues = sched->ring_mask * num_rings;
+ if (sched->max_queues > CONFIG_MAX_SCHED_QUEUES)
+ sched->max_queues = CONFIG_MAX_SCHED_QUEUES;
+
+ for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
+ odp_ticketlock_init(&sched->mask_lock[grp]);
+
+ for (i = 0; i < NUM_PRIO; i++) {
+ for (j = 0; j < MAX_SPREAD; j++) {
+ prio_queue_t *prio_q;
+
+ prio_q = &sched->prio_q[grp][i][j];
+ ring_u32_init(&prio_q->ring);
+ }
+ }
+ }
+
+ odp_ticketlock_init(&sched->pktio_lock);
+ for (i = 0; i < NUM_PKTIO; i++)
+ sched->pktio[i].num_pktin = 0;
+
+ odp_ticketlock_init(&sched->grp_lock);
+ odp_atomic_init_u32(&sched->grp_epoch, 0);
+ odp_atomic_init_u32(&sched->next_rand, 0);
+
+ prio_grp_mask_init();
+
+ for (i = 0; i < NUM_SCHED_GRPS; i++) {
+ memset(sched->sched_grp[i].name, 0, ODP_SCHED_GROUP_NAME_LEN);
+ odp_thrmask_zero(&sched->sched_grp[i].mask);
+ }
+
+ sched->sched_grp[ODP_SCHED_GROUP_ALL].allocated = 1;
+ sched->sched_grp[ODP_SCHED_GROUP_WORKER].allocated = 1;
+ sched->sched_grp[ODP_SCHED_GROUP_CONTROL].allocated = 1;
+ _odp_strcpy(sched->sched_grp[ODP_SCHED_GROUP_ALL].name, "__SCHED_GROUP_ALL",
+ ODP_SCHED_GROUP_NAME_LEN);
+ _odp_strcpy(sched->sched_grp[ODP_SCHED_GROUP_WORKER].name, "__SCHED_GROUP_WORKER",
+ ODP_SCHED_GROUP_NAME_LEN);
+ _odp_strcpy(sched->sched_grp[ODP_SCHED_GROUP_CONTROL].name, "__SCHED_GROUP_CONTROL",
+ ODP_SCHED_GROUP_NAME_LEN);
+
+ odp_thrmask_setall(&sched->mask_all);
+
+ _ODP_DBG("done\n");
+
+ return 0;
+}
+
+static int schedule_term_global(void)
+{
+ int ret = 0;
+ int rc = 0;
+ int i, j, grp;
+ uint32_t ring_mask = sched->ring_mask;
+
+ for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
+ for (i = 0; i < NUM_PRIO; i++) {
+ for (j = 0; j < MAX_SPREAD; j++) {
+ ring_u32_t *ring;
+ uint32_t qi;
+
+ ring = &sched->prio_q[grp][i][j].ring;
+
+ while (ring_u32_deq(ring, ring_mask, &qi)) {
+ odp_event_t events[1];
+ int num;
+
+ num = _odp_sched_queue_deq(qi, events, 1, 1);
+
+ if (num > 0)
+ _ODP_ERR("Queue not empty\n");
+ }
+ }
+ }
+ }
+
+ ret = odp_shm_free(sched->shm);
+ if (ret < 0) {
+ _ODP_ERR("Shm free failed for odp_scheduler");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int schedule_init_local(void)
+{
+ sched_local_init();
+ return 0;
+}
+
+static inline void grp_update_mask(int grp, const odp_thrmask_t *new_mask)
+{
+ odp_thrmask_copy(&sched->sched_grp[grp].mask, new_mask);
+ odp_atomic_add_rel_u32(&sched->grp_epoch, 1);
+}
+
+static inline int grp_update_tbl(void)
+{
+ int i;
+ int num = 0;
+ int thr = sched_local.thr;
+ uint64_t mask = 0;
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ for (i = 0; i < NUM_SCHED_GRPS; i++) {
+ if (sched->sched_grp[i].allocated == 0)
+ continue;
+
+ if (odp_thrmask_isset(&sched->sched_grp[i].mask, thr)) {
+ sched_local.grp[num] = i;
+ num++;
+ mask |= (uint64_t)1 << i;
+ }
+ }
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ sched_local.grp_mask = mask;
+ sched_local.grp_idx = 0;
+ sched_local.num_grp = num;
+
+ return num;
+}
+
+static uint32_t schedule_max_ordered_locks(void)
+{
+ return CONFIG_QUEUE_MAX_ORD_LOCKS;
+}
+
+static int schedule_min_prio(void)
+{
+ return 0;
+}
+
+static int schedule_max_prio(void)
+{
+ return NUM_PRIO - 1;
+}
+
+static int schedule_default_prio(void)
+{
+ return schedule_max_prio() / 2;
+}
+
+static int schedule_num_prio(void)
+{
+ return NUM_PRIO;
+}
+
+static inline int prio_level_from_api(int api_prio)
+{
+ return schedule_max_prio() - api_prio;
+}
+
+static inline void dec_queue_count(int grp, int prio, int spr)
+{
+ odp_ticketlock_lock(&sched->mask_lock[grp]);
+
+ sched->prio_q_count[grp][prio][spr]--;
+
+ /* Clear mask bit only when the last queue is removed */
+ if (sched->prio_q_count[grp][prio][spr] == 0)
+ sched->prio_q_mask[grp][prio] &= (uint8_t)(~(1 << spr));
+
+ odp_ticketlock_unlock(&sched->mask_lock[grp]);
+}
+
+static inline void update_queue_count(int grp, int prio, int old_spr, int new_spr)
+{
+ odp_ticketlock_lock(&sched->mask_lock[grp]);
+
+ sched->prio_q_mask[grp][prio] |= 1 << new_spr;
+ sched->prio_q_count[grp][prio][new_spr]++;
+
+ sched->prio_q_count[grp][prio][old_spr]--;
+
+ if (sched->prio_q_count[grp][prio][old_spr] == 0)
+ sched->prio_q_mask[grp][prio] &= (uint8_t)(~(1 << old_spr));
+
+ odp_ticketlock_unlock(&sched->mask_lock[grp]);
+}
+
+static uint8_t allocate_spread(int grp, int prio)
+{
+ uint8_t i, num_min, spr;
+ uint32_t num;
+ uint32_t min = UINT32_MAX;
+ uint8_t num_spread = sched->config.num_spread;
+ uint8_t min_spr[num_spread];
+
+ num_min = 1;
+ min_spr[0] = 0;
+
+ odp_ticketlock_lock(&sched->mask_lock[grp]);
+
+ /* Find spread(s) with the minimum number of queues */
+ for (i = 0; i < num_spread; i++) {
+ num = sched->prio_q_count[grp][prio][i];
+ if (num < min) {
+ min = num;
+ min_spr[0] = i;
+ num_min = 1;
+ } else if (num == min) {
+ min_spr[num_min] = i;
+ num_min++;
+ }
+ }
+
+ spr = min_spr[0];
+
+ /* When there are multiple minimum spreads, select one randomly */
+ if (num_min > 1) {
+ uint32_t next_rand = odp_atomic_fetch_inc_u32(&sched->next_rand);
+ uint8_t rand = sched_random_u8[next_rand % RANDOM_TBL_SIZE];
+
+ spr = min_spr[rand % num_min];
+ }
+
+ sched->prio_q_mask[grp][prio] |= 1 << spr;
+ sched->prio_q_count[grp][prio][spr]++;
+
+ odp_ticketlock_unlock(&sched->mask_lock[grp]);
+
+ return spr;
+}
+
+static int schedule_create_queue(uint32_t queue_index,
+ const odp_schedule_param_t *sched_param)
+{
+ int i;
+ uint8_t spread;
+ int grp = sched_param->group;
+ int prio = prio_level_from_api(sched_param->prio);
+
+ if (odp_global_rw->schedule_configured == 0) {
+ _ODP_ERR("Scheduler has not been configured\n");
+ return -1;
+ }
+
+ if (grp < 0 || grp >= NUM_SCHED_GRPS) {
+ _ODP_ERR("Bad schedule group %i\n", grp);
+ return -1;
+ }
+ if (grp == ODP_SCHED_GROUP_ALL && !sched->config_if.group_enable.all) {
+ _ODP_ERR("Trying to use disabled ODP_SCHED_GROUP_ALL\n");
+ return -1;
+ }
+ if (grp == ODP_SCHED_GROUP_CONTROL && !sched->config_if.group_enable.control) {
+ _ODP_ERR("Trying to use disabled ODP_SCHED_GROUP_CONTROL\n");
+ return -1;
+ }
+ if (grp == ODP_SCHED_GROUP_WORKER && !sched->config_if.group_enable.worker) {
+ _ODP_ERR("Trying to use disabled ODP_SCHED_GROUP_WORKER\n");
+ return -1;
+ }
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (sched->sched_grp[grp].allocated == 0) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ _ODP_ERR("Group not created: %i\n", grp);
+ return -1;
+ }
+
+ prio_grp_mask_set(prio, grp);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ spread = allocate_spread(grp, prio);
+
+ sched->queue[queue_index].grp = grp;
+ sched->queue[queue_index].prio = prio;
+ sched->queue[queue_index].spread = spread;
+ sched->queue[queue_index].sync = sched_param->sync;
+ sched->queue[queue_index].order_lock_count = sched_param->lock_count;
+ sched->queue[queue_index].poll_pktin = 0;
+ sched->queue[queue_index].pktio_index = 0;
+ sched->queue[queue_index].pktin_index = 0;
+
+ odp_atomic_init_u64(&sched->order[queue_index].ctx, 0);
+ odp_atomic_init_u64(&sched->order[queue_index].next_ctx, 0);
+
+ for (i = 0; i < CONFIG_QUEUE_MAX_ORD_LOCKS; i++)
+ odp_atomic_init_u64(&sched->order[queue_index].lock[i], 0);
+
+ return 0;
+}
+
+static inline uint8_t sched_sync_type(uint32_t queue_index)
+{
+ return sched->queue[queue_index].sync;
+}
+
+static void schedule_destroy_queue(uint32_t queue_index)
+{
+ int grp = sched->queue[queue_index].grp;
+ int prio = sched->queue[queue_index].prio;
+ int spread = sched->queue[queue_index].spread;
+
+ dec_queue_count(grp, prio, spread);
+
+ odp_ticketlock_lock(&sched->grp_lock);
+ prio_grp_mask_clear(prio, grp);
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ sched->queue[queue_index].grp = 0;
+ sched->queue[queue_index].prio = 0;
+ sched->queue[queue_index].spread = 0;
+
+ if ((sched_sync_type(queue_index) == ODP_SCHED_SYNC_ORDERED) &&
+ odp_atomic_load_u64(&sched->order[queue_index].ctx) !=
+ odp_atomic_load_u64(&sched->order[queue_index].next_ctx))
+ _ODP_ERR("queue reorder incomplete\n");
+}
+
+static int schedule_sched_queue(uint32_t queue_index)
+{
+ int grp = sched->queue[queue_index].grp;
+ int prio = sched->queue[queue_index].prio;
+ int spread = sched->queue[queue_index].spread;
+ ring_u32_t *ring = &sched->prio_q[grp][prio][spread].ring;
+
+ ring_u32_enq(ring, sched->ring_mask, queue_index);
+ return 0;
+}
+
+static void schedule_pktio_start(int pktio_index, int num_pktin,
+ int pktin_idx[], odp_queue_t queue[])
+{
+ int i;
+ uint32_t qi;
+
+ sched->pktio[pktio_index].num_pktin = num_pktin;
+
+ for (i = 0; i < num_pktin; i++) {
+ qi = queue_to_index(queue[i]);
+ sched->queue[qi].poll_pktin = 1;
+ sched->queue[qi].pktio_index = pktio_index;
+ sched->queue[qi].pktin_index = pktin_idx[i];
+
+ _ODP_ASSERT(pktin_idx[i] <= MAX_PKTIN_INDEX);
+
+ /* Start polling */
+ _odp_sched_queue_set_status(qi, QUEUE_STATUS_SCHED);
+ schedule_sched_queue(qi);
+ }
+}
+
+static inline void release_atomic(void)
+{
+ uint32_t qi = sched_local.stash.qi;
+ ring_u32_t *ring = sched_local.stash.ring;
+
+ /* Release current atomic queue */
+ ring_u32_enq(ring, sched->ring_mask, qi);
+
+ /* We don't hold sync context anymore */
+ sched_local.sync_ctx = NO_SYNC_CONTEXT;
+}
+
+static void schedule_release_atomic(void)
+{
+ if (sched_local.sync_ctx == ODP_SCHED_SYNC_ATOMIC &&
+ sched_local.stash.num_ev == 0)
+ release_atomic();
+}
+
+static inline int ordered_own_turn(uint32_t queue_index)
+{
+ uint64_t ctx;
+
+ ctx = odp_atomic_load_acq_u64(&sched->order[queue_index].ctx);
+
+ return ctx == sched_local.ordered.ctx;
+}
+
+static inline void wait_for_order(uint32_t queue_index)
+{
+ /* Busy loop to synchronize ordered processing */
+ while (1) {
+ if (ordered_own_turn(queue_index))
+ break;
+ odp_cpu_pause();
+ }
+}
+
+/**
+ * Perform stashed enqueue operations
+ *
+ * Should be called only when already in order.
+ */
+static inline void ordered_stash_release(void)
+{
+ int i;
+
+ for (i = 0; i < sched_local.ordered.stash_num; i++) {
+ odp_queue_t queue;
+ _odp_event_hdr_t **event_hdr;
+ int num, num_enq;
+
+ queue = sched_local.ordered.stash[i].queue;
+ event_hdr = sched_local.ordered.stash[i].event_hdr;
+ num = sched_local.ordered.stash[i].num;
+
+ num_enq = odp_queue_enq_multi(queue,
+ (odp_event_t *)event_hdr, num);
+
+ /* Drop packets that were not enqueued */
+ if (odp_unlikely(num_enq < num)) {
+ if (odp_unlikely(num_enq < 0))
+ num_enq = 0;
+
+ _ODP_DBG("Dropped %i packets\n", num - num_enq);
+ _odp_event_free_multi(&event_hdr[num_enq], num - num_enq);
+ }
+ }
+ sched_local.ordered.stash_num = 0;
+}
+
+static inline void release_ordered(void)
+{
+ uint32_t qi;
+ uint32_t i;
+
+ qi = sched_local.ordered.src_queue;
+
+ wait_for_order(qi);
+
+ /* Release all ordered locks */
+ for (i = 0; i < sched->queue[qi].order_lock_count; i++) {
+ if (!sched_local.ordered.lock_called.u8[i])
+ odp_atomic_store_rel_u64(&sched->order[qi].lock[i],
+ sched_local.ordered.ctx + 1);
+ }
+
+ sched_local.ordered.lock_called.all = 0;
+ sched_local.ordered.in_order = 0;
+
+ /* We don't hold sync context anymore */
+ sched_local.sync_ctx = NO_SYNC_CONTEXT;
+
+ ordered_stash_release();
+
+ /* Next thread can continue processing */
+ odp_atomic_add_rel_u64(&sched->order[qi].ctx, 1);
+}
+
+static void schedule_release_ordered(void)
+{
+ if (odp_unlikely((sched_local.sync_ctx != ODP_SCHED_SYNC_ORDERED) ||
+ sched_local.stash.num_ev))
+ return;
+
+ release_ordered();
+}
+
+static int schedule_term_local(void)
+{
+ if (sched_local.stash.num_ev) {
+ _ODP_ERR("Locally pre-scheduled events exist.\n");
+ return -1;
+ }
+
+ if (sched_local.sync_ctx == ODP_SCHED_SYNC_ATOMIC)
+ schedule_release_atomic();
+ else if (sched_local.sync_ctx == ODP_SCHED_SYNC_ORDERED)
+ schedule_release_ordered();
+
+ return 0;
+}
+
+static void schedule_config_init(odp_schedule_config_t *config)
+{
+ config->num_queues = sched->max_queues;
+ config->queue_size = _odp_queue_glb->config.max_queue_size;
+ config->sched_group.all = sched->config_if.group_enable.all;
+ config->sched_group.control = sched->config_if.group_enable.control;
+ config->sched_group.worker = sched->config_if.group_enable.worker;
+}
+
+static void schedule_group_clear(odp_schedule_group_t group)
+{
+ odp_thrmask_t zero;
+
+ odp_thrmask_zero(&zero);
+
+ if (group < 0 || group > ODP_SCHED_GROUP_CONTROL)
+ _ODP_ABORT("Invalid scheduling group\n");
+
+ grp_update_mask(group, &zero);
+ sched->sched_grp[group].allocated = 0;
+}
+
+static int schedule_config(const odp_schedule_config_t *config)
+{
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ sched->config_if.group_enable.all = config->sched_group.all;
+ sched->config_if.group_enable.control = config->sched_group.control;
+ sched->config_if.group_enable.worker = config->sched_group.worker;
+
+ /* Remove existing threads from predefined scheduling groups. */
+ if (!config->sched_group.all)
+ schedule_group_clear(ODP_SCHED_GROUP_ALL);
+
+ if (!config->sched_group.worker)
+ schedule_group_clear(ODP_SCHED_GROUP_WORKER);
+
+ if (!config->sched_group.control)
+ schedule_group_clear(ODP_SCHED_GROUP_CONTROL);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ return 0;
+}
+
+/* Spread load after adding 'num' queues */
+static inline uint32_t spread_load(int grp, int prio, int spr, int num)
+{
+ uint32_t num_q, num_thr;
+
+ num_q = sched->prio_q_count[grp][prio][spr];
+ num_thr = sched->sched_grp[grp].spread_thrs[spr];
+
+ if (num_thr == 0)
+ return UINT32_MAX;
+
+ return ((num_q + num) * QUEUE_LOAD) / num_thr;
+}
+
+static inline int balance_spread(int grp, int prio, int cur_spr)
+{
+ int spr;
+ uint64_t cur_load, min_load, load;
+ int num_spread = sched->config.num_spread;
+ int new_spr = cur_spr;
+
+ cur_load = spread_load(grp, prio, cur_spr, 0);
+ min_load = cur_load;
+
+ for (spr = 0; spr < num_spread; spr++) {
+ if (spr == cur_spr)
+ continue;
+
+ load = spread_load(grp, prio, spr, 1);
+
+ /* Move queue if improvement is larger than marginal */
+ if ((load + QUEUE_LOAD_MARGIN) < min_load) {
+ new_spr = spr;
+ min_load = load;
+ }
+ }
+
+ return new_spr;
+}
+
+static inline int copy_from_stash(odp_event_t out_ev[], uint32_t max)
+{
+ int i = 0;
+
+ while (sched_local.stash.num_ev && max) {
+ out_ev[i] = sched_local.stash.ev[sched_local.stash.ev_index];
+ sched_local.stash.ev_index++;
+ sched_local.stash.num_ev--;
+ max--;
+ i++;
+ }
+
+ return i;
+}
+
+static int schedule_ord_enq_multi(odp_queue_t dst_queue, void *event_hdr[],
+ int num, int *ret)
+{
+ int i;
+ uint32_t stash_num;
+ queue_entry_t *dst_qentry;
+ uint32_t src_queue;
+
+ /* This check is done for every queue enqueue operation, also for plain
+ * queues. Return fast when not holding a scheduling context. */
+ if (odp_likely(sched_local.sync_ctx != ODP_SCHED_SYNC_ORDERED))
+ return 0;
+
+ if (sched_local.ordered.in_order)
+ return 0;
+
+ dst_qentry = qentry_from_handle(dst_queue);
+
+ if (dst_qentry->param.order == ODP_QUEUE_ORDER_IGNORE)
+ return 0;
+
+ src_queue = sched_local.ordered.src_queue;
+ stash_num = sched_local.ordered.stash_num;
+
+ if (ordered_own_turn(src_queue)) {
+ /* Own turn, so can do enqueue directly. */
+ sched_local.ordered.in_order = 1;
+ ordered_stash_release();
+ return 0;
+ }
+
+ /* Pktout may drop packets, so the operation cannot be stashed. */
+ if (dst_qentry->pktout.pktio != ODP_PKTIO_INVALID ||
+ odp_unlikely(stash_num >= sched->config.order_stash_size)) {
+ /* If the local stash is full, wait until it is our turn and
+ * then release the stash and do enqueue directly. */
+ wait_for_order(src_queue);
+
+ sched_local.ordered.in_order = 1;
+
+ ordered_stash_release();
+ return 0;
+ }
+
+ sched_local.ordered.stash[stash_num].queue = dst_queue;
+ sched_local.ordered.stash[stash_num].num = num;
+ for (i = 0; i < num; i++)
+ sched_local.ordered.stash[stash_num].event_hdr[i] = event_hdr[i];
+
+ sched_local.ordered.stash_num++;
+
+ *ret = num;
+ return 1;
+}
+
+static inline int queue_is_pktin(uint32_t queue_index)
+{
+ return sched->queue[queue_index].poll_pktin;
+}
+
+static inline int poll_pktin(uint32_t qi, int direct_recv,
+ odp_event_t ev_tbl[], int max_num)
+{
+ int pktio_index, pktin_index, num, num_pktin;
+ _odp_event_hdr_t **hdr_tbl;
+ int ret;
+ void *q_int;
+ _odp_event_hdr_t *b_hdr[CONFIG_BURST_SIZE];
+
+ hdr_tbl = (_odp_event_hdr_t **)ev_tbl;
+
+ if (!direct_recv) {
+ hdr_tbl = b_hdr;
+
+ /* Limit burst to max queue enqueue size */
+ if (max_num > CONFIG_BURST_SIZE)
+ max_num = CONFIG_BURST_SIZE;
+ }
+
+ pktio_index = sched->queue[qi].pktio_index;
+ pktin_index = sched->queue[qi].pktin_index;
+
+ num = _odp_sched_cb_pktin_poll(pktio_index, pktin_index, hdr_tbl, max_num);
+
+ if (num == 0)
+ return 0;
+
+ /* Pktio stopped or closed. Call stop_finalize when we have stopped
+ * polling all pktin queues of the pktio. */
+ if (odp_unlikely(num < 0)) {
+ odp_ticketlock_lock(&sched->pktio_lock);
+ sched->pktio[pktio_index].num_pktin--;
+ num_pktin = sched->pktio[pktio_index].num_pktin;
+ odp_ticketlock_unlock(&sched->pktio_lock);
+
+ _odp_sched_queue_set_status(qi, QUEUE_STATUS_NOTSCHED);
+
+ if (num_pktin == 0)
+ _odp_sched_cb_pktio_stop_finalize(pktio_index);
+
+ return num;
+ }
+
+ if (direct_recv)
+ return num;
+
+ q_int = qentry_from_index(qi);
+
+ ret = odp_queue_enq_multi(q_int, (odp_event_t *)b_hdr, num);
+
+ /* Drop packets that were not enqueued */
+ if (odp_unlikely(ret < num)) {
+ int num_enq = ret;
+
+ if (odp_unlikely(ret < 0))
+ num_enq = 0;
+
+ _ODP_DBG("Dropped %i packets\n", num - num_enq);
+ _odp_event_free_multi(&b_hdr[num_enq], num - num_enq);
+ }
+
+ return ret;
+}
+
+static inline int schedule_grp_prio(odp_queue_t *out_queue, odp_event_t out_ev[], uint32_t max_num,
+ int grp, int prio, int first_spr, int balance)
+{
+ int spr, new_spr, i, ret;
+ uint32_t qi;
+ int num_spread = sched->config.num_spread;
+ uint32_t ring_mask = sched->ring_mask;
+ const uint32_t burst_def_sync[NUM_SCHED_SYNC] = {
+ sched->config.burst_default[ODP_SCHED_SYNC_PARALLEL][prio],
+ sched->config.burst_default[ODP_SCHED_SYNC_ATOMIC][prio],
+ sched->config.burst_default[ODP_SCHED_SYNC_ORDERED][prio]};
+ const uint32_t burst_max_sync[NUM_SCHED_SYNC] = {
+ sched->config.burst_max[ODP_SCHED_SYNC_PARALLEL][prio],
+ sched->config.burst_max[ODP_SCHED_SYNC_ATOMIC][prio],
+ sched->config.burst_max[ODP_SCHED_SYNC_ORDERED][prio]};
+
+ /* Select the first spread based on weights */
+ spr = first_spr;
+
+ for (i = 0; i < num_spread;) {
+ int num;
+ uint8_t sync_ctx, ordered;
+ odp_queue_t handle;
+ ring_u32_t *ring;
+ int pktin;
+ uint32_t max_deq;
+ int stashed = 1;
+ odp_event_t *ev_tbl = sched_local.stash.ev;
+
+ if (spr >= num_spread)
+ spr = 0;
+
+ /* No queues allocated to this spread */
+ if (odp_unlikely((sched->prio_q_mask[grp][prio] & (1 << spr)) == 0)) {
+ i++;
+ spr++;
+ continue;
+ }
+
+ ring = &sched->prio_q[grp][prio][spr].ring;
+
+ /* Get queue index from the spread queue */
+ if (ring_u32_deq(ring, ring_mask, &qi) == 0) {
+ /* Spread queue is empty */
+ i++;
+ spr++;
+ continue;
+ }
+
+ sync_ctx = sched_sync_type(qi);
+ ordered = (sync_ctx == ODP_SCHED_SYNC_ORDERED);
+ max_deq = burst_def_sync[sync_ctx];
+
+ /* When application's array is larger than default burst
+ * size, output all events directly there. Also, ordered
+ * queues are not stashed locally to improve
+ * parallelism. Ordered context can only be released
+ * when the local cache is empty. */
+ if (max_num > max_deq || ordered) {
+ const uint32_t burst_max = burst_max_sync[sync_ctx];
+
+ stashed = 0;
+ ev_tbl = out_ev;
+ max_deq = max_num;
+ if (max_num > burst_max)
+ max_deq = burst_max;
+ }
+
+ pktin = queue_is_pktin(qi);
+
+ /* Update queue spread before dequeue. Dequeue changes status of an empty
+ * queue, which enables a following enqueue operation to insert the queue
+ * back into scheduling (with new spread). */
+ if (odp_unlikely(balance)) {
+ new_spr = balance_spread(grp, prio, spr);
+
+ if (new_spr != spr) {
+ sched->queue[qi].spread = new_spr;
+ ring = &sched->prio_q[grp][prio][new_spr].ring;
+ update_queue_count(grp, prio, spr, new_spr);
+ }
+ }
+
+ num = _odp_sched_queue_deq(qi, ev_tbl, max_deq, !pktin);
+
+ if (odp_unlikely(num < 0)) {
+ /* Remove destroyed queue from scheduling. Continue scheduling
+ * the same group/prio/spread. */
+ continue;
+ }
+
+ if (num == 0) {
+ int direct_recv = !ordered;
+ int num_pkt;
+
+ if (!pktin) {
+ /* Remove empty queue from scheduling */
+ continue;
+ }
+
+ /* Poll packet input queue */
+ num_pkt = poll_pktin(qi, direct_recv, ev_tbl, max_deq);
+
+ if (odp_unlikely(num_pkt < 0)) {
+ /* Pktio has been stopped or closed. Stop polling
+ * the packet input queue. */
+ continue;
+ }
+
+ if (num_pkt == 0 || !direct_recv) {
+ /* No packets to be returned. Continue scheduling
+ * packet input queue even when it is empty. */
+ ring_u32_enq(ring, ring_mask, qi);
+
+ /* Continue scheduling from the next spread */
+ i++;
+ spr++;
+ continue;
+ }
+
+ /* Process packets from an atomic or parallel queue right away. */
+ num = num_pkt;
+ }
+
+ if (ordered) {
+ uint64_t ctx;
+ odp_atomic_u64_t *next_ctx;
+
+ next_ctx = &sched->order[qi].next_ctx;
+ ctx = odp_atomic_fetch_inc_u64(next_ctx);
+
+ sched_local.ordered.ctx = ctx;
+ sched_local.ordered.src_queue = qi;
+
+ /* Continue scheduling ordered queues */
+ ring_u32_enq(ring, ring_mask, qi);
+ sched_local.sync_ctx = sync_ctx;
+
+ } else if (sync_ctx == ODP_SCHED_SYNC_ATOMIC) {
+ /* Hold queue during atomic access */
+ sched_local.stash.qi = qi;
+ sched_local.stash.ring = ring;
+ sched_local.sync_ctx = sync_ctx;
+ } else {
+ /* Continue scheduling parallel queues */
+ ring_u32_enq(ring, ring_mask, qi);
+ }
+
+ handle = queue_from_index(qi);
+
+ if (stashed) {
+ sched_local.stash.num_ev = num;
+ sched_local.stash.ev_index = 0;
+ sched_local.stash.queue = handle;
+ ret = copy_from_stash(out_ev, max_num);
+ } else {
+ sched_local.stash.num_ev = 0;
+ ret = num;
+ }
+
+ /* Output the source queue handle */
+ if (out_queue)
+ *out_queue = handle;
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Schedule queues
+ */
+static inline int do_schedule(odp_queue_t *out_q, odp_event_t out_ev[], uint32_t max_num)
+{
+ int i, num_grp, ret, spr, first_id, grp_id, grp, prio;
+ uint32_t sched_round;
+ uint16_t spread_round;
+ uint32_t epoch;
+ uint64_t my_groups;
+ int balance = 0;
+
+ if (sched_local.stash.num_ev) {
+ ret = copy_from_stash(out_ev, max_num);
+
+ if (out_q)
+ *out_q = sched_local.stash.queue;
+
+ return ret;
+ }
+
+ /* Release schedule context */
+ if (sched_local.sync_ctx == ODP_SCHED_SYNC_ATOMIC)
+ release_atomic();
+ else if (sched_local.sync_ctx == ODP_SCHED_SYNC_ORDERED)
+ release_ordered();
+
+ if (odp_unlikely(sched_local.pause))
+ return 0;
+
+ sched_round = sched_local.sched_round++;
+
+ /* Each thread prefers a priority queue. Spread weight table avoids
+ * starvation of other priority queues on low thread counts. */
+ spread_round = sched_local.spread_round;
+
+ if (odp_likely(sched->load_balance)) {
+ /* Spread balance is checked max_spread times in every BALANCE_ROUNDS_M1 + 1
+ * scheduling rounds. */
+ if (odp_unlikely(sched_local.balance_on)) {
+ balance = 1;
+
+ if (sched_local.balance_start == spread_round)
+ sched_local.balance_on = 0;
+ }
+
+ if (odp_unlikely((sched_round & BALANCE_ROUNDS_M1) == 0)) {
+ sched_local.balance_start = spread_round;
+ sched_local.balance_on = 1;
+ }
+ }
+
+ if (odp_unlikely(spread_round + 1 >= sched->max_spread))
+ sched_local.spread_round = 0;
+ else
+ sched_local.spread_round = spread_round + 1;
+
+ spr = sched_local.spread_tbl[spread_round];
+
+ epoch = odp_atomic_load_acq_u32(&sched->grp_epoch);
+ num_grp = sched_local.num_grp;
+
+ if (odp_unlikely(sched_local.grp_epoch != epoch)) {
+ num_grp = grp_update_tbl();
+ sched_local.grp_epoch = epoch;
+ }
+
+ if (odp_unlikely(num_grp == 0))
+ return 0;
+
+ my_groups = sched_local.grp_mask;
+ first_id = sched_local.grp_idx;
+ sched_local.grp_idx = (first_id + 1) % num_grp;
+
+ for (prio = 0; prio < NUM_PRIO; prio++) {
+ grp_id = first_id;
+
+ if (prio_grp_mask_check(prio, my_groups) == 0) {
+ /* My groups do not have queues at this priority level, continue to
+ * the next level.
+ *
+ * As a performance optimization, prio_grp_mask[] is checked without
+ * taking the lock. Masks change infrequently and usage of an old mask
+ * just leads into searching events from old priority levels,
+ * new levels are likely used on the next schedule call. */
+ continue;
+ }
+
+ for (i = 0; i < num_grp; i++) {
+ grp = sched_local.grp[grp_id];
+
+ grp_id++;
+ if (odp_unlikely(grp_id >= num_grp))
+ grp_id = 0;
+
+ if (sched->prio_q_mask[grp][prio] == 0) {
+ /* Group does not have queues at this priority level */
+ continue;
+ }
+
+ /* Schedule events from the selected group and priority level */
+ ret = schedule_grp_prio(out_q, out_ev, max_num, grp, prio, spr, balance);
+
+ if (odp_likely(ret))
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static inline int schedule_run(odp_queue_t *out_queue, odp_event_t out_ev[], uint32_t max_num)
+{
+ timer_run(1);
+
+ return do_schedule(out_queue, out_ev, max_num);
+}
+
+static inline int schedule_loop(odp_queue_t *out_queue, uint64_t wait,
+ odp_event_t out_ev[], uint32_t max_num)
+{
+ odp_time_t next;
+ int first = 1;
+ int ret;
+
+ while (1) {
+ ret = do_schedule(out_queue, out_ev, max_num);
+ if (ret) {
+ timer_run(2);
+ break;
+ }
+ timer_run(1);
+
+ if (wait == ODP_SCHED_WAIT)
+ continue;
+
+ if (wait == ODP_SCHED_NO_WAIT)
+ break;
+
+ if (first) {
+ next = odp_time_add_ns(odp_time_local(), wait);
+ first = 0;
+ continue;
+ }
+
+ if (odp_time_cmp(next, odp_time_local()) < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static inline int schedule_loop_sleep(odp_queue_t *out_queue, uint64_t wait,
+ odp_event_t out_ev[], uint32_t max_num)
+{
+ int ret;
+ odp_time_t start, end, current, start_sleep;
+ int first = 1, sleep = 0;
+
+ while (1) {
+ ret = do_schedule(out_queue, out_ev, max_num);
+ if (ret) {
+ timer_run(2);
+ break;
+ }
+ uint64_t next = timer_run(sleep ? TIMER_SCAN_FORCE : 1);
+
+ if (first) {
+ start = odp_time_local();
+ start_sleep = odp_time_add_ns(start, sched->powersave.poll_time);
+ if (wait != ODP_SCHED_WAIT)
+ end = odp_time_add_ns(start, wait);
+ first = 0;
+ continue;
+ }
+
+ if (sleep && next) {
+ uint64_t sleep_nsec = _ODP_MIN(sched->powersave.sleep_time, next);
+
+ if (wait != ODP_SCHED_WAIT) {
+ uint64_t nsec_to_end = odp_time_diff_ns(end, current);
+
+ sleep_nsec = _ODP_MIN(sleep_nsec, nsec_to_end);
+ }
+
+ struct timespec ts = { 0, sleep_nsec };
+
+ nanosleep(&ts, NULL);
+ }
+
+ if (!sleep || wait != ODP_SCHED_WAIT)
+ current = odp_time_local();
+
+ if (!sleep && odp_time_cmp(start_sleep, current) < 0)
+ sleep = 1;
+
+ if (wait != ODP_SCHED_WAIT && odp_time_cmp(end, current) < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static odp_event_t schedule(odp_queue_t *out_queue, uint64_t wait)
+{
+ odp_event_t ev;
+
+ ev = ODP_EVENT_INVALID;
+
+ schedule_loop(out_queue, wait, &ev, 1);
+
+ return ev;
+}
+
+static odp_event_t schedule_sleep(odp_queue_t *out_queue, uint64_t wait)
+{
+ odp_event_t ev;
+
+ ev = ODP_EVENT_INVALID;
+
+ if (wait == ODP_SCHED_NO_WAIT)
+ schedule_loop(out_queue, wait, &ev, 1);
+ else
+ schedule_loop_sleep(out_queue, wait, &ev, 1);
+
+ return ev;
+}
+
+static int schedule_multi(odp_queue_t *out_queue, uint64_t wait,
+ odp_event_t events[], int num)
+{
+ return schedule_loop(out_queue, wait, events, num);
+}
+
+static int schedule_multi_sleep(odp_queue_t *out_queue, uint64_t wait,
+ odp_event_t events[], int num)
+{
+ if (wait == ODP_SCHED_NO_WAIT)
+ return schedule_loop(out_queue, wait, events, num);
+
+ return schedule_loop_sleep(out_queue, wait, events, num);
+}
+
+static int schedule_multi_no_wait(odp_queue_t *out_queue, odp_event_t events[],
+ int num)
+{
+ return schedule_run(out_queue, events, num);
+}
+
+static int schedule_multi_wait(odp_queue_t *out_queue, odp_event_t events[],
+ int num)
+{
+ int ret;
+
+ do {
+ ret = schedule_run(out_queue, events, num);
+ } while (ret == 0);
+
+ return ret;
+}
+
+static int schedule_multi_wait_sleep(odp_queue_t *out_queue, odp_event_t events[],
+ int num)
+{
+ return schedule_loop_sleep(out_queue, ODP_SCHED_WAIT, events, num);
+}
+
+static inline void order_lock(void)
+{
+ if (sched_local.sync_ctx != ODP_SCHED_SYNC_ORDERED)
+ return;
+
+ wait_for_order(sched_local.ordered.src_queue);
+}
+
+static void order_unlock(void)
+{
+ /* Nothing to do */
+}
+
+static void schedule_order_lock(uint32_t lock_index)
+{
+ odp_atomic_u64_t *ord_lock;
+ uint32_t queue_index;
+
+ if (sched_local.sync_ctx != ODP_SCHED_SYNC_ORDERED)
+ return;
+
+ queue_index = sched_local.ordered.src_queue;
+
+ _ODP_ASSERT(lock_index <= sched->queue[queue_index].order_lock_count &&
+ !sched_local.ordered.lock_called.u8[lock_index]);
+
+ ord_lock = &sched->order[queue_index].lock[lock_index];
+
+ /* Busy loop to synchronize ordered processing */
+ while (1) {
+ uint64_t lock_seq;
+
+ lock_seq = odp_atomic_load_acq_u64(ord_lock);
+
+ if (lock_seq == sched_local.ordered.ctx) {
+ sched_local.ordered.lock_called.u8[lock_index] = 1;
+ return;
+ }
+ odp_cpu_pause();
+ }
+}
+
+static void schedule_order_unlock(uint32_t lock_index)
+{
+ odp_atomic_u64_t *ord_lock;
+ uint32_t queue_index;
+
+ if (sched_local.sync_ctx != ODP_SCHED_SYNC_ORDERED)
+ return;
+
+ queue_index = sched_local.ordered.src_queue;
+
+ _ODP_ASSERT(lock_index <= sched->queue[queue_index].order_lock_count);
+
+ ord_lock = &sched->order[queue_index].lock[lock_index];
+
+ _ODP_ASSERT(sched_local.ordered.ctx == odp_atomic_load_u64(ord_lock));
+
+ odp_atomic_store_rel_u64(ord_lock, sched_local.ordered.ctx + 1);
+}
+
+static void schedule_order_unlock_lock(uint32_t unlock_index,
+ uint32_t lock_index)
+{
+ schedule_order_unlock(unlock_index);
+ schedule_order_lock(lock_index);
+}
+
+static void schedule_order_lock_start(uint32_t lock_index)
+{
+ (void)lock_index;
+}
+
+static void schedule_order_lock_wait(uint32_t lock_index)
+{
+ schedule_order_lock(lock_index);
+}
+
+static void schedule_pause(void)
+{
+ sched_local.pause = 1;
+}
+
+static void schedule_resume(void)
+{
+ sched_local.pause = 0;
+}
+
+static uint64_t schedule_wait_time(uint64_t ns)
+{
+ return ns;
+}
+
+static inline void spread_thrs_inc(odp_schedule_group_t group, int thr_tbl[], int count)
+{
+ int thr, i;
+ uint8_t spread;
+
+ for (i = 0; i < count; i++) {
+ thr = thr_tbl[i];
+ spread = spread_from_index(thr);
+ sched->sched_grp[group].spread_thrs[spread]++;
+ }
+}
+
+static inline void spread_thrs_dec(odp_schedule_group_t group, int thr_tbl[], int count)
+{
+ int thr, i;
+ uint8_t spread;
+
+ for (i = 0; i < count; i++) {
+ thr = thr_tbl[i];
+ spread = spread_from_index(thr);
+ sched->sched_grp[group].spread_thrs[spread]--;
+ }
+}
+
+static inline int threads_from_mask(int thr_tbl[], int count, const odp_thrmask_t *mask)
+{
+ int i;
+ int thr = odp_thrmask_first(mask);
+
+ for (i = 0; i < count; i++) {
+ if (thr < 0) {
+ _ODP_ERR("No more threads in the mask\n");
+ return -1;
+ }
+
+ thr_tbl[i] = thr;
+ thr = odp_thrmask_next(mask, thr);
+ }
+
+ return 0;
+}
+
+static odp_schedule_group_t schedule_group_create(const char *name,
+ const odp_thrmask_t *mask)
+{
+ odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
+ int count, i;
+
+ count = odp_thrmask_count(mask);
+ if (count < 0) {
+ _ODP_ERR("Bad thread count\n");
+ return ODP_SCHED_GROUP_INVALID;
+ }
+
+ int thr_tbl[ODP_THREAD_COUNT_MAX];
+
+ if (count && threads_from_mask(thr_tbl, count, mask))
+ return ODP_SCHED_GROUP_INVALID;
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ for (i = SCHED_GROUP_NAMED; i < NUM_SCHED_GRPS; i++) {
+ if (!sched->sched_grp[i].allocated) {
+ char *grp_name = sched->sched_grp[i].name;
+
+ if (name == NULL)
+ grp_name[0] = 0;
+ else
+ _odp_strcpy(grp_name, name,
+ ODP_SCHED_GROUP_NAME_LEN);
+
+ grp_update_mask(i, mask);
+ group = (odp_schedule_group_t)i;
+ spread_thrs_inc(group, thr_tbl, count);
+ sched->sched_grp[i].allocated = 1;
+ break;
+ }
+ }
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return group;
+}
+
+static int schedule_group_destroy(odp_schedule_group_t group)
+{
+ odp_thrmask_t zero;
+ int i;
+
+ if (group >= NUM_SCHED_GRPS || group < SCHED_GROUP_NAMED) {
+ _ODP_ERR("Bad group %i\n", group);
+ return -1;
+ }
+
+ odp_thrmask_zero(&zero);
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (sched->sched_grp[group].allocated == 0) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ _ODP_ERR("Group not created: %i\n", group);
+ return -1;
+ }
+
+ grp_update_mask(group, &zero);
+
+ for (i = 0; i < MAX_SPREAD; i++)
+ sched->sched_grp[group].spread_thrs[i] = 0;
+
+ memset(sched->sched_grp[group].name, 0, ODP_SCHED_GROUP_NAME_LEN);
+ sched->sched_grp[group].allocated = 0;
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return 0;
+}
+
+static odp_schedule_group_t schedule_group_lookup(const char *name)
+{
+ odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
+ int i;
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ for (i = SCHED_GROUP_NAMED; i < NUM_SCHED_GRPS; i++) {
+ if (strcmp(name, sched->sched_grp[i].name) == 0) {
+ group = (odp_schedule_group_t)i;
+ break;
+ }
+ }
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return group;
+}
+
+static int schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
+{
+ int i, count, thr;
+ odp_thrmask_t new_mask;
+
+ if (group >= NUM_SCHED_GRPS || group < SCHED_GROUP_NAMED) {
+ _ODP_ERR("Bad group %i\n", group);
+ return -1;
+ }
+
+ count = odp_thrmask_count(mask);
+ if (count <= 0) {
+ _ODP_ERR("No threads in the mask\n");
+ return -1;
+ }
+
+ int thr_tbl[count];
+
+ thr = odp_thrmask_first(mask);
+ for (i = 0; i < count; i++) {
+ if (thr < 0) {
+ _ODP_ERR("No more threads in the mask\n");
+ return -1;
+ }
+
+ thr_tbl[i] = thr;
+ thr = odp_thrmask_next(mask, thr);
+ }
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (sched->sched_grp[group].allocated == 0) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ _ODP_ERR("Bad group status\n");
+ return -1;
+ }
+
+ spread_thrs_inc(group, thr_tbl, count);
+
+ odp_thrmask_or(&new_mask, &sched->sched_grp[group].mask, mask);
+ grp_update_mask(group, &new_mask);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return 0;
+}
+
+static int schedule_group_leave(odp_schedule_group_t group, const odp_thrmask_t *mask)
+{
+ int i, count, thr;
+ odp_thrmask_t new_mask;
+
+ if (group >= NUM_SCHED_GRPS || group < SCHED_GROUP_NAMED) {
+ _ODP_ERR("Bad group %i\n", group);
+ return -1;
+ }
+
+ count = odp_thrmask_count(mask);
+ if (count <= 0) {
+ _ODP_ERR("No threads in the mask\n");
+ return -1;
+ }
+
+ int thr_tbl[count];
+
+ thr = odp_thrmask_first(mask);
+ for (i = 0; i < count; i++) {
+ if (thr < 0) {
+ _ODP_ERR("No more threads in the mask\n");
+ return -1;
+ }
+
+ thr_tbl[i] = thr;
+ thr = odp_thrmask_next(mask, thr);
+ }
+
+ odp_thrmask_xor(&new_mask, mask, &sched->mask_all);
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (sched->sched_grp[group].allocated == 0) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ _ODP_ERR("Bad group status\n");
+ return -1;
+ }
+
+ spread_thrs_dec(group, thr_tbl, count);
+
+ odp_thrmask_and(&new_mask, &sched->sched_grp[group].mask, &new_mask);
+ grp_update_mask(group, &new_mask);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return 0;
+}
+
+static int schedule_group_thrmask(odp_schedule_group_t group,
+ odp_thrmask_t *thrmask)
+{
+ int ret;
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (group < NUM_SCHED_GRPS && sched->sched_grp[group].allocated) {
+ *thrmask = sched->sched_grp[group].mask;
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return ret;
+}
+
+static int schedule_group_info(odp_schedule_group_t group,
+ odp_schedule_group_info_t *info)
+{
+ int ret;
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (group < NUM_SCHED_GRPS && sched->sched_grp[group].allocated) {
+ info->name = sched->sched_grp[group].name;
+ info->thrmask = sched->sched_grp[group].mask;
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return ret;
+}
+
+static int schedule_thr_add(odp_schedule_group_t group, int thr)
+{
+ odp_thrmask_t mask;
+ odp_thrmask_t new_mask;
+
+ if (group < 0 || group >= SCHED_GROUP_NAMED)
+ return -1;
+
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr);
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (!sched->sched_grp[group].allocated) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return 0;
+ }
+
+ odp_thrmask_or(&new_mask, &sched->sched_grp[group].mask, &mask);
+ spread_thrs_inc(group, &thr, 1);
+ grp_update_mask(group, &new_mask);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ return 0;
+}
+
+static int schedule_thr_rem(odp_schedule_group_t group, int thr)
+{
+ odp_thrmask_t mask;
+ odp_thrmask_t new_mask;
+
+ if (group < 0 || group >= SCHED_GROUP_NAMED)
+ return -1;
+
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr);
+ odp_thrmask_xor(&new_mask, &mask, &sched->mask_all);
+
+ odp_ticketlock_lock(&sched->grp_lock);
+
+ if (!sched->sched_grp[group].allocated) {
+ odp_ticketlock_unlock(&sched->grp_lock);
+ return 0;
+ }
+
+ odp_thrmask_and(&new_mask, &sched->sched_grp[group].mask, &new_mask);
+ spread_thrs_dec(group, &thr, 1);
+ grp_update_mask(group, &new_mask);
+
+ odp_ticketlock_unlock(&sched->grp_lock);
+
+ return 0;
+}
+
+static void schedule_prefetch(int num)
+{
+ (void)num;
+}
+
+static int schedule_num_grps(void)
+{
+ return NUM_SCHED_GRPS - SCHED_GROUP_NAMED;
+}
+
+static void schedule_get_config(schedule_config_t *config)
+{
+ *config = sched->config_if;
+}
+
+static int schedule_capability(odp_schedule_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_schedule_capability_t));
+
+ capa->max_ordered_locks = schedule_max_ordered_locks();
+ capa->max_groups = schedule_num_grps();
+ capa->max_prios = schedule_num_prio();
+ capa->max_queues = sched->max_queues;
+ capa->max_queue_size = _odp_queue_glb->config.max_queue_size;
+ capa->max_flow_id = BUF_HDR_MAX_FLOW_ID;
+ capa->order_wait = ODP_SUPPORT_YES;
+
+ return 0;
+}
+
+static void schedule_print(void)
+{
+ int spr, prio, grp, pos;
+ uint32_t num_queues, num_active;
+ ring_u32_t *ring;
+ odp_schedule_capability_t capa;
+ int num_spread = sched->config.num_spread;
+ const int col_width = 24;
+ const int size = 512;
+ char str[size];
+
+ (void)schedule_capability(&capa);
+
+ _ODP_PRINT("\nScheduler debug info\n");
+ _ODP_PRINT("--------------------\n");
+ _ODP_PRINT(" scheduler: basic\n");
+ _ODP_PRINT(" max groups: %u\n", capa.max_groups);
+ _ODP_PRINT(" max priorities: %u\n", capa.max_prios);
+ _ODP_PRINT(" num spread: %i\n", num_spread);
+ _ODP_PRINT(" prefer ratio: %u\n", sched->config.prefer_ratio);
+ _ODP_PRINT("\n");
+
+ pos = 0;
+ pos += _odp_snprint(&str[pos], size - pos, " Number of active event queues:\n");
+ pos += _odp_snprint(&str[pos], size - pos, " spread\n");
+ pos += _odp_snprint(&str[pos], size - pos, " ");
+
+ for (spr = 0; spr < num_spread; spr++)
+ pos += _odp_snprint(&str[pos], size - pos, " %7i", spr);
+
+ _ODP_PRINT("%s\n", str);
+
+ for (prio = 0; prio < NUM_PRIO; prio++) {
+ for (grp = 0; grp < NUM_SCHED_GRPS; grp++)
+ if (sched->prio_q_mask[grp][prio])
+ break;
+
+ if (grp == NUM_SCHED_GRPS)
+ continue;
+
+ _ODP_PRINT(" prio: %i\n", prio);
+
+ for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
+ if (sched->sched_grp[grp].allocated == 0)
+ continue;
+
+ pos = 0;
+ pos += _odp_snprint(&str[pos], size - pos, " group %i:", grp);
+
+ for (spr = 0; spr < num_spread; spr++) {
+ num_queues = sched->prio_q_count[grp][prio][spr];
+ ring = &sched->prio_q[grp][prio][spr].ring;
+ num_active = ring_u32_len(ring);
+ pos += _odp_snprint(&str[pos], size - pos, " %3u/%3u",
+ num_active, num_queues);
+ }
+
+ _ODP_PRINT("%s\n", str);
+ }
+ }
+
+ _ODP_PRINT("\n Number of threads per schedule group:\n");
+ _ODP_PRINT(" name spread\n");
+
+ for (grp = 0; grp < NUM_SCHED_GRPS; grp++) {
+ if (sched->sched_grp[grp].allocated == 0)
+ continue;
+
+ pos = 0;
+ pos += _odp_snprint(&str[pos], size - pos, " group %i: %-*s", grp, col_width,
+ sched->sched_grp[grp].name);
+
+ for (spr = 0; spr < num_spread; spr++)
+ pos += _odp_snprint(&str[pos], size - pos, " %u",
+ sched->sched_grp[grp].spread_thrs[spr]);
+
+ _ODP_PRINT("%s\n", str);
+ }
+
+ _ODP_PRINT("\n");
+}
+
+/* Returns spread for queue debug prints */
+int _odp_sched_basic_get_spread(uint32_t queue_index)
+{
+ return sched->queue[queue_index].spread;
+}
+
+const _odp_schedule_api_fn_t _odp_schedule_basic_api;
+const _odp_schedule_api_fn_t _odp_schedule_basic_sleep_api;
+
+static const _odp_schedule_api_fn_t *sched_api(void)
+{
+ if (sched->powersave.poll_time > 0)
+ return &_odp_schedule_basic_sleep_api;
+
+ return &_odp_schedule_basic_api;
+}
+
+/* Fill in scheduler interface */
+const schedule_fn_t _odp_schedule_basic_fn = {
+ .pktio_start = schedule_pktio_start,
+ .thr_add = schedule_thr_add,
+ .thr_rem = schedule_thr_rem,
+ .num_grps = schedule_num_grps,
+ .create_queue = schedule_create_queue,
+ .destroy_queue = schedule_destroy_queue,
+ .sched_queue = schedule_sched_queue,
+ .ord_enq_multi = schedule_ord_enq_multi,
+ .init_global = schedule_init_global,
+ .term_global = schedule_term_global,
+ .init_local = schedule_init_local,
+ .term_local = schedule_term_local,
+ .order_lock = order_lock,
+ .order_unlock = order_unlock,
+ .max_ordered_locks = schedule_max_ordered_locks,
+ .get_config = schedule_get_config,
+ .sched_api = sched_api,
+};
+
+/* Fill in scheduler API calls */
+const _odp_schedule_api_fn_t _odp_schedule_basic_api = {
+ .schedule_wait_time = schedule_wait_time,
+ .schedule_capability = schedule_capability,
+ .schedule_config_init = schedule_config_init,
+ .schedule_config = schedule_config,
+ .schedule = schedule,
+ .schedule_multi = schedule_multi,
+ .schedule_multi_wait = schedule_multi_wait,
+ .schedule_multi_no_wait = schedule_multi_no_wait,
+ .schedule_pause = schedule_pause,
+ .schedule_resume = schedule_resume,
+ .schedule_release_atomic = schedule_release_atomic,
+ .schedule_release_ordered = schedule_release_ordered,
+ .schedule_prefetch = schedule_prefetch,
+ .schedule_min_prio = schedule_min_prio,
+ .schedule_max_prio = schedule_max_prio,
+ .schedule_default_prio = schedule_default_prio,
+ .schedule_num_prio = schedule_num_prio,
+ .schedule_group_create = schedule_group_create,
+ .schedule_group_destroy = schedule_group_destroy,
+ .schedule_group_lookup = schedule_group_lookup,
+ .schedule_group_join = schedule_group_join,
+ .schedule_group_leave = schedule_group_leave,
+ .schedule_group_thrmask = schedule_group_thrmask,
+ .schedule_group_info = schedule_group_info,
+ .schedule_order_lock = schedule_order_lock,
+ .schedule_order_unlock = schedule_order_unlock,
+ .schedule_order_unlock_lock = schedule_order_unlock_lock,
+ .schedule_order_lock_start = schedule_order_lock_start,
+ .schedule_order_lock_wait = schedule_order_lock_wait,
+ .schedule_order_wait = order_lock,
+ .schedule_print = schedule_print
+};
+
+/* API functions used when powersave is enabled in the config file. */
+const _odp_schedule_api_fn_t _odp_schedule_basic_sleep_api = {
+ .schedule_wait_time = schedule_wait_time,
+ .schedule_capability = schedule_capability,
+ .schedule_config_init = schedule_config_init,
+ .schedule_config = schedule_config,
+ /* Only the following *_sleep functions differ from _odp_schedule_basic_api */
+ .schedule = schedule_sleep,
+ .schedule_multi = schedule_multi_sleep,
+ .schedule_multi_wait = schedule_multi_wait_sleep,
+ /* End of powersave specific functions */
+ .schedule_multi_no_wait = schedule_multi_no_wait,
+ .schedule_pause = schedule_pause,
+ .schedule_resume = schedule_resume,
+ .schedule_release_atomic = schedule_release_atomic,
+ .schedule_release_ordered = schedule_release_ordered,
+ .schedule_prefetch = schedule_prefetch,
+ .schedule_min_prio = schedule_min_prio,
+ .schedule_max_prio = schedule_max_prio,
+ .schedule_default_prio = schedule_default_prio,
+ .schedule_num_prio = schedule_num_prio,
+ .schedule_group_create = schedule_group_create,
+ .schedule_group_destroy = schedule_group_destroy,
+ .schedule_group_lookup = schedule_group_lookup,
+ .schedule_group_join = schedule_group_join,
+ .schedule_group_leave = schedule_group_leave,
+ .schedule_group_thrmask = schedule_group_thrmask,
+ .schedule_group_info = schedule_group_info,
+ .schedule_order_lock = schedule_order_lock,
+ .schedule_order_unlock = schedule_order_unlock,
+ .schedule_order_unlock_lock = schedule_order_unlock_lock,
+ .schedule_order_lock_start = schedule_order_lock_start,
+ .schedule_order_lock_wait = schedule_order_lock_wait,
+ .schedule_order_wait = order_lock,
+ .schedule_print = schedule_print
+};
diff --git a/platform/linux-generic/odp_schedule_if.c b/platform/linux-generic/odp_schedule_if.c
index a9ede98d3..c0f015623 100644
--- a/platform/linux-generic/odp_schedule_if.c
+++ b/platform/linux-generic/odp_schedule_if.c
@@ -1,123 +1,170 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
*/
-#include <odp_schedule_if.h>
+#include <odp/autoheader_internal.h>
-extern const schedule_fn_t schedule_sp_fn;
-extern const schedule_api_t schedule_sp_api;
+#include <odp/api/plat/schedule_inline_types.h>
-extern const schedule_fn_t schedule_default_fn;
-extern const schedule_api_t schedule_default_api;
+#include <odp_schedule_if.h>
+#include <odp_init_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
-extern const schedule_fn_t schedule_iquery_fn;
-extern const schedule_api_t schedule_iquery_api;
+#include <stdlib.h>
+#include <string.h>
-#ifdef ODP_SCHEDULE_SP
-const schedule_fn_t *sched_fn = &schedule_sp_fn;
-const schedule_api_t *sched_api = &schedule_sp_api;
-#elif defined(ODP_SCHEDULE_IQUERY)
-const schedule_fn_t *sched_fn = &schedule_iquery_fn;
-const schedule_api_t *sched_api = &schedule_iquery_api;
-#else
-const schedule_fn_t *sched_fn = &schedule_default_fn;
-const schedule_api_t *sched_api = &schedule_default_api;
-#endif
+/* Enable visibility to inline headers */
+#include <odp/visibility_begin.h>
-uint64_t odp_schedule_wait_time(uint64_t ns)
-{
- return sched_api->schedule_wait_time(ns);
-}
+const _odp_schedule_api_fn_t *_odp_sched_api;
-odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
+int _odp_schedule_configured(void)
{
- return sched_api->schedule(from, wait);
+ return odp_global_rw->schedule_configured;
}
-int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[],
- int num)
+#include <odp/visibility_end.h>
+
+extern const schedule_fn_t _odp_schedule_sp_fn;
+extern const schedule_fn_t _odp_schedule_basic_fn;
+extern const schedule_fn_t _odp_schedule_scalable_fn;
+const schedule_fn_t *_odp_sched_fn;
+int _odp_sched_id;
+
+int odp_schedule_capability(odp_schedule_capability_t *capa)
{
- return sched_api->schedule_multi(from, wait, events, num);
+ return _odp_sched_api->schedule_capability(capa);
}
-void odp_schedule_pause(void)
+void odp_schedule_config_init(odp_schedule_config_t *config)
{
- return sched_api->schedule_pause();
+ memset(config, 0, sizeof(*config));
+
+ _odp_sched_api->schedule_config_init(config);
}
-void odp_schedule_resume(void)
+int odp_schedule_config(const odp_schedule_config_t *config)
{
- return sched_api->schedule_resume();
+ int ret;
+ odp_schedule_config_t defconfig;
+
+ if (odp_global_rw->schedule_configured) {
+ _ODP_ERR("Scheduler has been configured already\n");
+ return -1;
+ }
+
+ if (!config) {
+ odp_schedule_config_init(&defconfig);
+ config = &defconfig;
+ }
+
+ ret = _odp_sched_api->schedule_config(config);
+
+ if (ret >= 0)
+ odp_global_rw->schedule_configured = 1;
+
+ return ret;
}
-void odp_schedule_release_atomic(void)
+int odp_schedule_min_prio(void)
{
- return sched_api->schedule_release_atomic();
+ return _odp_sched_api->schedule_min_prio();
}
-void odp_schedule_release_ordered(void)
+int odp_schedule_max_prio(void)
{
- return sched_api->schedule_release_ordered();
+ return _odp_sched_api->schedule_max_prio();
}
-void odp_schedule_prefetch(int num)
+int odp_schedule_default_prio(void)
{
- return sched_api->schedule_prefetch(num);
+ return _odp_sched_api->schedule_default_prio();
}
int odp_schedule_num_prio(void)
{
- return sched_api->schedule_num_prio();
+ return _odp_sched_api->schedule_num_prio();
}
odp_schedule_group_t odp_schedule_group_create(const char *name,
const odp_thrmask_t *mask)
{
- return sched_api->schedule_group_create(name, mask);
+ return _odp_sched_api->schedule_group_create(name, mask);
}
int odp_schedule_group_destroy(odp_schedule_group_t group)
{
- return sched_api->schedule_group_destroy(group);
+ return _odp_sched_api->schedule_group_destroy(group);
}
odp_schedule_group_t odp_schedule_group_lookup(const char *name)
{
- return sched_api->schedule_group_lookup(name);
+ return _odp_sched_api->schedule_group_lookup(name);
}
int odp_schedule_group_join(odp_schedule_group_t group,
const odp_thrmask_t *mask)
{
- return sched_api->schedule_group_join(group, mask);
+ return _odp_sched_api->schedule_group_join(group, mask);
}
int odp_schedule_group_leave(odp_schedule_group_t group,
const odp_thrmask_t *mask)
{
- return sched_api->schedule_group_leave(group, mask);
+ return _odp_sched_api->schedule_group_leave(group, mask);
}
int odp_schedule_group_thrmask(odp_schedule_group_t group,
odp_thrmask_t *thrmask)
{
- return sched_api->schedule_group_thrmask(group, thrmask);
+ return _odp_sched_api->schedule_group_thrmask(group, thrmask);
}
int odp_schedule_group_info(odp_schedule_group_t group,
odp_schedule_group_info_t *info)
{
- return sched_api->schedule_group_info(group, info);
+ return _odp_sched_api->schedule_group_info(group, info);
}
-void odp_schedule_order_lock(unsigned lock_index)
+void odp_schedule_print(void)
{
- return sched_api->schedule_order_lock(lock_index);
+ _odp_sched_api->schedule_print();
+}
+
+int _odp_schedule_init_global(void)
+{
+ const char *sched = getenv("ODP_SCHEDULER");
+
+ if (sched == NULL || !strcmp(sched, "default"))
+ sched = _ODP_SCHEDULE_DEFAULT;
+
+ _ODP_PRINT("Using scheduler '%s'\n", sched);
+
+ if (!strcmp(sched, "basic")) {
+ _odp_sched_id = _ODP_SCHED_ID_BASIC;
+ _odp_sched_fn = &_odp_schedule_basic_fn;
+ } else if (!strcmp(sched, "sp")) {
+ _odp_sched_id = _ODP_SCHED_ID_SP;
+ _odp_sched_fn = &_odp_schedule_sp_fn;
+ } else if (!strcmp(sched, "scalable")) {
+ _odp_sched_id = _ODP_SCHED_ID_SCALABLE;
+ _odp_sched_fn = &_odp_schedule_scalable_fn;
+ } else {
+ _ODP_ABORT("Unknown scheduler specified via ODP_SCHEDULER\n");
+ return -1;
+ }
+
+ if (_odp_sched_fn->init_global())
+ return -1;
+
+ _odp_sched_api = _odp_sched_fn->sched_api();
+
+ return 0;
}
-void odp_schedule_order_unlock(unsigned lock_index)
+int _odp_schedule_term_global(void)
{
- return sched_api->schedule_order_unlock(lock_index);
+ return _odp_sched_fn->term_global();
}
diff --git a/platform/linux-generic/odp_schedule_iquery.c b/platform/linux-generic/odp_schedule_iquery.c
deleted file mode 100644
index c38c7c01a..000000000
--- a/platform/linux-generic/odp_schedule_iquery.c
+++ /dev/null
@@ -1,1552 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/schedule.h>
-#include <odp_schedule_if.h>
-#include <odp/api/align.h>
-#include <odp/api/queue.h>
-#include <odp/api/shared_memory.h>
-#include <odp_internal.h>
-#include <odp_debug_internal.h>
-#include <odp_ring_internal.h>
-#include <odp_buffer_internal.h>
-#include <odp_bitmap_internal.h>
-#include <odp/api/thread.h>
-#include <odp/api/time.h>
-#include <odp/api/rwlock.h>
-#include <odp/api/hints.h>
-#include <odp/api/cpu.h>
-#include <odp/api/thrmask.h>
-#include <odp/api/packet_io.h>
-#include <odp_config_internal.h>
-
-/* Should remove this dependency */
-#include <odp_queue_internal.h>
-
-/* Number of priority levels */
-#define NUM_SCHED_PRIO 8
-
-ODP_STATIC_ASSERT(ODP_SCHED_PRIO_LOWEST == (NUM_SCHED_PRIO - 1),
- "lowest_prio_does_not_match_with_num_prios");
-
-ODP_STATIC_ASSERT((ODP_SCHED_PRIO_NORMAL > 0) &&
- (ODP_SCHED_PRIO_NORMAL < (NUM_SCHED_PRIO - 1)),
- "normal_prio_is_not_between_highest_and_lowest");
-
-/* Number of scheduling groups */
-#define NUM_SCHED_GRPS 256
-
-/* Start of named groups in group mask arrays */
-#define SCHED_GROUP_NAMED (ODP_SCHED_GROUP_CONTROL + 1)
-
-/* Instantiate a WAPL bitmap to be used as queue index bitmap */
-typedef WAPL_BITMAP(ODP_CONFIG_QUEUES) queue_index_bitmap_t;
-
-typedef struct {
- odp_rwlock_t lock;
- queue_index_bitmap_t queues; /* queues in this priority level */
-} sched_prio_t;
-
-typedef struct {
- odp_rwlock_t lock;
- bool allocated;
- odp_thrmask_t threads; /* threads subscribe to this group */
- queue_index_bitmap_t queues; /* queues in this group */
- char name[ODP_SCHED_GROUP_NAME_LEN];
-} sched_group_t;
-
-/* Packet input poll command queues */
-#define PKTIO_CMD_QUEUES 4
-
-/* Maximum number of packet input queues per command */
-#define MAX_PKTIN 16
-
-/* Maximum number of packet IO interfaces */
-#define NUM_PKTIO ODP_CONFIG_PKTIO_ENTRIES
-
-/* Maximum number of pktio poll commands */
-#define NUM_PKTIO_CMD (MAX_PKTIN * NUM_PKTIO)
-
-/* Not a valid index */
-#define NULL_INDEX ((uint32_t)-1)
-/* Pktio command is free */
-#define PKTIO_CMD_FREE ((uint32_t)-1)
-
-/* Packet IO poll queue ring size. In worst case, all pktios
- * have all pktins enabled and one poll command is created per
- * pktin queue. The ring size must be larger than or equal to
- * NUM_PKTIO_CMD / PKTIO_CMD_QUEUES, so that it can hold all
- * poll commands in the worst case.
- */
-#define PKTIO_RING_SIZE (NUM_PKTIO_CMD / PKTIO_CMD_QUEUES)
-
-/* Mask for wrapping around pktio poll command index */
-#define PKTIO_RING_MASK (PKTIO_RING_SIZE - 1)
-
-/* Maximum number of dequeues */
-#define MAX_DEQ CONFIG_BURST_SIZE
-
-/* Instantiate a RING data structure as pktio command queue */
-typedef struct {
- /* Ring header */
- ring_t ring;
-
- /* Ring data: pktio poll command indexes */
- uint32_t cmd_index[PKTIO_RING_SIZE];
-} pktio_cmd_queue_t ODP_ALIGNED_CACHE;
-
-/* Packet IO poll command */
-typedef struct {
- int pktio;
- int count;
- int pktin[MAX_PKTIN];
- uint32_t index;
-} pktio_cmd_t;
-
-/* Collect the pktio poll resources */
-typedef struct {
- odp_rwlock_t lock;
- /* count active commands per pktio interface */
- int actives[NUM_PKTIO];
- pktio_cmd_t commands[NUM_PKTIO_CMD];
- pktio_cmd_queue_t queues[PKTIO_CMD_QUEUES];
-} pktio_poll_t;
-
-/* Forward declaration */
-typedef struct sched_thread_local sched_thread_local_t;
-
-/* Order context of a queue */
-typedef struct {
- /* Current ordered context id */
- odp_atomic_u64_t ctx ODP_ALIGNED_CACHE;
-
- /* Next unallocated context id */
- odp_atomic_u64_t next_ctx;
-
- /* Array of ordered locks */
- odp_atomic_u64_t lock[CONFIG_QUEUE_MAX_ORD_LOCKS];
-
-} order_context_t ODP_ALIGNED_CACHE;
-
-typedef struct {
- odp_shm_t selfie;
-
- /* Schedule priorities */
- sched_prio_t prios[NUM_SCHED_PRIO];
-
- /* Schedule groups */
- sched_group_t groups[NUM_SCHED_GRPS];
-
- /* Cache queue parameters for easy reference */
- odp_schedule_param_t queues[ODP_CONFIG_QUEUES];
-
- /* Poll pktio inputs in spare time */
- pktio_poll_t pktio_poll;
-
- /* Queues send or unwind their availability indications
- * for scheduling, the bool value also serves as a focal
- * point for atomic competition. */
- bool availables[ODP_CONFIG_QUEUES];
-
- /* Quick reference to per thread context */
- sched_thread_local_t *threads[ODP_THREAD_COUNT_MAX];
-
- order_context_t order[ODP_CONFIG_QUEUES];
-} sched_global_t;
-
-/* Per thread events cache */
-typedef struct {
- int count;
- odp_queue_t queue;
- odp_event_t stash[MAX_DEQ], *top;
-} event_cache_t;
-
-/* Ordered stash size */
-#define MAX_ORDERED_STASH 512
-
-/* Storage for stashed enqueue operation arguments */
-typedef struct {
- odp_buffer_hdr_t *buf_hdr[QUEUE_MULTI_MAX];
- queue_entry_t *queue_entry;
- int num;
-} ordered_stash_t;
-
-/* Ordered lock states */
-typedef union {
- uint8_t u8[CONFIG_QUEUE_MAX_ORD_LOCKS];
- uint32_t all;
-} lock_called_t;
-
-ODP_STATIC_ASSERT(sizeof(lock_called_t) == sizeof(uint32_t),
- "Lock_called_values_do_not_fit_in_uint32");
-
-/* Instantiate a sparse bitmap to store thread's interested
- * queue indexes per priority.
- */
-typedef SPARSE_BITMAP(ODP_CONFIG_QUEUES) queue_index_sparse_t;
-
-struct sched_thread_local {
- int thread;
- bool pause;
-
- /* Cache events only for atomic queue */
- event_cache_t cache;
-
- /* Saved atomic context */
- bool *atomic;
-
- /* Record the pktio polls have done */
- uint16_t pktin_polls;
-
- /* Interested queue indexes to be checked by thread
- * at each priority level for scheduling, and a round
- * robin iterator to improve fairness between queues
- * in the same priority level.
- */
- odp_rwlock_t lock;
- queue_index_sparse_t indexes[NUM_SCHED_PRIO];
- sparse_bitmap_iterator_t iterators[NUM_SCHED_PRIO];
-
- struct {
- /* Source queue index */
- uint32_t src_queue;
- uint64_t ctx; /**< Ordered context id */
- int stash_num; /**< Number of stashed enqueue operations */
- uint8_t in_order; /**< Order status */
- lock_called_t lock_called; /**< States of ordered locks */
- /** Storage for stashed enqueue operations */
- ordered_stash_t stash[MAX_ORDERED_STASH];
- } ordered;
-};
-
-/* Global scheduler context */
-static sched_global_t *sched;
-
-/* Thread local scheduler context */
-static __thread sched_thread_local_t thread_local;
-
-static int schedule_init_global(void)
-{
- odp_shm_t shm;
- int i, k, prio, group;
-
- ODP_DBG("Schedule[iquery] init ... ");
-
- shm = odp_shm_reserve("odp_scheduler_iquery",
- sizeof(sched_global_t),
- ODP_CACHE_LINE_SIZE, 0);
-
- sched = odp_shm_addr(shm);
-
- if (sched == NULL) {
- ODP_ERR("Schedule[iquery] "
- "init: shm reserve.\n");
- return -1;
- }
-
- memset(sched, 0, sizeof(sched_global_t));
-
- sched->selfie = shm;
-
- for (prio = 0; prio < NUM_SCHED_PRIO; prio++)
- odp_rwlock_init(&sched->prios[prio].lock);
-
- for (group = 0; group < NUM_SCHED_GRPS; group++) {
- sched->groups[group].allocated = false;
- odp_rwlock_init(&sched->groups[group].lock);
- }
-
- odp_rwlock_init(&sched->pktio_poll.lock);
-
- for (i = 0; i < PKTIO_CMD_QUEUES; i++) {
- pktio_cmd_queue_t *queue =
- &sched->pktio_poll.queues[i];
-
- ring_init(&queue->ring);
-
- for (k = 0; k < PKTIO_RING_SIZE; k++)
- queue->cmd_index[k] = RING_EMPTY;
- }
-
- for (i = 0; i < NUM_PKTIO_CMD; i++)
- sched->pktio_poll.commands[i].index = PKTIO_CMD_FREE;
-
- ODP_DBG("done\n");
- return 0;
-}
-
-static int schedule_term_global(void)
-{
- uint32_t i;
- odp_shm_t shm = sched->selfie;
-
- for (i = 0; i < ODP_CONFIG_QUEUES; i++) {
- int count = 0;
- odp_event_t events[1];
-
- if (sched->availables[i])
- count = sched_cb_queue_deq_multi(i, events, 1);
-
- if (count < 0)
- sched_cb_queue_destroy_finalize(i);
- else if (count > 0)
- ODP_ERR("Queue (%d) not empty\n", i);
- }
-
- memset(sched, 0, sizeof(sched_global_t));
-
- if (odp_shm_free(shm) < 0) {
- ODP_ERR("Schedule[iquery] "
- "term: shm release.\n");
- return -1;
- }
- return 0;
-}
-
-/*
- * These APIs are used to manipulate thread's interests.
- */
-static void thread_set_interest(sched_thread_local_t *thread,
- unsigned int queue_index, int prio);
-
-static void thread_clear_interest(sched_thread_local_t *thread,
- unsigned int queue_index, int prio);
-
-static void thread_set_interests(sched_thread_local_t *thread,
- queue_index_bitmap_t *set);
-
-static void thread_clear_interests(sched_thread_local_t *thread,
- queue_index_bitmap_t *clear);
-
-static void sched_thread_local_reset(void)
-{
- int prio;
- queue_index_sparse_t *index;
- sparse_bitmap_iterator_t *iterator;
-
- memset(&thread_local, 0, sizeof(sched_thread_local_t));
-
- thread_local.thread = odp_thread_id();
- thread_local.cache.queue = ODP_QUEUE_INVALID;
- thread_local.ordered.src_queue = NULL_INDEX;
-
- odp_rwlock_init(&thread_local.lock);
-
- for (prio = 0; prio < NUM_SCHED_PRIO; prio++) {
- index = &thread_local.indexes[prio];
- iterator = &thread_local.iterators[prio];
-
- sparse_bitmap_zero(index);
- sparse_bitmap_iterator(iterator, index);
- }
-}
-
-static int schedule_init_local(void)
-{
- int group;
- sched_group_t *G;
- queue_index_bitmap_t collect;
-
- wapl_bitmap_zero(&collect);
- sched_thread_local_reset();
-
- /* Collect all queue indexes of the schedule groups
- * which this thread has subscribed
- */
- for (group = 0; group < NUM_SCHED_GRPS; group++) {
- G = &sched->groups[group];
- odp_rwlock_read_lock(&G->lock);
-
- if ((group < SCHED_GROUP_NAMED || G->allocated) &&
- odp_thrmask_isset(&G->threads, thread_local.thread))
- wapl_bitmap_or(&collect, &collect, &G->queues);
-
- odp_rwlock_read_unlock(&G->lock);
- }
-
- /* Distribute the above collected queue indexes into
- * thread local interests per priority level.
- */
- thread_set_interests(&thread_local, &collect);
-
- /* "Night gathers, and now my watch begins..." */
- sched->threads[thread_local.thread] = &thread_local;
- return 0;
-}
-
-static inline void schedule_release_context(void);
-
-static int schedule_term_local(void)
-{
- int group;
- sched_group_t *G;
-
- if (thread_local.cache.count) {
- ODP_ERR("Locally pre-scheduled events exist.\n");
- return -1;
- }
-
- schedule_release_context();
-
- /* Unsubscribe all named schedule groups */
- for (group = SCHED_GROUP_NAMED;
- group < NUM_SCHED_GRPS; group++) {
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- if (G->allocated && odp_thrmask_isset(
- &G->threads, thread_local.thread))
- odp_thrmask_clr(&G->threads, thread_local.thread);
-
- odp_rwlock_write_unlock(&G->lock);
- }
-
- /* "...for this night and all the nights to come." */
- sched->threads[thread_local.thread] = NULL;
- sched_thread_local_reset();
- return 0;
-}
-
-static int init_sched_queue(uint32_t queue_index,
- const odp_schedule_param_t *sched_param)
-{
- int prio, group, thread, i;
- sched_prio_t *P;
- sched_group_t *G;
- sched_thread_local_t *local;
-
- prio = sched_param->prio;
- group = sched_param->group;
-
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- /* Named schedule group must be created prior
- * to queue creation to this group.
- */
- if (group >= SCHED_GROUP_NAMED && !G->allocated) {
- odp_rwlock_write_unlock(&G->lock);
- return -1;
- }
-
- /* Record the queue in its priority level globally */
- P = &sched->prios[prio];
-
- odp_rwlock_write_lock(&P->lock);
- wapl_bitmap_set(&P->queues, queue_index);
- odp_rwlock_write_unlock(&P->lock);
-
- /* Record the queue in its schedule group */
- wapl_bitmap_set(&G->queues, queue_index);
-
- /* Cache queue parameters for easy reference */
- memcpy(&sched->queues[queue_index],
- sched_param, sizeof(odp_schedule_param_t));
-
- odp_atomic_init_u64(&sched->order[queue_index].ctx, 0);
- odp_atomic_init_u64(&sched->order[queue_index].next_ctx, 0);
-
- for (i = 0; i < CONFIG_QUEUE_MAX_ORD_LOCKS; i++)
- odp_atomic_init_u64(&sched->order[queue_index].lock[i], 0);
-
- /* Update all threads in this schedule group to
- * start check this queue index upon scheduling.
- */
- thread = odp_thrmask_first(&G->threads);
- while (thread >= 0) {
- local = sched->threads[thread];
- thread_set_interest(local, queue_index, prio);
- thread = odp_thrmask_next(&G->threads, thread);
- }
-
- odp_rwlock_write_unlock(&G->lock);
- return 0;
-}
-
-/*
- * Must be called with schedule group's rwlock held.
- * This is also being used in destroy_schedule_group()
- * to destroy all orphan queues while destroying a whole
- * schedule group.
- */
-static void __destroy_sched_queue(
- sched_group_t *G, uint32_t queue_index)
-{
- int prio, thread;
- sched_prio_t *P;
- sched_thread_local_t *local;
-
- prio = sched->queues[queue_index].prio;
-
- /* Forget the queue in its schedule group */
- wapl_bitmap_clear(&G->queues, queue_index);
-
- /* Forget queue schedule parameters */
- memset(&sched->queues[queue_index],
- 0, sizeof(odp_schedule_param_t));
-
- /* Update all threads in this schedule group to
- * stop check this queue index upon scheduling.
- */
- thread = odp_thrmask_first(&G->threads);
- while (thread >= 0) {
- local = sched->threads[thread];
- thread_clear_interest(local, queue_index, prio);
- thread = odp_thrmask_next(&G->threads, thread);
- }
-
- /* Forget the queue in its priority level globally */
- P = &sched->prios[prio];
-
- odp_rwlock_write_lock(&P->lock);
- wapl_bitmap_clear(&P->queues, queue_index);
- odp_rwlock_write_unlock(&P->lock);
-}
-
-static void destroy_sched_queue(uint32_t queue_index)
-{
- int group;
- sched_group_t *G;
-
- group = sched->queues[queue_index].group;
-
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- /* Named schedule group could have been destroyed
- * earlier and left these orphan queues.
- */
- if (group >= SCHED_GROUP_NAMED && !G->allocated) {
- odp_rwlock_write_unlock(&G->lock);
- return;
- }
-
- __destroy_sched_queue(G, queue_index);
- odp_rwlock_write_unlock(&G->lock);
-
- if (sched->queues[queue_index].sync == ODP_SCHED_SYNC_ORDERED &&
- odp_atomic_load_u64(&sched->order[queue_index].ctx) !=
- odp_atomic_load_u64(&sched->order[queue_index].next_ctx))
- ODP_ERR("queue reorder incomplete\n");
-}
-
-static int pktio_cmd_queue_hash(int pktio, int pktin)
-{
- return (pktio ^ pktin) % PKTIO_CMD_QUEUES;
-}
-
-static inline pktio_cmd_t *alloc_pktio_cmd(void)
-{
- int i;
- pktio_cmd_t *cmd = NULL;
-
- odp_rwlock_write_lock(&sched->pktio_poll.lock);
-
- /* Find next free command */
- for (i = 0; i < NUM_PKTIO_CMD; i++) {
- if (sched->pktio_poll.commands[i].index
- == PKTIO_CMD_FREE) {
- cmd = &sched->pktio_poll.commands[i];
- cmd->index = i;
- break;
- }
- }
-
- odp_rwlock_write_unlock(&sched->pktio_poll.lock);
- return cmd;
-}
-
-static inline void free_pktio_cmd(pktio_cmd_t *cmd)
-{
- odp_rwlock_write_lock(&sched->pktio_poll.lock);
-
- cmd->index = PKTIO_CMD_FREE;
-
- odp_rwlock_write_unlock(&sched->pktio_poll.lock);
-}
-
-static void schedule_pktio_start(int pktio, int count, int pktin[])
-{
- int i, index;
- pktio_cmd_t *cmd;
-
- if (count > MAX_PKTIN)
- ODP_ABORT("Too many input queues for scheduler\n");
-
- /* Record the active commands count per pktio interface */
- sched->pktio_poll.actives[pktio] = count;
-
- /* Create a pktio poll command per pktin */
- for (i = 0; i < count; i++) {
- cmd = alloc_pktio_cmd();
-
- if (cmd == NULL)
- ODP_ABORT("Scheduler out of pktio commands\n");
-
- index = pktio_cmd_queue_hash(pktio, pktin[i]);
-
- cmd->pktio = pktio;
- cmd->count = 1;
- cmd->pktin[0] = pktin[i];
- ring_enq(&sched->pktio_poll.queues[index].ring,
- PKTIO_RING_MASK, cmd->index);
- }
-}
-
-static int schedule_pktio_stop(int pktio, int pktin ODP_UNUSED)
-{
- int remains;
-
- odp_rwlock_write_lock(&sched->pktio_poll.lock);
-
- sched->pktio_poll.actives[pktio]--;
- remains = sched->pktio_poll.actives[pktio];
-
- odp_rwlock_write_unlock(&sched->pktio_poll.lock);
- return remains;
-}
-
-#define DO_SCHED_LOCK() odp_rwlock_read_lock(&thread_local.lock)
-#define DO_SCHED_UNLOCK() odp_rwlock_read_unlock(&thread_local.lock)
-
-static inline bool do_schedule_prio(int prio);
-
-static inline int pop_cache_events(odp_event_t ev[], unsigned int max)
-{
- int k = 0;
- event_cache_t *cache;
-
- cache = &thread_local.cache;
- while (cache->count && max) {
- ev[k] = *cache->top++;
- k++;
- max--;
- cache->count--;
- }
-
- return k;
-}
-
-static inline void assign_queue_handle(odp_queue_t *handle)
-{
- if (handle)
- *handle = thread_local.cache.queue;
-}
-
-static inline void pktio_poll_input(void)
-{
- int i, hash;
- uint32_t index;
-
- ring_t *ring;
- pktio_cmd_t *cmd;
-
- /*
- * Each thread starts the search for a poll command
- * from the hash(threadID) queue to mitigate contentions.
- * If the queue is empty, it moves to other queues.
- *
- * Most of the times, the search stops on the first
- * command found to optimize multi-threaded performance.
- * A small portion of polls have to do full iteration to
- * avoid packet input starvation when there are less
- * threads than command queues.
- */
- hash = thread_local.thread % PKTIO_CMD_QUEUES;
-
- for (i = 0; i < PKTIO_CMD_QUEUES; i++,
- hash = (hash + 1) % PKTIO_CMD_QUEUES) {
- ring = &sched->pktio_poll.queues[hash].ring;
- index = ring_deq(ring, PKTIO_RING_MASK);
-
- if (odp_unlikely(index == RING_EMPTY))
- continue;
-
- cmd = &sched->pktio_poll.commands[index];
-
- /* Poll packet input */
- if (odp_unlikely(sched_cb_pktin_poll(cmd->pktio,
- cmd->count,
- cmd->pktin))) {
- /* Pktio stopped or closed. Remove poll
- * command and call stop_finalize when all
- * commands of the pktio has been removed.
- */
- if (schedule_pktio_stop(cmd->pktio,
- cmd->pktin[0]) == 0)
- sched_cb_pktio_stop_finalize(cmd->pktio);
-
- free_pktio_cmd(cmd);
- } else {
- /* Continue scheduling the pktio */
- ring_enq(ring, PKTIO_RING_MASK, index);
-
- /* Do not iterate through all pktin poll
- * command queues every time.
- */
- if (odp_likely(thread_local.pktin_polls & 0xF))
- break;
- }
- }
-
- thread_local.pktin_polls++;
-}
-
-/*
- * Schedule queues
- */
-static int do_schedule(odp_queue_t *out_queue,
- odp_event_t out_ev[], unsigned int max_num)
-{
- int prio, count;
-
- /* Consume locally cached events */
- count = pop_cache_events(out_ev, max_num);
- if (count > 0) {
- assign_queue_handle(out_queue);
- return count;
- }
-
- schedule_release_context();
-
- if (odp_unlikely(thread_local.pause))
- return count;
-
- DO_SCHED_LOCK();
- /* Schedule events */
- for (prio = 0; prio < NUM_SCHED_PRIO; prio++) {
- /* Round robin iterate the interested queue
- * indexes in this priority level to compete
- * and consume available queues
- */
- if (!do_schedule_prio(prio))
- continue;
-
- count = pop_cache_events(out_ev, max_num);
- assign_queue_handle(out_queue);
- DO_SCHED_UNLOCK();
- return count;
- }
-
- DO_SCHED_UNLOCK();
-
- /* Poll packet input when there are no events */
- pktio_poll_input();
- return 0;
-}
-
-static int schedule_loop(odp_queue_t *out_queue, uint64_t wait,
- odp_event_t out_ev[], unsigned int max_num)
-{
- int count, first = 1;
- odp_time_t next, wtime;
-
- while (1) {
- count = do_schedule(out_queue, out_ev, max_num);
-
- if (count)
- break;
-
- if (wait == ODP_SCHED_WAIT)
- continue;
-
- if (wait == ODP_SCHED_NO_WAIT)
- break;
-
- if (first) {
- wtime = odp_time_local_from_ns(wait);
- next = odp_time_sum(odp_time_local(), wtime);
- first = 0;
- continue;
- }
-
- if (odp_time_cmp(next, odp_time_local()) < 0)
- break;
- }
-
- return count;
-}
-
-static odp_event_t schedule(odp_queue_t *out_queue, uint64_t wait)
-{
- odp_event_t ev;
-
- ev = ODP_EVENT_INVALID;
-
- schedule_loop(out_queue, wait, &ev, 1);
-
- return ev;
-}
-
-static int schedule_multi(odp_queue_t *out_queue, uint64_t wait,
- odp_event_t events[], int num)
-{
- return schedule_loop(out_queue, wait, events, num);
-}
-
-static void schedule_pause(void)
-{
- thread_local.pause = 1;
-}
-
-static void schedule_resume(void)
-{
- thread_local.pause = 0;
-}
-
-static uint64_t schedule_wait_time(uint64_t ns)
-{
- return ns;
-}
-
-static int number_of_priorites(void)
-{
- return NUM_SCHED_PRIO;
-}
-
-/*
- * Create a named schedule group with pre-defined
- * set of subscription threads.
- *
- * Sched queues belonging to this group must be
- * created after the group creation. Upon creation
- * the group holds 0 sched queues.
- */
-static odp_schedule_group_t schedule_group_create(
- const char *name, const odp_thrmask_t *mask)
-{
- int group;
- sched_group_t *G;
-
- for (group = SCHED_GROUP_NAMED;
- group < NUM_SCHED_GRPS; group++) {
- G = &sched->groups[group];
-
- odp_rwlock_write_lock(&G->lock);
- if (!G->allocated) {
- strncpy(G->name, name ? name : "",
- ODP_SCHED_GROUP_NAME_LEN - 1);
- odp_thrmask_copy(&G->threads, mask);
- wapl_bitmap_zero(&G->queues);
-
- G->allocated = true;
- odp_rwlock_write_unlock(&G->lock);
- return (odp_schedule_group_t)group;
- }
- odp_rwlock_write_unlock(&G->lock);
- }
-
- return ODP_SCHED_GROUP_INVALID;
-}
-
-static inline void __destroy_group_queues(sched_group_t *group)
-{
- unsigned int index;
- queue_index_bitmap_t queues;
- wapl_bitmap_iterator_t it;
-
- /* Constructor */
- wapl_bitmap_zero(&queues);
- wapl_bitmap_copy(&queues, &group->queues);
- wapl_bitmap_iterator(&it, &queues);
-
- /* Walk through the queue index bitmap */
- for (it.start(&it); it.has_next(&it);) {
- index = it.next(&it);
- __destroy_sched_queue(group, index);
- }
-}
-
-/*
- * Destroy a named schedule group.
- */
-static int schedule_group_destroy(odp_schedule_group_t group)
-{
- int done = -1;
- sched_group_t *G;
-
- if (group < SCHED_GROUP_NAMED ||
- group >= NUM_SCHED_GRPS)
- return -1;
-
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- if (G->allocated) {
- /* Destroy all queues in this schedule group
- * and leave no orphan queues.
- */
- __destroy_group_queues(G);
-
- done = 0;
- G->allocated = false;
- wapl_bitmap_zero(&G->queues);
- odp_thrmask_zero(&G->threads);
- memset(G->name, 0, ODP_SCHED_GROUP_NAME_LEN);
- }
-
- odp_rwlock_write_unlock(&G->lock);
- return done;
-}
-
-static odp_schedule_group_t schedule_group_lookup(const char *name)
-{
- int group;
- sched_group_t *G;
-
- for (group = SCHED_GROUP_NAMED;
- group < NUM_SCHED_GRPS; group++) {
- G = &sched->groups[group];
-
- odp_rwlock_read_lock(&G->lock);
- if (strcmp(name, G->name) == 0) {
- odp_rwlock_read_unlock(&G->lock);
- return (odp_schedule_group_t)group;
- }
- odp_rwlock_read_unlock(&G->lock);
- }
-
- return ODP_SCHED_GROUP_INVALID;
-}
-
-static int schedule_group_join(odp_schedule_group_t group,
- const odp_thrmask_t *mask)
-{
- int done = -1, thread;
- sched_group_t *G;
- sched_thread_local_t *local;
-
- /* Named schedule group only */
- if (group < SCHED_GROUP_NAMED ||
- group >= NUM_SCHED_GRPS)
- return done;
-
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- if (G->allocated) {
- /* Make new joined threads to start check
- * queue indexes in this schedule group
- */
- thread = odp_thrmask_first(mask);
- while (thread >= 0) {
- local = sched->threads[thread];
- thread_set_interests(local, &G->queues);
-
- odp_thrmask_set(&G->threads, thread);
- thread = odp_thrmask_next(mask, thread);
- }
- done = 0;
- }
-
- odp_rwlock_write_unlock(&G->lock);
- return done;
-}
-
-static int schedule_group_leave(odp_schedule_group_t group,
- const odp_thrmask_t *mask)
-{
- int done = -1, thread;
- sched_group_t *G;
- sched_thread_local_t *local;
-
- /* Named schedule group only */
- if (group < SCHED_GROUP_NAMED ||
- group >= NUM_SCHED_GRPS)
- return done;
-
- G = &sched->groups[group];
- odp_rwlock_write_lock(&G->lock);
-
- if (G->allocated) {
- /* Make leaving threads to stop check
- * queue indexes in this schedule group
- */
- thread = odp_thrmask_first(mask);
- while (thread >= 0) {
- local = sched->threads[thread];
- thread_clear_interests(local, &G->queues);
-
- odp_thrmask_clr(&G->threads, thread);
- thread = odp_thrmask_next(mask, thread);
- }
- done = 0;
- }
-
- odp_rwlock_write_unlock(&G->lock);
- return done;
-}
-
-static int schedule_group_thrmask(odp_schedule_group_t group,
- odp_thrmask_t *thrmask)
-{
- int done = -1;
- sched_group_t *G;
-
- /* Named schedule group only */
- if (group < SCHED_GROUP_NAMED ||
- group >= NUM_SCHED_GRPS)
- return done;
-
- G = &sched->groups[group];
- odp_rwlock_read_lock(&G->lock);
-
- if (G->allocated && thrmask != NULL) {
- done = 0;
- odp_thrmask_copy(thrmask, &G->threads);
- }
-
- odp_rwlock_read_unlock(&G->lock);
- return done;
-}
-
-static int schedule_group_info(odp_schedule_group_t group,
- odp_schedule_group_info_t *info)
-{
- int done = -1;
- sched_group_t *G;
-
- /* Named schedule group only */
- if (group < SCHED_GROUP_NAMED ||
- group >= NUM_SCHED_GRPS)
- return done;
-
- G = &sched->groups[group];
- odp_rwlock_read_lock(&G->lock);
-
- if (G->allocated && info != NULL) {
- done = 0;
- info->name = G->name;
- odp_thrmask_copy(&info->thrmask, &G->threads);
- }
-
- odp_rwlock_read_unlock(&G->lock);
- return done;
-}
-
-/* This function is a no-op */
-static void schedule_prefetch(int num ODP_UNUSED)
-{
-}
-
-/*
- * Limited to join and leave pre-defined schedule groups
- * before and after thread local initialization or termination.
- */
-static int group_add_thread(odp_schedule_group_t group, int thread)
-{
- sched_group_t *G;
-
- if (group < 0 || group >= SCHED_GROUP_NAMED)
- return -1;
-
- G = &sched->groups[group];
-
- odp_rwlock_write_lock(&G->lock);
- odp_thrmask_set(&G->threads, thread);
- odp_rwlock_write_unlock(&G->lock);
- return 0;
-}
-
-static int group_remove_thread(odp_schedule_group_t group, int thread)
-{
- sched_group_t *G;
-
- if (group < 0 || group >= SCHED_GROUP_NAMED)
- return -1;
-
- G = &sched->groups[group];
-
- odp_rwlock_write_lock(&G->lock);
- odp_thrmask_clr(&G->threads, thread);
- odp_rwlock_write_unlock(&G->lock);
- return 0;
-}
-
-static int number_of_groups(void)
-{
- return NUM_SCHED_GRPS;
-}
-
-static int schedule_sched_queue(uint32_t queue_index)
-{
- /* Set available indications globally */
- sched->availables[queue_index] = true;
- return 0;
-}
-
-static int schedule_unsched_queue(uint32_t queue_index)
-{
- /* Clear available indications globally */
- sched->availables[queue_index] = false;
- return 0;
-}
-
-static void schedule_release_atomic(void)
-{
- unsigned int queue_index;
-
- if ((thread_local.atomic != NULL) &&
- (thread_local.cache.count == 0)) {
- queue_index = thread_local.atomic - sched->availables;
- thread_local.atomic = NULL;
- sched->availables[queue_index] = true;
- }
-}
-
-static inline int ordered_own_turn(uint32_t queue_index)
-{
- uint64_t ctx;
-
- ctx = odp_atomic_load_acq_u64(&sched->order[queue_index].ctx);
-
- return ctx == thread_local.ordered.ctx;
-}
-
-static inline void wait_for_order(uint32_t queue_index)
-{
- /* Busy loop to synchronize ordered processing */
- while (1) {
- if (ordered_own_turn(queue_index))
- break;
- odp_cpu_pause();
- }
-}
-
-/**
- * Perform stashed enqueue operations
- *
- * Should be called only when already in order.
- */
-static inline void ordered_stash_release(void)
-{
- int i;
-
- for (i = 0; i < thread_local.ordered.stash_num; i++) {
- queue_entry_t *queue_entry;
- odp_buffer_hdr_t **buf_hdr;
- int num;
-
- queue_entry = thread_local.ordered.stash[i].queue_entry;
- buf_hdr = thread_local.ordered.stash[i].buf_hdr;
- num = thread_local.ordered.stash[i].num;
-
- queue_fn->enq_multi(qentry_to_int(queue_entry), buf_hdr, num);
- }
- thread_local.ordered.stash_num = 0;
-}
-
-static inline void release_ordered(void)
-{
- uint32_t qi;
- unsigned i;
-
- qi = thread_local.ordered.src_queue;
-
- wait_for_order(qi);
-
- /* Release all ordered locks */
- for (i = 0; i < sched->queues[qi].lock_count; i++) {
- if (!thread_local.ordered.lock_called.u8[i])
- odp_atomic_store_rel_u64(&sched->order[qi].lock[i],
- thread_local.ordered.ctx + 1);
- }
-
- thread_local.ordered.lock_called.all = 0;
- thread_local.ordered.src_queue = NULL_INDEX;
- thread_local.ordered.in_order = 0;
-
- ordered_stash_release();
-
- /* Next thread can continue processing */
- odp_atomic_add_rel_u64(&sched->order[qi].ctx, 1);
-}
-
-static void schedule_release_ordered(void)
-{
- uint32_t queue_index;
-
- queue_index = thread_local.ordered.src_queue;
-
- if (odp_unlikely((queue_index == NULL_INDEX) ||
- thread_local.cache.count))
- return;
-
- release_ordered();
-}
-
-static inline void schedule_release_context(void)
-{
- if (thread_local.ordered.src_queue != NULL_INDEX)
- release_ordered();
- else
- schedule_release_atomic();
-}
-
-static int schedule_ord_enq_multi(queue_t q_int, void *buf_hdr[],
- int num, int *ret)
-{
- int i;
- uint32_t stash_num = thread_local.ordered.stash_num;
- queue_entry_t *dst_queue = qentry_from_int(q_int);
- uint32_t src_queue = thread_local.ordered.src_queue;
-
- if ((src_queue == NULL_INDEX) || thread_local.ordered.in_order)
- return 0;
-
- if (ordered_own_turn(src_queue)) {
- /* Own turn, so can do enqueue directly. */
- thread_local.ordered.in_order = 1;
- ordered_stash_release();
- return 0;
- }
-
- /* Pktout may drop packets, so the operation cannot be stashed. */
- if (dst_queue->s.pktout.pktio != ODP_PKTIO_INVALID ||
- odp_unlikely(stash_num >= MAX_ORDERED_STASH)) {
- /* If the local stash is full, wait until it is our turn and
- * then release the stash and do enqueue directly. */
- wait_for_order(src_queue);
-
- thread_local.ordered.in_order = 1;
-
- ordered_stash_release();
- return 0;
- }
-
- thread_local.ordered.stash[stash_num].queue_entry = dst_queue;
- thread_local.ordered.stash[stash_num].num = num;
- for (i = 0; i < num; i++)
- thread_local.ordered.stash[stash_num].buf_hdr[i] = buf_hdr[i];
-
- thread_local.ordered.stash_num++;
-
- *ret = num;
- return 1;
-}
-
-static void order_lock(void)
-{
- uint32_t queue_index;
-
- queue_index = thread_local.ordered.src_queue;
-
- if (queue_index == NULL_INDEX)
- return;
-
- wait_for_order(queue_index);
-}
-
-static void order_unlock(void)
-{
-}
-
-static void schedule_order_lock(unsigned lock_index)
-{
- odp_atomic_u64_t *ord_lock;
- uint32_t queue_index;
-
- queue_index = thread_local.ordered.src_queue;
-
- ODP_ASSERT(queue_index != NULL_INDEX &&
- lock_index <= sched->queues[queue_index].lock_count &&
- !thread_local.ordered.lock_called.u8[lock_index]);
-
- ord_lock = &sched->order[queue_index].lock[lock_index];
-
- /* Busy loop to synchronize ordered processing */
- while (1) {
- uint64_t lock_seq;
-
- lock_seq = odp_atomic_load_acq_u64(ord_lock);
-
- if (lock_seq == thread_local.ordered.ctx) {
- thread_local.ordered.lock_called.u8[lock_index] = 1;
- return;
- }
- odp_cpu_pause();
- }
-}
-
-static void schedule_order_unlock(unsigned lock_index)
-{
- odp_atomic_u64_t *ord_lock;
- uint32_t queue_index;
-
- queue_index = thread_local.ordered.src_queue;
-
- ODP_ASSERT(queue_index != NULL_INDEX &&
- lock_index <= sched->queues[queue_index].lock_count);
-
- ord_lock = &sched->order[queue_index].lock[lock_index];
-
- ODP_ASSERT(thread_local.ordered.ctx == odp_atomic_load_u64(ord_lock));
-
- odp_atomic_store_rel_u64(ord_lock, thread_local.ordered.ctx + 1);
-}
-
-static unsigned schedule_max_ordered_locks(void)
-{
- return CONFIG_QUEUE_MAX_ORD_LOCKS;
-}
-
-static inline bool is_atomic_queue(unsigned int queue_index)
-{
- return (sched->queues[queue_index].sync == ODP_SCHED_SYNC_ATOMIC);
-}
-
-static inline bool is_ordered_queue(unsigned int queue_index)
-{
- return (sched->queues[queue_index].sync == ODP_SCHED_SYNC_ORDERED);
-}
-
-static void schedule_save_context(uint32_t queue_index)
-{
- if (is_atomic_queue(queue_index)) {
- thread_local.atomic = &sched->availables[queue_index];
- } else if (is_ordered_queue(queue_index)) {
- uint64_t ctx;
- odp_atomic_u64_t *next_ctx;
-
- next_ctx = &sched->order[queue_index].next_ctx;
- ctx = odp_atomic_fetch_inc_u64(next_ctx);
-
- thread_local.ordered.ctx = ctx;
- thread_local.ordered.src_queue = queue_index;
- }
-}
-
-/* Fill in scheduler interface */
-const schedule_fn_t schedule_iquery_fn = {
- .status_sync = 1,
- .pktio_start = schedule_pktio_start,
- .thr_add = group_add_thread,
- .thr_rem = group_remove_thread,
- .num_grps = number_of_groups,
- .init_queue = init_sched_queue,
- .destroy_queue = destroy_sched_queue,
- .sched_queue = schedule_sched_queue,
- .ord_enq_multi = schedule_ord_enq_multi,
- .init_global = schedule_init_global,
- .term_global = schedule_term_global,
- .init_local = schedule_init_local,
- .term_local = schedule_term_local,
- .order_lock = order_lock,
- .order_unlock = order_unlock,
- .max_ordered_locks = schedule_max_ordered_locks,
- .unsched_queue = schedule_unsched_queue,
- .save_context = schedule_save_context
-};
-
-/* Fill in scheduler API calls */
-const schedule_api_t schedule_iquery_api = {
- .schedule_wait_time = schedule_wait_time,
- .schedule = schedule,
- .schedule_multi = schedule_multi,
- .schedule_pause = schedule_pause,
- .schedule_resume = schedule_resume,
- .schedule_release_atomic = schedule_release_atomic,
- .schedule_release_ordered = schedule_release_ordered,
- .schedule_prefetch = schedule_prefetch,
- .schedule_num_prio = number_of_priorites,
- .schedule_group_create = schedule_group_create,
- .schedule_group_destroy = schedule_group_destroy,
- .schedule_group_lookup = schedule_group_lookup,
- .schedule_group_join = schedule_group_join,
- .schedule_group_leave = schedule_group_leave,
- .schedule_group_thrmask = schedule_group_thrmask,
- .schedule_group_info = schedule_group_info,
- .schedule_order_lock = schedule_order_lock,
- .schedule_order_unlock = schedule_order_unlock
-};
-
-static void thread_set_interest(sched_thread_local_t *thread,
- unsigned int queue_index, int prio)
-{
- queue_index_sparse_t *index;
-
- if (thread == NULL)
- return;
-
- if (prio >= NUM_SCHED_PRIO)
- return;
-
- index = &thread->indexes[prio];
-
- odp_rwlock_write_lock(&thread->lock);
- sparse_bitmap_set(index, queue_index);
- odp_rwlock_write_unlock(&thread->lock);
-}
-
-static void thread_clear_interest(sched_thread_local_t *thread,
- unsigned int queue_index, int prio)
-{
- queue_index_sparse_t *index;
-
- if (thread == NULL)
- return;
-
- if (prio >= NUM_SCHED_PRIO)
- return;
-
- index = &thread->indexes[prio];
-
- odp_rwlock_write_lock(&thread->lock);
- sparse_bitmap_clear(index, queue_index);
- odp_rwlock_write_unlock(&thread->lock);
-}
-
-static void thread_set_interests(sched_thread_local_t *thread,
- queue_index_bitmap_t *set)
-{
- int prio;
- sched_prio_t *P;
- unsigned int queue_index;
- queue_index_bitmap_t subset;
- wapl_bitmap_iterator_t it;
-
- if (thread == NULL || set == NULL)
- return;
-
- for (prio = 0; prio < NUM_SCHED_PRIO; prio++) {
- P = &sched->prios[prio];
- odp_rwlock_read_lock(&P->lock);
-
- /* The collection of queue indexes in 'set'
- * may belong to several priority levels.
- */
- wapl_bitmap_zero(&subset);
- wapl_bitmap_and(&subset, &P->queues, set);
-
- odp_rwlock_read_unlock(&P->lock);
-
- /* Add the subset to local indexes */
- wapl_bitmap_iterator(&it, &subset);
- for (it.start(&it); it.has_next(&it);) {
- queue_index = it.next(&it);
- thread_set_interest(thread, queue_index, prio);
- }
- }
-}
-
-static void thread_clear_interests(sched_thread_local_t *thread,
- queue_index_bitmap_t *clear)
-{
- int prio;
- sched_prio_t *P;
- unsigned int queue_index;
- queue_index_bitmap_t subset;
- wapl_bitmap_iterator_t it;
-
- if (thread == NULL || clear == NULL)
- return;
-
- for (prio = 0; prio < NUM_SCHED_PRIO; prio++) {
- P = &sched->prios[prio];
- odp_rwlock_read_lock(&P->lock);
-
- /* The collection of queue indexes in 'clear'
- * may belong to several priority levels.
- */
- wapl_bitmap_zero(&subset);
- wapl_bitmap_and(&subset, &P->queues, clear);
-
- odp_rwlock_read_unlock(&P->lock);
-
- /* Remove the subset from local indexes */
- wapl_bitmap_iterator(&it, &subset);
- for (it.start(&it); it.has_next(&it);) {
- queue_index = it.next(&it);
- thread_clear_interest(thread, queue_index, prio);
- }
- }
-}
-
-static inline bool compete_atomic_queue(unsigned int queue_index)
-{
- bool expected = sched->availables[queue_index];
-
- if (expected && is_atomic_queue(queue_index)) {
- expected = __atomic_compare_exchange_n(
- &sched->availables[queue_index],
- &expected, false, 0,
- __ATOMIC_RELEASE, __ATOMIC_RELAXED);
- }
-
- return expected;
-}
-
-static inline int consume_queue(int prio, unsigned int queue_index)
-{
- int count;
- unsigned int max = MAX_DEQ;
- event_cache_t *cache = &thread_local.cache;
-
- /* Low priorities have smaller batch size to limit
- * head of line blocking latency.
- */
- if (odp_unlikely(MAX_DEQ > 1 && prio > ODP_SCHED_PRIO_DEFAULT))
- max = MAX_DEQ / 2;
-
- /* For ordered queues we want consecutive events to
- * be dispatched to separate threads, so do not cache
- * them locally.
- */
- if (is_ordered_queue(queue_index))
- max = 1;
-
- count = sched_cb_queue_deq_multi(
- queue_index, cache->stash, max);
-
- if (count < 0) {
- DO_SCHED_UNLOCK();
- sched_cb_queue_destroy_finalize(queue_index);
- DO_SCHED_LOCK();
- return 0;
- }
-
- if (count == 0)
- return 0;
-
- cache->top = &cache->stash[0];
- cache->count = count;
- cache->queue = sched_cb_queue_handle(queue_index);
- return count;
-}
-
-static inline bool do_schedule_prio(int prio)
-{
- int nbits, next, end;
- unsigned int queue_index;
- sparse_bitmap_iterator_t *it;
-
- it = &thread_local.iterators[prio];
- nbits = (int)*it->_base.last;
-
- /* No interests at all! */
- if (nbits <= 0)
- return false;
-
- /* In critical path, cannot afford iterator calls,
- * do it manually with internal knowledge
- */
- it->_start = (it->_start + 1) % nbits;
- end = it->_start + nbits;
-
- for (next = it->_start; next < end; next++) {
- queue_index = it->_base.il[next % nbits];
-
- if (!compete_atomic_queue(queue_index))
- continue;
-
- if (!consume_queue(prio, queue_index))
- continue;
-
- return true;
- }
-
- return false;
-}
diff --git a/platform/linux-generic/odp_schedule_scalable.c b/platform/linux-generic/odp_schedule_scalable.c
new file mode 100644
index 000000000..4947353d6
--- /dev/null
+++ b/platform/linux-generic/odp_schedule_scalable.c
@@ -0,0 +1,2207 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/align.h>
+#include <odp/api/atomic.h>
+#include <odp/api/cpu.h>
+#include <odp/api/hints.h>
+#include <odp/api/schedule.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/sync.h>
+#include <odp/api/thread.h>
+#include <odp/api/thrmask.h>
+#include <odp/api/time.h>
+
+#include <odp/api/plat/schedule_inline_types.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp/api/plat/time_inlines.h>
+
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_ishmpool_internal.h>
+
+#include <odp/api/plat/cpu_inlines.h>
+#include <odp_llqueue.h>
+#include <odp_queue_scalable_internal.h>
+#include <odp_schedule_if.h>
+#include <odp_bitset.h>
+#include <odp_event_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_timer_internal.h>
+#include <odp_string_internal.h>
+
+#include <limits.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <odp/api/plat/ticketlock_inlines.h>
+#define LOCK(a) odp_ticketlock_lock((a))
+#define UNLOCK(a) odp_ticketlock_unlock((a))
+
+#define MAXTHREADS ATOM_BITSET_SIZE
+
+#define FLAG_PKTIN 0x80
+
+ODP_STATIC_ASSERT(_ODP_CHECK_IS_POWER2(CONFIG_MAX_SCHED_QUEUES),
+ "Number_of_queues_is_not_power_of_two");
+
+#define SCHED_GROUP_JOIN 0
+#define SCHED_GROUP_LEAVE 1
+#define NUM_AUTO_GROUPS (ODP_SCHED_GROUP_CONTROL + 1)
+
+typedef struct {
+ odp_shm_t shm;
+ _odp_ishm_pool_t *sched_shm_pool;
+ /** Currently used scheduler groups */
+ sched_group_mask_t sg_free;
+ sched_group_t *sg_vec[MAX_SCHED_GROUP];
+ /** Group lock for MT-safe APIs */
+ odp_spinlock_t sched_grp_lock;
+ /** Initialization lock */
+ odp_spinlock_t init_lock;
+ /** Per thread state */
+ sched_scalable_thread_state_t thread_state[MAXTHREADS];
+ uint16_t poll_count[CONFIG_PKTIO_ENTRIES];
+ /* Scheduler interface config options (not used in fast path) */
+ schedule_config_t config_if;
+} sched_global_t;
+
+static sched_global_t *global;
+
+__thread sched_scalable_thread_state_t *_odp_sched_ts;
+
+static int thread_state_init(int tidx)
+{
+ sched_scalable_thread_state_t *ts;
+ uint32_t i;
+
+ _ODP_ASSERT(tidx < MAXTHREADS);
+ ts = &global->thread_state[tidx];
+ ts->atomq = NULL;
+ ts->src_schedq = NULL;
+ ts->rctx = NULL;
+ ts->pause = false;
+ ts->out_of_order = false;
+ ts->tidx = tidx;
+ ts->dequeued = 0;
+ ts->ticket = TICKET_INVALID;
+ ts->priv_rvec_free = 0;
+ ts->rvec_free = (1ULL << TS_RVEC_SIZE) - 1;
+ ts->num_schedq = 0;
+ ts->sg_sem = 1; /* Start with sched group semaphore changed */
+ ts->loop_cnt = 0;
+ memset(ts->sg_actual, 0, sizeof(ts->sg_actual));
+ for (i = 0; i < TS_RVEC_SIZE; i++) {
+ ts->rvec[i].rvec_free = &ts->rvec_free;
+ ts->rvec[i].idx = i;
+ }
+ _odp_sched_ts = ts;
+
+ return 0;
+}
+
+static void insert_schedq_in_list(sched_scalable_thread_state_t *ts,
+ sched_queue_t *schedq)
+{
+ /* Find slot for schedq */
+ for (uint32_t i = 0; i < ts->num_schedq; i++) {
+ /* Lower value is higher priority and closer to start of list */
+ if (schedq->prio <= ts->schedq_list[i]->prio) {
+ /* This is the slot! */
+ sched_queue_t *tmp;
+
+ tmp = ts->schedq_list[i];
+ ts->schedq_list[i] = schedq;
+ schedq = tmp;
+ /* Continue the insertion procedure with the
+ * new schedq.
+ */
+ }
+ }
+ if (ts->num_schedq == SCHEDQ_PER_THREAD)
+ _ODP_ABORT("Too many schedqs\n");
+ ts->schedq_list[ts->num_schedq++] = schedq;
+}
+
+static void remove_schedq_from_list(sched_scalable_thread_state_t *ts,
+ sched_queue_t *schedq)
+{
+ /* Find schedq */
+ for (uint32_t i = 0; i < ts->num_schedq; i++)
+ if (ts->schedq_list[i] == schedq) {
+ /* Move remaining schedqs */
+ for (uint32_t j = i + 1; j < ts->num_schedq; j++)
+ ts->schedq_list[j - 1] = ts->schedq_list[j];
+ ts->num_schedq--;
+ return;
+ }
+ _ODP_ABORT("Cannot find schedq\n");
+}
+
+/*******************************************************************************
+ * Scheduler queues
+ ******************************************************************************/
+#ifndef odp_container_of
+#define odp_container_of(pointer, type, member) \
+ ((type *)(void *)(((char *)pointer) - offsetof(type, member)))
+#endif
+
+static inline void schedq_init(sched_queue_t *schedq, uint32_t prio)
+{
+ llqueue_init(&schedq->llq);
+ schedq->prio = prio;
+}
+
+static inline sched_elem_t *schedq_peek(sched_queue_t *schedq)
+{
+ struct llnode *ptr;
+
+ ptr = llq_head(&schedq->llq);
+ return odp_container_of(ptr, sched_elem_t, node);
+}
+
+static inline odp_bool_t schedq_cond_pop(sched_queue_t *schedq,
+ sched_elem_t *elem)
+{
+ return llq_dequeue_cond(&schedq->llq, &elem->node);
+}
+
+static inline void schedq_push(sched_queue_t *schedq, sched_elem_t *elem)
+{
+ llq_enqueue(&schedq->llq, &elem->node);
+}
+
+static inline odp_bool_t schedq_cond_rotate(sched_queue_t *schedq,
+ sched_elem_t *elem)
+{
+ return llq_cond_rotate(&schedq->llq, &elem->node);
+}
+
+static inline bool schedq_elem_on_queue(sched_elem_t *elem)
+{
+ return llq_on_queue(&elem->node);
+}
+
+/*******************************************************************************
+ * Shared metadata btwn scheduler and queue
+ ******************************************************************************/
+
+void _odp_sched_update_enq(sched_elem_t *q, uint32_t actual)
+{
+ qschedstate_t oss, nss;
+ uint32_t ticket;
+
+ oss = q->qschst;
+ /* Update event counter, optionally taking a ticket. */
+ do {
+ ticket = TICKET_INVALID;
+ nss = oss;
+ nss.numevts += actual;
+ if (odp_unlikely(oss.numevts <= 0 && nss.numevts > 0))
+ /* E -> NE transition */
+ if (q->qschst_type != ODP_SCHED_SYNC_ATOMIC ||
+ oss.cur_ticket == oss.nxt_ticket)
+ /* Parallel or ordered queues: always take
+ * ticket.
+ * Atomic queue: only take ticket if one is
+ * immediately available.
+ * Otherwise ticket already taken => queue
+ * processed by some thread.
+ */
+ ticket = nss.nxt_ticket++;
+ /* Else queue already was non-empty. */
+ /* Attempt to update numevts counter and optionally take ticket. */
+ } while (!__atomic_compare_exchange(&q->qschst, &oss, &nss,
+ true, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
+
+ if (odp_unlikely(ticket != TICKET_INVALID)) {
+ /* Wait for our turn to update schedq. */
+ if (odp_unlikely(__atomic_load_n(&q->qschst.cur_ticket,
+ __ATOMIC_ACQUIRE) != ticket))
+ _odp_wait_until_eq_acq_u8(&q->qschst.cur_ticket, ticket);
+
+ /* Enqueue at end of scheduler queue */
+ /* We are here because of empty-to-non-empty transition
+ * This means queue must be pushed to schedq if possible
+ * but we can't do that if it already is on the schedq
+ */
+ if (odp_likely(!schedq_elem_on_queue(q) &&
+ q->pop_deficit == 0)) {
+ /* Queue not already on schedq and no pop deficit means
+ * we can push queue to schedq */
+ schedq_push(q->schedq, q);
+ } else {
+ /* Missed push => cancels one missed pop */
+ q->pop_deficit--;
+ }
+ atomic_store_release(&q->qschst.cur_ticket, ticket + 1,
+ /*readonly=*/false);
+ }
+ /* Else queue was not empty or atomic queue already busy. */
+}
+
+void _odp_sched_update_enq_sp(sched_elem_t *q, uint32_t actual)
+{
+ qschedstate_t oss, nss;
+ uint32_t ticket;
+
+ oss = q->qschst;
+ /* Update event counter, optionally taking a ticket. */
+ ticket = TICKET_INVALID;
+ nss = oss;
+ nss.numevts += actual;
+ if (odp_unlikely(oss.numevts <= 0 && nss.numevts > 0)) {
+ /* E -> NE transition */
+ if (q->qschst_type != ODP_SCHED_SYNC_ATOMIC ||
+ oss.cur_ticket == oss.nxt_ticket) {
+ /* Parallel or ordered queues: always take
+ * ticket.
+ * Atomic queue: only take ticket if one is
+ * immediately available. Otherwise ticket already
+ * taken => queue owned/processed by some thread
+ */
+ ticket = nss.nxt_ticket++;
+ }
+ }
+ /* Else queue already was non-empty. */
+ /* Attempt to update numevts counter and optionally take ticket. */
+ q->qschst = nss;
+
+ if (odp_unlikely(ticket != TICKET_INVALID)) {
+ /* Enqueue at end of scheduler queue */
+ /* We are here because of empty-to-non-empty transition
+ * This means queue must be pushed to schedq if possible
+ * but we can't do that if it already is on the schedq
+ */
+ if (odp_likely(!schedq_elem_on_queue(q) &&
+ q->pop_deficit == 0)) {
+ /* Queue not already on schedq and no pop deficit means
+ * we can push queue to schedq */
+ schedq_push(q->schedq, q);
+ } else {
+ /* Missed push => cancels one missed pop */
+ q->pop_deficit--;
+ }
+ q->qschst.cur_ticket = ticket + 1;
+ }
+ /* Else queue was not empty or atomic queue already busy. */
+}
+
+#ifndef CONFIG_QSCHST_LOCK
+/* The scheduler is the only entity that performs the dequeue from a queue. */
+static void
+sched_update_deq(sched_elem_t *q,
+ uint32_t actual,
+ bool atomic) __attribute__((always_inline));
+static inline void
+sched_update_deq(sched_elem_t *q,
+ uint32_t actual, bool atomic)
+{
+ qschedstate_t oss, nss;
+ uint32_t ticket;
+
+ if (atomic) {
+ bool pushed = false;
+
+ /* We own this atomic queue, only we can dequeue from it and
+ * thus decrease numevts. Other threads may enqueue and thus
+ * increase numevts.
+ * This means that numevts can't unexpectedly become 0 and
+ * invalidate a push operation already performed
+ */
+ oss = q->qschst;
+ do {
+ _ODP_ASSERT(oss.cur_ticket == _odp_sched_ts->ticket);
+ nss = oss;
+ nss.numevts -= actual;
+ if (nss.numevts > 0 && !pushed) {
+ schedq_push(q->schedq, q);
+ pushed = true;
+ }
+ /* Attempt to release ticket expecting our view of
+ * numevts to be correct
+ * Unfortunately nxt_ticket will also be included in
+ * the CAS operation
+ */
+ nss.cur_ticket = _odp_sched_ts->ticket + 1;
+ } while (odp_unlikely(!__atomic_compare_exchange(&q->qschst, &oss, &nss, true,
+ __ATOMIC_RELEASE,
+ __ATOMIC_RELAXED)));
+ return;
+ }
+
+ oss = q->qschst;
+ do {
+ ticket = TICKET_INVALID;
+ nss = oss;
+ nss.numevts -= actual;
+ nss.wrr_budget -= actual;
+ if ((oss.numevts > 0 && nss.numevts <= 0) ||
+ oss.wrr_budget <= actual) {
+ /* If we have emptied parallel/ordered queue or
+ * exhausted its WRR budget, we need a ticket
+ * for a later pop.
+ */
+ ticket = nss.nxt_ticket++;
+ /* Reset wrr_budget as we might also push the
+ * queue to the schedq.
+ */
+ nss.wrr_budget = CONFIG_WRR_WEIGHT;
+ }
+ /* Attempt to update numevts and optionally take ticket. */
+ } while (!__atomic_compare_exchange(&q->qschst, &oss, &nss,
+ true, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
+
+ if (odp_unlikely(ticket != TICKET_INVALID)) {
+ _ODP_ASSERT(q->qschst_type != ODP_SCHED_SYNC_ATOMIC);
+ /* Wait for our turn to update schedq. */
+ if (odp_unlikely(__atomic_load_n(&q->qschst.cur_ticket,
+ __ATOMIC_ACQUIRE) != ticket))
+ _odp_wait_until_eq_acq_u8(&q->qschst.cur_ticket, ticket);
+
+ /* We are here because of non-empty-to-empty transition or
+ * WRR budget exhausted
+ * This means the queue must be popped from the schedq, now or
+ * later
+ * If there was no NE->E transition but instead the WRR budget
+ * was exhausted, the queue needs to be moved (popped and
+ * pushed) to the tail of the schedq
+ */
+ if (oss.numevts > 0 && nss.numevts <= 0) {
+ /* NE->E transition, need to pop */
+ if (!schedq_elem_on_queue(q) ||
+ !schedq_cond_pop(q->schedq, q)) {
+ /* Queue not at head, failed to dequeue
+ * Missed a pop.
+ */
+ q->pop_deficit++;
+ }
+ } else {
+ /* WRR budget exhausted
+ * Need to move queue to tail of schedq if possible
+ */
+ if (odp_likely(schedq_elem_on_queue(q))) {
+ /* Queue is on schedq, try to move it to
+ * the tail
+ */
+ (void)schedq_cond_rotate(q->schedq, q);
+ }
+ /* Else queue not on schedq or not at head of schedq
+ * No pop => no push
+ */
+ }
+ atomic_store_release(&q->qschst.cur_ticket, ticket + 1,
+ /*readonly=*/false);
+ }
+}
+#endif
+
+#ifdef CONFIG_QSCHST_LOCK
+static void
+sched_update_deq_sc(sched_elem_t *q,
+ uint32_t actual,
+ bool atomic) __attribute__((always_inline));
+static inline void
+sched_update_deq_sc(sched_elem_t *q,
+ uint32_t actual, bool atomic)
+{
+ qschedstate_t oss, nss;
+ uint32_t ticket;
+
+ if (atomic) {
+ _ODP_ASSERT(q->qschst.cur_ticket == _odp_sched_ts->ticket);
+ _ODP_ASSERT(q->qschst.cur_ticket != q->qschst.nxt_ticket);
+ q->qschst.numevts -= actual;
+ q->qschst.cur_ticket = _odp_sched_ts->ticket + 1;
+ if (q->qschst.numevts > 0)
+ schedq_push(q->schedq, q);
+ return;
+ }
+
+ oss = q->qschst;
+ ticket = TICKET_INVALID;
+ nss = oss;
+ nss.numevts -= actual;
+ nss.wrr_budget -= actual;
+ if ((oss.numevts > 0 && nss.numevts <= 0) || oss.wrr_budget <= actual) {
+ /* If we emptied the queue or
+ * if we have served the maximum number of events
+ * then we need a ticket for a later pop.
+ */
+ ticket = nss.nxt_ticket++;
+ /* Also reset wrr_budget as we might also push the
+ * queue to the schedq.
+ */
+ nss.wrr_budget = CONFIG_WRR_WEIGHT;
+ }
+ q->qschst = nss;
+
+ if (ticket != TICKET_INVALID) {
+ if (oss.numevts > 0 && nss.numevts <= 0) {
+ /* NE->E transition, need to pop */
+ if (!schedq_elem_on_queue(q) ||
+ !schedq_cond_pop(q->schedq, q)) {
+ /* Queue not at head, failed to dequeue.
+ * Missed a pop.
+ */
+ q->pop_deficit++;
+ }
+ } else {
+ /* WRR budget exhausted
+ * Need to move queue to tail of schedq if possible
+ */
+ if (odp_likely(schedq_elem_on_queue(q))) {
+ /* Queue is on schedq, try to move it to
+ * the tail
+ */
+ (void)schedq_cond_rotate(q->schedq, q);
+ }
+ /* Else queue not on schedq or not at head of schedq
+ * No pop => no push
+ */
+ }
+ q->qschst.cur_ticket = ticket + 1;
+ }
+}
+#endif
+
+static inline void sched_update_popd_sc(sched_elem_t *elem)
+{
+ if (elem->pop_deficit != 0 &&
+ schedq_elem_on_queue(elem) &&
+ schedq_cond_pop(elem->schedq, elem))
+ elem->pop_deficit--;
+}
+
+#ifndef CONFIG_QSCHST_LOCK
+static inline void sched_update_popd(sched_elem_t *elem)
+{
+ uint32_t ticket = __atomic_fetch_add(&elem->qschst.nxt_ticket,
+ 1,
+ __ATOMIC_RELAXED);
+ if (odp_unlikely(__atomic_load_n(&elem->qschst.cur_ticket,
+ __ATOMIC_ACQUIRE) != ticket))
+ _odp_wait_until_eq_acq_u8(&elem->qschst.cur_ticket, ticket);
+
+ sched_update_popd_sc(elem);
+ atomic_store_release(&elem->qschst.cur_ticket, ticket + 1,
+ /*readonly=*/false);
+}
+#endif
+
+static void signal_threads_add(sched_group_t *sg, uint32_t sgi, uint32_t prio)
+{
+ sched_group_mask_t thrds = sg->thr_wanted;
+ uint32_t thr;
+
+ while (!bitset_is_null(thrds)) {
+ thr = bitset_ffs(thrds) - 1;
+ thrds = bitset_clr(thrds, thr);
+ /* Notify the thread about membership in this
+ * group/priority.
+ */
+ atom_bitset_set(&global->thread_state[thr].sg_wanted[prio],
+ sgi, __ATOMIC_RELEASE);
+ __atomic_store_n(&global->thread_state[thr].sg_sem, 1,
+ __ATOMIC_RELEASE);
+ }
+}
+
+sched_queue_t *_odp_sched_queue_add(odp_schedule_group_t grp, uint32_t prio)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ uint32_t x;
+
+ _ODP_ASSERT(grp >= 0 && grp < (odp_schedule_group_t)MAX_SCHED_GROUP);
+ _ODP_ASSERT((global->sg_free & (1ULL << grp)) == 0);
+ _ODP_ASSERT(prio < ODP_SCHED_PRIO_NUM);
+
+ sgi = grp;
+ sg = global->sg_vec[sgi];
+
+ /* Use xcount to spread queues over the xfactor schedq's
+ * per priority.
+ */
+ x = __atomic_fetch_add(&sg->xcount[prio], 1, __ATOMIC_RELAXED);
+ if (x == 0) {
+ /* First ODP queue for this priority
+ * Notify all threads in sg->thr_wanted that they
+ * should join.
+ */
+ signal_threads_add(sg, sgi, prio);
+ }
+ return &sg->schedq[prio * sg->xfactor + x % sg->xfactor];
+}
+
+static uint32_t sched_pktin_add(odp_schedule_group_t grp, uint32_t prio)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+
+ _ODP_ASSERT(grp >= 0 && grp < (odp_schedule_group_t)MAX_SCHED_GROUP);
+ _ODP_ASSERT((global->sg_free & (1ULL << grp)) == 0);
+ _ODP_ASSERT(prio < ODP_SCHED_PRIO_NUM);
+
+ sgi = grp;
+ sg = global->sg_vec[sgi];
+
+ (void)_odp_sched_queue_add(grp, ODP_SCHED_PRIO_PKTIN);
+ return (ODP_SCHED_PRIO_PKTIN - prio) * sg->xfactor;
+}
+
+static void signal_threads_rem(sched_group_t *sg, uint32_t sgi, uint32_t prio)
+{
+ sched_group_mask_t thrds = sg->thr_wanted;
+ uint32_t thr;
+
+ while (!bitset_is_null(thrds)) {
+ thr = bitset_ffs(thrds) - 1;
+ thrds = bitset_clr(thrds, thr);
+ /* Notify the thread about membership in this
+ * group/priority.
+ */
+ atom_bitset_clr(&global->thread_state[thr].sg_wanted[prio],
+ sgi, __ATOMIC_RELEASE);
+ __atomic_store_n(&global->thread_state[thr].sg_sem, 1,
+ __ATOMIC_RELEASE);
+ }
+}
+
+void _odp_sched_queue_rem(odp_schedule_group_t grp, uint32_t prio)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ uint32_t x;
+
+ _ODP_ASSERT(grp >= 0 && grp < (odp_schedule_group_t)MAX_SCHED_GROUP);
+ _ODP_ASSERT((global->sg_free & (1ULL << grp)) == 0);
+ _ODP_ASSERT(prio < ODP_SCHED_PRIO_NUM);
+
+ sgi = grp;
+ sg = global->sg_vec[sgi];
+
+ x = __atomic_sub_fetch(&sg->xcount[prio], 1, __ATOMIC_RELAXED);
+ if (x == 0) {
+ /* Last ODP queue for this priority
+ * Notify all threads in sg->thr_wanted that they
+ * should leave.
+ */
+ signal_threads_rem(sg, sgi, prio);
+ }
+}
+
+static void sched_pktin_rem(odp_schedule_group_t grp)
+{
+ _odp_sched_queue_rem(grp, ODP_SCHED_PRIO_PKTIN);
+}
+
+static void update_sg_add(sched_scalable_thread_state_t *ts,
+ uint32_t p,
+ sched_group_mask_t sg_wanted)
+{
+ sched_group_mask_t added;
+ uint32_t sgi;
+ sched_group_t *sg;
+ uint32_t x;
+
+ added = bitset_andn(sg_wanted, ts->sg_actual[p]);
+ while (!bitset_is_null(added)) {
+ sgi = bitset_ffs(added) - 1;
+ sg = global->sg_vec[sgi];
+ for (x = 0; x < sg->xfactor; x++) {
+ /* Include our thread index to shift
+ * (rotate) the order of schedq's
+ */
+ insert_schedq_in_list(ts,
+ &sg->schedq[p * sg->xfactor +
+ (x + ts->tidx) % sg->xfactor]);
+ }
+ atom_bitset_set(&sg->thr_actual[p], ts->tidx, __ATOMIC_RELAXED);
+ added = bitset_clr(added, sgi);
+ }
+}
+
+static void update_sg_rem(sched_scalable_thread_state_t *ts,
+ uint32_t p,
+ sched_group_mask_t sg_wanted)
+{
+ sched_group_mask_t removed;
+ uint32_t sgi;
+ sched_group_t *sg;
+ uint32_t x;
+
+ removed = bitset_andn(ts->sg_actual[p], sg_wanted);
+ while (!bitset_is_null(removed)) {
+ sgi = bitset_ffs(removed) - 1;
+ sg = global->sg_vec[sgi];
+ for (x = 0; x < sg->xfactor; x++) {
+ remove_schedq_from_list(ts,
+ &sg->schedq[p *
+ sg->xfactor + x]);
+ }
+ atom_bitset_clr(&sg->thr_actual[p], ts->tidx, __ATOMIC_RELAXED);
+ removed = bitset_clr(removed, sgi);
+ }
+}
+
+static void update_sg_membership(sched_scalable_thread_state_t *ts)
+{
+ uint32_t p;
+ sched_group_mask_t sg_wanted;
+
+ for (p = 0; p < ODP_SCHED_PRIO_NUM; p++) {
+ sg_wanted = atom_bitset_load(&ts->sg_wanted[p],
+ __ATOMIC_ACQUIRE);
+ if (!bitset_is_eql(ts->sg_actual[p], sg_wanted)) {
+ /* Our sched_group membership has changed */
+ update_sg_add(ts, p, sg_wanted);
+ update_sg_rem(ts, p, sg_wanted);
+ ts->sg_actual[p] = sg_wanted;
+ }
+ }
+}
+
+/*******************************************************************************
+ * Scheduler
+ ******************************************************************************/
+
+static inline void _schedule_release_atomic(sched_scalable_thread_state_t *ts)
+{
+#ifdef CONFIG_QSCHST_LOCK
+ sched_update_deq_sc(ts->atomq, ts->dequeued, true);
+ _ODP_ASSERT(ts->atomq->qschst.cur_ticket != ts->ticket);
+ _ODP_ASSERT(ts->atomq->qschst.cur_ticket ==
+ ts->atomq->qschst.nxt_ticket);
+#else
+ sched_update_deq(ts->atomq, ts->dequeued, true);
+#endif
+ ts->atomq = NULL;
+ ts->ticket = TICKET_INVALID;
+}
+
+static inline void _schedule_release_ordered(sched_scalable_thread_state_t *ts)
+{
+ ts->out_of_order = false;
+ _odp_rctx_release(ts->rctx);
+ ts->rctx = NULL;
+}
+
+static void pktio_start(int pktio_idx,
+ int num_in_queue,
+ int in_queue_idx[],
+ odp_queue_t odpq[])
+{
+ int i, rxq;
+ queue_entry_t *qentry;
+ sched_elem_t *elem;
+
+ _ODP_ASSERT(pktio_idx < CONFIG_PKTIO_ENTRIES);
+ for (i = 0; i < num_in_queue; i++) {
+ rxq = in_queue_idx[i];
+ _ODP_ASSERT(rxq < ODP_PKTIN_MAX_QUEUES);
+ __atomic_fetch_add(&global->poll_count[pktio_idx], 1,
+ __ATOMIC_RELAXED);
+ qentry = _odp_qentry_from_ext(odpq[i]);
+ elem = &qentry->sched_elem;
+ elem->cons_type |= FLAG_PKTIN; /* Set pktin queue flag */
+ elem->pktio_idx = pktio_idx;
+ elem->rx_queue = rxq;
+ elem->xoffset = sched_pktin_add(elem->sched_grp, elem->sched_prio);
+ _ODP_ASSERT(elem->schedq != NULL);
+ schedq_push(elem->schedq, elem);
+ }
+}
+
+static void pktio_stop(sched_elem_t *elem)
+{
+ sched_pktin_rem(elem->sched_grp);
+ if (__atomic_sub_fetch(&global->poll_count[elem->pktio_idx],
+ 1, __ATOMIC_RELAXED) == 0) {
+ /* Call stop_finalize when all queues
+ * of the pktio have been removed */
+ _odp_sched_cb_pktio_stop_finalize(elem->pktio_idx);
+ }
+}
+
+static bool have_reorder_ctx(sched_scalable_thread_state_t *ts)
+{
+ if (odp_unlikely(bitset_is_null(ts->priv_rvec_free))) {
+ ts->priv_rvec_free = atom_bitset_xchg(&ts->rvec_free, 0,
+ __ATOMIC_RELAXED);
+ if (odp_unlikely(bitset_is_null(ts->priv_rvec_free))) {
+ /* No free reorder contexts for this thread */
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool is_pktin(sched_elem_t *elem)
+{
+ return (elem->cons_type & FLAG_PKTIN) != 0;
+}
+
+static inline bool is_atomic(sched_elem_t *elem)
+{
+ return elem->cons_type == (ODP_SCHED_SYNC_ATOMIC | FLAG_PKTIN);
+}
+
+static inline bool is_ordered(sched_elem_t *elem)
+{
+ return elem->cons_type == (ODP_SCHED_SYNC_ORDERED | FLAG_PKTIN);
+}
+
+static int poll_pktin(sched_elem_t *elem, odp_event_t ev[], int num_evts)
+{
+ sched_scalable_thread_state_t *ts = _odp_sched_ts;
+ int num, i;
+ /* For ordered queues only */
+ reorder_context_t *rctx;
+ reorder_window_t *rwin = NULL;
+ uint32_t sn = 0;
+ uint32_t idx;
+
+ if (is_ordered(elem)) {
+ /* Need reorder context and slot in reorder window */
+ rwin = queue_get_rwin((queue_entry_t *)elem);
+ _ODP_ASSERT(rwin != NULL);
+ if (odp_unlikely(!have_reorder_ctx(ts) ||
+ !_odp_rwin_reserve_sc(rwin, &sn))) {
+ /* Put back queue on source schedq */
+ schedq_push(ts->src_schedq, elem);
+ return 0;
+ }
+ /* Slot in reorder window reserved! */
+ }
+
+ /* Try to dequeue events from the ingress queue itself */
+ num = _odp_queue_deq_sc(elem, ev, num_evts);
+ if (odp_likely(num > 0)) {
+events_dequeued:
+ if (is_atomic(elem)) {
+ ts->atomq = elem; /* Remember */
+ ts->dequeued += num;
+ /* Don't push atomic queue on schedq */
+ } else /* Parallel or ordered */ {
+ if (is_ordered(elem)) {
+ /* Find and initialise an unused reorder
+ * context. */
+ idx = bitset_ffs(ts->priv_rvec_free) - 1;
+ ts->priv_rvec_free =
+ bitset_clr(ts->priv_rvec_free, idx);
+ rctx = &ts->rvec[idx];
+ _odp_rctx_init(rctx, idx, rwin, sn);
+ /* Are we in-order or out-of-order? */
+ ts->out_of_order = sn != rwin->hc.head;
+ ts->rctx = rctx;
+ }
+ schedq_push(elem->schedq, elem);
+ }
+ return num;
+ }
+
+ /* Ingress queue empty => poll pktio RX queue */
+ _odp_event_hdr_t *rx_evts[QUEUE_MULTI_MAX];
+ int num_rx = _odp_sched_cb_pktin_poll(elem->pktio_idx, elem->rx_queue,
+ rx_evts, QUEUE_MULTI_MAX);
+
+ if (odp_likely(num_rx > 0)) {
+ num = num_rx < num_evts ? num_rx : num_evts;
+ for (i = 0; i < num; i++) {
+ /* Return events directly to caller */
+ ev[i] = _odp_event_from_hdr(rx_evts[i]);
+ }
+ if (num_rx > num) {
+ /* Events remain, enqueue them */
+ i = _odp_queue_enq_sp(elem, &rx_evts[num], num_rx - num);
+ /* Enqueue must succeed as the queue was empty */
+ _ODP_ASSERT(i == num_rx - num);
+ }
+ goto events_dequeued;
+ }
+ /* No packets received, reset state and undo side effects */
+ if (is_atomic(elem))
+ ts->atomq = NULL;
+ else if (is_ordered(elem))
+ _odp_rwin_unreserve_sc(rwin, sn);
+
+ if (odp_likely(num_rx == 0)) {
+ /* RX queue empty, push it to pktin priority schedq */
+ sched_queue_t *schedq = ts->src_schedq;
+ /* Check if queue came from the designated schedq */
+ if (schedq == elem->schedq) {
+ /* Yes, add offset to the pktin priority level
+ * in order to get alternate schedq */
+ schedq += elem->xoffset;
+ }
+ /* Else no, queue must have come from alternate schedq */
+ schedq_push(schedq, elem);
+ } else /* num_rx < 0 => pktio stopped or closed */ {
+ /* Remove queue */
+ pktio_stop(elem);
+ /* Don't push queue to schedq */
+ }
+
+ _ODP_ASSERT(ts->atomq == NULL);
+ _ODP_ASSERT(!ts->out_of_order);
+ _ODP_ASSERT(ts->rctx == NULL);
+ return 0;
+}
+
+static int _schedule(odp_queue_t *from, odp_event_t ev[], int num_evts)
+{
+ sched_scalable_thread_state_t *ts;
+ sched_elem_t *atomq;
+ int num;
+ int cpu_id;
+ uint32_t i;
+
+ ts = _odp_sched_ts;
+ atomq = ts->atomq;
+
+ timer_run(1);
+
+ /* Once an atomic queue has been scheduled to a thread, it will stay
+ * on that thread until empty or 'rotated' by WRR
+ */
+ if (atomq != NULL && is_pktin(atomq)) {
+ /* Atomic pktin queue */
+ if (ts->dequeued < atomq->qschst.wrr_budget) {
+ _ODP_ASSERT(ts->src_schedq != NULL);
+ num = poll_pktin(atomq, ev, num_evts);
+ if (odp_likely(num != 0)) {
+ if (from)
+ *from = queue_get_handle((queue_entry_t *)atomq);
+ return num;
+ }
+ } else {
+ /* WRR budget exhausted, move queue to end of schedq */
+ schedq_push(atomq->schedq, atomq);
+ }
+ ts->atomq = NULL;
+ } else if (atomq != NULL) {
+ _ODP_ASSERT(ts->ticket != TICKET_INVALID);
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&atomq->qschlock);
+#endif
+dequeue_atomic:
+ _ODP_ASSERT(ts->ticket == atomq->qschst.cur_ticket);
+ _ODP_ASSERT(ts->ticket != atomq->qschst.nxt_ticket);
+ /* Atomic queues can be dequeued without lock since this thread
+ * has the only reference to the atomic queue being processed.
+ */
+ if (ts->dequeued < atomq->qschst.wrr_budget) {
+ num = _odp_queue_deq_sc(atomq, ev, num_evts);
+ if (odp_likely(num != 0)) {
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&atomq->qschlock);
+#endif
+ ts->dequeued += num;
+ /* Allow this thread to continue to 'own' this
+ * atomic queue until all events have been
+ * processed and the thread re-invokes the
+ * scheduler.
+ */
+ if (from)
+ *from = queue_get_handle((queue_entry_t *)atomq);
+ return num;
+ }
+ }
+ /* Atomic queue was empty or interrupted by WRR, release it. */
+ _schedule_release_atomic(ts);
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&atomq->qschlock);
+#endif
+ }
+
+ /* Check for and perform any scheduler group updates. */
+ if (odp_unlikely(__atomic_load_n(&ts->sg_sem, __ATOMIC_RELAXED) != 0)) {
+ (void)__atomic_load_n(&ts->sg_sem, __ATOMIC_ACQUIRE);
+ ts->sg_sem = 0;
+ update_sg_membership(ts);
+ }
+
+ cpu_id = odp_cpu_id();
+ /* Scan our schedq list from beginning to end */
+ for (i = 0; i < ts->num_schedq; i++) {
+ sched_queue_t *schedq = ts->schedq_list[i];
+ sched_elem_t *elem;
+
+ ts->loop_cnt++;
+restart_same:
+ elem = schedq_peek(schedq);
+ if (odp_unlikely(elem == NULL)) {
+ /* Schedq empty, look at next one. */
+ continue;
+ }
+ if (is_pktin(elem)) {
+ /* Pktio ingress queue */
+ if (elem->schedq != schedq) { /* Low priority schedq*/
+ if (elem->loop_check[cpu_id] != ts->loop_cnt)
+ elem->loop_check[cpu_id] = ts->loop_cnt;
+ else /* Wrapped around */
+ continue; /* Go to next schedq */
+ }
+
+ if (odp_unlikely(!schedq_cond_pop(schedq, elem)))
+ goto restart_same;
+
+ ts->src_schedq = schedq; /* Remember source schedq */
+ num = poll_pktin(elem, ev, num_evts);
+ if (odp_unlikely(num <= 0))
+ goto restart_same;
+ if (from)
+ *from = queue_get_handle((queue_entry_t *)elem);
+ return num;
+ } else if (elem->cons_type == ODP_SCHED_SYNC_ATOMIC) {
+ /* Dequeue element only if it is still at head
+ * of schedq.
+ */
+ if (odp_unlikely(!schedq_cond_pop(schedq, elem))) {
+ /* Queue not at head of schedq anymore, some
+ * other thread popped it.
+ */
+ goto restart_same;
+ }
+ ts->atomq = elem;
+ atomq = elem;
+ ts->dequeued = 0;
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&atomq->qschlock);
+ ts->ticket = atomq->qschst.nxt_ticket++;
+ _ODP_ASSERT(atomq->qschst.cur_ticket == ts->ticket);
+#else
+ /* Dequeued atomic queue from the schedq, only we
+ * can process it and any qschst updates are our
+ * responsibility.
+ */
+ /* The ticket taken below will signal producers */
+ ts->ticket = __atomic_fetch_add(&atomq->qschst.nxt_ticket, 1,
+ __ATOMIC_RELAXED);
+ while (__atomic_load_n(&atomq->qschst.cur_ticket,
+ __ATOMIC_ACQUIRE) != ts->ticket) {
+ /* No need to use WFE, spinning here seems
+ * very infrequent.
+ */
+ odp_cpu_pause();
+ }
+#endif
+ goto dequeue_atomic;
+ } else if (elem->cons_type == ODP_SCHED_SYNC_PARALLEL) {
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&elem->qschlock);
+ num = _odp_queue_deq_sc(elem, ev, num_evts);
+ if (odp_likely(num != 0)) {
+ sched_update_deq_sc(elem, num, false);
+ UNLOCK(&elem->qschlock);
+ if (from)
+ *from =
+ queue_get_handle((queue_entry_t *)elem);
+ return num;
+ }
+ UNLOCK(&elem->qschlock);
+#else
+ num = _odp_queue_deq_mc(elem, ev, num_evts);
+ if (odp_likely(num != 0)) {
+ sched_update_deq(elem, num, false);
+ if (from)
+ *from =
+ queue_get_handle((queue_entry_t *)elem);
+ return num;
+ }
+#endif
+ } else if (elem->cons_type == ODP_SCHED_SYNC_ORDERED) {
+ reorder_window_t *rwin;
+ reorder_context_t *rctx;
+ uint32_t sn;
+ uint32_t idx;
+
+ /* The ordered queue has a reorder window so requires
+ * order restoration. We must use a reorder context to
+ * collect all outgoing events. Ensure there is at least
+ * one available reorder context.
+ */
+ if (odp_unlikely(!have_reorder_ctx(ts)))
+ continue;
+
+ /* _odp_rwin_reserve and odp_queue_deq must be atomic or
+ * there will be a potential race condition.
+ * Allocate a slot in the reorder window.
+ */
+ rwin = queue_get_rwin((queue_entry_t *)elem);
+ _ODP_ASSERT(rwin != NULL);
+ if (odp_unlikely(!_odp_rwin_reserve(rwin, &sn))) {
+ /* Reorder window full */
+ /* Look at next schedq, find other queue */
+ continue;
+ }
+ /* Wait for our turn to dequeue */
+ if (odp_unlikely(__atomic_load_n(&rwin->turn, __ATOMIC_ACQUIRE) != sn))
+ _odp_wait_until_eq_acq_u32(&rwin->turn, sn);
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&elem->qschlock);
+#endif
+ num = _odp_queue_deq_sc(elem, ev, num_evts);
+ /* Wait for prod_read write in _odp_queue_dequeue_sc()
+ * to complete before we signal the next consumer
+ */
+ atomic_store_release(&rwin->turn, sn + 1,
+ /*readonly=*/false);
+ /* Find and initialise an unused reorder context. */
+ idx = bitset_ffs(ts->priv_rvec_free) - 1;
+ ts->priv_rvec_free =
+ bitset_clr(ts->priv_rvec_free, idx);
+ rctx = &ts->rvec[idx];
+ /* Need to initialise reorder context or we can't
+ * release it later.
+ */
+ _odp_rctx_init(rctx, idx, rwin, sn);
+
+ /* Was dequeue successful? */
+ if (odp_likely(num != 0)) {
+ /* Perform scheduler related updates */
+#ifdef CONFIG_QSCHST_LOCK
+ sched_update_deq_sc(elem, num,
+ /*atomic=*/false);
+ UNLOCK(&elem->qschlock);
+#else
+ sched_update_deq(elem, num, /*atomic=*/false);
+#endif
+
+ /* Are we in-order or out-of-order? */
+ ts->out_of_order = sn != rwin->hc.head;
+
+ ts->rctx = rctx;
+ if (from)
+ *from = queue_get_handle((queue_entry_t *)elem);
+ return num;
+ }
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&elem->qschlock);
+#endif
+ /* Since a slot was reserved in the reorder window,
+ * the reorder context needs to be released and
+ * inserted into the reorder window.
+ */
+ _odp_rctx_release(rctx);
+ _ODP_ASSERT(ts->rctx == NULL);
+ }
+ /* Dequeue from parallel/ordered queue failed
+ * Check if we have a queue at the head of the schedq that needs
+ * to be popped
+ */
+ if (odp_unlikely(__atomic_load_n(&elem->pop_deficit,
+ __ATOMIC_RELAXED) != 0)) {
+#ifdef CONFIG_QSCHST_LOCK
+ LOCK(&elem->qschlock);
+ sched_update_popd_sc(elem);
+ UNLOCK(&elem->qschlock);
+#else
+ sched_update_popd(elem);
+#endif
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+
+static void schedule_order_lock(uint32_t lock_index)
+{
+ struct reorder_context *rctx = _odp_sched_ts->rctx;
+
+ if (odp_unlikely(rctx == NULL ||
+ rctx->rwin == NULL ||
+ lock_index >= rctx->rwin->lock_count)) {
+ _ODP_ERR("Invalid call to odp_schedule_order_lock\n");
+ return;
+ }
+ if (odp_unlikely(__atomic_load_n(&rctx->rwin->olock[lock_index],
+ __ATOMIC_ACQUIRE) != rctx->sn))
+ _odp_wait_until_eq_acq_u32(&rctx->rwin->olock[lock_index], rctx->sn);
+}
+
+static void schedule_order_unlock(uint32_t lock_index)
+{
+ struct reorder_context *rctx;
+
+ rctx = _odp_sched_ts->rctx;
+ if (odp_unlikely(rctx == NULL ||
+ rctx->rwin == NULL ||
+ lock_index >= rctx->rwin->lock_count ||
+ rctx->rwin->olock[lock_index] != rctx->sn)) {
+ _ODP_ERR("Invalid call to odp_schedule_order_unlock\n");
+ return;
+ }
+ atomic_store_release(&rctx->rwin->olock[lock_index],
+ rctx->sn + 1,
+ /*readonly=*/false);
+ rctx->olock_flags |= 1U << lock_index;
+}
+
+static void schedule_order_unlock_lock(uint32_t unlock_index,
+ uint32_t lock_index)
+{
+ schedule_order_unlock(unlock_index);
+ schedule_order_lock(lock_index);
+}
+
+static void schedule_order_lock_start(uint32_t lock_index)
+{
+ (void)lock_index;
+}
+
+static void schedule_order_lock_wait(uint32_t lock_index)
+{
+ schedule_order_lock(lock_index);
+}
+
+static void schedule_release_atomic(void)
+{
+ sched_scalable_thread_state_t *ts;
+
+ ts = _odp_sched_ts;
+ if (odp_likely(ts->atomq != NULL)) {
+#ifdef CONFIG_QSCHST_LOCK
+ sched_elem_t *atomq;
+
+ atomq = ts->atomq;
+ LOCK(&atomq->qschlock);
+#endif
+ _schedule_release_atomic(ts);
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&atomq->qschlock);
+#endif
+ }
+}
+
+static void schedule_release_ordered(void)
+{
+ sched_scalable_thread_state_t *ts;
+
+ ts = _odp_sched_ts;
+ if (ts->rctx != NULL)
+ _schedule_release_ordered(ts);
+}
+
+static int schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t ev[],
+ int num)
+{
+ sched_scalable_thread_state_t *ts;
+ int n;
+ odp_time_t start;
+ odp_time_t deadline;
+
+ ts = _odp_sched_ts;
+ /* Release any previous reorder context. */
+ if (ts->rctx != NULL)
+ _schedule_release_ordered(ts);
+
+ if (odp_unlikely(ts->pause)) {
+ if (ts->atomq != NULL) {
+#ifdef CONFIG_QSCHST_LOCK
+ sched_elem_t *atomq;
+
+ atomq = ts->atomq;
+ LOCK(&atomq->qschlock);
+#endif
+ _schedule_release_atomic(ts);
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&atomq->qschlock);
+#endif
+ }
+ return 0;
+ }
+
+ if (wait == ODP_SCHED_NO_WAIT)
+ return _schedule(from, ev, num);
+
+ if (wait == ODP_SCHED_WAIT) {
+ for (;;) {
+ n = _schedule(from, ev, num);
+ if (odp_likely(n > 0))
+ return n;
+ }
+ }
+
+ start = odp_time_local();
+
+ n = _schedule(from, ev, num);
+ if (odp_likely(n > 0))
+ return n;
+
+ deadline = odp_time_add_ns(start, wait);
+
+ while (odp_time_cmp(deadline, odp_time_local()) > 0) {
+ n = _schedule(from, ev, num);
+ if (odp_likely(n > 0))
+ return n;
+ }
+
+ return 0;
+}
+
+static odp_event_t schedule(odp_queue_t *from, uint64_t wait)
+{
+ odp_event_t ev = ODP_EVENT_INVALID;
+ const int num = 1;
+ sched_scalable_thread_state_t *ts;
+ int n;
+ odp_time_t start;
+ odp_time_t deadline;
+
+ ts = _odp_sched_ts;
+ /* Release any previous reorder context. */
+ if (ts->rctx != NULL)
+ _schedule_release_ordered(ts);
+
+ if (odp_unlikely(ts->pause)) {
+ if (ts->atomq != NULL) {
+#ifdef CONFIG_QSCHST_LOCK
+ sched_elem_t *atomq;
+
+ atomq = ts->atomq;
+ LOCK(&atomq->qschlock);
+#endif
+ _schedule_release_atomic(ts);
+#ifdef CONFIG_QSCHST_LOCK
+ UNLOCK(&atomq->qschlock);
+#endif
+ }
+ return ev;
+ }
+
+ if (wait == ODP_SCHED_NO_WAIT) {
+ (void)_schedule(from, &ev, num);
+ return ev;
+ }
+
+ if (wait == ODP_SCHED_WAIT) {
+ for (;;) {
+ n = _schedule(from, &ev, num);
+ if (odp_likely(n > 0))
+ return ev;
+ }
+ }
+
+ start = odp_time_local();
+
+ n = _schedule(from, &ev, num);
+ if (odp_likely(n > 0))
+ return ev;
+
+ deadline = odp_time_add_ns(start, wait);
+
+ while (odp_time_cmp(deadline, odp_time_local()) > 0) {
+ n = _schedule(from, &ev, num);
+ if (odp_likely(n > 0))
+ return ev;
+ }
+
+ return ev;
+}
+
+static int schedule_multi_wait(odp_queue_t *from, odp_event_t events[],
+ int max_num)
+{
+ return schedule_multi(from, ODP_SCHED_WAIT, events, max_num);
+}
+
+static int schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[],
+ int max_num)
+{
+ return schedule_multi(from, ODP_SCHED_NO_WAIT, events, max_num);
+}
+
+static void schedule_pause(void)
+{
+ _odp_sched_ts->pause = true;
+}
+
+static void schedule_resume(void)
+{
+ _odp_sched_ts->pause = false;
+}
+
+static uint64_t schedule_wait_time(uint64_t ns)
+{
+ return ns;
+}
+
+static int schedule_num_prio(void)
+{
+ return ODP_SCHED_PRIO_NUM - 1; /* Discount the pktin priority level */
+}
+
+static int schedule_min_prio(void)
+{
+ return 0;
+}
+
+static int schedule_max_prio(void)
+{
+ return schedule_num_prio() - 1;
+}
+
+static int schedule_default_prio(void)
+{
+ return schedule_max_prio() / 2;
+}
+
+static int schedule_group_update(sched_group_t *sg,
+ uint32_t sgi,
+ const odp_thrmask_t *mask,
+ int join_leave)
+{
+ int thr;
+ uint32_t p;
+
+ /* Internal function, do not validate inputs */
+
+ /* Notify relevant threads about the change */
+ thr = odp_thrmask_first(mask);
+ while (0 <= thr) {
+ /* Add thread to scheduler group's wanted thread mask */
+ if (join_leave == SCHED_GROUP_JOIN)
+ atom_bitset_set(&sg->thr_wanted, thr, __ATOMIC_RELAXED);
+ else
+ atom_bitset_clr(&sg->thr_wanted, thr, __ATOMIC_RELAXED);
+ for (p = 0; p < ODP_SCHED_PRIO_NUM; p++) {
+ if (sg->xcount[p] != 0) {
+ sched_scalable_thread_state_t *state;
+
+ state = &global->thread_state[thr];
+
+ /* This priority level has ODP queues
+ * Notify the thread about membership in
+ * this group/priority
+ */
+ if (join_leave == SCHED_GROUP_JOIN)
+ atom_bitset_set(&state->sg_wanted[p],
+ sgi, __ATOMIC_RELEASE);
+ else
+ atom_bitset_clr(&state->sg_wanted[p],
+ sgi, __ATOMIC_RELEASE);
+ __atomic_store_n(&state->sg_sem, 1,
+ __ATOMIC_RELEASE);
+ }
+ }
+ thr = odp_thrmask_next(mask, thr);
+ }
+
+ return 0;
+}
+
+static int _schedule_group_thrmask(sched_group_t *sg, odp_thrmask_t *mask)
+{
+ bitset_t bs;
+ uint32_t bit;
+
+ /* Internal function, do not validate inputs */
+
+ odp_thrmask_zero(mask);
+ bs = sg->thr_wanted;
+ while (!bitset_is_null(bs)) {
+ bit = bitset_ffs(bs) - 1;
+ bs = bitset_clr(bs, bit);
+ odp_thrmask_set(mask, bit);
+ }
+
+ return 0;
+}
+
+static odp_schedule_group_t schedule_group_create(const char *name,
+ const odp_thrmask_t *mask)
+{
+ uint32_t sgi;
+ sched_group_mask_t free;
+ uint32_t xfactor;
+ sched_group_t *sg;
+ uint32_t p;
+ uint32_t x;
+ uint32_t size;
+
+ /* Validate inputs */
+ if (mask == NULL)
+ _ODP_ABORT("mask is NULL\n");
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ /* Allocate a scheduler group */
+ free = atom_bitset_load(&global->sg_free, __ATOMIC_RELAXED);
+ do {
+ /* All sched_groups in use */
+ if (bitset_is_null(free))
+ goto no_free_sched_group;
+
+ sgi = bitset_ffs(free) - 1;
+ /* All sched_groups in use */
+ if (sgi >= MAX_SCHED_GROUP)
+ goto no_free_sched_group;
+ } while (!atom_bitset_cmpxchg(&global->sg_free,
+ &free,
+ bitset_clr(free, sgi),
+ true,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_ACQUIRE));
+
+ /* Compute xfactor (spread factor) from the number of threads
+ * present in the thread mask. Preferable this would be an
+ * explicit parameter.
+ */
+ xfactor = odp_thrmask_count(mask);
+ if (xfactor < 1)
+ xfactor = CONFIG_DEFAULT_XFACTOR;
+
+ size = sizeof(sched_group_t) +
+ (ODP_SCHED_PRIO_NUM * xfactor - 1) * sizeof(sched_queue_t);
+ sg = (sched_group_t *)shm_pool_alloc_align(global->sched_shm_pool,
+ size);
+ if (sg == NULL)
+ goto shm_pool_alloc_failed;
+
+ _odp_strcpy(sg->name, name ? name : "", ODP_SCHED_GROUP_NAME_LEN);
+ global->sg_vec[sgi] = sg;
+ memset(sg->thr_actual, 0, sizeof(sg->thr_actual));
+ sg->thr_wanted = bitset_null();
+ sg->xfactor = xfactor;
+ for (p = 0; p < ODP_SCHED_PRIO_NUM; p++) {
+ sg->xcount[p] = 0;
+ for (x = 0; x < xfactor; x++)
+ schedq_init(&sg->schedq[p * xfactor + x], p);
+ }
+ if (odp_thrmask_count(mask) != 0)
+ schedule_group_update(sg, sgi, mask, SCHED_GROUP_JOIN);
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return (odp_schedule_group_t)(sgi);
+
+shm_pool_alloc_failed:
+ /* Free the allocated group index */
+ atom_bitset_set(&global->sg_free, sgi, __ATOMIC_RELAXED);
+
+no_free_sched_group:
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ODP_SCHED_GROUP_INVALID;
+}
+
+static int schedule_group_destroy(odp_schedule_group_t group)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ uint32_t p;
+ int ret = 0;
+
+ /* Validate inputs */
+ if (group < 0 || group >= (odp_schedule_group_t)MAX_SCHED_GROUP) {
+ ret = -1;
+ goto invalid_group;
+ }
+
+ if (_odp_sched_ts &&
+ odp_unlikely(__atomic_load_n(&_odp_sched_ts->sg_sem,
+ __ATOMIC_RELAXED) != 0)) {
+ (void)__atomic_load_n(&_odp_sched_ts->sg_sem,
+ __ATOMIC_ACQUIRE);
+ _odp_sched_ts->sg_sem = 0;
+ update_sg_membership(_odp_sched_ts);
+ }
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ sgi = (uint32_t)group;
+ if (bitset_is_set(global->sg_free, sgi)) {
+ ret = -1;
+ goto group_not_found;
+ }
+
+ sg = global->sg_vec[sgi];
+ /* First ensure all threads have processed group_join/group_leave
+ * requests.
+ */
+ for (p = 0; p < ODP_SCHED_PRIO_NUM; p++) {
+ if (sg->xcount[p] != 0) {
+ bitset_t wanted = atom_bitset_load(&sg->thr_wanted, __ATOMIC_RELAXED);
+
+ _odp_wait_until_eq_bitset(&sg->thr_actual[p], wanted);
+ }
+ /* Else ignore because no ODP queues on this prio */
+ }
+
+ /* Check if all threads/queues have left the group */
+ for (p = 0; p < ODP_SCHED_PRIO_NUM; p++) {
+ if (!bitset_is_null(sg->thr_actual[p])) {
+ _ODP_ERR("Group has threads\n");
+ ret = -1;
+ goto thrd_q_present_in_group;
+ }
+ if (p != ODP_SCHED_PRIO_PKTIN && sg->xcount[p] != 0) {
+ _ODP_ERR("Group has queues\n");
+ ret = -1;
+ goto thrd_q_present_in_group;
+ }
+ }
+
+ _odp_ishm_pool_free(global->sched_shm_pool, sg);
+ global->sg_vec[sgi] = NULL;
+ atom_bitset_set(&global->sg_free, sgi, __ATOMIC_RELEASE);
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ret;
+
+thrd_q_present_in_group:
+
+group_not_found:
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+invalid_group:
+
+ return ret;
+}
+
+static odp_schedule_group_t schedule_group_lookup(const char *name)
+{
+ uint32_t sgi;
+ odp_schedule_group_t group;
+
+ /* Validate inputs */
+ if (name == NULL)
+ _ODP_ABORT("name or mask is NULL\n");
+
+ group = ODP_SCHED_GROUP_INVALID;
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ /* Scan through the schedule group array */
+ for (sgi = 0; sgi < MAX_SCHED_GROUP; sgi++) {
+ if ((global->sg_vec[sgi] != NULL) &&
+ (strncmp(name, global->sg_vec[sgi]->name,
+ ODP_SCHED_GROUP_NAME_LEN) == 0)) {
+ group = (odp_schedule_group_t)sgi;
+ break;
+ }
+ }
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return group;
+}
+
+static int schedule_group_join(odp_schedule_group_t group,
+ const odp_thrmask_t *mask)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ int ret;
+
+ /* Validate inputs */
+ if (group < 0 || group >= ((odp_schedule_group_t)MAX_SCHED_GROUP))
+ return -1;
+
+ if (mask == NULL)
+ _ODP_ABORT("name or mask is NULL\n");
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ sgi = (uint32_t)group;
+ if (bitset_is_set(global->sg_free, sgi)) {
+ odp_spinlock_unlock(&global->sched_grp_lock);
+ return -1;
+ }
+
+ sg = global->sg_vec[sgi];
+ ret = schedule_group_update(sg, sgi, mask, SCHED_GROUP_JOIN);
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ret;
+}
+
+static int schedule_group_leave(odp_schedule_group_t group,
+ const odp_thrmask_t *mask)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ int ret = 0;
+
+ /* Validate inputs */
+ if (group < 0 || group >= (odp_schedule_group_t)MAX_SCHED_GROUP) {
+ ret = -1;
+ goto invalid_group;
+ }
+
+ if (mask == NULL)
+ _ODP_ABORT("name or mask is NULL\n");
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ sgi = (uint32_t)group;
+ if (bitset_is_set(global->sg_free, sgi)) {
+ ret = -1;
+ goto group_not_found;
+ }
+
+ sg = global->sg_vec[sgi];
+
+ ret = schedule_group_update(sg, sgi, mask, SCHED_GROUP_LEAVE);
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ret;
+
+group_not_found:
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+invalid_group:
+ return ret;
+}
+
+static int schedule_group_thrmask(odp_schedule_group_t group,
+ odp_thrmask_t *mask)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ int ret = 0;
+
+ /* Validate inputs */
+ if (group < 0 || group >= ((odp_schedule_group_t)MAX_SCHED_GROUP)) {
+ ret = -1;
+ goto invalid_group;
+ }
+
+ if (mask == NULL)
+ _ODP_ABORT("name or mask is NULL\n");
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ sgi = (uint32_t)group;
+ if (bitset_is_set(global->sg_free, sgi)) {
+ ret = -1;
+ goto group_not_found;
+ }
+
+ sg = global->sg_vec[sgi];
+ ret = _schedule_group_thrmask(sg, mask);
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ret;
+
+group_not_found:
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+invalid_group:
+ return ret;
+}
+
+static int schedule_group_info(odp_schedule_group_t group,
+ odp_schedule_group_info_t *info)
+{
+ uint32_t sgi;
+ sched_group_t *sg;
+ int ret = 0;
+
+ /* Validate inputs */
+ if (group < 0 || group >= ((odp_schedule_group_t)MAX_SCHED_GROUP)) {
+ ret = -1;
+ goto invalid_group;
+ }
+
+ if (info == NULL)
+ _ODP_ABORT("name or mask is NULL\n");
+
+ odp_spinlock_lock(&global->sched_grp_lock);
+
+ sgi = (uint32_t)group;
+ if (bitset_is_set(global->sg_free, sgi)) {
+ ret = -1;
+ goto group_not_found;
+ }
+
+ sg = global->sg_vec[sgi];
+
+ ret = _schedule_group_thrmask(sg, &info->thrmask);
+
+ info->name = sg->name;
+
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+ return ret;
+
+group_not_found:
+ odp_spinlock_unlock(&global->sched_grp_lock);
+
+invalid_group:
+ return ret;
+}
+
+static int schedule_init_global(void)
+{
+ odp_thrmask_t mask;
+ odp_schedule_group_t tmp_all;
+ odp_schedule_group_t tmp_wrkr;
+ odp_schedule_group_t tmp_ctrl;
+ odp_shm_t shm;
+ _odp_ishm_pool_t *pool;
+ uint32_t bits;
+ uint32_t pool_size;
+ uint64_t min_alloc;
+ uint64_t max_alloc;
+
+ shm = odp_shm_reserve("_odp_sched_scalable_global",
+ sizeof(sched_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ _ODP_ERR("Schedule init: Shm reserve failed.\n");
+ return -1;
+ }
+
+ memset(global, 0, sizeof(sched_global_t));
+ global->shm = shm;
+
+ /* Add storage required for sched groups. Assume worst case
+ * xfactor of MAXTHREADS.
+ */
+ pool_size = (sizeof(sched_group_t) +
+ (ODP_SCHED_PRIO_NUM * MAXTHREADS - 1) *
+ sizeof(sched_queue_t)) * MAX_SCHED_GROUP;
+ /* Choose min_alloc and max_alloc such that slab allocator
+ * is selected.
+ */
+ min_alloc = sizeof(sched_group_t) +
+ (ODP_SCHED_PRIO_NUM * MAXTHREADS - 1) *
+ sizeof(sched_queue_t);
+ max_alloc = min_alloc;
+ pool = _odp_ishm_pool_create("sched_shm_pool", pool_size,
+ min_alloc, max_alloc, 0);
+ if (pool == NULL) {
+ _ODP_ERR("Failed to allocate shared memory pool "
+ "for sched\n");
+ goto failed_sched_shm_pool_create;
+ }
+ global->sched_shm_pool = pool;
+
+ odp_spinlock_init(&global->sched_grp_lock);
+ odp_spinlock_init(&global->init_lock);
+
+ bits = MAX_SCHED_GROUP;
+ if (MAX_SCHED_GROUP == sizeof(global->sg_free) * CHAR_BIT)
+ global->sg_free = ~0;
+ else
+ global->sg_free = (1 << bits) - 1;
+
+ for (uint32_t i = 0; i < MAX_SCHED_GROUP; i++)
+ global->sg_vec[i] = NULL;
+ for (uint32_t i = 0; i < MAXTHREADS; i++) {
+ global->thread_state[i].sg_sem = 0;
+ for (uint32_t j = 0; j < ODP_SCHED_PRIO_NUM; j++)
+ global->thread_state[i].sg_wanted[j] = bitset_null();
+ }
+
+ /* Create sched groups for default GROUP_ALL, GROUP_WORKER and
+ * GROUP_CONTROL groups.
+ */
+ odp_thrmask_zero(&mask);
+ tmp_all = schedule_group_create("__group_all", &mask);
+ if (tmp_all != ODP_SCHED_GROUP_ALL) {
+ _ODP_ERR("Could not create ODP_SCHED_GROUP_ALL()\n");
+ goto failed_create_group_all;
+ }
+
+ tmp_wrkr = schedule_group_create("__group_worker", &mask);
+ if (tmp_wrkr != ODP_SCHED_GROUP_WORKER) {
+ _ODP_ERR("Could not create ODP_SCHED_GROUP_WORKER()\n");
+ goto failed_create_group_worker;
+ }
+
+ tmp_ctrl = schedule_group_create("__group_control", &mask);
+ if (tmp_ctrl != ODP_SCHED_GROUP_CONTROL) {
+ _ODP_ERR("Could not create ODP_SCHED_GROUP_CONTROL()\n");
+ goto failed_create_group_control;
+ }
+
+ global->config_if.group_enable.all = 1;
+ global->config_if.group_enable.control = 1;
+ global->config_if.group_enable.worker = 1;
+
+ return 0;
+
+failed_create_group_control:
+ if (tmp_ctrl != ODP_SCHED_GROUP_INVALID)
+ schedule_group_destroy(ODP_SCHED_GROUP_CONTROL);
+
+failed_create_group_worker:
+ if (tmp_wrkr != ODP_SCHED_GROUP_INVALID)
+ schedule_group_destroy(ODP_SCHED_GROUP_WORKER);
+
+failed_create_group_all:
+ if (tmp_all != ODP_SCHED_GROUP_INVALID)
+ schedule_group_destroy(ODP_SCHED_GROUP_ALL);
+
+failed_sched_shm_pool_create:
+
+ return -1;
+}
+
+static int schedule_term_global(void)
+{
+ /* Destroy enabled sched groups for default GROUP_ALL, GROUP_WORKER and
+ * GROUP_CONTROL groups. */
+ if (global->config_if.group_enable.all) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_ALL) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_ALL\n");
+ }
+ if (global->config_if.group_enable.worker) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_WORKER) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_WORKER\n");
+ }
+ if (global->config_if.group_enable.control) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_CONTROL) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_CONTROL\n");
+ }
+
+ _odp_ishm_pool_destroy(global->sched_shm_pool);
+
+ if (odp_shm_free(global->shm)) {
+ _ODP_ERR("Shm free failed for scalable scheduler");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int schedule_init_local(void)
+{
+ int thr_id;
+ odp_thread_type_t thr_type;
+ odp_thrmask_t mask;
+
+ thr_id = odp_thread_id();
+ if (thread_state_init(thr_id))
+ goto failed_to_init_ts;
+
+ /* Add this thread to default schedule groups */
+ thr_type = odp_thread_type();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ odp_spinlock_lock(&global->init_lock);
+
+ if (global->config_if.group_enable.all) {
+ if (schedule_group_join(ODP_SCHED_GROUP_ALL, &mask) != 0) {
+ _ODP_ERR("Failed to join ODP_SCHED_GROUP_ALL\n");
+ goto failed_to_join_grp_all;
+ }
+ }
+ if (global->config_if.group_enable.control && thr_type == ODP_THREAD_CONTROL) {
+ if (schedule_group_join(ODP_SCHED_GROUP_CONTROL, &mask) != 0) {
+ _ODP_ERR("Failed to join ODP_SCHED_GROUP_CONTROL\n");
+ goto failed_to_join_grp_ctrl;
+ }
+ }
+ if (global->config_if.group_enable.worker && thr_type == ODP_THREAD_WORKER) {
+ if (schedule_group_join(ODP_SCHED_GROUP_WORKER, &mask) != 0) {
+ _ODP_ERR("Failed to join ODP_SCHED_GROUP_WORKER\n");
+ goto failed_to_join_grp_wrkr;
+ }
+ }
+
+ odp_spinlock_unlock(&global->init_lock);
+
+ return 0;
+
+failed_to_join_grp_wrkr:
+failed_to_join_grp_ctrl:
+ if (global->config_if.group_enable.all)
+ schedule_group_leave(ODP_SCHED_GROUP_ALL, &mask);
+
+failed_to_join_grp_all:
+ odp_spinlock_unlock(&global->init_lock);
+
+failed_to_init_ts:
+ return -1;
+}
+
+static int schedule_term_local(void)
+{
+ int thr_id;
+ odp_thread_type_t thr_type;
+ odp_thrmask_t mask;
+ int rc = 0;
+
+ /* Remove this thread from default schedule groups */
+ thr_id = odp_thread_id();
+ thr_type = odp_thread_type();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ if (global->config_if.group_enable.all) {
+ if (schedule_group_leave(ODP_SCHED_GROUP_ALL, &mask) != 0)
+ _ODP_ERR("Failed to leave ODP_SCHED_GROUP_ALL\n");
+ }
+ if (global->config_if.group_enable.control && thr_type == ODP_THREAD_CONTROL) {
+ if (schedule_group_leave(ODP_SCHED_GROUP_CONTROL, &mask) != 0)
+ _ODP_ERR("Failed to leave ODP_SCHED_GROUP_CONTROL\n");
+ }
+ if (global->config_if.group_enable.worker && thr_type == ODP_THREAD_WORKER) {
+ if (schedule_group_leave(ODP_SCHED_GROUP_WORKER, &mask) != 0)
+ _ODP_ERR("Failed to leave ODP_SCHED_GROUP_WORKER\n");
+ }
+
+ update_sg_membership(_odp_sched_ts);
+
+ /* Check if the thread is still part of any groups */
+ if (_odp_sched_ts->num_schedq != 0) {
+ _ODP_ERR("Thread %d still part of scheduler group(s)\n", _odp_sched_ts->tidx);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void schedule_config_init(odp_schedule_config_t *config)
+{
+ config->num_queues = CONFIG_MAX_SCHED_QUEUES;
+ config->queue_size = 0; /* FIXME ? */
+ config->sched_group.all = true;
+ config->sched_group.control = true;
+ config->sched_group.worker = true;
+}
+
+static int schedule_config(const odp_schedule_config_t *config)
+{
+ odp_spinlock_lock(&global->init_lock);
+
+ global->config_if.group_enable.all = config->sched_group.all;
+ global->config_if.group_enable.control = config->sched_group.control;
+ global->config_if.group_enable.worker = config->sched_group.worker;
+
+ /* Destroy disabled predefined scheduling groups. */
+ if (!config->sched_group.all) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_ALL) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_ALL\n");
+ }
+ if (!config->sched_group.worker) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_WORKER) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_WORKER\n");
+ }
+
+ if (!config->sched_group.control) {
+ if (schedule_group_destroy(ODP_SCHED_GROUP_CONTROL) != 0)
+ _ODP_ERR("Failed to destroy ODP_SCHED_GROUP_CONTROL\n");
+ }
+
+ odp_spinlock_unlock(&global->init_lock);
+
+ return 0;
+}
+
+static int num_grps(void)
+{
+ return MAX_SCHED_GROUP - NUM_AUTO_GROUPS;
+}
+
+/*
+ * Stubs for internal scheduler abstraction layer due to absence of NULL
+ * checking before calling the function pointer.
+ */
+
+static int thr_add(odp_schedule_group_t group, int thr)
+{
+ /* This function is a schedule_init_local duplicate. */
+ (void)group;
+ (void)thr;
+ return 0;
+}
+
+static int thr_rem(odp_schedule_group_t group, int thr)
+{
+ /* This function is a schedule_term_local duplicate. */
+ (void)group;
+ (void)thr;
+ return 0;
+}
+
+static int create_queue(uint32_t queue_index,
+ const odp_schedule_param_t *sched_param)
+{
+ /* Not used in scalable scheduler. */
+ (void)queue_index;
+ (void)sched_param;
+ return 0;
+}
+
+static void destroy_queue(uint32_t queue_index)
+{
+ /* Not used in scalable scheduler. */
+ (void)queue_index;
+}
+
+static int sched_queue(uint32_t queue_index)
+{
+ /* Not used in scalable scheduler. */
+ (void)queue_index;
+ return 0;
+}
+
+static int ord_enq_multi(odp_queue_t handle, void *event_hdr[], int num,
+ int *ret)
+
+{
+ queue_entry_t *queue;
+ sched_scalable_thread_state_t *ts;
+ int actual;
+
+ ts = _odp_sched_ts;
+ queue = qentry_from_int(handle);
+ if (ts && odp_unlikely(ts->out_of_order) &&
+ (queue->param.order == ODP_QUEUE_ORDER_KEEP)) {
+ actual = _odp_rctx_save(queue, (_odp_event_hdr_t **)event_hdr, num);
+ *ret = actual;
+ return 1;
+ }
+ return 0;
+}
+
+static void schedule_prefetch(int num)
+{
+ (void)num;
+}
+
+/* Wait until we are in-order (when processing an ordered queue)
+ * Note: this function may be called also when processing other queue types
+ */
+static void order_lock(void)
+{
+ sched_scalable_thread_state_t *ts;
+ reorder_window_t *rwin;
+ uint32_t sn;
+
+ ts = _odp_sched_ts;
+ if (odp_unlikely(ts->out_of_order)) {
+ /* We are processing ordered queue and are currently
+ * out-of-order.
+ * We are in-order when our reorder window slot number (sn)
+ * equals the head of the reorder window.
+ */
+ _ODP_ASSERT(ts->rctx != NULL);
+ rwin = ts->rctx->rwin;
+ sn = ts->rctx->sn;
+ /* Use acquire ordering to be on the safe side even if
+ * this isn't an acquire/release situation (aka lock).
+ */
+ _odp_wait_until_eq_acq_u32(&rwin->hc.head, sn);
+ }
+}
+
+/* This function is unnecessary.
+ * The next thread becomes in-order when we release our reorder context
+ * (i.e. when odp_schedule() is called again.
+ */
+static void order_unlock(void)
+{
+ /* Nothing to do */
+}
+
+static uint32_t schedule_max_ordered_locks(void)
+{
+ return CONFIG_QUEUE_MAX_ORD_LOCKS;
+}
+
+static int schedule_capability(odp_schedule_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_schedule_capability_t));
+
+ capa->max_ordered_locks = schedule_max_ordered_locks();
+ capa->max_groups = num_grps();
+ capa->max_prios = schedule_num_prio();
+ capa->max_queues = CONFIG_MAX_SCHED_QUEUES;
+ capa->max_queue_size = 0;
+ capa->order_wait = ODP_SUPPORT_YES;
+
+ return 0;
+}
+
+static void schedule_print(void)
+{
+ odp_schedule_capability_t capa;
+
+ (void)schedule_capability(&capa);
+
+ _ODP_PRINT("\nScheduler debug info\n");
+ _ODP_PRINT("--------------------\n");
+ _ODP_PRINT(" scheduler: scalable\n");
+ _ODP_PRINT(" max groups: %u\n", capa.max_groups);
+ _ODP_PRINT(" max priorities: %u\n", capa.max_prios);
+ _ODP_PRINT("\n");
+}
+
+const _odp_schedule_api_fn_t _odp_schedule_scalable_api;
+
+static const _odp_schedule_api_fn_t *sched_api(void)
+{
+ return &_odp_schedule_scalable_api;
+}
+
+const schedule_fn_t _odp_schedule_scalable_fn = {
+ .pktio_start = pktio_start,
+ .thr_add = thr_add,
+ .thr_rem = thr_rem,
+ .num_grps = num_grps,
+ .create_queue = create_queue,
+ .destroy_queue = destroy_queue,
+ .sched_queue = sched_queue,
+ .ord_enq_multi = ord_enq_multi,
+ .init_global = schedule_init_global,
+ .term_global = schedule_term_global,
+ .init_local = schedule_init_local,
+ .term_local = schedule_term_local,
+ .order_lock = order_lock,
+ .order_unlock = order_unlock,
+ .max_ordered_locks = schedule_max_ordered_locks,
+ .sched_api = sched_api,
+};
+
+const _odp_schedule_api_fn_t _odp_schedule_scalable_api = {
+ .schedule_wait_time = schedule_wait_time,
+ .schedule_capability = schedule_capability,
+ .schedule_config_init = schedule_config_init,
+ .schedule_config = schedule_config,
+ .schedule = schedule,
+ .schedule_multi = schedule_multi,
+ .schedule_multi_wait = schedule_multi_wait,
+ .schedule_multi_no_wait = schedule_multi_no_wait,
+ .schedule_pause = schedule_pause,
+ .schedule_resume = schedule_resume,
+ .schedule_release_atomic = schedule_release_atomic,
+ .schedule_release_ordered = schedule_release_ordered,
+ .schedule_prefetch = schedule_prefetch,
+ .schedule_min_prio = schedule_min_prio,
+ .schedule_max_prio = schedule_max_prio,
+ .schedule_default_prio = schedule_default_prio,
+ .schedule_num_prio = schedule_num_prio,
+ .schedule_group_create = schedule_group_create,
+ .schedule_group_destroy = schedule_group_destroy,
+ .schedule_group_lookup = schedule_group_lookup,
+ .schedule_group_join = schedule_group_join,
+ .schedule_group_leave = schedule_group_leave,
+ .schedule_group_thrmask = schedule_group_thrmask,
+ .schedule_group_info = schedule_group_info,
+ .schedule_order_lock = schedule_order_lock,
+ .schedule_order_unlock = schedule_order_unlock,
+ .schedule_order_unlock_lock = schedule_order_unlock_lock,
+ .schedule_order_lock_start = schedule_order_lock_start,
+ .schedule_order_lock_wait = schedule_order_lock_wait,
+ .schedule_order_wait = order_lock,
+ .schedule_print = schedule_print
+};
diff --git a/platform/linux-generic/odp_schedule_scalable_ordered.c b/platform/linux-generic/odp_schedule_scalable_ordered.c
new file mode 100644
index 000000000..d8e0bcaaa
--- /dev/null
+++ b/platform/linux-generic/odp_schedule_scalable_ordered.c
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017 ARM Limited
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp/api/shared_memory.h>
+#include <odp/api/cpu.h>
+#include <odp/api/plat/cpu_inlines.h>
+
+#include <odp_bitset.h>
+#include <odp_event_internal.h>
+#include <odp_queue_scalable_internal.h>
+#include <odp_schedule_if.h>
+
+#include <string.h>
+
+extern __thread sched_scalable_thread_state_t *_odp_sched_ts;
+
+reorder_window_t *_odp_rwin_alloc(_odp_ishm_pool_t *pool, unsigned int lock_count)
+{
+ reorder_window_t *rwin;
+ uint32_t i;
+
+ rwin = (reorder_window_t *)
+ shm_pool_alloc_align(pool, sizeof(reorder_window_t));
+ if (rwin == NULL)
+ return NULL;
+
+ rwin->hc.head = 0;
+ rwin->hc.chgi = 0;
+ rwin->winmask = RWIN_SIZE - 1;
+ rwin->tail = 0;
+ rwin->turn = 0;
+ rwin->lock_count = (uint16_t)lock_count;
+ memset(rwin->olock, 0, sizeof(rwin->olock));
+ for (i = 0; i < RWIN_SIZE; i++)
+ rwin->ring[i] = NULL;
+
+ return rwin;
+}
+
+int _odp_rwin_free(_odp_ishm_pool_t *pool, reorder_window_t *rwin)
+{
+ return _odp_ishm_pool_free(pool, rwin);
+}
+
+bool _odp_rwin_reserve(reorder_window_t *rwin, uint32_t *sn)
+{
+ uint32_t head;
+ uint32_t oldt;
+ uint32_t newt;
+ uint32_t winmask;
+
+ /* Read head and tail separately */
+ oldt = rwin->tail;
+ winmask = rwin->winmask;
+ do {
+ /* Need __atomic_load to avoid compiler reordering */
+ head = __atomic_load_n(&rwin->hc.head, __ATOMIC_RELAXED);
+ if (odp_unlikely(oldt - head >= winmask))
+ return false;
+
+ newt = oldt + 1;
+ } while (!__atomic_compare_exchange(&rwin->tail,
+ &oldt,
+ &newt,
+ true,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED));
+ *sn = oldt;
+
+ return true;
+}
+
+bool _odp_rwin_reserve_sc(reorder_window_t *rwin, uint32_t *sn)
+{
+ uint32_t head;
+ uint32_t oldt;
+ uint32_t newt;
+ uint32_t winmask;
+
+ /* Read head and tail separately */
+ oldt = rwin->tail;
+ winmask = rwin->winmask;
+ head = rwin->hc.head;
+ if (odp_unlikely(oldt - head >= winmask))
+ return false;
+ newt = oldt + 1;
+ rwin->tail = newt;
+ *sn = oldt;
+
+ return true;
+}
+
+void _odp_rwin_unreserve_sc(reorder_window_t *rwin, uint32_t sn)
+{
+ _ODP_ASSERT(rwin->tail == sn + 1);
+ rwin->tail = sn;
+}
+
+static void rwin_insert(reorder_window_t *rwin,
+ reorder_context_t *rctx,
+ uint32_t sn,
+ void (*callback)(reorder_context_t *))
+{
+ /* Initialise to silence scan-build */
+ hc_t old = {0, 0};
+ hc_t new;
+ uint32_t winmask;
+
+ __atomic_load(&rwin->hc, &old, __ATOMIC_ACQUIRE);
+ winmask = rwin->winmask;
+ if (old.head != sn) {
+ /* We are out-of-order. Store context in reorder window,
+ * releasing its content.
+ */
+ _ODP_ASSERT(rwin->ring[sn & winmask] == NULL);
+ atomic_store_release(&rwin->ring[sn & winmask],
+ rctx,
+ /*readonly=*/false);
+ rctx = NULL;
+ do {
+ new.head = old.head;
+ new.chgi = old.chgi + 1; /* Changed value */
+ /* Update head & chgi, fail if any has changed */
+ if (__atomic_compare_exchange(&rwin->hc,
+ /* Updated on fail */
+ &old,
+ &new,
+ true,
+ /* Rel our ring update */
+ __ATOMIC_RELEASE,
+ __ATOMIC_ACQUIRE))
+ /* CAS succeeded => head same (we are not
+ * in-order), chgi updated.
+ */
+ return;
+ /* CAS failed => head and/or chgi changed.
+ * We might not be out-of-order anymore.
+ */
+ } while (old.head != sn);
+ }
+
+ /* old.head == sn => we are now in-order! */
+ _ODP_ASSERT(old.head == sn);
+ /* We are in-order so our responsibility to retire contexts */
+ new.head = old.head;
+ new.chgi = old.chgi + 1;
+
+ /* Retire our in-order context (if we still have it) */
+ if (rctx != NULL) {
+ callback(rctx);
+ new.head++;
+ }
+
+ /* Retire in-order contexts in the ring
+ * The first context might actually be ours (if we were originally
+ * out-of-order)
+ */
+ do {
+ for (;;) {
+ rctx = __atomic_load_n(&rwin->ring[new.head & winmask],
+ __ATOMIC_ACQUIRE);
+ if (rctx == NULL)
+ break;
+ /* We are the only thread that are in-order
+ * (until head updated) so don't have to use
+ * atomic load-and-clear (exchange)
+ */
+ rwin->ring[new.head & winmask] = NULL;
+ callback(rctx);
+ new.head++;
+ }
+ /* Update head&chgi, fail if chgi has changed (head cannot change) */
+ } while (!__atomic_compare_exchange(&rwin->hc,
+ &old, /* Updated on failure */
+ &new,
+ false, /* weak */
+ __ATOMIC_RELEASE, /* Release our ring updates */
+ __ATOMIC_ACQUIRE));
+}
+
+void _odp_rctx_init(reorder_context_t *rctx, uint16_t idx,
+ reorder_window_t *rwin, uint32_t sn)
+{
+ /* rctx->rvec_free and rctx->idx already initialised in
+ * thread_state_init function.
+ */
+ _ODP_ASSERT(rctx->idx == idx);
+ rctx->rwin = rwin;
+ rctx->sn = sn;
+ rctx->olock_flags = 0;
+ /* First => no next reorder context */
+ rctx->next_idx = idx;
+ /* Where to store next event */
+ rctx->cur_idx = idx;
+ rctx->numevts = 0;
+}
+
+static inline void rctx_free(const reorder_context_t *rctx)
+{
+ const reorder_context_t *const base = &rctx[-(int)rctx->idx];
+ const uint32_t first = rctx->idx;
+ uint32_t next_idx;
+
+ next_idx = rctx->next_idx;
+
+ _ODP_ASSERT(rctx->rwin != NULL);
+ /* Set free bit */
+ if (rctx->rvec_free == &_odp_sched_ts->rvec_free)
+ /* Since it is our own reorder context, we can instead
+ * perform a non-atomic and relaxed update on our private
+ * rvec_free.
+ */
+ _odp_sched_ts->priv_rvec_free =
+ bitset_set(_odp_sched_ts->priv_rvec_free, rctx->idx);
+ else
+ atom_bitset_set(rctx->rvec_free, rctx->idx, __ATOMIC_RELEASE);
+
+ /* Can't dereference rctx after the corresponding free bit is set */
+ while (next_idx != first) {
+ rctx = &base[next_idx];
+ next_idx = rctx->next_idx;
+ /* Set free bit */
+ if (rctx->rvec_free == &_odp_sched_ts->rvec_free)
+ _odp_sched_ts->priv_rvec_free =
+ bitset_set(_odp_sched_ts->priv_rvec_free, rctx->idx);
+ else
+ atom_bitset_set(rctx->rvec_free, rctx->idx,
+ __ATOMIC_RELEASE);
+ }
+}
+
+static inline void olock_unlock(const reorder_context_t *rctx,
+ reorder_window_t *rwin,
+ uint32_t lock_index)
+{
+ if ((rctx->olock_flags & (1U << lock_index)) == 0) {
+ /* Use relaxed ordering, we are not releasing any updates */
+ rwin->olock[lock_index] = rctx->sn + 1;
+ }
+}
+
+static void olock_release(const reorder_context_t *rctx)
+{
+ reorder_window_t *rwin;
+ uint32_t i;
+
+ rwin = rctx->rwin;
+
+ for (i = 0; i < rwin->lock_count; i++)
+ olock_unlock(rctx, rwin, i);
+}
+
+static void blocking_enqueue(queue_entry_t *q, _odp_event_hdr_t **evts, int num)
+{
+ int actual;
+
+ /* Iterate until all events have been successfully enqueued */
+ for (;;) {
+ /* Attempt to enqueue remaining events */
+ actual = q->enqueue_multi(qentry_to_int(q), evts, num);
+ if (odp_unlikely(actual < 0))
+ _ODP_ERR("Failed to enqueue deferred events\n");
+ /* Update for potential partial success */
+ evts += actual;
+ num -= actual;
+ if (num == 0)
+ break;
+ /* Back-off to decrease load on the system */
+ odp_cpu_pause();
+ }
+}
+
+static void rctx_retire(reorder_context_t *first)
+{
+ reorder_context_t *rctx;
+ queue_entry_t *q;
+ uint32_t i;
+ uint32_t j;
+ uint32_t num;
+
+ rctx = first;
+ do {
+ /* Process all events in this reorder context */
+ for (i = 0; i < rctx->numevts;) {
+ q = rctx->destq[i];
+ /* Find index of next different destq */
+ j = i + 1;
+ while (j < rctx->numevts && rctx->destq[j] == q)
+ j++;
+ num = j - i;
+ /* Blocking enqueue of events to this destq */
+ blocking_enqueue(q, &rctx->events[i], num);
+ i += num;
+ }
+ /* Update rctx pointer to point to 'next_idx' element */
+ rctx += (int)rctx->next_idx - (int)rctx->idx;
+ } while (rctx != first);
+ olock_release(first);
+ rctx_free(first);
+}
+
+void _odp_rctx_release(reorder_context_t *rctx)
+{
+ /* Insert reorder context into reorder window, potentially calling the
+ * rctx_retire function for all pending reorder_contexts.
+ */
+ rwin_insert(rctx->rwin, rctx, rctx->sn, rctx_retire);
+}
+
+/* Save destination queue and events in the reorder context for deferred
+ * enqueue.
+ */
+int _odp_rctx_save(queue_entry_t *queue, _odp_event_hdr_t *event_hdr[], int num)
+{
+ int i;
+ sched_scalable_thread_state_t *ts;
+ reorder_context_t *first;
+ reorder_context_t *cur;
+ bitset_t next_idx;
+
+ ts = _odp_sched_ts;
+ first = ts->rctx;
+ _ODP_ASSERT(ts->rctx != NULL);
+ cur = &first[(int)first->cur_idx - (int)first->idx];
+ for (i = 0; i < num; i++) {
+ if (odp_unlikely(cur->numevts == RC_EVT_SIZE)) {
+ /* No more space in current reorder context
+ * Try to allocate another.
+ */
+ if (odp_unlikely(bitset_is_null(ts->priv_rvec_free))) {
+ ts->priv_rvec_free = atom_bitset_xchg(&ts->rvec_free, 0,
+ __ATOMIC_RELAXED);
+
+ if (odp_unlikely(bitset_is_null(ts->priv_rvec_free)))
+ /* Out of reorder contexts.
+ * Return the number of events
+ * stored so far.
+ */
+ return i;
+ }
+ next_idx = bitset_ffs(ts->priv_rvec_free) - 1;
+ ts->priv_rvec_free =
+ bitset_clr(ts->priv_rvec_free,
+ next_idx);
+ /* Link current to next (for eventual
+ * retiring)
+ */
+ cur->next_idx = next_idx;
+ /* Link first to next (for next call to
+ * queue_enq_multi())
+ */
+ first->cur_idx = next_idx;
+ /* Update current to next */
+ cur = &ts->rvec[next_idx];
+ _odp_rctx_init(cur, next_idx, NULL, 0);
+ /* The last rctx (so far) */
+ cur->next_idx = first->idx;
+ }
+ cur->events[cur->numevts] = event_hdr[i];
+ cur->destq[cur->numevts] = queue;
+ cur->numevts++;
+ }
+ /* All events stored. */
+ return num;
+}
diff --git a/platform/linux-generic/odp_schedule_sp.c b/platform/linux-generic/odp_schedule_sp.c
index d2ba539ce..1ada5581b 100644
--- a/platform/linux-generic/odp_schedule_sp.c
+++ b/platform/linux-generic/odp_schedule_sp.c
@@ -1,30 +1,50 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
*/
-#include <string.h>
+/*
+ * Suppress bounds warnings about interior zero length arrays. Such an array
+ * is used intentionally in prio_queue_t.
+ */
+#if __GNUC__ >= 10
+#pragma GCC diagnostic ignored "-Wzero-length-bounds"
+#endif
+
+#include <odp/api/packet.h>
#include <odp/api/ticketlock.h>
#include <odp/api/thread.h>
+#include <odp/api/plat/thread_inlines.h>
#include <odp/api/time.h>
+#include <odp/api/plat/time_inlines.h>
#include <odp/api/schedule.h>
#include <odp/api/shared_memory.h>
+
+#include <odp/api/plat/schedule_inline_types.h>
+
#include <odp_schedule_if.h>
#include <odp_debug_internal.h>
-#include <odp_align_internal.h>
#include <odp_config_internal.h>
-#include <odp_ring_internal.h>
+#include <odp_event_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ring_u32_internal.h>
+#include <odp_timer_internal.h>
+#include <odp_queue_basic_internal.h>
+#include <odp_string_internal.h>
+#include <odp_global_data.h>
+
+#include <string.h>
#define NUM_THREAD ODP_THREAD_COUNT_MAX
-#define NUM_QUEUE ODP_CONFIG_QUEUES
-#define NUM_PKTIO ODP_CONFIG_PKTIO_ENTRIES
+#define NUM_QUEUE CONFIG_MAX_SCHED_QUEUES
+#define NUM_PKTIO CONFIG_PKTIO_ENTRIES
#define NUM_ORDERED_LOCKS 1
-#define NUM_PRIO 3
#define NUM_STATIC_GROUP 3
#define NUM_GROUP (NUM_STATIC_GROUP + 9)
#define NUM_PKTIN 32
-#define LOWEST_QUEUE_PRIO (NUM_PRIO - 2)
+#define NUM_PRIO 3
+#define MAX_API_PRIO (NUM_PRIO - 2)
+/* Lowest internal priority */
#define PKTIN_PRIO (NUM_PRIO - 1)
#define CMD_QUEUE 0
#define CMD_PKTIO 1
@@ -34,20 +54,17 @@
#define GROUP_PKTIN GROUP_ALL
/* Maximum number of commands: one priority/group for all queues and pktios */
-#define RING_SIZE (ROUNDUP_POWER2_U32(NUM_QUEUE + NUM_PKTIO))
+#define RING_SIZE (_ODP_ROUNDUP_POWER2_U32(NUM_QUEUE + NUM_PKTIO))
#define RING_MASK (RING_SIZE - 1)
/* Ring size must be power of two */
-ODP_STATIC_ASSERT(CHECK_IS_POWER2(RING_SIZE),
+ODP_STATIC_ASSERT(_ODP_CHECK_IS_POWER2(RING_SIZE),
"Ring_size_is_not_power_of_two");
ODP_STATIC_ASSERT(NUM_ORDERED_LOCKS <= CONFIG_QUEUE_MAX_ORD_LOCKS,
"Too_many_ordered_locks");
-struct sched_cmd_t;
-
-struct sched_cmd_s {
- struct sched_cmd_t *next;
+typedef struct ODP_ALIGNED_CACHE {
uint32_t index;
uint32_t ring_idx;
int type;
@@ -56,22 +73,20 @@ struct sched_cmd_s {
int init;
int num_pktin;
int pktin_idx[NUM_PKTIN];
-};
-
-typedef struct sched_cmd_t {
- struct sched_cmd_s s;
- uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct sched_cmd_s)) -
- sizeof(struct sched_cmd_s)];
-} sched_cmd_t ODP_ALIGNED_CACHE;
+ odp_queue_t queue[NUM_PKTIN];
+} sched_cmd_t;
-typedef struct {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+typedef struct ODP_ALIGNED_CACHE {
/* Ring header */
- ring_t ring;
+ ring_u32_t ring;
/* Ring data: queue indexes */
- uint32_t ring_idx[RING_SIZE];
+ uint32_t ring_idx[RING_SIZE]; /* overlaps with ring.data[] */
-} prio_queue_t ODP_ALIGNED_CACHE;
+} prio_queue_t;
+#pragma GCC diagnostic pop
typedef struct thr_group_t {
/* A generation counter for fast comparison if groups have changed */
@@ -85,13 +100,13 @@ typedef struct thr_group_t {
} thr_group_t;
-typedef struct sched_group_t {
+typedef struct ODP_ALIGNED_CACHE sched_group_t {
struct {
odp_ticketlock_t lock;
/* All groups */
struct {
- char name[ODP_SCHED_GROUP_NAME_LEN + 1];
+ char name[ODP_SCHED_GROUP_NAME_LEN];
odp_thrmask_t mask;
int allocated;
} group[NUM_GROUP];
@@ -101,14 +116,20 @@ typedef struct sched_group_t {
} s;
-} sched_group_t ODP_ALIGNED_CACHE;
+} sched_group_t;
typedef struct {
sched_cmd_t queue_cmd[NUM_QUEUE];
sched_cmd_t pktio_cmd[NUM_PKTIO];
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
prio_queue_t prio_queue[NUM_GROUP][NUM_PRIO];
+#pragma GCC diagnostic pop
sched_group_t sched_group;
odp_shm_t shm;
+ /* Scheduler interface config options (not used in fast path) */
+ schedule_config_t config_if;
} sched_global_t;
typedef struct {
@@ -123,6 +144,8 @@ typedef struct {
static sched_global_t *sched_global;
static __thread sched_local_t sched_local;
+static void remove_group(sched_group_t *sched_group, int thr, int group);
+
static inline uint32_t index_to_ring_idx(int pktio, uint32_t index)
{
if (pktio)
@@ -149,16 +172,16 @@ static int init_global(void)
odp_shm_t shm;
sched_group_t *sched_group = NULL;
- ODP_DBG("Using SP scheduler\n");
+ _ODP_DBG("Using SP scheduler\n");
- shm = odp_shm_reserve("sp_scheduler",
+ shm = odp_shm_reserve("_odp_sched_sp_global",
sizeof(sched_global_t),
ODP_CACHE_LINE_SIZE, 0);
sched_global = odp_shm_addr(shm);
if (sched_global == NULL) {
- ODP_ERR("Schedule init: Shm reserve failed.\n");
+ _ODP_ERR("Schedule init: Shm reserve failed.\n");
return -1;
}
@@ -166,22 +189,22 @@ static int init_global(void)
sched_global->shm = shm;
for (i = 0; i < NUM_QUEUE; i++) {
- sched_global->queue_cmd[i].s.type = CMD_QUEUE;
- sched_global->queue_cmd[i].s.index = i;
- sched_global->queue_cmd[i].s.ring_idx = index_to_ring_idx(0, i);
+ sched_global->queue_cmd[i].type = CMD_QUEUE;
+ sched_global->queue_cmd[i].index = i;
+ sched_global->queue_cmd[i].ring_idx = index_to_ring_idx(0, i);
}
for (i = 0; i < NUM_PKTIO; i++) {
- sched_global->pktio_cmd[i].s.type = CMD_PKTIO;
- sched_global->pktio_cmd[i].s.index = i;
- sched_global->pktio_cmd[i].s.ring_idx = index_to_ring_idx(1, i);
- sched_global->pktio_cmd[i].s.prio = PKTIN_PRIO;
- sched_global->pktio_cmd[i].s.group = GROUP_PKTIN;
+ sched_global->pktio_cmd[i].type = CMD_PKTIO;
+ sched_global->pktio_cmd[i].index = i;
+ sched_global->pktio_cmd[i].ring_idx = index_to_ring_idx(1, i);
+ sched_global->pktio_cmd[i].prio = PKTIN_PRIO;
+ sched_global->pktio_cmd[i].group = GROUP_PKTIN;
}
for (i = 0; i < NUM_GROUP; i++)
for (j = 0; j < NUM_PRIO; j++)
- ring_init(&sched_global->prio_queue[i][j].ring);
+ ring_u32_init(&sched_global->prio_queue[i][j].ring);
sched_group = &sched_global->sched_group;
odp_ticketlock_init(&sched_group->s.lock);
@@ -189,21 +212,25 @@ static int init_global(void)
for (i = 0; i < NUM_THREAD; i++)
odp_atomic_init_u32(&sched_group->s.thr[i].gen_cnt, 0);
- strncpy(sched_group->s.group[GROUP_ALL].name, "__group_all",
- ODP_SCHED_GROUP_NAME_LEN);
+ _odp_strcpy(sched_group->s.group[GROUP_ALL].name, "__group_all",
+ ODP_SCHED_GROUP_NAME_LEN);
odp_thrmask_zero(&sched_group->s.group[GROUP_ALL].mask);
sched_group->s.group[GROUP_ALL].allocated = 1;
- strncpy(sched_group->s.group[GROUP_WORKER].name, "__group_worker",
- ODP_SCHED_GROUP_NAME_LEN);
+ _odp_strcpy(sched_group->s.group[GROUP_WORKER].name, "__group_worker",
+ ODP_SCHED_GROUP_NAME_LEN);
odp_thrmask_zero(&sched_group->s.group[GROUP_WORKER].mask);
sched_group->s.group[GROUP_WORKER].allocated = 1;
- strncpy(sched_group->s.group[GROUP_CONTROL].name, "__group_control",
- ODP_SCHED_GROUP_NAME_LEN);
+ _odp_strcpy(sched_group->s.group[GROUP_CONTROL].name, "__group_control",
+ ODP_SCHED_GROUP_NAME_LEN);
odp_thrmask_zero(&sched_group->s.group[GROUP_CONTROL].mask);
sched_group->s.group[GROUP_CONTROL].allocated = 1;
+ sched_global->config_if.group_enable.all = 1;
+ sched_global->config_if.group_enable.control = 1;
+ sched_global->config_if.group_enable.worker = 1;
+
return 0;
}
@@ -217,18 +244,26 @@ static int init_local(void)
static int term_global(void)
{
+ odp_event_t event;
int qi, ret = 0;
for (qi = 0; qi < NUM_QUEUE; qi++) {
- if (sched_global->queue_cmd[qi].s.init) {
- /* todo: dequeue until empty ? */
- sched_cb_queue_destroy_finalize(qi);
+ int report = 1;
+
+ if (sched_global->queue_cmd[qi].init) {
+ while (_odp_sched_queue_deq(qi, &event, 1, 1) > 0) {
+ if (report) {
+ _ODP_ERR("Queue not empty\n");
+ report = 0;
+ }
+ odp_event_free(event);
+ }
}
}
ret = odp_shm_free(sched_global->shm);
if (ret < 0) {
- ODP_ERR("Shm free failed for sp_scheduler");
+ _ODP_ERR("Shm free failed for sp_scheduler");
ret = -1;
}
@@ -240,7 +275,61 @@ static int term_local(void)
return 0;
}
-static unsigned max_ordered_locks(void)
+static void schedule_config_init(odp_schedule_config_t *config)
+{
+ config->num_queues = CONFIG_MAX_SCHED_QUEUES;
+ config->queue_size = _odp_queue_glb->config.max_queue_size;
+ config->sched_group.all = true;
+ config->sched_group.control = true;
+ config->sched_group.worker = true;
+}
+
+static void schedule_group_clear(odp_schedule_group_t group)
+{
+ sched_group_t *sched_group = &sched_global->sched_group;
+ int thr;
+ const odp_thrmask_t *thrmask;
+
+ if (group < 0 || group >= NUM_STATIC_GROUP)
+ _ODP_ABORT("Invalid scheduling group\n");
+
+ thrmask = &sched_group->s.group[group].mask;
+
+ thr = odp_thrmask_first(thrmask);
+ while (thr >= 0) {
+ remove_group(sched_group, thr, group);
+ thr = odp_thrmask_next(thrmask, thr);
+ }
+
+ memset(&sched_group->s.group[group], 0, sizeof(sched_group->s.group[0]));
+}
+
+static int schedule_config(const odp_schedule_config_t *config)
+{
+ sched_group_t *sched_group = &sched_global->sched_group;
+
+ odp_ticketlock_lock(&sched_group->s.lock);
+
+ sched_global->config_if.group_enable.all = config->sched_group.all;
+ sched_global->config_if.group_enable.control = config->sched_group.control;
+ sched_global->config_if.group_enable.worker = config->sched_group.worker;
+
+ /* Remove existing threads from predefined scheduling groups. */
+ if (!config->sched_group.all)
+ schedule_group_clear(ODP_SCHED_GROUP_ALL);
+
+ if (!config->sched_group.worker)
+ schedule_group_clear(ODP_SCHED_GROUP_WORKER);
+
+ if (!config->sched_group.control)
+ schedule_group_clear(ODP_SCHED_GROUP_CONTROL);
+
+ odp_ticketlock_unlock(&sched_group->s.lock);
+
+ return 0;
+}
+
+static uint32_t max_ordered_locks(void)
{
return NUM_ORDERED_LOCKS;
}
@@ -255,7 +344,7 @@ static void add_group(sched_group_t *sched_group, int thr, int group)
thr_group->group[num] = group;
thr_group->num_group = num + 1;
gen_cnt = odp_atomic_load_u32(&thr_group->gen_cnt);
- odp_atomic_store_u32(&thr_group->gen_cnt, gen_cnt + 1);
+ odp_atomic_store_rel_u32(&thr_group->gen_cnt, gen_cnt + 1);
}
static void remove_group(sched_group_t *sched_group, int thr, int group)
@@ -266,6 +355,12 @@ static void remove_group(sched_group_t *sched_group, int thr, int group)
num = thr_group->num_group;
+ /* Extra array bounds check to suppress warning on GCC 7.4 with -O3 */
+ if (num >= NUM_GROUP) {
+ _ODP_ERR("Too many groups");
+ return;
+ }
+
for (i = 0; i < num; i++) {
if (thr_group->group[i] == group) {
found = 1;
@@ -282,7 +377,7 @@ static void remove_group(sched_group_t *sched_group, int thr, int group)
thr_group->num_group = num - 1;
gen_cnt = odp_atomic_load_u32(&thr_group->gen_cnt);
- odp_atomic_store_u32(&thr_group->gen_cnt, gen_cnt + 1);
+ odp_atomic_store_rel_u32(&thr_group->gen_cnt, gen_cnt + 1);
}
}
@@ -290,7 +385,7 @@ static int thr_add(odp_schedule_group_t group, int thr)
{
sched_group_t *sched_group = &sched_global->sched_group;
- if (group < 0 || group >= NUM_GROUP)
+ if (group < 0 || group >= NUM_STATIC_GROUP)
return -1;
if (thr < 0 || thr >= NUM_THREAD)
@@ -300,7 +395,7 @@ static int thr_add(odp_schedule_group_t group, int thr)
if (!sched_group->s.group[group].allocated) {
odp_ticketlock_unlock(&sched_group->s.lock);
- return -1;
+ return 0;
}
odp_thrmask_set(&sched_group->s.group[group].mask, thr);
@@ -315,14 +410,14 @@ static int thr_rem(odp_schedule_group_t group, int thr)
{
sched_group_t *sched_group = &sched_global->sched_group;
- if (group < 0 || group >= NUM_GROUP)
+ if (group < 0 || group >= NUM_STATIC_GROUP)
return -1;
odp_ticketlock_lock(&sched_group->s.lock);
if (!sched_group->s.group[group].allocated) {
odp_ticketlock_unlock(&sched_group->s.lock);
- return -1;
+ return 0;
}
odp_thrmask_clr(&sched_group->s.group[group].mask, thr);
@@ -339,44 +434,49 @@ static int num_grps(void)
return NUM_GROUP - NUM_STATIC_GROUP;
}
-static int init_queue(uint32_t qi, const odp_schedule_param_t *sched_param)
+static int create_queue(uint32_t qi, const odp_schedule_param_t *sched_param)
{
sched_group_t *sched_group = &sched_global->sched_group;
odp_schedule_group_t group = sched_param->group;
int prio = 0;
+ if (odp_global_rw->schedule_configured == 0) {
+ _ODP_ERR("Scheduler has not been configured\n");
+ return -1;
+ }
+
if (group < 0 || group >= NUM_GROUP)
return -1;
if (!sched_group->s.group[group].allocated)
return -1;
- if (sched_param->prio > 0)
- prio = LOWEST_QUEUE_PRIO;
+ /* Inverted prio value (max = 0) vs API */
+ prio = MAX_API_PRIO - sched_param->prio;
- sched_global->queue_cmd[qi].s.prio = prio;
- sched_global->queue_cmd[qi].s.group = group;
- sched_global->queue_cmd[qi].s.init = 1;
+ sched_global->queue_cmd[qi].prio = prio;
+ sched_global->queue_cmd[qi].group = group;
+ sched_global->queue_cmd[qi].init = 1;
return 0;
}
static void destroy_queue(uint32_t qi)
{
- sched_global->queue_cmd[qi].s.prio = 0;
- sched_global->queue_cmd[qi].s.group = 0;
- sched_global->queue_cmd[qi].s.init = 0;
+ sched_global->queue_cmd[qi].prio = 0;
+ sched_global->queue_cmd[qi].group = 0;
+ sched_global->queue_cmd[qi].init = 0;
}
static inline void add_tail(sched_cmd_t *cmd)
{
prio_queue_t *prio_queue;
- int group = cmd->s.group;
- int prio = cmd->s.prio;
- uint32_t idx = cmd->s.ring_idx;
+ int group = cmd->group;
+ int prio = cmd->prio;
+ uint32_t idx = cmd->ring_idx;
prio_queue = &sched_global->prio_queue[group][prio];
- ring_enq(&prio_queue->ring, RING_MASK, idx);
+ ring_u32_enq(&prio_queue->ring, RING_MASK, idx);
}
static inline sched_cmd_t *rem_head(int group, int prio)
@@ -386,9 +486,8 @@ static inline sched_cmd_t *rem_head(int group, int prio)
int pktio;
prio_queue = &sched_global->prio_queue[group][prio];
- ring_idx = ring_deq(&prio_queue->ring, RING_MASK);
- if (ring_idx == RING_EMPTY)
+ if (ring_u32_deq(&prio_queue->ring, RING_MASK, &ring_idx) == 0)
return NULL;
pktio = index_from_ring_idx(&index, ring_idx);
@@ -409,10 +508,10 @@ static int sched_queue(uint32_t qi)
return 0;
}
-static int ord_enq_multi(queue_t q_int, void *buf_hdr[], int num,
+static int ord_enq_multi(odp_queue_t queue, void *buf_hdr[], int num,
int *ret)
{
- (void)q_int;
+ (void)queue;
(void)buf_hdr;
(void)num;
(void)ret;
@@ -421,24 +520,27 @@ static int ord_enq_multi(queue_t q_int, void *buf_hdr[], int num,
return 0;
}
-static void pktio_start(int pktio_index, int num, int pktin_idx[])
+static void pktio_start(int pktio_index,
+ int num,
+ int pktin_idx[],
+ odp_queue_t queue[])
{
int i;
sched_cmd_t *cmd;
- ODP_DBG("pktio index: %i, %i pktin queues %i\n",
- pktio_index, num, pktin_idx[0]);
+ _ODP_DBG("pktio index: %i, %i pktin queues %i\n", pktio_index, num, pktin_idx[0]);
cmd = &sched_global->pktio_cmd[pktio_index];
if (num > NUM_PKTIN)
- ODP_ABORT("Supports only %i pktin queues per interface\n",
- NUM_PKTIN);
+ _ODP_ABORT("Supports only %i pktin queues per interface\n", NUM_PKTIN);
- for (i = 0; i < num; i++)
- cmd->s.pktin_idx[i] = pktin_idx[i];
+ for (i = 0; i < num; i++) {
+ cmd->pktin_idx[i] = pktin_idx[i];
+ cmd->queue[i] = queue[i];
+ }
- cmd->s.num_pktin = num;
+ cmd->num_pktin = num;
add_tail(cmd);
}
@@ -490,6 +592,26 @@ static uint64_t schedule_wait_time(uint64_t ns)
return ns;
}
+static inline void enqueue_packets(odp_queue_t queue,
+ _odp_event_hdr_t *hdr_tbl[], int num_pkt)
+{
+ int num_enq, num_drop;
+
+ num_enq = odp_queue_enq_multi(queue, (odp_event_t *)hdr_tbl,
+ num_pkt);
+
+ if (num_enq < 0)
+ num_enq = 0;
+
+ if (num_enq < num_pkt) {
+ num_drop = num_pkt - num_enq;
+
+ _ODP_DBG("Dropped %i packets\n", num_drop);
+ odp_packet_free_multi((odp_packet_t *)&hdr_tbl[num_enq],
+ num_drop);
+ }
+}
+
static int schedule_multi(odp_queue_t *from, uint64_t wait,
odp_event_t events[], int max_events ODP_UNUSED)
{
@@ -498,7 +620,7 @@ static int schedule_multi(odp_queue_t *from, uint64_t wait,
if (sched_local.cmd) {
/* Continue scheduling if queue is not empty */
- if (sched_cb_queue_empty(sched_local.cmd->s.index) == 0)
+ if (_odp_sched_queue_empty(sched_local.cmd->index) == 0)
add_tail(sched_local.cmd);
sched_local.cmd = NULL;
@@ -514,12 +636,34 @@ static int schedule_multi(odp_queue_t *from, uint64_t wait,
cmd = sched_cmd();
- if (cmd && cmd->s.type == CMD_PKTIO) {
- if (sched_cb_pktin_poll(cmd->s.index, cmd->s.num_pktin,
- cmd->s.pktin_idx)) {
- /* Pktio stopped or closed. */
- sched_cb_pktio_stop_finalize(cmd->s.index);
- } else {
+ if (cmd && cmd->type == CMD_PKTIO) {
+ _odp_event_hdr_t *hdr_tbl[CONFIG_BURST_SIZE];
+ int i;
+ int num_pkt = 0;
+ int max_num = CONFIG_BURST_SIZE;
+ int pktio_idx = cmd->index;
+ int num_pktin = cmd->num_pktin;
+ int *pktin_idx = cmd->pktin_idx;
+ odp_queue_t *queue = cmd->queue;
+
+ for (i = 0; i < num_pktin; i++) {
+ num_pkt = _odp_sched_cb_pktin_poll(pktio_idx,
+ pktin_idx[i],
+ hdr_tbl, max_num);
+
+ if (num_pkt < 0) {
+ /* Pktio stopped or closed. */
+ _odp_sched_cb_pktio_stop_finalize(pktio_idx);
+ break;
+ }
+
+ if (num_pkt == 0)
+ continue;
+
+ enqueue_packets(queue[i], hdr_tbl, num_pkt);
+ }
+
+ if (num_pkt >= 0) {
/* Continue polling pktio. */
add_tail(cmd);
}
@@ -529,6 +673,7 @@ static int schedule_multi(odp_queue_t *from, uint64_t wait,
}
if (cmd == NULL) {
+ timer_run(1);
/* All priority queues are empty */
if (wait == ODP_SCHED_NO_WAIT)
return 0;
@@ -537,8 +682,7 @@ static int schedule_multi(odp_queue_t *from, uint64_t wait,
continue;
if (update_t1) {
- t1 = odp_time_sum(odp_time_local(),
- odp_time_local_from_ns(wait));
+ t1 = odp_time_add_ns(odp_time_local(), wait);
update_t1 = 0;
continue;
}
@@ -549,31 +693,26 @@ static int schedule_multi(odp_queue_t *from, uint64_t wait,
return 0;
}
- qi = cmd->s.index;
- num = sched_cb_queue_deq_multi(qi, events, 1);
+ qi = cmd->index;
+ num = _odp_sched_queue_deq(qi, events, 1, 1);
- if (num > 0) {
- sched_local.cmd = cmd;
+ if (num <= 0) {
+ timer_run(1);
+ /* Destroyed or empty queue. Remove empty queue from
+ * scheduling. A dequeue operation to on an already
+ * empty queue moves it to NOTSCHED state and
+ * sched_queue() will be called on next enqueue. */
+ continue;
+ }
- if (from)
- *from = sched_cb_queue_handle(qi);
+ timer_run(2);
- return num;
- }
+ sched_local.cmd = cmd;
- if (num < 0) {
- /* Destroyed queue */
- sched_cb_queue_destroy_finalize(qi);
- continue;
- }
+ if (from)
+ *from = queue_from_index(qi);
- if (num == 0) {
- /* Remove empty queue from scheduling. A dequeue
- * operation to on an already empty queue moves
- * it to NOTSCHED state and sched_queue() will
- * be called on next enqueue. */
- continue;
- }
+ return num;
}
}
@@ -587,6 +726,18 @@ static odp_event_t schedule(odp_queue_t *from, uint64_t wait)
return ODP_EVENT_INVALID;
}
+static int schedule_multi_wait(odp_queue_t *from, odp_event_t events[],
+ int max_num)
+{
+ return schedule_multi(from, ODP_SCHED_WAIT, events, max_num);
+}
+
+static int schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[],
+ int max_num)
+{
+ return schedule_multi(from, ODP_SCHED_NO_WAIT, events, max_num);
+}
+
static void schedule_pause(void)
{
sched_local.pause = 1;
@@ -599,10 +750,12 @@ static void schedule_resume(void)
static void schedule_release_atomic(void)
{
+ /* Nothing to do */
}
static void schedule_release_ordered(void)
{
+ /* Nothing to do */
}
static void schedule_prefetch(int num)
@@ -610,6 +763,21 @@ static void schedule_prefetch(int num)
(void)num;
}
+static int schedule_min_prio(void)
+{
+ return 0;
+}
+
+static int schedule_max_prio(void)
+{
+ return MAX_API_PRIO;
+}
+
+static int schedule_default_prio(void)
+{
+ return schedule_max_prio() / 2;
+}
+
static int schedule_num_prio(void)
{
/* Lowest priority is used for pktin polling and is internal
@@ -622,7 +790,7 @@ static odp_schedule_group_t schedule_group_create(const char *name,
{
odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
sched_group_t *sched_group = &sched_global->sched_group;
- int i;
+ int i, thr;
odp_ticketlock_lock(&sched_group->s.lock);
@@ -630,17 +798,27 @@ static odp_schedule_group_t schedule_group_create(const char *name,
if (!sched_group->s.group[i].allocated) {
char *grp_name = sched_group->s.group[i].name;
- if (name == NULL) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#if __GNUC__ >= 13
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+ if (name == NULL)
grp_name[0] = 0;
- } else {
- strncpy(grp_name, name,
- ODP_SCHED_GROUP_NAME_LEN - 1);
- grp_name[ODP_SCHED_GROUP_NAME_LEN - 1] = 0;
- }
- odp_thrmask_copy(&sched_group->s.group[i].mask,
- thrmask);
+ else
+ _odp_strcpy(grp_name, name,
+ ODP_SCHED_GROUP_NAME_LEN);
+#pragma GCC diagnostic pop
+
+ odp_thrmask_copy(&sched_group->s.group[i].mask, thrmask);
sched_group->s.group[i].allocated = 1;
group = i;
+
+ thr = odp_thrmask_first(thrmask);
+ while (thr >= 0) {
+ add_group(sched_group, thr, group);
+ thr = odp_thrmask_next(thrmask, thr);
+ }
break;
}
}
@@ -653,6 +831,8 @@ static odp_schedule_group_t schedule_group_create(const char *name,
static int schedule_group_destroy(odp_schedule_group_t group)
{
sched_group_t *sched_group = &sched_global->sched_group;
+ int thr;
+ const odp_thrmask_t *thrmask;
if (group < NUM_STATIC_GROUP || group >= NUM_GROUP)
return -1;
@@ -664,6 +844,14 @@ static int schedule_group_destroy(odp_schedule_group_t group)
return -1;
}
+ thrmask = &sched_group->s.group[group].mask;
+
+ thr = odp_thrmask_first(thrmask);
+ while (thr >= 0) {
+ remove_group(sched_group, thr, group);
+ thr = odp_thrmask_next(thrmask, thr);
+ }
+
memset(&sched_group->s.group[group], 0,
sizeof(sched_group->s.group[0]));
@@ -804,32 +992,89 @@ static int schedule_group_info(odp_schedule_group_t group,
return 0;
}
-static void schedule_order_lock(unsigned lock_index)
+static void schedule_order_lock(uint32_t lock_index)
{
(void)lock_index;
}
-static void schedule_order_unlock(unsigned lock_index)
+static void schedule_order_unlock(uint32_t lock_index)
+{
+ (void)lock_index;
+}
+
+static void schedule_order_unlock_lock(uint32_t unlock_index,
+ uint32_t lock_index)
+{
+ (void)unlock_index;
+ (void)lock_index;
+}
+
+static void schedule_order_lock_start(uint32_t lock_index)
+{
+ (void)lock_index;
+}
+
+static void schedule_order_lock_wait(uint32_t lock_index)
{
(void)lock_index;
}
static void order_lock(void)
{
+ /* Nothing to do */
}
static void order_unlock(void)
{
+ /* Nothing to do */
+}
+
+static int schedule_capability(odp_schedule_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_schedule_capability_t));
+
+ capa->max_ordered_locks = max_ordered_locks();
+ capa->max_groups = num_grps();
+ capa->max_prios = schedule_num_prio();
+ capa->max_queues = CONFIG_MAX_SCHED_QUEUES;
+ capa->max_queue_size = _odp_queue_glb->config.max_queue_size;
+
+ return 0;
+}
+
+static void schedule_print(void)
+{
+ odp_schedule_capability_t capa;
+
+ (void)schedule_capability(&capa);
+
+ _ODP_PRINT("\nScheduler debug info\n");
+ _ODP_PRINT("--------------------\n");
+ _ODP_PRINT(" scheduler: sp\n");
+ _ODP_PRINT(" max groups: %u\n", capa.max_groups);
+ _ODP_PRINT(" max priorities: %u\n", capa.max_prios);
+ _ODP_PRINT("\n");
+}
+
+static void get_config(schedule_config_t *config)
+{
+ *config = sched_global->config_if;
+}
+
+const _odp_schedule_api_fn_t _odp_schedule_sp_api;
+
+static const _odp_schedule_api_fn_t *sched_api(void)
+{
+ return &_odp_schedule_sp_api;
}
/* Fill in scheduler interface */
-const schedule_fn_t schedule_sp_fn = {
- .status_sync = 0,
+const schedule_fn_t _odp_schedule_sp_fn = {
.pktio_start = pktio_start,
.thr_add = thr_add,
.thr_rem = thr_rem,
.num_grps = num_grps,
- .init_queue = init_queue,
+ .create_queue = create_queue,
.destroy_queue = destroy_queue,
.sched_queue = sched_queue,
.ord_enq_multi = ord_enq_multi,
@@ -840,20 +1085,28 @@ const schedule_fn_t schedule_sp_fn = {
.order_lock = order_lock,
.order_unlock = order_unlock,
.max_ordered_locks = max_ordered_locks,
- .unsched_queue = NULL,
- .save_context = NULL
+ .get_config = get_config,
+ .sched_api = sched_api,
};
/* Fill in scheduler API calls */
-const schedule_api_t schedule_sp_api = {
+const _odp_schedule_api_fn_t _odp_schedule_sp_api = {
.schedule_wait_time = schedule_wait_time,
+ .schedule_capability = schedule_capability,
+ .schedule_config_init = schedule_config_init,
+ .schedule_config = schedule_config,
.schedule = schedule,
.schedule_multi = schedule_multi,
+ .schedule_multi_wait = schedule_multi_wait,
+ .schedule_multi_no_wait = schedule_multi_no_wait,
.schedule_pause = schedule_pause,
.schedule_resume = schedule_resume,
.schedule_release_atomic = schedule_release_atomic,
.schedule_release_ordered = schedule_release_ordered,
.schedule_prefetch = schedule_prefetch,
+ .schedule_min_prio = schedule_min_prio,
+ .schedule_max_prio = schedule_max_prio,
+ .schedule_default_prio = schedule_default_prio,
.schedule_num_prio = schedule_num_prio,
.schedule_group_create = schedule_group_create,
.schedule_group_destroy = schedule_group_destroy,
@@ -863,5 +1116,10 @@ const schedule_api_t schedule_sp_api = {
.schedule_group_thrmask = schedule_group_thrmask,
.schedule_group_info = schedule_group_info,
.schedule_order_lock = schedule_order_lock,
- .schedule_order_unlock = schedule_order_unlock
+ .schedule_order_unlock = schedule_order_unlock,
+ .schedule_order_unlock_lock = schedule_order_unlock_lock,
+ .schedule_order_lock_start = schedule_order_lock_start,
+ .schedule_order_lock_wait = schedule_order_lock_wait,
+ .schedule_order_wait = order_lock,
+ .schedule_print = schedule_print
};
diff --git a/platform/linux-generic/odp_shared_memory.c b/platform/linux-generic/odp_shared_memory.c
index a6faff6e3..a6ea427f3 100644
--- a/platform/linux-generic/odp_shared_memory.c
+++ b/platform/linux-generic/odp_shared_memory.c
@@ -1,19 +1,22 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
*/
#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
#include <odp/api/debug.h>
#include <odp/api/std_types.h>
#include <odp/api/shared_memory.h>
#include <odp/api/plat/strong_types.h>
-#include <_ishm_internal.h>
+#include <odp_shm_internal.h>
+#include <odp_init_internal.h>
+#include <odp_global_data.h>
#include <string.h>
-ODP_STATIC_ASSERT(ODP_CONFIG_SHM_BLOCKS >= ODP_CONFIG_POOLS,
- "ODP_CONFIG_SHM_BLOCKS < ODP_CONFIG_POOLS");
+/* Supported ODP_SHM_* flags */
+#define SUPPORTED_SHM_FLAGS (ODP_SHM_PROC | ODP_SHM_SINGLE_VA | ODP_SHM_EXPORT | \
+ ODP_SHM_HP | ODP_SHM_NO_HP)
static inline uint32_t from_handle(odp_shm_t shm)
{
@@ -44,9 +47,10 @@ int odp_shm_capability(odp_shm_capability_t *capa)
{
memset(capa, 0, sizeof(odp_shm_capability_t));
- capa->max_blocks = ODP_CONFIG_SHM_BLOCKS;
- capa->max_size = 0;
+ capa->max_blocks = CONFIG_SHM_BLOCKS;
+ capa->max_size = odp_global_ro.shm_max_size;
capa->max_align = 0;
+ capa->flags = SUPPORTED_SHM_FLAGS;
return 0;
}
@@ -55,11 +59,17 @@ odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align,
uint32_t flags)
{
int block_index;
- int flgs = 0; /* internal ishm flags */
+ uint32_t flgs = 0; /* internal ishm flags */
+ uint32_t supported_flgs = SUPPORTED_SHM_FLAGS;
+
+ if (flags & ~supported_flgs) {
+ _ODP_ERR("Unsupported SHM flag\n");
+ return ODP_SHM_INVALID;
+ }
flgs = get_ishm_flags(flags);
- block_index = _odp_ishm_reserve(name, size, -1, align, flgs, flags);
+ block_index = _odp_ishm_reserve(name, size, -1, align, 0, flgs, flags);
if (block_index >= 0)
return to_handle(block_index);
else
@@ -105,13 +115,43 @@ int odp_shm_info(odp_shm_t shm, odp_shm_info_t *info)
info->size = ishm_info.size;
info->page_size = ishm_info.page_size;
info->flags = ishm_info.user_flags;
+ info->num_seg = 1;
+
+ return 0;
+}
+
+int odp_shm_segment_info(odp_shm_t shm, uint32_t index, uint32_t num,
+ odp_shm_segment_info_t seg_info[])
+{
+ odp_shm_info_t info;
+
+ /* No physical memory segment information available */
+ if (index != 0 || num != 1) {
+ _ODP_ERR("Only single segment supported (%u, %u)\n", index, num);
+ return -1;
+ }
+
+ if (odp_shm_info(shm, &info)) {
+ _ODP_ERR("SHM info call failed\n");
+ return -1;
+ }
+
+ seg_info[0].addr = (uintptr_t)info.addr;
+ seg_info[0].iova = ODP_SHM_IOVA_INVALID;
+ seg_info[0].pa = ODP_SHM_PA_INVALID;
+ seg_info[0].len = info.size;
return 0;
}
void odp_shm_print_all(void)
{
- _odp_ishm_status("Memory allocation status:");
+ _odp_ishm_status("ODP shared memory allocation status:");
+}
+
+void odp_shm_print(odp_shm_t shm)
+{
+ _odp_ishm_print(from_handle(shm));
}
uint64_t odp_shm_to_u64(odp_shm_t hdl)
diff --git a/platform/linux-generic/odp_sorted_list.c b/platform/linux-generic/odp_sorted_list.c
index 8a1dc3ac9..3cca8be0c 100644
--- a/platform/linux-generic/odp_sorted_list.c
+++ b/platform/linux-generic/odp_sorted_list.c
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <stdint.h>
@@ -28,9 +25,12 @@ typedef struct {
uint32_t pad;
} sorted_list_desc_t;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
typedef struct {
sorted_list_desc_t descs[0];
} sorted_list_descs_t;
+#pragma GCC diagnostic pop
typedef struct {
uint64_t total_inserts;
@@ -259,12 +259,12 @@ void _odp_sorted_list_stats_print(_odp_int_sorted_pool_t sorted_pool)
sorted_pool_t *pool;
pool = (sorted_pool_t *)(uintptr_t)sorted_pool;
- ODP_DBG("sorted_pool=0x%" PRIX64 "\n", sorted_pool);
- ODP_DBG(" max_sorted_lists=%u next_list_idx=%u\n",
- pool->max_sorted_lists, pool->next_list_idx);
- ODP_DBG(" total_inserts=%" PRIu64 " total_deletes=%" PRIu64
- " total_removes=%" PRIu64 "\n", pool->total_inserts,
- pool->total_deletes, pool->total_removes);
+ _ODP_PRINT(" sorted_pool=0x%" PRIX64 "\n", sorted_pool);
+ _ODP_PRINT(" max_sorted_lists=%u next_list_idx=%u\n",
+ pool->max_sorted_lists, pool->next_list_idx);
+ _ODP_PRINT(" total_inserts=%" PRIu64 " total_deletes=%" PRIu64
+ " total_removes=%" PRIu64 "\n", pool->total_inserts,
+ pool->total_deletes, pool->total_removes);
}
void _odp_sorted_pool_destroy(_odp_int_sorted_pool_t sorted_pool)
diff --git a/platform/linux-generic/odp_spinlock.c b/platform/linux-generic/odp_spinlock.c
deleted file mode 100644
index cb0f0533c..000000000
--- a/platform/linux-generic/odp_spinlock.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/spinlock.h>
-#include <odp/api/cpu.h>
-#include <odp_atomic_internal.h>
-
-void odp_spinlock_init(odp_spinlock_t *spinlock)
-{
- _odp_atomic_flag_init(&spinlock->lock, 0);
-}
-
-void odp_spinlock_lock(odp_spinlock_t *spinlock)
-{
- /* While the lock is already taken... */
- while (_odp_atomic_flag_tas(&spinlock->lock))
- /* ...spin reading the flag (relaxed MM),
- * the loop will exit when the lock becomes available
- * and we will retry the TAS operation above */
- while (_odp_atomic_flag_load(&spinlock->lock))
- odp_cpu_pause();
-}
-
-int odp_spinlock_trylock(odp_spinlock_t *spinlock)
-{
- return (_odp_atomic_flag_tas(&spinlock->lock) == 0);
-}
-
-void odp_spinlock_unlock(odp_spinlock_t *spinlock)
-{
- _odp_atomic_flag_clear(&spinlock->lock);
-}
-
-int odp_spinlock_is_locked(odp_spinlock_t *spinlock)
-{
- return _odp_atomic_flag_load(&spinlock->lock) != 0;
-}
diff --git a/platform/linux-generic/odp_spinlock_api.c b/platform/linux-generic/odp_spinlock_api.c
new file mode 100644
index 000000000..27155d867
--- /dev/null
+++ b/platform/linux-generic/odp_spinlock_api.c
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/spinlock.h>
+
+#define _ODP_NO_INLINE
+#include <odp/api/plat/spinlock_inlines.h>
diff --git a/platform/linux-generic/odp_spinlock_recursive.c b/platform/linux-generic/odp_spinlock_recursive.c
deleted file mode 100644
index 5ed481c4a..000000000
--- a/platform/linux-generic/odp_spinlock_recursive.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/spinlock_recursive.h>
-#include <odp/api/thread.h>
-
-#define NO_OWNER (-1)
-
-void odp_spinlock_recursive_init(odp_spinlock_recursive_t *rlock)
-{
- odp_spinlock_init(&rlock->lock);
- rlock->owner = NO_OWNER;
- rlock->cnt = 0;
-}
-
-void odp_spinlock_recursive_lock(odp_spinlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->owner == thr) {
- rlock->cnt++;
- return;
- }
-
- odp_spinlock_lock(&rlock->lock);
- rlock->owner = thr;
- rlock->cnt = 1;
-}
-
-int odp_spinlock_recursive_trylock(odp_spinlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->owner == thr) {
- rlock->cnt++;
- return 1;
- }
-
- if (odp_spinlock_trylock(&rlock->lock)) {
- rlock->owner = thr;
- rlock->cnt = 1;
- return 1;
- } else {
- return 0;
- }
-}
-
-void odp_spinlock_recursive_unlock(odp_spinlock_recursive_t *rlock)
-{
- rlock->cnt--;
-
- if (rlock->cnt > 0)
- return;
-
- rlock->owner = NO_OWNER;
- odp_spinlock_unlock(&rlock->lock);
-}
-
-int odp_spinlock_recursive_is_locked(odp_spinlock_recursive_t *rlock)
-{
- int thr = odp_thread_id();
-
- if (rlock->owner == thr)
- return 1;
-
- return odp_spinlock_is_locked(&rlock->lock);
-}
diff --git a/platform/linux-generic/odp_spinlock_recursive_api.c b/platform/linux-generic/odp_spinlock_recursive_api.c
new file mode 100644
index 000000000..ae87cb2f3
--- /dev/null
+++ b/platform/linux-generic/odp_spinlock_recursive_api.c
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/spinlock_recursive.h>
+
+#define _ODP_NO_INLINE
+#include <odp/api/plat/spinlock_recursive_inlines.h>
diff --git a/platform/linux-generic/odp_stash.c b/platform/linux-generic/odp_stash.c
new file mode 100644
index 000000000..154815dd4
--- /dev/null
+++ b/platform/linux-generic/odp_stash.c
@@ -0,0 +1,930 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp/api/align.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/stash.h>
+#include <odp/api/std_types.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/strong_types.h>
+
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_ring_mpmc_u32_internal.h>
+#include <odp_ring_mpmc_u64_internal.h>
+#include <odp_ring_u32_internal.h>
+#include <odp_ring_u64_internal.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+ODP_STATIC_ASSERT(CONFIG_INTERNAL_STASHES < CONFIG_MAX_STASHES, "TOO_MANY_INTERNAL_STASHES");
+
+#define MIN_RING_SIZE 64
+
+enum {
+ STASH_FREE = 0,
+ STASH_RESERVED,
+ STASH_ACTIVE
+};
+
+typedef struct stash_t stash_t;
+
+typedef void (*ring_u32_init_fn_t)(stash_t *stash);
+typedef int32_t (*ring_u32_enq_multi_fn_t)(stash_t *stash, const uint32_t val[], int32_t num);
+typedef int32_t (*ring_u32_enq_batch_fn_t)(stash_t *stash, const uint32_t val[], int32_t num);
+typedef int32_t (*ring_u32_deq_multi_fn_t)(stash_t *stash, uint32_t val[], int32_t num);
+typedef int32_t (*ring_u32_deq_batch_fn_t)(stash_t *stash, uint32_t val[], int32_t num);
+typedef int32_t (*ring_u32_len_fn_t)(stash_t *stash);
+
+typedef void (*ring_u64_init_fn_t)(stash_t *stash);
+typedef int32_t (*ring_u64_enq_multi_fn_t)(stash_t *stash, const uint64_t val[], int32_t num);
+typedef int32_t (*ring_u64_enq_batch_fn_t)(stash_t *stash, const uint64_t val[], int32_t num);
+typedef int32_t (*ring_u64_deq_multi_fn_t)(stash_t *stash, uint64_t val[], int32_t num);
+typedef int32_t (*ring_u64_deq_batch_fn_t)(stash_t *stash, uint64_t val[], int32_t num);
+typedef int32_t (*ring_u64_len_fn_t)(stash_t *stash);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+typedef struct ODP_ALIGNED_CACHE stash_t {
+ /* Ring functions */
+ union {
+ struct {
+ ring_u32_enq_multi_fn_t enq_multi;
+ ring_u32_enq_batch_fn_t enq_batch;
+ ring_u32_deq_multi_fn_t deq_multi;
+ ring_u32_deq_batch_fn_t deq_batch;
+ ring_u32_init_fn_t init;
+ ring_u32_len_fn_t len;
+ } u32;
+
+ struct {
+ ring_u64_enq_multi_fn_t enq_multi;
+ ring_u64_enq_batch_fn_t enq_batch;
+ ring_u64_deq_multi_fn_t deq_multi;
+ ring_u64_deq_batch_fn_t deq_batch;
+ ring_u64_init_fn_t init;
+ ring_u64_len_fn_t len;
+ } u64;
+ } ring_fn;
+
+ uint32_t ring_mask;
+ uint32_t ring_size;
+ uint32_t obj_size;
+
+ char name[ODP_STASH_NAME_LEN];
+ int index;
+ uint8_t strict_size;
+
+ /* Ring header followed by variable sized data (object handles) */
+ union {
+ struct ODP_ALIGNED_CACHE {
+ ring_u32_t hdr;
+ uint32_t data[];
+ } ring_u32;
+
+ struct ODP_ALIGNED_CACHE {
+ ring_u64_t hdr;
+ uint64_t data[];
+ } ring_u64;
+
+ struct ODP_ALIGNED_CACHE {
+ ring_mpmc_u32_t hdr;
+ uint32_t data[];
+ } ring_mpmc_u32;
+
+ struct ODP_ALIGNED_CACHE {
+ ring_mpmc_u64_t hdr;
+ uint64_t data[];
+ } ring_mpmc_u64;
+ };
+
+} stash_t;
+#pragma GCC diagnostic pop
+
+typedef struct stash_global_t {
+ odp_ticketlock_t lock;
+ odp_shm_t shm;
+ uint32_t max_num;
+ uint32_t max_num_obj;
+ uint32_t num_internal;
+ uint8_t stash_state[CONFIG_MAX_STASHES];
+ stash_t *stash[CONFIG_MAX_STASHES];
+ uint8_t data[] ODP_ALIGNED_CACHE;
+
+} stash_global_t;
+
+static stash_global_t *stash_global;
+
+static inline stash_t *stash_entry(odp_stash_t st)
+{
+ return (stash_t *)(uintptr_t)st;
+}
+
+static inline odp_stash_t stash_handle(stash_t *stash)
+{
+ return (odp_stash_t)(uintptr_t)stash;
+}
+
+int _odp_stash_init_global(void)
+{
+ odp_shm_t shm;
+ uint32_t max_num, max_num_obj;
+ const char *str;
+ uint64_t ring_max_size, stash_max_size, stash_data_size, offset;
+ const uint32_t internal_stashes = odp_global_ro.disable.dma ? 0 : CONFIG_INTERNAL_STASHES;
+ uint8_t *stash_data;
+ int val = 0;
+
+ if (odp_global_ro.disable.stash && odp_global_ro.disable.dma) {
+ _ODP_PRINT("Stash is DISABLED\n");
+ return 0;
+ }
+
+ _ODP_PRINT("Stash config:\n");
+
+ str = "stash.max_num";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", str, val);
+ max_num = val;
+
+ str = "stash.max_num_obj";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ _ODP_PRINT(" %s: %i\n", str, val);
+ max_num_obj = val;
+
+ _ODP_PRINT("\n");
+
+ /* Reserve resources for implementation internal stashes */
+ if (max_num > CONFIG_MAX_STASHES - internal_stashes) {
+ _ODP_ERR("Maximum supported number of stashes: %d\n",
+ CONFIG_MAX_STASHES - internal_stashes);
+ return -1;
+ }
+ max_num += internal_stashes;
+
+ /* Must have room for minimum sized ring */
+ if (max_num_obj < MIN_RING_SIZE)
+ max_num_obj = MIN_RING_SIZE - 1;
+
+ /* Ring size must be larger than the number of items stored */
+ ring_max_size = _ODP_ROUNDUP_POWER2_U32(max_num_obj + 1);
+
+ stash_max_size = _ODP_ROUNDUP_CACHE_LINE(sizeof(stash_t) +
+ (ring_max_size * sizeof(uint64_t)));
+ stash_data_size = max_num * stash_max_size;
+
+ shm = odp_shm_reserve("_odp_stash_global", sizeof(stash_global_t) + stash_data_size,
+ ODP_CACHE_LINE_SIZE, 0);
+
+ stash_global = odp_shm_addr(shm);
+
+ if (stash_global == NULL) {
+ _ODP_ERR("SHM reserve of stash global data failed\n");
+ return -1;
+ }
+
+ memset(stash_global, 0, sizeof(stash_global_t));
+ stash_global->shm = shm;
+ stash_global->max_num = max_num;
+ stash_global->max_num_obj = max_num_obj;
+ stash_global->num_internal = internal_stashes;
+ odp_ticketlock_init(&stash_global->lock);
+
+ /* Initialize stash pointers */
+ stash_data = stash_global->data;
+ offset = 0;
+
+ for (uint32_t i = 0; i < max_num; i++) {
+ stash_global->stash[i] = (stash_t *)(uintptr_t)(stash_data + offset);
+ offset += stash_max_size;
+ }
+
+ return 0;
+}
+
+int _odp_stash_term_global(void)
+{
+ if (odp_global_ro.disable.stash)
+ return 0;
+
+ if (stash_global == NULL)
+ return 0;
+
+ if (odp_shm_free(stash_global->shm)) {
+ _ODP_ERR("SHM free failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int odp_stash_capability(odp_stash_capability_t *capa, odp_stash_type_t type)
+{
+ uint32_t max_stashes;
+
+ if (odp_global_ro.disable.stash) {
+ _ODP_ERR("Stash is disabled\n");
+ return -1;
+ }
+
+ (void)type;
+ max_stashes = stash_global->max_num - stash_global->num_internal;
+
+ memset(capa, 0, sizeof(odp_stash_capability_t));
+
+ capa->max_stashes_any_type = max_stashes;
+ capa->max_stashes = max_stashes;
+
+ capa->max_num_obj = stash_global->max_num_obj;
+ capa->max_num.u8 = stash_global->max_num_obj;
+ capa->max_num.u16 = stash_global->max_num_obj;
+ capa->max_num.u32 = stash_global->max_num_obj;
+ capa->max_num.u64 = stash_global->max_num_obj;
+ capa->max_num.max_obj_size = stash_global->max_num_obj;
+
+ capa->max_obj_size = sizeof(uint64_t);
+ capa->max_get_batch = MIN_RING_SIZE;
+ capa->max_put_batch = MIN_RING_SIZE;
+ capa->stats.bit.count = 1;
+
+ return 0;
+}
+
+void odp_stash_param_init(odp_stash_param_t *param)
+{
+ memset(param, 0, sizeof(odp_stash_param_t));
+ param->type = ODP_STASH_TYPE_DEFAULT;
+ param->put_mode = ODP_STASH_OP_MT;
+ param->get_mode = ODP_STASH_OP_MT;
+}
+
+static int reserve_index(void)
+{
+ int index = -1;
+
+ odp_ticketlock_lock(&stash_global->lock);
+
+ for (uint32_t i = 0; i < stash_global->max_num; i++) {
+ if (stash_global->stash_state[i] == STASH_FREE) {
+ index = i;
+ stash_global->stash_state[i] = STASH_RESERVED;
+ break;
+ }
+ }
+
+ odp_ticketlock_unlock(&stash_global->lock);
+
+ return index;
+}
+
+static void free_index(int i)
+{
+ odp_ticketlock_lock(&stash_global->lock);
+
+ stash_global->stash_state[i] = STASH_FREE;
+
+ odp_ticketlock_unlock(&stash_global->lock);
+}
+
+static inline void strict_ring_u32_init(stash_t *stash)
+{
+ ring_u32_init(&stash->ring_u32.hdr);
+
+ for (uint32_t i = 0; i < stash->ring_size; i++)
+ stash->ring_u32.data[i] = 0;
+}
+
+static inline void strict_ring_u64_init(stash_t *stash)
+{
+ ring_u64_init(&stash->ring_u64.hdr);
+
+ for (uint32_t i = 0; i < stash->ring_size; i++)
+ stash->ring_u64.data[i] = 0;
+}
+
+static inline int32_t strict_ring_u32_enq_multi(stash_t *stash, const uint32_t val[], int32_t num)
+{
+ /* Success always */
+ ring_u32_enq_multi(&stash->ring_u32.hdr, stash->ring_mask, (uint32_t *)(uintptr_t)val, num);
+
+ return num;
+}
+
+static inline int32_t strict_ring_u64_enq_multi(stash_t *stash, const uint64_t val[], int32_t num)
+{
+ /* Success always */
+ ring_u64_enq_multi(&stash->ring_u64.hdr, stash->ring_mask, (uint64_t *)(uintptr_t)val, num);
+
+ return num;
+}
+
+static inline int32_t strict_ring_u32_deq_multi(stash_t *stash, uint32_t val[], int32_t num)
+{
+ return ring_u32_deq_multi(&stash->ring_u32.hdr, stash->ring_mask, val, num);
+}
+
+static inline int32_t strict_ring_u64_deq_multi(stash_t *stash, uint64_t val[], int32_t num)
+{
+ return ring_u64_deq_multi(&stash->ring_u64.hdr, stash->ring_mask, val, num);
+}
+
+static inline int32_t strict_ring_u32_deq_batch(stash_t *stash, uint32_t val[], int32_t num)
+{
+ return ring_u32_deq_batch(&stash->ring_u32.hdr, stash->ring_mask, val, num);
+}
+
+static inline int32_t strict_ring_u64_deq_batch(stash_t *stash, uint64_t val[], int32_t num)
+{
+ return ring_u64_deq_batch(&stash->ring_u64.hdr, stash->ring_mask, val, num);
+}
+
+static inline int32_t strict_ring_u32_len(stash_t *stash)
+{
+ return ring_u32_len(&stash->ring_u32.hdr);
+}
+
+static inline int32_t strict_ring_u64_len(stash_t *stash)
+{
+ return ring_u64_len(&stash->ring_u64.hdr);
+}
+
+static inline void mpmc_ring_u32_init(stash_t *stash)
+{
+ ring_mpmc_u32_init(&stash->ring_mpmc_u32.hdr);
+
+ for (uint32_t i = 0; i < stash->ring_size; i++)
+ stash->ring_mpmc_u32.data[i] = 0;
+}
+
+static inline void mpmc_ring_u64_init(stash_t *stash)
+{
+ ring_mpmc_u64_init(&stash->ring_mpmc_u64.hdr);
+
+ for (uint32_t i = 0; i < stash->ring_size; i++)
+ stash->ring_mpmc_u64.data[i] = 0;
+}
+
+static inline int32_t mpmc_ring_u32_enq_multi(stash_t *stash, const uint32_t val[], int32_t num)
+{
+ return ring_mpmc_u32_enq_multi(&stash->ring_mpmc_u32.hdr, stash->ring_mpmc_u32.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u64_enq_multi(stash_t *stash, const uint64_t val[], int32_t num)
+{
+ return ring_mpmc_u64_enq_multi(&stash->ring_mpmc_u64.hdr, stash->ring_mpmc_u64.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u32_enq_batch(stash_t *stash, const uint32_t val[], int32_t num)
+{
+ return ring_mpmc_u32_enq_batch(&stash->ring_mpmc_u32.hdr, stash->ring_mpmc_u32.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u64_enq_batch(stash_t *stash, const uint64_t val[], int32_t num)
+{
+ return ring_mpmc_u64_enq_batch(&stash->ring_mpmc_u64.hdr, stash->ring_mpmc_u64.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u32_deq_multi(stash_t *stash, uint32_t val[], int32_t num)
+{
+ return ring_mpmc_u32_deq_multi(&stash->ring_mpmc_u32.hdr, stash->ring_mpmc_u32.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u64_deq_multi(stash_t *stash, uint64_t val[], int32_t num)
+{
+ return ring_mpmc_u64_deq_multi(&stash->ring_mpmc_u64.hdr, stash->ring_mpmc_u64.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u32_deq_batch(stash_t *stash, uint32_t val[], int32_t num)
+{
+ return ring_mpmc_u32_deq_batch(&stash->ring_mpmc_u32.hdr, stash->ring_mpmc_u32.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u64_deq_batch(stash_t *stash, uint64_t val[], int32_t num)
+{
+ return ring_mpmc_u64_deq_batch(&stash->ring_mpmc_u64.hdr, stash->ring_mpmc_u64.data,
+ stash->ring_mask, val, num);
+}
+
+static inline int32_t mpmc_ring_u32_len(stash_t *stash)
+{
+ return ring_mpmc_u32_len(&stash->ring_mpmc_u32.hdr);
+}
+
+static inline int32_t mpmc_ring_u64_len(stash_t *stash)
+{
+ return ring_mpmc_u64_len(&stash->ring_mpmc_u64.hdr);
+}
+
+odp_stash_t odp_stash_create(const char *name, const odp_stash_param_t *param)
+{
+ stash_t *stash;
+ uint64_t ring_size;
+ int ring_u64, index;
+
+ if (odp_global_ro.disable.stash) {
+ _ODP_ERR("Stash is disabled\n");
+ return ODP_STASH_INVALID;
+ }
+
+ if (param->obj_size > sizeof(uint64_t)) {
+ _ODP_ERR("Too large object handle.\n");
+ return ODP_STASH_INVALID;
+ }
+
+ if (param->num_obj > stash_global->max_num_obj) {
+ _ODP_ERR("Too many objects.\n");
+ return ODP_STASH_INVALID;
+ }
+
+ if (name && strlen(name) >= ODP_STASH_NAME_LEN) {
+ _ODP_ERR("Too long name.\n");
+ return ODP_STASH_INVALID;
+ }
+
+ index = reserve_index();
+
+ if (index < 0) {
+ _ODP_ERR("Maximum number of stashes created already.\n");
+ return ODP_STASH_INVALID;
+ }
+
+ ring_u64 = 0;
+ if (param->obj_size > sizeof(uint32_t))
+ ring_u64 = 1;
+
+ ring_size = param->num_obj;
+
+ /* Ring size must be larger than the number of items stored */
+ if (ring_size + 1 <= MIN_RING_SIZE)
+ ring_size = MIN_RING_SIZE;
+ else
+ ring_size = _ODP_ROUNDUP_POWER2_U32(ring_size + 1);
+
+ stash = stash_global->stash[index];
+ memset(stash, 0, sizeof(stash_t));
+
+ /* Set ring function pointers */
+ stash->strict_size = !!param->strict_size;
+ if (stash->strict_size) {
+ if (ring_u64) {
+ stash->ring_fn.u64.init = strict_ring_u64_init;
+ stash->ring_fn.u64.enq_multi = strict_ring_u64_enq_multi;
+ stash->ring_fn.u64.enq_batch = strict_ring_u64_enq_multi;
+ stash->ring_fn.u64.deq_multi = strict_ring_u64_deq_multi;
+ stash->ring_fn.u64.deq_batch = strict_ring_u64_deq_batch;
+ stash->ring_fn.u64.len = strict_ring_u64_len;
+ } else {
+ stash->ring_fn.u32.init = strict_ring_u32_init;
+ stash->ring_fn.u32.enq_multi = strict_ring_u32_enq_multi;
+ stash->ring_fn.u32.enq_batch = strict_ring_u32_enq_multi;
+ stash->ring_fn.u32.deq_multi = strict_ring_u32_deq_multi;
+ stash->ring_fn.u32.deq_batch = strict_ring_u32_deq_batch;
+ stash->ring_fn.u32.len = strict_ring_u32_len;
+ }
+ } else {
+ if (ring_u64) {
+ stash->ring_fn.u64.init = mpmc_ring_u64_init;
+ stash->ring_fn.u64.enq_multi = mpmc_ring_u64_enq_multi;
+ stash->ring_fn.u64.enq_batch = mpmc_ring_u64_enq_batch;
+ stash->ring_fn.u64.deq_multi = mpmc_ring_u64_deq_multi;
+ stash->ring_fn.u64.deq_batch = mpmc_ring_u64_deq_batch;
+ stash->ring_fn.u64.len = mpmc_ring_u64_len;
+ } else {
+ stash->ring_fn.u32.init = mpmc_ring_u32_init;
+ stash->ring_fn.u32.enq_multi = mpmc_ring_u32_enq_multi;
+ stash->ring_fn.u32.enq_batch = mpmc_ring_u32_enq_batch;
+ stash->ring_fn.u32.deq_multi = mpmc_ring_u32_deq_multi;
+ stash->ring_fn.u32.deq_batch = mpmc_ring_u32_deq_batch;
+ stash->ring_fn.u32.len = mpmc_ring_u32_len;
+ }
+ }
+
+ if (name)
+ strcpy(stash->name, name);
+
+ stash->index = index;
+ stash->obj_size = param->obj_size;
+ stash->ring_mask = ring_size - 1;
+ stash->ring_size = ring_size;
+
+ if (ring_u64)
+ stash->ring_fn.u64.init(stash);
+ else
+ stash->ring_fn.u32.init(stash);
+
+ /* This makes stash visible to lookups */
+ odp_ticketlock_lock(&stash_global->lock);
+ stash_global->stash_state[index] = STASH_ACTIVE;
+ odp_ticketlock_unlock(&stash_global->lock);
+
+ return stash_handle(stash);
+}
+
+int odp_stash_destroy(odp_stash_t st)
+{
+ if (st == ODP_STASH_INVALID)
+ return -1;
+
+ free_index(stash_entry(st)->index);
+
+ return 0;
+}
+
+uint64_t odp_stash_to_u64(odp_stash_t st)
+{
+ return _odp_pri(st);
+}
+
+odp_stash_t odp_stash_lookup(const char *name)
+{
+ stash_t *stash;
+
+ if (name == NULL)
+ return ODP_STASH_INVALID;
+
+ odp_ticketlock_lock(&stash_global->lock);
+
+ for (uint32_t i = 0; i < stash_global->max_num; i++) {
+ stash = stash_global->stash[i];
+
+ if (stash_global->stash_state[i] == STASH_ACTIVE &&
+ strcmp(stash->name, name) == 0) {
+ odp_ticketlock_unlock(&stash_global->lock);
+ return stash_handle(stash);
+ }
+ }
+
+ odp_ticketlock_unlock(&stash_global->lock);
+
+ return ODP_STASH_INVALID;
+}
+
+static inline int32_t stash_put(odp_stash_t st, const void *obj, int32_t num, odp_bool_t is_batch)
+{
+ int32_t (*ring_u32_enq)(stash_t *stash, const uint32_t val[], int32_t num);
+ int32_t (*ring_u64_enq)(stash_t *stash, const uint64_t val[], int32_t num);
+ stash_t *stash = stash_entry(st);
+ uint32_t obj_size;
+ int32_t i;
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ if (is_batch) {
+ ring_u32_enq = stash->ring_fn.u32.enq_batch;
+ ring_u64_enq = stash->ring_fn.u64.enq_batch;
+ } else {
+ ring_u32_enq = stash->ring_fn.u32.enq_multi;
+ ring_u64_enq = stash->ring_fn.u64.enq_multi;
+ }
+
+ obj_size = stash->obj_size;
+
+ if (obj_size == sizeof(uint64_t))
+ return ring_u64_enq(stash, (uint64_t *)(uintptr_t)obj, num);
+
+ if (obj_size == sizeof(uint32_t))
+ return ring_u32_enq(stash, (uint32_t *)(uintptr_t)obj, num);
+
+ if (obj_size == sizeof(uint16_t)) {
+ const uint16_t *u16_ptr = obj;
+ uint32_t u32[num];
+
+ for (i = 0; i < num; i++)
+ u32[i] = u16_ptr[i];
+
+ return ring_u32_enq(stash, u32, num);
+ }
+
+ if (obj_size == sizeof(uint8_t)) {
+ const uint8_t *u8_ptr = obj;
+ uint32_t u32[num];
+
+ for (i = 0; i < num; i++)
+ u32[i] = u8_ptr[i];
+
+ return ring_u32_enq(stash, u32, num);
+ }
+
+ return -1;
+}
+
+int32_t odp_stash_put(odp_stash_t st, const void *obj, int32_t num)
+{
+ return stash_put(st, obj, num, false);
+}
+
+int32_t odp_stash_put_batch(odp_stash_t st, const void *obj, int32_t num)
+{
+ return stash_put(st, obj, num, true);
+}
+
+int32_t odp_stash_put_u32(odp_stash_t st, const uint32_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint32_t));
+
+ return stash->ring_fn.u32.enq_multi(stash, val, num);
+}
+
+int32_t odp_stash_put_u32_batch(odp_stash_t st, const uint32_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint32_t));
+
+ return stash->ring_fn.u32.enq_batch(stash, val, num);
+}
+
+int32_t odp_stash_put_u64(odp_stash_t st, const uint64_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint64_t));
+
+ return stash->ring_fn.u64.enq_multi(stash, (uint64_t *)(uintptr_t)val, num);
+}
+
+int32_t odp_stash_put_u64_batch(odp_stash_t st, const uint64_t val[],
+ int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint64_t));
+
+ return stash->ring_fn.u64.enq_batch(stash, (uint64_t *)(uintptr_t)val, num);
+}
+
+int32_t odp_stash_put_ptr(odp_stash_t st, const uintptr_t ptr[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uintptr_t));
+
+ if (sizeof(uintptr_t) == sizeof(uint32_t))
+ return stash->ring_fn.u32.enq_multi(stash, (uint32_t *)(uintptr_t)ptr, num);
+
+ if (sizeof(uintptr_t) == sizeof(uint64_t))
+ return stash->ring_fn.u64.enq_multi(stash, (uint64_t *)(uintptr_t)ptr, num);
+
+ return -1;
+}
+
+int32_t odp_stash_put_ptr_batch(odp_stash_t st, const uintptr_t ptr[],
+ int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uintptr_t));
+
+ if (sizeof(uintptr_t) == sizeof(uint32_t))
+ return stash->ring_fn.u32.enq_batch(stash, (uint32_t *)(uintptr_t)ptr, num);
+
+ if (sizeof(uintptr_t) == sizeof(uint64_t))
+ return stash->ring_fn.u64.enq_batch(stash, (uint64_t *)(uintptr_t)ptr, num);
+
+ return -1;
+}
+
+static inline int32_t stash_get(odp_stash_t st, void *obj, int32_t num, odp_bool_t is_batch)
+{
+ int32_t (*ring_u32_deq)(stash_t *stash, uint32_t val[], int32_t num);
+ int32_t (*ring_u64_deq)(stash_t *stash, uint64_t val[], int32_t num);
+ stash_t *stash = stash_entry(st);
+ uint32_t obj_size;
+ uint32_t i, num_deq;
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ if (is_batch) {
+ ring_u32_deq = stash->ring_fn.u32.deq_batch;
+ ring_u64_deq = stash->ring_fn.u64.deq_batch;
+ } else {
+ ring_u32_deq = stash->ring_fn.u32.deq_multi;
+ ring_u64_deq = stash->ring_fn.u64.deq_multi;
+ }
+
+ obj_size = stash->obj_size;
+
+ if (obj_size == sizeof(uint64_t))
+ return ring_u64_deq(stash, obj, num);
+
+ if (obj_size == sizeof(uint32_t))
+ return ring_u32_deq(stash, obj, num);
+
+ if (obj_size == sizeof(uint16_t)) {
+ uint16_t *u16_ptr = obj;
+ uint32_t u32[num];
+
+ num_deq = ring_u32_deq(stash, u32, num);
+
+ for (i = 0; i < num_deq; i++)
+ u16_ptr[i] = u32[i];
+
+ return num_deq;
+ }
+
+ if (obj_size == sizeof(uint8_t)) {
+ uint8_t *u8_ptr = obj;
+ uint32_t u32[num];
+
+ num_deq = ring_u32_deq(stash, u32, num);
+
+ for (i = 0; i < num_deq; i++)
+ u8_ptr[i] = u32[i];
+
+ return num_deq;
+ }
+
+ return -1;
+}
+
+int32_t odp_stash_get(odp_stash_t st, void *obj, int32_t num)
+{
+ return stash_get(st, obj, num, false);
+}
+
+int32_t odp_stash_get_batch(odp_stash_t st, void *obj, int32_t num)
+{
+ return stash_get(st, obj, num, true);
+}
+
+int32_t odp_stash_get_u32(odp_stash_t st, uint32_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint32_t));
+
+ return stash->ring_fn.u32.deq_multi(stash, val, num);
+}
+
+int32_t odp_stash_get_u32_batch(odp_stash_t st, uint32_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint32_t));
+
+ return stash->ring_fn.u32.deq_batch(stash, val, num);
+}
+
+int32_t odp_stash_get_u64(odp_stash_t st, uint64_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint64_t));
+
+ return stash->ring_fn.u64.deq_multi(stash, val, num);
+}
+
+int32_t odp_stash_get_u64_batch(odp_stash_t st, uint64_t val[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uint64_t));
+
+ return stash->ring_fn.u64.deq_batch(stash, val, num);
+}
+
+int32_t odp_stash_get_ptr(odp_stash_t st, uintptr_t ptr[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uintptr_t));
+
+ if (sizeof(uintptr_t) == sizeof(uint32_t))
+ return stash->ring_fn.u32.deq_multi(stash, (uint32_t *)(uintptr_t)ptr, num);
+
+ if (sizeof(uintptr_t) == sizeof(uint64_t))
+ return stash->ring_fn.u64.deq_multi(stash, (uint64_t *)(uintptr_t)ptr, num);
+
+ return -1;
+}
+
+int32_t odp_stash_get_ptr_batch(odp_stash_t st, uintptr_t ptr[], int32_t num)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ _ODP_ASSERT(stash->obj_size == sizeof(uintptr_t));
+
+ if (sizeof(uintptr_t) == sizeof(uint32_t))
+ return stash->ring_fn.u32.deq_batch(stash, (uint32_t *)(uintptr_t)ptr, num);
+
+ if (sizeof(uintptr_t) == sizeof(uint64_t))
+ return stash->ring_fn.u64.deq_batch(stash, (uint64_t *)(uintptr_t)ptr, num);
+
+ return -1;
+}
+
+int odp_stash_flush_cache(odp_stash_t st)
+{
+ if (odp_unlikely(st == ODP_STASH_INVALID))
+ return -1;
+
+ return 0;
+}
+
+static uint32_t stash_obj_count(stash_t *stash)
+{
+ uint32_t obj_size = stash->obj_size;
+
+ if (obj_size == sizeof(uint64_t))
+ return stash->ring_fn.u64.len(stash);
+
+ return stash->ring_fn.u32.len(stash);
+}
+
+void odp_stash_print(odp_stash_t st)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (st == ODP_STASH_INVALID) {
+ _ODP_ERR("Bad stash handle\n");
+ return;
+ }
+
+ _ODP_PRINT("\nStash info\n");
+ _ODP_PRINT("----------\n");
+ _ODP_PRINT(" handle 0x%" PRIx64 "\n", odp_stash_to_u64(st));
+ _ODP_PRINT(" name %s\n", stash->name);
+ _ODP_PRINT(" index %i\n", stash->index);
+ _ODP_PRINT(" obj size %u\n", stash->obj_size);
+ _ODP_PRINT(" obj count %u\n", stash_obj_count(stash));
+ _ODP_PRINT(" ring size %u\n", stash->ring_size);
+ _ODP_PRINT(" strict size %u\n", stash->strict_size);
+ _ODP_PRINT("\n");
+}
+
+int odp_stash_stats(odp_stash_t st, odp_stash_stats_t *stats)
+{
+ stash_t *stash = stash_entry(st);
+
+ if (st == ODP_STASH_INVALID) {
+ _ODP_ERR("Bad stash handle\n");
+ return -1;
+ }
+
+ stats->count = stash_obj_count(stash);
+ stats->cache_count = 0;
+
+ return 0;
+}
diff --git a/platform/linux-generic/odp_std.c b/platform/linux-generic/odp_std.c
new file mode 100644
index 000000000..dd2cd486c
--- /dev/null
+++ b/platform/linux-generic/odp_std.c
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/api/std.h>
+
+double odp_fract_u64_to_dbl(const odp_fract_u64_t *fract)
+{
+ double fraction;
+
+ if (fract->numer == 0)
+ fraction = 0.0;
+ else
+ fraction = (double)fract->numer / fract->denom;
+
+ return fract->integer + fraction;
+}
diff --git a/platform/linux-generic/odp_std_api.c b/platform/linux-generic/odp_std_api.c
new file mode 100644
index 000000000..1843fb1ce
--- /dev/null
+++ b/platform/linux-generic/odp_std_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#include <odp/api/std.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/std_inlines.h>
diff --git a/platform/linux-generic/odp_std_clib.c b/platform/linux-generic/odp_std_clib.c
deleted file mode 100644
index 24df249db..000000000
--- a/platform/linux-generic/odp_std_clib.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/std_clib.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/std_clib_inlines.h>
-#endif
diff --git a/platform/linux-generic/odp_string.c b/platform/linux-generic/odp_string.c
new file mode 100644
index 000000000..9ba56dee8
--- /dev/null
+++ b/platform/linux-generic/odp_string.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/hints.h>
+#include <odp_string_internal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+ODP_PRINTF_FORMAT(3, 4)
+int _odp_snprint(char *str, size_t size, const char *format, ...)
+{
+ va_list args;
+ int len;
+
+ /* No space to print new characters */
+ if (size < 1)
+ return 0;
+
+ if (size < 2) {
+ str[0] = 0;
+ return 0;
+ }
+
+ va_start(args, format);
+ len = vsnprintf(str, size, format, args);
+ va_end(args);
+
+ /* Error. Ensure that string has the end mark */
+ if (len < 0) {
+ str[0] = 0;
+ return 0;
+ }
+
+ /* Print would have been longer. Return the number of characters printed. */
+ if (len >= (int)size)
+ return (int)size - 1;
+
+ return len;
+}
+
+char *_odp_strcpy(char *restrict dst, const char *restrict src, size_t sz)
+{
+ if (!sz)
+ return dst;
+
+ strncpy(dst, src, sz - 1);
+ dst[sz - 1] = 0;
+ return dst;
+}
diff --git a/platform/linux-generic/odp_sync.c b/platform/linux-generic/odp_sync.c
deleted file mode 100644
index b7eb503ca..000000000
--- a/platform/linux-generic/odp_sync.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/sync.h>
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/sync_inlines.h>
-#endif
diff --git a/platform/linux-generic/odp_sync_api.c b/platform/linux-generic/odp_sync_api.c
new file mode 100644
index 000000000..cbcf16802
--- /dev/null
+++ b/platform/linux-generic/odp_sync_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#include <odp/api/sync.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/sync_inlines.h>
diff --git a/platform/linux-generic/odp_system_info.c b/platform/linux-generic/odp_system_info.c
index 40ffca078..c31068492 100644
--- a/platform/linux-generic/odp_system_info.c
+++ b/platform/linux-generic/odp_system_info.c
@@ -1,27 +1,26 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2020-2022 Nokia
*
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
+ * Copyright(c) 2010-2014 Intel Corporation
+ * - lib/eal/common/eal_common_string_fns.c
+ * - lib/eal/linux/eal_hugepage_info.c
*/
#include <odp_posix_extensions.h>
#include <odp/api/system_info.h>
#include <odp/api/version.h>
-#include <odp_internal.h>
+#include <odp_global_data.h>
+#include <odp_sysinfo_internal.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
#include <odp_debug_internal.h>
+#include <odp_config_internal.h>
#include <odp/api/align.h>
#include <odp/api/cpu.h>
+
#include <errno.h>
-#include <pthread.h>
-#include <sched.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
@@ -39,19 +38,9 @@
"/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size"
/*
- * Report the number of logical CPUs detected at boot time
- */
-static int sysconf_cpu_count(void)
-{
- return odp_global_data.num_cpus_installed;
-}
-
-#if defined __x86_64__ || defined __i386__ || defined __OCTEON__ || \
-defined __powerpc__
-/*
* Analysis of /sys/devices/system/cpu/ files
*/
-static int systemcpu_cache_line_size(void)
+static int read_cache_line_size(void)
{
FILE *file;
char str[128];
@@ -60,7 +49,9 @@ static int systemcpu_cache_line_size(void)
file = fopen(CACHE_LNSZ_FILE, "rt");
if (file == NULL) {
/* File not found */
- return 0;
+ _ODP_WARN("Unable to read host CPU cache line size. "
+ "Using ODP_CACHE_LINE_SIZE instead.\n");
+ return ODP_CACHE_LINE_SIZE;
}
if (fgets(str, sizeof(str), file) != NULL) {
@@ -74,17 +65,6 @@ static int systemcpu_cache_line_size(void)
return size;
}
-#else
-/*
- * Use dummy data if not available from /sys/devices/system/cpu/
- */
-static int systemcpu_cache_line_size(void)
-{
- return 64;
-}
-#endif
-
-
static uint64_t default_huge_page_size(void)
{
char str[1024];
@@ -92,16 +72,18 @@ static uint64_t default_huge_page_size(void)
FILE *file;
file = fopen("/proc/meminfo", "rt");
+ if (!file)
+ return 0;
while (fgets(str, sizeof(str), file) != NULL) {
if (sscanf(str, "Hugepagesize: %8lu kB", &sz) == 1) {
- ODP_DBG("defaut hp size is %" PRIu64 " kB\n", sz);
+ _ODP_DBG("default hp size is %lu kB\n", sz);
fclose(file);
return (uint64_t)sz * 1024;
}
}
- ODP_ERR("unable to get default hp size\n");
+ _ODP_ERR("unable to get default hp size\n");
fclose(file);
return 0;
}
@@ -227,7 +209,7 @@ static char *get_hugepage_dir(uint64_t hugepage_sz)
while (fgets(buf, sizeof(buf), fd)) {
if (strsplit(buf, sizeof(buf), tokens,
_FIELDNAME_MAX, split_tok) != _FIELDNAME_MAX) {
- ODP_ERR("Error parsing %s\n", proc_mounts);
+ _ODP_ERR("Error parsing %s\n", proc_mounts);
break; /* return NULL */
}
@@ -241,11 +223,9 @@ static char *get_hugepage_dir(uint64_t hugepage_sz)
retval = strdup(tokens[MOUNTPT]);
break;
}
- }
- /* there is an explicit page size, so check it */
- else {
- pagesz =
- str_to_size(&pagesz_str[pagesize_opt_len]);
+ } else {
+ /* there is an explicit page size, so check it */
+ pagesz = str_to_size(&pagesz_str[pagesize_opt_len]);
if (pagesz == hugepage_sz) {
retval = strdup(tokens[MOUNTPT]);
break;
@@ -259,33 +239,61 @@ static char *get_hugepage_dir(uint64_t hugepage_sz)
}
/*
- * Analysis of /sys/devices/system/cpu/ files
+ * Analysis of /sys/devices/system/cpu/cpu%d/cpufreq/ files
*/
-static int systemcpu(system_info_t *sysinfo)
+static uint64_t read_cpufreq(const char *filename, int id)
{
- int ret;
+ char path[256], buffer[256], *endptr = NULL;
+ FILE *file;
+ uint64_t ret = 0;
- ret = sysconf_cpu_count();
- if (ret == 0) {
- ODP_ERR("sysconf_cpu_count failed.\n");
- return -1;
- }
+ snprintf(path, sizeof(path),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/%s", id, filename);
- sysinfo->cpu_count = ret;
+ file = fopen(path, "r");
+ if (file == NULL)
+ return ret;
+ if (fgets(buffer, sizeof(buffer), file) != NULL)
+ ret = strtoull(buffer, &endptr, 0) * 1000;
- ret = systemcpu_cache_line_size();
+ fclose(file);
+
+ return ret;
+}
+
+static inline uint64_t cpu_hz_current(int id)
+{
+ uint64_t cur_hz = read_cpufreq("cpuinfo_cur_freq", id);
+
+ if (!cur_hz)
+ cur_hz = odp_cpu_arch_hz_current(id);
+
+ return cur_hz;
+}
+
+static inline uint64_t cpu_hz_static(int id)
+{
+ return odp_global_ro.system_info.cpu_hz[id];
+}
+
+/*
+ * Analysis of /sys/devices/system/cpu/ files
+ */
+static int system_cache_line(system_info_t *sysinfo)
+{
+ int ret;
+
+ ret = read_cache_line_size();
if (ret == 0) {
- ODP_ERR("systemcpu_cache_line_size failed.\n");
+ _ODP_ERR("read_cache_line_size failed.\n");
return -1;
}
sysinfo->cache_line_size = ret;
- if (ret != ODP_CACHE_LINE_SIZE) {
- ODP_ERR("Cache line sizes definitions don't match.\n");
- return -1;
- }
+ if (ret != ODP_CACHE_LINE_SIZE)
+ _ODP_WARN("Host CPU cache line size and ODP_CACHE_LINE_SIZE don't match.\n");
return 0;
}
@@ -303,33 +311,103 @@ static int system_hp(hugepage_info_t *hugeinfo)
return 0;
}
+static int read_config_file(void)
+{
+ const char *str;
+ int val = 0;
+
+ str = "system.cpu_mhz";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ odp_global_ro.system_info.default_cpu_hz = (uint64_t)val * 1000000;
+
+ str = "system.cpu_mhz_max";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ odp_global_ro.system_info.default_cpu_hz_max = (uint64_t)val * 1000000;
+
+ str = "system.cpu_hz_static";
+ if (!_odp_libconfig_lookup_int(str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ odp_global_ro.system_info.cpu_hz_static = !!val;
+
+ return 0;
+}
+
+static void print_compiler_info(void)
+{
+ _ODP_PRINT("Compiler defines:\n");
+ _ODP_PRINT(" __GCC_ATOMIC_LLONG_LOCK_FREE: %d\n", __GCC_ATOMIC_LLONG_LOCK_FREE);
+ _ODP_PRINT(" __GCC_ATOMIC_LONG_LOCK_FREE: %d\n", __GCC_ATOMIC_LONG_LOCK_FREE);
+ _ODP_PRINT(" __GCC_ATOMIC_INT_LOCK_FREE: %d\n", __GCC_ATOMIC_INT_LOCK_FREE);
+ _ODP_PRINT(" __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16: ");
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+ _ODP_PRINT("1\n");
+#else
+ _ODP_PRINT("0\n");
+#endif
+ _ODP_PRINT("\n");
+}
+
/*
* System info initialisation
*/
-int odp_system_info_init(void)
+int _odp_system_info_init(void)
{
+ int num_cpus;
+ int i;
FILE *file;
- memset(&odp_global_data.system_info, 0, sizeof(system_info_t));
+ memset(&odp_global_ro.system_info, 0, sizeof(system_info_t));
- odp_global_data.system_info.page_size = ODP_PAGE_SIZE;
+ odp_global_ro.system_info.page_size = ODP_PAGE_SIZE;
- file = fopen("/proc/cpuinfo", "rt");
- if (file == NULL) {
- ODP_ERR("Failed to open /proc/cpuinfo\n");
+ /* Read default CPU Hz values from config file */
+ if (read_config_file())
return -1;
- }
- cpuinfo_parser(file, &odp_global_data.system_info);
+ /* Check that CONFIG_NUM_CPU_IDS is large enough */
+ num_cpus = get_nprocs_conf();
+ if (num_cpus > CONFIG_NUM_CPU_IDS)
+ _ODP_ERR("Unable to handle all %d "
+ "CPU IDs. Increase CONFIG_NUM_CPU_IDS value.\n",
+ num_cpus);
- fclose(file);
+ /* Read and save all CPU frequencies for static mode */
+ if (odp_global_ro.system_info.cpu_hz_static)
+ for (i = 0; i < CONFIG_NUM_CPU_IDS; i++)
+ odp_global_ro.system_info.cpu_hz[i] = cpu_hz_current(i);
- if (systemcpu(&odp_global_data.system_info)) {
- ODP_ERR("systemcpu failed\n");
- return -1;
+ /* By default, read max frequency from a cpufreq file */
+ for (i = 0; i < CONFIG_NUM_CPU_IDS; i++) {
+ uint64_t cpu_hz_max = read_cpufreq("cpuinfo_max_freq", i);
+
+ if (cpu_hz_max)
+ odp_global_ro.system_info.cpu_hz_max[i] = cpu_hz_max;
}
- system_hp(&odp_global_data.hugepage_info);
+ file = fopen("/proc/cpuinfo", "rt");
+ if (file != NULL) {
+ /* Read CPU model, and set max cpu frequency
+ * if not set from cpufreq. */
+ _odp_cpuinfo_parser(file, &odp_global_ro.system_info);
+ fclose(file);
+ } else {
+ _odp_dummy_cpuinfo(&odp_global_ro.system_info);
+ }
+
+ if (system_cache_line(&odp_global_ro.system_info))
+ return -1;
+
+ system_hp(&odp_global_ro.hugepage_info);
+
+ print_compiler_info();
return 0;
}
@@ -337,9 +415,9 @@ int odp_system_info_init(void)
/*
* System info termination
*/
-int odp_system_info_term(void)
+int _odp_system_info_term(void)
{
- free(odp_global_data.hugepage_info.default_huge_page_dir);
+ free(odp_global_ro.hugepage_info.default_huge_page_dir);
return 0;
}
@@ -351,14 +429,20 @@ int odp_system_info_term(void)
*/
uint64_t odp_cpu_hz(void)
{
- int id = sched_getcpu();
+ int id = odp_cpu_id();
- return odp_cpu_hz_current(id);
+ if (odp_global_ro.system_info.cpu_hz_static)
+ return cpu_hz_static(id);
+ return cpu_hz_current(id);
}
uint64_t odp_cpu_hz_id(int id)
{
- return odp_cpu_hz_current(id);
+ _ODP_ASSERT(id >= 0 && id < CONFIG_NUM_CPU_IDS);
+
+ if (odp_global_ro.system_info.cpu_hz_static)
+ return cpu_hz_static(id);
+ return cpu_hz_current(id);
}
uint64_t odp_cpu_hz_max(void)
@@ -368,20 +452,63 @@ uint64_t odp_cpu_hz_max(void)
uint64_t odp_cpu_hz_max_id(int id)
{
- if (id >= 0 && id < MAX_CPU_NUMBER)
- return odp_global_data.system_info.cpu_hz_max[id];
+ if (id >= 0 && id < CONFIG_NUM_CPU_IDS)
+ return odp_global_ro.system_info.cpu_hz_max[id];
else
return 0;
}
uint64_t odp_sys_huge_page_size(void)
{
- return odp_global_data.hugepage_info.default_huge_page_size;
+ return odp_global_ro.hugepage_info.default_huge_page_size;
+}
+
+static int pagesz_compare(const void *pagesz1, const void *pagesz2)
+{
+ const uint64_t val1 = *(const uint64_t *)pagesz1;
+ const uint64_t val2 = *(const uint64_t *)pagesz2;
+
+ if (val1 < val2)
+ return -1;
+ if (val1 > val2)
+ return 1;
+ return 0;
+}
+
+int odp_sys_huge_page_size_all(uint64_t size[], int num)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int pagesz_num = 0;
+ int saved = 0;
+
+ /* See: kernel.org: hugetlbpage.txt */
+ dir = opendir("/sys/kernel/mm/hugepages");
+ if (!dir) {
+ _ODP_PRINT("Failed to open /sys/kernel/mm/hugepages: %s\n", strerror(errno));
+ return 0;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ unsigned long sz;
+
+ if (sscanf(entry->d_name, "hugepages-%8lukB", &sz) == 1) {
+ if (size != NULL && saved < num)
+ size[saved++] = sz * 1024;
+ pagesz_num++;
+ }
+ }
+ closedir(dir);
+
+ if (size != NULL && saved > 1)
+ qsort(size, saved, sizeof(uint64_t), pagesz_compare);
+
+ return pagesz_num;
}
uint64_t odp_sys_page_size(void)
{
- return odp_global_data.system_info.page_size;
+ return odp_global_ro.system_info.page_size;
}
const char *odp_cpu_model_str(void)
@@ -391,47 +518,112 @@ const char *odp_cpu_model_str(void)
const char *odp_cpu_model_str_id(int id)
{
- if (id >= 0 && id < MAX_CPU_NUMBER)
- return odp_global_data.system_info.model_str[id];
+ if (id >= 0 && id < CONFIG_NUM_CPU_IDS)
+ return odp_global_ro.system_info.model_str[id];
else
return NULL;
}
int odp_sys_cache_line_size(void)
{
- return odp_global_data.system_info.cache_line_size;
+ return odp_global_ro.system_info.cache_line_size;
}
int odp_cpu_count(void)
{
- return odp_global_data.system_info.cpu_count;
+ return odp_global_ro.num_cpus_installed;
+}
+
+int odp_system_info(odp_system_info_t *info)
+{
+ system_info_t *sys_info = &odp_global_ro.system_info;
+
+ memset(info, 0, sizeof(odp_system_info_t));
+
+ info->cpu_arch = sys_info->cpu_arch;
+ info->cpu_isa_sw = sys_info->cpu_isa_sw;
+ info->cpu_isa_hw = sys_info->cpu_isa_hw;
+
+ return 0;
}
void odp_sys_info_print(void)
{
- int len;
+ int len, num_cpu;
int max_len = 512;
+ odp_cpumask_t cpumask;
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
char str[max_len];
+ memset(cpumask_str, 0, sizeof(cpumask_str));
+
+ num_cpu = odp_cpumask_all_available(&cpumask);
+ odp_cpumask_to_str(&cpumask, cpumask_str, ODP_CPUMASK_STR_SIZE);
+
len = snprintf(str, max_len, "\n"
"ODP system info\n"
"---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
+ "ODP API version: %s\n"
+ "ODP impl name: %s\n"
+ "ODP impl details: %s\n"
+ "CPU model: %s\n"
+ "CPU freq (hz): %" PRIu64 "\n"
+ "Cache line size: %i\n"
+ "CPU count: %i\n"
+ "CPU mask: %s\n"
"\n",
odp_version_api_str(),
odp_version_impl_name(),
+ odp_version_impl_str(),
odp_cpu_model_str(),
odp_cpu_hz_max(),
odp_sys_cache_line_size(),
- odp_cpu_count());
+ num_cpu, cpumask_str);
str[len] = '\0';
- ODP_PRINT("%s", str);
+ _ODP_PRINT("%s", str);
- sys_info_print_arch();
+ _odp_sys_info_print_arch();
+}
+
+void odp_sys_config_print(void)
+{
+ /* Print ODP_CONFIG_FILE default and override values */
+ if (_odp_libconfig_print())
+ _ODP_ERR("Config file print failed\n");
+
+ _ODP_PRINT("\n\nodp_config_internal.h values:\n"
+ "-----------------------------\n");
+ _ODP_PRINT("CONFIG_NUM_CPU_IDS: %i\n", CONFIG_NUM_CPU_IDS);
+ _ODP_PRINT("CONFIG_INTERNAL_QUEUES: %i\n", CONFIG_INTERNAL_QUEUES);
+ _ODP_PRINT("CONFIG_MAX_PLAIN_QUEUES: %i\n", CONFIG_MAX_PLAIN_QUEUES);
+ _ODP_PRINT("CONFIG_MAX_SCHED_QUEUES: %i\n", CONFIG_MAX_SCHED_QUEUES);
+ _ODP_PRINT("CONFIG_MAX_QUEUES: %i\n", CONFIG_MAX_QUEUES);
+ _ODP_PRINT("CONFIG_QUEUE_MAX_ORD_LOCKS: %i\n", CONFIG_QUEUE_MAX_ORD_LOCKS);
+ _ODP_PRINT("CONFIG_MAX_DMA_SESSIONS: %i\n", CONFIG_MAX_DMA_SESSIONS);
+ _ODP_PRINT("CONFIG_INTERNAL_STASHES: %i\n", CONFIG_INTERNAL_STASHES);
+ _ODP_PRINT("CONFIG_MAX_STASHES: %i\n", CONFIG_MAX_STASHES);
+ _ODP_PRINT("CONFIG_PKTIO_ENTRIES: %i\n", CONFIG_PKTIO_ENTRIES);
+ _ODP_PRINT("CONFIG_BUFFER_ALIGN_MAX: %i\n", CONFIG_BUFFER_ALIGN_MAX);
+ _ODP_PRINT("CONFIG_PACKET_HEADROOM: %i\n", CONFIG_PACKET_HEADROOM);
+ _ODP_PRINT("CONFIG_PACKET_TAILROOM: %i\n", CONFIG_PACKET_TAILROOM);
+ _ODP_PRINT("CONFIG_PACKET_SEG_SIZE: %i\n", CONFIG_PACKET_SEG_SIZE);
+ _ODP_PRINT("CONFIG_PACKET_MAX_SEG_LEN: %i\n", CONFIG_PACKET_MAX_SEG_LEN);
+ _ODP_PRINT("CONFIG_PACKET_SEG_LEN_MIN: %i\n", CONFIG_PACKET_SEG_LEN_MIN);
+ _ODP_PRINT("CONFIG_PACKET_VECTOR_MAX_SIZE: %i\n", CONFIG_PACKET_VECTOR_MAX_SIZE);
+ _ODP_PRINT("CONFIG_INTERNAL_SHM_BLOCKS: %i\n", CONFIG_INTERNAL_SHM_BLOCKS);
+ _ODP_PRINT("CONFIG_SHM_BLOCKS: %i\n", CONFIG_SHM_BLOCKS);
+ _ODP_PRINT("CONFIG_BURST_SIZE: %i\n", CONFIG_BURST_SIZE);
+ _ODP_PRINT("CONFIG_INTERNAL_POOLS: %i\n", CONFIG_INTERNAL_POOLS);
+ _ODP_PRINT("CONFIG_POOLS: %i\n", CONFIG_POOLS);
+ _ODP_PRINT("CONFIG_POOL_MAX_NUM: %i\n", CONFIG_POOL_MAX_NUM);
+ _ODP_PRINT("CONFIG_POOL_CACHE_MAX_SIZE: %i\n", CONFIG_POOL_CACHE_MAX_SIZE);
+ _ODP_PRINT("CONFIG_POOL_STATISTICS: %i\n", CONFIG_POOL_STATISTICS);
+ _ODP_PRINT("CONFIG_IPSEC_MAX_NUM_SA: %i\n", CONFIG_IPSEC_MAX_NUM_SA);
+ _ODP_PRINT("CONFIG_TIMER_128BIT_ATOMICS: %i\n", CONFIG_TIMER_128BIT_ATOMICS);
+ _ODP_PRINT("CONFIG_TIMER_PROFILE_INLINE: %i\n", CONFIG_TIMER_PROFILE_INLINE);
+ _ODP_PRINT("CONFIG_ML_MAX_MODELS: %i\n", CONFIG_ML_MAX_MODELS);
+ _ODP_PRINT("CONFIG_ML_MAX_INPUTS: %i\n", CONFIG_ML_MAX_INPUTS);
+ _ODP_PRINT("CONFIG_ML_MAX_OUTPUTS: %i\n", CONFIG_ML_MAX_OUTPUTS);
+ _ODP_PRINT("\n");
}
diff --git a/platform/linux-generic/odp_thread.c b/platform/linux-generic/odp_thread.c
index 33a8a7f3c..34dbb2990 100644
--- a/platform/linux-generic/odp_thread.c
+++ b/platform/linux-generic/odp_thread.c
@@ -1,36 +1,31 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
*/
#include <odp_posix_extensions.h>
#include <sched.h>
+#include <odp/api/atomic.h>
#include <odp/api/thread.h>
#include <odp/api/thrmask.h>
-#include <odp_internal.h>
#include <odp/api/spinlock.h>
+#include <odp_init_internal.h>
#include <odp_config_internal.h>
#include <odp_debug_internal.h>
#include <odp/api/shared_memory.h>
#include <odp/api/align.h>
#include <odp/api/cpu.h>
#include <odp_schedule_if.h>
+#include <odp/api/plat/thread_inlines.h>
+#include <odp_libconfig_internal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
- int thr;
- int cpu;
- odp_thread_type_t type;
-} thread_state_t;
-
-
-typedef struct {
- thread_state_t thr[ODP_THREAD_COUNT_MAX];
+ _odp_thread_state_t thr[ODP_THREAD_COUNT_MAX];
struct {
odp_thrmask_t all;
@@ -38,26 +33,41 @@ typedef struct {
odp_thrmask_t control;
};
- uint32_t num;
- uint32_t num_worker;
- uint32_t num_control;
+ odp_atomic_u32_t num;
+ odp_atomic_u32_t num_worker;
+ odp_atomic_u32_t num_control;
+ uint32_t num_max;
odp_spinlock_t lock;
} thread_globals_t;
-
/* Globals */
static thread_globals_t *thread_globals;
+#include <odp/visibility_begin.h>
/* Thread local */
-static __thread thread_state_t *this_thread;
+__thread _odp_thread_state_t *_odp_this_thread;
+#include <odp/visibility_end.h>
-int odp_thread_init_global(void)
+int _odp_thread_init_global(void)
{
odp_shm_t shm;
+ int num_max = 0;
+ const char *str = "system.thread_count_max";
- shm = odp_shm_reserve("odp_thread_globals",
+ if (!_odp_libconfig_lookup_int(str, &num_max)) {
+ _ODP_ERR("Config option '%s' not found.\n", str);
+ return -1;
+ }
+ if (num_max <= 0) {
+ _ODP_ERR("Config option '%s' not valid.\n", str);
+ return -1;
+ }
+ if (num_max > ODP_THREAD_COUNT_MAX)
+ num_max = ODP_THREAD_COUNT_MAX;
+
+ shm = odp_shm_reserve("_odp_thread_global",
sizeof(thread_globals_t),
ODP_CACHE_LINE_SIZE, 0);
@@ -67,18 +77,28 @@ int odp_thread_init_global(void)
return -1;
memset(thread_globals, 0, sizeof(thread_globals_t));
+ odp_atomic_init_u32(&thread_globals->num, 0);
+ odp_atomic_init_u32(&thread_globals->num_worker, 0);
+ odp_atomic_init_u32(&thread_globals->num_control, 0);
odp_spinlock_init(&thread_globals->lock);
+ thread_globals->num_max = num_max;
+ _ODP_PRINT("System config:\n");
+ _ODP_PRINT(" system.thread_count_max: %d\n\n", num_max);
return 0;
}
-int odp_thread_term_global(void)
+int _odp_thread_term_global(void)
{
- int ret;
+ int ret, num;
- ret = odp_shm_free(odp_shm_lookup("odp_thread_globals"));
+ num = odp_atomic_load_u32(&thread_globals->num);
+ if (num)
+ _ODP_ERR("%u threads have not called odp_term_local().\n", num);
+
+ ret = odp_shm_free(odp_shm_lookup("_odp_thread_global"));
if (ret < 0)
- ODP_ERR("shm free failed for odp_thread_globals");
+ _ODP_ERR("shm free failed for _odp_thread_globals");
return ret;
}
@@ -88,22 +108,22 @@ static int alloc_id(odp_thread_type_t type)
int thr;
odp_thrmask_t *all = &thread_globals->all;
- if (thread_globals->num >= ODP_THREAD_COUNT_MAX)
+ if (odp_atomic_load_u32(&thread_globals->num) >= thread_globals->num_max)
return -1;
- for (thr = 0; thr < ODP_THREAD_COUNT_MAX; thr++) {
+ for (thr = 0; thr < (int)thread_globals->num_max; thr++) {
if (odp_thrmask_isset(all, thr) == 0) {
odp_thrmask_set(all, thr);
if (type == ODP_THREAD_WORKER) {
odp_thrmask_set(&thread_globals->worker, thr);
- thread_globals->num_worker++;
+ odp_atomic_inc_u32(&thread_globals->num_worker);
} else {
odp_thrmask_set(&thread_globals->control, thr);
- thread_globals->num_control++;
+ odp_atomic_inc_u32(&thread_globals->num_control);
}
- thread_globals->num++;
+ odp_atomic_inc_u32(&thread_globals->num);
return thr;
}
}
@@ -115,7 +135,7 @@ static int free_id(int thr)
{
odp_thrmask_t *all = &thread_globals->all;
- if (thr < 0 || thr >= ODP_THREAD_COUNT_MAX)
+ if (thr < 0 || thr >= (int)thread_globals->num_max)
return -1;
if (odp_thrmask_isset(all, thr) == 0)
@@ -125,34 +145,47 @@ static int free_id(int thr)
if (thread_globals->thr[thr].type == ODP_THREAD_WORKER) {
odp_thrmask_clr(&thread_globals->worker, thr);
- thread_globals->num_worker--;
+ odp_atomic_dec_u32(&thread_globals->num_worker);
} else {
odp_thrmask_clr(&thread_globals->control, thr);
- thread_globals->num_control--;
+ odp_atomic_dec_u32(&thread_globals->num_control);
}
- thread_globals->num--;
- return thread_globals->num;
+ return odp_atomic_fetch_dec_u32(&thread_globals->num) - 1;
}
-int odp_thread_init_local(odp_thread_type_t type)
+int _odp_thread_init_local(odp_thread_type_t type)
{
int id;
int cpu;
+ int group_all, group_worker, group_control;
+
+ group_all = 1;
+ group_worker = 1;
+ group_control = 1;
+
+ if (_odp_sched_fn->get_config) {
+ schedule_config_t schedule_config;
+
+ _odp_sched_fn->get_config(&schedule_config);
+ group_all = schedule_config.group_enable.all;
+ group_worker = schedule_config.group_enable.worker;
+ group_control = schedule_config.group_enable.control;
+ }
odp_spinlock_lock(&thread_globals->lock);
id = alloc_id(type);
odp_spinlock_unlock(&thread_globals->lock);
if (id < 0) {
- ODP_ERR("Too many threads\n");
+ _ODP_ERR("Too many threads\n");
return -1;
}
cpu = sched_getcpu();
if (cpu < 0) {
- ODP_ERR("getcpu failed\n");
+ _ODP_ERR("getcpu failed\n");
return -1;
}
@@ -160,76 +193,111 @@ int odp_thread_init_local(odp_thread_type_t type)
thread_globals->thr[id].cpu = cpu;
thread_globals->thr[id].type = type;
- this_thread = &thread_globals->thr[id];
+ _odp_this_thread = &thread_globals->thr[id];
+
+ if (group_all)
+ _odp_sched_fn->thr_add(ODP_SCHED_GROUP_ALL, id);
- sched_fn->thr_add(ODP_SCHED_GROUP_ALL, id);
+ if (type == ODP_THREAD_WORKER && group_worker)
+ _odp_sched_fn->thr_add(ODP_SCHED_GROUP_WORKER, id);
- if (type == ODP_THREAD_WORKER)
- sched_fn->thr_add(ODP_SCHED_GROUP_WORKER, id);
- else if (type == ODP_THREAD_CONTROL)
- sched_fn->thr_add(ODP_SCHED_GROUP_CONTROL, id);
+ if (type == ODP_THREAD_CONTROL && group_control)
+ _odp_sched_fn->thr_add(ODP_SCHED_GROUP_CONTROL, id);
return 0;
}
-int odp_thread_term_local(void)
+int _odp_thread_term_local(void)
{
int num;
- int id = this_thread->thr;
- odp_thread_type_t type = this_thread->type;
+ int group_all, group_worker, group_control;
+ int id = _odp_this_thread->thr;
+ odp_thread_type_t type = _odp_this_thread->type;
- sched_fn->thr_rem(ODP_SCHED_GROUP_ALL, id);
+ group_all = 1;
+ group_worker = 1;
+ group_control = 1;
- if (type == ODP_THREAD_WORKER)
- sched_fn->thr_rem(ODP_SCHED_GROUP_WORKER, id);
- else if (type == ODP_THREAD_CONTROL)
- sched_fn->thr_rem(ODP_SCHED_GROUP_CONTROL, id);
+ if (_odp_sched_fn->get_config) {
+ schedule_config_t schedule_config;
+
+ _odp_sched_fn->get_config(&schedule_config);
+ group_all = schedule_config.group_enable.all;
+ group_worker = schedule_config.group_enable.worker;
+ group_control = schedule_config.group_enable.control;
+ }
+
+ if (group_all)
+ _odp_sched_fn->thr_rem(ODP_SCHED_GROUP_ALL, id);
+
+ if (type == ODP_THREAD_WORKER && group_worker)
+ _odp_sched_fn->thr_rem(ODP_SCHED_GROUP_WORKER, id);
+
+ if (type == ODP_THREAD_CONTROL && group_control)
+ _odp_sched_fn->thr_rem(ODP_SCHED_GROUP_CONTROL, id);
+
+ _odp_this_thread = NULL;
odp_spinlock_lock(&thread_globals->lock);
num = free_id(id);
odp_spinlock_unlock(&thread_globals->lock);
if (num < 0) {
- ODP_ERR("failed to free thread id %i", id);
+ _ODP_ERR("failed to free thread id %i", id);
return -1;
}
return num; /* return a number of threads left */
}
-int odp_thread_id(void)
+int odp_thread_count(void)
{
- return this_thread->thr;
+ return odp_atomic_load_u32(&thread_globals->num);
}
-int odp_thread_count(void)
+int odp_thread_control_count(void)
{
- return thread_globals->num;
+ return odp_atomic_load_u32(&thread_globals->num_control);
+}
+
+int odp_thread_worker_count(void)
+{
+ return odp_atomic_load_u32(&thread_globals->num_worker);
}
int odp_thread_count_max(void)
{
- return ODP_THREAD_COUNT_MAX;
+ return thread_globals->num_max;
}
-odp_thread_type_t odp_thread_type(void)
+int odp_thread_control_count_max(void)
{
- return this_thread->type;
+ return thread_globals->num_max;
}
-int odp_cpu_id(void)
+int odp_thread_worker_count_max(void)
{
- return this_thread->cpu;
+ return thread_globals->num_max;
}
int odp_thrmask_worker(odp_thrmask_t *mask)
{
+ uint32_t num_worker;
+
+ odp_spinlock_lock(&thread_globals->lock);
odp_thrmask_copy(mask, &thread_globals->worker);
- return thread_globals->num_worker;
+ num_worker = odp_atomic_load_u32(&thread_globals->num_worker);
+ odp_spinlock_unlock(&thread_globals->lock);
+ return num_worker;
}
int odp_thrmask_control(odp_thrmask_t *mask)
{
+ uint32_t num_control;
+
+ odp_spinlock_lock(&thread_globals->lock);
odp_thrmask_copy(mask, &thread_globals->control);
- return thread_globals->num_control;
+ num_control = odp_atomic_load_u32(&thread_globals->num_control);
+ odp_spinlock_unlock(&thread_globals->lock);
+ return num_control;
}
diff --git a/platform/linux-generic/odp_thread_api.c b/platform/linux-generic/odp_thread_api.c
new file mode 100644
index 000000000..65f7820f6
--- /dev/null
+++ b/platform/linux-generic/odp_thread_api.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/thread.h>
+#include <odp/api/cpu.h>
+
+/* Include non-inlined versions of API functions */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/thread_inlines.h>
diff --git a/platform/linux-generic/odp_thrmask.c b/platform/linux-generic/odp_thrmask.c
index c176a5c6d..1dfde9e1b 100644
--- a/platform/linux-generic/odp_thrmask.c
+++ b/platform/linux-generic/odp_thrmask.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <odp/api/thrmask.h>
diff --git a/platform/linux-generic/odp_ticketlock.c b/platform/linux-generic/odp_ticketlock.c
deleted file mode 100644
index f73dd9ab4..000000000
--- a/platform/linux-generic/odp_ticketlock.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp/api/plat/ticketlock_inlines.h>
-#include <odp/api/spec/ticketlock.h>
-
-void odp_ticketlock_init(odp_ticketlock_t *ticketlock)
-{
- odp_atomic_init_u32(&ticketlock->next_ticket, 0);
- odp_atomic_init_u32(&ticketlock->cur_ticket, 0);
-}
-
-/* Include non-inlined versions of API functions */
-#if ODP_ABI_COMPAT == 1
-#include <odp/api/plat/ticketlock_inlines_api.h>
-#endif
diff --git a/platform/linux-generic/odp_ticketlock_api.c b/platform/linux-generic/odp_ticketlock_api.c
new file mode 100644
index 000000000..aac5945a6
--- /dev/null
+++ b/platform/linux-generic/odp_ticketlock_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+#include <odp/api/ticketlock.h>
+#include <odp/api/plat/atomic_inlines.h>
+
+#define _ODP_NO_INLINE
+#include <odp/api/plat/ticketlock_inlines.h>
diff --git a/platform/linux-generic/odp_time.c b/platform/linux-generic/odp_time.c
deleted file mode 100644
index a831cc513..000000000
--- a/platform/linux-generic/odp_time.c
+++ /dev/null
@@ -1,309 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_posix_extensions.h>
-
-#include <time.h>
-#include <odp/api/time.h>
-#include <odp/api/hints.h>
-#include <odp_debug_internal.h>
-#include <odp_time_internal.h>
-#include <string.h>
-#include <inttypes.h>
-
-typedef struct time_global_t {
- struct timespec spec_start;
- int use_hw;
- uint64_t hw_start;
- uint64_t hw_freq_hz;
-} time_global_t;
-
-static time_global_t global;
-
-/*
- * Posix timespec based functions
- */
-
-static inline uint64_t time_spec_diff_nsec(struct timespec *t2,
- struct timespec *t1)
-{
- struct timespec diff;
- uint64_t nsec;
-
- diff.tv_sec = t2->tv_sec - t1->tv_sec;
- diff.tv_nsec = t2->tv_nsec - t1->tv_nsec;
-
- if (diff.tv_nsec < 0) {
- diff.tv_nsec += ODP_TIME_SEC_IN_NS;
- diff.tv_sec -= 1;
- }
-
- nsec = (diff.tv_sec * ODP_TIME_SEC_IN_NS) + diff.tv_nsec;
-
- return nsec;
-}
-
-static inline odp_time_t time_spec_cur(void)
-{
- int ret;
- odp_time_t time;
- struct timespec sys_time;
-
- ret = clock_gettime(CLOCK_MONOTONIC_RAW, &sys_time);
- if (odp_unlikely(ret != 0))
- ODP_ABORT("clock_gettime failed\n");
-
- time.nsec = time_spec_diff_nsec(&sys_time, &global.spec_start);
-
- return time;
-}
-
-static inline uint64_t time_spec_res(void)
-{
- int ret;
- struct timespec tres;
-
- ret = clock_getres(CLOCK_MONOTONIC_RAW, &tres);
- if (odp_unlikely(ret != 0))
- ODP_ABORT("clock_getres failed\n");
-
- return ODP_TIME_SEC_IN_NS / (uint64_t)tres.tv_nsec;
-}
-
-static inline uint64_t time_spec_to_ns(odp_time_t time)
-{
- return time.nsec;
-}
-
-static inline odp_time_t time_spec_from_ns(uint64_t ns)
-{
- odp_time_t time;
-
- time.nsec = ns;
-
- return time;
-}
-
-/*
- * HW time counter based functions
- */
-
-static inline odp_time_t time_hw_cur(void)
-{
- odp_time_t time;
-
- time.count = cpu_global_time() - global.hw_start;
-
- return time;
-}
-
-static inline uint64_t time_hw_res(void)
-{
- return global.hw_freq_hz;
-}
-
-static inline uint64_t time_hw_to_ns(odp_time_t time)
-{
- uint64_t nsec;
- uint64_t freq_hz = global.hw_freq_hz;
- uint64_t count = time.count;
- uint64_t sec = 0;
-
- if (count >= freq_hz) {
- sec = count / freq_hz;
- count = count - sec * freq_hz;
- }
-
- nsec = (ODP_TIME_SEC_IN_NS * count) / freq_hz;
-
- return (sec * ODP_TIME_SEC_IN_NS) + nsec;
-}
-
-static inline odp_time_t time_hw_from_ns(uint64_t ns)
-{
- odp_time_t time;
- uint64_t count;
- uint64_t freq_hz = global.hw_freq_hz;
- uint64_t sec = 0;
-
- if (ns >= ODP_TIME_SEC_IN_NS) {
- sec = ns / ODP_TIME_SEC_IN_NS;
- ns = ns - sec * ODP_TIME_SEC_IN_NS;
- }
-
- count = sec * freq_hz;
- count += (ns * freq_hz) / ODP_TIME_SEC_IN_NS;
-
- time.count = count;
-
- return time;
-}
-
-/*
- * Common functions
- */
-
-static inline odp_time_t time_cur(void)
-{
- if (global.use_hw)
- return time_hw_cur();
-
- return time_spec_cur();
-}
-
-static inline uint64_t time_res(void)
-{
- if (global.use_hw)
- return time_hw_res();
-
- return time_spec_res();
-}
-
-static inline int time_cmp(odp_time_t t2, odp_time_t t1)
-{
- if (odp_likely(t2.u64 > t1.u64))
- return 1;
-
- if (t2.u64 < t1.u64)
- return -1;
-
- return 0;
-}
-
-static inline odp_time_t time_sum(odp_time_t t1, odp_time_t t2)
-{
- odp_time_t time;
-
- time.u64 = t1.u64 + t2.u64;
-
- return time;
-}
-
-static inline uint64_t time_to_ns(odp_time_t time)
-{
- if (global.use_hw)
- return time_hw_to_ns(time);
-
- return time_spec_to_ns(time);
-}
-
-static inline odp_time_t time_from_ns(uint64_t ns)
-{
- if (global.use_hw)
- return time_hw_from_ns(ns);
-
- return time_spec_from_ns(ns);
-}
-
-static inline void time_wait_until(odp_time_t time)
-{
- odp_time_t cur;
-
- do {
- cur = time_cur();
- } while (time_cmp(time, cur) > 0);
-}
-
-odp_time_t odp_time_local(void)
-{
- return time_cur();
-}
-
-odp_time_t odp_time_global(void)
-{
- return time_cur();
-}
-
-odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
-{
- odp_time_t time;
-
- time.u64 = t2.u64 - t1.u64;
-
- return time;
-}
-
-uint64_t odp_time_to_ns(odp_time_t time)
-{
- return time_to_ns(time);
-}
-
-odp_time_t odp_time_local_from_ns(uint64_t ns)
-{
- return time_from_ns(ns);
-}
-
-odp_time_t odp_time_global_from_ns(uint64_t ns)
-{
- return time_from_ns(ns);
-}
-
-int odp_time_cmp(odp_time_t t2, odp_time_t t1)
-{
- return time_cmp(t2, t1);
-}
-
-odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2)
-{
- return time_sum(t1, t2);
-}
-
-uint64_t odp_time_local_res(void)
-{
- return time_res();
-}
-
-uint64_t odp_time_global_res(void)
-{
- return time_res();
-}
-
-void odp_time_wait_ns(uint64_t ns)
-{
- odp_time_t cur = time_cur();
- odp_time_t wait = time_from_ns(ns);
- odp_time_t end_time = time_sum(cur, wait);
-
- time_wait_until(end_time);
-}
-
-void odp_time_wait_until(odp_time_t time)
-{
- return time_wait_until(time);
-}
-
-int odp_time_init_global(void)
-{
- int ret = 0;
-
- memset(&global, 0, sizeof(time_global_t));
-
- if (cpu_has_global_time()) {
- global.use_hw = 1;
- global.hw_freq_hz = cpu_global_time_freq();
-
- if (global.hw_freq_hz == 0)
- return -1;
-
- printf("HW time counter freq: %" PRIu64 " hz\n\n",
- global.hw_freq_hz);
-
- global.hw_start = cpu_global_time();
- return 0;
- }
-
- global.spec_start.tv_sec = 0;
- global.spec_start.tv_nsec = 0;
-
- ret = clock_gettime(CLOCK_MONOTONIC_RAW, &global.spec_start);
-
- return ret;
-}
-
-int odp_time_term_global(void)
-{
- return 0;
-}
diff --git a/platform/linux-generic/odp_time_api.c b/platform/linux-generic/odp_time_api.c
new file mode 100644
index 000000000..1aca5eb8c
--- /dev/null
+++ b/platform/linux-generic/odp_time_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp/api/time.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/time_inlines.h>
diff --git a/platform/linux-generic/odp_timer.c b/platform/linux-generic/odp_timer.c
index cf610bfa0..5d29dd06d 100644
--- a/platform/linux-generic/odp_timer.c
+++ b/platform/linux-generic/odp_timer.c
@@ -1,7 +1,6 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
/**
@@ -10,49 +9,65 @@
* ODP timer service
*
*/
-
-#if __SIZEOF_POINTER__ != 8
-/* TB_NEEDS_PAD defined if sizeof(odp_buffer_t) != 8 */
-#define TB_NEEDS_PAD
-#define TB_SET_PAD(x) ((x).pad = 0)
-#else
-#define TB_SET_PAD(x) (void)(x)
-#endif
-
#include <odp_posix_extensions.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <time.h>
-#include <signal.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <inttypes.h>
-#include <string.h>
-
#include <odp/api/align.h>
-#include <odp_align_internal.h>
#include <odp/api/atomic.h>
-#include <odp_atomic_internal.h>
-#include <odp/api/buffer.h>
-#include <odp_buffer_inlines.h>
#include <odp/api/cpu.h>
-#include <odp/api/pool.h>
-#include <odp_pool_internal.h>
#include <odp/api/debug.h>
-#include <odp_debug_internal.h>
#include <odp/api/event.h>
#include <odp/api/hints.h>
-#include <odp_internal.h>
+#include <odp/api/pool.h>
#include <odp/api/queue.h>
#include <odp/api/shared_memory.h>
#include <odp/api/spinlock.h>
-#include <odp/api/std_types.h>
+#include <odp/api/std.h>
#include <odp/api/sync.h>
#include <odp/api/time.h>
#include <odp/api/timer.h>
+
+/* Inlined API functions */
+#include <odp/api/plat/atomic_inlines.h>
+#include <odp/api/plat/event_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+#include <odp/api/plat/time_inlines.h>
+#include <odp/api/plat/timer_inlines.h>
+
+#include <odp/api/plat/timer_inline_types.h>
+
+#include <odp_atomic_internal.h>
+#include <odp_config_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_event_internal.h>
+#include <odp_global_data.h>
+#include <odp_init_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_string_internal.h>
+#include <odp_queue_if.h>
#include <odp_timer_internal.h>
+#include <odp_types_internal.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+/* Check whether 128-bit atomics should be used */
+#if defined(ODP_ATOMIC_U128) && CONFIG_TIMER_128BIT_ATOMICS
+#define USE_128BIT_ATOMICS 1
+#else
+#define USE_128BIT_ATOMICS 0
+#endif
+
+/* One divided by one nanosecond in Hz */
+#define GIGA_HZ 1000000000
#define TMO_UNUSED ((uint64_t)0xFFFFFFFFFFFFFFFF)
/* TMO_INACTIVE is or-ed with the expiration tick to indicate an expired timer.
@@ -60,117 +75,103 @@
* for checking the freshness of received timeouts */
#define TMO_INACTIVE ((uint64_t)0x8000000000000000)
-/******************************************************************************
- * Mutual exclusion in the absence of CAS16
- *****************************************************************************/
+/* Flag set into periodic tick value when periodic timer cancel did not succeed.
+ * Ack call checks this. */
+#define PERIODIC_CANCELLED TMO_INACTIVE
-#ifndef ODP_ATOMIC_U128
-#define NUM_LOCKS 1024
-static _odp_atomic_flag_t locks[NUM_LOCKS]; /* Multiple locks per cache line! */
-#define IDX2LOCK(idx) (&locks[(idx) % NUM_LOCKS])
-#endif
+/* Max timeout in capability. One year in nsec (0x0070 09D3 2DA3 0000). */
+#define MAX_TMO_NSEC (365 * 24 * ODP_TIME_HOUR_IN_NS)
-/******************************************************************************
- * Translation between timeout buffer and timeout header
- *****************************************************************************/
+/* Max inline timer resolution */
+#define MAX_INLINE_RES_NS 500
-static odp_timeout_hdr_t *timeout_hdr_from_buf(odp_buffer_t buf)
-{
- return (odp_timeout_hdr_t *)(void *)buf_hdl_to_hdr(buf);
-}
+/* Timer pool may be reused after this period */
+#define TIMER_POOL_REUSE_NS ODP_TIME_SEC_IN_NS
-static odp_timeout_hdr_t *timeout_hdr(odp_timeout_t tmo)
-{
- odp_buffer_t buf = odp_buffer_from_event(odp_timeout_to_event(tmo));
- return timeout_hdr_from_buf(buf);
-}
+/* Minimum periodic timer base frequency */
+#define MIN_BASE_HZ 1
-/******************************************************************************
- * odp_timer abstract datatype
- *****************************************************************************/
+/* Maximum periodic timer multiplier */
+#define MAX_MULTIPLIER 1000000
-typedef struct tick_buf_s {
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- /* No atomics support for 64-bit variables, will use separate lock */
- /* Use the same layout as odp_atomic_u64_t but without lock variable */
- struct {
- uint64_t v;
- } exp_tck;/* Expiration tick or TMO_xxx */
-#else
- odp_atomic_u64_t exp_tck;/* Expiration tick or TMO_xxx */
-#endif
- odp_buffer_t tmo_buf;/* ODP_BUFFER_INVALID if timer not active */
-#ifdef TB_NEEDS_PAD
- uint32_t pad;/* Need to be able to access padding for successful CAS */
+/* Maximum number of periodic timers per pool */
+#define MAX_PERIODIC_TIMERS 100
+
+/* Mutual exclusion in the absence of CAS16 */
+#if !USE_128BIT_ATOMICS
+#define NUM_LOCKS 256
+#define IDX2LOCK(tp, idx) (&(tp)->locks[(idx) % NUM_LOCKS])
#endif
-} tick_buf_t
-#ifdef ODP_ATOMIC_U128
+
+#define ACC_SIZE (1ull << 32)
+
+#include <odp/visibility_begin.h>
+
+/* Fill in timeout header field offsets for inline functions */
+const _odp_timeout_inline_offset_t
+_odp_timeout_inline_offset ODP_ALIGNED_CACHE = {
+ .expiration = offsetof(odp_timeout_hdr_t, expiration),
+ .timer = offsetof(odp_timeout_hdr_t, timer),
+ .user_ptr = offsetof(odp_timeout_hdr_t, user_ptr),
+ .uarea_addr = offsetof(odp_timeout_hdr_t, uarea_addr),
+};
+
+#include <odp/visibility_end.h>
+
+typedef union
+#if USE_128BIT_ATOMICS
ODP_ALIGNED(16) /* 16-byte atomic operations need properly aligned addresses */
#endif
-;
+tick_buf_s {
+#if USE_128BIT_ATOMICS
+ odp_atomic_u128_t tb_atomic_u128;
-#if __GCC_ATOMIC_LLONG_LOCK_FREE >= 2
-/* Only assert this when we perform atomic operations on tick_buf_t */
-ODP_STATIC_ASSERT(sizeof(tick_buf_t) == 16, "sizeof(tick_buf_t) == 16");
+ odp_u128_t tb_u128;
#endif
-typedef struct odp_timer_s {
- void *user_ptr;
- odp_queue_t queue;/* Used for free list when timer is free */
-} odp_timer;
-
-static void timer_init(odp_timer *tim,
- tick_buf_t *tb,
- odp_queue_t _q,
- void *_up)
-{
- tim->queue = _q;
- tim->user_ptr = _up;
- tb->tmo_buf = ODP_BUFFER_INVALID;
- /* All pad fields need a defined and constant value */
- TB_SET_PAD(*tb);
- /* Release the timer by setting timer state to inactive */
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- tb->exp_tck.v = TMO_INACTIVE;
-#else
- _odp_atomic_u64_store_mm(&tb->exp_tck, TMO_INACTIVE, _ODP_MEMMODEL_RLS);
+ struct {
+ /* Expiration tick or TMO_xxx */
+ odp_atomic_u64_t exp_tck;
+ union {
+ /* ODP_EVENT_INVALID if timer not active */
+ odp_event_t tmo_event;
+
+ /* Ensures that tick_buf_t is 128 bits */
+ uint64_t tmo_u64;
+ };
+ };
+} tick_buf_t;
+
+#if USE_128BIT_ATOMICS
+ODP_STATIC_ASSERT(sizeof(tick_buf_t) == 16, "sizeof(tick_buf_t) == 16");
#endif
-}
-/* Teardown when timer is freed */
-static void timer_fini(odp_timer *tim, tick_buf_t *tb)
-{
- ODP_ASSERT(tb->exp_tck.v == TMO_UNUSED);
- ODP_ASSERT(tb->tmo_buf == ODP_BUFFER_INVALID);
- tim->queue = ODP_QUEUE_INVALID;
- tim->user_ptr = NULL;
-}
+typedef struct {
+ const void *user_ptr;
+ odp_queue_t queue;
-static inline uint32_t get_next_free(odp_timer *tim)
-{
- /* Reusing 'queue' for next free index */
- return _odp_typeval(tim->queue);
-}
+ /* Period of periodic timer in ticks (nanoseconds), includes PERIODIC_CANCELLED flag. */
+ uint64_t periodic_ticks;
-static inline void set_next_free(odp_timer *tim, uint32_t nf)
-{
- ODP_ASSERT(tim->queue == ODP_QUEUE_INVALID);
- /* Reusing 'queue' for next free index */
- tim->queue = _odp_cast_scalar(odp_queue_t, nf);
-}
+ /* Periodic ticks fractional part. */
+ uint32_t periodic_ticks_frac;
-/******************************************************************************
- * odp_timer_pool abstract datatype
- * Inludes alloc and free timer
- *****************************************************************************/
+ /* Periodic ticks fractional part accumulator. */
+ uint32_t periodic_ticks_frac_acc;
+
+ /* Used for free list of timers */
+ uint32_t next_free;
-typedef struct odp_timer_pool_s {
-/* Put frequently accessed fields in the first cache line */
+} _odp_timer_t;
+
+typedef struct timer_pool_s {
+ /* Put frequently accessed fields in the first cache line */
+ uint64_t nsec_per_scan;
odp_atomic_u64_t cur_tick;/* Current tick value */
uint64_t min_rel_tck;
uint64_t max_rel_tck;
tick_buf_t *tick_buf; /* Expiration tick and timeout buffer */
- odp_timer *timers; /* User pointer and queue handle (and lock) */
+ _odp_timer_t *timers; /* User pointer and queue handle (and lock) */
odp_atomic_u32_t high_wm;/* High watermark of allocated timers */
odp_spinlock_t lock;
uint32_t num_alloc;/* Current number of allocated timers */
@@ -178,107 +179,146 @@ typedef struct odp_timer_pool_s {
uint32_t tp_idx;/* Index into timer_pool array */
odp_timer_pool_param_t param;
char name[ODP_TIMER_POOL_NAME_LEN];
- odp_shm_t shm;
timer_t timerid;
- int notify_overrun;
- pthread_t timer_thread; /* pthread_t of timer thread */
- pid_t timer_thread_id; /* gettid() for timer thread */
- int timer_thread_exit; /* request to exit for timer thread */
-} odp_timer_pool;
+ /*
+ * Timer pool overrun notification (debug print). Initialize to 0
+ * (don't notify). When value is 0 and a timer is started, set to 1
+ * (notify). When notification is done, set to 2 (don't notify).
+ */
+ odp_atomic_u32_t notify_overrun;
+ int owner;
+ pthread_t thr_pthread; /* pthread_t of timer thread */
+ odp_atomic_u64_t thr_pid; /* gettid() for timer thread */
+ int thr_warm_up; /* number of warm up rounds */
+ odp_atomic_u32_t thr_ready; /* thread ready from warm up */
+ odp_atomic_u32_t thr_exit; /* request to exit for timer thread */
+ double base_freq;
+ uint64_t max_multiplier;
+ uint8_t periodic;
+#if !USE_128BIT_ATOMICS
+ /* Multiple locks per cache line! */
+ _odp_atomic_flag_t locks[NUM_LOCKS] ODP_ALIGNED_CACHE;
+#endif
+
+} timer_pool_t;
-#define MAX_TIMER_POOLS 255 /* Leave one for ODP_TIMER_INVALID */
+/* Timer pool index must fit into 8 bits with one index value reserved to
+ * ODP_TIMER_POOL_INVALID. */
+#define MAX_TIMER_POOLS 32
#define INDEX_BITS 24
-static odp_atomic_u32_t num_timer_pools;
-static odp_timer_pool *timer_pool[MAX_TIMER_POOLS];
+#define TIMER_RES_TEST_LOOP_COUNT 10
+#define TIMER_RES_ROUNDUP_FACTOR 10
-static inline odp_timer_pool *handle_to_tp(odp_timer_t hdl)
+typedef struct timer_global_t {
+ odp_ticketlock_t lock;
+ odp_shm_t shm;
+ /* Max timer resolution in nanoseconds */
+ uint64_t highest_res_ns;
+ uint64_t highest_res_hz;
+ uint64_t max_base_hz;
+ uint64_t poll_interval_nsec;
+ int num_timer_pools;
+ uint8_t timer_pool_used[MAX_TIMER_POOLS];
+ odp_time_t destroy_time[MAX_TIMER_POOLS];
+ odp_shm_t tp_shm[MAX_TIMER_POOLS];
+ timer_pool_t *timer_pool[MAX_TIMER_POOLS];
+
+ /* These are read frequently from inline timer */
+ odp_time_t poll_interval_time;
+ odp_bool_t use_inline_timers;
+ int poll_interval;
+ int highest_tp_idx;
+ uint8_t thread_type;
+
+} timer_global_t;
+
+typedef struct timer_local_t {
+ odp_time_t last_run;
+ int run_cnt;
+ uint8_t poll_shared;
+ uint64_t prof_nsec;
+ uint64_t prof_rounds;
+} timer_local_t;
+
+/* Points to timer global data */
+static timer_global_t *timer_global;
+
+/* Timer thread local data */
+static __thread timer_local_t timer_local;
+
+static inline void set_next_free(_odp_timer_t *tim, uint32_t nf)
+{
+ _ODP_ASSERT(tim->queue == ODP_QUEUE_INVALID);
+
+ tim->next_free = nf;
+}
+
+static inline timer_pool_t *timer_pool_from_hdl(odp_timer_pool_t hdl)
+{
+ return (timer_pool_t *)(uintptr_t)hdl;
+}
+
+static inline odp_timer_pool_t timer_pool_to_hdl(timer_pool_t *tp)
+{
+ return (odp_timer_pool_t)tp;
+}
+
+static inline timer_pool_t *handle_to_tp(odp_timer_t hdl)
{
uint32_t tp_idx = _odp_typeval(hdl) >> INDEX_BITS;
- if (odp_likely(tp_idx < MAX_TIMER_POOLS)) {
- odp_timer_pool *tp = timer_pool[tp_idx];
- if (odp_likely(tp != NULL))
- return timer_pool[tp_idx];
- }
- ODP_ABORT("Invalid timer handle %#x\n", hdl);
+ timer_pool_t *tp;
+
+ _ODP_ASSERT(tp_idx < MAX_TIMER_POOLS);
+
+ tp = timer_global->timer_pool[tp_idx];
+
+ _ODP_ASSERT(tp != NULL);
+
+ return tp;
}
static inline uint32_t handle_to_idx(odp_timer_t hdl,
- struct odp_timer_pool_s *tp)
+ timer_pool_t *tp)
{
- uint32_t idx = _odp_typeval(hdl) & ((1U << INDEX_BITS) - 1U);
+ uint32_t idx = (_odp_typeval(hdl) & ((1U << INDEX_BITS) - 1U)) - 1;
+
+ _ODP_ASSERT(idx < odp_atomic_load_u32(&tp->high_wm));
+
__builtin_prefetch(&tp->tick_buf[idx], 0, 0);
- if (odp_likely(idx < odp_atomic_load_u32(&tp->high_wm)))
- return idx;
- ODP_ABORT("Invalid timer handle %#x\n", hdl);
+
+ return idx;
}
-static inline odp_timer_t tp_idx_to_handle(struct odp_timer_pool_s *tp,
- uint32_t idx)
+static inline odp_timer_t tp_idx_to_handle(timer_pool_t *tp,
+ uint32_t idx)
{
- ODP_ASSERT(idx < (1U << INDEX_BITS));
- return _odp_cast_scalar(odp_timer_t, (tp->tp_idx << INDEX_BITS) | idx);
+ _ODP_ASSERT((idx + 1) < (1U << INDEX_BITS));
+ return _odp_cast_scalar(odp_timer_t, (tp->tp_idx << INDEX_BITS) |
+ (idx + 1));
}
-/* Forward declarations */
-static void itimer_init(odp_timer_pool *tp);
-static void itimer_fini(odp_timer_pool *tp);
+static inline odp_timeout_hdr_t *timeout_hdr_from_event(odp_event_t event)
+{
+ return (odp_timeout_hdr_t *)(uintptr_t)event;
+}
-static odp_timer_pool_t odp_timer_pool_new(const char *name,
- const odp_timer_pool_param_t *param)
+static inline odp_timeout_hdr_t *timeout_hdr(odp_timeout_t tmo)
{
- uint32_t tp_idx = odp_atomic_fetch_add_u32(&num_timer_pools, 1);
- if (odp_unlikely(tp_idx >= MAX_TIMER_POOLS)) {
- /* Restore the previous value */
- odp_atomic_sub_u32(&num_timer_pools, 1);
- __odp_errno = ENFILE; /* Table overflow */
- return ODP_TIMER_POOL_INVALID;
- }
- size_t sz0 = ROUNDUP_CACHE_LINE(sizeof(odp_timer_pool));
- size_t sz1 = ROUNDUP_CACHE_LINE(sizeof(tick_buf_t) * param->num_timers);
- size_t sz2 = ROUNDUP_CACHE_LINE(sizeof(odp_timer) * param->num_timers);
- odp_shm_t shm = odp_shm_reserve(name, sz0 + sz1 + sz2,
- ODP_CACHE_LINE_SIZE, ODP_SHM_SW_ONLY);
- if (odp_unlikely(shm == ODP_SHM_INVALID))
- ODP_ABORT("%s: timer pool shm-alloc(%zuKB) failed\n",
- name, (sz0 + sz1 + sz2) / 1024);
- odp_timer_pool *tp = (odp_timer_pool *)odp_shm_addr(shm);
- odp_atomic_init_u64(&tp->cur_tick, 0);
+ return (odp_timeout_hdr_t *)(uintptr_t)tmo;
+}
- if (name == NULL) {
- tp->name[0] = 0;
- } else {
- strncpy(tp->name, name, ODP_TIMER_POOL_NAME_LEN - 1);
- tp->name[ODP_TIMER_POOL_NAME_LEN - 1] = 0;
- }
- tp->shm = shm;
- tp->param = *param;
- tp->min_rel_tck = odp_timer_ns_to_tick(tp, param->min_tmo);
- tp->max_rel_tck = odp_timer_ns_to_tick(tp, param->max_tmo);
- tp->num_alloc = 0;
- odp_atomic_init_u32(&tp->high_wm, 0);
- tp->first_free = 0;
- tp->notify_overrun = 1;
- tp->tick_buf = (void *)((char *)odp_shm_addr(shm) + sz0);
- tp->timers = (void *)((char *)odp_shm_addr(shm) + sz0 + sz1);
- /* Initialize all odp_timer entries */
- uint32_t i;
- for (i = 0; i < tp->param.num_timers; i++) {
- tp->timers[i].queue = ODP_QUEUE_INVALID;
- set_next_free(&tp->timers[i], i + 1);
- tp->timers[i].user_ptr = NULL;
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- tp->tick_buf[i].exp_tck.v = TMO_UNUSED;
-#else
- odp_atomic_init_u64(&tp->tick_buf[i].exp_tck, TMO_UNUSED);
-#endif
- tp->tick_buf[i].tmo_buf = ODP_BUFFER_INVALID;
- }
- tp->tp_idx = tp_idx;
- odp_spinlock_init(&tp->lock);
- timer_pool[tp_idx] = tp;
- if (tp->param.clk_src == ODP_CLOCK_CPU)
- itimer_init(tp);
- return tp;
+static uint64_t max_multiplier_capa(double freq)
+{
+ uint64_t mult;
+
+ if (freq < MIN_BASE_HZ)
+ return 0;
+
+ mult = timer_global->max_base_hz / freq;
+ if (mult > MAX_MULTIPLIER)
+ mult = MAX_MULTIPLIER;
+
+ return mult;
}
static void block_sigalarm(void)
@@ -290,384 +330,568 @@ static void block_sigalarm(void)
sigprocmask(SIG_BLOCK, &sigset, NULL);
}
-static void stop_timer_thread(odp_timer_pool *tp)
+static void posix_timer_stop(timer_pool_t *tp)
{
int ret;
- ODP_DBG("stop\n");
- tp->timer_thread_exit = 1;
- ret = pthread_join(tp->timer_thread, NULL);
+ /* Stop POSIX timer signals */
+ if (timer_delete(tp->timerid) != 0)
+ _ODP_ABORT("timer_delete() returned error: %s\n", strerror(errno));
+
+ /* Stop the thread */
+ _ODP_DBG("stop\n");
+ odp_atomic_store_u32(&tp->thr_exit, 1);
+ ret = pthread_join(tp->thr_pthread, NULL);
if (ret != 0)
- ODP_ABORT("unable to join thread, err %d\n", ret);
+ _ODP_ABORT("Unable to join thread, err %d\n", ret);
}
-static void odp_timer_pool_del(odp_timer_pool *tp)
+static void odp_timer_pool_del(timer_pool_t *tp)
{
- odp_spinlock_lock(&tp->lock);
- timer_pool[tp->tp_idx] = NULL;
+ int highest;
+ uint32_t tp_idx = tp->tp_idx;
- /* Stop timer triggering */
- if (tp->param.clk_src == ODP_CLOCK_CPU)
- itimer_fini(tp);
+ odp_spinlock_lock(&tp->lock);
- stop_timer_thread(tp);
+ if (!odp_global_rw->inline_timers)
+ posix_timer_stop(tp);
if (tp->num_alloc != 0) {
/* It's a programming error to attempt to destroy a */
/* timer pool which is still in use */
- ODP_ABORT("%s: timers in use\n", tp->name);
+ odp_spinlock_unlock(&tp->lock);
+ _ODP_ABORT("%s: timers in use\n", tp->name);
+ }
+
+ odp_spinlock_unlock(&tp->lock);
+
+ odp_ticketlock_lock(&timer_global->lock);
+ timer_global->timer_pool[tp_idx] = NULL;
+ timer_global->timer_pool_used[tp_idx] = 0;
+ timer_global->num_timer_pools--;
+ timer_global->destroy_time[tp_idx] = odp_time_global();
+
+ highest = -1;
+
+ /* Disable inline timer polling */
+ if (timer_global->num_timer_pools == 0) {
+ odp_global_rw->inline_timers = false;
+ } else {
+ int i;
+
+ for (i = 0; i < MAX_TIMER_POOLS; i++)
+ if (timer_global->timer_pool_used[i])
+ highest = i;
}
- int rc = odp_shm_free(tp->shm);
- if (rc != 0)
- ODP_ABORT("Failed to free shared memory (%d)\n", rc);
- odp_atomic_sub_u32(&num_timer_pools, 1);
+ timer_global->highest_tp_idx = highest;
+
+ odp_ticketlock_unlock(&timer_global->lock);
}
-static inline odp_timer_t timer_alloc(odp_timer_pool *tp,
- odp_queue_t queue,
- void *user_ptr)
+static inline odp_timer_t timer_alloc(timer_pool_t *tp, odp_queue_t queue, const void *user_ptr)
{
odp_timer_t hdl;
+
odp_spinlock_lock(&tp->lock);
if (odp_likely(tp->num_alloc < tp->param.num_timers)) {
tp->num_alloc++;
/* Remove first unused timer from free list */
- ODP_ASSERT(tp->first_free != tp->param.num_timers);
+ _ODP_ASSERT(tp->first_free != tp->param.num_timers);
uint32_t idx = tp->first_free;
- odp_timer *tim = &tp->timers[idx];
- tp->first_free = get_next_free(tim);
- /* Initialize timer */
- timer_init(tim, &tp->tick_buf[idx], queue, user_ptr);
- if (odp_unlikely(tp->num_alloc >
- odp_atomic_load_u32(&tp->high_wm)))
+ _odp_timer_t *tim = &tp->timers[idx];
+ tick_buf_t *tb = &tp->tick_buf[idx];
+
+ tp->first_free = tim->next_free;
+ tim->queue = queue;
+ tim->user_ptr = user_ptr;
+ tb->tmo_u64 = 0;
+ tb->tmo_event = ODP_EVENT_INVALID;
+
+ /* Release the timer by setting timer state to inactive */
+ odp_atomic_store_rel_u64(&tb->exp_tck, TMO_INACTIVE);
+
+
+ if (odp_unlikely(tp->num_alloc > odp_atomic_load_u32(&tp->high_wm))) {
/* Update high_wm last with release model to
* ensure timer initialization is visible */
- _odp_atomic_u32_store_mm(&tp->high_wm,
- tp->num_alloc,
- _ODP_MEMMODEL_RLS);
+ odp_atomic_store_rel_u32(&tp->high_wm, tp->num_alloc);
+ }
+
hdl = tp_idx_to_handle(tp, idx);
+ /* Add timer to queue */
+ _odp_queue_fn->timer_add(queue);
} else {
- __odp_errno = ENFILE; /* Reusing file table overflow */
+ /* Reusing file table overflow */
hdl = ODP_TIMER_INVALID;
}
odp_spinlock_unlock(&tp->lock);
return hdl;
}
-static odp_buffer_t timer_cancel(odp_timer_pool *tp,
- uint32_t idx,
- uint64_t new_state);
-
-static inline odp_buffer_t timer_free(odp_timer_pool *tp, uint32_t idx)
-{
- odp_timer *tim = &tp->timers[idx];
-
- /* Free the timer by setting timer state to unused and
- * grab any timeout buffer */
- odp_buffer_t old_buf = timer_cancel(tp, idx, TMO_UNUSED);
-
- /* Destroy timer */
- timer_fini(tim, &tp->tick_buf[idx]);
-
- /* Insert timer into free list */
- odp_spinlock_lock(&tp->lock);
- set_next_free(tim, tp->first_free);
- tp->first_free = idx;
- ODP_ASSERT(tp->num_alloc != 0);
- tp->num_alloc--;
- odp_spinlock_unlock(&tp->lock);
-
- return old_buf;
-}
-
-/******************************************************************************
- * Operations on timers
- * expire/reset/cancel timer
- *****************************************************************************/
-
-static bool timer_reset(uint32_t idx,
- uint64_t abs_tck,
- odp_buffer_t *tmo_buf,
- odp_timer_pool *tp)
+static bool timer_reset(uint32_t idx, uint64_t abs_tck, odp_event_t *tmo_event,
+ timer_pool_t *tp)
{
bool success = true;
tick_buf_t *tb = &tp->tick_buf[idx];
- if (tmo_buf == NULL || *tmo_buf == ODP_BUFFER_INVALID) {
-#ifdef ODP_ATOMIC_U128 /* Target supports 128-bit atomic operations */
+ if (tmo_event == NULL || *tmo_event == ODP_EVENT_INVALID) {
+#if USE_128BIT_ATOMICS /* Target supports 128-bit atomic operations */
tick_buf_t new, old;
+
+ /* Init all bits, also when tmo_event is less than 64 bits */
+ new.tmo_u64 = 0;
+ old.tmo_u64 = 0;
+
+ /* Relaxed and non-atomic read of current values */
+ old.exp_tck.v = tb->exp_tck.v;
+ old.tmo_event = tb->tmo_event;
+
do {
- /* Relaxed and non-atomic read of current values */
- old.exp_tck.v = tb->exp_tck.v;
- old.tmo_buf = tb->tmo_buf;
- TB_SET_PAD(old);
- /* Check if there actually is a timeout buffer
+ /* Check if there actually is a timeout event
* present */
- if (old.tmo_buf == ODP_BUFFER_INVALID) {
+ if (old.tmo_event == ODP_EVENT_INVALID) {
/* Cannot reset a timer with neither old nor
- * new timeout buffer */
+ * new timeout event */
success = false;
break;
}
/* Set up new values */
new.exp_tck.v = abs_tck;
- new.tmo_buf = old.tmo_buf;
- TB_SET_PAD(new);
+ new.tmo_event = old.tmo_event;
+
/* Atomic CAS will fail if we experienced torn reads,
* retry update sequence until CAS succeeds */
- } while (!_odp_atomic_u128_cmp_xchg_mm(
- (_odp_atomic_u128_t *)tb,
- (_uint128_t *)&old,
- (_uint128_t *)&new,
- _ODP_MEMMODEL_RLS,
- _ODP_MEMMODEL_RLX));
-#elif __GCC_ATOMIC_LLONG_LOCK_FREE >= 2 && \
- defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
- /* Target supports lock-free 64-bit CAS (and probably exchange) */
- /* Since locks/barriers are not good for C-A15, we take an
- * alternative approach using relaxed memory model */
- uint64_t old;
- /* Swap in new expiration tick, get back old tick which
- * will indicate active/inactive timer state */
- old = _odp_atomic_u64_xchg_mm(&tb->exp_tck, abs_tck,
- _ODP_MEMMODEL_RLX);
- if ((old & TMO_INACTIVE) != 0) {
- /* Timer was inactive (cancelled or expired),
- * we can't reset a timer without a timeout buffer.
- * Attempt to restore inactive state, we don't
- * want this timer to continue as active without
- * timeout as this will trigger unnecessary and
- * aborted expiration attempts.
- * We don't care if we fail, then some other thread
- * reset or cancelled the timer. Without any
- * synchronization between the threads, we have a
- * data race and the behavior is undefined */
- (void)_odp_atomic_u64_cmp_xchg_strong_mm(
- &tb->exp_tck,
- &abs_tck,
- old,
- _ODP_MEMMODEL_RLX,
- _ODP_MEMMODEL_RLX);
- success = false;
- }
-#else /* Target supports neither 128-bit nor 64-bit CAS => use lock */
+ } while (!odp_atomic_cas_rel_u128(&tb->tb_atomic_u128,
+ &old.tb_u128, new.tb_u128));
+#else
/* Take a related lock */
- while (_odp_atomic_flag_tas(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_tas(IDX2LOCK(tp, idx)))
/* While lock is taken, spin using relaxed loads */
- while (_odp_atomic_flag_load(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_load(IDX2LOCK(tp, idx)))
odp_cpu_pause();
- /* Only if there is a timeout buffer can be reset the timer */
- if (odp_likely(tb->tmo_buf != ODP_BUFFER_INVALID)) {
+ /* Only if there is a timeout event can the timer be reset */
+ if (odp_likely(tb->tmo_event != ODP_EVENT_INVALID)) {
/* Write the new expiration tick */
tb->exp_tck.v = abs_tck;
} else {
/* Cannot reset a timer with neither old nor new
- * timeout buffer */
+ * timeout event */
success = false;
}
/* Release the lock */
- _odp_atomic_flag_clear(IDX2LOCK(idx));
+ _odp_atomic_flag_clear(IDX2LOCK(tp, idx));
#endif
} else {
- /* We have a new timeout buffer which replaces any old one */
+ /* We have a new timeout event which replaces any old one */
/* Fill in some (constant) header fields for timeout events */
- if (odp_event_type(odp_buffer_to_event(*tmo_buf)) ==
- ODP_EVENT_TIMEOUT) {
- /* Convert from buffer to timeout hdr */
+ if (odp_event_type(*tmo_event) == ODP_EVENT_TIMEOUT) {
+ /* Convert from event to timeout hdr */
odp_timeout_hdr_t *tmo_hdr =
- timeout_hdr_from_buf(*tmo_buf);
+ timeout_hdr_from_event(*tmo_event);
tmo_hdr->timer = tp_idx_to_handle(tp, idx);
tmo_hdr->user_ptr = tp->timers[idx].user_ptr;
/* expiration field filled in when timer expires */
}
- /* Else ignore buffers of other types */
- odp_buffer_t old_buf = ODP_BUFFER_INVALID;
-#ifdef ODP_ATOMIC_U128
+ /* Else ignore events of other types */
+ odp_event_t old_event = ODP_EVENT_INVALID;
+#if USE_128BIT_ATOMICS
tick_buf_t new, old;
+
+ /* Init all bits, also when tmo_event is less than 64 bits */
+ new.tmo_u64 = 0;
+
new.exp_tck.v = abs_tck;
- new.tmo_buf = *tmo_buf;
- TB_SET_PAD(new);
- /* We are releasing the new timeout buffer to some other
+ new.tmo_event = *tmo_event;
+
+ /* We are releasing the new timeout event to some other
* thread */
_odp_atomic_u128_xchg_mm((_odp_atomic_u128_t *)tb,
- (_uint128_t *)&new,
- (_uint128_t *)&old,
+ (_odp_u128_t *)&new,
+ (_odp_u128_t *)&old,
_ODP_MEMMODEL_ACQ_RLS);
- old_buf = old.tmo_buf;
+ old_event = old.tmo_event;
#else
/* Take a related lock */
- while (_odp_atomic_flag_tas(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_tas(IDX2LOCK(tp, idx)))
/* While lock is taken, spin using relaxed loads */
- while (_odp_atomic_flag_load(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_load(IDX2LOCK(tp, idx)))
odp_cpu_pause();
- /* Swap in new buffer, save any old buffer */
- old_buf = tb->tmo_buf;
- tb->tmo_buf = *tmo_buf;
+ /* Swap in new event, save any old event */
+ old_event = tb->tmo_event;
+ tb->tmo_event = *tmo_event;
/* Write the new expiration tick */
tb->exp_tck.v = abs_tck;
/* Release the lock */
- _odp_atomic_flag_clear(IDX2LOCK(idx));
+ _odp_atomic_flag_clear(IDX2LOCK(tp, idx));
#endif
- /* Return old timeout buffer */
- *tmo_buf = old_buf;
+ /* Return old timeout event */
+ *tmo_event = old_event;
}
return success;
}
-static odp_buffer_t timer_cancel(odp_timer_pool *tp,
- uint32_t idx,
- uint64_t new_state)
+static odp_event_t timer_set_unused(timer_pool_t *tp, uint32_t idx)
{
tick_buf_t *tb = &tp->tick_buf[idx];
- odp_buffer_t old_buf;
+ odp_event_t old_event;
-#ifdef ODP_ATOMIC_U128
+#if USE_128BIT_ATOMICS
tick_buf_t new, old;
+
+ /* Init all bits, also when tmo_event is less than 64 bits */
+ new.tmo_u64 = 0;
+
/* Update the timer state (e.g. cancel the current timeout) */
- new.exp_tck.v = new_state;
- /* Swap out the old buffer */
- new.tmo_buf = ODP_BUFFER_INVALID;
- TB_SET_PAD(new);
+ new.exp_tck.v = TMO_UNUSED;
+ /* Swap out the old event */
+ new.tmo_event = ODP_EVENT_INVALID;
+
_odp_atomic_u128_xchg_mm((_odp_atomic_u128_t *)tb,
- (_uint128_t *)&new, (_uint128_t *)&old,
- _ODP_MEMMODEL_RLX);
- old_buf = old.tmo_buf;
+ (_odp_u128_t *)&new, (_odp_u128_t *)&old,
+ _ODP_MEMMODEL_ACQ_RLS);
+ old_event = old.tmo_event;
#else
/* Take a related lock */
- while (_odp_atomic_flag_tas(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_tas(IDX2LOCK(tp, idx)))
/* While lock is taken, spin using relaxed loads */
- while (_odp_atomic_flag_load(IDX2LOCK(idx)))
+ while (_odp_atomic_flag_load(IDX2LOCK(tp, idx)))
odp_cpu_pause();
/* Update the timer state (e.g. cancel the current timeout) */
- tb->exp_tck.v = new_state;
+ tb->exp_tck.v = TMO_UNUSED;
- /* Swap out the old buffer */
- old_buf = tb->tmo_buf;
- tb->tmo_buf = ODP_BUFFER_INVALID;
+ /* Swap out the old event */
+ old_event = tb->tmo_event;
+ tb->tmo_event = ODP_EVENT_INVALID;
/* Release the lock */
- _odp_atomic_flag_clear(IDX2LOCK(idx));
+ _odp_atomic_flag_clear(IDX2LOCK(tp, idx));
#endif
- /* Return the old buffer */
- return old_buf;
+ /* Return the old event */
+ return old_event;
}
-static unsigned timer_expire(odp_timer_pool *tp, uint32_t idx, uint64_t tick)
+int odp_timer_free(odp_timer_t hdl)
{
- odp_timer *tim = &tp->timers[idx];
+ timer_pool_t *tp = handle_to_tp(hdl);
+ uint32_t idx = handle_to_idx(hdl, tp);
+ _odp_timer_t *tim = &tp->timers[idx];
tick_buf_t *tb = &tp->tick_buf[idx];
- odp_buffer_t tmo_buf = ODP_BUFFER_INVALID;
+
+ /* Free the timer by setting timer state to unused and
+ * grab any timeout event */
+ odp_event_t old_event = timer_set_unused(tp, idx);
+ if (old_event != ODP_EVENT_INVALID) {
+ _ODP_ERR("Timer is active\n");
+ return -1;
+ }
+
+ /* Remove timer from queue */
+ _odp_queue_fn->timer_rem(tim->queue);
+
+ /* Destroy timer */
+ _ODP_ASSERT(tb->exp_tck.v == TMO_UNUSED);
+ _ODP_ASSERT(tb->tmo_event == ODP_EVENT_INVALID);
+ tim->queue = ODP_QUEUE_INVALID;
+ tim->user_ptr = NULL;
+
+ /* Insert timer into free list */
+ odp_spinlock_lock(&tp->lock);
+ set_next_free(tim, tp->first_free);
+ tp->first_free = idx;
+ _ODP_ASSERT(tp->num_alloc != 0);
+ tp->num_alloc--;
+ odp_spinlock_unlock(&tp->lock);
+
+ return 0;
+}
+
+static odp_event_t timer_cancel(timer_pool_t *tp, uint32_t idx)
+{
+ tick_buf_t *tb = &tp->tick_buf[idx];
+ odp_event_t old_event;
+
+#if USE_128BIT_ATOMICS
+ tick_buf_t new, old;
+
+ /* Init all bits, also when tmo_event is less than 64 bits */
+ new.tmo_u64 = 0;
+ old.tmo_u64 = 0;
+
+ /* Relaxed and non-atomic read of current values */
+ old.exp_tck.v = tb->exp_tck.v;
+ old.tmo_event = tb->tmo_event;
+
+ do {
+ /* Check if it is not expired already */
+ if (old.exp_tck.v & TMO_INACTIVE) {
+ old.tmo_event = ODP_EVENT_INVALID;
+ break;
+ }
+
+ /* Set up new values */
+ new.exp_tck.v = TMO_INACTIVE;
+ new.tmo_event = ODP_EVENT_INVALID;
+
+ /* Atomic CAS will fail if we experienced torn reads,
+ * retry update sequence until CAS succeeds */
+ } while (!odp_atomic_cas_rel_u128(&tb->tb_atomic_u128, &old.tb_u128,
+ new.tb_u128));
+
+ old_event = old.tmo_event;
+#else
+ /* Take a related lock */
+ while (_odp_atomic_flag_tas(IDX2LOCK(tp, idx)))
+ /* While lock is taken, spin using relaxed loads */
+ while (_odp_atomic_flag_load(IDX2LOCK(tp, idx)))
+ odp_cpu_pause();
+
+ /* Swap in new event, save any old event */
+ old_event = tb->tmo_event;
+ tb->tmo_event = ODP_EVENT_INVALID;
+
+ /* Write the new expiration tick if it not cancelled */
+ if (tb->exp_tck.v & TMO_INACTIVE)
+ old_event = ODP_EVENT_INVALID;
+ else
+ tb->exp_tck.v = TMO_INACTIVE;
+
+ /* Release the lock */
+ _odp_atomic_flag_clear(IDX2LOCK(tp, idx));
+#endif
+ /* Return the old event */
+ return old_event;
+}
+
+static inline void timer_expire(timer_pool_t *tp, uint32_t idx, uint64_t tick)
+{
uint64_t exp_tck;
-#ifdef ODP_ATOMIC_U128
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ _odp_timer_t *tim = &tp->timers[idx];
+ tick_buf_t *tb = &tp->tick_buf[idx];
+ odp_event_t tmo_event = ODP_EVENT_INVALID;
+
+#if USE_128BIT_ATOMICS
/* Atomic re-read for correctness */
- exp_tck = _odp_atomic_u64_load_mm(&tb->exp_tck, _ODP_MEMMODEL_RLX);
+ exp_tck = odp_atomic_load_acq_u64(&tb->exp_tck);
/* Re-check exp_tck */
if (odp_likely(exp_tck <= tick)) {
- /* Attempt to grab timeout buffer, replace with inactive timer
- * and invalid buffer */
+ /* Attempt to grab timeout event, replace with inactive timer
+ * and invalid event. */
tick_buf_t new, old;
+
+ /* Read queue handle between acq and rel. Timer_free overwrites the handle after
+ * it sets tick value to inactive. */
+ queue = tim->queue;
+
+ /* Init all bits, also when tmo_event is less than 64 bits. */
+ new.tmo_u64 = 0;
+ old.tmo_u64 = 0;
+
old.exp_tck.v = exp_tck;
- old.tmo_buf = tb->tmo_buf;
- TB_SET_PAD(old);
+ old.tmo_event = tb->tmo_event;
+
/* Set the inactive/expired bit keeping the expiration tick so
* that we can check against the expiration tick of the timeout
* when it is received */
new.exp_tck.v = exp_tck | TMO_INACTIVE;
- new.tmo_buf = ODP_BUFFER_INVALID;
- TB_SET_PAD(new);
- int succ = _odp_atomic_u128_cmp_xchg_mm(
- (_odp_atomic_u128_t *)tb,
- (_uint128_t *)&old, (_uint128_t *)&new,
- _ODP_MEMMODEL_RLS, _ODP_MEMMODEL_RLX);
+ new.tmo_event = ODP_EVENT_INVALID;
+
+ int succ = odp_atomic_cas_rel_u128(&tb->tb_atomic_u128,
+ &old.tb_u128, new.tb_u128);
if (succ)
- tmo_buf = old.tmo_buf;
+ tmo_event = old.tmo_event;
/* Else CAS failed, something changed => skip timer
* this tick, it will be checked again next tick */
}
/* Else false positive, ignore */
#else
- /* Take a related lock */
- while (_odp_atomic_flag_tas(IDX2LOCK(idx)))
- /* While lock is taken, spin using relaxed loads */
- while (_odp_atomic_flag_load(IDX2LOCK(idx)))
- odp_cpu_pause();
+ /* Try to take a related lock */
+ if (_odp_atomic_flag_tas(IDX2LOCK(tp, idx)))
+ return;
+
/* Proper check for timer expired */
exp_tck = tb->exp_tck.v;
if (odp_likely(exp_tck <= tick)) {
- /* Verify that there is a timeout buffer */
- if (odp_likely(tb->tmo_buf != ODP_BUFFER_INVALID)) {
- /* Grab timeout buffer, replace with inactive timer
- * and invalid buffer */
- tmo_buf = tb->tmo_buf;
- tb->tmo_buf = ODP_BUFFER_INVALID;
+ /* Verify that there is a timeout event */
+ if (odp_likely(tb->tmo_event != ODP_EVENT_INVALID)) {
+ queue = tim->queue;
+
+ /* Grab timeout event, replace with inactive timer
+ * and invalid event. */
+ tmo_event = tb->tmo_event;
+ tb->tmo_event = ODP_EVENT_INVALID;
/* Set the inactive/expired bit keeping the expiration
* tick so that we can check against the expiration
* tick of the timeout when it is received */
tb->exp_tck.v |= TMO_INACTIVE;
}
- /* Else somehow active timer without user buffer */
+ /* Else somehow active timer without user event */
}
/* Else false positive, ignore */
/* Release the lock */
- _odp_atomic_flag_clear(IDX2LOCK(idx));
+ _odp_atomic_flag_clear(IDX2LOCK(tp, idx));
#endif
- if (odp_likely(tmo_buf != ODP_BUFFER_INVALID)) {
+ if (odp_likely(tmo_event != ODP_EVENT_INVALID)) {
/* Fill in expiration tick for timeout events */
- if (odp_event_type(odp_buffer_to_event(tmo_buf)) ==
- ODP_EVENT_TIMEOUT) {
- /* Convert from buffer to timeout hdr */
+ if (odp_event_type(tmo_event) == ODP_EVENT_TIMEOUT) {
+ /* Convert from event to timeout hdr */
odp_timeout_hdr_t *tmo_hdr =
- timeout_hdr_from_buf(tmo_buf);
+ timeout_hdr_from_event(tmo_event);
tmo_hdr->expiration = exp_tck;
/* timer and user_ptr fields filled in when timer
* was set */
}
/* Else ignore events of other types */
/* Post the timeout to the destination queue */
- int rc = odp_queue_enq(tim->queue,
- odp_buffer_to_event(tmo_buf));
+ int rc = odp_queue_enq(queue, tmo_event);
+
if (odp_unlikely(rc != 0)) {
- odp_buffer_free(tmo_buf);
- ODP_ABORT("Failed to enqueue timeout buffer (%d)\n",
- rc);
+ _odp_event_free(tmo_event);
+ _ODP_ABORT("Failed to enqueue timeout event (%d)\n", rc);
}
- return 1;
- } else {
- /* Else false positive, ignore */
- return 0;
}
}
-static unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, uint64_t tick)
+static inline uint64_t timer_pool_scan(timer_pool_t *tp, uint64_t tick)
{
- tick_buf_t *array = &tpid->tick_buf[0];
- uint32_t high_wm = _odp_atomic_u32_load_mm(&tpid->high_wm,
- _ODP_MEMMODEL_ACQ);
- unsigned nexp = 0;
+ tick_buf_t *array = &tp->tick_buf[0];
+ uint32_t high_wm = odp_atomic_load_acq_u32(&tp->high_wm);
uint32_t i;
+ uint64_t min = UINT64_MAX;
- ODP_ASSERT(high_wm <= tpid->param.num_timers);
- for (i = 0; i < high_wm;) {
+ _ODP_ASSERT(high_wm <= tp->param.num_timers);
+ for (i = 0; i < high_wm; i++) {
/* As a rare occurrence, we can outsmart the HW prefetcher
* and the compiler (GCC -fprefetch-loop-arrays) with some
* tuned manual prefetching (32x16=512B ahead), seems to
* give 30% better performance on ARM C-A15 */
__builtin_prefetch(&array[i + 32], 0, 0);
/* Non-atomic read for speed */
- uint64_t exp_tck = array[i++].exp_tck.v;
+ uint64_t exp_tck = array[i].exp_tck.v;
+
if (odp_unlikely(exp_tck <= tick)) {
/* Attempt to expire timer */
- nexp += timer_expire(tpid, i - 1, tick);
+ timer_expire(tp, i, tick);
+ min = 0;
+ } else {
+ min = _ODP_MIN(min, exp_tck - tick);
}
}
- return nexp;
+
+ return min;
+}
+
+/******************************************************************************
+ * Inline timer processing
+ *****************************************************************************/
+
+static inline uint64_t timer_pool_scan_inline(int num, odp_time_t now, int force)
+{
+ timer_pool_t *tp;
+ uint64_t new_tick, old_tick, ticks_to_next_expire, nsec, min = UINT64_MAX;
+ int64_t diff;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ tp = timer_global->timer_pool[i];
+
+ if (tp == NULL)
+ continue;
+
+ if (odp_likely(tp->owner < 0)) {
+ /* Skip shared pool, if this thread is not configured
+ * to process those */
+ if (odp_unlikely(timer_local.poll_shared == 0))
+ continue;
+ } else {
+ /* Skip private pool, if this thread is not the owner */
+ if (tp->owner != odp_thread_id())
+ continue;
+ }
+
+ nsec = odp_time_to_ns(now);
+ new_tick = nsec / tp->nsec_per_scan;
+ old_tick = odp_atomic_load_u64(&tp->cur_tick);
+ diff = new_tick - old_tick;
+
+ if (diff < 1 && !force)
+ continue;
+
+ if (odp_atomic_cas_u64(&tp->cur_tick, &old_tick, new_tick)) {
+ if (ODP_DEBUG_PRINT && odp_atomic_load_u32(&tp->notify_overrun) == 1 &&
+ diff > 1) {
+ if (old_tick == 0) {
+ _ODP_DBG("Timer pool (%s) missed %" PRIi64 " scans in start up\n",
+ tp->name, diff - 1);
+ } else {
+ _ODP_DBG("Timer pool (%s) resolution too high: %" PRIi64 " scans missed\n",
+ tp->name, diff - 1);
+ odp_atomic_store_u32(&tp->notify_overrun, 2);
+ }
+ }
+ ticks_to_next_expire = timer_pool_scan(tp, nsec);
+ min = _ODP_MIN(min, ticks_to_next_expire);
+ }
+ }
+
+ return min;
+}
+
+uint64_t _odp_timer_run_inline(int dec)
+{
+ odp_time_t now;
+ int num = timer_global->highest_tp_idx + 1;
+ int force = (dec == TIMER_SCAN_FORCE);
+ int poll_interval = force ? 0 : timer_global->poll_interval;
+
+ if (num == 0)
+ return UINT64_MAX;
+
+ /* Rate limit how often this thread checks the timer pools. */
+
+ if (poll_interval > 1) {
+ timer_local.run_cnt -= dec;
+ if (timer_local.run_cnt > 0)
+ return UINT64_MAX;
+ timer_local.run_cnt = poll_interval;
+ }
+
+ now = odp_time_global();
+
+ if (poll_interval > 1) {
+ odp_time_t period = odp_time_diff(now, timer_local.last_run);
+
+ if (odp_time_cmp(period,
+ timer_global->poll_interval_time) < 0)
+ return UINT64_MAX;
+ timer_local.last_run = now;
+ }
+
+ if (force) {
+ timer_local.run_cnt = poll_interval;
+ timer_local.last_run = now;
+ }
+
+ /* Check the timer pools. */
+ if (CONFIG_TIMER_PROFILE_INLINE) {
+ odp_time_t t1 = odp_time_local_strict();
+
+ uint64_t ret = timer_pool_scan_inline(num, now, force);
+ odp_time_t t2 = odp_time_local_strict();
+
+ timer_local.prof_nsec += odp_time_diff_ns(t2, t1);
+ timer_local.prof_rounds++;
+ return ret;
+ } else {
+ return timer_pool_scan_inline(num, now, force);
+ }
}
/******************************************************************************
@@ -675,99 +899,194 @@ static unsigned odp_timer_pool_expire(odp_timer_pool_t tpid, uint64_t tick)
* Functions that use Linux/POSIX per-process timers and related facilities
*****************************************************************************/
-static void timer_notify(odp_timer_pool *tp)
+static inline void timer_run_posix(timer_pool_t *tp)
{
+ uint64_t nsec;
int overrun;
- int64_t prev_tick;
- if (tp->notify_overrun) {
+ if (ODP_DEBUG_PRINT && odp_atomic_load_u32(&tp->notify_overrun) == 1) {
overrun = timer_getoverrun(tp->timerid);
if (overrun) {
- ODP_ERR("\n\t%d ticks overrun on timer pool \"%s\", timer resolution too high\n",
- overrun, tp->name);
- tp->notify_overrun = 0;
+ _ODP_DBG("\n\t%d ticks overrun on timer pool \"%s\", timer resolution too high\n",
+ overrun, tp->name);
+ odp_atomic_store_u32(&tp->notify_overrun, 2);
}
}
- odp_timer *array = &tp->timers[0];
+ _odp_timer_t *array = &tp->timers[0];
uint32_t i;
/* Prefetch initial cache lines (match 32 above) */
for (i = 0; i < 32; i += ODP_CACHE_LINE_SIZE / sizeof(array[0]))
__builtin_prefetch(&array[i], 0, 0);
- prev_tick = odp_atomic_fetch_inc_u64(&tp->cur_tick);
- /* Scan timer array, looking for timers to expire */
- (void)odp_timer_pool_expire(tp, prev_tick + 1);
-
- /* Else skip scan of timers. cur_tick was updated and next itimer
- * invocation will process older expiration ticks as well */
+ nsec = odp_time_global_ns();
+ timer_pool_scan(tp, nsec);
}
static void *timer_thread(void *arg)
{
- odp_timer_pool *tp = (odp_timer_pool *)arg;
+ timer_pool_t *tp = (timer_pool_t *)arg;
sigset_t sigset;
int ret;
struct timespec tmo;
siginfo_t si;
+ int warm_up = tp->thr_warm_up;
+ int num = 0;
+ pid_t thr_pid;
- tp->timer_thread_id = (pid_t)syscall(SYS_gettid);
-
- tmo.tv_sec = 0;
+ tmo.tv_sec = 0;
tmo.tv_nsec = ODP_TIME_MSEC_IN_NS * 100;
+ /* Unblock sigalarm in this thread */
sigemptyset(&sigset);
- /* unblock sigalarm in this thread */
sigprocmask(SIG_BLOCK, &sigset, NULL);
-
sigaddset(&sigset, SIGALRM);
+ /* Signal that this thread has started */
+ thr_pid = (pid_t)syscall(SYS_gettid);
+ if (thr_pid <= 0) {
+ _ODP_ERR("Invalid tid: %d\n", thr_pid);
+ return NULL;
+ }
+ odp_atomic_store_u64(&tp->thr_pid, thr_pid);
+
while (1) {
ret = sigtimedwait(&sigset, &si, &tmo);
- if (tp->timer_thread_exit) {
- tp->timer_thread_id = 0;
+
+ if (odp_atomic_load_u32(&tp->thr_exit)) {
+ odp_atomic_store_u64(&tp->thr_pid, 0);
return NULL;
}
- if (ret > 0)
- timer_notify(tp);
+
+ if (ret <= 0)
+ continue;
+
+ timer_run_posix(tp);
+
+ if (num < warm_up) {
+ num++;
+
+ if (num == warm_up)
+ odp_atomic_store_rel_u32(&tp->thr_ready, 1);
+ }
}
return NULL;
}
-static void itimer_init(odp_timer_pool *tp)
+/* Get the max timer resolution without overrun and fill in timer_res variable.
+ *
+ * Set timer's interval with candidate resolutions to get the max resolution
+ * that the timer would not be overrun.
+ * The candidate resolution value is from 1ms to 100us, 10us...1ns etc.
+ */
+static int timer_res_init(void)
+{
+ struct sigevent sigev;
+ timer_t timerid;
+ uint64_t res, sec, nsec;
+ struct itimerspec ispec;
+ sigset_t sigset;
+ siginfo_t si;
+ int loop_cnt;
+ struct timespec tmo;
+
+ sigev.sigev_notify = SIGEV_THREAD_ID;
+ sigev._sigev_un._tid = (pid_t)syscall(SYS_gettid);
+ sigev.sigev_value.sival_ptr = NULL;
+ sigev.sigev_signo = SIGUSR1;
+
+ /* Create timer */
+ if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid))
+ _ODP_ABORT("timer_create() returned error %s\n", strerror(errno));
+
+ /* Timer resolution start from 1ms */
+ res = ODP_TIME_MSEC_IN_NS;
+ /* Set initial value of timer_res */
+ timer_global->highest_res_ns = res;
+ sigemptyset(&sigset);
+ /* Add SIGUSR1 to sigset */
+ sigaddset(&sigset, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+ while (res > 0) {
+ /* Loop for 10 times to test the result */
+ loop_cnt = TIMER_RES_TEST_LOOP_COUNT;
+ sec = res / ODP_TIME_SEC_IN_NS;
+ nsec = res - sec * ODP_TIME_SEC_IN_NS;
+
+ memset(&ispec, 0, sizeof(ispec));
+ ispec.it_interval.tv_sec = (time_t)sec;
+ ispec.it_interval.tv_nsec = (long)nsec;
+ ispec.it_value.tv_sec = (time_t)sec;
+ ispec.it_value.tv_nsec = (long)nsec;
+
+ if (timer_settime(timerid, 0, &ispec, NULL))
+ _ODP_ABORT("timer_settime() returned error %s\n", strerror(errno));
+ /* Set signal wait timeout to 10*res */
+ tmo.tv_sec = 0;
+ tmo.tv_nsec = res * 10;
+ while (loop_cnt--) {
+ if (sigtimedwait(&sigset, &si, &tmo) > 0) {
+ if (timer_getoverrun(timerid))
+ /* overrun at this resolution */
+ /* goto the end */
+ goto timer_res_init_done;
+ }
+ }
+ /* Set timer_res */
+ timer_global->highest_res_ns = res;
+ /* Test the next timer resolution candidate */
+ res /= 10;
+ }
+
+timer_res_init_done:
+ timer_global->highest_res_ns *= TIMER_RES_ROUNDUP_FACTOR;
+ if (timer_delete(timerid) != 0)
+ _ODP_ABORT("timer_delete() returned error %s\n", strerror(errno));
+ sigemptyset(&sigset);
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+ return 0;
+}
+
+static void posix_timer_start(timer_pool_t *tp)
{
struct sigevent sigev;
struct itimerspec ispec;
uint64_t res, sec, nsec;
int ret;
- ODP_DBG("Creating POSIX timer for timer pool %s, period %"
- PRIu64" ns\n", tp->name, tp->param.res_ns);
+ _ODP_DBG("Creating POSIX timer for timer pool %s, period %" PRIu64 " ns\n",
+ tp->name, tp->param.res_ns);
+
+ res = tp->param.res_ns;
+ sec = res / ODP_TIME_SEC_IN_NS;
+ nsec = res - sec * ODP_TIME_SEC_IN_NS;
+
+ odp_atomic_init_u64(&tp->thr_pid, 0);
+ tp->thr_warm_up = 1;
+
+ /* 20ms warm up */
+ if (res < (20 * ODP_TIME_MSEC_IN_NS))
+ tp->thr_warm_up = (20 * ODP_TIME_MSEC_IN_NS) / res;
- tp->timer_thread_id = 0;
- ret = pthread_create(&tp->timer_thread, NULL, timer_thread, tp);
+ odp_atomic_init_u32(&tp->thr_ready, 0);
+ ret = pthread_create(&tp->thr_pthread, NULL, timer_thread, tp);
if (ret)
- ODP_ABORT("unable to create timer thread\n");
+ _ODP_ABORT("Unable to create timer thread: %d\n", ret);
- /* wait thread set tp->timer_thread_id */
- do {
+ /* wait thread set tp->thr_pid */
+ while (!odp_atomic_load_u64(&tp->thr_pid))
sched_yield();
- } while (tp->timer_thread_id == 0);
memset(&sigev, 0, sizeof(sigev));
sigev.sigev_notify = SIGEV_THREAD_ID;
sigev.sigev_value.sival_ptr = tp;
- sigev._sigev_un._tid = tp->timer_thread_id;
+ sigev._sigev_un._tid = odp_atomic_load_u64(&tp->thr_pid);
sigev.sigev_signo = SIGALRM;
if (timer_create(CLOCK_MONOTONIC, &sigev, &tp->timerid))
- ODP_ABORT("timer_create() returned error %s\n",
- strerror(errno));
-
- res = tp->param.res_ns;
- sec = res / ODP_TIME_SEC_IN_NS;
- nsec = res - sec * ODP_TIME_SEC_IN_NS;
+ _ODP_ABORT("timer_create() returned error %s\n", strerror(errno));
memset(&ispec, 0, sizeof(ispec));
ispec.it_interval.tv_sec = (time_t)sec;
@@ -776,15 +1095,224 @@ static void itimer_init(odp_timer_pool *tp)
ispec.it_value.tv_nsec = (long)nsec;
if (timer_settime(tp->timerid, 0, &ispec, NULL))
- ODP_ABORT("timer_settime() returned error %s\n",
- strerror(errno));
+ _ODP_ABORT("timer_settime() returned error %s\n", strerror(errno));
+
+ /* Wait response from timer thread that warm up signals have been
+ * processed. Warm up helps avoiding overrun on the first timeout. */
+ while (odp_atomic_load_acq_u32(&tp->thr_ready) == 0)
+ sched_yield();
+
+ if (ODP_DEBUG_PRINT) {
+ uint32_t old_val = 0;
+
+ odp_atomic_cas_u32(&tp->notify_overrun, &old_val, 1);
+ }
}
-static void itimer_fini(odp_timer_pool *tp)
+static odp_timer_pool_t timer_pool_new(const char *name, const odp_timer_pool_param_t *param)
{
- if (timer_delete(tp->timerid) != 0)
- ODP_ABORT("timer_delete() returned error %s\n",
- strerror(errno));
+ uint32_t i;
+ int tp_idx;
+ size_t sz0, sz1, sz2;
+ uint64_t tp_size;
+ uint64_t res_ns, nsec_per_scan;
+ odp_shm_t shm;
+ timer_pool_t *tp;
+ odp_time_t diff, time;
+ odp_time_t max_diff = ODP_TIME_NULL;
+ double base_freq = 0.0;
+ uint64_t max_multiplier = 0;
+ uint32_t flags = 0;
+ int periodic = (param->timer_type == ODP_TIMER_TYPE_PERIODIC) ? 1 : 0;
+
+ if (param->res_ns)
+ res_ns = param->res_ns;
+ else
+ res_ns = GIGA_HZ / param->res_hz;
+
+ if (periodic) {
+ uint64_t max_capa, min_period_ns;
+
+ base_freq = odp_fract_u64_to_dbl(&param->periodic.base_freq_hz);
+ max_multiplier = param->periodic.max_multiplier;
+
+ if (base_freq < MIN_BASE_HZ || base_freq > timer_global->max_base_hz) {
+ _ODP_ERR("Bad base frequency: %f\n", base_freq);
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ max_capa = max_multiplier_capa(base_freq);
+
+ if (max_multiplier == 0 || max_multiplier > max_capa) {
+ _ODP_ERR("Bad max multiplier: %" PRIu64 "\n", max_multiplier);
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ min_period_ns = GIGA_HZ / (base_freq * max_multiplier);
+
+ if (res_ns > min_period_ns)
+ res_ns = min_period_ns;
+ }
+
+ if (odp_global_ro.shm_single_va)
+ flags |= ODP_SHM_SINGLE_VA;
+
+ time = odp_time_global();
+
+ odp_ticketlock_lock(&timer_global->lock);
+
+ if (timer_global->num_timer_pools >= MAX_TIMER_POOLS) {
+ odp_ticketlock_unlock(&timer_global->lock);
+ _ODP_DBG("No more free timer pools\n");
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ /* Find timer pool that has not been used for a while, or is used least recently.
+ * This ensures that inline scan of an old timer pool has completed and its memory
+ * can be freed. */
+ tp_idx = -1;
+ for (i = 0; i < MAX_TIMER_POOLS; i++) {
+ if (timer_global->timer_pool_used[i] == 0) {
+ diff = odp_time_diff(time, timer_global->destroy_time[i]);
+
+ if (odp_time_to_ns(diff) > TIMER_POOL_REUSE_NS) {
+ tp_idx = i;
+ break;
+ }
+
+ if (odp_time_cmp(diff, max_diff) > 0) {
+ max_diff = diff;
+ tp_idx = i;
+ }
+ }
+ }
+
+ if (tp_idx < 0) {
+ odp_ticketlock_unlock(&timer_global->lock);
+ _ODP_DBG("Did not find free timer pool\n");
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ shm = timer_global->tp_shm[tp_idx];
+ timer_global->timer_pool_used[tp_idx] = 1;
+ timer_global->num_timer_pools++;
+
+ odp_ticketlock_unlock(&timer_global->lock);
+
+ /* Free memory of previously destroyed timer pool */
+ if (shm != ODP_SHM_INVALID) {
+ if (odp_shm_free(shm)) {
+ _ODP_ERR("Failed to free shared memory: tp_idx %i\n", tp_idx);
+ goto error;
+ }
+ }
+
+ sz0 = _ODP_ROUNDUP_CACHE_LINE(sizeof(timer_pool_t));
+ sz1 = _ODP_ROUNDUP_CACHE_LINE(sizeof(tick_buf_t) * param->num_timers);
+ sz2 = _ODP_ROUNDUP_CACHE_LINE(sizeof(_odp_timer_t) * param->num_timers);
+ tp_size = sz0 + sz1 + sz2;
+
+ shm = odp_shm_reserve(name, tp_size, ODP_CACHE_LINE_SIZE, flags);
+
+ if (odp_unlikely(shm == ODP_SHM_INVALID)) {
+ _ODP_ERR("Timer pool shm reserve failed %" PRIu64 "kB\n", tp_size / 1024);
+ goto error;
+ }
+
+ tp = (timer_pool_t *)odp_shm_addr(shm);
+ memset(tp, 0, tp_size);
+
+ tp->periodic = periodic;
+
+ /* Scan timer pool twice during resolution interval */
+ if (res_ns > ODP_TIME_USEC_IN_NS)
+ nsec_per_scan = res_ns / 2;
+ else
+ nsec_per_scan = res_ns;
+
+ tp->nsec_per_scan = nsec_per_scan;
+
+ odp_atomic_init_u64(&tp->cur_tick, 0);
+
+ if (name == NULL)
+ tp->name[0] = 0;
+ else
+ _odp_strcpy(tp->name, name, ODP_TIMER_POOL_NAME_LEN);
+
+ tp->param = *param;
+ tp->param.res_ns = res_ns;
+ if (periodic) {
+ tp->base_freq = base_freq;
+ tp->max_multiplier = max_multiplier;
+ } else {
+ tp->min_rel_tck = odp_timer_ns_to_tick(timer_pool_to_hdl(tp), param->min_tmo);
+ tp->max_rel_tck = odp_timer_ns_to_tick(timer_pool_to_hdl(tp), param->max_tmo);
+ }
+ tp->num_alloc = 0;
+ odp_atomic_init_u32(&tp->high_wm, 0);
+ odp_atomic_init_u32(&tp->notify_overrun, 0);
+ odp_atomic_init_u32(&tp->thr_exit, 0);
+ tp->first_free = 0;
+ tp->owner = -1;
+
+ if (param->priv)
+ tp->owner = odp_thread_id();
+
+ tp->tick_buf = (void *)((char *)odp_shm_addr(shm) + sz0);
+ tp->timers = (void *)((char *)odp_shm_addr(shm) + sz0 + sz1);
+
+#if !USE_128BIT_ATOMICS
+ for (i = 0; i < NUM_LOCKS; i++)
+ _odp_atomic_flag_clear(&tp->locks[i]);
+#endif
+
+ /* Initialize all odp_timer entries */
+ for (i = 0; i < tp->param.num_timers; i++) {
+ tp->timers[i].queue = ODP_QUEUE_INVALID;
+ set_next_free(&tp->timers[i], i + 1);
+ tp->timers[i].user_ptr = NULL;
+ odp_atomic_init_u64(&tp->tick_buf[i].exp_tck, TMO_UNUSED);
+ tp->tick_buf[i].tmo_event = ODP_EVENT_INVALID;
+ }
+ tp->tp_idx = tp_idx;
+ odp_spinlock_init(&tp->lock);
+
+ odp_ticketlock_lock(&timer_global->lock);
+
+ /* Inline timer scan may find the timer pool after this */
+ odp_mb_release();
+ timer_global->timer_pool[tp_idx] = tp;
+ timer_global->tp_shm[tp_idx] = shm;
+
+ if (timer_global->num_timer_pools == 1)
+ odp_global_rw->inline_timers = timer_global->use_inline_timers;
+
+ /* Increase poll rate to match the highest resolution */
+ if (timer_global->poll_interval_nsec > nsec_per_scan) {
+ timer_global->poll_interval_nsec = nsec_per_scan;
+ timer_global->poll_interval_time =
+ odp_time_global_from_ns(nsec_per_scan);
+ }
+
+ /* Update the highest index for inline timer scan */
+ if (tp_idx > timer_global->highest_tp_idx)
+ timer_global->highest_tp_idx = tp_idx;
+
+ odp_ticketlock_unlock(&timer_global->lock);
+
+ if (!odp_global_rw->inline_timers)
+ posix_timer_start(tp);
+
+ return timer_pool_to_hdl(tp);
+
+error:
+ odp_ticketlock_lock(&timer_global->lock);
+ timer_global->tp_shm[tp_idx] = shm;
+ timer_global->timer_pool_used[tp_idx] = 0;
+ timer_global->num_timer_pools--;
+ odp_ticketlock_unlock(&timer_global->lock);
+
+ return ODP_TIMER_POOL_INVALID;
}
/******************************************************************************
@@ -792,17 +1320,147 @@ static void itimer_fini(odp_timer_pool *tp)
* Some parameter checks and error messages
* No modificatios of internal state
*****************************************************************************/
-odp_timer_pool_t
-odp_timer_pool_create(const char *name,
- const odp_timer_pool_param_t *param)
-{
- /* Verify that buffer pool can be used for timeouts */
- /* Verify that we have a valid (non-zero) timer resolution */
- if (param->res_ns == 0) {
- __odp_errno = EINVAL;
+int odp_timer_capability(odp_timer_clk_src_t clk_src,
+ odp_timer_capability_t *capa)
+{
+ if (clk_src != ODP_CLOCK_DEFAULT) {
+ _ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", clk_src);
+ return -1;
+ }
+
+ memset(capa, 0, sizeof(odp_timer_capability_t));
+
+ capa->max_pools_combined = MAX_TIMER_POOLS;
+ capa->max_pools = MAX_TIMER_POOLS;
+ capa->max_timers = 0;
+ capa->periodic.max_pools = MAX_TIMER_POOLS;
+ capa->periodic.max_timers = MAX_PERIODIC_TIMERS;
+ capa->highest_res_ns = timer_global->highest_res_ns;
+ capa->max_res.res_ns = timer_global->highest_res_ns;
+ capa->max_res.res_hz = timer_global->highest_res_hz;
+ capa->max_res.min_tmo = 0;
+ capa->max_res.max_tmo = MAX_TMO_NSEC;
+ capa->max_tmo.res_ns = timer_global->highest_res_ns;
+ capa->max_tmo.res_hz = timer_global->highest_res_hz;
+ capa->max_tmo.min_tmo = 0;
+ capa->max_tmo.max_tmo = MAX_TMO_NSEC;
+ capa->queue_type_sched = true;
+ capa->queue_type_plain = true;
+
+ capa->periodic.min_base_freq_hz.integer = MIN_BASE_HZ;
+ capa->periodic.max_base_freq_hz.integer = timer_global->max_base_hz;
+
+ return 0;
+}
+
+int odp_timer_res_capability(odp_timer_clk_src_t clk_src,
+ odp_timer_res_capability_t *res_capa)
+{
+ if (clk_src != ODP_CLOCK_DEFAULT) {
+ _ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", clk_src);
+ return -1;
+ }
+
+ if (res_capa->min_tmo) {
+ _ODP_ERR("Only res_ns or max_tmo based queries supported\n");
+ return -1;
+ }
+
+ if (res_capa->res_ns || res_capa->res_hz) {
+ if (res_capa->res_ns && res_capa->res_ns < timer_global->highest_res_ns) {
+ _ODP_DBG("Timeout resolution capability (res_ns) exceeded\n");
+ return -1;
+ }
+ if (res_capa->res_hz && res_capa->res_hz > timer_global->highest_res_hz) {
+ _ODP_DBG("Timeout resolution capability (res_hz) exceeded\n");
+ return -1;
+ }
+ res_capa->min_tmo = 0;
+ res_capa->max_tmo = MAX_TMO_NSEC;
+ } else { /* max_tmo */
+ if (res_capa->max_tmo > MAX_TMO_NSEC) {
+ _ODP_DBG("Maximum relative timeout capability (max_tmo) exceeded\n");
+ return -1;
+ }
+ res_capa->min_tmo = 0;
+ res_capa->res_ns = timer_global->highest_res_ns;
+ res_capa->res_hz = timer_global->highest_res_hz;
+ }
+
+ return 0;
+}
+
+int odp_timer_periodic_capability(odp_timer_clk_src_t clk_src,
+ odp_timer_periodic_capability_t *capa)
+{
+ double freq;
+ uint64_t multiplier;
+
+ if (clk_src != ODP_CLOCK_DEFAULT) {
+ _ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", clk_src);
+ return -1;
+ }
+
+ freq = odp_fract_u64_to_dbl(&capa->base_freq_hz);
+ if (freq < MIN_BASE_HZ || freq > timer_global->max_base_hz) {
+ _ODP_ERR("Base frequency not supported (min: %f, max %f)\n",
+ (double)MIN_BASE_HZ, (double)timer_global->max_base_hz);
+ return -1;
+ }
+
+ multiplier = max_multiplier_capa(freq);
+
+ if (capa->max_multiplier > multiplier)
+ return -1;
+
+ if (capa->res_ns && capa->res_ns < timer_global->highest_res_ns)
+ return -1;
+
+ /* Update capa with supported values */
+ capa->max_multiplier = multiplier;
+ capa->res_ns = timer_global->highest_res_ns;
+
+ /* All base frequencies within the range are supported */
+ return 1;
+}
+
+void odp_timer_pool_param_init(odp_timer_pool_param_t *param)
+{
+ memset(param, 0, sizeof(odp_timer_pool_param_t));
+ param->timer_type = ODP_TIMER_TYPE_SINGLE;
+ param->clk_src = ODP_CLOCK_DEFAULT;
+ param->exp_mode = ODP_TIMER_EXP_AFTER;
+}
+
+odp_timer_pool_t odp_timer_pool_create(const char *name,
+ const odp_timer_pool_param_t *param)
+{
+ if (odp_global_ro.init_param.not_used.feat.timer) {
+ _ODP_ERR("Trying to use disabled ODP feature.\n");
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ if (param->clk_src != ODP_CLOCK_DEFAULT) {
+ _ODP_ERR("Only ODP_CLOCK_DEFAULT supported. Requested %i.\n", param->clk_src);
return ODP_TIMER_POOL_INVALID;
}
- return odp_timer_pool_new(name, param);
+
+ if (param->timer_type != ODP_TIMER_TYPE_SINGLE &&
+ param->timer_type != ODP_TIMER_TYPE_PERIODIC) {
+ _ODP_ERR("Bad timer type %i\n", param->timer_type);
+ return ODP_TIMER_POOL_INVALID;
+ }
+
+ if ((param->res_ns && param->res_hz) || (param->res_ns == 0 && param->res_hz == 0))
+ return ODP_TIMER_POOL_INVALID;
+
+ if (param->res_hz == 0 && param->res_ns < timer_global->highest_res_ns)
+ return ODP_TIMER_POOL_INVALID;
+
+ if (param->res_ns == 0 && param->res_hz > timer_global->highest_res_hz)
+ return ODP_TIMER_POOL_INVALID;
+
+ return timer_pool_new(name, param);
}
void odp_timer_pool_start(void)
@@ -810,34 +1468,76 @@ void odp_timer_pool_start(void)
/* Nothing to do here, timer pools are started by the create call */
}
-void odp_timer_pool_destroy(odp_timer_pool_t tpid)
+int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
{
- odp_timer_pool_del(tpid);
-}
+ _ODP_ASSERT(timer_pool != NULL);
+ _ODP_ASSERT(num > 0);
+ if (ODP_DEBUG) {
+ for (int i = 0; i < num; i++)
+ _ODP_ASSERT(timer_pool[i] != ODP_TIMER_POOL_INVALID);
+ }
-uint64_t odp_timer_tick_to_ns(odp_timer_pool_t tpid, uint64_t ticks)
-{
- return ticks * tpid->param.res_ns;
+ /* Nothing to do here, timer pools are started by the create call. */
+ return num;
}
-uint64_t odp_timer_ns_to_tick(odp_timer_pool_t tpid, uint64_t ns)
+void odp_timer_pool_destroy(odp_timer_pool_t tpid)
{
- return (uint64_t)(ns / tpid->param.res_ns);
+ odp_timer_pool_del(timer_pool_from_hdl(tpid));
}
-uint64_t odp_timer_current_tick(odp_timer_pool_t tpid)
+int odp_timer_sample_ticks(odp_timer_pool_t timer_pool[], uint64_t tick[], uint64_t clk_count[],
+ int num)
{
- /* Relaxed atomic read for lowest overhead */
- return odp_atomic_load_u64(&tpid->cur_tick);
+ uint64_t nsec;
+ int i;
+
+ if (num <= 0 || num > MAX_TIMER_POOLS) {
+ _ODP_ERR("Bad number of timer pools: %i\n", num);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (odp_unlikely(timer_pool[i] == ODP_TIMER_POOL_INVALID)) {
+ _ODP_ERR("Invalid timer pool\n");
+ return -1;
+ }
+ }
+
+ nsec = odp_time_global_ns();
+
+ for (i = 0; i < num; i++) {
+ tick[i] = nsec;
+
+ if (clk_count)
+ clk_count[i] = tick[i];
+ }
+
+ return 0;
}
-int odp_timer_pool_info(odp_timer_pool_t tpid,
- odp_timer_pool_info_t *buf)
+int odp_timer_pool_info(odp_timer_pool_t tpid, odp_timer_pool_info_t *tp_info)
{
- buf->param = tpid->param;
- buf->cur_timers = tpid->num_alloc;
- buf->hwm_timers = odp_atomic_load_u32(&tpid->high_wm);
- buf->name = tpid->name;
+ timer_pool_t *tp;
+
+ if (odp_unlikely(tpid == ODP_TIMER_POOL_INVALID)) {
+ _ODP_ERR("Invalid timer pool.\n");
+ return -1;
+ }
+
+ tp = timer_pool_from_hdl(tpid);
+
+ memset(tp_info, 0, sizeof(odp_timer_pool_info_t));
+ tp_info->param = tp->param;
+ tp_info->cur_timers = tp->num_alloc;
+ tp_info->hwm_timers = odp_atomic_load_u32(&tp->high_wm);
+ tp_info->name = tp->name;
+
+ /* One API timer tick is one nsec. Leave source clock information to zero
+ * as there is no direct link between a source clock signal and a timer tick. */
+ tp_info->tick_info.freq.integer = ODP_TIME_SEC_IN_NS;
+ tp_info->tick_info.nsec.integer = 1;
+
return 0;
}
@@ -846,96 +1546,269 @@ uint64_t odp_timer_pool_to_u64(odp_timer_pool_t tpid)
return _odp_pri(tpid);
}
-odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid,
- odp_queue_t queue,
- void *user_ptr)
+odp_timer_t odp_timer_alloc(odp_timer_pool_t tpid, odp_queue_t queue, const void *user_ptr)
{
+ timer_pool_t *tp = timer_pool_from_hdl(tpid);
+
if (odp_unlikely(tpid == ODP_TIMER_POOL_INVALID)) {
- ODP_ERR("Invalid timer pool.\n");
+ _ODP_ERR("Invalid timer pool.\n");
return ODP_TIMER_INVALID;
}
if (odp_unlikely(queue == ODP_QUEUE_INVALID)) {
- ODP_ERR("%s: Invalid queue handle\n", tpid->name);
+ _ODP_ERR("%s: Invalid queue handle\n", tp->name);
return ODP_TIMER_INVALID;
}
/* We don't care about the validity of user_ptr because we will not
* attempt to dereference it */
- return timer_alloc(tpid, queue, user_ptr);
+ return timer_alloc(tp, queue, user_ptr);
}
-odp_event_t odp_timer_free(odp_timer_t hdl)
+int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param)
{
- odp_timer_pool *tp = handle_to_tp(hdl);
- uint32_t idx = handle_to_idx(hdl, tp);
- odp_buffer_t old_buf = timer_free(tp, idx);
- return odp_buffer_to_event(old_buf);
+ uint64_t abs_tick, rel_tick;
+ timer_pool_t *tp = handle_to_tp(timer);
+ uint64_t cur_tick = odp_time_global_ns();
+ uint32_t idx = handle_to_idx(timer, tp);
+ odp_event_t tmo_ev = start_param->tmo_ev;
+
+ if (start_param->tick_type == ODP_TIMER_TICK_ABS) {
+ abs_tick = start_param->tick;
+ rel_tick = abs_tick - cur_tick;
+
+ if (odp_unlikely(abs_tick < cur_tick + tp->min_rel_tck))
+ return ODP_TIMER_TOO_NEAR;
+ } else {
+ rel_tick = start_param->tick;
+ abs_tick = rel_tick + cur_tick;
+
+ if (odp_unlikely(rel_tick < tp->min_rel_tck))
+ return ODP_TIMER_TOO_NEAR;
+ }
+
+ if (odp_unlikely(rel_tick > tp->max_rel_tck))
+ return ODP_TIMER_TOO_FAR;
+
+ if (!timer_reset(idx, abs_tick, &tmo_ev, tp))
+ return ODP_TIMER_FAIL;
+
+ /* Check that timer was not active */
+ if (odp_unlikely(tmo_ev != ODP_EVENT_INVALID)) {
+ _ODP_ERR("Timer was active already\n");
+ odp_event_free(tmo_ev);
+ }
+
+ if (ODP_DEBUG_PRINT) {
+ uint32_t old_val = 0;
+
+ odp_atomic_cas_u32(&tp->notify_overrun, &old_val, 1);
+ }
+
+ return ODP_TIMER_SUCCESS;
}
-int odp_timer_set_abs(odp_timer_t hdl,
- uint64_t abs_tck,
- odp_event_t *tmo_ev)
+int odp_timer_restart(odp_timer_t timer, const odp_timer_start_t *start_param)
{
- odp_timer_pool *tp = handle_to_tp(hdl);
- uint32_t idx = handle_to_idx(hdl, tp);
- uint64_t cur_tick = odp_atomic_load_u64(&tp->cur_tick);
- if (odp_unlikely(abs_tck < cur_tick + tp->min_rel_tck))
- return ODP_TIMER_TOOEARLY;
- if (odp_unlikely(abs_tck > cur_tick + tp->max_rel_tck))
- return ODP_TIMER_TOOLATE;
- if (timer_reset(idx, abs_tck, (odp_buffer_t *)tmo_ev, tp))
- return ODP_TIMER_SUCCESS;
- else
- return ODP_TIMER_NOEVENT;
+ uint64_t abs_tick, rel_tick;
+ timer_pool_t *tp = handle_to_tp(timer);
+ uint64_t cur_tick = odp_time_global_ns();
+ uint32_t idx = handle_to_idx(timer, tp);
+
+ if (start_param->tick_type == ODP_TIMER_TICK_ABS) {
+ abs_tick = start_param->tick;
+ rel_tick = abs_tick - cur_tick;
+
+ if (odp_unlikely(abs_tick < cur_tick + tp->min_rel_tck))
+ return ODP_TIMER_TOO_NEAR;
+ } else {
+ rel_tick = start_param->tick;
+ abs_tick = rel_tick + cur_tick;
+
+ if (odp_unlikely(rel_tick < tp->min_rel_tck))
+ return ODP_TIMER_TOO_NEAR;
+ }
+
+ if (odp_unlikely(rel_tick > tp->max_rel_tck))
+ return ODP_TIMER_TOO_FAR;
+
+ /* Reset timer without changing the event */
+ if (!timer_reset(idx, abs_tick, NULL, tp))
+ return ODP_TIMER_FAIL;
+
+ return ODP_TIMER_SUCCESS;
}
-int odp_timer_set_rel(odp_timer_t hdl,
- uint64_t rel_tck,
- odp_event_t *tmo_ev)
+int odp_timer_periodic_start(odp_timer_t timer, const odp_timer_periodic_start_t *start_param)
{
- odp_timer_pool *tp = handle_to_tp(hdl);
- uint32_t idx = handle_to_idx(hdl, tp);
- uint64_t abs_tck = odp_atomic_load_u64(&tp->cur_tick) + rel_tck;
- if (odp_unlikely(rel_tck < tp->min_rel_tck))
- return ODP_TIMER_TOOEARLY;
- if (odp_unlikely(rel_tck > tp->max_rel_tck))
- return ODP_TIMER_TOOLATE;
- if (timer_reset(idx, abs_tck, (odp_buffer_t *)tmo_ev, tp))
- return ODP_TIMER_SUCCESS;
- else
- return ODP_TIMER_NOEVENT;
+ uint64_t abs_tick, period_ns;
+ timer_pool_t *tp = handle_to_tp(timer);
+ uint64_t cur_tick = odp_time_global_ns();
+ uint32_t idx = handle_to_idx(timer, tp);
+ odp_event_t tmo_ev = start_param->tmo_ev;
+ _odp_timer_t *tim = &tp->timers[idx];
+ uint64_t multiplier = start_param->freq_multiplier;
+ double freq = multiplier * tp->base_freq;
+ double period_ns_dbl;
+
+ if (odp_unlikely(!tp->periodic)) {
+ _ODP_ERR("Not a periodic timer\n");
+ return ODP_TIMER_FAIL;
+ }
+
+ if (odp_unlikely(multiplier == 0 || multiplier > tp->max_multiplier)) {
+ _ODP_ERR("Bad frequency multiplier: %" PRIu64 "\n", multiplier);
+ return ODP_TIMER_FAIL;
+ }
+
+ if (odp_unlikely(odp_event_type(tmo_ev) != ODP_EVENT_TIMEOUT)) {
+ _ODP_ERR("Event type is not timeout\n");
+ return ODP_TIMER_FAIL;
+ }
+
+ period_ns_dbl = (double)ODP_TIME_SEC_IN_NS / freq;
+ period_ns = period_ns_dbl;
+
+ if (period_ns == 0) {
+ _ODP_ERR("Too high periodic timer frequency: %f\n", freq);
+ return ODP_TIMER_FAIL;
+ }
+
+ if (period_ns & PERIODIC_CANCELLED) {
+ _ODP_ERR("Periodic timer frequency error: %f\n", freq);
+ return ODP_TIMER_FAIL;
+ }
+
+ tim->periodic_ticks = period_ns;
+ tim->periodic_ticks_frac = (period_ns_dbl - period_ns) * ACC_SIZE;
+ tim->periodic_ticks_frac_acc = 0;
+ abs_tick = start_param->first_tick;
+
+ if (abs_tick) {
+ if (odp_unlikely(abs_tick < cur_tick))
+ return ODP_TIMER_TOO_NEAR;
+
+ if (odp_unlikely(abs_tick > cur_tick + tim->periodic_ticks))
+ return ODP_TIMER_TOO_FAR;
+ } else {
+ abs_tick = cur_tick + tim->periodic_ticks;
+ }
+
+ if (!timer_reset(idx, abs_tick, &tmo_ev, tp))
+ return ODP_TIMER_FAIL;
+
+ /* Check that timer was not active */
+ if (odp_unlikely(tmo_ev != ODP_EVENT_INVALID)) {
+ _ODP_ERR("Timer was active already\n");
+ odp_event_free(tmo_ev);
+ }
+
+ return ODP_TIMER_SUCCESS;
+}
+
+int odp_timer_periodic_ack(odp_timer_t timer, odp_event_t tmo_ev)
+{
+ uint64_t abs_tick, acc;
+ odp_timeout_t tmo = odp_timeout_from_event(tmo_ev);
+ timer_pool_t *tp = handle_to_tp(timer);
+ uint32_t idx = handle_to_idx(timer, tp);
+ _odp_timer_t *tim = &tp->timers[idx];
+
+ if (odp_unlikely(odp_event_type(tmo_ev) != ODP_EVENT_TIMEOUT)) {
+ _ODP_ERR("Event type is not timeout\n");
+ return -1;
+ }
+
+ abs_tick = tim->periodic_ticks;
+
+ if (odp_unlikely(abs_tick & PERIODIC_CANCELLED)) {
+ /* Timer was tried to cancel earlier, stop now. */
+ return 2;
+ }
+
+ acc = (uint64_t)tim->periodic_ticks_frac_acc + (uint64_t)tim->periodic_ticks_frac;
+
+ if (acc >= ACC_SIZE) {
+ abs_tick++;
+ acc -= ACC_SIZE;
+ }
+
+ tim->periodic_ticks_frac_acc = acc;
+ abs_tick += odp_timeout_tick(tmo);
+
+ if (!timer_reset(idx, abs_tick, &tmo_ev, tp))
+ return -1;
+
+ /* This should never happen. Timer should be always inactive before
+ * timer_reset() call above. */
+ if (odp_unlikely(tmo_ev != ODP_EVENT_INVALID)) {
+ /* Reset returned an event, free it. */
+ _ODP_ERR("Timer was active already\n");
+ odp_event_free(tmo_ev);
+ }
+
+ return 0;
}
int odp_timer_cancel(odp_timer_t hdl, odp_event_t *tmo_ev)
{
- odp_timer_pool *tp = handle_to_tp(hdl);
+ timer_pool_t *tp = handle_to_tp(hdl);
uint32_t idx = handle_to_idx(hdl, tp);
/* Set the expiration tick of the timer to TMO_INACTIVE */
- odp_buffer_t old_buf = timer_cancel(tp, idx, TMO_INACTIVE);
- if (old_buf != ODP_BUFFER_INVALID) {
- *tmo_ev = odp_buffer_to_event(old_buf);
- return 0; /* Active timer cancelled, timeout returned */
- } else {
- return -1; /* Timer already expired, no timeout returned */
+ odp_event_t old_event = timer_cancel(tp, idx);
+
+ if (old_event != ODP_EVENT_INVALID) {
+ /* Active timer cancelled, timeout returned */
+ *tmo_ev = old_event;
+ return ODP_TIMER_SUCCESS;
}
-}
-uint64_t odp_timer_to_u64(odp_timer_t hdl)
-{
- return _odp_pri(hdl);
+ /* Timer already expired, no timeout returned */
+ return ODP_TIMER_TOO_NEAR;
}
-odp_timeout_t odp_timeout_from_event(odp_event_t ev)
+int odp_timer_periodic_cancel(odp_timer_t hdl)
{
- /* This check not mandated by the API specification */
- if (odp_event_type(ev) != ODP_EVENT_TIMEOUT)
- ODP_ABORT("Event not a timeout");
- return (odp_timeout_t)ev;
+ timer_pool_t *tp;
+ uint32_t idx;
+ _odp_timer_t *tim;
+ odp_event_t ev;
+
+ if (odp_unlikely(hdl == ODP_TIMER_INVALID)) {
+ _ODP_ERR("Bad timer pool handle\n");
+ return -1;
+ }
+
+ tp = handle_to_tp(hdl);
+
+ if (odp_unlikely(tp->periodic == 0)) {
+ _ODP_ERR("Not a periodic timer\n");
+ return -1;
+ }
+
+ idx = handle_to_idx(hdl, tp);
+ tim = &tp->timers[idx];
+ ev = timer_cancel(tp, idx);
+
+ /* Cancel failed on a periodic timer. Mark timer cancelled, so that
+ * a following ack call stops restarting it. */
+ tim->periodic_ticks |= PERIODIC_CANCELLED;
+
+ if (ev != ODP_EVENT_INVALID) {
+ /* Timer cancelled and timeout returned. Enqueue tmo, ack call will flag
+ * it as the last event. */
+ if (odp_unlikely(odp_queue_enq(tim->queue, ev))) {
+ _ODP_ERR("Failed to enqueue timeout event\n");
+ _odp_event_free(ev);
+ return -1;
+ }
+ }
+
+ return 0;
}
-odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
+uint64_t odp_timer_to_u64(odp_timer_t hdl)
{
- return (odp_event_t)tmo;
+ return _odp_pri(hdl);
}
uint64_t odp_timeout_to_u64(odp_timeout_t tmo)
@@ -943,69 +1816,366 @@ uint64_t odp_timeout_to_u64(odp_timeout_t tmo)
return _odp_pri(tmo);
}
-int odp_timeout_fresh(odp_timeout_t tmo)
+int ODP_DEPRECATE(odp_timeout_fresh)(odp_timeout_t tmo)
{
const odp_timeout_hdr_t *hdr = timeout_hdr(tmo);
odp_timer_t hdl = hdr->timer;
- odp_timer_pool *tp = handle_to_tp(hdl);
+
+ /* Timeout not connected to a timer */
+ if (odp_unlikely(hdl == ODP_TIMER_INVALID))
+ return 0;
+
+ timer_pool_t *tp = handle_to_tp(hdl);
uint32_t idx = handle_to_idx(hdl, tp);
tick_buf_t *tb = &tp->tick_buf[idx];
-#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2
- uint64_t exp_tck = tb->exp_tck.v;
-#else
uint64_t exp_tck = odp_atomic_load_u64(&tb->exp_tck);
-#endif
+
/* Return true if the timer still has the same expiration tick
* (ignoring the inactive/expired bit) as the timeout */
return hdr->expiration == (exp_tck & ~TMO_INACTIVE);
}
-odp_timer_t odp_timeout_timer(odp_timeout_t tmo)
+odp_timeout_t odp_timeout_alloc(odp_pool_t pool_hdl)
{
- return timeout_hdr(tmo)->timer;
+ odp_timeout_hdr_t *hdr;
+ odp_event_t event;
+ pool_t *pool;
+
+ _ODP_ASSERT(pool_hdl != ODP_POOL_INVALID);
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ _ODP_ASSERT(pool->type == ODP_POOL_TIMEOUT);
+
+ event = _odp_event_alloc(pool);
+ if (odp_unlikely(event == ODP_EVENT_INVALID))
+ return ODP_TIMEOUT_INVALID;
+
+ hdr = timeout_hdr_from_event(event);
+ hdr->timer = ODP_TIMER_INVALID;
+
+ return odp_timeout_from_event(event);
}
-uint64_t odp_timeout_tick(odp_timeout_t tmo)
+int odp_timeout_alloc_multi(odp_pool_t pool_hdl, odp_timeout_t tmo[], int num)
{
- return timeout_hdr(tmo)->expiration;
+ pool_t *pool;
+ int ret;
+
+ _ODP_ASSERT(pool_hdl != ODP_POOL_INVALID);
+ _ODP_ASSERT(tmo != NULL);
+ _ODP_ASSERT(num > 0);
+
+ pool = _odp_pool_entry(pool_hdl);
+
+ _ODP_ASSERT(pool->type == ODP_POOL_TIMEOUT);
+
+ ret = _odp_event_alloc_multi(pool, (_odp_event_hdr_t **)tmo, num);
+
+ for (int i = 0; i < ret; i++)
+ timeout_hdr(tmo[i])->timer = ODP_TIMER_INVALID;
+
+ return ret;
}
-void *odp_timeout_user_ptr(odp_timeout_t tmo)
+void odp_timeout_free(odp_timeout_t tmo)
{
- return timeout_hdr(tmo)->user_ptr;
+ _odp_event_free(odp_timeout_to_event(tmo));
}
-odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
+void odp_timeout_free_multi(odp_timeout_t tmo[], int num)
{
- odp_buffer_t buf = odp_buffer_alloc(pool);
- if (odp_unlikely(buf == ODP_BUFFER_INVALID))
- return ODP_TIMEOUT_INVALID;
- return odp_timeout_from_event(odp_buffer_to_event(buf));
+ _ODP_ASSERT(tmo != NULL);
+ _ODP_ASSERT(num > 0);
+
+ _odp_event_free_multi((_odp_event_hdr_t **)(uintptr_t)tmo, num);
}
-void odp_timeout_free(odp_timeout_t tmo)
+void odp_timer_pool_print(odp_timer_pool_t timer_pool)
+{
+ timer_pool_t *tp;
+ int len = 0;
+ int max_len = 512;
+ int n = max_len - 1;
+ char str[max_len];
+
+ if (timer_pool == ODP_TIMER_POOL_INVALID) {
+ _ODP_ERR("Bad timer pool handle\n");
+ return;
+ }
+
+ tp = timer_pool_from_hdl(timer_pool);
+
+ len += _odp_snprint(&str[len], n - len, "Timer pool info\n");
+ len += _odp_snprint(&str[len], n - len, "---------------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_timer_pool_to_u64(timer_pool));
+ len += _odp_snprint(&str[len], n - len, " tp index %u\n", tp->tp_idx);
+ len += _odp_snprint(&str[len], n - len, " num timers %u\n", tp->num_alloc);
+ len += _odp_snprint(&str[len], n - len, " num tp %i\n",
+ timer_global->num_timer_pools);
+ len += _odp_snprint(&str[len], n - len, " inline timers %i\n",
+ timer_global->use_inline_timers);
+ len += _odp_snprint(&str[len], n - len, " periodic %i\n", tp->periodic);
+ str[len] = 0;
+
+ _ODP_PRINT("%s\n", str);
+}
+
+void odp_timer_print(odp_timer_t timer)
{
- odp_event_t ev = odp_timeout_to_event(tmo);
- odp_buffer_free(odp_buffer_from_event(ev));
+ timer_pool_t *tp;
+ uint32_t idx;
+ _odp_timer_t *tim;
+ int len = 0;
+ int max_len = 512;
+ int n = max_len - 1;
+ char str[max_len];
+
+ if (timer == ODP_TIMER_INVALID) {
+ _ODP_ERR("Bad timer handle\n");
+ return;
+ }
+
+ tp = handle_to_tp(timer);
+ idx = handle_to_idx(timer, tp);
+ tim = &tp->timers[idx];
+
+ len += _odp_snprint(&str[len], n - len, "Timer info\n");
+ len += _odp_snprint(&str[len], n - len, "----------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_timer_to_u64(timer));
+ len += _odp_snprint(&str[len], n - len, " timer pool 0x%" PRIx64 "\n",
+ odp_timer_pool_to_u64(timer_pool_to_hdl(tp)));
+ len += _odp_snprint(&str[len], n - len, " timer index %u\n", idx);
+ len += _odp_snprint(&str[len], n - len, " dest queue 0x%" PRIx64 "\n",
+ odp_queue_to_u64(tim->queue));
+ len += _odp_snprint(&str[len], n - len, " user ptr %p\n", tim->user_ptr);
+ len += _odp_snprint(&str[len], n - len, " periodic ticks %" PRIu64 "\n",
+ tim->periodic_ticks & ~PERIODIC_CANCELLED);
+ str[len] = 0;
+
+ _ODP_PRINT("%s\n", str);
}
-int odp_timer_init_global(void)
+void odp_timeout_print(odp_timeout_t tmo)
{
-#ifndef ODP_ATOMIC_U128
+ const odp_timeout_hdr_t *tmo_hdr;
+ odp_timer_t timer;
+ int len = 0;
+ int max_len = 512;
+ int n = max_len - 1;
+ char str[max_len];
+
+ if (tmo == ODP_TIMEOUT_INVALID) {
+ _ODP_ERR("Bad timeout handle\n");
+ return;
+ }
+
+ tmo_hdr = timeout_hdr(tmo);
+ timer = tmo_hdr->timer;
+
+ len += _odp_snprint(&str[len], n - len, "Timeout info\n");
+ len += _odp_snprint(&str[len], n - len, "------------\n");
+ len += _odp_snprint(&str[len], n - len, " handle 0x%" PRIx64 "\n",
+ odp_timeout_to_u64(tmo));
+ len += _odp_snprint(&str[len], n - len, " expiration %" PRIu64 "\n",
+ tmo_hdr->expiration);
+ len += _odp_snprint(&str[len], n - len, " user ptr %p\n", tmo_hdr->user_ptr);
+ len += _odp_snprint(&str[len], n - len, " user area %p\n", tmo_hdr->uarea_addr);
+
+ if (timer != ODP_TIMER_INVALID) {
+ timer_pool_t *tp = handle_to_tp(timer);
+ uint32_t idx = handle_to_idx(timer, tp);
+
+ len += _odp_snprint(&str[len], n - len, " timer pool 0x%" PRIx64 "\n",
+ odp_timer_pool_to_u64(timer_pool_to_hdl(tp)));
+ len += _odp_snprint(&str[len], n - len, " timer 0x%" PRIx64 "\n",
+ odp_timer_to_u64(timer));
+ len += _odp_snprint(&str[len], n - len, " timer index %u\n", idx);
+ len += _odp_snprint(&str[len], n - len, " periodic %i\n", tp->periodic);
+ }
+ str[len] = 0;
+
+ _ODP_PRINT("%s\n", str);
+}
+
+int _odp_timer_init_global(const odp_init_t *params)
+{
+ odp_shm_t shm;
+ odp_time_t time;
+ const char *conf_str;
uint32_t i;
- for (i = 0; i < NUM_LOCKS; i++)
- _odp_atomic_flag_clear(&locks[i]);
+ int val = 0;
+
+ if (params && params->not_used.feat.timer) {
+ _ODP_DBG("Timers disabled\n");
+ timer_global = NULL;
+ return 0;
+ }
+
+ shm = odp_shm_reserve("_odp_timer_global", sizeof(timer_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ timer_global = odp_shm_addr(shm);
+
+ if (timer_global == NULL) {
+ _ODP_ERR("Shm reserve failed for odp_timer\n");
+ return -1;
+ }
+
+ memset(timer_global, 0, sizeof(timer_global_t));
+ odp_ticketlock_init(&timer_global->lock);
+ timer_global->shm = shm;
+ timer_global->highest_res_ns = MAX_INLINE_RES_NS;
+ timer_global->highest_tp_idx = -1;
+
+ time = odp_time_global();
+ for (i = 0; i < MAX_TIMER_POOLS; i++) {
+ timer_global->destroy_time[i] = time;
+ timer_global->tp_shm[i] = ODP_SHM_INVALID;
+ }
+
+#if USE_128BIT_ATOMICS
+ _ODP_PRINT("Timer using lock-less implementation\n");
#else
- ODP_DBG("Using lock-less timer implementation\n");
+ _ODP_PRINT("Timer using lock-based implementation\n");
#endif
- odp_atomic_init_u32(&num_timer_pools, 0);
- block_sigalarm();
+ _ODP_PRINT("Timer config:\n");
+
+ conf_str = "timer.inline";
+ if (!_odp_libconfig_lookup_int(conf_str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ goto error;
+ }
+ timer_global->use_inline_timers = val;
+ _ODP_PRINT(" %s: %i\n", conf_str, val);
+
+ conf_str = "timer.inline_poll_interval";
+ if (!_odp_libconfig_lookup_int(conf_str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ goto error;
+ }
+ timer_global->poll_interval = val;
+ _ODP_PRINT(" %s: %i\n", conf_str, val);
+
+ conf_str = "timer.inline_poll_interval_nsec";
+ if (!_odp_libconfig_lookup_int(conf_str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ goto error;
+ }
+ timer_global->poll_interval_nsec = val;
+ timer_global->poll_interval_time =
+ odp_time_global_from_ns(timer_global->poll_interval_nsec);
+ _ODP_PRINT(" %s: %i\n", conf_str, val);
+
+ conf_str = "timer.inline_thread_type";
+ if (!_odp_libconfig_lookup_int(conf_str, &val)) {
+ _ODP_ERR("Config option '%s' not found.\n", conf_str);
+ goto error;
+ }
+ timer_global->thread_type = val;
+ _ODP_PRINT(" %s: %i\n", conf_str, val);
+ _ODP_PRINT("\n");
+
+ if (!timer_global->use_inline_timers) {
+ timer_res_init();
+ block_sigalarm();
+ }
+
+ /* timer_res_init() may update highest_res_ns */
+ timer_global->highest_res_hz = GIGA_HZ / timer_global->highest_res_ns;
+ timer_global->max_base_hz = timer_global->highest_res_hz;
return 0;
+
+error:
+ odp_shm_free(shm);
+ return -1;
}
-int odp_timer_term_global(void)
+int _odp_timer_term_global(void)
{
+ odp_shm_t shm;
+ int i;
+ int rc = 0;
+
+ if (timer_global == NULL)
+ return 0;
+
+ for (i = 0; i < MAX_TIMER_POOLS; i++) {
+ shm = timer_global->tp_shm[i];
+
+ if (timer_global->timer_pool_used[i]) {
+ _ODP_ERR("Not destroyed timer pool: %i\n", i);
+ rc = -1;
+
+ /* Prevent crash from timer thread */
+ if (!odp_global_rw->inline_timers) {
+ timer_pool_t *tp = timer_global->timer_pool[i];
+
+ if (tp != NULL)
+ posix_timer_stop(tp);
+ }
+ }
+ if (shm != ODP_SHM_INVALID) {
+ if (odp_shm_free(shm)) {
+ _ODP_ERR("Shm free failed for timer pool: %i\n", i);
+ rc = -1;
+ }
+ }
+ }
+
+ if (odp_shm_free(timer_global->shm)) {
+ _ODP_ERR("Shm free failed for timer_global\n");
+ return -1;
+ }
+
+ return rc;
+}
+
+int _odp_timer_init_local(void)
+{
+ int conf_thr_type;
+ odp_thread_type_t thr_type;
+
+ timer_local.last_run = odp_time_global_from_ns(0);
+ timer_local.run_cnt = 1;
+ timer_local.poll_shared = 0;
+ timer_local.prof_nsec = 0;
+ timer_local.prof_rounds = 0;
+
+ /* Timer feature disabled */
+ if (timer_global == NULL)
+ return 0;
+
+ /* Check if this thread polls shared (non-private) timer pools */
+ conf_thr_type = timer_global->thread_type;
+ thr_type = odp_thread_type();
+
+ if (conf_thr_type == 0)
+ timer_local.poll_shared = 1;
+ else if (conf_thr_type == 1 && thr_type == ODP_THREAD_WORKER)
+ timer_local.poll_shared = 1;
+ else if (conf_thr_type == 2 && thr_type == ODP_THREAD_CONTROL)
+ timer_local.poll_shared = 1;
+
+ return 0;
+}
+
+int _odp_timer_term_local(void)
+{
+ if (CONFIG_TIMER_PROFILE_INLINE) {
+ if (timer_local.prof_rounds) {
+ _ODP_PRINT("\n"
+ "Inline timer profiling for thread %i:\n"
+ "scan rounds: %" PRIu64 "\n"
+ "ave scan nsec: %.1f\n",
+ odp_thread_id(), timer_local.prof_rounds,
+ (double)timer_local.prof_nsec / timer_local.prof_rounds);
+ }
+ }
+
return 0;
}
diff --git a/platform/linux-generic/odp_timer_api.c b/platform/linux-generic/odp_timer_api.c
new file mode 100644
index 000000000..7a3f6efce
--- /dev/null
+++ b/platform/linux-generic/odp_timer_api.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/timer.h>
+
+/* Non-inlined functions for ABI compat mode */
+#define _ODP_NO_INLINE
+#include <odp/api/plat/timer_inlines.h>
diff --git a/platform/linux-generic/odp_timer_wheel.c b/platform/linux-generic/odp_timer_wheel.c
index f2c802a85..aee65e82d 100644
--- a/platform/linux-generic/odp_timer_wheel.c
+++ b/platform/linux-generic/odp_timer_wheel.c
@@ -1,9 +1,6 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <stdint.h>
@@ -14,6 +11,7 @@
#include <odp_timer_wheel_internal.h>
#include <odp_traffic_mngr_internal.h>
#include <odp_debug_internal.h>
+#include <odp_macros_internal.h>
/* The following constants can be changed either at compile time or run time
* as long as the following constraints are met (by the way REV stands for
@@ -93,9 +91,12 @@ typedef union {
timer_blk_t *timer_blk_list;
} current_timer_slot_t;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
typedef struct {
current_timer_slot_t slots[0];
} current_wheel_t;
+#pragma GCC diagnostic pop
typedef struct {
uint32_t count;
@@ -104,7 +105,7 @@ typedef struct {
uint32_t head_idx;
uint32_t tail_idx;
uint32_t max_idx;
- current_timer_slot_t entries[0];
+ current_timer_slot_t entries[];
} expired_ring_t;
typedef struct {
@@ -129,9 +130,12 @@ typedef union { /* Each general_timer_slot is 16 bytes long. */
list_entry_t list_entry;
} general_timer_slot_t;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
typedef struct {
general_timer_slot_t slots[0];
} general_wheel_t;
+#pragma GCC diagnostic pop
typedef struct {
/* Note that rev stands for revolution - one complete sweep through
@@ -625,7 +629,7 @@ static int timer_current_wheel_update(timer_wheels_t *timer_wheels,
slot_idx = wheel_desc->slot_idx;
num_slots = wheel_desc->num_slots;
max_ticks = wheel_desc->max_ticks;
- max_cnt = (uint32_t)MIN(elapsed_ticks, 32);
+ max_cnt = _ODP_MIN(elapsed_ticks, UINT32_C(32));
current_wheel = timer_wheels->current_wheel;
ret_code = 0;
rc = -1;
@@ -636,9 +640,7 @@ static int timer_current_wheel_update(timer_wheels_t *timer_wheels,
if (timer_slot->user_data != 0) {
rc = expired_timers_append(timer_wheels, timer_slot);
if (rc < 0)
- timer_wheels->
- expired_timers_ring->
- expired_ring_full_cnt++;
+ timer_wheels->expired_timers_ring->expired_ring_full_cnt++;
timer_slot->user_data = 0;
}
@@ -935,10 +937,10 @@ uint32_t _odp_timer_wheel_count(_odp_timer_wheel_t timer_wheel)
static void _odp_int_timer_wheel_desc_print(wheel_desc_t *wheel_desc,
uint32_t wheel_idx)
{
- ODP_DBG(" wheel=%u num_slots=%u ticks_shift=%u ticks_per_slot=%u"
- " ticks_per_rev=%" PRIu64 "\n",
- wheel_idx, wheel_desc->num_slots, wheel_desc->ticks_shift,
- wheel_desc->ticks_per_slot, wheel_desc->ticks_per_rev);
+ _ODP_PRINT(" wheel=%u num_slots=%u ticks_shift=%u ticks_per_slot=%u"
+ " ticks_per_rev=%" PRIu64 "\n",
+ wheel_idx, wheel_desc->num_slots, wheel_desc->ticks_shift,
+ wheel_desc->ticks_per_slot, wheel_desc->ticks_per_rev);
}
void _odp_timer_wheel_stats_print(_odp_timer_wheel_t timer_wheel)
@@ -950,28 +952,28 @@ void _odp_timer_wheel_stats_print(_odp_timer_wheel_t timer_wheel)
timer_wheels = (timer_wheels_t *)(uintptr_t)timer_wheel;
expired_ring = timer_wheels->expired_timers_ring;
- ODP_DBG("_odp_int_timer_wheel_stats current_ticks=%" PRIu64 "\n",
- timer_wheels->current_ticks);
+ _ODP_PRINT(" _odp_int_timer_wheel_stats current_ticks=%" PRIu64 "\n",
+ timer_wheels->current_ticks);
for (wheel_idx = 0; wheel_idx < 4; wheel_idx++)
- _odp_int_timer_wheel_desc_print(
- &timer_wheels->wheel_descs[wheel_idx],
- wheel_idx);
-
- ODP_DBG(" total timer_inserts=%" PRIu64 " timer_removes=%" PRIu64
- " insert_fails=%" PRIu64 "\n",
- timer_wheels->total_timer_inserts,
- timer_wheels->total_timer_removes,
- timer_wheels->insert_fail_cnt);
- ODP_DBG(" total_promote_cnt=%" PRIu64 " promote_fail_cnt=%"
- PRIu64 "\n", timer_wheels->total_promote_cnt,
- timer_wheels->promote_fail_cnt);
- ODP_DBG(" free_list_size=%u min_size=%u peak_size=%u\n",
- timer_wheels->free_list_size, timer_wheels->min_free_list_size,
- timer_wheels->peak_free_list_size);
- ODP_DBG(" expired_timers_ring size=%u count=%u "
- "peak_count=%u full_cnt=%u\n",
- expired_ring->max_idx + 1, expired_ring->count,
- expired_ring->peak_count, expired_ring->expired_ring_full_cnt);
+ _odp_int_timer_wheel_desc_print(&timer_wheels->wheel_descs[wheel_idx], wheel_idx);
+
+ _ODP_PRINT(" total timer_inserts=%" PRIu64 " timer_removes=%" PRIu64
+ " insert_fails=%" PRIu64 "\n",
+ timer_wheels->total_timer_inserts,
+ timer_wheels->total_timer_removes,
+ timer_wheels->insert_fail_cnt);
+ _ODP_PRINT(" total_promote_cnt=%" PRIu64 " promote_fail_cnt=%"
+ PRIu64 "\n", timer_wheels->total_promote_cnt,
+ timer_wheels->promote_fail_cnt);
+ _ODP_PRINT(" free_list_size=%u min_size=%u peak_size=%u\n",
+ timer_wheels->free_list_size,
+ timer_wheels->min_free_list_size,
+ timer_wheels->peak_free_list_size);
+ _ODP_PRINT(" expired_timers_ring size=%u count=%u "
+ "peak_count=%u full_cnt=%u\n",
+ expired_ring->max_idx + 1, expired_ring->count,
+ expired_ring->peak_count,
+ expired_ring->expired_ring_full_cnt);
}
void _odp_timer_wheel_destroy(_odp_timer_wheel_t timer_wheel)
diff --git a/platform/linux-generic/odp_traffic_mngr.c b/platform/linux-generic/odp_traffic_mngr.c
index 53e104711..2de851dfc 100644
--- a/platform/linux-generic/odp_traffic_mngr.c
+++ b/platform/linux-generic/odp_traffic_mngr.c
@@ -1,12 +1,32 @@
-/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
- *
- * Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 EZchip Semiconductor Ltd.
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Marvell
+ * Copyright (c) 2022 Nokia
*/
-#define _GNU_SOURCE
+#include <odp_posix_extensions.h>
+
+#include <odp/api/packet.h>
+#include <odp/api/packet_flags.h>
+#include <odp/api/std_types.h>
+#include <odp/api/time.h>
+
+#include <odp/api/plat/byteorder_inlines.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp/api/plat/time_inlines.h>
+
+#include <odp_packet_io_internal.h>
+#include <odp_traffic_mngr_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_init_internal.h>
+#include <odp_global_data.h>
+#include <odp_schedule_if.h>
+#include <odp_event_internal.h>
+
+#include <protocols/eth.h>
+#include <protocols/ip.h>
+
#include <stdint.h>
#include <string.h>
#include <malloc.h>
@@ -18,10 +38,6 @@
#include <sched.h>
#include <unistd.h>
#include <pthread.h>
-#include <odp/api/std_types.h>
-#include <protocols/eth.h>
-#include <protocols/ip.h>
-#include <odp_traffic_mngr_internal.h>
/* Local vars */
static const
@@ -35,57 +51,121 @@ _odp_int_name_kind_t PROFILE_TO_HANDLE_KIND[ODP_TM_NUM_PROFILES] = {
static const pkt_desc_t EMPTY_PKT_DESC = { .word = 0 };
#define MAX_PRIORITIES ODP_TM_MAX_PRIORITIES
-#define NUM_SHAPER_COLORS ODP_NUM_SHAPER_COLORS
-static tm_prop_t basic_prop_tbl[MAX_PRIORITIES][NUM_SHAPER_COLORS] = {
+/* Shaper BW limits in bits/sec */
+#define TM_MIN_SHAPER_BW 8000ULL
+#define TM_MAX_SHAPER_BW (100ULL * 1000ULL * 1000ULL * 1000ULL)
+
+/* Possible values for running the shaper algorithm. TM_SHAPER_GREEN means that
+ * the traffic is within the commit specification (rate and burst size),
+ * TM_SHAPER_YELLOW means that the traffic is within the peak specification
+ * (rate and burst size) and TM_SHAPER_RED means that the traffic is exceeding
+ * both its commit and peak specifications. Note that packets can also have an
+ * assigned packet color of ODP_PACKET_GREEN, ODP_PACKET_YELLOW or
+ * ODP_PACKET_RED, which has a different meaning and purpose than the shaper
+ * colors.
+ */
+typedef enum {
+ TM_SHAPER_GREEN, TM_SHAPER_YELLOW, TM_SHAPER_RED
+} tm_shaper_color_t;
+
+/* Number of enumeration values defined in tm_shaper_color_t type. */
+#define NUM_SHAPER_COLORS 3
+
+static const tm_prop_t basic_prop_tbl[MAX_PRIORITIES][NUM_SHAPER_COLORS] = {
[0] = {
- [ODP_TM_SHAPER_GREEN] = { 0, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 0, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 0, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 0, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 0, DECR_BOTH },
+ [TM_SHAPER_RED] = { 0, DELAY_PKT } },
[1] = {
- [ODP_TM_SHAPER_GREEN] = { 1, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 1, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 1, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 1, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 1, DECR_BOTH },
+ [TM_SHAPER_RED] = { 1, DELAY_PKT } },
[2] = {
- [ODP_TM_SHAPER_GREEN] = { 2, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 2, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 2, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 2, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 2, DECR_BOTH },
+ [TM_SHAPER_RED] = { 2, DELAY_PKT } },
[3] = {
- [ODP_TM_SHAPER_GREEN] = { 3, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 3, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 3, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 3, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 3, DECR_BOTH },
+ [TM_SHAPER_RED] = { 3, DELAY_PKT } },
[4] = {
- [ODP_TM_SHAPER_GREEN] = { 4, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 4, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 4, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 4, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 4, DECR_BOTH },
+ [TM_SHAPER_RED] = { 4, DELAY_PKT } },
[5] = {
- [ODP_TM_SHAPER_GREEN] = { 5, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 5, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 5, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 5, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 5, DECR_BOTH },
+ [TM_SHAPER_RED] = { 5, DELAY_PKT } },
[6] = {
- [ODP_TM_SHAPER_GREEN] = { 6, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 6, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 6, DELAY_PKT } },
+ [TM_SHAPER_GREEN] = { 6, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 6, DECR_BOTH },
+ [TM_SHAPER_RED] = { 6, DELAY_PKT } },
[7] = {
- [ODP_TM_SHAPER_GREEN] = { 7, DECR_BOTH },
- [ODP_TM_SHAPER_YELLOW] = { 7, DECR_BOTH },
- [ODP_TM_SHAPER_RED] = { 7, DELAY_PKT } }
+ [TM_SHAPER_GREEN] = { 7, DECR_BOTH },
+ [TM_SHAPER_YELLOW] = { 7, DECR_BOTH },
+ [TM_SHAPER_RED] = { 7, DELAY_PKT } }
};
-/* Profile tables. */
-static dynamic_tbl_t odp_tm_profile_tbls[ODP_TM_NUM_PROFILES];
-
-/* TM systems table. */
-static tm_system_t *odp_tm_systems[ODP_TM_MAX_NUM_SYSTEMS];
-
-static tm_system_group_t *tm_group_list;
-
-static odp_ticketlock_t tm_create_lock;
-static odp_ticketlock_t tm_profile_lock;
-static odp_barrier_t tm_first_enq;
-
-static int g_main_thread_cpu = -1;
-static int g_tm_cpu_num;
+#define MAX_SHAPER_PROFILES 128
+#define MAX_SCHED_PROFILES 128
+#define MAX_THRESHOLD_PROFILES 128
+#define MAX_WRED_PROFILES 128
+
+typedef struct {
+ struct {
+ tm_shaper_params_t profile[MAX_SHAPER_PROFILES];
+ odp_ticketlock_t lock;
+ } shaper;
+ struct {
+ tm_sched_params_t profile[MAX_SCHED_PROFILES];
+ odp_ticketlock_t lock;
+ } sched;
+ struct {
+ tm_queue_thresholds_t profile[MAX_THRESHOLD_PROFILES];
+ odp_ticketlock_t lock;
+ } threshold;
+ struct {
+ tm_wred_params_t profile[MAX_WRED_PROFILES];
+ odp_ticketlock_t lock;
+ } wred;
+} profile_tbl_t;
+
+typedef struct {
+ tm_system_t system[ODP_TM_MAX_NUM_SYSTEMS];
+
+ struct {
+ tm_system_group_t group[ODP_TM_MAX_NUM_SYSTEMS];
+ odp_ticketlock_t lock;
+ } system_group;
+ struct {
+ tm_queue_obj_t obj[ODP_TM_MAX_TM_QUEUES];
+ odp_ticketlock_t lock;
+ } queue_obj;
+ struct {
+ tm_node_obj_t obj[ODP_TM_MAX_NUM_TM_NODES];
+ odp_ticketlock_t lock;
+ } node_obj;
+
+ profile_tbl_t profile_tbl;
+
+ odp_ticketlock_t create_lock;
+ odp_ticketlock_t profile_lock;
+ odp_barrier_t first_enq;
+
+ int main_thread_cpu;
+ int cpu_num;
+
+ /* Service threads */
+ odp_bool_t main_loop_running;
+ odp_atomic_u64_t atomic_request_cnt;
+ odp_atomic_u64_t currently_serving_cnt;
+ odp_atomic_u64_t atomic_done_cnt;
+
+ odp_shm_t shm;
+} tm_global_t;
+
+static tm_global_t *tm_glb;
/* Forward function declarations. */
static void tm_queue_cnts_decrement(tm_system_t *tm_system,
@@ -99,22 +179,14 @@ static odp_bool_t tm_demote_pkt_desc(tm_system_t *tm_system,
tm_shaper_obj_t *timer_shaper,
pkt_desc_t *demoted_pkt_desc);
-static int queue_tm_reenq(queue_t queue, odp_buffer_hdr_t *buf_hdr)
+static inline tm_queue_obj_t *tm_qobj_from_index(uint32_t queue_id)
{
- odp_tm_queue_t tm_queue = MAKE_ODP_TM_QUEUE((uint8_t *)queue -
- offsetof(tm_queue_obj_t,
- tm_qentry));
- odp_packet_t pkt = _odp_packet_from_buf_hdr(buf_hdr);
-
- return odp_tm_enq(tm_queue, pkt);
+ return &tm_glb->queue_obj.obj[queue_id];
}
-static int queue_tm_reenq_multi(queue_t queue ODP_UNUSED,
- odp_buffer_hdr_t *buf[] ODP_UNUSED,
- int num ODP_UNUSED)
+static inline tm_node_obj_t *tm_nobj_from_index(uint32_t node_id)
{
- ODP_ABORT("Invalid call to queue_tm_reenq_multi()\n");
- return 0;
+ return &tm_glb->node_obj.obj[node_id];
}
static tm_queue_obj_t *get_tm_queue_obj(tm_system_t *tm_system,
@@ -171,7 +243,7 @@ static void tm_init_random_data(tm_random_data_t *tm_random_data)
byte_cnt = 0;
while (byte_cnt < 256)
byte_cnt += odp_random_data(&tm_random_data->buf[byte_cnt],
- 256 - byte_cnt, 1);
+ 256 - byte_cnt, ODP_RANDOM_BASIC);
tm_random_data->next_random_byte = 0;
}
@@ -209,74 +281,131 @@ static odp_bool_t tm_random_drop(tm_random_data_t *tm_random_data,
return drop;
}
-static void *alloc_entry_in_dynamic_tbl(dynamic_tbl_t *dynamic_tbl,
- uint32_t record_size,
- uint32_t *dynamic_idx_ptr)
+static void *alloc_entry_in_tbl(profile_tbl_t *profile_tbl,
+ profile_kind_t profile_kind,
+ uint32_t *idx)
{
- uint32_t num_allocd, new_num_allocd, idx;
- void **new_array_ptrs, *new_record;
-
- num_allocd = dynamic_tbl->num_allocd;
- if (num_allocd <= dynamic_tbl->num_used) {
- /* Need to alloc or realloc the array of ptrs. */
- if (num_allocd <= 32)
- new_num_allocd = 64;
- else
- new_num_allocd = 4 * num_allocd;
+ uint32_t i;
- new_array_ptrs = malloc(new_num_allocd * sizeof(void *));
- memset(new_array_ptrs, 0, new_num_allocd * sizeof(void *));
+ switch (profile_kind) {
+ case TM_SHAPER_PROFILE: {
+ tm_shaper_params_t *profile = NULL;
- if (dynamic_tbl->num_used != 0)
- memcpy(new_array_ptrs, dynamic_tbl->array_ptrs,
- dynamic_tbl->num_used * sizeof(void *));
+ odp_ticketlock_lock(&profile_tbl->shaper.lock);
+ for (i = 0; i < MAX_SHAPER_PROFILES; i++) {
+ if (profile_tbl->shaper.profile[i].status !=
+ TM_STATUS_FREE)
+ continue;
- if (dynamic_tbl->array_ptrs)
- free(dynamic_tbl->array_ptrs);
-
- dynamic_tbl->num_allocd = new_num_allocd;
- dynamic_tbl->array_ptrs = new_array_ptrs;
+ profile = &profile_tbl->shaper.profile[i];
+ memset(profile, 0, sizeof(tm_shaper_params_t));
+ profile->status = TM_STATUS_RESERVED;
+ *idx = i;
+ break;
+ }
+ odp_ticketlock_unlock(&profile_tbl->shaper.lock);
+ return profile;
+ }
+ case TM_SCHED_PROFILE: {
+ tm_sched_params_t *profile = NULL;
+
+ odp_ticketlock_lock(&profile_tbl->sched.lock);
+ for (i = 0; i < MAX_SCHED_PROFILES; i++) {
+ if (profile_tbl->sched.profile[i].status !=
+ TM_STATUS_FREE)
+ continue;
+
+ profile = &profile_tbl->sched.profile[i];
+ memset(profile, 0, sizeof(tm_sched_params_t));
+ profile->status = TM_STATUS_RESERVED;
+ *idx = i;
+ break;
+ }
+ odp_ticketlock_unlock(&profile_tbl->sched.lock);
+ return profile;
+ }
+ case TM_THRESHOLD_PROFILE: {
+ tm_queue_thresholds_t *profile = NULL;
+
+ odp_ticketlock_lock(&profile_tbl->threshold.lock);
+ for (i = 0; i < MAX_THRESHOLD_PROFILES; i++) {
+ if (profile_tbl->threshold.profile[i].status !=
+ TM_STATUS_FREE)
+ continue;
+
+ profile = &profile_tbl->threshold.profile[i];
+ memset(profile, 0, sizeof(tm_queue_thresholds_t));
+ profile->status = TM_STATUS_RESERVED;
+ *idx = i;
+ break;
+ }
+ odp_ticketlock_unlock(&profile_tbl->threshold.lock);
+ return profile;
}
+ case TM_WRED_PROFILE: {
+ tm_wred_params_t *profile = NULL;
+
+ odp_ticketlock_lock(&profile_tbl->wred.lock);
+ for (i = 0; i < MAX_WRED_PROFILES; i++) {
+ if (profile_tbl->wred.profile[i].status !=
+ TM_STATUS_FREE)
+ continue;
+
+ profile = &profile_tbl->wred.profile[i];
+ memset(profile, 0, sizeof(tm_wred_params_t));
+ profile->status = TM_STATUS_RESERVED;
+ *idx = i;
+ break;
+ }
+ odp_ticketlock_unlock(&profile_tbl->wred.lock);
+ return profile;
+ }
+ default:
+ _ODP_ERR("Invalid TM profile\n");
+ return NULL;
+ }
+}
- idx = dynamic_tbl->num_used;
- new_record = malloc(record_size);
- memset(new_record, 0, record_size);
+static void free_tbl_entry(profile_tbl_t *profile_tbl,
+ profile_kind_t profile_kind,
+ uint32_t idx)
+{
+ switch (profile_kind) {
+ case TM_SHAPER_PROFILE:
+ odp_ticketlock_lock(&profile_tbl->shaper.lock);
+ profile_tbl->shaper.profile[idx].status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&profile_tbl->shaper.lock);
+ return;
- dynamic_tbl->array_ptrs[idx] = new_record;
- dynamic_tbl->num_used++;
- if (dynamic_idx_ptr)
- *dynamic_idx_ptr = idx;
+ case TM_SCHED_PROFILE:
+ odp_ticketlock_lock(&profile_tbl->sched.lock);
+ profile_tbl->sched.profile[idx].status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&profile_tbl->sched.lock);
+ return;
- return new_record;
-}
+ case TM_THRESHOLD_PROFILE:
+ odp_ticketlock_lock(&profile_tbl->threshold.lock);
+ profile_tbl->threshold.profile[idx].status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&profile_tbl->threshold.lock);
+ return;
-static void free_dynamic_tbl_entry(dynamic_tbl_t *dynamic_tbl,
- uint32_t record_size ODP_UNUSED,
- uint32_t dynamic_idx)
-{
- void *record;
+ case TM_WRED_PROFILE:
+ odp_ticketlock_lock(&profile_tbl->wred.lock);
+ profile_tbl->wred.profile[idx].status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&profile_tbl->wred.lock);
+ return;
- record = dynamic_tbl->array_ptrs[dynamic_idx];
- if (record) {
- free(record);
- dynamic_tbl->array_ptrs[dynamic_idx] = NULL;
- dynamic_tbl->num_freed++;
- if (dynamic_tbl->num_freed == dynamic_tbl->num_used) {
- free(dynamic_tbl->array_ptrs);
- memset(dynamic_tbl, 0, sizeof(dynamic_tbl_t));
- }
+ default:
+ _ODP_ERR("Invalid TM profile\n");
+ return;
}
}
-static input_work_queue_t *input_work_queue_create(void)
+static void input_work_queue_init(input_work_queue_t *input_work_queue)
{
- input_work_queue_t *input_work_queue;
-
- input_work_queue = malloc(sizeof(input_work_queue_t));
memset(input_work_queue, 0, sizeof(input_work_queue_t));
odp_atomic_init_u64(&input_work_queue->queue_cnt, 0);
odp_ticketlock_init(&input_work_queue->lock);
- return input_work_queue;
}
static void input_work_queue_destroy(input_work_queue_t *input_work_queue)
@@ -286,7 +415,7 @@ static void input_work_queue_destroy(input_work_queue_t *input_work_queue)
* stopped new tm_enq() (et al) calls from succeeding.
*/
odp_ticketlock_lock(&input_work_queue->lock);
- free(input_work_queue);
+ memset(input_work_queue, 0, sizeof(input_work_queue_t));
}
static int input_work_queue_append(tm_system_t *tm_system,
@@ -296,7 +425,7 @@ static int input_work_queue_append(tm_system_t *tm_system,
input_work_item_t *entry_ptr;
uint32_t queue_cnt, tail_idx;
- input_work_queue = tm_system->input_work_queue;
+ input_work_queue = &tm_system->input_work_queue;
queue_cnt = odp_atomic_load_u64(&input_work_queue->queue_cnt);
if (INPUT_WORK_RING_SIZE <= queue_cnt) {
input_work_queue->enqueue_fail_cnt++;
@@ -354,11 +483,12 @@ static tm_system_t *tm_system_alloc(void)
/* Find an open slot in the odp_tm_systems array. */
for (tm_idx = 0; tm_idx < ODP_TM_MAX_NUM_SYSTEMS; tm_idx++) {
- if (!odp_tm_systems[tm_idx]) {
- tm_system = malloc(sizeof(tm_system_t));
+ if (tm_glb->system[tm_idx].status == TM_STATUS_FREE) {
+ tm_system = &tm_glb->system[tm_idx];
memset(tm_system, 0, sizeof(tm_system_t));
- odp_tm_systems[tm_idx] = tm_system;
tm_system->tm_idx = tm_idx;
+ tm_system->status = TM_STATUS_RESERVED;
+ odp_atomic_init_u32(&tm_system->is_idle, 0);
return tm_system;
}
}
@@ -368,47 +498,38 @@ static tm_system_t *tm_system_alloc(void)
static void tm_system_free(tm_system_t *tm_system)
{
- if (tm_system->root_node)
- free(tm_system->root_node);
-
- if (tm_system->queue_num_tbl)
- free(tm_system->queue_num_tbl);
-
- odp_tm_systems[tm_system->tm_idx] = NULL;
- free(tm_system);
+ tm_glb->system[tm_system->tm_idx].status = TM_STATUS_FREE;
}
static void *tm_common_profile_create(const char *name,
profile_kind_t profile_kind,
- uint32_t object_size,
tm_handle_t *profile_handle_ptr,
_odp_int_name_t *name_tbl_id_ptr)
{
_odp_int_name_kind_t handle_kind;
_odp_int_name_t name_tbl_id;
- dynamic_tbl_t *dynamic_tbl;
tm_handle_t profile_handle;
- uint32_t dynamic_tbl_idx;
void *object_ptr;
-
- /* Note that alloc_entry_in_dynamic_tbl will zero out all of the memory
- * that it allocates, so an additional memset here is unnnecessary. */
- dynamic_tbl = &odp_tm_profile_tbls[profile_kind];
- object_ptr = alloc_entry_in_dynamic_tbl(dynamic_tbl, object_size,
- &dynamic_tbl_idx);
- if (!object_ptr)
+ uint32_t idx = 0;
+
+ /* Note that alloc_entry_in_tbl will zero out all of the memory that it
+ * allocates, so an additional memset here is unnecessary. */
+ object_ptr = alloc_entry_in_tbl(&tm_glb->profile_tbl, profile_kind,
+ &idx);
+ if (!object_ptr) {
+ _ODP_ERR("No free profiles left\n");
return NULL;
+ }
handle_kind = PROFILE_TO_HANDLE_KIND[profile_kind];
- profile_handle = MAKE_PROFILE_HANDLE(profile_kind, dynamic_tbl_idx);
+ profile_handle = MAKE_PROFILE_HANDLE(profile_kind, idx);
name_tbl_id = ODP_INVALID_NAME;
if ((name != NULL) && (name[0] != '\0')) {
name_tbl_id = _odp_int_name_tbl_add(name, handle_kind,
profile_handle);
if (name_tbl_id == ODP_INVALID_NAME) {
- free_dynamic_tbl_entry(dynamic_tbl, object_size,
- dynamic_tbl_idx);
+ free_tbl_entry(&tm_glb->profile_tbl, profile_kind, idx);
return NULL;
}
}
@@ -420,20 +541,18 @@ static void *tm_common_profile_create(const char *name,
}
static int tm_common_profile_destroy(tm_handle_t profile_handle,
- uint32_t object_size,
_odp_int_name_t name_tbl_id)
{
profile_kind_t profile_kind;
- dynamic_tbl_t *dynamic_tbl;
- uint32_t dynamic_tbl_idx;
+ uint32_t idx;
if (name_tbl_id != ODP_INVALID_NAME)
_odp_int_name_tbl_delete(name_tbl_id);
- profile_kind = GET_PROFILE_KIND(profile_handle);
- dynamic_tbl = &odp_tm_profile_tbls[profile_kind];
- dynamic_tbl_idx = GET_TBL_IDX(profile_handle);
- free_dynamic_tbl_entry(dynamic_tbl, object_size, dynamic_tbl_idx);
+ profile_kind = GET_PROFILE_KIND(profile_handle);
+ idx = GET_TBL_IDX(profile_handle);
+ free_tbl_entry(&tm_glb->profile_tbl, profile_kind, idx);
+
return 0;
}
@@ -441,16 +560,31 @@ static void *tm_get_profile_params(tm_handle_t profile_handle,
profile_kind_t expected_profile_kind)
{
profile_kind_t profile_kind;
- dynamic_tbl_t *dynamic_tbl;
- uint32_t dynamic_tbl_idx;
+ uint32_t idx;
profile_kind = GET_PROFILE_KIND(profile_handle);
if (profile_kind != expected_profile_kind)
return NULL;
- dynamic_tbl = &odp_tm_profile_tbls[profile_kind];
- dynamic_tbl_idx = GET_TBL_IDX(profile_handle);
- return dynamic_tbl->array_ptrs[dynamic_tbl_idx];
+ idx = GET_TBL_IDX(profile_handle);
+
+ switch (profile_kind) {
+ case TM_SHAPER_PROFILE:
+ return &tm_glb->profile_tbl.shaper.profile[idx];
+
+ case TM_SCHED_PROFILE:
+ return &tm_glb->profile_tbl.sched.profile[idx];
+
+ case TM_THRESHOLD_PROFILE:
+ return &tm_glb->profile_tbl.threshold.profile[idx];
+
+ case TM_WRED_PROFILE:
+ return &tm_glb->profile_tbl.wred.profile[idx];
+
+ default:
+ _ODP_ERR("Invalid TM profile\n");
+ return NULL;
+ }
}
static uint64_t tm_bps_to_rate(uint64_t bps)
@@ -476,7 +610,7 @@ static uint64_t tm_max_time_delta(uint64_t rate)
return (1ULL << (26 + 30)) / rate;
}
-static void tm_shaper_params_cvt_to(odp_tm_shaper_params_t *odp_shaper_params,
+static void tm_shaper_params_cvt_to(const odp_tm_shaper_params_t *shaper_params,
tm_shaper_params_t *tm_shaper_params)
{
uint64_t commit_rate, peak_rate, max_commit_time_delta, highest_rate;
@@ -484,8 +618,8 @@ static void tm_shaper_params_cvt_to(odp_tm_shaper_params_t *odp_shaper_params,
uint32_t min_time_delta;
int64_t commit_burst, peak_burst;
- commit_rate = tm_bps_to_rate(odp_shaper_params->commit_bps);
- if ((odp_shaper_params->commit_bps == 0) || (commit_rate == 0)) {
+ commit_rate = tm_bps_to_rate(shaper_params->commit_rate);
+ if ((shaper_params->commit_rate == 0) || (commit_rate == 0)) {
tm_shaper_params->max_commit_time_delta = 0;
tm_shaper_params->max_peak_time_delta = 0;
tm_shaper_params->commit_rate = 0;
@@ -500,18 +634,18 @@ static void tm_shaper_params_cvt_to(odp_tm_shaper_params_t *odp_shaper_params,
}
max_commit_time_delta = tm_max_time_delta(commit_rate);
- commit_burst = (int64_t)odp_shaper_params->commit_burst;
+ commit_burst = (int64_t)shaper_params->commit_burst;
- peak_rate = tm_bps_to_rate(odp_shaper_params->peak_bps);
- if ((odp_shaper_params->peak_bps == 0) || (peak_rate == 0)) {
+ peak_rate = tm_bps_to_rate(shaper_params->peak_rate);
+ if ((!shaper_params->dual_rate) || (peak_rate == 0)) {
peak_rate = 0;
max_peak_time_delta = 0;
peak_burst = 0;
min_time_delta = (uint32_t)((1 << 26) / commit_rate);
} else {
max_peak_time_delta = tm_max_time_delta(peak_rate);
- peak_burst = (int64_t)odp_shaper_params->peak_burst;
- highest_rate = MAX(commit_rate, peak_rate);
+ peak_burst = (int64_t)shaper_params->peak_burst;
+ highest_rate = _ODP_MAX(commit_rate, peak_rate);
min_time_delta = (uint32_t)((1 << 26) / highest_rate);
}
@@ -523,8 +657,8 @@ static void tm_shaper_params_cvt_to(odp_tm_shaper_params_t *odp_shaper_params,
tm_shaper_params->max_commit = commit_burst << (26 - 3);
tm_shaper_params->max_peak = peak_burst << (26 - 3);
tm_shaper_params->min_time_delta = min_time_delta;
- tm_shaper_params->len_adjust = odp_shaper_params->shaper_len_adjust;
- tm_shaper_params->dual_rate = odp_shaper_params->dual_rate;
+ tm_shaper_params->len_adjust = shaper_params->shaper_len_adjust;
+ tm_shaper_params->dual_rate = shaper_params->dual_rate;
tm_shaper_params->enabled = 1;
}
@@ -543,8 +677,8 @@ static void tm_shaper_params_cvt_from(tm_shaper_params_t *tm_shaper_params,
commit_burst = tm_shaper_params->max_commit >> (26 - 3);
peak_burst = tm_shaper_params->max_peak >> (26 - 3);
- odp_shaper_params->commit_bps = commit_bps;
- odp_shaper_params->peak_bps = peak_bps;
+ odp_shaper_params->commit_rate = commit_bps;
+ odp_shaper_params->peak_rate = peak_bps;
odp_shaper_params->commit_burst = (uint32_t)commit_burst;
odp_shaper_params->peak_burst = (uint32_t)peak_burst;
odp_shaper_params->shaper_len_adjust = tm_shaper_params->len_adjust;
@@ -615,8 +749,8 @@ static void tm_sched_config_set(tm_shaper_obj_t *shaper_obj,
}
/* Any locking required and validity checks must be done by the caller! */
-static void tm_threshold_config_set(tm_wred_node_t *wred_node,
- odp_tm_threshold_t thresholds_profile)
+static int tm_threshold_config_set(tm_wred_node_t *wred_node,
+ odp_tm_threshold_t thresholds_profile)
{
tm_queue_thresholds_t *threshold_params;
@@ -626,15 +760,18 @@ static void tm_threshold_config_set(tm_wred_node_t *wred_node,
}
if (thresholds_profile == ODP_TM_INVALID)
- return;
+ return 0;
threshold_params = tm_get_profile_params(thresholds_profile,
TM_THRESHOLD_PROFILE);
- if (threshold_params == NULL)
- return;
+ if (threshold_params == NULL) {
+ _ODP_DBG("threshold_params is NULL\n");
+ return -1;
+ }
threshold_params->ref_cnt++;
wred_node->threshold_params = threshold_params;
+ return 0;
}
/* Any locking required and validity checks must be done by the caller! */
@@ -695,9 +832,9 @@ static void update_shaper_elapsed_time(tm_system_t *tm_system,
else
commit_inc = time_delta * shaper_params->commit_rate;
- shaper_obj->commit_cnt = (int64_t)MIN(max_commit, commit + commit_inc);
+ shaper_obj->commit_cnt = (int64_t)_ODP_MIN(max_commit, commit + commit_inc);
- if (shaper_params->peak_rate != 0) {
+ if (shaper_params->dual_rate) {
peak = shaper_obj->peak_cnt;
max_peak = shaper_params->max_peak;
if (shaper_params->max_peak_time_delta <= time_delta)
@@ -705,7 +842,7 @@ static void update_shaper_elapsed_time(tm_system_t *tm_system,
else
peak_inc = time_delta * shaper_params->peak_rate;
- shaper_obj->peak_cnt = (int64_t)MIN(max_peak, peak + peak_inc);
+ shaper_obj->peak_cnt = (int64_t)_ODP_MIN(max_peak, peak + peak_inc);
}
shaper_obj->last_update_time = tm_system->current_time;
@@ -725,22 +862,22 @@ static uint64_t time_till_not_red(tm_shaper_params_t *shaper_params,
commit_delay = (-shaper_obj->commit_cnt)
/ shaper_params->commit_rate;
- min_time_delay = MAX(shaper_obj->shaper_params->min_time_delta, 256);
- commit_delay = MAX(commit_delay, min_time_delay);
- if (shaper_params->peak_rate == 0)
+ min_time_delay = _ODP_MAX(shaper_obj->shaper_params->min_time_delta, UINT64_C(256));
+ commit_delay = _ODP_MAX(commit_delay, min_time_delay);
+ if (!shaper_params->dual_rate)
return commit_delay;
peak_delay = 0;
if (shaper_obj->peak_cnt < 0)
peak_delay = (-shaper_obj->peak_cnt) / shaper_params->peak_rate;
- peak_delay = MAX(peak_delay, min_time_delay);
+ peak_delay = _ODP_MAX(peak_delay, min_time_delay);
if (0 < shaper_obj->commit_cnt)
return peak_delay;
else if (0 < shaper_obj->peak_cnt)
return commit_delay;
else
- return MIN(commit_delay, peak_delay);
+ return _ODP_MIN(commit_delay, peak_delay);
}
static int delete_timer(tm_system_t *tm_system ODP_UNUSED,
@@ -812,7 +949,7 @@ static void tm_block_pkt(tm_system_t *tm_system,
tm_queue_obj->timer_shaper, pkt_desc);
else if (tm_queue_obj->timer_reason != NO_CALLBACK)
- ODP_DBG("%s timer_reason != NO_CALLBACK\n", __func__);
+ _ODP_DBG("%s timer_reason != NO_CALLBACK\n", __func__);
tm_queue_obj->blocked_cnt = 1;
tm_queue_obj->blocked_scheduler = schedulers_obj;
@@ -842,8 +979,7 @@ static odp_bool_t delay_pkt(tm_system_t *tm_system,
rc = _odp_timer_wheel_insert(tm_system->_odp_int_timer_wheel,
wakeup_time, timer_context);
if (rc < 0) {
- ODP_DBG("%s odp_timer_wheel_insert() failed rc=%d\n",
- __func__, rc);
+ _ODP_DBG("%s odp_timer_wheel_insert() failed rc=%d\n", __func__, rc);
return false;
}
@@ -931,7 +1067,7 @@ static odp_bool_t rm_pkt_from_shaper(tm_system_t *tm_system,
(shaper_action == DECR_COMMIT))
shaper_obj->commit_cnt -= tkn_count;
- if (shaper_params->peak_rate != 0)
+ if (shaper_params->dual_rate)
if ((shaper_action == DECR_BOTH) ||
(shaper_action == DECR_PEAK))
shaper_obj->peak_cnt -= tkn_count;
@@ -949,30 +1085,30 @@ static odp_bool_t run_shaper(tm_system_t *tm_system,
pkt_desc_t *pkt_desc,
uint8_t priority)
{
- odp_tm_shaper_color_t shaper_color;
+ tm_shaper_color_t shaper_color;
tm_shaper_params_t *shaper_params;
odp_bool_t output_change;
tm_prop_t propagation;
shaper_params = shaper_obj->shaper_params;
- shaper_color = ODP_TM_SHAPER_GREEN;
+ shaper_color = TM_SHAPER_GREEN;
if (shaper_params) {
update_shaper_elapsed_time(tm_system, shaper_params,
shaper_obj);
if (shaper_params->enabled) {
if (0 < shaper_obj->commit_cnt)
- shaper_color = ODP_TM_SHAPER_GREEN;
- else if (shaper_params->peak_rate == 0)
- shaper_color = ODP_TM_SHAPER_RED;
+ shaper_color = TM_SHAPER_GREEN;
+ else if (!shaper_params->dual_rate)
+ shaper_color = TM_SHAPER_RED;
else if (shaper_obj->peak_cnt <= 0)
- shaper_color = ODP_TM_SHAPER_RED;
+ shaper_color = TM_SHAPER_RED;
else
- shaper_color = ODP_TM_SHAPER_YELLOW;
+ shaper_color = TM_SHAPER_YELLOW;
- if (shaper_color == ODP_TM_SHAPER_GREEN)
+ if (shaper_color == TM_SHAPER_GREEN)
tm_system->shaper_green_cnt++;
- else if (shaper_color == ODP_TM_SHAPER_YELLOW)
+ else if (shaper_color == TM_SHAPER_YELLOW)
tm_system->shaper_yellow_cnt++;
else
tm_system->shaper_red_cnt++;
@@ -1050,8 +1186,8 @@ static int tm_set_finish_time(tm_schedulers_obj_t *schedulers_obj,
frame_weight = ((inverted_weight * frame_len) + (1 << 15)) >> 16;
sched_state = &schedulers_obj->sched_states[new_priority];
- base_virtual_time = MAX(prod_shaper_obj->virtual_finish_time,
- sched_state->base_virtual_time);
+ base_virtual_time = _ODP_MAX(prod_shaper_obj->virtual_finish_time,
+ sched_state->base_virtual_time);
virtual_finish_time = base_virtual_time + frame_weight;
prod_shaper_obj->virtual_finish_time = virtual_finish_time;
@@ -1083,7 +1219,7 @@ static odp_bool_t run_sched(tm_system_t *tm_system,
new_sched_state = &schedulers_obj->sched_states[priority];
prev_best_pkt_desc = new_sched_state->smallest_pkt_desc;
if (pkt_descs_equal(new_pkt_desc, &prev_best_pkt_desc)) {
- ODP_DBG("%s spurious execution ****\n", __func__);
+ _ODP_DBG("%s spurious execution ****\n", __func__);
return false;
}
}
@@ -1106,10 +1242,9 @@ static odp_bool_t run_sched(tm_system_t *tm_system,
* virtual finish time, just insert it into this
* sched_state's list sorted by virtual finish times.
*/
- rc = _odp_sorted_list_insert(
- tm_system->_odp_int_sorted_pool,
- new_sched_state->sorted_list,
- new_finish_time, new_pkt_desc->word);
+ rc = _odp_sorted_list_insert(tm_system->_odp_int_sorted_pool,
+ new_sched_state->sorted_list,
+ new_finish_time, new_pkt_desc->word);
if (0 <= rc) {
new_sched_state->sorted_list_cnt++;
@@ -1302,7 +1437,7 @@ static odp_bool_t tm_propagate_pkt_desc(tm_system_t *tm_system,
if (!shaper_change)
return false;
- schedulers_obj = tm_node_obj->schedulers_obj;
+ schedulers_obj = &tm_node_obj->schedulers_obj;
prev_sched_pkt = schedulers_obj->out_pkt_desc;
sched_was_empty = prev_sched_pkt.queue_num == 0;
sched_change = false;
@@ -1396,7 +1531,7 @@ static odp_bool_t tm_demote_pkt_desc(tm_system_t *tm_system,
if ((!blocked_scheduler) && (!timer_shaper))
return false;
- if (tm_node_obj->schedulers_obj == blocked_scheduler)
+ if (&tm_node_obj->schedulers_obj == blocked_scheduler)
return false;
/* See if this first shaper_obj is delaying the demoted_pkt_desc */
@@ -1422,7 +1557,7 @@ static odp_bool_t tm_demote_pkt_desc(tm_system_t *tm_system,
if ((!demoted_pkt_desc) && (!shaper_change))
return false;
- schedulers_obj = tm_node_obj->schedulers_obj;
+ schedulers_obj = &tm_node_obj->schedulers_obj;
prev_sched_pkt = schedulers_obj->out_pkt_desc;
sched_was_empty = prev_sched_pkt.queue_num == 0;
sched_change = false;
@@ -1535,17 +1670,17 @@ static odp_bool_t tm_consume_pkt_desc(tm_system_t *tm_system,
shaper_is_empty = new_shaper_pkt.queue_num == 0;
if (pkt_descs_equal(&new_shaper_pkt, sent_pkt_desc))
- ODP_DBG("%s shaper has old pkt_desc\n", __func__);
+ _ODP_DBG("%s shaper has old pkt_desc\n", __func__);
tm_node_obj = shaper_obj->next_tm_node;
while (!tm_node_obj->is_root_node) { /* not at egress */
- schedulers_obj = tm_node_obj->schedulers_obj;
+ schedulers_obj = &tm_node_obj->schedulers_obj;
prev_sched_pkt = schedulers_obj->out_pkt_desc;
sent_priority = schedulers_obj->highest_priority;
/* Verify that the scheduler output is the sent_pkt_desc. */
if (pkt_descs_not_equal(&prev_sched_pkt, sent_pkt_desc)) {
- ODP_DBG("%s sched has bad out pkt_desc\n", __func__);
+ _ODP_DBG("%s sched has bad out pkt_desc\n", __func__);
return false;
}
@@ -1563,17 +1698,17 @@ static odp_bool_t tm_consume_pkt_desc(tm_system_t *tm_system,
sched_is_empty = new_sched_pkt.queue_num == 0;
if (pkt_descs_equal(&new_sched_pkt, sent_pkt_desc))
- ODP_DBG("%s sched has old pkt_desc\n", __func__);
+ _ODP_DBG("%s sched has old pkt_desc\n", __func__);
if (pkt_descs_equal(&new_sched_pkt, sent_pkt_desc))
- ODP_DBG("%s scheduler has old pkt_desc\n", __func__);
+ _ODP_DBG("%s scheduler has old pkt_desc\n", __func__);
shaper_obj = &tm_node_obj->shaper_obj;
prev_shaper_pkt = shaper_obj->out_pkt_desc;
/* Verify that the shaper output is the sent_pkt_desc. */
if (pkt_descs_not_equal(&prev_shaper_pkt, sent_pkt_desc)) {
- ODP_DBG("%s shaper has bad out pkt_desc\n", __func__);
+ _ODP_DBG("%s shaper has bad out pkt_desc\n", __func__);
return false;
}
@@ -1590,7 +1725,7 @@ static odp_bool_t tm_consume_pkt_desc(tm_system_t *tm_system,
shaper_is_empty = new_shaper_pkt.queue_num == 0;
if (pkt_descs_equal(&new_shaper_pkt, sent_pkt_desc))
- ODP_DBG("%s shaper has old pkt_desc\n", __func__);
+ _ODP_DBG("%s shaper has old pkt_desc\n", __func__);
tm_node_obj = shaper_obj->next_tm_node;
}
@@ -1623,7 +1758,7 @@ static odp_bool_t tm_consume_sent_pkt(tm_system_t *tm_system,
pkt_len = sent_pkt_desc->pkt_len;
tm_queue_obj->pkts_consumed_cnt++;
- tm_queue_cnts_decrement(tm_system, tm_queue_obj->tm_wred_node,
+ tm_queue_cnts_decrement(tm_system, &tm_queue_obj->tm_wred_node,
tm_queue_obj->priority, pkt_len);
/* Get the next pkt in the tm_queue, if there is one. */
@@ -1664,7 +1799,7 @@ static odp_tm_percent_t tm_queue_fullness(tm_wred_params_t *wred_params,
return 0;
fullness = (10000 * current_cnt) / max_cnt;
- return (odp_tm_percent_t)MIN(fullness, 50000);
+ return (odp_tm_percent_t)_ODP_MIN(fullness, UINT64_C(50000));
}
static odp_bool_t tm_local_random_drop(tm_system_t *tm_system,
@@ -1868,6 +2003,12 @@ static void tm_queue_cnts_decrement(tm_system_t *tm_system,
odp_atomic_sub_u64(&queue_cnts->byte_cnt, frame_len);
}
+static inline void activate_packet_aging(odp_packet_hdr_t *pkt_hdr)
+{
+ if (odp_unlikely(pkt_hdr->p.flags.tx_aging))
+ pkt_hdr->tx_aging_ns = pkt_hdr->tx_aging_ns + odp_time_global_ns();
+}
+
static int tm_enqueue(tm_system_t *tm_system,
tm_queue_obj_t *tm_queue_obj,
odp_packet_t pkt)
@@ -1889,22 +2030,27 @@ static int tm_enqueue(tm_system_t *tm_system,
pkt_color = odp_packet_color(pkt);
drop_eligible = odp_packet_drop_eligible(pkt);
- initial_tm_wred_node = tm_queue_obj->tm_wred_node;
+ initial_tm_wred_node = &tm_queue_obj->tm_wred_node;
if (drop_eligible) {
drop = random_early_discard(tm_system, tm_queue_obj,
initial_tm_wred_node, pkt_color);
if (drop)
- return -1;
+ return -2;
}
work_item.queue_num = tm_queue_obj->queue_num;
work_item.pkt = pkt;
- sched_fn->order_lock();
+ if (tm_queue_obj->ordered_enqueue)
+ _odp_sched_fn->order_lock();
+
+ activate_packet_aging(packet_hdr(pkt));
rc = input_work_queue_append(tm_system, &work_item);
- sched_fn->order_unlock();
+
+ if (tm_queue_obj->ordered_enqueue)
+ _odp_sched_fn->order_unlock();
if (rc < 0) {
- ODP_DBG("%s work queue full\n", __func__);
+ _ODP_DBG("%s work queue full\n", __func__);
return rc;
}
@@ -1920,8 +2066,8 @@ static void egress_vlan_marking(tm_vlan_marking_t *vlan_marking,
_odp_vlanhdr_t vlan_hdr, *vlan_hdr_ptr;
_odp_ethhdr_t *ether_hdr_ptr;
odp_bool_t split_hdr;
- uint32_t hdr_len;
uint16_t old_tci, new_tci;
+ uint32_t hdr_len = 0;
ether_hdr_ptr = odp_packet_l2_ptr(odp_pkt, &hdr_len);
vlan_hdr_ptr = (_odp_vlanhdr_t *)(ether_hdr_ptr + 1);
@@ -1935,8 +2081,7 @@ static void egress_vlan_marking(tm_vlan_marking_t *vlan_marking,
* correctness rather then performance. */
split_hdr = hdr_len < (_ODP_ETHHDR_LEN + _ODP_VLANHDR_LEN);
if (split_hdr) {
- odp_packet_copy_to_mem(odp_pkt, _ODP_ETHHDR_LEN,
- _ODP_VLANHDR_LEN, &vlan_hdr);
+ odp_packet_copy_to_mem(odp_pkt, _ODP_ETHHDR_LEN, _ODP_VLANHDR_LEN, &vlan_hdr);
vlan_hdr_ptr = &vlan_hdr;
}
@@ -1950,8 +2095,7 @@ static void egress_vlan_marking(tm_vlan_marking_t *vlan_marking,
vlan_hdr_ptr->tci = odp_cpu_to_be_16(new_tci);
if (split_hdr)
- odp_packet_copy_from_mem(odp_pkt, _ODP_ETHHDR_LEN,
- _ODP_VLANHDR_LEN, &vlan_hdr);
+ odp_packet_copy_from_mem(odp_pkt, _ODP_ETHHDR_LEN, _ODP_VLANHDR_LEN, &vlan_hdr);
}
static void egress_ipv4_tos_marking(tm_tos_marking_t *tos_marking,
@@ -1959,8 +2103,9 @@ static void egress_ipv4_tos_marking(tm_tos_marking_t *tos_marking,
{
_odp_ipv4hdr_t ipv4_hdr, *ipv4_hdr_ptr;
odp_bool_t split_hdr;
- uint32_t hdr_len, l3_offset, old_chksum, ones_compl_sum, tos_diff;
+ uint32_t l3_offset, old_chksum, ones_compl_sum, tos_diff;
uint8_t old_tos, new_tos, ecn;
+ uint32_t hdr_len = 0;
l3_offset = odp_packet_l3_offset(odp_pkt);
ipv4_hdr_ptr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
@@ -1974,8 +2119,7 @@ static void egress_ipv4_tos_marking(tm_tos_marking_t *tos_marking,
* correctness rather then performance. */
split_hdr = hdr_len < 12;
if (split_hdr) {
- odp_packet_copy_to_mem(odp_pkt, l3_offset,
- _ODP_IPV4HDR_LEN, &ipv4_hdr);
+ odp_packet_copy_to_mem(odp_pkt, l3_offset, _ODP_IPV4HDR_LEN, &ipv4_hdr);
ipv4_hdr_ptr = &ipv4_hdr;
}
@@ -2016,8 +2160,7 @@ static void egress_ipv4_tos_marking(tm_tos_marking_t *tos_marking,
ipv4_hdr_ptr->tos = new_tos;
ipv4_hdr_ptr->chksum = odp_cpu_to_be_16((~ones_compl_sum) & 0xFFFF);
if (split_hdr)
- odp_packet_copy_from_mem(odp_pkt, l3_offset,
- _ODP_IPV4HDR_LEN, &ipv4_hdr);
+ odp_packet_copy_from_mem(odp_pkt, l3_offset, _ODP_IPV4HDR_LEN, &ipv4_hdr);
}
static void egress_ipv6_tc_marking(tm_tos_marking_t *tos_marking,
@@ -2025,8 +2168,9 @@ static void egress_ipv6_tc_marking(tm_tos_marking_t *tos_marking,
{
_odp_ipv6hdr_t ipv6_hdr, *ipv6_hdr_ptr;
odp_bool_t split_hdr;
- uint32_t hdr_len, old_ver_tc_flow, new_ver_tc_flow, l3_offset;
+ uint32_t old_ver_tc_flow, new_ver_tc_flow, l3_offset;
uint8_t old_tc, new_tc, ecn;
+ uint32_t hdr_len = 0;
l3_offset = odp_packet_l3_offset(odp_pkt);
ipv6_hdr_ptr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
@@ -2040,8 +2184,7 @@ static void egress_ipv6_tc_marking(tm_tos_marking_t *tos_marking,
* correctness rather then performance. */
split_hdr = hdr_len < 4;
if (split_hdr) {
- odp_packet_copy_to_mem(odp_pkt, l3_offset,
- _ODP_IPV6HDR_LEN, &ipv6_hdr);
+ odp_packet_copy_to_mem(odp_pkt, l3_offset, _ODP_IPV6HDR_LEN, &ipv6_hdr);
ipv6_hdr_ptr = &ipv6_hdr;
}
@@ -2069,8 +2212,7 @@ static void egress_ipv6_tc_marking(tm_tos_marking_t *tos_marking,
ipv6_hdr_ptr->ver_tc_flow = odp_cpu_to_be_32(new_ver_tc_flow);
if (split_hdr)
- odp_packet_copy_from_mem(odp_pkt, l3_offset,
- _ODP_IPV6HDR_LEN, &ipv6_hdr);
+ odp_packet_copy_from_mem(odp_pkt, l3_offset, _ODP_IPV6HDR_LEN, &ipv6_hdr);
}
static void tm_egress_marking(tm_system_t *tm_system, odp_packet_t odp_pkt)
@@ -2080,6 +2222,7 @@ static void tm_egress_marking(tm_system_t *tm_system, odp_packet_t odp_pkt)
tm_tos_marking_t *ip_marking;
color = odp_packet_color(odp_pkt);
+ _ODP_ASSERT(color < ODP_NUM_PACKET_COLORS);
if (odp_packet_has_vlan(odp_pkt)) {
vlan_marking = &tm_system->marking.vlan_marking[color];
@@ -2098,12 +2241,19 @@ static void tm_egress_marking(tm_system_t *tm_system, odp_packet_t odp_pkt)
}
}
+static inline odp_bool_t is_packet_aged(odp_packet_hdr_t *pkt_hdr)
+{
+ return pkt_hdr->p.flags.tx_aging && pkt_hdr->tx_aging_ns < odp_time_global_ns();
+}
+
static void tm_send_pkt(tm_system_t *tm_system, uint32_t max_sends)
{
tm_queue_obj_t *tm_queue_obj;
odp_packet_t odp_pkt;
pkt_desc_t *pkt_desc;
uint32_t cnt;
+ int ret;
+ pktio_entry_t *pktio_entry;
for (cnt = 1; cnt <= max_sends; cnt++) {
pkt_desc = &tm_system->egress_pkt_desc;
@@ -2121,12 +2271,29 @@ static void tm_send_pkt(tm_system_t *tm_system, uint32_t max_sends)
tm_egress_marking(tm_system, odp_pkt);
tm_system->egress_pkt_desc = EMPTY_PKT_DESC;
- if (tm_system->egress.egress_kind == ODP_TM_EGRESS_PKT_IO)
- odp_pktout_send(tm_system->pktout, &odp_pkt, 1);
- else if (tm_system->egress.egress_kind == ODP_TM_EGRESS_FN)
+ if (tm_system->egress.egress_kind == ODP_TM_EGRESS_PKT_IO) {
+ pktio_entry = get_pktio_entry(tm_system->pktout.pktio);
+ if (odp_unlikely(_odp_pktio_tx_aging_enabled(pktio_entry) &&
+ is_packet_aged(packet_hdr(odp_pkt))))
+ ret = 0; /* Aged packet handled as a discard */
+ else
+ ret = odp_pktout_send(tm_system->pktout, &odp_pkt, 1);
+ if (odp_unlikely(ret != 1)) {
+ if (odp_unlikely(_odp_pktio_tx_compl_enabled(pktio_entry)))
+ _odp_pktio_process_tx_compl(pktio_entry, &odp_pkt, 1);
+ odp_packet_free(odp_pkt);
+ if (odp_unlikely(ret < 0))
+ odp_atomic_inc_u64(&tm_queue_obj->stats.errors);
+ else
+ odp_atomic_inc_u64(&tm_queue_obj->stats.discards);
+ } else {
+ odp_atomic_inc_u64(&tm_queue_obj->stats.packets);
+ }
+ } else if (tm_system->egress.egress_kind == ODP_TM_EGRESS_FN) {
tm_system->egress.egress_fcn(odp_pkt);
- else
+ } else {
return;
+ }
tm_queue_obj->sent_pkt = tm_queue_obj->pkt;
tm_queue_obj->sent_pkt_desc = tm_queue_obj->in_pkt_desc;
@@ -2155,8 +2322,7 @@ static int tm_process_input_work_queue(tm_system_t *tm_system,
for (cnt = 1; cnt <= pkts_to_process; cnt++) {
rc = input_work_queue_remove(input_work_queue, &work_item);
if (rc < 0) {
- ODP_DBG("%s input_work_queue_remove() failed\n",
- __func__);
+ _ODP_DBG("%s input_work_queue_remove() failed\n", __func__);
return rc;
}
@@ -2173,9 +2339,8 @@ static int tm_process_input_work_queue(tm_system_t *tm_system,
/* If the tm_queue_obj already has a pkt to work with,
* then just add this new pkt to the associated
* _odp_int_pkt_queue. */
- (void)_odp_pkt_queue_append(
- tm_system->_odp_int_queue_pool,
- tm_queue_obj->_odp_int_pkt_queue, pkt);
+ (void)_odp_pkt_queue_append(tm_system->_odp_int_queue_pool,
+ tm_queue_obj->_odp_int_pkt_queue, pkt);
tm_queue_obj->pkts_enqueued_cnt++;
} else {
/* If the tm_queue_obj doesn't have a pkt to work
@@ -2226,7 +2391,7 @@ static int tm_process_expired_timers(tm_system_t *tm_system,
if (tm_queue_obj->timer_cancels_outstanding != 0)
tm_queue_obj->timer_cancels_outstanding--;
else
- ODP_DBG("%s bad timer return\n", __func__);
+ _ODP_DBG("%s bad timer return\n", __func__);
return work_done;
}
@@ -2247,31 +2412,16 @@ static int tm_process_expired_timers(tm_system_t *tm_system,
return work_done;
}
-static volatile uint64_t busy_wait_counter;
-
-static odp_bool_t main_loop_running;
-static odp_atomic_u64_t atomic_request_cnt;
-static odp_atomic_u64_t currently_serving_cnt;
-static odp_atomic_u64_t atomic_done_cnt;
-
-static void busy_wait(uint32_t iterations)
-{
- uint32_t cnt;
-
- for (cnt = 1; cnt <= iterations; cnt++)
- busy_wait_counter++;
-}
-
static void signal_request(void)
{
- uint64_t my_request_num, serving_cnt;
+ uint64_t request_num, serving;
- my_request_num = odp_atomic_fetch_inc_u64(&atomic_request_cnt) + 1;
+ request_num = odp_atomic_fetch_inc_u64(&tm_glb->atomic_request_cnt) + 1;
- serving_cnt = odp_atomic_load_u64(&currently_serving_cnt);
- while (serving_cnt != my_request_num) {
- busy_wait(100);
- serving_cnt = odp_atomic_load_u64(&currently_serving_cnt);
+ serving = odp_atomic_load_u64(&tm_glb->currently_serving_cnt);
+ while (serving != request_num) {
+ _odp_cpu_pause();
+ serving = odp_atomic_load_u64(&tm_glb->currently_serving_cnt);
}
}
@@ -2279,26 +2429,26 @@ static void check_for_request(void)
{
uint64_t request_num, serving_cnt, done_cnt;
- request_num = odp_atomic_load_u64(&atomic_request_cnt);
- serving_cnt = odp_atomic_load_u64(&currently_serving_cnt);
+ request_num = odp_atomic_load_u64(&tm_glb->atomic_request_cnt);
+ serving_cnt = odp_atomic_load_u64(&tm_glb->currently_serving_cnt);
if (serving_cnt == request_num)
return;
/* Signal the other requesting thread to proceed and then
* wait for their done indication */
- odp_atomic_inc_u64(&currently_serving_cnt);
- busy_wait(100);
+ odp_atomic_inc_u64(&tm_glb->currently_serving_cnt);
+ _odp_cpu_pause();
- done_cnt = odp_atomic_load_u64(&atomic_done_cnt);
+ done_cnt = odp_atomic_load_u64(&tm_glb->atomic_done_cnt);
while (done_cnt != request_num) {
- busy_wait(100);
- done_cnt = odp_atomic_load_u64(&atomic_done_cnt);
+ _odp_cpu_pause();
+ done_cnt = odp_atomic_load_u64(&tm_glb->atomic_done_cnt);
}
}
static void signal_request_done(void)
{
- odp_atomic_inc_u64(&atomic_done_cnt);
+ odp_atomic_inc_u64(&tm_glb->atomic_done_cnt);
}
static int thread_affinity_get(odp_cpumask_t *odp_cpu_mask)
@@ -2310,8 +2460,7 @@ static int thread_affinity_get(odp_cpumask_t *odp_cpu_mask)
CPU_ZERO(&linux_cpu_set);
rc = sched_getaffinity(0, sizeof(cpu_set_t), &linux_cpu_set);
if (rc != 0) {
- ODP_DBG("%s sched_getaffinity failed with rc=%d\n",
- __func__, rc);
+ _ODP_DBG("%s sched_getaffinity failed with rc=%d\n", __func__, rc);
return -1;
}
@@ -2333,20 +2482,20 @@ static void *tm_system_thread(void *arg)
uint32_t destroying, work_queue_cnt, timer_cnt;
int rc;
- rc = odp_init_local((odp_instance_t)odp_global_data.main_pid,
+ rc = odp_init_local((odp_instance_t)odp_global_ro.main_pid,
ODP_THREAD_WORKER);
- ODP_ASSERT(rc == 0);
+ _ODP_ASSERT(rc == 0);
tm_group = arg;
tm_system = tm_group->first_tm_system;
_odp_int_timer_wheel = tm_system->_odp_int_timer_wheel;
- input_work_queue = tm_system->input_work_queue;
+ input_work_queue = &tm_system->input_work_queue;
/* Wait here until we have seen the first enqueue operation. */
odp_barrier_wait(&tm_group->tm_group_barrier);
- main_loop_running = true;
+ tm_glb->main_loop_running = true;
- destroying = odp_atomic_load_u64(&tm_system->destroying);
+ destroying = odp_atomic_load_acq_u64(&tm_system->destroying);
current_ns = odp_time_to_ns(odp_time_local());
_odp_timer_wheel_start(_odp_int_timer_wheel, current_ns);
@@ -2387,18 +2536,20 @@ static void *tm_system_thread(void *arg)
current_ns = odp_time_to_ns(odp_time_local());
tm_system->current_time = current_ns;
- tm_system->is_idle = (timer_cnt == 0) &&
- (work_queue_cnt == 0);
- destroying = odp_atomic_load_u64(&tm_system->destroying);
+ odp_atomic_store_rel_u32(&tm_system->is_idle,
+ (timer_cnt == 0) && (work_queue_cnt == 0));
+ destroying = odp_atomic_load_acq_u64(&tm_system->destroying);
/* Advance to the next tm_system in the tm_system_group. */
tm_system = tm_system->next;
_odp_int_timer_wheel = tm_system->_odp_int_timer_wheel;
- input_work_queue = tm_system->input_work_queue;
+ input_work_queue = &tm_system->input_work_queue;
}
odp_barrier_wait(&tm_system->tm_system_destroy_barrier);
- odp_term_local();
+ if (odp_term_local() < 0)
+ _ODP_ERR("Term local failed\n");
+
return NULL;
}
@@ -2407,12 +2558,14 @@ odp_bool_t odp_tm_is_idle(odp_tm_t odp_tm)
tm_system_t *tm_system;
tm_system = GET_TM_SYSTEM(odp_tm);
- return tm_system->is_idle;
+ return odp_atomic_load_acq_u32(&tm_system->is_idle);
}
void odp_tm_requirements_init(odp_tm_requirements_t *requirements)
{
memset(requirements, 0, sizeof(odp_tm_requirements_t));
+
+ requirements->pkt_prio_mode = ODP_TM_PKT_PRIO_MODE_PRESERVE;
}
void odp_tm_egress_init(odp_tm_egress_t *egress)
@@ -2420,21 +2573,8 @@ void odp_tm_egress_init(odp_tm_egress_t *egress)
memset(egress, 0, sizeof(odp_tm_egress_t));
}
-static tm_node_obj_t *create_dummy_root_node(void)
-{
- tm_node_obj_t *tm_node_obj;
-
- tm_node_obj = malloc(sizeof(tm_node_obj_t));
- if (!tm_node_obj)
- return NULL;
-
- memset(tm_node_obj, 0, sizeof(tm_node_obj_t));
- tm_node_obj->is_root_node = true;
- return tm_node_obj;
-}
-
-int odp_tm_capabilities(odp_tm_capabilities_t capabilities[] ODP_UNUSED,
- uint32_t capabilities_size)
+static int tm_capabilities(odp_tm_capabilities_t capabilities[],
+ uint32_t capabilities_size)
{
odp_tm_level_capabilities_t *per_level_cap;
odp_tm_capabilities_t *cap_ptr;
@@ -2447,6 +2587,9 @@ int odp_tm_capabilities(odp_tm_capabilities_t capabilities[] ODP_UNUSED,
cap_ptr = &capabilities[0];
memset(cap_ptr, 0, sizeof(odp_tm_capabilities_t));
+ if (odp_global_ro.init_param.mem_model == ODP_MEM_MODEL_PROCESS)
+ return 1;
+
cap_ptr->max_tm_queues = ODP_TM_MAX_TM_QUEUES;
cap_ptr->max_levels = ODP_TM_MAX_LEVELS;
cap_ptr->tm_queue_shaper_supported = true;
@@ -2457,6 +2600,24 @@ int odp_tm_capabilities(odp_tm_capabilities_t capabilities[] ODP_UNUSED,
cap_ptr->ecn_marking_supported = true;
cap_ptr->drop_prec_marking_supported = true;
+ cap_ptr->tm_queue_threshold.byte = true;
+ cap_ptr->tm_queue_threshold.packet = true;
+ cap_ptr->tm_queue_threshold.byte_and_packet = true;
+
+ cap_ptr->tm_queue_query_flags = (ODP_TM_QUERY_PKT_CNT |
+ ODP_TM_QUERY_BYTE_CNT |
+ ODP_TM_QUERY_THRESHOLDS);
+ cap_ptr->max_schedulers_per_node = ODP_TM_MAX_PRIORITIES;
+
+ cap_ptr->dynamic_topology_update = true;
+ cap_ptr->dynamic_shaper_update = true;
+ cap_ptr->dynamic_sched_update = true;
+ cap_ptr->dynamic_wred_update = true;
+ cap_ptr->dynamic_threshold_update = true;
+
+ /* We only support pkt priority mode preserve */
+ cap_ptr->pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE] = true;
+
for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
cap_ptr->marking_colors_supported[color] = true;
@@ -2468,17 +2629,55 @@ int odp_tm_capabilities(odp_tm_capabilities_t capabilities[] ODP_UNUSED,
per_level_cap->max_priority = ODP_TM_MAX_PRIORITIES - 1;
per_level_cap->min_weight = ODP_TM_MIN_SCHED_WEIGHT;
per_level_cap->max_weight = ODP_TM_MAX_SCHED_WEIGHT;
+ per_level_cap->min_burst = 0;
+ per_level_cap->max_burst = UINT32_MAX;
+ per_level_cap->min_rate = TM_MIN_SHAPER_BW;
+ per_level_cap->max_rate = TM_MAX_SHAPER_BW;
per_level_cap->tm_node_shaper_supported = true;
per_level_cap->tm_node_wred_supported = true;
per_level_cap->tm_node_dual_slope_supported = true;
per_level_cap->fair_queuing_supported = true;
per_level_cap->weights_supported = true;
+
+ per_level_cap->tm_node_threshold.byte = true;
+ per_level_cap->tm_node_threshold.packet = true;
+ per_level_cap->tm_node_threshold.byte_and_packet = true;
}
+ cap_ptr->queue_stats.counter.discards = 1;
+ cap_ptr->queue_stats.counter.errors = 1;
+ cap_ptr->queue_stats.counter.packets = 1;
+
return 1;
}
+int odp_tm_egress_capabilities(odp_tm_capabilities_t *capabilities,
+ const odp_tm_egress_t *egress)
+{
+ pktio_entry_t *entry;
+ int ret;
+
+ memset(capabilities, 0, sizeof(odp_tm_capabilities_t));
+ if (egress->egress_kind == ODP_TM_EGRESS_PKT_IO) {
+ entry = get_pktio_entry(egress->pktio);
+ if (entry == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n",
+ (uintptr_t)egress->pktio);
+ return -1;
+ }
+
+ /* Report not capable if pktout mode is not TM */
+ if (entry->param.out_mode != ODP_PKTOUT_MODE_TM)
+ return 0;
+ }
+
+ ret = tm_capabilities(capabilities, 1);
+ if (ret <= 0)
+ return -1;
+ return 0;
+}
+
static void tm_system_capabilities_set(odp_tm_capabilities_t *cap_ptr,
odp_tm_requirements_t *req_ptr)
{
@@ -2486,19 +2685,20 @@ static void tm_system_capabilities_set(odp_tm_capabilities_t *cap_ptr,
odp_tm_level_capabilities_t *per_level_cap;
odp_packet_color_t color;
odp_bool_t shaper_supported, wred_supported;
- odp_bool_t dual_slope;
+ odp_bool_t dual_slope, threshold;
uint32_t num_levels, level_idx, max_nodes;
uint32_t max_queues, max_fanin;
- uint8_t max_priority, min_weight, max_weight;
+ uint32_t min_weight, max_weight;
+ uint8_t max_priority;
- num_levels = MAX(MIN(req_ptr->num_levels, ODP_TM_MAX_LEVELS), 1);
+ num_levels = _ODP_MAX(_ODP_MIN(req_ptr->num_levels, ODP_TM_MAX_LEVELS), 1);
memset(cap_ptr, 0, sizeof(odp_tm_capabilities_t));
- max_queues = MIN(req_ptr->max_tm_queues,
- ODP_TM_MAX_NUM_TM_NODES);
+ max_queues = _ODP_MIN(req_ptr->max_tm_queues, (uint32_t)ODP_TM_MAX_NUM_TM_NODES);
shaper_supported = req_ptr->tm_queue_shaper_needed;
wred_supported = req_ptr->tm_queue_wred_needed;
dual_slope = req_ptr->tm_queue_dual_slope_needed;
+ threshold = req_ptr->tm_queue_threshold_needed;
cap_ptr->max_tm_queues = max_queues;
cap_ptr->max_levels = num_levels;
@@ -2510,6 +2710,23 @@ static void tm_system_capabilities_set(odp_tm_capabilities_t *cap_ptr,
cap_ptr->drop_prec_marking_supported =
req_ptr->drop_prec_marking_needed;
+ cap_ptr->tm_queue_threshold.byte = threshold;
+ cap_ptr->tm_queue_threshold.packet = threshold;
+ cap_ptr->tm_queue_threshold.byte_and_packet = threshold;
+
+ cap_ptr->tm_queue_query_flags = (ODP_TM_QUERY_PKT_CNT |
+ ODP_TM_QUERY_BYTE_CNT |
+ ODP_TM_QUERY_THRESHOLDS);
+ cap_ptr->max_schedulers_per_node = ODP_TM_MAX_PRIORITIES;
+
+ cap_ptr->dynamic_topology_update = true;
+ cap_ptr->dynamic_shaper_update = true;
+ cap_ptr->dynamic_sched_update = true;
+ cap_ptr->dynamic_wred_update = true;
+ cap_ptr->dynamic_threshold_update = true;
+
+ cap_ptr->pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE] = true;
+
for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
cap_ptr->marking_colors_supported[color] =
req_ptr->marking_colors_needed[color];
@@ -2518,31 +2735,45 @@ static void tm_system_capabilities_set(odp_tm_capabilities_t *cap_ptr,
per_level_cap = &cap_ptr->per_level[level_idx];
per_level_req = &req_ptr->per_level[level_idx];
- max_nodes = MIN(per_level_req->max_num_tm_nodes,
- ODP_TM_MAX_NUM_TM_NODES);
- max_fanin = MIN(per_level_req->max_fanin_per_node, 1024);
- max_priority = MIN(per_level_req->max_priority,
- ODP_TM_MAX_PRIORITIES - 1);
- min_weight = MAX(per_level_req->min_weight,
- ODP_TM_MIN_SCHED_WEIGHT);
- max_weight = MIN(per_level_req->max_weight,
- ODP_TM_MAX_SCHED_WEIGHT);
+ max_nodes = _ODP_MIN(per_level_req->max_num_tm_nodes,
+ (uint32_t)ODP_TM_MAX_NUM_TM_NODES);
+ max_fanin = _ODP_MIN(per_level_req->max_fanin_per_node,
+ UINT32_C(1024));
+ max_priority = _ODP_MIN(per_level_req->max_priority,
+ ODP_TM_MAX_PRIORITIES - 1);
+ min_weight = _ODP_MAX(per_level_req->min_weight,
+ ODP_TM_MIN_SCHED_WEIGHT);
+ max_weight = _ODP_MIN(per_level_req->max_weight,
+ ODP_TM_MAX_SCHED_WEIGHT);
shaper_supported = per_level_req->tm_node_shaper_needed;
wred_supported = per_level_req->tm_node_wred_needed;
dual_slope = per_level_req->tm_node_dual_slope_needed;
+ threshold = per_level_req->tm_node_threshold_needed;
per_level_cap->max_num_tm_nodes = max_nodes;
per_level_cap->max_fanin_per_node = max_fanin;
per_level_cap->max_priority = max_priority;
per_level_cap->min_weight = min_weight;
per_level_cap->max_weight = max_weight;
+ per_level_cap->min_burst = 0;
+ per_level_cap->max_burst = UINT32_MAX;
+ per_level_cap->min_rate = TM_MIN_SHAPER_BW;
+ per_level_cap->max_rate = TM_MAX_SHAPER_BW;
per_level_cap->tm_node_shaper_supported = shaper_supported;
per_level_cap->tm_node_wred_supported = wred_supported;
per_level_cap->tm_node_dual_slope_supported = dual_slope;
per_level_cap->fair_queuing_supported = true;
per_level_cap->weights_supported = true;
+
+ per_level_cap->tm_node_threshold.byte = threshold;
+ per_level_cap->tm_node_threshold.packet = threshold;
+ per_level_cap->tm_node_threshold.byte_and_packet = threshold;
}
+
+ cap_ptr->queue_stats.counter.discards = 1;
+ cap_ptr->queue_stats.counter.errors = 1;
+ cap_ptr->queue_stats.counter.packets = 1;
}
static int affinitize_main_thread(void)
@@ -2560,7 +2791,7 @@ static int affinitize_main_thread(void)
* just record this value and return. */
cpu_count = odp_cpumask_count(&odp_cpu_mask);
if (cpu_count == 1) {
- g_main_thread_cpu = odp_cpumask_first(&odp_cpu_mask);
+ tm_glb->main_thread_cpu = odp_cpumask_first(&odp_cpu_mask);
return 0;
} else if (cpu_count == 0) {
return -1;
@@ -2572,10 +2803,9 @@ static int affinitize_main_thread(void)
CPU_SET(cpu_num, &linux_cpu_set);
rc = sched_setaffinity(0, sizeof(cpu_set_t), &linux_cpu_set);
if (rc == 0)
- g_main_thread_cpu = cpu_num;
+ tm_glb->main_thread_cpu = cpu_num;
else
- ODP_DBG("%s sched_setaffinity failed with rc=%d\n",
- __func__, rc);
+ _ODP_DBG("%s sched_setaffinity failed with rc=%d\n", __func__, rc);
return rc;
}
@@ -2585,32 +2815,32 @@ static uint32_t tm_thread_cpu_select(void)
int cpu_count, cpu;
odp_cpumask_default_worker(&odp_cpu_mask, 0);
- if ((g_main_thread_cpu != -1) &&
- odp_cpumask_isset(&odp_cpu_mask, g_main_thread_cpu))
- odp_cpumask_clr(&odp_cpu_mask, g_main_thread_cpu);
+ if ((tm_glb->main_thread_cpu != -1) &&
+ odp_cpumask_isset(&odp_cpu_mask, tm_glb->main_thread_cpu))
+ odp_cpumask_clr(&odp_cpu_mask, tm_glb->main_thread_cpu);
cpu_count = odp_cpumask_count(&odp_cpu_mask);
if (cpu_count < 1) {
odp_cpumask_all_available(&odp_cpu_mask);
- if ((g_main_thread_cpu != -1) &&
- odp_cpumask_isset(&odp_cpu_mask, g_main_thread_cpu))
+ if ((tm_glb->main_thread_cpu != -1) &&
+ odp_cpumask_isset(&odp_cpu_mask, tm_glb->main_thread_cpu))
cpu_count = odp_cpumask_count(&odp_cpu_mask);
if (cpu_count < 1)
odp_cpumask_all_available(&odp_cpu_mask);
}
- if (g_tm_cpu_num == 0) {
+ if (tm_glb->cpu_num == 0) {
cpu = odp_cpumask_first(&odp_cpu_mask);
} else {
- cpu = odp_cpumask_next(&odp_cpu_mask, g_tm_cpu_num);
+ cpu = odp_cpumask_next(&odp_cpu_mask, tm_glb->cpu_num);
if (cpu == -1) {
- g_tm_cpu_num = 0;
+ tm_glb->cpu_num = 0;
cpu = odp_cpumask_first(&odp_cpu_mask);
}
}
- g_tm_cpu_num++;
+ tm_glb->cpu_num++;
return cpu;
}
@@ -2630,68 +2860,28 @@ static int tm_thread_create(tm_system_group_t *tm_group)
rc = pthread_create(&tm_group->thread, &tm_group->attr,
tm_system_thread, tm_group);
if (rc != 0)
- ODP_DBG("Failed to start thread on cpu num=%u\n", cpu_num);
-
+ _ODP_ERR("Failed to start TM thread on CPU #%u: %d\n", cpu_num, rc);
return rc;
}
-static _odp_tm_group_t _odp_tm_group_create(const char *name ODP_UNUSED)
-{
- tm_system_group_t *tm_group, *first_tm_group, *second_tm_group;
-
- tm_group = malloc(sizeof(tm_system_group_t));
- memset(tm_group, 0, sizeof(tm_system_group_t));
- odp_barrier_init(&tm_group->tm_group_barrier, 2);
-
- /* Add this group to the tm_group_list linked list. */
- if (tm_group_list == NULL) {
- tm_group_list = tm_group;
- tm_group->next = tm_group;
- tm_group->prev = tm_group;
- } else {
- first_tm_group = tm_group_list;
- second_tm_group = first_tm_group->next;
- first_tm_group->next = tm_group;
- second_tm_group->prev = tm_group;
- tm_group->next = second_tm_group;
- tm_group->prev = first_tm_group;
- }
-
- return MAKE_ODP_TM_SYSTEM_GROUP(tm_group);
-}
-
static void _odp_tm_group_destroy(_odp_tm_group_t odp_tm_group)
{
- tm_system_group_t *tm_group, *prev_tm_group, *next_tm_group;
+ tm_system_group_t *tm_group;
int rc;
tm_group = GET_TM_GROUP(odp_tm_group);
/* Wait for the thread to exit. */
- ODP_ASSERT(tm_group->num_tm_systems <= 1);
+ _ODP_ASSERT(tm_group->num_tm_systems <= 1);
rc = pthread_join(tm_group->thread, NULL);
- ODP_ASSERT(rc == 0);
+ _ODP_ASSERT(rc == 0);
pthread_attr_destroy(&tm_group->attr);
- if (g_tm_cpu_num > 0)
- g_tm_cpu_num--;
-
- /* Remove this group from the tm_group_list linked list. Special case
- * when this is the last tm_group in the linked list. */
- prev_tm_group = tm_group->prev;
- next_tm_group = tm_group->next;
- if (prev_tm_group == next_tm_group) {
- ODP_ASSERT(tm_group_list == tm_group);
- tm_group_list = NULL;
- } else {
- prev_tm_group->next = next_tm_group;
- next_tm_group->prev = prev_tm_group;
- if (tm_group_list == tm_group)
- tm_group_list = next_tm_group;
- }
+ if (tm_glb->cpu_num > 0)
+ tm_glb->cpu_num--;
- tm_group->prev = NULL;
- tm_group->next = NULL;
- free(tm_group);
+ odp_ticketlock_lock(&tm_glb->system_group.lock);
+ tm_group->status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&tm_glb->system_group.lock);
}
static int _odp_tm_group_add(_odp_tm_group_t odp_tm_group, odp_tm_t odp_tm)
@@ -2763,12 +2953,21 @@ static int _odp_tm_group_remove(_odp_tm_group_t odp_tm_group, odp_tm_t odp_tm)
return 0;
}
+static void _odp_tm_init_tm_group(tm_system_group_t *tm_group)
+{
+ memset(tm_group, 0, sizeof(tm_system_group_t));
+
+ tm_group->status = TM_STATUS_RESERVED;
+ odp_barrier_init(&tm_group->tm_group_barrier, 2);
+}
+
static int tm_group_attach(odp_tm_t odp_tm)
{
tm_system_group_t *tm_group, *min_tm_group;
_odp_tm_group_t odp_tm_group;
odp_cpumask_t all_cpus, worker_cpus;
uint32_t total_cpus, avail_cpus;
+ uint32_t i;
/* If this platform has a small number of cpu's then allocate one
* tm_group and assign all tm_system's to this tm_group. Otherwise in
@@ -2782,34 +2981,37 @@ static int tm_group_attach(odp_tm_t odp_tm)
avail_cpus = odp_cpumask_count(&worker_cpus);
if (total_cpus < 24) {
- tm_group = tm_group_list;
- odp_tm_group = MAKE_ODP_TM_SYSTEM_GROUP(tm_group);
- if (tm_group == NULL)
- odp_tm_group = _odp_tm_group_create("");
+ tm_group = &tm_glb->system_group.group[0];
- _odp_tm_group_add(odp_tm_group, odp_tm);
- return 0;
- }
+ odp_ticketlock_lock(&tm_glb->system_group.lock);
+ if (tm_group->status == TM_STATUS_FREE)
+ _odp_tm_init_tm_group(tm_group);
+ odp_ticketlock_unlock(&tm_glb->system_group.lock);
- /* Manycore case. */
- if ((tm_group_list == NULL) || (avail_cpus > 1)) {
- odp_tm_group = _odp_tm_group_create("");
+ odp_tm_group = MAKE_ODP_TM_SYSTEM_GROUP(tm_group);
_odp_tm_group_add(odp_tm_group, odp_tm);
return 0;
}
/* Pick a tm_group according to the smallest number of tm_systems. */
- tm_group = tm_group_list;
min_tm_group = NULL;
- while (tm_group != NULL) {
+ odp_ticketlock_lock(&tm_glb->system_group.lock);
+ for (i = 0; i < ODP_TM_MAX_NUM_SYSTEMS && i < avail_cpus; i++) {
+ tm_group = &tm_glb->system_group.group[i];
+
+ if (tm_group->status == TM_STATUS_FREE) {
+ _odp_tm_init_tm_group(tm_group);
+ min_tm_group = tm_group;
+ break;
+ }
+
if (min_tm_group == NULL)
min_tm_group = tm_group;
else if (tm_group->num_tm_systems <
min_tm_group->num_tm_systems)
min_tm_group = tm_group;
-
- tm_group = tm_group->next;
}
+ odp_ticketlock_unlock(&tm_glb->system_group.lock);
if (min_tm_group == NULL)
return -1;
@@ -2828,22 +3030,42 @@ odp_tm_t odp_tm_create(const char *name,
odp_bool_t create_fail;
odp_tm_t odp_tm;
odp_pktout_queue_t pktout;
- uint32_t malloc_len, max_num_queues, max_queued_pkts, max_timers;
+ uint32_t max_num_queues, max_queued_pkts, max_timers;
uint32_t max_tm_queues, max_sorted_lists;
int rc;
+ if (odp_global_ro.disable.traffic_mngr) {
+ _ODP_ERR("TM has been disabled\n");
+ return ODP_TM_INVALID;
+ }
+
+ if (odp_global_ro.init_param.mem_model == ODP_MEM_MODEL_PROCESS) {
+ _ODP_ERR("TM is not supported in process mode\n");
+ return ODP_TM_INVALID;
+ }
+
+ /* We only support global pkt priority mode */
+ if (requirements->pkt_prio_mode != ODP_TM_PKT_PRIO_MODE_PRESERVE) {
+ _ODP_ERR("Unsupported Packet priority mode\n");
+ return ODP_TM_INVALID;
+ }
+ odp_ticketlock_lock(&tm_glb->create_lock);
+
/* If we are using pktio output (usual case) get the first associated
* pktout_queue for this pktio and fail if there isn't one.
*/
- if (egress->egress_kind == ODP_TM_EGRESS_PKT_IO &&
- odp_pktout_queue(egress->pktio, &pktout, 1) != 1)
- return ODP_TM_INVALID;
+ if (egress->egress_kind == ODP_TM_EGRESS_PKT_IO) {
+ rc = _odp_pktio_pktout_tm_config(egress->pktio, &pktout, false);
+ if (rc) {
+ odp_ticketlock_unlock(&tm_glb->create_lock);
+ return ODP_TM_INVALID;
+ }
+ }
/* Allocate tm_system_t record. */
- odp_ticketlock_lock(&tm_create_lock);
tm_system = tm_system_alloc();
if (!tm_system) {
- odp_ticketlock_unlock(&tm_create_lock);
+ odp_ticketlock_unlock(&tm_glb->create_lock);
return ODP_TM_INVALID;
}
@@ -2851,7 +3073,7 @@ odp_tm_t odp_tm_create(const char *name,
name_tbl_id = _odp_int_name_tbl_add(name, ODP_TM_HANDLE, odp_tm);
if (name_tbl_id == ODP_INVALID_NAME) {
tm_system_free(tm_system);
- odp_ticketlock_unlock(&tm_create_lock);
+ odp_ticketlock_unlock(&tm_glb->create_lock);
return ODP_TM_INVALID;
}
@@ -2867,10 +3089,7 @@ odp_tm_t odp_tm_create(const char *name,
tm_system_capabilities_set(&tm_system->capabilities,
&tm_system->requirements);
- malloc_len = max_tm_queues * sizeof(tm_queue_obj_t *);
- tm_system->queue_num_tbl = malloc(malloc_len);
- memset(tm_system->queue_num_tbl, 0, malloc_len);
- tm_system->next_queue_num = 1;
+ tm_system->root_node.is_root_node = true;
tm_init_random_data(&tm_system->tm_random_data);
@@ -2887,34 +3106,24 @@ odp_tm_t odp_tm_create(const char *name,
odp_ticketlock_init(&tm_system->tm_system_lock);
odp_atomic_init_u64(&tm_system->destroying, 0);
- tm_system->_odp_int_sorted_pool = _odp_sorted_pool_create(
- max_sorted_lists);
+ tm_system->_odp_int_sorted_pool = _odp_sorted_pool_create(max_sorted_lists);
create_fail |= tm_system->_odp_int_sorted_pool
== _ODP_INT_SORTED_POOL_INVALID;
if (create_fail == 0) {
- tm_system->_odp_int_queue_pool = _odp_queue_pool_create(
- max_num_queues, max_queued_pkts);
+ tm_system->_odp_int_queue_pool = _odp_queue_pool_create(max_num_queues,
+ max_queued_pkts);
create_fail |= tm_system->_odp_int_queue_pool
== _ODP_INT_QUEUE_POOL_INVALID;
}
if (create_fail == 0) {
- tm_system->_odp_int_timer_wheel = _odp_timer_wheel_create(
- max_timers, tm_system);
+ tm_system->_odp_int_timer_wheel = _odp_timer_wheel_create(max_timers, tm_system);
create_fail |= tm_system->_odp_int_timer_wheel
== _ODP_INT_TIMER_WHEEL_INVALID;
}
- if (create_fail == 0) {
- tm_system->root_node = create_dummy_root_node();
- create_fail |= tm_system->root_node == NULL;
- }
-
- if (create_fail == 0) {
- tm_system->input_work_queue = input_work_queue_create();
- create_fail |= !tm_system->input_work_queue;
- }
+ input_work_queue_init(&tm_system->input_work_queue);
if (create_fail == 0) {
/* Pass any odp_groups or hints to tm_group_attach here. */
@@ -2925,30 +3134,22 @@ odp_tm_t odp_tm_create(const char *name,
if (create_fail) {
_odp_int_name_tbl_delete(name_tbl_id);
- if (tm_system->input_work_queue)
- input_work_queue_destroy(tm_system->input_work_queue);
- if (tm_system->_odp_int_sorted_pool
- != _ODP_INT_SORTED_POOL_INVALID)
- _odp_sorted_pool_destroy(
- tm_system->_odp_int_sorted_pool);
+ if (tm_system->_odp_int_sorted_pool != _ODP_INT_SORTED_POOL_INVALID)
+ _odp_sorted_pool_destroy(tm_system->_odp_int_sorted_pool);
- if (tm_system->_odp_int_queue_pool !=
- _ODP_INT_QUEUE_POOL_INVALID)
- _odp_queue_pool_destroy(
- tm_system->_odp_int_queue_pool);
+ if (tm_system->_odp_int_queue_pool != _ODP_INT_QUEUE_POOL_INVALID)
+ _odp_queue_pool_destroy(tm_system->_odp_int_queue_pool);
- if (tm_system->_odp_int_timer_wheel
- != _ODP_INT_TIMER_WHEEL_INVALID)
- _odp_timer_wheel_destroy(
- tm_system->_odp_int_timer_wheel);
+ if (tm_system->_odp_int_timer_wheel != _ODP_INT_TIMER_WHEEL_INVALID)
+ _odp_timer_wheel_destroy(tm_system->_odp_int_timer_wheel);
tm_system_free(tm_system);
- odp_ticketlock_unlock(&tm_create_lock);
+ odp_ticketlock_unlock(&tm_glb->create_lock);
return ODP_TM_INVALID;
}
- odp_ticketlock_unlock(&tm_create_lock);
+ odp_ticketlock_unlock(&tm_glb->create_lock);
return odp_tm;
}
@@ -2976,6 +3177,22 @@ int odp_tm_capability(odp_tm_t odp_tm, odp_tm_capabilities_t *capabilities)
return 0;
}
+int odp_tm_start(odp_tm_t odp_tm)
+{
+ (void)odp_tm;
+
+ /* Nothing more to do after TM create */
+ return 0;
+}
+
+int odp_tm_stop(odp_tm_t odp_tm)
+{
+ (void)odp_tm;
+
+ /* Nothing more to do for topology changes */
+ return 0;
+}
+
int odp_tm_destroy(odp_tm_t odp_tm)
{
tm_system_t *tm_system;
@@ -2986,7 +3203,7 @@ int odp_tm_destroy(odp_tm_t odp_tm)
* all new pkts are prevented from coming in.
*/
odp_barrier_init(&tm_system->tm_system_destroy_barrier, 2);
- odp_atomic_inc_u64(&tm_system->destroying);
+ odp_atomic_store_rel_u64(&tm_system->destroying, 1);
odp_barrier_wait(&tm_system->tm_system_destroy_barrier);
/* Remove ourselves from the group. If we are the last tm_system in
@@ -2994,11 +3211,12 @@ int odp_tm_destroy(odp_tm_t odp_tm)
* allocated by this group. */
_odp_tm_group_remove(tm_system->odp_tm_group, odp_tm);
- input_work_queue_destroy(tm_system->input_work_queue);
+ input_work_queue_destroy(&tm_system->input_work_queue);
_odp_sorted_pool_destroy(tm_system->_odp_int_sorted_pool);
_odp_queue_pool_destroy(tm_system->_odp_int_queue_pool);
_odp_timer_wheel_destroy(tm_system->_odp_int_timer_wheel);
+ _odp_int_name_tbl_delete(tm_system->name_tbl_id);
tm_system_free(tm_system);
return 0;
}
@@ -3132,14 +3350,17 @@ void odp_tm_shaper_params_init(odp_tm_shaper_params_t *params)
}
odp_tm_shaper_t odp_tm_shaper_create(const char *name,
- odp_tm_shaper_params_t *params)
+ const odp_tm_shaper_params_t *params)
{
tm_shaper_params_t *profile_obj;
odp_tm_shaper_t shaper_handle;
_odp_int_name_t name_tbl_id;
+ /* We don't support shaper in packet mode */
+ if (params->packet_mode)
+ return ODP_TM_INVALID;
+
profile_obj = tm_common_profile_create(name, TM_SHAPER_PROFILE,
- sizeof(tm_shaper_params_t),
&shaper_handle, &name_tbl_id);
if (!profile_obj)
return ODP_TM_INVALID;
@@ -3165,7 +3386,6 @@ int odp_tm_shaper_destroy(odp_tm_shaper_t shaper_profile)
return -1;
return tm_common_profile_destroy(shaper_profile,
- sizeof(tm_shaper_params_t),
profile_obj->name_tbl_id);
}
@@ -3186,7 +3406,7 @@ int odp_tm_shaper_params_read(odp_tm_shaper_t shaper_profile,
}
int odp_tm_shaper_params_update(odp_tm_shaper_t shaper_profile,
- odp_tm_shaper_params_t *params)
+ const odp_tm_shaper_params_t *params)
{
tm_shaper_params_t *profile_obj;
@@ -3197,7 +3417,7 @@ int odp_tm_shaper_params_update(odp_tm_shaper_t shaper_profile,
if (!profile_obj)
return -1;
- if (!main_loop_running) {
+ if (!tm_glb->main_loop_running) {
tm_shaper_params_cvt_to(params, profile_obj);
return 0;
}
@@ -3223,15 +3443,15 @@ void odp_tm_sched_params_init(odp_tm_sched_params_t *params)
memset(params, 0, sizeof(odp_tm_sched_params_t));
}
-static void tm_sched_params_cvt_to(odp_tm_sched_params_t *odp_sched_params,
+static void tm_sched_params_cvt_to(const odp_tm_sched_params_t *sched_params,
tm_sched_params_t *tm_sched_params)
{
odp_tm_sched_mode_t sched_mode;
uint32_t priority, weight, inv_weight;
for (priority = 0; priority < ODP_TM_MAX_PRIORITIES; priority++) {
- sched_mode = odp_sched_params->sched_modes[priority];
- weight = odp_sched_params->sched_weights[priority];
+ sched_mode = sched_params->sched_modes[priority];
+ weight = sched_params->sched_weights[priority];
if (weight == 0)
inv_weight = 0;
else
@@ -3259,14 +3479,13 @@ static void tm_sched_params_cvt_from(tm_sched_params_t *tm_sched_params,
}
odp_tm_sched_t odp_tm_sched_create(const char *name,
- odp_tm_sched_params_t *params)
+ const odp_tm_sched_params_t *params)
{
tm_sched_params_t *profile_obj;
_odp_int_name_t name_tbl_id;
odp_tm_sched_t sched_handle;
profile_obj = tm_common_profile_create(name, TM_SCHED_PROFILE,
- sizeof(tm_sched_params_t),
&sched_handle, &name_tbl_id);
if (!profile_obj)
return ODP_TM_INVALID;
@@ -3292,7 +3511,6 @@ int odp_tm_sched_destroy(odp_tm_sched_t sched_profile)
return -1;
return tm_common_profile_destroy(sched_profile,
- sizeof(tm_sched_params_t),
profile_obj->name_tbl_id);
}
@@ -3313,7 +3531,7 @@ int odp_tm_sched_params_read(odp_tm_sched_t sched_profile,
}
int odp_tm_sched_params_update(odp_tm_sched_t sched_profile,
- odp_tm_sched_params_t *params)
+ const odp_tm_sched_params_t *params)
{
tm_sched_params_t *profile_obj;
@@ -3324,7 +3542,7 @@ int odp_tm_sched_params_update(odp_tm_sched_t sched_profile,
if (!profile_obj)
return -1;
- if (!main_loop_running) {
+ if (!tm_glb->main_loop_running) {
tm_sched_params_cvt_to(params, profile_obj);
return 0;
}
@@ -3351,14 +3569,14 @@ void odp_tm_threshold_params_init(odp_tm_threshold_params_t *params)
}
odp_tm_threshold_t odp_tm_threshold_create(const char *name,
- odp_tm_threshold_params_t *params)
+ const odp_tm_threshold_params_t
+ *params)
{
tm_queue_thresholds_t *profile_obj;
odp_tm_threshold_t threshold_handle;
_odp_int_name_t name_tbl_id;
profile_obj = tm_common_profile_create(name, TM_THRESHOLD_PROFILE,
- sizeof(tm_queue_thresholds_t),
&threshold_handle, &name_tbl_id);
if (!profile_obj)
return ODP_TM_INVALID;
@@ -3387,7 +3605,6 @@ int odp_tm_threshold_destroy(odp_tm_threshold_t threshold_profile)
return -1;
return tm_common_profile_destroy(threshold_profile,
- sizeof(odp_tm_threshold_params_t),
threshold_params->name_tbl_id);
}
@@ -3412,7 +3629,7 @@ int odp_tm_thresholds_params_read(odp_tm_threshold_t threshold_profile,
}
int odp_tm_thresholds_params_update(odp_tm_threshold_t threshold_profile,
- odp_tm_threshold_params_t *params)
+ const odp_tm_threshold_params_t *params)
{
tm_queue_thresholds_t *profile_obj;
@@ -3424,7 +3641,7 @@ int odp_tm_thresholds_params_update(odp_tm_threshold_t threshold_profile,
if (!profile_obj)
return -1;
- if (!main_loop_running) {
+ if (!tm_glb->main_loop_running) {
profile_obj->max_pkts =
params->enable_max_pkts ? params->max_pkts : 0;
profile_obj->max_bytes =
@@ -3456,15 +3673,15 @@ void odp_tm_wred_params_init(odp_tm_wred_params_t *params)
memset(params, 0, sizeof(odp_tm_wred_params_t));
}
-static void tm_wred_params_cvt_to(odp_tm_wred_params_t *odp_tm_wred_params,
+static void tm_wred_params_cvt_to(const odp_tm_wred_params_t *params,
tm_wred_params_t *wred_params)
{
- wred_params->min_threshold = odp_tm_wred_params->min_threshold;
- wred_params->med_threshold = odp_tm_wred_params->med_threshold;
- wred_params->med_drop_prob = odp_tm_wred_params->med_drop_prob;
- wred_params->max_drop_prob = odp_tm_wred_params->max_drop_prob;
- wred_params->enable_wred = odp_tm_wred_params->enable_wred;
- wred_params->use_byte_fullness = odp_tm_wred_params->use_byte_fullness;
+ wred_params->min_threshold = params->min_threshold;
+ wred_params->med_threshold = params->med_threshold;
+ wred_params->med_drop_prob = params->med_drop_prob;
+ wred_params->max_drop_prob = params->max_drop_prob;
+ wred_params->enable_wred = params->enable_wred;
+ wred_params->use_byte_fullness = params->use_byte_fullness;
}
static void tm_wred_params_cvt_from(tm_wred_params_t *wred_params,
@@ -3478,14 +3695,14 @@ static void tm_wred_params_cvt_from(tm_wred_params_t *wred_params,
odp_tm_wred_params->use_byte_fullness = wred_params->use_byte_fullness;
}
-odp_tm_wred_t odp_tm_wred_create(const char *name, odp_tm_wred_params_t *params)
+odp_tm_wred_t odp_tm_wred_create(const char *name,
+ const odp_tm_wred_params_t *params)
{
tm_wred_params_t *profile_obj;
odp_tm_wred_t wred_handle;
_odp_int_name_t name_tbl_id;
profile_obj = tm_common_profile_create(name, TM_WRED_PROFILE,
- sizeof(tm_wred_params_t),
&wred_handle, &name_tbl_id);
if (!profile_obj)
@@ -3512,8 +3729,7 @@ int odp_tm_wred_destroy(odp_tm_wred_t wred_profile)
return -1;
return tm_common_profile_destroy(wred_profile,
- sizeof(tm_wred_params_t),
- ODP_INVALID_NAME);
+ wred_params->name_tbl_id);
}
int odp_tm_wred_params_read(odp_tm_wred_t wred_profile,
@@ -3533,7 +3749,7 @@ int odp_tm_wred_params_read(odp_tm_wred_t wred_profile,
}
int odp_tm_wred_params_update(odp_tm_wred_t wred_profile,
- odp_tm_wred_params_t *params)
+ const odp_tm_wred_params_t *params)
{
tm_wred_params_t *wred_params;
@@ -3544,7 +3760,7 @@ int odp_tm_wred_params_update(odp_tm_wred_t wred_profile,
if (!wred_params)
return -1;
- if (!main_loop_running) {
+ if (!tm_glb->main_loop_running) {
tm_wred_params_cvt_to(params, wred_params);
return 0;
}
@@ -3570,77 +3786,73 @@ void odp_tm_node_params_init(odp_tm_node_params_t *params)
memset(params, 0, sizeof(odp_tm_node_params_t));
}
-odp_tm_node_t odp_tm_node_create(odp_tm_t odp_tm,
- const char *name,
- odp_tm_node_params_t *params)
+odp_tm_node_t odp_tm_node_create(odp_tm_t odp_tm, const char *name,
+ const odp_tm_node_params_t *params)
{
odp_tm_level_requirements_t *requirements;
_odp_int_sorted_list_t sorted_list;
tm_schedulers_obj_t *schedulers_obj;
_odp_int_name_t name_tbl_id;
tm_wred_node_t *tm_wred_node;
- tm_node_obj_t *tm_node_obj;
+ tm_node_obj_t *tm_node_obj = NULL;
odp_tm_node_t odp_tm_node;
odp_tm_wred_t wred_profile;
tm_system_t *tm_system;
- uint32_t level, num_priorities, priority, schedulers_obj_len, color;
+ uint32_t level, num_priorities, priority, color;
+ uint32_t i;
/* Allocate a tm_node_obj_t record. */
tm_system = GET_TM_SYSTEM(odp_tm);
- tm_node_obj = malloc(sizeof(tm_node_obj_t));
- if (!tm_node_obj)
- return ODP_TM_INVALID;
- tm_wred_node = malloc(sizeof(tm_wred_node_t));
- if (!tm_wred_node) {
- free(tm_node_obj);
- return ODP_TM_INVALID;
- }
+ odp_ticketlock_lock(&tm_glb->node_obj.lock);
- level = params->level;
- requirements = &tm_system->requirements.per_level[level];
- num_priorities = requirements->max_priority + 1;
- schedulers_obj_len = sizeof(tm_schedulers_obj_t)
- + (sizeof(tm_sched_state_t) * num_priorities);
- schedulers_obj = malloc(schedulers_obj_len);
- if (!schedulers_obj) {
- free(tm_wred_node);
- free(tm_node_obj);
- return ODP_TM_INVALID;
- }
+ for (i = 0; i < ODP_TM_MAX_NUM_TM_NODES; i++) {
+ tm_node_obj_t *cur_node_obj = tm_nobj_from_index(i);
- memset(schedulers_obj, 0, schedulers_obj_len);
- odp_tm_node = MAKE_ODP_TM_NODE(tm_node_obj);
- name_tbl_id = ODP_INVALID_NAME;
- if ((name) && (name[0] != '\0')) {
- name_tbl_id = _odp_int_name_tbl_add(name, ODP_TM_NODE_HANDLE,
- odp_tm_node);
- if (name_tbl_id == ODP_INVALID_NAME) {
- free(schedulers_obj);
- free(tm_wred_node);
- free(tm_node_obj);
- return ODP_TM_INVALID;
+ if (cur_node_obj->status != TM_STATUS_FREE)
+ continue;
+
+ level = params->level;
+ requirements = &tm_system->requirements.per_level[level];
+ num_priorities = requirements->max_priority + 1;
+
+ odp_tm_node = MAKE_ODP_TM_NODE(cur_node_obj);
+ name_tbl_id = ODP_INVALID_NAME;
+ if ((name) && (name[0] != '\0')) {
+ name_tbl_id = _odp_int_name_tbl_add(name,
+ ODP_TM_NODE_HANDLE,
+ odp_tm_node);
+ if (name_tbl_id == ODP_INVALID_NAME)
+ break;
}
+ tm_node_obj = cur_node_obj;
+
+ memset(tm_node_obj, 0, sizeof(tm_node_obj_t));
+ tm_node_obj->status = TM_STATUS_RESERVED;
+
+ break;
}
- memset(tm_node_obj, 0, sizeof(tm_node_obj_t));
- memset(tm_wred_node, 0, sizeof(tm_wred_node_t));
- memset(schedulers_obj, 0, schedulers_obj_len);
+ odp_ticketlock_unlock(&tm_glb->node_obj.lock);
+
+ if (!tm_node_obj)
+ return ODP_TM_INVALID;
+
tm_node_obj->user_context = params->user_context;
tm_node_obj->name_tbl_id = name_tbl_id;
tm_node_obj->max_fanin = params->max_fanin;
tm_node_obj->is_root_node = false;
tm_node_obj->level = params->level;
tm_node_obj->tm_idx = tm_system->tm_idx;
- tm_node_obj->tm_wred_node = tm_wred_node;
- tm_node_obj->schedulers_obj = schedulers_obj;
+
+ tm_wred_node = &tm_node_obj->tm_wred_node;
odp_ticketlock_init(&tm_wred_node->tm_wred_node_lock);
+ schedulers_obj = &tm_node_obj->schedulers_obj;
schedulers_obj->num_priorities = num_priorities;
for (priority = 0; priority < num_priorities; priority++) {
- sorted_list = _odp_sorted_list_create(
- tm_system->_odp_int_sorted_pool,
- params->max_fanin);
+ sorted_list = _odp_sorted_list_create(tm_system->_odp_int_sorted_pool,
+ params->max_fanin);
schedulers_obj->sched_states[priority].sorted_list =
sorted_list;
}
@@ -3663,7 +3875,7 @@ odp_tm_node_t odp_tm_node_create(odp_tm_t odp_tm,
tm_node_obj->magic_num = TM_NODE_MAGIC_NUM;
tm_node_obj->shaper_obj.enclosing_entity = tm_node_obj;
tm_node_obj->shaper_obj.in_tm_node_obj = 1;
- tm_node_obj->schedulers_obj->enclosing_entity = tm_node_obj;
+ tm_node_obj->schedulers_obj.enclosing_entity = tm_node_obj;
odp_ticketlock_unlock(&tm_system->tm_system_lock);
return odp_tm_node;
@@ -3688,7 +3900,7 @@ int odp_tm_node_destroy(odp_tm_node_t tm_node)
if (!tm_node_obj)
return -1;
- tm_system = odp_tm_systems[tm_node_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_node_obj->tm_idx];
if (!tm_system)
return -1;
@@ -3706,16 +3918,14 @@ int odp_tm_node_destroy(odp_tm_node_t tm_node)
if (shaper_obj->shaper_params != NULL)
return -1;
- tm_wred_node = tm_node_obj->tm_wred_node;
- if (tm_wred_node != NULL) {
- if (tm_wred_node->threshold_params != NULL)
- return -1;
+ tm_wred_node = &tm_node_obj->tm_wred_node;
+ if (tm_wred_node->threshold_params != NULL)
+ return -1;
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_params = tm_wred_node->wred_params[color];
- if (wred_params != NULL)
- return -1;
- }
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+ wred_params = tm_wred_node->wred_params[color];
+ if (wred_params != NULL)
+ return -1;
}
/* Now that all of the checks are done, time to so some freeing. */
@@ -3723,25 +3933,22 @@ int odp_tm_node_destroy(odp_tm_node_t tm_node)
if (tm_node_obj->name_tbl_id != ODP_INVALID_NAME)
_odp_int_name_tbl_delete(tm_node_obj->name_tbl_id);
- if (tm_node_obj->tm_wred_node != NULL)
- free(tm_node_obj->tm_wred_node);
-
- schedulers_obj = tm_node_obj->schedulers_obj;
- if (schedulers_obj != NULL) {
- num_priorities = schedulers_obj->num_priorities;
- for (priority = 0; priority < num_priorities; priority++) {
- sched_state = &schedulers_obj->sched_states[priority];
- sorted_list = sched_state->sorted_list;
- sorted_pool = tm_system->_odp_int_sorted_pool;
- rc = _odp_sorted_list_destroy(sorted_pool,
- sorted_list);
- if (rc != 0)
- return rc;
- }
+ schedulers_obj = &tm_node_obj->schedulers_obj;
+ num_priorities = schedulers_obj->num_priorities;
+ for (priority = 0; priority < num_priorities; priority++) {
+ sched_state = &schedulers_obj->sched_states[priority];
+ sorted_list = sched_state->sorted_list;
+ sorted_pool = tm_system->_odp_int_sorted_pool;
+ rc = _odp_sorted_list_destroy(sorted_pool,
+ sorted_list);
+ if (rc != 0)
+ return rc;
}
- free(schedulers_obj);
- free(tm_node_obj);
+ odp_ticketlock_lock(&tm_glb->node_obj.lock);
+ tm_node_obj->status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&tm_glb->node_obj.lock);
+
odp_ticketlock_unlock(&tm_system->tm_system_lock);
return 0;
}
@@ -3751,19 +3958,25 @@ int odp_tm_node_shaper_config(odp_tm_node_t tm_node,
{
tm_node_obj_t *tm_node_obj;
tm_system_t *tm_system;
+ odp_bool_t sync_needed;
tm_node_obj = GET_TM_NODE_OBJ(tm_node);
if (!tm_node_obj)
return -1;
- tm_system = odp_tm_systems[tm_node_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_node_obj->tm_idx];
if (!tm_system)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
+ sync_needed = tm_glb->main_loop_running;
+ if (sync_needed)
+ signal_request();
tm_shaper_config_set(tm_system, shaper_profile,
&tm_node_obj->shaper_obj);
- odp_ticketlock_unlock(&tm_profile_lock);
+ if (sync_needed)
+ signal_request_done();
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -3782,10 +3995,10 @@ int odp_tm_node_sched_config(odp_tm_node_t tm_node,
if (!child_tm_node_obj)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
child_shaper_obj = &child_tm_node_obj->shaper_obj;
tm_sched_config_set(child_shaper_obj, sched_profile);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -3795,12 +4008,12 @@ int odp_tm_node_threshold_config(odp_tm_node_t tm_node,
tm_node_obj_t *tm_node_obj;
tm_node_obj = GET_TM_NODE_OBJ(tm_node);
- if ((!tm_node_obj) || (!tm_node_obj->tm_wred_node))
+ if (!tm_node_obj)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
- tm_threshold_config_set(tm_node_obj->tm_wred_node, thresholds_profile);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
+ tm_threshold_config_set(&tm_node_obj->tm_wred_node, thresholds_profile);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -3817,9 +4030,9 @@ int odp_tm_node_wred_config(odp_tm_node_t tm_node,
if (!tm_node_obj)
return -1;
- wred_node = tm_node_obj->tm_wred_node;
+ wred_node = &tm_node_obj->tm_wred_node;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
rc = 0;
if (pkt_color == ODP_PACKET_ALL_COLORS) {
for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
@@ -3830,7 +4043,7 @@ int odp_tm_node_wred_config(odp_tm_node_t tm_node,
rc = -1;
}
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return rc;
}
@@ -3870,102 +4083,99 @@ int odp_tm_node_context_set(odp_tm_node_t tm_node, void *user_context)
void odp_tm_queue_params_init(odp_tm_queue_params_t *params)
{
memset(params, 0, sizeof(odp_tm_queue_params_t));
+
+ params->ordered_enqueue = true;
}
odp_tm_queue_t odp_tm_queue_create(odp_tm_t odp_tm,
- odp_tm_queue_params_t *params)
+ const odp_tm_queue_params_t *params)
{
_odp_int_pkt_queue_t _odp_int_pkt_queue;
- tm_queue_obj_t *tm_queue_obj;
- tm_wred_node_t *tm_wred_node;
- odp_tm_queue_t odp_tm_queue;
- odp_queue_t queue;
+ tm_queue_obj_t *queue_obj;
+ odp_tm_queue_t odp_tm_queue = ODP_TM_INVALID;
odp_tm_wred_t wred_profile;
tm_system_t *tm_system;
uint32_t color;
+ uint32_t i;
/* Allocate a tm_queue_obj_t record. */
tm_system = GET_TM_SYSTEM(odp_tm);
- tm_queue_obj = malloc(sizeof(tm_queue_obj_t));
- if (!tm_queue_obj)
- return ODP_TM_INVALID;
- tm_wred_node = malloc(sizeof(tm_wred_node_t));
- if (!tm_wred_node) {
- free(tm_queue_obj);
- return ODP_TM_INVALID;
- }
+ odp_ticketlock_lock(&tm_glb->queue_obj.lock);
- _odp_int_pkt_queue = _odp_pkt_queue_create(
- tm_system->_odp_int_queue_pool);
- if (_odp_int_pkt_queue == _ODP_INT_PKT_QUEUE_INVALID) {
- free(tm_wred_node);
- free(tm_queue_obj);
- return ODP_TM_INVALID;
- }
+ for (i = 0; i < ODP_TM_MAX_TM_QUEUES; i++) {
+ _odp_int_queue_pool_t int_queue_pool;
- odp_tm_queue = MAKE_ODP_TM_QUEUE(tm_queue_obj);
- memset(tm_queue_obj, 0, sizeof(tm_queue_obj_t));
- memset(tm_wred_node, 0, sizeof(tm_wred_node_t));
- tm_queue_obj->user_context = params->user_context;
- tm_queue_obj->priority = params->priority;
- tm_queue_obj->tm_idx = tm_system->tm_idx;
- tm_queue_obj->queue_num = tm_system->next_queue_num++;
- tm_queue_obj->tm_wred_node = tm_wred_node;
- tm_queue_obj->_odp_int_pkt_queue = _odp_int_pkt_queue;
- tm_queue_obj->pkt = ODP_PACKET_INVALID;
- odp_ticketlock_init(&tm_wred_node->tm_wred_node_lock);
+ queue_obj = tm_qobj_from_index(i);
- queue = odp_queue_create(NULL, NULL);
- if (queue == ODP_QUEUE_INVALID) {
- free(tm_wred_node);
- free(tm_queue_obj);
- return ODP_TM_INVALID;
- }
- tm_queue_obj->tm_qentry = queue_fn->from_ext(queue);
- queue_fn->set_enq_deq_fn(tm_queue_obj->tm_qentry,
- queue_tm_reenq, queue_tm_reenq_multi,
- NULL, NULL);
+ if (queue_obj->status != TM_STATUS_FREE)
+ continue;
- tm_system->queue_num_tbl[tm_queue_obj->queue_num - 1] = tm_queue_obj;
- odp_ticketlock_lock(&tm_system->tm_system_lock);
- if (params->shaper_profile != ODP_TM_INVALID)
- tm_shaper_config_set(tm_system, params->shaper_profile,
- &tm_queue_obj->shaper_obj);
+ int_queue_pool = tm_system->_odp_int_queue_pool;
+ _odp_int_pkt_queue = _odp_pkt_queue_create(int_queue_pool);
+ if (_odp_int_pkt_queue == _ODP_INT_PKT_QUEUE_INVALID)
+ continue;
- if (params->threshold_profile != ODP_TM_INVALID)
- tm_threshold_config_set(tm_wred_node,
- params->threshold_profile);
+ odp_tm_queue = MAKE_ODP_TM_QUEUE(queue_obj);
+ memset(queue_obj, 0, sizeof(tm_queue_obj_t));
+ queue_obj->user_context = params->user_context;
+ queue_obj->priority = params->priority;
+ queue_obj->ordered_enqueue = params->ordered_enqueue;
+ queue_obj->tm_idx = tm_system->tm_idx;
+ queue_obj->queue_num = (uint32_t)_odp_int_pkt_queue;
+ queue_obj->_odp_int_pkt_queue = _odp_int_pkt_queue;
+ queue_obj->pkt = ODP_PACKET_INVALID;
+ odp_ticketlock_init(&queue_obj->tm_wred_node.tm_wred_node_lock);
+ odp_atomic_init_u64(&queue_obj->stats.discards, 0);
+ odp_atomic_init_u64(&queue_obj->stats.errors, 0);
+ odp_atomic_init_u64(&queue_obj->stats.packets, 0);
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_profile = params->wred_profile[color];
- if (wred_profile != ODP_TM_INVALID)
- tm_wred_config_set(tm_wred_node, color, wred_profile);
+ tm_system->queue_num_tbl[queue_obj->queue_num - 1] = queue_obj;
+
+ odp_ticketlock_lock(&tm_system->tm_system_lock);
+
+ if (params->shaper_profile != ODP_TM_INVALID)
+ tm_shaper_config_set(tm_system, params->shaper_profile,
+ &queue_obj->shaper_obj);
+
+ if (params->threshold_profile != ODP_TM_INVALID)
+ tm_threshold_config_set(&queue_obj->tm_wred_node,
+ params->threshold_profile);
+
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+ wred_profile = params->wred_profile[color];
+ if (wred_profile != ODP_TM_INVALID)
+ tm_wred_config_set(&queue_obj->tm_wred_node,
+ color, wred_profile);
+ }
+
+ queue_obj->magic_num = TM_QUEUE_MAGIC_NUM;
+ queue_obj->shaper_obj.enclosing_entity = queue_obj;
+ queue_obj->shaper_obj.in_tm_node_obj = 0;
+
+ odp_ticketlock_unlock(&tm_system->tm_system_lock);
+
+ queue_obj->status = TM_STATUS_RESERVED;
+ break;
}
- tm_queue_obj->magic_num = TM_QUEUE_MAGIC_NUM;
- tm_queue_obj->shaper_obj.enclosing_entity = tm_queue_obj;
- tm_queue_obj->shaper_obj.in_tm_node_obj = 0;
+ odp_ticketlock_unlock(&tm_glb->queue_obj.lock);
- odp_ticketlock_unlock(&tm_system->tm_system_lock);
return odp_tm_queue;
}
int odp_tm_queue_destroy(odp_tm_queue_t tm_queue)
{
- tm_wred_params_t *wred_params;
tm_shaper_obj_t *shaper_obj;
tm_queue_obj_t *tm_queue_obj;
- tm_wred_node_t *tm_wred_node;
tm_system_t *tm_system;
- uint32_t color;
/* First lookup tm_queue. */
tm_queue_obj = GET_TM_QUEUE_OBJ(tm_queue);
if (!tm_queue_obj)
return -1;
- tm_system = odp_tm_systems[tm_queue_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_queue_obj->tm_idx];
if (!tm_system)
return -1;
@@ -3976,33 +4186,16 @@ int odp_tm_queue_destroy(odp_tm_queue_t tm_queue)
(tm_queue_obj->pkt != ODP_PACKET_INVALID))
return -1;
- /* Check that there is no shaper profile, threshold profile or wred
- * profile currently associated with this tm_queue. */
- if (shaper_obj->shaper_params != NULL)
- return -1;
-
- tm_wred_node = tm_queue_obj->tm_wred_node;
- if (tm_wred_node != NULL) {
- if (tm_wred_node->threshold_params != NULL)
- return -1;
-
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_params = tm_wred_node->wred_params[color];
- if (wred_params != NULL)
- return -1;
- }
- }
-
/* Now that all of the checks are done, time to so some freeing. */
odp_ticketlock_lock(&tm_system->tm_system_lock);
tm_system->queue_num_tbl[tm_queue_obj->queue_num - 1] = NULL;
- odp_queue_destroy(queue_fn->to_ext(tm_queue_obj->tm_qentry));
+ odp_ticketlock_lock(&tm_glb->queue_obj.lock);
+ _odp_pkt_queue_destroy(tm_system->_odp_int_queue_pool,
+ tm_queue_obj->_odp_int_pkt_queue);
+ tm_queue_obj->status = TM_STATUS_FREE;
+ odp_ticketlock_unlock(&tm_glb->queue_obj.lock);
- /* First delete any associated tm_wred_node and then the tm_queue_obj
- * itself */
- free(tm_queue_obj->tm_wred_node);
- free(tm_queue_obj);
odp_ticketlock_unlock(&tm_system->tm_system_lock);
return 0;
}
@@ -4040,14 +4233,14 @@ int odp_tm_queue_shaper_config(odp_tm_queue_t tm_queue,
if (!tm_queue_obj)
return -1;
- tm_system = odp_tm_systems[tm_queue_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_queue_obj->tm_idx];
if (!tm_system)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
tm_shaper_config_set(tm_system, shaper_profile,
&tm_queue_obj->shaper_obj);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -4067,10 +4260,10 @@ int odp_tm_queue_sched_config(odp_tm_node_t tm_node,
if (!child_tm_queue_obj)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
child_shaper_obj = &child_tm_queue_obj->shaper_obj;
tm_sched_config_set(child_shaper_obj, sched_profile);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -4078,15 +4271,17 @@ int odp_tm_queue_threshold_config(odp_tm_queue_t tm_queue,
odp_tm_threshold_t thresholds_profile)
{
tm_queue_obj_t *tm_queue_obj;
+ int ret;
tm_queue_obj = GET_TM_QUEUE_OBJ(tm_queue);
if (!tm_queue_obj)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
- tm_threshold_config_set(tm_queue_obj->tm_wred_node, thresholds_profile);
- odp_ticketlock_unlock(&tm_profile_lock);
- return 0;
+ odp_ticketlock_lock(&tm_glb->profile_lock);
+ ret = tm_threshold_config_set(&tm_queue_obj->tm_wred_node,
+ thresholds_profile);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
+ return ret;
}
int odp_tm_queue_wred_config(odp_tm_queue_t tm_queue,
@@ -4102,9 +4297,9 @@ int odp_tm_queue_wred_config(odp_tm_queue_t tm_queue,
if (!tm_queue_obj)
return -1;
- wred_node = tm_queue_obj->tm_wred_node;
+ wred_node = &tm_queue_obj->tm_wred_node;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
rc = 0;
if (pkt_color == ODP_PACKET_ALL_COLORS) {
for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
@@ -4115,7 +4310,7 @@ int odp_tm_queue_wred_config(odp_tm_queue_t tm_queue,
rc = -1;
}
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return rc;
}
@@ -4180,13 +4375,15 @@ int odp_tm_node_connect(odp_tm_node_t src_tm_node, odp_tm_node_t dst_tm_node)
if ((!src_tm_node_obj) || src_tm_node_obj->is_root_node)
return -1;
- tm_system = odp_tm_systems[src_tm_node_obj->tm_idx];
+ tm_system = &tm_glb->system[src_tm_node_obj->tm_idx];
if (!tm_system)
return -1;
- src_tm_wred_node = src_tm_node_obj->tm_wred_node;
+ src_tm_wred_node = &src_tm_node_obj->tm_wred_node;
if (dst_tm_node == ODP_TM_ROOT) {
- src_tm_node_obj->shaper_obj.next_tm_node = tm_system->root_node;
+ tm_node_obj_t *root_node = &tm_system->root_node;
+
+ src_tm_node_obj->shaper_obj.next_tm_node = root_node;
src_tm_wred_node->next_tm_wred_node = NULL;
return 0;
}
@@ -4195,7 +4392,7 @@ int odp_tm_node_connect(odp_tm_node_t src_tm_node, odp_tm_node_t dst_tm_node)
if ((!dst_tm_node_obj) || dst_tm_node_obj->is_root_node)
return -1;
- dst_tm_wred_node = dst_tm_node_obj->tm_wred_node;
+ dst_tm_wred_node = &dst_tm_node_obj->tm_wred_node;
if (src_tm_node_obj->tm_idx != dst_tm_node_obj->tm_idx)
return -1;
@@ -4226,9 +4423,8 @@ int odp_tm_node_disconnect(odp_tm_node_t src_tm_node)
dst_tm_node_obj->current_tm_node_fanin--;
}
- src_tm_wred_node = src_tm_node_obj->tm_wred_node;
- if (src_tm_wred_node != NULL)
- src_tm_wred_node->next_tm_wred_node = NULL;
+ src_tm_wred_node = &src_tm_node_obj->tm_wred_node;
+ src_tm_wred_node->next_tm_wred_node = NULL;
src_tm_node_obj->shaper_obj.next_tm_node = NULL;
return 0;
@@ -4249,13 +4445,13 @@ int odp_tm_queue_connect(odp_tm_queue_t tm_queue, odp_tm_node_t dst_tm_node)
if (!src_tm_queue_obj)
return -1;
- tm_system = odp_tm_systems[src_tm_queue_obj->tm_idx];
+ tm_system = &tm_glb->system[src_tm_queue_obj->tm_idx];
if (!tm_system)
return -1;
- src_tm_wred_node = src_tm_queue_obj->tm_wred_node;
+ src_tm_wred_node = &src_tm_queue_obj->tm_wred_node;
if (dst_tm_node == ODP_TM_ROOT) {
- root_node = tm_system->root_node;
+ root_node = &tm_system->root_node;
src_tm_queue_obj->shaper_obj.next_tm_node = root_node;
src_tm_wred_node->next_tm_wred_node = NULL;
return 0;
@@ -4265,7 +4461,7 @@ int odp_tm_queue_connect(odp_tm_queue_t tm_queue, odp_tm_node_t dst_tm_node)
if ((!dst_tm_node_obj) || dst_tm_node_obj->is_root_node)
return -1;
- dst_tm_wred_node = dst_tm_node_obj->tm_wred_node;
+ dst_tm_wred_node = &dst_tm_node_obj->tm_wred_node;
if (src_tm_queue_obj->tm_idx != dst_tm_node_obj->tm_idx)
return -1;
@@ -4297,9 +4493,8 @@ int odp_tm_queue_disconnect(odp_tm_queue_t tm_queue)
dst_tm_node_obj->current_tm_queue_fanin--;
}
- src_tm_wred_node = src_tm_queue_obj->tm_wred_node;
- if (src_tm_wred_node != NULL)
- src_tm_wred_node->next_tm_wred_node = NULL;
+ src_tm_wred_node = &src_tm_queue_obj->tm_wred_node;
+ src_tm_wred_node->next_tm_wred_node = NULL;
src_tm_queue_obj->shaper_obj.next_tm_node = NULL;
return 0;
@@ -4309,19 +4504,23 @@ int odp_tm_enq(odp_tm_queue_t tm_queue, odp_packet_t pkt)
{
tm_queue_obj_t *tm_queue_obj;
tm_system_t *tm_system;
+ int rc;
tm_queue_obj = GET_TM_QUEUE_OBJ(tm_queue);
if (!tm_queue_obj)
return -1;
- tm_system = odp_tm_systems[tm_queue_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_queue_obj->tm_idx];
if (!tm_system)
return -1;
- if (odp_atomic_load_u64(&tm_system->destroying))
+ if (odp_atomic_load_acq_u64(&tm_system->destroying))
return -1;
- return tm_enqueue(tm_system, tm_queue_obj, pkt);
+ rc = tm_enqueue(tm_system, tm_queue_obj, pkt);
+ if (rc < 0)
+ return rc;
+ return 0;
}
int odp_tm_enq_with_cnt(odp_tm_queue_t tm_queue, odp_packet_t pkt)
@@ -4335,11 +4534,11 @@ int odp_tm_enq_with_cnt(odp_tm_queue_t tm_queue, odp_packet_t pkt)
if (!tm_queue_obj)
return -1;
- tm_system = odp_tm_systems[tm_queue_obj->tm_idx];
+ tm_system = &tm_glb->system[tm_queue_obj->tm_idx];
if (!tm_system)
return -1;
- if (odp_atomic_load_u64(&tm_system->destroying))
+ if (odp_atomic_load_acq_u64(&tm_system->destroying))
return -1;
rc = tm_enqueue(tm_system, tm_queue_obj, pkt);
@@ -4350,6 +4549,127 @@ int odp_tm_enq_with_cnt(odp_tm_queue_t tm_queue, odp_packet_t pkt)
return pkt_cnt;
}
+int odp_tm_enq_multi(odp_tm_queue_t tm_queue, const odp_packet_t packets[],
+ int num)
+{
+ tm_queue_obj_t *tm_queue_obj;
+ tm_system_t *tm_system;
+ int i, rc;
+
+ tm_queue_obj = GET_TM_QUEUE_OBJ(tm_queue);
+ if (!tm_queue_obj)
+ return -1;
+
+ tm_system = &tm_glb->system[tm_queue_obj->tm_idx];
+ if (!tm_system)
+ return -1;
+
+ if (odp_atomic_load_acq_u64(&tm_system->destroying))
+ return -1;
+
+ for (i = 0; i < num; i++) {
+ rc = tm_enqueue(tm_system, tm_queue_obj, packets[i]);
+ if (rc < 0 && rc != -2)
+ break;
+ /* For RED failure, just drop current pkt but
+ * continue with next pkts.
+ */
+ if (rc == -2) {
+ odp_packet_free(packets[i]);
+ odp_atomic_inc_u64(&tm_queue_obj->stats.discards);
+ }
+ }
+
+ return i;
+}
+
+int odp_tm_enq_multi_lso(odp_tm_queue_t tm_queue, const odp_packet_t packets[], int num,
+ const odp_packet_lso_opt_t *opt)
+{
+ int i, ret, num_pkt;
+ uint32_t payload_len, left_over_len;
+ odp_packet_t pkt;
+ odp_packet_lso_opt_t lso_opt;
+ const odp_packet_lso_opt_t *opt_ptr = &lso_opt;
+
+ if (odp_unlikely(num <= 0)) {
+ _ODP_ERR("No packets\n");
+ return -1;
+ }
+
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ if (opt)
+ opt_ptr = opt;
+
+ for (i = 0; i < num; i++) {
+ pkt = packets[i];
+
+ if (opt == NULL) {
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ if (pkt_hdr->p.flags.lso == 0) {
+ _ODP_ERR("No LSO options on packet %i\n", i);
+ goto error;
+ }
+ /* Fill in LSO options from packet */
+ lso_opt.lso_profile = _odp_lso_prof_from_idx(pkt_hdr->lso_profile_idx);
+ lso_opt.payload_offset = odp_packet_payload_offset(pkt);
+ lso_opt.max_payload_len = pkt_hdr->lso_max_payload;
+ }
+
+ /* Calculate number of packets */
+ num_pkt = _odp_lso_num_packets(pkt, opt_ptr, &payload_len, &left_over_len);
+ if (odp_unlikely(num_pkt <= 0)) {
+ _ODP_DBG("LSO num packets failed on packet %i\n", i);
+ goto error;
+ }
+
+ if (odp_unlikely(num_pkt == 1)) {
+ /* Segmentation not needed */
+ if (odp_tm_enq_multi(tm_queue, &pkt, 1) != 1) {
+ _ODP_DBG("TM enqueue failed on packet %i\n", i);
+
+ goto error;
+ }
+
+ continue;
+ }
+
+ /* Create packets */
+ odp_packet_t pkt_out[num_pkt];
+
+ ret = _odp_lso_create_packets(pkt, opt_ptr, payload_len, left_over_len, pkt_out,
+ num_pkt);
+
+ if (odp_unlikely(ret))
+ goto error;
+
+ /* Enqueue resulting packets */
+ ret = odp_tm_enq_multi(tm_queue, pkt_out, num_pkt);
+
+ if (odp_unlikely(ret < num_pkt)) {
+ _ODP_DBG("TM enqueue failed on packet %i\n", i);
+
+ if (ret < 0)
+ ret = 0;
+
+ odp_packet_free_multi(&pkt_out[ret], num_pkt - ret);
+ goto error;
+ }
+
+ /* Free original packet */
+ odp_packet_free(pkt);
+ }
+
+ return i;
+
+error:
+ if (i == 0)
+ return -1;
+
+ return i;
+}
+
int odp_tm_node_info(odp_tm_node_t tm_node, odp_tm_node_info_t *info)
{
tm_queue_thresholds_t *threshold_params;
@@ -4385,19 +4705,17 @@ int odp_tm_node_info(odp_tm_node_t tm_node, odp_tm_node_info_t *info)
if (shaper_params != NULL)
info->shaper_profile = shaper_params->shaper_profile;
- tm_wred_node = tm_node_obj->tm_wred_node;
- if (tm_wred_node != NULL) {
- threshold_params = tm_wred_node->threshold_params;
- if (threshold_params != NULL)
- info->threshold_profile =
- threshold_params->thresholds_profile;
+ tm_wred_node = &tm_node_obj->tm_wred_node;
+ threshold_params = tm_wred_node->threshold_params;
+ if (threshold_params != NULL)
+ info->threshold_profile =
+ threshold_params->thresholds_profile;
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_params = tm_wred_node->wred_params[color];
- if (wred_params != NULL)
- info->wred_profile[color] =
- wred_params->wred_profile;
- }
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+ wred_params = tm_wred_node->wred_params[color];
+ if (wred_params != NULL)
+ info->wred_profile[color] =
+ wred_params->wred_profile;
}
return 0;
@@ -4501,7 +4819,7 @@ int odp_tm_queue_info(odp_tm_queue_t tm_queue, odp_tm_queue_info_t *info)
if (shaper_params != NULL)
info->shaper_profile = shaper_params->shaper_profile;
- tm_wred_node = tm_queue_obj->tm_wred_node;
+ tm_wred_node = &tm_queue_obj->tm_wred_node;
if (tm_wred_node != NULL) {
threshold_params = tm_wred_node->threshold_params;
if (threshold_params != NULL)
@@ -4523,7 +4841,8 @@ static int tm_query_info_copy(tm_queue_info_t *queue_info,
uint32_t query_flags,
odp_tm_query_info_t *info)
{
- tm_queue_thresholds_t *threshold_params;
+ if ((query_flags & ODP_TM_QUERY_THRESHOLDS) && !queue_info->threshold_params)
+ return -1;
memset(info, 0, sizeof(odp_tm_query_info_t));
info->total_pkt_cnt =
@@ -4535,9 +4854,7 @@ static int tm_query_info_copy(tm_queue_info_t *queue_info,
info->approx_byte_cnt = 0;
if (query_flags & ODP_TM_QUERY_THRESHOLDS) {
- threshold_params = queue_info->threshold_params;
- if (!threshold_params)
- return -1;
+ tm_queue_thresholds_t *threshold_params = queue_info->threshold_params;
info->max_pkt_cnt = threshold_params->max_pkts;
info->max_byte_cnt = threshold_params->max_bytes;
@@ -4560,9 +4877,7 @@ int odp_tm_queue_query(odp_tm_queue_t tm_queue,
if (!tm_queue_obj)
return -1;
- tm_wred_node = tm_queue_obj->tm_wred_node;
- if (!tm_wred_node)
- return -1;
+ tm_wred_node = &tm_queue_obj->tm_wred_node;
/* **TBD** Where do we get the queue_info from. */
queue_info.threshold_params = tm_wred_node->threshold_params;
@@ -4605,11 +4920,11 @@ int odp_tm_priority_threshold_config(odp_tm_t odp_tm,
if (thresholds_profile == ODP_TM_INVALID)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
tm_system->priority_info[priority].threshold_params =
tm_get_profile_params(thresholds_profile,
TM_THRESHOLD_PROFILE);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -4622,10 +4937,10 @@ int odp_tm_total_threshold_config(odp_tm_t odp_tm,
if (thresholds_profile == ODP_TM_INVALID)
return -1;
- odp_ticketlock_lock(&tm_profile_lock);
- tm_system->total_info.threshold_params = tm_get_profile_params(
- thresholds_profile, TM_THRESHOLD_PROFILE);
- odp_ticketlock_unlock(&tm_profile_lock);
+ odp_ticketlock_lock(&tm_glb->profile_lock);
+ tm_system->total_info.threshold_params = tm_get_profile_params(thresholds_profile,
+ TM_THRESHOLD_PROFILE);
+ odp_ticketlock_unlock(&tm_glb->profile_lock);
return 0;
}
@@ -4634,57 +4949,140 @@ void odp_tm_stats_print(odp_tm_t odp_tm)
input_work_queue_t *input_work_queue;
tm_queue_obj_t *tm_queue_obj;
tm_system_t *tm_system;
- uint32_t queue_num, max_queue_num;
+ uint32_t queue_num;
tm_system = GET_TM_SYSTEM(odp_tm);
- input_work_queue = tm_system->input_work_queue;
-
- ODP_DBG("odp_tm_stats_print - tm_system=0x%" PRIX64 " tm_idx=%u\n",
- odp_tm, tm_system->tm_idx);
- ODP_DBG(" input_work_queue size=%u current cnt=%u peak cnt=%u\n",
- INPUT_WORK_RING_SIZE, input_work_queue->queue_cnt,
- input_work_queue->peak_cnt);
- ODP_DBG(" input_work_queue enqueues=%" PRIu64 " dequeues=% " PRIu64
- " fail_cnt=%" PRIu64 "\n", input_work_queue->total_enqueues,
- input_work_queue->total_dequeues,
- input_work_queue->enqueue_fail_cnt);
- ODP_DBG(" green_cnt=%" PRIu64 " yellow_cnt=%" PRIu64 " red_cnt=%"
- PRIu64 "\n", tm_system->shaper_green_cnt,
- tm_system->shaper_yellow_cnt,
- tm_system->shaper_red_cnt);
+ input_work_queue = &tm_system->input_work_queue;
+
+ _ODP_PRINT("\nTM stats\n");
+ _ODP_PRINT("--------\n");
+ _ODP_PRINT(" tm_system=0x%" PRIX64 " tm_idx=%u\n", odp_tm, tm_system->tm_idx);
+ _ODP_PRINT(" input_work_queue size=%u current cnt=%" PRIu64 " peak cnt=%" PRIu32 "\n",
+ INPUT_WORK_RING_SIZE, odp_atomic_load_u64(&input_work_queue->queue_cnt),
+ input_work_queue->peak_cnt);
+ _ODP_PRINT(" input_work_queue enqueues=%" PRIu64 " dequeues=%" PRIu64
+ " fail_cnt=%" PRIu64 "\n", input_work_queue->total_enqueues,
+ input_work_queue->total_dequeues,
+ input_work_queue->enqueue_fail_cnt);
+ _ODP_PRINT(" green_cnt=%" PRIu64 " yellow_cnt=%" PRIu64 " red_cnt=%" PRIu64 "\n",
+ tm_system->shaper_green_cnt,
+ tm_system->shaper_yellow_cnt,
+ tm_system->shaper_red_cnt);
_odp_pkt_queue_stats_print(tm_system->_odp_int_queue_pool);
_odp_timer_wheel_stats_print(tm_system->_odp_int_timer_wheel);
_odp_sorted_list_stats_print(tm_system->_odp_int_sorted_pool);
- max_queue_num = tm_system->next_queue_num;
- for (queue_num = 1; queue_num < max_queue_num; queue_num++) {
+ for (queue_num = 1; queue_num <= ODP_TM_MAX_TM_QUEUES; queue_num++) {
tm_queue_obj = tm_system->queue_num_tbl[queue_num - 1];
if (tm_queue_obj && tm_queue_obj->pkts_rcvd_cnt != 0)
- ODP_DBG("queue_num=%u priority=%u rcvd=%u enqueued=%u "
- "dequeued=%u consumed=%u\n",
- queue_num,
- tm_queue_obj->priority,
- tm_queue_obj->pkts_rcvd_cnt,
- tm_queue_obj->pkts_enqueued_cnt,
- tm_queue_obj->pkts_dequeued_cnt,
- tm_queue_obj->pkts_consumed_cnt);
+ _ODP_PRINT("queue_num=%u priority=%u rcvd=%u enqueued=%u "
+ "dequeued=%u consumed=%u\n",
+ queue_num,
+ tm_queue_obj->priority,
+ tm_queue_obj->pkts_rcvd_cnt,
+ tm_queue_obj->pkts_enqueued_cnt,
+ tm_queue_obj->pkts_dequeued_cnt,
+ tm_queue_obj->pkts_consumed_cnt);
}
}
-int odp_tm_init_global(void)
+int odp_tm_queue_stats(odp_tm_queue_t tm_queue, odp_tm_queue_stats_t *stats)
{
- odp_ticketlock_init(&tm_create_lock);
- odp_ticketlock_init(&tm_profile_lock);
- odp_barrier_init(&tm_first_enq, 2);
+ tm_queue_obj_t *tm_queue_obj = GET_TM_QUEUE_OBJ(tm_queue);
+
+ if (!tm_queue_obj) {
+ _ODP_ERR("Invalid TM queue handle\n");
+ return -1;
+ }
+
+ memset(stats, 0, sizeof(odp_tm_queue_stats_t));
+ stats->discards = odp_atomic_load_u64(&tm_queue_obj->stats.discards);
+ stats->errors = odp_atomic_load_u64(&tm_queue_obj->stats.errors);
+ stats->packets = odp_atomic_load_u64(&tm_queue_obj->stats.packets);
- odp_atomic_init_u64(&atomic_request_cnt, 0);
- odp_atomic_init_u64(&currently_serving_cnt, 0);
- odp_atomic_init_u64(&atomic_done_cnt, 0);
return 0;
}
-int odp_tm_term_global(void)
+uint64_t odp_tm_to_u64(odp_tm_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_queue_to_u64(odp_tm_queue_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_node_to_u64(odp_tm_node_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_shaper_to_u64(odp_tm_shaper_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_sched_to_u64(odp_tm_sched_t hdl)
{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_threshold_to_u64(odp_tm_threshold_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+uint64_t odp_tm_wred_to_u64(odp_tm_wred_t hdl)
+{
+ return _odp_pri(hdl);
+}
+
+int _odp_tm_init_global(void)
+{
+ odp_shm_t shm;
+
+ if (odp_global_ro.disable.traffic_mngr) {
+ _ODP_PRINT("\nODP traffic manager is DISABLED\n");
+ return 0;
+ }
+
+ shm = odp_shm_reserve("_odp_traffic_mng_global", sizeof(tm_global_t), 0, 0);
+ if (shm == ODP_SHM_INVALID)
+ return -1;
+
+ tm_glb = odp_shm_addr(shm);
+ memset(tm_glb, 0, sizeof(tm_global_t));
+
+ tm_glb->shm = shm;
+ tm_glb->main_thread_cpu = -1;
+
+ odp_ticketlock_init(&tm_glb->queue_obj.lock);
+ odp_ticketlock_init(&tm_glb->node_obj.lock);
+ odp_ticketlock_init(&tm_glb->system_group.lock);
+ odp_ticketlock_init(&tm_glb->create_lock);
+ odp_ticketlock_init(&tm_glb->profile_lock);
+ odp_ticketlock_init(&tm_glb->profile_tbl.sched.lock);
+ odp_ticketlock_init(&tm_glb->profile_tbl.shaper.lock);
+ odp_ticketlock_init(&tm_glb->profile_tbl.threshold.lock);
+ odp_ticketlock_init(&tm_glb->profile_tbl.wred.lock);
+ odp_barrier_init(&tm_glb->first_enq, 2);
+
+ odp_atomic_init_u64(&tm_glb->atomic_request_cnt, 0);
+ odp_atomic_init_u64(&tm_glb->currently_serving_cnt, 0);
+ odp_atomic_init_u64(&tm_glb->atomic_done_cnt, 0);
+ return 0;
+}
+
+int _odp_tm_term_global(void)
+{
+ if (odp_global_ro.disable.traffic_mngr)
+ return 0;
+
+ if (odp_shm_free(tm_glb->shm)) {
+ _ODP_ERR("shm free failed\n");
+ return -1;
+ }
return 0;
}
diff --git a/platform/linux-generic/odp_version.c b/platform/linux-generic/odp_version.c
index 7b704d054..8c8b9be09 100644
--- a/platform/linux-generic/odp_version.c
+++ b/platform/linux-generic/odp_version.c
@@ -1,7 +1,5 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
*/
#include <odp/api/version.h>
diff --git a/platform/linux-generic/odp_weak.c b/platform/linux-generic/odp_weak.c
index 0fbf6645e..aab23b1ec 100644
--- a/platform/linux-generic/odp_weak.c
+++ b/platform/linux-generic/odp_weak.c
@@ -1,10 +1,7 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
*/
-#include <odp_internal.h>
#include <odp/api/debug.h>
#include <odp_debug_internal.h>
#include <odp/api/hints.h>
@@ -21,6 +18,7 @@ int odp_override_log(odp_log_level_t level, const char *fmt, ...)
switch (level) {
case ODP_LOG_ERR:
case ODP_LOG_UNIMPLEMENTED:
+ case ODP_LOG_WARN:
case ODP_LOG_ABORT:
logfd = stderr;
break;
diff --git a/platform/linux-generic/pktio/dpdk.c b/platform/linux-generic/pktio/dpdk.c
index 19310651c..b28e63890 100644
--- a/platform/linux-generic/pktio/dpdk.c
+++ b/platform/linux-generic/pktio/dpdk.c
@@ -1,85 +1,315 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
*/
-#ifdef ODP_PKTIO_DPDK
+#include <odp/autoheader_internal.h>
-#include <odp_posix_extensions.h>
+#ifdef _ODP_PKTIO_DPDK
-#include <sched.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <math.h>
+#include <odp_posix_extensions.h>
#include <odp/api/cpumask.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/packet_io_stats.h>
+#include <odp/api/std_types.h>
+#include <odp/api/time.h>
#include <odp/api/plat/packet_inlines.h>
-#include <odp/api/packet.h>
+#include <odp/api/plat/time_inlines.h>
-#include <odp_packet_io_internal.h>
#include <odp_classification_internal.h>
-#include <odp_packet_dpdk.h>
#include <odp_debug_internal.h>
-
+#include <odp_global_data.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_dpdk.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
#include <protocols/eth.h>
+#include <protocols/udp.h>
+#include <odp_pool_internal.h>
+#include <odp_socket_common.h>
+#include <odp_string_internal.h>
#include <rte_config.h>
-#include <rte_malloc.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
#include <rte_mbuf.h>
+#include <rte_malloc.h>
#include <rte_mempool.h>
-#include <rte_ethdev.h>
+#include <rte_ip.h>
+#include <rte_ip_frag.h>
+#include <rte_log.h>
#include <rte_string_fns.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_version.h>
+#include <rte_vfio.h>
+
+/* NUMA is not supported on all platforms */
+#ifdef _ODP_HAVE_NUMA_LIBRARY
+#include <numa.h>
+#else
+#define numa_num_configured_nodes() 1
+#endif
-#if ODP_DPDK_ZERO_COPY
+#include <ctype.h>
+#include <errno.h>
+#include <sched.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0)
+ #define RTE_MBUF_F_RX_RSS_HASH PKT_RX_RSS_HASH
+ #define RTE_MBUF_F_TX_IPV4 PKT_TX_IPV4
+ #define RTE_MBUF_F_TX_IPV6 PKT_TX_IPV6
+ #define RTE_MBUF_F_TX_IP_CKSUM PKT_TX_IP_CKSUM
+ #define RTE_MBUF_F_TX_UDP_CKSUM PKT_TX_UDP_CKSUM
+ #define RTE_MBUF_F_TX_TCP_CKSUM PKT_TX_TCP_CKSUM
+ #define RTE_MEMPOOL_REGISTER_OPS MEMPOOL_REGISTER_OPS
+
+ #define RTE_ETH_RSS_IPV4 ETH_RSS_IPV4
+ #define RTE_ETH_RSS_FRAG_IPV4 ETH_RSS_FRAG_IPV4
+ #define RTE_ETH_RSS_NONFRAG_IPV4_TCP ETH_RSS_NONFRAG_IPV4_TCP
+ #define RTE_ETH_RSS_NONFRAG_IPV4_UDP ETH_RSS_NONFRAG_IPV4_UDP
+ #define RTE_ETH_RSS_NONFRAG_IPV4_OTHER ETH_RSS_NONFRAG_IPV4_OTHER
+
+ #define RTE_ETH_RSS_IPV6 ETH_RSS_IPV6
+ #define RTE_ETH_RSS_IPV6_EX ETH_RSS_IPV6_EX
+ #define RTE_ETH_RSS_IPV6_UDP_EX ETH_RSS_IPV6_UDP_EX
+ #define RTE_ETH_RSS_IPV6_TCP_EX ETH_RSS_IPV6_TCP_EX
+ #define RTE_ETH_RSS_FRAG_IPV6 ETH_RSS_FRAG_IPV6
+ #define RTE_ETH_RSS_NONFRAG_IPV6_TCP ETH_RSS_NONFRAG_IPV6_TCP
+ #define RTE_ETH_RSS_NONFRAG_IPV6_UDP ETH_RSS_NONFRAG_IPV6_UDP
+ #define RTE_ETH_RSS_NONFRAG_IPV6_OTHER ETH_RSS_NONFRAG_IPV6_OTHER
+
+ #define RTE_ETH_MQ_RX_RSS ETH_MQ_RX_RSS
+ #define RTE_ETH_MQ_TX_NONE ETH_MQ_TX_NONE
+
+ #define RTE_ETH_RX_OFFLOAD_IPV4_CKSUM DEV_RX_OFFLOAD_IPV4_CKSUM
+ #define RTE_ETH_RX_OFFLOAD_TCP_CKSUM DEV_RX_OFFLOAD_TCP_CKSUM
+ #define RTE_ETH_RX_OFFLOAD_UDP_CKSUM DEV_RX_OFFLOAD_UDP_CKSUM
+
+ #define RTE_ETH_TX_OFFLOAD_IPV4_CKSUM DEV_TX_OFFLOAD_IPV4_CKSUM
+ #define RTE_ETH_TX_OFFLOAD_SCTP_CKSUM DEV_TX_OFFLOAD_SCTP_CKSUM
+ #define RTE_ETH_TX_OFFLOAD_TCP_CKSUM DEV_TX_OFFLOAD_TCP_CKSUM
+ #define RTE_ETH_TX_OFFLOAD_UDP_CKSUM DEV_TX_OFFLOAD_UDP_CKSUM
+
+ #define RTE_ETH_FC_FULL RTE_FC_FULL
+ #define RTE_ETH_FC_RX_PAUSE RTE_FC_RX_PAUSE
+ #define RTE_ETH_FC_TX_PAUSE RTE_FC_TX_PAUSE
+ #define RTE_ETH_LINK_AUTONEG ETH_LINK_AUTONEG
+ #define RTE_ETH_LINK_FULL_DUPLEX ETH_LINK_FULL_DUPLEX
+ #define RTE_ETH_LINK_UP ETH_LINK_UP
+ #define RTE_ETH_SPEED_NUM_NONE ETH_SPEED_NUM_NONE
+#endif
+
+#define MEMPOOL_FLAGS 0
+
+#if _ODP_DPDK_ZERO_COPY
ODP_STATIC_ASSERT(CONFIG_PACKET_HEADROOM == RTE_PKTMBUF_HEADROOM,
"ODP and DPDK headroom sizes not matching!");
-ODP_STATIC_ASSERT(PKT_EXTRA_LEN >= sizeof(struct rte_mbuf),
- "DPDK rte_mbuf won't fit in odp_packet_hdr_t.extra!");
#endif
+/* DPDK poll mode drivers requiring minimum RX burst size DPDK_MIN_RX_BURST */
+#define IXGBE_DRV_NAME "net_ixgbe"
+#define I40E_DRV_NAME "net_i40e"
+
+#define PCAP_DRV_NAME "net_pcap"
+
+#define DPDK_MEMORY_MB 512
+#define DPDK_NB_MBUF 16384
+#define DPDK_MBUF_BUF_SIZE RTE_MBUF_DEFAULT_BUF_SIZE
+#define DPDK_MEMPOOL_CACHE_SIZE 64
+
+ODP_STATIC_ASSERT((DPDK_NB_MBUF % DPDK_MEMPOOL_CACHE_SIZE == 0) &&
+ (DPDK_MEMPOOL_CACHE_SIZE <= RTE_MEMPOOL_CACHE_MAX_SIZE) &&
+ (DPDK_MEMPOOL_CACHE_SIZE <= DPDK_MBUF_BUF_SIZE * 10 / 15)
+ , "DPDK mempool cache size failure");
+
+/* Minimum RX burst size */
+#define DPDK_MIN_RX_BURST 4
+
+ODP_STATIC_ASSERT(DPDK_MIN_RX_BURST <= UINT8_MAX, "DPDK_MIN_RX_BURST too large");
+
+/* Limits for setting link MTU */
+#define DPDK_MTU_MIN (RTE_ETHER_MIN_MTU + _ODP_ETHHDR_LEN)
+#define DPDK_MTU_MAX (9000 + _ODP_ETHHDR_LEN)
+
+/** DPDK runtime configuration options */
+typedef struct {
+ int num_rx_desc_default;
+ int num_tx_desc_default;
+ uint8_t multicast_en;
+ uint8_t rx_drop_en;
+ uint8_t set_flow_hash;
+} dpdk_opt_t;
+
+typedef struct {
+ /* Array for storing extra RX packets */
+ struct rte_mbuf *pkt[DPDK_MIN_RX_BURST];
+ /* Head of cache */
+ uint8_t idx;
+ /* Packets in cache */
+ uint8_t count;
+} pkt_cache_t;
+
+/* DPDK pktio specific data */
+typedef struct ODP_ALIGNED_CACHE {
+ /* --- Fast path data --- */
+
+ /* Function for mbuf to ODP packet conversion */
+ int (*mbuf_to_pkt_fn)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
+ struct rte_mbuf *mbuf_table[], uint16_t mbuf_num, odp_time_t *ts);
+ /* Packet output capabilities */
+ odp_pktout_config_opt_t pktout_capa;
+ /* DPDK port identifier */
+ uint16_t port_id;
+ /* Maximum transmission unit */
+ uint16_t mtu;
+ struct {
+ /* No locking for rx */
+ uint8_t lockless_rx;
+ /* No locking for tx */
+ uint8_t lockless_tx;
+ /* Store RX RSS hash as flow hash */
+ uint8_t set_flow_hash;
+ } flags;
+ /* Minimum RX burst size */
+ uint8_t min_rx_burst;
+ /* Cache for storing extra RX packets */
+ pkt_cache_t rx_cache[ODP_PKTIN_MAX_QUEUES];
+
+ /* --- Control path data --- */
+
+ /* Runtime config options */
+ dpdk_opt_t opt;
+ /* ODP packet pool */
+ odp_pool_t pool;
+ /* DPDK packet pool */
+ struct rte_mempool *pkt_pool;
+ /* RSS configuration */
+ struct rte_eth_rss_conf rss_conf;
+ /* Maximum packet length */
+ uint32_t data_room;
+ /* Maximum supported MTU value */
+ uint32_t mtu_max;
+ /* DPDK MTU has been modified */
+ uint8_t mtu_set;
+ /* Use system call to get/set vdev promisc mode */
+ uint8_t vdev_sysc_promisc;
+ /* Number of RX descriptors per queue */
+ uint16_t num_rx_desc[ODP_PKTIN_MAX_QUEUES];
+ /* Number of TX descriptors per queue */
+ uint16_t num_tx_desc[ODP_PKTOUT_MAX_QUEUES];
+
+ /* --- Locks for MT safe operations --- */
+
+ /* RX queue locks */
+ odp_ticketlock_t rx_lock[ODP_PKTIN_MAX_QUEUES] ODP_ALIGNED_CACHE;
+ /* TX queue locks */
+ odp_ticketlock_t tx_lock[ODP_PKTOUT_MAX_QUEUES] ODP_ALIGNED_CACHE;
+} pkt_dpdk_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_dpdk_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+typedef struct {
+ uint32_t dpdk_elt_size;
+ uint8_t pool_in_use;
+ struct rte_mempool *pkt_pool;
+} mem_src_data_t;
+
+ODP_STATIC_ASSERT(_ODP_POOL_MEM_SRC_DATA_SIZE >= sizeof(mem_src_data_t),
+ "_ODP_POOL_MEM_SRC_DATA_SIZE too small");
+
+static inline struct rte_mbuf *mbuf_from_pkt_hdr(odp_packet_hdr_t *pkt_hdr)
+{
+ return ((struct rte_mbuf *)pkt_hdr) - 1;
+}
+
+static inline odp_packet_hdr_t *pkt_hdr_from_mbuf(struct rte_mbuf *mbuf)
+{
+ return (odp_packet_hdr_t *)(mbuf + 1);
+}
+
+static inline pkt_dpdk_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_dpdk_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
+
+static inline mem_src_data_t *mem_src_priv(uint8_t *data)
+{
+ return (mem_src_data_t *)data;
+}
+
static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
-/* Has dpdk_pktio_init() been called */
-static odp_bool_t dpdk_initialized;
+static int dpdk_pktio_init(void);
+
+static int lookup_opt(const char *opt_name, const char *drv_name, int *val)
+{
+ const char *base = "pktio_dpdk";
+ int ret;
-#define MEMPOOL_OPS(hdl) \
-extern void mp_hdlr_init_##hdl(void)
+ ret = _odp_libconfig_lookup_ext_int(base, drv_name, opt_name, val);
+ if (ret == 0)
+ _ODP_ERR("Unable to find DPDK configuration option: %s\n", opt_name);
-MEMPOOL_OPS(ops_mp_mc);
-MEMPOOL_OPS(ops_sp_sc);
-MEMPOOL_OPS(ops_mp_sc);
-MEMPOOL_OPS(ops_sp_mc);
-MEMPOOL_OPS(ops_stack);
+ return ret;
+}
-/*
- * This function is not called from anywhere, it's only purpose is to make sure
- * that if ODP and DPDK are statically linked to an application, the GCC
- * constructors of mempool handlers are linked as well. Otherwise the linker
- * would omit them. It's not an issue with dynamic linking. */
-void refer_constructors(void);
-void refer_constructors(void)
+static int init_options(pktio_entry_t *pktio_entry,
+ const struct rte_eth_dev_info *dev_info)
{
- mp_hdlr_init_ops_mp_mc();
- mp_hdlr_init_ops_sp_sc();
- mp_hdlr_init_ops_mp_sc();
- mp_hdlr_init_ops_sp_mc();
- mp_hdlr_init_ops_stack();
+ dpdk_opt_t *opt = &pkt_priv(pktio_entry)->opt;
+ int val;
+
+ if (!lookup_opt("num_rx_desc", dev_info->driver_name,
+ &opt->num_rx_desc_default))
+ return -1;
+
+ if (!lookup_opt("num_tx_desc", dev_info->driver_name,
+ &opt->num_tx_desc_default))
+ return -1;
+
+ if (!lookup_opt("rx_drop_en", dev_info->driver_name, &val))
+ return -1;
+ opt->rx_drop_en = !!val;
+
+ if (!lookup_opt("set_flow_hash", NULL, &val))
+ return -1;
+ opt->set_flow_hash = !!val;
+
+ if (!lookup_opt("multicast_en", NULL, &val))
+ return -1;
+ opt->multicast_en = !!val;
+
+ _ODP_DBG("DPDK interface (%s): %" PRIu16 "\n", dev_info->driver_name,
+ pkt_priv(pktio_entry)->port_id);
+ _ODP_DBG(" multicast_en: %d\n", opt->multicast_en);
+ _ODP_DBG(" num_rx_desc: %d\n", opt->num_rx_desc_default);
+ _ODP_DBG(" num_tx_desc: %d\n", opt->num_tx_desc_default);
+ _ODP_DBG(" rx_drop_en: %d\n", opt->rx_drop_en);
+
+ return 0;
}
/**
* Calculate valid cache size for DPDK packet pool
*/
-static unsigned cache_size(uint32_t num)
+static uint32_t cache_size(uint32_t num)
{
- unsigned size = 0;
- unsigned i;
+ uint32_t size = 0;
+ uint32_t i;
if (!RTE_MEMPOOL_CACHE_MAX_SIZE)
return 0;
- i = ceil((double)num / RTE_MEMPOOL_CACHE_MAX_SIZE);
+ i = (num + RTE_MEMPOOL_CACHE_MAX_SIZE - 1) / RTE_MEMPOOL_CACHE_MAX_SIZE;
i = RTE_MAX(i, 2UL);
for (; i <= (num / 2); ++i)
if ((num % i) == 0) {
@@ -88,7 +318,7 @@ static unsigned cache_size(uint32_t num)
}
if (odp_unlikely(size > RTE_MEMPOOL_CACHE_MAX_SIZE ||
(uint32_t)size * 1.5 > num)) {
- ODP_ERR("Cache size calc failure: %d\n", size);
+ _ODP_ERR("Cache size calc failure: %d\n", size);
size = 0;
}
@@ -98,8 +328,7 @@ static unsigned cache_size(uint32_t num)
static inline uint16_t mbuf_data_off(struct rte_mbuf *mbuf,
odp_packet_hdr_t *pkt_hdr)
{
- return (uint64_t)pkt_hdr->buf_hdr.seg[0].data -
- (uint64_t)mbuf->buf_addr;
+ return (uintptr_t)pkt_hdr->seg_data - (uintptr_t)mbuf->buf_addr;
}
/**
@@ -113,103 +342,180 @@ static inline void mbuf_update(struct rte_mbuf *mbuf, odp_packet_hdr_t *pkt_hdr,
mbuf->data_len = pkt_len;
mbuf->pkt_len = pkt_len;
mbuf->refcnt = 1;
+ mbuf->ol_flags = 0;
- if (odp_unlikely(pkt_hdr->buf_hdr.base_data !=
- pkt_hdr->buf_hdr.seg[0].data))
+ if (odp_unlikely(((uint8_t *)mbuf->buf_addr + mbuf->data_off) != pkt_hdr->seg_data))
mbuf->data_off = mbuf_data_off(mbuf, pkt_hdr);
}
/**
- * Initialize mbuf
- *
- * Called once per ODP packet.
+ * Initialize packet mbuf. Modified version of standard rte_pktmbuf_init()
+ * function.
*/
-static void mbuf_init(struct rte_mempool *mp, struct rte_mbuf *mbuf,
- odp_packet_hdr_t *pkt_hdr)
+static void pktmbuf_init(struct rte_mempool *mp, void *opaque_arg ODP_UNUSED,
+ void *_m, unsigned i ODP_UNUSED)
{
- void *buf_addr = pkt_hdr->buf_hdr.base_data - RTE_PKTMBUF_HEADROOM;
+ struct rte_mbuf *m = _m;
+ uint32_t mbuf_size, buf_len, priv_size;
+ odp_packet_hdr_t *pkt_hdr;
+ void *buf_addr;
- memset(mbuf, 0, sizeof(struct rte_mbuf));
+ pkt_hdr = pkt_hdr_from_mbuf(m);
+ buf_addr = pkt_hdr->event_hdr.base_data - RTE_PKTMBUF_HEADROOM;
- mbuf->priv_size = 0;
- mbuf->buf_addr = buf_addr;
- mbuf->buf_physaddr = rte_mem_virt2phy(buf_addr);
- if (odp_unlikely(mbuf->buf_physaddr == RTE_BAD_PHYS_ADDR ||
- mbuf->buf_physaddr == 0))
- ODP_ABORT("Failed to map virt addr to phy");
+ priv_size = rte_pktmbuf_priv_size(mp);
+ mbuf_size = sizeof(struct rte_mbuf);
+ buf_len = rte_pktmbuf_data_room_size(mp);
- mbuf->buf_len = (uint16_t)rte_pktmbuf_data_room_size(mp);
- mbuf->data_off = RTE_PKTMBUF_HEADROOM;
- mbuf->pool = mp;
- mbuf->refcnt = 1;
- mbuf->nb_segs = 1;
- mbuf->port = 0xff;
+ /* odp_packet_hdr_t stored in private data so don't zero */
+ memset(m, 0, mbuf_size);
+ m->priv_size = priv_size;
+ m->buf_addr = buf_addr;
+
+ m->buf_iova = rte_mem_virt2iova(buf_addr);
+ if (odp_unlikely(m->buf_iova == 0))
+ _ODP_ABORT("Bad IO virtual address\n");
- /* Store ODP packet handle inside rte_mbuf */
- mbuf->userdata = packet_handle(pkt_hdr);
- pkt_hdr->extra_type = PKT_EXTRA_TYPE_DPDK;
+ m->buf_len = (uint16_t)buf_len;
+ m->data_off = RTE_PKTMBUF_HEADROOM;
+
+ /* Init some constant fields */
+ m->pool = mp;
+ m->nb_segs = 1;
+ m->port = MBUF_INVALID_PORT;
+ rte_mbuf_refcnt_set(m, 1);
+ m->next = NULL;
}
/**
* Create custom DPDK packet pool
*/
static struct rte_mempool *mbuf_pool_create(const char *name,
- pool_t *pool_entry)
+ pool_t *pool_entry,
+ uint32_t dpdk_elt_size)
{
- struct rte_mempool *mp;
+ odp_shm_info_t shm_info;
+ struct rte_mempool *mp = NULL;
struct rte_pktmbuf_pool_private mbp_priv;
- unsigned elt_size;
- unsigned num;
- uint16_t data_room_size;
+ struct rte_mempool_objsz sz;
+ unsigned int elt_size = dpdk_elt_size;
+ unsigned int num = pool_entry->num, populated = 0;
+ uint32_t total_size;
+ uint64_t page_size, offset = 0, remainder = 0;
+ uint8_t *addr;
+ int ret;
if (!(pool_entry->mem_from_huge_pages)) {
- ODP_ERR("DPDK requires memory is allocated from huge pages\n");
- return NULL;
+ _ODP_ERR("DPDK requires memory is allocated from huge pages\n");
+ goto fail;
}
- num = pool_entry->num;
- data_room_size = pool_entry->max_seg_len + CONFIG_PACKET_HEADROOM;
- elt_size = sizeof(struct rte_mbuf) + (unsigned)data_room_size;
- mbp_priv.mbuf_data_room_size = data_room_size;
- mbp_priv.mbuf_priv_size = 0;
+ if (pool_entry->seg_len < RTE_MBUF_DEFAULT_BUF_SIZE) {
+ _ODP_ERR("Some NICs need at least %dB buffers to not segment "
+ "standard ethernet frames. Increase pool seg_len.\n",
+ RTE_MBUF_DEFAULT_BUF_SIZE);
+ goto fail;
+ }
+
+ if (odp_shm_info(pool_entry->shm, &shm_info)) {
+ _ODP_ERR("Failed to query SHM info.\n");
+ goto fail;
+ }
+
+ page_size = shm_info.page_size;
+ total_size = rte_mempool_calc_obj_size(elt_size, MEMPOOL_FLAGS, &sz);
+ if (total_size != pool_entry->block_size) {
+ _ODP_ERR("DPDK pool block size not matching to ODP pool: "
+ "%" PRIu32 "/%" PRIu32 "\n", total_size,
+ pool_entry->block_size);
+ goto fail;
+ }
mp = rte_mempool_create_empty(name, num, elt_size, cache_size(num),
sizeof(struct rte_pktmbuf_pool_private),
- rte_socket_id(), 0);
+ rte_socket_id(), MEMPOOL_FLAGS);
if (mp == NULL) {
- ODP_ERR("Failed to create empty DPDK packet pool\n");
- return NULL;
+ _ODP_ERR("Failed to create empty DPDK packet pool\n");
+ goto fail;
}
+ mp->pool_data = _odp_pool_handle(pool_entry);
+
if (rte_mempool_set_ops_byname(mp, "odp_pool", pool_entry)) {
- ODP_ERR("Failed setting mempool operations\n");
- return NULL;
+ _ODP_ERR("Failed setting mempool operations\n");
+ goto fail;
}
+ mbp_priv.mbuf_data_room_size = pool_entry->headroom +
+ pool_entry->seg_len + pool_entry->tailroom;
+ mbp_priv.mbuf_priv_size = RTE_ALIGN(sizeof(odp_packet_hdr_t),
+ RTE_MBUF_PRIV_ALIGN);
rte_pktmbuf_pool_init(mp, &mbp_priv);
- if (rte_mempool_ops_alloc(mp)) {
- ODP_ERR("Failed allocating mempool\n");
- return NULL;
+ /* DPDK expects buffers that would be crossing a hugepage boundary to be aligned to the
+ * boundary. This isn't the case with ODP pools as boundary-crossing buffers are skipped
+ * and unused but still part of the pool. Thus, populate the mempool with several virtually
+ * and physically contiguous chunks as dictated by the skipped buffers. */
+ for (uint64_t i = 0; i < pool_entry->shm_size; i += page_size) {
+ remainder = (page_size - offset) % total_size;
+ addr = pool_entry->base_addr + i + offset;
+ ret = rte_mempool_populate_iova(mp, (char *)addr, rte_mem_virt2iova(addr),
+ page_size - remainder - offset,
+ NULL, NULL);
+
+ if (ret <= 0) {
+ _ODP_ERR("Failed to populate mempool: %d\n", ret);
+ goto fail;
+ }
+
+ populated += ret;
+ offset = remainder ? total_size - remainder : 0;
+ }
+
+ if (populated != num) {
+ _ODP_ERR("Failed to populate mempool with all requested blocks, populated: %u, "
+ "requested: %u\n", populated, num);
+ goto fail;
+ }
+
+ /* Map pages for DMA access to enable VFIO usage */
+ for (uint64_t i = 0; i < pool_entry->shm_size; i += page_size) {
+ addr = pool_entry->base_addr + i;
+
+ rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD,
+ (uint64_t)(uintptr_t)addr,
+ rte_mem_virt2iova(addr), page_size);
}
+ rte_mempool_obj_iter(mp, pktmbuf_init, NULL);
+
return mp;
+
+fail:
+ if (mp)
+ rte_mempool_free(mp);
+ return NULL;
}
/* DPDK external memory pool operations */
-static int pool_enqueue(struct rte_mempool *mp ODP_UNUSED,
+static int pool_enqueue(struct rte_mempool *mp,
void * const *obj_table, unsigned num)
{
odp_packet_t pkt_tbl[num];
+ pool_t *pool_entry = (pool_t *)mp->pool_config;
+ mem_src_data_t *mem_src_data = mem_src_priv(pool_entry->mem_src_data);
unsigned i;
- if (odp_unlikely(num == 0))
+ if (odp_unlikely(num == 0 || !mem_src_data->pool_in_use))
return 0;
- for (i = 0; i < num; i++)
- pkt_tbl[i] = (odp_packet_t)((struct rte_mbuf *)
- obj_table[i])->userdata;
+ for (i = 0; i < num; i++) {
+ struct rte_mbuf *mbuf = (struct rte_mbuf *)obj_table[i];
+ odp_packet_hdr_t *pkt_hdr = pkt_hdr_from_mbuf(mbuf);
+
+ pkt_tbl[i] = packet_handle(pkt_hdr);
+ }
odp_packet_free_multi(pkt_tbl, num);
@@ -225,8 +531,8 @@ static int pool_dequeue_bulk(struct rte_mempool *mp, void **obj_table,
int pkts;
int i;
- pkts = packet_alloc_multi(pool, pool_entry->max_seg_len, packet_tbl,
- num);
+ pkts = _odp_packet_alloc_multi(pool, pool_entry->seg_len, packet_tbl,
+ num);
if (odp_unlikely(pkts != (int)num)) {
if (pkts > 0)
@@ -235,25 +541,16 @@ static int pool_dequeue_bulk(struct rte_mempool *mp, void **obj_table,
}
for (i = 0; i < pkts; i++) {
- odp_packet_t pkt = packet_tbl[i];
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
- struct rte_mbuf *mbuf = (struct rte_mbuf *)
- (uintptr_t)pkt_hdr->extra;
- if (pkt_hdr->extra_type != PKT_EXTRA_TYPE_DPDK)
- mbuf_init(mp, mbuf, pkt_hdr);
- obj_table[i] = mbuf;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(packet_tbl[i]);
+
+ obj_table[i] = mbuf_from_pkt_hdr(pkt_hdr);
}
return 0;
}
-static int pool_alloc(struct rte_mempool *mp)
+static int pool_alloc(struct rte_mempool *mp ODP_UNUSED)
{
- pool_t *pool_entry = (pool_t *)mp->pool_config;
-
- mp->pool_data = pool_entry->pool_hdl;
- mp->flags |= MEMPOOL_F_POOL_CREATED;
-
return 0;
}
@@ -263,7 +560,7 @@ static unsigned pool_get_count(const struct rte_mempool *mp)
odp_pool_info_t info;
if (odp_pool_info(pool, &info)) {
- ODP_ERR("Failed to read pool info\n");
+ _ODP_ERR("Failed to read pool info\n");
return 0;
}
return info.params.pkt.num;
@@ -282,15 +579,75 @@ static void pool_free(struct rte_mempool *mp)
}
}
-static void pool_destroy(void *pool)
+static void pool_destroy(uint8_t *data)
{
- struct rte_mempool *mp = (struct rte_mempool *)pool;
+ mem_src_data_t *mem_src_data = mem_src_priv(data);
- if (mp != NULL)
- rte_mempool_free(mp);
+ if (mem_src_data->pkt_pool != NULL) {
+ mem_src_data->pool_in_use = 0;
+ rte_mempool_free(mem_src_data->pkt_pool);
+ }
+
+ mem_src_data->pkt_pool = NULL;
}
-static struct rte_mempool_ops ops_stack = {
+static int pool_create(uint8_t *data, pool_t *pool)
+{
+ struct rte_mempool *pkt_pool;
+ char pool_name[RTE_MEMPOOL_NAMESIZE];
+ mem_src_data_t *mem_src_data = mem_src_priv(data);
+
+ mem_src_data->pkt_pool = NULL;
+
+ if (!_ODP_DPDK_ZERO_COPY)
+ return 0;
+
+ mem_src_data->pool_in_use = 0;
+ snprintf(pool_name, sizeof(pool_name),
+ "dpdk_pktpool_%" PRIu32 "_%" PRIu32 "", odp_global_ro.main_pid,
+ pool->pool_idx);
+ pkt_pool = mbuf_pool_create(pool_name, pool, mem_src_data->dpdk_elt_size);
+
+ if (pkt_pool == NULL) {
+ _ODP_ERR("Creating external DPDK pool failed\n");
+ return -1;
+ }
+
+ mem_src_data->pkt_pool = pkt_pool;
+ mem_src_data->pool_in_use = 1;
+
+ return 0;
+}
+
+static void pool_obj_size(uint8_t *data, uint32_t *block_size, uint32_t *block_offset,
+ uint32_t *flags)
+{
+ struct rte_mempool_objsz sz;
+ uint32_t size;
+ uint32_t total_size;
+ mem_src_data_t *mem_src_data = mem_src_priv(data);
+
+ if (!_ODP_DPDK_ZERO_COPY)
+ return;
+
+ if (odp_global_rw->dpdk_initialized == 0) {
+ if (dpdk_pktio_init()) {
+ _ODP_ERR("Initializing DPDK failed\n");
+ *block_size = 0;
+ return;
+ }
+ odp_global_rw->dpdk_initialized = 1;
+ }
+
+ *flags |= ODP_SHM_HP;
+ size = *block_size + sizeof(struct rte_mbuf);
+ total_size = rte_mempool_calc_obj_size(size, MEMPOOL_FLAGS, &sz);
+ mem_src_data->dpdk_elt_size = sz.elt_size;
+ *block_size = total_size;
+ *block_offset = sz.header_size + sizeof(struct rte_mbuf);
+}
+
+static struct rte_mempool_ops odp_pool_ops = {
.name = "odp_pool",
.alloc = pool_alloc,
.free = pool_free,
@@ -299,7 +656,7 @@ static struct rte_mempool_ops ops_stack = {
.get_count = pool_get_count
};
-MEMPOOL_REGISTER_OPS(ops_stack);
+RTE_MEMPOOL_REGISTER_OPS(odp_pool_ops)
static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
odp_packet_t pkt_table[],
@@ -311,28 +668,34 @@ static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
uint16_t pkt_len;
struct rte_mbuf *mbuf;
void *data;
- int i, j;
+ int i, j, num;
+ uint32_t max_len;
int nb_pkts = 0;
- int alloc_len, num;
- odp_pool_t pool = pktio_entry->s.pkt_dpdk.pool;
+ int nb_cls = 0;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ odp_pool_t pool = pkt_dpdk->pool;
+ odp_pktin_config_opt_t pktin_cfg = pktio_entry->config.pktin;
+ odp_pktio_t input = pktio_entry->handle;
+ uint16_t frame_offset = pktio_entry->pktin_frame_offset;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
/* Allocate maximum sized packets */
- alloc_len = pktio_entry->s.pkt_dpdk.data_room;
+ max_len = pkt_dpdk->data_room;
- num = packet_alloc_multi(pool, alloc_len, pkt_table, mbuf_num);
+ num = _odp_packet_alloc_multi(pool, max_len + frame_offset,
+ pkt_table, mbuf_num);
if (num != mbuf_num) {
- ODP_DBG("packet_alloc_multi() unable to allocate all packets: "
- "%d/%" PRIu16 " allocated\n", num, mbuf_num);
+ _ODP_DBG("_odp_packet_alloc_multi() unable to allocate all packets: "
+ "%d/%" PRIu16 " allocated\n", num, mbuf_num);
for (i = num; i < mbuf_num; i++)
rte_pktmbuf_free(mbuf_table[i]);
}
for (i = 0; i < num; i++) {
- odp_packet_hdr_t parsed_hdr;
-
mbuf = mbuf_table[i];
if (odp_unlikely(mbuf->nb_segs != 1)) {
- ODP_ERR("Segmented buffers not supported\n");
+ _ODP_ERR("Segmented buffers not supported\n");
goto fail;
}
@@ -340,40 +703,77 @@ static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
odp_prefetch(data);
pkt_len = rte_pktmbuf_pkt_len(mbuf);
+ pkt = pkt_table[i];
+ pkt_hdr = packet_hdr(pkt);
- if (pktio_cls_enabled(pktio_entry)) {
- if (cls_classify_packet(pktio_entry,
- (const uint8_t *)data,
- pkt_len, pkt_len, &pool,
- &parsed_hdr))
- goto fail;
+ if (layer) {
+ int ret;
+
+ packet_parse_reset(pkt_hdr, 1);
+ ret = _odp_dpdk_packet_parse_common(pkt_hdr, data, pkt_len, pkt_len,
+ mbuf, layer, pktin_cfg);
+ if (ret)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors);
+
+ if (ret < 0) {
+ odp_packet_free(pkt);
+ rte_pktmbuf_free(mbuf);
+ continue;
+ }
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, (const uint8_t *)data,
+ &new_pool, pkt_hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+
+ if (ret) {
+ odp_packet_free(pkt);
+ rte_pktmbuf_free(mbuf);
+ continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &pkt_hdr, new_pool))) {
+ odp_packet_free(pkt);
+ rte_pktmbuf_free(mbuf);
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+ continue;
+ }
+ }
}
- pkt = pkt_table[i];
- pkt_hdr = odp_packet_hdr(pkt);
- pull_tail(pkt_hdr, alloc_len - pkt_len);
+ pull_tail(pkt_hdr, max_len - pkt_len);
+ if (frame_offset)
+ pull_head(pkt_hdr, frame_offset);
if (odp_packet_copy_from_mem(pkt, 0, pkt_len, data) != 0)
goto fail;
- pkt_hdr->input = pktio_entry->s.handle;
-
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, pkt_hdr);
- else
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
+ pkt_hdr->input = input;
- if (mbuf->ol_flags & PKT_RX_RSS_HASH)
- odp_packet_flow_hash_set(pkt, mbuf->hash.rss);
+ if (mbuf->ol_flags & RTE_MBUF_F_RX_RSS_HASH)
+ packet_set_flow_hash(pkt_hdr, mbuf->hash.rss);
packet_set_ts(pkt_hdr, ts);
- pkt_table[nb_pkts++] = pkt;
-
rte_pktmbuf_free(mbuf);
+
+ if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ pkt_table[nb_cls++] = pkt;
+ nb_cls = _odp_cls_enq(pkt_table, nb_cls, (i + 1 == num));
+ } else {
+ pkt_table[nb_pkts++] = pkt;
+ }
}
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(nb_cls))
+ _odp_cls_enq(pkt_table, nb_cls, true);
+
return nb_pkts;
fail:
@@ -385,33 +785,155 @@ fail:
return (i > 0 ? i : -1);
}
+static inline int check_proto(void *l3_hdr, odp_bool_t *l3_proto_v4,
+ uint8_t *l4_proto)
+{
+ uint8_t l3_proto_ver = _ODP_IPV4HDR_VER(*(uint8_t *)l3_hdr);
+
+ if (l3_proto_ver == _ODP_IPV4) {
+ struct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr *)l3_hdr;
+
+ *l3_proto_v4 = 1;
+ if (!rte_ipv4_frag_pkt_is_fragmented(ip))
+ *l4_proto = ip->next_proto_id;
+ else
+ *l4_proto = 0;
+
+ return 0;
+ } else if (l3_proto_ver == _ODP_IPV6) {
+ struct rte_ipv6_hdr *ipv6 = (struct rte_ipv6_hdr *)l3_hdr;
+
+ *l3_proto_v4 = 0;
+ *l4_proto = ipv6->proto;
+ return 0;
+ }
+
+ return -1;
+}
+
+static inline uint16_t phdr_csum(odp_bool_t ipv4, void *l3_hdr,
+ uint64_t ol_flags)
+{
+ if (ipv4)
+ return rte_ipv4_phdr_cksum(l3_hdr, ol_flags);
+ else /*ipv6*/
+ return rte_ipv6_phdr_cksum(l3_hdr, ol_flags);
+}
+
+#define OL_TX_CHKSUM_PKT(_cfg, _capa, _proto, _ovr_set, _ovr) \
+ (_capa && _proto && (_ovr_set ? _ovr : _cfg))
+
+static inline void pkt_set_ol_tx(odp_pktout_config_opt_t *pktout_cfg,
+ odp_pktout_config_opt_t *pktout_capa,
+ odp_packet_hdr_t *pkt_hdr,
+ struct rte_mbuf *mbuf,
+ char *mbuf_data)
+{
+ void *l3_hdr, *l4_hdr;
+ uint8_t l4_proto;
+ odp_bool_t l3_proto_v4;
+ odp_bool_t ipv4_chksum_pkt, udp_chksum_pkt, tcp_chksum_pkt;
+ packet_parser_t *pkt_p = &pkt_hdr->p;
+
+ if (pkt_p->l3_offset == ODP_PACKET_OFFSET_INVALID)
+ return;
+
+ l3_hdr = (void *)(mbuf_data + pkt_p->l3_offset);
+
+ if (check_proto(l3_hdr, &l3_proto_v4, &l4_proto))
+ return;
+
+ ipv4_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.ipv4_chksum,
+ pktout_capa->bit.ipv4_chksum,
+ l3_proto_v4,
+ pkt_p->flags.l3_chksum_set,
+ pkt_p->flags.l3_chksum);
+ udp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.udp_chksum,
+ pktout_capa->bit.udp_chksum,
+ (l4_proto == _ODP_IPPROTO_UDP),
+ pkt_p->flags.l4_chksum_set,
+ pkt_p->flags.l4_chksum);
+ tcp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.tcp_chksum,
+ pktout_capa->bit.tcp_chksum,
+ (l4_proto == _ODP_IPPROTO_TCP),
+ pkt_p->flags.l4_chksum_set,
+ pkt_p->flags.l4_chksum);
+
+ if (!ipv4_chksum_pkt && !udp_chksum_pkt && !tcp_chksum_pkt)
+ return;
+
+ mbuf->l2_len = pkt_p->l3_offset - pkt_p->l2_offset;
+
+ if (l3_proto_v4)
+ mbuf->ol_flags = RTE_MBUF_F_TX_IPV4;
+ else
+ mbuf->ol_flags = RTE_MBUF_F_TX_IPV6;
+
+ if (ipv4_chksum_pkt) {
+ mbuf->ol_flags |= RTE_MBUF_F_TX_IP_CKSUM;
+
+ ((struct rte_ipv4_hdr *)l3_hdr)->hdr_checksum = 0;
+ mbuf->l3_len = _ODP_IPV4HDR_IHL(*(uint8_t *)l3_hdr) * 4;
+ }
+
+ if (pkt_p->l4_offset == ODP_PACKET_OFFSET_INVALID)
+ return;
+
+ mbuf->l3_len = pkt_p->l4_offset - pkt_p->l3_offset;
+
+ l4_hdr = (void *)(mbuf_data + pkt_p->l4_offset);
+
+ if (udp_chksum_pkt) {
+ mbuf->ol_flags |= RTE_MBUF_F_TX_UDP_CKSUM;
+
+ ((struct rte_udp_hdr *)l4_hdr)->dgram_cksum =
+ phdr_csum(l3_proto_v4, l3_hdr, mbuf->ol_flags);
+ } else if (tcp_chksum_pkt) {
+ mbuf->ol_flags |= RTE_MBUF_F_TX_TCP_CKSUM;
+
+ ((struct rte_tcp_hdr *)l4_hdr)->cksum =
+ phdr_csum(l3_proto_v4, l3_hdr, mbuf->ol_flags);
+ }
+}
+
static inline int pkt_to_mbuf(pktio_entry_t *pktio_entry,
struct rte_mbuf *mbuf_table[],
- const odp_packet_t pkt_table[], uint16_t num)
+ const odp_packet_t pkt_table[], uint16_t num,
+ uint16_t *tx_ts_idx)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- int i, j;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
char *data;
- uint16_t pkt_len;
+ uint16_t i, j, pkt_len;
+ uint8_t chksum_enabled = pktio_entry->enabled.chksum_insert;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
+ odp_pktout_config_opt_t *pktout_cfg = &pktio_entry->config.pktout;
if (odp_unlikely((rte_pktmbuf_alloc_bulk(pkt_dpdk->pkt_pool,
mbuf_table, num)))) {
- ODP_ERR("Failed to alloc mbuf\n");
+ _ODP_ERR("Failed to alloc mbuf\n");
return 0;
}
for (i = 0; i < num; i++) {
- pkt_len = _odp_packet_len(pkt_table[i]);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt_table[i]);
- if (pkt_len > pkt_dpdk->mtu) {
- if (i == 0)
- __odp_errno = EMSGSIZE;
+ pkt_len = packet_len(pkt_hdr);
+
+ if (odp_unlikely(pkt_len > pkt_dpdk->mtu))
goto fail;
- }
/* Packet always fits in mbuf */
data = rte_pktmbuf_append(mbuf_table[i], pkt_len);
odp_packet_copy_to_mem(pkt_table[i], 0, pkt_len, data);
+
+ if (odp_unlikely(chksum_enabled))
+ pkt_set_ol_tx(pktout_cfg, &pkt_dpdk->pktout_capa, pkt_hdr,
+ mbuf_table[i], data);
+
+ if (odp_unlikely(tx_ts_enabled)) {
+ if (odp_unlikely(*tx_ts_idx == 0 && pkt_hdr->p.flags.ts_set))
+ *tx_ts_idx = i + 1;
+ }
}
return i;
@@ -419,68 +941,170 @@ fail:
for (j = i; j < num; j++)
rte_pktmbuf_free(mbuf_table[j]);
- return i;
+ return i > 0 ? i : -1;
+}
+
+static inline void prefetch_pkt(struct rte_mbuf *mbuf)
+{
+ odp_packet_hdr_t *pkt_hdr = pkt_hdr_from_mbuf(mbuf);
+ void *data = rte_pktmbuf_mtod(mbuf, char *);
+
+ odp_prefetch(pkt_hdr);
+ odp_prefetch_store((uint8_t *)pkt_hdr + ODP_CACHE_LINE_SIZE);
+ odp_prefetch(data);
}
+/**
+ * Convert mbufs when packet parsing is required
+ */
static inline int mbuf_to_pkt_zero(pktio_entry_t *pktio_entry,
odp_packet_t pkt_table[],
struct rte_mbuf *mbuf_table[],
uint16_t mbuf_num, odp_time_t *ts)
{
- odp_packet_t pkt;
- odp_packet_hdr_t *pkt_hdr;
- uint16_t pkt_len;
- struct rte_mbuf *mbuf;
- void *data;
- int i;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ const uint8_t set_flow_hash = pkt_dpdk->flags.set_flow_hash;
+ const odp_pktio_t input = pktio_entry->handle;
+ const odp_pktin_config_opt_t pktin_cfg = pktio_entry->config.pktin;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ int nb_cls = 0;
int nb_pkts = 0;
- odp_pool_t pool = pktio_entry->s.pkt_dpdk.pool;
- for (i = 0; i < mbuf_num; i++) {
- odp_packet_hdr_t parsed_hdr;
+ _ODP_ASSERT(layer != ODP_PROTO_LAYER_NONE);
+
+ prefetch_pkt(mbuf_table[0]);
+
+ if (odp_likely(mbuf_num > 1))
+ prefetch_pkt(mbuf_table[1]);
+
+ for (uint16_t i = 0; i < mbuf_num; i++) {
+ odp_packet_t pkt;
+ odp_packet_hdr_t *pkt_hdr;
+ struct rte_mbuf *mbuf;
+ void *data;
+ uint16_t pkt_len;
+ int ret;
+
+ if (odp_likely((i + 2) < mbuf_num))
+ prefetch_pkt(mbuf_table[i + 2]);
mbuf = mbuf_table[i];
if (odp_unlikely(mbuf->nb_segs != 1)) {
- ODP_ERR("Segmented buffers not supported\n");
+ _ODP_ERR("Segmented buffers not supported\n");
rte_pktmbuf_free(mbuf);
continue;
}
data = rte_pktmbuf_mtod(mbuf, char *);
pkt_len = rte_pktmbuf_pkt_len(mbuf);
+ pkt_hdr = pkt_hdr_from_mbuf(mbuf);
+ pkt = packet_handle(pkt_hdr);
+ packet_init(pkt_hdr, pkt_len);
+
+ /* Init buffer segments. Currently, only single segment packets
+ * are supported. */
+ pkt_hdr->seg_data = data;
+ pkt_hdr->input = input;
+
+ if (set_flow_hash && (mbuf->ol_flags & RTE_MBUF_F_RX_RSS_HASH))
+ packet_set_flow_hash(pkt_hdr, mbuf->hash.rss);
+
+ packet_set_ts(pkt_hdr, ts);
+
+ ret = _odp_dpdk_packet_parse_common(pkt_hdr, data, pkt_len, pkt_len,
+ mbuf, layer, pktin_cfg);
+ if (ret)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors);
+
+ if (ret < 0) {
+ rte_pktmbuf_free(mbuf);
+ continue;
+ }
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
- pkt = (odp_packet_t)mbuf->userdata;
- pkt_hdr = odp_packet_hdr(pkt);
+ ret = _odp_cls_classify_packet(pktio_entry, (const uint8_t *)data,
+ &new_pool, pkt_hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
- if (pktio_cls_enabled(pktio_entry)) {
- if (cls_classify_packet(pktio_entry,
- (const uint8_t *)data,
- pkt_len, pkt_len, &pool,
- &parsed_hdr))
- ODP_ERR("Unable to classify packet\n");
+ if (ret) {
rte_pktmbuf_free(mbuf);
continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(&pkt, &pkt_hdr, new_pool))) {
+ rte_pktmbuf_free(mbuf);
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+ continue;
+ }
+
+ /* Enqueue packet directly to CoS destination queue */
+ pkt_table[nb_cls++] = pkt;
+ nb_cls = _odp_cls_enq(pkt_table, nb_cls, (i + 1 == mbuf_num));
+ } else {
+ pkt_table[nb_pkts++] = pkt;
}
+ }
- /* Init buffer segments. Currently, only single segment packets
- * are supported. */
- pkt_hdr->buf_hdr.seg[0].data = data;
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(nb_cls))
+ _odp_cls_enq(pkt_table, nb_cls, true);
+
+ return nb_pkts;
+}
+
+/**
+ * Convert mbufs when packet parsing and classifier are disabled
+ */
+static inline int mbuf_to_pkt_zero_minimal(pktio_entry_t *pktio_entry,
+ odp_packet_t pkt_table[], struct rte_mbuf *mbuf_table[],
+ uint16_t mbuf_num, odp_time_t *ts)
+{
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ const uint8_t set_flow_hash = pkt_dpdk->flags.set_flow_hash;
+ const odp_pktio_t input = pktio_entry->handle;
+ uint16_t nb_pkts = 0;
+
+ _ODP_ASSERT(pktio_entry->parse_layer == ODP_PROTO_LAYER_NONE);
+ prefetch_pkt(mbuf_table[0]);
+
+ if (odp_likely(mbuf_num > 1))
+ prefetch_pkt(mbuf_table[1]);
+
+ for (int i = 0; i < mbuf_num; i++) {
+ odp_packet_hdr_t *pkt_hdr;
+ struct rte_mbuf *mbuf;
+ uint16_t pkt_len;
+
+ if (odp_likely((i + 2) < mbuf_num))
+ prefetch_pkt(mbuf_table[i + 2]);
+
+ mbuf = mbuf_table[i];
+ if (odp_unlikely(mbuf->nb_segs != 1)) {
+ _ODP_ERR("Segmented buffers not supported\n");
+ rte_pktmbuf_free(mbuf);
+ continue;
+ }
+
+ pkt_len = rte_pktmbuf_pkt_len(mbuf);
+ pkt_hdr = pkt_hdr_from_mbuf(mbuf);
packet_init(pkt_hdr, pkt_len);
- pkt_hdr->input = pktio_entry->s.handle;
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, pkt_hdr);
- else
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
+ /* Init buffer segments. Currently, only single segment packets
+ * are supported. */
+ pkt_hdr->seg_data = rte_pktmbuf_mtod(mbuf, uint8_t *);
+ pkt_hdr->input = input;
- if (mbuf->ol_flags & PKT_RX_RSS_HASH)
- odp_packet_flow_hash_set(pkt, mbuf->hash.rss);
+ if (set_flow_hash && (mbuf->ol_flags & RTE_MBUF_F_RX_RSS_HASH))
+ packet_set_flow_hash(pkt_hdr, mbuf->hash.rss);
packet_set_ts(pkt_hdr, ts);
- pkt_table[nb_pkts++] = pkt;
+ pkt_table[nb_pkts++] = packet_handle(pkt_hdr);
}
return nb_pkts;
@@ -489,50 +1113,52 @@ static inline int mbuf_to_pkt_zero(pktio_entry_t *pktio_entry,
static inline int pkt_to_mbuf_zero(pktio_entry_t *pktio_entry,
struct rte_mbuf *mbuf_table[],
const odp_packet_t pkt_table[], uint16_t num,
- uint16_t *copy_count)
+ uint16_t *copy_count, uint16_t cpy_idx[], uint16_t *tx_ts_idx)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- int i;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ odp_pktout_config_opt_t *pktout_cfg = &pktio_entry->config.pktout;
+ odp_pktout_config_opt_t *pktout_capa = &pkt_dpdk->pktout_capa;
+ uint16_t mtu = pkt_dpdk->mtu;
+ uint16_t i;
+ uint8_t chksum_enabled = pktio_entry->enabled.chksum_insert;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
*copy_count = 0;
for (i = 0; i < num; i++) {
odp_packet_t pkt = pkt_table[i];
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
- struct rte_mbuf *mbuf = (struct rte_mbuf *)
- (uintptr_t)pkt_hdr->extra;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+ struct rte_mbuf *mbuf = mbuf_from_pkt_hdr(pkt_hdr);
uint16_t pkt_len = odp_packet_len(pkt);
- if (odp_unlikely(pkt_len > pkt_dpdk->mtu))
+ if (odp_unlikely(pkt_len > mtu))
goto fail;
- if (odp_likely(pkt_hdr->buf_hdr.segcount == 1 &&
- pkt_hdr->extra_type == PKT_EXTRA_TYPE_DPDK)) {
+ if (odp_likely(pkt_hdr->seg_count == 1)) {
mbuf_update(mbuf, pkt_hdr, pkt_len);
- } else {
- pool_t *pool_entry = pkt_hdr->buf_hdr.pool_ptr;
- if (pkt_hdr->buf_hdr.segcount != 1 ||
- !pool_entry->mem_from_huge_pages) {
- /* Fall back to packet copy */
- if (odp_unlikely(pkt_to_mbuf(pktio_entry, &mbuf,
- &pkt, 1) != 1))
- goto fail;
- (*copy_count)++;
+ if (odp_unlikely(chksum_enabled))
+ pkt_set_ol_tx(pktout_cfg, pktout_capa, pkt_hdr,
+ mbuf, odp_packet_data(pkt));
+ } else {
+ uint16_t dummy_idx = 0;
- } else {
- mbuf_init(pkt_dpdk->pkt_pool, mbuf,
- pkt_hdr);
- mbuf_update(mbuf, pkt_hdr, pkt_len);
- }
+ /* Fall back to packet copy */
+ if (odp_unlikely(pkt_to_mbuf(pktio_entry, &mbuf,
+ &pkt, 1, &dummy_idx) != 1))
+ goto fail;
+ cpy_idx[(*copy_count)++] = i;
}
mbuf_table[i] = mbuf;
+
+ if (odp_unlikely(tx_ts_enabled)) {
+ if (odp_unlikely(*tx_ts_idx == 0 && pkt_hdr->p.flags.ts_set))
+ *tx_ts_idx = i + 1;
+ }
}
return i;
fail:
- if (i == 0)
- __odp_errno = EMSGSIZE;
- return i;
+ return i > 0 ? i : -1;
}
/* Test if s has only digits or not. Dpdk pktio uses only digits.*/
@@ -546,7 +1172,7 @@ static int dpdk_netdev_is_valid(const char *s)
return 1;
}
-static uint32_t dpdk_vdev_mtu_get(uint8_t port_id)
+static uint32_t dpdk_vdev_mtu_get(uint16_t port_id)
{
struct rte_eth_dev_info dev_info;
struct ifreq ifr;
@@ -560,18 +1186,18 @@ static uint32_t dpdk_vdev_mtu_get(uint8_t port_id)
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
- ODP_ERR("Failed to create control socket\n");
+ _ODP_ERR("Failed to create control socket\n");
return 0;
}
- mtu = mtu_get_fd(sockfd, ifr.ifr_name);
+ mtu = _odp_mtu_get_fd(sockfd, ifr.ifr_name);
close(sockfd);
return mtu;
}
static uint32_t dpdk_mtu_get(pktio_entry_t *pktio_entry)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
uint32_t mtu = 0;
if (rte_eth_dev_get_mtu(pkt_dpdk->port_id, (uint16_t *)&mtu))
@@ -590,7 +1216,34 @@ static uint32_t dpdk_mtu_get(pktio_entry_t *pktio_entry)
return mtu;
}
-static int dpdk_vdev_promisc_mode_get(uint8_t port_id)
+static uint32_t dpdk_maxlen(pktio_entry_t *pktio_entry)
+{
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+
+ return pkt_dpdk->mtu;
+}
+
+static int dpdk_maxlen_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ uint16_t mtu;
+ int ret;
+
+ /* DPDK MTU value does not include Ethernet header */
+ mtu = maxlen_input - _ODP_ETHHDR_LEN;
+
+ ret = rte_eth_dev_set_mtu(pkt_dpdk->port_id, mtu);
+ if (odp_unlikely(ret))
+ _ODP_ERR("rte_eth_dev_set_mtu() failed: %d\n", ret);
+
+ pkt_dpdk->mtu = maxlen_input;
+ pkt_dpdk->mtu_set = 1;
+
+ return ret;
+}
+
+static int dpdk_vdev_promisc_mode_get(uint16_t port_id)
{
struct rte_eth_dev_info dev_info;
struct ifreq ifr;
@@ -604,16 +1257,16 @@ static int dpdk_vdev_promisc_mode_get(uint8_t port_id)
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
- ODP_ERR("Failed to create control socket\n");
+ _ODP_ERR("Failed to create control socket\n");
return -1;
}
- mode = promisc_mode_get_fd(sockfd, ifr.ifr_name);
+ mode = _odp_promisc_mode_get_fd(sockfd, ifr.ifr_name);
close(sockfd);
return mode;
}
-static int dpdk_vdev_promisc_mode_set(uint8_t port_id, int enable)
+static int dpdk_vdev_promisc_mode_set(uint16_t port_id, int enable)
{
struct rte_eth_dev_info dev_info;
struct ifreq ifr;
@@ -627,78 +1280,88 @@ static int dpdk_vdev_promisc_mode_set(uint8_t port_id, int enable)
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
- ODP_ERR("Failed to create control socket\n");
+ _ODP_ERR("Failed to create control socket\n");
return -1;
}
- mode = promisc_mode_set_fd(sockfd, ifr.ifr_name, enable);
+ mode = _odp_promisc_mode_set_fd(sockfd, ifr.ifr_name, enable);
close(sockfd);
return mode;
}
-static void rss_conf_to_hash_proto(struct rte_eth_rss_conf *rss_conf,
+static void hash_proto_to_rss_conf(struct rte_eth_rss_conf *rss_conf,
const odp_pktin_hash_proto_t *hash_proto)
{
- memset(rss_conf, 0, sizeof(struct rte_eth_rss_conf));
-
if (hash_proto->proto.ipv4_udp)
- rss_conf->rss_hf |= ETH_RSS_NONFRAG_IPV4_UDP;
+ rss_conf->rss_hf |= RTE_ETH_RSS_NONFRAG_IPV4_UDP;
if (hash_proto->proto.ipv4_tcp)
- rss_conf->rss_hf |= ETH_RSS_NONFRAG_IPV4_TCP;
+ rss_conf->rss_hf |= RTE_ETH_RSS_NONFRAG_IPV4_TCP;
if (hash_proto->proto.ipv4)
- rss_conf->rss_hf |= ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4 |
- ETH_RSS_NONFRAG_IPV4_OTHER;
+ rss_conf->rss_hf |= RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_FRAG_IPV4 |
+ RTE_ETH_RSS_NONFRAG_IPV4_OTHER;
if (hash_proto->proto.ipv6_udp)
- rss_conf->rss_hf |= ETH_RSS_NONFRAG_IPV6_UDP |
- ETH_RSS_IPV6_UDP_EX;
+ rss_conf->rss_hf |= RTE_ETH_RSS_NONFRAG_IPV6_UDP |
+ RTE_ETH_RSS_IPV6_UDP_EX;
if (hash_proto->proto.ipv6_tcp)
- rss_conf->rss_hf |= ETH_RSS_NONFRAG_IPV6_TCP |
- ETH_RSS_IPV6_TCP_EX;
+ rss_conf->rss_hf |= RTE_ETH_RSS_NONFRAG_IPV6_TCP |
+ RTE_ETH_RSS_IPV6_TCP_EX;
if (hash_proto->proto.ipv6)
- rss_conf->rss_hf |= ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6 |
- ETH_RSS_NONFRAG_IPV6_OTHER |
- ETH_RSS_IPV6_EX;
+ rss_conf->rss_hf |= RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_FRAG_IPV6 |
+ RTE_ETH_RSS_NONFRAG_IPV6_OTHER |
+ RTE_ETH_RSS_IPV6_EX;
rss_conf->rss_key = NULL;
}
-static int dpdk_setup_port(pktio_entry_t *pktio_entry)
+static int dpdk_setup_eth_dev(pktio_entry_t *pktio_entry)
{
int ret;
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- struct rte_eth_rss_conf rss_conf;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ struct rte_eth_conf eth_conf;
+ uint64_t rx_offloads = 0;
+ uint64_t tx_offloads = 0;
- /* Always set some hash functions to enable DPDK RSS hash calculation */
- if (pkt_dpdk->hash.all_bits == 0) {
- memset(&rss_conf, 0, sizeof(struct rte_eth_rss_conf));
- rss_conf.rss_hf = ETH_RSS_IP | ETH_RSS_TCP | ETH_RSS_UDP;
- } else {
- rss_conf_to_hash_proto(&rss_conf, &pkt_dpdk->hash);
- }
-
- struct rte_eth_conf port_conf = {
- .rxmode = {
- .mq_mode = ETH_MQ_RX_RSS,
- .split_hdr_size = 0,
- .header_split = 0,
- .hw_ip_checksum = 0,
- .hw_vlan_filter = 0,
- .hw_strip_crc = 0,
- .enable_scatter = 0,
- },
- .rx_adv_conf = {
- .rss_conf = rss_conf,
- },
- .txmode = {
- .mq_mode = ETH_MQ_TX_NONE,
- },
- };
+ memset(&eth_conf, 0, sizeof(eth_conf));
+
+ eth_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
+ eth_conf.txmode.mq_mode = RTE_ETH_MQ_TX_NONE;
+ eth_conf.rx_adv_conf.rss_conf = pkt_dpdk->rss_conf;
+
+ /* Setup RX checksum offloads */
+ if (pktio_entry->config.pktin.bit.ipv4_chksum)
+ rx_offloads |= RTE_ETH_RX_OFFLOAD_IPV4_CKSUM;
+
+ if (pktio_entry->config.pktin.bit.udp_chksum)
+ rx_offloads |= RTE_ETH_RX_OFFLOAD_UDP_CKSUM;
+
+ if (pktio_entry->config.pktin.bit.tcp_chksum)
+ rx_offloads |= RTE_ETH_RX_OFFLOAD_TCP_CKSUM;
+
+ eth_conf.rxmode.offloads = rx_offloads;
+
+ /* Setup TX checksum offloads */
+ if (pktio_entry->config.pktout.bit.ipv4_chksum_ena)
+ tx_offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;
+
+ if (pktio_entry->config.pktout.bit.udp_chksum_ena)
+ tx_offloads |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;
+
+ if (pktio_entry->config.pktout.bit.tcp_chksum_ena)
+ tx_offloads |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;
+
+ if (pktio_entry->config.pktout.bit.sctp_chksum_ena)
+ tx_offloads |= RTE_ETH_TX_OFFLOAD_SCTP_CKSUM;
+
+ eth_conf.txmode.offloads = tx_offloads;
+
+ if (tx_offloads)
+ pktio_entry->enabled.chksum_insert = 1;
ret = rte_eth_dev_configure(pkt_dpdk->port_id,
- pktio_entry->s.num_in_queue,
- pktio_entry->s.num_out_queue, &port_conf);
+ pktio_entry->num_in_queue,
+ pktio_entry->num_out_queue, &eth_conf);
if (ret < 0) {
- ODP_ERR("Failed to setup device: err=%d, port=%" PRIu8 "\n",
- ret, pkt_dpdk->port_id);
+ _ODP_ERR("Failed to setup device: err=%d, port=%" PRIu8 "\n",
+ ret, pkt_dpdk->port_id);
return -1;
}
return 0;
@@ -706,24 +1369,18 @@ static int dpdk_setup_port(pktio_entry_t *pktio_entry)
static int dpdk_close(pktio_entry_t *pktio_entry)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
unsigned idx;
unsigned i, j;
/* Free cache packets */
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
- idx = pkt_dpdk->rx_cache[i].s.idx;
+ for (i = 0; i < ODP_PKTIN_MAX_QUEUES; i++) {
+ idx = pkt_dpdk->rx_cache[i].idx;
- for (j = 0; j < pkt_dpdk->rx_cache[i].s.count; j++)
- rte_pktmbuf_free(pkt_dpdk->rx_cache[i].s.pkt[idx++]);
+ for (j = 0; j < pkt_dpdk->rx_cache[i].count; j++)
+ rte_pktmbuf_free(pkt_dpdk->rx_cache[i].pkt[idx++]);
}
- if (pktio_entry->s.state != PKTIO_STATE_OPENED)
- rte_eth_dev_close(pkt_dpdk->port_id);
-
- if (!ODP_DPDK_ZERO_COPY)
- rte_mempool_free(pkt_dpdk->pkt_pool);
-
return 0;
}
@@ -737,8 +1394,8 @@ static int dpdk_pktio_init(void)
int32_t masklen;
int mem_str_len;
int cmd_len;
+ int numa_nodes;
cpu_set_t original_cpuset;
- struct rte_config *cfg;
/**
* DPDK init changes the affinity of the calling thread, so after it
@@ -753,7 +1410,7 @@ static int dpdk_pktio_init(void)
i = pthread_getaffinity_np(pthread_self(),
sizeof(original_cpuset), &original_cpuset);
if (i != 0) {
- ODP_ERR("Failed to read thread affinity: %d\n", i);
+ _ODP_ERR("Failed to read thread affinity: %d\n", i);
return -1;
}
@@ -767,25 +1424,39 @@ static int dpdk_pktio_init(void)
masklen = odp_cpumask_to_str(&mask, mask_str, ODP_CPUMASK_STR_SIZE);
if (masklen < 0) {
- ODP_ERR("CPU mask error: d\n", masklen);
+ _ODP_ERR("CPU mask error: %" PRId32 "\n", masklen);
return -1;
}
- mem_str_len = snprintf(NULL, 0, "%d", DPDK_MEMORY_MB);
+ mem_str_len = snprintf(NULL, 0, "%d,", DPDK_MEMORY_MB);
+
+ /* numa_num_configured_nodes() may return 0 on some platforms */
+ numa_nodes = numa_num_configured_nodes();
+ if (numa_nodes <= 0)
+ numa_nodes = 1;
+
+ char mem_str[mem_str_len * numa_nodes + 1];
+
+ for (i = 0; i < numa_nodes; i++)
+ sprintf(&mem_str[i * mem_str_len], "%d,", DPDK_MEMORY_MB);
+ mem_str[mem_str_len * numa_nodes - 1] = '\0';
cmdline = getenv("ODP_PKTIO_DPDK_PARAMS");
if (cmdline == NULL)
cmdline = "";
/* masklen includes the terminating null as well */
- cmd_len = strlen("odpdpdk -c -m ") + masklen + mem_str_len +
- strlen(cmdline) + strlen(" ");
+ cmd_len = snprintf(NULL, 0, "odpdpdk --file-prefix %" PRIu32 "_ "
+ "--proc-type auto -c %s --socket-mem %s %s ",
+ odp_global_ro.main_pid, mask_str, mem_str, cmdline);
char full_cmd[cmd_len];
/* first argument is facility log, simply bind it to odpdpdk for now.*/
- cmd_len = snprintf(full_cmd, cmd_len, "odpdpdk -c %s -m %d %s",
- mask_str, DPDK_MEMORY_MB, cmdline);
+ cmd_len = snprintf(full_cmd, cmd_len,
+ "odpdpdk --file-prefix %" PRIu32 "_ "
+ "--proc-type auto -c %s --socket-mem %s %s ",
+ odp_global_ro.main_pid, mask_str, mem_str, cmdline);
for (i = 0, dpdk_argc = 1; i < cmd_len; ++i) {
if (isspace(full_cmd[i]))
@@ -797,29 +1468,30 @@ static int dpdk_pktio_init(void)
dpdk_argc = rte_strsplit(full_cmd, strlen(full_cmd), dpdk_argv,
dpdk_argc, ' ');
for (i = 0; i < dpdk_argc; ++i)
- ODP_DBG("arg[%d]: %s\n", i, dpdk_argv[i]);
+ _ODP_DBG("arg[%d]: %s\n", i, dpdk_argv[i]);
i = rte_eal_init(dpdk_argc, dpdk_argv);
+ /* Force getopt() to reset its internal state */
+ optind = 0;
+
if (i < 0) {
- ODP_ERR("Cannot init the Intel DPDK EAL!\n");
+ _ODP_ERR("Cannot init the Intel DPDK EAL!\n");
return -1;
} else if (i + 1 != dpdk_argc) {
- ODP_DBG("Some DPDK args were not processed!\n");
- ODP_DBG("Passed: %d Consumed %d\n", dpdk_argc, i + 1);
+ _ODP_DBG("Some DPDK args were not processed!\n");
+ _ODP_DBG("Passed: %d Consumed %d\n", dpdk_argc, i + 1);
}
- ODP_DBG("rte_eal_init OK\n");
+ _ODP_DBG("rte_eal_init OK\n");
- rte_set_log_level(RTE_LOG_WARNING);
+ rte_log_set_global_level(RTE_LOG_WARNING);
i = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
&original_cpuset);
if (i)
- ODP_ERR("Failed to reset thread affinity: %d\n", i);
+ _ODP_ERR("Failed to reset thread affinity: %d\n", i);
- cfg = rte_eal_get_configuration();
- for (i = 0; i < RTE_MAX_LCORE; i++)
- cfg->lcore_role[i] = ROLE_RTE;
+ _ODP_PRINT("\nDPDK version: %s\n", rte_version());
return 0;
}
@@ -828,12 +1500,12 @@ static int dpdk_pktio_init(void)
static int dpdk_pktio_init_global(void)
{
if (getenv("ODP_PKTIO_DISABLE_DPDK")) {
- ODP_PRINT("PKTIO: dpdk pktio skipped,"
- " enabled export ODP_PKTIO_DISABLE_DPDK=1.\n");
+ _ODP_PRINT("PKTIO: dpdk pktio skipped,"
+ " enabled export ODP_PKTIO_DISABLE_DPDK=1.\n");
disable_pktio = 1;
} else {
- ODP_PRINT("PKTIO: initialized dpdk pktio,"
- " use export ODP_PKTIO_DISABLE_DPDK=1 to disable.\n");
+ _ODP_PRINT("PKTIO: initialized dpdk pktio,"
+ " use export ODP_PKTIO_DISABLE_DPDK=1 to disable.\n");
}
return 0;
}
@@ -844,7 +1516,7 @@ static int dpdk_pktio_init_local(void)
cpu = sched_getcpu();
if (cpu < 0) {
- ODP_ERR("getcpu failed\n");
+ _ODP_ERR("getcpu failed\n");
return -1;
}
@@ -853,25 +1525,129 @@ static int dpdk_pktio_init_local(void)
return 0;
}
+static void dpdk_mempool_free(struct rte_mempool *mp, void *arg ODP_UNUSED)
+{
+ rte_mempool_free(mp);
+}
+
+static int dpdk_pktio_term(void)
+{
+ uint16_t port_id;
+
+ if (!odp_global_rw->dpdk_initialized)
+ return 0;
+
+ RTE_ETH_FOREACH_DEV(port_id) {
+ rte_eth_dev_close(port_id);
+ }
+
+ if (!_ODP_DPDK_ZERO_COPY)
+ rte_mempool_walk(dpdk_mempool_free, NULL);
+
+ return 0;
+}
+
+static void prepare_rss_conf(pktio_entry_t *pktio_entry,
+ const odp_pktin_queue_param_t *p)
+{
+ struct rte_eth_dev_info dev_info;
+ uint64_t rss_hf_capa;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ uint16_t port_id = pkt_dpdk->port_id;
+
+ memset(&pkt_dpdk->rss_conf, 0, sizeof(struct rte_eth_rss_conf));
+
+ if (!p->hash_enable)
+ return;
+
+ rte_eth_dev_info_get(port_id, &dev_info);
+ rss_hf_capa = dev_info.flow_type_rss_offloads;
+
+ /* Print debug info about unsupported hash protocols */
+ if (p->hash_proto.proto.ipv4 &&
+ ((rss_hf_capa & RTE_ETH_RSS_IPV4) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv4 not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ if (p->hash_proto.proto.ipv4_udp &&
+ ((rss_hf_capa & RTE_ETH_RSS_NONFRAG_IPV4_UDP) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv4_udp not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ if (p->hash_proto.proto.ipv4_tcp &&
+ ((rss_hf_capa & RTE_ETH_RSS_NONFRAG_IPV4_TCP) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv4_tcp not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ if (p->hash_proto.proto.ipv6 &&
+ ((rss_hf_capa & RTE_ETH_RSS_IPV6) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv6 not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ if (p->hash_proto.proto.ipv6_udp &&
+ ((rss_hf_capa & RTE_ETH_RSS_NONFRAG_IPV6_UDP) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv6_udp not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ if (p->hash_proto.proto.ipv6_tcp &&
+ ((rss_hf_capa & RTE_ETH_RSS_NONFRAG_IPV6_TCP) == 0))
+ _ODP_PRINT("DPDK: hash_proto.ipv6_tcp not supported (rss_hf_capa 0x%" PRIx64 ")\n",
+ rss_hf_capa);
+
+ hash_proto_to_rss_conf(&pkt_dpdk->rss_conf, &p->hash_proto);
+
+ /* Filter out unsupported hash functions */
+ pkt_dpdk->rss_conf.rss_hf &= rss_hf_capa;
+}
+
static int dpdk_input_queues_config(pktio_entry_t *pktio_entry,
const odp_pktin_queue_param_t *p)
{
- odp_pktin_mode_t mode = pktio_entry->s.param.in_mode;
- odp_bool_t lockless;
+ struct rte_eth_dev_info dev_info;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ odp_pktin_mode_t mode = pktio_entry->param.in_mode;
+ uint8_t lockless;
+ int ret;
+
+ prepare_rss_conf(pktio_entry, p);
/**
* Scheduler synchronizes input queue polls. Only single thread
* at a time polls a queue */
- if (mode == ODP_PKTIN_MODE_SCHED ||
- p->op_mode == ODP_PKTIO_OP_MT_UNSAFE)
+ if (mode == ODP_PKTIN_MODE_SCHED || p->op_mode == ODP_PKTIO_OP_MT_UNSAFE)
lockless = 1;
else
lockless = 0;
- if (p->hash_enable && p->num_queues > 1)
- pktio_entry->s.pkt_dpdk.hash = p->hash_proto;
+ pkt_dpdk->flags.lockless_rx = lockless;
+
+ ret = rte_eth_dev_info_get(pkt_dpdk->port_id, &dev_info);
+ if (ret) {
+ _ODP_ERR("DPDK: rte_eth_dev_info_get() failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Configure RX descriptors */
+ for (uint32_t i = 0; i < p->num_queues; i++) {
+ uint16_t num_rx_desc = pkt_dpdk->opt.num_rx_desc_default;
+
+ if (mode == ODP_PKTIN_MODE_DIRECT && p->queue_size[i] != 0) {
+ num_rx_desc = p->queue_size[i];
+ /* Make sure correct alignment is used */
+ if (dev_info.rx_desc_lim.nb_align)
+ num_rx_desc = RTE_ALIGN_MUL_CEIL(num_rx_desc,
+ dev_info.rx_desc_lim.nb_align);
+ }
- pktio_entry->s.pkt_dpdk.lockless_rx = lockless;
+ if (num_rx_desc < dev_info.rx_desc_lim.nb_min ||
+ num_rx_desc > dev_info.rx_desc_lim.nb_max ||
+ num_rx_desc % dev_info.rx_desc_lim.nb_align) {
+ _ODP_ERR("DPDK: invalid number of RX descriptors (%" PRIu16 ") for queue "
+ "%" PRIu32 "\n", num_rx_desc, i);
+ return -1;
+ }
+ pkt_dpdk->num_rx_desc[i] = num_rx_desc;
+ }
return 0;
}
@@ -879,38 +1655,212 @@ static int dpdk_input_queues_config(pktio_entry_t *pktio_entry,
static int dpdk_output_queues_config(pktio_entry_t *pktio_entry,
const odp_pktout_queue_param_t *p)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- odp_bool_t lockless;
+ struct rte_eth_dev_info dev_info;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ uint8_t lockless;
+ int ret;
if (p->op_mode == ODP_PKTIO_OP_MT_UNSAFE)
lockless = 1;
else
lockless = 0;
- pkt_dpdk->lockless_tx = lockless;
+ pkt_dpdk->flags.lockless_tx = lockless;
+
+ ret = rte_eth_dev_info_get(pkt_dpdk->port_id, &dev_info);
+ if (ret) {
+ _ODP_ERR("DPDK: rte_eth_dev_info_get() failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Configure TX descriptors */
+ for (uint32_t i = 0; i < p->num_queues; i++) {
+ uint16_t num_tx_desc = pkt_dpdk->opt.num_tx_desc_default;
+ if (p->queue_size[i] != 0) {
+ num_tx_desc = p->queue_size[i];
+ /* Make sure correct alignment is used */
+ if (dev_info.tx_desc_lim.nb_align)
+ num_tx_desc = RTE_ALIGN_MUL_CEIL(num_tx_desc,
+ dev_info.tx_desc_lim.nb_align);
+ }
+
+ if (num_tx_desc < dev_info.tx_desc_lim.nb_min ||
+ num_tx_desc > dev_info.tx_desc_lim.nb_max ||
+ num_tx_desc % dev_info.tx_desc_lim.nb_align) {
+ _ODP_ERR("DPDK: invalid number of TX descriptors (%" PRIu16 ") for queue "
+ "%" PRIu32 "\n", num_tx_desc, i);
+ return -1;
+ }
+ pkt_dpdk->num_tx_desc[i] = num_tx_desc;
+ }
return 0;
}
-static void dpdk_init_capability(pktio_entry_t *pktio_entry,
- struct rte_eth_dev_info *dev_info)
+static int dpdk_init_capability(pktio_entry_t *pktio_entry,
+ const struct rte_eth_dev_info *dev_info)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- odp_pktio_capability_t *capa = &pkt_dpdk->capa;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ odp_pktio_capability_t *capa = &pktio_entry->capa;
+ struct rte_ether_addr mac_addr;
+ int ptype_cnt;
+ int ptype_l3_ipv4 = 0;
+ int ptype_l4_tcp = 0;
+ int ptype_l4_udp = 0;
+ int ret;
+ uint32_t ptype_mask = RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK;
- memset(dev_info, 0, sizeof(struct rte_eth_dev_info));
memset(capa, 0, sizeof(odp_pktio_capability_t));
- rte_eth_dev_info_get(pkt_dpdk->port_id, dev_info);
capa->max_input_queues = RTE_MIN(dev_info->max_rx_queues,
- PKTIO_MAX_QUEUES);
+ ODP_PKTIN_MAX_QUEUES);
+ capa->min_input_queue_size = dev_info->rx_desc_lim.nb_min;
+ capa->max_input_queue_size = dev_info->rx_desc_lim.nb_max;
+
+ /* ixgbe devices support only 16 rx queues in RSS mode */
+ if (!strncmp(dev_info->driver_name, IXGBE_DRV_NAME,
+ strlen(IXGBE_DRV_NAME)))
+ capa->max_input_queues = RTE_MIN((unsigned)16,
+ capa->max_input_queues);
+
capa->max_output_queues = RTE_MIN(dev_info->max_tx_queues,
- PKTIO_MAX_QUEUES);
+ ODP_PKTOUT_MAX_QUEUES);
+ capa->min_output_queue_size = dev_info->tx_desc_lim.nb_min;
+ capa->max_output_queue_size = dev_info->tx_desc_lim.nb_max;
+
capa->set_op.op.promisc_mode = 1;
+ /* Check if setting default MAC address is supporter */
+ rte_eth_macaddr_get(pkt_dpdk->port_id, &mac_addr);
+ ret = rte_eth_dev_default_mac_addr_set(pkt_dpdk->port_id, &mac_addr);
+ if (ret == 0) {
+ capa->set_op.op.mac_addr = 1;
+ } else if (ret != -ENOTSUP && ret != -EPERM) {
+ _ODP_ERR("Failed to set interface default MAC: %d\n", ret);
+ return -1;
+ }
+
+ /* Check if setting MTU is supported */
+ ret = rte_eth_dev_set_mtu(pkt_dpdk->port_id, pkt_dpdk->mtu - _ODP_ETHHDR_LEN);
+ /* From DPDK 21.11 onwards, calling rte_eth_dev_set_mtu() before device is configured with
+ * rte_eth_dev_configure() will result in failure. The least hacky (unfortunately still
+ * very hacky) way to continue checking the support is to take into account that the
+ * function will fail earlier with -ENOTSUP if MTU setting is not supported by device than
+ * if the device was not yet configured. */
+ if (ret != -ENOTSUP) {
+ capa->set_op.op.maxlen = 1;
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = DPDK_MTU_MIN;
+ capa->maxlen.max_input = pkt_dpdk->mtu_max;
+ capa->maxlen.min_output = DPDK_MTU_MIN;
+ capa->maxlen.max_output = pkt_dpdk->mtu_max;
+ }
+
+ ptype_cnt = rte_eth_dev_get_supported_ptypes(pkt_dpdk->port_id,
+ ptype_mask, NULL, 0);
+ if (ptype_cnt > 0) {
+ uint32_t ptypes[ptype_cnt];
+ int i;
+
+ ptype_cnt = rte_eth_dev_get_supported_ptypes(pkt_dpdk->port_id,
+ ptype_mask, ptypes,
+ ptype_cnt);
+ for (i = 0; i < ptype_cnt; i++)
+ switch (ptypes[i]) {
+ case RTE_PTYPE_L3_IPV4:
+ /* Fall through */
+ case RTE_PTYPE_L3_IPV4_EXT_UNKNOWN:
+ /* Fall through */
+ case RTE_PTYPE_L3_IPV4_EXT:
+ ptype_l3_ipv4 = 1;
+ break;
+ case RTE_PTYPE_L4_TCP:
+ ptype_l4_tcp = 1;
+ break;
+ case RTE_PTYPE_L4_UDP:
+ ptype_l4_udp = 1;
+ break;
+ }
+ }
+
odp_pktio_config_init(&capa->config);
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
+
+ capa->config.pktin.bit.ipv4_chksum = ptype_l3_ipv4 &&
+ (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_IPV4_CKSUM) ? 1 : 0;
+ if (capa->config.pktin.bit.ipv4_chksum)
+ capa->config.pktin.bit.drop_ipv4_err = 1;
+
+ capa->config.pktin.bit.udp_chksum = ptype_l4_udp &&
+ (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_UDP_CKSUM) ? 1 : 0;
+ if (capa->config.pktin.bit.udp_chksum)
+ capa->config.pktin.bit.drop_udp_err = 1;
+
+ capa->config.pktin.bit.tcp_chksum = ptype_l4_tcp &&
+ (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_TCP_CKSUM) ? 1 : 0;
+ if (capa->config.pktin.bit.tcp_chksum)
+ capa->config.pktin.bit.drop_tcp_err = 1;
+
+ capa->config.pktout.bit.ipv4_chksum =
+ (dev_info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) ? 1 : 0;
+ capa->config.pktout.bit.udp_chksum =
+ (dev_info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) ? 1 : 0;
+ capa->config.pktout.bit.tcp_chksum =
+ (dev_info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) ? 1 : 0;
+
+ capa->config.pktout.bit.ipv4_chksum_ena =
+ capa->config.pktout.bit.ipv4_chksum;
+ capa->config.pktout.bit.udp_chksum_ena =
+ capa->config.pktout.bit.udp_chksum;
+ capa->config.pktout.bit.tcp_chksum_ena =
+ capa->config.pktout.bit.tcp_chksum;
+ capa->config.pktout.bit.ts_ena = 1;
+
+ if (!_ODP_DPDK_ZERO_COPY) {
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+ capa->free_ctrl.dont_free = 1;
+ }
+
+ /* Copy for fast path access */
+ pkt_dpdk->pktout_capa = capa->config.pktout;
+
+ capa->stats.pktio.counter.in_octets = 1;
+ capa->stats.pktio.counter.in_packets = 1;
+ capa->stats.pktio.counter.in_discards = 1;
+ capa->stats.pktio.counter.in_errors = 1;
+ capa->stats.pktio.counter.out_octets = 1;
+ capa->stats.pktio.counter.out_packets = 1;
+ capa->stats.pktio.counter.out_errors = 1;
+
+ capa->stats.pktin_queue.counter.octets = 1;
+ capa->stats.pktin_queue.counter.packets = 1;
+ capa->stats.pktin_queue.counter.errors = 1;
+
+ capa->stats.pktout_queue.counter.octets = 1;
+ capa->stats.pktout_queue.counter.packets = 1;
+
+ return 0;
+}
+
+/* Some DPDK PMD virtual devices, like PCAP, do not support promisc
+ * mode change. Use system call for them. */
+static void promisc_mode_check(pkt_dpdk_t *pkt_dpdk)
+{
+ int ret;
+
+ ret = rte_eth_promiscuous_enable(pkt_dpdk->port_id);
+
+ if (!rte_eth_promiscuous_get(pkt_dpdk->port_id))
+ pkt_dpdk->vdev_sysc_promisc = 1;
+
+ ret += rte_eth_promiscuous_disable(pkt_dpdk->port_id);
+
+ if (ret)
+ pkt_dpdk->vdev_sysc_promisc = 1;
}
static int dpdk_open(odp_pktio_t id ODP_UNUSED,
@@ -918,86 +1868,113 @@ static int dpdk_open(odp_pktio_t id ODP_UNUSED,
const char *netdev,
odp_pool_t pool)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
struct rte_eth_dev_info dev_info;
struct rte_mempool *pkt_pool;
char pool_name[RTE_MEMPOOL_NAMESIZE];
uint16_t data_room;
uint32_t mtu;
- int i;
+ int i, ret;
pool_t *pool_entry;
+ uint16_t port_id;
if (disable_pktio)
return -1;
if (pool == ODP_POOL_INVALID)
return -1;
- pool_entry = pool_entry_from_hdl(pool);
+ pool_entry = _odp_pool_entry(pool);
- if (!dpdk_netdev_is_valid(netdev)) {
- ODP_ERR("Invalid dpdk netdev: %s\n", netdev);
+ /* Init pktio entry */
+ memset(pkt_dpdk, 0, sizeof(*pkt_dpdk));
+
+ if (!rte_eth_dev_get_port_by_name(netdev, &port_id))
+ pkt_dpdk->port_id = port_id;
+ else if (dpdk_netdev_is_valid(netdev))
+ pkt_dpdk->port_id = atoi(netdev);
+ else {
+ _ODP_ERR("Invalid DPDK interface name: %s\n", netdev);
return -1;
}
/* Initialize DPDK here instead of odp_init_global() to enable running
* 'make check' without root privileges */
- if (dpdk_initialized == 0) {
+ if (odp_global_rw->dpdk_initialized == 0) {
dpdk_pktio_init();
- dpdk_initialized = 1;
+ odp_global_rw->dpdk_initialized = 1;
}
- /* Init pktio entry */
- memset(pkt_dpdk, 0, sizeof(*pkt_dpdk));
-
pkt_dpdk->pool = pool;
- pkt_dpdk->port_id = atoi(netdev);
- if (rte_eth_dev_count() == 0) {
- ODP_ERR("No DPDK ports found\n");
+ if (rte_eth_dev_count_avail() == 0) {
+ _ODP_ERR("No DPDK ports found\n");
return -1;
}
- dpdk_init_capability(pktio_entry, &dev_info);
+ memset(&dev_info, 0, sizeof(struct rte_eth_dev_info));
+ ret = rte_eth_dev_info_get(pkt_dpdk->port_id, &dev_info);
+ if (ret) {
+ _ODP_ERR("Failed to read device info: %d\n", ret);
+ return -1;
+ }
+
+ /* Initialize runtime options */
+ if (init_options(pktio_entry, &dev_info)) {
+ _ODP_ERR("Initializing runtime options failed\n");
+ return -1;
+ }
+ pkt_dpdk->flags.set_flow_hash = pkt_dpdk->opt.set_flow_hash; /* Copy for fast path access */
mtu = dpdk_mtu_get(pktio_entry);
if (mtu == 0) {
- ODP_ERR("Failed to read interface MTU\n");
+ _ODP_ERR("Failed to read interface MTU\n");
return -1;
}
pkt_dpdk->mtu = mtu + _ODP_ETHHDR_LEN;
+ pkt_dpdk->mtu_max = RTE_MAX(pkt_dpdk->mtu, DPDK_MTU_MAX);
+ pkt_dpdk->mtu_set = 0;
- /* Some DPDK PMD virtual devices, like PCAP, do not support promisc
- * mode change. Use system call for them. */
- rte_eth_promiscuous_enable(pkt_dpdk->port_id);
- if (!rte_eth_promiscuous_get(pkt_dpdk->port_id))
- pkt_dpdk->vdev_sysc_promisc = 1;
- rte_eth_promiscuous_disable(pkt_dpdk->port_id);
+ promisc_mode_check(pkt_dpdk);
- if (!strcmp(dev_info.driver_name, "rte_ixgbe_pmd"))
- pkt_dpdk->min_rx_burst = DPDK_IXGBE_MIN_RX_BURST;
+ if (pkt_dpdk->opt.multicast_en)
+ ret = rte_eth_allmulticast_enable(pkt_dpdk->port_id);
+ else
+ ret = rte_eth_allmulticast_disable(pkt_dpdk->port_id);
+
+ /* Not supported by all PMDs, so ignore the return value */
+ if (ret)
+ _ODP_DBG("Configuring multicast reception not supported by the PMD\n");
+
+ /* Drivers requiring minimum burst size. Supports also *_vf versions
+ * of the drivers. */
+ if (!strncmp(dev_info.driver_name, IXGBE_DRV_NAME,
+ strlen(IXGBE_DRV_NAME)) ||
+ !strncmp(dev_info.driver_name, I40E_DRV_NAME,
+ strlen(I40E_DRV_NAME)))
+ pkt_dpdk->min_rx_burst = DPDK_MIN_RX_BURST;
else
pkt_dpdk->min_rx_burst = 0;
- if (ODP_DPDK_ZERO_COPY) {
- if (pool_entry->ext_desc != NULL) {
- pkt_pool = (struct rte_mempool *)pool_entry->ext_desc;
- } else {
- snprintf(pool_name, sizeof(pool_name),
- "pktpool_%" PRIu32 "", pool_entry->pool_idx);
- pkt_pool = mbuf_pool_create(pool_name,
- pool_entry);
- pool_entry->ext_destroy = pool_destroy;
- pool_entry->ext_desc = pkt_pool;
- }
+
+ if (_ODP_DPDK_ZERO_COPY) {
+ mem_src_data_t *mem_src_data = mem_src_priv(pool_entry->mem_src_data);
+
+ pkt_pool = mem_src_data->pkt_pool;
} else {
snprintf(pool_name, sizeof(pool_name), "pktpool_%s", netdev);
- pkt_pool = rte_pktmbuf_pool_create(pool_name,
- DPDK_NB_MBUF,
- DPDK_MEMPOOL_CACHE_SIZE, 0,
- DPDK_MBUF_BUF_SIZE,
- rte_socket_id());
+ /* Check if the pool exists already */
+ pkt_pool = rte_mempool_lookup(pool_name);
+ if (pkt_pool == NULL) {
+ unsigned cache_size = DPDK_MEMPOOL_CACHE_SIZE;
+
+ pkt_pool = rte_pktmbuf_pool_create(pool_name,
+ DPDK_NB_MBUF,
+ cache_size, 0,
+ DPDK_MBUF_BUF_SIZE,
+ rte_socket_id());
+ }
}
if (pkt_pool == NULL) {
- ODP_ERR("Cannot init mbuf packet pool\n");
+ _ODP_ERR("Cannot init mbuf packet pool\n");
return -1;
}
@@ -1005,66 +1982,162 @@ static int dpdk_open(odp_pktio_t id ODP_UNUSED,
data_room = rte_pktmbuf_data_room_size(pkt_dpdk->pkt_pool) -
RTE_PKTMBUF_HEADROOM;
- pkt_dpdk->data_room = RTE_MIN(pool_entry->max_seg_len, data_room);
+ pkt_dpdk->data_room = RTE_MIN(pool_entry->seg_len, data_room);
+
+ /* Reserve room for packet input offset */
+ pkt_dpdk->data_room -= pktio_entry->pktin_frame_offset;
/* Mbuf chaining not yet supported */
- pkt_dpdk->mtu = RTE_MIN(pkt_dpdk->mtu, pkt_dpdk->data_room);
+ pkt_dpdk->mtu = RTE_MIN(pkt_dpdk->mtu, pkt_dpdk->data_room);
+ pkt_dpdk->mtu_max = RTE_MIN(pkt_dpdk->mtu_max, pkt_dpdk->data_room);
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
+ if (dpdk_init_capability(pktio_entry, &dev_info)) {
+ _ODP_ERR("Failed to initialize capability\n");
+ return -1;
+ }
+
+ for (i = 0; i < ODP_PKTIN_MAX_QUEUES; i++)
odp_ticketlock_init(&pkt_dpdk->rx_lock[i]);
+
+ for (i = 0; i < ODP_PKTOUT_MAX_QUEUES; i++)
odp_ticketlock_init(&pkt_dpdk->tx_lock[i]);
- }
rte_eth_stats_reset(pkt_dpdk->port_id);
return 0;
}
-static int dpdk_start(pktio_entry_t *pktio_entry)
+static int dpdk_setup_eth_tx(pktio_entry_t *pktio_entry,
+ const pkt_dpdk_t *pkt_dpdk,
+ const struct rte_eth_dev_info *dev_info)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
- uint8_t port_id = pkt_dpdk->port_id;
+ uint32_t i;
int ret;
- unsigned i;
+ uint16_t port_id = pkt_dpdk->port_id;
- /* DPDK doesn't support nb_rx_q/nb_tx_q being 0 */
- if (!pktio_entry->s.num_in_queue)
- pktio_entry->s.num_in_queue = 1;
- if (!pktio_entry->s.num_out_queue)
- pktio_entry->s.num_out_queue = 1;
-
- /* init port */
- if (dpdk_setup_port(pktio_entry)) {
- ODP_ERR("Failed to configure device\n");
- return -1;
- }
- /* Init TX queues */
- for (i = 0; i < pktio_entry->s.num_out_queue; i++) {
- ret = rte_eth_tx_queue_setup(port_id, i, DPDK_NM_TX_DESC,
+ for (i = 0; i < pktio_entry->num_out_queue; i++) {
+ ret = rte_eth_tx_queue_setup(port_id, i,
+ pkt_dpdk->num_tx_desc[i],
rte_eth_dev_socket_id(port_id),
- NULL);
+ &dev_info->default_txconf);
if (ret < 0) {
- ODP_ERR("Queue setup failed: err=%d, port=%" PRIu8 "\n",
- ret, port_id);
+ _ODP_ERR("Queue setup failed: err=%d, port=%" PRIu8 "\n", ret, port_id);
return -1;
}
}
- /* Init RX queues */
- for (i = 0; i < pktio_entry->s.num_in_queue; i++) {
- ret = rte_eth_rx_queue_setup(port_id, i, DPDK_NM_RX_DESC,
+
+ /* Set per queue statistics mappings. Not supported by all PMDs, so
+ * ignore the return value. */
+ for (i = 0; i < pktio_entry->num_out_queue && i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
+ ret = rte_eth_dev_set_tx_queue_stats_mapping(port_id, i, i);
+ if (ret) {
+ _ODP_DBG("Mapping per TX queue statistics not supported: %d\n", ret);
+ break;
+ }
+ }
+ _ODP_DBG("Mapped %" PRIu32 "/%d TX counters\n", i, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+
+ return 0;
+}
+
+static int dpdk_setup_eth_rx(const pktio_entry_t *pktio_entry,
+ const pkt_dpdk_t *pkt_dpdk,
+ const struct rte_eth_dev_info *dev_info)
+{
+ struct rte_eth_rxconf rxconf;
+ uint32_t i;
+ int ret;
+ uint16_t port_id = pkt_dpdk->port_id;
+
+ rxconf = dev_info->default_rxconf;
+
+ rxconf.rx_drop_en = pkt_dpdk->opt.rx_drop_en;
+
+ for (i = 0; i < pktio_entry->num_in_queue; i++) {
+ ret = rte_eth_rx_queue_setup(port_id, i,
+ pkt_dpdk->num_rx_desc[i],
rte_eth_dev_socket_id(port_id),
- NULL, pkt_dpdk->pkt_pool);
+ &rxconf, pkt_dpdk->pkt_pool);
if (ret < 0) {
- ODP_ERR("Queue setup failed: err=%d, port=%" PRIu8 "\n",
- ret, port_id);
+ _ODP_ERR("Queue setup failed: err=%d, port=%" PRIu8 "\n", ret, port_id);
+ return -1;
+ }
+ }
+
+ /* Set per queue statistics mappings. Not supported by all PMDs, so
+ * ignore the return value. */
+ for (i = 0; i < pktio_entry->num_in_queue && i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
+ ret = rte_eth_dev_set_rx_queue_stats_mapping(port_id, i, i);
+ if (ret) {
+ _ODP_DBG("Mapping per RX queue statistics not supported: %d\n", ret);
+ break;
+ }
+ }
+ _ODP_DBG("Mapped %" PRIu32 "/%d RX counters\n", i, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+
+ return 0;
+}
+
+static int dpdk_start(pktio_entry_t *pktio_entry)
+{
+ struct rte_eth_dev_info dev_info;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
+ uint16_t port_id = pkt_dpdk->port_id;
+ int ret;
+
+ rte_eth_dev_info_get(port_id, &dev_info);
+
+ /* Pcap driver reconfiguration may fail if number of rx/tx queues is set to zero */
+ if (!strncmp(dev_info.driver_name, PCAP_DRV_NAME, strlen(PCAP_DRV_NAME))) {
+ if (!pktio_entry->num_in_queue) {
+ pktio_entry->num_in_queue = 1;
+ pkt_dpdk->num_rx_desc[0] = dev_info.rx_desc_lim.nb_min;
+ }
+ if (!pktio_entry->num_out_queue) {
+ pktio_entry->num_out_queue = 1;
+ pkt_dpdk->num_tx_desc[0] = dev_info.tx_desc_lim.nb_min;
+ }
+ }
+
+ /* Setup device */
+ if (dpdk_setup_eth_dev(pktio_entry)) {
+ _ODP_ERR("Failed to configure device\n");
+ return -1;
+ }
+
+ /* Setup TX queues */
+ if (dpdk_setup_eth_tx(pktio_entry, pkt_dpdk, &dev_info))
+ return -1;
+
+ /* Setup RX queues */
+ if (dpdk_setup_eth_rx(pktio_entry, pkt_dpdk, &dev_info))
+ return -1;
+
+ /* Restore MTU value reset by dpdk_setup_eth_rx() */
+ if (pkt_dpdk->mtu_set && pktio_entry->capa.set_op.op.maxlen) {
+ ret = dpdk_maxlen_set(pktio_entry, pkt_dpdk->mtu, 0);
+ if (ret) {
+ _ODP_ERR("Restoring device MTU failed: err=%d, port=%" PRIu8 "\n",
+ ret, port_id);
return -1;
}
}
+
+ if (_ODP_DPDK_ZERO_COPY) {
+ /* Use simpler function when packet parsing and classifying are not required */
+ if (pktio_entry->parse_layer == ODP_PROTO_LAYER_NONE)
+ pkt_dpdk->mbuf_to_pkt_fn = mbuf_to_pkt_zero_minimal;
+ else
+ pkt_dpdk->mbuf_to_pkt_fn = mbuf_to_pkt_zero;
+
+ } else {
+ pkt_dpdk->mbuf_to_pkt_fn = mbuf_to_pkt;
+ }
+
/* Start device */
ret = rte_eth_dev_start(port_id);
if (ret < 0) {
- ODP_ERR("Device start failed: err=%d, port=%" PRIu8 "\n",
- ret, port_id);
+ _ODP_ERR("Device start failed: err=%d, port=%" PRIu8 "\n", ret, port_id);
return -1;
}
@@ -1073,7 +2146,7 @@ static int dpdk_start(pktio_entry_t *pktio_entry)
static int dpdk_stop(pktio_entry_t *pktio_entry)
{
- rte_eth_dev_stop(pktio_entry->s.pkt_dpdk.port_id);
+ rte_eth_dev_stop(pkt_priv(pktio_entry)->port_id);
return 0;
}
@@ -1081,7 +2154,7 @@ static int dpdk_stop(pktio_entry_t *pktio_entry)
static int dpdk_recv(pktio_entry_t *pktio_entry, int index,
odp_packet_t pkt_table[], int num)
{
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
pkt_cache_t *rx_cache = &pkt_dpdk->rx_cache[index];
odp_time_t ts_val;
odp_time_t *ts = NULL;
@@ -1090,63 +2163,56 @@ static int dpdk_recv(pktio_entry_t *pktio_entry, int index,
int i;
unsigned cache_idx;
- if (odp_unlikely(pktio_entry->s.state != PKTIO_STATE_STARTED))
- return 0;
-
- if (!pkt_dpdk->lockless_rx)
+ if (!pkt_dpdk->flags.lockless_rx)
odp_ticketlock_lock(&pkt_dpdk->rx_lock[index]);
/**
- * ixgbe_pmd has a minimum supported RX burst size ('min_rx_burst'). If
- * 'num' < 'min_rx_burst', 'min_rx_burst' is used as rte_eth_rx_burst()
- * argument and the possibly received extra packets are cached for the
- * next dpdk_recv_queue() call to use.
+ * ixgbe and i40e drivers have a minimum supported RX burst size
+ * ('min_rx_burst'). If 'num' < 'min_rx_burst', 'min_rx_burst' is used
+ * as rte_eth_rx_burst() argument and the possibly received extra
+ * packets are cached for the next dpdk_recv_queue() call to use.
*
* Either use cached packets or receive new ones. Not both during the
* same call. */
- if (rx_cache->s.count > 0) {
- for (i = 0; i < num && rx_cache->s.count; i++) {
- rx_mbufs[i] = rx_cache->s.pkt[rx_cache->s.idx];
- rx_cache->s.idx++;
- rx_cache->s.count--;
+ if (rx_cache->count > 0) {
+ for (i = 0; i < num && rx_cache->count; i++) {
+ rx_mbufs[i] = rx_cache->pkt[rx_cache->idx];
+ rx_cache->idx++;
+ rx_cache->count--;
}
nb_rx = i;
} else if ((unsigned)num < pkt_dpdk->min_rx_burst) {
struct rte_mbuf *new_mbufs[pkt_dpdk->min_rx_burst];
- nb_rx = rte_eth_rx_burst(pktio_entry->s.pkt_dpdk.port_id, index,
+ nb_rx = rte_eth_rx_burst(pkt_priv(pktio_entry)->port_id, index,
new_mbufs, pkt_dpdk->min_rx_burst);
- rx_cache->s.idx = 0;
+ rx_cache->idx = 0;
for (i = 0; i < nb_rx; i++) {
if (i < num) {
rx_mbufs[i] = new_mbufs[i];
} else {
- cache_idx = rx_cache->s.count;
- rx_cache->s.pkt[cache_idx] = new_mbufs[i];
- rx_cache->s.count++;
+ cache_idx = rx_cache->count;
+ rx_cache->pkt[cache_idx] = new_mbufs[i];
+ rx_cache->count++;
}
}
nb_rx = RTE_MIN(num, nb_rx);
} else {
- nb_rx = rte_eth_rx_burst(pktio_entry->s.pkt_dpdk.port_id, index,
+ nb_rx = rte_eth_rx_burst(pkt_priv(pktio_entry)->port_id, index,
rx_mbufs, num);
}
- if (!pkt_dpdk->lockless_rx)
+ if (!pkt_dpdk->flags.lockless_rx)
odp_ticketlock_unlock(&pkt_dpdk->rx_lock[index]);
if (nb_rx > 0) {
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp) {
+ if (pktio_entry->config.pktin.bit.ts_all ||
+ pktio_entry->config.pktin.bit.ts_ptp) {
ts_val = odp_time_global();
ts = &ts_val;
}
- if (ODP_DPDK_ZERO_COPY)
- nb_rx = mbuf_to_pkt_zero(pktio_entry, pkt_table,
- rx_mbufs, nb_rx, ts);
- else
- nb_rx = mbuf_to_pkt(pktio_entry, pkt_table, rx_mbufs,
- nb_rx, ts);
+
+ nb_rx = pkt_dpdk->mbuf_to_pkt_fn(pktio_entry, pkt_table, rx_mbufs, nb_rx, ts);
}
return nb_rx;
@@ -1156,61 +2222,79 @@ static int dpdk_send(pktio_entry_t *pktio_entry, int index,
const odp_packet_t pkt_table[], int num)
{
struct rte_mbuf *tx_mbufs[num];
- pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
+ pkt_dpdk_t *pkt_dpdk = pkt_priv(pktio_entry);
uint16_t copy_count = 0;
- int tx_pkts;
- int i;
+ uint16_t cpy_idx[num];
+ uint16_t tx_pkts;
int mbufs;
+ uint16_t tx_ts_idx = 0;
- if (odp_unlikely(pktio_entry->s.state != PKTIO_STATE_STARTED))
- return 0;
-
- if (ODP_DPDK_ZERO_COPY)
+ if (_ODP_DPDK_ZERO_COPY)
mbufs = pkt_to_mbuf_zero(pktio_entry, tx_mbufs, pkt_table, num,
- &copy_count);
+ &copy_count, cpy_idx, &tx_ts_idx);
else
- mbufs = pkt_to_mbuf(pktio_entry, tx_mbufs, pkt_table, num);
+ mbufs = pkt_to_mbuf(pktio_entry, tx_mbufs, pkt_table, num,
+ &tx_ts_idx);
- if (!pkt_dpdk->lockless_tx)
+ if (odp_unlikely(mbufs < 1))
+ return mbufs;
+
+ if (!pkt_dpdk->flags.lockless_tx)
odp_ticketlock_lock(&pkt_dpdk->tx_lock[index]);
tx_pkts = rte_eth_tx_burst(pkt_dpdk->port_id, index,
tx_mbufs, mbufs);
- if (!pkt_dpdk->lockless_tx)
+ if (!pkt_dpdk->flags.lockless_tx)
odp_ticketlock_unlock(&pkt_dpdk->tx_lock[index]);
- if (ODP_DPDK_ZERO_COPY) {
+ if (odp_unlikely(tx_ts_idx && tx_pkts >= tx_ts_idx))
+ _odp_pktio_tx_ts_set(pktio_entry);
+
+ if (_ODP_DPDK_ZERO_COPY) {
/* Free copied packets */
if (odp_unlikely(copy_count)) {
- uint16_t freed = 0;
-
- for (i = 0; i < mbufs && freed != copy_count; i++) {
- odp_packet_t pkt = pkt_table[i];
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
-
- if (pkt_hdr->buf_hdr.segcount > 1) {
- if (odp_likely(i < tx_pkts))
- odp_packet_free(pkt);
- else
- rte_pktmbuf_free(tx_mbufs[i]);
- freed++;
- }
+ uint16_t idx;
+
+ for (uint16_t i = 0; i < copy_count; i++) {
+ idx = cpy_idx[i];
+
+ if (odp_likely(idx < tx_pkts))
+ odp_packet_free(pkt_table[idx]);
+ else
+ rte_pktmbuf_free(tx_mbufs[idx]);
}
}
- if (odp_unlikely(tx_pkts == 0 && __odp_errno != 0))
- return -1;
} else {
+ int i;
+ int first = tx_pkts;
+
if (odp_unlikely(tx_pkts < mbufs)) {
for (i = tx_pkts; i < mbufs; i++)
rte_pktmbuf_free(tx_mbufs[i]);
}
- if (odp_unlikely(tx_pkts == 0)) {
- if (__odp_errno != 0)
- return -1;
- } else {
- odp_packet_free_multi(pkt_table, tx_pkts);
+ if (odp_unlikely(tx_pkts == 0))
+ return 0;
+
+ /* Find the first packet with (possible) don't free flag */
+ for (i = 0; i < tx_pkts; i++) {
+ if (odp_packet_free_ctrl(pkt_table[i]) == ODP_PACKET_FREE_CTRL_DONT_FREE) {
+ first = i;
+ break;
+ }
+ }
+
+ /* Free first N packets that don't have the flag */
+ if (odp_likely(first > 0))
+ odp_packet_free_multi(pkt_table, first);
+
+ /* Free rest of the packets (according to the flag) */
+ for (i = first; i < tx_pkts; i++) {
+ if (odp_packet_free_ctrl(pkt_table[i]) == ODP_PACKET_FREE_CTRL_DONT_FREE)
+ continue;
+
+ odp_packet_free(pkt_table[i]);
}
}
@@ -1219,16 +2303,24 @@ static int dpdk_send(pktio_entry_t *pktio_entry, int index,
static int dpdk_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
{
- rte_eth_macaddr_get(pktio_entry->s.pkt_dpdk.port_id,
- (struct ether_addr *)mac_addr);
+ rte_eth_macaddr_get(pkt_priv(pktio_entry)->port_id,
+ (struct rte_ether_addr *)mac_addr);
return ETH_ALEN;
}
+static int dpdk_mac_addr_set(pktio_entry_t *pktio_entry, const void *mac_addr)
+{
+ struct rte_ether_addr addr = *(const struct rte_ether_addr *)mac_addr;
+
+ return rte_eth_dev_default_mac_addr_set(pkt_priv(pktio_entry)->port_id,
+ &addr);
+}
+
static int dpdk_promisc_mode_set(pktio_entry_t *pktio_entry, odp_bool_t enable)
{
- uint8_t port_id = pktio_entry->s.pkt_dpdk.port_id;
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
- if (pktio_entry->s.pkt_dpdk.vdev_sysc_promisc)
+ if (pkt_priv(pktio_entry)->vdev_sysc_promisc)
return dpdk_vdev_promisc_mode_set(port_id, enable);
if (enable)
@@ -1241,9 +2333,9 @@ static int dpdk_promisc_mode_set(pktio_entry_t *pktio_entry, odp_bool_t enable)
static int dpdk_promisc_mode_get(pktio_entry_t *pktio_entry)
{
- uint8_t port_id = pktio_entry->s.pkt_dpdk.port_id;
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
- if (pktio_entry->s.pkt_dpdk.vdev_sysc_promisc)
+ if (pkt_priv(pktio_entry)->vdev_sysc_promisc)
return dpdk_vdev_promisc_mode_get(port_id);
else
return rte_eth_promiscuous_get(port_id);
@@ -1252,7 +2344,7 @@ static int dpdk_promisc_mode_get(pktio_entry_t *pktio_entry)
static int dpdk_capability(pktio_entry_t *pktio_entry,
odp_pktio_capability_t *capa)
{
- *capa = pktio_entry->s.pkt_dpdk.capa;
+ *capa = pktio_entry->capa;
return 0;
}
@@ -1262,9 +2354,64 @@ static int dpdk_link_status(pktio_entry_t *pktio_entry)
memset(&link, 0, sizeof(struct rte_eth_link));
- rte_eth_link_get_nowait(pktio_entry->s.pkt_dpdk.port_id, &link);
+ rte_eth_link_get_nowait(pkt_priv(pktio_entry)->port_id, &link);
+ if (link.link_status)
+ return ODP_PKTIO_LINK_STATUS_UP;
+ return ODP_PKTIO_LINK_STATUS_DOWN;
+}
+
+static int dpdk_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ struct rte_eth_link link;
+ struct rte_eth_fc_conf fc_conf;
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
+ int ret;
- return link.link_status;
+ memset(&fc_conf, 0, sizeof(struct rte_eth_fc_conf));
+ memset(&link, 0, sizeof(struct rte_eth_link));
+
+ ret = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+ if (ret && ret != -ENOTSUP) {
+ _ODP_ERR("rte_eth_dev_flow_ctrl_get() failed\n");
+ return -1;
+ }
+
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
+ if (fc_conf.mode == RTE_ETH_FC_RX_PAUSE) {
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_ON;
+ } else if (fc_conf.mode == RTE_ETH_FC_TX_PAUSE) {
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_ON;
+ } else if (fc_conf.mode == RTE_ETH_FC_FULL) {
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_ON;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_ON;
+ }
+
+ rte_eth_link_get_nowait(port_id, &link);
+ if (link.link_autoneg == RTE_ETH_LINK_AUTONEG)
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_ON;
+ else
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+
+ if (link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX)
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ else
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_HALF;
+
+ if (link.link_speed == RTE_ETH_SPEED_NUM_NONE)
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ else
+ info->speed = link.link_speed;
+
+ if (link.link_status == RTE_ETH_LINK_UP)
+ info->status = ODP_PKTIO_LINK_STATUS_UP;
+ else
+ info->status = ODP_PKTIO_LINK_STATUS_DOWN;
+
+ info->media = "unknown";
+
+ return 0;
}
static void stats_convert(const struct rte_eth_stats *rte_stats,
@@ -1273,9 +2420,11 @@ static void stats_convert(const struct rte_eth_stats *rte_stats,
memset(stats, 0, sizeof(odp_pktio_stats_t));
stats->in_octets = rte_stats->ibytes;
+ stats->in_packets = rte_stats->ipackets;
stats->in_discards = rte_stats->imissed;
stats->in_errors = rte_stats->ierrors;
stats->out_octets = rte_stats->obytes;
+ stats->out_packets = rte_stats->opackets;
stats->out_errors = rte_stats->oerrors;
}
@@ -1284,7 +2433,7 @@ static int dpdk_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats)
int ret;
struct rte_eth_stats rte_stats;
- ret = rte_eth_stats_get(pktio_entry->s.pkt_dpdk.port_id, &rte_stats);
+ ret = rte_eth_stats_get(pkt_priv(pktio_entry)->port_id, &rte_stats);
if (ret == 0) {
stats_convert(&rte_stats, stats);
@@ -1295,34 +2444,195 @@ static int dpdk_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats)
static int dpdk_stats_reset(pktio_entry_t *pktio_entry)
{
- rte_eth_stats_reset(pktio_entry->s.pkt_dpdk.port_id);
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
+
+ (void)rte_eth_stats_reset(port_id);
+ (void)rte_eth_xstats_reset(port_id);
return 0;
}
-const pktio_if_ops_t dpdk_pktio_ops = {
+static int dpdk_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[], int num)
+{
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
+ int num_stats, ret, i;
+
+ num_stats = rte_eth_xstats_get_names(port_id, NULL, 0);
+ if (num_stats < 0) {
+ _ODP_ERR("rte_eth_xstats_get_names() failed: %d\n", num_stats);
+ return num_stats;
+ } else if (info == NULL || num == 0 || num_stats == 0) {
+ return num_stats;
+ }
+
+ struct rte_eth_xstat_name xstats_names[num_stats];
+
+ ret = rte_eth_xstats_get_names(port_id, xstats_names, num_stats);
+ if (ret < 0 || ret > num_stats) {
+ _ODP_ERR("rte_eth_xstats_get_names() failed: %d\n", ret);
+ return -1;
+ }
+ num_stats = ret;
+
+ for (i = 0; i < num && i < num_stats; i++)
+ _odp_strcpy(info[i].name, xstats_names[i].name,
+ ODP_PKTIO_STATS_EXTRA_NAME_LEN);
+
+ return num_stats;
+}
+
+static int dpdk_extra_stats(pktio_entry_t *pktio_entry,
+ uint64_t stats[], int num)
+{
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
+ int num_stats, ret, i;
+
+ num_stats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_stats < 0) {
+ _ODP_ERR("rte_eth_xstats_get() failed: %d\n", num_stats);
+ return num_stats;
+ } else if (stats == NULL || num == 0 || num_stats == 0) {
+ return num_stats;
+ }
+
+ struct rte_eth_xstat xstats[num_stats];
+
+ ret = rte_eth_xstats_get(port_id, xstats, num_stats);
+ if (ret < 0 || ret > num_stats) {
+ _ODP_ERR("rte_eth_xstats_get() failed: %d\n", ret);
+ return -1;
+ }
+ num_stats = ret;
+
+ for (i = 0; i < num && i < num_stats; i++)
+ stats[i] = xstats[i].value;
+
+ return num_stats;
+}
+
+static int dpdk_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat)
+{
+ uint16_t port_id = pkt_priv(pktio_entry)->port_id;
+ uint64_t xstat_id = id;
+ int ret;
+
+ ret = rte_eth_xstats_get_by_id(port_id, &xstat_id, stat, 1);
+ if (ret != 1) {
+ _ODP_ERR("rte_eth_xstats_get_by_id() failed: %d\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dpdk_pktin_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktin_queue_stats_t *pktin_stats)
+{
+ struct rte_eth_stats rte_stats;
+ int ret;
+
+ if (odp_unlikely(index > RTE_ETHDEV_QUEUE_STAT_CNTRS - 1)) {
+ _ODP_ERR("DPDK supports max %d per queue counters\n", RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ return -1;
+ }
+
+ ret = rte_eth_stats_get(pkt_priv(pktio_entry)->port_id, &rte_stats);
+ if (odp_unlikely(ret)) {
+ _ODP_ERR("Failed to read DPDK pktio stats: %d\n", ret);
+ return -1;
+ }
+
+ memset(pktin_stats, 0, sizeof(odp_pktin_queue_stats_t));
+
+ pktin_stats->packets = rte_stats.q_ipackets[index];
+ pktin_stats->octets = rte_stats.q_ibytes[index];
+ pktin_stats->errors = rte_stats.q_errors[index];
+
+ return 0;
+}
+
+static int dpdk_pktout_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktout_queue_stats_t *pktout_stats)
+{
+ struct rte_eth_stats rte_stats;
+ int ret;
+
+ if (odp_unlikely(index > RTE_ETHDEV_QUEUE_STAT_CNTRS - 1)) {
+ _ODP_ERR("DPDK supports max %d per queue counters\n", RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ return -1;
+ }
+
+ ret = rte_eth_stats_get(pkt_priv(pktio_entry)->port_id, &rte_stats);
+ if (odp_unlikely(ret)) {
+ _ODP_ERR("Failed to read DPDK pktio stats: %d\n", ret);
+ return -1;
+ }
+
+ memset(pktout_stats, 0, sizeof(odp_pktout_queue_stats_t));
+
+ pktout_stats->packets = rte_stats.q_opackets[index];
+ pktout_stats->octets = rte_stats.q_obytes[index];
+
+ return 0;
+}
+
+const pktio_if_ops_t _odp_dpdk_pktio_ops = {
.name = "dpdk",
.init_global = dpdk_pktio_init_global,
.init_local = dpdk_pktio_init_local,
- .term = NULL,
+ .term = dpdk_pktio_term,
.open = dpdk_open,
.close = dpdk_close,
.start = dpdk_start,
.stop = dpdk_stop,
.stats = dpdk_stats,
.stats_reset = dpdk_stats_reset,
+ .pktin_queue_stats = dpdk_pktin_stats,
+ .pktout_queue_stats = dpdk_pktout_stats,
+ .extra_stat_info = dpdk_extra_stat_info,
+ .extra_stats = dpdk_extra_stats,
+ .extra_stat_counter = dpdk_extra_stat_counter,
.recv = dpdk_recv,
.send = dpdk_send,
.link_status = dpdk_link_status,
- .mtu_get = dpdk_mtu_get,
+ .link_info = dpdk_link_info,
+ .maxlen_get = dpdk_maxlen,
+ .maxlen_set = dpdk_maxlen_set,
.promisc_mode_set = dpdk_promisc_mode_set,
.promisc_mode_get = dpdk_promisc_mode_get,
.mac_get = dpdk_mac_addr_get,
+ .mac_set = dpdk_mac_addr_set,
.capability = dpdk_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL,
.input_queues_config = dpdk_input_queues_config,
.output_queues_config = dpdk_output_queues_config
};
-#endif /* ODP_PKTIO_DPDK */
+static odp_bool_t is_mem_src_active(void)
+{
+ return !disable_pktio && _ODP_DPDK_ZERO_COPY;
+}
+
+static void force_mem_src_disable(void)
+{
+ if (_ODP_DPDK_ZERO_COPY)
+ disable_pktio = 1;
+}
+
+const _odp_pool_mem_src_ops_t _odp_pool_dpdk_mem_src_ops = {
+ .name = "dpdk_zc",
+ .is_active = is_mem_src_active,
+ .force_disable = force_mem_src_disable,
+ .adjust_size = pool_obj_size,
+ .bind = pool_create,
+ .unbind = pool_destroy
+};
+
+#else
+/* Avoid warning about empty translation unit */
+typedef int _odp_dummy;
+#endif /* _ODP_PKTIO_DPDK */
diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
deleted file mode 100644
index d8f9e12cb..000000000
--- a/platform/linux-generic/pktio/ethtool.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <linux/sockios.h>
-#include <linux/if.h>
-#include <linux/ethtool.h>
-#include <errno.h>
-#include <net/if.h>
-
-#include <odp_api.h>
-#include <odp_packet_socket.h>
-#include <odp_debug_internal.h>
-
-static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
-{
- struct {
- struct ethtool_sset_info hdr;
- uint32_t buf[1];
- } sset_info;
- struct ethtool_drvinfo drvinfo;
- uint32_t len;
- struct ethtool_gstrings *strings;
- ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
-
- sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
- sset_info.hdr.reserved = 0;
- sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
- ifr->ifr_data = &sset_info;
- if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
- len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
- } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
- /* Fallback for old kernel versions */
- drvinfo.cmd = ETHTOOL_GDRVINFO;
- ifr->ifr_data = &drvinfo;
- if (ioctl(fd, SIOCETHTOOL, ifr)) {
- __odp_errno = errno;
- ODP_ERR("Cannot get stats information\n");
- return NULL;
- }
- len = *(uint32_t *)(void *)((char *)&drvinfo + drvinfo_offset);
- } else {
- __odp_errno = errno;
- return NULL;
- }
-
- if (!len) {
- ODP_ERR("len is zero");
- return NULL;
- }
-
- strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
- if (!strings) {
- ODP_ERR("alloc failed\n");
- return NULL;
- }
-
- strings->cmd = ETHTOOL_GSTRINGS;
- strings->string_set = ETH_SS_STATS;
- strings->len = len;
- ifr->ifr_data = strings;
- if (ioctl(fd, SIOCETHTOOL, ifr)) {
- __odp_errno = errno;
- ODP_ERR("Cannot get stats information\n");
- free(strings);
- return NULL;
- }
-
- return strings;
-}
-
-static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
-{
- struct ethtool_gstrings *strings;
- struct ethtool_stats *estats;
- unsigned int n_stats, i;
- int err;
- int cnts;
-
- strings = get_stringset(fd, ifr);
- if (!strings)
- return -1;
-
- n_stats = strings->len;
- if (n_stats < 1) {
- ODP_ERR("no stats available\n");
- free(strings);
- return -1;
- }
-
- estats = calloc(1, n_stats * sizeof(uint64_t) +
- sizeof(struct ethtool_stats));
- if (!estats) {
- free(strings);
- return -1;
- }
-
- estats->cmd = ETHTOOL_GSTATS;
- estats->n_stats = n_stats;
- ifr->ifr_data = estats;
- err = ioctl(fd, SIOCETHTOOL, ifr);
- if (err < 0) {
- __odp_errno = errno;
- free(strings);
- free(estats);
- return -1;
- }
-
- cnts = 0;
- for (i = 0; i < n_stats; i++) {
- char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
- uint64_t val = estats->data[i];
-
- if (!strcmp(cnt, "rx_octets")) {
- stats->in_octets = val;
- cnts++;
- } else if (!strcmp(cnt, "rx_ucast_packets")) {
- stats->in_ucast_pkts = val;
- cnts++;
- } else if (!strcmp(cnt, "rx_discards")) {
- stats->in_discards = val;
- cnts++;
- } else if (!strcmp(cnt, "rx_errors")) {
- stats->in_errors = val;
- cnts++;
- } else if (!strcmp(cnt, "tx_octets")) {
- stats->out_octets = val;
- cnts++;
- } else if (!strcmp(cnt, "tx_ucast_packets")) {
- stats->out_ucast_pkts = val;
- cnts++;
- } else if (!strcmp(cnt, "tx_discards")) {
- stats->out_discards = val;
- cnts++;
- } else if (!strcmp(cnt, "tx_errors")) {
- stats->out_errors = val;
- cnts++;
- }
- }
-
- free(strings);
- free(estats);
-
- /* Ethtool strings came from kernel driver. Name of that
- * strings is not universal. Current function needs to be updated
- * if your driver has different names for counters */
- if (cnts < 8)
- return -1;
-
- return 0;
-}
-
-int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats)
-{
- struct ifreq ifr;
-
- memset(&ifr, 0, sizeof(ifr));
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
-
- return ethtool_stats(fd, &ifr, stats);
-}
diff --git a/platform/linux-generic/pktio/ethtool_rss.c b/platform/linux-generic/pktio/ethtool_rss.c
new file mode 100644
index 000000000..7cfce7812
--- /dev/null
+++ b/platform/linux-generic/pktio/ethtool_rss.c
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <odp_ethtool_rss.h>
+#include <odp_debug_internal.h>
+
+/**
+ * Get enabled hash options of a packet socket
+ *
+ * @param fd Socket file descriptor
+ * @param name Interface name
+ * @param flow_type Packet flow type
+ * @param options[out] Enabled hash options
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+static inline int get_rss_hash_options(int fd, const char *name,
+ uint32_t flow_type, uint64_t *options)
+{
+ struct ifreq ifr;
+ struct ethtool_rxnfc rsscmd;
+
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&rsscmd, 0, sizeof(rsscmd));
+ *options = 0;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+
+ rsscmd.cmd = ETHTOOL_GRXFH;
+ rsscmd.flow_type = flow_type;
+
+ ifr.ifr_data = (caddr_t)&rsscmd;
+
+ if (ioctl(fd, SIOCETHTOOL, &ifr) < 0)
+ return -1;
+
+ *options = rsscmd.data;
+ return 0;
+}
+
+int _odp_rss_conf_get_fd(int fd, const char *name,
+ odp_pktin_hash_proto_t *hash_proto)
+{
+ uint64_t options;
+ int rss_enabled = 0;
+
+ memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t));
+
+ get_rss_hash_options(fd, name, IPV4_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) {
+ hash_proto->proto.ipv4 = 1;
+ rss_enabled++;
+ }
+ get_rss_hash_options(fd, name, TCP_V4_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
+ (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
+ hash_proto->proto.ipv4_tcp = 1;
+ rss_enabled++;
+ }
+ get_rss_hash_options(fd, name, UDP_V4_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
+ (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
+ hash_proto->proto.ipv4_udp = 1;
+ rss_enabled++;
+ }
+ get_rss_hash_options(fd, name, IPV6_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) {
+ hash_proto->proto.ipv6 = 1;
+ rss_enabled++;
+ }
+ get_rss_hash_options(fd, name, TCP_V6_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
+ (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
+ hash_proto->proto.ipv6_tcp = 1;
+ rss_enabled++;
+ }
+ get_rss_hash_options(fd, name, UDP_V6_FLOW, &options);
+ if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
+ (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
+ hash_proto->proto.ipv6_udp = 1;
+ rss_enabled++;
+ }
+ return rss_enabled;
+}
+
+/**
+ * Set hash options of a packet socket
+ *
+ * @param fd Socket file descriptor
+ * @param name Interface name
+ * @param flow_type Packet flow type
+ * @param options Hash options
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+static inline int set_rss_hash(int fd, const char *name,
+ uint32_t flow_type, uint64_t options)
+{
+ struct ifreq ifr;
+ struct ethtool_rxnfc rsscmd;
+
+ memset(&rsscmd, 0, sizeof(rsscmd));
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+
+ rsscmd.cmd = ETHTOOL_SRXFH;
+ rsscmd.flow_type = flow_type;
+ rsscmd.data = options;
+
+ ifr.ifr_data = (caddr_t)&rsscmd;
+
+ if (ioctl(fd, SIOCETHTOOL, &ifr) < 0)
+ return -1;
+
+ return 0;
+}
+
+int _odp_rss_conf_set_fd(int fd, const char *name,
+ const odp_pktin_hash_proto_t *hash_proto)
+{
+ uint64_t options;
+ odp_pktin_hash_proto_t cur_hash;
+
+ /* Compare to currently set hash protocols */
+ _odp_rss_conf_get_fd(fd, name, &cur_hash);
+
+ if (hash_proto->proto.ipv4_udp && !cur_hash.proto.ipv4_udp) {
+ options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ if (set_rss_hash(fd, name, UDP_V4_FLOW, options))
+ return -1;
+ }
+ if (hash_proto->proto.ipv4_tcp && !cur_hash.proto.ipv4_tcp) {
+ options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ if (set_rss_hash(fd, name, TCP_V4_FLOW, options))
+ return -1;
+ }
+ if (hash_proto->proto.ipv6_udp && !cur_hash.proto.ipv6_udp) {
+ options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ if (set_rss_hash(fd, name, UDP_V6_FLOW, options))
+ return -1;
+ }
+ if (hash_proto->proto.ipv6_tcp && !cur_hash.proto.ipv6_tcp) {
+ options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ if (set_rss_hash(fd, name, TCP_V6_FLOW, options))
+ return -1;
+ }
+ if (hash_proto->proto.ipv4 && !cur_hash.proto.ipv4) {
+ options = RXH_IP_SRC | RXH_IP_DST;
+ if (set_rss_hash(fd, name, IPV4_FLOW, options))
+ return -1;
+ }
+ if (hash_proto->proto.ipv6 && !cur_hash.proto.ipv6) {
+ options = RXH_IP_SRC | RXH_IP_DST;
+ if (set_rss_hash(fd, name, IPV6_FLOW, options))
+ return -1;
+ }
+ return 0;
+}
+
+int _odp_rss_conf_get_supported_fd(int fd, const char *name,
+ odp_pktin_hash_proto_t *hash_proto)
+{
+ uint64_t options;
+ int rss_supported = 0;
+
+ memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t));
+
+ if (!get_rss_hash_options(fd, name, IPV4_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, IPV4_FLOW, options)) {
+ hash_proto->proto.ipv4 = 1;
+ rss_supported++;
+ }
+ }
+ if (!get_rss_hash_options(fd, name, TCP_V4_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, TCP_V4_FLOW, options)) {
+ hash_proto->proto.ipv4_tcp = 1;
+ rss_supported++;
+ }
+ }
+ if (!get_rss_hash_options(fd, name, UDP_V4_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, UDP_V4_FLOW, options)) {
+ hash_proto->proto.ipv4_udp = 1;
+ rss_supported++;
+ }
+ }
+ if (!get_rss_hash_options(fd, name, IPV6_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, IPV6_FLOW, options)) {
+ hash_proto->proto.ipv6 = 1;
+ rss_supported++;
+ }
+ }
+ if (!get_rss_hash_options(fd, name, TCP_V6_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, TCP_V6_FLOW, options)) {
+ hash_proto->proto.ipv6_tcp = 1;
+ rss_supported++;
+ }
+ }
+ if (!get_rss_hash_options(fd, name, UDP_V6_FLOW, &options)) {
+ if (!set_rss_hash(fd, name, UDP_V6_FLOW, options)) {
+ hash_proto->proto.ipv6_udp = 1;
+ rss_supported++;
+ }
+ }
+ return rss_supported;
+}
+
+void _odp_rss_conf_print(const odp_pktin_hash_proto_t *hash_proto)
+{ int max_len = 512;
+ char str[max_len];
+ int len = 0;
+ int n = max_len - 1;
+
+ len += snprintf(&str[len], n - len, " rss conf\n");
+
+ if (hash_proto->proto.ipv4)
+ len += snprintf(&str[len], n - len,
+ " IPV4\n");
+ if (hash_proto->proto.ipv4_tcp)
+ len += snprintf(&str[len], n - len,
+ " IPV4 TCP\n");
+ if (hash_proto->proto.ipv4_udp)
+ len += snprintf(&str[len], n - len,
+ " IPV4 UDP\n");
+ if (hash_proto->proto.ipv6)
+ len += snprintf(&str[len], n - len,
+ " IPV6\n");
+ if (hash_proto->proto.ipv6_tcp)
+ len += snprintf(&str[len], n - len,
+ " IPV6 TCP\n");
+ if (hash_proto->proto.ipv6_udp)
+ len += snprintf(&str[len], n - len,
+ " IPV6 UDP\n");
+ str[len] = '\0';
+
+ _ODP_PRINT("%s\n", str);
+}
+
diff --git a/platform/linux-generic/pktio/io_ops.c b/platform/linux-generic/pktio/io_ops.c
index fbf30ca7a..ff97a25be 100644
--- a/platform/linux-generic/pktio/io_ops.c
+++ b/platform/linux-generic/pktio/io_ops.c
@@ -1,31 +1,29 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
*/
+#include <odp/autoheader_internal.h>
#include <odp_packet_io_internal.h>
/* Ops for all implementation of pktio.
* Order matters. The first implementation to setup successfully
* will be picked.
* Array must be NULL terminated */
-const pktio_if_ops_t * const pktio_if_ops[] = {
- &loopback_pktio_ops,
-#ifdef ODP_PKTIO_DPDK
- &dpdk_pktio_ops,
+const pktio_if_ops_t * const _odp_pktio_if_ops[] = {
+ &_odp_loopback_pktio_ops,
+#ifdef _ODP_PKTIO_DPDK
+ &_odp_dpdk_pktio_ops,
#endif
-#ifdef ODP_NETMAP
- &netmap_pktio_ops,
+#ifdef _ODP_PKTIO_XDP
+ &_odp_sock_xdp_pktio_ops,
#endif
-#ifdef HAVE_PCAP
- &pcap_pktio_ops,
+#ifdef _ODP_PKTIO_PCAP
+ &_odp_pcap_pktio_ops,
#endif
-#ifdef _ODP_PKTIO_IPC
- &ipc_pktio_ops,
-#endif
- &tap_pktio_ops,
- &sock_mmap_pktio_ops,
- &sock_mmsg_pktio_ops,
+ &_odp_ipc_pktio_ops,
+ &_odp_tap_pktio_ops,
+ &_odp_null_pktio_ops,
+ &_odp_sock_mmap_pktio_ops,
+ &_odp_sock_mmsg_pktio_ops,
NULL
};
diff --git a/platform/linux-generic/pktio/ipc.c b/platform/linux-generic/pktio/ipc.c
index 8c7db84e6..2e6110dc2 100644
--- a/platform/linux-generic/pktio/ipc.c
+++ b/platform/linux-generic/pktio/ipc.c
@@ -1,49 +1,202 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
*/
-#include <odp_packet_io_ipc_internal.h>
+
+#include <odp/api/hints.h>
+#include <odp/api/system_info.h>
+
#include <odp_debug_internal.h>
#include <odp_packet_io_internal.h>
-#include <odp/api/system_info.h>
+#include <odp_pool_internal.h>
+#include <odp_macros_internal.h>
#include <odp_shm_internal.h>
-#include <_ishm_internal.h>
+#include <odp_ring_ptr_internal.h>
+#include <odp_global_data.h>
+#include <fcntl.h>
+#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <unistd.h>
+
+/* Debug level for IPC */
+#define IPC_DBG 3
-#define IPC_ODP_DEBUG_PRINT 0
+/* Burst size for IPC free operations */
+#define IPC_BURST_SIZE 32
-#define IPC_ODP_DBG(fmt, ...) \
- do { \
- if (IPC_ODP_DEBUG_PRINT == 1) \
- ODP_DBG(fmt, ##__VA_ARGS__);\
- } while (0)
+/* that struct is exported to shared memory, so that processes can find
+ * each other.
+ */
+struct pktio_info {
+ struct {
+ /* Pool base address */
+ void *base_addr;
+ /* number of buffer*/
+ char pool_name[ODP_POOL_NAME_LEN];
+ /* 1 if master finished creation of all shared objects */
+ int init_done;
+ /* IPC ring size */
+ uint32_t ring_size;
+ /* IPC ring mask */
+ uint32_t ring_mask;
+ } master;
+ struct {
+ /* Pool base address */
+ void *base_addr;
+ char pool_name[ODP_POOL_NAME_LEN];
+ /* pid of the slave process written to shm and
+ * used by master to look up memory created by
+ * slave
+ */
+ int pid;
+ int init_done;
+ } slave;
+} ODP_PACKED;
+
+typedef struct {
+ /* TX */
+ struct {
+ /* ODP ring for IPC msg packets indexes transmitted to shared
+ * memory */
+ ring_ptr_t *send;
+ /* ODP ring for IPC msg packets indexes already processed by
+ * remote process */
+ ring_ptr_t *free;
+ } tx;
+ /* RX */
+ struct {
+ /* ODP ring for IPC msg packets indexes received from shared
+ * memory (from remote process) */
+ ring_ptr_t *recv;
+ /* odp ring for ipc msg packets indexes already processed by
+ * current process */
+ ring_ptr_t *free;
+ /* local cache to keep packet order right */
+ ring_ptr_t *cache;
+ } rx; /* slave */
+ /* Remote pool mdata base addr */
+ void *pool_mdata_base;
+ /* Remote pool base address for offset calculation */
+ void *remote_base_addr;
+ odp_pool_t pool; /**< Pool of main process */
+ enum {
+ PKTIO_TYPE_IPC_MASTER = 0, /**< Master is the process which
+ creates shm */
+ PKTIO_TYPE_IPC_SLAVE /**< Slave is the process which
+ connects to shm */
+ } type; /**< define if it's master or slave process */
+ odp_atomic_u32_t ready; /**< 1 - pktio is ready and can recv/send
+ packet, 0 - not yet ready */
+ /* Local copy of IPC ring size */
+ uint32_t ring_size;
+ /* Local copy IPC ring mask */
+ uint32_t ring_mask;
+ struct pktio_info *pinfo;
+ odp_shm_t pinfo_shm;
+ odp_shm_t remote_pool_shm; /**< shm of remote pool get with
+ _ipc_map_remote_pool() */
+} pkt_ipc_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_ipc_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static inline pkt_ipc_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_ipc_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
/* MAC address for the "ipc" interface */
-static const char pktio_ipc_mac[] = {0x12, 0x12, 0x12, 0x12, 0x12, 0x12};
+static const uint8_t pktio_ipc_mac[] = {0x12, 0x12, 0x12, 0x12, 0x12, 0x12};
static odp_shm_t _ipc_map_remote_pool(const char *name, int pid);
+/* create the ring */
+static ring_ptr_t *_ring_create(const char *name, uint32_t count,
+ uint32_t shm_flags)
+{
+ ring_ptr_t *r;
+ size_t ring_size;
+ odp_shm_t shm;
+
+ if (odp_global_ro.shm_single_va)
+ shm_flags |= ODP_SHM_SINGLE_VA;
+
+ /* count must be a power of 2 */
+ if (!_ODP_CHECK_IS_POWER2(count)) {
+ _ODP_ERR("Requested size is invalid, must be a power of 2\n");
+ return NULL;
+ }
+
+ ring_size = sizeof(ring_ptr_t) + count * sizeof(void *);
+
+ /* reserve a memory zone for this ring.*/
+ shm = odp_shm_reserve(name, ring_size, ODP_CACHE_LINE_SIZE, shm_flags);
+
+ r = odp_shm_addr(shm);
+ if (r != NULL) {
+ /* init the ring structure */
+ ring_ptr_init(r);
+
+ } else {
+ _ODP_ERR("Cannot reserve memory\n");
+ }
+
+ return r;
+}
+
+static int _ring_destroy(const char *name)
+{
+ odp_shm_t shm = odp_shm_lookup(name);
+
+ if (shm != ODP_SHM_INVALID)
+ return odp_shm_free(shm);
+
+ return 0;
+}
+
+/**
+ * Return the number of entries in a ring.
+ */
+static uint32_t _ring_count(ring_ptr_t *r, uint32_t mask)
+{
+ uint32_t prod_tail = odp_atomic_load_u32(&r->r.w_tail);
+ uint32_t cons_tail = odp_atomic_load_u32(&r->r.r_tail);
+
+ return (prod_tail - cons_tail) & mask;
+}
+
+/**
+ * Return the number of free entries in a ring.
+ */
+static uint32_t _ring_free_count(ring_ptr_t *r, uint32_t mask)
+{
+ uint32_t prod_tail = odp_atomic_load_u32(&r->r.w_tail);
+ uint32_t cons_tail = odp_atomic_load_u32(&r->r.r_tail);
+
+ return (cons_tail - prod_tail - 1) & mask;
+}
+
static const char *_ipc_odp_buffer_pool_shm_name(odp_pool_t pool_hdl)
{
pool_t *pool;
odp_shm_t shm;
odp_shm_info_t info;
- pool = pool_entry_from_hdl(pool_hdl);
+ pool = _odp_pool_entry(pool_hdl);
shm = pool->shm;
- odp_shm_info(shm, &info);
+ if (odp_shm_info(shm, &info))
+ return "name_unknown";
return info.name;
}
static int _ipc_master_start(pktio_entry_t *pktio_entry)
{
- struct pktio_info *pinfo = pktio_entry->s.ipc.pinfo;
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ struct pktio_info *pinfo = pktio_ipc->pinfo;
odp_shm_t shm;
if (pinfo->slave.init_done == 0)
@@ -52,18 +205,17 @@ static int _ipc_master_start(pktio_entry_t *pktio_entry)
shm = _ipc_map_remote_pool(pinfo->slave.pool_name,
pinfo->slave.pid);
if (shm == ODP_SHM_INVALID) {
- ODP_DBG("no pool file %s for pid %d\n",
- pinfo->slave.pool_name, pinfo->slave.pid);
+ _ODP_DBG("no pool file %s for pid %d\n", pinfo->slave.pool_name, pinfo->slave.pid);
return -1;
}
- pktio_entry->s.ipc.remote_pool_shm = shm;
- pktio_entry->s.ipc.pool_base = odp_shm_addr(shm);
- pktio_entry->s.ipc.pool_mdata_base = (char *)odp_shm_addr(shm);
+ pktio_ipc->remote_pool_shm = shm;
+ pktio_ipc->remote_base_addr = pinfo->slave.base_addr;
+ pktio_ipc->pool_mdata_base = (char *)odp_shm_addr(shm);
- odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 1);
+ odp_atomic_store_u32(&pktio_ipc->ready, 1);
- IPC_ODP_DBG("%s started.\n", pktio_entry->s.name);
+ ODP_DBG_LVL(IPC_DBG, "%s started.\n", pktio_entry->name);
return 0;
}
@@ -71,16 +223,38 @@ static int _ipc_init_master(pktio_entry_t *pktio_entry,
const char *dev,
odp_pool_t pool_hdl)
{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
char ipc_shm_name[ODP_POOL_NAME_LEN + sizeof("_m_prod")];
- pool_t *pool;
struct pktio_info *pinfo;
const char *pool_name;
+ pool_t *pool = _odp_pool_entry(pool_hdl);
+ uint32_t ring_size;
+ uint32_t ring_mask;
- pool = pool_entry_from_hdl(pool_hdl);
- (void)pool;
+ if ((uint64_t)_ODP_ROUNDUP_POWER2_U32(pool->num + 1) > UINT32_MAX) {
+ _ODP_ERR("Too large packet pool\n");
+ return -1;
+ }
+
+ /* Ring must be able to store all packets in the pool */
+ ring_size = _ODP_ROUNDUP_POWER2_U32(pool->num + 1);
+
+ /* Ring size has to larger than burst size */
+ if (ring_size <= IPC_BURST_SIZE)
+ ring_size = _ODP_ROUNDUP_POWER2_U32(IPC_BURST_SIZE + 1);
+ ring_mask = ring_size - 1;
+
+ pktio_ipc->ring_size = ring_size;
+ pktio_ipc->ring_mask = ring_mask;
if (strlen(dev) > (ODP_POOL_NAME_LEN - sizeof("_m_prod"))) {
- ODP_ERR("too big ipc name\n");
+ _ODP_ERR("too big ipc name\n");
+ return -1;
+ }
+
+ pktio_ipc->rx.cache = _ring_create("ipc_rx_cache", ring_size, 0);
+ if (!pktio_ipc->rx.cache) {
+ _ODP_ERR("pid %d unable to create ipc rx cache\n", getpid());
return -1;
}
@@ -88,77 +262,75 @@ static int _ipc_init_master(pktio_entry_t *pktio_entry,
* to be processed packets ring.
*/
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_prod", dev);
- pktio_entry->s.ipc.tx.send = _ring_create(ipc_shm_name,
- PKTIO_IPC_ENTRIES,
- _RING_SHM_PROC | _RING_NO_LIST);
- if (!pktio_entry->s.ipc.tx.send) {
- ODP_ERR("pid %d unable to create ipc ring %s name\n",
- getpid(), ipc_shm_name);
+ pktio_ipc->tx.send = _ring_create(ipc_shm_name, ring_size,
+ ODP_SHM_PROC | ODP_SHM_EXPORT);
+ if (!pktio_ipc->tx.send) {
+ _ODP_ERR("pid %d unable to create ipc ring %s name\n", getpid(), ipc_shm_name);
return -1;
}
- ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.send),
- _ring_free_count(pktio_entry->s.ipc.tx.send));
+ _ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->tx.send, ring_mask),
+ _ring_free_count(pktio_ipc->tx.send, ring_mask));
/* generate name in shm like ipc_pktio_p for
* already processed packets
*/
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_cons", dev);
- pktio_entry->s.ipc.tx.free = _ring_create(ipc_shm_name,
- PKTIO_IPC_ENTRIES,
- _RING_SHM_PROC | _RING_NO_LIST);
- if (!pktio_entry->s.ipc.tx.free) {
- ODP_ERR("pid %d unable to create ipc ring %s name\n",
- getpid(), ipc_shm_name);
+ pktio_ipc->tx.free = _ring_create(ipc_shm_name, ring_size,
+ ODP_SHM_PROC | ODP_SHM_EXPORT);
+ if (!pktio_ipc->tx.free) {
+ _ODP_ERR("pid %d unable to create ipc ring %s name\n", getpid(), ipc_shm_name);
goto free_m_prod;
}
- ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.free),
- _ring_free_count(pktio_entry->s.ipc.tx.free));
+ _ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->tx.free, ring_mask),
+ _ring_free_count(pktio_ipc->tx.free, ring_mask));
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_prod", dev);
- pktio_entry->s.ipc.rx.recv = _ring_create(ipc_shm_name,
- PKTIO_IPC_ENTRIES,
- _RING_SHM_PROC | _RING_NO_LIST);
- if (!pktio_entry->s.ipc.rx.recv) {
- ODP_ERR("pid %d unable to create ipc ring %s name\n",
- getpid(), ipc_shm_name);
+ pktio_ipc->rx.recv = _ring_create(ipc_shm_name, ring_size,
+ ODP_SHM_PROC | ODP_SHM_EXPORT);
+ if (!pktio_ipc->rx.recv) {
+ _ODP_ERR("pid %d unable to create ipc ring %s name\n", getpid(), ipc_shm_name);
goto free_m_cons;
}
- ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.recv),
- _ring_free_count(pktio_entry->s.ipc.rx.recv));
+ _ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->rx.recv, ring_mask),
+ _ring_free_count(pktio_ipc->rx.recv, ring_mask));
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", dev);
- pktio_entry->s.ipc.rx.free = _ring_create(ipc_shm_name,
- PKTIO_IPC_ENTRIES,
- _RING_SHM_PROC | _RING_NO_LIST);
- if (!pktio_entry->s.ipc.rx.free) {
- ODP_ERR("pid %d unable to create ipc ring %s name\n",
- getpid(), ipc_shm_name);
+ pktio_ipc->rx.free = _ring_create(ipc_shm_name, ring_size,
+ ODP_SHM_PROC | ODP_SHM_EXPORT);
+ if (!pktio_ipc->rx.free) {
+ _ODP_ERR("pid %d unable to create ipc ring %s name\n", getpid(), ipc_shm_name);
goto free_s_prod;
}
- ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.free),
- _ring_free_count(pktio_entry->s.ipc.rx.free));
+ _ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->rx.free, ring_mask),
+ _ring_free_count(pktio_ipc->rx.free, ring_mask));
/* Set up pool name for remote info */
- pinfo = pktio_entry->s.ipc.pinfo;
+ pinfo = pktio_ipc->pinfo;
pool_name = _ipc_odp_buffer_pool_shm_name(pool_hdl);
- if (strlen(pool_name) > ODP_POOL_NAME_LEN) {
- ODP_ERR("pid %d ipc pool name %s is too big %d\n",
- getpid(), pool_name, strlen(pool_name));
+ if (strlen(pool_name) >= ODP_POOL_NAME_LEN) {
+ _ODP_ERR("pid %d ipc pool name %s is too big %zu\n",
+ getpid(), pool_name, strlen(pool_name));
goto free_s_prod;
}
- memcpy(pinfo->master.pool_name, pool_name, strlen(pool_name));
+ strcpy(pinfo->master.pool_name, pool_name);
+
+ /* Export ring info for the slave process to use */
+ pinfo->master.ring_size = ring_size;
+ pinfo->master.ring_mask = ring_mask;
+ pinfo->master.base_addr = odp_shm_addr(pool->shm);
+
pinfo->slave.base_addr = 0;
pinfo->slave.pid = 0;
pinfo->slave.init_done = 0;
- pktio_entry->s.ipc.pool = pool_hdl;
+ pktio_ipc->pool = pool_hdl;
- ODP_DBG("Pre init... DONE.\n");
+ _ODP_DBG("Pre init... DONE.\n");
pinfo->master.init_done = 1;
_ipc_master_start(pktio_entry);
@@ -180,13 +352,12 @@ free_m_prod:
static void _ipc_export_pool(struct pktio_info *pinfo,
odp_pool_t pool_hdl)
{
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
snprintf(pinfo->slave.pool_name, ODP_POOL_NAME_LEN, "%s",
_ipc_odp_buffer_pool_shm_name(pool_hdl));
- pinfo->slave.pid = odp_global_data.main_pid;
- pinfo->slave.block_size = pool->block_size;
- pinfo->slave.base_addr = pool->base_addr;
+ pinfo->slave.pid = odp_global_ro.main_pid;
+ pinfo->slave.base_addr = odp_shm_addr(pool->shm);
}
static odp_shm_t _ipc_map_remote_pool(const char *name, int pid)
@@ -197,11 +368,11 @@ static odp_shm_t _ipc_map_remote_pool(const char *name, int pid)
snprintf(rname, ODP_SHM_NAME_LEN, "remote-%s", name);
shm = odp_shm_import(name, pid, rname);
if (shm == ODP_SHM_INVALID) {
- ODP_ERR("unable map %s\n", name);
+ _ODP_ERR("unable map %s\n", name);
return ODP_SHM_INVALID;
}
- IPC_ODP_DBG("Mapped remote pool %s to local %s\n", name, rname);
+ ODP_DBG_LVL(IPC_DBG, "Mapped remote pool %s to local %s\n", name, rname);
return shm;
}
@@ -211,99 +382,117 @@ static void *_ipc_shm_map(char *name, int pid)
shm = odp_shm_import(name, pid, name);
if (ODP_SHM_INVALID == shm) {
- ODP_ERR("unable to map: %s\n", name);
+ _ODP_ERR("unable to map: %s\n", name);
return NULL;
}
return odp_shm_addr(shm);
}
-static int _ipc_init_slave(const char *dev,
- pktio_entry_t *pktio_entry,
- odp_pool_t pool)
+static int _ipc_init_slave(const char *dev, pktio_entry_t *pktio_entry,
+ odp_pool_t pool_hdl)
{
- if (strlen(dev) > (ODP_POOL_NAME_LEN - sizeof("_slave_r")))
- ODP_ABORT("too big ipc name\n");
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
+ uint32_t ring_size = pktio_ipc->pinfo->master.ring_size;
+
+ if (strlen(dev) > (ODP_POOL_NAME_LEN - sizeof("_slave_r"))) {
+ _ODP_ERR("Too big ipc name\n");
+ return -1;
+ }
+
+ /* Check that IPC rings are able to store all packets */
+ if (pool->num >= ring_size) {
+ _ODP_ERR("Slave process packet pool too large. Master process "
+ "packet pool has to be larger than slave pool.\n");
+ return -1;
+ }
+
+ pktio_ipc->rx.cache = _ring_create("ipc_rx_cache", ring_size, 0);
+ if (!pktio_ipc->rx.cache) {
+ _ODP_ERR("Pid %d unable to create ipc rx cache\n", getpid());
+ return -1;
+ }
+ pktio_ipc->ring_size = ring_size;
+ pktio_ipc->ring_mask = pktio_ipc->pinfo->master.ring_mask;
+ pktio_ipc->pool = pool_hdl;
- pktio_entry->s.ipc.pool = pool;
return 0;
}
static int _ipc_slave_start(pktio_entry_t *pktio_entry)
{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
char ipc_shm_name[ODP_POOL_NAME_LEN + sizeof("_slave_r")];
struct pktio_info *pinfo;
odp_shm_t shm;
char tail[ODP_POOL_NAME_LEN];
char dev[ODP_POOL_NAME_LEN];
int pid;
+ uint32_t ring_mask = pktio_ipc->ring_mask;
- if (sscanf(pktio_entry->s.name, "ipc:%d:%s", &pid, tail) != 2) {
- ODP_ERR("wrong pktio name\n");
+ if (sscanf(pktio_entry->name, "ipc:%d:%s", &pid, tail) != 2) {
+ _ODP_ERR("wrong pktio name\n");
return -1;
}
sprintf(dev, "ipc:%s", tail);
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_prod", dev);
- pktio_entry->s.ipc.rx.recv = _ipc_shm_map(ipc_shm_name, pid);
- if (!pktio_entry->s.ipc.rx.recv) {
- ODP_DBG("pid %d unable to find ipc ring %s name\n",
- getpid(), dev);
+ pktio_ipc->rx.recv = _ipc_shm_map(ipc_shm_name, pid);
+ if (!pktio_ipc->rx.recv) {
+ _ODP_DBG("pid %d unable to find ipc ring %s name\n", getpid(), dev);
sleep(1);
return -1;
}
- ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.recv),
- _ring_free_count(pktio_entry->s.ipc.rx.recv));
+ _ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->rx.recv, ring_mask),
+ _ring_free_count(pktio_ipc->rx.recv, ring_mask));
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_cons", dev);
- pktio_entry->s.ipc.rx.free = _ipc_shm_map(ipc_shm_name, pid);
- if (!pktio_entry->s.ipc.rx.free) {
- ODP_ERR("pid %d unable to find ipc ring %s name\n",
- getpid(), dev);
+ pktio_ipc->rx.free = _ipc_shm_map(ipc_shm_name, pid);
+ if (!pktio_ipc->rx.free) {
+ _ODP_ERR("pid %d unable to find ipc ring %s name\n", getpid(), dev);
goto free_m_prod;
}
- ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.free),
- _ring_free_count(pktio_entry->s.ipc.rx.free));
+ _ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->rx.free, ring_mask),
+ _ring_free_count(pktio_ipc->rx.free, ring_mask));
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_prod", dev);
- pktio_entry->s.ipc.tx.send = _ipc_shm_map(ipc_shm_name, pid);
- if (!pktio_entry->s.ipc.tx.send) {
- ODP_ERR("pid %d unable to find ipc ring %s name\n",
- getpid(), dev);
+ pktio_ipc->tx.send = _ipc_shm_map(ipc_shm_name, pid);
+ if (!pktio_ipc->tx.send) {
+ _ODP_ERR("pid %d unable to find ipc ring %s name\n", getpid(), dev);
goto free_m_cons;
}
- ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.send),
- _ring_free_count(pktio_entry->s.ipc.tx.send));
+ _ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->tx.send, ring_mask),
+ _ring_free_count(pktio_ipc->tx.send, ring_mask));
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", dev);
- pktio_entry->s.ipc.tx.free = _ipc_shm_map(ipc_shm_name, pid);
- if (!pktio_entry->s.ipc.tx.free) {
- ODP_ERR("pid %d unable to find ipc ring %s name\n",
- getpid(), dev);
+ pktio_ipc->tx.free = _ipc_shm_map(ipc_shm_name, pid);
+ if (!pktio_ipc->tx.free) {
+ _ODP_ERR("pid %d unable to find ipc ring %s name\n", getpid(), dev);
goto free_s_prod;
}
- ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
- ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.free),
- _ring_free_count(pktio_entry->s.ipc.tx.free));
+ _ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
+ ipc_shm_name, _ring_count(pktio_ipc->tx.free, ring_mask),
+ _ring_free_count(pktio_ipc->tx.free, ring_mask));
/* Get info about remote pool */
- pinfo = pktio_entry->s.ipc.pinfo;
+ pinfo = pktio_ipc->pinfo;
shm = _ipc_map_remote_pool(pinfo->master.pool_name,
pid);
- pktio_entry->s.ipc.remote_pool_shm = shm;
- pktio_entry->s.ipc.pool_mdata_base = (char *)odp_shm_addr(shm);
- pktio_entry->s.ipc.pkt_size = pinfo->master.block_size;
+ pktio_ipc->remote_pool_shm = shm;
+ pktio_ipc->pool_mdata_base = (char *)odp_shm_addr(shm);
+ pktio_ipc->remote_base_addr = pinfo->master.base_addr;
- _ipc_export_pool(pinfo, pktio_entry->s.ipc.pool);
+ _ipc_export_pool(pinfo, pktio_ipc->pool);
- odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 1);
+ odp_atomic_store_u32(&pktio_ipc->ready, 1);
pinfo->slave.init_done = 1;
- ODP_DBG("%s started.\n", pktio_entry->s.name);
+ _ODP_DBG("%s started.\n", pktio_entry->name);
return 0;
free_s_prod:
@@ -326,70 +515,71 @@ static int ipc_pktio_open(odp_pktio_t id ODP_UNUSED,
const char *dev,
odp_pool_t pool)
{
- int ret = -1;
- int pid ODP_UNUSED;
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ int ret = 0;
+ int pid;
struct pktio_info *pinfo;
char name[ODP_POOL_NAME_LEN + sizeof("_info")];
char tail[ODP_POOL_NAME_LEN];
odp_shm_t shm;
- ODP_STATIC_ASSERT(ODP_POOL_NAME_LEN == _RING_NAMESIZE,
- "mismatch pool and ring name arrays");
-
if (strncmp(dev, "ipc", 3))
return -1;
- odp_atomic_init_u32(&pktio_entry->s.ipc.ready, 0);
-
- pktio_entry->s.ipc.rx.cache = _ring_create("ipc_rx_cache",
- PKTIO_IPC_ENTRIES,
- _RING_NO_LIST);
+ odp_atomic_init_u32(&pktio_ipc->ready, 0);
/* Shared info about remote pktio */
if (sscanf(dev, "ipc:%d:%s", &pid, tail) == 2) {
- pktio_entry->s.ipc.type = PKTIO_TYPE_IPC_SLAVE;
+ pktio_ipc->type = PKTIO_TYPE_IPC_SLAVE;
snprintf(name, sizeof(name), "ipc:%s_info", tail);
- IPC_ODP_DBG("lookup for name %s for pid %d\n", name, pid);
+ ODP_DBG_LVL(IPC_DBG, "lookup for name %s for pid %d\n", name, pid);
shm = odp_shm_import(name, pid, name);
if (ODP_SHM_INVALID == shm)
return -1;
+
pinfo = odp_shm_addr(shm);
if (!pinfo->master.init_done) {
odp_shm_free(shm);
return -1;
}
- pktio_entry->s.ipc.pinfo = pinfo;
- pktio_entry->s.ipc.pinfo_shm = shm;
- ODP_DBG("process %d is slave\n", getpid());
+ pktio_ipc->pinfo = pinfo;
+ pktio_ipc->pinfo_shm = shm;
+ _ODP_DBG("process %d is slave\n", getpid());
ret = _ipc_init_slave(name, pktio_entry, pool);
} else {
- pktio_entry->s.ipc.type = PKTIO_TYPE_IPC_MASTER;
+ pktio_ipc->type = PKTIO_TYPE_IPC_MASTER;
snprintf(name, sizeof(name), "%s_info", dev);
shm = odp_shm_reserve(name, sizeof(struct pktio_info),
ODP_CACHE_LINE_SIZE,
- _ODP_ISHM_EXPORT | _ODP_ISHM_LOCK);
+ ODP_SHM_EXPORT | ODP_SHM_SINGLE_VA);
if (ODP_SHM_INVALID == shm) {
- ODP_ERR("can not create shm %s\n", name);
+ _ODP_ERR("can not create shm %s\n", name);
return -1;
}
pinfo = odp_shm_addr(shm);
pinfo->master.init_done = 0;
pinfo->master.pool_name[0] = 0;
- pktio_entry->s.ipc.pinfo = pinfo;
- pktio_entry->s.ipc.pinfo_shm = shm;
- ODP_DBG("process %d is master\n", getpid());
+
+ pktio_ipc->pinfo = pinfo;
+ pktio_ipc->pinfo_shm = shm;
+ _ODP_DBG("process %d is master\n", getpid());
ret = _ipc_init_master(pktio_entry, dev, pool);
}
+ if (ret)
+ odp_shm_free(shm);
+
return ret;
}
-static void _ipc_free_ring_packets(pktio_entry_t *pktio_entry, _ring_t *r)
+static void _ipc_free_ring_packets(pktio_entry_t *pktio_entry, ring_ptr_t *r,
+ uint32_t r_mask)
{
- uintptr_t offsets[PKTIO_IPC_ENTRIES];
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ uintptr_t offsets[IPC_BURST_SIZE];
int ret;
void **rbuf_p;
int i;
@@ -399,14 +589,13 @@ static void _ipc_free_ring_packets(pktio_entry_t *pktio_entry, _ring_t *r)
if (!r)
return;
- pool = pool_entry_from_hdl(pktio_entry->s.ipc.pool);
+ pool = _odp_pool_entry(pktio_ipc->pool);
addr = odp_shm_addr(pool->shm);
rbuf_p = (void *)&offsets;
while (1) {
- ret = _ring_mc_dequeue_burst(r, rbuf_p,
- PKTIO_IPC_ENTRIES);
+ ret = ring_ptr_deq_multi(r, r_mask, rbuf_p, IPC_BURST_SIZE);
if (ret <= 0)
break;
for (i = 0; i < ret; i++) {
@@ -424,36 +613,38 @@ static void _ipc_free_ring_packets(pktio_entry_t *pktio_entry, _ring_t *r)
static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
odp_packet_t pkt_table[], int len)
{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ uint32_t ring_mask = pktio_ipc->ring_mask;
int pkts = 0;
int i;
- _ring_t *r;
- _ring_t *r_p;
- uintptr_t offsets[PKTIO_IPC_ENTRIES];
+ ring_ptr_t *r;
+ ring_ptr_t *r_p;
+ uintptr_t offsets[len];
void **ipcbufs_p = (void *)&offsets[0];
uint32_t ready;
- int pkts_ring;
- ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
+ ready = odp_atomic_load_u32(&pktio_ipc->ready);
if (odp_unlikely(!ready)) {
- IPC_ODP_DBG("start pktio is missing before usage?\n");
+ ODP_DBG_LVL(IPC_DBG, "start pktio is missing before usage?\n");
return 0;
}
- _ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
+ _ipc_free_ring_packets(pktio_entry, pktio_ipc->tx.free, ring_mask);
/* rx from cache */
- r = pktio_entry->s.ipc.rx.cache;
- pkts = _ring_mc_dequeue_burst(r, ipcbufs_p, len);
+ r = pktio_ipc->rx.cache;
+ pkts = ring_ptr_deq_multi(r, ring_mask, ipcbufs_p, len);
if (odp_unlikely(pkts < 0))
- ODP_ABORT("internal error dequeue\n");
+ _ODP_ABORT("internal error dequeue\n");
/* rx from other app */
if (pkts == 0) {
ipcbufs_p = (void *)&offsets[0];
- r = pktio_entry->s.ipc.rx.recv;
- pkts = _ring_mc_dequeue_burst(r, ipcbufs_p, len);
+ r = pktio_ipc->rx.recv;
+ pkts = ring_ptr_deq_multi(r, ring_mask, ipcbufs_p,
+ len);
if (odp_unlikely(pkts < 0))
- ODP_ABORT("internal error dequeue\n");
+ _ODP_ABORT("internal error dequeue\n");
}
/* fast path */
@@ -468,25 +659,26 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
uint64_t data_pool_off;
void *rmt_data_ptr;
- phdr = (void *)((uint8_t *)pktio_entry->s.ipc.pool_mdata_base +
- offsets[i]);
+ phdr = (void *)((uint8_t *)pktio_ipc->pool_mdata_base +
+ offsets[i]);
- pool = pktio_entry->s.ipc.pool;
+ pool = pktio_ipc->pool;
if (odp_unlikely(pool == ODP_POOL_INVALID))
- ODP_ABORT("invalid pool");
+ _ODP_ABORT("invalid pool");
- data_pool_off = phdr->buf_hdr.ipc_data_offset;
+ data_pool_off = (uint8_t *)phdr->seg_data -
+ (uint8_t *)pktio_ipc->remote_base_addr;
pkt = odp_packet_alloc(pool, phdr->frame_len);
if (odp_unlikely(pkt == ODP_PACKET_INVALID)) {
/* Original pool might be smaller then
- * PKTIO_IPC_ENTRIES. If packet can not be
+ * ring size. If packet can not be
* allocated from pool at this time,
* simple get in on next recv() call. To keep
* packet ordering store such packets in local
* cache.
*/
- IPC_ODP_DBG("unable to allocate packet %d/%d\n",
+ ODP_DBG_LVL(IPC_DBG, "unable to allocate packet %d/%d\n",
i, pkts);
break;
}
@@ -494,123 +686,106 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
/* Copy packet data. */
pkt_data = odp_packet_data(pkt);
if (odp_unlikely(!pkt_data))
- ODP_ABORT("unable to map pkt_data ipc_slave %d\n",
- (PKTIO_TYPE_IPC_SLAVE ==
- pktio_entry->s.ipc.type));
+ _ODP_ABORT("unable to map pkt_data ipc_slave %d\n",
+ (PKTIO_TYPE_IPC_SLAVE == pktio_ipc->type));
/* Copy packet data from shared pool to local pool. */
- rmt_data_ptr = (uint8_t *)pktio_entry->s.ipc.pool_mdata_base +
- data_pool_off;
+ rmt_data_ptr = (uint8_t *)pktio_ipc->pool_mdata_base +
+ data_pool_off;
memcpy(pkt_data, rmt_data_ptr, phdr->frame_len);
/* Copy packets L2, L3 parsed offsets and size */
- copy_packet_cls_metadata(phdr, odp_packet_hdr(pkt));
+ _odp_packet_copy_cls_md(packet_hdr(pkt), phdr);
- odp_packet_hdr(pkt)->frame_len = phdr->frame_len;
- odp_packet_hdr(pkt)->headroom = phdr->headroom;
- odp_packet_hdr(pkt)->tailroom = phdr->tailroom;
+ packet_hdr(pkt)->frame_len = phdr->frame_len;
+ packet_hdr(pkt)->headroom = phdr->headroom;
+ packet_hdr(pkt)->tailroom = phdr->tailroom;
/* Take classification fields */
- odp_packet_hdr(pkt)->p = phdr->p;
+ packet_hdr(pkt)->p = phdr->p;
pkt_table[i] = pkt;
}
- /* put back to rx ring dequed but not processed packets*/
+ /* put back to rx ring dequeued but not processed packets*/
if (pkts != i) {
ipcbufs_p = (void *)&offsets[i];
- r_p = pktio_entry->s.ipc.rx.cache;
- pkts_ring = _ring_mp_enqueue_burst(r_p, ipcbufs_p, pkts - i);
-
- if (pkts_ring != (pkts - i))
- ODP_ABORT("bug to enqueue packets\n");
+ r_p = pktio_ipc->rx.cache;
+ ring_ptr_enq_multi(r_p, ring_mask, ipcbufs_p,
+ pkts - i);
if (i == 0)
return 0;
-
}
/*num of actually received packets*/
pkts = i;
/* Now tell other process that we no longer need that buffers.*/
- r_p = pktio_entry->s.ipc.rx.free;
-
-repeat:
+ r_p = pktio_ipc->rx.free;
ipcbufs_p = (void *)&offsets[0];
- pkts_ring = _ring_mp_enqueue_burst(r_p, ipcbufs_p, pkts);
- if (odp_unlikely(pkts_ring < 0))
- ODP_ABORT("ipc: odp_ring_mp_enqueue_bulk r_p fail\n");
+ ring_ptr_enq_multi(r_p, ring_mask, ipcbufs_p, pkts);
for (i = 0; i < pkts; i++) {
- IPC_ODP_DBG("%d/%d send to be free packet offset %x\n",
+ ODP_DBG_LVL(IPC_DBG, "%d/%d send to be free packet offset %" PRIuPTR "\n",
i, pkts, offsets[i]);
}
- if (odp_unlikely(pkts != pkts_ring)) {
- IPC_ODP_DBG("odp_ring_full: %d, odp_ring_count %d,"
- " _ring_free_count %d\n",
- _ring_full(r_p), _ring_count(r_p),
- _ring_free_count(r_p));
- ipcbufs_p = (void *)&offsets[pkts_ring - 1];
- pkts = pkts - pkts_ring;
- goto repeat;
- }
-
return pkts;
}
static int ipc_pktio_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkt_table[], int len)
+ odp_packet_t pkt_table[], int num)
{
int ret;
- odp_ticketlock_lock(&pktio_entry->s.rxl);
+ odp_ticketlock_lock(&pktio_entry->rxl);
- ret = ipc_pktio_recv_lockless(pktio_entry, pkt_table, len);
+ ret = ipc_pktio_recv_lockless(pktio_entry, pkt_table, num);
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ odp_ticketlock_unlock(&pktio_entry->rxl);
return ret;
}
static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
- const odp_packet_t pkt_table[], int len)
+ const odp_packet_t pkt_table[], int num)
{
- _ring_t *r;
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ uint32_t ring_mask = pktio_ipc->ring_mask;
+ ring_ptr_t *r;
void **rbuf_p;
- int ret;
int i;
- uint32_t ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
- odp_packet_t pkt_table_mapped[len]; /**< Ready to send packet has to be
+ uint32_t ready = odp_atomic_load_u32(&pktio_ipc->ready);
+ pool_t *ipc_pool = _odp_pool_entry(pktio_ipc->pool);
+ odp_packet_t pkt_table_mapped[num]; /**< Ready to send packet has to be
* in memory mapped pool. */
- uintptr_t offsets[len];
+ uintptr_t offsets[num];
if (odp_unlikely(!ready))
return 0;
- _ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
+ _ipc_free_ring_packets(pktio_entry, pktio_ipc->tx.free, ring_mask);
/* Copy packets to shm shared pool if they are in different
* pool, or if they are references (we can't share across IPC).
*/
- for (i = 0; i < len; i++) {
+ for (i = 0; i < num; i++) {
odp_packet_t pkt = pkt_table[i];
- pool_t *ipc_pool = pool_entry_from_hdl(pktio_entry->s.ipc.pool);
odp_packet_hdr_t *pkt_hdr;
pool_t *pool;
- pkt_hdr = odp_packet_hdr(pkt);
- pool = pkt_hdr->buf_hdr.pool_ptr;
+ pkt_hdr = packet_hdr(pkt);
+ pool = _odp_pool_entry(pkt_hdr->event_hdr.pool);
if (pool->pool_idx != ipc_pool->pool_idx ||
odp_packet_has_ref(pkt)) {
odp_packet_t newpkt;
- newpkt = odp_packet_copy(pkt, pktio_entry->s.ipc.pool);
+ newpkt = odp_packet_copy(pkt, pktio_ipc->pool);
if (newpkt == ODP_PACKET_INVALID)
- ODP_ABORT("Unable to copy packet\n");
+ _ODP_ABORT("Unable to copy packet\n");
odp_packet_free(pkt);
pkt_table_mapped[i] = newpkt;
@@ -620,58 +795,44 @@ static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
}
/* Set offset to phdr for outgoing packets */
- for (i = 0; i < len; i++) {
- uint64_t data_pool_off;
+ for (i = 0; i < num; i++) {
odp_packet_t pkt = pkt_table_mapped[i];
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
odp_pool_t pool_hdl = odp_packet_pool(pkt);
- pool_t *pool = pool_entry_from_hdl(pool_hdl);
+ pool_t *pool = _odp_pool_entry(pool_hdl);
offsets[i] = (uint8_t *)pkt_hdr -
(uint8_t *)odp_shm_addr(pool->shm);
- data_pool_off = (uint8_t *)pkt_hdr->buf_hdr.seg[0].data -
- (uint8_t *)odp_shm_addr(pool->shm);
/* compile all function code even if ipc disabled with config */
- pkt_hdr->buf_hdr.ipc_data_offset = data_pool_off;
- IPC_ODP_DBG("%d/%d send packet %llx, pool %llx,"
- "phdr = %p, offset %x sendoff %x, addr %llx iaddr %llx\n",
- i, len,
+ ODP_DBG_LVL(IPC_DBG, "%d/%d send packet %" PRIu64 ", pool %" PRIu64 ","
+ "phdr = %p, offset %td, sendoff %" PRIxPTR ", addr %p iaddr "
+ "%p\n", i, num,
odp_packet_to_u64(pkt), odp_pool_to_u64(pool_hdl),
- pkt_hdr, pkt_hdr->buf_hdr.ipc_data_offset,
- offsets[i], odp_shm_addr(pool->shm),
- odp_shm_addr(pool_entry_from_hdl(
- pktio_entry->s.ipc.pool)->shm));
+ (void *)pkt_hdr, (uint8_t *)pkt_hdr->seg_data -
+ (uint8_t *)odp_shm_addr(pool->shm), offsets[i],
+ odp_shm_addr(pool->shm),
+ odp_shm_addr(ipc_pool->shm));
}
/* Put packets to ring to be processed by other process. */
rbuf_p = (void *)&offsets[0];
- r = pktio_entry->s.ipc.tx.send;
- ret = _ring_mp_enqueue_burst(r, rbuf_p, len);
- if (odp_unlikely(ret < 0)) {
- ODP_ERR("pid %d odp_ring_mp_enqueue_bulk fail, ipc_slave %d, ret %d\n",
- getpid(),
- (PKTIO_TYPE_IPC_SLAVE == pktio_entry->s.ipc.type),
- ret);
- ODP_ERR("odp_ring_full: %d, odp_ring_count %d, _ring_free_count %d\n",
- _ring_full(r), _ring_count(r),
- _ring_free_count(r));
- ODP_ABORT("Unexpected!\n");
- }
+ r = pktio_ipc->tx.send;
+ ring_ptr_enq_multi(r, ring_mask, rbuf_p, num);
- return len;
+ return num;
}
static int ipc_pktio_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkt_table[], int len)
+ const odp_packet_t pkt_table[], int num)
{
int ret;
- odp_ticketlock_lock(&pktio_entry->s.txl);
+ odp_ticketlock_lock(&pktio_entry->txl);
- ret = ipc_pktio_send_lockless(pktio_entry, pkt_table, len);
+ ret = ipc_pktio_send_lockless(pktio_entry, pkt_table, num);
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ odp_ticketlock_unlock(&pktio_entry->txl);
return ret;
}
@@ -691,14 +852,15 @@ static int ipc_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
static int ipc_start(pktio_entry_t *pktio_entry)
{
- uint32_t ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ uint32_t ready = odp_atomic_load_u32(&pktio_ipc->ready);
if (ready) {
- ODP_ABORT("%s Already started\n", pktio_entry->s.name);
+ _ODP_ABORT("%s Already started\n", pktio_entry->name);
return -1;
}
- if (pktio_entry->s.ipc.type == PKTIO_TYPE_IPC_MASTER)
+ if (pktio_ipc->type == PKTIO_TYPE_IPC_MASTER)
return _ipc_master_start(pktio_entry);
else
return _ipc_slave_start(pktio_entry);
@@ -706,41 +868,79 @@ static int ipc_start(pktio_entry_t *pktio_entry)
static int ipc_stop(pktio_entry_t *pktio_entry)
{
- unsigned tx_send = 0, tx_free = 0;
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+ uint32_t ring_mask = pktio_ipc->ring_mask;
- odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 0);
+ odp_atomic_store_u32(&pktio_ipc->ready, 0);
- if (pktio_entry->s.ipc.tx.send)
- _ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.send);
+ if (pktio_ipc->tx.send)
+ _ipc_free_ring_packets(pktio_entry, pktio_ipc->tx.send,
+ ring_mask);
/* other process can transfer packets from one ring to
* other, use delay here to free that packets. */
sleep(1);
- if (pktio_entry->s.ipc.tx.free)
- _ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
-
- if (pktio_entry->s.ipc.tx.send)
- tx_send = _ring_count(pktio_entry->s.ipc.tx.send);
- if (pktio_entry->s.ipc.tx.free)
- tx_free = _ring_count(pktio_entry->s.ipc.tx.free);
- if (tx_send | tx_free) {
- ODP_DBG("IPC rings: tx send %d tx free %d\n",
- tx_send, tx_free);
- }
+ if (pktio_ipc->tx.free)
+ _ipc_free_ring_packets(pktio_entry, pktio_ipc->tx.free,
+ ring_mask);
+
+ return 0;
+}
+
+static int ipc_link_status(pktio_entry_t *pktio_entry)
+{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+
+ if (odp_atomic_load_u32(&pktio_ipc->ready))
+ return ODP_PKTIO_LINK_STATUS_UP;
+ return ODP_PKTIO_LINK_STATUS_DOWN;
+}
+
+static int ipc_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
+
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ info->media = "virtual";
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ if (odp_atomic_load_u32(&pktio_ipc->ready))
+ info->status = ODP_PKTIO_LINK_STATUS_UP;
+ else
+ info->status = ODP_PKTIO_LINK_STATUS_DOWN;
+
+ return 0;
+}
+
+static int ipc_capability(pktio_entry_t *pktio_entry ODP_UNUSED, odp_pktio_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_pktio_capability_t));
+
+ capa->max_input_queues = 1;
+ capa->max_output_queues = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
return 0;
}
static int ipc_close(pktio_entry_t *pktio_entry)
{
+ pkt_ipc_t *pktio_ipc = pkt_priv(pktio_entry);
char ipc_shm_name[ODP_POOL_NAME_LEN + sizeof("_m_prod")];
- char *dev = pktio_entry->s.name;
+ char *dev = pktio_entry->name;
char name[ODP_POOL_NAME_LEN];
char tail[ODP_POOL_NAME_LEN];
int pid = 0;
ipc_stop(pktio_entry);
- odp_shm_free(pktio_entry->s.ipc.remote_pool_shm);
+ odp_shm_free(pktio_ipc->remote_pool_shm);
if (sscanf(dev, "ipc:%d:%s", &pid, tail) == 2)
snprintf(name, sizeof(name), "ipc:%s", tail);
@@ -748,7 +948,7 @@ static int ipc_close(pktio_entry_t *pktio_entry)
snprintf(name, sizeof(name), "%s", dev);
/* unlink this pktio info for both master and slave */
- odp_shm_free(pktio_entry->s.ipc.pinfo_shm);
+ odp_shm_free(pktio_ipc->pinfo_shm);
/* destroy rings */
snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", name);
@@ -764,17 +964,10 @@ static int ipc_close(pktio_entry_t *pktio_entry)
return 0;
}
-static int ipc_pktio_init_global(void)
-{
- _ring_tailq_init();
- ODP_PRINT("PKTIO: initialized ipc interface.\n");
- return 0;
-}
-
-const pktio_if_ops_t ipc_pktio_ops = {
+const pktio_if_ops_t _odp_ipc_pktio_ops = {
.name = "ipc",
.print = NULL,
- .init_global = ipc_pktio_init_global,
+ .init_global = NULL,
.init_local = NULL,
.term = NULL,
.open = ipc_pktio_open,
@@ -783,11 +976,16 @@ const pktio_if_ops_t ipc_pktio_ops = {
.send = ipc_pktio_send,
.start = ipc_start,
.stop = ipc_stop,
- .mtu_get = ipc_mtu_get,
+ .link_status = ipc_link_status,
+ .link_info = ipc_link_info,
+ .capability = ipc_capability,
+ .maxlen_get = ipc_mtu_get,
.promisc_mode_set = NULL,
.promisc_mode_get = NULL,
.mac_get = ipc_mac_addr_get,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .mac_set = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL
};
diff --git a/platform/linux-generic/pktio/loop.c b/platform/linux-generic/pktio/loop.c
index c825393ac..05a1a3dce 100644
--- a/platform/linux-generic/pktio/loop.c
+++ b/platform/linux-generic/pktio/loop.c
@@ -1,257 +1,793 @@
-/* Copyright (c) 2013, Linaro Limited
- * Copyright (c) 2013, Nokia Solutions and Networks
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2013-2023 Nokia Solutions and Networks
*/
-#include <odp_api.h>
-#include <odp_packet_internal.h>
-#include <odp_packet_io_internal.h>
+#include <odp/api/debug.h>
+#include <odp/api/event.h>
+#include <odp/api/hash.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/queue.h>
+#include <odp/api/time.h>
+
+#include <odp/api/plat/byteorder_inlines.h>
+#include <odp/api/plat/packet_flag_inlines.h>
+#include <odp/api/plat/queue_inlines.h>
+
+#include <odp_parse_internal.h>
#include <odp_classification_internal.h>
#include <odp_debug_internal.h>
-#include <odp/api/hints.h>
+#include <odp_event_internal.h>
+#include <odp_global_data.h>
+#include <odp_ipsec_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_macros_internal.h>
#include <odp_queue_if.h>
#include <protocols/eth.h>
#include <protocols/ip.h>
-#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define MAX_QUEUES (ODP_PKTIN_MAX_QUEUES > ODP_PKTOUT_MAX_QUEUES ? \
+ ODP_PKTIN_MAX_QUEUES : ODP_PKTOUT_MAX_QUEUES)
+
+#define MAX_LOOP 16
+
+#define LOOP_MTU_MIN 68
+#define LOOP_MTU_MAX UINT16_MAX
+
+#define LOOP_MAX_QUEUE_SIZE 1024
+
+typedef struct {
+ odp_atomic_u64_t in_octets;
+ odp_atomic_u64_t in_packets;
+ odp_atomic_u64_t in_discards;
+ odp_atomic_u64_t in_errors;
+ odp_atomic_u64_t out_octets;
+ odp_atomic_u64_t out_packets;
+} stats_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ /* queue handle as the "wire" */
+ odp_queue_t queue;
+ /* queue specific statistics */
+ stats_t stats;
+ /* config input queue size */
+ uint32_t in_size;
+ /* config output queue size */
+ uint32_t out_size;
+} loop_queue_t;
+
+typedef struct {
+ /* loopback entries for "loop" device */
+ loop_queue_t loopqs[MAX_QUEUES];
+ /* hash config */
+ odp_pktin_hash_proto_t hash;
+ /* config queue count */
+ uint32_t num_conf_qs;
+ /* actual number queues */
+ uint32_t num_qs;
+ /* link MTU */
+ uint16_t mtu;
+ /* index of "loop" device */
+ uint8_t idx;
+ /* create or re-create queue during start */
+ uint8_t queue_create;
+} pkt_loop_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_loop_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static inline pkt_loop_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_loop_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
/* MAC address for the "loop" interface */
-static const char pktio_loop_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x01};
+static const uint8_t pktio_loop_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x01};
-static int loopback_stats_reset(pktio_entry_t *pktio_entry);
+static int loopback_init_capability(pktio_entry_t *pktio_entry);
-static int loopback_open(odp_pktio_t id, pktio_entry_t *pktio_entry,
+static int loopback_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
const char *devname, odp_pool_t pool ODP_UNUSED)
{
- if (strcmp(devname, "loop"))
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+ long idx;
+
+ if (!strcmp(devname, "loop")) {
+ idx = 0;
+ } else if (!strncmp(devname, "loop", 4)) {
+ char *end;
+
+ idx = strtol(devname + 4, &end, 10);
+ if (idx <= 0 || idx >= MAX_LOOP || *end)
+ return -1;
+ } else {
return -1;
+ }
+
+ memset(pkt_loop, 0, sizeof(pkt_loop_t));
+ pkt_loop->mtu = LOOP_MTU_MAX;
+ pkt_loop->idx = idx;
+ pkt_loop->queue_create = 1;
+ loopback_init_capability(pktio_entry);
+
+ for (uint32_t i = 0; i < MAX_QUEUES; i++) {
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_octets, 0);
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_packets, 0);
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_discards, 0);
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_errors, 0);
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.out_octets, 0);
+ odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.out_packets, 0);
+ }
+
+ return 0;
+}
+
+static int loopback_queue_destroy(odp_queue_t queue)
+{
+ odp_event_t event;
- char loopq_name[ODP_QUEUE_NAME_LEN];
+ do {
+ event = odp_queue_deq(queue);
+ if (event != ODP_EVENT_INVALID)
+ odp_event_free(event);
- snprintf(loopq_name, sizeof(loopq_name), "%" PRIu64 "-pktio_loopq",
- odp_pktio_to_u64(id));
- pktio_entry->s.pkt_loop.loopq =
- odp_queue_create(loopq_name, NULL);
+ } while (event != ODP_EVENT_INVALID);
- if (pktio_entry->s.pkt_loop.loopq == ODP_QUEUE_INVALID)
+ if (odp_queue_destroy(queue)) {
+ _ODP_ERR("Destroying loopback pktio queue failed\n");
return -1;
+ }
+ return 0;
+}
+
+static int loopback_queues_destroy(loop_queue_t *queues, uint32_t num_queues)
+{
+ int ret = 0;
+
+ for (uint32_t i = 0; i < num_queues; i++) {
+ if (loopback_queue_destroy(queues[i].queue))
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int loopback_start(pktio_entry_t *pktio_entry)
+{
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+ odp_queue_param_t queue_param;
+ char queue_name[ODP_QUEUE_NAME_LEN];
+
+ /* Re-create queue only when necessary */
+ if (!pkt_loop->queue_create)
+ return 0;
+
+ /* Destroy old queues */
+ if (loopback_queues_destroy(pkt_loop->loopqs, pkt_loop->num_qs))
+ return -1;
+
+ pkt_loop->num_qs = 0;
+
+ for (uint32_t i = 0; i < pkt_loop->num_conf_qs; i++) {
+ odp_queue_param_init(&queue_param);
+ queue_param.size = _ODP_MAX(pkt_loop->loopqs[i].in_size,
+ pkt_loop->loopqs[i].out_size);
+ snprintf(queue_name, sizeof(queue_name), "_odp_pktio_loopq-%" PRIu64 "-%u",
+ odp_pktio_to_u64(pktio_entry->handle), i);
+ pkt_loop->loopqs[i].queue = odp_queue_create(queue_name, &queue_param);
+
+ if (pkt_loop->loopqs[i].queue == ODP_QUEUE_INVALID) {
+ _ODP_ERR("Creating loopback pktio queue %s failed\n", queue_name);
+ (void)loopback_queues_destroy(pkt_loop->loopqs, i);
+ return -1;
+ }
+ }
- loopback_stats_reset(pktio_entry);
+ pkt_loop->num_qs = pkt_loop->num_conf_qs;
+
+ return 0;
+}
+
+static int loopback_pktin_queue_config(pktio_entry_t *pktio_entry,
+ const odp_pktin_queue_param_t *param)
+{
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ pkt_loop->num_conf_qs = param->num_queues;
+ pkt_loop->queue_create = 1;
+ pkt_loop->hash.all_bits = param->hash_enable ? param->hash_proto.all_bits : 0;
+
+ if (pktio_entry->param.in_mode == ODP_PKTIN_MODE_DIRECT) {
+ for (uint32_t i = 0; i < MAX_QUEUES; i++) {
+ if (i < pkt_loop->num_conf_qs)
+ pkt_loop->loopqs[i].in_size = param->queue_size[i];
+ else
+ pkt_loop->loopqs[i].in_size = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int loopback_pktout_queue_config(pktio_entry_t *pktio_entry,
+ const odp_pktout_queue_param_t *param)
+{
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ pkt_loop->queue_create = 1;
+
+ for (uint32_t i = 0; i < MAX_QUEUES; i++) {
+ if (i < param->num_queues)
+ pkt_loop->loopqs[i].out_size = param->queue_size[i];
+ else
+ pkt_loop->loopqs[i].out_size = 0;
+ }
return 0;
}
static int loopback_close(pktio_entry_t *pktio_entry)
{
- return odp_queue_destroy(pktio_entry->s.pkt_loop.loopq);
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ return loopback_queues_destroy(pkt_loop->loopqs, pkt_loop->num_qs);
}
-static int loopback_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkts[], int len)
+static int loopback_recv(pktio_entry_t *pktio_entry, int index, odp_packet_t pkts[], int num)
{
int nbr, i;
- odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
- queue_t queue;
+ loop_queue_t *entry = &pkt_priv(pktio_entry)->loopqs[index];
+ odp_queue_t queue = entry->queue;
+ stats_t *stats = &entry->stats;
+ _odp_event_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
+ odp_packet_t cls_tbl[QUEUE_MULTI_MAX];
odp_packet_hdr_t *pkt_hdr;
- odp_packet_hdr_t parsed_hdr;
odp_packet_t pkt;
odp_time_t ts_val;
odp_time_t *ts = NULL;
int num_rx = 0;
- int failed = 0;
-
- if (odp_unlikely(len > QUEUE_MULTI_MAX))
- len = QUEUE_MULTI_MAX;
+ int packets = 0;
+ int num_cls = 0;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ uint32_t octets = 0;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
- odp_ticketlock_lock(&pktio_entry->s.rxl);
+ if (odp_unlikely(num > QUEUE_MULTI_MAX))
+ num = QUEUE_MULTI_MAX;
- queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq);
- nbr = queue_fn->deq_multi(queue, hdr_tbl, len);
+ nbr = odp_queue_deq_multi(queue, (odp_event_t *)hdr_tbl, num);
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp) {
+ if (opt.bit.ts_all || opt.bit.ts_ptp) {
ts_val = odp_time_global();
ts = &ts_val;
}
for (i = 0; i < nbr; i++) {
uint32_t pkt_len;
+ int do_ipsec_enq = 0;
- pkt = packet_from_buf_hdr(hdr_tbl[i]);
+ pkt = packet_from_event_hdr(hdr_tbl[i]);
pkt_len = odp_packet_len(pkt);
+ pkt_hdr = packet_hdr(pkt);
-
- if (pktio_cls_enabled(pktio_entry)) {
- odp_packet_t new_pkt;
- odp_pool_t new_pool;
+ if (layer) {
uint8_t *pkt_addr;
- uint8_t buf[PACKET_PARSE_SEG_LEN];
+ uint8_t buf[PARSE_BYTES];
int ret;
uint32_t seg_len = odp_packet_seg_len(pkt);
/* Make sure there is enough data for the packet
* parser in the case of a segmented packet. */
- if (odp_unlikely(seg_len < PACKET_PARSE_SEG_LEN &&
- pkt_len > PACKET_PARSE_SEG_LEN)) {
- odp_packet_copy_to_mem(pkt, 0,
- PACKET_PARSE_SEG_LEN,
- buf);
- seg_len = PACKET_PARSE_SEG_LEN;
+ if (odp_unlikely(seg_len < PARSE_BYTES &&
+ pkt_len > seg_len)) {
+ seg_len = _ODP_MIN(pkt_len, PARSE_BYTES);
+ odp_packet_copy_to_mem(pkt, 0, seg_len, buf);
pkt_addr = buf;
} else {
pkt_addr = odp_packet_data(pkt);
}
- ret = cls_classify_packet(pktio_entry, pkt_addr,
- pkt_len, seg_len,
- &new_pool, &parsed_hdr);
- if (ret) {
- failed++;
+
+ packet_parse_reset(pkt_hdr, 1);
+ ret = _odp_packet_parse_common(pkt_hdr, pkt_addr, pkt_len,
+ seg_len, layer, opt);
+ if (ret)
+ odp_atomic_inc_u64(&stats->in_errors);
+
+ if (ret < 0) {
odp_packet_free(pkt);
continue;
}
- if (new_pool != odp_packet_pool(pkt)) {
- new_pkt = odp_packet_copy(pkt, new_pool);
- odp_packet_free(pkt);
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, pkt_addr,
+ &new_pool, pkt_hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&stats->in_discards);
+
+ if (ret) {
+ odp_packet_free(pkt);
+ continue;
+ }
- if (new_pkt == ODP_PACKET_INVALID) {
- failed++;
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &pkt_hdr, new_pool))) {
+ odp_packet_free(pkt);
+ odp_atomic_inc_u64(&stats->in_discards);
continue;
}
- pkt = new_pkt;
}
}
- pkt_hdr = odp_packet_hdr(pkt);
- pkt_hdr->input = pktio_entry->s.handle;
+ packet_set_ts(pkt_hdr, ts);
+ pkt_hdr->input = pktio_entry->handle;
+
+ /* Try IPsec inline processing */
+ if (pktio_entry->config.inbound_ipsec &&
+ !pkt_hdr->p.flags.ip_err &&
+ odp_packet_has_ipsec(pkt)) {
+ do_ipsec_enq = !_odp_ipsec_try_inline(&pkt);
+ pkt_hdr = packet_hdr(pkt);
+ }
+
+ if (!pkt_hdr->p.flags.all.error) {
+ octets += pkt_len;
+ packets++;
+ }
+
+ if (do_ipsec_enq) {
+ if (odp_unlikely(odp_queue_enq(pkt_hdr->dst_queue,
+ odp_packet_to_event(pkt)))) {
+ odp_atomic_inc_u64(&stats->in_discards);
+ if (!pkt_hdr->p.flags.all.error) {
+ octets -= pkt_len;
+ packets--;
+ }
+ odp_packet_free(pkt);
+ }
+ } else if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ cls_tbl[num_cls++] = pkt;
+ num_cls = _odp_cls_enq(cls_tbl, num_cls, (i + 1 == nbr));
+ } else {
+ pkts[num_rx++] = pkt;
+ }
+ }
+
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(num_cls))
+ _odp_cls_enq(cls_tbl, num_cls, true);
+
+ odp_atomic_add_u64(&stats->in_octets, octets);
+ odp_atomic_add_u64(&stats->in_packets, packets);
+
+ return num_rx;
+}
+
+#define OL_TX_CHKSUM_PKT(_cfg, _capa, _proto, _ovr_set, _ovr) \
+ (_capa && _proto && (_ovr_set ? _ovr : _cfg))
+
+static inline int check_proto(void *l3_hdr,
+ uint32_t l3_len,
+ odp_bool_t *l3_proto_v4,
+ uint8_t *l4_proto)
+{
+ uint8_t l3_proto_ver = _ODP_IPV4HDR_VER(*(uint8_t *)l3_hdr);
+
+ if (l3_proto_ver == _ODP_IPV4 && l3_len >= _ODP_IPV4HDR_LEN) {
+ _odp_ipv4hdr_t *ip = l3_hdr;
+ uint16_t frag_offset = odp_be_to_cpu_16(ip->frag_offset);
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, pkt_hdr);
+ *l3_proto_v4 = 1;
+ if (!_ODP_IPV4HDR_IS_FRAGMENT(frag_offset))
+ *l4_proto = ip->proto;
else
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
+ *l4_proto = 255;
- packet_set_ts(pkt_hdr, ts);
+ return 0;
+ } else if (l3_proto_ver == _ODP_IPV6 && l3_len >= _ODP_IPV6HDR_LEN) {
+ _odp_ipv6hdr_t *ipv6 = l3_hdr;
- pktio_entry->s.stats.in_octets += pkt_len;
+ *l3_proto_v4 = 0;
+ *l4_proto = ipv6->next_hdr;
- pkts[num_rx++] = pkt;
+ /* FIXME: check that packet is not a fragment !!!
+ * Might require parsing headers spanning several segments, so
+ * not implemented yet. */
+ return 0;
}
- pktio_entry->s.stats.in_errors += failed;
- pktio_entry->s.stats.in_ucast_pkts += num_rx - failed;
+ return -1;
+}
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+static inline void loopback_fix_checksums(odp_packet_t pkt,
+ odp_pktout_config_opt_t *pktout_cfg,
+ odp_pktout_config_opt_t *pktout_capa)
+{
+ odp_bool_t l3_proto_v4 = false;
+ uint8_t l4_proto;
+ void *l3_hdr;
+ uint32_t l3_len;
+ odp_bool_t ipv4_chksum_pkt, udp_chksum_pkt, tcp_chksum_pkt,
+ sctp_chksum_pkt;
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
+
+ l3_hdr = odp_packet_l3_ptr(pkt, &l3_len);
+
+ if (l3_hdr == NULL ||
+ check_proto(l3_hdr, l3_len, &l3_proto_v4, &l4_proto))
+ return;
+
+ ipv4_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.ipv4_chksum,
+ pktout_capa->bit.ipv4_chksum,
+ l3_proto_v4,
+ pkt_hdr->p.flags.l3_chksum_set,
+ pkt_hdr->p.flags.l3_chksum);
+ udp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.udp_chksum,
+ pktout_capa->bit.udp_chksum,
+ l4_proto == _ODP_IPPROTO_UDP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+ tcp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.tcp_chksum,
+ pktout_capa->bit.tcp_chksum,
+ l4_proto == _ODP_IPPROTO_TCP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+ sctp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.sctp_chksum,
+ pktout_capa->bit.sctp_chksum,
+ l4_proto == _ODP_IPPROTO_SCTP,
+ pkt_hdr->p.flags.l4_chksum_set,
+ pkt_hdr->p.flags.l4_chksum);
+
+ if (ipv4_chksum_pkt)
+ _odp_packet_ipv4_chksum_insert(pkt);
+
+ if (tcp_chksum_pkt)
+ _odp_packet_tcp_chksum_insert(pkt);
+
+ if (udp_chksum_pkt)
+ _odp_packet_udp_chksum_insert(pkt);
+
+ if (sctp_chksum_pkt)
+ _odp_packet_sctp_chksum_insert(pkt);
+}
- return num_rx;
+static inline uint8_t *add_data(uint8_t *data, void *src, uint32_t len)
+{
+ return (uint8_t *)memcpy(data, src, len) + len;
}
-static int loopback_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkt_tbl[], int len)
+static inline odp_queue_t get_dest_queue(const pkt_loop_t *pkt_loop, odp_packet_t pkt, int index)
{
- odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX];
- queue_t queue;
+ const odp_pktin_hash_proto_t *hash = &pkt_loop->hash;
+ _odp_udphdr_t udp;
+ _odp_tcphdr_t tcp;
+ _odp_ipv4hdr_t ipv4;
+ _odp_ipv6hdr_t ipv6;
+ uint32_t off;
+ /* Space for UDP/TCP source and destination ports and IPv4/IPv6 source and destination
+ * addresses. */
+ uint8_t data[2 * sizeof(uint16_t) + 2 * 4 * sizeof(uint32_t)];
+ uint8_t *head = data;
+
+ if (hash->all_bits == 0)
+ return pkt_loop->loopqs[index % pkt_loop->num_qs].queue;
+
+ memset(data, 0, sizeof(data));
+ off = odp_packet_l4_offset(pkt);
+
+ if (off != ODP_PACKET_OFFSET_INVALID) {
+ if ((hash->proto.ipv4_udp || hash->proto.ipv6_udp) && odp_packet_has_udp(pkt)) {
+ if (odp_packet_copy_to_mem(pkt, off, _ODP_UDPHDR_LEN, &udp) == 0) {
+ head = add_data(head, &udp.src_port, sizeof(udp.src_port));
+ head = add_data(head, &udp.dst_port, sizeof(udp.dst_port));
+ }
+ } else if ((hash->proto.ipv4_tcp || hash->proto.ipv6_tcp) &&
+ odp_packet_has_tcp(pkt)) {
+ if (odp_packet_copy_to_mem(pkt, off, _ODP_TCPHDR_LEN, &tcp) == 0) {
+ head = add_data(head, &tcp.src_port, sizeof(tcp.src_port));
+ head = add_data(head, &tcp.dst_port, sizeof(tcp.dst_port));
+ }
+ }
+ }
+
+ off = odp_packet_l3_offset(pkt);
+
+ if (off != ODP_PACKET_OFFSET_INVALID) {
+ if (hash->proto.ipv4 && odp_packet_has_ipv4(pkt)) {
+ if (odp_packet_copy_to_mem(pkt, off, _ODP_IPV4HDR_LEN, &ipv4) == 0) {
+ head = add_data(head, &ipv4.src_addr, sizeof(ipv4.src_addr));
+ head = add_data(head, &ipv4.dst_addr, sizeof(ipv4.dst_addr));
+ }
+ } else if (hash->proto.ipv6 && odp_packet_has_ipv6(pkt)) {
+ if (odp_packet_copy_to_mem(pkt, off, _ODP_IPV6HDR_LEN, &ipv6) == 0) {
+ head = add_data(head, &ipv6.src_addr, sizeof(ipv6.src_addr));
+ head = add_data(head, &ipv6.dst_addr, sizeof(ipv6.dst_addr));
+ }
+ }
+ }
+
+ return pkt_loop->loopqs[odp_hash_crc32c(data, head - data, 0) % pkt_loop->num_qs].queue;
+}
+
+static int loopback_send(pktio_entry_t *pktio_entry, int index, const odp_packet_t pkt_tbl[],
+ int num)
+{
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+ odp_queue_t queue;
+ stats_t *stats;
int i;
int ret;
- uint32_t bytes = 0;
+ int nb_tx = 0;
+ int tx_ts_idx = 0;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
+ odp_pktout_config_opt_t *pktout_cfg = &pktio_entry->config.pktout;
+ odp_pktout_config_opt_t *pktout_capa = &pktio_entry->capa.config.pktout;
- if (odp_unlikely(len > QUEUE_MULTI_MAX))
- len = QUEUE_MULTI_MAX;
+ if (pkt_loop->num_qs == 0)
+ return 0;
- for (i = 0; i < len; ++i) {
- hdr_tbl[i] = packet_to_buf_hdr(pkt_tbl[i]);
- bytes += odp_packet_len(pkt_tbl[i]);
- }
+ stats = &pkt_loop->loopqs[index].stats;
- odp_ticketlock_lock(&pktio_entry->s.txl);
+ if (odp_unlikely(num > QUEUE_MULTI_MAX))
+ num = QUEUE_MULTI_MAX;
- queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq);
- ret = queue_fn->enq_multi(queue, hdr_tbl, len);
+ for (i = 0; i < num; ++i) {
+ uint32_t pkt_len = odp_packet_len(pkt_tbl[i]);
- if (ret > 0) {
- pktio_entry->s.stats.out_ucast_pkts += ret;
- pktio_entry->s.stats.out_octets += bytes;
- } else {
- ODP_DBG("queue enqueue failed %i\n", ret);
- ret = -1;
+ if (odp_unlikely(pkt_len > pkt_loop->mtu)) {
+ if (nb_tx == 0)
+ return -1;
+ break;
+ }
+
+ if (tx_ts_enabled && tx_ts_idx == 0) {
+ if (odp_unlikely(packet_hdr(pkt_tbl[i])->p.flags.ts_set))
+ tx_ts_idx = i + 1;
+ }
+
+ packet_subtype_set(pkt_tbl[i], ODP_EVENT_PACKET_BASIC);
+ loopback_fix_checksums(pkt_tbl[i], pktout_cfg, pktout_capa);
+ queue = get_dest_queue(pkt_loop, pkt_tbl[i], index);
+ ret = odp_queue_enq(queue, odp_packet_to_event(pkt_tbl[i]));
+
+ if (ret < 0) {
+ _ODP_DBG("queue enqueue failed %i to queue: %" PRIu64 "\n", ret,
+ odp_queue_to_u64(queue));
+ break;
+ }
+
+ nb_tx++;
+ odp_atomic_inc_u64(&stats->out_packets);
+ odp_atomic_add_u64(&stats->out_octets, pkt_len);
}
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ if (nb_tx > 0) {
+ if (odp_unlikely(tx_ts_idx) && nb_tx >= tx_ts_idx)
+ _odp_pktio_tx_ts_set(pktio_entry);
+ }
- return ret;
+ return nb_tx;
}
-static uint32_t loopback_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+static uint32_t loopback_mtu_get(pktio_entry_t *pktio_entry)
{
- /* the loopback interface imposes no maximum transmit size limit */
- return INT_MAX;
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ return pkt_loop->mtu;
}
-static int loopback_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
- void *mac_addr)
+static int loopback_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ pkt_loop->mtu = maxlen_input;
+
+ return 0;
+}
+
+static int loopback_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
{
memcpy(mac_addr, pktio_loop_mac, ETH_ALEN);
+ ((uint8_t *)mac_addr)[ETH_ALEN - 1] += pkt_priv(pktio_entry)->idx;
return ETH_ALEN;
}
static int loopback_link_status(pktio_entry_t *pktio_entry ODP_UNUSED)
{
/* loopback interfaces are always up */
- return 1;
+ return ODP_PKTIO_LINK_STATUS_UP;
+}
+
+static int loopback_link_info(pktio_entry_t *pktio_entry ODP_UNUSED, odp_pktio_link_info_t *info)
+{
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ info->media = "virtual";
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ info->status = ODP_PKTIO_LINK_STATUS_UP;
+
+ return 0;
}
-static int loopback_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
- odp_pktio_capability_t *capa)
+static int loopback_init_capability(pktio_entry_t *pktio_entry)
{
+ odp_pktio_capability_t *capa = &pktio_entry->capa;
+ odp_queue_capability_t queue_capa;
+
+ if (odp_queue_capability(&queue_capa)) {
+ _ODP_ERR("Queue capability failed\n");
+ return -1;
+ }
+
memset(capa, 0, sizeof(odp_pktio_capability_t));
- capa->max_input_queues = 1;
- capa->max_output_queues = 1;
- capa->set_op.op.promisc_mode = 1;
+ capa->max_input_queues = ODP_PKTIN_MAX_QUEUES;
+ capa->max_output_queues = ODP_PKTOUT_MAX_QUEUES;
+ capa->set_op.op.promisc_mode = 0;
+ capa->set_op.op.maxlen = 1;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = LOOP_MTU_MIN;
+ capa->maxlen.max_input = LOOP_MTU_MAX;
+ capa->maxlen.min_output = LOOP_MTU_MIN;
+ capa->maxlen.max_output = LOOP_MTU_MAX;
+
+ capa->min_input_queue_size = 1;
+ capa->max_input_queue_size = queue_capa.plain.max_size;
+ if (capa->max_input_queue_size == 0)
+ capa->max_input_queue_size = LOOP_MAX_QUEUE_SIZE;
+
+ capa->min_output_queue_size = 1;
+ capa->max_output_queue_size = queue_capa.plain.max_size;
+ if (capa->max_output_queue_size == 0)
+ capa->max_output_queue_size = LOOP_MAX_QUEUE_SIZE;
odp_pktio_config_init(&capa->config);
+ capa->config.enable_loop = 1;
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
+ capa->config.pktin.bit.ipv4_chksum = 1;
+ capa->config.pktin.bit.tcp_chksum = 1;
+ capa->config.pktin.bit.udp_chksum = 1;
+ capa->config.pktin.bit.sctp_chksum = 1;
+ capa->config.pktout.bit.ipv4_chksum = 1;
+ capa->config.pktout.bit.tcp_chksum = 1;
+ capa->config.pktout.bit.udp_chksum = 1;
+ capa->config.pktout.bit.sctp_chksum = 1;
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+
+ if (odp_global_ro.disable.ipsec == 0) {
+ capa->config.inbound_ipsec = 1;
+ capa->config.outbound_ipsec = 1;
+ }
+
+ capa->config.pktout.bit.ipv4_chksum_ena =
+ capa->config.pktout.bit.ipv4_chksum;
+ capa->config.pktout.bit.udp_chksum_ena =
+ capa->config.pktout.bit.udp_chksum;
+ capa->config.pktout.bit.tcp_chksum_ena =
+ capa->config.pktout.bit.tcp_chksum;
+ capa->config.pktout.bit.sctp_chksum_ena =
+ capa->config.pktout.bit.sctp_chksum;
+
+ capa->stats.pktio.counter.in_octets = 1;
+ capa->stats.pktio.counter.in_packets = 1;
+ capa->stats.pktio.counter.in_errors = 1;
+ capa->stats.pktio.counter.in_discards = 1;
+ capa->stats.pktio.counter.out_octets = 1;
+ capa->stats.pktio.counter.out_packets = 1;
+ capa->stats.pktin_queue.counter.octets = 1;
+ capa->stats.pktin_queue.counter.packets = 1;
+ capa->stats.pktin_queue.counter.errors = 1;
+ capa->stats.pktin_queue.counter.discards = 1;
+ capa->stats.pktout_queue.counter.octets = 1;
+ capa->stats.pktout_queue.counter.packets = 1;
+ return 0;
+}
+
+static int loopback_capability(pktio_entry_t *pktio_entry, odp_pktio_capability_t *capa)
+{
+ *capa = pktio_entry->capa;
return 0;
}
-static int loopback_promisc_mode_set(pktio_entry_t *pktio_entry,
- odp_bool_t enable)
+static int loopback_promisc_mode_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ return 1;
+}
+
+static int loopback_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats)
{
- pktio_entry->s.pkt_loop.promisc = enable;
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ memset(stats, 0, sizeof(odp_pktio_stats_t));
+
+ for (uint32_t i = 0; i < MAX_QUEUES; i++) {
+ stats_t *qs = &pkt_loop->loopqs[i].stats;
+
+ stats->in_octets += odp_atomic_load_u64(&qs->in_octets);
+ stats->in_packets += odp_atomic_load_u64(&qs->in_packets);
+ stats->in_discards += odp_atomic_load_u64(&qs->in_discards);
+ stats->in_errors += odp_atomic_load_u64(&qs->in_errors);
+ stats->out_octets += odp_atomic_load_u64(&qs->out_octets);
+ stats->out_packets += odp_atomic_load_u64(&qs->out_packets);
+ }
+
return 0;
}
-static int loopback_promisc_mode_get(pktio_entry_t *pktio_entry)
+static int loopback_stats_reset(pktio_entry_t *pktio_entry)
{
- return pktio_entry->s.pkt_loop.promisc ? 1 : 0;
+ pkt_loop_t *pkt_loop = pkt_priv(pktio_entry);
+
+ for (uint32_t i = 0; i < MAX_QUEUES; i++) {
+ stats_t *qs = &pkt_loop->loopqs[i].stats;
+
+ odp_atomic_store_u64(&qs->in_octets, 0);
+ odp_atomic_store_u64(&qs->in_packets, 0);
+ odp_atomic_store_u64(&qs->in_discards, 0);
+ odp_atomic_store_u64(&qs->in_errors, 0);
+ odp_atomic_store_u64(&qs->out_octets, 0);
+ odp_atomic_store_u64(&qs->out_packets, 0);
+ }
+
+ return 0;
}
-static int loopback_stats(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats)
+static int loopback_pktin_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktin_queue_stats_t *pktin_stats)
{
- memcpy(stats, &pktio_entry->s.stats, sizeof(odp_pktio_stats_t));
+ stats_t *qs = &pkt_priv(pktio_entry)->loopqs[index].stats;
+
+ memset(pktin_stats, 0, sizeof(odp_pktin_queue_stats_t));
+ pktin_stats->octets = odp_atomic_load_u64(&qs->in_octets);
+ pktin_stats->packets = odp_atomic_load_u64(&qs->in_packets);
+ pktin_stats->discards = odp_atomic_load_u64(&qs->in_discards);
+ pktin_stats->errors = odp_atomic_load_u64(&qs->in_errors);
+
return 0;
}
-static int loopback_stats_reset(pktio_entry_t *pktio_entry ODP_UNUSED)
+static int loopback_pktout_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktout_queue_stats_t *pktout_stats)
{
- memset(&pktio_entry->s.stats, 0, sizeof(odp_pktio_stats_t));
+ stats_t *qs = &pkt_priv(pktio_entry)->loopqs[index].stats;
+
+ memset(pktout_stats, 0, sizeof(odp_pktout_queue_stats_t));
+ pktout_stats->octets = odp_atomic_load_u64(&qs->out_octets);
+ pktout_stats->packets = odp_atomic_load_u64(&qs->out_packets);
+
return 0;
}
static int loop_init_global(void)
{
- ODP_PRINT("PKTIO: initialized loop interface.\n");
+ _ODP_PRINT("PKTIO: initialized loop interface.\n");
return 0;
}
-const pktio_if_ops_t loopback_pktio_ops = {
+const pktio_if_ops_t _odp_loopback_pktio_ops = {
.name = "loop",
.print = NULL,
.init_global = loop_init_global,
@@ -259,21 +795,27 @@ const pktio_if_ops_t loopback_pktio_ops = {
.term = NULL,
.open = loopback_open,
.close = loopback_close,
- .start = NULL,
+ .start = loopback_start,
.stop = NULL,
.stats = loopback_stats,
.stats_reset = loopback_stats_reset,
+ .pktin_queue_stats = loopback_pktin_stats,
+ .pktout_queue_stats = loopback_pktout_stats,
.recv = loopback_recv,
.send = loopback_send,
- .mtu_get = loopback_mtu_get,
- .promisc_mode_set = loopback_promisc_mode_set,
+ .maxlen_get = loopback_mtu_get,
+ .maxlen_set = loopback_mtu_set,
+ .promisc_mode_set = NULL,
.promisc_mode_get = loopback_promisc_mode_get,
.mac_get = loopback_mac_addr_get,
+ .mac_set = NULL,
.link_status = loopback_link_status,
+ .link_info = loopback_link_info,
.capability = loopback_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL,
- .input_queues_config = NULL,
- .output_queues_config = NULL,
+ .input_queues_config = loopback_pktin_queue_config,
+ .output_queues_config = loopback_pktout_queue_config,
};
diff --git a/platform/linux-generic/pktio/netmap.c b/platform/linux-generic/pktio/netmap.c
deleted file mode 100644
index 928bb00af..000000000
--- a/platform/linux-generic/pktio/netmap.c
+++ /dev/null
@@ -1,973 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifdef ODP_NETMAP
-
-#include <odp_posix_extensions.h>
-
-#include <odp/api/plat/packet_inlines.h>
-#include <odp/api/packet.h>
-
-#include <odp_packet_io_internal.h>
-#include <odp_packet_netmap.h>
-#include <odp_packet_socket.h>
-#include <odp_debug_internal.h>
-#include <protocols/eth.h>
-
-#include <sys/ioctl.h>
-#include <poll.h>
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-#include <odp_classification_datamodel.h>
-#include <odp_classification_inlines.h>
-#include <odp_classification_internal.h>
-
-#include <inttypes.h>
-
-/* Disable netmap debug prints */
-#ifndef ND
-#define ND(_fmt, ...) do {} while (0)
-#define D(_fmt, ...) do {} while (0)
-#define RD(lps, format, ...) do {} while (0)
-#endif
-
-#define NETMAP_WITH_LIBS
-#include <net/netmap_user.h>
-
-#define NM_WAIT_TIMEOUT 10 /* netmap_wait_for_link() timeout in seconds */
-#define NM_INJECT_RETRIES 10
-
-static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
-static int netmap_stats_reset(pktio_entry_t *pktio_entry);
-
-static int netmap_do_ioctl(pktio_entry_t *pktio_entry, unsigned long cmd,
- int subcmd)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- struct ethtool_value eval;
- struct ifreq ifr;
- int err;
- int fd = pkt_nm->sockfd;
-
- memset(&ifr, 0, sizeof(ifr));
- snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s",
- pktio_entry->s.pkt_nm.if_name);
-
- switch (cmd) {
- case SIOCSIFFLAGS:
- ifr.ifr_flags = pkt_nm->if_flags & 0xffff;
- break;
- case SIOCETHTOOL:
- eval.cmd = subcmd;
- eval.data = 0;
- ifr.ifr_data = (caddr_t)&eval;
- break;
- default:
- break;
- }
- err = ioctl(fd, cmd, &ifr);
- if (err)
- goto done;
-
- switch (cmd) {
- case SIOCGIFFLAGS:
- pkt_nm->if_flags = (ifr.ifr_flags << 16) |
- (0xffff & ifr.ifr_flags);
- break;
- case SIOCETHTOOL:
- if (subcmd == ETHTOOL_GLINK)
- return eval.data;
- break;
- default:
- break;
- }
-done:
- if (err)
- ODP_ERR("ioctl err %d %lu: %s\n", err, cmd, strerror(errno));
-
- return err;
-}
-
-/**
- * Map netmap rings to pktin/pktout queues
- *
- * @param rings Array of netmap descriptor rings
- * @param num_queues Number of pktin/pktout queues
- * @param num_rings Number of matching netmap rings
- */
-static inline void map_netmap_rings(netmap_ring_t *rings,
- unsigned num_queues, unsigned num_rings)
-{
- struct netmap_ring_t *desc_ring;
- unsigned rings_per_queue;
- unsigned remainder;
- unsigned mapped_rings;
- unsigned i;
- unsigned desc_id = 0;
-
- rings_per_queue = num_rings / num_queues;
- remainder = num_rings % num_queues;
-
- if (remainder)
- ODP_DBG("WARNING: Netmap rings mapped unevenly to queues\n");
-
- for (i = 0; i < num_queues; i++) {
- desc_ring = &rings[i].s;
- if (i < remainder)
- mapped_rings = rings_per_queue + 1;
- else
- mapped_rings = rings_per_queue;
-
- desc_ring->first = desc_id;
- desc_ring->cur = desc_id;
- desc_ring->last = desc_ring->first + mapped_rings - 1;
- desc_ring->num = mapped_rings;
-
- desc_id = desc_ring->last + 1;
- }
-}
-
-static int netmap_input_queues_config(pktio_entry_t *pktio_entry,
- const odp_pktin_queue_param_t *p)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- odp_pktin_mode_t mode = pktio_entry->s.param.in_mode;
- unsigned num_queues = p->num_queues;
- odp_bool_t lockless;
-
- /* Scheduler synchronizes input queue polls. Only single thread
- * at a time polls a queue */
- if (mode == ODP_PKTIN_MODE_SCHED)
- lockless = 1;
- else
- lockless = (p->op_mode == ODP_PKTIO_OP_MT_UNSAFE);
-
- if (p->hash_enable && num_queues > 1) {
- if (rss_conf_set_fd(pktio_entry->s.pkt_nm.sockfd,
- pktio_entry->s.pkt_nm.if_name,
- &p->hash_proto)) {
- ODP_ERR("Failed to configure input hash\n");
- return -1;
- }
- }
-
- pkt_nm->lockless_rx = lockless;
-
- return 0;
-}
-
-static int netmap_output_queues_config(pktio_entry_t *pktio_entry,
- const odp_pktout_queue_param_t *p)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
-
- pkt_nm->lockless_tx = (p->op_mode == ODP_PKTIO_OP_MT_UNSAFE);
-
- return 0;
-}
-
-/**
- * Close netmap descriptors
- *
- * Can be reopened using netmap_start() function.
- *
- * @param pktio_entry Packet IO entry
- */
-static inline void netmap_close_descriptors(pktio_entry_t *pktio_entry)
-{
- int i, j;
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
-
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
- for (j = 0; j < NM_MAX_DESC; j++) {
- if (pkt_nm->rx_desc_ring[i].s.desc[j] != NULL) {
- nm_close(pkt_nm->rx_desc_ring[i].s.desc[j]);
- pkt_nm->rx_desc_ring[i].s.desc[j] = NULL;
- }
- }
- for (j = 0; j < NM_MAX_DESC; j++) {
- if (pkt_nm->tx_desc_ring[i].s.desc[j] != NULL) {
- nm_close(pkt_nm->tx_desc_ring[i].s.desc[j]);
- pkt_nm->tx_desc_ring[i].s.desc[j] = NULL;
- }
- }
- }
-
- pkt_nm->num_rx_desc_rings = 0;
- pkt_nm->num_tx_desc_rings = 0;
-}
-
-static int netmap_close(pktio_entry_t *pktio_entry)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
-
- netmap_close_descriptors(pktio_entry);
-
- if (pkt_nm->sockfd != -1 && close(pkt_nm->sockfd) != 0) {
- __odp_errno = errno;
- ODP_ERR("close(sockfd): %s\n", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int netmap_link_status(pktio_entry_t *pktio_entry)
-{
- if (pktio_entry->s.pkt_nm.is_virtual)
- return 1;
-
- return link_status_fd(pktio_entry->s.pkt_nm.sockfd,
- pktio_entry->s.pkt_nm.if_name);
-}
-
-/**
- * Wait for netmap link to come up
- *
- * @param pktio_entry Packet IO entry
- *
- * @retval 1 link is up
- * @retval 0 link is down
- * @retval <0 on failure
- */
-static inline int netmap_wait_for_link(pktio_entry_t *pktio_entry)
-{
- int i;
- int ret;
-
- /* Wait for the link to come up */
- for (i = 0; i <= NM_WAIT_TIMEOUT; i++) {
- ret = netmap_link_status(pktio_entry);
- if (ret == -1)
- return -1;
- /* nm_open() causes the physical link to reset. When using a
- * direct attached loopback cable there may be a small delay
- * until the opposing end's interface comes back up again. In
- * this case without the additional sleep pktio validation
- * tests fail. */
- if (!pktio_entry->s.pkt_nm.is_virtual)
- sleep(1);
- if (ret == 1)
- return 1;
- }
- ODP_DBG("%s link is down\n", pktio_entry->s.pkt_nm.if_name);
- return 0;
-}
-
-/**
- * Initialize netmap capability values
- *
- * @param pktio_entry Packet IO entry
- */
-static void netmap_init_capability(pktio_entry_t *pktio_entry)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- odp_pktio_capability_t *capa = &pkt_nm->capa;
-
- memset(&pkt_nm->capa, 0, sizeof(odp_pktio_capability_t));
-
- capa->max_input_queues = PKTIO_MAX_QUEUES;
- if (pkt_nm->num_rx_rings < PKTIO_MAX_QUEUES)
- capa->max_input_queues = pkt_nm->num_rx_rings;
- if (capa->max_input_queues > NM_MAX_DESC) {
- /* Have to use a single descriptor to fetch packets from all
- * netmap rings */
- capa->max_input_queues = 1;
- ODP_DBG("Unable to store all %" PRIu32 " rx rings (max %d)\n"
- " max input queues: %u\n", pkt_nm->num_rx_rings,
- NM_MAX_DESC, capa->max_input_queues);
- }
-
- capa->max_output_queues = PKTIO_MAX_QUEUES;
- if (pkt_nm->num_tx_rings < PKTIO_MAX_QUEUES)
- capa->max_output_queues = pkt_nm->num_tx_rings;
- if (capa->max_output_queues > NM_MAX_DESC) {
- capa->max_output_queues = NM_MAX_DESC;
- ODP_DBG("Unable to store all %" PRIu32 " tx rings (max %d)\n"
- " max output queues: %u\n", pkt_nm->num_tx_rings,
- NM_MAX_DESC, capa->max_output_queues);
- }
-
- capa->set_op.op.promisc_mode = 1;
-
- odp_pktio_config_init(&capa->config);
- capa->config.pktin.bit.ts_all = 1;
- capa->config.pktin.bit.ts_ptp = 1;
-}
-
-/**
- * Open a netmap interface
- *
- * In addition to standard interfaces (with or without modified netmap drivers)
- * virtual VALE and pipe interfaces are also supported. These can be used for
- * example for testing packet IO functionality without any physical interfaces.
- *
- * To use virtual interfaces the 'netdev' device name has to begin with 'vale'
- * prefix. A valid VALE device name would be e.g. 'vale0'. Pipe device names
- * have to include also '{NN' (master) or '}NN' (slave) suffix. A valid pipe
- * master would be e.g. 'vale0{0' and a slave to the same pipe 'vale0}0'.
- *
- * Netmap requires standard interface names to begin with 'netmap:' prefix.
- * netmap_open() adds the prefix if it is missing. Virtual interfaces don't
- * require the 'netmap:' prefix.
- *
- * @param id Packet IO handle
- * @param pktio_entry Packet IO entry
- * @param netdev Packet IO device name
- * @param pool Default pool from which to allocate storage for packets
- *
- * @retval 0 on success
- * @retval <0 on failure
- */
-static int netmap_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
- const char *netdev, odp_pool_t pool)
-{
- int i;
- int err;
- int sockfd;
- const char *prefix;
- uint32_t mtu;
- uint32_t buf_size;
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- struct nm_desc *desc;
- struct netmap_ring *ring;
- odp_pktin_hash_proto_t hash_proto;
- odp_pktio_stats_t cur_stats;
-
- if (disable_pktio)
- return -1;
-
- if (pool == ODP_POOL_INVALID)
- return -1;
-
- /* Init pktio entry */
- memset(pkt_nm, 0, sizeof(*pkt_nm));
- pkt_nm->sockfd = -1;
- pkt_nm->pool = pool;
-
- /* max frame len taking into account the l2-offset */
- pkt_nm->max_frame_len = CONFIG_PACKET_MAX_SEG_LEN;
-
- /* allow interface to be opened with or without the 'netmap:' prefix */
- prefix = "netmap:";
- if (strncmp(netdev, "netmap:", 7) == 0)
- netdev += 7;
- if (strncmp(netdev, "vale", 4) == 0) {
- pkt_nm->is_virtual = 1;
- prefix = "";
- }
-
- snprintf(pkt_nm->nm_name, sizeof(pkt_nm->nm_name), "%s%s", prefix,
- netdev);
- snprintf(pkt_nm->if_name, sizeof(pkt_nm->if_name), "%s", netdev);
-
- /* Dummy open here to check if netmap module is available and to read
- * capability info. */
- desc = nm_open(pkt_nm->nm_name, NULL, 0, NULL);
- if (desc == NULL) {
- ODP_ERR("nm_open(%s) failed\n", pkt_nm->nm_name);
- goto error;
- }
- pkt_nm->num_rx_rings = desc->nifp->ni_rx_rings;
- pkt_nm->num_tx_rings = desc->nifp->ni_tx_rings;
-
- netmap_init_capability(pktio_entry);
-
- ring = NETMAP_RXRING(desc->nifp, desc->cur_rx_ring);
- buf_size = ring->nr_buf_size;
- nm_close(desc);
-
- for (i = 0; i < PKTIO_MAX_QUEUES; i++) {
- odp_ticketlock_init(&pkt_nm->rx_desc_ring[i].s.lock);
- odp_ticketlock_init(&pkt_nm->tx_desc_ring[i].s.lock);
- }
-
- if (pkt_nm->is_virtual) {
- static unsigned mac;
-
- pkt_nm->capa.max_input_queues = 1;
- pkt_nm->capa.set_op.op.promisc_mode = 0;
- pkt_nm->mtu = buf_size;
- pktio_entry->s.stats_type = STATS_UNSUPPORTED;
- /* Set MAC address for virtual interface */
- pkt_nm->if_mac[0] = 0x2;
- pkt_nm->if_mac[5] = ++mac;
-
- return 0;
- }
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd == -1) {
- ODP_ERR("Cannot get device control socket\n");
- goto error;
- }
- pkt_nm->sockfd = sockfd;
-
- /* Use either interface MTU (+ ethernet header length) or netmap buffer
- * size as MTU, whichever is smaller. */
- mtu = mtu_get_fd(pktio_entry->s.pkt_nm.sockfd, pkt_nm->if_name);
- if (mtu == 0) {
- ODP_ERR("Unable to read interface MTU\n");
- goto error;
- }
- mtu += _ODP_ETHHDR_LEN;
- pkt_nm->mtu = (mtu < buf_size) ? mtu : buf_size;
-
- /* Check if RSS is supported. If not, set 'max_input_queues' to 1. */
- if (rss_conf_get_supported_fd(sockfd, netdev, &hash_proto) == 0) {
- ODP_DBG("RSS not supported\n");
- pkt_nm->capa.max_input_queues = 1;
- }
-
- err = netmap_do_ioctl(pktio_entry, SIOCGIFFLAGS, 0);
- if (err)
- goto error;
- if ((pkt_nm->if_flags & IFF_UP) == 0)
- ODP_DBG("%s is down\n", pkt_nm->if_name);
-
- err = mac_addr_get_fd(sockfd, netdev, pkt_nm->if_mac);
- if (err)
- goto error;
-
- /* netmap uses only ethtool to get statistics counters */
- err = ethtool_stats_get_fd(pktio_entry->s.pkt_nm.sockfd,
- pkt_nm->if_name, &cur_stats);
- if (err) {
- ODP_ERR("netmap pktio %s does not support statistics counters\n",
- pkt_nm->if_name);
- pktio_entry->s.stats_type = STATS_UNSUPPORTED;
- } else {
- pktio_entry->s.stats_type = STATS_ETHTOOL;
- }
-
- (void)netmap_stats_reset(pktio_entry);
-
- return 0;
-
-error:
- netmap_close(pktio_entry);
- return -1;
-}
-
-static int netmap_start(pktio_entry_t *pktio_entry)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- netmap_ring_t *desc_ring;
- struct nm_desc *desc_ptr;
- unsigned i;
- unsigned j;
- unsigned num_rx_desc = 0;
- uint64_t flags;
- odp_pktin_mode_t in_mode = pktio_entry->s.param.in_mode;
- odp_pktout_mode_t out_mode = pktio_entry->s.param.out_mode;
-
- /* If no pktin/pktout queues have been configured. Configure one
- * for each direction. */
- if (!pktio_entry->s.num_in_queue &&
- in_mode != ODP_PKTIN_MODE_DISABLED) {
- odp_pktin_queue_param_t param;
-
- odp_pktin_queue_param_init(&param);
- param.num_queues = 1;
- if (odp_pktin_queue_config(pktio_entry->s.handle, &param))
- return -1;
- }
- if (!pktio_entry->s.num_out_queue &&
- out_mode == ODP_PKTOUT_MODE_DIRECT) {
- odp_pktout_queue_param_t param;
-
- odp_pktout_queue_param_init(&param);
- param.num_queues = 1;
- if (odp_pktout_queue_config(pktio_entry->s.handle, &param))
- return -1;
- }
-
- if (pkt_nm->num_rx_desc_rings == pktio_entry->s.num_in_queue &&
- pkt_nm->num_tx_desc_rings == pktio_entry->s.num_out_queue)
- return (netmap_wait_for_link(pktio_entry) == 1) ? 0 : -1;
-
- netmap_close_descriptors(pktio_entry);
-
- /* Map pktin/pktout queues to netmap rings */
- if (pktio_entry->s.num_in_queue) {
- /* In single queue case only one netmap descriptor is
- * required. */
- num_rx_desc = (pktio_entry->s.num_in_queue == 1) ? 1 :
- pkt_nm->num_rx_rings;
-
- map_netmap_rings(pkt_nm->rx_desc_ring,
- pktio_entry->s.num_in_queue, num_rx_desc);
- }
- if (pktio_entry->s.num_out_queue)
- /* Enough to map only one netmap tx ring per pktout queue */
- map_netmap_rings(pkt_nm->tx_desc_ring,
- pktio_entry->s.num_out_queue,
- pktio_entry->s.num_out_queue);
-
- /* Use nm_open() to parse netmap flags from interface name */
- desc_ptr = nm_open(pkt_nm->nm_name, NULL, 0, NULL);
- if (desc_ptr == NULL) {
- ODP_ERR("nm_start(%s) failed\n", pkt_nm->nm_name);
- goto error;
- }
- struct nm_desc base_desc = *desc_ptr;
-
- nm_close(desc_ptr);
-
- base_desc.self = &base_desc;
- base_desc.mem = NULL;
- base_desc.req.nr_ringid = 0;
- if ((base_desc.req.nr_flags & NR_REG_MASK) == NR_REG_ALL_NIC ||
- (base_desc.req.nr_flags & NR_REG_MASK) == NR_REG_ONE_NIC) {
- base_desc.req.nr_flags &= ~NR_REG_MASK;
- if (num_rx_desc == 1)
- base_desc.req.nr_flags |= NR_REG_ALL_NIC;
- else
- base_desc.req.nr_flags |= NR_REG_ONE_NIC;
- }
-
- /* Only the first rx descriptor does mmap */
- desc_ring = pkt_nm->rx_desc_ring;
- flags = NM_OPEN_IFNAME | NETMAP_NO_TX_POLL;
- desc_ring[0].s.desc[0] = nm_open(pkt_nm->nm_name, NULL, flags,
- &base_desc);
- if (desc_ring[0].s.desc[0] == NULL) {
- ODP_ERR("nm_start(%s) failed\n", pkt_nm->nm_name);
- goto error;
- }
- /* Open rest of the rx descriptors (one per netmap ring) */
- flags = NM_OPEN_IFNAME | NETMAP_NO_TX_POLL | NM_OPEN_NO_MMAP;
- for (i = 0; i < pktio_entry->s.num_in_queue; i++) {
- for (j = desc_ring[i].s.first; j <= desc_ring[i].s.last; j++) {
- if (i == 0 && j == 0) { /* First already opened */
- if (num_rx_desc > 1)
- continue;
- else
- break;
- }
- base_desc.req.nr_ringid = j;
- desc_ring[i].s.desc[j] = nm_open(pkt_nm->nm_name, NULL,
- flags, &base_desc);
- if (desc_ring[i].s.desc[j] == NULL) {
- ODP_ERR("nm_start(%s) failed\n",
- pkt_nm->nm_name);
- goto error;
- }
- }
- }
- /* Open tx descriptors */
- desc_ring = pkt_nm->tx_desc_ring;
- flags = NM_OPEN_IFNAME | NM_OPEN_NO_MMAP;
-
- if ((base_desc.req.nr_flags & NR_REG_MASK) == NR_REG_ALL_NIC) {
- base_desc.req.nr_flags &= ~NR_REG_ALL_NIC;
- base_desc.req.nr_flags |= NR_REG_ONE_NIC;
- }
-
- for (i = 0; i < pktio_entry->s.num_out_queue; i++) {
- for (j = desc_ring[i].s.first; j <= desc_ring[i].s.last; j++) {
- base_desc.req.nr_ringid = j;
- desc_ring[i].s.desc[j] = nm_open(pkt_nm->nm_name, NULL,
- flags, &base_desc);
- if (desc_ring[i].s.desc[j] == NULL) {
- ODP_ERR("nm_start(%s) failed\n",
- pkt_nm->nm_name);
- goto error;
- }
- }
- }
- pkt_nm->num_rx_desc_rings = pktio_entry->s.num_in_queue;
- pkt_nm->num_tx_desc_rings = pktio_entry->s.num_out_queue;
- /* Wait for the link to come up */
- return (netmap_wait_for_link(pktio_entry) == 1) ? 0 : -1;
-
-error:
- netmap_close_descriptors(pktio_entry);
- return -1;
-}
-
-static int netmap_stop(pktio_entry_t *pktio_entry ODP_UNUSED)
-{
- return 0;
-}
-
-/**
- * Create ODP packets from netmap packets
- *
- * @param pktio_entry Packet IO entry
- * @param pkt_tbl Array for new ODP packet handles
- * @param slot_tbl Array of netmap ring slots
- * @param slot_num Number of netmap ring slots
- * @param ts Pointer to pktin timestamp
- *
- * @retval Number of created packets
- */
-static inline int netmap_pkt_to_odp(pktio_entry_t *pktio_entry,
- odp_packet_t pkt_tbl[],
- netmap_slot_t slot_tbl[], int16_t slot_num,
- odp_time_t *ts)
-{
- odp_packet_t pkt;
- odp_pool_t pool = pktio_entry->s.pkt_nm.pool;
- odp_packet_hdr_t *pkt_hdr;
- odp_packet_hdr_t parsed_hdr;
- int i;
- int num;
- int alloc_len;
-
- /* Allocate maximum sized packets */
- alloc_len = pktio_entry->s.pkt_nm.mtu;
-
- num = packet_alloc_multi(pool, alloc_len, pkt_tbl, slot_num);
-
- for (i = 0; i < num; i++) {
- netmap_slot_t slot;
- uint16_t len;
-
- slot = slot_tbl[i];
- len = slot.len;
-
- odp_prefetch(slot.buf);
-
- if (odp_unlikely(len > pktio_entry->s.pkt_nm.max_frame_len)) {
- ODP_ERR("RX: frame too big %" PRIu16 " %zu!\n", len,
- pktio_entry->s.pkt_nm.max_frame_len);
- goto fail;
- }
-
- if (odp_unlikely(len < _ODP_ETH_LEN_MIN)) {
- ODP_ERR("RX: Frame truncated: %" PRIu16 "\n", len);
- goto fail;
- }
-
- if (pktio_cls_enabled(pktio_entry)) {
- if (cls_classify_packet(pktio_entry,
- (const uint8_t *)slot.buf, len,
- len, &pool, &parsed_hdr))
- goto fail;
- }
-
- pkt = pkt_tbl[i];
- pkt_hdr = odp_packet_hdr(pkt);
- pull_tail(pkt_hdr, alloc_len - len);
-
- /* For now copy the data in the mbuf,
- worry about zero-copy later */
- if (odp_packet_copy_from_mem(pkt, 0, len, slot.buf) != 0)
- goto fail;
-
- pkt_hdr->input = pktio_entry->s.handle;
-
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, pkt_hdr);
- else
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
-
- packet_set_ts(pkt_hdr, ts);
- }
-
- return i;
-
-fail:
- odp_packet_free_multi(&pkt_tbl[i], num - i);
- return i;
-}
-
-static inline int netmap_recv_desc(pktio_entry_t *pktio_entry,
- struct nm_desc *desc,
- odp_packet_t pkt_table[], int num)
-{
- struct netmap_ring *ring;
- odp_time_t ts_val;
- odp_time_t *ts = NULL;
- netmap_slot_t slot_tbl[num];
- char *buf;
- uint32_t slot_id;
- int i;
- int ring_id = desc->cur_rx_ring;
- int num_rx = 0;
- int num_rings = desc->last_rx_ring - desc->first_rx_ring + 1;
-
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp)
- ts = &ts_val;
-
- for (i = 0; i < num_rings && num_rx != num; i++) {
- if (ring_id > desc->last_rx_ring)
- ring_id = desc->first_rx_ring;
-
- ring = NETMAP_RXRING(desc->nifp, ring_id);
-
- while (!nm_ring_empty(ring) && num_rx != num) {
- slot_id = ring->cur;
- buf = NETMAP_BUF(ring, ring->slot[slot_id].buf_idx);
-
- slot_tbl[num_rx].buf = buf;
- slot_tbl[num_rx].len = ring->slot[slot_id].len;
- num_rx++;
-
- ring->cur = nm_ring_next(ring, slot_id);
- }
- ring->head = ring->cur;
- ring_id++;
- }
- desc->cur_rx_ring = ring_id;
-
- if (num_rx) {
- if (ts != NULL)
- ts_val = odp_time_global();
- return netmap_pkt_to_odp(pktio_entry, pkt_table, slot_tbl,
- num_rx, ts);
- }
- return 0;
-}
-
-static int netmap_recv(pktio_entry_t *pktio_entry, int index,
- odp_packet_t pkt_table[], int num)
-{
- struct nm_desc *desc;
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- unsigned first_desc_id = pkt_nm->rx_desc_ring[index].s.first;
- unsigned last_desc_id = pkt_nm->rx_desc_ring[index].s.last;
- unsigned desc_id;
- int num_desc = pkt_nm->rx_desc_ring[index].s.num;
- int i;
- int num_rx = 0;
- int max_fd = 0;
- fd_set empty_rings;
-
- if (odp_unlikely(pktio_entry->s.state != PKTIO_STATE_STARTED))
- return 0;
-
- FD_ZERO(&empty_rings);
-
- if (!pkt_nm->lockless_rx)
- odp_ticketlock_lock(&pkt_nm->rx_desc_ring[index].s.lock);
-
- desc_id = pkt_nm->rx_desc_ring[index].s.cur;
-
- for (i = 0; i < num_desc && num_rx != num; i++) {
- if (desc_id > last_desc_id)
- desc_id = first_desc_id;
-
- desc = pkt_nm->rx_desc_ring[index].s.desc[desc_id];
-
- num_rx += netmap_recv_desc(pktio_entry, desc,
- &pkt_table[num_rx], num - num_rx);
-
- if (num_rx != num) {
- FD_SET(desc->fd, &empty_rings);
- if (desc->fd > max_fd)
- max_fd = desc->fd;
- }
- desc_id++;
- }
- pkt_nm->rx_desc_ring[index].s.cur = desc_id;
-
- if (num_rx != num) {
- struct timeval tout = {.tv_sec = 0, .tv_usec = 0};
-
- if (select(max_fd + 1, &empty_rings, NULL, NULL, &tout) == -1)
- ODP_ERR("RX: select error\n");
- }
- if (!pkt_nm->lockless_rx)
- odp_ticketlock_unlock(&pkt_nm->rx_desc_ring[index].s.lock);
-
- return num_rx;
-}
-
-static int netmap_send(pktio_entry_t *pktio_entry, int index,
- const odp_packet_t pkt_table[], int num)
-{
- pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
- struct pollfd polld;
- struct nm_desc *desc;
- struct netmap_ring *ring;
- int i;
- int nb_tx;
- int desc_id;
- odp_packet_t pkt;
- uint32_t pkt_len;
- unsigned slot_id;
- char *buf;
-
- if (odp_unlikely(pktio_entry->s.state != PKTIO_STATE_STARTED))
- return 0;
-
- /* Only one netmap tx ring per pktout queue */
- desc_id = pkt_nm->tx_desc_ring[index].s.cur;
- desc = pkt_nm->tx_desc_ring[index].s.desc[desc_id];
- ring = NETMAP_TXRING(desc->nifp, desc->cur_tx_ring);
-
- if (!pkt_nm->lockless_tx)
- odp_ticketlock_lock(&pkt_nm->tx_desc_ring[index].s.lock);
-
- polld.fd = desc->fd;
- polld.events = POLLOUT;
-
- for (nb_tx = 0; nb_tx < num; nb_tx++) {
- pkt = pkt_table[nb_tx];
- pkt_len = _odp_packet_len(pkt);
-
- if (pkt_len > pkt_nm->mtu) {
- if (nb_tx == 0)
- __odp_errno = EMSGSIZE;
- break;
- }
- for (i = 0; i < NM_INJECT_RETRIES; i++) {
- if (nm_ring_empty(ring)) {
- poll(&polld, 1, 0);
- continue;
- }
- slot_id = ring->cur;
- ring->slot[slot_id].flags = 0;
- ring->slot[slot_id].len = pkt_len;
-
- buf = NETMAP_BUF(ring, ring->slot[slot_id].buf_idx);
-
- if (odp_packet_copy_to_mem(pkt, 0, pkt_len, buf)) {
- i = NM_INJECT_RETRIES;
- break;
- }
- ring->cur = nm_ring_next(ring, slot_id);
- ring->head = ring->cur;
- break;
- }
- if (i == NM_INJECT_RETRIES)
- break;
- }
- /* Send pending packets */
- poll(&polld, 1, 0);
-
- if (!pkt_nm->lockless_tx)
- odp_ticketlock_unlock(&pkt_nm->tx_desc_ring[index].s.lock);
-
- if (odp_unlikely(nb_tx == 0)) {
- if (__odp_errno != 0)
- return -1;
- } else {
- odp_packet_free_multi(pkt_table, nb_tx);
- }
-
- return nb_tx;
-}
-
-static int netmap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
-{
- memcpy(mac_addr, pktio_entry->s.pkt_nm.if_mac, ETH_ALEN);
- return ETH_ALEN;
-}
-
-static uint32_t netmap_mtu_get(pktio_entry_t *pktio_entry)
-{
- return pktio_entry->s.pkt_nm.mtu;
-}
-
-static int netmap_promisc_mode_set(pktio_entry_t *pktio_entry,
- odp_bool_t enable)
-{
- if (pktio_entry->s.pkt_nm.is_virtual) {
- __odp_errno = ENOTSUP;
- return -1;
- }
-
- return promisc_mode_set_fd(pktio_entry->s.pkt_nm.sockfd,
- pktio_entry->s.pkt_nm.if_name, enable);
-}
-
-static int netmap_promisc_mode_get(pktio_entry_t *pktio_entry)
-{
- if (pktio_entry->s.pkt_nm.is_virtual)
- return 0;
-
- return promisc_mode_get_fd(pktio_entry->s.pkt_nm.sockfd,
- pktio_entry->s.pkt_nm.if_name);
-}
-
-static int netmap_capability(pktio_entry_t *pktio_entry,
- odp_pktio_capability_t *capa)
-{
- *capa = pktio_entry->s.pkt_nm.capa;
- return 0;
-}
-
-static int netmap_stats(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats)
-{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(stats, 0, sizeof(*stats));
- return 0;
- }
-
- return sock_stats_fd(pktio_entry,
- stats,
- pktio_entry->s.pkt_nm.sockfd);
-}
-
-static int netmap_stats_reset(pktio_entry_t *pktio_entry)
-{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(&pktio_entry->s.stats, 0,
- sizeof(odp_pktio_stats_t));
- return 0;
- }
-
- return sock_stats_reset_fd(pktio_entry,
- pktio_entry->s.pkt_nm.sockfd);
-}
-
-static void netmap_print(pktio_entry_t *pktio_entry)
-{
- odp_pktin_hash_proto_t hash_proto;
-
- if (rss_conf_get_fd(pktio_entry->s.pkt_nm.sockfd,
- pktio_entry->s.pkt_nm.if_name, &hash_proto))
- rss_conf_print(&hash_proto);
-}
-
-static int netmap_init_global(void)
-{
- if (getenv("ODP_PKTIO_DISABLE_NETMAP")) {
- ODP_PRINT("PKTIO: netmap pktio skipped,"
- " enabled export ODP_PKTIO_DISABLE_NETMAP=1.\n");
- disable_pktio = 1;
- } else {
- ODP_PRINT("PKTIO: initialized netmap pktio,"
- " use export ODP_PKTIO_DISABLE_NETMAP=1 to disable.\n"
- " Netmap prefixes are netmap:eth0 or vale:eth0. Refer to"
- " Netmap documentation for usage information.\n");
- }
- return 0;
-}
-
-const pktio_if_ops_t netmap_pktio_ops = {
- .name = "netmap",
- .print = netmap_print,
- .init_global = netmap_init_global,
- .init_local = NULL,
- .term = NULL,
- .open = netmap_open,
- .close = netmap_close,
- .start = netmap_start,
- .stop = netmap_stop,
- .link_status = netmap_link_status,
- .stats = netmap_stats,
- .stats_reset = netmap_stats_reset,
- .mtu_get = netmap_mtu_get,
- .promisc_mode_set = netmap_promisc_mode_set,
- .promisc_mode_get = netmap_promisc_mode_get,
- .mac_get = netmap_mac_addr_get,
- .capability = netmap_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
- .config = NULL,
- .input_queues_config = netmap_input_queues_config,
- .output_queues_config = netmap_output_queues_config,
- .recv = netmap_recv,
- .send = netmap_send
-};
-
-#endif /* ODP_NETMAP */
diff --git a/platform/linux-generic/pktio/null.c b/platform/linux-generic/pktio/null.c
new file mode 100644
index 000000000..862969415
--- /dev/null
+++ b/platform/linux-generic/pktio/null.c
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
+
+#include <odp_packet_io_internal.h>
+
+#include <stdint.h>
+
+static int null_close(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ return 0;
+}
+
+static int null_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry ODP_UNUSED,
+ const char *devname, odp_pool_t pool ODP_UNUSED)
+{
+ if (strncmp(devname, "null:", 5) != 0)
+ return -1;
+
+ return 0;
+}
+
+static int null_recv(pktio_entry_t *pktio_entry ODP_UNUSED,
+ int index ODP_UNUSED, odp_packet_t pkt_table[] ODP_UNUSED,
+ int len ODP_UNUSED)
+{
+ return 0;
+}
+
+static int null_fd_set(pktio_entry_t *pktio_entry ODP_UNUSED,
+ int index ODP_UNUSED, fd_set *readfds ODP_UNUSED)
+{
+ return 0;
+}
+
+static int null_recv_tmo(pktio_entry_t *pktio_entry ODP_UNUSED,
+ int index ODP_UNUSED,
+ odp_packet_t pkt_table[] ODP_UNUSED,
+ int num ODP_UNUSED, uint64_t usecs)
+{
+ struct timeval timeout;
+ int maxfd = -1;
+ fd_set readfds;
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+ FD_ZERO(&readfds);
+
+ select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+
+ return 0;
+}
+
+static int null_recv_mq_tmo(pktio_entry_t *pktio_entry[] ODP_UNUSED,
+ int index[] ODP_UNUSED, uint32_t num_q ODP_UNUSED,
+ odp_packet_t pkt_table[] ODP_UNUSED,
+ int num ODP_UNUSED, uint32_t *from ODP_UNUSED,
+ uint64_t usecs)
+{
+ struct timeval timeout;
+ int maxfd = -1;
+ fd_set readfds;
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ FD_ZERO(&readfds);
+
+ select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+
+ return 0;
+}
+
+static int null_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
+ const odp_packet_t pkt_table[], int num)
+{
+ odp_bool_t set_tx_ts = false;
+
+ if (_odp_pktio_tx_ts_enabled(pktio_entry)) {
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (odp_unlikely(packet_hdr(pkt_table[i])->p.flags.ts_set)) {
+ set_tx_ts = true;
+ break;
+ }
+ }
+ }
+
+ odp_packet_free_multi(pkt_table, num);
+
+ if (odp_unlikely(set_tx_ts))
+ _odp_pktio_tx_ts_set(pktio_entry);
+
+ return num;
+}
+
+#define PKTIO_NULL_MTU (64 * 1024)
+
+static uint32_t null_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ return PKTIO_NULL_MTU;
+}
+
+static const uint8_t null_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x05};
+
+static int null_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
+ void *mac_addr)
+{
+ memcpy(mac_addr, null_mac, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+static int null_promisc_mode_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ /* Promisc mode disabled. Mode does not matter, as packet input does not
+ * return any packets.*/
+ return 0;
+}
+
+static int null_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
+ odp_pktio_capability_t *capa)
+{
+ memset(capa, 0, sizeof(odp_pktio_capability_t));
+
+ capa->max_input_queues = ODP_PKTIN_MAX_QUEUES;
+ capa->max_output_queues = ODP_PKTOUT_MAX_QUEUES;
+ capa->set_op.op.promisc_mode = 0;
+
+ odp_pktio_config_init(&capa->config);
+ capa->config.pktin.bit.ts_all = 1;
+ capa->config.pktin.bit.ts_ptp = 1;
+
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+
+ return 0;
+}
+
+static int null_inqueues_config(pktio_entry_t *pktio_entry ODP_UNUSED,
+ const odp_pktin_queue_param_t *p ODP_UNUSED)
+{
+ return 0;
+}
+
+static int null_outqueues_config(pktio_entry_t *pktio_entry ODP_UNUSED,
+ const odp_pktout_queue_param_t *p ODP_UNUSED)
+{
+ return 0;
+}
+
+static int null_init_global(void)
+{
+ _ODP_PRINT("PKTIO: initialized null interface.\n");
+ return 0;
+}
+
+static int null_link_status(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ return ODP_PKTIO_LINK_STATUS_UP;
+}
+
+static int null_link_info(pktio_entry_t *pktio_entry ODP_UNUSED, odp_pktio_link_info_t *info)
+{
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ info->media = "virtual";
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ info->status = ODP_PKTIO_LINK_STATUS_UP;
+
+ return 0;
+}
+
+const pktio_if_ops_t _odp_null_pktio_ops = {
+ .name = "null",
+ .print = NULL,
+ .init_global = null_init_global,
+ .init_local = NULL,
+ .term = NULL,
+ .open = null_open,
+ .close = null_close,
+ .start = NULL,
+ .stop = NULL,
+ .recv = null_recv,
+ .recv_tmo = null_recv_tmo,
+ .recv_mq_tmo = null_recv_mq_tmo,
+ .fd_set = null_fd_set,
+ .send = null_send,
+ .maxlen_get = null_mtu_get,
+ .promisc_mode_set = NULL,
+ .promisc_mode_get = null_promisc_mode_get,
+ .mac_get = null_mac_addr_get,
+ .capability = null_capability,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
+ .config = NULL,
+ .input_queues_config = null_inqueues_config,
+ .output_queues_config = null_outqueues_config,
+ .link_status = null_link_status,
+ .link_info = null_link_info
+};
diff --git a/platform/linux-generic/pktio/pcap.c b/platform/linux-generic/pktio/pcap.c
index a467b6402..02b4d467a 100644
--- a/platform/linux-generic/pktio/pcap.c
+++ b/platform/linux-generic/pktio/pcap.c
@@ -1,7 +1,6 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
*/
/**
@@ -29,14 +28,26 @@
* doesn't exist it will be created, if it does exist it will
* be overwritten.
* loops the number of times to iterate through the input file, set
- * to 0 to loop indefinitely. The default value is 1.
+ * to 0 to loop indefinitely. The default value is 1. Looping is
+ * only supported in thread mode (ODP_MEM_MODEL_THREAD).
*
* The total length of the string is limited by PKTIO_NAME_LEN.
*/
#include <odp_posix_extensions.h>
-#include <odp_api.h>
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/packet_inlines.h>
+
+#include <odp_parse_internal.h>
+#include <odp_classification_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_global_data.h>
#include <odp_packet_internal.h>
#include <odp_packet_io_internal.h>
@@ -45,9 +56,34 @@
#include <errno.h>
#include <pcap/pcap.h>
#include <pcap/bpf.h>
+#include <stdint.h>
+
+typedef struct {
+ char *fname_rx; /**< name of pcap file for rx */
+ char *fname_tx; /**< name of pcap file for tx */
+ void *rx; /**< rx pcap handle */
+ void *tx; /**< tx pcap handle */
+ void *tx_dump; /**< tx pcap dumper handle */
+ odp_pool_t pool; /**< rx pool */
+ uint32_t mtu; /**< link MTU */
+ int loops; /**< number of times to loop rx pcap */
+ int loop_cnt; /**< number of loops completed */
+ odp_bool_t promisc; /**< promiscuous mode state */
+} pkt_pcap_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_pcap_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static inline pkt_pcap_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_pcap_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
+
+#define PKTIO_PCAP_MTU_MIN (68 + _ODP_ETHHDR_LEN)
+#define PKTIO_PCAP_MTU_MAX (64 * 1024)
-#define PKTIO_PCAP_MTU (64 * 1024)
-static const char pcap_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x04};
+/* Dummy MAC address used in .pcap test files, when not in promisc mode */
+static const uint8_t pcap_mac[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x02};
static int pcapif_stats_reset(pktio_entry_t *pktio_entry);
@@ -71,7 +107,7 @@ static int _pcapif_parse_devname(pkt_pcap_t *pcap, const char *devname)
} else if (strncmp(tok, "loops=", 6) == 0) {
pcap->loops = atoi(tok + 6);
if (pcap->loops < 0) {
- ODP_ERR("invalid loop count\n");
+ _ODP_ERR("invalid loop count\n");
return -1;
}
}
@@ -87,14 +123,13 @@ static int _pcapif_init_rx(pkt_pcap_t *pcap)
pcap->rx = pcap_open_offline(pcap->fname_rx, errbuf);
if (!pcap->rx) {
- ODP_ERR("failed to open pcap file %s (%s)\n",
- pcap->fname_rx, errbuf);
+ _ODP_ERR("failed to open pcap file %s (%s)\n", pcap->fname_rx, errbuf);
return -1;
}
linktype = pcap_datalink(pcap->rx);
if (linktype != DLT_EN10MB) {
- ODP_ERR("unsupported datalink type: %d\n", linktype);
+ _ODP_ERR("unsupported datalink type: %d\n", linktype);
return -1;
}
@@ -108,42 +143,78 @@ static int _pcapif_init_tx(pkt_pcap_t *pcap)
if (!tx) {
/* if there is no rx pcap_t already open for rx, a dummy
* one needs to be opened for writing the dump */
- tx = pcap_open_dead(DLT_EN10MB, PKTIO_PCAP_MTU);
+ tx = pcap_open_dead(DLT_EN10MB, PKTIO_PCAP_MTU_MAX);
if (!tx) {
- ODP_ERR("failed to open TX dump\n");
+ _ODP_ERR("failed to open TX dump\n");
return -1;
}
pcap->tx = tx;
}
- pcap->buf = malloc(PKTIO_PCAP_MTU);
- if (!pcap->buf) {
- ODP_ERR("failed to malloc temp buffer\n");
- return -1;
- }
-
pcap->tx_dump = pcap_dump_open(tx, pcap->fname_tx);
if (!pcap->tx_dump) {
- ODP_ERR("failed to open dump file %s (%s)\n",
- pcap->fname_tx, pcap_geterr(tx));
+ _ODP_ERR("failed to open dump file %s (%s)\n", pcap->fname_tx, pcap_geterr(tx));
return -1;
}
return pcap_dump_flush(pcap->tx_dump);
}
+static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry,
+ odp_bool_t enable)
+{
+ char filter_exp[64] = {0};
+ struct bpf_program bpf;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
+
+ if (!pcap->rx) {
+ pcap->promisc = enable;
+ return 0;
+ }
+
+ if (!enable) {
+ char mac_str[18];
+
+ snprintf(mac_str, sizeof(mac_str),
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ pcap_mac[0], pcap_mac[1], pcap_mac[2],
+ pcap_mac[3], pcap_mac[4], pcap_mac[5]);
+
+ snprintf(filter_exp, sizeof(filter_exp),
+ "ether dst %s or broadcast or multicast",
+ mac_str);
+ }
+
+ if (pcap_compile(pcap->rx, &bpf, filter_exp,
+ 0, PCAP_NETMASK_UNKNOWN) != 0) {
+ _ODP_ERR("failed to compile promisc mode filter: %s\n", pcap_geterr(pcap->rx));
+ return -1;
+ }
+
+ if (pcap_setfilter(pcap->rx, &bpf) != 0) {
+ pcap_freecode(&bpf);
+ _ODP_ERR("failed to set promisc mode filter: %s\n", pcap_geterr(pcap->rx));
+ return -1;
+ }
+
+ pcap_freecode(&bpf);
+ pcap->promisc = enable;
+
+ return 0;
+}
+
static int pcapif_init(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
const char *devname, odp_pool_t pool)
{
- pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
int ret;
memset(pcap, 0, sizeof(pkt_pcap_t));
pcap->loop_cnt = 1;
pcap->loops = 1;
pcap->pool = pool;
- pcap->promisc = 1;
+ pcap->mtu = PKTIO_PCAP_MTU_MAX;
ret = _pcapif_parse_devname(pcap, devname);
@@ -158,12 +229,15 @@ static int pcapif_init(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
(void)pcapif_stats_reset(pktio_entry);
+ if (pcapif_promisc_mode_set(pktio_entry, 0))
+ ret = -1;
+
return ret;
}
static int pcapif_close(pktio_entry_t *pktio_entry)
{
- pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
if (pcap->tx_dump)
pcap_dump_close(pcap->tx_dump);
@@ -174,7 +248,6 @@ static int pcapif_close(pktio_entry_t *pktio_entry)
if (pcap->rx)
pcap_close(pcap->rx);
- free(pcap->buf);
free(pcap->fname_rx);
free(pcap->fname_tx);
@@ -185,6 +258,10 @@ static int _pcapif_reopen(pkt_pcap_t *pcap)
{
char errbuf[PCAP_ERRBUF_SIZE];
+ /* Reopen causes pcap internal failure in process mode */
+ if (odp_global_ro.init_param.mem_model == ODP_MEM_MODEL_PROCESS)
+ return 1;
+
if (pcap->loops != 0 && ++pcap->loop_cnt >= pcap->loops)
return 1;
@@ -193,8 +270,7 @@ static int _pcapif_reopen(pkt_pcap_t *pcap)
pcap->rx = pcap_open_offline(pcap->fname_rx, errbuf);
if (!pcap->rx) {
- ODP_ERR("failed to reopen pcap file %s (%s)\n",
- pcap->fname_rx, errbuf);
+ _ODP_ERR("failed to reopen pcap file %s (%s)\n", pcap->fname_rx, errbuf);
return 1;
}
@@ -202,7 +278,7 @@ static int _pcapif_reopen(pkt_pcap_t *pcap)
}
static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkts[], int len)
+ odp_packet_t pkts[], int num)
{
int i;
struct pcap_pkthdr *hdr;
@@ -210,21 +286,28 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
odp_packet_t pkt;
odp_packet_hdr_t *pkt_hdr;
uint32_t pkt_len;
- pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
odp_time_t ts_val;
odp_time_t *ts = NULL;
-
- odp_ticketlock_lock(&pktio_entry->s.rxl);
-
- if (pktio_entry->s.state != PKTIO_STATE_STARTED || !pcap->rx) {
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ int packets = 0;
+ uint32_t octets = 0;
+ int num_pkts = 0;
+ int num_cls = 0;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ uint16_t frame_offset = pktio_entry->pktin_frame_offset;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
+
+ odp_ticketlock_lock(&pktio_entry->rxl);
+
+ if (odp_unlikely(!pcap->rx)) {
+ odp_ticketlock_unlock(&pktio_entry->rxl);
return 0;
}
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp)
+ if (opt.bit.ts_all || opt.bit.ts_ptp)
ts = &ts_val;
- for (i = 0; i < len; ) {
+ for (i = 0; i < num; i++) {
int ret;
ret = pcap_next_ex(pcap->rx, &hdr, &data);
@@ -238,41 +321,89 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
pkt_len = hdr->caplen;
- ret = packet_alloc_multi(pcap->pool, pkt_len, &pkt, 1);
+ ret = _odp_packet_alloc_multi(pcap->pool, pkt_len + frame_offset,
+ &pkt, 1);
if (odp_unlikely(ret != 1))
break;
if (ts != NULL)
ts_val = odp_time_global();
- pkt_hdr = odp_packet_hdr(pkt);
+ pkt_hdr = packet_hdr(pkt);
+ if (frame_offset)
+ pull_head(pkt_hdr, frame_offset);
- if (odp_packet_copy_from_mem(pkt, 0, hdr->caplen, data) != 0) {
- ODP_ERR("failed to copy packet data\n");
+ if (odp_packet_copy_from_mem(pkt, 0, pkt_len, data) != 0) {
+ _ODP_ERR("failed to copy packet data\n");
break;
}
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
- pktio_entry->s.stats.in_octets += pkt_hdr->frame_len;
+ if (layer) {
+ ret = _odp_packet_parse_common(pkt_hdr, data, pkt_len,
+ pkt_len, layer, opt);
+ if (ret)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors);
+
+ if (ret < 0) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, data,
+ &new_pool, pkt_hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+
+ if (ret) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &pkt_hdr, new_pool))) {
+ odp_packet_free(pkt);
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+ continue;
+ }
+ }
+ }
packet_set_ts(pkt_hdr, ts);
- pkt_hdr->input = pktio_entry->s.handle;
+ pkt_hdr->input = pktio_entry->handle;
- pkts[i] = pkt;
+ if (!pkt_hdr->p.flags.all.error) {
+ octets += pkt_len;
+ packets++;
+ }
- i++;
+ /* Enqueue packets directly to classifier destination queue */
+ if (cls_enabled) {
+ pkts[num_cls++] = pkt;
+ num_cls = _odp_cls_enq(pkts, num_cls, (i + 1 == num));
+ } else {
+ pkts[num_pkts++] = pkt;
+ }
}
- pktio_entry->s.stats.in_ucast_pkts += i;
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(num_cls))
+ _odp_cls_enq(pkts, num_cls, true);
- return i;
+ pktio_entry->stats.in_octets += octets;
+ pktio_entry->stats.in_packets += packets;
+
+ odp_ticketlock_unlock(&pktio_entry->rxl);
+
+ return num_pkts;
}
static int _pcapif_dump_pkt(pkt_pcap_t *pcap, odp_packet_t pkt)
{
struct pcap_pkthdr hdr;
+ uint8_t tx_buf[PKTIO_PCAP_MTU_MAX];
if (!pcap->tx_dump)
return 0;
@@ -281,34 +412,30 @@ static int _pcapif_dump_pkt(pkt_pcap_t *pcap, odp_packet_t pkt)
hdr.len = hdr.caplen;
(void)gettimeofday(&hdr.ts, NULL);
- if (odp_packet_copy_to_mem(pkt, 0, hdr.len, pcap->buf) != 0)
+ if (odp_packet_copy_to_mem(pkt, 0, hdr.len, tx_buf) != 0)
return -1;
- pcap_dump(pcap->tx_dump, &hdr, pcap->buf);
+ pcap_dump(pcap->tx_dump, &hdr, tx_buf);
(void)pcap_dump_flush(pcap->tx_dump);
return 0;
}
static int pcapif_send_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkts[], int len)
+ const odp_packet_t pkts[], int num)
{
- pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
int i;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
- odp_ticketlock_lock(&pktio_entry->s.txl);
-
- if (pktio_entry->s.state != PKTIO_STATE_STARTED) {
- odp_ticketlock_unlock(&pktio_entry->s.txl);
- return 0;
- }
+ odp_ticketlock_lock(&pktio_entry->txl);
- for (i = 0; i < len; ++i) {
- int pkt_len = odp_packet_len(pkts[i]);
+ for (i = 0; i < num; ++i) {
+ uint32_t pkt_len = odp_packet_len(pkts[i]);
- if (pkt_len > PKTIO_PCAP_MTU) {
+ if (odp_unlikely(pkt_len > pcap->mtu)) {
if (i == 0) {
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ odp_ticketlock_unlock(&pktio_entry->txl);
return -1;
}
break;
@@ -317,20 +444,36 @@ static int pcapif_send_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
if (_pcapif_dump_pkt(pcap, pkts[i]) != 0)
break;
- pktio_entry->s.stats.out_octets += pkt_len;
+ pktio_entry->stats.out_octets += pkt_len;
+
+ if (odp_unlikely(tx_ts_enabled && packet_hdr(pkts[i])->p.flags.ts_set))
+ _odp_pktio_tx_ts_set(pktio_entry);
+
odp_packet_free(pkts[i]);
}
- pktio_entry->s.stats.out_ucast_pkts += i;
+ pktio_entry->stats.out_packets += i;
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ odp_ticketlock_unlock(&pktio_entry->txl);
return i;
}
static uint32_t pcapif_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED)
{
- return PKTIO_PCAP_MTU;
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
+
+ return pcap->mtu;
+}
+
+static int pcapif_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_pcap_t *pcap = pkt_priv(pktio_entry);
+
+ pcap->mtu = maxlen_input;
+
+ return 0;
}
static int pcapif_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
@@ -349,81 +492,79 @@ static int pcapif_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
capa->max_input_queues = 1;
capa->max_output_queues = 1;
capa->set_op.op.promisc_mode = 1;
+ capa->set_op.op.maxlen = 1;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = PKTIO_PCAP_MTU_MIN;
+ capa->maxlen.max_input = PKTIO_PCAP_MTU_MAX;
+ capa->maxlen.min_output = PKTIO_PCAP_MTU_MIN;
+ capa->maxlen.max_output = PKTIO_PCAP_MTU_MAX;
odp_pktio_config_init(&capa->config);
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
- return 0;
-}
-
-static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry,
- odp_bool_t enable)
-{
- char filter_exp[64] = {0};
- struct bpf_program bpf;
- pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
-
- if (!pcap->rx) {
- pcap->promisc = enable;
- return 0;
- }
-
- if (!enable) {
- char mac_str[18];
-
- snprintf(mac_str, sizeof(mac_str),
- "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
- pcap_mac[0], pcap_mac[1], pcap_mac[2],
- pcap_mac[3], pcap_mac[4], pcap_mac[5]);
-
- snprintf(filter_exp, sizeof(filter_exp),
- "ether dst %s or broadcast or multicast",
- mac_str);
- }
-
- if (pcap_compile(pcap->rx, &bpf, filter_exp,
- 0, PCAP_NETMASK_UNKNOWN) != 0) {
- ODP_ERR("failed to compile promisc mode filter: %s\n",
- pcap_geterr(pcap->rx));
- return -1;
- }
- if (pcap_setfilter(pcap->rx, &bpf) != 0) {
- ODP_ERR("failed to set promisc mode filter: %s\n",
- pcap_geterr(pcap->rx));
- return -1;
- }
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
- pcap->promisc = enable;
+ capa->stats.pktio.counter.in_octets = 1;
+ capa->stats.pktio.counter.in_packets = 1;
+ capa->stats.pktio.counter.in_discards = 1;
+ capa->stats.pktio.counter.in_errors = 1;
+ capa->stats.pktio.counter.out_octets = 1;
+ capa->stats.pktio.counter.out_packets = 1;
return 0;
}
static int pcapif_promisc_mode_get(pktio_entry_t *pktio_entry)
{
- return pktio_entry->s.pkt_pcap.promisc;
+ return pkt_priv(pktio_entry)->promisc;
}
static int pcapif_stats_reset(pktio_entry_t *pktio_entry)
{
- memset(&pktio_entry->s.stats, 0, sizeof(odp_pktio_stats_t));
+ memset(&pktio_entry->stats, 0, sizeof(odp_pktio_stats_t));
return 0;
}
static int pcapif_stats(pktio_entry_t *pktio_entry,
odp_pktio_stats_t *stats)
{
- memcpy(stats, &pktio_entry->s.stats, sizeof(odp_pktio_stats_t));
+ memcpy(stats, &pktio_entry->stats, sizeof(odp_pktio_stats_t));
return 0;
}
static int pcapif_init_global(void)
{
- ODP_PRINT("PKTIO: initialized pcap interface.\n");
+ _ODP_PRINT("PKTIO: initialized pcap interface.\n");
+ return 0;
+}
+
+static int pcapif_link_status(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+ return ODP_PKTIO_LINK_STATUS_UP;
+}
+
+static int pcapif_link_info(pktio_entry_t *pktio_entry ODP_UNUSED, odp_pktio_link_info_t *info)
+{
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ info->media = "virtual";
+ info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF;
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ info->status = ODP_PKTIO_LINK_STATUS_UP;
+
return 0;
}
-const pktio_if_ops_t pcap_pktio_ops = {
+const pktio_if_ops_t _odp_pcap_pktio_ops = {
.name = "pcap",
.print = NULL,
.init_global = pcapif_init_global,
@@ -434,14 +575,19 @@ const pktio_if_ops_t pcap_pktio_ops = {
.stats_reset = pcapif_stats_reset,
.recv = pcapif_recv_pkt,
.send = pcapif_send_pkt,
- .mtu_get = pcapif_mtu_get,
+ .maxlen_get = pcapif_mtu_get,
+ .maxlen_set = pcapif_mtu_set,
.promisc_mode_set = pcapif_promisc_mode_set,
.promisc_mode_get = pcapif_promisc_mode_get,
.mac_get = pcapif_mac_addr_get,
+ .mac_set = NULL,
.capability = pcapif_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL,
.input_queues_config = NULL,
.output_queues_config = NULL,
+ .link_status = pcapif_link_status,
+ .link_info = pcapif_link_info
};
diff --git a/platform/linux-generic/pktio/pktio_common.c b/platform/linux-generic/pktio/pktio_common.c
index 611bb451a..7dbd77c49 100644
--- a/platform/linux-generic/pktio/pktio_common.c
+++ b/platform/linux-generic/pktio/pktio_common.c
@@ -1,82 +1,135 @@
-/* Copyright (c) 2013, Linaro Limited
- * Copyright (c) 2013, Nokia Solutions and Networks
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2013 Nokia Solutions and Networks
*/
#include <odp_packet_io_internal.h>
-#include <odp_classification_internal.h>
#include <errno.h>
+#include <inttypes.h>
-int sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd)
+static int sock_recv_mq_tmo_select(pktio_entry_t * const *entry,
+ const int index[],
+ uint32_t num_q, uint32_t *from,
+ odp_packet_t packets[], int num,
+ uint64_t usecs, fd_set *readfds,
+ int maxfd)
{
- int err = 0;
- odp_pktio_stats_t cur_stats;
+ struct timeval timeout;
+ uint32_t i;
+ int ret;
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(&pktio_entry->s.stats, 0,
- sizeof(odp_pktio_stats_t));
- return 0;
- }
+ for (i = 0; i < num_q; i++) {
+ ret = entry[i]->ops->recv(entry[i], index[i], packets, num);
- memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
+ if (ret > 0 && from)
+ *from = i;
- if (pktio_entry->s.stats_type == STATS_ETHTOOL) {
- (void)ethtool_stats_get_fd(fd,
- pktio_entry->s.name,
- &cur_stats);
- } else if (pktio_entry->s.stats_type == STATS_SYSFS) {
- err = sysfs_stats(pktio_entry, &cur_stats);
- if (err != 0)
- ODP_ERR("stats error\n");
+ if (ret != 0)
+ return ret;
}
- if (err == 0)
- memcpy(&pktio_entry->s.stats, &cur_stats,
- sizeof(odp_pktio_stats_t));
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ if (select(maxfd + 1, readfds, NULL, NULL, &timeout) == 0)
+ return 0;
+
+ for (i = 0; i < num_q; i++) {
+ ret = entry[i]->ops->recv(entry[i], index[i], packets, num);
- return err;
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
}
-int sock_stats_fd(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats,
- int fd)
+int _odp_sock_recv_mq_tmo_try_int_driven(const struct odp_pktin_queue_t queues[],
+ uint32_t num_q, uint32_t *from,
+ odp_packet_t packets[], int num,
+ uint64_t usecs, int *trial_successful)
{
- odp_pktio_stats_t cur_stats;
- int ret = 0;
+ uint32_t i;
+ pktio_entry_t *entry[num_q];
+ int index[num_q];
+ fd_set readfds;
+ int maxfd = -1;
+ int (*impl)(pktio_entry_t *entry[], int index[], uint32_t num_q,
+ odp_packet_t packets[], int num, uint32_t *from,
+ uint64_t wait_usecs) = NULL;
+ int impl_set = 0;
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED)
- return 0;
+ /* First, we get pktio entries and queue indices. We then see if the
+ implementation function pointers are the same. If they are the
+ same, impl will be set to non-NULL; otherwise it will be NULL. */
+
+ for (i = 0; i < num_q; i++) {
+ entry[i] = get_pktio_entry(queues[i].pktio);
+ index[i] = queues[i].index;
+ if (entry[i] == NULL) {
+ _ODP_DBG("pktio entry %" PRIuPTR " does not exist\n",
+ (uintptr_t)queues[i].pktio);
+ *trial_successful = 0;
+ return -1;
+ }
+
+ if (odp_unlikely(entry[i]->state != PKTIO_STATE_STARTED)) {
+ *trial_successful = 0;
+ return 0;
+ }
+
+ if (entry[i]->ops->recv_mq_tmo == NULL &&
+ entry[i]->ops->fd_set == NULL) {
+ *trial_successful = 0;
+ return 0;
+ }
+ if (!impl_set) {
+ impl = entry[i]->ops->recv_mq_tmo;
+ impl_set = 1;
+ } else {
+ if (impl != entry[i]->ops->recv_mq_tmo)
+ impl = NULL;
+ }
+ }
+
+ /* Check whether we can call the compatible implementation */
+ if (impl != NULL) {
+ *trial_successful = 1;
+ return impl(entry, index, num_q, packets, num, from, usecs);
+ }
+
+ /* Get file descriptor sets of devices. maxfd will be -1 if this
+ fails. */
+ FD_ZERO(&readfds);
+ for (i = 0; i < num_q; i++) {
+ if (entry[i]->ops->fd_set) {
+ int maxfd2;
+
+ maxfd2 = entry[i]->ops->fd_set(entry[i], queues[i].index, &readfds);
+ if (maxfd2 < 0) {
+ maxfd = -1;
+ break;
+ }
+ if (maxfd2 > maxfd)
+ maxfd = maxfd2;
+ } else {
+ maxfd = -1;
+ }
+ }
- memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
- if (pktio_entry->s.stats_type == STATS_ETHTOOL) {
- (void)ethtool_stats_get_fd(fd,
- pktio_entry->s.name,
- &cur_stats);
- } else if (pktio_entry->s.stats_type == STATS_SYSFS) {
- sysfs_stats(pktio_entry, &cur_stats);
+ /* Check whether we can call the select() implementation */
+ if (maxfd >= 0) {
+ *trial_successful = 1;
+ return sock_recv_mq_tmo_select(entry, index, num_q, from,
+ packets, num, usecs,
+ &readfds, maxfd);
}
- stats->in_octets = cur_stats.in_octets -
- pktio_entry->s.stats.in_octets;
- stats->in_ucast_pkts = cur_stats.in_ucast_pkts -
- pktio_entry->s.stats.in_ucast_pkts;
- stats->in_discards = cur_stats.in_discards -
- pktio_entry->s.stats.in_discards;
- stats->in_errors = cur_stats.in_errors -
- pktio_entry->s.stats.in_errors;
- stats->in_unknown_protos = cur_stats.in_unknown_protos -
- pktio_entry->s.stats.in_unknown_protos;
-
- stats->out_octets = cur_stats.out_octets -
- pktio_entry->s.stats.out_octets;
- stats->out_ucast_pkts = cur_stats.out_ucast_pkts -
- pktio_entry->s.stats.out_ucast_pkts;
- stats->out_discards = cur_stats.out_discards -
- pktio_entry->s.stats.out_discards;
- stats->out_errors = cur_stats.out_errors -
- pktio_entry->s.stats.out_errors;
-
- return ret;
+ /* No mechanism worked. Set trial_successful to 0 so that polling will
+ be used by the main implementation. */
+ *trial_successful = 0;
+ return 0;
}
diff --git a/platform/linux-generic/pktio/ring.c b/platform/linux-generic/pktio/ring.c
deleted file mode 100644
index e3c73d1cb..000000000
--- a/platform/linux-generic/pktio/ring.c
+++ /dev/null
@@ -1,658 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Derived from FreeBSD's bufring.c
- *
- **************************************************************************
- *
- * Copyright (c) 2007,2008 Kip Macy kmacy@freebsd.org
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. The name of Kip Macy nor the names of other
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ***************************************************************************/
-
-#include <odp_api.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <inttypes.h>
-#include <odp_packet_io_ring_internal.h>
-#include <odp_internal.h>
-
-static TAILQ_HEAD(, _ring) odp_ring_list;
-
-#define RING_VAL_IS_POWER_2(x) ((((x) - 1) & (x)) == 0)
-
-/*
- * the enqueue of pointers on the ring.
- */
-#define ENQUEUE_PTRS() do { \
- const uint32_t size = r->prod.size; \
- uint32_t idx = prod_head & mask; \
- if (odp_likely(idx + n < size)) { \
- for (i = 0; i < (n & ((~(unsigned)0x3))); i += 4, idx += 4) { \
- r->ring[idx] = obj_table[i]; \
- r->ring[idx + 1] = obj_table[i + 1]; \
- r->ring[idx + 2] = obj_table[i + 2]; \
- r->ring[idx + 3] = obj_table[i + 3]; \
- } \
- switch (n & 0x3) { \
- case 3: \
- r->ring[idx++] = obj_table[i++]; \
- case 2: \
- r->ring[idx++] = obj_table[i++]; \
- case 1: \
- r->ring[idx++] = obj_table[i++]; \
- } \
- } else { \
- for (i = 0; idx < size; i++, idx++)\
- r->ring[idx] = obj_table[i]; \
- for (idx = 0; i < n; i++, idx++) \
- r->ring[idx] = obj_table[i]; \
- } \
-} while (0)
-
-/*
- * the actual copy of pointers on the ring to obj_table.
- */
-#define DEQUEUE_PTRS() do { \
- uint32_t idx = cons_head & mask; \
- const uint32_t size = r->cons.size; \
- if (odp_likely(idx + n < size)) { \
- for (i = 0; i < (n & (~(unsigned)0x3)); i += 4, idx += 4) {\
- obj_table[i] = r->ring[idx]; \
- obj_table[i + 1] = r->ring[idx + 1]; \
- obj_table[i + 2] = r->ring[idx + 2]; \
- obj_table[i + 3] = r->ring[idx + 3]; \
- } \
- switch (n & 0x3) { \
- case 3: \
- obj_table[i++] = r->ring[idx++]; \
- case 2: \
- obj_table[i++] = r->ring[idx++]; \
- case 1: \
- obj_table[i++] = r->ring[idx++]; \
- } \
- } else { \
- for (i = 0; idx < size; i++, idx++) \
- obj_table[i] = r->ring[idx]; \
- for (idx = 0; i < n; i++, idx++) \
- obj_table[i] = r->ring[idx]; \
- } \
-} while (0)
-
-static odp_rwlock_t qlock; /* rings tailq lock */
-
-/* init tailq_ring */
-void _ring_tailq_init(void)
-{
- TAILQ_INIT(&odp_ring_list);
- odp_rwlock_init(&qlock);
-}
-
-/* create the ring */
-_ring_t *
-_ring_create(const char *name, unsigned count, unsigned flags)
-{
- char ring_name[_RING_NAMESIZE];
- _ring_t *r;
- size_t ring_size;
- uint32_t shm_flag;
- odp_shm_t shm;
-
- if (flags & _RING_SHM_PROC)
- shm_flag = ODP_SHM_PROC | ODP_SHM_EXPORT;
- else
- shm_flag = 0;
-
- /* count must be a power of 2 */
- if (!RING_VAL_IS_POWER_2(count) || (count > _RING_SZ_MASK)) {
- ODP_ERR("Requested size is invalid, must be power of 2,"
- "and do not exceed the size limit %u\n",
- _RING_SZ_MASK);
- __odp_errno = EINVAL;
- return NULL;
- }
-
- snprintf(ring_name, sizeof(ring_name), "%s", name);
- ring_size = count * sizeof(void *) + sizeof(_ring_t);
-
- odp_rwlock_write_lock(&qlock);
- /* reserve a memory zone for this ring.*/
- shm = odp_shm_reserve(ring_name, ring_size, ODP_CACHE_LINE_SIZE,
- shm_flag);
-
- r = odp_shm_addr(shm);
-
- if (r != NULL) {
- /* init the ring structure */
- snprintf(r->name, sizeof(r->name), "%s", name);
- r->flags = flags;
- r->prod.watermark = count;
- r->prod.sp_enqueue = !!(flags & _RING_F_SP_ENQ);
- r->cons.sc_dequeue = !!(flags & _RING_F_SC_DEQ);
- r->prod.size = count;
- r->cons.size = count;
- r->prod.mask = count - 1;
- r->cons.mask = count - 1;
- r->prod.head = 0;
- r->cons.head = 0;
- r->prod.tail = 0;
- r->cons.tail = 0;
-
- if (!(flags & _RING_NO_LIST))
- TAILQ_INSERT_TAIL(&odp_ring_list, r, next);
- } else {
- __odp_errno = ENOMEM;
- ODP_ERR("Cannot reserve memory\n");
- }
-
- odp_rwlock_write_unlock(&qlock);
- return r;
-}
-
-int _ring_destroy(const char *name)
-{
- odp_shm_t shm = odp_shm_lookup(name);
-
- if (shm != ODP_SHM_INVALID) {
- _ring_t *r = odp_shm_addr(shm);
-
- odp_rwlock_write_lock(&qlock);
- if (!(r->flags & _RING_NO_LIST))
- TAILQ_REMOVE(&odp_ring_list, r, next);
- odp_rwlock_write_unlock(&qlock);
-
- return odp_shm_free(shm);
- }
- return 0;
-}
-
-/*
- * change the high water mark. If *count* is 0, water marking is
- * disabled
- */
-int _ring_set_water_mark(_ring_t *r, unsigned count)
-{
- if (count >= r->prod.size)
- return -EINVAL;
-
- /* if count is 0, disable the watermarking */
- if (count == 0)
- count = r->prod.size;
-
- r->prod.watermark = count;
- return 0;
-}
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- */
-int ___ring_mp_do_enqueue(_ring_t *r, void * const *obj_table,
- unsigned n, enum _ring_queue_behavior behavior)
-{
- uint32_t prod_head, prod_next;
- uint32_t cons_tail, free_entries;
- const unsigned max = n;
- int success;
- unsigned i;
- uint32_t mask = r->prod.mask;
- int ret;
-
- /* move prod.head atomically */
- do {
- /* Reset n to the initial burst count */
- n = max;
-
- prod_head = __atomic_load_n(&r->prod.head, __ATOMIC_RELAXED);
- cons_tail = __atomic_load_n(&r->cons.tail, __ATOMIC_ACQUIRE);
- /* The subtraction is done between two unsigned 32bits value
- * (the result is always modulo 32 bits even if we have
- * prod_head > cons_tail). So 'free_entries' is always between 0
- * and size(ring)-1. */
- free_entries = (mask + cons_tail - prod_head);
-
- /* check that we have enough room in ring */
- if (odp_unlikely(n > free_entries)) {
- if (behavior == _RING_QUEUE_FIXED)
- return -ENOBUFS;
- /* No free entry available */
- if (odp_unlikely(free_entries == 0))
- return 0;
-
- n = free_entries;
- }
-
- prod_next = prod_head + n;
- success = __atomic_compare_exchange_n(&r->prod.head,
- &prod_head,
- prod_next,
- false/*strong*/,
- __ATOMIC_ACQUIRE,
- __ATOMIC_RELAXED);
- } while (odp_unlikely(success == 0));
-
- /* write entries in ring */
- ENQUEUE_PTRS();
-
- /* if we exceed the watermark */
- if (odp_unlikely(((mask + 1) - free_entries + n) > r->prod.watermark)) {
- ret = (behavior == _RING_QUEUE_FIXED) ? -EDQUOT :
- (int)(n | _RING_QUOT_EXCEED);
- } else {
- ret = (behavior == _RING_QUEUE_FIXED) ? 0 : n;
- }
-
- /*
- * If there are other enqueues in progress that preceded us,
- * we need to wait for them to complete
- */
- while (odp_unlikely(__atomic_load_n(&r->prod.tail, __ATOMIC_RELAXED) !=
- prod_head))
- odp_cpu_pause();
-
- /* Release our entries and the memory they refer to */
- __atomic_store_n(&r->prod.tail, prod_next, __ATOMIC_RELEASE);
- return ret;
-}
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- */
-int ___ring_sp_do_enqueue(_ring_t *r, void * const *obj_table,
- unsigned n, enum _ring_queue_behavior behavior)
-{
- uint32_t prod_head, cons_tail;
- uint32_t prod_next, free_entries;
- unsigned i;
- uint32_t mask = r->prod.mask;
- int ret;
-
- prod_head = r->prod.head;
- cons_tail = __atomic_load_n(&r->cons.tail, __ATOMIC_ACQUIRE);
- /* The subtraction is done between two unsigned 32bits value
- * (the result is always modulo 32 bits even if we have
- * prod_head > cons_tail). So 'free_entries' is always between 0
- * and size(ring)-1. */
- free_entries = mask + cons_tail - prod_head;
-
- /* check that we have enough room in ring */
- if (odp_unlikely(n > free_entries)) {
- if (behavior == _RING_QUEUE_FIXED)
- return -ENOBUFS;
- /* No free entry available */
- if (odp_unlikely(free_entries == 0))
- return 0;
-
- n = free_entries;
- }
-
- prod_next = prod_head + n;
- r->prod.head = prod_next;
-
- /* write entries in ring */
- ENQUEUE_PTRS();
-
- /* if we exceed the watermark */
- if (odp_unlikely(((mask + 1) - free_entries + n) > r->prod.watermark)) {
- ret = (behavior == _RING_QUEUE_FIXED) ? -EDQUOT :
- (int)(n | _RING_QUOT_EXCEED);
- } else {
- ret = (behavior == _RING_QUEUE_FIXED) ? 0 : n;
- }
-
- /* Release our entries and the memory they refer to */
- __atomic_store_n(&r->prod.tail, prod_next, __ATOMIC_RELEASE);
- return ret;
-}
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe).
- */
-
-int ___ring_mc_do_dequeue(_ring_t *r, void **obj_table,
- unsigned n, enum _ring_queue_behavior behavior)
-{
- uint32_t cons_head, prod_tail;
- uint32_t cons_next, entries;
- const unsigned max = n;
- int success;
- unsigned i;
- uint32_t mask = r->prod.mask;
-
- /* move cons.head atomically */
- do {
- /* Restore n as it may change every loop */
- n = max;
-
- cons_head = __atomic_load_n(&r->cons.head, __ATOMIC_RELAXED);
- prod_tail = __atomic_load_n(&r->prod.tail, __ATOMIC_ACQUIRE);
- /* The subtraction is done between two unsigned 32bits value
- * (the result is always modulo 32 bits even if we have
- * cons_head > prod_tail). So 'entries' is always between 0
- * and size(ring)-1. */
- entries = (prod_tail - cons_head);
-
- /* Set the actual entries for dequeue */
- if (n > entries) {
- if (behavior == _RING_QUEUE_FIXED)
- return -ENOENT;
- if (odp_unlikely(entries == 0))
- return 0;
-
- n = entries;
- }
-
- cons_next = cons_head + n;
- success = __atomic_compare_exchange_n(&r->cons.head,
- &cons_head,
- cons_next,
- false/*strong*/,
- __ATOMIC_ACQUIRE,
- __ATOMIC_RELAXED);
- } while (odp_unlikely(success == 0));
-
- /* copy in table */
- DEQUEUE_PTRS();
-
- /*
- * If there are other dequeues in progress that preceded us,
- * we need to wait for them to complete
- */
- while (odp_unlikely(__atomic_load_n(&r->cons.tail, __ATOMIC_RELAXED) !=
- cons_head))
- odp_cpu_pause();
-
- /* Release our entries and the memory they refer to */
- __atomic_store_n(&r->cons.tail, cons_next, __ATOMIC_RELEASE);
-
- return behavior == _RING_QUEUE_FIXED ? 0 : n;
-}
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).
- */
-int ___ring_sc_do_dequeue(_ring_t *r, void **obj_table,
- unsigned n, enum _ring_queue_behavior behavior)
-{
- uint32_t cons_head, prod_tail;
- uint32_t cons_next, entries;
- unsigned i;
- uint32_t mask = r->prod.mask;
-
- cons_head = r->cons.head;
- prod_tail = __atomic_load_n(&r->prod.tail, __ATOMIC_ACQUIRE);
- /* The subtraction is done between two unsigned 32bits value
- * (the result is always modulo 32 bits even if we have
- * cons_head > prod_tail). So 'entries' is always between 0
- * and size(ring)-1. */
- entries = prod_tail - cons_head;
-
- if (n > entries) {
- if (behavior == _RING_QUEUE_FIXED)
- return -ENOENT;
- if (odp_unlikely(entries == 0))
- return 0;
-
- n = entries;
- }
-
- cons_next = cons_head + n;
- r->cons.head = cons_next;
-
- /* Acquire the pointers and the memory they refer to */
- /* copy in table */
- DEQUEUE_PTRS();
-
- __atomic_store_n(&r->cons.tail, cons_next, __ATOMIC_RELEASE);
- return behavior == _RING_QUEUE_FIXED ? 0 : n;
-}
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- */
-int _ring_mp_enqueue_bulk(_ring_t *r, void * const *obj_table,
- unsigned n)
-{
- return ___ring_mp_do_enqueue(r, obj_table, n,
- _RING_QUEUE_FIXED);
-}
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- */
-int _ring_sp_enqueue_bulk(_ring_t *r, void * const *obj_table,
- unsigned n)
-{
- return ___ring_sp_do_enqueue(r, obj_table, n,
- _RING_QUEUE_FIXED);
-}
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe).
- */
-int _ring_mc_dequeue_bulk(_ring_t *r, void **obj_table, unsigned n)
-{
- return ___ring_mc_do_dequeue(r, obj_table, n,
- _RING_QUEUE_FIXED);
-}
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).
- */
-int _ring_sc_dequeue_bulk(_ring_t *r, void **obj_table, unsigned n)
-{
- return ___ring_sc_do_dequeue(r, obj_table, n,
- _RING_QUEUE_FIXED);
-}
-
-/**
- * Test if a ring is full.
- */
-int _ring_full(const _ring_t *r)
-{
- uint32_t prod_tail = r->prod.tail;
- uint32_t cons_tail = r->cons.tail;
-
- return (((cons_tail - prod_tail - 1) & r->prod.mask) == 0);
-}
-
-/**
- * Test if a ring is empty.
- */
-int _ring_empty(const _ring_t *r)
-{
- uint32_t prod_tail = r->prod.tail;
- uint32_t cons_tail = r->cons.tail;
-
- return !!(cons_tail == prod_tail);
-}
-
-/**
- * Return the number of entries in a ring.
- */
-unsigned _ring_count(const _ring_t *r)
-{
- uint32_t prod_tail = r->prod.tail;
- uint32_t cons_tail = r->cons.tail;
-
- return (prod_tail - cons_tail) & r->prod.mask;
-}
-
-/**
- * Return the number of free entries in a ring.
- */
-unsigned _ring_free_count(const _ring_t *r)
-{
- uint32_t prod_tail = r->prod.tail;
- uint32_t cons_tail = r->cons.tail;
-
- return (cons_tail - prod_tail - 1) & r->prod.mask;
-}
-
-/* dump the status of the ring on the console */
-void _ring_dump(const _ring_t *r)
-{
- ODP_DBG("ring <%s>@%p\n", r->name, r);
- ODP_DBG(" flags=%x\n", r->flags);
- ODP_DBG(" size=%" PRIu32 "\n", r->prod.size);
- ODP_DBG(" ct=%" PRIu32 "\n", r->cons.tail);
- ODP_DBG(" ch=%" PRIu32 "\n", r->cons.head);
- ODP_DBG(" pt=%" PRIu32 "\n", r->prod.tail);
- ODP_DBG(" ph=%" PRIu32 "\n", r->prod.head);
- ODP_DBG(" used=%u\n", _ring_count(r));
- ODP_DBG(" avail=%u\n", _ring_free_count(r));
- if (r->prod.watermark == r->prod.size)
- ODP_DBG(" watermark=0\n");
- else
- ODP_DBG(" watermark=%" PRIu32 "\n", r->prod.watermark);
-}
-
-/* dump the status of all rings on the console */
-void _ring_list_dump(void)
-{
- const _ring_t *mp = NULL;
-
- odp_rwlock_read_lock(&qlock);
-
- TAILQ_FOREACH(mp, &odp_ring_list, next) {
- _ring_dump(mp);
- }
-
- odp_rwlock_read_unlock(&qlock);
-}
-
-/* search a ring from its name */
-_ring_t *_ring_lookup(const char *name)
-{
- _ring_t *r;
-
- odp_rwlock_read_lock(&qlock);
- TAILQ_FOREACH(r, &odp_ring_list, next) {
- if (strncmp(name, r->name, _RING_NAMESIZE) == 0)
- break;
- }
- odp_rwlock_read_unlock(&qlock);
-
- return r;
-}
-
-/**
- * Enqueue several objects on the ring (multi-producers safe).
- */
-int _ring_mp_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n)
-{
- return ___ring_mp_do_enqueue(r, obj_table, n,
- _RING_QUEUE_VARIABLE);
-}
-
-/**
- * Enqueue several objects on a ring (NOT multi-producers safe).
- */
-int _ring_sp_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n)
-{
- return ___ring_sp_do_enqueue(r, obj_table, n,
- _RING_QUEUE_VARIABLE);
-}
-
-/**
- * Enqueue several objects on a ring.
- */
-int _ring_enqueue_burst(_ring_t *r, void * const *obj_table,
- unsigned n)
-{
- if (r->prod.sp_enqueue)
- return _ring_sp_enqueue_burst(r, obj_table, n);
- else
- return _ring_mp_enqueue_burst(r, obj_table, n);
-}
-
-/**
- * Dequeue several objects from a ring (multi-consumers safe).
- */
-int _ring_mc_dequeue_burst(_ring_t *r, void **obj_table, unsigned n)
-{
- return ___ring_mc_do_dequeue(r, obj_table, n,
- _RING_QUEUE_VARIABLE);
-}
-
-/**
- * Dequeue several objects from a ring (NOT multi-consumers safe).
- */
-int _ring_sc_dequeue_burst(_ring_t *r, void **obj_table, unsigned n)
-{
- return ___ring_sc_do_dequeue(r, obj_table, n,
- _RING_QUEUE_VARIABLE);
-}
-
-/**
- * Dequeue multiple objects from a ring up to a maximum number.
- */
-int _ring_dequeue_burst(_ring_t *r, void **obj_table, unsigned n)
-{
- if (r->cons.sc_dequeue)
- return _ring_sc_dequeue_burst(r, obj_table, n);
- else
- return _ring_mc_dequeue_burst(r, obj_table, n);
-}
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index a383adc6a..bd4ffc94f 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -1,53 +1,56 @@
-/* Copyright (c) 2013, Linaro Limited
- * Copyright (c) 2013, Nokia Solutions and Networks
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2013-2023 Nokia Solutions and Networks
*/
#include <odp_posix_extensions.h>
+#include <odp/api/align.h>
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp_socket_common.h>
+#include <odp_parse_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_packet_io_stats.h>
+#include <odp_debug_internal.h>
+#include <odp_classification_internal.h>
+#include <odp_macros_internal.h>
+
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <linux/if_packet.h>
-#include <linux/filter.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <unistd.h>
-#include <bits/wordsize.h>
-#include <net/ethernet.h>
-#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
#include <net/if.h>
-#include <inttypes.h>
-#include <poll.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/syscall.h>
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-#include <odp_api.h>
-#include <odp_packet_socket.h>
-#include <odp_packet_internal.h>
-#include <odp_packet_io_internal.h>
-#include <odp_align_internal.h>
-#include <odp_debug_internal.h>
-#include <odp_classification_datamodel.h>
-#include <odp_classification_inlines.h>
-#include <odp_classification_internal.h>
-#include <odp/api/hints.h>
+typedef struct {
+ odp_ticketlock_t rx_lock ODP_ALIGNED_CACHE;
+ odp_ticketlock_t tx_lock ODP_ALIGNED_CACHE;
+ int sockfd; /**< socket descriptor */
+ odp_pool_t pool; /**< pool to alloc packets from */
+ uint32_t mtu; /**< maximum transmission unit */
+ uint32_t mtu_max; /**< maximum supported MTU value */
+ unsigned char if_mac[ETH_ALEN]; /**< IF eth mac addr */
+} pkt_sock_t;
-#include <protocols/eth.h>
-#include <protocols/ip.h>
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_sock_t),
+ "PKTIO_PRIVATE_SIZE too small");
-#define MAX_SEGS CONFIG_PACKET_MAX_SEGS
-#define PACKET_JUMBO_LEN (9 * 1024)
+static inline pkt_sock_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_sock_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
@@ -85,389 +88,18 @@ int sendmmsg(int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags)
#endif
}
-
-/** Eth buffer start offset from u32-aligned address to make sure the following
- * header (e.g. IP) starts at a 32-bit aligned address.
- */
-#define ETHBUF_OFFSET (ODP_ALIGN_ROUNDUP(_ODP_ETHHDR_LEN, sizeof(uint32_t)) \
- - _ODP_ETHHDR_LEN)
-
-/** Round up buffer address to get a properly aliged eth buffer, i.e. aligned
- * so that the next header always starts at a 32bit aligned address.
- */
-#define ETHBUF_ALIGN(buf_ptr) ((uint8_t *)ODP_ALIGN_ROUNDUP_PTR((buf_ptr), \
- sizeof(uint32_t)) + ETHBUF_OFFSET)
-
-/**
- * ODP_PACKET_SOCKET_MMSG:
- * ODP_PACKET_SOCKET_MMAP:
- * ODP_PACKET_NETMAP:
- */
-int mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[])
-{
- struct ifreq ethreq;
- int ret;
-
- memset(&ethreq, 0, sizeof(ethreq));
- snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", name);
- ret = ioctl(fd, SIOCGIFHWADDR, &ethreq);
- if (ret != 0) {
- __odp_errno = errno;
- ODP_ERR("ioctl(SIOCGIFHWADDR): %s: \"%s\".\n", strerror(errno),
- ethreq.ifr_name);
- return -1;
- }
-
- memcpy(mac_dst, (unsigned char *)ethreq.ifr_ifru.ifru_hwaddr.sa_data,
- ETH_ALEN);
- return 0;
-}
-
-/*
- * ODP_PACKET_SOCKET_MMSG:
- * ODP_PACKET_SOCKET_MMAP:
- * ODP_PACKET_NETMAP:
- */
-uint32_t mtu_get_fd(int fd, const char *name)
-{
- struct ifreq ifr;
- int ret;
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
- ret = ioctl(fd, SIOCGIFMTU, &ifr);
- if (ret < 0) {
- __odp_errno = errno;
- ODP_DBG("ioctl(SIOCGIFMTU): %s: \"%s\".\n", strerror(errno),
- ifr.ifr_name);
- return 0;
- }
- return ifr.ifr_mtu;
-}
-
-/*
- * ODP_PACKET_SOCKET_MMSG:
- * ODP_PACKET_SOCKET_MMAP:
- * ODP_PACKET_NETMAP:
- */
-int promisc_mode_set_fd(int fd, const char *name, int enable)
-{
- struct ifreq ifr;
- int ret;
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
- ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
- if (ret < 0) {
- __odp_errno = errno;
- ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno),
- ifr.ifr_name);
- return -1;
- }
-
- if (enable)
- ifr.ifr_flags |= IFF_PROMISC;
- else
- ifr.ifr_flags &= ~(IFF_PROMISC);
-
- ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
- if (ret < 0) {
- __odp_errno = errno;
- ODP_DBG("ioctl(SIOCSIFFLAGS): %s: \"%s\".\n", strerror(errno),
- ifr.ifr_name);
- return -1;
- }
- return 0;
-}
-
-/*
- * ODP_PACKET_SOCKET_MMSG:
- * ODP_PACKET_SOCKET_MMAP:
- * ODP_PACKET_NETMAP:
- */
-int promisc_mode_get_fd(int fd, const char *name)
-{
- struct ifreq ifr;
- int ret;
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
- ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
- if (ret < 0) {
- __odp_errno = errno;
- ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno),
- ifr.ifr_name);
- return -1;
- }
-
- return !!(ifr.ifr_flags & IFF_PROMISC);
-}
-
-int link_status_fd(int fd, const char *name)
-{
- struct ifreq ifr;
- int ret;
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
- ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
- if (ret < 0) {
- __odp_errno = errno;
- ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno),
- ifr.ifr_name);
- return -1;
- }
-
- return !!(ifr.ifr_flags & IFF_RUNNING);
-}
-
-/**
- * Get enabled hash options of a packet socket
- *
- * @param fd Socket file descriptor
- * @param name Interface name
- * @param flow_type Packet flow type
- * @param options[out] Enabled hash options
- *
- * @retval 0 on success
- * @retval <0 on failure
- */
-static inline int get_rss_hash_options(int fd, const char *name,
- uint32_t flow_type, uint64_t *options)
-{
- struct ifreq ifr;
- struct ethtool_rxnfc rsscmd;
-
- memset(&ifr, 0, sizeof(ifr));
- memset(&rsscmd, 0, sizeof(rsscmd));
- *options = 0;
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
-
- rsscmd.cmd = ETHTOOL_GRXFH;
- rsscmd.flow_type = flow_type;
-
- ifr.ifr_data = (caddr_t)&rsscmd;
-
- if (ioctl(fd, SIOCETHTOOL, &ifr) < 0)
- return -1;
-
- *options = rsscmd.data;
- return 0;
-}
-
-int rss_conf_get_fd(int fd, const char *name,
- odp_pktin_hash_proto_t *hash_proto)
-{
- uint64_t options;
- int rss_enabled = 0;
-
- memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t));
-
- get_rss_hash_options(fd, name, IPV4_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) {
- hash_proto->proto.ipv4 = 1;
- rss_enabled++;
- }
- get_rss_hash_options(fd, name, TCP_V4_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
- (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
- hash_proto->proto.ipv4_tcp = 1;
- rss_enabled++;
- }
- get_rss_hash_options(fd, name, UDP_V4_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
- (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
- hash_proto->proto.ipv4_udp = 1;
- rss_enabled++;
- }
- get_rss_hash_options(fd, name, IPV6_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) {
- hash_proto->proto.ipv6 = 1;
- rss_enabled++;
- }
- get_rss_hash_options(fd, name, TCP_V6_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
- (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
- hash_proto->proto.ipv6_tcp = 1;
- rss_enabled++;
- }
- get_rss_hash_options(fd, name, UDP_V6_FLOW, &options);
- if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) &&
- (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) {
- hash_proto->proto.ipv6_udp = 1;
- rss_enabled++;
- }
- return rss_enabled;
-}
-
-/**
- * Set hash options of a packet socket
- *
- * @param fd Socket file descriptor
- * @param name Interface name
- * @param flow_type Packet flow type
- * @param options Hash options
- *
- * @retval 0 on success
- * @retval <0 on failure
- */
-static inline int set_rss_hash(int fd, const char *name,
- uint32_t flow_type, uint64_t options)
-{
- struct ifreq ifr;
- struct ethtool_rxnfc rsscmd;
-
- memset(&rsscmd, 0, sizeof(rsscmd));
-
- snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
-
- rsscmd.cmd = ETHTOOL_SRXFH;
- rsscmd.flow_type = flow_type;
- rsscmd.data = options;
-
- ifr.ifr_data = (caddr_t)&rsscmd;
-
- if (ioctl(fd, SIOCETHTOOL, &ifr) < 0)
- return -1;
-
- return 0;
-}
-
-int rss_conf_set_fd(int fd, const char *name,
- const odp_pktin_hash_proto_t *hash_proto)
-{
- uint64_t options;
- odp_pktin_hash_proto_t cur_hash;
-
- /* Compare to currently set hash protocols */
- rss_conf_get_fd(fd, name, &cur_hash);
-
- if (hash_proto->proto.ipv4_udp && !cur_hash.proto.ipv4_udp) {
- options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
- if (set_rss_hash(fd, name, UDP_V4_FLOW, options))
- return -1;
- }
- if (hash_proto->proto.ipv4_tcp && !cur_hash.proto.ipv4_tcp) {
- options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
- if (set_rss_hash(fd, name, TCP_V4_FLOW, options))
- return -1;
- }
- if (hash_proto->proto.ipv6_udp && !cur_hash.proto.ipv6_udp) {
- options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
- if (set_rss_hash(fd, name, UDP_V6_FLOW, options))
- return -1;
- }
- if (hash_proto->proto.ipv6_tcp && !cur_hash.proto.ipv6_tcp) {
- options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3;
- if (set_rss_hash(fd, name, TCP_V6_FLOW, options))
- return -1;
- }
- if (hash_proto->proto.ipv4 && !cur_hash.proto.ipv4) {
- options = RXH_IP_SRC | RXH_IP_DST;
- if (set_rss_hash(fd, name, IPV4_FLOW, options))
- return -1;
- }
- if (hash_proto->proto.ipv6 && !cur_hash.proto.ipv6) {
- options = RXH_IP_SRC | RXH_IP_DST;
- if (set_rss_hash(fd, name, IPV6_FLOW, options))
- return -1;
- }
- return 0;
-}
-
-int rss_conf_get_supported_fd(int fd, const char *name,
- odp_pktin_hash_proto_t *hash_proto)
-{
- uint64_t options;
- int rss_supported = 0;
-
- memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t));
-
- if (!get_rss_hash_options(fd, name, IPV4_FLOW, &options)) {
- if (!set_rss_hash(fd, name, IPV4_FLOW, options)) {
- hash_proto->proto.ipv4 = 1;
- rss_supported++;
- }
- }
- if (!get_rss_hash_options(fd, name, TCP_V4_FLOW, &options)) {
- if (!set_rss_hash(fd, name, TCP_V4_FLOW, options)) {
- hash_proto->proto.ipv4_tcp = 1;
- rss_supported++;
- }
- }
- if (!get_rss_hash_options(fd, name, UDP_V4_FLOW, &options)) {
- if (!set_rss_hash(fd, name, UDP_V4_FLOW, options)) {
- hash_proto->proto.ipv4_udp = 1;
- rss_supported++;
- }
- }
- if (!get_rss_hash_options(fd, name, IPV6_FLOW, &options)) {
- if (!set_rss_hash(fd, name, IPV6_FLOW, options)) {
- hash_proto->proto.ipv6 = 1;
- rss_supported++;
- }
- }
- if (!get_rss_hash_options(fd, name, TCP_V6_FLOW, &options)) {
- if (!set_rss_hash(fd, name, TCP_V6_FLOW, options)) {
- hash_proto->proto.ipv6_tcp = 1;
- rss_supported++;
- }
- }
- if (!get_rss_hash_options(fd, name, UDP_V6_FLOW, &options)) {
- if (!set_rss_hash(fd, name, UDP_V6_FLOW, options)) {
- hash_proto->proto.ipv6_udp = 1;
- rss_supported++;
- }
- }
- return rss_supported;
-}
-
-void rss_conf_print(const odp_pktin_hash_proto_t *hash_proto)
-{ int max_len = 512;
- char str[max_len];
- int len = 0;
- int n = max_len - 1;
-
- len += snprintf(&str[len], n - len, " rss conf\n");
-
- if (hash_proto->proto.ipv4)
- len += snprintf(&str[len], n - len,
- " IPV4\n");
- if (hash_proto->proto.ipv4_tcp)
- len += snprintf(&str[len], n - len,
- " IPV4 TCP\n");
- if (hash_proto->proto.ipv4_udp)
- len += snprintf(&str[len], n - len,
- " IPV4 UDP\n");
- if (hash_proto->proto.ipv6)
- len += snprintf(&str[len], n - len,
- " IPV6\n");
- if (hash_proto->proto.ipv6_tcp)
- len += snprintf(&str[len], n - len,
- " IPV6 TCP\n");
- if (hash_proto->proto.ipv6_udp)
- len += snprintf(&str[len], n - len,
- " IPV6 UDP\n");
- str[len] = '\0';
-
- ODP_PRINT("%s\n", str);
-}
-
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_close(pktio_entry_t *pktio_entry)
{
- pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
+
if (pkt_sock->sockfd != -1 && close(pkt_sock->sockfd) != 0) {
- __odp_errno = errno;
- ODP_ERR("close(sockfd): %s\n", strerror(errno));
+ _ODP_ERR("close(sockfd): %s\n", strerror(errno));
return -1;
}
return 0;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
odp_pool_t pool)
{
@@ -477,8 +109,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
struct ifreq ethreq;
struct sockaddr_ll sa_ll;
char shm_name[ODP_SHM_NAME_LEN];
- pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
- odp_pktio_stats_t cur_stats;
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
/* Init pktio entry */
memset(pkt_sock, 0, sizeof(*pkt_sock));
@@ -493,8 +124,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sockfd == -1) {
- __odp_errno = errno;
- ODP_ERR("socket(): %s\n", strerror(errno));
+ _ODP_ERR("socket(): %s\n", strerror(errno));
goto error;
}
pkt_sock->sockfd = sockfd;
@@ -504,20 +134,21 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", netdev);
err = ioctl(sockfd, SIOCGIFINDEX, &ethreq);
if (err != 0) {
- __odp_errno = errno;
- ODP_ERR("ioctl(SIOCGIFINDEX): %s: \"%s\".\n", strerror(errno),
- ethreq.ifr_name);
+ _ODP_ERR("ioctl(SIOCGIFINDEX): %s: \"%s\".\n", strerror(errno), ethreq.ifr_name);
goto error;
}
if_idx = ethreq.ifr_ifindex;
- err = mac_addr_get_fd(sockfd, netdev, pkt_sock->if_mac);
+ err = _odp_mac_addr_get_fd(sockfd, netdev, pkt_sock->if_mac);
if (err != 0)
goto error;
- pkt_sock->mtu = mtu_get_fd(sockfd, netdev);
+ pkt_sock->mtu = _odp_mtu_get_fd(sockfd, netdev);
if (!pkt_sock->mtu)
goto error;
+ pkt_sock->mtu_max = _ODP_SOCKET_MTU_MAX;
+ if (pkt_sock->mtu > pkt_sock->mtu_max)
+ pkt_sock->mtu_max = pkt_sock->mtu;
/* bind socket to if */
memset(&sa_ll, 0, sizeof(sa_ll));
@@ -525,31 +156,22 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
sa_ll.sll_ifindex = if_idx;
sa_ll.sll_protocol = htons(ETH_P_ALL);
if (bind(sockfd, (struct sockaddr *)&sa_ll, sizeof(sa_ll)) < 0) {
- __odp_errno = errno;
- ODP_ERR("bind(to IF): %s\n", strerror(errno));
+ _ODP_ERR("bind(to IF): %s\n", strerror(errno));
goto error;
}
- err = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
- pktio_entry->s.name,
- &cur_stats);
- if (err != 0) {
- err = sysfs_stats(pktio_entry, &cur_stats);
- if (err != 0) {
- pktio_entry->s.stats_type = STATS_UNSUPPORTED;
- ODP_DBG("pktio: %s unsupported stats\n",
- pktio_entry->s.name);
- } else {
- pktio_entry->s.stats_type = STATS_SYSFS;
- }
- } else {
- pktio_entry->s.stats_type = STATS_ETHTOOL;
- }
+ pktio_entry->stats_type = _odp_sock_stats_type_fd(pktio_entry,
+ pkt_sock->sockfd);
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED)
+ _ODP_DBG("pktio: %s unsupported stats\n", pktio_entry->name);
err = sock_stats_reset(pktio_entry);
if (err != 0)
goto error;
+ odp_ticketlock_init(&pkt_sock->rx_lock);
+ odp_ticketlock_init(&pkt_sock->tx_lock);
+
return 0;
error:
@@ -558,9 +180,6 @@ error:
return -1;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_mmsg_open(odp_pktio_t id ODP_UNUSED,
pktio_entry_t *pktio_entry,
const char *devname, odp_pool_t pool)
@@ -570,89 +189,133 @@ static int sock_mmsg_open(odp_pktio_t id ODP_UNUSED,
return sock_setup_pkt(pktio_entry, devname, pool);
}
-static uint32_t _rx_pkt_to_iovec(odp_packet_t pkt,
- struct iovec iovecs[MAX_SEGS])
+static inline uint32_t _rx_pkt_to_iovec(odp_packet_t pkt, struct iovec *iovecs)
{
- odp_packet_seg_t seg = odp_packet_first_seg(pkt);
+ odp_packet_seg_t seg;
uint32_t seg_count = odp_packet_num_segs(pkt);
- uint32_t seg_id = 0;
- uint32_t iov_count = 0;
- uint8_t *ptr;
- uint32_t seglen;
-
- for (seg_id = 0; seg_id < seg_count; ++seg_id) {
- ptr = odp_packet_seg_data(pkt, seg);
- seglen = odp_packet_seg_data_len(pkt, seg);
-
- if (ptr) {
- iovecs[iov_count].iov_base = ptr;
- iovecs[iov_count].iov_len = seglen;
- iov_count++;
- }
- seg = odp_packet_next_seg(pkt, seg);
+ uint32_t i;
+
+ if (odp_likely(seg_count == 1)) {
+ iovecs[0].iov_base = odp_packet_data(pkt);
+ iovecs[0].iov_len = odp_packet_len(pkt);
+ return 1;
}
- return iov_count;
+ seg = odp_packet_first_seg(pkt);
+
+ for (i = 0; i < seg_count; i++) {
+ iovecs[i].iov_base = odp_packet_seg_data(pkt, seg);
+ iovecs[i].iov_len = odp_packet_seg_data_len(pkt, seg);
+ seg = odp_packet_next_seg(pkt, seg);
+ }
+ return i;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkt_table[], int len)
+ odp_packet_t pkt_table[], int num)
{
- pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
odp_pool_t pool = pkt_sock->pool;
odp_time_t ts_val;
odp_time_t *ts = NULL;
const int sockfd = pkt_sock->sockfd;
- struct mmsghdr msgvec[len];
- struct iovec iovecs[len][MAX_SEGS];
+ struct mmsghdr msgvec[num];
+ struct iovec iovecs[num][PKT_MAX_SEGS];
int nb_rx = 0;
+ int nb_cls = 0;
int nb_pkts;
int recv_msgs;
int i;
-
- odp_ticketlock_lock(&pktio_entry->s.rxl);
-
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp)
- ts = &ts_val;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ uint16_t frame_offset = pktio_entry->pktin_frame_offset;
+ uint32_t alloc_len = pkt_sock->mtu + frame_offset;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
memset(msgvec, 0, sizeof(msgvec));
- nb_pkts = packet_alloc_multi(pool, pkt_sock->mtu, pkt_table, len);
+ nb_pkts = _odp_packet_alloc_multi(pool, alloc_len, pkt_table, num);
for (i = 0; i < nb_pkts; i++) {
+ if (frame_offset)
+ pull_head(packet_hdr(pkt_table[i]), frame_offset);
msgvec[i].msg_hdr.msg_iovlen =
_rx_pkt_to_iovec(pkt_table[i], iovecs[i]);
msgvec[i].msg_hdr.msg_iov = iovecs[i];
}
+ odp_ticketlock_lock(&pkt_sock->rx_lock);
recv_msgs = recvmmsg(sockfd, msgvec, nb_pkts, MSG_DONTWAIT, NULL);
+ odp_ticketlock_unlock(&pkt_sock->rx_lock);
- if (ts != NULL)
+ if (opt.bit.ts_all || opt.bit.ts_ptp) {
ts_val = odp_time_global();
+ ts = &ts_val;
+ }
for (i = 0; i < recv_msgs; i++) {
void *base = msgvec[i].msg_hdr.msg_iov->iov_base;
struct ethhdr *eth_hdr = base;
odp_packet_t pkt = pkt_table[i];
- odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+ odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt);
uint16_t pkt_len = msgvec[i].msg_len;
int ret;
- if (pktio_cls_enabled(pktio_entry)) {
- uint16_t seg_len = pkt_len;
+ if (odp_unlikely(msgvec[i].msg_hdr.msg_flags & MSG_TRUNC)) {
+ odp_packet_free(pkt);
+ _ODP_DBG("dropped truncated packet\n");
+ continue;
+ }
- if (msgvec[i].msg_hdr.msg_iov->iov_len < pkt_len)
- seg_len = msgvec[i].msg_hdr.msg_iov->iov_len;
+ ret = odp_packet_trunc_tail(&pkt, odp_packet_len(pkt) - pkt_len,
+ NULL, NULL);
+ if (ret < 0) {
+ _ODP_ERR("trunc_tail failed");
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (layer) {
+ uint8_t buf[PARSE_BYTES];
+ uint16_t seg_len = msgvec[i].msg_hdr.msg_iov->iov_len;
+
+ /* Make sure there is enough data for the packet
+ * parser in the case of a segmented packet. */
+ if (odp_unlikely(seg_len < PARSE_BYTES && pkt_len > seg_len)) {
+ seg_len = _ODP_MIN(pkt_len, PARSE_BYTES);
+ odp_packet_copy_to_mem(pkt, 0, seg_len, buf);
+ base = buf;
+ }
+
+ ret = _odp_packet_parse_common(pkt_hdr, base, pkt_len,
+ seg_len, layer, opt);
+ if (ret)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors);
- if (cls_classify_packet(pktio_entry, base, pkt_len,
- seg_len, &pool, pkt_hdr)) {
- ODP_ERR("cls_classify_packet failed");
+ if (ret < 0) {
odp_packet_free(pkt);
continue;
}
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, base,
+ &new_pool, pkt_hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+
+ if (ret) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &pkt_hdr, new_pool))) {
+ odp_packet_free(pkt);
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+ continue;
+ }
+ }
}
/* Don't receive packets sent by ourselves */
@@ -662,85 +325,174 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
continue;
}
- ret = odp_packet_trunc_tail(&pkt, odp_packet_len(pkt) - pkt_len,
- NULL, NULL);
- if (ret < 0) {
- ODP_ERR("trunk_tail failed");
- odp_packet_free(pkt);
- continue;
+ pkt_hdr->input = pktio_entry->handle;
+ packet_set_ts(pkt_hdr, ts);
+
+ if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ pkt_table[nb_cls++] = pkt;
+ nb_cls = _odp_cls_enq(pkt_table, nb_cls, (i + 1 == recv_msgs));
+ } else {
+ pkt_table[nb_rx++] = pkt;
}
+ }
- pkt_hdr->input = pktio_entry->s.handle;
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(nb_cls))
+ _odp_cls_enq(pkt_table, nb_cls, true);
- if (!pktio_cls_enabled(pktio_entry))
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
+ /* Free unused pkt buffers */
+ if (i < nb_pkts)
+ odp_packet_free_multi(&pkt_table[i], nb_pkts - i);
- pkt_hdr->input = pktio_entry->s.handle;
- packet_set_ts(pkt_hdr, ts);
+ return nb_rx;
+}
+
+static int sock_fd_set(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
+ fd_set *readfds)
+{
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
+ const int sockfd = pkt_sock->sockfd;
+
+ FD_SET(sockfd, readfds);
+ return sockfd;
+}
+
+static int sock_recv_tmo(pktio_entry_t *pktio_entry, int index,
+ odp_packet_t pkt_table[], int num, uint64_t usecs)
+{
+ struct timeval timeout;
+ int ret;
+ int maxfd;
+ fd_set readfds;
+
+ ret = sock_mmsg_recv(pktio_entry, index, pkt_table, num);
+ if (ret != 0)
+ return ret;
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ FD_ZERO(&readfds);
+ maxfd = sock_fd_set(pktio_entry, index, &readfds);
- pkt_table[nb_rx++] = pkt;
+ while (1) {
+ ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret <= 0)
+ return 0;
+
+ ret = sock_mmsg_recv(pktio_entry, index, pkt_table, num);
+ if (odp_likely(ret))
+ return ret;
+
+ /* If no packets, continue wait until timeout expires */
}
+}
- /* Free unused pkt buffers */
- for (; i < nb_pkts; i++)
- odp_packet_free(pkt_table[i]);
+static int sock_recv_mq_tmo(pktio_entry_t *pktio_entry[], int index[],
+ uint32_t num_q, odp_packet_t pkt_table[], int num,
+ uint32_t *from, uint64_t usecs)
+{
+ struct timeval timeout;
+ uint32_t i;
+ int ret;
+ int maxfd = -1, maxfd2;
+ fd_set readfds;
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ for (i = 0; i < num_q; i++) {
+ ret = sock_mmsg_recv(pktio_entry[i], index[i], pkt_table, num);
- return nb_rx;
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret != 0)
+ return ret;
+ }
+
+ FD_ZERO(&readfds);
+
+ for (i = 0; i < num_q; i++) {
+ maxfd2 = sock_fd_set(pktio_entry[i], index[i], &readfds);
+ if (maxfd2 > maxfd)
+ maxfd = maxfd2;
+ }
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ while (1) {
+ ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0; i < num_q; i++) {
+ ret = sock_mmsg_recv(pktio_entry[i], index[i],
+ pkt_table, num);
+
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret)
+ return ret;
+ }
+
+ /* If no packets, continue wait until timeout expires */
+ }
}
-static uint32_t _tx_pkt_to_iovec(odp_packet_t pkt,
- struct iovec iovecs[MAX_SEGS])
+static inline uint32_t _tx_pkt_to_iovec(odp_packet_t pkt, struct iovec *iovecs)
{
- uint32_t pkt_len = odp_packet_len(pkt);
- uint32_t offset = odp_packet_l2_offset(pkt);
- uint32_t iov_count = 0;
-
- while (offset < pkt_len) {
- uint32_t seglen;
-
- iovecs[iov_count].iov_base = odp_packet_offset(pkt, offset,
- &seglen, NULL);
- iovecs[iov_count].iov_len = seglen;
- iov_count++;
- offset += seglen;
+ odp_packet_seg_t seg;
+ int seg_count = odp_packet_num_segs(pkt);
+ int i;
+
+ if (odp_likely(seg_count == 1)) {
+ iovecs[0].iov_base = odp_packet_data(pkt);
+ iovecs[0].iov_len = odp_packet_len(pkt);
+ return 1;
+ }
+
+ seg = odp_packet_first_seg(pkt);
+ for (i = 0; i < seg_count; i++) {
+ iovecs[i].iov_base = odp_packet_seg_data(pkt, seg);
+ iovecs[i].iov_len = odp_packet_seg_data_len(pkt, seg);
+ seg = odp_packet_next_seg(pkt, seg);
}
- return iov_count;
+ return i;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkt_table[], int len)
+ const odp_packet_t pkt_table[], int num)
{
- pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
- struct mmsghdr msgvec[len];
- struct iovec iovecs[len][MAX_SEGS];
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
+ struct mmsghdr msgvec[num];
+ struct iovec iovecs[num][PKT_MAX_SEGS];
int ret;
- int sockfd;
- int n, i;
-
- odp_ticketlock_lock(&pktio_entry->s.txl);
+ int sockfd = pkt_sock->sockfd;
+ int i;
+ int tx_ts_idx = 0;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
- sockfd = pkt_sock->sockfd;
memset(msgvec, 0, sizeof(msgvec));
- for (i = 0; i < len; i++) {
+ for (i = 0; i < num; i++) {
msgvec[i].msg_hdr.msg_iov = iovecs[i];
msgvec[i].msg_hdr.msg_iovlen = _tx_pkt_to_iovec(pkt_table[i],
- iovecs[i]);
+ iovecs[i]);
+ if (tx_ts_enabled && tx_ts_idx == 0) {
+ if (odp_unlikely(packet_hdr(pkt_table[i])->p.flags.ts_set))
+ tx_ts_idx = i + 1;
+ }
}
- for (i = 0; i < len; ) {
- ret = sendmmsg(sockfd, &msgvec[i], len - i, MSG_DONTWAIT);
+ odp_ticketlock_lock(&pkt_sock->tx_lock);
+
+ for (i = 0; i < num; ) {
+ ret = sendmmsg(sockfd, &msgvec[i], num - i, MSG_DONTWAIT);
if (odp_unlikely(ret <= -1)) {
if (i == 0 && SOCK_ERR_REPORT(errno)) {
- __odp_errno = errno;
- ODP_ERR("sendmmsg(): %s\n", strerror(errno));
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ _ODP_ERR("sendmmsg(): %s\n", strerror(errno));
+ odp_ticketlock_unlock(&pkt_sock->tx_lock);
return -1;
}
break;
@@ -749,111 +501,148 @@ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
i += ret;
}
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ if (odp_unlikely(tx_ts_idx && i >= tx_ts_idx))
+ _odp_pktio_tx_ts_set(pktio_entry);
- for (n = 0; n < i; ++n)
- odp_packet_free(pkt_table[n]);
+ odp_ticketlock_unlock(&pkt_sock->tx_lock);
+
+ odp_packet_free_multi(pkt_table, i);
return i;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static uint32_t sock_mtu_get(pktio_entry_t *pktio_entry)
{
- return pktio_entry->s.pkt_sock.mtu;
+ return pkt_priv(pktio_entry)->mtu;
+}
+
+static int sock_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
+ int ret;
+
+ ret = _odp_mtu_set_fd(pkt_sock->sockfd, pktio_entry->name, maxlen_input);
+ if (ret)
+ return ret;
+
+ pkt_sock->mtu = maxlen_input;
+
+ return 0;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_mac_addr_get(pktio_entry_t *pktio_entry,
void *mac_addr)
{
- memcpy(mac_addr, pktio_entry->s.pkt_sock.if_mac, ETH_ALEN);
+ memcpy(mac_addr, pkt_priv(pktio_entry)->if_mac, ETH_ALEN);
return ETH_ALEN;
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_promisc_mode_set(pktio_entry_t *pktio_entry,
odp_bool_t enable)
{
- return promisc_mode_set_fd(pktio_entry->s.pkt_sock.sockfd,
- pktio_entry->s.name, enable);
+ return _odp_promisc_mode_set_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name, enable);
}
-/*
- * ODP_PACKET_SOCKET_MMSG:
- */
static int sock_promisc_mode_get(pktio_entry_t *pktio_entry)
{
- return promisc_mode_get_fd(pktio_entry->s.pkt_sock.sockfd,
- pktio_entry->s.name);
+ return _odp_promisc_mode_get_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name);
}
static int sock_link_status(pktio_entry_t *pktio_entry)
{
- return link_status_fd(pktio_entry->s.pkt_sock.sockfd,
- pktio_entry->s.name);
+ return _odp_link_status_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name);
+}
+
+static int sock_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ return _odp_link_info_fd(pkt_priv(pktio_entry)->sockfd, pktio_entry->name, info);
}
-static int sock_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
+static int sock_capability(pktio_entry_t *pktio_entry,
odp_pktio_capability_t *capa)
{
+ pkt_sock_t *pkt_sock = pkt_priv(pktio_entry);
+
memset(capa, 0, sizeof(odp_pktio_capability_t));
capa->max_input_queues = 1;
capa->max_output_queues = 1;
capa->set_op.op.promisc_mode = 1;
+ capa->set_op.op.maxlen = 1;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_input = pkt_sock->mtu_max;
+ capa->maxlen.min_output = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_output = pkt_sock->mtu_max;
odp_pktio_config_init(&capa->config);
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
+
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+
+ /* Fill statistics capabilities */
+ _odp_sock_stats_capa(pktio_entry, capa);
+
return 0;
}
static int sock_stats(pktio_entry_t *pktio_entry,
odp_pktio_stats_t *stats)
{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(stats, 0, sizeof(*stats));
- return 0;
- }
-
- return sock_stats_fd(pktio_entry,
- stats,
- pktio_entry->s.pkt_sock.sockfd);
+ return _odp_sock_stats_fd(pktio_entry, stats, pkt_priv(pktio_entry)->sockfd);
}
static int sock_stats_reset(pktio_entry_t *pktio_entry)
{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(&pktio_entry->s.stats, 0,
- sizeof(odp_pktio_stats_t));
- return 0;
- }
+ return _odp_sock_stats_reset_fd(pktio_entry, pkt_priv(pktio_entry)->sockfd);
+}
+
+static int sock_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[],
+ int num)
+{
+ return _odp_sock_extra_stat_info(pktio_entry, info, num,
+ pkt_priv(pktio_entry)->sockfd);
+}
- return sock_stats_reset_fd(pktio_entry,
- pktio_entry->s.pkt_sock.sockfd);
+static int sock_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[],
+ int num)
+{
+ return _odp_sock_extra_stats(pktio_entry, stats, num,
+ pkt_priv(pktio_entry)->sockfd);
+}
+
+static int sock_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat)
+{
+ return _odp_sock_extra_stat_counter(pktio_entry, id, stat,
+ pkt_priv(pktio_entry)->sockfd);
}
static int sock_init_global(void)
{
if (getenv("ODP_PKTIO_DISABLE_SOCKET_MMSG")) {
- ODP_PRINT("PKTIO: socket mmsg skipped,"
+ _ODP_PRINT("PKTIO: socket mmsg skipped,"
" enabled export ODP_PKTIO_DISABLE_SOCKET_MMSG=1.\n");
disable_pktio = 1;
} else {
- ODP_PRINT("PKTIO: initialized socket mmsg,"
- "use export ODP_PKTIO_DISABLE_SOCKET_MMSG=1 to disable.\n");
+ _ODP_PRINT("PKTIO: initialized socket mmsg,"
+ " use export ODP_PKTIO_DISABLE_SOCKET_MMSG=1 to disable.\n");
}
return 0;
}
-const pktio_if_ops_t sock_mmsg_pktio_ops = {
+const pktio_if_ops_t _odp_sock_mmsg_pktio_ops = {
.name = "socket",
.print = NULL,
.init_global = sock_init_global,
@@ -865,16 +654,26 @@ const pktio_if_ops_t sock_mmsg_pktio_ops = {
.stop = NULL,
.stats = sock_stats,
.stats_reset = sock_stats_reset,
+ .extra_stat_info = sock_extra_stat_info,
+ .extra_stats = sock_extra_stats,
+ .extra_stat_counter = sock_extra_stat_counter,
.recv = sock_mmsg_recv,
+ .recv_tmo = sock_recv_tmo,
+ .recv_mq_tmo = sock_recv_mq_tmo,
+ .fd_set = sock_fd_set,
.send = sock_mmsg_send,
- .mtu_get = sock_mtu_get,
+ .maxlen_get = sock_mtu_get,
+ .maxlen_set = sock_mtu_set,
.promisc_mode_set = sock_promisc_mode_set,
.promisc_mode_get = sock_promisc_mode_get,
.mac_get = sock_mac_addr_get,
+ .mac_set = NULL,
.link_status = sock_link_status,
+ .link_info = sock_link_info,
.capability = sock_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL,
.input_queues_config = NULL,
.output_queues_config = NULL,
diff --git a/platform/linux-generic/pktio/socket_common.c b/platform/linux-generic/pktio/socket_common.c
new file mode 100644
index 000000000..503df1685
--- /dev/null
+++ b/platform/linux-generic/pktio/socket_common.c
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019-2020 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/ethtool.h>
+#include <linux/if_packet.h>
+#include <linux/sockios.h>
+#include <errno.h>
+#include <odp_debug_internal.h>
+#include <odp_socket_common.h>
+#include <protocols/eth.h>
+
+/* Fallback for old kernels (< v4.6) */
+#ifndef ETHTOOL_GLINKSETTINGS
+#define ETHTOOL_GLINKSETTINGS 0x0000004c
+
+struct ethtool_link_settings {
+ uint32_t cmd;
+ uint32_t speed;
+ uint8_t duplex;
+ uint8_t port;
+ uint8_t phy_address;
+ uint8_t autoneg;
+ uint8_t mdio_support;
+ uint8_t eth_tp_mdix;
+ uint8_t eth_tp_mdix_ctrl;
+ int8_t link_mode_masks_nwords;
+ uint32_t reserved[8];
+ uint32_t link_mode_masks[0];
+};
+#endif
+
+/**
+ * ODP_PACKET_SOCKET_MMSG:
+ * ODP_PACKET_SOCKET_MMAP:
+ */
+int _odp_mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[])
+{
+ struct ifreq ethreq;
+ int ret;
+
+ memset(&ethreq, 0, sizeof(ethreq));
+ snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", name);
+ ret = ioctl(fd, SIOCGIFHWADDR, &ethreq);
+ if (ret != 0) {
+ _ODP_ERR("ioctl(SIOCGIFHWADDR): %s: \"%s\".\n", strerror(errno), ethreq.ifr_name);
+ return -1;
+ }
+
+ memcpy(mac_dst, (unsigned char *)ethreq.ifr_ifru.ifru_hwaddr.sa_data,
+ ETH_ALEN);
+ return 0;
+}
+
+/*
+ * ODP_PACKET_SOCKET_MMSG:
+ * ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_TAP:
+ */
+uint32_t _odp_mtu_get_fd(int fd, const char *name)
+{
+ struct ifreq ifr;
+ int ret;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+ ret = ioctl(fd, SIOCGIFMTU, &ifr);
+ if (ret < 0) {
+ _ODP_ERR("ioctl(SIOCGIFMTU): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return 0;
+ }
+ return ifr.ifr_mtu + _ODP_ETHHDR_LEN;
+}
+
+/*
+ * ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_SOCKET_MMSG:
+ * ODP_PACKET_TAP:
+ */
+int _odp_mtu_set_fd(int fd, const char *name, int mtu)
+{
+ struct ifreq ifr;
+ int ret;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+ ifr.ifr_mtu = mtu - _ODP_ETHHDR_LEN;
+
+ ret = ioctl(fd, SIOCSIFMTU, &ifr);
+ if (ret < 0) {
+ _ODP_ERR("ioctl(SIOCSIFMTU): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * ODP_PACKET_SOCKET_MMSG:
+ * ODP_PACKET_SOCKET_MMAP:
+ */
+int _odp_promisc_mode_set_fd(int fd, const char *name, int enable)
+{
+ struct ifreq ifr;
+ int ret;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+ ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (ret < 0) {
+ _ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+
+ if (enable)
+ ifr.ifr_flags |= IFF_PROMISC;
+ else
+ ifr.ifr_flags &= ~(IFF_PROMISC);
+
+ ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
+ if (ret < 0) {
+ _ODP_DBG("ioctl(SIOCSIFFLAGS): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * ODP_PACKET_SOCKET_MMSG:
+ * ODP_PACKET_SOCKET_MMAP:
+ */
+int _odp_promisc_mode_get_fd(int fd, const char *name)
+{
+ struct ifreq ifr;
+ int ret;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+ ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (ret < 0) {
+ _ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+
+ return !!(ifr.ifr_flags & IFF_PROMISC);
+}
+
+int _odp_link_status_fd(int fd, const char *name)
+{
+ struct ifreq ifr;
+ int ret;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+ ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (ret < 0) {
+ _ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return ODP_PKTIO_LINK_STATUS_UNKNOWN;
+ }
+
+ if (ifr.ifr_flags & IFF_RUNNING)
+ return ODP_PKTIO_LINK_STATUS_UP;
+ return ODP_PKTIO_LINK_STATUS_DOWN;
+}
+
+int _odp_link_info_fd(int fd, const char *name, odp_pktio_link_info_t *info)
+{
+ struct ethtool_link_settings hcmd = {.cmd = ETHTOOL_GLINKSETTINGS};
+ struct ethtool_link_settings *ecmd;
+ struct ethtool_pauseparam pcmd = {.cmd = ETHTOOL_GPAUSEPARAM};
+ struct ifreq ifr;
+ int status;
+
+ status = _odp_link_status_fd(fd, name);
+ if (status < 0)
+ return -1;
+
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+
+ /* Link pause status */
+ ifr.ifr_data = (void *)&pcmd;
+ if (ioctl(fd, SIOCETHTOOL, &ifr) && errno != EOPNOTSUPP) {
+ _ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+
+ /* Try to perform handshake and fall back to old API if failed */
+ ifr.ifr_data = (void *)&hcmd;
+ if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
+ struct ethtool_cmd ecmd_old = {.cmd = ETHTOOL_GSET};
+
+ ifr.ifr_data = (void *)&ecmd_old;
+ if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
+ _ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno),
+ ifr.ifr_name);
+ return -1;
+ }
+
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+ info->speed = ethtool_cmd_speed(&ecmd_old);
+ if (info->speed == (uint32_t)SPEED_UNKNOWN)
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+
+ if (ecmd_old.autoneg == AUTONEG_ENABLE)
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_ON;
+ else if (ecmd_old.autoneg == AUTONEG_DISABLE)
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ else
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_UNKNOWN;
+
+ if (ecmd_old.duplex == DUPLEX_HALF)
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_HALF;
+ else if (ecmd_old.duplex == DUPLEX_FULL)
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ else
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_UNKNOWN;
+
+ info->pause_rx = pcmd.rx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = pcmd.tx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF;
+
+ if (ecmd_old.port == PORT_TP)
+ info->media = "copper";
+ else if (ecmd_old.port == PORT_FIBRE)
+ info->media = "fiber";
+ else if (ecmd_old.port == PORT_OTHER)
+ info->media = "other";
+ else
+ info->media = "unknown";
+
+ info->status = status;
+
+ return 0;
+ }
+
+ if (hcmd.link_mode_masks_nwords >= 0 || hcmd.cmd != ETHTOOL_GLINKSETTINGS) {
+ _ODP_ERR("ETHTOOL_GLINKSETTINGS handshake failed\n");
+ return -1;
+ }
+ /* Absolute value indicates kernel recommended 'link_mode_masks_nwords' value. */
+ hcmd.link_mode_masks_nwords = -hcmd.link_mode_masks_nwords;
+
+ /* Reserve space for the three bitmasks (map_supported, map_advertising, map_lp_advertising)
+ * at the end of struct ethtool_link_settings. 'link_mode_masks_nwords' defines the bitmask
+ * length in 32-bit words. */
+ uint8_t data[offsetof(struct ethtool_link_settings, link_mode_masks) +
+ (3 * sizeof(uint32_t) * hcmd.link_mode_masks_nwords)] ODP_ALIGNED_CACHE;
+
+ ecmd = (void *)data;
+ *ecmd = hcmd;
+ ifr.ifr_data = (void *)ecmd;
+ if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
+ _ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno), ifr.ifr_name);
+ return -1;
+ }
+
+ memset(info, 0, sizeof(odp_pktio_link_info_t));
+ if (ecmd->speed == (uint32_t)SPEED_UNKNOWN)
+ info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN;
+ else
+ info->speed = ecmd->speed;
+
+ if (ecmd->autoneg == AUTONEG_ENABLE)
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_ON;
+ else if (ecmd->autoneg == AUTONEG_DISABLE)
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF;
+ else
+ info->autoneg = ODP_PKTIO_LINK_AUTONEG_UNKNOWN;
+
+ if (ecmd->duplex == DUPLEX_HALF)
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_HALF;
+ else if (ecmd->duplex == DUPLEX_FULL)
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL;
+ else
+ info->duplex = ODP_PKTIO_LINK_DUPLEX_UNKNOWN;
+
+ info->pause_rx = pcmd.rx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF;
+ info->pause_tx = pcmd.tx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF;
+
+ if (ecmd->port == PORT_TP)
+ info->media = "copper";
+ else if (ecmd->port == PORT_FIBRE)
+ info->media = "fiber";
+ else if (ecmd->port == PORT_OTHER)
+ info->media = "other";
+ else
+ info->media = "unknown";
+
+ info->status = status;
+
+ return 0;
+}
diff --git a/platform/linux-generic/pktio/socket_mmap.c b/platform/linux-generic/pktio/socket_mmap.c
index 6fc4b4ccf..e8457cac2 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -1,13 +1,31 @@
-/* Copyright (c) 2013, Linaro Limited
- * Copyright (c) 2013, Nokia Solutions and Networks
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2013-2023 Nokia Solutions and Networks
*/
#include <odp_posix_extensions.h>
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/packet_inlines.h>
+
+#include <odp_socket_common.h>
+#include <odp_parse_internal.h>
+#include <odp_packet_internal.h>
#include <odp_packet_io_internal.h>
+#include <odp_packet_io_stats.h>
+#include <odp_debug_internal.h>
+#include <odp_classification_datamodel.h>
+#include <odp_classification_internal.h>
+#include <odp_global_data.h>
+#include <odp_macros_internal.h>
+
+#include <protocols/eth.h>
+#include <protocols/ip.h>
#include <sys/socket.h>
#include <stdio.h>
@@ -21,51 +39,69 @@
#include <poll.h>
#include <sys/ioctl.h>
#include <errno.h>
+#include <time.h>
+#include <linux/if_packet.h>
+
+/* VLAN flags in tpacket2_hdr status */
+#ifdef TP_STATUS_VLAN_TPID_VALID
+#define VLAN_VALID (TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID)
+#else
+#define VLAN_VALID (TP_STATUS_VLAN_VALID)
+#endif
+
+/* Reserve 4MB memory for frames in a RX/TX ring */
+#define FRAME_MEM_SIZE (4 * 1024 * 1024)
+#define BLOCK_SIZE (4 * 1024)
+
+/** packet mmap ring */
+struct ring {
+ odp_ticketlock_t lock;
+ struct iovec *rd;
+ unsigned int frame_num;
+ int rd_num;
+
+ odp_shm_t shm;
+ int sock;
+ int type;
+ int version;
+ uint8_t *mm_space;
+ size_t mm_len;
+ size_t rd_len;
+ int flen;
+
+ struct tpacket_req req;
+};
-#include <odp_api.h>
-#include <odp_packet_socket.h>
-#include <odp_packet_internal.h>
-#include <odp_packet_io_internal.h>
-#include <odp_debug_internal.h>
-#include <odp_classification_datamodel.h>
-#include <odp_classification_inlines.h>
-#include <odp_classification_internal.h>
-#include <odp/api/hints.h>
-
-#include <protocols/eth.h>
-#include <protocols/ip.h>
-
-static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
-
-static int set_pkt_sock_fanout_mmap(pkt_sock_mmap_t *const pkt_sock,
- int sock_group_idx)
+ODP_STATIC_ASSERT(offsetof(struct ring, mm_space) <= ODP_CACHE_LINE_SIZE,
+ "ERR_STRUCT_RING");
+
+/** Packet socket using mmap rings for both Rx and Tx */
+typedef struct {
+ /** Packet mmap ring for Rx */
+ struct ring rx_ring ODP_ALIGNED_CACHE;
+ /** Packet mmap ring for Tx */
+ struct ring tx_ring ODP_ALIGNED_CACHE;
+
+ int sockfd ODP_ALIGNED_CACHE;
+ odp_pool_t pool;
+ int mtu; /**< maximum transmission unit */
+ uint32_t mtu_max; /**< maximum supported MTU value */
+ size_t frame_offset; /**< frame start offset from start of pkt buf */
+ uint8_t *mmap_base;
+ unsigned int mmap_len;
+ unsigned char if_mac[ETH_ALEN];
+ struct sockaddr_ll ll;
+} pkt_sock_mmap_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_sock_mmap_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static inline pkt_sock_mmap_t *pkt_priv(pktio_entry_t *pktio_entry)
{
- int sockfd = pkt_sock->sockfd;
- int val;
- int err;
- uint16_t fanout_group;
-
- fanout_group = (uint16_t)(sock_group_idx & 0xffff);
- val = (PACKET_FANOUT_HASH << 16) | fanout_group;
-
- err = setsockopt(sockfd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val));
- if (err != 0) {
- __odp_errno = errno;
- ODP_ERR("setsockopt(PACKET_FANOUT): %s\n", strerror(errno));
- return -1;
- }
- return 0;
+ return (pkt_sock_mmap_t *)(uintptr_t)(pktio_entry->pkt_priv);
}
-union frame_map {
- struct {
- struct tpacket2_hdr tp_h ODP_ALIGNED(TPACKET_ALIGNMENT);
- struct sockaddr_ll s_ll
- ODP_ALIGNED(TPACKET_ALIGN(sizeof(struct tpacket2_hdr)));
- } *v2;
-
- void *raw;
-};
+static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
static int mmap_pkt_socket(void)
{
@@ -74,15 +110,13 @@ static int mmap_pkt_socket(void)
int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock == -1) {
- __odp_errno = errno;
- ODP_ERR("socket(SOCK_RAW): %s\n", strerror(errno));
+ _ODP_ERR("socket(SOCK_RAW): %s\n", strerror(errno));
return -1;
}
ret = setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver));
if (ret == -1) {
- __odp_errno = errno;
- ODP_ERR("setsockopt(PACKET_VERSION): %s\n", strerror(errno));
+ _ODP_ERR("setsockopt(PACKET_VERSION): %s\n", strerror(errno));
close(sock);
return -1;
}
@@ -90,321 +124,379 @@ static int mmap_pkt_socket(void)
return sock;
}
-static inline int mmap_rx_kernel_ready(struct tpacket2_hdr *hdr)
-{
- return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER);
-}
-
-static inline void mmap_rx_user_ready(struct tpacket2_hdr *hdr)
-{
- hdr->tp_status = TP_STATUS_KERNEL;
- __sync_synchronize();
-}
-
-static inline int mmap_tx_kernel_ready(struct tpacket2_hdr *hdr)
-{
- return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING));
-}
-
-static inline void mmap_tx_user_ready(struct tpacket2_hdr *hdr)
-{
- hdr->tp_status = TP_STATUS_SEND_REQUEST;
- __sync_synchronize();
-}
-
-static uint8_t *pkt_mmap_vlan_insert(uint8_t *l2_hdr_ptr,
- uint16_t mac_offset,
- uint16_t vlan_tci,
- int *pkt_len_ptr)
+static inline unsigned next_frame(unsigned cur_frame, unsigned frame_count)
{
- _odp_ethhdr_t *eth_hdr;
- _odp_vlanhdr_t *vlan_hdr;
- uint8_t *new_l2_ptr;
- int orig_pkt_len;
-
- /* First try to see if the mac_offset is large enough to accommodate
- * shifting the Ethernet header down to open up space for the IEEE
- * 802.1Q vlan header.
- */
- if (_ODP_VLANHDR_LEN < mac_offset) {
- orig_pkt_len = *pkt_len_ptr;
- new_l2_ptr = l2_hdr_ptr - _ODP_VLANHDR_LEN;
- memmove(new_l2_ptr, l2_hdr_ptr, _ODP_ETHHDR_LEN);
-
- eth_hdr = (_odp_ethhdr_t *)new_l2_ptr;
- vlan_hdr = (_odp_vlanhdr_t *)(new_l2_ptr + _ODP_ETHHDR_LEN);
- vlan_hdr->tci = odp_cpu_to_be_16(vlan_tci);
- vlan_hdr->type = eth_hdr->type;
- eth_hdr->type = odp_cpu_to_be_16(_ODP_ETHTYPE_VLAN);
- *pkt_len_ptr = orig_pkt_len + _ODP_VLANHDR_LEN;
- return new_l2_ptr;
- }
-
- return l2_hdr_ptr;
+ return odp_unlikely(cur_frame + 1 >= frame_count) ? 0 : cur_frame + 1;
}
static inline unsigned pkt_mmap_v2_rx(pktio_entry_t *pktio_entry,
pkt_sock_mmap_t *pkt_sock,
- odp_packet_t pkt_table[], unsigned len,
+ odp_packet_t pkt_table[], unsigned num,
unsigned char if_mac[])
{
- union frame_map ppd;
odp_time_t ts_val;
odp_time_t *ts = NULL;
- unsigned frame_num, next_frame_num;
- uint8_t *pkt_buf;
+ unsigned int frame_num, next_frame_num;
+ uint8_t *pkt_buf, *next_ptr;
int pkt_len;
+ uint32_t alloc_len;
struct ethhdr *eth_hdr;
- unsigned i;
- unsigned nb_rx;
+ unsigned int i;
+ unsigned int nb_rx = 0;
+ unsigned int nb_cls = 0;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
struct ring *ring;
- int ret;
+ odp_pool_t pool = pkt_sock->pool;
+ uint16_t frame_offset = pktio_entry->pktin_frame_offset;
+ uint16_t vlan_len = 0;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp)
+ if (opt.bit.ts_all || opt.bit.ts_ptp)
ts = &ts_val;
ring = &pkt_sock->rx_ring;
frame_num = ring->frame_num;
+ next_ptr = ring->rd[frame_num].iov_base;
- for (i = 0, nb_rx = 0; i < len; i++) {
+ for (i = 0; i < num; i++) {
+ struct tpacket2_hdr *tp_hdr;
+ odp_packet_t pkt;
odp_packet_hdr_t *hdr;
- odp_packet_hdr_t parsed_hdr;
- odp_pool_t pool = pkt_sock->pool;
- int num;
+ int ret;
+
+ tp_hdr = (void *)next_ptr;
- if (!mmap_rx_kernel_ready(ring->rd[frame_num].iov_base))
+ if (tp_hdr->tp_status == TP_STATUS_KERNEL)
break;
+ next_frame_num = next_frame(frame_num, ring->rd_num);
+ next_ptr = ring->rd[next_frame_num].iov_base;
+ odp_prefetch(next_ptr);
+ odp_prefetch(next_ptr + ODP_CACHE_LINE_SIZE);
+
if (ts != NULL)
ts_val = odp_time_global();
- ppd.raw = ring->rd[frame_num].iov_base;
- next_frame_num = (frame_num + 1) % ring->rd_num;
+ pkt_buf = (uint8_t *)(void *)tp_hdr + tp_hdr->tp_mac;
+ pkt_len = tp_hdr->tp_snaplen;
- pkt_buf = (uint8_t *)ppd.raw + ppd.v2->tp_h.tp_mac;
- pkt_len = ppd.v2->tp_h.tp_snaplen;
+ if (odp_unlikely(pkt_len > pkt_sock->mtu)) {
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
+ frame_num = next_frame_num;
+ _ODP_DBG("dropped oversized packet\n");
+ continue;
+ }
+
+ /* Check if packet had a VLAN header */
+ if ((tp_hdr->tp_status & VLAN_VALID) == VLAN_VALID)
+ vlan_len = 4;
+
+ alloc_len = pkt_len + frame_offset + vlan_len;
+ ret = _odp_packet_alloc_multi(pool, alloc_len, &pkt, 1);
+
+ if (odp_unlikely(ret != 1)) {
+ /* Stop receiving packets when pool is empty. Leave
+ * the current frame into the ring. */
+ break;
+ }
/* Don't receive packets sent by ourselves */
eth_hdr = (struct ethhdr *)pkt_buf;
if (odp_unlikely(ethaddrs_equal(if_mac,
eth_hdr->h_source))) {
- mmap_rx_user_ready(ppd.raw); /* drop */
+ odp_packet_free(pkt);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
frame_num = next_frame_num;
continue;
}
- if (ppd.v2->tp_h.tp_status & TP_STATUS_VLAN_VALID)
- pkt_buf = pkt_mmap_vlan_insert(pkt_buf,
- ppd.v2->tp_h.tp_mac,
- ppd.v2->tp_h.tp_vlan_tci,
- &pkt_len);
+ hdr = packet_hdr(pkt);
- if (pktio_cls_enabled(pktio_entry)) {
- if (cls_classify_packet(pktio_entry, pkt_buf, pkt_len,
- pkt_len, &pool, &parsed_hdr)) {
- mmap_rx_user_ready(ppd.raw); /* drop */
- frame_num = next_frame_num;
- continue;
- }
- }
+ if (frame_offset)
+ pull_head(hdr, frame_offset);
- num = packet_alloc_multi(pool, pkt_len, &pkt_table[nb_rx], 1);
+ if (vlan_len)
+ pull_head(hdr, vlan_len);
- if (odp_unlikely(num != 1)) {
- pkt_table[nb_rx] = ODP_PACKET_INVALID;
- mmap_rx_user_ready(ppd.raw); /* drop */
- frame_num = next_frame_num;
- continue;
- }
- hdr = odp_packet_hdr(pkt_table[nb_rx]);
- ret = odp_packet_copy_from_mem(pkt_table[nb_rx], 0,
- pkt_len, pkt_buf);
+ ret = odp_packet_copy_from_mem(pkt, 0, pkt_len, pkt_buf);
if (ret != 0) {
- odp_packet_free(pkt_table[nb_rx]);
- mmap_rx_user_ready(ppd.raw); /* drop */
+ odp_packet_free(pkt);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
frame_num = next_frame_num;
continue;
}
- hdr->input = pktio_entry->s.handle;
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, hdr);
- else
- packet_parse_layer(hdr,
- pktio_entry->s.config.parser.layer);
+ if (vlan_len) {
+ /* Recreate VLAN header. Move MAC addresses and
+ * insert a VLAN header in between source MAC address
+ * and Ethernet type. */
+ uint8_t *mac;
+ uint16_t *type, *tci;
+
+ push_head(hdr, vlan_len);
+ mac = packet_data(hdr);
+ memmove(mac, mac + vlan_len, 2 * _ODP_ETHADDR_LEN);
+ type = (uint16_t *)(uintptr_t)
+ (mac + 2 * _ODP_ETHADDR_LEN);
+
+ #ifdef TP_STATUS_VLAN_TPID_VALID
+ *type = odp_cpu_to_be_16(tp_hdr->tp_vlan_tpid);
+ #else
+ /* Fallback for old kernels (< v3.14) */
+ uint16_t *type2;
+ static int warning_printed;
+
+ if (warning_printed == 0) {
+ _ODP_DBG("Original TPID value lost. Using 0x8100 for single tagged and 0x88a8 for double tagged.\n");
+ warning_printed = 1;
+ }
+ type2 = (uint16_t *)(uintptr_t)(mac + (2 * _ODP_ETHADDR_LEN) + vlan_len);
+ /* Recreate TPID 0x88a8 for double tagged and 0x8100 for single tagged */
+ if (*type2 == odp_cpu_to_be_16(0x8100))
+ *type = odp_cpu_to_be_16(0x88a8);
+ else
+ *type = odp_cpu_to_be_16(0x8100);
+ #endif
+
+ tci = type + 1;
+ *tci = odp_cpu_to_be_16(tp_hdr->tp_vlan_tci);
+ }
+
+ if (layer) {
+ ret = _odp_packet_parse_common(hdr, pkt_buf, pkt_len,
+ pkt_len, layer, opt);
+ if (ret)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors);
+
+ if (ret < 0) {
+ odp_packet_free(pkt);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
+ frame_num = next_frame_num;
+ continue;
+ }
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, pkt_buf,
+ &new_pool, hdr);
+ if (ret < 0)
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+
+ if (ret) {
+ odp_packet_free(pkt);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
+ frame_num = next_frame_num;
+ continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &hdr, new_pool))) {
+ odp_packet_free(pkt);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
+ frame_num = next_frame_num;
+ odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards);
+ continue;
+ }
+ }
+ }
+ hdr->input = pktio_entry->handle;
packet_set_ts(hdr, ts);
- mmap_rx_user_ready(ppd.raw);
+ tp_hdr->tp_status = TP_STATUS_KERNEL;
frame_num = next_frame_num;
- nb_rx++;
+ if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ pkt_table[nb_cls++] = pkt;
+ nb_cls = _odp_cls_enq(pkt_table, nb_cls, (i + 1 == num));
+ } else {
+ pkt_table[nb_rx++] = pkt;
+ }
}
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(nb_cls))
+ _odp_cls_enq(pkt_table, nb_cls, true);
+
ring->frame_num = frame_num;
+
return nb_rx;
}
-static inline unsigned pkt_mmap_v2_tx(int sock, struct ring *ring,
- const odp_packet_t pkt_table[],
- unsigned len)
+static inline int pkt_mmap_v2_tx(pktio_entry_t *pktio_entry, int sock,
+ struct ring *ring,
+ const odp_packet_t pkt_table[], uint32_t num)
{
- union frame_map ppd;
- uint32_t pkt_len;
- unsigned first_frame_num, frame_num, frame_count;
+ uint32_t i, pkt_len, num_tx, tp_status;
+ uint32_t first_frame_num, frame_num, next_frame_num, frame_count;
int ret;
uint8_t *buf;
- unsigned n, i = 0;
- unsigned nb_tx = 0;
- int send_errno;
+ void *next_ptr;
+ struct tpacket2_hdr *tp_hdr[num];
int total_len = 0;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
+ uint32_t tx_ts_idx = 0;
- first_frame_num = ring->frame_num;
- frame_num = first_frame_num;
+ frame_num = ring->frame_num;
+ first_frame_num = frame_num;
frame_count = ring->rd_num;
+ next_ptr = ring->rd[frame_num].iov_base;
+
+ if (num > frame_count)
+ num = frame_count;
+
+ for (i = 0; i < num; i++) {
+ tp_hdr[i] = next_ptr;
+ tp_status = tp_hdr[i]->tp_status & 0x7;
+
+ if (tp_status != TP_STATUS_AVAILABLE) {
+ if (tp_status == TP_STATUS_WRONG_FORMAT) {
+ _ODP_ERR("Socket mmap: wrong format\n");
+ return -1;
+ }
- while (i < len) {
- ppd.raw = ring->rd[frame_num].iov_base;
- if (!odp_unlikely(mmap_tx_kernel_ready(ppd.raw)))
break;
+ }
+
+ next_frame_num = next_frame(frame_num, frame_count);
+ next_ptr = ring->rd[next_frame_num].iov_base;
+ odp_prefetch(next_ptr);
pkt_len = odp_packet_len(pkt_table[i]);
- ppd.v2->tp_h.tp_snaplen = pkt_len;
- ppd.v2->tp_h.tp_len = pkt_len;
+ tp_hdr[i]->tp_len = pkt_len;
total_len += pkt_len;
- buf = (uint8_t *)ppd.raw + TPACKET2_HDRLEN -
+ buf = (uint8_t *)(void *)tp_hdr[i] + TPACKET2_HDRLEN -
sizeof(struct sockaddr_ll);
odp_packet_copy_to_mem(pkt_table[i], 0, pkt_len, buf);
- mmap_tx_user_ready(ppd.raw);
+ tp_hdr[i]->tp_status = TP_STATUS_SEND_REQUEST;
- if (++frame_num >= frame_count)
- frame_num = 0;
+ frame_num = next_frame_num;
- i++;
+ if (tx_ts_enabled && tx_ts_idx == 0) {
+ if (odp_unlikely(packet_hdr(pkt_table[i])->p.flags.ts_set))
+ tx_ts_idx = i + 1;
+ }
}
- ret = sendto(sock, NULL, 0, MSG_DONTWAIT, NULL, 0);
- send_errno = errno;
-
- /* On success, the return value indicates the number of bytes sent. On
- * failure a value of -1 is returned, even if the failure occurred
- * after some of the packets in the ring have already been sent, so we
- * need to inspect the packet status to determine which were sent. */
- if (odp_likely(ret == total_len)) {
- nb_tx = i;
- ring->frame_num = frame_num;
- } else if (ret == -1) {
- for (frame_num = first_frame_num, n = 0; n < i; ++n) {
- struct tpacket2_hdr *hdr = ring->rd[frame_num].iov_base;
-
- if (odp_likely(hdr->tp_status == TP_STATUS_AVAILABLE ||
- hdr->tp_status == TP_STATUS_SENDING)) {
- nb_tx++;
- } else {
- /* The remaining frames weren't sent, clear
- * their status to indicate we're not waiting
- * for the kernel to process them. */
- hdr->tp_status = TP_STATUS_AVAILABLE;
- }
+ num = i;
+ num_tx = num;
- if (++frame_num >= frame_count)
- frame_num = 0;
- }
+ /* Ping kernel to send packets */
+ ret = send(sock, NULL, 0, MSG_DONTWAIT);
+
+ ring->frame_num = frame_num;
- ring->frame_num = (first_frame_num + nb_tx) % frame_count;
+ if (odp_unlikely(ret != total_len)) {
+ uint32_t frame_sum;
- if (nb_tx == 0 && SOCK_ERR_REPORT(send_errno)) {
- __odp_errno = send_errno;
- /* ENOBUFS indicates that the transmit queue is full,
- * which will happen regularly when overloaded so don't
- * print it */
- if (errno != ENOBUFS)
- ODP_ERR("sendto(pkt mmap): %s\n",
- strerror(send_errno));
+ /* Returns -1 when nothing is sent (send() would block) */
+ if (ret < 0 && errno != EWOULDBLOCK) {
+ _ODP_ERR("Socket mmap: send failed, ret %i, errno %i\n", ret, errno);
return -1;
}
- } else {
- /* Short send, return value is number of bytes sent so use this
- * to determine number of complete frames sent. */
- for (n = 0; n < i && ret > 0; ++n) {
- ret -= odp_packet_len(pkt_table[n]);
- if (ret >= 0)
- nb_tx++;
+
+ /* Check how many first packets have been sent
+ * (TP_STATUS_AVAILABLE or TP_STATUS_SENDING). Assuming that
+ * the rest will not be sent. */
+ for (i = 0; i < num; i++) {
+ tp_status = tp_hdr[i]->tp_status & 0x7;
+
+ if (tp_status == TP_STATUS_SEND_REQUEST)
+ break;
+
+ if (tp_status == TP_STATUS_WRONG_FORMAT) {
+ _ODP_ERR("Socket mmap: wrong format\n");
+ break;
+ }
}
- ring->frame_num = (first_frame_num + nb_tx) % frame_count;
- }
+ num_tx = i;
- for (i = 0; i < nb_tx; ++i)
- odp_packet_free(pkt_table[i]);
+ /* Clear status of not sent packets */
+ for (i = num_tx; i < num; i++)
+ tp_hdr[i]->tp_status = TP_STATUS_AVAILABLE;
- return nb_tx;
-}
+ frame_sum = first_frame_num + num_tx;
+ ring->frame_num = frame_sum;
-static void mmap_fill_ring(struct ring *ring, odp_pool_t pool_hdl, int fanout)
-{
- int pz = getpagesize();
- pool_t *pool;
-
- if (pool_hdl == ODP_POOL_INVALID)
- ODP_ABORT("Invalid pool handle\n");
-
- pool = pool_entry_from_hdl(pool_hdl);
-
- /* Frame has to capture full packet which can fit to the pool block.*/
- ring->req.tp_frame_size = (pool->headroom + pool->data_size +
- pool->tailroom + TPACKET_HDRLEN +
- TPACKET_ALIGNMENT + + (pz - 1)) & (-pz);
-
- /* Calculate how many pages do we need to hold all pool packets
- * and align size to page boundary.
- */
- ring->req.tp_block_size = (ring->req.tp_frame_size *
- pool->num + (pz - 1)) & (-pz);
-
- if (!fanout) {
- /* Single socket is in use. Use 1 block with buf_num frames. */
- ring->req.tp_block_nr = 1;
- } else {
- /* Fanout is in use, more likely taffic split accodring to
- * number of cpu threads. Use cpu blocks and buf_num frames. */
- ring->req.tp_block_nr = odp_cpu_count();
+ if (frame_sum >= frame_count)
+ ring->frame_num = frame_sum - frame_count;
}
- ring->req.tp_frame_nr = ring->req.tp_block_size /
- ring->req.tp_frame_size * ring->req.tp_block_nr;
+ if (odp_unlikely(tx_ts_idx && num_tx >= tx_ts_idx))
+ _odp_pktio_tx_ts_set(pktio_entry);
- ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr;
- ring->rd_num = ring->req.tp_frame_nr;
- ring->flen = ring->req.tp_frame_size;
+ /* Free sent packets */
+ odp_packet_free_multi(pkt_table, num_tx);
+
+ return num_tx;
}
-static int mmap_setup_ring(int sock, struct ring *ring, int type,
- odp_pool_t pool_hdl, int fanout)
+static int mmap_setup_ring(pkt_sock_mmap_t *pkt_sock, struct ring *ring,
+ int type)
{
+ odp_shm_t shm;
+ uint32_t block_size, block_nr, frame_size, frame_nr;
+ uint32_t ring_size;
+ int flags;
+ int sock = pkt_sock->sockfd;
+ int mtu = pkt_sock->mtu_max;
int ret = 0;
ring->sock = sock;
ring->type = type;
ring->version = TPACKET_V2;
- mmap_fill_ring(ring, pool_hdl, fanout);
+ frame_size = _ODP_ROUNDUP_POWER2_U32(mtu + TPACKET_HDRLEN + TPACKET_ALIGNMENT);
+ block_size = BLOCK_SIZE;
+ if (frame_size > block_size)
+ block_size = frame_size;
+
+ block_nr = FRAME_MEM_SIZE / block_size;
+ frame_nr = (block_size / frame_size) * block_nr;
+ ring_size = frame_nr * sizeof(struct iovec);
+ flags = 0;
+
+ if (odp_global_ro.shm_single_va)
+ flags += ODP_SHM_SINGLE_VA;
+
+ shm = odp_shm_reserve(NULL, ring_size, ODP_CACHE_LINE_SIZE, flags);
+
+ if (shm == ODP_SHM_INVALID) {
+ _ODP_ERR("Reserving shm failed\n");
+ return -1;
+ }
+ ring->shm = shm;
+
+ ring->req.tp_block_size = block_size;
+ ring->req.tp_block_nr = block_nr;
+ ring->req.tp_frame_size = frame_size;
+ ring->req.tp_frame_nr = frame_nr;
+
+ ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr;
+ ring->rd_num = ring->req.tp_frame_nr;
+ ring->flen = ring->req.tp_frame_size;
+ ring->rd_len = ring_size;
+
+ _ODP_DBG(" tp_block_size %u\n", ring->req.tp_block_size);
+ _ODP_DBG(" tp_block_nr %u\n", ring->req.tp_block_nr);
+ _ODP_DBG(" tp_frame_size %u\n", ring->req.tp_frame_size);
+ _ODP_DBG(" tp_frame_nr %u\n", ring->req.tp_frame_nr);
ret = setsockopt(sock, SOL_PACKET, type, &ring->req, sizeof(ring->req));
if (ret == -1) {
- __odp_errno = errno;
- ODP_ERR("setsockopt(pkt mmap): %s\n", strerror(errno));
+ _ODP_ERR("setsockopt(pkt mmap): %s\n", strerror(errno));
return -1;
}
- ring->rd_len = ring->rd_num * sizeof(*ring->rd);
- ring->rd = malloc(ring->rd_len);
+ ring->rd = odp_shm_addr(shm);
if (!ring->rd) {
- __odp_errno = errno;
- ODP_ERR("malloc(): %s\n", strerror(errno));
+ _ODP_ERR("Reading shm addr failed\n");
return -1;
}
@@ -428,8 +520,7 @@ static int mmap_sock(pkt_sock_mmap_t *pkt_sock)
MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0);
if (pkt_sock->mmap_base == MAP_FAILED) {
- __odp_errno = errno;
- ODP_ERR("mmap rx&tx buffer failed: %s\n", strerror(errno));
+ _ODP_ERR("mmap rx&tx buffer failed: %s\n", strerror(errno));
return -1;
}
@@ -457,9 +548,17 @@ static int mmap_sock(pkt_sock_mmap_t *pkt_sock)
static int mmap_unmap_sock(pkt_sock_mmap_t *pkt_sock)
{
- free(pkt_sock->rx_ring.rd);
- free(pkt_sock->tx_ring.rd);
- return munmap(pkt_sock->mmap_base, pkt_sock->mmap_len);
+ int ret = 0;
+
+ if (pkt_sock->rx_ring.shm != ODP_SHM_INVALID)
+ odp_shm_free(pkt_sock->rx_ring.shm);
+ if (pkt_sock->tx_ring.shm != ODP_SHM_INVALID)
+ odp_shm_free(pkt_sock->tx_ring.shm);
+
+ if (pkt_sock->mmap_base != MAP_FAILED)
+ ret = munmap(pkt_sock->mmap_base, pkt_sock->mmap_len);
+
+ return ret;
}
static int mmap_bind_sock(pkt_sock_mmap_t *pkt_sock, const char *netdev)
@@ -476,8 +575,7 @@ static int mmap_bind_sock(pkt_sock_mmap_t *pkt_sock, const char *netdev)
ret = bind(pkt_sock->sockfd, (struct sockaddr *)&pkt_sock->ll,
sizeof(pkt_sock->ll));
if (ret == -1) {
- __odp_errno = errno;
- ODP_ERR("bind(to IF): %s\n", strerror(errno));
+ _ODP_ERR("bind(to IF): %s\n", strerror(errno));
return -1;
}
@@ -486,18 +584,17 @@ static int mmap_bind_sock(pkt_sock_mmap_t *pkt_sock, const char *netdev)
static int sock_mmap_close(pktio_entry_t *entry)
{
- pkt_sock_mmap_t *const pkt_sock = &entry->s.pkt_sock_mmap;
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(entry);
int ret;
ret = mmap_unmap_sock(pkt_sock);
if (ret != 0) {
- ODP_ERR("mmap_unmap_sock() %s\n", strerror(errno));
+ _ODP_ERR("mmap_unmap_sock() %s\n", strerror(errno));
return -1;
}
if (pkt_sock->sockfd != -1 && close(pkt_sock->sockfd) != 0) {
- __odp_errno = errno;
- ODP_ERR("close(sockfd): %s\n", strerror(errno));
+ _ODP_ERR("close(sockfd): %s\n", strerror(errno));
return -1;
}
@@ -510,18 +607,17 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
{
int if_idx;
int ret = 0;
- odp_pktio_stats_t cur_stats;
if (disable_pktio)
return -1;
- pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
- int fanout = 1;
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(pktio_entry);
/* Init pktio entry */
memset(pkt_sock, 0, sizeof(*pkt_sock));
/* set sockfd to -1, because a valid socked might be initialized to 0 */
pkt_sock->sockfd = -1;
+ pkt_sock->mmap_base = MAP_FAILED;
if (pool == ODP_POOL_INVALID)
return -1;
@@ -530,6 +626,10 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
pkt_sock->frame_offset = 0;
pkt_sock->pool = pool;
+ odp_ticketlock_init(&pkt_sock->rx_ring.lock);
+ odp_ticketlock_init(&pkt_sock->tx_ring.lock);
+ pkt_sock->rx_ring.shm = ODP_SHM_INVALID;
+ pkt_sock->tx_ring.shm = ODP_SHM_INVALID;
pkt_sock->sockfd = mmap_pkt_socket();
if (pkt_sock->sockfd == -1)
goto error;
@@ -538,13 +638,22 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
if (ret != 0)
goto error;
- ret = mmap_setup_ring(pkt_sock->sockfd, &pkt_sock->tx_ring,
- PACKET_TX_RING, pool, fanout);
+ pkt_sock->mtu = _odp_mtu_get_fd(pkt_sock->sockfd, netdev);
+ if (!pkt_sock->mtu)
+ goto error;
+ pkt_sock->mtu_max = _ODP_SOCKET_MTU_MAX;
+ if (pkt_sock->mtu > _ODP_SOCKET_MTU_MAX)
+ pkt_sock->mtu_max = pkt_sock->mtu;
+
+ _ODP_DBG("MTU size: %i\n", pkt_sock->mtu);
+
+ _ODP_DBG("TX ring setup:\n");
+ ret = mmap_setup_ring(pkt_sock, &pkt_sock->tx_ring, PACKET_TX_RING);
if (ret != 0)
goto error;
- ret = mmap_setup_ring(pkt_sock->sockfd, &pkt_sock->rx_ring,
- PACKET_RX_RING, pool, fanout);
+ _ODP_DBG("RX ring setup:\n");
+ ret = mmap_setup_ring(pkt_sock, &pkt_sock->rx_ring, PACKET_RX_RING);
if (ret != 0)
goto error;
@@ -552,42 +661,23 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
if (ret != 0)
goto error;
- ret = mac_addr_get_fd(pkt_sock->sockfd, netdev, pkt_sock->if_mac);
+ ret = _odp_mac_addr_get_fd(pkt_sock->sockfd, netdev, pkt_sock->if_mac);
if (ret != 0)
goto error;
if_idx = if_nametoindex(netdev);
if (if_idx == 0) {
- __odp_errno = errno;
- ODP_ERR("if_nametoindex(): %s\n", strerror(errno));
+ _ODP_ERR("if_nametoindex(): %s\n", strerror(errno));
goto error;
}
- pkt_sock->fanout = fanout;
- if (fanout) {
- ret = set_pkt_sock_fanout_mmap(pkt_sock, if_idx);
- if (ret != 0)
- goto error;
- }
-
- ret = ethtool_stats_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
- pktio_entry->s.name,
- &cur_stats);
- if (ret != 0) {
- ret = sysfs_stats(pktio_entry, &cur_stats);
- if (ret != 0) {
- pktio_entry->s.stats_type = STATS_UNSUPPORTED;
- ODP_DBG("pktio: %s unsupported stats\n",
- pktio_entry->s.name);
- } else {
- pktio_entry->s.stats_type = STATS_SYSFS;
- }
- } else {
- pktio_entry->s.stats_type = STATS_ETHTOOL;
- }
+ pktio_entry->stats_type = _odp_sock_stats_type_fd(pktio_entry,
+ pkt_sock->sockfd);
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED)
+ _ODP_DBG("pktio: %s unsupported stats\n", pktio_entry->name);
- ret = sock_stats_reset_fd(pktio_entry,
- pktio_entry->s.pkt_sock_mmap.sockfd);
+ ret = _odp_sock_stats_reset_fd(pktio_entry,
+ pkt_priv(pktio_entry)->sockfd);
if (ret != 0)
goto error;
@@ -598,119 +688,268 @@ error:
return -1;
}
+static int sock_mmap_fd_set(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
+ fd_set *readfds)
+{
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(pktio_entry);
+ int fd;
+
+ odp_ticketlock_lock(&pktio_entry->rxl);
+ fd = pkt_sock->sockfd;
+ FD_SET(fd, readfds);
+ odp_ticketlock_unlock(&pktio_entry->rxl);
+
+ return fd;
+}
+
static int sock_mmap_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkt_table[], int len)
+ odp_packet_t pkt_table[], int num)
{
- pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(pktio_entry);
int ret;
- odp_ticketlock_lock(&pktio_entry->s.rxl);
- ret = pkt_mmap_v2_rx(pktio_entry, pkt_sock, pkt_table, len,
+ odp_ticketlock_lock(&pkt_sock->rx_ring.lock);
+ ret = pkt_mmap_v2_rx(pktio_entry, pkt_sock, pkt_table, num,
pkt_sock->if_mac);
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ odp_ticketlock_unlock(&pkt_sock->rx_ring.lock);
return ret;
}
+static int sock_mmap_recv_tmo(pktio_entry_t *pktio_entry, int index,
+ odp_packet_t pkt_table[], int num, uint64_t usecs)
+{
+ struct timeval timeout;
+ int ret;
+ int maxfd;
+ fd_set readfds;
+
+ ret = sock_mmap_recv(pktio_entry, index, pkt_table, num);
+ if (ret != 0)
+ return ret;
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ FD_ZERO(&readfds);
+ maxfd = sock_mmap_fd_set(pktio_entry, index, &readfds);
+
+ while (1) {
+ ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+
+ if (ret <= 0)
+ return ret;
+
+ ret = sock_mmap_recv(pktio_entry, index, pkt_table, num);
+
+ if (ret)
+ return ret;
+
+ /* If no packets, continue wait until timeout expires */
+ }
+}
+
+static int sock_mmap_recv_mq_tmo(pktio_entry_t *pktio_entry[], int index[],
+ uint32_t num_q, odp_packet_t pkt_table[], int num,
+ uint32_t *from, uint64_t usecs)
+{
+ struct timeval timeout;
+ uint32_t i;
+ int ret;
+ int maxfd = -1, maxfd2;
+ fd_set readfds;
+
+ for (i = 0; i < num_q; i++) {
+ ret = sock_mmap_recv(pktio_entry[i], index[i], pkt_table, num);
+
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret != 0)
+ return ret;
+ }
+
+ FD_ZERO(&readfds);
+
+ for (i = 0; i < num_q; i++) {
+ maxfd2 = sock_mmap_fd_set(pktio_entry[i], index[i], &readfds);
+ if (maxfd2 > maxfd)
+ maxfd = maxfd2;
+ }
+
+ timeout.tv_sec = usecs / (1000 * 1000);
+ timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL);
+
+ while (1) {
+ ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
+
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0; i < num_q; i++) {
+ ret = sock_mmap_recv(pktio_entry[i], index[i],
+ pkt_table, num);
+
+ if (ret > 0 && from)
+ *from = i;
+
+ if (ret)
+ return ret;
+ }
+
+ /* If no packets, continue wait until timeout expires */
+ }
+}
+
static int sock_mmap_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkt_table[], int len)
+ const odp_packet_t pkt_table[], int num)
{
int ret;
- pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(pktio_entry);
- odp_ticketlock_lock(&pktio_entry->s.txl);
- ret = pkt_mmap_v2_tx(pkt_sock->tx_ring.sock, &pkt_sock->tx_ring,
- pkt_table, len);
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ odp_ticketlock_lock(&pkt_sock->tx_ring.lock);
+ ret = pkt_mmap_v2_tx(pktio_entry, pkt_sock->tx_ring.sock,
+ &pkt_sock->tx_ring, pkt_table, num);
+ odp_ticketlock_unlock(&pkt_sock->tx_ring.lock);
return ret;
}
static uint32_t sock_mmap_mtu_get(pktio_entry_t *pktio_entry)
{
- return mtu_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
- pktio_entry->s.name);
+ return _odp_mtu_get_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name);
+}
+
+static int sock_mmap_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_sock_mmap_t *pkt_sock = pkt_priv(pktio_entry);
+ int ret;
+
+ ret = _odp_mtu_set_fd(pkt_sock->sockfd, pktio_entry->name, maxlen_input);
+ if (ret)
+ return ret;
+
+ pkt_sock->mtu = maxlen_input;
+
+ return 0;
}
static int sock_mmap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
{
- memcpy(mac_addr, pktio_entry->s.pkt_sock_mmap.if_mac, ETH_ALEN);
+ memcpy(mac_addr, pkt_priv(pktio_entry)->if_mac, ETH_ALEN);
return ETH_ALEN;
}
static int sock_mmap_promisc_mode_set(pktio_entry_t *pktio_entry,
odp_bool_t enable)
{
- return promisc_mode_set_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
- pktio_entry->s.name, enable);
+ return _odp_promisc_mode_set_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name, enable);
}
static int sock_mmap_promisc_mode_get(pktio_entry_t *pktio_entry)
{
- return promisc_mode_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
- pktio_entry->s.name);
+ return _odp_promisc_mode_get_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name);
}
static int sock_mmap_link_status(pktio_entry_t *pktio_entry)
{
- return link_status_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
- pktio_entry->s.name);
+ return _odp_link_status_fd(pkt_priv(pktio_entry)->sockfd,
+ pktio_entry->name);
+}
+
+static int sock_mmap_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ return _odp_link_info_fd(pkt_priv(pktio_entry)->sockfd, pktio_entry->name, info);
}
-static int sock_mmap_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
+static int sock_mmap_capability(pktio_entry_t *pktio_entry,
odp_pktio_capability_t *capa)
{
+ pkt_sock_mmap_t *const pkt_sock = pkt_priv(pktio_entry);
+
memset(capa, 0, sizeof(odp_pktio_capability_t));
capa->max_input_queues = 1;
capa->max_output_queues = 1;
capa->set_op.op.promisc_mode = 1;
+ capa->set_op.op.maxlen = 1;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_input = pkt_sock->mtu_max;
+ capa->maxlen.min_output = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_output = pkt_sock->mtu_max;
odp_pktio_config_init(&capa->config);
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
+
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+
+ /* Fill statistics capabilities */
+ _odp_sock_stats_capa(pktio_entry, capa);
+
return 0;
}
static int sock_mmap_stats(pktio_entry_t *pktio_entry,
odp_pktio_stats_t *stats)
{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(stats, 0, sizeof(*stats));
- return 0;
- }
-
- return sock_stats_fd(pktio_entry,
- stats,
- pktio_entry->s.pkt_sock_mmap.sockfd);
+ return _odp_sock_stats_fd(pktio_entry,
+ stats,
+ pkt_priv(pktio_entry)->sockfd);
}
static int sock_mmap_stats_reset(pktio_entry_t *pktio_entry)
{
- if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
- memset(&pktio_entry->s.stats, 0,
- sizeof(odp_pktio_stats_t));
- return 0;
- }
+ return _odp_sock_stats_reset_fd(pktio_entry,
+ pkt_priv(pktio_entry)->sockfd);
+}
+
+static int sock_mmap_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[],
+ int num)
+{
+ return _odp_sock_extra_stat_info(pktio_entry, info, num,
+ pkt_priv(pktio_entry)->sockfd);
+}
+
+static int sock_mmap_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[],
+ int num)
+{
+ return _odp_sock_extra_stats(pktio_entry, stats, num,
+ pkt_priv(pktio_entry)->sockfd);
+}
- return sock_stats_reset_fd(pktio_entry,
- pktio_entry->s.pkt_sock_mmap.sockfd);
+static int sock_mmap_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat)
+{
+ return _odp_sock_extra_stat_counter(pktio_entry, id, stat,
+ pkt_priv(pktio_entry)->sockfd);
}
static int sock_mmap_init_global(void)
{
if (getenv("ODP_PKTIO_DISABLE_SOCKET_MMAP")) {
- ODP_PRINT("PKTIO: socket mmap skipped,"
+ _ODP_PRINT("PKTIO: socket mmap skipped,"
" enabled export ODP_PKTIO_DISABLE_SOCKET_MMAP=1.\n");
disable_pktio = 1;
} else {
- ODP_PRINT("PKTIO: initialized socket mmap,"
+ _ODP_PRINT("PKTIO: initialized socket mmap,"
" use export ODP_PKTIO_DISABLE_SOCKET_MMAP=1 to disable.\n");
}
return 0;
}
-const pktio_if_ops_t sock_mmap_pktio_ops = {
+const pktio_if_ops_t _odp_sock_mmap_pktio_ops = {
.name = "socket_mmap",
.print = NULL,
.init_global = sock_mmap_init_global,
@@ -722,16 +961,26 @@ const pktio_if_ops_t sock_mmap_pktio_ops = {
.stop = NULL,
.stats = sock_mmap_stats,
.stats_reset = sock_mmap_stats_reset,
+ .extra_stat_info = sock_mmap_extra_stat_info,
+ .extra_stats = sock_mmap_extra_stats,
+ .extra_stat_counter = sock_mmap_extra_stat_counter,
.recv = sock_mmap_recv,
+ .recv_tmo = sock_mmap_recv_tmo,
+ .recv_mq_tmo = sock_mmap_recv_mq_tmo,
.send = sock_mmap_send,
- .mtu_get = sock_mmap_mtu_get,
+ .fd_set = sock_mmap_fd_set,
+ .maxlen_get = sock_mmap_mtu_get,
+ .maxlen_set = sock_mmap_mtu_set,
.promisc_mode_set = sock_mmap_promisc_mode_set,
.promisc_mode_get = sock_mmap_promisc_mode_get,
.mac_get = sock_mmap_mac_addr_get,
+ .mac_set = NULL,
.link_status = sock_mmap_link_status,
+ .link_info = sock_mmap_link_info,
.capability = sock_mmap_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL,
.input_queues_config = NULL,
.output_queues_config = NULL,
diff --git a/platform/linux-generic/pktio/socket_xdp.c b/platform/linux-generic/pktio/socket_xdp.c
new file mode 100644
index 000000000..2cd7b080b
--- /dev/null
+++ b/platform/linux-generic/pktio/socket_xdp.c
@@ -0,0 +1,1247 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+#include <odp/autoheader_internal.h>
+
+#ifdef _ODP_PKTIO_XDP
+
+#include <odp_posix_extensions.h>
+#include <odp/api/cpu.h>
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io_stats.h>
+#include <odp/api/system_info.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp_classification_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_libconfig_internal.h>
+#include <odp_macros_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_parse_internal.h>
+#include <odp_pool_internal.h>
+#include <odp_socket_common.h>
+
+#include <errno.h>
+#include <linux/ethtool.h>
+#include <linux/if_xdp.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <xdp/xsk.h>
+
+#define NUM_DESCS_DEFAULT 1024U
+#define MIN_FRAME_SIZE 2048U
+
+#define MAX_QUEUES (ODP_PKTIN_MAX_QUEUES > ODP_PKTOUT_MAX_QUEUES ? \
+ ODP_PKTIN_MAX_QUEUES : ODP_PKTOUT_MAX_QUEUES)
+
+#define IF_DELIM " "
+#define Q_DELIM ':'
+#define CONF_BASE_STR "pktio_xdp"
+#define RX_DESCS_STR "num_rx_desc"
+#define TX_DESCS_STR "num_tx_desc"
+
+enum {
+ RX_PKT_ALLOC_ERR,
+ RX_DESC_RSV_ERR,
+ TX_PKT_ALLOC_ERR,
+ TX_DESC_RSV_ERR
+};
+
+static const char * const internal_stats_strs[] = {
+ "rx_packet_allocation_errors",
+ "rx_umem_descriptor_reservation_errors",
+ "tx_packet_allocation_errors",
+ "tx_umem_descriptor_reservation_errors"
+};
+
+#define MAX_INTERNAL_STATS _ODP_ARRAY_SIZE(internal_stats_strs)
+
+static const char * const shadow_q_driver_strs[] = {
+ "mlx",
+};
+
+typedef struct {
+ uint64_t rx_dropped;
+ uint64_t rx_inv_descs;
+ uint64_t tx_inv_descs;
+} xdp_sock_stats_t;
+
+typedef struct {
+ odp_ticketlock_t rx_lock ODP_ALIGNED_CACHE;
+ odp_ticketlock_t tx_lock ODP_ALIGNED_CACHE;
+ struct xsk_ring_cons rx;
+ struct xsk_ring_cons compl_q;
+ struct xsk_ring_prod tx;
+ struct xsk_ring_prod fill_q;
+ odp_pktin_queue_stats_t qi_stats;
+ odp_pktout_queue_stats_t qo_stats;
+ xdp_sock_stats_t xdp_stats;
+ struct xsk_socket *xsk;
+ uint64_t i_stats[MAX_INTERNAL_STATS];
+} xdp_sock_t;
+
+typedef struct {
+ struct xsk_ring_prod fill_q;
+ struct xsk_ring_cons compl_q;
+ struct xsk_umem *umem;
+ pool_t *pool;
+ int num_rx_desc;
+ int num_tx_desc;
+ uint32_t ref_cnt;
+} xdp_umem_info_t;
+
+typedef struct {
+ uint32_t rx;
+ uint32_t tx;
+ uint32_t other;
+ uint32_t combined;
+} drv_channels_t;
+
+typedef struct {
+ /* Queue counts for getting/setting driver's ethtool queue configuration. */
+ drv_channels_t drv_channels;
+ /* Packet I/O level requested input queue count. */
+ uint32_t num_in_conf_qs;
+ /* Packet I/O level requested output queue count. */
+ uint32_t num_out_conf_qs;
+ /* Actual internal queue count. */
+ uint32_t num_qs;
+ /* Length of driver's ethtool RSS indirection table. */
+ uint32_t drv_num_rss;
+} q_num_conf_t;
+
+typedef struct {
+ xdp_sock_t qs[MAX_QUEUES];
+ xdp_umem_info_t *umem_info;
+ q_num_conf_t q_num_conf;
+ int pktio_idx;
+ int helper_sock;
+ uint32_t mtu;
+ uint32_t max_mtu;
+ uint32_t bind_q;
+ odp_bool_t lockless_rx;
+ odp_bool_t lockless_tx;
+ odp_bool_t is_shadow_q;
+} xdp_sock_info_t;
+
+typedef struct {
+ odp_packet_hdr_t *pkt_hdr;
+ odp_packet_t pkt;
+ uint8_t *data;
+ uint32_t len;
+} pkt_data_t;
+
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(xdp_sock_info_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static odp_bool_t disable_pktio;
+
+static int sock_xdp_init_global(void)
+{
+ if (getenv("ODP_PKTIO_DISABLE_SOCKET_XDP")) {
+ _ODP_PRINT("PKTIO: socket xdp skipped,"
+ " enabled export ODP_PKTIO_DISABLE_SOCKET_XDP=1.\n");
+ disable_pktio = true;
+ } else {
+ _ODP_PRINT("PKTIO: initialized socket xdp,"
+ " use export ODP_PKTIO_DISABLE_SOCKET_XDP=1 to disable.\n");
+ }
+
+ return 0;
+}
+
+static inline xdp_sock_info_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (xdp_sock_info_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
+
+static odp_bool_t get_nic_queue_count(int fd, const char *devname, drv_channels_t *cur_channels)
+{
+ struct ethtool_channels channels;
+ struct ifreq ifr;
+ int ret;
+
+ memset(&channels, 0, sizeof(struct ethtool_channels));
+ channels.cmd = ETHTOOL_GCHANNELS;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)&channels;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1) {
+ _ODP_DBG("Unable to query NIC queue capabilities: %s\n", strerror(errno));
+ return false;
+ }
+
+ cur_channels->rx = channels.rx_count;
+ cur_channels->tx = channels.tx_count;
+ cur_channels->other = channels.other_count;
+ cur_channels->combined = channels.combined_count;
+
+ return true;
+}
+
+static odp_bool_t get_nic_rss_indir_count(int fd, const char *devname, uint32_t *drv_num_rss)
+{
+ struct ethtool_rxfh indir;
+ struct ifreq ifr;
+ int ret;
+
+ memset(&indir, 0, sizeof(struct ethtool_rxfh));
+ indir.cmd = ETHTOOL_GRSSH;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)&indir;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1) {
+ _ODP_DBG("Unable to query NIC RSS indirection table size: %s\n", strerror(errno));
+ return false;
+ }
+
+ *drv_num_rss = indir.indir_size;
+
+ return true;
+}
+
+static odp_bool_t is_shadow_q_driver(int fd, const char *devname)
+{
+ struct ethtool_drvinfo info;
+ struct ifreq ifr;
+ int ret;
+
+ memset(&info, 0, sizeof(struct ethtool_drvinfo));
+ info.cmd = ETHTOOL_GDRVINFO;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)&info;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1) {
+ _ODP_DBG("Unable to query NIC driver information: %s\n", strerror(errno));
+ return false;
+ }
+
+ for (uint32_t i = 0U; i < _ODP_ARRAY_SIZE(shadow_q_driver_strs); ++i) {
+ if (strstr(info.driver, shadow_q_driver_strs[i]) != NULL) {
+ _ODP_PRINT("Driver with XDP shadow queues in use: %s, manual RSS"
+ " configuration likely required\n", info.driver);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void parse_options(xdp_umem_info_t *umem_info)
+{
+ if (!_odp_libconfig_lookup_ext_int(CONF_BASE_STR, NULL, RX_DESCS_STR,
+ &umem_info->num_rx_desc) ||
+ !_odp_libconfig_lookup_ext_int(CONF_BASE_STR, NULL, TX_DESCS_STR,
+ &umem_info->num_tx_desc)) {
+ _ODP_ERR("Unable to parse xdp descriptor configuration, using defaults (%d)\n",
+ NUM_DESCS_DEFAULT);
+ goto defaults;
+ }
+
+ if (umem_info->num_rx_desc <= 0 || umem_info->num_tx_desc <= 0 ||
+ !_ODP_CHECK_IS_POWER2(umem_info->num_rx_desc) ||
+ !_ODP_CHECK_IS_POWER2(umem_info->num_tx_desc)) {
+ _ODP_ERR("Invalid xdp descriptor configuration, using defaults (%d)\n",
+ NUM_DESCS_DEFAULT);
+ goto defaults;
+ }
+
+ return;
+
+defaults:
+ umem_info->num_rx_desc = NUM_DESCS_DEFAULT;
+ umem_info->num_tx_desc = NUM_DESCS_DEFAULT;
+}
+
+static int sock_xdp_open(odp_pktio_t pktio, pktio_entry_t *pktio_entry, const char *devname,
+ odp_pool_t pool_hdl)
+{
+ xdp_sock_info_t *priv;
+ pool_t *pool;
+ int ret;
+
+ if (disable_pktio)
+ return -1;
+
+ priv = pkt_priv(pktio_entry);
+ memset(priv, 0, sizeof(xdp_sock_info_t));
+ pool = _odp_pool_entry(pool_hdl);
+ priv->umem_info = (xdp_umem_info_t *)pool->mem_src_data;
+ priv->umem_info->pool = pool;
+ /* Mark transitory kernel-owned packets with the pktio index, so that they can be freed on
+ * close. */
+ priv->pktio_idx = 1 + odp_pktio_index(pktio);
+ /* Querying with ioctl() via AF_XDP socket doesn't seem to work, so
+ * create a helper socket for this. */
+ ret = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (ret == -1) {
+ _ODP_ERR("Error creating helper socket for xdp: %s\n", strerror(errno));
+ return -1;
+ }
+
+ priv->helper_sock = ret;
+ priv->mtu = _odp_mtu_get_fd(priv->helper_sock, devname);
+
+ if (priv->mtu == 0U)
+ goto mtu_err;
+
+ priv->max_mtu = pool->seg_len;
+
+ for (int i = 0; i < MAX_QUEUES; ++i) {
+ odp_ticketlock_init(&priv->qs[i].rx_lock);
+ odp_ticketlock_init(&priv->qs[i].tx_lock);
+ }
+
+ if (!get_nic_queue_count(priv->helper_sock, devname, &priv->q_num_conf.drv_channels) ||
+ !get_nic_rss_indir_count(priv->helper_sock, devname, &priv->q_num_conf.drv_num_rss))
+ _ODP_WARN("Unable to query NIC queue count/RSS, manual cleanup required\n");
+
+ priv->is_shadow_q = is_shadow_q_driver(priv->helper_sock, pktio_entry->name);
+ parse_options(priv->umem_info);
+ _ODP_DBG("Socket xdp interface (%s):\n", pktio_entry->name);
+ _ODP_DBG(" num_rx_desc: %d\n", priv->umem_info->num_rx_desc);
+ _ODP_DBG(" num_tx_desc: %d\n", priv->umem_info->num_tx_desc);
+
+ return 0;
+
+mtu_err:
+ close(priv->helper_sock);
+
+ return -1;
+}
+
+static odp_bool_t set_nic_queue_count(int fd, const char *devname, drv_channels_t *new_channels)
+{
+ struct ethtool_channels channels;
+ struct ifreq ifr;
+ int ret;
+
+ memset(&channels, 0, sizeof(struct ethtool_channels));
+ channels.cmd = ETHTOOL_SCHANNELS;
+ channels.rx_count = new_channels->rx;
+ channels.tx_count = new_channels->tx;
+ channels.other_count = new_channels->other;
+ channels.combined_count = new_channels->combined;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)&channels;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1) {
+ _ODP_DBG("Unable to set NIC queue count: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t set_nic_rss_indir(int fd, const char *devname, struct ethtool_rxfh *indir)
+{
+ struct ifreq ifr;
+ int ret;
+
+ indir->cmd = ETHTOOL_SRSSH;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)indir;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1) {
+ _ODP_DBG("Unable to set NIC RSS indirection table: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static int sock_xdp_close(pktio_entry_t *pktio_entry)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ struct ethtool_rxfh indir;
+
+ memset(&indir, 0, sizeof(struct ethtool_rxfh));
+
+ if (priv->q_num_conf.num_qs != 0U)
+ (void)set_nic_queue_count(priv->helper_sock, pktio_entry->name,
+ &priv->q_num_conf.drv_channels);
+
+ if (priv->q_num_conf.drv_num_rss != 0U && !priv->is_shadow_q)
+ (void)set_nic_rss_indir(priv->helper_sock, pktio_entry->name, &indir);
+
+ close(priv->helper_sock);
+
+ return 0;
+}
+
+static int umem_create(xdp_umem_info_t *umem_info)
+{
+ struct xsk_umem_config cfg;
+
+ if (umem_info->ref_cnt++ > 0U)
+ return 0;
+
+ /* Fill queue size is recommended to be >= HW RX ring size + AF_XDP RX
+ * ring size, so use size twice the size of AF_XDP RX ring. */
+ cfg.fill_size = umem_info->num_rx_desc * 2U;
+ cfg.comp_size = umem_info->num_tx_desc;
+ cfg.frame_size = umem_info->pool->block_size;
+ cfg.frame_headroom = sizeof(odp_packet_hdr_t) + umem_info->pool->headroom;
+ cfg.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
+
+ return xsk_umem__create(&umem_info->umem, umem_info->pool->base_addr,
+ umem_info->pool->shm_size, &umem_info->fill_q, &umem_info->compl_q,
+ &cfg);
+}
+
+static void fill_socket_config(struct xsk_socket_config *config, xdp_umem_info_t *umem_info)
+{
+ config->rx_size = umem_info->num_rx_desc * 2U;
+ config->tx_size = umem_info->num_tx_desc;
+ config->libxdp_flags = 0U;
+ config->xdp_flags = 0U;
+ config->bind_flags = XDP_ZEROCOPY;
+}
+
+static odp_bool_t reserve_fill_queue_elements(xdp_sock_info_t *sock_info, xdp_sock_t *sock,
+ int num)
+{
+ pool_t *pool;
+ odp_packet_t packets[num];
+ int count;
+ struct xsk_ring_prod *fill_q;
+ uint32_t start_idx;
+ int pktio_idx;
+ uint32_t block_size;
+ odp_packet_hdr_t *pkt_hdr;
+
+ pool = sock_info->umem_info->pool;
+ count = odp_packet_alloc_multi(_odp_pool_handle(pool), sock_info->mtu, packets, num);
+
+ if (count <= 0) {
+ ++sock->i_stats[RX_PKT_ALLOC_ERR];
+ return false;
+ }
+
+ fill_q = &sock->fill_q;
+
+ if (xsk_ring_prod__reserve(fill_q, count, &start_idx) == 0U) {
+ odp_packet_free_multi(packets, count);
+ ++sock->i_stats[RX_DESC_RSV_ERR];
+ return false;
+ }
+
+ pktio_idx = sock_info->pktio_idx;
+ block_size = pool->block_size;
+
+ for (int i = 0; i < count; ++i) {
+ pkt_hdr = packet_hdr(packets[i]);
+ pkt_hdr->ms_pktio_idx = pktio_idx;
+ *xsk_ring_prod__fill_addr(fill_q, start_idx++) =
+ pkt_hdr->event_hdr.index.event * block_size;
+ }
+
+ xsk_ring_prod__submit(&sock->fill_q, count);
+
+ return true;
+}
+
+static odp_bool_t create_sockets(xdp_sock_info_t *sock_info, const char *devname)
+{
+ struct xsk_socket_config config;
+ uint32_t bind_q, i;
+ struct xsk_umem *umem;
+ xdp_sock_t *sock;
+ int ret;
+
+ bind_q = sock_info->bind_q;
+ umem = sock_info->umem_info->umem;
+
+ for (i = 0U; i < sock_info->q_num_conf.num_qs;) {
+ sock = &sock_info->qs[i];
+ fill_socket_config(&config, sock_info->umem_info);
+ ret = xsk_socket__create_shared(&sock->xsk, devname, bind_q, umem, &sock->rx,
+ &sock->tx, &sock->fill_q, &sock->compl_q, &config);
+
+ if (ret) {
+ _ODP_ERR("Error creating xdp socket for bind queue %u: %d\n", bind_q, ret);
+ goto err;
+ }
+
+ ++i;
+
+ if (!reserve_fill_queue_elements(sock_info, sock, config.rx_size)) {
+ _ODP_ERR("Unable to reserve fill queue descriptors for queue: %u\n",
+ bind_q);
+ goto err;
+ }
+
+ ++bind_q;
+ }
+
+ /* Ring setup/clean up routines seem to be asynchronous with some drivers and might not be
+ * ready yet after xsk_socket__create_shared(). */
+ sleep(1U);
+
+ return true;
+
+err:
+ for (uint32_t j = 0U; j < i; ++j) {
+ xsk_socket__delete(sock_info->qs[j].xsk);
+ sock_info->qs[j].xsk = NULL;
+ }
+
+ return false;
+}
+
+static void umem_delete(xdp_umem_info_t *umem_info)
+{
+ if (umem_info->ref_cnt-- != 1U)
+ return;
+
+ while (xsk_umem__delete(umem_info->umem) == -EBUSY)
+ continue;
+}
+
+static int sock_xdp_start(pktio_entry_t *pktio_entry)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ int ret;
+ drv_channels_t channels = priv->q_num_conf.drv_channels;
+ struct ethtool_rxfh *indir = calloc(1U, sizeof(struct ethtool_rxfh)
+ + sizeof(((struct ethtool_rxfh *)0)->rss_config[0U])
+ * priv->q_num_conf.drv_num_rss);
+
+ if (indir == NULL) {
+ _ODP_ERR("Error allocating NIC RSS table\n");
+ return -1;
+ }
+
+ ret = umem_create(priv->umem_info);
+
+ if (ret) {
+ _ODP_ERR("Error creating UMEM pool for xdp: %d\n", ret);
+ goto err;
+ }
+
+ priv->q_num_conf.num_qs = _ODP_MAX(priv->q_num_conf.num_in_conf_qs,
+ priv->q_num_conf.num_out_conf_qs);
+ priv->bind_q = priv->is_shadow_q ? priv->q_num_conf.num_qs : 0U;
+ channels.combined = priv->q_num_conf.num_qs;
+
+ if (!set_nic_queue_count(priv->helper_sock, pktio_entry->name, &channels))
+ _ODP_WARN("Unable to configure NIC queue count, manual configuration required\n");
+
+ if (priv->q_num_conf.num_in_conf_qs > 0U && !priv->is_shadow_q) {
+ indir->indir_size = priv->q_num_conf.drv_num_rss;
+
+ for (uint32_t i = 0U; i < indir->indir_size; ++i)
+ indir->rss_config[i] = (i % priv->q_num_conf.num_in_conf_qs);
+
+ if (!set_nic_rss_indir(priv->helper_sock, pktio_entry->name, indir))
+ _ODP_WARN("Unable to configure NIC RSS, manual configuration required\n");
+ }
+
+ if (!create_sockets(priv, pktio_entry->name))
+ goto sock_err;
+
+ return 0;
+
+sock_err:
+ umem_delete(priv->umem_info);
+
+err:
+ free(indir);
+
+ return -1;
+}
+
+static int sock_xdp_stop(pktio_entry_t *pktio_entry)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ pool_t *pool = priv->umem_info->pool;
+ odp_packet_hdr_t *pkt_hdr;
+
+ for (uint32_t i = 0U; i < priv->q_num_conf.num_qs; ++i) {
+ if (priv->qs[i].xsk != NULL) {
+ xsk_socket__delete(priv->qs[i].xsk);
+ priv->qs[i].xsk = NULL;
+ }
+ }
+
+ umem_delete(priv->umem_info);
+ /* Ring setup/clean up routines seem to be asynchronous with some drivers and might not be
+ * ready yet after xsk_socket__delete(). */
+ sleep(1U);
+
+ /* Free all packets that were in fill or completion queues at the time of closing. */
+ for (uint32_t i = 0U; i < pool->num + pool->skipped_blocks; ++i) {
+ pkt_hdr = packet_hdr(packet_from_event_hdr(event_hdr_from_index(pool, i)));
+
+ if (pkt_hdr->ms_pktio_idx == priv->pktio_idx) {
+ pkt_hdr->ms_pktio_idx = 0U;
+ odp_packet_free(packet_handle(pkt_hdr));
+ }
+ }
+
+ return 0;
+}
+
+static int sock_xdp_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ xdp_sock_t *sock;
+ odp_pktin_queue_stats_t qi_stats;
+ odp_pktout_queue_stats_t qo_stats;
+ struct xdp_statistics xdp_stats;
+ socklen_t optlen = sizeof(struct xdp_statistics);
+
+ memset(stats, 0, sizeof(odp_pktio_stats_t));
+
+ for (uint32_t i = 0U; i < priv->q_num_conf.num_qs; ++i) {
+ sock = &priv->qs[i];
+ qi_stats = sock->qi_stats;
+ qo_stats = sock->qo_stats;
+ stats->in_octets += qi_stats.octets;
+ stats->in_packets += qi_stats.packets;
+ stats->in_errors += qi_stats.errors;
+ stats->out_octets += qo_stats.octets;
+ stats->out_packets += qo_stats.packets;
+
+ if (!getsockopt(xsk_socket__fd(sock->xsk), SOL_XDP, XDP_STATISTICS, &xdp_stats,
+ &optlen)) {
+ stats->in_errors += (xdp_stats.rx_dropped - sock->xdp_stats.rx_dropped);
+ stats->in_discards +=
+ (xdp_stats.rx_invalid_descs - sock->xdp_stats.rx_inv_descs);
+ stats->out_discards +=
+ (xdp_stats.tx_invalid_descs - sock->xdp_stats.tx_inv_descs);
+ }
+ }
+
+ return 0;
+}
+
+static int sock_xdp_stats_reset(pktio_entry_t *pktio_entry)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ xdp_sock_t *sock;
+ struct xdp_statistics xdp_stats;
+ socklen_t optlen = sizeof(struct xdp_statistics);
+
+ for (uint32_t i = 0U; i < priv->q_num_conf.num_qs; ++i) {
+ sock = &priv->qs[i];
+ memset(&sock->qi_stats, 0, sizeof(odp_pktin_queue_stats_t));
+ memset(&sock->qo_stats, 0, sizeof(odp_pktout_queue_stats_t));
+ memset(sock->i_stats, 0, sizeof(sock->i_stats));
+
+ if (!getsockopt(xsk_socket__fd(sock->xsk), SOL_XDP, XDP_STATISTICS, &xdp_stats,
+ &optlen)) {
+ sock->xdp_stats.rx_dropped = xdp_stats.rx_dropped;
+ sock->xdp_stats.rx_inv_descs = xdp_stats.rx_invalid_descs;
+ sock->xdp_stats.tx_inv_descs = xdp_stats.tx_invalid_descs;
+ }
+ }
+
+ return 0;
+}
+
+static int sock_xdp_pktin_queue_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktin_queue_stats_t *pktin_stats)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ xdp_sock_t *sock;
+ struct xdp_statistics xdp_stats;
+ socklen_t optlen = sizeof(struct xdp_statistics);
+
+ sock = &priv->qs[index];
+ *pktin_stats = sock->qi_stats;
+
+ if (!getsockopt(xsk_socket__fd(sock->xsk), SOL_XDP, XDP_STATISTICS, &xdp_stats, &optlen)) {
+ pktin_stats->errors += (xdp_stats.rx_dropped - sock->xdp_stats.rx_dropped);
+ pktin_stats->discards +=
+ (xdp_stats.rx_invalid_descs - sock->xdp_stats.rx_inv_descs);
+ }
+
+ return 0;
+}
+
+static int sock_xdp_pktout_queue_stats(pktio_entry_t *pktio_entry, uint32_t index,
+ odp_pktout_queue_stats_t *pktout_stats)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ xdp_sock_t *sock;
+ struct xdp_statistics xdp_stats;
+ socklen_t optlen = sizeof(struct xdp_statistics);
+
+ sock = &priv->qs[index];
+ *pktout_stats = sock->qo_stats;
+
+ if (!getsockopt(xsk_socket__fd(sock->xsk), SOL_XDP, XDP_STATISTICS, &xdp_stats, &optlen))
+ pktout_stats->discards +=
+ (xdp_stats.tx_invalid_descs - sock->xdp_stats.tx_inv_descs);
+
+ return 0;
+}
+
+static int sock_xdp_extra_stat_info(pktio_entry_t *pktio_entry, odp_pktio_extra_stat_info_t info[],
+ int num)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ const int total_stats = MAX_INTERNAL_STATS * priv->q_num_conf.num_qs;
+
+ if (info != NULL && num > 0) {
+ for (int i = 0; i < _ODP_MIN(num, total_stats); ++i)
+ snprintf(info[i].name, ODP_PKTIO_STATS_EXTRA_NAME_LEN - 1,
+ "q%" PRIu64 "_%s", i / MAX_INTERNAL_STATS,
+ internal_stats_strs[i % MAX_INTERNAL_STATS]);
+ }
+
+ return total_stats;
+}
+
+static int sock_xdp_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[], int num)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ const int total_stats = MAX_INTERNAL_STATS * priv->q_num_conf.num_qs;
+ uint64_t *i_stats;
+
+ if (stats != NULL && num > 0) {
+ for (int i = 0; i < _ODP_MIN(num, total_stats); ++i) {
+ i_stats = priv->qs[i / MAX_INTERNAL_STATS].i_stats;
+ stats[i] = i_stats[i % MAX_INTERNAL_STATS];
+ }
+ }
+
+ return total_stats;
+}
+
+static int sock_xdp_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id, uint64_t *stat)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ const uint32_t total_stats = MAX_INTERNAL_STATS * priv->q_num_conf.num_qs;
+
+ if (id >= total_stats) {
+ _ODP_ERR("Invalid counter id: %u (allowed range: 0-%u)\n", id, total_stats - 1U);
+ return -1;
+ }
+
+ *stat = priv->qs[id / MAX_INTERNAL_STATS].i_stats[id % MAX_INTERNAL_STATS];
+
+ return 0;
+}
+
+static inline void extract_data(const struct xdp_desc *rx_desc, uint8_t *pool_base_addr,
+ pkt_data_t *pkt_data)
+{
+ uint64_t frame_off;
+ uint64_t pkt_off;
+
+ /* UMEM "addresses" are offsets from start of a registered UMEM area.
+ * Additionally, the packet data offset (where received packet data
+ * starts within a UMEM frame) is encoded to the UMEM address with
+ * XSK_UNALIGNED_BUF_OFFSET_SHIFT left bitshift when XDP_ZEROCOPY and
+ * XDP_UMEM_UNALIGNED_CHUNK_FLAG are enabled. */
+ frame_off = rx_desc->addr;
+ pkt_off = xsk_umem__add_offset_to_addr(frame_off);
+ frame_off = xsk_umem__extract_addr(frame_off);
+ pkt_data->pkt_hdr = xsk_umem__get_data(pool_base_addr, frame_off);
+ pkt_data->pkt = packet_handle(pkt_data->pkt_hdr);
+ pkt_data->data = xsk_umem__get_data(pool_base_addr, pkt_off);
+ pkt_data->len = rx_desc->len;
+}
+
+static uint32_t process_received(pktio_entry_t *pktio_entry, xdp_sock_t *sock, pool_t *pool,
+ uint32_t start_idx, odp_packet_t packets[], int num)
+{
+ struct xsk_ring_cons *rx = &sock->rx;
+ uint8_t *base_addr = pool->base_addr;
+ pkt_data_t pkt_data;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ int ret;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
+ uint64_t errors = 0U, octets = 0U;
+ odp_pktio_t pktio_hdl = pktio_entry->handle;
+ uint32_t num_rx = 0U;
+ uint32_t num_cls = 0U;
+ uint32_t num_pkts = 0U;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+
+ for (int i = 0; i < num; ++i) {
+ extract_data(xsk_ring_cons__rx_desc(rx, start_idx++), base_addr, &pkt_data);
+ pkt_data.pkt_hdr->ms_pktio_idx = 0U;
+ packet_init(pkt_data.pkt_hdr, pkt_data.len);
+ pkt_data.pkt_hdr->seg_data = pkt_data.data;
+ pkt_data.pkt_hdr->event_hdr.base_data = pkt_data.data;
+
+ if (layer) {
+ ret = _odp_packet_parse_common(pkt_data.pkt_hdr, pkt_data.data,
+ pkt_data.len, pkt_data.len,
+ layer, opt);
+
+ if (ret)
+ ++errors;
+
+ if (ret < 0) {
+ odp_packet_free(pkt_data.pkt);
+ continue;
+ }
+
+ if (cls_enabled) {
+ odp_pool_t new_pool;
+
+ ret = _odp_cls_classify_packet(pktio_entry, pkt_data.data,
+ &new_pool, pkt_data.pkt_hdr);
+ if (ret) {
+ odp_packet_free(pkt_data.pkt);
+ continue;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt_data.pkt, &pkt_data.pkt_hdr, new_pool))) {
+ odp_packet_free(pkt_data.pkt);
+ continue;
+ }
+ }
+ }
+
+ pkt_data.pkt_hdr->input = pktio_hdl;
+ num_pkts++;
+ octets += pkt_data.len;
+
+ if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ packets[num_cls++] = pkt_data.pkt;
+ num_cls = _odp_cls_enq(packets, num_cls, (i + 1 == num));
+ } else {
+ packets[num_rx++] = pkt_data.pkt;
+ }
+ }
+
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(num_cls))
+ _odp_cls_enq(packets, num_cls, true);
+
+ sock->qi_stats.octets += octets;
+ sock->qi_stats.packets += num_pkts;
+ sock->qi_stats.errors += errors;
+
+ return num_rx;
+}
+
+static int sock_xdp_recv(pktio_entry_t *pktio_entry, int index, odp_packet_t packets[], int num)
+{
+ xdp_sock_info_t *priv;
+ xdp_sock_t *sock;
+ struct pollfd fd;
+ uint32_t start_idx = 0U, recvd, procd;
+
+ priv = pkt_priv(pktio_entry);
+ _ODP_ASSERT((uint32_t)index < priv->q_num_conf.num_in_conf_qs);
+ sock = &priv->qs[index];
+
+ if (!priv->lockless_rx)
+ odp_ticketlock_lock(&sock->rx_lock);
+
+ if (odp_unlikely(xsk_ring_prod__needs_wakeup(&sock->fill_q))) {
+ fd.fd = xsk_socket__fd(sock->xsk);
+ fd.events = POLLIN;
+ (void)poll(&fd, 1U, 0);
+ }
+
+ recvd = xsk_ring_cons__peek(&sock->rx, num, &start_idx);
+
+ if (recvd == 0U) {
+ if (!priv->lockless_rx)
+ odp_ticketlock_unlock(&sock->rx_lock);
+ return 0;
+ }
+
+ procd = process_received(pktio_entry, sock, priv->umem_info->pool, start_idx, packets,
+ recvd);
+ xsk_ring_cons__release(&sock->rx, recvd);
+ (void)reserve_fill_queue_elements(priv, sock, recvd);
+
+ if (!priv->lockless_rx)
+ odp_ticketlock_unlock(&sock->rx_lock);
+
+ return procd;
+}
+
+static void handle_pending_tx(xdp_sock_t *sock, uint8_t *base_addr, int num)
+{
+ struct xsk_ring_cons *compl_q;
+ uint32_t sent;
+ uint32_t start_idx;
+ uint64_t frame_off;
+ odp_packet_t pkt;
+
+ if (odp_unlikely(xsk_ring_prod__needs_wakeup(&sock->tx)))
+ (void)sendto(xsk_socket__fd(sock->xsk), NULL, 0U, MSG_DONTWAIT, NULL, 0U);
+
+ compl_q = &sock->compl_q;
+ sent = xsk_ring_cons__peek(compl_q, num, &start_idx);
+
+ if (sent) {
+ odp_packet_t packets[sent];
+
+ for (uint32_t i = 0U; i < sent; ++i) {
+ frame_off = *xsk_ring_cons__comp_addr(compl_q, start_idx++);
+ frame_off = xsk_umem__extract_addr(frame_off);
+ pkt = xsk_umem__get_data(base_addr, frame_off);
+ packets[i] = pkt;
+ packet_hdr(packets[i])->ms_pktio_idx = 0U;
+ }
+
+ odp_packet_free_multi(packets, sent);
+ xsk_ring_cons__release(compl_q, sent);
+ }
+}
+
+static inline void populate_tx_desc(odp_packet_hdr_t *pkt_hdr, pool_t *pool,
+ struct xdp_desc *tx_desc, uint32_t len)
+{
+ uint64_t frame_off;
+ uint64_t pkt_off;
+
+ frame_off = pkt_hdr->event_hdr.index.event * pool->block_size;
+ pkt_off = (uint64_t)(uintptr_t)pkt_hdr->seg_data - (uint64_t)(uintptr_t)pool->base_addr
+ - frame_off;
+ pkt_off <<= XSK_UNALIGNED_BUF_OFFSET_SHIFT;
+ tx_desc->addr = frame_off | pkt_off;
+ tx_desc->len = len;
+}
+
+static inline void populate_tx_descs(odp_packet_hdr_t *pkt_hdr, pool_t *pool,
+ struct xsk_ring_prod *tx, int seg_cnt, uint32_t start_idx,
+ int pktio_idx)
+{
+ if (odp_likely(seg_cnt == 1)) {
+ populate_tx_desc(pkt_hdr, pool, xsk_ring_prod__tx_desc(tx, start_idx),
+ pkt_hdr->frame_len);
+ pkt_hdr->ms_pktio_idx = pktio_idx;
+ } else {
+ for (int i = 0; i < seg_cnt; ++i) {
+ populate_tx_desc(pkt_hdr, pool, xsk_ring_prod__tx_desc(tx, start_idx++),
+ pkt_hdr->seg_len);
+ pkt_hdr->ms_pktio_idx = pktio_idx;
+ pkt_hdr = pkt_hdr->seg_next;
+ }
+ }
+}
+
+static int sock_xdp_send(pktio_entry_t *pktio_entry, int index, const odp_packet_t packets[],
+ int num)
+{
+ xdp_sock_info_t *priv;
+ xdp_sock_t *sock;
+ pool_t *pool;
+ odp_pool_t pool_hdl;
+ int pktio_idx, i, seg_cnt;
+ struct xsk_ring_prod *tx;
+ uint8_t *base_addr;
+ odp_packet_t pkt;
+ odp_packet_hdr_t *pkt_hdr;
+ uint32_t tx_descs, start_idx, sent = 0U;
+ uint64_t octets = 0U;
+
+ if (odp_unlikely(num == 0))
+ return 0;
+
+ priv = pkt_priv(pktio_entry);
+ _ODP_ASSERT((uint32_t)index < priv->q_num_conf.num_out_conf_qs);
+ sock = &priv->qs[index];
+
+ if (!priv->lockless_tx)
+ odp_ticketlock_lock(&sock->tx_lock);
+
+ pool = priv->umem_info->pool;
+ pool_hdl = _odp_pool_handle(pool);
+ pktio_idx = priv->pktio_idx;
+ tx = &sock->tx;
+ base_addr = priv->umem_info->pool->base_addr;
+ tx_descs = priv->umem_info->num_tx_desc;
+
+ for (i = 0; i < num; ++i) {
+ pkt = ODP_PACKET_INVALID;
+ pkt_hdr = packet_hdr(packets[i]);
+ seg_cnt = pkt_hdr->seg_count;
+
+ if (_odp_pool_entry(pkt_hdr->event_hdr.pool) != pool) {
+ pkt = odp_packet_copy(packets[i], pool_hdl);
+
+ if (odp_unlikely(pkt == ODP_PACKET_INVALID)) {
+ ++sock->i_stats[TX_PKT_ALLOC_ERR];
+ break;
+ }
+
+ pkt_hdr = packet_hdr(pkt);
+ seg_cnt = pkt_hdr->seg_count;
+ }
+
+ if (xsk_ring_prod__reserve(tx, seg_cnt, &start_idx) == 0U) {
+ handle_pending_tx(sock, base_addr, tx_descs);
+
+ if (xsk_ring_prod__reserve(tx, seg_cnt, &start_idx) == 0U) {
+ if (pkt != ODP_PACKET_INVALID)
+ odp_packet_free(pkt);
+
+ ++sock->i_stats[TX_DESC_RSV_ERR];
+
+ break;
+ }
+ }
+
+ if (pkt != ODP_PACKET_INVALID)
+ odp_packet_free(packets[i]);
+
+ populate_tx_descs(pkt_hdr, pool, tx, seg_cnt, start_idx, pktio_idx);
+ sent += seg_cnt;
+ octets += pkt_hdr->frame_len;
+ }
+
+ xsk_ring_prod__submit(tx, sent);
+ handle_pending_tx(sock, base_addr, tx_descs);
+ sock->qo_stats.octets += octets;
+ sock->qo_stats.packets += i;
+
+ if (!priv->lockless_tx)
+ odp_ticketlock_unlock(&sock->tx_lock);
+
+ return i;
+}
+
+static uint32_t sock_xdp_mtu_get(pktio_entry_t *pktio_entry)
+{
+ return pkt_priv(pktio_entry)->mtu;
+}
+
+static int sock_xdp_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+ int ret;
+
+ ret = _odp_mtu_set_fd(priv->helper_sock, pktio_entry->name, maxlen_input);
+ if (ret)
+ return ret;
+
+ priv->mtu = maxlen_input;
+
+ return 0;
+}
+
+static int sock_xdp_promisc_mode_set(pktio_entry_t *pktio_entry, int enable)
+{
+ return _odp_promisc_mode_set_fd(pkt_priv(pktio_entry)->helper_sock,
+ pktio_entry->name, enable);
+}
+
+static int sock_xdp_promisc_mode_get(pktio_entry_t *pktio_entry)
+{
+ return _odp_promisc_mode_get_fd(pkt_priv(pktio_entry)->helper_sock,
+ pktio_entry->name);
+}
+
+static int sock_xdp_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED, void *mac_addr)
+{
+ return _odp_mac_addr_get_fd(pkt_priv(pktio_entry)->helper_sock,
+ pktio_entry->name, mac_addr) ? -1 : ETH_ALEN;
+}
+
+static int sock_xdp_link_status(pktio_entry_t *pktio_entry)
+{
+ return _odp_link_status_fd(pkt_priv(pktio_entry)->helper_sock,
+ pktio_entry->name);
+}
+
+static int sock_xdp_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ return _odp_link_info_fd(pkt_priv(pktio_entry)->helper_sock,
+ pktio_entry->name, info);
+}
+
+static int get_nic_queue_capability(int fd, const char *devname, odp_pktio_capability_t *capa)
+{
+ struct ethtool_channels channels;
+ struct ifreq ifr;
+ int ret;
+ const uint32_t cc = odp_cpu_count();
+ uint32_t max_channels;
+
+ memset(&channels, 0, sizeof(struct ethtool_channels));
+ channels.cmd = ETHTOOL_GCHANNELS;
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname);
+ ifr.ifr_data = (char *)&channels;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+
+ if (ret == -1 || channels.max_combined == 0U) {
+ if (ret == -1 && errno != EOPNOTSUPP) {
+ _ODP_ERR("Unable to query NIC queue capabilities: %s\n", strerror(errno));
+ return -1;
+ }
+
+ channels.max_combined = 1U;
+ }
+
+ max_channels = _ODP_MIN(cc, channels.max_combined);
+ capa->max_input_queues = _ODP_MIN((uint32_t)ODP_PKTIN_MAX_QUEUES, max_channels);
+ capa->max_output_queues = _ODP_MIN((uint32_t)ODP_PKTOUT_MAX_QUEUES, max_channels);
+
+ return 0;
+}
+
+static int sock_xdp_capability(pktio_entry_t *pktio_entry, odp_pktio_capability_t *capa)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+
+ memset(capa, 0, sizeof(odp_pktio_capability_t));
+
+ if (get_nic_queue_capability(priv->helper_sock, pktio_entry->name, capa))
+ return -1;
+
+ capa->set_op.op.promisc_mode = 1U;
+ capa->set_op.op.maxlen = 1U;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_input = priv->max_mtu;
+ capa->maxlen.min_output = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_output = priv->max_mtu;
+
+ capa->config.parser.layer = ODP_PROTO_LAYER_ALL;
+
+ capa->stats.pktio.counter.in_octets = 1U;
+ capa->stats.pktio.counter.in_packets = 1U;
+ capa->stats.pktio.counter.in_errors = 1U;
+ capa->stats.pktio.counter.in_discards = 1U;
+ capa->stats.pktio.counter.out_octets = 1U;
+ capa->stats.pktio.counter.out_packets = 1U;
+ capa->stats.pktio.counter.out_discards = 1U;
+
+ capa->stats.pktin_queue.counter.octets = 1U;
+ capa->stats.pktin_queue.counter.packets = 1U;
+ capa->stats.pktin_queue.counter.errors = 1U;
+ capa->stats.pktin_queue.counter.discards = 1U;
+ capa->stats.pktout_queue.counter.octets = 1U;
+ capa->stats.pktout_queue.counter.packets = 1U;
+ capa->stats.pktout_queue.counter.discards = 1U;
+
+ return 0;
+}
+
+static int sock_xdp_input_queues_config(pktio_entry_t *pktio_entry,
+ const odp_pktin_queue_param_t *param)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+
+ priv->q_num_conf.num_in_conf_qs = param->num_queues;
+ priv->lockless_rx = pktio_entry->param.in_mode == ODP_PKTIN_MODE_SCHED ||
+ param->op_mode == ODP_PKTIO_OP_MT_UNSAFE;
+
+ return 0;
+}
+
+static int sock_xdp_output_queues_config(pktio_entry_t *pktio_entry,
+ const odp_pktout_queue_param_t *param)
+{
+ xdp_sock_info_t *priv = pkt_priv(pktio_entry);
+
+ priv->q_num_conf.num_out_conf_qs = param->num_queues;
+ priv->lockless_tx = param->op_mode == ODP_PKTIO_OP_MT_UNSAFE;
+
+ return 0;
+}
+
+const pktio_if_ops_t _odp_sock_xdp_pktio_ops = {
+ .name = "socket_xdp",
+ .print = NULL,
+ .init_global = sock_xdp_init_global,
+ .init_local = NULL,
+ .term = NULL,
+ .open = sock_xdp_open,
+ .close = sock_xdp_close,
+ .start = sock_xdp_start,
+ .stop = sock_xdp_stop,
+ .stats = sock_xdp_stats,
+ .stats_reset = sock_xdp_stats_reset,
+ .pktin_queue_stats = sock_xdp_pktin_queue_stats,
+ .pktout_queue_stats = sock_xdp_pktout_queue_stats,
+ .extra_stat_info = sock_xdp_extra_stat_info,
+ .extra_stats = sock_xdp_extra_stats,
+ .extra_stat_counter = sock_xdp_extra_stat_counter,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
+ .recv = sock_xdp_recv,
+ .recv_tmo = NULL,
+ .recv_mq_tmo = NULL,
+ .fd_set = NULL,
+ .send = sock_xdp_send,
+ .maxlen_get = sock_xdp_mtu_get,
+ .maxlen_set = sock_xdp_mtu_set,
+ .promisc_mode_set = sock_xdp_promisc_mode_set,
+ .promisc_mode_get = sock_xdp_promisc_mode_get,
+ .mac_get = sock_xdp_mac_addr_get,
+ .mac_set = NULL,
+ .link_status = sock_xdp_link_status,
+ .link_info = sock_xdp_link_info,
+ .capability = sock_xdp_capability,
+ .config = NULL,
+ .input_queues_config = sock_xdp_input_queues_config,
+ .output_queues_config = sock_xdp_output_queues_config
+};
+
+static odp_bool_t sock_xdp_is_mem_src_active(void)
+{
+ return !disable_pktio;
+}
+
+static void sock_xdp_force_mem_src_disable(void)
+{
+ disable_pktio = true;
+}
+
+static void sock_xdp_adjust_block_size(uint8_t *data ODP_UNUSED, uint32_t *block_size,
+ uint32_t *block_offset ODP_UNUSED, uint32_t *flags)
+{
+ const uint32_t size = *block_size + XDP_PACKET_HEADROOM;
+ const uint64_t ps = odp_sys_page_size();
+ /* AF_XDP requires frames to be between 2kB and page size, so with
+ * XDP_ZEROCOPY, if block size is less than 2kB, adjust it to 2kB, if
+ * it is larger than page size, make pool creation fail. */
+ if (disable_pktio)
+ return;
+
+ if (size > ps) {
+ _ODP_ERR("Adjusted pool block size larger than page size: %u > %" PRIu64 "\n",
+ size, ps);
+ *block_size = 0U;
+ }
+
+ *flags |= ODP_SHM_HP;
+ *block_size = _ODP_MAX(size, MIN_FRAME_SIZE);
+}
+
+const _odp_pool_mem_src_ops_t _odp_pool_sock_xdp_mem_src_ops = {
+ .name = "xdp_zc",
+ .is_active = sock_xdp_is_mem_src_active,
+ .force_disable = sock_xdp_force_mem_src_disable,
+ .adjust_size = sock_xdp_adjust_block_size,
+ .bind = NULL,
+ .unbind = NULL
+};
+
+#else
+/* Avoid warning about empty translation unit */
+typedef int _odp_dummy;
+#endif
diff --git a/platform/linux-generic/pktio/stats/ethtool_stats.c b/platform/linux-generic/pktio/stats/ethtool_stats.c
new file mode 100644
index 000000000..1cee0c4d5
--- /dev/null
+++ b/platform/linux-generic/pktio/stats/ethtool_stats.c
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <odp_posix_extensions.h>
+
+#include <odp/api/packet_io_stats.h>
+
+#include <odp_debug_internal.h>
+#include <odp_string_internal.h>
+#include <odp_ethtool_stats.h>
+
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <errno.h>
+#include <net/if.h>
+
+/*
+ * Suppress bounds warnings about interior zero length arrays. Such an array
+ * is used intentionally in sset_info.
+ */
+#if __GNUC__ >= 10
+#pragma GCC diagnostic ignored "-Wzero-length-bounds"
+#endif
+
+static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
+{
+ union {
+ struct ethtool_sset_info hdr;
+ /* Reserve space for hdr.data. */
+ uint8_t buf[sizeof(struct ethtool_sset_info) +
+ sizeof(((struct ethtool_sset_info *)0)->data[0])];
+ } sset_info;
+ struct ethtool_drvinfo drvinfo;
+ uint32_t len;
+ struct ethtool_gstrings *strings;
+ ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
+
+ sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+ sset_info.hdr.reserved = 0;
+ sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
+ ifr->ifr_data = (void *)&sset_info;
+ if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
+ len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
+ } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
+ /* Fallback for old kernel versions */
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr->ifr_data = (void *)&drvinfo;
+ if (ioctl(fd, SIOCETHTOOL, ifr)) {
+ _ODP_ERR("Cannot get stats information: %s\n", strerror(errno));
+ return NULL;
+ }
+ len = *(uint32_t *)(void *)((char *)&drvinfo + drvinfo_offset);
+ } else {
+ return NULL;
+ }
+
+ if (!len) {
+ _ODP_ERR("len is zero");
+ return NULL;
+ }
+
+ strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+ if (!strings) {
+ _ODP_ERR("alloc failed\n");
+ return NULL;
+ }
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = ETH_SS_STATS;
+ strings->len = len;
+ ifr->ifr_data = (void *)strings;
+ if (ioctl(fd, SIOCETHTOOL, ifr)) {
+ _ODP_ERR("Cannot get stats information: %s\n", strerror(errno));
+ free(strings);
+ return NULL;
+ }
+
+ return strings;
+}
+
+static int ethtool_stats_get(int fd, const char *name,
+ struct ethtool_gstrings **strings_out,
+ struct ethtool_stats **estats_out,
+ unsigned int *nstats_out)
+{
+ struct ethtool_gstrings *strings;
+ struct ethtool_stats *estats;
+ struct ifreq ifr;
+ unsigned int n_stats;
+ int err;
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+
+ strings = get_stringset(fd, &ifr);
+ if (!strings)
+ return -1;
+
+ n_stats = strings->len;
+ if (n_stats < 1) {
+ _ODP_ERR("no stats available\n");
+ free(strings);
+ return -1;
+ }
+
+ estats = calloc(1, n_stats * sizeof(uint64_t) +
+ sizeof(struct ethtool_stats));
+ if (!estats) {
+ free(strings);
+ return -1;
+ }
+
+ estats->cmd = ETHTOOL_GSTATS;
+ estats->n_stats = n_stats;
+ ifr.ifr_data = (void *)estats;
+ err = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (err < 0) {
+ free(strings);
+ free(estats);
+ return -1;
+ }
+
+ if (strings_out)
+ *strings_out = strings;
+ else
+ free(strings);
+
+ if (estats_out)
+ *estats_out = estats;
+ else
+ free(estats);
+
+ if (nstats_out)
+ *nstats_out = n_stats;
+
+ return 0;
+}
+
+int _odp_ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats)
+{
+ struct ethtool_gstrings *strings;
+ struct ethtool_stats *estats;
+ unsigned int i, n_stats;
+ int cnts = 0;
+
+ if (ethtool_stats_get(fd, name, &strings, &estats, &n_stats))
+ return -1;
+
+ for (i = 0; i < n_stats; i++) {
+ char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
+ uint64_t val = estats->data[i];
+
+ if (!strcmp(cnt, "rx_octets") ||
+ !strcmp(cnt, "rx_bytes")) {
+ stats->in_octets = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_packets")) {
+ stats->in_packets = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_ucast_packets") ||
+ !strcmp(cnt, "rx_unicast")) {
+ stats->in_ucast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_broadcast") ||
+ !strcmp(cnt, "rx_bcast_packets")) {
+ stats->in_bcast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_multicast") ||
+ !strcmp(cnt, "rx_mcast_packets")) {
+ stats->in_mcast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_discards") ||
+ !strcmp(cnt, "rx_dropped")) {
+ stats->in_discards = val;
+ cnts++;
+ } else if (!strcmp(cnt, "rx_errors")) {
+ stats->in_errors = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_octets") ||
+ !strcmp(cnt, "tx_bytes")) {
+ stats->out_octets = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_packets")) {
+ stats->out_packets = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_ucast_packets") ||
+ !strcmp(cnt, "tx_unicast")) {
+ stats->out_ucast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_broadcast") ||
+ !strcmp(cnt, "tx_bcast_packets")) {
+ stats->out_bcast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_multicast") ||
+ !strcmp(cnt, "tx_mcast_packets")) {
+ stats->out_mcast_pkts = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_discards") ||
+ !strcmp(cnt, "tx_dropped")) {
+ stats->out_discards = val;
+ cnts++;
+ } else if (!strcmp(cnt, "tx_errors")) {
+ stats->out_errors = val;
+ cnts++;
+ }
+ }
+
+ free(strings);
+ free(estats);
+
+ /* Ethtool strings came from kernel driver. Name of that
+ * strings is not universal. Current function needs to be updated
+ * if your driver has different names for counters */
+ if (cnts < 14)
+ return -1;
+
+ return 0;
+}
+
+int _odp_ethtool_extra_stat_info(int fd, const char *name,
+ odp_pktio_extra_stat_info_t info[], int num)
+{
+ struct ethtool_gstrings *strings;
+ unsigned int i, n_stats;
+
+ if (ethtool_stats_get(fd, name, &strings, NULL, &n_stats))
+ return -1;
+
+ for (i = 0; i < n_stats && i < (unsigned int)num; i++) {
+ char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
+
+ _odp_strcpy(info[i].name, cnt, ODP_PKTIO_STATS_EXTRA_NAME_LEN);
+ }
+
+ free(strings);
+
+ return n_stats;
+}
+
+int _odp_ethtool_extra_stats(int fd, const char *name, uint64_t stats[], int num)
+{
+ struct ethtool_stats *estats;
+ unsigned int i, n_stats;
+
+ if (ethtool_stats_get(fd, name, NULL, &estats, &n_stats))
+ return -1;
+
+ for (i = 0; i < n_stats && i < (unsigned int)num; i++)
+ stats[i] = estats->data[i];
+
+ free(estats);
+
+ return n_stats;
+}
+
+int _odp_ethtool_extra_stat_counter(int fd, const char *name, uint32_t id,
+ uint64_t *stat)
+{
+ struct ethtool_stats *estats;
+ unsigned int n_stats;
+ int ret = 0;
+
+ if (ethtool_stats_get(fd, name, NULL, &estats, &n_stats))
+ return -1;
+
+ if (id >= n_stats) {
+ _ODP_ERR("Invalid counter id\n");
+ ret = -1;
+ } else {
+ *stat = estats->data[id];
+ }
+
+ free(estats);
+
+ return ret;
+}
diff --git a/platform/linux-generic/pktio/stats/packet_io_stats.c b/platform/linux-generic/pktio/stats/packet_io_stats.c
new file mode 100644
index 000000000..b839faaa6
--- /dev/null
+++ b/platform/linux-generic/pktio/stats/packet_io_stats.c
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_packet_io_stats.h>
+#include <odp_ethtool_stats.h>
+#include <odp_sysfs_stats.h>
+
+#include <string.h>
+
+static int sock_stats_get(pktio_entry_t *e, odp_pktio_stats_t *stats, int fd)
+{
+ int ret = 0;
+
+ memset(stats, 0, sizeof(*stats));
+
+ if (e->stats_type == STATS_ETHTOOL)
+ ret = _odp_ethtool_stats_get_fd(fd, e->name, stats);
+ else if (e->stats_type == STATS_SYSFS)
+ ret = _odp_sysfs_stats(e, stats);
+
+ if (ret)
+ _ODP_ERR("Failed to get pktio statistics.\n");
+
+ return ret;
+}
+
+int _odp_sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd)
+{
+ odp_pktio_stats_t cur_stats;
+ int ret;
+
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED) {
+ memset(&pktio_entry->stats, 0,
+ sizeof(odp_pktio_stats_t));
+ return 0;
+ }
+
+ ret = sock_stats_get(pktio_entry, &cur_stats, fd);
+
+ if (!ret)
+ memcpy(&pktio_entry->stats, &cur_stats,
+ sizeof(odp_pktio_stats_t));
+
+ return ret;
+}
+
+int _odp_sock_stats_fd(pktio_entry_t *pktio_entry,
+ odp_pktio_stats_t *stats,
+ int fd)
+{
+ odp_pktio_stats_t cur_stats;
+
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED) {
+ memset(stats, 0, sizeof(*stats));
+ return 0;
+ }
+
+ if (sock_stats_get(pktio_entry, &cur_stats, fd))
+ return -1;
+
+ stats->in_octets = cur_stats.in_octets -
+ pktio_entry->stats.in_octets;
+ stats->in_packets = cur_stats.in_packets -
+ pktio_entry->stats.in_packets;
+ stats->in_ucast_pkts = cur_stats.in_ucast_pkts -
+ pktio_entry->stats.in_ucast_pkts;
+ stats->in_bcast_pkts = cur_stats.in_bcast_pkts -
+ pktio_entry->stats.in_bcast_pkts;
+ stats->in_mcast_pkts = cur_stats.in_mcast_pkts -
+ pktio_entry->stats.in_mcast_pkts;
+ stats->in_discards = cur_stats.in_discards -
+ pktio_entry->stats.in_discards;
+ stats->in_errors = cur_stats.in_errors -
+ pktio_entry->stats.in_errors;
+ stats->out_octets = cur_stats.out_octets -
+ pktio_entry->stats.out_octets;
+ stats->out_packets = cur_stats.out_packets -
+ pktio_entry->stats.out_packets;
+ stats->out_ucast_pkts = cur_stats.out_ucast_pkts -
+ pktio_entry->stats.out_ucast_pkts;
+ stats->out_bcast_pkts = cur_stats.out_bcast_pkts -
+ pktio_entry->stats.out_bcast_pkts;
+ stats->out_mcast_pkts = cur_stats.out_mcast_pkts -
+ pktio_entry->stats.out_mcast_pkts;
+ stats->out_discards = cur_stats.out_discards -
+ pktio_entry->stats.out_discards;
+ stats->out_errors = cur_stats.out_errors -
+ pktio_entry->stats.out_errors;
+
+ return 0;
+}
+
+int _odp_sock_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[], int num,
+ int fd)
+{
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED)
+ return 0;
+
+ if (pktio_entry->stats_type == STATS_ETHTOOL)
+ return _odp_ethtool_extra_stat_info(fd, pktio_entry->name,
+ info, num);
+ else if (pktio_entry->stats_type == STATS_SYSFS)
+ return _odp_sysfs_extra_stat_info(pktio_entry, info, num);
+
+ return 0;
+}
+
+int _odp_sock_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[], int num,
+ int fd)
+{
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED)
+ return 0;
+
+ if (pktio_entry->stats_type == STATS_ETHTOOL)
+ return _odp_ethtool_extra_stats(fd, pktio_entry->name,
+ stats, num);
+ else if (pktio_entry->stats_type == STATS_SYSFS)
+ return _odp_sysfs_extra_stats(pktio_entry, stats, num);
+
+ return 0;
+}
+
+int _odp_sock_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat, int fd)
+{
+ if (pktio_entry->stats_type == STATS_UNSUPPORTED)
+ return -1;
+
+ if (pktio_entry->stats_type == STATS_ETHTOOL) {
+ return _odp_ethtool_extra_stat_counter(fd, pktio_entry->name,
+ id, stat);
+ } else if (pktio_entry->stats_type == STATS_SYSFS) {
+ return _odp_sysfs_extra_stat_counter(pktio_entry, id, stat);
+ }
+
+ return 0;
+}
+
+pktio_stats_type_t _odp_sock_stats_type_fd(pktio_entry_t *pktio_entry, int fd)
+{
+ odp_pktio_stats_t cur_stats;
+
+ if (!_odp_ethtool_stats_get_fd(fd, pktio_entry->name, &cur_stats))
+ return STATS_ETHTOOL;
+
+ if (!_odp_sysfs_stats(pktio_entry, &cur_stats))
+ return STATS_SYSFS;
+
+ return STATS_UNSUPPORTED;
+}
+
+void _odp_sock_stats_capa(pktio_entry_t *pktio_entry,
+ odp_pktio_capability_t *capa)
+{
+ capa->stats.pktio.all_counters = 0;
+ capa->stats.pktin_queue.all_counters = 0;
+ capa->stats.pktout_queue.all_counters = 0;
+
+ if (pktio_entry->stats_type == STATS_SYSFS) {
+ capa->stats.pktio.counter.in_octets = 1;
+ capa->stats.pktio.counter.in_packets = 1;
+ capa->stats.pktio.counter.in_ucast_pkts = 1;
+ capa->stats.pktio.counter.in_mcast_pkts = 1;
+ capa->stats.pktio.counter.in_discards = 1;
+ capa->stats.pktio.counter.in_errors = 1;
+ capa->stats.pktio.counter.out_octets = 1;
+ capa->stats.pktio.counter.out_packets = 1;
+ capa->stats.pktio.counter.out_ucast_pkts = 1;
+ capa->stats.pktio.counter.out_discards = 1;
+ capa->stats.pktio.counter.out_errors = 1;
+ } else if (pktio_entry->stats_type == STATS_ETHTOOL) {
+ capa->stats.pktio.counter.in_octets = 1;
+ capa->stats.pktio.counter.in_packets = 1;
+ capa->stats.pktio.counter.in_ucast_pkts = 1;
+ capa->stats.pktio.counter.in_bcast_pkts = 1;
+ capa->stats.pktio.counter.in_mcast_pkts = 1;
+ capa->stats.pktio.counter.in_discards = 1;
+ capa->stats.pktio.counter.in_errors = 1;
+ capa->stats.pktio.counter.out_octets = 1;
+ capa->stats.pktio.counter.out_packets = 1;
+ capa->stats.pktio.counter.out_ucast_pkts = 1;
+ capa->stats.pktio.counter.out_bcast_pkts = 1;
+ capa->stats.pktio.counter.out_mcast_pkts = 1;
+ capa->stats.pktio.counter.out_discards = 1;
+ capa->stats.pktio.counter.out_errors = 1;
+ }
+}
diff --git a/platform/linux-generic/pktio/stats/sysfs_stats.c b/platform/linux-generic/pktio/stats/sysfs_stats.c
new file mode 100644
index 000000000..9d2b3ec75
--- /dev/null
+++ b/platform/linux-generic/pktio/stats/sysfs_stats.c
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <odp/api/packet_io_stats.h>
+
+#include <odp_debug_internal.h>
+#include <odp_sysfs_stats.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+#include <linux/limits.h>
+
+#define SYSFS_DIR "/sys/class/net/%s/statistics"
+
+static int sysfs_get_val(const char *fname, uint64_t *val)
+{
+ FILE *file;
+ char str[128];
+ int ret = -1;
+
+ file = fopen(fname, "rt");
+ if (file == NULL) {
+ /* do not print debug err if sysfs is not supported by
+ * kernel driver.
+ */
+ if (errno != ENOENT)
+ _ODP_ERR("fopen %s: %s\n", fname, strerror(errno));
+ return 0;
+ }
+
+ if (fgets(str, sizeof(str), file) != NULL)
+ ret = sscanf(str, "%" SCNx64, val);
+
+ (void)fclose(file);
+
+ if (ret != 1) {
+ _ODP_ERR("read %s\n", fname);
+ return -1;
+ }
+
+ return 0;
+}
+
+int _odp_sysfs_stats(pktio_entry_t *pktio_entry,
+ odp_pktio_stats_t *stats)
+{
+ char fname[256];
+ const char *dev = pktio_entry->name;
+ int ret = 0;
+
+ sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev);
+ ret -= sysfs_get_val(fname, &stats->in_octets);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
+ ret -= sysfs_get_val(fname, &stats->in_packets);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
+ ret -= sysfs_get_val(fname, &stats->in_ucast_pkts);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/multicast", dev);
+ ret -= sysfs_get_val(fname, &stats->in_mcast_pkts);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/rx_dropped", dev);
+ ret -= sysfs_get_val(fname, &stats->in_discards);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev);
+ ret -= sysfs_get_val(fname, &stats->in_errors);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/tx_bytes", dev);
+ ret -= sysfs_get_val(fname, &stats->out_octets);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
+ ret -= sysfs_get_val(fname, &stats->out_packets);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
+ ret -= sysfs_get_val(fname, &stats->out_ucast_pkts);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev);
+ ret -= sysfs_get_val(fname, &stats->out_discards);
+
+ sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev);
+ ret -= sysfs_get_val(fname, &stats->out_errors);
+
+ return ret;
+}
+
+int _odp_sysfs_extra_stat_info(pktio_entry_t *pktio_entry,
+ odp_pktio_extra_stat_info_t info[], int num)
+{
+ struct dirent *e;
+ DIR *dir;
+ char sysfs_dir[PATH_MAX];
+ int counters = 0;
+
+ snprintf(sysfs_dir, PATH_MAX, SYSFS_DIR, pktio_entry->name);
+ dir = opendir(sysfs_dir);
+ if (!dir) {
+ _ODP_ERR("Failed to open sysfs dir: %s\n", sysfs_dir);
+ return -1;
+ }
+
+ while ((e = readdir(dir)) != NULL) {
+ /* Skip . and .. */
+ if (strncmp(e->d_name, ".", 1) == 0)
+ continue;
+
+ if (info && counters < num)
+ snprintf(info[counters].name,
+ ODP_PKTIO_STATS_EXTRA_NAME_LEN, "%s",
+ e->d_name);
+ counters++;
+ }
+ (void)closedir(dir);
+
+ return counters;
+}
+
+int _odp_sysfs_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[],
+ int num)
+{
+ struct dirent *e;
+ DIR *dir;
+ char sysfs_dir[PATH_MAX];
+ char file_path[PATH_MAX];
+ int counters = 0;
+
+ snprintf(sysfs_dir, PATH_MAX, SYSFS_DIR, pktio_entry->name);
+ dir = opendir(sysfs_dir);
+ if (!dir) {
+ _ODP_ERR("Failed to open dir: %s\n", sysfs_dir);
+ return -1;
+ }
+
+ while ((e = readdir(dir)) != NULL) {
+ uint64_t val;
+
+ /* Skip . and .. */
+ if (strncmp(e->d_name, ".", 1) == 0)
+ continue;
+
+ snprintf(file_path, PATH_MAX, "%s/%s", sysfs_dir, e->d_name);
+ if (sysfs_get_val(file_path, &val)) {
+ _ODP_ERR("Failed to read file: %s/n", file_path);
+ counters = -1;
+ break;
+ }
+
+ if (stats && counters < num)
+ stats[counters] = val;
+
+ counters++;
+ }
+ (void)closedir(dir);
+
+ return counters;
+}
+
+int _odp_sysfs_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id,
+ uint64_t *stat)
+{
+ struct dirent *e;
+ DIR *dir;
+ char sysfs_dir[PATH_MAX];
+ char file_path[PATH_MAX];
+ uint32_t counters = 0;
+ int ret = -1;
+
+ snprintf(sysfs_dir, PATH_MAX, SYSFS_DIR, pktio_entry->name);
+ dir = opendir(sysfs_dir);
+ if (!dir) {
+ _ODP_ERR("Failed to open dir: %s\n", sysfs_dir);
+ return -1;
+ }
+
+ while ((e = readdir(dir)) != NULL) {
+ /* Skip . and .. */
+ if (strncmp(e->d_name, ".", 1) == 0)
+ continue;
+
+ if (counters == id) {
+ uint64_t val;
+
+ snprintf(file_path, PATH_MAX, "%s/%s",
+ sysfs_dir, e->d_name);
+ if (sysfs_get_val(file_path, &val)) {
+ _ODP_ERR("Failed to read file: %s/n", file_path);
+ } else {
+ *stat = val;
+ ret = 0;
+ }
+ break;
+ }
+ counters++;
+ }
+ (void)closedir(dir);
+
+ return ret;
+}
diff --git a/platform/linux-generic/pktio/sysfs.c b/platform/linux-generic/pktio/sysfs.c
deleted file mode 100644
index be0822ddd..000000000
--- a/platform/linux-generic/pktio/sysfs.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_packet_io_internal.h>
-#include <errno.h>
-#include <string.h>
-#include <inttypes.h>
-
-static int sysfs_get_val(const char *fname, uint64_t *val)
-{
- FILE *file;
- char str[128];
- int ret = -1;
-
- file = fopen(fname, "rt");
- if (file == NULL) {
- __odp_errno = errno;
- /* do not print debug err if sysfs is not supported by
- * kernel driver.
- */
- if (errno != ENOENT)
- ODP_ERR("fopen %s: %s\n", fname, strerror(errno));
- return 0;
- }
-
- if (fgets(str, sizeof(str), file) != NULL)
- ret = sscanf(str, "%" SCNx64, val);
-
- (void)fclose(file);
-
- if (ret != 1) {
- ODP_ERR("read %s\n", fname);
- return -1;
- }
-
- return 0;
-}
-
-int sysfs_stats(pktio_entry_t *pktio_entry,
- odp_pktio_stats_t *stats)
-{
- char fname[256];
- const char *dev = pktio_entry->s.name;
- int ret = 0;
-
- sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev);
- ret -= sysfs_get_val(fname, &stats->in_octets);
-
- sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
- ret -= sysfs_get_val(fname, &stats->in_ucast_pkts);
-
- sprintf(fname, "/sys/class/net/%s/statistics/rx_droppped", dev);
- ret -= sysfs_get_val(fname, &stats->in_discards);
-
- sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev);
- ret -= sysfs_get_val(fname, &stats->in_errors);
-
- /* stats->in_unknown_protos is not supported in sysfs */
-
- sprintf(fname, "/sys/class/net/%s/statistics/tx_bytes", dev);
- ret -= sysfs_get_val(fname, &stats->out_octets);
-
- sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
- ret -= sysfs_get_val(fname, &stats->out_ucast_pkts);
-
- sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev);
- ret -= sysfs_get_val(fname, &stats->out_discards);
-
- sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev);
- ret -= sysfs_get_val(fname, &stats->out_errors);
-
- return ret;
-}
diff --git a/platform/linux-generic/pktio/tap.c b/platform/linux-generic/pktio/tap.c
index 650c12a77..7806f825a 100644
--- a/platform/linux-generic/pktio/tap.c
+++ b/platform/linux-generic/pktio/tap.c
@@ -1,7 +1,6 @@
-/* Copyright (c) 2015, Ilya Maximets <i.maximets@samsung.com>
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015 Ilya Maximets <i.maximets@samsung.com>
+ * Copyright (c) 2021-2023 Nokia
*/
/**
@@ -29,6 +28,21 @@
#include <odp_posix_extensions.h>
+#include <odp/api/debug.h>
+#include <odp/api/hints.h>
+#include <odp/api/packet_io.h>
+#include <odp/api/random.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp/api/plat/packet_inlines.h>
+
+#include <odp_parse_internal.h>
+#include <odp_debug_internal.h>
+#include <odp_socket_common.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_classification_internal.h>
+
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@@ -39,24 +53,55 @@
#include <sys/types.h>
#include <linux/if_tun.h>
-#include <odp_api.h>
-#include <odp_packet_socket.h>
-#include <odp_packet_internal.h>
-#include <odp_packet_io_internal.h>
-#include <odp_classification_internal.h>
+typedef struct {
+ int fd; /**< file descriptor for tap interface*/
+ int skfd; /**< socket descriptor */
+ uint32_t mtu; /**< cached mtu */
+ uint32_t mtu_max; /**< maximum supported MTU value */
+ unsigned char if_mac[ETH_ALEN]; /**< MAC address of pktio side (not a
+ MAC address of kernel interface)*/
+ odp_pool_t pool; /**< pool to alloc packets from */
+} pkt_tap_t;
-#define BUF_SIZE 65536
+ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_tap_t),
+ "PKTIO_PRIVATE_SIZE too small");
+
+static inline pkt_tap_t *pkt_priv(pktio_entry_t *pktio_entry)
+{
+ return (pkt_tap_t *)(uintptr_t)(pktio_entry->pkt_priv);
+}
static int gen_random_mac(unsigned char *mac)
{
mac[0] = 0x7a; /* not multicast and local assignment bit is set */
- if (odp_random_data(mac + 1, 5, false) < 5) {
- ODP_ERR("odp_random_data failed.\n");
+ if (odp_random_data(mac + 1, 5, ODP_RANDOM_BASIC) < 5) {
+ _ODP_ERR("odp_random_data failed.\n");
return -1;
}
return 0;
}
+static int mac_addr_set_fd(int fd, const char *name,
+ const unsigned char mac_dst[])
+{
+ struct ifreq ethreq;
+ int ret;
+
+ memset(&ethreq, 0, sizeof(ethreq));
+ snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", name);
+
+ ethreq.ifr_hwaddr.sa_family = AF_UNIX;
+ memcpy(ethreq.ifr_hwaddr.sa_data, mac_dst, ETH_ALEN);
+
+ ret = ioctl(fd, SIOCSIFHWADDR, &ethreq);
+ if (ret != 0) {
+ _ODP_ERR("ioctl(SIOCSIFHWADDR): %s: \"%s\".\n", strerror(errno), ethreq.ifr_name);
+ return -1;
+ }
+
+ return 0;
+}
+
static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
pktio_entry_t *pktio_entry,
const char *devname, odp_pool_t pool)
@@ -64,7 +109,7 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
int fd, skfd, flags;
uint32_t mtu;
struct ifreq ifr;
- pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
if (strncmp(devname, "tap:", 4) != 0)
return -1;
@@ -79,8 +124,7 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
fd = open("/dev/net/tun", O_RDWR);
if (fd < 0) {
- __odp_errno = errno;
- ODP_ERR("failed to open /dev/net/tun: %s\n", strerror(errno));
+ _ODP_ERR("failed to open /dev/net/tun: %s\n", strerror(errno));
return -1;
}
@@ -94,23 +138,19 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname + 4);
if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
- __odp_errno = errno;
- ODP_ERR("%s: creating tap device failed: %s\n",
- ifr.ifr_name, strerror(errno));
+ _ODP_ERR("%s: creating tap device failed: %s\n", ifr.ifr_name, strerror(errno));
goto tap_err;
}
/* Set nonblocking mode on interface. */
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
- __odp_errno = errno;
- ODP_ERR("fcntl(F_GETFL) failed: %s\n", strerror(errno));
+ _ODP_ERR("fcntl(F_GETFL) failed: %s\n", strerror(errno));
goto tap_err;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
- __odp_errno = errno;
- ODP_ERR("fcntl(F_SETFL) failed: %s\n", strerror(errno));
+ _ODP_ERR("fcntl(F_SETFL) failed: %s\n", strerror(errno));
goto tap_err;
}
@@ -120,61 +160,102 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
/* Create AF_INET socket for network interface related operations. */
skfd = socket(AF_INET, SOCK_DGRAM, 0);
if (skfd < 0) {
- __odp_errno = errno;
- ODP_ERR("socket creation failed: %s\n", strerror(errno));
+ _ODP_ERR("socket creation failed: %s\n", strerror(errno));
goto tap_err;
}
- mtu = mtu_get_fd(skfd, devname + 4);
+ mtu = _odp_mtu_get_fd(skfd, devname + 4);
if (mtu == 0) {
- __odp_errno = errno;
- ODP_ERR("mtu_get_fd failed: %s\n", strerror(errno));
+ _ODP_ERR("_odp_mtu_get_fd failed: %s\n", strerror(errno));
goto sock_err;
}
+ tap->mtu_max = _ODP_SOCKET_MTU_MAX;
+ if (mtu > tap->mtu_max)
+ tap->mtu_max = mtu;
+
+ tap->fd = fd;
+ tap->skfd = skfd;
+ tap->mtu = mtu;
+ tap->pool = pool;
+ return 0;
+sock_err:
+ close(skfd);
+tap_err:
+ close(fd);
+ _ODP_ERR("Tap device alloc failed.\n");
+ return -1;
+}
- /* Up interface by default. */
- if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
- __odp_errno = errno;
- ODP_ERR("ioctl(SIOCGIFFLAGS) failed: %s\n", strerror(errno));
+static int tap_pktio_start(pktio_entry_t *pktio_entry)
+{
+ struct ifreq ifr;
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
+
+ odp_memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s",
+ (char *)pktio_entry->name + 4);
+
+ /* Up interface by default. */
+ if (ioctl(tap->skfd, SIOCGIFFLAGS, &ifr) < 0) {
+ _ODP_ERR("ioctl(SIOCGIFFLAGS) failed: %s\n", strerror(errno));
goto sock_err;
}
ifr.ifr_flags |= IFF_UP;
ifr.ifr_flags |= IFF_RUNNING;
- if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) {
- __odp_errno = errno;
- ODP_ERR("failed to come up: %s\n", strerror(errno));
+ if (ioctl(tap->skfd, SIOCSIFFLAGS, &ifr) < 0) {
+ _ODP_ERR("failed to come up: %s\n", strerror(errno));
goto sock_err;
}
- tap->fd = fd;
- tap->skfd = skfd;
- tap->mtu = mtu;
- tap->pool = pool;
return 0;
sock_err:
- close(skfd);
-tap_err:
- close(fd);
- ODP_ERR("Tap device alloc failed.\n");
+ _ODP_ERR("Tap device open failed.\n");
+ return -1;
+}
+
+static int tap_pktio_stop(pktio_entry_t *pktio_entry)
+{
+ struct ifreq ifr;
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
+
+ odp_memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, IF_NAMESIZE, "%s",
+ (char *)pktio_entry->name + 4);
+
+ /* Up interface by default. */
+ if (ioctl(tap->skfd, SIOCGIFFLAGS, &ifr) < 0) {
+ _ODP_ERR("ioctl(SIOCGIFFLAGS) failed: %s\n", strerror(errno));
+ goto sock_err;
+ }
+
+ ifr.ifr_flags &= ~IFF_UP;
+ ifr.ifr_flags &= ~IFF_RUNNING;
+
+ if (ioctl(tap->skfd, SIOCSIFFLAGS, &ifr) < 0) {
+ _ODP_ERR("failed to come up: %s\n", strerror(errno));
+ goto sock_err;
+ }
+
+ return 0;
+sock_err:
+ _ODP_ERR("Tap device open failed.\n");
return -1;
}
static int tap_pktio_close(pktio_entry_t *pktio_entry)
{
int ret = 0;
- pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
if (tap->fd != -1 && close(tap->fd) != 0) {
- __odp_errno = errno;
- ODP_ERR("close(tap->fd): %s\n", strerror(errno));
+ _ODP_ERR("close(tap->fd): %s\n", strerror(errno));
ret = -1;
}
if (tap->skfd != -1 && close(tap->skfd) != 0) {
- __odp_errno = errno;
- ODP_ERR("close(tap->skfd): %s\n", strerror(errno));
+ _ODP_ERR("close(tap->skfd): %s\n", strerror(errno));
ret = -1;
}
@@ -186,103 +267,135 @@ static odp_packet_t pack_odp_pkt(pktio_entry_t *pktio_entry, const void *data,
{
odp_packet_t pkt;
odp_packet_hdr_t *pkt_hdr;
- odp_packet_hdr_t parsed_hdr;
int num;
+ uint16_t frame_offset = pktio_entry->pktin_frame_offset;
+ const odp_proto_layer_t layer = pktio_entry->parse_layer;
+ const odp_pktin_config_opt_t opt = pktio_entry->config.pktin;
- if (pktio_cls_enabled(pktio_entry)) {
- if (cls_classify_packet(pktio_entry, data, len, len,
- &pktio_entry->s.pkt_tap.pool,
- &parsed_hdr)) {
- return ODP_PACKET_INVALID;
- }
- }
-
- num = packet_alloc_multi(pktio_entry->s.pkt_tap.pool, len, &pkt, 1);
-
+ num = _odp_packet_alloc_multi(pkt_priv(pktio_entry)->pool,
+ len + frame_offset, &pkt, 1);
if (num != 1)
return ODP_PACKET_INVALID;
+ pkt_hdr = packet_hdr(pkt);
+
+ if (frame_offset)
+ pull_head(pkt_hdr, frame_offset);
+
if (odp_packet_copy_from_mem(pkt, 0, len, data) < 0) {
- ODP_ERR("failed to copy packet data\n");
+ _ODP_ERR("failed to copy packet data\n");
odp_packet_free(pkt);
return ODP_PACKET_INVALID;
}
- pkt_hdr = odp_packet_hdr(pkt);
+ if (layer) {
+ if (_odp_packet_parse_common(pkt_hdr, data, len, len, layer,
+ opt) < 0) {
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
- if (pktio_cls_enabled(pktio_entry))
- copy_packet_cls_metadata(&parsed_hdr, pkt_hdr);
- else
- packet_parse_layer(pkt_hdr,
- pktio_entry->s.config.parser.layer);
+ if (pktio_cls_enabled(pktio_entry)) {
+ odp_pool_t new_pool;
+
+ if (_odp_cls_classify_packet(pktio_entry, data,
+ &new_pool, pkt_hdr)) {
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ if (odp_unlikely(_odp_pktio_packet_to_pool(
+ &pkt, &pkt_hdr, new_pool))) {
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+ }
+ }
packet_set_ts(pkt_hdr, ts);
- pkt_hdr->input = pktio_entry->s.handle;
+ pkt_hdr->input = pktio_entry->handle;
return pkt;
}
static int tap_pktio_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- odp_packet_t pkts[], int len)
+ odp_packet_t pkts[], int num)
{
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
ssize_t retval;
int i;
- uint8_t buf[BUF_SIZE];
- pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+ uint32_t mtu = tap->mtu;
+ uint8_t buf[mtu];
odp_time_t ts_val;
odp_time_t *ts = NULL;
+ int num_rx = 0;
+ int num_cls = 0;
+ const int cls_enabled = pktio_cls_enabled(pktio_entry);
+ odp_packet_t pkt;
- odp_ticketlock_lock(&pktio_entry->s.rxl);
+ odp_ticketlock_lock(&pktio_entry->rxl);
- if (pktio_entry->s.config.pktin.bit.ts_all ||
- pktio_entry->s.config.pktin.bit.ts_ptp)
+ if (pktio_entry->config.pktin.bit.ts_all ||
+ pktio_entry->config.pktin.bit.ts_ptp)
ts = &ts_val;
- for (i = 0; i < len; i++) {
+ for (i = 0; i < num; i++) {
do {
- retval = read(tap->fd, buf, BUF_SIZE);
+ retval = read(tap->fd, buf, mtu);
} while (retval < 0 && errno == EINTR);
if (ts != NULL)
ts_val = odp_time_global();
if (retval < 0) {
- __odp_errno = errno;
break;
}
- pkts[i] = pack_odp_pkt(pktio_entry, buf, retval, ts);
- if (pkts[i] == ODP_PACKET_INVALID)
+ pkt = pack_odp_pkt(pktio_entry, buf, retval, ts);
+ if (pkt == ODP_PACKET_INVALID)
break;
+
+ if (cls_enabled) {
+ /* Enqueue packets directly to classifier destination queue */
+ pkts[num_cls++] = pkt;
+ num_cls = _odp_cls_enq(pkts, num_cls, (i + 1 == num));
+ } else {
+ pkts[num_rx++] = pkt;
+ }
}
- odp_ticketlock_unlock(&pktio_entry->s.rxl);
+ /* Enqueue remaining classified packets */
+ if (odp_unlikely(num_cls))
+ _odp_cls_enq(pkts, num_cls, true);
- return i;
+ odp_ticketlock_unlock(&pktio_entry->rxl);
+
+ return num_rx;
}
static int tap_pktio_send_lockless(pktio_entry_t *pktio_entry,
- const odp_packet_t pkts[], int len)
+ const odp_packet_t pkts[], int num)
{
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
ssize_t retval;
int i, n;
uint32_t pkt_len;
- uint8_t buf[BUF_SIZE];
- pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+ uint32_t mtu = tap->mtu;
+ uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry);
+ uint8_t buf[mtu];
- for (i = 0; i < len; i++) {
+ for (i = 0; i < num; i++) {
pkt_len = odp_packet_len(pkts[i]);
- if (pkt_len > tap->mtu) {
+ if (odp_unlikely(pkt_len > mtu)) {
if (i == 0) {
- __odp_errno = EMSGSIZE;
return -1;
}
break;
}
if (odp_packet_copy_to_mem(pkts[i], 0, pkt_len, buf) < 0) {
- ODP_ERR("failed to copy packet data\n");
+ _ODP_ERR("failed to copy packet data\n");
break;
}
@@ -292,19 +405,22 @@ static int tap_pktio_send_lockless(pktio_entry_t *pktio_entry,
if (retval < 0) {
if (i == 0 && SOCK_ERR_REPORT(errno)) {
- __odp_errno = errno;
- ODP_ERR("write(): %s\n", strerror(errno));
+ _ODP_ERR("write(): %s\n", strerror(errno));
return -1;
}
break;
} else if ((uint32_t)retval != pkt_len) {
- ODP_ERR("sent partial ethernet packet\n");
+ _ODP_ERR("sent partial ethernet packet\n");
if (i == 0) {
- __odp_errno = EMSGSIZE;
return -1;
}
break;
}
+
+ if (tx_ts_enabled) {
+ if (odp_unlikely(packet_hdr(pkts[i])->p.flags.ts_set))
+ _odp_pktio_tx_ts_set(pktio_entry);
+ }
}
for (n = 0; n < i; n++)
@@ -314,15 +430,15 @@ static int tap_pktio_send_lockless(pktio_entry_t *pktio_entry,
}
static int tap_pktio_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
- const odp_packet_t pkts[], int len)
+ const odp_packet_t pkts[], int num)
{
int ret;
- odp_ticketlock_lock(&pktio_entry->s.txl);
+ odp_ticketlock_lock(&pktio_entry->txl);
- ret = tap_pktio_send_lockless(pktio_entry, pkts, len);
+ ret = tap_pktio_send_lockless(pktio_entry, pkts, num);
- odp_ticketlock_unlock(&pktio_entry->s.txl);
+ odp_ticketlock_unlock(&pktio_entry->txl);
return ret;
}
@@ -331,49 +447,102 @@ static uint32_t tap_mtu_get(pktio_entry_t *pktio_entry)
{
uint32_t ret;
- ret = mtu_get_fd(pktio_entry->s.pkt_tap.skfd,
- pktio_entry->s.name + 4);
+ ret = _odp_mtu_get_fd(pkt_priv(pktio_entry)->skfd,
+ pktio_entry->name + 4);
if (ret > 0)
- pktio_entry->s.pkt_tap.mtu = ret;
+ pkt_priv(pktio_entry)->mtu = ret;
return ret;
}
+static int tap_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input,
+ uint32_t maxlen_output ODP_UNUSED)
+{
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
+ int ret;
+
+ ret = _odp_mtu_set_fd(tap->skfd, pktio_entry->name + 4, maxlen_input);
+ if (ret)
+ return ret;
+
+ tap->mtu = maxlen_input;
+
+ return 0;
+}
+
static int tap_promisc_mode_set(pktio_entry_t *pktio_entry,
odp_bool_t enable)
{
- return promisc_mode_set_fd(pktio_entry->s.pkt_tap.skfd,
- pktio_entry->s.name + 4, enable);
+ return _odp_promisc_mode_set_fd(pkt_priv(pktio_entry)->skfd,
+ pktio_entry->name + 4, enable);
}
static int tap_promisc_mode_get(pktio_entry_t *pktio_entry)
{
- return promisc_mode_get_fd(pktio_entry->s.pkt_tap.skfd,
- pktio_entry->s.name + 4);
+ return _odp_promisc_mode_get_fd(pkt_priv(pktio_entry)->skfd,
+ pktio_entry->name + 4);
}
static int tap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
{
- memcpy(mac_addr, pktio_entry->s.pkt_tap.if_mac, ETH_ALEN);
+ memcpy(mac_addr, pkt_priv(pktio_entry)->if_mac, ETH_ALEN);
return ETH_ALEN;
}
+static int tap_mac_addr_set(pktio_entry_t *pktio_entry, const void *mac_addr)
+{
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
+
+ memcpy(tap->if_mac, mac_addr, ETH_ALEN);
+
+ return mac_addr_set_fd(tap->fd, (char *)pktio_entry->name + 4,
+ tap->if_mac);
+}
+
+static int tap_link_status(pktio_entry_t *pktio_entry)
+{
+ return _odp_link_status_fd(pkt_priv(pktio_entry)->skfd,
+ pktio_entry->name + 4);
+}
+
+static int tap_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info)
+{
+ return _odp_link_info_fd(pkt_priv(pktio_entry)->skfd, pktio_entry->name + 4, info);
+}
+
static int tap_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
odp_pktio_capability_t *capa)
{
+ pkt_tap_t *tap = pkt_priv(pktio_entry);
+
memset(capa, 0, sizeof(odp_pktio_capability_t));
capa->max_input_queues = 1;
capa->max_output_queues = 1;
capa->set_op.op.promisc_mode = 1;
+ capa->set_op.op.mac_addr = 1;
+ capa->set_op.op.maxlen = 1;
+
+ capa->maxlen.equal = true;
+ capa->maxlen.min_input = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_input = tap->mtu_max;
+ capa->maxlen.min_output = _ODP_SOCKET_MTU_MIN;
+ capa->maxlen.max_output = tap->mtu_max;
odp_pktio_config_init(&capa->config);
capa->config.pktin.bit.ts_all = 1;
capa->config.pktin.bit.ts_ptp = 1;
+
+ capa->config.pktout.bit.ts_ena = 1;
+ capa->config.pktout.bit.tx_compl_ena = 1;
+ capa->tx_compl.mode_all = 1;
+ capa->tx_compl.mode_event = 1;
+ capa->tx_compl.mode_poll = 1;
+
return 0;
}
-const pktio_if_ops_t tap_pktio_ops = {
+const pktio_if_ops_t _odp_tap_pktio_ops = {
.name = "tap",
.print = NULL,
.init_global = NULL,
@@ -381,16 +550,21 @@ const pktio_if_ops_t tap_pktio_ops = {
.term = NULL,
.open = tap_pktio_open,
.close = tap_pktio_close,
- .start = NULL,
- .stop = NULL,
+ .start = tap_pktio_start,
+ .stop = tap_pktio_stop,
.recv = tap_pktio_recv,
.send = tap_pktio_send,
- .mtu_get = tap_mtu_get,
+ .maxlen_get = tap_mtu_get,
+ .maxlen_set = tap_mtu_set,
.promisc_mode_set = tap_promisc_mode_set,
.promisc_mode_get = tap_promisc_mode_get,
.mac_get = tap_mac_addr_get,
+ .mac_set = tap_mac_addr_set,
+ .link_status = tap_link_status,
+ .link_info = tap_link_info,
.capability = tap_capability,
- .pktin_ts_res = NULL,
- .pktin_ts_from_ns = NULL,
+ .pktio_ts_res = NULL,
+ .pktio_ts_from_ns = NULL,
+ .pktio_time = NULL,
.config = NULL
};
diff --git a/platform/linux-generic/test/.gitignore b/platform/linux-generic/test/.gitignore
new file mode 100644
index 000000000..88eb4dce8
--- /dev/null
+++ b/platform/linux-generic/test/.gitignore
@@ -0,0 +1,3 @@
+*.log
+*.trs
+*.env
diff --git a/platform/linux-generic/test/Makefile.am b/platform/linux-generic/test/Makefile.am
new file mode 100644
index 000000000..7aca5fd3f
--- /dev/null
+++ b/platform/linux-generic/test/Makefile.am
@@ -0,0 +1,58 @@
+include $(top_srcdir)/test/Makefile.inc
+TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/validation
+
+if WITH_OPENSSL_CRYPTO
+TESTS_ENVIRONMENT += WITH_OPENSSL_CRYPTO=1
+else
+TESTS_ENVIRONMENT += WITH_OPENSSL_CRYPTO=0
+endif
+
+SUBDIRS =
+TESTS =
+
+if test_vald
+TESTS += validation/api/pktio/pktio_run.sh \
+ validation/api/pktio/pktio_run_tap.sh \
+ validation/api/shmem/shmem_linux$(EXEEXT)
+
+SUBDIRS += validation/api/pktio \
+ validation/api/shmem \
+ pktio_ipc \
+ example \
+ performance
+
+if WITH_ML
+TESTS += validation/api/ml/ml_linux$(EXEEXT)
+SUBDIRS += validation/api/ml
+endif
+
+if ODP_PKTIO_PCAP
+TESTS += validation/api/pktio/pktio_run_pcap.sh
+endif
+if PKTIO_DPDK
+TESTS += validation/api/pktio/pktio_run_dpdk.sh
+endif
+TESTS += pktio_ipc/pktio_ipc_run.sh
+SUBDIRS += pktio_ipc
+else
+#performance tests refer to pktio_env
+if test_perf
+SUBDIRS += validation/api/pktio \
+ performance
+endif
+endif
+
+TEST_EXTENSIONS = .sh
+
+TESTNAME = linux-generic
+
+TESTENV = tests-$(TESTNAME).env
+
+test_DATA = $(TESTENV)
+
+DISTCLEANFILES = $(TESTENV)
+.PHONY: $(TESTENV)
+$(TESTENV):
+ echo "TESTS=\"$(TESTS)\"" > $@
+ echo "$(TESTS_ENVIRONMENT)" >> $@
+ echo "$(LOG_COMPILER)" >> $@
diff --git a/platform/linux-generic/test/example/Makefile.am b/platform/linux-generic/test/example/Makefile.am
new file mode 100644
index 000000000..a9d0948de
--- /dev/null
+++ b/platform/linux-generic/test/example/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = \
+ classifier \
+ ipsec_api \
+ ipsec_crypto \
+ l2fwd_simple \
+ l3fwd \
+ packet \
+ ping \
+ simple_pipeline \
+ switch
diff --git a/platform/linux-generic/test/example/classifier/Makefile.am b/platform/linux-generic/test/example/classifier/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/classifier/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/classifier/pktio_env b/platform/linux-generic/test/example/classifier/pktio_env
new file mode 100644
index 000000000..1a3cc51a2
--- /dev/null
+++ b/platform/linux-generic/test/example/classifier/pktio_env
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create one pcap interface
+# which uses udp64.pcap to inject traffic.
+#
+# Network set-up
+# +---------+ +-----------+
+# |pcap intf| IF0<---> | Classifier|
+# +--------- +-----------+
+#
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+echo "using PCAP in=${PCAP_IN}"
+
+IF0=pcap:in=${PCAP_IN}
+TIME_OUT_VAL=1
+CPASS_COUNT_ARG1=100
+CPASS_COUNT_ARG2=100
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ return 0;
+}
+
+setup_interfaces()
+{
+ return 0
+}
+
+cleanup_interfaces()
+{
+ return 0
+}
diff --git a/platform/linux-generic/test/example/ipsec_api/Makefile.am b/platform/linux-generic/test/example/ipsec_api/Makefile.am
new file mode 100644
index 000000000..2535ad466
--- /dev/null
+++ b/platform/linux-generic/test/example/ipsec_api/Makefile.am
@@ -0,0 +1,21 @@
+EXTRA_DIST = pktio_env
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/example/ipsec_api/pktio_env b/platform/linux-generic/test/example/ipsec_api/pktio_env
new file mode 100644
index 000000000..67a59919a
--- /dev/null
+++ b/platform/linux-generic/test/example/ipsec_api/pktio_env
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# ipsec_api application uses two loop devices loop0 and loop1.
+#
+
+if [ "$0" == "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+ exit 1
+fi
+
+# Absolute path to the .env file.
+LINUX_ENV_PATH=$PWD/../../platform/linux-generic/test
+
+TESTENV="tests-linux-generic.env"
+
+if [ -f $LINUX_ENV_PATH/$TESTENV ]; then
+ source $LINUX_ENV_PATH/$TESTENV
+else
+ echo "BUG: unable to find $TESTENV!"
+ echo "$TESTENV has to be in following directory: "
+ echo " $LINUX_ENV_PATH"
+ exit 1
+fi
+
+# Skip IPsec example tests when there's no OpenSSL.
+if [ -n "$WITH_OPENSSL_CRYPTO" ] && [ ${WITH_OPENSSL_CRYPTO} -eq 0 ]; then
+ echo "Crypto not supported. Skipping."
+ exit 77
+fi
+
+if [ -n "$ODPH_PROC_MODE" ] && [ ${ODPH_PROC_MODE} -ne 0 ]; then
+ echo "Process mode not supported. Skipping."
+ exit 77
+fi
+
+# Skip live and router mode tests.
+if [ ${IPSEC_APP_MODE} -eq 1 ] || [ ${IPSEC_APP_MODE} -eq 2 ]; then
+ echo "IPsec Live / Router mode test. Skipping."
+ exit 77
+fi
+
+IF0=p7p1
+IF1=p8p1
+
+NEXT_HOP_MAC0=08:00:27:76:B5:E0
+NEXT_HOP_MAC1=08:00:27:F5:8B:DB
+
+LIF0=loop1
+LIF1=loop2
+
+IF_LIST=$LIF0,$LIF1
+ROUTE_IF_INB=$LIF0
+ROUTE_IF_OUTB=$LIF1
+OUT_IF=$LIF1
+IN_IF=$LIF0
+
+validate_result()
+{
+ return 0
+}
+
+setup_interfaces()
+{
+ return 0
+}
+
+cleanup_interfaces()
+{
+ return 0
+}
diff --git a/platform/linux-generic/test/example/ipsec_crypto/Makefile.am b/platform/linux-generic/test/example/ipsec_crypto/Makefile.am
new file mode 100644
index 000000000..2535ad466
--- /dev/null
+++ b/platform/linux-generic/test/example/ipsec_crypto/Makefile.am
@@ -0,0 +1,21 @@
+EXTRA_DIST = pktio_env
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/example/ipsec_crypto/pktio_env b/platform/linux-generic/test/example/ipsec_crypto/pktio_env
new file mode 100644
index 000000000..82d68f860
--- /dev/null
+++ b/platform/linux-generic/test/example/ipsec_crypto/pktio_env
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# ipsec_api application uses two loop devices loop0 and loop1.
+#
+
+if [ "$0" == "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+ exit 1
+fi
+
+# Absolute path to the .env file.
+LINUX_ENV_PATH=$PWD/../../platform/linux-generic/test
+
+TESTENV="tests-linux-generic.env"
+
+if [ -f $LINUX_ENV_PATH/$TESTENV ]; then
+ source $LINUX_ENV_PATH/$TESTENV
+else
+ echo "BUG: unable to find $TESTENV!"
+ echo "$TESTENV has to be in following directory: "
+ echo " $LINUX_ENV_PATH"
+ exit 1
+fi
+
+# Skip IPsec example tests when there's no OpenSSL.
+if [ -n "$WITH_OPENSSL_CRYPTO" ] && [ ${WITH_OPENSSL_CRYPTO} -eq 0 ]; then
+ echo "Crypto not supported. Skipping."
+ exit 77
+fi
+
+if [ -n "$ODPH_PROC_MODE" ] && [ ${ODPH_PROC_MODE} -ne 0 ]; then
+ echo "Process mode not supported. Skipping."
+ exit 77
+fi
+
+# Skip live and router mode tests.
+if [ ${IPSEC_APP_MODE} -eq 1 ] || [ ${IPSEC_APP_MODE} -eq 2 ]; then
+ echo "Live / Router mode test. Skipping."
+ exit 77
+fi
+
+IF0=p7p1
+IF1=p8p1
+
+NEXT_HOP_MAC0=08:00:27:76:B5:E0
+NEXT_HOP_MAC1=08:00:27:F5:8B:DB
+
+LIF0=loop1
+LIF1=loop2
+
+IF_LIST=$LIF0,$LIF1
+ROUTE_IF_INB=$LIF0
+ROUTE_IF_OUTB=$LIF1
+OUT_IF=$LIF1
+IN_IF=$LIF0
+
+validate_result()
+{
+ return 0
+}
+
+setup_interfaces()
+{
+ return 0
+}
+
+cleanup_interfaces()
+{
+ return 0
+}
diff --git a/platform/linux-generic/test/example/l2fwd_simple/Makefile.am b/platform/linux-generic/test/example/l2fwd_simple/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/l2fwd_simple/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/l2fwd_simple/pktio_env b/platform/linux-generic/test/example/l2fwd_simple/pktio_env
new file mode 100644
index 000000000..016ebf660
--- /dev/null
+++ b/platform/linux-generic/test/example/l2fwd_simple/pktio_env
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 <---> IF1
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+echo "using PCAP_IN = ${PCAP_IN}"
+
+IF0=pcap:in=${PCAP_IN}
+IF1=pcap:out=pcapout.pcap
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ if [ `stat -c %s pcapout.pcap` -ne `stat -c %s ${PCAP_IN}` ]; then
+ echo "File sizes disagree"
+ exit 1
+ fi
+
+ rm -f pcapout.pcap
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1."
+ return 0
+}
diff --git a/platform/linux-generic/test/example/l3fwd/Makefile.am b/platform/linux-generic/test/example/l3fwd/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/l3fwd/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/l3fwd/pktio_env b/platform/linux-generic/test/example/l3fwd/pktio_env
new file mode 100644
index 000000000..b176d5a3b
--- /dev/null
+++ b/platform/linux-generic/test/example/l3fwd/pktio_env
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 <---> IF1
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+PCAP_OUT="pcapout.pcap"
+PCAP_IN_SIZE=`stat -c %s ${PCAP_IN}`
+echo "using PCAP_IN = ${PCAP_IN}, PCAP_OUT = ${PCAP_OUT}"
+
+IF0=pcap:in=${PCAP_IN}
+IF1=pcap:out=${PCAP_OUT}
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
+ if [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
+ echo "in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+ exit 1
+ fi
+
+ echo "Pass: in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+ rm -f pcapout.pcap
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1."
+ return 0
+}
diff --git a/platform/linux-generic/test/example/packet/Makefile.am b/platform/linux-generic/test/example/packet/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/packet/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/packet/pktio_env b/platform/linux-generic/test/example/packet/pktio_env
new file mode 100644
index 000000000..47d643ef0
--- /dev/null
+++ b/platform/linux-generic/test/example/packet/pktio_env
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 <---> IF1
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+PCAP_OUT="pcapout.pcap"
+PCAP_IN_SIZE=`stat -c %s ${PCAP_IN}`
+echo "using PCAP in=${PCAP_IN}:out=${PCAP_OUT} size %${PCAP_IN_SIZE}"
+
+IF0=pcap:in=${PCAP_IN}:loops=10
+IF1=pcap:in=${PCAP_IN}:out=${PCAP_OUT}
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
+ if [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
+ echo "Error: in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+ exit 1
+ fi
+
+ rm -f pcapout.pcap
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1."
+ return 0
+}
diff --git a/platform/linux-generic/test/example/ping/Makefile.am b/platform/linux-generic/test/example/ping/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/ping/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/ping/pktio_env b/platform/linux-generic/test/example/ping/pktio_env
new file mode 100644
index 000000000..8b3464ee4
--- /dev/null
+++ b/platform/linux-generic/test/example/ping/pktio_env
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 <---> IF1
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name icmp_echo_req.pcap -print -quit`
+PCAP_OUT="pcapout.pcap"
+PCAP_IN_SIZE=`stat -c %s ${PCAP_IN}`
+echo "using PCAP in=${PCAP_IN}:out=${PCAP_OUT} size %${PCAP_IN_SIZE}"
+
+IF0=pcap:in=${PCAP_IN}:out=${PCAP_OUT}
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ PCAP_OUT_SIZE=`stat -c %s ${PCAP_OUT}`
+ if [ ${PCAP_IN_SIZE} -ne ${PCAP_OUT_SIZE} ]; then
+ echo "Error: in:${PCAP_IN_SIZE} out:${PCAP_OUT_SIZE}"
+ exit 1
+ fi
+
+ echo "pcap in size:${PCAP_IN_SIZE} pcap out size:${PCAP_OUT_SIZE}"
+ rm -f pcapout.pcap
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1."
+ return 0
+}
diff --git a/platform/linux-generic/test/example/simple_pipeline/Makefile.am b/platform/linux-generic/test/example/simple_pipeline/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/simple_pipeline/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/simple_pipeline/pktio_env b/platform/linux-generic/test/example/simple_pipeline/pktio_env
new file mode 100644
index 000000000..016ebf660
--- /dev/null
+++ b/platform/linux-generic/test/example/simple_pipeline/pktio_env
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 <---> IF1
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+echo "using PCAP_IN = ${PCAP_IN}"
+
+IF0=pcap:in=${PCAP_IN}
+IF1=pcap:out=pcapout.pcap
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ if [ `stat -c %s pcapout.pcap` -ne `stat -c %s ${PCAP_IN}` ]; then
+ echo "File sizes disagree"
+ exit 1
+ fi
+
+ rm -f pcapout.pcap
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1."
+ return 0
+}
diff --git a/platform/linux-generic/test/example/switch/Makefile.am b/platform/linux-generic/test/example/switch/Makefile.am
new file mode 100644
index 000000000..2ffced539
--- /dev/null
+++ b/platform/linux-generic/test/example/switch/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = pktio_env
diff --git a/platform/linux-generic/test/example/switch/pktio_env b/platform/linux-generic/test/example/switch/pktio_env
new file mode 100644
index 000000000..8df735b3b
--- /dev/null
+++ b/platform/linux-generic/test/example/switch/pktio_env
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Marvell
+#
+# Script to setup interfaces used for running application on linux-generic.
+#
+# For linux-generic the default behavior is to create two pcap interfaces
+# and one interface uses udp64.pcap to inject traffic. An output pcap file
+# is generated via second interface.
+#
+# Network set-up
+# IF0 |---> IF1
+# |---> IF2
+# |---> IF3
+
+NUM_RX_PORT=3
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+echo "Switch test using PCAP_IN = ${PCAP_IN}"
+
+IF0=pcap:in=${PCAP_IN}
+IF1=pcap:out=pcapout1.pcap
+IF2=pcap:out=pcapout2.pcap
+IF3=pcap:out=pcapout3.pcap
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ for i in `seq 1 $NUM_RX_PORT`;
+ do
+ if [ `stat -c %s pcapout${i}.pcap` -ne `stat -c %s ${PCAP_IN}` ]; then
+ echo "Error: Output file $i size not matching"
+ exit 1
+ fi
+ rm -f pcapout${i}.pcap
+ done
+}
+
+setup_interfaces()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1, $IF2, $IF3."
+ return 0
+}
+
+cleanup_interfaces()
+{
+ echo "pktio: cleaning up test interfaces $IF0, $IF1, $IF2, $IF3."
+ return 0
+}
diff --git a/platform/linux-generic/test/inline-timer.conf b/platform/linux-generic/test/inline-timer.conf
new file mode 100644
index 000000000..fa3b6982f
--- /dev/null
+++ b/platform/linux-generic/test/inline-timer.conf
@@ -0,0 +1,8 @@
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+timer: {
+ # Enable inline timer implementation
+ inline = 1
+}
diff --git a/platform/linux-generic/test/packet_align.conf b/platform/linux-generic/test/packet_align.conf
new file mode 100644
index 000000000..fb1418348
--- /dev/null
+++ b/platform/linux-generic/test/packet_align.conf
@@ -0,0 +1,21 @@
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+pool: {
+ pkt: {
+ # Non-zero, larger than cache line size, power of two value.
+ base_align = 128
+ }
+
+ buf: {
+ # Non-zero, larger than cache line size, power of two value.
+ min_align = 256
+ }
+}
+
+pktio: {
+ # Ethernet header offset is 2 bytes, so that Ethernet payload
+ # starts at 16 byte alignment.
+ pktin_frame_offset = 2
+}
diff --git a/platform/linux-generic/test/performance/Makefile.am b/platform/linux-generic/test/performance/Makefile.am
new file mode 100644
index 000000000..4070f09f2
--- /dev/null
+++ b/platform/linux-generic/test/performance/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = dmafwd
diff --git a/platform/linux-generic/test/performance/dmafwd/Makefile.am b/platform/linux-generic/test/performance/dmafwd/Makefile.am
new file mode 100644
index 000000000..91d42cc74
--- /dev/null
+++ b/platform/linux-generic/test/performance/dmafwd/Makefile.am
@@ -0,0 +1,18 @@
+EXTRA_DIST = pktio_env
+
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/performance/dmafwd/pktio_env b/platform/linux-generic/test/performance/dmafwd/pktio_env
new file mode 100644
index 000000000..91075973e
--- /dev/null
+++ b/platform/linux-generic/test/performance/dmafwd/pktio_env
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+
+PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+PCAP_OUT=dmafwd_out.pcap
+IF0=pcap:in=${PCAP_IN}:out=${PCAP_OUT}
+DUMP=tcpdump
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "ERROR: Platform specific env file has to be sourced."
+fi
+
+validate_result()
+{
+ local RET=0
+
+ if command -v ${DUMP}; then
+ local VALIN=valin
+ local VALOUT=valout
+
+ ${DUMP} -r ${PCAP_IN} -t -x > ${VALIN}
+ ${DUMP} -r ${PCAP_OUT} -t -x > ${VALOUT}
+ diff ${VALIN} ${VALOUT}
+ RET=$?
+ rm -f ${VALIN}
+ rm -f ${VALOUT}
+ else
+ echo "WARNING: No ${DUMP} available, using \"stat\" for diff"
+ local SZIN=$(stat -c %s ${PCAP_IN})
+ local SZOUT=$(stat -c %s ${PCAP_OUT})
+
+ if [ ${SZIN} -ne ${SZOUT} ]; then
+ RET=1
+ fi
+ fi
+
+ rm -f ${PCAP_OUT}
+
+ if [ $RET -ne 0 ]; then
+ echo "ERROR: Input and output captures do not match, exiting"
+ exit 1
+ fi
+
+ return 0
+}
+
+setup_interfaces()
+{
+ return 0
+}
+
+cleanup_interfaces()
+{
+ return 0
+}
diff --git a/test/linux-generic/pktio_ipc/.gitignore b/platform/linux-generic/test/pktio_ipc/.gitignore
index 49ee4fd29..49ee4fd29 100644
--- a/test/linux-generic/pktio_ipc/.gitignore
+++ b/platform/linux-generic/test/pktio_ipc/.gitignore
diff --git a/platform/linux-generic/test/pktio_ipc/Makefile.am b/platform/linux-generic/test/pktio_ipc/Makefile.am
new file mode 100644
index 000000000..b9623cc76
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/Makefile.am
@@ -0,0 +1,31 @@
+include $(top_srcdir)/test/Makefile.inc
+TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/validation
+
+test_PROGRAMS = pktio_ipc1\
+ pktio_ipc2
+
+pktio_ipc1_SOURCES = pktio_ipc1.c ipc_common.c ipc_common.h
+pktio_ipc2_SOURCES = pktio_ipc2.c ipc_common.c ipc_common.h
+
+dist_check_SCRIPTS = pktio_ipc_run.sh
+test_SCRIPTS = $(dist_check_SCRIPTS)
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/pktio_ipc/ipc_common.c b/platform/linux-generic/test/pktio_ipc/ipc_common.c
new file mode 100644
index 000000000..128a7c6e1
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/ipc_common.c
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#include "ipc_common.h"
+
+/** Start time in seconds */
+int start_time_sec;
+/** Run time in seconds */
+int run_time_sec;
+/** Pid of the master process */
+int master_pid;
+
+int ipc_odp_packet_send_or_free(odp_pktio_t pktio,
+ odp_packet_t pkt_tbl[], int num)
+{
+ int ret;
+ int sent = 0;
+ odp_time_t start_time;
+ odp_time_t end_time;
+ odp_pktout_queue_t pktout;
+ int i;
+
+ memset(&pktout, 0, sizeof(pktout));
+ start_time = odp_time_local();
+ end_time = odp_time_add_ns(start_time, ODP_TIME_SEC_IN_NS);
+
+ if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
+ ODPH_ERR("no output queue\n");
+ return -1;
+ }
+
+ while (sent != num) {
+ ret = odp_pktout_send(pktout, &pkt_tbl[sent], num - sent);
+ if (ret < 0) {
+ ODPH_ERR("odp_pktout_send return %d\n", ret);
+ for (i = sent; i < num; i++)
+ odp_packet_free(pkt_tbl[i]);
+ return -1;
+ }
+
+ sent += ret;
+
+ if (odp_time_cmp(end_time, odp_time_local()) < 0) {
+ for (i = sent; i < num; i++)
+ odp_packet_free(pkt_tbl[i]);
+ ODPH_ERR("Send Timeout!\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+odp_pktio_t create_pktio(odp_pool_t pool, int master_pid)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_t ipc_pktio;
+ char name[30];
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (master_pid)
+ sprintf(name, TEST_IPC_PKTIO_PID_NAME, master_pid);
+ else
+ sprintf(name, TEST_IPC_PKTIO_NAME);
+
+ printf("pid: %d, create IPC pktio %s\n", getpid(), name);
+ ipc_pktio = odp_pktio_open(name, pool, &pktio_param);
+ if (ipc_pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Error: ipc pktio %s create failed.\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktin_queue_config(ipc_pktio, NULL)) {
+ ODPH_ERR("Input queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktout_queue_config(ipc_pktio, NULL)) {
+ ODPH_ERR("Output queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ return ipc_pktio;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+void parse_args(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ static struct option longopts[] = {
+ {"start-timeout", required_argument, NULL, 's'},
+ {"run-time", required_argument, NULL, 't'},
+ {"pid", required_argument, NULL, 'p'}, /* master process pid */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ start_time_sec = 0; /* wait forever if time is 0 */
+ run_time_sec = 0; /* loop forever if time to run is 0 */
+ master_pid = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+s:t:p:h",
+ longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 's':
+ start_time_sec = atoi(optarg);
+ break;
+ case 't':
+ run_time_sec = atoi(optarg);
+ break;
+ case 'p':
+ master_pid = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ }
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+void print_info(char *progname)
+{
+ odp_sys_info_print();
+
+ printf("Running ODP appl: \"%s\"\n",
+ progname);
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Print usage information
+ */
+void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ "\n"
+ "OpenDataPlane odp-linux ipc test application.\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -h, --help Display help and exit.\n"
+ " -p, --pid PID of the master process.\n"
+ " -t, --run-time Time to run in seconds.\n"
+ " -s, --start-timeout Maximum time for pktio startup.\n"
+ "\n", NO_PATH(progname)
+ );
+}
diff --git a/platform/linux-generic/test/pktio_ipc/ipc_common.h b/platform/linux-generic/test/pktio_ipc/ipc_common.h
new file mode 100644
index 000000000..94ec21460
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/ipc_common.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sched.h>
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/** @def SHM_PKT_POOL_SIZE
+ * @brief Size of the shared memory block
+ */
+#define SHM_PKT_POOL_SIZE 8192
+
+/** @def SHM_PKT_POOL_BUF_SIZE
+ * @brief Buffer size of the packet pool buffer
+ */
+#define SHM_PKT_POOL_BUF_SIZE 100
+
+/** @def MAX_PKT_BURST
+ * @brief Maximum number of packet bursts
+ */
+#define MAX_PKT_BURST 16
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+#define TEST_SEQ_MAGIC 0x92749451
+#define TEST_SEQ_MAGIC_2 0x81638340
+
+#define TEST_ALLOC_MAGIC 0x1234adcd
+
+#define TEST_IPC_PKTIO_NAME "ipc:ipktio"
+#define TEST_IPC_PKTIO_PID_NAME "ipc:%d:ipktio"
+
+/** Can be any name, same or not the same. */
+#define TEST_IPC_POOL_NAME "ipc_packet_pool"
+
+/** magic number and sequence at start of packet payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+ odp_u32be_t seq;
+} pkt_head_t;
+
+/** magic number at end of packet payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+} pkt_tail_t;
+
+/** Start time in seconds */
+extern int start_time_sec;
+
+/** Run time in seconds */
+extern int run_time_sec;
+
+/** PID of the master process */
+extern int master_pid;
+
+/* helper funcs */
+void parse_args(int argc, char *argv[]);
+void print_info(char *progname);
+void usage(char *progname);
+
+/**
+ * Create a ipc pktio handle.
+ *
+ * @param pool Pool to associate with device for packet RX/TX
+ * @param master_pid Pid of master process
+ *
+ * @return The handle of the created pktio object.
+ * @retval ODP_PKTIO_INVALID if the create fails.
+ */
+odp_pktio_t create_pktio(odp_pool_t pool, int master_pid);
+
+/** Spin and send all packet from table
+ *
+ * @param pktio pktio device
+ * @param pkt_tbl packets table
+ * @param num number of packets
+ */
+int ipc_odp_packet_send_or_free(odp_pktio_t pktio,
+ odp_packet_t pkt_tbl[],
+ int num);
diff --git a/platform/linux-generic/test/pktio_ipc/pktio_ipc1.c b/platform/linux-generic/test/pktio_ipc/pktio_ipc1.c
new file mode 100644
index 000000000..df7a5ca3f
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/pktio_ipc1.c
@@ -0,0 +1,381 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#include "ipc_common.h"
+
+/**
+ * @file
+ * @example pktio_ipc1.c ODP IPC example application.
+ * This application works in pair with pktio_ipc2 application.
+ * It opens ipc pktio, allocates packets, sets magic number and
+ * sends packets to ipc pktio. Then app reads packets and checks
+ * that magic number was properly updated and there is no packet
+ * loss (i.e. sequesce counter continiusly incrementing.)
+ */
+
+/**
+ * Packet IO loopback worker thread using bursts from/to IO resources
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static int pktio_run_loop(odp_pool_t pool)
+{
+ int pkts;
+ odp_pktio_t ipc_pktio = ODP_PKTIO_INVALID;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ uint64_t cnt = 0; /* increasing counter on each send packet */
+ uint64_t cnt_recv = 0; /* increasing counter to validate
+ cnt on receive */
+ uint64_t stat_pkts = 0;
+ uint64_t stat_pkts_alloc = 0;
+ uint64_t stat_pkts_prev = 0;
+ uint64_t stat_errors = 0;
+ uint64_t stat_free = 0;
+ odp_time_t start_cycle;
+ odp_time_t current_cycle;
+ odp_time_t cycle;
+ odp_time_t diff;
+ odp_time_t wait;
+ int ret;
+ odp_pktin_queue_t pktin;
+ char name[30];
+ int sync_cnt = 0;
+
+ if (master_pid)
+ sprintf(name, TEST_IPC_PKTIO_PID_NAME, master_pid);
+ else
+ sprintf(name, TEST_IPC_PKTIO_NAME);
+
+ wait = odp_time_local_from_ns(start_time_sec * ODP_TIME_SEC_IN_NS);
+ start_cycle = odp_time_local();
+ current_cycle = start_cycle;
+
+ for (;;) {
+ if (start_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ printf("timeout exit 1, start_time_sec %d\n",
+ start_time_sec);
+ return -1;
+ }
+ }
+
+ ipc_pktio = create_pktio(pool, master_pid);
+ if (ipc_pktio != ODP_PKTIO_INVALID)
+ break;
+ if (!master_pid)
+ break;
+
+ odp_time_wait_ns(50 * ODP_TIME_MSEC_IN_NS);
+ }
+
+ if (ipc_pktio == ODP_PKTIO_INVALID)
+ return -1;
+
+ if (odp_pktin_queue(ipc_pktio, &pktin, 1) != 1) {
+ ODPH_ERR("no input queue\n");
+ return -1;
+ }
+
+ /* start ipc pktio, i.e. wait until other process connects */
+ for (;;) {
+ if (start_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ printf("timeout exit 2, start_time_sec %d\n",
+ start_time_sec);
+ goto exit;
+ }
+ }
+
+ ret = odp_pktio_start(ipc_pktio);
+ if (!ret)
+ break;
+
+ /* Reduce polling frequency to once per 50ms */
+ odp_time_wait_ns(50 * ODP_TIME_MSEC_IN_NS);
+ }
+
+ /* packets loop */
+ wait = odp_time_local_from_ns(run_time_sec * ODP_TIME_SEC_IN_NS);
+ start_cycle = odp_time_local();
+ for (;;) {
+ int i;
+
+ /* 1. exit loop if time specified */
+ if (run_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ ODPH_DBG("exit after %d seconds\n",
+ run_time_sec);
+ break;
+ }
+ }
+
+ /* 2. Receive packets back from ipc_pktio, validate magic
+ * number sequence counter and free that packet
+ */
+ while (1) {
+ pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
+ if (pkts <= 0)
+ break;
+
+ for (i = 0; i < pkts; i++) {
+ odp_packet_t pkt = pkt_tbl[i];
+ pkt_head_t head;
+ pkt_tail_t tail;
+ size_t off;
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ stat_errors++;
+ stat_free++;
+ odp_packet_free(pkt);
+ ODPH_ERR("invalid l4 offset\n");
+ }
+
+ off += ODPH_UDPHDR_LEN;
+ ret = odp_packet_copy_to_mem(pkt, off,
+ sizeof(head),
+ &head);
+ if (ret) {
+ stat_errors++;
+ stat_free++;
+ odp_packet_free(pkt);
+ ODPH_DBG("error\n");
+ continue;
+ }
+
+ if (head.magic == TEST_ALLOC_MAGIC) {
+ stat_free++;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (head.magic != TEST_SEQ_MAGIC_2) {
+ stat_errors++;
+ stat_free++;
+ odp_packet_free(pkt);
+ ODPH_DBG("error\n");
+ continue;
+ }
+
+ off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
+ ret = odp_packet_copy_to_mem(pkt, off,
+ sizeof(tail),
+ &tail);
+ if (ret) {
+ stat_errors++;
+ stat_free++;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (tail.magic != TEST_SEQ_MAGIC) {
+ stat_errors++;
+ stat_free++;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ cnt_recv++;
+
+ if (head.seq != cnt_recv && sync_cnt) {
+ stat_errors++;
+ odp_packet_free(pkt);
+ ODPH_DBG("head.seq %d - cnt_recv "
+ "%" PRIu64 " = %" PRIu64 "\n",
+ head.seq, cnt_recv,
+ head.seq - cnt_recv);
+ cnt_recv = head.seq;
+ stat_free++;
+ continue;
+ }
+
+ stat_pkts++;
+ odp_packet_free(pkt);
+ }
+ }
+
+ /* 3. emulate that pkts packets were received */
+ ret = odp_random_data((uint8_t *)&pkts, sizeof(pkts),
+ ODP_RANDOM_BASIC);
+ if (ret != sizeof(pkts)) {
+ ODPH_ABORT("random failed");
+ break;
+ }
+ pkts = ((pkts & 0xffff) % MAX_PKT_BURST) + 1;
+
+ for (i = 0; i < pkts; i++) {
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(pool, SHM_PKT_POOL_BUF_SIZE);
+ if (pkt == ODP_PACKET_INVALID)
+ break;
+
+ stat_pkts_alloc++;
+ odp_packet_l4_offset_set(pkt, 30);
+ pkt_tbl[i] = pkt;
+ }
+
+ pkts = i;
+
+ /* 4. Copy counter and magic numbers to that packets */
+ for (i = 0; i < pkts; i++) {
+ pkt_head_t head;
+ pkt_tail_t tail;
+ size_t off;
+ odp_packet_t pkt = pkt_tbl[i];
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID)
+ ODPH_ABORT("packet L4 offset not set");
+
+ head.magic = TEST_SEQ_MAGIC;
+ head.seq = cnt++;
+
+ off += ODPH_UDPHDR_LEN;
+ ret = odp_packet_copy_from_mem(pkt, off, sizeof(head),
+ &head);
+ if (ret)
+ ODPH_ABORT("unable to copy in head data");
+
+ tail.magic = TEST_SEQ_MAGIC;
+ off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
+ ret = odp_packet_copy_from_mem(pkt, off, sizeof(tail),
+ &tail);
+ if (ret)
+ ODPH_ABORT("unable to copy in tail data");
+ }
+
+ /* 5. Send packets to ipc_pktio */
+ ret = ipc_odp_packet_send_or_free(ipc_pktio, pkt_tbl, pkts);
+ if (ret < 0) {
+ ODPH_DBG("unable to sending to ipc pktio\n");
+ break;
+ }
+
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, current_cycle);
+ if (odp_time_cmp(odp_time_local_from_ns(ODP_TIME_SEC_IN_NS),
+ diff) < 0) {
+ current_cycle = cycle;
+ if (!sync_cnt && stat_errors == (MAX_PKT_BURST + 2)) {
+ stat_errors = 0;
+ sync_cnt = 1;
+ }
+ printf("\rpkts: %" PRIu64 ", alloc %" PRIu64 ","
+ " errors %" PRIu64 ", pps %" PRIu64 ","
+ " free %" PRIu64 ".",
+ stat_pkts, stat_pkts_alloc, stat_errors,
+ (stat_pkts + stat_pkts_alloc - stat_pkts_prev),
+ stat_free);
+ fflush(stdout);
+ stat_pkts_prev = stat_pkts + stat_pkts_alloc;
+ }
+ }
+
+ /* cleanup and exit */
+ ret = odp_pktio_stop(ipc_pktio);
+ if (ret) {
+ ODPH_DBG("odp_pktio_stop error %d\n", ret);
+ return -1;
+ }
+
+exit:
+ ret = odp_pktio_close(ipc_pktio);
+ if (ret) {
+ ODPH_DBG("odp_pktio_close error %d\n", ret);
+ return -1;
+ }
+
+ return (stat_errors || stat_pkts < 1000) ? -1 : 0;
+}
+
+/**
+ * ODP packet example main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ int ret;
+ cpu_set_t cpu_set;
+ odp_cpumask_t mask;
+ int cpu;
+ pid_t pid;
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv);
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, NULL, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_cpumask_default_worker(&mask, 0);
+ cpu = odp_cpumask_first(&mask);
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(cpu, &cpu_set);
+
+ pid = getpid();
+
+ if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpu_set)) {
+ printf("Set CPU affinity failed.\n");
+ return -1;
+ }
+
+ printf("ipc_pktio1 %d run on cpu %d\n", pid, cpu);
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_WORKER)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]));
+
+ /* Create packet pool */
+ memset(&params, 0, sizeof(params));
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_SIZE;
+ params.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create(TEST_IPC_POOL_NAME, &params);
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pool_print(pool);
+
+ ret = pktio_run_loop(pool);
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Error: odp_pool_destroy() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: odp_term_local() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: odp_term_global() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ODPH_DBG("return %d\n", ret);
+ return ret;
+}
diff --git a/platform/linux-generic/test/pktio_ipc/pktio_ipc2.c b/platform/linux-generic/test/pktio_ipc/pktio_ipc2.c
new file mode 100644
index 000000000..fc3b6833a
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/pktio_ipc2.c
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @file
+ *
+ * @example pktio_ipc2.c ODP IPC example application.
+ * This application works in pair with pktio_ipc1 application.
+ * It opens ipc pktio, reads packets and updates magic number.
+ * Also it allocates some packets from internal pool and sends
+ * to ipc pktio.
+ */
+
+#include "ipc_common.h"
+
+static int ipc_second_process(int master_pid)
+{
+ odp_pktio_t ipc_pktio = ODP_PKTIO_INVALID;
+ odp_pool_param_t params;
+ odp_pool_t pool;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ odp_packet_t alloc_pkt;
+ int pkts;
+ int ret;
+ int i;
+ odp_time_t start_cycle;
+ odp_time_t cycle;
+ odp_time_t diff;
+ odp_time_t wait;
+ uint64_t stat_pkts = 0;
+ odp_pktin_queue_t pktin;
+
+ /* Create packet pool */
+ memset(&params, 0, sizeof(params));
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_SIZE;
+ params.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create(TEST_IPC_POOL_NAME, &params);
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ wait = odp_time_local_from_ns(start_time_sec * ODP_TIME_SEC_IN_NS);
+ start_cycle = odp_time_local();
+
+ for (;;) {
+ /* exit loop if time specified */
+ if (start_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ printf("timeout exit 1, start_time_sec %d\n",
+ start_time_sec);
+ goto not_started;
+ }
+ }
+
+ ipc_pktio = create_pktio(pool, master_pid);
+ if (ipc_pktio != ODP_PKTIO_INVALID)
+ break;
+ if (!master_pid)
+ break;
+
+ odp_time_wait_ns(50 * ODP_TIME_MSEC_IN_NS);
+ }
+
+ if (ipc_pktio == ODP_PKTIO_INVALID) {
+ odp_pool_destroy(pool);
+ return -1;
+ }
+
+ memset(&pktin, 0, sizeof(pktin)); /* not needed but makes GCC happy */
+ if (odp_pktin_queue(ipc_pktio, &pktin, 1) != 1) {
+ odp_pool_destroy(pool);
+ ODPH_ERR("no input queue\n");
+ return -1;
+ }
+
+ /* start ipc pktio, i.e. wait until other process connects */
+ for (;;) {
+ /* 1. exit loop if time specified */
+ if (start_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ printf("timeout exit 2, start_time_sec %d\n",
+ start_time_sec);
+ goto not_started;
+ }
+ }
+
+ ret = odp_pktio_start(ipc_pktio);
+ if (!ret)
+ break;
+
+ /* Reduce polling frequency to once per 50ms */
+ odp_time_wait_ns(50 * ODP_TIME_MSEC_IN_NS);
+ }
+
+ wait = odp_time_local_from_ns(run_time_sec * ODP_TIME_SEC_IN_NS);
+ start_cycle = odp_time_local();
+ for (;;) {
+ /* exit loop if time specified */
+ if (run_time_sec) {
+ cycle = odp_time_local();
+ diff = odp_time_diff(cycle, start_cycle);
+ if (odp_time_cmp(wait, diff) < 0) {
+ ODPH_DBG("exit after %d seconds\n",
+ run_time_sec);
+ break;
+ }
+ }
+
+ /* recv some packets and change MAGIC to MAGIC_2 */
+ pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
+ if (pkts <= 0)
+ continue;
+
+ for (i = 0; i < pkts; i++) {
+ odp_packet_t pkt = pkt_tbl[i];
+ pkt_head_t head;
+ size_t off;
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ ODPH_ERR("invalid l4 offset\n");
+ for (int j = i; j < pkts; j++)
+ odp_packet_free(pkt_tbl[j]);
+ break;
+ }
+
+ off += ODPH_UDPHDR_LEN;
+ ret = odp_packet_copy_to_mem(pkt, off, sizeof(head),
+ &head);
+ if (ret)
+ ODPH_ABORT("unable copy out head data");
+
+ if (head.magic != TEST_SEQ_MAGIC) {
+ ODPH_ERR("Wrong head magic! %x", head.magic);
+ for (int j = i; j < pkts; j++)
+ odp_packet_free(pkt_tbl[j]);
+ break;
+ }
+
+ /* Modify magic number in packet */
+ head.magic = TEST_SEQ_MAGIC_2;
+ ret = odp_packet_copy_from_mem(pkt, off, sizeof(head),
+ &head);
+ if (ret)
+ ODPH_ABORT("unable to copy in head data");
+ }
+
+ /* send all packets back */
+ ret = ipc_odp_packet_send_or_free(ipc_pktio, pkt_tbl, i);
+ if (ret < 0)
+ ODPH_ABORT("can not send packets\n");
+
+ stat_pkts += ret;
+
+ /* alloc packet from local pool, set magic to ALLOC_MAGIC,
+ * and send it.*/
+ alloc_pkt = odp_packet_alloc(pool, SHM_PKT_POOL_BUF_SIZE);
+ if (alloc_pkt != ODP_PACKET_INVALID) {
+ pkt_head_t head;
+ size_t off;
+
+ odp_packet_l4_offset_set(alloc_pkt, 30);
+
+ head.magic = TEST_ALLOC_MAGIC;
+
+ off = odp_packet_l4_offset(alloc_pkt);
+ off += ODPH_UDPHDR_LEN;
+ ret = odp_packet_copy_from_mem(alloc_pkt, off,
+ sizeof(head),
+ &head);
+ if (ret)
+ ODPH_ABORT("unable to copy in head data");
+
+ pkt_tbl[0] = alloc_pkt;
+ ret = ipc_odp_packet_send_or_free(ipc_pktio,
+ pkt_tbl, 1);
+ if (ret < 0)
+ ODPH_ABORT("can not send packets\n");
+ stat_pkts += 1;
+ }
+ }
+
+ /* cleanup and exit */
+ ret = odp_pktio_stop(ipc_pktio);
+ if (ret) {
+ ODPH_DBG("ipc2: odp_pktio_stop error %d\n", ret);
+ return -1;
+ }
+
+not_started:
+ ret = odp_pktio_close(ipc_pktio);
+ if (ret) {
+ ODPH_DBG("ipc2: odp_pktio_close error %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_pool_destroy(pool);
+ if (ret)
+ ODPH_DBG("ipc2: pool_destroy error %d\n", ret);
+
+ return stat_pkts > 1000 ? 0 : -1;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ int ret;
+ cpu_set_t cpu_set;
+ odp_cpumask_t mask;
+ int cpu;
+ pid_t pid;
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv);
+
+ if (odp_init_global(&instance, NULL, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_cpumask_default_worker(&mask, 0);
+ cpu = odp_cpumask_first(&mask);
+ ret = odp_cpumask_next(&mask, cpu);
+ if (ret != -1)
+ cpu = ret;
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(cpu, &cpu_set);
+
+ pid = getpid();
+
+ if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpu_set)) {
+ printf("Set CPU affinity failed to cpu %d.\n", cpu);
+ return -1;
+ }
+
+ printf("ipc_pktio2 %d run on cpu %d\n", pid, cpu);
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_WORKER)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = ipc_second_process(master_pid);
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: odp_term_local() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: odp_term_global() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/platform/linux-generic/test/pktio_ipc/pktio_ipc_run.sh b/platform/linux-generic/test/pktio_ipc/pktio_ipc_run.sh
new file mode 100755
index 000000000..b181668e8
--- /dev/null
+++ b/platform/linux-generic/test/pktio_ipc/pktio_ipc_run.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015-2018 Linaro Limited
+# Copyright (c) 2023 Nokia
+#
+
+# directories where test binary can be found:
+# -in the validation dir when running make check (intree or out of tree)
+# -in the script directory, when running after 'make install', or
+# -in the validation when running standalone (./pktio_ipc_run) intree.
+# -in the current directory.
+# running stand alone out of tree requires setting PATH
+PATH=./pktio_ipc:$PATH
+PATH=$(dirname $0):$PATH
+PATH=$(dirname $0)/../../../../platform/linux-generic/test/pktio_ipc:$PATH
+PATH=.:$PATH
+
+STARTTIME=30
+RUNTIME=1
+
+run()
+{
+ local ret=0
+
+ echo "==== run pktio_ipc1 then pktio_ipc2 ===="
+ pktio_ipc1${EXEEXT} -s ${STARTTIME} -t ${RUNTIME} &
+ IPC_PID=$!
+
+ pktio_ipc2${EXEEXT} -p ${IPC_PID} -s ${STARTTIME} -t ${RUNTIME}
+ ret=$?
+
+ (kill ${IPC_PID} 2>&1 > /dev/null ) > /dev/null
+ if [ $? -eq 0 ]; then
+ wait $IPC_PID
+ echo "pktio_ipc1${EXEEXT} was killed"
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
+ else
+ echo "normal exit of 2 application"
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ fi
+
+ if [ $ret -ne 0 ]; then
+ echo "!!!First stage FAILED $ret!!!"
+ exit $ret
+ else
+ echo "First stage PASSED"
+ fi
+
+ echo "==== run pktio_ipc2 then pktio_ipc1 ===="
+ pktio_ipc2${EXEEXT} -s ${STARTTIME} -t ${RUNTIME} &
+ IPC_PID=$!
+
+ pktio_ipc1${EXEEXT} -p ${IPC_PID} -s ${STARTTIME} -t ${RUNTIME}
+ ret=$?
+
+ (kill ${IPC_PID} 2>&1 > /dev/null ) > /dev/null
+ if [ $? -eq 0 ]; then
+ wait $IPC_PID
+ echo "pktio_ipc2${EXEEXT} was killed"
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
+ else
+ echo "normal exit of 2 application"
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ fi
+
+ if [ $ret -ne 0 ]; then
+ echo "!!! FAILED !!!"
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
+ exit $ret
+ else
+ ls -l /dev/shm/${UID}/odp* 2> /dev/null
+ echo "Second stage PASSED"
+ fi
+
+ echo "!!!PASSED!!!"
+ exit 0
+}
+
+case "$1" in
+ *) run ;;
+esac
diff --git a/platform/linux-generic/test/process-mode.conf b/platform/linux-generic/test/process-mode.conf
new file mode 100644
index 000000000..f4c6f7952
--- /dev/null
+++ b/platform/linux-generic/test/process-mode.conf
@@ -0,0 +1,9 @@
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+# Shared memory options
+shm: {
+ # Increase the amount of single VA memory
+ single_va_size_kb = 1048576
+}
diff --git a/platform/linux-generic/test/sched-basic.conf b/platform/linux-generic/test/sched-basic.conf
new file mode 100644
index 000000000..8a6d0ac98
--- /dev/null
+++ b/platform/linux-generic/test/sched-basic.conf
@@ -0,0 +1,13 @@
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+# Test scheduler with an odd spread value and without dynamic load balance
+sched_basic: {
+ prio_spread = 3
+ load_balance = 0
+ powersave: {
+ poll_time_nsec = 5000
+ sleep_time_nsec = 50000
+ }
+}
diff --git a/platform/linux-generic/test/stash-custom.conf b/platform/linux-generic/test/stash-custom.conf
new file mode 100644
index 000000000..6a2496303
--- /dev/null
+++ b/platform/linux-generic/test/stash-custom.conf
@@ -0,0 +1,8 @@
+# Mandatory fields
+odp_implementation = "linux-generic"
+config_file_version = "0.1.28"
+
+# Test overflow safe stash variant
+stash: {
+ strict_size = 0
+}
diff --git a/platform/linux-generic/test/validation/api/Makefile.inc b/platform/linux-generic/test/validation/api/Makefile.inc
new file mode 100644
index 000000000..cda6237ea
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/Makefile.inc
@@ -0,0 +1 @@
+include $(top_srcdir)/test/validation/api/Makefile.inc
diff --git a/platform/linux-generic/test/validation/api/ml/.gitignore b/platform/linux-generic/test/validation/api/ml/.gitignore
new file mode 100644
index 000000000..e31f902c4
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/.gitignore
@@ -0,0 +1 @@
+ml_linux
diff --git a/platform/linux-generic/test/validation/api/ml/Makefile.am b/platform/linux-generic/test/validation/api/ml/Makefile.am
new file mode 100644
index 000000000..f4b9e9755
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/Makefile.am
@@ -0,0 +1,34 @@
+include ../Makefile.inc
+
+test_PROGRAMS = ml_linux
+ml_linux_SOURCES = ml_linux.c
+
+EXTRA_DIST = \
+ batch_add_gen.py \
+ batch_add.onnx \
+ gen_models.sh \
+ README.md \
+ requirements.txt \
+ simple_linear_gen.py \
+ simple_linear.onnx
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/validation/api/ml/README.md b/platform/linux-generic/test/validation/api/ml/README.md
new file mode 100644
index 000000000..80ad30e96
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/README.md
@@ -0,0 +1,23 @@
+# How to run ML validation test
+
+Simple onnx models are used to test ML API.
+
+## Generate models
+
+### Install python requirements
+
+```bash
+python3 -m pip install -r <this directory>/requirements.txt
+```
+
+### Generate models for validation tests
+
+```bash
+<this directory>/gen_models.sh
+```
+
+## Run ML validation tests
+
+```bash
+<this directory>/ml_linux
+```
diff --git a/platform/linux-generic/test/validation/api/ml/batch_add.onnx b/platform/linux-generic/test/validation/api/ml/batch_add.onnx
new file mode 100644
index 000000000..43485f463
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/batch_add.onnx
Binary files differ
diff --git a/platform/linux-generic/test/validation/api/ml/batch_add_gen.py b/platform/linux-generic/test/validation/api/ml/batch_add_gen.py
new file mode 100644
index 000000000..33515bd2f
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/batch_add_gen.py
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+import onnx
+from onnx import helper
+from onnx import TensorProto
+
+graph = helper.make_graph(
+ [ # nodes
+ helper.make_node("Add", ["x1", "x2"], ["y"], "Batch Add"),
+ ],
+ "Batch Add", # name
+ [ # inputs
+ helper.make_tensor_value_info('x1', TensorProto.DOUBLE, ["c", 3]),
+ helper.make_tensor_value_info('x2', TensorProto.DOUBLE, ["c", 3]),
+ ],
+ [ # outputs
+ helper.make_tensor_value_info('y', TensorProto.DOUBLE, ["c", 3]),
+ ]
+)
+
+model = helper.make_model(
+ graph,
+ opset_imports=[helper.make_opsetid("", 14)],
+ producer_name='ODP validation tests',
+ model_version=1,
+ doc_string="y = x1 + x2",
+ ir_version = 8
+)
+
+onnx.save(model, 'batch_add.onnx')
diff --git a/platform/linux-generic/test/validation/api/ml/gen_models.sh b/platform/linux-generic/test/validation/api/ml/gen_models.sh
new file mode 100755
index 000000000..d88f3c432
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/gen_models.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+set -e
+
+# cd to the directory where this script is in
+cd "$( dirname "${BASH_SOURCE[0]}" )"
+
+python3 simple_linear_gen.py
+
+python3 batch_add_gen.py
diff --git a/platform/linux-generic/test/validation/api/ml/ml_linux.c b/platform/linux-generic/test/validation/api/ml/ml_linux.c
new file mode 100644
index 000000000..28e18fbb5
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/ml_linux.c
@@ -0,0 +1,1167 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define TIMEOUT 5
+#define MODEL_NAME "Test"
+#define NUM_INPUTS 1
+#define NUM_OUTPUTS 1
+#define RUN_NUM 2
+#define BUF_LEN 256
+#define CONFIG_MAX_MODEL_SIZE 500
+
+#define COMPL_POOL_NAME "ML compl pool"
+#define NUM_COMPL 10
+
+/**
+ * About model simple_linear.onnx being tested in this suite
+ *
+ * Model info:
+ * Version: 1
+ * Inputs: name: x, type: int32, shape: [1]
+ * Outputs: name: y, type: int32, shape: [1]
+ *
+ * The model is of form y = 3 * x + 4
+ * Thus when x = 5, the output y should be 19.
+ */
+typedef struct global_t {
+ int disabled;
+ odp_ml_capability_t ml_capa;
+ odp_ml_config_t ml_config;
+ odp_ml_model_param_t model_param;
+ odp_ml_model_t ml_model;
+ odp_pool_t compl_pool;
+ odp_queue_t queue;
+ odp_ml_data_t data;
+ odp_ml_data_seg_t input_seg;
+ odp_ml_data_seg_t output_seg;
+ odp_ml_run_param_t run_param;
+ uint64_t wait_ns;
+ int32_t x;
+ int32_t y;
+ int32_t y_expected;
+
+} global_t;
+
+static global_t global;
+
+static int fill_model_param(const char *model_name, odp_ml_model_param_t *model_param)
+{
+ size_t size;
+ char *pos;
+ char *exe_dir;
+ size_t exe_dir_len;
+ FILE *model_file;
+ char exe_path[BUF_LEN];
+ ssize_t exe_path_len;
+ char model_path[BUF_LEN];
+
+ /* Model file is placed in the same directory as the executable ml_linux */
+ exe_path_len = readlink("/proc/self/exe", exe_path, BUF_LEN - 1);
+ if (exe_path_len != -1) {
+ exe_path[exe_path_len] = '\0';
+
+ pos = strstr(exe_path, ".libs");
+ if (pos)
+ *(pos + 5) = '\0';
+
+ exe_dir = dirname(exe_path);
+ exe_dir_len = strlen(exe_dir);
+
+ memcpy(model_path, exe_dir, exe_dir_len);
+ model_path[exe_dir_len] = '/';
+ model_path[exe_dir_len + 1] = '\0';
+
+ strncat(model_path, model_name, BUF_LEN - strlen(model_path) - 1);
+ ODPH_DBG("model_path: %s\n", model_path);
+ model_file = fopen(model_path, "rb");
+ } else { /* Can't get executable path, try to find model file at current dir*/
+ model_file = fopen(model_name, "rb");
+ }
+
+ if (model_file == NULL) {
+ perror("Failed to open model file");
+ return -1;
+ }
+
+ /* Get the model file size in bytes */
+ fseek(model_file, 0, SEEK_END);
+ model_param->size = ftell(model_file);
+ rewind(model_file);
+
+ model_param->model = malloc(model_param->size);
+ if (!model_param->model) {
+ ODPH_ERR("\n\nMemory allocation failed\n");
+ fclose(model_file);
+ return -1;
+ }
+ size = fread(model_param->model, model_param->size, 1, model_file);
+
+ fclose(model_file);
+ if (size != 1) {
+ ODPH_ERR("\n\nRead model file failed\n");
+ return -1;
+ }
+
+ model_param->max_compl_id = 0;
+
+ return 0;
+}
+
+static int ml_suite_init(void)
+{
+ odp_ml_capability_t *ml_capa = &global.ml_capa;
+ odp_queue_param_t queue_param;
+ odp_ml_compl_pool_param_t ml_pool_param;
+
+ memset(&global, 0, sizeof(global_t));
+ global.queue = ODP_QUEUE_INVALID;
+ global.compl_pool = ODP_POOL_INVALID;
+
+ if (odp_ml_capability(ml_capa)) {
+ ODPH_ERR("ML capability failed\n");
+ return -1;
+ }
+
+ if (ml_capa->max_models == 0) {
+ global.disabled = 1;
+ ODPH_DBG("ML test disabled\n");
+ return 0;
+ }
+
+ /* Configure ML */
+ odp_ml_config_init(&global.ml_config);
+ global.ml_config.max_models_created = ml_capa->max_models;
+ global.ml_config.max_models_loaded = ml_capa->max_models_loaded;
+ global.ml_config.max_model_size = CONFIG_MAX_MODEL_SIZE;
+
+ if (ml_capa->load.compl_mode_mask & ODP_ML_COMPL_MODE_SYNC)
+ global.ml_config.load_mode_mask |= ODP_ML_COMPL_MODE_SYNC;
+
+ if (ml_capa->load.compl_mode_mask & ODP_ML_COMPL_MODE_POLL)
+ global.ml_config.load_mode_mask |= ODP_ML_COMPL_MODE_POLL;
+
+ if (ml_capa->load.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ global.ml_config.load_mode_mask |= ODP_ML_COMPL_MODE_EVENT;
+
+ if (ml_capa->run.compl_mode_mask & ODP_ML_COMPL_MODE_SYNC)
+ global.ml_config.run_mode_mask |= ODP_ML_COMPL_MODE_SYNC;
+
+ if (ml_capa->run.compl_mode_mask & ODP_ML_COMPL_MODE_POLL)
+ global.ml_config.run_mode_mask |= ODP_ML_COMPL_MODE_POLL;
+
+ if (ml_capa->run.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ global.ml_config.run_mode_mask |= ODP_ML_COMPL_MODE_EVENT;
+
+ if (odp_ml_config(&global.ml_config)) {
+ ODPH_ERR("\n\nConfiguring ML failed\n");
+ return -1;
+ }
+
+ global.x = 5;
+ global.wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+ global.y_expected = 19; /* y = 3 * x + 4 = 3 * 5 + 4 = 19 */
+
+ /* Prepare data for running model inference */
+ odp_ml_run_param_init(&global.run_param);
+
+ global.data.num_input_seg = NUM_INPUTS;
+ global.data.input_seg = &global.input_seg;
+ global.input_seg.size = sizeof(int32_t);
+ global.input_seg.addr = &global.x;
+
+ global.data.num_output_seg = NUM_OUTPUTS;
+ global.data.output_seg = &global.output_seg;
+ global.output_seg.size = sizeof(int32_t);
+ global.output_seg.addr = &global.y;
+
+ if (fill_model_param("simple_linear.onnx", &global.model_param))
+ return -1;
+
+ /* Create ML model */
+ global.ml_model = odp_ml_model_create(MODEL_NAME, &global.model_param);
+ if (global.ml_model == ODP_ML_MODEL_INVALID) {
+ ODPH_ERR("Create ML model failed\n");
+ goto error;
+ }
+
+ /* Asynchronous mode with event completion is not supported */
+ if (!((ml_capa->load.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT) ||
+ (ml_capa->run.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)))
+ return 0;
+
+ /* Create a queue for sending ML completion event to */
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global.queue = odp_queue_create("ML compl queue", &queue_param);
+ if (global.queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed\n");
+ goto error;
+ }
+
+ /* Create an ML job completion pool */
+ if (ml_capa->pool.max_num < NUM_COMPL) {
+ ODPH_ERR("Too small ML compl pool %u\n", ml_capa->pool.max_num);
+ goto error;
+ }
+
+ odp_ml_compl_pool_param_init(&ml_pool_param);
+ ml_pool_param.num = NUM_COMPL;
+
+ global.compl_pool = odp_ml_compl_pool_create(COMPL_POOL_NAME, &ml_pool_param);
+ if (global.compl_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Create ML completion pool failed\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ free(global.model_param.model);
+ return -1;
+}
+
+static int ml_suite_term(void)
+{
+ if (global.compl_pool != ODP_POOL_INVALID &&
+ odp_pool_destroy(global.compl_pool)) {
+ ODPH_ERR("Completion pool destroy failed\n");
+ return -1;
+ }
+
+ if (global.ml_model && odp_ml_model_destroy(global.ml_model)) {
+ ODPH_ERR("Destroy ML model failed\n");
+ return -1;
+ }
+
+ if (global.queue != ODP_QUEUE_INVALID &&
+ odp_queue_destroy(global.queue)) {
+ ODPH_ERR("Destroy ML queue failed\n");
+ return -1;
+ }
+
+ free(global.model_param.model);
+
+ return 0;
+}
+
+static int check_ml_support(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_load_sync(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_SYNC)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_load_poll(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_POLL)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_load_event(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_run_sync(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ /* Model run test uses synchronous load */
+ if ((global.ml_config.run_mode_mask & ODP_ML_COMPL_MODE_SYNC) &&
+ (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_SYNC))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_run_poll(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ /* Poll mode model run test uses synchronous load */
+ if ((global.ml_config.run_mode_mask & ODP_ML_COMPL_MODE_POLL) &&
+ (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_SYNC))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_run_event(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ /* Poll mode model run test uses synchronous load */
+ if ((global.ml_config.run_mode_mask & ODP_ML_COMPL_MODE_EVENT) &&
+ (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_SYNC))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_run_poll_event(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ /* test_ml_run_start_multi uses synchronous load, poll mode and event mode run */
+ if ((global.ml_config.run_mode_mask & ODP_ML_COMPL_MODE_EVENT) &&
+ (global.ml_config.run_mode_mask & ODP_ML_COMPL_MODE_POLL) &&
+ (global.ml_config.load_mode_mask & ODP_ML_COMPL_MODE_SYNC))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void test_ml_debug(void)
+{
+ uint64_t u64;
+
+ u64 = odp_ml_model_to_u64(global.ml_model);
+ CU_ASSERT(u64 != odp_ml_model_to_u64(ODP_ML_MODEL_INVALID));
+ printf("\n ML model handle: 0x%" PRIx64 "\n", u64);
+
+ odp_ml_model_print(global.ml_model);
+}
+
+static void test_ml_model_create(void)
+{
+ uint32_t i;
+ /* One for global.ml_model */
+ uint32_t max_models = global.ml_config.max_models_created - 1;
+ odp_ml_model_t models[max_models];
+
+ for (i = 0; i < max_models; i++) {
+ models[i] = odp_ml_model_create(NULL, &global.model_param);
+
+ if (models[i] == ODP_ML_MODEL_INVALID) {
+ ODPH_ERR("ML model create failed: %u / %u\n", i, max_models);
+ break;
+ }
+ }
+
+ CU_ASSERT(i == max_models);
+ max_models = i;
+
+ /* Destroy valid models */
+ for (i = 0; i < max_models; i++)
+ CU_ASSERT_FATAL(odp_ml_model_destroy(models[i]) == 0);
+}
+
+static void test_ml_model_lookup(void)
+{
+ odp_ml_model_t model2;
+ odp_ml_model_t model_lookup;
+
+ /* Look up model with the same name, should find one with equal handle */
+ model_lookup = odp_ml_model_lookup(MODEL_NAME);
+ CU_ASSERT_FATAL(model_lookup != ODP_ML_MODEL_INVALID);
+ CU_ASSERT(odp_ml_model_to_u64(global.ml_model) == odp_ml_model_to_u64(model_lookup));
+
+ /* Look up model with a different name, should return invalid handle */
+ model_lookup = odp_ml_model_lookup("diff");
+ CU_ASSERT_FATAL(model_lookup == ODP_ML_MODEL_INVALID);
+
+ model2 = odp_ml_model_create(MODEL_NAME, &global.model_param);
+ CU_ASSERT_FATAL(model2 != ODP_ML_MODEL_INVALID);
+ CU_ASSERT(odp_ml_model_to_u64(global.ml_model) != odp_ml_model_to_u64(model2));
+
+ model_lookup = odp_ml_model_lookup(MODEL_NAME);
+ CU_ASSERT(odp_ml_model_to_u64(model_lookup) == odp_ml_model_to_u64(global.ml_model) ||
+ odp_ml_model_to_u64(model_lookup) == odp_ml_model_to_u64(model2));
+
+ CU_ASSERT(odp_ml_model_destroy(model2) == 0);
+}
+
+static void test_ml_model_info(void)
+{
+ int ret;
+ uint32_t num_ret;
+ odp_ml_model_info_t ml_info;
+ odp_ml_input_info_t input_info[2];
+ odp_ml_output_info_t output_info[2];
+
+ /* Verify model info about global.ml_model, namely, simple_linear.onnx */
+ memset(&ml_info, 0x88, sizeof(odp_ml_model_info_t));
+ ret = odp_ml_model_info(global.ml_model, &ml_info);
+ CU_ASSERT(ret == 0);
+ CU_ASSERT(!strcmp(ml_info.name, MODEL_NAME));
+ CU_ASSERT(ml_info.model_version == 1);
+ CU_ASSERT(ml_info.num_inputs == NUM_INPUTS);
+ CU_ASSERT(ml_info.num_outputs == NUM_OUTPUTS);
+
+ num_ret = odp_ml_model_input_info(global.ml_model, input_info, NUM_INPUTS);
+ CU_ASSERT(num_ret == NUM_INPUTS);
+ CU_ASSERT(!strcmp(input_info[0].name, "x"));
+ CU_ASSERT(input_info[0].shape.num_dim == 1);
+ CU_ASSERT(input_info[0].shape.dim[0] == 1);
+ CU_ASSERT((int)input_info[0].data_type == ODP_ML_DATA_TYPE_INT32);
+
+ /* When num is 0, return normally, and input_info is ignored */
+ num_ret = odp_ml_model_input_info(global.ml_model, input_info, 0);
+ CU_ASSERT(num_ret == NUM_INPUTS);
+
+ /* When num is bigger than actual number of inputs, extra input_info is left untouched */
+ input_info[1].data_type = (odp_ml_data_type_t)-1;
+ num_ret = odp_ml_model_input_info(global.ml_model, input_info, NUM_INPUTS + 1);
+ CU_ASSERT(num_ret == NUM_INPUTS);
+ CU_ASSERT(!strcmp(input_info[0].name, "x"));
+ CU_ASSERT(input_info[0].shape.num_dim == 1);
+ CU_ASSERT(input_info[0].shape.dim[0] == 1);
+ CU_ASSERT((int)input_info[0].data_type == ODP_ML_DATA_TYPE_INT32);
+ /* input_info[1] is left untouched */
+ CU_ASSERT(input_info[1].data_type == (odp_ml_data_type_t)-1);
+
+ num_ret = odp_ml_model_output_info(global.ml_model, output_info, NUM_OUTPUTS);
+ CU_ASSERT(num_ret == NUM_OUTPUTS);
+ CU_ASSERT(!strcmp(output_info[0].name, "y"));
+ CU_ASSERT(output_info[0].shape.num_dim == 1);
+ CU_ASSERT(output_info[0].shape.dim[0] == 1);
+ CU_ASSERT((int)output_info[0].data_type == ODP_ML_DATA_TYPE_INT32);
+
+ /* When num is 0, return normally, and input_info is ignored */
+ num_ret = odp_ml_model_output_info(global.ml_model, output_info, 0);
+ CU_ASSERT(num_ret == NUM_OUTPUTS);
+
+ /* When num is bigger than actual number of inputs, extra output_info is left untouched */
+ num_ret = odp_ml_model_output_info(global.ml_model, output_info, NUM_OUTPUTS + 1);
+ output_info[1].shape.num_dim = 98876;
+ CU_ASSERT(num_ret == NUM_OUTPUTS);
+ CU_ASSERT(!strcmp(output_info[0].name, "y"));
+ CU_ASSERT(output_info[0].shape.num_dim == 1);
+ CU_ASSERT(output_info[0].shape.dim[0] == 1);
+ CU_ASSERT((int)output_info[0].data_type == ODP_ML_DATA_TYPE_INT32);
+ /* output_info[1] is left untouched */
+ CU_ASSERT(output_info[1].shape.num_dim == 98876);
+}
+
+static void test_ml_model_load(void)
+{
+ int ret;
+ odp_ml_model_t test_model;
+ odp_ml_load_result_t result;
+
+ test_model = odp_ml_model_create(NULL, &global.model_param);
+ CU_ASSERT_FATAL(test_model != ODP_ML_MODEL_INVALID);
+
+ ret = odp_ml_model_load(test_model, &result);
+ CU_ASSERT(ret == 0);
+ CU_ASSERT(result.error_code == 0);
+
+ ret = odp_ml_model_unload(test_model, NULL);
+ CU_ASSERT(ret == 0);
+
+ CU_ASSERT(odp_ml_model_destroy(test_model) == 0);
+}
+
+/* Test asynchronous model loading in ODP_ML_COMPL_MODE_POLL mode */
+static void test_ml_model_load_async_poll(void)
+{
+ int ret;
+ odp_ml_load_result_t result;
+ odp_ml_compl_param_t compl_param;
+ int dummy = 6;
+ void *user_ptr = &dummy;
+ uint64_t wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+
+ memset(&result, 0, sizeof(result));
+ odp_ml_compl_param_init(&compl_param);
+ compl_param.mode = ODP_ML_COMPL_MODE_POLL;
+ compl_param.compl_id = 0;
+ compl_param.user_ptr = user_ptr;
+
+ ret = odp_ml_model_load_start(global.ml_model, &compl_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* When odp_ml_model_load_start() succeeded, continue to check completion status */
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_model_load_status(global.ml_model, 0, &result);
+ if (ret)
+ break;
+
+ /* ret = 0 meaning run has not finished, continue to check status */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ CU_ASSERT(ret > 0);
+ CU_ASSERT(result.error_code == 0);
+ CU_ASSERT(result.user_ptr == user_ptr);
+ /* odp_ml_model_load does not modify data in user_ptr */
+ if (result.user_ptr)
+ CU_ASSERT(*(int *)result.user_ptr == dummy);
+
+ ret = odp_ml_model_unload_start(global.ml_model, &compl_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* When odp_ml_model_unload_start() succeeded, continue to check completion
+ * status */
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_model_unload_status(global.ml_model, 0, &result);
+ if (ret)
+ break;
+
+ /* ret = 0 meaning run has not finished, continue to check status */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ CU_ASSERT_FATAL(ret > 0);
+ CU_ASSERT(result.error_code == 0);
+ CU_ASSERT(result.user_ptr == user_ptr);
+
+ /* odp_ml_model_unload does not modify data in user_ptr */
+ if (result.user_ptr)
+ CU_ASSERT(*(int *)result.user_ptr == dummy);
+}
+
+static int
+get_result_from_ml_compl_event(odp_ml_load_result_t *load_result, odp_ml_run_result_t *run_result)
+{
+ int ret;
+ odp_event_t ev;
+ odp_ml_compl_t compl;
+ odp_event_type_t ev_type;
+ odp_queue_t from_queue = ODP_QUEUE_INVALID;
+ uint64_t sched_wait = odp_schedule_wait_time(global.wait_ns);
+
+ /* Run event scheduler to find the ml completion event */
+ for (int i = 0; i < TIMEOUT; i++) {
+ ev = odp_schedule(&from_queue, sched_wait);
+ if (ev != ODP_EVENT_INVALID)
+ break;
+ }
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ if (ev == ODP_EVENT_INVALID) {
+ ODPH_ERR("Timeout while waiting for completion event\n");
+ return -1;
+ }
+
+ ev_type = odp_event_type(ev);
+ CU_ASSERT(from_queue == global.queue);
+ CU_ASSERT(ev_type == ODP_EVENT_ML_COMPL);
+ if (from_queue != global.queue || ev_type != ODP_EVENT_ML_COMPL) {
+ odp_event_free(ev);
+ ODPH_ERR("Received unexpected event while waiting for completion\n");
+ return -1;
+ }
+
+ compl = odp_ml_compl_from_event(ev);
+ CU_ASSERT(compl != ODP_ML_COMPL_INVALID);
+
+ if (load_result) {
+ CU_ASSERT(odp_ml_compl_load_result(compl, NULL) == 0);
+ ret = odp_ml_compl_load_result(compl, load_result);
+ } else {
+ CU_ASSERT(odp_ml_compl_run_result(compl, NULL) == 0);
+ ret = odp_ml_compl_run_result(compl, run_result);
+ }
+
+ CU_ASSERT(ret == 0);
+ odp_ml_compl_free(compl);
+
+ return ret;
+}
+
+/* Test asynchronous model loading in ODP_ML_COMPL_MODE_EVENT mode */
+static void test_ml_model_load_async_event(void)
+{
+ int ret;
+ odp_ml_compl_t compl;
+ odp_ml_load_result_t result;
+ odp_ml_compl_param_t compl_param;
+ int dummy = 6;
+ void *user_ptr = &dummy;
+
+ compl = odp_ml_compl_alloc(global.compl_pool);
+ CU_ASSERT_FATAL(compl != ODP_ML_COMPL_INVALID);
+
+ odp_ml_compl_param_init(&compl_param);
+ compl_param.mode = ODP_ML_COMPL_MODE_EVENT;
+ compl_param.event = odp_ml_compl_to_event(compl);
+ compl_param.queue = global.queue;
+ compl_param.user_ptr = user_ptr;
+
+ ret = odp_ml_model_load_start(global.ml_model, &compl_param);
+ CU_ASSERT(ret == 0);
+
+ /* Return when odp_ml_model_load_start() failed */
+ if (ret) {
+ odp_ml_compl_free(compl);
+ ODPH_ERR("ML model odp_ml_model_load_start() failed\n");
+ return;
+ }
+
+ /* Run event scheduler to find the ml completion event and verify it */
+ if (get_result_from_ml_compl_event(&result, NULL))
+ return;
+
+ CU_ASSERT(result.error_code == 0);
+ CU_ASSERT(result.user_ptr == user_ptr);
+
+ /* Model load does not modify data in user_ptr */
+ if (result.user_ptr)
+ CU_ASSERT(*(int *)result.user_ptr == dummy);
+
+ compl = odp_ml_compl_alloc(global.compl_pool);
+ CU_ASSERT(compl != ODP_ML_COMPL_INVALID);
+
+ if (compl == ODP_ML_COMPL_INVALID)
+ return;
+
+ compl_param.event = odp_ml_compl_to_event(compl);
+ ret = odp_ml_model_unload_start(global.ml_model, &compl_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* Run event scheduler to find the ml completion event and verify it */
+ if (get_result_from_ml_compl_event(&result, NULL))
+ return;
+
+ CU_ASSERT(result.error_code == 0);
+ CU_ASSERT(result.user_ptr == user_ptr);
+
+ /* odp_ml_model_unload does not modify data in user_ptr */
+ if (result.user_ptr)
+ CU_ASSERT(*(int *)result.user_ptr == dummy);
+}
+
+/* About model batch_add.onnx being tested in this function
+ *
+ * Model info:
+ * Version: 1
+ * Inputs:
+ * inputs[0]: name: x1, type: double, shape: [c, 3]
+ * inputs[1]: name: x2, type: double, shape: [c, 3]
+ * Outputs:
+ * Outputs[0]: name: y, type: double, shape: [c, 3]
+ *
+ * The model computes element-wise sum of input tensors x1 and x2 and stores them
+ * in y. The first dimension of input and output tensors represent batch size,
+ * thus it must be the same for all tensors here. The dynamic dimension size
+ * in the output tensor here can be deduced from the given batch size, thus no
+ * need for the implementation to fill it.
+ */
+#define NUM_COLUMN 3
+#define MAX_BATCH_SIZE 4
+#define SIZE (NUM_COLUMN * MAX_BATCH_SIZE * sizeof(double))
+static void run_model_batch_add(void)
+{
+ int ret;
+ odp_ml_data_t data;
+ odp_ml_model_t model;
+ odp_ml_data_seg_t input_segs[SIZE * 2];
+ odp_ml_data_seg_t output_segs[SIZE];
+ odp_ml_run_result_t result;
+ odp_ml_run_param_t run_param;
+ odp_ml_model_param_t model_param;
+
+ double y[12];
+ double y_expected[12];
+ uint32_t batch_size = MAX_BATCH_SIZE;
+ double x1[12] = {97, 47, 62, 19, 93, 59, 67, 42, 28, 55, 46, 31};
+ double x2[12] = {81, 56, 27, 4, 69, 12, 91, 98, 23, 90, 52, 64};
+
+ for (int i = 0; i < 12; i++)
+ y_expected[i] = x1[i] + x2[i];
+
+ odp_ml_model_param_init(&model_param);
+
+ odp_ml_data_format_t input_format[2] = {
+ {
+ .data_type = ODP_ML_DATA_TYPE_FP64,
+ .data_type_size = 8,
+ .shape.type = ODP_ML_SHAPE_BATCH,
+ .shape.num_dim = 2,
+ .shape.dim = {ODP_ML_DIM_DYNAMIC, NUM_COLUMN},
+ .shape.dim_max = {MAX_BATCH_SIZE, NUM_COLUMN}
+ },
+ {
+ .data_type = ODP_ML_DATA_TYPE_FP64,
+ .data_type_size = 8,
+ .shape.type = ODP_ML_SHAPE_BATCH,
+ .shape.num_dim = 2,
+ .shape.dim = {ODP_ML_DIM_DYNAMIC, NUM_COLUMN},
+ .shape.dim_max = {MAX_BATCH_SIZE, NUM_COLUMN}
+ }
+ };
+
+ model_param.extra_info.num_inputs = 2;
+ model_param.extra_info.input_format = input_format;
+
+ /* Verify model info about matrix_mul.onnx */
+ if (fill_model_param("batch_add.onnx", &model_param))
+ return;
+
+ model = odp_ml_model_create("batch_add", &model_param);
+ free(model_param.model);
+ CU_ASSERT(model != ODP_ML_MODEL_INVALID);
+ if (!model)
+ return;
+
+ if (odp_ml_model_load(model, NULL)) {
+ CU_ASSERT(odp_ml_model_destroy(model) == 0);
+ return;
+ }
+
+ odp_ml_model_print(model);
+
+ /* Prepare parameters for running inference */
+ odp_ml_run_param_init(&run_param);
+ run_param.result = &result;
+
+ data.num_input_seg = 2;
+ data.input_seg = input_segs;
+ input_segs[0].addr = x1;
+ input_segs[1].addr = x2;
+
+ data.num_output_seg = 1;
+ data.output_seg = output_segs;
+ output_segs[0].size = sizeof(y);
+ output_segs[0].addr = y;
+
+ /* Test different batch sizes */
+ for (int i = 0; i < MAX_BATCH_SIZE; i++) {
+ run_param.batch_size = batch_size;
+ input_segs[0].size = sizeof(double) * NUM_COLUMN * batch_size;
+ input_segs[1].size = sizeof(double) * NUM_COLUMN * batch_size;
+ ret = odp_ml_run(model, &data, &run_param);
+ CU_ASSERT(ret == 1);
+ if (ret != 1)
+ goto fail;
+
+ for (uint32_t j = 0; j < batch_size * NUM_COLUMN; j++)
+ CU_ASSERT(y[j] == y_expected[j]);
+
+ batch_size--;
+ }
+
+ /* Test also without run results */
+ run_param.result = NULL;
+ ret = odp_ml_run(model, &data, &run_param);
+ CU_ASSERT(ret == 1);
+
+ /* Test different segment sizes */
+ batch_size = MAX_BATCH_SIZE;
+ odp_ml_run_param_init(&run_param);
+ run_param.result = &result;
+ run_param.batch_size = batch_size;
+ data.input_seg = input_segs;
+ data.output_seg = output_segs;
+
+ for (int seg_size = SIZE; seg_size > 0; seg_size--) {
+ int num_seg = (SIZE + seg_size - 1) / seg_size;
+
+ if ((uint32_t)num_seg > global.ml_capa.max_segs_per_input ||
+ (uint32_t)num_seg > global.ml_capa.max_segs_per_output)
+ break;
+
+ data.num_input_seg = num_seg * 2;
+ data.num_output_seg = num_seg;
+
+ for (int seg = 0; seg < num_seg; seg++) {
+ int size = seg_size;
+
+ if (seg == num_seg - 1)
+ size = SIZE - seg * seg_size;
+
+ input_segs[seg].addr = (char *)x1 + seg * seg_size;
+ input_segs[seg].size = size;
+ input_segs[seg + num_seg].addr = (char *)x2 + seg * seg_size;
+ input_segs[seg + num_seg].size = size;
+ output_segs[seg].addr = (char *)y + seg * seg_size;
+ output_segs[seg].size = size;
+ }
+
+ memset(y, 0, sizeof(y));
+ ret = odp_ml_run(model, &data, &run_param);
+ CU_ASSERT(ret == 1);
+ if (ret != 1)
+ goto fail;
+
+ for (uint32_t j = 0; j < batch_size * NUM_COLUMN; j++)
+ CU_ASSERT(y[j] == y_expected[j]);
+ }
+
+fail:
+ CU_ASSERT_FATAL(odp_ml_model_unload(model, NULL) == 0);
+ CU_ASSERT(odp_ml_model_destroy(model) == 0);
+}
+
+static void run_global_ml_model(void)
+{
+ int ret = 0;
+ odp_ml_run_result_t result;
+
+ ret = odp_ml_model_load(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ global.run_param.result = &result;
+
+ ret = odp_ml_run(global.ml_model, &global.data, &global.run_param);
+ CU_ASSERT(ret == 1);
+ CU_ASSERT(!result.error_code);
+ CU_ASSERT(*(int32_t *)global.output_seg.addr == global.y_expected);
+
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+ global.run_param.result = NULL;
+}
+
+static void test_ml_run(void)
+{
+ run_global_ml_model();
+ run_model_batch_add();
+}
+
+static void test_ml_run_multi(void)
+{
+ int ret;
+ int32_t y;
+ int32_t x = 8;
+ int32_t y_expected = 28;
+ odp_ml_data_t data[RUN_NUM];
+ odp_ml_data_seg_t input_seg;
+ odp_ml_data_seg_t output_seg;
+ odp_ml_run_param_t param[RUN_NUM];
+ odp_ml_run_result_t result[RUN_NUM];
+ uint64_t wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+
+ ret = odp_ml_model_load(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ param[0] = global.run_param;
+ param[0].result = &result[0];
+ odp_ml_run_param_init(&param[1]);
+ param[1].result = &result[1];
+
+ /* Prepare data for running model inference */
+ data[0] = global.data;
+ data[1].num_input_seg = NUM_INPUTS;
+ data[1].input_seg = &input_seg;
+ input_seg.size = sizeof(int32_t);
+ input_seg.addr = &x;
+
+ data[1].num_output_seg = NUM_OUTPUTS;
+ data[1].output_seg = &output_seg;
+ output_seg.size = sizeof(int32_t);
+ output_seg.addr = &y;
+
+ int num_completed = 0;
+
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_run_multi(global.ml_model, data + num_completed, param + num_completed,
+ RUN_NUM - num_completed);
+ CU_ASSERT(ret >= 0);
+ if (ret < 0)
+ break;
+
+ num_completed += ret;
+
+ if (num_completed >= RUN_NUM)
+ break;
+
+ odp_time_wait_ns(wait_ns);
+ }
+
+ CU_ASSERT(num_completed == RUN_NUM);
+ CU_ASSERT(!result[0].error_code);
+ CU_ASSERT(!result[1].error_code);
+ CU_ASSERT(*(int32_t *)global.output_seg.addr == global.y_expected);
+ CU_ASSERT(*(int32_t *)output_seg.addr == y_expected);
+
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+/* Test asynchronous inference running in ODP_ML_COMPL_MODE_EVENT mode */
+static void test_ml_model_run_async_event(void)
+{
+ int ret;
+ void *user_ptr;
+ odp_ml_compl_t compl;
+ odp_ml_run_result_t result;
+ odp_ml_data_seg_t *outputs;
+ odp_ml_compl_param_t compl_param;
+
+ /* Load model in order to run inference */
+ ret = odp_ml_model_load(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ compl = odp_ml_compl_alloc(global.compl_pool);
+ CU_ASSERT_FATAL(compl != ODP_ML_COMPL_INVALID);
+
+ odp_ml_compl_param_init(&compl_param);
+ compl_param.mode = ODP_ML_COMPL_MODE_EVENT;
+ compl_param.event = odp_ml_compl_to_event(compl);
+ compl_param.queue = global.queue;
+
+ /* user_ptr structure maintains the output data pointer for output retrieval */
+ user_ptr = &global.output_seg;
+ compl_param.user_ptr = user_ptr;
+
+ memset(global.output_seg.addr, 0, global.output_seg.size);
+ ret = odp_ml_run_start(global.ml_model, &global.data, &compl_param, NULL);
+ CU_ASSERT_FATAL(ret == 1);
+
+ /* Run event scheduler to find the ml completion event and verify it */
+ if (get_result_from_ml_compl_event(NULL, &result))
+ return;
+
+ CU_ASSERT(!result.error_code);
+ CU_ASSERT(result.user_ptr == user_ptr);
+
+ outputs = (odp_ml_data_seg_t *)result.user_ptr;
+ CU_ASSERT(*(int32_t *)outputs[0].addr == global.y_expected);
+
+ /* Unload model */
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+/* Test asynchronous inference running in ODP_ML_COMPL_MODE_POLL mode */
+static void test_ml_model_run_async_poll(void)
+{
+ int ret;
+ void *user_ptr;
+ odp_ml_run_result_t result;
+ odp_ml_data_seg_t *outputs;
+ odp_ml_compl_param_t compl_param;
+ uint64_t wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+
+ memset(&result, 0, sizeof(result));
+ /* Load model in order to run inference */
+ ret = odp_ml_model_load(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_ml_compl_param_init(&compl_param);
+ compl_param.mode = ODP_ML_COMPL_MODE_POLL;
+ compl_param.compl_id = 0;
+
+ /* user_ptr structure maintains the output data pointer for output retrieval */
+ user_ptr = &global.output_seg;
+ compl_param.user_ptr = user_ptr;
+
+ memset(global.output_seg.addr, 0, global.output_seg.size);
+ ret = odp_ml_run_start(global.ml_model, &global.data, &compl_param, NULL);
+ CU_ASSERT_FATAL(ret == 1);
+
+ /* When odp_ml_run_start() succeeded, continue to check completion status */
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_run_status(global.ml_model, 0, &result);
+ if (ret)
+ break;
+
+ /* ret = 0 meaning run has not finished, continue to check status */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ outputs = (odp_ml_data_seg_t *)result.user_ptr;
+
+ CU_ASSERT(ret > 0);
+ CU_ASSERT(!result.error_code);
+ CU_ASSERT(result.user_ptr == user_ptr);
+ CU_ASSERT(*(int32_t *)outputs[0].addr == global.y_expected);
+
+ /* Unload model */
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static void test_ml_run_start_multi(void)
+{
+ int ret;
+ int32_t y;
+ odp_ml_compl_t compl;
+ odp_ml_data_t data[RUN_NUM];
+ odp_ml_data_seg_t input_seg;
+ odp_ml_data_seg_t output_seg;
+ odp_ml_data_seg_t *outputs[RUN_NUM];
+ odp_ml_compl_param_t compl_param[RUN_NUM];
+ odp_ml_run_result_t run_result[RUN_NUM];
+ int32_t x = 5;
+ int32_t y_expected = 19;
+ uint64_t wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+
+ /* Load model in order to run inference */
+ ret = odp_ml_model_load(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ compl = odp_ml_compl_alloc(global.compl_pool);
+ CU_ASSERT_FATAL(compl != ODP_ML_COMPL_INVALID);
+
+ /* Prepare data for running model inference */
+ data[0] = global.data;
+
+ data[1].num_input_seg = NUM_INPUTS;
+ data[1].input_seg = &input_seg;
+ input_seg.size = sizeof(int32_t);
+ input_seg.addr = &x;
+
+ data[1].num_output_seg = NUM_OUTPUTS;
+ data[1].output_seg = &output_seg;
+ output_seg.size = sizeof(int32_t);
+ output_seg.addr = &y;
+
+ /* Two completion parameters: one use event mode, another poll mode */
+ odp_ml_compl_param_init(&compl_param[0]);
+ compl_param[0].mode = ODP_ML_COMPL_MODE_EVENT;
+ compl_param[0].event = odp_ml_compl_to_event(compl);
+ compl_param[0].queue = global.queue;
+ /* user_ptr structure maintains the output data pointer for output retrieval */
+ compl_param[0].user_ptr = &global.output_seg;
+
+ odp_ml_compl_param_init(&compl_param[1]);
+ compl_param[1].mode = ODP_ML_COMPL_MODE_POLL;
+ compl_param[1].compl_id = 0;
+ /* user_ptr structure maintains the output data pointer for output retrieval */
+ compl_param[1].user_ptr = &output_seg;
+
+ memset(global.output_seg.addr, 0, sizeof(int32_t));
+
+ int num_completed = 0;
+
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_run_start_multi(global.ml_model, data + num_completed,
+ compl_param + num_completed, NULL,
+ RUN_NUM - num_completed);
+ CU_ASSERT(ret >= 0);
+ if (ret < 0)
+ break;
+
+ num_completed += ret;
+
+ if (num_completed >= RUN_NUM)
+ break;
+
+ odp_time_wait_ns(wait_ns);
+ }
+
+ CU_ASSERT(num_completed == RUN_NUM);
+
+ /* Run event scheduler to find the ml completion event and verify it */
+ if (get_result_from_ml_compl_event(NULL, &run_result[0])) {
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ return;
+ }
+
+ CU_ASSERT(!run_result[0].error_code);
+ CU_ASSERT(run_result[0].user_ptr == &global.output_seg);
+ outputs[0] = (odp_ml_data_seg_t *)run_result[0].user_ptr;
+ CU_ASSERT(*(int32_t *)outputs[0][0].addr == global.y_expected);
+
+ /* Check completion status for the poll mode */
+ for (int i = 0; i < TIMEOUT; i++) {
+ ret = odp_ml_run_status(global.ml_model, 0, &run_result[1]);
+ if (ret)
+ break;
+
+ /* ret = 0 meaning run has not finished, continue to check status */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ outputs[1] = (odp_ml_data_seg_t *)run_result[1].user_ptr;
+ CU_ASSERT(ret > 0);
+ CU_ASSERT(!run_result[1].error_code);
+ CU_ASSERT(run_result[1].user_ptr == &output_seg);
+ CU_ASSERT(*(int32_t *)outputs[1][0].addr == y_expected);
+
+ /* Unload model */
+ ret = odp_ml_model_unload(global.ml_model, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static void test_ml_model_extra_stat_info(void)
+{
+ int ret;
+
+ ret = odp_ml_model_extra_stat_info(global.ml_model, NULL, 0);
+ CU_ASSERT(ret >= 0);
+}
+
+static void test_ml_model_extra_stats(void)
+{
+ int ret;
+
+ ret = odp_ml_model_extra_stats(global.ml_model, NULL, 0);
+ CU_ASSERT(ret >= 0);
+}
+
+odp_testinfo_t ml_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(test_ml_debug, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_create, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_lookup, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_info, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_load, check_load_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_load_async_poll, check_load_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_load_async_event, check_load_event),
+ /* Synchronous load/unload is used load/unload model before/after model run */
+ ODP_TEST_INFO_CONDITIONAL(test_ml_run, check_run_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_run_multi, check_run_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_run_async_event, check_run_event),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_run_async_poll, check_run_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_run_start_multi, check_run_poll_event),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_extra_stat_info, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_model_extra_stats, check_ml_support),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t ml_suites[] = {
+ {"ML", ml_suite_init, ml_suite_term, ml_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(ml_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/platform/linux-generic/test/validation/api/ml/requirements.txt b/platform/linux-generic/test/validation/api/ml/requirements.txt
new file mode 100644
index 000000000..2dcba7a3a
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/requirements.txt
@@ -0,0 +1,2 @@
+onnx
+numpy
diff --git a/platform/linux-generic/test/validation/api/ml/simple_linear.onnx b/platform/linux-generic/test/validation/api/ml/simple_linear.onnx
new file mode 100644
index 000000000..45c4b95b9
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/simple_linear.onnx
Binary files differ
diff --git a/platform/linux-generic/test/validation/api/ml/simple_linear_gen.py b/platform/linux-generic/test/validation/api/ml/simple_linear_gen.py
new file mode 100644
index 000000000..b3e6124cd
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/ml/simple_linear_gen.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+import onnx
+from onnx import helper
+from onnx import TensorProto
+
+weight = helper.make_tensor(name='w', data_type=TensorProto.INT32, dims=[1], vals=[3])
+w = helper.make_node('Constant', inputs=[], outputs=['w'], name='weight', value=weight)
+
+bias = helper.make_tensor(name='b', data_type=TensorProto.INT32, dims=[1], vals=[4])
+b = helper.make_node('Constant', inputs=[], outputs=['b'], name='bias', value=bias)
+
+# The functional nodes:
+mul = helper.make_node('Mul', inputs=['x', 'w'], outputs=['wx'], name='Mul')
+add = helper.make_node('Add', inputs=['wx', 'b'], outputs=['y'], name='Add')
+
+# Create the graph
+g = helper.make_graph([w, mul, b, add], 'linear',
+ [helper.make_tensor_value_info('x', TensorProto.INT32, [1])],
+ [helper.make_tensor_value_info('y', TensorProto.INT32, [1])]
+)
+
+model = helper.make_model(
+ producer_name='ODP validation tests',
+ model_version=1,
+ doc_string="y = 3x + 4",
+ graph=g,
+ opset_imports=[helper.make_opsetid("", 13)]
+)
+
+# Save the model
+onnx.save(model, 'simple_linear.onnx')
diff --git a/test/common_plat/validation/api/.gitignore b/platform/linux-generic/test/validation/api/pktio/.gitignore
index 7e563b8b3..7e563b8b3 100644
--- a/test/common_plat/validation/api/.gitignore
+++ b/platform/linux-generic/test/validation/api/pktio/.gitignore
diff --git a/platform/linux-generic/test/validation/api/pktio/Makefile.am b/platform/linux-generic/test/validation/api/pktio/Makefile.am
new file mode 100644
index 000000000..1646743fe
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/Makefile.am
@@ -0,0 +1,32 @@
+dist_check_SCRIPTS = pktio_env \
+ pktio_run.sh \
+ pktio_run_tap.sh
+
+if ODP_PKTIO_PCAP
+dist_check_SCRIPTS += pktio_run_pcap.sh
+endif
+if PKTIO_DPDK
+dist_check_SCRIPTS += pktio_run_dpdk.sh
+endif
+
+test_SCRIPTS = $(dist_check_SCRIPTS)
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/platform/linux-generic/test/validation/api/pktio/pktio_env b/platform/linux-generic/test/validation/api/pktio/pktio_env
new file mode 100644
index 000000000..754e73b75
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/pktio_env
@@ -0,0 +1,118 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015-2018 Linaro Limited
+#
+# Test script wrapper for running ODP pktio apps on linux-generic.
+#
+# For linux-generic the default behavior is to create two pairs of
+# virtual Ethernet interfaces and provide the names of these via
+# environment variables to pktio apps, the interfaces will be removed
+# before the script exits.
+#
+# Note that the creation of virtual Ethernet devices depends on having
+# CONFIG_VETH enabled in the kernel, if not enabled the env setup will be skipped.
+#
+# Network set up
+# IF0 <---> IF1
+# IF2 <---> IF3
+IF0=pktiop0p1
+IF1=pktiop1p0
+IF2=pktiop2p3
+IF3=pktiop3p2
+
+if [ "$0" = "$BASH_SOURCE" ]; then
+ echo "Error: Platform specific env file has to be sourced."
+fi
+
+check_for_root()
+{
+ if [ "$(id -u)" != "0" ]; then
+ echo "check_for_root(): need to be root to setup VETH"
+ return 1
+ fi
+ return 0
+}
+
+# wait for a network interface's operational state to be "up"
+wait_for_iface_up()
+{
+ iface=$1
+ cnt=0
+
+ while [ $cnt -lt 50 ]; do
+ read operstate < /sys/class/net/$iface/operstate
+
+ if [ $? -ne 0 ]; then
+ break
+ elif [ "$operstate" = "up" ]; then
+ return 0
+ fi
+
+ sleep 0.1
+ cnt=`expr $cnt + 1`
+ done
+
+ return 1
+}
+
+setup_pktio_env()
+{
+ echo "pktio: setting up test interfaces $IF0, $IF1, $IF2, $IF3."
+
+ check_for_root
+ if [ $? -ne 0 ]; then
+ return 1
+ fi
+
+ for iface in $IF0 $IF1 $IF2 $IF3; do
+ ip link show $iface 2> /dev/null
+ if [ $? -eq 0 ]; then
+ echo "pktio: interface $iface already exist $?"
+ return 2
+ fi
+ done
+
+ if [ "$1" = "clean" ]; then
+ trap cleanup_pktio_env EXIT
+ fi
+
+ ip link add $IF0 type veth peer name $IF1
+ if [ $? -ne 0 ]; then
+ echo "pktio: error: unable to create veth pair"
+ return 3
+ fi
+ ip link add $IF2 type veth peer name $IF3
+ if [ $? -ne 0 ]; then
+ echo "pktio: error: unable to create veth pair"
+ return 4
+ fi
+
+ for iface in $IF0 $IF1 $IF2 $IF3; do
+ ip link set $iface mtu 9216 up
+ ifconfig $iface -arp
+ done
+
+ # check that the interface has come up before starting the test
+ for iface in $IF0 $IF1 $IF2 $IF3; do
+ wait_for_iface_up $iface
+ if [ $? -ne 0 ]; then
+ echo "pktio: interface $iface failed to come up"
+ return 5
+ fi
+ done
+}
+
+cleanup_pktio_env()
+{
+ echo "pktio: removing test interfaces $IF0, $IF1, $IF2, $IF3"
+ check_for_root
+ if [ $? -ne 0 ]; then
+ return 1
+ fi
+
+ for iface in $IF0 $IF1 $IF2 $IF3; do
+ ip link del $iface 2> /dev/null
+ done
+ return 0
+}
diff --git a/platform/linux-generic/test/validation/api/pktio/pktio_run.sh b/platform/linux-generic/test/validation/api/pktio/pktio_run.sh
new file mode 100755
index 000000000..89671df54
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/pktio_run.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015-2018 Linaro Limited
+#
+
+# Proceed the pktio tests. This script expects at least one argument:
+# setup) setup the pktio test environment
+# cleanup) cleanup the pktio test environment
+# run) run the pktio tests (setup, run, cleanup)
+# extra arguments are passed unchanged to the test itself (pktio_main)
+# Without arguments, "run" is assumed and no extra argument is passed to the
+# test (legacy mode).
+#
+
+# directories where pktio_main binary can be found:
+# -in the validation dir when running make check (intree or out of tree)
+# -in the script directory, when running after 'make install', or
+# -in the validation when running standalone (./pktio_run) intree.
+# -in the current directory.
+# running stand alone out of tree requires setting PATH
+PATH=${TEST_DIR}/api/pktio:$PATH
+PATH=$(dirname $0):$PATH
+PATH=$(dirname $0)/../../../../../../test/validation/api/pktio:$PATH
+PATH=.:$PATH
+
+pktio_main_path=$(which pktio_main${EXEEXT})
+if [ -x "$pktio_main_path" ] ; then
+ echo "running with pktio_main: $pktio_run_path"
+else
+ echo "cannot find pktio_main: please set you PATH for it."
+ exit 1
+fi
+
+# directory where platform test sources are, including scripts
+TEST_SRC_DIR=$(dirname $0)
+
+# exit codes expected by automake for skipped tests
+TEST_SKIPPED=77
+
+# Use installed pktio env or for make check take it from platform directory
+if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
+ . ${TEST_SRC_DIR}/pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
+ echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
+ exit 1
+fi
+
+run_test()
+{
+ local ret=0
+
+ # environment variables are used to control which socket method is
+ # used, so try each combination to ensure decent coverage.
+ for distype in MMAP MMSG; do
+ unset ODP_PKTIO_DISABLE_SOCKET_${distype}
+ done
+
+ for distype in SKIP MMAP; do
+ if [ "$disabletype" != "SKIP" ]; then
+ export ODP_PKTIO_DISABLE_SOCKET_${distype}=y
+ fi
+ pktio_main${EXEEXT} $*
+ if [ $? -ne 0 ]; then
+ ret=1
+ fi
+ done
+
+ if [ $ret -ne 0 ]; then
+ echo "!!! FAILED !!!"
+ fi
+
+ return $ret
+}
+
+run()
+{
+ # need to be root to run tests with real interfaces
+ if [ "$(id -u)" != "0" ]; then
+ exit $ret
+ fi
+
+ if [ "$ODP_PKTIO_IF0" = "" ]; then
+ # no interfaces specified, use default veth interfaces
+ # setup by the pktio_env script
+ setup_pktio_env clean
+ if [ $? != 0 ]; then
+ echo "Failed to setup test environment, skipping test."
+ exit $TEST_SKIPPED
+ fi
+ export ODP_PKTIO_IF0=$IF0
+ export ODP_PKTIO_IF1=$IF1
+ fi
+
+ run_test
+ ret=$?
+
+ exit $ret
+}
+
+if [ $# != 0 ]; then
+ action=$1
+ shift
+fi
+
+case "$action" in
+ setup) setup_pktio_env ;;
+ cleanup) cleanup_pktio_env ;;
+ run) run ;;
+ *) run ;;
+esac
diff --git a/platform/linux-generic/test/validation/api/pktio/pktio_run_dpdk.sh b/platform/linux-generic/test/validation/api/pktio/pktio_run_dpdk.sh
new file mode 100755
index 000000000..68510c2a3
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/pktio_run_dpdk.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
+#
+
+# Proceed the pktio tests. This script expects at least one argument:
+# setup) setup the pktio test environment
+# cleanup) cleanup the pktio test environment
+# run) run the pktio tests (setup, run, cleanup)
+# extra arguments are passed unchanged to the test itself (pktio_main)
+# Without arguments, "run" is assumed and no extra argument is passed to the
+# test (legacy mode).
+#
+
+# directories where pktio_main binary can be found:
+# -in the validation dir when running make check (intree or out of tree)
+# -in the script directory, when running after 'make install', or
+# -in the validation when running standalone (./pktio_run) intree.
+# -in the current directory.
+# running stand alone out of tree requires setting PATH
+PATH=${TEST_DIR}/api/pktio:$PATH
+PATH=$(dirname $0):$PATH
+PATH=$(dirname $0)/../../../../../../test/validation/api/pktio:$PATH
+PATH=.:$PATH
+
+pktio_main_path=$(which pktio_main${EXEEXT})
+if [ -x "$pktio_main_path" ] ; then
+ echo "running with pktio_main: $pktio_run_path"
+else
+ echo "cannot find pktio_main: please set you PATH for it."
+fi
+
+# directory where platform test sources are, including scripts
+TEST_SRC_DIR=$(dirname $0)
+
+# exit codes expected by automake for skipped tests
+TEST_SKIPPED=77
+
+# Use installed pktio env or for make check take it from platform directory
+if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
+ . ${TEST_SRC_DIR}/pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
+ echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
+ exit 1
+fi
+
+run_test()
+{
+ local ret=0
+
+ pktio_main${EXEEXT} $*
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "!!! FAILED !!!"
+ fi
+
+ exit $ret
+}
+
+run()
+{
+ # need to be root to set the interface.
+ if [ "$(id -u)" != "0" ]; then
+ echo "pktio: need to be root to setup DPDK interfaces."
+ return $TEST_SKIPPED
+ fi
+
+ if [ "$ODP_PKTIO_IF0" = "" ]; then
+ setup_pktio_env clean
+ export ODP_PKTIO_DPDK_PARAMS="--no-pci --vdev net_pcap0,iface=$IF0 --vdev net_pcap1,iface=$IF1"
+ export ODP_PKTIO_IF0=dpdk:0
+ export ODP_PKTIO_IF1=dpdk:1
+ fi
+
+ run_test
+}
+
+if [ $# != 0 ]; then
+ action=$1
+ shift
+fi
+
+case "$1" in
+ setup) setup_pktio_env ;;
+ cleanup) cleanup_pktio_env ;;
+ run) run ;;
+ *) run ;;
+esac
diff --git a/platform/linux-generic/test/validation/api/pktio/pktio_run_pcap.sh b/platform/linux-generic/test/validation/api/pktio/pktio_run_pcap.sh
new file mode 100755
index 000000000..700abbf35
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/pktio_run_pcap.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015-2018 Linaro Limited
+#
+
+# any parameter passed as arguments to this script is passed unchanged to
+# the test itself (pktio_main)
+
+# directories where pktio_main binary can be found:
+# -in the validation dir when running make check (intree or out of tree)
+# -in the script directory, when running after 'make install', or
+# -in the validation when running standalone intree.
+# -in the current directory.
+# running stand alone out of tree requires setting PATH
+PATH=${TEST_DIR}/api/pktio:$PATH
+PATH=$(dirname $0):$PATH
+PATH=$(dirname $0)/../../../../../../test/validation/api/pktio:$PATH
+PATH=.:$PATH
+
+pktio_main_path=$(which pktio_main${EXEEXT})
+if [ -x "$pktio_main_path" ] ; then
+ echo "running with $pktio_main_path"
+else
+ echo "cannot find pktio_main${EXEEXT}: please set you PATH for it."
+ exit 1
+fi
+
+export ODP_PKTIO_TEST_DISABLE_START_STOP=1
+
+PCAP_FNAME=vald.pcap
+export ODP_PKTIO_IF0="pcap:out=${PCAP_FNAME}"
+export ODP_PKTIO_IF1="pcap:in=${PCAP_FNAME}"
+pktio_main${EXEEXT} $*
+ret=$?
+rm -f ${PCAP_FNAME}
+exit $ret
diff --git a/platform/linux-generic/test/validation/api/pktio/pktio_run_tap.sh b/platform/linux-generic/test/validation/api/pktio/pktio_run_tap.sh
new file mode 100755
index 000000000..0fd08134e
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/pktio/pktio_run_tap.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015 Ilya Maximets <i.maximets@samsung.com>
+#
+
+
+# any parameter passed as arguments to this script is passed unchanged to
+# the test itself (pktio_main)
+
+# directories where pktio_main binary can be found:
+# -in the validation dir when running make check (intree or out of tree)
+# -in the script directory, when running after 'make install', or
+# -in the validation when running standalone intree.
+# -in the current directory.
+# running stand alone out of tree requires setting PATH
+PATH=${TEST_DIR}/api/pktio:$PATH
+PATH=$(dirname $0):$PATH
+PATH=$(dirname $0)/../../../../../../test/validation/api/pktio:$PATH
+PATH=.:$PATH
+
+pktio_main_path=$(which pktio_main${EXEEXT})
+if [ -x "$pktio_main_path" ] ; then
+ echo "running with $pktio_main_path"
+else
+ echo "cannot find pktio_main${EXEEXT}: please set you PATH for it."
+fi
+
+# exit code expected by automake for skipped tests
+TEST_SKIPPED=77
+
+TAP_BASE_NAME=iotap_vald
+IF0=${TAP_BASE_NAME}0
+IF1=${TAP_BASE_NAME}1
+BR=${TAP_BASE_NAME}_br
+
+export ODP_PKTIO_IF0="tap:$IF0"
+export ODP_PKTIO_IF1="tap:$IF1"
+
+tap_cleanup()
+{
+ ret=$?
+
+ for iface in $IF0 $IF1; do
+ ip link set dev $iface nomaster
+ done
+
+ ip link delete $BR type bridge
+
+ for iface in $IF0 $IF1; do
+ ip tuntap del mode tap $iface
+ done
+
+ trap - EXIT
+ exit $ret
+}
+
+tap_setup()
+{
+ if [ "$(id -u)" != "0" ]; then
+ echo "pktio: need to be root to setup TAP interfaces."
+ return $TEST_SKIPPED
+ fi
+
+ for iface in $IF0 $IF1 $BR; do
+ ip link show $iface 2> /dev/null
+ if [ $? -eq 0 ]; then
+ echo "pktio: interface $iface already exist $?"
+ return 2
+ fi
+ done
+
+ trap tap_cleanup EXIT
+
+ for iface in $IF0 $IF1; do
+ ip tuntap add mode tap $iface
+ if [ $? -ne 0 ]; then
+ echo "pktio: error: unable to create TAP device $iface"
+ return 3
+ fi
+ done
+
+ ip link add name $BR type bridge
+ if [ $? -ne 0 ]; then
+ echo "pktio: error: unable to create bridge $BR"
+ return 3
+ fi
+
+ for iface in $IF0 $IF1; do
+ ip link set dev $iface master $BR
+ if [ $? -ne 0 ]; then
+ echo "pktio: error: unable to add $iface to bridge $BR"
+ return 4
+ fi
+ done
+
+ for iface in $IF0 $IF1 $BR; do
+ ifconfig $iface -arp
+ sysctl -w net.ipv6.conf.${iface}.disable_ipv6=1
+ ip link set dev $iface mtu 9216 up
+ done
+
+ return 0
+}
+
+tap_setup
+ret=$?
+if [ $ret -ne 0 ]; then
+ echo "pktio: tap_setup() FAILED!"
+ exit $TEST_SKIPPED
+fi
+
+# Using ODP_WAIT_FOR_NETWORK to prevent fail if tap still not enabled in bridge
+ODP_WAIT_FOR_NETWORK=yes pktio_main${EXEEXT} $*
+ret=$?
+
+exit $ret
diff --git a/test/linux-generic/validation/api/shmem/.gitignore b/platform/linux-generic/test/validation/api/shmem/.gitignore
index 74195f576..74195f576 100644
--- a/test/linux-generic/validation/api/shmem/.gitignore
+++ b/platform/linux-generic/test/validation/api/shmem/.gitignore
diff --git a/platform/linux-generic/test/validation/api/shmem/Makefile.am b/platform/linux-generic/test/validation/api/shmem/Makefile.am
new file mode 100644
index 000000000..309eceb92
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/Makefile.am
@@ -0,0 +1,14 @@
+include ../Makefile.inc
+
+#the main test program is shmem_linux, which, in turn, starts a shmem_odp:
+test_PROGRAMS = shmem_linux shmem_odp1 shmem_odp2
+
+#shmem_linux is stand alone, pure linux (no ODP):
+shmem_linux_SOURCES = shmem_linux.c shmem_linux.h shmem_common.h
+shmem_linux_LDFLAGS =
+shmem_linux_LDADD =
+
+#shmem_odp1 and shmem_odp2 are the 2 ODP processes:
+shmem_odp1_SOURCES = shmem_odp1.c shmem_odp1.h shmem_common.h
+
+shmem_odp2_SOURCES = shmem_odp2.c shmem_odp2.h shmem_common.h
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_common.h b/platform/linux-generic/test/validation/api/shmem/shmem_common.h
new file mode 100644
index 000000000..145aea350
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_common.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#ifndef _COMMON_TEST_SHMEM_H_
+#define _COMMON_TEST_SHMEM_H_
+
+#define SHM_NAME "odp_linux_shared_mem"
+#define DEFAULT_SHM_DIR "/dev/shm"
+#define FIFO_NAME_FMT "%s/%d/shmem_test_fifo-%d"
+#define ALIGN_SIZE (128)
+#define TEST_SHARE_FOO (0xf0f0f0f0)
+#define TEST_SHARE_BAR (0xf0f0f0f)
+#define TEST_FAILURE 'F'
+#define TEST_SUCCESS 'S'
+
+typedef struct {
+ uint32_t foo;
+ uint32_t bar;
+} test_shared_linux_data_t;
+
+#endif
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_linux.c b/platform/linux-generic/test/validation/api/shmem/shmem_linux.c
new file mode 100644
index 000000000..061d86d6e
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_linux.c
@@ -0,0 +1,331 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/* this test makes sure that odp shared memory created with the ODP_SHM_PROC
+ * flag is visible under linux, and checks that memory created with the
+ * ODP_SHM_EXPORT flag is visible by other ODP instances.
+ * It therefore checks both that the link
+ * name under /dev/shm is correct, and also checks that the memory contents
+ * is indeed shared.
+ * we want:
+ * -the odp test to run using C UNIT
+ * -the main process to return the correct return code.
+ * (for the autotools test harness)
+ *
+ * To achieve this, the flow of operations is as follows:
+ *
+ * linux process (main, non odp) |
+ * (shmem_linux.c) |
+ * |
+ * |
+ * |
+ * main() |
+ * forks odp_app1 process |
+ * wait for named pipe creation |
+ * |
+ * | ODP_APP1 process
+ * | (shmem_odp1.c)
+ * |
+ * | allocate shmem
+ * | populate shmem
+ * | create named pipe
+ * | wait for test report in fifo...
+ * read shared memory |
+ * check if memory contents is OK |
+ * If not OK, write "F" in fifo and |
+ * exit with failure code. | -------------------
+ * |
+ * forks odp app2 process | ODP APP2 process
+ * wait for child termination & status| (shmem_odp2.c)
+ * | lookup ODP_APP1 shared memory,
+ * | check if memory contents is OK
+ * | Exit(0) on success, exit(1) on fail
+ * If child failed, write "F" in fifo |
+ * exit with failure code. | -------------------
+ * |
+ * OK, write "S" in fifo, |
+ * wait for child termination & status|
+ * terminate with same status as child|
+ * | ODP APP1 process
+ * | (shmem_odp1.c)
+ * |
+ * | ...(continued)
+ * | read S(success) or F(fail) from fifo
+ * | report success or failure to C-Unit
+ * | Exit(0) on success, exit(1) on fail
+ * wait for child termination & status |
+ * terminate with same status as child |
+ * |
+ * \|/
+ * time
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <libgen.h>
+#include <linux/limits.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include "shmem_linux.h"
+#include "shmem_common.h"
+
+#define ODP_APP1_NAME "shmem_odp1" /* name of the odp1 program, in this dir */
+#define ODP_APP2_NAME "shmem_odp2" /* name of the odp2 program, in this dir */
+/* odp-<pid>-shm-<name> */
+#define DEVNAME_DEFAULT_DIR "/dev/shm"
+#define DEVNAME_FMT "%s/%d/odp-%" PRIu64 "-shm-%s"
+#define MAX_FIFO_WAIT 30 /* Max time waiting for the fifo (sec) */
+
+/*
+ * read the attributes of an externally shared mem object:
+ * input: ext_odp_pid, blockname: the remote ODP instance and the exported
+ * block name to be searched.
+ * Output: filename: the memory block underlying file to be opened
+ * (the given buffer should be big enough i.e. at
+ * least ISHM_FILENAME_MAXLEN bytes)
+ * The 3 following parameters are really here for debug
+ * as they are really meaningless in a non-odp process:
+ * len: the block real length (bytes, multiple of page sz)
+ * flags: the _ishm flags setting the block was created with
+ * align: the alignment setting the block was created with
+ *
+ * return 0 on success, non zero on error
+ */
+static int read_shmem_attributes(uint64_t ext_odp_pid, const char *blockname,
+ char *filename, uint64_t *len,
+ uint32_t *flags, uint64_t *user_len,
+ uint32_t *user_flags, uint32_t *align,
+ uint64_t *offset)
+{
+ char shm_attr_filename[PATH_MAX];
+ FILE *export_file;
+ char *shm_dir = getenv("ODP_SHM_DIR");
+
+ sprintf(shm_attr_filename, DEVNAME_FMT,
+ shm_dir ? shm_dir : DEVNAME_DEFAULT_DIR,
+ getuid(),
+ ext_odp_pid, blockname);
+
+ /* O_CREAT flag not given => failure if shm_attr_filename does not
+ * already exist */
+ export_file = fopen(shm_attr_filename, "r");
+ if (export_file == NULL)
+ return -1;
+
+ if (fscanf(export_file, "ODP exported shm block info: ") != 0)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "ishm_blockname: %*s ") != 0)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "file: %s ", filename) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "length: %" PRIu64 " ", len) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "flags: %" PRIu32 " ", flags) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "user_length: %" PRIu64 " ", user_len) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "user_flags: %" PRIu32 " ", user_flags) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "align: %" PRIu32 " ", align) != 1)
+ goto export_file_read_err;
+
+ if (fscanf(export_file, "offset: %" PRIu64 " ", offset) != 1)
+ goto export_file_read_err;
+
+ fclose(export_file);
+ return 0;
+
+export_file_read_err:
+ fclose(export_file);
+ return -1;
+}
+
+void test_success(char *fifo_name, int fd, pid_t odp_app)
+{
+ int status;
+ int nb_char;
+ char result = TEST_SUCCESS;
+ /* write "Success" to the FIFO */
+ nb_char = write(fd, &result, sizeof(char));
+ close(fd);
+ /* wait for the odp app1 to terminate */
+ waitpid(odp_app, &status, 0);
+ /* if the write failed, report an error anyway */
+ if (nb_char != 1)
+ status = 1;
+ unlink(fifo_name);
+ exit(status); /* the status reported by the odp side is returned */
+}
+
+void test_failure(char *fifo_name, int fd, pid_t odp_app)
+{
+ int status;
+ char result;
+
+ int nb_char __attribute__((unused)); /*ignored: we fail anyway */
+
+ result = TEST_FAILURE;
+ /* write "Failure" to the FIFO */
+ nb_char = write(fd, &result, sizeof(char));
+ close(fd);
+ /* wait for the odp app1 to terminate */
+ waitpid(odp_app, &status, 0);
+ unlink(fifo_name);
+ exit(1); /* error */
+}
+
+int main(int argc __attribute__((unused)), char *argv[])
+{
+ char prg_name[PATH_MAX];
+ char odp_name1[PATH_MAX];
+ char odp_name2[PATH_MAX];
+ int nb_sec;
+ int size;
+ pid_t odp_app1;
+ pid_t odp_app2;
+ char *odp_params1 = NULL;
+ char *odp_params2[3];
+ char pid1[10];
+ char fifo_name[PATH_MAX]; /* fifo for linux->odp feedback */
+ int fifo_fd = -1;
+ char shm_filename[PATH_MAX];/* shared mem device name, under /dev/shm */
+ uint64_t len;
+ uint64_t offset;
+ uint32_t flags;
+ uint64_t user_len;
+ uint32_t user_flags;
+ uint32_t align;
+ int shm_fd;
+ test_shared_linux_data_t *addr;
+ int app2_status;
+ uid_t uid = getuid();
+ char *shm_dir = getenv("ODP_SHM_DIR");
+ const char *exeext = getenv("EXEEXT");
+ char *dir_name;
+
+ if (exeext == NULL)
+ exeext = "";
+
+ strncpy(prg_name, argv[0], PATH_MAX - 1);
+ prg_name[PATH_MAX - 1] = 0;
+ dir_name = dirname(prg_name);
+
+ /* odp_app1 is in the same directory as this file: */
+ snprintf(odp_name1, sizeof(odp_name1), "%s/%s%s", dir_name, ODP_APP1_NAME, exeext);
+
+ /* start the ODP application: */
+ odp_app1 = fork();
+ if (odp_app1 < 0) /* error */
+ exit(1);
+
+ if (odp_app1 == 0) { /* child */
+ execv(odp_name1, &odp_params1); /* no return unless error */
+ fprintf(stderr, "execv failed: %s\n", strerror(errno));
+ }
+
+ /* wait max 30 sec for the fifo to be created by the ODP side.
+ * Just die if time expire as there is no fifo to communicate
+ * through... */
+ sprintf(fifo_name, FIFO_NAME_FMT,
+ shm_dir ? shm_dir : DEFAULT_SHM_DIR,
+ uid, odp_app1);
+ for (nb_sec = 0; nb_sec < MAX_FIFO_WAIT; nb_sec++) {
+ fifo_fd = open(fifo_name, O_WRONLY);
+ if (fifo_fd >= 0)
+ break;
+ sleep(1);
+ }
+ if (fifo_fd < 0)
+ exit(1);
+ printf("pipe found\n");
+
+ /* the linux named pipe has now been found, meaning that the
+ * ODP application is up and running, and has allocated shmem.
+ * check to see if linux can see the created shared memory: */
+
+ /* read the shared memory attributes (includes the shm filename): */
+ if (read_shmem_attributes(odp_app1, SHM_NAME,
+ shm_filename, &len, &flags,
+ &user_len, &user_flags, &align,
+ &offset) != 0) {
+ printf("error read_shmem_attributes\n");
+ test_failure(fifo_name, fifo_fd, odp_app1);
+ }
+
+ /* open the shm filename (which is either on /dev/shm/ or on hugetlbfs)
+ * O_CREAT flag not given => failure if shm_devname does not already
+ * exist */
+ shm_fd = open(shm_filename, O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (shm_fd == -1) {
+ fprintf(stderr, "unable to open %s\n", shm_filename);
+ test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
+ }
+
+ /* linux ODP guarantees page size alignment. Larger alignment may
+ * fail as 2 different processes will have fully unrelated
+ * virtual spaces.
+ */
+ size = sizeof(test_shared_linux_data_t);
+
+ addr = mmap(NULL, size, PROT_READ, MAP_SHARED, shm_fd, offset);
+ if (addr == MAP_FAILED) {
+ fprintf(stderr, "shmem_linux: mmap failed: %s\n",
+ strerror(errno));
+ test_failure(fifo_name, fifo_fd, odp_app1);
+ }
+
+ /* check that we see what the ODP application wrote in the memory */
+ if ((addr->foo != TEST_SHARE_FOO) || (addr->bar != TEST_SHARE_BAR)) {
+ fprintf(stderr, "ERROR: addr->foo %x addr->bar %x\n",
+ addr->foo, addr->bar);
+ test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
+ }
+
+ /* odp_app2 is in the same directory as this file: */
+ snprintf(odp_name2, sizeof(odp_name2), "%s/%s%s", dir_name, ODP_APP2_NAME, exeext);
+
+ /* start the second ODP application with pid of ODP_APP1 as parameter:*/
+ sprintf(pid1, "%d", odp_app1);
+ odp_params2[0] = odp_name2;
+ odp_params2[1] = pid1;
+ odp_params2[2] = NULL;
+ odp_app2 = fork();
+ if (odp_app2 < 0) /* error */
+ exit(1);
+
+ if (odp_app2 == 0) { /* child */
+ execv(odp_name2, odp_params2); /* no return unless error */
+ fprintf(stderr, "execv failed: %s\n", strerror(errno));
+ }
+
+ /* wait for the second ODP application to terminate:
+ * status is OK if that second ODP application could see the
+ * memory shared by the first one. */
+ waitpid(odp_app2, &app2_status, 0);
+
+ if (app2_status)
+ test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
+
+ /* everything looked good: */
+ test_success(fifo_name, fifo_fd, odp_app1);
+}
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_linux.h b/platform/linux-generic/test/validation/api/shmem/shmem_linux.h
new file mode 100644
index 000000000..d3f83636b
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_linux.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+void test_success(char *fifo_name, int fd, pid_t odp_app);
+void test_failure(char *fifo_name, int fd, pid_t odp_app);
+int main(int argc, char *argv[]);
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_odp1.c b/platform/linux-generic/test/validation/api/shmem/shmem_odp1.c
new file mode 100644
index 000000000..09dd04cd1
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_odp1.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <odp_cunit_common.h>
+#include "shmem_odp1.h"
+#include "shmem_common.h"
+
+#define TEST_SHARE_FOO (0xf0f0f0f0)
+#define TEST_SHARE_BAR (0xf0f0f0f)
+
+void shmem_test_odp_shm_proc(void)
+{
+ char fifo_name[PATH_MAX];
+ int fd;
+ odp_shm_t shm;
+ test_shared_data_t *test_shared_data;
+ char test_result;
+ char *shm_dir = getenv("ODP_SHM_DIR");
+
+ printf("start with pid %d\n", getpid());
+ /* reminder: ODP_SHM_PROC => export to linux, ODP_SHM_EXPORT=>to odp */
+ shm = odp_shm_reserve(SHM_NAME,
+ sizeof(test_shared_data_t),
+ ALIGN_SIZE, ODP_SHM_PROC | ODP_SHM_EXPORT);
+ CU_ASSERT_FATAL(ODP_SHM_INVALID != shm);
+ test_shared_data = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(NULL != test_shared_data);
+ test_shared_data->foo = TEST_SHARE_FOO;
+ test_shared_data->bar = TEST_SHARE_BAR;
+
+ odp_mb_full();
+
+ /* open the fifo: this will indicate to linux process that it can
+ * start the shmem lookups and check if it sees the data */
+ sprintf(fifo_name, FIFO_NAME_FMT,
+ shm_dir ? shm_dir : DEFAULT_SHM_DIR,
+ getuid(), getpid());
+ CU_ASSERT_FATAL(mkfifo(fifo_name, 0666) == 0);
+
+ /* read from the fifo: the linux process result: */
+ printf("shmem_odp1: opening fifo: %s\n", fifo_name);
+ fd = open(fifo_name, O_RDONLY);
+ CU_ASSERT_FATAL(fd >= 0);
+
+ printf("shmem_odp1: reading fifo: %s\n", fifo_name);
+ CU_ASSERT(read(fd, &test_result, sizeof(char)) == 1);
+ printf("shmem_odp1: read fifo: %d\n", test_result);
+ printf("shmem_odp1: closing fifo: %s\n", fifo_name);
+ close(fd);
+ CU_ASSERT_FATAL(test_result == TEST_SUCCESS);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+odp_testinfo_t shmem_suite[] = {
+ ODP_TEST_INFO(shmem_test_odp_shm_proc),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t shmem_suites[] = {
+ {"Shared Memory", NULL, NULL, shmem_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(shmem_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_odp1.h b/platform/linux-generic/test/validation/api/shmem/shmem_odp1.h
new file mode 100644
index 000000000..64bffd684
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_odp1.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+void shmem_test_odp_shm_proc(void);
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_odp2.c b/platform/linux-generic/test/validation/api/shmem/shmem_odp2.c
new file mode 100644
index 000000000..1f4c04fa0
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_odp2.c
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <odp_cunit_common.h>
+#include "shmem_odp2.h"
+#include "shmem_common.h"
+
+#define TEST_SHARE_FOO (0xf0f0f0f0)
+#define TEST_SHARE_BAR (0xf0f0f0f)
+
+/* The C unit test harness is run by ODP1 app which will be told the return
+ * status of this process. See top of shmem_linux.c for chart flow of events
+ */
+int main(int argc, char *argv[])
+{
+ odp_instance_t odp1;
+ odp_instance_t odp2;
+ odp_shm_t shm;
+ odp_shm_info_t info;
+ test_shared_data_t *test_shared_data;
+
+ /* odp init: */
+ if (0 != odp_init_global(&odp2, NULL, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return 1;
+ }
+ if (0 != odp_init_local(odp2, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return 1;
+ }
+
+ /* test: map ODP1 memory and check its contents:
+ * The pid of the ODP instantiation process sharing its memory
+ * is given as first arg. In linux-generic ODP, this pid is actually
+ * the ODP instance */
+ if (argc != 2) {
+ ODPH_ERR("One single parameter expected, %d found\n", argc);
+ return 1;
+ }
+ odp1 = (odp_instance_t)atoi(argv[1]);
+
+ printf("shmem_odp2: trying to grab %s from pid %d\n",
+ SHM_NAME, (int)odp1);
+ shm = odp_shm_import(SHM_NAME, odp1, SHM_NAME);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("odp_shm_import() failed\n");
+ return 1;
+ }
+
+ /* check that the read size matches the allocated size (in other ODP):*/
+ if ((odp_shm_info(shm, &info)) ||
+ (info.size != sizeof(*test_shared_data))) {
+ ODPH_ERR("odp_shm_info() failed\n");
+ return 1;
+ }
+
+ test_shared_data = odp_shm_addr(shm);
+ if (test_shared_data == NULL) {
+ ODPH_ERR("odp_shm_addr() failed\n");
+ return 1;
+ }
+
+ if (test_shared_data->foo != TEST_SHARE_FOO) {
+ ODPH_ERR("Invalid data TEST_SHARE_FOO\n");
+ return 1;
+ }
+
+ if (test_shared_data->bar != TEST_SHARE_BAR) {
+ ODPH_ERR("Invalid data TEST_SHARE_BAR\n");
+ return 1;
+ }
+
+ if (odp_shm_free(shm) != 0) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return 1;
+ }
+
+ /* odp term: */
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return 1;
+ }
+
+ if (0 != odp_term_global(odp2)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return 1;
+ }
+
+ printf("%s SUCCESS\n", __FILE__);
+ return 0;
+}
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_odp2.h b/platform/linux-generic/test/validation/api/shmem/shmem_odp2.h
new file mode 100644
index 000000000..cf841e6fd
--- /dev/null
+++ b/platform/linux-generic/test/validation/api/shmem/shmem_odp2.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+int main(int argc, char *argv[]);
diff --git a/scripts/build-pktio-dpdk b/scripts/build-pktio-dpdk
deleted file mode 100755
index 6c6830ac0..000000000
--- a/scripts/build-pktio-dpdk
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-TARGET=${TARGET:-"x86_64-native-linuxapp-gcc"}
-
-export ROOT_DIR=$(readlink -e $(dirname $0) | sed 's|/scripts||')
-pushd ${ROOT_DIR}
-
-echo '#include "pcap.h"' | cpp -H -o /dev/null 2>&1
-if [ "$?" != "0" ]; then
- echo "Error: pcap is not installed. You may need to install libpcap-dev"
-fi
-
-git -c advice.detachedHead=false clone -q --depth=1 --single-branch --branch=v17.02 http://dpdk.org/git/dpdk dpdk
-pushd dpdk
-git log --oneline --decorate
-
-#Make and edit DPDK configuration
-make config T=${TARGET} O=${TARGET}
-pushd ${TARGET}
-#To use I/O without DPDK supported NIC's enable pcap pmd:
-sed -ri 's,(CONFIG_RTE_LIBRTE_PMD_PCAP=).*,\1y,' .config
-popd
-
-#Build DPDK
-make install T=${TARGET} EXTRA_CFLAGS="-fPIC"
-popd
-
-#Build ODP
-./bootstrap;
-./configure --enable-test-vald --enable-test-perf --enable-test-cpp \
- --enable-debug --enable-debug-print \
- --with-dpdk-path=`pwd`/dpdk/${TARGET}
-make
diff --git a/scripts/check-globals.sh b/scripts/check-globals.sh
new file mode 100755
index 000000000..d198c50c8
--- /dev/null
+++ b/scripts/check-globals.sh
@@ -0,0 +1,37 @@
+#!/bin/bash -e
+#
+# Check that global symbols in a static library conform to a given regex.
+# Only static library is checked, since libtool -export-symbols-regex
+# takes care of dynamic libraries.
+#
+# Required variables:
+# LIBTOOL Path to libtool.
+# NM Path to nm.
+# LIB Library directory.
+# lib_LTLIBRARIES Library .la file.
+# CHECK_GLOBALS_REGEX Global symbols matching this regex are accepted.
+#
+
+tmpfile=$(mktemp)
+
+# get $objdir
+$LIBTOOL --config > $tmpfile
+. $tmpfile
+
+# get $old_library (static library name)
+. $lib_LTLIBRARIES
+
+echo "$old_library: Checking global symbols, regex: $CHECK_GLOBALS_REGEX"
+
+# get a list of symbols that are global, are not undefined or weak, and
+# do not match the regex
+$NM -g --defined-only $LIB/$objdir/$old_library | \
+ egrep " [uA-T] " | egrep -v "$CHECK_GLOBALS_REGEX" | tee $tmpfile
+
+num=$(cat $tmpfile | wc -l)
+rm -f $tmpfile
+
+if [ "$num" != "0" ]; then
+ echo "$old_library: ($num non-matching symbols)"
+ false
+fi
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 16316b928..71af1c682 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1,14 +1,19 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
# (c) 2001, Dave Jones. (the file handling bit)
# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
-# Licensed under the terms of the GNU GPL License version 2
+# (c) 2010-2018 Joe Perches <joe@perches.com>
use strict;
+use warnings;
use POSIX;
use File::Basename;
use Cwd 'abs_path';
+use Term::ANSIColor qw(:constants);
+use Encode qw(decode encode);
my $P = $0;
my $D = dirname(abs_path($P));
@@ -18,22 +23,31 @@ my $V = '0.32';
use Getopt::Long qw(:config no_auto_abbrev);
my $quiet = 0;
+my $verbose = 0;
+my %verbose_messages = ();
+my %verbose_emitted = ();
my $tree = 1;
my $chk_signoff = 1;
my $chk_patch = 1;
my $tst_only;
my $emacs = 0;
my $terse = 0;
+my $showfile = 0;
my $file = 0;
+my $git = 0;
+my %git_commits = ();
my $check = 0;
my $check_orig = 0;
my $summary = 1;
my $mailback = 0;
my $summary_file = 0;
my $show_types = 0;
+my $list_types = 0;
my $fix = 0;
my $fix_inplace = 0;
my $root;
+my $gitroot = $ENV{'GIT_DIR'};
+$gitroot = ".git" if !defined($gitroot);
my %debug;
my %camelcase = ();
my %use_type = ();
@@ -42,13 +56,25 @@ my %ignore_type = ();
my @ignore = ();
my $help = 0;
my $configuration_file = ".checkpatch.conf";
-my $max_line_length = 80;
+my $max_line_length = 100;
my $ignore_perl_version = 0;
my $minimum_perl_version = 5.10.0;
my $min_conf_desc_length = 4;
my $spelling_file = "$D/spelling.txt";
my $codespell = 0;
-my $codespellfile = "/usr/local/share/codespell/dictionary.txt";
+my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $user_codespellfile = "";
+my $conststructsfile = "$D/const_structs.checkpatch";
+my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst";
+my $typedefsfile;
+my $color = "auto";
+my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
+# git output parsing needs US English output, so first set backtick child process LANGUAGE
+my $git_command ='export LANGUAGE=en_US.UTF-8; git';
+my $tabsize = 8;
+my ${CONFIG_} = "CONFIG_";
+
+my %maybe_linker_symbol; # for externs in c exceptions, when seen in *vmlinux.lds.h
sub help {
my ($exitcode) = @_;
@@ -59,18 +85,34 @@ Version: $V
Options:
-q, --quiet quiet
+ -v, --verbose verbose mode
--no-tree run without a kernel tree
--no-signoff do not check for 'Signed-off-by' line
--patch treat FILE as patchfile (default)
--emacs emacs compile window format
--terse one line per report
+ --showfile emit diffed file position, not input file position
+ -g, --git treat FILE as a single commit or git revision range
+ single git commit with:
+ <rev>
+ <rev>^
+ <rev>~n
+ multiple git commits with:
+ <rev1>..<rev2>
+ <rev1>...<rev2>
+ <rev>-<count>
+ git merges are ignored
-f, --file treat FILE as regular source file
--subjective, --strict enable more subjective tests
+ --list-types list the possible message types
--types TYPE(,TYPE2...) show only these comma separated message types
--ignore TYPE(,TYPE2...) ignore various comma separated message types
- --max-line-length=n set the maximum line length, if exceeded, warn
+ --show-types show the specific message type in the output
+ --max-line-length=n set the maximum line length, (default $max_line_length)
+ if exceeded, warn on patches
+ requires --strict for use with --file
--min-conf-desc-length=n set the min description length, if shorter, warn
- --show-types show the message "types" in the output
+ --tab-size=n set the number of spaces for tab (default $tabsize)
--root=PATH PATH to the kernel tree root
--no-summary suppress the per-file summary
--mailback only produce a report in case of warnings/errors
@@ -91,8 +133,13 @@ Options:
--ignore-perl-version override checking of perl version. expect
runtime errors.
--codespell Use the codespell dictionary for spelling/typos
- (default:/usr/local/share/codespell/dictionary.txt)
+ (default:$codespellfile)
--codespellfile Use this codespell dictionary
+ --typedefsfile Read additional types from this file
+ --color[=WHEN] Use colors 'always', 'never', or only when output
+ is a terminal ('auto'). Default is 'auto'.
+ --kconfig-prefix=WORD use WORD as a prefix for Kconfig symbols (default
+ ${CONFIG_})
-h, --help, --version display this help and exit
When FILE is - read standard input.
@@ -101,6 +148,74 @@ EOM
exit($exitcode);
}
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+sub list_types {
+ my ($exitcode) = @_;
+
+ my $count = 0;
+
+ local $/ = undef;
+
+ open(my $script, '<', abs_path($P)) or
+ die "$P: Can't read '$P' $!\n";
+
+ my $text = <$script>;
+ close($script);
+
+ my %types = ();
+ # Also catch when type or level is passed through a variable
+ while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
+ if (defined($1)) {
+ if (exists($types{$2})) {
+ $types{$2} .= ",$1" if ($types{$2} ne $1);
+ } else {
+ $types{$2} = $1;
+ }
+ } else {
+ $types{$2} = "UNDETERMINED";
+ }
+ }
+
+ print("#\tMessage type\n\n");
+ if ($color) {
+ print(" ( Color coding: ");
+ print(RED . "ERROR" . RESET);
+ print(" | ");
+ print(YELLOW . "WARNING" . RESET);
+ print(" | ");
+ print(GREEN . "CHECK" . RESET);
+ print(" | ");
+ print("Multiple levels / Undetermined");
+ print(" )\n\n");
+ }
+
+ foreach my $type (sort keys %types) {
+ my $orig_type = $type;
+ if ($color) {
+ my $level = $types{$type};
+ if ($level eq "ERROR") {
+ $type = RED . $type . RESET;
+ } elsif ($level eq "WARN") {
+ $type = YELLOW . $type . RESET;
+ } elsif ($level eq "CHK") {
+ $type = GREEN . $type . RESET;
+ }
+ }
+ print(++$count . "\t" . $type . "\n");
+ if ($verbose && exists($verbose_messages{$orig_type})) {
+ my $message = $verbose_messages{$orig_type};
+ $message =~ s/\n/\n\t/g;
+ print("\t" . $message . "\n\n");
+ }
+ }
+
+ exit($exitcode);
+}
+
my $conf = which_conf($configuration_file);
if (-f $conf) {
my @conf_args;
@@ -127,21 +242,74 @@ if (-f $conf) {
unshift(@ARGV, @conf_args) if @conf_args;
}
+sub load_docs {
+ open(my $docs, '<', "$docsfile")
+ or warn "$P: Can't read the documentation file $docsfile $!\n";
+
+ my $type = '';
+ my $desc = '';
+ my $in_desc = 0;
+
+ while (<$docs>) {
+ chomp;
+ my $line = $_;
+ $line =~ s/\s+$//;
+
+ if ($line =~ /^\s*\*\*(.+)\*\*$/) {
+ if ($desc ne '') {
+ $verbose_messages{$type} = trim($desc);
+ }
+ $type = $1;
+ $desc = '';
+ $in_desc = 1;
+ } elsif ($in_desc) {
+ if ($line =~ /^(?:\s{4,}|$)/) {
+ $line =~ s/^\s{4}//;
+ $desc .= $line;
+ $desc .= "\n";
+ } else {
+ $verbose_messages{$type} = trim($desc);
+ $type = '';
+ $desc = '';
+ $in_desc = 0;
+ }
+ }
+ }
+
+ if ($desc ne '') {
+ $verbose_messages{$type} = trim($desc);
+ }
+ close($docs);
+}
+
+# Perl's Getopt::Long allows options to take optional arguments after a space.
+# Prevent --color by itself from consuming other arguments
+foreach (@ARGV) {
+ if ($_ eq "--color" || $_ eq "-color") {
+ $_ = "--color=$color";
+ }
+}
+
GetOptions(
'q|quiet+' => \$quiet,
+ 'v|verbose!' => \$verbose,
'tree!' => \$tree,
'signoff!' => \$chk_signoff,
'patch!' => \$chk_patch,
'emacs!' => \$emacs,
'terse!' => \$terse,
+ 'showfile!' => \$showfile,
'f|file!' => \$file,
+ 'g|git!' => \$git,
'subjective!' => \$check,
'strict!' => \$check,
'ignore=s' => \@ignore,
'types=s' => \@use,
'show-types!' => \$show_types,
+ 'list-types!' => \$list_types,
'max-line-length=i' => \$max_line_length,
'min-conf-desc-length=i' => \$min_conf_desc_length,
+ 'tab-size=i' => \$tabsize,
'root=s' => \$root,
'summary!' => \$summary,
'mailback!' => \$mailback,
@@ -152,30 +320,79 @@ GetOptions(
'debug=s' => \%debug,
'test-only=s' => \$tst_only,
'codespell!' => \$codespell,
- 'codespellfile=s' => \$codespellfile,
+ 'codespellfile=s' => \$user_codespellfile,
+ 'typedefsfile=s' => \$typedefsfile,
+ 'color=s' => \$color,
+ 'no-color' => \$color, #keep old behaviors of -nocolor
+ 'nocolor' => \$color, #keep old behaviors of -nocolor
+ 'kconfig-prefix=s' => \${CONFIG_},
'h|help' => \$help,
'version' => \$help
-) or help(1);
+) or $help = 2;
+
+if ($user_codespellfile) {
+ # Use the user provided codespell file unconditionally
+ $codespellfile = $user_codespellfile;
+} elsif (!(-f $codespellfile)) {
+ # If /usr/share/codespell/dictionary.txt is not present, try to find it
+ # under codespell's install directory: <codespell_root>/data/dictionary.txt
+ if (($codespell || $help) && which("python3") ne "") {
+ my $python_codespell_dict = << "EOF";
+
+import os.path as op
+import codespell_lib
+codespell_dir = op.dirname(codespell_lib.__file__)
+codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt')
+print(codespell_file, end='')
+EOF
+
+ my $codespell_dict = `python3 -c "$python_codespell_dict" 2> /dev/null`;
+ $codespellfile = $codespell_dict if (-f $codespell_dict);
+ }
+}
+
+# $help is 1 if either -h, --help or --version is passed as option - exitcode: 0
+# $help is 2 if invalid option is passed - exitcode: 1
+help($help - 1) if ($help);
+
+die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix));
+die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse);
+
+if ($color =~ /^[01]$/) {
+ $color = !$color;
+} elsif ($color =~ /^always$/i) {
+ $color = 1;
+} elsif ($color =~ /^never$/i) {
+ $color = 0;
+} elsif ($color =~ /^auto$/i) {
+ $color = (-t STDOUT);
+} else {
+ die "$P: Invalid color mode: $color\n";
+}
-help(0) if ($help);
+load_docs() if ($verbose);
+list_types(0) if ($list_types);
$fix = 1 if ($fix_inplace);
$check_orig = $check;
my $exit = 0;
+my $perl_version_ok = 1;
if ($^V && $^V lt $minimum_perl_version) {
+ $perl_version_ok = 0;
printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
- if (!$ignore_perl_version) {
- exit(1);
- }
+ exit(1) if (!$ignore_perl_version);
}
+#if no filenames are given, push '-' to read patch from stdin
if ($#ARGV < 0) {
- print "$P: no input files\n";
- exit(1);
+ push(@ARGV, '-');
}
+# skip TAB size 1 to avoid additional checks on $tabsize - 1
+die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2);
+
sub hash_save_array_words {
my ($hashRef, $arrayRef) = @_;
@@ -196,12 +413,12 @@ sub hash_save_array_words {
sub hash_show_words {
my ($hashRef, $prefix) = @_;
- if ($quiet == 0 && keys %$hashRef) {
- print "NOTE: $prefix message types:";
+ if (keys %$hashRef) {
+ print "\nNOTE: $prefix message types:";
foreach my $word (sort keys %$hashRef) {
print " $word";
}
- print "\n\n";
+ print "\n";
}
}
@@ -258,10 +475,12 @@ our $Sparse = qr{
__force|
__iomem|
__must_check|
- __init_refok|
__kprobes|
__ref|
- __rcu
+ __refconst|
+ __refdata|
+ __rcu|
+ __private
}x;
our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
@@ -273,10 +492,11 @@ our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeIni
# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
our $Attribute = qr{
const|
+ volatile|
__percpu|
__nocast|
__safe|
- __bitwise__|
+ __bitwise|
__packed__|
__packed2__|
__naked|
@@ -289,12 +509,15 @@ our $Attribute = qr{
__noclone|
__deprecated|
__read_mostly|
+ __ro_after_init|
__kprobes|
$InitAttribute|
+ __aligned\s*\(.*\)|
____cacheline_aligned|
____cacheline_aligned_in_smp|
____cacheline_internodealigned_in_smp|
- __weak
+ __weak|
+ __alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\)
}x;
our $Modifier;
our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__};
@@ -306,7 +529,7 @@ our $Binary = qr{(?i)0b[01]+$Int_type?};
our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?};
our $Int = qr{[0-9]+$Int_type?};
our $Octal = qr{0[0-7]+$Int_type?};
-our $String = qr{"[X\t]*"};
+our $String = qr{(?:\b[Lu])?"[X\t]*"};
our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?};
@@ -347,31 +570,50 @@ our $UTF8 = qr{
| $NON_ASCII_UTF8
}x;
+our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
our $typeOtherOSTypedefs = qr{(?x:
u_(?:char|short|int|long) | # bsd
u(?:nchar|short|int|long) # sysv
)};
-
-our $typeTypedefs = qr{(?x:
+our $typeKernelTypedefs = qr{(?x:
(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
atomic_t
)};
+our $typeStdioTypedefs = qr{(?x:
+ FILE
+)};
+our $typeTypedefs = qr{(?x:
+ $typeC99Typedefs\b|
+ $typeOtherOSTypedefs\b|
+ $typeKernelTypedefs\b|
+ $typeStdioTypedefs\b
+)};
+
+our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};
our $logFunctions = qr{(?x:
- printk(?:_ratelimited|_once|)|
+ printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
(?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
+ TP_printk|
WARN(?:_RATELIMIT|_ONCE|)|
panic|
MODULE_[A-Z_]+|
- seq_vprintf|seq_printf|seq_puts|
- ODP_ASSERT|ODP_DBG|ODP_ERR|ODP_ABORT|ODP_LOG|ODP_PRINT|
- EXAMPLE_DBG|EXAMPLE_ERR|EXAMPLE_ABORT|
- LOG_DBG|LOG_ERR|LOG_ABORT|
- printf
+ seq_vprintf|seq_printf|seq_puts
+)};
+
+our $allocFunctions = qr{(?x:
+ (?:(?:devm_)?
+ (?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? |
+ kstrdup(?:_const)? |
+ kmemdup(?:_nul)?) |
+ (?:\w+)?alloc_skb(?:_ip_align)? |
+ # dev_alloc_skb/netdev_alloc_skb, et al
+ dma_alloc_coherent
)};
our $signature_tags = qr{(?xi:
Signed-off-by:|
+ Co-developed-by:|
Acked-by:|
Tested-by:|
Reviewed-by:|
@@ -381,6 +623,115 @@ our $signature_tags = qr{(?xi:
Cc:
)};
+our @link_tags = qw(Link Closes);
+
+#Create a search and print patterns for all these strings to be used directly below
+our $link_tags_search = "";
+our $link_tags_print = "";
+foreach my $entry (@link_tags) {
+ if ($link_tags_search ne "") {
+ $link_tags_search .= '|';
+ $link_tags_print .= ' or ';
+ }
+ $entry .= ':';
+ $link_tags_search .= $entry;
+ $link_tags_print .= "'$entry'";
+}
+$link_tags_search = "(?:${link_tags_search})";
+
+our $tracing_logging_tags = qr{(?xi:
+ [=-]*> |
+ <[=-]* |
+ \[ |
+ \] |
+ start |
+ called |
+ entered |
+ entry |
+ enter |
+ in |
+ inside |
+ here |
+ begin |
+ exit |
+ end |
+ done |
+ leave |
+ completed |
+ out |
+ return |
+ [\.\!:\s]*
+)};
+
+sub edit_distance_min {
+ my (@arr) = @_;
+ my $len = scalar @arr;
+ if ((scalar @arr) < 1) {
+ # if underflow, return
+ return;
+ }
+ my $min = $arr[0];
+ for my $i (0 .. ($len-1)) {
+ if ($arr[$i] < $min) {
+ $min = $arr[$i];
+ }
+ }
+ return $min;
+}
+
+sub get_edit_distance {
+ my ($str1, $str2) = @_;
+ $str1 = lc($str1);
+ $str2 = lc($str2);
+ $str1 =~ s/-//g;
+ $str2 =~ s/-//g;
+ my $len1 = length($str1);
+ my $len2 = length($str2);
+ # two dimensional array storing minimum edit distance
+ my @distance;
+ for my $i (0 .. $len1) {
+ for my $j (0 .. $len2) {
+ if ($i == 0) {
+ $distance[$i][$j] = $j;
+ } elsif ($j == 0) {
+ $distance[$i][$j] = $i;
+ } elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) {
+ $distance[$i][$j] = $distance[$i - 1][$j - 1];
+ } else {
+ my $dist1 = $distance[$i][$j - 1]; #insert distance
+ my $dist2 = $distance[$i - 1][$j]; # remove
+ my $dist3 = $distance[$i - 1][$j - 1]; #replace
+ $distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3);
+ }
+ }
+ }
+ return $distance[$len1][$len2];
+}
+
+sub find_standard_signature {
+ my ($sign_off) = @_;
+ my @standard_signature_tags = (
+ 'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:',
+ 'Reviewed-by:', 'Reported-by:', 'Suggested-by:'
+ );
+ foreach my $signature (@standard_signature_tags) {
+ return $signature if (get_edit_distance($sign_off, $signature) <= 2);
+ }
+
+ return "";
+}
+
+our $obsolete_archives = qr{(?xi:
+ \Qfreedesktop.org/archives/dri-devel\E |
+ \Qlists.infradead.org\E |
+ \Qlkml.org\E |
+ \Qmail-archive.com\E |
+ \Qmailman.alsa-project.org/pipermail\E |
+ \Qmarc.info\E |
+ \Qozlabs.org/pipermail\E |
+ \Qspinics.net\E
+)};
+
our @typeListMisordered = (
qr{char\s+(?:un)?signed},
qr{int\s+(?:(?:un)?signed\s+)?short\s},
@@ -422,6 +773,29 @@ our @typeList = (
qr{${Ident}_handler_fn},
@typeListMisordered,
);
+
+our $C90_int_types = qr{(?x:
+ long\s+long\s+int\s+(?:un)?signed|
+ long\s+long\s+(?:un)?signed\s+int|
+ long\s+long\s+(?:un)?signed|
+ (?:(?:un)?signed\s+)?long\s+long\s+int|
+ (?:(?:un)?signed\s+)?long\s+long|
+ int\s+long\s+long\s+(?:un)?signed|
+ int\s+(?:(?:un)?signed\s+)?long\s+long|
+
+ long\s+int\s+(?:un)?signed|
+ long\s+(?:un)?signed\s+int|
+ long\s+(?:un)?signed|
+ (?:(?:un)?signed\s+)?long\s+int|
+ (?:(?:un)?signed\s+)?long|
+ int\s+long\s+(?:un)?signed|
+ int\s+(?:(?:un)?signed\s+)?long|
+
+ int\s+(?:un)?signed|
+ (?:(?:un)?signed\s+)?int
+)};
+
+our @typeListFile = ();
our @typeListWithAttr = (
@typeList,
qr{struct\s+$InitAttribute\s+$Ident},
@@ -431,6 +805,7 @@ our @typeListWithAttr = (
our @modifierList = (
qr{fastcall},
);
+our @modifierListFile = ();
our @mode_permission_funcs = (
["module_param", 3],
@@ -438,15 +813,47 @@ our @mode_permission_funcs = (
["module_param_array_named", 5],
["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
["proc_create(?:_data|)", 2],
- ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2],
+ ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
+ ["IIO_DEV_ATTR_[A-Z_]+", 1],
+ ["SENSOR_(?:DEVICE_|)ATTR_2", 2],
+ ["SENSOR_TEMPLATE(?:_2|)", 3],
+ ["__ATTR", 2],
);
+my $word_pattern = '\b[A-Z]?[a-z]{2,}\b';
+
#Create a search pattern for all these functions to speed up a loop below
our $mode_perms_search = "";
foreach my $entry (@mode_permission_funcs) {
$mode_perms_search .= '|' if ($mode_perms_search ne "");
$mode_perms_search .= $entry->[0];
}
+$mode_perms_search = "(?:${mode_perms_search})";
+
+our %deprecated_apis = (
+ "synchronize_rcu_bh" => "synchronize_rcu",
+ "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited",
+ "call_rcu_bh" => "call_rcu",
+ "rcu_barrier_bh" => "rcu_barrier",
+ "synchronize_sched" => "synchronize_rcu",
+ "synchronize_sched_expedited" => "synchronize_rcu_expedited",
+ "call_rcu_sched" => "call_rcu",
+ "rcu_barrier_sched" => "rcu_barrier",
+ "get_state_synchronize_sched" => "get_state_synchronize_rcu",
+ "cond_synchronize_sched" => "cond_synchronize_rcu",
+ "kmap" => "kmap_local_page",
+ "kunmap" => "kunmap_local",
+ "kmap_atomic" => "kmap_local_page",
+ "kunmap_atomic" => "kunmap_local",
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $deprecated_apis_search = "";
+foreach my $entry (keys %deprecated_apis) {
+ $deprecated_apis_search .= '|' if ($deprecated_apis_search ne "");
+ $deprecated_apis_search .= $entry;
+}
+$deprecated_apis_search = "(?:${deprecated_apis_search})";
our $mode_perms_world_writable = qr{
S_IWUGO |
@@ -456,6 +863,63 @@ our $mode_perms_world_writable = qr{
0[0-7][0-7][2367]
}x;
+our %mode_permission_string_types = (
+ "S_IRWXU" => 0700,
+ "S_IRUSR" => 0400,
+ "S_IWUSR" => 0200,
+ "S_IXUSR" => 0100,
+ "S_IRWXG" => 0070,
+ "S_IRGRP" => 0040,
+ "S_IWGRP" => 0020,
+ "S_IXGRP" => 0010,
+ "S_IRWXO" => 0007,
+ "S_IROTH" => 0004,
+ "S_IWOTH" => 0002,
+ "S_IXOTH" => 0001,
+ "S_IRWXUGO" => 0777,
+ "S_IRUGO" => 0444,
+ "S_IWUGO" => 0222,
+ "S_IXUGO" => 0111,
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $mode_perms_string_search = "";
+foreach my $entry (keys %mode_permission_string_types) {
+ $mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
+ $mode_perms_string_search .= $entry;
+}
+our $single_mode_perms_string_search = "(?:${mode_perms_string_search})";
+our $multi_mode_perms_string_search = qr{
+ ${single_mode_perms_string_search}
+ (?:\s*\|\s*${single_mode_perms_string_search})*
+}x;
+
+sub perms_to_octal {
+ my ($string) = @_;
+
+ return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/);
+
+ my $val = "";
+ my $oval = "";
+ my $to = 0;
+ my $curpos = 0;
+ my $lastpos = 0;
+ while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
+ $curpos = pos($string);
+ my $match = $2;
+ my $omatch = $1;
+ last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
+ $lastpos = $curpos;
+ $to |= $mode_permission_string_types{$match};
+ $val .= '\s*\|\s*' if ($val ne "");
+ $val .= $match;
+ $oval .= $omatch;
+ }
+ $oval =~ s/^\s*\|\s*//;
+ $oval =~ s/\s*\|\s*$//;
+ return sprintf("%04o", $to);
+}
+
our $allowed_asm_includes = qr{(?x:
irq|
memory|
@@ -513,14 +977,53 @@ if ($codespell) {
$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
+sub read_words {
+ my ($wordsRef, $file) = @_;
+
+ if (open(my $words, '<', $file)) {
+ while (<$words>) {
+ my $line = $_;
+
+ $line =~ s/\s*\n?$//g;
+ $line =~ s/^\s*//g;
+
+ next if ($line =~ m/^\s*#/);
+ next if ($line =~ m/^\s*$/);
+ if ($line =~ /\s/) {
+ print("$file: '$line' invalid - ignored\n");
+ next;
+ }
+
+ $$wordsRef .= '|' if (defined $$wordsRef);
+ $$wordsRef .= $line;
+ }
+ close($file);
+ return 1;
+ }
+
+ return 0;
+}
+
+my $const_structs;
+if (show_type("CONST_STRUCT")) {
+ read_words(\$const_structs, $conststructsfile)
+ or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+}
+
+if (defined($typedefsfile)) {
+ my $typeOtherTypedefs;
+ read_words(\$typeOtherTypedefs, $typedefsfile)
+ or warn "No additional types will be considered - file '$typedefsfile': $!\n";
+ $typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs);
+}
+
sub build_types {
- my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)";
- my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)";
+ my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)";
+ my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)";
my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)";
my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)";
$Modifier = qr{(?:$Attribute|$Sparse|$mods)};
$BasicType = qr{
- (?:$typeOtherOSTypedefs\b)|
(?:$typeTypedefs\b)|
(?:${all}\b)
}x;
@@ -528,7 +1031,6 @@ sub build_types {
(?:$Modifier\s+|const\s+)*
(?:
(?:typeof|__typeof__)\s*\([^\)]*\)|
- (?:$typeOtherOSTypedefs\b)|
(?:$typeTypedefs\b)|
(?:${all}\b)
)
@@ -546,19 +1048,18 @@ sub build_types {
(?:
(?:typeof|__typeof__)\s*\([^\)]*\)|
(?:$typeTypedefs\b)|
- (?:$typeOtherOSTypedefs\b)|
(?:${allWithAttr}\b)
)
(?:\s+$Modifier|\s+const)*
}x;
$Type = qr{
$NonptrType
- (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+ (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
(?:\s+$Inline|\s+$Modifier)*
}x;
$TypeMisordered = qr{
$NonptrTypeMisordered
- (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+ (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
(?:\s+$Inline|\s+$Modifier)*
}x;
$Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
@@ -577,11 +1078,19 @@ our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
our $declaration_macros = qr{(?x:
- (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,2}\s*\(|
- (?:$Storage\s+)?LIST_HEAD\s*\(|
- (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(
+ (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
+ (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
+ (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(|
+ (?:$Storage\s+)?(?:XA_STATE|XA_STATE_ORDER)\s*\(
)};
+our %allow_repeated_words = (
+ add => '',
+ added => '',
+ bad => '',
+ be => '',
+);
+
sub deparenthesize {
my ($string) = @_;
return "" if (!defined($string));
@@ -622,6 +1131,31 @@ sub seed_camelcase_file {
}
}
+our %maintained_status = ();
+
+sub is_maintained_obsolete {
+ my ($filename) = @_;
+
+ return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
+
+ if (!exists($maintained_status{$filename})) {
+ $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+ }
+
+ return $maintained_status{$filename} =~ /obsolete/i;
+}
+
+sub is_SPDX_License_valid {
+ my ($license) = @_;
+
+ return 1 if (!$tree || which("python3") eq "" || !(-x "$root/scripts/spdxcheck.py") || !(-e "$gitroot"));
+
+ my $root_path = abs_path($root);
+ my $status = `cd "$root_path"; echo "$license" | scripts/spdxcheck.py -`;
+ return 0 if ($status ne "");
+ return 1;
+}
+
my $camelcase_seeded = 0;
sub seed_camelcase_includes {
return if ($camelcase_seeded);
@@ -632,8 +1166,8 @@ sub seed_camelcase_includes {
$camelcase_seeded = 1;
- if (-e ".git") {
- my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
+ if (-e "$gitroot") {
+ my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`;
chomp $git_last_include_commit;
$camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
} else {
@@ -660,8 +1194,8 @@ sub seed_camelcase_includes {
return;
}
- if (-e ".git") {
- $files = `git ls-files "include/*.h"`;
+ if (-e "$gitroot") {
+ $files = `${git_command} ls-files "include/*.h"`;
@include_files = split('\n', $files);
}
@@ -680,18 +1214,28 @@ sub seed_camelcase_includes {
}
}
+sub git_is_single_file {
+ my ($filename) = @_;
+
+ return 0 if ((which("git") eq "") || !(-e "$gitroot"));
+
+ my $output = `${git_command} ls-files -- $filename 2>/dev/null`;
+ my $count = $output =~ tr/\n//;
+ return $count eq 1 && $output =~ m{^${filename}$};
+}
+
sub git_commit_info {
my ($commit, $id, $desc) = @_;
- return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));
+ return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot"));
- my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`;
+ my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`;
$output =~ s/^\s*//gm;
my @lines = split("\n", $output);
return ($id, $desc) if ($#lines < 0);
- if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) {
+ if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) {
# Maybe one day convert this block of bash into something that returns
# all matching commit ids, but it's very slow...
#
@@ -701,7 +1245,9 @@ sub git_commit_info {
# git log --format='%H %s' -1 $line |
# echo "commit $(cut -c 1-12,41-)"
# done
- } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) {
+ } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./ ||
+ $lines[0] =~ /^fatal: bad object $commit/) {
+ $id = undef;
} else {
$id = substr($lines[0], 0, 12);
$desc = substr($lines[0], 41);
@@ -719,10 +1265,46 @@ my @fixed_inserted = ();
my @fixed_deleted = ();
my $fixlinenr = -1;
+# If input is git commits, extract all commits from the commit expressions.
+# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
+die "$P: No git repository found\n" if ($git && !-e "$gitroot");
+
+if ($git) {
+ my @commits = ();
+ foreach my $commit_expr (@ARGV) {
+ my $git_range;
+ if ($commit_expr =~ m/^(.*)-(\d+)$/) {
+ $git_range = "-$2 $1";
+ } elsif ($commit_expr =~ m/\.\./) {
+ $git_range = "$commit_expr";
+ } else {
+ $git_range = "-1 $commit_expr";
+ }
+ my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+ foreach my $line (split(/\n/, $lines)) {
+ $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
+ next if (!defined($1) || !defined($2));
+ my $sha1 = $1;
+ my $subject = $2;
+ unshift(@commits, $sha1);
+ $git_commits{$sha1} = $subject;
+ }
+ }
+ die "$P: no git commits after extraction!\n" if (@commits == 0);
+ @ARGV = @commits;
+}
+
my $vname;
+$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"};
for my $filename (@ARGV) {
my $FILE;
- if ($file) {
+ my $is_git_file = git_is_single_file($filename);
+ my $oldfile = $file;
+ $file = 1 if ($is_git_file);
+ if ($git) {
+ open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
+ die "$P: $filename: git format-patch failed - $!\n";
+ } elsif ($file) {
open($FILE, '-|', "diff -u /dev/null $filename") ||
die "$P: $filename: diff failed - $!\n";
} elsif ($filename eq '-') {
@@ -733,14 +1315,24 @@ for my $filename (@ARGV) {
}
if ($filename eq '-') {
$vname = 'Your patch';
+ } elsif ($git) {
+ $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
} else {
$vname = $filename;
}
while (<$FILE>) {
chomp;
push(@rawlines, $_);
+ $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i);
}
close($FILE);
+
+ if ($#ARGV > 0 && $quiet == 0) {
+ print '-' x length($vname) . "\n";
+ print "$vname\n";
+ print '-' x length($vname) . "\n";
+ }
+
if (!process($filename)) {
$exit = 1;
}
@@ -750,6 +1342,30 @@ for my $filename (@ARGV) {
@fixed_inserted = ();
@fixed_deleted = ();
$fixlinenr = -1;
+ @modifierListFile = ();
+ @typeListFile = ();
+ build_types();
+ $file = $oldfile if ($is_git_file);
+}
+
+if (!$quiet) {
+ hash_show_words(\%use_type, "Used");
+ hash_show_words(\%ignore_type, "Ignored");
+
+ if (!$perl_version_ok) {
+ print << "EOM"
+
+NOTE: perl $^V is not modern enough to detect all possible issues.
+ An upgrade to at least perl $minimum_perl_version is suggested.
+EOM
+ }
+ if ($exit) {
+ print << "EOM"
+
+NOTE: If any of the errors are false positives, please report
+ them to the maintainer, see CHECKPATCH in MAINTAINERS.
+EOM
+ }
}
exit($exit);
@@ -775,6 +1391,8 @@ sub parse_email {
my ($formatted_email) = @_;
my $name = "";
+ my $quoted = "";
+ my $name_comment = "";
my $address = "";
my $comment = "";
@@ -788,7 +1406,7 @@ sub parse_email {
} elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
$address = $1;
$comment = $2 if defined $2;
- $formatted_email =~ s/$address.*$//;
+ $formatted_email =~ s/\Q$address\E.*$//;
$name = $formatted_email;
$name = trim($name);
$name =~ s/^\"|\"$//g;
@@ -805,42 +1423,76 @@ sub parse_email {
}
}
- $name = trim($name);
- $name =~ s/^\"|\"$//g;
+ # Extract comments from names excluding quoted parts
+ # "John D. (Doe)" - Do not extract
+ if ($name =~ s/\"(.+)\"//) {
+ $quoted = $1;
+ }
+ while ($name =~ s/\s*($balanced_parens)\s*/ /) {
+ $name_comment .= trim($1);
+ }
+ $name =~ s/^[ \"]+|[ \"]+$//g;
+ $name = trim("$quoted $name");
+
$address = trim($address);
$address =~ s/^\<|\>$//g;
+ $comment = trim($comment);
if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
$name = "\"$name\"";
}
- return ($name, $address, $comment);
+ return ($name, $name_comment, $address, $comment);
}
sub format_email {
- my ($name, $address) = @_;
+ my ($name, $name_comment, $address, $comment) = @_;
my $formatted_email;
- $name = trim($name);
- $name =~ s/^\"|\"$//g;
+ $name =~ s/^[ \"]+|[ \"]+$//g;
$address = trim($address);
+ $address =~ s/(?:\.|\,|\")+$//; ##trailing commas, dots or quotes
if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
$name = "\"$name\"";
}
+ $name_comment = trim($name_comment);
+ $name_comment = " $name_comment" if ($name_comment ne "");
+ $comment = trim($comment);
+ $comment = " $comment" if ($comment ne "");
+
if ("$name" eq "") {
$formatted_email = "$address";
} else {
- $formatted_email = "$name <$address>";
+ $formatted_email = "$name$name_comment <$address>";
}
-
+ $formatted_email .= "$comment";
return $formatted_email;
}
+sub reformat_email {
+ my ($email) = @_;
+
+ my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+ return format_email($email_name, $name_comment, $email_address, $comment);
+}
+
+sub same_email_addresses {
+ my ($email1, $email2) = @_;
+
+ my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1);
+ my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2);
+
+ return $email1_name eq $email2_name &&
+ $email1_address eq $email2_address &&
+ $name1_comment eq $name2_comment &&
+ $comment1 eq $comment2;
+}
+
sub which {
my ($bin) = @_;
@@ -874,7 +1526,7 @@ sub expand_tabs {
if ($c eq "\t") {
$res .= ' ';
$n++;
- for (; ($n % 8) != 0; $n++) {
+ for (; ($n % $tabsize) != 0; $n++) {
$res .= ' ';
}
next;
@@ -930,7 +1582,7 @@ sub sanitise_line {
for ($off = 1; $off < length($line); $off++) {
$c = substr($line, $off, 1);
- # Comments we are wacking completly including the begin
+ # Comments we are whacking completely including the begin
# and end, all to $;.
if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
$sanitise_quote = '*/';
@@ -999,13 +1651,19 @@ sub sanitise_line {
$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
}
+ if ($allow_c99_comments && $res =~ m@(//.*$)@) {
+ my $match = $1;
+ $res =~ s/\Q$match\E/"$;" x length($match)/e;
+ }
+
return $res;
}
sub get_quoted_string {
my ($line, $rawline) = @_;
- return "" if ($line !~ m/(\"[X\t]+\")/g);
+ return "" if (!defined($line) || !defined($rawline));
+ return "" if ($line !~ m/($String)/g);
return substr($rawline, $-[0], $+[0] - $-[0]);
}
@@ -1297,8 +1955,16 @@ sub ctx_statement_level {
sub ctx_locate_comment {
my ($first_line, $end_line) = @_;
+ # If c99 comment on the current line, or the line before or after
+ my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@);
+ return $current_comment if (defined $current_comment);
+ ($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@);
+ return $current_comment if (defined $current_comment);
+ ($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@);
+ return $current_comment if (defined $current_comment);
+
# Catch a comment on the end of the line itself.
- my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+ ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
return $current_comment if (defined $current_comment);
# Look through the context and try and figure out if there is a
@@ -1352,6 +2018,28 @@ sub raw_line {
return $line;
}
+sub get_stat_real {
+ my ($linenr, $lc) = @_;
+
+ my $stat_real = raw_line($linenr, 0);
+ for (my $count = $linenr + 1; $count <= $lc; $count++) {
+ $stat_real = $stat_real . "\n" . raw_line($count, 0);
+ }
+
+ return $stat_real;
+}
+
+sub get_stat_here {
+ my ($linenr, $cnt, $here) = @_;
+
+ my $herectx = $here . "\n";
+ for (my $n = 0; $n < $cnt; $n++) {
+ $herectx .= raw_line($linenr, $n) . "\n";
+ }
+
+ return $herectx;
+}
+
sub cat_vet {
my ($vet) = @_;
my ($res, $coded);
@@ -1614,13 +2302,13 @@ sub possible {
for my $modifier (split(' ', $possible)) {
if ($modifier !~ $notPermitted) {
warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
- push(@modifierList, $modifier);
+ push(@modifierListFile, $modifier);
}
}
} else {
warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
- push(@typeList, $possible);
+ push(@typeListFile, $possible);
}
build_types();
} else {
@@ -1633,6 +2321,8 @@ my $prefix = '';
sub show_type {
my ($type) = @_;
+ $type =~ tr/[a-z]/[A-Z]/;
+
return defined $use_type{$type} if (scalar keys %use_type > 0);
return !defined $ignore_type{$type};
@@ -1645,15 +2335,41 @@ sub report {
(defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
return 0;
}
- my $line;
+ my $output = '';
+ if ($color) {
+ if ($level eq 'ERROR') {
+ $output .= RED;
+ } elsif ($level eq 'WARNING') {
+ $output .= YELLOW;
+ } else {
+ $output .= GREEN;
+ }
+ }
+ $output .= $prefix . $level . ':';
if ($show_types) {
- $line = "$prefix$level:$type: $msg\n";
- } else {
- $line = "$prefix$level: $msg\n";
+ $output .= BLUE if ($color);
+ $output .= "$type:";
+ }
+ $output .= RESET if ($color);
+ $output .= ' ' . $msg . "\n";
+
+ if ($showfile) {
+ my @lines = split("\n", $output, -1);
+ splice(@lines, 1, 1);
+ $output = join("\n", @lines);
}
- $line = (split('\n', $line))[0] . "\n" if ($terse);
- push(our @report, $line);
+ if ($terse) {
+ $output = (split('\n', $output))[0] . "\n";
+ }
+
+ if ($verbose && exists($verbose_messages{$type}) &&
+ !exists($verbose_emitted{$type})) {
+ $output .= $verbose_messages{$type} . "\n\n";
+ $verbose_emitted{$type} = 1;
+ }
+
+ push(our @report, $output);
return 1;
}
@@ -1840,7 +2556,7 @@ sub string_find_replace {
sub tabify {
my ($leading) = @_;
- my $source_indent = 8;
+ my $source_indent = $tabsize;
my $max_spaces_before_tab = $source_indent - 1;
my $spaces_to_tab = " " x $source_indent;
@@ -1882,6 +2598,28 @@ sub pos_last_openparen {
return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
}
+sub get_raw_comment {
+ my ($line, $rawline) = @_;
+ my $comment = '';
+
+ for my $i (0 .. (length($line) - 1)) {
+ if (substr($line, $i, 1) eq "$;") {
+ $comment .= substr($rawline, $i, 1);
+ }
+ }
+
+ return $comment;
+}
+
+sub exclude_global_initialisers {
+ my ($realfile) = @_;
+
+ # Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c).
+ return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ ||
+ $realfile =~ m@^samples/bpf/.*_kern\.c$@ ||
+ $realfile =~ m@/bpf/.*\.bpf\.c$@;
+}
+
sub process {
my $filename = shift;
@@ -1898,14 +2636,24 @@ sub process {
our $clean = 1;
my $signoff = 0;
+ my $author = '';
+ my $authorsignoff = 0;
+ my $author_sob = '';
my $is_patch = 0;
-
+ my $is_binding_patch = -1;
my $in_header_lines = $file ? 0 : 1;
my $in_commit_log = 0; #Scanning lines before patch
+ my $has_patch_separator = 0; #Found a --- line
+ my $has_commit_log = 0; #Encountered lines before patch
+ my $commit_log_lines = 0; #Number of commit log lines
+ my $commit_log_possible_stack_dump = 0;
my $commit_log_long_line = 0;
- my $reported_maintainer_file = 1; # No MAINTAINTERS so silence warning
+ my $commit_log_has_diff = 0;
+ my $reported_maintainer_file = 1; # ODP has no MAINTAINERS file
my $non_utf8_charset = 0;
+ my $last_git_commit_id_linenr = -1;
+
my $last_blank_line = 0;
my $last_coalesced_string_linenr = -1;
@@ -1920,6 +2668,7 @@ sub process {
my $realline = 0;
my $realcnt = 0;
my $here = '';
+ my $context_function; #undef'd unless there's a known function
my $in_comment = 0;
my $comment_edge = 0;
my $first_line = 0;
@@ -1943,6 +2692,8 @@ sub process {
my $camelcase_file_seeded = 0;
+ my $checklicenseline = 1;
+
sanitise_line_reset();
my $line;
foreach my $rawline (@rawlines) {
@@ -1953,12 +2704,12 @@ sub process {
if ($rawline=~/^\+\+\+\s+(\S+)/) {
$setup_docs = 0;
- if ($1 =~ m@Documentation/kernel-parameters.txt$@) {
+ if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) {
$setup_docs = 1;
}
#next;
}
- if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+ if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
$realline=$1-1;
if (defined $2) {
$realcnt=$3+1;
@@ -2034,9 +2785,20 @@ sub process {
$sline =~ s/$;/ /g; #with comments as spaces
my $rawline = $rawlines[$linenr - 1];
+ my $raw_comment = get_raw_comment($line, $rawline);
+
+# check if it's a mode change, rename or start of a patch
+ if (!$in_commit_log &&
+ ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ ||
+ ($line =~ /^rename (?:from|to) \S+\s*$/ ||
+ $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) {
+ $is_patch = 1;
+ }
#extract the line range in the file after the patch is applied
- if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+ if (!$in_commit_log &&
+ $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
+ my $context = $4;
$is_patch = 1;
$first_line = $linenr + 1;
$realline=$1-1;
@@ -2052,6 +2814,11 @@ sub process {
%suppress_whiletrailers = ();
%suppress_export = ();
$suppress_statement = 0;
+ if ($context =~ /\b(\w+)\s*\(/) {
+ $context_function = $1;
+ } else {
+ undef $context_function;
+ }
next;
# track the line number as we move through the hunk, note that
@@ -2077,10 +2844,6 @@ sub process {
my $hunk_line = ($realcnt != 0);
-#make up the handle for any error we report on this line
- $prefix = "$filename:$realline: " if ($emacs && $file);
- $prefix = "$filename:$linenr: " if ($emacs && !$file);
-
$here = "#$linenr: " if (!$file);
$here = "#$realline: " if ($file);
@@ -2110,12 +2873,41 @@ sub process {
$found_file = 1;
}
+#make up the handle for any error we report on this line
+ if ($showfile) {
+ $prefix = "$realfile:$realline: "
+ } elsif ($emacs) {
+ if ($file) {
+ $prefix = "$filename:$realline: ";
+ } else {
+ $prefix = "$filename:$linenr: ";
+ }
+ }
+
if ($found_file) {
- if ($realfile =~ m@^(drivers/net/|net/)@) {
+ if (is_maintained_obsolete($realfile)) {
+ WARN("OBSOLETE",
+ "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n");
+ }
+ if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
$check = 1;
} else {
$check = $check_orig;
}
+ $checklicenseline = 1;
+
+ if ($realfile !~ /^MAINTAINERS/) {
+ my $last_binding_patch = $is_binding_patch;
+
+ $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@;
+
+ if (($last_binding_patch != -1) &&
+ ($last_binding_patch ^ $is_binding_patch)) {
+ WARN("DT_SPLIT_BINDING_PATCH",
+ "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n");
+ }
+ }
+
next;
}
@@ -2127,6 +2919,29 @@ sub process {
$cnt_lines++ if ($realcnt != 0);
+# Verify the existence of a commit log if appropriate
+# 2 is used because a $signature is counted in $commit_log_lines
+ if ($in_commit_log) {
+ if ($line !~ /^\s*$/) {
+ $commit_log_lines++; #could be a $signature
+ }
+ } elsif ($has_commit_log && $commit_log_lines < 2) {
+ WARN("COMMIT_MESSAGE",
+ "Missing commit description - Add an appropriate one\n");
+ $commit_log_lines = 2; #warn only once
+ }
+
+# Check if the commit log has what seems like a diff which can confuse patch
+ if ($in_commit_log && !$commit_log_has_diff &&
+ (($line =~ m@^\s+diff\b.*a/([\w/]+)@ &&
+ $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) ||
+ $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
+ $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
+ ERROR("DIFF_IN_COMMIT_MSG",
+ "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr);
+ $commit_log_has_diff = 1;
+ }
+
# Check for incorrect file permissions
if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
my $permhere = $here . "FILE: $realfile\n";
@@ -2137,10 +2952,61 @@ sub process {
}
}
+# Check the patch for a From:
+ if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) {
+ $author = $1;
+ my $curline = $linenr;
+ while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) {
+ $author .= $1;
+ }
+ $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i);
+ $author =~ s/"//g;
+ $author = reformat_email($author);
+ }
+
# Check the patch for a signoff:
- if ($line =~ /^\s*signed-off-by:/i) {
+ if ($line =~ /^\s*signed-off-by:\s*(.*)/i) {
$signoff++;
$in_commit_log = 0;
+ if ($author ne '' && $authorsignoff != 1) {
+ if (same_email_addresses($1, $author)) {
+ $authorsignoff = 1;
+ } else {
+ my $ctx = $1;
+ my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx);
+ my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author);
+
+ if (lc $email_address eq lc $author_address && $email_name eq $author_name) {
+ $author_sob = $ctx;
+ $authorsignoff = 2;
+ } elsif (lc $email_address eq lc $author_address) {
+ $author_sob = $ctx;
+ $authorsignoff = 3;
+ } elsif ($email_name eq $author_name) {
+ $author_sob = $ctx;
+ $authorsignoff = 4;
+
+ my $address1 = $email_address;
+ my $address2 = $author_address;
+
+ if ($address1 =~ /(\S+)\+\S+(\@.*)/) {
+ $address1 = "$1$2";
+ }
+ if ($address2 =~ /(\S+)\+\S+(\@.*)/) {
+ $address2 = "$1$2";
+ }
+ if ($address1 eq $address2) {
+ $authorsignoff = 5;
+ }
+ }
+ }
+ }
+ }
+
+# Check for patch separator
+ if ($line =~ /^---$/) {
+ $has_patch_separator = 1;
+ $in_commit_log = 0;
}
# Check if MAINTAINERS is being updated. If so, there's probably no need to
@@ -2159,8 +3025,17 @@ sub process {
my $ucfirst_sign_off = ucfirst(lc($sign_off));
if ($sign_off !~ /$signature_tags/) {
- WARN("BAD_SIGN_OFF",
- "Non-standard signature: $sign_off\n" . $herecurr);
+ my $suggested_signature = find_standard_signature($sign_off);
+ if ($suggested_signature eq "") {
+ WARN("BAD_SIGN_OFF",
+ "Non-standard signature: $sign_off\n" . $herecurr);
+ } else {
+ if (WARN("BAD_SIGN_OFF",
+ "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/;
+ }
+ }
}
if (defined $space_before && $space_before ne "") {
if (WARN("BAD_SIGN_OFF",
@@ -2188,8 +3063,8 @@ sub process {
}
}
- my ($email_name, $email_address, $comment) = parse_email($email);
- my $suggested_email = format_email(($email_name, $email_address));
+ my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+ my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
if ($suggested_email eq "") {
ERROR("BAD_SIGN_OFF",
"Unrecognized email address: '$email'\n" . $herecurr);
@@ -2199,11 +3074,77 @@ sub process {
$dequoted =~ s/" </ </;
# Don't force email to have quotes
# Allow just an angle bracketed address
- if ("$dequoted$comment" ne $email &&
- "<$email_address>$comment" ne $email &&
- "$suggested_email$comment" ne $email) {
+ if (!same_email_addresses($email, $suggested_email)) {
+ if (WARN("BAD_SIGN_OFF",
+ "email address '$email' might be better as '$suggested_email'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/;
+ }
+ }
+
+ # Address part shouldn't have comments
+ my $stripped_address = $email_address;
+ $stripped_address =~ s/\([^\(\)]*\)//g;
+ if ($email_address ne $stripped_address) {
+ if (WARN("BAD_SIGN_OFF",
+ "address part of email should not have comments: '$email_address'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/;
+ }
+ }
+
+ # Only one name comment should be allowed
+ my $comment_count = () = $name_comment =~ /\([^\)]+\)/g;
+ if ($comment_count > 1) {
WARN("BAD_SIGN_OFF",
- "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
+ "Use a single name comment in email: '$email'\n" . $herecurr);
+ }
+
+
+ # stable@vger.kernel.org or stable@kernel.org shouldn't
+ # have an email name. In addition comments should strictly
+ # begin with a #
+ if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) {
+ if (($comment ne "" && $comment !~ /^#.+/) ||
+ ($email_name ne "")) {
+ my $cur_name = $email_name;
+ my $new_comment = $comment;
+ $cur_name =~ s/[a-zA-Z\s\-\"]+//g;
+
+ # Remove brackets enclosing comment text
+ # and # from start of comments to get comment text
+ $new_comment =~ s/^\((.*)\)$/$1/;
+ $new_comment =~ s/^\[(.*)\]$/$1/;
+ $new_comment =~ s/^[\s\#]+|\s+$//g;
+
+ $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment);
+ $new_comment = " # $new_comment" if ($new_comment ne "");
+ my $new_email = "$email_address$new_comment";
+
+ if (WARN("BAD_STABLE_ADDRESS_STYLE",
+ "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+ }
+ }
+ } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) {
+ my $new_comment = $comment;
+
+ # Extract comment text from within brackets or
+ # c89 style /*...*/ comments
+ $new_comment =~ s/^\[(.*)\]$/$1/;
+ $new_comment =~ s/^\/\*(.*)\*\/$/$1/;
+
+ $new_comment = trim($new_comment);
+ $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo
+ $new_comment = "($new_comment)" if ($new_comment ne "");
+ my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment);
+
+ if (WARN("BAD_SIGN_OFF",
+ "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+ }
}
}
@@ -2217,6 +3158,80 @@ sub process {
} else {
$signatures{$sig_nospace} = 1;
}
+
+# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email
+ if ($sign_off =~ /^co-developed-by:$/i) {
+ if ($email eq $author) {
+ WARN("BAD_SIGN_OFF",
+ "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . $herecurr);
+ }
+ if (!defined $lines[$linenr]) {
+ WARN("BAD_SIGN_OFF",
+ "Co-developed-by: must be immediately followed by Signed-off-by:\n" . $herecurr);
+ } elsif ($rawlines[$linenr] !~ /^signed-off-by:\s*(.*)/i) {
+ WARN("BAD_SIGN_OFF",
+ "Co-developed-by: must be immediately followed by Signed-off-by:\n" . $herecurr . $rawlines[$linenr] . "\n");
+ } elsif ($1 ne $email) {
+ WARN("BAD_SIGN_OFF",
+ "Co-developed-by and Signed-off-by: name/email do not match\n" . $herecurr . $rawlines[$linenr] . "\n");
+ }
+ }
+
+# check if Reported-by: is followed by a Closes: tag
+ if ($sign_off =~ /^reported(?:|-and-tested)-by:$/i) {
+ if (!defined $lines[$linenr]) {
+ WARN("BAD_REPORTED_BY_LINK",
+ "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . "\n");
+ } elsif ($rawlines[$linenr] !~ /^closes:\s*/i) {
+ WARN("BAD_REPORTED_BY_LINK",
+ "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n");
+ }
+ }
+ }
+
+
+# Check Fixes: styles is correct
+ if (!$in_header_lines &&
+ $line =~ /^\s*fixes:?\s*(?:commit\s*)?[0-9a-f]{5,}\b/i) {
+ my $orig_commit = "";
+ my $id = "0123456789ab";
+ my $title = "commit title";
+ my $tag_case = 1;
+ my $tag_space = 1;
+ my $id_length = 1;
+ my $id_case = 1;
+ my $title_has_quotes = 0;
+
+ if ($line =~ /(\s*fixes:?)\s+([0-9a-f]{5,})\s+($balanced_parens)/i) {
+ my $tag = $1;
+ $orig_commit = $2;
+ $title = $3;
+
+ $tag_case = 0 if $tag eq "Fixes:";
+ $tag_space = 0 if ($line =~ /^fixes:? [0-9a-f]{5,} ($balanced_parens)/i);
+
+ $id_length = 0 if ($orig_commit =~ /^[0-9a-f]{12}$/i);
+ $id_case = 0 if ($orig_commit !~ /[A-F]/);
+
+ # Always strip leading/trailing parens then double quotes if existing
+ $title = substr($title, 1, -1);
+ if ($title =~ /^".*"$/) {
+ $title = substr($title, 1, -1);
+ $title_has_quotes = 1;
+ }
+ }
+
+ my ($cid, $ctitle) = git_commit_info($orig_commit, $id,
+ $title);
+
+ if ($ctitle ne $title || $tag_case || $tag_space ||
+ $id_length || $id_case || !$title_has_quotes) {
+ if (WARN("BAD_FIXES_TAG",
+ "Please use correct Fixes: style 'Fixes: <12 chars of sha1> (\"<title line>\")' - ie: 'Fixes: $cid (\"$ctitle\")'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] = "Fixes: $cid (\"$ctitle\")";
+ }
+ }
}
# Check email subject for common tools that don't need to be mentioned
@@ -2226,69 +3241,160 @@ sub process {
"A patch subject line should describe the change not the tool that found it\n" . $herecurr);
}
-# Check for old stable address
- if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) {
- ERROR("STABLE_ADDRESS",
- "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr);
+# Check for Gerrit Change-Ids not in any patch context
+ if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) {
+ if (ERROR("GERRIT_CHANGE_ID",
+ "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) &&
+ $fix) {
+ fix_delete_line($fixlinenr, $rawline);
+ }
}
-# Check for unwanted Gerrit info
- if ($in_commit_log && $line =~ /^\s*change-id:/i) {
- ERROR("GERRIT_CHANGE_ID",
- "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
+# Check if the commit log is in a possible stack dump
+ if ($in_commit_log && !$commit_log_possible_stack_dump &&
+ ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
+ $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
+ # timestamp
+ $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) ||
+ $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ ||
+ $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) {
+ # stack dump address styles
+ $commit_log_possible_stack_dump = 1;
}
# Check for line lengths > 75 in commit log, warn once
if ($in_commit_log && !$commit_log_long_line &&
- length($line) > 75) {
+ length($line) > 75 &&
+ !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ ||
+ # file delta changes
+ $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ ||
+ # filename then :
+ $line =~ /^\s*(?:Fixes:|$link_tags_search|$signature_tags)/i ||
+ # A Fixes:, link or signature tag line
+ $commit_log_possible_stack_dump)) {
WARN("COMMIT_LOG_LONG_LINE",
- "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
+ "Prefer a maximum 75 chars per line (possible unwrapped commit description?)\n" . $herecurr);
$commit_log_long_line = 1;
}
+# Reset possible stack dump if a blank line is found
+ if ($in_commit_log && $commit_log_possible_stack_dump &&
+ $line =~ /^\s*$/) {
+ $commit_log_possible_stack_dump = 0;
+ }
+
+# Check for odd tags before a URI/URL
+ if ($in_commit_log &&
+ $line =~ /^\s*(\w+:)\s*http/ && $1 !~ /^$link_tags_search$/) {
+ if ($1 =~ /^v(?:ersion)?\d+/i) {
+ WARN("COMMIT_LOG_VERSIONING",
+ "Patch version information should be after the --- line\n" . $herecurr);
+ } else {
+ WARN("COMMIT_LOG_USE_LINK",
+ "Unknown link reference '$1', use $link_tags_print instead\n" . $herecurr);
+ }
+ }
+
+# Check for misuse of the link tags
+ if ($in_commit_log &&
+ $line =~ /^\s*(\w+:)\s*(\S+)/) {
+ my $tag = $1;
+ my $value = $2;
+ if ($tag =~ /^$link_tags_search$/ && $value !~ m{^https?://}) {
+ WARN("COMMIT_LOG_WRONG_LINK",
+ "'$tag' should be followed by a public http(s) link\n" . $herecurr);
+ }
+ }
+
+# Check for lines starting with a #
+ if ($in_commit_log && $line =~ /^#/) {
+ if (WARN("COMMIT_COMMENT_SYMBOL",
+ "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/^/ /;
+ }
+ }
+
# Check for git id commit length and improperly formed commit descriptions
- if ($in_commit_log && $line =~ /\b(c)ommit\s+([0-9a-f]{5,})/i) {
- my $init_char = $1;
- my $orig_commit = lc($2);
+# A correctly formed commit description is:
+# commit <SHA-1 hash length 12+ chars> ("Complete commit subject")
+# with the commit subject '("' prefix and '")' suffix
+# This is a fairly compilicated block as it tests for what appears to be
+# bare SHA-1 hash with minimum length of 5. It also avoids several types of
+# possible SHA-1 matches.
+# A commit match can span multiple lines so this block attempts to find a
+# complete typical commit on a maximum of 3 lines
+ if ($perl_version_ok &&
+ $in_commit_log && !$commit_log_possible_stack_dump &&
+ $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i &&
+ $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
+ (($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
+ ($line =~ /\bcommit\s*$/i && defined($rawlines[$linenr]) && $rawlines[$linenr] =~ /^\s*[0-9a-f]{5,}\b/i)) ||
+ ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
+ $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
+ $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
+ my $init_char = "c";
+ my $orig_commit = "";
my $short = 1;
my $long = 0;
my $case = 1;
my $space = 1;
- my $hasdesc = 0;
- my $hasparens = 0;
my $id = '0123456789ab';
my $orig_desc = "commit description";
my $description = "";
+ my $herectx = $herecurr;
+ my $has_parens = 0;
+ my $has_quotes = 0;
+
+ my $input = $line;
+ if ($line =~ /(?:\bcommit\s+[0-9a-f]{5,}|\bcommit\s*$)/i) {
+ for (my $n = 0; $n < 2; $n++) {
+ if ($input =~ /\bcommit\s+[0-9a-f]{5,}\s*($balanced_parens)/i) {
+ $orig_desc = $1;
+ $has_parens = 1;
+ # Always strip leading/trailing parens then double quotes if existing
+ $orig_desc = substr($orig_desc, 1, -1);
+ if ($orig_desc =~ /^".*"$/) {
+ $orig_desc = substr($orig_desc, 1, -1);
+ $has_quotes = 1;
+ }
+ last;
+ }
+ last if ($#lines < $linenr + $n);
+ $input .= " " . trim($rawlines[$linenr + $n]);
+ $herectx .= "$rawlines[$linenr + $n]\n";
+ }
+ $herectx = $herecurr if (!$has_parens);
+ }
- $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i);
- $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i);
- $space = 0 if ($line =~ /\bcommit [0-9a-f]/i);
- $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
- if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) {
- $orig_desc = $1;
- $hasparens = 1;
- } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i &&
- defined $rawlines[$linenr] &&
- $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) {
- $orig_desc = $1;
- $hasparens = 1;
- } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i &&
- defined $rawlines[$linenr] &&
- $rawlines[$linenr] =~ /^\s*[^"]+"\)/) {
- $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i;
- $orig_desc = $1;
- $rawlines[$linenr] =~ /^\s*([^"]+)"\)/;
- $orig_desc .= " " . $1;
- $hasparens = 1;
+ if ($input =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) {
+ $init_char = $1;
+ $orig_commit = lc($2);
+ $short = 0 if ($input =~ /\bcommit\s+[0-9a-f]{12,40}/i);
+ $long = 1 if ($input =~ /\bcommit\s+[0-9a-f]{41,}/i);
+ $space = 0 if ($input =~ /\bcommit [0-9a-f]/i);
+ $case = 0 if ($input =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
+ } elsif ($input =~ /\b([0-9a-f]{12,40})\b/i) {
+ $orig_commit = lc($1);
}
($id, $description) = git_commit_info($orig_commit,
$id, $orig_desc);
- if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) {
+ if (defined($id) &&
+ ($short || $long || $space || $case || ($orig_desc ne $description) || !$has_quotes) &&
+ $last_git_commit_id_linenr != $linenr - 1) {
ERROR("GIT_COMMIT_ID",
- "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr);
+ "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herectx);
}
+ #don't report the next line if this line ends in commit and the sha1 hash is the next line
+ $last_git_commit_id_linenr = $linenr if ($line =~ /\bcommit\s*$/i);
+ }
+
+# Check for mailing list archives other than lore.kernel.org
+ if ($rawline =~ m{http.*\b$obsolete_archives}) {
+ WARN("PREFER_LORE_ARCHIVE",
+ "Use lore.kernel.org archive links when possible - see https://lore.kernel.org/lists.html\n" . $herecurr);
}
# Check for added, moved or deleted files
@@ -2297,11 +3403,20 @@ sub process {
$line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
(defined($1) || defined($2))))) {
+ $is_patch = 1;
$reported_maintainer_file = 1;
WARN("FILE_PATH_CHANGES",
"added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
}
+# Check for adding new DT bindings not in schema format
+ if (!$in_commit_log &&
+ ($line =~ /^new file mode\s*\d+\s*$/) &&
+ ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) {
+ WARN("DT_SCHEMA_BINDING_PATCH",
+ "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n");
+ }
+
# Check for wrappage within a valid hunk of the file
if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
ERROR("CORRUPTED_PATCH",
@@ -2309,20 +3424,6 @@ sub process {
$herecurr) if (!$emitted_corrupt++);
}
-# Check for absolute kernel paths.
- if ($tree) {
- while ($line =~ m{(?:^|\s)(/\S*)}g) {
- my $file = $1;
-
- if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
- check_absolute_file($1, $herecurr)) {
- #
- } else {
- check_absolute_file($file, $herecurr);
- }
- }
- }
-
# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
if (($realfile =~ /^$/ || $line =~ /^\+/) &&
$rawline !~ m/^$UTF8*$/) {
@@ -2339,10 +3440,11 @@ sub process {
# Check if it's the start of a commit log
# (not a header line and we haven't seen the patch filename)
if ($in_header_lines && $realfile =~ /^$/ &&
- !($rawline =~ /^\s+\S/ ||
- $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) {
+ !($rawline =~ /^\s+(?:\S|$)/ ||
+ $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) {
$in_header_lines = 0;
$in_commit_log = 1;
+ $has_commit_log = 1;
}
# Check if there is UTF-8 in a commit log when a mail header has explicitly
@@ -2359,24 +3461,106 @@ sub process {
"8-bit UTF-8 used in possible commit log\n" . $herecurr);
}
+# Check for absolute kernel paths in commit message
+ if ($tree && $in_commit_log) {
+ while ($line =~ m{(?:^|\s)(/\S*)}g) {
+ my $file = $1;
+
+ if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+ check_absolute_file($1, $herecurr)) {
+ #
+ } else {
+ check_absolute_file($file, $herecurr);
+ }
+ }
+ }
+
# Check for various typo / spelling mistakes
if (defined($misspellings) &&
($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
- while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) {
+ while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) {
my $typo = $1;
+ my $blank = copy_spacing($rawline);
+ my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo);
+ my $hereptr = "$hereline$ptr\n";
my $typo_fix = $spelling_fix{lc($typo)};
$typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
$typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
- my $msg_type = \&WARN;
- $msg_type = \&CHK if ($file);
- if (&{$msg_type}("TYPO_SPELLING",
- "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) &&
+ my $msg_level = \&WARN;
+ $msg_level = \&CHK if ($file);
+ if (&{$msg_level}("TYPO_SPELLING",
+ "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) &&
$fix) {
$fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
}
}
}
+# check for invalid commit id
+ if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) {
+ my $id;
+ my $description;
+ ($id, $description) = git_commit_info($2, undef, undef);
+ if (!defined($id)) {
+ WARN("UNKNOWN_COMMIT_ID",
+ "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr);
+ }
+ }
+
+# check for repeated words separated by a single space
+# avoid false positive from list command eg, '-rw-r--r-- 1 root root'
+ if (($rawline =~ /^\+/ || $in_commit_log) &&
+ $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) {
+ pos($rawline) = 1 if (!$in_commit_log);
+ while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) {
+
+ my $first = $1;
+ my $second = $2;
+ my $start_pos = $-[1];
+ my $end_pos = $+[2];
+ if ($first =~ /(?:struct|union|enum)/) {
+ pos($rawline) += length($first) + length($second) + 1;
+ next;
+ }
+
+ next if (lc($first) ne lc($second));
+ next if ($first eq 'long');
+
+ # check for character before and after the word matches
+ my $start_char = '';
+ my $end_char = '';
+ $start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1));
+ $end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline));
+
+ next if ($start_char =~ /^\S$/);
+ next if (index(" \t.,;?!", $end_char) == -1);
+
+ # avoid repeating hex occurrences like 'ff ff fe 09 ...'
+ if ($first =~ /\b[0-9a-f]{2,}\b/i) {
+ next if (!exists($allow_repeated_words{lc($first)}));
+ }
+
+ if (WARN("REPEATED_WORD",
+ "Possible repeated word: '$first'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\b$first $second\b/$first/;
+ }
+ }
+
+ # if it's a repeated word on consecutive lines in a comment block
+ if ($prevline =~ /$;+\s*$/ &&
+ $prevrawline =~ /($word_pattern)\s*$/) {
+ my $last_word = $1;
+ if ($rawline =~ /^\+\s*\*\s*$last_word /) {
+ if (WARN("REPEATED_WORD",
+ "Possible repeated word: '$last_word'\n" . $hereprev) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/;
+ }
+ }
+ }
+ }
+
# ignore non-hunk lines and lines being removed
next if (!$hunk_line || $line =~ /^-/);
@@ -2401,69 +3585,105 @@ sub process {
# Check for FSF mailing addresses.
if ($rawline =~ /\bwrite to the Free/i ||
+ $rawline =~ /\b675\s+Mass\s+Ave/i ||
$rawline =~ /\b59\s+Temple\s+Pl/i ||
$rawline =~ /\b51\s+Franklin\s+St/i) {
my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- my $msg_type = \&ERROR;
- $msg_type = \&CHK if ($file);
- &{$msg_type}("FSF_MAILING_ADDRESS",
- "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
+ my $msg_level = \&ERROR;
+ $msg_level = \&CHK if ($file);
+ &{$msg_level}("FSF_MAILING_ADDRESS",
+ "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
}
# check for Kconfig help text having a real description
# Only applies when adding the entry originally, after that we do not have
# sufficient context to determine whether it is indeed long enough.
if ($realfile =~ /Kconfig/ &&
- $line =~ /^\+\s*config\s+/) {
- my $length = 0;
- my $cnt = $realcnt;
- my $ln = $linenr + 1;
- my $f;
- my $is_start = 0;
- my $is_end = 0;
- for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
- $f = $lines[$ln - 1];
- $cnt-- if ($lines[$ln - 1] !~ /^-/);
- $is_end = $lines[$ln - 1] =~ /^\+/;
+ # 'choice' is usually the last thing on the line (though
+ # Kconfig supports named choices), so use a word boundary
+ # (\b) rather than a whitespace character (\s)
+ $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) {
+ my $ln = $linenr;
+ my $needs_help = 0;
+ my $has_help = 0;
+ my $help_length = 0;
+ while (defined $lines[$ln]) {
+ my $f = $lines[$ln++];
next if ($f =~ /^-/);
- last if (!$file && $f =~ /^\@\@/);
+ last if ($f !~ /^[\+ ]/); # !patch context
- if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) {
- $is_start = 1;
- } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
- $length = -1;
+ if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
+ $needs_help = 1;
+ next;
+ }
+ if ($f =~ /^\+\s*help\s*$/) {
+ $has_help = 1;
+ next;
}
- $f =~ s/^.//;
- $f =~ s/#.*//;
- $f =~ s/^\s+//;
- next if ($f =~ /^$/);
- if ($f =~ /^\s*config\s/) {
- $is_end = 1;
+ $f =~ s/^.//; # strip patch context [+ ]
+ $f =~ s/#.*//; # strip # directives
+ $f =~ s/^\s+//; # strip leading blanks
+ next if ($f =~ /^$/); # skip blank lines
+
+ # At the end of this Kconfig block:
+ # This only checks context lines in the patch
+ # and so hopefully shouldn't trigger false
+ # positives, even though some of these are
+ # common words in help texts
+ if ($f =~ /^(?:config|menuconfig|choice|endchoice|
+ if|endif|menu|endmenu|source)\b/x) {
last;
}
- $length++;
+ $help_length++ if ($has_help);
}
- if ($is_start && $is_end && $length < $min_conf_desc_length) {
+ if ($needs_help &&
+ $help_length < $min_conf_desc_length) {
+ my $stat_real = get_stat_real($linenr, $ln - 1);
WARN("CONFIG_DESCRIPTION",
- "please write a paragraph that describes the config symbol fully\n" . $herecurr);
+ "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n");
}
- #print "is_start<$is_start> is_end<$is_end> length<$length>\n";
}
-# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig.
- if ($realfile =~ /Kconfig/ &&
- $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) {
- WARN("CONFIG_EXPERIMENTAL",
- "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n");
- }
-
-# discourage the use of boolean for type definition attributes of Kconfig options
- if ($realfile =~ /Kconfig/ &&
- $line =~ /^\+\s*\bboolean\b/) {
- WARN("CONFIG_TYPE_BOOLEAN",
- "Use of boolean is deprecated, please use bool instead.\n" . $herecurr);
+# check MAINTAINERS entries
+ if ($realfile =~ /^MAINTAINERS$/) {
+# check MAINTAINERS entries for the right form
+ if ($rawline =~ /^\+[A-Z]:/ &&
+ $rawline !~ /^\+[A-Z]:\t\S/) {
+ if (WARN("MAINTAINERS_STYLE",
+ "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+ }
+ }
+# check MAINTAINERS entries for the right ordering too
+ my $preferred_order = 'MRLSWQBCPTFXNK';
+ if ($rawline =~ /^\+[A-Z]:/ &&
+ $prevrawline =~ /^[\+ ][A-Z]:/) {
+ $rawline =~ /^\+([A-Z]):\s*(.*)/;
+ my $cur = $1;
+ my $curval = $2;
+ $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/;
+ my $prev = $1;
+ my $prevval = $2;
+ my $curindex = index($preferred_order, $cur);
+ my $previndex = index($preferred_order, $prev);
+ if ($curindex < 0) {
+ WARN("MAINTAINERS_STYLE",
+ "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr);
+ } else {
+ if ($previndex >= 0 && $curindex < $previndex) {
+ WARN("MAINTAINERS_STYLE",
+ "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev);
+ } elsif ((($prev eq 'F' && $cur eq 'F') ||
+ ($prev eq 'X' && $cur eq 'X')) &&
+ ($prevval cmp $curval) > 0) {
+ WARN("MAINTAINERS_STYLE",
+ "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev);
+ }
+ }
+ }
}
if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
@@ -2488,7 +3708,7 @@ sub process {
my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
my $dt_path = $root . "/Documentation/devicetree/bindings/";
- my $vp_file = $dt_path . "vendor-prefixes.txt";
+ my $vp_file = $dt_path . "vendor-prefixes.yaml";
foreach my $compat (@compats) {
my $compat2 = $compat;
@@ -2503,7 +3723,7 @@ sub process {
next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
my $vendor = $1;
- `grep -Eq "^$vendor\\b" $vp_file`;
+ `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`;
if ( $? >> 8 ) {
WARN("UNDOCUMENTED_DT_STRING",
"DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
@@ -2511,46 +3731,162 @@ sub process {
}
}
+# check for using SPDX license tag at beginning of files
+ if ($realline == $checklicenseline) {
+ if ($rawline =~ /^[ \+]\s*\#\!\s*\//) {
+ $checklicenseline = 2;
+ } elsif ($rawline =~ /^\+/) {
+ my $comment = "";
+ if ($realfile =~ /\.(h|s|S)$/) {
+ $comment = '/*';
+ } elsif ($realfile =~ /\.(c|rs|dts|dtsi)$/) {
+ $comment = '//';
+ } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) {
+ $comment = '#';
+ } elsif ($realfile =~ /\.rst$/) {
+ $comment = '..';
+ }
+
+# check SPDX comment style for .[chsS] files
+ if ($realfile =~ /\.[chsS]$/ &&
+ $rawline =~ /SPDX-License-Identifier:/ &&
+ $rawline !~ m@^\+\s*\Q$comment\E\s*@) {
+ WARN("SPDX_LICENSE_TAG",
+ "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr);
+ }
+
+ if ($comment !~ /^$/ &&
+ $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) {
+ WARN("SPDX_LICENSE_TAG",
+ "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
+ } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) {
+ my $spdx_license = $1;
+ if (!is_SPDX_License_valid($spdx_license)) {
+ WARN("SPDX_LICENSE_TAG",
+ "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr);
+ }
+ if ($realfile =~ m@^Documentation/devicetree/bindings/@ &&
+ $spdx_license !~ /GPL-2\.0(?:-only)? OR BSD-2-Clause/) {
+ my $msg_level = \&WARN;
+ $msg_level = \&CHK if ($file);
+ if (&{$msg_level}("SPDX_LICENSE_TAG",
+
+ "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/;
+ }
+ }
+ if ($realfile =~ m@^include/dt-bindings/@ &&
+ $spdx_license !~ /GPL-2\.0(?:-only)? OR \S+/) {
+ WARN("SPDX_LICENSE_TAG",
+ "DT binding headers should be licensed (GPL-2.0-only OR .*)\n" . $herecurr);
+ }
+ }
+ }
+ }
+
+# check for embedded filenames
+ if ($rawline =~ /^\+.*\b\Q$realfile\E\b/) {
+ WARN("EMBEDDED_FILENAME",
+ "It's generally not useful to have the filename in the file\n" . $herecurr);
+ }
+
# check we are in a valid source file if not then ignore this hunk
- next if ($realfile !~ /\.(h|c|s|S|pl|sh|dtsi|dts)$/);
-
-#line length limit
- if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ &&
- $rawline !~ /^.\s*\*\s*\@$Ident\s/ &&
- !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?$String\s*(?:|,|\)\s*;)\s*$/ ||
- $line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
- $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) &&
- $length > $max_line_length)
- {
- WARN("LONG_LINE",
- "line over $max_line_length characters\n" . $herecurr);
+ next if ($realfile !~ /\.(h|c|rs|s|S|sh|dtsi|dts)$/);
+
+# check for using SPDX-License-Identifier on the wrong line number
+ if ($realline != $checklicenseline &&
+ $rawline =~ /\bSPDX-License-Identifier:/ &&
+ substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) {
+ WARN("SPDX_LICENSE_TAG",
+ "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr);
+ }
+
+# line length limit (with some exclusions)
+#
+# There are a few types of lines that may extend beyond $max_line_length:
+# logging functions like pr_info that end in a string
+# lines with a single string
+# #defines that are a single string
+# lines with an RFC3986 like URL
+#
+# There are 3 different line length message types:
+# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length
+# LONG_LINE_STRING a string starts before but extends beyond $max_line_length
+# LONG_LINE all other lines longer than $max_line_length
+#
+# if LONG_LINE is ignored, the other 2 types are also ignored
+#
+
+ if ($line =~ /^\+/ && $length > $max_line_length) {
+ my $msg_type = "LONG_LINE";
+
+ # Check the allowed long line types first
+
+ # logging functions that end in a string that starts
+ # before $max_line_length
+ if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ &&
+ length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+ $msg_type = "";
+
+ # lines with only strings (w/ possible termination)
+ # #defines with only strings
+ } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
+ $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
+ $msg_type = "";
+
+ # More special cases
+ } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ ||
+ $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) {
+ $msg_type = "";
+
+ # URL ($rawline is used in case the URL is in a comment)
+ } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) {
+ $msg_type = "";
+
+ # Otherwise set the alternate message types
+
+ # a comment starts before $max_line_length
+ } elsif ($line =~ /($;[\s$;]*)$/ &&
+ length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+ $msg_type = "LONG_LINE_COMMENT"
+
+ # a quoted string starts before $max_line_length
+ } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ &&
+ length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+ $msg_type = "LONG_LINE_STRING"
+ }
+
+ if ($msg_type ne "" &&
+ (show_type("LONG_LINE") || show_type($msg_type))) {
+ my $msg_level = \&WARN;
+ $msg_level = \&CHK if ($file);
+ &{$msg_level}($msg_type,
+ "line length of $length exceeds $max_line_length columns\n" . $herecurr);
+ }
}
# check for adding lines without a newline.
if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
- WARN("MISSING_EOF_NEWLINE",
- "adding a line without newline at end of file\n" . $herecurr);
+ if (WARN("MISSING_EOF_NEWLINE",
+ "adding a line without newline at end of file\n" . $herecurr) &&
+ $fix) {
+ fix_delete_line($fixlinenr+1, "No newline at end of file");
+ }
}
-# Blackfin: use hi/lo macros
- if ($realfile =~ m@arch/blackfin/.*\.S$@) {
- if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) {
- my $herevet = "$here\n" . cat_vet($line) . "\n";
- ERROR("LO_MACRO",
- "use the LO() macro, not (... & 0xFFFF)\n" . $herevet);
- }
- if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) {
- my $herevet = "$here\n" . cat_vet($line) . "\n";
- ERROR("HI_MACRO",
- "use the HI() macro, not (... >> 16)\n" . $herevet);
- }
+# check for .L prefix local symbols in .S files
+ if ($realfile =~ /\.S$/ &&
+ $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) {
+ WARN("AVOID_L_PREFIX",
+ "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/core-api/asm-annotations.rst\n" . $herecurr);
}
# check we are in a valid source file C or perl if not then ignore this hunk
next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/);
# at the beginning of a line any tabs must come first and anything
-# more than 8 must use tabs.
+# more than $tabsize must use tabs.
if ($rawline =~ /^\+\s* \t\s*\S/ ||
$rawline =~ /^\+\s* \s*/) {
my $herevet = "$here\n" . cat_vet($rawline) . "\n";
@@ -2569,21 +3905,54 @@ sub process {
"please, no space before tabs\n" . $herevet) &&
$fix) {
while ($fixed[$fixlinenr] =~
- s/(^\+.*) {8,8}\t/$1\t\t/) {}
+ s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {}
while ($fixed[$fixlinenr] =~
s/(^\+.*) +\t/$1\t/) {}
}
}
+# check for assignments on the start of a line
+ if ($sline =~ /^\+\s+($Assignment)[^=]/) {
+ my $operator = $1;
+ if (CHK("ASSIGNMENT_CONTINUATIONS",
+ "Assignment operator '$1' should be on the previous line\n" . $hereprev) &&
+ $fix && $prevrawline =~ /^\+/) {
+ # add assignment operator to the previous line, remove from current line
+ $fixed[$fixlinenr - 1] .= " $operator";
+ $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+ }
+ }
+
# check for && or || at the start of a line
if ($rawline =~ /^\+\s*(&&|\|\|)/) {
- CHK("LOGICAL_CONTINUATIONS",
- "Logical continuations should be on the previous line\n" . $hereprev);
+ my $operator = $1;
+ if (CHK("LOGICAL_CONTINUATIONS",
+ "Logical continuations should be on the previous line\n" . $hereprev) &&
+ $fix && $prevrawline =~ /^\+/) {
+ # insert logical operator at last non-comment, non-whitepsace char on previous line
+ $prevline =~ /[\s$;]*$/;
+ my $line_end = substr($prevrawline, $-[0]);
+ $fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/;
+ $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+ }
+ }
+
+# check indentation starts on a tab stop
+ if ($perl_version_ok &&
+ $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) {
+ my $indent = length($1);
+ if ($indent % $tabsize) {
+ if (WARN("TABSTOP",
+ "Statements should start on a tabstop\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e;
+ }
+ }
}
# check multi-line statement indentation matches previous line
- if ($^V && $^V ge 5.10.0 &&
- $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
+ if ($perl_version_ok &&
+ $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
$prevline =~ /^\+(\t*)(.*)$/;
my $oldindent = $1;
my $rest = $2;
@@ -2594,8 +3963,8 @@ sub process {
my $newindent = $2;
my $goodtabindent = $oldindent .
- "\t" x ($pos / 8) .
- " " x ($pos % 8);
+ "\t" x ($pos / $tabsize) .
+ " " x ($pos % $tabsize);
my $goodspaceindent = $oldindent . " " x $pos;
if ($newindent ne $goodtabindent &&
@@ -2628,30 +3997,57 @@ sub process {
}
}
+# Block comment styles
+# Networking with an initial /*
if ($realfile =~ m@^(drivers/net/|net/)@ &&
$prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
$rawline =~ /^\+[ \t]*\*/ &&
- $realline > 2) {
+ $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier
WARN("NETWORKING_BLOCK_COMMENT_STYLE",
"networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
}
- if ($realfile =~ m@^(drivers/net/|net/)@ &&
- $prevrawline =~ /^\+[ \t]*\/\*/ && #starting /*
+# Block comments use * on subsequent lines
+ if ($prevline =~ /$;[ \t]*$/ && #ends in comment
+ $prevrawline =~ /^\+.*?\/\*/ && #starting /*
$prevrawline !~ /\*\/[ \t]*$/ && #no trailing */
$rawline =~ /^\+/ && #line is new
$rawline !~ /^\+[ \t]*\*/) { #no leading *
- WARN("NETWORKING_BLOCK_COMMENT_STYLE",
- "networking block comments start with * on subsequent lines\n" . $hereprev);
+ WARN("BLOCK_COMMENT_STYLE",
+ "Block comments use * on subsequent lines\n" . $hereprev);
}
- if ($realfile =~ m@^(drivers/net/|net/)@ &&
- $rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */
+# Block comments use */ on trailing lines
+ if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */
$rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/
$rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/
$rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */
- WARN("NETWORKING_BLOCK_COMMENT_STYLE",
- "networking block comments put the trailing */ on a separate line\n" . $herecurr);
+ WARN("BLOCK_COMMENT_STYLE",
+ "Block comments use a trailing */ on a separate line\n" . $herecurr);
+ }
+
+# Block comment * alignment
+ if ($prevline =~ /$;[ \t]*$/ && #ends in comment
+ $line =~ /^\+[ \t]*$;/ && #leading comment
+ $rawline =~ /^\+[ \t]*\*/ && #leading *
+ (($prevrawline =~ /^\+.*?\/\*/ && #leading /*
+ $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */
+ $prevrawline =~ /^\+[ \t]*\*/)) { #leading *
+ my $oldindent;
+ $prevrawline =~ m@^\+([ \t]*/?)\*@;
+ if (defined($1)) {
+ $oldindent = expand_tabs($1);
+ } else {
+ $prevrawline =~ m@^\+(.*/?)\*@;
+ $oldindent = expand_tabs($1);
+ }
+ $rawline =~ m@^\+([ \t]*)\*@;
+ my $newindent = $1;
+ $newindent = expand_tabs($newindent);
+ if (length($oldindent) ne length($newindent)) {
+ WARN("BLOCK_COMMENT_STYLE",
+ "Block comments should align the * on each line\n" . $hereprev);
+ }
}
# check for missing blank lines after struct/union declarations
@@ -2659,12 +4055,13 @@ sub process {
if ($prevline =~ /^[\+ ]};?\s*$/ &&
$line =~ /^\+/ &&
!($line =~ /^\+\s*$/ ||
- $line =~ /^\+\s*EXPORT_SYMBOL/ ||
+ $line =~ /^\+\s*(?:EXPORT_SYMBOL|early_param|ALLOW_ERROR_INJECTION)/ ||
$line =~ /^\+\s*MODULE_/i ||
$line =~ /^\+\s*\#\s*(?:end|elif|else)/ ||
$line =~ /^\+[a-z_]*init/ ||
$line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ ||
$line =~ /^\+\s*DECLARE/ ||
+ $line =~ /^\+\s*builtin_[\w_]*driver/ ||
$line =~ /^\+\s*__setup/)) {
if (CHK("LINE_SPACING",
"Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) &&
@@ -2687,43 +4084,48 @@ sub process {
}
# check for missing blank lines after declarations
- if ($sline =~ /^\+\s+\S/ && #Not at char 1
- # actual declarations
- ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+# (declarations must have the same indentation and not be at the start of line)
+ if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) {
+ # use temporaries
+ my $sl = $sline;
+ my $pl = $prevline;
+ # remove $Attribute/$Sparse uses to simplify comparisons
+ $sl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+ $pl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+ if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
# function pointer declarations
- $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+ $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
# foo bar; where foo is some local typedef or #define
- $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+ $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
# known declaration macros
- $prevline =~ /^\+\s+$declaration_macros/) &&
+ $pl =~ /^\+\s+$declaration_macros/) &&
# for "else if" which can look like "$Ident $Ident"
- !($prevline =~ /^\+\s+$c90_Keywords\b/ ||
+ !($pl =~ /^\+\s+$c90_Keywords\b/ ||
# other possible extensions of declaration lines
- $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+ $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
# not starting a section or a macro "\" extended line
- $prevline =~ /(?:\{\s*|\\)$/) &&
+ $pl =~ /(?:\{\s*|\\)$/) &&
# looks like a declaration
- !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+ !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
# function pointer declarations
- $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+ $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
# foo bar; where foo is some local typedef or #define
- $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+ $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
# known declaration macros
- $sline =~ /^\+\s+$declaration_macros/ ||
+ $sl =~ /^\+\s+$declaration_macros/ ||
# start of struct or union or enum
- $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ ||
+ $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ ||
# start or end of block or continuation of declaration
- $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
+ $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
# bitfield continuation
- $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
+ $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
# other possible extensions of declaration lines
- $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) &&
- # indentation of previous and current line are the same
- (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) {
- if (WARN("LINE_SPACING",
- "Missing a blank line after declarations\n" . $hereprev) &&
- $fix) {
- fix_insert_line($fixlinenr, "\+");
+ $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) {
+ if (WARN("LINE_SPACING",
+ "Missing a blank line after declarations\n" . $hereprev) &&
+ $fix) {
+ fix_insert_line($fixlinenr, "\+");
+ }
}
}
@@ -2744,6 +4146,23 @@ sub process {
# check we are in a valid C source file if not then ignore this hunk
next if ($realfile !~ /\.(h|c)$/);
+# check for unusual line ending [ or (
+ if ($line =~ /^\+.*([\[\(])\s*$/) {
+ CHK("OPEN_ENDED_LINE",
+ "Lines should not end with a '$1'\n" . $herecurr);
+ }
+
+# check if this appears to be the start function declaration, save the name
+ if ($sline =~ /^\+\{\s*$/ &&
+ $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) {
+ $context_function = $1;
+ }
+
+# check if this appears to be the end of function declaration
+ if ($sline =~ /^\+\}\s*$/) {
+ undef $context_function;
+ }
+
# check indentation of any line with a bare else
# (but not if it is a multiple line "if (foo) return bar; else return baz;")
# if the previous line is a break or return and is indented 1 tab more...
@@ -2759,39 +4178,25 @@ sub process {
}
# check indentation of a line with a break;
-# if the previous line is a goto or return and is indented the same # of tabs
+# if the previous line is a goto, return or break
+# and is indented the same # of tabs
if ($sline =~ /^\+([\t]+)break\s*;\s*$/) {
my $tabs = $1;
- if ($prevline =~ /^\+$tabs(?:goto|return)\b/) {
- WARN("UNNECESSARY_BREAK",
- "break is not useful after a goto or return\n" . $hereprev);
+ if ($prevline =~ /^\+$tabs(goto|return|break)\b/) {
+ if (WARN("UNNECESSARY_BREAK",
+ "break is not useful after a $1\n" . $hereprev) &&
+ $fix) {
+ fix_delete_line($fixlinenr, $rawline);
+ }
}
}
-# discourage the addition of CONFIG_EXPERIMENTAL in #if(def).
- if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) {
- WARN("CONFIG_EXPERIMENTAL",
- "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n");
- }
-
# check for RCS/CVS revision markers
if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
WARN("CVS_KEYWORD",
"CVS style keyword markers, these will _not_ be updated\n". $herecurr);
}
-# Blackfin: don't use __builtin_bfin_[cs]sync
- if ($line =~ /__builtin_bfin_csync/) {
- my $herevet = "$here\n" . cat_vet($line) . "\n";
- ERROR("CSYNC",
- "use the CSYNC() macro in asm/blackfin.h\n" . $herevet);
- }
- if ($line =~ /__builtin_bfin_ssync/) {
- my $herevet = "$here\n" . cat_vet($line) . "\n";
- ERROR("SSYNC",
- "use the SSYNC() macro in asm/blackfin.h\n" . $herevet);
- }
-
# check for old HOTPLUG __dev<foo> section markings
if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) {
WARN("HOTPLUG_SECTION",
@@ -2802,7 +4207,7 @@ sub process {
my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
$realline_next);
#print "LINE<$line>\n";
- if ($linenr >= $suppress_statement &&
+ if ($linenr > $suppress_statement &&
$realcnt && $sline =~ /.\s*\S/) {
($stat, $cond, $line_nr_next, $remain_next, $off_next) =
ctx_statement_block($linenr, $realcnt, 0);
@@ -2949,7 +4354,7 @@ sub process {
}
# Check relative indent for conditionals and blocks.
- if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+ if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
($stat, $cond, $line_nr_next, $remain_next, $off_next) =
ctx_statement_block($linenr, $realcnt, 0)
if (!defined $stat);
@@ -2957,15 +4362,22 @@ sub process {
substr($s, 0, length($c), '');
- # Make sure we remove the line prefixes as we have
- # none on the first line, and are going to readd them
- # where necessary.
- $s =~ s/\n./\n/gs;
+ # remove inline comments
+ $s =~ s/$;/ /g;
+ $c =~ s/$;/ /g;
# Find out how long the conditional actually is.
my @newlines = ($c =~ /\n/gs);
my $cond_lines = 1 + $#newlines;
+ # Make sure we remove the line prefixes as we have
+ # none on the first line, and are going to readd them
+ # where necessary.
+ $s =~ s/\n./\n/gs;
+ while ($s =~ /\n\s+\\\n/) {
+ $cond_lines += $s =~ s/\n\s+\\\n/\n/g;
+ }
+
# We want to check the first line inside the block
# starting at the end of the conditional, so remove:
# 1) any blank line termination
@@ -3031,8 +4443,12 @@ sub process {
#print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
- if ($check && (($sindent % 8) != 0 ||
- ($sindent <= $indent && $s ne ''))) {
+ if ($check && $s ne '' &&
+ (($sindent % $tabsize) != 0 ||
+ ($sindent < $indent) ||
+ ($sindent == $indent &&
+ ($s !~ /^\s*(?:\}|\{|else\b)/)) ||
+ ($sindent > $indent + $tabsize))) {
WARN("SUSPECT_CODE_INDENT",
"suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
}
@@ -3054,6 +4470,53 @@ sub process {
#ignore lines not being added
next if ($line =~ /^[^\+]/);
+# check for self assignments used to avoid compiler warnings
+# e.g.: int foo = foo, *bar = NULL;
+# struct foo bar = *(&(bar));
+ if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) {
+ my $var = $1;
+ if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) {
+ WARN("SELF_ASSIGNMENT",
+ "Do not use self-assignments to avoid compiler warnings\n" . $herecurr);
+ }
+ }
+
+# check for dereferences that span multiple lines
+ if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ &&
+ $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) {
+ $prevline =~ /($Lval\s*(?:\.|->))\s*$/;
+ my $ref = $1;
+ $line =~ /^.\s*($Lval)/;
+ $ref .= $1;
+ $ref =~ s/\s//g;
+ WARN("MULTILINE_DEREFERENCE",
+ "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev);
+ }
+
+# check for declarations of signed or unsigned without int
+ while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
+ my $type = $1;
+ my $var = $2;
+ $var = "" if (!defined $var);
+ if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) {
+ my $sign = $1;
+ my $pointer = $2;
+
+ $pointer = "" if (!defined $pointer);
+
+ if (WARN("UNSPECIFIED_INT",
+ "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) &&
+ $fix) {
+ my $decl = trim($sign) . " int ";
+ my $comp_pointer = $pointer;
+ $comp_pointer =~ s/\s//g;
+ $decl .= $comp_pointer;
+ $decl = rtrim($decl) if ($var eq "");
+ $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@;
+ }
+ }
+ }
+
# TEST: allow direct testing of the type matcher.
if ($dbg_type) {
if ($line =~ /^.\s*$Declare\s*$/) {
@@ -3089,7 +4552,7 @@ sub process {
$fixedline =~ s/\s*=\s*$/ = {/;
fix_insert_line($fixlinenr, $fixedline);
$fixedline = $line;
- $fixedline =~ s/^(.\s*){\s*/$1/;
+ $fixedline =~ s/^(.\s*)\{\s*/$1/;
fix_insert_line($fixlinenr, $fixedline);
}
}
@@ -3133,13 +4596,13 @@ sub process {
if (defined $realline_next &&
exists $lines[$realline_next - 1] &&
!defined $suppress_export{$realline_next} &&
- ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
- $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+ ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
# Handle definitions which produce identifiers with
# a prefix:
# XXX(foo);
# EXPORT_SYMBOL(something_foo);
my $name = $1;
+ $name =~ s/^\s*($Ident).*/$1/;
if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ &&
$name =~ /^${Ident}_$2/) {
#print "FOO C name<$name>\n";
@@ -3161,8 +4624,7 @@ sub process {
}
if (!defined $suppress_export{$linenr} &&
$prevline =~ /^.\s*$/ &&
- ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
- $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+ ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
#print "FOO B <$lines[$linenr - 1]>\n";
$suppress_export{$linenr} = 2;
}
@@ -3173,21 +4635,21 @@ sub process {
}
# check for global initialisers.
- if ($line =~ /^\+(\s*$Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/) {
+ if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ &&
+ !exclude_global_initialisers($realfile)) {
if (ERROR("GLOBAL_INITIALISERS",
- "do not initialise globals to 0 or NULL\n" .
- $herecurr) &&
+ "do not initialise globals to $1\n" . $herecurr) &&
$fix) {
- $fixed[$fixlinenr] =~ s/($Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/$1;/;
+ $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/;
}
}
# check for static initialisers.
- if ($line =~ /^\+.*\bstatic\s.*=\s*(0|NULL|false)\s*;/) {
+ if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) {
if (ERROR("INITIALISED_STATIC",
- "do not initialise statics to 0 or NULL\n" .
+ "do not initialise statics to $1\n" .
$herecurr) &&
$fix) {
- $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*(0|NULL|false)\s*;/$1;/;
+ $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/;
}
}
@@ -3198,19 +4660,48 @@ sub process {
"type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr);
}
+# check for unnecessary <signed> int declarations of short/long/long long
+ while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) {
+ my $type = trim($1);
+ next if ($type !~ /\bint\b/);
+ next if ($type !~ /\b(?:short|long\s+long|long)\b/);
+ my $new_type = $type;
+ $new_type =~ s/\b\s*int\s*\b/ /;
+ $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /;
+ $new_type =~ s/^const\s+//;
+ $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/);
+ $new_type = "const $new_type" if ($type =~ /^const\b/);
+ $new_type =~ s/\s+/ /g;
+ $new_type = trim($new_type);
+ if (WARN("UNNECESSARY_INT",
+ "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/;
+ }
+ }
+
# check for static const char * arrays.
if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) {
WARN("STATIC_CONST_CHAR_ARRAY",
"static const char * array should probably be static const char * const\n" .
$herecurr);
- }
+ }
+
+# check for initialized const char arrays that should be static const
+ if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) {
+ if (WARN("STATIC_CONST_CHAR_ARRAY",
+ "const array should probably be static const\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/;
+ }
+ }
# check for static char foo[] = "bar" declarations.
if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
WARN("STATIC_CONST_CHAR_ARRAY",
"static char array declaration should probably be static const char\n" .
$herecurr);
- }
+ }
# check for const <foo> const where <foo> is not a pointer or array type
if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) {
@@ -3224,12 +4715,24 @@ sub process {
}
}
+# check for const static or static <non ptr type> const declarations
+# prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const'
+ if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ ||
+ $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) {
+ if (WARN("STATIC_CONST",
+ "Move const after static - use 'static const $1'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/;
+ $fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/;
+ }
+ }
+
# check for non-global char *foo[] = {"bar", ...} declarations.
if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
WARN("STATIC_CONST_CHAR_ARRAY",
"char * array declaration might be better as static const\n" .
$herecurr);
- }
+ }
# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo)
if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) {
@@ -3245,7 +4748,7 @@ sub process {
}
# check for function declarations without arguments like "int foo()"
- if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) {
+ if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) {
if (ERROR("FUNCTION_WITHOUT_ARGS",
"Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
$fix) {
@@ -3253,23 +4756,13 @@ sub process {
}
}
-# check for uses of DEFINE_PCI_DEVICE_TABLE
- if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) {
- if (WARN("DEFINE_PCI_DEVICE_TABLE",
- "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /;
- }
- }
-
# check for new typedefs, only function parameters and sparse annotations
# make sense.
if ($line =~ /\btypedef\s/ &&
$line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
$line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
$line !~ /\b$typeTypedefs\b/ &&
- $line !~ /\b$typeOtherOSTypedefs\b/ &&
- $line !~ /\b__bitwise(?:__|)\b/) {
+ $line !~ /\b__bitwise\b/) {
WARN("NEW_TYPEDEFS",
"do not add new typedefs\n" . $herecurr);
}
@@ -3330,13 +4823,15 @@ sub process {
}
}
-# # no BUG() or BUG_ON()
-# if ($line =~ /\b(BUG|BUG_ON)\b/) {
-# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n";
-# print "$herecurr";
-# $clean = 0;
-# }
+# do not use BUG() or variants
+ if ($line =~ /\b(?!AA_|BUILD_|DCCP_|IDA_|KVM_|RWLOCK_|snd_|SPIN_)(?:[a-zA-Z_]*_)?BUG(?:_ON)?(?:_[A-Z_]+)?\s*\(/) {
+ my $msg_level = \&WARN;
+ $msg_level = \&CHK if ($file);
+ &{$msg_level}("AVOID_BUG",
+ "Do not crash the kernel unless it is absolutely unavoidable--use WARN_ON_ONCE() plus recovery code (if feasible) instead of BUG() or variants\n" . $herecurr);
+ }
+# avoid LINUX_VERSION_CODE
if ($line =~ /\bLINUX_VERSION_CODE\b/) {
WARN("LINUX_VERSION_CODE",
"LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr);
@@ -3348,49 +4843,29 @@ sub process {
"Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr);
}
-# printk should use KERN_* levels. Note that follow on printk's on the
-# same line do not need a level, so we use the current block context
-# to try and find and validate the current printk. In summary the current
-# printk includes all preceding printk's which have no newline on the end.
-# we assume the first bad printk is the one to report.
- if ($line =~ /\bprintk\((?!KERN_)\s*"/) {
- my $ok = 0;
- for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) {
- #print "CHECK<$lines[$ln - 1]\n";
- # we have a preceding printk if it ends
- # with "\n" ignore it, else it is to blame
- if ($lines[$ln - 1] =~ m{\bprintk\(}) {
- if ($rawlines[$ln - 1] !~ m{\\n"}) {
- $ok = 1;
- }
- last;
- }
- }
- if ($ok == 0) {
- WARN("PRINTK_WITHOUT_KERN_LEVEL",
- "printk() should include KERN_ facility level\n" . $herecurr);
- }
+# printk should use KERN_* levels
+ if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) {
+ WARN("PRINTK_WITHOUT_KERN_LEVEL",
+ "printk() should include KERN_<LEVEL> facility level\n" . $herecurr);
}
- if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) {
- my $orig = $1;
+# prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL>
+ if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) {
+ my $printk = $1;
+ my $modifier = $2;
+ my $orig = $3;
+ $modifier = "" if (!defined($modifier));
my $level = lc($orig);
$level = "warn" if ($level eq "warning");
my $level2 = $level;
$level2 = "dbg" if ($level eq "debug");
+ $level .= $modifier;
+ $level2 .= $modifier;
WARN("PREFER_PR_LEVEL",
- "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr);
- }
-
- if ($line =~ /\bpr_warning\s*\(/) {
- if (WARN("PREFER_PR_LEVEL",
- "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~
- s/\bpr_warning\b/pr_warn/;
- }
+ "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to $printk(KERN_$orig ...\n" . $herecurr);
}
+# prefer dev_<level> to dev_printk(KERN_<LEVEL>
if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) {
my $orig = $1;
my $level = lc($orig);
@@ -3400,6 +4875,12 @@ sub process {
"Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr);
}
+# trace_printk should not be used in production code.
+ if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) {
+ WARN("TRACE_PRINTK",
+ "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr);
+ }
+
# ENOSYS means "bad syscall nr" and nothing else. This will have a small
# number of false positives, but assembly files are not checked, so at
# least the arch entry code will not trigger this warning.
@@ -3408,16 +4889,29 @@ sub process {
"ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr);
}
+# ENOTSUPP is not a standard error code and should be avoided in new patches.
+# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP.
+# Similarly to ENOSYS warning a small number of false positives is expected.
+ if (!$file && $line =~ /\bENOTSUPP\b/) {
+ if (WARN("ENOTSUPP",
+ "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/;
+ }
+ }
+
# function brace can't be on same line, except for #defines of do while,
# or if closed on same line
- if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and
- !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) {
+ if ($perl_version_ok &&
+ $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ &&
+ $sline !~ /\#\s*define\b.*do\s*\{/ &&
+ $sline !~ /}/) {
if (ERROR("OPEN_BRACE",
- "open brace '{' following function declarations go on the next line\n" . $herecurr) &&
+ "open brace '{' following function definitions go on the next line\n" . $herecurr) &&
$fix) {
fix_delete_line($fixlinenr, $rawline);
my $fixed_line = $rawline;
- $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/;
+ $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/;
my $line1 = $1;
my $line2 = $2;
fix_insert_line($fixlinenr, ltrim($line1));
@@ -3439,7 +4933,7 @@ sub process {
my $fixedline = rtrim($prevrawline) . " {";
fix_insert_line($fixlinenr, $fixedline);
$fixedline = $rawline;
- $fixedline =~ s/^(.\s*){\s*/$1\t/;
+ $fixedline =~ s/^(.\s*)\{\s*/$1\t/;
if ($fixedline !~ /^\+\s*$/) {
fix_insert_line($fixlinenr, $fixedline);
}
@@ -3534,7 +5028,7 @@ sub process {
my ($where, $prefix) = ($-[1], $1);
if ($prefix !~ /$Type\s+$/ &&
($where != 0 || $prefix !~ /^.\s+$/) &&
- $prefix !~ /[{,]\s+$/) {
+ $prefix !~ /[{,:]\s+$/) {
if (ERROR("BRACKET_SPACE",
"space prohibited before open square bracket '['\n" . $herecurr) &&
$fix) {
@@ -3555,7 +5049,7 @@ sub process {
if|for|while|switch|return|case|
volatile|__volatile__|
__attribute__|format|__extension__|
- asm|__asm__)$/x)
+ asm|__asm__|scoped_guard)$/x)
{
# cpp #define statements have non-optional spaces, ie
# if there is a space between the name and the open
@@ -3828,7 +5322,7 @@ sub process {
# A colon needs no spaces before when it is
# terminating a case value or a label.
} elsif ($opv eq ':C' || $opv eq ':L') {
- if ($ctx =~ /Wx./) {
+ if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) {
if (ERROR("SPACING",
"space prohibited before that '$op' $at\n" . $hereptr)) {
$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
@@ -3846,7 +5340,7 @@ sub process {
($op eq '>' &&
$ca =~ /<\S+\@\S+$/))
{
- $ok = 1;
+ $ok = 1;
}
# for asm volatile statements
@@ -3859,11 +5353,11 @@ sub process {
# messages are ERROR, but ?: are CHK
if ($ok == 0) {
- my $msg_type = \&ERROR;
- $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
+ my $msg_level = \&ERROR;
+ $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
- if (&{$msg_type}("SPACING",
- "spaces required around that '$op' $at\n" . $hereptr)) {
+ if (&{$msg_level}("SPACING",
+ "spaces required around that '$op' $at\n" . $hereptr)) {
$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
if (defined $fix_elements[$n + 2]) {
$fix_elements[$n + 2] =~ s/^\s+//;
@@ -3912,7 +5406,7 @@ sub process {
## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
##
## # Remove any bracketed sections to ensure we do not
-## # falsly report the parameters of functions.
+## # falsely report the parameters of functions.
## my $ln = $line;
## while ($ln =~ s/\([^\(\)]*\)//g) {
## }
@@ -3923,12 +5417,12 @@ sub process {
## }
#need space before brace following if, while, etc
- if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\){/) ||
- $line =~ /do\{/) {
+ if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) ||
+ $line =~ /\b(?:else|do)\{/) {
if (ERROR("SPACING",
"space required before the open brace '{'\n" . $herecurr) &&
$fix) {
- $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/;
+ $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/;
}
}
@@ -3942,7 +5436,7 @@ sub process {
# closing brace should have a space following it when it has anything
# on the line
- if ($line =~ /}(?!(?:,|;|\)))\S/) {
+ if ($line =~ /}(?!(?:,|;|\)|\}))\S/) {
if (ERROR("SPACING",
"space required after that close brace '}'\n" . $herecurr) &&
$fix) {
@@ -4016,9 +5510,39 @@ sub process {
}
}
-#goto labels aren't indented, allow a single space however
- if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and
- !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) {
+# check for unnecessary parentheses around comparisons in if uses
+# when !drivers/staging or command-line uses --strict
+ if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) &&
+ $perl_version_ok && defined($stat) &&
+ $stat =~ /(^.\s*if\s*($balanced_parens))/) {
+ my $if_stat = $1;
+ my $test = substr($2, 1, -1);
+ my $herectx;
+ while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) {
+ my $match = $1;
+ # avoid parentheses around potential macro args
+ next if ($match =~ /^\s*\w+\s*$/);
+ if (!defined($herectx)) {
+ $herectx = $here . "\n";
+ my $cnt = statement_rawlines($if_stat);
+ for (my $n = 0; $n < $cnt; $n++) {
+ my $rl = raw_line($linenr, $n);
+ $herectx .= $rl . "\n";
+ last if $rl =~ /^[ \+].*\{/;
+ }
+ }
+ CHK("UNNECESSARY_PARENTHESES",
+ "Unnecessary parentheses around '$match'\n" . $herectx);
+ }
+ }
+
+# check that goto labels aren't indented (allow a single space indentation)
+# and ignore bitfield definitions like foo:1
+# Strictly, labels can have whitespace after the identifier and before the :
+# but this is not allowed here as many ?: uses would appear to be labels
+ if ($sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ &&
+ $sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ &&
+ $sline !~ /^.\s+default:/) {
if (WARN("INDENTED_LABEL",
"labels should not be indented\n" . $herecurr) &&
$fix) {
@@ -4027,10 +5551,21 @@ sub process {
}
}
+# check if a statement with a comma should be two statements like:
+# foo = bar(), /* comma should be semicolon */
+# bar = baz();
+ if (defined($stat) &&
+ $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) {
+ my $cnt = statement_rawlines($stat);
+ my $herectx = get_stat_here($linenr, $cnt, $here);
+ WARN("SUSPECT_COMMA_SEMICOLON",
+ "Possible comma where semicolon could be used\n" . $herectx);
+ }
+
# return is not a function
if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
my $spacing = $1;
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
my $value = $1;
$value = deparenthesize($value);
@@ -4054,10 +5589,10 @@ sub process {
$lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) {
WARN("RETURN_VOID",
"void function return statements are not generally useful\n" . $hereprev);
- }
+ }
# if statements using unnecessary parentheses - ie: if ((foo == bar))
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$line =~ /\bif\s*((?:\(\s*){2,})/) {
my $openparens = $1;
my $count = $openparens =~ tr@\(@\(@;
@@ -4070,10 +5605,39 @@ sub process {
}
}
+# comparisons with a constant or upper case identifier on the left
+# avoid cases like "foo + BAR < baz"
+# only fix matches surrounded by parentheses to avoid incorrect
+# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5"
+ if ($perl_version_ok &&
+ $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) {
+ my $lead = $1;
+ my $const = $2;
+ my $comp = $3;
+ my $to = $4;
+ my $newcomp = $comp;
+ if ($lead !~ /(?:$Operators|\.)\s*$/ &&
+ $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ &&
+ WARN("CONSTANT_COMPARISON",
+ "Comparisons should place the constant on the right side of the test\n" . $herecurr) &&
+ $fix) {
+ if ($comp eq "<") {
+ $newcomp = ">";
+ } elsif ($comp eq "<=") {
+ $newcomp = ">=";
+ } elsif ($comp eq ">") {
+ $newcomp = "<";
+ } elsif ($comp eq ">=") {
+ $newcomp = "<=";
+ }
+ $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/;
+ }
+ }
+
# Return of what appears to be an errno should normally be negative
if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) {
my $name = $1;
- if ($name ne 'EOF' && $name ne 'ERROR') {
+ if ($name ne 'EOF' && $name ne 'ERROR' && $name !~ /^EPOLL/) {
WARN("USE_NEGATIVE_ERRNO",
"return of an errno should typically be negative (ie: return -$1)\n" . $herecurr);
}
@@ -4116,17 +5680,41 @@ sub process {
defined($stat) && defined($cond) &&
$line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
my ($s, $c) = ($stat, $cond);
+ my $fixed_assign_in_if = 0;
if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
- ERROR("ASSIGN_IN_IF",
- "do not use assignment in if condition\n" . $herecurr);
+ if (ERROR("ASSIGN_IN_IF",
+ "do not use assignment in if condition\n" . $herecurr) &&
+ $fix && $perl_version_ok) {
+ if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) {
+ my $space = $1;
+ my $not = $2;
+ my $statement = $3;
+ my $assigned = $4;
+ my $test = $8;
+ my $against = $9;
+ my $brace = $15;
+ fix_delete_line($fixlinenr, $rawline);
+ fix_insert_line($fixlinenr, "$space$statement;");
+ my $newline = "${space}if (";
+ $newline .= '!' if defined($not);
+ $newline .= '(' if (defined $not && defined($test) && defined($against));
+ $newline .= "$assigned";
+ $newline .= " $test $against" if (defined($test) && defined($against));
+ $newline .= ')' if (defined $not && defined($test) && defined($against));
+ $newline .= ')';
+ $newline .= " {" if (defined($brace));
+ fix_insert_line($fixlinenr + 1, $newline);
+ $fixed_assign_in_if = 1;
+ }
+ }
}
# Find out what is on the end of the line after the
# conditional.
substr($s, 0, length($c), '');
$s =~ s/\n.*//g;
- $s =~ s/$;//g; # Remove any comments
+ $s =~ s/$;//g; # Remove any comments
if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
$c !~ /}\s*while\s*/)
{
@@ -4141,8 +5729,20 @@ sub process {
$stat_real = "[...]\n$stat_real";
}
- ERROR("TRAILING_STATEMENTS",
- "trailing statements should be on next line\n" . $herecurr . $stat_real);
+ if (ERROR("TRAILING_STATEMENTS",
+ "trailing statements should be on next line\n" . $herecurr . $stat_real) &&
+ !$fixed_assign_in_if &&
+ $cond_lines == 0 &&
+ $fix && $perl_version_ok &&
+ $fixed[$fixlinenr] =~ /^\+(\s*)((?:if|while|for)\s*$balanced_parens)\s*(.*)$/) {
+ my $indent = $1;
+ my $test = $2;
+ my $rest = rtrim($4);
+ if ($rest =~ /;$/) {
+ $fixed[$fixlinenr] = "\+$indent$test";
+ fix_insert_line($fixlinenr + 1, "$indent\t$rest");
+ }
+ }
}
}
@@ -4165,7 +5765,7 @@ sub process {
# if and else should not have general statements after it
if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
my $s = $1;
- $s =~ s/$;//g; # Remove any comments
+ $s =~ s/$;//g; # Remove any comments
if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
ERROR("TRAILING_STATEMENTS",
"trailing statements should be on next line\n" . $herecurr);
@@ -4237,33 +5837,35 @@ sub process {
while ($line =~ m{($Constant|$Lval)}g) {
my $var = $1;
-#gcc binary extension
- if ($var =~ /^$Binary$/) {
- if (WARN("GCC_BINARY_CONSTANT",
- "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) &&
- $fix) {
- my $hexval = sprintf("0x%x", oct($var));
- $fixed[$fixlinenr] =~
- s/\b$var\b/$hexval/;
- }
- }
-
#CamelCase
if ($var !~ /^$Constant$/ &&
$var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore some autogenerated defines and enum values
+ $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ &&
#Ignore Page<foo> variants
$var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ &&
-#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show)
- $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ &&
-#Ignore some three character SI units explicitly, like MiB and KHz
+#ODP ignores
$var !~ /\bCU_/ &&
+ $var !~ /\bPRI[diux]PTR/ &&
+ $var !~ /\bPRI[diux]8/ &&
+ $var !~ /\bPRI[diux]16/ &&
$var !~ /\bPRI[diux]32/ &&
$var !~ /\bPRI[diux]64/ &&
$var !~ /\bSCN[diux]8/ &&
+ $var !~ /\bSCN[diux]16/ &&
$var !~ /\bSCN[diux]32/ &&
$var !~ /\bSCN[diux]64/ &&
+#Ignore onnxruntime CamelCase usage
+ $var !~ /\bOrt*/ &&
+ $var !~ /\bort_api->*/ &&
+#Ignore ETHTOOL_LINK_MODE_<foo> variants
+ $var !~ /^ETHTOOL_LINK_MODE_/ &&
+#Ignore SI style variants like nS, mV and dB
+#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE)
+ $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ &&
+#Ignore some three character SI units explicitly, like MiB and KHz
$var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
- while ($var =~ m{($Ident)}g) {
+ while ($var =~ m{\b($Ident)}g) {
my $word = $1;
next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/);
if ($check) {
@@ -4331,27 +5933,41 @@ sub process {
#print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
$has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/);
- $has_arg_concat = 1 if ($ctx =~ /\#\#/);
+ $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/);
+
+ $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//;
+ my $define_args = $1;
+ my $define_stmt = $dstat;
+ my @def_args = ();
+
+ if (defined $define_args && $define_args ne "") {
+ $define_args = substr($define_args, 1, length($define_args) - 2);
+ $define_args =~ s/\s*//g;
+ $define_args =~ s/\\\+?//g;
+ @def_args = split(",", $define_args);
+ }
- $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//;
$dstat =~ s/$;//g;
$dstat =~ s/\\\n.//g;
$dstat =~ s/^\s*//s;
$dstat =~ s/\s*$//s;
# Flatten any parentheses and braces
- while ($dstat =~ s/\([^\(\)]*\)/1/ ||
- $dstat =~ s/\{[^\{\}]*\}/1/ ||
- $dstat =~ s/\[[^\[\]]*\]/1/)
+ while ($dstat =~ s/\([^\(\)]*\)/1u/ ||
+ $dstat =~ s/\{[^\{\}]*\}/1u/ ||
+ $dstat =~ s/.\[[^\[\]]*\]/1u/)
{
}
- # Flatten any obvious string concatentation.
- while ($dstat =~ s/("X*")\s*$Ident/$1/ ||
- $dstat =~ s/$Ident\s*("X*")/$1/)
+ # Flatten any obvious string concatenation.
+ while ($dstat =~ s/($String)\s*$Ident/$1/ ||
+ $dstat =~ s/$Ident\s*($String)/$1/)
{
}
+ # Make asm volatile uses seem like a generic function
+ $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g;
+
my $exceptions = qr{
$Declare|
module_param_named|
@@ -4362,9 +5978,15 @@ sub process {
union|
struct|
\.$Ident\s*=\s*|
- ^\"|\"$
+ ^\"|\"$|
+ ^\[
}x;
#print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+
+ $ctx =~ s/\n*$//;
+ my $stmt_cnt = statement_rawlines($ctx);
+ my $herectx = get_stat_here($linenr, $stmt_cnt, $here);
+
if ($dstat ne '' &&
$dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(),
$dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo();
@@ -4373,45 +5995,82 @@ sub process {
$dstat !~ /$exceptions/ &&
$dstat !~ /^\.$Ident\s*=/ && # .foo =
$dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo
+ $dstat !~ /^case\b/ && # case ...
$dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...)
+ $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ && # while (...) {...}
$dstat !~ /^for\s*$Constant$/ && # for (...)
$dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar()
$dstat !~ /^do\s*{/ && # do {...
$dstat !~ /^\(\{/ && # ({...
$ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
{
- $ctx =~ s/\n*$//;
- my $herectx = $here . "\n";
- my $cnt = statement_rawlines($ctx);
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
-
- if ($dstat =~ /;/) {
+ if ($dstat =~ /^\s*if\b/) {
+ ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+ "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
+ } elsif ($dstat =~ /;/) {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
"Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
} else {
ERROR("COMPLEX_MACRO",
"Macros with complex values should be enclosed in parentheses\n" . "$herectx");
}
+
+ }
+
+ # Make $define_stmt single line, comment-free, etc
+ my @stmt_array = split('\n', $define_stmt);
+ my $first = 1;
+ $define_stmt = "";
+ foreach my $l (@stmt_array) {
+ $l =~ s/\\$//;
+ if ($first) {
+ $define_stmt = $l;
+ $first = 0;
+ } elsif ($l =~ /^[\+ ]/) {
+ $define_stmt .= substr($l, 1);
+ }
+ }
+ $define_stmt =~ s/$;//g;
+ $define_stmt =~ s/\s+/ /g;
+ $define_stmt = trim($define_stmt);
+
+# check if any macro arguments are reused (ignore '...' and 'type')
+ foreach my $arg (@def_args) {
+ next if ($arg =~ /\.\.\./);
+ next if ($arg =~ /^type$/i);
+ my $tmp_stmt = $define_stmt;
+ $tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+ $tmp_stmt =~ s/\#+\s*$arg\b//g;
+ $tmp_stmt =~ s/\b$arg\s*\#\#//g;
+ my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g;
+ if ($use_cnt > 1) {
+ CHK("MACRO_ARG_REUSE",
+ "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx");
+ }
+# check if any macro arguments may have other precedence issues
+ if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m &&
+ ((defined($1) && $1 ne ',') ||
+ (defined($2) && $2 ne ','))) {
+ CHK("MACRO_ARG_PRECEDENCE",
+ "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
+ }
}
# check for macros with flow control, but without ## concatenation
# ## concatenation is commonly a macro that defines a function so ignore those
if ($has_flow_statement && !$has_arg_concat) {
- my $herectx = $here . "\n";
my $cnt = statement_rawlines($ctx);
+ my $herectx = get_stat_here($linenr, $cnt, $here);
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
WARN("MACRO_WITH_FLOW_CONTROL",
"Macros with flow control statements should be avoided\n" . "$herectx");
}
# check for line continuations outside of #defines, preprocessor #, and asm
+ } elsif ($realfile =~ m@/vmlinux.lds.h$@) {
+ $line =~ s/(\w+)/$maybe_linker_symbol{$1}++/ge;
+ #print "REAL: $realfile\nln: $line\nkeys:", sort keys %maybe_linker_symbol;
} else {
if ($prevline !~ /^..*\\$/ &&
$line !~ /^\+\s*\#.*\\$/ && # preprocessor
@@ -4425,7 +6084,7 @@ sub process {
# do {} while (0) macro tests:
# single-statement macros do not need to be enclosed in do while (0) loop,
# macro should not end with a semicolon
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$realfile !~ m@/vmlinux.lds.h$@ &&
$line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
my $ln = $linenr;
@@ -4445,11 +6104,7 @@ sub process {
$ctx =~ s/\n*$//;
my $cnt = statement_rawlines($ctx);
- my $herectx = $here . "\n";
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
+ my $herectx = get_stat_here($linenr, $cnt, $here);
if (($stmts =~ tr/;/;/) == 1 &&
$stmts !~ /^\s*(if|while|for|switch)\b/) {
@@ -4463,27 +6118,13 @@ sub process {
} elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) {
$ctx =~ s/\n*$//;
my $cnt = statement_rawlines($ctx);
- my $herectx = $here . "\n";
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
+ my $herectx = get_stat_here($linenr, $cnt, $here);
WARN("TRAILING_SEMICOLON",
"macros should not use a trailing semicolon\n" . "$herectx");
}
}
-# make sure symbols are always wrapped with VMLINUX_SYMBOL() ...
-# all assignments may have only one of the following with an assignment:
-# .
-# ALIGN(...)
-# VMLINUX_SYMBOL(...)
- if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) {
- WARN("MISSING_VMLINUX_SYMBOL",
- "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr);
- }
-
# check for redundant bracing round if etc
if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) {
my ($level, $endln, @chunks) =
@@ -4590,18 +6231,20 @@ sub process {
}
}
if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) {
- my $herectx = $here . "\n";
my $cnt = statement_rawlines($block);
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
+ my $herectx = get_stat_here($linenr, $cnt, $here);
WARN("BRACES",
"braces {} are not necessary for single statement blocks\n" . $herectx);
}
}
+# check for single line unbalanced braces
+ if ($sline =~ /^.\s*\}\s*else\s*$/ ||
+ $sline =~ /^.\s*else\s*\{\s*$/) {
+ CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr);
+ }
+
# check for unnecessary blank lines around braces
if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) {
if (CHK("BRACES",
@@ -4622,14 +6265,14 @@ sub process {
my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b};
if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) {
WARN("VOLATILE",
- "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr);
+ "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr);
}
# Check for user-visible strings broken across lines, which breaks the ability
# to grep for the string. Make exceptions when the previous string ends in a
# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{'
# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value
- if ($line =~ /^\+\s*"[X\t]*"/ &&
+ if ($line =~ /^\+\s*$String/ &&
$prevline =~ /"\s*$/ &&
$prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) {
if (WARN("SPLIT_STRING",
@@ -4664,6 +6307,29 @@ sub process {
"break quoted strings at a space character\n" . $hereprev);
}
+# check for an embedded function name in a string when the function is known
+# This does not work very well for -f --file checking as it depends on patch
+# context providing the function name or a single line form for in-file
+# function declarations
+ if ($line =~ /^\+.*$String/ &&
+ defined($context_function) &&
+ get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
+ length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
+ WARN("EMBEDDED_FUNCTION_NAME",
+ "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
+ }
+
+# check for unnecessary function tracing like uses
+# This does not use $logFunctions because there are many instances like
+# 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions
+ if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) {
+ if (WARN("TRACING_LOGGING",
+ "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) &&
+ $fix) {
+ fix_delete_line($fixlinenr, $rawline);
+ }
+ }
+
# check for spaces before a quoted newline
if ($rawline =~ /^.*\".*\s\\n/) {
if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
@@ -4675,48 +6341,104 @@ sub process {
}
# concatenated string without spaces between elements
- if ($line =~ /"X+"[A-Z_]+/ || $line =~ /[A-Z_]+"X+"/) {
- CHK("CONCATENATED_STRING",
- "Concatenated strings should use spaces between elements\n" . $herecurr);
+ if ($line =~ /$String[A-Z_]/ ||
+ ($line =~ /([A-Za-z0-9_]+)$String/ && $1 !~ /^[Lu]$/)) {
+ if (CHK("CONCATENATED_STRING",
+ "Concatenated strings should use spaces between elements\n" . $herecurr) &&
+ $fix) {
+ while ($line =~ /($String)/g) {
+ my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+ $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/;
+ $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/;
+ }
+ }
}
# uncoalesced string fragments
- if ($line =~ /"X*"\s*"/) {
- WARN("STRING_FRAGMENTS",
- "Consecutive strings are generally better as a single string\n" . $herecurr);
+ if ($line =~ /$String\s*[Lu]?"/) {
+ if (WARN("STRING_FRAGMENTS",
+ "Consecutive strings are generally better as a single string\n" . $herecurr) &&
+ $fix) {
+ while ($line =~ /($String)(?=\s*")/g) {
+ my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+ $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e;
+ }
+ }
}
-# check for %L{u,d,i} in strings
- my $string;
+# check for non-standard and hex prefixed decimal printf formats
+ my $show_L = 1; #don't show the same defect twice
+ my $show_Z = 1;
while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
- $string = substr($rawline, $-[1], $+[1] - $-[1]);
+ my $string = substr($rawline, $-[1], $+[1] - $-[1]);
$string =~ s/%%/__/g;
- if ($string =~ /(?<!%)%L[udi]/) {
+ # check for %L
+ if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) {
WARN("PRINTF_L",
- "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr);
- last;
+ "\%L$1 is non-standard C, use %ll$1\n" . $herecurr);
+ $show_L = 0;
+ }
+ # check for %Z
+ if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) {
+ WARN("PRINTF_Z",
+ "%Z$1 is non-standard C, use %z$1\n" . $herecurr);
+ $show_Z = 0;
+ }
+ # check for 0x<decimal>
+ if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) {
+ ERROR("PRINTF_0XDECIMAL",
+ "Prefixing 0x with decimal output is defective\n" . $herecurr);
}
}
# check for line continuations in quoted strings with odd counts of "
- if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) {
+ if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) {
WARN("LINE_CONTINUATIONS",
"Avoid line continuations in quoted strings\n" . $herecurr);
}
# warn about #if 0
if ($line =~ /^.\s*\#\s*if\s+0\b/) {
- CHK("REDUNDANT_CODE",
- "if this code is redundant consider removing it\n" .
- $herecurr);
+ WARN("IF_0",
+ "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr);
+ }
+
+# warn about #if 1
+ if ($line =~ /^.\s*\#\s*if\s+1\b/) {
+ WARN("IF_1",
+ "Consider removing the #if 1 and its #endif\n" . $herecurr);
}
# check for needless "if (<foo>) fn(<foo>)" uses
if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) {
- my $expr = '\s*\(\s*' . quotemeta($1) . '\s*\)\s*;';
- if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?)$expr/) {
- WARN('NEEDLESS_IF',
- "$1(NULL) is safe and this check is probably not required\n" . $hereprev);
+ my $tested = quotemeta($1);
+ my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;';
+ if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) {
+ my $func = $1;
+ if (WARN('NEEDLESS_IF',
+ "$func(NULL) is safe and this check is probably not required\n" . $hereprev) &&
+ $fix) {
+ my $do_fix = 1;
+ my $leading_tabs = "";
+ my $new_leading_tabs = "";
+ if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) {
+ $leading_tabs = $1;
+ } else {
+ $do_fix = 0;
+ }
+ if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) {
+ $new_leading_tabs = $1;
+ if (length($leading_tabs) + 1 ne length($new_leading_tabs)) {
+ $do_fix = 0;
+ }
+ } else {
+ $do_fix = 0;
+ }
+ if ($do_fix) {
+ fix_delete_line($fixlinenr - 1, $prevrawline);
+ $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/;
+ }
+ }
}
}
@@ -4731,7 +6453,8 @@ sub process {
my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0);
# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n");
- if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) {
+ if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ &&
+ $s !~ /\b__GFP_NOWARN\b/ ) {
WARN("OOM_MESSAGE",
"Possible unnecessary 'out of memory' message\n" . $hereprev);
}
@@ -4748,8 +6471,36 @@ sub process {
}
}
+# check for logging continuations
+ if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) {
+ WARN("LOGGING_CONTINUATION",
+ "Avoid logging continuation uses where feasible\n" . $herecurr);
+ }
+
+# check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions
+ if (defined $stat &&
+ $line =~ /\b$logFunctions\s*\(/ &&
+ index($stat, '"') >= 0) {
+ my $lc = $stat =~ tr@\n@@;
+ $lc = $lc + $linenr;
+ my $stat_real = get_stat_real($linenr, $lc);
+ pos($stat_real) = index($stat_real, '"');
+ while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) {
+ my $pspec = $1;
+ my $h = $2;
+ my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@;
+ if (WARN("UNNECESSARY_MODIFIER",
+ "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") &&
+ $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) {
+ my $nspec = $pspec;
+ $nspec =~ s/h//g;
+ $fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/;
+ }
+ }
+ }
+
# check for mask then right shift without a parentheses
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ &&
$4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so
WARN("MASK_THEN_SHIFT",
@@ -4757,7 +6508,7 @@ sub process {
}
# check for pointer comparisons to NULL
- if ($^V && $^V ge 5.10.0) {
+ if ($perl_version_ok) {
while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) {
my $val = $1;
my $equal = "!";
@@ -4846,7 +6597,7 @@ sub process {
# ignore udelay's < 10, however
if (! ($delay < 10) ) {
CHK("USLEEP_RANGE",
- "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+ "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
}
if ($delay > 2000) {
WARN("LONG_UDELAY",
@@ -4858,7 +6609,7 @@ sub process {
if ($line =~ /\bmsleep\s*\((\d+)\);/) {
if ($1 < 20) {
WARN("MSLEEP",
- "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+ "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
}
}
@@ -4902,22 +6653,77 @@ sub process {
}
}
# check for memory barriers without a comment.
- if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) {
+
+ my $barriers = qr{
+ mb|
+ rmb|
+ wmb
+ }x;
+ my $barrier_stems = qr{
+ mb__before_atomic|
+ mb__after_atomic|
+ store_release|
+ load_acquire|
+ store_mb|
+ (?:$barriers)
+ }x;
+ my $all_barriers = qr{
+ (?:$barriers)|
+ smp_(?:$barrier_stems)|
+ virt_(?:$barrier_stems)
+ }x;
+
+ if ($line =~ /\b(?:$all_barriers)\s*\(/) {
if (!ctx_has_comment($first_line, $linenr)) {
WARN("MEMORY_BARRIER",
"memory barrier without comment\n" . $herecurr);
}
}
+
+ my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x;
+
+ if ($realfile !~ m@^include/asm-generic/@ &&
+ $realfile !~ m@/barrier\.h$@ &&
+ $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ &&
+ $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) {
+ WARN("MEMORY_BARRIER",
+ "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr);
+ }
+
+# check for waitqueue_active without a comment.
+ if ($line =~ /\bwaitqueue_active\s*\(/) {
+ if (!ctx_has_comment($first_line, $linenr)) {
+ WARN("WAITQUEUE_ACTIVE",
+ "waitqueue_active without comment\n" . $herecurr);
+ }
+ }
+
+# check for data_race without a comment.
+ if ($line =~ /\bdata_race\s*\(/) {
+ if (!ctx_has_comment($first_line, $linenr)) {
+ WARN("DATA_RACE",
+ "data_race without comment\n" . $herecurr);
+ }
+ }
+
# check of hardware specific defines
if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
CHK("ARCH_DEFINES",
"architecture specific defines should be avoided\n" . $herecurr);
}
+# check that the storage class is not after a type
+ if ($line =~ /\b($Type)\s+($Storage)\b/) {
+ WARN("STORAGE_CLASS",
+ "storage class '$2' should be located before type '$1'\n" . $herecurr);
+ }
# Check that the storage class is at the beginning of a declaration
- if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) {
+ if ($line =~ /\b$Storage\b/ &&
+ $line !~ /^.\s*$Storage/ &&
+ $line =~ /^.\s*(.+?)\$Storage\s/ &&
+ $1 !~ /[\,\)]\s*$/) {
WARN("STORAGE_CLASS",
- "storage class should be at the beginning of the declaration\n" . $herecurr)
+ "storage class should be at the beginning of the declaration\n" . $herecurr);
}
# check the location of the inline attribute, that it is between
@@ -4939,43 +6745,73 @@ sub process {
}
}
-# Check for __attribute__ packed, prefer __packed
- if ($realfile !~ m@\binclude/uapi/@ &&
- $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
- WARN("PREFER_PACKED",
- "__packed is preferred over __attribute__((packed))\n" . $herecurr);
- }
-
-# Check for __attribute__ aligned, prefer __aligned
- if ($realfile !~ m@\binclude/uapi/@ &&
- $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) {
- WARN("PREFER_ALIGNED",
- "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr);
- }
-
-# Check for __attribute__ format(printf, prefer __printf
+# Check for compiler attributes
if ($realfile !~ m@\binclude/uapi/@ &&
- $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) {
- if (WARN("PREFER_PRINTF",
- "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex;
-
+ $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) {
+ my $attr = $1;
+ $attr =~ s/\s*\(\s*(.*)\)\s*/$1/;
+
+ my %attr_list = (
+ "alias" => "__alias",
+ "aligned" => "__aligned",
+ "always_inline" => "__always_inline",
+ "assume_aligned" => "__assume_aligned",
+ "cold" => "__cold",
+ "const" => "__attribute_const__",
+ "copy" => "__copy",
+ "designated_init" => "__designated_init",
+ "externally_visible" => "__visible",
+ "format" => "printf|scanf",
+ "gnu_inline" => "__gnu_inline",
+ "malloc" => "__malloc",
+ "mode" => "__mode",
+ "no_caller_saved_registers" => "__no_caller_saved_registers",
+ "noclone" => "__noclone",
+ "noinline" => "noinline",
+ "nonstring" => "__nonstring",
+ "noreturn" => "__noreturn",
+ "packed" => "__packed",
+ "pure" => "__pure",
+ "section" => "__section",
+ "used" => "__used",
+ "weak" => "__weak"
+ );
+
+ while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) {
+ my $orig_attr = $1;
+ my $params = '';
+ $params = $2 if defined($2);
+ my $curr_attr = $orig_attr;
+ $curr_attr =~ s/^[\s_]+|[\s_]+$//g;
+ if (exists($attr_list{$curr_attr})) {
+ my $new = $attr_list{$curr_attr};
+ if ($curr_attr eq "format" && $params) {
+ $params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/;
+ $new = "__$1\($2";
+ } else {
+ $new = "$new$params";
+ }
+ if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+ "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) &&
+ $fix) {
+ my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?';
+ $fixed[$fixlinenr] =~ s/$remove//;
+ $fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/;
+ $fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/;
+ $fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//;
+ }
+ }
}
- }
-# Check for __attribute__ format(scanf, prefer __scanf
- if ($realfile !~ m@\binclude/uapi/@ &&
- $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) {
- if (WARN("PREFER_SCANF",
- "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex;
+ # Check for __attribute__ unused, prefer __always_unused or __maybe_unused
+ if ($attr =~ /^_*unused/) {
+ WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+ "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr);
}
}
# Check for __attribute__ weak, or __weak declarations (may have link issues)
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ &&
($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ ||
$line =~ /\b__weak\b/)) {
@@ -4983,6 +6819,45 @@ sub process {
"Using weak declarations can have unintended link defects\n" . $herecurr);
}
+# check for c99 types like uint8_t used outside of uapi/ and tools/
+ if ($realfile !~ m@\binclude/uapi/@ &&
+ $realfile !~ m@\btools/@ &&
+ $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) {
+ my $type = $1;
+ if ($type =~ /\b($typeC99Typedefs)\b/) {
+ $type = $1;
+ my $kernel_type = 'u';
+ $kernel_type = 's' if ($type =~ /^_*[si]/);
+ $type =~ /(\d+)/;
+ $kernel_type .= $1;
+ if (CHK("PREFER_KERNEL_TYPES",
+ "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/;
+ }
+ }
+ }
+
+# check for cast of C90 native int or longer types constants
+ if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) {
+ my $cast = $1;
+ my $const = $2;
+ my $suffix = "";
+ my $newconst = $const;
+ $newconst =~ s/${Int_type}$//;
+ $suffix .= 'U' if ($cast =~ /\bunsigned\b/);
+ if ($cast =~ /\blong\s+long\b/) {
+ $suffix .= 'LL';
+ } elsif ($cast =~ /\blong\b/) {
+ $suffix .= 'L';
+ }
+ if (WARN("TYPECAST_INT_CONSTANT",
+ "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/;
+ }
+ }
+
# check for sizeof(&)
if ($line =~ /\bsizeof\s*\(\s*\&/) {
WARN("SIZEOF_ADDRESS",
@@ -5017,10 +6892,66 @@ sub process {
}
}
+# check for vsprintf extension %p<foo> misuses
+ if ($perl_version_ok &&
+ defined $stat &&
+ $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
+ $1 !~ /^_*volatile_*$/) {
+ my $stat_real;
+
+ my $lc = $stat =~ tr@\n@@;
+ $lc = $lc + $linenr;
+ for (my $count = $linenr; $count <= $lc; $count++) {
+ my $specifier;
+ my $extension;
+ my $qualifier;
+ my $bad_specifier = "";
+ my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
+ $fmt =~ s/%%//g;
+
+ while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
+ $specifier = $1;
+ $extension = $2;
+ $qualifier = $3;
+ if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ ||
+ ($extension eq "f" &&
+ defined $qualifier && $qualifier !~ /^w/) ||
+ ($extension eq "4" &&
+ defined $qualifier && $qualifier !~ /^cc/)) {
+ $bad_specifier = $specifier;
+ last;
+ }
+ if ($extension eq "x" && !defined($stat_real)) {
+ if (!defined($stat_real)) {
+ $stat_real = get_stat_real($linenr, $lc);
+ }
+ WARN("VSPRINTF_SPECIFIER_PX",
+ "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n");
+ }
+ }
+ if ($bad_specifier ne "") {
+ my $stat_real = get_stat_real($linenr, $lc);
+ my $msg_level = \&WARN;
+ my $ext_type = "Invalid";
+ my $use = "";
+ if ($bad_specifier =~ /p[Ff]/) {
+ $use = " - use %pS instead";
+ $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/);
+ } elsif ($bad_specifier =~ /pA/) {
+ $use = " - '%pA' is only intended to be used from Rust code";
+ $msg_level = \&ERROR;
+ }
+
+ &{$msg_level}("VSPRINTF_POINTER_EXTENSION",
+ "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n");
+ }
+ }
+ }
+
# Check for misused memsets
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
- $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/s) {
+ $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) {
my $ms_addr = $2;
my $ms_val = $7;
@@ -5035,8 +6966,87 @@ sub process {
}
}
+# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar)
+# if ($perl_version_ok &&
+# defined $stat &&
+# $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+# if (WARN("PREFER_ETHER_ADDR_COPY",
+# "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
+# $fix) {
+# $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
+# }
+# }
+
+# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar)
+# if ($perl_version_ok &&
+# defined $stat &&
+# $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+# WARN("PREFER_ETHER_ADDR_EQUAL",
+# "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
+# }
+
+# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr
+# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr
+# if ($perl_version_ok &&
+# defined $stat &&
+# $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#
+# my $ms_val = $7;
+#
+# if ($ms_val =~ /^(?:0x|)0+$/i) {
+# if (WARN("PREFER_ETH_ZERO_ADDR",
+# "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
+# $fix) {
+# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
+# }
+# } elsif ($ms_val =~ /^(?:0xff|255)$/i) {
+# if (WARN("PREFER_ETH_BROADCAST_ADDR",
+# "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
+# $fix) {
+# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
+# }
+# }
+# }
+
+# strcpy uses that should likely be strscpy
+ if ($line =~ /\bstrcpy\s*\(/) {
+ WARN("STRCPY",
+ "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr);
+ }
+
+# strlcpy uses that should likely be strscpy
+ if ($line =~ /\bstrlcpy\s*\(/) {
+ WARN("STRLCPY",
+ "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr);
+ }
+
+# strncpy uses that should likely be strscpy or strscpy_pad
+ if ($line =~ /\bstrncpy\s*\(/) {
+ WARN("STRNCPY",
+ "Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr);
+ }
+
+# ethtool_sprintf uses that should likely be ethtool_puts
+ if ($line =~ /\bethtool_sprintf\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) {
+ if (WARN("PREFER_ETHTOOL_PUTS",
+ "Prefer ethtool_puts over ethtool_sprintf with only two arguments\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bethtool_sprintf\s*\(\s*($FuncArg)\s*,\s*($FuncArg)/ethtool_puts($1, $7)/;
+ }
+ }
+
+ # use $rawline because $line loses %s via sanitization and thus we can't match against it.
+ if ($rawline =~ /\bethtool_sprintf\s*\(\s*$FuncArg\s*,\s*\"\%s\"\s*,\s*$FuncArg\s*\)/) {
+ if (WARN("PREFER_ETHTOOL_PUTS",
+ "Prefer ethtool_puts over ethtool_sprintf with standalone \"%s\" specifier\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bethtool_sprintf\s*\(\s*($FuncArg)\s*,\s*"\%s"\s*,\s*($FuncArg)/ethtool_puts($1, $7)/;
+ }
+ }
+
+
# typecasts on min/max could be min_t/max_t
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
$stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) {
if (defined $2 || defined $7) {
@@ -5060,23 +7070,23 @@ sub process {
}
# check usleep_range arguments
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
$stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) {
my $min = $1;
my $max = $7;
if ($min eq $max) {
WARN("USLEEP_RANGE",
- "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+ "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
} elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
$min > $max) {
WARN("USLEEP_RANGE",
- "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+ "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
}
}
# check for naked sscanf
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
$line =~ /\bsscanf\b/ &&
($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
@@ -5084,24 +7094,18 @@ sub process {
$stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
my $lc = $stat =~ tr@\n@@;
$lc = $lc + $linenr;
- my $stat_real = raw_line($linenr, 0);
- for (my $count = $linenr + 1; $count <= $lc; $count++) {
- $stat_real = $stat_real . "\n" . raw_line($count, 0);
- }
+ my $stat_real = get_stat_real($linenr, $lc);
WARN("NAKED_SSCANF",
"unchecked sscanf return value\n" . "$here\n$stat_real\n");
}
# check for simple sscanf that should be kstrto<foo>
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
$line =~ /\bsscanf\b/) {
my $lc = $stat =~ tr@\n@@;
$lc = $lc + $linenr;
- my $stat_real = raw_line($linenr, 0);
- for (my $count = $linenr + 1; $count <= $lc; $count++) {
- $stat_real = $stat_real . "\n" . raw_line($count, 0);
- }
+ my $stat_real = get_stat_real($linenr, $lc);
if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) {
my $format = $6;
my $count = $format =~ tr@%@%@;
@@ -5134,8 +7138,7 @@ sub process {
if (defined $cond) {
substr($s, 0, length($cond), '');
}
- if ($s =~ /^\s*;/ &&
- $function_name ne 'uninitialized_var')
+ if ($s =~ /^\s*;/)
{
WARN("AVOID_EXTERNS",
"externs should be avoided in .c files\n" . $herecurr);
@@ -5147,43 +7150,98 @@ sub process {
}
} elsif ($realfile =~ /\.c$/ && defined $stat &&
+ $stat =~ /^\+extern struct\s+(\w+)\s+(\w+)\[\];/)
+ {
+ my ($st_type, $st_name) = ($1, $2);
+
+ for my $s (keys %maybe_linker_symbol) {
+ #print "Linker symbol? $st_name : $s\n";
+ goto LIKELY_LINKER_SYMBOL
+ if $st_name =~ /$s/;
+ }
+ WARN("AVOID_EXTERNS",
+ "found a file-scoped extern type:$st_type name:$st_name in .c file\n"
+ . "is this a linker symbol ?\n" . $herecurr);
+ LIKELY_LINKER_SYMBOL:
+
+ } elsif ($realfile =~ /\.c$/ && defined $stat &&
$stat =~ /^.\s*extern\s+/)
{
WARN("AVOID_EXTERNS",
"externs should be avoided in .c files\n" . $herecurr);
}
+# check for function declarations that have arguments without identifier names
+ if (defined $stat &&
+ $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s &&
+ $1 ne "void") {
+ my $args = trim($1);
+ while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
+ my $arg = trim($1);
+ if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) {
+ WARN("FUNCTION_ARGUMENTS",
+ "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
+ }
+ }
+ }
+
+# check for function definitions
+ if ($perl_version_ok &&
+ defined $stat &&
+ $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) {
+ $context_function = $1;
+
+# check for multiline function definition with misplaced open brace
+ my $ok = 0;
+ my $cnt = statement_rawlines($stat);
+ my $herectx = $here . "\n";
+ for (my $n = 0; $n < $cnt; $n++) {
+ my $rl = raw_line($linenr, $n);
+ $herectx .= $rl . "\n";
+ $ok = 1 if ($rl =~ /^[ \+]\{/);
+ $ok = 1 if ($rl =~ /\{/ && $n == 0);
+ last if $rl =~ /^[ \+].*\{/;
+ }
+ if (!$ok) {
+ ERROR("OPEN_BRACE",
+ "open brace '{' following function definitions go on the next line\n" . $herectx);
+ }
+ }
+
# checks for new __setup's
if ($rawline =~ /\b__setup\("([^"]*)"/) {
my $name = $1;
if (!grep(/$name/, @setup_docs)) {
CHK("UNDOCUMENTED_SETUP",
- "__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr);
+ "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr);
}
}
-# check for pointless casting of kmalloc return
- if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) {
+# check for pointless casting of alloc functions
+ if ($line =~ /\*\s*\)\s*$allocFunctions\b/) {
WARN("UNNECESSARY_CASTS",
"unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
}
# alloc style
# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
- if ($^V && $^V ge 5.10.0 &&
- $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
+ if ($perl_version_ok &&
+ $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
CHK("ALLOC_SIZEOF_STRUCT",
"Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
}
-# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc
- if ($^V && $^V ge 5.10.0 &&
- $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
+# check for (kv|k)[mz]alloc with multiplies that could be kmalloc_array/kvmalloc_array/kvcalloc/kcalloc
+ if ($perl_version_ok &&
+ defined $stat &&
+ $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
my $oldfunc = $3;
my $a1 = $4;
my $a2 = $10;
my $newfunc = "kmalloc_array";
+ $newfunc = "kvmalloc_array" if ($oldfunc eq "kvmalloc");
+ $newfunc = "kvcalloc" if ($oldfunc eq "kvzalloc");
$newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
my $r1 = $a1;
my $r2 = $a2;
@@ -5193,24 +7251,28 @@ sub process {
}
if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ &&
!($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) {
+ my $cnt = statement_rawlines($stat);
+ my $herectx = get_stat_here($linenr, $cnt, $here);
+
if (WARN("ALLOC_WITH_MULTIPLY",
- "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) &&
+ "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
+ $cnt == 1 &&
$fix) {
- $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
-
+ $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
}
}
}
# check for krealloc arg reuse
- if ($^V && $^V ge 5.10.0 &&
- $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) {
+ if ($perl_version_ok &&
+ $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ &&
+ $1 eq $3) {
WARN("KREALLOC_ARG_REUSE",
"Reusing the krealloc arg is almost always a bug\n" . $herecurr);
}
# check for alloc argument mismatch
- if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) {
+ if ($line =~ /\b((?:devm_)?((?:k|kv)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/) {
WARN("ALLOC_ARRAY_ARGS",
"$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
}
@@ -5224,8 +7286,9 @@ sub process {
}
}
-# check for #defines like: 1 << <digit> that could be BIT(digit)
- if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
+# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
+ if ($realfile !~ m@^include/uapi/@ &&
+ $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
my $ull = "";
$ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
if (CHK("BIT_MACRO",
@@ -5235,41 +7298,51 @@ sub process {
}
}
-# check for case / default statements not preceded by break/fallthrough/switch
- if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
- my $has_break = 0;
- my $has_statement = 0;
- my $count = 0;
- my $prevline = $linenr;
- while ($prevline > 1 && ($file || $count < 3) && !$has_break) {
- $prevline--;
- my $rline = $rawlines[$prevline - 1];
- my $fline = $lines[$prevline - 1];
- last if ($fline =~ /^\@\@/);
- next if ($fline =~ /^\-/);
- next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
- $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
- next if ($fline =~ /^.[\s$;]*$/);
- $has_statement = 1;
- $count++;
- $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/);
- }
- if (!$has_break && $has_statement) {
- WARN("MISSING_BREAK",
- "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr);
+# check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too)
+ if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) {
+ WARN("IS_ENABLED_CONFIG",
+ "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr);
+ }
+
+# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
+ if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+ my $config = $1;
+ if (WARN("PREFER_IS_ENABLED",
+ "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
+ }
+ }
+
+# check for /* fallthrough */ like comment, prefer fallthrough;
+ my @fallthroughs = (
+ 'fallthrough',
+ '@fallthrough@',
+ 'lint -fallthrough[ \t]*',
+ 'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)',
+ '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?',
+ 'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+ 'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+ );
+ if ($raw_comment ne '') {
+ foreach my $ft (@fallthroughs) {
+ if ($raw_comment =~ /$ft/) {
+ my $msg_level = \&WARN;
+ $msg_level = \&CHK if ($file);
+ &{$msg_level}("PREFER_FALLTHROUGH",
+ "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr);
+ last;
+ }
}
}
# check for switch/default statements without a break;
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
defined $stat &&
$stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
- my $ctx = '';
- my $herectx = $here . "\n";
my $cnt = statement_rawlines($stat);
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
+ my $herectx = get_stat_here($linenr, $cnt, $here);
+
WARN("DEFAULT_NO_BREAK",
"switch default: should use break\n" . $herectx);
}
@@ -5340,62 +7413,39 @@ sub process {
"please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
}
+# check for spin_is_locked(), suggest lockdep instead
+ if ($line =~ /\bspin_is_locked\(/) {
+ WARN("USE_LOCKDEP",
+ "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr);
+ }
+
+# check for deprecated apis
+ if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) {
+ my $deprecated_api = $1;
+ my $new_api = $deprecated_apis{$deprecated_api};
+ WARN("DEPRECATED_API",
+ "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr);
+ }
+
# check for various structs that are normally const (ops, kgdb, device_tree)
- my $const_structs = qr{
- acpi_dock_ops|
- address_space_operations|
- backlight_ops|
- block_device_operations|
- dentry_operations|
- dev_pm_ops|
- dma_map_ops|
- extent_io_ops|
- file_lock_operations|
- file_operations|
- hv_ops|
- ide_dma_ops|
- intel_dvo_dev_ops|
- item_operations|
- iwl_ops|
- kgdb_arch|
- kgdb_io|
- kset_uevent_ops|
- lock_manager_operations|
- microcode_ops|
- mtrr_ops|
- neigh_ops|
- nlmsvc_binding|
- of_device_id|
- pci_raw_ops|
- pipe_buf_operations|
- platform_hibernation_ops|
- platform_suspend_ops|
- proto_ops|
- rpc_pipe_ops|
- seq_operations|
- snd_ac97_build_ops|
- soc_pcmcia_socket_ops|
- stacktrace_ops|
- sysfs_ops|
- tty_operations|
- uart_ops|
- usb_mon_operations|
- wd_ops}x;
- if ($line !~ /\bconst\b/ &&
- $line =~ /\bstruct\s+($const_structs)\b/) {
+# and avoid what seem like struct definitions 'struct foo {'
+ if (defined($const_structs) &&
+ $line !~ /\bconst\b/ &&
+ $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
WARN("CONST_STRUCT",
- "struct $1 should normally be const\n" .
- $herecurr);
+ "struct $1 should normally be const\n" . $herecurr);
}
# use of NR_CPUS is usually wrong
# ignore definitions of NR_CPUS and usage to define arrays as likely right
+# ignore designated initializers using NR_CPUS
if ($line =~ /\bNR_CPUS\b/ &&
$line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
$line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
$line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
$line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
- $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
+ $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ &&
+ $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/)
{
WARN("NR_CPUS",
"usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
@@ -5408,12 +7458,39 @@ sub process {
}
# likely/unlikely comparisons similar to "(likely(foo) > 0)"
- if ($^V && $^V ge 5.10.0 &&
+ if ($perl_version_ok &&
$line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) {
WARN("LIKELY_MISUSE",
"Using $1 should generally have parentheses around the comparison\n" . $herecurr);
}
+# return sysfs_emit(foo, fmt, ...) fmt without newline
+ if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ &&
+ substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) {
+ my $offset = $+[6] - 1;
+ if (WARN("SYSFS_EMIT",
+ "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) &&
+ $fix) {
+ substr($fixed[$fixlinenr], $offset, 0) = '\\n';
+ }
+ }
+
+# check for array definition/declarations that should use flexible arrays instead
+ if ($sline =~ /^[\+ ]\s*\}(?:\s*__packed)?\s*;\s*$/ &&
+ $prevline =~ /^\+\s*(?:\}(?:\s*__packed\s*)?|$Type)\s*$Ident\s*\[\s*(0|1)\s*\]\s*;\s*$/) {
+ if (ERROR("FLEXIBLE_ARRAY",
+ "Use C99 flexible arrays - see https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays\n" . $hereprev) &&
+ $1 == '0' && $fix) {
+ $fixed[$fixlinenr - 1] =~ s/\[\s*0\s*\]/[]/;
+ }
+ }
+
+# nested likely/unlikely calls
+ if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) {
+ WARN("LIKELY_MISUSE",
+ "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr);
+ }
+
# whine mightly about in_atomic
if ($line =~ /\bin_atomic\s*\(/) {
if ($realfile =~ m@^drivers/@) {
@@ -5425,6 +7502,30 @@ sub process {
}
}
+# Complain about RCU Tasks Trace used outside of BPF (and of course, RCU).
+ our $rcu_trace_funcs = qr{(?x:
+ rcu_read_lock_trace |
+ rcu_read_lock_trace_held |
+ rcu_read_unlock_trace |
+ call_rcu_tasks_trace |
+ synchronize_rcu_tasks_trace |
+ rcu_barrier_tasks_trace |
+ rcu_request_urgent_qs_task
+ )};
+ our $rcu_trace_paths = qr{(?x:
+ kernel/bpf/ |
+ include/linux/bpf |
+ net/bpf/ |
+ kernel/rcu/ |
+ include/linux/rcu
+ )};
+ if ($line =~ /\b($rcu_trace_funcs)\s*\(/) {
+ if ($realfile !~ m{^$rcu_trace_paths}) {
+ WARN("RCU_TASKS_TRACE",
+ "use of RCU tasks trace is incorrect outside BPF or core RCU code\n" . $herecurr);
+ }
+ }
+
# check for lockdep_set_novalidate_class
if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
$line =~ /__lockdep_no_validate__\s*\)/ ) {
@@ -5442,36 +7543,144 @@ sub process {
"Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
}
+# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO>
+# and whether or not function naming is typical and if
+# DEVICE_ATTR permissions uses are unusual too
+ if ($perl_version_ok &&
+ defined $stat &&
+ $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) {
+ my $var = $1;
+ my $perms = $2;
+ my $show = $3;
+ my $store = $4;
+ my $octal_perms = perms_to_octal($perms);
+ if ($show =~ /^${var}_show$/ &&
+ $store =~ /^${var}_store$/ &&
+ $octal_perms eq "0644") {
+ if (WARN("DEVICE_ATTR_RW",
+ "Use DEVICE_ATTR_RW\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/;
+ }
+ } elsif ($show =~ /^${var}_show$/ &&
+ $store =~ /^NULL$/ &&
+ $octal_perms eq "0444") {
+ if (WARN("DEVICE_ATTR_RO",
+ "Use DEVICE_ATTR_RO\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/;
+ }
+ } elsif ($show =~ /^NULL$/ &&
+ $store =~ /^${var}_store$/ &&
+ $octal_perms eq "0200") {
+ if (WARN("DEVICE_ATTR_WO",
+ "Use DEVICE_ATTR_WO\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/;
+ }
+ } elsif ($octal_perms eq "0644" ||
+ $octal_perms eq "0444" ||
+ $octal_perms eq "0200") {
+ my $newshow = "$show";
+ $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show");
+ my $newstore = $store;
+ $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store");
+ my $rename = "";
+ if ($show ne $newshow) {
+ $rename .= " '$show' to '$newshow'";
+ }
+ if ($store ne $newstore) {
+ $rename .= " '$store' to '$newstore'";
+ }
+ WARN("DEVICE_ATTR_FUNCTIONS",
+ "Consider renaming function(s)$rename\n" . $herecurr);
+ } else {
+ WARN("DEVICE_ATTR_PERMS",
+ "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr);
+ }
+ }
+
# Mode permission misuses where it seems decimal should be octal
# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
- if ($^V && $^V ge 5.10.0 &&
+# o Ignore module_param*(...) uses with a decimal 0 permission as that has a
+# specific definition of not visible in sysfs.
+# o Ignore proc_create*(...) uses with a decimal 0 permission as that means
+# use the default permissions
+ if ($perl_version_ok &&
+ defined $stat &&
$line =~ /$mode_perms_search/) {
foreach my $entry (@mode_permission_funcs) {
my $func = $entry->[0];
my $arg_pos = $entry->[1];
+ my $lc = $stat =~ tr@\n@@;
+ $lc = $lc + $linenr;
+ my $stat_real = get_stat_real($linenr, $lc);
+
my $skip_args = "";
if ($arg_pos > 1) {
$arg_pos--;
$skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
}
- my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]";
- if ($line =~ /$test/) {
+ my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
+ if ($stat =~ /$test/) {
my $val = $1;
$val = $6 if ($skip_args ne "");
-
- if ($val !~ /^0$/ &&
+ if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") &&
(($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
- length($val) ne 4)) {
+ ($val =~ /^$Octal$/ && length($val) ne 4))) {
ERROR("NON_OCTAL_PERMISSIONS",
- "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr);
- } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) {
+ "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
+ }
+ if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
ERROR("EXPORTED_WORLD_WRITABLE",
- "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+ "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
}
}
}
}
+
+# check for uses of S_<PERMS> that could be octal for readability
+ while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) {
+ my $oval = $1;
+ my $octal = perms_to_octal($oval);
+ if (WARN("SYMBOLIC_PERMS",
+ "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/;
+ }
+ }
+
+# validate content of MODULE_LICENSE against list from include/linux/module.h
+ if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
+ my $extracted_string = get_quoted_string($line, $rawline);
+ my $valid_licenses = qr{
+ GPL|
+ GPL\ v2|
+ GPL\ and\ additional\ rights|
+ Dual\ BSD/GPL|
+ Dual\ MIT/GPL|
+ Dual\ MPL/GPL|
+ Proprietary
+ }x;
+ if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) {
+ WARN("MODULE_LICENSE",
+ "unknown module license " . $extracted_string . "\n" . $herecurr);
+ }
+ if (!$file && $extracted_string eq '"GPL v2"') {
+ if (WARN("MODULE_LICENSE",
+ "Prefer \"GPL\" over \"GPL v2\" - see commit bf7fbeeae6db (\"module: Cure the MODULE_LICENSE \"GPL\" vs. \"GPL v2\" bogosity\")\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bMODULE_LICENSE\s*\(\s*"GPL v2"\s*\)/MODULE_LICENSE("GPL")/;
+ }
+ }
+ }
+
+# check for sysctl duplicate constants
+ if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) {
+ WARN("DUPLICATED_SYSCTL_CONST",
+ "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
+ }
}
# If we have no input at all, then there is nothing to report on
@@ -5486,19 +7695,48 @@ sub process {
exit(0);
}
- # This is not a patch, and we are are in 'no-patch' mode so
+ # This is not a patch, and we are in 'no-patch' mode so
# just keep quiet.
if (!$chk_patch && !$is_patch) {
exit(0);
}
- if (!$is_patch) {
+ if (!$is_patch && $filename !~ /cover-letter\.patch$/) {
ERROR("NOT_UNIFIED_DIFF",
"Does not appear to be a unified-diff format patch\n");
}
- if ($is_patch && $chk_signoff && $signoff == 0) {
- ERROR("MISSING_SIGN_OFF",
- "Missing Signed-off-by: line(s)\n");
+ if ($is_patch && $has_commit_log && $chk_signoff) {
+ if ($signoff == 0) {
+ ERROR("MISSING_SIGN_OFF",
+ "Missing Signed-off-by: line(s)\n");
+ } elsif ($authorsignoff != 1) {
+ # authorsignoff values:
+ # 0 -> missing sign off
+ # 1 -> sign off identical
+ # 2 -> names and addresses match, comments mismatch
+ # 3 -> addresses match, names different
+ # 4 -> names match, addresses different
+ # 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match
+
+ my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'";
+
+ if ($authorsignoff == 0) {
+ ERROR("NO_AUTHOR_SIGN_OFF",
+ "Missing Signed-off-by: line by nominal patch author '$author'\n");
+ } elsif ($authorsignoff == 2) {
+ CHK("FROM_SIGN_OFF_MISMATCH",
+ "From:/Signed-off-by: email comments mismatch: $sob_msg\n");
+ } elsif ($authorsignoff == 3) {
+ WARN("FROM_SIGN_OFF_MISMATCH",
+ "From:/Signed-off-by: email name mismatch: $sob_msg\n");
+ } elsif ($authorsignoff == 4) {
+ WARN("FROM_SIGN_OFF_MISMATCH",
+ "From:/Signed-off-by: email address mismatch: $sob_msg\n");
+ } elsif ($authorsignoff == 5) {
+ WARN("FROM_SIGN_OFF_MISMATCH",
+ "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n");
+ }
+ }
}
print report_dump();
@@ -5507,28 +7745,29 @@ sub process {
print "total: $cnt_error errors, $cnt_warn warnings, " .
(($check)? "$cnt_chk checks, " : "") .
"$cnt_lines lines checked\n";
- print "\n" if ($quiet == 0);
}
if ($quiet == 0) {
+ # If there were any defects found and not already fixing them
+ if (!$clean and !$fix) {
+ print << "EOM"
- if ($^V lt 5.10.0) {
- print("NOTE: perl $^V is not modern enough to detect all possible issues.\n");
- print("An upgrade to at least perl v5.10.0 is suggested.\n\n");
+NOTE: For some of the reported defects, checkpatch may be able to
+ mechanically convert to the typical style using --fix or --fix-inplace.
+EOM
}
-
# If there were whitespace errors which cleanpatch can fix
# then suggest that.
if ($rpt_cleaners) {
- print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n";
- print " scripts/cleanfile\n\n";
$rpt_cleaners = 0;
+ print << "EOM"
+
+NOTE: Whitespace errors detected.
+ You may wish to use scripts/cleanpatch or scripts/cleanfile
+EOM
}
}
- hash_show_words(\%use_type, "Used");
- hash_show_words(\%ignore_type, "Ignored");
-
if ($clean == 0 && $fix &&
("@rawlines" ne "@fixed" ||
$#fixed_inserted >= 0 || $#fixed_deleted >= 0)) {
@@ -5556,6 +7795,7 @@ sub process {
if (!$quiet) {
print << "EOM";
+
Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
Do _NOT_ trust the results written to this file.
@@ -5563,22 +7803,17 @@ Do _NOT_ submit these changes without inspecting them for correctness.
This EXPERIMENTAL file is simply a convenience to help rewrite patches.
No warranties, expressed or implied...
-
EOM
}
}
- if ($clean == 1 && $quiet == 0) {
- print "$vname has no obvious style problems and is ready for submission.\n"
- }
- if ($clean == 0 && $quiet == 0) {
- print << "EOM";
-$vname has style problems, please review.
-
-If any of these errors are false positives, please report
-them to the maintainer, see CHECKPATCH in MAINTAINERS.
-EOM
+ if ($quiet == 0) {
+ print "\n";
+ if ($clean == 1) {
+ print "$vname has no obvious style problems and is ready for submission.\n";
+ } else {
+ print "$vname has style problems, please review.\n";
+ }
}
-
return $clean;
-}
+} \ No newline at end of file
diff --git a/scripts/ci-checkpatches.sh b/scripts/ci-checkpatches.sh
index cb1c4e65d..4ce077342 100755
--- a/scripts/ci-checkpatches.sh
+++ b/scripts/ci-checkpatches.sh
@@ -1,17 +1,15 @@
-#!/bin/bash
+#!/bin/bash -x
PATCHES=$1
echo "Run checkpatch for ${PATCHES}"
-# Generate patches provided with $1.
-# In case of force push and range is broken
-# validate only the latest commit if it's not merge commit.
-git format-patch ${PATCHES}
-if [ $? -ne 0 ]; then
- git show --summary HEAD| grep -q '^Merge:';
- if [ $? -ne 0 ]; then
- git format-patch HEAD^;
- perl ./scripts/checkpatch.pl *.patch;
- fi;
+# Generate patches provided with $1. If commit range is not available validate
+# only the latest commit.
+
+if [ "$PATCHES" = "" ]; then
+ git format-patch -1 -M HEAD;
else
- perl ./scripts/checkpatch.pl *.patch;
+ git format-patch ${PATCHES}
fi
+
+perl ./scripts/checkpatch.pl *.patch;
+exit $?
diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh
new file mode 100755
index 000000000..4a92cfd83
--- /dev/null
+++ b/scripts/ci/build.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -e
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+./configure \
+ --host=${TARGET_ARCH} --build=${BUILD_ARCH:-x86_64-linux-gnu} \
+ --enable-dpdk \
+ --prefix=/opt/odp \
+ ${CONF}
+
+make clean
+
+make -j $(nproc)
+
+make install
+
+make installcheck
diff --git a/scripts/ci/build_arm64.sh b/scripts/ci/build_arm64.sh
new file mode 100755
index 000000000..4cac830ae
--- /dev/null
+++ b/scripts/ci/build_arm64.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+set -e
+
+export TARGET_ARCH=aarch64-linux-gnu
+if [[ $(uname -m) =~ ^(arm64|aarch64)$ ]]; then
+ export BUILD_ARCH=aarch64-linux-gnu
+fi
+
+if [ "$TARGET_ARCH" == "$BUILD_ARCH" ]; then
+ # Native build
+ if [ "${CC#clang}" != "${CC}" ] ; then
+ export CXX="clang++"
+ fi
+
+ # Required by CentOS and Rocky Linux to find DPDK install
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib64/pkgconfig
+
+else
+ # Cross compilation
+ if [ "${CC#clang}" != "${CC}" ] ; then
+ export CC="clang --target=${TARGET_ARCH}"
+ export CXX="clang++ --target=${TARGET_ARCH}"
+ else
+ export CC="${TARGET_ARCH}-gcc"
+ export CXX="${TARGET_ARCH}-g++"
+ fi
+
+ export CPPFLAGS="-I/usr/include/${TARGET_ARCH}/dpdk"
+
+ # Use target libraries
+ export PKG_CONFIG_PATH=
+ export PKG_CONFIG_LIBDIR=/usr/lib/${TARGET_ARCH}/pkgconfig:/usr/local/lib/${TARGET_ARCH}/pkgconfig
+fi
+
+# ARMv8 crypto
+export PKG_CONFIG_PATH=~/aarch64cryptolib/pkgconfig:$PKG_CONFIG_PATH
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/build_armhf.sh b/scripts/ci/build_armhf.sh
new file mode 100755
index 000000000..4ae0f19b1
--- /dev/null
+++ b/scripts/ci/build_armhf.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+set -e
+
+export TARGET_ARCH=arm-linux-gnueabihf
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CC="clang --target=${TARGET_ARCH}"
+ export CXX="clang++ --target=${TARGET_ARCH}"
+else
+ export CC="${TARGET_ARCH}-gcc"
+ export CXX="${TARGET_ARCH}-g++"
+fi
+export CFLAGS="-march=armv7-a"
+export CXXFLAGS="-march=armv7-a"
+
+# No DPDK on ARMv7
+export CONF="${CONF} --disable-dpdk"
+
+# Use target libraries
+export PKG_CONFIG_PATH=
+export PKG_CONFIG_LIBDIR=/usr/lib/${TARGET_ARCH}/pkgconfig
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/build_i386.sh b/scripts/ci/build_i386.sh
new file mode 100755
index 000000000..797dd454d
--- /dev/null
+++ b/scripts/ci/build_i386.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+export TARGET_ARCH=i686-linux-gnu
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CC="clang --target=${TARGET_ARCH}"
+ export CXX="clang++ --target=${TARGET_ARCH}"
+else
+ export CFLAGS="-m32"
+ export CXXFLAGS="-m32"
+ export LDFLAGS="-m32"
+fi
+export CPPFLAGS="-I/usr/include/i386-linux-gnu/dpdk"
+
+# Use target libraries
+export PKG_CONFIG_PATH=
+export PKG_CONFIG_LIBDIR=/usr/lib/i386-linux-gnu/pkgconfig
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/build_ppc64el.sh b/scripts/ci/build_ppc64el.sh
new file mode 100755
index 000000000..93dd36290
--- /dev/null
+++ b/scripts/ci/build_ppc64el.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+set -e
+
+export TARGET_ARCH=powerpc64le-linux-gnu
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CC="clang --target=${TARGET_ARCH}"
+ export CXX="clang++ --target=${TARGET_ARCH}"
+ # DPDK clang build broken
+ export CONF="${CONF} --disable-dpdk"
+
+ # Ignore warnings about large atomic operation performance penalty
+ export ODP_CFLAGS_EXTRA=-Wno-error=atomic-alignment
+
+else
+ export CC="${TARGET_ARCH}-gcc"
+ export CXX="${TARGET_ARCH}-g++"
+fi
+export CPPFLAGS="-I/usr/include/${TARGET_ARCH}/dpdk"
+
+# Use target libraries
+export PKG_CONFIG_PATH=
+export PKG_CONFIG_LIBDIR=/usr/lib/${TARGET_ARCH}/pkgconfig
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/build_riscv64.sh b/scripts/ci/build_riscv64.sh
new file mode 100755
index 000000000..e52505c68
--- /dev/null
+++ b/scripts/ci/build_riscv64.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+
+export TARGET_ARCH=riscv64-linux-gnu
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CC="clang --target=${TARGET_ARCH}"
+ export CXX="clang++ --target=${TARGET_ARCH}"
+else
+ export CC="${TARGET_ARCH}-gcc"
+ export CXX="${TARGET_ARCH}-g++"
+fi
+
+# Use target libraries
+export PKG_CONFIG_PATH=
+export PKG_CONFIG_LIBDIR=/usr/lib/${TARGET_ARCH}/pkgconfig
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/build_static.sh b/scripts/ci/build_static.sh
new file mode 100755
index 000000000..24d8a2d6a
--- /dev/null
+++ b/scripts/ci/build_static.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+
+CONFIG_OPT="--prefix=/opt/odp ${CONF}"
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+echo "./configure $CONFIG_OPT"
+./configure $CONFIG_OPT
+
+make clean
+
+make -j $(nproc)
+
+make install
+
+make installcheck
diff --git a/scripts/ci/build_x86_64.sh b/scripts/ci/build_x86_64.sh
new file mode 100755
index 000000000..9211bab88
--- /dev/null
+++ b/scripts/ci/build_x86_64.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -e
+
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CXX="clang++"
+fi
+
+# Required by CentOS and Rocky Linux to find DPDK install
+export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib64/pkgconfig:/usr/lib/pkgconfig/
+
+exec "$(dirname "$0")"/build.sh
diff --git a/scripts/ci/check.sh b/scripts/ci/check.sh
new file mode 100755
index 000000000..cfb9e4b55
--- /dev/null
+++ b/scripts/ci/check.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+
+echo 1500 | tee /proc/sys/vm/nr_hugepages
+mkdir -p /mnt/huge
+mount -t hugetlbfs nodev /mnt/huge
+
+"`dirname "$0"`"/build_${ARCH}.sh
+
+cd "$(dirname "$0")"/../..
+
+# Ignore possible failures there because these tests depends on measurements
+# and systems might differ in performance.
+export CI="true"
+make check
+
+umount /mnt/huge
diff --git a/scripts/ci/check_inline_timer.sh b/scripts/ci/check_inline_timer.sh
new file mode 100755
index 000000000..48800be35
--- /dev/null
+++ b/scripts/ci/check_inline_timer.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -e
+
+echo 1000 | tee /proc/sys/vm/nr_hugepages
+mkdir -p /mnt/huge
+mount -t hugetlbfs nodev /mnt/huge
+
+"`dirname "$0"`"/build_${ARCH}.sh
+
+cd "$(dirname "$0")"/../..
+
+ODP_SCHEDULER=basic ./test/validation/api/timer/timer_main
+ODP_SCHEDULER=sp ./test/validation/api/timer/timer_main
+ODP_SCHEDULER=scalable ./test/validation/api/timer/timer_main
+
+umount /mnt/huge
diff --git a/scripts/ci/check_pktio.sh b/scripts/ci/check_pktio.sh
new file mode 100755
index 000000000..3c6fd8c86
--- /dev/null
+++ b/scripts/ci/check_pktio.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+echo 1000 | tee /proc/sys/vm/nr_hugepages
+mkdir -p /mnt/huge
+mount -t hugetlbfs nodev /mnt/huge
+
+"`dirname "$0"`"/build_${ARCH}.sh
+
+cd "$(dirname "$0")"/../..
+
+./platform/linux-generic/test/validation/api/pktio/pktio_run.sh
+
+umount /mnt/huge
diff --git a/scripts/ci/coverage.sh b/scripts/ci/coverage.sh
new file mode 100755
index 000000000..c59a704e1
--- /dev/null
+++ b/scripts/ci/coverage.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+set -e
+
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CXX="clang++"
+fi
+
+echo 1000 | tee /proc/sys/vm/nr_hugepages
+mkdir -p /mnt/huge
+mount -t hugetlbfs nodev /mnt/huge
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+./configure \
+ CFLAGS="-O0 -coverage $CFLAGS" CXXFLAGS="-O0 -coverage $CXXFLAGS" LDFLAGS="--coverage $LDFLAGS" \
+ --enable-debug=full --enable-helper-linux --enable-dpdk
+export CCACHE_DISABLE=1
+make -j $(nproc)
+
+# Ignore possible failures there because these tests depends on measurements
+# and systems might differ in performance.
+export CI="true"
+
+ODP_SCHEDULER=basic make check
+
+# Run only validation tests for scalable and sp schedulers
+pushd ./test/validation/api/
+ODP_SCHEDULER=scalable CI_SKIP=pktio_test_pktin_event_sched make check
+ODP_SCHEDULER=sp make check
+popd
+
+# Convert gcno files into gcov (required by Codecov)
+find . -type f -name '*.gcno' -exec gcov -pb {} +
+
+umount /mnt/huge
diff --git a/scripts/ci/coverity.sh b/scripts/ci/coverity.sh
new file mode 100755
index 000000000..7272f8ed1
--- /dev/null
+++ b/scripts/ci/coverity.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+./configure --enable-debug=full
+
+make clean
+
+cov-build --dir cov-int make -j $(nproc)
+
+tar czf odp-coverity.tgz cov-int
+
+curl --form token="${COVERITY_TOKEN}" \
+ --form email="${COVERITY_EMAIL}" \
+ --form file=@odp-coverity.tgz \
+ --form version="${GITHUB_SHA}" \
+ --form description="GitHub Actions ODP Coverity Build" \
+ "https://scan.coverity.com/builds?project=${COVERITY_PROJECT}"
diff --git a/scripts/ci/distcheck.sh b/scripts/ci/distcheck.sh
new file mode 100755
index 000000000..9d45536f4
--- /dev/null
+++ b/scripts/ci/distcheck.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CXX="clang++"
+fi
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+./configure ${CONF}
+
+# Ignore possible failures there because these tests depends on measurements
+# and systems might differ in performance.
+export CI="true"
+
+# Additional configure flags for distcheck
+export DISTCHECK_CONFIGURE_FLAGS="${CONF}"
+
+make distcheck
diff --git a/scripts/ci/out_of_tree.sh b/scripts/ci/out_of_tree.sh
new file mode 100755
index 000000000..d39f55438
--- /dev/null
+++ b/scripts/ci/out_of_tree.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -e
+
+if [ "${CC#clang}" != "${CC}" ] ; then
+ export CXX="clang++"
+fi
+
+cd "$(dirname "$0")"/../..
+./bootstrap
+mkdir tmp && cd tmp
+../configure ${CONF}
+make -j $(nproc)
+
diff --git a/scripts/spelling.txt b/scripts/spelling.txt
index eb38f49d4..a22e955c6 100644
--- a/scripts/spelling.txt
+++ b/scripts/spelling.txt
@@ -9,7 +9,12 @@
#
abandonning||abandoning
abigious||ambiguous
+abitrary||arbitrary
abitrate||arbitrate
+abnornally||abnormally
+abnrormal||abnormal
+abord||abort
+aboslute||absolute
abov||above
abreviated||abbreviated
absense||absence
@@ -17,14 +22,18 @@ absolut||absolute
absoulte||absolute
acccess||access
acceess||access
+accelaration||acceleration
+accelearion||acceleration
acceleratoin||acceleration
accelleration||acceleration
+accelrometer||accelerometer
accesing||accessing
accesnt||accent
accessable||accessible
accesss||access
accidentaly||accidentally
accidentually||accidentally
+acclerated||accelerated
accoding||according
accomodate||accommodate
accomodates||accommodates
@@ -34,8 +43,11 @@ accout||account
accquire||acquire
accquired||acquired
accross||across
+accumalate||accumulate
+accumalator||accumulator
acessable||accessible
acess||access
+acessing||accessing
achitecture||architecture
acient||ancient
acitions||actions
@@ -48,24 +60,36 @@ acording||according
activete||activate
actived||activated
actualy||actually
+actvie||active
acumulating||accumulating
+acumulative||accumulative
acumulator||accumulator
+acutally||actually
adapater||adapter
+adderted||asserted
addional||additional
additionaly||additionally
+additonal||additional
addres||address
+adddress||address
addreses||addresses
addresss||address
+addrress||address
aditional||additional
aditionally||additionally
aditionaly||additionally
adminstrative||administrative
adress||address
adresses||addresses
+adrresses||addresses
+advertisment||advertisement
adviced||advised
afecting||affecting
againt||against
agaist||against
+aggreataon||aggregation
+aggreation||aggregation
+ajust||adjust
albumns||albums
alegorical||allegorical
algined||aligned
@@ -73,15 +97,19 @@ algorith||algorithm
algorithmical||algorithmically
algoritm||algorithm
algoritms||algorithms
+algorithmn||algorithm
algorrithm||algorithm
algorritm||algorithm
aligment||alignment
alignement||alignment
allign||align
alligned||aligned
+alllocate||allocate
+alloated||allocated
allocatote||allocate
allocatrd||allocated
allocte||allocate
+allocted||allocated
allpication||application
alocate||allocate
alogirhtms||algorithms
@@ -89,11 +117,17 @@ alogrithm||algorithm
alot||a lot
alow||allow
alows||allows
+alreay||already
+alredy||already
altough||although
alue||value
ambigious||ambiguous
+ambigous||ambiguous
amoung||among
+amount of times||number of times
amout||amount
+amplifer||amplifier
+amplifyer||amplifier
an union||a union
an user||a user
an userspace||a userspace
@@ -104,6 +138,7 @@ anniversery||anniversary
annoucement||announcement
anomolies||anomalies
anomoly||anomaly
+anonynous||anonymous
anway||anyway
aplication||application
appearence||appearance
@@ -122,35 +157,56 @@ aquired||acquired
aquisition||acquisition
arbitary||arbitrary
architechture||architecture
+archtecture||architecture
arguement||argument
arguements||arguments
+arithmatic||arithmetic
aritmetic||arithmetic
arne't||aren't
arraival||arrival
artifical||artificial
artillary||artillery
asign||assign
+asser||assert
assertation||assertion
+assertting||asserting
+assgined||assigned
assiged||assigned
assigment||assignment
assigments||assignments
assistent||assistant
+assocaited||associated
+assocating||associating
assocation||association
associcated||associated
assotiated||associated
+asssert||assert
assum||assume
assumtpion||assumption
asuming||assuming
asycronous||asynchronous
+asychronous||asynchronous
asynchnous||asynchronous
+asynchronus||asynchronous
+asynchromous||asynchronous
+asymetric||asymmetric
+asymmeric||asymmetric
+atleast||at least
atomatically||automatically
atomicly||atomically
atempt||attempt
+atrributes||attributes
attachement||attachment
+attatch||attach
attched||attached
+attemp||attempt
attemps||attempts
+attemping||attempting
+attepmpt||attempt
+attnetion||attention
attruibutes||attributes
authentification||authentication
+authenicated||authenticated
automaticaly||automatically
automaticly||automatically
automatize||automate
@@ -164,10 +220,12 @@ avaible||available
availabe||available
availabled||available
availablity||availability
+availaible||available
availale||available
availavility||availability
availble||available
availiable||available
+availible||available
avalable||available
avaliable||available
aysnc||async
@@ -181,6 +239,7 @@ baloons||balloons
bandwith||bandwidth
banlance||balance
batery||battery
+battey||battery
beacuse||because
becasue||because
becomming||becoming
@@ -192,30 +251,59 @@ beter||better
betweeen||between
bianries||binaries
bitmast||bitmask
+bitwiedh||bitwidth
boardcast||broadcast
borad||board
boundry||boundary
brievely||briefly
+brigde||bridge
+broadcase||broadcast
broadcat||broadcast
+bufer||buffer
+bufferred||buffered
+bufufer||buffer
cacluated||calculated
+caculate||calculate
caculation||calculation
+cadidate||candidate
+cahces||caches
calender||calendar
+calescing||coalescing
+calibraiton||calibration
calle||called
callibration||calibration
+callled||called
+callser||caller
calucate||calculate
calulate||calculate
cancelation||cancellation
+cancle||cancel
+cant||can't
+cant'||can't
+canot||cannot
+cann't||can't
+cannnot||cannot
+capabiity||capability
capabilites||capabilities
+capabilties||capabilities
+capabilty||capability
capabitilies||capabilities
+capablity||capability
capatibilities||capabilities
+capapbilities||capabilities
+captuer||capture
+caputure||capture
carefuly||carefully
cariage||carriage
+casued||caused
catagory||category
cehck||check
challange||challenge
challanges||challenges
+chache||cache
chanell||channel
changable||changeable
+chanined||chained
channle||channel
channnel||channel
charachter||character
@@ -226,6 +314,7 @@ charaters||characters
charcter||character
chcek||check
chck||check
+checksumed||checksummed
checksuming||checksumming
childern||children
childs||children
@@ -233,6 +322,7 @@ chiled||child
chked||checked
chnage||change
chnages||changes
+chnange||change
chnnel||channel
choosen||chosen
chouse||chose
@@ -241,6 +331,9 @@ claread||cleared
clared||cleared
closeing||closing
clustred||clustered
+cnfiguration||configuration
+coexistance||coexistence
+colescing||coalescing
collapsable||collapsible
colorfull||colorful
comand||command
@@ -251,14 +344,19 @@ comminucation||communication
commited||committed
commiting||committing
committ||commit
+commmand||command
+commnunication||communication
commoditiy||commodity
comsume||consume
comsumer||consumer
comsuming||consuming
+comaptible||compatible
compability||compatibility
compaibility||compatibility
+comparsion||comparison
compatability||compatibility
compatable||compatible
+compatibililty||compatibility
compatibiliy||compatibility
compatibilty||compatibility
compatiblity||compatibility
@@ -269,22 +367,36 @@ completition||completion
completly||completely
complient||compliant
componnents||components
+compoment||component
+comppatible||compatible
compres||compress
compresion||compression
+compresser||compressor
comression||compression
+comsumed||consumed
+comunicate||communicate
comunication||communication
conbination||combination
conditionaly||conditionally
+conditon||condition
+condtion||condition
+condtional||conditional
conected||connected
-connecetd||connected
+conector||connector
+configration||configuration
+configred||configured
configuartion||configuration
+configuation||configuration
+configued||configured
configuratoin||configuration
configuraton||configuration
configuretion||configuration
configutation||configuration
conider||consider
conjuction||conjunction
+connecetd||connected
connectinos||connections
+connetor||connector
connnection||connection
connnections||connections
consistancy||consistency
@@ -294,10 +406,14 @@ containts||contains
contaisn||contains
contant||contact
contence||contents
+contiguos||contiguous
+continious||continuous
continous||continuous
continously||continuously
continueing||continuing
+contiuous||continuous
contraints||constraints
+contruct||construct
contol||control
contoller||controller
controled||controlled
@@ -315,21 +431,34 @@ correponding||corresponding
correponds||corresponds
correspoding||corresponding
cotrol||control
+cound||could
couter||counter
coutner||counter
+creationg||creating
cryptocraphic||cryptographic
+cummulative||cumulative
cunter||counter
curently||currently
cylic||cyclic
dafault||default
+deactive||deactivate
deafult||default
deamon||daemon
+debouce||debounce
+decendant||descendant
+decendants||descendants
decompres||decompress
+decsribed||described
decription||description
+dectected||detected
defailt||default
+deferal||deferral
+deffered||deferred
defferred||deferred
definate||definite
definately||definitely
+definiation||definition
+definiton||definition
defintion||definition
defintions||definitions
defualt||default
@@ -343,25 +472,38 @@ delare||declare
delares||declares
delaring||declaring
delemiter||delimiter
+delibrately||deliberately
+delievered||delivered
+demodualtor||demodulator
+demension||dimension
dependancies||dependencies
dependancy||dependency
dependant||dependent
+dependend||dependent
depreacted||deprecated
depreacte||deprecate
desactivate||deactivate
desciptor||descriptor
desciptors||descriptors
+descripto||descriptor
descripton||description
descrition||description
descritptor||descriptor
desctiptor||descriptor
desriptor||descriptor
desriptors||descriptors
+desination||destination
+destionation||destination
+destoried||destroyed
destory||destroy
destoryed||destroyed
destorys||destroys
destroied||destroyed
detabase||database
+deteced||detected
+detecion||detection
+detectt||detect
+detroyed||destroyed
develope||develop
developement||development
developped||developed
@@ -371,43 +513,78 @@ developpment||development
deveolpment||development
devided||divided
deviece||device
+devision||division
diable||disable
+diabled||disabled
+dicline||decline
dictionnary||dictionary
didnt||didn't
diferent||different
differrence||difference
diffrent||different
+differenciate||differentiate
+diffreential||differential
diffrentiate||differentiate
difinition||definition
+digial||digital
+dimention||dimension
+dimesions||dimensions
+diconnected||disconnected
+disabed||disabled
+disasembler||disassembler
+disble||disable
+disgest||digest
+disired||desired
+dispalying||displaying
+dissable||disable
diplay||display
+directon||direction
+direcly||directly
direectly||directly
+diregard||disregard
disassocation||disassociation
disapear||disappear
disapeared||disappeared
disappared||disappeared
+disbale||disable
+disbaled||disabled
disble||disable
disbled||disabled
disconnet||disconnect
discontinous||discontinuous
+disharge||discharge
+disnabled||disabled
dispertion||dispersion
dissapears||disappears
+dissconect||disconnect
distiction||distinction
+divisable||divisible
+divsiors||divisors
+dsiabled||disabled
docuentation||documentation
documantation||documentation
documentaion||documentation
documment||document
doesnt||doesn't
+donwload||download
+donwloading||downloading
dorp||drop
dosen||doesn
downlad||download
downlads||downloads
+droped||dropped
+droput||dropout
druing||during
+dyanmic||dynamic
dynmaic||dynamic
+eanable||enable
+eanble||enable
easilly||easily
ecspecially||especially
edditable||editable
editting||editing
efective||effective
+effectivness||effectiveness
efficently||efficiently
ehther||ether
eigth||eight
@@ -415,14 +592,23 @@ elementry||elementary
eletronic||electronic
embeded||embedded
enabledi||enabled
+enbale||enable
+enble||enable
enchanced||enhanced
encorporating||incorporating
encrupted||encrypted
encrypiton||encryption
+encryptio||encryption
endianess||endianness
+enpoint||endpoint
enhaced||enhanced
enlightnment||enlightenment
+enqueing||enqueuing
+entires||entries
+entites||entities
+entrys||entries
enocded||encoded
+enought||enough
enterily||entirely
enviroiment||environment
enviroment||environment
@@ -433,19 +619,37 @@ equiped||equipped
equivelant||equivalent
equivilant||equivalent
eror||error
+errorr||error
+errror||error
estbalishment||establishment
etsablishment||establishment
etsbalishment||establishment
+evalute||evaluate
+evalutes||evaluates
+evalution||evaluation
excecutable||executable
+excceed||exceed
exceded||exceeded
+exceds||exceeds
+exceeed||exceed
excellant||excellent
+exchnage||exchange
+execeeded||exceeded
+execeeds||exceeds
+exeed||exceed
+exeeds||exceeds
+exeuction||execution
existance||existence
existant||existent
exixt||exist
+exsits||exists
exlcude||exclude
+exlcuding||excluding
exlcusive||exclusive
+exlusive||exclusive
exmaple||example
expecially||especially
+experies||expires
explicite||explicit
explicitely||explicitly
explict||explicit
@@ -454,47 +658,69 @@ explictly||explicitly
expresion||expression
exprimental||experimental
extened||extended
+exteneded||extended
extensability||extensibility
extention||extension
+extenstion||extension
extracter||extractor
-falied||failed
+faied||failed
+faield||failed
faild||failed
+failded||failed
+failer||failure
faill||fail
failied||failed
faillure||failure
failue||failure
failuer||failure
+failng||failing
faireness||fairness
falied||failed
faliure||failure
+fallbck||fallback
familar||familiar
fatser||faster
feauture||feature
feautures||features
fetaure||feature
fetaures||features
+fetcing||fetching
fileystem||filesystem
+fimrware||firmware
fimware||firmware
+firmare||firmware
+firmaware||firmware
+firtly||firstly
+firware||firmware
+firwmare||firmware
finanize||finalize
findn||find
finilizes||finalizes
finsih||finish
+fliter||filter
flusing||flushing
folloing||following
followign||following
followings||following
follwing||following
+fonud||found
forseeable||foreseeable
forse||force
fortan||fortran
forwardig||forwarding
+frambuffer||framebuffer
framming||framing
framwork||framework
+frequence||frequency
frequncy||frequency
+frequancy||frequency
frome||from
+fronend||frontend
fucntion||function
fuction||function
fuctions||functions
+fullill||fulfill
+funcation||function
funcion||function
functionallity||functionality
functionaly||functionally
@@ -505,41 +731,65 @@ funtions||functions
furthur||further
futhermore||furthermore
futrue||future
+gatable||gateable
+gateing||gating
+gauage||gauge
gaurenteed||guaranteed
generiously||generously
genereate||generate
+genereted||generated
genric||generic
+gerenal||general
+geting||getting
globel||global
grabing||grabbing
grahical||graphical
grahpical||graphical
+granularty||granularity
grapic||graphic
+grranted||granted
guage||gauge
guarenteed||guaranteed
guarentee||guarantee
halfs||halves
hander||handler
handfull||handful
+hanlde||handle
hanled||handled
happend||happened
+hardare||hardware
harware||hardware
+hardward||hardware
+havind||having
heirarchically||hierarchically
+heirarchy||hierarchy
+heirachy||hierarchy
helpfull||helpful
+hearbeat||heartbeat
+heterogenous||heterogeneous
+hexdecimal||hexadecimal
+hybernate||hibernate
hierachy||hierarchy
hierarchie||hierarchy
+homogenous||homogeneous
howver||however
hsould||should
hypervior||hypervisor
hypter||hyper
+idel||idle
identidier||identifier
iligal||illegal
illigal||illegal
+illgal||illegal
+iomaped||iomapped
imblance||imbalance
immeadiately||immediately
immedaite||immediate
+immedate||immediate
immediatelly||immediately
immediatly||immediately
immidiate||immediate
+immutible||immutable
impelentation||implementation
impementated||implemented
implemantation||implementation
@@ -547,24 +797,34 @@ implemenation||implementation
implementaiton||implementation
implementated||implemented
implemention||implementation
+implementd||implemented
implemetation||implementation
implemntation||implementation
implentation||implementation
implmentation||implementation
implmenting||implementing
+incative||inactive
incomming||incoming
+incompaitiblity||incompatibility
incompatabilities||incompatibilities
incompatable||incompatible
+incompatble||incompatible
inconsistant||inconsistent
increas||increase
+incremeted||incremented
incrment||increment
+incuding||including
+inculde||include
indendation||indentation
indended||intended
independant||independent
independantly||independently
independed||independent
indiate||indicate
+indicat||indicate
inexpect||inexpected
+inferface||interface
+infinit||infinite
infomation||information
informatiom||information
informations||information
@@ -579,34 +839,49 @@ initalize||initialize
initation||initiation
initators||initiators
initialiazation||initialization
+initializationg||initialization
initializiation||initialization
+initialze||initialize
initialzed||initialized
+initialzing||initializing
initilization||initialization
initilize||initialize
+initliaze||initialize
+initilized||initialized
inofficial||unofficial
+inrerface||interface
insititute||institute
+instace||instance
instal||install
+instanciate||instantiate
instanciated||instantiated
+instuments||instruments
+insufficent||insufficient
inteface||interface
integreated||integrated
integrety||integrity
integrey||integrity
intendet||intended
intented||intended
+interal||internal
interanl||internal
interchangable||interchangeable
interferring||interfering
interger||integer
+intergrated||integrated
intermittant||intermittent
internel||internal
interoprability||interoperability
+interuupt||interrupt
+interupt||interrupt
+interupts||interrupts
interrface||interface
interrrupt||interrupt
interrup||interrupt
interrups||interrupts
interruptted||interrupted
interupted||interrupted
-interupt||interrupt
+intiailized||initialized
intial||initial
intialisation||initialisation
intialised||initialised
@@ -615,24 +890,35 @@ intialization||initialization
intialized||initialized
intialize||initialize
intregral||integral
+intrerrupt||interrupt
intrrupt||interrupt
intterrupt||interrupt
intuative||intuitive
+inavlid||invalid
invaid||invalid
+invaild||invalid
+invailid||invalid
+invald||invalid
invalde||invalid
invalide||invalid
+invalidiate||invalidate
invalud||invalid
invididual||individual
invokation||invocation
invokations||invocations
+ireelevant||irrelevant
irrelevent||irrelevant
isnt||isn't
isssue||issue
+issus||issues
+iteraions||iterations
iternations||iterations
itertation||iteration
itslef||itself
+ivalid||invalid
jave||java
jeffies||jiffies
+jumpimng||jumping
juse||just
jus||just
kown||known
@@ -642,6 +928,7 @@ langauge||language
langugage||language
lauch||launch
layed||laid
+legnth||length
leightweight||lightweight
lengh||length
lenght||length
@@ -652,40 +939,66 @@ libary||library
librairies||libraries
libraris||libraries
licenceing||licencing
+limted||limited
+logaritmic||logarithmic
loggging||logging
loggin||login
logile||logfile
+loobpack||loopback
loosing||losing
losted||lost
+maangement||management
machinary||machinery
+maibox||mailbox
maintainance||maintenance
maintainence||maintenance
maintan||maintain
makeing||making
+mailformed||malformed
malplaced||misplaced
malplace||misplace
managable||manageable
+managament||management
managment||management
mangement||management
+manger||manager
manoeuvering||maneuvering
+manufaucturing||manufacturing
mappping||mapping
+maping||mapping
+matchs||matches
mathimatical||mathematical
mathimatic||mathematic
mathimatics||mathematics
+maxmium||maximum
+maximium||maximum
maxium||maximum
mechamism||mechanism
+mechanim||mechanism
meetign||meeting
+memeory||memory
+memmber||member
+memoery||memory
+memroy||memory
ment||meant
mergable||mergeable
mesage||message
+mesages||messages
messags||messages
messgaes||messages
messsage||message
messsages||messages
+metdata||metadata
+micropone||microphone
microprocesspr||microprocessor
+migrateable||migratable
+millenium||millennium
milliseonds||milliseconds
+minimim||minimum
minium||minimum
minimam||minimum
+minimun||minimum
+miniumum||minimum
minumum||minimum
misalinged||misaligned
miscelleneous||miscellaneous
@@ -693,21 +1006,35 @@ misformed||malformed
mispelled||misspelled
mispelt||misspelt
mising||missing
+mismactch||mismatch
+missign||missing
+missmanaged||mismanaged
+missmatch||mismatch
+misssing||missing
miximum||maximum
mmnemonic||mnemonic
mnay||many
+modfiy||modify
+modifer||modifier
+modul||module
modulues||modules
momery||memory
+memomry||memory
+monitring||monitoring
monochorome||monochrome
monochromo||monochrome
monocrome||monochrome
mopdule||module
mroe||more
mulitplied||multiplied
+muliple||multiple
+multipler||multiplier
multidimensionnal||multidimensional
+multipe||multiple
multple||multiple
mumber||number
muticast||multicast
+mutilcast||multicast
mutiple||multiple
mutli||multi
nams||names
@@ -724,45 +1051,67 @@ negotation||negotiation
nerver||never
nescessary||necessary
nessessary||necessary
+none existent||non-existent
noticable||noticeable
+notication||notification
notications||notifications
+notifcations||notifications
notifed||notified
+notity||notify
+nubmer||number
numebr||number
+numer||number
numner||number
+nunber||number
obtaion||obtain
+obusing||abusing
occassionally||occasionally
occationally||occasionally
occurance||occurrence
occurances||occurrences
+occurd||occurred
occured||occurred
occurence||occurrence
occure||occurred
-occured||occurred
occuring||occurring
+ocurrence||occurrence
+offser||offset
offet||offset
+offlaod||offload
+offloded||offloaded
+offseting||offsetting
+oflload||offload
omited||omitted
omiting||omitting
omitt||omit
ommiting||omitting
ommitted||omitted
onself||oneself
+onthe||on the
ony||only
+openning||opening
operatione||operation
opertaions||operations
+opportunies||opportunities
optionnal||optional
optmizations||optimizations
orientatied||orientated
orientied||oriented
orignal||original
+originial||original
otherise||otherwise
ouput||output
oustanding||outstanding
overaall||overall
overhread||overhead
overlaping||overlapping
+oveflow||overflow
+overflw||overflow
+overlfow||overflow
overide||override
overrided||overridden
overriden||overridden
+overrrun||overrun
overun||overrun
overwritting||overwriting
overwriten||overwritten
@@ -773,8 +1122,10 @@ packege||package
packge||package
packtes||packets
pakage||package
+paket||packet
pallette||palette
paln||plan
+palne||plane
paramameters||parameters
paramaters||parameters
paramater||parameter
@@ -782,22 +1133,36 @@ parametes||parameters
parametised||parametrised
paramter||parameter
paramters||parameters
+parmaters||parameters
particuarly||particularly
particularily||particularly
+partion||partition
+partions||partitions
partiton||partition
pased||passed
passin||passing
pathes||paths
+pattrns||patterns
pecularities||peculiarities
peformance||performance
+peforming||performing
peice||piece
pendantic||pedantic
peprocessor||preprocessor
+perfomance||performance
perfoming||performing
+perfomring||performing
+periperal||peripheral
+peripherial||peripheral
permissons||permissions
+permited||permitted
peroid||period
persistance||persistence
persistant||persistent
+phoneticly||phonetically
+plaform||platform
+plalform||platform
+platfoem||platform
platfrom||platform
plattform||platform
pleaes||please
@@ -809,7 +1174,12 @@ poiter||pointer
posible||possible
positon||position
possibilites||possibilities
+potocol||protocol
powerfull||powerful
+pramater||parameter
+preamle||preamble
+preample||preamble
+preapre||prepare
preceeded||preceded
preceeding||preceding
preceed||precede
@@ -818,17 +1188,29 @@ precission||precision
preemptable||preemptible
prefered||preferred
prefferably||preferably
+prefitler||prefilter
+preform||perform
premption||preemption
prepaired||prepared
+prepate||prepare
+preperation||preparation
+preprare||prepare
pressre||pressure
+presuambly||presumably
+previosuly||previously
+previsously||previously
primative||primitive
princliple||principle
priorty||priority
+priting||printing
privilaged||privileged
privilage||privilege
priviledge||privilege
+priviledged||privileged
priviledges||privileges
+privleges||privileges
probaly||probably
+probabalistic||probabilistic
procceed||proceed
proccesors||processors
procesed||processed
@@ -841,12 +1223,17 @@ processsed||processed
processsing||processing
procteted||protected
prodecure||procedure
+progamming||programming
progams||programs
progess||progress
+programable||programmable
programers||programmers
programm||program
programms||programs
+progres||progress
progresss||progress
+prohibitted||prohibited
+prohibitting||prohibiting
promiscous||promiscuous
promps||prompts
pronnounced||pronounced
@@ -856,34 +1243,46 @@ pronunce||pronounce
propery||property
propigate||propagate
propigation||propagation
+propogation||propagation
propogate||propagate
prosess||process
protable||portable
protcol||protocol
protecion||protection
+protedcted||protected
protocoll||protocol
promixity||proximity
psudo||pseudo
psuedo||pseudo
psychadelic||psychedelic
+purgable||purgeable
pwoer||power
+queing||queuing
quering||querying
+querrying||querying
+queus||queues
+randomally||randomly
raoming||roaming
reasearcher||researcher
reasearchers||researchers
reasearch||research
+receieve||receive
recepient||recipient
+recevied||received
receving||receiving
+recievd||received
recieved||received
recieve||receive
reciever||receiver
recieves||receives
+recieving||receiving
recogniced||recognised
recognizeable||recognizable
recommanded||recommended
recyle||recycle
redircet||redirect
redirectrion||redirection
+redundacy||redundancy
reename||rename
refcounf||refcount
refence||reference
@@ -893,8 +1292,12 @@ refering||referring
refernces||references
refernnce||reference
refrence||reference
+regiser||register
+registed||registered
registerd||registered
+registeration||registration
registeresd||registered
+registerred||registered
registes||registers
registraration||registration
regsiter||register
@@ -905,6 +1308,7 @@ regulamentations||regulations
reigstration||registration
releated||related
relevent||relevant
+reloade||reload
remoote||remote
remore||remote
removeable||removable
@@ -915,29 +1319,46 @@ replys||replies
reponse||response
representaion||representation
reqeust||request
+reqister||register
+requed||requeued
requestied||requested
requiere||require
requirment||requirement
requred||required
requried||required
requst||request
+requsted||requested
+reregisteration||reregistration
reseting||resetting
+reseved||reserved
+reseverd||reserved
resizeable||resizable
+resonable||reasonable
+resotre||restore
+resouce||resource
resouces||resources
resoures||resources
responce||response
+resrouce||resource
ressizes||resizes
ressource||resource
ressources||resources
+restesting||retesting
+resumbmitting||resubmitting
retransmited||retransmitted
retreived||retrieved
retreive||retrieve
+retreiving||retrieving
retrive||retrieve
+retrived||retrieved
+retrun||return
+retun||return
retuned||returned
reudce||reduce
reuest||request
reuqest||request
reutnred||returned
+revsion||revision
rmeoved||removed
rmeove||remove
rmeoves||removes
@@ -946,36 +1367,52 @@ routins||routines
rquest||request
runing||running
runned||ran
+runnnig||running
runnning||running
runtine||runtime
sacrifying||sacrificing
safly||safely
safty||safety
+satify||satisfy
+satisifed||satisfied
savable||saveable
+scaleing||scaling
scaned||scanned
scaning||scanning
scarch||search
+schdule||schedule
seach||search
searchs||searches
+secion||section
secquence||sequence
secund||second
segement||segment
+seleted||selected
+semaphone||semaphore
+senario||scenario
senarios||scenarios
sentivite||sensitive
separatly||separately
sepcify||specify
-sepc||spec
seperated||separated
seperately||separately
seperate||separate
seperatly||separately
seperator||separator
sepperate||separate
+seqeunce||sequence
+seqeuncer||sequencer
+seqeuencer||sequencer
sequece||sequence
+sequemce||sequence
sequencial||sequential
+serivce||service
serveral||several
+servive||service
setts||sets
settting||setting
+shapshot||snapshot
+shoft||shift
shotdown||shutdown
shoud||should
shouldnt||shouldn't
@@ -983,24 +1420,36 @@ shoule||should
shrinked||shrunk
siginificantly||significantly
signabl||signal
+significanly||significantly
similary||similarly
similiar||similar
simlar||similar
simliar||similar
simpified||simplified
+simultaneusly||simultaneously
+simultanous||simultaneous
singaled||signaled
singal||signal
singed||signed
+slect||select
sleeped||slept
+sliped||slipped
+softwade||software
softwares||software
+soley||solely
+soluation||solution
+souce||source
speach||speech
specfic||specific
+specfield||specified
speciefied||specified
specifc||specific
specifed||specified
specificatin||specification
specificaton||specification
+specificed||specified
specifing||specifying
+specifiy||specify
specifiying||specifying
speficied||specified
speicify||specify
@@ -1017,8 +1466,13 @@ staion||station
standardss||standards
standartization||standardization
standart||standard
+standy||standby
+stardard||standard
staticly||statically
+statisitcs||statistics
+statuss||status
stoped||stopped
+stoping||stopping
stoppped||stopped
straming||streaming
struc||struct
@@ -1030,12 +1484,18 @@ sturcture||structure
subdirectoires||subdirectories
suble||subtle
substract||subtract
+submited||submitted
+submition||submission
+succeded||succeeded
+suceed||succeed
+succesfuly||successfully
succesfully||successfully
succesful||successful
successed||succeeded
successfull||successful
successfuly||successfully
sucessfully||successfully
+sucessful||successful
sucess||success
superflous||superfluous
superseeded||superseded
@@ -1044,14 +1504,18 @@ suported||supported
suport||support
supportet||supported
suppored||supported
+supporing||supporting
supportin||supporting
suppoted||supported
suppported||supported
suppport||support
+supprot||support
supress||suppress
+surpressed||suppressed
surpresses||suppresses
susbsystem||subsystem
suspeneded||suspended
+suspsend||suspend
suspicously||suspiciously
swaping||swapping
switchs||switches
@@ -1062,9 +1526,14 @@ swithced||switched
swithcing||switching
swithed||switched
swithing||switching
+swtich||switch
+syfs||sysfs
symetric||symmetric
synax||syntax
synchonized||synchronized
+sychronization||synchronization
+sychronously||synchronously
+synchronuously||synchronously
syncronize||synchronize
syncronized||synchronized
syncronizing||synchronizing
@@ -1073,42 +1542,79 @@ syste||system
sytem||system
sythesis||synthesis
taht||that
+tained||tainted
+tarffic||traffic
+tansmit||transmit
targetted||targeted
targetting||targeting
+taskelt||tasklet
teh||the
+temeprature||temperature
temorary||temporary
temproarily||temporarily
+temperture||temperature
+theads||threads
therfore||therefore
thier||their
threds||threads
+threee||three
threshhold||threshold
+thresold||threshold
+throtting||throttling
throught||through
+tansition||transition
+trackling||tracking
+troughput||throughput
+trys||tries
thses||these
+tiggers||triggers
tiggered||triggered
tipically||typically
+timeing||timing
timout||timeout
tmis||this
+toogle||toggle
torerable||tolerable
+torlence||tolerance
+traget||target
+traking||tracking
tramsmitted||transmitted
tramsmit||transmit
+tranasction||transaction
+tranceiver||transceiver
tranfer||transfer
+tranmission||transmission
+tranport||transport
+transcevier||transceiver
transciever||transceiver
transferd||transferred
transfered||transferred
transfering||transferring
transision||transition
+transistioned||transitioned
transmittd||transmitted
transormed||transformed
+trasfer||transfer
trasmission||transmission
treshold||threshold
+triggerd||triggered
+trigerred||triggered
trigerring||triggering
trun||turn
+tunning||tuning
ture||true
tyep||type
udpate||update
+updtes||updates
uesd||used
+unknwon||unknown
+uknown||unknown
+usccess||success
uncommited||uncommitted
+uncompatible||incompatible
unconditionaly||unconditionally
+undeflow||underflow
+undelying||underlying
underun||underrun
unecessary||unnecessary
unexecpted||unexpected
@@ -1119,31 +1625,52 @@ unexpeted||unexpected
unexpexted||unexpected
unfortunatelly||unfortunately
unifiy||unify
+uniterrupted||uninterrupted
+uninterruptable||uninterruptible
unintialized||uninitialized
+unitialized||uninitialized
unkmown||unknown
unknonw||unknown
+unknouwn||unknown
unknow||unknown
unkown||unknown
+unamed||unnamed
+uneeded||unneeded
unneded||unneeded
+unneccecary||unnecessary
+unneccesary||unnecessary
+unneccessary||unnecessary
+unnecesary||unnecessary
unneedingly||unnecessarily
unnsupported||unsupported
+unuspported||unsupported
unmached||unmatched
+unprecise||imprecise
+unpriviledged||unprivileged
+unpriviliged||unprivileged
unregester||unregister
unresgister||unregister
unrgesiter||unregister
unsinged||unsigned
unstabel||unstable
+unsolicted||unsolicited
unsolicitied||unsolicited
unsuccessfull||unsuccessful
unsuported||unsupported
untill||until
+ununsed||unused
unuseful||useless
+unvalid||invalid
upate||update
+upsupported||unsupported
+upto||up to
+useable||usable
usefule||useful
usefull||useful
usege||usage
usera||users
usualy||usually
+usupported||unsupported
utilites||utilities
utillities||utilities
utilties||utilities
@@ -1158,8 +1685,12 @@ varible||variable
varient||variant
vaule||value
verbse||verbose
+veify||verify
+verfication||verification
+veriosn||version
verisons||versions
verison||version
+veritical||vertical
verson||version
vicefersa||vice-versa
virtal||virtual
@@ -1167,7 +1698,12 @@ virtaul||virtual
virtiual||virtual
visiters||visitors
vitual||virtual
+vunerable||vulnerable
+wakeus||wakeups
+was't||wasn't
+wathdog||watchdog
wating||waiting
+wiat||wait
wether||whether
whataver||whatever
whcih||which
@@ -1175,12 +1711,19 @@ whenver||whenever
wheter||whether
whe||when
wierd||weird
+wihout||without
wiil||will
wirte||write
withing||within
wnat||want
+wont||won't
workarould||workaround
writeing||writing
writting||writing
+wtih||with
zombe||zombie
zomebie||zombie
+#
+# ODP additions
+#
+odp_cache_aligned||ODP_ALIGNED_CACHE
diff --git a/test/Makefile.am b/test/Makefile.am
deleted file mode 100644
index 3b5917329..000000000
--- a/test/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = common_plat @with_platform@
diff --git a/test/Makefile.inc b/test/Makefile.inc
index 0fbc3c423..cc6a33df5 100644
--- a/test/Makefile.inc
+++ b/test/Makefile.inc
@@ -1,27 +1,34 @@
-include $(top_srcdir)/platform/@with_platform@/Makefile.inc
-LIB = $(top_builddir)/lib
+include $(top_srcdir)/Makefile.inc
+
+COMMON_DIR = $(top_builddir)/test/common
+
+LIBODP = $(LIB)/libodphelper.la $(LIB)/lib$(ODP_LIB_NAME).la
+
+LIBCUNIT_COMMON = $(COMMON_DIR)/libcunit_common.la
+LIBCPUMASK_COMMON = $(COMMON_DIR)/libcpumask_common.la
+LIBPACKET_COMMON = $(COMMON_DIR)/libpacket_common.la
+LIBTHRMASK_COMMON = $(COMMON_DIR)/libthrmask_common.la
#in the following line, the libs using the symbols should come before
#the libs containing them! The includer is given a chance to add things
-#before libodp by setting PRE_LDADD before the inclusion.
-LDADD = $(PRE_LDADD) $(LIB)/libodphelper.la $(LIB)/libodp-linux.la $(DPDK_PMDS)
+#before libodp by setting PRELDADD before the inclusion.
+LDADD = $(PRELDADD) $(LIBODP)
+PRELDADD =
+
+AM_CPPFLAGS = \
+ $(ODP_INCLUDES) \
+ $(HELPER_INCLUDES) \
+ -I$(top_srcdir)/test/common
-INCFLAGS = \
- -I$(top_builddir)/platform/@with_platform@/include \
- -I$(top_srcdir)/helper/include \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ \
- -I$(top_srcdir)/platform/@with_platform@/include \
- -I$(top_srcdir)/test \
- -I$(top_builddir)/include
+AM_CFLAGS += $(CUNIT_CFLAGS)
-AM_CFLAGS += $(INCFLAGS)
-AM_CPPFLAGS += $(CUNIT_CPPFLAGS)
-AM_CXXFLAGS = $(INCFLAGS)
+if STATIC_APPS
+AM_LDFLAGS += -static
+endif
-AM_LDFLAGS += -L$(LIB)
+AM_LDFLAGS += $(PLAT_DEP_LIBS)
@VALGRIND_CHECK_RULES@
-valgrind_tools = memcheck
-TESTS_ENVIRONMENT= ODP_PLATFORM=${with_platform} EXEEXT=${EXEEXT}
+TESTS_ENVIRONMENT = ODP_PLATFORM=${with_platform} \
+ EXEEXT=${EXEEXT}
diff --git a/test/README b/test/README
index f4886d35b..eebde9a47 100644
--- a/test/README
+++ b/test/README
@@ -1,9 +1,7 @@
-Copyright (c) 2014, Linaro Limited
-All rights reserved.
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2014-2018 Linaro Limited
-SPDX-License-Identifier: BSD-3-Clause
-
-Files in test/common_plat/validation directory are intended to be terse
+Files in test/validation directory are intended to be terse
checks that help ensure that the ODP implementations all perform identically
and to specification. Tests inside the validation directory shall be platform
independent.
@@ -15,3 +13,8 @@ $ make
$ cd test/<platform_name>
$ make check-valgrind
+To run these tests with gdb, use the following libtool command:
+$ libtool --mode=execute gdb ./<test_name>
+
+Refer to the prerequisites section of the DEPENDENCIES file for how to
+install the libtool package.
diff --git a/test/common/Makefile.am b/test/common/Makefile.am
new file mode 100644
index 000000000..72658df73
--- /dev/null
+++ b/test/common/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/test/Makefile.inc
+
+if cunit_support
+
+noinst_LTLIBRARIES = \
+ libcunit_common.la \
+ libcpumask_common.la \
+ libpacket_common.la \
+ libthrmask_common.la
+
+libcunit_common_la_SOURCES = odp_cunit_common.c odp_cunit_common.h
+libcunit_common_la_LIBADD = $(CUNIT_LIBS)
+
+libcpumask_common_la_SOURCES = mask_common.c mask_common.h
+
+libpacket_common_la_SOURCES = packet_common.c packet_common.h
+
+libthrmask_common_la_SOURCES = mask_common.c mask_common.h
+libthrmask_common_la_CFLAGS = $(AM_CFLAGS) -DTEST_THRMASK
+
+endif
+
+noinst_HEADERS = test_common_macros.h \
+ test_packet_custom.h \
+ test_packet_ipsec.h \
+ test_packet_ipv4.h \
+ test_packet_ipv4_with_crc.h \
+ test_packet_ipv6.h
diff --git a/test/common/mask_common.c b/test/common/mask_common.c
new file mode 100644
index 000000000..130429c61
--- /dev/null
+++ b/test/common/mask_common.c
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "odp_cunit_common.h"
+#include "mask_common.h"
+
+#include <stdlib.h>
+
+/*
+ * The following strings are used to build masks with odp_*mask_from_str().
+ * Both 0x prefixed and non prefixed hex values are supported.
+ */
+#define TEST_MASK_NONE "0x0"
+#define TEST_MASK_0 "0x1"
+#define TEST_MASK_1 "0x2"
+#define TEST_MASK_2 "0x4"
+#define TEST_MASK_0_2 "0x5"
+#define TEST_MASK_0_3 "0x9"
+#define TEST_MASK_1_2 "0x6"
+#define TEST_MASK_1_3 "0xA"
+#define TEST_MASK_0_1_2 "0x7"
+#define TEST_MASK_0_2_4_6 "0x55"
+#define TEST_MASK_1_2_4_6 "0x56"
+
+#define TEST_MASK_0_NO_PREFIX "1"
+
+/* padding pattern used to check buffer overflow: */
+#define FILLING_PATTERN 0x55
+
+/*
+ * returns the length of a string, excluding terminating NULL.
+ * As its C lib strlen equivalent. Just rewritten here to avoid C lib
+ * dependency in ODP tests (for platform independent / bare metal testing)
+ */
+static unsigned int stringlen(const char *str)
+{
+ unsigned int i = 0;
+
+ while (str[i] != 0)
+ i++;
+ return i;
+}
+
+/*
+ * builds a string containing a 0x prefixed hex number where a single bit
+ * (corresponding to a cpu or thread) is set.
+ * The string is null terminated.
+ * bit_set_str(0) returns "0x1".
+ * bit_set_str(10) returns "0x400".
+ * The buffer should be at least ceil(offs/4)+3 bytes long,
+ * to accommodate with 4 bits per nibble + "0x" prefix + null.
+ */
+#define BITS_PER_NIBBLE 4
+static void bit_set_str(char *buff, int offs)
+{
+ const char *hex_nibble = "1248";
+ int i = 0;
+
+ buff[i++] = '0';
+ buff[i++] = 'x';
+ buff[i++] = hex_nibble[offs % BITS_PER_NIBBLE];
+ while (offs > 3) {
+ buff[i++] = '0';
+ offs -= BITS_PER_NIBBLE;
+ }
+ buff[i++] = 0; /* null */
+}
+
+/*
+ * Returns the maximum number of CPUs that a mask can contain.
+ */
+unsigned mask_capacity(void)
+{
+ _odp_mask_t mask;
+
+ _odp_mask_setall(&mask);
+
+ return _odp_mask_count(&mask);
+}
+
+MASK_TESTFUNC(to_from_str)
+{
+ _odp_mask_t mask;
+ int32_t str_sz;
+ unsigned int buf_sz; /* buf size for the 2 following bufs */
+ char *buf_in;
+ char *buf_out;
+ unsigned int cpu;
+ unsigned int i;
+
+ /* makes sure the mask has room for at least 1 CPU...: */
+ CU_ASSERT_FATAL(mask_capacity() > 0);
+
+ /* allocate memory for the buffers containing the mask strings:
+ 1 char per nibble, i.e. 1 char per 4 cpus +extra for "0x" and null:*/
+ buf_sz = (mask_capacity() >> 2) + 20;
+ buf_in = malloc(buf_sz);
+ buf_out = malloc(buf_sz);
+ CU_ASSERT_FATAL(buf_in && buf_out);
+
+ /* test 1 CPU at a time for all possible cpu positions in the mask */
+ for (cpu = 0; cpu < mask_capacity(); cpu++) {
+ /* init buffer for overwrite check: */
+ for (i = 0; i < buf_sz; i++)
+ buf_out[i] = FILLING_PATTERN;
+
+ /* generate a hex string with that cpu set: */
+ bit_set_str(buf_in, cpu);
+
+ /* generate mask: */
+ _odp_mask_from_str(&mask, buf_in);
+
+ /* reverse cpu mask computation to get string back: */
+ str_sz = _odp_mask_to_str(&mask, buf_out,
+ stringlen(buf_in) + 1);
+
+ /* check that returned size matches original (with NULL): */
+ CU_ASSERT(str_sz == (int32_t)stringlen(buf_in) + 1);
+
+ /* check that returned string matches original: */
+ CU_ASSERT(!strcmp(buf_out, buf_in));
+
+ /* check that no extra buffer writes occurred: */
+ CU_ASSERT(buf_out[stringlen(buf_in) + 2] == FILLING_PATTERN);
+ }
+
+ /* re-init buffer for overwrite check: */
+ for (i = 0; i < buf_sz; i++)
+ buf_out[i] = FILLING_PATTERN;
+
+ /* check for buffer overflow when too small buffer given: */
+ _odp_mask_from_str(&mask, TEST_MASK_0);
+ str_sz = _odp_mask_to_str(&mask, buf_out, stringlen(TEST_MASK_0));
+
+ CU_ASSERT(str_sz == -1);
+
+ for (i = 0; i < buf_sz; i++)
+ CU_ASSERT(buf_out[i] == FILLING_PATTERN);
+
+ /* check for handling of missing "0x" prefix: */
+ _odp_mask_from_str(&mask, TEST_MASK_0_NO_PREFIX);
+
+ str_sz = _odp_mask_to_str(&mask, buf_out,
+ stringlen(TEST_MASK_0) + 1);
+ CU_ASSERT(str_sz == (int32_t)stringlen(TEST_MASK_0) + 1);
+
+ CU_ASSERT(!strcmp(buf_out, TEST_MASK_0));
+
+ free(buf_out);
+ free(buf_in);
+}
+
+MASK_TESTFUNC(equal)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+ _odp_mask_t mask3;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask3, TEST_MASK_NONE);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+ CU_ASSERT(!_odp_mask_equal(&mask1, &mask3));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ _odp_mask_from_str(&mask2, TEST_MASK_0_2);
+ _odp_mask_from_str(&mask3, TEST_MASK_1_2);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+ CU_ASSERT(!_odp_mask_equal(&mask1, &mask3));
+
+ if (mask_capacity() < 8)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2_4_6);
+ _odp_mask_from_str(&mask2, TEST_MASK_0_2_4_6);
+ _odp_mask_from_str(&mask3, TEST_MASK_1_2_4_6);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+ CU_ASSERT(!_odp_mask_equal(&mask1, &mask3));
+}
+
+MASK_TESTFUNC(zero)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_zero(&mask2);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+}
+
+MASK_TESTFUNC(set)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_set(&mask1, 0);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask2, TEST_MASK_0_3);
+ _odp_mask_set(&mask1, 3);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+
+ /* make sure that re-asserting a cpu has no impact: */
+ _odp_mask_set(&mask1, 3);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+}
+
+MASK_TESTFUNC(clr)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_from_str(&mask2, TEST_MASK_NONE);
+ _odp_mask_clr(&mask1, 0);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_clr(&mask1, 2);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+
+ _odp_mask_from_str(&mask2, TEST_MASK_NONE);
+ _odp_mask_clr(&mask1, 0);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+
+ /* make sure that re-clearing a cpu has no impact: */
+ _odp_mask_clr(&mask1, 0);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+}
+
+MASK_TESTFUNC(isset)
+{
+ _odp_mask_t mask1;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ CU_ASSERT(_odp_mask_isset(&mask1, 0));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ CU_ASSERT(!_odp_mask_isset(&mask1, 0));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ CU_ASSERT(_odp_mask_isset(&mask1, 0));
+ CU_ASSERT(!_odp_mask_isset(&mask1, 1));
+ CU_ASSERT(_odp_mask_isset(&mask1, 2));
+ CU_ASSERT(!_odp_mask_isset(&mask1, 3));
+}
+
+MASK_TESTFUNC(count)
+{
+ _odp_mask_t mask1;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ CU_ASSERT(_odp_mask_count(&mask1) == 1);
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ CU_ASSERT(_odp_mask_count(&mask1) == 0);
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ CU_ASSERT(_odp_mask_count(&mask1) == 2);
+}
+
+MASK_TESTFUNC(and)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+ _odp_mask_t mask3;
+ _odp_mask_t mask4;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_0);
+ _odp_mask_and(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_NONE);
+ _odp_mask_and(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask4, TEST_MASK_NONE);
+ _odp_mask_and(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ _odp_mask_from_str(&mask2, TEST_MASK_1_2);
+ _odp_mask_from_str(&mask4, TEST_MASK_2);
+ _odp_mask_and(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+}
+
+MASK_TESTFUNC(or)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+ _odp_mask_t mask3;
+ _odp_mask_t mask4;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_0);
+ _odp_mask_or(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_0);
+ _odp_mask_or(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask4, TEST_MASK_NONE);
+ _odp_mask_or(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0_2);
+ _odp_mask_from_str(&mask2, TEST_MASK_1);
+ _odp_mask_from_str(&mask4, TEST_MASK_0_1_2);
+ _odp_mask_or(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+}
+
+MASK_TESTFUNC(xor)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+ _odp_mask_t mask3;
+ _odp_mask_t mask4;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_NONE);
+ _odp_mask_xor(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_0);
+ _odp_mask_from_str(&mask4, TEST_MASK_0);
+ _odp_mask_xor(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask2, TEST_MASK_NONE);
+ _odp_mask_from_str(&mask4, TEST_MASK_NONE);
+ _odp_mask_xor(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_2);
+ _odp_mask_from_str(&mask2, TEST_MASK_1_2);
+ _odp_mask_from_str(&mask4, TEST_MASK_1);
+ _odp_mask_xor(&mask3, &mask1, &mask2);
+ CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
+}
+
+MASK_TESTFUNC(copy)
+{
+ _odp_mask_t mask1;
+ _odp_mask_t mask2;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ _odp_mask_copy(&mask2, &mask1);
+ CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
+}
+
+MASK_TESTFUNC(first)
+{
+ _odp_mask_t mask1;
+
+ /* check when there is no first */
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ CU_ASSERT(_odp_mask_first(&mask1) == -1);
+
+ /* single CPU case: */
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ CU_ASSERT(_odp_mask_first(&mask1) == 0);
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_1_3);
+ CU_ASSERT(_odp_mask_first(&mask1) == 1);
+}
+
+MASK_TESTFUNC(last)
+{
+ _odp_mask_t mask1;
+
+ /* check when there is no last: */
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ CU_ASSERT(_odp_mask_last(&mask1) == -1);
+
+ /* single CPU case: */
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ CU_ASSERT(_odp_mask_last(&mask1) == 0);
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_1_3);
+ CU_ASSERT(_odp_mask_last(&mask1) == 3);
+}
+
+MASK_TESTFUNC(next)
+{
+ unsigned int i;
+ int expected[] = {1, 3, 3, -1};
+ _odp_mask_t mask1;
+
+ /* case when the mask does not contain any CPU: */
+ _odp_mask_from_str(&mask1, TEST_MASK_NONE);
+ CU_ASSERT(_odp_mask_next(&mask1, -1) == -1);
+
+ /* case when the mask just contain CPU 0: */
+ _odp_mask_from_str(&mask1, TEST_MASK_0);
+ CU_ASSERT(_odp_mask_next(&mask1, -1) == 0);
+ CU_ASSERT(_odp_mask_next(&mask1, 0) == -1);
+
+ if (mask_capacity() < 4)
+ return;
+
+ _odp_mask_from_str(&mask1, TEST_MASK_1_3);
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(expected); i++)
+ CU_ASSERT(_odp_mask_next(&mask1, i) == expected[i]);
+}
+
+MASK_TESTFUNC(setall)
+{
+ int num;
+ int max = mask_capacity();
+ _odp_mask_t mask;
+
+ _odp_mask_setall(&mask);
+ num = _odp_mask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num <= max);
+}
diff --git a/test/common/mask_common.h b/test/common/mask_common.h
new file mode 100644
index 000000000..a40dd7c04
--- /dev/null
+++ b/test/common/mask_common.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_MASK_COMMON_H_
+#define ODP_MASK_COMMON_H_
+
+/*
+ * The same set of tests are used for testing both the odp_thrmask_ and
+ * odp_cpumask_ APIs.
+ *
+ * To build the thrmask tests TEST_THRMASK must be defined.
+ */
+#ifdef TEST_THRMASK
+typedef odp_thrmask_t _odp_mask_t;
+#define MASK_API_PREFIX(n) odp_thrmask_##n
+#define MASK_TESTFUNC(n) void thread_test_odp_thrmask_##n(void)
+#else
+typedef odp_cpumask_t _odp_mask_t;
+#define MASK_API_PREFIX(n) odp_cpumask_##n
+#define MASK_TESTFUNC(n) void cpumask_test_odp_cpumask_##n(void)
+#endif
+
+#define _odp_mask_from_str MASK_API_PREFIX(from_str)
+#define _odp_mask_to_str MASK_API_PREFIX(to_str)
+#define _odp_mask_equal MASK_API_PREFIX(equal)
+#define _odp_mask_zero MASK_API_PREFIX(zero)
+#define _odp_mask_set MASK_API_PREFIX(set)
+#define _odp_mask_clr MASK_API_PREFIX(clr)
+#define _odp_mask_isset MASK_API_PREFIX(isset)
+#define _odp_mask_count MASK_API_PREFIX(count)
+#define _odp_mask_and MASK_API_PREFIX(and)
+#define _odp_mask_or MASK_API_PREFIX(or)
+#define _odp_mask_xor MASK_API_PREFIX(xor)
+#define _odp_mask_copy MASK_API_PREFIX(copy)
+#define _odp_mask_first MASK_API_PREFIX(first)
+#define _odp_mask_next MASK_API_PREFIX(next)
+#define _odp_mask_last MASK_API_PREFIX(last)
+#define _odp_mask_setall MASK_API_PREFIX(setall)
+
+unsigned mask_capacity(void);
+
+MASK_TESTFUNC(to_from_str);
+MASK_TESTFUNC(equal);
+MASK_TESTFUNC(zero);
+MASK_TESTFUNC(set);
+MASK_TESTFUNC(clr);
+MASK_TESTFUNC(isset);
+MASK_TESTFUNC(count);
+MASK_TESTFUNC(and);
+MASK_TESTFUNC(or);
+MASK_TESTFUNC(xor);
+MASK_TESTFUNC(copy);
+MASK_TESTFUNC(first);
+MASK_TESTFUNC(last);
+MASK_TESTFUNC(next);
+MASK_TESTFUNC(setall);
+
+#endif
diff --git a/test/common/odp_cunit_common.c b/test/common/odp_cunit_common.c
new file mode 100644
index 000000000..23587faa0
--- /dev/null
+++ b/test/common/odp_cunit_common.c
@@ -0,0 +1,751 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ * Copyright (c) 2021 Marvell
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <odp_api.h>
+#include "odp_cunit_common.h"
+#include <odp/helper/odph_api.h>
+
+#include <CUnit/TestDB.h>
+
+#if defined __GNUC__ && (((__GNUC__ == 4) && \
+ (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+#include <CUnit/Automated.h>
+#if defined __GNUC__ && (((__GNUC__ == 4) && \
+ (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic pop
+#endif
+
+/* Globals */
+static int running_in_ci;
+static odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+static int threads_running;
+static odp_instance_t instance;
+static bool control_thread;
+static char *progname;
+static int (*thread_func)(void *);
+
+/*
+ * global init/term functions which may be registered
+ * defaults to functions performing odp init/term.
+ */
+static int tests_global_init(odp_instance_t *inst);
+static int tests_global_term(odp_instance_t inst);
+static struct {
+ int (*global_init_ptr)(odp_instance_t *inst);
+ int (*global_term_ptr)(odp_instance_t inst);
+} global_init_term = {tests_global_init, tests_global_term};
+
+static odp_suiteinfo_t *global_testsuites;
+
+#define MAX_STR_LEN 256
+#define MAX_FAILURES 10
+
+/* Recorded assertion failure for later CUnit call in the initial thread */
+typedef struct assertion_failure_t {
+ char cond[MAX_STR_LEN];
+ char file[MAX_STR_LEN];
+ unsigned int line;
+ int fatal;
+} assertion_failure_t;
+
+typedef struct thr_global_t {
+ assertion_failure_t failure[MAX_FAILURES];
+ unsigned long num_failures;
+} thr_global_t;
+
+static thr_global_t *thr_global;
+
+static __thread int initial_thread = 1; /* Are we the initial thread? */
+static __thread jmp_buf longjmp_env;
+
+void odp_cu_assert(CU_BOOL value, unsigned int line,
+ const char *condition, const char *file, CU_BOOL fatal)
+{
+ unsigned long idx;
+
+ if (initial_thread) {
+ CU_assertImplementation(value, line, condition, file, "", fatal);
+ return;
+ }
+
+ /* Assertion ok, just return */
+ if (value)
+ return;
+
+ /*
+ * Non-initial thread/process cannot call CUnit assert because:
+ *
+ * - CU_assertImplementation() is not thread-safe
+ * - In process mode an assertion failure would be lost because it
+ * would not be recorded in the memory of the initial process.
+ * - Fatal asserts in CUnit perform longjmp which cannot be done in
+ * an other thread or process that did the setjmp.
+ *
+ * --> Record the assertion failure in shared memory so that it can be
+ * processed later in the context of the initial thread/process.
+ * --> In fatal assert, longjmp within the current thread.
+ */
+
+ idx = __atomic_fetch_add(&thr_global->num_failures, 1, __ATOMIC_RELAXED);
+
+ if (idx < MAX_FAILURES) {
+ assertion_failure_t *a = &thr_global->failure[idx];
+
+ strncpy(a->cond, condition, sizeof(a->cond));
+ strncpy(a->file, file, sizeof(a->file));
+ a->cond[sizeof(a->cond) - 1] = 0;
+ a->file[sizeof(a->file) - 1] = 0;
+ a->line = line;
+ a->fatal = fatal;
+ }
+
+ if (fatal)
+ longjmp(longjmp_env, 1);
+}
+
+static void handle_postponed_asserts(void)
+{
+ unsigned long num = thr_global->num_failures;
+
+ if (num > MAX_FAILURES)
+ num = MAX_FAILURES;
+
+ for (unsigned long n = 0; n < num; n++) {
+ assertion_failure_t *a = &thr_global->failure[n];
+
+ /*
+ * Turn fatal failures into non-fatal failures as we are just
+ * reporting them. Threads that saw fatal failures which
+ * prevented them from continuing have already been stopped.
+ */
+ CU_assertImplementation(0, a->line, a->cond, a->file, "", CU_FALSE);
+ }
+ thr_global->num_failures = 0;
+}
+
+static int threads_init(void)
+{
+ static int initialized;
+
+ if (initialized)
+ return 0;
+
+ /*
+ * Use shared memory mapping for the global structure to make it
+ * visible in the child processes if running in process mode.
+ */
+ thr_global = mmap(NULL, sizeof(thr_global_t),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0);
+ if (thr_global == MAP_FAILED)
+ return -1;
+
+ initialized = 1;
+ return 0;
+}
+
+static int run_thread(void *arg)
+{
+ int rc;
+
+ /* Make sure this is zero also in process mode "threads" */
+ initial_thread = 0;
+
+ if (setjmp(longjmp_env) == 0) {
+ /* Normal return, proceed to the thread function. */
+ rc = (*thread_func)(arg);
+ } else {
+ /*
+ * Return from longjmp done by the thread function.
+ * We return 0 here since odph_thread_join() does not like
+ * nonzero exit statuses.
+ */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int odp_cunit_thread_create(int num, int func_ptr(void *), void *const arg[], int priv, int sync)
+{
+ int i, ret;
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[num];
+
+ if (num > ODP_THREAD_COUNT_MAX) {
+ fprintf(stderr, "error: %s: too many threads: num=%d max=%d\n", __func__,
+ num, ODP_THREAD_COUNT_MAX);
+ return -1;
+ }
+
+ if (threads_running) {
+ /* thread_tbl is already in use */
+ fprintf(stderr, "error: %s: threads already running\n", __func__);
+ return -1;
+ }
+
+ thread_func = func_ptr;
+
+ odph_thread_common_param_init(&thr_common);
+
+ if (arg == NULL)
+ priv = 0;
+
+ for (i = 0; i < num; i++) {
+ odph_thread_param_init(&thr_param[i]);
+
+ thr_param[i].start = run_thread;
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+
+ if (arg)
+ thr_param[i].arg = arg[i];
+ else
+ thr_param[i].arg = NULL;
+
+ if (priv == 0)
+ break;
+ }
+
+ odp_cpumask_default_worker(&cpumask, num);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = !priv;
+ thr_common.sync = sync;
+
+ /* Create and start additional threads */
+ ret = odph_thread_create(thread_tbl, &thr_common, thr_param, num);
+
+ if (ret != num)
+ fprintf(stderr, "error: odph_thread_create() failed.\n");
+
+ threads_running = (ret > 0);
+
+ return ret;
+}
+
+int odp_cunit_thread_join(int num)
+{
+ /* Wait for threads to exit */
+ if (odph_thread_join(thread_tbl, num) != num) {
+ fprintf(stderr, "error: odph_thread_join() failed.\n");
+ return -1;
+ }
+ threads_running = 0;
+ thread_func = 0;
+
+ handle_postponed_asserts();
+
+ return 0;
+}
+
+static int tests_global_init(odp_instance_t *inst)
+{
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+ odp_thread_type_t thr_type;
+
+ if (odph_options(&helper_options)) {
+ fprintf(stderr, "error: odph_options() failed.\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ fprintf(stderr, "error: odp_init_global() failed.\n");
+ return -1;
+ }
+
+ thr_type = control_thread ? ODP_THREAD_CONTROL : ODP_THREAD_WORKER;
+ if (0 != odp_init_local(*inst, thr_type)) {
+ fprintf(stderr, "error: odp_init_local() failed.\n");
+ return -1;
+ }
+ if (0 != odp_schedule_config(NULL)) {
+ fprintf(stderr, "error: odp_schedule_config(NULL) failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tests_global_term(odp_instance_t inst)
+{
+ if (0 != odp_term_local()) {
+ fprintf(stderr, "error: odp_term_local() failed.\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ fprintf(stderr, "error: odp_term_global() failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * register tests_global_init and tests_global_term functions.
+ * If some of these functions are not registered, the defaults functions
+ * (tests_global_init() and tests_global_term()) defined above are used.
+ * One should use these register functions when defining these hooks.
+ * Note that passing NULL as function pointer is valid and will simply
+ * prevent the default (odp init/term) to be done.
+ */
+void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst))
+{
+ global_init_term.global_init_ptr = func_init_ptr;
+}
+
+void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst))
+{
+ global_init_term.global_term_ptr = func_term_ptr;
+}
+
+static odp_suiteinfo_t *cunit_get_suite_info(const char *suite_name)
+{
+ odp_suiteinfo_t *sinfo;
+
+ for (sinfo = global_testsuites; sinfo->name; sinfo++)
+ if (strcmp(sinfo->name, suite_name) == 0)
+ return sinfo;
+
+ return NULL;
+}
+
+static odp_testinfo_t *cunit_get_test_info(odp_suiteinfo_t *sinfo,
+ const char *test_name)
+{
+ odp_testinfo_t *tinfo;
+
+ for (tinfo = sinfo->testinfo_tbl; tinfo->name; tinfo++)
+ if (strcmp(tinfo->name, test_name) == 0)
+ return tinfo;
+
+ return NULL;
+}
+
+/* A wrapper for the suite's init function. This is done to allow for a
+ * potential runtime check to determine whether each test in the suite
+ * is active (enabled by using ODP_TEST_INFO_CONDITIONAL()). If present,
+ * the conditional check is run after the suite's init function.
+ */
+static int _cunit_suite_init(void)
+{
+ int ret = 0;
+ CU_pSuite cur_suite = CU_get_current_suite();
+ odp_suiteinfo_t *sinfo;
+ odp_testinfo_t *tinfo;
+
+ /* find the suite currently being run */
+ cur_suite = CU_get_current_suite();
+ if (!cur_suite)
+ return -1;
+
+ sinfo = cunit_get_suite_info(cur_suite->pName);
+ if (!sinfo)
+ return -1;
+
+ /* execute its init function */
+ if (sinfo->init_func) {
+ ret = sinfo->init_func();
+ if (ret)
+ return ret;
+ }
+
+ /* run any configured conditional checks and mark inactive tests */
+ for (tinfo = sinfo->testinfo_tbl; tinfo->name; tinfo++) {
+ CU_pTest ptest;
+ CU_ErrorCode err;
+
+ if (!tinfo->check_active || tinfo->check_active())
+ continue;
+
+ /* test is inactive, mark it as such */
+ ptest = CU_get_test_by_name(tinfo->name, cur_suite);
+ if (ptest)
+ err = CU_set_test_active(ptest, CU_FALSE);
+ else
+ err = CUE_NOTEST;
+
+ if (err != CUE_SUCCESS) {
+ fprintf(stderr, "%s: failed to set test %s inactive\n",
+ __func__, tinfo->name);
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/* Print names of all inactive tests of the suite. This should be called by
+ * every suite terminate function. Otherwise, inactive tests are not listed in
+ * test suite results. */
+int odp_cunit_print_inactive(void)
+{
+ CU_pSuite cur_suite;
+ CU_pTest ptest;
+ odp_suiteinfo_t *sinfo;
+ odp_testinfo_t *tinfo;
+ int first = 1;
+
+ cur_suite = CU_get_current_suite();
+ if (cur_suite == NULL)
+ return -1;
+
+ sinfo = cunit_get_suite_info(cur_suite->pName);
+ if (sinfo == NULL)
+ return -1;
+
+ for (tinfo = sinfo->testinfo_tbl; tinfo->name; tinfo++) {
+ ptest = CU_get_test_by_name(tinfo->name, cur_suite);
+ if (ptest == NULL) {
+ fprintf(stderr, "%s: test not found: %s\n",
+ __func__, tinfo->name);
+ return -1;
+ }
+
+ if (ptest->fActive)
+ continue;
+
+ if (first) {
+ printf("\n\nSuite: %s\n", sinfo->name);
+ printf(" Inactive tests:\n");
+ first = 0;
+ }
+
+ printf(" %s\n", tinfo->name);
+ }
+
+ return 0;
+}
+
+int odp_cunit_set_inactive(void)
+{
+ CU_pSuite cur_suite;
+ CU_pTest ptest;
+ odp_suiteinfo_t *sinfo;
+ odp_testinfo_t *tinfo;
+
+ cur_suite = CU_get_current_suite();
+ if (cur_suite == NULL)
+ return -1;
+
+ sinfo = cunit_get_suite_info(cur_suite->pName);
+ if (sinfo == NULL)
+ return -1;
+
+ for (tinfo = sinfo->testinfo_tbl; tinfo->name; tinfo++) {
+ ptest = CU_get_test_by_name(tinfo->name, cur_suite);
+ if (ptest == NULL) {
+ fprintf(stderr, "%s: test not found: %s\n",
+ __func__, tinfo->name);
+ return -1;
+ }
+ CU_set_test_active(ptest, false);
+ }
+
+ return 0;
+}
+
+static int default_term_func(void)
+{
+ return odp_cunit_print_inactive();
+}
+
+static void _cunit_test_setup_func(void)
+{
+ CU_AllTestsCompleteMessageHandler all_test_comp_handler;
+ CU_SuiteCompleteMessageHandler suite_comp_handler;
+ CU_pFailureRecord failrec;
+ CU_pSuite suite;
+
+ if (!getenv("ODP_CUNIT_FAIL_IMMEDIATE"))
+ return;
+
+ if (CU_get_number_of_failure_records() == 0)
+ return;
+
+ /* User wants the suite to fail immediately once a test hits an error */
+ suite = CU_get_current_suite();
+ failrec = CU_get_failure_list();
+
+ printf("Force aborting as a previous test failed\n");
+
+ /* Call the Cleanup functions before aborting */
+ suite->pCleanupFunc();
+
+ suite_comp_handler = CU_get_suite_complete_handler();
+ if (suite_comp_handler)
+ suite_comp_handler(suite, failrec);
+
+ all_test_comp_handler = CU_get_all_test_complete_handler();
+ if (all_test_comp_handler)
+ all_test_comp_handler(failrec);
+
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Register suites and tests with CUnit.
+ *
+ * Similar to CU_register_suites() but using locally defined wrapper
+ * types.
+ */
+static int cunit_register_suites(odp_suiteinfo_t testsuites[])
+{
+ odp_suiteinfo_t *sinfo;
+ odp_testinfo_t *tinfo;
+ CU_pSuite suite;
+ CU_pTest test;
+ CU_CleanupFunc term_func;
+
+ for (sinfo = testsuites; sinfo->name; sinfo++) {
+ term_func = default_term_func;
+ if (sinfo->term_func)
+ term_func = sinfo->term_func;
+
+ suite = CU_add_suite_with_setup_and_teardown(sinfo->name, _cunit_suite_init,
+ term_func, _cunit_test_setup_func,
+ NULL);
+ if (!suite)
+ return CU_get_error();
+
+ for (tinfo = sinfo->testinfo_tbl; tinfo->name; tinfo++) {
+ test = CU_add_test(suite, tinfo->name,
+ tinfo->test_func);
+ if (!test)
+ return CU_get_error();
+ }
+ }
+
+ return 0;
+}
+
+static int cunit_update_test(CU_pSuite suite,
+ odp_suiteinfo_t *sinfo,
+ odp_testinfo_t *updated_tinfo)
+{
+ CU_pTest test = NULL;
+ CU_ErrorCode err;
+ odp_testinfo_t *tinfo;
+ const char *test_name = updated_tinfo->name;
+
+ tinfo = cunit_get_test_info(sinfo, test_name);
+ if (tinfo)
+ test = CU_get_test(suite, test_name);
+
+ if (!tinfo || !test) {
+ fprintf(stderr, "%s: unable to find existing test named %s\n",
+ __func__, test_name);
+ return -1;
+ }
+
+ err = CU_set_test_func(test, updated_tinfo->test_func);
+ if (err != CUE_SUCCESS) {
+ fprintf(stderr, "%s: failed to update test func for %s\n",
+ __func__, test_name);
+ return -1;
+ }
+
+ tinfo->check_active = updated_tinfo->check_active;
+
+ return 0;
+}
+
+static int cunit_update_suite(odp_suiteinfo_t *updated_sinfo)
+{
+ CU_pSuite suite = NULL;
+ CU_ErrorCode err;
+ odp_suiteinfo_t *sinfo;
+ odp_testinfo_t *tinfo;
+
+ /* find previously registered suite with matching name */
+ sinfo = cunit_get_suite_info(updated_sinfo->name);
+
+ if (sinfo) {
+ /* lookup the associated CUnit suite */
+ suite = CU_get_suite_by_name(updated_sinfo->name,
+ CU_get_registry());
+ }
+
+ if (!sinfo || !suite) {
+ fprintf(stderr, "%s: unable to find existing suite named %s\n",
+ __func__, updated_sinfo->name);
+ return -1;
+ }
+
+ sinfo->init_func = updated_sinfo->init_func;
+ sinfo->term_func = updated_sinfo->term_func;
+
+ err = CU_set_suite_cleanupfunc(suite, updated_sinfo->term_func);
+ if (err != CUE_SUCCESS) {
+ fprintf(stderr, "%s: failed to update cleanup func for %s\n",
+ __func__, updated_sinfo->name);
+ return -1;
+ }
+
+ for (tinfo = updated_sinfo->testinfo_tbl; tinfo->name; tinfo++) {
+ int ret;
+
+ ret = cunit_update_test(suite, sinfo, tinfo);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Run tests previously registered via odp_cunit_register()
+ */
+int odp_cunit_run(void)
+{
+ int ret;
+
+ printf("\tODP API version: %s\n", odp_version_api_str());
+ printf("\tODP implementation name: %s\n", odp_version_impl_name());
+ printf("\tODP implementation version: %s\n", odp_version_impl_str());
+
+ if (getenv("ODP_TEST_OUT_XML")) {
+ CU_set_output_filename(progname);
+ CU_automated_run_tests();
+ } else {
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ }
+
+ ret = CU_get_number_of_failure_records();
+
+ CU_cleanup_registry();
+
+ /* call test executable termination hook, if any */
+ if (global_init_term.global_term_ptr &&
+ ((*global_init_term.global_term_ptr)(instance) != 0))
+ return -1;
+
+ return (ret) ? -1 : 0;
+}
+
+/*
+ * Update suites/tests previously registered via odp_cunit_register().
+ *
+ * Note that this is intended for modifying the properties of already
+ * registered suites/tests. New suites/tests can only be registered via
+ * odp_cunit_register().
+ */
+int odp_cunit_update(odp_suiteinfo_t testsuites[])
+{
+ int ret = 0;
+ odp_suiteinfo_t *sinfo;
+
+ for (sinfo = testsuites; sinfo->name && ret == 0; sinfo++)
+ ret = cunit_update_suite(sinfo);
+
+ return ret;
+}
+
+/*
+ * Register test suites to be run via odp_cunit_run()
+ */
+int odp_cunit_register(odp_suiteinfo_t testsuites[])
+{
+ if (threads_init())
+ return -1;
+
+ /* call test executable init hook, if any */
+ if (global_init_term.global_init_ptr) {
+ if ((*global_init_term.global_init_ptr)(&instance) == 0) {
+ /* After ODP initialization, set main thread's
+ * CPU affinity to the 1st available control CPU core
+ */
+ int cpu = 0;
+ odp_cpumask_t cpuset;
+
+ odp_cpumask_zero(&cpuset);
+ if (odp_cpumask_default_control(&cpuset, 1) == 1) {
+ cpu = odp_cpumask_first(&cpuset);
+ odph_odpthread_setaffinity(cpu);
+ }
+ } else {
+ /* ODP initialization failed */
+ return -1;
+ }
+ }
+
+ CU_set_error_action(CUEA_ABORT);
+
+ CU_initialize_registry();
+ global_testsuites = testsuites;
+ cunit_register_suites(testsuites);
+ CU_set_fail_on_inactive(CU_FALSE);
+
+ return 0;
+}
+
+/*
+ * Parse command line options to extract options affecting cunit_common.
+ * (hence also helpers options as cunit_common uses the helpers)
+ * Options private to the test calling cunit_common are not parsed here.
+ */
+int odp_cunit_parse_options(int *argc, char *argv[])
+{
+ const char *ctrl_thread_env = getenv("CI_THREAD_TYPE_CONTROL");
+ const char *env = getenv("CI");
+
+ progname = argv[0];
+ *argc = odph_parse_options(*argc, argv);
+ /* Check if we need to use control thread */
+ if (ctrl_thread_env && !strcmp(ctrl_thread_env, "true"))
+ control_thread = true;
+
+ if (env && !strcmp(env, "true")) {
+ running_in_ci = 1;
+ ODPH_DBG("\nWARNING: test result can be used for code coverage only.\n"
+ "CI=true env variable is set!\n");
+ }
+
+ return 0;
+}
+
+int odp_cunit_ret(int val)
+{
+ return running_in_ci ? 0 : val;
+}
+
+int odp_cunit_ci(void)
+{
+ return running_in_ci;
+}
+
+int odp_cunit_ci_skip(const char *test_name)
+{
+ const char *ci_skip;
+ const char *found;
+
+ ci_skip = getenv("CI_SKIP");
+ if (ci_skip == NULL)
+ return 0;
+
+ found = strstr(ci_skip, test_name);
+
+ return found != NULL;
+}
diff --git a/test/common/odp_cunit_common.h b/test/common/odp_cunit_common.h
new file mode 100644
index 000000000..8a5053589
--- /dev/null
+++ b/test/common/odp_cunit_common.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+/**
+ * @file
+ *
+ * ODP test application common headers
+ */
+
+#ifndef ODP_CUNICT_COMMON_H
+#define ODP_CUNICT_COMMON_H
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <CUnit/Basic.h>
+#include <odp_api.h>
+
+typedef int (*cunit_test_check_active)(void);
+
+typedef struct {
+ const char *name;
+ CU_TestFunc test_func;
+ cunit_test_check_active check_active;
+} odp_testinfo_t;
+
+typedef struct {
+ const char *name;
+ CU_InitializeFunc init_func;
+ CU_CleanupFunc term_func;
+ odp_testinfo_t *testinfo_tbl;
+} odp_suiteinfo_t;
+
+static inline int odp_cunit_test_inactive(void) { return 0; }
+static inline void odp_cunit_test_missing(void) { }
+
+/* An active test case, with the test name matching the test function name */
+#define ODP_TEST_INFO(test_func) \
+ {#test_func, test_func, NULL}
+
+/* A test case that is unconditionally inactive. Its name will be registered
+ * with CUnit but it won't be executed and will be reported as inactive in
+ * the result summary. */
+#define ODP_TEST_INFO_INACTIVE(test_func, ...) \
+ {#test_func, odp_cunit_test_missing, odp_cunit_test_inactive}
+
+#define ODP_TEST_INACTIVE 0
+#define ODP_TEST_ACTIVE 1
+
+/* A test case that may be marked as inactive at runtime based on the
+ * return value of the cond_func function. A return value of ODP_TEST_INACTIVE
+ * means inactive, ODP_TEST_ACTIVE means active. */
+#define ODP_TEST_INFO_CONDITIONAL(test_func, cond_func) \
+ {#test_func, test_func, cond_func}
+
+#define ODP_TEST_INFO_NULL {NULL, NULL, NULL}
+#define ODP_SUITE_INFO_NULL {NULL, NULL, NULL, NULL}
+
+typedef struct {
+ uint32_t foo;
+ uint32_t bar;
+} test_shared_data_t;
+
+/* parse parameters that affect the behaviour of odp_cunit_common */
+int odp_cunit_parse_options(int *argc, char *argv[]);
+/* register suites to be run via odp_cunit_run() */
+int odp_cunit_register(odp_suiteinfo_t testsuites[]);
+/* update tests previously registered via odp_cunit_register() */
+int odp_cunit_update(odp_suiteinfo_t testsuites[]);
+/* the function, called by module main(), to run the testsuites: */
+int odp_cunit_run(void);
+
+/* Create threads for a validation test
+ *
+ * Thread arguments table (arg[]) can be set to NULL, when there are no arguments.
+ * When 'priv' is 0, the same argument pointer (arg[0]) is passed to all threads. Otherwise,
+ * a pointer is passed (from arg[]) to each thread. When 'sync' is 1, thread
+ * creation is synchronized (odph_thread_common_param_t.sync). Returns 0 on success.
+ */
+int odp_cunit_thread_create(int num, int func_ptr(void *arg), void *const arg[],
+ int priv, int sync);
+
+/* Wait for previously created threads to exit */
+int odp_cunit_thread_join(int num);
+
+/**
+ * Global tests initialization/termination.
+ *
+ * Initialize global resources needed by the test executable. Default
+ * definition does ODP init / term (both global and local).
+ * Test executables can override it by calling one of the register function
+ * below.
+ * The functions are called at the very beginning and very end of the test
+ * execution. Passing NULL to odp_cunit_register_global_init() and/or
+ * odp_cunit_register_global_term() is legal and will simply prevent the
+ * default (ODP init/term) to be done.
+ */
+void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst));
+
+void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst));
+
+int odp_cunit_ret(int val);
+int odp_cunit_ci(void);
+int odp_cunit_print_inactive(void);
+int odp_cunit_set_inactive(void);
+
+/* Check from CI_SKIP environment variable if the test case should be skipped by CI */
+int odp_cunit_ci_skip(const char *test_name);
+
+void odp_cu_assert(CU_BOOL value, unsigned int line,
+ const char *condition, const char *file, CU_BOOL fatal);
+
+/*
+ * Wrapper for CU_assertImplementation for the fatal asserts to show the
+ * compiler and static analyzers that the function does not return if the
+ * assertion fails. This reduces bogus warnings generated from the code
+ * after the fatal assert.
+ */
+static inline void odp_cu_assert_fatal(CU_BOOL value, unsigned int line,
+ const char *condition, const char *file)
+{
+ odp_cu_assert(value, line, condition, file, CU_TRUE);
+
+ if (!value) {
+ /* not reached */
+ abort(); /* this has noreturn function attribute */
+ for (;;) /* this also shows that return is not possible */
+ ;
+ }
+}
+
+/*
+ * Redefine the macros used in ODP. Do it without the do-while idiom for
+ * compatibility with CU and existing code that assumes this kind of macros.
+ */
+
+#undef CU_ASSERT
+#define CU_ASSERT(value) \
+ { odp_cu_assert((value), __LINE__, #value, __FILE__, CU_FALSE); }
+
+#undef CU_ASSERT_FATAL
+#define CU_ASSERT_FATAL(value) \
+ { odp_cu_assert_fatal((value), __LINE__, #value, __FILE__); }
+
+#undef CU_FAIL
+#define CU_FAIL(msg) \
+ { odp_cu_assert(CU_FALSE, __LINE__, ("CU_FAIL(" #msg ")"), __FILE__, CU_FALSE); }
+
+#undef CU_FAIL_FATAL
+#define CU_FAIL_FATAL(msg) \
+ { odp_cu_assert_fatal(CU_FALSE, __LINE__, ("CU_FAIL_FATAL(" #msg ")"), __FILE__); }
+
+#undef CU_ASSERT_TRUE
+#undef CU_ASSERT_TRUE_FATAL
+#undef CU_ASSERT_FALSE
+#undef CU_ASSERT_FALSE_FATAL
+#undef CU_ASSERT_EQUAL
+#undef CU_ASSERT_EQUAL_FATAL
+#undef CU_ASSERT_NOT_EQUAL
+#undef CU_ASSERT_NOT_EQUAL_FATAL
+#undef CU_ASSERT_PTR_EQUAL
+#undef CU_ASSERT_PTR_EQUAL_FATAL
+#undef CU_ASSERT_PTR_NOT_EQUAL
+#undef CU_ASSERT_PTR_NOT_EQUAL_FATAL
+#undef CU_ASSERT_PTR_NULL
+#undef CU_ASSERT_PTR_NULL_FATAL
+#undef CU_ASSERT_PTR_NOT_NULL
+#undef CU_ASSERT_PTR_NOT_NULL_FATAL
+#undef CU_ASSERT_STRING_EQUAL
+#undef CU_ASSERT_STRING_EQUAL_FATAL
+#undef CU_ASSERT_STRING_NOT_EQUAL
+#undef CU_ASSERT_STRING_NOT_EQUAL_FATAL
+#undef CU_ASSERT_NSTRING_EQUAL
+#undef CU_ASSERT_NSTRING_EQUAL_FATAL
+#undef CU_ASSERT_NSTRING_NOT_EQUAL
+#undef CU_ASSERT_NSTRING_NOT_EQUAL_FATAL
+#undef CU_ASSERT_DOUBLE_EQUAL
+#undef CU_ASSERT_DOUBLE_EQUAL_FATAL
+#undef CU_ASSERT_DOUBLE_NOT_EQUAL
+#undef CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL
+
+#endif /* ODP_CUNICT_COMMON_H */
diff --git a/test/common/packet_common.c b/test/common/packet_common.c
new file mode 100644
index 000000000..a2b960af6
--- /dev/null
+++ b/test/common/packet_common.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <packet_common.h>
+#include <string.h>
+
+void test_packet_set_md(odp_packet_t pkt)
+{
+ const int binary_flag = 1;
+ const uint32_t offset = 1;
+ uint8_t *uarea = odp_packet_user_area(pkt);
+ uint32_t uarea_size = odp_packet_user_area_size(pkt);
+
+ for (uint32_t n = 0; n < uarea_size; n++)
+ uarea[n] = n;
+
+ /*
+ * Some of these flags cannot be set simultaneously, but we do not
+ * care here if some flags get cleared.
+ */
+ odp_packet_has_l2_set(pkt, binary_flag);
+ odp_packet_has_l3_set(pkt, binary_flag);
+ odp_packet_has_l4_set(pkt, binary_flag);
+ odp_packet_has_eth_set(pkt, binary_flag);
+ odp_packet_has_eth_bcast_set(pkt, binary_flag);
+ odp_packet_has_eth_mcast_set(pkt, !binary_flag);
+ odp_packet_has_jumbo_set(pkt, binary_flag);
+ odp_packet_has_vlan_set(pkt, binary_flag);
+ odp_packet_has_vlan_qinq_set(pkt, binary_flag);
+ odp_packet_has_arp_set(pkt, binary_flag);
+ odp_packet_has_ipv4_set(pkt, binary_flag);
+ odp_packet_has_ipv6_set(pkt, !binary_flag);
+ odp_packet_has_ip_bcast_set(pkt, binary_flag);
+ odp_packet_has_ip_mcast_set(pkt, binary_flag);
+ odp_packet_has_ipfrag_set(pkt, binary_flag);
+ odp_packet_has_ipopt_set(pkt, binary_flag);
+ odp_packet_has_ipsec_set(pkt, binary_flag);
+ odp_packet_has_udp_set(pkt, binary_flag);
+ odp_packet_has_tcp_set(pkt, !binary_flag);
+ odp_packet_has_sctp_set(pkt, binary_flag);
+ odp_packet_has_icmp_set(pkt, binary_flag);
+
+ odp_packet_user_ptr_set(pkt, &pkt);
+ odp_packet_user_flag_set(pkt, binary_flag);
+ (void)odp_packet_l2_offset_set(pkt, offset);
+ (void)odp_packet_l3_offset_set(pkt, offset);
+ (void)odp_packet_l4_offset_set(pkt, offset);
+ odp_packet_flow_hash_set(pkt, 0x12345678);
+ odp_packet_ts_set(pkt, odp_time_local_from_ns(ODP_TIME_SEC_IN_NS));
+ odp_packet_color_set(pkt, ODP_PACKET_YELLOW);
+ odp_packet_drop_eligible_set(pkt, binary_flag);
+ odp_packet_shaper_len_adjust_set(pkt, -42);
+ (void)odp_packet_payload_offset_set(pkt, offset);
+}
+
+void test_packet_get_md(odp_packet_t pkt, test_packet_md_t *md)
+{
+ uint8_t *uarea = odp_packet_user_area(pkt);
+ uint32_t uarea_size = odp_packet_user_area_size(pkt);
+
+ memset(md, 0, sizeof(*md));
+
+ if (uarea)
+ md->user_area_chksum = odp_chksum_ones_comp16(uarea, uarea_size);
+
+ md->has_error = !!odp_packet_has_error(pkt);
+ md->has_l2_error = !!odp_packet_has_l2_error(pkt);
+ md->has_l3_error = !!odp_packet_has_l3_error(pkt);
+ md->has_l4_error = !!odp_packet_has_l4_error(pkt);
+ md->has_l2 = !!odp_packet_has_l2(pkt);
+ md->has_l3 = !!odp_packet_has_l3(pkt);
+ md->has_l4 = !!odp_packet_has_l4(pkt);
+ md->has_eth = !!odp_packet_has_eth(pkt);
+ md->has_eth_bcast = !!odp_packet_has_eth_bcast(pkt);
+ md->has_eth_mcast = !!odp_packet_has_eth_mcast(pkt);
+ md->has_jumbo = !!odp_packet_has_jumbo(pkt);
+ md->has_vlan = !!odp_packet_has_vlan(pkt);
+ md->has_vlan_qinq = !!odp_packet_has_vlan_qinq(pkt);
+ md->has_arp = !!odp_packet_has_arp(pkt);
+ md->has_ipv4 = !!odp_packet_has_ipv4(pkt);
+ md->has_ipv6 = !!odp_packet_has_ipv6(pkt);
+ md->has_ip_bcast = !!odp_packet_has_ip_bcast(pkt);
+ md->has_ip_mcast = !!odp_packet_has_ip_mcast(pkt);
+ md->has_ipfrag = !!odp_packet_has_ipfrag(pkt);
+ md->has_ipopt = !!odp_packet_has_ipopt(pkt);
+ md->has_ipsec = !!odp_packet_has_ipsec(pkt);
+ md->has_udp = !!odp_packet_has_udp(pkt);
+ md->has_tcp = !!odp_packet_has_tcp(pkt);
+ md->has_sctp = !!odp_packet_has_sctp(pkt);
+ md->has_icmp = !!odp_packet_has_icmp(pkt);
+ md->has_flow_hash = !!odp_packet_has_flow_hash(pkt);
+ md->has_ts = !!odp_packet_has_ts(pkt);
+
+ md->len = odp_packet_len(pkt);
+ md->packet_pool = odp_packet_pool(pkt);
+ md->packet_input = odp_packet_input(pkt);
+ md->user_ptr = odp_packet_user_ptr(pkt);
+ md->user_flag = odp_packet_user_flag(pkt);
+ md->l2_offset = odp_packet_l2_offset(pkt);
+ md->l3_offset = odp_packet_l3_offset(pkt);
+ md->l4_offset = odp_packet_l4_offset(pkt);
+ md->l2_type = odp_packet_l2_type(pkt);
+ md->l3_type = odp_packet_l3_type(pkt);
+ md->l4_type = odp_packet_l4_type(pkt);
+ md->l3_chksum_status = odp_packet_l3_chksum_status(pkt);
+ md->l4_chksum_status = odp_packet_l4_chksum_status(pkt);
+ md->flow_hash = md->has_flow_hash ? odp_packet_flow_hash(pkt) : 0;
+ md->ts = md->has_ts ? odp_packet_ts(pkt) : odp_time_global_from_ns(0);
+ md->color = odp_packet_color(pkt);
+ md->drop_eligible = !!odp_packet_drop_eligible(pkt);
+ md->shaper_len_adjust = odp_packet_shaper_len_adjust(pkt);
+ md->cls_mark = odp_packet_cls_mark(pkt);
+ md->has_lso_request = !!odp_packet_has_lso_request(pkt);
+ md->payload_offset = odp_packet_payload_offset(pkt);
+ md->aging_tmo = odp_packet_aging_tmo(pkt);
+ md->has_tx_compl_request = !!odp_packet_has_tx_compl_request(pkt);
+ md->proto_stats = odp_packet_proto_stats(pkt);
+}
+
+int test_packet_is_md_equal(const test_packet_md_t *md_1,
+ const test_packet_md_t *md_2)
+{
+ /*
+ * With certain assumptions this should typically work. If it does
+ * not, we would get false negatives which we should spot as test
+ * failures.
+ */
+
+ return (!memcmp(md_1, md_2, sizeof(*md_1)));
+}
diff --git a/test/common/packet_common.h b/test/common/packet_common.h
new file mode 100644
index 000000000..2ffc5eab0
--- /dev/null
+++ b/test/common/packet_common.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+
+typedef struct test_packet_md_t {
+ int has_error;
+ int has_l2_error;
+ int has_l3_error;
+ int has_l4_error;
+ int has_l2;
+ int has_l3;
+ int has_l4;
+ int has_eth;
+ int has_eth_bcast;
+ int has_eth_mcast;
+ int has_jumbo;
+ int has_vlan;
+ int has_vlan_qinq;
+ int has_arp;
+ int has_ipv4;
+ int has_ipv6;
+ int has_ip_bcast;
+ int has_ip_mcast;
+ int has_ipfrag;
+ int has_ipopt;
+ int has_ipsec;
+ int has_udp;
+ int has_tcp;
+ int has_sctp;
+ int has_icmp;
+ int has_flow_hash;
+ int has_ts;
+ uint32_t len;
+ odp_pool_t packet_pool;
+ odp_pktio_t packet_input;
+ void *user_ptr;
+ uint16_t user_area_chksum;
+ int user_flag;
+ uint32_t l2_offset;
+ uint32_t l3_offset;
+ uint32_t l4_offset;
+ odp_proto_l2_type_t l2_type;
+ odp_proto_l3_type_t l3_type;
+ odp_proto_l4_type_t l4_type;
+ odp_packet_chksum_status_t l3_chksum_status;
+ odp_packet_chksum_status_t l4_chksum_status;
+ uint32_t flow_hash;
+ odp_time_t ts;
+ odp_packet_color_t color;
+ odp_bool_t drop_eligible;
+ int8_t shaper_len_adjust;
+ uint64_t cls_mark;
+ int has_lso_request;
+ uint32_t payload_offset;
+ uint64_t aging_tmo;
+ int has_tx_compl_request;
+ odp_proto_stats_t proto_stats;
+} test_packet_md_t;
+
+void test_packet_set_md(odp_packet_t pkt);
+void test_packet_get_md(odp_packet_t pkt, test_packet_md_t *md);
+int test_packet_is_md_equal(const test_packet_md_t *md_1,
+ const test_packet_md_t *md_2);
diff --git a/test/common/test_common_macros.h b/test/common/test_common_macros.h
new file mode 100644
index 000000000..405f626e9
--- /dev/null
+++ b/test/common/test_common_macros.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef TEST_COMMON_MACROS_H_
+#define TEST_COMMON_MACROS_H_
+
+/*
+ * Common macros for validation tests
+ */
+
+/* Check if 'x' is a power of two value */
+#define TEST_CHECK_POW2(x) ((((x) - 1) & (x)) == 0)
+
+#endif
diff --git a/test/common/test_packet_custom.h b/test/common/test_packet_custom.h
new file mode 100644
index 000000000..5045a14aa
--- /dev/null
+++ b/test/common/test_packet_custom.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2021 Nokia
+ */
+
+#ifndef TEST_PACKET_CUSTOM_H_
+#define TEST_PACKET_CUSTOM_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test packets without CRC */
+
+/* Ethernet type 0x88B5: EthernetIEEE Std 802 - Local Experimental Ethertype 1
+ *
+ * Imaginary, custom protocol on top of Ethernet.
+ */
+static const uint8_t test_packet_custom_eth_1[] = {
+ /* Ethernet */
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x09, 0x00, 0x04, 0x00, 0x88, 0xB5,
+ /* Header fields (16 bit):
+ * packet length, segment number, segment offset, port number */
+ 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x05, 0x67,
+ /* Payload 701 bytes */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
+ 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07,
+ 0x00, 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B,
+ 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F,
+ 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13,
+ 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17,
+ 0x00, 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B,
+ 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F,
+ 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23,
+ 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27,
+ 0x00, 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B,
+ 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F,
+ 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33,
+ 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37,
+ 0x00, 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B,
+ 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F,
+ 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43,
+ 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47,
+ 0x00, 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B,
+ 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F,
+ 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53,
+ 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
+ 0x00, 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B,
+ 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F,
+ 0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63,
+ 0x00, 0x64, 0x00, 0x65, 0x00, 0x66, 0x00, 0x67,
+ 0x00, 0x68, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x6B,
+ 0x00, 0x6C, 0x00, 0x6D, 0x00, 0x6E, 0x00, 0x6F,
+ 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x73,
+ 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x77,
+ 0x00, 0x78, 0x00, 0x79, 0x00, 0x7A, 0x00, 0x7B,
+ 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F,
+ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
+ 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
+ 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00,
+ 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00,
+ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
+ 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
+ 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00,
+ 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00,
+ 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00,
+ 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00,
+ 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00,
+ 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00,
+ 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00,
+ 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00,
+ 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00,
+ 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00,
+ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00,
+ 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00,
+ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00,
+ 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00,
+ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00,
+ 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00,
+ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00,
+ 0xDC, 0x00, 0xDD, 0x00, 0xDE
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/common/test_packet_ipsec.h b/test/common/test_packet_ipsec.h
new file mode 100644
index 000000000..9fb6ae2c2
--- /dev/null
+++ b/test/common/test_packet_ipsec.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef TEST_PACKET_IPSEC_H_
+#define TEST_PACKET_IPSEC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test packets without CRC */
+
+static const uint8_t test_packet_ipv4_ipsec_ah[] = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IPv4 */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xd9, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x6c, 0x2e, 0xf7, 0x1f, 0x7c, 0x70, 0x39, 0xa3,
+ 0x4a, 0x77, 0x01, 0x47, 0x9e, 0x45, 0x73, 0x51,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b
+};
+
+static const uint8_t test_packet_ipv4_ipsec_esp[] = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IPv4 */
+ 0x45, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0x19, 0x18, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IPv4 */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x04,
+
+ /* ICV */
+ 0x73, 0x8d, 0xf6, 0x9a, 0x26, 0x06, 0x4d, 0xa1,
+ 0x88, 0x37, 0x65, 0xab, 0x0d, 0xe9, 0x95, 0x3b
+};
+
+static const uint8_t test_packet_ipv6_ipsec_ah[] = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IPv6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x33, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* AH */
+ 0x29, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x62, 0x96, 0x2b, 0x40, 0x3e, 0x53, 0x76, 0x4a,
+ 0x4d, 0x7f, 0xf6, 0x22, 0x35, 0x3c, 0x74, 0xe2,
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* IPv6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b
+};
+
+static const uint8_t test_packet_ipv6_ipsec_esp[] = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IPv6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x32, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IPv4 */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x04,
+
+ /* ICV */
+ 0x73, 0x8d, 0xf6, 0x9a, 0x26, 0x06, 0x4d, 0xa1,
+ 0x88, 0x37, 0x65, 0xab, 0x0d, 0xe9, 0x95, 0x3b
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/common/test_packet_ipv4.h b/test/common/test_packet_ipv4.h
new file mode 100644
index 000000000..c9c0ef09f
--- /dev/null
+++ b/test/common/test_packet_ipv4.h
@@ -0,0 +1,457 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#ifndef TEST_PACKET_IPV4_H_
+#define TEST_PACKET_IPV4_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test packets without CRC */
+
+/* ARP request */
+static const uint8_t test_packet_arp[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x06, 0x00, 0x01,
+ 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0xC0, 0xA8, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8,
+ 0x01, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11
+};
+
+/* ICMPv4 echo reply */
+static const uint8_t test_packet_ipv4_icmp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01,
+ 0xF3, 0x7B, 0xC0, 0xA8, 0x01, 0x01, 0xC4, 0xA8,
+ 0x01, 0x02, 0x00, 0x00, 0xB7, 0xAB, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11
+};
+
+/* IPv4 TCP */
+static const uint8_t test_packet_ipv4_tcp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06,
+ 0xF3, 0x76, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
+ 0x01, 0x01, 0x04, 0xD2, 0x10, 0xE1, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x50, 0x02,
+ 0x00, 0x00, 0x0C, 0xCA, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05
+};
+
+/* IPv4 UDP */
+static const uint8_t test_packet_ipv4_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xF3, 0x6B, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
+ 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1A,
+ 0x2F, 0x97, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11
+};
+
+/* ETH SNAP IPv4 UDP */
+static const uint8_t test_packet_snap_ipv4_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x00, 0x36, 0xAA, 0xAA,
+ 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xF7, 0x6B, 0xC0, 0xA8, 0x01, 0x02, 0xC0, 0xA8,
+ 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1A,
+ 0x33, 0x97, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11
+};
+
+/* VLAN IPv4 UDP
+ * - type 0x8100, tag 23
+ */
+static const uint8_t test_packet_vlan_ipv4_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x17,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x2A, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x11, 0xF3, 0x6F, 0xC0, 0xA8,
+ 0x01, 0x02, 0xC4, 0xA8, 0x01, 0x01, 0x00, 0x3F,
+ 0x00, 0x3F, 0x00, 0x16, 0x4D, 0xBF, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D
+};
+
+/* VLAN Q-in-Q IPv4 UDP
+ * - Outer: type 0x88a8, tag 1
+ * - Inner: type 0x8100, tag 2
+ */
+static const uint8_t test_packet_vlan_qinq_ipv4_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x88, 0xA8, 0x00, 0x01,
+ 0x81, 0x00, 0x00, 0x02, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xF3, 0x73, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
+ 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x12,
+ 0x63, 0xDF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09
+};
+
+/* IPv4 SCTP
+ * - chunk type: payload data
+ */
+static const uint8_t test_packet_ipv4_sctp[] = {
+ 0x00, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x77, 0x00, 0x01, 0x00, 0x00, 0x40, 0x84,
+ 0xF8, 0xAE, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
+ 0x00, 0x02, 0x04, 0xD2, 0x16, 0x2E, 0xDE, 0xAD,
+ 0xBE, 0xEF, 0x31, 0x44, 0xE3, 0xFE, 0x00, 0x00,
+ 0x00, 0x57, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x79,
+ 0x20, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x20, 0x70,
+ 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x73,
+ 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x6C, 0x65, 0x6E, 0x67, 0x74,
+ 0x68, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
+ 0x20, 0x69, 0x73, 0x20, 0x37, 0x31, 0x20, 0x62,
+ 0x79, 0x74, 0x65, 0x73, 0x2E
+};
+
+static const uint8_t test_packet_mcast_eth_ipv4_udp[] = {
+ 0x03, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x02, 0x00,
+ 0x00, 0x03, 0x04, 0x05, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x63, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0xC8, 0xDB, 0xC0, 0xA8, 0x00, 0x01, 0xEF, 0x01,
+ 0x02, 0x03, 0x04, 0xD2, 0x16, 0x2E, 0x00, 0x4F,
+ 0x25, 0x61, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x6D, 0x79, 0x20, 0x64, 0x75, 0x6D,
+ 0x6D, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6C, 0x6F,
+ 0x61, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6C,
+ 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x37, 0x31, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73,
+ 0x2E
+};
+
+static const uint8_t test_packet_bcast_eth_ipv4_udp[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00,
+ 0x00, 0x03, 0x04, 0x05, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x63, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0xB9, 0xE0, 0xC0, 0xA8, 0x00, 0x01, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x04, 0xD2, 0x16, 0x2E, 0x00, 0x4F,
+ 0x16, 0x66, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x6D, 0x79, 0x20, 0x64, 0x75, 0x6D,
+ 0x6D, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6C, 0x6F,
+ 0x61, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6C,
+ 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x37, 0x31, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73,
+ 0x2E
+};
+
+static const uint8_t test_packet_ipv4_udp_first_frag[] = {
+ 0x02, 0x00, 0x00, 0x04, 0x05, 0x06, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x63, 0x00, 0x01, 0x20, 0x00, 0x40, 0x11,
+ 0xD9, 0x35, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
+ 0x00, 0x02, 0x04, 0xD2, 0x16, 0x2E, 0x01, 0x17,
+ 0x54, 0xF3, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x6D, 0x79, 0x20, 0x64, 0x75, 0x6D,
+ 0x6D, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6C, 0x6F,
+ 0x61, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6C,
+ 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x37, 0x31, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73,
+ 0x2E
+};
+
+static const uint8_t test_packet_ipv4_udp_last_frag[] = {
+ 0x02, 0x00, 0x00, 0x04, 0x05, 0x06, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x5B, 0x00, 0x01, 0x00, 0x0A, 0x40, 0x11,
+ 0xF9, 0x33, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
+ 0x00, 0x02, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x6D, 0x79, 0x20, 0x64, 0x75, 0x6D,
+ 0x6D, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6C, 0x6F,
+ 0x61, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6C,
+ 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x37, 0x31, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73,
+ 0x2E
+};
+
+/* IPv4 / Record Route + NOP options / ICMP */
+static const uint8_t test_packet_ipv4_rr_nop_icmp[] = {
+ 0x02, 0x00, 0x00, 0x04, 0x05, 0x06, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x08, 0x00, 0x49, 0x00,
+ 0x00, 0x2C, 0x00, 0x01, 0x00, 0x00, 0x40, 0x01,
+ 0x8E, 0xE2, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
+ 0x00, 0x02, 0x07, 0x0F, 0x0C, 0xC0, 0xA8, 0x04,
+ 0x01, 0xC0, 0xA8, 0x05, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x08, 0x00, 0xF7, 0xFF, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+/* Ethernet/IPv4/UDP packet. Ethernet frame length 325 bytes (+ CRC).
+ * - source IP addr: 192.168.1.2
+ * - destination IP addr: 192.168.1.1
+ */
+static const uint8_t test_packet_ipv4_udp_325[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x37, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xF6, 0x62, 0xC0, 0xA8, 0x01, 0x02, 0xC0, 0xA8,
+ 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x01, 0x23,
+ 0x02, 0xED, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A
+};
+
+/* Ethernet/IPv4/UDP packet. Ethernet frame length 1500 bytes (+ CRC). */
+static const uint8_t test_packet_ipv4_udp_1500[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x05, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xF1, 0xCB, 0xC0, 0xA8, 0x01, 0x02, 0xC0, 0xA8,
+ 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x05, 0xBA,
+ 0xF8, 0x59, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
+ 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
+ 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+ 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/common/test_packet_ipv4_with_crc.h b/test/common/test_packet_ipv4_with_crc.h
new file mode 100644
index 000000000..f0763823b
--- /dev/null
+++ b/test/common/test_packet_ipv4_with_crc.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef TEST_PACKET_IPV4_WITH_CRC_H_
+#define TEST_PACKET_IPV4_WITH_CRC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Ethernet/IPv4/UDP test packets with CRC. Last 4 bytes are the Ethernet FCS. */
+
+/* Frame length is 64 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_64_crc[] = {
+ 0x12, 0xA8, 0x87, 0x93, 0x25, 0x39, 0x1A, 0x29,
+ 0x92, 0x49, 0x00, 0x32, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0x8D, 0xC0, 0xA8, 0xDE, 0xC7, 0xC0, 0xA8,
+ 0x6F, 0x19, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1A,
+ 0xA8, 0x69, 0xD3, 0xFA, 0x53, 0xCD, 0xFF, 0xF7,
+ 0x11, 0xB0, 0x3B, 0xD1, 0x1F, 0xF4, 0x64, 0xBB,
+ 0x70, 0x11, 0x1D, 0x9E, 0x00, 0xA8, 0xC6, 0xBA
+};
+
+/* Frame length is 68 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_68_crc[] = {
+ 0x12, 0x69, 0x5C, 0xDF, 0xB0, 0xDB, 0x1A, 0x38,
+ 0xC8, 0x96, 0x1E, 0x5A, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAA, 0xBA, 0xC0, 0xA8, 0xDE, 0xF8, 0xC0, 0xA8,
+ 0x6F, 0xB7, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1E,
+ 0x06, 0xFC, 0x73, 0x90, 0xFC, 0x24, 0x58, 0xE6,
+ 0x2F, 0x29, 0xA7, 0x18, 0x77, 0xF8, 0x8D, 0xB6,
+ 0xF5, 0xB6, 0xE4, 0x3A, 0x09, 0xD8, 0x9F, 0xE0,
+ 0x88, 0xEB, 0x77, 0x75
+};
+
+/* Frame length is 70 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_70_crc[] = {
+ 0x12, 0x7F, 0x60, 0x61, 0x5F, 0x98, 0x1A, 0x18,
+ 0x74, 0x06, 0x52, 0x94, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0xDD, 0xC0, 0xA8, 0xDE, 0x89, 0xC0, 0xA8,
+ 0x6F, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x20,
+ 0xDC, 0xBC, 0xC2, 0x76, 0xB8, 0x05, 0x50, 0x86,
+ 0x90, 0xA3, 0x46, 0x76, 0x89, 0x9B, 0xF8, 0xD9,
+ 0x3A, 0x36, 0x58, 0x64, 0xD5, 0xF2, 0xBE, 0xA2,
+ 0x07, 0xD5, 0x80, 0x25, 0x82, 0x94
+};
+
+/* Frame length is 71 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_71_crc[] = {
+ 0x12, 0x37, 0x9F, 0x5A, 0x81, 0x77, 0x1A, 0xB2,
+ 0x2F, 0x2F, 0xF8, 0xAE, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0x6B, 0xC0, 0xA8, 0xDE, 0xA2, 0xC0, 0xA8,
+ 0x6F, 0x59, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x21,
+ 0xAA, 0x78, 0x09, 0xE6, 0xC7, 0x2B, 0x99, 0x6D,
+ 0xC7, 0xA9, 0xE2, 0x8E, 0xB7, 0x21, 0xE0, 0x9C,
+ 0xAC, 0x23, 0x77, 0x44, 0xB0, 0x61, 0x1C, 0x70,
+ 0x15, 0xB7, 0xD3, 0x4D, 0x8E, 0xB3, 0xA4
+};
+
+/* Frame length is 287 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_287_crc[] = {
+ 0x12, 0x16, 0xB7, 0x09, 0x63, 0x96, 0x1A, 0xDE,
+ 0xBE, 0x52, 0xEC, 0x53, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAA, 0x5F, 0xC0, 0xA8, 0xDE, 0xB6, 0xC0, 0xA8,
+ 0x6F, 0x79, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0xF9,
+ 0x99, 0x92, 0xA3, 0x3E, 0x1A, 0xAA, 0xFF, 0xA6,
+ 0xAB, 0xDD, 0xF5, 0x55, 0x28, 0x08, 0xCA, 0x0E,
+ 0x99, 0x27, 0x61, 0xB5, 0x9D, 0x65, 0x01, 0x07,
+ 0x55, 0x79, 0x57, 0x89, 0x7D, 0x35, 0xBF, 0xBE,
+ 0xDC, 0x5C, 0xBF, 0x9D, 0xDF, 0xB5, 0xD4, 0x42,
+ 0x81, 0x5D, 0x72, 0xE4, 0x6D, 0x61, 0x46, 0x07,
+ 0x67, 0x8C, 0xBE, 0xF3, 0xE8, 0x7E, 0xCB, 0x64,
+ 0x08, 0xFE, 0xE6, 0x83, 0xA1, 0x92, 0x51, 0x15,
+ 0xB2, 0x4C, 0x9F, 0xF3, 0x01, 0xE6, 0x76, 0xBA,
+ 0x05, 0x12, 0x94, 0xC3, 0x02, 0x0B, 0x10, 0x56,
+ 0x76, 0x70, 0xE2, 0x1A, 0xB4, 0x52, 0xA6, 0xD0,
+ 0xCF, 0x8C, 0x9D, 0x41, 0xB9, 0x52, 0xF5, 0x75,
+ 0xAC, 0x0D, 0x4A, 0x26, 0xC9, 0x66, 0x6C, 0x74,
+ 0x00, 0xA1, 0x63, 0xDB, 0x2F, 0x2D, 0xB0, 0x61,
+ 0x8E, 0x79, 0xD6, 0x14, 0x4A, 0x09, 0x19, 0xB3,
+ 0x70, 0xC8, 0x86, 0xAC, 0x0D, 0xA0, 0x33, 0x46,
+ 0x94, 0x48, 0xC8, 0x20, 0x7F, 0x5D, 0x3E, 0xDA,
+ 0x39, 0xB5, 0xE7, 0x12, 0x3C, 0xF0, 0xAF, 0x92,
+ 0x76, 0x4F, 0xA1, 0xC7, 0xF2, 0xCA, 0xAD, 0x76,
+ 0xB4, 0x5C, 0xA8, 0xAA, 0xE5, 0xA2, 0x94, 0xF1,
+ 0x30, 0xA4, 0x22, 0xC7, 0x6B, 0xF3, 0x75, 0x53,
+ 0x7A, 0xF4, 0x29, 0x51, 0x70, 0x7B, 0x94, 0x50,
+ 0xF8, 0x9B, 0x4B, 0x1D, 0xF4, 0xBD, 0xE8, 0x7F,
+ 0x63, 0xF0, 0x0B, 0x24, 0x88, 0x80, 0x9F, 0xDC,
+ 0x49, 0xCA, 0x5F, 0x05, 0xD5, 0x4E, 0x98, 0x46,
+ 0x89, 0x06, 0x30, 0x81, 0x15, 0xF7, 0xE7, 0x02,
+ 0xDA, 0x05, 0xDD, 0xFD, 0x97, 0x0B, 0x55, 0x37,
+ 0x45, 0x2B, 0xB8, 0x03, 0x3F, 0x63, 0xDD, 0x70,
+ 0xA6, 0x61, 0x87, 0xC1, 0x04, 0x99, 0x2F, 0x1D,
+ 0x2F, 0x94, 0x04, 0x88, 0x71, 0x8B, 0x31, 0x12,
+ 0xE5, 0x34, 0x5E, 0x01, 0x24, 0x62, 0x28
+};
+
+/* Frame length is 400 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_400_crc[] = {
+ 0x12, 0x61, 0x73, 0x60, 0x9D, 0x86, 0x1A, 0x85,
+ 0x0E, 0xF2, 0x3B, 0x2E, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAA, 0xB3, 0xC0, 0xA8, 0xDE, 0x0B, 0xC0, 0xA8,
+ 0x6F, 0x5F, 0x00, 0x3F, 0x00, 0x3F, 0x01, 0x6A,
+ 0x16, 0x66, 0xE1, 0xC0, 0xC1, 0x8E, 0xA5, 0x1A,
+ 0x01, 0x2A, 0x00, 0xBB, 0x41, 0x3B, 0x9C, 0xB8,
+ 0x18, 0x8A, 0x70, 0x3C, 0xD6, 0xBA, 0xE3, 0x1C,
+ 0xC6, 0x7B, 0x34, 0xB1, 0xB0, 0x00, 0x19, 0xE6,
+ 0xA2, 0xB2, 0xA6, 0x90, 0xF0, 0x26, 0x71, 0xFE,
+ 0x7A, 0x4C, 0xF4, 0xD1, 0x2D, 0xEA, 0x32, 0x0E,
+ 0xCD, 0x49, 0x9E, 0x72, 0xF1, 0x2F, 0x38, 0x06,
+ 0x4F, 0x0C, 0xF9, 0xF3, 0x39, 0x78, 0x71, 0x96,
+ 0x68, 0xDD, 0xAF, 0xD7, 0xF9, 0x71, 0x61, 0xB7,
+ 0xB5, 0x68, 0x3C, 0x29, 0x95, 0x67, 0x9E, 0x23,
+ 0x85, 0x3B, 0x72, 0xF4, 0x69, 0xCB, 0x55, 0xD8,
+ 0x5E, 0x4B, 0xF6, 0xCA, 0x42, 0xB3, 0xC3, 0x99,
+ 0x76, 0x70, 0xC2, 0x3E, 0xE2, 0x59, 0xBC, 0x6D,
+ 0x3A, 0xE4, 0xA1, 0x6A, 0x80, 0x9A, 0x28, 0x1E,
+ 0xCB, 0xC8, 0xB6, 0x6A, 0x46, 0x78, 0x81, 0xBB,
+ 0x7B, 0x9F, 0xF5, 0xDF, 0xD2, 0x98, 0x57, 0x17,
+ 0x54, 0xD1, 0xA8, 0x6D, 0xB5, 0xC5, 0xCC, 0x47,
+ 0x92, 0x2A, 0xEB, 0x3D, 0xF7, 0x6B, 0x18, 0x28,
+ 0x24, 0x58, 0x30, 0x7B, 0x91, 0x1D, 0x05, 0xD7,
+ 0x2F, 0x70, 0xBC, 0xD9, 0xF1, 0x0F, 0x74, 0x37,
+ 0x8B, 0x6A, 0x29, 0x0B, 0x7A, 0x9C, 0xD7, 0x6E,
+ 0x44, 0xA0, 0xE2, 0x49, 0x01, 0xC2, 0xB5, 0x68,
+ 0x1A, 0x53, 0xA9, 0xD0, 0x51, 0xA1, 0x29, 0x53,
+ 0x01, 0x27, 0x15, 0x61, 0xA7, 0x00, 0x63, 0x21,
+ 0xA2, 0xA2, 0x0C, 0xC0, 0x37, 0xC8, 0x26, 0x0A,
+ 0xD8, 0xB0, 0x4D, 0x37, 0xA6, 0x87, 0x48, 0x07,
+ 0x34, 0x22, 0xEA, 0x11, 0x8E, 0xEE, 0x35, 0x57,
+ 0x7A, 0x2A, 0xC6, 0x1F, 0xFD, 0x53, 0x6D, 0xFE,
+ 0x21, 0xE0, 0x1B, 0x36, 0xF6, 0x30, 0x01, 0x42,
+ 0xD7, 0xC1, 0xF6, 0xAE, 0xEE, 0xA2, 0x19, 0x2C,
+ 0xFB, 0x2B, 0xB8, 0xE5, 0x50, 0xEB, 0x71, 0x0D,
+ 0x20, 0xE2, 0x97, 0xBA, 0xFA, 0xF0, 0xD8, 0xF6,
+ 0x91, 0x8E, 0x1C, 0x12, 0xBE, 0xBC, 0xAF, 0x3E,
+ 0xC8, 0x3A, 0xA3, 0x57, 0xE2, 0xFB, 0x70, 0x00,
+ 0xF5, 0xD7, 0xDE, 0xF4, 0xA0, 0x80, 0x25, 0x9B,
+ 0x7E, 0xB7, 0x52, 0xDB, 0xA7, 0xC0, 0xEC, 0x30,
+ 0x79, 0x13, 0xD8, 0xFF, 0x98, 0x54, 0x7A, 0x27,
+ 0x33, 0x85, 0x1D, 0xDA, 0x89, 0x7B, 0x95, 0xAB,
+ 0xAC, 0x8E, 0x22, 0xE7, 0x85, 0x95, 0x98, 0x29,
+ 0x19, 0x12, 0xBD, 0x29, 0x0A, 0xA9, 0xF3, 0xD5,
+ 0x61, 0xD7, 0x17, 0xA3, 0x8A, 0xE0, 0xA8, 0x25,
+ 0xA0, 0x09, 0x2C, 0xDE, 0xEC, 0x08, 0xCF, 0x54,
+ 0xA8, 0xB9, 0x4E, 0x66, 0x08, 0x12, 0x13, 0xE0,
+ 0x7B, 0x59, 0xA2, 0x4D, 0x2E, 0x94, 0x33, 0x0C,
+ 0xD1, 0x42, 0xA0, 0xA6, 0xCC, 0x80, 0x7F, 0xA8
+};
+
+/* Frame length is 503 bytes with CRC. */
+static const uint8_t test_packet_ipv4_udp_503_crc[] = {
+ 0x12, 0xA5, 0xD4, 0xC0, 0xC6, 0xC4, 0x1A, 0xCA,
+ 0xB9, 0x97, 0x2B, 0x57, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAA, 0x01, 0xC0, 0xA8, 0xDE, 0x34, 0xC0, 0xA8,
+ 0x6F, 0x81, 0x00, 0x3F, 0x00, 0x3F, 0x01, 0xD1,
+ 0xD3, 0xC9, 0x46, 0x40, 0x04, 0x0F, 0x98, 0xEF,
+ 0x43, 0xB0, 0xDC, 0xEC, 0x33, 0x67, 0x66, 0x85,
+ 0xFD, 0x87, 0xC9, 0x58, 0xD7, 0x41, 0x02, 0xB8,
+ 0xD7, 0x24, 0xF2, 0x53, 0x04, 0xE5, 0x12, 0x11,
+ 0xB3, 0x5C, 0x62, 0x3F, 0x11, 0xD0, 0xA2, 0xEC,
+ 0xFC, 0x74, 0xC5, 0x93, 0x17, 0x31, 0xA3, 0xFB,
+ 0x1D, 0xA6, 0xEE, 0x5C, 0x75, 0x6E, 0x67, 0xC3,
+ 0x07, 0xA1, 0x51, 0xB7, 0x4F, 0x9D, 0x26, 0x1D,
+ 0xAF, 0x06, 0x8A, 0x59, 0x0A, 0x0B, 0x7D, 0xB6,
+ 0x8E, 0xEB, 0xD4, 0x08, 0xD1, 0xB8, 0xEA, 0x90,
+ 0x20, 0x5C, 0x93, 0x1F, 0x14, 0xD9, 0x51, 0x7E,
+ 0x65, 0xD5, 0xCB, 0x0E, 0x03, 0x55, 0x7A, 0xAC,
+ 0x63, 0xC9, 0xA6, 0xD7, 0x17, 0x49, 0x91, 0x15,
+ 0xA2, 0x1E, 0xF2, 0x92, 0x8A, 0x84, 0xA4, 0x0B,
+ 0xAF, 0xAE, 0xA0, 0xEA, 0xDA, 0x0B, 0x29, 0xB3,
+ 0x99, 0xC8, 0x46, 0x9E, 0x4B, 0x96, 0x75, 0x86,
+ 0x77, 0xAD, 0x9E, 0x01, 0x62, 0x10, 0x46, 0xD1,
+ 0xE0, 0x13, 0x05, 0x7B, 0x6A, 0x1B, 0x3A, 0x35,
+ 0x71, 0xA6, 0xFD, 0x05, 0xF2, 0x8B, 0x55, 0x28,
+ 0x4B, 0x82, 0xAB, 0xB1, 0x4D, 0xE6, 0x7F, 0x72,
+ 0x92, 0xBA, 0x5A, 0x1F, 0x10, 0xEB, 0x04, 0xB1,
+ 0xEF, 0xD4, 0xF6, 0x09, 0x98, 0x07, 0x12, 0xD6,
+ 0x0F, 0x4B, 0x92, 0xB8, 0x82, 0xE1, 0x3F, 0xA6,
+ 0x22, 0x0C, 0xE3, 0x8D, 0x31, 0xD0, 0x00, 0x39,
+ 0x5C, 0xF9, 0xC2, 0x79, 0x4C, 0x5F, 0x33, 0x7E,
+ 0x78, 0x69, 0xAE, 0x85, 0x3D, 0xD0, 0x96, 0xB6,
+ 0x30, 0xA5, 0x47, 0x4B, 0xB3, 0x96, 0x4D, 0xF4,
+ 0xC6, 0x6D, 0xD5, 0x7A, 0x20, 0xD9, 0x60, 0xA3,
+ 0x7F, 0x71, 0xBF, 0x57, 0x3C, 0xF6, 0x3B, 0x00,
+ 0x22, 0xD8, 0x14, 0x37, 0x80, 0xFC, 0x2D, 0x9C,
+ 0x7D, 0xBD, 0x05, 0x05, 0xAC, 0x31, 0xE9, 0xDC,
+ 0xE0, 0xAD, 0x68, 0xC2, 0x42, 0x8B, 0x08, 0x78,
+ 0xA0, 0x2A, 0x37, 0x00, 0x08, 0x37, 0x84, 0xFF,
+ 0x96, 0x2B, 0x0F, 0x66, 0x8A, 0x15, 0x3E, 0x51,
+ 0x9D, 0x9A, 0xB2, 0x30, 0x96, 0x3A, 0x7A, 0x24,
+ 0x18, 0xD5, 0x86, 0xAC, 0xBE, 0x6D, 0x5E, 0x80,
+ 0x6A, 0x2D, 0x14, 0xBD, 0xD9, 0xAA, 0x76, 0x43,
+ 0x7B, 0x6A, 0x89, 0x5B, 0x82, 0xA3, 0x33, 0x9E,
+ 0x39, 0x44, 0x38, 0x12, 0x98, 0x39, 0x68, 0x95,
+ 0x15, 0xEB, 0x16, 0x7F, 0xBC, 0x07, 0xCB, 0x83,
+ 0x82, 0x81, 0x3C, 0xD6, 0xD7, 0xD8, 0x7A, 0x93,
+ 0x7A, 0x9B, 0x69, 0x5F, 0x91, 0x2C, 0x73, 0x49,
+ 0xF9, 0xC4, 0x7D, 0xF3, 0xDB, 0xB7, 0x1A, 0xF7,
+ 0x80, 0xFA, 0xFF, 0x84, 0x66, 0xE2, 0xB8, 0x48,
+ 0x93, 0x2E, 0x99, 0x93, 0x29, 0x48, 0xF6, 0xB9,
+ 0x3B, 0xC8, 0x97, 0xB8, 0xDF, 0x3A, 0x66, 0x1A,
+ 0x84, 0x21, 0x6B, 0x1D, 0x86, 0x3C, 0xFA, 0x12,
+ 0x00, 0x07, 0x2B, 0x03, 0xE2, 0x85, 0x8B, 0x98,
+ 0x43, 0x3D, 0x11, 0x3B, 0xF8, 0x82, 0x54, 0x7B,
+ 0x65, 0xF8, 0xFA, 0xAE, 0x93, 0x54, 0x74, 0xDB,
+ 0x83, 0x63, 0xE9, 0xD7, 0xC2, 0x4E, 0x6F, 0xAD,
+ 0x3E, 0x1C, 0x81, 0x43, 0x58, 0x78, 0xAE, 0x3B,
+ 0x3A, 0xB5, 0x8E, 0x18, 0x6B, 0x0F, 0xFA, 0xA2,
+ 0xA0, 0x34, 0x7C, 0x8B, 0xD6, 0x03, 0x05, 0x52,
+ 0x9D, 0x93, 0xDE, 0x68, 0xB7, 0x77, 0xE1, 0x92,
+ 0xE1, 0x40, 0xE9, 0x8E, 0xF1, 0x44, 0x87, 0xF9,
+ 0x21, 0x9E, 0xF7, 0x70, 0xAB, 0x76, 0x52, 0xF6,
+ 0x96, 0x83, 0x04, 0x4C, 0x80, 0xEF, 0x86
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/common/test_packet_ipv6.h b/test/common/test_packet_ipv6.h
new file mode 100644
index 000000000..427f7fbdd
--- /dev/null
+++ b/test/common/test_packet_ipv6.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#ifndef TEST_PACKET_IPV6_H_
+#define TEST_PACKET_IPV6_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test packets without CRC */
+
+/* ICMPv6 echo request */
+static const uint8_t test_packet_ipv6_icmp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
+ 0x00, 0x00, 0x00, 0x08, 0x3A, 0xFF, 0xFE, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
+ 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
+ 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x80, 0x00,
+ 0x1B, 0xC2, 0x00, 0x01, 0x00, 0x02
+};
+
+/* IPv6 TCP */
+static const uint8_t test_packet_ipv6_tcp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
+ 0x00, 0x00, 0x00, 0x14, 0x06, 0xFF, 0xFE, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
+ 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
+ 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x04, 0xD2,
+ 0x10, 0xE1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 0x50, 0x02, 0x00, 0x00, 0x36, 0x35,
+ 0x00, 0x00
+};
+
+/* IPv6 UDP */
+static const uint8_t test_packet_ipv6_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
+ 0x00, 0x00, 0x00, 0x08, 0x11, 0xFF, 0xFE, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
+ 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
+ 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x00, 0x3F,
+ 0x00, 0x3F, 0x00, 0x08, 0x9B, 0x68
+};
+
+/* VLAN IPv6
+ * - type 0x8100, tag 23
+ */
+static const uint8_t test_packet_vlan_ipv6_udp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x17,
+ 0x86, 0xDD, 0x60, 0x30, 0x00, 0x00, 0x00, 0x08,
+ 0x11, 0xFF, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x09, 0xFF, 0xFE, 0x00,
+ 0x04, 0x00, 0x35, 0x55, 0x55, 0x55, 0x66, 0x66,
+ 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x88, 0x88,
+ 0x88, 0x88, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x08,
+ 0x9B, 0x68
+};
+
+/* IPv6 SCTP
+ * - chunk type: payload data
+ */
+static const uint8_t test_packet_ipv6_sctp[] = {
+ 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
+ 0x00, 0x00, 0x00, 0x63, 0x84, 0xFF, 0xFE, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
+ 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
+ 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x04, 0xD2,
+ 0x16, 0x2E, 0xDE, 0xAD, 0xBE, 0xEF, 0x31, 0x44,
+ 0xE3, 0xFE, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00,
+ 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x73, 0x20, 0x6D, 0x79, 0x20, 0x64, 0x75, 0x6D,
+ 0x6D, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6C, 0x6F,
+ 0x61, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6C,
+ 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x37, 0x31, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73,
+ 0x2E
+};
+
+/* Multi-cast Ethernet, IPv6 UDP */
+static const uint8_t test_packet_mcast_eth_ipv6_udp[] = {
+ 0x33, 0x33, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00,
+ 0x00, 0x03, 0x04, 0x05, 0x86, 0xDD, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x4F, 0x11, 0x40, 0xFE, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFE, 0x03, 0x04, 0x05, 0xFF, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x04, 0xD2,
+ 0x16, 0x2E, 0x00, 0x4F, 0xD6, 0x79, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x79,
+ 0x20, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x20, 0x70,
+ 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x73,
+ 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x20, 0x54,
+ 0x68, 0x65, 0x20, 0x6C, 0x65, 0x6E, 0x67, 0x74,
+ 0x68, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
+ 0x20, 0x69, 0x73, 0x20, 0x37, 0x31, 0x20, 0x62,
+ 0x79, 0x74, 0x65, 0x73, 0x2E
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/common_plat/Makefile.am b/test/common_plat/Makefile.am
deleted file mode 100644
index af78bb653..000000000
--- a/test/common_plat/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-SUBDIRS =
-
-if cunit_support
-SUBDIRS += common
-endif
-
-SUBDIRS += performance miscellaneous validation
diff --git a/test/common_plat/common/Makefile.am b/test/common_plat/common/Makefile.am
deleted file mode 100644
index 7d88d2ea5..000000000
--- a/test/common_plat/common/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-AUTOMAKE_OPTIONS = foreign
-include $(top_srcdir)/test/Makefile.inc
-
-noinst_LTLIBRARIES = libcunit_common.la libcpumask_common.la libthrmask_common.la
-
-libcunit_common_la_SOURCES = odp_cunit_common.c
-libcunit_common_la_LIBADD = $(CUNIT_LIBS)
-
-libcpumask_common_la_SOURCES = mask_common.c
-
-libthrmask_common_la_SOURCES = mask_common.c
-libthrmask_common_la_CFLAGS = $(AM_CFLAGS) -DTEST_THRMASK
-
-EXTRA_DIST = mask_common.h odp_cunit_common.h
diff --git a/test/common_plat/common/mask_common.c b/test/common_plat/common/mask_common.c
deleted file mode 100644
index b31534c64..000000000
--- a/test/common_plat/common/mask_common.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-
-#include "odp_cunit_common.h"
-#include "mask_common.h"
-
-#include <stdlib.h>
-
-/*
- * The following strings are used to build masks with odp_*mask_from_str().
- * Both 0x prefixed and non prefixed hex values are supported.
- */
-#define TEST_MASK_NONE "0x0"
-#define TEST_MASK_0 "0x1"
-#define TEST_MASK_1 "0x2"
-#define TEST_MASK_2 "0x4"
-#define TEST_MASK_0_2 "0x5"
-#define TEST_MASK_0_3 "0x9"
-#define TEST_MASK_1_2 "0x6"
-#define TEST_MASK_1_3 "0xA"
-#define TEST_MASK_0_1_2 "0x7"
-#define TEST_MASK_0_2_4_6 "0x55"
-#define TEST_MASK_1_2_4_6 "0x56"
-
-#define TEST_MASK_0_NO_PREFIX "1"
-
-/* padding pattern used to check buffer overflow: */
-#define FILLING_PATTERN 0x55
-
-/*
- * returns the length of a string, excluding terminating NULL.
- * As its C lib strlen equivalent. Just rewritten here to avoid C lib
- * dependency in ODP tests (for platform independent / bare metal testing)
- */
-static unsigned int stringlen(const char *str)
-{
- unsigned int i = 0;
-
- while (str[i] != 0)
- i++;
- return i;
-}
-
-/*
- * builds a string containing a 0x prefixed hex number where a single bit
- * (corresponding to a cpu or thread) is set.
- * The string is null terminated.
- * bit_set_str(0) returns "0x1".
- * bit_set_str(10) returns "0x400".
- * The buffer should be at least ceil(offs/4)+3 bytes long,
- * to accommodate with 4 bits per nibble + "0x" prefix + null.
- */
-#define BITS_PER_NIBBLE 4
-static void bit_set_str(char *buff, int offs)
-{
- const char *hex_nibble = "1248";
- int i = 0;
-
- buff[i++] = '0';
- buff[i++] = 'x';
- buff[i++] = hex_nibble[offs % BITS_PER_NIBBLE];
- while (offs > 3) {
- buff[i++] = '0';
- offs -= BITS_PER_NIBBLE;
- }
- buff[i++] = 0; /* null */
-}
-
-/*
- * Returns the maximum number of CPUs that a mask can contain.
- */
-unsigned mask_capacity(void)
-{
- _odp_mask_t mask;
-
- _odp_mask_setall(&mask);
-
- return _odp_mask_count(&mask);
-}
-
-MASK_TESTFUNC(to_from_str)
-{
- _odp_mask_t mask;
- int32_t str_sz;
- unsigned int buf_sz; /* buf size for the 2 following bufs */
- char *buf_in;
- char *buf_out;
- unsigned int cpu;
- unsigned int i;
-
- /* makes sure the mask has room for at least 1 CPU...: */
- CU_ASSERT_FATAL(mask_capacity() > 0);
-
- /* allocate memory for the buffers containing the mask strings:
- 1 char per nibble, i.e. 1 char per 4 cpus +extra for "0x" and null:*/
- buf_sz = (mask_capacity() >> 2) + 20;
- buf_in = malloc(buf_sz);
- buf_out = malloc(buf_sz);
- CU_ASSERT_FATAL(buf_in && buf_out);
-
- /* test 1 CPU at a time for all possible cpu positions in the mask */
- for (cpu = 0; cpu < mask_capacity(); cpu++) {
- /* init buffer for overwrite check: */
- for (i = 0; i < buf_sz; i++)
- buf_out[i] = FILLING_PATTERN;
-
- /* generate a hex string with that cpu set: */
- bit_set_str(buf_in, cpu);
-
- /* generate mask: */
- _odp_mask_from_str(&mask, buf_in);
-
- /* reverse cpu mask computation to get string back: */
- str_sz = _odp_mask_to_str(&mask, buf_out,
- stringlen(buf_in) + 1);
-
- /* check that returned size matches original (with NULL): */
- CU_ASSERT(str_sz == (int32_t)stringlen(buf_in) + 1);
-
- /* check that returned string matches original (with NULL): */
- CU_ASSERT_NSTRING_EQUAL(buf_out, buf_in, stringlen(buf_in) + 1);
-
- /* check that no extra buffer writes occurred: */
- CU_ASSERT(buf_out[stringlen(buf_in) + 2] == FILLING_PATTERN);
- }
-
- /* re-init buffer for overwrite check: */
- for (i = 0; i < buf_sz; i++)
- buf_out[i] = FILLING_PATTERN;
-
- /* check for buffer overflow when too small buffer given: */
- _odp_mask_from_str(&mask, TEST_MASK_0);
- str_sz = _odp_mask_to_str(&mask, buf_out, stringlen(TEST_MASK_0));
-
- CU_ASSERT(str_sz == -1);
-
- for (i = 0; i < buf_sz; i++)
- CU_ASSERT(buf_out[i] == FILLING_PATTERN);
-
- /* check for handling of missing "0x" prefix: */
- _odp_mask_from_str(&mask, TEST_MASK_0_NO_PREFIX);
-
- str_sz = _odp_mask_to_str(&mask, buf_out,
- stringlen(TEST_MASK_0) + 1);
- CU_ASSERT(str_sz == (int32_t)stringlen(TEST_MASK_0) + 1);
-
- CU_ASSERT_NSTRING_EQUAL(buf_out, TEST_MASK_0,
- stringlen(TEST_MASK_0) + 1);
-
- free(buf_out);
- free(buf_in);
-}
-
-MASK_TESTFUNC(equal)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
- _odp_mask_t mask3;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask3, TEST_MASK_NONE);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
- CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- _odp_mask_from_str(&mask2, TEST_MASK_0_2);
- _odp_mask_from_str(&mask3, TEST_MASK_1_2);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
- CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3));
-
- if (mask_capacity() < 8)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2_4_6);
- _odp_mask_from_str(&mask2, TEST_MASK_0_2_4_6);
- _odp_mask_from_str(&mask3, TEST_MASK_1_2_4_6);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
- CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3));
-}
-
-MASK_TESTFUNC(zero)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_zero(&mask2);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-}
-
-MASK_TESTFUNC(set)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_set(&mask1, 0);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask2, TEST_MASK_0_3);
- _odp_mask_set(&mask1, 3);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-
- /* make sure that re-asserting a cpu has no impact: */
- _odp_mask_set(&mask1, 3);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-}
-
-MASK_TESTFUNC(clr)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_from_str(&mask2, TEST_MASK_NONE);
- _odp_mask_clr(&mask1, 0);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_clr(&mask1, 2);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-
- _odp_mask_from_str(&mask2, TEST_MASK_NONE);
- _odp_mask_clr(&mask1, 0);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-
- /* make sure that re-clearing a cpu has no impact: */
- _odp_mask_clr(&mask1, 0);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-}
-
-MASK_TESTFUNC(isset)
-{
- _odp_mask_t mask1;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- CU_ASSERT(_odp_mask_isset(&mask1, 0));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 0));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- CU_ASSERT(_odp_mask_isset(&mask1, 0));
- CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 1));
- CU_ASSERT(_odp_mask_isset(&mask1, 2));
- CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 3));
-}
-
-MASK_TESTFUNC(count)
-{
- _odp_mask_t mask1;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- CU_ASSERT(_odp_mask_count(&mask1) == 1);
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- CU_ASSERT(_odp_mask_count(&mask1) == 0);
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- CU_ASSERT(_odp_mask_count(&mask1) == 2);
-}
-
-MASK_TESTFUNC(and)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
- _odp_mask_t mask3;
- _odp_mask_t mask4;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_0);
- _odp_mask_and(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_NONE);
- _odp_mask_and(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_NONE);
- _odp_mask_from_str(&mask4, TEST_MASK_NONE);
- _odp_mask_and(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- _odp_mask_from_str(&mask2, TEST_MASK_1_2);
- _odp_mask_from_str(&mask4, TEST_MASK_2);
- _odp_mask_and(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-}
-
-MASK_TESTFUNC(or)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
- _odp_mask_t mask3;
- _odp_mask_t mask4;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_0);
- _odp_mask_or(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_0);
- _odp_mask_or(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_NONE);
- _odp_mask_from_str(&mask4, TEST_MASK_NONE);
- _odp_mask_or(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0_2);
- _odp_mask_from_str(&mask2, TEST_MASK_1);
- _odp_mask_from_str(&mask4, TEST_MASK_0_1_2);
- _odp_mask_or(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-}
-
-MASK_TESTFUNC(xor)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
- _odp_mask_t mask3;
- _odp_mask_t mask4;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_NONE);
- _odp_mask_xor(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_0);
- _odp_mask_from_str(&mask4, TEST_MASK_0);
- _odp_mask_xor(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- _odp_mask_from_str(&mask2, TEST_MASK_NONE);
- _odp_mask_from_str(&mask4, TEST_MASK_NONE);
- _odp_mask_xor(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_2);
- _odp_mask_from_str(&mask2, TEST_MASK_1_2);
- _odp_mask_from_str(&mask4, TEST_MASK_1);
- _odp_mask_xor(&mask3, &mask1, &mask2);
- CU_ASSERT(_odp_mask_equal(&mask3, &mask4));
-}
-
-MASK_TESTFUNC(copy)
-{
- _odp_mask_t mask1;
- _odp_mask_t mask2;
-
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- _odp_mask_copy(&mask2, &mask1);
- CU_ASSERT(_odp_mask_equal(&mask1, &mask2));
-}
-
-MASK_TESTFUNC(first)
-{
- _odp_mask_t mask1;
-
- /* check when there is no first */
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- CU_ASSERT(_odp_mask_first(&mask1) == -1);
-
- /* single CPU case: */
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- CU_ASSERT(_odp_mask_first(&mask1) == 0);
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_1_3);
- CU_ASSERT(_odp_mask_first(&mask1) == 1);
-}
-
-MASK_TESTFUNC(last)
-{
- _odp_mask_t mask1;
-
- /* check when there is no last: */
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- CU_ASSERT(_odp_mask_last(&mask1) == -1);
-
- /* single CPU case: */
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- CU_ASSERT(_odp_mask_last(&mask1) == 0);
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_1_3);
- CU_ASSERT(_odp_mask_last(&mask1) == 3);
-}
-
-MASK_TESTFUNC(next)
-{
- unsigned int i;
- int expected[] = {1, 3, 3, -1};
- _odp_mask_t mask1;
-
- /* case when the mask does not contain any CPU: */
- _odp_mask_from_str(&mask1, TEST_MASK_NONE);
- CU_ASSERT(_odp_mask_next(&mask1, -1) == -1);
-
- /* case when the mask just contain CPU 0: */
- _odp_mask_from_str(&mask1, TEST_MASK_0);
- CU_ASSERT(_odp_mask_next(&mask1, -1) == 0);
- CU_ASSERT(_odp_mask_next(&mask1, 0) == -1);
-
- if (mask_capacity() < 4)
- return;
-
- _odp_mask_from_str(&mask1, TEST_MASK_1_3);
-
- for (i = 0; i < sizeof(expected) / sizeof(int); i++)
- CU_ASSERT(_odp_mask_next(&mask1, i) == expected[i]);
-}
-
-MASK_TESTFUNC(setall)
-{
- int num;
- int max = mask_capacity();
- _odp_mask_t mask;
-
- _odp_mask_setall(&mask);
- num = _odp_mask_count(&mask);
-
- CU_ASSERT(num > 0);
- CU_ASSERT(num <= max);
-}
diff --git a/test/common_plat/common/mask_common.h b/test/common_plat/common/mask_common.h
deleted file mode 100644
index e7a38a7c7..000000000
--- a/test/common_plat/common/mask_common.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_MASK_COMMON_H_
-#define ODP_MASK_COMMON_H_
-
-/*
- * The same set of tests are used for testing both the odp_thrmask_ and
- * odp_cpumask_ APIs.
- *
- * To build the thrmask tests TEST_THRMASK must be defined.
- */
-#ifdef TEST_THRMASK
-typedef odp_thrmask_t _odp_mask_t;
-#define MASK_API_PREFIX(n) odp_thrmask_##n
-#define MASK_TESTFUNC(n) void thread_test_odp_thrmask_##n(void)
-#else
-typedef odp_cpumask_t _odp_mask_t;
-#define MASK_API_PREFIX(n) odp_cpumask_##n
-#define MASK_TESTFUNC(n) void cpumask_test_odp_cpumask_##n(void)
-#endif
-
-#define _odp_mask_from_str MASK_API_PREFIX(from_str)
-#define _odp_mask_to_str MASK_API_PREFIX(to_str)
-#define _odp_mask_equal MASK_API_PREFIX(equal)
-#define _odp_mask_zero MASK_API_PREFIX(zero)
-#define _odp_mask_set MASK_API_PREFIX(set)
-#define _odp_mask_clr MASK_API_PREFIX(clr)
-#define _odp_mask_isset MASK_API_PREFIX(isset)
-#define _odp_mask_count MASK_API_PREFIX(count)
-#define _odp_mask_and MASK_API_PREFIX(and)
-#define _odp_mask_or MASK_API_PREFIX(or)
-#define _odp_mask_xor MASK_API_PREFIX(xor)
-#define _odp_mask_copy MASK_API_PREFIX(copy)
-#define _odp_mask_first MASK_API_PREFIX(first)
-#define _odp_mask_next MASK_API_PREFIX(next)
-#define _odp_mask_last MASK_API_PREFIX(last)
-#define _odp_mask_setall MASK_API_PREFIX(setall)
-
-unsigned mask_capacity(void);
-
-MASK_TESTFUNC(to_from_str);
-MASK_TESTFUNC(equal);
-MASK_TESTFUNC(zero);
-MASK_TESTFUNC(set);
-MASK_TESTFUNC(clr);
-MASK_TESTFUNC(isset);
-MASK_TESTFUNC(count);
-MASK_TESTFUNC(and);
-MASK_TESTFUNC(or);
-MASK_TESTFUNC(xor);
-MASK_TESTFUNC(copy);
-MASK_TESTFUNC(first);
-MASK_TESTFUNC(last);
-MASK_TESTFUNC(next);
-MASK_TESTFUNC(setall);
-
-#endif
diff --git a/test/common_plat/common/odp_cunit_common.c b/test/common_plat/common/odp_cunit_common.c
deleted file mode 100644
index d3328af6c..000000000
--- a/test/common_plat/common/odp_cunit_common.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <string.h>
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include <odp/helper/odph_api.h>
-/* Globals */
-static odph_odpthread_t thread_tbl[MAX_WORKERS];
-static odp_instance_t instance;
-
-/*
- * global init/term functions which may be registered
- * defaults to functions performing odp init/term.
- */
-static int tests_global_init(odp_instance_t *inst);
-static int tests_global_term(odp_instance_t inst);
-static struct {
- int (*global_init_ptr)(odp_instance_t *inst);
- int (*global_term_ptr)(odp_instance_t inst);
-} global_init_term = {tests_global_init, tests_global_term};
-
-static odp_suiteinfo_t *global_testsuites;
-
-/** create test thread */
-int odp_cunit_thread_create(int func_ptr(void *), pthrd_arg *arg)
-{
- odp_cpumask_t cpumask;
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = func_ptr;
- thr_params.arg = arg;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- /* Create and init additional threads */
- odp_cpumask_default_worker(&cpumask, arg->numthrds);
-
- return odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-}
-
-/** exit from test thread */
-int odp_cunit_thread_exit(pthrd_arg *arg)
-{
- /* Wait for other threads to exit */
- if (odph_odpthreads_join(thread_tbl) != arg->numthrds) {
- fprintf(stderr,
- "error: odph_odpthreads_join() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-static int tests_global_init(odp_instance_t *inst)
-{
- if (0 != odp_init_global(inst, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return -1;
- }
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-static int tests_global_term(odp_instance_t inst)
-{
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_global(inst)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-/*
- * register tests_global_init and tests_global_term functions.
- * If some of these functions are not registered, the defaults functions
- * (tests_global_init() and tests_global_term()) defined above are used.
- * One should use these register functions when defining these hooks.
- * Note that passing NULL as function pointer is valid and will simply
- * prevent the default (odp init/term) to be done.
- */
-void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst))
-{
- global_init_term.global_init_ptr = func_init_ptr;
-}
-
-void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst))
-{
- global_init_term.global_term_ptr = func_term_ptr;
-}
-
-static odp_suiteinfo_t *cunit_get_suite_info(const char *suite_name)
-{
- odp_suiteinfo_t *sinfo;
-
- for (sinfo = global_testsuites; sinfo->pName; sinfo++)
- if (strcmp(sinfo->pName, suite_name) == 0)
- return sinfo;
-
- return NULL;
-}
-
-static odp_testinfo_t *cunit_get_test_info(odp_suiteinfo_t *sinfo,
- const char *test_name)
-{
- odp_testinfo_t *tinfo;
-
- for (tinfo = sinfo->pTests; tinfo->pName; tinfo++)
- if (strcmp(tinfo->pName, test_name) == 0)
- return tinfo;
-
- return NULL;
-}
-
-/* A wrapper for the suite's init function. This is done to allow for a
- * potential runtime check to determine whether each test in the suite
- * is active (enabled by using ODP_TEST_INFO_CONDITIONAL()). If present,
- * the conditional check is run after the suite's init function.
- */
-static int _cunit_suite_init(void)
-{
- int ret = 0;
- CU_pSuite cur_suite = CU_get_current_suite();
- odp_suiteinfo_t *sinfo;
- odp_testinfo_t *tinfo;
-
- /* find the suite currently being run */
- cur_suite = CU_get_current_suite();
- if (!cur_suite)
- return -1;
-
- sinfo = cunit_get_suite_info(cur_suite->pName);
- if (!sinfo)
- return -1;
-
- /* execute its init function */
- if (sinfo->pInitFunc) {
- ret = sinfo->pInitFunc();
- if (ret)
- return ret;
- }
-
- /* run any configured conditional checks and mark inactive tests */
- for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) {
- CU_pTest ptest;
- CU_ErrorCode err;
-
- if (!tinfo->check_active || tinfo->check_active())
- continue;
-
- /* test is inactive, mark it as such */
- ptest = CU_get_test_by_name(tinfo->pName, cur_suite);
- if (ptest)
- err = CU_set_test_active(ptest, CU_FALSE);
- else
- err = CUE_NOTEST;
-
- if (err != CUE_SUCCESS) {
- fprintf(stderr, "%s: failed to set test %s inactive\n",
- __func__, tinfo->pName);
- return -1;
- }
- }
-
- return ret;
-}
-
-/*
- * Register suites and tests with CUnit.
- *
- * Similar to CU_register_suites() but using locally defined wrapper
- * types.
- */
-static int cunit_register_suites(odp_suiteinfo_t testsuites[])
-{
- odp_suiteinfo_t *sinfo;
- odp_testinfo_t *tinfo;
- CU_pSuite suite;
- CU_pTest test;
-
- for (sinfo = testsuites; sinfo->pName; sinfo++) {
- suite = CU_add_suite(sinfo->pName,
- _cunit_suite_init, sinfo->pCleanupFunc);
- if (!suite)
- return CU_get_error();
-
- for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) {
- test = CU_add_test(suite, tinfo->pName,
- tinfo->pTestFunc);
- if (!test)
- return CU_get_error();
- }
- }
-
- return 0;
-}
-
-static int cunit_update_test(CU_pSuite suite,
- odp_suiteinfo_t *sinfo,
- odp_testinfo_t *updated_tinfo)
-{
- CU_pTest test = NULL;
- CU_ErrorCode err;
- odp_testinfo_t *tinfo;
- const char *test_name = updated_tinfo->pName;
-
- tinfo = cunit_get_test_info(sinfo, test_name);
- if (tinfo)
- test = CU_get_test(suite, test_name);
-
- if (!tinfo || !test) {
- fprintf(stderr, "%s: unable to find existing test named %s\n",
- __func__, test_name);
- return -1;
- }
-
- err = CU_set_test_func(test, updated_tinfo->pTestFunc);
- if (err != CUE_SUCCESS) {
- fprintf(stderr, "%s: failed to update test func for %s\n",
- __func__, test_name);
- return -1;
- }
-
- tinfo->check_active = updated_tinfo->check_active;
-
- return 0;
-}
-
-static int cunit_update_suite(odp_suiteinfo_t *updated_sinfo)
-{
- CU_pSuite suite = NULL;
- CU_ErrorCode err;
- odp_suiteinfo_t *sinfo;
- odp_testinfo_t *tinfo;
-
- /* find previously registered suite with matching name */
- sinfo = cunit_get_suite_info(updated_sinfo->pName);
-
- if (sinfo) {
- /* lookup the associated CUnit suite */
- suite = CU_get_suite_by_name(updated_sinfo->pName,
- CU_get_registry());
- }
-
- if (!sinfo || !suite) {
- fprintf(stderr, "%s: unable to find existing suite named %s\n",
- __func__, updated_sinfo->pName);
- return -1;
- }
-
- sinfo->pInitFunc = updated_sinfo->pInitFunc;
- sinfo->pCleanupFunc = updated_sinfo->pCleanupFunc;
-
- err = CU_set_suite_cleanupfunc(suite, updated_sinfo->pCleanupFunc);
- if (err != CUE_SUCCESS) {
- fprintf(stderr, "%s: failed to update cleanup func for %s\n",
- __func__, updated_sinfo->pName);
- return -1;
- }
-
- for (tinfo = updated_sinfo->pTests; tinfo->pName; tinfo++) {
- int ret;
-
- ret = cunit_update_test(suite, sinfo, tinfo);
- if (ret != 0)
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Run tests previously registered via odp_cunit_register()
- */
-int odp_cunit_run(void)
-{
- int ret;
-
- printf("\tODP API version: %s\n", odp_version_api_str());
- printf("\tODP implementation name: %s\n", odp_version_impl_name());
- printf("\tODP implementation version: %s\n", odp_version_impl_str());
-
- CU_basic_set_mode(CU_BRM_VERBOSE);
- CU_basic_run_tests();
-
- ret = CU_get_number_of_failure_records();
-
- CU_cleanup_registry();
-
- /* call test executable terminason hook, if any */
- if (global_init_term.global_term_ptr &&
- ((*global_init_term.global_term_ptr)(instance) != 0))
- return -1;
-
- return (ret) ? -1 : 0;
-}
-
-/*
- * Update suites/tests previously registered via odp_cunit_register().
- *
- * Note that this is intended for modifying the properties of already
- * registered suites/tests. New suites/tests can only be registered via
- * odp_cunit_register().
- */
-int odp_cunit_update(odp_suiteinfo_t testsuites[])
-{
- int ret = 0;
- odp_suiteinfo_t *sinfo;
-
- for (sinfo = testsuites; sinfo->pName && ret == 0; sinfo++)
- ret = cunit_update_suite(sinfo);
-
- return ret;
-}
-
-/*
- * Register test suites to be run via odp_cunit_run()
- */
-int odp_cunit_register(odp_suiteinfo_t testsuites[])
-{
- /* call test executable init hook, if any */
- if (global_init_term.global_init_ptr) {
- if ((*global_init_term.global_init_ptr)(&instance) == 0) {
- /* After ODP initialization, set main thread's
- * CPU affinity to the 1st available control CPU core
- */
- int cpu = 0;
- odp_cpumask_t cpuset;
-
- odp_cpumask_zero(&cpuset);
- if (odp_cpumask_default_control(&cpuset, 1) == 1) {
- cpu = odp_cpumask_first(&cpuset);
- odph_odpthread_setaffinity(cpu);
- }
- } else {
- /* ODP initialization failed */
- return -1;
- }
- }
-
- CU_set_error_action(CUEA_ABORT);
-
- CU_initialize_registry();
- global_testsuites = testsuites;
- cunit_register_suites(testsuites);
- CU_set_fail_on_inactive(CU_FALSE);
-
- return 0;
-}
-
-/*
- * Parse command line options to extract options affectiong cunit_common.
- * (hence also helpers options as cunit_common uses the helpers)
- * Options private to the test calling cunit_common are not parsed here.
- */
-int odp_cunit_parse_options(int argc, char *argv[])
-{
- return odph_parse_options(argc, argv, NULL, NULL);
-}
diff --git a/test/common_plat/common/odp_cunit_common.h b/test/common_plat/common/odp_cunit_common.h
deleted file mode 100644
index 486a5ec51..000000000
--- a/test/common_plat/common/odp_cunit_common.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP test application common headers
- */
-
-#ifndef ODP_CUNICT_COMMON_H
-#define ODP_CUNICT_COMMON_H
-
-#include <stdint.h>
-#include <inttypes.h>
-#include "CUnit/Basic.h"
-#include "CUnit/TestDB.h"
-#include <odp_api.h>
-
-#define MAX_WORKERS 32 /**< Maximum number of work threads */
-
-typedef int (*cunit_test_check_active)(void);
-
-typedef struct {
- const char *pName;
- CU_TestFunc pTestFunc;
- cunit_test_check_active check_active;
-} odp_testinfo_t;
-
-typedef struct {
- const char *pName;
- CU_InitializeFunc pInitFunc;
- CU_CleanupFunc pCleanupFunc;
- odp_testinfo_t *pTests;
-} odp_suiteinfo_t;
-
-static inline int odp_cunit_test_inactive(void) { return 0; }
-static inline void odp_cunit_test_missing(void) { }
-
-/* An active test case, with the test name matching the test function name */
-#define ODP_TEST_INFO(test_func) \
- {#test_func, test_func, NULL}
-
-/* A test case that is unconditionally inactive. Its name will be registered
- * with CUnit but it won't be executed and will be reported as inactive in
- * the result summary. */
-#define ODP_TEST_INFO_INACTIVE(test_func, args...) \
- {#test_func, odp_cunit_test_missing, odp_cunit_test_inactive}
-
-#define ODP_TEST_INACTIVE 0
-#define ODP_TEST_ACTIVE 1
-
-/* A test case that may be marked as inactive at runtime based on the
- * return value of the cond_func function. A return value of ODP_TEST_INACTIVE
- * means inactive, ODP_TEST_ACTIVE means active. */
-#define ODP_TEST_INFO_CONDITIONAL(test_func, cond_func) \
- {#test_func, test_func, cond_func}
-
-#define ODP_TEST_INFO_NULL {NULL, NULL, NULL}
-#define ODP_SUITE_INFO_NULL {NULL, NULL, NULL, NULL}
-
-typedef struct {
- uint32_t foo;
- uint32_t bar;
-} test_shared_data_t;
-
-/**
- * Thread argument
- */
-typedef struct {
- int testcase; /**< specifies which set of API's to exercise */
- int numthrds; /**< no of pthreads to create */
-} pthrd_arg;
-
-/* parse parameters that affect the behaviour of odp_cunit_common */
-int odp_cunit_parse_options(int argc, char *argv[]);
-/* register suites to be run via odp_cunit_run() */
-int odp_cunit_register(odp_suiteinfo_t testsuites[]);
-/* update tests previously registered via odp_cunit_register() */
-int odp_cunit_update(odp_suiteinfo_t testsuites[]);
-/* the function, called by module main(), to run the testsuites: */
-int odp_cunit_run(void);
-
-/** create thread for start_routine function (which returns 0 on success) */
-int odp_cunit_thread_create(int func_ptr(void *), pthrd_arg *arg);
-int odp_cunit_thread_exit(pthrd_arg *);
-
-/**
- * Global tests initialization/termination.
- *
- * Initialize global resources needed by the test executable. Default
- * definition does ODP init / term (both global and local).
- * Test executables can override it by calling one of the register function
- * below.
- * The functions are called at the very beginning and very end of the test
- * execution. Passing NULL to odp_cunit_register_global_init() and/or
- * odp_cunit_register_global_term() is legal and will simply prevent the
- * default (ODP init/term) to be done.
- */
-void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst));
-
-void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst));
-
-#endif /* ODP_CUNICT_COMMON_H */
diff --git a/test/common_plat/m4/configure.m4 b/test/common_plat/m4/configure.m4
deleted file mode 100644
index be878bd7d..000000000
--- a/test/common_plat/m4/configure.m4
+++ /dev/null
@@ -1,33 +0,0 @@
-m4_include([test/common_plat/m4/miscellaneous.m4])
-m4_include([test/common_plat/m4/performance.m4])
-m4_include([test/common_plat/m4/validation.m4])
-
-AC_CONFIG_FILES([test/common_plat/Makefile
- test/common_plat/common/Makefile
- test/common_plat/miscellaneous/Makefile
- test/common_plat/performance/Makefile
- test/common_plat/validation/Makefile
- test/common_plat/validation/api/atomic/Makefile
- test/common_plat/validation/api/barrier/Makefile
- test/common_plat/validation/api/buffer/Makefile
- test/common_plat/validation/api/classification/Makefile
- test/common_plat/validation/api/cpumask/Makefile
- test/common_plat/validation/api/crypto/Makefile
- test/common_plat/validation/api/errno/Makefile
- test/common_plat/validation/api/hash/Makefile
- test/common_plat/validation/api/init/Makefile
- test/common_plat/validation/api/lock/Makefile
- test/common_plat/validation/api/Makefile
- test/common_plat/validation/api/packet/Makefile
- test/common_plat/validation/api/pktio/Makefile
- test/common_plat/validation/api/pool/Makefile
- test/common_plat/validation/api/queue/Makefile
- test/common_plat/validation/api/random/Makefile
- test/common_plat/validation/api/scheduler/Makefile
- test/common_plat/validation/api/shmem/Makefile
- test/common_plat/validation/api/std_clib/Makefile
- test/common_plat/validation/api/system/Makefile
- test/common_plat/validation/api/thread/Makefile
- test/common_plat/validation/api/time/Makefile
- test/common_plat/validation/api/timer/Makefile
- test/common_plat/validation/api/traffic_mngr/Makefile])
diff --git a/test/common_plat/m4/miscellaneous.m4 b/test/common_plat/m4/miscellaneous.m4
deleted file mode 100644
index cc881edb7..000000000
--- a/test/common_plat/m4/miscellaneous.m4
+++ /dev/null
@@ -1,9 +0,0 @@
-##########################################################################
-# Enable/disable test-cpp
-##########################################################################
-test_cpp=no
-AC_ARG_ENABLE([test-cpp],
- [ --enable-test-cpp run basic test aginast cpp],
- [if test "x$enableval" = "xyes"; then
- test_cpp=yes
- fi])
diff --git a/test/common_plat/m4/performance.m4 b/test/common_plat/m4/performance.m4
deleted file mode 100644
index 1e2000d97..000000000
--- a/test/common_plat/m4/performance.m4
+++ /dev/null
@@ -1,9 +0,0 @@
-##########################################################################
-# Enable/disable test-perf
-##########################################################################
-test_perf=no
-AC_ARG_ENABLE([test-perf],
- [ --enable-test-perf run test in test/performance],
- [if test "x$enableval" = "xyes"; then
- test_perf=yes
- fi])
diff --git a/test/common_plat/m4/validation.m4 b/test/common_plat/m4/validation.m4
deleted file mode 100644
index 67edac7ae..000000000
--- a/test/common_plat/m4/validation.m4
+++ /dev/null
@@ -1,61 +0,0 @@
-##########################################################################
-# Enable/disable Unit tests
-##########################################################################
-cunit_support=no
-test_vald=no
-AC_ARG_ENABLE([test_vald],
- [ --enable-test-vald run test in test/validation],
- [if test x$enableval = xyes; then
- test_vald=yes
- cunit_support=yes
- fi])
-
-##########################################################################
-# Enable/disable Unit tests
-##########################################################################
-AC_ARG_ENABLE([cunit_support],
- [ --enable-cunit-support include cunit infrastructure],
- [if test x$enableval = xyes; then
- cunit_support=yes
- fi])
-
-##########################################################################
-# Set optional CUnit path
-##########################################################################
-AC_ARG_WITH([cunit-path],
-AC_HELP_STRING([--with-cunit-path=DIR path to CUnit libs and headers],
- [(or in the default path if not specified).]),
- [CUNIT_PATH=$withval
- CUNIT_CPPFLAGS="-I$CUNIT_PATH/include"
- CUNIT_LIBS="-L$CUNIT_PATH/lib"
- cunit_support=yes],[])
-
-##########################################################################
-# Save and set temporary compilation flags
-##########################################################################
-OLD_LIBS=$LIBS
-OLD_CPPFLAGS=$CPPFLAGS
-LIBS="$CUNIT_LIBS $LIBS"
-CPPFLAGS="$CUNIT_CPPFLAGS $CPPFLAGS"
-
-##########################################################################
-# Check for CUnit availability
-##########################################################################
-if test x$cunit_support = xyes
-then
- AC_CHECK_LIB([cunit],[CU_get_error], [CUNIT_LIBS="$CUNIT_LIBS -lcunit"],
- [AC_MSG_ERROR([CUnit libraries required])])
- AC_CHECK_HEADERS([CUnit/Basic.h], [],
- [AC_MSG_FAILURE(["can't find cunit headers"])])
-else
- cunit_support=no
-fi
-
-AC_SUBST([CUNIT_CPPFLAGS])
-AC_SUBST([CUNIT_LIBS])
-
-##########################################################################
-# Restore old saved variables
-##########################################################################
-LIBS=$OLD_LIBS
-CPPFLAGS=$OLD_CPPFLAGS
diff --git a/test/common_plat/miscellaneous/.gitignore b/test/common_plat/miscellaneous/.gitignore
deleted file mode 100644
index 6e555c58e..000000000
--- a/test/common_plat/miscellaneous/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-odp_api_from_cpp
-*.trs
-*.log
diff --git a/test/common_plat/miscellaneous/Makefile.am b/test/common_plat/miscellaneous/Makefile.am
deleted file mode 100644
index 7d8cf3531..000000000
--- a/test/common_plat/miscellaneous/Makefile.am
+++ /dev/null
@@ -1,12 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-
-if test_cpp
-bin_PROGRAMS = odp_api_from_cpp$(EXEEXT)
-TESTS = odp_api_from_cpp$(EXEEXT)
-endif
-
-odp_api_from_cpp_CXXFLAGS = $(AM_CXXFLAGS)
-
-odp_api_from_cpp_LDFLAGS = $(AM_LDFLAGS) -static
-
-dist_odp_api_from_cpp_SOURCES = odp_api_from_cpp.cpp
diff --git a/test/common_plat/miscellaneous/odp_api_from_cpp.cpp b/test/common_plat/miscellaneous/odp_api_from_cpp.cpp
deleted file mode 100644
index 4578ae4be..000000000
--- a/test/common_plat/miscellaneous/odp_api_from_cpp.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <cstdio>
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-
-int main(int argc ODP_UNUSED, const char *argv[] ODP_UNUSED)
-{
- printf("\tODP API version: %s\n", odp_version_api_str());
- printf("\tODP implementation version: %s\n", odp_version_impl_str());
-
- return 0;
-}
diff --git a/test/common_plat/performance/.gitignore b/test/common_plat/performance/.gitignore
deleted file mode 100644
index 72035e002..000000000
--- a/test/common_plat/performance/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-*.log
-*.trs
-odp_atomic
-odp_bench_packet
-odp_crypto
-odp_l2fwd
-odp_pktio_ordered
-odp_pktio_perf
-odp_sched_latency
-odp_scheduling
diff --git a/test/common_plat/performance/Makefile.am b/test/common_plat/performance/Makefile.am
deleted file mode 100644
index 3299f03f8..000000000
--- a/test/common_plat/performance/Makefile.am
+++ /dev/null
@@ -1,55 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-
-TESTS_ENVIRONMENT += TEST_DIR=${builddir}
-
-EXECUTABLES = odp_bench_packet$(EXEEXT) \
- odp_crypto$(EXEEXT) \
- odp_pktio_perf$(EXEEXT)
-
-COMPILE_ONLY = odp_l2fwd$(EXEEXT) \
- odp_pktio_ordered$(EXEEXT) \
- odp_sched_latency$(EXEEXT) \
- odp_scheduling$(EXEEXT)
-
-TESTSCRIPTS = odp_l2fwd_run.sh \
- odp_pktio_ordered_run.sh \
- odp_sched_latency_run.sh \
- odp_scheduling_run.sh
-
-TEST_EXTENSIONS = .sh
-
-if test_perf
-TESTS = $(EXECUTABLES) $(TESTSCRIPTS)
-endif
-
-bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
-
-odp_bench_packet_LDFLAGS = $(AM_LDFLAGS) -static
-odp_bench_packet_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_crypto_LDFLAGS = $(AM_LDFLAGS) -static
-odp_crypto_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_pktio_perf_LDFLAGS = $(AM_LDFLAGS) -static
-odp_pktio_perf_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_l2fwd_LDFLAGS = $(AM_LDFLAGS) -static
-odp_l2fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_pktio_ordered_LDFLAGS = $(AM_LDFLAGS) -static
-odp_pktio_ordered_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_sched_latency_LDFLAGS = $(AM_LDFLAGS) -static
-odp_sched_latency_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static
-odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
-
-noinst_HEADERS = \
- $(top_srcdir)/test/test_debug.h \
- dummy_crc.h
-
-dist_odp_bench_packet_SOURCES = odp_bench_packet.c
-dist_odp_crypto_SOURCES = odp_crypto.c
-dist_odp_pktio_ordered_SOURCES = odp_pktio_ordered.c
-dist_odp_sched_latency_SOURCES = odp_sched_latency.c
-dist_odp_scheduling_SOURCES = odp_scheduling.c
-dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c
-
-EXTRA_DIST = $(TESTSCRIPTS)
-
-dist_check_SCRIPTS = udp64.pcap
diff --git a/test/common_plat/performance/dummy_crc.h b/test/common_plat/performance/dummy_crc.h
deleted file mode 100644
index 38da44455..000000000
--- a/test/common_plat/performance/dummy_crc.h
+++ /dev/null
@@ -1,493 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file
- *
- * This file is meant only for generating dummy processing work.
- */
-
-#include <stdint.h>
-#include <stddef.h>
-
-static const uint32_t dummy_crc32c_tables[8][256] = {{
- 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C,
- 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
- 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C,
- 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
- 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC,
- 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
- 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512,
- 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
- 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD,
- 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
- 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF,
- 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
- 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F,
- 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
- 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F,
- 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
- 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E,
- 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
- 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E,
- 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
- 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE,
- 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
- 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4,
- 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
- 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B,
- 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
- 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5,
- 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
- 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975,
- 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
- 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905,
- 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
- 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8,
- 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
- 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8,
- 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
- 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78,
- 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
- 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6,
- 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
- 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69,
- 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
- 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
-},
-{
- 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB,
- 0x69CF5132, 0x7A6DC945, 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21,
- 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, 0x3FC5F181, 0x2C6769F6,
- 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,
- 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92,
- 0xCB1E630B, 0xD8BCFB7C, 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B,
- 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, 0xE29F20BA, 0xF13DB8CD,
- 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF,
- 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28,
- 0x298143B1, 0x3A23DBC6, 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2,
- 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, 0xFF17C604, 0xECB55E73,
- 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41,
- 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17,
- 0x0BCC548E, 0x186ECCF9, 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C,
- 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, 0x5DC6F43D, 0x4E646C4A,
- 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,
- 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD,
- 0xE9537434, 0xFAF1EC43, 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27,
- 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, 0xBF59D487, 0xACFB4CF0,
- 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2,
- 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94,
- 0x4B82460D, 0x5820DE7A, 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260,
- 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, 0x66D73941, 0x7575A136,
- 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004,
- 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3,
- 0xADC95A4A, 0xBE6BC23D, 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059,
- 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, 0x844819FB, 0x97EA818C,
- 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,
- 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8,
- 0x70938B71, 0x63311306, 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3,
- 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, 0x26992BC2, 0x353BB3B5,
- 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287,
- 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556,
- 0x6D1B6DCF, 0x7EB9F5B8, 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC,
- 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, 0x3B11CD7C, 0x28B3550B,
- 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439,
- 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F,
- 0xCFCA5FF6, 0xDC68C781, 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766,
- 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, 0xE64B1C47, 0xF5E98430,
- 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,
- 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5,
- 0x2D557F4C, 0x3EF7E73B, 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F,
- 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483,
-},
-{
- 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664,
- 0xD1B1F617, 0x74F06469, 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6,
- 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, 0x70A27D8A, 0xD5E3EFF4,
- 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,
- 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B,
- 0x9942B558, 0x3C032726, 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67,
- 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, 0xD915C5D1, 0x7C5457AF,
- 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8,
- 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA,
- 0x40577089, 0xE516E2F7, 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828,
- 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, 0xC76580D9, 0x622412A7,
- 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0,
- 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878,
- 0x2E85480B, 0x8BC4DA75, 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20,
- 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, 0x8F96C396, 0x2AD751E8,
- 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,
- 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9,
- 0xF7908DDA, 0x52D11FA4, 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B,
- 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, 0x56830647, 0xF3C29439,
- 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E,
- 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6,
- 0xBF63CE95, 0x1A225CEB, 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730,
- 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, 0xB3764986, 0x1637DBF8,
- 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF,
- 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD,
- 0x2A34FCDE, 0x8F756EA0, 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F,
- 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, 0x6A638C57, 0xCF221E29,
- 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,
- 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6,
- 0x83834485, 0x26C2D6FB, 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE,
- 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, 0x2290CF18, 0x87D15D66,
- 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71,
- 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE,
- 0x9DF3018D, 0x38B293F3, 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C,
- 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, 0x3CE08A10, 0x99A1186E,
- 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79,
- 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1,
- 0xD50042C2, 0x7041D0BC, 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD,
- 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, 0x9557324B, 0x3016A035,
- 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,
- 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760,
- 0x0C158713, 0xA954156D, 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2,
- 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8,
-},
-{
- 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B,
- 0xC4451272, 0x1900B8CA, 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF,
- 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, 0xE964B13D, 0x34211B85,
- 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,
- 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990,
- 0xDB65C0A9, 0x06206A11, 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2,
- 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, 0x2161776D, 0xFC24DDD5,
- 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7,
- 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD,
- 0xFA04B7C4, 0x27411D7C, 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69,
- 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, 0xABA65FE7, 0x76E3F55F,
- 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D,
- 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A,
- 0x99A72E73, 0x44E284CB, 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3,
- 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, 0xB4868D3C, 0x69C32784,
- 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,
- 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027,
- 0xB8C6591E, 0x6583F3A6, 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3,
- 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, 0x95E7FA51, 0x48A250E9,
- 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B,
- 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC,
- 0xA7E68BC5, 0x7AA3217D, 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006,
- 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, 0xA4E4AAD9, 0x79A10061,
- 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213,
- 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349,
- 0x7F816A70, 0xA2C4C0C8, 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD,
- 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, 0x8585DDB4, 0x58C0770C,
- 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,
- 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519,
- 0xB784AC20, 0x6AC10698, 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0,
- 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, 0x9AA50F6F, 0x47E0A5D7,
- 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5,
- 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93,
- 0x3D4384AA, 0xE0062E12, 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07,
- 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, 0x106227E5, 0xCD278D5D,
- 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F,
- 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48,
- 0x22635671, 0xFF26FCC9, 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A,
- 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, 0xD867E1B5, 0x05224B0D,
- 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,
- 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825,
- 0x0302211C, 0xDE478BA4, 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1,
- 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842,
-},
-{
- 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C,
- 0x906761E8, 0xA8760E44, 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65,
- 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, 0x8F2261D3, 0xB7330E7F,
- 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,
- 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E,
- 0xDA220BAA, 0xE2336406, 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3,
- 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, 0xDECFBEC6, 0xE6DED16A,
- 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082,
- 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598,
- 0x04EDB56C, 0x3CFCDAC0, 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1,
- 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, 0x37516AAE, 0x0F400502,
- 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA,
- 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023,
- 0x625100D7, 0x5A406F7B, 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89,
- 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, 0x7D1400EC, 0x45056F40,
- 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,
- 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5,
- 0xBC9EBE11, 0x848FD1BD, 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C,
- 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, 0xA3DBBE2A, 0x9BCAD186,
- 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E,
- 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7,
- 0xF6DBD453, 0xCECABBFF, 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8,
- 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, 0xABC5DECD, 0x93D4B161,
- 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089,
- 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593,
- 0x71E7D567, 0x49F6BACB, 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA,
- 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, 0x750A600B, 0x4D1B0FA7,
- 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,
- 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86,
- 0x200A0A72, 0x181B65DE, 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C,
- 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, 0x3F4F0A49, 0x075E65E5,
- 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D,
- 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE,
- 0xC994DE1A, 0xF185B1B6, 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497,
- 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, 0xD6D1DE21, 0xEEC0B18D,
- 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065,
- 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC,
- 0x83D1B458, 0xBBC0DBF4, 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51,
- 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, 0x873C0134, 0xBF2D6E98,
- 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,
- 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A,
- 0x5D1E0A9E, 0x650F6532, 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013,
- 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3,
-},
-{
- 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E,
- 0x697997B4, 0x8649FCAD, 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5,
- 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, 0xC00C303E, 0x2F3C5B27,
- 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,
- 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F,
- 0xC973BF95, 0x2643D48C, 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57,
- 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, 0xE5F20E92, 0x0AC2658B,
- 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F,
- 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD,
- 0x2C81B107, 0xC3B1DA1E, 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576,
- 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, 0x0E045BEB, 0xE13430F2,
- 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746,
- 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A,
- 0x077BD440, 0xE84BBF59, 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F,
- 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, 0xAE0E73CA, 0x413E18D3,
- 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,
- 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108,
- 0xE289DAD2, 0x0DB9B1CB, 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3,
- 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, 0x4BFC7D58, 0xA4CC1641,
- 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5,
- 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929,
- 0x4283F2F3, 0xADB399EA, 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C,
- 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, 0x7C0EAFC9, 0x933EC4D0,
- 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364,
- 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86,
- 0xB57D105C, 0x5A4D7B45, 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D,
- 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, 0x99FCA15B, 0x76CCCA42,
- 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,
- 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A,
- 0x90832EF0, 0x7FB345E9, 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF,
- 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, 0x39F6897A, 0xD6C6E263,
- 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7,
- 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053,
- 0x7B757B89, 0x94451090, 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8,
- 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, 0xD200DC03, 0x3D30B71A,
- 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE,
- 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872,
- 0xDB7F53A8, 0x344F38B1, 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A,
- 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, 0xF7FEE2AF, 0x18CE89B6,
- 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,
- 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0,
- 0x3E8D5D3A, 0xD1BD3623, 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B,
- 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C,
-},
-{
- 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919,
- 0x75E69C41, 0x1DE5B089, 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B,
- 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, 0x9C5BFAA6, 0xF458D66E,
- 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,
- 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC,
- 0xA7909BB4, 0xCF93B77C, 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5,
- 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, 0x73767EEE, 0x1B755226,
- 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67,
- 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002,
- 0xD4E6E55A, 0xBCE5C992, 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110,
- 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, 0x7AB7077A, 0x12B42BB2,
- 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3,
- 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330,
- 0x417C6668, 0x297F4AA0, 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884,
- 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, 0xA8C1008F, 0xC0C22C47,
- 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,
- 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE,
- 0x320A1886, 0x5A09344E, 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC,
- 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, 0xDBB77E61, 0xB3B452A9,
- 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8,
- 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B,
- 0xE07C1F73, 0x887F33BB, 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC,
- 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, 0xBB43F3A7, 0xD340DF6F,
- 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E,
- 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B,
- 0x1CD36813, 0x74D044DB, 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59,
- 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, 0xC8358D49, 0xA036A181,
- 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,
- 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903,
- 0xF3FEEC5B, 0x9BFDC093, 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7,
- 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, 0x1A438ABC, 0x7240A674,
- 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35,
- 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097,
- 0xFA3F95CF, 0x923CB907, 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185,
- 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, 0x1382F328, 0x7B81DFE0,
- 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1,
- 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762,
- 0x2849923A, 0x404ABEF2, 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B,
- 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, 0xFCAF7760, 0x94AC5BA8,
- 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,
- 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C,
- 0x5B3FECD4, 0x333CC01C, 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E,
- 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F,
-},
-{
- 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A,
- 0xB3657823, 0xFA590504, 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3,
- 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, 0x847609B4, 0xCD4A7493,
- 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,
- 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224,
- 0x7528754D, 0x3C14086A, 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0,
- 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, 0x4F3B6143, 0x06071C64,
- 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447,
- 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367,
- 0x3A13140E, 0x732F6929, 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E,
- 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, 0x1A00CB32, 0x533CB615,
- 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36,
- 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2,
- 0xEB5EB7CB, 0xA262CAEC, 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF,
- 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, 0xDC4DC65C, 0x9571BB7B,
- 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,
- 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1,
- 0xA465D688, 0xED59ABAF, 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18,
- 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, 0x9376A71F, 0xDA4ADA38,
- 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B,
- 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F,
- 0x6228DBE6, 0x2B14A6C1, 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D,
- 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, 0x763A92BE, 0x3F06EF99,
- 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA,
- 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A,
- 0x0312E7F3, 0x4A2E9AD4, 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63,
- 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, 0x3901F3FD, 0x703D8EDA,
- 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,
- 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D,
- 0xC85F8F04, 0x8163F223, 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20,
- 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, 0xFF4CFE93, 0xB67083B4,
- 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97,
- 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C,
- 0x9D642575, 0xD4585852, 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5,
- 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, 0xAA7754E2, 0xE34B29C5,
- 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6,
- 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72,
- 0x5B29281B, 0x1215553C, 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6,
- 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, 0x613A3C15, 0x28064132,
- 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,
- 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31,
- 0x14124958, 0x5D2E347F, 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8,
- 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5,
-} };
-
-#define DUMMY_CRC32_UPD(crc, n) \
- (dummy_crc32c_tables[(n)][(crc) & 0xff] ^ \
- dummy_crc32c_tables[(n) - 1][((crc) >> 8) & 0xff])
-
-static inline uint32_t dummy_crc32c_u32(uint32_t data, uint32_t init_val)
-{
- uint32_t crc, term1, term2;
-
- crc = init_val;
- crc ^= data;
-
- term1 = DUMMY_CRC32_UPD(crc, 3);
- term2 = crc >> 16;
- crc = term1 ^ DUMMY_CRC32_UPD(term2, 1);
-
- return crc;
-}
-
-static inline uint32_t dummy_crc32c_u64(uint64_t data, uint32_t init_val)
-{
- union {
- uint64_t u64;
- uint32_t u32[2];
- } d;
- d.u64 = data;
-
- uint32_t crc, term1, term2;
-
- crc = init_val;
- crc ^= d.u32[0];
-
- term1 = DUMMY_CRC32_UPD(crc, 7);
- term2 = crc >> 16;
- crc = term1 ^ DUMMY_CRC32_UPD(term2, 5);
- term1 = DUMMY_CRC32_UPD(d.u32[1], 3);
- term2 = d.u32[1] >> 16;
- crc ^= term1 ^ DUMMY_CRC32_UPD(term2, 1);
-
- return crc;
-}
-
-static inline uint32_t dummy_hash_crc32c(const void *data, uint32_t data_len,
- uint32_t init_val)
-{
- size_t i;
- uint64_t temp = 0;
- uintptr_t pd = (uintptr_t)data;
-
- for (i = 0; i < data_len / 8; i++) {
- init_val = dummy_crc32c_u64(*(const uint64_t *)pd, init_val);
- pd += 8;
- }
-
- switch (7 - (data_len & 0x07)) {
- case 0:
- temp |= (uint64_t)*((const uint8_t *)pd + 6) << 48;
- /* Fallthrough */
- case 1:
- temp |= (uint64_t)*((const uint8_t *)pd + 5) << 40;
- /* Fallthrough */
- case 2:
- temp |= (uint64_t)*((const uint8_t *)pd + 4) << 32;
- temp |= *(const uint32_t *)pd;
- init_val = dummy_crc32c_u64(temp, init_val);
- break;
- case 3:
- init_val = dummy_crc32c_u32(*(const uint32_t *)pd, init_val);
- break;
- case 4:
- temp |= *((const uint8_t *)pd + 2) << 16;
- /* Fallthrough */
- case 5:
- temp |= *((const uint8_t *)pd + 1) << 8;
- /* Fallthrough */
- case 6:
- temp |= *(const uint8_t *)pd;
- init_val = dummy_crc32c_u32(temp, init_val);
- /* Fallthrough */
- default:
- break;
- }
-
- return init_val;
-}
diff --git a/test/common_plat/performance/odp_bench_packet.c b/test/common_plat/performance/odp_bench_packet.c
deleted file mode 100644
index c4cd6139b..000000000
--- a/test/common_plat/performance/odp_bench_packet.c
+++ /dev/null
@@ -1,1697 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_bench_packet.c Microbenchmarks for packet functions
- */
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <signal.h>
-
-#include <test_debug.h>
-
-#include <odp_api.h>
-#include <odp/helper/threads.h>
-#include <odp/helper/eth.h>
-#include <odp/helper/ip.h>
-#include <odp/helper/udp.h>
-
-/** Minimum number of packet data bytes in the first segment */
-#define PKT_POOL_SEG_LEN 128
-
-/** Packet user area size in bytes */
-#define PKT_POOL_UAREA_SIZE 8
-
-/** Minimum test packet size */
-#define TEST_MIN_PKT_SIZE 64
-
-/** Maximum test packet size */
-#define TEST_MAX_PKT_SIZE 2048
-
-/** Number of test runs per individual benchmark */
-#define TEST_REPEAT_COUNT 1000
-
-/** Number of times to run tests for each packet size */
-#define TEST_SIZE_RUN_COUNT 10
-
-/** Maximum burst size for *_multi operations */
-#define TEST_MAX_BURST 64
-
-/** Offset of the contiguous area */
-#define TEST_ALIGN_OFFSET 16
-
-/** Length of the contiguous area */
-#define TEST_ALIGN_LEN 32
-
-/** Minimum byte alignment of contiguous area */
-#define TEST_ALIGN 32
-
-/** Test packet offsets */
-#define TEST_L2_OFFSET 0
-#define TEST_L3_OFFSET (TEST_MIN_PKT_SIZE / 4)
-#define TEST_L4_OFFSET (TEST_MIN_PKT_SIZE / 2)
-
-/** Default burst size for *_multi operations */
-#define TEST_DEF_BURST 8
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-
-#define BENCH_INFO(run, init, term, name) \
- {#run, run, init, term, name}
-
-ODP_STATIC_ASSERT((TEST_ALIGN_OFFSET + TEST_ALIGN_LEN) <= TEST_MIN_PKT_SIZE,
- "Invalid_alignment");
-
-/** Warm up round packet size */
-#define WARM_UP TEST_MIN_PKT_SIZE
-
-/** Test packet sizes */
-const uint32_t test_packet_len[] = {WARM_UP, TEST_MIN_PKT_SIZE, 128, 256, 512,
- 1024, 1518, TEST_MAX_PKT_SIZE};
-
-/**
- * Parsed command line arguments
- */
-typedef struct {
- int bench_idx; /** Benchmark index to run indefinitely */
- int burst_size; /** Burst size for *_multi operations */
-} appl_args_t;
-
-/**
- * Initialize benchmark resources
- */
-typedef void (*bench_init_fn_t)(void);
-
-/**
- * Run benchmark
- *
- * @retval >0 on success
- * */
-typedef int (*bench_run_fn_t)(void);
-
-/**
- * Release benchmark resources
- */
-typedef void (*bench_term_fn_t)(void);
-
-/**
- * Benchmark data
- */
-typedef struct {
- const char *name;
- bench_run_fn_t run;
- bench_init_fn_t init;
- bench_term_fn_t term;
- const char *desc;
-} bench_info_t;
-
-/**
- * Grouping of all global data
- */
-typedef struct {
- /** Application (parsed) arguments */
- appl_args_t appl;
- /** Packet pool */
- odp_pool_t pool;
- /** Benchmark functions */
- bench_info_t *bench;
- /** Number of benchmark functions */
- int num_bench;
- struct {
- /** Test packet length */
- uint32_t len;
- /** Minimum test packet headroom */
- uint32_t headroom;
- /** Minimum test packet tailroom */
- uint32_t tailroom;
- /** Minimum test packet segment length */
- uint32_t seg_len;
- } pkt;
- /** Array for storing test packets */
- odp_packet_t pkt_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
- /** Array for storing test packets */
- odp_packet_t pkt2_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test event */
- odp_event_t event_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test pointers */
- void *ptr_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test segments */
- odp_packet_seg_t seg_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test outputs */
- uint32_t output_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test pool handles */
- odp_pool_t pool_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test pktio handles */
- odp_pktio_t pktio_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test timestamps */
- odp_time_t ts_tbl[TEST_REPEAT_COUNT];
- /** Array for storing test data */
- uint8_t data_tbl[TEST_REPEAT_COUNT][TEST_MAX_PKT_SIZE];
- /** Benchmark run failed */
- uint8_t bench_failed;
-} args_t;
-
-/** Global pointer to args */
-static args_t *gbl_args;
-/** Global barrier to synchronize main and worker */
-static odp_barrier_t barrier;
-/** Break worker loop if set to 1 */
-static int exit_thread;
-
-static void sig_handler(int signo ODP_UNUSED)
-{
- exit_thread = 1;
-}
-
-/**
- * Run given benchmark indefinitely
- */
-static void run_indef(args_t *args, int idx)
-{
- const char *desc;
-
- desc = args->bench[idx].desc != NULL ?
- args->bench[idx].desc : args->bench[idx].name;
-
- printf("Running %s() indefinitely\n", desc);
-
- while (!exit_thread) {
- int ret;
-
- if (args->bench[idx].init != NULL)
- args->bench[idx].init();
-
- ret = args->bench[idx].run();
-
- if (args->bench[idx].term != NULL)
- args->bench[idx].term();
-
- if (!ret)
- LOG_ABORT("Benchmark %s failed\n", desc);
- }
-}
-
-/**
- * Master function for running the microbenchmarks
- */
-static int run_benchmarks(void *arg)
-{
- int i, j, k;
- args_t *args = arg;
- int num_sizes = sizeof(test_packet_len) / sizeof(test_packet_len[0]);
- double results[gbl_args->num_bench][num_sizes];
-
- memset(results, 0, sizeof(results));
-
- printf("\nRunning benchmarks (cycles per call)\n"
- "------------------------------------\n");
-
- for (i = 0; i < num_sizes; i++) {
- uint64_t tot_cycles = 0;
-
- printf("\nPacket length: %6d bytes\n"
- "---------------------------\n", test_packet_len[i]);
-
- gbl_args->pkt.len = test_packet_len[i];
-
- for (j = 0, k = 1; j < gbl_args->num_bench; k++) {
- int ret;
- uint64_t c1, c2;
- const char *desc;
-
- if (args->appl.bench_idx &&
- (j + 1) != args->appl.bench_idx) {
- j++;
- continue;
- } else if (args->appl.bench_idx &&
- (j + 1) == args->appl.bench_idx) {
- run_indef(args, j);
- return 0;
- }
-
- desc = args->bench[j].desc != NULL ?
- args->bench[j].desc :
- args->bench[j].name;
-
- if (args->bench[j].init != NULL)
- args->bench[j].init();
-
- c1 = odp_cpu_cycles();
- ret = args->bench[j].run();
- c2 = odp_cpu_cycles();
-
- if (args->bench[j].term != NULL)
- args->bench[j].term();
-
- if (!ret) {
- LOG_ERR("Benchmark %s failed\n", desc);
- args->bench_failed = 1;
- return -1;
- }
-
- tot_cycles += odp_cpu_cycles_diff(c2, c1);
-
- if (k >= TEST_SIZE_RUN_COUNT) {
- double cycles;
-
- /** Each benchmark runs internally
- * TEST_REPEAT_COUNT times. */
- cycles = ((double)tot_cycles) /
- (TEST_SIZE_RUN_COUNT *
- TEST_REPEAT_COUNT);
- results[j][i] = cycles;
-
- printf("%-30s: %8.1f\n", desc, cycles);
-
- j++;
- k = 0;
- tot_cycles = 0;
- }
- }
- }
- printf("\n%-30s", "Benchmark / packet_size [B]");
- for (i = 0; i < num_sizes; i++) {
- if (i == 0)
- printf(" WARM UP ");
- else
- printf("%8.1d ", test_packet_len[i]);
- }
- printf("\n---------------------------------");
- for (i = 0; i < num_sizes; i++)
- printf("----------");
-
- for (i = 0; i < gbl_args->num_bench; i++) {
- printf("\n[%02d] %-30s", i + 1, args->bench[i].desc != NULL ?
- args->bench[i].desc : args->bench[i].name);
-
- for (j = 0; j < num_sizes; j++)
- printf("%8.1f ", results[i][j]);
- }
- printf("\n\n");
- return 0;
-}
-
-static void allocate_test_packets(uint32_t len, odp_packet_t pkt[], int num)
-{
- int pkts = 0;
-
- while (pkts < num) {
- int ret;
-
- ret = odp_packet_alloc_multi(gbl_args->pool, len, &pkt[pkts],
- num - pkts);
- if (ret < 0)
- LOG_ABORT("Allocating test packets failed\n");
-
- pkts += ret;
- }
-}
-
-static void alloc_packets_half(void)
-{
- allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT);
-}
-
-static void alloc_packets_multi(void)
-{
- allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
-}
-
-static void alloc_concat_packets(void)
-{
- allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT);
- allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt2_tbl,
- TEST_REPEAT_COUNT);
-}
-
-static void alloc_ref_packets(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
-
- allocate_test_packets(gbl_args->pkt.len, pkt_tbl, TEST_REPEAT_COUNT);
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- ref_tbl[i] = odp_packet_ref(pkt_tbl[i], TEST_MIN_PKT_SIZE / 2);
- if (ref_tbl[i] == ODP_PACKET_INVALID)
- LOG_ABORT("Allocating packet reference failed\n");
- }
-}
-
-static void alloc_packets_twice(void)
-{
- allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT);
- allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt2_tbl,
- TEST_REPEAT_COUNT);
-}
-
-static void create_packets(void)
-{
- int i;
- uint32_t headroom, tailroom, seg_len;
- uint32_t min_headroom = 0;
- uint32_t min_tailroom = 0;
- uint32_t min_seg_len = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
-
- allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT);
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- headroom = odp_packet_headroom(pkt_tbl[i]);
- tailroom = odp_packet_tailroom(pkt_tbl[i]);
- seg_len = odp_packet_seg_len(pkt_tbl[i]);
-
- seg_tbl[i] = odp_packet_first_seg(pkt_tbl[i]);
-
- if (i == 0) {
- min_headroom = headroom;
- min_tailroom = tailroom;
- min_seg_len = seg_len;
- } else {
- if (headroom < min_headroom)
- min_headroom = headroom;
- if (tailroom < min_tailroom)
- min_tailroom = tailroom;
- if (seg_len < min_seg_len)
- min_seg_len = seg_len;
- }
-
- if (odp_packet_l2_offset_set(pkt_tbl[i], TEST_L2_OFFSET) ||
- odp_packet_l3_offset_set(pkt_tbl[i], TEST_L3_OFFSET) ||
- odp_packet_l4_offset_set(pkt_tbl[i], TEST_L4_OFFSET))
- LOG_ABORT("Setting test packet offsets failed\n");
-
- odp_packet_flow_hash_set(pkt_tbl[i], i);
- odp_packet_ts_set(pkt_tbl[i], odp_time_local());
- }
- gbl_args->pkt.headroom = min_headroom;
- gbl_args->pkt.tailroom = min_tailroom;
- gbl_args->pkt.seg_len = min_seg_len;
-}
-
-static void create_events(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- create_packets();
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->event_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
-}
-
-static void free_packets(void)
-{
- odp_packet_free_multi(gbl_args->pkt_tbl, TEST_REPEAT_COUNT);
-}
-
-static void free_packets_multi(void)
-{
- odp_packet_free_multi(gbl_args->pkt_tbl,
- TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
-}
-
-static void free_packets_twice(void)
-{
- odp_packet_free_multi(gbl_args->pkt_tbl, TEST_REPEAT_COUNT);
- odp_packet_free_multi(gbl_args->pkt2_tbl, TEST_REPEAT_COUNT);
-}
-
-static int bench_empty(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->output_tbl[i] = i;
-
- return i;
-}
-
-static int bench_packet_alloc(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- odp_packet_t pkt;
-
- pkt = odp_packet_alloc(gbl_args->pool, gbl_args->pkt.len);
-
- gbl_args->pkt_tbl[i] = pkt;
- }
-
- return i;
-}
-
-static int bench_packet_alloc_multi(void)
-{
- int i;
- int pkts = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- pkts += odp_packet_alloc_multi(gbl_args->pool,
- gbl_args->pkt.len,
- &gbl_args->pkt_tbl[pkts],
- gbl_args->appl.burst_size);
- return pkts;
-}
-
-static int bench_packet_free(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- odp_packet_free(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_free_multi(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- int pkt_idx = i * gbl_args->appl.burst_size;
-
- odp_packet_free_multi(&gbl_args->pkt_tbl[pkt_idx],
- gbl_args->appl.burst_size);
- }
- return i;
-}
-
-static int bench_packet_alloc_free(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- odp_packet_t pkt;
-
- pkt = odp_packet_alloc(gbl_args->pool, gbl_args->pkt.len);
-
- odp_packet_free(pkt);
- }
- return i;
-}
-
-static int bench_packet_alloc_free_multi(void)
-{
- int i;
- int pkts;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++) {
- pkts = odp_packet_alloc_multi(gbl_args->pool, gbl_args->pkt.len,
- gbl_args->pkt_tbl,
- gbl_args->appl.burst_size);
- odp_packet_free_multi(gbl_args->pkt_tbl, pkts);
- }
- return i;
-}
-
-static int bench_packet_reset(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_reset(gbl_args->pkt_tbl[i],
- gbl_args->pkt.len);
- return !ret;
-}
-
-static int bench_packet_from_event(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- pkt_tbl[i] = odp_packet_from_event(gbl_args->event_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_to_event(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->event_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_head(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_head(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_buf_len(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_buf_len(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_data(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_data(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_seg_len(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_seg_len(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_len(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_len(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_headroom(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_headroom(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_tailroom(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_tailroom(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_tail(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_tail(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_offset(void)
-{
- int i;
- uint32_t offset = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_offset(gbl_args->pkt_tbl[i],
- offset, NULL, NULL);
- return i;
-}
-
-static int bench_packet_prefetch(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- odp_packet_prefetch(gbl_args->pkt_tbl[i], 0, gbl_args->pkt.len);
-
- return i;
-}
-
-static int bench_packet_push_head(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- uint32_t hroom = gbl_args->pkt.headroom;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_push_head(pkt_tbl[i], hroom);
-
- return i;
-}
-
-static int bench_packet_pull_head(void)
-{
- int i;
- uint32_t len = gbl_args->pkt.seg_len - 1;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_pull_head(pkt_tbl[i], len);
-
- return i;
-}
-
-static int bench_packet_push_tail(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- uint32_t troom = gbl_args->pkt.tailroom;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_push_tail(pkt_tbl[i], troom);
-
- return i;
-}
-
-static int bench_packet_pull_tail(void)
-{
- int i;
- uint32_t len = gbl_args->pkt.seg_len - 1;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_pull_tail(pkt_tbl[i], len);
-
- return i;
-}
-
-static int bench_packet_extend_head(void)
-{
- int i;
- int ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- void **ptr_tbl = gbl_args->ptr_tbl;
- uint32_t *data_tbl = gbl_args->output_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_extend_head(&pkt_tbl[i], len, ptr_tbl[i],
- &data_tbl[i]);
- return ret >= 0;
-}
-
-static int bench_packet_trunc_head(void)
-{
- int i;
- int ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- void **ptr_tbl = gbl_args->ptr_tbl;
- uint32_t *data_tbl = gbl_args->output_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_trunc_head(&pkt_tbl[i], len, ptr_tbl[i],
- &data_tbl[i]);
- return ret >= 0;
-}
-
-static int bench_packet_extend_tail(void)
-{
- int i;
- int ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- void **ptr_tbl = gbl_args->ptr_tbl;
- uint32_t *data_tbl = gbl_args->output_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_extend_tail(&pkt_tbl[i], len, ptr_tbl[i],
- &data_tbl[i]);
- return ret >= 0;
-}
-
-static int bench_packet_trunc_tail(void)
-{
- int i;
- int ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- void **ptr_tbl = gbl_args->ptr_tbl;
- uint32_t *data_tbl = gbl_args->output_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_trunc_tail(&pkt_tbl[i], len, ptr_tbl[i],
- &data_tbl[i]);
- return ret >= 0;
-}
-
-static int bench_packet_add_data(void)
-{
- int i;
- int ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- uint32_t len = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_add_data(&pkt_tbl[i], 0, len);
-
- return ret >= 0;
-}
-
-static int bench_packet_rem_data(void)
-{
- int i;
- int ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- uint32_t len = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_rem_data(&pkt_tbl[i], 0, len);
-
- return ret >= 0;
-}
-
-static int bench_packet_align(void)
-{
- int i;
- int ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_align(&pkt_tbl[i], TEST_ALIGN_OFFSET,
- TEST_ALIGN_LEN, TEST_ALIGN);
- return ret >= 0;
-}
-
-static int bench_packet_is_segmented(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_is_segmented(gbl_args->pkt_tbl[i]);
-
- return (ret == 0) ? 1 : ret;
-}
-
-static int bench_packet_num_segs(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_num_segs(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_first_seg(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->seg_tbl[i] = odp_packet_first_seg(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_last_seg(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->seg_tbl[i] = odp_packet_last_seg(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_next_seg(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->seg_tbl[i] = odp_packet_next_seg(pkt_tbl[i],
- seg_tbl[i]);
- return i;
-}
-
-static int bench_packet_seg_data(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_seg_data(pkt_tbl[i],
- seg_tbl[i]);
- return i;
-}
-
-static int bench_packet_seg_data_len(void)
-{
- int i;
- uint32_t ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_seg_data_len(pkt_tbl[i], seg_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_concat(void)
-{
- int i;
- int ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *frag_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_concat(&pkt_tbl[i], frag_tbl[i]);
-
- return ret >= 0;
-}
-
-static int bench_packet_split(void)
-{
- int i;
- int ret = 0;
- uint32_t head_len;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *frag_tbl = gbl_args->pkt2_tbl;
-
- head_len = odp_packet_len(pkt_tbl[0]) / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_split(&pkt_tbl[i], head_len, &frag_tbl[i]);
-
- return ret >= 0;
-}
-
-static int bench_packet_copy(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *cpy_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- cpy_tbl[i] = odp_packet_copy(pkt_tbl[i], gbl_args->pool);
-
- return i;
-}
-
-static int bench_packet_copy_part(void)
-{
- int i;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *cpy_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- cpy_tbl[i] = odp_packet_copy_part(pkt_tbl[i], 0, len,
- gbl_args->pool);
- return i;
-}
-
-static int bench_packet_copy_to_mem(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t len = gbl_args->pkt.len;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_copy_to_mem(pkt_tbl[i], 0, len,
- gbl_args->data_tbl[i]);
- return !ret;
-}
-
-static int bench_packet_copy_from_mem(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t len = gbl_args->pkt.len;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_copy_from_mem(pkt_tbl[i], 0, len,
- gbl_args->data_tbl[i]);
- return !ret;
-}
-
-static int bench_packet_copy_from_pkt(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t len = gbl_args->pkt.len;
- odp_packet_t *dst_tbl = gbl_args->pkt_tbl;
- odp_packet_t *src_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_copy_from_pkt(dst_tbl[i], 0, src_tbl[i], 0,
- len);
- return !ret;
-}
-
-static int bench_packet_copy_data(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_copy_data(pkt_tbl[i], 0, len, len);
-
- return !ret;
-}
-
-static int bench_packet_move_data(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t len = gbl_args->pkt.len / 2;
- uint32_t offset = len / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_move_data(pkt_tbl[i], offset, len, len);
-
- return !ret;
-}
-
-static int bench_packet_pool(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->pool_tbl[i] = odp_packet_pool(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_input(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->pktio_tbl[i] = odp_packet_input(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_input_index(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_input_index(gbl_args->pkt_tbl[i]);
-
- return (ret == 0) ? 1 : ret;
-}
-
-static int bench_packet_user_ptr(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_user_ptr(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_user_ptr_set(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- odp_packet_user_ptr_set(gbl_args->pkt_tbl[i],
- gbl_args->ptr_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_user_area(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_user_area(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_user_area_size(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_user_area_size(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_l2_ptr(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_l2_ptr(gbl_args->pkt_tbl[i],
- NULL);
- return i;
-}
-
-static int bench_packet_l2_offset(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l2_offset(gbl_args->pkt_tbl[i]);
-
- return ret >= 0;
-}
-
-static int bench_packet_l2_offset_set(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t offset = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l2_offset_set(gbl_args->pkt_tbl[i], offset);
-
- return !ret;
-}
-
-static int bench_packet_l3_ptr(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_l3_ptr(gbl_args->pkt_tbl[i],
- NULL);
- return i;
-}
-
-static int bench_packet_l3_offset(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l3_offset(gbl_args->pkt_tbl[i]);
-
- return ret >= 0;
-}
-
-static int bench_packet_l3_offset_set(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t offset = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l3_offset_set(gbl_args->pkt_tbl[i], offset);
-
- return !ret;
-}
-
-static int bench_packet_l4_ptr(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ptr_tbl[i] = odp_packet_l4_ptr(gbl_args->pkt_tbl[i],
- NULL);
- return i;
-}
-
-static int bench_packet_l4_offset(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l4_offset(gbl_args->pkt_tbl[i]);
-
- return ret >= 0;
-}
-
-static int bench_packet_l4_offset_set(void)
-{
- int i;
- uint32_t ret = 0;
- uint32_t offset = gbl_args->pkt.len / 2;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_l4_offset_set(gbl_args->pkt_tbl[i], offset);
-
- return !ret;
-}
-
-static int bench_packet_flow_hash(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_flow_hash(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_flow_hash_set(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- odp_packet_flow_hash_set(gbl_args->pkt_tbl[i], i);
-
- return i;
-}
-
-static int bench_packet_ts(void)
-{
- int i;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- gbl_args->ts_tbl[i] = odp_packet_ts(gbl_args->pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_ts_set(void)
-{
- int i;
- odp_time_t ts = odp_time_local();
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- odp_packet_ts_set(gbl_args->pkt_tbl[i], ts);
-
- return i;
-}
-
-static int bench_packet_ref_static(void)
-{
- int i;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ref_tbl[i] = odp_packet_ref_static(pkt_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_ref(void)
-{
- int i;
- uint32_t offset = TEST_MIN_PKT_SIZE / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ref_tbl[i] = odp_packet_ref(pkt_tbl[i], offset);
-
- return i;
-}
-
-static int bench_packet_ref_pkt(void)
-{
- int i;
- uint32_t offset = TEST_MIN_PKT_SIZE / 2;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
- odp_packet_t *hdr_tbl = gbl_args->pkt2_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- hdr_tbl[i] = odp_packet_ref_pkt(pkt_tbl[i], offset, hdr_tbl[i]);
-
- return i;
-}
-
-static int bench_packet_unshared_len(void)
-{
- int i;
- uint32_t ret = 0;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_unshared_len(gbl_args->pkt_tbl[i]);
-
- return ret;
-}
-
-static int bench_packet_has_ref(void)
-{
- int i;
- uint32_t ret = 0;
- odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
-
- for (i = 0; i < TEST_REPEAT_COUNT; i++)
- ret += odp_packet_has_ref(pkt_tbl[i]);
-
- return i;
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "OpenDataPlane Packet function microbenchmark.\n"
- "\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s\n"
- "\n"
- "Optional OPTIONS:\n"
- " -b, --burst Test packet burst size.\n"
- " -i, --index Benchmark index to run indefinitely.\n"
- " -h, --help Display help and exit.\n\n"
- "\n", NO_PATH(progname), NO_PATH(progname));
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
- int opt;
- int long_index;
- static const struct option longopts[] = {
- {"burst", required_argument, NULL, 'b'},
- {"help", no_argument, NULL, 'h'},
- {"index", required_argument, NULL, 'i'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "b:i:h";
-
- /* Let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- opterr = 0; /* Do not issue errors on helper options */
-
- appl_args->bench_idx = 0; /* Run all benchmarks */
- appl_args->burst_size = TEST_DEF_BURST;
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'b':
- appl_args->burst_size = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
- case 'i':
- appl_args->bench_idx = atoi(optarg);
- break;
- default:
- break;
- }
- }
-
- if (appl_args->burst_size < 1 ||
- appl_args->burst_size > TEST_MAX_BURST) {
- printf("Invalid burst size (max %d)\n", TEST_MAX_BURST);
- exit(EXIT_FAILURE);
- }
-
- optind = 1; /* Reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args ODP_UNUSED)
-{
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_version_impl_name(),
- odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n", progname);
- fflush(NULL);
-}
-
-/**
- * Test functions
- */
-bench_info_t test_suite[] = {
- BENCH_INFO(bench_empty, NULL, NULL, NULL),
- BENCH_INFO(bench_packet_alloc, NULL, free_packets, NULL),
- BENCH_INFO(bench_packet_alloc_multi, NULL, free_packets_multi,
- NULL),
- BENCH_INFO(bench_packet_free, create_packets, NULL, NULL),
- BENCH_INFO(bench_packet_free_multi, alloc_packets_multi, NULL,
- NULL),
- BENCH_INFO(bench_packet_alloc_free, NULL, NULL, NULL),
- BENCH_INFO(bench_packet_alloc_free_multi, NULL, NULL, NULL),
- BENCH_INFO(bench_packet_reset, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_from_event, create_events, free_packets,
- NULL),
- BENCH_INFO(bench_packet_to_event, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_head, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_buf_len, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_data, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_seg_len, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_len, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_headroom, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_tailroom, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_tail, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_offset, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_prefetch, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_push_head, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_pull_head, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_push_tail, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_pull_tail, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_extend_head, alloc_packets_half,
- free_packets, NULL),
- BENCH_INFO(bench_packet_trunc_head, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_extend_tail, alloc_packets_half,
- free_packets, NULL),
- BENCH_INFO(bench_packet_trunc_tail, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_add_data, alloc_packets_half,
- free_packets, NULL),
- BENCH_INFO(bench_packet_rem_data, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_align, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_is_segmented, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_num_segs, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_first_seg, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_last_seg, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_next_seg, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_seg_data, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_seg_data_len, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_concat, alloc_concat_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_split, create_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_copy, create_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_copy_part, create_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_copy_to_mem, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_copy_from_mem, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_copy_from_pkt, alloc_packets_twice,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_copy_data, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_move_data, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_pool, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_input, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_input_index, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_user_ptr, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_user_ptr_set, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_user_area, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_user_area_size, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_l2_ptr, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l2_offset, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l2_offset_set, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_l3_ptr, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l3_offset, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l3_offset_set, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_l4_ptr, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l4_offset, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_l4_offset_set, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_flow_hash, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_flow_hash_set, create_packets,
- free_packets, NULL),
- BENCH_INFO(bench_packet_ts, create_packets, free_packets, NULL),
- BENCH_INFO(bench_packet_ts_set, create_packets, free_packets,
- NULL),
- BENCH_INFO(bench_packet_ref_static, create_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_ref, create_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_ref_pkt, alloc_packets_twice,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_unshared_len, alloc_ref_packets,
- free_packets_twice, NULL),
- BENCH_INFO(bench_packet_has_ref, alloc_ref_packets,
- free_packets_twice, NULL),
-};
-
-/**
- * ODP packet microbenchmark application
- */
-int main(int argc, char *argv[])
-{
- odph_odpthread_t worker_thread;
- int cpu;
- odp_shm_t shm;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_pool_capability_t capa;
- odp_pool_param_t params;
- odp_instance_t instance;
- uint32_t pkt_num;
- uint8_t ret;
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Reserve memory for args from shared mem */
- shm = odp_shm_reserve("shm_args", sizeof(args_t),
- ODP_CACHE_LINE_SIZE, 0);
-
- if (shm == ODP_SHM_INVALID) {
- LOG_ERR("Error: shared mem reserve failed.\n");
- exit(EXIT_FAILURE);
- }
-
- gbl_args = odp_shm_addr(shm);
-
- if (gbl_args == NULL) {
- LOG_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
-
- memset(gbl_args, 0, sizeof(args_t));
-
- gbl_args->bench = test_suite;
- gbl_args->num_bench = sizeof(test_suite) / sizeof(test_suite[0]);
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &gbl_args->appl);
-
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]), &gbl_args->appl);
-
- /* Get default worker cpumask */
- if (odp_cpumask_default_worker(&cpumask, 1) != 1) {
- LOG_ERR("Error: unable to allocate worker thread.\n");
- exit(EXIT_FAILURE);
- }
-
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- /* Check pool capability */
- if (odp_pool_capability(&capa)) {
- LOG_ERR("Error: unable to query pool capability.\n");
- exit(EXIT_FAILURE);
- }
-
- /* At least 2 x TEST_REPEAT_COUNT packets required */
- pkt_num = (gbl_args->appl.burst_size > 2) ?
- gbl_args->appl.burst_size * TEST_REPEAT_COUNT :
- 2 * TEST_REPEAT_COUNT;
-
- if (capa.pkt.max_num && capa.pkt.max_num < pkt_num) {
- LOG_ERR("Error: packet pool size not supported.\n");
- printf("MAX: %" PRIu32 "\n", capa.pkt.max_num);
- exit(EXIT_FAILURE);
- } else if (capa.pkt.max_len && capa.pkt.max_len < TEST_MAX_PKT_SIZE) {
- LOG_ERR("Error: packet length not supported.\n");
- exit(EXIT_FAILURE);
- } else if (capa.pkt.max_seg_len &&
- capa.pkt.max_seg_len < PKT_POOL_SEG_LEN) {
- LOG_ERR("Error: segment length not supported.\n");
- exit(EXIT_FAILURE);
- } else if (capa.pkt.max_uarea_size &&
- capa.pkt.max_uarea_size < PKT_POOL_UAREA_SIZE) {
- LOG_ERR("Error: user area size not supported.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = PKT_POOL_SEG_LEN;
- params.pkt.len = TEST_MAX_PKT_SIZE;
- params.pkt.num = pkt_num;
- params.pkt.uarea_size = PKT_POOL_UAREA_SIZE;
- params.type = ODP_POOL_PACKET;
-
- gbl_args->pool = odp_pool_create("packet pool", &params);
-
- if (gbl_args->pool == ODP_POOL_INVALID) {
- LOG_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- printf("CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("CPU mask: %s\n", cpumaskstr);
- printf("Burst size: %d\n", gbl_args->appl.burst_size);
- printf("Bench repeat: %d\n", TEST_REPEAT_COUNT);
-
- odp_pool_print(gbl_args->pool);
-
- memset(&worker_thread, 0, sizeof(odph_odpthread_t));
-
- odp_barrier_init(&barrier, 1 + 1);
-
- signal(SIGINT, sig_handler);
-
- /* Create worker threads */
- cpu = odp_cpumask_first(&cpumask);
-
- odp_cpumask_t thd_mask;
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_benchmarks;
- thr_params.arg = gbl_args;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
- odph_odpthreads_create(&worker_thread, &thd_mask,
- &thr_params);
-
- odph_odpthreads_join(&worker_thread);
-
- ret = gbl_args->bench_failed;
-
- if (odp_pool_destroy(gbl_args->pool)) {
- LOG_ERR("Error: pool destroy\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_shm_free(shm)) {
- LOG_ERR("Error: shm free\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- LOG_ERR("Error: term local\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- LOG_ERR("Error: term global\n");
- exit(EXIT_FAILURE);
- }
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_crypto.c b/test/common_plat/performance/odp_crypto.c
deleted file mode 100644
index c3dd6d38f..000000000
--- a/test/common_plat/performance/odp_crypto.c
+++ /dev/null
@@ -1,982 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif /* _GNU_SOURCE */
-
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-
-#define app_err(fmt, ...) \
- fprintf(stderr, "%s:%d:%s(): Error: " fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__)
-
-/** @def POOL_NUM_PKT
- * Number of packets in the pool
- */
-#define POOL_NUM_PKT 64
-
-static uint8_t test_iv[8] = "01234567";
-
-static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a,
- 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10,
-};
-
-static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a,
- 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14,
- 0x15, 0x16, 0x17, 0x18
-};
-
-/**
- * Structure that holds template for session create call
- * for different algorithms supported by test
- */
-typedef struct {
- const char *name; /**< Algorithm name */
- odp_crypto_session_param_t session; /**< Prefilled crypto session params */
-} crypto_alg_config_t;
-
-/**
- * Parsed command line crypto arguments. Describes test configuration.
- */
-typedef struct {
- /**
- * If non zero prints content of packets. Enabled by -d or
- * --debug option.
- */
- int debug_packets;
-
- /**
- * If non zero Try to run crypto operation in place. Note some
- * implementation may not support such mode. Enabled by -n or
- * --inplace option.
- */
- int in_place;
-
- /**
- * If non zeor output of previous operation taken as input for
- * next encrypt operations. Enabled by -r or --reuse option.
- */
- int reuse_packet;
-
- /**
- * Maximum number of outstanding encryption requests. Note code
- * poll for results over queue and if nothing is available it can
- * submit more encryption requests up to maximum number specified by
- * this option. Specified through -f or --flight option.
- */
- int in_flight;
-
- /**
- * Number of iteration to repeat crypto operation to get good
- * average number. Specified through -i or --terations option.
- * Default is 10000.
- */
- int iteration_count;
-
- /**
- * Maximum sessions. Currently is not used.
- */
- int max_sessions;
-
- /**
- * Payload size to test. If 0 set of predefined payload sizes
- * is tested. Specified through -p or --payload option.
- */
- int payload_length;
-
- /**
- * Pointer to selected algorithm to test. If NULL all available
- * alogorthims are tested. Name of algorithm is passed through
- * -a or --algorithm option.
- */
- crypto_alg_config_t *alg_config;
-
- /**
- * Use scheduler to get completion events from crypto operation.
- * Specified through -s argument.
- * */
- int schedule;
-
- /*
- * Poll completion queue for crypto completion events.
- * Specified through -p argument.
- */
- int poll;
-} crypto_args_t;
-
-/*
- * Helper structure that holds averages for test of one algorithm
- * for given payload size.
- */
-typedef struct {
- /**
- * Elapsed time for one crypto operation.
- */
- double elapsed;
-
- /**
- * CPU time spent pre one crypto operation by whole process
- * i.e include current and all other threads in process.
- * It is filled with 'getrusage(RUSAGE_SELF, ...)' call.
- */
- double rusage_self;
-
- /**
- * CPU time spent per one crypto operation by current thread
- * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)'
- * call.
- */
- double rusage_thread;
-} crypto_run_result_t;
-
-/**
- * Structure holds one snap to misc times of current process.
- */
-typedef struct {
- struct timeval tv; /**< Elapsed time */
- struct rusage ru_self; /**< Rusage value for whole process */
- struct rusage ru_thread; /**< Rusage value for current thread */
-} time_record_t;
-
-static void parse_args(int argc, char *argv[], crypto_args_t *cargs);
-static void usage(char *progname);
-
-/**
- * Set of predefined payloads.
- */
-static unsigned int payloads[] = {
- 16,
- 64,
- 256,
- 1024,
- 8192,
- 16384
-};
-
-/** Number of payloads used in the test */
-static unsigned num_payloads;
-
-/**
- * Set of known algorithms to test
- */
-static crypto_alg_config_t algs_config[] = {
- {
- .name = "3des-cbc-null",
- .session = {
- .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
- .cipher_key = {
- .data = test_key24,
- .length = sizeof(test_key24)
- },
- .iv = {
- .data = test_iv,
- .length = 8,
- },
- .auth_alg = ODP_AUTH_ALG_NULL
- },
- },
- {
- .name = "3des-cbc-hmac-md5-96",
- .session = {
- .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
- .cipher_key = {
- .data = test_key24,
- .length = sizeof(test_key24)
- },
- .iv = {
- .data = test_iv,
- .length = 8,
- },
- .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
- .auth_key = {
- .data = test_key16,
- .length = sizeof(test_key16)
- },
- .auth_digest_len = 12,
- },
- },
- {
- .name = "null-hmac-md5-96",
- .session = {
- .cipher_alg = ODP_CIPHER_ALG_NULL,
- .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
- .auth_key = {
- .data = test_key16,
- .length = sizeof(test_key16)
- },
- .auth_digest_len = 12,
- },
- },
-};
-
-/**
- * Find corresponding config for given name. Returns NULL
- * if config for given name is not found.
- */
-static crypto_alg_config_t *
-find_config_by_name(const char *name) {
- unsigned int i;
- crypto_alg_config_t *ret = NULL;
-
- for (i = 0; i < (sizeof(algs_config) / sizeof(crypto_alg_config_t));
- i++) {
- if (strcmp(algs_config[i].name, name) == 0) {
- ret = algs_config + i;
- break;
- }
- }
- return ret;
-}
-
-/**
- * Helper function that prints list of algorithms that this
- * test understands.
- */
-static void
-print_config_names(const char *prefix) {
- unsigned int i;
-
- for (i = 0; i < (sizeof(algs_config) / sizeof(crypto_alg_config_t));
- i++) {
- printf("%s %s\n", prefix, algs_config[i].name);
- }
-}
-
-/**
- * Snap current time values and put them into 'rec'.
- */
-static void
-fill_time_record(time_record_t *rec)
-{
- gettimeofday(&rec->tv, NULL);
- getrusage(RUSAGE_SELF, &rec->ru_self);
- getrusage(RUSAGE_THREAD, &rec->ru_thread);
-}
-
-/**
- * Calculated CPU time difference for given two rusage structures.
- * Note it adds user space and system time together.
- */
-static unsigned long long
-get_rusage_diff(struct rusage *start, struct rusage *end)
-{
- unsigned long long rusage_diff;
- unsigned long long rusage_start;
- unsigned long long rusage_end;
-
- rusage_start = (start->ru_utime.tv_sec * 1000000) +
- (start->ru_utime.tv_usec);
- rusage_start += (start->ru_stime.tv_sec * 1000000) +
- (start->ru_stime.tv_usec);
-
- rusage_end = (end->ru_utime.tv_sec * 1000000) +
- (end->ru_utime.tv_usec);
- rusage_end += (end->ru_stime.tv_sec * 1000000) +
- (end->ru_stime.tv_usec);
-
- rusage_diff = rusage_end - rusage_start;
-
- return rusage_diff;
-}
-
-/**
- * Get diff for RUSAGE_SELF (whole process) between two time snap
- * records.
- */
-static unsigned long long
-get_rusage_self_diff(time_record_t *start, time_record_t *end)
-{
- return get_rusage_diff(&start->ru_self, &end->ru_self);
-}
-
-/**
- * Get diff for RUSAGE_THREAD (current thread only) between two
- * time snap records.
- */
-static unsigned long long
-get_rusage_thread_diff(time_record_t *start, time_record_t *end)
-{
- return get_rusage_diff(&start->ru_thread, &end->ru_thread);
-}
-
-/**
- * Get diff of elapsed time between two time snap records
- */
-static unsigned long long
-get_elapsed_usec(time_record_t *start, time_record_t *end)
-{
- unsigned long long s;
- unsigned long long e;
-
- s = (start->tv.tv_sec * 1000000) +
- (start->tv.tv_usec);
- e = (end->tv.tv_sec * 1000000) +
- (end->tv.tv_usec);
-
- return e - s;
-}
-
-#define REPORT_HEADER "\n%30.30s %15s %15s %15s %15s %15s %15s\n"
-#define REPORT_LINE "%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n"
-
-/**
- * Print header line for our report.
- */
-static void
-print_result_header(void)
-{
- printf(REPORT_HEADER,
- "algorithm", "avg over #", "payload (bytes)", "elapsed (us)",
- "rusg self (us)", "rusg thrd (us)", "throughput (Kb)");
-}
-
-/**
- * Print one line of our report.
- */
-static void
-print_result(crypto_args_t *cargs,
- unsigned int payload_length,
- crypto_alg_config_t *config,
- crypto_run_result_t *result)
-{
- unsigned int throughput;
-
- throughput = (1000000.0 / result->elapsed) * payload_length / 1024;
- printf(REPORT_LINE,
- config->name, cargs->iteration_count, payload_length,
- result->elapsed, result->rusage_self, result->rusage_thread,
- throughput);
-}
-
-/**
- * Print piece of memory with given size.
- */
-static void
-print_mem(const char *msg,
- const unsigned char *ptr,
- unsigned int len)
-{
- unsigned i, j;
- char c;
- char line[81];
- char *p;
-
- if (msg)
- printf("\n%s (bytes size = %d)", msg, len);
-
- for (i = 0; i < len; i += 16) {
- p = line;
- sprintf(p, "\n%04x ", i); p += 8;
-
- for (j = 0; j < 16; j++) {
- if (i + j == len)
- break;
-
- sprintf(p, " %02x", (ptr)[i + j]); p += 3;
- }
-
- for (; j < 16; j++) {
- sprintf(p, " "); p += 3;
- }
-
- sprintf(p, " "); p += 3;
-
- for (j = 0; j < 16; j++) {
- if (i + j == len)
- break;
- c = (ptr)[i + j];
- *p++ = (' ' <= c && c <= '~') ? c : '.';
- }
-
- *p = '\0';
- printf("%s", line);
- }
- printf("\n");
-}
-
-/**
- * Create ODP crypto session for given config.
- */
-static int
-create_session_from_config(odp_crypto_session_t *session,
- crypto_alg_config_t *config,
- crypto_args_t *cargs)
-{
- odp_crypto_session_param_t params;
- odp_crypto_ses_create_err_t ses_create_rc;
- odp_pool_t pkt_pool;
- odp_queue_t out_queue;
-
- odp_crypto_session_param_init(&params);
- memcpy(&params, &config->session, sizeof(odp_crypto_session_param_t));
- params.op = ODP_CRYPTO_OP_ENCODE;
- params.pref_mode = ODP_CRYPTO_SYNC;
-
- /* Lookup the packet pool */
- pkt_pool = odp_pool_lookup("packet_pool");
- if (pkt_pool == ODP_POOL_INVALID) {
- app_err("packet_pool pool not found\n");
- return -1;
- }
- params.output_pool = pkt_pool;
-
- if (cargs->schedule || cargs->poll) {
- out_queue = odp_queue_lookup("crypto-out");
- if (out_queue == ODP_QUEUE_INVALID) {
- app_err("crypto-out queue not found\n");
- return -1;
- }
- params.compl_queue = out_queue;
-
- } else {
- params.compl_queue = ODP_QUEUE_INVALID;
- }
- if (odp_crypto_session_create(&params, session,
- &ses_create_rc)) {
- app_err("crypto session create failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-/**
- * Run measurement iterations for given config and payload size.
- * Result of run returned in 'result' out parameter.
- */
-static int
-run_measure_one(crypto_args_t *cargs,
- crypto_alg_config_t *config,
- odp_crypto_session_t *session,
- unsigned int payload_length,
- crypto_run_result_t *result)
-{
- odp_crypto_op_param_t params;
-
- odp_pool_t pkt_pool;
- odp_queue_t out_queue;
- odp_packet_t pkt;
- int rc = 0;
-
- odp_bool_t posted = 0;
-
- pkt_pool = odp_pool_lookup("packet_pool");
- if (pkt_pool == ODP_POOL_INVALID) {
- app_err("pkt_pool not found\n");
- return -1;
- }
-
- out_queue = odp_queue_lookup("crypto-out");
- if (cargs->schedule || cargs->poll) {
- if (out_queue == ODP_QUEUE_INVALID) {
- app_err("crypto-out queue not found\n");
- return -1;
- }
- }
-
- pkt = odp_packet_alloc(pkt_pool, payload_length);
- if (pkt == ODP_PACKET_INVALID) {
- app_err("failed to allocate buffer\n");
- return -1;
- }
-
- void *mem = odp_packet_data(pkt);
-
- memset(mem, 1, payload_length);
-
- time_record_t start, end;
- int packets_sent = 0;
- int packets_received = 0;
-
- /* Initialize parameters block */
- memset(&params, 0, sizeof(params));
- params.session = *session;
-
- params.cipher_range.offset = 0;
- params.cipher_range.length = payload_length;
-
- params.auth_range.offset = 0;
- params.auth_range.length = payload_length;
- params.hash_result_offset = payload_length;
-
- if (cargs->reuse_packet) {
- params.pkt = pkt;
- params.out_pkt = cargs->in_place ? pkt :
- ODP_PACKET_INVALID;
- }
-
- fill_time_record(&start);
-
- while ((packets_sent < cargs->iteration_count) ||
- (packets_received < cargs->iteration_count)) {
- void *mem;
- odp_crypto_op_result_t result;
-
- if ((packets_sent < cargs->iteration_count) &&
- (packets_sent - packets_received <
- cargs->in_flight)) {
- if (!cargs->reuse_packet) {
- /*
- * For in place test we use just one
- * statically allocated buffer.
- * For now in place test we have to
- * allocate and initialize packet
- * every time.
- * Note we leaked one packet here.
- */
- odp_packet_t newpkt;
-
- newpkt = odp_packet_alloc(pkt_pool,
- payload_length);
- if (newpkt == ODP_PACKET_INVALID) {
- app_err("failed to allocate buffer\n");
- return -1;
- }
- mem = odp_packet_data(newpkt);
- memset(mem, 1, payload_length);
- params.pkt = newpkt;
- params.out_pkt = cargs->in_place ? newpkt :
- ODP_PACKET_INVALID;
- }
-
- if (cargs->debug_packets) {
- mem = odp_packet_data(params.pkt);
- print_mem("Packet before encryption:",
- mem, payload_length);
- }
-
- rc = odp_crypto_operation(&params, &posted,
- &result);
- if (rc)
- app_err("failed odp_crypto_operation: rc = %d\n",
- rc);
- else
- packets_sent++;
- }
-
- if (!posted) {
- packets_received++;
- if (cargs->debug_packets) {
- mem = odp_packet_data(params.out_pkt);
- print_mem("Immediately encrypted packet", mem,
- payload_length +
- config->session.auth_digest_len);
- }
- if (!cargs->in_place) {
- if (cargs->reuse_packet) {
- params.pkt = params.out_pkt;
- params.out_pkt = ODP_PACKET_INVALID;
- } else {
- odp_packet_free(params.out_pkt);
- }
- }
- } else {
- odp_event_t ev;
- odp_crypto_compl_t compl;
- odp_crypto_op_result_t result;
- odp_packet_t out_pkt;
-
- if (cargs->schedule)
- ev = odp_schedule(NULL,
- ODP_SCHED_NO_WAIT);
- else
- ev = odp_queue_deq(out_queue);
-
- while (ev != ODP_EVENT_INVALID) {
- compl = odp_crypto_compl_from_event(ev);
- odp_crypto_compl_result(compl, &result);
- odp_crypto_compl_free(compl);
- out_pkt = result.pkt;
-
- if (cargs->debug_packets) {
- mem = odp_packet_data(out_pkt);
- print_mem("Receieved encrypted packet",
- mem,
- payload_length +
- config->
- session.auth_digest_len);
- }
- if (cargs->reuse_packet) {
- params.pkt = out_pkt;
- params.out_pkt = ODP_PACKET_INVALID;
- } else {
- odp_packet_free(out_pkt);
- }
- packets_received++;
- if (cargs->schedule)
- ev = odp_schedule(NULL,
- ODP_SCHED_NO_WAIT);
- else
- ev = odp_queue_deq(out_queue);
- };
- }
- }
-
- fill_time_record(&end);
-
- {
- double count;
-
- count = get_elapsed_usec(&start, &end);
- result->elapsed = count /
- cargs->iteration_count;
-
- count = get_rusage_self_diff(&start, &end);
- result->rusage_self = count /
- cargs->iteration_count;
-
- count = get_rusage_thread_diff(&start, &end);
- result->rusage_thread = count /
- cargs->iteration_count;
- }
-
- odp_packet_free(pkt);
-
- return rc;
-}
-
-/**
- * Process one algorithm. Note if paload size is specicified it is
- * only one run. Or iterate over set of predefined payloads.
- */
-static int
-run_measure_one_config(crypto_args_t *cargs,
- crypto_alg_config_t *config)
-{
- crypto_run_result_t result;
- odp_crypto_session_t session;
- int rc = 0;
-
- if (create_session_from_config(&session, config, cargs))
- return -1;
-
- if (cargs->payload_length) {
- rc = run_measure_one(cargs, config, &session,
- cargs->payload_length, &result);
- if (!rc) {
- print_result_header();
- print_result(cargs, cargs->payload_length,
- config, &result);
- }
- } else {
- unsigned i;
-
- print_result_header();
- for (i = 0; i < num_payloads; i++) {
- rc = run_measure_one(cargs, config, &session,
- payloads[i], &result);
- if (rc)
- break;
- print_result(cargs, payloads[i],
- config, &result);
- }
- }
-
- odp_crypto_session_destroy(session);
-
- return rc;
-}
-
-typedef struct thr_arg {
- crypto_args_t crypto_args;
- crypto_alg_config_t *crypto_alg_config;
-} thr_arg_t;
-
-static int run_thr_func(void *arg)
-{
- thr_arg_t *thr_args = (thr_arg_t *)arg;
-
- run_measure_one_config(&thr_args->crypto_args,
- thr_args->crypto_alg_config);
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- crypto_args_t cargs;
- odp_pool_t pool;
- odp_queue_param_t qparam;
- odp_pool_param_t params;
- odp_queue_t out_queue = ODP_QUEUE_INVALID;
- thr_arg_t thr_arg;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- int num_workers = 1;
- odph_odpthread_t thr[num_workers];
- odp_instance_t instance;
- odp_pool_capability_t capa;
- uint32_t max_seg_len;
- unsigned i;
-
- memset(&cargs, 0, sizeof(cargs));
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &cargs);
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- app_err("ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Init this thread */
- odp_init_local(instance, ODP_THREAD_WORKER);
-
- if (odp_pool_capability(&capa)) {
- app_err("Pool capability request failed.\n");
- exit(EXIT_FAILURE);
- }
-
- max_seg_len = capa.pkt.max_seg_len;
-
- for (i = 0; i < sizeof(payloads) / sizeof(unsigned int); i++) {
- if (payloads[i] > max_seg_len)
- break;
- }
-
- num_payloads = i;
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = max_seg_len;
- params.pkt.len = max_seg_len;
- params.pkt.num = POOL_NUM_PKT;
- params.type = ODP_POOL_PACKET;
- pool = odp_pool_create("packet_pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- app_err("packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
- odp_pool_print(pool);
-
- odp_queue_param_init(&qparam);
- if (cargs.schedule) {
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- out_queue = odp_queue_create("crypto-out", &qparam);
- } else if (cargs.poll) {
- qparam.type = ODP_QUEUE_TYPE_PLAIN;
- out_queue = odp_queue_create("crypto-out", &qparam);
- }
- if (cargs.schedule || cargs.poll) {
- if (out_queue == ODP_QUEUE_INVALID) {
- app_err("crypto-out queue create failed.\n");
- exit(EXIT_FAILURE);
- }
- }
-
- if (cargs.schedule) {
- printf("Run in async scheduled mode\n");
-
- thr_arg.crypto_args = cargs;
- thr_arg.crypto_alg_config = cargs.alg_config;
- num_workers = odp_cpumask_default_worker(&cpumask,
- num_workers);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
- sizeof(cpumaskstr));
- printf("num worker threads: %i\n",
- num_workers);
- printf("first CPU: %i\n",
- odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n",
- cpumaskstr);
- } else if (cargs.poll) {
- printf("Run in async poll mode\n");
- } else {
- printf("Run in sync mode\n");
- }
-
- memset(thr, 0, sizeof(thr));
-
- if (cargs.alg_config) {
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_thr_func;
- thr_params.arg = &thr_arg;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- if (cargs.schedule) {
- odph_odpthreads_create(&thr[0], &cpumask, &thr_params);
- odph_odpthreads_join(&thr[0]);
- } else {
- run_measure_one_config(&cargs, cargs.alg_config);
- }
- } else {
- unsigned int i;
-
- for (i = 0;
- i < (sizeof(algs_config) / sizeof(crypto_alg_config_t));
- i++) {
- run_measure_one_config(&cargs, algs_config + i);
- }
- }
-
- if (odp_pool_destroy(pool)) {
- app_err("Error: pool destroy\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- app_err("Error: term local\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- app_err("Error: term global\n");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
-
-static void parse_args(int argc, char *argv[], crypto_args_t *cargs)
-{
- int opt;
- int long_index;
- static const struct option longopts[] = {
- {"algorithm", optional_argument, NULL, 'a'},
- {"debug", no_argument, NULL, 'd'},
- {"flight", optional_argument, NULL, 'f'},
- {"help", no_argument, NULL, 'h'},
- {"iterations", optional_argument, NULL, 'i'},
- {"inplace", no_argument, NULL, 'n'},
- {"payload", optional_argument, NULL, 'l'},
- {"sessions", optional_argument, NULL, 'm'},
- {"reuse", no_argument, NULL, 'r'},
- {"poll", no_argument, NULL, 'p'},
- {"schedule", no_argument, NULL, 's'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+a:c:df:hi:m:nl:spr";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- cargs->in_place = 0;
- cargs->in_flight = 1;
- cargs->debug_packets = 0;
- cargs->iteration_count = 10000;
- cargs->payload_length = 0;
- cargs->alg_config = NULL;
- cargs->reuse_packet = 0;
- cargs->schedule = 0;
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'a':
- cargs->alg_config = find_config_by_name(optarg);
- if (!cargs->alg_config) {
- printf("cannot test crypto '%s' configuration\n",
- optarg);
- usage(argv[0]);
- exit(-1);
- }
- break;
- case 'd':
- cargs->debug_packets = 1;
- break;
- case 'i':
- cargs->iteration_count = atoi(optarg);
- break;
- case 'f':
- cargs->in_flight = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
- case 'm':
- cargs->max_sessions = atoi(optarg);
- break;
- case 'n':
- cargs->in_place = 1;
- break;
- case 'l':
- cargs->payload_length = atoi(optarg);
- break;
- case 'r':
- cargs->reuse_packet = 1;
- break;
- case 's':
- cargs->schedule = 1;
- break;
- case 'p':
- cargs->poll = 1;
- break;
- default:
- break;
- }
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-
- if ((cargs->in_flight > 1) && cargs->reuse_packet) {
- printf("-f (in flight > 1) and -r (reuse packet) options are not compatible\n");
- usage(argv[0]);
- exit(-1);
- }
- if (cargs->schedule && cargs->poll) {
- printf("-s (schedule) and -p (poll) options are not compatible\n");
- usage(argv[0]);
- exit(-1);
- }
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s -i 100000\n"
- "\n"
- "OpenDataPlane crypto speed measure.\n"
- "Optional OPTIONS\n"
- " -a, --algorithm <name> Specify algorithm name (default all)\n"
- " Supported values are:\n",
- progname, progname);
-
- print_config_names(" ");
- printf(" -d, --debug Enable dump of processed packets.\n"
- " -f, --flight <number> Max number of packet processed in parallel (default 1)\n"
- " -i, --iterations <number> Number of iterations.\n"
- " -n, --inplace Encrypt on place.\n"
- " -l, --payload Payload length.\n"
- " -r, --reuse Output encrypted packet is passed as input\n"
- " to next encrypt iteration.\n"
- " -s, --schedule Use scheduler for completion events.\n"
- " -p, --poll Poll completion queue for completion events.\n"
- " -h, --help Display help and exit.\n"
- "\n");
-}
diff --git a/test/common_plat/performance/odp_l2fwd.c b/test/common_plat/performance/odp_l2fwd.c
deleted file mode 100644
index 25a92cc39..000000000
--- a/test/common_plat/performance/odp_l2fwd.c
+++ /dev/null
@@ -1,1689 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_l2fwd.c ODP basic forwarding application
- */
-
-/** enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <assert.h>
-#include <signal.h>
-
-#include <test_debug.h>
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-
-/** @def MAX_WORKERS
- * @brief Maximum number of worker threads
- */
-#define MAX_WORKERS 32
-
-/** @def SHM_PKT_POOL_SIZE
- * @brief Size of the shared memory block
- */
-#define SHM_PKT_POOL_SIZE 8192
-
-/** @def SHM_PKT_POOL_BUF_SIZE
- * @brief Buffer size of the packet pool buffer
- */
-#define SHM_PKT_POOL_BUF_SIZE 1856
-
-/** @def MAX_PKT_BURST
- * @brief Maximum number of packet in a burst
- */
-#define MAX_PKT_BURST 32
-
-/** Maximum number of pktio queues per interface */
-#define MAX_QUEUES 32
-
-/** Maximum number of pktio interfaces */
-#define MAX_PKTIOS 8
-
-/** Maximum pktio index table size */
-#define MAX_PKTIO_INDEXES 1024
-
-/**
- * Packet input mode
- */
-typedef enum pktin_mode_t {
- DIRECT_RECV,
- PLAIN_QUEUE,
- SCHED_PARALLEL,
- SCHED_ATOMIC,
- SCHED_ORDERED,
-} pktin_mode_t;
-
-/**
- * Packet output modes
- */
-typedef enum pktout_mode_t {
- PKTOUT_DIRECT,
- PKTOUT_QUEUE
-} pktout_mode_t;
-
-static inline int sched_mode(pktin_mode_t in_mode)
-{
- return (in_mode == SCHED_PARALLEL) ||
- (in_mode == SCHED_ATOMIC) ||
- (in_mode == SCHED_ORDERED);
-}
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-/**
- * Parsed command line application arguments
- */
-typedef struct {
- int cpu_count;
- int if_count; /**< Number of interfaces to be used */
- int addr_count; /**< Number of dst addresses to be used */
- int num_workers; /**< Number of worker threads */
- char **if_names; /**< Array of pointers to interface names */
- odph_ethaddr_t addrs[MAX_PKTIOS]; /**< Array of dst addresses */
- pktin_mode_t in_mode; /**< Packet input mode */
- pktout_mode_t out_mode; /**< Packet output mode */
- int time; /**< Time in seconds to run. */
- int accuracy; /**< Number of seconds to get and print statistics */
- char *if_str; /**< Storage for interface names */
- int dst_change; /**< Change destination eth addresses */
- int src_change; /**< Change source eth addresses */
- int error_check; /**< Check packet errors */
- int sched_mode; /**< Scheduler mode */
- int num_groups; /**< Number of scheduling groups */
-} appl_args_t;
-
-static int exit_threads; /**< Break workers loop if set to 1 */
-
-static void sig_handler(int signo ODP_UNUSED)
-{
- exit_threads = 1;
-}
-
-/**
- * Statistics
- */
-typedef union {
- struct {
- /** Number of forwarded packets */
- uint64_t packets;
- /** Packets dropped due to receive error */
- uint64_t rx_drops;
- /** Packets dropped due to transmit error */
- uint64_t tx_drops;
- } s;
-
- uint8_t padding[ODP_CACHE_LINE_SIZE];
-} stats_t ODP_ALIGNED_CACHE;
-
-/**
- * Thread specific arguments
- */
-typedef struct thread_args_t {
- int thr_idx;
- int num_pktio;
- int num_groups;
-
- struct {
- odp_pktin_queue_t pktin;
- odp_pktout_queue_t pktout;
- odp_queue_t rx_queue;
- odp_queue_t tx_queue;
- int rx_idx;
- int tx_idx;
- int rx_queue_idx;
- int tx_queue_idx;
- } pktio[MAX_PKTIOS];
-
- /* Groups to join */
- odp_schedule_group_t group[MAX_PKTIOS];
-
- /* Pointer to per thread stats */
- stats_t *stats;
-
-} thread_args_t;
-
-/**
- * Grouping of all global data
- */
-typedef struct {
- /** Barriers to synchronize main and workers */
- odp_barrier_t init_barrier;
- odp_barrier_t term_barrier;
- /** Per thread packet stats */
- stats_t stats[MAX_WORKERS];
- /** Application (parsed) arguments */
- appl_args_t appl;
- /** Thread specific arguments */
- thread_args_t thread[MAX_WORKERS];
- /** Table of port ethernet addresses */
- odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
- /** Table of dst ethernet addresses */
- odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
- /** Table of dst ports. This is used by non-sched modes. */
- int dst_port[MAX_PKTIOS];
- /** Table of pktio handles */
- struct {
- odp_pktio_t pktio;
- odp_pktin_queue_t pktin[MAX_QUEUES];
- odp_pktout_queue_t pktout[MAX_QUEUES];
- odp_queue_t rx_q[MAX_QUEUES];
- odp_queue_t tx_q[MAX_QUEUES];
- int num_rx_thr;
- int num_tx_thr;
- int num_rx_queue;
- int num_tx_queue;
- int next_rx_queue;
- int next_tx_queue;
- } pktios[MAX_PKTIOS];
-
- /** Destination port lookup table.
- * Table index is pktio_index of the API. This is used by the sched
- * mode. */
- uint8_t dst_port_from_idx[MAX_PKTIO_INDEXES];
-
-} args_t;
-
-/** Global pointer to args */
-static args_t *gbl_args;
-
-/**
- * Drop packets which input parsing marked as containing errors.
- *
- * Frees packets with error and modifies pkt_tbl[] to only contain packets with
- * no detected errors.
- *
- * @param pkt_tbl Array of packets
- * @param num Number of packets in pkt_tbl[]
- *
- * @return Number of packets dropped
- */
-static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
-{
- odp_packet_t pkt;
- unsigned dropped = 0;
- unsigned i, j;
-
- for (i = 0, j = 0; i < num; ++i) {
- pkt = pkt_tbl[i];
-
- if (odp_unlikely(odp_packet_has_error(pkt))) {
- odp_packet_free(pkt); /* Drop */
- dropped++;
- } else if (odp_unlikely(i != j++)) {
- pkt_tbl[j - 1] = pkt;
- }
- }
-
- return dropped;
-}
-
-/**
- * Fill packets' eth addresses according to the destination port
- *
- * @param pkt_tbl Array of packets
- * @param num Number of packets in the array
- * @param dst_port Destination port
- */
-static inline void fill_eth_addrs(odp_packet_t pkt_tbl[],
- unsigned num, int dst_port)
-{
- odp_packet_t pkt;
- odph_ethhdr_t *eth;
- unsigned i;
-
- if (!gbl_args->appl.dst_change && !gbl_args->appl.src_change)
- return;
-
- for (i = 0; i < num; ++i) {
- pkt = pkt_tbl[i];
-
- odp_packet_prefetch(pkt, 0, ODPH_ETHHDR_LEN);
-
- eth = odp_packet_data(pkt);
-
- if (gbl_args->appl.src_change)
- eth->src = gbl_args->port_eth_addr[dst_port];
-
- if (gbl_args->appl.dst_change)
- eth->dst = gbl_args->dst_eth_addr[dst_port];
- }
-}
-
-static inline int event_queue_send(odp_queue_t queue, odp_packet_t *pkt_tbl,
- unsigned pkts)
-{
- int ret;
- unsigned i;
- unsigned sent = 0;
- odp_event_t ev_tbl[pkts];
-
- for (i = 0; i < pkts; i++)
- ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
-
- while (sent < pkts) {
- ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
-
- if (ret < 0) {
- LOG_ERR("Failed to send packet as events\n");
- break;
- }
-
- sent += ret;
- }
-
- return sent;
-}
-
-/**
- * Packet IO worker thread using scheduled queues
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-static int run_worker_sched_mode(void *arg)
-{
- int pkts;
- int thr;
- int dst_idx;
- int i;
- int pktio, num_pktio;
- odp_pktout_queue_t pktout[MAX_PKTIOS];
- odp_queue_t tx_queue[MAX_PKTIOS];
- thread_args_t *thr_args = arg;
- stats_t *stats = thr_args->stats;
- int use_event_queue = gbl_args->appl.out_mode;
- pktin_mode_t in_mode = gbl_args->appl.in_mode;
-
- thr = odp_thread_id();
-
- if (gbl_args->appl.num_groups) {
- odp_thrmask_t mask;
-
- odp_thrmask_zero(&mask);
- odp_thrmask_set(&mask, thr);
-
- /* Join non-default groups */
- for (i = 0; i < thr_args->num_groups; i++) {
- if (odp_schedule_group_join(thr_args->group[i],
- &mask)) {
- LOG_ERR("Join failed\n");
- return -1;
- }
- }
- }
-
- num_pktio = thr_args->num_pktio;
-
- if (num_pktio > MAX_PKTIOS) {
- LOG_ERR("Too many pktios %i\n", num_pktio);
- return -1;
- }
-
- for (pktio = 0; pktio < num_pktio; pktio++) {
- tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
- pktout[pktio] = thr_args->pktio[pktio].pktout;
- }
-
- printf("[%02i] PKTIN_SCHED_%s, %s\n", thr,
- (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
- ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
- (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
-
- odp_barrier_wait(&gbl_args->init_barrier);
-
- /* Loop packets */
- while (!exit_threads) {
- odp_event_t ev_tbl[MAX_PKT_BURST];
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- int sent;
- unsigned tx_drops;
- int src_idx;
-
- pkts = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT, ev_tbl,
- MAX_PKT_BURST);
-
- if (pkts <= 0)
- continue;
-
- for (i = 0; i < pkts; i++)
- pkt_tbl[i] = odp_packet_from_event(ev_tbl[i]);
-
- if (gbl_args->appl.error_check) {
- int rx_drops;
-
- /* Drop packets with errors */
- rx_drops = drop_err_pkts(pkt_tbl, pkts);
-
- if (odp_unlikely(rx_drops)) {
- stats->s.rx_drops += rx_drops;
- if (pkts == rx_drops)
- continue;
-
- pkts -= rx_drops;
- }
- }
-
- /* packets from the same queue are from the same interface */
- src_idx = odp_packet_input_index(pkt_tbl[0]);
- assert(src_idx >= 0);
- dst_idx = gbl_args->dst_port_from_idx[src_idx];
- fill_eth_addrs(pkt_tbl, pkts, dst_idx);
-
- if (odp_unlikely(use_event_queue))
- sent = event_queue_send(tx_queue[dst_idx], pkt_tbl,
- pkts);
- else
- sent = odp_pktout_send(pktout[dst_idx], pkt_tbl, pkts);
-
- sent = odp_unlikely(sent < 0) ? 0 : sent;
- tx_drops = pkts - sent;
-
- if (odp_unlikely(tx_drops)) {
- stats->s.tx_drops += tx_drops;
-
- /* Drop rejected packets */
- for (i = sent; i < pkts; i++)
- odp_packet_free(pkt_tbl[i]);
- }
-
- stats->s.packets += pkts;
- }
-
- /* Make sure that latest stat writes are visible to other threads */
- odp_mb_full();
-
- /* Wait until pktio devices are stopped */
- odp_barrier_wait(&gbl_args->term_barrier);
-
- /* Free remaining events in queues */
- while (1) {
- odp_event_t ev;
-
- ev = odp_schedule(NULL,
- odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- odp_event_free(ev);
- }
-
- return 0;
-}
-
-/**
- * Packet IO worker thread using plain queues
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-static int run_worker_plain_queue_mode(void *arg)
-{
- int thr;
- int pkts;
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- int dst_idx, num_pktio;
- odp_queue_t queue;
- odp_pktout_queue_t pktout;
- odp_queue_t tx_queue;
- int pktio = 0;
- thread_args_t *thr_args = arg;
- stats_t *stats = thr_args->stats;
- int use_event_queue = gbl_args->appl.out_mode;
- int i;
-
- thr = odp_thread_id();
-
- num_pktio = thr_args->num_pktio;
- dst_idx = thr_args->pktio[pktio].tx_idx;
- queue = thr_args->pktio[pktio].rx_queue;
- pktout = thr_args->pktio[pktio].pktout;
- tx_queue = thr_args->pktio[pktio].tx_queue;
-
- printf("[%02i] num pktios %i, PKTIN_QUEUE, %s\n", thr, num_pktio,
- (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
-
- odp_barrier_wait(&gbl_args->init_barrier);
-
- /* Loop packets */
- while (!exit_threads) {
- int sent;
- unsigned tx_drops;
- odp_event_t event[MAX_PKT_BURST];
-
- if (num_pktio > 1) {
- dst_idx = thr_args->pktio[pktio].tx_idx;
- queue = thr_args->pktio[pktio].rx_queue;
- pktout = thr_args->pktio[pktio].pktout;
- if (odp_unlikely(use_event_queue))
- tx_queue = thr_args->pktio[pktio].tx_queue;
-
- pktio++;
- if (pktio == num_pktio)
- pktio = 0;
- }
-
- pkts = odp_queue_deq_multi(queue, event, MAX_PKT_BURST);
- if (odp_unlikely(pkts <= 0))
- continue;
-
- for (i = 0; i < pkts; i++)
- pkt_tbl[i] = odp_packet_from_event(event[i]);
-
- if (gbl_args->appl.error_check) {
- int rx_drops;
-
- /* Drop packets with errors */
- rx_drops = drop_err_pkts(pkt_tbl, pkts);
-
- if (odp_unlikely(rx_drops)) {
- stats->s.rx_drops += rx_drops;
- if (pkts == rx_drops)
- continue;
-
- pkts -= rx_drops;
- }
- }
-
- fill_eth_addrs(pkt_tbl, pkts, dst_idx);
-
- if (odp_unlikely(use_event_queue))
- sent = event_queue_send(tx_queue, pkt_tbl, pkts);
- else
- sent = odp_pktout_send(pktout, pkt_tbl, pkts);
-
- sent = odp_unlikely(sent < 0) ? 0 : sent;
- tx_drops = pkts - sent;
-
- if (odp_unlikely(tx_drops)) {
- int i;
-
- stats->s.tx_drops += tx_drops;
-
- /* Drop rejected packets */
- for (i = sent; i < pkts; i++)
- odp_packet_free(pkt_tbl[i]);
- }
-
- stats->s.packets += pkts;
- }
-
- /* Make sure that latest stat writes are visible to other threads */
- odp_mb_full();
-
- /* Wait until pktio devices are stopped */
- odp_barrier_wait(&gbl_args->term_barrier);
-
- /* Free remaining events in queues */
- for (i = 0; i < num_pktio; i++) {
- odp_time_t recv_last = odp_time_local();
- odp_time_t since_last;
-
- queue = thr_args->pktio[i].rx_queue;
- do {
- odp_event_t ev = odp_queue_deq(queue);
-
- if (ev != ODP_EVENT_INVALID) {
- recv_last = odp_time_local();
- odp_event_free(ev);
- }
-
- since_last = odp_time_diff(odp_time_local(), recv_last);
- } while (odp_time_to_ns(since_last) < ODP_TIME_SEC_IN_NS);
- }
-
- return 0;
-}
-
-/**
- * Packet IO worker thread accessing IO resources directly
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-static int run_worker_direct_mode(void *arg)
-{
- int thr;
- int pkts;
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- int dst_idx, num_pktio;
- odp_pktin_queue_t pktin;
- odp_pktout_queue_t pktout;
- odp_queue_t tx_queue;
- int pktio = 0;
- thread_args_t *thr_args = arg;
- stats_t *stats = thr_args->stats;
- int use_event_queue = gbl_args->appl.out_mode;
-
- thr = odp_thread_id();
-
- num_pktio = thr_args->num_pktio;
- dst_idx = thr_args->pktio[pktio].tx_idx;
- pktin = thr_args->pktio[pktio].pktin;
- pktout = thr_args->pktio[pktio].pktout;
- tx_queue = thr_args->pktio[pktio].tx_queue;
-
- printf("[%02i] num pktios %i, PKTIN_DIRECT, %s\n", thr, num_pktio,
- (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
-
- odp_barrier_wait(&gbl_args->init_barrier);
-
- /* Loop packets */
- while (!exit_threads) {
- int sent;
- unsigned tx_drops;
-
- if (num_pktio > 1) {
- dst_idx = thr_args->pktio[pktio].tx_idx;
- pktin = thr_args->pktio[pktio].pktin;
- pktout = thr_args->pktio[pktio].pktout;
- if (odp_unlikely(use_event_queue))
- tx_queue = thr_args->pktio[pktio].tx_queue;
-
- pktio++;
- if (pktio == num_pktio)
- pktio = 0;
- }
-
- pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
- if (odp_unlikely(pkts <= 0))
- continue;
-
- if (gbl_args->appl.error_check) {
- int rx_drops;
-
- /* Drop packets with errors */
- rx_drops = drop_err_pkts(pkt_tbl, pkts);
-
- if (odp_unlikely(rx_drops)) {
- stats->s.rx_drops += rx_drops;
- if (pkts == rx_drops)
- continue;
-
- pkts -= rx_drops;
- }
- }
-
- fill_eth_addrs(pkt_tbl, pkts, dst_idx);
-
- if (odp_unlikely(use_event_queue))
- sent = event_queue_send(tx_queue, pkt_tbl, pkts);
- else
- sent = odp_pktout_send(pktout, pkt_tbl, pkts);
-
- sent = odp_unlikely(sent < 0) ? 0 : sent;
- tx_drops = pkts - sent;
-
- if (odp_unlikely(tx_drops)) {
- int i;
-
- stats->s.tx_drops += tx_drops;
-
- /* Drop rejected packets */
- for (i = sent; i < pkts; i++)
- odp_packet_free(pkt_tbl[i]);
- }
-
- stats->s.packets += pkts;
- }
-
- /* Make sure that latest stat writes are visible to other threads */
- odp_mb_full();
-
- return 0;
-}
-
-/**
- * Create a pktio handle, optionally associating a default input queue.
- *
- * @param dev Name of device to open
- * @param index Pktio index
- * @param pool Pool to associate with device for packet RX/TX
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
- odp_pool_t pool, odp_schedule_group_t group)
-{
- odp_pktio_t pktio;
- odp_pktio_param_t pktio_param;
- odp_schedule_sync_t sync_mode;
- odp_pktio_capability_t capa;
- odp_pktio_config_t config;
- odp_pktin_queue_param_t pktin_param;
- odp_pktout_queue_param_t pktout_param;
- odp_pktio_op_mode_t mode_rx;
- odp_pktio_op_mode_t mode_tx;
- pktin_mode_t in_mode = gbl_args->appl.in_mode;
- odp_pktio_info_t info;
-
- odp_pktio_param_init(&pktio_param);
-
- if (in_mode == PLAIN_QUEUE)
- pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
- else if (in_mode != DIRECT_RECV) /* pktin_mode SCHED_* */
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- if (gbl_args->appl.out_mode != PKTOUT_DIRECT)
- pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
-
- pktio = odp_pktio_open(dev, pool, &pktio_param);
- if (pktio == ODP_PKTIO_INVALID) {
- LOG_ERR("Error: failed to open %s\n", dev);
- return -1;
- }
-
- if (odp_pktio_info(pktio, &info)) {
- LOG_ERR("Error: pktio info failed %s\n", dev);
- return -1;
- }
-
- printf("created pktio %" PRIu64 ", dev: %s, drv: %s\n",
- odp_pktio_to_u64(pktio), dev, info.drv_name);
-
- if (odp_pktio_capability(pktio, &capa)) {
- LOG_ERR("Error: capability query failed %s\n", dev);
- return -1;
- }
-
- odp_pktio_config_init(&config);
- config.parser.layer = gbl_args->appl.error_check ?
- ODP_PKTIO_PARSER_LAYER_ALL :
- ODP_PKTIO_PARSER_LAYER_NONE;
- odp_pktio_config(pktio, &config);
-
- odp_pktin_queue_param_init(&pktin_param);
- odp_pktout_queue_param_init(&pktout_param);
-
- /* By default use a queue per worker. Sched mode ignores rx side
- * setting. */
- mode_rx = ODP_PKTIO_OP_MT_UNSAFE;
- mode_tx = ODP_PKTIO_OP_MT_UNSAFE;
-
- if (gbl_args->appl.sched_mode) {
- if (gbl_args->appl.in_mode == SCHED_ATOMIC)
- sync_mode = ODP_SCHED_SYNC_ATOMIC;
- else if (gbl_args->appl.in_mode == SCHED_ORDERED)
- sync_mode = ODP_SCHED_SYNC_ORDERED;
- else
- sync_mode = ODP_SCHED_SYNC_PARALLEL;
-
- pktin_param.queue_param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- pktin_param.queue_param.sched.sync = sync_mode;
- pktin_param.queue_param.sched.group = group;
- }
-
- if (num_rx > (int)capa.max_input_queues) {
- printf("Sharing %i input queues between %i workers\n",
- capa.max_input_queues, num_rx);
- num_rx = capa.max_input_queues;
- mode_rx = ODP_PKTIO_OP_MT;
- }
-
- if (num_tx > (int)capa.max_output_queues) {
- printf("Sharing %i output queues between %i workers\n",
- capa.max_output_queues, num_tx);
- num_tx = capa.max_output_queues;
- mode_tx = ODP_PKTIO_OP_MT;
- }
-
- pktin_param.hash_enable = 1;
- pktin_param.hash_proto.proto.ipv4_udp = 1;
- pktin_param.num_queues = num_rx;
- pktin_param.op_mode = mode_rx;
-
- pktout_param.op_mode = mode_tx;
- pktout_param.num_queues = num_tx;
-
- if (odp_pktin_queue_config(pktio, &pktin_param)) {
- LOG_ERR("Error: input queue config failed %s\n", dev);
- return -1;
- }
-
- if (odp_pktout_queue_config(pktio, &pktout_param)) {
- LOG_ERR("Error: output queue config failed %s\n", dev);
- return -1;
- }
-
- if (gbl_args->appl.in_mode == DIRECT_RECV) {
- if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin,
- num_rx) != num_rx) {
- LOG_ERR("Error: pktin queue query failed %s\n",
- dev);
- return -1;
- }
- } else {
- if (odp_pktin_event_queue(pktio,
- gbl_args->pktios[idx].rx_q,
- num_rx) != num_rx) {
- LOG_ERR("Error: pktin event queue query failed %s\n",
- dev);
- return -1;
- }
- }
-
- if (gbl_args->appl.out_mode == PKTOUT_DIRECT) {
- if (odp_pktout_queue(pktio,
- gbl_args->pktios[idx].pktout,
- num_tx) != num_tx) {
- LOG_ERR("Error: pktout queue query failed %s\n", dev);
- return -1;
- }
- } else {
- if (odp_pktout_event_queue(pktio,
- gbl_args->pktios[idx].tx_q,
- num_tx) != num_tx) {
- LOG_ERR("Error: event queue query failed %s\n", dev);
- return -1;
- }
- }
-
- printf("created %i input and %i output queues on (%s)\n",
- num_rx, num_tx, dev);
-
- gbl_args->pktios[idx].num_rx_queue = num_rx;
- gbl_args->pktios[idx].num_tx_queue = num_tx;
- gbl_args->pktios[idx].pktio = pktio;
-
- return 0;
-}
-
-/**
- * Print statistics
- *
- * @param num_workers Number of worker threads
- * @param thr_stats Pointer to stats storage
- * @param duration Number of seconds to loop in
- * @param timeout Number of seconds for stats calculation
- *
- */
-static int print_speed_stats(int num_workers, stats_t *thr_stats,
- int duration, int timeout)
-{
- uint64_t pkts = 0;
- uint64_t pkts_prev = 0;
- uint64_t pps;
- uint64_t rx_drops, tx_drops;
- uint64_t maximum_pps = 0;
- int i;
- int elapsed = 0;
- int stats_enabled = 1;
- int loop_forever = (duration == 0);
-
- if (timeout <= 0) {
- stats_enabled = 0;
- timeout = 1;
- }
- /* Wait for all threads to be ready*/
- odp_barrier_wait(&gbl_args->init_barrier);
-
- do {
- pkts = 0;
- rx_drops = 0;
- tx_drops = 0;
-
- sleep(timeout);
-
- for (i = 0; i < num_workers; i++) {
- pkts += thr_stats[i].s.packets;
- rx_drops += thr_stats[i].s.rx_drops;
- tx_drops += thr_stats[i].s.tx_drops;
- }
- if (stats_enabled) {
- pps = (pkts - pkts_prev) / timeout;
- if (pps > maximum_pps)
- maximum_pps = pps;
- printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
- maximum_pps);
-
- printf(" %" PRIu64 " rx drops, %" PRIu64 " tx drops\n",
- rx_drops, tx_drops);
-
- pkts_prev = pkts;
- }
- elapsed += timeout;
- } while (!exit_threads && (loop_forever || (elapsed < duration)));
-
- if (stats_enabled)
- printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
- maximum_pps);
-
- return pkts > 100 ? 0 : -1;
-}
-
-static void print_port_mapping(void)
-{
- int if_count;
- int pktio;
-
- if_count = gbl_args->appl.if_count;
-
- printf("\nPort config\n--------------------\n");
-
- for (pktio = 0; pktio < if_count; pktio++) {
- const char *dev = gbl_args->appl.if_names[pktio];
-
- printf("Port %i (%s)\n", pktio, dev);
- printf(" rx workers %i\n",
- gbl_args->pktios[pktio].num_rx_thr);
- printf(" tx workers %i\n",
- gbl_args->pktios[pktio].num_tx_thr);
- printf(" rx queues %i\n",
- gbl_args->pktios[pktio].num_rx_queue);
- printf(" tx queues %i\n",
- gbl_args->pktios[pktio].num_tx_queue);
- }
-
- printf("\n");
-}
-
-/**
- * Find the destination port for a given input port
- *
- * @param port Input port index
- */
-static int find_dest_port(int port)
-{
- /* Even number of ports */
- if (gbl_args->appl.if_count % 2 == 0)
- return (port % 2 == 0) ? port + 1 : port - 1;
-
- /* Odd number of ports */
- if (port == gbl_args->appl.if_count - 1)
- return 0;
- else
- return port + 1;
-}
-
-/*
- * Bind worker threads to interfaces and calculate number of queues needed
- *
- * less workers (N) than interfaces (M)
- * - assign each worker to process every Nth interface
- * - workers process inequal number of interfaces, when M is not divisible by N
- * - needs only single queue per interface
- * otherwise
- * - assign an interface to every Mth worker
- * - interfaces are processed by inequal number of workers, when N is not
- * divisible by M
- * - tries to configure a queue per worker per interface
- * - shares queues, if interface capability does not allows a queue per worker
- */
-static void bind_workers(void)
-{
- int if_count, num_workers;
- int rx_idx, tx_idx, thr, pktio, i;
- thread_args_t *thr_args;
-
- if_count = gbl_args->appl.if_count;
- num_workers = gbl_args->appl.num_workers;
-
- if (gbl_args->appl.sched_mode) {
- /* all threads receive and send on all pktios */
- for (i = 0; i < if_count; i++) {
- gbl_args->pktios[i].num_rx_thr = num_workers;
- gbl_args->pktios[i].num_tx_thr = num_workers;
- }
-
- for (thr = 0; thr < num_workers; thr++) {
- thr_args = &gbl_args->thread[thr];
- thr_args->num_pktio = if_count;
-
- /* In sched mode, pktios are not cross connected with
- * local pktio indexes */
- for (i = 0; i < if_count; i++) {
- thr_args->pktio[i].rx_idx = i;
- thr_args->pktio[i].tx_idx = i;
- }
- }
- } else {
- /* initialize port forwarding table */
- for (rx_idx = 0; rx_idx < if_count; rx_idx++)
- gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
-
- if (if_count > num_workers) {
- /* Less workers than pktios. Assign single worker per
- * pktio. */
- thr = 0;
-
- for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
- thr_args = &gbl_args->thread[thr];
- pktio = thr_args->num_pktio;
- /* Cross connect rx to tx */
- tx_idx = gbl_args->dst_port[rx_idx];
- thr_args->pktio[pktio].rx_idx = rx_idx;
- thr_args->pktio[pktio].tx_idx = tx_idx;
- thr_args->num_pktio++;
-
- gbl_args->pktios[rx_idx].num_rx_thr++;
- gbl_args->pktios[tx_idx].num_tx_thr++;
-
- thr++;
- if (thr >= num_workers)
- thr = 0;
- }
- } else {
- /* More workers than pktios. Assign at least one worker
- * per pktio. */
- rx_idx = 0;
-
- for (thr = 0; thr < num_workers; thr++) {
- thr_args = &gbl_args->thread[thr];
- pktio = thr_args->num_pktio;
- /* Cross connect rx to tx */
- tx_idx = gbl_args->dst_port[rx_idx];
- thr_args->pktio[pktio].rx_idx = rx_idx;
- thr_args->pktio[pktio].tx_idx = tx_idx;
- thr_args->num_pktio++;
-
- gbl_args->pktios[rx_idx].num_rx_thr++;
- gbl_args->pktios[tx_idx].num_tx_thr++;
-
- rx_idx++;
- if (rx_idx >= if_count)
- rx_idx = 0;
- }
- }
- }
-}
-
-/*
- * Bind queues to threads and fill in missing thread arguments (handles)
- */
-static void bind_queues(void)
-{
- int num_workers;
- int thr, i;
-
- num_workers = gbl_args->appl.num_workers;
-
- printf("\nQueue binding (indexes)\n-----------------------\n");
-
- for (thr = 0; thr < num_workers; thr++) {
- int rx_idx, tx_idx;
- thread_args_t *thr_args = &gbl_args->thread[thr];
- int num = thr_args->num_pktio;
-
- printf("worker %i\n", thr);
-
- for (i = 0; i < num; i++) {
- int rx_queue, tx_queue;
-
- rx_idx = thr_args->pktio[i].rx_idx;
- tx_idx = thr_args->pktio[i].tx_idx;
- rx_queue = gbl_args->pktios[rx_idx].next_rx_queue;
- tx_queue = gbl_args->pktios[tx_idx].next_tx_queue;
-
- thr_args->pktio[i].rx_queue_idx = rx_queue;
- thr_args->pktio[i].tx_queue_idx = tx_queue;
- thr_args->pktio[i].pktin =
- gbl_args->pktios[rx_idx].pktin[rx_queue];
- thr_args->pktio[i].rx_queue =
- gbl_args->pktios[rx_idx].rx_q[rx_queue];
- thr_args->pktio[i].pktout =
- gbl_args->pktios[tx_idx].pktout[tx_queue];
- thr_args->pktio[i].tx_queue =
- gbl_args->pktios[tx_idx].tx_q[tx_queue];
-
- if (!gbl_args->appl.sched_mode)
- printf(" rx: pktio %i, queue %i\n",
- rx_idx, rx_queue);
-
- printf(" tx: pktio %i, queue %i\n",
- tx_idx, tx_queue);
-
- rx_queue++;
- tx_queue++;
-
- if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue)
- rx_queue = 0;
- if (tx_queue >= gbl_args->pktios[tx_idx].num_tx_queue)
- tx_queue = 0;
-
- gbl_args->pktios[rx_idx].next_rx_queue = rx_queue;
- gbl_args->pktios[tx_idx].next_tx_queue = tx_queue;
- }
- }
-
- printf("\n");
-}
-
-static void init_port_lookup_tbl(void)
-{
- int rx_idx, if_count;
-
- if_count = gbl_args->appl.if_count;
-
- for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
- odp_pktio_t pktio = gbl_args->pktios[rx_idx].pktio;
- int pktio_idx = odp_pktio_index(pktio);
- int dst_port = find_dest_port(rx_idx);
-
- if (pktio_idx < 0 || pktio_idx >= MAX_PKTIO_INDEXES) {
- LOG_ERR("Bad pktio index %i\n", pktio_idx);
- exit(EXIT_FAILURE);
- }
-
- gbl_args->dst_port_from_idx[pktio_idx] = dst_port;
- }
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "OpenDataPlane L2 forwarding application.\n"
- "\n"
- "Usage: %s [options]\n"
- "\n"
- " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
- " In the above example,\n"
- " eth0 will send pkts to eth1 and vice versa\n"
- " eth2 will send pkts to eth3 and vice versa\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -i, --interface <name> Eth interfaces (comma-separated, no spaces)\n"
- " Interface count min 1, max %i\n"
- "\n"
- "Optional OPTIONS:\n"
- " -m, --mode <arg> Packet input mode\n"
- " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n"
- " 1: Scheduler mode with parallel queues:\n"
- " PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n"
- " 2: Scheduler mode with atomic queues:\n"
- " PKTIN_MODE_SCHED + SCHED_SYNC_ATOMIC\n"
- " 3: Scheduler mode with ordered queues:\n"
- " PKTIN_MODE_SCHED + SCHED_SYNC_ORDERED\n"
- " 4: Plain queue mode: PKTIN_MODE_QUEUE\n"
- " -o, --out_mode <arg> Packet output mode\n"
- " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n"
- " 1: Queue mode: PKTOUT_MODE_QUEUE\n"
- " -c, --count <num> CPU count.\n"
- " -t, --time <sec> Time in seconds to run.\n"
- " -a, --accuracy <sec> Time in seconds get print statistics\n"
- " (default is 1 second).\n"
- " -d, --dst_change <arg> 0: Don't change packets' dst eth addresses\n"
- " 1: Change packets' dst eth addresses (default)\n"
- " -s, --src_change <arg> 0: Don't change packets' src eth addresses\n"
- " 1: Change packets' src eth addresses (default)\n"
- " -r, --dst_addr <addr> Destination addresses (comma-separated, no spaces)\n"
- " Requires also the -d flag to be set\n"
- " -e, --error_check <arg> 0: Don't check packet errors (default)\n"
- " 1: Check packet errors\n"
- " -g, --groups <num> Number of groups to use: 0 ... num\n"
- " 0: SCHED_GROUP_ALL (default)\n"
- " num: must not exceed number of interfaces or workers\n"
- " -h, --help Display help and exit.\n\n"
- "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS
- );
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
- int opt;
- int long_index;
- char *token;
- char *addr_str;
- size_t len;
- int i;
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"time", required_argument, NULL, 't'},
- {"accuracy", required_argument, NULL, 'a'},
- {"interface", required_argument, NULL, 'i'},
- {"mode", required_argument, NULL, 'm'},
- {"out_mode", required_argument, NULL, 'o'},
- {"dst_addr", required_argument, NULL, 'r'},
- {"dst_change", required_argument, NULL, 'd'},
- {"src_change", required_argument, NULL, 's'},
- {"error_check", required_argument, NULL, 'e'},
- {"groups", required_argument, NULL, 'g'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:+t:+a:i:m:o:r:d:s:e:g:h";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- appl_args->time = 0; /* loop forever if time to run is 0 */
- appl_args->accuracy = 1; /* get and print pps stats second */
- appl_args->dst_change = 1; /* change eth dst address by default */
- appl_args->src_change = 1; /* change eth src address by default */
- appl_args->num_groups = 0; /* use default group */
- appl_args->error_check = 0; /* don't check packet errors by default */
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'c':
- appl_args->cpu_count = atoi(optarg);
- break;
- case 't':
- appl_args->time = atoi(optarg);
- break;
- case 'a':
- appl_args->accuracy = atoi(optarg);
- break;
- /* parse packet-io interface names */
- case 'r':
- len = strlen(optarg);
- if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- addr_str = malloc(len);
- if (addr_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* store the mac addresses names */
- strcpy(addr_str, optarg);
- for (token = strtok(addr_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- if (i >= MAX_PKTIOS) {
- printf("too many MAC addresses\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- if (odph_eth_addr_parse(&appl_args->addrs[i],
- token) != 0) {
- printf("invalid MAC address\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- }
- appl_args->addr_count = i;
- if (appl_args->addr_count < 1) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- free(addr_str);
- break;
- case 'i':
- len = strlen(optarg);
- if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- appl_args->if_str = malloc(len);
- if (appl_args->if_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* count the number of tokens separated by ',' */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL;
- token = strtok(NULL, ","), i++)
- ;
-
- appl_args->if_count = i;
-
- if (appl_args->if_count < 1 ||
- appl_args->if_count > MAX_PKTIOS) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* allocate storage for the if names */
- appl_args->if_names =
- calloc(appl_args->if_count, sizeof(char *));
-
- /* store the if names (reset names string) */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- appl_args->if_names[i] = token;
- }
- break;
- case 'm':
- i = atoi(optarg);
- if (i == 1)
- appl_args->in_mode = SCHED_PARALLEL;
- else if (i == 2)
- appl_args->in_mode = SCHED_ATOMIC;
- else if (i == 3)
- appl_args->in_mode = SCHED_ORDERED;
- else if (i == 4)
- appl_args->in_mode = PLAIN_QUEUE;
- else
- appl_args->in_mode = DIRECT_RECV;
- break;
- case 'o':
- i = atoi(optarg);
- if (i != 0)
- appl_args->out_mode = PKTOUT_QUEUE;
- break;
- case 'd':
- appl_args->dst_change = atoi(optarg);
- break;
- case 's':
- appl_args->src_change = atoi(optarg);
- break;
- case 'e':
- appl_args->error_check = atoi(optarg);
- break;
- case 'g':
- appl_args->num_groups = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
- default:
- break;
- }
- }
-
- if (appl_args->if_count == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- if (appl_args->addr_count != 0 &&
- appl_args->addr_count != appl_args->if_count) {
- printf("Number of destination addresses differs from number"
- " of interfaces\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args)
-{
- int i;
-
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_version_impl_name(),
- odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n"
- "IF-count: %i\n"
- "Using IFs: ",
- progname, appl_args->if_count);
- for (i = 0; i < appl_args->if_count; ++i)
- printf(" %s", appl_args->if_names[i]);
- printf("\n"
- "Mode: ");
- if (appl_args->in_mode == DIRECT_RECV)
- printf("PKTIN_DIRECT, ");
- else if (appl_args->in_mode == PLAIN_QUEUE)
- printf("PKTIN_QUEUE, ");
- else if (appl_args->in_mode == SCHED_PARALLEL)
- printf("PKTIN_SCHED_PARALLEL, ");
- else if (appl_args->in_mode == SCHED_ATOMIC)
- printf("PKTIN_SCHED_ATOMIC, ");
- else if (appl_args->in_mode == SCHED_ORDERED)
- printf("PKTIN_SCHED_ORDERED, ");
-
- if (appl_args->out_mode)
- printf("PKTOUT_QUEUE");
- else
- printf("PKTOUT_DIRECT");
-
- printf("\n\n");
- fflush(NULL);
-}
-
-static void gbl_args_init(args_t *args)
-{
- int pktio, queue;
-
- memset(args, 0, sizeof(args_t));
-
- for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
- args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
-
- for (queue = 0; queue < MAX_QUEUES; queue++)
- args->pktios[pktio].rx_q[queue] = ODP_QUEUE_INVALID;
- }
-}
-
-static void create_groups(int num, odp_schedule_group_t *group)
-{
- int i;
- odp_thrmask_t zero;
-
- odp_thrmask_zero(&zero);
-
- /* Create groups */
- for (i = 0; i < num; i++) {
- group[i] = odp_schedule_group_create(NULL, &zero);
-
- if (group[i] == ODP_SCHED_GROUP_INVALID) {
- LOG_ERR("Group create failed\n");
- exit(EXIT_FAILURE);
- }
- }
-}
-
-/**
- * ODP L2 forwarding main function
- */
-int main(int argc, char *argv[])
-{
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- odp_pool_t pool;
- int i;
- int cpu;
- int num_workers;
- odp_shm_t shm;
- odp_cpumask_t cpumask;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odph_ethaddr_t new_addr;
- odp_pool_param_t params;
- int ret;
- stats_t *stats;
- int if_count;
- int (*thr_run_func)(void *);
- odp_instance_t instance;
- int num_groups;
- odp_schedule_group_t group[MAX_PKTIOS];
-
- /* Signal handler has to be registered before global init in case ODP
- * implementation creates internal threads/processes. */
- signal(SIGINT, sig_handler);
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Reserve memory for args from shared mem */
- shm = odp_shm_reserve("shm_args", sizeof(args_t),
- ODP_CACHE_LINE_SIZE, 0);
- gbl_args = odp_shm_addr(shm);
-
- if (gbl_args == NULL) {
- LOG_ERR("Error: shared mem alloc failed.\n");
- exit(EXIT_FAILURE);
- }
- gbl_args_init(gbl_args);
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &gbl_args->appl);
-
- if (sched_mode(gbl_args->appl.in_mode))
- gbl_args->appl.sched_mode = 1;
-
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]), &gbl_args->appl);
-
- /* Default to system CPU count unless user specified */
- num_workers = MAX_WORKERS;
- if (gbl_args->appl.cpu_count)
- num_workers = gbl_args->appl.cpu_count;
-
- /* Get default worker cpumask */
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- gbl_args->appl.num_workers = num_workers;
-
- for (i = 0; i < num_workers; i++)
- gbl_args->thread[i].thr_idx = i;
-
- if_count = gbl_args->appl.if_count;
-
- num_groups = gbl_args->appl.num_groups;
-
- printf("num worker threads: %i\n", num_workers);
- printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n", cpumaskstr);
-
- if (num_groups)
- printf("num groups: %i\n", num_groups);
-
- printf("\n");
-
- if (num_groups > if_count || num_groups > num_workers) {
- LOG_ERR("Too many groups. Number of groups may not exceed "
- "number of interfaces or workers.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_SIZE;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create("packet pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- LOG_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
- odp_pool_print(pool);
-
- if (odp_pktio_max_index() >= MAX_PKTIO_INDEXES)
- LOG_DBG("Warning: max pktio index (%u) is too large\n",
- odp_pktio_max_index());
-
- bind_workers();
-
- /* Default */
- if (num_groups == 0) {
- group[0] = ODP_SCHED_GROUP_ALL;
- num_groups = 1;
- } else {
- create_groups(num_groups, group);
- }
-
- for (i = 0; i < if_count; ++i) {
- const char *dev = gbl_args->appl.if_names[i];
- int num_rx, num_tx;
- odp_schedule_group_t grp;
-
- /* A queue per worker in scheduled mode */
- num_rx = num_workers;
- num_tx = num_workers;
-
- if (!gbl_args->appl.sched_mode) {
- /* A queue per assigned worker */
- num_rx = gbl_args->pktios[i].num_rx_thr;
- num_tx = gbl_args->pktios[i].num_tx_thr;
- }
-
- /* Round robin pktios to groups */
- grp = group[i % num_groups];
-
- if (create_pktio(dev, i, num_rx, num_tx, pool, grp))
- exit(EXIT_FAILURE);
-
- /* Save interface ethernet address */
- if (odp_pktio_mac_addr(gbl_args->pktios[i].pktio,
- gbl_args->port_eth_addr[i].addr,
- ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
- LOG_ERR("Error: interface ethernet address unknown\n");
- exit(EXIT_FAILURE);
- }
-
- /* Save destination eth address */
- if (gbl_args->appl.dst_change) {
- /* 02:00:00:00:00:XX */
- memset(&new_addr, 0, sizeof(odph_ethaddr_t));
- if (gbl_args->appl.addr_count) {
- memcpy(&new_addr, &gbl_args->appl.addrs[i],
- sizeof(odph_ethaddr_t));
- } else {
- new_addr.addr[0] = 0x02;
- new_addr.addr[5] = i;
- }
- gbl_args->dst_eth_addr[i] = new_addr;
- }
- }
-
- gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
-
- bind_queues();
-
- init_port_lookup_tbl();
-
- if (!gbl_args->appl.sched_mode)
- print_port_mapping();
-
- memset(thread_tbl, 0, sizeof(thread_tbl));
-
- stats = gbl_args->stats;
-
- odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
- odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
-
- if (gbl_args->appl.in_mode == DIRECT_RECV)
- thr_run_func = run_worker_direct_mode;
- else if (gbl_args->appl.in_mode == PLAIN_QUEUE)
- thr_run_func = run_worker_plain_queue_mode;
- else /* SCHED_PARALLEL / SCHED_ATOMIC / SCHED_ORDERED */
- thr_run_func = run_worker_sched_mode;
-
- /* Create worker threads */
- cpu = odp_cpumask_first(&cpumask);
- for (i = 0; i < num_workers; ++i) {
- odp_cpumask_t thd_mask;
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = thr_run_func;
- thr_params.arg = &gbl_args->thread[i];
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- /* Round robin threads to groups */
- gbl_args->thread[i].num_groups = 1;
- gbl_args->thread[i].group[0] = group[i % num_groups];
-
- gbl_args->thread[i].stats = &stats[i];
-
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
- odph_odpthreads_create(&thread_tbl[i], &thd_mask,
- &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
- }
-
- /* Start packet receive and transmit */
- for (i = 0; i < if_count; ++i) {
- odp_pktio_t pktio;
-
- pktio = gbl_args->pktios[i].pktio;
- ret = odp_pktio_start(pktio);
- if (ret) {
- LOG_ERR("Error: unable to start %s\n",
- gbl_args->appl.if_names[i]);
- exit(EXIT_FAILURE);
- }
- }
-
- ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
- gbl_args->appl.accuracy);
-
- for (i = 0; i < if_count; ++i) {
- if (odp_pktio_stop(gbl_args->pktios[i].pktio)) {
- LOG_ERR("Error: unable to stop %s\n",
- gbl_args->appl.if_names[i]);
- exit(EXIT_FAILURE);
- }
- }
-
- exit_threads = 1;
- if (gbl_args->appl.in_mode != DIRECT_RECV)
- odp_barrier_wait(&gbl_args->term_barrier);
-
- /* Master thread waits for other threads to exit */
- for (i = 0; i < num_workers; ++i)
- odph_odpthreads_join(&thread_tbl[i]);
-
- for (i = 0; i < if_count; ++i) {
- if (odp_pktio_close(gbl_args->pktios[i].pktio)) {
- LOG_ERR("Error: unable to close %s\n",
- gbl_args->appl.if_names[i]);
- exit(EXIT_FAILURE);
- }
- }
-
- free(gbl_args->appl.if_names);
- free(gbl_args->appl.if_str);
-
- if (odp_pool_destroy(pool)) {
- LOG_ERR("Error: pool destroy\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_shm_free(shm)) {
- LOG_ERR("Error: shm free\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- LOG_ERR("Error: term local\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- LOG_ERR("Error: term global\n");
- exit(EXIT_FAILURE);
- }
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_l2fwd_run.sh b/test/common_plat/performance/odp_l2fwd_run.sh
deleted file mode 100755
index 6871e4b07..000000000
--- a/test/common_plat/performance/odp_l2fwd_run.sh
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# TEST_DIR is set by Makefile, when we add a rule to Makefile for odp_l2fwd_run
-# we can use TEST_DIR the same way odp_pktio_run uses it now.
-# If TEST_DIR is not set it means we are not running with make, and in this case
-# there are two situations:
-# 1. user build ODP in the same dir as the source (most likely)
-# here the user can simply call odp_l2fwd_run
-# 2. user may have built ODP in a separate build dir (like bitbake usually does)
-# here the user has to do something like $ODP/test/performance/odp_l2fwd_run
-#
-# In both situations the script assumes that the user is in the directory where
-# odp_l2fwd exists. If that's not true, then the user has to specify the path
-# to it and run:
-# TEST_DIR=$builddir $ODP/test/performance/odp_l2fwd_run
-
-# directory where test binaries have been built
-TEST_DIR="${TEST_DIR:-$PWD}"
-# directory where test sources are, including scripts
-TEST_SRC_DIR=$(dirname $0)
-
-PATH=$TEST_DIR:$TEST_DIR/../../../example/generator:$PATH
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-PLATFORM_VALIDATION=${TEST_SRC_DIR}/../../$ODP_PLATFORM/validation
-
-FLOOD_MODE=0
-
-# Use installed pktio env or for make check take it from platform directory
-if [ -f "./pktio_env" ]; then
- . ./pktio_env
-elif [ "$ODP_PLATFORM" = "" ]; then
- echo "$0: error: ODP_PLATFORM must be defined"
- # not skipped as this should never happen via "make check"
- exit 1
-elif [ -f ${PLATFORM_VALIDATION}/api/pktio/pktio_env ]; then
- . ${PLATFORM_VALIDATION}/api/pktio/pktio_env
-else
- echo "BUG: unable to find pktio_env!"
- echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
- echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
- exit 1
-fi
-
-run_l2fwd()
-{
- setup_pktio_env clean # install trap to call cleanup_pktio_env
-
- if [ $? -ne 0 ]; then
- echo "setup_pktio_env error $?"
- exit $TEST_SKIPPED
- fi
-
- type odp_generator > /dev/null
- if [ $? -ne 0 ]; then
- echo "odp_generator not installed. Aborting."
- cleanup_pktio_env
- exit 1
- fi
-
- # Max 4 workers
- # @todo: ensure that generator and l2fwd workers are not allocated to
- # the same CPUs
- (odp_generator${EXEEXT} --interval $FLOOD_MODE -I $IF0 \
- --srcip 192.168.0.1 --dstip 192.168.0.2 \
- -m u -w 4 2>&1 > /dev/null) \
- 2>&1 > /dev/null &
-
- GEN_PID=$!
-
- # this just turns off output buffering so that you still get periodic
- # output while piping to tee, as long as stdbuf is available.
- if [ "$(which stdbuf)" != "" ]; then
- STDBUF="stdbuf -o 0"
- else
- STDBUF=
- fi
- LOG=odp_l2fwd_tmp.log
-
- # Max 2 workers
- $STDBUF odp_l2fwd${EXEEXT} -i $IF1,$IF2 -m 0 -t 30 -c 2 | tee $LOG
- ret=$?
-
- kill ${GEN_PID}
-
- if [ ! -f $LOG ]; then
- echo "FAIL: $LOG not found"
- ret=1
- elif [ $ret -eq 0 ]; then
- PASS_PPS=5000
- if [ "${TEST}" = "coverage" ]; then
- PASS_PPS=10
- fi
- MAX_PPS=$(awk '/TEST RESULT/ {print $3}' $LOG)
- if [ "$MAX_PPS" -lt "$PASS_PPS" ]; then
- echo "FAIL: pps below threshold $MAX_PPS < $PASS_PPS"
- ret=1
- fi
- fi
-
- rm -f $LOG
- cleanup_pktio_env
-
- exit $ret
-}
-
-case "$1" in
- setup) setup_pktio_env ;;
- cleanup) cleanup_pktio_env ;;
- *) run_l2fwd ;;
-esac
diff --git a/test/common_plat/performance/odp_pktio_ordered.c b/test/common_plat/performance/odp_pktio_ordered.c
deleted file mode 100644
index 4bb0bef93..000000000
--- a/test/common_plat/performance/odp_pktio_ordered.c
+++ /dev/null
@@ -1,1347 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_pktio_ordered.c ODP ordered pktio test application
- */
-
-/** enable strtok */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <test_debug.h>
-#include <dummy_crc.h>
-
-#include <odp_api.h>
-#include <odp/helper/threads.h>
-#include <odp/helper/eth.h>
-#include <odp/helper/ip.h>
-#include <odp/helper/udp.h>
-
-/** Jenkins hash support.
- *
- * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)
- *
- * http://burtleburtle.net/bob/hash/
- *
- * These are the credits from Bob's sources:
- *
- * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
- *
- * These are functions for producing 32-bit hashes for hash table lookup.
- * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
- * are externally useful functions. Routines to test the hash are included
- * if SELF_TEST is defined. You can use this free for any purpose. It's in
- * the public domain. It has no warranty.
- *
- * $FreeBSD$
- */
-#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
-
-#define mix(a, b, c) \
-{ \
- a -= c; a ^= rot(c, 4); c += b; \
- b -= a; b ^= rot(a, 6); a += c; \
- c -= b; c ^= rot(b, 8); b += a; \
- a -= c; a ^= rot(c, 16); c += b; \
- b -= a; b ^= rot(a, 19); a += c; \
- c -= b; c ^= rot(b, 4); b += a; \
-}
-
-#define final(a, b, c) \
-{ \
- c ^= b; c -= rot(b, 14); \
- a ^= c; a -= rot(c, 11); \
- b ^= a; b -= rot(a, 25); \
- c ^= b; c -= rot(b, 16); \
- a ^= c; a -= rot(c, 4); \
- b ^= a; b -= rot(a, 14); \
- c ^= b; c -= rot(b, 24); \
-}
-
-#define JHASH_GOLDEN_RATIO 0x9e3779b9
-
-/** Maximum number of worker threads */
-#define MAX_WORKERS 64
-
-/** Number of packet buffers in the memory pool */
-#define PKT_POOL_SIZE 8192
-
-/** Buffer size of the packet pool buffer in bytes*/
-#define PKT_POOL_BUF_SIZE 1856
-
-/** Packet user area size in bytes */
-#define PKT_UAREA_SIZE 32
-
-/** Maximum number of packets in a burst */
-#define MAX_PKT_BURST 32
-
-/** Maximum number of pktio queues per interface */
-#define MAX_QUEUES 32
-
-/** Maximum number of pktio interfaces */
-#define MAX_PKTIOS 8
-
-/** Maximum number of packet flows */
-#define MAX_FLOWS 128
-
-ODP_STATIC_ASSERT(MAX_PKTIOS < MAX_FLOWS,
- "MAX_FLOWS must be greater than MAX_PKTIOS\n");
-
-/** Minimum valid packet length */
-#define MIN_PACKET_LEN (ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN + ODPH_UDPHDR_LEN)
-
-/** Default number of input queues */
-#define DEF_NUM_RX_QUEUES 1
-
-/** Default number of flows */
-#define DEF_NUM_FLOWS 12
-
-/** Default number of extra processing rounds */
-#define DEF_EXTRA_ROUNDS 15
-
-/** Default statistics print interval in seconds */
-#define DEF_STATS_INT 1
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-
-/**
- * Packet input mode
- */
-typedef enum pktin_mode_t {
- SCHED_ORDERED = 0,
- SCHED_ATOMIC,
- SCHED_PARALLEL
-} pktin_mode_t;
-
-/**
- * Parsed command line application arguments
- */
-typedef struct {
- int cpu_count; /**< CPU count */
- int if_count; /**< Number of interfaces to be used */
- int addr_count; /**< Number of dst addresses to be used */
- int num_rx_q; /**< Number of input queues per interface */
- int num_flows; /**< Number of packet flows */
- int extra_rounds; /**< Number of extra input processing rounds */
- char **if_names; /**< Array of pointers to interface names */
- odph_ethaddr_t addrs[MAX_PKTIOS]; /**< Array of dst addresses */
- pktin_mode_t in_mode; /**< Packet input mode */
- int time; /**< Time in seconds to run. */
- int accuracy; /**< Statistics print interval */
- char *if_str; /**< Storage for interface names */
-} appl_args_t;
-
-static int exit_threads; /**< Break workers loop if set to 1 */
-
-/**
- * Queue context
- */
-typedef struct {
- odp_bool_t input_queue; /**< Input queue */
- uint64_t idx; /**< Queue index */
- uint64_t seq[MAX_FLOWS]; /**< Per flow sequence numbers */
-} qcontext_t;
-
-/**
- * Flow info stored in the packet user area
- */
-typedef struct {
- uint64_t seq; /**< Sequence number */
- uint32_t crc; /**< CRC hash */
- uint16_t idx; /**< Flow index */
- uint8_t src_idx; /**< Source port index */
- uint8_t dst_idx; /**< Destination port index */
-
-} flow_t;
-ODP_STATIC_ASSERT(sizeof(flow_t) <= PKT_UAREA_SIZE,
- "Flow data doesn't fit in the packet user area\n");
-
-/**
- * Statistics
- */
-typedef union {
- struct {
- /** Number of forwarded packets */
- uint64_t packets;
- /** Packets dropped due to a receive error */
- uint64_t rx_drops;
- /** Packets dropped due to a transmit error */
- uint64_t tx_drops;
- /** Packets with invalid sequence number */
- uint64_t invalid_seq;
- } s;
-
- uint8_t padding[ODP_CACHE_LINE_SIZE];
-} stats_t ODP_ALIGNED_CACHE;
-
-/**
- * IPv4 5-tuple
- */
-typedef struct {
- int32_t src_ip;
- int32_t dst_ip;
- int16_t src_port;
- int16_t dst_port;
- int8_t proto;
- int8_t pad0;
- int16_t pad1;
-} ipv4_tuple5_t;
-
-/**
- * Packet headers
- */
-typedef struct {
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ipv4;
- odph_udphdr_t *udp;
-} packet_hdr_t;
-
-/**
- * Thread specific arguments
- */
-typedef struct thread_args_t {
- stats_t *stats; /**< Pointer to per thread statistics */
-} thread_args_t;
-
-/**
- * Grouping of all global data
- */
-typedef struct {
- /** Per thread packet stats */
- stats_t stats[MAX_WORKERS];
- /** Application (parsed) arguments */
- appl_args_t appl;
- /** Thread specific arguments */
- thread_args_t thread[MAX_WORKERS];
- /** Table of port ethernet addresses */
- odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
- /** Table of dst ethernet addresses */
- odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
- /** Table of dst ports */
- int dst_port[MAX_PKTIOS];
- /** Table of atomic queues for flows */
- odp_queue_t fqueue[MAX_PKTIOS][MAX_FLOWS];
- /** Table of flow queue contexts */
- qcontext_t flow_qcontext[MAX_PKTIOS][MAX_FLOWS];
- /** Table of input queue contexts */
- qcontext_t input_qcontext[MAX_PKTIOS][MAX_QUEUES];
- /** Table of pktio handles */
- struct {
- odp_pktio_t pktio;
- odp_pktout_queue_t pktout[MAX_FLOWS];
- odp_queue_t pktin[MAX_QUEUES];
- int num_rx_queue;
- int num_tx_queue;
- } pktios[MAX_PKTIOS];
-} args_t;
-
-/** Global pointer to args */
-static args_t *gbl_args;
-
-/** Global barrier to synchronize main and workers */
-static odp_barrier_t barrier;
-
-/**
- * Lookup the destination port for a given packet
- *
- * @param pkt ODP packet handle
- */
-static inline int lookup_dest_port(odp_packet_t pkt)
-{
- int i, src_idx;
- odp_pktio_t pktio_src;
-
- pktio_src = odp_packet_input(pkt);
-
- for (src_idx = -1, i = 0; gbl_args->pktios[i].pktio
- != ODP_PKTIO_INVALID; i++)
- if (gbl_args->pktios[i].pktio == pktio_src)
- src_idx = i;
-
- if (src_idx == -1)
- LOG_ABORT("Failed to determine pktio input\n");
-
- return gbl_args->dst_port[src_idx];
-}
-
-/**
- * Map required packet headers
- *
- * @param pkt Packet handle
- * @param hdr[out] Packet headers
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static inline int packet_hdr(odp_packet_t pkt, packet_hdr_t *hdr)
-{
- uint8_t *udp;
- uint16_t eth_type;
- uint8_t ihl;
-
- if (odp_unlikely(odp_packet_seg_len(pkt) < MIN_PACKET_LEN))
- return -1;
-
- if (odp_unlikely(!odp_packet_has_eth(pkt)))
- return -1;
-
- hdr->eth = odp_packet_l2_ptr(pkt, NULL);
- eth_type = odp_be_to_cpu_16(hdr->eth->type);
- if (odp_unlikely(eth_type != ODPH_ETHTYPE_IPV4))
- return -1;
-
- hdr->ipv4 = (odph_ipv4hdr_t *)(hdr->eth + 1);
- if (odp_unlikely(hdr->ipv4->proto != ODPH_IPPROTO_UDP))
- return -1;
-
- ihl = ODPH_IPV4HDR_IHL(hdr->ipv4->ver_ihl);
- if (odp_unlikely(ihl < ODPH_IPV4HDR_IHL_MIN))
- return -1;
-
- udp = (uint8_t *)hdr->ipv4 + (ihl * 4);
-
- hdr->udp = (odph_udphdr_t *)udp;
-
- return 0;
-}
-
-/**
- * Compute hash from a 5-tuple
- *
- * @param key IPv4 5-tuple
- *
- * @return 32-bit hash value
- */
-static inline uint64_t calc_ipv4_5tuple_hash(ipv4_tuple5_t *tuple)
-{
- uint32_t a, b, c;
-
- a = tuple->proto + JHASH_GOLDEN_RATIO;
- b = tuple->src_ip + JHASH_GOLDEN_RATIO;
- c = tuple->dst_ip + JHASH_GOLDEN_RATIO;
-
- mix(a, b, c);
-
- a += (tuple->src_port << 16) + tuple->dst_port + JHASH_GOLDEN_RATIO;
- final(a, b, c);
-
- return c;
-}
-
-/**
- * Compute packet flow index
- *
- * @param hdr Packet headers
- *
- * @return Flow index
- */
-static inline uint64_t calc_flow_idx(packet_hdr_t *hdr)
-{
- ipv4_tuple5_t tuple;
- uint64_t idx;
-
- tuple.dst_ip = odp_be_to_cpu_32(hdr->ipv4->dst_addr);
- tuple.src_ip = odp_be_to_cpu_32(hdr->ipv4->src_addr);
- tuple.proto = hdr->ipv4->proto;
- tuple.src_port = odp_be_to_cpu_16(hdr->udp->src_port);
- tuple.dst_port = odp_be_to_cpu_16(hdr->udp->dst_port);
- tuple.pad0 = 0;
- tuple.pad1 = 0;
- idx = calc_ipv4_5tuple_hash(&tuple);
-
- return idx % gbl_args->appl.num_flows;
-}
-
-/**
- * Fill packet's eth addresses according to the destination port
- *
- * @param hdr[out] Packet headers
- * @param dst_port Destination port
- */
-static inline void fill_eth_addrs(packet_hdr_t *hdr, int dst_port)
-{
- hdr->eth->src = gbl_args->port_eth_addr[dst_port];
- hdr->eth->dst = gbl_args->dst_eth_addr[dst_port];
-}
-
-/**
- * Process flow queue
- *
- * @param ev_tbl Array of events
- * @param num Number of events in the array
- * @param stats Pointer for storing thread statistics
- * @param qcontext Source queue context
- * @param pktout Arrays of output queues
- */
-static inline void process_flow(odp_event_t ev_tbl[], int num, stats_t *stats,
- qcontext_t *qcontext,
- odp_pktout_queue_t pktout[][MAX_FLOWS])
-{
- odp_packet_t pkt;
- flow_t *flow;
- uint64_t queue_seq;
- int dst_if;
- int i;
- int sent;
-
- for (i = 0; i < num; i++) {
- pkt = odp_packet_from_event(ev_tbl[i]);
-
- flow = odp_packet_user_area(pkt);
-
- queue_seq = qcontext->seq[flow->src_idx];
-
- /* Check sequence number */
- if (gbl_args->appl.in_mode != SCHED_PARALLEL &&
- odp_unlikely(flow->seq != queue_seq)) {
- printf("Invalid sequence number: packet_seq=%" PRIu64 ""
- " queue_seq=%" PRIu64 ", src_if=%" PRIu8 ", "
- "dst_if=%" PRIu8 ", flow=%" PRIu16 "\n",
- flow->seq, queue_seq, flow->src_idx,
- flow->dst_idx, flow->idx);
- qcontext->seq[flow->src_idx] = flow->seq + 1;
- stats->s.invalid_seq++;
- } else {
- qcontext->seq[flow->src_idx]++;
- }
-
- dst_if = flow->dst_idx;
- sent = odp_pktout_send(pktout[dst_if][flow->idx], &pkt, 1);
-
- if (odp_unlikely(sent != 1)) {
- stats->s.tx_drops++;
- odp_packet_free(pkt);
- }
- stats->s.packets++;
- }
-}
-
-/**
- * Process input queue
- *
- * @param ev_tbl Array of events
- * @param num Number of events in the array
- * @param stats Pointer for storing thread statistics
- * @param qcontext Source queue context
- */
-static inline void process_input(odp_event_t ev_tbl[], int num, stats_t *stats,
- qcontext_t *qcontext)
-{
- flow_t *flow;
- flow_t *flow_tbl[MAX_PKT_BURST];
- int ret;
- int i, j;
- int pkts = 0;
-
- for (i = 0; i < num; i++) {
- odp_packet_t pkt;
- packet_hdr_t hdr;
- int flow_idx;
-
- pkt = odp_packet_from_event(ev_tbl[i]);
-
- odp_packet_prefetch(pkt, 0, MIN_PACKET_LEN);
-
- ret = packet_hdr(pkt, &hdr);
- if (odp_unlikely(ret)) {
- odp_packet_free(pkt);
- stats->s.rx_drops++;
- continue;
- }
-
- flow_idx = calc_flow_idx(&hdr);
-
- fill_eth_addrs(&hdr, flow_idx);
-
- flow = odp_packet_user_area(pkt);
- flow->idx = flow_idx;
- flow->src_idx = qcontext->idx;
- flow->dst_idx = lookup_dest_port(pkt);
- flow_tbl[pkts] = flow;
-
- /* Simulate "fat pipe" processing by generating extra work */
- for (j = 0; j < gbl_args->appl.extra_rounds; j++)
- flow->crc = dummy_hash_crc32c(odp_packet_data(pkt),
- odp_packet_len(pkt), 0);
- pkts++;
- }
-
- if (odp_unlikely(!pkts))
- return;
-
- /* Set sequence numbers */
- if (gbl_args->appl.in_mode == SCHED_ORDERED)
- odp_schedule_order_lock(0);
-
- for (i = 0; i < pkts; i++) {
- flow = flow_tbl[i];
- flow->seq = qcontext->seq[flow->idx]++;
- }
-
- if (gbl_args->appl.in_mode == SCHED_ORDERED)
- odp_schedule_order_unlock(0);
-
- for (i = 0; i < pkts; i++) {
- flow = flow_tbl[i];
- ret = odp_queue_enq(gbl_args->fqueue[flow->dst_idx][flow->idx],
- ev_tbl[i]);
-
- if (odp_unlikely(ret != 0)) {
- LOG_ERR("odp_queue_enq() failed\n");
- stats->s.tx_drops++;
- odp_event_free(ev_tbl[i]);
- } else {
- stats->s.packets++;
- }
- }
-}
-
-/**
- * Worker thread
- *
- * @param arg Thread arguments of type 'thread_args_t *'
- */
-static int run_worker(void *arg)
-{
- odp_event_t ev_tbl[MAX_PKT_BURST];
- odp_queue_t queue;
- odp_pktout_queue_t pktout[MAX_PKTIOS][MAX_FLOWS];
- qcontext_t *qcontext;
- thread_args_t *thr_args = arg;
- stats_t *stats = thr_args->stats;
- int pkts;
- int i, j;
-
- memset(pktout, 0, sizeof(pktout));
-
- for (i = 0; i < gbl_args->appl.if_count; i++) {
- for (j = 0; j < gbl_args->appl.num_flows; j++) {
- pktout[i][j] = gbl_args->pktios[i].pktout[j %
- gbl_args->pktios[i].num_tx_queue];
- }
- }
- odp_barrier_wait(&barrier);
-
- /* Loop packets */
- while (!exit_threads) {
- pkts = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, ev_tbl,
- MAX_PKT_BURST);
- if (pkts <= 0)
- continue;
-
- qcontext = odp_queue_context(queue);
-
- if (qcontext->input_queue)
- process_input(ev_tbl, pkts, stats, qcontext);
- else
- process_flow(ev_tbl, pkts, stats, qcontext, pktout);
- }
-
- /* Free remaining events in queues */
- while (1) {
- odp_event_t ev;
-
- ev = odp_schedule(NULL,
- odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- odp_event_free(ev);
- }
-
- return 0;
-}
-
-/**
- * Create a pktio handle and associate with input queues
- *
- * @param dev Name of device to open
- * @param index Pktio index
- * @param num_rx Number of input queues
- * @param num_tx Number of output queues
- * @param pool Pool to associate with device for packet RX/TX
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
- odp_pool_t pool)
-{
- odp_pktio_t pktio;
- odp_pktio_param_t pktio_param;
- odp_pktio_capability_t capa;
- odp_pktio_config_t config;
- odp_pktin_queue_param_t pktin_param;
- odp_pktout_queue_param_t pktout_param;
- odp_pktio_op_mode_t mode_rx;
- odp_pktio_op_mode_t mode_tx;
- int i;
-
- odp_pktio_param_init(&pktio_param);
-
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open(dev, pool, &pktio_param);
- if (pktio == ODP_PKTIO_INVALID) {
- LOG_ERR("Error: failed to open %s\n", dev);
- return -1;
- }
-
- printf("Created pktio %" PRIu64 " (%s)\n",
- odp_pktio_to_u64(pktio), dev);
-
- if (odp_pktio_capability(pktio, &capa)) {
- LOG_ERR("Error: capability query failed %s\n", dev);
- odp_pktio_close(pktio);
- return -1;
- }
-
- odp_pktio_config_init(&config);
- config.parser.layer = ODP_PKTIO_PARSER_LAYER_L2;
- odp_pktio_config(pktio, &config);
-
- odp_pktin_queue_param_init(&pktin_param);
- odp_pktout_queue_param_init(&pktout_param);
-
- mode_tx = ODP_PKTIO_OP_MT;
- mode_rx = ODP_PKTIO_OP_MT;
-
- if (gbl_args->appl.in_mode == SCHED_ATOMIC) {
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- } else if (gbl_args->appl.in_mode == SCHED_PARALLEL) {
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- } else {
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
- pktin_param.queue_param.sched.lock_count = 1;
- }
- pktin_param.queue_param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
-
- if (num_rx > (int)capa.max_input_queues) {
- printf("Allocating %i shared input queues, %i requested\n",
- capa.max_input_queues, num_rx);
- num_rx = capa.max_input_queues;
- mode_rx = ODP_PKTIO_OP_MT;
- }
-
- if (num_tx > (int)capa.max_output_queues) {
- printf("Allocating %i shared output queues, %i requested\n",
- capa.max_output_queues, num_tx);
- num_tx = capa.max_output_queues;
- mode_tx = ODP_PKTIO_OP_MT;
- }
-
- pktin_param.hash_enable = 1;
- pktin_param.hash_proto.proto.ipv4_udp = 1;
- pktin_param.num_queues = num_rx;
- pktin_param.op_mode = mode_rx;
-
- pktout_param.op_mode = mode_tx;
- pktout_param.num_queues = num_tx;
-
- if (odp_pktin_queue_config(pktio, &pktin_param)) {
- LOG_ERR("Error: input queue config failed %s\n", dev);
- return -1;
- }
-
- if (odp_pktout_queue_config(pktio, &pktout_param)) {
- LOG_ERR("Error: output queue config failed %s\n", dev);
- return -1;
- }
-
- if (odp_pktin_event_queue(pktio, gbl_args->pktios[idx].pktin,
- num_rx) != num_rx) {
- LOG_ERR("Error: pktin event queue query failed %s\n",
- dev);
- return -1;
- }
-
- /* Set queue contexts */
- for (i = 0; i < num_rx; i++) {
- gbl_args->input_qcontext[idx][i].idx = idx;
- gbl_args->input_qcontext[idx][i].input_queue = 1;
-
- if (odp_queue_context_set(gbl_args->pktios[idx].pktin[i],
- &gbl_args->input_qcontext[idx][i],
- sizeof(qcontext_t))) {
- LOG_ERR("Error: pktin queue context set failed %s\n",
- dev);
- return -1;
- }
- }
-
- if (odp_pktout_queue(pktio,
- gbl_args->pktios[idx].pktout,
- num_tx) != num_tx) {
- LOG_ERR("Error: pktout queue query failed %s\n", dev);
- return -1;
- }
-
- printf("Created %i input and %i output queues on (%s)\n",
- num_rx, num_tx, dev);
-
- gbl_args->pktios[idx].num_rx_queue = num_rx;
- gbl_args->pktios[idx].num_tx_queue = num_tx;
- gbl_args->pktios[idx].pktio = pktio;
-
- return 0;
-}
-
-/**
- * Print statistics
- *
- * @param num_workers Number of worker threads
- * @param thr_stats Pointer to stats storage
- * @param duration Number of seconds to loop in
- * @param timeout Number of seconds for stats calculation
- *
- */
-static int print_speed_stats(int num_workers, stats_t *thr_stats,
- int duration, int timeout)
-{
- uint64_t pkts = 0;
- uint64_t pkts_prev = 0;
- uint64_t pps;
- uint64_t rx_drops, tx_drops, invalid_seq;
- uint64_t maximum_pps = 0;
- int i;
- int elapsed = 0;
- int stats_enabled = 1;
- int loop_forever = (duration == 0);
-
- if (timeout <= 0) {
- stats_enabled = 0;
- timeout = 1;
- }
- /* Wait for all threads to be ready*/
- odp_barrier_wait(&barrier);
-
- do {
- pkts = 0;
- rx_drops = 0;
- tx_drops = 0;
- invalid_seq = 0;
-
- sleep(timeout);
-
- for (i = 0; i < num_workers; i++) {
- pkts += thr_stats[i].s.packets;
- rx_drops += thr_stats[i].s.rx_drops;
- tx_drops += thr_stats[i].s.tx_drops;
- invalid_seq += thr_stats[i].s.invalid_seq;
- }
- if (stats_enabled) {
- pps = (pkts - pkts_prev) / timeout;
- if (pps > maximum_pps)
- maximum_pps = pps;
- printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
- maximum_pps);
-
- printf("%" PRIu64 " rx drops, %" PRIu64 " tx drops, ",
- rx_drops, tx_drops);
-
- printf("%" PRIu64 " invalid seq\n", invalid_seq);
-
- pkts_prev = pkts;
- }
- elapsed += timeout;
- } while (loop_forever || (elapsed < duration));
-
- if (stats_enabled)
- printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
- maximum_pps);
-
- return (pkts > 100 && !invalid_seq) ? 0 : -1;
-}
-
-/**
- * Find the destination port for a given input port
- *
- * @param port Input port index
- */
-static int find_dest_port(int port)
-{
- /* Even number of ports */
- if (gbl_args->appl.if_count % 2 == 0)
- return (port % 2 == 0) ? port + 1 : port - 1;
-
- /* Odd number of ports */
- if (port == gbl_args->appl.if_count - 1)
- return 0;
- else
- return port + 1;
-}
-
-/**
- * Initialize port forwarding table
- */
-static void init_forwarding_tbl(void)
-{
- int rx_idx;
-
- for (rx_idx = 0; rx_idx < gbl_args->appl.if_count; rx_idx++)
- gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
- printf("\n"
- "OpenDataPlane ordered pktio application.\n"
- "\n"
- "Usage: %s OPTIONS\n"
- " E.g. %s -i eth0,eth1\n"
- " In the above example,\n"
- " eth0 will send pkts to eth1 and vice versa\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
- " Interface count min 1, max %i\n"
- "\n"
- "Optional OPTIONS:\n"
- " -m, --mode Packet input mode\n"
- " 0: Scheduled ordered queues (default)\n"
- " 1: Scheduled atomic queues\n"
- " 2: Scheduled parallel queues (packet order not maintained)\n"
- " -r, --num_rx_q Number of RX queues per interface\n"
- " -f, --num_flows Number of packet flows\n"
- " -e, --extra_input <number> Number of extra input processing rounds\n"
- " -c, --count <number> CPU count.\n"
- " -t, --time <number> Time in seconds to run.\n"
- " -a, --accuracy <number> Statistics print interval in seconds\n"
- " (default is 1 second).\n"
- " -d, --dst_addr Destination addresses (comma-separated, no spaces)\n"
- " -h, --help Display help and exit.\n\n"
- "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS
- );
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
- int opt;
- int long_index;
- char *token;
- char *addr_str;
- size_t len;
- int i;
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"time", required_argument, NULL, 't'},
- {"accuracy", required_argument, NULL, 'a'},
- {"interface", required_argument, NULL, 'i'},
- {"mode", required_argument, NULL, 'm'},
- {"dst_addr", required_argument, NULL, 'd'},
- {"num_rx_q", required_argument, NULL, 'r'},
- {"num_flows", required_argument, NULL, 'f'},
- {"extra_input", required_argument, NULL, 'e'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:+t:+a:i:m:d:r:f:e:h";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- appl_args->time = 0; /* loop forever if time to run is 0 */
- appl_args->accuracy = DEF_STATS_INT;
- appl_args->num_rx_q = DEF_NUM_RX_QUEUES;
- appl_args->num_flows = DEF_NUM_FLOWS;
- appl_args->extra_rounds = DEF_EXTRA_ROUNDS;
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'c':
- appl_args->cpu_count = atoi(optarg);
- break;
- case 't':
- appl_args->time = atoi(optarg);
- break;
- case 'a':
- appl_args->accuracy = atoi(optarg);
- break;
- /* parse packet-io interface names */
- case 'd':
- len = strlen(optarg);
- if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- addr_str = malloc(len);
- if (addr_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* store the mac addresses names */
- strcpy(addr_str, optarg);
- for (token = strtok(addr_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- if (i >= MAX_PKTIOS) {
- printf("too many MAC addresses\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- if (odph_eth_addr_parse(&appl_args->addrs[i],
- token) != 0) {
- printf("invalid MAC address\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- }
- appl_args->addr_count = i;
- if (appl_args->addr_count < 1) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- free(addr_str);
- break;
- case 'i':
- len = strlen(optarg);
- if (len == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- len += 1; /* add room for '\0' */
-
- appl_args->if_str = malloc(len);
- if (appl_args->if_str == NULL) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* count the number of tokens separated by ',' */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL;
- token = strtok(NULL, ","), i++)
- ;
-
- appl_args->if_count = i;
-
- if (appl_args->if_count < 1 ||
- appl_args->if_count > MAX_PKTIOS) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- /* allocate storage for the if names */
- appl_args->if_names =
- calloc(appl_args->if_count, sizeof(char *));
-
- /* store the if names (reset names string) */
- strcpy(appl_args->if_str, optarg);
- for (token = strtok(appl_args->if_str, ","), i = 0;
- token != NULL; token = strtok(NULL, ","), i++) {
- appl_args->if_names[i] = token;
- }
- break;
- case 'm':
- i = atoi(optarg);
- if (i == 1)
- appl_args->in_mode = SCHED_ATOMIC;
- else if (i == 2)
- appl_args->in_mode = SCHED_PARALLEL;
- else
- appl_args->in_mode = SCHED_ORDERED;
- break;
- case 'r':
- appl_args->num_rx_q = atoi(optarg);
- break;
- case 'f':
- appl_args->num_flows = atoi(optarg);
- break;
- case 'e':
- appl_args->extra_rounds = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
- default:
- break;
- }
- }
-
- if (appl_args->cpu_count > MAX_WORKERS) {
- printf("Too many workers requested %d, max: %d\n",
- appl_args->cpu_count, MAX_WORKERS);
- exit(EXIT_FAILURE);
- }
-
- if (appl_args->num_flows > MAX_FLOWS) {
- printf("Too many flows requested %d, max: %d\n",
- appl_args->num_flows, MAX_FLOWS);
- exit(EXIT_FAILURE);
- }
-
- if (appl_args->if_count == 0 || appl_args->num_flows == 0 ||
- appl_args->num_rx_q == 0) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- if (appl_args->addr_count != 0 &&
- appl_args->addr_count != appl_args->if_count) {
- printf("Number of destination addresses differs from number"
- " of interfaces\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args)
-{
- int i;
-
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "ODP impl name: %s\n"
- "CPU model: %s\n"
- "CPU freq (hz): %" PRIu64 "\n"
- "Cache line size: %i\n"
- "CPU count: %i\n"
- "\n",
- odp_version_api_str(), odp_version_impl_name(),
- odp_cpu_model_str(), odp_cpu_hz_max(),
- odp_sys_cache_line_size(), odp_cpu_count());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n"
- "IF-count: %i\n"
- "Using IFs: ",
- progname, appl_args->if_count);
- for (i = 0; i < appl_args->if_count; ++i)
- printf(" %s", appl_args->if_names[i]);
- printf("\n\n");
- fflush(NULL);
-}
-
-static void gbl_args_init(args_t *args)
-{
- int pktio, queue;
-
- memset(args, 0, sizeof(args_t));
-
- for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
- args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
-
- for (queue = 0; queue < MAX_QUEUES; queue++)
- args->pktios[pktio].pktin[queue] = ODP_QUEUE_INVALID;
- }
-}
-
-/**
- * ODP ordered pktio application
- */
-int main(int argc, char *argv[])
-{
- odp_cpumask_t cpumask;
- odp_instance_t instance;
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_shm_t shm;
- odp_queue_capability_t capa;
- odph_ethaddr_t new_addr;
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- stats_t *stats;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- int cpu;
- int i, j;
- int if_count;
- int ret;
- int num_workers;
- int in_mode;
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Reserve memory for args from shared mem */
- shm = odp_shm_reserve("shm_args", sizeof(args_t),
- ODP_CACHE_LINE_SIZE, 0);
-
- if (shm == ODP_SHM_INVALID) {
- LOG_ERR("Error: shared mem reserve failed.\n");
- exit(EXIT_FAILURE);
- }
-
- gbl_args = odp_shm_addr(shm);
-
- if (gbl_args == NULL) {
- LOG_ERR("Error: shared mem alloc failed.\n");
- odp_shm_free(shm);
- exit(EXIT_FAILURE);
- }
- gbl_args_init(gbl_args);
-
- /* Parse and store the application arguments */
- parse_args(argc, argv, &gbl_args->appl);
-
- if (gbl_args->appl.in_mode == SCHED_ORDERED) {
- /* At least one ordered lock required */
- odp_queue_capability(&capa);
- if (capa.max_ordered_locks < 1) {
- LOG_ERR("Error: Ordered locks not available.\n");
- exit(EXIT_FAILURE);
- }
- }
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]), &gbl_args->appl);
-
- /* Default to system CPU count unless user specified */
- num_workers = MAX_WORKERS;
- if (gbl_args->appl.cpu_count)
- num_workers = gbl_args->appl.cpu_count;
-
- /* Get default worker cpumask */
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- if_count = gbl_args->appl.if_count;
-
- printf("Num worker threads: %i\n", num_workers);
- printf("First CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("CPU mask: %s\n\n", cpumaskstr);
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = PKT_POOL_BUF_SIZE;
- params.pkt.len = PKT_POOL_BUF_SIZE;
- params.pkt.num = PKT_POOL_SIZE;
- params.pkt.uarea_size = PKT_UAREA_SIZE;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create("packet pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- LOG_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
- odp_pool_print(pool);
-
- init_forwarding_tbl();
-
- for (i = 0; i < if_count; ++i) {
- const char *dev = gbl_args->appl.if_names[i];
- int num_rx, num_tx;
-
- num_rx = gbl_args->appl.num_rx_q;
- num_tx = gbl_args->appl.num_flows;
-
- if (create_pktio(dev, i, num_rx, num_tx, pool))
- exit(EXIT_FAILURE);
-
- /* Save interface ethernet address */
- if (odp_pktio_mac_addr(gbl_args->pktios[i].pktio,
- gbl_args->port_eth_addr[i].addr,
- ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
- LOG_ERR("Error: interface ethernet address unknown\n");
- exit(EXIT_FAILURE);
- }
-
- odp_pktio_print(gbl_args->pktios[i].pktio);
-
- /* Save destination eth address */
- /* 02:00:00:00:00:XX */
- memset(&new_addr, 0, sizeof(odph_ethaddr_t));
- if (gbl_args->appl.addr_count) {
- memcpy(&new_addr, &gbl_args->appl.addrs[i],
- sizeof(odph_ethaddr_t));
- } else {
- new_addr.addr[0] = 0x02;
- new_addr.addr[5] = i;
- }
- gbl_args->dst_eth_addr[i] = new_addr;
- }
-
- gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
-
- /* Allocate the same number of flows to each interface */
- for (i = 0; i < if_count; i++) {
- odp_pktio_capability_t capa;
-
- if (odp_pktio_capability(gbl_args->pktios[i].pktio, &capa)) {
- LOG_ERR("Error: pktio capability failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if ((unsigned)gbl_args->appl.num_flows > capa.max_output_queues)
- gbl_args->appl.num_flows = capa.max_output_queues;
- }
-
- /* Create atomic queues for packet tagging */
- for (i = 0; i < if_count; i++) {
- for (j = 0; j < gbl_args->appl.num_flows; j++) {
- odp_queue_t queue;
- odp_queue_param_t qparam;
- char qname[ODP_QUEUE_NAME_LEN];
-
- snprintf(qname, sizeof(qname), "flow_%d_%d", i, j);
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
-
- gbl_args->flow_qcontext[i][j].idx = i;
- gbl_args->flow_qcontext[i][j].input_queue = 0;
- qparam.context = &gbl_args->flow_qcontext[i][j];
- qparam.context_len = sizeof(qcontext_t);
-
- queue = odp_queue_create(qname, &qparam);
- if (queue == ODP_QUEUE_INVALID) {
- LOG_ERR("Error: flow queue create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- gbl_args->fqueue[i][j] = queue;
- }
- }
-
- in_mode = gbl_args->appl.in_mode;
- printf("\nApplication parameters\n"
- "----------------------\n"
- "Input queues: %d\n"
- "Mode: %s\n"
- "Flows: %d\n"
- "Extra rounds: %d\n\n", gbl_args->appl.num_rx_q,
- (in_mode == SCHED_ATOMIC) ? "PKTIN_SCHED_ATOMIC" :
- (in_mode == SCHED_PARALLEL ? "PKTIN_SCHED_PARALLEL" :
- "PKTIN_SCHED_ORDERED"), gbl_args->appl.num_flows,
- gbl_args->appl.extra_rounds);
-
- memset(thread_tbl, 0, sizeof(thread_tbl));
-
- stats = gbl_args->stats;
-
- odp_barrier_init(&barrier, num_workers + 1);
-
- /* Create worker threads */
- cpu = odp_cpumask_first(&cpumask);
- for (i = 0; i < num_workers; ++i) {
- odp_cpumask_t thd_mask;
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_worker;
- thr_params.arg = &gbl_args->thread[i];
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- gbl_args->thread[i].stats = &stats[i];
-
- odp_cpumask_zero(&thd_mask);
- odp_cpumask_set(&thd_mask, cpu);
- odph_odpthreads_create(&thread_tbl[i], &thd_mask,
- &thr_params);
- cpu = odp_cpumask_next(&cpumask, cpu);
- }
-
- /* Start packet receive and transmit */
- for (i = 0; i < if_count; ++i) {
- odp_pktio_t pktio;
-
- pktio = gbl_args->pktios[i].pktio;
- ret = odp_pktio_start(pktio);
- if (ret) {
- LOG_ERR("Error: unable to start %s\n",
- gbl_args->appl.if_names[i]);
- exit(EXIT_FAILURE);
- }
- }
-
- ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
- gbl_args->appl.accuracy);
-
- /* Stop receiving new packet */
- for (i = 0; i < if_count; i++)
- odp_pktio_stop(gbl_args->pktios[i].pktio);
-
- exit_threads = 1;
-
- /* Master thread waits for other threads to exit */
- for (i = 0; i < num_workers; ++i)
- odph_odpthreads_join(&thread_tbl[i]);
-
- for (i = 0; i < if_count; i++) {
- odp_pktio_close(gbl_args->pktios[i].pktio);
-
- for (j = 0; j < gbl_args->appl.num_flows; j++)
- odp_queue_destroy(gbl_args->fqueue[i][j]);
- }
-
- free(gbl_args->appl.if_names);
- free(gbl_args->appl.if_str);
-
- if (odp_pool_destroy(pool)) {
- LOG_ERR("Error: pool destroy\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_shm_free(shm)) {
- LOG_ERR("Error: shm free\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- LOG_ERR("Error: term local\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- LOG_ERR("Error: term global\n");
- exit(EXIT_FAILURE);
- }
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_pktio_ordered_run.sh b/test/common_plat/performance/odp_pktio_ordered_run.sh
deleted file mode 100755
index d6c2be526..000000000
--- a/test/common_plat/performance/odp_pktio_ordered_run.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-TEST_SRC_DIR=$(dirname $0)
-TEST_DIR="${TEST_DIR:-$(dirname $0)}"
-
-DURATION=5
-LOG=odp_pktio_ordered.log
-LOOPS=100000000
-PASS_PPS=5000
-PCAP_IN=`find . ${TEST_SRC_DIR} $(dirname $0) -name udp64.pcap -print -quit`
-PCAP_OUT=/dev/null
-
-if [ ! -f ${PCAP_IN} ]; then
- echo "FAIL: no udp64.pcap"
- exit 1
-fi
-
-# This just turns off output buffering so that you still get periodic
-# output while piping to tee, as long as stdbuf is available.
-if [ "$(which stdbuf)" != "" ]; then
- STDBUF="stdbuf -o 0"
-else
- STDBUF=
-fi
-
-$STDBUF ${TEST_DIR}/odp_pktio_ordered${EXEEXT} \
- -i pcap:in=${PCAP_IN}:loops=$LOOPS,pcap:out=${PCAP_OUT} \
- -t $DURATION | tee $LOG
-
-ret=${PIPESTATUS[0]}
-
-if [ $ret -ne 0 ]; then
- echo "FAIL: no odp_pktio_ordered${EXEEXT}"
- rm -f $LOG
- exit $ret
-fi
-
-if [ ! -f $LOG ]; then
- echo "FAIL: $LOG not found"
- ret=1
- exit $ret
-fi
-
-MAX_PPS=$(awk '/TEST RESULT/ {print $3}' $LOG)
-echo "MAX_PPS=$MAX_PPS"
-if [ $MAX_PPS -lt $PASS_PPS ]; then
- echo "FAIL: pps below threshold $MAX_PPS < $PASS_PPS"
- ret=1
-fi
-
-rm -f $LOG
-
-exit $ret
diff --git a/test/common_plat/performance/odp_pktio_perf.c b/test/common_plat/performance/odp_pktio_perf.c
deleted file mode 100644
index 094630811..000000000
--- a/test/common_plat/performance/odp_pktio_perf.c
+++ /dev/null
@@ -1,1089 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * ODP Packet IO basic performance test application.
- *
- * Runs a number of transmit and receive workers on separate cores, the
- * transmitters generate packets at a defined rate and the receivers consume
- * them. Generated packets are UDP and each packet is marked with a magic
- * number in the UDP payload allowing receiver to distinguish them from other
- * traffic.
- *
- * Each test iteration runs for a fixed period, at the end of the iteration
- * it is verified that the number of packets transmitted was as expected and
- * that all transmitted packets were received.
- *
- * The default mode is to run multiple test iterations at different rates to
- * determine the maximum rate at which no packet loss occurs. Alternatively
- * a single packet rate can be specified on the command line.
- *
- */
-#include <odp_api.h>
-
-#include <odp/helper/odph_api.h>
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-#include <test_debug.h>
-
-#define TEST_SKIP 77
-
-#define PKT_BUF_NUM (32 * 1024)
-#define MAX_NUM_IFACES 2
-#define TEST_HDR_MAGIC 0x92749451
-#define MAX_WORKERS 32
-#define BATCH_LEN_MAX 32
-
-/* Packet rate at which to start when using binary search */
-#define RATE_SEARCH_INITIAL_PPS 1000000
-
-/* When using the binary search method to determine the maximum
- * achievable packet rate, this value specifies how close the pass
- * and fail measurements must be before the test is terminated. */
-#define RATE_SEARCH_ACCURACY_PPS 100000
-
-/* Amount of time to wait, in nanoseconds, between the transmitter(s)
- * completing and the receiver(s) being shutdown. Any packets not
- * received by this time will be assumed to have been lost. */
-#define SHUTDOWN_DELAY_NS (ODP_TIME_MSEC_IN_NS * 100)
-
-#define VPRINT(fmt, ...) \
- do { \
- if (gbl_args->args.verbose) \
- printf(fmt, ##__VA_ARGS__); \
- } while (0)
-
-#define CACHE_ALIGN_ROUNDUP(x)\
- ((ODP_CACHE_LINE_SIZE) * \
- (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE)))
-
-#define PKT_HDR_LEN (sizeof(pkt_head_t) + ODPH_UDPHDR_LEN + \
- ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN)
-
-/** Parsed command line application arguments */
-typedef struct {
- int cpu_count; /* CPU count */
- int num_tx_workers;/* Number of CPUs to use for transmit */
- int duration; /* Number of seconds to run each iteration
- of the test for */
- uint32_t tx_batch_len; /* Number of packets to send in a single
- batch */
- int schedule; /* 1: receive packets via scheduler
- 0: receive packets via direct deq */
- uint32_t rx_batch_len; /* Number of packets to receive in a single
- batch */
- uint64_t pps; /* Attempted packet rate */
- int verbose; /* Print verbose information, such as per
- thread statistics */
- unsigned pkt_len; /* Packet payload length in bytes (not
- including headers) */
- int search; /* Set implicitly when pps is not configured.
- Perform a search at different packet rates
- to determine the maximum rate at which no
- packet loss occurs. */
-
- char *if_str;
- const char *ifaces[MAX_NUM_IFACES];
- int num_ifaces;
-} test_args_t;
-
-struct rx_stats_s {
- uint64_t rx_cnt; /* Valid packets received */
- uint64_t rx_ignore; /* Ignored packets */
-};
-
-typedef union rx_stats_u {
- struct rx_stats_s s;
- uint8_t pad[CACHE_ALIGN_ROUNDUP(sizeof(struct rx_stats_s))];
-} pkt_rx_stats_t;
-
-struct tx_stats_s {
- uint64_t tx_cnt; /* Packets transmitted */
- uint64_t alloc_failures;/* Packet allocation failures */
- uint64_t enq_failures; /* Enqueue failures */
- odp_time_t idle_ticks; /* Idle ticks count in TX loop */
-};
-
-typedef union tx_stats_u {
- struct tx_stats_s s;
- uint8_t pad[CACHE_ALIGN_ROUNDUP(sizeof(struct tx_stats_s))];
-} pkt_tx_stats_t;
-
-/* Test global variables */
-typedef struct {
- odp_instance_t instance;
- test_args_t args;
- odp_barrier_t rx_barrier;
- odp_barrier_t tx_barrier;
- odp_pktio_t pktio_tx;
- odp_pktio_t pktio_rx;
- pkt_rx_stats_t *rx_stats;
- pkt_tx_stats_t *tx_stats;
- uint8_t src_mac[ODPH_ETHADDR_LEN];
- uint8_t dst_mac[ODPH_ETHADDR_LEN];
- uint32_t rx_stats_size;
- uint32_t tx_stats_size;
-} test_globals_t;
-
-/* Status of max rate search */
-typedef struct {
- uint64_t pps_curr; /* Current attempted PPS */
- uint64_t pps_pass; /* Highest passing PPS */
- uint64_t pps_fail; /* Lowest failing PPS */
- int warmup; /* Warmup stage - ignore results */
-} test_status_t;
-
-/* Thread specific arguments */
-typedef struct {
- int batch_len; /* Number of packets per transmit batch */
- int duration; /* Run duration in seconds */
- uint64_t pps; /* Packets per second for this thread */
-} thread_args_t;
-
-typedef struct {
- odp_u32be_t magic; /* Packet header magic number */
-} pkt_head_t;
-
-/* Pool from which transmitted packets are allocated */
-static odp_pool_t transmit_pkt_pool = ODP_POOL_INVALID;
-
-/* Sequence number of IP packets */
-static odp_atomic_u32_t ip_seq;
-
-/* Indicate to the receivers to shutdown */
-static odp_atomic_u32_t shutdown;
-
-/* Application global data */
-static test_globals_t *gbl_args;
-
-/*
- * Generate a single test packet for transmission.
- */
-static odp_packet_t pktio_create_packet(void)
-{
- odp_packet_t pkt;
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ip;
- odph_udphdr_t *udp;
- char *buf;
- uint16_t seq;
- uint32_t offset;
- pkt_head_t pkt_hdr;
- size_t payload_len;
-
- payload_len = sizeof(pkt_hdr) + gbl_args->args.pkt_len;
-
- pkt = odp_packet_alloc(transmit_pkt_pool,
- payload_len + ODPH_UDPHDR_LEN +
- ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN);
-
- if (pkt == ODP_PACKET_INVALID)
- return ODP_PACKET_INVALID;
-
- buf = odp_packet_data(pkt);
-
- /* Ethernet */
- offset = 0;
- odp_packet_l2_offset_set(pkt, offset);
- eth = (odph_ethhdr_t *)buf;
- memcpy(eth->src.addr, gbl_args->src_mac, ODPH_ETHADDR_LEN);
- memcpy(eth->dst.addr, gbl_args->dst_mac, ODPH_ETHADDR_LEN);
- eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
-
- /* IP */
- offset += ODPH_ETHHDR_LEN;
- odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- ip->dst_addr = odp_cpu_to_be_32(0);
- ip->src_addr = odp_cpu_to_be_32(0);
- ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->tot_len = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN +
- ODPH_IPV4HDR_LEN);
- ip->ttl = 128;
- ip->proto = ODPH_IPPROTO_UDP;
- seq = odp_atomic_fetch_inc_u32(&ip_seq);
- ip->id = odp_cpu_to_be_16(seq);
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
-
- /* UDP */
- offset += ODPH_IPV4HDR_LEN;
- odp_packet_l4_offset_set(pkt, offset);
- udp = (odph_udphdr_t *)(buf + offset);
- udp->src_port = odp_cpu_to_be_16(0);
- udp->dst_port = odp_cpu_to_be_16(0);
- udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
- udp->chksum = 0;
-
- /* payload */
- offset += ODPH_UDPHDR_LEN;
- pkt_hdr.magic = TEST_HDR_MAGIC;
- if (odp_packet_copy_from_mem(pkt, offset, sizeof(pkt_hdr),
- &pkt_hdr) != 0)
- LOG_ABORT("Failed to generate test packet.\n");
-
- return pkt;
-}
-
-/*
- * Check if a packet payload contains test payload magic number.
- */
-static int pktio_pkt_has_magic(odp_packet_t pkt)
-{
- size_t l4_off;
- pkt_head_t pkt_hdr;
-
- l4_off = odp_packet_l4_offset(pkt);
- if (l4_off) {
- int ret = odp_packet_copy_to_mem(pkt,
- l4_off + ODPH_UDPHDR_LEN,
- sizeof(pkt_hdr), &pkt_hdr);
-
- if (ret != 0)
- return 0;
-
- if (pkt_hdr.magic == TEST_HDR_MAGIC)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Allocate packets for transmission.
- */
-static int alloc_packets(odp_packet_t *pkt_tbl, int num_pkts)
-{
- int n;
-
- for (n = 0; n < num_pkts; ++n) {
- pkt_tbl[n] = pktio_create_packet();
- if (pkt_tbl[n] == ODP_PACKET_INVALID)
- break;
- }
-
- return n;
-}
-
-static int send_packets(odp_pktout_queue_t pktout,
- odp_packet_t *pkt_tbl, unsigned pkts)
-{
- unsigned tx_drops;
- unsigned sent = 0;
-
- if (pkts == 0)
- return 0;
-
- while (sent < pkts) {
- int ret;
-
- ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
-
- if (odp_likely(ret > 0))
- sent += ret;
- else
- break;
- }
-
- tx_drops = pkts - sent;
-
- if (odp_unlikely(tx_drops))
- odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
-
- return sent;
-}
-
-/*
- * Main packet transmit routine. Transmit packets at a fixed rate for
- * specified length of time.
- */
-static int run_thread_tx(void *arg)
-{
- test_globals_t *globals;
- int thr_id;
- odp_pktout_queue_t pktout;
- pkt_tx_stats_t *stats;
- odp_time_t cur_time, send_time_end, send_duration;
- odp_time_t burst_gap_end, burst_gap;
- uint32_t batch_len;
- int unsent_pkts = 0;
- odp_packet_t tx_packet[BATCH_LEN_MAX];
- odp_time_t idle_start = ODP_TIME_NULL;
-
- thread_args_t *targs = arg;
-
- batch_len = targs->batch_len;
-
- if (batch_len > BATCH_LEN_MAX)
- batch_len = BATCH_LEN_MAX;
-
- thr_id = odp_thread_id();
-
- globals = odp_shm_addr(odp_shm_lookup("test_globals"));
- stats = &globals->tx_stats[thr_id];
-
- if (odp_pktout_queue(globals->pktio_tx, &pktout, 1) != 1)
- LOG_ABORT("Failed to get output queue for thread %d\n", thr_id);
-
- burst_gap = odp_time_local_from_ns(
- ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len));
- send_duration =
- odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS);
-
- odp_barrier_wait(&globals->tx_barrier);
-
- cur_time = odp_time_local();
- send_time_end = odp_time_sum(cur_time, send_duration);
- burst_gap_end = cur_time;
- while (odp_time_cmp(send_time_end, cur_time) > 0) {
- unsigned alloc_cnt = 0, tx_cnt;
-
- if (odp_time_cmp(burst_gap_end, cur_time) > 0) {
- cur_time = odp_time_local();
- if (!odp_time_cmp(idle_start, ODP_TIME_NULL))
- idle_start = cur_time;
- continue;
- }
-
- if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) {
- odp_time_t diff = odp_time_diff(cur_time, idle_start);
-
- stats->s.idle_ticks =
- odp_time_sum(diff, stats->s.idle_ticks);
-
- idle_start = ODP_TIME_NULL;
- }
-
- burst_gap_end = odp_time_sum(burst_gap_end, burst_gap);
-
- alloc_cnt = alloc_packets(tx_packet, batch_len - unsent_pkts);
- if (alloc_cnt != batch_len)
- stats->s.alloc_failures++;
-
- tx_cnt = send_packets(pktout, tx_packet, alloc_cnt);
- unsent_pkts = alloc_cnt - tx_cnt;
- stats->s.enq_failures += unsent_pkts;
- stats->s.tx_cnt += tx_cnt;
-
- cur_time = odp_time_local();
- }
-
- VPRINT(" %02d: TxPkts %-8" PRIu64 " EnqFail %-6" PRIu64
- " AllocFail %-6" PRIu64 " Idle %" PRIu64 "ms\n",
- thr_id, stats->s.tx_cnt,
- stats->s.enq_failures, stats->s.alloc_failures,
- odp_time_to_ns(stats->s.idle_ticks) /
- (uint64_t)ODP_TIME_MSEC_IN_NS);
-
- return 0;
-}
-
-static int receive_packets(odp_queue_t queue,
- odp_event_t *event_tbl, unsigned num_pkts)
-{
- int n_ev = 0;
-
- if (num_pkts == 0)
- return 0;
-
- if (queue != ODP_QUEUE_INVALID) {
- if (num_pkts == 1) {
- event_tbl[0] = odp_queue_deq(queue);
- n_ev = event_tbl[0] != ODP_EVENT_INVALID;
- } else {
- n_ev = odp_queue_deq_multi(queue, event_tbl, num_pkts);
- }
- } else {
- if (num_pkts == 1) {
- event_tbl[0] = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
- n_ev = event_tbl[0] != ODP_EVENT_INVALID;
- } else {
- n_ev = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
- event_tbl, num_pkts);
- }
- }
- return n_ev;
-}
-
-static int run_thread_rx(void *arg)
-{
- test_globals_t *globals;
- int thr_id, batch_len;
- odp_queue_t queue = ODP_QUEUE_INVALID;
- odp_packet_t pkt;
-
- thread_args_t *targs = arg;
-
- batch_len = targs->batch_len;
-
- if (batch_len > BATCH_LEN_MAX)
- batch_len = BATCH_LEN_MAX;
-
- thr_id = odp_thread_id();
-
- globals = odp_shm_addr(odp_shm_lookup("test_globals"));
-
- pkt_rx_stats_t *stats = &globals->rx_stats[thr_id];
-
- if (gbl_args->args.schedule == 0) {
- if (odp_pktin_event_queue(globals->pktio_rx, &queue, 1) != 1)
- LOG_ABORT("No input queue.\n");
- }
-
- odp_barrier_wait(&globals->rx_barrier);
- while (1) {
- odp_event_t ev[BATCH_LEN_MAX];
- int i, n_ev;
-
- n_ev = receive_packets(queue, ev, batch_len);
-
- for (i = 0; i < n_ev; ++i) {
- if (odp_event_type(ev[i]) == ODP_EVENT_PACKET) {
- pkt = odp_packet_from_event(ev[i]);
- if (pktio_pkt_has_magic(pkt))
- stats->s.rx_cnt++;
- else
- stats->s.rx_ignore++;
- }
- odp_event_free(ev[i]);
- }
- if (n_ev == 0 && odp_atomic_load_u32(&shutdown))
- break;
- }
-
- return 0;
-}
-
-/*
- * Process the results from a single fixed rate test run to determine whether
- * it passed or failed. Pass criteria are that the requested transmit packet
- * rate was achieved and that all of the transmitted packets were received.
- */
-static int process_results(uint64_t expected_tx_cnt,
- test_status_t *status)
-{
- int fail = 0;
- uint64_t drops = 0;
- uint64_t rx_pkts = 0;
- uint64_t tx_pkts = 0;
- uint64_t attempted_pps;
- int i;
- char str[512];
- int len = 0;
-
- for (i = 0; i < odp_thread_count_max(); ++i) {
- rx_pkts += gbl_args->rx_stats[i].s.rx_cnt;
- tx_pkts += gbl_args->tx_stats[i].s.tx_cnt;
- }
-
- if (rx_pkts == 0) {
- LOG_ERR("no packets received\n");
- return -1;
- }
-
- if (tx_pkts < (expected_tx_cnt - (expected_tx_cnt / 100))) {
- /* failed to transmit packets at (99% of) requested rate */
- fail = 1;
- } else if (tx_pkts > rx_pkts) {
- /* failed to receive all of the transmitted packets */
- fail = 1;
- drops = tx_pkts - rx_pkts;
- }
-
- attempted_pps = status->pps_curr;
-
- len += snprintf(&str[len], sizeof(str) - 1 - len,
- "PPS: %-8" PRIu64 " ", attempted_pps);
- len += snprintf(&str[len], sizeof(str) - 1 - len,
- "Succeeded: %-4s ", fail ? "No" : "Yes");
- len += snprintf(&str[len], sizeof(str) - 1 - len,
- "TxPkts: %-8" PRIu64 " ", tx_pkts);
- len += snprintf(&str[len], sizeof(str) - 1 - len,
- "RxPkts: %-8" PRIu64 " ", rx_pkts);
- len += snprintf(&str[len], sizeof(str) - 1 - len,
- "DropPkts: %-8" PRIu64 " ", drops);
- printf("%s\n", str);
-
- if (gbl_args->args.search == 0) {
- printf("Result: %s\n", fail ? "FAILED" : "PASSED");
- return fail ? -1 : 0;
- }
-
- if (fail && (status->pps_fail == 0 ||
- attempted_pps < status->pps_fail)) {
- status->pps_fail = attempted_pps;
- } else if (attempted_pps > status->pps_pass) {
- status->pps_pass = attempted_pps;
- }
-
- if (status->pps_fail == 0) {
- /* ramping up, double the previously attempted pps */
- status->pps_curr *= 2;
- } else {
- /* set the new target to half way between the upper and lower
- * limits */
- status->pps_curr = status->pps_pass +
- ((status->pps_fail - status->pps_pass) / 2);
- }
-
- /* stop once the pass and fail measurements are within range */
- if ((status->pps_fail - status->pps_pass) < RATE_SEARCH_ACCURACY_PPS) {
- unsigned pkt_len = gbl_args->args.pkt_len + PKT_HDR_LEN;
- int mbps = (pkt_len * status->pps_pass * 8) / 1024 / 1024;
-
- printf("Maximum packet rate: %" PRIu64 " PPS (%d Mbps)\n",
- status->pps_pass, mbps);
-
- return 0;
- }
-
- return 1;
-}
-
-static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx,
- odp_cpumask_t *thd_mask_rx)
-{
- odp_cpumask_t cpumask;
- int num_workers, num_tx_workers, num_rx_workers;
- int i, cpu;
-
- num_workers =
- odp_cpumask_default_worker(&cpumask,
- gbl_args->args.cpu_count);
- if (num_workers < 2) {
- LOG_ERR("Need at least two cores\n");
- return TEST_SKIP;
- }
-
- if (gbl_args->args.num_tx_workers) {
- if (gbl_args->args.num_tx_workers > (num_workers - 1)) {
- LOG_ERR("Invalid TX worker count\n");
- return -1;
- }
- num_tx_workers = gbl_args->args.num_tx_workers;
- } else {
- /* default is to split the available cores evenly into TX and
- * RX workers, favour TX for odd core count */
- num_tx_workers = (num_workers + 1) / 2;
- }
-
- odp_cpumask_zero(thd_mask_tx);
- odp_cpumask_zero(thd_mask_rx);
-
- cpu = odp_cpumask_first(&cpumask);
- for (i = 0; i < num_workers; ++i) {
- if (i < num_tx_workers)
- odp_cpumask_set(thd_mask_tx, cpu);
- else
- odp_cpumask_set(thd_mask_rx, cpu);
- cpu = odp_cpumask_next(&cpumask, cpu);
- }
-
- num_rx_workers = odp_cpumask_count(thd_mask_rx);
-
- odp_barrier_init(&gbl_args->rx_barrier, num_rx_workers + 1);
- odp_barrier_init(&gbl_args->tx_barrier, num_tx_workers + 1);
-
- return 0;
-}
-
-/*
- * Run a single instance of the throughput test. When attempting to determine
- * the maximum packet rate this will be invoked multiple times with the only
- * difference between runs being the target PPS rate.
- */
-static int run_test_single(odp_cpumask_t *thd_mask_tx,
- odp_cpumask_t *thd_mask_rx,
- test_status_t *status)
-{
- odph_odpthread_t thd_tbl[MAX_WORKERS];
- thread_args_t args_tx, args_rx;
- uint64_t expected_tx_cnt;
- int num_tx_workers, num_rx_workers;
- odph_odpthread_params_t thr_params;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = gbl_args->instance;
-
- odp_atomic_store_u32(&shutdown, 0);
-
- memset(thd_tbl, 0, sizeof(thd_tbl));
- memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
- memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
-
- expected_tx_cnt = status->pps_curr * gbl_args->args.duration;
-
- /* start receiver threads first */
- thr_params.start = run_thread_rx;
- thr_params.arg = &args_rx;
- args_rx.batch_len = gbl_args->args.rx_batch_len;
- odph_odpthreads_create(&thd_tbl[0], thd_mask_rx, &thr_params);
- odp_barrier_wait(&gbl_args->rx_barrier);
- num_rx_workers = odp_cpumask_count(thd_mask_rx);
-
- /* then start transmitters */
- thr_params.start = run_thread_tx;
- thr_params.arg = &args_tx;
- num_tx_workers = odp_cpumask_count(thd_mask_tx);
- args_tx.pps = status->pps_curr / num_tx_workers;
- args_tx.duration = gbl_args->args.duration;
- args_tx.batch_len = gbl_args->args.tx_batch_len;
- odph_odpthreads_create(&thd_tbl[num_rx_workers], thd_mask_tx,
- &thr_params);
- odp_barrier_wait(&gbl_args->tx_barrier);
-
- /* wait for transmitter threads to terminate */
- odph_odpthreads_join(&thd_tbl[num_rx_workers]);
-
- /* delay to allow transmitted packets to reach the receivers */
- odp_time_wait_ns(SHUTDOWN_DELAY_NS);
-
- /* indicate to the receivers to exit */
- odp_atomic_store_u32(&shutdown, 1);
-
- /* wait for receivers */
- odph_odpthreads_join(&thd_tbl[0]);
-
- if (!status->warmup)
- return process_results(expected_tx_cnt, status);
-
- return 1;
-}
-
-static int run_test(void)
-{
- int ret;
- int i;
- odp_cpumask_t txmask, rxmask;
- test_status_t status = {
- .pps_curr = gbl_args->args.pps,
- .pps_pass = 0,
- .pps_fail = 0,
- .warmup = 1,
- };
-
- ret = setup_txrx_masks(&txmask, &rxmask);
- if (ret)
- return ret;
-
- printf("Starting test with params:\n");
- printf("\tTransmit workers: \t%d\n", odp_cpumask_count(&txmask));
- printf("\tReceive workers: \t%d\n", odp_cpumask_count(&rxmask));
- printf("\tDuration (seconds): \t%d\n", gbl_args->args.duration);
- printf("\tTransmit batch length:\t%" PRIu32 "\n",
- gbl_args->args.tx_batch_len);
- printf("\tReceive batch length: \t%" PRIu32 "\n",
- gbl_args->args.rx_batch_len);
- printf("\tPacket receive method:\t%s\n",
- gbl_args->args.schedule ? "schedule" : "plain");
- printf("\tInterface(s): \t");
- for (i = 0; i < gbl_args->args.num_ifaces; ++i)
- printf("%s ", gbl_args->args.ifaces[i]);
- printf("\n");
-
- /* first time just run the test but throw away the results */
- run_test_single(&txmask, &rxmask, &status);
- status.warmup = 0;
-
- while (1) {
- ret = run_test_single(&txmask, &rxmask, &status);
- if (ret <= 0)
- break;
- }
-
- return ret;
-}
-
-static odp_pktio_t create_pktio(const char *iface, int schedule)
-{
- odp_pool_t pool;
- odp_pktio_t pktio;
- char pool_name[ODP_POOL_NAME_LEN];
- odp_pool_param_t params;
- odp_pktio_param_t pktio_param;
-
- odp_pool_param_init(&params);
- params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
- params.pkt.seg_len = params.pkt.len;
- params.pkt.num = PKT_BUF_NUM;
- params.type = ODP_POOL_PACKET;
-
- snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s", iface);
- pool = odp_pool_create(pool_name, &params);
- if (pool == ODP_POOL_INVALID)
- return ODP_PKTIO_INVALID;
-
- odp_pktio_param_init(&pktio_param);
-
- if (schedule)
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
- else
- pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
-
- pktio = odp_pktio_open(iface, pool, &pktio_param);
-
- return pktio;
-}
-
-static int test_init(void)
-{
- odp_pool_param_t params;
- const char *iface;
- int schedule;
-
- odp_pool_param_init(&params);
- params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
- params.pkt.seg_len = params.pkt.len;
- params.pkt.num = PKT_BUF_NUM;
- params.type = ODP_POOL_PACKET;
-
- transmit_pkt_pool = odp_pool_create("pkt_pool_transmit", &params);
- if (transmit_pkt_pool == ODP_POOL_INVALID)
- LOG_ABORT("Failed to create transmit pool\n");
-
- odp_atomic_init_u32(&ip_seq, 0);
- odp_atomic_init_u32(&shutdown, 0);
-
- iface = gbl_args->args.ifaces[0];
- schedule = gbl_args->args.schedule;
-
- /* create pktios and associate input/output queues */
- gbl_args->pktio_tx = create_pktio(iface, schedule);
- if (gbl_args->args.num_ifaces > 1) {
- iface = gbl_args->args.ifaces[1];
- gbl_args->pktio_rx = create_pktio(iface, schedule);
- } else {
- gbl_args->pktio_rx = gbl_args->pktio_tx;
- }
-
- odp_pktio_mac_addr(gbl_args->pktio_tx, gbl_args->src_mac,
- ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(gbl_args->pktio_rx, gbl_args->dst_mac,
- ODPH_ETHADDR_LEN);
-
- if (gbl_args->pktio_rx == ODP_PKTIO_INVALID ||
- gbl_args->pktio_tx == ODP_PKTIO_INVALID) {
- LOG_ERR("failed to open pktio\n");
- return -1;
- }
-
- /* Create single queue with default parameters */
- if (odp_pktout_queue_config(gbl_args->pktio_tx, NULL)) {
- LOG_ERR("failed to configure pktio_tx queue\n");
- return -1;
- }
-
- /* Configure also input side (with defaults) */
- if (odp_pktin_queue_config(gbl_args->pktio_tx, NULL)) {
- LOG_ERR("failed to configure pktio_tx queue\n");
- return -1;
- }
-
- if (gbl_args->args.num_ifaces > 1) {
- if (odp_pktout_queue_config(gbl_args->pktio_rx, NULL)) {
- LOG_ERR("failed to configure pktio_rx queue\n");
- return -1;
- }
-
- if (odp_pktin_queue_config(gbl_args->pktio_rx, NULL)) {
- LOG_ERR("failed to configure pktio_rx queue\n");
- return -1;
- }
- }
-
- if (odp_pktio_start(gbl_args->pktio_tx) != 0)
- return -1;
- if (gbl_args->args.num_ifaces > 1 &&
- odp_pktio_start(gbl_args->pktio_rx))
- return -1;
-
- return 0;
-}
-
-static int empty_inq(odp_pktio_t pktio)
-{
- odp_queue_t queue;
- odp_event_t ev;
- odp_queue_type_t q_type;
-
- if (odp_pktin_event_queue(pktio, &queue, 1) != 1)
- return -1;
-
- q_type = odp_queue_type(queue);
-
- /* flush any pending events */
- while (1) {
- if (q_type == ODP_QUEUE_TYPE_PLAIN)
- ev = odp_queue_deq(queue);
- else
- ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
-
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
- else
- break;
- }
-
- return 0;
-}
-
-static int test_term(void)
-{
- char pool_name[ODP_POOL_NAME_LEN];
- odp_pool_t pool;
- int i;
- int ret = 0;
-
- if (gbl_args->pktio_tx != gbl_args->pktio_rx) {
- if (odp_pktio_stop(gbl_args->pktio_tx)) {
- LOG_ERR("Failed to stop pktio_tx\n");
- return -1;
- }
-
- if (odp_pktio_close(gbl_args->pktio_tx)) {
- LOG_ERR("Failed to close pktio_tx\n");
- ret = -1;
- }
- }
-
- empty_inq(gbl_args->pktio_rx);
-
- if (odp_pktio_stop(gbl_args->pktio_rx)) {
- LOG_ERR("Failed to stop pktio_rx\n");
- return -1;
- }
-
- if (odp_pktio_close(gbl_args->pktio_rx) != 0) {
- LOG_ERR("Failed to close pktio_rx\n");
- ret = -1;
- }
-
- for (i = 0; i < gbl_args->args.num_ifaces; ++i) {
- snprintf(pool_name, sizeof(pool_name),
- "pkt_pool_%s", gbl_args->args.ifaces[i]);
- pool = odp_pool_lookup(pool_name);
- if (pool == ODP_POOL_INVALID)
- continue;
-
- if (odp_pool_destroy(pool) != 0) {
- LOG_ERR("Failed to destroy pool %s\n", pool_name);
- ret = -1;
- }
- }
-
- if (odp_pool_destroy(transmit_pkt_pool) != 0) {
- LOG_ERR("Failed to destroy transmit pool\n");
- ret = -1;
- }
-
- free(gbl_args->args.if_str);
-
- if (odp_shm_free(odp_shm_lookup("test_globals")) != 0) {
- LOG_ERR("Failed to free test_globals\n");
- ret = -1;
- }
- if (odp_shm_free(odp_shm_lookup("test_globals.rx_stats")) != 0) {
- LOG_ERR("Failed to free test_globals.rx_stats\n");
- ret = -1;
- }
- if (odp_shm_free(odp_shm_lookup("test_globals.tx_stats")) != 0) {
- LOG_ERR("Failed to free test_globals.tx_stats\n");
- ret = -1;
- }
-
- return ret;
-}
-
-static void usage(void)
-{
- printf("\nUsage: odp_pktio_perf [options]\n\n");
- printf(" -c, --count <number> CPU count\n");
- printf(" default: all available\n");
- printf(" -t, --txcount <number> Number of CPUs to use for TX\n");
- printf(" default: cpu_count+1/2\n");
- printf(" -b, --txbatch <length> Number of packets per TX batch\n");
- printf(" default: %d\n", BATCH_LEN_MAX);
- printf(" -p, --plain Plain input queue for packet RX\n");
- printf(" default: disabled (use scheduler)\n");
- printf(" -R, --rxbatch <length> Number of packets per RX batch\n");
- printf(" default: %d\n", BATCH_LEN_MAX);
- printf(" -l, --length <length> Additional payload length in bytes\n");
- printf(" default: 0\n");
- printf(" -r, --rate <number> Attempted packet rate in PPS\n");
- printf(" -i, --interface <list> List of interface names to use\n");
- printf(" -d, --duration <secs> Duration of each test iteration\n");
- printf(" -v, --verbose Print verbose information\n");
- printf(" -h, --help This help\n");
- printf("\n");
-}
-
-static void parse_args(int argc, char *argv[], test_args_t *args)
-{
- int opt;
- int long_index;
-
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"txcount", required_argument, NULL, 't'},
- {"txbatch", required_argument, NULL, 'b'},
- {"plain", no_argument, NULL, 'p'},
- {"rxbatch", required_argument, NULL, 'R'},
- {"length", required_argument, NULL, 'l'},
- {"rate", required_argument, NULL, 'r'},
- {"interface", required_argument, NULL, 'i'},
- {"duration", required_argument, NULL, 'd'},
- {"verbose", no_argument, NULL, 'v'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:t:b:pR:l:r:i:d:vh";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- args->cpu_count = 0; /* all CPUs */
- args->num_tx_workers = 0; /* defaults to cpu_count+1/2 */
- args->tx_batch_len = BATCH_LEN_MAX;
- args->rx_batch_len = BATCH_LEN_MAX;
- args->duration = 1;
- args->pps = RATE_SEARCH_INITIAL_PPS;
- args->search = 1;
- args->schedule = 1;
- args->verbose = 0;
-
- opterr = 0; /* do not issue errors on helper options */
-
- while (1) {
- opt = getopt_long(argc, argv, shortopts,
- longopts, &long_index);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'h':
- usage();
- exit(EXIT_SUCCESS);
- case 'c':
- args->cpu_count = atoi(optarg);
- break;
- case 't':
- args->num_tx_workers = atoi(optarg);
- break;
- case 'd':
- args->duration = atoi(optarg);
- break;
- case 'r':
- args->pps = atoi(optarg);
- args->search = 0;
- args->verbose = 1;
- break;
- case 'i':
- {
- char *token;
-
- args->if_str = malloc(strlen(optarg) + 1);
-
- if (!args->if_str)
- LOG_ABORT("Failed to alloc iface storage\n");
-
- strcpy(args->if_str, optarg);
- for (token = strtok(args->if_str, ",");
- token != NULL && args->num_ifaces < MAX_NUM_IFACES;
- token = strtok(NULL, ","))
- args->ifaces[args->num_ifaces++] = token;
- }
- break;
- case 'p':
- args->schedule = 0;
- break;
- case 'b':
- args->tx_batch_len = atoi(optarg);
- break;
- case 'R':
- args->rx_batch_len = atoi(optarg);
- break;
- case 'v':
- args->verbose = 1;
- break;
- case 'l':
- args->pkt_len = atoi(optarg);
- break;
- }
- }
-
- if (args->num_ifaces == 0) {
- args->ifaces[0] = "loop";
- args->num_ifaces = 1;
- }
-}
-
-int main(int argc, char **argv)
-{
- int ret;
- odp_shm_t shm;
- int max_thrs;
- odp_instance_t instance;
-
- if (odp_init_global(&instance, NULL, NULL) != 0)
- LOG_ABORT("Failed global init.\n");
-
- if (odp_init_local(instance, ODP_THREAD_CONTROL) != 0)
- LOG_ABORT("Failed local init.\n");
-
- shm = odp_shm_reserve("test_globals",
- sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
- gbl_args = odp_shm_addr(shm);
- if (gbl_args == NULL)
- LOG_ABORT("Shared memory reserve failed.\n");
- memset(gbl_args, 0, sizeof(test_globals_t));
-
- max_thrs = odp_thread_count_max();
-
- gbl_args->instance = instance;
- gbl_args->rx_stats_size = max_thrs * sizeof(pkt_rx_stats_t);
- gbl_args->tx_stats_size = max_thrs * sizeof(pkt_tx_stats_t);
-
- shm = odp_shm_reserve("test_globals.rx_stats",
- gbl_args->rx_stats_size,
- ODP_CACHE_LINE_SIZE, 0);
-
- gbl_args->rx_stats = odp_shm_addr(shm);
-
- if (gbl_args->rx_stats == NULL)
- LOG_ABORT("Shared memory reserve failed.\n");
-
- memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
-
- shm = odp_shm_reserve("test_globals.tx_stats",
- gbl_args->tx_stats_size,
- ODP_CACHE_LINE_SIZE, 0);
-
- gbl_args->tx_stats = odp_shm_addr(shm);
-
- if (gbl_args->tx_stats == NULL)
- LOG_ABORT("Shared memory reserve failed.\n");
-
- memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
-
- parse_args(argc, argv, &gbl_args->args);
-
- ret = test_init();
-
- if (ret == 0) {
- ret = run_test();
- test_term();
- }
-
- odp_term_local();
- odp_term_global(instance);
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_sched_latency.c b/test/common_plat/performance/odp_sched_latency.c
deleted file mode 100644
index 026f2f6c7..000000000
--- a/test/common_plat/performance/odp_sched_latency.c
+++ /dev/null
@@ -1,792 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_sched_latency.c ODP scheduling latency benchmark application
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include <test_debug.h>
-
-/* ODP main header */
-#include <odp_api.h>
-
-/* ODP helper for Linux apps */
-#include <odp/helper/odph_api.h>
-
-/* GNU lib C */
-#include <getopt.h>
-
-#define MAX_WORKERS 64 /**< Maximum number of worker threads */
-#define MAX_QUEUES 4096 /**< Maximum number of queues */
-#define EVENT_POOL_SIZE (1024 * 1024) /**< Event pool size */
-#define TEST_ROUNDS (4 * 1024 * 1024) /**< Test rounds for each thread */
-#define MAIN_THREAD 1 /**< Thread ID performing maintenance tasks */
-
-/* Default values for command line arguments */
-#define SAMPLE_EVENT_PER_PRIO 0 /**< Allocate a separate sample event for
- each priority */
-#define HI_PRIO_EVENTS 0 /**< Number of high priority events */
-#define LO_PRIO_EVENTS 32 /**< Number of low priority events */
-#define HI_PRIO_QUEUES 16 /**< Number of high priority queues */
-#define LO_PRIO_QUEUES 64 /**< Number of low priority queues */
-
-#define EVENTS_PER_HI_PRIO_QUEUE 0 /**< Alloc HI_PRIO_QUEUES x HI_PRIO_EVENTS
- events */
-#define EVENTS_PER_LO_PRIO_QUEUE 1 /**< Alloc LO_PRIO_QUEUES x LO_PRIO_EVENTS
- events */
-ODP_STATIC_ASSERT(HI_PRIO_QUEUES <= MAX_QUEUES, "Too many HI priority queues");
-ODP_STATIC_ASSERT(LO_PRIO_QUEUES <= MAX_QUEUES, "Too many LO priority queues");
-
-#define CACHE_ALIGN_ROUNDUP(x)\
- ((ODP_CACHE_LINE_SIZE) * \
- (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE)))
-
-/* Test priorities */
-#define NUM_PRIOS 2 /**< Number of tested priorities */
-#define HI_PRIO 0
-#define LO_PRIO 1
-
-/** Test event types */
-typedef enum {
- WARM_UP, /**< Warm up event */
- COOL_DOWN,/**< Last event on queue */
- TRAFFIC, /**< Event used only as traffic load */
- SAMPLE /**< Event used to measure latency */
-} event_type_t;
-
-/** Test event */
-typedef struct {
- uint64_t ts; /**< Send timestamp */
- event_type_t type; /**< Message type */
- int src_idx[NUM_PRIOS]; /**< Source ODP queue */
- int prio; /**< Source queue priority */
-} test_event_t;
-
-/** Test arguments */
-typedef struct {
- int cpu_count; /**< CPU count */
- odp_schedule_sync_t sync_type; /**< Scheduler sync type */
- struct {
- int queues; /**< Number of scheduling queues */
- int events; /**< Number of events */
- odp_bool_t events_per_queue; /**< Allocate 'queues' x 'events'
- test events */
- } prio[NUM_PRIOS];
- odp_bool_t sample_per_prio; /**< Allocate a separate sample event for
- each priority */
-} test_args_t;
-
-/** Latency measurements statistics */
-typedef struct {
- uint64_t events; /**< Total number of received events */
- uint64_t sample_events; /**< Number of received sample events */
- uint64_t tot; /**< Total event latency. Sum of all events. */
- uint64_t min; /**< Minimum event latency */
- uint64_t max; /**< Maximum event latency */
-} test_stat_t;
-
-/** Performance test statistics (per core) */
-typedef union {
- test_stat_t prio[NUM_PRIOS]; /**< Test statistics per priority */
-
- uint8_t pad[CACHE_ALIGN_ROUNDUP(NUM_PRIOS * sizeof(test_stat_t))];
-} core_stat_t ODP_ALIGNED_CACHE;
-
-/** Test global variables */
-typedef struct {
- core_stat_t core_stat[MAX_WORKERS]; /**< Core specific stats */
- odp_barrier_t barrier; /**< Barrier for thread synchronization */
- odp_pool_t pool; /**< Pool for allocating test events */
- test_args_t args; /**< Parsed command line arguments */
- odp_queue_t queue[NUM_PRIOS][MAX_QUEUES]; /**< Scheduled queues */
-} test_globals_t;
-
-/**
- * Clear all scheduled queues.
- *
- * Use special cool_down event to guarantee that queue is drained.
- */
-static void clear_sched_queues(test_globals_t *globals)
-{
- odp_event_t ev;
- odp_buffer_t buf;
- test_event_t *event;
- int i, j;
- odp_queue_t fromq;
-
- /* Allocate the cool_down event. */
- buf = odp_buffer_alloc(globals->pool);
- if (buf == ODP_BUFFER_INVALID)
- LOG_ABORT("Buffer alloc failed.\n");
-
- event = odp_buffer_addr(buf);
- event->type = COOL_DOWN;
- ev = odp_buffer_to_event(buf);
-
- for (i = 0; i < NUM_PRIOS; i++) {
- for (j = 0; j < globals->args.prio[i].queues; j++) {
- /* Enqueue cool_down event on each queue. */
- if (odp_queue_enq(globals->queue[i][j], ev))
- LOG_ABORT("Queue enqueue failed.\n");
-
- /* Invoke scheduler until cool_down event has been
- * received. */
- while (1) {
- ev = odp_schedule(NULL, ODP_SCHED_WAIT);
- buf = odp_buffer_from_event(ev);
- event = odp_buffer_addr(buf);
- if (event->type == COOL_DOWN)
- break;
- odp_event_free(ev);
- }
- }
- }
-
- /* Free the cool_down event. */
- odp_event_free(ev);
-
- /* Call odp_schedule() to trigger a release of any scheduler context. */
- ev = odp_schedule(&fromq, ODP_SCHED_NO_WAIT);
- if (ev != ODP_EVENT_INVALID)
- LOG_ABORT("Queue %" PRIu64 " not empty.\n",
- odp_queue_to_u64(fromq));
-}
-
-/**
- * Enqueue events into queues
- *
- * @param prio Queue priority (HI_PRIO/LO_PRIO)
- * @param num_queues Number of queues
- * @param num_events Number of 'TRAFFIC' events
- * @param num_samples Number of 'SAMPLE' events
- * @param div_events If true, divide 'num_events' between 'num_queues'. if
- * false, enqueue 'num_events' to each queue.
- * @param globals Test shared data
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static int enqueue_events(int prio, int num_queues, int num_events,
- int num_samples, odp_bool_t div_events,
- test_globals_t *globals)
-{
- odp_buffer_t buf[num_events + num_samples];
- odp_event_t ev[num_events + num_samples];
- odp_queue_t queue;
- test_event_t *event;
- int i, j, ret;
- int enq_events;
- int events_per_queue;
- int tot_events;
- int rdy_events = 0;
-
- tot_events = num_events + num_samples;
-
- if (!num_queues || !tot_events)
- return 0;
-
- events_per_queue = tot_events;
- if (div_events)
- events_per_queue = (tot_events + num_queues - 1) / num_queues;
-
- for (i = 0; i < num_queues; i++) {
- queue = globals->queue[prio][i];
-
- ret = odp_buffer_alloc_multi(globals->pool, buf,
- events_per_queue);
- if (ret != events_per_queue) {
- LOG_ERR("Buffer alloc failed. Try increasing EVENT_POOL_SIZE.\n");
- ret = ret < 0 ? 0 : ret;
- odp_buffer_free_multi(buf, ret);
- return -1;
- }
- for (j = 0; j < events_per_queue; j++) {
- if (!odp_buffer_is_valid(buf[j])) {
- LOG_ERR("Buffer alloc failed\n");
- odp_buffer_free_multi(buf, events_per_queue);
- return -1;
- }
-
- event = odp_buffer_addr(buf[j]);
- memset(event, 0, sizeof(test_event_t));
-
- /* Latency isn't measured from the first processing
- * round. */
- if (num_samples > 0) {
- event->type = WARM_UP;
- num_samples--;
- } else {
- event->type = TRAFFIC;
- }
- event->src_idx[prio] = i;
- event->prio = prio;
- ev[j] = odp_buffer_to_event(buf[j]);
- }
-
- enq_events = 0;
- do {
- ret = odp_queue_enq_multi(queue, &ev[enq_events],
- events_per_queue -
- enq_events);
- if (ret < 0) {
- LOG_ERR("Queue enqueue failed.\n");
- return -1;
- }
- enq_events += ret;
- } while (enq_events < events_per_queue);
-
- rdy_events += events_per_queue;
- if (div_events && rdy_events >= tot_events)
- return 0;
- }
- return 0;
-}
-
-/**
- * Print latency measurement results
- *
- * @param globals Test shared data
- */
-static void print_results(test_globals_t *globals)
-{
- test_stat_t *lat;
- odp_schedule_sync_t stype;
- test_stat_t total;
- test_args_t *args;
- uint64_t avg;
- int i, j;
-
- args = &globals->args;
- stype = globals->args.sync_type;
-
- printf("\n%s queue scheduling latency\n",
- (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" :
- ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL"));
-
- printf(" LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues);
- if (args->prio[LO_PRIO].events_per_queue)
- printf(" LO_PRIO event per queue: %i\n",
- args->prio[LO_PRIO].events);
- else
- printf(" LO_PRIO events: %i\n", args->prio[LO_PRIO].events);
-
- printf(" HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues);
- if (args->prio[HI_PRIO].events_per_queue)
- printf(" HI_PRIO event per queue: %i\n\n",
- args->prio[HI_PRIO].events);
- else
- printf(" HI_PRIO events: %i\n\n", args->prio[HI_PRIO].events);
-
- for (i = 0; i < NUM_PRIOS; i++) {
- memset(&total, 0, sizeof(test_stat_t));
- total.min = UINT64_MAX;
-
- printf("%s priority\n"
- "Thread Avg[ns] Min[ns] Max[ns] Samples Total\n"
- "---------------------------------------------------------------\n",
- i == HI_PRIO ? "HIGH" : "LOW");
- for (j = 1; j <= args->cpu_count; j++) {
- lat = &globals->core_stat[j].prio[i];
-
- if (lat->sample_events == 0) {
- printf("%-8d N/A\n", j);
- continue;
- }
-
- if (lat->max > total.max)
- total.max = lat->max;
- if (lat->min < total.min)
- total.min = lat->min;
- total.tot += lat->tot;
- total.sample_events += lat->sample_events;
- total.events += lat->events;
-
- avg = lat->events ? lat->tot / lat->sample_events : 0;
- printf("%-8d %-10" PRIu64 " %-10" PRIu64 " "
- "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n",
- j, avg, lat->min, lat->max, lat->sample_events,
- lat->events);
- }
- printf("---------------------------------------------------------------\n");
- if (total.sample_events == 0) {
- printf("Total N/A\n\n");
- continue;
- }
- avg = total.events ? total.tot / total.sample_events : 0;
- printf("Total %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " "
- "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, total.min,
- total.max, total.sample_events, total.events);
- }
-}
-
-/**
- * Measure latency of scheduled ODP events
- *
- * Schedule and enqueue events until 'TEST_ROUNDS' events have been processed.
- * Scheduling latency is measured only from type 'SAMPLE' events. Other events
- * are simply enqueued back to the scheduling queues.
- *
- * For 'TRAFFIC' type events the destination queue is selected from the same
- * priority class as source queue. 'SAMPLE' type event may change priority
- * depending on the command line arguments.
- *
- * @param thr Thread ID
- * @param globals Test shared data
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static int test_schedule(int thr, test_globals_t *globals)
-{
- odp_event_t ev;
- odp_buffer_t buf;
- odp_queue_t src_queue;
- odp_queue_t dst_queue;
- uint64_t latency;
- uint32_t i;
- test_event_t *event;
- test_stat_t *stats;
- int dst_idx;
-
- memset(&globals->core_stat[thr], 0, sizeof(core_stat_t));
- globals->core_stat[thr].prio[HI_PRIO].min = UINT64_MAX;
- globals->core_stat[thr].prio[LO_PRIO].min = UINT64_MAX;
-
- for (i = 0; i < TEST_ROUNDS; i++) {
- ev = odp_schedule(&src_queue, ODP_SCHED_WAIT);
-
- buf = odp_buffer_from_event(ev);
- event = odp_buffer_addr(buf);
-
- stats = &globals->core_stat[thr].prio[event->prio];
-
- if (event->type == SAMPLE) {
- latency = odp_time_to_ns(odp_time_global()) - event->ts;
-
- if (latency > stats->max)
- stats->max = latency;
- if (latency < stats->min)
- stats->min = latency;
- stats->tot += latency;
- stats->sample_events++;
-
- /* Move sample event to a different priority */
- if (!globals->args.sample_per_prio &&
- globals->args.prio[!event->prio].queues)
- event->prio = !event->prio;
- }
-
- if (odp_unlikely(event->type == WARM_UP))
- event->type = SAMPLE;
- else
- stats->events++;
-
- /* Move event to next queue */
- dst_idx = event->src_idx[event->prio] + 1;
- if (dst_idx >= globals->args.prio[event->prio].queues)
- dst_idx = 0;
- event->src_idx[event->prio] = dst_idx;
- dst_queue = globals->queue[event->prio][dst_idx];
-
- if (event->type == SAMPLE)
- event->ts = odp_time_to_ns(odp_time_global());
-
- if (odp_queue_enq(dst_queue, ev)) {
- LOG_ERR("[%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- /* Clear possible locally stored buffers */
- odp_schedule_pause();
-
- while (1) {
- ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT);
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- if (odp_queue_enq(src_queue, ev)) {
- LOG_ERR("[%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- odp_schedule_resume();
-
- odp_barrier_wait(&globals->barrier);
-
- if (thr == MAIN_THREAD) {
- clear_sched_queues(globals);
- print_results(globals);
- }
-
- return 0;
-}
-
-/**
- * Worker thread
- *
- * @param arg Arguments
- *
- * @retval 0 on success
- * @retval -1 on failure
- */
-static int run_thread(void *arg ODP_UNUSED)
-{
- odp_shm_t shm;
- test_globals_t *globals;
- test_args_t *args;
- int thr;
- int sample_events = 0;
-
- thr = odp_thread_id();
-
- shm = odp_shm_lookup("test_globals");
- globals = odp_shm_addr(shm);
-
- if (globals == NULL) {
- LOG_ERR("Shared mem lookup failed\n");
- return -1;
- }
-
- if (thr == MAIN_THREAD) {
- args = &globals->args;
-
- if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues,
- args->prio[HI_PRIO].events, 1,
- !args->prio[HI_PRIO].events_per_queue,
- globals))
- return -1;
-
- if (!args->prio[HI_PRIO].queues || args->sample_per_prio)
- sample_events = 1;
-
- if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues,
- args->prio[LO_PRIO].events, sample_events,
- !args->prio[LO_PRIO].events_per_queue,
- globals))
- return -1;
- }
-
- odp_barrier_wait(&globals->barrier);
-
- if (test_schedule(thr, globals))
- return -1;
-
- return 0;
-}
-
-/**
- * Print usage information
- */
-static void usage(void)
-{
- printf("\n"
- "OpenDataPlane scheduler latency benchmark application.\n"
- "\n"
- "Usage: ./odp_sched_latency [options]\n"
- "Optional OPTIONS:\n"
- " -c, --count <number> CPU count\n"
- " -l, --lo-prio-queues <number> Number of low priority scheduled queues\n"
- " -t, --hi-prio-queues <number> Number of high priority scheduled queues\n"
- " -m, --lo-prio-events-per-queue <number> Number of events per low priority queue\n"
- " -n, --hi-prio-events-per-queue <number> Number of events per high priority queues\n"
- " -o, --lo-prio-events <number> Total number of low priority events (overrides the\n"
- " number of events per queue)\n"
- " -p, --hi-prio-events <number> Total number of high priority events (overrides the\n"
- " number of events per queue)\n"
- " -r --sample-per-prio Allocate a separate sample event for each priority. By default\n"
- " a single sample event is used and its priority is changed after\n"
- " each processing round.\n"
- " -s, --sync Scheduled queues' sync type\n"
- " 0: ODP_SCHED_SYNC_PARALLEL (default)\n"
- " 1: ODP_SCHED_SYNC_ATOMIC\n"
- " 2: ODP_SCHED_SYNC_ORDERED\n"
- " -h, --help Display help and exit.\n\n"
- );
-}
-
-/**
- * Parse arguments
- *
- * @param argc Argument count
- * @param argv Argument vector
- * @param args Test arguments
- */
-static void parse_args(int argc, char *argv[], test_args_t *args)
-{
- int opt;
- int long_index;
- int i;
-
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"lo-prio-queues", required_argument, NULL, 'l'},
- {"hi-prio-queues", required_argument, NULL, 't'},
- {"lo-prio-events-per-queue", required_argument, NULL, 'm'},
- {"hi-prio-events-per-queue", required_argument, NULL, 'n'},
- {"lo-prio-events", required_argument, NULL, 'o'},
- {"hi-prio-events", required_argument, NULL, 'p'},
- {"sample-per-prio", no_argument, NULL, 'r'},
- {"sync", required_argument, NULL, 's'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:s:l:t:m:n:o:p:rh";
-
- /* Let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- args->sync_type = ODP_SCHED_SYNC_PARALLEL;
- args->sample_per_prio = SAMPLE_EVENT_PER_PRIO;
- args->prio[LO_PRIO].queues = LO_PRIO_QUEUES;
- args->prio[HI_PRIO].queues = HI_PRIO_QUEUES;
- args->prio[LO_PRIO].events = LO_PRIO_EVENTS;
- args->prio[HI_PRIO].events = HI_PRIO_EVENTS;
- args->prio[LO_PRIO].events_per_queue = EVENTS_PER_LO_PRIO_QUEUE;
- args->prio[HI_PRIO].events_per_queue = EVENTS_PER_HI_PRIO_QUEUE;
-
- opterr = 0; /* Do not issue errors on helper options */
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'c':
- args->cpu_count = atoi(optarg);
- break;
- case 'l':
- args->prio[LO_PRIO].queues = atoi(optarg);
- break;
- case 't':
- args->prio[HI_PRIO].queues = atoi(optarg);
- break;
- case 'm':
- args->prio[LO_PRIO].events = atoi(optarg);
- args->prio[LO_PRIO].events_per_queue = 1;
- break;
- case 'n':
- args->prio[HI_PRIO].events = atoi(optarg);
- args->prio[HI_PRIO].events_per_queue = 1;
- break;
- case 'o':
- args->prio[LO_PRIO].events = atoi(optarg);
- args->prio[LO_PRIO].events_per_queue = 0;
- break;
- case 'p':
- args->prio[HI_PRIO].events = atoi(optarg);
- args->prio[HI_PRIO].events_per_queue = 0;
- break;
- case 's':
- i = atoi(optarg);
- if (i == 1)
- args->sync_type = ODP_SCHED_SYNC_ATOMIC;
- else if (i == 2)
- args->sync_type = ODP_SCHED_SYNC_ORDERED;
- else
- args->sync_type = ODP_SCHED_SYNC_PARALLEL;
- break;
- case 'r':
- args->sample_per_prio = 1;
- break;
- case 'h':
- usage();
- exit(EXIT_SUCCESS);
- break;
-
- default:
- break;
- }
- }
-
- /* Make sure arguments are valid */
- if (args->cpu_count > MAX_WORKERS)
- args->cpu_count = MAX_WORKERS;
- if (args->prio[LO_PRIO].queues > MAX_QUEUES)
- args->prio[LO_PRIO].queues = MAX_QUEUES;
- if (args->prio[HI_PRIO].queues > MAX_QUEUES)
- args->prio[HI_PRIO].queues = MAX_QUEUES;
- if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) {
- printf("No queues configured\n");
- usage();
- exit(EXIT_FAILURE);
- }
-}
-
-/**
- * Test main function
- */
-int main(int argc, char *argv[])
-{
- odp_instance_t instance;
- odph_odpthread_t *thread_tbl;
- odph_odpthread_params_t thr_params;
- odp_cpumask_t cpumask;
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_shm_t shm;
- test_globals_t *globals;
- test_args_t args;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- int i, j;
- int ret = 0;
- int num_workers = 0;
-
- printf("\nODP scheduling latency benchmark starts\n\n");
-
- memset(&args, 0, sizeof(args));
- parse_args(argc, argv, &args);
-
- /* ODP global init */
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("ODP global init failed.\n");
- return -1;
- }
-
- /*
- * Init this thread. It makes also ODP calls when
- * setting up resources for worker threads.
- */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("ODP global init failed.\n");
- return -1;
- }
-
- odp_sys_info_print();
-
- /* Get default worker cpumask */
- if (args.cpu_count)
- num_workers = args.cpu_count;
-
- num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
- args.cpu_count = num_workers;
-
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- printf("CPU mask info:\n");
- printf(" Worker threads: %i\n", num_workers);
- printf(" First CPU: %i\n", odp_cpumask_first(&cpumask));
- printf(" CPU mask: %s\n", cpumaskstr);
-
- thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers);
- if (!thread_tbl) {
- LOG_ERR("no memory for thread_tbl\n");
- return -1;
- }
-
- shm = odp_shm_reserve("test_globals",
- sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
- if (shm == ODP_SHM_INVALID) {
- LOG_ERR("Shared memory reserve failed.\n");
- return -1;
- }
-
- globals = odp_shm_addr(shm);
- memset(globals, 0, sizeof(test_globals_t));
- memcpy(&globals->args, &args, sizeof(test_args_t));
-
- /*
- * Create event pool
- */
- odp_pool_param_init(&params);
- params.buf.size = sizeof(test_event_t);
- params.buf.align = 0;
- params.buf.num = EVENT_POOL_SIZE;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create("event_pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- LOG_ERR("Pool create failed.\n");
- return -1;
- }
- globals->pool = pool;
-
- /*
- * Create queues for schedule test
- */
- for (i = 0; i < NUM_PRIOS; i++) {
- char name[] = "sched_XX_YY";
- odp_queue_t queue;
- odp_queue_param_t param;
- int prio;
-
- if (i == HI_PRIO)
- prio = ODP_SCHED_PRIO_HIGHEST;
- else
- prio = ODP_SCHED_PRIO_LOWEST;
-
- name[6] = '0' + (prio / 10);
- name[7] = '0' + prio - (10 * (prio / 10));
-
- odp_queue_param_init(&param);
- param.type = ODP_QUEUE_TYPE_SCHED;
- param.sched.prio = prio;
- param.sched.sync = args.sync_type;
- param.sched.group = ODP_SCHED_GROUP_ALL;
-
- for (j = 0; j < args.prio[i].queues; j++) {
- name[9] = '0' + j / 10;
- name[10] = '0' + j - 10 * (j / 10);
-
- queue = odp_queue_create(name, &param);
-
- if (queue == ODP_QUEUE_INVALID) {
- LOG_ERR("Scheduled queue create failed.\n");
- return -1;
- }
-
- globals->queue[i][j] = queue;
- }
- }
-
- odp_barrier_init(&globals->barrier, num_workers);
-
- /* Create and launch worker threads */
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
- thr_params.start = run_thread;
- thr_params.arg = NULL;
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-
- /* Wait for worker threads to terminate */
- odph_odpthreads_join(thread_tbl);
- free(thread_tbl);
-
- printf("ODP scheduling latency test complete\n\n");
-
- for (i = 0; i < NUM_PRIOS; i++) {
- odp_queue_t queue;
- int num_queues;
-
- num_queues = args.prio[i].queues;
-
- for (j = 0; j < num_queues; j++) {
- queue = globals->queue[i][j];
- ret += odp_queue_destroy(queue);
- }
- }
-
- ret += odp_shm_free(shm);
- ret += odp_pool_destroy(pool);
- ret += odp_term_local();
- ret += odp_term_global(instance);
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_sched_latency_run.sh b/test/common_plat/performance/odp_sched_latency_run.sh
deleted file mode 100755
index 6048f5816..000000000
--- a/test/common_plat/performance/odp_sched_latency_run.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Script that passes command line arguments to odp_sched_latency test when
-# launched by 'make check'
-
-TEST_DIR="${TEST_DIR:-$(dirname $0)}"
-ALL=0
-
-run()
-{
- echo odp_sched_latency_run starts requesting $1 worker threads
- echo ===============================================
-
- $TEST_DIR/odp_sched_latency${EXEEXT} -c $1 || exit $?
-}
-
-run 1
-run 5
-run 8
-run 11
-run $ALL
-
-exit 0
diff --git a/test/common_plat/performance/odp_scheduling.c b/test/common_plat/performance/odp_scheduling.c
deleted file mode 100644
index 38e76257b..000000000
--- a/test/common_plat/performance/odp_scheduling.c
+++ /dev/null
@@ -1,987 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_example.c ODP example application
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include <test_debug.h>
-
-/* ODP main header */
-#include <odp_api.h>
-
-/* ODP helper for Linux apps */
-#include <odp/helper/odph_api.h>
-
-/* Needs librt*/
-#include <time.h>
-
-/* GNU lib C */
-#include <getopt.h>
-
-#define NUM_MSG (512 * 1024) /**< Number of msg in pool */
-#define MAX_ALLOCS 32 /**< Alloc burst size */
-#define QUEUES_PER_PRIO 64 /**< Queue per priority */
-#define NUM_PRIOS 2 /**< Number of tested priorities */
-#define QUEUE_ROUNDS (512 * 1024) /**< Queue test rounds */
-#define ALLOC_ROUNDS (1024 * 1024) /**< Alloc test rounds */
-#define MULTI_BUFS_MAX 4 /**< Buffer burst size */
-#define TEST_SEC 2 /**< Time test duration in sec */
-#define STATS_PER_LINE 8 /**< Stats per printed line */
-
-/** Dummy message */
-typedef struct {
- int msg_id; /**< Message ID */
- int seq; /**< Sequence number */
-} test_message_t;
-
-#define MSG_HELLO 1 /**< Hello */
-#define MSG_ACK 2 /**< Ack */
-
-/** Test arguments */
-typedef struct {
- int cpu_count; /**< CPU count */
- int fairness; /**< Check fairness */
-} test_args_t;
-
-typedef struct {
- uint64_t num_ev;
-
- /* Round up the struct size to cache line size */
- uint8_t pad[ODP_CACHE_LINE_SIZE - sizeof(uint64_t)];
-} queue_context_t ODP_ALIGNED_CACHE;
-
-/** Test global variables */
-typedef struct {
- odp_barrier_t barrier;
- odp_spinlock_t lock;
- odp_pool_t pool;
- int first_thr;
- test_args_t args;
- odp_queue_t queue[NUM_PRIOS][QUEUES_PER_PRIO];
- queue_context_t queue_ctx[NUM_PRIOS][QUEUES_PER_PRIO];
-} test_globals_t;
-
-/* Prints and initializes queue statistics */
-static void print_stats(int prio, test_globals_t *globals)
-{
- int i, j, k;
-
- if (prio == ODP_SCHED_PRIO_HIGHEST)
- i = 0;
- else
- i = 1;
-
- printf("\nQueue fairness\n-----+--------\n");
-
- for (j = 0; j < QUEUES_PER_PRIO;) {
- printf(" %2i | ", j);
-
- for (k = 0; k < STATS_PER_LINE - 1; k++) {
- printf(" %8" PRIu64,
- globals->queue_ctx[i][j].num_ev);
- globals->queue_ctx[i][j++].num_ev = 0;
- }
-
- printf(" %8" PRIu64 "\n", globals->queue_ctx[i][j].num_ev);
- globals->queue_ctx[i][j++].num_ev = 0;
- }
-
- printf("\n");
-}
-
-/**
- * @internal Clear all scheduled queues. Retry to be sure that all
- * buffers have been scheduled.
- */
-static void clear_sched_queues(void)
-{
- odp_event_t ev;
-
- while (1) {
- ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- odp_event_free(ev);
- }
-}
-
-/**
- * @internal Enqueue events into queues
- *
- * @param thr Thread
- * @param prio Queue priority
- * @param num_queues Number of queues
- * @param num_events Number of events
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int enqueue_events(int thr, int prio, int num_queues, int num_events,
- test_globals_t *globals)
-{
- odp_buffer_t buf[num_events];
- odp_event_t ev[num_events];
- odp_queue_t queue;
- int i, j, k, ret;
-
- if (prio == ODP_SCHED_PRIO_HIGHEST)
- i = 0;
- else
- i = 1;
-
- /* Alloc and enqueue a buffer per queue */
- for (j = 0; j < num_queues; j++) {
- queue = globals->queue[i][j];
-
- ret = odp_buffer_alloc_multi(globals->pool, buf, num_events);
- if (ret != num_events) {
- LOG_ERR(" [%i] buffer alloc failed\n", thr);
- ret = ret < 0 ? 0 : ret;
- odp_buffer_free_multi(buf, ret);
- return -1;
- }
- for (k = 0; k < num_events; k++) {
- if (!odp_buffer_is_valid(buf[k])) {
- LOG_ERR(" [%i] buffer alloc failed\n", thr);
- odp_buffer_free_multi(buf, num_events);
- return -1;
- }
- ev[k] = odp_buffer_to_event(buf[k]);
- }
-
- ret = odp_queue_enq_multi(queue, ev, num_events);
- if (ret != num_events) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- ret = ret < 0 ? 0 : ret;
- odp_buffer_free_multi(&buf[ret], num_events - ret);
- return -1;
- }
- }
-
- return 0;
-}
-
-/**
- * @internal Test single buffer alloc and free
- *
- * @param thr Thread
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_alloc_single(int thr, test_globals_t *globals)
-{
- int i;
- odp_buffer_t temp_buf;
- uint64_t c1, c2, cycles;
-
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < ALLOC_ROUNDS; i++) {
- temp_buf = odp_buffer_alloc(globals->pool);
-
- if (!odp_buffer_is_valid(temp_buf)) {
- LOG_ERR(" [%i] alloc_single failed\n", thr);
- return -1;
- }
-
- odp_buffer_free(temp_buf);
- }
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
- cycles = cycles / ALLOC_ROUNDS;
-
- printf(" [%i] alloc_sng alloc+free %6" PRIu64 " CPU cycles\n",
- thr, cycles);
-
- return 0;
-}
-
-/**
- * @internal Test multiple buffers alloc and free
- *
- * @param thr Thread
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_alloc_multi(int thr, test_globals_t *globals)
-{
- int i, j, ret;
- odp_buffer_t temp_buf[MAX_ALLOCS];
- uint64_t c1, c2, cycles;
-
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < ALLOC_ROUNDS; i++) {
- ret = odp_buffer_alloc_multi(globals->pool, temp_buf,
- MAX_ALLOCS);
- if (ret != MAX_ALLOCS) {
- LOG_ERR(" [%i] buffer alloc failed\n", thr);
- ret = ret < 0 ? 0 : ret;
- odp_buffer_free_multi(temp_buf, ret);
- return -1;
- }
-
- for (j = 0; j < MAX_ALLOCS; j++) {
- if (!odp_buffer_is_valid(temp_buf[j])) {
- LOG_ERR(" [%i] alloc_multi failed\n", thr);
- odp_buffer_free_multi(temp_buf, MAX_ALLOCS);
- return -1;
- }
- }
- odp_buffer_free_multi(temp_buf, MAX_ALLOCS);
- }
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
- cycles = cycles / (ALLOC_ROUNDS * MAX_ALLOCS);
-
- printf(" [%i] alloc_multi alloc+free %6" PRIu64 " CPU cycles\n",
- thr, cycles);
-
- return 0;
-}
-
-/**
- * @internal Test plain queues
- *
- * Enqueue to and dequeue to/from a single shared queue.
- *
- * @param thr Thread
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_plain_queue(int thr, test_globals_t *globals)
-{
- odp_event_t ev;
- odp_buffer_t buf;
- test_message_t *t_msg;
- odp_queue_t queue;
- uint64_t c1, c2, cycles;
- int i, j;
-
- /* Alloc test message */
- buf = odp_buffer_alloc(globals->pool);
-
- if (!odp_buffer_is_valid(buf)) {
- LOG_ERR(" [%i] buffer alloc failed\n", thr);
- return -1;
- }
-
- /* odp_buffer_print(buf); */
-
- t_msg = odp_buffer_addr(buf);
- t_msg->msg_id = MSG_HELLO;
- t_msg->seq = 0;
-
- queue = odp_queue_lookup("plain_queue");
-
- if (queue == ODP_QUEUE_INVALID) {
- printf(" [%i] Queue lookup failed.\n", thr);
- return -1;
- }
-
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < QUEUE_ROUNDS; i++) {
- ev = odp_buffer_to_event(buf);
-
- if (odp_queue_enq(queue, ev)) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- odp_buffer_free(buf);
- return -1;
- }
-
- /* When enqueue and dequeue are decoupled (e.g. not using a
- * common lock), an enqueued event may not be immediately
- * visible to dequeue. So we just try again for a while. */
- for (j = 0; j < 100; j++) {
- ev = odp_queue_deq(queue);
- if (ev != ODP_EVENT_INVALID)
- break;
- odp_cpu_pause();
- }
-
- buf = odp_buffer_from_event(ev);
-
- if (!odp_buffer_is_valid(buf)) {
- LOG_ERR(" [%i] Queue empty.\n", thr);
- return -1;
- }
- }
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
- cycles = cycles / QUEUE_ROUNDS;
-
- printf(" [%i] plain_queue enq+deq %6" PRIu64 " CPU cycles\n",
- thr, cycles);
-
- odp_buffer_free(buf);
- return 0;
-}
-
-/**
- * @internal Test scheduling of a single queue - with odp_schedule()
- *
- * Enqueue a buffer to the shared queue. Schedule and enqueue the received
- * buffer back into the queue.
- *
- * @param str Test case name string
- * @param thr Thread
- * @param prio Priority
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_schedule_single(const char *str, int thr,
- int prio, test_globals_t *globals)
-{
- odp_event_t ev;
- odp_queue_t queue;
- uint64_t c1, c2, cycles;
- uint32_t i;
- uint32_t tot;
-
- if (enqueue_events(thr, prio, 1, 1, globals))
- return -1;
-
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < QUEUE_ROUNDS; i++) {
- ev = odp_schedule(&queue, ODP_SCHED_WAIT);
-
- if (odp_queue_enq(queue, ev)) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- /* Clear possible locally stored buffers */
- odp_schedule_pause();
-
- tot = i;
-
- while (1) {
- ev = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- tot++;
-
- if (odp_queue_enq(queue, ev)) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- odp_schedule_resume();
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
-
- odp_barrier_wait(&globals->barrier);
- clear_sched_queues();
-
- cycles = cycles / tot;
-
- printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles);
-
- return 0;
-}
-
-/**
- * @internal Test scheduling of multiple queues - with odp_schedule()
- *
- * Enqueue a buffer to each queue. Schedule and enqueue the received
- * buffer back into the queue it came from.
- *
- * @param str Test case name string
- * @param thr Thread
- * @param prio Priority
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_schedule_many(const char *str, int thr,
- int prio, test_globals_t *globals)
-{
- odp_event_t ev;
- odp_queue_t queue;
- uint64_t c1, c2, cycles;
- uint32_t i;
- uint32_t tot;
-
- if (enqueue_events(thr, prio, QUEUES_PER_PRIO, 1, globals))
- return -1;
-
- /* Start sched-enq loop */
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < QUEUE_ROUNDS; i++) {
- ev = odp_schedule(&queue, ODP_SCHED_WAIT);
-
- if (odp_queue_enq(queue, ev)) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- /* Clear possible locally stored buffers */
- odp_schedule_pause();
-
- tot = i;
-
- while (1) {
- ev = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
-
- if (ev == ODP_EVENT_INVALID)
- break;
-
- tot++;
-
- if (odp_queue_enq(queue, ev)) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- odp_event_free(ev);
- return -1;
- }
- }
-
- odp_schedule_resume();
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
-
- odp_barrier_wait(&globals->barrier);
- clear_sched_queues();
-
- cycles = cycles / tot;
-
- printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles);
-
- return 0;
-}
-
-/**
- * @internal Test scheduling of multiple queues with multi_sched and multi_enq
- *
- * @param str Test case name string
- * @param thr Thread
- * @param prio Priority
- * @param globals Test shared data
- *
- * @return 0 if successful
- */
-static int test_schedule_multi(const char *str, int thr,
- int prio, test_globals_t *globals)
-{
- odp_event_t ev[MULTI_BUFS_MAX];
- odp_queue_t queue;
- uint64_t c1, c2, cycles;
- int i;
- int num;
- uint32_t tot = 0;
-
- if (enqueue_events(thr, prio, QUEUES_PER_PRIO, MULTI_BUFS_MAX, globals))
- return -1;
-
- /* Start sched-enq loop */
- c1 = odp_cpu_cycles();
-
- for (i = 0; i < QUEUE_ROUNDS; i++) {
- num = odp_schedule_multi(&queue, ODP_SCHED_WAIT, ev,
- MULTI_BUFS_MAX);
-
- tot += num;
-
- if (globals->args.fairness) {
- queue_context_t *queue_ctx;
-
- queue_ctx = odp_queue_context(queue);
- queue_ctx->num_ev += num;
- }
-
- /* Assume we can enqueue all events */
- if (odp_queue_enq_multi(queue, ev, num) != num) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- return -1;
- }
- }
-
- /* Clear possible locally stored events */
- odp_schedule_pause();
-
- while (1) {
- num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, ev,
- MULTI_BUFS_MAX);
-
- if (num == 0)
- break;
-
- tot += num;
-
- if (globals->args.fairness) {
- queue_context_t *queue_ctx;
-
- queue_ctx = odp_queue_context(queue);
- queue_ctx->num_ev += num;
- }
-
- /* Assume we can enqueue all events */
- if (odp_queue_enq_multi(queue, ev, num) != num) {
- LOG_ERR(" [%i] Queue enqueue failed.\n", thr);
- return -1;
- }
- }
-
- odp_schedule_resume();
-
- c2 = odp_cpu_cycles();
- cycles = odp_cpu_cycles_diff(c2, c1);
-
- odp_barrier_wait(&globals->barrier);
- clear_sched_queues();
-
- if (tot)
- cycles = cycles / tot;
- else
- cycles = 0;
-
- printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles);
-
- odp_barrier_wait(&globals->barrier);
-
- if (globals->args.fairness && globals->first_thr == thr)
- print_stats(prio, globals);
-
- return 0;
-}
-
-/**
- * @internal Worker thread
- *
- * @param arg Arguments
- *
- * @return non zero on failure
- */
-static int run_thread(void *arg ODP_UNUSED)
-{
- int thr;
- odp_shm_t shm;
- test_globals_t *globals;
- odp_barrier_t *barrier;
-
- thr = odp_thread_id();
-
- printf("Thread %i starts on CPU %i\n", thr, odp_cpu_id());
-
- shm = odp_shm_lookup("test_globals");
- globals = odp_shm_addr(shm);
-
- if (globals == NULL) {
- LOG_ERR("Shared mem lookup failed\n");
- return -1;
- }
-
- barrier = &globals->barrier;
-
- /*
- * Test barriers back-to-back
- */
- odp_barrier_wait(barrier);
- odp_barrier_wait(barrier);
- odp_barrier_wait(barrier);
- odp_barrier_wait(barrier);
- odp_barrier_wait(barrier);
-
- /* Select which thread is the first_thr */
- while (globals->first_thr < 0) {
- if (odp_spinlock_trylock(&globals->lock)) {
- globals->first_thr = thr;
- odp_spinlock_unlock(&globals->lock);
- }
- }
-
- odp_barrier_wait(barrier);
-
- if (test_alloc_single(thr, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_alloc_multi(thr, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_plain_queue(thr, globals))
- return -1;
-
- /* Low prio */
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_single("sched_____s_lo", thr,
- ODP_SCHED_PRIO_LOWEST, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_many("sched_____m_lo", thr,
- ODP_SCHED_PRIO_LOWEST, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_multi("sched_multi_lo", thr,
- ODP_SCHED_PRIO_LOWEST, globals))
- return -1;
-
- /* High prio */
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_single("sched_____s_hi", thr,
- ODP_SCHED_PRIO_HIGHEST, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_many("sched_____m_hi", thr,
- ODP_SCHED_PRIO_HIGHEST, globals))
- return -1;
-
- odp_barrier_wait(barrier);
-
- if (test_schedule_multi("sched_multi_hi", thr,
- ODP_SCHED_PRIO_HIGHEST, globals))
- return -1;
-
- printf("Thread %i exits\n", thr);
- fflush(NULL);
- return 0;
-}
-
-/**
- * @internal Test cycle counter frequency
- */
-static void test_cpu_freq(void)
-{
- odp_time_t cur_time, test_time, start_time, end_time;
- uint64_t c1, c2, cycles;
- uint64_t nsec;
- double diff_max_hz, max_cycles;
-
- printf("\nCPU cycle count frequency test (runs about %i sec)\n",
- TEST_SEC);
-
- test_time = odp_time_local_from_ns(TEST_SEC * ODP_TIME_SEC_IN_NS);
- start_time = odp_time_local();
- end_time = odp_time_sum(start_time, test_time);
-
- /* Start the measurement */
- c1 = odp_cpu_cycles();
-
- do {
- cur_time = odp_time_local();
- } while (odp_time_cmp(end_time, cur_time) > 0);
-
- c2 = odp_cpu_cycles();
-
- test_time = odp_time_diff(cur_time, start_time);
- nsec = odp_time_to_ns(test_time);
-
- cycles = odp_cpu_cycles_diff(c2, c1);
- max_cycles = (nsec * odp_cpu_hz_max()) / 1000000000.0;
-
- /* Compare measured CPU cycles to maximum theoretical CPU cycle count */
- diff_max_hz = ((double)(cycles) - max_cycles) / max_cycles;
-
- printf("odp_time %" PRIu64 " ns\n", nsec);
- printf("odp_cpu_cycles %" PRIu64 " CPU cycles\n", cycles);
- printf("odp_sys_cpu_hz %" PRIu64 " hz\n", odp_cpu_hz_max());
- printf("Diff from max CPU freq %f%%\n", diff_max_hz * 100.0);
-
- printf("\n");
-}
-
-/**
- * @internal Print help
- */
-static void print_usage(void)
-{
- printf("\n\nUsage: ./odp_example [options]\n");
- printf("Options:\n");
- printf(" -c, --count <number> CPU count, 0=all available, default=0\n");
- printf(" -h, --help this help\n");
- printf(" -f, --fair collect fairness statistics\n");
- printf("\n\n");
-}
-
-/**
- * @internal Parse arguments
- *
- * @param argc Argument count
- * @param argv Argument vector
- * @param args Test arguments
- */
-static void parse_args(int argc, char *argv[], test_args_t *args)
-{
- int opt;
- int long_index;
-
- static const struct option longopts[] = {
- {"count", required_argument, NULL, 'c'},
- {"fair", no_argument, NULL, 'f'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- static const char *shortopts = "+c:fh";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- opterr = 0; /* do not issue errors on helper options */
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 'f':
- args->fairness = 1;
- break;
-
- case 'c':
- args->cpu_count = atoi(optarg);
- break;
-
- case 'h':
- print_usage();
- exit(EXIT_SUCCESS);
- break;
-
- default:
- break;
- }
- }
-}
-
-/**
- * Test main function
- */
-int main(int argc, char *argv[])
-{
- odph_odpthread_t *thread_tbl;
- test_args_t args;
- int num_workers;
- odp_cpumask_t cpumask;
- odp_pool_t pool;
- odp_queue_t plain_queue;
- int i, j;
- odp_shm_t shm;
- test_globals_t *globals;
- char cpumaskstr[ODP_CPUMASK_STR_SIZE];
- odp_pool_param_t params;
- int ret = 0;
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
-
- printf("\nODP example starts\n\n");
-
- memset(&args, 0, sizeof(args));
- parse_args(argc, argv, &args);
-
- /* ODP global init */
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("ODP global init failed.\n");
- return -1;
- }
-
- /*
- * Init this thread. It makes also ODP calls when
- * setting up resources for worker threads.
- */
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("ODP global init failed.\n");
- return -1;
- }
-
- printf("\n");
- printf("ODP system info\n");
- printf("---------------\n");
- printf("ODP API version: %s\n", odp_version_api_str());
- printf("ODP impl name: %s\n", odp_version_impl_name());
- printf("ODP impl details: %s\n", odp_version_impl_str());
- printf("CPU model: %s\n", odp_cpu_model_str());
- printf("CPU freq (hz): %" PRIu64 "\n", odp_cpu_hz_max());
- printf("Cache line size: %i\n", odp_sys_cache_line_size());
- printf("Max CPU count: %i\n", odp_cpu_count());
-
- printf("\n");
-
- /* Get default worker cpumask */
- num_workers = odp_cpumask_default_worker(&cpumask, args.cpu_count);
- (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
- printf("num worker threads: %i\n", num_workers);
- printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
- printf("cpu mask: %s\n", cpumaskstr);
-
- thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers);
- if (!thread_tbl) {
- LOG_ERR("no memory for thread_tbl\n");
- return -1;
- }
-
- /* Test cycle count frequency */
- test_cpu_freq();
-
- shm = odp_shm_reserve("test_globals",
- sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
- if (shm == ODP_SHM_INVALID) {
- LOG_ERR("Shared memory reserve failed.\n");
- return -1;
- }
-
- globals = odp_shm_addr(shm);
- memset(globals, 0, sizeof(test_globals_t));
- memcpy(&globals->args, &args, sizeof(test_args_t));
-
- /*
- * Create message pool
- */
-
- odp_pool_param_init(&params);
- params.buf.size = sizeof(test_message_t);
- params.buf.align = 0;
- params.buf.num = NUM_MSG;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create("msg_pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- LOG_ERR("Pool create failed.\n");
- return -1;
- }
-
- globals->pool = pool;
-
- /*
- * Create a queue for plain queue test
- */
- plain_queue = odp_queue_create("plain_queue", NULL);
-
- if (plain_queue == ODP_QUEUE_INVALID) {
- LOG_ERR("Plain queue create failed.\n");
- return -1;
- }
-
- /*
- * Create queues for schedule test. QUEUES_PER_PRIO per priority.
- */
- for (i = 0; i < NUM_PRIOS; i++) {
- char name[] = "sched_XX_YY";
- odp_queue_t queue;
- odp_queue_param_t param;
- int prio;
-
- if (i == 0)
- prio = ODP_SCHED_PRIO_HIGHEST;
- else
- prio = ODP_SCHED_PRIO_LOWEST;
-
- name[6] = '0' + (prio / 10);
- name[7] = '0' + prio - (10 * (prio / 10));
-
- odp_queue_param_init(&param);
- param.type = ODP_QUEUE_TYPE_SCHED;
- param.sched.prio = prio;
- param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- param.sched.group = ODP_SCHED_GROUP_ALL;
-
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- name[9] = '0' + j / 10;
- name[10] = '0' + j - 10 * (j / 10);
-
- queue = odp_queue_create(name, &param);
-
- if (queue == ODP_QUEUE_INVALID) {
- LOG_ERR("Schedule queue create failed.\n");
- return -1;
- }
-
- globals->queue[i][j] = queue;
-
- if (odp_queue_context_set(queue,
- &globals->queue_ctx[i][j],
- sizeof(queue_context_t))
- < 0) {
- LOG_ERR("Queue context set failed.\n");
- return -1;
- }
- }
- }
-
- odp_shm_print_all();
-
- odp_pool_print(pool);
-
- /* Barrier to sync test case execution */
- odp_barrier_init(&globals->barrier, num_workers);
-
- odp_spinlock_init(&globals->lock);
- globals->first_thr = -1;
-
- /* Create and launch worker threads */
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
- thr_params.start = run_thread;
- thr_params.arg = NULL;
- odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
-
- /* Wait for worker threads to terminate */
- odph_odpthreads_join(thread_tbl);
- free(thread_tbl);
-
- printf("ODP example complete\n\n");
-
- for (i = 0; i < NUM_PRIOS; i++) {
- odp_queue_t queue;
-
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- queue = globals->queue[i][j];
- ret += odp_queue_destroy(queue);
- }
- }
-
- ret += odp_shm_free(shm);
- ret += odp_queue_destroy(plain_queue);
- ret += odp_pool_destroy(pool);
- ret += odp_term_local();
- ret += odp_term_global(instance);
-
- return ret;
-}
diff --git a/test/common_plat/performance/odp_scheduling_run.sh b/test/common_plat/performance/odp_scheduling_run.sh
deleted file mode 100755
index a22326d4e..000000000
--- a/test/common_plat/performance/odp_scheduling_run.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Script that passes command line arguments to odp_scheduling test when
-# launched by 'make check'
-
-TEST_DIR="${TEST_DIR:-$(dirname $0)}"
-ret=0
-ALL=0
-
-run()
-{
- echo odp_scheduling_run starts requesting $1 worker threads
- echo ===============================================
-
- $TEST_DIR/odp_scheduling${EXEEXT} -c $1 || ret=1
-}
-
-run 1
-run 5
-run 8
-run 11
-run $ALL
-
-exit $ret
diff --git a/test/common_plat/validation/Makefile.am b/test/common_plat/validation/Makefile.am
deleted file mode 100644
index 5d525fba4..000000000
--- a/test/common_plat/validation/Makefile.am
+++ /dev/null
@@ -1,3 +0,0 @@
-if cunit_support
- SUBDIRS = api
-endif
diff --git a/test/common_plat/validation/api/Makefile.am b/test/common_plat/validation/api/Makefile.am
deleted file mode 100644
index e2d30a673..000000000
--- a/test/common_plat/validation/api/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-ODP_MODULES = atomic \
- barrier \
- buffer \
- classification \
- cpumask \
- crypto \
- errno \
- hash \
- init \
- lock \
- queue \
- packet \
- pktio \
- pool \
- random \
- scheduler \
- std_clib \
- thread \
- time \
- timer \
- traffic_mngr \
- shmem \
- system
-
-SUBDIRS = $(ODP_MODULES)
-
-#The tests will need to retain the deprecated test implementation
-AM_CFLAGS += -Wno-deprecated-declarations
diff --git a/test/common_plat/validation/api/Makefile.inc b/test/common_plat/validation/api/Makefile.inc
deleted file mode 100644
index 177d2ac38..000000000
--- a/test/common_plat/validation/api/Makefile.inc
+++ /dev/null
@@ -1,19 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-
-COMMON_DIR = $(top_builddir)/test/common_plat/common
-
-#the following option ensure that option '-I.' is not passed to gcc,
-#therefore distinguishing between '#include "X"' and '#include <X>'.
-#It allows common filenames (such as 'errno.h') to be used locally.
-AUTOMAKE_OPTIONS = nostdinc
-
-AM_CFLAGS += -I$(top_srcdir)/test/common_plat/common
-AM_LDFLAGS += -static
-AM_LDFLAGS += $(DPDK_PMDS)
-
-AM_CPPFLAGS += $(CUNIT_CPPFLAGS)
-
-LIBCUNIT_COMMON = $(COMMON_DIR)/libcunit_common.la
-LIBCPUMASK_COMMON = $(COMMON_DIR)/libcpumask_common.la
-LIBTHRMASK_COMMON = $(COMMON_DIR)/libthrmask_common.la
-LIBODP = $(LIB)/libodphelper.la $(LIB)/libodp-linux.la
diff --git a/test/common_plat/validation/api/README b/test/common_plat/validation/api/README
deleted file mode 100644
index 1baebaafc..000000000
--- a/test/common_plat/validation/api/README
+++ /dev/null
@@ -1,35 +0,0 @@
-Copyright (c) 2015, Linaro Limited
-All rights reserved.
-
-SPDX-License-Identifier: BSD-3-Clause
-
-
-To add tests in here, please observe the rules listed below. This list
-is a brief overview, for a more detailed explanation of the test
-framework refer to the ODP Implementers' Guide, which can built as
-follows:
-
- ./configure --enable-user-guides
- make
-
-Output will be in doc/output/. If this fails, check the documentation
-section of the DEPENDENCIES file.
-
-Rules for all tests under this tree:
-
-1. Tests must be placed in the directory of the module they belong to.
-
-2. Tests must be platform agnostic, i.e.
-
- - should be written in plain C only.
- - may only use C standard library functions, CUnit functions and of
- course ODP functions
- - should be expected to pass on all ODP implementations
-
- Tests that do not follow these rules should be placed in the platform
- specific test area (currently platform/<platform>/test/).
-
-3. If a new ODP API module is created, please update the Makefile.am.
-
-4. Symbols exported from test libraries must respect the naming
- convention detailed in the ODP Implementers' Guide.
diff --git a/test/common_plat/validation/api/atomic/Makefile.am b/test/common_plat/validation/api/atomic/Makefile.am
deleted file mode 100644
index 9b6bd6315..000000000
--- a/test/common_plat/validation/api/atomic/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestatomic.la
-libtestatomic_la_SOURCES = atomic.c
-
-test_PROGRAMS = atomic_main$(EXEEXT)
-dist_atomic_main_SOURCES = atomic_main.c
-atomic_main_LDADD = libtestatomic.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = atomic.h
diff --git a/test/common_plat/validation/api/atomic/atomic.c b/test/common_plat/validation/api/atomic/atomic.c
deleted file mode 100644
index db9484bc2..000000000
--- a/test/common_plat/validation/api/atomic/atomic.c
+++ /dev/null
@@ -1,909 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <malloc.h>
-#include <odp_api.h>
-#include <CUnit/Basic.h>
-#include <odp_cunit_common.h>
-#include <unistd.h>
-#include "atomic.h"
-
-#define VERBOSE 0
-#define MAX_ITERATIONS 1000
-
-#define ADD_SUB_CNT 5
-
-#define CNT 10
-#define U32_INIT_VAL (1UL << 10)
-#define U64_INIT_VAL (1ULL << 33)
-#define U32_MAGIC 0xa23f65b2
-#define U64_MAGIC 0xf2e1c5430cb6a52e
-
-#define GLOBAL_SHM_NAME "GlobalLockTest"
-
-#define UNUSED __attribute__((__unused__))
-
-#define CHECK_MAX_MIN (1 << 0)
-#define CHECK_XCHG (1 << 2)
-
-static odp_atomic_u32_t a32u;
-static odp_atomic_u64_t a64u;
-static odp_atomic_u32_t a32u_min;
-static odp_atomic_u32_t a32u_max;
-static odp_atomic_u64_t a64u_min;
-static odp_atomic_u64_t a64u_max;
-static odp_atomic_u32_t a32u_xchg;
-static odp_atomic_u64_t a64u_xchg;
-
-typedef __volatile uint32_t volatile_u32_t;
-typedef __volatile uint64_t volatile_u64_t;
-
-typedef struct {
- /* Global variables */
- uint32_t g_num_threads;
- uint32_t g_iterations;
- uint32_t g_verbose;
- uint32_t g_max_num_cores;
-
- volatile_u32_t global_lock_owner;
-} global_shared_mem_t;
-
-/* Per-thread memory */
-typedef struct {
- global_shared_mem_t *global_mem;
-
- int thread_id;
- int thread_core;
-
- volatile_u64_t delay_counter;
-} per_thread_mem_t;
-
-static odp_shm_t global_shm;
-static global_shared_mem_t *global_mem;
-
-/* Initialise per-thread memory */
-static per_thread_mem_t *thread_init(void)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_shm_t global_shm;
- uint32_t per_thread_mem_len;
-
- per_thread_mem_len = sizeof(per_thread_mem_t);
- per_thread_mem = malloc(per_thread_mem_len);
- memset(per_thread_mem, 0, per_thread_mem_len);
-
- per_thread_mem->delay_counter = 1;
-
- per_thread_mem->thread_id = odp_thread_id();
- per_thread_mem->thread_core = odp_cpu_id();
-
- global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- global_mem = odp_shm_addr(global_shm);
- CU_ASSERT_PTR_NOT_NULL(global_mem);
-
- per_thread_mem->global_mem = global_mem;
-
- return per_thread_mem;
-}
-
-static void thread_finalize(per_thread_mem_t *per_thread_mem)
-{
- free(per_thread_mem);
-}
-
-static void test_atomic_inc_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_inc_u32(&a32u);
-}
-
-static void test_atomic_inc_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_inc_u64(&a64u);
-}
-
-static void test_atomic_dec_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_dec_u32(&a32u);
-}
-
-static void test_atomic_dec_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_dec_u64(&a64u);
-}
-
-static void test_atomic_fetch_inc_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_inc_u32(&a32u);
-}
-
-static void test_atomic_fetch_inc_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_inc_u64(&a64u);
-}
-
-static void test_atomic_fetch_dec_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_dec_u32(&a32u);
-}
-
-static void test_atomic_fetch_dec_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_dec_u64(&a64u);
-}
-
-static void test_atomic_add_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_add_u32(&a32u, ADD_SUB_CNT);
-}
-
-static void test_atomic_add_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_add_u64(&a64u, ADD_SUB_CNT);
-}
-
-static void test_atomic_sub_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_sub_u32(&a32u, ADD_SUB_CNT);
-}
-
-static void test_atomic_sub_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_sub_u64(&a64u, ADD_SUB_CNT);
-}
-
-static void test_atomic_fetch_add_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_add_u32(&a32u, ADD_SUB_CNT);
-}
-
-static void test_atomic_fetch_add_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_add_u64(&a64u, ADD_SUB_CNT);
-}
-
-static void test_atomic_fetch_sub_32(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_sub_u32(&a32u, ADD_SUB_CNT);
-}
-
-static void test_atomic_fetch_sub_64(void)
-{
- int i;
-
- for (i = 0; i < CNT; i++)
- odp_atomic_fetch_sub_u64(&a64u, ADD_SUB_CNT);
-}
-
-static void test_atomic_min_32(void)
-{
- int i;
- uint32_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_fetch_dec_u32(&a32u);
- odp_atomic_min_u32(&a32u_min, tmp);
- }
-}
-
-static void test_atomic_min_64(void)
-{
- int i;
- uint64_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_fetch_dec_u64(&a64u);
- odp_atomic_min_u64(&a64u_min, tmp);
- }
-}
-
-static void test_atomic_max_32(void)
-{
- int i;
- uint32_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_fetch_inc_u32(&a32u);
- odp_atomic_max_u32(&a32u_max, tmp);
- }
-}
-
-static void test_atomic_max_64(void)
-{
- int i;
- uint64_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_fetch_inc_u64(&a64u);
- odp_atomic_max_u64(&a64u_max, tmp);
- }
-}
-
-static void test_atomic_cas_inc_32(void)
-{
- int i;
- uint32_t old;
-
- for (i = 0; i < CNT; i++) {
- old = odp_atomic_load_u32(&a32u);
-
- while (odp_atomic_cas_u32(&a32u, &old, old + 1) == 0)
- ;
- }
-}
-
-static void test_atomic_cas_dec_32(void)
-{
- int i;
- uint32_t old;
-
- for (i = 0; i < CNT; i++) {
- old = odp_atomic_load_u32(&a32u);
-
- while (odp_atomic_cas_u32(&a32u, &old, old - 1) == 0)
- ;
- }
-}
-
-static void test_atomic_cas_inc_64(void)
-{
- int i;
- uint64_t old;
-
- for (i = 0; i < CNT; i++) {
- old = odp_atomic_load_u64(&a64u);
-
- while (odp_atomic_cas_u64(&a64u, &old, old + 1) == 0)
- ;
- }
-}
-
-static void test_atomic_cas_dec_64(void)
-{
- int i;
- uint64_t old;
-
- for (i = 0; i < CNT; i++) {
- old = odp_atomic_load_u64(&a64u);
-
- while (odp_atomic_cas_u64(&a64u, &old, old - 1) == 0)
- ;
- }
-}
-
-static void test_atomic_xchg_32(void)
-{
- uint32_t old, new;
- int i;
-
- for (i = 0; i < CNT; i++) {
- new = odp_atomic_fetch_inc_u32(&a32u);
- old = odp_atomic_xchg_u32(&a32u_xchg, new);
-
- if (old & 0x1)
- odp_atomic_xchg_u32(&a32u_xchg, 0);
- else
- odp_atomic_xchg_u32(&a32u_xchg, 1);
- }
-
- odp_atomic_sub_u32(&a32u, CNT);
- odp_atomic_xchg_u32(&a32u_xchg, U32_MAGIC);
-}
-
-static void test_atomic_xchg_64(void)
-{
- uint64_t old, new;
- int i;
-
- for (i = 0; i < CNT; i++) {
- new = odp_atomic_fetch_inc_u64(&a64u);
- old = odp_atomic_xchg_u64(&a64u_xchg, new);
-
- if (old & 0x1)
- odp_atomic_xchg_u64(&a64u_xchg, 0);
- else
- odp_atomic_xchg_u64(&a64u_xchg, 1);
- }
-
- odp_atomic_sub_u64(&a64u, CNT);
- odp_atomic_xchg_u64(&a64u_xchg, U64_MAGIC);
-}
-
-static void test_atomic_non_relaxed_32(void)
-{
- int i;
- uint32_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_load_acq_u32(&a32u);
- odp_atomic_store_rel_u32(&a32u, tmp);
-
- tmp = odp_atomic_load_acq_u32(&a32u_max);
- odp_atomic_add_rel_u32(&a32u_max, 1);
-
- tmp = odp_atomic_load_acq_u32(&a32u_min);
- odp_atomic_sub_rel_u32(&a32u_min, 1);
-
- tmp = odp_atomic_load_u32(&a32u_xchg);
- while (odp_atomic_cas_acq_u32(&a32u_xchg, &tmp, tmp + 1) == 0)
- ;
-
- tmp = odp_atomic_load_u32(&a32u_xchg);
- while (odp_atomic_cas_rel_u32(&a32u_xchg, &tmp, tmp + 1) == 0)
- ;
-
- tmp = odp_atomic_load_u32(&a32u_xchg);
- /* finally set value for validation */
- while (odp_atomic_cas_acq_rel_u32(&a32u_xchg, &tmp, U32_MAGIC)
- == 0)
- ;
- }
-}
-
-static void test_atomic_non_relaxed_64(void)
-{
- int i;
- uint64_t tmp;
-
- for (i = 0; i < CNT; i++) {
- tmp = odp_atomic_load_acq_u64(&a64u);
- odp_atomic_store_rel_u64(&a64u, tmp);
-
- tmp = odp_atomic_load_acq_u64(&a64u_max);
- odp_atomic_add_rel_u64(&a64u_max, 1);
-
- tmp = odp_atomic_load_acq_u64(&a64u_min);
- odp_atomic_sub_rel_u64(&a64u_min, 1);
-
- tmp = odp_atomic_load_u64(&a64u_xchg);
- while (odp_atomic_cas_acq_u64(&a64u_xchg, &tmp, tmp + 1) == 0)
- ;
-
- tmp = odp_atomic_load_u64(&a64u_xchg);
- while (odp_atomic_cas_rel_u64(&a64u_xchg, &tmp, tmp + 1) == 0)
- ;
-
- tmp = odp_atomic_load_u64(&a64u_xchg);
- /* finally set value for validation */
- while (odp_atomic_cas_acq_rel_u64(&a64u_xchg, &tmp, U64_MAGIC)
- == 0)
- ;
- }
-}
-
-static void test_atomic_inc_dec_32(void)
-{
- test_atomic_inc_32();
- test_atomic_dec_32();
-}
-
-static void test_atomic_inc_dec_64(void)
-{
- test_atomic_inc_64();
- test_atomic_dec_64();
-}
-
-static void test_atomic_fetch_inc_dec_32(void)
-{
- test_atomic_fetch_inc_32();
- test_atomic_fetch_dec_32();
-}
-
-static void test_atomic_fetch_inc_dec_64(void)
-{
- test_atomic_fetch_inc_64();
- test_atomic_fetch_dec_64();
-}
-
-static void test_atomic_add_sub_32(void)
-{
- test_atomic_add_32();
- test_atomic_sub_32();
-}
-
-static void test_atomic_add_sub_64(void)
-{
- test_atomic_add_64();
- test_atomic_sub_64();
-}
-
-static void test_atomic_fetch_add_sub_32(void)
-{
- test_atomic_fetch_add_32();
- test_atomic_fetch_sub_32();
-}
-
-static void test_atomic_fetch_add_sub_64(void)
-{
- test_atomic_fetch_add_64();
- test_atomic_fetch_sub_64();
-}
-
-static void test_atomic_max_min_32(void)
-{
- test_atomic_max_32();
- test_atomic_min_32();
-}
-
-static void test_atomic_max_min_64(void)
-{
- test_atomic_max_64();
- test_atomic_min_64();
-}
-
-static void test_atomic_cas_inc_dec_32(void)
-{
- test_atomic_cas_inc_32();
- test_atomic_cas_dec_32();
-}
-
-static void test_atomic_cas_inc_dec_64(void)
-{
- test_atomic_cas_inc_64();
- test_atomic_cas_dec_64();
-}
-
-static void test_atomic_init(void)
-{
- odp_atomic_init_u32(&a32u, 0);
- odp_atomic_init_u64(&a64u, 0);
- odp_atomic_init_u32(&a32u_min, 0);
- odp_atomic_init_u32(&a32u_max, 0);
- odp_atomic_init_u64(&a64u_min, 0);
- odp_atomic_init_u64(&a64u_max, 0);
- odp_atomic_init_u32(&a32u_xchg, 0);
- odp_atomic_init_u64(&a64u_xchg, 0);
-}
-
-static void test_atomic_store(void)
-{
- odp_atomic_store_u32(&a32u, U32_INIT_VAL);
- odp_atomic_store_u64(&a64u, U64_INIT_VAL);
- odp_atomic_store_u32(&a32u_min, U32_INIT_VAL);
- odp_atomic_store_u32(&a32u_max, U32_INIT_VAL);
- odp_atomic_store_u64(&a64u_min, U64_INIT_VAL);
- odp_atomic_store_u64(&a64u_max, U64_INIT_VAL);
- odp_atomic_store_u32(&a32u_xchg, U32_INIT_VAL);
- odp_atomic_store_u64(&a64u_xchg, U64_INIT_VAL);
-}
-
-static void test_atomic_validate(int check)
-{
- CU_ASSERT(U32_INIT_VAL == odp_atomic_load_u32(&a32u));
- CU_ASSERT(U64_INIT_VAL == odp_atomic_load_u64(&a64u));
-
- if (check & CHECK_MAX_MIN) {
- CU_ASSERT(odp_atomic_load_u32(&a32u_max) >
- odp_atomic_load_u32(&a32u_min));
-
- CU_ASSERT(odp_atomic_load_u64(&a64u_max) >
- odp_atomic_load_u64(&a64u_min));
- }
-
- if (check & CHECK_XCHG) {
- CU_ASSERT(odp_atomic_load_u32(&a32u_xchg) == U32_MAGIC);
- CU_ASSERT(odp_atomic_load_u64(&a64u_xchg) == U64_MAGIC);
- }
-}
-
-int atomic_init(odp_instance_t *inst)
-{
- uint32_t workers_count, max_threads;
- int ret = 0;
- odp_cpumask_t mask;
-
- if (0 != odp_init_global(inst, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return -1;
- }
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return -1;
- }
-
- global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
- sizeof(global_shared_mem_t), 64,
- ODP_SHM_SW_ONLY);
- if (ODP_SHM_INVALID == global_shm) {
- fprintf(stderr, "Unable reserve memory for global_shm\n");
- return -1;
- }
-
- global_mem = odp_shm_addr(global_shm);
- memset(global_mem, 0, sizeof(global_shared_mem_t));
-
- global_mem->g_num_threads = MAX_WORKERS;
- global_mem->g_iterations = MAX_ITERATIONS;
- global_mem->g_verbose = VERBOSE;
-
- workers_count = odp_cpumask_default_worker(&mask, 0);
-
- max_threads = (workers_count >= MAX_WORKERS) ?
- MAX_WORKERS : workers_count;
-
- if (max_threads < global_mem->g_num_threads) {
- printf("Requested num of threads is too large\n");
- printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
- global_mem->g_num_threads,
- max_threads);
- global_mem->g_num_threads = max_threads;
- }
-
- printf("Num of threads used = %" PRIu32 "\n",
- global_mem->g_num_threads);
-
- return ret;
-}
-
-int atomic_term(odp_instance_t inst)
-{
- odp_shm_t shm;
-
- shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- if (0 != odp_shm_free(shm)) {
- fprintf(stderr, "error: odp_shm_free() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_global(inst)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-/* Atomic tests */
-static int test_atomic_inc_dec_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_inc_dec_32();
- test_atomic_inc_dec_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_add_sub_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_add_sub_32();
- test_atomic_add_sub_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_fetch_inc_dec_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_fetch_inc_dec_32();
- test_atomic_fetch_inc_dec_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_fetch_add_sub_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_fetch_add_sub_32();
- test_atomic_fetch_add_sub_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_max_min_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_max_min_32();
- test_atomic_max_min_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_cas_inc_dec_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_cas_inc_dec_32();
- test_atomic_cas_inc_dec_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_xchg_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_xchg_32();
- test_atomic_xchg_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int test_atomic_non_relaxed_thread(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
-
- per_thread_mem = thread_init();
- test_atomic_non_relaxed_32();
- test_atomic_non_relaxed_64();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void test_atomic_functional(int func_ptr(void *), int check)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- test_atomic_init();
- test_atomic_store();
- odp_cunit_thread_create(func_ptr, &arg);
- odp_cunit_thread_exit(&arg);
- test_atomic_validate(check);
-}
-
-void atomic_test_atomic_inc_dec(void)
-{
- test_atomic_functional(test_atomic_inc_dec_thread, 0);
-}
-
-void atomic_test_atomic_add_sub(void)
-{
- test_atomic_functional(test_atomic_add_sub_thread, 0);
-}
-
-void atomic_test_atomic_fetch_inc_dec(void)
-{
- test_atomic_functional(test_atomic_fetch_inc_dec_thread, 0);
-}
-
-void atomic_test_atomic_fetch_add_sub(void)
-{
- test_atomic_functional(test_atomic_fetch_add_sub_thread, 0);
-}
-
-void atomic_test_atomic_max_min(void)
-{
- test_atomic_functional(test_atomic_max_min_thread, CHECK_MAX_MIN);
-}
-
-void atomic_test_atomic_cas_inc_dec(void)
-{
- test_atomic_functional(test_atomic_cas_inc_dec_thread, 0);
-}
-
-void atomic_test_atomic_xchg(void)
-{
- test_atomic_functional(test_atomic_xchg_thread, CHECK_XCHG);
-}
-
-void atomic_test_atomic_non_relaxed(void)
-{
- test_atomic_functional(test_atomic_non_relaxed_thread,
- CHECK_MAX_MIN | CHECK_XCHG);
-}
-
-void atomic_test_atomic_op_lock_free(void)
-{
- odp_atomic_op_t atomic_op;
- int ret_null, ret;
-
- memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t));
- atomic_op.all_bits = 0;
-
- CU_ASSERT(atomic_op.all_bits == 0);
- CU_ASSERT(atomic_op.op.init == 0);
- CU_ASSERT(atomic_op.op.load == 0);
- CU_ASSERT(atomic_op.op.store == 0);
- CU_ASSERT(atomic_op.op.fetch_add == 0);
- CU_ASSERT(atomic_op.op.add == 0);
- CU_ASSERT(atomic_op.op.fetch_sub == 0);
- CU_ASSERT(atomic_op.op.sub == 0);
- CU_ASSERT(atomic_op.op.fetch_inc == 0);
- CU_ASSERT(atomic_op.op.inc == 0);
- CU_ASSERT(atomic_op.op.fetch_dec == 0);
- CU_ASSERT(atomic_op.op.dec == 0);
- CU_ASSERT(atomic_op.op.min == 0);
- CU_ASSERT(atomic_op.op.max == 0);
- CU_ASSERT(atomic_op.op.cas == 0);
- CU_ASSERT(atomic_op.op.xchg == 0);
-
- /* Test setting first, last and couple of other bits */
- atomic_op.op.init = 1;
- CU_ASSERT(atomic_op.op.init == 1);
- CU_ASSERT(atomic_op.all_bits != 0);
- atomic_op.op.init = 0;
- CU_ASSERT(atomic_op.all_bits == 0);
-
- atomic_op.op.xchg = 1;
- CU_ASSERT(atomic_op.op.xchg == 1);
- CU_ASSERT(atomic_op.all_bits != 0);
- atomic_op.op.xchg = 0;
- CU_ASSERT(atomic_op.all_bits == 0);
-
- atomic_op.op.add = 1;
- CU_ASSERT(atomic_op.op.add == 1);
- CU_ASSERT(atomic_op.all_bits != 0);
- atomic_op.op.add = 0;
- CU_ASSERT(atomic_op.all_bits == 0);
-
- atomic_op.op.dec = 1;
- CU_ASSERT(atomic_op.op.dec == 1);
- CU_ASSERT(atomic_op.all_bits != 0);
- atomic_op.op.dec = 0;
- CU_ASSERT(atomic_op.all_bits == 0);
-
- memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t));
- ret = odp_atomic_lock_free_u64(&atomic_op);
- ret_null = odp_atomic_lock_free_u64(NULL);
-
- CU_ASSERT(ret == ret_null);
-
- /* Init operation is not atomic by the spec. Call to
- * odp_atomic_lock_free_u64() zeros it but never sets it. */
-
- if (ret == 0) {
- /* none are lock free */
- CU_ASSERT(atomic_op.all_bits == 0);
- CU_ASSERT(atomic_op.op.init == 0);
- CU_ASSERT(atomic_op.op.load == 0);
- CU_ASSERT(atomic_op.op.store == 0);
- CU_ASSERT(atomic_op.op.fetch_add == 0);
- CU_ASSERT(atomic_op.op.add == 0);
- CU_ASSERT(atomic_op.op.fetch_sub == 0);
- CU_ASSERT(atomic_op.op.sub == 0);
- CU_ASSERT(atomic_op.op.fetch_inc == 0);
- CU_ASSERT(atomic_op.op.inc == 0);
- CU_ASSERT(atomic_op.op.fetch_dec == 0);
- CU_ASSERT(atomic_op.op.dec == 0);
- CU_ASSERT(atomic_op.op.min == 0);
- CU_ASSERT(atomic_op.op.max == 0);
- CU_ASSERT(atomic_op.op.cas == 0);
- CU_ASSERT(atomic_op.op.xchg == 0);
- }
-
- if (ret == 1) {
- /* some are lock free */
- CU_ASSERT(atomic_op.all_bits != 0);
- CU_ASSERT(atomic_op.op.init == 0);
- }
-
- if (ret == 2) {
- /* all are lock free */
- CU_ASSERT(atomic_op.all_bits != 0);
- CU_ASSERT(atomic_op.op.init == 0);
- CU_ASSERT(atomic_op.op.load == 1);
- CU_ASSERT(atomic_op.op.store == 1);
- CU_ASSERT(atomic_op.op.fetch_add == 1);
- CU_ASSERT(atomic_op.op.add == 1);
- CU_ASSERT(atomic_op.op.fetch_sub == 1);
- CU_ASSERT(atomic_op.op.sub == 1);
- CU_ASSERT(atomic_op.op.fetch_inc == 1);
- CU_ASSERT(atomic_op.op.inc == 1);
- CU_ASSERT(atomic_op.op.fetch_dec == 1);
- CU_ASSERT(atomic_op.op.dec == 1);
- CU_ASSERT(atomic_op.op.min == 1);
- CU_ASSERT(atomic_op.op.max == 1);
- CU_ASSERT(atomic_op.op.cas == 1);
- CU_ASSERT(atomic_op.op.xchg == 1);
- }
-}
-
-odp_testinfo_t atomic_suite_atomic[] = {
- ODP_TEST_INFO(atomic_test_atomic_inc_dec),
- ODP_TEST_INFO(atomic_test_atomic_add_sub),
- ODP_TEST_INFO(atomic_test_atomic_fetch_inc_dec),
- ODP_TEST_INFO(atomic_test_atomic_fetch_add_sub),
- ODP_TEST_INFO(atomic_test_atomic_max_min),
- ODP_TEST_INFO(atomic_test_atomic_cas_inc_dec),
- ODP_TEST_INFO(atomic_test_atomic_xchg),
- ODP_TEST_INFO(atomic_test_atomic_non_relaxed),
- ODP_TEST_INFO(atomic_test_atomic_op_lock_free),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t atomic_suites[] = {
- {"atomic", NULL, NULL,
- atomic_suite_atomic},
- ODP_SUITE_INFO_NULL
-};
-
-int atomic_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- odp_cunit_register_global_init(atomic_init);
- odp_cunit_register_global_term(atomic_term);
-
- ret = odp_cunit_register(atomic_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/atomic/atomic.h b/test/common_plat/validation/api/atomic/atomic.h
deleted file mode 100644
index 66796c8e3..000000000
--- a/test/common_plat/validation/api/atomic/atomic.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_ATOMIC_H_
-#define _ODP_TEST_ATOMIC_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void atomic_test_atomic_inc_dec(void);
-void atomic_test_atomic_add_sub(void);
-void atomic_test_atomic_fetch_inc_dec(void);
-void atomic_test_atomic_fetch_add_sub(void);
-void atomic_test_atomic_max_min(void);
-void atomic_test_atomic_cas_inc_dec(void);
-void atomic_test_atomic_xchg(void);
-void atomic_test_atomic_non_relaxed(void);
-void atomic_test_atomic_op_lock_free(void);
-
-/* test arrays: */
-extern odp_testinfo_t atomic_suite_atomic[];
-
-/* test array init/term functions: */
-int atomic_suite_init(void);
-
-/* test registry: */
-extern odp_suiteinfo_t atomic_suites[];
-
-/* executable init/term functions: */
-int atomic_init(odp_instance_t *inst);
-int atomic_term(odp_instance_t inst);
-
-/* main test program: */
-int atomic_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/atomic/atomic_main.c b/test/common_plat/validation/api/atomic/atomic_main.c
deleted file mode 100644
index db035373e..000000000
--- a/test/common_plat/validation/api/atomic/atomic_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "atomic.h"
-
-int main(int argc, char *argv[])
-{
- return atomic_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/barrier/Makefile.am b/test/common_plat/validation/api/barrier/Makefile.am
deleted file mode 100644
index 8fc632c27..000000000
--- a/test/common_plat/validation/api/barrier/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestbarrier.la
-libtestbarrier_la_SOURCES = barrier.c
-
-test_PROGRAMS = barrier_main$(EXEEXT)
-dist_barrier_main_SOURCES = barrier_main.c
-barrier_main_LDADD = libtestbarrier.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = barrier.h
diff --git a/test/common_plat/validation/api/barrier/barrier.c b/test/common_plat/validation/api/barrier/barrier.c
deleted file mode 100644
index 79ee82b3b..000000000
--- a/test/common_plat/validation/api/barrier/barrier.c
+++ /dev/null
@@ -1,421 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <malloc.h>
-#include <odp_api.h>
-#include <CUnit/Basic.h>
-#include <odp_cunit_common.h>
-#include <unistd.h>
-#include "barrier.h"
-
-#define VERBOSE 0
-#define MAX_ITERATIONS 1000
-#define BARRIER_ITERATIONS 64
-
-#define SLOW_BARRIER_DELAY 400
-#define BASE_DELAY 6
-
-#define NUM_TEST_BARRIERS BARRIER_ITERATIONS
-#define NUM_RESYNC_BARRIERS 100
-
-#define BARRIER_DELAY 10
-
-#define GLOBAL_SHM_NAME "GlobalLockTest"
-
-#define UNUSED __attribute__((__unused__))
-
-static volatile int temp_result;
-
-typedef __volatile uint32_t volatile_u32_t;
-typedef __volatile uint64_t volatile_u64_t;
-
-typedef struct {
- odp_atomic_u32_t wait_cnt;
-} custom_barrier_t;
-
-typedef struct {
- /* Global variables */
- uint32_t g_num_threads;
- uint32_t g_iterations;
- uint32_t g_verbose;
- uint32_t g_max_num_cores;
-
- odp_barrier_t test_barriers[NUM_TEST_BARRIERS];
- custom_barrier_t custom_barrier1[NUM_TEST_BARRIERS];
- custom_barrier_t custom_barrier2[NUM_TEST_BARRIERS];
- volatile_u32_t slow_thread_num;
- volatile_u32_t barrier_cnt1;
- volatile_u32_t barrier_cnt2;
- odp_barrier_t global_barrier;
-
-} global_shared_mem_t;
-
-/* Per-thread memory */
-typedef struct {
- global_shared_mem_t *global_mem;
-
- int thread_id;
- int thread_core;
-
- volatile_u64_t delay_counter;
-} per_thread_mem_t;
-
-static odp_shm_t global_shm;
-static global_shared_mem_t *global_mem;
-
-/*
-* Delay a consistent amount of time. Ideally the amount of CPU time taken
-* is linearly proportional to "iterations". The goal is to try to do some
-* work that the compiler optimizer won't optimize away, and also to
-* minimize loads and stores (at least to different memory addresses)
-* so as to not affect or be affected by caching issues. This does NOT have to
-* correlate to a specific number of cpu cycles or be consistent across
-* CPU architectures.
-*/
-static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations)
-{
- volatile_u64_t *counter_ptr;
- uint32_t cnt;
-
- counter_ptr = &per_thread_mem->delay_counter;
-
- for (cnt = 1; cnt <= iterations; cnt++)
- (*counter_ptr)++;
-}
-
-/* Initialise per-thread memory */
-static per_thread_mem_t *thread_init(void)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_shm_t global_shm;
- uint32_t per_thread_mem_len;
-
- per_thread_mem_len = sizeof(per_thread_mem_t);
- per_thread_mem = malloc(per_thread_mem_len);
- memset(per_thread_mem, 0, per_thread_mem_len);
-
- per_thread_mem->delay_counter = 1;
-
- per_thread_mem->thread_id = odp_thread_id();
- per_thread_mem->thread_core = odp_cpu_id();
-
- global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- global_mem = odp_shm_addr(global_shm);
- CU_ASSERT_PTR_NOT_NULL(global_mem);
-
- per_thread_mem->global_mem = global_mem;
-
- return per_thread_mem;
-}
-
-static void thread_finalize(per_thread_mem_t *per_thread_mem)
-{
- free(per_thread_mem);
-}
-
-static void custom_barrier_init(custom_barrier_t *custom_barrier,
- uint32_t num_threads)
-{
- odp_atomic_init_u32(&custom_barrier->wait_cnt, num_threads);
-}
-
-static void custom_barrier_wait(custom_barrier_t *custom_barrier)
-{
- volatile_u64_t counter = 1;
- uint32_t delay_cnt, wait_cnt;
-
- odp_atomic_sub_u32(&custom_barrier->wait_cnt, 1);
-
- wait_cnt = 1;
- while (wait_cnt != 0) {
- for (delay_cnt = 1; delay_cnt <= BARRIER_DELAY; delay_cnt++)
- counter++;
-
- wait_cnt = odp_atomic_load_u32(&custom_barrier->wait_cnt);
- }
-}
-
-static uint32_t barrier_test(per_thread_mem_t *per_thread_mem,
- odp_bool_t no_barrier_test)
-{
- global_shared_mem_t *global_mem;
- uint32_t barrier_errs, iterations, cnt, i_am_slow_thread;
- uint32_t thread_num, slow_thread_num, next_slow_thread, num_threads;
- uint32_t lock_owner_delay, barrier_cnt1, barrier_cnt2;
-
- thread_num = odp_thread_id();
- global_mem = per_thread_mem->global_mem;
- num_threads = global_mem->g_num_threads;
- iterations = BARRIER_ITERATIONS;
-
- barrier_errs = 0;
- lock_owner_delay = SLOW_BARRIER_DELAY;
-
- for (cnt = 1; cnt < iterations; cnt++) {
- /* Wait here until all of the threads reach this point */
- custom_barrier_wait(&global_mem->custom_barrier1[cnt]);
-
- barrier_cnt1 = global_mem->barrier_cnt1;
- barrier_cnt2 = global_mem->barrier_cnt2;
-
- if ((barrier_cnt1 != cnt) || (barrier_cnt2 != cnt)) {
- printf("thread_num=%" PRIu32 " barrier_cnts of %" PRIu32
- " %" PRIu32 " cnt=%" PRIu32 "\n",
- thread_num, barrier_cnt1, barrier_cnt2, cnt);
- barrier_errs++;
- }
-
- /* Wait here until all of the threads reach this point */
- custom_barrier_wait(&global_mem->custom_barrier2[cnt]);
-
- slow_thread_num = global_mem->slow_thread_num;
- i_am_slow_thread = thread_num == slow_thread_num;
- next_slow_thread = slow_thread_num + 1;
- if (num_threads < next_slow_thread)
- next_slow_thread = 1;
-
- /*
- * Now run the test, which involves having all but one thread
- * immediately calling odp_barrier_wait(), and one thread wait a
- * moderate amount of time and then calling odp_barrier_wait().
- * The test fails if any of the first group of threads
- * has not waited for the "slow" thread. The "slow" thread is
- * responsible for re-initializing the barrier for next trial.
- */
- if (i_am_slow_thread) {
- thread_delay(per_thread_mem, lock_owner_delay);
- lock_owner_delay += BASE_DELAY;
- if ((global_mem->barrier_cnt1 != cnt) ||
- (global_mem->barrier_cnt2 != cnt) ||
- (global_mem->slow_thread_num
- != slow_thread_num))
- barrier_errs++;
- }
-
- if (no_barrier_test == 0)
- odp_barrier_wait(&global_mem->test_barriers[cnt]);
-
- global_mem->barrier_cnt1 = cnt + 1;
- odp_mb_full();
-
- if (i_am_slow_thread) {
- global_mem->slow_thread_num = next_slow_thread;
- global_mem->barrier_cnt2 = cnt + 1;
- odp_mb_full();
- } else {
- while (global_mem->barrier_cnt2 != (cnt + 1))
- thread_delay(per_thread_mem, BASE_DELAY);
- }
- }
-
- if ((global_mem->g_verbose) && (barrier_errs != 0))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " barrier_errs in %" PRIu32 " iterations\n", thread_num,
- per_thread_mem->thread_id,
- per_thread_mem->thread_core, barrier_errs, iterations);
-
- return barrier_errs;
-}
-
-static int no_barrier_functional_test(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
- uint32_t barrier_errs;
-
- per_thread_mem = thread_init();
- barrier_errs = barrier_test(per_thread_mem, 1);
-
- /*
- * Note that the following CU_ASSERT MAY appear incorrect, but for the
- * no_barrier test it should see barrier_errs or else there is something
- * wrong with the test methodology or the ODP thread implementation.
- * So this test PASSES only if it sees barrier_errs or a single
- * worker was used.
- */
- CU_ASSERT(barrier_errs != 0 || global_mem->g_num_threads == 1);
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int barrier_functional_test(void *arg UNUSED)
-{
- per_thread_mem_t *per_thread_mem;
- uint32_t barrier_errs;
-
- per_thread_mem = thread_init();
- barrier_errs = barrier_test(per_thread_mem, 0);
-
- CU_ASSERT(barrier_errs == 0);
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void barrier_test_init(void)
-{
- uint32_t num_threads, idx;
-
- num_threads = global_mem->g_num_threads;
-
- for (idx = 0; idx < NUM_TEST_BARRIERS; idx++) {
- odp_barrier_init(&global_mem->test_barriers[idx], num_threads);
- custom_barrier_init(&global_mem->custom_barrier1[idx],
- num_threads);
- custom_barrier_init(&global_mem->custom_barrier2[idx],
- num_threads);
- }
-
- global_mem->slow_thread_num = 1;
- global_mem->barrier_cnt1 = 1;
- global_mem->barrier_cnt2 = 1;
-}
-
-/* Barrier tests */
-void barrier_test_memory_barrier(void)
-{
- volatile int a = 0;
- volatile int b = 0;
- volatile int c = 0;
- volatile int d = 0;
-
- /* Call all memory barriers to verify that those are implemented */
- a = 1;
- odp_mb_release();
- b = 1;
- odp_mb_acquire();
- c = 1;
- odp_mb_full();
- d = 1;
-
- /* Avoid "variable set but not used" warning */
- temp_result = a + b + c + d;
-}
-
-void barrier_test_no_barrier_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- barrier_test_init();
- odp_cunit_thread_create(no_barrier_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void barrier_test_barrier_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- barrier_test_init();
- odp_cunit_thread_create(barrier_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t barrier_suite_barrier[] = {
- ODP_TEST_INFO(barrier_test_memory_barrier),
- ODP_TEST_INFO(barrier_test_no_barrier_functional),
- ODP_TEST_INFO(barrier_test_barrier_functional),
- ODP_TEST_INFO_NULL
-};
-
-int barrier_init(odp_instance_t *inst)
-{
- uint32_t workers_count, max_threads;
- int ret = 0;
- odp_cpumask_t mask;
-
- if (0 != odp_init_global(inst, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return -1;
- }
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return -1;
- }
-
- global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
- sizeof(global_shared_mem_t), 64,
- ODP_SHM_SW_ONLY);
- if (ODP_SHM_INVALID == global_shm) {
- fprintf(stderr, "Unable reserve memory for global_shm\n");
- return -1;
- }
-
- global_mem = odp_shm_addr(global_shm);
- memset(global_mem, 0, sizeof(global_shared_mem_t));
-
- global_mem->g_num_threads = MAX_WORKERS;
- global_mem->g_iterations = MAX_ITERATIONS;
- global_mem->g_verbose = VERBOSE;
-
- workers_count = odp_cpumask_default_worker(&mask, 0);
-
- max_threads = (workers_count >= MAX_WORKERS) ?
- MAX_WORKERS : workers_count;
-
- if (max_threads < global_mem->g_num_threads) {
- printf("Requested num of threads is too large\n");
- printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
- global_mem->g_num_threads,
- max_threads);
- global_mem->g_num_threads = max_threads;
- }
-
- printf("Num of threads used = %" PRIu32 "\n",
- global_mem->g_num_threads);
-
- return ret;
-}
-
-int barrier_term(odp_instance_t inst)
-{
- odp_shm_t shm;
-
- shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- if (0 != odp_shm_free(shm)) {
- fprintf(stderr, "error: odp_shm_free() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_global(inst)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-odp_suiteinfo_t barrier_suites[] = {
- {"barrier", NULL, NULL,
- barrier_suite_barrier},
- ODP_SUITE_INFO_NULL
-};
-
-int barrier_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- odp_cunit_register_global_init(barrier_init);
- odp_cunit_register_global_term(barrier_term);
-
- ret = odp_cunit_register(barrier_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/barrier/barrier.h b/test/common_plat/validation/api/barrier/barrier.h
deleted file mode 100644
index 188bcb8fa..000000000
--- a/test/common_plat/validation/api/barrier/barrier.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_BARRIER_H_
-#define _ODP_TEST_BARRIER_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void barrier_test_memory_barrier(void);
-void barrier_test_no_barrier_functional(void);
-void barrier_test_barrier_functional(void);
-
-/* test arrays: */
-extern odp_testinfo_t barrier_suite_barrier[];
-
-/* test registry: */
-extern odp_suiteinfo_t barrier_suites[];
-
-/* executable init/term functions: */
-int barrier_init(odp_instance_t *inst);
-int barrier_term(odp_instance_t inst);
-
-/* main test program: */
-int barrier_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/barrier/barrier_main.c b/test/common_plat/validation/api/barrier/barrier_main.c
deleted file mode 100644
index 064decf6c..000000000
--- a/test/common_plat/validation/api/barrier/barrier_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "barrier.h"
-
-int main(int argc, char *argv[])
-{
- return barrier_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/buffer/Makefile.am b/test/common_plat/validation/api/buffer/Makefile.am
deleted file mode 100644
index add2a3419..000000000
--- a/test/common_plat/validation/api/buffer/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestbuffer.la
-libtestbuffer_la_SOURCES = buffer.c
-
-test_PROGRAMS = buffer_main$(EXEEXT)
-dist_buffer_main_SOURCES = buffer_main.c
-buffer_main_LDADD = libtestbuffer.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = buffer.h
diff --git a/test/common_plat/validation/api/buffer/buffer.c b/test/common_plat/validation/api/buffer/buffer.c
deleted file mode 100644
index 7c723d4f4..000000000
--- a/test/common_plat/validation/api/buffer/buffer.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include "odp_cunit_common.h"
-#include "buffer.h"
-
-#define BUF_ALIGN ODP_CACHE_LINE_SIZE
-#define BUF_SIZE 1500
-
-static odp_pool_t raw_pool;
-static odp_buffer_t raw_buffer = ODP_BUFFER_INVALID;
-
-int buffer_suite_init(void)
-{
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_BUFFER;
- params.buf.size = BUF_SIZE;
- params.buf.align = BUF_ALIGN;
- params.buf.num = 100;
-
- raw_pool = odp_pool_create("raw_pool", &params);
- if (raw_pool == ODP_POOL_INVALID)
- return -1;
- raw_buffer = odp_buffer_alloc(raw_pool);
- if (raw_buffer == ODP_BUFFER_INVALID)
- return -1;
- return 0;
-}
-
-int buffer_suite_term(void)
-{
- odp_buffer_free(raw_buffer);
- if (odp_pool_destroy(raw_pool) != 0)
- return -1;
- return 0;
-}
-
-void buffer_test_pool_alloc(void)
-{
- odp_pool_t pool;
- const int num = 3;
- odp_buffer_t buffer[num];
- odp_event_t ev;
- int index;
- char wrong_type = 0, wrong_size = 0, wrong_align = 0;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_BUFFER;
- params.buf.size = BUF_SIZE;
- params.buf.align = BUF_ALIGN;
- params.buf.num = num;
-
- pool = odp_pool_create("buffer_pool_alloc", &params);
- odp_pool_print(pool);
-
- /* Try to allocate num items from the pool */
- for (index = 0; index < num; index++) {
- uintptr_t addr;
-
- buffer[index] = odp_buffer_alloc(pool);
-
- if (buffer[index] == ODP_BUFFER_INVALID)
- break;
-
- ev = odp_buffer_to_event(buffer[index]);
- if (odp_event_type(ev) != ODP_EVENT_BUFFER)
- wrong_type = 1;
- if (odp_buffer_size(buffer[index]) < BUF_SIZE)
- wrong_size = 1;
-
- addr = (uintptr_t)odp_buffer_addr(buffer[index]);
-
- if ((addr % BUF_ALIGN) != 0)
- wrong_align = 1;
-
- if (wrong_type || wrong_size || wrong_align)
- odp_buffer_print(buffer[index]);
- }
-
- /* Check that the pool had at least num items */
- CU_ASSERT(index == num);
- /* index points out of buffer[] or it point to an invalid buffer */
- index--;
-
- /* Check that the pool had correct buffers */
- CU_ASSERT(wrong_type == 0);
- CU_ASSERT(wrong_size == 0);
- CU_ASSERT(wrong_align == 0);
-
- for (; index >= 0; index--)
- odp_buffer_free(buffer[index]);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-/* Wrapper to call odp_buffer_alloc_multi multiple times until
- * either no mure buffers are returned, or num buffers were alloced */
-static int buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buffer[], int num)
-{
- int ret, total = 0;
-
- do {
- ret = odp_buffer_alloc_multi(pool, buffer + total, num - total);
- CU_ASSERT(ret >= 0);
- CU_ASSERT(ret <= num - total);
- total += ret;
- } while (total < num && ret);
-
- return total;
-}
-
-void buffer_test_pool_alloc_multi(void)
-{
- odp_pool_t pool;
- const int num = 3;
- odp_buffer_t buffer[num + 1];
- odp_event_t ev;
- int index;
- char wrong_type = 0, wrong_size = 0, wrong_align = 0;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_BUFFER;
- params.buf.size = BUF_SIZE;
- params.buf.align = BUF_ALIGN;
- params.buf.num = num;
-
- pool = odp_pool_create("buffer_pool_alloc_multi", &params);
- odp_pool_print(pool);
-
- /* Try to allocate num + 1 items from the pool */
- CU_ASSERT_FATAL(buffer_alloc_multi(pool, buffer, num + 1) == num);
-
- for (index = 0; index < num; index++) {
- uintptr_t addr;
-
- if (buffer[index] == ODP_BUFFER_INVALID)
- break;
-
- ev = odp_buffer_to_event(buffer[index]);
- if (odp_event_type(ev) != ODP_EVENT_BUFFER)
- wrong_type = 1;
- if (odp_buffer_size(buffer[index]) < BUF_SIZE)
- wrong_size = 1;
-
- addr = (uintptr_t)odp_buffer_addr(buffer[index]);
-
- if ((addr % BUF_ALIGN) != 0)
- wrong_align = 1;
-
- if (wrong_type || wrong_size || wrong_align)
- odp_buffer_print(buffer[index]);
- }
-
- /* Check that the pool had at least num items */
- CU_ASSERT(index == num);
-
- /* Check that the pool had correct buffers */
- CU_ASSERT(wrong_type == 0);
- CU_ASSERT(wrong_size == 0);
- CU_ASSERT(wrong_align == 0);
-
- odp_buffer_free_multi(buffer, num);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void buffer_test_pool_free(void)
-{
- odp_pool_t pool;
- odp_buffer_t buffer;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_BUFFER;
- params.buf.size = 64;
- params.buf.align = BUF_ALIGN;
- params.buf.num = 1;
-
- pool = odp_pool_create("buffer_pool_free", &params);
-
- /* Allocate the only buffer from the pool */
- buffer = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buffer != ODP_BUFFER_INVALID);
-
- /* Pool should have only one buffer */
- CU_ASSERT_FATAL(odp_buffer_alloc(pool) == ODP_BUFFER_INVALID)
-
- odp_buffer_free(buffer);
-
- /* Check that the buffer was returned back to the pool */
- buffer = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buffer != ODP_BUFFER_INVALID);
-
- odp_buffer_free(buffer);
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void buffer_test_pool_free_multi(void)
-{
- odp_pool_t pool[2];
- odp_buffer_t buffer[4];
- odp_buffer_t buf_inval[2];
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_BUFFER;
- params.buf.size = 64;
- params.buf.align = BUF_ALIGN;
- params.buf.num = 2;
-
- pool[0] = odp_pool_create("buffer_pool_free_multi_0", &params);
- pool[1] = odp_pool_create("buffer_pool_free_multi_1", &params);
- CU_ASSERT_FATAL(pool[0] != ODP_POOL_INVALID);
- CU_ASSERT_FATAL(pool[1] != ODP_POOL_INVALID);
-
- /* Allocate all the buffers from the pools */
- CU_ASSERT_FATAL(buffer_alloc_multi(pool[0], &buffer[0], 2) == 2);
- CU_ASSERT_FATAL(buffer_alloc_multi(pool[1], &buffer[2], 2) == 2);
-
- /* Pools should have no more buffer */
- CU_ASSERT(odp_buffer_alloc_multi(pool[0], buf_inval, 2) == 0);
- CU_ASSERT(odp_buffer_alloc_multi(pool[1], buf_inval, 2) == 0);
-
- /* Try to free both buffers from both pools at once */
- odp_buffer_free_multi(buffer, 4);
-
- /* Check that all buffers were returned back to the pools */
- CU_ASSERT_FATAL(buffer_alloc_multi(pool[0], &buffer[0], 2) == 2);
- CU_ASSERT_FATAL(buffer_alloc_multi(pool[1], &buffer[2], 2) == 2);
-
- odp_buffer_free_multi(buffer, 4);
- CU_ASSERT(odp_pool_destroy(pool[0]) == 0);
- CU_ASSERT(odp_pool_destroy(pool[1]) == 0);
-}
-
-void buffer_test_management_basic(void)
-{
- odp_event_t ev = odp_buffer_to_event(raw_buffer);
-
- CU_ASSERT(odp_buffer_is_valid(raw_buffer) == 1);
- CU_ASSERT(odp_buffer_pool(raw_buffer) != ODP_POOL_INVALID);
- CU_ASSERT(odp_event_type(ev) == ODP_EVENT_BUFFER);
- CU_ASSERT(odp_buffer_size(raw_buffer) >= BUF_SIZE);
- CU_ASSERT(odp_buffer_addr(raw_buffer) != NULL);
- odp_buffer_print(raw_buffer);
- CU_ASSERT(odp_buffer_to_u64(raw_buffer) !=
- odp_buffer_to_u64(ODP_BUFFER_INVALID));
- CU_ASSERT(odp_event_to_u64(ev) != odp_event_to_u64(ODP_EVENT_INVALID));
-}
-
-odp_testinfo_t buffer_suite[] = {
- ODP_TEST_INFO(buffer_test_pool_alloc),
- ODP_TEST_INFO(buffer_test_pool_free),
- ODP_TEST_INFO(buffer_test_pool_alloc_multi),
- ODP_TEST_INFO(buffer_test_pool_free_multi),
- ODP_TEST_INFO(buffer_test_management_basic),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t buffer_suites[] = {
- {"buffer tests", buffer_suite_init, buffer_suite_term, buffer_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int buffer_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(buffer_suites);
-
- if (ret == 0)
- odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/buffer/buffer.h b/test/common_plat/validation/api/buffer/buffer.h
deleted file mode 100644
index 48331e3f1..000000000
--- a/test/common_plat/validation/api/buffer/buffer.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_BUFFER_H_
-#define _ODP_TEST_BUFFER_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void buffer_test_pool_alloc(void);
-void buffer_test_pool_free(void);
-void buffer_test_pool_alloc_multi(void);
-void buffer_test_pool_free_multi(void);
-void buffer_test_management_basic(void);
-
-/* test arrays: */
-extern odp_testinfo_t buffer_suite[];
-
-/* test array init/term functions: */
-int buffer_suite_init(void);
-int buffer_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t buffer_suites[];
-
-/* main test program: */
-int buffer_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/buffer/buffer_main.c b/test/common_plat/validation/api/buffer/buffer_main.c
deleted file mode 100644
index 47168f8b9..000000000
--- a/test/common_plat/validation/api/buffer/buffer_main.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include "buffer.h"
-
-int main(int argc, char *argv[])
-{
- return buffer_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/classification/Makefile.am b/test/common_plat/validation/api/classification/Makefile.am
deleted file mode 100644
index df382c51f..000000000
--- a/test/common_plat/validation/api/classification/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestclassification.la
-libtestclassification_la_SOURCES = odp_classification_basic.c \
- odp_classification_tests.c \
- odp_classification_test_pmr.c \
- odp_classification_common.c \
- classification.c
-
-test_PROGRAMS = classification_main$(EXEEXT)
-dist_classification_main_SOURCES = classification_main.c
-classification_main_LDADD = libtestclassification.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = classification.h odp_classification_testsuites.h
diff --git a/test/common_plat/validation/api/classification/classification.c b/test/common_plat/validation/api/classification/classification.c
deleted file mode 100644
index 1032e7f1f..000000000
--- a/test/common_plat/validation/api/classification/classification.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "odp_classification_testsuites.h"
-#include "classification.h"
-
-odp_suiteinfo_t classification_suites[] = {
- { .pName = "classification basic",
- .pTests = classification_suite_basic,
- },
- { .pName = "classification pmr tests",
- .pTests = classification_suite_pmr,
- .pInitFunc = classification_suite_pmr_init,
- .pCleanupFunc = classification_suite_pmr_term,
- },
- { .pName = "classification tests",
- .pTests = classification_suite,
- .pInitFunc = classification_suite_init,
- .pCleanupFunc = classification_suite_term,
- },
- ODP_SUITE_INFO_NULL,
-};
-
-int classification_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(classification_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/classification/classification.h b/test/common_plat/validation/api/classification/classification.h
deleted file mode 100644
index 326177ad8..000000000
--- a/test/common_plat/validation/api/classification/classification.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_CLASSIFICATION_H_
-#define _ODP_TEST_CLASSIFICATION_H_
-
-#include <odp_cunit_common.h>
-
-#define SHM_PKT_NUM_BUFS 32
-#define SHM_PKT_BUF_SIZE 1024
-
-/* Config values for Default CoS */
-#define TEST_DEFAULT 1
-#define CLS_DEFAULT 0
-#define CLS_DEFAULT_SADDR "10.0.0.1/32"
-#define CLS_DEFAULT_DADDR "10.0.0.100/32"
-#define CLS_DEFAULT_SPORT 1024
-#define CLS_DEFAULT_DPORT 2048
-#define CLS_DEFAULT_DMAC 0x010203040506
-#define CLS_DEFAULT_SMAC 0x060504030201
-
-/* Config values for Error CoS */
-#define TEST_ERROR 1
-#define CLS_ERROR 1
-
-/* Config values for PMR_CHAIN */
-#define TEST_PMR_CHAIN 1
-#define CLS_PMR_CHAIN_SRC 2
-#define CLS_PMR_CHAIN_DST 3
-#define CLS_PMR_CHAIN_SADDR "10.0.0.5/32"
-#define CLS_PMR_CHAIN_PORT 3000
-
-/* Config values for PMR */
-#define TEST_PMR 1
-#define CLS_PMR 4
-#define CLS_PMR_PORT 4000
-
-/* Config values for PMR SET */
-#define TEST_PMR_SET 1
-#define CLS_PMR_SET 5
-#define CLS_PMR_SET_SADDR "10.0.0.6/32"
-#define CLS_PMR_SET_PORT 5000
-
-/* Config values for CoS L2 Priority */
-#define TEST_L2_QOS 1
-#define CLS_L2_QOS_0 6
-#define CLS_L2_QOS_MAX 5
-
-#define CLS_ENTRIES (CLS_L2_QOS_0 + CLS_L2_QOS_MAX)
-
-/* Test Packet values */
-#define DATA_MAGIC 0x01020304
-#define TEST_SEQ_INVALID ((uint32_t)~0)
-
-/* Test packet Time-to-live */
-#define DEFAULT_TTL 128
-
-/* Test packet default DSCP value */
-#define LOW_DROP_PRECEDENCE 0x02
-#define MEDIUM_DROP_PRECEDENCE 0x04
-#define HIGH_DROP_PRECEDENCE 0x06
-#define DROP_PRECEDENCE_MASK 0x06
-#define DSCP_CLASS1 0x08
-#define DSCP_CLASS2 0x10
-#define DSCP_CLASS3 0x18
-#define DSCP_CLASS4 0x20
-#define DEFAULT_DSCP (DSCP_CLASS2 | LOW_DROP_PRECEDENCE)
-
-/* Test packet default ECN */
-#define DEFAULT_ECN ODPH_IP_ECN_ECT0
-
-/* Test packet default TOS */
-#define DEFAULT_TOS ((DEFAULT_DSCP << ODPH_IP_TOS_DSCP_SHIFT) | \
- DEFAULT_ECN)
-
-/* test functions: */
-void classification_test_create_cos(void);
-void classification_test_destroy_cos(void);
-void classification_test_create_pmr_match(void);
-void classification_test_cos_set_queue(void);
-void classification_test_cos_set_pool(void);
-void classification_test_cos_set_drop(void);
-void classification_test_pmr_composite_create(void);
-void classification_test_pmr_composite_destroy(void);
-
-void classification_test_pktio_set_skip(void);
-void classification_test_pktio_set_headroom(void);
-void classification_test_pktio_configure(void);
-void classification_test_pktio_test(void);
-
-void classification_test_pmr_term_tcp_dport(void);
-void classification_test_pmr_term_tcp_sport(void);
-void classification_test_pmr_term_udp_dport(void);
-void classification_test_pmr_term_udp_sport(void);
-void classification_test_pmr_term_ipproto(void);
-void classification_test_pmr_term_dmac(void);
-void classification_test_pmr_term_packet_len(void);
-void classification_test_pmr_term_vlan_id_0(void);
-void classification_test_pmr_term_vlan_id_x(void);
-void classification_test_pmr_term_eth_type_0(void);
-void classification_test_pmr_term_eth_type_x(void);
-void classification_test_pktin_classifier_flag(void);
-
-/* test arrays: */
-extern odp_testinfo_t classification_suite_basic[];
-extern odp_testinfo_t classification_suite[];
-
-/* test array init/term functions: */
-int classification_suite_init(void);
-int classification_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t classification_suites[];
-
-/* main test program: */
-int classification_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/classification/classification_main.c b/test/common_plat/validation/api/classification/classification_main.c
deleted file mode 100644
index 8902463c2..000000000
--- a/test/common_plat/validation/api/classification/classification_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "classification.h"
-
-int main(int argc, char *argv[])
-{
- return classification_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/classification/odp_classification_basic.c b/test/common_plat/validation/api/classification/odp_classification_basic.c
deleted file mode 100644
index 3f4a82ed3..000000000
--- a/test/common_plat/validation/api/classification/odp_classification_basic.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_cunit_common.h>
-#include "odp_classification_testsuites.h"
-#include "classification.h"
-
-#define PMR_SET_NUM 5
-
-void classification_test_create_cos(void)
-{
- odp_cos_t cos;
- odp_cls_cos_param_t cls_param;
- odp_pool_t pool;
- odp_queue_t queue;
-
- pool = pool_create("cls_basic_pool");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- queue = queue_create("cls_basic_queue", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(NULL, &cls_param);
- CU_ASSERT(odp_cos_to_u64(cos) != odp_cos_to_u64(ODP_COS_INVALID));
- odp_cos_destroy(cos);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
-}
-
-void classification_test_destroy_cos(void)
-{
- odp_cos_t cos;
- char name[ODP_COS_NAME_LEN];
- odp_pool_t pool;
- odp_queue_t queue;
- odp_cls_cos_param_t cls_param;
- int retval;
-
- pool = pool_create("cls_basic_pool");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- queue = queue_create("cls_basic_queue", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- sprintf(name, "ClassOfService");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(name, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
- retval = odp_cos_destroy(cos);
- CU_ASSERT(retval == 0);
- retval = odp_cos_destroy(ODP_COS_INVALID);
- CU_ASSERT(retval < 0);
-
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
-}
-
-void classification_test_create_pmr_match(void)
-{
- odp_pmr_t pmr;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pmr_param_t pmr_param;
- odp_cos_t default_cos;
- odp_cos_t cos;
- odp_queue_t default_queue;
- odp_queue_t queue;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t pkt_pool;
- odp_cls_cos_param_t cls_param;
- odp_pktio_t pktio;
-
- pkt_pool = pool_create("pkt_pool");
- CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID);
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("pmr_match", true);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("pmr_match");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create("pmr_match", &cls_param);
- CU_ASSERT(cos != ODP_COS_INVALID);
-
- val = 1024;
- mask = 0xffff;
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = find_first_supported_l3_pmr();
- pmr_param.range_term = false;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
- CU_ASSERT(odp_pmr_to_u64(pmr) != odp_pmr_to_u64(ODP_PMR_INVAL));
- /* destroy the created PMR */
- retval = odp_cls_pmr_destroy(pmr);
- CU_ASSERT(retval == 0);
-
- /* destroy an INVALID PMR */
- retval = odp_cls_pmr_destroy(ODP_PMR_INVAL);
- CU_ASSERT(retval < 0);
-
- odp_queue_destroy(queue);
- odp_pool_destroy(pool);
- odp_pool_destroy(pkt_pool);
- odp_cos_destroy(cos);
- odp_queue_destroy(default_queue);
- odp_pool_destroy(default_pool);
- odp_cos_destroy(default_cos);
- odp_pktio_close(pktio);
-}
-
-void classification_test_cos_set_queue(void)
-{
- int retval;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pool_t pool;
- odp_queue_t queue;
- odp_queue_t queue_cos;
- odp_cos_t cos_queue;
- odp_queue_t recvqueue;
-
- pool = pool_create("cls_basic_pool");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- queue = queue_create("cls_basic_queue", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- sprintf(cosname, "CoSQueue");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_queue = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_queue != ODP_COS_INVALID);
-
- queue_cos = queue_create("QueueCoS", true);
- CU_ASSERT_FATAL(queue_cos != ODP_QUEUE_INVALID);
-
- retval = odp_cos_queue_set(cos_queue, queue_cos);
- CU_ASSERT(retval == 0);
- recvqueue = odp_cos_queue(cos_queue);
- CU_ASSERT(recvqueue == queue_cos);
-
- odp_cos_destroy(cos_queue);
- odp_queue_destroy(queue_cos);
- odp_queue_destroy(queue);
- odp_pool_destroy(pool);
-}
-
-void classification_test_cos_set_pool(void)
-{
- int retval;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pool_t pool;
- odp_queue_t queue;
- odp_pool_t cos_pool;
- odp_cos_t cos;
- odp_pool_t recvpool;
-
- pool = pool_create("cls_basic_pool");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- queue = queue_create("cls_basic_queue", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- sprintf(cosname, "CoSQueue");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- cos_pool = pool_create("PoolCoS");
- CU_ASSERT_FATAL(cos_pool != ODP_POOL_INVALID);
-
- retval = odp_cls_cos_pool_set(cos, cos_pool);
- CU_ASSERT(retval == 0);
- recvpool = odp_cls_cos_pool(cos);
- CU_ASSERT(recvpool == cos_pool);
-
- odp_cos_destroy(cos);
- odp_queue_destroy(queue);
- odp_pool_destroy(pool);
- odp_pool_destroy(cos_pool);
-}
-
-void classification_test_cos_set_drop(void)
-{
- int retval;
- char cosname[ODP_COS_NAME_LEN];
- odp_cos_t cos_drop;
- odp_queue_t queue;
- odp_pool_t pool;
- odp_cls_cos_param_t cls_param;
-
- pool = pool_create("cls_basic_pool");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- queue = queue_create("cls_basic_queue", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- sprintf(cosname, "CoSDrop");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_drop = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_drop != ODP_COS_INVALID);
-
- retval = odp_cos_drop_set(cos_drop, ODP_COS_DROP_POOL);
- CU_ASSERT(retval == 0);
- CU_ASSERT(ODP_COS_DROP_POOL == odp_cos_drop(cos_drop));
-
- retval = odp_cos_drop_set(cos_drop, ODP_COS_DROP_NEVER);
- CU_ASSERT(retval == 0);
- CU_ASSERT(ODP_COS_DROP_NEVER == odp_cos_drop(cos_drop));
- odp_cos_destroy(cos_drop);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
-}
-
-void classification_test_pmr_composite_create(void)
-{
- odp_pmr_t pmr_composite;
- int retval;
- odp_pmr_param_t pmr_terms[PMR_SET_NUM];
- odp_cos_t default_cos;
- odp_cos_t cos;
- odp_queue_t default_queue;
- odp_queue_t queue;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t pkt_pool;
- odp_cls_cos_param_t cls_param;
- odp_pktio_t pktio;
- uint16_t val = 1024;
- uint16_t mask = 0xffff;
- int i;
-
- pkt_pool = pool_create("pkt_pool");
- CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID);
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("pmr_match", true);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("pmr_match");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create("pmr_match", &cls_param);
- CU_ASSERT(cos != ODP_COS_INVALID);
-
- for (i = 0; i < PMR_SET_NUM; i++) {
- odp_cls_pmr_param_init(&pmr_terms[i]);
- pmr_terms[i].term = ODP_PMR_TCP_DPORT;
- pmr_terms[i].match.value = &val;
- pmr_terms[i].range_term = false;
- pmr_terms[i].match.mask = &mask;
- pmr_terms[i].val_sz = sizeof(val);
- }
-
- pmr_composite = odp_cls_pmr_create(pmr_terms, PMR_SET_NUM,
- default_cos, cos);
- CU_ASSERT(odp_pmr_to_u64(pmr_composite) !=
- odp_pmr_to_u64(ODP_PMR_INVAL));
-
- retval = odp_cls_pmr_destroy(pmr_composite);
- CU_ASSERT(retval == 0);
-
- odp_queue_destroy(queue);
- odp_pool_destroy(pool);
- odp_pool_destroy(pkt_pool);
- odp_cos_destroy(cos);
- odp_queue_destroy(default_queue);
- odp_pool_destroy(default_pool);
- odp_cos_destroy(default_cos);
- odp_pktio_close(pktio);
-}
-
-odp_testinfo_t classification_suite_basic[] = {
- ODP_TEST_INFO(classification_test_create_cos),
- ODP_TEST_INFO(classification_test_destroy_cos),
- ODP_TEST_INFO(classification_test_create_pmr_match),
- ODP_TEST_INFO(classification_test_cos_set_queue),
- ODP_TEST_INFO(classification_test_cos_set_drop),
- ODP_TEST_INFO(classification_test_cos_set_pool),
- ODP_TEST_INFO(classification_test_pmr_composite_create),
- ODP_TEST_INFO_NULL,
-};
diff --git a/test/common_plat/validation/api/classification/odp_classification_common.c b/test/common_plat/validation/api/classification/odp_classification_common.c
deleted file mode 100644
index de8a9327f..000000000
--- a/test/common_plat/validation/api/classification/odp_classification_common.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "odp_classification_testsuites.h"
-#include "classification.h"
-#include <odp_cunit_common.h>
-#include "test_debug.h"
-
-typedef struct cls_test_packet {
- odp_u32be_t magic;
- odp_u32be_t seq;
-} cls_test_packet_t;
-
-static uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.0.0.1 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 1
-};
-
-static uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.0.0.100 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 100
-};
-
-odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool,
- odp_bool_t cls_enable)
-{
- odp_pktio_t pktio;
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t pktin_param;
- int ret;
-
- if (pool == ODP_POOL_INVALID)
- return ODP_PKTIO_INVALID;
-
- odp_pktio_param_init(&pktio_param);
- if (q_type == ODP_QUEUE_TYPE_PLAIN)
- pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
- else
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open("loop", pool, &pktio_param);
- if (pktio == ODP_PKTIO_INVALID) {
- ret = odp_pool_destroy(pool);
- if (ret)
- fprintf(stderr, "unable to destroy pool.\n");
- return ODP_PKTIO_INVALID;
- }
-
- odp_pktin_queue_param_init(&pktin_param);
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- pktin_param.classifier_enable = cls_enable;
- pktin_param.hash_enable = false;
-
- if (odp_pktin_queue_config(pktio, &pktin_param)) {
- fprintf(stderr, "pktin queue config failed.\n");
- return ODP_PKTIO_INVALID;
- }
-
- if (odp_pktout_queue_config(pktio, NULL)) {
- fprintf(stderr, "pktout queue config failed.\n");
- return ODP_PKTIO_INVALID;
- }
-
- return pktio;
-}
-
-int stop_pktio(odp_pktio_t pktio)
-{
- odp_event_t ev;
-
- if (odp_pktio_stop(pktio)) {
- fprintf(stderr, "pktio stop failed.\n");
- return -1;
- }
-
- while (1) {
- ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
-
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
- else
- break;
- }
-
- return 0;
-}
-
-int cls_pkt_set_seq(odp_packet_t pkt)
-{
- static uint32_t seq;
- cls_test_packet_t data;
- uint32_t offset;
- odph_ipv4hdr_t *ip;
- odph_tcphdr_t *tcp;
- int status;
-
- data.magic = DATA_MAGIC;
- data.seq = ++seq;
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- offset = odp_packet_l4_offset(pkt);
- CU_ASSERT_FATAL(offset != ODP_PACKET_OFFSET_INVALID);
-
- if (ip->proto == ODPH_IPPROTO_UDP)
- status = odp_packet_copy_from_mem(pkt, offset + ODPH_UDPHDR_LEN,
- sizeof(data), &data);
- else {
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- status = odp_packet_copy_from_mem(pkt, offset + tcp->hl * 4,
- sizeof(data), &data);
- }
-
- return status;
-}
-
-uint32_t cls_pkt_get_seq(odp_packet_t pkt)
-{
- uint32_t offset;
- cls_test_packet_t data;
- odph_ipv4hdr_t *ip;
- odph_tcphdr_t *tcp;
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- offset = odp_packet_l4_offset(pkt);
-
- if (offset == ODP_PACKET_OFFSET_INVALID || ip == NULL)
- return TEST_SEQ_INVALID;
-
- if (ip->proto == ODPH_IPPROTO_UDP)
- odp_packet_copy_to_mem(pkt, offset + ODPH_UDPHDR_LEN,
- sizeof(data), &data);
- else {
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- odp_packet_copy_to_mem(pkt, offset + tcp->hl * 4,
- sizeof(data), &data);
- }
-
- if (data.magic == DATA_MAGIC)
- return data.seq;
-
- return TEST_SEQ_INVALID;
-}
-
-int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask)
-{
- int b[4];
- int qualifier = 32;
- int converted;
-
- if (strchr(ipaddress, '/')) {
- converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
- &b[3], &b[2], &b[1], &b[0],
- &qualifier);
- if (5 != converted)
- return -1;
- } else {
- converted = sscanf(ipaddress, "%d.%d.%d.%d",
- &b[3], &b[2], &b[1], &b[0]);
- if (4 != converted)
- return -1;
- }
-
- if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
- return -1;
- if (!qualifier || (qualifier > 32))
- return -1;
-
- *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
- if (mask)
- *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1));
-
- return 0;
-}
-
-void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio)
-{
- odp_pktout_queue_t pktout;
-
- CU_ASSERT_FATAL(odp_pktout_queue(pktio, &pktout, 1) == 1);
- CU_ASSERT(odp_pktout_send(pktout, &pkt, 1) == 1);
-}
-
-odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns)
-{
- odp_event_t ev;
-
- ev = odp_schedule(queue, ns);
- return odp_packet_from_event(ev);
-}
-
-odp_queue_t queue_create(const char *queuename, bool sched)
-{
- odp_queue_t queue;
- odp_queue_param_t qparam;
-
- if (sched) {
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
-
- queue = odp_queue_create(queuename, &qparam);
- } else {
- queue = odp_queue_create(queuename, NULL);
- }
-
- return queue;
-}
-
-odp_pool_t pool_create(const char *poolname)
-{
- odp_pool_param_t param;
-
- odp_pool_param_init(&param);
- param.pkt.seg_len = SHM_PKT_BUF_SIZE;
- param.pkt.len = SHM_PKT_BUF_SIZE;
- param.pkt.num = SHM_PKT_NUM_BUFS;
- param.type = ODP_POOL_PACKET;
-
- return odp_pool_create(poolname, &param);
-}
-
-odp_packet_t create_packet(cls_packet_info_t pkt_info)
-{
- uint32_t seqno;
- odph_ethhdr_t *ethhdr;
- odph_udphdr_t *udp;
- odph_tcphdr_t *tcp;
- odph_ipv4hdr_t *ip;
- odph_ipv6hdr_t *ipv6;
- uint16_t payload_len;
- uint64_t src_mac = CLS_DEFAULT_SMAC;
- uint64_t dst_mac = CLS_DEFAULT_DMAC;
- uint64_t dst_mac_be;
- uint32_t addr = 0;
- uint32_t mask;
- odp_packet_t pkt;
- int packet_len = 0;
- uint32_t version, tc, flow, ver_tc_flow;
- uint8_t *buf, next_hdr;
- uint32_t l4_len, l3_len, l2_len, l3_offset, l4_offset;
- uint16_t vlan_hdr_len = 0;
- uint16_t l2_hdr_len = 0;
- uint16_t l3_hdr_len = 0;
- uint16_t l4_hdr_len = 0;
- uint16_t eth_type;
- odp_u16be_t *vlan_type;
- odph_vlanhdr_t *vlan_hdr;
-
- /* 48 bit ethernet address needs to be left shifted for proper
- value after changing to be*/
- dst_mac_be = odp_cpu_to_be_64(dst_mac);
- if (dst_mac != dst_mac_be)
- dst_mac_be = dst_mac_be >> (64 - 8 * ODPH_ETHADDR_LEN);
-
- payload_len = sizeof(cls_test_packet_t) + pkt_info.len;
- seqno = odp_atomic_fetch_inc_u32(pkt_info.seq);
-
- vlan_hdr_len = pkt_info.vlan ? ODPH_VLANHDR_LEN : 0;
- vlan_hdr_len = pkt_info.vlan_qinq ? 2 * vlan_hdr_len : vlan_hdr_len;
- l3_hdr_len = pkt_info.ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
- l4_hdr_len = pkt_info.udp ? ODPH_UDPHDR_LEN : ODPH_TCPHDR_LEN;
- eth_type = pkt_info.ipv6 ? ODPH_ETHTYPE_IPV6 : ODPH_ETHTYPE_IPV4;
- next_hdr = pkt_info.udp ? ODPH_IPPROTO_UDP : ODPH_IPPROTO_TCP;
- l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
- l4_len = l4_hdr_len + payload_len;
- l3_len = l3_hdr_len + l4_len;
- l2_len = l2_hdr_len + l3_len;
- packet_len = l2_len;
-
- pkt = odp_packet_alloc(pkt_info.pool, packet_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- /* Ethernet Header */
- buf = odp_packet_data(pkt);
- odp_packet_l2_offset_set(pkt, 0);
- ethhdr = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- memcpy(ethhdr->src.addr, &src_mac, ODPH_ETHADDR_LEN);
- memcpy(ethhdr->dst.addr, &dst_mac_be, ODPH_ETHADDR_LEN);
- vlan_type = (odp_u16be_t *)(void *)&ethhdr->type;
- vlan_hdr = (odph_vlanhdr_t *)(ethhdr + 1);
-
- if (pkt_info.vlan_qinq) {
- odp_packet_has_vlan_qinq_set(pkt, 1);
- *vlan_type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN_OUTER);
- vlan_hdr->tci = odp_cpu_to_be_16(0);
- vlan_type = (uint16_t *)(void *)&vlan_hdr->type;
- vlan_hdr++;
- }
- if (pkt_info.vlan) {
- /* Default vlan header */
- odp_packet_has_vlan_set(pkt, 1);
- *vlan_type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN);
- vlan_hdr->tci = odp_cpu_to_be_16(0);
- vlan_hdr->type = odp_cpu_to_be_16(eth_type);
- } else {
- ethhdr->type = odp_cpu_to_be_16(eth_type);
- }
-
- l3_offset = l2_hdr_len;
- odp_packet_l3_offset_set(pkt, l3_offset);
-
- if (!pkt_info.ipv6) {
- /* ipv4 */
- ip = (odph_ipv4hdr_t *)(buf + l3_offset);
-
- parse_ipv4_string(CLS_DEFAULT_DADDR, &addr, &mask);
- ip->dst_addr = odp_cpu_to_be_32(addr);
-
- parse_ipv4_string(CLS_DEFAULT_SADDR, &addr, &mask);
- ip->src_addr = odp_cpu_to_be_32(addr);
- ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->id = odp_cpu_to_be_16(seqno);
- ip->chksum = 0;
- ip->chksum = odph_ipv4_csum_update(pkt);
- ip->proto = next_hdr;
- ip->tot_len = odp_cpu_to_be_16(l3_len);
- ip->ttl = DEFAULT_TTL;
- odp_packet_has_ipv4_set(pkt, 1);
- } else {
- /* ipv6 */
- odp_packet_has_ipv6_set(pkt, 1);
- ipv6 = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- version = ODPH_IPV6 << ODPH_IPV6HDR_VERSION_SHIFT;
- tc = DEFAULT_TOS << ODPH_IPV6HDR_TC_SHIFT;
- flow = seqno << ODPH_IPV6HDR_FLOW_LABEL_SHIFT;
- ver_tc_flow = version | tc | flow;
-
- ipv6->ver_tc_flow = odp_cpu_to_be_32(ver_tc_flow);
- ipv6->payload_len = odp_cpu_to_be_16(l4_len);
- ipv6->next_hdr = next_hdr;
- ipv6->hop_limit = DEFAULT_TTL;
- memcpy(ipv6->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
- memcpy(ipv6->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
- }
-
- l4_offset = l3_offset + l3_hdr_len;
- odp_packet_l4_offset_set(pkt, l4_offset);
- tcp = (odph_tcphdr_t *)(buf + l4_offset);
- udp = (odph_udphdr_t *)(buf + l4_offset);
-
- /* udp */
- if (pkt_info.udp) {
- udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
- udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
- udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
- udp->chksum = 0;
- odp_packet_has_udp_set(pkt, 1);
- if (odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) {
- LOG_ERR("odph_udp_tcp_chksum failed\n");
- return ODP_PACKET_INVALID;
- }
- } else {
- tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
- tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
- tcp->hl = ODPH_TCPHDR_LEN / 4;
- tcp->cksm = 0;
- odp_packet_has_tcp_set(pkt, 1);
- if (odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) {
- LOG_ERR("odph_udp_tcp_chksum failed\n");
- return ODP_PACKET_INVALID;
- }
-
- }
-
- /* set pkt sequence number */
- cls_pkt_set_seq(pkt);
-
- return pkt;
-}
-
-odp_cls_pmr_term_t find_first_supported_l3_pmr(void)
-{
- odp_cls_pmr_term_t term = ODP_PMR_TCP_DPORT;
- odp_cls_capability_t capability;
-
- odp_cls_capability(&capability);
-
- /* choose supported PMR */
- if (capability.supported_terms.bit.udp_sport)
- term = ODP_PMR_UDP_SPORT;
- else if (capability.supported_terms.bit.udp_dport)
- term = ODP_PMR_UDP_DPORT;
- else if (capability.supported_terms.bit.tcp_sport)
- term = ODP_PMR_TCP_SPORT;
- else if (capability.supported_terms.bit.tcp_dport)
- term = ODP_PMR_TCP_DPORT;
- else
- CU_FAIL("Implementations doesn't support any TCP/UDP PMR");
-
- return term;
-}
-
-int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port)
-{
- odph_udphdr_t *udp;
- odph_tcphdr_t *tcp;
- odp_cls_pmr_term_t term;
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- port = odp_cpu_to_be_16(port);
- term = find_first_supported_l3_pmr();
- switch (term) {
- case ODP_PMR_UDP_SPORT:
- udp->src_port = port;
- break;
- case ODP_PMR_UDP_DPORT:
- udp->dst_port = port;
- break;
- case ODP_PMR_TCP_DPORT:
- tcp->dst_port = port;
- break;
- case ODP_PMR_TCP_SPORT:
- tcp->src_port = port;
- break;
- default:
- CU_FAIL("Unsupported L3 term");
- return -1;
- }
-
- return 0;
-}
diff --git a/test/common_plat/validation/api/classification/odp_classification_test_pmr.c b/test/common_plat/validation/api/classification/odp_classification_test_pmr.c
deleted file mode 100644
index 0acf2cf74..000000000
--- a/test/common_plat/validation/api/classification/odp_classification_test_pmr.c
+++ /dev/null
@@ -1,1961 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "odp_classification_testsuites.h"
-#include "classification.h"
-#include <odp_cunit_common.h>
-
-static odp_pool_t pkt_pool;
-/** sequence number of IP packets */
-odp_atomic_u32_t seq;
-
-static cls_packet_info_t default_pkt_info;
-
-int classification_suite_pmr_init(void)
-{
- pkt_pool = pool_create("classification_pmr_pool");
- if (ODP_POOL_INVALID == pkt_pool) {
- fprintf(stderr, "Packet pool creation failed.\n");
- return -1;
- }
-
- memset(&default_pkt_info, 0, sizeof(cls_packet_info_t));
- default_pkt_info.pool = pkt_pool;
- default_pkt_info.seq = &seq;
-
- odp_atomic_init_u32(&seq, 0);
-
- return 0;
-}
-
-static int start_pktio(odp_pktio_t pktio)
-{
- if (odp_pktio_start(pktio)) {
- fprintf(stderr, "unable to start loop\n");
- return -1;
- }
-
- return 0;
-}
-
-void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos,
- odp_queue_t *queue, odp_pool_t *pool)
-{
- odp_cls_cos_param_t cls_param;
- odp_pool_t default_pool;
- odp_cos_t default_cos;
- odp_queue_t default_queue;
- int retval;
- char cosname[ODP_COS_NAME_LEN];
-
- default_pool = pool_create("DefaultPool");
- CU_ASSERT(default_pool != ODP_POOL_INVALID);
-
- default_queue = queue_create("DefaultQueue", true);
- CU_ASSERT(default_queue != ODP_QUEUE_INVALID);
-
- sprintf(cosname, "DefaultCos");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = default_pool;
- cls_param.queue = default_queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- default_cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT(default_cos != ODP_COS_INVALID);
-
- retval = odp_pktio_default_cos_set(pktio, default_cos);
- CU_ASSERT(retval == 0);
-
- *cos = default_cos;
- *queue = default_queue;
- *pool = default_pool;
-}
-
-int classification_suite_pmr_term(void)
-{
- int retcode = 0;
-
- if (0 != odp_pool_destroy(pkt_pool)) {
- fprintf(stderr, "pkt_pool destroy failed.\n");
- retcode = -1;
- }
-
- return retcode;
-}
-
-void classification_test_pktin_classifier_flag(void)
-{
- odp_packet_t pkt;
- odph_tcphdr_t *tcp;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pool_t pool;
- odp_pool_t pool_recv;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
-
- val = CLS_DEFAULT_DPORT;
- mask = 0xffff;
- seqno = 0;
-
- /* classifier is disabled in pktin queue configuration */
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, false);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("tcp_dport1", true);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("tcp_dport1");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "tcp_dport");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_TCP_DPORT;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
-
- enqueue_pktio_interface(pkt, pktio);
-
- /* since classifier flag is disabled in pktin queue configuration
- packet will not be delivered in classifier queues */
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- pool_recv = odp_packet_pool(pkt);
- /* since classifier is disabled packet should not be received in
- pool and queue configured with classifier */
- CU_ASSERT(pool != pool_recv);
- CU_ASSERT(retqueue != queue);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
-
- odp_packet_free(pkt);
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pool_destroy(pool);
- odp_pool_destroy(default_pool);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_tcp_dport(void)
-{
- odp_packet_t pkt;
- odph_tcphdr_t *tcp;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pool_t pool;
- odp_pool_t pool_recv;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- val = CLS_DEFAULT_DPORT;
- mask = 0xffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("tcp_dport1", true);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("tcp_dport1");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "tcp_dport");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_TCP_DPORT;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- pool_recv = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_recv);
- CU_ASSERT(retqueue == queue);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
-
- odp_packet_free(pkt);
-
- /* Other packets are delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
-
- odp_packet_free(pkt);
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pool_destroy(pool);
- odp_pool_destroy(default_pool);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_tcp_sport(void)
-{
- odp_packet_t pkt;
- odph_tcphdr_t *tcp;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
-
- val = CLS_DEFAULT_SPORT;
- mask = 0xffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("tcp_sport", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("tcp_sport");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "tcp_sport");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_TCP_SPORT;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- odp_packet_free(pkt);
-
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
-
- odp_packet_free(pkt);
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_udp_dport(void)
-{
- odp_packet_t pkt;
- odph_udphdr_t *udp;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_pmr_param_t pmr_param;
- odp_cls_cos_param_t cls_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = CLS_DEFAULT_DPORT;
- mask = 0xffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("udp_dport", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("udp_dport");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "udp_dport");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_UDP_DPORT;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- odp_packet_free(pkt);
-
- /* Other packets received in default queue */
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
-
- odp_packet_free(pkt);
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_udp_sport(void)
-{
- odp_packet_t pkt;
- odph_udphdr_t *udp;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_pmr_param_t pmr_param;
- odp_cls_cos_param_t cls_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = CLS_DEFAULT_SPORT;
- mask = 0xffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("udp_sport", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("udp_sport");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "udp_sport");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_UDP_SPORT;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- odp_packet_free(pkt);
-
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
- udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- odp_packet_free(pkt);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_ipproto(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint8_t val;
- uint8_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = ODPH_IPPROTO_UDP;
- mask = 0xff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("ipproto", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("ipproto");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "ipproto");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_IPPROTO;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_dmac(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint64_t val;
- uint64_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = CLS_DEFAULT_DMAC; /* 48 bit Ethernet Mac address */
- mask = 0xffffffffffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("dmac", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("dmac");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "dmac");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_DMAC;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = ODPH_ETHADDR_LEN;
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- memset(eth->dst.addr, 0, ODPH_ETHADDR_LEN);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_packet_len(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = 1024;
- /*Mask value will match any packet of length 1000 - 1099*/
- mask = 0xff00;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("packet_len", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("packet_len");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "packet_len");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_LEN;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- /* create packet of payload length 1024 */
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt_info.len = 1024;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_vlan_id_0(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- odph_vlanhdr_t *vlan_0;
- cls_packet_info_t pkt_info;
-
- val = 1024;
- mask = 0xff00;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("vlan_id_0", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("vlan_id_0");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "vlan_id_0");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_VLAN_ID_0;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- /* create packet of payload length 1024 */
- pkt_info = default_pkt_info;
- pkt_info.vlan = true;
- pkt_info.vlan_qinq = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- vlan_0 = (odph_vlanhdr_t *)(eth + 1);
- vlan_0->tci = odp_cpu_to_be_16(1024);
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_vlan_id_x(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- odph_vlanhdr_t *vlan_x;
- cls_packet_info_t pkt_info;
-
- val = 1024;
- mask = 0xff00;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("vlan_id_x", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("vlan_id_x");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "vlan_id_x");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_VLAN_ID_X;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- /* create packet of payload length 1024 */
- pkt_info = default_pkt_info;
- pkt_info.vlan = true;
- pkt_info.vlan_qinq = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- vlan_x = (odph_vlanhdr_t *)(eth + 1);
- vlan_x++;
- vlan_x->tci = odp_cpu_to_be_16(1024);
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_eth_type_0(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = 0x88A8;
- mask = 0xffff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("eth_type_0", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("eth_type_0");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "eth_type_0");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_ETHTYPE_0;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.vlan = true;
- pkt_info.vlan_qinq = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-void classification_test_pmr_term_eth_type_x(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint16_t val;
- uint16_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- odph_vlanhdr_t *vlan_x;
- cls_packet_info_t pkt_info;
-
- val = 0x8100;
- mask = 0xff00;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("eth_type_x", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("eth_type_x");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "eth_type_x");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_ETHTYPE_X;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- /* create packet of payload length 1024 */
- pkt_info = default_pkt_info;
- pkt_info.vlan = true;
- pkt_info.vlan_qinq = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- vlan_x = (odph_vlanhdr_t *)(eth + 1);
- vlan_x->tci = odp_cpu_to_be_16(1024);
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == default_pool);
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-static void classification_test_pmr_pool_set(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint8_t val;
- uint8_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_pool_t pool_new;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = ODPH_IPPROTO_UDP;
- mask = 0xff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("ipproto1", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("ipproto1");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "ipproto1");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- pool_new = pool_create("ipproto2");
- CU_ASSERT_FATAL(pool_new != ODP_POOL_INVALID);
-
- /* new pool is set on CoS */
- retval = odp_cls_cos_pool_set(cos, pool_new);
- CU_ASSERT(retval == 0);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_IPPROTO;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool_new);
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_pool_destroy(pool_new);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-static void classification_test_pmr_queue_set(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- uint8_t val;
- uint8_t mask;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_cos_t default_cos;
- odp_pool_t default_pool;
- odp_pool_t pool;
- odp_queue_t queue_new;
- odp_pool_t recvpool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- char cosname[ODP_COS_NAME_LEN];
- odp_cls_cos_param_t cls_param;
- odp_pmr_param_t pmr_param;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- val = ODPH_IPPROTO_UDP;
- mask = 0xff;
- seqno = 0;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("ipproto1", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("ipproto1");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "ipproto1");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- queue_new = queue_create("ipproto2", true);
- CU_ASSERT_FATAL(queue_new != ODP_QUEUE_INVALID);
-
- /* new queue is set on CoS */
- retval = odp_cos_queue_set(cos, queue_new);
- CU_ASSERT(retval == 0);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_IPPROTO;
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT(pmr != ODP_PMR_INVAL);
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- recvpool = odp_packet_pool(pkt);
- CU_ASSERT(recvpool == pool);
- CU_ASSERT(retqueue == queue_new);
- odp_packet_free(pkt);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue_new);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-static void classification_test_pmr_term_daddr(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_pool_t pool;
- odp_pool_t default_pool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- odp_cos_t default_cos;
- uint32_t addr;
- uint32_t mask;
- char cosname[ODP_QUEUE_NAME_LEN];
- odp_pmr_param_t pmr_param;
- odp_cls_cos_param_t cls_param;
- odph_ipv4hdr_t *ip;
- const char *dst_addr = "10.0.0.99/32";
- odph_ethhdr_t *eth;
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("daddr", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("daddr");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "daddr");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- parse_ipv4_string(dst_addr, &addr, &mask);
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_DIP_ADDR;
- pmr_param.match.value = &addr;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(addr);
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT_FATAL(pmr != ODP_PMR_INVAL);
-
- /* packet with dst ip address matching PMR rule to be
- received in the CoS queue*/
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- ip->dst_addr = odp_cpu_to_be_32(addr);
- ip->chksum = odph_ipv4_csum_update(pkt);
-
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(default_pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-static void classification_test_pmr_term_ipv6daddr(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_pool_t pool;
- odp_pool_t default_pool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- odp_cos_t default_cos;
- char cosname[ODP_QUEUE_NAME_LEN];
- odp_pmr_param_t pmr_param;
- odp_cls_cos_param_t cls_param;
- odph_ipv6hdr_t *ip;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
-
- uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.1.1.100 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 1, 1, 100
- };
- uint8_t ipv6_mask[ODPH_IPV6ADDR_LEN] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("daddr", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("daddr");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "daddr");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_DIP6_ADDR;
- pmr_param.match.value = IPV6_DST_ADDR;
- pmr_param.match.mask = ipv6_mask;
- pmr_param.val_sz = ODPH_IPV6ADDR_LEN;
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT_FATAL(pmr != ODP_PMR_INVAL);
-
- /* packet with dst ip address matching PMR rule to be
- received in the CoS queue*/
- pkt_info = default_pkt_info;
- pkt_info.ipv6 = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- ip = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- memcpy(ip->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
-
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-static void classification_test_pmr_term_ipv6saddr(void)
-{
- odp_packet_t pkt;
- uint32_t seqno;
- int retval;
- odp_pktio_t pktio;
- odp_queue_t queue;
- odp_queue_t retqueue;
- odp_queue_t default_queue;
- odp_pool_t pool;
- odp_pool_t default_pool;
- odp_pmr_t pmr;
- odp_cos_t cos;
- odp_cos_t default_cos;
- char cosname[ODP_QUEUE_NAME_LEN];
- odp_pmr_param_t pmr_param;
- odp_cls_cos_param_t cls_param;
- odph_ipv6hdr_t *ip;
- odph_ethhdr_t *eth;
- cls_packet_info_t pkt_info;
- uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.0.0.100 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 1, 1, 1
- };
- uint8_t ipv6_mask[ODPH_IPV6ADDR_LEN] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
-
- pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
- retval = start_pktio(pktio);
- CU_ASSERT(retval == 0);
-
- configure_default_cos(pktio, &default_cos,
- &default_queue, &default_pool);
-
- queue = queue_create("saddr", true);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = pool_create("saddr");
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- sprintf(cosname, "saddr");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue;
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
-
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_SIP6_ADDR;
- pmr_param.match.value = IPV6_SRC_ADDR;
- pmr_param.match.mask = ipv6_mask;
- pmr_param.val_sz = ODPH_IPV6ADDR_LEN;
-
- pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
- CU_ASSERT_FATAL(pmr != ODP_PMR_INVAL);
-
- /* packet with dst ip address matching PMR rule to be
- received in the CoS queue*/
- pkt_info = default_pkt_info;
- pkt_info.ipv6 = true;
-
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
- ip = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- memcpy(ip->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
-
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == queue);
- odp_packet_free(pkt);
-
- /* Other packets delivered to default queue */
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
- odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
-
- enqueue_pktio_interface(pkt, pktio);
-
- pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- CU_ASSERT(retqueue == default_queue);
-
- odp_cos_destroy(cos);
- odp_cos_destroy(default_cos);
- odp_cls_pmr_destroy(pmr);
- odp_packet_free(pkt);
- stop_pktio(pktio);
- odp_pool_destroy(default_pool);
- odp_pool_destroy(pool);
- odp_queue_destroy(queue);
- odp_queue_destroy(default_queue);
- odp_pktio_close(pktio);
-}
-
-odp_testinfo_t classification_suite_pmr[] = {
- ODP_TEST_INFO(classification_test_pmr_term_tcp_dport),
- ODP_TEST_INFO(classification_test_pmr_term_tcp_sport),
- ODP_TEST_INFO(classification_test_pmr_term_udp_dport),
- ODP_TEST_INFO(classification_test_pmr_term_udp_sport),
- ODP_TEST_INFO(classification_test_pmr_term_ipproto),
- ODP_TEST_INFO(classification_test_pmr_term_dmac),
- ODP_TEST_INFO(classification_test_pmr_pool_set),
- ODP_TEST_INFO(classification_test_pmr_queue_set),
- ODP_TEST_INFO(classification_test_pmr_term_daddr),
- ODP_TEST_INFO(classification_test_pmr_term_ipv6saddr),
- ODP_TEST_INFO(classification_test_pmr_term_ipv6daddr),
- ODP_TEST_INFO(classification_test_pmr_term_packet_len),
- ODP_TEST_INFO(classification_test_pmr_term_vlan_id_0),
- ODP_TEST_INFO(classification_test_pmr_term_vlan_id_x),
- ODP_TEST_INFO(classification_test_pmr_term_eth_type_0),
- ODP_TEST_INFO(classification_test_pmr_term_eth_type_x),
- ODP_TEST_INFO(classification_test_pktin_classifier_flag),
- ODP_TEST_INFO_NULL,
-};
diff --git a/test/common_plat/validation/api/classification/odp_classification_tests.c b/test/common_plat/validation/api/classification/odp_classification_tests.c
deleted file mode 100644
index a0f73e393..000000000
--- a/test/common_plat/validation/api/classification/odp_classification_tests.c
+++ /dev/null
@@ -1,725 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "odp_classification_testsuites.h"
-#include "classification.h"
-#include <odp_cunit_common.h>
-
-static odp_cos_t cos_list[CLS_ENTRIES];
-static odp_pmr_t pmr_list[CLS_ENTRIES];
-static odp_queue_t queue_list[CLS_ENTRIES];
-static odp_pool_t pool_list[CLS_ENTRIES];
-
-static odp_pool_t pool_default;
-static odp_pktio_t pktio_loop;
-
-/** sequence number of IP packets */
-odp_atomic_u32_t seq;
-
-/* default packet info */
-static cls_packet_info_t default_pkt_info;
-
-int classification_suite_init(void)
-{
- int i;
- int ret;
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t pktin_param;
-
- pool_default = pool_create("classification_pool");
- if (ODP_POOL_INVALID == pool_default) {
- fprintf(stderr, "Packet pool creation failed.\n");
- return -1;
- }
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio_loop = odp_pktio_open("loop", pool_default, &pktio_param);
- if (pktio_loop == ODP_PKTIO_INVALID) {
- ret = odp_pool_destroy(pool_default);
- if (ret)
- fprintf(stderr, "unable to destroy pool.\n");
- return -1;
- }
-
- memset(&default_pkt_info, 0, sizeof(cls_packet_info_t));
- default_pkt_info.pool = pool_default;
- default_pkt_info.seq = &seq;
-
- odp_pktin_queue_param_init(&pktin_param);
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- pktin_param.classifier_enable = true;
- pktin_param.hash_enable = false;
-
- if (odp_pktin_queue_config(pktio_loop, &pktin_param)) {
- fprintf(stderr, "pktin queue config failed.\n");
- return -1;
- }
-
- if (odp_pktout_queue_config(pktio_loop, NULL)) {
- fprintf(stderr, "pktout queue config failed.\n");
- return -1;
- }
-
- for (i = 0; i < CLS_ENTRIES; i++)
- cos_list[i] = ODP_COS_INVALID;
-
- for (i = 0; i < CLS_ENTRIES; i++)
- pmr_list[i] = ODP_PMR_INVAL;
-
- for (i = 0; i < CLS_ENTRIES; i++)
- queue_list[i] = ODP_QUEUE_INVALID;
-
- for (i = 0; i < CLS_ENTRIES; i++)
- pool_list[i] = ODP_POOL_INVALID;
-
- odp_atomic_init_u32(&seq, 0);
-
- ret = odp_pktio_start(pktio_loop);
- if (ret) {
- fprintf(stderr, "unable to start loop\n");
- return -1;
- }
-
- return 0;
-}
-
-int classification_suite_term(void)
-{
- int i;
- int retcode = 0;
-
- if (0 > stop_pktio(pktio_loop)) {
- fprintf(stderr, "stop pktio failed.\n");
- retcode = -1;
- }
-
- if (0 > odp_pktio_close(pktio_loop)) {
- fprintf(stderr, "pktio close failed.\n");
- retcode = -1;
- }
-
- if (0 != odp_pool_destroy(pool_default)) {
- fprintf(stderr, "pool_default destroy failed.\n");
- retcode = -1;
- }
-
- for (i = 0; i < CLS_ENTRIES; i++)
- odp_cos_destroy(cos_list[i]);
-
- for (i = 0; i < CLS_ENTRIES; i++)
- odp_cls_pmr_destroy(pmr_list[i]);
-
- for (i = 0; i < CLS_ENTRIES; i++)
- odp_queue_destroy(queue_list[i]);
-
- for (i = 0; i < CLS_ENTRIES; i++)
- odp_pool_destroy(pool_list[i]);
-
- return retcode;
-}
-
-void configure_cls_pmr_chain(void)
-{
- /* PKTIO --> PMR_SRC(SRC IP ADDR) --> PMR_DST (TCP SPORT) */
-
- /* Packet matching only the SRC IP ADDR should be delivered
- in queue[CLS_PMR_CHAIN_SRC] and a packet matching both SRC IP ADDR and
- TCP SPORT should be delivered to queue[CLS_PMR_CHAIN_DST] */
-
- uint16_t val;
- uint16_t maskport;
- char cosname[ODP_QUEUE_NAME_LEN];
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
- char queuename[ODP_QUEUE_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
- uint32_t addr;
- uint32_t mask;
- odp_pmr_param_t pmr_param;
- odp_queue_capability_t queue_capa;
-
- CU_ASSERT_FATAL(odp_queue_capability(&queue_capa) == 0);
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_NORMAL;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- qparam.sched.lock_count = queue_capa.max_ordered_locks;
- sprintf(queuename, "%s", "SrcQueue");
-
- queue_list[CLS_PMR_CHAIN_SRC] = odp_queue_create(queuename, &qparam);
-
- CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_SRC] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "%s", "SrcPool");
- pool_list[CLS_PMR_CHAIN_SRC] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_SRC] != ODP_POOL_INVALID);
-
- sprintf(cosname, "SrcCos");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_PMR_CHAIN_SRC];
- cls_param.queue = queue_list[CLS_PMR_CHAIN_SRC];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
-
- cos_list[CLS_PMR_CHAIN_SRC] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_SRC] != ODP_COS_INVALID);
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_NORMAL;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- sprintf(queuename, "%s", "DstQueue");
-
- queue_list[CLS_PMR_CHAIN_DST] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_DST] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "%s", "DstPool");
- pool_list[CLS_PMR_CHAIN_DST] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_DST] != ODP_POOL_INVALID);
-
- sprintf(cosname, "DstCos");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_PMR_CHAIN_DST];
- cls_param.queue = queue_list[CLS_PMR_CHAIN_DST];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_list[CLS_PMR_CHAIN_DST] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_DST] != ODP_COS_INVALID);
-
- parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = ODP_PMR_SIP_ADDR;
- pmr_param.match.value = &addr;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(addr);
- pmr_list[CLS_PMR_CHAIN_SRC] =
- odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_DEFAULT],
- cos_list[CLS_PMR_CHAIN_SRC]);
- CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_SRC] != ODP_PMR_INVAL);
-
- val = CLS_PMR_CHAIN_PORT;
- maskport = 0xffff;
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = find_first_supported_l3_pmr();
- pmr_param.match.value = &val;
- pmr_param.match.mask = &maskport;
- pmr_param.val_sz = sizeof(val);
- pmr_list[CLS_PMR_CHAIN_DST] =
- odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_PMR_CHAIN_SRC],
- cos_list[CLS_PMR_CHAIN_DST]);
- CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_DST] != ODP_PMR_INVAL);
-}
-
-void test_cls_pmr_chain(void)
-{
- odp_packet_t pkt;
- odph_ipv4hdr_t *ip;
- odp_queue_t queue;
- odp_pool_t pool;
- uint32_t addr = 0;
- uint32_t mask;
- uint32_t seqno = 0;
- cls_packet_info_t pkt_info;
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
- ip->src_addr = odp_cpu_to_be_32(addr);
- ip->chksum = 0;
- ip->chksum = odph_ipv4_csum_update(pkt);
-
- set_first_supported_pmr_port(pkt, CLS_PMR_CHAIN_PORT);
-
- enqueue_pktio_interface(pkt, pktio_loop);
-
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_DST]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_DST]);
- odp_packet_free(pkt);
-
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
- ip->src_addr = odp_cpu_to_be_32(addr);
- ip->chksum = 0;
- ip->chksum = odph_ipv4_csum_update(pkt);
-
- enqueue_pktio_interface(pkt, pktio_loop);
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_SRC]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_SRC]);
- odp_packet_free(pkt);
-}
-
-void configure_pktio_default_cos(void)
-{
- int retval;
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
- char cosname[ODP_COS_NAME_LEN];
- char queuename[ODP_QUEUE_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- sprintf(queuename, "%s", "DefaultQueue");
- queue_list[CLS_DEFAULT] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_list[CLS_DEFAULT] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "DefaultPool");
- pool_list[CLS_DEFAULT] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_DEFAULT] != ODP_POOL_INVALID);
-
- sprintf(cosname, "DefaultCoS");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_DEFAULT];
- cls_param.queue = queue_list[CLS_DEFAULT];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_list[CLS_DEFAULT] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_DEFAULT] != ODP_COS_INVALID);
-
- retval = odp_pktio_default_cos_set(pktio_loop, cos_list[CLS_DEFAULT]);
- CU_ASSERT(retval == 0);
-}
-
-void test_pktio_default_cos(void)
-{
- odp_packet_t pkt;
- odp_queue_t queue;
- uint32_t seqno = 0;
- odp_pool_t pool;
- cls_packet_info_t pkt_info;
-
- /* create a default packet */
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- enqueue_pktio_interface(pkt, pktio_loop);
-
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- /* Default packet should be received in default queue */
- CU_ASSERT(queue == queue_list[CLS_DEFAULT]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_DEFAULT]);
-
- odp_packet_free(pkt);
-}
-
-void configure_pktio_error_cos(void)
-{
- int retval;
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
- char queuename[ODP_QUEUE_NAME_LEN];
- char cosname[ODP_COS_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_LOWEST;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- sprintf(queuename, "%s", "ErrorCos");
-
- queue_list[CLS_ERROR] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_list[CLS_ERROR] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "ErrorPool");
- pool_list[CLS_ERROR] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_ERROR] != ODP_POOL_INVALID);
-
- sprintf(cosname, "%s", "ErrorCos");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_ERROR];
- cls_param.queue = queue_list[CLS_ERROR];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_list[CLS_ERROR] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_ERROR] != ODP_COS_INVALID);
-
- retval = odp_pktio_error_cos_set(pktio_loop, cos_list[CLS_ERROR]);
- CU_ASSERT(retval == 0);
-}
-
-void test_pktio_error_cos(void)
-{
- odp_queue_t queue;
- odp_packet_t pkt;
- odp_pool_t pool;
- cls_packet_info_t pkt_info;
-
- /*Create an error packet */
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
-
- /* Incorrect IpV4 version */
- ip->ver_ihl = 8 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->chksum = 0;
- enqueue_pktio_interface(pkt, pktio_loop);
-
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- /* Error packet should be received in error queue */
- CU_ASSERT(queue == queue_list[CLS_ERROR]);
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_ERROR]);
- odp_packet_free(pkt);
-}
-
-void classification_test_pktio_set_skip(void)
-{
- int retval;
- size_t offset = 5;
-
- retval = odp_pktio_skip_set(pktio_loop, offset);
- CU_ASSERT(retval == 0);
-
- retval = odp_pktio_skip_set(ODP_PKTIO_INVALID, offset);
- CU_ASSERT(retval < 0);
-
- /* reset skip value to zero as validation suite expects
- offset to be zero*/
-
- retval = odp_pktio_skip_set(pktio_loop, 0);
- CU_ASSERT(retval == 0);
-}
-
-void classification_test_pktio_set_headroom(void)
-{
- size_t headroom;
- int retval;
-
- headroom = 5;
- retval = odp_pktio_headroom_set(pktio_loop, headroom);
- CU_ASSERT(retval == 0);
-
- retval = odp_pktio_headroom_set(ODP_PKTIO_INVALID, headroom);
- CU_ASSERT(retval < 0);
-}
-
-void configure_cos_with_l2_priority(void)
-{
- uint8_t num_qos = CLS_L2_QOS_MAX;
- odp_cos_t cos_tbl[CLS_L2_QOS_MAX];
- odp_queue_t queue_tbl[CLS_L2_QOS_MAX];
- odp_pool_t pool;
- uint8_t qos_tbl[CLS_L2_QOS_MAX];
- char cosname[ODP_COS_NAME_LEN];
- char queuename[ODP_QUEUE_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
- int retval;
- int i;
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
-
- /** Initialize scalar variable qos_tbl **/
- for (i = 0; i < CLS_L2_QOS_MAX; i++)
- qos_tbl[i] = 0;
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- for (i = 0; i < num_qos; i++) {
- qparam.sched.prio = ODP_SCHED_PRIO_LOWEST - i;
- sprintf(queuename, "%s_%d", "L2_Queue", i);
- queue_tbl[i] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_tbl[i] != ODP_QUEUE_INVALID);
- queue_list[CLS_L2_QOS_0 + i] = queue_tbl[i];
-
- sprintf(poolname, "%s_%d", "L2_Pool", i);
- pool = pool_create(poolname);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- pool_list[CLS_L2_QOS_0 + i] = pool;
-
- sprintf(cosname, "%s_%d", "L2_Cos", i);
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool;
- cls_param.queue = queue_tbl[i];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_tbl[i] = odp_cls_cos_create(cosname, &cls_param);
- if (cos_tbl[i] == ODP_COS_INVALID)
- break;
-
- cos_list[CLS_L2_QOS_0 + i] = cos_tbl[i];
- qos_tbl[i] = i;
- }
- /* count 'i' is passed instead of num_qos to handle the rare scenario
- if the odp_cls_cos_create() failed in the middle*/
- retval = odp_cos_with_l2_priority(pktio_loop, i, qos_tbl, cos_tbl);
- CU_ASSERT(retval == 0);
-}
-
-void test_cos_with_l2_priority(void)
-{
- odp_packet_t pkt;
- odph_ethhdr_t *ethhdr;
- odph_vlanhdr_t *vlan;
- odp_queue_t queue;
- odp_pool_t pool;
- uint32_t seqno = 0;
- cls_packet_info_t pkt_info;
- uint8_t i;
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt_info.vlan = true;
-
- for (i = 0; i < CLS_L2_QOS_MAX; i++) {
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- ethhdr = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- vlan = (odph_vlanhdr_t *)(ethhdr + 1);
- vlan->tci = odp_cpu_to_be_16(i << 13);
- enqueue_pktio_interface(pkt, pktio_loop);
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(queue == queue_list[CLS_L2_QOS_0 + i]);
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_L2_QOS_0 + i]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- odp_packet_free(pkt);
- }
-}
-
-void configure_pmr_cos(void)
-{
- uint16_t val;
- uint16_t mask;
- odp_pmr_param_t pmr_param;
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
- char cosname[ODP_COS_NAME_LEN];
- char queuename[ODP_QUEUE_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- sprintf(queuename, "%s", "PMR_CoS");
-
- queue_list[CLS_PMR] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_list[CLS_PMR] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "PMR_Pool");
- pool_list[CLS_PMR] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_PMR] != ODP_POOL_INVALID);
-
- sprintf(cosname, "PMR_CoS");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_PMR];
- cls_param.queue = queue_list[CLS_PMR];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_list[CLS_PMR] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_PMR] != ODP_COS_INVALID);
-
- val = CLS_PMR_PORT;
- mask = 0xffff;
- odp_cls_pmr_param_init(&pmr_param);
- pmr_param.term = find_first_supported_l3_pmr();
- pmr_param.match.value = &val;
- pmr_param.match.mask = &mask;
- pmr_param.val_sz = sizeof(val);
-
- pmr_list[CLS_PMR] = odp_cls_pmr_create(&pmr_param, 1,
- cos_list[CLS_DEFAULT],
- cos_list[CLS_PMR]);
- CU_ASSERT_FATAL(pmr_list[CLS_PMR] != ODP_PMR_INVAL);
-}
-
-void test_pmr_cos(void)
-{
- odp_packet_t pkt;
- odp_queue_t queue;
- odp_pool_t pool;
- uint32_t seqno = 0;
- cls_packet_info_t pkt_info;
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
- set_first_supported_pmr_port(pkt, CLS_PMR_PORT);
- enqueue_pktio_interface(pkt, pktio_loop);
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(queue == queue_list[CLS_PMR]);
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_PMR]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- odp_packet_free(pkt);
-}
-
-void configure_pktio_pmr_composite(void)
-{
- odp_pmr_param_t pmr_params[2];
- uint16_t val;
- uint16_t maskport;
- int num_terms = 2; /* one pmr for each L3 and L4 */
- odp_queue_param_t qparam;
- odp_cls_cos_param_t cls_param;
- char cosname[ODP_COS_NAME_LEN];
- char queuename[ODP_QUEUE_NAME_LEN];
- char poolname[ODP_POOL_NAME_LEN];
- uint32_t addr = 0;
- uint32_t mask;
-
- odp_queue_param_init(&qparam);
- qparam.type = ODP_QUEUE_TYPE_SCHED;
- qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST;
- qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparam.sched.group = ODP_SCHED_GROUP_ALL;
- sprintf(queuename, "%s", "cos_pmr_composite_queue");
-
- queue_list[CLS_PMR_SET] = odp_queue_create(queuename, &qparam);
- CU_ASSERT_FATAL(queue_list[CLS_PMR_SET] != ODP_QUEUE_INVALID);
-
- sprintf(poolname, "cos_pmr_composite_pool");
- pool_list[CLS_PMR_SET] = pool_create(poolname);
- CU_ASSERT_FATAL(pool_list[CLS_PMR_SET] != ODP_POOL_INVALID);
-
- sprintf(cosname, "cos_pmr_composite");
- odp_cls_cos_param_init(&cls_param);
- cls_param.pool = pool_list[CLS_PMR_SET];
- cls_param.queue = queue_list[CLS_PMR_SET];
- cls_param.drop_policy = ODP_COS_DROP_POOL;
- cos_list[CLS_PMR_SET] = odp_cls_cos_create(cosname, &cls_param);
- CU_ASSERT_FATAL(cos_list[CLS_PMR_SET] != ODP_COS_INVALID);
-
- parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask);
- odp_cls_pmr_param_init(&pmr_params[0]);
- pmr_params[0].term = ODP_PMR_SIP_ADDR;
- pmr_params[0].match.value = &addr;
- pmr_params[0].match.mask = &mask;
- pmr_params[0].val_sz = sizeof(addr);
-
- val = CLS_PMR_SET_PORT;
- maskport = 0xffff;
- odp_cls_pmr_param_init(&pmr_params[1]);
- pmr_params[1].term = find_first_supported_l3_pmr();
- pmr_params[1].match.value = &val;
- pmr_params[1].match.mask = &maskport;
- pmr_params[1].range_term = false;
- pmr_params[1].val_sz = sizeof(val);
-
- pmr_list[CLS_PMR_SET] = odp_cls_pmr_create(pmr_params, num_terms,
- cos_list[CLS_DEFAULT],
- cos_list[CLS_PMR_SET]);
- CU_ASSERT_FATAL(pmr_list[CLS_PMR_SET] != ODP_PMR_INVAL);
-}
-
-void test_pktio_pmr_composite_cos(void)
-{
- uint32_t addr = 0;
- uint32_t mask;
- odph_ipv4hdr_t *ip;
- odp_packet_t pkt;
- odp_pool_t pool;
- odp_queue_t queue;
- uint32_t seqno = 0;
- cls_packet_info_t pkt_info;
-
- pkt_info = default_pkt_info;
- pkt_info.udp = true;
- pkt = create_packet(pkt_info);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- seqno = cls_pkt_get_seq(pkt);
- CU_ASSERT(seqno != TEST_SEQ_INVALID);
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
- parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask);
- ip->src_addr = odp_cpu_to_be_32(addr);
- ip->chksum = 0;
- ip->chksum = odph_ipv4_csum_update(pkt);
-
- set_first_supported_pmr_port(pkt, CLS_PMR_SET_PORT);
- enqueue_pktio_interface(pkt, pktio_loop);
- pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(queue == queue_list[CLS_PMR_SET]);
- pool = odp_packet_pool(pkt);
- CU_ASSERT(pool == pool_list[CLS_PMR_SET]);
- CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
- odp_packet_free(pkt);
-}
-
-void classification_test_pktio_configure(void)
-{
- /* Configure the Different CoS for the pktio interface */
- if (TEST_DEFAULT)
- configure_pktio_default_cos();
- if (TEST_ERROR)
- configure_pktio_error_cos();
- if (TEST_PMR_CHAIN)
- configure_cls_pmr_chain();
- if (TEST_L2_QOS)
- configure_cos_with_l2_priority();
- if (TEST_PMR)
- configure_pmr_cos();
- if (TEST_PMR_SET)
- configure_pktio_pmr_composite();
-}
-
-void classification_test_pktio_test(void)
-{
- /* Test Different CoS on the pktio interface */
- if (TEST_DEFAULT)
- test_pktio_default_cos();
- if (TEST_ERROR)
- test_pktio_error_cos();
- if (TEST_PMR_CHAIN)
- test_cls_pmr_chain();
- if (TEST_L2_QOS)
- test_cos_with_l2_priority();
- if (TEST_PMR)
- test_pmr_cos();
- if (TEST_PMR_SET)
- test_pktio_pmr_composite_cos();
-}
-
-odp_testinfo_t classification_suite[] = {
- ODP_TEST_INFO(classification_test_pktio_set_skip),
- ODP_TEST_INFO(classification_test_pktio_set_headroom),
- ODP_TEST_INFO(classification_test_pktio_configure),
- ODP_TEST_INFO(classification_test_pktio_test),
- ODP_TEST_INFO_NULL,
-};
diff --git a/test/common_plat/validation/api/classification/odp_classification_testsuites.h b/test/common_plat/validation/api/classification/odp_classification_testsuites.h
deleted file mode 100644
index e1624162f..000000000
--- a/test/common_plat/validation/api/classification/odp_classification_testsuites.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ODP_CLASSIFICATION_TESTSUITES_H_
-#define ODP_CLASSIFICATION_TESTSUITES_H_
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-#include <odp_cunit_common.h>
-#include <stdbool.h>
-
-typedef struct cls_packet_info {
- odp_pool_t pool;
- bool vlan;
- bool vlan_qinq;
- odp_atomic_u32_t *seq;
- bool udp;
- bool ipv6;
- uint32_t len;
-} cls_packet_info_t;
-
-extern odp_testinfo_t classification_suite[];
-extern odp_testinfo_t classification_suite_basic[];
-extern odp_testinfo_t classification_suite_pmr[];
-
-int classification_suite_init(void);
-int classification_suite_term(void);
-
-int classification_suite_pmr_term(void);
-int classification_suite_pmr_init(void);
-
-odp_packet_t create_packet(cls_packet_info_t pkt_info);
-int cls_pkt_set_seq(odp_packet_t pkt);
-uint32_t cls_pkt_get_seq(odp_packet_t pkt);
-odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool,
- odp_bool_t cls_enable);
-void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos,
- odp_queue_t *queue, odp_pool_t *pool);
-int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask);
-void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio);
-odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns);
-odp_pool_t pool_create(const char *poolname);
-odp_queue_t queue_create(const char *queuename, bool sched);
-void configure_pktio_default_cos(void);
-void test_pktio_default_cos(void);
-void configure_pktio_error_cos(void);
-void test_pktio_error_cos(void);
-void configure_cls_pmr_chain(void);
-void test_cls_pmr_chain(void);
-void configure_cos_with_l2_priority(void);
-void test_cos_with_l2_priority(void);
-void configure_pmr_cos(void);
-void test_pmr_cos(void);
-void configure_pktio_pmr_composite(void);
-void test_pktio_pmr_composite_cos(void);
-int stop_pktio(odp_pktio_t pktio);
-odp_cls_pmr_term_t find_first_supported_l3_pmr(void);
-int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port);
-
-#endif /* ODP_BUFFER_TESTSUITES_H_ */
diff --git a/test/common_plat/validation/api/cpumask/Makefile.am b/test/common_plat/validation/api/cpumask/Makefile.am
deleted file mode 100644
index ec5fce338..000000000
--- a/test/common_plat/validation/api/cpumask/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestcpumask.la
-libtestcpumask_la_SOURCES = cpumask.c
-libtestcpumask_la_LIBADD = $(LIBCPUMASK_COMMON)
-
-test_PROGRAMS = cpumask_main$(EXEEXT)
-dist_cpumask_main_SOURCES = cpumask_main.c
-cpumask_main_LDADD = libtestcpumask.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = cpumask.h
diff --git a/test/common_plat/validation/api/cpumask/cpumask.c b/test/common_plat/validation/api/cpumask/cpumask.c
deleted file mode 100644
index a0cb559fb..000000000
--- a/test/common_plat/validation/api/cpumask/cpumask.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-
-#include "odp_cunit_common.h"
-#include "cpumask.h"
-#include "mask_common.h"
-
-/* default worker parameter to get all that may be available */
-#define ALL_AVAILABLE 0
-
-void cpumask_test_odp_cpumask_def_control(void)
-{
- unsigned num;
- unsigned mask_count;
- unsigned max_cpus = mask_capacity();
- odp_cpumask_t mask;
-
- num = odp_cpumask_default_control(&mask, ALL_AVAILABLE);
- mask_count = odp_cpumask_count(&mask);
-
- CU_ASSERT(mask_count == num);
- CU_ASSERT(num > 0);
- CU_ASSERT(num <= max_cpus);
-}
-
-void cpumask_test_odp_cpumask_def_worker(void)
-{
- unsigned num;
- unsigned mask_count;
- unsigned max_cpus = mask_capacity();
- odp_cpumask_t mask;
-
- num = odp_cpumask_default_worker(&mask, ALL_AVAILABLE);
- mask_count = odp_cpumask_count(&mask);
-
- CU_ASSERT(mask_count == num);
- CU_ASSERT(num > 0);
- CU_ASSERT(num <= max_cpus);
-}
-
-void cpumask_test_odp_cpumask_def(void)
-{
- unsigned mask_count;
- unsigned num_worker;
- unsigned num_control;
- unsigned max_cpus = mask_capacity();
- unsigned available_cpus = odp_cpu_count();
- unsigned requested_cpus;
- odp_cpumask_t mask;
-
- CU_ASSERT(available_cpus <= max_cpus);
-
- if (available_cpus > 1)
- requested_cpus = available_cpus - 1;
- else
- requested_cpus = available_cpus;
- num_worker = odp_cpumask_default_worker(&mask, requested_cpus);
- mask_count = odp_cpumask_count(&mask);
- CU_ASSERT(mask_count == num_worker);
-
- num_control = odp_cpumask_default_control(&mask, 1);
- mask_count = odp_cpumask_count(&mask);
- CU_ASSERT(mask_count == num_control);
-
- CU_ASSERT(num_control >= 1);
- CU_ASSERT(num_worker <= available_cpus);
- CU_ASSERT(num_worker > 0);
-}
-
-odp_testinfo_t cpumask_suite[] = {
- ODP_TEST_INFO(cpumask_test_odp_cpumask_to_from_str),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_equal),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_zero),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_set),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_clr),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_isset),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_count),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_and),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_or),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_xor),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_copy),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_first),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_last),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_next),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_setall),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_def_control),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_def_worker),
- ODP_TEST_INFO(cpumask_test_odp_cpumask_def),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t cpumask_suites[] = {
- {"Cpumask", NULL, NULL, cpumask_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int cpumask_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(cpumask_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/cpumask/cpumask.h b/test/common_plat/validation/api/cpumask/cpumask.h
deleted file mode 100644
index 87a4512bf..000000000
--- a/test/common_plat/validation/api/cpumask/cpumask.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_CPUMASK_H_
-#define _ODP_TEST_CPUMASK_H_
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-
-/* test functions: */
-#include "mask_common.h"
-void cpumask_test_odp_cpumask_def_control(void);
-void cpumask_test_odp_cpumask_def_worker(void);
-void cpumask_test_odp_cpumask_def(void);
-
-/* test arrays: */
-extern odp_testinfo_t cpumask_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t cpumask_suites[];
-
-/* main test program: */
-int cpumask_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/cpumask/cpumask_main.c b/test/common_plat/validation/api/cpumask/cpumask_main.c
deleted file mode 100644
index 39e3171ca..000000000
--- a/test/common_plat/validation/api/cpumask/cpumask_main.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include "cpumask.h"
-
-int main(int argc, char *argv[])
-{
- return cpumask_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/crypto/Makefile.am b/test/common_plat/validation/api/crypto/Makefile.am
deleted file mode 100644
index 3ea41b41f..000000000
--- a/test/common_plat/validation/api/crypto/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestcrypto.la
-libtestcrypto_la_SOURCES = crypto.c \
- odp_crypto_test_inp.c
-
-test_PROGRAMS = crypto_main$(EXEEXT)
-dist_crypto_main_SOURCES = crypto_main.c
-crypto_main_LDADD = libtestcrypto.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = crypto.h odp_crypto_test_inp.h test_vectors.h test_vectors_len.h
diff --git a/test/common_plat/validation/api/crypto/crypto.c b/test/common_plat/validation/api/crypto/crypto.c
deleted file mode 100644
index 94beb2f12..000000000
--- a/test/common_plat/validation/api/crypto/crypto.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "odp_crypto_test_inp.h"
-#include "crypto.h"
-
-#define PKT_POOL_NUM 64
-#define PKT_POOL_LEN (1 * 1024)
-
-odp_suiteinfo_t crypto_suites[] = {
- {ODP_CRYPTO_SYNC_INP, crypto_suite_sync_init, crypto_suite_term,
- crypto_suite},
- {ODP_CRYPTO_ASYNC_INP, crypto_suite_async_init, crypto_suite_term,
- crypto_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int crypto_init(odp_instance_t *inst)
-{
- odp_pool_param_t params;
- odp_pool_t pool;
- odp_queue_t out_queue;
- odp_pool_capability_t pool_capa;
-
- if (0 != odp_init_global(inst, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return -1;
- }
-
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return -1;
- }
-
- if (odp_pool_capability(&pool_capa) < 0) {
- fprintf(stderr, "error: odp_pool_capability() failed.\n");
- return -1;
- }
-
- odp_pool_param_init(&params);
- params.pkt.seg_len = PKT_POOL_LEN;
- params.pkt.len = PKT_POOL_LEN;
- params.pkt.num = PKT_POOL_NUM;
- params.type = ODP_POOL_PACKET;
-
- if (pool_capa.pkt.max_seg_len &&
- PKT_POOL_LEN > pool_capa.pkt.max_seg_len) {
- fprintf(stderr, "Warning: small packet segment length\n");
- params.pkt.seg_len = pool_capa.pkt.max_seg_len;
- }
-
- if (pool_capa.pkt.max_len &&
- PKT_POOL_LEN > pool_capa.pkt.max_len) {
- fprintf(stderr, "Pool max packet length too small\n");
- return -1;
- }
-
- pool = odp_pool_create("packet_pool", &params);
-
- if (ODP_POOL_INVALID == pool) {
- fprintf(stderr, "Packet pool creation failed.\n");
- return -1;
- }
- out_queue = odp_queue_create("crypto-out", NULL);
- if (ODP_QUEUE_INVALID == out_queue) {
- fprintf(stderr, "Crypto outq creation failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-int crypto_term(odp_instance_t inst)
-{
- odp_pool_t pool;
- odp_queue_t out_queue;
-
- out_queue = odp_queue_lookup("crypto-out");
- if (ODP_QUEUE_INVALID != out_queue) {
- if (odp_queue_destroy(out_queue))
- fprintf(stderr, "Crypto outq destroy failed.\n");
- } else {
- fprintf(stderr, "Crypto outq not found.\n");
- }
-
- pool = odp_pool_lookup("packet_pool");
- if (ODP_POOL_INVALID != pool) {
- if (odp_pool_destroy(pool))
- fprintf(stderr, "Packet pool destroy failed.\n");
- } else {
- fprintf(stderr, "Packet pool not found.\n");
- }
-
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_global(inst)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-int crypto_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- odp_cunit_register_global_init(crypto_init);
- odp_cunit_register_global_term(crypto_term);
-
- ret = odp_cunit_register(crypto_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/crypto/crypto.h b/test/common_plat/validation/api/crypto/crypto.h
deleted file mode 100644
index dd15b448f..000000000
--- a/test/common_plat/validation/api/crypto/crypto.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_CRYPTO_H_
-#define _ODP_TEST_CRYPTO_H_
-
-#include "odp_cunit_common.h"
-
-/* test functions: */
-void crypto_test_enc_alg_null(void);
-void crypto_test_dec_alg_null(void);
-void crypto_test_enc_alg_3des_cbc(void);
-void crypto_test_enc_alg_3des_cbc_ovr_iv(void);
-void crypto_test_dec_alg_3des_cbc(void);
-void crypto_test_dec_alg_3des_cbc_ovr_iv(void);
-void crypto_test_enc_alg_aes128_cbc(void);
-void crypto_test_enc_alg_aes128_cbc_ovr_iv(void);
-void crypto_test_dec_alg_aes128_cbc(void);
-void crypto_test_dec_alg_aes128_cbc_ovr_iv(void);
-void crypto_test_enc_alg_aes128_gcm(void);
-void crypto_test_enc_alg_aes128_gcm_ovr_iv(void);
-void crypto_test_dec_alg_aes128_gcm(void);
-void crypto_test_dec_alg_aes128_gcm_ovr_iv(void);
-void crypto_test_gen_alg_hmac_md5(void);
-void crypto_test_check_alg_hmac_md5(void);
-void crypto_test_gen_alg_hmac_sha1(void);
-void crypto_test_check_alg_hmac_sha1(void);
-void crypto_test_gen_alg_hmac_sha256(void);
-void crypto_test_check_alg_hmac_sha256(void);
-void crypto_test_gen_alg_hmac_sha512(void);
-void crypto_test_check_alg_hmac_sha512(void);
-
-/* test arrays: */
-extern odp_testinfo_t crypto_suite[];
-
-/* test array init/term functions: */
-int crypto_suite_sync_init(void);
-int crypto_suite_async_init(void);
-
-/* test registry: */
-extern odp_suiteinfo_t crypto_suites[];
-
-/* executable init/term functions: */
-int crypto_init(odp_instance_t *inst);
-int crypto_term(odp_instance_t inst);
-
-/* main test program: */
-int crypto_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/crypto/crypto_main.c b/test/common_plat/validation/api/crypto/crypto_main.c
deleted file mode 100644
index d8c26fa25..000000000
--- a/test/common_plat/validation/api/crypto/crypto_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "crypto.h"
-
-int main(int argc, char *argv[])
-{
- return crypto_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c
deleted file mode 100644
index ae600e230..000000000
--- a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c
+++ /dev/null
@@ -1,1589 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <CUnit/Basic.h>
-#include <odp_cunit_common.h>
-#include "test_vectors.h"
-#include "odp_crypto_test_inp.h"
-#include "crypto.h"
-
-#define MAX_ALG_CAPA 32
-
-struct suite_context_s {
- odp_crypto_op_mode_t pref_mode;
- odp_pool_t pool;
- odp_queue_t queue;
-};
-
-static struct suite_context_s suite_context;
-
-static const char *auth_alg_name(odp_auth_alg_t auth)
-{
- switch (auth) {
- case ODP_AUTH_ALG_NULL:
- return "ODP_AUTH_ALG_NULL";
- case ODP_AUTH_ALG_MD5_HMAC:
- return "ODP_AUTH_ALG_MD5_HMAC";
- case ODP_AUTH_ALG_SHA1_HMAC:
- return "ODP_AUTH_ALG_SHA1_HMAC";
- case ODP_AUTH_ALG_SHA256_HMAC:
- return "ODP_AUTH_ALG_SHA256_HMAC";
- case ODP_AUTH_ALG_SHA512_HMAC:
- return "ODP_AUTH_ALG_SHA512_HMAC";
- case ODP_AUTH_ALG_AES_GCM:
- return "ODP_AUTH_ALG_AES_GCM";
- default:
- return "Unknown";
- }
-}
-
-static const char *cipher_alg_name(odp_cipher_alg_t cipher)
-{
- switch (cipher) {
- case ODP_CIPHER_ALG_NULL:
- return "ODP_CIPHER_ALG_NULL";
- case ODP_CIPHER_ALG_DES:
- return "ODP_CIPHER_ALG_DES";
- case ODP_CIPHER_ALG_3DES_CBC:
- return "ODP_CIPHER_ALG_3DES_CBC";
- case ODP_CIPHER_ALG_AES_CBC:
- return "ODP_CIPHER_ALG_AES_CBC";
- case ODP_CIPHER_ALG_AES_GCM:
- return "ODP_CIPHER_ALG_AES_GCM";
- default:
- return "Unknown";
- }
-}
-
-/* Basic algorithm run function for async inplace mode.
- * Creates a session from input parameters and runs one operation
- * on input_vec. Checks the output of the crypto operation against
- * output_vec. Operation completion event is dequeued polling the
- * session output queue. Completion context pointer is retrieved
- * and checked against the one set before the operation.
- * Completion event can be a separate buffer or the input packet
- * buffer can be used.
- * */
-static void alg_test(odp_crypto_op_t op,
- odp_bool_t should_fail,
- odp_cipher_alg_t cipher_alg,
- odp_crypto_iv_t ses_iv,
- uint8_t *op_iv_ptr,
- odp_crypto_key_t cipher_key,
- odp_auth_alg_t auth_alg,
- odp_crypto_key_t auth_key,
- odp_packet_data_range_t *cipher_range,
- odp_packet_data_range_t *auth_range,
- uint8_t *aad,
- uint32_t aad_len,
- const uint8_t *plaintext,
- unsigned int plaintext_len,
- const uint8_t *ciphertext,
- unsigned int ciphertext_len,
- const uint8_t *digest,
- uint32_t digest_len)
-{
- odp_crypto_session_t session;
- odp_crypto_capability_t capa;
- int rc;
- odp_crypto_ses_create_err_t status;
- odp_bool_t posted;
- odp_event_t event;
- odp_crypto_compl_t compl_event;
- odp_crypto_op_result_t result;
- odp_crypto_session_param_t ses_params;
- odp_crypto_op_param_t op_params;
- uint8_t *data_addr;
- int data_off;
- odp_crypto_cipher_capability_t cipher_capa[MAX_ALG_CAPA];
- odp_crypto_auth_capability_t auth_capa[MAX_ALG_CAPA];
- int num, i;
- int found;
-
- rc = odp_crypto_capability(&capa);
- CU_ASSERT(!rc);
-
- if (cipher_alg == ODP_CIPHER_ALG_3DES_CBC &&
- !(capa.ciphers.bit.trides_cbc))
- rc = -1;
- if (cipher_alg == ODP_CIPHER_ALG_AES_CBC &&
- !(capa.ciphers.bit.aes_cbc))
- rc = -1;
- if (cipher_alg == ODP_CIPHER_ALG_AES_GCM &&
- !(capa.ciphers.bit.aes_gcm))
- rc = -1;
- if (cipher_alg == ODP_CIPHER_ALG_DES &&
- !(capa.ciphers.bit.des))
- rc = -1;
- if (cipher_alg == ODP_CIPHER_ALG_NULL &&
- !(capa.ciphers.bit.null))
- rc = -1;
-
- CU_ASSERT(!rc);
- CU_ASSERT((~capa.ciphers.all_bits & capa.hw_ciphers.all_bits) == 0);
-
- if (auth_alg == ODP_AUTH_ALG_AES_GCM &&
- !(capa.auths.bit.aes_gcm))
- rc = -1;
- if (auth_alg == ODP_AUTH_ALG_MD5_HMAC &&
- !(capa.auths.bit.md5_hmac))
- rc = -1;
- if (auth_alg == ODP_AUTH_ALG_NULL &&
- !(capa.auths.bit.null))
- rc = -1;
- if (auth_alg == ODP_AUTH_ALG_SHA1_HMAC &&
- !(capa.auths.bit.sha1_hmac))
- rc = -1;
- if (auth_alg == ODP_AUTH_ALG_SHA256_HMAC &&
- !(capa.auths.bit.sha256_hmac))
- rc = -1;
- if (auth_alg == ODP_AUTH_ALG_SHA512_HMAC &&
- !(capa.auths.bit.sha512_hmac))
- rc = -1;
-
- CU_ASSERT(!rc);
- CU_ASSERT((~capa.auths.all_bits & capa.hw_auths.all_bits) == 0);
-
- num = odp_crypto_cipher_capability(cipher_alg, cipher_capa,
- MAX_ALG_CAPA);
-
- CU_ASSERT(num > 0);
- found = 0;
-
- CU_ASSERT(num <= MAX_ALG_CAPA);
-
- if (num > MAX_ALG_CAPA)
- num = MAX_ALG_CAPA;
-
- /* Search for the test case */
- for (i = 0; i < num; i++) {
- if (cipher_capa[i].key_len == cipher_key.length &&
- cipher_capa[i].iv_len == ses_iv.length) {
- found = 1;
- break;
- }
- }
-
- CU_ASSERT(found);
-
- num = odp_crypto_auth_capability(auth_alg, auth_capa, MAX_ALG_CAPA);
-
- CU_ASSERT(num > 0);
- found = 0;
-
- CU_ASSERT(num <= MAX_ALG_CAPA);
-
- if (num > MAX_ALG_CAPA)
- num = MAX_ALG_CAPA;
-
- /* Search for the test case */
- for (i = 0; i < num; i++) {
- if (auth_capa[i].digest_len == digest_len &&
- auth_capa[i].key_len == auth_key.length) {
- found = 1;
- break;
- }
- }
-
- CU_ASSERT(found);
-
- /* Create a crypto session */
- odp_crypto_session_param_init(&ses_params);
- ses_params.op = op;
- ses_params.auth_cipher_text = false;
- ses_params.pref_mode = suite_context.pref_mode;
- ses_params.cipher_alg = cipher_alg;
- ses_params.auth_alg = auth_alg;
- ses_params.compl_queue = suite_context.queue;
- ses_params.output_pool = suite_context.pool;
- ses_params.cipher_key = cipher_key;
- ses_params.iv = ses_iv;
- ses_params.auth_key = auth_key;
- ses_params.auth_digest_len = digest_len;
-
- rc = odp_crypto_session_create(&ses_params, &session, &status);
- CU_ASSERT_FATAL(!rc);
- CU_ASSERT(status == ODP_CRYPTO_SES_CREATE_ERR_NONE);
- CU_ASSERT(odp_crypto_session_to_u64(session) !=
- odp_crypto_session_to_u64(ODP_CRYPTO_SESSION_INVALID));
-
- /* Prepare input data */
- odp_packet_t pkt = odp_packet_alloc(suite_context.pool,
- plaintext_len + digest_len);
- CU_ASSERT(pkt != ODP_PACKET_INVALID);
- data_addr = odp_packet_data(pkt);
- memcpy(data_addr, plaintext, plaintext_len);
- data_off = 0;
-
- /* Prepare input/output params */
- memset(&op_params, 0, sizeof(op_params));
- op_params.session = session;
- op_params.pkt = pkt;
- op_params.out_pkt = pkt;
- op_params.ctx = (void *)0xdeadbeef;
-
- if (cipher_range) {
- op_params.cipher_range = *cipher_range;
- data_off = cipher_range->offset;
- } else {
- op_params.cipher_range.offset = data_off;
- op_params.cipher_range.length = plaintext_len;
- }
- if (auth_range) {
- op_params.auth_range = *auth_range;
- } else {
- op_params.auth_range.offset = data_off;
- op_params.auth_range.length = plaintext_len;
- }
- if (op_iv_ptr)
- op_params.override_iv_ptr = op_iv_ptr;
-
- op_params.aad.ptr = aad;
- op_params.aad.length = aad_len;
-
- op_params.hash_result_offset = plaintext_len;
- if (0 != digest_len) {
- memcpy(data_addr + op_params.hash_result_offset,
- digest, digest_len);
- }
-
- rc = odp_crypto_operation(&op_params, &posted, &result);
- if (rc < 0) {
- CU_FAIL("Failed odp_crypto_operation()");
- goto cleanup;
- }
-
- if (posted) {
- /* Poll completion queue for results */
- do {
- event = odp_queue_deq(suite_context.queue);
- } while (event == ODP_EVENT_INVALID);
-
- compl_event = odp_crypto_compl_from_event(event);
- CU_ASSERT(odp_crypto_compl_to_u64(compl_event) ==
- odp_crypto_compl_to_u64(odp_crypto_compl_from_event(event)));
- odp_crypto_compl_result(compl_event, &result);
- odp_crypto_compl_free(compl_event);
- }
-
- CU_ASSERT(result.pkt == pkt);
- CU_ASSERT(result.ctx == (void *)0xdeadbeef);
-
- if (should_fail) {
- CU_ASSERT(!result.ok);
- goto cleanup;
- }
-
- CU_ASSERT(result.ok);
-
- if (cipher_alg != ODP_CIPHER_ALG_NULL)
- CU_ASSERT(!memcmp(data_addr, ciphertext, ciphertext_len));
-
- if (op == ODP_CRYPTO_OP_ENCODE && auth_alg != ODP_AUTH_ALG_NULL)
- CU_ASSERT(!memcmp(data_addr + op_params.hash_result_offset,
- digest, digest_len));
-cleanup:
- rc = odp_crypto_session_destroy(session);
- CU_ASSERT(!rc);
-
- odp_packet_free(pkt);
-}
-
-/**
- * Check if given cipher and authentication algorithms are supported
- *
- * @param cipher Cipher algorithm
- * @param auth Authentication algorithm
- *
- * @retval ODP_TEST_ACTIVE when both algorithms are supported
- * @retval ODP_TEST_INACTIVE when either algorithm is not supported
- */
-static int check_alg_support(odp_cipher_alg_t cipher, odp_auth_alg_t auth)
-{
- odp_crypto_capability_t capability;
-
- if (odp_crypto_capability(&capability))
- return ODP_TEST_INACTIVE;
-
- /* Cipher algorithms */
- switch (cipher) {
- case ODP_CIPHER_ALG_NULL:
- if (!capability.ciphers.bit.null)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_CIPHER_ALG_DES:
- if (!capability.ciphers.bit.des)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_CIPHER_ALG_3DES_CBC:
- if (!capability.ciphers.bit.trides_cbc)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_CIPHER_ALG_AES_CBC:
- if (!capability.ciphers.bit.aes_cbc)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_CIPHER_ALG_AES_GCM:
- if (!capability.ciphers.bit.aes_gcm)
- return ODP_TEST_INACTIVE;
- break;
- default:
- fprintf(stderr, "Unsupported cipher algorithm\n");
- return ODP_TEST_INACTIVE;
- }
-
- /* Authentication algorithms */
- switch (auth) {
- case ODP_AUTH_ALG_NULL:
- if (!capability.auths.bit.null)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_AUTH_ALG_MD5_HMAC:
- if (!capability.auths.bit.md5_hmac)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_AUTH_ALG_SHA1_HMAC:
- if (!capability.auths.bit.sha1_hmac)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_AUTH_ALG_SHA256_HMAC:
- if (!capability.auths.bit.sha256_hmac)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_AUTH_ALG_SHA512_HMAC:
- if (!capability.auths.bit.sha512_hmac)
- return ODP_TEST_INACTIVE;
- break;
- case ODP_AUTH_ALG_AES_GCM:
- if (!capability.auths.bit.aes_gcm)
- return ODP_TEST_INACTIVE;
- break;
- default:
- fprintf(stderr, "Unsupported authentication algorithm\n");
- return ODP_TEST_INACTIVE;
- }
-
- return ODP_TEST_ACTIVE;
-}
-
-/**
- * Check if given cipher options are supported
- *
- * @param cipher Cipher algorithm
- * @param key_len Key length
- * @param iv_len IV length
- *
- * @retval non-zero if both cipher options are supported
- * @retval 0 if both options are not supported
- */
-static int check_cipher_options(odp_cipher_alg_t cipher, uint32_t key_len,
- uint32_t iv_len)
-{
- int i;
- int num;
- odp_crypto_cipher_capability_t cipher_capa[MAX_ALG_CAPA];
-
- num = odp_crypto_cipher_capability(cipher, cipher_capa, MAX_ALG_CAPA);
- CU_ASSERT_FATAL(num >= 1);
-
- for (i = 0; i < num; i++) {
- if (key_len == cipher_capa[i].key_len &&
- iv_len == cipher_capa[i].iv_len)
- break;
- }
-
- if (i == num) {
- printf("\n Unsupported: alg=%s, key_len=%" PRIu32 ", "
- "iv_len=%" PRIu32 "\n", cipher_alg_name(cipher), key_len,
- iv_len);
- return 0;
- }
- return 1;
-}
-
-/**
- * Check if given authentication options are supported
- *
- * @param auth Authentication algorithm
- * @param key_len Key length
- * @param digest_len Digest length
- *
- * @retval non-zero if both authentication options are supported
- * @retval 0 if both options are not supported
- */
-static int check_auth_options(odp_auth_alg_t auth, uint32_t key_len,
- uint32_t digest_len)
-{
- int i;
- int num;
- odp_crypto_auth_capability_t capa[MAX_ALG_CAPA];
-
- num = odp_crypto_auth_capability(auth, capa, MAX_ALG_CAPA);
- CU_ASSERT_FATAL(num >= 1);
-
- for (i = 0; i < num; i++) {
- if (key_len == capa[i].key_len &&
- digest_len == capa[i].digest_len)
- break;
- }
-
- if (i == num) {
- printf("\n Unsupported: alg=%s, key_len=%" PRIu32 ", "
- "digest_len=%" PRIu32 "\n", auth_alg_name(auth), key_len,
- digest_len);
- return 0;
- }
- return 1;
-}
-
-static int check_alg_null(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_NULL);
-}
-
-void crypto_test_enc_alg_null(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0};
- unsigned int test_vec_num = (sizeof(null_reference_length) /
- sizeof(null_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- if (!check_cipher_options(ODP_CIPHER_ALG_NULL,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- null_reference_plaintext[i],
- null_reference_length[i],
- null_reference_plaintext[i],
- null_reference_length[i], NULL, 0);
- }
-}
-
-void crypto_test_dec_alg_null(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- unsigned int test_vec_num = (sizeof(null_reference_length) /
- sizeof(null_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- if (!check_cipher_options(ODP_CIPHER_ALG_NULL,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- null_reference_plaintext[i],
- null_reference_length[i],
- null_reference_plaintext[i],
- null_reference_length[i], NULL, 0);
- }
-}
-
-static int check_alg_3des_cbc(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_3DES_CBC, ODP_AUTH_ALG_NULL);
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for 3DES_CBC algorithm. IV for the operation is the session IV.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.*/
-void crypto_test_enc_alg_3des_cbc(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv;
- unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) /
- sizeof(tdes_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = tdes_cbc_reference_key[i];
- cipher_key.length = sizeof(tdes_cbc_reference_key[i]);
- iv.data = tdes_cbc_reference_iv[i];
- iv.length = sizeof(tdes_cbc_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_3DES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_3DES_CBC,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- tdes_cbc_reference_plaintext[i],
- tdes_cbc_reference_length[i],
- tdes_cbc_reference_ciphertext[i],
- tdes_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for 3DES_CBC algorithm. IV for the operation is the operation IV.
- * */
-void crypto_test_enc_alg_3des_cbc_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = TDES_CBC_IV_LEN };
- unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) /
- sizeof(tdes_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = tdes_cbc_reference_key[i];
- cipher_key.length = sizeof(tdes_cbc_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_3DES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_3DES_CBC,
- iv,
- tdes_cbc_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- tdes_cbc_reference_plaintext[i],
- tdes_cbc_reference_length[i],
- tdes_cbc_reference_ciphertext[i],
- tdes_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for 3DES_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_3des_cbc(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) /
- sizeof(tdes_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = tdes_cbc_reference_key[i];
- cipher_key.length = sizeof(tdes_cbc_reference_key[i]);
- iv.data = tdes_cbc_reference_iv[i];
- iv.length = sizeof(tdes_cbc_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_3DES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_3DES_CBC,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- tdes_cbc_reference_ciphertext[i],
- tdes_cbc_reference_length[i],
- tdes_cbc_reference_plaintext[i],
- tdes_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for 3DES_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_3des_cbc_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = TDES_CBC_IV_LEN };
- unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) /
- sizeof(tdes_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = tdes_cbc_reference_key[i];
- cipher_key.length = sizeof(tdes_cbc_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_3DES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_3DES_CBC,
- iv,
- tdes_cbc_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- tdes_cbc_reference_ciphertext[i],
- tdes_cbc_reference_length[i],
- tdes_cbc_reference_plaintext[i],
- tdes_cbc_reference_length[i], NULL, 0);
- }
-}
-
-static int check_alg_aes_gcm(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_AES_GCM, ODP_AUTH_ALG_AES_GCM);
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for AES128_GCM algorithm. IV for the operation is the session IV.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.*/
-void crypto_test_enc_alg_aes128_gcm(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN };
- unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) /
- sizeof(aes128_gcm_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_gcm_reference_key[i];
- cipher_key.length = sizeof(aes128_gcm_reference_key[i]);
- iv.data = aes128_gcm_reference_iv[i];
- iv.length = sizeof(aes128_gcm_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_GCM,
- cipher_key.length, iv.length))
- continue;
- if (!check_auth_options(ODP_AUTH_ALG_AES_GCM,
- auth_key.length,
- aes128_gcm_reference_tag_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i] +
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_tag_length[i]);
- }
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for AES128_GCM algorithm. IV for the operation is the session IV.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.*/
-void crypto_test_enc_alg_aes128_gcm_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN };
- unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) /
- sizeof(aes128_gcm_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_gcm_reference_key[i];
- cipher_key.length = sizeof(aes128_gcm_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_GCM,
- cipher_key.length, iv.length))
- continue;
- if (!check_auth_options(ODP_AUTH_ALG_AES_GCM,
- auth_key.length,
- aes128_gcm_reference_tag_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- aes128_gcm_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i] +
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_tag_length[i]);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for 3DES_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_aes128_gcm(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN };
- uint8_t wrong_digest[AES128_GCM_DIGEST_LEN];
- unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) /
- sizeof(aes128_gcm_reference_length[0]));
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_gcm_reference_key[i];
- cipher_key.length = sizeof(aes128_gcm_reference_key[i]);
- iv.data = aes128_gcm_reference_iv[i];
- iv.length = sizeof(aes128_gcm_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_GCM,
- cipher_key.length, iv.length))
- continue;
- if (!check_auth_options(ODP_AUTH_ALG_AES_GCM,
- auth_key.length,
- aes128_gcm_reference_tag_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i] +
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_tag_length[i]);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- wrong_digest,
- aes128_gcm_reference_tag_length[i]);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for 3DES_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_aes128_gcm_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN };
- uint8_t wrong_digest[AES128_GCM_DIGEST_LEN];
- unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) /
- sizeof(aes128_gcm_reference_length[0]));
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_gcm_reference_key[i];
- cipher_key.length = sizeof(aes128_gcm_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_GCM,
- cipher_key.length, iv.length))
- continue;
- if (!check_auth_options(ODP_AUTH_ALG_AES_GCM,
- auth_key.length,
- aes128_gcm_reference_tag_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- aes128_gcm_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_ciphertext[i] +
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_tag_length[i]);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_AES_GCM,
- iv,
- aes128_gcm_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_AES_GCM,
- auth_key,
- &aes128_gcm_cipher_range[i],
- &aes128_gcm_cipher_range[i],
- aes128_gcm_reference_aad[i],
- aes128_gcm_reference_aad_length[i],
- aes128_gcm_reference_ciphertext[i],
- aes128_gcm_reference_length[i],
- aes128_gcm_reference_plaintext[i],
- aes128_gcm_reference_length[i],
- wrong_digest,
- aes128_gcm_reference_tag_length[i]);
- }
-}
-
-static int check_alg_aes_cbc(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_AES_CBC, ODP_AUTH_ALG_NULL);
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for AES128_CBC algorithm. IV for the operation is the session IV.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.*/
-void crypto_test_enc_alg_aes128_cbc(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv;
- unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) /
- sizeof(aes128_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_cbc_reference_key[i];
- cipher_key.length = sizeof(aes128_cbc_reference_key[i]);
- iv.data = aes128_cbc_reference_iv[i];
- iv.length = sizeof(aes128_cbc_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_AES_CBC,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- aes128_cbc_reference_plaintext[i],
- aes128_cbc_reference_length[i],
- aes128_cbc_reference_ciphertext[i],
- aes128_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of encode (plaintext -> ciphertext)
- * operation for AES128_CBC algorithm. IV for the operation is the operation IV.
- * */
-void crypto_test_enc_alg_aes128_cbc_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_CBC_IV_LEN };
- unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) /
- sizeof(aes128_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_cbc_reference_key[i];
- cipher_key.length = sizeof(aes128_cbc_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_AES_CBC,
- iv,
- aes128_cbc_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- aes128_cbc_reference_plaintext[i],
- aes128_cbc_reference_length[i],
- aes128_cbc_reference_ciphertext[i],
- aes128_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for AES128_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_aes128_cbc(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) /
- sizeof(aes128_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_cbc_reference_key[i];
- cipher_key.length = sizeof(aes128_cbc_reference_key[i]);
- iv.data = aes128_cbc_reference_iv[i];
- iv.length = sizeof(aes128_cbc_reference_iv[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_AES_CBC,
- iv,
- NULL,
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- aes128_cbc_reference_ciphertext[i],
- aes128_cbc_reference_length[i],
- aes128_cbc_reference_plaintext[i],
- aes128_cbc_reference_length[i], NULL, 0);
- }
-}
-
-/* This test verifies the correctness of decode (ciphertext -> plaintext)
- * operation for AES128_CBC algorithm. IV for the operation is the session IV
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_dec_alg_aes128_cbc_ovr_iv(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = AES128_CBC_IV_LEN };
- unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) /
- sizeof(aes128_cbc_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- cipher_key.data = aes128_cbc_reference_key[i];
- cipher_key.length = sizeof(aes128_cbc_reference_key[i]);
-
- if (!check_cipher_options(ODP_CIPHER_ALG_AES_CBC,
- cipher_key.length, iv.length))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_AES_CBC,
- iv,
- aes128_cbc_reference_iv[i],
- cipher_key,
- ODP_AUTH_ALG_NULL,
- auth_key,
- NULL, NULL,
- NULL, 0,
- aes128_cbc_reference_ciphertext[i],
- aes128_cbc_reference_length[i],
- aes128_cbc_reference_plaintext[i],
- aes128_cbc_reference_length[i], NULL, 0);
- }
-}
-
-static int check_alg_hmac_md5(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_MD5_HMAC);
-}
-
-/* This test verifies the correctness of HMAC_MD5 digest operation.
- * The output check length is truncated to 12 bytes (96 bits) as
- * returned by the crypto operation API call.
- * Note that hash digest is a one-way operation.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_gen_alg_hmac_md5(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
-
- unsigned int test_vec_num = (sizeof(hmac_md5_reference_length) /
- sizeof(hmac_md5_reference_length[0]));
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_md5_reference_key[i];
- auth_key.length = sizeof(hmac_md5_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_MD5_HMAC, auth_key.length,
- hmac_md5_reference_digest_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_MD5_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_md5_reference_plaintext[i],
- hmac_md5_reference_length[i],
- NULL, 0,
- hmac_md5_reference_digest[i],
- hmac_md5_reference_digest_length[i]);
- }
-}
-
-void crypto_test_check_alg_hmac_md5(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- uint8_t wrong_digest[HMAC_MD5_DIGEST_LEN];
-
- unsigned int test_vec_num = (sizeof(hmac_md5_reference_length) /
- sizeof(hmac_md5_reference_length[0]));
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_md5_reference_key[i];
- auth_key.length = sizeof(hmac_md5_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_MD5_HMAC, auth_key.length,
- hmac_md5_reference_digest_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_MD5_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_md5_reference_plaintext[i],
- hmac_md5_reference_length[i],
- NULL, 0,
- hmac_md5_reference_digest[i],
- hmac_md5_reference_digest_length[i]);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_MD5_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_md5_reference_plaintext[i],
- hmac_md5_reference_length[i],
- NULL, 0,
- wrong_digest,
- hmac_md5_reference_digest_length[i]);
- }
-}
-
-static int check_alg_hmac_sha1(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA1_HMAC);
-}
-
-/* This test verifies the correctness of HMAC_SHA1 digest operation.
- * The output check length is truncated to 12 bytes (96 bits) as
- * returned by the crypto operation API call.
- * Note that hash digest is a one-way operation.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_gen_alg_hmac_sha1(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
-
- unsigned int test_vec_num = (sizeof(hmac_sha1_reference_length) /
- sizeof(hmac_sha1_reference_length[0]));
-
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha1_reference_key[i];
- auth_key.length = sizeof(hmac_sha1_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA1_HMAC,
- auth_key.length,
- HMAC_SHA1_96_CHECK_LEN))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA1_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha1_reference_plaintext[i],
- hmac_sha1_reference_length[i],
- NULL, 0,
- hmac_sha1_reference_digest[i],
- HMAC_SHA1_96_CHECK_LEN);
- }
-}
-
-void crypto_test_check_alg_hmac_sha1(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- uint8_t wrong_digest[HMAC_SHA1_DIGEST_LEN];
-
- unsigned int test_vec_num = (sizeof(hmac_sha1_reference_length) /
- sizeof(hmac_sha1_reference_length[0]));
-
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha1_reference_key[i];
- auth_key.length = sizeof(hmac_sha1_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA1_HMAC,
- auth_key.length,
- HMAC_SHA1_96_CHECK_LEN))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA1_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha1_reference_plaintext[i],
- hmac_sha1_reference_length[i],
- NULL, 0,
- hmac_sha1_reference_digest[i],
- HMAC_SHA1_96_CHECK_LEN);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA1_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha1_reference_plaintext[i],
- hmac_sha1_reference_length[i],
- NULL, 0,
- wrong_digest,
- HMAC_SHA1_96_CHECK_LEN);
- }
-}
-
-static int check_alg_hmac_sha256(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA256_HMAC);
-}
-
-/* This test verifies the correctness of HMAC_SHA256 digest operation.
- * The output check length is truncated to 16 bytes (128 bits) as
- * returned by the crypto operation API call.
- * Note that hash digest is a one-way operation.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_gen_alg_hmac_sha256(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
-
- unsigned int test_vec_num = (sizeof(hmac_sha256_reference_length) /
- sizeof(hmac_sha256_reference_length[0]));
-
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha256_reference_key[i];
- auth_key.length = sizeof(hmac_sha256_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA256_HMAC,
- auth_key.length,
- hmac_sha256_reference_digest_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA256_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha256_reference_plaintext[i],
- hmac_sha256_reference_length[i],
- NULL, 0,
- hmac_sha256_reference_digest[i],
- hmac_sha256_reference_digest_length[i]);
- }
-}
-
-void crypto_test_check_alg_hmac_sha256(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- uint8_t wrong_digest[HMAC_SHA256_DIGEST_LEN];
-
- unsigned int test_vec_num = (sizeof(hmac_sha256_reference_length) /
- sizeof(hmac_sha256_reference_length[0]));
-
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha256_reference_key[i];
- auth_key.length = sizeof(hmac_sha256_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA256_HMAC,
- auth_key.length,
- hmac_sha256_reference_digest_length[i]))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA256_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha256_reference_plaintext[i],
- hmac_sha256_reference_length[i],
- NULL, 0,
- hmac_sha256_reference_digest[i],
- hmac_sha256_reference_digest_length[i]);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA256_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha256_reference_plaintext[i],
- hmac_sha256_reference_length[i],
- NULL, 0,
- wrong_digest,
- hmac_sha256_reference_digest_length[i]);
- }
-}
-
-static int check_alg_hmac_sha512(void)
-{
- return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA512_HMAC);
-}
-
-/* This test verifies the correctness of HMAC_SHA512 digest operation.
- * The output check length is truncated to 32 bytes (256 bits) as
- * returned by the crypto operation API call.
- * Note that hash digest is a one-way operation.
- * In addition the test verifies if the implementation can use the
- * packet buffer as completion event buffer.
- * */
-void crypto_test_gen_alg_hmac_sha512(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
-
- unsigned int test_vec_num = (sizeof(hmac_sha512_reference_length) /
- sizeof(hmac_sha512_reference_length[0]));
-
- unsigned int i;
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha512_reference_key[i];
- auth_key.length = sizeof(hmac_sha512_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA512_HMAC,
- auth_key.length,
- HMAC_SHA512_256_CHECK_LEN))
- continue;
-
- alg_test(ODP_CRYPTO_OP_ENCODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA512_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha512_reference_plaintext[i],
- hmac_sha512_reference_length[i],
- NULL, 0,
- hmac_sha512_reference_digest[i],
- HMAC_SHA512_256_CHECK_LEN);
- }
-}
-
-void crypto_test_check_alg_hmac_sha512(void)
-{
- odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 },
- auth_key = { .data = NULL, .length = 0 };
- odp_crypto_iv_t iv = { .data = NULL, .length = 0 };
- uint8_t wrong_digest[HMAC_SHA512_DIGEST_LEN];
-
- unsigned int test_vec_num = (sizeof(hmac_sha512_reference_length) /
- sizeof(hmac_sha512_reference_length[0]));
-
- unsigned int i;
-
- memset(wrong_digest, 0xa5, sizeof(wrong_digest));
-
- for (i = 0; i < test_vec_num; i++) {
- auth_key.data = hmac_sha512_reference_key[i];
- auth_key.length = sizeof(hmac_sha512_reference_key[i]);
-
- if (!check_auth_options(ODP_AUTH_ALG_SHA512_HMAC,
- auth_key.length,
- HMAC_SHA512_256_CHECK_LEN))
- continue;
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 0,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA512_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha512_reference_plaintext[i],
- hmac_sha512_reference_length[i],
- NULL, 0,
- hmac_sha512_reference_digest[i],
- HMAC_SHA512_256_CHECK_LEN);
-
- alg_test(ODP_CRYPTO_OP_DECODE,
- 1,
- ODP_CIPHER_ALG_NULL,
- iv,
- iv.data,
- cipher_key,
- ODP_AUTH_ALG_SHA512_HMAC,
- auth_key,
- NULL, NULL,
- NULL, 0,
- hmac_sha512_reference_plaintext[i],
- hmac_sha512_reference_length[i],
- NULL, 0,
- wrong_digest,
- HMAC_SHA512_256_CHECK_LEN);
- }
-}
-
-int crypto_suite_sync_init(void)
-{
- suite_context.pool = odp_pool_lookup("packet_pool");
- if (suite_context.pool == ODP_POOL_INVALID)
- return -1;
-
- suite_context.queue = ODP_QUEUE_INVALID;
- suite_context.pref_mode = ODP_CRYPTO_SYNC;
- return 0;
-}
-
-int crypto_suite_async_init(void)
-{
- suite_context.pool = odp_pool_lookup("packet_pool");
- if (suite_context.pool == ODP_POOL_INVALID)
- return -1;
- suite_context.queue = odp_queue_lookup("crypto-out");
- if (suite_context.queue == ODP_QUEUE_INVALID)
- return -1;
-
- suite_context.pref_mode = ODP_CRYPTO_ASYNC;
- return 0;
-}
-
-odp_testinfo_t crypto_suite[] = {
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_null,
- check_alg_null),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_null,
- check_alg_null),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_3des_cbc,
- check_alg_3des_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_3des_cbc,
- check_alg_3des_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_3des_cbc_ovr_iv,
- check_alg_3des_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_3des_cbc_ovr_iv,
- check_alg_3des_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes128_cbc,
- check_alg_aes_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes128_cbc,
- check_alg_aes_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes128_cbc_ovr_iv,
- check_alg_aes_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes128_cbc_ovr_iv,
- check_alg_aes_cbc),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes128_gcm,
- check_alg_aes_gcm),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes128_gcm_ovr_iv,
- check_alg_aes_gcm),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes128_gcm,
- check_alg_aes_gcm),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes128_gcm_ovr_iv,
- check_alg_aes_gcm),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_md5,
- check_alg_hmac_md5),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_md5,
- check_alg_hmac_md5),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha1,
- check_alg_hmac_sha1),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha1,
- check_alg_hmac_sha1),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha256,
- check_alg_hmac_sha256),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha256,
- check_alg_hmac_sha256),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha512,
- check_alg_hmac_sha512),
- ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha512,
- check_alg_hmac_sha512),
- ODP_TEST_INFO_NULL,
-};
-
-int crypto_suite_term(void)
-{
- int i;
- int first = 1;
-
- for (i = 0; crypto_suite[i].pName; i++) {
- if (crypto_suite[i].check_active &&
- crypto_suite[i].check_active() == ODP_TEST_INACTIVE) {
- if (first) {
- first = 0;
- printf("\n\n Inactive tests:\n");
- }
- printf(" %s\n", crypto_suite[i].pName);
- }
- }
- return 0;
-}
diff --git a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h
deleted file mode 100644
index 0f6933790..000000000
--- a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#ifndef ODP_CRYPTO_TEST_ASYNC_INP_
-#define ODP_CRYPTO_TEST_ASYNC_INP_
-
-#include <odp_cunit_common.h>
-
-/* Suite names */
-#define ODP_CRYPTO_ASYNC_INP "odp_crypto_async_inp"
-#define ODP_CRYPTO_SYNC_INP "odp_crypto_sync_inp"
-
-/* Suite test array */
-extern odp_testinfo_t crypto_suite[];
-
-int crypto_suite_sync_init(void);
-int crypto_suite_async_init(void);
-int crypto_suite_term(void);
-
-#endif
diff --git a/test/common_plat/validation/api/crypto/test_vectors.h b/test/common_plat/validation/api/crypto/test_vectors.h
deleted file mode 100644
index bd8bf347d..000000000
--- a/test/common_plat/validation/api/crypto/test_vectors.h
+++ /dev/null
@@ -1,450 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_CRYPTO_VECTORS_H_
-#define _ODP_TEST_CRYPTO_VECTORS_H_
-
-#include "test_vectors_len.h"
-
-/** length in bytes */
-static uint32_t null_reference_length[] = { 8, 16 };
-
-static uint8_t
-null_reference_plaintext[][NULL_MAX_DATA_LEN] = {
- {0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56},
-
- {0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87, 0x6d, 0x8e, 0xa2, 0x30,
- 0x94, 0xea, 0x53, 0x09}
-};
-
-/* TDES-CBC reference vectors, according to
- * "http://csrc.nist.gov/groups/STM/cavp/documents/des/DESMMT.pdf"
- */
-static uint8_t tdes_cbc_reference_key[][TDES_CBC_KEY_LEN] = {
- {0x62, 0x7f, 0x46, 0x0e, 0x08, 0x10, 0x4a, 0x10, 0x43, 0xcd, 0x26, 0x5d,
- 0x58, 0x40, 0xea, 0xf1, 0x31, 0x3e, 0xdf, 0x97, 0xdf, 0x2a, 0x8a, 0x8c,
- },
-
- {0x37, 0xae, 0x5e, 0xbf, 0x46, 0xdf, 0xf2, 0xdc, 0x07, 0x54, 0xb9, 0x4f,
- 0x31, 0xcb, 0xb3, 0x85, 0x5e, 0x7f, 0xd3, 0x6d, 0xc8, 0x70, 0xbf, 0xae}
-};
-
-static uint8_t tdes_cbc_reference_iv[][TDES_CBC_IV_LEN] = {
- {0x8e, 0x29, 0xf7, 0x5e, 0xa7, 0x7e, 0x54, 0x75},
-
- {0x3d, 0x1d, 0xe3, 0xcc, 0x13, 0x2e, 0x3b, 0x65}
-};
-
-/** length in bytes */
-static uint32_t tdes_cbc_reference_length[] = { 8, 16 };
-
-static uint8_t
-tdes_cbc_reference_plaintext[][TDES_CBC_MAX_DATA_LEN] = {
- {0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56},
-
- {0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87, 0x6d, 0x8e, 0xa2, 0x30,
- 0x94, 0xea, 0x53, 0x09}
-};
-
-static uint8_t
-tdes_cbc_reference_ciphertext[][TDES_CBC_MAX_DATA_LEN] = {
- {0xb2, 0x2b, 0x8d, 0x66, 0xde, 0x97, 0x06, 0x92},
-
- {0x7b, 0x1f, 0x7c, 0x7e, 0x3b, 0x1c, 0x94, 0x8e, 0xbd, 0x04, 0xa7, 0x5f,
- 0xfb, 0xa7, 0xd2, 0xf5}
-};
-
-static uint8_t aes128_cbc_reference_key[][AES128_CBC_KEY_LEN] = {
- {0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
- 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
- {0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
- 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
- {0x6c, 0x3e, 0xa0, 0x47, 0x76, 0x30, 0xce, 0x21,
- 0xa2, 0xce, 0x33, 0x4a, 0xa7, 0x46, 0xc2, 0xcd },
- {0x56, 0xe4, 0x7a, 0x38, 0xc5, 0x59, 0x89, 0x74,
- 0xbc, 0x46, 0x90, 0x3d, 0xba, 0x29, 0x03, 0x49 }
-};
-
-static uint8_t aes128_cbc_reference_iv[][AES128_CBC_IV_LEN] = {
- { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
- 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
- { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
- 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
- { 0xc7, 0x82, 0xdc, 0x4c, 0x09, 0x8c, 0x66, 0xcb,
- 0xd9, 0xcd, 0x27, 0xd8, 0x25, 0x68, 0x2c, 0x81 },
- { 0x8c, 0xe8, 0x2e, 0xef, 0xbe, 0xa0, 0xda, 0x3c,
- 0x44, 0x69, 0x9e, 0xd7, 0xdb, 0x51, 0xb7, 0xd9 }
-};
-
-/** length in bytes */
-static uint32_t aes128_cbc_reference_length[] = { 16, 32, 48, 64 };
-
-static uint8_t
-aes128_cbc_reference_plaintext[][AES128_CBC_MAX_DATA_LEN] = {
- "Single block msg",
- { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
- "This is a 48-byte message (exactly 3 AES blocks)",
- { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
- 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
- 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
- 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
- 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
- 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
- 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf }
-};
-
-static uint8_t
-aes128_cbc_reference_ciphertext[][AES128_CBC_MAX_DATA_LEN] = {
- { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
- 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
- { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
- 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
- 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
- 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
- { 0xd0, 0xa0, 0x2b, 0x38, 0x36, 0x45, 0x17, 0x53,
- 0xd4, 0x93, 0x66, 0x5d, 0x33, 0xf0, 0xe8, 0x86,
- 0x2d, 0xea, 0x54, 0xcd, 0xb2, 0x93, 0xab, 0xc7,
- 0x50, 0x69, 0x39, 0x27, 0x67, 0x72, 0xf8, 0xd5,
- 0x02, 0x1c, 0x19, 0x21, 0x6b, 0xad, 0x52, 0x5c,
- 0x85, 0x79, 0x69, 0x5d, 0x83, 0xba, 0x26, 0x84 },
- { 0xc3, 0x0e, 0x32, 0xff, 0xed, 0xc0, 0x77, 0x4e,
- 0x6a, 0xff, 0x6a, 0xf0, 0x86, 0x9f, 0x71, 0xaa,
- 0x0f, 0x3a, 0xf0, 0x7a, 0x9a, 0x31, 0xa9, 0xc6,
- 0x84, 0xdb, 0x20, 0x7e, 0xb0, 0xef, 0x8e, 0x4e,
- 0x35, 0x90, 0x7a, 0xa6, 0x32, 0xc3, 0xff, 0xdf,
- 0x86, 0x8b, 0xb7, 0xb2, 0x9d, 0x3d, 0x46, 0xad,
- 0x83, 0xce, 0x9f, 0x9a, 0x10, 0x2e, 0xe9, 0x9d,
- 0x49, 0xa5, 0x3e, 0x87, 0xf4, 0xc3, 0xda, 0x55 }
-};
-
-/* AES-GCM test vectors extracted from
- * https://tools.ietf.org/html/draft-mcgrew-gcm-test-01#section-2
- */
-static uint8_t aes128_gcm_reference_key[][AES128_GCM_KEY_LEN] = {
- { 0x4c, 0x80, 0xcd, 0xef, 0xbb, 0x5d, 0x10, 0xda,
- 0x90, 0x6a, 0xc7, 0x3c, 0x36, 0x13, 0xa6, 0x34 },
- { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
- 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 },
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- { 0x3d, 0xe0, 0x98, 0x74, 0xb3, 0x88, 0xe6, 0x49,
- 0x19, 0x88, 0xd0, 0xc3, 0x60, 0x7e, 0xae, 0x1f }
-};
-
-static uint8_t aes128_gcm_reference_iv[][AES128_GCM_IV_LEN] = {
- { 0x2e, 0x44, 0x3b, 0x68, 0x49, 0x56, 0xed, 0x7e,
- 0x3b, 0x24, 0x4c, 0xfe },
- { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
- 0xde, 0xca, 0xf8, 0x88 },
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00 },
- { 0x57, 0x69, 0x0e, 0x43, 0x4e, 0x28, 0x00, 0x00,
- 0xa2, 0xfc, 0xa1, 0xa3 }
-};
-
-static uint32_t aes128_gcm_reference_length[] = { 72, 64, 64, 28};
-
-static uint32_t aes128_gcm_reference_tag_length[] = { 16, 16, 16, 16};
-
-static uint32_t aes128_gcm_reference_aad_length[] = { 12, 8, 8, 12};
-
-static odp_packet_data_range_t aes128_gcm_cipher_range[] = {
- { .offset = 0, .length = 72 },
- { .offset = 0, .length = 64 },
- { .offset = 0, .length = 64 },
- { .offset = 0, .length = 28 },
-};
-
-static uint8_t aes128_gcm_reference_aad[][AES128_GCM_MAX_DATA_LEN] = {
- { 0x00, 0x00, 0x43, 0x21, 0x87, 0x65, 0x43, 0x21,
- 0x00, 0x00, 0x00, 0x00, },
- { 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a, },
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, },
- { 0x42, 0xf6, 0x7e, 0x3f, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, },
-};
-
-static uint8_t
-aes128_gcm_reference_plaintext[][AES128_GCM_MAX_DATA_LEN] = {
- { 0x45, 0x00, 0x00, 0x48, 0x69, 0x9a, 0x00, 0x00,
- 0x80, 0x11, 0x4d, 0xb7, 0xc0, 0xa8, 0x01, 0x02,
- 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x9b, 0xf1, 0x56,
- 0x38, 0xd3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x04, 0x5f, 0x73, 0x69,
- 0x70, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x03, 0x73,
- 0x69, 0x70, 0x09, 0x63, 0x79, 0x62, 0x65, 0x72,
- 0x63, 0x69, 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00,
- 0x00, 0x21, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01 },
-
- { 0x45, 0x00, 0x00, 0x3e, 0x69, 0x8f, 0x00, 0x00,
- 0x80, 0x11, 0x4d, 0xcc, 0xc0, 0xa8, 0x01, 0x02,
- 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x98, 0x00, 0x35,
- 0x00, 0x2a, 0x23, 0x43, 0xb2, 0xd0, 0x01, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x73, 0x69, 0x70, 0x09, 0x63, 0x79, 0x62,
- 0x65, 0x72, 0x63, 0x69, 0x74, 0x79, 0x02, 0x64,
- 0x6b, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 },
-
- { 0x45, 0x00, 0x00, 0x3c, 0x99, 0xc5, 0x00, 0x00,
- 0x80, 0x01, 0xcb, 0x7a, 0x40, 0x67, 0x93, 0x18,
- 0x01, 0x01, 0x01, 0x01, 0x08, 0x00, 0x07, 0x5c,
- 0x02, 0x00, 0x44, 0x00, 0x61, 0x62, 0x63, 0x64,
- 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
- 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
- 0x75, 0x76, 0x77, 0x61, 0x62, 0x63, 0x64, 0x65,
- 0x66, 0x67, 0x68, 0x69, 0x01, 0x02, 0x02, 0x01 },
-
- { 0x45, 0x00, 0x00, 0x1c, 0x42, 0xa2, 0x00, 0x00,
- 0x80, 0x01, 0x44, 0x1f, 0x40, 0x67, 0x93, 0xb6,
- 0xe0, 0x00, 0x00, 0x02, 0x0a, 0x00, 0xf5, 0xff,
- 0x01, 0x02, 0x02, 0x01 }
-};
-
-static uint8_t
-aes128_gcm_reference_ciphertext[][AES128_GCM_MAX_DATA_LEN] = {
- { /* Plain */
- 0xfe, 0xcf, 0x53, 0x7e, 0x72, 0x9d, 0x5b, 0x07,
- 0xdc, 0x30, 0xdf, 0x52, 0x8d, 0xd2, 0x2b, 0x76,
- 0x8d, 0x1b, 0x98, 0x73, 0x66, 0x96, 0xa6, 0xfd,
- 0x34, 0x85, 0x09, 0xfa, 0x13, 0xce, 0xac, 0x34,
- 0xcf, 0xa2, 0x43, 0x6f, 0x14, 0xa3, 0xf3, 0xcf,
- 0x65, 0x92, 0x5b, 0xf1, 0xf4, 0xa1, 0x3c, 0x5d,
- 0x15, 0xb2, 0x1e, 0x18, 0x84, 0xf5, 0xff, 0x62,
- 0x47, 0xae, 0xab, 0xb7, 0x86, 0xb9, 0x3b, 0xce,
- 0x61, 0xbc, 0x17, 0xd7, 0x68, 0xfd, 0x97, 0x32,
- /* Digest */
- 0x45, 0x90, 0x18, 0x14, 0x8f, 0x6c, 0xbe, 0x72,
- 0x2f, 0xd0, 0x47, 0x96, 0x56, 0x2d, 0xfd, 0xb4 },
-
- { /* Plain */
- 0xde, 0xb2, 0x2c, 0xd9, 0xb0, 0x7c, 0x72, 0xc1,
- 0x6e, 0x3a, 0x65, 0xbe, 0xeb, 0x8d, 0xf3, 0x04,
- 0xa5, 0xa5, 0x89, 0x7d, 0x33, 0xae, 0x53, 0x0f,
- 0x1b, 0xa7, 0x6d, 0x5d, 0x11, 0x4d, 0x2a, 0x5c,
- 0x3d, 0xe8, 0x18, 0x27, 0xc1, 0x0e, 0x9a, 0x4f,
- 0x51, 0x33, 0x0d, 0x0e, 0xec, 0x41, 0x66, 0x42,
- 0xcf, 0xbb, 0x85, 0xa5, 0xb4, 0x7e, 0x48, 0xa4,
- 0xec, 0x3b, 0x9b, 0xa9, 0x5d, 0x91, 0x8b, 0xd1,
- /* Digest */
- 0x83, 0xb7, 0x0d, 0x3a, 0xa8, 0xbc, 0x6e, 0xe4,
- 0xc3, 0x09, 0xe9, 0xd8, 0x5a, 0x41, 0xad, 0x4a },
-
- { /* Plain */
- 0x46, 0x88, 0xda, 0xf2, 0xf9, 0x73, 0xa3, 0x92,
- 0x73, 0x29, 0x09, 0xc3, 0x31, 0xd5, 0x6d, 0x60,
- 0xf6, 0x94, 0xab, 0xaa, 0x41, 0x4b, 0x5e, 0x7f,
- 0xf5, 0xfd, 0xcd, 0xff, 0xf5, 0xe9, 0xa2, 0x84,
- 0x45, 0x64, 0x76, 0x49, 0x27, 0x19, 0xff, 0xb6,
- 0x4d, 0xe7, 0xd9, 0xdc, 0xa1, 0xe1, 0xd8, 0x94,
- 0xbc, 0x3b, 0xd5, 0x78, 0x73, 0xed, 0x4d, 0x18,
- 0x1d, 0x19, 0xd4, 0xd5, 0xc8, 0xc1, 0x8a, 0xf3,
- /* Digest */
- 0xf8, 0x21, 0xd4, 0x96, 0xee, 0xb0, 0x96, 0xe9,
- 0x8a, 0xd2, 0xb6, 0x9e, 0x47, 0x99, 0xc7, 0x1d },
-
- { /* Plain */
- 0xfb, 0xa2, 0xca, 0x84, 0x5e, 0x5d, 0xf9, 0xf0,
- 0xf2, 0x2c, 0x3e, 0x6e, 0x86, 0xdd, 0x83, 0x1e,
- 0x1f, 0xc6, 0x57, 0x92, 0xcd, 0x1a, 0xf9, 0x13,
- 0x0e, 0x13, 0x79, 0xed,
- /* Digest */
- 0x36, 0x9f, 0x07, 0x1f, 0x35, 0xe0, 0x34, 0xbe,
- 0x95, 0xf1, 0x12, 0xe4, 0xe7, 0xd0, 0x5d, 0x35 }
-};
-
-static uint8_t hmac_md5_reference_key[][HMAC_MD5_KEY_LEN] = {
- { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b },
-
- /* "Jefe" */
- { 0x4a, 0x65, 0x66, 0x65 },
-
- { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }
-};
-
-static uint32_t hmac_md5_reference_length[] = { 8, 28, 50 };
-
-static uint8_t
-hmac_md5_reference_plaintext[][HMAC_MD5_MAX_DATA_LEN] = {
- /* "Hi There" */
- { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
-
- /* what do ya want for nothing?*/
- { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
- 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
- 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
- 0x69, 0x6e, 0x67, 0x3f },
-
- { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }
-};
-
-static uint8_t hmac_md5_reference_digest[][HMAC_MD5_DIGEST_LEN] = {
- { 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
- 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d },
-
- { 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
- 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 },
-
- { 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
- 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 }
-};
-
-static uint32_t hmac_md5_reference_digest_length[] = {
- 12, 12, 12
-};
-
-static uint8_t hmac_sha256_reference_key[][HMAC_SHA256_KEY_LEN] = {
- { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b },
-
- /* "Jefe" */
- { 0x4a, 0x65, 0x66, 0x65 },
-
- { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa }
-};
-
-static uint32_t hmac_sha256_reference_length[] = { 8, 28, 50 };
-
-static uint8_t
-hmac_sha256_reference_plaintext[][HMAC_SHA256_MAX_DATA_LEN] = {
- /* "Hi There" */
- { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
-
- /* what do ya want for nothing?*/
- { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
- 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
- 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
- 0x69, 0x6e, 0x67, 0x3f },
-
- { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }
-};
-
-static uint8_t hmac_sha256_reference_digest[][HMAC_SHA256_DIGEST_LEN] = {
- { 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
- 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b },
-
- { 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
- 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7 },
-
- { 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
- 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7 }
-};
-
-static uint32_t hmac_sha256_reference_digest_length[] = {
- 16, 16, 16
-};
-
-static uint8_t hmac_sha1_reference_key[][HMAC_SHA1_KEY_LEN] = {
- { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b },
-
- /* "Jefe" */
- { 0x4a, 0x65, 0x66, 0x65 },
-
- { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa }
-};
-
-static uint32_t hmac_sha1_reference_length[] = { 8, 28, 50 };
-
-static uint8_t
-hmac_sha1_reference_plaintext[][HMAC_SHA1_MAX_DATA_LEN] = {
- /* "Hi There" */
- { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
-
- /* what do ya want for nothing?*/
- { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
- 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
- 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
- 0x69, 0x6e, 0x67, 0x3f },
-
- { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }
-};
-
-static uint8_t hmac_sha1_reference_digest[][HMAC_SHA1_DIGEST_LEN] = {
- { 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05,
- 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6 },
-
- { 0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb,
- 0x2f, 0xa2, 0xd2, 0x74, 0x16, 0xd5 },
-
- { 0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac,
- 0x11, 0xcd, 0x91, 0xa3, 0x9a, 0xf4 },
-};
-
-static uint8_t hmac_sha512_reference_key[][HMAC_SHA512_KEY_LEN] = {
- { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b },
-
- /* "Jefe" */
- { 0x4a, 0x65, 0x66, 0x65 },
-
- { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa }
-};
-
-static uint32_t hmac_sha512_reference_length[] = { 8, 28, 50 };
-
-static uint8_t
-hmac_sha512_reference_plaintext[][HMAC_SHA512_MAX_DATA_LEN] = {
- /* "Hi There" */
- { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
-
- /* what do ya want for nothing?*/
- { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
- 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
- 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
- 0x69, 0x6e, 0x67, 0x3f },
-
- { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }
-};
-
-static uint8_t hmac_sha512_reference_digest[][HMAC_SHA512_DIGEST_LEN] = {
- { 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d,
- 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0,
- 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78,
- 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde },
-
- { 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2,
- 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3,
- 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6,
- 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54 },
-
- { 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84,
- 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9,
- 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36,
- 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39 }
-};
-
-#endif
diff --git a/test/common_plat/validation/api/crypto/test_vectors_len.h b/test/common_plat/validation/api/crypto/test_vectors_len.h
deleted file mode 100644
index 20a7ddbad..000000000
--- a/test/common_plat/validation/api/crypto/test_vectors_len.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#ifndef TEST_VECTORS_LEN_
-#define TEST_VECTORS_LEN_
-
-/* NULL */
-#define NULL_MAX_DATA_LEN 16
-
-/* TDES-CBC */
-#define TDES_CBC_KEY_LEN 24
-#define TDES_CBC_IV_LEN 8
-#define TDES_CBC_MAX_DATA_LEN 16
-
-/* AES128-CBC */
-#define AES128_CBC_KEY_LEN 16
-#define AES128_CBC_IV_LEN 16
-#define AES128_CBC_MAX_DATA_LEN 64
-
-/* AES128-CBC */
-#define AES128_GCM_KEY_LEN 16
-#define AES128_GCM_IV_LEN 12
-#define AES128_GCM_MAX_DATA_LEN 106
-#define AES128_GCM_DIGEST_LEN 16
-
-/* HMAC-MD5 */
-#define HMAC_MD5_KEY_LEN 16
-#define HMAC_MD5_MAX_DATA_LEN 128
-#define HMAC_MD5_DIGEST_LEN 16
-
-/* HMAC-SHA256 */
-#define HMAC_SHA256_KEY_LEN 32
-#define HMAC_SHA256_MAX_DATA_LEN 128
-#define HMAC_SHA256_DIGEST_LEN 32
-
-/* HMAC-SHA1 */
-#define HMAC_SHA1_KEY_LEN 20
-#define HMAC_SHA1_MAX_DATA_LEN 128
-#define HMAC_SHA1_DIGEST_LEN 20
-#define HMAC_SHA1_96_CHECK_LEN 12
-
-/* HMAC-SHA512 */
-#define HMAC_SHA512_KEY_LEN 64
-#define HMAC_SHA512_MAX_DATA_LEN 128
-#define HMAC_SHA512_DIGEST_LEN 64
-#define HMAC_SHA512_256_CHECK_LEN 32
-
-#endif
diff --git a/test/common_plat/validation/api/errno/Makefile.am b/test/common_plat/validation/api/errno/Makefile.am
deleted file mode 100644
index a24275d6e..000000000
--- a/test/common_plat/validation/api/errno/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtesterrno.la
-libtesterrno_la_SOURCES = errno.c
-
-test_PROGRAMS = errno_main$(EXEEXT)
-dist_errno_main_SOURCES = errno_main.c
-errno_main_LDADD = libtesterrno.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = errno.h
diff --git a/test/common_plat/validation/api/errno/errno.c b/test/common_plat/validation/api/errno/errno.c
deleted file mode 100644
index e3b6ced54..000000000
--- a/test/common_plat/validation/api/errno/errno.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include "odp_cunit_common.h"
-#include "errno.h"
-
-void errno_test_odp_errno_sunny_day(void)
-{
- int my_errno;
-
- odp_errno_zero();
- my_errno = odp_errno();
- CU_ASSERT_TRUE(my_errno == 0);
- odp_errno_print("odp_errno");
- CU_ASSERT_PTR_NOT_NULL(odp_errno_str(my_errno));
-}
-
-odp_testinfo_t errno_suite[] = {
- ODP_TEST_INFO(errno_test_odp_errno_sunny_day),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t errno_suites[] = {
- {"Errno", NULL, NULL, errno_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int errno_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(errno_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/errno/errno.h b/test/common_plat/validation/api/errno/errno.h
deleted file mode 100644
index 720385196..000000000
--- a/test/common_plat/validation/api/errno/errno.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_ERRNO_H_
-#define _ODP_TEST_ERRNO_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void errno_test_odp_errno_sunny_day(void);
-
-/* test arrays: */
-extern odp_testinfo_t errno_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t errno_suites[];
-
-/* main test program: */
-int errno_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/errno/errno_main.c b/test/common_plat/validation/api/errno/errno_main.c
deleted file mode 100644
index 0138279ef..000000000
--- a/test/common_plat/validation/api/errno/errno_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "errno.h"
-
-int main(int argc, char *argv[])
-{
- return errno_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/hash/Makefile.am b/test/common_plat/validation/api/hash/Makefile.am
deleted file mode 100644
index b899b8bd3..000000000
--- a/test/common_plat/validation/api/hash/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtesthash.la
-libtesthash_la_SOURCES = hash.c
-
-test_PROGRAMS = hash_main$(EXEEXT)
-dist_hash_main_SOURCES = hash_main.c
-hash_main_LDADD = libtesthash.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = hash.h
diff --git a/test/common_plat/validation/api/hash/hash.c b/test/common_plat/validation/api/hash/hash.c
deleted file mode 100644
index b353fcecd..000000000
--- a/test/common_plat/validation/api/hash/hash.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "hash.h"
-
-void hash_test_crc32c(void)
-{
- uint32_t test_value = 0x12345678;
- uint32_t ret = odp_hash_crc32c(&test_value, 4, 0);
-
- CU_ASSERT(ret == 0xfa745634);
-
- test_value = 0x87654321;
- ret = odp_hash_crc32c(&test_value, 4, 0);
-
- CU_ASSERT(ret == 0xaca37da7);
-
- uint32_t test_values[] = {0x12345678, 0x87654321};
-
- ret = odp_hash_crc32c(test_values, 8, 0);
-
- CU_ASSERT(ret == 0xe6e910b0);
-}
-
-odp_testinfo_t hash_suite[] = {
- ODP_TEST_INFO(hash_test_crc32c),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t hash_suites[] = {
- {"Hash", NULL, NULL, hash_suite},
- ODP_SUITE_INFO_NULL
-};
-
-int hash_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(hash_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/hash/hash.h b/test/common_plat/validation/api/hash/hash.h
deleted file mode 100644
index 936571e6a..000000000
--- a/test/common_plat/validation/api/hash/hash.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_HASH_H_
-#define _ODP_TEST_HASH_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void hash_test_crc32c(void);
-
-/* test arrays: */
-extern odp_testinfo_t hash_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t hash_suites[];
-
-/* main test program: */
-int hash_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/hash/hash_main.c b/test/common_plat/validation/api/hash/hash_main.c
deleted file mode 100644
index f9818b7bb..000000000
--- a/test/common_plat/validation/api/hash/hash_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "hash.h"
-
-int main(int argc, char *argv[])
-{
- return hash_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/init/.gitignore b/test/common_plat/validation/api/init/.gitignore
deleted file mode 100644
index f433708b0..000000000
--- a/test/common_plat/validation/api/init/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-init_main_abort
-init_main_log
-init_main_ok
diff --git a/test/common_plat/validation/api/init/Makefile.am b/test/common_plat/validation/api/init/Makefile.am
deleted file mode 100644
index 0793e6423..000000000
--- a/test/common_plat/validation/api/init/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-include ../Makefile.inc
-noinst_LTLIBRARIES = libtestinit.la
-libtestinit_la_SOURCES = init.c
-
-# most platforms are expected not to support multiple ODP inits
-# following each other: therefore 3 separate binaries are
-# created, each containing its ODP init test.
-test_PROGRAMS = init_main_abort$(EXEEXT) init_main_log$(EXEEXT) init_main_ok$(EXEEXT)
-dist_init_main_abort_SOURCES = init_main_abort.c
-dist_init_main_log_SOURCES = init_main_log.c
-dist_init_main_ok_SOURCES = init_main_ok.c
-init_main_abort_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP)
-init_main_log_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP)
-init_main_ok_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = init.h
diff --git a/test/common_plat/validation/api/init/init.c b/test/common_plat/validation/api/init/init.c
deleted file mode 100644
index 61055fad5..000000000
--- a/test/common_plat/validation/api/init/init.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <odp_api.h>
-#include <CUnit/Basic.h>
-#include "init.h"
-
-/* flag set when the replacement logging function is used */
-int replacement_logging_used;
-
-/* replacement abort function: */
-static void odp_init_abort(void) ODP_NORETURN;
-
-/* replacement log function: */
-ODP_PRINTF_FORMAT(2, 3)
-static int odp_init_log(odp_log_level_t level, const char *fmt, ...);
-
-/* test ODP global init, with alternate abort function */
-void init_test_odp_init_global_replace_abort(void)
-{
- int status;
- struct odp_init_t init_data;
- odp_instance_t instance;
-
- memset(&init_data, 0, sizeof(init_data));
- init_data.abort_fn = &odp_init_abort;
-
- status = odp_init_global(&instance, &init_data, NULL);
- CU_ASSERT_FATAL(status == 0);
-
- status = odp_term_global(instance);
- CU_ASSERT(status == 0);
-}
-
-odp_testinfo_t init_suite_abort[] = {
- ODP_TEST_INFO(init_test_odp_init_global_replace_abort),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t init_suites_abort[] = {
- {"Init", NULL, NULL, init_suite_abort},
- ODP_SUITE_INFO_NULL,
-};
-
-static void odp_init_abort(void)
-{
- abort();
-}
-
-int init_main_abort(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- /* prevent default ODP init: */
- odp_cunit_register_global_init(NULL);
- odp_cunit_register_global_term(NULL);
-
- /* run the tests: */
- ret = odp_cunit_register(init_suites_abort);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
-
-/* test ODP global init, with alternate log function */
-void init_test_odp_init_global_replace_log(void)
-{
- int status;
- struct odp_init_t init_data;
- odp_instance_t instance;
-
- memset(&init_data, 0, sizeof(init_data));
- init_data.log_fn = &odp_init_log;
-
- replacement_logging_used = 0;
-
- status = odp_init_global(&instance, &init_data, NULL);
- CU_ASSERT_FATAL(status == 0);
-
- CU_ASSERT_TRUE(replacement_logging_used || ODP_DEBUG_PRINT == 0);
-
- status = odp_term_global(instance);
- CU_ASSERT(status == 0);
-}
-
-odp_testinfo_t init_suite_log[] = {
- ODP_TEST_INFO(init_test_odp_init_global_replace_log),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t init_suites_log[] = {
- {"Init", NULL, NULL, init_suite_log},
- ODP_SUITE_INFO_NULL,
-};
-
-static int odp_init_log(odp_log_level_t level __attribute__((unused)),
- const char *fmt, ...)
-{
- va_list args;
- int r;
-
- /* just set a flag to be sure the replacement fn was used */
- replacement_logging_used = 1;
-
- va_start(args, fmt);
- r = vfprintf(stderr, fmt, args);
- va_end(args);
-
- return r;
-}
-
-int init_main_log(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- /* prevent default ODP init: */
- odp_cunit_register_global_init(NULL);
- odp_cunit_register_global_term(NULL);
-
- /* register the tests: */
- ret = odp_cunit_register(init_suites_log);
-
- /* run the tests: */
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
-
-/* test normal ODP global init */
-void init_test_odp_init_global(void)
-{
- int status;
- odp_instance_t instance;
-
- status = odp_init_global(&instance, NULL, NULL);
- CU_ASSERT_FATAL(status == 0);
-
- status = odp_term_global(instance);
- CU_ASSERT(status == 0);
-}
-
-odp_testinfo_t init_suite_ok[] = {
- ODP_TEST_INFO(init_test_odp_init_global),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t init_suites_ok[] = {
- {"Init", NULL, NULL, init_suite_ok},
- ODP_SUITE_INFO_NULL,
-};
-
-int init_main_ok(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- /* prevent default ODP init: */
- odp_cunit_register_global_init(NULL);
- odp_cunit_register_global_term(NULL);
-
- /* register the tests: */
- ret = odp_cunit_register(init_suites_ok);
-
- /* run the tests: */
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/init/init.h b/test/common_plat/validation/api/init/init.h
deleted file mode 100644
index cad9cf988..000000000
--- a/test/common_plat/validation/api/init/init.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_INIT_H_
-#define _ODP_TEST_INIT_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void init_test_odp_init_global_replace_abort(void);
-void init_test_odp_init_global_replace_log(void);
-void init_test_odp_init_global(void);
-
-/* test arrays: */
-extern odp_testinfo_t init_suite_abort[];
-extern odp_testinfo_t init_suite_log[];
-extern odp_testinfo_t init_suite_ok[];
-
-/* test registry: */
-extern odp_suiteinfo_t init_suites_abort[];
-extern odp_suiteinfo_t init_suites_log[];
-extern odp_suiteinfo_t init_suites_ok[];
-
-/* main test program: */
-int init_main_abort(int argc, char *argv[]);
-int init_main_log(int argc, char *argv[]);
-int init_main_ok(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/init/init_main_abort.c b/test/common_plat/validation/api/init/init_main_abort.c
deleted file mode 100644
index 2e0faafb8..000000000
--- a/test/common_plat/validation/api/init/init_main_abort.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include "init.h"
-
-int main(int argc, char *argv[])
-{
- return init_main_abort(argc, argv);
-}
diff --git a/test/common_plat/validation/api/init/init_main_log.c b/test/common_plat/validation/api/init/init_main_log.c
deleted file mode 100644
index 41dd00d72..000000000
--- a/test/common_plat/validation/api/init/init_main_log.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include "init.h"
-
-int main(int argc, char *argv[])
-{
- return init_main_log(argc, argv);
-}
diff --git a/test/common_plat/validation/api/init/init_main_ok.c b/test/common_plat/validation/api/init/init_main_ok.c
deleted file mode 100644
index 6053ec188..000000000
--- a/test/common_plat/validation/api/init/init_main_ok.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include "init.h"
-
-int main(int argc, char *argv[])
-{
- return init_main_ok(argc, argv);
-}
diff --git a/test/common_plat/validation/api/lock/Makefile.am b/test/common_plat/validation/api/lock/Makefile.am
deleted file mode 100644
index 29993df44..000000000
--- a/test/common_plat/validation/api/lock/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestlock.la
-libtestlock_la_SOURCES = lock.c
-
-test_PROGRAMS = lock_main$(EXEEXT)
-dist_lock_main_SOURCES = lock_main.c
-lock_main_LDADD = libtestlock.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = lock.h
diff --git a/test/common_plat/validation/api/lock/lock.c b/test/common_plat/validation/api/lock/lock.c
deleted file mode 100644
index f8a1d8c01..000000000
--- a/test/common_plat/validation/api/lock/lock.c
+++ /dev/null
@@ -1,1265 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <malloc.h>
-#include <odp_api.h>
-#include <CUnit/Basic.h>
-#include <odp_cunit_common.h>
-#include <unistd.h>
-#include "lock.h"
-
-#define VERBOSE 0
-
-#define MIN_ITERATIONS 1000
-#define MAX_ITERATIONS 30000
-#define ITER_MPLY_FACTOR 3
-
-#define SLOW_BARRIER_DELAY 400
-#define BASE_DELAY 6
-#define MIN_DELAY 1
-
-#define NUM_RESYNC_BARRIERS 100
-
-#define GLOBAL_SHM_NAME "GlobalLockTest"
-
-#define UNUSED __attribute__((__unused__))
-
-typedef __volatile uint32_t volatile_u32_t;
-typedef __volatile uint64_t volatile_u64_t;
-
-typedef struct {
- odp_atomic_u32_t wait_cnt;
-} custom_barrier_t;
-
-typedef struct {
- /* Global variables */
- uint32_t g_num_threads;
- uint32_t g_iterations;
- uint32_t g_verbose;
- uint32_t g_max_num_cores;
-
- volatile_u32_t slow_thread_num;
- volatile_u32_t barrier_cnt1;
- volatile_u32_t barrier_cnt2;
- odp_barrier_t global_barrier;
-
- /* Used to periodically resync within the lock functional tests */
- odp_barrier_t barrier_array[NUM_RESYNC_BARRIERS];
-
- /* Locks */
- odp_spinlock_t global_spinlock;
- odp_spinlock_recursive_t global_recursive_spinlock;
- odp_ticketlock_t global_ticketlock;
- odp_rwlock_t global_rwlock;
- odp_rwlock_recursive_t global_recursive_rwlock;
-
- volatile_u32_t global_lock_owner;
-} global_shared_mem_t;
-
-/* Per-thread memory */
-typedef struct {
- global_shared_mem_t *global_mem;
-
- int thread_id;
- int thread_core;
-
- odp_spinlock_t per_thread_spinlock;
- odp_spinlock_recursive_t per_thread_recursive_spinlock;
- odp_ticketlock_t per_thread_ticketlock;
- odp_rwlock_t per_thread_rwlock;
- odp_rwlock_recursive_t per_thread_recursive_rwlock;
-
- volatile_u64_t delay_counter;
-} per_thread_mem_t;
-
-static odp_shm_t global_shm;
-static global_shared_mem_t *global_mem;
-
-/*
-* Delay a consistent amount of time. Ideally the amount of CPU time taken
-* is linearly proportional to "iterations". The goal is to try to do some
-* work that the compiler optimizer won't optimize away, and also to
-* minimize loads and stores (at least to different memory addresses)
-* so as to not affect or be affected by caching issues. This does NOT have to
-* correlate to a specific number of cpu cycles or be consistent across
-* CPU architectures.
-*/
-static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations)
-{
- volatile_u64_t *counter_ptr;
- uint32_t cnt;
-
- counter_ptr = &per_thread_mem->delay_counter;
-
- for (cnt = 1; cnt <= iterations; cnt++)
- (*counter_ptr)++;
-}
-
-/* Initialise per-thread memory */
-static per_thread_mem_t *thread_init(void)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_shm_t global_shm;
- uint32_t per_thread_mem_len;
-
- per_thread_mem_len = sizeof(per_thread_mem_t);
- per_thread_mem = malloc(per_thread_mem_len);
- memset(per_thread_mem, 0, per_thread_mem_len);
-
- per_thread_mem->delay_counter = 1;
-
- per_thread_mem->thread_id = odp_thread_id();
- per_thread_mem->thread_core = odp_cpu_id();
-
- global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- global_mem = odp_shm_addr(global_shm);
- CU_ASSERT_PTR_NOT_NULL(global_mem);
-
- per_thread_mem->global_mem = global_mem;
-
- return per_thread_mem;
-}
-
-static void thread_finalize(per_thread_mem_t *per_thread_mem)
-{
- free(per_thread_mem);
-}
-
-static void spinlock_api_test(odp_spinlock_t *spinlock)
-{
- odp_spinlock_init(spinlock);
- CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
-
- odp_spinlock_lock(spinlock);
- CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1);
-
- odp_spinlock_unlock(spinlock);
- CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
-
- CU_ASSERT(odp_spinlock_trylock(spinlock) == 1);
-
- CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1);
-
- odp_spinlock_unlock(spinlock);
- CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
-}
-
-static int spinlock_api_tests(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_spinlock_t local_spin_lock;
-
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- spinlock_api_test(&local_spin_lock);
- spinlock_api_test(&per_thread_mem->per_thread_spinlock);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void spinlock_recursive_api_test(odp_spinlock_recursive_t *spinlock)
-{
- odp_spinlock_recursive_init(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
-
- odp_spinlock_recursive_lock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- odp_spinlock_recursive_lock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- odp_spinlock_recursive_unlock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- odp_spinlock_recursive_unlock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
-
- CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- odp_spinlock_recursive_unlock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
-
- odp_spinlock_recursive_unlock(spinlock);
- CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
-}
-
-static int spinlock_recursive_api_tests(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_spinlock_recursive_t local_recursive_spin_lock;
-
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- spinlock_recursive_api_test(&local_recursive_spin_lock);
- spinlock_recursive_api_test(
- &per_thread_mem->per_thread_recursive_spinlock);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void ticketlock_api_test(odp_ticketlock_t *ticketlock)
-{
- odp_ticketlock_init(ticketlock);
- CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
-
- odp_ticketlock_lock(ticketlock);
- CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1);
-
- odp_ticketlock_unlock(ticketlock);
- CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
-
- CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 1);
- CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 0);
- CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1);
-
- odp_ticketlock_unlock(ticketlock);
- CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
-}
-
-static int ticketlock_api_tests(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_ticketlock_t local_ticket_lock;
-
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- ticketlock_api_test(&local_ticket_lock);
- ticketlock_api_test(&per_thread_mem->per_thread_ticketlock);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void rwlock_api_test(odp_rwlock_t *rw_lock)
-{
- int rc = 0;
-
- odp_rwlock_init(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
-
- odp_rwlock_read_lock(rw_lock);
-
- rc = odp_rwlock_read_trylock(rw_lock);
- CU_ASSERT(rc != 0);
- if (rc == 1)
- odp_rwlock_read_unlock(rw_lock);
-
- rc = odp_rwlock_write_trylock(rw_lock);
- CU_ASSERT(rc == 0);
- if (rc == 1)
- odp_rwlock_write_unlock(rw_lock);
-
- odp_rwlock_read_unlock(rw_lock);
-
- rc = odp_rwlock_read_trylock(rw_lock);
- CU_ASSERT(rc != 0);
- if (rc == 1)
- odp_rwlock_read_unlock(rw_lock);
-
- odp_rwlock_write_lock(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */
-
- rc = odp_rwlock_read_trylock(rw_lock);
- CU_ASSERT(rc == 0);
- if (rc == 1)
- odp_rwlock_read_unlock(rw_lock);
-
- rc = odp_rwlock_write_trylock(rw_lock);
- CU_ASSERT(rc == 0);
- if (rc == 1)
- odp_rwlock_write_unlock(rw_lock);
-
- odp_rwlock_write_unlock(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
-
- rc = odp_rwlock_write_trylock(rw_lock);
- CU_ASSERT(rc != 0);
- if (rc == 1)
- odp_rwlock_write_unlock(rw_lock);
-}
-
-static int rwlock_api_tests(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_rwlock_t local_rwlock;
-
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- rwlock_api_test(&local_rwlock);
- rwlock_api_test(&per_thread_mem->per_thread_rwlock);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static void rwlock_recursive_api_test(odp_rwlock_recursive_t *rw_lock)
-{
- int rc;
-
- odp_rwlock_recursive_init(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
-
- odp_rwlock_recursive_read_lock(rw_lock);
- odp_rwlock_recursive_read_lock(rw_lock);
- rc = odp_rwlock_recursive_read_trylock(rw_lock);
- CU_ASSERT(rc == 1);
- rc = odp_rwlock_recursive_write_trylock(rw_lock);
- CU_ASSERT(rc == 0);
-
- odp_rwlock_recursive_read_unlock(rw_lock);
- odp_rwlock_recursive_read_unlock(rw_lock);
- odp_rwlock_recursive_read_unlock(rw_lock);
-
- odp_rwlock_recursive_write_lock(rw_lock);
- odp_rwlock_recursive_write_lock(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */
- rc = odp_rwlock_recursive_read_trylock(rw_lock);
- CU_ASSERT(rc == 0);
- rc = odp_rwlock_recursive_write_trylock(rw_lock);
- CU_ASSERT(rc == 1);
-
- odp_rwlock_recursive_write_unlock(rw_lock);
- odp_rwlock_recursive_write_unlock(rw_lock);
- odp_rwlock_recursive_write_unlock(rw_lock);
- /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
-}
-
-static int rwlock_recursive_api_tests(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- odp_rwlock_recursive_t local_recursive_rwlock;
-
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- rwlock_recursive_api_test(&local_recursive_rwlock);
- rwlock_recursive_api_test(&per_thread_mem->per_thread_recursive_rwlock);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-/*
- * Tests that we do have contention between threads when running.
- * Also adjust the number of iterations to be done (by other tests)
- * so we have a fair chance to see that the tested synchronizer
- * does avoid the race condition.
- */
-static int no_lock_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, current_errs, lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = 0;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = MAX_ITERATIONS / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- /*
- * Tunning the iteration number:
- * Here, we search for an iteration number that guarantees to show
- * race conditions between the odp threads.
- * Iterations is set to ITER_MPLY_FACTOR * cnt where cnt is when
- * the threads start to see "errors" (i.e. effect of other threads
- * running concurrentely without any synchronisation mechanism).
- * In other words, "iterations" is set to ITER_MPLY_FACTOR times the
- * minimum loop count necessary to see a need for synchronisation
- * mechanism.
- * If, later, these "errors" disappear when running other tests up to
- * "iterations" with synchro, the effect of the tested synchro mechanism
- * is likely proven.
- * If we reach "MAX_ITERATIONS", and "iteration" remains zero,
- * it means that we cannot see any race condition between the different
- * running theads (e.g. the OS is not preemptive) and all other tests
- * being passed won't tell much about the functionality of the
- * tested synchro mechanism.
- */
- for (cnt = 1; cnt <= MAX_ITERATIONS; cnt++) {
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
-
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- if (!iterations)
- iterations = cnt;
- }
-
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- thread_delay(per_thread_mem, MIN_DELAY);
-
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- if (!iterations)
- iterations = cnt;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if (global_mem->g_verbose)
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures in %" PRIu32 " iterations\n",
- thread_num,
- per_thread_mem->thread_id,
- per_thread_mem->thread_core,
- sync_failures, iterations);
-
- /* Note that the following CU_ASSERT MAY appear incorrect, but for the
- * no_lock test it should see sync_failures or else there is something
- * wrong with the test methodology or the ODP thread implementation.
- * So this test PASSES only if it sees sync_failures or a single
- * worker was used.
- */
- CU_ASSERT(sync_failures != 0 || global_mem->g_num_threads == 1);
-
- /*
- * set the iterration for the future tests to be far above the
- * contention level
- */
- iterations *= ITER_MPLY_FACTOR;
-
- if (iterations > MAX_ITERATIONS)
- iterations = MAX_ITERATIONS;
- if (iterations < MIN_ITERATIONS)
- iterations = MIN_ITERATIONS;
-
- /*
- * Note that the following statement has race conditions:
- * global_mem->g_iterations should really be an atomic and a TAS
- * function be used. But this would mean that we would be testing
- * synchronisers assuming synchronisers works...
- * If we do not use atomic TAS, we may not get the grand max for
- * all threads, but we are guaranteed to have passed the error
- * threshold, for at least some threads, which is good enough
- */
- if (iterations > global_mem->g_iterations)
- global_mem->g_iterations = iterations;
-
- odp_mb_full();
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int spinlock_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, is_locked_errs, current_errs;
- uint32_t lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = global_mem->g_iterations;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- is_locked_errs = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = iterations / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- for (cnt = 1; cnt <= iterations; cnt++) {
- /* Acquire the shared global lock */
- odp_spinlock_lock(&global_mem->global_spinlock);
-
- /* Make sure we have the lock AND didn't previously own it */
- if (odp_spinlock_is_locked(&global_mem->global_spinlock) != 1)
- is_locked_errs++;
-
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Now set the global_lock_owner to be us, wait a while, and
- * then we see if anyone else has snuck in and changed the
- * global_lock_owner to be themselves
- */
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- /* Release shared lock, and make sure we no longer have it */
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- odp_spinlock_unlock(&global_mem->global_spinlock);
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if ((global_mem->g_verbose) &&
- ((sync_failures != 0) || (is_locked_errs != 0)))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures and %" PRIu32
- " is_locked_errs in %" PRIu32
- " iterations\n", thread_num,
- per_thread_mem->thread_id, per_thread_mem->thread_core,
- sync_failures, is_locked_errs, iterations);
-
- CU_ASSERT(sync_failures == 0);
- CU_ASSERT(is_locked_errs == 0);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int spinlock_recursive_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, recursive_errs, is_locked_errs, current_errs;
- uint32_t lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = global_mem->g_iterations;
-
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- recursive_errs = 0;
- is_locked_errs = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = iterations / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- for (cnt = 1; cnt <= iterations; cnt++) {
- /* Acquire the shared global lock */
- odp_spinlock_recursive_lock(
- &global_mem->global_recursive_spinlock);
-
- /* Make sure we have the lock AND didn't previously own it */
- if (odp_spinlock_recursive_is_locked(
- &global_mem->global_recursive_spinlock) != 1)
- is_locked_errs++;
-
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Now set the global_lock_owner to be us, wait a while, and
- * then we see if anyone else has snuck in and changed the
- * global_lock_owner to be themselves
- */
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- /* Verify that we can acquire the lock recursively */
- odp_spinlock_recursive_lock(
- &global_mem->global_recursive_spinlock);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- recursive_errs++;
- }
-
- /* Release the lock and verify that we still have it*/
- odp_spinlock_recursive_unlock(
- &global_mem->global_recursive_spinlock);
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- recursive_errs++;
- }
-
- /* Release shared lock, and make sure we no longer have it */
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- odp_spinlock_recursive_unlock(
- &global_mem->global_recursive_spinlock);
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if ((global_mem->g_verbose) &&
- (sync_failures != 0 || recursive_errs != 0 || is_locked_errs != 0))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures and %" PRIu32
- " recursive_errs and %" PRIu32
- " is_locked_errs in %" PRIu32
- " iterations\n", thread_num,
- per_thread_mem->thread_id, per_thread_mem->thread_core,
- sync_failures, recursive_errs, is_locked_errs,
- iterations);
-
- CU_ASSERT(sync_failures == 0);
- CU_ASSERT(recursive_errs == 0);
- CU_ASSERT(is_locked_errs == 0);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int ticketlock_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, is_locked_errs, current_errs;
- uint32_t lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = global_mem->g_iterations;
-
- /* Wait here until all of the threads have also reached this point */
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- is_locked_errs = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = iterations / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- for (cnt = 1; cnt <= iterations; cnt++) {
- /* Acquire the shared global lock */
- odp_ticketlock_lock(&global_mem->global_ticketlock);
-
- /* Make sure we have the lock AND didn't previously own it */
- if (odp_ticketlock_is_locked(&global_mem->global_ticketlock)
- != 1)
- is_locked_errs++;
-
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Now set the global_lock_owner to be us, wait a while, and
- * then we see if anyone else has snuck in and changed the
- * global_lock_owner to be themselves
- */
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- /* Release shared lock, and make sure we no longer have it */
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- odp_ticketlock_unlock(&global_mem->global_ticketlock);
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and then rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if ((global_mem->g_verbose) &&
- ((sync_failures != 0) || (is_locked_errs != 0)))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures and %" PRIu32
- " is_locked_errs in %" PRIu32 " iterations\n",
- thread_num,
- per_thread_mem->thread_id, per_thread_mem->thread_core,
- sync_failures, is_locked_errs, iterations);
-
- CU_ASSERT(sync_failures == 0);
- CU_ASSERT(is_locked_errs == 0);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int rwlock_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, current_errs, lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = global_mem->g_iterations;
-
- /* Wait here until all of the threads have also reached this point */
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = iterations / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- for (cnt = 1; cnt <= iterations; cnt++) {
- /* Verify that we can obtain a read lock */
- odp_rwlock_read_lock(&global_mem->global_rwlock);
-
- /* Verify lock is unowned (no writer holds it) */
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Release the read lock */
- odp_rwlock_read_unlock(&global_mem->global_rwlock);
-
- /* Acquire the shared global lock */
- odp_rwlock_write_lock(&global_mem->global_rwlock);
-
- /* Make sure we have lock now AND didn't previously own it */
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Now set the global_lock_owner to be us, wait a while, and
- * then we see if anyone else has snuck in and changed the
- * global_lock_owner to be themselves
- */
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- /* Release shared lock, and make sure we no longer have it */
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- odp_rwlock_write_unlock(&global_mem->global_rwlock);
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and then rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if ((global_mem->g_verbose) && (sync_failures != 0))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures in %" PRIu32 " iterations\n", thread_num,
- per_thread_mem->thread_id,
- per_thread_mem->thread_core,
- sync_failures, iterations);
-
- CU_ASSERT(sync_failures == 0);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-static int rwlock_recursive_functional_test(void *arg UNUSED)
-{
- global_shared_mem_t *global_mem;
- per_thread_mem_t *per_thread_mem;
- uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
- uint32_t sync_failures, recursive_errs, current_errs, lock_owner_delay;
-
- thread_num = odp_cpu_id() + 1;
- per_thread_mem = thread_init();
- global_mem = per_thread_mem->global_mem;
- iterations = global_mem->g_iterations;
-
- /* Wait here until all of the threads have also reached this point */
- odp_barrier_wait(&global_mem->global_barrier);
-
- sync_failures = 0;
- recursive_errs = 0;
- current_errs = 0;
- rs_idx = 0;
- resync_cnt = iterations / NUM_RESYNC_BARRIERS;
- lock_owner_delay = BASE_DELAY;
-
- for (cnt = 1; cnt <= iterations; cnt++) {
- /* Verify that we can obtain a read lock */
- odp_rwlock_recursive_read_lock(
- &global_mem->global_recursive_rwlock);
-
- /* Verify lock is unowned (no writer holds it) */
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Verify we can get read lock recursively */
- odp_rwlock_recursive_read_lock(
- &global_mem->global_recursive_rwlock);
-
- /* Verify lock is unowned (no writer holds it) */
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Release the read lock */
- odp_rwlock_recursive_read_unlock(
- &global_mem->global_recursive_rwlock);
- odp_rwlock_recursive_read_unlock(
- &global_mem->global_recursive_rwlock);
-
- /* Acquire the shared global lock */
- odp_rwlock_recursive_write_lock(
- &global_mem->global_recursive_rwlock);
-
- /* Make sure we have lock now AND didn't previously own it */
- if (global_mem->global_lock_owner != 0) {
- current_errs++;
- sync_failures++;
- }
-
- /* Now set the global_lock_owner to be us, wait a while, and
- * then we see if anyone else has snuck in and changed the
- * global_lock_owner to be themselves
- */
- global_mem->global_lock_owner = thread_num;
- odp_mb_full();
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- /* Acquire it again and verify we still own it */
- odp_rwlock_recursive_write_lock(
- &global_mem->global_recursive_rwlock);
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- recursive_errs++;
- }
-
- /* Release the recursive lock and make sure we still own it */
- odp_rwlock_recursive_write_unlock(
- &global_mem->global_recursive_rwlock);
- thread_delay(per_thread_mem, lock_owner_delay);
- if (global_mem->global_lock_owner != thread_num) {
- current_errs++;
- recursive_errs++;
- }
-
- /* Release shared lock, and make sure we no longer have it */
- global_mem->global_lock_owner = 0;
- odp_mb_full();
- odp_rwlock_recursive_write_unlock(
- &global_mem->global_recursive_rwlock);
- if (global_mem->global_lock_owner == thread_num) {
- current_errs++;
- sync_failures++;
- }
-
- if (current_errs == 0)
- lock_owner_delay++;
-
- /* Wait a small amount of time and then rerun the test */
- thread_delay(per_thread_mem, BASE_DELAY);
-
- /* Try to resync all of the threads to increase contention */
- if ((rs_idx < NUM_RESYNC_BARRIERS) &&
- ((cnt % resync_cnt) == (resync_cnt - 1)))
- odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
- }
-
- if ((global_mem->g_verbose) && (sync_failures != 0))
- printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
- " sync_failures and %" PRIu32
- " recursive_errs in %" PRIu32
- " iterations\n", thread_num,
- per_thread_mem->thread_id,
- per_thread_mem->thread_core,
- sync_failures, recursive_errs, iterations);
-
- CU_ASSERT(sync_failures == 0);
- CU_ASSERT(recursive_errs == 0);
-
- thread_finalize(per_thread_mem);
-
- return CU_get_number_of_failures();
-}
-
-/* Thread-unsafe tests */
-void lock_test_no_lock_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(no_lock_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t lock_suite_no_locking[] = {
- ODP_TEST_INFO(lock_test_no_lock_functional), /* must be first */
- ODP_TEST_INFO_NULL
-};
-
-/* Spin lock tests */
-void lock_test_spinlock_api(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(spinlock_api_tests, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_spinlock_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_spinlock_init(&global_mem->global_spinlock);
- odp_cunit_thread_create(spinlock_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_spinlock_recursive_api(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(spinlock_recursive_api_tests, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_spinlock_recursive_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_spinlock_recursive_init(&global_mem->global_recursive_spinlock);
- odp_cunit_thread_create(spinlock_recursive_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t lock_suite_spinlock[] = {
- ODP_TEST_INFO(lock_test_spinlock_api),
- ODP_TEST_INFO(lock_test_spinlock_functional),
- ODP_TEST_INFO_NULL
-};
-
-odp_testinfo_t lock_suite_spinlock_recursive[] = {
- ODP_TEST_INFO(lock_test_spinlock_recursive_api),
- ODP_TEST_INFO(lock_test_spinlock_recursive_functional),
- ODP_TEST_INFO_NULL
-};
-
-/* Ticket lock tests */
-void lock_test_ticketlock_api(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(ticketlock_api_tests, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_ticketlock_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_ticketlock_init(&global_mem->global_ticketlock);
-
- odp_cunit_thread_create(ticketlock_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t lock_suite_ticketlock[] = {
- ODP_TEST_INFO(lock_test_ticketlock_api),
- ODP_TEST_INFO(lock_test_ticketlock_functional),
- ODP_TEST_INFO_NULL
-};
-
-/* RW lock tests */
-void lock_test_rwlock_api(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(rwlock_api_tests, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_rwlock_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_rwlock_init(&global_mem->global_rwlock);
- odp_cunit_thread_create(rwlock_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t lock_suite_rwlock[] = {
- ODP_TEST_INFO(lock_test_rwlock_api),
- ODP_TEST_INFO(lock_test_rwlock_functional),
- ODP_TEST_INFO_NULL
-};
-
-void lock_test_rwlock_recursive_api(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_cunit_thread_create(rwlock_recursive_api_tests, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-void lock_test_rwlock_recursive_functional(void)
-{
- pthrd_arg arg;
-
- arg.numthrds = global_mem->g_num_threads;
- odp_rwlock_recursive_init(&global_mem->global_recursive_rwlock);
- odp_cunit_thread_create(rwlock_recursive_functional_test, &arg);
- odp_cunit_thread_exit(&arg);
-}
-
-odp_testinfo_t lock_suite_rwlock_recursive[] = {
- ODP_TEST_INFO(lock_test_rwlock_recursive_api),
- ODP_TEST_INFO(lock_test_rwlock_recursive_functional),
- ODP_TEST_INFO_NULL
-};
-
-int lock_suite_init(void)
-{
- uint32_t num_threads, idx;
-
- num_threads = global_mem->g_num_threads;
- odp_barrier_init(&global_mem->global_barrier, num_threads);
- for (idx = 0; idx < NUM_RESYNC_BARRIERS; idx++)
- odp_barrier_init(&global_mem->barrier_array[idx], num_threads);
-
- return 0;
-}
-
-int lock_init(odp_instance_t *inst)
-{
- uint32_t workers_count, max_threads;
- int ret = 0;
- odp_cpumask_t mask;
-
- if (0 != odp_init_global(inst, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return -1;
- }
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return -1;
- }
-
- global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
- sizeof(global_shared_mem_t), 64,
- ODP_SHM_SW_ONLY);
- if (ODP_SHM_INVALID == global_shm) {
- fprintf(stderr, "Unable reserve memory for global_shm\n");
- return -1;
- }
-
- global_mem = odp_shm_addr(global_shm);
- memset(global_mem, 0, sizeof(global_shared_mem_t));
-
- global_mem->g_num_threads = MAX_WORKERS;
- global_mem->g_iterations = 0; /* tuned by first test */
- global_mem->g_verbose = VERBOSE;
-
- workers_count = odp_cpumask_default_worker(&mask, 0);
-
- max_threads = (workers_count >= MAX_WORKERS) ?
- MAX_WORKERS : workers_count;
-
- if (max_threads < global_mem->g_num_threads) {
- printf("Requested num of threads is too large\n");
- printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
- global_mem->g_num_threads,
- max_threads);
- global_mem->g_num_threads = max_threads;
- }
-
- printf("Num of threads used = %" PRIu32 "\n",
- global_mem->g_num_threads);
-
- return ret;
-}
-
-int lock_term(odp_instance_t inst)
-{
- odp_shm_t shm;
-
- shm = odp_shm_lookup(GLOBAL_SHM_NAME);
- if (0 != odp_shm_free(shm)) {
- fprintf(stderr, "error: odp_shm_free() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return -1;
- }
-
- if (0 != odp_term_global(inst)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-odp_suiteinfo_t lock_suites[] = {
- {"nolocking", lock_suite_init, NULL,
- lock_suite_no_locking}, /* must be first */
- {"spinlock", lock_suite_init, NULL,
- lock_suite_spinlock},
- {"spinlock_recursive", lock_suite_init, NULL,
- lock_suite_spinlock_recursive},
- {"ticketlock", lock_suite_init, NULL,
- lock_suite_ticketlock},
- {"rwlock", lock_suite_init, NULL,
- lock_suite_rwlock},
- {"rwlock_recursive", lock_suite_init, NULL,
- lock_suite_rwlock_recursive},
- ODP_SUITE_INFO_NULL
-};
-
-int lock_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- odp_cunit_register_global_init(lock_init);
- odp_cunit_register_global_term(lock_term);
-
- ret = odp_cunit_register(lock_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/lock/lock.h b/test/common_plat/validation/api/lock/lock.h
deleted file mode 100644
index e0f49728b..000000000
--- a/test/common_plat/validation/api/lock/lock.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_LOCK_H_
-#define _ODP_TEST_LOCK_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void lock_test_no_lock_functional(void);
-void lock_test_spinlock_api(void);
-void lock_test_spinlock_functional(void);
-void lock_test_spinlock_recursive_api(void);
-void lock_test_spinlock_recursive_functional(void);
-void lock_test_ticketlock_api(void);
-void lock_test_ticketlock_functional(void);
-void lock_test_rwlock_api(void);
-void lock_test_rwlock_functional(void);
-void lock_test_rwlock_recursive_api(void);
-void lock_test_rwlock_recursive_functional(void);
-
-/* test arrays: */
-extern odp_testinfo_t lock_suite_no_locking[];
-extern odp_testinfo_t lock_suite_spinlock[];
-extern odp_testinfo_t lock_suite_spinlock_recursive[];
-extern odp_testinfo_t lock_suite_ticketlock[];
-extern odp_testinfo_t lock_suite_rwlock[];
-extern odp_testinfo_t lock_suite_rwlock_recursive[];
-
-/* test array init/term functions: */
-int lock_suite_init(void);
-
-/* test registry: */
-extern odp_suiteinfo_t lock_suites[];
-
-/* executable init/term functions: */
-int lock_init(odp_instance_t *inst);
-int lock_term(odp_instance_t inst);
-
-/* main test program: */
-int lock_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/lock/lock_main.c b/test/common_plat/validation/api/lock/lock_main.c
deleted file mode 100644
index 5a30f02b4..000000000
--- a/test/common_plat/validation/api/lock/lock_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "lock.h"
-
-int main(int argc, char *argv[])
-{
- return lock_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/packet/Makefile.am b/test/common_plat/validation/api/packet/Makefile.am
deleted file mode 100644
index d8ebc1a23..000000000
--- a/test/common_plat/validation/api/packet/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestpacket.la
-libtestpacket_la_SOURCES = packet.c
-
-test_PROGRAMS = packet_main$(EXEEXT)
-dist_packet_main_SOURCES = packet_main.c
-packet_main_LDADD = libtestpacket.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = packet.h
diff --git a/test/common_plat/validation/api/packet/packet.c b/test/common_plat/validation/api/packet/packet.c
deleted file mode 100644
index 284aaeb5a..000000000
--- a/test/common_plat/validation/api/packet/packet.c
+++ /dev/null
@@ -1,2451 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdlib.h>
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "packet.h"
-
-#define PACKET_BUF_LEN ODP_CONFIG_PACKET_SEG_LEN_MIN
-/* Reserve some tailroom for tests */
-#define PACKET_TAILROOM_RESERVE 4
-/* Number of packets in the test packet pool */
-#define PACKET_POOL_NUM 300
-
-static odp_pool_t packet_pool, packet_pool_no_uarea, packet_pool_double_uarea;
-static uint32_t packet_len;
-
-static uint32_t segmented_packet_len;
-static odp_bool_t segmentation_supported = true;
-
-odp_packet_t test_packet, segmented_test_packet;
-
-static struct udata_struct {
- uint64_t u64;
- uint32_t u32;
- char str[10];
-} test_packet_udata = {
- 123456,
- 789912,
- "abcdefg",
-};
-
-#define packet_compare_offset(pkt1, off1, pkt2, off2, len) \
- _packet_compare_offset((pkt1), (off1), (pkt2), (off2), (len), __LINE__)
-
-#define packet_compare_data(pkt1, pkt2) \
- _packet_compare_data((pkt1), (pkt2), __LINE__)
-
-static void _packet_compare_data(odp_packet_t pkt1, odp_packet_t pkt2,
- int line)
-{
- uint32_t len = odp_packet_len(pkt1);
- uint32_t offset = 0;
- uint32_t seglen1, seglen2, cmplen;
- int ret;
-
- CU_ASSERT_FATAL(len == odp_packet_len(pkt2));
-
- while (len > 0) {
- void *pkt1map = odp_packet_offset(pkt1, offset, &seglen1, NULL);
- void *pkt2map = odp_packet_offset(pkt2, offset, &seglen2, NULL);
-
- CU_ASSERT_PTR_NOT_NULL_FATAL(pkt1map);
- CU_ASSERT_PTR_NOT_NULL_FATAL(pkt2map);
- cmplen = seglen1 < seglen2 ? seglen1 : seglen2;
- ret = memcmp(pkt1map, pkt2map, cmplen);
-
- if (ret) {
- printf("\ncompare_data failed: line %i, offset %"
- PRIu32 "\n", line, offset);
- }
-
- CU_ASSERT(ret == 0);
-
- offset += cmplen;
- len -= cmplen;
- }
-}
-
-static int fill_data_forward(odp_packet_t pkt, uint32_t offset, uint32_t len,
- uint32_t *cur_data)
-{
- uint8_t buf[len];
- uint32_t i, data;
-
- data = *cur_data;
-
- for (i = 0; i < len; i++)
- buf[i] = data++;
-
- *cur_data = data;
-
- return odp_packet_copy_from_mem(pkt, offset, len, buf);
-}
-
-static int fill_data_backward(odp_packet_t pkt, uint32_t offset, uint32_t len,
- uint32_t *cur_data)
-{
- uint8_t buf[len];
- uint32_t i, data;
-
- data = *cur_data;
-
- for (i = 0; i < len; i++)
- buf[len - i - 1] = data++;
-
- *cur_data = data;
-
- return odp_packet_copy_from_mem(pkt, offset, len, buf);
-}
-
-int packet_suite_init(void)
-{
- odp_pool_param_t params;
- odp_pool_capability_t capa;
- struct udata_struct *udat;
- uint32_t udat_size;
- uint8_t data = 0;
- uint32_t i;
- uint32_t num = PACKET_POOL_NUM;
-
- if (odp_pool_capability(&capa) < 0) {
- printf("pool_capability failed\n");
- return -1;
- }
- if (capa.pkt.max_segs_per_pkt == 0)
- capa.pkt.max_segs_per_pkt = 10;
-
- /* Pick a typical packet size and decrement it to the single segment
- * limit if needed (min_seg_len maybe equal to max_len
- * on some systems). */
- packet_len = 512;
- while (packet_len > (capa.pkt.min_seg_len - PACKET_TAILROOM_RESERVE))
- packet_len--;
-
- if (capa.pkt.max_len) {
- segmented_packet_len = capa.pkt.max_len;
- } else {
- segmented_packet_len = capa.pkt.min_seg_len *
- capa.pkt.max_segs_per_pkt;
- }
- if (capa.pkt.max_num != 0 && capa.pkt.max_num < num)
- num = capa.pkt.max_num;
-
- odp_pool_param_init(&params);
-
- params.type = ODP_POOL_PACKET;
- params.pkt.seg_len = capa.pkt.min_seg_len;
- params.pkt.len = capa.pkt.min_seg_len;
- params.pkt.num = num;
- params.pkt.uarea_size = sizeof(struct udata_struct);
-
- packet_pool = odp_pool_create("packet_pool", &params);
- if (packet_pool == ODP_POOL_INVALID) {
- printf("pool_create failed: 1\n");
- return -1;
- }
-
- params.pkt.uarea_size = 0;
- packet_pool_no_uarea = odp_pool_create("packet_pool_no_uarea",
- &params);
- if (packet_pool_no_uarea == ODP_POOL_INVALID) {
- odp_pool_destroy(packet_pool);
- printf("pool_create failed: 2\n");
- return -1;
- }
-
- params.pkt.uarea_size = 2 * sizeof(struct udata_struct);
- packet_pool_double_uarea = odp_pool_create("packet_pool_double_uarea",
- &params);
-
- if (packet_pool_double_uarea == ODP_POOL_INVALID) {
- odp_pool_destroy(packet_pool_no_uarea);
- odp_pool_destroy(packet_pool);
- printf("pool_create failed: 3\n");
- return -1;
- }
-
- test_packet = odp_packet_alloc(packet_pool, packet_len);
-
- for (i = 0; i < packet_len; i++) {
- odp_packet_copy_from_mem(test_packet, i, 1, &data);
- data++;
- }
-
- /* Try to allocate the largest possible packet to see
- * if segmentation is supported */
- do {
- segmented_test_packet = odp_packet_alloc(packet_pool,
- segmented_packet_len);
- if (segmented_test_packet == ODP_PACKET_INVALID)
- segmented_packet_len -= capa.pkt.min_seg_len;
- } while (segmented_test_packet == ODP_PACKET_INVALID);
-
- if (odp_packet_is_valid(test_packet) == 0 ||
- odp_packet_is_valid(segmented_test_packet) == 0) {
- printf("packet_is_valid failed\n");
- return -1;
- }
-
- segmentation_supported = odp_packet_is_segmented(segmented_test_packet);
-
- data = 0;
- for (i = 0; i < segmented_packet_len; i++) {
- odp_packet_copy_from_mem(segmented_test_packet, i, 1, &data);
- data++;
- }
-
- udat = odp_packet_user_area(test_packet);
- udat_size = odp_packet_user_area_size(test_packet);
- if (!udat || udat_size != sizeof(struct udata_struct)) {
- printf("packet_user_area failed: 1\n");
- return -1;
- }
-
- odp_pool_print(packet_pool);
- memcpy(udat, &test_packet_udata, sizeof(struct udata_struct));
-
- udat = odp_packet_user_area(segmented_test_packet);
- udat_size = odp_packet_user_area_size(segmented_test_packet);
- if (udat == NULL || udat_size != sizeof(struct udata_struct)) {
- printf("packet_user_area failed: 2\n");
- return -1;
- }
-
- memcpy(udat, &test_packet_udata, sizeof(struct udata_struct));
-
- return 0;
-}
-
-int packet_suite_term(void)
-{
- odp_packet_free(test_packet);
- odp_packet_free(segmented_test_packet);
-
- if (odp_pool_destroy(packet_pool_double_uarea) != 0 ||
- odp_pool_destroy(packet_pool_no_uarea) != 0 ||
- odp_pool_destroy(packet_pool) != 0)
- return -1;
-
- return 0;
-}
-
-void packet_test_alloc_free(void)
-{
- odp_pool_t pool;
- odp_packet_t packet;
- odp_pool_param_t params;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- odp_pool_param_init(&params);
-
- params.type = ODP_POOL_PACKET;
- params.pkt.seg_len = capa.pkt.min_seg_len;
- params.pkt.len = capa.pkt.min_seg_len;
- params.pkt.num = 1;
-
- pool = odp_pool_create("packet_pool_alloc", &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- /* Allocate the only buffer from the pool */
- packet = odp_packet_alloc(pool, packet_len);
- CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(packet) == packet_len);
- CU_ASSERT(odp_event_type(odp_packet_to_event(packet)) ==
- ODP_EVENT_PACKET);
- CU_ASSERT(odp_packet_to_u64(packet) !=
- odp_packet_to_u64(ODP_PACKET_INVALID));
-
- /* Pool should have only one packet */
- CU_ASSERT_FATAL(odp_packet_alloc(pool, packet_len)
- == ODP_PACKET_INVALID);
-
- odp_packet_free(packet);
-
- /* Check that the buffer was returned back to the pool */
- packet = odp_packet_alloc(pool, packet_len);
- CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(packet) == packet_len);
-
- odp_packet_free(packet);
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-/* Wrapper to call odp_packet_alloc_multi multiple times until
- * either no mure buffers are returned, or num buffers were alloced */
-static int packet_alloc_multi(odp_pool_t pool, uint32_t pkt_len,
- odp_packet_t pkt[], int num)
-{
- int ret, total = 0;
-
- do {
- ret = odp_packet_alloc_multi(pool, pkt_len, pkt + total,
- num - total);
- CU_ASSERT(ret >= 0);
- CU_ASSERT(ret <= num - total);
- total += ret;
- } while (total < num && ret);
-
- return total;
-}
-
-void packet_test_alloc_free_multi(void)
-{
- const int num_pkt = 2;
- odp_pool_t pool[2];
- int i, ret;
- odp_packet_t packet[2 * num_pkt + 1];
- odp_packet_t inval_pkt[num_pkt];
- odp_pool_param_t params;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- odp_pool_param_init(&params);
-
- params.type = ODP_POOL_PACKET;
- params.pkt.seg_len = capa.pkt.min_seg_len;
- params.pkt.len = capa.pkt.min_seg_len;
- params.pkt.num = num_pkt;
-
- pool[0] = odp_pool_create("packet_pool_alloc_multi_0", &params);
- pool[1] = odp_pool_create("packet_pool_alloc_multi_1", &params);
- CU_ASSERT_FATAL(pool[0] != ODP_POOL_INVALID);
- CU_ASSERT_FATAL(pool[1] != ODP_POOL_INVALID);
-
- /* Allocate all the packets from the pools */
-
- ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt + 1);
- CU_ASSERT_FATAL(ret == num_pkt);
- ret = packet_alloc_multi(pool[1], packet_len,
- &packet[num_pkt], num_pkt + 1);
- CU_ASSERT_FATAL(ret == num_pkt);
-
- for (i = 0; i < 2 * num_pkt; ++i) {
- CU_ASSERT(odp_packet_len(packet[i]) == packet_len);
- CU_ASSERT(odp_event_type(odp_packet_to_event(packet[i])) ==
- ODP_EVENT_PACKET);
- CU_ASSERT(odp_packet_to_u64(packet[i]) !=
- odp_packet_to_u64(ODP_PACKET_INVALID));
- }
-
- /* Pools should have no more packets */
- ret = odp_packet_alloc_multi(pool[0], packet_len, inval_pkt, num_pkt);
- CU_ASSERT(ret == 0);
- ret = odp_packet_alloc_multi(pool[1], packet_len, inval_pkt, num_pkt);
- CU_ASSERT(ret == 0);
-
- /* Free all packets from all pools at once */
- odp_packet_free_multi(packet, 2 * num_pkt);
-
- /* Check that all the packets were returned back to their pools */
- ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt);
- CU_ASSERT(ret);
- ret = packet_alloc_multi(pool[1], packet_len,
- &packet[num_pkt], num_pkt);
- CU_ASSERT(ret);
-
- for (i = 0; i < 2 * num_pkt; ++i) {
- CU_ASSERT_FATAL(packet[i] != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(packet[i]) == packet_len);
- }
- odp_packet_free_multi(packet, 2 * num_pkt);
- CU_ASSERT(odp_pool_destroy(pool[0]) == 0);
- CU_ASSERT(odp_pool_destroy(pool[1]) == 0);
-}
-
-void packet_test_alloc_segmented(void)
-{
- const int num = 5;
- odp_packet_t pkts[num];
- odp_packet_t pkt;
- uint32_t max_len;
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_pool_capability_t capa;
- int ret, i, num_alloc;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
- if (capa.pkt.max_segs_per_pkt == 0)
- capa.pkt.max_segs_per_pkt = 10;
-
- if (capa.pkt.max_len)
- max_len = capa.pkt.max_len;
- else
- max_len = capa.pkt.min_seg_len * capa.pkt.max_segs_per_pkt;
-
- odp_pool_param_init(&params);
-
- params.type = ODP_POOL_PACKET;
- params.pkt.seg_len = capa.pkt.min_seg_len;
- params.pkt.len = max_len;
-
- /* Ensure that 'num' segmented packets can be allocated */
- params.pkt.num = num * capa.pkt.max_segs_per_pkt;
-
- pool = odp_pool_create("pool_alloc_segmented", &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- /* Less than max len allocs */
- pkt = odp_packet_alloc(pool, max_len / 2);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(pkt) == max_len / 2);
-
- odp_packet_free(pkt);
-
- num_alloc = 0;
- for (i = 0; i < num; i++) {
- ret = odp_packet_alloc_multi(pool, max_len / 2,
- &pkts[num_alloc], num - num_alloc);
- CU_ASSERT_FATAL(ret >= 0);
- num_alloc += ret;
- if (num_alloc >= num)
- break;
- }
-
- CU_ASSERT(num_alloc == num);
-
- for (i = 0; i < num_alloc; i++)
- CU_ASSERT(odp_packet_len(pkts[i]) == max_len / 2);
-
- odp_packet_free_multi(pkts, num_alloc);
-
- /* Max len allocs */
- pkt = odp_packet_alloc(pool, max_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(pkt) == max_len);
-
- odp_packet_free(pkt);
-
- num_alloc = 0;
- for (i = 0; i < num; i++) {
- ret = odp_packet_alloc_multi(pool, max_len,
- &pkts[num_alloc], num - num_alloc);
- CU_ASSERT_FATAL(ret >= 0);
- num_alloc += ret;
- if (num_alloc >= num)
- break;
- }
-
- CU_ASSERT(num_alloc == num);
-
- for (i = 0; i < num_alloc; i++)
- CU_ASSERT(odp_packet_len(pkts[i]) == max_len);
-
- odp_packet_free_multi(pkts, num_alloc);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_event_conversion(void)
-{
- odp_packet_t pkt = test_packet;
- odp_packet_t tmp_pkt;
- odp_event_t ev;
-
- ev = odp_packet_to_event(pkt);
- CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
- CU_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
-
- tmp_pkt = odp_packet_from_event(ev);
- CU_ASSERT_FATAL(tmp_pkt != ODP_PACKET_INVALID);
- CU_ASSERT(tmp_pkt == pkt);
- packet_compare_data(tmp_pkt, pkt);
-}
-
-void packet_test_basic_metadata(void)
-{
- odp_packet_t pkt = test_packet;
- odp_time_t ts;
-
- CU_ASSERT_PTR_NOT_NULL(odp_packet_head(pkt));
- CU_ASSERT_PTR_NOT_NULL(odp_packet_data(pkt));
-
- CU_ASSERT(odp_packet_pool(pkt) != ODP_POOL_INVALID);
- /* Packet was allocated by application so shouldn't have valid pktio. */
- CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID);
- CU_ASSERT(odp_packet_input_index(pkt) < 0);
-
- odp_packet_flow_hash_set(pkt, UINT32_MAX);
- CU_ASSERT(odp_packet_has_flow_hash(pkt));
- CU_ASSERT(odp_packet_flow_hash(pkt) == UINT32_MAX);
- odp_packet_has_flow_hash_clr(pkt);
- CU_ASSERT(!odp_packet_has_flow_hash(pkt));
-
- ts = odp_time_global();
- odp_packet_ts_set(pkt, ts);
- CU_ASSERT_FATAL(odp_packet_has_ts(pkt));
- CU_ASSERT(!odp_time_cmp(ts, odp_packet_ts(pkt)));
- odp_packet_has_ts_clr(pkt);
- CU_ASSERT(!odp_packet_has_ts(pkt));
-}
-
-void packet_test_length(void)
-{
- odp_packet_t pkt = test_packet;
- uint32_t buf_len, headroom, tailroom;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- buf_len = odp_packet_buf_len(pkt);
- headroom = odp_packet_headroom(pkt);
- tailroom = odp_packet_tailroom(pkt);
-
- CU_ASSERT(odp_packet_len(pkt) == packet_len);
- CU_ASSERT(headroom >= capa.pkt.min_headroom);
- CU_ASSERT(tailroom >= capa.pkt.min_tailroom);
-
- CU_ASSERT(buf_len >= packet_len + headroom + tailroom);
-}
-
-void packet_test_prefetch(void)
-{
- odp_packet_prefetch(test_packet, 0, odp_packet_len(test_packet));
- CU_PASS();
-}
-
-void packet_test_debug(void)
-{
- CU_ASSERT(odp_packet_is_valid(test_packet) == 1);
- odp_packet_print(test_packet);
-}
-
-void packet_test_context(void)
-{
- odp_packet_t pkt = test_packet;
- char ptr_test_value = 2;
- void *prev_ptr;
- struct udata_struct *udat;
-
- prev_ptr = odp_packet_user_ptr(pkt);
- odp_packet_user_ptr_set(pkt, &ptr_test_value);
- CU_ASSERT(odp_packet_user_ptr(pkt) == &ptr_test_value);
- odp_packet_user_ptr_set(pkt, prev_ptr);
-
- udat = odp_packet_user_area(pkt);
- CU_ASSERT_PTR_NOT_NULL(udat);
- CU_ASSERT(odp_packet_user_area_size(pkt) ==
- sizeof(struct udata_struct));
- CU_ASSERT(memcmp(udat, &test_packet_udata, sizeof(struct udata_struct))
- == 0);
-
- odp_packet_reset(pkt, packet_len);
-}
-
-void packet_test_layer_offsets(void)
-{
- odp_packet_t pkt = test_packet;
- uint8_t *l2_addr, *l3_addr, *l4_addr;
- uint32_t seg_len;
- const uint32_t l2_off = 2;
- const uint32_t l3_off = l2_off + 14;
- const uint32_t l4_off = l3_off + 14;
- int ret;
-
- /* Set offsets to the same value */
- ret = odp_packet_l2_offset_set(pkt, l2_off);
- CU_ASSERT(ret == 0);
- ret = odp_packet_l3_offset_set(pkt, l2_off);
- CU_ASSERT(ret == 0);
- ret = odp_packet_l4_offset_set(pkt, l2_off);
- CU_ASSERT(ret == 0);
-
- /* Addresses should be the same */
- l2_addr = odp_packet_l2_ptr(pkt, &seg_len);
- CU_ASSERT(seg_len != 0);
- l3_addr = odp_packet_l3_ptr(pkt, &seg_len);
- CU_ASSERT(seg_len != 0);
- l4_addr = odp_packet_l4_ptr(pkt, &seg_len);
- CU_ASSERT(seg_len != 0);
- CU_ASSERT_PTR_NOT_NULL(l2_addr);
- CU_ASSERT(l2_addr == l3_addr);
- CU_ASSERT(l2_addr == l4_addr);
-
- /* Set offsets to the different values */
- odp_packet_l2_offset_set(pkt, l2_off);
- CU_ASSERT(odp_packet_l2_offset(pkt) == l2_off);
- odp_packet_l3_offset_set(pkt, l3_off);
- CU_ASSERT(odp_packet_l3_offset(pkt) == l3_off);
- odp_packet_l4_offset_set(pkt, l4_off);
- CU_ASSERT(odp_packet_l4_offset(pkt) == l4_off);
-
- /* Addresses should not be the same */
- l2_addr = odp_packet_l2_ptr(pkt, NULL);
- CU_ASSERT_PTR_NOT_NULL(l2_addr);
- l3_addr = odp_packet_l3_ptr(pkt, NULL);
- CU_ASSERT_PTR_NOT_NULL(l3_addr);
- l4_addr = odp_packet_l4_ptr(pkt, NULL);
- CU_ASSERT_PTR_NOT_NULL(l4_addr);
-
- CU_ASSERT(l2_addr != l3_addr);
- CU_ASSERT(l2_addr != l4_addr);
- CU_ASSERT(l3_addr != l4_addr);
-}
-
-static void _verify_headroom_shift(odp_packet_t *pkt,
- int shift)
-{
- uint32_t room = odp_packet_headroom(*pkt);
- uint32_t seg_data_len = odp_packet_seg_len(*pkt);
- uint32_t pkt_data_len = odp_packet_len(*pkt);
- void *data;
- char *data_orig = odp_packet_data(*pkt);
- char *head_orig = odp_packet_head(*pkt);
- uint32_t seg_len;
- int extended, rc;
-
- if (shift >= 0) {
- if ((uint32_t)abs(shift) <= room) {
- data = odp_packet_push_head(*pkt, shift);
- extended = 0;
- } else {
- rc = odp_packet_extend_head(pkt, shift,
- &data, &seg_len);
- extended = 1;
- }
- } else {
- if ((uint32_t)abs(shift) <= seg_data_len) {
- data = odp_packet_pull_head(*pkt, -shift);
- extended = 0;
- } else {
- rc = odp_packet_trunc_head(pkt, -shift,
- &data, &seg_len);
- extended = 1;
- }
- }
-
- CU_ASSERT_PTR_NOT_NULL(data);
- if (extended) {
- CU_ASSERT(rc >= 0);
- if (shift >= 0) {
- CU_ASSERT(odp_packet_seg_len(*pkt) == shift - room);
- } else {
- CU_ASSERT(odp_packet_headroom(*pkt) >=
- (uint32_t)abs(shift) - seg_data_len);
- }
- CU_ASSERT(odp_packet_head(*pkt) != head_orig);
- } else {
- CU_ASSERT(odp_packet_headroom(*pkt) == room - shift);
- CU_ASSERT(odp_packet_seg_len(*pkt) == seg_data_len + shift);
- CU_ASSERT(data == data_orig - shift);
- CU_ASSERT(odp_packet_head(*pkt) == head_orig);
- }
-
- CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift);
- CU_ASSERT(odp_packet_data(*pkt) == data);
-}
-
-void packet_test_headroom(void)
-{
- odp_packet_t pkt = odp_packet_copy(test_packet,
- odp_packet_pool(test_packet));
- uint32_t room;
- uint32_t seg_data_len;
- uint32_t push_val, pull_val;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- room = odp_packet_headroom(pkt);
-
- CU_ASSERT(room >= capa.pkt.min_headroom);
-
- seg_data_len = odp_packet_seg_len(pkt);
- CU_ASSERT(seg_data_len >= 1);
-
- pull_val = seg_data_len / 2;
- push_val = room;
-
- _verify_headroom_shift(&pkt, -pull_val);
- _verify_headroom_shift(&pkt, push_val + pull_val);
- _verify_headroom_shift(&pkt, -push_val);
- _verify_headroom_shift(&pkt, 0);
-
- if (segmentation_supported) {
- push_val = room * 2;
- _verify_headroom_shift(&pkt, push_val);
- _verify_headroom_shift(&pkt, 0);
- _verify_headroom_shift(&pkt, -push_val);
- }
-
- odp_packet_free(pkt);
-}
-
-static void _verify_tailroom_shift(odp_packet_t *pkt,
- int shift)
-{
- odp_packet_seg_t seg;
- uint32_t room;
- uint32_t seg_data_len, pkt_data_len, seg_len;
- void *tail;
- char *tail_orig;
- int extended, rc;
-
- room = odp_packet_tailroom(*pkt);
- pkt_data_len = odp_packet_len(*pkt);
- tail_orig = odp_packet_tail(*pkt);
-
- seg = odp_packet_last_seg(*pkt);
- CU_ASSERT(seg != ODP_PACKET_SEG_INVALID);
- seg_data_len = odp_packet_seg_data_len(*pkt, seg);
-
- if (shift >= 0) {
- uint32_t l2_off, l3_off, l4_off;
-
- l2_off = odp_packet_l2_offset(*pkt);
- l3_off = odp_packet_l3_offset(*pkt);
- l4_off = odp_packet_l4_offset(*pkt);
-
- if ((uint32_t)abs(shift) <= room) {
- tail = odp_packet_push_tail(*pkt, shift);
- extended = 0;
- } else {
- rc = odp_packet_extend_tail(pkt, shift,
- &tail, &seg_len);
- extended = 1;
- }
-
- CU_ASSERT(l2_off == odp_packet_l2_offset(*pkt));
- CU_ASSERT(l3_off == odp_packet_l3_offset(*pkt));
- CU_ASSERT(l4_off == odp_packet_l4_offset(*pkt));
- } else {
- if ((uint32_t)abs(shift) <= seg_data_len) {
- tail = odp_packet_pull_tail(*pkt, -shift);
- extended = 0;
- } else {
- rc = odp_packet_trunc_tail(pkt, -shift,
- &tail, &seg_len);
- extended = 1;
- }
- }
-
- CU_ASSERT_PTR_NOT_NULL(tail);
- if (extended) {
- CU_ASSERT(rc >= 0);
- CU_ASSERT(odp_packet_last_seg(*pkt) != seg);
- seg = odp_packet_last_seg(*pkt);
- if (shift > 0) {
- CU_ASSERT(odp_packet_seg_data_len(*pkt, seg) ==
- shift - room);
- } else {
- CU_ASSERT(odp_packet_tailroom(*pkt) >=
- (uint32_t)abs(shift) - seg_data_len);
- CU_ASSERT(seg_len == odp_packet_tailroom(*pkt));
- }
- } else {
- CU_ASSERT(odp_packet_seg_data_len(*pkt, seg) ==
- seg_data_len + shift);
- CU_ASSERT(odp_packet_tailroom(*pkt) == room - shift);
- if (room == 0 || (room - shift) == 0)
- return;
- if (shift >= 0) {
- CU_ASSERT(odp_packet_tail(*pkt) ==
- tail_orig + shift);
- } else {
- CU_ASSERT(tail == tail_orig + shift);
- }
- }
-
- CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift);
- if (shift >= 0) {
- CU_ASSERT(tail == tail_orig);
- } else {
- CU_ASSERT(odp_packet_tail(*pkt) == tail);
- }
-}
-
-void packet_test_tailroom(void)
-{
- odp_packet_t pkt = odp_packet_copy(test_packet,
- odp_packet_pool(test_packet));
- odp_packet_seg_t segment;
- uint32_t room;
- uint32_t seg_data_len;
- uint32_t push_val, pull_val;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- segment = odp_packet_last_seg(pkt);
- CU_ASSERT(segment != ODP_PACKET_SEG_INVALID);
- room = odp_packet_tailroom(pkt);
- CU_ASSERT(room >= capa.pkt.min_tailroom);
-
- seg_data_len = odp_packet_seg_data_len(pkt, segment);
- CU_ASSERT(seg_data_len >= 1);
-
- pull_val = seg_data_len / 2;
- /* Leave one byte in a tailroom for odp_packet_tail() to succeed */
- push_val = (room > 0) ? room - 1 : room;
-
- _verify_tailroom_shift(&pkt, -pull_val);
- _verify_tailroom_shift(&pkt, push_val + pull_val);
- _verify_tailroom_shift(&pkt, -push_val);
- _verify_tailroom_shift(&pkt, 0);
-
- if (segmentation_supported) {
- push_val = room + 100;
- _verify_tailroom_shift(&pkt, push_val);
- _verify_tailroom_shift(&pkt, 0);
- _verify_tailroom_shift(&pkt, -push_val);
- }
-
- odp_packet_free(pkt);
-}
-
-void packet_test_segments(void)
-{
- int num_segs, seg_index;
- uint32_t data_len;
- odp_packet_seg_t seg;
- odp_packet_t pkt = test_packet;
- odp_packet_t seg_pkt = segmented_test_packet;
-
- CU_ASSERT(odp_packet_is_valid(pkt) == 1);
-
- num_segs = odp_packet_num_segs(pkt);
- CU_ASSERT(num_segs != 0);
-
- if (odp_packet_is_segmented(pkt)) {
- CU_ASSERT(num_segs > 1);
- } else {
- CU_ASSERT(num_segs == 1);
- }
-
- CU_ASSERT(odp_packet_is_segmented(pkt) == 0);
- if (segmentation_supported)
- CU_ASSERT(odp_packet_is_segmented(seg_pkt) == 1);
-
- seg = odp_packet_first_seg(pkt);
- data_len = 0;
- seg_index = 0;
- while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) {
- uint32_t seg_data_len;
- void *seg_data;
-
- seg_data_len = odp_packet_seg_data_len(pkt, seg);
- seg_data = odp_packet_seg_data(pkt, seg);
-
- CU_ASSERT(seg_data_len > 0);
- CU_ASSERT_PTR_NOT_NULL(seg_data);
- CU_ASSERT(odp_packet_seg_to_u64(seg) !=
- odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID));
- CU_ASSERT(odp_memcmp(seg_data, seg_data, seg_data_len) == 0);
-
- data_len += seg_data_len;
-
- seg_index++;
- seg = odp_packet_next_seg(pkt, seg);
- }
-
- CU_ASSERT(seg_index == num_segs);
- CU_ASSERT(data_len <= odp_packet_buf_len(pkt));
- CU_ASSERT(data_len == odp_packet_len(pkt));
-
- if (seg_index == num_segs)
- CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
-
- seg = odp_packet_first_seg(seg_pkt);
- num_segs = odp_packet_num_segs(seg_pkt);
-
- data_len = 0;
- seg_index = 0;
-
- while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) {
- uint32_t seg_data_len;
- void *seg_data;
-
- seg_data_len = odp_packet_seg_data_len(seg_pkt, seg);
- seg_data = odp_packet_seg_data(seg_pkt, seg);
-
- CU_ASSERT(seg_data_len > 0);
- CU_ASSERT(seg_data != NULL);
- CU_ASSERT(odp_packet_seg_to_u64(seg) !=
- odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID));
- CU_ASSERT(odp_memcmp(seg_data, seg_data, seg_data_len) == 0);
-
- data_len += seg_data_len;
-
- seg_index++;
- seg = odp_packet_next_seg(seg_pkt, seg);
- }
-
- CU_ASSERT(seg_index == num_segs);
- CU_ASSERT(data_len <= odp_packet_buf_len(seg_pkt));
- CU_ASSERT(data_len == odp_packet_len(seg_pkt));
-
- if (seg_index == num_segs)
- CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
-}
-
-void packet_test_segment_last(void)
-{
- odp_packet_t pkt = test_packet;
- odp_packet_seg_t seg;
-
- seg = odp_packet_last_seg(pkt);
- CU_ASSERT_FATAL(seg != ODP_PACKET_SEG_INVALID);
-
- seg = odp_packet_next_seg(pkt, seg);
- CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
-}
-
-#define TEST_INFLAG(packet, flag) \
-do { \
- odp_packet_has_##flag##_set(packet, 0); \
- CU_ASSERT(odp_packet_has_##flag(packet) == 0); \
- odp_packet_has_##flag##_set(packet, 1); \
- CU_ASSERT(odp_packet_has_##flag(packet) != 0); \
-} while (0)
-
-void packet_test_in_flags(void)
-{
- odp_packet_t pkt = test_packet;
-
- TEST_INFLAG(pkt, l2);
- TEST_INFLAG(pkt, l3);
- TEST_INFLAG(pkt, l4);
- TEST_INFLAG(pkt, eth);
- TEST_INFLAG(pkt, eth_bcast);
- TEST_INFLAG(pkt, eth_mcast);
- TEST_INFLAG(pkt, jumbo);
- TEST_INFLAG(pkt, vlan);
- TEST_INFLAG(pkt, vlan_qinq);
- TEST_INFLAG(pkt, arp);
- TEST_INFLAG(pkt, ipv4);
- TEST_INFLAG(pkt, ipv6);
- TEST_INFLAG(pkt, ip_bcast);
- TEST_INFLAG(pkt, ip_mcast);
- TEST_INFLAG(pkt, ipfrag);
- TEST_INFLAG(pkt, ipopt);
- TEST_INFLAG(pkt, ipsec);
- TEST_INFLAG(pkt, udp);
- TEST_INFLAG(pkt, tcp);
- TEST_INFLAG(pkt, sctp);
- TEST_INFLAG(pkt, icmp);
-}
-
-void packet_test_error_flags(void)
-{
- odp_packet_t pkt = test_packet;
- int err;
-
- /**
- * The packet have not been classified so it doesn't have error flags
- * properly set. Just check that functions return one of allowed values.
- */
- err = odp_packet_has_error(pkt);
- CU_ASSERT(err == 0 || err == 1);
-
- err = odp_packet_has_l2_error(pkt);
- CU_ASSERT(err == 0 || err == 1);
-
- err = odp_packet_has_l3_error(pkt);
- CU_ASSERT(err == 0 || err == 1);
-
- err = odp_packet_has_l4_error(pkt);
- CU_ASSERT(err == 0 || err == 1);
-}
-
-struct packet_metadata {
- uint32_t l2_off;
- uint32_t l3_off;
- uint32_t l4_off;
- void *usr_ptr;
- uint64_t usr_u64;
-};
-
-void packet_test_add_rem_data(void)
-{
- odp_packet_t pkt, new_pkt;
- uint32_t pkt_len, offset, add_len;
- void *usr_ptr;
- struct udata_struct *udat, *new_udat;
- int ret;
- odp_pool_capability_t capa;
- uint32_t min_seg_len;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- min_seg_len = capa.pkt.min_seg_len;
-
- pkt = odp_packet_alloc(packet_pool, packet_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- pkt_len = odp_packet_len(pkt);
- usr_ptr = odp_packet_user_ptr(pkt);
- udat = odp_packet_user_area(pkt);
- CU_ASSERT(odp_packet_user_area_size(pkt) ==
- sizeof(struct udata_struct));
- memcpy(udat, &test_packet_udata, sizeof(struct udata_struct));
-
- offset = pkt_len / 2;
-
- if (segmentation_supported) {
- /* Insert one more packet length in the middle of a packet */
- add_len = min_seg_len;
- } else {
- /* Add diff between largest and smaller packets
- * which is at least tailroom */
- add_len = segmented_packet_len - packet_len;
- }
-
- new_pkt = pkt;
- ret = odp_packet_add_data(&new_pkt, offset, add_len);
- CU_ASSERT(ret >= 0);
- if (ret < 0)
- goto free_packet;
- CU_ASSERT(odp_packet_len(new_pkt) == pkt_len + add_len);
- /* Verify that user metadata is preserved */
- CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr);
-
- /* Verify that user metadata has been preserved */
- new_udat = odp_packet_user_area(new_pkt);
- CU_ASSERT_PTR_NOT_NULL(new_udat);
- CU_ASSERT(odp_packet_user_area_size(new_pkt) ==
- sizeof(struct udata_struct));
- CU_ASSERT(memcmp(new_udat, &test_packet_udata,
- sizeof(struct udata_struct)) == 0);
-
- pkt = new_pkt;
-
- pkt_len = odp_packet_len(pkt);
- usr_ptr = odp_packet_user_ptr(pkt);
-
- ret = odp_packet_rem_data(&new_pkt, offset, add_len);
- CU_ASSERT(ret >= 0);
- if (ret < 0)
- goto free_packet;
- CU_ASSERT(odp_packet_len(new_pkt) == pkt_len - add_len);
- CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr);
-
- /* Verify that user metadata has been preserved */
- new_udat = odp_packet_user_area(new_pkt);
- CU_ASSERT_PTR_NOT_NULL(new_udat);
- CU_ASSERT(odp_packet_user_area_size(new_pkt) ==
- sizeof(struct udata_struct));
- CU_ASSERT(memcmp(new_udat, &test_packet_udata,
- sizeof(struct udata_struct)) == 0);
-
- pkt = new_pkt;
-
-free_packet:
- odp_packet_free(pkt);
-}
-
-#define COMPARE_HAS_INFLAG(p1, p2, flag) \
- CU_ASSERT(odp_packet_has_##flag(p1) == odp_packet_has_##flag(p2))
-
-#define COMPARE_INFLAG(p1, p2, flag) \
- CU_ASSERT(odp_packet_##flag(p1) == odp_packet_##flag(p2))
-
-static void _packet_compare_inflags(odp_packet_t pkt1, odp_packet_t pkt2)
-{
- COMPARE_HAS_INFLAG(pkt1, pkt2, l2);
- COMPARE_HAS_INFLAG(pkt1, pkt2, l3);
- COMPARE_HAS_INFLAG(pkt1, pkt2, l4);
- COMPARE_HAS_INFLAG(pkt1, pkt2, eth);
- COMPARE_HAS_INFLAG(pkt1, pkt2, eth_bcast);
- COMPARE_HAS_INFLAG(pkt1, pkt2, eth_mcast);
- COMPARE_HAS_INFLAG(pkt1, pkt2, jumbo);
- COMPARE_HAS_INFLAG(pkt1, pkt2, vlan);
- COMPARE_HAS_INFLAG(pkt1, pkt2, vlan_qinq);
- COMPARE_HAS_INFLAG(pkt1, pkt2, arp);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ipv4);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ipv6);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ip_bcast);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ip_mcast);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ipfrag);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ipopt);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ipsec);
- COMPARE_HAS_INFLAG(pkt1, pkt2, udp);
- COMPARE_HAS_INFLAG(pkt1, pkt2, tcp);
- COMPARE_HAS_INFLAG(pkt1, pkt2, sctp);
- COMPARE_HAS_INFLAG(pkt1, pkt2, icmp);
- COMPARE_HAS_INFLAG(pkt1, pkt2, flow_hash);
- COMPARE_HAS_INFLAG(pkt1, pkt2, ts);
-
- COMPARE_INFLAG(pkt1, pkt2, color);
- COMPARE_INFLAG(pkt1, pkt2, drop_eligible);
- COMPARE_INFLAG(pkt1, pkt2, shaper_len_adjust);
-}
-
-static void _packet_compare_udata(odp_packet_t pkt1, odp_packet_t pkt2)
-{
- uint32_t usize1 = odp_packet_user_area_size(pkt1);
- uint32_t usize2 = odp_packet_user_area_size(pkt2);
-
- void *uaddr1 = odp_packet_user_area(pkt1);
- void *uaddr2 = odp_packet_user_area(pkt2);
-
- uint32_t cmplen = usize1 <= usize2 ? usize1 : usize2;
-
- if (cmplen)
- CU_ASSERT(!memcmp(uaddr1, uaddr2, cmplen));
-}
-
-static void _packet_compare_offset(odp_packet_t pkt1, uint32_t off1,
- odp_packet_t pkt2, uint32_t off2,
- uint32_t len, int line)
-{
- uint32_t seglen1, seglen2, cmplen;
- int ret;
-
- if (off1 + len > odp_packet_len(pkt1) ||
- off2 + len > odp_packet_len(pkt2))
- return;
-
- while (len > 0) {
- void *pkt1map = odp_packet_offset(pkt1, off1, &seglen1, NULL);
- void *pkt2map = odp_packet_offset(pkt2, off2, &seglen2, NULL);
-
- CU_ASSERT_PTR_NOT_NULL_FATAL(pkt1map);
- CU_ASSERT_PTR_NOT_NULL_FATAL(pkt2map);
- cmplen = seglen1 < seglen2 ? seglen1 : seglen2;
- if (len < cmplen)
- cmplen = len;
-
- ret = memcmp(pkt1map, pkt2map, cmplen);
-
- if (ret) {
- printf("\ncompare_offset failed: line %i, off1 %"
- PRIu32 ", off2 %" PRIu32 "\n", line, off1, off2);
- }
-
- CU_ASSERT(ret == 0);
-
- off1 += cmplen;
- off2 += cmplen;
- len -= cmplen;
- }
-}
-
-void packet_test_copy(void)
-{
- odp_packet_t pkt;
- odp_packet_t pkt_copy, pkt_part;
- odp_pool_t pool;
- uint32_t i, plen, seg_len, src_offset, dst_offset;
- void *pkt_data;
-
- pkt = odp_packet_copy(test_packet, packet_pool_no_uarea);
- CU_ASSERT(pkt == ODP_PACKET_INVALID);
- if (pkt != ODP_PACKET_INVALID)
- odp_packet_free(pkt);
-
- pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- packet_compare_data(pkt, test_packet);
- pool = odp_packet_pool(pkt);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- pkt_copy = odp_packet_copy(pkt, pool);
- CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID);
-
- CU_ASSERT(pkt != pkt_copy);
- CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_copy));
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_copy));
-
- _packet_compare_inflags(pkt, pkt_copy);
- packet_compare_data(pkt, pkt_copy);
- CU_ASSERT(odp_packet_user_area_size(pkt) ==
- odp_packet_user_area_size(test_packet));
- _packet_compare_udata(pkt, pkt_copy);
- odp_packet_free(pkt_copy);
- odp_packet_free(pkt);
-
- pkt = odp_packet_copy(test_packet, packet_pool_double_uarea);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- packet_compare_data(pkt, test_packet);
- pool = odp_packet_pool(pkt);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- pkt_copy = odp_packet_copy(pkt, pool);
- CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID);
-
- CU_ASSERT(pkt != pkt_copy);
- CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_copy));
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_copy));
-
- _packet_compare_inflags(pkt, pkt_copy);
- packet_compare_data(pkt, pkt_copy);
- CU_ASSERT(odp_packet_user_area_size(pkt) ==
- 2 * odp_packet_user_area_size(test_packet));
- _packet_compare_udata(pkt, pkt_copy);
- _packet_compare_udata(pkt, test_packet);
- odp_packet_free(pkt_copy);
-
- /* Now test copy_part */
- pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt) + 1, pool);
- CU_ASSERT(pkt_part == ODP_PACKET_INVALID);
- pkt_part = odp_packet_copy_part(pkt, odp_packet_len(pkt), 1, pool);
- CU_ASSERT(pkt_part == ODP_PACKET_INVALID);
-
- pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt), pool);
- CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID);
- CU_ASSERT(pkt != pkt_part);
- CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_part));
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_part));
-
- packet_compare_data(pkt, pkt_part);
- odp_packet_free(pkt_part);
-
- plen = odp_packet_len(pkt);
- for (i = 0; i < plen / 2; i += 5) {
- pkt_part = odp_packet_copy_part(pkt, i, plen / 4, pool);
- CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(pkt_part) == plen / 4);
- packet_compare_offset(pkt_part, 0, pkt, i, plen / 4);
- odp_packet_free(pkt_part);
- }
-
- /* Test copy and move apis */
- CU_ASSERT(odp_packet_copy_data(pkt, 0, plen - plen / 8, plen / 8) == 0);
- packet_compare_offset(pkt, 0, pkt, plen - plen / 8, plen / 8);
- packet_compare_offset(pkt, 0, test_packet, plen - plen / 8, plen / 8);
-
- /* Test segment crossing if we support segments */
- pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL);
- CU_ASSERT(pkt_data != NULL);
-
- if (seg_len < plen) {
- src_offset = seg_len - 15;
- dst_offset = seg_len - 5;
- } else {
- src_offset = seg_len - 40;
- dst_offset = seg_len - 25;
- }
-
- pkt_part = odp_packet_copy_part(pkt, src_offset, 20, pool);
- CU_ASSERT(odp_packet_move_data(pkt, dst_offset, src_offset, 20) == 0);
- packet_compare_offset(pkt, dst_offset, pkt_part, 0, 20);
-
- odp_packet_free(pkt_part);
- odp_packet_free(pkt);
-}
-
-void packet_test_copydata(void)
-{
- odp_packet_t pkt = test_packet;
- uint32_t pkt_len = odp_packet_len(pkt);
- uint8_t *data_buf;
- uint32_t i;
- int correct_memory;
-
- CU_ASSERT_FATAL(pkt_len > 0);
-
- data_buf = malloc(pkt_len);
- CU_ASSERT_PTR_NOT_NULL_FATAL(data_buf);
-
- for (i = 0; i < pkt_len; i++)
- data_buf[i] = (uint8_t)i;
-
- CU_ASSERT(!odp_packet_copy_from_mem(pkt, 0, pkt_len, data_buf));
- memset(data_buf, 0, pkt_len);
- CU_ASSERT(!odp_packet_copy_to_mem(pkt, 0, pkt_len, data_buf));
-
- correct_memory = 1;
- for (i = 0; i < pkt_len; i++)
- if (data_buf[i] != (uint8_t)i) {
- correct_memory = 0;
- break;
- }
- CU_ASSERT(correct_memory);
-
- free(data_buf);
-
- pkt = odp_packet_alloc(odp_packet_pool(test_packet), pkt_len / 2);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, test_packet, 0,
- pkt_len) < 0);
- CU_ASSERT(odp_packet_copy_from_pkt(pkt, pkt_len, test_packet, 0,
- 1) < 0);
-
- for (i = 0; i < pkt_len / 2; i++) {
- CU_ASSERT(odp_packet_copy_from_pkt(pkt, i, test_packet, i,
- 1) == 0);
- }
-
- packet_compare_offset(pkt, 0, test_packet, 0, pkt_len / 2);
- odp_packet_free(pkt);
-
- pkt = odp_packet_alloc(odp_packet_pool(segmented_test_packet),
- odp_packet_len(segmented_test_packet) / 2);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, segmented_test_packet,
- odp_packet_len(pkt) / 4,
- odp_packet_len(pkt)) == 0);
- packet_compare_offset(pkt, 0, segmented_test_packet,
- odp_packet_len(pkt) / 4,
- odp_packet_len(pkt));
- odp_packet_free(pkt);
-}
-
-void packet_test_concatsplit(void)
-{
- odp_packet_t pkt, pkt2;
- uint32_t pkt_len;
- odp_packet_t splits[4];
- odp_pool_t pool;
-
- pool = odp_packet_pool(test_packet);
- pkt = odp_packet_copy(test_packet, pool);
- pkt2 = odp_packet_copy(test_packet, pool);
- pkt_len = odp_packet_len(test_packet);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
- CU_ASSERT(pkt_len == odp_packet_len(pkt));
- CU_ASSERT(pkt_len == odp_packet_len(pkt2));
-
- CU_ASSERT(odp_packet_concat(&pkt, pkt2) >= 0);
- CU_ASSERT(odp_packet_len(pkt) == pkt_len * 2);
- packet_compare_offset(pkt, 0, pkt, pkt_len, pkt_len);
-
- CU_ASSERT(odp_packet_split(&pkt, pkt_len, &pkt2) == 0);
- CU_ASSERT(pkt != pkt2);
- CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt2));
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt2));
- packet_compare_data(pkt, pkt2);
- packet_compare_data(pkt, test_packet);
-
- odp_packet_free(pkt);
- odp_packet_free(pkt2);
-
- pkt = odp_packet_copy(segmented_test_packet,
- odp_packet_pool(segmented_test_packet));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- pkt_len = odp_packet_len(pkt);
-
- packet_compare_data(pkt, segmented_test_packet);
- CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0);
- CU_ASSERT(pkt != splits[0]);
- CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(splits[0]));
- CU_ASSERT(odp_packet_len(pkt) == pkt_len / 2);
- CU_ASSERT(odp_packet_len(pkt) + odp_packet_len(splits[0]) == pkt_len);
-
- packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2);
- packet_compare_offset(splits[0], 0, segmented_test_packet,
- pkt_len / 2, odp_packet_len(splits[0]));
-
- CU_ASSERT(odp_packet_concat(&pkt, splits[0]) >= 0);
- packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2);
- packet_compare_offset(pkt, pkt_len / 2, segmented_test_packet,
- pkt_len / 2, pkt_len / 2);
- packet_compare_offset(pkt, 0, segmented_test_packet, 0,
- pkt_len);
-
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet));
- packet_compare_data(pkt, segmented_test_packet);
-
- CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0);
- CU_ASSERT(odp_packet_split(&pkt, pkt_len / 4, &splits[1]) == 0);
- CU_ASSERT(odp_packet_split(&pkt, pkt_len / 8, &splits[2]) == 0);
-
- CU_ASSERT(odp_packet_len(splits[0]) + odp_packet_len(splits[1]) +
- odp_packet_len(splits[2]) + odp_packet_len(pkt) == pkt_len);
-
- CU_ASSERT(odp_packet_concat(&pkt, splits[2]) >= 0);
- CU_ASSERT(odp_packet_concat(&pkt, splits[1]) >= 0);
- CU_ASSERT(odp_packet_concat(&pkt, splits[0]) >= 0);
-
- CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet));
- packet_compare_data(pkt, segmented_test_packet);
-
- odp_packet_free(pkt);
-}
-
-void packet_test_concat_small(void)
-{
- odp_pool_capability_t capa;
- odp_pool_t pool;
- odp_pool_param_t param;
- odp_packet_t pkt, pkt2;
- int ret;
- uint8_t *data;
- uint32_t i;
- uint32_t len = 32000;
- uint8_t buf[len];
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- if (capa.pkt.max_len && capa.pkt.max_len < len)
- len = capa.pkt.max_len;
-
- odp_pool_param_init(&param);
-
- param.type = ODP_POOL_PACKET;
- param.pkt.len = len;
- param.pkt.num = PACKET_POOL_NUM;
-
- pool = odp_pool_create("packet_pool_concat", &param);
- CU_ASSERT(packet_pool != ODP_POOL_INVALID);
-
- pkt = odp_packet_alloc(pool, 1);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- data = odp_packet_data(pkt);
- *data = 0;
-
- for (i = 0; i < len - 1; i++) {
- pkt2 = odp_packet_alloc(pool, 1);
- CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
-
- data = odp_packet_data(pkt2);
- *data = i + 1;
-
- ret = odp_packet_concat(&pkt, pkt2);
- CU_ASSERT(ret >= 0);
-
- if (ret < 0) {
- odp_packet_free(pkt2);
- break;
- }
- }
-
- CU_ASSERT(odp_packet_len(pkt) == len);
-
- len = odp_packet_len(pkt);
-
- memset(buf, 0, len);
- CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
-
- for (i = 0; i < len; i++)
- CU_ASSERT(buf[i] == (i % 256));
-
- odp_packet_free(pkt);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_concat_extend_trunc(void)
-{
- odp_pool_capability_t capa;
- odp_pool_t pool;
- odp_pool_param_t param;
- odp_packet_t pkt, pkt2;
- int i, ret;
- uint32_t alloc_len, ext_len, trunc_len, cur_len;
- uint32_t len = 1900;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- if (capa.pkt.max_len && capa.pkt.max_len < len)
- len = capa.pkt.max_len;
-
- alloc_len = len / 8;
- ext_len = len / 4;
- trunc_len = len / 3;
-
- odp_pool_param_init(&param);
-
- param.type = ODP_POOL_PACKET;
- param.pkt.len = len;
- param.pkt.num = PACKET_POOL_NUM;
-
- pool = odp_pool_create("packet_pool_concat", &param);
- CU_ASSERT_FATAL(packet_pool != ODP_POOL_INVALID);
-
- pkt = odp_packet_alloc(pool, alloc_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- cur_len = odp_packet_len(pkt);
-
- for (i = 0; i < 2; i++) {
- pkt2 = odp_packet_alloc(pool, alloc_len);
- CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
-
- ret = odp_packet_concat(&pkt, pkt2);
- CU_ASSERT(ret >= 0);
-
- if (ret < 0)
- odp_packet_free(pkt2);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len + alloc_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
- }
-
- ret = odp_packet_extend_tail(&pkt, ext_len, NULL, NULL);
- CU_ASSERT(ret >= 0);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len + ext_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
-
- ret = odp_packet_extend_head(&pkt, ext_len, NULL, NULL);
- CU_ASSERT(ret >= 0);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len + ext_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
-
- pkt2 = odp_packet_alloc(pool, alloc_len);
- CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
-
- ret = odp_packet_concat(&pkt, pkt2);
- CU_ASSERT(ret >= 0);
-
- if (ret < 0)
- odp_packet_free(pkt2);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len + alloc_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
-
- ret = odp_packet_trunc_head(&pkt, trunc_len, NULL, NULL);
- CU_ASSERT(ret >= 0);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len - trunc_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
-
- ret = odp_packet_trunc_tail(&pkt, trunc_len, NULL, NULL);
- CU_ASSERT(ret >= 0);
-
- CU_ASSERT(odp_packet_len(pkt) == (cur_len - trunc_len));
- cur_len = odp_packet_len(pkt);
- CU_ASSERT(cur_len == odp_packet_unshared_len(pkt));
-
- odp_packet_free(pkt);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_extend_small(void)
-{
- odp_pool_capability_t capa;
- odp_pool_t pool;
- odp_pool_param_t param;
- odp_packet_t pkt;
- int ret, round;
- uint8_t *data;
- uint32_t i, seg_len;
- int tail = 1;
- uint32_t len = 32000;
- uint8_t buf[len];
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- if (capa.pkt.max_len && capa.pkt.max_len < len)
- len = capa.pkt.max_len;
-
- odp_pool_param_init(&param);
-
- param.type = ODP_POOL_PACKET;
- param.pkt.len = len;
- param.pkt.num = PACKET_POOL_NUM;
-
- pool = odp_pool_create("packet_pool_extend", &param);
- CU_ASSERT_FATAL(packet_pool != ODP_POOL_INVALID);
-
- for (round = 0; round < 2; round++) {
- pkt = odp_packet_alloc(pool, 1);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- data = odp_packet_data(pkt);
- *data = 0;
-
- for (i = 0; i < len - 1; i++) {
- if (tail) {
- ret = odp_packet_extend_tail(&pkt, 1,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- } else {
- ret = odp_packet_extend_head(&pkt, 1,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- }
-
- if (ret < 0)
- break;
-
- if (tail) {
- /* assert needs brackets */
- CU_ASSERT(seg_len == 1);
- } else {
- CU_ASSERT(seg_len > 0);
- }
-
- *data = i + 1;
- }
-
- CU_ASSERT(odp_packet_len(pkt) == len);
- CU_ASSERT(odp_packet_unshared_len(pkt) == len);
-
- len = odp_packet_len(pkt);
-
- memset(buf, 0, len);
- CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
-
- for (i = 0; i < len; i++) {
- int match;
-
- if (tail) {
- match = (buf[i] == (i % 256));
- CU_ASSERT(match);
- } else {
- match = (buf[len - 1 - i] == (i % 256));
- CU_ASSERT(match);
- }
-
- /* Limit the number of failed asserts to
- one per packet */
- if (!match)
- break;
- }
-
- odp_packet_free(pkt);
-
- tail = 0;
- }
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_extend_large(void)
-{
- odp_pool_capability_t capa;
- odp_pool_t pool;
- odp_pool_param_t param;
- odp_packet_t pkt;
- int ret, round;
- uint8_t *data;
- uint32_t i, seg_len, ext_len, cur_len, cur_data;
- int tail = 1;
- int num_div = 16;
- int div = 1;
- uint32_t len = 32000;
- uint8_t buf[len];
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- if (capa.pkt.max_len && capa.pkt.max_len < len)
- len = capa.pkt.max_len;
-
- odp_pool_param_init(&param);
-
- param.type = ODP_POOL_PACKET;
- param.pkt.len = len;
- param.pkt.num = PACKET_POOL_NUM;
-
- pool = odp_pool_create("packet_pool_extend", &param);
- CU_ASSERT_FATAL(packet_pool != ODP_POOL_INVALID);
-
- for (round = 0; round < 2 * num_div; round++) {
- ext_len = len / div;
- cur_len = ext_len;
-
- pkt = odp_packet_alloc(pool, ext_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- cur_data = 0;
-
- if (tail) {
- ret = fill_data_forward(pkt, 0, ext_len, &cur_data);
- CU_ASSERT(ret == 0);
- } else {
- ret = fill_data_backward(pkt, 0, ext_len, &cur_data);
- CU_ASSERT(ret == 0);
- }
-
- while (cur_len < len) {
- if ((len - cur_len) < ext_len)
- ext_len = len - cur_len;
-
- if (tail) {
- ret = odp_packet_extend_tail(&pkt, ext_len,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- } else {
- ret = odp_packet_extend_head(&pkt, ext_len,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- }
-
- if (ret < 0)
- break;
-
- if (tail) {
- /* assert needs brackets */
- CU_ASSERT((seg_len > 0) &&
- (seg_len <= ext_len));
- ret = fill_data_forward(pkt, cur_len, ext_len,
- &cur_data);
- CU_ASSERT(ret == 0);
- } else {
- CU_ASSERT(seg_len > 0);
- CU_ASSERT(data == odp_packet_data(pkt));
- ret = fill_data_backward(pkt, 0, ext_len,
- &cur_data);
- CU_ASSERT(ret == 0);
- }
-
- cur_len += ext_len;
- }
-
- CU_ASSERT(odp_packet_len(pkt) == len);
-
- len = odp_packet_len(pkt);
-
- memset(buf, 0, len);
- CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
-
- for (i = 0; i < len; i++) {
- int match;
-
- if (tail) {
- match = (buf[i] == (i % 256));
- CU_ASSERT(match);
- } else {
- match = (buf[len - 1 - i] == (i % 256));
- CU_ASSERT(match);
- }
-
- /* Limit the number of failed asserts to
- one per packet */
- if (!match)
- break;
- }
-
- odp_packet_free(pkt);
-
- div++;
- if (div > num_div) {
- /* test extend head */
- div = 1;
- tail = 0;
- }
- }
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_extend_mix(void)
-{
- odp_pool_capability_t capa;
- odp_pool_t pool;
- odp_pool_param_t param;
- odp_packet_t pkt;
- int ret, round;
- uint8_t *data;
- uint32_t i, seg_len, ext_len, cur_len, cur_data;
- int small_count;
- int tail = 1;
- uint32_t len = 32000;
- uint8_t buf[len];
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- if (capa.pkt.max_len && capa.pkt.max_len < len)
- len = capa.pkt.max_len;
-
- odp_pool_param_init(&param);
-
- param.type = ODP_POOL_PACKET;
- param.pkt.len = len;
- param.pkt.num = PACKET_POOL_NUM;
-
- pool = odp_pool_create("packet_pool_extend", &param);
- CU_ASSERT_FATAL(packet_pool != ODP_POOL_INVALID);
-
- for (round = 0; round < 2; round++) {
- small_count = 30;
- ext_len = len / 10;
- cur_len = ext_len;
-
- pkt = odp_packet_alloc(pool, ext_len);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- cur_data = 0;
-
- if (tail) {
- ret = fill_data_forward(pkt, 0, ext_len, &cur_data);
- CU_ASSERT(ret == 0);
- } else {
- ret = fill_data_backward(pkt, 0, ext_len, &cur_data);
- CU_ASSERT(ret == 0);
- }
-
- while (cur_len < len) {
- if (small_count) {
- small_count--;
- ext_len = len / 100;
- } else {
- ext_len = len / 4;
- }
-
- if ((len - cur_len) < ext_len)
- ext_len = len - cur_len;
-
- if (tail) {
- ret = odp_packet_extend_tail(&pkt, ext_len,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- CU_ASSERT((seg_len > 0) &&
- (seg_len <= ext_len));
- ret = fill_data_forward(pkt, cur_len, ext_len,
- &cur_data);
- CU_ASSERT(ret == 0);
- } else {
- ret = odp_packet_extend_head(&pkt, ext_len,
- (void **)&data,
- &seg_len);
- CU_ASSERT(ret >= 0);
- CU_ASSERT(seg_len > 0);
- CU_ASSERT(data == odp_packet_data(pkt));
- ret = fill_data_backward(pkt, 0, ext_len,
- &cur_data);
- CU_ASSERT(ret == 0);
- }
-
- cur_len += ext_len;
- }
-
- CU_ASSERT(odp_packet_len(pkt) == len);
-
- len = odp_packet_len(pkt);
-
- memset(buf, 0, len);
- CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
-
- for (i = 0; i < len; i++) {
- int match;
-
- if (tail) {
- match = (buf[i] == (i % 256));
- CU_ASSERT(match);
- } else {
- match = (buf[len - 1 - i] == (i % 256));
- CU_ASSERT(match);
- }
-
- /* Limit the number of failed asserts to
- one per packet */
- if (!match)
- break;
- }
-
- odp_packet_free(pkt);
-
- tail = 0;
- }
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void packet_test_extend_ref(void)
-{
- odp_packet_t max_pkt, ref;
- uint32_t hr, tr, max_len;
- odp_pool_capability_t capa;
-
- CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
-
- max_pkt = odp_packet_copy(segmented_test_packet,
- odp_packet_pool(segmented_test_packet));
- CU_ASSERT_FATAL(max_pkt != ODP_PACKET_INVALID);
- max_len = odp_packet_len(max_pkt);
-
- /* Maximize the max pkt */
- hr = odp_packet_headroom(max_pkt);
- tr = odp_packet_tailroom(max_pkt);
- odp_packet_push_head(max_pkt, hr);
- odp_packet_push_tail(max_pkt, tr);
-
- /* Max packet should not be extendable at either end */
- if (max_len == capa.pkt.max_len) {
- CU_ASSERT(odp_packet_extend_tail(&max_pkt, 1, NULL, NULL) < 0);
- CU_ASSERT(odp_packet_extend_head(&max_pkt, 1, NULL, NULL) < 0);
- }
-
- /* See if we can trunc and extend anyway */
- CU_ASSERT(odp_packet_trunc_tail(&max_pkt, hr + tr + 1,
- NULL, NULL) >= 0);
- CU_ASSERT(odp_packet_extend_head(&max_pkt, 1, NULL, NULL) >= 0);
- CU_ASSERT(odp_packet_len(max_pkt) == max_len);
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == max_len);
-
- /* Now try with a reference in place */
- CU_ASSERT(odp_packet_trunc_tail(&max_pkt, 100, NULL, NULL) >= 0);
- ref = odp_packet_ref(max_pkt, 100);
-
- /* Verify ref lengths */
- CU_ASSERT(ref != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_len(ref) == max_len - 200);
- if (odp_packet_has_ref(ref) == 1) {
- CU_ASSERT(odp_packet_unshared_len(ref) == 0);
-
- /* And ref's affect on max_pkt */
- CU_ASSERT(odp_packet_has_ref(max_pkt) == 1);
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == 100);
- } else {
- CU_ASSERT(odp_packet_unshared_len(ref) == odp_packet_len(ref));
- CU_ASSERT(odp_packet_unshared_len(max_pkt) ==
- odp_packet_len(max_pkt));
- }
-
- /* Now extend max_pkt and verify effect */
- CU_ASSERT(odp_packet_extend_head(&max_pkt, 10, NULL, NULL) >= 0);
- CU_ASSERT(odp_packet_len(max_pkt) == max_len - 90);
-
- if (odp_packet_has_ref(max_pkt) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == 110);
- }
-
- /* Extend on max_pkt should not affect ref */
- CU_ASSERT(odp_packet_len(ref) == max_len - 200);
-
- if (odp_packet_has_ref(ref) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref) == 0);
- }
-
- /* Now extend ref and verify effect*/
- CU_ASSERT(odp_packet_extend_head(&ref, 20, NULL, NULL) >= 0);
- CU_ASSERT(odp_packet_len(ref) == max_len - 180);
-
- if (odp_packet_has_ref(ref) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref) == 20);
- }
-
- /* Extend on ref should not affect max_pkt */
- CU_ASSERT(odp_packet_len(max_pkt) == max_len - 90);
-
- if (odp_packet_has_ref(max_pkt) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == 110);
- }
-
- /* Trunc max_pkt of all unshared len */
- CU_ASSERT(odp_packet_trunc_head(&max_pkt, 110, NULL, NULL) >= 0);
-
- /* Verify effect on max_pkt */
- CU_ASSERT(odp_packet_len(max_pkt) == max_len - 200);
-
- if (odp_packet_has_ref(max_pkt) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == 0);
- }
-
- /* Verify that ref is unchanged */
- CU_ASSERT(odp_packet_len(ref) == max_len - 180);
-
- if (odp_packet_has_ref(ref) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref) == 20);
- }
-
- /* Free ref and verify that max_pkt is back to being unreferenced */
- odp_packet_free(ref);
- CU_ASSERT(odp_packet_has_ref(max_pkt) == 0);
- CU_ASSERT(odp_packet_len(max_pkt) == max_len - 200);
- CU_ASSERT(odp_packet_unshared_len(max_pkt) == max_len - 200);
-
- odp_packet_free(max_pkt);
-}
-
-void packet_test_align(void)
-{
- odp_packet_t pkt;
- uint32_t pkt_len, seg_len, offset, aligned_seglen;
- void *pkt_data, *aligned_data;
- const uint32_t max_align = 32;
-
- pkt = odp_packet_copy_part(segmented_test_packet, 0,
- odp_packet_len(segmented_test_packet) / 2,
- odp_packet_pool(segmented_test_packet));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
-
- pkt_len = odp_packet_len(pkt);
- seg_len = odp_packet_seg_len(pkt);
-
- if (odp_packet_is_segmented(pkt)) {
- /* Can't address across segment boundaries */
- CU_ASSERT(odp_packet_align(&pkt, 0, pkt_len, 0) < 0);
-
- offset = seg_len - 5;
- (void)odp_packet_offset(pkt, offset, &seg_len, NULL);
-
- /* Realign for addressability */
- CU_ASSERT(odp_packet_align(&pkt, offset,
- seg_len + 2, 0) >= 0);
-
- /* Alignment doesn't change packet length or contents */
- CU_ASSERT(odp_packet_len(pkt) == pkt_len);
- (void)odp_packet_offset(pkt, offset, &aligned_seglen, NULL);
- packet_compare_offset(pkt, offset,
- segmented_test_packet, offset,
- aligned_seglen);
-
- /* Verify requested contiguous addressabilty */
- CU_ASSERT(aligned_seglen >= seg_len + 2);
- }
-
- /* Get a misaligned address */
- pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL);
- offset = seg_len - 5;
- pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL);
- if ((uintptr_t)pkt_data % max_align == 0) {
- offset--;
- pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL);
- }
-
- /* Realign for alignment */
- CU_ASSERT(odp_packet_align(&pkt, offset, 1, max_align) >= 0);
- aligned_data = odp_packet_offset(pkt, offset, &aligned_seglen, NULL);
-
- CU_ASSERT(odp_packet_len(pkt) == pkt_len);
- packet_compare_offset(pkt, offset, segmented_test_packet, offset,
- aligned_seglen);
- CU_ASSERT((uintptr_t)aligned_data % max_align == 0);
-
- odp_packet_free(pkt);
-}
-
-void packet_test_offset(void)
-{
- odp_packet_t pkt = test_packet;
- uint32_t seg_len, full_seg_len;
- odp_packet_seg_t seg;
- uint8_t *ptr, *start_ptr;
- uint32_t offset;
-
- ptr = odp_packet_offset(pkt, 0, &seg_len, &seg);
- CU_ASSERT(seg != ODP_PACKET_SEG_INVALID);
- CU_ASSERT(seg_len > 1);
- CU_ASSERT(seg_len == odp_packet_seg_len(pkt));
- CU_ASSERT(seg_len == odp_packet_seg_data_len(pkt, seg));
- CU_ASSERT_PTR_NOT_NULL(ptr);
- CU_ASSERT(ptr == odp_packet_data(pkt));
- CU_ASSERT(ptr == odp_packet_seg_data(pkt, seg));
-
- /* Query a second byte */
- start_ptr = ptr;
- full_seg_len = seg_len;
- offset = 1;
-
- ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
- CU_ASSERT_PTR_NOT_NULL(ptr);
- CU_ASSERT(ptr == start_ptr + offset);
- CU_ASSERT(seg_len == full_seg_len - offset);
-
- /* Query the last byte in a segment */
- offset = full_seg_len - 1;
-
- ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
- CU_ASSERT_PTR_NOT_NULL(ptr);
- CU_ASSERT(ptr == start_ptr + offset);
- CU_ASSERT(seg_len == full_seg_len - offset);
-
- /* Query the last byte in a packet */
- offset = odp_packet_len(pkt) - 1;
- ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
- CU_ASSERT_PTR_NOT_NULL(ptr);
- CU_ASSERT(seg_len == 1);
-
- /* Pass NULL to [out] arguments */
- ptr = odp_packet_offset(pkt, 0, NULL, NULL);
- CU_ASSERT_PTR_NOT_NULL(ptr);
-}
-
-void packet_test_ref(void)
-{
- odp_packet_t base_pkt, segmented_base_pkt, hdr_pkt[4],
- ref_pkt[4], refhdr_pkt[4], hdr_cpy;
- uint32_t pkt_len, segmented_pkt_len, hdr_len[4], offset[4], hr[4],
- base_hr, ref_len[4];
- int i;
-
- base_pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
- base_hr = odp_packet_headroom(base_pkt);
- pkt_len = odp_packet_len(test_packet);
- CU_ASSERT_FATAL(base_pkt != ODP_PACKET_INVALID);
-
- segmented_base_pkt =
- odp_packet_copy(segmented_test_packet,
- odp_packet_pool(segmented_test_packet));
- segmented_pkt_len = odp_packet_len(segmented_test_packet);
- CU_ASSERT_FATAL(segmented_base_pkt != ODP_PACKET_INVALID);
-
- CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
-
- hdr_pkt[0] =
- odp_packet_copy_part(segmented_test_packet, 0,
- odp_packet_len(segmented_test_packet) / 4,
- odp_packet_pool(segmented_test_packet));
- CU_ASSERT_FATAL(hdr_pkt[0] != ODP_PACKET_INVALID);
- hdr_len[0] = odp_packet_len(hdr_pkt[0]);
- offset[0] = 0;
-
- hdr_pkt[1] =
- odp_packet_copy_part(segmented_test_packet, 10,
- odp_packet_len(segmented_test_packet) / 8,
- odp_packet_pool(segmented_test_packet));
- CU_ASSERT_FATAL(hdr_pkt[1] != ODP_PACKET_INVALID);
- hdr_len[1] = odp_packet_len(hdr_pkt[1]);
- offset[1] = 5;
-
- hdr_pkt[2] = odp_packet_copy_part(test_packet, 0,
- odp_packet_len(test_packet) / 4,
- odp_packet_pool(test_packet));
- CU_ASSERT_FATAL(hdr_pkt[2] != ODP_PACKET_INVALID);
- hdr_len[2] = odp_packet_len(hdr_pkt[2]);
- offset[2] = 64;
-
- hdr_pkt[3] = odp_packet_copy_part(test_packet, 0,
- odp_packet_len(test_packet) / 4,
- odp_packet_pool(test_packet));
- CU_ASSERT_FATAL(hdr_pkt[3] != ODP_PACKET_INVALID);
- hdr_len[3] = odp_packet_len(hdr_pkt[3]);
- offset[3] = 64;
-
- /* Nothing is a ref or has a ref before we start */
- for (i = 0; i < 4; i++) {
- CU_ASSERT(odp_packet_has_ref(hdr_pkt[i]) == 0);
- CU_ASSERT(odp_packet_len(hdr_pkt[i]) ==
- odp_packet_unshared_len(hdr_pkt[i]));
- }
-
- /* Create a couple of refs */
- refhdr_pkt[0] = odp_packet_ref_pkt(base_pkt, offset[0], hdr_pkt[0]);
- refhdr_pkt[1] = odp_packet_ref_pkt(base_pkt, offset[1], hdr_pkt[1]);
-
- CU_ASSERT(refhdr_pkt[0] != ODP_PACKET_INVALID);
- CU_ASSERT(refhdr_pkt[1] != ODP_PACKET_INVALID);
-
- /* If base packet has now references, ref packet should be also
- * references. */
- if (odp_packet_has_ref(base_pkt) == 1) {
- CU_ASSERT(odp_packet_has_ref(refhdr_pkt[0]) == 1);
- CU_ASSERT(odp_packet_has_ref(refhdr_pkt[1]) == 1);
-
- CU_ASSERT(odp_packet_unshared_len(base_pkt) == 0);
- } else {
- CU_ASSERT(odp_packet_unshared_len(base_pkt) == pkt_len);
- }
-
- CU_ASSERT(odp_packet_len(refhdr_pkt[0]) ==
- hdr_len[0] + pkt_len - offset[0]);
- CU_ASSERT(odp_packet_len(refhdr_pkt[1]) ==
- hdr_len[1] + pkt_len - offset[1]);
-
- if (odp_packet_has_ref(refhdr_pkt[0]) == 1) {
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[0]) == hdr_len[0]);
- } else {
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[0]) ==
- odp_packet_len(refhdr_pkt[0]));
- }
-
- if (odp_packet_has_ref(refhdr_pkt[1]) == 1) {
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[1]) == hdr_len[1]);
- } else {
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[1]) ==
- odp_packet_len(refhdr_pkt[1]));
- }
-
- packet_compare_offset(refhdr_pkt[0], hdr_len[0],
- base_pkt, offset[0],
- pkt_len - offset[0]);
-
- packet_compare_offset(refhdr_pkt[1], hdr_len[1],
- base_pkt, offset[1],
- pkt_len - offset[1]);
-
- /* See if compound references are supported and if so that they
- * operate properly */
- hdr_cpy = odp_packet_copy(hdr_pkt[2], odp_packet_pool(hdr_pkt[2]));
- CU_ASSERT_FATAL(hdr_cpy != ODP_PACKET_INVALID);
-
- refhdr_pkt[2] = odp_packet_ref_pkt(refhdr_pkt[0], 2, hdr_cpy);
- CU_ASSERT(refhdr_pkt[2] != ODP_PACKET_INVALID);
-
- if (odp_packet_has_ref(refhdr_pkt[2]) == 1) {
- CU_ASSERT(odp_packet_has_ref(refhdr_pkt[0]) == 1);
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[2]) == hdr_len[2]);
- CU_ASSERT(odp_packet_unshared_len(refhdr_pkt[0]) == 2);
- }
-
- /* Delete the refs */
- odp_packet_free(refhdr_pkt[0]);
- odp_packet_free(refhdr_pkt[1]);
- odp_packet_free(refhdr_pkt[2]);
-
- /* Verify that base_pkt no longer has a ref */
- CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
-
- /* Now create a two more shared refs */
- refhdr_pkt[2] = odp_packet_ref_pkt(base_pkt, offset[2], hdr_pkt[2]);
- refhdr_pkt[3] = odp_packet_ref_pkt(base_pkt, offset[3], hdr_pkt[3]);
-
- CU_ASSERT(hdr_pkt[2] != ODP_PACKET_INVALID);
- CU_ASSERT(hdr_pkt[3] != ODP_PACKET_INVALID);
-
- if (odp_packet_has_ref(base_pkt) == 1) {
- CU_ASSERT(odp_packet_has_ref(refhdr_pkt[2]) == 1);
- CU_ASSERT(odp_packet_has_ref(refhdr_pkt[3]) == 1);
- }
-
- CU_ASSERT(odp_packet_len(refhdr_pkt[2]) ==
- odp_packet_len(refhdr_pkt[3]));
-
- packet_compare_offset(refhdr_pkt[2], 0,
- refhdr_pkt[3], 0,
- odp_packet_len(hdr_pkt[2]));
-
- /* Delete the headers */
- odp_packet_free(refhdr_pkt[2]);
- odp_packet_free(refhdr_pkt[3]);
-
- /* Verify that base_pkt is no longer ref'd */
- CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
-
- /* Create a static reference */
- ref_pkt[0] = odp_packet_ref_static(base_pkt);
- CU_ASSERT(ref_pkt[0] != ODP_PACKET_INVALID);
-
- if (odp_packet_has_ref(base_pkt) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_has_ref(ref_pkt[0]) == 1);
- }
-
- CU_ASSERT(odp_packet_len(ref_pkt[0]) == odp_packet_len(base_pkt));
- packet_compare_offset(ref_pkt[0], 0, base_pkt, 0,
- odp_packet_len(base_pkt));
-
- /* Now delete it */
- odp_packet_free(ref_pkt[0]);
- CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
-
- /* Create references */
- ref_pkt[0] = odp_packet_ref(segmented_base_pkt, offset[0]);
- CU_ASSERT_FATAL(ref_pkt[0] != ODP_PACKET_INVALID);
-
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_has_ref(segmented_base_pkt) == 1);
- }
-
- ref_pkt[1] = odp_packet_ref(segmented_base_pkt, offset[1]);
- CU_ASSERT_FATAL(ref_pkt[1] != ODP_PACKET_INVALID);
-
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_has_ref(segmented_base_pkt) == 1);
- }
-
- /* Verify reference lengths */
- CU_ASSERT(odp_packet_len(ref_pkt[0]) == segmented_pkt_len - offset[0]);
- CU_ASSERT(odp_packet_len(ref_pkt[1]) == segmented_pkt_len - offset[1]);
-
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) == 0);
- }
-
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 0);
- }
-
- /* Free the base pkts -- references should still be valid */
- odp_packet_free(base_pkt);
- odp_packet_free(segmented_base_pkt);
-
- packet_compare_offset(ref_pkt[0], 0,
- segmented_test_packet, offset[0],
- segmented_pkt_len - offset[0]);
- packet_compare_offset(ref_pkt[1], 0,
- segmented_test_packet, offset[1],
- segmented_pkt_len - offset[1]);
-
- /* Verify we can modify the refs */
- hr[0] = odp_packet_headroom(ref_pkt[0]);
- hr[1] = odp_packet_headroom(ref_pkt[1]);
-
- CU_ASSERT(odp_packet_push_head(ref_pkt[0], hr[0]) != NULL);
-
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) == hr[0]);
- }
-
- CU_ASSERT(odp_packet_len(ref_pkt[0]) ==
- hr[0] + segmented_pkt_len - offset[0]);
-
- CU_ASSERT(odp_packet_pull_head(ref_pkt[0], hr[0] / 2) != NULL);
-
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) ==
- hr[0] - (hr[0] / 2));
- }
-
- if (hr[1] > 0) {
- CU_ASSERT(odp_packet_push_head(ref_pkt[1], 1) != NULL);
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 1);
- }
- CU_ASSERT(odp_packet_len(ref_pkt[1]) ==
- 1 + segmented_pkt_len - offset[1]);
- CU_ASSERT(odp_packet_pull_head(ref_pkt[1], 1) != NULL);
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 0);
- }
- CU_ASSERT(odp_packet_len(ref_pkt[1]) ==
- segmented_pkt_len - offset[1]);
- }
-
- odp_packet_free(ref_pkt[0]);
- odp_packet_free(ref_pkt[1]);
-
- /* Verify we can modify base packet after reference is created */
- base_pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
-
- ref_pkt[1] = odp_packet_ref(base_pkt, offset[1]);
- CU_ASSERT_FATAL(ref_pkt[1] != ODP_PACKET_INVALID);
- ref_len[1] = odp_packet_len(ref_pkt[1]);
- CU_ASSERT(ref_len[1] == odp_packet_len(base_pkt) - offset[1]);
-
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 0);
- }
-
- CU_ASSERT(odp_packet_push_head(base_pkt, base_hr / 2) != NULL);
-
- if (odp_packet_has_ref(base_pkt) == 1) {
- CU_ASSERT(odp_packet_unshared_len(base_pkt) ==
- base_hr / 2 + offset[1]);
- }
- CU_ASSERT(odp_packet_len(ref_pkt[1]) == ref_len[1]);
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 0);
- }
-
- ref_pkt[0] = odp_packet_ref(base_pkt, offset[0]);
- CU_ASSERT_FATAL(ref_pkt[0] != ODP_PACKET_INVALID);
- ref_len[0] = odp_packet_len(ref_pkt[0]);
- CU_ASSERT(ref_len[0] = odp_packet_len(base_pkt) - offset[0]);
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) == 0);
- }
-
- CU_ASSERT(odp_packet_push_head(base_pkt,
- base_hr - base_hr / 2) != NULL);
- if (odp_packet_has_ref(base_pkt) == 1) {
- CU_ASSERT(odp_packet_unshared_len(base_pkt) ==
- base_hr - base_hr / 2 + offset[0]);
- }
- CU_ASSERT(odp_packet_len(ref_pkt[1]) == ref_len[1]);
- CU_ASSERT(odp_packet_len(ref_pkt[0]) == ref_len[0]);
-
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == 0);
- }
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) == 0);
- }
-
- hr[0] = odp_packet_headroom(ref_pkt[0]);
- hr[1] = odp_packet_headroom(ref_pkt[1]);
- CU_ASSERT(odp_packet_push_head(ref_pkt[0], hr[0]) != NULL);
- CU_ASSERT(odp_packet_push_head(ref_pkt[1], hr[1]) != NULL);
- if (odp_packet_has_ref(ref_pkt[0]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[0]) == hr[0]);
- }
- if (odp_packet_has_ref(ref_pkt[1]) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(ref_pkt[1]) == hr[1]);
- }
- if (odp_packet_has_ref(base_pkt) == 1) {
- /* CU_ASSERT needs braces */
- CU_ASSERT(odp_packet_unshared_len(base_pkt) ==
- base_hr - base_hr / 2 + offset[0]);
- }
-
- odp_packet_free(base_pkt);
- odp_packet_free(ref_pkt[0]);
- odp_packet_free(ref_pkt[1]);
-}
-
-odp_testinfo_t packet_suite[] = {
- ODP_TEST_INFO(packet_test_alloc_free),
- ODP_TEST_INFO(packet_test_alloc_free_multi),
- ODP_TEST_INFO(packet_test_alloc_segmented),
- ODP_TEST_INFO(packet_test_basic_metadata),
- ODP_TEST_INFO(packet_test_debug),
- ODP_TEST_INFO(packet_test_segments),
- ODP_TEST_INFO(packet_test_length),
- ODP_TEST_INFO(packet_test_prefetch),
- ODP_TEST_INFO(packet_test_headroom),
- ODP_TEST_INFO(packet_test_tailroom),
- ODP_TEST_INFO(packet_test_context),
- ODP_TEST_INFO(packet_test_event_conversion),
- ODP_TEST_INFO(packet_test_layer_offsets),
- ODP_TEST_INFO(packet_test_segment_last),
- ODP_TEST_INFO(packet_test_in_flags),
- ODP_TEST_INFO(packet_test_error_flags),
- ODP_TEST_INFO(packet_test_add_rem_data),
- ODP_TEST_INFO(packet_test_copy),
- ODP_TEST_INFO(packet_test_copydata),
- ODP_TEST_INFO(packet_test_concatsplit),
- ODP_TEST_INFO(packet_test_concat_small),
- ODP_TEST_INFO(packet_test_concat_extend_trunc),
- ODP_TEST_INFO(packet_test_extend_small),
- ODP_TEST_INFO(packet_test_extend_large),
- ODP_TEST_INFO(packet_test_extend_mix),
- ODP_TEST_INFO(packet_test_extend_ref),
- ODP_TEST_INFO(packet_test_align),
- ODP_TEST_INFO(packet_test_offset),
- ODP_TEST_INFO(packet_test_ref),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t packet_suites[] = {
- { .pName = "packet tests",
- .pTests = packet_suite,
- .pInitFunc = packet_suite_init,
- .pCleanupFunc = packet_suite_term,
- },
- ODP_SUITE_INFO_NULL,
-};
-
-int packet_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(packet_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/packet/packet.h b/test/common_plat/validation/api/packet/packet.h
deleted file mode 100644
index 783b7a117..000000000
--- a/test/common_plat/validation/api/packet/packet.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_PACKET_H_
-#define _ODP_TEST_PACKET_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void packet_test_alloc_free(void);
-void packet_test_alloc_free_multi(void);
-void packet_test_alloc_segmented(void);
-void packet_test_event_conversion(void);
-void packet_test_basic_metadata(void);
-void packet_test_length(void);
-void packet_test_prefetch(void);
-void packet_test_debug(void);
-void packet_test_context(void);
-void packet_test_layer_offsets(void);
-void packet_test_headroom(void);
-void packet_test_tailroom(void);
-void packet_test_segments(void);
-void packet_test_segment_last(void);
-void packet_test_in_flags(void);
-void packet_test_error_flags(void);
-void packet_test_add_rem_data(void);
-void packet_test_copy(void);
-void packet_test_copydata(void);
-void packet_test_concatsplit(void);
-void packet_test_concat_small(void);
-void packet_test_concat_extend_trunc(void);
-void packet_test_extend_small(void);
-void packet_test_extend_large(void);
-void packet_test_extend_mix(void);
-void packet_test_extend_ref(void);
-void packet_test_align(void);
-void packet_test_offset(void);
-void packet_test_ref(void);
-
-/* test arrays: */
-extern odp_testinfo_t packet_suite[];
-
-/* test array init/term functions: */
-int packet_suite_init(void);
-int packet_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t packet_suites[];
-
-/* main test program: */
-int packet_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/packet/packet_main.c b/test/common_plat/validation/api/packet/packet_main.c
deleted file mode 100644
index 511bb104b..000000000
--- a/test/common_plat/validation/api/packet/packet_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "packet.h"
-
-int main(int argc, char *argv[])
-{
- return packet_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/pktio/Makefile.am b/test/common_plat/validation/api/pktio/Makefile.am
deleted file mode 100644
index c6368fba3..000000000
--- a/test/common_plat/validation/api/pktio/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestpktio.la
-libtestpktio_la_SOURCES = pktio.c
-
-test_PROGRAMS = pktio_main$(EXEEXT)
-dist_pktio_main_SOURCES = pktio_main.c parser.c
-pktio_main_LDADD = libtestpktio.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = pktio.h parser.h
diff --git a/test/common_plat/validation/api/pktio/parser.c b/test/common_plat/validation/api/pktio/parser.c
deleted file mode 100644
index ad7101d08..000000000
--- a/test/common_plat/validation/api/pktio/parser.c
+++ /dev/null
@@ -1,545 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-
-#include <odp/helper/odph_api.h>
-
-#include <stdlib.h>
-#include "parser.h"
-#include "pktio.h"
-
-#define MAX_NUM_IFACES 2
-#define PKT_POOL_NUM 256
-#define PKT_POOL_BUF_LEN (2 * 1024)
-
-/**
- * local container for pktio attributes
- */
-typedef struct {
- const char *name;
- odp_pktio_t hdl;
- odp_pktout_queue_t pktout;
- odp_pktin_queue_t pktin;
-} pktio_info_t;
-
-/** Interface names used for testing */
-static const char *iface_name[MAX_NUM_IFACES];
-
-/** Test interfaces */
-pktio_info_t pktios[MAX_NUM_IFACES];
-pktio_info_t *pktio_a;
-pktio_info_t *pktio_b;
-
-/** Number of interfaces being used (1=loopback, 2=pair) */
-static int num_ifaces;
-
-/** While testing real-world interfaces additional time may be needed for
- * external network to enable link to pktio interface that just become up.
- */
-static bool wait_for_network;
-
-/** Parser packet pool */
-odp_pool_t parser_pool = ODP_POOL_INVALID;
-
-static inline void wait_linkup(odp_pktio_t pktio)
-{
- /* wait 1 second for link up */
- uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
- int wait_num = 100;
- int i;
- int ret = -1;
-
- for (i = 0; i < wait_num; i++) {
- ret = odp_pktio_link_status(pktio);
- if (ret < 0 || ret == 1)
- break;
- /* link is down, call status again after delay */
- odp_time_wait_ns(wait_ns);
- }
-}
-
-static int pkt_pool_create(void)
-{
- odp_pool_capability_t capa;
- odp_pool_param_t params;
-
- if (odp_pool_capability(&capa) != 0) {
- printf("Error: unable to query pool capability.\n");
- return -1;
- }
-
- if (capa.pkt.max_num && capa.pkt.max_num < PKT_POOL_NUM) {
- printf("Error: packet pool size not supported.\n");
- printf("MAX: %" PRIu32 "\n", capa.pkt.max_num);
- return -1;
- } else if (capa.pkt.max_len && capa.pkt.max_len < PKT_POOL_BUF_LEN) {
- printf("Error: packet length not supported.\n");
- return -1;
- } else if (capa.pkt.max_seg_len &&
- capa.pkt.max_seg_len < PKT_POOL_BUF_LEN) {
- printf("Error: segment length not supported.\n");
- return -1;
- }
-
- odp_pool_param_init(&params);
- params.pkt.seg_len = PKT_POOL_BUF_LEN;
- params.pkt.len = PKT_POOL_BUF_LEN;
- params.pkt.num = PKT_POOL_NUM;
- params.type = ODP_POOL_PACKET;
-
- parser_pool = odp_pool_create("pkt_pool_default", &params);
- if (parser_pool == ODP_POOL_INVALID) {
- printf("Error: packet pool create failed.\n");
- return -1;
- }
-
- return 0;
-}
-
-static odp_pktio_t create_pktio(int iface_idx, odp_pool_t pool)
-{
- odp_pktio_t pktio;
- odp_pktio_config_t config;
- odp_pktio_param_t pktio_param;
- const char *iface = iface_name[iface_idx];
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
- pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
-
- pktio = odp_pktio_open(iface, pool, &pktio_param);
- if (pktio == ODP_PKTIO_INVALID) {
- printf("Error: failed to open %s\n", iface);
- return ODP_PKTIO_INVALID;
- }
-
- odp_pktio_config_init(&config);
- config.parser.layer = ODP_PKTIO_PARSER_LAYER_ALL;
- if (odp_pktio_config(pktio, &config)) {
- printf("Error: failed to configure %s\n", iface);
- return ODP_PKTIO_INVALID;
- }
-
- /* By default, single input and output queue is used */
- if (odp_pktin_queue_config(pktio, NULL)) {
- printf("Error: failed to config input queue for %s\n", iface);
- return ODP_PKTIO_INVALID;
- }
- if (odp_pktout_queue_config(pktio, NULL)) {
- printf("Error: failed to config output queue for %s\n", iface);
- return ODP_PKTIO_INVALID;
- }
-
- if (wait_for_network)
- odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
-
- return pktio;
-}
-
-static odp_packet_t create_packet(const uint8_t *data, uint32_t len)
-{
- odp_packet_t pkt;
-
- pkt = odp_packet_alloc(parser_pool, len);
- if (pkt == ODP_PACKET_INVALID)
- return ODP_PACKET_INVALID;
-
- if (odp_packet_copy_from_mem(pkt, 0, len, data)) {
- printf("Error: failed to copy test packet data\n");
- odp_packet_free(pkt);
- return ODP_PACKET_INVALID;
- }
-
- odp_packet_l2_offset_set(pkt, 0);
-
- return pkt;
-}
-
-/**
- * Receive incoming packets and compare them to the original. Function returns
- * a valid packet handle only when the received packet matches to the original
- * packet.
- */
-static odp_packet_t recv_and_cmp_packet(odp_pktin_queue_t pktin,
- odp_packet_t orig_pkt, uint64_t ns)
-{
- odp_packet_t pkt = ODP_PACKET_INVALID;
- odp_time_t wait_time, end;
- uint32_t orig_len;
- uint8_t *orig_data;
-
- orig_len = odp_packet_len(orig_pkt);
- orig_data = odp_packet_data(orig_pkt);
- wait_time = odp_time_local_from_ns(ns);
- end = odp_time_sum(odp_time_local(), wait_time);
-
- do {
- int ret;
- odp_packet_t tmp_pkt;
-
- ret = odp_pktin_recv(pktin, &tmp_pkt, 1);
- if (ret < 0)
- break;
-
- if (ret == 1) {
- uint32_t len;
- uint8_t *data;
-
- len = odp_packet_len(tmp_pkt);
- data = odp_packet_data(tmp_pkt);
-
- if (len == orig_len &&
- memcmp(data, orig_data, len) == 0) {
- pkt = tmp_pkt;
- break;
- }
- odp_packet_free(tmp_pkt);
- }
- } while (odp_time_cmp(end, odp_time_local()) > 0);
-
- return pkt;
-}
-
-/**
- * Creates a test packet from data array and loops it through the test pktio
- * interfaces forcing packet parsing.
- */
-static odp_packet_t loopback_packet(pktio_info_t *pktio_a,
- pktio_info_t *pktio_b, const uint8_t *data,
- uint32_t len)
-{
- odp_packet_t pkt;
- odp_packet_t sent_pkt;
-
- pkt = create_packet(data, len);
- if (pkt == ODP_PACKET_INVALID) {
- CU_FAIL("failed to generate test packet");
- return ODP_PACKET_INVALID;
- }
-
- pktio_pkt_set_macs(pkt, pktio_a->hdl, pktio_b->hdl);
-
- sent_pkt = odp_packet_copy(pkt, parser_pool);
- if (sent_pkt == ODP_PACKET_INVALID) {
- CU_FAIL_FATAL("failed to copy test packet");
- odp_packet_free(pkt);
- return ODP_PACKET_INVALID;
- }
-
- while (1) {
- int ret = odp_pktout_send(pktio_a->pktout, &pkt, 1);
-
- if (ret < 0) {
- CU_FAIL_FATAL("failed to send test packet");
- odp_packet_free(pkt);
- odp_packet_free(sent_pkt);
- return ODP_PACKET_INVALID;
- }
- if (ret == 1)
- break;
- }
-
- /* and wait for them to arrive back */
- pkt = recv_and_cmp_packet(pktio_b->pktin, sent_pkt, ODP_TIME_SEC_IN_NS);
- odp_packet_free(sent_pkt);
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_input(pkt) == pktio_b->hdl);
- CU_ASSERT(odp_packet_has_error(pkt) == 0);
-
- return pkt;
-}
-
-void parser_test_arp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_arp,
- sizeof(test_packet_arp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_arp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv4(pkt));
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv4_icmp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_icmp,
- sizeof(test_packet_ipv4_icmp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv4(pkt));
- CU_ASSERT(odp_packet_has_icmp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
- CU_ASSERT(!odp_packet_has_udp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv4_tcp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_tcp,
- sizeof(test_packet_ipv4_tcp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv4(pkt));
- CU_ASSERT(odp_packet_has_tcp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
- CU_ASSERT(!odp_packet_has_udp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv4_udp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_udp,
- sizeof(test_packet_ipv4_udp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv4(pkt));
- CU_ASSERT(odp_packet_has_udp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_vlan_ipv4_udp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv4_udp,
- sizeof(test_packet_vlan_ipv4_udp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_vlan(pkt));
- CU_ASSERT(odp_packet_has_ipv4(pkt));
- CU_ASSERT(odp_packet_has_udp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_vlan_qinq_ipv4_udp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_qinq_ipv4_udp,
- sizeof(test_packet_vlan_qinq_ipv4_udp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_vlan(pkt));
- CU_ASSERT(odp_packet_has_vlan_qinq(pkt));
- CU_ASSERT(odp_packet_has_ipv4(pkt));
- CU_ASSERT(odp_packet_has_udp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv6(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv6_icmp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_icmp,
- sizeof(test_packet_ipv6_icmp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv6(pkt));
- CU_ASSERT(odp_packet_has_icmp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv4(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
- CU_ASSERT(!odp_packet_has_udp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv6_tcp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_tcp,
- sizeof(test_packet_ipv6_tcp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv6(pkt));
- CU_ASSERT(odp_packet_has_tcp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv4(pkt));
- CU_ASSERT(!odp_packet_has_udp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_ipv6_udp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_udp,
- sizeof(test_packet_ipv6_udp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_ipv6(pkt));
- CU_ASSERT(odp_packet_has_udp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv4(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
-
- odp_packet_free(pkt);
-}
-
-void parser_test_vlan_ipv6_udp(void)
-{
- odp_packet_t pkt;
-
- pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv6_udp,
- sizeof(test_packet_vlan_ipv6_udp));
- CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_has_eth(pkt));
- CU_ASSERT(odp_packet_has_vlan(pkt));
- CU_ASSERT(odp_packet_has_ipv6(pkt));
- CU_ASSERT(odp_packet_has_udp(pkt));
-
- CU_ASSERT(!odp_packet_has_ipv4(pkt));
- CU_ASSERT(!odp_packet_has_tcp(pkt));
-
- odp_packet_free(pkt);
-}
-
-int parser_suite_init(void)
-{
- int i;
-
- if (getenv("ODP_WAIT_FOR_NETWORK"))
- wait_for_network = true;
-
- iface_name[0] = getenv("ODP_PKTIO_IF0");
- iface_name[1] = getenv("ODP_PKTIO_IF1");
- num_ifaces = 1;
-
- if (!iface_name[0]) {
- printf("No interfaces specified, using default \"loop\".\n");
- iface_name[0] = "loop";
- } else if (!iface_name[1]) {
- printf("Using loopback interface: %s\n", iface_name[0]);
- } else {
- num_ifaces = 2;
- printf("Using paired interfaces: %s %s\n",
- iface_name[0], iface_name[1]);
- }
-
- if (pkt_pool_create() != 0) {
- printf("Error: failed to create parser pool\n");
- return -1;
- }
-
- /* Create pktios and associate input/output queues */
- for (i = 0; i < num_ifaces; ++i) {
- pktio_info_t *io;
-
- io = &pktios[i];
- io->name = iface_name[i];
- io->hdl = create_pktio(i, parser_pool);
- if (io->hdl == ODP_PKTIO_INVALID) {
- printf("Error: failed to open iface");
- return -1;
- }
-
- if (odp_pktout_queue(io->hdl, &io->pktout, 1) != 1) {
- printf("Error: failed to start iface: %s\n", io->name);
- return -1;
- }
-
- if (odp_pktin_queue(io->hdl, &io->pktin, 1) != 1) {
- printf("Error: failed to start iface: %s\n", io->name);
- return -1;
- }
-
- if (odp_pktio_start(io->hdl)) {
- printf("Error: failed to start iface: %s\n", io->name);
- return -1;
- }
-
- wait_linkup(io->hdl);
- }
-
- pktio_a = &pktios[0];
- pktio_b = &pktios[1];
- if (num_ifaces == 1)
- pktio_b = pktio_a;
-
- return 0;
-}
-
-int parser_suite_term(void)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < num_ifaces; ++i) {
- if (odp_pktio_stop(pktios[i].hdl)) {
- printf("Error: failed to stop pktio: %s\n",
- pktios[i].name);
- ret = -1;
- }
- if (odp_pktio_close(pktios[i].hdl)) {
- printf("Error: failed to close pktio: %s\n",
- pktios[i].name);
- ret = -1;
- }
- }
-
- if (odp_pool_destroy(parser_pool) != 0) {
- printf("Error: failed to destroy packet pool\n");
- ret = -1;
- }
-
- return ret;
-}
-
-/**
- * Certain tests can only be run with 'loop' pktio.
- */
-static int loop_pktio(void)
-{
- if (strcmp(iface_name[0], "loop") == 0)
- return ODP_TEST_ACTIVE;
- else
- return ODP_TEST_INACTIVE;
-}
-
-odp_testinfo_t parser_suite[] = {
- ODP_TEST_INFO(parser_test_arp),
- ODP_TEST_INFO(parser_test_ipv4_icmp),
- ODP_TEST_INFO(parser_test_ipv4_tcp),
- ODP_TEST_INFO(parser_test_ipv4_udp),
- ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv4_udp, loop_pktio),
- ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_qinq_ipv4_udp, loop_pktio),
- ODP_TEST_INFO(parser_test_ipv6_icmp),
- ODP_TEST_INFO(parser_test_ipv6_tcp),
- ODP_TEST_INFO(parser_test_ipv6_udp),
- ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv6_udp, loop_pktio),
- ODP_TEST_INFO_NULL
-};
diff --git a/test/common_plat/validation/api/pktio/parser.h b/test/common_plat/validation/api/pktio/parser.h
deleted file mode 100644
index 5cc2b988c..000000000
--- a/test/common_plat/validation/api/pktio/parser.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/* Copyright (c) 2017, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_PARSER_H_
-#define _ODP_TEST_PARSER_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void parser_test_arp(void);
-void parser_test_ipv4_icmp(void);
-void parser_test_ipv4_tcp(void);
-void parser_test_ipv4_udp(void);
-void parser_test_vlan_ipv4_udp(void);
-void parser_test_vlan_qinq_ipv4_udp(void);
-void parser_test_ipv6_icmp(void);
-void parser_test_ipv6_tcp(void);
-void parser_test_ipv6_udp(void);
-void parser_test_vlan_ipv6_udp(void);
-
-/* test array init/term functions: */
-int parser_suite_term(void);
-int parser_suite_init(void);
-
-/* test arrays: */
-extern odp_testinfo_t parser_suite[];
-
-/* Test packets without CRC */
-
-/**
- * ARP request
- */
-static const uint8_t test_packet_arp[] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x08, 0x06, 0x00, 0x01,
- 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0xC0, 0xA8, 0x01, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8,
- 0x01, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
- 0x0E, 0x0F, 0x10, 0x11
-};
-
-/**
- * ICMPv4 echo reply
- */
-static const uint8_t test_packet_ipv4_icmp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
- 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01,
- 0xF3, 0x7B, 0xC0, 0xA8, 0x01, 0x01, 0xC4, 0xA8,
- 0x01, 0x02, 0x00, 0x00, 0xB7, 0xAB, 0x00, 0x01,
- 0x00, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
- 0x0E, 0x0F, 0x10, 0x11
-};
-
-/**
- * IPv4 TCP
- */
-static const uint8_t test_packet_ipv4_tcp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
- 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06,
- 0xF3, 0x76, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
- 0x01, 0x01, 0x04, 0xD2, 0x10, 0xE1, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x50, 0x00,
- 0x00, 0x00, 0x0C, 0xCC, 0x00, 0x00, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05
-};
-
-/**
- * IPv4 UDP
- */
-static const uint8_t test_packet_ipv4_udp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x08, 0x00, 0x45, 0x00,
- 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
- 0xF3, 0x6B, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
- 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1A,
- 0x2F, 0x97, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
- 0x0E, 0x0F, 0x10, 0x11
-};
-
-/**
- * VLAN IPv4 UDP
- * - ID: 23
- */
-static const uint8_t test_packet_vlan_ipv4_udp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x17,
- 0x08, 0x00, 0x45, 0x00, 0x00, 0x2A, 0x00, 0x00,
- 0x00, 0x00, 0x40, 0x11, 0xF3, 0x6F, 0xC0, 0xA8,
- 0x01, 0x02, 0xC4, 0xA8, 0x01, 0x01, 0x00, 0x3F,
- 0x00, 0x3F, 0x00, 0x16, 0x4D, 0xBF, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0A, 0x0B, 0x0C, 0x0D
-};
-
-/**
- * VLAN Q-in-Q IPv4 UDP
- * - Outer: Tag Protocol ID 0x88a8, VLAN ID 1
- * - Inner: Tag Protocol ID 0x8100, VLAN ID 2
- */
-static const uint8_t test_packet_vlan_qinq_ipv4_udp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x88, 0xA8, 0x00, 0x01,
- 0x81, 0x00, 0x00, 0x02, 0x08, 0x00, 0x45, 0x00,
- 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
- 0xF3, 0x73, 0xC0, 0xA8, 0x01, 0x02, 0xC4, 0xA8,
- 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x12,
- 0x63, 0xDF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09
-};
-
-/**
- * ICMPv6 echo request
- */
-static const uint8_t test_packet_ipv6_icmp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
- 0x00, 0x00, 0x00, 0x08, 0x3A, 0xFF, 0xFE, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
- 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
- 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x80, 0x00,
- 0x1B, 0xC2, 0x00, 0x01, 0x00, 0x02
-};
-
-/**
- * IPv6 TCP
- */
-static const uint8_t test_packet_ipv6_tcp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
- 0x00, 0x00, 0x00, 0x14, 0x06, 0xFF, 0xFE, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
- 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
- 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x04, 0xD2,
- 0x10, 0xE1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x36, 0x37,
- 0x00, 0x00
-};
-
-/**
- * IPv6 UDP
- */
-static const uint8_t test_packet_ipv6_udp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x86, 0xDD, 0x60, 0x30,
- 0x00, 0x00, 0x00, 0x08, 0x11, 0xFF, 0xFE, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x09, 0xFF, 0xFE, 0x00, 0x04, 0x00, 0x35, 0x55,
- 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77,
- 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x00, 0x3F,
- 0x00, 0x3F, 0x00, 0x08, 0x9B, 0x68
-};
-
-/**
- * VLAN IPv6
- * - ID: 23
- */
-static const uint8_t test_packet_vlan_ipv6_udp[] = {
- 0x00, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x17,
- 0x86, 0xDD, 0x60, 0x30, 0x00, 0x00, 0x00, 0x08,
- 0x11, 0xFF, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x09, 0xFF, 0xFE, 0x00,
- 0x04, 0x00, 0x35, 0x55, 0x55, 0x55, 0x66, 0x66,
- 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x88, 0x88,
- 0x88, 0x88, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x08,
- 0x9B, 0x68
-};
-
-#endif
diff --git a/test/common_plat/validation/api/pktio/pktio.c b/test/common_plat/validation/api/pktio/pktio.c
deleted file mode 100644
index c476a7126..000000000
--- a/test/common_plat/validation/api/pktio/pktio.c
+++ /dev/null
@@ -1,2213 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-
-#include <odp/helper/odph_api.h>
-
-#include <stdlib.h>
-#include "pktio.h"
-#include "parser.h"
-
-#define PKT_BUF_NUM 32
-#define PKT_BUF_SIZE (9 * 1024)
-#define PKT_LEN_NORMAL 64
-#define PKT_LEN_MAX (PKT_BUF_SIZE - ODPH_ETHHDR_LEN - \
- ODPH_IPV4HDR_LEN - ODPH_UDPHDR_LEN)
-
-#define USE_MTU 0
-#define MAX_NUM_IFACES 2
-#define TEST_SEQ_INVALID ((uint32_t)~0)
-#define TEST_SEQ_MAGIC 0x92749451
-#define TX_BATCH_LEN 4
-#define MAX_QUEUES 128
-
-#define PKTIN_TS_INTERVAL (50 * ODP_TIME_MSEC_IN_NS)
-#define PKTIN_TS_MIN_RES 1000
-#define PKTIN_TS_MAX_RES 10000000000
-#define PKTIN_TS_CMP_RES 1
-
-#define PKTIO_SRC_MAC {1, 2, 3, 4, 5, 6}
-#define PKTIO_DST_MAC {6, 5, 4, 3, 2, 1}
-#undef DEBUG_STATS
-
-/** interface names used for testing */
-static const char *iface_name[MAX_NUM_IFACES];
-
-/** number of interfaces being used (1=loopback, 2=pair) */
-static int num_ifaces;
-
-/** while testing real-world interfaces additional time may be
- needed for external network to enable link to pktio
- interface that just become up.*/
-static bool wait_for_network;
-
-/** local container for pktio attributes */
-typedef struct {
- const char *name;
- odp_pktio_t id;
- odp_pktout_queue_t pktout;
- odp_queue_t queue_out;
- odp_queue_t inq;
- odp_pktin_mode_t in_mode;
-} pktio_info_t;
-
-/** magic number and sequence at start of UDP payload */
-typedef struct ODP_PACKED {
- odp_u32be_t magic;
- odp_u32be_t seq;
-} pkt_head_t;
-
-/** magic number at end of UDP payload */
-typedef struct ODP_PACKED {
- odp_u32be_t magic;
-} pkt_tail_t;
-
-/** Run mode */
-typedef enum {
- PKT_POOL_UNSEGMENTED,
- PKT_POOL_SEGMENTED,
-} pkt_segmented_e;
-
-typedef enum {
- TXRX_MODE_SINGLE,
- TXRX_MODE_MULTI,
- TXRX_MODE_MULTI_EVENT
-} txrx_mode_e;
-
-typedef enum {
- RECV_TMO,
- RECV_MQ_TMO,
- RECV_MQ_TMO_NO_IDX,
-} recv_tmo_mode_e;
-
-/** size of transmitted packets */
-static uint32_t packet_len = PKT_LEN_NORMAL;
-
-/** default packet pool */
-odp_pool_t default_pkt_pool = ODP_POOL_INVALID;
-
-/** sequence number of IP packets */
-odp_atomic_u32_t ip_seq;
-
-/** Type of pool segmentation */
-pkt_segmented_e pool_segmentation = PKT_POOL_UNSEGMENTED;
-
-odp_pool_t pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
-
-static inline void _pktio_wait_linkup(odp_pktio_t pktio)
-{
- /* wait 1 second for link up */
- uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
- int wait_num = 100;
- int i;
- int ret = -1;
-
- for (i = 0; i < wait_num; i++) {
- ret = odp_pktio_link_status(pktio);
- if (ret < 0 || ret == 1)
- break;
- /* link is down, call status again after delay */
- odp_time_wait_ns(wait_ns);
- }
-
- if (ret != -1) {
- /* assert only if link state supported and
- * it's down. */
- CU_ASSERT_FATAL(ret == 1);
- }
-}
-
-static void set_pool_len(odp_pool_param_t *params, odp_pool_capability_t *capa)
-{
- uint32_t len;
- uint32_t seg_len;
-
- len = (capa->pkt.max_len && capa->pkt.max_len < PKT_BUF_SIZE) ?
- capa->pkt.max_len : PKT_BUF_SIZE;
- seg_len = capa->pkt.max_seg_len ? capa->pkt.max_seg_len : PKT_BUF_SIZE;
-
- switch (pool_segmentation) {
- case PKT_POOL_SEGMENTED:
- /* Force segment to minimum size */
- params->pkt.seg_len = 0;
- params->pkt.len = len;
- break;
- case PKT_POOL_UNSEGMENTED:
- default:
- params->pkt.seg_len = seg_len;
- params->pkt.len = len;
- break;
- }
-}
-
-void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst)
-{
- uint32_t len;
- odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
- int ret;
-
- ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
- CU_ASSERT(ret == ODPH_ETHADDR_LEN);
- CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
-
- ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
- CU_ASSERT(ret == ODPH_ETHADDR_LEN);
- CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
-}
-
-static uint32_t pktio_pkt_set_seq(odp_packet_t pkt)
-{
- static uint32_t tstseq;
- size_t off;
- pkt_head_t head;
- pkt_tail_t tail;
-
- off = odp_packet_l4_offset(pkt);
- if (off == ODP_PACKET_OFFSET_INVALID) {
- CU_FAIL("packet L4 offset not set");
- return TEST_SEQ_INVALID;
- }
-
- head.magic = TEST_SEQ_MAGIC;
- head.seq = tstseq;
-
- off += ODPH_UDPHDR_LEN;
- if (odp_packet_copy_from_mem(pkt, off, sizeof(head), &head) != 0)
- return TEST_SEQ_INVALID;
-
- tail.magic = TEST_SEQ_MAGIC;
- off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
- if (odp_packet_copy_from_mem(pkt, off, sizeof(tail), &tail) != 0)
- return TEST_SEQ_INVALID;
-
- tstseq++;
-
- return head.seq;
-}
-
-static uint32_t pktio_pkt_seq(odp_packet_t pkt)
-{
- size_t off;
- uint32_t seq = TEST_SEQ_INVALID;
- pkt_head_t head;
- pkt_tail_t tail;
-
- if (pkt == ODP_PACKET_INVALID) {
- fprintf(stderr, "error: pkt invalid\n");
- return TEST_SEQ_INVALID;
- }
-
- off = odp_packet_l4_offset(pkt);
- if (off == ODP_PACKET_OFFSET_INVALID) {
- fprintf(stderr, "error: offset invalid\n");
- return TEST_SEQ_INVALID;
- }
-
- off += ODPH_UDPHDR_LEN;
- if (odp_packet_copy_to_mem(pkt, off, sizeof(head), &head) != 0) {
- fprintf(stderr, "error: header copy failed\n");
- return TEST_SEQ_INVALID;
- }
-
- if (head.magic != TEST_SEQ_MAGIC) {
- fprintf(stderr, "error: header magic invalid %" PRIu32 "\n",
- head.magic);
- return TEST_SEQ_INVALID;
- }
-
- if (odp_packet_len(pkt) == packet_len) {
- off = packet_len - sizeof(tail);
- if (odp_packet_copy_to_mem(pkt, off, sizeof(tail),
- &tail) != 0) {
- fprintf(stderr, "error: header copy failed\n");
- return TEST_SEQ_INVALID;
- }
-
- if (tail.magic == TEST_SEQ_MAGIC) {
- seq = head.seq;
- CU_ASSERT(seq != TEST_SEQ_INVALID);
- } else {
- fprintf(stderr,
- "error: tail magic invalid %" PRIu32 "\n",
- tail.magic);
- }
- } else {
- fprintf(stderr,
- "error: packet length invalid: "
- "%" PRIu32 "(%" PRIu32 ")\n",
- odp_packet_len(pkt), packet_len);
- }
-
- return seq;
-}
-
-static uint32_t pktio_init_packet(odp_packet_t pkt)
-{
- odph_ethhdr_t *eth;
- odph_ipv4hdr_t *ip;
- odph_udphdr_t *udp;
- char *buf;
- uint16_t seq;
- uint8_t src_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_SRC_MAC;
- uint8_t dst_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_DST_MAC;
- int pkt_len = odp_packet_len(pkt);
-
- buf = odp_packet_data(pkt);
-
- /* Ethernet */
- odp_packet_l2_offset_set(pkt, 0);
- eth = (odph_ethhdr_t *)buf;
- memcpy(eth->src.addr, src_mac, ODPH_ETHADDR_LEN);
- memcpy(eth->dst.addr, dst_mac, ODPH_ETHADDR_LEN);
- eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
-
- /* IP */
- odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
- ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
- ip->dst_addr = odp_cpu_to_be_32(0x0a000064);
- ip->src_addr = odp_cpu_to_be_32(0x0a000001);
- ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
- ip->tot_len = odp_cpu_to_be_16(pkt_len - ODPH_ETHHDR_LEN);
- ip->ttl = 128;
- ip->proto = ODPH_IPPROTO_UDP;
- seq = odp_atomic_fetch_inc_u32(&ip_seq);
- ip->id = odp_cpu_to_be_16(seq);
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
-
- /* UDP */
- odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- udp = (odph_udphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
- udp->src_port = odp_cpu_to_be_16(12049);
- udp->dst_port = odp_cpu_to_be_16(12050);
- udp->length = odp_cpu_to_be_16(pkt_len -
- ODPH_ETHHDR_LEN - ODPH_IPV4HDR_LEN);
- udp->chksum = 0;
-
- return pktio_pkt_set_seq(pkt);
-}
-
-static int pktio_fixup_checksums(odp_packet_t pkt)
-{
- odph_ipv4hdr_t *ip;
- odph_udphdr_t *udp;
- uint32_t len;
-
- ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len);
-
- if (ip->proto != ODPH_IPPROTO_UDP) {
- CU_FAIL("unexpected L4 protocol");
- return -1;
- }
-
- udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, &len);
-
- ip->chksum = 0;
- odph_ipv4_csum_update(pkt);
- udp->chksum = 0;
- udp->chksum = odph_ipv4_udp_chksum(pkt);
-
- return 0;
-}
-
-static int default_pool_create(void)
-{
- odp_pool_param_t params;
- odp_pool_capability_t pool_capa;
- char pool_name[ODP_POOL_NAME_LEN];
-
- if (odp_pool_capability(&pool_capa) != 0)
- return -1;
-
- if (default_pkt_pool != ODP_POOL_INVALID)
- return -1;
-
- odp_pool_param_init(&params);
- set_pool_len(&params, &pool_capa);
- params.pkt.num = PKT_BUF_NUM;
- params.type = ODP_POOL_PACKET;
-
- snprintf(pool_name, sizeof(pool_name),
- "pkt_pool_default_%d", pool_segmentation);
- default_pkt_pool = odp_pool_create(pool_name, &params);
- if (default_pkt_pool == ODP_POOL_INVALID)
- return -1;
-
- return 0;
-}
-
-static odp_pktio_t create_pktio(int iface_idx, odp_pktin_mode_t imode,
- odp_pktout_mode_t omode)
-{
- odp_pktio_t pktio;
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t pktin_param;
- const char *iface = iface_name[iface_idx];
-
- odp_pktio_param_init(&pktio_param);
-
- pktio_param.in_mode = imode;
- pktio_param.out_mode = omode;
-
- pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- CU_ASSERT(odp_pktio_to_u64(pktio) !=
- odp_pktio_to_u64(ODP_PKTIO_INVALID));
-
- odp_pktin_queue_param_init(&pktin_param);
-
- /* Atomic queue when in scheduled mode */
- pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
-
- /* By default, single input and output queue in all modes. Config can
- * be overridden before starting the interface. */
- CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0);
- CU_ASSERT(odp_pktout_queue_config(pktio, NULL) == 0);
-
- if (wait_for_network)
- odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
-
- return pktio;
-}
-
-static int flush_input_queue(odp_pktio_t pktio, odp_pktin_mode_t imode)
-{
- odp_event_t ev;
- odp_queue_t queue = ODP_QUEUE_INVALID;
-
- if (imode == ODP_PKTIN_MODE_QUEUE) {
- /* Assert breaks else-if without brackets */
- CU_ASSERT_FATAL(odp_pktin_event_queue(pktio, &queue, 1) == 1);
- } else if (imode == ODP_PKTIN_MODE_DIRECT) {
- return 0;
- }
-
- /* flush any pending events */
- while (1) {
- if (queue != ODP_QUEUE_INVALID)
- ev = odp_queue_deq(queue);
- else
- ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
-
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
- else
- break;
- }
-
- return 0;
-}
-
-static int create_packets(odp_packet_t pkt_tbl[], uint32_t pkt_seq[], int num,
- odp_pktio_t pktio_src, odp_pktio_t pktio_dst)
-{
- int i;
-
- for (i = 0; i < num; i++) {
- pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len);
- if (pkt_tbl[i] == ODP_PACKET_INVALID)
- break;
-
- pkt_seq[i] = pktio_init_packet(pkt_tbl[i]);
- if (pkt_seq[i] == TEST_SEQ_INVALID) {
- odp_packet_free(pkt_tbl[i]);
- break;
- }
-
- pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst);
-
- if (pktio_fixup_checksums(pkt_tbl[i]) != 0) {
- odp_packet_free(pkt_tbl[i]);
- break;
- }
- }
-
- return i;
-}
-
-static int get_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
- int num, txrx_mode_e mode)
-{
- odp_event_t evt_tbl[num];
- int num_evts = 0;
- int num_pkts = 0;
- int i;
-
- if (pktio_rx->in_mode == ODP_PKTIN_MODE_DIRECT) {
- odp_pktin_queue_t pktin;
-
- CU_ASSERT_FATAL(odp_pktin_queue(pktio_rx->id, &pktin, 1) == 1);
- return odp_pktin_recv(pktin, pkt_tbl, num);
- }
-
- if (mode == TXRX_MODE_MULTI) {
- if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
- num_evts = odp_queue_deq_multi(pktio_rx->inq, evt_tbl,
- num);
- else
- num_evts = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
- evt_tbl, num);
- } else {
- odp_event_t evt_tmp;
-
- if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
- evt_tmp = odp_queue_deq(pktio_rx->inq);
- else
- evt_tmp = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
-
- if (evt_tmp != ODP_EVENT_INVALID)
- evt_tbl[num_evts++] = evt_tmp;
- }
-
- /* convert events to packets, discarding any non-packet events */
- for (i = 0; i < num_evts; ++i) {
- if (odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET)
- pkt_tbl[num_pkts++] = odp_packet_from_event(evt_tbl[i]);
- else
- odp_event_free(evt_tbl[i]);
- }
-
- return num_pkts;
-}
-
-static int wait_for_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
- uint32_t seq_tbl[], int num, txrx_mode_e mode,
- uint64_t ns)
-{
- odp_time_t wait_time, end;
- int num_rx = 0;
- int i;
- odp_packet_t pkt_tmp[num];
-
- wait_time = odp_time_local_from_ns(ns);
- end = odp_time_sum(odp_time_local(), wait_time);
-
- do {
- int n = get_packets(pktio_rx, pkt_tmp, num - num_rx, mode);
-
- if (n < 0)
- break;
-
- for (i = 0; i < n; ++i) {
- if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx])
- pkt_tbl[num_rx++] = pkt_tmp[i];
- else
- odp_packet_free(pkt_tmp[i]);
- }
- } while (num_rx < num && odp_time_cmp(end, odp_time_local()) > 0);
-
- return num_rx;
-}
-
-static int recv_packets_tmo(odp_pktio_t pktio, odp_packet_t pkt_tbl[],
- uint32_t seq_tbl[], int num, recv_tmo_mode_e mode,
- uint64_t tmo, uint64_t ns)
-{
- odp_packet_t pkt_tmp[num];
- odp_pktin_queue_t pktin[MAX_QUEUES];
- odp_time_t ts1, ts2;
- int num_rx = 0;
- int num_q;
- int i;
- int n;
- unsigned from_val;
- unsigned *from = NULL;
-
- if (mode == RECV_MQ_TMO)
- from = &from_val;
-
- num_q = odp_pktin_queue(pktio, pktin, MAX_QUEUES);
- CU_ASSERT_FATAL(num_q > 0);
-
- /** Multiple odp_pktin_recv_tmo()/odp_pktin_recv_mq_tmo() calls may be
- * required to discard possible non-test packets. */
- do {
- ts1 = odp_time_global();
- if (mode == RECV_TMO)
- n = odp_pktin_recv_tmo(pktin[0], pkt_tmp, num - num_rx,
- tmo);
- else
- n = odp_pktin_recv_mq_tmo(pktin, (unsigned)num_q,
- from, pkt_tmp,
- num - num_rx, tmo);
- ts2 = odp_time_global();
-
- if (n <= 0)
- break;
- for (i = 0; i < n; i++) {
- if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx])
- pkt_tbl[num_rx++] = pkt_tmp[i];
- else
- odp_packet_free(pkt_tmp[i]);
- }
- if (mode == RECV_MQ_TMO)
- CU_ASSERT(from_val < (unsigned)num_q);
- } while (num_rx < num);
-
- if (tmo == ODP_PKTIN_WAIT)
- CU_ASSERT(num_rx == num);
- if (num_rx < num)
- CU_ASSERT(odp_time_to_ns(odp_time_diff(ts2, ts1)) >= ns);
-
- return num_rx;
-}
-
-static int send_packets(odp_pktout_queue_t pktout,
- odp_packet_t *pkt_tbl, unsigned pkts)
-{
- int ret;
- unsigned sent = 0;
-
- while (sent < pkts) {
- ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
-
- if (ret < 0) {
- CU_FAIL_FATAL("failed to send test packet");
- return -1;
- }
-
- sent += ret;
- }
-
- return 0;
-}
-
-static int send_packet_events(odp_queue_t queue,
- odp_packet_t *pkt_tbl, unsigned pkts)
-{
- int ret;
- unsigned i;
- unsigned sent = 0;
- odp_event_t ev_tbl[pkts];
-
- for (i = 0; i < pkts; i++)
- ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
-
- while (sent < pkts) {
- ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
-
- if (ret < 0) {
- CU_FAIL_FATAL("failed to send test packet as events");
- return -1;
- }
-
- sent += ret;
- }
-
- return 0;
-}
-
-static void pktio_txrx_multi(pktio_info_t *pktio_a, pktio_info_t *pktio_b,
- int num_pkts, txrx_mode_e mode)
-{
- odp_packet_t tx_pkt[num_pkts];
- odp_packet_t rx_pkt[num_pkts];
- uint32_t tx_seq[num_pkts];
- int i, ret, num_rx;
-
- if (packet_len == USE_MTU) {
- odp_pool_capability_t pool_capa;
- uint32_t mtu;
-
- mtu = odp_pktio_mtu(pktio_a->id);
- if (odp_pktio_mtu(pktio_b->id) < mtu)
- mtu = odp_pktio_mtu(pktio_b->id);
- CU_ASSERT_FATAL(mtu > 0);
- packet_len = mtu;
- if (packet_len > PKT_LEN_MAX)
- packet_len = PKT_LEN_MAX;
-
- CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
-
- if (pool_capa.pkt.max_len &&
- packet_len > pool_capa.pkt.max_len)
- packet_len = pool_capa.pkt.max_len;
- }
-
- /* generate test packets to send */
- ret = create_packets(tx_pkt, tx_seq, num_pkts, pktio_a->id,
- pktio_b->id);
- if (ret != num_pkts) {
- CU_FAIL("failed to generate test packets");
- return;
- }
-
- /* send packet(s) out */
- if (mode == TXRX_MODE_SINGLE) {
- for (i = 0; i < num_pkts; ++i) {
- ret = odp_pktout_send(pktio_a->pktout, &tx_pkt[i], 1);
- if (ret != 1) {
- CU_FAIL_FATAL("failed to send test packet");
- odp_packet_free(tx_pkt[i]);
- return;
- }
- }
- } else if (mode == TXRX_MODE_MULTI) {
- send_packets(pktio_a->pktout, tx_pkt, num_pkts);
- } else {
- send_packet_events(pktio_a->queue_out, tx_pkt, num_pkts);
- }
-
- /* and wait for them to arrive back */
- num_rx = wait_for_packets(pktio_b, rx_pkt, tx_seq,
- num_pkts, mode, ODP_TIME_SEC_IN_NS);
- CU_ASSERT(num_rx == num_pkts);
-
- for (i = 0; i < num_rx; ++i) {
- CU_ASSERT_FATAL(rx_pkt[i] != ODP_PACKET_INVALID);
- CU_ASSERT(odp_packet_input(rx_pkt[i]) == pktio_b->id);
- CU_ASSERT(odp_packet_has_error(rx_pkt[i]) == 0);
- odp_packet_free(rx_pkt[i]);
- }
-}
-
-static void test_txrx(odp_pktin_mode_t in_mode, int num_pkts,
- txrx_mode_e mode)
-{
- int ret, i, if_b;
- pktio_info_t pktios[MAX_NUM_IFACES];
- pktio_info_t *io;
-
- /* create pktios and associate input/output queues */
- for (i = 0; i < num_ifaces; ++i) {
- odp_pktout_queue_t pktout;
- odp_queue_t queue;
- odp_pktout_mode_t out_mode = ODP_PKTOUT_MODE_DIRECT;
-
- if (mode == TXRX_MODE_MULTI_EVENT)
- out_mode = ODP_PKTOUT_MODE_QUEUE;
-
- io = &pktios[i];
-
- io->name = iface_name[i];
- io->id = create_pktio(i, in_mode, out_mode);
- if (io->id == ODP_PKTIO_INVALID) {
- CU_FAIL("failed to open iface");
- return;
- }
-
- if (mode == TXRX_MODE_MULTI_EVENT) {
- CU_ASSERT_FATAL(odp_pktout_event_queue(io->id,
- &queue, 1) == 1);
- } else {
- CU_ASSERT_FATAL(odp_pktout_queue(io->id,
- &pktout, 1) == 1);
- io->pktout = pktout;
- queue = ODP_QUEUE_INVALID;
- }
-
- io->queue_out = queue;
- io->in_mode = in_mode;
-
- if (in_mode == ODP_PKTIN_MODE_QUEUE) {
- CU_ASSERT_FATAL(odp_pktin_event_queue(io->id, &queue, 1)
- == 1);
- io->inq = queue;
- } else {
- io->inq = ODP_QUEUE_INVALID;
- }
-
- ret = odp_pktio_start(io->id);
- CU_ASSERT(ret == 0);
-
- _pktio_wait_linkup(io->id);
- }
-
- /* if we have two interfaces then send through one and receive on
- * another but if there's only one assume it's a loopback */
- if_b = (num_ifaces == 1) ? 0 : 1;
- pktio_txrx_multi(&pktios[0], &pktios[if_b], num_pkts, mode);
-
- for (i = 0; i < num_ifaces; ++i) {
- ret = odp_pktio_stop(pktios[i].id);
- CU_ASSERT_FATAL(ret == 0);
- flush_input_queue(pktios[i].id, in_mode);
- ret = odp_pktio_close(pktios[i].id);
- CU_ASSERT(ret == 0);
- }
-}
-
-void pktio_test_plain_queue(void)
-{
- test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_SINGLE);
- test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_SINGLE);
-}
-
-void pktio_test_plain_multi(void)
-{
- test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI);
- test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI);
-}
-
-void pktio_test_plain_multi_event(void)
-{
- test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI_EVENT);
- test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT);
-}
-
-void pktio_test_sched_queue(void)
-{
- test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_SINGLE);
- test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_SINGLE);
-}
-
-void pktio_test_sched_multi(void)
-{
- test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI);
- test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI);
-}
-
-void pktio_test_sched_multi_event(void)
-{
- test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI_EVENT);
- test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT);
-}
-
-void pktio_test_recv(void)
-{
- test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_SINGLE);
-}
-
-void pktio_test_recv_multi(void)
-{
- test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI);
-}
-
-void pktio_test_recv_multi_event(void)
-{
- test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_MULTI_EVENT);
- test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT);
-}
-
-void pktio_test_recv_queue(void)
-{
- odp_pktio_t pktio_tx, pktio_rx;
- odp_pktio_t pktio[MAX_NUM_IFACES];
- odp_pktio_capability_t capa;
- odp_pktin_queue_param_t in_queue_param;
- odp_pktout_queue_param_t out_queue_param;
- odp_pktout_queue_t pktout_queue[MAX_QUEUES];
- odp_pktin_queue_t pktin_queue[MAX_QUEUES];
- odp_packet_t pkt_tbl[TX_BATCH_LEN];
- odp_packet_t tmp_pkt[TX_BATCH_LEN];
- uint32_t pkt_seq[TX_BATCH_LEN];
- odp_time_t wait_time, end;
- int num_rx = 0;
- int num_queues;
- int ret;
- int i;
-
- CU_ASSERT_FATAL(num_ifaces >= 1);
-
- /* Open and configure interfaces */
- for (i = 0; i < num_ifaces; ++i) {
- pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
-
- odp_pktin_queue_param_init(&in_queue_param);
- num_queues = capa.max_input_queues;
- in_queue_param.num_queues = num_queues;
- in_queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
- in_queue_param.hash_proto.proto.ipv4_udp = 1;
-
- ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
- CU_ASSERT_FATAL(ret == 0);
-
- odp_pktout_queue_param_init(&out_queue_param);
- out_queue_param.num_queues = capa.max_output_queues;
-
- ret = odp_pktout_queue_config(pktio[i], &out_queue_param);
- CU_ASSERT_FATAL(ret == 0);
-
- CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
- }
-
- for (i = 0; i < num_ifaces; ++i)
- _pktio_wait_linkup(pktio[i]);
-
- pktio_tx = pktio[0];
- if (num_ifaces > 1)
- pktio_rx = pktio[1];
- else
- pktio_rx = pktio_tx;
-
- /* Allocate and initialize test packets */
- ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
- pktio_rx);
- if (ret != TX_BATCH_LEN) {
- CU_FAIL("Failed to generate test packets");
- return;
- }
-
- /* Send packets */
- num_queues = odp_pktout_queue(pktio_tx, pktout_queue, MAX_QUEUES);
- CU_ASSERT_FATAL(num_queues > 0);
- if (num_queues > MAX_QUEUES)
- num_queues = MAX_QUEUES;
-
- ret = odp_pktout_send(pktout_queue[num_queues - 1], pkt_tbl,
- TX_BATCH_LEN);
- CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
-
- /* Receive packets */
- num_queues = odp_pktin_queue(pktio_rx, pktin_queue, MAX_QUEUES);
- CU_ASSERT_FATAL(num_queues > 0);
- if (num_queues > MAX_QUEUES)
- num_queues = MAX_QUEUES;
-
- wait_time = odp_time_local_from_ns(ODP_TIME_SEC_IN_NS);
- end = odp_time_sum(odp_time_local(), wait_time);
- do {
- int n = 0;
-
- for (i = 0; i < num_queues; i++) {
- n = odp_pktin_recv(pktin_queue[i], tmp_pkt,
- TX_BATCH_LEN);
- if (n != 0)
- break;
- }
- if (n < 0)
- break;
- for (i = 0; i < n; i++) {
- if (pktio_pkt_seq(tmp_pkt[i]) == pkt_seq[num_rx])
- pkt_tbl[num_rx++] = tmp_pkt[i];
- else
- odp_packet_free(tmp_pkt[i]);
- }
- } while (num_rx < TX_BATCH_LEN &&
- odp_time_cmp(end, odp_time_local()) > 0);
-
- CU_ASSERT(num_rx == TX_BATCH_LEN);
-
- for (i = 0; i < num_rx; i++)
- odp_packet_free(pkt_tbl[i]);
-
- for (i = 0; i < num_ifaces; i++) {
- CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
- CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
- }
-}
-
-static void test_recv_tmo(recv_tmo_mode_e mode)
-{
- odp_pktio_t pktio_tx, pktio_rx;
- odp_pktio_t pktio[MAX_NUM_IFACES];
- odp_pktio_capability_t capa;
- odp_pktin_queue_param_t in_queue_param;
- odp_pktout_queue_t pktout_queue;
- int test_pkt_count = 6;
- odp_packet_t pkt_tbl[test_pkt_count];
- uint32_t pkt_seq[test_pkt_count];
- uint64_t ns;
- unsigned num_q;
- int ret;
- int i;
-
- CU_ASSERT_FATAL(num_ifaces >= 1);
-
- /* Open and configure interfaces */
- for (i = 0; i < num_ifaces; ++i) {
- pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
-
- odp_pktin_queue_param_init(&in_queue_param);
- if (mode == RECV_TMO)
- num_q = 1;
- else
- num_q = (capa.max_input_queues < MAX_QUEUES) ?
- capa.max_input_queues : MAX_QUEUES;
- in_queue_param.num_queues = num_q;
- in_queue_param.hash_enable = (num_q > 1) ? 1 : 0;
- in_queue_param.hash_proto.proto.ipv4_udp = 1;
-
- ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
- CU_ASSERT_FATAL(ret == 0);
-
- CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
- }
-
- for (i = 0; i < num_ifaces; i++)
- _pktio_wait_linkup(pktio[i]);
-
- pktio_tx = pktio[0];
- pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
-
- ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
- CU_ASSERT_FATAL(ret > 0);
-
- memset(pkt_seq, 0, sizeof(pkt_seq));
-
- /* No packets sent yet, so should wait */
- ns = 100 * ODP_TIME_MSEC_IN_NS;
- ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode,
- odp_pktin_wait_time(ns), ns);
- CU_ASSERT(ret == 0);
-
- ret = create_packets(pkt_tbl, pkt_seq, test_pkt_count, pktio_tx,
- pktio_rx);
- CU_ASSERT_FATAL(ret == test_pkt_count);
-
- ret = odp_pktout_send(pktout_queue, pkt_tbl, test_pkt_count);
- CU_ASSERT_FATAL(ret == test_pkt_count);
-
- ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode,
- ODP_PKTIN_WAIT, 0);
- CU_ASSERT_FATAL(ret == 1);
-
- ret = recv_packets_tmo(pktio_rx, &pkt_tbl[1], &pkt_seq[1], 1, mode,
- ODP_PKTIN_NO_WAIT, 0);
- CU_ASSERT_FATAL(ret == 1);
-
- ret = recv_packets_tmo(pktio_rx, &pkt_tbl[2], &pkt_seq[2], 1, mode,
- odp_pktin_wait_time(0), 0);
- CU_ASSERT_FATAL(ret == 1);
-
- ret = recv_packets_tmo(pktio_rx, &pkt_tbl[3], &pkt_seq[3], 3, mode,
- odp_pktin_wait_time(ns), ns);
- CU_ASSERT_FATAL(ret == 3);
-
- for (i = 0; i < test_pkt_count; i++)
- odp_packet_free(pkt_tbl[i]);
-
- for (i = 0; i < num_ifaces; i++) {
- CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
- CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
- }
-}
-
-void pktio_test_recv_tmo(void)
-{
- test_recv_tmo(RECV_TMO);
-}
-
-void pktio_test_recv_mq_tmo(void)
-{
- test_recv_tmo(RECV_MQ_TMO);
- test_recv_tmo(RECV_MQ_TMO_NO_IDX);
-}
-
-void pktio_test_recv_mtu(void)
-{
- packet_len = USE_MTU;
- pktio_test_sched_multi();
- packet_len = PKT_LEN_NORMAL;
-}
-
-void pktio_test_mtu(void)
-{
- int ret;
- uint32_t mtu;
-
- odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- mtu = odp_pktio_mtu(pktio);
- CU_ASSERT(mtu > 0);
-
- printf(" %" PRIu32 " ", mtu);
-
- ret = odp_pktio_close(pktio);
- CU_ASSERT(ret == 0);
-}
-
-void pktio_test_promisc(void)
-{
- int ret;
- odp_pktio_capability_t capa;
-
- odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- ret = odp_pktio_promisc_mode(pktio);
- CU_ASSERT(ret >= 0);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
- if (!capa.set_op.op.promisc_mode) {
- printf("promiscuous mode not supported\n");
- ret = odp_pktio_close(pktio);
- CU_ASSERT(ret == 0);
- return;
- }
-
- ret = odp_pktio_promisc_mode_set(pktio, 1);
- CU_ASSERT(0 == ret);
-
- /* Verify that promisc mode set */
- ret = odp_pktio_promisc_mode(pktio);
- CU_ASSERT(1 == ret);
-
- ret = odp_pktio_promisc_mode_set(pktio, 0);
- CU_ASSERT(0 == ret);
-
- /* Verify that promisc mode is not set */
- ret = odp_pktio_promisc_mode(pktio);
- CU_ASSERT(0 == ret);
-
- ret = odp_pktio_close(pktio);
- CU_ASSERT(ret == 0);
-}
-
-void pktio_test_mac(void)
-{
- unsigned char mac_addr[ODP_PKTIO_MACADDR_MAXSIZE];
- int mac_len;
- int ret;
- odp_pktio_t pktio;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- printf("testing mac for %s\n", iface_name[0]);
-
- mac_len = odp_pktio_mac_addr(pktio, mac_addr,
- ODP_PKTIO_MACADDR_MAXSIZE);
- CU_ASSERT(ODPH_ETHADDR_LEN == mac_len);
- CU_ASSERT(ODP_PKTIO_MACADDR_MAXSIZE >= mac_len);
-
- printf(" %X:%X:%X:%X:%X:%X ",
- mac_addr[0], mac_addr[1], mac_addr[2],
- mac_addr[3], mac_addr[4], mac_addr[5]);
-
- /* Fail case: wrong addr_size. Expected <0. */
- mac_len = odp_pktio_mac_addr(pktio, mac_addr, 2);
- CU_ASSERT(mac_len < 0);
-
- ret = odp_pktio_close(pktio);
- CU_ASSERT(0 == ret);
-}
-
-void pktio_test_open(void)
-{
- odp_pktio_t pktio;
- odp_pktio_param_t pktio_param;
- int i;
-
- /* test the sequence open->close->open->close() */
- for (i = 0; i < 2; ++i) {
- pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
- CU_ASSERT(odp_pktio_close(pktio) == 0);
- }
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open("nothere", default_pkt_pool, &pktio_param);
- CU_ASSERT(pktio == ODP_PKTIO_INVALID);
-}
-
-void pktio_test_lookup(void)
-{
- odp_pktio_t pktio, pktio_inval;
- odp_pktio_param_t pktio_param;
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
- CU_ASSERT(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT(odp_pktio_lookup(iface_name[0]) == pktio);
-
- pktio_inval = odp_pktio_open(iface_name[0], default_pkt_pool,
- &pktio_param);
- CU_ASSERT(odp_errno() != 0);
- CU_ASSERT(pktio_inval == ODP_PKTIO_INVALID);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
-
- CU_ASSERT(odp_pktio_lookup(iface_name[0]) == ODP_PKTIO_INVALID);
-}
-
-void pktio_test_index(void)
-{
- odp_pktio_t pktio, pktio_inval = ODP_PKTIO_INVALID;
- odp_pktio_param_t pktio_param;
- int ndx;
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
- CU_ASSERT(pktio != ODP_PKTIO_INVALID);
-
- ndx = odp_pktio_index(pktio);
- CU_ASSERT(ndx >= 0);
- CU_ASSERT(odp_pktio_index(pktio_inval) < 0);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
- CU_ASSERT(odp_pktio_index(pktio) < 0);
-}
-
-static void pktio_test_print(void)
-{
- odp_pktio_t pktio;
- int i;
-
- for (i = 0; i < num_ifaces; ++i) {
- pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- /* Print pktio debug info and test that the
- * odp_pktio_print() function is implemented. */
- odp_pktio_print(pktio);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
- }
-}
-
-void pktio_test_pktio_config(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktio_config_t config;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- odp_pktio_config_init(&config);
-
- CU_ASSERT(config.parser.layer == ODP_PKTIO_PARSER_LAYER_ALL);
-
- CU_ASSERT(odp_pktio_config(pktio, NULL) == 0);
-
- CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
-
- config = capa.config;
- CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
-
- CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
-}
-
-void pktio_test_info(void)
-{
- odp_pktio_t pktio;
- odp_pktio_info_t pktio_info;
- int i;
-
- for (i = 0; i < num_ifaces; i++) {
- pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_info(pktio, &pktio_info) == 0);
-
- printf("pktio %d\n name %s\n driver %s\n", i,
- pktio_info.name, pktio_info.drv_name);
-
- CU_ASSERT(strcmp(pktio_info.name, iface_name[i]) == 0);
- CU_ASSERT(pktio_info.pool == pool[i]);
- CU_ASSERT(pktio_info.param.in_mode == ODP_PKTIN_MODE_QUEUE);
- CU_ASSERT(pktio_info.param.out_mode == ODP_PKTOUT_MODE_DIRECT);
-
- CU_ASSERT(odp_pktio_info(ODP_PKTIO_INVALID, &pktio_info) < 0);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
- }
-}
-
-void pktio_test_pktin_queue_config_direct(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktin_queue_param_t queue_param;
- odp_pktin_queue_t pktin_queues[MAX_QUEUES];
- odp_queue_t in_queues[MAX_QUEUES];
- int num_queues;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT(odp_pktio_capability(ODP_PKTIO_INVALID, &capa) < 0);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
- capa.max_input_queues > 0);
- num_queues = capa.max_input_queues;
-
- odp_pktin_queue_param_init(&queue_param);
-
- queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
- queue_param.hash_proto.proto.ipv4_udp = 1;
- queue_param.num_queues = num_queues;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES)
- == num_queues);
- CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) < 0);
-
- queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
- queue_param.num_queues = 1;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktin_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
-
- queue_param.num_queues = capa.max_input_queues + 1;
- CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
-
- CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
-}
-
-void pktio_test_pktin_queue_config_sched(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktin_queue_param_t queue_param;
- odp_pktin_queue_t pktin_queues[MAX_QUEUES];
- odp_queue_t in_queues[MAX_QUEUES];
- int num_queues;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
- capa.max_input_queues > 0);
- num_queues = capa.max_input_queues;
-
- odp_pktin_queue_param_init(&queue_param);
-
- queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
- queue_param.hash_proto.proto.ipv4_udp = 1;
- queue_param.num_queues = num_queues;
- queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
- queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
- == num_queues);
- CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0);
-
- queue_param.num_queues = 1;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- queue_param.num_queues = capa.max_input_queues + 1;
- CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
-
- CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
-}
-
-void pktio_test_pktin_queue_config_queue(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktin_queue_param_t queue_param;
- odp_pktin_queue_t pktin_queues[MAX_QUEUES];
- odp_queue_t in_queues[MAX_QUEUES];
- int num_queues;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_QUEUE, ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
- capa.max_input_queues > 0);
- num_queues = capa.max_input_queues;
-
- odp_pktin_queue_param_init(&queue_param);
-
- queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
- queue_param.hash_proto.proto.ipv4_udp = 1;
- queue_param.num_queues = num_queues;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
- == num_queues);
- CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0);
-
- queue_param.num_queues = 1;
- CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
-
- queue_param.num_queues = capa.max_input_queues + 1;
- CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
-}
-
-void pktio_test_pktout_queue_config(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktout_queue_param_t queue_param;
- odp_pktout_queue_t pktout_queues[MAX_QUEUES];
- int num_queues;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
- capa.max_output_queues > 0);
- num_queues = capa.max_output_queues;
-
- odp_pktout_queue_param_init(&queue_param);
-
- queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
- queue_param.num_queues = num_queues;
- CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktout_queue(pktio, pktout_queues, MAX_QUEUES)
- == num_queues);
-
- queue_param.op_mode = ODP_PKTIO_OP_MT;
- queue_param.num_queues = 1;
- CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
-
- CU_ASSERT(odp_pktout_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
-
- queue_param.num_queues = capa.max_output_queues + 1;
- CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) < 0);
-
- CU_ASSERT(odp_pktio_close(pktio) == 0);
-}
-
-#ifdef DEBUG_STATS
-static void _print_pktio_stats(odp_pktio_stats_t *s, const char *name)
-{
- fprintf(stderr, "\n%s:\n"
- " in_octets %" PRIu64 "\n"
- " in_ucast_pkts %" PRIu64 "\n"
- " in_discards %" PRIu64 "\n"
- " in_errors %" PRIu64 "\n"
- " in_unknown_protos %" PRIu64 "\n"
- " out_octets %" PRIu64 "\n"
- " out_ucast_pkts %" PRIu64 "\n"
- " out_discards %" PRIu64 "\n"
- " out_errors %" PRIu64 "\n",
- name,
- s->in_octets,
- s->in_ucast_pkts,
- s->in_discards,
- s->in_errors,
- s->in_unknown_protos,
- s->out_octets,
- s->out_ucast_pkts,
- s->out_discards,
- s->out_errors);
-}
-#endif
-
-/* some pktio like netmap support various methods to
- * get statistics counters. ethtool strings are not standardised
- * and sysfs may not be supported. skip pktio_stats test until
- * we will solve that.*/
-int pktio_check_statistics_counters(void)
-{
- odp_pktio_t pktio;
- odp_pktio_stats_t stats;
- int ret;
- odp_pktio_param_t pktio_param;
- const char *iface = iface_name[0];
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
-
- pktio = odp_pktio_open(iface, pool[0], &pktio_param);
- if (pktio == ODP_PKTIO_INVALID)
- return ODP_TEST_INACTIVE;
-
- ret = odp_pktio_stats(pktio, &stats);
- (void)odp_pktio_close(pktio);
-
- if (ret == 0)
- return ODP_TEST_ACTIVE;
-
- return ODP_TEST_INACTIVE;
-}
-
-void pktio_test_statistics_counters(void)
-{
- odp_pktio_t pktio_rx, pktio_tx;
- odp_pktio_t pktio[MAX_NUM_IFACES] = {
- ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
- };
- odp_packet_t pkt;
- odp_packet_t tx_pkt[1000];
- uint32_t pkt_seq[1000];
- odp_event_t ev;
- int i, pkts, tx_pkts, ret, alloc = 0;
- odp_pktout_queue_t pktout;
- uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
- odp_pktio_stats_t stats[2];
-
- for (i = 0; i < num_ifaces; i++) {
- pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
-
- CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
- }
- pktio_tx = pktio[0];
- pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
-
- CU_ASSERT(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
-
- ret = odp_pktio_start(pktio_tx);
- CU_ASSERT(ret == 0);
- if (num_ifaces > 1) {
- ret = odp_pktio_start(pktio_rx);
- CU_ASSERT(ret == 0);
- }
-
- /* flush packets with magic number in pipes */
- for (i = 0; i < 1000; i++) {
- ev = odp_schedule(NULL, wait);
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
- }
-
- alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
-
- ret = odp_pktio_stats_reset(pktio_tx);
- CU_ASSERT(ret == 0);
- if (num_ifaces > 1) {
- ret = odp_pktio_stats_reset(pktio_rx);
- CU_ASSERT(ret == 0);
- }
-
- /* send */
- for (pkts = 0; pkts != alloc; ) {
- ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
- if (ret < 0) {
- CU_FAIL("unable to send packet\n");
- break;
- }
- pkts += ret;
- }
- tx_pkts = pkts;
-
- /* get */
- for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
- ev = odp_schedule(NULL, wait);
- if (ev != ODP_EVENT_INVALID) {
- if (odp_event_type(ev) == ODP_EVENT_PACKET) {
- pkt = odp_packet_from_event(ev);
- if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
- pkts++;
- }
- odp_event_free(ev);
- }
- }
-
- CU_ASSERT(pkts == tx_pkts);
-
- ret = odp_pktio_stats(pktio_tx, &stats[0]);
- CU_ASSERT(ret == 0);
-
- if (num_ifaces > 1) {
- ret = odp_pktio_stats(pktio_rx, &stats[1]);
- CU_ASSERT(ret == 0);
- CU_ASSERT((stats[1].in_ucast_pkts == 0) ||
- (stats[1].in_ucast_pkts >= (uint64_t)pkts));
- CU_ASSERT((stats[0].out_octets == 0) ||
- (stats[0].out_octets >=
- (PKT_LEN_NORMAL * (uint64_t)pkts)));
- } else {
- CU_ASSERT((stats[0].in_ucast_pkts == 0) ||
- (stats[0].in_ucast_pkts == (uint64_t)pkts));
- CU_ASSERT((stats[0].in_octets == 0) ||
- (stats[0].in_octets ==
- (PKT_LEN_NORMAL * (uint64_t)pkts)));
- }
-
- CU_ASSERT(0 == stats[0].in_discards);
- CU_ASSERT(0 == stats[0].in_errors);
- CU_ASSERT(0 == stats[0].in_unknown_protos);
- CU_ASSERT(0 == stats[0].out_discards);
- CU_ASSERT(0 == stats[0].out_errors);
-
- for (i = 0; i < num_ifaces; i++) {
- CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
-#ifdef DEBUG_STATS
- _print_pktio_stats(&stats[i], iface_name[i]);
-#endif
- flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
- CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
- }
-}
-
-void pktio_test_start_stop(void)
-{
- odp_pktio_t pktio[MAX_NUM_IFACES];
- odp_pktio_t pktio_in;
- odp_packet_t pkt;
- odp_packet_t tx_pkt[1000];
- uint32_t pkt_seq[1000];
- odp_event_t ev;
- int i, pkts, ret, alloc = 0;
- odp_pktout_queue_t pktout;
- uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
-
- for (i = 0; i < num_ifaces; i++) {
- pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
- }
-
- CU_ASSERT(odp_pktout_queue(pktio[0], &pktout, 1) == 1);
-
- /* Interfaces are stopped by default,
- * Check that stop when stopped generates an error */
- ret = odp_pktio_stop(pktio[0]);
- CU_ASSERT(ret < 0);
-
- /* start first */
- ret = odp_pktio_start(pktio[0]);
- CU_ASSERT(ret == 0);
- /* Check that start when started generates an error */
- ret = odp_pktio_start(pktio[0]);
- CU_ASSERT(ret < 0);
-
- _pktio_wait_linkup(pktio[0]);
-
- /* Test Rx on a stopped interface. Only works if there are 2 */
- if (num_ifaces > 1) {
- alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0],
- pktio[1]);
-
- for (pkts = 0; pkts != alloc; ) {
- ret = odp_pktout_send(pktout, &tx_pkt[pkts],
- alloc - pkts);
- if (ret < 0) {
- CU_FAIL("unable to enqueue packet\n");
- break;
- }
- pkts += ret;
- }
- /* check that packets did not arrive */
- for (i = 0, pkts = 0; i < 1000; i++) {
- ev = odp_schedule(NULL, wait);
- if (ev == ODP_EVENT_INVALID)
- continue;
-
- if (odp_event_type(ev) == ODP_EVENT_PACKET) {
- pkt = odp_packet_from_event(ev);
- if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
- pkts++;
- }
- odp_event_free(ev);
- }
- if (pkts)
- CU_FAIL("pktio stopped, received unexpected events");
-
- /* start both, send and get packets */
- /* 0 already started */
- ret = odp_pktio_start(pktio[1]);
- CU_ASSERT(ret == 0);
-
- _pktio_wait_linkup(pktio[1]);
-
- /* flush packets with magic number in pipes */
- for (i = 0; i < 1000; i++) {
- ev = odp_schedule(NULL, wait);
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
- }
- }
-
- if (num_ifaces > 1)
- pktio_in = pktio[1];
- else
- pktio_in = pktio[0];
-
- alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0], pktio_in);
-
- /* send */
- for (pkts = 0; pkts != alloc; ) {
- ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
- if (ret < 0) {
- CU_FAIL("unable to enqueue packet\n");
- break;
- }
- pkts += ret;
- }
-
- /* get */
- for (i = 0, pkts = 0; i < 1000; i++) {
- ev = odp_schedule(NULL, wait);
- if (ev != ODP_EVENT_INVALID) {
- if (odp_event_type(ev) == ODP_EVENT_PACKET) {
- pkt = odp_packet_from_event(ev);
- if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
- pkts++;
- }
- odp_event_free(ev);
- }
- }
- CU_ASSERT(pkts == alloc);
-
- for (i = 0; i < num_ifaces; i++) {
- CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
- CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
- }
-
- /* Verify that a schedule call after stop and close does not generate
- errors. */
- ev = odp_schedule(NULL, wait);
- CU_ASSERT(ev == ODP_EVENT_INVALID);
- if (ev != ODP_EVENT_INVALID)
- odp_event_free(ev);
-}
-
-/*
- * This is a pre-condition check that the pktio_test_send_failure()
- * test case can be run. If the TX interface MTU is larger that the
- * biggest packet we can allocate then the test won't be able to
- * attempt to send packets larger than the MTU, so skip the test.
- */
-int pktio_check_send_failure(void)
-{
- odp_pktio_t pktio_tx;
- uint32_t mtu;
- odp_pktio_param_t pktio_param;
- int iface_idx = 0;
- const char *iface = iface_name[iface_idx];
- odp_pool_capability_t pool_capa;
-
- if (odp_pool_capability(&pool_capa) < 0) {
- fprintf(stderr, "%s: pool capability failed\n", __func__);
- return ODP_TEST_INACTIVE;
- };
-
- memset(&pktio_param, 0, sizeof(pktio_param));
-
- pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
-
- pktio_tx = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
- if (pktio_tx == ODP_PKTIO_INVALID) {
- fprintf(stderr, "%s: failed to open pktio\n", __func__);
- return ODP_TEST_INACTIVE;
- }
-
- /* read the MTU from the transmit interface */
- mtu = odp_pktio_mtu(pktio_tx);
-
- odp_pktio_close(pktio_tx);
-
- /* Failure test supports only single segment */
- if (pool_capa.pkt.max_seg_len &&
- pool_capa.pkt.max_seg_len < mtu + 32)
- return ODP_TEST_INACTIVE;
-
- return ODP_TEST_ACTIVE;
-}
-
-void pktio_test_send_failure(void)
-{
- odp_pktio_t pktio_tx, pktio_rx;
- odp_packet_t pkt_tbl[TX_BATCH_LEN];
- uint32_t pkt_seq[TX_BATCH_LEN];
- int ret, i, alloc_pkts;
- uint32_t mtu;
- odp_pool_param_t pool_params;
- odp_pool_t pkt_pool;
- int long_pkt_idx = TX_BATCH_LEN / 2;
- pktio_info_t info_rx;
- odp_pktout_queue_t pktout;
- odp_pool_capability_t pool_capa;
-
- pktio_tx = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DIRECT);
- if (pktio_tx == ODP_PKTIO_INVALID) {
- CU_FAIL("failed to open pktio");
- return;
- }
-
- CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
-
- /* read the MTU from the transmit interface */
- mtu = odp_pktio_mtu(pktio_tx);
-
- ret = odp_pktio_start(pktio_tx);
- CU_ASSERT_FATAL(ret == 0);
-
- _pktio_wait_linkup(pktio_tx);
-
- CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
-
- if (pool_capa.pkt.max_seg_len &&
- pool_capa.pkt.max_seg_len < mtu + 32) {
- CU_FAIL("Max packet seg length is too small.");
- return;
- }
-
- /* configure the pool so that we can generate test packets larger
- * than the interface MTU */
- odp_pool_param_init(&pool_params);
- pool_params.pkt.len = mtu + 32;
- pool_params.pkt.seg_len = pool_params.pkt.len;
- pool_params.pkt.num = TX_BATCH_LEN + 1;
- pool_params.type = ODP_POOL_PACKET;
- pkt_pool = odp_pool_create("pkt_pool_oversize", &pool_params);
- CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID);
-
- if (num_ifaces > 1) {
- pktio_rx = create_pktio(1, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DIRECT);
- ret = odp_pktio_start(pktio_rx);
- CU_ASSERT_FATAL(ret == 0);
-
- _pktio_wait_linkup(pktio_rx);
- } else {
- pktio_rx = pktio_tx;
- }
-
- /* generate a batch of packets with a single overly long packet
- * in the middle */
- for (i = 0; i < TX_BATCH_LEN; ++i) {
- uint32_t pkt_len;
-
- if (i == long_pkt_idx)
- pkt_len = pool_params.pkt.len;
- else
- pkt_len = PKT_LEN_NORMAL;
-
- pkt_tbl[i] = odp_packet_alloc(pkt_pool, pkt_len);
- if (pkt_tbl[i] == ODP_PACKET_INVALID)
- break;
-
- pkt_seq[i] = pktio_init_packet(pkt_tbl[i]);
-
- pktio_pkt_set_macs(pkt_tbl[i], pktio_tx, pktio_rx);
- if (pktio_fixup_checksums(pkt_tbl[i]) != 0) {
- odp_packet_free(pkt_tbl[i]);
- break;
- }
-
- if (pkt_seq[i] == TEST_SEQ_INVALID) {
- odp_packet_free(pkt_tbl[i]);
- break;
- }
- }
- alloc_pkts = i;
-
- if (alloc_pkts == TX_BATCH_LEN) {
- /* try to send the batch with the long packet in the middle,
- * the initial short packets should be sent successfully */
- odp_errno_zero();
- ret = odp_pktout_send(pktout, pkt_tbl, TX_BATCH_LEN);
- CU_ASSERT_FATAL(ret == long_pkt_idx);
- CU_ASSERT(odp_errno() == 0);
-
- info_rx.id = pktio_rx;
- info_rx.inq = ODP_QUEUE_INVALID;
- info_rx.in_mode = ODP_PKTIN_MODE_DIRECT;
-
- i = wait_for_packets(&info_rx, pkt_tbl, pkt_seq, ret,
- TXRX_MODE_MULTI, ODP_TIME_SEC_IN_NS);
-
- if (i == ret) {
- /* now try to send starting with the too-long packet
- * and verify it fails */
- odp_errno_zero();
- ret = odp_pktout_send(pktout,
- &pkt_tbl[long_pkt_idx],
- TX_BATCH_LEN - long_pkt_idx);
- CU_ASSERT(ret == -1);
- CU_ASSERT(odp_errno() != 0);
- } else {
- CU_FAIL("failed to receive transmitted packets\n");
- }
-
- /* now reduce the size of the long packet and attempt to send
- * again - should work this time */
- i = long_pkt_idx;
- odp_packet_pull_tail(pkt_tbl[i],
- odp_packet_len(pkt_tbl[i]) -
- PKT_LEN_NORMAL);
- pkt_seq[i] = pktio_init_packet(pkt_tbl[i]);
-
- pktio_pkt_set_macs(pkt_tbl[i], pktio_tx, pktio_rx);
- ret = pktio_fixup_checksums(pkt_tbl[i]);
- CU_ASSERT_FATAL(ret == 0);
-
- CU_ASSERT_FATAL(pkt_seq[i] != TEST_SEQ_INVALID);
- ret = odp_pktout_send(pktout, &pkt_tbl[i], TX_BATCH_LEN - i);
- CU_ASSERT_FATAL(ret == (TX_BATCH_LEN - i));
-
- i = wait_for_packets(&info_rx, &pkt_tbl[i], &pkt_seq[i], ret,
- TXRX_MODE_MULTI, ODP_TIME_SEC_IN_NS);
- CU_ASSERT(i == ret);
- } else {
- CU_FAIL("failed to generate test packets\n");
- }
-
- for (i = 0; i < alloc_pkts; ++i) {
- if (pkt_tbl[i] != ODP_PACKET_INVALID)
- odp_packet_free(pkt_tbl[i]);
- }
-
- if (pktio_rx != pktio_tx) {
- CU_ASSERT(odp_pktio_stop(pktio_rx) == 0);
- CU_ASSERT(odp_pktio_close(pktio_rx) == 0);
- }
- CU_ASSERT(odp_pktio_stop(pktio_tx) == 0);
- CU_ASSERT(odp_pktio_close(pktio_tx) == 0);
- CU_ASSERT(odp_pool_destroy(pkt_pool) == 0);
-}
-
-void pktio_test_recv_on_wonly(void)
-{
- odp_pktio_t pktio;
- int ret;
- odp_pktin_queue_t pktin;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_DISABLED,
- ODP_PKTOUT_MODE_DIRECT);
-
- if (pktio == ODP_PKTIO_INVALID) {
- CU_FAIL("failed to open pktio");
- return;
- }
-
- CU_ASSERT(odp_pktin_queue(pktio, &pktin, 1) == 0);
-
- ret = odp_pktio_start(pktio);
- CU_ASSERT_FATAL(ret == 0);
-
- _pktio_wait_linkup(pktio);
-
- ret = odp_pktio_stop(pktio);
- CU_ASSERT_FATAL(ret == 0);
-
- ret = odp_pktio_close(pktio);
- CU_ASSERT_FATAL(ret == 0);
-}
-
-void pktio_test_send_on_ronly(void)
-{
- odp_pktio_t pktio;
- int ret;
- odp_pktout_queue_t pktout;
-
- pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DISABLED);
-
- if (pktio == ODP_PKTIO_INVALID) {
- CU_FAIL("failed to open pktio");
- return;
- }
-
- CU_ASSERT(odp_pktout_queue(pktio, &pktout, 1) == 0);
-
- ret = odp_pktio_start(pktio);
- CU_ASSERT_FATAL(ret == 0);
-
- _pktio_wait_linkup(pktio);
-
- ret = odp_pktio_stop(pktio);
- CU_ASSERT_FATAL(ret == 0);
-
- ret = odp_pktio_close(pktio);
- CU_ASSERT_FATAL(ret == 0);
-}
-
-int pktio_check_pktin_ts(void)
-{
- odp_pktio_t pktio;
- odp_pktio_capability_t capa;
- odp_pktio_param_t pktio_param;
- int ret;
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
-
- pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
- if (pktio == ODP_PKTIO_INVALID)
- return ODP_TEST_INACTIVE;
-
- ret = odp_pktio_capability(pktio, &capa);
- (void)odp_pktio_close(pktio);
-
- if (ret < 0 || !capa.config.pktin.bit.ts_all)
- return ODP_TEST_INACTIVE;
-
- return ODP_TEST_ACTIVE;
-}
-
-void pktio_test_pktin_ts(void)
-{
- odp_pktio_t pktio_tx, pktio_rx;
- odp_pktio_t pktio[MAX_NUM_IFACES];
- pktio_info_t pktio_rx_info;
- odp_pktio_capability_t capa;
- odp_pktio_config_t config;
- odp_pktout_queue_t pktout_queue;
- odp_packet_t pkt_tbl[TX_BATCH_LEN];
- uint32_t pkt_seq[TX_BATCH_LEN];
- uint64_t ns1, ns2;
- uint64_t res;
- odp_time_t ts_prev;
- odp_time_t ts;
- int num_rx = 0;
- int ret;
- int i;
-
- CU_ASSERT_FATAL(num_ifaces >= 1);
-
- /* Open and configure interfaces */
- for (i = 0; i < num_ifaces; ++i) {
- pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
- ODP_PKTOUT_MODE_DIRECT);
- CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
-
- CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
- CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all);
-
- odp_pktio_config_init(&config);
- config.pktin.bit.ts_all = 1;
- CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
-
- CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
- }
-
- for (i = 0; i < num_ifaces; i++)
- _pktio_wait_linkup(pktio[i]);
-
- pktio_tx = pktio[0];
- pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
- pktio_rx_info.id = pktio_rx;
- pktio_rx_info.inq = ODP_QUEUE_INVALID;
- pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
-
- /* Test odp_pktin_ts_res() and odp_pktin_ts_from_ns() */
- res = odp_pktin_ts_res(pktio_tx);
- CU_ASSERT(res > PKTIN_TS_MIN_RES);
- CU_ASSERT(res < PKTIN_TS_MAX_RES);
- ns1 = 100;
- ts = odp_pktin_ts_from_ns(pktio_tx, ns1);
- ns2 = odp_time_to_ns(ts);
- /* Allow some arithmetic tolerance */
- CU_ASSERT((ns2 <= (ns1 + PKTIN_TS_CMP_RES)) &&
- (ns2 >= (ns1 - PKTIN_TS_CMP_RES)));
-
- ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
- pktio_rx);
- CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
-
- ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
- CU_ASSERT_FATAL(ret > 0);
-
- /* Send packets one at a time and add delay between the packets */
- for (i = 0; i < TX_BATCH_LEN; i++) {
- CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
- &pkt_tbl[i], 1) == 1);
- ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
- 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS);
- if (ret != 1)
- break;
- odp_time_wait_ns(PKTIN_TS_INTERVAL);
- }
- num_rx = i;
- CU_ASSERT(num_rx == TX_BATCH_LEN);
-
- ts_prev = ODP_TIME_NULL;
- for (i = 0; i < num_rx; i++) {
- ts = odp_packet_ts(pkt_tbl[i]);
-
- CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0);
-
- ts_prev = ts;
- odp_packet_free(pkt_tbl[i]);
- }
-
- for (i = 0; i < num_ifaces; i++) {
- CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
- CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
- }
-}
-
-static int create_pool(const char *iface, int num)
-{
- char pool_name[ODP_POOL_NAME_LEN];
- odp_pool_param_t params;
- odp_pool_capability_t pool_capa;
-
- if (odp_pool_capability(&pool_capa) != 0)
- return -1;
-
- odp_pool_param_init(&params);
- set_pool_len(&params, &pool_capa);
- params.pkt.num = PKT_BUF_NUM;
- params.type = ODP_POOL_PACKET;
-
- snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s_%d",
- iface, pool_segmentation);
-
- pool[num] = odp_pool_create(pool_name, &params);
- if (ODP_POOL_INVALID == pool[num]) {
- fprintf(stderr, "%s: failed to create pool: %d",
- __func__, odp_errno());
- return -1;
- }
-
- return 0;
-}
-
-static int pktio_suite_init(void)
-{
- int i;
-
- odp_atomic_init_u32(&ip_seq, 0);
-
- if (getenv("ODP_WAIT_FOR_NETWORK"))
- wait_for_network = true;
-
- iface_name[0] = getenv("ODP_PKTIO_IF0");
- iface_name[1] = getenv("ODP_PKTIO_IF1");
- num_ifaces = 1;
-
- if (!iface_name[0]) {
- printf("No interfaces specified, using default \"loop\".\n");
- iface_name[0] = "loop";
- } else if (!iface_name[1]) {
- printf("Using loopback interface: %s\n", iface_name[0]);
- } else {
- num_ifaces = 2;
- printf("Using paired interfaces: %s %s\n",
- iface_name[0], iface_name[1]);
- }
-
- for (i = 0; i < num_ifaces; i++) {
- if (create_pool(iface_name[i], i) != 0)
- return -1;
- }
-
- if (default_pool_create() != 0) {
- fprintf(stderr, "error: failed to create default pool\n");
- return -1;
- }
-
- return 0;
-}
-
-int pktio_suite_init_unsegmented(void)
-{
- pool_segmentation = PKT_POOL_UNSEGMENTED;
- return pktio_suite_init();
-}
-
-int pktio_suite_init_segmented(void)
-{
- pool_segmentation = PKT_POOL_SEGMENTED;
- return pktio_suite_init();
-}
-
-int pktio_suite_term(void)
-{
- char pool_name[ODP_POOL_NAME_LEN];
- odp_pool_t pool;
- int i;
- int ret = 0;
-
- for (i = 0; i < num_ifaces; ++i) {
- snprintf(pool_name, sizeof(pool_name),
- "pkt_pool_%s_%d", iface_name[i], pool_segmentation);
- pool = odp_pool_lookup(pool_name);
- if (pool == ODP_POOL_INVALID)
- continue;
-
- if (odp_pool_destroy(pool) != 0) {
- fprintf(stderr, "error: failed to destroy pool %s\n",
- pool_name);
- ret = -1;
- }
- }
-
- if (odp_pool_destroy(default_pkt_pool) != 0) {
- fprintf(stderr, "error: failed to destroy default pool\n");
- ret = -1;
- }
- default_pkt_pool = ODP_POOL_INVALID;
-
- return ret;
-}
-
-odp_testinfo_t pktio_suite_unsegmented[] = {
- ODP_TEST_INFO(pktio_test_open),
- ODP_TEST_INFO(pktio_test_lookup),
- ODP_TEST_INFO(pktio_test_index),
- ODP_TEST_INFO(pktio_test_print),
- ODP_TEST_INFO(pktio_test_pktio_config),
- ODP_TEST_INFO(pktio_test_info),
- ODP_TEST_INFO(pktio_test_pktin_queue_config_direct),
- ODP_TEST_INFO(pktio_test_pktin_queue_config_sched),
- ODP_TEST_INFO(pktio_test_pktin_queue_config_queue),
- ODP_TEST_INFO(pktio_test_pktout_queue_config),
- ODP_TEST_INFO(pktio_test_plain_queue),
- ODP_TEST_INFO(pktio_test_plain_multi),
- ODP_TEST_INFO(pktio_test_sched_queue),
- ODP_TEST_INFO(pktio_test_sched_multi),
- ODP_TEST_INFO(pktio_test_recv),
- ODP_TEST_INFO(pktio_test_recv_multi),
- ODP_TEST_INFO(pktio_test_recv_queue),
- ODP_TEST_INFO(pktio_test_recv_tmo),
- ODP_TEST_INFO(pktio_test_recv_mq_tmo),
- ODP_TEST_INFO(pktio_test_recv_mtu),
- ODP_TEST_INFO_CONDITIONAL(pktio_test_send_failure,
- pktio_check_send_failure),
- ODP_TEST_INFO(pktio_test_mtu),
- ODP_TEST_INFO(pktio_test_promisc),
- ODP_TEST_INFO(pktio_test_mac),
- ODP_TEST_INFO(pktio_test_start_stop),
- ODP_TEST_INFO(pktio_test_recv_on_wonly),
- ODP_TEST_INFO(pktio_test_send_on_ronly),
- ODP_TEST_INFO(pktio_test_plain_multi_event),
- ODP_TEST_INFO(pktio_test_sched_multi_event),
- ODP_TEST_INFO(pktio_test_recv_multi_event),
- ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters,
- pktio_check_statistics_counters),
- ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_ts,
- pktio_check_pktin_ts),
- ODP_TEST_INFO_NULL
-};
-
-odp_testinfo_t pktio_suite_segmented[] = {
- ODP_TEST_INFO(pktio_test_plain_queue),
- ODP_TEST_INFO(pktio_test_plain_multi),
- ODP_TEST_INFO(pktio_test_sched_queue),
- ODP_TEST_INFO(pktio_test_sched_multi),
- ODP_TEST_INFO(pktio_test_recv),
- ODP_TEST_INFO(pktio_test_recv_multi),
- ODP_TEST_INFO(pktio_test_recv_mtu),
- ODP_TEST_INFO_CONDITIONAL(pktio_test_send_failure,
- pktio_check_send_failure),
- ODP_TEST_INFO_NULL
-};
-
-odp_suiteinfo_t pktio_suites[] = {
- {"Packet I/O Unsegmented", pktio_suite_init_unsegmented,
- pktio_suite_term, pktio_suite_unsegmented},
- {"Packet I/O Segmented", pktio_suite_init_segmented,
- pktio_suite_term, pktio_suite_segmented},
- {"Packet parser", parser_suite_init, parser_suite_term, parser_suite},
- ODP_SUITE_INFO_NULL
-};
-
-int pktio_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(pktio_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/pktio/pktio.h b/test/common_plat/validation/api/pktio/pktio.h
deleted file mode 100644
index b8799d9eb..000000000
--- a/test/common_plat/validation/api/pktio/pktio.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_PKTIO_H_
-#define _ODP_TEST_PKTIO_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void pktio_test_plain_queue(void);
-void pktio_test_plain_multi(void);
-void pktio_test_sched_queue(void);
-void pktio_test_sched_multi(void);
-void pktio_test_recv(void);
-void pktio_test_recv_multi(void);
-void pktio_test_recv_queue(void);
-void pktio_test_recv_tmo(void);
-void pktio_test_recv_mq_tmo(void);
-void pktio_test_recv_mtu(void);
-void pktio_test_mtu(void);
-void pktio_test_promisc(void);
-void pktio_test_mac(void);
-void pktio_test_inq_remdef(void);
-void pktio_test_open(void);
-void pktio_test_lookup(void);
-void pktio_test_index(void);
-void pktio_test_info(void);
-void pktio_test_inq(void);
-void pktio_test_pktio_config(void);
-void pktio_test_pktin_queue_config_direct(void);
-void pktio_test_pktin_queue_config_sched(void);
-void pktio_test_pktin_queue_config_queue(void);
-void pktio_test_pktout_queue_config(void);
-void pktio_test_start_stop(void);
-int pktio_check_send_failure(void);
-void pktio_test_send_failure(void);
-void pktio_test_recv_on_wonly(void);
-void pktio_test_send_on_ronly(void);
-void pktio_test_plain_multi_event(void);
-void pktio_test_sched_multi_event(void);
-void pktio_test_recv_multi_event(void);
-int pktio_check_statistics_counters(void);
-void pktio_test_statistics_counters(void);
-int pktio_check_pktin_ts(void);
-void pktio_test_pktin_ts(void);
-
-/* test arrays: */
-extern odp_testinfo_t pktio_suite[];
-
-/* test array init/term functions: */
-int pktio_suite_term(void);
-int pktio_suite_init_segmented(void);
-int pktio_suite_init_unsegmented(void);
-
-/* test registry: */
-extern odp_suiteinfo_t pktio_suites[];
-
-/* main test program: */
-int pktio_main(int argc, char *argv[]);
-
-/* functions shared by parser test suite */
-void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst);
-
-#endif
diff --git a/test/common_plat/validation/api/pktio/pktio_main.c b/test/common_plat/validation/api/pktio/pktio_main.c
deleted file mode 100644
index 2928e1b8a..000000000
--- a/test/common_plat/validation/api/pktio/pktio_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "pktio.h"
-
-int main(int argc, char *argv[])
-{
- return pktio_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/pool/Makefile.am b/test/common_plat/validation/api/pool/Makefile.am
deleted file mode 100644
index 1eb8d714b..000000000
--- a/test/common_plat/validation/api/pool/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestpool.la
-libtestpool_la_SOURCES = pool.c
-
-test_PROGRAMS = pool_main$(EXEEXT)
-dist_pool_main_SOURCES = pool_main.c
-pool_main_LDADD = libtestpool.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = pool.h
diff --git a/test/common_plat/validation/api/pool/pool.c b/test/common_plat/validation/api/pool/pool.c
deleted file mode 100644
index 8687941f7..000000000
--- a/test/common_plat/validation/api/pool/pool.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include "odp_cunit_common.h"
-#include "pool.h"
-
-static const int default_buffer_size = 1500;
-static const int default_buffer_num = 1000;
-
-static void pool_create_destroy(odp_pool_param_t *params)
-{
- odp_pool_t pool;
-
- pool = odp_pool_create(NULL, params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- CU_ASSERT(odp_pool_to_u64(pool) !=
- odp_pool_to_u64(ODP_POOL_INVALID));
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void pool_test_create_destroy_buffer(void)
-{
- odp_pool_param_t params = {
- .buf = {
- .size = default_buffer_size,
- .align = ODP_CACHE_LINE_SIZE,
- .num = default_buffer_num,
- },
- .type = ODP_POOL_BUFFER,
- };
-
- pool_create_destroy(&params);
-}
-
-void pool_test_create_destroy_packet(void)
-{
- odp_pool_param_t params = {
- .pkt = {
- .seg_len = 0,
- .len = default_buffer_size,
- .num = default_buffer_num,
- },
- .type = ODP_POOL_PACKET,
- };
-
- pool_create_destroy(&params);
-}
-
-void pool_test_create_destroy_timeout(void)
-{
- odp_pool_param_t params = {
- .tmo = {
- .num = default_buffer_num,
- },
- .type = ODP_POOL_TIMEOUT,
- };
-
- pool_create_destroy(&params);
-}
-
-void pool_test_lookup_info_print(void)
-{
- odp_pool_t pool;
- const char pool_name[] = "pool_for_lookup_test";
- odp_pool_info_t info;
- odp_pool_param_t params = {
- .buf = {
- .size = default_buffer_size,
- .align = ODP_CACHE_LINE_SIZE,
- .num = default_buffer_num,
- },
- .type = ODP_POOL_BUFFER,
- };
-
- pool = odp_pool_create(pool_name, &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- pool = odp_pool_lookup(pool_name);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0);
- CU_ASSERT(strncmp(pool_name, info.name, sizeof(pool_name)) == 0);
- CU_ASSERT(params.buf.size <= info.params.buf.size);
- CU_ASSERT(params.buf.align <= info.params.buf.align);
- CU_ASSERT(params.buf.num <= info.params.buf.num);
- CU_ASSERT(params.type == info.params.type);
-
- odp_pool_print(pool);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-odp_testinfo_t pool_suite[] = {
- ODP_TEST_INFO(pool_test_create_destroy_buffer),
- ODP_TEST_INFO(pool_test_create_destroy_packet),
- ODP_TEST_INFO(pool_test_create_destroy_timeout),
- ODP_TEST_INFO(pool_test_lookup_info_print),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t pool_suites[] = {
- { .pName = "Pool tests",
- .pTests = pool_suite,
- },
- ODP_SUITE_INFO_NULL,
-};
-
-int pool_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(pool_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/pool/pool.h b/test/common_plat/validation/api/pool/pool.h
deleted file mode 100644
index 29e517633..000000000
--- a/test/common_plat/validation/api/pool/pool.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_POOL_H_
-#define _ODP_TEST_POOL_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void pool_test_create_destroy_buffer(void);
-void pool_test_create_destroy_packet(void);
-void pool_test_create_destroy_timeout(void);
-void pool_test_create_destroy_buffer_shm(void);
-void pool_test_lookup_info_print(void);
-
-/* test arrays: */
-extern odp_testinfo_t pool_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t pool_suites[];
-
-/* main test program: */
-int pool_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/pool/pool_main.c b/test/common_plat/validation/api/pool/pool_main.c
deleted file mode 100644
index bf06585b5..000000000
--- a/test/common_plat/validation/api/pool/pool_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "pool.h"
-
-int main(int argc, char *argv[])
-{
- return pool_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/queue/Makefile.am b/test/common_plat/validation/api/queue/Makefile.am
deleted file mode 100644
index a477e3c56..000000000
--- a/test/common_plat/validation/api/queue/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestqueue.la
-libtestqueue_la_SOURCES = queue.c
-
-test_PROGRAMS = queue_main$(EXEEXT)
-dist_queue_main_SOURCES = queue_main.c
-queue_main_LDADD = libtestqueue.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = queue.h
diff --git a/test/common_plat/validation/api/queue/queue.c b/test/common_plat/validation/api/queue/queue.c
deleted file mode 100644
index f08d811f1..000000000
--- a/test/common_plat/validation/api/queue/queue.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "queue.h"
-
-#define MAX_BUFFER_QUEUE (8)
-#define MSG_POOL_SIZE (4 * 1024 * 1024)
-#define CONFIG_MAX_ITERATION (100)
-#define MAX_QUEUES (64 * 1024)
-
-static int queue_context = 0xff;
-static odp_pool_t pool;
-
-static void generate_name(char *name, uint32_t index)
-{
- /* Uniqueue name for up to 300M queues */
- name[0] = 'A' + ((index / (26 * 26 * 26 * 26 * 26)) % 26);
- name[1] = 'A' + ((index / (26 * 26 * 26 * 26)) % 26);
- name[2] = 'A' + ((index / (26 * 26 * 26)) % 26);
- name[3] = 'A' + ((index / (26 * 26)) % 26);
- name[4] = 'A' + ((index / 26) % 26);
- name[5] = 'A' + (index % 26);
-}
-
-int queue_suite_init(void)
-{
- odp_pool_param_t params;
-
- params.buf.size = 0;
- params.buf.align = ODP_CACHE_LINE_SIZE;
- params.buf.num = 1024 * 10;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create("msg_pool", &params);
-
- if (ODP_POOL_INVALID == pool) {
- printf("Pool create failed.\n");
- return -1;
- }
- return 0;
-}
-
-int queue_suite_term(void)
-{
- return odp_pool_destroy(pool);
-}
-
-void queue_test_capa(void)
-{
- odp_queue_capability_t capa;
- odp_queue_param_t qparams;
- char name[ODP_QUEUE_NAME_LEN];
- odp_queue_t queue[MAX_QUEUES];
- uint32_t num_queues, min, i, j;
-
- memset(&capa, 0, sizeof(odp_queue_capability_t));
- CU_ASSERT(odp_queue_capability(&capa) == 0);
-
- CU_ASSERT(capa.max_queues != 0);
- CU_ASSERT(capa.max_ordered_locks != 0);
- CU_ASSERT(capa.max_sched_groups != 0);
- CU_ASSERT(capa.sched_prios != 0);
- CU_ASSERT(capa.plain.max_num != 0);
- CU_ASSERT(capa.sched.max_num != 0);
-
- min = capa.plain.max_num;
- if (min > capa.sched.max_num)
- min = capa.sched.max_num;
-
- CU_ASSERT(capa.max_queues >= min);
-
- for (i = 0; i < ODP_QUEUE_NAME_LEN; i++)
- name[i] = 'A' + (i % 26);
-
- name[ODP_QUEUE_NAME_LEN - 1] = 0;
-
- odp_queue_param_init(&qparams);
-
- for (j = 0; j < 2; j++) {
- if (j == 0) {
- num_queues = capa.plain.max_num;
- } else {
- num_queues = capa.sched.max_num;
- qparams.type = ODP_QUEUE_TYPE_SCHED;
- }
-
- if (num_queues > MAX_QUEUES)
- num_queues = MAX_QUEUES;
-
- for (i = 0; i < num_queues; i++) {
- generate_name(name, i);
- queue[i] = odp_queue_create(name, &qparams);
-
- if (queue[i] == ODP_QUEUE_INVALID) {
- CU_FAIL("Queue create failed");
- num_queues = i;
- break;
- }
-
- CU_ASSERT(odp_queue_lookup(name) != ODP_QUEUE_INVALID);
- }
-
- for (i = 0; i < num_queues; i++)
- CU_ASSERT(odp_queue_destroy(queue[i]) == 0);
- }
-}
-
-void queue_test_mode(void)
-{
- odp_queue_param_t qparams;
- odp_queue_t queue;
- int i, j;
- odp_queue_op_mode_t mode[3] = { ODP_QUEUE_OP_MT,
- ODP_QUEUE_OP_MT_UNSAFE,
- ODP_QUEUE_OP_DISABLED };
-
- odp_queue_param_init(&qparams);
-
- /* Plain queue modes */
- for (i = 0; i < 3; i++) {
- for (j = 0; j < 3; j++) {
- /* Should not disable both enq and deq */
- if (i == 2 && j == 2)
- break;
-
- qparams.enq_mode = mode[i];
- qparams.deq_mode = mode[j];
- queue = odp_queue_create("test_queue", &qparams);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
- if (queue != ODP_QUEUE_INVALID)
- CU_ASSERT(odp_queue_destroy(queue) == 0);
- }
- }
-
- odp_queue_param_init(&qparams);
- qparams.type = ODP_QUEUE_TYPE_SCHED;
-
- /* Scheduled queue modes. Dequeue mode is fixed. */
- for (i = 0; i < 3; i++) {
- qparams.enq_mode = mode[i];
- queue = odp_queue_create("test_queue", &qparams);
- CU_ASSERT(queue != ODP_QUEUE_INVALID);
- if (queue != ODP_QUEUE_INVALID)
- CU_ASSERT(odp_queue_destroy(queue) == 0);
- }
-}
-
-void queue_test_param(void)
-{
- odp_queue_t queue, null_queue;
- odp_event_t enev[MAX_BUFFER_QUEUE];
- odp_event_t deev[MAX_BUFFER_QUEUE];
- odp_buffer_t buf;
- odp_event_t ev;
- odp_pool_t msg_pool;
- odp_event_t *pev_tmp;
- int i, deq_ret, ret;
- int nr_deq_entries = 0;
- int max_iteration = CONFIG_MAX_ITERATION;
- odp_queue_param_t qparams;
- odp_buffer_t enbuf;
-
- /* Schedule type queue */
- odp_queue_param_init(&qparams);
- qparams.type = ODP_QUEUE_TYPE_SCHED;
- qparams.sched.prio = ODP_SCHED_PRIO_LOWEST;
- qparams.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qparams.sched.group = ODP_SCHED_GROUP_WORKER;
-
- queue = odp_queue_create("test_queue", &qparams);
- CU_ASSERT(ODP_QUEUE_INVALID != queue);
- CU_ASSERT(odp_queue_to_u64(queue) !=
- odp_queue_to_u64(ODP_QUEUE_INVALID));
- CU_ASSERT(queue == odp_queue_lookup("test_queue"));
- CU_ASSERT(ODP_QUEUE_TYPE_SCHED == odp_queue_type(queue));
- CU_ASSERT(ODP_SCHED_PRIO_LOWEST == odp_queue_sched_prio(queue));
- CU_ASSERT(ODP_SCHED_SYNC_PARALLEL == odp_queue_sched_type(queue));
- CU_ASSERT(ODP_SCHED_GROUP_WORKER == odp_queue_sched_group(queue));
-
- CU_ASSERT(0 == odp_queue_context_set(queue, &queue_context,
- sizeof(queue_context)));
-
- CU_ASSERT(&queue_context == odp_queue_context(queue));
- CU_ASSERT(odp_queue_destroy(queue) == 0);
-
- /* Create queue with no name */
- odp_queue_param_init(&qparams);
- null_queue = odp_queue_create(NULL, &qparams);
- CU_ASSERT(ODP_QUEUE_INVALID != null_queue);
-
- /* Plain type queue */
- odp_queue_param_init(&qparams);
- qparams.type = ODP_QUEUE_TYPE_PLAIN;
- qparams.context = &queue_context;
- qparams.context_len = sizeof(queue_context);
-
- queue = odp_queue_create("test_queue", &qparams);
- CU_ASSERT(ODP_QUEUE_INVALID != queue);
- CU_ASSERT(queue == odp_queue_lookup("test_queue"));
- CU_ASSERT(ODP_QUEUE_TYPE_PLAIN == odp_queue_type(queue));
- CU_ASSERT(&queue_context == odp_queue_context(queue));
-
- /* Destroy queue with no name */
- CU_ASSERT(odp_queue_destroy(null_queue) == 0);
-
- msg_pool = odp_pool_lookup("msg_pool");
- buf = odp_buffer_alloc(msg_pool);
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
- ev = odp_buffer_to_event(buf);
-
- if (!(CU_ASSERT(odp_queue_enq(queue, ev) == 0))) {
- odp_buffer_free(buf);
- } else {
- CU_ASSERT(ev == odp_queue_deq(queue));
- odp_buffer_free(buf);
- }
-
- for (i = 0; i < MAX_BUFFER_QUEUE; i++) {
- buf = odp_buffer_alloc(msg_pool);
- enev[i] = odp_buffer_to_event(buf);
- }
-
- /*
- * odp_queue_enq_multi may return 0..n buffers due to the resource
- * constraints in the implementation at that given point of time.
- * But here we assume that we succeed in enqueuing all buffers.
- */
- ret = odp_queue_enq_multi(queue, enev, MAX_BUFFER_QUEUE);
- CU_ASSERT(MAX_BUFFER_QUEUE == ret);
- i = ret < 0 ? 0 : ret;
- for ( ; i < MAX_BUFFER_QUEUE; i++)
- odp_event_free(enev[i]);
-
- pev_tmp = deev;
- do {
- deq_ret = odp_queue_deq_multi(queue, pev_tmp,
- MAX_BUFFER_QUEUE);
- nr_deq_entries += deq_ret;
- max_iteration--;
- pev_tmp += deq_ret;
- CU_ASSERT(max_iteration >= 0);
- } while (nr_deq_entries < MAX_BUFFER_QUEUE);
-
- for (i = 0; i < MAX_BUFFER_QUEUE; i++) {
- enbuf = odp_buffer_from_event(enev[i]);
- CU_ASSERT(enev[i] == deev[i]);
- odp_buffer_free(enbuf);
- }
-
- CU_ASSERT(odp_queue_destroy(queue) == 0);
-}
-
-void queue_test_info(void)
-{
- odp_queue_t q_plain, q_order;
- const char *const nq_plain = "test_q_plain";
- const char *const nq_order = "test_q_order";
- odp_queue_info_t info;
- odp_queue_param_t param;
- odp_queue_capability_t capability;
- char q_plain_ctx[] = "test_q_plain context data";
- char q_order_ctx[] = "test_q_order context data";
- unsigned lock_count;
- char *ctx;
- int ret;
-
- /* Create a plain queue and set context */
- q_plain = odp_queue_create(nq_plain, NULL);
- CU_ASSERT(ODP_QUEUE_INVALID != q_plain);
- CU_ASSERT(odp_queue_context_set(q_plain, q_plain_ctx,
- sizeof(q_plain_ctx)) == 0);
-
- memset(&capability, 0, sizeof(odp_queue_capability_t));
- CU_ASSERT(odp_queue_capability(&capability) == 0);
- /* Create a scheduled ordered queue with explicitly set params */
- odp_queue_param_init(&param);
- param.type = ODP_QUEUE_TYPE_SCHED;
- param.sched.prio = ODP_SCHED_PRIO_NORMAL;
- param.sched.sync = ODP_SCHED_SYNC_ORDERED;
- param.sched.group = ODP_SCHED_GROUP_ALL;
- if (capability.max_ordered_locks)
- param.sched.lock_count = 1;
- else
- param.sched.lock_count = 0;
- param.context = q_order_ctx;
- q_order = odp_queue_create(nq_order, &param);
- CU_ASSERT(ODP_QUEUE_INVALID != q_order);
-
- /* Check info for the plain queue */
- CU_ASSERT(odp_queue_info(q_plain, &info) == 0);
- CU_ASSERT(strcmp(nq_plain, info.name) == 0);
- CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_PLAIN);
- CU_ASSERT(info.param.type == odp_queue_type(q_plain));
- ctx = info.param.context; /* 'char' context ptr */
- CU_ASSERT(ctx == q_plain_ctx);
- CU_ASSERT(info.param.context == odp_queue_context(q_plain));
-
- /* Check info for the scheduled ordered queue */
- CU_ASSERT(odp_queue_info(q_order, &info) == 0);
- CU_ASSERT(strcmp(nq_order, info.name) == 0);
- CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_SCHED);
- CU_ASSERT(info.param.type == odp_queue_type(q_order));
- ctx = info.param.context; /* 'char' context ptr */
- CU_ASSERT(ctx == q_order_ctx);
- CU_ASSERT(info.param.context == odp_queue_context(q_order));
- CU_ASSERT(info.param.sched.prio == odp_queue_sched_prio(q_order));
- CU_ASSERT(info.param.sched.sync == odp_queue_sched_type(q_order));
- CU_ASSERT(info.param.sched.group == odp_queue_sched_group(q_order));
- ret = odp_queue_lock_count(q_order);
- CU_ASSERT(ret >= 0);
- lock_count = (unsigned)ret;
- CU_ASSERT(info.param.sched.lock_count == lock_count);
-
- CU_ASSERT(odp_queue_destroy(q_plain) == 0);
- CU_ASSERT(odp_queue_destroy(q_order) == 0);
-}
-
-odp_testinfo_t queue_suite[] = {
- ODP_TEST_INFO(queue_test_capa),
- ODP_TEST_INFO(queue_test_mode),
- ODP_TEST_INFO(queue_test_param),
- ODP_TEST_INFO(queue_test_info),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t queue_suites[] = {
- {"Queue", queue_suite_init, queue_suite_term, queue_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int queue_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(queue_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/queue/queue.h b/test/common_plat/validation/api/queue/queue.h
deleted file mode 100644
index 6b787b1d6..000000000
--- a/test/common_plat/validation/api/queue/queue.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_QUEUE_H_
-#define _ODP_TEST_QUEUE_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void queue_test_capa(void);
-void queue_test_mode(void);
-void queue_test_param(void);
-void queue_test_info(void);
-
-/* test arrays: */
-extern odp_testinfo_t queue_suite[];
-
-/* test array init/term functions: */
-int queue_suite_init(void);
-int queue_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t queue_suites[];
-
-/* main test program: */
-int queue_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/queue/queue_main.c b/test/common_plat/validation/api/queue/queue_main.c
deleted file mode 100644
index b461b860a..000000000
--- a/test/common_plat/validation/api/queue/queue_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "queue.h"
-
-int main(int argc, char *argv[])
-{
- return queue_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/random/Makefile.am b/test/common_plat/validation/api/random/Makefile.am
deleted file mode 100644
index 69259a4db..000000000
--- a/test/common_plat/validation/api/random/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestrandom.la
-libtestrandom_la_SOURCES = random.c
-
-test_PROGRAMS = random_main$(EXEEXT)
-dist_random_main_SOURCES = random_main.c
-random_main_LDADD = libtestrandom.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = random.h
diff --git a/test/common_plat/validation/api/random/random.c b/test/common_plat/validation/api/random/random.c
deleted file mode 100644
index a0e2ef72f..000000000
--- a/test/common_plat/validation/api/random/random.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "random.h"
-
-void random_test_get_size(void)
-{
- int32_t ret;
- uint8_t buf[32];
-
- ret = odp_random_data(buf, sizeof(buf), ODP_RANDOM_BASIC);
- CU_ASSERT(ret == sizeof(buf));
-}
-
-void random_test_kind(void)
-{
- int32_t rc;
- uint8_t buf[4096];
- uint32_t buf_size = sizeof(buf);
- odp_random_kind_t max_kind = odp_random_max_kind();
-
- rc = odp_random_data(buf, buf_size, max_kind);
- CU_ASSERT(rc > 0);
-
- switch (max_kind) {
- case ODP_RANDOM_BASIC:
- rc = odp_random_data(buf, 4, ODP_RANDOM_CRYPTO);
- CU_ASSERT(rc < 0);
- /* Fall through */
-
- case ODP_RANDOM_CRYPTO:
- rc = odp_random_data(buf, 4, ODP_RANDOM_TRUE);
- CU_ASSERT(rc < 0);
- break;
-
- default:
- break;
- }
-}
-
-void random_test_repeat(void)
-{
- uint8_t buf1[1024];
- uint8_t buf2[1024];
- int32_t rc;
- uint64_t seed1 = 12345897;
- uint64_t seed2 = seed1;
-
- rc = odp_random_test_data(buf1, sizeof(buf1), &seed1);
- CU_ASSERT(rc == sizeof(buf1));
-
- rc = odp_random_test_data(buf2, sizeof(buf2), &seed2);
- CU_ASSERT(rc == sizeof(buf2));
-
- CU_ASSERT(seed1 == seed2);
- CU_ASSERT(memcmp(buf1, buf2, sizeof(buf1)) == 0);
-}
-
-odp_testinfo_t random_suite[] = {
- ODP_TEST_INFO(random_test_get_size),
- ODP_TEST_INFO(random_test_kind),
- ODP_TEST_INFO(random_test_repeat),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t random_suites[] = {
- {"Random", NULL, NULL, random_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int random_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(random_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/random/random.h b/test/common_plat/validation/api/random/random.h
deleted file mode 100644
index c4bca7827..000000000
--- a/test/common_plat/validation/api/random/random.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_RANDOM_H_
-#define _ODP_TEST_RANDOM_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void random_test_get_size(void);
-void random_test_kind(void);
-void random_test_repeat(void);
-
-/* test arrays: */
-extern odp_testinfo_t random_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t random_suites[];
-
-/* main test program: */
-int random_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/random/random_main.c b/test/common_plat/validation/api/random/random_main.c
deleted file mode 100644
index 8f38a84c6..000000000
--- a/test/common_plat/validation/api/random/random_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "random.h"
-
-int main(int argc, char *argv[])
-{
- return random_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/scheduler/.gitignore b/test/common_plat/validation/api/scheduler/.gitignore
deleted file mode 100644
index b4eb30091..000000000
--- a/test/common_plat/validation/api/scheduler/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-scheduler_main
diff --git a/test/common_plat/validation/api/scheduler/Makefile.am b/test/common_plat/validation/api/scheduler/Makefile.am
deleted file mode 100644
index 2555cab81..000000000
--- a/test/common_plat/validation/api/scheduler/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestscheduler.la
-libtestscheduler_la_SOURCES = scheduler.c
-
-test_PROGRAMS = scheduler_main$(EXEEXT)
-dist_scheduler_main_SOURCES = scheduler_main.c
-scheduler_main_LDADD = libtestscheduler.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = scheduler.h
diff --git a/test/common_plat/validation/api/scheduler/scheduler.c b/test/common_plat/validation/api/scheduler/scheduler.c
deleted file mode 100644
index 4f99435c9..000000000
--- a/test/common_plat/validation/api/scheduler/scheduler.c
+++ /dev/null
@@ -1,1673 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include "odp_cunit_common.h"
-#include "scheduler.h"
-
-#define MAX_WORKERS_THREADS 32
-#define MAX_ORDERED_LOCKS 2
-#define MSG_POOL_SIZE (64 * 1024)
-#define QUEUES_PER_PRIO 16
-#define BUF_SIZE 64
-#define BUFS_PER_QUEUE 100
-#define BUFS_PER_QUEUE_EXCL 10000
-#define BURST_BUF_SIZE 4
-#define NUM_BUFS_PAUSE 1000
-#define NUM_BUFS_BEFORE_PAUSE 10
-#define NUM_GROUPS 2
-
-#define GLOBALS_SHM_NAME "test_globals"
-#define MSG_POOL_NAME "msg_pool"
-#define QUEUE_CTX_POOL_NAME "queue_ctx_pool"
-#define SHM_THR_ARGS_NAME "shm_thr_args"
-
-#define ONE_Q 1
-#define MANY_QS QUEUES_PER_PRIO
-
-#define ONE_PRIO 1
-
-#define SCHD_ONE 0
-#define SCHD_MULTI 1
-
-#define DISABLE_EXCL_ATOMIC 0
-#define ENABLE_EXCL_ATOMIC 1
-
-#define MAGIC 0xdeadbeef
-#define MAGIC1 0xdeadbeef
-#define MAGIC2 0xcafef00d
-
-#define CHAOS_NUM_QUEUES 6
-#define CHAOS_NUM_BUFS_PER_QUEUE 6
-#define CHAOS_NUM_ROUNDS 1000
-#define CHAOS_NUM_EVENTS (CHAOS_NUM_QUEUES * CHAOS_NUM_BUFS_PER_QUEUE)
-#define CHAOS_DEBUG (CHAOS_NUM_ROUNDS < 1000)
-#define CHAOS_PTR_TO_NDX(p) ((uint64_t)(uint32_t)(uintptr_t)p)
-#define CHAOS_NDX_TO_PTR(n) ((void *)(uintptr_t)n)
-
-#define ODP_WAIT_TOLERANCE (60 * ODP_TIME_MSEC_IN_NS)
-
-/* Test global variables */
-typedef struct {
- int num_workers;
- odp_barrier_t barrier;
- int buf_count;
- int buf_count_cpy;
- odp_ticketlock_t lock;
- odp_spinlock_t atomic_lock;
- struct {
- odp_queue_t handle;
- char name[ODP_QUEUE_NAME_LEN];
- } chaos_q[CHAOS_NUM_QUEUES];
-} test_globals_t;
-
-typedef struct {
- pthrd_arg cu_thr;
- test_globals_t *globals;
- odp_schedule_sync_t sync;
- int num_queues;
- int num_prio;
- int num_bufs;
- int num_workers;
- int enable_schd_multi;
- int enable_excl_atomic;
-} thread_args_t;
-
-typedef struct {
- uint64_t sequence;
- uint64_t lock_sequence[MAX_ORDERED_LOCKS];
- uint64_t output_sequence;
-} buf_contents;
-
-typedef struct {
- odp_buffer_t ctx_handle;
- odp_queue_t pq_handle;
- uint64_t sequence;
- uint64_t lock_sequence[MAX_ORDERED_LOCKS];
-} queue_context;
-
-typedef struct {
- uint64_t evno;
- uint64_t seqno;
-} chaos_buf;
-
-odp_pool_t pool;
-odp_pool_t queue_ctx_pool;
-
-static int drain_queues(void)
-{
- odp_event_t ev;
- uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
- int ret = 0;
-
- while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID) {
- odp_event_free(ev);
- ret++;
- }
-
- return ret;
-}
-
-static int exit_schedule_loop(void)
-{
- odp_event_t ev;
- int ret = 0;
-
- odp_schedule_pause();
-
- while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT))
- != ODP_EVENT_INVALID) {
- odp_event_free(ev);
- ret++;
- }
-
- odp_schedule_resume();
-
- return ret;
-}
-
-static void release_context(odp_schedule_sync_t sync)
-{
- if (sync == ODP_SCHED_SYNC_ATOMIC)
- odp_schedule_release_atomic();
- else if (sync == ODP_SCHED_SYNC_ORDERED)
- odp_schedule_release_ordered();
-}
-
-void scheduler_test_wait_time(void)
-{
- int i;
- odp_queue_t queue;
- uint64_t wait_time;
- odp_queue_param_t qp;
- odp_time_t lower_limit, upper_limit;
- odp_time_t start_time, end_time, diff;
-
- /* check on read */
- wait_time = odp_schedule_wait_time(0);
- wait_time = odp_schedule_wait_time(1);
-
- /* check ODP_SCHED_NO_WAIT */
- odp_queue_param_init(&qp);
- qp.type = ODP_QUEUE_TYPE_SCHED;
- qp.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- qp.sched.prio = ODP_SCHED_PRIO_NORMAL;
- qp.sched.group = ODP_SCHED_GROUP_ALL;
- queue = odp_queue_create("dummy_queue", &qp);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- wait_time = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS);
- start_time = odp_time_local();
- odp_schedule(&queue, ODP_SCHED_NO_WAIT);
- end_time = odp_time_local();
-
- diff = odp_time_diff(end_time, start_time);
- lower_limit = ODP_TIME_NULL;
- upper_limit = odp_time_local_from_ns(ODP_WAIT_TOLERANCE);
-
- CU_ASSERT(odp_time_cmp(diff, lower_limit) >= 0);
- CU_ASSERT(odp_time_cmp(diff, upper_limit) <= 0);
-
- /* check time correctness */
- start_time = odp_time_local();
- for (i = 1; i < 6; i++) {
- odp_schedule(&queue, wait_time);
- printf("%d..", i);
- }
- end_time = odp_time_local();
-
- diff = odp_time_diff(end_time, start_time);
- lower_limit = odp_time_local_from_ns(5 * ODP_TIME_SEC_IN_NS -
- ODP_WAIT_TOLERANCE);
- upper_limit = odp_time_local_from_ns(5 * ODP_TIME_SEC_IN_NS +
- ODP_WAIT_TOLERANCE);
-
- CU_ASSERT(odp_time_cmp(diff, lower_limit) >= 0);
- CU_ASSERT(odp_time_cmp(diff, upper_limit) <= 0);
-
- CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
-}
-
-void scheduler_test_num_prio(void)
-{
- int prio;
-
- prio = odp_schedule_num_prio();
-
- CU_ASSERT(prio > 0);
- CU_ASSERT(prio == odp_schedule_num_prio());
-}
-
-void scheduler_test_queue_destroy(void)
-{
- odp_pool_t p;
- odp_pool_param_t params;
- odp_queue_param_t qp;
- odp_queue_t queue, from;
- odp_buffer_t buf;
- odp_event_t ev;
- uint32_t *u32;
- int i;
- odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
- ODP_SCHED_SYNC_ATOMIC,
- ODP_SCHED_SYNC_ORDERED};
-
- odp_queue_param_init(&qp);
- odp_pool_param_init(&params);
- params.buf.size = 100;
- params.buf.align = 0;
- params.buf.num = 1;
- params.type = ODP_POOL_BUFFER;
-
- p = odp_pool_create("sched_destroy_pool", &params);
-
- CU_ASSERT_FATAL(p != ODP_POOL_INVALID);
-
- for (i = 0; i < 3; i++) {
- qp.type = ODP_QUEUE_TYPE_SCHED;
- qp.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qp.sched.sync = sync[i];
- qp.sched.group = ODP_SCHED_GROUP_ALL;
-
- queue = odp_queue_create("sched_destroy_queue", &qp);
-
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- buf = odp_buffer_alloc(p);
-
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
-
- u32 = odp_buffer_addr(buf);
- u32[0] = MAGIC;
-
- ev = odp_buffer_to_event(buf);
- if (!(CU_ASSERT(odp_queue_enq(queue, ev) == 0)))
- odp_buffer_free(buf);
-
- ev = odp_schedule(&from, ODP_SCHED_WAIT);
-
- CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
-
- CU_ASSERT_FATAL(from == queue);
-
- buf = odp_buffer_from_event(ev);
- u32 = odp_buffer_addr(buf);
-
- CU_ASSERT_FATAL(u32[0] == MAGIC);
-
- odp_buffer_free(buf);
- release_context(qp.sched.sync);
- CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
- }
-
- CU_ASSERT_FATAL(odp_pool_destroy(p) == 0);
-}
-
-void scheduler_test_groups(void)
-{
- odp_pool_t p;
- odp_pool_param_t params;
- odp_queue_t queue_grp1, queue_grp2;
- odp_buffer_t buf;
- odp_event_t ev;
- uint32_t *u32;
- int i, j, rc;
- odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
- ODP_SCHED_SYNC_ATOMIC,
- ODP_SCHED_SYNC_ORDERED};
- int thr_id = odp_thread_id();
- odp_thrmask_t zeromask, mymask, testmask;
- odp_schedule_group_t mygrp1, mygrp2, null_grp, lookup;
- odp_schedule_group_info_t info;
-
- odp_thrmask_zero(&zeromask);
- odp_thrmask_zero(&mymask);
- odp_thrmask_set(&mymask, thr_id);
-
- /* Can't find a group before we create it */
- lookup = odp_schedule_group_lookup("Test Group 1");
- CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID);
-
- /* Now create the group */
- mygrp1 = odp_schedule_group_create("Test Group 1", &zeromask);
- CU_ASSERT_FATAL(mygrp1 != ODP_SCHED_GROUP_INVALID);
-
- /* Verify we can now find it */
- lookup = odp_schedule_group_lookup("Test Group 1");
- CU_ASSERT(lookup == mygrp1);
-
- /* Threadmask should be retrievable and be what we expect */
- rc = odp_schedule_group_thrmask(mygrp1, &testmask);
- CU_ASSERT(rc == 0);
- CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
-
- /* Now join the group and verify we're part of it */
- rc = odp_schedule_group_join(mygrp1, &mymask);
- CU_ASSERT(rc == 0);
-
- rc = odp_schedule_group_thrmask(mygrp1, &testmask);
- CU_ASSERT(rc == 0);
- CU_ASSERT(odp_thrmask_isset(&testmask, thr_id));
-
- /* Info struct */
- memset(&info, 0, sizeof(odp_schedule_group_info_t));
- rc = odp_schedule_group_info(mygrp1, &info);
- CU_ASSERT(rc == 0);
- CU_ASSERT(odp_thrmask_equal(&info.thrmask, &mymask) != 0);
- CU_ASSERT(strcmp(info.name, "Test Group 1") == 0);
-
- /* We can't join or leave an unknown group */
- rc = odp_schedule_group_join(ODP_SCHED_GROUP_INVALID, &mymask);
- CU_ASSERT(rc != 0);
-
- rc = odp_schedule_group_leave(ODP_SCHED_GROUP_INVALID, &mymask);
- CU_ASSERT(rc != 0);
-
- /* But we can leave our group */
- rc = odp_schedule_group_leave(mygrp1, &mymask);
- CU_ASSERT(rc == 0);
-
- rc = odp_schedule_group_thrmask(mygrp1, &testmask);
- CU_ASSERT(rc == 0);
- CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
-
- /* Create group with no name */
- null_grp = odp_schedule_group_create(NULL, &zeromask);
- CU_ASSERT(null_grp != ODP_SCHED_GROUP_INVALID);
-
- /* We shouldn't be able to find our second group before creating it */
- lookup = odp_schedule_group_lookup("Test Group 2");
- CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID);
-
- /* Now create it and verify we can find it */
- mygrp2 = odp_schedule_group_create("Test Group 2", &zeromask);
- CU_ASSERT_FATAL(mygrp2 != ODP_SCHED_GROUP_INVALID);
-
- lookup = odp_schedule_group_lookup("Test Group 2");
- CU_ASSERT(lookup == mygrp2);
-
- /* Destroy group with no name */
- CU_ASSERT_FATAL(odp_schedule_group_destroy(null_grp) == 0);
-
- /* Verify we're not part of it */
- rc = odp_schedule_group_thrmask(mygrp2, &testmask);
- CU_ASSERT(rc == 0);
- CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
-
- /* Now join the group and verify we're part of it */
- rc = odp_schedule_group_join(mygrp2, &mymask);
- CU_ASSERT(rc == 0);
-
- rc = odp_schedule_group_thrmask(mygrp2, &testmask);
- CU_ASSERT(rc == 0);
- CU_ASSERT(odp_thrmask_isset(&testmask, thr_id));
-
- /* Now verify scheduler adherence to groups */
- odp_pool_param_init(&params);
- params.buf.size = 100;
- params.buf.align = 0;
- params.buf.num = 2;
- params.type = ODP_POOL_BUFFER;
-
- p = odp_pool_create("sched_group_pool", &params);
-
- CU_ASSERT_FATAL(p != ODP_POOL_INVALID);
-
- for (i = 0; i < 3; i++) {
- odp_queue_param_t qp;
- odp_queue_t queue, from;
- odp_schedule_group_t mygrp[NUM_GROUPS];
- odp_queue_t queue_grp[NUM_GROUPS];
- int num = NUM_GROUPS;
-
- odp_queue_param_init(&qp);
- qp.type = ODP_QUEUE_TYPE_SCHED;
- qp.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qp.sched.sync = sync[i];
- qp.sched.group = mygrp1;
-
- /* Create and populate a group in group 1 */
- queue_grp1 = odp_queue_create("sched_group_test_queue_1", &qp);
- CU_ASSERT_FATAL(queue_grp1 != ODP_QUEUE_INVALID);
- CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp1) == mygrp1);
-
- buf = odp_buffer_alloc(p);
-
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
-
- u32 = odp_buffer_addr(buf);
- u32[0] = MAGIC1;
-
- ev = odp_buffer_to_event(buf);
- rc = odp_queue_enq(queue_grp1, ev);
- CU_ASSERT(rc == 0);
- if (rc)
- odp_buffer_free(buf);
-
- /* Now create and populate a queue in group 2 */
- qp.sched.group = mygrp2;
- queue_grp2 = odp_queue_create("sched_group_test_queue_2", &qp);
- CU_ASSERT_FATAL(queue_grp2 != ODP_QUEUE_INVALID);
- CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp2) == mygrp2);
-
- buf = odp_buffer_alloc(p);
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
-
- u32 = odp_buffer_addr(buf);
- u32[0] = MAGIC2;
-
- ev = odp_buffer_to_event(buf);
- rc = odp_queue_enq(queue_grp2, ev);
- CU_ASSERT(rc == 0);
- if (rc)
- odp_buffer_free(buf);
-
- /* Swap between two groups. Application should serve both
- * groups to avoid potential head of line blocking in
- * scheduler. */
- mygrp[0] = mygrp1;
- mygrp[1] = mygrp2;
- queue_grp[0] = queue_grp1;
- queue_grp[1] = queue_grp2;
- j = 0;
-
- /* Ensure that each test run starts from mygrp1 */
- odp_schedule_group_leave(mygrp1, &mymask);
- odp_schedule_group_leave(mygrp2, &mymask);
- odp_schedule_group_join(mygrp1, &mymask);
-
- while (num) {
- queue = queue_grp[j];
- ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
-
- if (ev == ODP_EVENT_INVALID) {
- /* change group */
- rc = odp_schedule_group_leave(mygrp[j],
- &mymask);
- CU_ASSERT_FATAL(rc == 0);
-
- j = (j + 1) % NUM_GROUPS;
- rc = odp_schedule_group_join(mygrp[j],
- &mymask);
- CU_ASSERT_FATAL(rc == 0);
- continue;
- }
-
- CU_ASSERT_FATAL(from == queue);
-
- buf = odp_buffer_from_event(ev);
- u32 = odp_buffer_addr(buf);
-
- if (from == queue_grp1) {
- /* CU_ASSERT_FATAL needs these brackets */
- CU_ASSERT_FATAL(u32[0] == MAGIC1);
- } else {
- CU_ASSERT_FATAL(u32[0] == MAGIC2);
- }
-
- odp_buffer_free(buf);
-
- /* Tell scheduler we're about to request an event.
- * Not needed, but a convenient place to test this API.
- */
- odp_schedule_prefetch(1);
-
- num--;
- }
-
- /* Release schduler context and leave groups */
- odp_schedule_group_join(mygrp1, &mymask);
- odp_schedule_group_join(mygrp2, &mymask);
- CU_ASSERT(exit_schedule_loop() == 0);
- odp_schedule_group_leave(mygrp1, &mymask);
- odp_schedule_group_leave(mygrp2, &mymask);
-
- /* Done with queues for this round */
- CU_ASSERT_FATAL(odp_queue_destroy(queue_grp1) == 0);
- CU_ASSERT_FATAL(odp_queue_destroy(queue_grp2) == 0);
-
- /* Verify we can no longer find our queues */
- CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_1") ==
- ODP_QUEUE_INVALID);
- CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_2") ==
- ODP_QUEUE_INVALID);
- }
-
- CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp1) == 0);
- CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp2) == 0);
- CU_ASSERT_FATAL(odp_pool_destroy(p) == 0);
-}
-
-static int chaos_thread(void *arg)
-{
- uint64_t i, wait;
- int rc;
- chaos_buf *cbuf;
- odp_event_t ev;
- odp_queue_t from;
- thread_args_t *args = (thread_args_t *)arg;
- test_globals_t *globals = args->globals;
- int me = odp_thread_id();
- odp_time_t start_time, end_time, diff;
-
- if (CHAOS_DEBUG)
- printf("Chaos thread %d starting...\n", me);
-
- /* Wait for all threads to start */
- odp_barrier_wait(&globals->barrier);
- start_time = odp_time_local();
-
- /* Run the test */
- wait = odp_schedule_wait_time(5 * ODP_TIME_MSEC_IN_NS);
- for (i = 0; i < CHAOS_NUM_ROUNDS; i++) {
- ev = odp_schedule(&from, wait);
- if (ev == ODP_EVENT_INVALID)
- continue;
-
- cbuf = odp_buffer_addr(odp_buffer_from_event(ev));
- CU_ASSERT_FATAL(cbuf != NULL);
- if (CHAOS_DEBUG)
- printf("Thread %d received event %" PRIu64
- " seq %" PRIu64
- " from Q %s, sending to Q %s\n",
- me, cbuf->evno, cbuf->seqno,
- globals->
- chaos_q
- [CHAOS_PTR_TO_NDX(odp_queue_context(from))].name,
- globals->
- chaos_q[cbuf->seqno % CHAOS_NUM_QUEUES].name);
-
- rc = odp_queue_enq(
- globals->
- chaos_q[cbuf->seqno++ % CHAOS_NUM_QUEUES].handle,
- ev);
- CU_ASSERT_FATAL(rc == 0);
- }
-
- if (CHAOS_DEBUG)
- printf("Thread %d completed %d rounds...terminating\n",
- odp_thread_id(), CHAOS_NUM_EVENTS);
-
- exit_schedule_loop();
-
- end_time = odp_time_local();
- diff = odp_time_diff(end_time, start_time);
-
- printf("Thread %d ends, elapsed time = %" PRIu64 "us\n",
- odp_thread_id(), odp_time_to_ns(diff) / 1000);
-
- return 0;
-}
-
-static void chaos_run(unsigned int qtype)
-{
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_queue_param_t qp;
- odp_buffer_t buf;
- chaos_buf *cbuf;
- test_globals_t *globals;
- thread_args_t *args;
- odp_shm_t shm;
- int i, rc;
- odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
- ODP_SCHED_SYNC_ATOMIC,
- ODP_SCHED_SYNC_ORDERED};
- const unsigned num_sync = (sizeof(sync) / sizeof(odp_schedule_sync_t));
- const char *const qtypes[] = {"parallel", "atomic", "ordered"};
-
- /* Set up the scheduling environment */
- shm = odp_shm_lookup(GLOBALS_SHM_NAME);
- CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
- globals = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL_FATAL(globals);
-
- shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
- CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
- args = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL_FATAL(args);
-
- args->globals = globals;
- args->cu_thr.numthrds = globals->num_workers;
-
- odp_queue_param_init(&qp);
- odp_pool_param_init(&params);
- params.buf.size = sizeof(chaos_buf);
- params.buf.align = 0;
- params.buf.num = CHAOS_NUM_EVENTS;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create("sched_chaos_pool", &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- qp.type = ODP_QUEUE_TYPE_SCHED;
- qp.sched.prio = ODP_SCHED_PRIO_DEFAULT;
- qp.sched.group = ODP_SCHED_GROUP_ALL;
-
- for (i = 0; i < CHAOS_NUM_QUEUES; i++) {
- uint32_t ndx = (qtype == num_sync ? i % num_sync : qtype);
-
- qp.sched.sync = sync[ndx];
- snprintf(globals->chaos_q[i].name,
- sizeof(globals->chaos_q[i].name),
- "chaos queue %d - %s", i,
- qtypes[ndx]);
-
- globals->chaos_q[i].handle =
- odp_queue_create(globals->chaos_q[i].name, &qp);
- CU_ASSERT_FATAL(globals->chaos_q[i].handle !=
- ODP_QUEUE_INVALID);
- rc = odp_queue_context_set(globals->chaos_q[i].handle,
- CHAOS_NDX_TO_PTR(i), 0);
- CU_ASSERT_FATAL(rc == 0);
- }
-
- /* Now populate the queues with the initial seed elements */
- for (i = 0; i < CHAOS_NUM_EVENTS; i++) {
- buf = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
- cbuf = odp_buffer_addr(buf);
- cbuf->evno = i;
- cbuf->seqno = 0;
- rc = odp_queue_enq(
- globals->chaos_q[i % CHAOS_NUM_QUEUES].handle,
- odp_buffer_to_event(buf));
- CU_ASSERT_FATAL(rc == 0);
- }
-
- /* Run the test */
- odp_cunit_thread_create(chaos_thread, &args->cu_thr);
- odp_cunit_thread_exit(&args->cu_thr);
-
- if (CHAOS_DEBUG)
- printf("Thread %d returning from chaos threads..cleaning up\n",
- odp_thread_id());
-
- drain_queues();
- exit_schedule_loop();
-
- for (i = 0; i < CHAOS_NUM_QUEUES; i++) {
- if (CHAOS_DEBUG)
- printf("Destroying queue %s\n",
- globals->chaos_q[i].name);
- rc = odp_queue_destroy(globals->chaos_q[i].handle);
- CU_ASSERT(rc == 0);
- }
-
- rc = odp_pool_destroy(pool);
- CU_ASSERT(rc == 0);
-}
-
-void scheduler_test_parallel(void)
-{
- chaos_run(0);
-}
-
-void scheduler_test_atomic(void)
-{
- chaos_run(1);
-}
-
-void scheduler_test_ordered(void)
-{
- chaos_run(2);
-}
-
-void scheduler_test_chaos(void)
-{
- chaos_run(3);
-}
-
-static int schedule_common_(void *arg)
-{
- thread_args_t *args = (thread_args_t *)arg;
- odp_schedule_sync_t sync;
- test_globals_t *globals;
- queue_context *qctx;
- buf_contents *bctx, *bctx_cpy;
- odp_pool_t pool;
- int locked;
- int num;
- odp_event_t ev;
- odp_buffer_t buf, buf_cpy;
- odp_queue_t from;
-
- globals = args->globals;
- sync = args->sync;
-
- pool = odp_pool_lookup(MSG_POOL_NAME);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- if (args->num_workers > 1)
- odp_barrier_wait(&globals->barrier);
-
- while (1) {
- from = ODP_QUEUE_INVALID;
- num = 0;
-
- odp_ticketlock_lock(&globals->lock);
- if (globals->buf_count == 0) {
- odp_ticketlock_unlock(&globals->lock);
- break;
- }
- odp_ticketlock_unlock(&globals->lock);
-
- if (args->enable_schd_multi) {
- odp_event_t events[BURST_BUF_SIZE],
- ev_cpy[BURST_BUF_SIZE];
- odp_buffer_t buf_cpy[BURST_BUF_SIZE];
- int j;
-
- num = odp_schedule_multi(&from, ODP_SCHED_NO_WAIT,
- events, BURST_BUF_SIZE);
- CU_ASSERT(num >= 0);
- CU_ASSERT(num <= BURST_BUF_SIZE);
- if (num == 0)
- continue;
-
- if (sync == ODP_SCHED_SYNC_ORDERED) {
- int ndx;
- int ndx_max;
- int rc;
-
- ndx_max = odp_queue_lock_count(from);
- CU_ASSERT_FATAL(ndx_max >= 0);
-
- qctx = odp_queue_context(from);
-
- for (j = 0; j < num; j++) {
- bctx = odp_buffer_addr(
- odp_buffer_from_event
- (events[j]));
-
- buf_cpy[j] = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buf_cpy[j] !=
- ODP_BUFFER_INVALID);
- bctx_cpy = odp_buffer_addr(buf_cpy[j]);
- memcpy(bctx_cpy, bctx,
- sizeof(buf_contents));
- bctx_cpy->output_sequence =
- bctx_cpy->sequence;
- ev_cpy[j] =
- odp_buffer_to_event(buf_cpy[j]);
- }
-
- rc = odp_queue_enq_multi(qctx->pq_handle,
- ev_cpy, num);
- CU_ASSERT(rc == num);
-
- bctx = odp_buffer_addr(
- odp_buffer_from_event(events[0]));
- for (ndx = 0; ndx < ndx_max; ndx++) {
- odp_schedule_order_lock(ndx);
- CU_ASSERT(bctx->sequence ==
- qctx->lock_sequence[ndx]);
- qctx->lock_sequence[ndx] += num;
- odp_schedule_order_unlock(ndx);
- }
- }
-
- for (j = 0; j < num; j++)
- odp_event_free(events[j]);
- } else {
- ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
- if (ev == ODP_EVENT_INVALID)
- continue;
-
- buf = odp_buffer_from_event(ev);
- num = 1;
- if (sync == ODP_SCHED_SYNC_ORDERED) {
- int ndx;
- int ndx_max;
- int rc;
-
- ndx_max = odp_queue_lock_count(from);
- CU_ASSERT_FATAL(ndx_max >= 0);
-
- qctx = odp_queue_context(from);
- bctx = odp_buffer_addr(buf);
- buf_cpy = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buf_cpy != ODP_BUFFER_INVALID);
- bctx_cpy = odp_buffer_addr(buf_cpy);
- memcpy(bctx_cpy, bctx, sizeof(buf_contents));
- bctx_cpy->output_sequence = bctx_cpy->sequence;
-
- rc = odp_queue_enq(qctx->pq_handle,
- odp_buffer_to_event
- (buf_cpy));
- CU_ASSERT(rc == 0);
-
- for (ndx = 0; ndx < ndx_max; ndx++) {
- odp_schedule_order_lock(ndx);
- CU_ASSERT(bctx->sequence ==
- qctx->lock_sequence[ndx]);
- qctx->lock_sequence[ndx] += num;
- odp_schedule_order_unlock(ndx);
- }
- }
-
- odp_buffer_free(buf);
- }
-
- if (args->enable_excl_atomic) {
- locked = odp_spinlock_trylock(&globals->atomic_lock);
- CU_ASSERT(locked != 0);
- CU_ASSERT(from != ODP_QUEUE_INVALID);
- if (locked) {
- int cnt;
- odp_time_t time = ODP_TIME_NULL;
- /* Do some work here to keep the thread busy */
- for (cnt = 0; cnt < 1000; cnt++)
- time = odp_time_sum(time,
- odp_time_local());
-
- odp_spinlock_unlock(&globals->atomic_lock);
- }
- }
-
- release_context(sync);
- odp_ticketlock_lock(&globals->lock);
-
- globals->buf_count -= num;
-
- if (globals->buf_count < 0) {
- odp_ticketlock_unlock(&globals->lock);
- CU_FAIL_FATAL("Buffer counting failed");
- }
-
- odp_ticketlock_unlock(&globals->lock);
- }
-
- if (args->num_workers > 1)
- odp_barrier_wait(&globals->barrier);
-
- if (sync == ODP_SCHED_SYNC_ORDERED)
- locked = odp_ticketlock_trylock(&globals->lock);
- else
- locked = 0;
-
- if (locked && globals->buf_count_cpy > 0) {
- odp_event_t ev;
- odp_queue_t pq;
- uint64_t seq;
- uint64_t bcount = 0;
- int i, j;
- char name[32];
- uint64_t num_bufs = args->num_bufs;
- uint64_t buf_count = globals->buf_count_cpy;
-
- for (i = 0; i < args->num_prio; i++) {
- for (j = 0; j < args->num_queues; j++) {
- snprintf(name, sizeof(name),
- "plain_%d_%d_o", i, j);
- pq = odp_queue_lookup(name);
- CU_ASSERT_FATAL(pq != ODP_QUEUE_INVALID);
-
- seq = 0;
- while (1) {
- ev = odp_queue_deq(pq);
-
- if (ev == ODP_EVENT_INVALID) {
- CU_ASSERT(seq == num_bufs);
- break;
- }
-
- bctx = odp_buffer_addr(
- odp_buffer_from_event(ev));
-
- CU_ASSERT(bctx->sequence == seq);
- seq++;
- bcount++;
- odp_event_free(ev);
- }
- }
- }
- CU_ASSERT(bcount == buf_count);
- globals->buf_count_cpy = 0;
- }
-
- if (locked)
- odp_ticketlock_unlock(&globals->lock);
-
- /* Clear scheduler atomic / ordered context between tests */
- num = exit_schedule_loop();
-
- CU_ASSERT(num == 0);
-
- if (num)
- printf("\nDROPPED %i events\n\n", num);
-
- return 0;
-}
-
-static void fill_queues(thread_args_t *args)
-{
- odp_schedule_sync_t sync;
- int num_queues, num_prio;
- odp_pool_t pool;
- int i, j, k;
- int buf_count = 0;
- test_globals_t *globals;
- char name[32];
- int ret;
- odp_buffer_t buf;
- odp_event_t ev;
-
- globals = args->globals;
- sync = args->sync;
- num_queues = args->num_queues;
- num_prio = args->num_prio;
-
- pool = odp_pool_lookup(MSG_POOL_NAME);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- for (i = 0; i < num_prio; i++) {
- for (j = 0; j < num_queues; j++) {
- odp_queue_t queue;
-
- switch (sync) {
- case ODP_SCHED_SYNC_PARALLEL:
- snprintf(name, sizeof(name),
- "sched_%d_%d_n", i, j);
- break;
- case ODP_SCHED_SYNC_ATOMIC:
- snprintf(name, sizeof(name),
- "sched_%d_%d_a", i, j);
- break;
- case ODP_SCHED_SYNC_ORDERED:
- snprintf(name, sizeof(name),
- "sched_%d_%d_o", i, j);
- break;
- default:
- CU_ASSERT_FATAL(0);
- break;
- }
-
- queue = odp_queue_lookup(name);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- for (k = 0; k < args->num_bufs; k++) {
- buf = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
- ev = odp_buffer_to_event(buf);
- if (sync == ODP_SCHED_SYNC_ORDERED) {
- queue_context *qctx =
- odp_queue_context(queue);
- buf_contents *bctx =
- odp_buffer_addr(buf);
- bctx->sequence = qctx->sequence++;
- }
-
- ret = odp_queue_enq(queue, ev);
- CU_ASSERT_FATAL(ret == 0);
-
- if (ret)
- odp_buffer_free(buf);
- else
- buf_count++;
- }
- }
- }
-
- globals->buf_count = buf_count;
- globals->buf_count_cpy = buf_count;
-}
-
-static void reset_queues(thread_args_t *args)
-{
- int i, j, k;
- int num_prio = args->num_prio;
- int num_queues = args->num_queues;
- char name[32];
-
- for (i = 0; i < num_prio; i++) {
- for (j = 0; j < num_queues; j++) {
- odp_queue_t queue;
-
- snprintf(name, sizeof(name),
- "sched_%d_%d_o", i, j);
- queue = odp_queue_lookup(name);
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- for (k = 0; k < args->num_bufs; k++) {
- queue_context *qctx =
- odp_queue_context(queue);
- int ndx;
- int ndx_max;
-
- ndx_max = odp_queue_lock_count(queue);
- CU_ASSERT_FATAL(ndx_max >= 0);
- qctx->sequence = 0;
- for (ndx = 0; ndx < ndx_max; ndx++)
- qctx->lock_sequence[ndx] = 0;
- }
- }
- }
-}
-
-static void schedule_common(odp_schedule_sync_t sync, int num_queues,
- int num_prio, int enable_schd_multi)
-{
- thread_args_t args;
- odp_shm_t shm;
- test_globals_t *globals;
-
- shm = odp_shm_lookup(GLOBALS_SHM_NAME);
- CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
- globals = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL_FATAL(globals);
-
- memset(&args, 0, sizeof(thread_args_t));
- args.globals = globals;
- args.sync = sync;
- args.num_queues = num_queues;
- args.num_prio = num_prio;
- args.num_bufs = BUFS_PER_QUEUE;
- args.num_workers = 1;
- args.enable_schd_multi = enable_schd_multi;
- args.enable_excl_atomic = 0; /* Not needed with a single CPU */
-
- fill_queues(&args);
-
- schedule_common_(&args);
- if (sync == ODP_SCHED_SYNC_ORDERED)
- reset_queues(&args);
-}
-
-static void parallel_execute(odp_schedule_sync_t sync, int num_queues,
- int num_prio, int enable_schd_multi,
- int enable_excl_atomic)
-{
- odp_shm_t shm;
- test_globals_t *globals;
- thread_args_t *args;
-
- shm = odp_shm_lookup(GLOBALS_SHM_NAME);
- CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
- globals = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL_FATAL(globals);
-
- shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
- CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
- args = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL_FATAL(args);
-
- args->globals = globals;
- args->sync = sync;
- args->num_queues = num_queues;
- args->num_prio = num_prio;
- if (enable_excl_atomic)
- args->num_bufs = BUFS_PER_QUEUE_EXCL;
- else
- args->num_bufs = BUFS_PER_QUEUE;
- args->num_workers = globals->num_workers;
- args->enable_schd_multi = enable_schd_multi;
- args->enable_excl_atomic = enable_excl_atomic;
-
- fill_queues(args);
-
- /* Create and launch worker threads */
- args->cu_thr.numthrds = globals->num_workers;
- odp_cunit_thread_create(schedule_common_, &args->cu_thr);
-
- /* Wait for worker threads to terminate */
- odp_cunit_thread_exit(&args->cu_thr);
-
- /* Cleanup ordered queues for next pass */
- if (sync == ODP_SCHED_SYNC_ORDERED)
- reset_queues(args);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL */
-void scheduler_test_1q_1t_n(void)
-{
- schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_ONE);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC */
-void scheduler_test_1q_1t_a(void)
-{
- schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED */
-void scheduler_test_1q_1t_o(void)
-{
- schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_ONE);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL */
-void scheduler_test_mq_1t_n(void)
-{
- /* Only one priority involved in these tests, but use
- the same number of queues the more general case uses */
- schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, ONE_PRIO, SCHD_ONE);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC */
-void scheduler_test_mq_1t_a(void)
-{
- schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, ONE_PRIO, SCHD_ONE);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED */
-void scheduler_test_mq_1t_o(void)
-{
- schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, ONE_PRIO, SCHD_ONE);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL */
-void scheduler_test_mq_1t_prio_n(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_ONE);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC */
-void scheduler_test_mq_1t_prio_a(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_ONE);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED */
-void scheduler_test_mq_1t_prio_o(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_ONE);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL */
-void scheduler_test_mq_mt_prio_n(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_ONE,
- DISABLE_EXCL_ATOMIC);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC */
-void scheduler_test_mq_mt_prio_a(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_ONE,
- DISABLE_EXCL_ATOMIC);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED */
-void scheduler_test_mq_mt_prio_o(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_ONE,
- DISABLE_EXCL_ATOMIC);
-}
-
-/* 1 queue many threads check exclusive access on ATOMIC queues */
-void scheduler_test_1q_mt_a_excl(void)
-{
- parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE,
- ENABLE_EXCL_ATOMIC);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL multi */
-void scheduler_test_multi_1q_1t_n(void)
-{
- schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_MULTI);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC multi */
-void scheduler_test_multi_1q_1t_a(void)
-{
- schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI);
-}
-
-/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED multi */
-void scheduler_test_multi_1q_1t_o(void)
-{
- schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_MULTI);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL multi */
-void scheduler_test_multi_mq_1t_n(void)
-{
- /* Only one priority involved in these tests, but use
- the same number of queues the more general case uses */
- schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, ONE_PRIO, SCHD_MULTI);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC multi */
-void scheduler_test_multi_mq_1t_a(void)
-{
- schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, ONE_PRIO, SCHD_MULTI);
-}
-
-/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED multi */
-void scheduler_test_multi_mq_1t_o(void)
-{
- schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, ONE_PRIO, SCHD_MULTI);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL multi */
-void scheduler_test_multi_mq_1t_prio_n(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_MULTI);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC multi */
-void scheduler_test_multi_mq_1t_prio_a(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_MULTI);
-}
-
-/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED multi */
-void scheduler_test_multi_mq_1t_prio_o(void)
-{
- int prio = odp_schedule_num_prio();
-
- schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_MULTI);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL multi */
-void scheduler_test_multi_mq_mt_prio_n(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_MULTI, 0);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC multi */
-void scheduler_test_multi_mq_mt_prio_a(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_MULTI, 0);
-}
-
-/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED multi */
-void scheduler_test_multi_mq_mt_prio_o(void)
-{
- int prio = odp_schedule_num_prio();
-
- parallel_execute(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_MULTI, 0);
-}
-
-/* 1 queue many threads check exclusive access on ATOMIC queues multi */
-void scheduler_test_multi_1q_mt_a_excl(void)
-{
- parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI,
- ENABLE_EXCL_ATOMIC);
-}
-
-void scheduler_test_pause_resume(void)
-{
- odp_queue_t queue;
- odp_buffer_t buf;
- odp_event_t ev;
- odp_queue_t from;
- int i;
- int local_bufs = 0;
- int ret;
-
- queue = odp_queue_lookup("sched_0_0_n");
- CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
-
- pool = odp_pool_lookup(MSG_POOL_NAME);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- for (i = 0; i < NUM_BUFS_PAUSE; i++) {
- buf = odp_buffer_alloc(pool);
- CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
- ev = odp_buffer_to_event(buf);
- ret = odp_queue_enq(queue, ev);
- CU_ASSERT(ret == 0);
-
- if (ret)
- odp_buffer_free(buf);
- }
-
- for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
- from = ODP_QUEUE_INVALID;
- ev = odp_schedule(&from, ODP_SCHED_WAIT);
- CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
- CU_ASSERT(from == queue);
- buf = odp_buffer_from_event(ev);
- odp_buffer_free(buf);
- }
-
- odp_schedule_pause();
-
- while (1) {
- ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
- if (ev == ODP_EVENT_INVALID)
- break;
-
- CU_ASSERT(from == queue);
- buf = odp_buffer_from_event(ev);
- odp_buffer_free(buf);
- local_bufs++;
- }
-
- CU_ASSERT(local_bufs < NUM_BUFS_PAUSE - NUM_BUFS_BEFORE_PAUSE);
-
- odp_schedule_resume();
-
- for (i = local_bufs + NUM_BUFS_BEFORE_PAUSE; i < NUM_BUFS_PAUSE; i++) {
- ev = odp_schedule(&from, ODP_SCHED_WAIT);
- CU_ASSERT(from == queue);
- buf = odp_buffer_from_event(ev);
- odp_buffer_free(buf);
- }
-
- ret = exit_schedule_loop();
-
- CU_ASSERT(ret == 0);
-}
-
-static int create_queues(void)
-{
- int i, j, prios, rc;
- odp_queue_capability_t capa;
- odp_pool_param_t params;
- odp_buffer_t queue_ctx_buf;
- queue_context *qctx, *pqctx;
- uint32_t ndx;
- odp_queue_param_t p;
-
- if (odp_queue_capability(&capa) < 0) {
- printf("Queue capability query failed\n");
- return -1;
- }
-
- /* Limit to test maximum */
- if (capa.max_ordered_locks > MAX_ORDERED_LOCKS) {
- capa.max_ordered_locks = MAX_ORDERED_LOCKS;
- printf("Testing only %u ordered locks\n",
- capa.max_ordered_locks);
- }
-
- prios = odp_schedule_num_prio();
- odp_pool_param_init(&params);
- params.buf.size = sizeof(queue_context);
- params.buf.num = prios * QUEUES_PER_PRIO * 2;
- params.type = ODP_POOL_BUFFER;
-
- queue_ctx_pool = odp_pool_create(QUEUE_CTX_POOL_NAME, &params);
-
- if (queue_ctx_pool == ODP_POOL_INVALID) {
- printf("Pool creation failed (queue ctx).\n");
- return -1;
- }
-
- for (i = 0; i < prios; i++) {
- odp_queue_param_init(&p);
- p.type = ODP_QUEUE_TYPE_SCHED;
- p.sched.prio = i;
-
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- /* Per sched sync type */
- char name[32];
- odp_queue_t q, pq;
-
- snprintf(name, sizeof(name), "sched_%d_%d_n", i, j);
- p.sched.sync = ODP_SCHED_SYNC_PARALLEL;
- q = odp_queue_create(name, &p);
-
- if (q == ODP_QUEUE_INVALID) {
- printf("Schedule queue create failed.\n");
- return -1;
- }
-
- snprintf(name, sizeof(name), "sched_%d_%d_a", i, j);
- p.sched.sync = ODP_SCHED_SYNC_ATOMIC;
- p.size = BUFS_PER_QUEUE_EXCL;
- q = odp_queue_create(name, &p);
-
- if (q == ODP_QUEUE_INVALID) {
- printf("Schedule queue create failed.\n");
- return -1;
- }
-
- snprintf(name, sizeof(name), "plain_%d_%d_o", i, j);
- pq = odp_queue_create(name, NULL);
- if (pq == ODP_QUEUE_INVALID) {
- printf("Plain queue create failed.\n");
- return -1;
- }
-
- queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool);
-
- if (queue_ctx_buf == ODP_BUFFER_INVALID) {
- printf("Cannot allocate plain queue ctx buf\n");
- return -1;
- }
-
- pqctx = odp_buffer_addr(queue_ctx_buf);
- pqctx->ctx_handle = queue_ctx_buf;
- pqctx->sequence = 0;
-
- rc = odp_queue_context_set(pq, pqctx, 0);
-
- if (rc != 0) {
- printf("Cannot set plain queue context\n");
- return -1;
- }
-
- snprintf(name, sizeof(name), "sched_%d_%d_o", i, j);
- p.sched.sync = ODP_SCHED_SYNC_ORDERED;
- p.sched.lock_count = capa.max_ordered_locks;
- p.size = 0;
- q = odp_queue_create(name, &p);
-
- if (q == ODP_QUEUE_INVALID) {
- printf("Schedule queue create failed.\n");
- return -1;
- }
- if (odp_queue_lock_count(q) !=
- (int)capa.max_ordered_locks) {
- printf("Queue %" PRIu64 " created with "
- "%d locks instead of expected %d\n",
- odp_queue_to_u64(q),
- odp_queue_lock_count(q),
- capa.max_ordered_locks);
- return -1;
- }
-
- queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool);
-
- if (queue_ctx_buf == ODP_BUFFER_INVALID) {
- printf("Cannot allocate queue ctx buf\n");
- return -1;
- }
-
- qctx = odp_buffer_addr(queue_ctx_buf);
- qctx->ctx_handle = queue_ctx_buf;
- qctx->pq_handle = pq;
- qctx->sequence = 0;
-
- for (ndx = 0;
- ndx < capa.max_ordered_locks;
- ndx++) {
- qctx->lock_sequence[ndx] = 0;
- }
-
- rc = odp_queue_context_set(q, qctx, 0);
-
- if (rc != 0) {
- printf("Cannot set queue context\n");
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-int scheduler_suite_init(void)
-{
- odp_cpumask_t mask;
- odp_shm_t shm;
- odp_pool_t pool;
- test_globals_t *globals;
- thread_args_t *args;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.buf.size = BUF_SIZE;
- params.buf.align = 0;
- params.buf.num = MSG_POOL_SIZE;
- params.type = ODP_POOL_BUFFER;
-
- pool = odp_pool_create(MSG_POOL_NAME, &params);
-
- if (pool == ODP_POOL_INVALID) {
- printf("Pool creation failed (msg).\n");
- return -1;
- }
-
- shm = odp_shm_reserve(GLOBALS_SHM_NAME,
- sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
-
- globals = odp_shm_addr(shm);
-
- if (!globals) {
- printf("Shared memory reserve failed (globals).\n");
- return -1;
- }
-
- memset(globals, 0, sizeof(test_globals_t));
-
- globals->num_workers = odp_cpumask_default_worker(&mask, 0);
- if (globals->num_workers > MAX_WORKERS)
- globals->num_workers = MAX_WORKERS;
-
- shm = odp_shm_reserve(SHM_THR_ARGS_NAME, sizeof(thread_args_t),
- ODP_CACHE_LINE_SIZE, 0);
- args = odp_shm_addr(shm);
-
- if (!args) {
- printf("Shared memory reserve failed (args).\n");
- return -1;
- }
-
- memset(args, 0, sizeof(thread_args_t));
-
- /* Barrier to sync test case execution */
- odp_barrier_init(&globals->barrier, globals->num_workers);
- odp_ticketlock_init(&globals->lock);
- odp_spinlock_init(&globals->atomic_lock);
-
- if (create_queues() != 0)
- return -1;
-
- return 0;
-}
-
-static int destroy_queue(const char *name)
-{
- odp_queue_t q;
- queue_context *qctx;
-
- q = odp_queue_lookup(name);
-
- if (q == ODP_QUEUE_INVALID)
- return -1;
- qctx = odp_queue_context(q);
- if (qctx)
- odp_buffer_free(qctx->ctx_handle);
-
- return odp_queue_destroy(q);
-}
-
-static int destroy_queues(void)
-{
- int i, j, prios;
-
- prios = odp_schedule_num_prio();
-
- for (i = 0; i < prios; i++) {
- for (j = 0; j < QUEUES_PER_PRIO; j++) {
- char name[32];
-
- snprintf(name, sizeof(name), "sched_%d_%d_n", i, j);
- if (destroy_queue(name) != 0)
- return -1;
-
- snprintf(name, sizeof(name), "sched_%d_%d_a", i, j);
- if (destroy_queue(name) != 0)
- return -1;
-
- snprintf(name, sizeof(name), "sched_%d_%d_o", i, j);
- if (destroy_queue(name) != 0)
- return -1;
-
- snprintf(name, sizeof(name), "plain_%d_%d_o", i, j);
- if (destroy_queue(name) != 0)
- return -1;
- }
- }
-
- if (odp_pool_destroy(queue_ctx_pool) != 0) {
- fprintf(stderr, "error: failed to destroy queue ctx pool\n");
- return -1;
- }
-
- return 0;
-}
-
-int scheduler_suite_term(void)
-{
- odp_pool_t pool;
- odp_shm_t shm;
-
- if (destroy_queues() != 0) {
- fprintf(stderr, "error: failed to destroy queues\n");
- return -1;
- }
-
- pool = odp_pool_lookup(MSG_POOL_NAME);
- if (odp_pool_destroy(pool) != 0)
- fprintf(stderr, "error: failed to destroy pool\n");
-
- shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
- if (odp_shm_free(shm) != 0)
- fprintf(stderr, "error: failed to free shm\n");
-
- shm = odp_shm_lookup(GLOBALS_SHM_NAME);
- if (odp_shm_free(shm) != 0)
- fprintf(stderr, "error: failed to free shm\n");
-
- return 0;
-}
-
-odp_testinfo_t scheduler_suite[] = {
- ODP_TEST_INFO(scheduler_test_wait_time),
- ODP_TEST_INFO(scheduler_test_num_prio),
- ODP_TEST_INFO(scheduler_test_queue_destroy),
- ODP_TEST_INFO(scheduler_test_groups),
- ODP_TEST_INFO(scheduler_test_pause_resume),
- ODP_TEST_INFO(scheduler_test_parallel),
- ODP_TEST_INFO(scheduler_test_atomic),
- ODP_TEST_INFO(scheduler_test_ordered),
- ODP_TEST_INFO(scheduler_test_chaos),
- ODP_TEST_INFO(scheduler_test_1q_1t_n),
- ODP_TEST_INFO(scheduler_test_1q_1t_a),
- ODP_TEST_INFO(scheduler_test_1q_1t_o),
- ODP_TEST_INFO(scheduler_test_mq_1t_n),
- ODP_TEST_INFO(scheduler_test_mq_1t_a),
- ODP_TEST_INFO(scheduler_test_mq_1t_o),
- ODP_TEST_INFO(scheduler_test_mq_1t_prio_n),
- ODP_TEST_INFO(scheduler_test_mq_1t_prio_a),
- ODP_TEST_INFO(scheduler_test_mq_1t_prio_o),
- ODP_TEST_INFO(scheduler_test_mq_mt_prio_n),
- ODP_TEST_INFO(scheduler_test_mq_mt_prio_a),
- ODP_TEST_INFO(scheduler_test_mq_mt_prio_o),
- ODP_TEST_INFO(scheduler_test_1q_mt_a_excl),
- ODP_TEST_INFO(scheduler_test_multi_1q_1t_n),
- ODP_TEST_INFO(scheduler_test_multi_1q_1t_a),
- ODP_TEST_INFO(scheduler_test_multi_1q_1t_o),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_n),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_a),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_o),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_n),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_a),
- ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_o),
- ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_n),
- ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_a),
- ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_o),
- ODP_TEST_INFO(scheduler_test_multi_1q_mt_a_excl),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t scheduler_suites[] = {
- {"Scheduler",
- scheduler_suite_init, scheduler_suite_term, scheduler_suite
- },
- ODP_SUITE_INFO_NULL,
-};
-
-int scheduler_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(scheduler_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/scheduler/scheduler.h b/test/common_plat/validation/api/scheduler/scheduler.h
deleted file mode 100644
index a619d89b2..000000000
--- a/test/common_plat/validation/api/scheduler/scheduler.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_SCHEDULER_H_
-#define _ODP_TEST_SCHEDULER_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void scheduler_test_wait_time(void);
-void scheduler_test_num_prio(void);
-void scheduler_test_queue_destroy(void);
-void scheduler_test_groups(void);
-void scheduler_test_chaos(void);
-void scheduler_test_parallel(void);
-void scheduler_test_atomic(void);
-void scheduler_test_ordered(void);
-void scheduler_test_1q_1t_n(void);
-void scheduler_test_1q_1t_a(void);
-void scheduler_test_1q_1t_o(void);
-void scheduler_test_mq_1t_n(void);
-void scheduler_test_mq_1t_a(void);
-void scheduler_test_mq_1t_o(void);
-void scheduler_test_mq_1t_prio_n(void);
-void scheduler_test_mq_1t_prio_a(void);
-void scheduler_test_mq_1t_prio_o(void);
-void scheduler_test_mq_mt_prio_n(void);
-void scheduler_test_mq_mt_prio_a(void);
-void scheduler_test_mq_mt_prio_o(void);
-void scheduler_test_1q_mt_a_excl(void);
-void scheduler_test_multi_1q_1t_n(void);
-void scheduler_test_multi_1q_1t_a(void);
-void scheduler_test_multi_1q_1t_o(void);
-void scheduler_test_multi_mq_1t_n(void);
-void scheduler_test_multi_mq_1t_a(void);
-void scheduler_test_multi_mq_1t_o(void);
-void scheduler_test_multi_mq_1t_prio_n(void);
-void scheduler_test_multi_mq_1t_prio_a(void);
-void scheduler_test_multi_mq_1t_prio_o(void);
-void scheduler_test_multi_mq_mt_prio_n(void);
-void scheduler_test_multi_mq_mt_prio_a(void);
-void scheduler_test_multi_mq_mt_prio_o(void);
-void scheduler_test_multi_1q_mt_a_excl(void);
-void scheduler_test_pause_resume(void);
-
-/* test arrays: */
-extern odp_testinfo_t scheduler_suite[];
-
-/* test array init/term functions: */
-int scheduler_suite_init(void);
-int scheduler_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t scheduler_suites[];
-
-/* main test program: */
-int scheduler_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/scheduler/scheduler_main.c b/test/common_plat/validation/api/scheduler/scheduler_main.c
deleted file mode 100644
index 57cfa5fc5..000000000
--- a/test/common_plat/validation/api/scheduler/scheduler_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "scheduler.h"
-
-int main(int argc, char *argv[])
-{
- return scheduler_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/shmem/Makefile.am b/test/common_plat/validation/api/shmem/Makefile.am
deleted file mode 100644
index da88af662..000000000
--- a/test/common_plat/validation/api/shmem/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestshmem.la
-libtestshmem_la_SOURCES = shmem.c
-
-test_PROGRAMS = shmem_main$(EXEEXT)
-dist_shmem_main_SOURCES = shmem_main.c
-shmem_main_LDADD = libtestshmem.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = shmem.h
diff --git a/test/common_plat/validation/api/shmem/shmem.c b/test/common_plat/validation/api/shmem/shmem.c
deleted file mode 100644
index 0e757a708..000000000
--- a/test/common_plat/validation/api/shmem/shmem.c
+++ /dev/null
@@ -1,738 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "shmem.h"
-#include <stdlib.h>
-
-#define ALIGN_SIZE (128)
-#define MEM_NAME "test_shmem"
-#define NAME_LEN (sizeof(MEM_NAME) + 20)
-#define TEST_SHARE_FOO (0xf0f0f0f0)
-#define TEST_SHARE_BAR (0xf0f0f0f)
-#define SMALL_MEM 10
-#define MEDIUM_MEM 4096
-#define BIG_MEM 65536
-#define STRESS_SIZE 32 /* power of 2 and <=256 */
-#define STRESS_RANDOM_SZ 5
-#define STRESS_ITERATION 5000
-
-typedef enum {
- STRESS_FREE, /* entry is free and can be allocated */
- STRESS_BUSY, /* entry is being processed: don't touch */
- STRESS_ALLOC /* entry is allocated and can be freed */
-} stress_state_t;
-
-typedef struct {
- stress_state_t state;
- odp_shm_t shm;
- char name[NAME_LEN];
- void *address;
- uint32_t flags;
- uint32_t size;
- uint64_t align;
- uint8_t data_val;
-} stress_data_t;
-
-typedef struct {
- odp_barrier_t test_barrier1;
- odp_barrier_t test_barrier2;
- odp_barrier_t test_barrier3;
- odp_barrier_t test_barrier4;
- uint32_t foo;
- uint32_t bar;
- odp_atomic_u32_t index;
- uint32_t nb_threads;
- odp_shm_t shm[MAX_WORKERS];
- void *address[MAX_WORKERS];
- char name[MAX_WORKERS][NAME_LEN];
- odp_spinlock_t stress_lock;
- stress_data_t stress[STRESS_SIZE];
-} shared_test_data_t;
-
-/* memory stuff expected to fit in a single page */
-typedef struct {
- int data[SMALL_MEM];
-} shared_test_data_small_t;
-
-/* memory stuff expected to fit in a huge page */
-typedef struct {
- int data[MEDIUM_MEM];
-} shared_test_data_medium_t;
-
-/* memory stuff expected to fit in many huge pages */
-typedef struct {
- int data[BIG_MEM];
-} shared_test_data_big_t;
-
-/*
- * thread part for the shmem_test_basic test
- */
-static int run_test_basic_thread(void *arg ODP_UNUSED)
-{
- odp_shm_info_t info;
- odp_shm_t shm;
- shared_test_data_t *shared_test_data;
- int thr;
-
- thr = odp_thread_id();
- printf("Thread %i starts\n", thr);
-
- shm = odp_shm_lookup(MEM_NAME);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- shared_test_data = odp_shm_addr(shm);
- CU_ASSERT(NULL != shared_test_data);
-
- odp_barrier_wait(&shared_test_data->test_barrier1);
- odp_shm_print_all();
- CU_ASSERT(TEST_SHARE_FOO == shared_test_data->foo);
- CU_ASSERT(TEST_SHARE_BAR == shared_test_data->bar);
- CU_ASSERT(0 == odp_shm_info(shm, &info));
- CU_ASSERT(0 == strcmp(MEM_NAME, info.name));
- CU_ASSERT(0 == info.flags);
- CU_ASSERT(shared_test_data == info.addr);
- CU_ASSERT(sizeof(shared_test_data_t) <= info.size);
- CU_ASSERT((info.page_size == odp_sys_huge_page_size()) ||
- (info.page_size == odp_sys_page_size()))
- odp_shm_print_all();
-
- fflush(stdout);
- return CU_get_number_of_failures();
-}
-
-/*
- * test basic things: shmem creation, info, share, and free
- */
-void shmem_test_basic(void)
-{
- pthrd_arg thrdarg;
- odp_shm_t shm;
- odp_shm_t shm2;
- shared_test_data_t *shared_test_data;
- odp_cpumask_t unused;
-
- shm = odp_shm_reserve(MEM_NAME,
- sizeof(shared_test_data_t), ALIGN_SIZE, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- CU_ASSERT(odp_shm_to_u64(shm) !=
- odp_shm_to_u64(ODP_SHM_INVALID));
-
- /* also check that another reserve with same name is accepted: */
- shm2 = odp_shm_reserve(MEM_NAME,
- sizeof(shared_test_data_t), ALIGN_SIZE, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm2);
- CU_ASSERT(odp_shm_to_u64(shm2) !=
- odp_shm_to_u64(ODP_SHM_INVALID));
-
- CU_ASSERT(0 == odp_shm_free(shm));
- CU_ASSERT(0 == odp_shm_free(shm2));
- CU_ASSERT(ODP_SHM_INVALID == odp_shm_lookup(MEM_NAME));
-
- shm = odp_shm_reserve(MEM_NAME,
- sizeof(shared_test_data_t), ALIGN_SIZE, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
-
- shared_test_data = odp_shm_addr(shm);
- CU_ASSERT_FATAL(NULL != shared_test_data);
- shared_test_data->foo = TEST_SHARE_FOO;
- shared_test_data->bar = TEST_SHARE_BAR;
-
- thrdarg.numthrds = odp_cpumask_default_worker(&unused, 0);
-
- if (thrdarg.numthrds > MAX_WORKERS)
- thrdarg.numthrds = MAX_WORKERS;
-
- odp_barrier_init(&shared_test_data->test_barrier1, thrdarg.numthrds);
- odp_cunit_thread_create(run_test_basic_thread, &thrdarg);
- CU_ASSERT(odp_cunit_thread_exit(&thrdarg) >= 0);
-
- CU_ASSERT(0 == odp_shm_free(shm));
-}
-
-/*
- * thread part for the shmem_test_reserve_after_fork
- */
-static int run_test_reserve_after_fork(void *arg ODP_UNUSED)
-{
- odp_shm_t shm;
- shared_test_data_t *glob_data;
- int thr;
- int thr_index;
- int size;
- shared_test_data_small_t *pattern_small;
- shared_test_data_medium_t *pattern_medium;
- shared_test_data_big_t *pattern_big;
- int i;
-
- thr = odp_thread_id();
- printf("Thread %i starts\n", thr);
-
- shm = odp_shm_lookup(MEM_NAME);
- glob_data = odp_shm_addr(shm);
-
- /*
- * odp_thread_id are not guaranteed to be consecutive, so we create
- * a consecutive ID
- */
- thr_index = odp_atomic_fetch_inc_u32(&glob_data->index);
-
- /* allocate some memory (of different sizes) and fill with pattern */
- snprintf(glob_data->name[thr_index], NAME_LEN, "%s-%09d",
- MEM_NAME, thr_index);
- switch (thr_index % 3) {
- case 0:
- size = sizeof(shared_test_data_small_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_small = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_small);
- for (i = 0; i < SMALL_MEM; i++)
- pattern_small->data[i] = i;
- break;
- case 1:
- size = sizeof(shared_test_data_medium_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_medium = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_medium);
- for (i = 0; i < MEDIUM_MEM; i++)
- pattern_medium->data[i] = (i << 2);
- break;
- case 2:
- size = sizeof(shared_test_data_big_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_big = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_big);
- for (i = 0; i < BIG_MEM; i++)
- pattern_big->data[i] = (i >> 2);
- break;
- }
-
- /* print block address */
- printf("In thread: Block index: %d mapped at %lx\n",
- thr_index, (long int)odp_shm_addr(shm));
-
- odp_barrier_wait(&glob_data->test_barrier1);
- odp_barrier_wait(&glob_data->test_barrier2);
-
- fflush(stdout);
- return CU_get_number_of_failures();
-}
-
-/*
- * test sharing memory reserved after odp_thread creation (e.g. fork()):
- */
-void shmem_test_reserve_after_fork(void)
-{
- pthrd_arg thrdarg;
- odp_shm_t shm;
- odp_shm_t thr_shm;
- shared_test_data_t *glob_data;
- odp_cpumask_t unused;
- int thr_index;
- int i;
- void *address;
- shared_test_data_small_t *pattern_small;
- shared_test_data_medium_t *pattern_medium;
- shared_test_data_big_t *pattern_big;
-
- shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t), 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(glob_data);
-
- thrdarg.numthrds = odp_cpumask_default_worker(&unused, 0);
- if (thrdarg.numthrds > MAX_WORKERS)
- thrdarg.numthrds = MAX_WORKERS;
-
- odp_barrier_init(&glob_data->test_barrier1, thrdarg.numthrds + 1);
- odp_barrier_init(&glob_data->test_barrier2, thrdarg.numthrds + 1);
- odp_atomic_store_u32(&glob_data->index, 0);
-
- odp_cunit_thread_create(run_test_reserve_after_fork, &thrdarg);
-
- /* wait until all threads have made their shm_reserve: */
- odp_barrier_wait(&glob_data->test_barrier1);
-
- /* perform a lookup of all memories: */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- thr_shm = odp_shm_lookup(glob_data->name[thr_index]);
- CU_ASSERT(thr_shm == glob_data->shm[thr_index]);
- }
-
- /* check that the patterns are correct: */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- switch (thr_index % 3) {
- case 0:
- pattern_small =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_small);
- for (i = 0; i < SMALL_MEM; i++)
- CU_ASSERT(pattern_small->data[i] == i);
- break;
- case 1:
- pattern_medium =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_medium);
- for (i = 0; i < MEDIUM_MEM; i++)
- CU_ASSERT(pattern_medium->data[i] == (i << 2));
- break;
- case 2:
- pattern_big =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_big);
- for (i = 0; i < BIG_MEM; i++)
- CU_ASSERT(pattern_big->data[i] == (i >> 2));
- break;
- }
- }
-
- /*
- * print the mapping address of the blocks
- */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- address = odp_shm_addr(glob_data->shm[thr_index]);
- printf("In main Block index: %d mapped at %lx\n",
- thr_index, (long int)address);
- }
-
- /* unblock the threads and let them terminate (no free is done): */
- odp_barrier_wait(&glob_data->test_barrier2);
-
- /* at the same time, (race),free of all memories: */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- thr_shm = glob_data->shm[thr_index];
- CU_ASSERT(odp_shm_free(thr_shm) == 0);
- }
-
- /* wait for all thread endings: */
- CU_ASSERT(odp_cunit_thread_exit(&thrdarg) >= 0);
-
- /* just glob_data should remain: */
-
- CU_ASSERT(0 == odp_shm_free(shm));
-}
-
-/*
- * thread part for the shmem_test_singleva_after_fork
- */
-static int run_test_singleva_after_fork(void *arg ODP_UNUSED)
-{
- odp_shm_t shm;
- shared_test_data_t *glob_data;
- int thr;
- int thr_index;
- int size;
- shared_test_data_small_t *pattern_small;
- shared_test_data_medium_t *pattern_medium;
- shared_test_data_big_t *pattern_big;
- uint32_t i;
- int ret;
-
- thr = odp_thread_id();
- printf("Thread %i starts\n", thr);
-
- shm = odp_shm_lookup(MEM_NAME);
- glob_data = odp_shm_addr(shm);
-
- /*
- * odp_thread_id are not guaranteed to be consecutive, so we create
- * a consecutive ID
- */
- thr_index = odp_atomic_fetch_inc_u32(&glob_data->index);
-
- /* allocate some memory (of different sizes) and fill with pattern */
- snprintf(glob_data->name[thr_index], NAME_LEN, "%s-%09d",
- MEM_NAME, thr_index);
- switch (thr_index % 3) {
- case 0:
- size = sizeof(shared_test_data_small_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size,
- 0, ODP_SHM_SINGLE_VA);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_small = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_small);
- glob_data->address[thr_index] = (void *)pattern_small;
- for (i = 0; i < SMALL_MEM; i++)
- pattern_small->data[i] = i;
- break;
- case 1:
- size = sizeof(shared_test_data_medium_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size,
- 0, ODP_SHM_SINGLE_VA);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_medium = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_medium);
- glob_data->address[thr_index] = (void *)pattern_medium;
- for (i = 0; i < MEDIUM_MEM; i++)
- pattern_medium->data[i] = (i << 2);
- break;
- case 2:
- size = sizeof(shared_test_data_big_t);
- shm = odp_shm_reserve(glob_data->name[thr_index], size,
- 0, ODP_SHM_SINGLE_VA);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data->shm[thr_index] = shm;
- pattern_big = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(pattern_big);
- glob_data->address[thr_index] = (void *)pattern_big;
- for (i = 0; i < BIG_MEM; i++)
- pattern_big->data[i] = (i >> 2);
- break;
- }
-
- /* print block address */
- printf("In thread: Block index: %d mapped at %lx\n",
- thr_index, (long int)odp_shm_addr(shm));
-
- odp_barrier_wait(&glob_data->test_barrier1);
- odp_barrier_wait(&glob_data->test_barrier2);
-
- /* map each-other block, checking common address: */
- for (i = 0; i < glob_data->nb_threads; i++) {
- shm = odp_shm_lookup(glob_data->name[i]);
- CU_ASSERT(shm == glob_data->shm[i]);
- CU_ASSERT(odp_shm_addr(shm) == glob_data->address[i]);
- }
-
- /* wait for main control task and free the allocated block */
- odp_barrier_wait(&glob_data->test_barrier3);
- odp_barrier_wait(&glob_data->test_barrier4);
- ret = odp_shm_free(glob_data->shm[thr_index]);
- CU_ASSERT(ret == 0);
-
- fflush(stdout);
- return CU_get_number_of_failures();
-}
-
-/*
- * test sharing memory reserved after odp_thread creation (e.g. fork()):
- * with single VA flag.
- */
-void shmem_test_singleva_after_fork(void)
-{
- pthrd_arg thrdarg;
- odp_shm_t shm;
- odp_shm_t thr_shm;
- shared_test_data_t *glob_data;
- odp_cpumask_t unused;
- int thr_index;
- int i;
- void *address;
- shared_test_data_small_t *pattern_small;
- shared_test_data_medium_t *pattern_medium;
- shared_test_data_big_t *pattern_big;
-
- shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t),
- 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != shm);
- glob_data = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(glob_data);
-
- thrdarg.numthrds = odp_cpumask_default_worker(&unused, 0);
- if (thrdarg.numthrds > MAX_WORKERS)
- thrdarg.numthrds = MAX_WORKERS;
-
- glob_data->nb_threads = thrdarg.numthrds;
- odp_barrier_init(&glob_data->test_barrier1, thrdarg.numthrds + 1);
- odp_barrier_init(&glob_data->test_barrier2, thrdarg.numthrds + 1);
- odp_barrier_init(&glob_data->test_barrier3, thrdarg.numthrds + 1);
- odp_barrier_init(&glob_data->test_barrier4, thrdarg.numthrds + 1);
- odp_atomic_store_u32(&glob_data->index, 0);
-
- odp_cunit_thread_create(run_test_singleva_after_fork, &thrdarg);
-
- /* wait until all threads have made their shm_reserve: */
- odp_barrier_wait(&glob_data->test_barrier1);
-
- /* perform a lookup of all memories: */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- thr_shm = odp_shm_lookup(glob_data->name[thr_index]);
- CU_ASSERT(thr_shm == glob_data->shm[thr_index]);
- }
-
- /* check that the patterns are correct: */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- switch (thr_index % 3) {
- case 0:
- pattern_small =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_small);
- for (i = 0; i < SMALL_MEM; i++)
- CU_ASSERT(pattern_small->data[i] == i);
- break;
- case 1:
- pattern_medium =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_medium);
- for (i = 0; i < MEDIUM_MEM; i++)
- CU_ASSERT(pattern_medium->data[i] == (i << 2));
- break;
- case 2:
- pattern_big =
- odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT_PTR_NOT_NULL(pattern_big);
- for (i = 0; i < BIG_MEM; i++)
- CU_ASSERT(pattern_big->data[i] == (i >> 2));
- break;
- }
- }
-
- /*
- * check that the mapping address is common to all (SINGLE_VA):
- */
- for (thr_index = 0; thr_index < thrdarg.numthrds; thr_index++) {
- address = odp_shm_addr(glob_data->shm[thr_index]);
- CU_ASSERT(glob_data->address[thr_index] == address);
- }
-
- /* unblock the threads and let them map each-other blocks: */
- odp_barrier_wait(&glob_data->test_barrier2);
-
- /* then check mem status */
- odp_barrier_wait(&glob_data->test_barrier3);
-
- /* unblock the threads and let them free all thread blocks: */
- odp_barrier_wait(&glob_data->test_barrier4);
-
- /* wait for all thread endings: */
- CU_ASSERT(odp_cunit_thread_exit(&thrdarg) >= 0);
-
- /* just glob_data should remain: */
-
- CU_ASSERT(0 == odp_shm_free(shm));
-}
-
-/*
- * thread part for the shmem_test_stress
- */
-static int run_test_stress(void *arg ODP_UNUSED)
-{
- odp_shm_t shm;
- uint8_t *address;
- shared_test_data_t *glob_data;
- uint8_t random_bytes[STRESS_RANDOM_SZ];
- uint32_t index;
- uint32_t size;
- uint64_t align;
- uint32_t flags;
- uint8_t data;
- uint32_t iter;
- uint32_t i;
-
- shm = odp_shm_lookup(MEM_NAME);
- glob_data = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(glob_data);
-
- /* wait for general GO! */
- odp_barrier_wait(&glob_data->test_barrier1);
-
- /*
- * at each iteration: pick up a random index for
- * glob_data->stress[index]: If the entry is free, allocated mem
- * randomly. If it is already allocated, make checks and free it:
- * Note that different tread can allocate or free a given block
- */
- for (iter = 0; iter < STRESS_ITERATION; iter++) {
- /* get 4 random bytes from which index, size ,align, flags
- * and data will be derived:
- */
- odp_random_data(random_bytes, STRESS_RANDOM_SZ, 0);
- index = random_bytes[0] & (STRESS_SIZE - 1);
-
- odp_spinlock_lock(&glob_data->stress_lock);
-
- switch (glob_data->stress[index].state) {
- case STRESS_FREE:
- /* allocated a new block for this entry */
-
- glob_data->stress[index].state = STRESS_BUSY;
- odp_spinlock_unlock(&glob_data->stress_lock);
-
- size = (random_bytes[1] + 1) << 6; /* up to 16Kb */
- /* we just play with the VA flag. randomly setting
- * the mlock flag may exceed user ulimit -l
- */
- flags = random_bytes[2] & ODP_SHM_SINGLE_VA;
- align = (random_bytes[3] + 1) << 6;/* up to 16Kb */
- data = random_bytes[4];
-
- snprintf(glob_data->stress[index].name, NAME_LEN,
- "%s-%09d", MEM_NAME, index);
- shm = odp_shm_reserve(glob_data->stress[index].name,
- size, align, flags);
- glob_data->stress[index].shm = shm;
- if (shm == ODP_SHM_INVALID) { /* out of mem ? */
- odp_spinlock_lock(&glob_data->stress_lock);
- glob_data->stress[index].state = STRESS_ALLOC;
- odp_spinlock_unlock(&glob_data->stress_lock);
- continue;
- }
-
- address = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(address);
- glob_data->stress[index].address = address;
- glob_data->stress[index].flags = flags;
- glob_data->stress[index].size = size;
- glob_data->stress[index].align = align;
- glob_data->stress[index].data_val = data;
-
- /* write some data: writing each byte would be a
- * waste of time: just make sure each page is reached */
- for (i = 0; i < size; i += 256)
- address[i] = (data++) & 0xFF;
- odp_spinlock_lock(&glob_data->stress_lock);
- glob_data->stress[index].state = STRESS_ALLOC;
- odp_spinlock_unlock(&glob_data->stress_lock);
-
- break;
-
- case STRESS_ALLOC:
- /* free the block for this entry */
-
- glob_data->stress[index].state = STRESS_BUSY;
- odp_spinlock_unlock(&glob_data->stress_lock);
- shm = glob_data->stress[index].shm;
-
- if (shm == ODP_SHM_INVALID) { /* out of mem ? */
- odp_spinlock_lock(&glob_data->stress_lock);
- glob_data->stress[index].state = STRESS_FREE;
- odp_spinlock_unlock(&glob_data->stress_lock);
- continue;
- }
-
- CU_ASSERT(odp_shm_lookup(glob_data->stress[index].name)
- != 0);
-
- address = odp_shm_addr(shm);
- CU_ASSERT_PTR_NOT_NULL(address);
-
- align = glob_data->stress[index].align;
- if (align) {
- align = glob_data->stress[index].align;
- CU_ASSERT(((uintptr_t)address & (align - 1))
- == 0)
- }
-
- flags = glob_data->stress[index].flags;
- if (flags & ODP_SHM_SINGLE_VA)
- CU_ASSERT(glob_data->stress[index].address ==
- address)
-
- /* check that data is reachable and correct: */
- data = glob_data->stress[index].data_val;
- size = glob_data->stress[index].size;
- for (i = 0; i < size; i += 256) {
- CU_ASSERT(address[i] == (data & 0xFF));
- data++;
- }
-
- CU_ASSERT(!odp_shm_free(glob_data->stress[index].shm));
-
- odp_spinlock_lock(&glob_data->stress_lock);
- glob_data->stress[index].state = STRESS_FREE;
- odp_spinlock_unlock(&glob_data->stress_lock);
-
- break;
-
- case STRESS_BUSY:
- default:
- odp_spinlock_unlock(&glob_data->stress_lock);
- break;
- }
- }
-
- fflush(stdout);
- return CU_get_number_of_failures();
-}
-
-/*
- * stress tests
- */
-void shmem_test_stress(void)
-{
- pthrd_arg thrdarg;
- odp_shm_t shm;
- odp_shm_t globshm;
- shared_test_data_t *glob_data;
- odp_cpumask_t unused;
- uint32_t i;
-
- globshm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t),
- 0, 0);
- CU_ASSERT(ODP_SHM_INVALID != globshm);
- glob_data = odp_shm_addr(globshm);
- CU_ASSERT_PTR_NOT_NULL(glob_data);
-
- thrdarg.numthrds = odp_cpumask_default_worker(&unused, 0);
- if (thrdarg.numthrds > MAX_WORKERS)
- thrdarg.numthrds = MAX_WORKERS;
-
- glob_data->nb_threads = thrdarg.numthrds;
- odp_barrier_init(&glob_data->test_barrier1, thrdarg.numthrds);
- odp_spinlock_init(&glob_data->stress_lock);
-
- /* before starting the threads, mark all entries as free: */
- for (i = 0; i < STRESS_SIZE; i++)
- glob_data->stress[i].state = STRESS_FREE;
-
- /* create threads */
- odp_cunit_thread_create(run_test_stress, &thrdarg);
-
- /* wait for all thread endings: */
- CU_ASSERT(odp_cunit_thread_exit(&thrdarg) >= 0);
-
- /* release left overs: */
- for (i = 0; i < STRESS_SIZE; i++) {
- shm = glob_data->stress[i].shm;
- if ((glob_data->stress[i].state == STRESS_ALLOC) &&
- (glob_data->stress[i].shm != ODP_SHM_INVALID)) {
- CU_ASSERT(odp_shm_lookup(glob_data->stress[i].name) ==
- shm);
- CU_ASSERT(!odp_shm_free(shm));
- }
- }
-
- CU_ASSERT(0 == odp_shm_free(globshm));
-
- /* check that no memory is left over: */
-}
-
-odp_testinfo_t shmem_suite[] = {
- ODP_TEST_INFO(shmem_test_basic),
- ODP_TEST_INFO(shmem_test_reserve_after_fork),
- ODP_TEST_INFO(shmem_test_singleva_after_fork),
- ODP_TEST_INFO(shmem_test_stress),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t shmem_suites[] = {
- {"Shared Memory", NULL, NULL, shmem_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int shmem_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(shmem_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/shmem/shmem.h b/test/common_plat/validation/api/shmem/shmem.h
deleted file mode 100644
index 092aa8005..000000000
--- a/test/common_plat/validation/api/shmem/shmem.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_SHMEM_H_
-#define _ODP_TEST_SHMEM_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void shmem_test_basic(void);
-void shmem_test_reserve_after_fork(void);
-void shmem_test_singleva_after_fork(void);
-void shmem_test_stress(void);
-
-/* test arrays: */
-extern odp_testinfo_t shmem_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t shmem_suites[];
-
-/* main test program: */
-int shmem_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/shmem/shmem_main.c b/test/common_plat/validation/api/shmem/shmem_main.c
deleted file mode 100644
index 4c6913051..000000000
--- a/test/common_plat/validation/api/shmem/shmem_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "shmem.h"
-
-int main(int argc, char *argv[])
-{
- return shmem_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/std_clib/.gitignore b/test/common_plat/validation/api/std_clib/.gitignore
deleted file mode 100644
index 37828330a..000000000
--- a/test/common_plat/validation/api/std_clib/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-std_clib_main
diff --git a/test/common_plat/validation/api/std_clib/Makefile.am b/test/common_plat/validation/api/std_clib/Makefile.am
deleted file mode 100644
index e2fc0ccf3..000000000
--- a/test/common_plat/validation/api/std_clib/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libteststd_clib.la
-libteststd_clib_la_SOURCES = std_clib.c
-
-test_PROGRAMS = std_clib_main$(EXEEXT)
-dist_std_clib_main_SOURCES = std_clib_main.c
-std_clib_main_LDADD = libteststd_clib.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = std_clib.h
diff --git a/test/common_plat/validation/api/std_clib/std_clib.c b/test/common_plat/validation/api/std_clib/std_clib.c
deleted file mode 100644
index 7f089eabb..000000000
--- a/test/common_plat/validation/api/std_clib/std_clib.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include "std_clib.h"
-
-#include <string.h>
-
-#define PATTERN 0x5e
-
-static void std_clib_test_memcpy(void)
-{
- uint8_t src[] = {0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15};
- uint8_t dst[16];
- int ret;
-
- memset(dst, 0, sizeof(dst));
-
- odp_memcpy(dst, src, sizeof(dst));
-
- ret = memcmp(dst, src, sizeof(dst));
-
- CU_ASSERT(ret == 0);
-}
-
-static void std_clib_test_memset(void)
-{
- uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15};
- uint8_t ref[16];
- int ret;
-
- odp_memset(data, PATTERN, sizeof(data));
-
- memset(ref, PATTERN, sizeof(ref));
-
- ret = memcmp(data, ref, sizeof(data));
-
- CU_ASSERT(ret == 0);
-}
-
-static void std_clib_test_memcmp(void)
-{
- uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16};
- uint8_t equal[] = {1, 2, 3, 4, 5, 6, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16};
- uint8_t greater_11[] = {1, 2, 3, 4, 5, 6, 7, 8,
- 9, 10, 99, 12, 13, 14, 15, 16};
- uint8_t less_6[] = {1, 2, 3, 4, 5, 2, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16};
- size_t i;
-
- CU_ASSERT(odp_memcmp(data, equal, 0) == 0);
- CU_ASSERT(odp_memcmp(data, equal, sizeof(data)) == 0);
- CU_ASSERT(odp_memcmp(data, equal, sizeof(data) - 3) == 0);
-
- CU_ASSERT(odp_memcmp(greater_11, data, sizeof(data)) > 0);
- CU_ASSERT(odp_memcmp(greater_11, data, 11) > 0);
- CU_ASSERT(odp_memcmp(greater_11, data, 10) == 0);
-
- CU_ASSERT(odp_memcmp(less_6, data, sizeof(data)) < 0);
- CU_ASSERT(odp_memcmp(less_6, data, 6) < 0);
- CU_ASSERT(odp_memcmp(less_6, data, 5) == 0);
-
- for (i = 0; i < sizeof(data); i++) {
- uint8_t tmp;
-
- CU_ASSERT(odp_memcmp(data, equal, i + 1) == 0);
- tmp = equal[i];
- equal[i] = 88;
- CU_ASSERT(odp_memcmp(data, equal, i + 1) < 0);
- equal[i] = 0;
- CU_ASSERT(odp_memcmp(data, equal, i + 1) > 0);
- equal[i] = tmp;
- }
-}
-
-odp_testinfo_t std_clib_suite[] = {
- ODP_TEST_INFO(std_clib_test_memcpy),
- ODP_TEST_INFO(std_clib_test_memset),
- ODP_TEST_INFO(std_clib_test_memcmp),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t std_clib_suites[] = {
- {"Std C library", NULL, NULL, std_clib_suite},
- ODP_SUITE_INFO_NULL
-};
-
-int std_clib_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(std_clib_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/std_clib/std_clib.h b/test/common_plat/validation/api/std_clib/std_clib.h
deleted file mode 100644
index 2804f27e2..000000000
--- a/test/common_plat/validation/api/std_clib/std_clib.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_STD_CLIB_H_
-#define _ODP_TEST_STD_CLIB_H_
-
-#include <odp_cunit_common.h>
-
-/* test arrays: */
-extern odp_testinfo_t std_clib_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t std_clib_suites[];
-
-/* main test program: */
-int std_clib_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/std_clib/std_clib_main.c b/test/common_plat/validation/api/std_clib/std_clib_main.c
deleted file mode 100644
index ef6f2736f..000000000
--- a/test/common_plat/validation/api/std_clib/std_clib_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "std_clib.h"
-
-int main(int argc, char *argv[])
-{
- return std_clib_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/system/Makefile.am b/test/common_plat/validation/api/system/Makefile.am
deleted file mode 100644
index 3789c36c2..000000000
--- a/test/common_plat/validation/api/system/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestsystem.la
-libtestsystem_la_SOURCES = system.c
-
-test_PROGRAMS = system_main$(EXEEXT)
-dist_system_main_SOURCES = system_main.c
-system_main_LDADD = libtestsystem.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = system.h
diff --git a/test/common_plat/validation/api/system/system.c b/test/common_plat/validation/api/system/system.c
deleted file mode 100644
index 5b7ca01ae..000000000
--- a/test/common_plat/validation/api/system/system.c
+++ /dev/null
@@ -1,352 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <ctype.h>
-#include <odp_api.h>
-#include <odp/api/cpumask.h>
-#include "odp_cunit_common.h"
-#include "test_debug.h"
-#include "system.h"
-
-#define DIFF_TRY_NUM 160
-#define RES_TRY_NUM 10
-
-void system_test_odp_version_numbers(void)
-{
- int char_ok = 0;
- char version_string[128];
- char *s = version_string;
-
- strncpy(version_string, odp_version_api_str(),
- sizeof(version_string) - 1);
-
- while (*s) {
- if (isdigit((int)*s) || (strncmp(s, ".", 1) == 0)) {
- char_ok = 1;
- s++;
- } else {
- char_ok = 0;
- LOG_DBG("\nBAD VERSION=%s\n", version_string);
- break;
- }
- }
- CU_ASSERT(char_ok);
-}
-
-void system_test_odp_cpu_count(void)
-{
- int cpus;
-
- cpus = odp_cpu_count();
- CU_ASSERT(0 < cpus);
-}
-
-void system_test_odp_cpu_cycles(void)
-{
- uint64_t c2, c1;
-
- c1 = odp_cpu_cycles();
- odp_time_wait_ns(100);
- c2 = odp_cpu_cycles();
-
- CU_ASSERT(c2 != c1);
-}
-
-void system_test_odp_cpu_cycles_max(void)
-{
- uint64_t c2, c1;
- uint64_t max1, max2;
-
- max1 = odp_cpu_cycles_max();
- odp_time_wait_ns(100);
- max2 = odp_cpu_cycles_max();
-
- CU_ASSERT(max1 >= UINT32_MAX / 2);
- CU_ASSERT(max1 == max2);
-
- c1 = odp_cpu_cycles();
- odp_time_wait_ns(1000);
- c2 = odp_cpu_cycles();
-
- CU_ASSERT(c1 <= max1 && c2 <= max1);
-}
-
-void system_test_odp_cpu_cycles_resolution(void)
-{
- int i;
- uint64_t res;
- uint64_t c2, c1, max;
-
- max = odp_cpu_cycles_max();
-
- res = odp_cpu_cycles_resolution();
- CU_ASSERT(res != 0);
- CU_ASSERT(res < max / 1024);
-
- for (i = 0; i < RES_TRY_NUM; i++) {
- c1 = odp_cpu_cycles();
- odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS + i);
- c2 = odp_cpu_cycles();
-
- CU_ASSERT(c1 % res == 0);
- CU_ASSERT(c2 % res == 0);
- }
-}
-
-void system_test_odp_cpu_cycles_diff(void)
-{
- int i;
- uint64_t c2, c1, c3, max;
- uint64_t tmp, diff, res;
-
- res = odp_cpu_cycles_resolution();
- max = odp_cpu_cycles_max();
-
- /* check resolution for wrap */
- c1 = max - 2 * res;
- do
- c2 = odp_cpu_cycles();
- while (c1 < c2);
-
- diff = odp_cpu_cycles_diff(c1, c1);
- CU_ASSERT(diff == 0);
-
- /* wrap */
- tmp = c2 + (max - c1) + res;
- diff = odp_cpu_cycles_diff(c2, c1);
- CU_ASSERT(diff == tmp);
- CU_ASSERT(diff % res == 0);
-
- /* no wrap, revert args */
- tmp = c1 - c2;
- diff = odp_cpu_cycles_diff(c1, c2);
- CU_ASSERT(diff == tmp);
- CU_ASSERT(diff % res == 0);
-
- c3 = odp_cpu_cycles();
- for (i = 0; i < DIFF_TRY_NUM; i++) {
- c1 = odp_cpu_cycles();
- odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS + i);
- c2 = odp_cpu_cycles();
-
- CU_ASSERT(c2 != c1);
- CU_ASSERT(c1 % res == 0);
- CU_ASSERT(c2 % res == 0);
- CU_ASSERT(c1 <= max && c2 <= max);
-
- if (c2 > c1)
- tmp = c2 - c1;
- else
- tmp = c2 + (max - c1) + res;
-
- diff = odp_cpu_cycles_diff(c2, c1);
- CU_ASSERT(diff == tmp);
- CU_ASSERT(diff % res == 0);
-
- /* wrap is detected and verified */
- if (c2 < c1)
- break;
- }
-
- /* wrap was detected, no need to continue */
- if (i < DIFF_TRY_NUM)
- return;
-
- /* wrap has to be detected if possible */
- CU_ASSERT(max > UINT32_MAX);
- CU_ASSERT((max - c3) > UINT32_MAX);
-
- printf("wrap was not detected...");
-}
-
-void system_test_odp_sys_cache_line_size(void)
-{
- uint64_t cache_size;
-
- cache_size = odp_sys_cache_line_size();
- CU_ASSERT(0 < cache_size);
- CU_ASSERT(ODP_CACHE_LINE_SIZE == cache_size);
-}
-
-void system_test_odp_cpu_model_str(void)
-{
- char model[128];
-
- snprintf(model, 128, "%s", odp_cpu_model_str());
- CU_ASSERT(strlen(model) > 0);
- CU_ASSERT(strlen(model) < 127);
-}
-
-void system_test_odp_cpu_model_str_id(void)
-{
- char model[128];
- odp_cpumask_t mask;
- int i, num, cpu;
-
- num = odp_cpumask_all_available(&mask);
- cpu = odp_cpumask_first(&mask);
-
- for (i = 0; i < num; i++) {
- snprintf(model, 128, "%s", odp_cpu_model_str_id(cpu));
- CU_ASSERT(strlen(model) > 0);
- CU_ASSERT(strlen(model) < 127);
- cpu = odp_cpumask_next(&mask, cpu);
- }
-}
-
-void system_test_odp_sys_page_size(void)
-{
- uint64_t page;
-
- page = odp_sys_page_size();
- CU_ASSERT(0 < page);
- CU_ASSERT(ODP_PAGE_SIZE == page);
-}
-
-void system_test_odp_sys_huge_page_size(void)
-{
- uint64_t page;
-
- page = odp_sys_huge_page_size();
- CU_ASSERT(0 < page);
-}
-
-int system_check_odp_cpu_hz(void)
-{
- if (odp_cpu_hz() == 0) {
- fprintf(stderr, "odp_cpu_hz is not supported, skipping\n");
- return ODP_TEST_INACTIVE;
- }
-
- return ODP_TEST_ACTIVE;
-}
-
-void system_test_odp_cpu_hz(void)
-{
- uint64_t hz = odp_cpu_hz();
-
- /* Test value sanity: less than 10GHz */
- CU_ASSERT(hz < 10 * GIGA_HZ);
-
- /* larger than 1kHz */
- CU_ASSERT(hz > 1 * KILO_HZ);
-}
-
-int system_check_odp_cpu_hz_id(void)
-{
- uint64_t hz;
- odp_cpumask_t mask;
- int i, num, cpu;
-
- num = odp_cpumask_all_available(&mask);
- cpu = odp_cpumask_first(&mask);
-
- for (i = 0; i < num; i++) {
- hz = odp_cpu_hz_id(cpu);
- if (hz == 0) {
- fprintf(stderr, "cpu %d does not support"
- " odp_cpu_hz_id(),"
- "skip that test\n", cpu);
- return ODP_TEST_INACTIVE;
- }
- cpu = odp_cpumask_next(&mask, cpu);
- }
-
- return ODP_TEST_ACTIVE;
-}
-
-void system_test_odp_cpu_hz_id(void)
-{
- uint64_t hz;
- odp_cpumask_t mask;
- int i, num, cpu;
-
- num = odp_cpumask_all_available(&mask);
- cpu = odp_cpumask_first(&mask);
-
- for (i = 0; i < num; i++) {
- hz = odp_cpu_hz_id(cpu);
- /* Test value sanity: less than 10GHz */
- CU_ASSERT(hz < 10 * GIGA_HZ);
- /* larger than 1kHz */
- CU_ASSERT(hz > 1 * KILO_HZ);
- cpu = odp_cpumask_next(&mask, cpu);
- }
-}
-
-void system_test_odp_cpu_hz_max(void)
-{
- uint64_t hz;
-
- hz = odp_cpu_hz_max();
- CU_ASSERT(0 < hz);
-}
-
-void system_test_odp_cpu_hz_max_id(void)
-{
- uint64_t hz;
- odp_cpumask_t mask;
- int i, num, cpu;
-
- num = odp_cpumask_all_available(&mask);
- cpu = odp_cpumask_first(&mask);
-
- for (i = 0; i < num; i++) {
- hz = odp_cpu_hz_max_id(cpu);
- CU_ASSERT(0 < hz);
- cpu = odp_cpumask_next(&mask, cpu);
- }
-}
-
-void system_test_info_print(void)
-{
- printf("\n\nCalling system info print...\n");
- odp_sys_info_print();
- printf("...done. ");
-}
-
-odp_testinfo_t system_suite[] = {
- ODP_TEST_INFO(system_test_odp_version_numbers),
- ODP_TEST_INFO(system_test_odp_cpu_count),
- ODP_TEST_INFO(system_test_odp_sys_cache_line_size),
- ODP_TEST_INFO(system_test_odp_cpu_model_str),
- ODP_TEST_INFO(system_test_odp_cpu_model_str_id),
- ODP_TEST_INFO(system_test_odp_sys_page_size),
- ODP_TEST_INFO(system_test_odp_sys_huge_page_size),
- ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz,
- system_check_odp_cpu_hz),
- ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz_id,
- system_check_odp_cpu_hz_id),
- ODP_TEST_INFO(system_test_odp_cpu_hz_max),
- ODP_TEST_INFO(system_test_odp_cpu_hz_max_id),
- ODP_TEST_INFO(system_test_odp_cpu_cycles),
- ODP_TEST_INFO(system_test_odp_cpu_cycles_max),
- ODP_TEST_INFO(system_test_odp_cpu_cycles_resolution),
- ODP_TEST_INFO(system_test_odp_cpu_cycles_diff),
- ODP_TEST_INFO(system_test_info_print),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t system_suites[] = {
- {"System Info", NULL, NULL, system_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int system_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(system_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/system/system.h b/test/common_plat/validation/api/system/system.h
deleted file mode 100644
index c33729b94..000000000
--- a/test/common_plat/validation/api/system/system.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_SYSTEM_H_
-#define _ODP_TEST_SYSTEM_H_
-
-#include <odp_cunit_common.h>
-
-#define GIGA_HZ 1000000000ULL
-#define KILO_HZ 1000ULL
-
-/* test functions: */
-void system_test_odp_version_numbers(void);
-void system_test_odp_cpu_count(void);
-void system_test_odp_sys_cache_line_size(void);
-void system_test_odp_cpu_model_str(void);
-void system_test_odp_cpu_model_str_id(void);
-void system_test_odp_sys_page_size(void);
-void system_test_odp_sys_huge_page_size(void);
-int system_check_odp_cpu_hz(void);
-void system_test_odp_cpu_hz(void);
-int system_check_odp_cpu_hz_id(void);
-void system_test_odp_cpu_hz_id(void);
-void system_test_odp_cpu_hz_max(void);
-void system_test_odp_cpu_hz_max_id(void);
-void system_test_odp_cpu_cycles_max(void);
-void system_test_odp_cpu_cycles(void);
-void system_test_odp_cpu_cycles_diff(void);
-void system_test_odp_cpu_cycles_resolution(void);
-void system_test_info_print(void);
-
-/* test arrays: */
-extern odp_testinfo_t system_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t system_suites[];
-
-/* main test program: */
-int system_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/system/system_main.c b/test/common_plat/validation/api/system/system_main.c
deleted file mode 100644
index 50d202a84..000000000
--- a/test/common_plat/validation/api/system/system_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "system.h"
-
-int main(int argc, char *argv[])
-{
- return system_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/thread/Makefile.am b/test/common_plat/validation/api/thread/Makefile.am
deleted file mode 100644
index eaf680cf5..000000000
--- a/test/common_plat/validation/api/thread/Makefile.am
+++ /dev/null
@@ -1,12 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestthread.la
-libtestthread_la_SOURCES = thread.c
-libtestthread_la_CFLAGS = $(AM_CFLAGS) -DTEST_THRMASK
-libtestthread_la_LIBADD = $(LIBTHRMASK_COMMON)
-
-test_PROGRAMS = thread_main$(EXEEXT)
-dist_thread_main_SOURCES = thread_main.c
-thread_main_LDADD = libtestthread.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = thread.h
diff --git a/test/common_plat/validation/api/thread/thread.c b/test/common_plat/validation/api/thread/thread.c
deleted file mode 100644
index 24f1c4580..000000000
--- a/test/common_plat/validation/api/thread/thread.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-#include <mask_common.h>
-#include <test_debug.h>
-#include "thread.h"
-
-/* Test thread entry and exit synchronization barriers */
-odp_barrier_t bar_entry;
-odp_barrier_t bar_exit;
-
-void thread_test_odp_cpu_id(void)
-{
- (void)odp_cpu_id();
- CU_PASS();
-}
-
-void thread_test_odp_thread_id(void)
-{
- (void)odp_thread_id();
- CU_PASS();
-}
-
-void thread_test_odp_thread_count(void)
-{
- (void)odp_thread_count();
- CU_PASS();
-}
-
-static int thread_func(void *arg TEST_UNUSED)
-{
- /* indicate that thread has started */
- odp_barrier_wait(&bar_entry);
-
- CU_ASSERT(odp_thread_type() == ODP_THREAD_WORKER);
-
- /* wait for indication that we can exit */
- odp_barrier_wait(&bar_exit);
-
- return CU_get_number_of_failures();
-}
-
-void thread_test_odp_thrmask_worker(void)
-{
- odp_thrmask_t mask;
- int ret;
- pthrd_arg args = { .testcase = 0, .numthrds = 1 };
-
- CU_ASSERT_FATAL(odp_thread_type() == ODP_THREAD_CONTROL);
-
- odp_barrier_init(&bar_entry, args.numthrds + 1);
- odp_barrier_init(&bar_exit, args.numthrds + 1);
-
- /* should start out with 0 worker threads */
- ret = odp_thrmask_worker(&mask);
- CU_ASSERT(ret == odp_thrmask_count(&mask));
- CU_ASSERT(ret == 0);
-
- /* start the test thread(s) */
- ret = odp_cunit_thread_create(thread_func, &args);
- CU_ASSERT(ret == args.numthrds);
-
- if (ret != args.numthrds)
- return;
-
- /* wait for thread(s) to start */
- odp_barrier_wait(&bar_entry);
-
- ret = odp_thrmask_worker(&mask);
- CU_ASSERT(ret == odp_thrmask_count(&mask));
- CU_ASSERT(ret == args.numthrds);
- CU_ASSERT(ret <= odp_thread_count_max());
-
- /* allow thread(s) to exit */
- odp_barrier_wait(&bar_exit);
-
- odp_cunit_thread_exit(&args);
-}
-
-void thread_test_odp_thrmask_control(void)
-{
- odp_thrmask_t mask;
- int ret;
-
- CU_ASSERT(odp_thread_type() == ODP_THREAD_CONTROL);
-
- /* should start out with 1 worker thread */
- ret = odp_thrmask_control(&mask);
- CU_ASSERT(ret == odp_thrmask_count(&mask));
- CU_ASSERT(ret == 1);
-}
-
-odp_testinfo_t thread_suite[] = {
- ODP_TEST_INFO(thread_test_odp_cpu_id),
- ODP_TEST_INFO(thread_test_odp_thread_id),
- ODP_TEST_INFO(thread_test_odp_thread_count),
- ODP_TEST_INFO(thread_test_odp_thrmask_to_from_str),
- ODP_TEST_INFO(thread_test_odp_thrmask_equal),
- ODP_TEST_INFO(thread_test_odp_thrmask_zero),
- ODP_TEST_INFO(thread_test_odp_thrmask_set),
- ODP_TEST_INFO(thread_test_odp_thrmask_clr),
- ODP_TEST_INFO(thread_test_odp_thrmask_isset),
- ODP_TEST_INFO(thread_test_odp_thrmask_count),
- ODP_TEST_INFO(thread_test_odp_thrmask_and),
- ODP_TEST_INFO(thread_test_odp_thrmask_or),
- ODP_TEST_INFO(thread_test_odp_thrmask_xor),
- ODP_TEST_INFO(thread_test_odp_thrmask_copy),
- ODP_TEST_INFO(thread_test_odp_thrmask_first),
- ODP_TEST_INFO(thread_test_odp_thrmask_last),
- ODP_TEST_INFO(thread_test_odp_thrmask_next),
- ODP_TEST_INFO(thread_test_odp_thrmask_worker),
- ODP_TEST_INFO(thread_test_odp_thrmask_control),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t thread_suites[] = {
- {"thread", NULL, NULL, thread_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int thread_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(thread_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/thread/thread.h b/test/common_plat/validation/api/thread/thread.h
deleted file mode 100644
index d511c9259..000000000
--- a/test/common_plat/validation/api/thread/thread.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_THREAD_H_
-#define _ODP_TEST_THREAD_H_
-
-#include <odp_api.h>
-#include <odp_cunit_common.h>
-
-/* test functions: */
-#ifndef TEST_THRMASK
-#define TEST_THRMASK
-#endif
-#include "mask_common.h"
-void thread_test_odp_cpu_id(void);
-void thread_test_odp_thread_id(void);
-void thread_test_odp_thread_count(void);
-void thread_test_odp_thrmask_control(void);
-void thread_test_odp_thrmask_worker(void);
-
-/* test arrays: */
-extern odp_testinfo_t thread_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t thread_suites[];
-
-/* main test program: */
-int thread_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/thread/thread_main.c b/test/common_plat/validation/api/thread/thread_main.c
deleted file mode 100644
index 53c756551..000000000
--- a/test/common_plat/validation/api/thread/thread_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "thread.h"
-
-int main(int argc, char *argv[])
-{
- return thread_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/time/Makefile.am b/test/common_plat/validation/api/time/Makefile.am
deleted file mode 100644
index ca9c05627..000000000
--- a/test/common_plat/validation/api/time/Makefile.am
+++ /dev/null
@@ -1,19 +0,0 @@
-include ../Makefile.inc
-
-TESTS_ENVIRONMENT += TEST_DIR=${builddir}
-
-TESTSCRIPTS = time.sh
-TEST_EXTENSIONS = .sh
-
-TESTS = $(TESTSCRIPTS)
-
-noinst_LTLIBRARIES = libtesttime.la
-libtesttime_la_SOURCES = time.c
-
-test_PROGRAMS = time_main$(EXEEXT)
-dist_time_main_SOURCES = time_main.c
-time_main_LDADD = libtesttime.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = time_test.h $(TESTSCRIPTS)
-dist_check_SCRIPTS = $(TESTSCRIPTS)
-test_SCRIPTS = $(dist_check_SCRIPTS)
diff --git a/test/common_plat/validation/api/time/time.c b/test/common_plat/validation/api/time/time.c
deleted file mode 100644
index e2ca2e17c..000000000
--- a/test/common_plat/validation/api/time/time.c
+++ /dev/null
@@ -1,481 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp_api.h>
-#include "odp_cunit_common.h"
-#include "time_test.h"
-#include <time.h>
-
-#define BUSY_LOOP_CNT 30000000 /* used for t > min resolution */
-#define BUSY_LOOP_CNT_LONG 6000000000 /* used for t > 4 sec */
-#define MIN_TIME_RATE 32000
-#define MAX_TIME_RATE 15000000000
-#define DELAY_TOLERANCE 20000000 /* deviation for delay */
-#define WAIT_SECONDS 3
-
-static uint64_t local_res;
-static uint64_t global_res;
-
-typedef odp_time_t time_cb(void);
-typedef uint64_t time_res_cb(void);
-typedef odp_time_t time_from_ns_cb(uint64_t ns);
-
-void time_test_constants(void)
-{
- uint64_t ns;
-
- ns = ODP_TIME_SEC_IN_NS / 1000;
- CU_ASSERT(ns == ODP_TIME_MSEC_IN_NS);
- ns /= 1000;
- CU_ASSERT(ns == ODP_TIME_USEC_IN_NS);
-}
-
-static void time_test_res(time_res_cb time_res, uint64_t *res)
-{
- uint64_t rate;
-
- rate = time_res();
- CU_ASSERT(rate > MIN_TIME_RATE);
- CU_ASSERT(rate < MAX_TIME_RATE);
-
- *res = ODP_TIME_SEC_IN_NS / rate;
- if (ODP_TIME_SEC_IN_NS % rate)
- (*res)++;
-}
-
-void time_test_local_res(void)
-{
- time_test_res(odp_time_local_res, &local_res);
-}
-
-void time_test_global_res(void)
-{
- time_test_res(odp_time_global_res, &global_res);
-}
-
-/* check that related conversions come back to the same value */
-static void time_test_conversion(time_from_ns_cb time_from_ns, uint64_t res)
-{
- uint64_t ns1, ns2;
- odp_time_t time;
- uint64_t upper_limit, lower_limit;
-
- ns1 = 100;
- time = time_from_ns(ns1);
-
- ns2 = odp_time_to_ns(time);
-
- /* need to check within arithmetic tolerance that the same
- * value in ns is returned after conversions */
- upper_limit = ns1 + res;
- lower_limit = ns1 - res;
- CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
-
- ns1 = 60 * 11 * ODP_TIME_SEC_IN_NS;
- time = time_from_ns(ns1);
-
- ns2 = odp_time_to_ns(time);
-
- /* need to check within arithmetic tolerance that the same
- * value in ns is returned after conversions */
- upper_limit = ns1 + res;
- lower_limit = ns1 - res;
- CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
-
- /* test on 0 */
- ns1 = odp_time_to_ns(ODP_TIME_NULL);
- CU_ASSERT(ns1 == 0);
-}
-
-void time_test_local_conversion(void)
-{
- time_test_conversion(odp_time_local_from_ns, local_res);
-}
-
-void time_test_global_conversion(void)
-{
- time_test_conversion(odp_time_global_from_ns, global_res);
-}
-
-void time_test_monotony(void)
-{
- volatile uint64_t count = 0;
- odp_time_t l_t1, l_t2, l_t3;
- odp_time_t g_t1, g_t2, g_t3;
- uint64_t ns1, ns2, ns3;
-
- l_t1 = odp_time_local();
- g_t1 = odp_time_global();
-
- while (count < BUSY_LOOP_CNT) {
- count++;
- };
-
- l_t2 = odp_time_local();
- g_t2 = odp_time_global();
-
- while (count < BUSY_LOOP_CNT_LONG) {
- count++;
- };
-
- l_t3 = odp_time_local();
- g_t3 = odp_time_global();
-
- ns1 = odp_time_to_ns(l_t1);
- ns2 = odp_time_to_ns(l_t2);
- ns3 = odp_time_to_ns(l_t3);
-
- /* Local time assertions */
- CU_ASSERT(ns2 > ns1);
- CU_ASSERT(ns3 > ns2);
-
- ns1 = odp_time_to_ns(g_t1);
- ns2 = odp_time_to_ns(g_t2);
- ns3 = odp_time_to_ns(g_t3);
-
- /* Global time assertions */
- CU_ASSERT(ns2 > ns1);
- CU_ASSERT(ns3 > ns2);
-}
-
-static void time_test_cmp(time_cb time_cur, time_from_ns_cb time_from_ns)
-{
- /* volatile to stop optimization of busy loop */
- volatile int count = 0;
- odp_time_t t1, t2, t3;
-
- t1 = time_cur();
-
- while (count < BUSY_LOOP_CNT) {
- count++;
- };
-
- t2 = time_cur();
-
- while (count < BUSY_LOOP_CNT * 2) {
- count++;
- };
-
- t3 = time_cur();
-
- CU_ASSERT(odp_time_cmp(t2, t1) > 0);
- CU_ASSERT(odp_time_cmp(t3, t2) > 0);
- CU_ASSERT(odp_time_cmp(t3, t1) > 0);
- CU_ASSERT(odp_time_cmp(t1, t2) < 0);
- CU_ASSERT(odp_time_cmp(t2, t3) < 0);
- CU_ASSERT(odp_time_cmp(t1, t3) < 0);
- CU_ASSERT(odp_time_cmp(t1, t1) == 0);
- CU_ASSERT(odp_time_cmp(t2, t2) == 0);
- CU_ASSERT(odp_time_cmp(t3, t3) == 0);
-
- t2 = time_from_ns(60 * 10 * ODP_TIME_SEC_IN_NS);
- t1 = time_from_ns(3);
-
- CU_ASSERT(odp_time_cmp(t2, t1) > 0);
- CU_ASSERT(odp_time_cmp(t1, t2) < 0);
-
- t1 = time_from_ns(0);
- CU_ASSERT(odp_time_cmp(t1, ODP_TIME_NULL) == 0);
-}
-
-void time_test_local_cmp(void)
-{
- time_test_cmp(odp_time_local, odp_time_local_from_ns);
-}
-
-void time_test_global_cmp(void)
-{
- time_test_cmp(odp_time_global, odp_time_global_from_ns);
-}
-
-/* check that a time difference gives a reasonable result */
-static void time_test_diff(time_cb time_cur,
- time_from_ns_cb time_from_ns,
- uint64_t res)
-{
- /* volatile to stop optimization of busy loop */
- volatile int count = 0;
- odp_time_t diff, t1, t2;
- uint64_t nsdiff, ns1, ns2, ns;
- uint64_t upper_limit, lower_limit;
-
- /* test timestamp diff */
- t1 = time_cur();
-
- while (count < BUSY_LOOP_CNT) {
- count++;
- };
-
- t2 = time_cur();
- CU_ASSERT(odp_time_cmp(t2, t1) > 0);
-
- diff = odp_time_diff(t2, t1);
- CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
-
- ns1 = odp_time_to_ns(t1);
- ns2 = odp_time_to_ns(t2);
- ns = ns2 - ns1;
- nsdiff = odp_time_to_ns(diff);
-
- upper_limit = ns + 2 * res;
- lower_limit = ns - 2 * res;
- CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
-
- /* test timestamp and interval diff */
- ns1 = 54;
- t1 = time_from_ns(ns1);
- ns = ns2 - ns1;
-
- diff = odp_time_diff(t2, t1);
- CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
- nsdiff = odp_time_to_ns(diff);
-
- upper_limit = ns + 2 * res;
- lower_limit = ns - 2 * res;
- CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
-
- /* test interval diff */
- ns2 = 60 * 10 * ODP_TIME_SEC_IN_NS;
- ns = ns2 - ns1;
-
- t2 = time_from_ns(ns2);
- diff = odp_time_diff(t2, t1);
- CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
- nsdiff = odp_time_to_ns(diff);
-
- upper_limit = ns + 2 * res;
- lower_limit = ns - 2 * res;
- CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
-
- /* same time has to diff to 0 */
- diff = odp_time_diff(t2, t2);
- CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) == 0);
-
- diff = odp_time_diff(t2, ODP_TIME_NULL);
- CU_ASSERT(odp_time_cmp(t2, diff) == 0);
-}
-
-void time_test_local_diff(void)
-{
- time_test_diff(odp_time_local, odp_time_local_from_ns, local_res);
-}
-
-void time_test_global_diff(void)
-{
- time_test_diff(odp_time_global, odp_time_global_from_ns, global_res);
-}
-
-/* check that a time sum gives a reasonable result */
-static void time_test_sum(time_cb time_cur,
- time_from_ns_cb time_from_ns,
- uint64_t res)
-{
- odp_time_t sum, t1, t2;
- uint64_t nssum, ns1, ns2, ns;
- uint64_t upper_limit, lower_limit;
-
- /* sum timestamp and interval */
- t1 = time_cur();
- ns2 = 103;
- t2 = time_from_ns(ns2);
- ns1 = odp_time_to_ns(t1);
- ns = ns1 + ns2;
-
- sum = odp_time_sum(t2, t1);
- CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
- nssum = odp_time_to_ns(sum);
-
- upper_limit = ns + 2 * res;
- lower_limit = ns - 2 * res;
- CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit));
-
- /* sum intervals */
- ns1 = 60 * 13 * ODP_TIME_SEC_IN_NS;
- t1 = time_from_ns(ns1);
- ns = ns1 + ns2;
-
- sum = odp_time_sum(t2, t1);
- CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
- nssum = odp_time_to_ns(sum);
-
- upper_limit = ns + 2 * res;
- lower_limit = ns - 2 * res;
- CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit));
-
- /* test on 0 */
- sum = odp_time_sum(t2, ODP_TIME_NULL);
- CU_ASSERT(odp_time_cmp(t2, sum) == 0);
-}
-
-void time_test_local_sum(void)
-{
- time_test_sum(odp_time_local, odp_time_local_from_ns, local_res);
-}
-
-void time_test_global_sum(void)
-{
- time_test_sum(odp_time_global, odp_time_global_from_ns, global_res);
-}
-
-static void time_test_wait_until(time_cb time_cur, time_from_ns_cb time_from_ns)
-{
- int i;
- odp_time_t lower_limit, upper_limit;
- odp_time_t start_time, end_time, wait;
- odp_time_t second = time_from_ns(ODP_TIME_SEC_IN_NS);
-
- start_time = time_cur();
- wait = start_time;
- for (i = 0; i < WAIT_SECONDS; i++) {
- wait = odp_time_sum(wait, second);
- odp_time_wait_until(wait);
- }
- end_time = time_cur();
-
- wait = odp_time_diff(end_time, start_time);
- lower_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS -
- DELAY_TOLERANCE);
- upper_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS +
- DELAY_TOLERANCE);
-
- if (odp_time_cmp(wait, lower_limit) < 0) {
- fprintf(stderr, "Exceed lower limit: "
- "wait is %" PRIu64 ", lower_limit %" PRIu64 "\n",
- odp_time_to_ns(wait), odp_time_to_ns(lower_limit));
- CU_FAIL("Exceed lower limit\n");
- }
-
- if (odp_time_cmp(wait, upper_limit) > 0) {
- fprintf(stderr, "Exceed upper limit: "
- "wait is %" PRIu64 ", upper_limit %" PRIu64 "\n",
- odp_time_to_ns(wait), odp_time_to_ns(lower_limit));
- CU_FAIL("Exceed upper limit\n");
- }
-}
-
-void time_test_local_wait_until(void)
-{
- time_test_wait_until(odp_time_local, odp_time_local_from_ns);
-}
-
-void time_test_global_wait_until(void)
-{
- time_test_wait_until(odp_time_global, odp_time_global_from_ns);
-}
-
-void time_test_wait_ns(void)
-{
- int i;
- odp_time_t lower_limit, upper_limit;
- odp_time_t start_time, end_time, diff;
-
- start_time = odp_time_local();
- for (i = 0; i < WAIT_SECONDS; i++)
- odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
- end_time = odp_time_local();
-
- diff = odp_time_diff(end_time, start_time);
-
- lower_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS -
- DELAY_TOLERANCE);
- upper_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS +
- DELAY_TOLERANCE);
-
- if (odp_time_cmp(diff, lower_limit) < 0) {
- fprintf(stderr, "Exceed lower limit: "
- "diff is %" PRIu64 ", lower_limit %" PRIu64 "\n",
- odp_time_to_ns(diff), odp_time_to_ns(lower_limit));
- CU_FAIL("Exceed lower limit\n");
- }
-
- if (odp_time_cmp(diff, upper_limit) > 0) {
- fprintf(stderr, "Exceed upper limit: "
- "diff is %" PRIu64 ", upper_limit %" PRIu64 "\n",
- odp_time_to_ns(diff), odp_time_to_ns(lower_limit));
- CU_FAIL("Exceed upper limit\n");
- }
-}
-
-static void time_test_accuracy(time_cb time_cur, time_from_ns_cb time_from_ns)
-{
- int i;
- odp_time_t t1, t2, wait, diff;
- clock_t c1, c2;
- double sec_t, sec_c;
- odp_time_t sec = time_from_ns(ODP_TIME_SEC_IN_NS);
-
- c1 = clock();
- t1 = time_cur();
-
- wait = odp_time_sum(t1, sec);
- for (i = 0; i < 5; i++) {
- odp_time_wait_until(wait);
- wait = odp_time_sum(wait, sec);
- }
-
- t2 = time_cur();
- c2 = clock();
-
- diff = odp_time_diff(t2, t1);
- sec_t = ((double)odp_time_to_ns(diff)) / ODP_TIME_SEC_IN_NS;
- sec_c = ((double)(c2 - c1)) / CLOCKS_PER_SEC;
-
- /* Check that ODP time is within +-5% of system time */
- CU_ASSERT(sec_t < sec_c * 1.05);
- CU_ASSERT(sec_t > sec_c * 0.95);
-}
-
-static void time_test_local_accuracy(void)
-{
- time_test_accuracy(odp_time_local, odp_time_local_from_ns);
-}
-
-static void time_test_global_accuracy(void)
-{
- time_test_accuracy(odp_time_global, odp_time_global_from_ns);
-}
-
-odp_testinfo_t time_suite_time[] = {
- ODP_TEST_INFO(time_test_constants),
- ODP_TEST_INFO(time_test_local_res),
- ODP_TEST_INFO(time_test_local_conversion),
- ODP_TEST_INFO(time_test_monotony),
- ODP_TEST_INFO(time_test_local_cmp),
- ODP_TEST_INFO(time_test_local_diff),
- ODP_TEST_INFO(time_test_local_sum),
- ODP_TEST_INFO(time_test_local_wait_until),
- ODP_TEST_INFO(time_test_wait_ns),
- ODP_TEST_INFO(time_test_local_accuracy),
- ODP_TEST_INFO(time_test_global_res),
- ODP_TEST_INFO(time_test_global_conversion),
- ODP_TEST_INFO(time_test_global_cmp),
- ODP_TEST_INFO(time_test_global_diff),
- ODP_TEST_INFO(time_test_global_sum),
- ODP_TEST_INFO(time_test_global_wait_until),
- ODP_TEST_INFO(time_test_global_accuracy),
- ODP_TEST_INFO_NULL
-};
-
-odp_suiteinfo_t time_suites[] = {
- {"Time", NULL, NULL, time_suite_time},
- ODP_SUITE_INFO_NULL
-};
-
-int time_main(int argc, char *argv[])
-{
- int ret;
-
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- ret = odp_cunit_register(time_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/time/time.sh b/test/common_plat/validation/api/time/time.sh
deleted file mode 100755
index 02bf75a76..000000000
--- a/test/common_plat/validation/api/time/time.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2017, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# directories where time_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./time) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/time:$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/time:$PATH
-PATH=$(dirname $0):$PATH
-PATH=`pwd`:$PATH
-
-time_main_path=$(which time_main${EXEEXT})
-if [ -x "$time_main_path" ] ; then
- echo "running with time_main: $time_run_path"
-else
- echo "cannot find time_main: please set you PATH for it."
- exit 1
-fi
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-time_main${EXEEXT}
-ret=$?
-
-SIGSEGV=139
-
-if [ "${TRAVIS}" = "true" ] && [ $ret -ne 0 ] &&
- [ ${TEST} = "coverage" ] && [ $ret -ne ${SIGSEGV} ]; then
- echo "SKIP: skip due significant slowdown under code coverage"
- exit ${TEST_SKIPPED}
-fi
-
-exit $ret
diff --git a/test/common_plat/validation/api/time/time_main.c b/test/common_plat/validation/api/time/time_main.c
deleted file mode 100644
index bf1cfe7bd..000000000
--- a/test/common_plat/validation/api/time/time_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "time_test.h"
-
-int main(int argc, char *argv[])
-{
- return time_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/time/time_test.h b/test/common_plat/validation/api/time/time_test.h
deleted file mode 100644
index 109562944..000000000
--- a/test/common_plat/validation/api/time/time_test.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_TIME_H_
-#define _ODP_TEST_TIME_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void time_test_constants(void);
-void time_test_local_res(void);
-void time_test_global_res(void);
-void time_test_local_conversion(void);
-void time_test_global_conversion(void);
-void time_test_local_cmp(void);
-void time_test_global_cmp(void);
-void time_test_local_diff(void);
-void time_test_global_diff(void);
-void time_test_local_sum(void);
-void time_test_global_sum(void);
-void time_test_local_wait_until(void);
-void time_test_global_wait_until(void);
-void time_test_wait_ns(void);
-void time_test_monotony(void);
-
-/* test arrays: */
-extern odp_testinfo_t time_suite_time[];
-
-/* test registry: */
-extern odp_suiteinfo_t time_suites[];
-
-/* main test program: */
-int time_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/timer/Makefile.am b/test/common_plat/validation/api/timer/Makefile.am
deleted file mode 100644
index fe6872f41..000000000
--- a/test/common_plat/validation/api/timer/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtesttimer.la
-libtesttimer_la_SOURCES = timer.c
-
-test_PROGRAMS = timer_main$(EXEEXT)
-dist_timer_main_SOURCES = timer_main.c
-timer_main_LDADD = libtesttimer.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = timer.h
diff --git a/test/common_plat/validation/api/timer/timer.c b/test/common_plat/validation/api/timer/timer.c
deleted file mode 100644
index b7d84c649..000000000
--- a/test/common_plat/validation/api/timer/timer.c
+++ /dev/null
@@ -1,605 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- */
-
-/* For rand_r and nanosleep */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <time.h>
-#include <odp.h>
-#include <odp/helper/odph_api.h>
-#include "odp_cunit_common.h"
-#include "test_debug.h"
-#include "timer.h"
-
-/** @private Timeout range in milliseconds (ms) */
-#define RANGE_MS 2000
-
-/** @private Number of timers per thread */
-#define NTIMERS 2000
-
-/** @private Barrier for thread synchronisation */
-static odp_barrier_t test_barrier;
-
-/** @private Timeout pool handle used by all threads */
-static odp_pool_t tbp;
-
-/** @private Timer pool handle used by all threads */
-static odp_timer_pool_t tp;
-
-/** @private Count of timeouts delivered too late */
-static odp_atomic_u32_t ndelivtoolate;
-
-/** @private Sum of all allocated timers from all threads. Thread-local
- * caches may make this number lower than the capacity of the pool */
-static odp_atomic_u32_t timers_allocated;
-
-/* @private Timer helper structure */
-struct test_timer {
- odp_timer_t tim; /* Timer handle */
- odp_event_t ev; /* Timeout event */
- odp_event_t ev2; /* Copy of event handle */
- uint64_t tick; /* Expiration tick or TICK_INVALID */
-};
-
-#define TICK_INVALID (~(uint64_t)0)
-
-void timer_test_timeout_pool_alloc(void)
-{
- odp_pool_t pool;
- const int num = 3;
- odp_timeout_t tmo[num];
- odp_event_t ev;
- int index;
- char wrong_type = 0;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_TIMEOUT;
- params.tmo.num = num;
-
- pool = odp_pool_create("timeout_pool_alloc", &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
-
- odp_pool_print(pool);
-
- /* Try to allocate num items from the pool */
- for (index = 0; index < num; index++) {
- tmo[index] = odp_timeout_alloc(pool);
-
- if (tmo[index] == ODP_TIMEOUT_INVALID)
- break;
-
- ev = odp_timeout_to_event(tmo[index]);
- if (odp_event_type(ev) != ODP_EVENT_TIMEOUT)
- wrong_type = 1;
- }
-
- /* Check that the pool had at least num items */
- CU_ASSERT(index == num);
- /* index points out of buffer[] or it point to an invalid buffer */
- index--;
-
- /* Check that the pool had correct buffers */
- CU_ASSERT(wrong_type == 0);
-
- for (; index >= 0; index--)
- odp_timeout_free(tmo[index]);
-
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void timer_test_timeout_pool_free(void)
-{
- odp_pool_t pool;
- odp_timeout_t tmo;
- odp_pool_param_t params;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_TIMEOUT;
- params.tmo.num = 1;
-
- pool = odp_pool_create("timeout_pool_free", &params);
- CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
- odp_pool_print(pool);
-
- /* Allocate the only timeout from the pool */
- tmo = odp_timeout_alloc(pool);
- CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
-
- /* Pool should have only one timeout */
- CU_ASSERT_FATAL(odp_timeout_alloc(pool) == ODP_TIMEOUT_INVALID)
-
- odp_timeout_free(tmo);
-
- /* Check that the timeout was returned back to the pool */
- tmo = odp_timeout_alloc(pool);
- CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
-
- odp_timeout_free(tmo);
- CU_ASSERT(odp_pool_destroy(pool) == 0);
-}
-
-void timer_test_odp_timer_cancel(void)
-{
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_timer_pool_param_t tparam;
- odp_timer_pool_t tp;
- odp_queue_t queue;
- odp_timer_t tim;
- odp_event_t ev;
- odp_timeout_t tmo;
- odp_timer_set_t rc;
- uint64_t tick;
-
- odp_pool_param_init(&params);
- params.type = ODP_POOL_TIMEOUT;
- params.tmo.num = 1;
-
- pool = odp_pool_create("tmo_pool_for_cancel", &params);
-
- if (pool == ODP_POOL_INVALID)
- CU_FAIL_FATAL("Timeout pool create failed");
-
- tparam.res_ns = 100 * ODP_TIME_MSEC_IN_NS;
- tparam.min_tmo = 1 * ODP_TIME_SEC_IN_NS;
- tparam.max_tmo = 10 * ODP_TIME_SEC_IN_NS;
- tparam.num_timers = 1;
- tparam.priv = 0;
- tparam.clk_src = ODP_CLOCK_CPU;
- tp = odp_timer_pool_create(NULL, &tparam);
- if (tp == ODP_TIMER_POOL_INVALID)
- CU_FAIL_FATAL("Timer pool create failed");
-
- /* Start all created timer pools */
- odp_timer_pool_start();
-
- queue = odp_queue_create("timer_queue", NULL);
- if (queue == ODP_QUEUE_INVALID)
- CU_FAIL_FATAL("Queue create failed");
-
- #define USER_PTR ((void *)0xdead)
- tim = odp_timer_alloc(tp, queue, USER_PTR);
- if (tim == ODP_TIMER_INVALID)
- CU_FAIL_FATAL("Failed to allocate timer");
- LOG_DBG("Timer handle: %" PRIu64 "\n", odp_timer_to_u64(tim));
-
- ev = odp_timeout_to_event(odp_timeout_alloc(pool));
- if (ev == ODP_EVENT_INVALID)
- CU_FAIL_FATAL("Failed to allocate timeout");
-
- tick = odp_timer_ns_to_tick(tp, 2 * ODP_TIME_SEC_IN_NS);
-
- rc = odp_timer_set_rel(tim, tick, &ev);
- if (rc != ODP_TIMER_SUCCESS)
- CU_FAIL_FATAL("Failed to set timer (relative time)");
-
- ev = ODP_EVENT_INVALID;
- if (odp_timer_cancel(tim, &ev) != 0)
- CU_FAIL_FATAL("Failed to cancel timer (relative time)");
-
- if (ev == ODP_EVENT_INVALID)
- CU_FAIL_FATAL("Cancel did not return event");
-
- tmo = odp_timeout_from_event(ev);
- if (tmo == ODP_TIMEOUT_INVALID)
- CU_FAIL_FATAL("Cancel did not return timeout");
- LOG_DBG("Timeout handle: %" PRIu64 "\n", odp_timeout_to_u64(tmo));
-
- if (odp_timeout_timer(tmo) != tim)
- CU_FAIL("Cancel invalid tmo.timer");
-
- if (odp_timeout_user_ptr(tmo) != USER_PTR)
- CU_FAIL("Cancel invalid tmo.user_ptr");
-
- odp_timeout_free(tmo);
-
- ev = odp_timer_free(tim);
- if (ev != ODP_EVENT_INVALID)
- CU_FAIL_FATAL("Free returned event");
-
- odp_timer_pool_destroy(tp);
-
- if (odp_queue_destroy(queue) != 0)
- CU_FAIL_FATAL("Failed to destroy queue");
-
- if (odp_pool_destroy(pool) != 0)
- CU_FAIL_FATAL("Failed to destroy pool");
-}
-
-/* @private Handle a received (timeout) event */
-static void handle_tmo(odp_event_t ev, bool stale, uint64_t prev_tick)
-{
- CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); /* Internal error */
- if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) {
- /* Not a timeout event */
- CU_FAIL("Unexpected event type received");
- return;
- }
- /* Read the metadata from the timeout */
- odp_timeout_t tmo = odp_timeout_from_event(ev);
- odp_timer_t tim = odp_timeout_timer(tmo);
- uint64_t tick = odp_timeout_tick(tmo);
- struct test_timer *ttp = odp_timeout_user_ptr(tmo);
-
- if (tim == ODP_TIMER_INVALID)
- CU_FAIL("odp_timeout_timer() invalid timer");
- if (!ttp)
- CU_FAIL("odp_timeout_user_ptr() null user ptr");
-
- if (ttp && ttp->ev2 != ev)
- CU_FAIL("odp_timeout_user_ptr() wrong user ptr");
- if (ttp && ttp->tim != tim)
- CU_FAIL("odp_timeout_timer() wrong timer");
- if (stale) {
- if (odp_timeout_fresh(tmo))
- CU_FAIL("Wrong status (fresh) for stale timeout");
- /* Stale timeout => local timer must have invalid tick */
- if (ttp && ttp->tick != TICK_INVALID)
- CU_FAIL("Stale timeout for active timer");
- } else {
- if (!odp_timeout_fresh(tmo))
- CU_FAIL("Wrong status (stale) for fresh timeout");
- /* Fresh timeout => local timer must have matching tick */
- if (ttp && ttp->tick != tick) {
- LOG_DBG("Wrong tick: expected %" PRIu64
- " actual %" PRIu64 "\n",
- ttp->tick, tick);
- CU_FAIL("odp_timeout_tick() wrong tick");
- }
- /* Check that timeout was delivered 'timely' */
- if (tick > odp_timer_current_tick(tp))
- CU_FAIL("Timeout delivered early");
- if (tick < prev_tick) {
- LOG_DBG("Too late tick: %" PRIu64
- " prev_tick %" PRIu64"\n",
- tick, prev_tick);
- /* We don't report late timeouts using CU_FAIL */
- odp_atomic_inc_u32(&ndelivtoolate);
- }
- }
-
- if (ttp) {
- /* Internal error */
- CU_ASSERT_FATAL(ttp->ev == ODP_EVENT_INVALID);
- ttp->ev = ev;
- }
-}
-
-/* @private Worker thread entrypoint which performs timer alloc/set/cancel/free
- * tests */
-static int worker_entrypoint(void *arg TEST_UNUSED)
-{
- int thr = odp_thread_id();
- uint32_t i, allocated;
- unsigned seed = thr;
- int rc;
- odp_queue_t queue;
- struct test_timer *tt;
- uint32_t nset;
- uint64_t tck;
- uint32_t nrcv;
- uint32_t nreset;
- uint32_t ncancel;
- uint32_t ntoolate;
- uint32_t ms;
- uint64_t prev_tick;
- odp_event_t ev;
- struct timespec ts;
- uint32_t nstale;
- odp_timer_set_t timer_rc;
-
- queue = odp_queue_create("timer_queue", NULL);
- if (queue == ODP_QUEUE_INVALID)
- CU_FAIL_FATAL("Queue create failed");
-
- tt = malloc(sizeof(struct test_timer) * NTIMERS);
- if (!tt)
- CU_FAIL_FATAL("malloc failed");
-
- /* Prepare all timers */
- for (i = 0; i < NTIMERS; i++) {
- tt[i].ev = odp_timeout_to_event(odp_timeout_alloc(tbp));
- if (tt[i].ev == ODP_EVENT_INVALID) {
- LOG_DBG("Failed to allocate timeout (%" PRIu32 "/%d)\n",
- i, NTIMERS);
- break;
- }
- tt[i].tim = odp_timer_alloc(tp, queue, &tt[i]);
- if (tt[i].tim == ODP_TIMER_INVALID) {
- LOG_DBG("Failed to allocate timer (%" PRIu32 "/%d)\n",
- i, NTIMERS);
- odp_event_free(tt[i].ev);
- break;
- }
- tt[i].ev2 = tt[i].ev;
- tt[i].tick = TICK_INVALID;
- }
- allocated = i;
- if (allocated == 0)
- CU_FAIL_FATAL("unable to alloc a timer");
- odp_atomic_fetch_add_u32(&timers_allocated, allocated);
-
- odp_barrier_wait(&test_barrier);
-
- /* Initial set all timers with a random expiration time */
- nset = 0;
- for (i = 0; i < allocated; i++) {
- tck = odp_timer_current_tick(tp) + 1 +
- odp_timer_ns_to_tick(tp, (rand_r(&seed) % RANGE_MS)
- * 1000000ULL);
- timer_rc = odp_timer_set_abs(tt[i].tim, tck, &tt[i].ev);
- if (timer_rc != ODP_TIMER_SUCCESS) {
- CU_FAIL("Failed to set timer");
- } else {
- tt[i].tick = tck;
- nset++;
- }
- }
-
- /* Step through wall time, 1ms at a time and check for expired timers */
- nrcv = 0;
- nreset = 0;
- ncancel = 0;
- ntoolate = 0;
- prev_tick = odp_timer_current_tick(tp);
-
- for (ms = 0; ms < 7 * RANGE_MS / 10 && allocated > 0; ms++) {
- while ((ev = odp_queue_deq(queue)) != ODP_EVENT_INVALID) {
- /* Subtract one from prev_tick to allow for timeouts
- * to be delivered a tick late */
- handle_tmo(ev, false, prev_tick - 1);
- nrcv++;
- }
- prev_tick = odp_timer_current_tick(tp);
- i = rand_r(&seed) % allocated;
- if (tt[i].ev == ODP_EVENT_INVALID &&
- (rand_r(&seed) % 2 == 0)) {
- /* Timer active, cancel it */
- rc = odp_timer_cancel(tt[i].tim, &tt[i].ev);
- if (rc != 0)
- /* Cancel failed, timer already expired */
- ntoolate++;
- tt[i].tick = TICK_INVALID;
- ncancel++;
- } else {
- if (tt[i].ev != ODP_EVENT_INVALID)
- /* Timer inactive => set */
- nset++;
- else
- /* Timer active => reset */
- nreset++;
- uint64_t tck = 1 + odp_timer_ns_to_tick(tp,
- (rand_r(&seed) % RANGE_MS) * 1000000ULL);
- odp_timer_set_t rc;
- uint64_t cur_tick;
- /* Loop until we manage to read cur_tick and set a
- * relative timer in the same tick */
- do {
- cur_tick = odp_timer_current_tick(tp);
- rc = odp_timer_set_rel(tt[i].tim,
- tck, &tt[i].ev);
- } while (cur_tick != odp_timer_current_tick(tp));
- if (rc == ODP_TIMER_TOOEARLY ||
- rc == ODP_TIMER_TOOLATE) {
- CU_FAIL("Failed to set timer (tooearly/toolate)");
- } else if (rc != ODP_TIMER_SUCCESS) {
- /* Set/reset failed, timer already expired */
- ntoolate++;
- } else if (rc == ODP_TIMER_SUCCESS) {
- /* Save expected expiration tick on success */
- tt[i].tick = cur_tick + tck;
- }
- }
- ts.tv_sec = 0;
- ts.tv_nsec = 1000000; /* 1ms */
- if (nanosleep(&ts, NULL) < 0)
- CU_FAIL_FATAL("nanosleep failed");
- }
-
- /* Cancel and free all timers */
- nstale = 0;
- for (i = 0; i < allocated; i++) {
- (void)odp_timer_cancel(tt[i].tim, &tt[i].ev);
- tt[i].tick = TICK_INVALID;
- if (tt[i].ev == ODP_EVENT_INVALID)
- /* Cancel too late, timer already expired and
- * timeout enqueued */
- nstale++;
- }
-
- LOG_DBG("Thread %u: %" PRIu32 " timers set\n", thr, nset);
- LOG_DBG("Thread %u: %" PRIu32 " timers reset\n", thr, nreset);
- LOG_DBG("Thread %u: %" PRIu32 " timers cancelled\n", thr, ncancel);
- LOG_DBG("Thread %u: %" PRIu32 " timers reset/cancelled too late\n",
- thr, ntoolate);
- LOG_DBG("Thread %u: %" PRIu32 " timeouts received\n", thr, nrcv);
- LOG_DBG("Thread %u: %" PRIu32
- " stale timeout(s) after odp_timer_free()\n",
- thr, nstale);
-
- /* Delay some more to ensure timeouts for expired timers can be
- * received. Can not use busy loop here to make background timer
- * thread finish their work. */
- ts.tv_sec = 0;
- ts.tv_nsec = (3 * RANGE_MS / 10 + 50) * ODP_TIME_MSEC_IN_NS;
- if (nanosleep(&ts, NULL) < 0)
- CU_FAIL_FATAL("nanosleep failed");
-
- while (nstale != 0) {
- ev = odp_queue_deq(queue);
- if (ev != ODP_EVENT_INVALID) {
- handle_tmo(ev, true, 0/*Don't care for stale tmo's*/);
- nstale--;
- } else {
- CU_FAIL("Failed to receive stale timeout");
- break;
- }
- }
-
- for (i = 0; i < allocated; i++) {
- if (odp_timer_free(tt[i].tim) != ODP_EVENT_INVALID)
- CU_FAIL("odp_timer_free");
- }
-
- /* Check if there any more (unexpected) events */
- ev = odp_queue_deq(queue);
- if (ev != ODP_EVENT_INVALID)
- CU_FAIL("Unexpected event received");
-
- rc = odp_queue_destroy(queue);
- CU_ASSERT(rc == 0);
- for (i = 0; i < allocated; i++) {
- if (tt[i].ev != ODP_EVENT_INVALID)
- odp_event_free(tt[i].ev);
- }
-
- free(tt);
- LOG_DBG("Thread %u: exiting\n", thr);
- return CU_get_number_of_failures();
-}
-
-/* @private Timer test case entrypoint */
-void timer_test_odp_timer_all(void)
-{
- int rc;
- odp_pool_param_t params;
- odp_timer_pool_param_t tparam;
- odp_cpumask_t unused;
- odp_timer_pool_info_t tpinfo;
- uint64_t tick;
- uint64_t ns;
- uint64_t t2;
- pthrd_arg thrdarg;
-
- /* Reserve at least one core for running other processes so the timer
- * test hopefully can run undisturbed and thus get better timing
- * results. */
- int num_workers = odp_cpumask_default_worker(&unused, 0);
-
- /* force to max CPU count */
- if (num_workers > MAX_WORKERS)
- num_workers = MAX_WORKERS;
-
- /* On a single-CPU machine run at least one thread */
- if (num_workers < 1)
- num_workers = 1;
-
- /* Create timeout pools */
- odp_pool_param_init(&params);
- params.type = ODP_POOL_TIMEOUT;
- params.tmo.num = (NTIMERS + 1) * num_workers;
-
- tbp = odp_pool_create("tmo_pool", &params);
- if (tbp == ODP_POOL_INVALID)
- CU_FAIL_FATAL("Timeout pool create failed");
-
-#define NAME "timer_pool"
-#define RES (10 * ODP_TIME_MSEC_IN_NS / 3)
-#define MIN (10 * ODP_TIME_MSEC_IN_NS / 3)
-#define MAX (1000000 * ODP_TIME_MSEC_IN_NS)
- /* Create a timer pool */
- tparam.res_ns = RES;
- tparam.min_tmo = MIN;
- tparam.max_tmo = MAX;
- tparam.num_timers = num_workers * NTIMERS;
- tparam.priv = 0;
- tparam.clk_src = ODP_CLOCK_CPU;
- tp = odp_timer_pool_create(NAME, &tparam);
- if (tp == ODP_TIMER_POOL_INVALID)
- CU_FAIL_FATAL("Timer pool create failed");
-
- /* Start all created timer pools */
- odp_timer_pool_start();
-
- if (odp_timer_pool_info(tp, &tpinfo) != 0)
- CU_FAIL("odp_timer_pool_info");
- CU_ASSERT(strcmp(tpinfo.name, NAME) == 0);
- CU_ASSERT(tpinfo.param.res_ns == RES);
- CU_ASSERT(tpinfo.param.min_tmo == MIN);
- CU_ASSERT(tpinfo.param.max_tmo == MAX);
- CU_ASSERT(strcmp(tpinfo.name, NAME) == 0);
-
- LOG_DBG("Timer pool handle: %" PRIu64 "\n", odp_timer_pool_to_u64(tp));
- LOG_DBG("#timers..: %u\n", NTIMERS);
- LOG_DBG("Tmo range: %u ms (%" PRIu64 " ticks)\n", RANGE_MS,
- odp_timer_ns_to_tick(tp, 1000000ULL * RANGE_MS));
-
- for (tick = 0; tick < 1000000000000ULL; tick += 1000000ULL) {
- ns = odp_timer_tick_to_ns(tp, tick);
- t2 = odp_timer_ns_to_tick(tp, ns);
- if (tick != t2)
- CU_FAIL("Invalid conversion tick->ns->tick");
- }
-
- /* Initialize barrier used by worker threads for synchronization */
- odp_barrier_init(&test_barrier, num_workers);
-
- /* Initialize the shared timeout counter */
- odp_atomic_init_u32(&ndelivtoolate, 0);
-
- /* Initialize the number of finally allocated elements */
- odp_atomic_init_u32(&timers_allocated, 0);
-
- /* Create and start worker threads */
- thrdarg.testcase = 0;
- thrdarg.numthrds = num_workers;
- odp_cunit_thread_create(worker_entrypoint, &thrdarg);
-
- /* Wait for worker threads to exit */
- odp_cunit_thread_exit(&thrdarg);
- LOG_DBG("Number of timeouts delivered/received too late: %" PRIu32 "\n",
- odp_atomic_load_u32(&ndelivtoolate));
-
- /* Check some statistics after the test */
- if (odp_timer_pool_info(tp, &tpinfo) != 0)
- CU_FAIL("odp_timer_pool_info");
- CU_ASSERT(tpinfo.param.num_timers == (unsigned)num_workers * NTIMERS);
- CU_ASSERT(tpinfo.cur_timers == 0);
- CU_ASSERT(tpinfo.hwm_timers == odp_atomic_load_u32(&timers_allocated));
-
- /* Destroy timer pool, all timers must have been freed */
- odp_timer_pool_destroy(tp);
-
- /* Destroy timeout pool, all timeouts must have been freed */
- rc = odp_pool_destroy(tbp);
- CU_ASSERT(rc == 0);
-
- CU_PASS("ODP timer test");
-}
-
-odp_testinfo_t timer_suite[] = {
- ODP_TEST_INFO(timer_test_timeout_pool_alloc),
- ODP_TEST_INFO(timer_test_timeout_pool_free),
- ODP_TEST_INFO(timer_test_odp_timer_cancel),
- ODP_TEST_INFO(timer_test_odp_timer_all),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t timer_suites[] = {
- {"Timer", NULL, NULL, timer_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int timer_main(int argc, char *argv[])
-{
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- int ret = odp_cunit_register(timer_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/timer/timer.h b/test/common_plat/validation/api/timer/timer.h
deleted file mode 100644
index bd304fffd..000000000
--- a/test/common_plat/validation/api/timer/timer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_TIMER_H_
-#define _ODP_TEST_TIMER_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void timer_test_timeout_pool_alloc(void);
-void timer_test_timeout_pool_free(void);
-void timer_test_odp_timer_cancel(void);
-void timer_test_odp_timer_all(void);
-
-/* test arrays: */
-extern odp_testinfo_t timer_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t timer_suites[];
-
-/* main test program: */
-int timer_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/timer/timer_main.c b/test/common_plat/validation/api/timer/timer_main.c
deleted file mode 100644
index c318763fa..000000000
--- a/test/common_plat/validation/api/timer/timer_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "timer.h"
-
-int main(int argc, char *argv[])
-{
- return timer_main(argc, argv);
-}
diff --git a/test/common_plat/validation/api/traffic_mngr/Makefile.am b/test/common_plat/validation/api/traffic_mngr/Makefile.am
deleted file mode 100644
index 44d50a914..000000000
--- a/test/common_plat/validation/api/traffic_mngr/Makefile.am
+++ /dev/null
@@ -1,19 +0,0 @@
-include ../Makefile.inc
-
-TESTS_ENVIRONMENT += TEST_DIR=${builddir}
-
-TESTSCRIPTS = traffic_mngr.sh
-TEST_EXTENSIONS = .sh
-
-TESTS = $(TESTSCRIPTS)
-
-noinst_LTLIBRARIES = libtesttraffic_mngr.la
-libtesttraffic_mngr_la_SOURCES = traffic_mngr.c
-
-test_PROGRAMS = traffic_mngr_main$(EXEEXT)
-dist_traffic_mngr_main_SOURCES = traffic_mngr_main.c
-traffic_mngr_main_LDADD = libtesttraffic_mngr.la -lm $(LIBCUNIT_COMMON) $(LIBODP)
-
-EXTRA_DIST = traffic_mngr.h $(TESTSCRIPTS)
-dist_check_SCRIPTS = $(TESTSCRIPTS)
-test_SCRIPTS = $(dist_check_SCRIPTS)
diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c
deleted file mode 100644
index 88a7d8c7e..000000000
--- a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c
+++ /dev/null
@@ -1,4017 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#define _GNU_SOURCE
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <math.h>
-#include <odp.h>
-#include <odp/helper/odph_api.h>
-#include <test_debug.h>
-#include "odp_cunit_common.h"
-#include "traffic_mngr.h"
-
-#define TM_DEBUG 0
-
-#define MAX_CAPABILITIES 16
-#define MAX_NUM_IFACES 2
-#define MAX_TM_SYSTEMS 3
-#define NUM_LEVELS 3
-#define NUM_PRIORITIES 4
-#define NUM_QUEUES_PER_NODE NUM_PRIORITIES
-#define FANIN_RATIO 8
-#define NUM_LEVEL0_TM_NODES 1
-#define NUM_LEVEL1_TM_NODES FANIN_RATIO
-#define NUM_LEVEL2_TM_NODES (FANIN_RATIO * FANIN_RATIO)
-#define NUM_TM_QUEUES (NUM_LEVEL2_TM_NODES * NUM_QUEUES_PER_NODE)
-#define NUM_SHAPER_PROFILES 64
-#define NUM_SCHED_PROFILES 64
-#define NUM_THRESHOLD_PROFILES 64
-#define NUM_WRED_PROFILES 64
-#define NUM_SHAPER_TEST_PROFILES 8
-#define NUM_SCHED_TEST_PROFILES 8
-#define NUM_THRESH_TEST_PROFILES 8
-#define NUM_WRED_TEST_PROFILES 8
-
-#define ODP_NUM_PKT_COLORS ODP_NUM_PACKET_COLORS
-#define PKT_GREEN ODP_PACKET_GREEN
-#define PKT_YELLOW ODP_PACKET_YELLOW
-#define PKT_RED ODP_PACKET_RED
-
-#define MIN_COMMIT_BW (64 * 1024)
-#define MIN_COMMIT_BURST 8000
-#define MIN_PEAK_BW 2000000
-#define MIN_PEAK_BURST 16000
-
-#define INITIAL_RCV_GAP_DROP 10 /* This is a percent of rcvd pkts */
-#define ENDING_RCV_GAP_DROP 20 /* This is a percent of rcvd pkts */
-
-#define MIN_SHAPER_BW_RCV_GAP 80 /* Percent of expected_rcv_gap */
-#define MAX_SHAPER_BW_RCV_GAP 125 /* Percent of expected_rcv_gap */
-
-#define MIN_PKT_THRESHOLD 10
-#define MIN_BYTE_THRESHOLD 2048
-
-#define MIN_WRED_THRESH 5
-#define MED_WRED_THRESH 10
-#define MED_DROP_PROB 4
-#define MAX_DROP_PROB 8
-
-#define MAX_PKTS 1000
-#define PKT_BUF_SIZE 1460
-#define MAX_PAYLOAD 1400
-#define USE_IPV4 false
-#define USE_IPV6 true
-#define USE_UDP false
-#define USE_TCP true
-#define LOW_DROP_PRECEDENCE 0x02
-#define MEDIUM_DROP_PRECEDENCE 0x04
-#define HIGH_DROP_PRECEDENCE 0x06
-#define DROP_PRECEDENCE_MASK 0x06
-#define DSCP_CLASS1 0x08
-#define DSCP_CLASS2 0x10
-#define DSCP_CLASS3 0x18
-#define DSCP_CLASS4 0x20
-#define DEFAULT_DSCP (DSCP_CLASS2 | LOW_DROP_PRECEDENCE)
-#define DEFAULT_ECN ODPH_IP_ECN_ECT0
-#define DEFAULT_TOS ((DEFAULT_DSCP << ODPH_IP_TOS_DSCP_SHIFT) | \
- DEFAULT_ECN)
-#define DEFAULT_TTL 128
-#define DEFAULT_UDP_SRC_PORT 12049
-#define DEFAULT_UDP_DST_PORT 12050
-#define DEFAULT_TCP_SRC_PORT 0xDEAD
-#define DEFAULT_TCP_DST_PORT 0xBABE
-#define DEFAULT_TCP_SEQ_NUM 0x12345678
-#define DEFAULT_TCP_ACK_NUM 0x12340000
-#define DEFAULT_TCP_WINDOW 0x4000
-#define VLAN_PRIORITY_BK 1 /* Background - lowest priority */
-#define VLAN_PRIORITY_BE 0 /* Best Effort */
-#define VLAN_PRIORITY_EE 2 /* Excellent Effort */
-#define VLAN_PRIORITY_NC 7 /* Network Control - highest priority */
-#define VLAN_DEFAULT_VID 12
-#define VLAN_NO_DEI ((VLAN_PRIORITY_EE << 13) | VLAN_DEFAULT_VID)
-#define ETHERNET_IFG 12 /* Ethernet Interframe Gap */
-#define ETHERNET_PREAMBLE 8
-#define ETHERNET_OVHD_LEN (ETHERNET_IFG + ETHERNET_PREAMBLE)
-#define CRC_LEN 4
-#define SHAPER_LEN_ADJ ETHERNET_OVHD_LEN
-#define TM_NAME_LEN 32
-#define BILLION 1000000000ULL
-#define MS 1000000 /* Millisecond in units of NS */
-#define MBPS 1000000
-#define GBPS 1000000000
-
-#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
-#define MAX(a, b) (((a) <= (b)) ? (b) : (a))
-
-#define TM_PERCENT(percent) ((uint32_t)(100 * percent))
-
-typedef enum {
- SHAPER_PROFILE, SCHED_PROFILE, THRESHOLD_PROFILE, WRED_PROFILE
-} profile_kind_t;
-
-typedef struct {
- uint32_t num_queues;
- odp_tm_queue_t tm_queues[0];
-} tm_queue_desc_t;
-
-typedef struct tm_node_desc_s tm_node_desc_t;
-
-struct tm_node_desc_s {
- uint32_t level;
- uint32_t node_idx;
- uint32_t num_children;
- char *node_name;
- odp_tm_node_t node;
- odp_tm_node_t parent_node;
- tm_queue_desc_t *queue_desc;
- tm_node_desc_t *children[0];
-};
-
-typedef struct {
- uint32_t num_samples;
- uint32_t min_rcv_gap;
- uint32_t max_rcv_gap;
- uint32_t total_rcv_gap;
- uint64_t total_rcv_gap_squared;
- uint32_t avg_rcv_gap;
- uint32_t std_dev_gap;
-} rcv_stats_t;
-
-typedef struct {
- odp_time_t xmt_time;
- odp_time_t rcv_time;
- uint64_t delta_ns;
- odp_tm_queue_t tm_queue;
- uint16_t pkt_len;
- uint16_t xmt_unique_id;
- uint16_t xmt_idx;
- uint8_t pkt_class;
- uint8_t was_rcvd;
-} xmt_pkt_desc_t;
-
-typedef struct {
- odp_time_t rcv_time;
- xmt_pkt_desc_t *xmt_pkt_desc;
- uint16_t rcv_unique_id;
- uint16_t xmt_idx;
- uint8_t errors;
- uint8_t matched;
- uint8_t pkt_class;
- uint8_t is_ipv4_pkt;
-} rcv_pkt_desc_t;
-
-typedef struct {
- odp_tm_percent_t confidence_percent;
- odp_tm_percent_t drop_percent;
- uint32_t min_cnt;
- uint32_t max_cnt;
-} wred_pkt_cnts_t;
-
-typedef struct {
- uint32_t num_queues;
- uint32_t priority;
- odp_tm_queue_t tm_queues[NUM_LEVEL2_TM_NODES];
-} queue_array_t;
-
-typedef struct {
- queue_array_t queue_array[NUM_PRIORITIES];
-} queues_set_t;
-
-typedef struct {
- uint16_t vlan_tci;
- uint8_t pkt_class;
- uint8_t ip_tos; /* TOS for IPv4 and TC for IPv6 */
- odp_packet_color_t pkt_color;
- odp_bool_t drop_eligible;
- odp_bool_t use_vlan; /* Else no VLAN header */
- odp_bool_t use_ipv6; /* Else use IPv4 */
- odp_bool_t use_tcp; /* Else use UDP */
-} pkt_info_t;
-
-static const char ALPHABET[] =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-/* The following constant table determines the minimum and maximum number of
- * pkts that will be received when sending 100 pkts through a system with a
- * drop probability of p% (using a uniform probability distribution), with a
- * confidence of 99.9% 99.99% and 99.999%. The confidence is interepreted as
- * follows: a 99.99% confidence says that receiving LESS pkts than the given
- * minimum or receiving MORE pkts than the given maximum (assuming a uniform
- * drop percent of p) will happen less than 1 time in 10,000 trials.
- * Mathematically the minimum pkt cnt is the largest value of cnt
- * that satisfies the following equation:
- * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=0..cnt)",
- * where cf is the confidence, caret (^) represents exponentiation,
- * binomial(n,k) is the binomial coefficient defined as n! / (k! * (n-k)!).
- * and p is the drop probability. Similarly the maximum pkt cnt is the
- * smallest value of cnt that satisfies the equation:
- * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=cnt..100)".
- * As a consequence of this, it should be the case that:
- * cf/100 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=min..max)".
- */
-static wred_pkt_cnts_t EXPECTED_PKT_RCVD[] = {
- { TM_PERCENT(99.0), TM_PERCENT(10.0), 82, 97 },
- { TM_PERCENT(99.0), TM_PERCENT(20.0), 69, 90 },
- { TM_PERCENT(99.0), TM_PERCENT(30.0), 58, 81 },
- { TM_PERCENT(99.0), TM_PERCENT(40.0), 47, 72 },
- { TM_PERCENT(99.0), TM_PERCENT(50.0), 37, 63 },
- { TM_PERCENT(99.0), TM_PERCENT(60.0), 28, 53 },
- { TM_PERCENT(99.0), TM_PERCENT(70.0), 19, 42 },
- { TM_PERCENT(99.0), TM_PERCENT(80.0), 10, 31 },
- { TM_PERCENT(99.0), TM_PERCENT(90.0), 3, 18 },
-
- { TM_PERCENT(99.9), TM_PERCENT(10.0), 79, 98 },
- { TM_PERCENT(99.9), TM_PERCENT(20.0), 66, 92 },
- { TM_PERCENT(99.9), TM_PERCENT(30.0), 54, 84 },
- { TM_PERCENT(99.9), TM_PERCENT(40.0), 44, 76 },
- { TM_PERCENT(99.9), TM_PERCENT(50.0), 34, 66 },
- { TM_PERCENT(99.9), TM_PERCENT(60.0), 24, 56 },
- { TM_PERCENT(99.9), TM_PERCENT(70.0), 16, 46 },
- { TM_PERCENT(99.9), TM_PERCENT(80.0), 8, 34 },
- { TM_PERCENT(99.9), TM_PERCENT(90.0), 2, 21 },
-
- { TM_PERCENT(99.99), TM_PERCENT(10.0), 77, 99 },
- { TM_PERCENT(99.99), TM_PERCENT(20.0), 63, 94 },
- { TM_PERCENT(99.99), TM_PERCENT(30.0), 51, 87 },
- { TM_PERCENT(99.99), TM_PERCENT(40.0), 41, 78 },
- { TM_PERCENT(99.99), TM_PERCENT(50.0), 31, 69 },
- { TM_PERCENT(99.99), TM_PERCENT(60.0), 22, 59 },
- { TM_PERCENT(99.99), TM_PERCENT(70.0), 13, 49 },
- { TM_PERCENT(99.99), TM_PERCENT(80.0), 6, 37 },
- { TM_PERCENT(99.99), TM_PERCENT(90.0), 1, 23 },
-};
-
-static uint8_t EQUAL_WEIGHTS[FANIN_RATIO] = {
- 16, 16, 16, 16, 16, 16, 16, 16
-};
-
-static uint8_t INCREASING_WEIGHTS[FANIN_RATIO] = {
- 8, 12, 16, 24, 32, 48, 64, 96
-};
-
-static uint8_t IPV4_SRC_ADDR[ODPH_IPV4ADDR_LEN] = {
- 10, 0, 0, 1 /* I.e. 10.0.0.1 */
-};
-
-static uint8_t IPV4_DST_ADDR[ODPH_IPV4ADDR_LEN] = {
- 10, 0, 0, 100 /* I.e. 10.0.0.100 */
-};
-
-static uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.0.0.1 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 1
-};
-
-static uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
- /* I.e. ::ffff:10.0.0.100 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 100
-};
-
-static odp_tm_t odp_tm_systems[MAX_TM_SYSTEMS];
-static tm_node_desc_t *root_node_descs[MAX_TM_SYSTEMS];
-static uint32_t num_odp_tm_systems;
-
-static odp_tm_capabilities_t tm_capabilities;
-
-static odp_tm_shaper_t shaper_profiles[NUM_SHAPER_PROFILES];
-static odp_tm_sched_t sched_profiles[NUM_SCHED_PROFILES];
-static odp_tm_threshold_t threshold_profiles[NUM_THRESHOLD_PROFILES];
-static odp_tm_wred_t wred_profiles[NUM_WRED_PROFILES][ODP_NUM_PKT_COLORS];
-
-static uint32_t num_shaper_profiles;
-static uint32_t num_sched_profiles;
-static uint32_t num_threshold_profiles;
-static uint32_t num_wred_profiles;
-
-static uint8_t payload_data[MAX_PAYLOAD];
-
-static odp_packet_t xmt_pkts[MAX_PKTS];
-static xmt_pkt_desc_t xmt_pkt_descs[MAX_PKTS];
-static uint32_t num_pkts_made;
-static uint32_t num_pkts_sent;
-
-static odp_packet_t rcv_pkts[MAX_PKTS];
-static rcv_pkt_desc_t rcv_pkt_descs[MAX_PKTS];
-static uint32_t num_rcv_pkts;
-
-static uint32_t rcv_gaps[MAX_PKTS];
-static uint32_t rcv_gap_cnt;
-
-static queues_set_t queues_set;
-static uint32_t unique_id_list[MAX_PKTS];
-
-/* interface names used for testing */
-static const char *iface_name[MAX_NUM_IFACES];
-
-/** number of interfaces being used (1=loopback, 2=pair) */
-static uint32_t num_ifaces;
-
-static odp_pool_t pools[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
-
-static odp_pktio_t pktios[MAX_NUM_IFACES];
-static odp_pktin_queue_t pktins[MAX_NUM_IFACES];
-static odp_pktout_queue_t pktouts[MAX_NUM_IFACES];
-static odp_pktin_queue_t rcv_pktin;
-static odp_pktio_t xmt_pktio;
-
-static odph_ethaddr_t src_mac;
-static odph_ethaddr_t dst_mac;
-
-static uint32_t cpu_unique_id;
-static uint32_t cpu_tcp_seq_num;
-
-static void busy_wait(uint64_t nanoseconds)
-{
- odp_time_t start_time, end_time;
-
- start_time = odp_time_local();
- end_time = odp_time_sum(start_time,
- odp_time_local_from_ns(nanoseconds));
-
- while (odp_time_cmp(odp_time_local(), end_time) < 0)
- odp_cpu_pause();
-}
-
-static odp_bool_t approx_eq32(uint32_t val, uint32_t correct)
-{
- uint64_t low_bound, val_times_100, high_bound;
-
- if (val == correct)
- return true;
-
- low_bound = 98 * (uint64_t)correct;
- val_times_100 = 100 * (uint64_t)val;
- high_bound = 102 * (uint64_t)correct;
-
- if ((low_bound <= val_times_100) && (val_times_100 <= high_bound))
- return true;
- else
- return false;
-}
-
-static odp_bool_t approx_eq64(uint64_t val, uint64_t correct)
-{
- uint64_t low_bound, val_times_100, high_bound;
-
- if (val == correct)
- return true;
-
- low_bound = 98 * correct;
- val_times_100 = 100 * val;
- high_bound = 102 * correct;
-
- if ((low_bound <= val_times_100) && (val_times_100 <= high_bound))
- return true;
- else
- return false;
-}
-
-static int test_overall_capabilities(void)
-{
- odp_tm_level_capabilities_t *per_level;
- odp_tm_capabilities_t capabilities_array[MAX_CAPABILITIES];
- odp_tm_capabilities_t *cap_ptr;
- uint32_t num_records, idx, num_levels, level;
- int rc;
-
- rc = odp_tm_capabilities(capabilities_array, MAX_CAPABILITIES);
- if (rc < 0) {
- CU_ASSERT(rc < 0);
- return -1;
- }
-
- /* Now test the return code (which did not indicate a failure code)
- * to make sure that there is at least ONE capabilities record
- * returned */
- if (rc == 0) {
- CU_ASSERT(rc != 0);
- return -1;
- }
-
- /* Now test the return code to see if there were more capabilities
- * records than the call above allowed for. This is not an error,
- * just an interesting fact.
- */
- num_records = MAX_CAPABILITIES;
- if (MAX_CAPABILITIES < rc)
- LOG_DBG("There were more than %u capabilities (%u)\n",
- MAX_CAPABILITIES, rc);
- else
- num_records = rc;
-
- /* Loop through the returned capabilities (there MUST be at least one)
- * and do some basic checks to prove that it isn't just an empty
- * record. */
- for (idx = 0; idx < num_records; idx++) {
- cap_ptr = &capabilities_array[idx];
- if (cap_ptr->max_tm_queues == 0) {
- CU_ASSERT(cap_ptr->max_tm_queues != 0);
- return -1;
- }
-
- if (cap_ptr->max_levels == 0) {
- CU_ASSERT(cap_ptr->max_levels != 0);
- return -1;
- }
-
- num_levels = cap_ptr->max_levels;
- for (level = 0; level < num_levels; level++) {
- per_level = &cap_ptr->per_level[level];
-
- if (per_level->max_num_tm_nodes == 0) {
- CU_ASSERT(per_level->max_num_tm_nodes != 0);
- return -1;
- }
-
- if (per_level->max_fanin_per_node == 0) {
- CU_ASSERT(per_level->max_fanin_per_node != 0);
- return -1;
- }
-
- if (per_level->max_priority == 0) {
- CU_ASSERT(per_level->max_priority != 0);
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-static int wait_linkup(odp_pktio_t pktio)
-{
- /* wait 1 second for link up */
- uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
- int wait_num = 100;
- int i;
- int ret = -1;
-
- for (i = 0; i < wait_num; i++) {
- ret = odp_pktio_link_status(pktio);
- if (ret < 0 || ret == 1)
- break;
- /* link is down, call status again after delay */
- odp_time_wait_ns(wait_ns);
- }
-
- return ret;
-}
-
-static int open_pktios(void)
-{
- odp_pktio_param_t pktio_param;
- odp_pool_param_t pool_param;
- odp_pktio_t pktio;
- odp_pool_t pkt_pool;
- uint32_t iface;
- char pool_name[ODP_POOL_NAME_LEN];
- int rc, ret;
-
- odp_pool_param_init(&pool_param);
- pool_param.pkt.num = 10 * MAX_PKTS;
- pool_param.type = ODP_POOL_PACKET;
-
- odp_pktio_param_init(&pktio_param);
- pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
- pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
-
- for (iface = 0; iface < num_ifaces; iface++) {
- snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s",
- iface_name[iface]);
-
- pkt_pool = odp_pool_create(pool_name, &pool_param);
- if (pkt_pool == ODP_POOL_INVALID) {
- CU_FAIL("unable to create pool");
- return -1;
- }
-
- pools[iface] = pkt_pool;
- pktio = odp_pktio_open(iface_name[iface], pkt_pool,
- &pktio_param);
- if (pktio == ODP_PKTIO_INVALID)
- pktio = odp_pktio_lookup(iface_name[iface]);
- if (pktio == ODP_PKTIO_INVALID) {
- LOG_ERR("odp_pktio_open() failed\n");
- return -1;
- }
-
- /* Set defaults for PktIn and PktOut queues */
- (void)odp_pktin_queue_config(pktio, NULL);
- (void)odp_pktout_queue_config(pktio, NULL);
- rc = odp_pktio_promisc_mode_set(pktio, true);
- if (rc != 0)
- printf("****** promisc_mode_set failed ******\n");
-
- pktios[iface] = pktio;
-
- if (odp_pktin_queue(pktio, &pktins[iface], 1) != 1) {
- odp_pktio_close(pktio);
- LOG_ERR("odp_pktio_open() failed: no pktin queue\n");
- return -1;
- }
-
- if (odp_pktout_queue(pktio, &pktouts[iface], 1) != 1) {
- odp_pktio_close(pktio);
- LOG_ERR("odp_pktio_open() failed: no pktout queue\n");
- return -1;
- }
-
- rc = -1;
- if (iface == 0)
- rc = odp_pktio_mac_addr(pktio, &src_mac,
- ODPH_ETHADDR_LEN);
-
- if ((iface == 1) || (num_ifaces == 1))
- rc = odp_pktio_mac_addr(pktio, &dst_mac,
- ODPH_ETHADDR_LEN);
-
- if (rc != ODPH_ETHADDR_LEN) {
- LOG_ERR("odp_pktio_mac_addr() failed\n");
- return -1;
- }
- }
-
- if (2 <= num_ifaces) {
- xmt_pktio = pktios[0];
- rcv_pktin = pktins[1];
- ret = odp_pktio_start(pktios[1]);
- if (ret != 0) {
- LOG_ERR("odp_pktio_start() failed\n");
- return -1;
- }
- } else {
- xmt_pktio = pktios[0];
- rcv_pktin = pktins[0];
- }
-
- ret = odp_pktio_start(pktios[0]);
- if (ret != 0) {
- LOG_ERR("odp_pktio_start() failed\n");
- return -1;
- }
-
- /* Now wait until the link or links are up. */
- rc = wait_linkup(pktios[0]);
- if (rc != 1) {
- LOG_ERR("link %" PRIX64 " not up\n",
- odp_pktio_to_u64(pktios[0]));
- return -1;
- }
-
- if (num_ifaces < 2)
- return 0;
-
- /* Wait for 2nd link to be up */
- rc = wait_linkup(pktios[1]);
- if (rc != 1) {
- LOG_ERR("link %" PRIX64 " not up\n",
- odp_pktio_to_u64(pktios[0]));
- return -1;
- }
-
- return 0;
-}
-
-static int get_unique_id(odp_packet_t odp_pkt,
- uint16_t *unique_id_ptr,
- uint8_t *is_ipv4_pkt_ptr)
-{
- odp_u32be_t be_ver_tc_flow;
- odp_u16be_t be_ip_ident;
- odp_bool_t is_ipv4;
- uint32_t l3_offset, ident_offset, flow_offset, ver_tc_flow;
- uint16_t unique_id;
-
- l3_offset = odp_packet_l3_offset(odp_pkt);
-
- if (odp_packet_has_ipv4(odp_pkt)) {
- /* For IPv4 pkts use the ident field to store the unique_id. */
- ident_offset = l3_offset + offsetof(odph_ipv4hdr_t, id);
-
- odp_packet_copy_to_mem(odp_pkt, ident_offset, 2, &be_ip_ident);
- unique_id = odp_be_to_cpu_16(be_ip_ident);
- is_ipv4 = true;
- } else if (odp_packet_has_ipv6(odp_pkt)) {
- /* For IPv6 pkts use the flow field to store the unique_id. */
- flow_offset = l3_offset + offsetof(odph_ipv6hdr_t, ver_tc_flow);
-
- odp_packet_copy_to_mem(odp_pkt, flow_offset, 4,
- &be_ver_tc_flow);
- ver_tc_flow = odp_be_to_cpu_32(be_ver_tc_flow);
- unique_id = ver_tc_flow & ODPH_IPV6HDR_FLOW_LABEL_MASK;
- is_ipv4 = false;
- } else {
- return -1;
- }
-
- if (unique_id_ptr != NULL)
- *unique_id_ptr = unique_id;
-
- if (is_ipv4_pkt_ptr != NULL)
- *is_ipv4_pkt_ptr = is_ipv4;
-
- return 0;
-}
-
-static int get_vlan_tci(odp_packet_t odp_pkt, uint16_t *vlan_tci_ptr)
-{
- odph_vlanhdr_t *vlan_hdr;
- odph_ethhdr_t *ether_hdr;
- uint32_t hdr_len;
- uint16_t vlan_tci;
-
- if (!odp_packet_has_vlan(odp_pkt))
- return -1;
-
- /* *TBD* check value of hdr_len? */
- ether_hdr = odp_packet_l2_ptr(odp_pkt, &hdr_len);
- vlan_hdr = (odph_vlanhdr_t *)(ether_hdr + 1);
- vlan_tci = odp_be_to_cpu_16(vlan_hdr->tci);
- if (vlan_tci_ptr != NULL)
- *vlan_tci_ptr = vlan_tci;
-
- return 0;
-}
-
-/* Returns either the TOS field for IPv4 pkts or the TC field for IPv6 pkts. */
-static int get_ip_tos(odp_packet_t odp_pkt, uint8_t *tos_ptr)
-{
- odph_ipv4hdr_t *ipv4_hdr;
- odph_ipv6hdr_t *ipv6_hdr;
- uint32_t hdr_len, ver_tc_flow;
- uint8_t tos, tc;
-
- if (odp_packet_has_ipv4(odp_pkt)) {
- ipv4_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
- if (hdr_len < 12)
- return -1;
-
- tos = ipv4_hdr->tos;
- } else if (odp_packet_has_ipv6(odp_pkt)) {
- ipv6_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
- if (hdr_len < 4)
- return -1;
-
- ver_tc_flow = odp_be_to_cpu_32(ipv6_hdr->ver_tc_flow);
- tc = (ver_tc_flow & ODPH_IPV6HDR_TC_MASK)
- >> ODPH_IPV6HDR_TC_SHIFT;
- tos = tc;
- } else {
- return -1;
- }
-
- if (tos_ptr != NULL)
- *tos_ptr = tos;
-
- return 0;
-}
-
-static odp_packet_t make_pkt(odp_pool_t pkt_pool,
- uint32_t payload_len,
- uint16_t unique_id,
- pkt_info_t *pkt_info)
-{
- odph_vlanhdr_t *vlan_hdr;
- odph_ipv4hdr_t *ipv4_hdr;
- odph_ipv6hdr_t *ipv6_hdr;
- odph_ethhdr_t *eth_hdr;
- odph_udphdr_t *udp_hdr;
- odph_tcphdr_t *tcp_hdr;
- odp_packet_t odp_pkt;
- uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len;
- uint32_t l4_len, l3_len, l2_len, pkt_len, l3_offset, l4_offset;
- uint32_t version, tc, flow, ver_tc_flow, app_offset;
- uint16_t final_ether_type;
- uint8_t *buf, *pkt_class_ptr, next_hdr;
- int rc;
-
- l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN;
- l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
- vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0;
- l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
- l4_len = l4_hdr_len + payload_len;
- l3_len = l3_hdr_len + l4_len;
- l2_len = l2_hdr_len + l3_len;
- pkt_len = l2_len;
- if (unique_id == 0) {
- LOG_ERR("make_pkt called with invalid unique_id of 0\n");
- return ODP_PACKET_INVALID;
- }
-
- odp_pkt = odp_packet_alloc(pkt_pool, pkt_len);
- if (odp_pkt == ODP_PACKET_INVALID)
- return ODP_PACKET_INVALID;
-
- buf = odp_packet_data(odp_pkt);
-
- /* Ethernet Header */
- odp_packet_l2_offset_set(odp_pkt, 0);
- eth_hdr = (odph_ethhdr_t *)buf;
- final_ether_type = pkt_info->use_ipv6 ? ODPH_ETHTYPE_IPV6
- : ODPH_ETHTYPE_IPV4;
- memcpy(eth_hdr->src.addr, &src_mac, ODPH_ETHADDR_LEN);
- memcpy(eth_hdr->dst.addr, &dst_mac, ODPH_ETHADDR_LEN);
-
- /* Vlan Header */
- if (pkt_info->use_vlan) {
- odp_packet_has_vlan_set(odp_pkt, 1);
- eth_hdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN);
- vlan_hdr = (odph_vlanhdr_t *)(eth_hdr + 1);
- vlan_hdr->tci = odp_cpu_to_be_16(pkt_info->vlan_tci);
- vlan_hdr->type = odp_cpu_to_be_16(final_ether_type);
- } else {
- eth_hdr->type = odp_cpu_to_be_16(final_ether_type);
- }
-
- l3_offset = l2_hdr_len;
- next_hdr = pkt_info->use_tcp ? ODPH_IPPROTO_TCP : ODPH_IPPROTO_UDP;
- odp_packet_l3_offset_set(odp_pkt, l3_offset);
- if (pkt_info->use_ipv6) {
- /* IPv6 Header */
- odp_packet_has_ipv6_set(odp_pkt, 1);
- version = ODPH_IPV6 << ODPH_IPV6HDR_VERSION_SHIFT;
- tc = pkt_info->ip_tos << ODPH_IPV6HDR_TC_SHIFT;
- flow = unique_id << ODPH_IPV6HDR_FLOW_LABEL_SHIFT;
- ver_tc_flow = version | tc | flow;
-
- ipv6_hdr = (odph_ipv6hdr_t *)(buf + l3_offset);
- ipv6_hdr->ver_tc_flow = odp_cpu_to_be_32(ver_tc_flow);
- ipv6_hdr->payload_len = odp_cpu_to_be_16(l4_len);
- ipv6_hdr->next_hdr = next_hdr;
- ipv6_hdr->hop_limit = DEFAULT_TTL;
- memcpy(ipv6_hdr->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
- memcpy(ipv6_hdr->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
- } else {
- /* IPv4 Header */
- odp_packet_has_ipv4_set(odp_pkt, 1);
- ipv4_hdr = (odph_ipv4hdr_t *)(buf + l3_offset);
- ipv4_hdr->ver_ihl = (ODPH_IPV4 << 4) | ODPH_IPV4HDR_IHL_MIN;
- ipv4_hdr->tos = pkt_info->ip_tos;
- ipv4_hdr->tot_len = odp_cpu_to_be_16(l3_len);
- ipv4_hdr->id = odp_cpu_to_be_16(unique_id);
- ipv4_hdr->frag_offset = 0;
- ipv4_hdr->ttl = DEFAULT_TTL;
- ipv4_hdr->proto = next_hdr;
- ipv4_hdr->chksum = 0;
- memcpy(&ipv4_hdr->src_addr, IPV4_SRC_ADDR, ODPH_IPV4ADDR_LEN);
- memcpy(&ipv4_hdr->dst_addr, IPV4_DST_ADDR, ODPH_IPV4ADDR_LEN);
- }
-
- l4_offset = l3_offset + l3_hdr_len;
- odp_packet_l4_offset_set(odp_pkt, l4_offset);
- tcp_hdr = (odph_tcphdr_t *)(buf + l4_offset);
- udp_hdr = (odph_udphdr_t *)(buf + l4_offset);
-
- if (pkt_info->use_tcp) {
- /* TCP Header */
- odp_packet_has_tcp_set(odp_pkt, 1);
- tcp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_TCP_SRC_PORT);
- tcp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_TCP_DST_PORT);
- tcp_hdr->seq_no = odp_cpu_to_be_32(cpu_tcp_seq_num);
- tcp_hdr->ack_no = odp_cpu_to_be_32(DEFAULT_TCP_ACK_NUM);
- tcp_hdr->window = odp_cpu_to_be_16(DEFAULT_TCP_WINDOW);
- tcp_hdr->cksm = 0;
- tcp_hdr->urgptr = 0;
-
- tcp_hdr->doffset_flags = 0;
- tcp_hdr->hl = 5;
- tcp_hdr->ack = 1;
- cpu_tcp_seq_num += payload_len;
- } else {
- /* UDP Header */
- odp_packet_has_udp_set(odp_pkt, 1);
- udp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_UDP_SRC_PORT);
- udp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_UDP_DST_PORT);
- udp_hdr->length = odp_cpu_to_be_16(l4_len);
- udp_hdr->chksum = 0;
- }
-
- app_offset = l4_offset + l4_hdr_len;
- rc = odp_packet_copy_from_mem(odp_pkt, app_offset, payload_len,
- payload_data);
- CU_ASSERT_FATAL(rc == 0);
-
- pkt_class_ptr = odp_packet_offset(odp_pkt, app_offset, NULL, NULL);
- CU_ASSERT_FATAL(pkt_class_ptr != NULL);
- *pkt_class_ptr = pkt_info->pkt_class;
-
- /* Calculate and insert checksums. First the IPv4 header checksum. */
- if (!pkt_info->use_ipv6)
- odph_ipv4_csum_update(odp_pkt);
-
- /* Next the UDP/TCP checksum. */
- if (odph_udp_tcp_chksum(odp_pkt, ODPH_CHKSUM_GENERATE, NULL) != 0)
- LOG_ERR("odph_udp_tcp_chksum failed\n");
-
- return odp_pkt;
-}
-
-static xmt_pkt_desc_t *find_matching_xmt_pkt_desc(uint16_t unique_id)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- uint32_t xmt_pkt_idx;
-
- if (unique_id == 0)
- return NULL;
-
- for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) {
- xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
- if (xmt_pkt_desc->xmt_unique_id == unique_id)
- return xmt_pkt_desc;
- }
-
- return NULL;
-}
-
-static int receive_pkts(odp_tm_t odp_tm,
- odp_pktin_queue_t pktin,
- uint32_t num_pkts,
- uint64_t rate_bps)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- rcv_pkt_desc_t *rcv_pkt_desc;
- odp_packet_t rcv_pkt;
- odp_time_t start_time, current_time, duration, xmt_time;
- odp_time_t rcv_time, delta_time;
- uint64_t temp1, timeout_ns, duration_ns, delta_ns;
- uint32_t pkts_rcvd, rcv_idx, l4_offset, l4_hdr_len, app_offset;
- uint16_t unique_id;
- uint8_t *pkt_class_ptr, pkt_class, is_ipv4_pkt;
- int rc;
-
- temp1 = (1000000ULL * 10000ULL * (uint64_t)num_pkts) / rate_bps;
- timeout_ns = 1000ULL * ((4ULL * temp1) + 10000ULL);
-
- pkts_rcvd = 0;
- start_time = odp_time_local();
- duration_ns = 0;
-
- while ((pkts_rcvd < num_pkts) || (!odp_tm_is_idle(odp_tm))) {
- rc = odp_pktin_recv(pktin, &rcv_pkts[pkts_rcvd], 1);
- if (rc < 0)
- return rc;
-
- current_time = odp_time_local();
- duration = odp_time_diff(current_time, start_time);
- duration_ns = odp_time_to_ns(duration);
- if (rc == 1)
- rcv_pkt_descs[pkts_rcvd++].rcv_time = current_time;
- else if (timeout_ns < duration_ns)
- break;
- }
-
- /* Now go through matching the rcv pkts to the xmt pkts, determining
- * which xmt_pkts were lost and for the ones that did arrive, how
- * long did they take. We don't do this work while receiving the pkts
- * in the loop above because we want to try to get as accurate a
- * rcv timestamp as possible. */
- for (rcv_idx = 0; rcv_idx < pkts_rcvd; rcv_idx++) {
- rcv_pkt = rcv_pkts[rcv_idx];
- rcv_pkt_desc = &rcv_pkt_descs[rcv_idx];
-
- if (odp_packet_has_error(rcv_pkt)) {
- rcv_pkt_desc->errors = 0x01 |
- (odp_packet_has_l2_error(rcv_pkt) << 1) |
- (odp_packet_has_l3_error(rcv_pkt) << 2) |
- (odp_packet_has_l4_error(rcv_pkt) << 3);
-
- LOG_ERR("received a pkt with the following errors\n");
- LOG_ERR(" l2_err=%u l3_err=%u l4_err=%u. Skipping\n",
- (rcv_pkt_desc->errors >> 1) & 0x1,
- (rcv_pkt_desc->errors >> 2) & 0x1,
- (rcv_pkt_desc->errors >> 3) & 0x1);
- }
-
- unique_id = 0;
- rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4_pkt);
- if (rc != 0) {
- LOG_ERR("received a non IPv4/IPv6 pkt\n");
- return -1;
- }
-
- rcv_pkt_desc->rcv_unique_id = unique_id;
- rcv_pkt_desc->is_ipv4_pkt = is_ipv4_pkt;
- if (odp_packet_has_udp(rcv_pkt))
- l4_hdr_len = ODPH_UDPHDR_LEN;
- else if (odp_packet_has_tcp(rcv_pkt))
- l4_hdr_len = ODPH_TCPHDR_LEN;
- else
- l4_hdr_len = 0;
-
- l4_offset = odp_packet_l4_offset(rcv_pkt);
- app_offset = l4_offset + l4_hdr_len;
- pkt_class_ptr = odp_packet_offset(rcv_pkt, app_offset,
- NULL, NULL);
- if (pkt_class_ptr != NULL)
- rcv_pkt_desc->pkt_class = *pkt_class_ptr;
-
- xmt_pkt_desc = find_matching_xmt_pkt_desc(unique_id);
- if (xmt_pkt_desc != NULL) {
- rcv_pkt_desc->xmt_pkt_desc = xmt_pkt_desc;
- rcv_pkt_desc->matched = true;
-
- xmt_time = xmt_pkt_desc->xmt_time;
- rcv_time = rcv_pkt_desc->rcv_time;
- pkt_class = rcv_pkt_desc->pkt_class;
- delta_time = odp_time_diff(rcv_time, xmt_time);
- delta_ns = odp_time_to_ns(delta_time);
-
- rcv_pkt_desc->xmt_idx = xmt_pkt_desc->xmt_idx;
- xmt_pkt_desc->rcv_time = rcv_time;
- xmt_pkt_desc->delta_ns = delta_ns;
- xmt_pkt_desc->pkt_class = pkt_class;
- xmt_pkt_desc->was_rcvd = 1;
- }
- }
-
- return pkts_rcvd;
-}
-
-static void dump_rcvd_pkts(uint32_t first_rcv_idx, uint32_t last_rcv_idx)
-{
- rcv_pkt_desc_t *rcv_pkt_desc;
- odp_packet_t rcv_pkt;
- uint32_t rcv_idx;
- int32_t xmt_idx;
- uint16_t unique_id = 0;
- uint8_t is_ipv4 = 0;
- int rc;
-
- for (rcv_idx = first_rcv_idx; rcv_idx <= last_rcv_idx; rcv_idx++) {
- rcv_pkt = rcv_pkts[rcv_idx];
- rcv_pkt_desc = &rcv_pkt_descs[rcv_idx];
- rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4);
- xmt_idx = -1;
- if (rcv_pkt_desc->matched)
- xmt_idx = rcv_pkt_desc->xmt_pkt_desc->xmt_idx;
-
- printf("rcv_idx=%" PRIu32 " odp_pkt=0x%" PRIX64 " "
- "xmt_idx=%" PRId32 " pkt_class=%u is_ipv4=%u "
- "unique_id=0x%X (rc=%d)\n",
- rcv_idx, odp_packet_to_u64(rcv_pkt), xmt_idx,
- rcv_pkt_desc->pkt_class, is_ipv4, unique_id, rc);
- }
-}
-
-static void free_rcvd_pkts(void)
-{
- odp_packet_t rcv_pkt;
- uint32_t rcv_idx;
-
- /* Go through all of the received pkts and free them. */
- for (rcv_idx = 0; rcv_idx < num_rcv_pkts; rcv_idx++) {
- rcv_pkt = rcv_pkts[rcv_idx];
- if (rcv_pkt != ODP_PACKET_INVALID) {
- odp_packet_free(rcv_pkt);
- rcv_pkts[rcv_idx] = ODP_PACKET_INVALID;
- }
- }
-}
-
-static void flush_leftover_pkts(odp_tm_t odp_tm, odp_pktin_queue_t pktin)
-{
- odp_packet_t rcv_pkt;
- odp_time_t start_time, current_time, duration;
- uint64_t min_timeout_ns, max_timeout_ns, duration_ns;
- int rc;
-
- /* Set the timeout to be at least 10 milliseconds and at most 100
- * milliseconds */
- min_timeout_ns = 10 * ODP_TIME_MSEC_IN_NS;
- max_timeout_ns = 100 * ODP_TIME_MSEC_IN_NS;
- start_time = odp_time_local();
-
- while (true) {
- rc = odp_pktin_recv(pktin, &rcv_pkt, 1);
- if (rc == 1)
- odp_packet_free(rcv_pkt);
-
- current_time = odp_time_local();
- duration = odp_time_diff(current_time, start_time);
- duration_ns = odp_time_to_ns(duration);
-
- if (max_timeout_ns <= duration_ns)
- break;
- else if (duration_ns < min_timeout_ns)
- ;
- else if ((odp_tm_is_idle(odp_tm)) && (rc == 0))
- break;
-
- /* Busy wait here a little bit to prevent overwhelming the
- * odp_pktin_recv logic. */
- busy_wait(10000);
- }
-}
-
-static void init_xmt_pkts(pkt_info_t *pkt_info)
-{
- memset(xmt_pkts, 0, sizeof(xmt_pkts));
- memset(xmt_pkt_descs, 0, sizeof(xmt_pkt_descs));
- num_pkts_made = 0;
- num_pkts_sent = 0;
-
- free_rcvd_pkts();
- memset(rcv_pkts, 0, sizeof(rcv_pkts));
- memset(rcv_pkt_descs, 0, sizeof(rcv_pkt_descs));
- num_rcv_pkts = 0;
-
- memset(rcv_gaps, 0, sizeof(rcv_gaps));
- rcv_gap_cnt = 0;
- memset(pkt_info, 0, sizeof(pkt_info_t));
- pkt_info->ip_tos = DEFAULT_TOS;
-}
-
-static int make_pkts(uint32_t num_pkts,
- uint32_t pkt_len,
- pkt_info_t *pkt_info)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- odp_packet_t odp_pkt;
- uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len;
- uint32_t hdrs_len, payload_len, idx, unique_id, xmt_pkt_idx;
-
- l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN;
- l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
- vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0;
- l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
-
- hdrs_len = l2_hdr_len + l3_hdr_len + l4_hdr_len;
- payload_len = pkt_len - hdrs_len;
-
- for (idx = 0; idx < num_pkts; idx++) {
- unique_id = cpu_unique_id++;
- xmt_pkt_idx = num_pkts_made++;
- xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
- xmt_pkt_desc->pkt_len = pkt_len;
- xmt_pkt_desc->xmt_unique_id = unique_id;
- xmt_pkt_desc->pkt_class = pkt_info->pkt_class;
-
- odp_pkt = make_pkt(pools[0], payload_len, unique_id, pkt_info);
- if (odp_pkt == ODP_PACKET_INVALID)
- return -1;
-
- odp_packet_color_set(odp_pkt, pkt_info->pkt_color);
- odp_packet_drop_eligible_set(odp_pkt, pkt_info->drop_eligible);
- odp_packet_shaper_len_adjust_set(odp_pkt, SHAPER_LEN_ADJ);
-
- xmt_pkts[xmt_pkt_idx] = odp_pkt;
- }
-
- return 0;
-}
-
-static uint32_t send_pkts(odp_tm_queue_t tm_queue, uint32_t num_pkts)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- odp_packet_t odp_pkt;
- uint32_t idx, xmt_pkt_idx, pkts_sent;
- int rc;
-
- /* Now send the pkts as fast as we can. */
- pkts_sent = 0;
- for (idx = 0; idx < num_pkts; idx++) {
- xmt_pkt_idx = num_pkts_sent;
- odp_pkt = xmt_pkts[xmt_pkt_idx];
- xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
-
- /* Alternate calling with odp_tm_enq and odp_tm_enq_with_cnt */
- if ((idx & 1) == 0)
- rc = odp_tm_enq(tm_queue, odp_pkt);
- else
- rc = odp_tm_enq_with_cnt(tm_queue, odp_pkt);
-
- xmt_pkt_desc->xmt_idx = xmt_pkt_idx;
- if (0 <= rc) {
- xmt_pkt_desc->xmt_time = odp_time_local();
- xmt_pkt_desc->tm_queue = tm_queue;
- pkts_sent++;
- } else {
- odp_packet_free(odp_pkt);
- xmt_pkts[xmt_pkt_idx] = ODP_PACKET_INVALID;
- }
-
- num_pkts_sent++;
- }
-
- return pkts_sent;
-}
-
-static uint32_t pkts_rcvd_in_send_order(void)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- odp_time_t last_rcv_time, rcv_time;
- uint32_t xmt_pkt_idx, pkts_rcvd;
-
- pkts_rcvd = 0;
- last_rcv_time = ODP_TIME_NULL;
- for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) {
- xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
- rcv_time = xmt_pkt_desc->rcv_time;
- if (xmt_pkt_desc->was_rcvd != 0) {
- if ((pkts_rcvd != 0) &&
- (odp_time_cmp(rcv_time, last_rcv_time) < 0))
- return 0;
-
- pkts_rcvd++;
- last_rcv_time = xmt_pkt_desc->rcv_time;
- }
- }
-
- return pkts_rcvd;
-}
-
-static int unique_id_list_idx(uint32_t unique_id,
- uint32_t unique_id_list[],
- uint32_t unique_id_list_len)
-{
- uint32_t idx;
-
- for (idx = 0; idx < unique_id_list_len; idx++)
- if (unique_id_list[idx] == unique_id)
- return idx;
-
- return -1;
-}
-
-static uint32_t pkts_rcvd_in_given_order(uint32_t unique_id_list[],
- uint32_t unique_id_list_len,
- uint8_t pkt_class,
- odp_bool_t match_pkt_class,
- odp_bool_t ignore_pkt_class)
-{
- rcv_pkt_desc_t *rcv_pkt_desc;
- odp_bool_t is_match;
- uint32_t rcv_pkt_idx, pkts_in_order, pkts_out_of_order;
- uint32_t rcv_unique_id;
- int last_pkt_idx, pkt_idx;
-
- pkts_in_order = 1;
- pkts_out_of_order = 0;
- last_pkt_idx = -1;
- pkt_idx = -1;
-
- for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
- rcv_pkt_desc = &rcv_pkt_descs[rcv_pkt_idx];
-
- if (ignore_pkt_class)
- is_match = true;
- else if (match_pkt_class)
- is_match = rcv_pkt_desc->pkt_class == pkt_class;
- else
- is_match = rcv_pkt_desc->pkt_class != pkt_class;
-
- if (is_match) {
- rcv_unique_id = rcv_pkt_desc->rcv_unique_id;
- pkt_idx = unique_id_list_idx(rcv_unique_id,
- unique_id_list,
- unique_id_list_len);
- if (0 <= pkt_idx) {
- if (0 <= last_pkt_idx) {
- if (last_pkt_idx < pkt_idx)
- pkts_in_order++;
- else
- pkts_out_of_order++;
- }
-
- last_pkt_idx = pkt_idx;
- }
- }
- }
-
- return pkts_in_order;
-}
-
-static inline void record_rcv_gap(odp_time_t rcv_time, odp_time_t last_rcv_time)
-{
- odp_time_t delta_time;
- uint64_t delta_ns;
- uint32_t rcv_gap;
-
- rcv_gap = 0;
- if (odp_time_cmp(last_rcv_time, rcv_time) <= 0) {
- delta_time = odp_time_diff(rcv_time, last_rcv_time);
- delta_ns = odp_time_to_ns(delta_time);
- rcv_gap = delta_ns / 1000;
- }
-
- /* Note that rcv_gap is in units of microseconds. */
- rcv_gaps[rcv_gap_cnt++] = rcv_gap;
-}
-
-static int rcv_gap_cmp(const void *left_ptr, const void *right_ptr)
-{
- uint32_t left_value, right_value;
-
- left_value = * (const uint32_t *)left_ptr;
- right_value = * (const uint32_t *)right_ptr;
-
- if (left_value < right_value)
- return -1;
- else if (left_value == right_value)
- return 0;
- else
- return 1;
-}
-
-static inline void calc_rcv_stats(rcv_stats_t *rcv_stats,
- uint32_t initial_drop_percent,
- uint32_t ending_drop_percent)
-{
- uint32_t first_rcv_gap_idx, last_rcv_gap_idx, idx, rcv_gap;
-
- /* Sort the rcv_gaps, and then drop the outlying x values before doing
- * doing the rcv stats on the remaining */
- qsort(&rcv_gaps[0], rcv_gap_cnt, sizeof(uint32_t), rcv_gap_cmp);
-
- /* Next we drop the outlying values before doing doing the rcv stats
- * on the remaining rcv_gap values. The number of initial (very low)
- * rcv_gaps dropped and the number of ending (very high) rcv_gaps
- * drops is based on the percentages passed in. */
- first_rcv_gap_idx = (rcv_gap_cnt * initial_drop_percent) / 100;
- last_rcv_gap_idx = (rcv_gap_cnt * (100 - ending_drop_percent)) / 100;
- for (idx = first_rcv_gap_idx; idx <= last_rcv_gap_idx; idx++) {
- rcv_gap = rcv_gaps[idx];
- rcv_stats->min_rcv_gap = MIN(rcv_stats->min_rcv_gap, rcv_gap);
- rcv_stats->max_rcv_gap = MAX(rcv_stats->max_rcv_gap, rcv_gap);
- rcv_stats->total_rcv_gap += rcv_gap;
- rcv_stats->total_rcv_gap_squared += rcv_gap * rcv_gap;
- rcv_stats->num_samples++;
- }
-}
-
-static int rcv_rate_stats(rcv_stats_t *rcv_stats, uint8_t pkt_class)
-{
- xmt_pkt_desc_t *xmt_pkt_desc;
- odp_time_t last_rcv_time, rcv_time;
- uint32_t pkt_idx, pkts_rcvd, num;
- uint32_t avg, variance, std_dev;
-
- pkts_rcvd = 0;
- last_rcv_time = ODP_TIME_NULL;
- memset(rcv_stats, 0, sizeof(rcv_stats_t));
- rcv_stats->min_rcv_gap = 1000000000;
-
- for (pkt_idx = 0; pkt_idx < num_pkts_sent; pkt_idx++) {
- xmt_pkt_desc = &xmt_pkt_descs[pkt_idx];
- if ((xmt_pkt_desc->was_rcvd != 0) &&
- (xmt_pkt_desc->pkt_class == pkt_class)) {
- rcv_time = xmt_pkt_desc->rcv_time;
- if (pkts_rcvd != 0)
- record_rcv_gap(rcv_time, last_rcv_time);
- pkts_rcvd++;
- last_rcv_time = rcv_time;
- }
- }
-
- if (pkts_rcvd == 0)
- return -1;
-
- calc_rcv_stats(rcv_stats, INITIAL_RCV_GAP_DROP, ENDING_RCV_GAP_DROP);
- num = rcv_stats->num_samples;
- if (num == 0)
- return -1;
-
- avg = rcv_stats->total_rcv_gap / num;
- variance = (rcv_stats->total_rcv_gap_squared / num) - avg * avg;
- std_dev = (uint32_t)sqrt((double)variance);
-
- rcv_stats->avg_rcv_gap = avg;
- rcv_stats->std_dev_gap = std_dev;
- return 0;
-}
-
-static int create_tm_queue(odp_tm_t odp_tm,
- odp_tm_node_t tm_node,
- uint32_t node_idx,
- tm_queue_desc_t *queue_desc,
- uint32_t priority)
-{
- odp_tm_queue_params_t queue_params;
- odp_tm_queue_t tm_queue;
- odp_tm_wred_t green_profile, yellow_profile, red_profile;
- int rc;
-
- odp_tm_queue_params_init(&queue_params);
- queue_params.priority = priority;
- if (priority == 0) {
- green_profile = wred_profiles[node_idx][PKT_GREEN];
- yellow_profile = wred_profiles[node_idx][PKT_YELLOW];
- red_profile = wred_profiles[node_idx][PKT_RED];
-
- queue_params.shaper_profile = shaper_profiles[0];
- queue_params.threshold_profile = threshold_profiles[0];
- queue_params.wred_profile[PKT_GREEN] = green_profile;
- queue_params.wred_profile[PKT_YELLOW] = yellow_profile;
- queue_params.wred_profile[PKT_RED] = red_profile;
- }
-
- tm_queue = odp_tm_queue_create(odp_tm, &queue_params);
- if (tm_queue == ODP_TM_INVALID) {
- LOG_ERR("odp_tm_queue_create() failed\n");
- return -1;
- }
-
- queue_desc->tm_queues[priority] = tm_queue;
- rc = odp_tm_queue_connect(tm_queue, tm_node);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_connect() failed\n");
- odp_tm_queue_destroy(tm_queue);
- return -1;
- }
-
- return 0;
-}
-
-static int destroy_tm_queue(odp_tm_queue_t tm_queue)
-{
- odp_tm_queue_disconnect(tm_queue);
- return odp_tm_queue_destroy(tm_queue);
-}
-
-static tm_node_desc_t *create_tm_node(odp_tm_t odp_tm,
- uint32_t level,
- uint32_t num_levels,
- uint32_t node_idx,
- tm_node_desc_t *parent_node_desc)
-{
- odp_tm_node_params_t node_params;
- tm_queue_desc_t *queue_desc;
- tm_node_desc_t *node_desc;
- odp_tm_wred_t green_profile, yellow_profile, red_profile;
- odp_tm_node_t tm_node, parent_node;
- uint32_t node_desc_size, queue_desc_size, priority;
- char node_name[TM_NAME_LEN];
- int rc;
-
- odp_tm_node_params_init(&node_params);
- node_params.shaper_profile = ODP_TM_INVALID;
- node_params.threshold_profile = ODP_TM_INVALID;
- node_params.wred_profile[PKT_GREEN] = ODP_TM_INVALID;
- node_params.wred_profile[PKT_YELLOW] = ODP_TM_INVALID;
- node_params.wred_profile[PKT_RED] = ODP_TM_INVALID;
- if (node_idx == 0) {
- node_params.shaper_profile = shaper_profiles[0];
- node_params.threshold_profile = threshold_profiles[0];
- if (level == num_levels) {
- green_profile = wred_profiles[node_idx][PKT_GREEN];
- yellow_profile = wred_profiles[node_idx][PKT_YELLOW];
- red_profile = wred_profiles[node_idx][PKT_RED];
-
- node_params.wred_profile[PKT_GREEN] = green_profile;
- node_params.wred_profile[PKT_YELLOW] = yellow_profile;
- node_params.wred_profile[PKT_RED] = red_profile;
- }
- }
-
- node_params.max_fanin = FANIN_RATIO;
- node_params.level = level;
- if (parent_node_desc == NULL)
- snprintf(node_name, sizeof(node_name), "node_%" PRIu32,
- node_idx + 1);
- else
- snprintf(node_name, sizeof(node_name), "%s_%" PRIu32,
- parent_node_desc->node_name, node_idx + 1);
-
- tm_node = odp_tm_node_create(odp_tm, node_name, &node_params);
- if (tm_node == ODP_TM_INVALID) {
- LOG_ERR("odp_tm_node_create() failed @ level=%" PRIu32 "\n",
- level);
- return NULL;
- }
-
- /* Now connect this node to the lower level "parent" node. */
- if (level == 0 || !parent_node_desc)
- parent_node = ODP_TM_ROOT;
- else
- parent_node = parent_node_desc->node;
-
- rc = odp_tm_node_connect(tm_node, parent_node);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_connect() failed @ level=%" PRIu32 "\n",
- level);
- odp_tm_node_destroy(tm_node);
- return NULL;
- }
-
- node_desc_size = sizeof(tm_node_desc_t) +
- sizeof(odp_tm_node_t) * FANIN_RATIO;
- node_desc = malloc(node_desc_size);
- memset(node_desc, 0, node_desc_size);
- node_desc->level = level;
- node_desc->node_idx = node_idx;
- node_desc->num_children = FANIN_RATIO;
- node_desc->node = tm_node;
- node_desc->parent_node = parent_node;
- node_desc->node_name = strdup(node_name);
-
- /* Finally if the level is the highest then make fanin_ratio tm_queues
- * feeding this node. */
- if (level < (num_levels - 1))
- return node_desc;
-
- node_desc->num_children = 0;
- queue_desc_size = sizeof(tm_queue_desc_t) +
- sizeof(odp_tm_queue_t) * NUM_QUEUES_PER_NODE;
- queue_desc = malloc(queue_desc_size);
- memset(queue_desc, 0, queue_desc_size);
- queue_desc->num_queues = NUM_QUEUES_PER_NODE;
- node_desc->queue_desc = queue_desc;
-
- for (priority = 0; priority < NUM_QUEUES_PER_NODE; priority++) {
- rc = create_tm_queue(odp_tm, tm_node, node_idx, queue_desc,
- priority);
- if (rc != 0) {
- LOG_ERR("create_tm_queue() failed @ "
- "level=%" PRIu32 "\n", level);
- while (priority > 0)
- (void)destroy_tm_queue
- (queue_desc->tm_queues[--priority]);
- free(queue_desc);
- free(node_desc);
- return NULL;
- }
- }
-
- return node_desc;
-}
-
-static tm_node_desc_t *create_tm_subtree(odp_tm_t odp_tm,
- uint32_t level,
- uint32_t num_levels,
- uint32_t node_idx,
- tm_node_desc_t *parent_node)
-{
- tm_node_desc_t *node_desc, *child_desc;
- uint32_t child_idx;
-
- node_desc = create_tm_node(odp_tm, level, num_levels,
- node_idx, parent_node);
- if (node_desc == NULL) {
- LOG_ERR("create_tm_node() failed @ level=%" PRIu32 "\n", level);
- return NULL;
- }
-
- if (level < (num_levels - 1)) {
- for (child_idx = 0; child_idx < FANIN_RATIO; child_idx++) {
- child_desc = create_tm_subtree(odp_tm, level + 1,
- num_levels, child_idx,
- node_desc);
- if (child_desc == NULL) {
- LOG_ERR("create_tm_subtree failed "
- "level=%" PRIu32 "\n", level);
-
- return NULL;
- }
-
- node_desc->children[child_idx] = child_desc;
- }
- }
-
- return node_desc;
-}
-
-static odp_tm_node_t find_tm_node(uint8_t tm_system_idx, const char *node_name)
-{
- return odp_tm_node_lookup(odp_tm_systems[tm_system_idx], node_name);
-}
-
-static tm_node_desc_t *find_node_desc(uint8_t tm_system_idx,
- const char *node_name)
-{
- tm_node_desc_t *node_desc;
- uint32_t child_num;
- char *name_ptr;
-
- /* Assume node_name is "node_" followed by a sequence of integers
- * separated by underscores, where each integer is the child number to
- * get to the next level node. */
- node_desc = root_node_descs[tm_system_idx];
- name_ptr = strchr(node_name, '_');
- if (name_ptr == NULL)
- return NULL;
-
- /* Skip over the first integer */
- name_ptr++;
- name_ptr = strchr(name_ptr, '_');
- if (name_ptr != NULL)
- name_ptr++;
-
- while (node_desc != NULL) {
- if (strncmp(node_desc->node_name, node_name, TM_NAME_LEN) == 0)
- return node_desc;
-
- if (name_ptr == NULL)
- return NULL;
-
- child_num = atoi(name_ptr);
- if (node_desc->num_children < child_num)
- return NULL;
-
- node_desc = node_desc->children[child_num - 1];
- name_ptr = strchr(name_ptr, '_');
- if (name_ptr != NULL)
- name_ptr++;
- }
-
- return NULL;
-}
-
-static odp_tm_queue_t find_tm_queue(uint8_t tm_system_idx,
- const char *node_name,
- uint8_t priority)
-{
- tm_queue_desc_t *queue_desc;
- tm_node_desc_t *node_desc;
-
- node_desc = find_node_desc(tm_system_idx, node_name);
- if (node_desc == NULL)
- return ODP_TM_INVALID;
-
- queue_desc = node_desc->queue_desc;
- if (queue_desc == NULL)
- return ODP_TM_INVALID;
-
- return queue_desc->tm_queues[priority];
-}
-
-static uint32_t find_child_queues(uint8_t tm_system_idx,
- tm_node_desc_t *node_desc,
- uint8_t priority,
- odp_tm_queue_t tm_queues[],
- uint32_t max_queues)
-{
- tm_queue_desc_t *queue_desc;
- tm_node_desc_t *child_node_desc;
- uint32_t num_children, num_queues, child_idx, rem_queues;
-
- if (max_queues == 0)
- return 0;
-
- queue_desc = node_desc->queue_desc;
- if (queue_desc != NULL) {
- tm_queues[0] = queue_desc->tm_queues[priority];
- return 1;
- }
-
- num_children = node_desc->num_children;
- num_queues = 0;
-
- for (child_idx = 0; child_idx < num_children; child_idx++) {
- child_node_desc = node_desc->children[child_idx];
- rem_queues = max_queues - num_queues;
- num_queues += find_child_queues(tm_system_idx, child_node_desc,
- priority,
- &tm_queues[num_queues],
- rem_queues);
- if (num_queues == max_queues)
- break;
- }
-
- return num_queues;
-}
-
-static int create_tm_system(void)
-{
- odp_tm_level_requirements_t *per_level;
- odp_tm_requirements_t requirements;
- odp_tm_egress_t egress;
- odp_packet_color_t color;
- tm_node_desc_t *root_node_desc;
- uint32_t level, max_nodes[ODP_TM_MAX_LEVELS];
- odp_tm_t odp_tm, found_odp_tm;
- char tm_name[TM_NAME_LEN];
- int rc;
-
- odp_tm_requirements_init(&requirements);
- odp_tm_egress_init(&egress);
-
- requirements.max_tm_queues = NUM_TM_QUEUES + 1;
- requirements.num_levels = NUM_LEVELS;
- requirements.tm_queue_shaper_needed = true;
- requirements.tm_queue_wred_needed = true;
- requirements.tm_queue_dual_slope_needed = true;
- requirements.vlan_marking_needed = false;
- requirements.ecn_marking_needed = true;
- requirements.drop_prec_marking_needed = true;
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
- requirements.marking_colors_needed[color] = true;
-
- /* Set the max_num_tm_nodes to be double the expected number of nodes
- * at that level */
- memset(max_nodes, 0, sizeof(max_nodes));
- max_nodes[0] = 2 * NUM_LEVEL0_TM_NODES;
- max_nodes[1] = 2 * NUM_LEVEL1_TM_NODES;
- max_nodes[2] = 2 * NUM_LEVEL2_TM_NODES;
- max_nodes[3] = 2 * NUM_LEVEL2_TM_NODES * FANIN_RATIO;
-
- for (level = 0; level < NUM_LEVELS; level++) {
- per_level = &requirements.per_level[level];
- per_level->max_priority = NUM_PRIORITIES - 1;
- per_level->max_num_tm_nodes = max_nodes[level];
- per_level->max_fanin_per_node = FANIN_RATIO;
- per_level->tm_node_shaper_needed = true;
- per_level->tm_node_wred_needed = false;
- per_level->tm_node_dual_slope_needed = false;
- per_level->fair_queuing_needed = true;
- per_level->weights_needed = true;
- }
-
- egress.egress_kind = ODP_TM_EGRESS_PKT_IO;
- egress.pktio = xmt_pktio;
-
- snprintf(tm_name, sizeof(tm_name), "TM_system_%" PRIu32,
- num_odp_tm_systems);
- odp_tm = odp_tm_create(tm_name, &requirements, &egress);
- if (odp_tm == ODP_TM_INVALID) {
- LOG_ERR("odp_tm_create() failed\n");
- return -1;
- }
-
- odp_tm_systems[num_odp_tm_systems] = odp_tm;
-
- root_node_desc = create_tm_subtree(odp_tm, 0, NUM_LEVELS, 0, NULL);
- root_node_descs[num_odp_tm_systems] = root_node_desc;
- if (root_node_desc == NULL) {
- LOG_ERR("create_tm_subtree() failed\n");
- return -1;
- }
-
- num_odp_tm_systems++;
-
- /* Test odp_tm_capability and odp_tm_find. */
- rc = odp_tm_capability(odp_tm, &tm_capabilities);
- if (rc != 0) {
- LOG_ERR("odp_tm_capability() failed\n");
- return -1;
- }
-
- found_odp_tm = odp_tm_find(tm_name, &requirements, &egress);
- if ((found_odp_tm == ODP_TM_INVALID) || (found_odp_tm != odp_tm)) {
- LOG_ERR("odp_tm_find() failed\n");
- return -1;
- }
-
- return 0;
-}
-
-static void dump_tm_subtree(tm_node_desc_t *node_desc)
-{
- odp_tm_node_info_t node_info;
- uint32_t idx, num_queues, child_idx;
- int rc;
-
- for (idx = 0; idx < node_desc->level; idx++)
- printf(" ");
-
- rc = odp_tm_node_info(node_desc->node, &node_info);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n",
- node_desc->node);
- }
-
- num_queues = 0;
- if (node_desc->queue_desc != NULL)
- num_queues = node_desc->queue_desc->num_queues;
-
- printf("node_desc=%p name='%s' tm_node=0x%" PRIX64 " idx=%" PRIu32 " "
- "level=%" PRIu32" parent=0x%" PRIX64 " children=%" PRIu32 " "
- "queues=%" PRIu32 " queue_fanin=%" PRIu32 " "
- "node_fanin=%" PRIu32 "\n",
- node_desc, node_desc->node_name, node_desc->node,
- node_desc->node_idx, node_desc->level, node_desc->parent_node,
- node_desc->num_children, num_queues, node_info.tm_queue_fanin,
- node_info.tm_node_fanin);
-
- for (child_idx = 0; child_idx < node_desc->num_children; child_idx++)
- dump_tm_subtree(node_desc->children[child_idx]);
-}
-
-static void dump_tm_tree(uint32_t tm_idx)
-{
- tm_node_desc_t *root_node_desc;
-
- if (!TM_DEBUG)
- return;
-
- root_node_desc = root_node_descs[tm_idx];
- dump_tm_subtree(root_node_desc);
-}
-
-static int unconfig_tm_queue_profiles(odp_tm_queue_t tm_queue)
-{
- odp_tm_queue_info_t queue_info;
- odp_tm_wred_t wred_profile;
- uint32_t color;
- int rc;
-
- rc = odp_tm_queue_info(tm_queue, &queue_info);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_info failed code=%d\n", rc);
- return rc;
- }
-
- if (queue_info.shaper_profile != ODP_TM_INVALID) {
- rc = odp_tm_queue_shaper_config(tm_queue, ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_shaper_config failed code=%d\n",
- rc);
- return rc;
- }
- }
-
- if (queue_info.threshold_profile != ODP_TM_INVALID) {
- rc = odp_tm_queue_threshold_config(tm_queue, ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_threshold_config failed "
- "code=%d\n", rc);
- return rc;
- }
- }
-
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_profile = queue_info.wred_profile[color];
- if (wred_profile != ODP_TM_INVALID) {
- rc = odp_tm_queue_wred_config(tm_queue, color,
- ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_wred_config failed "
- "color=%" PRIu32 " code=%d\n",
- color, rc);
- return rc;
- }
- }
- }
-
- return 0;
-}
-
-static int destroy_tm_queues(tm_queue_desc_t *queue_desc)
-{
- odp_tm_queue_t tm_queue;
- uint32_t num_queues, queue_idx;
- int rc;
-
- num_queues = queue_desc->num_queues;
- for (queue_idx = 0; queue_idx < num_queues; queue_idx++) {
- tm_queue = queue_desc->tm_queues[queue_idx];
- if (tm_queue != ODP_TM_INVALID) {
- rc = odp_tm_queue_disconnect(tm_queue);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_disconnect failed "
- "idx=%" PRIu32 " code=%d\n",
- queue_idx, rc);
- return rc;
- }
-
- rc = unconfig_tm_queue_profiles(tm_queue);
- if (rc != 0) {
- LOG_ERR("unconfig_tm_queue_profiles failed "
- "idx=%" PRIu32 " code=%d\n",
- queue_idx, rc);
- return rc;
- }
-
- rc = odp_tm_queue_destroy(tm_queue);
- if (rc != 0) {
- LOG_ERR("odp_tm_queue_destroy failed "
- "idx=%" PRIu32 " code=%d\n",
- queue_idx, rc);
- return rc;
- }
- }
- }
-
- free(queue_desc);
- return 0;
-}
-
-static int unconfig_tm_node_profiles(odp_tm_node_t tm_node)
-{
- odp_tm_node_info_t node_info;
- odp_tm_wred_t wred_profile;
- uint32_t color;
- int rc;
-
- rc = odp_tm_node_info(tm_node, &node_info);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_info failed code=%d\n", rc);
- return rc;
- }
-
- if (node_info.shaper_profile != ODP_TM_INVALID) {
- rc = odp_tm_node_shaper_config(tm_node, ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_shaper_config failed code=%d\n",
- rc);
- return rc;
- }
- }
-
- if (node_info.threshold_profile != ODP_TM_INVALID) {
- rc = odp_tm_node_threshold_config(tm_node, ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_threshold_config failed "
- "code=%d\n", rc);
- return rc;
- }
- }
-
- for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
- wred_profile = node_info.wred_profile[color];
- if (wred_profile != ODP_TM_INVALID) {
- rc = odp_tm_node_wred_config(tm_node, color,
- ODP_TM_INVALID);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_wred_config failed "
- "color=%" PRIu32 " code=%d\n",
- color, rc);
- return rc;
- }
- }
- }
-
- return 0;
-}
-
-static int destroy_tm_subtree(tm_node_desc_t *node_desc)
-{
- tm_queue_desc_t *queue_desc;
- tm_node_desc_t *child_desc;
- odp_tm_node_t tm_node;
- uint32_t num_children, child_num;
- int rc;
-
- num_children = node_desc->num_children;
- for (child_num = 0; child_num < num_children; child_num++) {
- child_desc = node_desc->children[child_num];
- if (child_desc != NULL) {
- rc = destroy_tm_subtree(child_desc);
- if (rc != 0) {
- LOG_ERR("destroy_tm_subtree failed "
- "child_num=%" PRIu32 " code=%d\n",
- child_num, rc);
- return rc;
- }
- }
- }
-
- queue_desc = node_desc->queue_desc;
- if (queue_desc != NULL) {
- rc = destroy_tm_queues(queue_desc);
- if (rc != 0) {
- LOG_ERR("destroy_tm_queues failed code=%d\n", rc);
- return rc;
- }
- }
-
- tm_node = node_desc->node;
- rc = odp_tm_node_disconnect(tm_node);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_disconnect failed code=%d\n", rc);
- return rc;
- }
-
- rc = unconfig_tm_node_profiles(tm_node);
- if (rc != 0) {
- LOG_ERR("unconfig_tm_node_profiles failed code=%d\n", rc);
- return rc;
- }
-
- rc = odp_tm_node_destroy(tm_node);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_destroy failed code=%d\n", rc);
- return rc;
- }
-
- if (node_desc->node_name)
- free(node_desc->node_name);
-
- free(node_desc);
- return 0;
-}
-
-static int destroy_all_shaper_profiles(void)
-{
- odp_tm_shaper_t shaper_profile;
- uint32_t idx;
- int rc;
-
- for (idx = 0; idx < NUM_SHAPER_PROFILES; idx++) {
- shaper_profile = shaper_profiles[idx];
- if (shaper_profile != ODP_TM_INVALID) {
- rc = odp_tm_shaper_destroy(shaper_profile);
- if (rc != 0) {
- LOG_ERR("odp_tm_sched_destroy failed "
- "idx=%" PRIu32 " code=%d\n", idx, rc);
- return rc;
- }
- shaper_profiles[idx] = ODP_TM_INVALID;
- }
- }
-
- return 0;
-}
-
-static int destroy_all_sched_profiles(void)
-{
- odp_tm_sched_t sched_profile;
- uint32_t idx;
- int rc;
-
- for (idx = 0; idx < NUM_SCHED_PROFILES; idx++) {
- sched_profile = sched_profiles[idx];
- if (sched_profile != ODP_TM_INVALID) {
- rc = odp_tm_sched_destroy(sched_profile);
- if (rc != 0) {
- LOG_ERR("odp_tm_sched_destroy failed "
- "idx=%" PRIu32 " code=%d\n", idx, rc);
- return rc;
- }
- sched_profiles[idx] = ODP_TM_INVALID;
- }
- }
-
- return 0;
-}
-
-static int destroy_all_threshold_profiles(void)
-{
- odp_tm_threshold_t threshold_profile;
- uint32_t idx;
- int rc;
-
- for (idx = 0; idx < NUM_THRESHOLD_PROFILES; idx++) {
- threshold_profile = threshold_profiles[idx];
- if (threshold_profile != ODP_TM_INVALID) {
- rc = odp_tm_threshold_destroy(threshold_profile);
- if (rc != 0) {
- LOG_ERR("odp_tm_threshold_destroy failed "
- "idx=%" PRIu32 " code=%d\n", idx, rc);
- return rc;
- }
- threshold_profiles[idx] = ODP_TM_INVALID;
- }
- }
-
- return 0;
-}
-
-static int destroy_all_wred_profiles(void)
-{
- odp_tm_wred_t wred_profile;
- uint32_t idx, color;
- int rc;
-
- for (idx = 0; idx < NUM_WRED_PROFILES; idx++) {
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- wred_profile = wred_profiles[idx][color];
- if (wred_profile != ODP_TM_INVALID) {
- rc = odp_tm_wred_destroy(wred_profile);
- if (rc != 0) {
- LOG_ERR("odp_tm_wred_destroy failed "
- "idx=%" PRIu32 " "
- "color=%" PRIu32 " code=%d\n",
- idx, color, rc);
- return rc;
- }
- wred_profiles[idx][color] = ODP_TM_INVALID;
- }
- }
- }
-
- return 0;
-}
-
-static int destroy_all_profiles(void)
-{
- int rc;
-
- rc = destroy_all_shaper_profiles();
- if (rc != 0) {
- LOG_ERR("destroy_all_shaper_profiles failed code=%d\n", rc);
- return rc;
- }
-
- rc = destroy_all_sched_profiles();
- if (rc != 0) {
- LOG_ERR("destroy_all_sched_profiles failed code=%d\n", rc);
- return rc;
- }
-
- rc = destroy_all_threshold_profiles();
- if (rc != 0) {
- LOG_ERR("destroy_all_threshold_profiles failed code=%d\n", rc);
- return rc;
- }
-
- rc = destroy_all_wred_profiles();
- if (rc != 0) {
- LOG_ERR("destroy_all_wred_profiles failed code=%d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-static int destroy_tm_systems(void)
-{
- uint32_t idx;
-
- /* Close/free the TM systems. */
- for (idx = 0; idx < num_odp_tm_systems; idx++) {
- if (destroy_tm_subtree(root_node_descs[idx]) != 0)
- return -1;
-
- if (odp_tm_destroy(odp_tm_systems[idx]) != 0)
- return -1;
- }
-
- /* Close/free the TM profiles. */
- if (destroy_all_profiles() != 0)
- return -1;
-
- return 0;
-}
-
-int traffic_mngr_suite_init(void)
-{
- uint32_t payload_len, copy_len;
-
- /* Initialize some global variables. */
- num_pkts_made = 0;
- num_pkts_sent = 0;
- num_rcv_pkts = 0;
- cpu_unique_id = 1;
- cpu_tcp_seq_num = DEFAULT_TCP_SEQ_NUM;
- memset(xmt_pkts, 0, sizeof(xmt_pkts));
- memset(rcv_pkts, 0, sizeof(rcv_pkts));
-
- payload_len = 0;
- while (payload_len < MAX_PAYLOAD) {
- copy_len = MIN(MAX_PAYLOAD - payload_len, sizeof(ALPHABET));
- memcpy(&payload_data[payload_len], ALPHABET, copy_len);
- payload_len += copy_len;
- }
-
- /* Next open a single or pair of interfaces. This should be the same
- * logic as in the pktio_suite_init() function in the
- * test/validation/pktio.c file. */
- iface_name[0] = getenv("ODP_PKTIO_IF0");
- iface_name[1] = getenv("ODP_PKTIO_IF1");
- num_ifaces = 1;
-
- if (!iface_name[0]) {
- printf("No interfaces specified, using default \"loop\".\n");
- iface_name[0] = "loop";
- } else if (!iface_name[1]) {
- printf("Using loopback interface: %s\n", iface_name[0]);
- } else {
- num_ifaces = 2;
- printf("Using paired interfaces: %s %s\n",
- iface_name[0], iface_name[1]);
- }
-
- if (open_pktios() != 0)
- return -1;
-
- return 0;
-}
-
-int traffic_mngr_suite_term(void)
-{
- uint32_t iface;
-
- /* Close the pktios and associated packet pools. */
- free_rcvd_pkts();
- for (iface = 0; iface < num_ifaces; iface++) {
- if (odp_pktio_stop(pktios[iface]) != 0)
- return -1;
-
- if (odp_pktio_close(pktios[iface]) != 0)
- return -1;
-
- if (odp_pool_destroy(pools[iface]) != 0)
- return -1;
- }
-
- return 0;
-}
-
-static void check_shaper_profile(char *shaper_name, uint32_t shaper_idx)
-{
- odp_tm_shaper_params_t shaper_params;
- odp_tm_shaper_t profile;
-
- profile = odp_tm_shaper_lookup(shaper_name);
- CU_ASSERT(profile != ODP_TM_INVALID);
- CU_ASSERT(profile == shaper_profiles[shaper_idx - 1]);
- if (profile != shaper_profiles[shaper_idx - 1])
- return;
-
- odp_tm_shaper_params_read(profile, &shaper_params);
- CU_ASSERT(approx_eq64(shaper_params.commit_bps,
- shaper_idx * MIN_COMMIT_BW));
- CU_ASSERT(approx_eq64(shaper_params.peak_bps,
- shaper_idx * MIN_PEAK_BW));
- CU_ASSERT(approx_eq32(shaper_params.commit_burst,
- shaper_idx * MIN_COMMIT_BURST));
- CU_ASSERT(approx_eq32(shaper_params.peak_burst,
- shaper_idx * MIN_PEAK_BURST));
-
- CU_ASSERT(shaper_params.shaper_len_adjust == SHAPER_LEN_ADJ);
- CU_ASSERT(shaper_params.dual_rate == 0);
-}
-
-void traffic_mngr_test_shaper_profile(void)
-{
- odp_tm_shaper_params_t shaper_params;
- odp_tm_shaper_t profile;
- uint32_t idx, shaper_idx, i;
- char shaper_name[TM_NAME_LEN];
-
- odp_tm_shaper_params_init(&shaper_params);
- shaper_params.shaper_len_adjust = SHAPER_LEN_ADJ;
- shaper_params.dual_rate = 0;
-
- for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) {
- snprintf(shaper_name, sizeof(shaper_name),
- "shaper_profile_%" PRIu32, idx);
- shaper_params.commit_bps = idx * MIN_COMMIT_BW;
- shaper_params.peak_bps = idx * MIN_PEAK_BW;
- shaper_params.commit_burst = idx * MIN_COMMIT_BURST;
- shaper_params.peak_burst = idx * MIN_PEAK_BURST;
-
- profile = odp_tm_shaper_create(shaper_name, &shaper_params);
- CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
-
- /* Make sure profile handle is unique */
- for (i = 1; i < idx - 1; i++)
- CU_ASSERT(profile != shaper_profiles[i - 1]);
-
- shaper_profiles[idx - 1] = profile;
- num_shaper_profiles++;
- }
-
- /* Now test odp_tm_shaper_lookup */
- for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) {
- /* The following equation is designed is somewhat randomize
- * the lookup of the profiles to catch any implementations
- *taking shortcuts. */
- shaper_idx = ((3 + 7 * idx) % NUM_SHAPER_TEST_PROFILES) + 1;
- snprintf(shaper_name, sizeof(shaper_name),
- "shaper_profile_%" PRIu32, shaper_idx);
-
- check_shaper_profile(shaper_name, shaper_idx);
- }
-}
-
-static void check_sched_profile(char *sched_name, uint32_t sched_idx)
-{
- odp_tm_sched_params_t sched_params;
- odp_tm_sched_t profile;
- uint32_t priority;
-
- profile = odp_tm_sched_lookup(sched_name);
- CU_ASSERT(profile != ODP_TM_INVALID);
- CU_ASSERT(profile == sched_profiles[sched_idx - 1]);
- if (profile != sched_profiles[sched_idx - 1])
- return;
-
- odp_tm_sched_params_read(profile, &sched_params);
- for (priority = 0; priority < NUM_PRIORITIES; priority++) {
- CU_ASSERT(sched_params.sched_modes[priority] ==
- ODP_TM_BYTE_BASED_WEIGHTS);
- CU_ASSERT(approx_eq32(sched_params.sched_weights[priority],
- 8 + sched_idx + priority));
- }
-}
-
-void traffic_mngr_test_sched_profile(void)
-{
- odp_tm_sched_params_t sched_params;
- odp_tm_sched_t profile;
- uint32_t idx, priority, sched_idx, i;
- char sched_name[TM_NAME_LEN];
-
- odp_tm_sched_params_init(&sched_params);
-
- for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) {
- snprintf(sched_name, sizeof(sched_name),
- "sched_profile_%" PRIu32, idx);
- for (priority = 0; priority < 16; priority++) {
- sched_params.sched_modes[priority] =
- ODP_TM_BYTE_BASED_WEIGHTS;
- sched_params.sched_weights[priority] = 8 + idx +
- priority;
- }
-
- profile = odp_tm_sched_create(sched_name, &sched_params);
- CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
-
- /* Make sure profile handle is unique */
- for (i = 1; i < idx - 1; i++)
- CU_ASSERT(profile != sched_profiles[i - 1]);
-
- sched_profiles[idx - 1] = profile;
- num_sched_profiles++;
- }
-
- /* Now test odp_tm_sched_lookup */
- for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) {
- /* The following equation is designed is somewhat randomize
- * the lookup of the profiles to catch any implementations
- * taking shortcuts. */
- sched_idx = ((3 + 7 * idx) % NUM_SCHED_TEST_PROFILES) + 1;
- snprintf(sched_name, sizeof(sched_name),
- "sched_profile_%" PRIu32, sched_idx);
- check_sched_profile(sched_name, sched_idx);
- }
-}
-
-static void check_threshold_profile(char *threshold_name,
- uint32_t threshold_idx)
-{
- odp_tm_threshold_params_t threshold_params;
- odp_tm_threshold_t profile;
-
- profile = odp_tm_thresholds_lookup(threshold_name);
- CU_ASSERT(profile != ODP_TM_INVALID);
- CU_ASSERT(profile == threshold_profiles[threshold_idx - 1]);
-
- if (profile == threshold_profiles[threshold_idx - 1])
- return;
-
- odp_tm_thresholds_params_read(profile, &threshold_params);
- CU_ASSERT(threshold_params.max_pkts ==
- threshold_idx * MIN_PKT_THRESHOLD);
- CU_ASSERT(threshold_params.max_bytes ==
- threshold_idx * MIN_BYTE_THRESHOLD);
- CU_ASSERT(threshold_params.enable_max_pkts == 1);
- CU_ASSERT(threshold_params.enable_max_bytes == 1);
-}
-
-void traffic_mngr_test_threshold_profile(void)
-{
- odp_tm_threshold_params_t threshold_params;
- odp_tm_threshold_t profile;
- uint32_t idx, threshold_idx, i;
- char threshold_name[TM_NAME_LEN];
-
- odp_tm_threshold_params_init(&threshold_params);
- threshold_params.enable_max_pkts = 1;
- threshold_params.enable_max_bytes = 1;
-
- for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) {
- snprintf(threshold_name, sizeof(threshold_name),
- "threshold_profile_%" PRIu32, idx);
- threshold_params.max_pkts = idx * MIN_PKT_THRESHOLD;
- threshold_params.max_bytes = idx * MIN_BYTE_THRESHOLD;
-
- profile = odp_tm_threshold_create(threshold_name,
- &threshold_params);
- CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
-
- /* Make sure profile handle is unique */
- for (i = 1; i < idx - 1; i++)
- CU_ASSERT(profile != threshold_profiles[i - 1]);
-
- threshold_profiles[idx - 1] = profile;
- num_threshold_profiles++;
- }
-
- /* Now test odp_tm_threshold_lookup */
- for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) {
- /* The following equation is designed is somewhat randomize
- * the lookup of the profiles to catch any implementations
- * taking shortcuts. */
- threshold_idx = ((3 + 7 * idx) % NUM_THRESH_TEST_PROFILES) + 1;
- snprintf(threshold_name, sizeof(threshold_name),
- "threshold_profile_%" PRIu32, threshold_idx);
- check_threshold_profile(threshold_name, threshold_idx);
- }
-}
-
-static void check_wred_profile(char *wred_name,
- uint32_t wred_idx,
- uint32_t color)
-{
- odp_tm_wred_params_t wred_params;
- odp_tm_wred_t profile;
-
- profile = odp_tm_wred_lookup(wred_name);
- CU_ASSERT(profile != ODP_TM_INVALID);
- CU_ASSERT(profile == wred_profiles[wred_idx - 1][color]);
- if (profile != wred_profiles[wred_idx - 1][color])
- return;
-
- odp_tm_wred_params_read(profile, &wred_params);
- CU_ASSERT(wred_params.min_threshold == wred_idx * MIN_WRED_THRESH);
- CU_ASSERT(wred_params.med_threshold == wred_idx * MED_WRED_THRESH);
- CU_ASSERT(wred_params.med_drop_prob == wred_idx * MED_DROP_PROB);
- CU_ASSERT(wred_params.max_drop_prob == wred_idx * MAX_DROP_PROB);
-
- CU_ASSERT(wred_params.enable_wred == 1);
- CU_ASSERT(wred_params.use_byte_fullness == 0);
-}
-
-void traffic_mngr_test_wred_profile(void)
-{
- odp_tm_wred_params_t wred_params;
- odp_tm_wred_t profile;
- uint32_t idx, color, wred_idx, i, c;
- char wred_name[TM_NAME_LEN];
-
- odp_tm_wred_params_init(&wred_params);
- wred_params.enable_wred = 1;
- wred_params.use_byte_fullness = 0;
-
- for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) {
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- snprintf(wred_name, sizeof(wred_name),
- "wred_profile_%" PRIu32 "_%" PRIu32,
- idx, color);
- wred_params.min_threshold = idx * MIN_WRED_THRESH;
- wred_params.med_threshold = idx * MED_WRED_THRESH;
- wred_params.med_drop_prob = idx * MED_DROP_PROB;
- wred_params.max_drop_prob = idx * MAX_DROP_PROB;
-
- profile = odp_tm_wred_create(wred_name, &wred_params);
- CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
-
- /* Make sure profile handle is unique */
- for (i = 1; i < idx - 1; i++)
- for (c = 0; c < ODP_NUM_PKT_COLORS; c++)
- CU_ASSERT(profile !=
- wred_profiles[i - 1][c]);
-
- wred_profiles[idx - 1][color] = profile;
- }
-
- num_wred_profiles++;
- }
-
- /* Now test odp_tm_wred_lookup */
- for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) {
- /* The following equation is designed is somewhat randomize
- * the lookup of the profiles to catch any implementations
- * taking shortcuts. */
- wred_idx = ((3 + 7 * idx) % NUM_WRED_TEST_PROFILES) + 1;
-
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- snprintf(wred_name, sizeof(wred_name),
- "wred_profile_%" PRIu32 "_%" PRIu32,
- wred_idx, color);
- check_wred_profile(wred_name, wred_idx, color);
- }
- }
-}
-
-static int set_shaper(const char *node_name,
- const char *shaper_name,
- const uint64_t commit_bps,
- const uint64_t commit_burst_in_bits)
-{
- odp_tm_shaper_params_t shaper_params;
- odp_tm_shaper_t shaper_profile;
- odp_tm_node_t tm_node;
-
- tm_node = find_tm_node(0, node_name);
- if (tm_node == ODP_TM_INVALID) {
- LOG_ERR("find_tm_node(%s) failed\n", node_name);
- CU_ASSERT_FATAL(tm_node != ODP_TM_INVALID);
- return -1;
- }
-
- odp_tm_shaper_params_init(&shaper_params);
- shaper_params.commit_bps = commit_bps;
- shaper_params.peak_bps = 0;
- shaper_params.commit_burst = commit_burst_in_bits;
- shaper_params.peak_burst = 0;
- shaper_params.shaper_len_adjust = 0;
- shaper_params.dual_rate = 0;
-
- /* First see if a shaper profile already exists with this name, in
- * which case we use that profile, else create a new one. */
- shaper_profile = odp_tm_shaper_lookup(shaper_name);
- if (shaper_profile != ODP_TM_INVALID) {
- odp_tm_shaper_params_update(shaper_profile, &shaper_params);
- } else {
- shaper_profile = odp_tm_shaper_create(shaper_name,
- &shaper_params);
- shaper_profiles[num_shaper_profiles] = shaper_profile;
- num_shaper_profiles++;
- }
-
- return odp_tm_node_shaper_config(tm_node, shaper_profile);
-}
-
-int traffic_mngr_check_shaper(void)
-{
- odp_cpumask_t cpumask;
- int cpucount = odp_cpumask_all_available(&cpumask);
-
- if (cpucount < 2) {
- LOG_DBG("\nSkipping shaper test because cpucount = %d "
- "is less then min number 2 required\n", cpucount);
- LOG_DBG("Rerun with more cpu resources\n");
- return ODP_TEST_INACTIVE;
- }
-
- return ODP_TEST_ACTIVE;
-}
-
-int traffic_mngr_check_scheduler(void)
-{
- odp_cpumask_t cpumask;
- int cpucount = odp_cpumask_all_available(&cpumask);
-
- if (cpucount < 2) {
- LOG_DBG("\nSkipping scheduler test because cpucount = %d "
- "is less then min number 2 required\n", cpucount);
- LOG_DBG("Rerun with more cpu resources\n");
- return ODP_TEST_INACTIVE;
- }
-
- return ODP_TEST_ACTIVE;
-}
-
-static int test_shaper_bw(const char *shaper_name,
- const char *node_name,
- uint8_t priority,
- uint64_t commit_bps)
-{
- odp_tm_queue_t tm_queue;
- rcv_stats_t rcv_stats;
- pkt_info_t pkt_info;
- uint64_t expected_rcv_gap_us;
- uint32_t num_pkts, pkt_len, pkts_rcvd_in_order, avg_rcv_gap;
- uint32_t min_rcv_gap, max_rcv_gap, pkts_sent;
- int rc, ret_code;
-
- /* This test can support a commit_bps from 64K to 2 Gbps and possibly
- * up to a max of 10 Gbps, but no higher. */
- CU_ASSERT_FATAL(commit_bps <= (10ULL * 1000000000ULL));
-
- /* Pick a tm_queue and set the parent node's shaper BW to be commit_bps
- * with a small burst tolerance. Then send the traffic with a pkt_len
- * such that the pkt start time to next pkt start time is 10,000 bit
- * times and then measure the average inter-arrival receive "gap" in
- * microseconds. */
- tm_queue = find_tm_queue(0, node_name, priority);
- if (set_shaper(node_name, shaper_name, commit_bps, 10000) != 0)
- return -1;
-
- init_xmt_pkts(&pkt_info);
- num_pkts = 50;
- pkt_len = (10000 / 8) - (ETHERNET_OVHD_LEN + CRC_LEN);
- pkt_info.pkt_class = 1;
- if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0)
- return -1;
-
- pkts_sent = send_pkts(tm_queue, num_pkts);
-
- /* The expected inter arrival receive gap in seconds is equal to
- * "10,000 bits / commit_bps". To get the gap time in microseconds
- * we multiply this by one million. The timeout we use is 50 times
- * this gap time (since we send 50 pkts) multiplied by 4 to be
- * conservative, plus a constant time of 1 millisecond to account for
- * testing delays. This then needs to be expressed in nanoseconds by
- * multiplying by 1000. */
- expected_rcv_gap_us = (1000000ULL * 10000ULL) / commit_bps;
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
- commit_bps);
- pkts_rcvd_in_order = pkts_rcvd_in_send_order();
- ret_code = -1;
-
- /* First verify that MOST of the pkts were received in any order. */
- if (num_rcv_pkts <= (pkts_sent / 2)) {
- /* This is fairly major failure in that most of the pkts didn't
- * even get received, regardless of rate or order. Log the error
- * to assist with debugging */
- LOG_ERR("Sent %" PRIu32 " pkts but only %" PRIu32 " "
- "came back\n", pkts_sent, num_rcv_pkts);
- CU_ASSERT(num_rcv_pkts <= (pkts_sent / 2));
- } else if (pkts_rcvd_in_order <= 32) {
- LOG_ERR("Sent %" PRIu32 " pkts but only %" PRIu32 " "
- "came back (%" PRIu32 " in order)\n",
- pkts_sent, num_rcv_pkts, pkts_rcvd_in_order);
- CU_ASSERT(pkts_rcvd_in_order <= 32);
- } else {
- if (pkts_rcvd_in_order < pkts_sent)
- LOG_DBG("Info: of %" PRIu32 " pkts sent %" PRIu32 " "
- "came back (%" PRIu32 " in order)\n",
- pkts_sent, num_rcv_pkts, pkts_rcvd_in_order);
-
- /* Next determine the inter arrival receive pkt statistics. */
- rc = rcv_rate_stats(&rcv_stats, pkt_info.pkt_class);
- CU_ASSERT(rc == 0);
-
- /* Next verify that the rcvd pkts have an average inter-receive
- * gap of "expected_rcv_gap_us" microseconds, +/- 25%. */
- avg_rcv_gap = rcv_stats.avg_rcv_gap;
- min_rcv_gap = ((MIN_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) /
- 100) - 2;
- max_rcv_gap = ((MAX_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) /
- 100) + 2;
- if ((avg_rcv_gap < min_rcv_gap) ||
- (max_rcv_gap < avg_rcv_gap)) {
- LOG_ERR("min=%" PRIu32 " avg_rcv_gap=%" PRIu32 " "
- "max=%" PRIu32 " std_dev_gap=%" PRIu32 "\n",
- rcv_stats.min_rcv_gap, avg_rcv_gap,
- rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap);
- LOG_ERR(" expected_rcv_gap=%" PRIu64 " acceptable "
- "rcv_gap range=%" PRIu32 "..%" PRIu32 "\n",
- expected_rcv_gap_us, min_rcv_gap, max_rcv_gap);
- } else if (expected_rcv_gap_us < rcv_stats.std_dev_gap) {
- LOG_ERR("min=%" PRIu32 " avg_rcv_gap=%" PRIu32 " "
- "max=%" PRIu32 " std_dev_gap=%" PRIu32 "\n",
- rcv_stats.min_rcv_gap, avg_rcv_gap,
- rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap);
- LOG_ERR(" expected_rcv_gap=%" PRIu64 " acceptable "
- "rcv_gap range=%" PRIu32 "..%" PRIu32 "\n",
- expected_rcv_gap_us, min_rcv_gap, max_rcv_gap);
- ret_code = 0;
- } else {
- ret_code = 0;
- }
-
- CU_ASSERT((min_rcv_gap <= avg_rcv_gap) &&
- (avg_rcv_gap <= max_rcv_gap));
- CU_ASSERT(rcv_stats.std_dev_gap <= expected_rcv_gap_us);
- }
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return ret_code;
-}
-
-static int set_sched_fanin(const char *node_name,
- const char *sched_base_name,
- odp_tm_sched_mode_t sched_mode,
- uint8_t sched_weights[FANIN_RATIO])
-{
- odp_tm_sched_params_t sched_params;
- odp_tm_sched_t sched_profile;
- tm_node_desc_t *node_desc, *child_desc;
- odp_tm_node_t tm_node, fanin_node;
- uint32_t fanin_cnt, fanin, priority;
- uint8_t sched_weight;
- char sched_name[TM_NAME_LEN];
- int rc;
-
- node_desc = find_node_desc(0, node_name);
- if (node_desc == NULL)
- return -1;
-
- fanin_cnt = MIN(node_desc->num_children, FANIN_RATIO);
- for (fanin = 0; fanin < fanin_cnt; fanin++) {
- odp_tm_sched_params_init(&sched_params);
- sched_weight = sched_weights[fanin];
-
- /* Set the weights and mode the same for all priorities */
- for (priority = 0; priority < NUM_PRIORITIES; priority++) {
- sched_params.sched_modes[priority] = sched_mode;
- sched_params.sched_weights[priority] = sched_weight;
- }
-
- /* Create the scheduler profile name using the sched_base_name
- * and the fanin index */
- snprintf(sched_name, sizeof(sched_name), "%s_%" PRIu32,
- sched_base_name, fanin);
-
- /* First see if a sched profile already exists with this name,
- * in which case we use that profile, else create a new one. */
- sched_profile = odp_tm_sched_lookup(sched_name);
- if (sched_profile != ODP_TM_INVALID) {
- odp_tm_sched_params_update(sched_profile,
- &sched_params);
- } else {
- sched_profile = odp_tm_sched_create(sched_name,
- &sched_params);
- sched_profiles[num_sched_profiles] = sched_profile;
- num_sched_profiles++;
- }
-
- /* Apply the weights to the nodes fan-in. */
- child_desc = node_desc->children[fanin];
- tm_node = node_desc->node;
- fanin_node = child_desc->node;
- rc = odp_tm_node_sched_config(tm_node, fanin_node,
- sched_profile);
- if (rc != 0)
- return -1;
- }
-
- return 0;
-}
-
-static int test_sched_queue_priority(const char *shaper_name,
- const char *node_name,
- uint32_t num_pkts)
-{
- odp_tm_queue_t tm_queues[NUM_PRIORITIES];
- pkt_info_t pkt_info;
- uint32_t pkt_cnt, pkts_in_order, base_idx;
- uint32_t idx, unique_id, pkt_len, base_pkt_len, pkts_sent;
- int priority;
-
- memset(unique_id_list, 0, sizeof(unique_id_list));
- for (priority = 0; priority < NUM_PRIORITIES; priority++)
- tm_queues[priority] = find_tm_queue(0, node_name, priority);
-
- /* Enable the shaper to be low bandwidth. */
- pkt_len = 1400;
- set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len);
-
- /* Make a couple of low priority dummy pkts first. */
- init_xmt_pkts(&pkt_info);
- if (make_pkts(4, pkt_len, &pkt_info) != 0)
- return -1;
-
- /* Now make "num_pkts" first at the lowest priority, then "num_pkts"
- * at the second lowest priority, etc until "num_pkts" are made last
- * at the highest priority (which is always priority 0). */
- pkt_cnt = NUM_PRIORITIES * num_pkts;
- base_pkt_len = 256;
- for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
- unique_id = cpu_unique_id;
- pkt_info.pkt_class = priority + 1;
- pkt_len = base_pkt_len + 64 * priority;
- if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0)
- return -1;
-
- base_idx = priority * num_pkts;
- for (idx = 0; idx < num_pkts; idx++)
- unique_id_list[base_idx + idx] = unique_id++;
- }
-
- /* Send the low priority dummy pkts first. The arrival order of
- * these pkts will be ignored. */
- pkts_sent = send_pkts(tm_queues[NUM_PRIORITIES - 1], 4);
-
- /* Now send "num_pkts" first at the lowest priority, then "num_pkts"
- * at the second lowest priority, etc until "num_pkts" are sent last
- * at the highest priority. */
- for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--)
- pkts_sent += send_pkts(tm_queues[priority], num_pkts);
-
- busy_wait(1000000); /* wait 1 millisecond */
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
-
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
- pkt_cnt + 4, 64 * 1000);
-
- /* Check rcvd packet arrivals to make sure that pkts arrived in
- * priority order, except for perhaps the first few lowest priority
- * dummy pkts. */
- pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, pkt_cnt, 0,
- false, false);
- if (pkts_in_order != pkt_cnt) {
- LOG_ERR("pkts_sent=%" PRIu32 " pkt_cnt=%" PRIu32 " "
- "num_rcv_pkts=%" PRIu32 " rcvd_in_order=%" PRIu32 "\n",
- pkts_sent, pkt_cnt, num_rcv_pkts, pkts_in_order);
- }
-
- CU_ASSERT(pkts_in_order == pkt_cnt);
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return 0;
-}
-
-static int test_sched_node_priority(const char *shaper_name,
- const char *node_name,
- uint32_t num_pkts)
-{
- odp_tm_queue_t *tm_queues, tm_queue;
- tm_node_desc_t *node_desc;
- queue_array_t *queue_array;
- pkt_info_t pkt_info;
- uint32_t total_num_queues, max_queues, num_queues, pkt_cnt;
- uint32_t pkts_in_order, base_idx, queue_idx, idx, unique_id;
- uint32_t pkt_len, base_pkt_len, total_pkt_cnt, pkts_sent;
- int priority;
-
- memset(unique_id_list, 0, sizeof(unique_id_list));
- node_desc = find_node_desc(0, node_name);
- if (node_desc == NULL)
- return -1;
-
- total_num_queues = 0;
- for (priority = 0; priority < NUM_PRIORITIES; priority++) {
- max_queues = NUM_LEVEL2_TM_NODES;
- queue_array = &queues_set.queue_array[priority];
- tm_queues = queue_array->tm_queues;
- num_queues = find_child_queues(0, node_desc, priority,
- tm_queues, max_queues);
- queue_array->num_queues = num_queues;
- queue_array->priority = priority;
- total_num_queues += num_queues;
- }
-
- /* Enable the shaper to be low bandwidth. */
- pkt_len = 1400;
- set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len);
-
- /* Make a couple of low priority large dummy pkts first. */
- init_xmt_pkts(&pkt_info);
- if (make_pkts(4, pkt_len, &pkt_info) != 0)
- return -1;
-
- /* Now make "num_pkts" for each tm_queue at the lowest priority, then
- * "num_pkts" for each tm_queue at the second lowest priority, etc.
- * until "num_pkts" for each tm_queue at the highest priority are made
- * last. Note that the highest priority is always priority 0. */
- total_pkt_cnt = total_num_queues * num_pkts;
- base_pkt_len = 256;
- base_idx = 0;
- for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
- unique_id = cpu_unique_id;
- queue_array = &queues_set.queue_array[priority];
- num_queues = queue_array->num_queues;
- pkt_cnt = num_queues * num_pkts;
- pkt_info.pkt_class = priority + 1;
- pkt_len = base_pkt_len + 64 * priority;
- if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
- return -1;
-
- base_idx = priority * num_pkts;
- for (idx = 0; idx < pkt_cnt; idx++)
- unique_id_list[base_idx + idx] = unique_id++;
- }
-
- /* Send the low priority dummy pkts first. The arrival order of
- * these pkts will be ignored. */
- queue_array = &queues_set.queue_array[NUM_PRIORITIES - 1];
- tm_queue = queue_array->tm_queues[0];
- pkts_sent = send_pkts(tm_queue, 4);
-
- /* Now send "num_pkts" for each tm_queue at the lowest priority, then
- * "num_pkts" for each tm_queue at the second lowest priority, etc.
- * until "num_pkts" for each tm_queue at the highest priority are sent
- * last. */
- for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
- queue_array = &queues_set.queue_array[priority];
- num_queues = queue_array->num_queues;
- for (queue_idx = 0; queue_idx < num_queues; queue_idx++) {
- tm_queue = queue_array->tm_queues[queue_idx];
- pkts_sent += send_pkts(tm_queue, num_pkts);
- }
- }
-
- busy_wait(1000000); /* wait 1 millisecond */
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
-
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
- pkts_sent, 64 * 1000);
-
- /* Check rcvd packet arrivals to make sure that pkts arrived in
- * priority order, except for perhaps the first few lowest priority
- * dummy pkts. */
- pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, total_pkt_cnt,
- 0, false, false);
- CU_ASSERT(pkts_in_order == total_pkt_cnt);
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return 0;
-}
-
-static int test_sched_wfq(const char *sched_base_name,
- const char *shaper_name,
- const char *node_name,
- odp_tm_sched_mode_t sched_mode,
- uint8_t sched_weights[FANIN_RATIO])
-{
- odp_tm_queue_t tm_queues[FANIN_RATIO], tm_queue;
- tm_node_desc_t *node_desc, *child_desc;
- rcv_stats_t rcv_stats[FANIN_RATIO];
- pkt_info_t pkt_info;
- uint32_t fanin_cnt, fanin, num_queues, pkt_cnt;
- uint32_t pkt_len, pkts_sent, pkt_idx;
- uint8_t pkt_class;
- int priority, rc;
-
- memset(tm_queues, 0, sizeof(tm_queues));
- node_desc = find_node_desc(0, node_name);
- if (node_desc == NULL)
- return -1;
-
- rc = set_sched_fanin(node_name, sched_base_name, sched_mode,
- sched_weights);
- if (rc != 0)
- return -1;
-
- /* Now determine at least one tm_queue that feeds into each fanin/
- * child node. */
- priority = 0;
- fanin_cnt = MIN(node_desc->num_children, FANIN_RATIO);
- for (fanin = 0; fanin < fanin_cnt; fanin++) {
- child_desc = node_desc->children[fanin];
- num_queues = find_child_queues(0, child_desc, priority,
- &tm_queues[fanin], 1);
- if (num_queues != 1)
- return -1;
- }
-
- /* Enable the shaper to be low bandwidth. */
- pkt_len = 1400;
- set_shaper(node_name, shaper_name, 64 * 1000, 8 * pkt_len);
-
- /* Make a couple of low priority dummy pkts first. */
- init_xmt_pkts(&pkt_info);
- if (make_pkts(4, pkt_len, &pkt_info) != 0)
- return -1;
-
- /* Make 100 pkts for each fanin of this node, alternating amongst
- * the inputs. */
- pkt_cnt = FANIN_RATIO * 100;
- fanin = 0;
- for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
- pkt_len = 128 + 128 * fanin;
- pkt_info.pkt_class = 1 + fanin++;
- if (make_pkts(1, pkt_len, &pkt_info) != 0)
- return -1;
-
- if (FANIN_RATIO <= fanin)
- fanin = 0;
- }
-
- /* Send the low priority dummy pkts first. The arrival order of
- * these pkts will be ignored. */
- pkts_sent = send_pkts(tm_queues[NUM_PRIORITIES - 1], 4);
-
- /* Now send the test pkts, alternating amongst the input queues. */
- fanin = 0;
- for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
- tm_queue = tm_queues[fanin++];
- pkts_sent += send_pkts(tm_queue, 1);
- if (FANIN_RATIO <= fanin)
- fanin = 0;
- }
-
- busy_wait(1000000); /* wait 1 millisecond */
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
-
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
- pkt_cnt + 4, 64 * 1000);
-
- /* Check rcvd packet arrivals to make sure that pkts arrived in
- * an order commensurate with their weights, sched mode and pkt_len. */
- for (fanin = 0; fanin < fanin_cnt; fanin++) {
- pkt_class = 1 + fanin;
- CU_ASSERT(rcv_rate_stats(&rcv_stats[fanin], pkt_class) == 0);
- }
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return 0;
-}
-
-static int set_queue_thresholds(odp_tm_queue_t tm_queue,
- const char *threshold_name,
- odp_tm_threshold_params_t *threshold_params)
-{
- odp_tm_threshold_t threshold_profile;
-
- /* First see if a threshold profile already exists with this name, in
- * which case we use that profile, else create a new one. */
- threshold_profile = odp_tm_thresholds_lookup(threshold_name);
- if (threshold_profile != ODP_TM_INVALID) {
- odp_tm_thresholds_params_update(threshold_profile,
- threshold_params);
- } else {
- threshold_profile = odp_tm_threshold_create(threshold_name,
- threshold_params);
- threshold_profiles[num_threshold_profiles] = threshold_profile;
- num_threshold_profiles++;
- }
-
- return odp_tm_queue_threshold_config(tm_queue, threshold_profile);
-}
-
-static int test_threshold(const char *threshold_name,
- const char *shaper_name,
- const char *node_name,
- uint8_t priority,
- uint32_t max_pkts,
- uint32_t max_bytes)
-{
- odp_tm_threshold_params_t threshold_params;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- uint32_t num_pkts, pkt_len, pkts_sent;
-
- odp_tm_threshold_params_init(&threshold_params);
- if (max_pkts != 0) {
- max_pkts = MIN(max_pkts, MAX_PKTS / 3);
- threshold_params.max_pkts = max_pkts;
- threshold_params.enable_max_pkts = true;
- num_pkts = 2 * max_pkts;
- pkt_len = 256;
- } else if (max_bytes != 0) {
- max_bytes = MIN(max_bytes, MAX_PKTS * MAX_PAYLOAD / 3);
- threshold_params.max_bytes = max_bytes;
- threshold_params.enable_max_bytes = true;
- num_pkts = 2 * max_bytes / MAX_PAYLOAD;
- pkt_len = MAX_PAYLOAD;
- } else {
- return -1;
- }
-
- /* Pick a tm_queue and set the tm_queue's threshold profile and then
- * send in twice the amount of traffic as suggested by the thresholds
- * and make sure at least SOME pkts get dropped. */
- tm_queue = find_tm_queue(0, node_name, priority);
- if (set_queue_thresholds(tm_queue, threshold_name,
- &threshold_params) != 0) {
- LOG_ERR("set_queue_thresholds failed\n");
- return -1;
- }
-
- /* Enable the shaper to be very low bandwidth. */
- set_shaper(node_name, shaper_name, 256 * 1000, 8 * pkt_len);
-
- init_xmt_pkts(&pkt_info);
- pkt_info.drop_eligible = true;
- pkt_info.pkt_class = 1;
- if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0) {
- LOG_ERR("make_pkts failed\n");
- return -1;
- }
-
- pkts_sent = send_pkts(tm_queue, num_pkts);
-
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
- 1 * GBPS);
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
-
- if (num_rcv_pkts < num_pkts)
- return 0;
-
- CU_ASSERT(num_rcv_pkts < pkts_sent);
- return 0;
-}
-
-static wred_pkt_cnts_t *search_expected_pkt_rcv_tbl(odp_tm_percent_t confidence,
- odp_tm_percent_t drop_perc)
-{
- wred_pkt_cnts_t *wred_pkt_cnts;
- uint32_t idx, table_size;
-
- /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
- table_size = sizeof(EXPECTED_PKT_RCVD) / sizeof(wred_pkt_cnts_t);
- for (idx = 0; idx < table_size; idx++) {
- wred_pkt_cnts = &EXPECTED_PKT_RCVD[idx];
- if ((wred_pkt_cnts->confidence_percent == confidence) &&
- (wred_pkt_cnts->drop_percent == drop_perc))
- return wred_pkt_cnts;
- }
-
- return NULL;
-}
-
-static int set_queue_wred(odp_tm_queue_t tm_queue,
- const char *wred_name,
- uint8_t pkt_color,
- odp_tm_percent_t drop_percent,
- odp_bool_t use_byte_fullness,
- odp_bool_t use_dual_slope)
-{
- odp_tm_wred_params_t wred_params;
- odp_tm_wred_t wred_profile;
-
- odp_tm_wred_params_init(&wred_params);
- if (use_dual_slope) {
- wred_params.min_threshold = TM_PERCENT(20);
- wred_params.med_threshold = TM_PERCENT(40);
- wred_params.med_drop_prob = drop_percent;
- wred_params.max_drop_prob = drop_percent;
- } else {
- wred_params.min_threshold = 0;
- wred_params.med_threshold = TM_PERCENT(20);
- wred_params.med_drop_prob = 0;
- wred_params.max_drop_prob = 2 * drop_percent;
- }
-
- wred_params.enable_wred = true;
- wred_params.use_byte_fullness = use_byte_fullness;
-
- /* First see if a wred profile already exists with this name, in
- * which case we use that profile, else create a new one. */
- wred_profile = odp_tm_wred_lookup(wred_name);
- if (wred_profile != ODP_TM_INVALID) {
- odp_tm_wred_params_update(wred_profile, &wred_params);
- } else {
- wred_profile = odp_tm_wred_create(wred_name, &wred_params);
- if (wred_profiles[num_wred_profiles - 1][pkt_color] ==
- ODP_TM_INVALID) {
- wred_profiles[num_wred_profiles - 1][pkt_color] =
- wred_profile;
- } else {
- wred_profiles[num_wred_profiles][pkt_color] =
- wred_profile;
- num_wred_profiles++;
- }
- }
-
- return odp_tm_queue_wred_config(tm_queue, pkt_color, wred_profile);
-}
-
-static int test_byte_wred(const char *wred_name,
- const char *shaper_name,
- const char *threshold_name,
- const char *node_name,
- uint8_t priority,
- uint8_t pkt_color,
- odp_tm_percent_t drop_percent,
- odp_bool_t use_dual_slope)
-{
- odp_tm_threshold_params_t threshold_params;
- wred_pkt_cnts_t *wred_pkt_cnts;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- uint32_t num_fill_pkts, num_test_pkts, pkts_sent;
-
- /* Pick the tm_queue and set the tm_queue's wred profile to drop the
- * given percentage of traffic, then send 100 pkts and see how many
- * pkts are received. */
- tm_queue = find_tm_queue(0, node_name, priority);
- set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent,
- true, use_dual_slope);
-
- /* Enable the shaper to be very low bandwidth. */
- set_shaper(node_name, shaper_name, 64 * 1000, 8 * PKT_BUF_SIZE);
-
- /* Set the threshold to be byte based and to handle 200 pkts of
- * size PKT_BUF_SIZE. This way the byte-fullness for the wred test
- * pkts will be around 60%. */
- odp_tm_threshold_params_init(&threshold_params);
- threshold_params.max_bytes = 200 * PKT_BUF_SIZE;
- threshold_params.enable_max_bytes = true;
- if (set_queue_thresholds(tm_queue, threshold_name,
- &threshold_params) != 0) {
- LOG_ERR("set_queue_thresholds failed\n");
- return -1;
- }
-
- /* Make and send the first batch of pkts whose job is to set the
- * queue byte fullness to around 60% for the subsequent test packets.
- * These packets MUST have drop_eligible false. */
- init_xmt_pkts(&pkt_info);
- num_fill_pkts = 120;
- pkt_info.pkt_color = pkt_color;
- pkt_info.pkt_class = 0;
- pkt_info.drop_eligible = false;
- if (make_pkts(num_fill_pkts, PKT_BUF_SIZE, &pkt_info) != 0)
- return -1;
-
- send_pkts(tm_queue, num_fill_pkts);
-
- /* Now send the real test pkts, which are all small so as to try to
- * keep the byte fullness still close to the 60% point. These pkts
- * MUST have drop_eligible true. */
- num_test_pkts = 100;
- pkt_info.pkt_class = 1;
- pkt_info.drop_eligible = true;
- if (make_pkts(num_test_pkts, 128, &pkt_info) != 0)
- return -1;
-
- pkts_sent = send_pkts(tm_queue, num_test_pkts);
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
- num_fill_pkts + pkts_sent, 64 * 1000);
-
- /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
- wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9),
- drop_percent);
- if (wred_pkt_cnts == NULL)
- return -1;
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
-
- if ((wred_pkt_cnts->min_cnt <= pkts_sent) &&
- (pkts_sent <= wred_pkt_cnts->max_cnt))
- return 0;
-
- CU_ASSERT((wred_pkt_cnts->min_cnt <= pkts_sent) &&
- (pkts_sent <= wred_pkt_cnts->max_cnt));
- return 0;
-}
-
-static int test_pkt_wred(const char *wred_name,
- const char *shaper_name,
- const char *threshold_name,
- const char *node_name,
- uint8_t priority,
- uint8_t pkt_color,
- odp_tm_percent_t drop_percent,
- odp_bool_t use_dual_slope)
-{
- odp_tm_threshold_params_t threshold_params;
- wred_pkt_cnts_t *wred_pkt_cnts;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- uint32_t num_fill_pkts, num_test_pkts, pkts_sent;
-
- /* Pick the tm_queue and set the tm_queue's wred profile to drop the
- * given percentage of traffic, then send 100 pkts and see how many
- * pkts are received. */
- tm_queue = find_tm_queue(0, node_name, priority);
- set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent,
- false, use_dual_slope);
-
- /* Enable the shaper to be very low bandwidth. */
- set_shaper(node_name, shaper_name, 64 * 1000, 1000);
-
- /* Set the threshold to be pkt based and to handle 1000 pkts. This
- * way the pkt-fullness for the wred test pkts will be around 60%. */
- odp_tm_threshold_params_init(&threshold_params);
- threshold_params.max_pkts = 1000;
- threshold_params.enable_max_pkts = true;
- if (set_queue_thresholds(tm_queue, threshold_name,
- &threshold_params) != 0) {
- LOG_ERR("set_queue_thresholds failed\n");
- return -1;
- }
-
- /* Make and send the first batch of pkts whose job is to set the
- * queue pkt fullness to around 60% for the subsequent test packets.
- * These packets MUST have drop_eligible false. */
- init_xmt_pkts(&pkt_info);
- num_fill_pkts = 600;
- pkt_info.pkt_color = pkt_color;
- pkt_info.pkt_class = 0;
- pkt_info.drop_eligible = false;
- if (make_pkts(num_fill_pkts, 80, &pkt_info) != 0)
- return -1;
-
- send_pkts(tm_queue, num_fill_pkts);
-
- /* Now send the real test pkts. These pkts MUST have drop_eligible
- * true. */
- num_test_pkts = 100;
- pkt_info.pkt_class = 1;
- pkt_info.drop_eligible = true;
- if (make_pkts(num_test_pkts, 80, &pkt_info) != 0)
- return -1;
-
- pkts_sent = send_pkts(tm_queue, num_test_pkts);
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
- num_fill_pkts + pkts_sent, 64 * 1000);
-
- /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
- wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9),
- drop_percent);
- if (wred_pkt_cnts == NULL)
- return -1;
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
-
- if ((wred_pkt_cnts->min_cnt <= pkts_sent) &&
- (pkts_sent <= wred_pkt_cnts->max_cnt))
- return 0;
-
- CU_ASSERT((wred_pkt_cnts->min_cnt <= pkts_sent) &&
- (pkts_sent <= wred_pkt_cnts->max_cnt));
- return 0;
-}
-
-static int test_query_functions(const char *shaper_name,
- const char *node_name,
- uint8_t priority,
- uint32_t num_pkts)
-{
- odp_tm_query_info_t query_info;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- uint64_t commit_bps, expected_pkt_cnt, expected_byte_cnt;
- int rc;
-
- /* Pick a tm_queue and set the egress node's shaper BW to be 64K bps
- * with a small burst tolerance. Then send the traffic. */
- tm_queue = find_tm_queue(0, node_name, priority);
- commit_bps = 64 * 1000;
- if (set_shaper(node_name, shaper_name, commit_bps, 1000) != 0)
- return -1;
-
- init_xmt_pkts(&pkt_info);
- pkt_info.pkt_class = 1;
- if (make_pkts(num_pkts, PKT_BUF_SIZE, &pkt_info) != 0)
- return -1;
-
- send_pkts(tm_queue, num_pkts);
-
- /* Assume all but 2 of the pkts are still in the queue.*/
- expected_pkt_cnt = num_pkts - 2;
- expected_byte_cnt = expected_pkt_cnt * PKT_BUF_SIZE;
-
- rc = odp_tm_queue_query(tm_queue,
- ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
- &query_info);
- CU_ASSERT(rc == 0);
- CU_ASSERT(query_info.total_pkt_cnt_valid);
- CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
- CU_ASSERT(query_info.total_byte_cnt_valid);
- CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
-
- rc = odp_tm_priority_query(odp_tm_systems[0], priority,
- ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
- &query_info);
- CU_ASSERT(rc == 0);
- CU_ASSERT(query_info.total_pkt_cnt_valid);
- CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
- CU_ASSERT(query_info.total_byte_cnt_valid);
- CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
-
- rc = odp_tm_total_query(odp_tm_systems[0],
- ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
- &query_info);
- CU_ASSERT(rc == 0);
- CU_ASSERT(query_info.total_pkt_cnt_valid);
- CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
- CU_ASSERT(query_info.total_byte_cnt_valid);
- CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
-
- /* Disable the shaper, so as to get the pkts out quicker. */
- set_shaper(node_name, shaper_name, 0, 0);
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, num_pkts,
- commit_bps);
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return 0;
-}
-
-static int check_vlan_marking_pkts(void)
-{
- odp_packet_t rcv_pkt;
- uint32_t rcv_pkt_idx, err_cnt;
- uint16_t tci;
- uint8_t pkt_class, dei, expected_dei;
-
- /* Check rcvd packets to make sure that pkt_class 1 pkts continue to
- * not have a VLAN header, pkt class 2 pkts have a VLAN header with the
- * drop precedence not set and pkt class 3 pkts have a VLAN header with
- * the DEI bit set. */
- err_cnt = 0;
- for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
- rcv_pkt = rcv_pkts[rcv_pkt_idx];
- pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class;
-
- switch (pkt_class) {
- case 1:
- /* Make sure no VLAN header. */
- if (odp_packet_has_vlan(rcv_pkt)) {
- err_cnt++;
- LOG_ERR("VLAN incorrectly added\n");
- CU_ASSERT(odp_packet_has_vlan(rcv_pkt));
- }
- break;
-
- case 2:
- case 3:
- /* Make sure it does have a VLAN header */
- if (!odp_packet_has_vlan(rcv_pkt)) {
- err_cnt++;
- LOG_ERR("VLAN header missing\n");
- CU_ASSERT(!odp_packet_has_vlan(rcv_pkt));
- break;
- }
-
- /* Make sure DEI bit is 0 if pkt_class == 2, and 1 if
- * pkt_class == 3. */
- if (get_vlan_tci(rcv_pkt, &tci) != 0) {
- err_cnt++;
- LOG_ERR("VLAN header missing\n");
- CU_ASSERT(!odp_packet_has_vlan(rcv_pkt));
- break;
- }
-
- dei = (tci >> ODPH_VLANHDR_DEI_SHIFT) & 1;
- expected_dei = (pkt_class == 2) ? 0 : 1;
- if (dei != expected_dei) {
- LOG_ERR("expected_dei=%u rcvd dei=%u\n",
- expected_dei, dei);
- err_cnt++;
- CU_ASSERT(dei == expected_dei);
- }
- break;
-
- default:
- /* Log error but otherwise ignore, since it is
- * probably a stray pkt from a previous test. */
- LOG_ERR("Pkt rcvd with invalid pkt class\n");
- }
- }
-
- return (err_cnt == 0) ? 0 : -1;
-}
-
-static int test_vlan_marking(const char *node_name,
- odp_packet_color_t pkt_color)
-{
- odp_packet_color_t color;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- odp_tm_t odp_tm;
- uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent;
- int rc;
-
- /* First disable vlan marking for all colors. These "disable" calls
- * should NEVER fail. */
- odp_tm = odp_tm_systems[0];
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- rc = odp_tm_vlan_marking(odp_tm, color, false);
- if (rc != 0) {
- LOG_ERR("disabling odp_tm_vlan_marking() failed\n");
- return -1;
- }
- }
-
- /* Next enable vlan marking for just the given color parameter */
- rc = odp_tm_vlan_marking(odp_tm, pkt_color, true);
-
- tm_queue = find_tm_queue(0, node_name, 0);
- if (tm_queue == ODP_TM_INVALID) {
- LOG_ERR("No tm_queue found for node_name='%s'\n", node_name);
- return -1;
- }
-
- /* Next make 2*X pkts of each color, half with vlan headers -
- * half without. */
- init_xmt_pkts(&pkt_info);
-
- pkt_cnt = 5;
- num_pkts = 0;
- pkt_len = 600;
- pkt_info.pkt_class = 1;
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- num_pkts += pkt_cnt;
- pkt_info.pkt_color = color;
- if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
- return -1;
- }
-
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- num_pkts += pkt_cnt;
- pkt_info.pkt_color = color;
- pkt_info.pkt_class = (color == pkt_color) ? 3 : 2;
- pkt_info.use_vlan = true;
- pkt_info.vlan_tci = VLAN_NO_DEI;
- if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
- return -1;
- }
-
- pkts_sent = send_pkts(tm_queue, num_pkts);
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
- 1000 * 1000);
- if (num_rcv_pkts == 0) {
- LOG_ERR("No pkts received\n");
- rc = -1;
- } else if (num_rcv_pkts != pkts_sent) {
- LOG_ERR("pkts_sent=%" PRIu32 " but num_rcv_pkts=%" PRIu32 "\n",
- pkts_sent, num_rcv_pkts);
- dump_rcvd_pkts(0, num_rcv_pkts - 1);
- CU_ASSERT(num_rcv_pkts == pkts_sent);
- } else {
- rc = check_vlan_marking_pkts();
- }
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return rc;
-}
-
-static int check_tos_marking_pkts(odp_bool_t use_ipv6,
- odp_bool_t use_tcp,
- odp_bool_t test_ecn,
- odp_bool_t test_drop_prec,
- uint8_t unmarked_tos,
- uint8_t new_dscp,
- uint8_t dscp_mask)
-{
- odp_packet_t rcv_pkt;
- uint32_t rcv_pkt_idx;
- uint8_t unmarked_ecn, unmarked_dscp, shifted_dscp, pkt_class;
- uint8_t tos, expected_tos;
- int rc;
-
- /* Turn off test_ecn for UDP pkts, since ECN marking should
- * only happen for TCP pkts. */
- if (!use_tcp)
- test_ecn = false;
-
- /* The expected_tos value is only the expected TOS/TC field for pkts
- * that have been enabled for modification, as indicated by the
- * pkt_class associated with this pkt. */
- unmarked_ecn = (unmarked_tos & ODPH_IP_TOS_ECN_MASK)
- >> ODPH_IP_TOS_ECN_SHIFT;
- unmarked_dscp = (unmarked_tos & ODPH_IP_TOS_DSCP_MASK)
- >> ODPH_IP_TOS_DSCP_SHIFT;
- new_dscp = (new_dscp & dscp_mask) | (unmarked_dscp & ~dscp_mask);
- shifted_dscp = new_dscp << ODPH_IP_TOS_DSCP_SHIFT;
-
- if (test_ecn && test_drop_prec)
- expected_tos = shifted_dscp | ODPH_IP_ECN_CE;
- else if (test_ecn)
- expected_tos = unmarked_tos | ODPH_IP_ECN_CE;
- else if (test_drop_prec)
- expected_tos = shifted_dscp | unmarked_ecn;
- else
- expected_tos = unmarked_tos;
-
- for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
- rcv_pkt = rcv_pkts[rcv_pkt_idx];
- pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class;
-
- /* Check that the pkts match the use_ipv6 setting */
- if (use_ipv6)
- rc = odp_packet_has_ipv6(rcv_pkt);
- else
- rc = odp_packet_has_ipv4(rcv_pkt);
-
- if (rc != 1) {
- if (use_ipv6)
- LOG_ERR("Expected IPv6 pkt but got IPv4");
- else
- LOG_ERR("Expected IPv4 pkt but got IPv6");
-
- return -1;
- }
-
- /* Check that the pkts match the use_tcp setting */
- if (use_tcp)
- rc = odp_packet_has_tcp(rcv_pkt);
- else
- rc = odp_packet_has_udp(rcv_pkt);
-
- if (rc != 1) {
- if (use_tcp)
- LOG_ERR("Expected TCP pkt but got UDP");
- else
- LOG_ERR("Expected UDP pkt but got TCP");
-
- return -1;
- }
-
- /* Now get the tos field to see if it was changed */
- rc = get_ip_tos(rcv_pkt, &tos);
- if (rc != 0) {
- LOG_ERR("get_ip_tos failed\n");
- return -1;
- }
-
- switch (pkt_class) {
- case 2:
- /* Tos field must be unchanged. */
- if (unmarked_tos != tos) {
- LOG_ERR("Tos was changed from 0x%X to 0x%X\n",
- unmarked_tos, tos);
- return -1;
- }
- break;
-
- case 3:
- /* Tos field must be changed. */
- if (tos != expected_tos) {
- LOG_ERR("tos=0x%X instead of expected 0x%X\n",
- tos, expected_tos);
- CU_ASSERT(tos == expected_tos);
- }
- break;
-
- default:
- /* Log error but otherwise ignore, since it is
- * probably a stray pkt from a previous test. */
- LOG_ERR("Pkt rcvd with invalid pkt class=%u\n",
- pkt_class);
- }
- }
-
- return 0;
-}
-
-static int test_ip_marking(const char *node_name,
- odp_packet_color_t pkt_color,
- odp_bool_t use_ipv6,
- odp_bool_t use_tcp,
- odp_bool_t test_ecn,
- odp_bool_t test_drop_prec,
- uint8_t new_dscp,
- uint8_t dscp_mask)
-{
- odp_packet_color_t color;
- odp_tm_queue_t tm_queue;
- pkt_info_t pkt_info;
- odp_tm_t odp_tm;
- uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent;
- int rc, ret_code;
-
- /* First disable IP TOS marking for all colors. These "disable" calls
- * should NEVER fail. */
- odp_tm = odp_tm_systems[0];
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- rc = odp_tm_ecn_marking(odp_tm, color, false);
- if (rc != 0) {
- LOG_ERR("disabling odp_tm_ecn_marking() failed\n");
- return -1;
- }
-
- rc = odp_tm_drop_prec_marking(odp_tm, color, false);
- if (rc != 0) {
- LOG_ERR("disabling odp_tm_drop_prec_marking failed\n");
- return -1;
- }
- }
-
- /* Next enable IP TOS marking for just the given color parameter */
- if ((!test_ecn) && (!test_drop_prec))
- return 0;
-
- if (test_ecn) {
- rc = odp_tm_ecn_marking(odp_tm, pkt_color, true);
- if (rc != 0) {
- LOG_ERR("odp_tm_ecn_marking() call failed\n");
- return -1;
- }
- }
-
- if (test_drop_prec) {
- rc = odp_tm_drop_prec_marking(odp_tm, pkt_color, true);
- if (rc != 0) {
- LOG_ERR("odp_tm_drop_prec_marking() call failed\n");
- return -1;
- }
- }
-
- tm_queue = find_tm_queue(0, node_name, 0);
- if (tm_queue == ODP_TM_INVALID) {
- LOG_ERR("No tm_queue found for node_name='%s'\n", node_name);
- return -1;
- }
-
- init_xmt_pkts(&pkt_info);
- pkt_info.use_ipv6 = use_ipv6;
- pkt_info.use_tcp = use_tcp;
- pkt_info.ip_tos = DEFAULT_TOS;
-
- pkt_cnt = 5;
- num_pkts = 0;
- pkt_len = 1340;
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- num_pkts += pkt_cnt;
- pkt_info.pkt_color = color;
- if (test_drop_prec || (test_ecn && use_tcp))
- pkt_info.pkt_class = (color == pkt_color) ? 3 : 2;
- else
- pkt_info.pkt_class = 2;
-
- if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) {
- LOG_ERR("make_pkts failed\n");
- return -1;
- }
- }
-
- pkts_sent = send_pkts(tm_queue, num_pkts);
- num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
- 1000 * 1000);
- ret_code = -1;
-
- if (num_rcv_pkts == 0) {
- LOG_ERR("No pkts received\n");
- CU_ASSERT(num_rcv_pkts != 0);
- ret_code = -1;
- } else if (num_rcv_pkts != pkts_sent) {
- LOG_ERR("pkts_sent=%" PRIu32 " but num_rcv_pkts=%" PRIu32 "\n",
- pkts_sent, num_rcv_pkts);
- dump_rcvd_pkts(0, num_rcv_pkts - 1);
- CU_ASSERT(num_rcv_pkts == pkts_sent);
- ret_code = -1;
- } else {
- rc = check_tos_marking_pkts(use_ipv6, use_tcp, test_ecn,
- test_drop_prec, DEFAULT_TOS,
- new_dscp, dscp_mask);
- CU_ASSERT(rc == 0);
- ret_code = (rc == 0) ? 0 : -1;
- }
-
- flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
- CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
- return ret_code;
-}
-
-static int test_protocol_marking(const char *node_name,
- odp_packet_color_t pkt_color,
- odp_bool_t test_ecn,
- odp_bool_t test_drop_prec,
- uint8_t new_dscp,
- uint8_t dscp_mask)
-{
- uint32_t errs = 0;
- int rc;
-
- /* Now call test_ip_marking once for all combinations of IPv4 or IPv6
- * pkts AND for UDP or TCP. */
- rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_UDP,
- test_ecn, test_drop_prec, new_dscp, dscp_mask);
- CU_ASSERT(rc == 0);
- if (rc != 0) {
- LOG_ERR("test_ip_marking failed using IPV4/UDP pkts color=%u "
- "test_ecn=%u test_drop_prec=%u\n",
- pkt_color, test_ecn, test_drop_prec);
- errs++;
- }
-
- rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_UDP,
- test_ecn, test_drop_prec, new_dscp, dscp_mask);
- CU_ASSERT(rc == 0);
- if (rc != 0) {
- LOG_ERR("test_ip_marking failed using IPV6/UDP pkts color=%u "
- "test_ecn=%u test_drop_prec=%u\n",
- pkt_color, test_ecn, test_drop_prec);
- errs++;
- }
-
- rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_TCP,
- test_ecn, test_drop_prec, new_dscp, dscp_mask);
- CU_ASSERT(rc == 0);
- if (rc != 0) {
- LOG_ERR("test_ip_marking failed using IPV4/TCP pkts color=%u "
- "test_ecn=%u test_drop_prec=%u\n",
- pkt_color, test_ecn, test_drop_prec);
- errs++;
- }
-
- rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_TCP,
- test_ecn, test_drop_prec, new_dscp, dscp_mask);
- CU_ASSERT(rc == 0);
- if (rc != 0) {
- LOG_ERR("test_ip_marking failed using IPV6/TCP pkts color=%u "
- "test_ecn=%u test_drop_prec=%u\n",
- pkt_color, test_ecn, test_drop_prec);
- errs++;
- }
-
- return (errs == 0) ? 0 : -1;
-}
-
-static int ip_marking_tests(const char *node_name,
- odp_bool_t test_ecn,
- odp_bool_t test_drop_prec)
-{
- odp_packet_color_t color;
- uint32_t errs = 0;
- uint8_t new_dscp, dscp_mask;
- int rc;
-
- dscp_mask = DROP_PRECEDENCE_MASK;
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- if (tm_capabilities.marking_colors_supported[color]) {
- if (color == PKT_YELLOW)
- new_dscp = MEDIUM_DROP_PRECEDENCE;
- else if (color == PKT_RED)
- new_dscp = HIGH_DROP_PRECEDENCE;
- else
- new_dscp = LOW_DROP_PRECEDENCE;
-
- rc = test_protocol_marking(node_name, color, test_ecn,
- test_drop_prec, new_dscp,
- dscp_mask);
- CU_ASSERT(rc == 0);
- if (rc != 0)
- errs++;
- }
- }
-
- return (errs == 0) ? 0 : -1;
-}
-
-static int walk_tree_backwards(odp_tm_node_t tm_node)
-{
- odp_tm_node_fanin_info_t fanin_info;
- odp_tm_node_info_t node_info;
- odp_tm_queue_t first_tm_queue;
- odp_tm_node_t first_tm_node;
- uint32_t tm_queue_fanin, tm_node_fanin;
- int rc;
-
- /* Start from the given tm_node and try to go backwards until a valid
- * and active tm_queue is reached. */
- rc = odp_tm_node_info(tm_node, &node_info);
- if (rc != 0) {
- LOG_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n",
- tm_node);
- return rc;
- }
-
- if ((node_info.tm_queue_fanin == 0) &&
- (node_info.tm_node_fanin == 0)) {
- LOG_ERR("odp_tm_node_info showed no fanin for this node\n");
- return -1;
- }
-
- fanin_info.tm_queue = ODP_TM_INVALID;
- fanin_info.tm_node = ODP_TM_INVALID;
- fanin_info.is_last = false;
-
- /* TBD* Loop over the entire fanin list verifying the fanin counts.
- * Also remember the first tm_queue and tm_node seen. */
- tm_queue_fanin = 0;
- tm_node_fanin = 0;
- first_tm_queue = ODP_TM_INVALID;
- first_tm_node = ODP_TM_INVALID;
-
- while (!fanin_info.is_last) {
- rc = odp_tm_node_fanin_info(tm_node, &fanin_info);
- if (rc != 0)
- return rc;
-
- if ((fanin_info.tm_queue != ODP_TM_INVALID) &&
- (fanin_info.tm_node != ODP_TM_INVALID)) {
- LOG_ERR("Both tm_queue and tm_node are set\n");
- return -1;
- } else if (fanin_info.tm_queue != ODP_TM_INVALID) {
- tm_queue_fanin++;
- if (first_tm_queue == ODP_TM_INVALID)
- first_tm_queue = fanin_info.tm_queue;
- } else if (fanin_info.tm_node != ODP_TM_INVALID) {
- tm_node_fanin++;
- if (first_tm_node == ODP_TM_INVALID)
- first_tm_node = fanin_info.tm_node;
- } else {
- LOG_ERR("both tm_queue and tm_node are INVALID\n");
- return -1;
- }
- }
-
- if (tm_queue_fanin != node_info.tm_queue_fanin)
- LOG_ERR("tm_queue_fanin count error\n");
- else if (tm_node_fanin != node_info.tm_node_fanin)
- LOG_ERR("tm_node_fanin count error\n");
-
- /* If we have found a tm_queue then we are successfully done. */
- if (first_tm_queue != ODP_TM_INVALID)
- return 0;
-
- /* Now recurse up a level */
- return walk_tree_backwards(first_tm_node);
-}
-
-static int test_fanin_info(const char *node_name)
-{
- tm_node_desc_t *node_desc;
- odp_tm_node_t tm_node;
-
- node_desc = find_node_desc(0, node_name);
- if (node_desc == NULL) {
- LOG_ERR("node_name %s not found\n", node_name);
- return -1;
- }
-
- tm_node = node_desc->node;
- if (tm_node == ODP_TM_INVALID) {
- LOG_ERR("tm_node is ODP_TM_INVALID\n");
- return -1;
- }
-
- return walk_tree_backwards(node_desc->node);
-}
-
-void traffic_mngr_test_capabilities(void)
-{
- CU_ASSERT(test_overall_capabilities() == 0);
-}
-
-void traffic_mngr_test_tm_create(void)
-{
- /* Create the first/primary TM system. */
- CU_ASSERT_FATAL(create_tm_system() == 0);
- dump_tm_tree(0);
-}
-
-void traffic_mngr_test_shaper(void)
-{
- CU_ASSERT(test_shaper_bw("bw1", "node_1_1_1", 0, 1 * MBPS) == 0);
- CU_ASSERT(test_shaper_bw("bw4", "node_1_1_1", 1, 4 * MBPS) == 0);
- CU_ASSERT(test_shaper_bw("bw10", "node_1_1_1", 2, 10 * MBPS) == 0);
- CU_ASSERT(test_shaper_bw("bw40", "node_1_1_1", 3, 40 * MBPS) == 0);
- CU_ASSERT(test_shaper_bw("bw100", "node_1_1_2", 0, 100 * MBPS) == 0);
-}
-
-void traffic_mngr_test_scheduler(void)
-{
- CU_ASSERT(test_sched_queue_priority("que_prio", "node_1_1_3", 10) == 0);
- return;
-
- /* The following tests are not quite ready for production use. */
- CU_ASSERT(test_sched_node_priority("node_prio", "node_1_3", 4) == 0);
-
- CU_ASSERT(test_sched_wfq("sched_rr", "shaper_rr", "node_1_3",
- ODP_TM_FRAME_BASED_WEIGHTS,
- EQUAL_WEIGHTS) == 0);
- CU_ASSERT(test_sched_wfq("sched_wrr", "shaper_wrr", "node_1_3",
- ODP_TM_FRAME_BASED_WEIGHTS,
- INCREASING_WEIGHTS) == 0);
- CU_ASSERT(test_sched_wfq("sched_wfq", "shaper_wfq", "node_1_3",
- ODP_TM_BYTE_BASED_WEIGHTS,
- INCREASING_WEIGHTS) == 0);
-}
-
-void traffic_mngr_test_thresholds(void)
-{
- CU_ASSERT(test_threshold("thresh_A", "shaper_A", "node_1_2_1", 0,
- 16, 0) == 0);
- CU_ASSERT(test_threshold("thresh_B", "shaper_B", "node_1_2_1", 1,
- 0, 6400) == 0);
-}
-
-void traffic_mngr_test_byte_wred(void)
-{
- if (!tm_capabilities.tm_queue_wred_supported) {
- LOG_DBG("\nwas not run because tm_capabilities indicates"
- " no WRED support\n");
- return;
- }
-
- CU_ASSERT(test_byte_wred("byte_wred_30G", "byte_bw_30G",
- "byte_thresh_30G", "node_1_3_1", 1,
- ODP_PACKET_GREEN, TM_PERCENT(30), true) == 0);
- CU_ASSERT(test_byte_wred("byte_wred_50Y", "byte_bw_50Y",
- "byte_thresh_50Y", "node_1_3_1", 2,
- ODP_PACKET_YELLOW, TM_PERCENT(50), true) == 0);
- CU_ASSERT(test_byte_wred("byte_wred_70R", "byte_bw_70R",
- "byte_thresh_70R", "node_1_3_1", 3,
- ODP_PACKET_RED, TM_PERCENT(70), true) == 0);
-
- CU_ASSERT(test_byte_wred("byte_wred_40G", "byte_bw_40G",
- "byte_thresh_40G", "node_1_3_1", 1,
- ODP_PACKET_GREEN, TM_PERCENT(30), false) == 0);
-}
-
-void traffic_mngr_test_pkt_wred(void)
-{
- int rc;
-
- if (!tm_capabilities.tm_queue_wred_supported) {
- LOG_DBG("\ntest_pkt_wred was not run because tm_capabilities "
- "indicates no WRED support\n");
- return;
- }
-
- CU_ASSERT(test_pkt_wred("pkt_wred_40G", "pkt_bw_40G",
- "pkt_thresh_40G", "node_1_3_2", 1,
- ODP_PACKET_GREEN, TM_PERCENT(30), false) == 0);
-
- if (!tm_capabilities.tm_queue_dual_slope_supported) {
- LOG_DBG("since tm_capabilities indicates no dual slope "
- "WRED support these tests are skipped.\n");
- return;
- }
-
- rc = test_pkt_wred("pkt_wred_30G", "pkt_bw_30G",
- "pkt_thresh_30G", "node_1_3_2", 1,
- ODP_PACKET_GREEN, TM_PERCENT(30), true);
- CU_ASSERT(rc == 0);
-
- CU_ASSERT(test_pkt_wred("pkt_wred_50Y", "pkt_bw_50Y",
- "pkt_thresh_50Y", "node_1_3_2", 2,
- ODP_PACKET_YELLOW, TM_PERCENT(50), true) == 0);
- CU_ASSERT(test_pkt_wred("pkt_wred_70R", "pkt_bw_70R",
- "pkt_thresh_70R", "node_1_3_2", 3,
- ODP_PACKET_RED, TM_PERCENT(70), true) == 0);
-}
-
-void traffic_mngr_test_query(void)
-{
- CU_ASSERT(test_query_functions("query_shaper", "node_1_3_3", 3, 10)
- == 0);
-}
-
-void traffic_mngr_test_marking(void)
-{
- odp_packet_color_t color;
- odp_bool_t test_ecn, test_drop_prec;
- int rc;
-
- if (tm_capabilities.vlan_marking_supported) {
- for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
- rc = test_vlan_marking("node_1_3_1", color);
- CU_ASSERT(rc == 0);
- }
- } else {
- LOG_DBG("\ntest_vlan_marking was not run because "
- "tm_capabilities indicates no vlan marking support\n");
- }
-
- if (tm_capabilities.ecn_marking_supported) {
- test_ecn = true;
- test_drop_prec = false;
-
- rc = ip_marking_tests("node_1_3_2", test_ecn, test_drop_prec);
- CU_ASSERT(rc == 0);
- } else {
- LOG_DBG("\necn_marking tests were not run because "
- "tm_capabilities indicates no ecn marking support\n");
- }
-
- if (tm_capabilities.drop_prec_marking_supported) {
- test_ecn = false;
- test_drop_prec = true;
-
- rc = ip_marking_tests("node_1_4_2", test_ecn, test_drop_prec);
- CU_ASSERT(rc == 0);
- } else {
- LOG_DBG("\ndrop_prec marking tests were not run because "
- "tm_capabilities indicates no drop precedence "
- "marking support\n");
- }
-
- if (tm_capabilities.ecn_marking_supported &&
- tm_capabilities.drop_prec_marking_supported) {
- test_ecn = true;
- test_drop_prec = true;
-
- rc = ip_marking_tests("node_1_4_2", test_ecn, test_drop_prec);
- CU_ASSERT(rc == 0);
- }
-}
-
-void traffic_mngr_test_fanin_info(void)
-{
- CU_ASSERT(test_fanin_info("node_1") == 0);
- CU_ASSERT(test_fanin_info("node_1_2") == 0);
- CU_ASSERT(test_fanin_info("node_1_3_7") == 0);
-}
-
-void traffic_mngr_test_destroy(void)
-{
- CU_ASSERT(destroy_tm_systems() == 0);
-}
-
-odp_testinfo_t traffic_mngr_suite[] = {
- ODP_TEST_INFO(traffic_mngr_test_capabilities),
- ODP_TEST_INFO(traffic_mngr_test_tm_create),
- ODP_TEST_INFO(traffic_mngr_test_shaper_profile),
- ODP_TEST_INFO(traffic_mngr_test_sched_profile),
- ODP_TEST_INFO(traffic_mngr_test_threshold_profile),
- ODP_TEST_INFO(traffic_mngr_test_wred_profile),
- ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_shaper,
- traffic_mngr_check_shaper),
- ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_scheduler,
- traffic_mngr_check_scheduler),
- ODP_TEST_INFO(traffic_mngr_test_thresholds),
- ODP_TEST_INFO(traffic_mngr_test_byte_wred),
- ODP_TEST_INFO(traffic_mngr_test_pkt_wred),
- ODP_TEST_INFO(traffic_mngr_test_query),
- ODP_TEST_INFO(traffic_mngr_test_marking),
- ODP_TEST_INFO(traffic_mngr_test_fanin_info),
- ODP_TEST_INFO(traffic_mngr_test_destroy),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t traffic_mngr_suites[] = {
- { "traffic_mngr tests", traffic_mngr_suite_init,
- traffic_mngr_suite_term, traffic_mngr_suite },
- ODP_SUITE_INFO_NULL
-};
-
-int traffic_mngr_main(int argc, char *argv[])
-{
- /* parse common options: */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- int ret = odp_cunit_register(traffic_mngr_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h
deleted file mode 100644
index af115fef7..000000000
--- a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_TEST_TRAFFIC_MNGR_H_
-#define _ODP_TEST_TRAFFIC_MNGR_H_
-
-#include <odp_cunit_common.h>
-
-int traffic_mngr_check_shaper(void);
-int traffic_mngr_check_scheduler(void);
-
-/* test functions: */
-void traffic_mngr_test_capabilities(void);
-void traffic_mngr_test_tm_create(void);
-void traffic_mngr_test_shaper_profile(void);
-void traffic_mngr_test_sched_profile(void);
-void traffic_mngr_test_threshold_profile(void);
-void traffic_mngr_test_wred_profile(void);
-void traffic_mngr_test_shaper(void);
-void traffic_mngr_test_scheduler(void);
-void traffic_mngr_test_thresholds(void);
-void traffic_mngr_test_byte_wred(void);
-void traffic_mngr_test_pkt_wred(void);
-void traffic_mngr_test_query(void);
-void traffic_mngr_test_marking(void);
-void traffic_mngr_test_fanin_info(void);
-void traffic_mngr_test_destroy(void);
-
-/* test arrays: */
-extern odp_testinfo_t traffic_mngr_suite[];
-
-/* test suite init/term functions: */
-int traffic_mngr_suite_init(void);
-int traffic_mngr_suite_term(void);
-
-/* test registry: */
-extern odp_suiteinfo_t traffic_mngr_suites[];
-
-/* main test program: */
-int traffic_mngr_main(int argc, char *argv[]);
-
-#endif
diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.sh b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.sh
deleted file mode 100755
index 4db7ea384..000000000
--- a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2017, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# directories where traffic_mngr_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./traffic_mngr) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/traffic_mngr:$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/traffic_mngr:$PATH
-PATH=$(dirname $0):$PATH
-PATH=`pwd`:$PATH
-
-traffic_mngr_main_path=$(which traffic_mngr_main${EXEEXT})
-if [ -x "$traffic_mngr_main_path" ] ; then
- echo "running with traffic_mngr_main: $traffic_mngr_run_path"
-else
- echo "cannot find traffic_mngr_main: please set you PATH for it."
- exit 1
-fi
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-traffic_mngr_main${EXEEXT}
-ret=$?
-
-SIGSEGV=139
-
-if [ "${TRAVIS}" = "true" ] && [ $ret -ne 0 ] && [ $ret -ne ${SIGSEGV} ]; then
- echo "SKIP: skip due to not isolated environment"
- exit ${TEST_SKIPPED}
-fi
-
-exit $ret
diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c b/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c
deleted file mode 100644
index 1fc1f78d7..000000000
--- a/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "traffic_mngr.h"
-
-int main(int argc, char *argv[])
-{
- return traffic_mngr_main(argc, argv);
-}
diff --git a/test/linux-generic/Makefile.am b/test/linux-generic/Makefile.am
deleted file mode 100644
index f92083d73..000000000
--- a/test/linux-generic/Makefile.am
+++ /dev/null
@@ -1,80 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/common_plat/validation
-
-ALL_API_VALIDATION_DIR = ${top_builddir}/test/common_plat/validation/api
-
-SUBDIRS = performance
-
-if test_vald
-TESTS = validation/api/pktio/pktio_run.sh \
- validation/api/pktio/pktio_run_tap.sh \
- validation/api/shmem/shmem_linux \
- $(ALL_API_VALIDATION_DIR)/atomic/atomic_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/barrier/barrier_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/buffer/buffer_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/classification/classification_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/cpumask/cpumask_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/crypto/crypto_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/errno/errno_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/hash/hash_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/init/init_main_ok$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/init/init_main_abort$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/init/init_main_log$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/lock/lock_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/packet/packet_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/pool/pool_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/queue/queue_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/random/random_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/scheduler/scheduler_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/std_clib/std_clib_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/thread/thread_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/time/time.sh \
- $(ALL_API_VALIDATION_DIR)/timer/timer_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/traffic_mngr/traffic_mngr.sh \
- $(ALL_API_VALIDATION_DIR)/shmem/shmem_main$(EXEEXT) \
- $(ALL_API_VALIDATION_DIR)/system/system_main$(EXEEXT) \
- ring/ring_main$(EXEEXT)
-
-SUBDIRS += validation/api/pktio\
- validation/api/shmem\
- mmap_vlan_ins\
- pktio_ipc\
- ring
-
-if HAVE_PCAP
-TESTS += validation/api/pktio/pktio_run_pcap.sh
-
-TESTS += mmap_vlan_ins/mmap_vlan_ins.sh
-SUBDIRS += mmap_vlan_ins
-endif
-if netmap_support
-TESTS += validation/api/pktio/pktio_run_netmap.sh
-endif
-if PKTIO_DPDK
-TESTS += validation/api/pktio/pktio_run_dpdk.sh
-endif
-TESTS += pktio_ipc/pktio_ipc_run.sh
-SUBDIRS += pktio_ipc
-else
-#performance tests refer to pktio_env
-if test_perf
-SUBDIRS += validation/api/pktio
-endif
-endif
-
-TEST_EXTENSIONS = .sh
-
-dist_check_SCRIPTS = run-test tests-validation.env $(LOG_COMPILER)
-
-test_SCRIPTS = $(dist_check_SCRIPTS)
-
-tests-validation.env:
- echo "TESTS=\"$(TESTS)\"" > $@
- echo "$(TESTS_ENVIRONMENT)" >> $@
- echo "$(LOG_COMPILER)" >> $@
-
-if test_installdir
-installcheck-local:
- $(DESTDIR)/$(testdir)/run-test
-endif
-
diff --git a/test/linux-generic/Makefile.inc b/test/linux-generic/Makefile.inc
deleted file mode 100644
index 30b56e263..000000000
--- a/test/linux-generic/Makefile.inc
+++ /dev/null
@@ -1,22 +0,0 @@
-# The following definitions may be used by platform tests that wish to
-# build specific ODP applications, (i.e those whose do more than validation
-# test wrapping)
-
-AM_LDFLAGS += -static
-
-AM_CPPFLAGS += $(CUNIT_CPPFLAGS)
-
-LIBCUNIT_COMMON = $(top_builddir)/test/common_plat/common/libcunit_common.la
-LIB = $(top_builddir)/lib
-LIBODP = $(LIB)/libodphelper.la $(LIB)/libodp-linux.la $(DPDK_PMDS)
-
-INCCUNIT_COMMON = -I$(top_srcdir)/test/common_plat/common
-INCODP = \
- -I$(top_builddir)/include \
- -I$(top_builddir)/platform/@with_platform@/include \
- -I$(top_srcdir)/helper/include \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ \
- -I$(top_srcdir)/platform/@with_platform@/arch/$(ARCH_DIR) \
- -I$(top_srcdir)/platform/@with_platform@/include \
- -I$(top_srcdir)/test
diff --git a/test/linux-generic/m4/configure.m4 b/test/linux-generic/m4/configure.m4
deleted file mode 100644
index 8746dabc8..000000000
--- a/test/linux-generic/m4/configure.m4
+++ /dev/null
@@ -1,9 +0,0 @@
-m4_include([test/linux-generic/m4/performance.m4])
-
-AC_CONFIG_FILES([test/linux-generic/Makefile
- test/linux-generic/validation/api/shmem/Makefile
- test/linux-generic/validation/api/pktio/Makefile
- test/linux-generic/mmap_vlan_ins/Makefile
- test/linux-generic/pktio_ipc/Makefile
- test/linux-generic/ring/Makefile
- test/linux-generic/performance/Makefile])
diff --git a/test/linux-generic/m4/performance.m4 b/test/linux-generic/m4/performance.m4
deleted file mode 100644
index 7f54b96d7..000000000
--- a/test/linux-generic/m4/performance.m4
+++ /dev/null
@@ -1,9 +0,0 @@
-##########################################################################
-# Enable/disable test-perf-proc
-##########################################################################
-test_perf_proc=no
-AC_ARG_ENABLE([test-perf-proc],
- [ --enable-test-perf-proc run test in test/performance in process mode],
- [if test "x$enableval" = "xyes"; then
- test_perf_proc=yes
- fi])
diff --git a/test/linux-generic/mmap_vlan_ins/.gitignore b/test/linux-generic/mmap_vlan_ins/.gitignore
deleted file mode 100644
index 755fa2ed5..000000000
--- a/test/linux-generic/mmap_vlan_ins/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.pcap
-plat_mmap_vlan_ins
diff --git a/test/linux-generic/mmap_vlan_ins/Makefile.am b/test/linux-generic/mmap_vlan_ins/Makefile.am
deleted file mode 100644
index 5cac159c7..000000000
--- a/test/linux-generic/mmap_vlan_ins/Makefile.am
+++ /dev/null
@@ -1,15 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/validation
-
-dist_check_SCRIPTS = vlan.pcap \
- mmap_vlan_ins.sh \
- pktio_env
-
-test_SCRIPTS = $(dist_check_SCRIPTS)
-
-test_PROGRAMS = plat_mmap_vlan_ins$(EXEEXT)
-plat_mmap_vlan_ins_LDFLAGS = $(AM_LDFLAGS) -static
-plat_mmap_vlan_ins_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-
-# Clonned from example odp_l2fwd simple
-dist_plat_mmap_vlan_ins_SOURCES = mmap_vlan_ins.c
diff --git a/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.c b/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.c
deleted file mode 100644
index e91a9d0dd..000000000
--- a/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <signal.h>
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-
-#define POOL_NUM_PKT 100
-#define POOL_SEG_LEN 1500
-#define MAX_PKT_BURST 32
-#define MAX_WORKERS 1
-
-static int g_ret;
-
-struct {
- odp_pktio_t if0, if1;
- odp_pktin_queue_t if0in, if1in;
- odp_pktout_queue_t if0out, if1out;
- odph_ethaddr_t src, dst;
-} global;
-
-static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
- odp_pktin_queue_t *pktin,
- odp_pktout_queue_t *pktout)
-{
- odp_pktio_param_t pktio_param;
- odp_pktin_queue_param_t in_queue_param;
- odp_pktout_queue_param_t out_queue_param;
- odp_pktio_t pktio;
-
- odp_pktio_param_init(&pktio_param);
-
- pktio = odp_pktio_open(name, pool, &pktio_param);
- if (pktio == ODP_PKTIO_INVALID) {
- printf("Failed to open %s\n", name);
- exit(1);
- }
-
- odp_pktin_queue_param_init(&in_queue_param);
- odp_pktout_queue_param_init(&out_queue_param);
-
- in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
-
- if (odp_pktin_queue_config(pktio, &in_queue_param)) {
- printf("Failed to config input queue for %s\n", name);
- exit(1);
- }
-
- out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
-
- if (odp_pktout_queue_config(pktio, &out_queue_param)) {
- printf("Failed to config output queue for %s\n", name);
- exit(1);
- }
-
- if (odp_pktin_queue(pktio, pktin, 1) != 1) {
- printf("pktin queue query failed for %s\n", name);
- exit(1);
- }
- if (odp_pktout_queue(pktio, pktout, 1) != 1) {
- printf("pktout queue query failed for %s\n", name);
- exit(1);
- }
- return pktio;
-}
-
-static int run_worker(void *arg ODP_UNUSED)
-{
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- int pkts, sent, tx_drops, i;
- int total_pkts = 0;
- uint64_t wait_time = odp_pktin_wait_time(2 * ODP_TIME_SEC_IN_NS);
-
- if (odp_pktio_start(global.if0)) {
- printf("unable to start input interface\n");
- exit(1);
- }
- printf("started input interface\n");
- if (odp_pktio_start(global.if1)) {
- printf("unable to start output interface\n");
- exit(1);
- }
- printf("started output interface\n");
- printf("started all\n");
-
- while (1) {
- pkts = odp_pktin_recv_tmo(global.if0in, pkt_tbl, MAX_PKT_BURST,
- wait_time);
- if (odp_unlikely(pkts <= 0)) {
- printf("recv tmo!\n");
- break;
- }
-
- for (i = 0; i < pkts; i++) {
- odp_packet_t pkt = pkt_tbl[i];
- odph_ethhdr_t *eth;
-
- if (odp_unlikely(!odp_packet_has_eth(pkt))) {
- printf("warning: packet has no eth header\n");
- return 0;
- }
- eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
- eth->src = global.src;
- eth->dst = global.dst;
- }
- sent = odp_pktout_send(global.if1out, pkt_tbl, pkts);
- if (sent < 0)
- sent = 0;
- total_pkts += sent;
- tx_drops = pkts - sent;
- if (odp_unlikely(tx_drops))
- odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
- }
-
- printf("Total send packets: %d\n", total_pkts);
-
- if (total_pkts < 10)
- g_ret = -1;
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_cpumask_t cpumask;
- odph_odpthread_t thd[MAX_WORKERS];
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
- int opt;
- int long_index;
-
- static const struct option longopts[] = { {NULL, 0, NULL, 0} };
- static const char *shortopts = "";
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, shortopts, longopts);
-
- /*
- * parse own options: currentely none, but this will move optind
- * to the first non-option argument. (in case there where helprt args)
- */
- opterr = 0; /* do not issue errors on helper options */
- while (1) {
- opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
- if (-1 == opt)
- break; /* No more options */
- }
-
- if (argc != optind + 4 ||
- odph_eth_addr_parse(&global.dst, argv[optind + 2]) != 0 ||
- odph_eth_addr_parse(&global.src, argv[optind + 3]) != 0) {
- printf("Usage: odp_l2fwd_simple eth0 eth1 01:02:03:04:05:06"
- " 07:08:09:0a:0b:0c\n");
- printf("Where eth0 and eth1 are the used interfaces"
- " (must have 2 of them)\n");
- printf("And the hexadecimal numbers are destination MAC address"
- " and source MAC address\n");
- exit(1);
- }
-
- if (odp_init_global(&instance, NULL, NULL)) {
- printf("Error: ODP global init failed.\n");
- exit(1);
- }
-
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- printf("Error: ODP local init failed.\n");
- exit(1);
- }
-
- /* Create packet pool */
- odp_pool_param_init(&params);
- params.pkt.seg_len = POOL_SEG_LEN;
- params.pkt.len = POOL_SEG_LEN;
- params.pkt.num = POOL_NUM_PKT;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create("packet pool", &params);
-
- if (pool == ODP_POOL_INVALID) {
- printf("Error: packet pool create failed.\n");
- exit(1);
- }
-
- global.if0 = create_pktio(argv[optind], pool, &global.if0in,
- &global.if0out);
- global.if1 = create_pktio(argv[optind + 1], pool, &global.if1in,
- &global.if1out);
-
- odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = run_worker;
- thr_params.arg = NULL;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- odph_odpthreads_create(thd, &cpumask, &thr_params);
- odph_odpthreads_join(thd);
-
- if (odp_pool_destroy(pool)) {
- printf("Error: pool destroy\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- printf("Error: term local\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- printf("Error: term global\n");
- exit(EXIT_FAILURE);
- }
-
- return g_ret;
-}
diff --git a/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.sh b/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.sh
deleted file mode 100755
index 3c6df8ecd..000000000
--- a/test/linux-generic/mmap_vlan_ins/mmap_vlan_ins.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-#
-# This test is intend to test pkt_mmap_vlan_insert() feature for
-# linux-generic packet mmap pktio.
-#
-#
-export ODP_PKTIO_DISABLE_SOCKET_MMSG=1
-
-# directory where platform test sources are, including scripts
-TEST_SRC_DIR=$(dirname $0)
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-# directories where binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/../mmap_vlan_ins:$PATH
-PATH=`pwd`/mmap_vlan_ins:$PATH
-PATH=$(dirname $0):$PATH
-PATH=.:$PATH
-
-bin_path=$(which plat_mmap_vlan_ins${EXEEXT})
-if [ -x "$bin_path" ] ; then
- echo "running with plat_mmap_vlan_ins: $bin_path"
-else
- echo "cannot find plat_mmap_vlan_ins: please set you PATH for it."
- pwd
- exit 1
-fi
-
-
-# Use installed pktio env or for make check take it from platform directory
-if [ -f "./pktio_env" ]; then
- . ./pktio_env
-elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
- . ${TEST_SRC_DIR}/pktio_env
-else
- echo "BUG: unable to find pktio_env!"
- echo "pktio_env has to be in current directory or"
- echo " in platform/\$ODP_PLATFORM/test."
- echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
- exit 1
-fi
-
-setup_pktio_env
-if [ $? -ne 0 ]; then
- return 77 # Skip the test
-fi
-
-PCAP_IN=`find . ${TEST_DIR} $(dirname $0) -name vlan.pcap -print -quit`
-echo "using PCAP_IN = ${PCAP_IN}"
-PCAP_OUT=vlan_out.pcap
-
-# Listen on veth pipe and write to pcap Send pcap
-plat_mmap_vlan_ins${EXEEXT} pktiop0p1 pcap:out=${PCAP_OUT} \
- 00:02:03:04:05:06 00:08:09:0a:0b:0c &
-# Send pcap file to veth interface
-plat_mmap_vlan_ins${EXEEXT} pcap:in=${PCAP_IN} pktiop1p0 \
- 01:02:03:04:05:06 01:08:09:0a:0b:0c
-
-rm -f ${PCAP_OUT}
-cleanup_pktio_env
-
-exit 0
diff --git a/test/linux-generic/mmap_vlan_ins/pktio_env b/test/linux-generic/mmap_vlan_ins/pktio_env
deleted file mode 100644
index 345b5bd56..000000000
--- a/test/linux-generic/mmap_vlan_ins/pktio_env
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Test script wrapper for running ODP pktio apps on linux-generic.
-#
-# For linux-generic the default behavior is to create two pairs of
-# virtual Ethernet interfaces and provide the names of these via
-# environment variables to pktio apps, the interfaces will be removed
-# before the script exits.
-#
-# Note that the creation of virtual Ethernet devices depends on having
-# CONFIG_VETH enabled in the kernel, if not enabled the env setup will be skipped.
-#
-# Network set up
-# IF0 <---> IF1
-# IF2 <---> IF3
-IF0=pktiop0p1
-IF1=pktiop1p0
-IF2=pktiop2p3
-IF3=pktiop3p2
-
-if [ "$0" = "$BASH_SOURCE" ]; then
- echo "Error: Platform specific env file has to be sourced."
-fi
-
-check_for_root()
-{
- if [ "$(id -u)" != "0" ]; then
- echo "check_for_root(): need to be root to setup VETH"
- return 1
- fi
- return 0
-}
-
-# wait for a network interface's operational state to be "up"
-wait_for_iface_up()
-{
- iface=$1
- cnt=0
-
- while [ $cnt -lt 50 ]; do
- read operstate < /sys/class/net/$iface/operstate
-
- if [ $? -ne 0 ]; then
- break
- elif [ "$operstate" = "up" ]; then
- return 0
- fi
-
- sleep 0.1
- cnt=`expr $cnt + 1`
- done
-
- return 1
-}
-
-setup_pktio_env()
-{
- echo "pktio: setting up test interfaces $IF0, $IF1, $IF2, $IF3."
-
- check_for_root
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link show $iface 2> /dev/null
- if [ $? -eq 0 ]; then
- echo "pktio: interface $iface already exist $?"
- return 2
- fi
- done
-
- if [ "$1" = "clean" ]; then
- trap cleanup_pktio_env EXIT
- fi
-
- ip link add $IF0 type veth peer name $IF1
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create veth pair"
- return 3
- fi
- ip link add $IF2 type veth peer name $IF3
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create veth pair"
- return 4
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link set $iface mtu 9216 up
- ifconfig $iface -arp
- done
-
- # check that the interface has come up before starting the test
- for iface in $IF0 $IF1 $IF2 $IF3; do
- wait_for_iface_up $iface
- if [ $? -ne 0 ]; then
- echo "pktio: interface $iface failed to come up"
- return 5
- fi
- done
-}
-
-cleanup_pktio_env()
-{
- echo "pktio: removing test interfaces $IF0, $IF1, $IF2, $IF3"
- check_for_root
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link del $iface 2> /dev/null
- done
- return 0
-}
diff --git a/test/linux-generic/mmap_vlan_ins/vlan.pcap b/test/linux-generic/mmap_vlan_ins/vlan.pcap
deleted file mode 100644
index 106ccb682..000000000
--- a/test/linux-generic/mmap_vlan_ins/vlan.pcap
+++ /dev/null
Binary files differ
diff --git a/test/linux-generic/performance/.gitignore b/test/linux-generic/performance/.gitignore
deleted file mode 100644
index 7e563b8b3..000000000
--- a/test/linux-generic/performance/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.log
-*.trs
diff --git a/test/linux-generic/performance/Makefile.am b/test/linux-generic/performance/Makefile.am
deleted file mode 100644
index cb72fce96..000000000
--- a/test/linux-generic/performance/Makefile.am
+++ /dev/null
@@ -1,13 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-
-TESTS_ENVIRONMENT += TEST_DIR=${builddir}
-
-TESTSCRIPTS = odp_scheduling_run_proc.sh
-
-TEST_EXTENSIONS = .sh
-
-if test_perf_proc
-TESTS = $(TESTSCRIPTS)
-endif
-
-EXTRA_DIST = $(TESTSCRIPTS)
diff --git a/test/linux-generic/performance/odp_scheduling_run_proc.sh b/test/linux-generic/performance/odp_scheduling_run_proc.sh
deleted file mode 100755
index 384017aff..000000000
--- a/test/linux-generic/performance/odp_scheduling_run_proc.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Script that passes command line arguments to odp_scheduling test when
-# launched by 'make check'
-
-TEST_DIR="${TEST_DIR:-$(dirname $0)}"
-PERFORMANCE="$TEST_DIR/../../common_plat/performance"
-ret=0
-ALL=0
-
-run()
-{
- echo odp_scheduling_run starts requesting $1 worker threads
- echo =====================================================
-
- $PERFORMANCE/odp_scheduling${EXEEXT} --odph_proc -c $1 || ret=1
-}
-
-run 1
-run 5
-run 8
-run 11
-run $ALL
-
-exit $ret
diff --git a/test/linux-generic/pktio_ipc/Makefile.am b/test/linux-generic/pktio_ipc/Makefile.am
deleted file mode 100644
index 8858bd2f5..000000000
--- a/test/linux-generic/pktio_ipc/Makefile.am
+++ /dev/null
@@ -1,20 +0,0 @@
-include $(top_srcdir)/test/Makefile.inc
-TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/validation
-
-test_PROGRAMS = pktio_ipc1\
- pktio_ipc2
-
-pktio_ipc1_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-pktio_ipc1_LDFLAGS = $(AM_LDFLAGS) -static
-pktio_ipc2_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-pktio_ipc2_LDFLAGS = $(AM_LDFLAGS) -static
-
-noinst_HEADERS = $(top_srcdir)/test/test_debug.h
-
-dist_pktio_ipc1_SOURCES = pktio_ipc1.c ipc_common.c
-dist_pktio_ipc2_SOURCES = pktio_ipc2.c ipc_common.c
-
-EXTRA_DIST = ipc_common.h
-
-dist_check_SCRIPTS = pktio_ipc_run.sh
-test_SCRIPTS = $(dist_check_SCRIPTS)
diff --git a/test/linux-generic/pktio_ipc/ipc_common.c b/test/linux-generic/pktio_ipc/ipc_common.c
deleted file mode 100644
index 85cbc8b41..000000000
--- a/test/linux-generic/pktio_ipc/ipc_common.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "ipc_common.h"
-
-/** Run time in seconds */
-int run_time_sec;
-/** Pid of the master process */
-int master_pid;
-
-int ipc_odp_packet_send_or_free(odp_pktio_t pktio,
- odp_packet_t pkt_tbl[], int num)
-{
- int ret;
- int sent = 0;
- odp_time_t start_time;
- odp_time_t end_time;
- odp_time_t wait;
- odp_pktout_queue_t pktout;
- int i;
-
- start_time = odp_time_local();
- wait = odp_time_local_from_ns(ODP_TIME_SEC_IN_NS);
- end_time = odp_time_sum(start_time, wait);
-
- if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
- EXAMPLE_ERR("no output queue\n");
- return -1;
- }
-
- while (sent != num) {
- ret = odp_pktout_send(pktout, &pkt_tbl[sent], num - sent);
- if (ret < 0) {
- EXAMPLE_ERR("odp_pktout_send return %d\n", ret);
- for (i = sent; i < num; i++)
- odp_packet_free(pkt_tbl[i]);
- return -1;
- }
-
- sent += ret;
-
- if (odp_time_cmp(end_time, odp_time_local()) < 0) {
- for (i = sent; i < num; i++)
- odp_packet_free(pkt_tbl[i]);
- EXAMPLE_ERR("Send Timeout!\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-odp_pktio_t create_pktio(odp_pool_t pool, int master_pid)
-{
- odp_pktio_param_t pktio_param;
- odp_pktio_t ipc_pktio;
- char name[30];
-
- odp_pktio_param_init(&pktio_param);
-
- if (master_pid)
- sprintf(name, TEST_IPC_PKTIO_PID_NAME, master_pid);
- else
- sprintf(name, TEST_IPC_PKTIO_NAME);
-
- printf("pid: %d, create IPC pktio %s\n", getpid(), name);
- ipc_pktio = odp_pktio_open(name, pool, &pktio_param);
- if (ipc_pktio == ODP_PKTIO_INVALID) {
- EXAMPLE_ERR("Error: ipc pktio %s create failed.\n", name);
- return ODP_PKTIO_INVALID;
- }
-
- if (odp_pktin_queue_config(ipc_pktio, NULL)) {
- EXAMPLE_ERR("Input queue config failed\n");
- return ODP_PKTIO_INVALID;
- }
-
- if (odp_pktout_queue_config(ipc_pktio, NULL)) {
- EXAMPLE_ERR("Output queue config failed\n");
- return ODP_PKTIO_INVALID;
- }
-
- return ipc_pktio;
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc argument count
- * @param argv[] argument vector
- * @param appl_args Store application arguments here
- */
-void parse_args(int argc, char *argv[])
-{
- int opt;
- int long_index;
- static struct option longopts[] = {
- {"time", required_argument, NULL, 't'},
- {"pid", required_argument, NULL, 'p'}, /* master process pid */
- {"help", no_argument, NULL, 'h'}, /* return 'h' */
- {NULL, 0, NULL, 0}
- };
-
- run_time_sec = 0; /* loop forever if time to run is 0 */
- master_pid = 0;
-
- while (1) {
- opt = getopt_long(argc, argv, "+t:p:h",
- longopts, &long_index);
-
- if (opt == -1)
- break; /* No more options */
-
- switch (opt) {
- case 't':
- run_time_sec = atoi(optarg);
- break;
- case 'p':
- master_pid = atoi(optarg);
- break;
- case 'h':
- default:
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- break;
- }
- }
-
- optind = 1; /* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-void print_info(char *progname)
-{
- printf("\n"
- "ODP system info\n"
- "---------------\n"
- "ODP API version: %s\n"
- "CPU model: %s\n"
- "\n",
- odp_version_api_str(), odp_cpu_model_str());
-
- printf("Running ODP appl: \"%s\"\n"
- "-----------------\n"
- "Using IF: %s\n",
- progname, pktio_name);
- printf("\n\n");
- fflush(NULL);
-}
-
-/**
- * Prinf usage information
- */
-void usage(char *progname)
-{
- printf("\n"
- "Usage: %s OPTIONS\n"
- " E.g. -n ipc_name_space %s -t seconds\n"
- "\n"
- "OpenDataPlane odp-linux ipc test application.\n"
- "\n"
- "Mandatory OPTIONS:\n"
- " -n, --ns IPC name space ID /dev/shm/odp-<ns>-objname.\n"
- "Optional OPTIONS\n"
- " -h, --help Display help and exit.\n"
- " -t, --time Time to run in seconds.\n"
- "\n", NO_PATH(progname), NO_PATH(progname)
- );
-}
diff --git a/test/linux-generic/pktio_ipc/ipc_common.h b/test/linux-generic/pktio_ipc/ipc_common.h
deleted file mode 100644
index ca78b6846..000000000
--- a/test/linux-generic/pktio_ipc/ipc_common.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#define _POSIX_C_SOURCE 200809L
-
-#define _GNU_SOURCE
-#include <sched.h>
-
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/wait.h>
-
-#include <example_debug.h>
-
-#include <odp.h>
-#include <odp/helper/odph_api.h>
-
-/** @def SHM_PKT_POOL_SIZE
- * @brief Size of the shared memory block
- */
-#define SHM_PKT_POOL_SIZE 8192
-
-/** @def SHM_PKT_POOL_BUF_SIZE
- * @brief Buffer size of the packet pool buffer
- */
-#define SHM_PKT_POOL_BUF_SIZE 100
-
-/** @def MAX_PKT_BURST
- * @brief Maximum number of packet bursts
- */
-#define MAX_PKT_BURST 16
-
-/** Get rid of path in filename - only for unix-type paths using '/' */
-#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
- strrchr((file_name), '/') + 1 : (file_name))
-
-#define TEST_SEQ_MAGIC 0x92749451
-#define TEST_SEQ_MAGIC_2 0x81638340
-
-#define TEST_ALLOC_MAGIC 0x1234adcd
-
-#define TEST_IPC_PKTIO_NAME "ipc:ipktio"
-#define TEST_IPC_PKTIO_PID_NAME "ipc:%d:ipktio"
-
-/** Can be any name, same or not the same. */
-#define TEST_IPC_POOL_NAME "ipc_packet_pool"
-
-/** magic number and sequence at start of packet payload */
-typedef struct ODP_PACKED {
- odp_u32be_t magic;
- odp_u32be_t seq;
-} pkt_head_t;
-
-/** magic number at end of packet payload */
-typedef struct ODP_PACKED {
- odp_u32be_t magic;
-} pkt_tail_t;
-
-/** Application argument */
-char *pktio_name;
-
-/** Run time in seconds */
-int run_time_sec;
-
-/** PID of the master process */
-int master_pid;
-
-/* helper funcs */
-void parse_args(int argc, char *argv[]);
-void print_info(char *progname);
-void usage(char *progname);
-
-/**
- * Create a ipc pktio handle.
- *
- * @param pool Pool to associate with device for packet RX/TX
- * @param master_pid Pid of master process
- *
- * @return The handle of the created pktio object.
- * @retval ODP_PKTIO_INVALID if the create fails.
- */
-odp_pktio_t create_pktio(odp_pool_t pool, int master_pid);
-
-/** Spin and send all packet from table
- *
- * @param pktio pktio device
- * @param pkt_tbl packets table
- * @param num number of packets
- */
-int ipc_odp_packet_send_or_free(odp_pktio_t pktio,
- odp_packet_t pkt_tbl[],
- int num);
diff --git a/test/linux-generic/pktio_ipc/pktio_ipc1.c b/test/linux-generic/pktio_ipc/pktio_ipc1.c
deleted file mode 100644
index 5d8434bf2..000000000
--- a/test/linux-generic/pktio_ipc/pktio_ipc1.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "ipc_common.h"
-
-/**
- * @file
- * @example pktio_ipc1.c ODP IPC example application.
- * This application works in pair with pktio_ipc2 application.
- * It opens ipc pktio, allocates packets, sets magic number and
- * sends packets to ipc pktio. Then app reads packets and checks
- * that magic number was properly updated and there is no packet
- * loss (i.e. sequesce counter continiusly incrementing.)
- */
-
-/**
- * Packet IO loopback worker thread using bursts from/to IO resources
- *
- * @param arg thread arguments of type 'thread_args_t *'
- */
-static int pktio_run_loop(odp_pool_t pool)
-{
- int pkts;
- odp_pktio_t ipc_pktio = ODP_PKTIO_INVALID;
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- uint64_t cnt = 0; /* increasing counter on each send packet */
- uint64_t cnt_recv = 0; /* increasing counter to validate
- cnt on receive */
- uint64_t stat_pkts = 0;
- uint64_t stat_pkts_alloc = 0;
- uint64_t stat_pkts_prev = 0;
- uint64_t stat_errors = 0;
- uint64_t stat_free = 0;
- odp_time_t start_cycle;
- odp_time_t current_cycle;
- odp_time_t cycle;
- odp_time_t diff;
- odp_time_t wait;
- int ret;
- odp_pktin_queue_t pktin;
- char name[30];
- int sync_cnt = 0;
-
- if (master_pid)
- sprintf(name, TEST_IPC_PKTIO_PID_NAME, master_pid);
- else
- sprintf(name, TEST_IPC_PKTIO_NAME);
-
- wait = odp_time_local_from_ns(run_time_sec * ODP_TIME_SEC_IN_NS);
- start_cycle = odp_time_local();
- current_cycle = start_cycle;
-
- for (;;) {
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- printf("timeout exit, run_time_sec %d\n",
- run_time_sec);
- return -1;
- }
- }
-
- ipc_pktio = create_pktio(pool, master_pid);
- if (ipc_pktio != ODP_PKTIO_INVALID)
- break;
- if (!master_pid)
- break;
- }
-
- if (ipc_pktio == ODP_PKTIO_INVALID)
- return -1;
-
- if (odp_pktin_queue(ipc_pktio, &pktin, 1) != 1) {
- EXAMPLE_ERR("no input queue\n");
- return -1;
- }
-
- /* start ipc pktio, i.e. wait until other process connects */
- for (;;) {
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- printf("timeout exit, run_time_sec %d\n",
- run_time_sec);
- goto exit;
- }
- }
-
- ret = odp_pktio_start(ipc_pktio);
- if (!ret)
- break;
- }
-
- /* packets loop */
- for (;;) {
- int i;
-
- /* 1. exit loop if time specified */
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- EXAMPLE_DBG("exit after %d seconds\n",
- run_time_sec);
- break;
- }
- }
-
- /* 2. Receive packets back from ipc_pktio, validate magic
- * number sequence counter and free that packet
- */
- while (1) {
- pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
- if (pkts <= 0)
- break;
-
- for (i = 0; i < pkts; i++) {
- odp_packet_t pkt = pkt_tbl[i];
- pkt_head_t head;
- pkt_tail_t tail;
- size_t off;
-
- off = odp_packet_l4_offset(pkt);
- if (off == ODP_PACKET_OFFSET_INVALID) {
- stat_errors++;
- stat_free++;
- odp_packet_free(pkt);
- EXAMPLE_ERR("invalid l4 offset\n");
- }
-
- off += ODPH_UDPHDR_LEN;
- ret = odp_packet_copy_to_mem(pkt, off,
- sizeof(head),
- &head);
- if (ret) {
- stat_errors++;
- stat_free++;
- odp_packet_free(pkt);
- EXAMPLE_DBG("error\n");
- continue;
- }
-
- if (head.magic == TEST_ALLOC_MAGIC) {
- stat_free++;
- odp_packet_free(pkt);
- continue;
- }
-
- if (head.magic != TEST_SEQ_MAGIC_2) {
- stat_errors++;
- stat_free++;
- odp_packet_free(pkt);
- EXAMPLE_DBG("error\n");
- continue;
- }
-
- off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
- ret = odp_packet_copy_to_mem(pkt, off,
- sizeof(tail),
- &tail);
- if (ret) {
- stat_errors++;
- stat_free++;
- odp_packet_free(pkt);
- continue;
- }
-
- if (tail.magic != TEST_SEQ_MAGIC) {
- stat_errors++;
- stat_free++;
- odp_packet_free(pkt);
- continue;
- }
-
- cnt_recv++;
-
- if (head.seq != cnt_recv && sync_cnt) {
- stat_errors++;
- odp_packet_free(pkt);
- EXAMPLE_DBG("head.seq %d - "
- "cnt_recv %" PRIu64 ""
- " = %" PRIu64 "\n",
- head.seq, cnt_recv,
- head.seq - cnt_recv);
- cnt_recv = head.seq;
- stat_free++;
- continue;
- }
-
- stat_pkts++;
- odp_packet_free(pkt);
- }
- }
-
- /* 3. emulate that pkts packets were received */
- odp_random_data((uint8_t *)&pkts, sizeof(pkts), 0);
- pkts = ((pkts & 0xffff) % MAX_PKT_BURST) + 1;
-
- for (i = 0; i < pkts; i++) {
- odp_packet_t pkt;
-
- pkt = odp_packet_alloc(pool, SHM_PKT_POOL_BUF_SIZE);
- if (pkt == ODP_PACKET_INVALID)
- break;
-
- stat_pkts_alloc++;
- odp_packet_l4_offset_set(pkt, 30);
- pkt_tbl[i] = pkt;
- }
-
- pkts = i;
-
- /* 4. Copy counter and magic numbers to that packets */
- for (i = 0; i < pkts; i++) {
- pkt_head_t head;
- pkt_tail_t tail;
- size_t off;
- odp_packet_t pkt = pkt_tbl[i];
-
- off = odp_packet_l4_offset(pkt);
- if (off == ODP_PACKET_OFFSET_INVALID)
- EXAMPLE_ABORT("packet L4 offset not set");
-
- head.magic = TEST_SEQ_MAGIC;
- head.seq = cnt++;
-
- off += ODPH_UDPHDR_LEN;
- ret = odp_packet_copy_from_mem(pkt, off, sizeof(head),
- &head);
- if (ret)
- EXAMPLE_ABORT("unable to copy in head data");
-
- tail.magic = TEST_SEQ_MAGIC;
- off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
- ret = odp_packet_copy_from_mem(pkt, off, sizeof(tail),
- &tail);
- if (ret)
- EXAMPLE_ABORT("unable to copy in tail data");
- }
-
- /* 5. Send packets to ipc_pktio */
- ret = ipc_odp_packet_send_or_free(ipc_pktio, pkt_tbl, pkts);
- if (ret < 0) {
- EXAMPLE_DBG("unable to sending to ipc pktio\n");
- break;
- }
-
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, current_cycle);
- if (odp_time_cmp(odp_time_local_from_ns(ODP_TIME_SEC_IN_NS),
- diff) < 0) {
- current_cycle = cycle;
- if (!sync_cnt && stat_errors == (MAX_PKT_BURST + 2)) {
- stat_errors = 0;
- sync_cnt = 1;
- }
- printf("\rpkts: %" PRIu64 ", alloc %" PRIu64 ","
- " errors %" PRIu64 ", pps %" PRIu64 ","
- " free %" PRIu64 ".",
- stat_pkts, stat_pkts_alloc, stat_errors,
- (stat_pkts + stat_pkts_alloc - stat_pkts_prev),
- stat_free);
- fflush(stdout);
- stat_pkts_prev = stat_pkts + stat_pkts_alloc;
- }
- }
-
- /* cleanup and exit */
- ret = odp_pktio_stop(ipc_pktio);
- if (ret) {
- EXAMPLE_DBG("odp_pktio_stop error %d\n", ret);
- return -1;
- }
-
-exit:
- ret = odp_pktio_close(ipc_pktio);
- if (ret) {
- EXAMPLE_DBG("odp_pktio_close error %d\n", ret);
- return -1;
- }
-
- return (stat_errors || stat_pkts < 1000) ? -1 : 0;
-}
-
-/**
- * ODP packet example main function
- */
-int main(int argc, char *argv[])
-{
- odp_pool_t pool;
- odp_pool_param_t params;
- odp_instance_t instance;
- int ret;
- cpu_set_t cpu_set;
- odp_cpumask_t mask;
- int cpu;
- pid_t pid;
-
- /* Parse and store the application arguments */
- parse_args(argc, argv);
-
- /* Init ODP before calling anything else */
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- odp_cpumask_default_worker(&mask, 0);
- cpu = odp_cpumask_first(&mask);
-
- CPU_ZERO(&cpu_set);
- CPU_SET(cpu, &cpu_set);
-
- pid = getpid();
-
- if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpu_set)) {
- printf("Set CPU affinity failed.\n");
- return -1;
- }
-
- printf("ipc_pktio1 %d run on cpu %d\n", pid, cpu);
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_WORKER)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* Print both system and application information */
- print_info(NO_PATH(argv[0]));
-
- /* Create packet pool */
- memset(&params, 0, sizeof(params));
- params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_SIZE;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create(TEST_IPC_POOL_NAME, &params);
- if (pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- odp_pool_print(pool);
-
- ret = pktio_run_loop(pool);
-
- if (odp_pool_destroy(pool)) {
- EXAMPLE_ERR("Error: odp_pool_destroy() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_local()) {
- EXAMPLE_ERR("Error: odp_term_local() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- EXAMPLE_ERR("Error: odp_term_global() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- EXAMPLE_DBG("return %d\n", ret);
- return ret;
-}
diff --git a/test/linux-generic/pktio_ipc/pktio_ipc2.c b/test/linux-generic/pktio_ipc/pktio_ipc2.c
deleted file mode 100644
index 95d69e80b..000000000
--- a/test/linux-generic/pktio_ipc/pktio_ipc2.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/* Copyright (c) 2015, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example pktio_ipc2.c ODP IPC example application.
- * This application works in pair with pktio_ipc1 application.
- * It opens ipc pktio, reads packets and updates magic number.
- * Also it allocates some packets from internal pool and sends
- * to ipc pktio.
- */
-
-#include "ipc_common.h"
-
-static int ipc_second_process(int master_pid)
-{
- odp_pktio_t ipc_pktio = ODP_PKTIO_INVALID;
- odp_pool_param_t params;
- odp_pool_t pool;
- odp_packet_t pkt_tbl[MAX_PKT_BURST];
- odp_packet_t alloc_pkt;
- int pkts;
- int ret;
- int i;
- odp_time_t start_cycle;
- odp_time_t cycle;
- odp_time_t diff;
- odp_time_t wait;
- uint64_t stat_pkts = 0;
- odp_pktin_queue_t pktin;
-
- /* Create packet pool */
- memset(&params, 0, sizeof(params));
- params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
- params.pkt.num = SHM_PKT_POOL_SIZE;
- params.type = ODP_POOL_PACKET;
-
- pool = odp_pool_create(TEST_IPC_POOL_NAME, &params);
- if (pool == ODP_POOL_INVALID) {
- EXAMPLE_ERR("Error: packet pool create failed.\n");
- exit(EXIT_FAILURE);
- }
-
- wait = odp_time_local_from_ns(run_time_sec * ODP_TIME_SEC_IN_NS);
- start_cycle = odp_time_local();
-
- for (;;) {
- /* exit loop if time specified */
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- printf("timeout exit, run_time_sec %d\n",
- run_time_sec);
- goto not_started;
- }
- }
-
- ipc_pktio = create_pktio(pool, master_pid);
- if (ipc_pktio != ODP_PKTIO_INVALID)
- break;
- if (!master_pid)
- break;
- }
-
- if (ipc_pktio == ODP_PKTIO_INVALID) {
- odp_pool_destroy(pool);
- return -1;
- }
-
- if (odp_pktin_queue(ipc_pktio, &pktin, 1) != 1) {
- odp_pool_destroy(pool);
- EXAMPLE_ERR("no input queue\n");
- return -1;
- }
-
- /* start ipc pktio, i.e. wait until other process connects */
- for (;;) {
- /* 1. exit loop if time specified */
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- printf("timeout exit, run_time_sec %d\n",
- run_time_sec);
- goto not_started;
- }
- }
-
- ret = odp_pktio_start(ipc_pktio);
- if (!ret)
- break;
- }
-
- for (;;) {
- /* exit loop if time specified */
- if (run_time_sec) {
- cycle = odp_time_local();
- diff = odp_time_diff(cycle, start_cycle);
- if (odp_time_cmp(wait, diff) < 0) {
- EXAMPLE_DBG("exit after %d seconds\n",
- run_time_sec);
- break;
- }
- }
-
- /* recv some packets and change MAGIC to MAGIC_2 */
- pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
- if (pkts <= 0)
- continue;
-
- for (i = 0; i < pkts; i++) {
- odp_packet_t pkt = pkt_tbl[i];
- pkt_head_t head;
- size_t off;
-
- off = odp_packet_l4_offset(pkt);
- if (off == ODP_PACKET_OFFSET_INVALID) {
- EXAMPLE_ERR("invalid l4 offset\n");
- for (int j = i; j < pkts; j++)
- odp_packet_free(pkt_tbl[j]);
- break;
- }
-
- off += ODPH_UDPHDR_LEN;
- ret = odp_packet_copy_to_mem(pkt, off, sizeof(head),
- &head);
- if (ret)
- EXAMPLE_ABORT("unable copy out head data");
-
- if (head.magic != TEST_SEQ_MAGIC) {
- EXAMPLE_ERR("Wrong head magic! %x", head.magic);
- for (int j = i; j < pkts; j++)
- odp_packet_free(pkt_tbl[j]);
- break;
- }
-
- /* Modify magic number in packet */
- head.magic = TEST_SEQ_MAGIC_2;
- ret = odp_packet_copy_from_mem(pkt, off, sizeof(head),
- &head);
- if (ret)
- EXAMPLE_ABORT("unable to copy in head data");
- }
-
- /* send all packets back */
- ret = ipc_odp_packet_send_or_free(ipc_pktio, pkt_tbl, i);
- if (ret < 0)
- EXAMPLE_ABORT("can not send packets\n");
-
- stat_pkts += ret;
-
- /* alloc packet from local pool, set magic to ALLOC_MAGIC,
- * and send it.*/
- alloc_pkt = odp_packet_alloc(pool, SHM_PKT_POOL_BUF_SIZE);
- if (alloc_pkt != ODP_PACKET_INVALID) {
- pkt_head_t head;
- size_t off;
-
- odp_packet_l4_offset_set(alloc_pkt, 30);
-
- head.magic = TEST_ALLOC_MAGIC;
-
- off = odp_packet_l4_offset(alloc_pkt);
- off += ODPH_UDPHDR_LEN;
- ret = odp_packet_copy_from_mem(alloc_pkt, off,
- sizeof(head),
- &head);
- if (ret)
- EXAMPLE_ABORT("unable to copy in head data");
-
- pkt_tbl[0] = alloc_pkt;
- ret = ipc_odp_packet_send_or_free(ipc_pktio,
- pkt_tbl, 1);
- if (ret < 0)
- EXAMPLE_ABORT("can not send packets\n");
- stat_pkts += 1;
- }
- }
-
- /* cleanup and exit */
- ret = odp_pktio_stop(ipc_pktio);
- if (ret) {
- EXAMPLE_DBG("ipc2: odp_pktio_stop error %d\n", ret);
- return -1;
- }
-
-not_started:
- ret = odp_pktio_close(ipc_pktio);
- if (ret) {
- EXAMPLE_DBG("ipc2: odp_pktio_close error %d\n", ret);
- return -1;
- }
-
- ret = odp_pool_destroy(pool);
- if (ret)
- EXAMPLE_DBG("ipc2: pool_destroy error %d\n", ret);
-
- return stat_pkts > 1000 ? 0 : -1;
-}
-
-int main(int argc, char *argv[])
-{
- odp_instance_t instance;
- int ret;
- cpu_set_t cpu_set;
- odp_cpumask_t mask;
- int cpu;
- pid_t pid;
-
- /* Parse and store the application arguments */
- parse_args(argc, argv);
-
- if (odp_init_global(&instance, NULL, NULL)) {
- EXAMPLE_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- odp_cpumask_default_worker(&mask, 0);
- cpu = odp_cpumask_first(&mask);
- ret = odp_cpumask_next(&mask, cpu);
- if (ret != -1)
- cpu = ret;
-
- CPU_ZERO(&cpu_set);
- CPU_SET(cpu, &cpu_set);
-
- pid = getpid();
-
- if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpu_set)) {
- printf("Set CPU affinity failed to cpu %d.\n", cpu);
- return -1;
- }
-
- printf("ipc_pktio2 %d run on cpu %d\n", pid, cpu);
-
- /* Init this thread */
- if (odp_init_local(instance, ODP_THREAD_WORKER)) {
- EXAMPLE_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- ret = ipc_second_process(master_pid);
-
- if (odp_term_local()) {
- EXAMPLE_ERR("Error: odp_term_local() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- EXAMPLE_ERR("Error: odp_term_global() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- return ret;
-}
diff --git a/test/linux-generic/pktio_ipc/pktio_ipc_run.sh b/test/linux-generic/pktio_ipc/pktio_ipc_run.sh
deleted file mode 100755
index b713c63cd..000000000
--- a/test/linux-generic/pktio_ipc/pktio_ipc_run.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# directories where test binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./pktio_ipc_run) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=./pktio_ipc:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../platform/linux-generic/test/pktio_ipc:$PATH
-PATH=.:$PATH
-
-RUNTIME1=10
-RUNTIME2=5
-TIMEOUT=13
-if [ "${TEST}" = "coverage" ]; then
- RUNTIME1=30
- RUNTIME2=15
- TIMEOUT=20
-fi
-
-run()
-{
- local ret=0
-
- echo "==== run pktio_ipc1 then pktio_ipc2 ===="
- pktio_ipc1${EXEEXT} -t ${RUNTIME1} &
- IPC_PID=$!
-
- pktio_ipc2${EXEEXT} -p ${IPC_PID} -t ${RUNTIME2}
- ret=$?
- # pktio_ipc1 should do clean up and exit just
- # after pktio_ipc2 exited. If it does not happen
- # kill him in test.
- sleep ${TIMEOUT}
- (kill ${IPC_PID} 2>&1 > /dev/null ) > /dev/null
- if [ $? -eq 0 ]; then
- echo "pktio_ipc1${EXEEXT} was killed"
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
- else
- echo "normal exit of 2 application"
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- fi
-
- if [ $ret -ne 0 ]; then
- echo "!!!First stage FAILED $ret!!!"
- exit $ret
- else
- echo "First stage PASSED"
- fi
-
- echo "==== run pktio_ipc2 then pktio_ipc1 ===="
- pktio_ipc2${EXEEXT} -t ${RUNTIME1} &
- IPC_PID=$!
-
- pktio_ipc1${EXEEXT} -p ${IPC_PID} -t ${RUNTIME2}
- ret=$?
- # pktio_ipc2 do not exit on pktio_ipc1 disconnect
- # wait until it exits cleanly
- sleep ${TIMEOUT}
- (kill ${IPC_PID} 2>&1 > /dev/null ) > /dev/null
- if [ $? -eq 0 ]; then
- echo "pktio_ipc2${EXEEXT} was killed"
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
- else
- echo "normal exit of 2 application"
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- fi
-
- if [ $ret -ne 0 ]; then
- echo "!!! FAILED !!!"
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- rm -rf /dev/shm/${UID}/odp-${IPC_PID}* 2>&1 > /dev/null
- exit $ret
- else
- ls -l /dev/shm/${UID}/odp* 2> /dev/null
- echo "Second stage PASSED"
- fi
-
- echo "!!!PASSED!!!"
- exit 0
-}
-
-case "$1" in
- *) run ;;
-esac
diff --git a/test/linux-generic/ring/.gitignore b/test/linux-generic/ring/.gitignore
deleted file mode 100644
index 7341a340c..000000000
--- a/test/linux-generic/ring/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-ring_main
diff --git a/test/linux-generic/ring/Makefile.am b/test/linux-generic/ring/Makefile.am
deleted file mode 100644
index c08658482..000000000
--- a/test/linux-generic/ring/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-include ../Makefile.inc
-
-noinst_LTLIBRARIES = libtestring.la
-libtestring_la_SOURCES = ring_suites.c ring_basic.c ring_stress.c
-libtestring_la_CFLAGS = $(AM_CFLAGS) $(INCCUNIT_COMMON) $(INCODP)
-
-test_PROGRAMS = ring_main$(EXEEXT)
-dist_ring_main_SOURCES = ring_main.c
-
-ring_main_LDFLAGS = $(AM_LDFLAGS)
-ring_main_LDADD = libtestring.la $(LIBCUNIT_COMMON) $(LIBODP)
-
-noinst_HEADERS = ring_suites.h
-
diff --git a/test/linux-generic/ring/ring_basic.c b/test/linux-generic/ring/ring_basic.c
deleted file mode 100644
index 926dc465d..000000000
--- a/test/linux-generic/ring/ring_basic.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP ring basic test
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <test_debug.h>
-#include <odp_cunit_common.h>
-#include <odp_packet_io_ring_internal.h>
-
-#include "ring_suites.h"
-
-/* labor functions declaration */
-static void __do_basic_burst(_ring_t *r);
-static void __do_basic_bulk(_ring_t *r);
-static void __do_basic_watermark(_ring_t *r);
-
-/* dummy object pointers for enqueue and dequeue testing */
-static void **test_enq_data;
-static void **test_deq_data;
-
-/* create two rings: one for single thread usage scenario
- * and another for multiple thread usage scenario.
- * st - single thread usage scenario
- * mt - multiple thread usage scenario
- */
-static const char *st_ring_name = "ST basic ring";
-static const char *mt_ring_name = "MT basic ring";
-static _ring_t *st_ring, *mt_ring;
-
-int ring_test_basic_start(void)
-{
- int i = 0;
-
- /* alloc dummy object pointers for enqueue testing */
- test_enq_data = malloc(RING_SIZE * 2 * sizeof(void *));
- if (NULL == test_enq_data) {
- LOG_ERR("failed to allocate basic test enqeue data\n");
- return -1;
- }
-
- for (i = 0; i < RING_SIZE * 2; i++)
- test_enq_data[i] = (void *)(unsigned long)i;
-
- /* alloc dummy object pointers for dequeue testing */
- test_deq_data = malloc(RING_SIZE * 2 * sizeof(void *));
- if (NULL == test_deq_data) {
- LOG_ERR("failed to allocate basic test dequeue data\n");
- free(test_enq_data); test_enq_data = NULL;
- return -1;
- }
-
- memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
- return 0;
-}
-
-int ring_test_basic_end(void)
-{
- _ring_destroy(st_ring_name);
- _ring_destroy(mt_ring_name);
-
- free(test_enq_data);
- free(test_deq_data);
- return 0;
-}
-
-/* basic test cases */
-void ring_test_basic_create(void)
-{
- /* prove illegal size shall fail */
- st_ring = _ring_create(st_ring_name, ILLEGAL_SIZE, 0);
- CU_ASSERT(NULL == st_ring);
- CU_ASSERT(EINVAL == __odp_errno);
-
- /* create ring for single thread usage scenario */
- st_ring = _ring_create(st_ring_name, RING_SIZE,
- _RING_F_SP_ENQ | _RING_F_SC_DEQ);
-
- CU_ASSERT(NULL != st_ring);
- CU_ASSERT(_ring_lookup(st_ring_name) == st_ring);
-
- /* create ring for multiple thread usage scenario */
- mt_ring = _ring_create(mt_ring_name, RING_SIZE,
- _RING_SHM_PROC);
-
- CU_ASSERT(NULL != mt_ring);
- CU_ASSERT(_ring_lookup(mt_ring_name) == mt_ring);
-}
-
-void ring_test_basic_burst(void)
-{
- /* two rounds to cover both single
- * thread and multiple thread APIs
- */
- __do_basic_burst(st_ring);
- __do_basic_burst(mt_ring);
-}
-
-void ring_test_basic_bulk(void)
-{
- __do_basic_bulk(st_ring);
- __do_basic_bulk(mt_ring);
-}
-
-void ring_test_basic_watermark(void)
-{
- __do_basic_watermark(st_ring);
- __do_basic_watermark(mt_ring);
-}
-
-/* labor functions definition */
-static void __do_basic_burst(_ring_t *r)
-{
- int result = 0;
- unsigned int count = 0;
- void * const *source = test_enq_data;
- void * const *dest = test_deq_data;
- void **enq = NULL, **deq = NULL;
-
- enq = test_enq_data; deq = test_deq_data;
-
- /* ring is empty */
- CU_ASSERT(1 == _ring_empty(r));
-
- /* enqueue 1 object */
- result = _ring_enqueue_burst(r, enq, 1);
- enq += 1;
- CU_ASSERT(1 == (result & _RING_SZ_MASK));
-
- /* enqueue 2 objects */
- result = _ring_enqueue_burst(r, enq, 2);
- enq += 2;
- CU_ASSERT(2 == (result & _RING_SZ_MASK));
-
- /* enqueue HALF_BULK objects */
- result = _ring_enqueue_burst(r, enq, HALF_BULK);
- enq += HALF_BULK;
- CU_ASSERT(HALF_BULK == (result & _RING_SZ_MASK));
-
- /* ring is neither empty nor full */
- CU_ASSERT(0 == _ring_full(r));
- CU_ASSERT(0 == _ring_empty(r));
-
- /* _ring_count() equals enqueued */
- count = (1 + 2 + HALF_BULK);
- CU_ASSERT(count == _ring_count(r));
- /* _ring_free_count() equals rooms left */
- count = (RING_SIZE - 1) - count;
- CU_ASSERT(count == _ring_free_count(r));
-
- /* exceed the size, enquene as many as possible */
- result = _ring_enqueue_burst(r, enq, HALF_BULK);
- enq += count;
- CU_ASSERT(count == (result & _RING_SZ_MASK));
- CU_ASSERT(1 == _ring_full(r));
-
- /* dequeue 1 object */
- result = _ring_dequeue_burst(r, deq, 1);
- deq += 1;
- CU_ASSERT(1 == (result & _RING_SZ_MASK));
-
- /* dequeue 2 objects */
- result = _ring_dequeue_burst(r, deq, 2);
- deq += 2;
- CU_ASSERT(2 == (result & _RING_SZ_MASK));
-
- /* dequeue HALF_BULK objects */
- result = _ring_dequeue_burst(r, deq, HALF_BULK);
- deq += HALF_BULK;
- CU_ASSERT(HALF_BULK == (result & _RING_SZ_MASK));
-
- /* _ring_free_count() equals dequeued */
- count = (1 + 2 + HALF_BULK);
- CU_ASSERT(count == _ring_free_count(r));
- /* _ring_count() equals remained left */
- count = (RING_SIZE - 1) - count;
- CU_ASSERT(count == _ring_count(r));
-
- /* underrun the size, dequeue as many as possible */
- result = _ring_dequeue_burst(r, deq, HALF_BULK);
- deq += count;
- CU_ASSERT(count == (result & _RING_SZ_MASK));
- CU_ASSERT(1 == _ring_empty(r));
-
- /* check data */
- CU_ASSERT(0 == memcmp(source, dest, deq - dest));
-
- /* reset dequeue data */
- memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
-}
-
-/* incomplete ring API set: strange!
- * complement _ring_enqueue/dequeue_bulk to improve coverage
- */
-static inline int __ring_enqueue_bulk(
- _ring_t *r, void * const *objects, unsigned bulk)
-{
- if (r->prod.sp_enqueue)
- return _ring_sp_enqueue_bulk(r, objects, bulk);
- else
- return _ring_mp_enqueue_bulk(r, objects, bulk);
-}
-
-static inline int __ring_dequeue_bulk(
- _ring_t *r, void **objects, unsigned bulk)
-{
- if (r->cons.sc_dequeue)
- return _ring_sc_dequeue_bulk(r, objects, bulk);
- else
- return _ring_mc_dequeue_bulk(r, objects, bulk);
-}
-
-static void __do_basic_bulk(_ring_t *r)
-{
- int result = 0;
- unsigned int count = 0;
- void * const *source = test_enq_data;
- void * const *dest = test_deq_data;
- void **enq = NULL, **deq = NULL;
-
- enq = test_enq_data; deq = test_deq_data;
-
- /* ring is empty */
- CU_ASSERT(1 == _ring_empty(r));
-
- /* enqueue 1 object */
- result = __ring_enqueue_bulk(r, enq, 1);
- enq += 1;
- CU_ASSERT(0 == result);
-
- /* enqueue 2 objects */
- result = __ring_enqueue_bulk(r, enq, 2);
- enq += 2;
- CU_ASSERT(0 == result);
-
- /* enqueue HALF_BULK objects */
- result = __ring_enqueue_bulk(r, enq, HALF_BULK);
- enq += HALF_BULK;
- CU_ASSERT(0 == result);
-
- /* ring is neither empty nor full */
- CU_ASSERT(0 == _ring_full(r));
- CU_ASSERT(0 == _ring_empty(r));
-
- /* _ring_count() equals enqueued */
- count = (1 + 2 + HALF_BULK);
- CU_ASSERT(count == _ring_count(r));
- /* _ring_free_count() equals rooms left */
- count = (RING_SIZE - 1) - count;
- CU_ASSERT(count == _ring_free_count(r));
-
- /* exceed the size, enquene shall fail with -ENOBUFS */
- result = __ring_enqueue_bulk(r, enq, HALF_BULK);
- CU_ASSERT(-ENOBUFS == result);
-
- /* fullful the ring */
- result = __ring_enqueue_bulk(r, enq, count);
- enq += count;
- CU_ASSERT(0 == result);
- CU_ASSERT(1 == _ring_full(r));
-
- /* dequeue 1 object */
- result = __ring_dequeue_bulk(r, deq, 1);
- deq += 1;
- CU_ASSERT(0 == result);
-
- /* dequeue 2 objects */
- result = __ring_dequeue_bulk(r, deq, 2);
- deq += 2;
- CU_ASSERT(0 == result);
-
- /* dequeue HALF_BULK objects */
- result = __ring_dequeue_bulk(r, deq, HALF_BULK);
- deq += HALF_BULK;
- CU_ASSERT(0 == result);
-
- /* _ring_free_count() equals dequeued */
- count = (1 + 2 + HALF_BULK);
- CU_ASSERT(count == _ring_free_count(r));
- /* _ring_count() equals remained left */
- count = (RING_SIZE - 1) - count;
- CU_ASSERT(count == _ring_count(r));
-
- /* underrun the size, dequeue shall fail with -ENOENT */
- result = __ring_dequeue_bulk(r, deq, HALF_BULK);
- CU_ASSERT(-ENOENT == result);
-
- /* empty the queue */
- result = __ring_dequeue_bulk(r, deq, count);
- deq += count;
- CU_ASSERT(0 == result);
- CU_ASSERT(1 == _ring_empty(r));
-
- /* check data */
- CU_ASSERT(0 == memcmp(source, dest, deq - dest));
-
- /* reset dequeue data */
- memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
-}
-
-void __do_basic_watermark(_ring_t *r)
-{
- int result = 0;
- void * const *source = test_enq_data;
- void * const *dest = test_deq_data;
- void **enq = NULL, **deq = NULL;
-
- enq = test_enq_data; deq = test_deq_data;
-
- /* bulk = 3/4 watermark to trigger alarm on 2nd enqueue */
- const unsigned watermark = PIECE_BULK;
- const unsigned bulk = (watermark / 4) * 3;
-
- /* watermark cannot exceed ring size */
- result = _ring_set_water_mark(r, ILLEGAL_SIZE);
- CU_ASSERT(-EINVAL == result);
-
- /* set watermark */
- result = _ring_set_water_mark(r, watermark);
- CU_ASSERT(0 == result);
-
- /* 1st enqueue shall succeed */
- result = __ring_enqueue_bulk(r, enq, bulk);
- enq += bulk;
- CU_ASSERT(0 == result);
-
- /* 2nd enqueue shall succeed but return -EDQUOT */
- result = __ring_enqueue_bulk(r, enq, bulk);
- enq += bulk;
- CU_ASSERT(-EDQUOT == result);
-
- /* dequeue 1st bulk */
- result = __ring_dequeue_bulk(r, deq, bulk);
- deq += bulk;
- CU_ASSERT(0 == result);
-
- /* dequeue 2nd bulk */
- result = __ring_dequeue_bulk(r, deq, bulk);
- deq += bulk;
- CU_ASSERT(0 == result);
-
- /* check data */
- CU_ASSERT(0 == memcmp(source, dest, deq - dest));
-
- /* reset watermark */
- result = _ring_set_water_mark(r, 0);
- CU_ASSERT(0 == result);
-
- /* reset dequeue data */
- memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
-}
diff --git a/test/linux-generic/ring/ring_main.c b/test/linux-generic/ring/ring_main.c
deleted file mode 100644
index 715268843..000000000
--- a/test/linux-generic/ring/ring_main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "ring_suites.h"
-
-int main(int argc, char *argv[])
-{
- return ring_suites_main(argc, argv);
-}
diff --git a/test/linux-generic/ring/ring_stress.c b/test/linux-generic/ring/ring_stress.c
deleted file mode 100644
index b6ddb34e3..000000000
--- a/test/linux-generic/ring/ring_stress.c
+++ /dev/null
@@ -1,244 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/**
- * @file
- *
- * ODP ring stress test
- */
-
-#define _GNU_SOURCE
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <odp_api.h>
-#include <odp/helper/odph_api.h>
-#include <odp_packet_io_ring_internal.h>
-#include <test_debug.h>
-#include <odp_cunit_common.h>
-
-#include "ring_suites.h"
-
-/* There's even number of producer and consumer threads and each thread does
- * this many successful enq or deq operations */
-#define NUM_BULK_OP ((RING_SIZE / PIECE_BULK) * 100)
-
-/*
- * Since cunit framework cannot work with multi-threading, ask workers
- * to save their results for delayed assertion after thread collection.
- */
-static int worker_results[MAX_WORKERS];
-
-/*
- * Note : make sure that both enqueue and dequeue
- * operation starts at same time so to avoid data corruption
- * Its because atomic lock will protect only indexes, but if order of
- * read or write operation incorrect then data mismatch will happen
- * So its resposibility of application develop to take care of order of
- * data read or write.
- */
-typedef enum {
- STRESS_1_1_PRODUCER_CONSUMER,
- STRESS_1_N_PRODUCER_CONSUMER,
- STRESS_N_1_PRODUCER_CONSUMER,
- STRESS_N_M_PRODUCER_CONSUMER
-} stress_case_t;
-
-/* worker function declarations */
-static int stress_worker(void *_data);
-
-/* global name for later look up in workers' context */
-static const char *ring_name = "stress_ring";
-
-/* barrier to run threads at the same time */
-static odp_barrier_t barrier;
-
-int ring_test_stress_start(void)
-{
- _ring_t *r_stress = NULL;
-
- /* multiple thread usage scenario, thread or process sharable */
- r_stress = _ring_create(ring_name, RING_SIZE, _RING_SHM_PROC);
- if (r_stress == NULL) {
- LOG_ERR("create ring failed for stress.\n");
- return -1;
- }
-
- return 0;
-}
-
-int ring_test_stress_end(void)
-{
- _ring_destroy(ring_name);
- return 0;
-}
-
-void ring_test_stress_1_1_producer_consumer(void)
-{
- int i = 0;
- odp_cpumask_t cpus;
- pthrd_arg worker_param;
-
- /* reset results for delayed assertion */
- memset(worker_results, 0, sizeof(worker_results));
-
- /* request 2 threads to run 1:1 stress */
- worker_param.numthrds = odp_cpumask_default_worker(&cpus, 2);
- worker_param.testcase = STRESS_1_1_PRODUCER_CONSUMER;
-
- /* not failure, insufficient resource */
- if (worker_param.numthrds < 2) {
- LOG_ERR("insufficient cpu for 1:1 "
- "producer/consumer stress.\n");
- return;
- }
-
- odp_barrier_init(&barrier, 2);
-
- /* kick the workers */
- odp_cunit_thread_create(stress_worker, &worker_param);
-
- /* collect the results */
- odp_cunit_thread_exit(&worker_param);
-
- /* delayed assertion due to cunit limitation */
- for (i = 0; i < worker_param.numthrds; i++)
- CU_ASSERT(0 == worker_results[i]);
-}
-
-void ring_test_stress_N_M_producer_consumer(void)
-{
- int i = 0;
- odp_cpumask_t cpus;
- pthrd_arg worker_param;
-
- /* reset results for delayed assertion */
- memset(worker_results, 0, sizeof(worker_results));
-
- /* request MAX_WORKERS threads to run N:M stress */
- worker_param.numthrds =
- odp_cpumask_default_worker(&cpus, MAX_WORKERS);
- worker_param.testcase = STRESS_N_M_PRODUCER_CONSUMER;
-
- /* not failure, insufficient resource */
- if (worker_param.numthrds < 3) {
- LOG_ERR("insufficient cpu for N:M "
- "producer/consumer stress.\n");
- return;
- }
-
- /* force even number of threads */
- if (worker_param.numthrds & 0x1)
- worker_param.numthrds -= 1;
-
- odp_barrier_init(&barrier, worker_param.numthrds);
-
- /* kick the workers */
- odp_cunit_thread_create(stress_worker, &worker_param);
-
- /* collect the results */
- odp_cunit_thread_exit(&worker_param);
-
- /* delayed assertion due to cunit limitation */
- for (i = 0; i < worker_param.numthrds; i++)
- CU_ASSERT(0 == worker_results[i]);
-}
-
-void ring_test_stress_1_N_producer_consumer(void)
-{
-}
-
-void ring_test_stress_N_1_producer_consumer(void)
-{
-}
-
-void ring_test_stress_ring_list_dump(void)
-{
- /* improve code coverage */
- _ring_list_dump();
-}
-
-/* worker function for multiple producer instances */
-static int do_producer(_ring_t *r)
-{
- void *enq[PIECE_BULK];
- int i;
- int num = NUM_BULK_OP;
-
- /* data pattern to be evaluated later in consumer */
- for (i = 0; i < PIECE_BULK; i++)
- enq[i] = (void *)(uintptr_t)i;
-
- while (num)
- if (_ring_mp_enqueue_bulk(r, enq, PIECE_BULK) == 0)
- num--;
-
- return 0;
-}
-
-/* worker function for multiple consumer instances */
-static int do_consumer(_ring_t *r)
-{
- void *deq[PIECE_BULK];
- int i;
- int num = NUM_BULK_OP;
-
- while (num) {
- if (_ring_mc_dequeue_bulk(r, deq, PIECE_BULK) == 0) {
- num--;
-
- /* evaluate the data pattern */
- for (i = 0; i < PIECE_BULK; i++)
- CU_ASSERT(deq[i] == (void *)(uintptr_t)i);
- }
- }
-
- return 0;
-}
-
-static int stress_worker(void *_data)
-{
- pthrd_arg *worker_param = (pthrd_arg *)_data;
- _ring_t *r_stress = NULL;
- int *result = NULL;
- int worker_id = odp_thread_id();
-
- /* save the worker result for delayed assertion */
- result = &worker_results[(worker_id % worker_param->numthrds)];
-
- /* verify ring lookup in worker context */
- r_stress = _ring_lookup(ring_name);
- if (NULL == r_stress) {
- LOG_ERR("ring lookup %s not found\n", ring_name);
- return (*result = -1);
- }
-
- odp_barrier_wait(&barrier);
-
- switch (worker_param->testcase) {
- case STRESS_1_1_PRODUCER_CONSUMER:
- case STRESS_N_M_PRODUCER_CONSUMER:
- /* interleaved producer/consumer */
- if (0 == (worker_id % 2))
- *result = do_producer(r_stress);
- else if (1 == (worker_id % 2))
- *result = do_consumer(r_stress);
- break;
- case STRESS_1_N_PRODUCER_CONSUMER:
- case STRESS_N_1_PRODUCER_CONSUMER:
- default:
- LOG_ERR("invalid or not-implemented stress type (%d)\n",
- worker_param->testcase);
- break;
- }
-
- odp_barrier_wait(&barrier);
-
- return 0;
-}
diff --git a/test/linux-generic/ring/ring_suites.c b/test/linux-generic/ring/ring_suites.c
deleted file mode 100644
index f321a762a..000000000
--- a/test/linux-generic/ring/ring_suites.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <odp_api.h>
-#include <test_debug.h>
-#include <odp_cunit_common.h>
-#include <odp_packet_io_ring_internal.h>
-
-#include "ring_suites.h"
-
-static int ring_suites_init(odp_instance_t *inst)
-{
- if (0 != odp_init_global(inst, NULL, NULL)) {
- LOG_ERR("error: odp_init_global() failed.\n");
- return -1;
- }
- if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
- LOG_ERR("error: odp_init_local() failed.\n");
- return -1;
- }
-
- _ring_tailq_init();
- return 0;
-}
-
-static odp_testinfo_t ring_suite_basic[] = {
- ODP_TEST_INFO(ring_test_basic_create),
- ODP_TEST_INFO(ring_test_basic_burst),
- ODP_TEST_INFO(ring_test_basic_bulk),
- ODP_TEST_INFO(ring_test_basic_watermark),
- ODP_TEST_INFO_NULL,
-};
-
-static odp_testinfo_t ring_suite_stress[] = {
- ODP_TEST_INFO(ring_test_stress_1_1_producer_consumer),
- ODP_TEST_INFO(ring_test_stress_1_N_producer_consumer),
- ODP_TEST_INFO(ring_test_stress_N_1_producer_consumer),
- ODP_TEST_INFO(ring_test_stress_N_M_producer_consumer),
- ODP_TEST_INFO(ring_test_stress_ring_list_dump),
- ODP_TEST_INFO_NULL,
-};
-
-static odp_suiteinfo_t ring_suites[] = {
- {"ring basic", ring_test_basic_start,
- ring_test_basic_end, ring_suite_basic},
- {"ring stress", ring_test_stress_start,
- ring_test_stress_end, ring_suite_stress},
- ODP_SUITE_INFO_NULL
-};
-
-int ring_suites_main(int argc, char *argv[])
-{
- int ret;
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- if (odp_cunit_parse_options(argc, argv))
- return -1;
-
- odp_cunit_register_global_init(ring_suites_init);
-
- ret = odp_cunit_register(ring_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/linux-generic/ring/ring_suites.h b/test/linux-generic/ring/ring_suites.h
deleted file mode 100644
index 5fa5b9c52..000000000
--- a/test/linux-generic/ring/ring_suites.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#define RING_SIZE 4096
-#define PIECE_BULK 32
-
-#define HALF_BULK (RING_SIZE >> 1)
-#define ILLEGAL_SIZE (RING_SIZE | 0x3)
-
-/* test suite start and stop */
-int ring_test_basic_start(void);
-int ring_test_basic_end(void);
-
-/* basic test cases */
-void ring_test_basic_create(void);
-void ring_test_basic_burst(void);
-void ring_test_basic_bulk(void);
-void ring_test_basic_watermark(void);
-
-/* test suite start and stop */
-int ring_test_stress_start(void);
-int ring_test_stress_end(void);
-
-/* stress test cases */
-void ring_test_stress_1_1_producer_consumer(void);
-void ring_test_stress_1_N_producer_consumer(void);
-void ring_test_stress_N_1_producer_consumer(void);
-void ring_test_stress_N_M_producer_consumer(void);
-void ring_test_stress_ring_list_dump(void);
-
-int ring_suites_main(int argc, char *argv[]);
diff --git a/test/linux-generic/run-test b/test/linux-generic/run-test
deleted file mode 100755
index 2bff651cc..000000000
--- a/test/linux-generic/run-test
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/bin/bash
-#
-# Run the ODP test applications and report status in a format that
-# matches the automake "make check" output.
-#
-# The list of tests to be run is obtained by sourcing a file that
-# contains an environment variable in the form;
-#
-# TEST="test_app1 test_app2"
-#
-# The default behaviour is to run all the tests defined in files
-# named tests-*.env in the same directory as this script, but a single
-# test definition file can be specified using the TEST_DEF environment
-# variable.
-#
-# Test definition files may optionally also specify a LOG_COMPILER
-# which will be invoked as a wrapper to each of the test application
-# (as per automake).
-#
-TDIR=$(dirname $(readlink -f $0))
-PASS=0
-FAIL=0
-SKIP=0
-res=0
-
-if [ "$V" != "0" ]; then
- verbose=1
-else
- verbose=0
- mkdir -p logs
-fi
-
-do_run_tests() {
- source $1
-
- for tc in $TESTS; do
- tc=$(basename $tc)
- if [ "$verbose" = "0" ]; then
- logfile=logs/${tc}.log
- touch $logfile || logfile=/dev/null
- $LOG_COMPILER $TDIR/$tc > $logfile 2>&1
- else
- $LOG_COMPILER $TDIR/$tc
- fi
-
- tres=$?
- case $tres in
- 0) echo "PASS: $tc"; let PASS=$PASS+1 ;;
- 77) echo "SKIP: $tc"; let SKIP=$SKIP+1 ;;
- *) echo "FAIL: $tc"; let FAIL=$FAIL+1; res=1 ;;
- esac
- done
-}
-
-if [ "$TEST_DEFS" != "" -a -f "$TEST_DEFS" ]; then
- do_run_tests $TEST_DEFS
-elif [ "$1" != "" ]; then
- do_run_tests $TDIR/tests-${1}.env
-else
- for tenv in $TDIR/tests-*.env; do
- do_run_tests $tenv
- done
-fi
-
-echo "TEST RESULT: $PASS tests passed, $SKIP skipped, $FAIL failed"
-
-exit $res
diff --git a/test/linux-generic/validation/Makefile.inc b/test/linux-generic/validation/Makefile.inc
deleted file mode 100644
index cf1dedb9f..000000000
--- a/test/linux-generic/validation/Makefile.inc
+++ /dev/null
@@ -1 +0,0 @@
-include $(top_srcdir)/test/linux-generic/Makefile.inc
diff --git a/test/linux-generic/validation/api/Makefile.inc b/test/linux-generic/validation/api/Makefile.inc
deleted file mode 100644
index 19c9448c0..000000000
--- a/test/linux-generic/validation/api/Makefile.inc
+++ /dev/null
@@ -1 +0,0 @@
-include $(top_srcdir)/test/linux-generic/validation/Makefile.inc
diff --git a/test/linux-generic/validation/api/pktio/.gitignore b/test/linux-generic/validation/api/pktio/.gitignore
deleted file mode 100644
index 7e563b8b3..000000000
--- a/test/linux-generic/validation/api/pktio/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.log
-*.trs
diff --git a/test/linux-generic/validation/api/pktio/Makefile.am b/test/linux-generic/validation/api/pktio/Makefile.am
deleted file mode 100644
index 4a1434397..000000000
--- a/test/linux-generic/validation/api/pktio/Makefile.am
+++ /dev/null
@@ -1,15 +0,0 @@
-dist_check_SCRIPTS = pktio_env \
- pktio_run.sh \
- pktio_run_tap.sh
-
-if HAVE_PCAP
-dist_check_SCRIPTS += pktio_run_pcap.sh
-endif
-if netmap_support
-dist_check_SCRIPTS += pktio_run_netmap.sh
-endif
-if PKTIO_DPDK
-dist_check_SCRIPTS += pktio_run_dpdk.sh
-endif
-
-test_SCRIPTS = $(dist_check_SCRIPTS)
diff --git a/test/linux-generic/validation/api/pktio/pktio_env b/test/linux-generic/validation/api/pktio/pktio_env
deleted file mode 100644
index 345b5bd56..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_env
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-# Test script wrapper for running ODP pktio apps on linux-generic.
-#
-# For linux-generic the default behavior is to create two pairs of
-# virtual Ethernet interfaces and provide the names of these via
-# environment variables to pktio apps, the interfaces will be removed
-# before the script exits.
-#
-# Note that the creation of virtual Ethernet devices depends on having
-# CONFIG_VETH enabled in the kernel, if not enabled the env setup will be skipped.
-#
-# Network set up
-# IF0 <---> IF1
-# IF2 <---> IF3
-IF0=pktiop0p1
-IF1=pktiop1p0
-IF2=pktiop2p3
-IF3=pktiop3p2
-
-if [ "$0" = "$BASH_SOURCE" ]; then
- echo "Error: Platform specific env file has to be sourced."
-fi
-
-check_for_root()
-{
- if [ "$(id -u)" != "0" ]; then
- echo "check_for_root(): need to be root to setup VETH"
- return 1
- fi
- return 0
-}
-
-# wait for a network interface's operational state to be "up"
-wait_for_iface_up()
-{
- iface=$1
- cnt=0
-
- while [ $cnt -lt 50 ]; do
- read operstate < /sys/class/net/$iface/operstate
-
- if [ $? -ne 0 ]; then
- break
- elif [ "$operstate" = "up" ]; then
- return 0
- fi
-
- sleep 0.1
- cnt=`expr $cnt + 1`
- done
-
- return 1
-}
-
-setup_pktio_env()
-{
- echo "pktio: setting up test interfaces $IF0, $IF1, $IF2, $IF3."
-
- check_for_root
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link show $iface 2> /dev/null
- if [ $? -eq 0 ]; then
- echo "pktio: interface $iface already exist $?"
- return 2
- fi
- done
-
- if [ "$1" = "clean" ]; then
- trap cleanup_pktio_env EXIT
- fi
-
- ip link add $IF0 type veth peer name $IF1
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create veth pair"
- return 3
- fi
- ip link add $IF2 type veth peer name $IF3
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create veth pair"
- return 4
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link set $iface mtu 9216 up
- ifconfig $iface -arp
- done
-
- # check that the interface has come up before starting the test
- for iface in $IF0 $IF1 $IF2 $IF3; do
- wait_for_iface_up $iface
- if [ $? -ne 0 ]; then
- echo "pktio: interface $iface failed to come up"
- return 5
- fi
- done
-}
-
-cleanup_pktio_env()
-{
- echo "pktio: removing test interfaces $IF0, $IF1, $IF2, $IF3"
- check_for_root
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- for iface in $IF0 $IF1 $IF2 $IF3; do
- ip link del $iface 2> /dev/null
- done
- return 0
-}
diff --git a/test/linux-generic/validation/api/pktio/pktio_run.sh b/test/linux-generic/validation/api/pktio/pktio_run.sh
deleted file mode 100755
index 19def8c5a..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_run.sh
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# Proceed the pktio tests. This script expects at least one argument:
-# setup) setup the pktio test environment
-# cleanup) cleanup the pktio test environment
-# run) run the pktio tests (setup, run, cleanup)
-# extra arguments are passed unchanged to the test itself (pktio_main)
-# Without arguments, "run" is assumed and no extra argument is passed to the
-# test (legacy mode).
-#
-
-# directories where pktio_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./pktio_run) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/pktio:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/pktio:$PATH
-PATH=.:$PATH
-
-pktio_main_path=$(which pktio_main${EXEEXT})
-if [ -x "$pktio_main_path" ] ; then
- echo "running with pktio_main: $pktio_run_path"
-else
- echo "cannot find pktio_main: please set you PATH for it."
- exit 1
-fi
-
-# directory where platform test sources are, including scripts
-TEST_SRC_DIR=$(dirname $0)
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-# Use installed pktio env or for make check take it from platform directory
-if [ -f "./pktio_env" ]; then
- . ./pktio_env
-elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
- . ${TEST_SRC_DIR}/pktio_env
-else
- echo "BUG: unable to find pktio_env!"
- echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
- echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
- exit 1
-fi
-
-run_test()
-{
- local ret=0
-
- # environment variables are used to control which socket method is
- # used, so try each combination to ensure decent coverage.
- for distype in MMAP MMSG; do
- unset ODP_PKTIO_DISABLE_SOCKET_${distype}
- done
-
- # this script doesn't support testing with netmap
- export ODP_PKTIO_DISABLE_NETMAP=y
-
- for distype in SKIP MMAP; do
- if [ "$disabletype" != "SKIP" ]; then
- export ODP_PKTIO_DISABLE_SOCKET_${distype}=y
- fi
- pktio_main${EXEEXT} $*
- if [ $? -ne 0 ]; then
- ret=1
- fi
- done
-
- if [ $ret -ne 0 ]; then
- echo "!!! FAILED !!!"
- fi
-
- return $ret
-}
-
-run()
-{
- echo "pktio: using 'loop' device"
- pktio_main${EXEEXT} $*
- loop_ret=$?
-
- # need to be root to run tests with real interfaces
- if [ "$(id -u)" != "0" ]; then
- exit $ret
- fi
-
- if [ "$ODP_PKTIO_IF0" = "" ]; then
- # no interfaces specified, use default veth interfaces
- # setup by the pktio_env script
- setup_pktio_env clean
- if [ $? != 0 ]; then
- echo "Failed to setup test environment, skipping test."
- exit $TEST_SKIPPED
- fi
- export ODP_PKTIO_IF0=$IF0
- export ODP_PKTIO_IF1=$IF1
- fi
-
- run_test
- ret=$?
-
- [ $ret = 0 ] && ret=$loop_ret
-
- exit $ret
-}
-
-if [ $# != 0 ]; then
- action=$1
- shift
-fi
-
-case "$action" in
- setup) setup_pktio_env ;;
- cleanup) cleanup_pktio_env ;;
- run) run ;;
- *) run ;;
-esac
diff --git a/test/linux-generic/validation/api/pktio/pktio_run_dpdk.sh b/test/linux-generic/validation/api/pktio/pktio_run_dpdk.sh
deleted file mode 100755
index 3060dc003..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_run_dpdk.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# Proceed the pktio tests. This script expects at least one argument:
-# setup) setup the pktio test environment
-# cleanup) cleanup the pktio test environment
-# run) run the pktio tests (setup, run, cleanup)
-# extra arguments are passed unchanged to the test itself (pktio_main)
-# Without arguments, "run" is assumed and no extra argument is passed to the
-# test (legacy mode).
-#
-
-# directories where pktio_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./pktio_run) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/pktio:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/pktio:$PATH
-PATH=.:$PATH
-
-pktio_main_path=$(which pktio_main${EXEEXT})
-if [ -x "$pktio_main_path" ] ; then
- echo "running with pktio_main: $pktio_run_path"
-else
- echo "cannot find pktio_main: please set you PATH for it."
-fi
-
-# directory where platform test sources are, including scripts
-TEST_SRC_DIR=$(dirname $0)
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-# Use installed pktio env or for make check take it from platform directory
-if [ -f "./pktio_env" ]; then
- . ./pktio_env
-elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
- . ${TEST_SRC_DIR}/pktio_env
-else
- echo "BUG: unable to find pktio_env!"
- echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
- echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
- exit 1
-fi
-
-run_test()
-{
- local ret=0
-
- pktio_main${EXEEXT} $*
- ret=$?
- if [ $ret -ne 0 ]; then
- echo "!!! FAILED !!!"
- fi
-
- exit $ret
-}
-
-run()
-{
- # need to be root to set the interface.
- if [ "$(id -u)" != "0" ]; then
- echo "pktio: need to be root to setup DPDK interfaces."
- return $TEST_SKIPPED
- fi
-
- if [ "$ODP_PKTIO_IF0" = "" ]; then
- setup_pktio_env clean
- export ODP_PKTIO_DPDK_PARAMS="--no-pci --vdev eth_pcap0,iface=$IF0 --vdev eth_pcap1,iface=$IF1"
- export ODP_PKTIO_IF0=0
- export ODP_PKTIO_IF1=1
- fi
-
- run_test
-}
-
-if [ $# != 0 ]; then
- action=$1
- shift
-fi
-
-case "$1" in
- setup) setup_pktio_env ;;
- cleanup) cleanup_pktio_env ;;
- run) run ;;
- *) run ;;
-esac
diff --git a/test/linux-generic/validation/api/pktio/pktio_run_netmap.sh b/test/linux-generic/validation/api/pktio/pktio_run_netmap.sh
deleted file mode 100755
index 7dde7ae1c..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_run_netmap.sh
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# any parameter passed as arguments to this script is passed unchanged to
-# the test itself (pktio_main)
-
-# directories where pktio_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone (./pktio_run) intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/pktio:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/pktio:$PATH
-PATH=.:$PATH
-
-pktio_main_path=$(which pktio_main${EXEEXT})
-if [ -x "$pktio_main_path" ] ; then
- echo "running with pktio_main: $pktio_main_path"
-else
- echo "cannot find pktio_main: please set you PATH for it."
-fi
-
-# directory where platform test sources are, including scripts
-TEST_SRC_DIR=$(dirname $0)
-
-# exit codes expected by automake for skipped tests
-TEST_SKIPPED=77
-
-# Use installed pktio env or for make check take it from the test directory
-if [ -f "./pktio_env" ]; then
- . ./pktio_env
-elif [ -f ${TEST_SRC_DIR}/pktio_env ]; then
- . ${TEST_SRC_DIR}/pktio_env
-else
- echo "ERROR: unable to find pktio_env!"
- echo "pktio_env has to be in current directory or in ${TEST_SRC_DIR}"
- exit 1
-fi
-
-run_test()
-{
- local ret=0
-
- pktio_main${EXEEXT} $*
- ret=$?
-
- if [ $ret -ne 0 ]; then
- echo "!!! FAILED !!!"
- fi
-
- return $ret
-}
-
-run_test_vale()
-{
- # use two vale ports on the same switch
- export ODP_PKTIO_IF0=valetest:0
- export ODP_PKTIO_IF1=valetest:1
- run_test
- return $?
-}
-
-run_test_pipe()
-{
- # use a netmap pipe
- export ODP_PKTIO_IF0=valetest:0{0
- export ODP_PKTIO_IF1=valetest:0}0
- run_test
- return $?
-}
-
-run_test_veth()
-{
- if [ "$(lsmod | grep veth)" = "" ]; then
- echo "netmap enabled veth module not loaded, skipping test."
- return 0
- fi
-
- setup_pktio_env clean
- export ODP_PKTIO_IF0=$IF0
- export ODP_PKTIO_IF1=$IF1
- run_test
- return $?
-}
-
-run()
-{
- local ret=0
-
- # need to be root to run these tests
- if [ "$(id -u)" != "0" ]; then
- echo "netmap tests must be run as root, skipping test."
- exit $TEST_SKIPPED
- fi
-
- if [ "$(lsmod | grep netmap)" = "" ]; then
- echo "netmap kernel module not loaded, skipping test."
- exit $TEST_SKIPPED
- fi
-
- if [ "$ODP_PKTIO_IF0" != "" ]; then
- run_test
- ret=$?
- else
- run_test_vale
- r=$?; [ $ret = 0 ] && ret=$r
- run_test_pipe
- r=$?; [ $ret = 0 ] && ret=$r
- run_test_veth
- r=$?; [ $ret = 0 ] && ret=$r
- fi
-
- exit $ret
-}
-
-run
diff --git a/test/linux-generic/validation/api/pktio/pktio_run_pcap.sh b/test/linux-generic/validation/api/pktio/pktio_run_pcap.sh
deleted file mode 100755
index b5b773548..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_run_pcap.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Linaro Limited
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-# any parameter passed as arguments to this script is passed unchanged to
-# the test itself (pktio_main)
-
-# directories where pktio_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/pktio:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/pktio:$PATH
-PATH=.:$PATH
-
-pktio_main_path=$(which pktio_main${EXEEXT})
-if [ -x "$pktio_main_path" ] ; then
- echo "running with $pktio_main_path"
-else
- echo "cannot find pktio_main${EXEEXT}: please set you PATH for it."
-fi
-
-PCAP_FNAME=vald.pcap
-export ODP_PKTIO_IF0="pcap:out=${PCAP_FNAME}"
-export ODP_PKTIO_IF1="pcap:in=${PCAP_FNAME}"
-pktio_main${EXEEXT} $*
-ret=$?
-rm -f ${PCAP_FNAME}
-exit $ret
diff --git a/test/linux-generic/validation/api/pktio/pktio_run_tap.sh b/test/linux-generic/validation/api/pktio/pktio_run_tap.sh
deleted file mode 100755
index 89579ca68..000000000
--- a/test/linux-generic/validation/api/pktio/pktio_run_tap.sh
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2015, Ilya Maximets <i.maximets@samsung.com>
-# All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-
-# any parameter passed as arguments to this script is passed unchanged to
-# the test itself (pktio_main)
-
-# directories where pktio_main binary can be found:
-# -in the validation dir when running make check (intree or out of tree)
-# -in the script directory, when running after 'make install', or
-# -in the validation when running standalone intree.
-# -in the current directory.
-# running stand alone out of tree requires setting PATH
-PATH=${TEST_DIR}/api/pktio:$PATH
-PATH=$(dirname $0):$PATH
-PATH=$(dirname $0)/../../../../common_plat/validation/api/pktio:$PATH
-PATH=.:$PATH
-
-pktio_main_path=$(which pktio_main${EXEEXT})
-if [ -x "$pktio_main_path" ] ; then
- echo "running with $pktio_main_path"
-else
- echo "cannot find pktio_main${EXEEXT}: please set you PATH for it."
-fi
-
-# exit code expected by automake for skipped tests
-TEST_SKIPPED=77
-
-TAP_BASE_NAME=iotap_vald
-IF0=${TAP_BASE_NAME}0
-IF1=${TAP_BASE_NAME}1
-BR=${TAP_BASE_NAME}_br
-
-export ODP_PKTIO_IF0="tap:$IF0"
-export ODP_PKTIO_IF1="tap:$IF1"
-
-tap_cleanup()
-{
- ret=$?
-
- for iface in $IF0 $IF1; do
- ip link set dev $iface nomaster
- done
-
- ip link delete $BR type bridge
-
- for iface in $IF0 $IF1; do
- ip tuntap del mode tap $iface
- done
-
- trap - EXIT
- exit $ret
-}
-
-tap_setup()
-{
- if [ "$(id -u)" != "0" ]; then
- echo "pktio: need to be root to setup TAP interfaces."
- return $TEST_SKIPPED
- fi
-
- for iface in $IF0 $IF1 $BR; do
- ip link show $iface 2> /dev/null
- if [ $? -eq 0 ]; then
- echo "pktio: interface $iface already exist $?"
- return 2
- fi
- done
-
- trap tap_cleanup EXIT
-
- for iface in $IF0 $IF1; do
- ip tuntap add mode tap $iface
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create TAP device $iface"
- return 3
- fi
- done
-
- ip link add name $BR type bridge
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to create bridge $BR"
- return 3
- fi
-
- for iface in $IF0 $IF1; do
- ip link set dev $iface master $BR
- if [ $? -ne 0 ]; then
- echo "pktio: error: unable to add $iface to bridge $BR"
- return 4
- fi
- done
-
- for iface in $IF0 $IF1 $BR; do
- ifconfig $iface -arp
- sysctl -w net.ipv6.conf.${iface}.disable_ipv6=1
- ip link set dev $iface mtu 9216 up
- done
-
- return 0
-}
-
-tap_setup
-ret=$?
-if [ $ret -ne 0 ]; then
- echo "pktio: tap_setup() FAILED!"
- exit $TEST_SKIPPED
-fi
-
-# Using ODP_WAIT_FOR_NETWORK to prevent fail if tap still not enabled in bridge
-ODP_WAIT_FOR_NETWORK=yes pktio_main${EXEEXT} $*
-ret=$?
-
-exit $ret
diff --git a/test/linux-generic/validation/api/shmem/Makefile.am b/test/linux-generic/validation/api/shmem/Makefile.am
deleted file mode 100644
index b0ae62738..000000000
--- a/test/linux-generic/validation/api/shmem/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-include ../Makefile.inc
-
-#the main test program is shmem_linux, which, in turn, starts a shmem_odp:
-test_PROGRAMS = shmem_linux$(EXEEXT)
-test_extra_PROGRAMS = shmem_odp1$(EXEEXT) shmem_odp2$(EXEEXT)
-test_extradir = $(testdir)
-
-#shmem_linux is stand alone, pure linux (no ODP):
-dist_shmem_linux_SOURCES = shmem_linux.c
-shmem_linux_LDFLAGS = $(AM_LDFLAGS) -lrt
-
-#shmem_odp1 and shmem_odp2 are the 2 ODP processes:
-dist_shmem_odp1_SOURCES = shmem_odp1.c
-shmem_odp1_CFLAGS = $(AM_CFLAGS) \
- $(INCCUNIT_COMMON) \
- $(INCODP)
-shmem_odp1_LDFLAGS = $(AM_LDFLAGS)
-shmem_odp1_LDADD = $(LIBCUNIT_COMMON) $(LIBODP)
-
-dist_shmem_odp2_SOURCES = shmem_odp2.c
-shmem_odp2_CFLAGS = $(AM_CFLAGS) \
- $(INCCUNIT_COMMON) \
- $(INCODP)
-shmem_odp2_LDFLAGS = $(AM_LDFLAGS)
-shmem_odp2_LDADD = $(LIBCUNIT_COMMON) $(LIBODP)
-
-
-noinst_HEADERS = shmem_common.h shmem_linux.h shmem_odp1.h shmem_odp2.h
diff --git a/test/linux-generic/validation/api/shmem/shmem.h b/test/linux-generic/validation/api/shmem/shmem.h
deleted file mode 100644
index 2368a2e1c..000000000
--- a/test/linux-generic/validation/api/shmem/shmem.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _ODP_LINUX_TEST_SHMEM_H_
-#define _ODP_LINUX_TEST_SHMEM_H_
-
-#include <odp_cunit_common.h>
-
-/* test functions: */
-void shmem_test_odp_shm_proc(void);
-
-/* test arrays: */
-extern odp_testinfo_t shmem_linux_suite[];
-
-/* test registry: */
-extern odp_suiteinfo_t shmem_linux_suites[];
-
-#endif
diff --git a/test/linux-generic/validation/api/shmem/shmem_common.h b/test/linux-generic/validation/api/shmem/shmem_common.h
deleted file mode 100644
index 0a90297fa..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_common.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef _COMMON_TEST_SHMEM_H_
-#define _COMMON_TEST_SHMEM_H_
-
-#define SHM_NAME "odp_linux_shared_mem"
-#define DEFAULT_SHM_DIR "/dev/shm"
-#define FIFO_NAME_FMT "%s/%d/shmem_test_fifo-%d"
-#define ALIGN_SIZE (128)
-#define TEST_SHARE_FOO (0xf0f0f0f0)
-#define TEST_SHARE_BAR (0xf0f0f0f)
-#define TEST_FAILURE 'F'
-#define TEST_SUCCESS 'S'
-
-typedef struct {
- uint32_t foo;
- uint32_t bar;
-} test_shared_linux_data_t;
-
-#endif
diff --git a/test/linux-generic/validation/api/shmem/shmem_linux.c b/test/linux-generic/validation/api/shmem/shmem_linux.c
deleted file mode 100644
index fe11a7db9..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_linux.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/* this test makes sure that odp shared memory created with the ODP_SHM_PROC
- * flag is visible under linux, and checks that memory created with the
- * ODP_SHM_EXPORT flag is visible by other ODP instances.
- * It therefore checks both that the link
- * name under /dev/shm is correct, and also checks that the memory contents
- * is indeed shared.
- * we want:
- * -the odp test to run using C UNIT
- * -the main process to return the correct return code.
- * (for the autotools test harness)
- *
- * To achieve this, the flow of operations is as follows:
- *
- * linux process (main, non odp) |
- * (shmem_linux.c) |
- * |
- * |
- * |
- * main() |
- * forks odp_app1 process |
- * wait for named pipe creation |
- * |
- * | ODP_APP1 process
- * | (shmem_odp1.c)
- * |
- * | allocate shmem
- * | populate shmem
- * | create named pipe
- * | wait for test report in fifo...
- * read shared memory |
- * check if memory contents is OK |
- * If not OK, write "F" in fifo and |
- * exit with failure code. | -------------------
- * |
- * forks odp app2 process | ODP APP2 process
- * wait for child terminaison & status| (shmem_odp2.c)
- * | lookup ODP_APP1 shared memory,
- * | check if memory contents is OK
- * | Exit(0) on success, exit(1) on fail
- * If child failed, write "F" in fifo |
- * exit with failure code. | -------------------
- * |
- * OK, write "S" in fifo, |
- * wait for child terminaison & status|
- * terminate with same status as child|
- * | ODP APP1 process
- * | (shmem_odp1.c)
- * |
- * | ...(continued)
- * | read S(success) or F(fail) from fifo
- * | report success or failure to C-Unit
- * | Exit(0) on success, exit(1) on fail
- * wait for child terminaison & status |
- * terminate with same status as child |
- * |
- * \|/
- * time
- */
-
-#include <stdint.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <linux/limits.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include <libgen.h>
-#include <linux/limits.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include "shmem_linux.h"
-#include "shmem_common.h"
-
-#define ODP_APP1_NAME "shmem_odp1" /* name of the odp1 program, in this dir */
-#define ODP_APP2_NAME "shmem_odp2" /* name of the odp2 program, in this dir */
-/* odp-<pid>-shm-<name> */
-#define DEVNAME_DEFAULT_DIR "/dev/shm"
-#define DEVNAME_FMT "%s/%d/odp-%" PRIu64 "-shm-%s"
-#define MAX_FIFO_WAIT 30 /* Max time waiting for the fifo (sec) */
-
-/*
- * read the attributes of a externaly shared mem object:
- * input: ext_odp_pid, blockname: the remote ODP instance and the exported
- * block name to be searched.
- * Output: filename: the memory block underlaying file to be opened
- * (the given buffer should be big enough i.e. at
- * least ISHM_FILENAME_MAXLEN bytes)
- * The 3 following parameters are really here for debug
- * as they are really meaningles in a non-odp process:
- * len: the block real length (bytes, multiple of page sz)
- * flags: the _ishm flags setting the block was created with
- * align: the alignement setting the block was created with
- *
- * return 0 on success, non zero on error
- */
-static int read_shmem_attribues(uint64_t ext_odp_pid, const char *blockname,
- char *filename, uint64_t *len,
- uint32_t *flags, uint64_t *user_len,
- uint32_t *user_flags, uint32_t *align)
-{
- char shm_attr_filename[PATH_MAX];
- FILE *export_file;
- char *shm_dir = getenv("ODP_SHM_DIR");
-
- sprintf(shm_attr_filename, DEVNAME_FMT,
- shm_dir ? shm_dir : DEVNAME_DEFAULT_DIR,
- getuid(),
- ext_odp_pid, blockname);
-
- /* O_CREAT flag not given => failure if shm_attr_filename does not
- * already exist */
- export_file = fopen(shm_attr_filename, "r");
- if (export_file == NULL)
- return -1;
-
- if (fscanf(export_file, "ODP exported shm block info: ") != 0)
- goto export_file_read_err;
-
- if (fscanf(export_file, "ishm_blockname: %*s ") != 0)
- goto export_file_read_err;
-
- if (fscanf(export_file, "file: %s ", filename) != 1)
- goto export_file_read_err;
-
- if (fscanf(export_file, "length: %" PRIu64 " ", len) != 1)
- goto export_file_read_err;
-
- if (fscanf(export_file, "flags: %" PRIu32 " ", flags) != 1)
- goto export_file_read_err;
-
- if (fscanf(export_file, "user_length: %" PRIu64 " ", user_len) != 1)
- goto export_file_read_err;
-
- if (fscanf(export_file, "user_flags: %" PRIu32 " ", user_flags) != 1)
- goto export_file_read_err;
-
- if (fscanf(export_file, "align: %" PRIu32 " ", align) != 1)
- goto export_file_read_err;
-
- fclose(export_file);
- return 0;
-
-export_file_read_err:
- fclose(export_file);
- return -1;
-}
-
-void test_success(char *fifo_name, int fd, pid_t odp_app)
-{
- int status;
- int nb_char;
- char result = TEST_SUCCESS;
- /* write "Success" to the FIFO */
- nb_char = write(fd, &result, sizeof(char));
- close(fd);
- /* wait for the odp app1 to terminate */
- waitpid(odp_app, &status, 0);
- /* if the write failed, report an error anyway */
- if (nb_char != 1)
- status = 1;
- unlink(fifo_name);
- exit(status); /* the status reported by the odp side is returned */
-}
-
-void test_failure(char *fifo_name, int fd, pid_t odp_app)
-{
- int status;
- char result;
-
- int nb_char __attribute__((unused)); /*ignored: we fail anyway */
-
- result = TEST_FAILURE;
- /* write "Failure" to the FIFO */
- nb_char = write(fd, &result, sizeof(char));
- close(fd);
- /* wait for the odp app1 to terminate */
- waitpid(odp_app, &status, 0);
- unlink(fifo_name);
- exit(1); /* error */
-}
-
-int main(int argc __attribute__((unused)), char *argv[])
-{
- char prg_name[PATH_MAX];
- char odp_name1[PATH_MAX];
- char odp_name2[PATH_MAX];
- int nb_sec;
- int size;
- pid_t odp_app1;
- pid_t odp_app2;
- char *odp_params1 = NULL;
- char *odp_params2[3];
- char pid1[10];
- char fifo_name[PATH_MAX]; /* fifo for linux->odp feedback */
- int fifo_fd = -1;
- char shm_filename[PATH_MAX];/* shared mem device name, under /dev/shm */
- uint64_t len;
- uint32_t flags;
- uint64_t user_len;
- uint32_t user_flags;
- uint32_t align;
- int shm_fd;
- test_shared_linux_data_t *addr;
- int app2_status;
- uid_t uid = getuid();
- char *shm_dir = getenv("ODP_SHM_DIR");
-
- /* odp_app1 is in the same directory as this file: */
- strncpy(prg_name, argv[0], PATH_MAX - 1);
- sprintf(odp_name1, "%s/%s", dirname(prg_name), ODP_APP1_NAME);
-
- /* start the ODP application: */
- odp_app1 = fork();
- if (odp_app1 < 0) /* error */
- exit(1);
-
- if (odp_app1 == 0) { /* child */
- execv(odp_name1, &odp_params1); /* no return unless error */
- fprintf(stderr, "execv failed: %s\n", strerror(errno));
- }
-
- /* wait max 30 sec for the fifo to be created by the ODP side.
- * Just die if time expire as there is no fifo to communicate
- * through... */
- sprintf(fifo_name, FIFO_NAME_FMT,
- shm_dir ? shm_dir : DEFAULT_SHM_DIR,
- uid, odp_app1);
- for (nb_sec = 0; nb_sec < MAX_FIFO_WAIT; nb_sec++) {
- fifo_fd = open(fifo_name, O_WRONLY);
- if (fifo_fd >= 0)
- break;
- sleep(1);
- }
- if (fifo_fd < 0)
- exit(1);
- printf("pipe found\n");
-
- /* the linux named pipe has now been found, meaning that the
- * ODP application is up and running, and has allocated shmem.
- * check to see if linux can see the created shared memory: */
-
- /* read the shared memory attributes (includes the shm filename): */
- if (read_shmem_attribues(odp_app1, SHM_NAME,
- shm_filename, &len, &flags,
- &user_len, &user_flags, &align) != 0) {
- printf("erorr read_shmem_attribues\n");
- test_failure(fifo_name, fifo_fd, odp_app1);
- }
-
- /* open the shm filename (which is either on /dev/shm/ or on hugetlbfs)
- * O_CREAT flag not given => failure if shm_devname does not already
- * exist */
- shm_fd = open(shm_filename, O_RDONLY,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (shm_fd == -1) {
- fprintf(stderr, "unable to open %s\n", shm_filename);
- test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
- }
-
- /* linux ODP guarantees page size alignement. Larger alignment may
- * fail as 2 different processes will have fully unrelated
- * virtual spaces.
- */
- size = sizeof(test_shared_linux_data_t);
-
- addr = mmap(NULL, size, PROT_READ, MAP_SHARED, shm_fd, 0);
- if (addr == MAP_FAILED) {
- fprintf(stderr, "shmem_linux: map failed!\n");
- test_failure(fifo_name, fifo_fd, odp_app1);
- }
-
- /* check that we see what the ODP application wrote in the memory */
- if ((addr->foo != TEST_SHARE_FOO) || (addr->bar != TEST_SHARE_BAR)) {
- fprintf(stderr, "ERROR: addr->foo %x addr->bar %x\n",
- addr->foo, addr->bar);
- test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
- }
-
- /* odp_app2 is in the same directory as this file: */
- strncpy(prg_name, argv[0], PATH_MAX - 1);
- sprintf(odp_name2, "%s/%s", dirname(prg_name), ODP_APP2_NAME);
-
- /* start the second ODP application with pid of ODP_APP1 as parameter:*/
- sprintf(pid1, "%d", odp_app1);
- odp_params2[0] = odp_name2;
- odp_params2[1] = pid1;
- odp_params2[2] = NULL;
- odp_app2 = fork();
- if (odp_app2 < 0) /* error */
- exit(1);
-
- if (odp_app2 == 0) { /* child */
- execv(odp_name2, odp_params2); /* no return unless error */
- fprintf(stderr, "execv failed: %s\n", strerror(errno));
- }
-
- /* wait for the second ODP application to terminate:
- * status is OK if that second ODP application could see the
- * memory shared by the first one. */
- waitpid(odp_app2, &app2_status, 0);
-
- if (app2_status)
- test_failure(fifo_name, fifo_fd, odp_app1); /* no return */
-
- /* everything looked good: */
- test_success(fifo_name, fifo_fd, odp_app1);
-}
diff --git a/test/linux-generic/validation/api/shmem/shmem_linux.h b/test/linux-generic/validation/api/shmem/shmem_linux.h
deleted file mode 100644
index a07a7758f..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_linux.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-void test_success(char *fifo_name, int fd, pid_t odp_app);
-void test_failure(char *fifo_name, int fd, pid_t odp_app);
-int main(int argc, char *argv[]);
diff --git a/test/linux-generic/validation/api/shmem/shmem_odp1.c b/test/linux-generic/validation/api/shmem/shmem_odp1.c
deleted file mode 100644
index 0ced4554d..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_odp1.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp.h>
-#include <linux/limits.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include <odp_cunit_common.h>
-#include "shmem_odp1.h"
-#include "shmem_common.h"
-
-#define TEST_SHARE_FOO (0xf0f0f0f0)
-#define TEST_SHARE_BAR (0xf0f0f0f)
-
-void shmem_test_odp_shm_proc(void)
-{
- char fifo_name[PATH_MAX];
- int fd;
- odp_shm_t shm;
- test_shared_data_t *test_shared_data;
- char test_result;
- char *shm_dir = getenv("ODP_SHM_DIR");
-
- printf("start with pid %d\n", getpid());
- /* reminder: ODP_SHM_PROC => export to linux, ODP_SHM_EXPORT=>to odp */
- shm = odp_shm_reserve(SHM_NAME,
- sizeof(test_shared_data_t),
- ALIGN_SIZE, ODP_SHM_PROC | ODP_SHM_EXPORT);
- CU_ASSERT_FATAL(ODP_SHM_INVALID != shm);
- test_shared_data = odp_shm_addr(shm);
- CU_ASSERT_FATAL(NULL != test_shared_data);
- test_shared_data->foo = TEST_SHARE_FOO;
- test_shared_data->bar = TEST_SHARE_BAR;
-
- odp_mb_full();
-
- /* open the fifo: this will indicate to linux process that it can
- * start the shmem lookups and check if it sees the data */
- sprintf(fifo_name, FIFO_NAME_FMT,
- shm_dir ? shm_dir : DEFAULT_SHM_DIR,
- getuid(), getpid());
- CU_ASSERT_FATAL(mkfifo(fifo_name, 0666) == 0);
-
- /* read from the fifo: the linux process result: */
- printf("shmem_odp1: opening fifo: %s\n", fifo_name);
- fd = open(fifo_name, O_RDONLY);
- CU_ASSERT_FATAL(fd >= 0);
-
- printf("shmem_odp1: reading fifo: %s\n", fifo_name);
- CU_ASSERT(read(fd, &test_result, sizeof(char)) == 1);
- printf("shmem_odp1: read fifo: %d\n", test_result);
- printf("shmem_odp1: closing fifo: %s\n", fifo_name);
- close(fd);
- CU_ASSERT_FATAL(test_result == TEST_SUCCESS);
-
- CU_ASSERT(odp_shm_free(shm) == 0);
-}
-
-odp_testinfo_t shmem_suite[] = {
- ODP_TEST_INFO(shmem_test_odp_shm_proc),
- ODP_TEST_INFO_NULL,
-};
-
-odp_suiteinfo_t shmem_suites[] = {
- {"Shared Memory", NULL, NULL, shmem_suite},
- ODP_SUITE_INFO_NULL,
-};
-
-int main(void)
-{
- int ret = odp_cunit_register(shmem_suites);
-
- if (ret == 0)
- ret = odp_cunit_run();
-
- return ret;
-}
diff --git a/test/linux-generic/validation/api/shmem/shmem_odp1.h b/test/linux-generic/validation/api/shmem/shmem_odp1.h
deleted file mode 100644
index 614bbf805..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_odp1.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-void shmem_test_odp_shm_proc(void);
diff --git a/test/linux-generic/validation/api/shmem/shmem_odp2.c b/test/linux-generic/validation/api/shmem/shmem_odp2.c
deleted file mode 100644
index 0144407b9..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_odp2.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <odp.h>
-#include <linux/limits.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include <odp_cunit_common.h>
-#include "shmem_odp2.h"
-#include "shmem_common.h"
-
-#define TEST_SHARE_FOO (0xf0f0f0f0)
-#define TEST_SHARE_BAR (0xf0f0f0f)
-
-/* The C unit test harness is run by ODP1 app which will be told the return
- * staus of this process. See top of shmem_linux.c for chart flow of events
- */
-int main(int argc, char *argv[])
-{
- odp_instance_t odp1;
- odp_instance_t odp2;
- odp_shm_t shm;
- odp_shm_info_t info;
- test_shared_data_t *test_shared_data;
-
- /* odp init: */
- if (0 != odp_init_global(&odp2, NULL, NULL)) {
- fprintf(stderr, "error: odp_init_global() failed.\n");
- return 1;
- }
- if (0 != odp_init_local(odp2, ODP_THREAD_CONTROL)) {
- fprintf(stderr, "error: odp_init_local() failed.\n");
- return 1;
- }
-
- /* test: map ODP1 memory and check its contents:
- * The pid of the ODP instantiation process sharing its memory
- * is given as first arg. In linux-generic ODP, this pid is actually
- * the ODP instance */
- if (argc != 2) {
- fprintf(stderr, "One single parameter expected, %d found.\n",
- argc);
- return 1;
- }
- odp1 = (odp_instance_t)atoi(argv[1]);
-
- printf("shmem_odp2: trying to grab %s from pid %d\n",
- SHM_NAME, (int)odp1);
- shm = odp_shm_import(SHM_NAME, odp1, SHM_NAME);
- if (shm == ODP_SHM_INVALID) {
- fprintf(stderr, "error: odp_shm_lookup_external failed.\n");
- return 1;
- }
-
- /* check that the read size matches the allocated size (in other ODP):*/
- if ((odp_shm_info(shm, &info)) ||
- (info.size != sizeof(*test_shared_data))) {
- fprintf(stderr, "error: odp_shm_info failed.\n");
- return 1;
- }
-
- test_shared_data = odp_shm_addr(shm);
- if (test_shared_data == NULL) {
- fprintf(stderr, "error: odp_shm_addr failed.\n");
- return 1;
- }
-
- if (test_shared_data->foo != TEST_SHARE_FOO) {
- fprintf(stderr, "error: Invalid data TEST_SHARE_FOO.\n");
- return 1;
- }
-
- if (test_shared_data->bar != TEST_SHARE_BAR) {
- fprintf(stderr, "error: Invalid data TEST_SHARE_BAR.\n");
- return 1;
- }
-
- if (odp_shm_free(shm) != 0) {
- fprintf(stderr, "error: odp_shm_free() failed.\n");
- return 1;
- }
-
- /* odp term: */
- if (0 != odp_term_local()) {
- fprintf(stderr, "error: odp_term_local() failed.\n");
- return 1;
- }
-
- if (0 != odp_term_global(odp2)) {
- fprintf(stderr, "error: odp_term_global() failed.\n");
- return 1;
- }
-
- printf("%s SUCSESS\n", __FILE__);
- return 0;
-}
diff --git a/test/linux-generic/validation/api/shmem/shmem_odp2.h b/test/linux-generic/validation/api/shmem/shmem_odp2.h
deleted file mode 100644
index a8db909a8..000000000
--- a/test/linux-generic/validation/api/shmem/shmem_odp2.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (c) 2016, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-int main(int argc, char *argv[]);
diff --git a/test/m4/configure.m4 b/test/m4/configure.m4
index 460e8449f..ea05e954f 100644
--- a/test/m4/configure.m4
+++ b/test/m4/configure.m4
@@ -1,3 +1,53 @@
-m4_include([test/common_plat/m4/configure.m4])
+##########################################################################
+# Build and install test applications
+##########################################################################
+AC_ARG_WITH([tests],
+ [AS_HELP_STRING([--without-tests],
+ [don't build and install test applications]
+ [[default=with]])],
+ [],
+ [with_tests=yes])
+AM_CONDITIONAL([WITH_TESTS], [test x$with_tests != xno])
-AC_CONFIG_FILES([test/Makefile])
+m4_include([test/m4/miscellaneous.m4])
+m4_include([test/m4/performance.m4])
+m4_include([test/m4/validation.m4])
+
+AC_CONFIG_FILES([test/common/Makefile
+ test/miscellaneous/Makefile
+ test/performance/Makefile
+ test/validation/Makefile
+ test/validation/api/align/Makefile
+ test/validation/api/atomic/Makefile
+ test/validation/api/barrier/Makefile
+ test/validation/api/buffer/Makefile
+ test/validation/api/byteorder/Makefile
+ test/validation/api/chksum/Makefile
+ test/validation/api/classification/Makefile
+ test/validation/api/comp/Makefile
+ test/validation/api/cpumask/Makefile
+ test/validation/api/crypto/Makefile
+ test/validation/api/dma/Makefile
+ test/validation/api/errno/Makefile
+ test/validation/api/event/Makefile
+ test/validation/api/hash/Makefile
+ test/validation/api/hints/Makefile
+ test/validation/api/init/Makefile
+ test/validation/api/ipsec/Makefile
+ test/validation/api/lock/Makefile
+ test/validation/api/Makefile
+ test/validation/api/ml/Makefile
+ test/validation/api/packet/Makefile
+ test/validation/api/pktio/Makefile
+ test/validation/api/pool/Makefile
+ test/validation/api/queue/Makefile
+ test/validation/api/random/Makefile
+ test/validation/api/scheduler/Makefile
+ test/validation/api/shmem/Makefile
+ test/validation/api/stash/Makefile
+ test/validation/api/std/Makefile
+ test/validation/api/system/Makefile
+ test/validation/api/thread/Makefile
+ test/validation/api/time/Makefile
+ test/validation/api/timer/Makefile
+ test/validation/api/traffic_mngr/Makefile])
diff --git a/test/m4/miscellaneous.m4 b/test/m4/miscellaneous.m4
new file mode 100644
index 000000000..62178c9f2
--- /dev/null
+++ b/test/m4/miscellaneous.m4
@@ -0,0 +1,23 @@
+##########################################################################
+# Enable/disable test-cpp
+##########################################################################
+AC_ARG_ENABLE([test-cpp],
+ [AS_HELP_STRING([--disable-test-cpp], [run basic test against cpp]
+ [[default=enable-if-cpp-works]])],
+ [test_cpp=$enableval],
+ [test_cpp=check])
+
+if test "x$test_cpp" != "xno" ; then
+ AC_CACHE_CHECK([if C++ compiler works], [odp_cv_cxx_works],
+ [AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [odp_cv_cxx_works=yes],
+ [odp_cv_cxx_works=no])
+ AC_LANG_POP([C++])])
+ AS_IF([test "x$test_cpp$odp_cv_cxx_works" = "xyesno"],
+ [AC_MSG_FAILURE([C++ compiler test failed])],
+ [test "x$test_cpp$odp_cv_cxx_works" = "xcheckno"],
+ [AC_MSG_NOTICE([disabling C++ test]) ; test_cpp=no],
+ [test_cpp=yes])
+fi
+
+AM_CONDITIONAL([test_cpp], [test x$test_cpp = xyes ])
diff --git a/test/m4/performance.m4 b/test/m4/performance.m4
new file mode 100644
index 000000000..fce0ffb4f
--- /dev/null
+++ b/test/m4/performance.m4
@@ -0,0 +1,9 @@
+##########################################################################
+# Enable/disable test-perf
+##########################################################################
+AC_ARG_ENABLE([test-perf],
+ [AS_HELP_STRING([--enable-test-perf],
+ [run test in test/performance [default=enabled]])],
+ [test_perf=$enableval],
+ [test_perf=yes])
+AM_CONDITIONAL([test_perf], [test x$test_perf = xyes ])
diff --git a/test/m4/validation.m4 b/test/m4/validation.m4
new file mode 100644
index 000000000..a90910008
--- /dev/null
+++ b/test/m4/validation.m4
@@ -0,0 +1,34 @@
+##########################################################################
+# Enable/disable Unit tests
+##########################################################################
+AC_ARG_ENABLE([test_vald],
+ [AS_HELP_STRING([--enable-test-vald],
+ [run test in test/validation [default=enabled]])],
+ [test_vald=$enableval],
+ [test_vald=check])
+
+##########################################################################
+# Check for CUnit availability
+##########################################################################
+cunit_support=$test_vald
+AS_IF([test "x$cunit_support" != "xno"],
+ [PKG_CHECK_MODULES([CUNIT], [cunit], [cunit_support=yes],
+ [AC_MSG_WARN([pkg-config could not find CUnit, guessing])
+ cunit_support=yes
+ AC_CHECK_HEADERS([CUnit/Basic.h], [], [cunit_support=no])
+ AC_CHECK_LIB([cunit], [CU_get_error], [CUNIT_LIBS="-lcunit"],
+ [cunit_support=no])
+])])
+
+AS_IF([test "x$test_vald" = "xyes" -a "x$cunit_support" = "xno"],
+ [AC_MSG_ERROR([Validation testsuite requested, but CUnit was not found])],
+ [test "x$test_vald" = "xcheck" -a "x$cunit_support" = "xno"],
+ [AC_MSG_WARN([CUnit was not found, disabling validation testsuite])
+ test_vald=no],
+ [test "x$test_vald" != "xno"], [test_vald=yes])
+
+AM_CONDITIONAL([cunit_support], [test "x$cunit_support" = "xyes"])
+AM_CONDITIONAL([test_vald], [test "x$test_vald" = "xyes"])
+
+AC_SUBST([CUNIT_CFLAGS])
+AC_SUBST([CUNIT_LIBS])
diff --git a/test/miscellaneous/.gitignore b/test/miscellaneous/.gitignore
new file mode 100644
index 000000000..74fb9b3ca
--- /dev/null
+++ b/test/miscellaneous/.gitignore
@@ -0,0 +1,5 @@
+odp_dyn_workers
+odp_api_from_cpp
+odp_api_headers
+*.trs
+*.log
diff --git a/test/miscellaneous/Makefile.am b/test/miscellaneous/Makefile.am
new file mode 100644
index 000000000..449ee7b24
--- /dev/null
+++ b/test/miscellaneous/Makefile.am
@@ -0,0 +1,78 @@
+include $(top_srcdir)/test/Makefile.inc
+
+bin_PROGRAMS = odp_dyn_workers
+
+if test_cpp
+bin_PROGRAMS += odp_api_from_cpp
+endif
+
+odp_dyn_workers_CFLAGS = $(AM_CFLAGS) -Wno-format-nonliteral
+odp_dyn_workers_SOURCES = odp_dyn_workers.c
+odp_api_from_cpp_SOURCES = odp_api_from_cpp.cpp
+
+TESTSCRIPTS = odp_dyn_workers_run.sh
+
+TESTS = $(TESTSCRIPTS)
+
+if test_cpp
+TESTS += odp_api_from_cpp
+endif
+
+noinst_PROGRAMS = odp_api_headers
+odp_api_headers_CFLAGS = $(AM_CFLAGS) -Wconversion
+odp_api_headers_SOURCES = odp_api_headers.c
+
+PKGCONFIG = PKG_CONFIG_PATH=$(libdir)/pkgconfig:$$PKG_CONFIG_PATH pkg-config --cflags --libs
+
+if enable_shared
+
+PROGRAM_shared = odp_api_headers_shared
+
+installcheck-local: $(PROGRAM_shared)
+
+$(PROGRAM_shared): $(srcdir)/$(odp_api_headers_SOURCES)
+ $(CC) $(AM_CFLAGS) $(CFLAGS) $^ -o $@ \
+ `$(PKGCONFIG) libodphelper` `$(PKGCONFIG) lib$(ODP_LIB_NAME)`
+if ! cross_compile
+ LD_LIBRARY_PATH=$(libdir) ./$@
+endif
+endif
+
+if enable_static
+
+PROGRAM_static = odp_api_headers_static
+
+installcheck-local: $(PROGRAM_static)
+
+$(PROGRAM_static): $(srcdir)/$(odp_api_headers_SOURCES)
+ $(CC) $(AM_CFLAGS) $(CFLAGS) $^ -o $@ \
+ `$(PKGCONFIG) --static libodphelper | sed "s/-lodphelper/-l:libodphelper.a/"` \
+ `$(PKGCONFIG) --static lib$(ODP_LIB_NAME) | sed "s/-l$(ODP_LIB_NAME)/-l:lib$(ODP_LIB_NAME).a/"`
+if ! cross_compile
+ ./$@
+endif
+endif
+
+DISTCLEANFILES = $(PROGRAM_shared) $(PROGRAM_static)
+
+dist_check_SCRIPTS = $(TESTSCRIPTS)
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS) $(dist_check_DATA); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS) $(dist_check_DATA); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/test/miscellaneous/odp_api_from_cpp.cpp b/test/miscellaneous/odp_api_from_cpp.cpp
new file mode 100644
index 000000000..c5aae0c3f
--- /dev/null
+++ b/test/miscellaneous/odp_api_from_cpp.cpp
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include <iostream>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+int main(int argc ODP_UNUSED, const char *argv[] ODP_UNUSED)
+{
+ odp_instance_t inst;
+
+ if (odp_init_global(&inst, NULL, NULL))
+ exit(EXIT_FAILURE);
+
+ if (odp_init_local(inst, ODP_THREAD_WORKER))
+ exit(EXIT_FAILURE);
+
+ std::cout << "\tODP API version: " << odp_version_api_str() << std::endl;
+ std::cout << "\tODP implementation version: " << odp_version_impl_str() << std::endl;
+
+ if (odp_term_local())
+ exit(EXIT_FAILURE);
+
+ if (odp_term_global(inst))
+ exit(EXIT_FAILURE);
+
+ return 0;
+}
diff --git a/test/miscellaneous/odp_api_headers.c b/test/miscellaneous/odp_api_headers.c
new file mode 100644
index 000000000..3884ab307
--- /dev/null
+++ b/test/miscellaneous/odp_api_headers.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+int main(int argc ODP_UNUSED, char *argv[] ODP_UNUSED)
+{
+ odp_instance_t inst;
+
+ if (odp_init_global(&inst, NULL, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ return -1;
+ }
+
+ if (odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ return -1;
+ }
+
+ odp_sys_info_print();
+ printf("Helper library version: %s\n", odph_version_str());
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("Global term failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/miscellaneous/odp_dyn_workers.c b/test/miscellaneous/odp_dyn_workers.c
new file mode 100644
index 000000000..16d9bab4e
--- /dev/null
+++ b/test/miscellaneous/odp_dyn_workers.c
@@ -0,0 +1,1357 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define S_(x) #x
+#define S(x) S_(x)
+#define MAX_PROGS 8
+#define CMD_DELIMITER ","
+#define PROG_NAME "odp_dyn_workers"
+
+#define FOREACH_CMD(CMD) \
+ CMD(ADD_WORKER) \
+ CMD(REM_WORKER)
+
+#define GENERATE_ENUM(ENUM) ENUM,
+#define GENERATE_STRING(STRING) #STRING,
+#define ADDITION 'a'
+#define REMOVAL 'r'
+#define DELAY 'd'
+#define IDX_DELIMITER ":"
+#define MAX_NIBBLE 15
+#define MAX_WORKERS MAX_NIBBLE
+#define MAX_PATTERN_LEN 32U
+#define ENV_PREFIX "ODP"
+#define ENV_DELIMITER "="
+#define UNKNOWN_CMD MAX_NIBBLE
+#define EXIT_PROG (UNKNOWN_CMD - 1U)
+#define DELAY_PROG (EXIT_PROG - 1U)
+
+ODP_STATIC_ASSERT(MAX_WORKERS <= MAX_NIBBLE, "Too many workers");
+
+enum {
+ FOREACH_CMD(GENERATE_ENUM)
+};
+
+typedef enum {
+ PRS_OK,
+ PRS_NOK,
+ PRS_TERM
+} parse_result_t;
+
+enum {
+ PARENT,
+ CHILD
+};
+
+typedef enum {
+ DOWN,
+ UP
+} state_t;
+
+enum {
+ CONN_ERR = -1,
+ PEER_ERR,
+ CMD_NOK,
+ CMD_SUMMARY,
+ CMD_OK
+};
+
+static const char *const cmdstrs[] = {
+ FOREACH_CMD(GENERATE_STRING)
+};
+
+ODP_STATIC_ASSERT(ODPH_ARRAY_SIZE(cmdstrs) < DELAY_PROG, "Too many commands");
+
+typedef struct {
+ uint64_t thread_id;
+ uint64_t num_handled;
+ uint64_t enq_errs;
+ uint64_t runtime;
+} summary_t;
+
+typedef struct prog_t {
+ summary_t summary;
+ char *env;
+ char *cpumask;
+ pid_t pid;
+ int socket;
+ state_t state;
+} prog_t;
+
+typedef struct {
+ uint64_t val1;
+ uint8_t val2;
+ uint8_t op;
+} pattern_t;
+
+typedef struct {
+ pattern_t pattern[MAX_PATTERN_LEN];
+ prog_t progs[MAX_PROGS];
+ uint32_t num_p_elems;
+ uint32_t num_progs;
+ uint32_t max_cmd_len;
+ odp_bool_t is_running;
+} global_config_t;
+
+typedef struct worker_config_s worker_config_t;
+
+typedef struct worker_config_s {
+ odph_thread_t thread;
+ odp_barrier_t barrier;
+ summary_t summary;
+ odp_ticketlock_t lock;
+ odp_schedule_group_t grp;
+ odp_queue_t queue;
+ worker_config_t *configs;
+ odp_atomic_u32_t is_running;
+ uint8_t idx;
+} worker_config_t;
+
+typedef struct {
+ worker_config_t worker_config[MAX_WORKERS];
+ odp_instance_t instance;
+ odp_cpumask_t cpumask;
+ odp_pool_t pool;
+ summary_t *pending_summary;
+ uint32_t num_workers;
+ int socket;
+} prog_config_t;
+
+typedef struct {
+ struct {
+ uint16_t is_active;
+ uint16_t cpu;
+ uint32_t thread_id;
+ } workers[MAX_WORKERS];
+} result_t;
+
+typedef odp_bool_t (*input_fn_t)(global_config_t *config, uint8_t *cmd, uint32_t *prog_idx,
+ uint32_t *worker_idx);
+typedef odp_bool_t (*cmd_fn_t)(prog_config_t *config, uint8_t aux);
+
+static global_config_t conf;
+static prog_config_t *prog_conf;
+
+static void terminate(int signal ODP_UNUSED)
+{
+ conf.is_running = false;
+}
+
+static odp_bool_t setup_signals(void)
+{
+ struct sigaction action = { .sa_handler = terminate };
+
+ if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
+ sigaddset(&action.sa_mask, SIGTERM) == -1 ||
+ sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
+ sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1)
+ return false;
+
+ return true;
+}
+
+static void init_options(global_config_t *config)
+{
+ uint32_t max_len = 0U, str_len;
+
+ memset(config, 0, sizeof(*config));
+
+ for (uint32_t i = 0U; i < ODPH_ARRAY_SIZE(cmdstrs); ++i) {
+ str_len = strlen(cmdstrs[i]);
+
+ if (str_len > max_len)
+ max_len = str_len;
+ }
+
+ config->max_cmd_len = max_len;
+}
+
+static void parse_masks(global_config_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp;
+ prog_t *prog;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, CMD_DELIMITER);
+
+ while (tmp && config->num_progs < MAX_PROGS) {
+ prog = &config->progs[config->num_progs];
+ prog->cpumask = strdup(tmp);
+
+ if (prog->cpumask != NULL)
+ ++config->num_progs;
+
+ tmp = strtok(NULL, CMD_DELIMITER);
+ }
+
+ free(tmp_str);
+}
+
+static void parse_pattern(global_config_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp, op;
+ uint8_t num_elems = 0U;
+ pattern_t *pattern;
+ uint64_t val1;
+ uint32_t val2;
+ int ret;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, CMD_DELIMITER);
+
+ while (tmp && num_elems < MAX_PATTERN_LEN) {
+ pattern = &config->pattern[num_elems];
+ /* Use invalid values to prevent correct values by chance. */
+ val1 = -1;
+ val2 = -1;
+ ret = sscanf(tmp, "%c%" PRIu64 IDX_DELIMITER "%u", &op, &val1, &val2);
+
+ if ((ret == 2 || ret == 3) && (op == ADDITION || op == REMOVAL || op == DELAY)) {
+ pattern->val1 = val1;
+ pattern->val2 = val2;
+ pattern->op = op;
+ ++num_elems;
+ }
+
+ tmp = strtok(NULL, CMD_DELIMITER);
+ }
+
+ free(tmp_str);
+ config->num_p_elems = num_elems;
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Simple interactive ODP dynamic worker tester. Can be used to verify ability of\n"
+ "an implementation to dynamically add and remove workers from one ODP application\n"
+ "to another. Acts as a frontend and forks ODP applications based on\n"
+ "configuration.\n"
+ "\n"
+ "Usage: " PROG_NAME " OPTIONS\n"
+ "\n"
+ " E.g. ODP0=MY_ENV=MY_VAL ODP1=MY_ENV=MY_VAL " PROG_NAME " -c 0x80,0x80\n"
+ " ...\n"
+ " > %s 0 0\n"
+ " > %s 0 0\n"
+ " > %s 1 0\n"
+ " > %s 1 0\n"
+ " ...\n"
+ " " PROG_NAME " -c 0x80,0x80 -p %c0%s0%s%c1000000000%s%c0%s0\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ "\n"
+ " -c, --cpumasks CPU masks for to-be-created ODP processes, comma-separated, no\n"
+ " spaces. CPU mask format should be as expected by\n"
+ " 'odp_cpumask_from_str()'. Parsed amount of CPU masks will be\n"
+ " the number of ODP processes to be created. Theoretical maximum\n"
+ " number of CPU mask entries (and to-be-created ODP processes) is\n"
+ " %u. Theoretical maximum number of workers per ODP process is\n"
+ " %u. These might be further limited by the implementation.\n\n"
+ " A single environment variable can be passed to the processes.\n"
+ " The format should be: 'ODP<x>=<name>=<value>', where <x> is\n"
+ " process index, starting from 0.\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ "\n"
+ " -p, --pattern Non-interactive mode with a pattern of worker additions,\n"
+ " removals and delays, delimited by '%s', no spaces. Additions\n"
+ " are indicated with '%c' prefix, removals with '%c' prefix, both\n"
+ " followed by process index, starting from 0 and worker thread\n"
+ " index within given cpumask delimited by '%s', and delays with\n"
+ " '%c' prefix, followed by a delay in nanoseconds. Process\n"
+ " indexes are based on the parsed process count of '--cpumasks'\n"
+ " option. Additions and removals should be equal in the aggregate\n"
+ " and removals should never outnumber additions at any instant.\n"
+ " Maximum pattern length is %u.\n"
+ " -h, --help This help.\n"
+ "\n", cmdstrs[ADD_WORKER], cmdstrs[REM_WORKER], cmdstrs[ADD_WORKER],
+ cmdstrs[REM_WORKER], ADDITION, IDX_DELIMITER, CMD_DELIMITER, DELAY, CMD_DELIMITER,
+ REMOVAL, IDX_DELIMITER, MAX_PROGS, MAX_WORKERS, CMD_DELIMITER, ADDITION, REMOVAL,
+ IDX_DELIMITER, DELAY, MAX_PATTERN_LEN);
+}
+
+static parse_result_t check_options(const global_config_t *config)
+{
+ const pattern_t *pattern;
+ int64_t num_tot = 0U;
+
+ if (config->num_progs == 0U || config->num_progs > MAX_PROGS) {
+ printf("Invalid number of CPU masks: %u\n", config->num_progs);
+ return PRS_NOK;
+ }
+
+ for (uint32_t i = 0U; i < config->num_p_elems; ++i) {
+ pattern = &config->pattern[i];
+
+ if (pattern->op != DELAY) {
+ if (pattern->val1 >= config->num_progs) {
+ ODPH_ERR("Invalid pattern, invalid process index: %" PRIu64 "\n",
+ pattern->val1);
+ return PRS_NOK;
+ }
+
+ if (pattern->val2 > MAX_WORKERS - 1) {
+ ODPH_ERR("Invalid pattern, invalid worker index: %u\n",
+ pattern->val2);
+ return PRS_NOK;
+ }
+ }
+
+ if (pattern->op == ADDITION)
+ ++num_tot;
+ else if (pattern->op == REMOVAL)
+ --num_tot;
+
+ if (num_tot < 0) {
+ ODPH_ERR("Invalid pattern, removals exceed additions instantaneously\n");
+ return PRS_NOK;
+ }
+ }
+
+ if (num_tot > 0) {
+ ODPH_ERR("Invalid pattern, more additions than removals\n");
+ return PRS_NOK;
+ }
+
+ return PRS_OK;
+}
+
+static parse_result_t parse_options(int argc, char **argv, global_config_t *config)
+{
+ int opt, long_index;
+
+ static const struct option longopts[] = {
+ { "cpumasks", required_argument, NULL, 'c' },
+ { "pattern", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "c:p:h";
+
+ init_options(config);
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ parse_masks(config, optarg);
+ break;
+ case 'p':
+ parse_pattern(config, optarg);
+ break;
+ case 'h':
+ print_usage();
+ return PRS_TERM;
+ case '?':
+ default:
+ print_usage();
+ return PRS_NOK;
+ }
+ }
+
+ return check_options(config);
+}
+
+static odp_bool_t setup_pkill(pid_t ppid)
+{
+ return prctl(PR_SET_PDEATHSIG, SIGKILL) != -1 && getppid() == ppid;
+}
+
+ODP_PRINTF_FORMAT(2, 3)
+int log_fn(odp_log_level_t level, const char *fmt, ...);
+
+int log_fn(odp_log_level_t level, const char *fmt, ...)
+{
+ int pri;
+ va_list args;
+
+ switch (level) {
+ case ODP_LOG_DBG:
+ case ODP_LOG_PRINT:
+ pri = LOG_INFO;
+ break;
+ case ODP_LOG_WARN:
+ pri = LOG_WARNING;
+ break;
+ case ODP_LOG_ERR:
+ case ODP_LOG_UNIMPLEMENTED:
+ case ODP_LOG_ABORT:
+ pri = LOG_ERR;
+ break;
+ default:
+ pri = LOG_INFO;
+ break;
+ }
+
+ va_start(args, fmt);
+ vsyslog(pri, fmt, args);
+ va_end(args);
+
+ /* Just return something that's not considered an error. */
+ return 0;
+}
+
+static odp_bool_t disable_stream(int fd, odp_bool_t read)
+{
+ const int null = open("/dev/null", read ? O_RDONLY : O_WRONLY);
+ odp_bool_t ret = false;
+
+ if (null == -1)
+ return ret;
+
+ ret = dup2(null, fd) != -1;
+ close(null);
+
+ return ret;
+}
+
+static odp_bool_t set_odp_env(char *env)
+{
+ char *tmp_str = strdup(env), *tmp;
+ int ret;
+ odp_bool_t func_ret = false;
+
+ if (tmp_str == NULL)
+ return func_ret;
+
+ tmp = strtok(tmp_str, ENV_DELIMITER);
+
+ if (tmp != NULL) {
+ ret = setenv(tmp, strstr(env, ENV_DELIMITER) + 1U, 0);
+
+ if (ret == -1)
+ perror("setenv");
+
+ func_ret = ret != -1;
+ }
+
+ free(tmp_str);
+
+ return func_ret;
+}
+
+static odp_bool_t setup_prog_config(prog_config_t *config, odp_instance_t odp_instance,
+ char *cpumask, int socket)
+{
+ worker_config_t *worker_config;
+ odp_pool_param_t param;
+ odp_pool_t pool;
+
+ memset(config, 0, sizeof(*config));
+
+ for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
+ worker_config = &config->worker_config[i];
+ worker_config->thread.cpu = -1;
+ odp_ticketlock_init(&worker_config->lock);
+ worker_config->queue = ODP_QUEUE_INVALID;
+ odp_atomic_init_u32(&worker_config->is_running, 0U);
+ }
+
+ config->instance = odp_instance;
+ odp_cpumask_from_str(&config->cpumask, cpumask);
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_BUFFER;
+ param.buf.num = 1U;
+ param.buf.size = ODP_CACHE_LINE_SIZE;
+ pool = odp_pool_create(NULL, &param);
+
+ if (pool == ODP_POOL_INVALID) {
+ log_fn(ODP_LOG_ERR, "Error creating process buffer pool\n");
+ return false;
+ }
+
+ config->pool = pool;
+ config->socket = socket;
+
+ return true;
+}
+
+static inline void decode_cmd(uint8_t data, uint8_t *cmd, uint8_t *aux)
+{
+ /* Actual command will be in the high nibble and worker index in the low nibble. */
+ *cmd = data >> 4U;
+ *aux = data & 0xF;
+}
+
+static void build_result(const prog_config_t *config, result_t *result)
+{
+ uint32_t num = 0U;
+ const worker_config_t *worker_config;
+
+ for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
+ worker_config = &config->worker_config[i];
+
+ if (worker_config->thread.cpu != -1) {
+ result->workers[num].is_active = 1;
+ result->workers[num].thread_id = worker_config->summary.thread_id;
+ result->workers[num].cpu = worker_config->thread.cpu;
+ ++num;
+ }
+ }
+}
+
+static void run_command(cmd_fn_t cmd_fn, uint8_t aux, prog_config_t *config, int socket)
+{
+ const odp_bool_t is_ok = cmd_fn(config, aux);
+ const summary_t *summary = config->pending_summary;
+ uint8_t rep = !is_ok ? CMD_NOK : summary != NULL ? CMD_SUMMARY : CMD_OK;
+ result_t result;
+
+ (void)TEMP_FAILURE_RETRY(send(socket, &rep, sizeof(rep), MSG_NOSIGNAL));
+
+ /* Same machine, no internet in-between, just send the structs as is. */
+ if (rep == CMD_OK) {
+ memset(&result, 0, sizeof(result));
+ build_result(config, &result);
+ (void)TEMP_FAILURE_RETRY(send(socket, (const void *)&result, sizeof(result),
+ MSG_NOSIGNAL));
+ } else if (rep == CMD_SUMMARY) {
+ (void)TEMP_FAILURE_RETRY(send(socket, (const void *)summary, sizeof(*summary),
+ MSG_NOSIGNAL));
+ config->pending_summary = NULL;
+ }
+}
+
+static odp_bool_t setup_worker_config(worker_config_t *config)
+{
+ odp_thrmask_t tmask;
+ odp_schedule_group_t grp;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+
+ odp_thrmask_zero(&tmask);
+ grp = odp_schedule_group_create(NULL, &tmask);
+
+ if (grp == ODP_SCHED_GROUP_INVALID) {
+ log_fn(ODP_LOG_ERR, "Error creating scheduler group\n");
+ return false;
+ }
+
+ config->grp = grp;
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.group = config->grp;
+ queue = odp_queue_create(NULL, &queue_param);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ log_fn(ODP_LOG_ERR, "Error creating queue\n");
+ (void)odp_schedule_group_destroy(config->grp);
+ return false;
+ }
+
+ odp_ticketlock_lock(&config->lock);
+ config->queue = queue;
+ odp_ticketlock_unlock(&config->lock);
+
+ return true;
+}
+
+static inline int get_cpu(odp_cpumask_t *mask, int idx)
+{
+ int cpu = odp_cpumask_first(mask);
+
+ while (idx--) {
+ cpu = odp_cpumask_next(mask, cpu);
+
+ if (cpu < 0)
+ break;
+ }
+
+ return cpu;
+}
+
+static odp_bool_t signal_ready(int socket)
+{
+ uint8_t cmd = CMD_OK;
+ ssize_t ret;
+
+ ret = TEMP_FAILURE_RETRY(send(socket, &cmd, sizeof(cmd), MSG_NOSIGNAL));
+
+ if (ret != 1) {
+ log_fn(ODP_LOG_ERR, "Error signaling process readiness: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static void enq_to_next_queue(worker_config_t *config, int idx, odp_event_t ev, summary_t *summary)
+{
+ worker_config_t *worker_config;
+ int ret;
+
+ for (uint32_t i = 1U; i <= MAX_WORKERS; ++i) {
+ worker_config = &config[(idx + i) % MAX_WORKERS];
+ odp_ticketlock_lock(&worker_config->lock);
+
+ if (worker_config->queue == ODP_QUEUE_INVALID) {
+ odp_ticketlock_unlock(&worker_config->lock);
+ continue;
+ }
+
+ ret = odp_queue_enq(worker_config->queue, ev);
+ ++summary->num_handled;
+
+ if (ret < 0)
+ ++summary->enq_errs;
+
+ odp_ticketlock_unlock(&worker_config->lock);
+ return;
+ }
+
+ odp_event_free(ev);
+}
+
+static int run_worker(void *args)
+{
+ odp_time_t tm;
+ odp_thrmask_t tmask;
+ const int thread_id = odp_thread_id();
+ worker_config_t *config = args;
+ odp_event_t ev;
+ worker_config_t *configs = config->configs;
+ summary_t *summary = &config->summary;
+ const uint8_t idx = config->idx;
+
+ summary->thread_id = thread_id;
+ tm = odp_time_local_strict();
+ odp_thrmask_zero(&tmask);
+ odp_thrmask_set(&tmask, thread_id);
+
+ if (odp_schedule_group_join(config->grp, &tmask) < 0)
+ /* Log but still continue. */
+ log_fn(ODP_LOG_ERR, "Error joining scheduler group\n");
+
+ odp_barrier_wait(&config->barrier);
+
+ while (odp_atomic_load_u32(&config->is_running)) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ enq_to_next_queue(configs, idx, ev, summary);
+ }
+
+ while (true) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ enq_to_next_queue(configs, idx, ev, summary);
+ }
+
+ summary->runtime = odp_time_diff_ns(odp_time_local_strict(), tm);
+
+ return 0;
+}
+
+static void shutdown_worker(worker_config_t *config)
+{
+ odp_queue_t queue;
+
+ odp_ticketlock_lock(&config->lock);
+ queue = config->queue;
+ config->queue = ODP_QUEUE_INVALID;
+ odp_ticketlock_unlock(&config->lock);
+
+ odp_atomic_store_u32(&config->is_running, 0U);
+ (void)odph_thread_join(&config->thread, 1);
+ (void)odp_queue_destroy(queue);
+ (void)odp_schedule_group_destroy(config->grp);
+}
+
+static odp_bool_t bootstrap_scheduling(worker_config_t *config, odp_pool_t pool)
+{
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ if (buf == ODP_BUFFER_INVALID)
+ /* Event still in circulation. */
+ return true;
+
+ if (odp_queue_enq(config->queue, odp_buffer_to_event(buf)) < 0) {
+ log_fn(ODP_LOG_ERR, "Error enqueueing bootstrap event\n");
+ odp_buffer_free(buf);
+ shutdown_worker(config);
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t add_worker(prog_config_t *config, uint8_t idx)
+{
+ worker_config_t *worker_config;
+ odph_thread_common_param_t thr_common;
+ int set_cpu;
+ odp_cpumask_t cpumask;
+ odph_thread_param_t thr_param;
+
+ if (config->num_workers == MAX_WORKERS) {
+ log_fn(ODP_LOG_WARN, "Maximum number of workers already created\n");
+ return false;
+ }
+
+ if (idx >= MAX_WORKERS) {
+ log_fn(ODP_LOG_ERR, "Worker index out of bounds: %u\n", idx);
+ return false;
+ }
+
+ worker_config = &config->worker_config[idx];
+
+ if (worker_config->thread.cpu != -1) {
+ log_fn(ODP_LOG_WARN, "Worker already created: %u\n", idx);
+ return false;
+ }
+
+ set_cpu = get_cpu(&config->cpumask, idx);
+
+ if (set_cpu < 0) {
+ log_fn(ODP_LOG_ERR, "No CPU found for index: %u\n", idx);
+ return false;
+ }
+
+ memset(&worker_config->summary, 0, sizeof(worker_config->summary));
+
+ if (!setup_worker_config(worker_config))
+ return false;
+
+ worker_config->configs = config->worker_config;
+ worker_config->idx = idx;
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = config->instance;
+
+ odp_cpumask_zero(&cpumask);
+ odp_cpumask_set(&cpumask, set_cpu);
+ thr_common.cpumask = &cpumask;
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_worker;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+ thr_param.arg = worker_config;
+ odp_atomic_store_u32(&worker_config->is_running, 1U);
+ /* Control thread + worker thread = barrier count */
+ odp_barrier_init(&worker_config->barrier, 2);
+
+ if (odph_thread_create(&worker_config->thread, &thr_common, &thr_param, 1) != 1) {
+ log_fn(ODP_LOG_ERR, "Error creating worker\n");
+ (void)odp_queue_destroy(worker_config->queue);
+ (void)odp_schedule_group_destroy(worker_config->grp);
+ return false;
+ }
+
+ odp_barrier_wait(&worker_config->barrier);
+ ++config->num_workers;
+
+ if (config->num_workers == 1U && !bootstrap_scheduling(worker_config, config->pool))
+ return false;
+
+ return true;
+}
+
+static odp_bool_t remove_worker(prog_config_t *config, uint8_t idx)
+{
+ worker_config_t *worker_config;
+
+ if (config->num_workers == 0U) {
+ log_fn(ODP_LOG_WARN, "No more workers to remove\n");
+ return false;
+ }
+
+ if (idx >= MAX_WORKERS) {
+ log_fn(ODP_LOG_ERR, "Worker index out of bounds: %u\n", idx);
+ return false;
+ }
+
+ worker_config = &config->worker_config[idx];
+
+ if (worker_config->thread.cpu == -1) {
+ log_fn(ODP_LOG_WARN, "Worker already removed: %u\n", idx);
+ return false;
+ }
+
+ shutdown_worker(worker_config);
+ --config->num_workers;
+ worker_config->thread.cpu = -1;
+ config->pending_summary = &worker_config->summary;
+
+ return true;
+}
+
+static odp_bool_t do_exit(prog_config_t *config, uint8_t aux ODP_UNUSED)
+{
+ for (uint32_t i = 0U; i < MAX_WORKERS; ++i)
+ remove_worker(config, i);
+
+ return true;
+}
+
+static void run_prog(prog_config_t *config)
+{
+ odp_bool_t is_running = true;
+ int socket = config->socket;
+ ssize_t ret;
+ uint8_t data, cmd, aux;
+
+ while (is_running) {
+ ret = TEMP_FAILURE_RETRY(recv(socket, &data, sizeof(data), 0));
+
+ if (ret != 1)
+ continue;
+
+ decode_cmd(data, &cmd, &aux);
+
+ switch (cmd) {
+ case ADD_WORKER:
+ run_command(add_worker, aux, config, socket);
+ break;
+ case REM_WORKER:
+ run_command(remove_worker, aux, config, socket);
+ break;
+ case EXIT_PROG:
+ run_command(do_exit, aux, config, socket);
+ is_running = false;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void teardown_prog(prog_config_t *config)
+{
+ (void)odp_pool_destroy(config->pool);
+}
+
+static void run_odp(char *cpumask, int socket)
+{
+ odp_instance_t odp_instance;
+ odp_init_t param;
+ odp_shm_t shm_cfg = ODP_SHM_INVALID;
+
+ odp_init_param_init(&param);
+ param.log_fn = log_fn;
+
+ if (odp_init_global(&odp_instance, &param, NULL)) {
+ log_fn(ODP_LOG_ERR, "ODP global init failed\n");
+ return;
+ }
+
+ if (odp_init_local(odp_instance, ODP_THREAD_CONTROL)) {
+ log_fn(ODP_LOG_ERR, "ODP local init failed\n");
+ return;
+ }
+
+ shm_cfg = odp_shm_reserve(NULL, sizeof(prog_config_t), ODP_CACHE_LINE_SIZE, 0U);
+
+ if (shm_cfg == ODP_SHM_INVALID) {
+ log_fn(ODP_LOG_ERR, "Error reserving shared memory\n");
+ return;
+ }
+
+ prog_conf = odp_shm_addr(shm_cfg);
+
+ if (prog_conf == NULL) {
+ log_fn(ODP_LOG_ERR, "Error resolving shared memory address\n");
+ return;
+ }
+
+ if (odp_schedule_config(NULL) < 0) {
+ log_fn(ODP_LOG_ERR, "Error configuring scheduler\n");
+ return;
+ }
+
+ if (!setup_prog_config(prog_conf, odp_instance, cpumask, socket))
+ return;
+
+ if (!signal_ready(prog_conf->socket))
+ return;
+
+ run_prog(prog_conf);
+ teardown_prog(prog_conf);
+ (void)odp_shm_free(shm_cfg);
+
+ if (odp_term_local()) {
+ log_fn(ODP_LOG_ERR, "ODP local terminate failed\n");
+ return;
+ }
+
+ if (odp_term_global(odp_instance)) {
+ log_fn(ODP_LOG_ERR, "ODP global terminate failed\n");
+ return;
+ }
+}
+
+static odp_bool_t wait_process_ready(int socket)
+{
+ uint8_t data;
+ ssize_t ret;
+
+ ret = TEMP_FAILURE_RETRY(recv(socket, &data, sizeof(data), 0));
+
+ if (ret <= 0) {
+ if (ret < 0)
+ perror("recv");
+
+ return false;
+ }
+
+ return true;
+}
+
+static inline odp_bool_t is_interactive(const global_config_t *config)
+{
+ return config->num_p_elems == 0U;
+}
+
+static void print_cli_usage(void)
+{
+ printf("\nValid commands are:\n\n");
+
+ for (uint32_t i = 0U; i < ODPH_ARRAY_SIZE(cmdstrs); ++i)
+ printf(" %s <process index> <worker index>\n", cmdstrs[i]);
+
+ printf("\n");
+}
+
+static char *get_format_str(uint32_t max_cmd_len)
+{
+ const int cmd_len = snprintf(NULL, 0U, "%u", max_cmd_len);
+ uint32_t str_len;
+
+ if (cmd_len <= 0)
+ return NULL;
+
+ str_len = strlen("%s %u %u") + cmd_len + 1U;
+
+ char fmt[str_len];
+
+ snprintf(fmt, str_len, "%%%ds %%u %%u", max_cmd_len);
+
+ return strdup(fmt);
+}
+
+static uint8_t map_str_to_command(const char *cmdstr, uint32_t len)
+{
+ for (uint32_t i = 0U; i < ODPH_ARRAY_SIZE(cmdstrs); ++i)
+ if (strncmp(cmdstr, cmdstrs[i], len) == 0)
+ return i;
+
+ return UNKNOWN_CMD;
+}
+
+static odp_bool_t get_stdin_command(global_config_t *config, uint8_t *cmd, uint32_t *prog_idx,
+ uint32_t *worker_idx)
+{
+ char *input, cmdstr[config->max_cmd_len + 1U], *fmt;
+ size_t size;
+ ssize_t ret;
+
+ input = NULL;
+ memset(cmdstr, 0, sizeof(cmdstr));
+ printf("> ");
+ ret = getline(&input, &size, stdin);
+
+ if (ret == -1)
+ return false;
+
+ fmt = get_format_str(config->max_cmd_len);
+
+ if (fmt == NULL) {
+ printf("Unable to parse command\n");
+ return false;
+ }
+
+ ret = sscanf(input, fmt, cmdstr, prog_idx, worker_idx);
+ free(input);
+ free(fmt);
+
+ if (ret == EOF)
+ return false;
+
+ if (ret != 3) {
+ printf("Unable to parse command\n");
+ return false;
+ }
+
+ *cmd = map_str_to_command(cmdstr, config->max_cmd_len);
+ return true;
+}
+
+static uint8_t map_char_to_command(char cmdchar)
+{
+ switch (cmdchar) {
+ case ADDITION:
+ return ADD_WORKER;
+ case REMOVAL:
+ return REM_WORKER;
+ case DELAY:
+ return DELAY_PROG;
+ default:
+ return UNKNOWN_CMD;
+ }
+}
+
+static odp_bool_t get_pattern_command(global_config_t *config, uint8_t *cmd, uint32_t *prog_idx,
+ uint32_t *worker_idx)
+{
+ static uint32_t i;
+ const pattern_t *pattern;
+ struct timespec ts;
+
+ if (i == config->num_p_elems) {
+ config->is_running = false;
+ return false;
+ }
+
+ pattern = &config->pattern[i++];
+ *cmd = map_char_to_command(pattern->op);
+
+ if (*cmd == DELAY_PROG) {
+ ts.tv_sec = pattern->val1 / ODP_TIME_SEC_IN_NS;
+ ts.tv_nsec = pattern->val1 % ODP_TIME_SEC_IN_NS;
+ nanosleep(&ts, NULL);
+ return false;
+ }
+
+ *prog_idx = pattern->val1;
+ *worker_idx = pattern->val2;
+
+ return true;
+}
+
+static inline uint8_t encode_cmd(uint8_t cmd, uint8_t worker_idx)
+{
+ /* Actual command will be in the high nibble and worker index in the low nibble. */
+ cmd <<= 4U;
+ cmd |= worker_idx;
+
+ return cmd;
+}
+
+static odp_bool_t is_peer_down(int error)
+{
+ return error == ECONNRESET || error == EPIPE || error == ETIMEDOUT;
+}
+
+static int send_command(int socket, uint8_t cmd)
+{
+ uint8_t data;
+ ssize_t ret;
+ odp_bool_t is_down;
+
+ ret = TEMP_FAILURE_RETRY(send(socket, &cmd, sizeof(cmd), MSG_NOSIGNAL));
+
+ if (ret != 1) {
+ is_down = is_peer_down(errno);
+ perror("send");
+ return is_down ? PEER_ERR : CONN_ERR;
+ }
+
+ ret = TEMP_FAILURE_RETRY(recv(socket, &data, sizeof(data), 0));
+
+ if (ret <= 0) {
+ is_down = ret == 0 || is_peer_down(errno);
+
+ if (ret < 0)
+ perror("recv");
+
+ return is_down ? PEER_ERR : CONN_ERR;
+ }
+
+ return data;
+}
+
+static odp_bool_t recv_summary(int socket, summary_t *summary)
+{
+ const ssize_t size = sizeof(*summary),
+ ret = TEMP_FAILURE_RETRY(recv(socket, summary, size, 0));
+
+ return ret == size;
+}
+
+static void dump_summary(pid_t pid, const summary_t *summary)
+{
+ printf("\nremoved worker summary:\n"
+ " ODP process ID: %d\n"
+ " thread ID: %" PRIu64 "\n"
+ " events handled: %" PRIu64 "\n"
+ " enqueue errors: %" PRIu64 "\n"
+ " runtime: %" PRIu64 " (ns)\n\n", pid, summary->thread_id,
+ summary->num_handled, summary->enq_errs, summary->runtime);
+}
+
+static odp_bool_t check_summary(const summary_t *summary)
+{
+ if (summary->num_handled == 0U) {
+ printf("Summary check failure: no events handled\n");
+ return false;
+ }
+
+ if (summary->enq_errs > 0U) {
+ printf("Summary check failure: enqueue errors\n");
+ return false;
+ }
+
+ if (summary->runtime == 0U) {
+ printf("Summary check failure: no run time recorded\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void dump_result(int socket, pid_t pid)
+{
+ result_t result;
+ const ssize_t size = sizeof(result),
+ ret = TEMP_FAILURE_RETRY(recv(socket, &result, size, 0));
+
+ if (ret != size)
+ return;
+
+ printf("\nODP process %d:\n"
+ "|\n", pid);
+
+ for (uint32_t i = 0U; i < MAX_WORKERS; i++)
+ if (result.workers[i].is_active)
+ printf("|--- Worker thread ID %u on CPU %u\n",
+ result.workers[i].thread_id, result.workers[i].cpu);
+
+ printf("\n");
+}
+
+static odp_bool_t run_global(global_config_t *config)
+{
+ input_fn_t input_fn;
+ uint32_t prog_idx, worker_idx;
+ uint8_t cmd;
+ prog_t *prog;
+ ssize_t ret;
+ odp_bool_t is_recv, func_ret = true;
+
+ print_cli_usage();
+ input_fn = is_interactive(config) ? get_stdin_command : get_pattern_command;
+ config->is_running = true;
+
+ while (config->is_running) {
+ if (!input_fn(config, &cmd, &prog_idx, &worker_idx))
+ continue;
+
+ if (cmd == UNKNOWN_CMD) {
+ printf("Unrecognized command\n");
+ continue;
+ }
+
+ if (prog_idx >= config->num_progs) {
+ printf("Invalid process index: %u\n", prog_idx);
+ continue;
+ }
+
+ prog = &config->progs[prog_idx];
+
+ if (prog->state == DOWN) {
+ printf("ODP process index %u has already exited\n", prog_idx);
+ continue;
+ }
+
+ ret = send_command(prog->socket, encode_cmd(cmd, worker_idx));
+
+ if (ret == CONN_ERR) {
+ printf("Fatal connection error, aborting\n");
+ abort();
+ }
+
+ if (ret == PEER_ERR) {
+ printf("ODP process index %u has exited\n", prog_idx);
+ prog->state = DOWN;
+ continue;
+ }
+
+ if (ret == CMD_NOK) {
+ printf("ODP process index %u was unable to execute the command\n",
+ prog_idx);
+ continue;
+ }
+
+ if (ret == CMD_SUMMARY) {
+ is_recv = recv_summary(prog->socket, &prog->summary);
+
+ if (is_recv)
+ dump_summary(prog->pid, &prog->summary);
+
+ if (!is_interactive(config) &&
+ !(is_recv && check_summary(&prog->summary))) {
+ config->is_running = false;
+ func_ret = false;
+ }
+
+ continue;
+ }
+
+ if (ret == CMD_OK)
+ dump_result(prog->socket, prog->pid);
+ }
+
+ for (uint32_t i = 0U; i < config->num_progs; ++i) {
+ prog = &config->progs[i];
+
+ if (prog->state == UP) {
+ for (uint32_t j = 0U; j < MAX_WORKERS; ++j) {
+ ret = send_command(prog->socket, encode_cmd(REM_WORKER, j));
+
+ if (ret == CONN_ERR || ret == PEER_ERR)
+ break;
+
+ if (ret != CMD_SUMMARY)
+ continue;
+
+ if (recv_summary(prog->socket, &prog->summary))
+ dump_summary(prog->pid, &prog->summary);
+ }
+
+ (void)send_command(prog->socket, encode_cmd(EXIT_PROG, 0));
+ (void)TEMP_FAILURE_RETRY(waitpid(prog->pid, NULL, 0));
+ }
+ }
+
+ return func_ret;
+}
+
+static void teardown_global(const global_config_t *config)
+{
+ const prog_t *prog;
+
+ for (uint32_t i = 0U; i < config->num_progs; ++i) {
+ prog = &config->progs[i];
+ close(prog->socket);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ parse_result_t res;
+ int ret, func_ret = EXIT_SUCCESS;
+ prog_t *prog;
+ pid_t pid, ppid;
+ const size_t envsize = strlen(ENV_PREFIX S(MAX_PROGS)) + 1U;
+ char *env, prog_env[envsize];
+
+ if (!setup_signals()) {
+ printf("Error setting up signals, exiting\n");
+ return EXIT_FAILURE;
+ }
+
+ res = parse_options(argc, argv, &conf);
+
+ if (res == PRS_NOK)
+ return EXIT_FAILURE;
+
+ if (res == PRS_TERM)
+ return EXIT_SUCCESS;
+
+ printf("*** ODP dynamic worker tester ***\n\n");
+
+ for (uint32_t i = 0U; i < conf.num_progs; ++i) {
+ int sockets[2U];
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+
+ if (ret == -1) {
+ perror("socketpair");
+ return EXIT_FAILURE;
+ }
+
+ prog = &conf.progs[i];
+ snprintf(prog_env, envsize, "%s%u", ENV_PREFIX, i);
+ env = getenv(prog_env);
+
+ if (env != NULL)
+ prog->env = strdup(env);
+
+ prog->socket = sockets[PARENT];
+ ppid = getpid();
+ pid = fork();
+
+ if (pid == -1) {
+ perror("fork");
+ return EXIT_FAILURE;
+ }
+
+ if (pid == 0) {
+ close(sockets[PARENT]);
+
+ if (!setup_pkill(ppid)) {
+ log_fn(ODP_LOG_ERR, "Error setting up pdeath signal, exiting\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!disable_stream(STDIN_FILENO, true) ||
+ !disable_stream(STDERR_FILENO, false) ||
+ !disable_stream(STDOUT_FILENO, false)) {
+ log_fn(ODP_LOG_ERR, "Error disabling streams, exiting\n");
+ return EXIT_FAILURE;
+ }
+
+ if (prog->env != NULL && !set_odp_env(prog->env)) {
+ log_fn(ODP_LOG_ERR, "Error setting up environment, exiting\n");
+ return EXIT_FAILURE;
+ }
+
+ run_odp(prog->cpumask, sockets[CHILD]);
+ goto exit;
+ } else {
+ close(sockets[CHILD]);
+ prog->pid = pid;
+
+ if (!wait_process_ready(prog->socket)) {
+ printf("Error launching process: %d, exiting\n", prog->pid);
+ return EXIT_FAILURE;
+ }
+
+ prog->state = UP;
+ printf("Created ODP process, pid: %d, CPU mask: %s, process index: %u\n",
+ prog->pid, prog->cpumask, i);
+ }
+ }
+
+ func_ret = run_global(&conf) ? EXIT_SUCCESS : EXIT_FAILURE;
+ teardown_global(&conf);
+
+exit:
+ return func_ret;
+}
diff --git a/test/miscellaneous/odp_dyn_workers_run.sh b/test/miscellaneous/odp_dyn_workers_run.sh
new file mode 100755
index 000000000..f58520a6c
--- /dev/null
+++ b/test/miscellaneous/odp_dyn_workers_run.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2024 Nokia
+#
+
+MAX_CPUS=$(nproc)
+# Frontend and control threads on one core and to-be-swapped worker thread on another core,
+# otherwise weird issues might occur
+REQ_CPUS=2
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+BIN=odp_dyn_workers
+DEL=100000000
+
+if [ ${MAX_CPUS} -lt ${REQ_CPUS} ]; then
+ echo "Not enough CPUs (requested ${REQ_CPUS}, available ${MAX_CPUS}). Skipping test."
+ exit 77
+fi
+
+taskset -c 0 ${TEST_DIR}/${BIN}${EXEEXT} -c 0x2,0x2 -p a0:0,d${DEL},r0:0,d${DEL},a1:0,d${DEL},r1:0
diff --git a/test/performance/.gitignore b/test/performance/.gitignore
new file mode 100644
index 000000000..d5ab7df24
--- /dev/null
+++ b/test/performance/.gitignore
@@ -0,0 +1,33 @@
+*.log
+*.trs
+odp_atomic
+odp_atomic_perf
+odp_bench_buffer
+odp_bench_misc
+odp_bench_packet
+odp_bench_pktio_sp
+odp_bench_timer
+odp_cpu_bench
+odp_crc
+odp_crypto
+odp_dmafwd
+odp_dma_perf
+odp_ipsec
+odp_ipsecfwd
+odp_l2fwd
+odp_lock_perf
+odp_mem_perf
+odp_packet_gen
+odp_pktio_ordered
+odp_pktio_perf
+odp_pool_latency
+odp_pool_perf
+odp_queue_perf
+odp_random
+odp_sched_latency
+odp_sched_perf
+odp_sched_pktio
+odp_stash_perf
+odp_stress
+odp_timer_accuracy
+odp_timer_perf
diff --git a/test/performance/Makefile.am b/test/performance/Makefile.am
new file mode 100644
index 000000000..8142d5db9
--- /dev/null
+++ b/test/performance/Makefile.am
@@ -0,0 +1,120 @@
+include $(top_srcdir)/test/Makefile.inc
+
+TESTS_ENVIRONMENT += TEST_DIR=${builddir}
+
+EXECUTABLES = odp_atomic_perf \
+ odp_bench_buffer \
+ odp_bench_misc \
+ odp_bench_packet \
+ odp_bench_pktio_sp \
+ odp_bench_timer \
+ odp_crc \
+ odp_lock_perf \
+ odp_mem_perf \
+ odp_pktio_perf \
+ odp_pool_latency \
+ odp_pool_perf \
+ odp_queue_perf \
+ odp_stash_perf \
+ odp_random \
+ odp_stress
+
+COMPILE_ONLY = odp_cpu_bench \
+ odp_crypto \
+ odp_dmafwd \
+ odp_dma_perf \
+ odp_ipsec \
+ odp_l2fwd \
+ odp_packet_gen \
+ odp_pktio_ordered \
+ odp_sched_latency \
+ odp_sched_perf \
+ odp_sched_pktio \
+ odp_timer_accuracy \
+ odp_timer_perf
+
+if LIBCONFIG
+COMPILE_ONLY += odp_ipsecfwd
+endif
+
+TESTSCRIPTS = odp_cpu_bench_run.sh \
+ odp_crypto_run.sh \
+ odp_dma_perf_run.sh \
+ odp_ipsec_run.sh \
+ odp_l2fwd_run.sh \
+ odp_packet_gen_run.sh \
+ odp_sched_latency_run.sh \
+ odp_sched_perf_run.sh \
+ odp_sched_pktio_run.sh \
+ odp_timer_accuracy_run.sh \
+ odp_timer_perf_run.sh
+
+if ODP_PKTIO_PCAP
+TESTSCRIPTS += odp_dmafwd_run.sh \
+ odp_pktio_ordered_run.sh
+endif
+
+TEST_EXTENSIONS = .sh
+
+if test_perf
+TESTS = $(EXECUTABLES) $(TESTSCRIPTS)
+endif
+
+bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
+
+odp_atomic_perf_SOURCES = odp_atomic_perf.c
+odp_bench_buffer_SOURCES = odp_bench_buffer.c bench_common.c bench_common.h
+odp_bench_misc_SOURCES = odp_bench_misc.c bench_common.c bench_common.h
+odp_bench_packet_SOURCES = odp_bench_packet.c bench_common.c bench_common.h
+odp_bench_pktio_sp_SOURCES = odp_bench_pktio_sp.c bench_common.c bench_common.h
+odp_bench_timer_SOURCES = odp_bench_timer.c bench_common.c bench_common.h
+odp_cpu_bench_SOURCES = odp_cpu_bench.c
+odp_crc_SOURCES = odp_crc.c
+odp_crypto_SOURCES = odp_crypto.c
+odp_dmafwd_SOURCES = odp_dmafwd.c
+odp_dma_perf_SOURCES = odp_dma_perf.c
+odp_ipsec_SOURCES = odp_ipsec.c
+odp_lock_perf_SOURCES = odp_lock_perf.c
+odp_mem_perf_SOURCES = odp_mem_perf.c
+odp_packet_gen_SOURCES = odp_packet_gen.c
+odp_pktio_ordered_SOURCES = odp_pktio_ordered.c dummy_crc.h
+odp_sched_latency_SOURCES = odp_sched_latency.c
+odp_sched_pktio_SOURCES = odp_sched_pktio.c
+odp_pktio_perf_SOURCES = odp_pktio_perf.c
+odp_pool_latency_SOURCES = odp_pool_latency.c
+odp_pool_perf_SOURCES = odp_pool_perf.c
+odp_queue_perf_SOURCES = odp_queue_perf.c
+odp_random_SOURCES = odp_random.c
+odp_sched_perf_SOURCES = odp_sched_perf.c
+odp_stress_SOURCES = odp_stress.c
+odp_timer_accuracy_SOURCES = odp_timer_accuracy.c
+odp_timer_perf_SOURCES = odp_timer_perf.c
+
+if LIBCONFIG
+odp_ipsecfwd_SOURCES = odp_ipsecfwd.c
+AM_CFLAGS += $(LIBCONFIG_CFLAGS)
+endif
+
+dist_check_SCRIPTS = $(TESTSCRIPTS)
+
+dist_check_DATA = udp64.pcap
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS) $(dist_check_DATA); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(dist_check_SCRIPTS) $(dist_check_DATA); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/test/performance/bench_common.c b/test/performance/bench_common.c
new file mode 100644
index 000000000..640889503
--- /dev/null
+++ b/test/performance/bench_common.c
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+void bench_suite_init(bench_suite_t *suite)
+{
+ memset(suite, 0, sizeof(bench_suite_t));
+
+ suite->measure_time = true;
+
+ odp_atomic_init_u32(&suite->exit_worker, 0);
+}
+
+void bench_run_indef(bench_info_t *info, odp_atomic_u32_t *exit_thread)
+{
+ const char *desc;
+
+ desc = info->desc != NULL ? info->desc : info->name;
+
+ printf("Running odp_%s test indefinitely\n", desc);
+
+ while (!odp_atomic_load_u32(exit_thread)) {
+ int ret;
+
+ if (info->init != NULL)
+ info->init();
+
+ ret = info->run();
+
+ if (info->term != NULL)
+ info->term();
+
+ if (!ret)
+ ODPH_ABORT("Benchmark %s failed\n", desc);
+ }
+}
+
+int bench_run(void *arg)
+{
+ uint64_t c1, c2;
+ odp_time_t t1, t2;
+ bench_suite_t *suite = arg;
+ const uint64_t repeat_count = suite->repeat_count;
+ const odp_bool_t meas_time = suite->measure_time;
+ double result;
+
+ printf("\nAverage %s per function call\n", meas_time ? "time (nsec)" : "CPU cycles");
+ printf("-------------------------------------------------\n");
+
+ for (int j = 0; j < suite->num_bench; j++) {
+ int ret;
+ const char *desc;
+ const bench_info_t *bench = &suite->bench[j];
+ uint64_t max_rounds = suite->rounds;
+ uint64_t total = 0;
+
+ if (bench->max_rounds && bench->max_rounds < max_rounds)
+ max_rounds = bench->max_rounds;
+
+ /* Run selected test indefinitely */
+ if (suite->indef_idx) {
+ if ((j + 1) != suite->indef_idx) {
+ j++;
+ continue;
+ }
+ bench_run_indef(&suite->bench[j], &suite->exit_worker);
+ return 0;
+ }
+
+ desc = bench->desc != NULL ? bench->desc : bench->name;
+
+ /* The zeroeth round is a warmup round that will be ignored */
+ for (uint64_t round = 0; round <= max_rounds; round++) {
+ if (bench->init != NULL)
+ bench->init();
+
+ if (meas_time)
+ t1 = odp_time_local_strict();
+ else
+ c1 = odp_cpu_cycles();
+
+ ret = bench->run();
+
+ if (meas_time)
+ t2 = odp_time_local_strict();
+ else
+ c2 = odp_cpu_cycles();
+
+ if (bench->term != NULL)
+ bench->term();
+
+ if (!ret) {
+ ODPH_ERR("Benchmark odp_%s failed\n", desc);
+ suite->retval = -1;
+ return -1;
+ }
+
+ if (odp_unlikely(round == 0))
+ continue;
+ if (meas_time)
+ total += odp_time_diff_ns(t2, t1);
+ else
+ total += odp_cpu_cycles_diff(c2, c1);
+ }
+
+ /* Each benchmark runs internally 'repeat_count' times. */
+ result = ((double)total) / (max_rounds * repeat_count);
+
+ printf("[%02d] odp_%-26s: %12.2f\n", j + 1, desc, result);
+ if (suite->result)
+ suite->result[j] = result;
+ }
+ printf("\n");
+ /* Print dummy result to prevent compiler to optimize it away*/
+ if (suite->dummy)
+ printf("(dummy result: 0x%" PRIx64 ")\n\n", suite->dummy);
+
+ return 0;
+}
+
+void bench_tm_suite_init(bench_tm_suite_t *suite)
+{
+ memset(suite, 0, sizeof(bench_tm_suite_t));
+
+ odp_atomic_init_u32(&suite->exit_worker, 0);
+}
+
+uint8_t bench_tm_func_register(bench_tm_result_t *res, const char *func_name)
+{
+ uint8_t num_func = res->num;
+
+ if (num_func >= BENCH_TM_MAX_FUNC)
+ ODPH_ABORT("Too many test functions (max %d)\n", BENCH_TM_MAX_FUNC);
+
+ res->func[num_func].name = func_name;
+ res->num++;
+
+ return num_func;
+}
+
+void bench_tm_func_record(odp_time_t t2, odp_time_t t1, bench_tm_result_t *res, uint8_t id)
+{
+ odp_time_t diff = odp_time_diff(t2, t1);
+
+ ODPH_ASSERT(id < BENCH_TM_MAX_FUNC);
+
+ res->func[id].tot = odp_time_sum(res->func[id].tot, diff);
+
+ if (odp_time_cmp(diff, res->func[id].min) < 0)
+ res->func[id].min = diff;
+
+ if (odp_time_cmp(diff, res->func[id].max) > 0)
+ res->func[id].max = diff;
+
+ res->func[id].num++;
+}
+
+static void init_result(bench_tm_result_t *res)
+{
+ memset(res, 0, sizeof(bench_tm_result_t));
+
+ for (int i = 0; i < BENCH_TM_MAX_FUNC; i++) {
+ res->func[i].tot = ODP_TIME_NULL;
+ res->func[i].min = odp_time_local_from_ns(ODP_TIME_HOUR_IN_NS);
+ res->func[i].max = ODP_TIME_NULL;
+ }
+}
+
+static void print_results(bench_tm_result_t *res)
+{
+ for (uint8_t i = 0; i < res->num; i++) {
+ uint64_t num = res->func[i].num ? res->func[i].num : 1;
+
+ printf(" %-38s %-12" PRIu64 " %-12" PRIu64 " %-12" PRIu64 "\n",
+ res->func[i].name,
+ odp_time_to_ns(res->func[i].min),
+ odp_time_to_ns(res->func[i].tot) / num,
+ odp_time_to_ns(res->func[i].max));
+ }
+}
+
+int bench_tm_run(void *arg)
+{
+ bench_tm_suite_t *suite = arg;
+
+ printf("\nLatency (nsec) per function call min avg max\n");
+ printf("------------------------------------------------------------------------------\n");
+
+ for (uint32_t j = 0; j < suite->num_bench; j++) {
+ const bench_tm_info_t *bench = &suite->bench[j];
+ uint64_t rounds = suite->rounds;
+ bench_tm_result_t res;
+
+ /* Run only selected test case */
+ if (suite->bench_idx && (j + 1) != suite->bench_idx)
+ continue;
+
+ if (bench->cond != NULL && !bench->cond()) {
+ printf("[%02d] %-41s n/a n/a n/a\n",
+ j + 1, bench->name);
+ continue;
+ }
+
+ if (bench->max_rounds && bench->max_rounds < rounds)
+ rounds = bench->max_rounds;
+
+ /*
+ * Run each test twice.
+ * Results from the first warm-up round are ignored.
+ */
+ for (uint32_t i = 0; i < 2; i++) {
+ if (odp_atomic_load_u32(&suite->exit_worker))
+ return 0;
+
+ init_result(&res);
+
+ if (bench->init != NULL)
+ bench->init();
+
+ if (bench->run(&res, rounds)) {
+ ODPH_ERR("Benchmark %s failed\n", bench->name);
+ suite->retval = -1;
+ return -1;
+ }
+
+ if (bench->term != NULL)
+ bench->term();
+
+ }
+ printf("[%02d] %-26s\n", j + 1, bench->name);
+ print_results(&res);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/test/performance/bench_common.h b/test/performance/bench_common.h
new file mode 100644
index 000000000..4b59c941f
--- /dev/null
+++ b/test/performance/bench_common.h
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef BENCH_COMMON_H
+#define BENCH_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <stdint.h>
+
+/**
+ * Check benchmark preconditions
+ *
+ * Returns !0 if benchmark precondition is met.
+ */
+typedef int (*bench_cond_fn_t)(void);
+
+/**
+ * Initialize benchmark resources
+ */
+typedef void (*bench_init_fn_t)(void);
+
+/**
+ * Run benchmark
+ *
+ * Returns >0 on success.
+ */
+typedef int (*bench_run_fn_t)(void);
+
+/**
+ * Release benchmark resources
+ */
+typedef void (*bench_term_fn_t)(void);
+
+/* Benchmark test data */
+typedef struct {
+ /* Default test name */
+ const char *name;
+
+ /* Optional alternate test description */
+ const char *desc;
+
+ /* Optional precondition to run test */
+ bench_cond_fn_t cond;
+
+ /* Optional test initializer function */
+ bench_init_fn_t init;
+
+ /* Test function to run */
+ bench_run_fn_t run;
+
+ /* Optional test terminate function */
+ bench_term_fn_t term;
+
+ /* Optional test specific limit for rounds (tuning for slow implementations) */
+ uint32_t max_rounds;
+
+} bench_info_t;
+
+/* Benchmark suite data */
+typedef struct {
+ /* Array of benchmark functions */
+ bench_info_t *bench;
+
+ /* Number of benchmark functions */
+ int num_bench;
+
+ /* Optional benchmark index to run indefinitely (1...num_bench) */
+ int indef_idx;
+
+ /* Suite exit value output */
+ int retval;
+
+ /* Measure time vs. CPU cycles */
+ odp_bool_t measure_time;
+
+ /* Break worker loop if set to 1 */
+ odp_atomic_u32_t exit_worker;
+
+ /* Number of API function calls per test case */
+ uint64_t repeat_count;
+
+ /* Number of rounds per test case */
+ uint64_t rounds;
+
+ /* Dummy test result output */
+ uint64_t dummy;
+
+ /* Optional test result output array */
+ double *result;
+
+} bench_suite_t;
+
+/**
+ * Initialize benchmark suite parameters
+ */
+void bench_suite_init(bench_suite_t *suite);
+
+/**
+ * Run selected test indefinitely
+ */
+void bench_run_indef(bench_info_t *info, odp_atomic_u32_t *exit_thread);
+
+/**
+ * Run test suite and print results
+ *
+ * The argument is of type 'bench_suite_t *'. Returns 0 on success and <0 on failure.
+ */
+int bench_run(void *arg);
+
+/*
+ * Timed benchmark framework
+ *
+ * The main difference compared to the standard benchmark suite is that all
+ * latency measurements are performed inside the test cases.
+ */
+
+/* Maximum number of benchmarked functions per test case */
+#define BENCH_TM_MAX_FUNC 8
+
+/* Timed benchmark results */
+typedef struct bench_tm_results_s {
+ /* Results per function */
+ struct {
+ /* Name of function */
+ const char *name;
+
+ /* Total duration of all function calls */
+ odp_time_t tot;
+
+ /* Minimum duration */
+ odp_time_t min;
+
+ /* Maximum duration */
+ odp_time_t max;
+
+ /* Number of measurements */
+ uint64_t num;
+
+ } func[BENCH_TM_MAX_FUNC];
+
+ /* Number of registered test functions */
+ uint8_t num;
+
+} bench_tm_result_t;
+
+/**
+ * Timed benchmark test case
+ *
+ * Returns 0 on success and <0 on failure.
+ */
+typedef int (*bench_tm_run_fn_t)(bench_tm_result_t *res, int repeat_count);
+
+/* Timed benchmark test case */
+typedef struct {
+ /* Test case name */
+ const char *name;
+
+ /* Optional precondition to run test */
+ bench_cond_fn_t cond;
+
+ /* Optional test initializer function */
+ bench_init_fn_t init;
+
+ /* Test function to run */
+ bench_tm_run_fn_t run;
+
+ /* Optional test termination function */
+ bench_term_fn_t term;
+
+ /* Optional test specific limit for rounds (tuning for slow implementations) */
+ uint32_t max_rounds;
+
+} bench_tm_info_t;
+
+/* Timed benchmark suite data */
+typedef struct {
+ /* Array of benchmark test cases */
+ bench_tm_info_t *bench;
+
+ /* Number of benchmark test cases */
+ uint32_t num_bench;
+
+ /* Optional benchmark index to run (1...num_bench) */
+ uint32_t bench_idx;
+
+ /* Suite exit value output */
+ int retval;
+
+ /* Number of rounds per test case */
+ uint64_t rounds;
+
+ /* Break worker loop if set to 1 */
+ odp_atomic_u32_t exit_worker;
+
+} bench_tm_suite_t;
+
+/**
+ * Initialize benchmark suite data
+ */
+void bench_tm_suite_init(bench_tm_suite_t *suite);
+
+/**
+ * Register function for benchmarking
+ *
+ * Called by each test case to register benchmarked functions. Returns function
+ * ID for recording benchmark results. At most BENCH_TM_MAX_FUNC functions can
+ * be registered per test case.
+ */
+uint8_t bench_tm_func_register(bench_tm_result_t *res, const char *func_name);
+
+/**
+ * Record results for previously registered function
+ *
+ * Test case must call this function every test round for each registered
+ * function.
+ */
+void bench_tm_func_record(odp_time_t t2, odp_time_t t1, bench_tm_result_t *res, uint8_t id);
+
+/**
+ * Run timed test suite and print results
+ *
+ * The argument is of type 'bench_tm_suite_t *'. Returns 0 on success and <0 on failure.
+ */
+int bench_tm_run(void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/performance/dummy_crc.h b/test/performance/dummy_crc.h
new file mode 100644
index 000000000..8491b8fdc
--- /dev/null
+++ b/test/performance/dummy_crc.h
@@ -0,0 +1,463 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ *
+ * Copyright(c) 2010-2014 Intel Corporation
+ * - lib/hash/rte_crc_sw.h
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+/**
+ * @file
+ *
+ * This file is meant only for generating dummy processing work.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+static const uint32_t dummy_crc32c_tables[8][256] = {{
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C,
+ 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+ 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C,
+ 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC,
+ 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+ 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512,
+ 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD,
+ 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+ 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF,
+ 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F,
+ 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+ 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F,
+ 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E,
+ 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+ 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E,
+ 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE,
+ 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+ 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4,
+ 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B,
+ 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+ 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5,
+ 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975,
+ 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+ 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905,
+ 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8,
+ 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+ 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8,
+ 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78,
+ 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+ 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6,
+ 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69,
+ 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+ 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
+},
+{
+ 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB,
+ 0x69CF5132, 0x7A6DC945, 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21,
+ 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, 0x3FC5F181, 0x2C6769F6,
+ 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,
+ 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92,
+ 0xCB1E630B, 0xD8BCFB7C, 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B,
+ 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, 0xE29F20BA, 0xF13DB8CD,
+ 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF,
+ 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28,
+ 0x298143B1, 0x3A23DBC6, 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2,
+ 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, 0xFF17C604, 0xECB55E73,
+ 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41,
+ 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17,
+ 0x0BCC548E, 0x186ECCF9, 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C,
+ 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, 0x5DC6F43D, 0x4E646C4A,
+ 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,
+ 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD,
+ 0xE9537434, 0xFAF1EC43, 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27,
+ 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, 0xBF59D487, 0xACFB4CF0,
+ 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2,
+ 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94,
+ 0x4B82460D, 0x5820DE7A, 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260,
+ 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, 0x66D73941, 0x7575A136,
+ 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004,
+ 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3,
+ 0xADC95A4A, 0xBE6BC23D, 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059,
+ 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, 0x844819FB, 0x97EA818C,
+ 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,
+ 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8,
+ 0x70938B71, 0x63311306, 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3,
+ 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, 0x26992BC2, 0x353BB3B5,
+ 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287,
+ 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556,
+ 0x6D1B6DCF, 0x7EB9F5B8, 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC,
+ 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, 0x3B11CD7C, 0x28B3550B,
+ 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439,
+ 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F,
+ 0xCFCA5FF6, 0xDC68C781, 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766,
+ 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, 0xE64B1C47, 0xF5E98430,
+ 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,
+ 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5,
+ 0x2D557F4C, 0x3EF7E73B, 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F,
+ 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483,
+},
+{
+ 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664,
+ 0xD1B1F617, 0x74F06469, 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6,
+ 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, 0x70A27D8A, 0xD5E3EFF4,
+ 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,
+ 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B,
+ 0x9942B558, 0x3C032726, 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67,
+ 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, 0xD915C5D1, 0x7C5457AF,
+ 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8,
+ 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA,
+ 0x40577089, 0xE516E2F7, 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828,
+ 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, 0xC76580D9, 0x622412A7,
+ 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0,
+ 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878,
+ 0x2E85480B, 0x8BC4DA75, 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20,
+ 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, 0x8F96C396, 0x2AD751E8,
+ 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,
+ 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9,
+ 0xF7908DDA, 0x52D11FA4, 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B,
+ 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, 0x56830647, 0xF3C29439,
+ 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E,
+ 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6,
+ 0xBF63CE95, 0x1A225CEB, 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730,
+ 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, 0xB3764986, 0x1637DBF8,
+ 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF,
+ 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD,
+ 0x2A34FCDE, 0x8F756EA0, 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F,
+ 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, 0x6A638C57, 0xCF221E29,
+ 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,
+ 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6,
+ 0x83834485, 0x26C2D6FB, 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE,
+ 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, 0x2290CF18, 0x87D15D66,
+ 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71,
+ 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE,
+ 0x9DF3018D, 0x38B293F3, 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C,
+ 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, 0x3CE08A10, 0x99A1186E,
+ 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79,
+ 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1,
+ 0xD50042C2, 0x7041D0BC, 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD,
+ 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, 0x9557324B, 0x3016A035,
+ 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,
+ 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760,
+ 0x0C158713, 0xA954156D, 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2,
+ 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8,
+},
+{
+ 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B,
+ 0xC4451272, 0x1900B8CA, 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF,
+ 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, 0xE964B13D, 0x34211B85,
+ 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,
+ 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990,
+ 0xDB65C0A9, 0x06206A11, 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2,
+ 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, 0x2161776D, 0xFC24DDD5,
+ 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7,
+ 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD,
+ 0xFA04B7C4, 0x27411D7C, 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69,
+ 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, 0xABA65FE7, 0x76E3F55F,
+ 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D,
+ 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A,
+ 0x99A72E73, 0x44E284CB, 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3,
+ 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, 0xB4868D3C, 0x69C32784,
+ 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,
+ 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027,
+ 0xB8C6591E, 0x6583F3A6, 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3,
+ 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, 0x95E7FA51, 0x48A250E9,
+ 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B,
+ 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC,
+ 0xA7E68BC5, 0x7AA3217D, 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006,
+ 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, 0xA4E4AAD9, 0x79A10061,
+ 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213,
+ 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349,
+ 0x7F816A70, 0xA2C4C0C8, 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD,
+ 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, 0x8585DDB4, 0x58C0770C,
+ 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,
+ 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519,
+ 0xB784AC20, 0x6AC10698, 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0,
+ 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, 0x9AA50F6F, 0x47E0A5D7,
+ 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5,
+ 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93,
+ 0x3D4384AA, 0xE0062E12, 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07,
+ 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, 0x106227E5, 0xCD278D5D,
+ 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F,
+ 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48,
+ 0x22635671, 0xFF26FCC9, 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A,
+ 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, 0xD867E1B5, 0x05224B0D,
+ 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,
+ 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825,
+ 0x0302211C, 0xDE478BA4, 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1,
+ 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842,
+},
+{
+ 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C,
+ 0x906761E8, 0xA8760E44, 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65,
+ 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, 0x8F2261D3, 0xB7330E7F,
+ 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,
+ 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E,
+ 0xDA220BAA, 0xE2336406, 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3,
+ 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, 0xDECFBEC6, 0xE6DED16A,
+ 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082,
+ 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598,
+ 0x04EDB56C, 0x3CFCDAC0, 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1,
+ 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, 0x37516AAE, 0x0F400502,
+ 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA,
+ 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023,
+ 0x625100D7, 0x5A406F7B, 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89,
+ 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, 0x7D1400EC, 0x45056F40,
+ 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,
+ 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5,
+ 0xBC9EBE11, 0x848FD1BD, 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C,
+ 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, 0xA3DBBE2A, 0x9BCAD186,
+ 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E,
+ 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7,
+ 0xF6DBD453, 0xCECABBFF, 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8,
+ 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, 0xABC5DECD, 0x93D4B161,
+ 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089,
+ 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593,
+ 0x71E7D567, 0x49F6BACB, 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA,
+ 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, 0x750A600B, 0x4D1B0FA7,
+ 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,
+ 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86,
+ 0x200A0A72, 0x181B65DE, 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C,
+ 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, 0x3F4F0A49, 0x075E65E5,
+ 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D,
+ 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE,
+ 0xC994DE1A, 0xF185B1B6, 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497,
+ 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, 0xD6D1DE21, 0xEEC0B18D,
+ 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065,
+ 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC,
+ 0x83D1B458, 0xBBC0DBF4, 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51,
+ 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, 0x873C0134, 0xBF2D6E98,
+ 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,
+ 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A,
+ 0x5D1E0A9E, 0x650F6532, 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013,
+ 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3,
+},
+{
+ 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E,
+ 0x697997B4, 0x8649FCAD, 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5,
+ 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, 0xC00C303E, 0x2F3C5B27,
+ 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,
+ 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F,
+ 0xC973BF95, 0x2643D48C, 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57,
+ 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, 0xE5F20E92, 0x0AC2658B,
+ 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F,
+ 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD,
+ 0x2C81B107, 0xC3B1DA1E, 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576,
+ 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, 0x0E045BEB, 0xE13430F2,
+ 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746,
+ 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A,
+ 0x077BD440, 0xE84BBF59, 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F,
+ 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, 0xAE0E73CA, 0x413E18D3,
+ 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,
+ 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108,
+ 0xE289DAD2, 0x0DB9B1CB, 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3,
+ 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, 0x4BFC7D58, 0xA4CC1641,
+ 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5,
+ 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929,
+ 0x4283F2F3, 0xADB399EA, 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C,
+ 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, 0x7C0EAFC9, 0x933EC4D0,
+ 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364,
+ 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86,
+ 0xB57D105C, 0x5A4D7B45, 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D,
+ 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, 0x99FCA15B, 0x76CCCA42,
+ 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,
+ 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A,
+ 0x90832EF0, 0x7FB345E9, 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF,
+ 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, 0x39F6897A, 0xD6C6E263,
+ 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7,
+ 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053,
+ 0x7B757B89, 0x94451090, 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8,
+ 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, 0xD200DC03, 0x3D30B71A,
+ 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE,
+ 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872,
+ 0xDB7F53A8, 0x344F38B1, 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A,
+ 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, 0xF7FEE2AF, 0x18CE89B6,
+ 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,
+ 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0,
+ 0x3E8D5D3A, 0xD1BD3623, 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B,
+ 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C,
+},
+{
+ 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919,
+ 0x75E69C41, 0x1DE5B089, 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B,
+ 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, 0x9C5BFAA6, 0xF458D66E,
+ 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,
+ 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC,
+ 0xA7909BB4, 0xCF93B77C, 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5,
+ 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, 0x73767EEE, 0x1B755226,
+ 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67,
+ 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002,
+ 0xD4E6E55A, 0xBCE5C992, 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110,
+ 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, 0x7AB7077A, 0x12B42BB2,
+ 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3,
+ 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330,
+ 0x417C6668, 0x297F4AA0, 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884,
+ 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, 0xA8C1008F, 0xC0C22C47,
+ 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,
+ 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE,
+ 0x320A1886, 0x5A09344E, 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC,
+ 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, 0xDBB77E61, 0xB3B452A9,
+ 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8,
+ 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B,
+ 0xE07C1F73, 0x887F33BB, 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC,
+ 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, 0xBB43F3A7, 0xD340DF6F,
+ 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E,
+ 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B,
+ 0x1CD36813, 0x74D044DB, 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59,
+ 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, 0xC8358D49, 0xA036A181,
+ 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,
+ 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903,
+ 0xF3FEEC5B, 0x9BFDC093, 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7,
+ 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, 0x1A438ABC, 0x7240A674,
+ 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35,
+ 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097,
+ 0xFA3F95CF, 0x923CB907, 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185,
+ 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, 0x1382F328, 0x7B81DFE0,
+ 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1,
+ 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762,
+ 0x2849923A, 0x404ABEF2, 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B,
+ 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, 0xFCAF7760, 0x94AC5BA8,
+ 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,
+ 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C,
+ 0x5B3FECD4, 0x333CC01C, 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E,
+ 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F,
+},
+{
+ 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A,
+ 0xB3657823, 0xFA590504, 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3,
+ 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, 0x847609B4, 0xCD4A7493,
+ 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,
+ 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224,
+ 0x7528754D, 0x3C14086A, 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0,
+ 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, 0x4F3B6143, 0x06071C64,
+ 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447,
+ 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367,
+ 0x3A13140E, 0x732F6929, 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E,
+ 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, 0x1A00CB32, 0x533CB615,
+ 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36,
+ 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2,
+ 0xEB5EB7CB, 0xA262CAEC, 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF,
+ 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, 0xDC4DC65C, 0x9571BB7B,
+ 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,
+ 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1,
+ 0xA465D688, 0xED59ABAF, 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18,
+ 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, 0x9376A71F, 0xDA4ADA38,
+ 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B,
+ 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F,
+ 0x6228DBE6, 0x2B14A6C1, 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D,
+ 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, 0x763A92BE, 0x3F06EF99,
+ 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA,
+ 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A,
+ 0x0312E7F3, 0x4A2E9AD4, 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63,
+ 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, 0x3901F3FD, 0x703D8EDA,
+ 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,
+ 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D,
+ 0xC85F8F04, 0x8163F223, 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20,
+ 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, 0xFF4CFE93, 0xB67083B4,
+ 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97,
+ 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C,
+ 0x9D642575, 0xD4585852, 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5,
+ 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, 0xAA7754E2, 0xE34B29C5,
+ 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6,
+ 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72,
+ 0x5B29281B, 0x1215553C, 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6,
+ 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, 0x613A3C15, 0x28064132,
+ 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,
+ 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31,
+ 0x14124958, 0x5D2E347F, 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8,
+ 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5,
+} };
+
+#define DUMMY_CRC32_UPD(crc, n) \
+ (dummy_crc32c_tables[(n)][(crc) & 0xff] ^ \
+ dummy_crc32c_tables[(n) - 1][((crc) >> 8) & 0xff])
+
+static inline uint32_t dummy_crc32c_u32(uint32_t data, uint32_t init_val)
+{
+ uint32_t crc, term1, term2;
+
+ crc = init_val;
+ crc ^= data;
+
+ term1 = DUMMY_CRC32_UPD(crc, 3);
+ term2 = crc >> 16;
+ crc = term1 ^ DUMMY_CRC32_UPD(term2, 1);
+
+ return crc;
+}
+
+static inline uint32_t dummy_crc32c_u64(uint64_t data, uint32_t init_val)
+{
+ union {
+ uint64_t u64;
+ uint32_t u32[2];
+ } d;
+ d.u64 = data;
+
+ uint32_t crc, term1, term2;
+
+ crc = init_val;
+ crc ^= d.u32[0];
+
+ term1 = DUMMY_CRC32_UPD(crc, 7);
+ term2 = crc >> 16;
+ crc = term1 ^ DUMMY_CRC32_UPD(term2, 5);
+ term1 = DUMMY_CRC32_UPD(d.u32[1], 3);
+ term2 = d.u32[1] >> 16;
+ crc ^= term1 ^ DUMMY_CRC32_UPD(term2, 1);
+
+ return crc;
+}
+
+static inline uint32_t dummy_hash_crc32c(const void *data, uint32_t data_len,
+ uint32_t init_val)
+{
+ size_t i;
+ uint64_t temp = 0;
+ uintptr_t pd = (uintptr_t)data;
+
+ for (i = 0; i < data_len / 8; i++) {
+ init_val = dummy_crc32c_u64(*(const uint64_t *)pd, init_val);
+ pd += 8;
+ }
+
+ switch (7 - (data_len & 0x07)) {
+ case 0:
+ temp |= (uint64_t)*((const uint8_t *)pd + 6) << 48;
+ /* Fallthrough */
+ case 1:
+ temp |= (uint64_t)*((const uint8_t *)pd + 5) << 40;
+ /* Fallthrough */
+ case 2:
+ temp |= (uint64_t)*((const uint8_t *)pd + 4) << 32;
+ temp |= *(const uint32_t *)pd;
+ init_val = dummy_crc32c_u64(temp, init_val);
+ break;
+ case 3:
+ init_val = dummy_crc32c_u32(*(const uint32_t *)pd, init_val);
+ break;
+ case 4:
+ temp |= *((const uint8_t *)pd + 2) << 16;
+ /* Fallthrough */
+ case 5:
+ temp |= *((const uint8_t *)pd + 1) << 8;
+ /* Fallthrough */
+ case 6:
+ temp |= *(const uint8_t *)pd;
+ init_val = dummy_crc32c_u32(temp, init_val);
+ /* Fallthrough */
+ default:
+ break;
+ }
+
+ return init_val;
+}
diff --git a/test/performance/odp_atomic_perf.c b/test/performance/odp_atomic_perf.c
new file mode 100644
index 000000000..af0a37921
--- /dev/null
+++ b/test/performance/odp_atomic_perf.c
@@ -0,0 +1,1403 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_atomic_perf.c
+ *
+ * Performance test application for atomic operation APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/* Default number of test rounds */
+#define NUM_ROUNDS 100000u
+
+/* Initial value for atomic variables. Supports up to 2 billion
+ * rounds of 32-bit min and max tests. */
+#define INIT_VAL 0x80000000
+
+/* Max number of workers if num_cpu=0 */
+#define DEFAULT_MAX_WORKERS 10
+
+#define TEST_INFO(name, test, validate, op_type) \
+ {name, test, validate, op_type}
+
+/* Test function template */
+typedef void (*test_fn_t)(void *val, void *out, uint32_t num_round);
+/* Test result validation function template */
+typedef int (*validate_fn_t)(void *val, void *out, uint32_t num_round,
+ uint32_t num_worker, int private);
+
+typedef enum {
+ OP_32BIT,
+ OP_64BIT,
+ OP_128BIT
+} op_bit_t;
+
+/* Command line options */
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t num_round;
+ int private;
+
+} test_options_t;
+
+/* Cache aligned atomics for private mode operation */
+typedef struct ODP_ALIGNED_CACHE test_atomic_t {
+ union {
+ odp_atomic_u32_t u32;
+ odp_atomic_u64_t u64;
+ odp_atomic_u128_t u128;
+ };
+} test_atomic_t;
+
+typedef struct test_global_t test_global_t;
+
+/* Worker thread context */
+typedef struct test_thread_ctx_t {
+ test_global_t *global;
+ test_fn_t func;
+ uint64_t nsec;
+ uint32_t idx;
+ op_bit_t type;
+
+} test_thread_ctx_t;
+
+/* Global data */
+struct test_global_t {
+ test_options_t test_options;
+ odp_barrier_t barrier;
+ union {
+ odp_atomic_u32_t atomic_u32;
+ odp_atomic_u64_t atomic_u64;
+ odp_atomic_u128_t atomic_u128;
+ };
+ odp_cpumask_t cpumask;
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_thread_ctx_t thread_ctx[ODP_THREAD_COUNT_MAX];
+ test_atomic_t atomic_private[ODP_THREAD_COUNT_MAX];
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ odp_u128_t u128;
+ } output[ODP_THREAD_COUNT_MAX];
+};
+
+typedef struct {
+ const char *name;
+ test_fn_t test_fn;
+ validate_fn_t validate_fn;
+ op_bit_t type;
+} test_case_t;
+
+static test_global_t *test_global;
+
+static inline void test_atomic_load_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_load_u32(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_load_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_load_u64(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_load_u128(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t *result = out;
+ odp_u128_t ret;
+
+ ret.u64[0] = 0;
+ ret.u64[1] = 0;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ odp_u128_t cur_val = odp_atomic_load_u128(atomic_val);
+
+ ret.u64[0] += cur_val.u64[0];
+ ret.u64[1] += cur_val.u64[1];
+ }
+
+ *result = ret;
+}
+
+static inline int validate_atomic_init_val_u32(void *val, void *out, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED,
+ int private ODP_UNUSED)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+
+ return (odp_atomic_load_u32(atomic_val) != INIT_VAL) ||
+ (*result != (uint32_t)INIT_VAL * num_round);
+}
+
+static inline int validate_atomic_init_val_u64(void *val, void *out, uint32_t num_round,
+ uint32_t worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+
+ return (odp_atomic_load_u64(atomic_val) != INIT_VAL) ||
+ (*result != (uint64_t)INIT_VAL * num_round);
+}
+
+static inline int validate_atomic_init_val_u128(void *val, void *out, uint32_t num_round,
+ uint32_t worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ odp_u128_t atomic_val = odp_atomic_load_u128((odp_atomic_u128_t *)val);
+ odp_u128_t *result = out;
+
+ if (atomic_val.u64[0] != INIT_VAL || atomic_val.u64[1] != INIT_VAL)
+ return -1;
+
+ if (result->u64[0] != (uint64_t)INIT_VAL * num_round ||
+ result->u64[1] != (uint64_t)INIT_VAL * num_round)
+ return -1;
+
+ return 0;
+}
+
+static inline void test_atomic_store_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_store_u32(atomic_val, new_val++);
+}
+
+static inline void test_atomic_store_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_store_u64(atomic_val, new_val++);
+}
+
+static inline void test_atomic_store_u128(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t new_val;
+
+ new_val.u64[0] = INIT_VAL + 1;
+ new_val.u64[1] = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ odp_atomic_store_u128(atomic_val, new_val);
+ new_val.u64[0]++;
+ new_val.u64[1]++;
+ }
+}
+
+static inline int validate_atomic_num_round_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ return odp_atomic_load_u32(atomic_val) != ((uint32_t)INIT_VAL + num_round);
+}
+
+static inline int validate_atomic_num_round_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ return odp_atomic_load_u64(atomic_val) != ((uint64_t)INIT_VAL + num_round);
+}
+
+static inline int validate_atomic_num_round_u128(void *val, void *out ODP_UNUSED,
+ uint32_t num_round, uint32_t worker ODP_UNUSED,
+ int private ODP_UNUSED)
+{
+ odp_u128_t atomic_val = odp_atomic_load_u128((odp_atomic_u128_t *)val);
+
+ return (atomic_val.u64[0] != ((uint64_t)INIT_VAL + num_round) ||
+ atomic_val.u64[1] != ((uint64_t)INIT_VAL + num_round));
+}
+
+static inline void test_atomic_fetch_add_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_add_u32(atomic_val, 1);
+
+ *result = ret;
+}
+
+static inline void test_atomic_fetch_add_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_add_u64(atomic_val, 1);
+
+ *result = ret;
+}
+
+static inline int validate_atomic_add_round_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker, int private)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ if (private)
+ return odp_atomic_load_u32(atomic_val) != ((uint32_t)INIT_VAL + num_round);
+
+ return odp_atomic_load_u32(atomic_val) != (INIT_VAL + (num_worker * num_round));
+}
+
+static inline int validate_atomic_add_round_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker, int private)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ if (private)
+ return odp_atomic_load_u64(atomic_val) != ((uint64_t)INIT_VAL + num_round);
+
+ return odp_atomic_load_u64(atomic_val) != (INIT_VAL + ((uint64_t)num_worker * num_round));
+}
+
+static inline void test_atomic_add_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_add_u32(atomic_val, 1);
+}
+
+static inline void test_atomic_add_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_add_u64(atomic_val, 1);
+}
+
+static inline void test_atomic_fetch_sub_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_sub_u32(atomic_val, 1);
+
+ *result = ret;
+}
+
+static inline void test_atomic_fetch_sub_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_sub_u64(atomic_val, 1);
+
+ *result = ret;
+}
+
+static inline int validate_atomic_sub_round_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker, int private)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ if (private)
+ return odp_atomic_load_u32(atomic_val) != ((uint32_t)INIT_VAL - num_round);
+
+ return odp_atomic_load_u32(atomic_val) != ((uint32_t)INIT_VAL - (num_worker * num_round));
+}
+
+static inline int validate_atomic_sub_round_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker, int private)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ if (private)
+ return odp_atomic_load_u64(atomic_val) != ((uint64_t)INIT_VAL - num_round);
+
+ return odp_atomic_load_u64(atomic_val) != ((uint64_t)INIT_VAL -
+ ((uint64_t)num_worker * num_round));
+}
+
+static inline void test_atomic_sub_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_sub_u32(atomic_val, 1);
+}
+
+static inline void test_atomic_sub_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_sub_u64(atomic_val, 1);
+}
+
+static inline void test_atomic_fetch_inc_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_inc_u32(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_fetch_inc_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_inc_u64(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_inc_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_inc_u32(atomic_val);
+}
+
+static inline void test_atomic_inc_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_inc_u64(atomic_val);
+}
+
+static inline void test_atomic_fetch_dec_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_dec_u32(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_fetch_dec_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_fetch_dec_u64(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_dec_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_dec_u32(atomic_val);
+}
+
+static inline void test_atomic_dec_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_dec_u64(atomic_val);
+}
+
+static inline void test_atomic_max_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_max = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_max_u32(atomic_val, new_max++);
+}
+
+static inline void test_atomic_max_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_max = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_max_u64(atomic_val, new_max++);
+}
+
+static inline int validate_atomic_max_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ uint32_t result = odp_atomic_load_u32((odp_atomic_u32_t *)val);
+
+ return (result != ((uint32_t)INIT_VAL + num_round)) && (result != UINT32_MAX);
+}
+
+static inline int validate_atomic_max_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ uint64_t result = odp_atomic_load_u64((odp_atomic_u64_t *)val);
+
+ return (result != ((uint64_t)INIT_VAL + num_round)) && (result != UINT64_MAX);
+}
+
+static inline void test_atomic_min_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_min = INIT_VAL - 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_min_u32(atomic_val, new_min--);
+}
+
+static inline void test_atomic_min_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_min = INIT_VAL - 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_min_u64(atomic_val, new_min--);
+}
+
+static inline int validate_atomic_min_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ uint32_t result = odp_atomic_load_u32((odp_atomic_u32_t *)val);
+
+ return result != ((uint32_t)INIT_VAL - num_round) && result != 0;
+}
+
+static inline int validate_atomic_min_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private ODP_UNUSED)
+{
+ uint64_t result = odp_atomic_load_u64((odp_atomic_u64_t *)val);
+
+ return result != ((uint64_t)INIT_VAL - num_round) && result != 0;
+}
+
+static inline void test_atomic_cas_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+ uint32_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_u32(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+ uint64_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_u64(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_u128(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t new_val;
+ odp_u128_t old_val;
+
+ new_val.u64[0] = INIT_VAL + 1;
+ new_val.u64[1] = INIT_VAL + 1;
+ old_val.u64[0] = INIT_VAL;
+ old_val.u64[1] = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_u128(atomic_val, &old_val, new_val)) {
+ old_val = new_val;
+ new_val.u64[0]++;
+ new_val.u64[1]++;
+ }
+ }
+}
+
+static inline int validate_atomic_cas_u32(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private)
+{
+ uint32_t result = odp_atomic_load_u32((odp_atomic_u32_t *)val);
+
+ if (private)
+ return result != ((uint32_t)INIT_VAL + num_round);
+
+ return result > ((uint32_t)INIT_VAL + num_round);
+}
+
+static inline int validate_atomic_cas_u64(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private)
+{
+ uint64_t result = odp_atomic_load_u64((odp_atomic_u64_t *)val);
+
+ if (private)
+ return result != ((uint64_t)INIT_VAL + num_round);
+
+ return result > ((uint64_t)INIT_VAL + num_round);
+}
+
+static inline int validate_atomic_cas_u128(void *val, void *out ODP_UNUSED, uint32_t num_round,
+ uint32_t num_worker ODP_UNUSED, int private)
+{
+ odp_u128_t result = odp_atomic_load_u128((odp_atomic_u128_t *)val);
+
+ if (private)
+ return (result.u64[0] != ((uint64_t)INIT_VAL + num_round) ||
+ result.u64[1] != ((uint64_t)INIT_VAL + num_round));
+
+ return (result.u64[0] > ((uint64_t)INIT_VAL + num_round) ||
+ result.u64[1] > ((uint64_t)INIT_VAL + num_round));
+}
+
+static inline void test_atomic_xchg_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_xchg_u32(atomic_val, new_val++);
+
+ *result = ret;
+}
+
+static inline void test_atomic_xchg_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_xchg_u64(atomic_val, new_val++);
+
+ *result = ret;
+}
+
+static inline void test_atomic_load_acq_u32(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t *result = out;
+ uint32_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_load_acq_u32(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_load_acq_u64(void *val, void *out, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t *result = out;
+ uint64_t ret = 0;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ ret += odp_atomic_load_acq_u64(atomic_val);
+
+ *result = ret;
+}
+
+static inline void test_atomic_store_rel_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_store_rel_u32(atomic_val, new_val++);
+}
+
+static inline void test_atomic_store_rel_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_store_rel_u64(atomic_val, new_val++);
+}
+
+static inline void test_atomic_add_rel_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_add_rel_u32(atomic_val, 1);
+}
+
+static inline void test_atomic_add_rel_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_add_rel_u64(atomic_val, 1);
+}
+
+static inline void test_atomic_sub_rel_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_sub_rel_u32(atomic_val, 1);
+}
+
+static inline void test_atomic_sub_rel_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+
+ for (uint32_t i = 0; i < num_round; i++)
+ odp_atomic_sub_rel_u64(atomic_val, 1);
+}
+
+static inline void test_atomic_cas_acq_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+ uint32_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_u32(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_acq_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+ uint64_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_u64(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_acq_u128(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t new_val;
+ odp_u128_t old_val;
+
+ new_val.u64[0] = INIT_VAL + 1;
+ new_val.u64[1] = INIT_VAL + 1;
+ old_val.u64[0] = INIT_VAL;
+ old_val.u64[1] = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_u128(atomic_val, &old_val, new_val)) {
+ old_val = new_val;
+ new_val.u64[0]++;
+ new_val.u64[1]++;
+ }
+ }
+}
+
+static inline void test_atomic_cas_rel_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+ uint32_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_rel_u32(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_rel_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+ uint64_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_rel_u64(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_rel_u128(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t new_val;
+ odp_u128_t old_val;
+
+ new_val.u64[0] = INIT_VAL + 1;
+ new_val.u64[1] = INIT_VAL + 1;
+ old_val.u64[0] = INIT_VAL;
+ old_val.u64[1] = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_rel_u128(atomic_val, &old_val, new_val)) {
+ old_val = new_val;
+ new_val.u64[0]++;
+ new_val.u64[1]++;
+ }
+ }
+}
+
+static inline void test_atomic_cas_acq_rel_u32(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u32_t *atomic_val = val;
+ uint32_t new_val = INIT_VAL + 1;
+ uint32_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_rel_u32(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_acq_rel_u64(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u64_t *atomic_val = val;
+ uint64_t new_val = INIT_VAL + 1;
+ uint64_t old_val = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_rel_u64(atomic_val, &old_val, new_val))
+ old_val = new_val++;
+ }
+}
+
+static inline void test_atomic_cas_acq_rel_u128(void *val, void *out ODP_UNUSED, uint32_t num_round)
+{
+ odp_atomic_u128_t *atomic_val = val;
+ odp_u128_t new_val;
+ odp_u128_t old_val;
+
+ new_val.u64[0] = INIT_VAL + 1;
+ new_val.u64[1] = INIT_VAL + 1;
+ old_val.u64[0] = INIT_VAL;
+ old_val.u64[1] = INIT_VAL;
+
+ for (uint32_t i = 0; i < num_round; i++) {
+ if (odp_atomic_cas_acq_rel_u128(atomic_val, &old_val, new_val)) {
+ old_val = new_val;
+ new_val.u64[0]++;
+ new_val.u64[1]++;
+ }
+ }
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Atomic operations performance test\n"
+ "\n"
+ "Usage: odp_atomic_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs (or max %d) (default)\n"
+ " -r, --num_round Number of rounds (default %u)\n"
+ " -p, --private 0: The same atomic variable is shared between threads (default)\n"
+ " 1: Atomic variables are private to each thread\n"
+ " -h, --help This help\n"
+ "\n", DEFAULT_MAX_WORKERS, NUM_ROUNDS);
+}
+
+static void print_info(test_options_t *test_options)
+{
+ odp_atomic_op_t atomic_ops;
+
+ printf("\nAtomic operations performance test configuration:\n");
+ printf(" num cpu %u\n", test_options->num_cpu);
+ printf(" num rounds %u\n", test_options->num_round);
+ printf(" private %i\n", test_options->private);
+ printf("\n");
+
+ atomic_ops.all_bits = 0;
+ odp_atomic_lock_free_u64(&atomic_ops);
+
+ printf("\nAtomic operations lock-free:\n");
+ printf(" odp_atomic_load_u64: %" PRIu32 "\n", atomic_ops.op.load);
+ printf(" odp_atomic_store_u64: %" PRIu32 "\n", atomic_ops.op.store);
+ printf(" odp_atomic_fetch_add_u64: %" PRIu32 "\n", atomic_ops.op.fetch_add);
+ printf(" odp_atomic_add_u64: %" PRIu32 "\n", atomic_ops.op.add);
+ printf(" odp_atomic_fetch_sub_u64: %" PRIu32 "\n", atomic_ops.op.fetch_sub);
+ printf(" odp_atomic_sub_u64: %" PRIu32 "\n", atomic_ops.op.sub);
+ printf(" odp_atomic_fetch_inc_u64: %" PRIu32 "\n", atomic_ops.op.fetch_inc);
+ printf(" odp_atomic_inc_u64: %" PRIu32 "\n", atomic_ops.op.inc);
+ printf(" odp_atomic_fetch_dec_u64: %" PRIu32 "\n", atomic_ops.op.fetch_dec);
+ printf(" odp_atomic_dec_u64: %" PRIu32 "\n", atomic_ops.op.dec);
+ printf(" odp_atomic_min_u64: %" PRIu32 "\n", atomic_ops.op.min);
+ printf(" odp_atomic_max_u64: %" PRIu32 "\n", atomic_ops.op.max);
+ printf(" odp_atomic_cas_u64: %" PRIu32 "\n", atomic_ops.op.cas);
+ printf(" odp_atomic_xchg_u64: %" PRIu32 "\n", atomic_ops.op.xchg);
+
+ atomic_ops.all_bits = 0;
+ odp_atomic_lock_free_u128(&atomic_ops);
+
+ printf(" odp_atomic_load_u128: %" PRIu32 "\n", atomic_ops.op.load);
+ printf(" odp_atomic_store_u128: %" PRIu32 "\n", atomic_ops.op.store);
+ printf(" odp_atomic_cas_u128: %" PRIu32 "\n", atomic_ops.op.cas);
+
+ printf("\n\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_round", required_argument, NULL, 'r'},
+ {"private", required_argument, NULL, 'p'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:r:p:h";
+
+ memset(test_options, 0, sizeof(test_options_t));
+ test_options->num_cpu = 0;
+ test_options->num_round = NUM_ROUNDS;
+ test_options->private = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atol(optarg);
+ break;
+ case 'p':
+ test_options->private = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_round < 1) {
+ ODPH_ERR("Invalid number of test rounds: %" PRIu32 "\n", test_options->num_round);
+ return -1;
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret, max_num;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ max_num = num_cpu;
+ if (num_cpu == 0) {
+ max_num = ODP_THREAD_COUNT_MAX - 1;
+ if (max_num > DEFAULT_MAX_WORKERS)
+ max_num = DEFAULT_MAX_WORKERS;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, max_num);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Too many workers. Max supported %i.\n", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ if (ret > max_num) {
+ ODPH_ERR("Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
+ return -1;
+ }
+
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ return 0;
+}
+
+static int init_test(test_global_t *global, const char *name, op_bit_t type)
+{
+ odp_u128_t init_val;
+
+ init_val.u64[0] = INIT_VAL;
+ init_val.u64[1] = INIT_VAL;
+
+ printf("TEST: %s\n", name);
+
+ if (type == OP_32BIT)
+ odp_atomic_init_u32(&global->atomic_u32, INIT_VAL);
+ else if (type == OP_64BIT)
+ odp_atomic_init_u64(&global->atomic_u64, INIT_VAL);
+ else if (type == OP_128BIT)
+ odp_atomic_init_u128(&global->atomic_u128, init_val);
+ else
+ return -1;
+
+ for (int i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (type == OP_32BIT) {
+ global->output[i].u32 = 0;
+ odp_atomic_init_u32(&global->atomic_private[i].u32, INIT_VAL);
+ } else if (type == OP_64BIT) {
+ global->output[i].u64 = 0;
+ odp_atomic_init_u64(&global->atomic_private[i].u64, INIT_VAL);
+ } else {
+ global->output[i].u128.u64[0] = 0;
+ global->output[i].u128.u64[1] = 0;
+ odp_atomic_init_u128(&global->atomic_private[i].u128, init_val);
+ }
+ }
+ return 0;
+}
+
+static int run_test(void *arg)
+{
+ uint64_t nsec;
+ odp_time_t t1, t2;
+ test_thread_ctx_t *thread_ctx = arg;
+ test_global_t *global = thread_ctx->global;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ uint32_t idx = thread_ctx->idx;
+ test_fn_t test_func = thread_ctx->func;
+ op_bit_t type = thread_ctx->type;
+ void *val;
+ void *out;
+ uint32_t out_u32 = 0;
+ uint64_t out_u64 = 0;
+ odp_u128_t out_u128;
+
+ out_u128.u64[0] = 0;
+ out_u128.u64[1] = 0;
+
+ if (type == OP_32BIT) {
+ val = &global->atomic_u32;
+ out = &out_u32;
+ } else if (type == OP_64BIT) {
+ val = &global->atomic_u64;
+ out = &out_u64;
+ } else {
+ val = &global->atomic_u128;
+ out = &out_u128;
+ }
+
+ if (global->test_options.private) {
+ if (type == OP_32BIT)
+ val = &global->atomic_private[idx].u32;
+ else if (type == OP_64BIT)
+ val = &global->atomic_private[idx].u64;
+ else
+ val = &global->atomic_private[idx].u128;
+ }
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+
+ test_func(val, out, num_round);
+
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats */
+ thread_ctx->nsec = nsec;
+ if (type == OP_32BIT)
+ global->output[idx].u32 = out_u32;
+ else if (type == OP_64BIT)
+ global->output[idx].u64 = out_u64;
+ else
+ global->output[idx].u128 = out_u128;
+
+ return 0;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance,
+ test_fn_t func, op_bit_t type)
+{
+ odph_thread_common_param_t param;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ odph_thread_common_param_init(&param);
+ param.instance = instance;
+ param.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
+
+ thread_ctx->global = global;
+ thread_ctx->idx = i;
+ thread_ctx->func = func;
+ thread_ctx->type = type;
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ thr_param[i].start = run_test;
+ thr_param[i].arg = thread_ctx;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &param, thr_param, num_cpu);
+ if (ret != num_cpu) {
+ ODPH_ERR("Failed to create all threads %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int validate_results(test_global_t *global, validate_fn_t validate, op_bit_t type)
+{
+ int i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ int num_cpu = test_options->num_cpu;
+ int private = global->test_options.private;
+ void *val;
+ void *out;
+
+ for (i = 0; i < num_cpu; i++) {
+ if (type == OP_32BIT) {
+ out = &global->output[i].u32;
+ val = &global->atomic_u32;
+ if (private)
+ val = &global->atomic_private[i].u32;
+ } else if (type == OP_64BIT) {
+ out = &global->output[i].u64;
+ val = &global->atomic_u64;
+ if (private)
+ val = &global->atomic_private[i].u64;
+ } else {
+ out = &global->output[i].u128;
+ val = &global->atomic_u128;
+ if (private)
+ val = &global->atomic_private[i].u128;
+ }
+
+ if (validate(val, out, num_round, num_cpu, private))
+ return -1;
+ }
+ return 0;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double nsec_ave;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ uint32_t num_round = test_options->num_round;
+ uint64_t nsec_sum = 0;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ nsec_sum += global->thread_ctx[i].nsec;
+
+ if (nsec_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ nsec_ave = nsec_sum / num_cpu;
+ num = 0;
+
+ printf("---------------------------------------------\n");
+ printf("Per thread results (Millions of ops per sec):\n");
+ printf("---------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->thread_ctx[i].nsec) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%8.2f ", num_round / (global->thread_ctx[i].nsec / 1000.0));
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("Average results over %i threads:\n", num_cpu);
+ printf("---------------------------------------\n");
+ printf(" duration: %8.2f sec\n", nsec_ave / ODP_TIME_SEC_IN_NS);
+ printf(" operations per cpu: %8.2fM ops/sec\n", num_round / (nsec_ave / 1000.0));
+ printf(" total operations: %8.2fM ops/sec\n",
+ (num_cpu * num_round) / (nsec_ave / 1000.0));
+ printf("\n\n");
+}
+
+/**
+ * Test functions
+ */
+static test_case_t test_suite[] = {
+ TEST_INFO("odp_atomic_load_u32", test_atomic_load_u32,
+ validate_atomic_init_val_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_store_u32", test_atomic_store_u32,
+ validate_atomic_num_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_fetch_add_u32", test_atomic_fetch_add_u32,
+ validate_atomic_add_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_add_u32", test_atomic_add_u32,
+ validate_atomic_add_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_fetch_sub_u32", test_atomic_fetch_sub_u32,
+ validate_atomic_sub_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_sub_u32", test_atomic_sub_u32,
+ validate_atomic_sub_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_fetch_inc_u32", test_atomic_fetch_inc_u32,
+ validate_atomic_add_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_inc_u32", test_atomic_inc_u32,
+ validate_atomic_add_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_fetch_dec_u32", test_atomic_fetch_dec_u32,
+ validate_atomic_sub_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_dec_u32", test_atomic_dec_u32,
+ validate_atomic_sub_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_max_u32", test_atomic_max_u32,
+ validate_atomic_max_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_min_u32", test_atomic_min_u32,
+ validate_atomic_min_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_cas_u32", test_atomic_cas_u32,
+ validate_atomic_cas_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_xchg_u32", test_atomic_xchg_u32,
+ validate_atomic_num_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_load_acq_u32", test_atomic_load_acq_u32,
+ validate_atomic_init_val_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_store_rel_u32", test_atomic_store_rel_u32,
+ validate_atomic_num_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_add_rel_u32", test_atomic_add_rel_u32,
+ validate_atomic_add_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_sub_rel_u32", test_atomic_sub_rel_u32,
+ validate_atomic_sub_round_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_cas_acq_u32", test_atomic_cas_acq_u32,
+ validate_atomic_cas_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_cas_rel_u32", test_atomic_cas_rel_u32,
+ validate_atomic_cas_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_cas_acq_rel_u32", test_atomic_cas_acq_rel_u32,
+ validate_atomic_cas_u32, OP_32BIT),
+ TEST_INFO("odp_atomic_load_u64", test_atomic_load_u64,
+ validate_atomic_init_val_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_store_u64", test_atomic_store_u64,
+ validate_atomic_num_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_fetch_add_u64", test_atomic_fetch_add_u64,
+ validate_atomic_add_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_add_u64", test_atomic_add_u64,
+ validate_atomic_add_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_fetch_sub_u64", test_atomic_fetch_sub_u64,
+ validate_atomic_sub_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_sub_u64", test_atomic_sub_u64,
+ validate_atomic_sub_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_fetch_inc_u64", test_atomic_fetch_inc_u64,
+ validate_atomic_add_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_inc_u64", test_atomic_inc_u64,
+ validate_atomic_add_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_fetch_dec_u64", test_atomic_fetch_dec_u64,
+ validate_atomic_sub_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_dec_u64", test_atomic_dec_u64,
+ validate_atomic_sub_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_max_u64", test_atomic_max_u64,
+ validate_atomic_max_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_min_u64", test_atomic_min_u64,
+ validate_atomic_min_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_cas_u64", test_atomic_cas_u64,
+ validate_atomic_cas_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_xchg_u64", test_atomic_xchg_u64,
+ validate_atomic_num_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_load_acq_u64", test_atomic_load_acq_u64,
+ validate_atomic_init_val_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_store_rel_u64", test_atomic_store_rel_u64,
+ validate_atomic_num_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_add_rel_u64", test_atomic_add_rel_u64,
+ validate_atomic_add_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_sub_rel_u64", test_atomic_sub_rel_u64,
+ validate_atomic_sub_round_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_cas_acq_u64", test_atomic_cas_acq_u64,
+ validate_atomic_cas_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_cas_rel_u64", test_atomic_cas_rel_u64,
+ validate_atomic_cas_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_cas_acq_rel_u64", test_atomic_cas_acq_rel_u64,
+ validate_atomic_cas_u64, OP_64BIT),
+ TEST_INFO("odp_atomic_load_u128", test_atomic_load_u128,
+ validate_atomic_init_val_u128, OP_128BIT),
+ TEST_INFO("odp_atomic_store_u128", test_atomic_store_u128,
+ validate_atomic_num_round_u128, OP_128BIT),
+ TEST_INFO("odp_atomic_cas_u128", test_atomic_cas_u128,
+ validate_atomic_cas_u128, OP_128BIT),
+ TEST_INFO("odp_atomic_cas_acq_u128", test_atomic_cas_acq_u128,
+ validate_atomic_cas_u128, OP_128BIT),
+ TEST_INFO("odp_atomic_cas_rel_u128", test_atomic_cas_rel_u128,
+ validate_atomic_cas_u128, OP_128BIT),
+ TEST_INFO("odp_atomic_cas_acq_rel_u128", test_atomic_cas_acq_rel_u128,
+ validate_atomic_cas_u128, OP_128BIT),
+};
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_options_t test_options;
+ int num_tests, i;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (parse_options(argc, argv, &test_options))
+ exit(EXIT_FAILURE);
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.stash = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for global data from shared mem */
+ shm = odp_shm_reserve("test_global", sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ test_global = odp_shm_addr(shm);
+ if (test_global == NULL) {
+ ODPH_ERR("Shared memory alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(test_global, 0, sizeof(test_global_t));
+ test_global->test_options = test_options;
+
+ odp_sys_info_print();
+
+ if (set_num_cpu(test_global))
+ exit(EXIT_FAILURE);
+
+ print_info(&test_global->test_options);
+
+ /* Loop all test cases */
+ num_tests = ODPH_ARRAY_SIZE(test_suite);
+
+ for (i = 0; i < num_tests; i++) {
+ /* Initialize test variables */
+ if (init_test(test_global, test_suite[i].name, test_suite[i].type)) {
+ ODPH_ERR("Failed to initialize atomics.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Start workers */
+ if (start_workers(test_global, instance, test_suite[i].test_fn, test_suite[i].type))
+ exit(EXIT_FAILURE);
+
+ /* Wait workers to exit */
+ odph_thread_join(test_global->thread_tbl, test_global->test_options.num_cpu);
+
+ print_stat(test_global);
+
+ /* Validate test results */
+ if (validate_results(test_global, test_suite[i].validate_fn, test_suite[i].type)) {
+ ODPH_ERR("Test %s result validation failed.\n", test_suite[i].name);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shm free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_bench_buffer.c b/test/performance/odp_bench_buffer.c
new file mode 100644
index 000000000..838617f78
--- /dev/null
+++ b/test/performance/odp_bench_buffer.c
@@ -0,0 +1,894 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @example odp_bench_buffer.c
+ *
+ * Microbenchmark application for buffer API functions
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/** Default buffer size */
+#define TEST_BUF_SIZE 1024
+
+/** Default pool user area size in bytes */
+#define TEST_UAREA_SIZE 8
+
+/** Number of API function calls per test case */
+#define TEST_REPEAT_COUNT 1000
+
+/** Default number of rounds per test case */
+#define TEST_ROUNDS 100u
+
+/** Maximum burst size for *_multi operations */
+#define TEST_MAX_BURST 64
+
+/** Default burst size for *_multi operations */
+#define TEST_DEF_BURST 8
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+#define BENCH_INFO(run_fn, init_fn, term_fn, alt_name) \
+ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .desc = alt_name}
+
+#define BENCH_INFO_COND(run_fn, init_fn, term_fn, alt_name, cond_fn) \
+ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .desc = alt_name, \
+ .cond = cond_fn}
+
+/**
+ * Parsed command line arguments
+ */
+typedef struct {
+ int bench_idx; /** Benchmark index to run indefinitely */
+ int burst_size; /** Burst size for *_multi operations */
+ int cache_size; /** Pool cache size */
+ int time; /** Measure time vs. CPU cycles */
+ uint32_t rounds; /** Rounds per test case */
+} appl_args_t;
+
+/**
+ * Grouping of all global data
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Common benchmark suite data */
+ bench_suite_t suite;
+ /** Buffer pool */
+ odp_pool_t pool;
+ /** Buffer size */
+ uint32_t buf_size;
+ /** Buffer user area size */
+ uint32_t uarea_size;
+ /** Max flow id */
+ uint32_t max_flow_id;
+ /** Array for storing test buffers */
+ odp_buffer_t buf_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** Array for storing test event */
+ odp_event_t event_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** Array for storing test pointers */
+ void *ptr_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test pool handles */
+ odp_pool_t pool_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test event types */
+ odp_event_type_t event_type_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** Array for storing test event subtypes */
+ odp_event_subtype_t event_subtype_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** CPU mask as string */
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
+} args_t;
+
+/** Global pointer to args */
+static args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1);
+}
+
+static void allocate_test_buffers(odp_buffer_t buf[], int num)
+{
+ int num_buf = 0;
+
+ while (num_buf < num) {
+ int ret;
+
+ ret = odp_buffer_alloc_multi(gbl_args->pool, &buf[num_buf], num - num_buf);
+ if (ret < 0)
+ ODPH_ABORT("Allocating test buffers failed\n");
+
+ num_buf += ret;
+ }
+}
+
+static void create_buffers(void)
+{
+ allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT);
+}
+
+static void create_buffers_multi(void)
+{
+ allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+}
+
+static void create_events(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+
+ allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT);
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->event_tbl[i] = odp_buffer_to_event(buf_tbl[i]);
+}
+
+static void create_events_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+
+ allocate_test_buffers(gbl_args->buf_tbl,
+ TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+
+ for (int i = 0; i < TEST_REPEAT_COUNT * gbl_args->appl.burst_size; i++)
+ gbl_args->event_tbl[i] = odp_buffer_to_event(buf_tbl[i]);
+}
+
+static void free_buffers(void)
+{
+ odp_buffer_free_multi(gbl_args->buf_tbl, TEST_REPEAT_COUNT);
+}
+
+static void free_buffers_multi(void)
+{
+ odp_buffer_free_multi(gbl_args->buf_tbl, TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+}
+
+static int check_uarea(void)
+{
+ return !!gbl_args->uarea_size;
+}
+
+static int check_flow_aware(void)
+{
+ return !!gbl_args->max_flow_id;
+}
+
+static int buffer_from_event(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ buf_tbl[i] = odp_buffer_from_event(event_tbl[i]);
+
+ return i;
+}
+
+static int buffer_from_event_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_buffer_from_event_multi(&buf_tbl[i * burst_size],
+ &event_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int buffer_to_event(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ event_tbl[i] = odp_buffer_to_event(buf_tbl[i]);
+
+ return i;
+}
+
+static int buffer_to_event_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_buffer_to_event_multi(&buf_tbl[i * burst_size],
+ &event_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int buffer_addr(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ptr_tbl[i] = odp_buffer_addr(buf_tbl[i]);
+
+ return i;
+}
+
+static int buffer_size(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ uint32_t ret = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_buffer_size(buf_tbl[i]);
+
+ return ret;
+}
+
+static int buffer_user_area(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ptr_tbl[i] = odp_buffer_user_area(buf_tbl[i]);
+
+ return i;
+}
+
+static int buffer_pool(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_pool_t *pool_tbl = gbl_args->pool_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ pool_tbl[i] = odp_buffer_pool(buf_tbl[i]);
+
+ return i;
+}
+
+static int buffer_alloc(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_pool_t pool = gbl_args->pool;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ buf_tbl[i] = odp_buffer_alloc(pool);
+
+ return i;
+}
+
+static int buffer_alloc_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_pool_t pool = gbl_args->pool;
+ int burst_size = gbl_args->appl.burst_size;
+ int num = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ num += odp_buffer_alloc_multi(pool, &buf_tbl[num], burst_size);
+
+ return num;
+}
+
+static int buffer_free(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_buffer_free(buf_tbl[i]);
+
+ return i;
+}
+
+static int buffer_free_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_buffer_free_multi(&buf_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int buffer_alloc_free(void)
+{
+ odp_pool_t pool = gbl_args->pool;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+ return 0;
+
+ odp_buffer_free(buf);
+ }
+ return i;
+}
+
+static int buffer_alloc_free_multi(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ odp_pool_t pool = gbl_args->pool;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int num = odp_buffer_alloc_multi(pool, buf_tbl, burst_size);
+
+ if (odp_unlikely(num < 1))
+ return 0;
+
+ odp_buffer_free_multi(buf_tbl, num);
+ }
+ return i;
+}
+
+static int buffer_is_valid(void)
+{
+ odp_buffer_t *buf_tbl = gbl_args->buf_tbl;
+ uint32_t ret = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_buffer_is_valid(buf_tbl[i]);
+
+ return ret;
+}
+
+static int event_type(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ event_type_tbl[i] = odp_event_type(event_tbl[i]);
+
+ return i;
+}
+
+static int event_subtype(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_subtype_t *event_subtype_tbl = gbl_args->event_subtype_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ event_subtype_tbl[i] = odp_event_subtype(event_tbl[i]);
+
+ return i;
+}
+
+static int event_types(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl;
+ odp_event_subtype_t *event_subtype_tbl = gbl_args->event_subtype_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ event_type_tbl[i] = odp_event_types(event_tbl[i], &event_subtype_tbl[i]);
+
+ return i;
+}
+
+static int event_types_multi(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl;
+ odp_event_subtype_t *event_subtype_tbl = gbl_args->event_subtype_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_types_multi(&event_tbl[i * burst_size],
+ &event_type_tbl[i * burst_size],
+ &event_subtype_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int event_types_multi_no_sub(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_types_multi(&event_tbl[i * burst_size],
+ &event_type_tbl[i * burst_size], NULL, burst_size);
+
+ return i;
+}
+
+static int event_type_multi(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ uint32_t ret = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_event_type_multi(&event_tbl[i * burst_size], burst_size,
+ &event_type_tbl[i]);
+
+ return ret;
+}
+
+static int event_pool(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ odp_pool_t *pool_tbl = gbl_args->pool_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ pool_tbl[i] = odp_event_pool(event_tbl[i]);
+
+ return i;
+}
+
+static int event_user_area(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ptr_tbl[i] = odp_event_user_area(event_tbl[i]);
+
+ return i;
+}
+
+static int event_user_area_and_flag(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ int ret = 0;
+ int flag;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++) {
+ ptr_tbl[i] = odp_event_user_area_and_flag(event_tbl[i], &flag);
+ ret += flag;
+ }
+
+ return ret;
+}
+
+static int event_is_valid(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+
+ uint32_t ret = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_event_is_valid(event_tbl[i]);
+
+ return ret;
+}
+
+static int event_free(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_free(event_tbl[i]);
+
+ return i;
+}
+
+static int event_free_multi(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_free_multi(&event_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int event_free_sp(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int burst_size = gbl_args->appl.burst_size;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_free_sp(&event_tbl[i * burst_size], burst_size);
+
+ return i;
+}
+
+static int event_flow_id(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ uint32_t ret = 0;
+
+ for (int i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_event_flow_id(event_tbl[i]);
+
+ return !ret;
+}
+
+static int event_flow_id_set(void)
+{
+ odp_event_t *event_tbl = gbl_args->event_tbl;
+ int i = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_event_flow_id_set(event_tbl[i], 0);
+
+ return i;
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane Buffer/Event API microbenchmarks.\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -b, --burst <num> Test burst size.\n"
+ " -c, --cache_size <num> Pool cache size.\n"
+ " -i, --index <idx> Benchmark index to run indefinitely.\n"
+ " -r, --rounds <num> Run each test case 'num' times (default %u).\n"
+ " -t, --time <opt> Time measurement. 0: measure CPU cycles (default), 1: measure time\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname), TEST_ROUNDS);
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"burst", required_argument, NULL, 'b'},
+ {"cache_size", required_argument, NULL, 'c'},
+ {"index", required_argument, NULL, 'i'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"time", required_argument, NULL, 't'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "c:b:i:r:t:h";
+
+ appl_args->bench_idx = 0; /* Run all benchmarks */
+ appl_args->burst_size = TEST_DEF_BURST;
+ appl_args->cache_size = -1;
+ appl_args->rounds = TEST_ROUNDS;
+ appl_args->time = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cache_size = atoi(optarg);
+ break;
+ case 'b':
+ appl_args->burst_size = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'i':
+ appl_args->bench_idx = atoi(optarg);
+ break;
+ case 'r':
+ appl_args->rounds = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->burst_size < 1 ||
+ appl_args->burst_size > TEST_MAX_BURST) {
+ printf("Invalid burst size (max %d)\n", TEST_MAX_BURST);
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->rounds < 1) {
+ printf("Invalid number test rounds: %d\n", appl_args->rounds);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* Reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(void)
+{
+ odp_sys_info_print();
+
+ printf("\n"
+ "odp_bench_buffer options\n"
+ "------------------------\n");
+
+ printf("Burst size: %d\n", gbl_args->appl.burst_size);
+ printf("Buffer size: %d\n", gbl_args->buf_size);
+ printf("CPU mask: %s\n", gbl_args->cpumask_str);
+ if (gbl_args->appl.cache_size < 0)
+ printf("Pool cache size: default\n");
+ else
+ printf("Pool cache size: %d\n", gbl_args->appl.cache_size);
+ printf("Measurement unit: %s\n", gbl_args->appl.time ? "nsec" : "CPU cycles");
+ printf("Test rounds: %u\n", gbl_args->appl.rounds);
+ printf("\n");
+}
+
+/**
+ * Test functions
+ */
+bench_info_t test_suite[] = {
+ BENCH_INFO(buffer_from_event, create_events, free_buffers, NULL),
+ BENCH_INFO(buffer_from_event_multi, create_events_multi, free_buffers_multi, NULL),
+ BENCH_INFO(buffer_to_event, create_buffers, free_buffers, NULL),
+ BENCH_INFO(buffer_to_event_multi, create_buffers_multi, free_buffers_multi, NULL),
+ BENCH_INFO(buffer_addr, create_buffers, free_buffers, NULL),
+ BENCH_INFO(buffer_size, create_buffers, free_buffers, NULL),
+ BENCH_INFO_COND(buffer_user_area, create_buffers, free_buffers, NULL, check_uarea),
+ BENCH_INFO(buffer_pool, create_buffers, free_buffers, NULL),
+ BENCH_INFO(buffer_alloc, NULL, free_buffers, NULL),
+ BENCH_INFO(buffer_alloc_multi, NULL, free_buffers_multi, NULL),
+ BENCH_INFO(buffer_free, create_buffers, NULL, NULL),
+ BENCH_INFO(buffer_free_multi, create_buffers_multi, NULL, NULL),
+ BENCH_INFO(buffer_alloc_free, NULL, NULL, NULL),
+ BENCH_INFO(buffer_alloc_free_multi, NULL, NULL, NULL),
+ BENCH_INFO(buffer_is_valid, create_buffers, free_buffers, NULL),
+ BENCH_INFO(event_type, create_events, free_buffers, NULL),
+ BENCH_INFO(event_subtype, create_events, free_buffers, NULL),
+ BENCH_INFO(event_types, create_events, free_buffers, NULL),
+ BENCH_INFO(event_types_multi, create_events_multi, free_buffers_multi, NULL),
+ BENCH_INFO(event_types_multi_no_sub, create_events_multi, free_buffers_multi,
+ "event_types_multi (no sub)"),
+ BENCH_INFO(event_type_multi, create_events_multi, free_buffers_multi, NULL),
+ BENCH_INFO(event_pool, create_events, free_buffers, NULL),
+ BENCH_INFO_COND(event_user_area, create_events, free_buffers, NULL, check_uarea),
+ BENCH_INFO_COND(event_user_area_and_flag, create_events, free_buffers, NULL, check_uarea),
+ BENCH_INFO(event_is_valid, create_events, free_buffers, NULL),
+ BENCH_INFO(event_free, create_events, NULL, NULL),
+ BENCH_INFO(event_free_multi, create_events_multi, NULL, NULL),
+ BENCH_INFO(event_free_sp, create_events_multi, NULL, NULL),
+ BENCH_INFO_COND(event_flow_id, create_events, free_buffers, NULL, check_flow_aware),
+ BENCH_INFO_COND(event_flow_id_set, create_events, free_buffers, NULL, check_flow_aware),
+};
+
+/**
+ * ODP buffer microbenchmark application
+ */
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t worker_thread;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int cpu;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask, default_mask;
+ odp_schedule_capability_t sched_capa;
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ uint32_t buf_num;
+ uint8_t ret;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(gbl_args, 0, sizeof(args_t));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ bench_suite_init(&gbl_args->suite);
+ gbl_args->suite.bench = test_suite;
+ gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite);
+ gbl_args->suite.indef_idx = gbl_args->appl.bench_idx;
+ gbl_args->suite.rounds = gbl_args->appl.rounds;
+ gbl_args->suite.repeat_count = TEST_REPEAT_COUNT;
+ gbl_args->suite.measure_time = !!gbl_args->appl.time;
+
+ /* Get default worker cpumask */
+ if (odp_cpumask_default_worker(&default_mask, 1) != 1) {
+ ODPH_ERR("Error: unable to allocate worker thread\n");
+ exit(EXIT_FAILURE);
+ }
+ (void)odp_cpumask_to_str(&default_mask, gbl_args->cpumask_str,
+ sizeof(gbl_args->cpumask_str));
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("Error: schedule capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args->max_flow_id = 0;
+ if (sched_capa.max_flow_id) {
+ odp_schedule_config_t sched_config;
+
+ odp_schedule_config_init(&sched_config);
+ sched_config.max_flow_id = 1;
+
+ if (odp_schedule_config(&sched_config)) {
+ ODPH_ERR("Error: schedule config failed\n");
+ exit(EXIT_FAILURE);
+ }
+ gbl_args->max_flow_id = 1;
+ }
+
+ if (odp_pool_capability(&capa)) {
+ ODPH_ERR("Error: unable to query pool capability\n");
+ exit(EXIT_FAILURE);
+ }
+
+ buf_num = gbl_args->appl.burst_size * TEST_REPEAT_COUNT;
+
+ if (capa.buf.max_num && capa.buf.max_num < buf_num) {
+ ODPH_ERR("Error: pool size not supported (max %" PRIu32 ")\n", capa.buf.max_num);
+ exit(EXIT_FAILURE);
+ } else if (gbl_args->appl.cache_size > (int)capa.buf.max_cache_size) {
+ ODPH_ERR("Error: cache size not supported (max %" PRIu32 ")\n",
+ capa.buf.max_cache_size);
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args->buf_size = TEST_BUF_SIZE;
+ if (capa.buf.max_size && capa.buf.max_size < TEST_BUF_SIZE)
+ gbl_args->buf_size = capa.buf.max_size;
+
+ gbl_args->uarea_size = TEST_UAREA_SIZE < capa.buf.max_uarea_size ?
+ TEST_UAREA_SIZE : capa.buf.max_uarea_size;
+
+ print_info();
+
+ /* Create buffer pool */
+ odp_pool_param_init(&params);
+ params.buf.size = gbl_args->buf_size;
+ params.buf.num = buf_num;
+ params.buf.uarea_size = gbl_args->uarea_size;
+ if (gbl_args->appl.cache_size >= 0)
+ params.buf.cache_size = gbl_args->appl.cache_size;
+ params.type = ODP_POOL_BUFFER;
+
+ gbl_args->pool = odp_pool_create("microbench", &params);
+ if (gbl_args->pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: pool create failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pool_print(gbl_args->pool);
+
+ memset(&worker_thread, 0, sizeof(odph_thread_t));
+
+ signal(SIGINT, sig_handler);
+
+ /* Create worker thread */
+ cpu = odp_cpumask_first(&default_mask);
+
+ odp_cpumask_zero(&cpumask);
+ odp_cpumask_set(&cpumask, cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = bench_run;
+ thr_param.arg = &gbl_args->suite;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&worker_thread, &thr_common, &thr_param, 1);
+
+ odph_thread_join(&worker_thread, 1);
+
+ ret = gbl_args->suite.retval;
+
+ if (odp_pool_destroy(gbl_args->pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shm free\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_bench_misc.c b/test/performance/odp_bench_misc.c
new file mode 100644
index 000000000..a0e9476e6
--- /dev/null
+++ b/test/performance/odp_bench_misc.c
@@ -0,0 +1,1061 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @example odp_bench_misc.c
+ *
+ * Microbenchmark application for miscellaneous API functions
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for sigaction */
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Number of API function calls per test case */
+#define REPEAT_COUNT 1024
+
+/* Default number of rounds per test case */
+#define ROUNDS 1000u
+
+#define BENCH_INFO(run_fn, init_fn, max, alt_name) \
+ {.name = #run_fn, .run = run_fn, .init = init_fn, .max_rounds = max, .desc = alt_name}
+
+typedef struct {
+ /* Measure time vs CPU cycles */
+ int time;
+
+ /* Benchmark index to run indefinitely */
+ int bench_idx;
+
+ /* Rounds per test case */
+ uint32_t rounds;
+
+} appl_args_t;
+
+/* Global data */
+typedef struct {
+ appl_args_t appl;
+
+ /* Common benchmark suite data */
+ bench_suite_t suite;
+
+ /* Time stamp 1 */
+ odp_time_t t1[REPEAT_COUNT];
+ /* Time stamp 2 */
+ odp_time_t t2[REPEAT_COUNT];
+ /* Resulting time stamp */
+ odp_time_t t3[REPEAT_COUNT];
+
+ odp_time_t global_short[REPEAT_COUNT];
+ odp_time_t global_long[REPEAT_COUNT];
+
+ /* Integer input / output data */
+ uint64_t a1[REPEAT_COUNT];
+ uint64_t a2[REPEAT_COUNT];
+ uint32_t b1[REPEAT_COUNT];
+ uint32_t b2[REPEAT_COUNT];
+ uint16_t c1[REPEAT_COUNT];
+ uint16_t c2[REPEAT_COUNT];
+
+ /* CPU mask as string */
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
+
+} gbl_args_t;
+
+static gbl_args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1);
+}
+
+static int setup_sig_handler(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sig_handler;
+
+ /* No additional signals blocked. By default, the signal which triggered
+ * the handler is blocked. */
+ if (sigemptyset(&action.sa_mask))
+ return -1;
+
+ if (sigaction(SIGINT, &action, NULL))
+ return -1;
+
+ return 0;
+}
+
+static void init_time_global(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_global();
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t2[i] = odp_time_global();
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_global_ns();
+}
+
+static void init_time_local(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_local();
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t2[i] = odp_time_local();
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_local_ns();
+}
+
+static void init_cpu_cycles(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_cycles();
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a2[i] = odp_cpu_cycles();
+}
+
+static int time_local(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_local();
+
+ return i;
+}
+
+static int time_local_strict(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_local_strict();
+
+ return i;
+}
+
+static int time_local_ns(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_local_ns();
+
+ return i;
+}
+
+static int time_local_strict_ns(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_local_strict_ns();
+
+ return i;
+}
+
+static int time_global(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_global();
+
+ return i;
+}
+
+static int time_global_strict(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_global_strict();
+
+ return i;
+}
+
+static int time_global_ns(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_global_ns();
+
+ return i;
+}
+
+static int time_global_strict_ns(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_global_strict_ns();
+
+ return i;
+}
+
+static int time_diff(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ odp_time_t *t3 = gbl_args->t3;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t3[i] = odp_time_diff(t2[i], t1[i]);
+
+ return i;
+}
+
+static int time_diff_ns(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ uint64_t res = 0;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ res += odp_time_diff_ns(t2[i], t1[i]);
+
+ gbl_args->suite.dummy += res;
+
+ return i;
+}
+
+static int time_add_ns(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t3 = gbl_args->t3;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t3[i] = odp_time_add_ns(t1[i], a1[i]);
+
+ return i;
+}
+
+static int time_sum(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ odp_time_t *t3 = gbl_args->t3;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t3[i] = odp_time_sum(t1[i], t2[i]);
+
+ return i;
+}
+
+static int time_to_ns_short(void)
+{
+ int i;
+ odp_time_t *t = gbl_args->global_short;
+ uint64_t res = 0;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ res += odp_time_to_ns(t[i]);
+
+ gbl_args->suite.dummy += res;
+
+ return i;
+}
+
+static int time_to_ns_long(void)
+{
+ int i;
+ odp_time_t *t = gbl_args->global_long;
+ uint64_t res = 0;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ res += odp_time_to_ns(t[i]);
+
+ gbl_args->suite.dummy += res;
+
+ return i;
+}
+
+static int time_local_from_ns(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_local_from_ns(a1[i]);
+
+ return i;
+}
+
+static int time_global_from_ns(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ t1[i] = odp_time_global_from_ns(a1[i]);
+
+ return i;
+}
+
+static int time_cmp(void)
+{
+ int i;
+ odp_time_t *t1 = gbl_args->t1;
+ odp_time_t *t2 = gbl_args->t2;
+ int res = 0;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ res += odp_time_cmp(t1[i], t2[i]);
+
+ gbl_args->suite.dummy += res;
+
+ return i;
+}
+
+static int time_local_res(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_local_res();
+
+ return i;
+}
+
+static int time_global_res(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_time_global_res();
+
+ return i;
+}
+
+static int time_startup(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ odp_time_startup_t startup;
+
+ for (i = 0; i < REPEAT_COUNT; i++) {
+ odp_time_startup(&startup);
+ a1[i] = startup.global_ns;
+ }
+
+ return i;
+}
+
+static int cpu_id(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_id();
+
+ return i;
+}
+
+static int cpu_count(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_count();
+
+ return i;
+}
+
+static int cpu_hz(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_hz();
+
+ return i;
+}
+
+static int cpu_hz_id(void)
+{
+ int i;
+ const int id = odp_cpu_id();
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_hz_id(id);
+
+ return i;
+}
+
+static int cpu_hz_max(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_hz_max();
+
+ return i;
+}
+
+static int cpu_hz_max_id(void)
+{
+ int i;
+ const int id = odp_cpu_id();
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_hz_max_id(id);
+
+ return i;
+}
+
+static int cpu_cycles(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_cycles();
+
+ return i;
+}
+
+static int cpu_cycles_diff(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+ uint64_t res = 0;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ res += odp_cpu_cycles_diff(a2[i], a1[i]);
+
+ gbl_args->suite.dummy += res;
+
+ return i;
+}
+
+static int cpu_cycles_max(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_cycles_max();
+
+ return i;
+}
+
+static int cpu_cycles_resolution(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_cpu_cycles_resolution();
+
+ return i;
+}
+
+static int cpu_pause(void)
+{
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ odp_cpu_pause();
+
+ return i;
+}
+
+static int thread_id(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_thread_id();
+
+ return i;
+}
+
+static int thread_count(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_thread_count();
+
+ return i;
+}
+
+static int thread_count_max(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_thread_count_max();
+
+ return i;
+}
+
+static int thread_type(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = (int)odp_thread_type();
+
+ return i;
+}
+
+static int be_to_cpu_64(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a2[i] = odp_be_to_cpu_64(a1[i]);
+
+ return i;
+}
+
+static int be_to_cpu_32(void)
+{
+ int i;
+ uint32_t *b1 = gbl_args->b1;
+ uint32_t *b2 = gbl_args->b2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ b2[i] = odp_be_to_cpu_32(b1[i]);
+
+ return i;
+}
+
+static int be_to_cpu_16(void)
+{
+ int i;
+ uint16_t *c1 = gbl_args->c1;
+ uint16_t *c2 = gbl_args->c2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ c2[i] = odp_be_to_cpu_16(c1[i]);
+
+ return i;
+}
+
+static int cpu_to_be_64(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a2[i] = odp_cpu_to_be_64(a1[i]);
+
+ return i;
+}
+
+static int cpu_to_be_32(void)
+{
+ int i;
+ uint32_t *b1 = gbl_args->b1;
+ uint32_t *b2 = gbl_args->b2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ b2[i] = odp_cpu_to_be_32(b1[i]);
+
+ return i;
+}
+
+static int cpu_to_be_16(void)
+{
+ int i;
+ uint16_t *c1 = gbl_args->c1;
+ uint16_t *c2 = gbl_args->c2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ c2[i] = odp_cpu_to_be_16(c1[i]);
+
+ return i;
+}
+
+static int le_to_cpu_64(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a2[i] = odp_le_to_cpu_64(a1[i]);
+
+ return i;
+}
+
+static int le_to_cpu_32(void)
+{
+ int i;
+ uint32_t *b1 = gbl_args->b1;
+ uint32_t *b2 = gbl_args->b2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ b2[i] = odp_le_to_cpu_32(b1[i]);
+
+ return i;
+}
+
+static int le_to_cpu_16(void)
+{
+ int i;
+ uint16_t *c1 = gbl_args->c1;
+ uint16_t *c2 = gbl_args->c2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ c2[i] = odp_le_to_cpu_16(c1[i]);
+
+ return i;
+}
+
+static int cpu_to_le_64(void)
+{
+ int i;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t *a2 = gbl_args->a2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a2[i] = odp_cpu_to_le_64(a1[i]);
+
+ return i;
+}
+
+static int cpu_to_le_32(void)
+{
+ int i;
+ uint32_t *b1 = gbl_args->b1;
+ uint32_t *b2 = gbl_args->b2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ b2[i] = odp_cpu_to_le_32(b1[i]);
+
+ return i;
+}
+
+static int cpu_to_le_16(void)
+{
+ int i;
+ uint16_t *c1 = gbl_args->c1;
+ uint16_t *c2 = gbl_args->c2;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ c2[i] = odp_cpu_to_le_16(c1[i]);
+
+ return i;
+}
+
+static int mb_release(void)
+{
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ odp_mb_release();
+
+ return i;
+}
+
+static int mb_acquire(void)
+{
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ odp_mb_acquire();
+
+ return i;
+}
+
+static int mb_full(void)
+{
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ odp_mb_full();
+
+ return i;
+}
+
+static int prefetch(void)
+{
+ uint64_t *a1 = gbl_args->a1;
+ uint32_t index = 0;
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++) {
+ odp_prefetch(&a1[index]);
+
+ /* Prefetch every 64B */
+ index += 8;
+ if (odp_unlikely(index >= REPEAT_COUNT))
+ index = 0;
+ }
+
+ return i;
+}
+
+static int prefetch_store(void)
+{
+ uint64_t *a1 = gbl_args->a1;
+ uint32_t index = 0;
+ int i;
+
+ for (i = 0; i < REPEAT_COUNT; i++) {
+ odp_prefetch_store(&a1[index]);
+
+ /* Prefetch every 64B */
+ index += 8;
+ if (odp_unlikely(index >= REPEAT_COUNT))
+ index = 0;
+ }
+
+ return i;
+}
+
+bench_info_t test_suite[] = {
+ BENCH_INFO(time_local, NULL, 0, NULL),
+ BENCH_INFO(time_local_strict, NULL, 0, NULL),
+ BENCH_INFO(time_local_ns, NULL, 0, NULL),
+ BENCH_INFO(time_local_strict_ns, NULL, 0, NULL),
+ BENCH_INFO(time_global, NULL, 0, NULL),
+ BENCH_INFO(time_global_strict, NULL, 0, NULL),
+ BENCH_INFO(time_global_ns, NULL, 0, NULL),
+ BENCH_INFO(time_global_strict_ns, NULL, 0, NULL),
+ BENCH_INFO(time_diff, init_time_global, 0, "time_diff (global)"),
+ BENCH_INFO(time_diff, init_time_local, 0, "time_diff (local)"),
+ BENCH_INFO(time_diff_ns, init_time_global, 0, NULL),
+ BENCH_INFO(time_add_ns, init_time_global, 0, NULL),
+ BENCH_INFO(time_sum, init_time_global, 0, NULL),
+ BENCH_INFO(time_to_ns_short, NULL, 0, "time_to_ns (short)"),
+ BENCH_INFO(time_to_ns_long, NULL, 0, "time_to_ns (long)"),
+ BENCH_INFO(time_local_from_ns, init_time_global, 0, NULL),
+ BENCH_INFO(time_global_from_ns, init_time_global, 0, NULL),
+ BENCH_INFO(time_cmp, init_time_global, 0, NULL),
+ BENCH_INFO(time_local_res, NULL, 0, NULL),
+ BENCH_INFO(time_global_res, NULL, 0, NULL),
+ BENCH_INFO(time_startup, NULL, 0, NULL),
+ BENCH_INFO(cpu_id, NULL, 0, NULL),
+ BENCH_INFO(cpu_count, NULL, 0, NULL),
+ BENCH_INFO(cpu_hz, NULL, 1, NULL),
+ BENCH_INFO(cpu_hz_id, NULL, 1, NULL),
+ BENCH_INFO(cpu_hz_max, NULL, 0, NULL),
+ BENCH_INFO(cpu_hz_max_id, NULL, 0, NULL),
+ BENCH_INFO(cpu_cycles, NULL, 0, NULL),
+ BENCH_INFO(cpu_cycles_diff, init_cpu_cycles, 0, NULL),
+ BENCH_INFO(cpu_cycles_max, NULL, 0, NULL),
+ BENCH_INFO(cpu_cycles_resolution, NULL, 0, NULL),
+ BENCH_INFO(cpu_pause, NULL, 0, NULL),
+ BENCH_INFO(thread_id, NULL, 0, NULL),
+ BENCH_INFO(thread_count, NULL, 0, NULL),
+ BENCH_INFO(thread_count_max, NULL, 0, NULL),
+ BENCH_INFO(thread_type, NULL, 0, NULL),
+ BENCH_INFO(be_to_cpu_64, NULL, 0, NULL),
+ BENCH_INFO(be_to_cpu_32, NULL, 0, NULL),
+ BENCH_INFO(be_to_cpu_16, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_be_64, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_be_32, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_be_16, NULL, 0, NULL),
+ BENCH_INFO(le_to_cpu_64, NULL, 0, NULL),
+ BENCH_INFO(le_to_cpu_32, NULL, 0, NULL),
+ BENCH_INFO(le_to_cpu_16, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_le_64, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_le_32, NULL, 0, NULL),
+ BENCH_INFO(cpu_to_le_16, NULL, 0, NULL),
+ BENCH_INFO(mb_release, NULL, 0, NULL),
+ BENCH_INFO(mb_acquire, NULL, 0, NULL),
+ BENCH_INFO(mb_full, NULL, 0, NULL),
+ BENCH_INFO(prefetch, NULL, 0, NULL),
+ BENCH_INFO(prefetch_store, NULL, 0, NULL),
+};
+
+/* Print usage information */
+static void usage(void)
+{
+ printf("\n"
+ "ODP miscellaneous API micro benchmarks\n"
+ "\n"
+ "Options:\n"
+ " -t, --time <opt> Time measurement. 0: measure CPU cycles (default), 1: measure time\n"
+ " -i, --index <idx> Benchmark index to run indefinitely.\n"
+ " -r, --rounds <num> Run each test case 'num' times (default %u).\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", ROUNDS);
+}
+
+/* Parse command line arguments */
+static int parse_args(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ appl_args_t *appl_args = &gbl_args->appl;
+ static const struct option longopts[] = {
+ {"time", required_argument, NULL, 't'},
+ {"index", required_argument, NULL, 'i'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "t:i:r:h";
+
+ appl_args->time = 0; /* Measure CPU cycles */
+ appl_args->bench_idx = 0; /* Run all benchmarks */
+ appl_args->rounds = ROUNDS;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'i':
+ appl_args->bench_idx = atoi(optarg);
+ break;
+ case 'r':
+ appl_args->rounds = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ return 1;
+ default:
+ ODPH_ERR("Bad option. Use -h for help.\n");
+ return -1;
+ }
+ }
+
+ if (appl_args->rounds < 1) {
+ ODPH_ERR("Invalid test cycle repeat count: %u\n", appl_args->rounds);
+ return -1;
+ }
+
+ if (appl_args->bench_idx < 0 || appl_args->bench_idx > (int)ODPH_ARRAY_SIZE(test_suite)) {
+ ODPH_ERR("Bad bench index %i\n", appl_args->bench_idx);
+ return -1;
+ }
+
+ optind = 1; /* Reset 'extern optind' from the getopt lib */
+
+ return 0;
+}
+
+/* Print system and application info */
+static void print_info(void)
+{
+ odp_sys_info_print();
+
+ printf("\n"
+ "odp_bench_misc options\n"
+ "----------------------\n");
+
+ printf("CPU mask: %s\n", gbl_args->cpumask_str);
+ printf("Measurement unit: %s\n", gbl_args->appl.time ? "nsec" : "CPU cycles");
+ printf("Test rounds: %u\n", gbl_args->appl.rounds);
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t worker_thread;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int cpu, i;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask, default_mask;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ int ret = 0;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Global init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setup_sig_handler()) {
+ ODPH_ERR("Signal handler setup failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(gbl_args_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL) {
+ ODPH_ERR("Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(gbl_args, 0, sizeof(gbl_args_t));
+
+ for (i = 0; i < REPEAT_COUNT; i++) {
+ gbl_args->t1[i] = ODP_TIME_NULL;
+ gbl_args->t2[i] = ODP_TIME_NULL;
+ gbl_args->t3[i] = ODP_TIME_NULL;
+ gbl_args->global_short[i] = odp_time_global_from_ns(ODP_TIME_MSEC_IN_NS);
+ gbl_args->global_long[i] = odp_time_global_from_ns(10 * ODP_TIME_SEC_IN_NS);
+ gbl_args->a1[i] = i;
+ gbl_args->a2[i] = i;
+ gbl_args->b1[i] = i;
+ gbl_args->b2[i] = i;
+ gbl_args->c1[i] = i;
+ gbl_args->c2[i] = i;
+ }
+
+ /* Parse and store the application arguments */
+ ret = parse_args(argc, argv);
+ if (ret)
+ goto exit;
+
+ bench_suite_init(&gbl_args->suite);
+ gbl_args->suite.bench = test_suite;
+ gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite);
+ gbl_args->suite.measure_time = !!gbl_args->appl.time;
+ gbl_args->suite.indef_idx = gbl_args->appl.bench_idx;
+ gbl_args->suite.rounds = gbl_args->appl.rounds;
+ gbl_args->suite.repeat_count = REPEAT_COUNT;
+
+ /* Get default worker cpumask */
+ if (odp_cpumask_default_worker(&default_mask, 1) != 1) {
+ ODPH_ERR("Unable to allocate worker thread\n");
+ ret = -1;
+ goto exit;
+ }
+
+ (void)odp_cpumask_to_str(&default_mask, gbl_args->cpumask_str,
+ sizeof(gbl_args->cpumask_str));
+
+ print_info();
+
+ memset(&worker_thread, 0, sizeof(odph_thread_t));
+
+ /* Create worker thread */
+ cpu = odp_cpumask_first(&default_mask);
+
+ odp_cpumask_zero(&cpumask);
+ odp_cpumask_set(&cpumask, cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = bench_run;
+ thr_param.arg = &gbl_args->suite;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&worker_thread, &thr_common, &thr_param, 1);
+
+ odph_thread_join(&worker_thread, 1);
+
+ ret = gbl_args->suite.retval;
+
+exit:
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shared mem free failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/performance/odp_bench_packet.c b/test/performance/odp_bench_packet.c
new file mode 100644
index 000000000..a8494bd28
--- /dev/null
+++ b/test/performance/odp_bench_packet.c
@@ -0,0 +1,1777 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @example odp_bench_packet.c
+ *
+ * Microbenchmark application for packet API functions
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include <test_packet_ipv4.h>
+#include <test_packet_ipv6.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+/** Packet user area size in bytes */
+#define PKT_POOL_UAREA_SIZE 8
+
+/** Minimum test packet size */
+#define TEST_MIN_PKT_SIZE 64
+
+/** Maximum test packet size */
+#define TEST_MAX_PKT_SIZE 2048
+
+/** Number of API function calls per test case */
+#define TEST_REPEAT_COUNT 1000
+
+/** Number of rounds per test case */
+#define TEST_ROUNDS 2u
+
+/** Maximum burst size for *_multi operations */
+#define TEST_MAX_BURST 64
+
+/** Offset of the contiguous area */
+#define TEST_ALIGN_OFFSET 16
+
+/** Length of the contiguous area */
+#define TEST_ALIGN_LEN 32
+
+/** Minimum byte alignment of contiguous area */
+#define TEST_ALIGN 32
+
+/** Test packet offsets */
+#define TEST_L2_OFFSET 0
+#define TEST_L3_OFFSET (TEST_MIN_PKT_SIZE / 4)
+#define TEST_L4_OFFSET (TEST_MIN_PKT_SIZE / 2)
+
+/** Default burst size for *_multi operations */
+#define TEST_DEF_BURST 8
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+#define BENCH_INFO(run_fn, init_fn, term_fn, alt_name) \
+ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .desc = alt_name}
+
+ODP_STATIC_ASSERT((TEST_ALIGN_OFFSET + TEST_ALIGN_LEN) <= TEST_MIN_PKT_SIZE,
+ "Invalid_alignment");
+
+/** Test packet sizes */
+const uint32_t test_packet_len[] = {TEST_MIN_PKT_SIZE, 128, 256, 512,
+ 1024, 1518, TEST_MAX_PKT_SIZE};
+
+/**
+ * Parsed command line arguments
+ */
+typedef struct {
+ int bench_idx; /** Benchmark index to run indefinitely */
+ int burst_size; /** Burst size for *_multi operations */
+ int cache_size; /** Pool cache size */
+ int time; /** Measure time vs. CPU cycles */
+ uint32_t rounds; /** Rounds per test case */
+} appl_args_t;
+
+/**
+ * Grouping of all global data
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Common benchmark suite data */
+ bench_suite_t suite;
+ /** Packet pool */
+ odp_pool_t pool;
+ struct {
+ /** Test packet length */
+ uint32_t len;
+ /** Minimum test packet headroom */
+ uint32_t headroom;
+ /** Minimum test packet tailroom */
+ uint32_t tailroom;
+ /** Minimum test packet segment length */
+ uint32_t seg_len;
+ } pkt;
+ /** Array for storing test packets */
+ odp_packet_t pkt_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** Array for storing test packets */
+ odp_packet_t pkt2_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test event */
+ odp_event_t event_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST];
+ /** Array for storing test pointers */
+ void *ptr_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test segments */
+ odp_packet_seg_t seg_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test outputs */
+ uint32_t output_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test pool handles */
+ odp_pool_t pool_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test pktio handles */
+ odp_pktio_t pktio_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test timestamps */
+ odp_time_t ts_tbl[TEST_REPEAT_COUNT];
+ /** Array for storing test data */
+ uint8_t data_tbl[TEST_REPEAT_COUNT][TEST_MAX_PKT_SIZE];
+} args_t;
+
+/** Global pointer to args */
+static args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1);
+}
+
+/**
+ * Master function for running the microbenchmarks
+ */
+static int run_benchmarks(void *arg)
+{
+ int i;
+ args_t *args = arg;
+ bench_suite_t *suite = &args->suite;
+ int num_sizes = ODPH_ARRAY_SIZE(test_packet_len);
+ double results[num_sizes][suite->num_bench];
+
+ memset(results, 0, sizeof(results));
+
+ for (i = 0; i < num_sizes; i++) {
+ printf("Packet length: %6d bytes", test_packet_len[i]);
+
+ gbl_args->pkt.len = test_packet_len[i];
+
+ suite->result = results[i];
+
+ bench_run(suite);
+ }
+
+ printf("\n%-35s", "Benchmark / packet_size [B]");
+ for (i = 0; i < num_sizes; i++)
+ printf("%8.1d ", test_packet_len[i]);
+
+ printf("\n---------------------------------");
+ for (i = 0; i < num_sizes; i++)
+ printf("----------");
+
+ for (i = 0; i < suite->num_bench; i++) {
+ printf("\n[%02d] odp_%-26s", i + 1, suite->bench[i].desc != NULL ?
+ suite->bench[i].desc : suite->bench[i].name);
+
+ for (int j = 0; j < num_sizes; j++)
+ printf("%8.1f ", results[j][i]);
+ }
+ printf("\n\n");
+ return 0;
+}
+
+static void allocate_test_packets(uint32_t len, odp_packet_t pkt[], int num)
+{
+ int pkts = 0;
+
+ while (pkts < num) {
+ int ret;
+
+ ret = odp_packet_alloc_multi(gbl_args->pool, len, &pkt[pkts],
+ num - pkts);
+ if (ret < 0)
+ ODPH_ABORT("Allocating test packets failed\n");
+
+ pkts += ret;
+ }
+}
+
+static void alloc_packets_half(void)
+{
+ allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT);
+}
+
+static void alloc_packets_multi(void)
+{
+ allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+}
+
+static void alloc_concat_packets(void)
+{
+ allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT);
+ allocate_test_packets(gbl_args->pkt.len / 2, gbl_args->pkt2_tbl,
+ TEST_REPEAT_COUNT);
+}
+
+static void alloc_ref_packets(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
+
+ allocate_test_packets(gbl_args->pkt.len, pkt_tbl, TEST_REPEAT_COUNT);
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ ref_tbl[i] = odp_packet_ref(pkt_tbl[i], TEST_MIN_PKT_SIZE / 2);
+ if (ref_tbl[i] == ODP_PACKET_INVALID)
+ ODPH_ABORT("Allocating packet reference failed\n");
+ }
+}
+
+static void alloc_packets_twice(void)
+{
+ allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT);
+ allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt2_tbl,
+ TEST_REPEAT_COUNT);
+}
+
+static void alloc_parse_packets(const void *pkt_data, uint32_t len)
+{
+ int i;
+
+ allocate_test_packets(len, gbl_args->pkt_tbl, TEST_REPEAT_COUNT);
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ if (odp_packet_copy_from_mem(gbl_args->pkt_tbl[i], 0, len,
+ pkt_data))
+ ODPH_ABORT("Copying test packet failed\n");
+ }
+}
+
+static void alloc_parse_packets_ipv4_tcp(void)
+{
+ alloc_parse_packets(test_packet_ipv4_tcp, sizeof(test_packet_ipv4_tcp));
+}
+
+static void alloc_parse_packets_ipv4_udp(void)
+{
+ alloc_parse_packets(test_packet_ipv4_udp, sizeof(test_packet_ipv4_udp));
+}
+
+static void alloc_parse_packets_ipv6_tcp(void)
+{
+ alloc_parse_packets(test_packet_ipv6_tcp, sizeof(test_packet_ipv6_tcp));
+}
+
+static void alloc_parse_packets_ipv6_udp(void)
+{
+ alloc_parse_packets(test_packet_ipv6_udp, sizeof(test_packet_ipv6_udp));
+}
+
+static void alloc_parse_packets_multi(const void *pkt_data, uint32_t len)
+{
+ int i;
+
+ allocate_test_packets(len, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+
+ for (i = 0; i < TEST_REPEAT_COUNT * gbl_args->appl.burst_size; i++) {
+ if (odp_packet_copy_from_mem(gbl_args->pkt_tbl[i], 0, len,
+ pkt_data))
+ ODPH_ABORT("Copying test packet failed\n");
+ }
+}
+
+static void alloc_parse_packets_multi_ipv4_tcp(void)
+{
+ alloc_parse_packets_multi(test_packet_ipv4_tcp,
+ sizeof(test_packet_ipv4_tcp));
+}
+
+static void alloc_parse_packets_multi_ipv4_udp(void)
+{
+ alloc_parse_packets_multi(test_packet_ipv4_udp,
+ sizeof(test_packet_ipv4_udp));
+}
+
+static void alloc_parse_packets_multi_ipv6_tcp(void)
+{
+ alloc_parse_packets_multi(test_packet_ipv6_tcp,
+ sizeof(test_packet_ipv6_tcp));
+}
+
+static void alloc_parse_packets_multi_ipv6_udp(void)
+{
+ alloc_parse_packets_multi(test_packet_ipv6_udp,
+ sizeof(test_packet_ipv6_udp));
+}
+
+static void create_packets(void)
+{
+ int i;
+ uint32_t headroom, tailroom, seg_len;
+ uint32_t min_headroom = 0;
+ uint32_t min_tailroom = 0;
+ uint32_t min_seg_len = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
+
+ allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT);
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ headroom = odp_packet_headroom(pkt_tbl[i]);
+ tailroom = odp_packet_tailroom(pkt_tbl[i]);
+ seg_len = odp_packet_seg_len(pkt_tbl[i]);
+
+ seg_tbl[i] = odp_packet_first_seg(pkt_tbl[i]);
+
+ if (i == 0) {
+ min_headroom = headroom;
+ min_tailroom = tailroom;
+ min_seg_len = seg_len;
+ } else {
+ if (headroom < min_headroom)
+ min_headroom = headroom;
+ if (tailroom < min_tailroom)
+ min_tailroom = tailroom;
+ if (seg_len < min_seg_len)
+ min_seg_len = seg_len;
+ }
+
+ if (odp_packet_l2_offset_set(pkt_tbl[i], TEST_L2_OFFSET) ||
+ odp_packet_l3_offset_set(pkt_tbl[i], TEST_L3_OFFSET) ||
+ odp_packet_l4_offset_set(pkt_tbl[i], TEST_L4_OFFSET))
+ ODPH_ABORT("Setting test packet offsets failed\n");
+
+ odp_packet_flow_hash_set(pkt_tbl[i], i);
+ odp_packet_ts_set(pkt_tbl[i], odp_time_local());
+ }
+ gbl_args->pkt.headroom = min_headroom;
+ gbl_args->pkt.tailroom = min_tailroom;
+ gbl_args->pkt.seg_len = min_seg_len;
+}
+
+static void create_events(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ create_packets();
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->event_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
+}
+
+static void create_events_multi(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ allocate_test_packets(gbl_args->pkt.len, gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+
+ for (i = 0; i < TEST_REPEAT_COUNT * gbl_args->appl.burst_size; i++)
+ gbl_args->event_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
+}
+
+static void free_packets(void)
+{
+ odp_packet_free_multi(gbl_args->pkt_tbl, TEST_REPEAT_COUNT);
+}
+
+static void free_packets_multi(void)
+{
+ odp_packet_free_multi(gbl_args->pkt_tbl,
+ TEST_REPEAT_COUNT * gbl_args->appl.burst_size);
+}
+
+static void free_packets_twice(void)
+{
+ odp_packet_free_multi(gbl_args->pkt_tbl, TEST_REPEAT_COUNT);
+ odp_packet_free_multi(gbl_args->pkt2_tbl, TEST_REPEAT_COUNT);
+}
+
+static int packet_alloc(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(gbl_args->pool, gbl_args->pkt.len);
+
+ gbl_args->pkt_tbl[i] = pkt;
+ }
+
+ return i;
+}
+
+static int packet_alloc_multi(void)
+{
+ int i;
+ int pkts = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ pkts += odp_packet_alloc_multi(gbl_args->pool,
+ gbl_args->pkt.len,
+ &gbl_args->pkt_tbl[pkts],
+ gbl_args->appl.burst_size);
+ return pkts;
+}
+
+static int packet_free(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_free(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_free_multi(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int pkt_idx = i * gbl_args->appl.burst_size;
+
+ odp_packet_free_multi(&gbl_args->pkt_tbl[pkt_idx],
+ gbl_args->appl.burst_size);
+ }
+ return i;
+}
+
+static int packet_free_sp(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int pkt_idx = i * gbl_args->appl.burst_size;
+
+ odp_packet_free_sp(&gbl_args->pkt_tbl[pkt_idx],
+ gbl_args->appl.burst_size);
+ }
+ return i;
+}
+
+static int packet_alloc_free(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(gbl_args->pool, gbl_args->pkt.len);
+
+ odp_packet_free(pkt);
+ }
+ return i;
+}
+
+static int packet_alloc_free_multi(void)
+{
+ int i;
+ int pkts;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ pkts = odp_packet_alloc_multi(gbl_args->pool, gbl_args->pkt.len,
+ gbl_args->pkt_tbl,
+ gbl_args->appl.burst_size);
+
+ if (pkts < 0)
+ ODPH_ABORT("Packet alloc failed\n");
+
+ odp_packet_free_multi(gbl_args->pkt_tbl, pkts);
+ }
+ return i;
+}
+
+static int packet_reset(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_reset(gbl_args->pkt_tbl[i],
+ gbl_args->pkt.len);
+ return !ret;
+}
+
+static int packet_from_event(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ pkt_tbl[i] = odp_packet_from_event(gbl_args->event_tbl[i]);
+
+ return i;
+}
+
+static int packet_from_event_multi(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int idx = i * gbl_args->appl.burst_size;
+
+ odp_packet_from_event_multi(&gbl_args->pkt_tbl[idx],
+ &gbl_args->event_tbl[idx],
+ gbl_args->appl.burst_size);
+ }
+ return i;
+}
+
+static int packet_to_event(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->event_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_to_event_multi(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int idx = i * gbl_args->appl.burst_size;
+
+ odp_packet_to_event_multi(&gbl_args->pkt_tbl[idx],
+ &gbl_args->event_tbl[idx],
+ gbl_args->appl.burst_size);
+ }
+ return i;
+}
+
+static int packet_head(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_head(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_buf_len(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_buf_len(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_data(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_data(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_data_seg_len(void)
+{
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t *output_tbl = gbl_args->output_tbl;
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_data_seg_len(pkt_tbl[i],
+ &output_tbl[i]);
+ return i;
+}
+
+static int packet_seg_len(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_seg_len(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_len(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_len(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_headroom(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_headroom(gbl_args->pkt_tbl[i]);
+
+ return i + ret;
+}
+
+static int packet_tailroom(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_tailroom(gbl_args->pkt_tbl[i]);
+
+ return i + ret;
+}
+
+static int packet_tail(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_tail(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_offset(void)
+{
+ int i;
+ uint32_t offset = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_offset(gbl_args->pkt_tbl[i],
+ offset, NULL, NULL);
+ return i;
+}
+
+static int packet_prefetch(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_prefetch(gbl_args->pkt_tbl[i], 0, gbl_args->pkt.len);
+
+ return i;
+}
+
+static int packet_push_head(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t hroom = gbl_args->pkt.headroom;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_push_head(pkt_tbl[i], hroom);
+
+ return i;
+}
+
+static int packet_pull_head(void)
+{
+ int i;
+ uint32_t len = gbl_args->pkt.seg_len - 1;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_pull_head(pkt_tbl[i], len);
+
+ return i;
+}
+
+static int packet_push_tail(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t troom = gbl_args->pkt.tailroom;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_push_tail(pkt_tbl[i], troom);
+
+ return i;
+}
+
+static int packet_pull_tail(void)
+{
+ int i;
+ uint32_t len = gbl_args->pkt.seg_len - 1;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_pull_tail(pkt_tbl[i], len);
+
+ return i;
+}
+
+static int packet_extend_head(void)
+{
+ int i;
+ int ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ uint32_t *data_tbl = gbl_args->output_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_extend_head(&pkt_tbl[i], len, &ptr_tbl[i],
+ &data_tbl[i]);
+ return ret >= 0;
+}
+
+static int packet_trunc_head(void)
+{
+ int i;
+ int ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ uint32_t *data_tbl = gbl_args->output_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_trunc_head(&pkt_tbl[i], len, &ptr_tbl[i],
+ &data_tbl[i]);
+ return ret >= 0;
+}
+
+static int packet_extend_tail(void)
+{
+ int i;
+ int ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ uint32_t *data_tbl = gbl_args->output_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_extend_tail(&pkt_tbl[i], len, &ptr_tbl[i],
+ &data_tbl[i]);
+ return ret >= 0;
+}
+
+static int packet_trunc_tail(void)
+{
+ int i;
+ int ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ void **ptr_tbl = gbl_args->ptr_tbl;
+ uint32_t *data_tbl = gbl_args->output_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_trunc_tail(&pkt_tbl[i], len, &ptr_tbl[i],
+ &data_tbl[i]);
+ return ret >= 0;
+}
+
+static int packet_add_data(void)
+{
+ int i;
+ int ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t len = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_add_data(&pkt_tbl[i], 0, len);
+
+ return ret >= 0;
+}
+
+static int packet_rem_data(void)
+{
+ int i;
+ int ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t len = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_rem_data(&pkt_tbl[i], 0, len);
+
+ return ret >= 0;
+}
+
+static int packet_align(void)
+{
+ int i;
+ int ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_align(&pkt_tbl[i], TEST_ALIGN_OFFSET,
+ TEST_ALIGN_LEN, TEST_ALIGN);
+ return ret >= 0;
+}
+
+static int packet_is_segmented(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_is_segmented(gbl_args->pkt_tbl[i]);
+
+ return (ret == 0) ? 1 : ret;
+}
+
+static int packet_num_segs(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_num_segs(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_first_seg(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->seg_tbl[i] = odp_packet_first_seg(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_last_seg(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->seg_tbl[i] = odp_packet_last_seg(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_next_seg(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->seg_tbl[i] = odp_packet_next_seg(pkt_tbl[i],
+ seg_tbl[i]);
+ return i;
+}
+
+static int packet_seg_data(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_seg_data(pkt_tbl[i],
+ seg_tbl[i]);
+ return i;
+}
+
+static int packet_seg_data_len(void)
+{
+ int i;
+ uint32_t ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_seg_t *seg_tbl = gbl_args->seg_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_seg_data_len(pkt_tbl[i], seg_tbl[i]);
+
+ return ret;
+}
+
+static int packet_concat(void)
+{
+ int i;
+ int ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *frag_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_concat(&pkt_tbl[i], frag_tbl[i]);
+
+ return ret >= 0;
+}
+
+static int packet_split(void)
+{
+ int i;
+ int ret = 0;
+ uint32_t head_len;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *frag_tbl = gbl_args->pkt2_tbl;
+
+ head_len = odp_packet_len(pkt_tbl[0]) / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_split(&pkt_tbl[i], head_len, &frag_tbl[i]);
+
+ return ret >= 0;
+}
+
+static int packet_copy(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *cpy_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ cpy_tbl[i] = odp_packet_copy(pkt_tbl[i], gbl_args->pool);
+
+ return i;
+}
+
+static int packet_copy_part(void)
+{
+ int i;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *cpy_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ cpy_tbl[i] = odp_packet_copy_part(pkt_tbl[i], 0, len,
+ gbl_args->pool);
+ return i;
+}
+
+static int packet_copy_to_mem(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t len = gbl_args->pkt.len;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_copy_to_mem(pkt_tbl[i], 0, len,
+ gbl_args->data_tbl[i]);
+ return !ret;
+}
+
+static int packet_copy_from_mem(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t len = gbl_args->pkt.len;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_copy_from_mem(pkt_tbl[i], 0, len,
+ gbl_args->data_tbl[i]);
+ return !ret;
+}
+
+static int packet_copy_from_pkt(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t len = gbl_args->pkt.len;
+ odp_packet_t *dst_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *src_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_copy_from_pkt(dst_tbl[i], 0, src_tbl[i], 0,
+ len);
+ return !ret;
+}
+
+static int packet_copy_data(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_copy_data(pkt_tbl[i], 0, len, len);
+
+ return !ret;
+}
+
+static int packet_move_data(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t len = gbl_args->pkt.len / 2;
+ uint32_t offset = len / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_move_data(pkt_tbl[i], offset, len, len);
+
+ return !ret;
+}
+
+static int packet_pool(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->pool_tbl[i] = odp_packet_pool(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_input(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->pktio_tbl[i] = odp_packet_input(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_input_index(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_input_index(gbl_args->pkt_tbl[i]);
+
+ return (ret == 0) ? 1 : ret;
+}
+
+static int packet_user_ptr(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_user_ptr(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_user_ptr_set(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_user_ptr_set(gbl_args->pkt_tbl[i],
+ gbl_args->ptr_tbl[i]);
+
+ return i;
+}
+
+static int packet_user_area(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_user_area(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_user_area_size(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_user_area_size(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_user_flag(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += !odp_packet_user_flag(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_user_flag_set(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_user_flag_set(gbl_args->pkt_tbl[i], 1);
+
+ return i;
+}
+
+static int packet_l2_ptr(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_l2_ptr(gbl_args->pkt_tbl[i],
+ NULL);
+ return i;
+}
+
+static int packet_l2_offset(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l2_offset(gbl_args->pkt_tbl[i]);
+
+ return ret >= 0;
+}
+
+static int packet_l2_offset_set(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t offset = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l2_offset_set(gbl_args->pkt_tbl[i], offset);
+
+ return !ret;
+}
+
+static int packet_l3_ptr(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_l3_ptr(gbl_args->pkt_tbl[i],
+ NULL);
+ return i;
+}
+
+static int packet_l3_offset(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l3_offset(gbl_args->pkt_tbl[i]);
+
+ return ret >= 0;
+}
+
+static int packet_l3_offset_set(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t offset = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l3_offset_set(gbl_args->pkt_tbl[i], offset);
+
+ return !ret;
+}
+
+static int packet_l4_ptr(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ptr_tbl[i] = odp_packet_l4_ptr(gbl_args->pkt_tbl[i],
+ NULL);
+ return i;
+}
+
+static int packet_l4_offset(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l4_offset(gbl_args->pkt_tbl[i]);
+
+ return ret >= 0;
+}
+
+static int packet_l4_offset_set(void)
+{
+ int i;
+ uint32_t ret = 0;
+ uint32_t offset = gbl_args->pkt.len / 2;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_l4_offset_set(gbl_args->pkt_tbl[i], offset);
+
+ return !ret;
+}
+
+static int packet_flow_hash(void)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_flow_hash(gbl_args->pkt_tbl[i]);
+
+ return ret;
+}
+
+static int packet_flow_hash_set(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_flow_hash_set(gbl_args->pkt_tbl[i], i);
+
+ return i;
+}
+
+static int packet_ts(void)
+{
+ int i;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->ts_tbl[i] = odp_packet_ts(gbl_args->pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_ts_set(void)
+{
+ int i;
+ odp_time_t ts = odp_time_local();
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ odp_packet_ts_set(gbl_args->pkt_tbl[i], ts);
+
+ return i;
+}
+
+static int packet_ref_static(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ref_tbl[i] = odp_packet_ref_static(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_ref(void)
+{
+ int i;
+ uint32_t offset = TEST_MIN_PKT_SIZE / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *ref_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ref_tbl[i] = odp_packet_ref(pkt_tbl[i], offset);
+
+ return i;
+}
+
+static int packet_ref_pkt(void)
+{
+ int i;
+ uint32_t offset = TEST_MIN_PKT_SIZE / 2;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ odp_packet_t *hdr_tbl = gbl_args->pkt2_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ hdr_tbl[i] = odp_packet_ref_pkt(pkt_tbl[i], offset, hdr_tbl[i]);
+
+ return i;
+}
+
+static int packet_has_ref(void)
+{
+ int i;
+ uint32_t ret = 0;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_has_ref(pkt_tbl[i]);
+
+ return i + ret;
+}
+
+static int packet_subtype(void)
+{
+ int i;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ gbl_args->output_tbl[i] = odp_packet_subtype(pkt_tbl[i]);
+
+ return i;
+}
+
+static int packet_parse(void)
+{
+ odp_packet_parse_param_t param;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ int ret = 0;
+ int i;
+
+ memset(&param, 0, sizeof(odp_packet_parse_param_t));
+ param.proto = ODP_PROTO_ETH;
+ param.last_layer = ODP_PROTO_LAYER_ALL;
+ param.chksums.chksum.ipv4 = 1;
+ param.chksums.chksum.tcp = 1;
+ param.chksums.chksum.udp = 1;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++)
+ ret += odp_packet_parse(pkt_tbl[i], 0, &param);
+
+ return !ret;
+}
+
+static int packet_parse_multi(void)
+{
+ int burst_size = gbl_args->appl.burst_size;
+ int ret = 0;
+ int i;
+ odp_packet_parse_param_t param;
+ odp_packet_t *pkt_tbl = gbl_args->pkt_tbl;
+ uint32_t offsets[burst_size];
+
+ memset(&offsets, 0, sizeof(offsets));
+
+ memset(&param, 0, sizeof(odp_packet_parse_param_t));
+ param.proto = ODP_PROTO_ETH;
+ param.last_layer = ODP_PROTO_LAYER_ALL;
+ param.chksums.chksum.ipv4 = 1;
+ param.chksums.chksum.tcp = 1;
+ param.chksums.chksum.udp = 1;
+
+ for (i = 0; i < TEST_REPEAT_COUNT; i++) {
+ int idx = i * burst_size;
+
+ ret += odp_packet_parse_multi(&pkt_tbl[idx], offsets,
+ burst_size, &param);
+ }
+ return (ret == TEST_REPEAT_COUNT * burst_size);
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane Packet function microbenchmark.\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -b, --burst <num> Test packet burst size.\n"
+ " -c, --cache_size <num> Pool cache size.\n"
+ " -i, --index <idx> Benchmark index to run indefinitely.\n"
+ " -r, --rounds <num> Run each test case 'num' times (default %u).\n"
+ " -t, --time <opt> Time measurement. 0: measure CPU cycles (default), 1: measure time\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname), TEST_ROUNDS);
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"burst", required_argument, NULL, 'b'},
+ {"cache_size", required_argument, NULL, 'c'},
+ {"index", required_argument, NULL, 'i'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"time", required_argument, NULL, 't'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "c:b:i:r:t:h";
+
+ appl_args->bench_idx = 0; /* Run all benchmarks */
+ appl_args->burst_size = TEST_DEF_BURST;
+ appl_args->cache_size = -1;
+ appl_args->rounds = TEST_ROUNDS;
+ appl_args->time = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cache_size = atoi(optarg);
+ break;
+ case 'b':
+ appl_args->burst_size = atoi(optarg);
+ break;
+ case 'i':
+ appl_args->bench_idx = atoi(optarg);
+ break;
+ case 'r':
+ appl_args->rounds = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (appl_args->burst_size < 1 ||
+ appl_args->burst_size > TEST_MAX_BURST) {
+ printf("Invalid burst size (max %d)\n", TEST_MAX_BURST);
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->rounds < 1) {
+ printf("Invalid number test rounds: %d\n", appl_args->rounds);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* Reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args ODP_UNUSED)
+{
+ odp_sys_info_print();
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n", progname);
+ fflush(NULL);
+}
+
+/**
+ * Test functions
+ */
+bench_info_t test_suite[] = {
+ BENCH_INFO(packet_alloc, NULL, free_packets, NULL),
+ BENCH_INFO(packet_alloc_multi, NULL, free_packets_multi, NULL),
+ BENCH_INFO(packet_free, create_packets, NULL, NULL),
+ BENCH_INFO(packet_free_multi, alloc_packets_multi, NULL, NULL),
+ BENCH_INFO(packet_free_sp, alloc_packets_multi, NULL, NULL),
+ BENCH_INFO(packet_alloc_free, NULL, NULL, NULL),
+ BENCH_INFO(packet_alloc_free_multi, NULL, NULL, NULL),
+ BENCH_INFO(packet_reset, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_from_event, create_events, free_packets, NULL),
+ BENCH_INFO(packet_from_event_multi, create_events_multi, free_packets_multi, NULL),
+ BENCH_INFO(packet_to_event, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_to_event_multi, alloc_packets_multi, free_packets_multi, NULL),
+ BENCH_INFO(packet_head, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_buf_len, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_data, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_data_seg_len, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_seg_len, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_len, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_headroom, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_tailroom, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_tail, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_offset, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_prefetch, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_push_head, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_pull_head, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_push_tail, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_pull_tail, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_extend_head, alloc_packets_half, free_packets, NULL),
+ BENCH_INFO(packet_trunc_head, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_extend_tail, alloc_packets_half, free_packets, NULL),
+ BENCH_INFO(packet_trunc_tail, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_add_data, alloc_packets_half, free_packets, NULL),
+ BENCH_INFO(packet_rem_data, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_align, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_is_segmented, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_num_segs, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_first_seg, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_last_seg, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_next_seg, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_seg_data, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_seg_data_len, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_concat, alloc_concat_packets, free_packets, NULL),
+ BENCH_INFO(packet_split, create_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_copy, create_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_copy_part, create_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_copy_to_mem, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_copy_from_mem, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_copy_from_pkt, alloc_packets_twice, free_packets_twice, NULL),
+ BENCH_INFO(packet_copy_data, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_move_data, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_pool, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_input, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_input_index, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_ptr, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_ptr_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_area, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_area_size, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_flag, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_user_flag_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l2_ptr, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l2_offset, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l2_offset_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l3_ptr, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l3_offset, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l3_offset_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l4_ptr, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l4_offset, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_l4_offset_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_flow_hash, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_flow_hash_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_ts, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_ts_set, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_ref_static, create_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_ref, create_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_ref_pkt, alloc_packets_twice, free_packets_twice, NULL),
+ BENCH_INFO(packet_has_ref, alloc_ref_packets, free_packets_twice, NULL),
+ BENCH_INFO(packet_subtype, create_packets, free_packets, NULL),
+ BENCH_INFO(packet_parse, alloc_parse_packets_ipv4_tcp, free_packets,
+ "packet_parse ipv4/tcp"),
+ BENCH_INFO(packet_parse, alloc_parse_packets_ipv4_udp, free_packets,
+ "packet_parse ipv4/udp"),
+ BENCH_INFO(packet_parse, alloc_parse_packets_ipv6_tcp, free_packets,
+ "packet_parse ipv6/tcp"),
+ BENCH_INFO(packet_parse, alloc_parse_packets_ipv6_udp, free_packets,
+ "packet_parse ipv6/udp"),
+ BENCH_INFO(packet_parse_multi, alloc_parse_packets_multi_ipv4_tcp, free_packets_multi,
+ "packet_parse_multi ipv4/tcp"),
+ BENCH_INFO(packet_parse_multi, alloc_parse_packets_multi_ipv4_udp, free_packets_multi,
+ "packet_parse_multi ipv4/udp"),
+ BENCH_INFO(packet_parse_multi, alloc_parse_packets_multi_ipv6_tcp, free_packets_multi,
+ "packet_parse_multi ipv6/tcp"),
+ BENCH_INFO(packet_parse_multi, alloc_parse_packets_multi_ipv6_udp, free_packets_multi,
+ "packet_parse_multi ipv6/udp"),
+};
+
+/**
+ * ODP packet microbenchmark application
+ */
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t worker_thread;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int cpu;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ uint32_t pkt_num, seg_len;
+ uint8_t ret;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+
+ if (gbl_args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(gbl_args, 0, sizeof(args_t));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ bench_suite_init(&gbl_args->suite);
+ gbl_args->suite.bench = test_suite;
+ gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite);
+ gbl_args->suite.indef_idx = gbl_args->appl.bench_idx;
+ gbl_args->suite.rounds = gbl_args->appl.rounds;
+ gbl_args->suite.repeat_count = TEST_REPEAT_COUNT;
+ gbl_args->suite.measure_time = !!gbl_args->appl.time;
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &gbl_args->appl);
+
+ /* Get default worker cpumask */
+ if (odp_cpumask_default_worker(&cpumask, 1) != 1) {
+ ODPH_ERR("Error: unable to allocate worker thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ /* Check pool capability */
+ if (odp_pool_capability(&capa)) {
+ ODPH_ERR("Error: unable to query pool capability.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* At least 2 x TEST_REPEAT_COUNT packets required */
+ pkt_num = (gbl_args->appl.burst_size > 2) ?
+ gbl_args->appl.burst_size * TEST_REPEAT_COUNT :
+ 2 * TEST_REPEAT_COUNT;
+
+ if (capa.pkt.max_num && capa.pkt.max_num < pkt_num) {
+ ODPH_ERR("Error: packet pool size not supported.\n");
+ printf("MAX: %" PRIu32 "\n", capa.pkt.max_num);
+ exit(EXIT_FAILURE);
+ } else if (capa.pkt.max_len &&
+ capa.pkt.max_len < 2 * TEST_MAX_PKT_SIZE) {
+ ODPH_ERR("Error: packet length not supported.\n");
+ exit(EXIT_FAILURE);
+ } else if (capa.pkt.max_uarea_size &&
+ capa.pkt.max_uarea_size < PKT_POOL_UAREA_SIZE) {
+ ODPH_ERR("Error: user area size not supported.\n");
+ exit(EXIT_FAILURE);
+ } else if (gbl_args->appl.cache_size > (int)capa.pkt.max_cache_size) {
+ ODPH_ERR("Error: cache size not supported (max %" PRIu32 ")\n",
+ capa.pkt.max_cache_size);
+ exit(EXIT_FAILURE);
+ }
+
+ seg_len = TEST_MAX_PKT_SIZE;
+ if (capa.pkt.max_seg_len && capa.pkt.max_seg_len < seg_len) {
+ seg_len = capa.pkt.max_seg_len;
+ printf("\nWarn: allocated packets may be segmented (min seg_len=%" PRIu32 ")\n\n",
+ seg_len);
+ }
+
+ /* Create packet pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = seg_len;
+ /* Using packet length as twice the TEST_MAX_PKT_SIZE as some
+ * test cases (packet_ref_pkt) might allocate a bigger
+ * packet than TEST_MAX_PKT_SIZE.
+ */
+ params.pkt.len = 2 * TEST_MAX_PKT_SIZE;
+ params.pkt.num = pkt_num;
+ params.pkt.uarea_size = PKT_POOL_UAREA_SIZE;
+ if (gbl_args->appl.cache_size >= 0)
+ params.pkt.cache_size = gbl_args->appl.cache_size;
+ params.type = ODP_POOL_PACKET;
+
+ gbl_args->pool = odp_pool_create("packet pool", &params);
+
+ if (gbl_args->pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("CPU mask: %s\n", cpumaskstr);
+ printf("Burst size: %d\n", gbl_args->appl.burst_size);
+ printf("Bench repeat: %d\n", TEST_REPEAT_COUNT);
+ printf("Measurement unit: %s\n", gbl_args->appl.time ? "nsec" : "CPU cycles");
+ printf("Test rounds: %u\n", gbl_args->appl.rounds);
+ if (gbl_args->appl.cache_size < 0)
+ printf("Pool cache size: default\n");
+ else
+ printf("Pool cache size: %d\n", gbl_args->appl.cache_size);
+
+ odp_pool_print(gbl_args->pool);
+
+ memset(&worker_thread, 0, sizeof(odph_thread_t));
+
+ signal(SIGINT, sig_handler);
+
+ /* Create worker threads */
+ cpu = odp_cpumask_first(&cpumask);
+
+ odp_cpumask_t thd_mask;
+
+ odp_cpumask_zero(&thd_mask);
+ odp_cpumask_set(&thd_mask, cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &thd_mask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_benchmarks;
+ thr_param.arg = gbl_args;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&worker_thread, &thr_common, &thr_param, 1);
+
+ odph_thread_join(&worker_thread, 1);
+
+ ret = gbl_args->suite.retval;
+
+ if (odp_pool_destroy(gbl_args->pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+ gbl_args = NULL;
+ odp_mb_full();
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shm free\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_bench_pktio_sp.c b/test/performance/odp_bench_pktio_sp.c
new file mode 100644
index 000000000..017e7565f
--- /dev/null
+++ b/test/performance/odp_bench_pktio_sp.c
@@ -0,0 +1,1140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @example odp_bench_pktio_sp.c
+ *
+ * Microbenchmark application for packet IO slow path functions
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for sigaction */
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Default number of rounds per test case */
+#define ROUNDS 100u
+
+/* Maximum interface name length */
+#define MAX_NAME_LEN 128
+
+#define BENCH_INFO(run_fn, init_fn, term_fn, cond_fn, rounds) \
+ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .cond = cond_fn,\
+ .max_rounds = rounds}
+
+typedef struct {
+ /* Command line options */
+ struct {
+ /* Rounds per test case */
+ uint32_t rounds;
+
+ /* Test case index to run */
+ uint32_t case_idx;
+
+ /* Interface name */
+ char name[MAX_NAME_LEN];
+
+ /* Packet input mode */
+ odp_pktin_mode_t in_mode;
+
+ /* Packet output mode */
+ odp_pktout_mode_t out_mode;
+
+ /* Number of packet input queues */
+ uint32_t num_input_queues;
+
+ /* Number of packet output queues */
+ uint32_t num_output_queues;
+
+ /* Number of PMRs */
+ uint32_t num_pmr;
+ } opt;
+
+ /* Packet IO device */
+ odp_pktio_t pktio;
+
+ /* Packet IO capability*/
+ odp_pktio_capability_t capa;
+
+ /* Packet pool */
+ odp_pool_t pool;
+
+ /* Packet IO statistics */
+ odp_pktio_stats_t stats;
+
+ /* Input queue statistics */
+ odp_pktin_queue_stats_t pktin_queue_stats;
+
+ /* Output queue statistics */
+ odp_pktout_queue_stats_t pktout_queue_stats;
+
+ /* Data for cls_pmr_create() test */
+ struct {
+ /* Term used to create PMRs */
+ odp_cls_pmr_term_t term;
+
+ /* Is test enabled */
+ odp_bool_t enabled;
+
+ } cls_pmr_create;
+
+ /* Common benchmark suite data */
+ bench_tm_suite_t suite;
+
+ /* CPU mask as string */
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
+
+} appl_args_t;
+
+static appl_args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1);
+}
+
+static int setup_sig_handler(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sig_handler;
+
+ /* No additional signals blocked. By default, the signal which triggered
+ * the handler is blocked. */
+ if (sigemptyset(&action.sa_mask))
+ return -1;
+
+ if (sigaction(SIGINT, &action, NULL))
+ return -1;
+
+ return 0;
+}
+
+static void clean_pending_events(void)
+{
+ while (1) {
+ odp_event_t event = odp_schedule(NULL, odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS));
+
+ if (event != ODP_EVENT_INVALID) {
+ odp_event_free(event);
+ continue;
+ }
+ break;
+ };
+}
+
+static odp_pool_t create_packet_pool(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t param;
+ odp_pool_t pool;
+
+ if (odp_pool_capability(&capa))
+ ODPH_ABORT("Reading pool capabilities failed\n");
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = 512;
+ param.pkt.len = 2048;
+
+ if (capa.pkt.max_num && capa.pkt.max_num < param.pkt.num)
+ param.pkt.num = capa.pkt.max_num;
+
+ if (capa.pkt.max_len && capa.pkt.max_len < param.pkt.len)
+ param.pkt.len = capa.pkt.max_len;
+
+ pool = odp_pool_create("pktio_pool", &param);
+ if (pool == ODP_POOL_INVALID)
+ ODPH_ABORT("Creating packet pool failed\n");
+
+ return pool;
+}
+
+static void pktio_setup_param(odp_pktin_mode_t in_mode, odp_pktout_mode_t out_mode, odp_bool_t cls)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_param_t param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ int ret;
+
+ pool = create_packet_pool();
+
+ odp_pktio_param_init(&param);
+ param.in_mode = in_mode;
+ param.out_mode = out_mode;
+
+ pktio = odp_pktio_open(appl_args->opt.name, pool, &param);
+ if (pktio == ODP_PKTIO_INVALID)
+ ODPH_ABORT("Opening pktio failed\n");
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.num_queues = appl_args->opt.num_input_queues;
+
+ if (cls) {
+ pktin_param.classifier_enable = true;
+ } else {
+ if (pktin_param.num_queues > 1) {
+ pktin_param.hash_enable = true;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ }
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.num_queues = appl_args->opt.num_output_queues;
+
+ ret = odp_pktin_queue_config(pktio, &pktin_param);
+ if (ret)
+ ODPH_ABORT("Configuring packet input queues failed: %d\n", ret);
+
+ ret = odp_pktout_queue_config(pktio, &pktout_param);
+ if (ret)
+ ODPH_ABORT("Configuring packet output queues failed: %d\n", ret);
+
+ ret = odp_pktio_start(pktio);
+ if (ret)
+ ODPH_ABORT("Starting pktio failed: %d\n", ret);
+
+ appl_args->pool = pool;
+ appl_args->pktio = pktio;
+}
+
+static void pktio_setup(void)
+{
+ pktio_setup_param(gbl_args->opt.in_mode, gbl_args->opt.out_mode, false);
+}
+
+static void pktio_setup_direct_rx(void)
+{
+ pktio_setup_param(ODP_PKTIN_MODE_DIRECT, gbl_args->opt.out_mode, false);
+}
+
+static void pktio_setup_sched_rx(void)
+{
+ pktio_setup_param(ODP_PKTIN_MODE_SCHED, gbl_args->opt.out_mode, false);
+}
+
+static void pktio_setup_cls(void)
+{
+ pktio_setup_param(ODP_PKTIN_MODE_SCHED, gbl_args->opt.out_mode, true);
+}
+
+static void pktio_setup_direct_tx(void)
+{
+ pktio_setup_param(gbl_args->opt.in_mode, ODP_PKTOUT_MODE_DIRECT, false);
+}
+
+static void pktio_setup_queue_tx(void)
+{
+ pktio_setup_param(gbl_args->opt.in_mode, ODP_PKTOUT_MODE_QUEUE, false);
+}
+
+static void pktio_clean_param(odp_pktin_mode_t in_mode)
+{
+ appl_args_t *appl_args = gbl_args;
+ int ret;
+
+ ret = odp_pktio_stop(appl_args->pktio);
+ if (ret)
+ ODPH_ABORT("Stopping pktio failed: %d\n", ret);
+
+ /* Clean possible pre-scheduled packets */
+ if (in_mode == ODP_PKTIN_MODE_SCHED)
+ clean_pending_events();
+
+ ret = odp_pktio_close(appl_args->pktio);
+ if (ret)
+ ODPH_ABORT("Closing pktio failed: %d\n", ret);
+
+ ret = odp_pool_destroy(appl_args->pool);
+ if (ret)
+ ODPH_ABORT("Destroying pktio pool failed: %d\n", ret);
+}
+
+static void pktio_clean(void)
+{
+ pktio_clean_param(gbl_args->opt.in_mode);
+}
+
+static void pktio_clean_direct_rx(void)
+{
+ pktio_clean_param(ODP_PKTIN_MODE_DIRECT);
+}
+
+static void pktio_clean_sched_rx(void)
+{
+ pktio_clean_param(ODP_PKTIN_MODE_SCHED);
+}
+
+static int pktio_capability(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_capability_t *capa = &appl_args->capa;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktio_capability()");
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktio_capability(pktio, capa);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio capa failed: %d\n", ret);
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktio_lookup(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ const char *name = appl_args->opt.name;
+ odp_pktio_t pktio;
+ odp_time_t t1, t2;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktio_lookup()");
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ pktio = odp_pktio_lookup(name);
+ t2 = odp_time_local_strict();
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Pktio lookup failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktio_open_start_stop_close(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_param_t param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ odp_time_t t1, t2, t3, t4, t5, t6, t7, t8;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktio_open()");
+ uint8_t id2 = bench_tm_func_register(res, "odp_pktin_queue_config()");
+ uint8_t id3 = bench_tm_func_register(res, "odp_pktout_queue_config()");
+ uint8_t id4 = bench_tm_func_register(res, "odp_pktio_start()");
+ uint8_t id5 = bench_tm_func_register(res, "odp_pktio_stop()");
+ uint8_t id6 = bench_tm_func_register(res, "odp_pktio_close()");
+
+ pool = create_packet_pool();
+
+ odp_pktio_param_init(&param);
+ param.in_mode = appl_args->opt.in_mode;
+ param.out_mode = appl_args->opt.out_mode;
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.num_queues = appl_args->opt.num_input_queues;
+ if (pktin_param.num_queues > 1) {
+ pktin_param.hash_enable = true;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.num_queues = appl_args->opt.num_output_queues;
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ pktio = odp_pktio_open(appl_args->opt.name, pool, &param);
+ t2 = odp_time_local_strict();
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Opening pktio failed\n");
+ return -1;
+ }
+
+ ret = odp_pktin_queue_config(pktio, &pktin_param);
+ t3 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Configuring packet input queues failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_pktout_queue_config(pktio, &pktout_param);
+ t4 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Configuring packet output queues failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_pktio_start(pktio);
+ t5 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Starting pktio failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_pktio_stop(pktio);
+ t6 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Stopping pktio failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Clean possible pre-scheduled packets */
+ if (appl_args->opt.in_mode == ODP_PKTIN_MODE_SCHED)
+ clean_pending_events();
+
+ t7 = odp_time_local_strict();
+ ret = odp_pktio_close(pktio);
+ t8 = odp_time_local_strict();
+ if (ret) {
+ ODPH_ERR("Closing pktio failed: %d\n", ret);
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ bench_tm_func_record(t3, t2, res, id2);
+ bench_tm_func_record(t4, t3, res, id3);
+ bench_tm_func_record(t5, t4, res, id4);
+ bench_tm_func_record(t6, t5, res, id5);
+ bench_tm_func_record(t8, t7, res, id6);
+ }
+
+ ret = odp_pool_destroy(pool);
+ if (ret) {
+ ODPH_ERR("Destroying pktio pool failed: %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+static int pktio_stats(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_stats_t *stats = &appl_args->stats;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktio_stats()");
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktio_stats(pktio, stats);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktio_stats_reset(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_time_t t1, t2;
+ int ret;
+ int id1 = bench_tm_func_register(res, "odp_pktio_stats_reset()");
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktio_stats_reset(pktio);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Resetting pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktin_queue_stats(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktin_queue_stats_t *stats = &appl_args->pktin_queue_stats;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_pktin_queue_t queue;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktin_queue_stats()");
+
+ ret = odp_pktin_queue(pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Reading pktio input queue failed\n");
+ return -1;
+ }
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktin_queue_stats(queue, stats);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktin_event_queue_stats(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktin_queue_stats_t *stats = &appl_args->pktin_queue_stats;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_queue_t queue;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktin_event_queue_stats()");
+
+ ret = odp_pktin_event_queue(pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Reading pktio input queue failed\n");
+ return -1;
+ }
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktin_event_queue_stats(pktio, queue, stats);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktout_queue_stats(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktout_queue_stats_t *stats = &appl_args->pktout_queue_stats;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_pktout_queue_t queue;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktout_queue_stats()");
+
+ ret = odp_pktout_queue(pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Reading pktio input queue failed\n");
+ return -1;
+ }
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktout_queue_stats(queue, stats);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int pktout_event_queue_stats(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktout_queue_stats_t *stats = &appl_args->pktout_queue_stats;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_queue_t queue;
+ odp_time_t t1, t2;
+ int ret;
+ uint8_t id1 = bench_tm_func_register(res, "odp_pktout_event_queue_stats()");
+
+ ret = odp_pktout_event_queue(pktio, &queue, 1);
+ if (ret < 1) {
+ ODPH_ERR("Reading pktio input queue failed\n");
+ return -1;
+ }
+
+ for (int i = 0; i < repeat_count; i++) {
+ t1 = odp_time_local_strict();
+ ret = odp_pktout_event_queue_stats(pktio, queue, stats);
+ t2 = odp_time_local_strict();
+
+ if (ret) {
+ ODPH_ERR("Reading pktio stats failed\n");
+ return -1;
+ }
+
+ bench_tm_func_record(t2, t1, res, id1);
+ }
+ return 0;
+}
+
+static int find_first_supported_l3_pmr(const odp_cls_capability_t *capa, odp_cls_pmr_term_t *term)
+{
+ *term = ODP_PMR_TCP_DPORT;
+
+ if (capa->supported_terms.bit.udp_sport)
+ *term = ODP_PMR_UDP_SPORT;
+ else if (capa->supported_terms.bit.udp_dport)
+ *term = ODP_PMR_UDP_DPORT;
+ else if (capa->supported_terms.bit.tcp_sport)
+ *term = ODP_PMR_TCP_SPORT;
+ else if (capa->supported_terms.bit.tcp_dport)
+ *term = ODP_PMR_TCP_DPORT;
+ else
+ return 0;
+
+ return 1;
+}
+
+/* Capabilities required for cls_pmr_create() test */
+static int check_cls_capa(void)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_cls_capability_t cls_capa;
+ odp_schedule_capability_t sched_capa;
+ int ret;
+
+ ret = odp_cls_capability(&cls_capa);
+ if (ret) {
+ ODPH_ERR("Reading classifier capa failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_schedule_capability(&sched_capa);
+ if (ret) {
+ ODPH_ERR("Reading scheduler capa failed: %d\n", ret);
+ return -1;
+ }
+
+ if (!find_first_supported_l3_pmr(&cls_capa, &appl_args->cls_pmr_create.term)) {
+ ODPH_ERR("Implementations doesn't support any TCP/UDP PMRs\n");
+ return 0;
+ }
+
+ /* One extra CoS and queue required for the default CoS */
+ if (appl_args->opt.num_pmr + 1 > cls_capa.max_cos) {
+ ODPH_ERR("Not enough CoSes supported for PMR test: %u/%u\n",
+ appl_args->opt.num_pmr + 1, cls_capa.max_cos);
+ return 0;
+ }
+
+ if (appl_args->opt.num_pmr + 1 > sched_capa.max_queues) {
+ ODPH_ERR("Not enough queues supported for PMR test: %u/%u\n",
+ appl_args->opt.num_pmr + 1, sched_capa.max_queues);
+ return 0;
+ }
+
+ appl_args->cls_pmr_create.enabled = true;
+
+ return 1;
+}
+
+static int check_cls_cond(void)
+{
+ return gbl_args->cls_pmr_create.enabled;
+}
+
+static int cls_pmr_create(bench_tm_result_t *res, int repeat_count)
+{
+ appl_args_t *appl_args = gbl_args;
+ odp_pktio_t pktio = appl_args->pktio;
+ odp_cls_cos_param_t cos_param;
+ odp_queue_param_t queue_param;
+ odp_pmr_param_t pmr_param;
+ odp_cos_t default_cos;
+ uint32_t num_cos = appl_args->opt.num_pmr + 1;
+ uint32_t num_pmr = num_cos - 1;
+ uint32_t cos_created = 0;
+ uint32_t queue_created = 0;
+ uint16_t val = 1024;
+ uint16_t mask = 0xffff;
+ int ret = 0;
+ odp_time_t t1, t2;
+ odp_cos_t cos[num_cos];
+ odp_queue_t queue[num_cos];
+ odp_pmr_t pmr[num_pmr];
+ uint8_t id1 = bench_tm_func_register(res, "odp_cls_pmr_create()");
+ uint8_t id2 = bench_tm_func_register(res, "odp_cls_pmr_destroy()");
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+
+ odp_cls_cos_param_init(&cos_param);
+
+ for (uint32_t i = 0; i < num_cos; i++) {
+ queue[i] = odp_queue_create(NULL, &queue_param);
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("odp_queue_create() failed %u / %u\n", i + 1, num_cos);
+ break;
+ }
+
+ cos_param.queue = queue[i];
+ queue_created++;
+
+ cos[i] = odp_cls_cos_create(NULL, &cos_param);
+ if (cos[i] == ODP_COS_INVALID) {
+ ODPH_ERR("odp_cls_cos_create() failed %u / %u\n", i + 1, num_cos);
+ break;
+ }
+ cos_created++;
+ }
+
+ if (queue_created != num_cos)
+ ODPH_ERR("Unable to create all queues: %u/%u\n", queue_created, num_cos);
+
+ if (cos_created != num_cos) {
+ ODPH_ERR("Unable to create all CoSes: %u/%u\n", cos_created, num_cos);
+ goto destroy_cos;
+ }
+
+ default_cos = cos[0];
+
+ ret = odp_pktio_default_cos_set(pktio, default_cos);
+ if (ret) {
+ ODPH_ERR("Setting default CoS failed: %d\n", ret);
+ goto destroy_cos;
+ }
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = appl_args->cls_pmr_create.term;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ for (uint32_t i = 0; i < (uint32_t)repeat_count; i++) {
+ uint32_t pmr_created = 0;
+
+ for (uint32_t j = 0; j < num_pmr; j++) {
+ t1 = odp_time_local_strict();
+ pmr[j] = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos[j + 1]);
+ t2 = odp_time_local_strict();
+
+ if (pmr[j] == ODP_PMR_INVALID)
+ break;
+ bench_tm_func_record(t2, t1, res, id1);
+
+ val++;
+ pmr_created++;
+ }
+
+ for (uint32_t j = 0; j < pmr_created; j++) {
+ t1 = odp_time_local_strict();
+ ret = odp_cls_pmr_destroy(pmr[j]);
+ t2 = odp_time_local_strict();
+
+ if (ret)
+ ODPH_ABORT("Destroying PMR failed: %d\n", ret);
+
+ bench_tm_func_record(t2, t1, res, id2);
+ }
+
+ if (i == 0)
+ ODPH_DBG("Created %u PMRs\n", pmr_created);
+ }
+
+ ret = odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+
+destroy_cos:
+ for (uint32_t i = 0; i < cos_created; i++)
+ ret = odp_cos_destroy(cos[i]);
+
+ for (uint32_t i = 0; i < queue_created; i++)
+ ret = odp_queue_destroy(queue[i]);
+
+ return ret;
+}
+
+bench_tm_info_t test_suite[] = {
+ BENCH_INFO(pktio_capability, pktio_setup, pktio_clean, NULL, 0),
+ BENCH_INFO(pktio_lookup, pktio_setup, pktio_clean, NULL, 0),
+ BENCH_INFO(pktio_open_start_stop_close, NULL, NULL, NULL, 0),
+ BENCH_INFO(pktio_stats, pktio_setup, pktio_clean, NULL, 0),
+ BENCH_INFO(pktin_queue_stats, pktio_setup_direct_rx, pktio_clean_direct_rx, NULL, 0),
+ BENCH_INFO(pktin_event_queue_stats, pktio_setup_sched_rx, pktio_clean_sched_rx, NULL, 0),
+ BENCH_INFO(pktout_queue_stats, pktio_setup_direct_tx, pktio_clean, NULL, 0),
+ BENCH_INFO(pktout_event_queue_stats, pktio_setup_queue_tx, pktio_clean, NULL, 0),
+ BENCH_INFO(pktio_stats_reset, pktio_setup, pktio_clean, NULL, 0),
+ BENCH_INFO(cls_pmr_create, pktio_setup_cls, pktio_clean_sched_rx, check_cls_cond, 0)
+};
+
+/* Print usage information */
+static void usage(void)
+{
+ printf("\n"
+ "ODP pktio API slow path micro benchmarks\n"
+ "\n"
+ "Options:\n"
+ " -i, --interface <name> Ethernet interface name (default loop).\n"
+ " -m, --in_mode <arg> Packet input mode\n"
+ " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n"
+ " 1: Scheduler mode with parallel queues:\n"
+ " PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n"
+ " -o, --out_mode <arg> Packet output mode\n"
+ " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n"
+ " 1: Queue mode: PKTOUT_MODE_QUEUE\n"
+ " -p, --pmr <num> Number of PMRs to create/destroy per round (default 1)\n"
+ " -q, --rx_queues <num> Number of packet input queues (default 1)\n"
+ " -t, --tx_queues <num> Number of packet output queues (default 1)\n"
+ " -r, --rounds <num> Run each test case 'num' times (default %u).\n"
+ " -s, --select <idx> Run only selected test case.\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", ROUNDS);
+}
+
+static int parse_interface(appl_args_t *appl_args, const char *optarg)
+{
+ if (strlen(optarg) + 1 > MAX_NAME_LEN) {
+ ODPH_ERR("Unable to store interface name (MAX_NAME_LEN=%d)\n", MAX_NAME_LEN);
+ return -1;
+ }
+ strncpy(appl_args->opt.name, optarg, MAX_NAME_LEN);
+ return 0;
+}
+
+/* Parse command line arguments */
+static int parse_args(int argc, char *argv[])
+{
+ int i;
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'},
+ {"in_mode", required_argument, NULL, 'm'},
+ {"out_mode", required_argument, NULL, 'o'},
+ {"pmr", required_argument, NULL, 'p'},
+ {"rx_queues", required_argument, NULL, 'q'},
+ {"tx_queues", required_argument, NULL, 't'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"select", required_argument, NULL, 's'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "i:m:o:p:q:r:s:t:h";
+
+ strncpy(gbl_args->opt.name, "loop", MAX_NAME_LEN);
+ gbl_args->opt.rounds = ROUNDS;
+ gbl_args->opt.in_mode = ODP_PKTIN_MODE_DIRECT;
+ gbl_args->opt.out_mode = ODP_PKTOUT_MODE_DIRECT;
+ gbl_args->opt.num_input_queues = 1;
+ gbl_args->opt.num_output_queues = 1;
+ gbl_args->opt.num_pmr = 1;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'i':
+ if (parse_interface(gbl_args, optarg))
+ return -1;
+ break;
+ case 'm':
+ i = atoi(optarg);
+ if (i == 1)
+ gbl_args->opt.in_mode = ODP_PKTIN_MODE_SCHED;
+ else
+ gbl_args->opt.in_mode = ODP_PKTIN_MODE_DIRECT;
+ break;
+ case 'o':
+ i = atoi(optarg);
+ if (i == 1)
+ gbl_args->opt.out_mode = ODP_PKTOUT_MODE_QUEUE;
+ else
+ gbl_args->opt.out_mode = ODP_PKTOUT_MODE_DIRECT;
+ break;
+ case 'p':
+ gbl_args->opt.num_pmr = atoi(optarg);
+ break;
+ case 'q':
+ gbl_args->opt.num_input_queues = atoi(optarg);
+ break;
+ case 'r':
+ gbl_args->opt.rounds = atoi(optarg);
+ break;
+ case 's':
+ gbl_args->opt.case_idx = atoi(optarg);
+ break;
+ case 't':
+ gbl_args->opt.num_output_queues = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ return 1;
+ default:
+ ODPH_ERR("Bad option. Use -h for help.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int check_args(appl_args_t *appl_args)
+{
+ odp_pktio_param_t param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ int ret;
+
+ if (gbl_args->opt.rounds < 1) {
+ ODPH_ERR("Invalid test repeat count: %u\n", gbl_args->opt.rounds);
+ return -1;
+ }
+
+ if (gbl_args->opt.case_idx > ODPH_ARRAY_SIZE(test_suite)) {
+ ODPH_ERR("Invalid test case index: %u\n", gbl_args->opt.case_idx);
+ return -1;
+ }
+
+ pool = create_packet_pool();
+
+ odp_pktio_param_init(&param);
+ param.in_mode = appl_args->opt.in_mode;
+ param.out_mode = appl_args->opt.out_mode;
+
+ pktio = odp_pktio_open(appl_args->opt.name, pool, &param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Opening pktio failed\n");
+ return -1;
+ }
+
+ ret = odp_pktio_capability(pktio, &capa);
+ if (ret) {
+ ODPH_ERR("Reading pktio capa failed: %d\n", ret);
+ return -1;
+ }
+
+ if (appl_args->opt.num_input_queues > capa.max_input_queues) {
+ ODPH_ERR("Too many input queues: %u/%u\n", appl_args->opt.num_input_queues,
+ capa.max_input_queues);
+ return -1;
+ }
+
+ if (appl_args->opt.num_output_queues > capa.max_output_queues) {
+ ODPH_ERR("Too many output queues: %u/%u\n", appl_args->opt.num_output_queues,
+ capa.max_output_queues);
+ return -1;
+ }
+
+ ret = odp_pktio_close(pktio);
+ if (ret) {
+ ODPH_ERR("Closing pktio failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = odp_pool_destroy(pool);
+ if (ret) {
+ ODPH_ERR("Destroying pktio pool failed: %d\n", ret);
+ return -1;
+ }
+
+ if (check_cls_capa() < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Print application info */
+static void print_info(appl_args_t *appl_args)
+{
+ odp_sys_info_print();
+
+ printf("\n"
+ "odp_bench_pktio_sp options\n"
+ "--------------------------\n");
+
+ printf("CPU mask: %s\n", gbl_args->cpumask_str);
+ printf("Interface: %s\n", gbl_args->opt.name);
+
+ printf("Input mode: ");
+ if (appl_args->opt.in_mode == ODP_PKTIN_MODE_SCHED)
+ printf("sched\n");
+ else
+ printf("direct\n");
+
+ printf("Output mode: ");
+ if (appl_args->opt.out_mode == ODP_PKTOUT_MODE_QUEUE)
+ printf("plain\n");
+ else
+ printf("direct\n");
+
+ printf("Input queues: %u\n", gbl_args->opt.num_input_queues);
+ printf("Output queues: %u\n", gbl_args->opt.num_output_queues);
+ printf("PMRs: %u\n", gbl_args->opt.num_pmr);
+ printf("Test rounds: %d\n", gbl_args->opt.rounds);
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t worker_thread;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask, default_mask;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ int cpu;
+ int ret;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Global init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = odp_schedule_config(NULL);
+ if (ret) {
+ ODPH_ERR("Schedule config failed: %d\n", ret);
+ exit(EXIT_FAILURE);
+ }
+
+ if (setup_sig_handler()) {
+ ODPH_ERR("Signal handler setup failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(appl_args_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL) {
+ ODPH_ERR("Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(gbl_args, 0, sizeof(appl_args_t));
+
+ /* Parse and store the application arguments */
+ ret = parse_args(argc, argv);
+ if (ret)
+ goto exit;
+
+ if (check_args(gbl_args))
+ goto exit;
+
+ bench_tm_suite_init(&gbl_args->suite);
+ gbl_args->suite.bench = test_suite;
+ gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite);
+ gbl_args->suite.rounds = gbl_args->opt.rounds;
+ gbl_args->suite.bench_idx = gbl_args->opt.case_idx;
+
+ /* Get default worker cpumask */
+ if (odp_cpumask_default_worker(&default_mask, 1) != 1) {
+ ODPH_ERR("Unable to allocate worker thread\n");
+ ret = -1;
+ goto exit;
+ }
+
+ (void)odp_cpumask_to_str(&default_mask, gbl_args->cpumask_str,
+ sizeof(gbl_args->cpumask_str));
+
+ print_info(gbl_args);
+
+ memset(&worker_thread, 0, sizeof(odph_thread_t));
+
+ /* Create worker thread */
+ cpu = odp_cpumask_first(&default_mask);
+
+ odp_cpumask_zero(&cpumask);
+ odp_cpumask_set(&cpumask, cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = bench_tm_run;
+ thr_param.arg = &gbl_args->suite;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&worker_thread, &thr_common, &thr_param, 1);
+
+ odph_thread_join(&worker_thread, 1);
+
+ ret = gbl_args->suite.retval;
+
+exit:
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shared mem free failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/performance/odp_bench_timer.c b/test/performance/odp_bench_timer.c
new file mode 100644
index 000000000..ad80367d1
--- /dev/null
+++ b/test/performance/odp_bench_timer.c
@@ -0,0 +1,740 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @example odp_bench_timer.c
+ *
+ * Microbenchmark application for timer API functions
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for sigaction */
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "bench_common.h"
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Number of API function calls per test case */
+#define REPEAT_COUNT 1000
+
+/* Default number of rounds per test case */
+#define ROUNDS 1000u
+
+/** User area size in bytes */
+#define UAREA_SIZE 8
+
+/** Timer duration in nsec */
+#define TIMER_NSEC 50000000
+
+#define BENCH_INFO(run_fn, max, alt_name) \
+ {.name = #run_fn, .run = run_fn, .max_rounds = max, .desc = alt_name}
+
+typedef struct {
+ /* Command line options */
+ struct {
+ /* Clock source to be used */
+ int clk_src;
+
+ /* Measure time vs CPU cycles */
+ int time;
+
+ /* Benchmark index to run indefinitely */
+ int bench_idx;
+
+ /* Rounds per test case */
+ uint32_t rounds;
+
+ } opt;
+
+ /* Common benchmark suite data */
+ bench_suite_t suite;
+
+ odp_timer_pool_t timer_pool;
+ odp_timer_t timer;
+ odp_queue_t queue;
+ odp_pool_t pool;
+ odp_timeout_t timeout;
+ odp_event_t event;
+ uint64_t timer_nsec;
+ uint64_t tick;
+ uint64_t nsec;
+ double tick_hz;
+ int plain_queue;
+
+ /* Test case input / output data */
+ uint64_t a1[REPEAT_COUNT];
+ odp_event_t ev[REPEAT_COUNT];
+ odp_timeout_t tmo[REPEAT_COUNT];
+ odp_timer_t tim[REPEAT_COUNT];
+
+ /* CPU mask as string */
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
+
+} gbl_args_t;
+
+static gbl_args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1);
+}
+
+static int setup_sig_handler(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sig_handler;
+
+ /* No additional signals blocked. By default, the signal which triggered
+ * the handler is blocked. */
+ if (sigemptyset(&action.sa_mask))
+ return -1;
+
+ if (sigaction(SIGINT, &action, NULL))
+ return -1;
+
+ return 0;
+}
+
+static int timer_current_tick(void)
+{
+ int i;
+ odp_timer_pool_t timer_pool = gbl_args->timer_pool;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timer_current_tick(timer_pool);
+
+ return i;
+}
+
+static int timer_tick_to_ns(void)
+{
+ int i;
+ odp_timer_pool_t timer_pool = gbl_args->timer_pool;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t tick = gbl_args->tick;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timer_tick_to_ns(timer_pool, tick);
+
+ return i;
+}
+
+static int timer_ns_to_tick(void)
+{
+ int i;
+ odp_timer_pool_t timer_pool = gbl_args->timer_pool;
+ uint64_t *a1 = gbl_args->a1;
+ uint64_t nsec = gbl_args->nsec;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timer_ns_to_tick(timer_pool, nsec);
+
+ return i;
+}
+
+static int timeout_to_event(void)
+{
+ int i;
+ odp_event_t *ev = gbl_args->ev;
+ odp_timeout_t timeout = gbl_args->timeout;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ ev[i] = odp_timeout_to_event(timeout);
+
+ gbl_args->suite.dummy += odp_event_to_u64(ev[0]);
+
+ return i;
+}
+
+static int timeout_from_event(void)
+{
+ int i;
+ odp_event_t ev = gbl_args->event;
+ odp_timeout_t *tmo = gbl_args->tmo;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ tmo[i] = odp_timeout_from_event(ev);
+
+ gbl_args->suite.dummy += odp_timeout_to_u64(tmo[0]);
+
+ return i;
+}
+
+static int timeout_timer(void)
+{
+ int i;
+ odp_timeout_t timeout = gbl_args->timeout;
+ odp_timer_t *tim = gbl_args->tim;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ tim[i] = odp_timeout_timer(timeout);
+
+ gbl_args->suite.dummy += odp_timer_to_u64(tim[0]);
+
+ return i;
+}
+
+static int timeout_tick(void)
+{
+ int i;
+ odp_timeout_t timeout = gbl_args->timeout;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timeout_tick(timeout);
+
+ return i;
+}
+
+static int timeout_user_ptr(void)
+{
+ int i;
+ odp_timeout_t timeout = gbl_args->timeout;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = (uintptr_t)odp_timeout_user_ptr(timeout);
+
+ return i;
+}
+
+static int timeout_user_area(void)
+{
+ int i;
+ odp_timeout_t timeout = gbl_args->timeout;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = (uintptr_t)odp_timeout_user_area(timeout);
+
+ return i;
+}
+
+static int timeout_to_u64(void)
+{
+ int i;
+ odp_timeout_t timeout = gbl_args->timeout;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timeout_to_u64(timeout);
+
+ return i;
+}
+
+static int timer_to_u64(void)
+{
+ int i;
+ odp_timer_t timer = gbl_args->timer;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timer_to_u64(timer);
+
+ return i;
+}
+
+static int timer_pool_to_u64(void)
+{
+ int i;
+ odp_timer_pool_t tp = gbl_args->timer_pool;
+ uint64_t *a1 = gbl_args->a1;
+
+ for (i = 0; i < REPEAT_COUNT; i++)
+ a1[i] = odp_timer_pool_to_u64(tp);
+
+ return i;
+}
+
+bench_info_t test_suite[] = {
+ BENCH_INFO(timer_current_tick, 0, NULL),
+ BENCH_INFO(timer_tick_to_ns, 0, NULL),
+ BENCH_INFO(timer_ns_to_tick, 0, NULL),
+ BENCH_INFO(timeout_to_event, 0, NULL),
+ BENCH_INFO(timeout_from_event, 0, NULL),
+ BENCH_INFO(timeout_timer, 0, NULL),
+ BENCH_INFO(timeout_tick, 0, NULL),
+ BENCH_INFO(timeout_user_ptr, 0, NULL),
+ BENCH_INFO(timeout_user_area, 0, NULL),
+ BENCH_INFO(timeout_to_u64, 0, NULL),
+ BENCH_INFO(timer_to_u64, 0, NULL),
+ BENCH_INFO(timer_pool_to_u64, 0, NULL),
+};
+
+/* Print usage information */
+static void usage(void)
+{
+ printf("\n"
+ "ODP timer API micro benchmarks\n"
+ "\n"
+ "Options:\n"
+ " -s, --clk_src Clock source select (default 0):\n"
+ " 0: ODP_CLOCK_DEFAULT\n"
+ " 1: ODP_CLOCK_SRC_1, ...\n"
+ " -t, --time <opt> Time measurement. 0: measure CPU cycles (default), 1: measure time\n"
+ " -i, --index <idx> Benchmark index to run indefinitely.\n"
+ " -r, --rounds <num> Run each test case 'num' times (default %u).\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", ROUNDS);
+}
+
+/* Parse command line arguments */
+static int parse_args(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"clk_src", required_argument, NULL, 's'},
+ {"time", required_argument, NULL, 't'},
+ {"index", required_argument, NULL, 'i'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "s:t:i:r:h";
+
+ gbl_args->opt.clk_src = ODP_CLOCK_DEFAULT;
+ gbl_args->opt.time = 0; /* Measure CPU cycles */
+ gbl_args->opt.bench_idx = 0; /* Run all benchmarks */
+ gbl_args->opt.rounds = ROUNDS;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 's':
+ gbl_args->opt.clk_src = atoi(optarg);
+ break;
+ case 't':
+ gbl_args->opt.time = atoi(optarg);
+ break;
+ case 'i':
+ gbl_args->opt.bench_idx = atoi(optarg);
+ break;
+ case 'r':
+ gbl_args->opt.rounds = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ return 1;
+ default:
+ ODPH_ERR("Bad option. Use -h for help.\n");
+ return -1;
+ }
+ }
+
+ if (gbl_args->opt.rounds < 1) {
+ ODPH_ERR("Invalid test cycle repeat count: %u\n", gbl_args->opt.rounds);
+ return -1;
+ }
+
+ if (gbl_args->opt.bench_idx < 0 ||
+ gbl_args->opt.bench_idx > (int)ODPH_ARRAY_SIZE(test_suite)) {
+ ODPH_ERR("Bad bench index %i\n", gbl_args->opt.bench_idx);
+ return -1;
+ }
+
+ optind = 1; /* Reset 'extern optind' from the getopt lib */
+
+ return 0;
+}
+
+/* Print system and application info */
+static void print_info(void)
+{
+ odp_sys_info_print();
+
+ printf("\n"
+ "odp_bench_timer options\n"
+ "-----------------------\n");
+
+ printf("CPU mask: %s\n", gbl_args->cpumask_str);
+ printf("Clock source: %i\n", gbl_args->opt.clk_src);
+ printf("Measurement unit: %s\n", gbl_args->opt.time ? "nsec" : "CPU cycles");
+ printf("Test rounds: %u\n", gbl_args->opt.rounds);
+ printf("Timer duration: %" PRIu64 " nsec\n", gbl_args->timer_nsec);
+ printf("Timer tick freq: %.2f Hz\n", gbl_args->tick_hz);
+ printf("\n");
+}
+
+static int create_timer(void)
+{
+ odp_pool_capability_t pool_capa;
+ odp_timer_capability_t timer_capa;
+ odp_timer_clk_src_t clk_src;
+ odp_timer_pool_param_t tp_param;
+ odp_timer_pool_t tp;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_timeout_t tmo;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ odp_timer_t timer;
+ uint64_t t1, t2, diff, tick1, tick2;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capa failed\n");
+ return -1;
+ }
+
+ clk_src = gbl_args->opt.clk_src;
+ if (odp_timer_capability(clk_src, &timer_capa)) {
+ ODPH_ERR("Timer capa failed\n");
+ return -1;
+ }
+
+ odp_timer_pool_param_init(&tp_param);
+ tp_param.clk_src = clk_src;
+ tp_param.res_ns = timer_capa.max_res.res_ns;
+ tp_param.min_tmo = timer_capa.max_res.min_tmo;
+ tp_param.max_tmo = timer_capa.max_res.max_tmo;
+ tp_param.num_timers = 10;
+
+ tp = odp_timer_pool_create("bench_timer", &tp_param);
+
+ if (tp == ODP_TIMER_POOL_INVALID) {
+ ODPH_ERR("Timer pool create failed\n");
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&tp, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ return -1;
+ }
+
+ gbl_args->timer_pool = tp;
+
+ gbl_args->timer_nsec = TIMER_NSEC;
+ if (TIMER_NSEC < tp_param.min_tmo)
+ gbl_args->timer_nsec = tp_param.min_tmo;
+ else if (TIMER_NSEC > tp_param.max_tmo)
+ gbl_args->timer_nsec = tp_param.max_tmo;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = 10;
+ pool_param.tmo.uarea_size = UAREA_SIZE;
+ if (UAREA_SIZE > pool_capa.tmo.max_uarea_size)
+ pool_param.tmo.uarea_size = pool_capa.tmo.max_uarea_size;
+
+ pool = odp_pool_create("bench_timer", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Timeout pool create failed\n");
+ return -1;
+ }
+
+ gbl_args->pool = pool;
+
+ tmo = odp_timeout_alloc(pool);
+
+ if (tmo == ODP_TIMEOUT_INVALID) {
+ ODPH_ERR("Timeout alloc failed\n");
+ return -1;
+ }
+
+ gbl_args->timeout = tmo;
+ gbl_args->tick = odp_timer_current_tick(tp);
+ gbl_args->nsec = odp_timer_tick_to_ns(tp, gbl_args->tick);
+
+ /* Measure timer tick frequency for test information */
+ t1 = odp_time_global_strict_ns();
+ tick1 = odp_timer_current_tick(tp);
+
+ odp_time_wait_ns(200 * ODP_TIME_MSEC_IN_NS);
+
+ tick2 = odp_timer_current_tick(tp);
+ t2 = odp_time_global_strict_ns();
+ diff = t2 - t1;
+
+ if (diff)
+ gbl_args->tick_hz = (tick2 - tick1) / ((double)diff / ODP_TIME_SEC_IN_NS);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ if (timer_capa.queue_type_sched == 0) {
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ gbl_args->plain_queue = 1;
+ }
+
+ queue = odp_queue_create("bench_timer", &queue_param);
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed\n");
+ return -1;
+ }
+
+ gbl_args->queue = queue;
+
+ timer = odp_timer_alloc(tp, queue, (void *)(uintptr_t)0xdeadbeef);
+ if (timer == ODP_TIMER_INVALID) {
+ ODPH_ERR("Timer alloc failed\n");
+ return -1;
+ }
+
+ gbl_args->timer = timer;
+
+ return 0;
+}
+
+static int wait_timer(void)
+{
+ odp_timer_start_t start_param;
+ odp_timer_t timer = gbl_args->timer;
+ odp_timer_pool_t tp = gbl_args->timer_pool;
+ uint64_t wait_nsec = 2 * gbl_args->timer_nsec;
+ uint64_t sched_wait = odp_schedule_wait_time(wait_nsec);
+ odp_event_t ev;
+ uint64_t start;
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = odp_timer_ns_to_tick(tp, gbl_args->timer_nsec);
+ start_param.tmo_ev = odp_timeout_to_event(gbl_args->timeout);
+
+ if (odp_timer_start(timer, &start_param) != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer start failed\n");
+ return -1;
+ }
+
+ gbl_args->timeout = ODP_TIMEOUT_INVALID;
+ gbl_args->event = ODP_EVENT_INVALID;
+
+ /* Wait for timeout */
+ if (gbl_args->plain_queue) {
+ start = odp_time_global_ns();
+ while (1) {
+ ev = odp_queue_deq(gbl_args->queue);
+
+ if (ev != ODP_EVENT_INVALID)
+ break;
+
+ if ((odp_time_global_ns() - start) > wait_nsec) {
+ ODPH_ERR("Timeout event missing\n");
+ return -1;
+ }
+ }
+
+ gbl_args->event = ev;
+ } else {
+ ev = odp_schedule(NULL, sched_wait);
+
+ if (ev == ODP_EVENT_INVALID) {
+ ODPH_ERR("Timeout event missing\n");
+ return -1;
+ }
+
+ gbl_args->event = ev;
+
+ /* Free schedule context */
+ if (odp_schedule(NULL, ODP_SCHED_NO_WAIT) != ODP_EVENT_INVALID) {
+ ODPH_ERR("Extra timeout event\n");
+ return -1;
+ }
+ }
+
+ if (odp_event_type(gbl_args->event) != ODP_EVENT_TIMEOUT) {
+ ODPH_ERR("Bad event type\n");
+ return -1;
+ }
+
+ gbl_args->timeout = odp_timeout_from_event(gbl_args->event);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t worker_thread;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int cpu, i;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask, default_mask;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ int ret = 0;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Global init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setup_sig_handler()) {
+ ODPH_ERR("Signal handler setup failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_schedule_config(NULL);
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(gbl_args_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL) {
+ ODPH_ERR("Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(gbl_args, 0, sizeof(gbl_args_t));
+ gbl_args->timer_pool = ODP_TIMER_POOL_INVALID;
+ gbl_args->timer = ODP_TIMER_INVALID;
+ gbl_args->queue = ODP_QUEUE_INVALID;
+ gbl_args->pool = ODP_POOL_INVALID;
+ gbl_args->timeout = ODP_TIMEOUT_INVALID;
+
+ for (i = 0; i < REPEAT_COUNT; i++) {
+ gbl_args->a1[i] = i;
+ gbl_args->ev[i] = ODP_EVENT_INVALID;
+ gbl_args->tmo[i] = ODP_TIMEOUT_INVALID;
+ gbl_args->tim[i] = ODP_TIMER_INVALID;
+ }
+
+ /* Parse and store the application arguments */
+ ret = parse_args(argc, argv);
+ if (ret)
+ goto exit;
+
+ bench_suite_init(&gbl_args->suite);
+ gbl_args->suite.bench = test_suite;
+ gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite);
+ gbl_args->suite.measure_time = !!gbl_args->opt.time;
+ gbl_args->suite.indef_idx = gbl_args->opt.bench_idx;
+ gbl_args->suite.rounds = gbl_args->opt.rounds;
+ gbl_args->suite.repeat_count = REPEAT_COUNT;
+
+ /* Get default worker cpumask */
+ if (odp_cpumask_default_worker(&default_mask, 1) != 1) {
+ ODPH_ERR("Unable to allocate worker thread\n");
+ ret = -1;
+ goto exit;
+ }
+
+ (void)odp_cpumask_to_str(&default_mask, gbl_args->cpumask_str,
+ sizeof(gbl_args->cpumask_str));
+
+ /* Create timer and other resources */
+ ret = create_timer();
+ if (ret)
+ goto exit;
+
+ print_info();
+
+ /* Start one timer and wait for the timeout event. Timer expiration fills in
+ * timeout event metadata. */
+ ret = wait_timer();
+ if (ret)
+ goto exit;
+
+ memset(&worker_thread, 0, sizeof(odph_thread_t));
+
+ /* Create worker thread */
+ cpu = odp_cpumask_first(&default_mask);
+
+ odp_cpumask_zero(&cpumask);
+ odp_cpumask_set(&cpumask, cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = bench_run;
+ thr_param.arg = &gbl_args->suite;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&worker_thread, &thr_common, &thr_param, 1);
+
+ odph_thread_join(&worker_thread, 1);
+
+ ret = gbl_args->suite.retval;
+
+exit:
+ if (gbl_args->timeout != ODP_TIMEOUT_INVALID)
+ odp_timeout_free(gbl_args->timeout);
+
+ if (gbl_args->pool != ODP_POOL_INVALID)
+ odp_pool_destroy(gbl_args->pool);
+
+ if (gbl_args->timer != ODP_TIMER_INVALID) {
+ if (odp_timer_free(gbl_args->timer)) {
+ ODPH_ERR("Timer free failed\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (gbl_args->timer_pool != ODP_TIMER_POOL_INVALID)
+ odp_timer_pool_destroy(gbl_args->timer_pool);
+
+ if (gbl_args->queue != ODP_QUEUE_INVALID) {
+ if (odp_queue_destroy(gbl_args->queue)) {
+ ODPH_ERR("Queue destroy failed\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shared mem free failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global term failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/performance/odp_cpu_bench.c b/test/performance/odp_cpu_bench.c
new file mode 100644
index 000000000..674015d8a
--- /dev/null
+++ b/test/performance/odp_cpu_bench.c
@@ -0,0 +1,835 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @example odp_cpu_bench.c
+ *
+ * Application for CPU stress testing
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Queues are divided into groups and tests packets are passed only between
+ * queues which belong to the same group. */
+#define MAX_GROUPS 64
+#define QUEUES_PER_GROUP 4
+#define PKTS_PER_QUEUE 256
+
+#define MAX_EVENT_BURST 32
+#define CRC_INIT_VAL 123456789
+#define PASS_PACKETS 10000
+
+/* Default number of entries in the test lookup table */
+#define DEF_LOOKUP_TBL_SIZE (1024 * 1024)
+
+#define MAX_WORKERS \
+ (((ODP_THREAD_COUNT_MAX - 1) > (MAX_GROUPS * QUEUES_PER_GROUP)) ? \
+ (MAX_GROUPS * QUEUES_PER_GROUP) : \
+ (ODP_THREAD_COUNT_MAX - 1))
+
+ODP_STATIC_ASSERT(MAX_WORKERS <= MAX_GROUPS * QUEUES_PER_GROUP,
+ "Not enough queues for all workers");
+
+/* Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+/* Test dummy lookup table entry */
+typedef struct {
+ uint64_t idx;
+ uint32_t val0;
+ uint32_t val1;
+} lookup_entry_t;
+
+/* Test packet */
+typedef struct {
+ uint32_t seq;
+ uint32_t crc;
+ uint16_t group;
+} test_hdr_t;
+
+/* Parsed application arguments */
+typedef struct {
+ uint64_t lookup_tbl_size; /* Lookup table size */
+ int accuracy; /* Number of seconds between stats prints */
+ unsigned int cpu_count; /* CPU count */
+ int time; /* Time in seconds to run */
+} appl_args_t;
+
+/* Statistics */
+typedef union ODP_ALIGNED_CACHE {
+ struct {
+ /* Number of processed packets */
+ uint64_t pkts;
+ /* Number of dropped packets */
+ uint64_t dropped_pkts;
+ /* Time spent processing packets */
+ uint64_t nsec;
+ /* Cycles spent processing packets */
+ uint64_t cycles;
+ } s;
+
+ uint8_t padding[ODP_CACHE_LINE_SIZE];
+} stats_t;
+
+/* Thread specific data */
+typedef struct thread_args_t {
+ stats_t stats;
+ uint16_t idx;
+} thread_args_t;
+
+/* Grouping of all global data */
+typedef struct {
+ /* Thread specific arguments */
+ thread_args_t thread[MAX_WORKERS];
+ /* Barriers to synchronize main and workers */
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ /* Application (parsed) arguments */
+ appl_args_t appl;
+ /* Test queues */
+ odp_queue_t queue[MAX_GROUPS][QUEUES_PER_GROUP];
+ /* Test lookup table */
+ lookup_entry_t *lookup_tbl;
+ /* Break workers loop if set to 1 */
+ odp_atomic_u32_t exit_threads;
+} args_t;
+
+/* Global pointer to args */
+static args_t *gbl_args;
+
+static const uint8_t test_udp_packet[] = {
+ 0x00, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x45, 0x00,
+ 0x02, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0xF7, 0x7C, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
+ 0x00, 0x02, 0x04, 0xD2, 0x1A, 0x82, 0x02, 0x08,
+ 0x24, 0x1E, 0xC9, 0x56, 0xB4, 0xD6, 0x4B, 0x64,
+ 0xB3, 0x01, 0xA1, 0x97, 0x4D, 0xD1, 0xA4, 0x76,
+ 0xF5, 0x7B, 0x27, 0x22, 0x6C, 0xA9, 0xED, 0x29,
+ 0x6E, 0x02, 0x80, 0xF7, 0xC4, 0x2D, 0x2A, 0x96,
+ 0x2D, 0xF6, 0x02, 0x8E, 0x89, 0x9F, 0x8C, 0xF4,
+ 0x0D, 0xC5, 0xE5, 0x1F, 0xA1, 0x52, 0xC3, 0x4B,
+ 0x5C, 0x4C, 0xDF, 0x14, 0x05, 0x6A, 0xA8, 0xD7,
+ 0xAD, 0x4F, 0x22, 0xA6, 0xB8, 0xF9, 0x52, 0x5A,
+ 0xB8, 0xF9, 0xE2, 0x2C, 0x05, 0x2A, 0x6F, 0xF2,
+ 0xCA, 0xA1, 0xA7, 0xC3, 0x56, 0xE1, 0xDB, 0xC1,
+ 0xDB, 0x86, 0x26, 0x55, 0xAC, 0xBE, 0xE1, 0x3D,
+ 0x82, 0x86, 0xB9, 0xDE, 0x3E, 0xD3, 0x11, 0xAB,
+ 0x65, 0x6A, 0xED, 0x1B, 0x60, 0xBE, 0x69, 0x71,
+ 0xB2, 0xA8, 0x5B, 0xB1, 0x06, 0xE3, 0x48, 0x14,
+ 0xC9, 0x13, 0x73, 0xDA, 0xBE, 0xE4, 0x7A, 0x5F,
+ 0xC0, 0xE0, 0xCA, 0xF3, 0x7A, 0xCA, 0x3F, 0xC9,
+ 0x4A, 0xEE, 0x47, 0x76, 0x67, 0xF0, 0x0D, 0x3F,
+ 0x7F, 0x3D, 0x69, 0xEA, 0x39, 0x53, 0x7C, 0xE3,
+ 0xED, 0x78, 0x79, 0x47, 0x60, 0x95, 0xCB, 0xDC,
+ 0x26, 0x60, 0x46, 0xAC, 0x47, 0xDA, 0x4C, 0x4D,
+ 0x0F, 0xE1, 0x68, 0x43, 0xBC, 0xCD, 0x4E, 0xFE,
+ 0x2E, 0xD6, 0xC2, 0x6E, 0x63, 0xEA, 0xB3, 0x98,
+ 0xCA, 0x8F, 0x7F, 0x05, 0xDF, 0x72, 0x8F, 0x6E,
+ 0x3E, 0x6D, 0xC7, 0x94, 0x59, 0x9D, 0x15, 0x5B,
+ 0xB8, 0x02, 0x52, 0x4F, 0x68, 0x3A, 0xF1, 0xFF,
+ 0xA9, 0xA4, 0x30, 0x29, 0xE0, 0x1C, 0xA0, 0x1B,
+ 0x50, 0xAB, 0xFD, 0x06, 0x84, 0xD4, 0x33, 0x51,
+ 0x01, 0xB3, 0x5F, 0x49, 0x5F, 0x21, 0xA0, 0xA1,
+ 0xC9, 0x08, 0xB3, 0xDF, 0x72, 0x9B, 0x5B, 0x70,
+ 0x89, 0x96, 0x08, 0x25, 0x88, 0x1E, 0xED, 0x52,
+ 0xDC, 0x98, 0xA0, 0xB8, 0x83, 0x2A, 0xA0, 0x90,
+ 0x45, 0xC9, 0x77, 0xD2, 0x19, 0xD7, 0x6B, 0xAB,
+ 0x49, 0x67, 0x7C, 0xD1, 0xE0, 0x23, 0xA2, 0x36,
+ 0xB2, 0x91, 0x3B, 0x23, 0x3B, 0x03, 0x36, 0xAF,
+ 0xAD, 0x81, 0xFA, 0x6F, 0x68, 0xD5, 0xBE, 0x73,
+ 0x1D, 0x56, 0x8A, 0xE8, 0x1A, 0xB4, 0xA8, 0x7C,
+ 0xF3, 0x82, 0x10, 0xD0, 0xF2, 0x1D, 0x9C, 0xEA,
+ 0xAB, 0xE7, 0xEC, 0x53, 0x6D, 0x52, 0xBD, 0x29,
+ 0x86, 0x21, 0xCE, 0xAA, 0xF3, 0x68, 0xA6, 0xEC,
+ 0x7E, 0xCA, 0x6F, 0xEB, 0xE1, 0x81, 0x80, 0x7C,
+ 0xF3, 0xE5, 0x22, 0xA0, 0x91, 0x08, 0xB7, 0x35,
+ 0x15, 0x87, 0x0C, 0x77, 0x31, 0x9C, 0x2F, 0x73,
+ 0xCE, 0x29, 0x6F, 0xC6, 0xAC, 0x9F, 0x68, 0xB8,
+ 0x6A, 0xFC, 0xD3, 0xB5, 0x08, 0x98, 0xAE, 0xE4,
+ 0x20, 0x84, 0x24, 0x69, 0xA5, 0xF5, 0x4A, 0x9D,
+ 0x44, 0x26, 0x5A, 0xF9, 0x6B, 0x5E, 0x5D, 0xC8,
+ 0x6F, 0xD4, 0x62, 0x91, 0xE5, 0x8E, 0x80, 0x05,
+ 0xA1, 0x95, 0x09, 0xEA, 0xFE, 0x84, 0x6D, 0xC3,
+ 0x0D, 0xD4, 0x32, 0xA4, 0x38, 0xB2, 0xF7, 0x9D,
+ 0x58, 0xD3, 0x5D, 0x93, 0x5F, 0x67, 0x86, 0xE1,
+ 0xAF, 0xFF, 0xE9, 0xFE, 0xF4, 0x71, 0x63, 0xE3,
+ 0x3E, 0xE1, 0x7A, 0x80, 0x5A, 0x23, 0x4F, 0x5B,
+ 0x54, 0x21, 0x0E, 0xE2, 0xAF, 0x01, 0x2E, 0xA4,
+ 0xF5, 0x1F, 0x59, 0x96, 0x3E, 0x82, 0xF3, 0x44,
+ 0xDF, 0xA6, 0x7C, 0x64, 0x5D, 0xC7, 0x79, 0xA1,
+ 0x17, 0xE1, 0x06, 0x14, 0x3E, 0x1B, 0x46, 0xCA,
+ 0x71, 0xC8, 0x05, 0x62, 0xD0, 0x56, 0x23, 0x9B,
+ 0xBA, 0xFE, 0x6D, 0xA8, 0x03, 0x4C, 0x23, 0xD8,
+ 0x98, 0x8A, 0xE8, 0x9C, 0x93, 0x8E, 0xB7, 0x24,
+ 0x31, 0x2A, 0x81, 0x72, 0x8F, 0x13, 0xD4, 0x7E,
+ 0xEB, 0xB1, 0xEE, 0x33, 0xD9, 0xF4, 0x96, 0x5E,
+ 0x6C, 0x3D, 0x45, 0x9C, 0xE0, 0x71, 0xA3, 0xFA,
+ 0x17, 0x2B, 0xC3, 0x07, 0xD6, 0x86, 0xA2, 0x06,
+ 0xC5, 0x33, 0xF0, 0xEA, 0x25, 0x70, 0x68, 0x56,
+ 0xD5, 0xB0
+};
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+}
+
+static inline void init_packet(odp_packet_t pkt, uint32_t seq, uint16_t group)
+{
+ odp_una_u32_t *payload;
+ test_hdr_t *hdr;
+ odp_packet_parse_param_t param;
+
+ param.proto = ODP_PROTO_ETH;
+ param.last_layer = ODP_PROTO_LAYER_ALL;
+ param.chksums.all_chksum = 0;
+ if (odp_packet_parse(pkt, 0, &param))
+ ODPH_ABORT("odp_packet_parse() failed\n");
+
+ /* Modify UDP payload and update checksum */
+ payload = odp_packet_offset(pkt, odp_packet_l4_offset(pkt) +
+ ODPH_UDPHDR_LEN, NULL, NULL);
+ *payload = seq;
+ if (odph_udp_chksum_set(pkt))
+ ODPH_ABORT("odph_udp_chksum_set() failed\n");
+
+ /* Test header is stored in user area */
+ hdr = odp_packet_user_area(pkt);
+ hdr->seq = seq;
+ hdr->group = group;
+ hdr->crc = odp_hash_crc32c(odp_packet_data(pkt), odp_packet_len(pkt),
+ CRC_INIT_VAL);
+}
+
+static inline odp_queue_t work_on_event(odp_event_t event)
+{
+ odp_packet_t pkt;
+ odp_packet_parse_param_t param;
+ odph_udphdr_t *udp_hdr;
+ test_hdr_t *hdr;
+ lookup_entry_t *lookup_entry;
+ odp_una_u32_t *payload;
+ uint32_t crc;
+ uint32_t pkt_len;
+ uint8_t *data;
+ uint32_t new_val;
+ uint32_t old_val;
+
+ if (odp_event_type(event) != ODP_EVENT_PACKET)
+ return ODP_QUEUE_INVALID;
+
+ pkt = odp_packet_from_event(event);
+ hdr = odp_packet_user_area(pkt);
+ pkt_len = odp_packet_len(pkt);
+ data = odp_packet_data(pkt);
+
+ crc = odp_hash_crc32c(data, pkt_len, CRC_INIT_VAL);
+ if (crc != hdr->crc)
+ ODPH_ERR("Error: Invalid packet crc\n");
+
+ param.proto = ODP_PROTO_ETH;
+ param.last_layer = ODP_PROTO_LAYER_ALL;
+ param.chksums.all_chksum = 1;
+ if (odp_packet_parse(pkt, 0, &param)) {
+ ODPH_ERR("Error: odp_packet_parse() failed\n");
+ return ODP_QUEUE_INVALID;
+ }
+
+ /* Modify packet data using lookup table value and sequence number, and
+ * update UDP checksum accordingly. */
+ lookup_entry = &gbl_args->lookup_tbl[(crc + hdr->seq) %
+ gbl_args->appl.lookup_tbl_size];
+ udp_hdr = odp_packet_l4_ptr(pkt, NULL);
+ payload = odp_packet_offset(pkt, odp_packet_l4_offset(pkt) +
+ ODPH_UDPHDR_LEN, NULL, NULL);
+ old_val = *payload;
+ *payload += lookup_entry->idx % 2 ? lookup_entry->val1 :
+ lookup_entry->val0;
+ new_val = *payload;
+ udp_hdr->chksum = ~(~udp_hdr->chksum + (-old_val) + new_val);
+
+ payload++;
+ old_val = *payload;
+ *payload += hdr->seq;
+ new_val = *payload;
+ udp_hdr->chksum = ~(~udp_hdr->chksum + (-old_val) + new_val);
+
+ hdr->crc = odp_hash_crc32c(data, pkt_len, CRC_INIT_VAL);
+
+ return gbl_args->queue[hdr->group][hdr->seq++ % QUEUES_PER_GROUP];
+}
+
+/**
+ * Worker thread
+ */
+static int run_thread(void *arg)
+{
+ thread_args_t *thr_args = arg;
+ stats_t *stats = &thr_args->stats;
+ odp_time_t t1, t2;
+ uint64_t c1, c2;
+
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ c1 = odp_cpu_cycles();
+ t1 = odp_time_local();
+
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ odp_event_t event_tbl[MAX_EVENT_BURST];
+ odp_queue_t dst_queue;
+ int num_events;
+ int i;
+
+ num_events = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
+ event_tbl, MAX_EVENT_BURST);
+ if (num_events <= 0)
+ continue;
+
+ for (i = 0; i < num_events; i++) {
+ odp_event_t event = event_tbl[i];
+
+ dst_queue = work_on_event(event);
+ if (odp_unlikely(dst_queue == ODP_QUEUE_INVALID)) {
+ stats->s.dropped_pkts++;
+ odp_event_free(event);
+ continue;
+ }
+
+ if (odp_unlikely(odp_queue_enq(dst_queue, event))) {
+ ODPH_ERR("Error: odp_queue_enq() failed\n");
+ stats->s.dropped_pkts++;
+ odp_event_free(event);
+ break;
+ }
+
+ stats->s.pkts++;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ stats->s.cycles = c2 - c1;
+ stats->s.nsec = odp_time_diff_ns(t2, t1);
+
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ /* Free remaining events in queues */
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL,
+ odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ return 0;
+}
+
+/*
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane CPU benchmarking application.\n"
+ "\n"
+ "Usage: %s [options]\n"
+ "\n"
+ " E.g. %s -c 4 -t 30\n"
+ "Options:\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -t, --time <sec> Time in seconds to run\n"
+ " (default is 10 second).\n"
+ " -a, --accuracy <sec> Time in seconds get print statistics\n"
+ " (default is 1 second).\n"
+ " -l, --lookup_tbl <num> Number of entries in dummy lookup table\n"
+ " (default is %d).\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname), DEF_LOOKUP_TBL_SIZE);
+}
+
+/**
+ * @internal Parse arguments
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @param args Test arguments
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+
+ static const struct option longopts[] = {
+ {"accuracy", required_argument, NULL, 'a'},
+ {"cpu", required_argument, NULL, 'c'},
+ {"lookup_tbl", required_argument, NULL, 'l'},
+ {"time", required_argument, NULL, 't'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+a:c:l:t:h";
+
+ appl_args->accuracy = 1; /* Get and print pps stats second */
+ appl_args->cpu_count = 1;
+ appl_args->lookup_tbl_size = DEF_LOOKUP_TBL_SIZE;
+ appl_args->time = 10; /* Loop forever if time to run is 0 */
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'a':
+ appl_args->accuracy = atoi(optarg);
+ break;
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ case 'l':
+ appl_args->lookup_tbl_size = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->lookup_tbl_size < 1) {
+ printf("At least one lookup table entry required.\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * Print statistics
+ *
+ * num_workers Number of worker threads
+ * thr_stats Pointers to stats storage
+ * duration Number of seconds to loop
+ */
+static int print_stats(int num_workers, stats_t **thr_stats, int duration,
+ int accuracy)
+{
+ uint64_t pkts;
+ uint64_t dropped;
+ uint64_t pkts_prev = 0;
+ uint64_t nsec = 0;
+ uint64_t cycles = 0;
+ int i;
+ int elapsed = 0;
+ int stats_enabled = 1;
+ int loop_forever = (duration == 0);
+
+ if (accuracy <= 0) {
+ stats_enabled = 0;
+ accuracy = 1;
+ }
+ /* Wait for all threads to be ready*/
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ do {
+ uint64_t pps;
+
+ sleep(accuracy);
+
+ pkts = 0;
+ dropped = 0;
+ for (i = 0; i < num_workers; i++) {
+ pkts += thr_stats[i]->s.pkts;
+ dropped += thr_stats[i]->s.dropped_pkts;
+ }
+
+ pps = (pkts - pkts_prev) / accuracy;
+
+ if (stats_enabled) {
+ printf("%.2f Mpps, ", pps / 1000000.0);
+
+ printf("%" PRIu64 " dropped\n", dropped);
+ }
+
+ pkts_prev = pkts;
+ elapsed += accuracy;
+ } while (!odp_atomic_load_u32(&gbl_args->exit_threads) &&
+ (loop_forever || (elapsed < duration)));
+
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ pkts = 0;
+ dropped = 0;
+ for (i = 0; i < num_workers; i++) {
+ pkts += thr_stats[i]->s.pkts;
+ dropped += thr_stats[i]->s.dropped_pkts;
+ nsec += thr_stats[i]->s.nsec;
+ cycles += thr_stats[i]->s.cycles;
+ }
+
+ printf("\nRESULTS - per thread (Million packets per sec):\n");
+ printf("-----------------------------------------------\n");
+ printf(" avg 1 2 3 4 5 6 7 8 9 10\n");
+ printf("%6.2f ", pkts / (nsec / 1000.0));
+
+ for (i = 0; i < num_workers; i++) {
+ if (i != 0 && (i % 10) == 0)
+ printf("\n ");
+
+ printf("%6.2f ", thr_stats[i]->s.pkts /
+ (thr_stats[i]->s.nsec / 1000.0));
+ }
+ printf("\n\n");
+
+ nsec /= num_workers;
+ printf("RESULTS - total over %i threads:\n", num_workers);
+ printf("----------------------------------\n");
+ printf(" avg packets per sec: %.3f M\n", pkts / (nsec / 1000.0));
+ printf(" avg cycles per packet: %" PRIu64 "\n", cycles / pkts);
+ printf(" dropped packets: %" PRIu64 "\n\n", dropped);
+
+ return pkts > PASS_PACKETS ? 0 : -1;
+}
+
+static void gbl_args_init(args_t *args)
+{
+ memset(args, 0, sizeof(args_t));
+ odp_atomic_init_u32(&args->exit_threads, 0);
+}
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+ stats_t *stats[MAX_WORKERS];
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_WORKERS];
+ odp_cpumask_t cpumask;
+ odp_pool_capability_t pool_capa;
+ odp_pool_t pool;
+ odp_schedule_config_t schedule_config;
+ odp_shm_t shm;
+ odp_shm_t lookup_tbl_shm;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ uint32_t num_pkts;
+ uint32_t num_groups;
+ uint32_t num_queues;
+ uint32_t pkts_per_group;
+ uint32_t pkt_len;
+ uint32_t init_val;
+ unsigned int num_workers;
+ unsigned int i, j;
+ int ret = 0;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init);
+
+ /* List features not to be used (may optimize performance) */
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Signal handler has to be registered before global init in case ODP
+ * implementation creates internal threads/processes. */
+ signal(SIGINT, sig_handler);
+
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Error: ODP global init failed\n");
+ return -1;
+ }
+
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE,
+ 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ gbl_args_init(gbl_args);
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ lookup_tbl_shm = odp_shm_reserve("lookup_tbl_shm",
+ sizeof(lookup_entry_t) *
+ gbl_args->appl.lookup_tbl_size,
+ ODP_CACHE_LINE_SIZE, 0);
+ if (lookup_tbl_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args->lookup_tbl = odp_shm_addr(lookup_tbl_shm);
+ if (gbl_args->lookup_tbl == NULL) {
+ ODPH_ERR("Error: lookup table mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n");
+ odp_sys_info_print();
+
+ /* Default to system CPU count unless user specified */
+ num_workers = MAX_WORKERS;
+ if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
+ num_workers = gbl_args->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ odp_schedule_config_init(&schedule_config);
+ odp_schedule_config(&schedule_config);
+
+ /* Make sure a single queue can store all the packets in a group */
+ pkts_per_group = QUEUES_PER_GROUP * PKTS_PER_QUEUE;
+ if (schedule_config.queue_size &&
+ schedule_config.queue_size < pkts_per_group)
+ pkts_per_group = schedule_config.queue_size;
+
+ /* Divide queues evenly into groups */
+ if (schedule_config.num_queues < QUEUES_PER_GROUP) {
+ ODPH_ERR("Error: min %d queues required\n", QUEUES_PER_GROUP);
+ return -1;
+ }
+ num_queues = num_workers > schedule_config.num_queues ?
+ schedule_config.num_queues : num_workers;
+ num_groups = (num_queues + QUEUES_PER_GROUP - 1) / QUEUES_PER_GROUP;
+ if (num_groups * QUEUES_PER_GROUP > schedule_config.num_queues)
+ num_groups--;
+ num_queues = num_groups * QUEUES_PER_GROUP;
+
+ for (i = 0; i < num_groups; i++) {
+ for (j = 0; j < QUEUES_PER_GROUP; j++) {
+ odp_queue_t queue;
+ odp_queue_param_t param;
+
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_SCHED;
+ param.sched.prio = odp_schedule_default_prio();
+ param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ param.sched.group = ODP_SCHED_GROUP_ALL;
+ param.size = pkts_per_group;
+
+ queue = odp_queue_create(NULL, &param);
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error: odp_queue_create() failed\n");
+ return -1;
+ }
+ gbl_args->queue[i][j] = queue;
+ }
+ }
+
+ /* Create packet pool */
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Error: odp_pool_capability() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ num_pkts = pkts_per_group * num_groups;
+ if (num_pkts > pool_capa.pkt.max_num)
+ num_pkts = pool_capa.pkt.max_num;
+
+ pkt_len = sizeof(test_udp_packet);
+ if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len)
+ pkt_len = pool_capa.pkt.max_len;
+
+ if (pool_capa.pkt.max_seg_len && pkt_len > pool_capa.pkt.max_seg_len)
+ pkt_len = pool_capa.pkt.max_seg_len;
+
+ if (pkt_len < sizeof(test_udp_packet)) {
+ ODPH_ERR("Error: min %dB single segment packets required\n",
+ (int)sizeof(test_udp_packet));
+ exit(EXIT_FAILURE);
+ }
+
+ if (pool_capa.pkt.max_uarea_size &&
+ pool_capa.pkt.max_uarea_size < sizeof(test_hdr_t)) {
+ ODPH_ERR("Error: min %dB of packet user area required\n",
+ (int)sizeof(test_hdr_t));
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.len = pkt_len;
+ params.pkt.max_len = pkt_len;
+ params.pkt.seg_len = pkt_len;
+ params.pkt.num = num_pkts;
+ params.pkt.max_num = num_pkts;
+ params.pkt.uarea_size = sizeof(test_hdr_t);
+ params.type = ODP_POOL_PACKET;
+ pool = odp_pool_create("pkt_pool", &params);
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: packet pool create failed\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_pool_print(pool);
+
+ printf("CPU bench args\n--------------\n");
+ printf(" workers: %u\n", num_workers);
+ printf(" queues: %" PRIu32 "\n", num_queues);
+ printf(" pkts: %" PRIu32 "\n", num_pkts);
+ printf(" pkt size: %" PRIu32 " B\n", pkt_len);
+ printf(" lookup entries: %" PRIu64 "\n\n",
+ gbl_args->appl.lookup_tbl_size);
+
+ /* Spread test packets into queues */
+ for (i = 0; i < num_pkts; i++) {
+ odp_packet_t pkt = odp_packet_alloc(pool, pkt_len);
+ odp_event_t ev;
+ odp_queue_t queue;
+ uint16_t group = i % num_groups;
+
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("Error: odp_packet_alloc() failed\n");
+ return -1;
+ }
+
+ odp_packet_copy_from_mem(pkt, 0, pkt_len, test_udp_packet);
+
+ init_packet(pkt, i, group);
+
+ queue = gbl_args->queue[group][i % QUEUES_PER_GROUP];
+
+ ev = odp_packet_to_event(pkt);
+ if (odp_queue_enq(queue, ev)) {
+ ODPH_ERR("Error: odp_queue_enq() failed\n");
+ return -1;
+ }
+ }
+
+ odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
+ odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
+
+ /* Initialize lookup table */
+ init_val = CRC_INIT_VAL;
+ for (i = 0; i < gbl_args->appl.lookup_tbl_size; i++) {
+ uint32_t *val0 = &gbl_args->lookup_tbl[i].val0;
+ uint32_t *val1 = &gbl_args->lookup_tbl[i].val1;
+
+ gbl_args->lookup_tbl[i].idx = i;
+
+ *val0 = i;
+ *val0 = odp_hash_crc32c(val0, sizeof(uint32_t), init_val);
+ *val1 = odp_hash_crc32c(val0, sizeof(uint32_t), init_val);
+ init_val = *val1;
+ }
+
+ /* Create worker threads */
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+
+ for (i = 0; i < num_workers; i++) {
+ gbl_args->thread[i].idx = i;
+ stats[i] = &gbl_args->thread[i].stats;
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = run_thread;
+ thr_param[i].arg = &gbl_args->thread[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
+
+ ret = print_stats(num_workers, stats, gbl_args->appl.time,
+ gbl_args->appl.accuracy);
+
+ /* Master thread waits for other threads to exit */
+ odph_thread_join(thread_tbl, num_workers);
+
+ for (i = 0; i < num_groups; i++) {
+ for (j = 0; j < QUEUES_PER_GROUP; j++) {
+ if (odp_queue_destroy(gbl_args->queue[i][j])) {
+ ODPH_ERR("Error: queue destroy\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ gbl_args = NULL;
+ odp_mb_full();
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shm free\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(lookup_tbl_shm)) {
+ ODPH_ERR("Error: shm free\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_cpu_bench_run.sh b/test/performance/odp_cpu_bench_run.sh
new file mode 100755
index 000000000..15be2e729
--- /dev/null
+++ b/test/performance/odp_cpu_bench_run.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+# Use short run time in make check
+
+$TEST_DIR/odp_cpu_bench${EXEEXT} -t 1
+
+if [ $? -ne 0 ] ; then
+ echo Test FAILED
+ exit 1
+fi
+
+exit 0
diff --git a/test/performance/odp_crc.c b/test/performance/odp_crc.c
new file mode 100644
index 000000000..1b631c691
--- /dev/null
+++ b/test/performance/odp_crc.c
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_crc.c
+ *
+ * Performance test application for CRC hash APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define KB 1024ull
+#define MB (1024ull * 1024ull)
+
+/* Command line options */
+typedef struct {
+ uint32_t size;
+ uint32_t rounds;
+ uint32_t offset;
+ uint32_t test;
+} options_t;
+
+static options_t options;
+static const options_t options_def = {
+ .size = 16,
+ .rounds = 10000,
+ .offset = 0,
+ .test = 0,
+};
+
+static void print_usage(void)
+{
+ printf("\n"
+ "CRC performance test\n"
+ "\n"
+ "Usage: odp_crc_perf [options]\n"
+ "\n"
+ " -s, --size Size of buffer in KB (default %u)\n"
+ " -r, --rounds Number of test rounds (default %u)\n"
+ " Rounded down to nearest multiple of 8\n"
+ " -o, --offset Offset of data (default %u)\n"
+ " -t, --test Which API to test (default %u)\n"
+ " 0: both\n"
+ " 1: odp_hash_crc32c\n"
+ " 2: odp_hash_crc32\n"
+ " -h, --help This help\n"
+ "\n",
+ options_def.size, options_def.rounds, options_def.offset,
+ options.test);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ { "size", required_argument, NULL, 's' },
+ { "rounds", required_argument, NULL, 'r' },
+ { "offset", required_argument, NULL, 'o' },
+ { "test", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+s:r:o:t:h";
+
+ options = options_def;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 's':
+ options.size = atol(optarg);
+ break;
+ case 'r':
+ options.rounds = atol(optarg);
+ break;
+ case 'o':
+ options.offset = atol(optarg);
+ break;
+ case 't':
+ options.test = atol(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (options.size < 1) {
+ ODPH_ERR("Invalid size: %" PRIu32 "\n", options.size);
+ return -1;
+ }
+
+ if (options.offset > 4 * KB) {
+ ODPH_ERR("Invalid offset: %" PRIu32 "\n", options.offset);
+ return -1;
+ }
+
+ if (options.test > 2) {
+ ODPH_ERR("Invalid API to test: %" PRIu32 "\n", options.test);
+ return -1;
+ }
+
+ return ret;
+}
+
+static void report(uint64_t nsec)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint32_t rounds = options.rounds & ~7ul;
+ double mb, seconds;
+
+ printf("size: %d KB rounds: %d offset: %d ",
+ options.size, rounds, options.offset);
+ mb = (double)(size * (uint64_t)rounds) / (double)MB;
+ seconds = (double)nsec / (double)ODP_TIME_SEC_IN_NS;
+ printf("MB: %.3f seconds: %.3f ", mb, seconds);
+ printf("MB/s: %.3f", mb / seconds);
+ printf("\n\n");
+}
+
+static uint64_t measure_crc32c(uint8_t *data, uint32_t size)
+{
+ void *p = data + options.offset;
+ uint32_t crc = 1;
+ volatile uint32_t v;
+ odp_time_t start = odp_time_local();
+
+ for (uint32_t i = 0; i < options.rounds / 8; i++) {
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ }
+
+ /* Make sure that crc is not optimized out. */
+ v = crc;
+
+ /* Quell "unused" warning. */
+ (void)v;
+
+ return odp_time_diff_ns(odp_time_local(), start);
+}
+
+static void test_odp_hash_crc32c(uint8_t *data)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint64_t nsec;
+
+ /* Warm-up. */
+ measure_crc32c(data, size);
+
+ /* Actual measurement. */
+ nsec = measure_crc32c(data, size);
+
+ report(nsec);
+}
+
+static uint64_t measure_crc32(uint8_t *data, uint32_t size)
+{
+ void *p = data + options.offset;
+ uint32_t crc = 1;
+ volatile uint32_t v;
+ odp_time_t start = odp_time_local();
+
+ for (uint32_t i = 0; i < options.rounds / 8; i++) {
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ }
+
+ /* Make sure that crc is not optimized out. */
+ v = crc;
+
+ /* Quell "unused" warning. */
+ (void)v;
+
+ return odp_time_diff_ns(odp_time_local(), start);
+}
+
+static void test_odp_hash_crc32(uint8_t *data)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint64_t nsec;
+
+ /* Warm-up. */
+ measure_crc32(data, size);
+
+ /* Actual measurement. */
+ nsec = measure_crc32(data, size);
+
+ report(nsec);
+}
+
+int main(int argc, char **argv)
+{
+ odp_instance_t instance;
+ odp_init_t init;
+
+ if (parse_options(argc, argv))
+ exit(EXIT_FAILURE);
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.stash = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_sys_info_print();
+
+ uint8_t *buf, *data;
+ uint32_t size = options.size * KB;
+ uint64_t seed = 1;
+ const unsigned long page = 4 * KB;
+
+ /* One extra page for alignment, another one for offset. */
+ buf = (uint8_t *)malloc(size + page * 2);
+
+ if (!buf) {
+ ODPH_ERR("Memory allocation failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Align to start of page. */
+ data = (uint8_t *)(((uintptr_t)buf + (page - 1)) & ~(page - 1));
+
+ if (odp_random_test_data(data, size, &seed) != (int32_t)size) {
+ ODPH_ERR("odp_random_test_data() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (options.test == 0 || options.test == 1) {
+ printf("odp_hash_crc32c\n"
+ "---------------\n");
+ test_odp_hash_crc32c(data);
+ }
+
+ if (options.test == 0 || options.test == 2) {
+ printf("odp_hash_crc32\n"
+ "--------------\n");
+ test_odp_hash_crc32(data);
+ }
+
+ free(buf);
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_crypto.c b/test/performance/odp_crypto.c
new file mode 100644
index 000000000..380e798c9
--- /dev/null
+++ b/test/performance/odp_crypto.c
@@ -0,0 +1,1524 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @example odp_crypto.c
+ *
+ * Performance test application for crypto APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/** @def POOL_NUM_PKT
+ * Number of packets in the pool
+ */
+#define POOL_NUM_PKT 64
+
+#define AAD_LEN 8 /* typical AAD length used in IPsec when ESN is not in use */
+#define MAX_AUTH_DIGEST_LEN 32 /* maximum MAC length in bytes */
+
+static uint8_t test_aad[AAD_LEN] = "01234567";
+static uint8_t test_iv[16] = "0123456789abcdef";
+
+static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10,
+};
+
+static uint8_t test_key20[20] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+};
+
+static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18
+};
+
+static uint8_t test_key32[32] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20,
+};
+
+static uint8_t test_key64[64] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x4b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x55, 0x36, 0x37,
+ 0x38, 0x39, 0x5a, 0x3b, 0x3c,
+ 0x3d, 0x3e, 0x5f, 0x40,
+};
+
+/**
+ * Structure that holds template for session create call
+ * for different algorithms supported by test
+ */
+typedef struct {
+ const char *name; /**< Algorithm name */
+ odp_crypto_session_param_t session; /**< Prefilled crypto session params */
+ int cipher_in_bit_mode; /**< Cipher range in bits, probed at run time */
+ int auth_in_bit_mode; /**< Auth range in bits, probed at run time */
+} crypto_alg_config_t;
+
+/**
+ * Parsed command line crypto arguments. Describes test configuration.
+ */
+typedef struct {
+ /**
+ * If non-zero, prints content of packets. Enabled by -d or
+ * --debug option.
+ */
+ int debug_packets;
+
+ /**
+ * If non-zero, try to run crypto operation in place. Note some
+ * implementation may not support such mode. Enabled by -n or
+ * --inplace option.
+ */
+ int in_place;
+
+ /**
+ * If non-zero, output of previous operation taken as input for
+ * next encrypt operations. Enabled by -r or --reuse option.
+ */
+ int reuse_packet;
+
+ /**
+ * Maximum number of outstanding encryption requests. Note code
+ * poll for results over queue and if nothing is available it can
+ * submit more encryption requests up to maximum number specified by
+ * this option. Specified through -f or --flight option.
+ */
+ int in_flight;
+
+ /**
+ * Number of iteration to repeat crypto operation to get good
+ * average number. Specified through -i or --iterations option.
+ * Default is 10000.
+ */
+ int iteration_count;
+
+ /**
+ * Maximum sessions. Currently is not used.
+ */
+ int max_sessions;
+
+ /**
+ * Payload size to test. If 0 set of predefined payload sizes
+ * is tested. Specified through -p or --payload option.
+ */
+ int payload_length;
+
+ /**
+ * Pointer to selected algorithm to test. If NULL all available
+ * algorithms are tested. Name of algorithm is passed through
+ * -a or --algorithm option.
+ */
+ crypto_alg_config_t *alg_config;
+
+ /**
+ * Use scheduler to get completion events from crypto operation.
+ * Specified through -s argument.
+ * */
+ int schedule;
+
+ /*
+ * Poll completion queue for crypto completion events.
+ * Specified through -p argument.
+ */
+ int poll;
+} crypto_args_t;
+
+/*
+ * Helper structure that holds averages for test of one algorithm
+ * for given payload size.
+ */
+typedef struct {
+ /**
+ * Elapsed time for one crypto operation.
+ */
+ double elapsed;
+
+ /**
+ * CPU time spent pre one crypto operation by whole process
+ * i.e include current and all other threads in process.
+ * It is filled with 'getrusage(RUSAGE_SELF, ...)' call.
+ */
+ double rusage_self;
+
+ /**
+ * CPU time spent per one crypto operation by current thread
+ * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)'
+ * call.
+ */
+ double rusage_thread;
+} crypto_run_result_t;
+
+/**
+ * Structure holds one snap to misc times of current process.
+ */
+typedef struct {
+ struct timeval tv; /**< Elapsed time */
+ struct rusage ru_self; /**< Rusage value for whole process */
+ struct rusage ru_thread; /**< Rusage value for current thread */
+} time_record_t;
+
+/* Arguments for one test run */
+typedef struct test_run_arg_t {
+ crypto_args_t crypto_args;
+ crypto_alg_config_t *crypto_alg_config;
+ odp_crypto_capability_t crypto_capa;
+
+} test_run_arg_t;
+
+static void parse_args(int argc, char *argv[], crypto_args_t *cargs);
+static void usage(char *progname);
+
+/**
+ * Set of predefined payloads.
+ */
+static unsigned int payloads[] = {
+ 16,
+ 64,
+ 256,
+ 1024,
+ 8192,
+ 16384
+};
+
+/** Number of payloads used in the test */
+static unsigned num_payloads;
+
+/**
+ * Set of known algorithms to test
+ */
+static crypto_alg_config_t algs_config[] = {
+ {
+ .name = "3des-cbc-null",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key = {
+ .data = test_key24,
+ .length = sizeof(test_key24)
+ },
+ .cipher_iv_len = 8,
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "3des-cbc-hmac-md5-96",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key = {
+ .data = test_key24,
+ .length = sizeof(test_key24)
+ },
+ .cipher_iv_len = 8,
+ .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_digest_len = 12,
+ },
+ },
+ {
+ .name = "null-hmac-md5-96",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_digest_len = 12,
+ },
+ },
+ {
+ .name = "aes-cbc-null",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "aes-cbc-hmac-sha1-96",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ .auth_digest_len = 12,
+ },
+ },
+ {
+ .name = "null-hmac-sha1-96",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ .auth_digest_len = 12,
+ },
+ },
+ {
+ .name = "aes-ctr-null",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "aes-ctr-hmac-sha1-96",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ .auth_digest_len = 12,
+ },
+ },
+ {
+ .name = "null-hmac-sha256-128",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA256_HMAC,
+ .auth_key = {
+ .data = test_key32,
+ .length = sizeof(test_key32)
+ },
+ .auth_digest_len = 16,
+ },
+ },
+ {
+ .name = "null-hmac-sha512-256",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA512_HMAC,
+ .auth_key = {
+ .data = test_key64,
+ .length = sizeof(test_key64)
+ },
+ .auth_digest_len = 32,
+ },
+ },
+ {
+ .name = "null-aes-gmac",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_AES_GMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_iv_len = 12,
+ .auth_digest_len = 16,
+ },
+ },
+ {
+ .name = "aes-gcm",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_GCM,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 12,
+ .auth_alg = ODP_AUTH_ALG_AES_GCM,
+ .auth_digest_len = 16,
+ .auth_aad_len = AAD_LEN,
+ },
+ },
+ {
+ .name = "aes-ccm",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CCM,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 11,
+ .auth_alg = ODP_AUTH_ALG_AES_CCM,
+ .auth_digest_len = 16,
+ .auth_aad_len = AAD_LEN,
+ },
+ },
+ {
+ .name = "chacha20-poly1305",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_CHACHA20_POLY1305,
+ .cipher_key = {
+ .data = test_key32,
+ .length = sizeof(test_key32)
+ },
+ .cipher_iv_len = 12,
+ .auth_alg = ODP_AUTH_ALG_CHACHA20_POLY1305,
+ .auth_digest_len = 16,
+ .auth_aad_len = AAD_LEN,
+ },
+ },
+ {
+ .name = "zuc-eea3",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_ZUC_EEA3,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_NULL,
+ },
+ },
+ {
+ .name = "zuc-eia3",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_ZUC_EIA3,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_iv_len = 16,
+ .auth_digest_len = 4,
+ },
+ },
+ {
+ .name = "zuc-eea3-zuc-eia3",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_ZUC_EEA3,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_ZUC_EIA3,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_iv_len = 16,
+ .auth_digest_len = 4,
+ },
+ },
+ {
+ .name = "snow3g-uea2",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_SNOW3G_UEA2,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_NULL,
+ },
+ },
+ {
+ .name = "snow3g-uia2",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SNOW3G_UIA2,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_iv_len = 16,
+ .auth_digest_len = 4,
+ },
+ },
+ {
+ .name = "snow3g-uea2-snow3g-uia2",
+ .session = {
+ .cipher_alg = ODP_CIPHER_ALG_SNOW3G_UEA2,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_iv_len = 16,
+ .auth_alg = ODP_AUTH_ALG_SNOW3G_UIA2,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_iv_len = 16,
+ .auth_digest_len = 4,
+ },
+ },
+};
+
+/**
+ * Find corresponding config for given name. Returns NULL
+ * if config for given name is not found.
+ */
+static crypto_alg_config_t *
+find_config_by_name(const char *name) {
+ unsigned int i;
+ crypto_alg_config_t *ret = NULL;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++) {
+ if (strcmp(algs_config[i].name, name) == 0) {
+ ret = algs_config + i;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Helper function that prints list of algorithms that this
+ * test understands.
+ */
+static void
+print_config_names(const char *prefix) {
+ unsigned int i;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++)
+ printf("%s %s\n", prefix, algs_config[i].name);
+}
+
+/**
+ * Snap current time values and put them into 'rec'.
+ */
+static void
+fill_time_record(time_record_t *rec)
+{
+ gettimeofday(&rec->tv, NULL);
+ getrusage(RUSAGE_SELF, &rec->ru_self);
+ getrusage(RUSAGE_THREAD, &rec->ru_thread);
+}
+
+/**
+ * Calculated CPU time difference for given two rusage structures.
+ * Note it adds user space and system time together.
+ */
+static unsigned long long
+get_rusage_diff(struct rusage *start, struct rusage *end)
+{
+ unsigned long long rusage_diff;
+ unsigned long long rusage_start;
+ unsigned long long rusage_end;
+
+ rusage_start = (start->ru_utime.tv_sec * 1000000) +
+ (start->ru_utime.tv_usec);
+ rusage_start += (start->ru_stime.tv_sec * 1000000) +
+ (start->ru_stime.tv_usec);
+
+ rusage_end = (end->ru_utime.tv_sec * 1000000) +
+ (end->ru_utime.tv_usec);
+ rusage_end += (end->ru_stime.tv_sec * 1000000) +
+ (end->ru_stime.tv_usec);
+
+ rusage_diff = rusage_end - rusage_start;
+
+ return rusage_diff;
+}
+
+/**
+ * Get diff for RUSAGE_SELF (whole process) between two time snap
+ * records.
+ */
+static unsigned long long
+get_rusage_self_diff(time_record_t *start, time_record_t *end)
+{
+ return get_rusage_diff(&start->ru_self, &end->ru_self);
+}
+
+/**
+ * Get diff for RUSAGE_THREAD (current thread only) between two
+ * time snap records.
+ */
+static unsigned long long
+get_rusage_thread_diff(time_record_t *start, time_record_t *end)
+{
+ return get_rusage_diff(&start->ru_thread, &end->ru_thread);
+}
+
+/**
+ * Get diff of elapsed time between two time snap records
+ */
+static unsigned long long
+get_elapsed_usec(time_record_t *start, time_record_t *end)
+{
+ unsigned long long s;
+ unsigned long long e;
+
+ s = (start->tv.tv_sec * 1000000) +
+ (start->tv.tv_usec);
+ e = (end->tv.tv_sec * 1000000) +
+ (end->tv.tv_usec);
+
+ return e - s;
+}
+
+#define REPORT_HEADER "%30.30s %15s %15s %15s %15s %15s %15s\n"
+#define REPORT_LINE "%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n"
+
+/**
+ * Print header line for our report.
+ */
+static void
+print_result_header(void)
+{
+ printf(REPORT_HEADER,
+ "algorithm", "avg over #", "payload (bytes)", "elapsed (us)",
+ "rusg self (us)", "rusg thrd (us)", "throughput (Kb)");
+}
+
+/**
+ * Print one line of our report.
+ */
+static void
+print_result(crypto_args_t *cargs,
+ unsigned int payload_length,
+ crypto_alg_config_t *config,
+ crypto_run_result_t *result)
+{
+ unsigned int throughput;
+
+ throughput = (1000000.0 / result->elapsed) * payload_length / 1024;
+ printf(REPORT_LINE,
+ config->name, cargs->iteration_count, payload_length,
+ result->elapsed, result->rusage_self, result->rusage_thread,
+ throughput);
+}
+
+/**
+ * Print piece of memory with given size.
+ */
+static void
+print_mem(const char *msg,
+ const unsigned char *ptr,
+ unsigned int len)
+{
+ unsigned i, j;
+ char c;
+ char line[81];
+ char *p;
+
+ if (msg)
+ printf("\n%s (bytes size = %d)", msg, len);
+
+ for (i = 0; i < len; i += 16) {
+ p = line;
+ sprintf(p, "\n%04x ", i); p += 8;
+
+ for (j = 0; j < 16; j++) {
+ if (i + j == len)
+ break;
+
+ sprintf(p, " %02x", (ptr)[i + j]); p += 3;
+ }
+
+ for (; j < 16; j++) {
+ sprintf(p, " "); p += 3;
+ }
+
+ sprintf(p, " "); p += 3;
+
+ for (j = 0; j < 16; j++) {
+ if (i + j == len)
+ break;
+ c = (ptr)[i + j];
+ *p++ = (' ' <= c && c <= '~') ? c : '.';
+ }
+
+ *p = '\0';
+ printf("%s", line);
+ }
+ printf("\n");
+}
+
+/**
+ * Create ODP crypto session for given config.
+ */
+static int
+create_session_from_config(odp_crypto_session_t *session,
+ crypto_alg_config_t *config,
+ crypto_args_t *cargs)
+{
+ odp_crypto_session_param_t params;
+ odp_crypto_ses_create_err_t ses_create_rc;
+ odp_pool_t pkt_pool;
+ odp_queue_t out_queue;
+
+ odp_crypto_session_param_init(&params);
+ memcpy(&params, &config->session, sizeof(odp_crypto_session_param_t));
+ params.op = ODP_CRYPTO_OP_ENCODE;
+ params.auth_cipher_text = true;
+
+ /* Lookup the packet pool */
+ pkt_pool = odp_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("packet_pool pool not found\n");
+ return -1;
+ }
+ params.output_pool = pkt_pool;
+
+ if (cargs->schedule || cargs->poll) {
+ out_queue = odp_queue_lookup("crypto-out");
+ if (out_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("crypto-out queue not found\n");
+ return -1;
+ }
+ params.compl_queue = out_queue;
+ params.op_mode = ODP_CRYPTO_ASYNC;
+ } else {
+ params.compl_queue = ODP_QUEUE_INVALID;
+ params.op_mode = ODP_CRYPTO_SYNC;
+ }
+ if (odp_crypto_session_create(&params, session,
+ &ses_create_rc)) {
+ switch (ses_create_rc) {
+ case ODP_CRYPTO_SES_ERR_ALG_COMBO:
+ printf(" requested algorithm combination not supported\n");
+ return 1;
+ case ODP_CRYPTO_SES_ERR_ALG_ORDER:
+ printf(" requested algorithm order not supported\n");
+ return 1;
+ case ODP_CRYPTO_SES_ERR_PARAMS:
+ printf(" requested session parameters not supported\n");
+ return 1;
+ default:
+ break;
+ }
+ ODPH_ERR("crypto session create failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static odp_packet_t
+make_packet(odp_pool_t pkt_pool, unsigned int payload_length)
+{
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(pkt_pool, payload_length);
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("failed to allocate buffer\n");
+ return pkt;
+ }
+
+ void *mem = odp_packet_data(pkt);
+
+ memset(mem, 1, payload_length);
+
+ return pkt;
+}
+
+/**
+ * Run measurement iterations for given config and payload size.
+ * Result of run returned in 'result' out parameter.
+ */
+static int
+run_measure_one(crypto_args_t *cargs,
+ crypto_alg_config_t *config,
+ odp_crypto_session_t *session,
+ unsigned int payload_length,
+ crypto_run_result_t *result)
+{
+ odp_crypto_packet_op_param_t params;
+
+ odp_pool_t pkt_pool;
+ odp_queue_t out_queue;
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+ int rc = 0;
+ uint32_t packet_len = payload_length + MAX_AUTH_DIGEST_LEN;
+
+ pkt_pool = odp_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("pkt_pool not found\n");
+ return -1;
+ }
+
+ out_queue = odp_queue_lookup("crypto-out");
+ if (cargs->schedule || cargs->poll) {
+ if (out_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("crypto-out queue not found\n");
+ return -1;
+ }
+ }
+
+ if (cargs->reuse_packet) {
+ pkt = make_packet(pkt_pool, packet_len);
+ if (ODP_PACKET_INVALID == pkt)
+ return -1;
+ }
+
+ time_record_t start, end;
+ int packets_sent = 0;
+ int packets_received = 0;
+
+ /* Initialize parameters block */
+ memset(&params, 0, sizeof(params));
+ params.session = *session;
+ params.cipher_iv_ptr = test_iv;
+ params.auth_iv_ptr = test_iv;
+ params.aad_ptr = test_aad;
+
+ params.cipher_range.offset = 0;
+ params.cipher_range.length = config->cipher_in_bit_mode ? payload_length * 8
+ : payload_length;
+ params.auth_range.offset = 0;
+ params.auth_range.length = config->auth_in_bit_mode ? payload_length * 8
+ : payload_length;
+ params.hash_result_offset = payload_length;
+
+ fill_time_record(&start);
+
+ while ((packets_sent < cargs->iteration_count) ||
+ (packets_received < cargs->iteration_count)) {
+ void *mem;
+
+ if ((packets_sent < cargs->iteration_count) &&
+ (packets_sent - packets_received <
+ cargs->in_flight)) {
+ odp_packet_t out_pkt;
+
+ if (!cargs->reuse_packet) {
+ pkt = make_packet(pkt_pool, packet_len);
+ if (ODP_PACKET_INVALID == pkt)
+ return -1;
+ }
+
+ out_pkt = cargs->in_place ? pkt : ODP_PACKET_INVALID;
+
+ if (cargs->debug_packets) {
+ mem = odp_packet_data(pkt);
+ print_mem("Packet before encryption:",
+ mem, payload_length);
+ }
+
+ if (cargs->schedule || cargs->poll) {
+ rc = odp_crypto_op_enq(&pkt, &out_pkt,
+ &params, 1);
+ if (rc <= 0) {
+ ODPH_ERR("failed odp_crypto_packet_op_enq: rc = %d\n", rc);
+ if (!cargs->reuse_packet)
+ odp_packet_free(pkt);
+ break;
+ }
+ packets_sent += rc;
+ } else {
+ rc = odp_crypto_op(&pkt, &out_pkt,
+ &params, 1);
+ if (rc <= 0) {
+ ODPH_ERR("failed odp_crypto_packet_op: rc = %d\n", rc);
+ if (!cargs->reuse_packet)
+ odp_packet_free(pkt);
+ break;
+ }
+ packets_sent += rc;
+ packets_received++;
+ if (odp_unlikely(odp_crypto_result(NULL, out_pkt) != 0)) {
+ ODPH_ERR("Crypto operation failed\n");
+ odp_packet_free(out_pkt);
+ return -1;
+ }
+ if (cargs->debug_packets) {
+ mem = odp_packet_data(out_pkt);
+ print_mem("Immediately encrypted "
+ "packet",
+ mem,
+ payload_length +
+ config->session.
+ auth_digest_len);
+ }
+ if (cargs->reuse_packet)
+ pkt = out_pkt;
+ else
+ odp_packet_free(out_pkt);
+ }
+ }
+
+ if (cargs->schedule || cargs->poll) {
+ odp_event_t ev;
+ odp_packet_t out_pkt;
+
+ if (cargs->schedule)
+ ev = odp_schedule(NULL,
+ ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(out_queue);
+
+ while (ev != ODP_EVENT_INVALID) {
+ out_pkt = odp_crypto_packet_from_event(ev);
+ if (odp_unlikely(odp_crypto_result(NULL, out_pkt) != 0)) {
+ ODPH_ERR("Crypto operation failed\n");
+ odp_packet_free(out_pkt);
+ return -1;
+ }
+ if (cargs->debug_packets) {
+ mem = odp_packet_data(out_pkt);
+ print_mem("Received encrypted packet",
+ mem,
+ payload_length +
+ config->
+ session.auth_digest_len);
+ }
+ if (cargs->reuse_packet)
+ pkt = out_pkt;
+ else
+ odp_packet_free(out_pkt);
+ packets_received++;
+ if (cargs->schedule)
+ ev = odp_schedule(NULL,
+ ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(out_queue);
+ };
+ }
+ }
+
+ fill_time_record(&end);
+
+ {
+ double count;
+
+ count = get_elapsed_usec(&start, &end);
+ result->elapsed = count /
+ cargs->iteration_count;
+
+ count = get_rusage_self_diff(&start, &end);
+ result->rusage_self = count /
+ cargs->iteration_count;
+
+ count = get_rusage_thread_diff(&start, &end);
+ result->rusage_thread = count /
+ cargs->iteration_count;
+ }
+
+ if (cargs->reuse_packet)
+ odp_packet_free(pkt);
+
+ return rc < 0 ? rc : 0;
+}
+
+static int check_cipher_alg(const odp_crypto_capability_t *capa,
+ odp_cipher_alg_t alg)
+{
+ switch (alg) {
+ case ODP_CIPHER_ALG_NULL:
+ if (capa->ciphers.bit.null)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_DES:
+ if (capa->ciphers.bit.des)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ if (capa->ciphers.bit.trides_cbc)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ if (capa->ciphers.bit.aes_cbc)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ if (capa->ciphers.bit.aes_ctr)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ if (capa->ciphers.bit.aes_gcm)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ if (capa->ciphers.bit.aes_ccm)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ if (capa->ciphers.bit.chacha20_poly1305)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ if (capa->ciphers.bit.zuc_eea3)
+ return 0;
+ break;
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ if (capa->ciphers.bit.snow3g_uea2)
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static int check_auth_alg(const odp_crypto_capability_t *capa,
+ odp_auth_alg_t alg)
+{
+ switch (alg) {
+ case ODP_AUTH_ALG_NULL:
+ if (capa->auths.bit.null)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ if (capa->auths.bit.md5_hmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ if (capa->auths.bit.sha1_hmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ if (capa->auths.bit.sha256_hmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ if (capa->auths.bit.sha384_hmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ if (capa->auths.bit.sha512_hmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ if (capa->auths.bit.aes_gcm)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ if (capa->auths.bit.aes_gmac)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ if (capa->auths.bit.aes_ccm)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ if (capa->auths.bit.chacha20_poly1305)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ if (capa->auths.bit.zuc_eia3)
+ return 0;
+ break;
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ if (capa->auths.bit.snow3g_uia2)
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static int check_cipher_params(const odp_crypto_capability_t *crypto_capa,
+ const odp_crypto_session_param_t *param,
+ int *bit_mode)
+{
+ int num, rc;
+
+ if (check_cipher_alg(crypto_capa, param->cipher_alg))
+ return 1;
+
+ num = odp_crypto_cipher_capability(param->cipher_alg, NULL, 0);
+ if (num <= 0)
+ return 1;
+
+ odp_crypto_cipher_capability_t cipher_capa[num];
+
+ rc = odp_crypto_cipher_capability(param->cipher_alg, cipher_capa, num);
+ if (rc < num)
+ num = rc;
+
+ for (int n = 0; n < num; n++) {
+ odp_crypto_cipher_capability_t *capa = &cipher_capa[n];
+
+ if (capa->key_len != param->cipher_key.length ||
+ capa->iv_len != param->cipher_iv_len)
+ continue;
+
+ *bit_mode = capa->bit_mode;
+ return 0;
+ }
+ return 1;
+}
+
+static int aad_len_ok(const odp_crypto_auth_capability_t *capa, uint32_t len)
+{
+ if (len < capa->aad_len.min || len > capa->aad_len.max)
+ return 0;
+
+ if (len == capa->aad_len.min)
+ return 1;
+ if (capa->aad_len.inc == 0)
+ return 0;
+
+ return ((len - capa->aad_len.min) % capa->aad_len.inc) == 0;
+}
+
+static int check_auth_params(const odp_crypto_capability_t *crypto_capa,
+ const odp_crypto_session_param_t *param,
+ int *bit_mode)
+{
+ int num, rc;
+
+ if (param->auth_digest_len > MAX_AUTH_DIGEST_LEN) {
+ ODPH_ERR("MAX_AUTH_DIGEST_LEN too low\n");
+ return 1;
+ }
+
+ if (check_auth_alg(crypto_capa, param->auth_alg))
+ return 1;
+
+ num = odp_crypto_auth_capability(param->auth_alg, NULL, 0);
+ if (num <= 0)
+ return 1;
+
+ odp_crypto_auth_capability_t auth_capa[num];
+
+ rc = odp_crypto_auth_capability(param->auth_alg, auth_capa, num);
+ if (rc < num)
+ num = rc;
+
+ for (int n = 0; n < num; n++) {
+ odp_crypto_auth_capability_t *capa = &auth_capa[n];
+
+ if (capa->digest_len != param->auth_digest_len ||
+ capa->key_len != param->auth_key.length ||
+ capa->iv_len != param->auth_iv_len)
+ continue;
+
+ if (!aad_len_ok(capa, param->auth_aad_len))
+ continue;
+
+ *bit_mode = capa->bit_mode;
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Process one algorithm. Note if payload size is specified it is
+ * only one run. Or iterate over set of predefined payloads.
+ */
+static int run_measure_one_config(test_run_arg_t *arg)
+{
+ crypto_run_result_t result;
+ odp_crypto_session_t session;
+ crypto_args_t *cargs = &arg->crypto_args;
+ crypto_alg_config_t *config = arg->crypto_alg_config;
+ odp_crypto_capability_t crypto_capa = arg->crypto_capa;
+ int rc = 0;
+
+ printf("\n");
+
+ if (check_cipher_params(&crypto_capa, &config->session,
+ &config->cipher_in_bit_mode)) {
+ printf(" Cipher algorithm not supported\n");
+ rc = 1;
+ }
+
+ if (check_auth_params(&crypto_capa, &config->session,
+ &config->auth_in_bit_mode)) {
+ printf(" Auth algorithm not supported\n");
+ rc = 1;
+ }
+
+#if ODP_VERSION_API >= ODP_VERSION_API_NUM(1, 42, 0)
+ /* Bit mode ciphers can now be used in byte mode. */
+ config->cipher_in_bit_mode = 0;
+ config->auth_in_bit_mode = 0;
+#endif
+
+ if (rc == 0)
+ rc = create_session_from_config(&session, config, cargs);
+ if (rc) {
+ printf(" => %s skipped\n", config->name);
+ return rc > 0 ? 0 : -1;
+ }
+
+ if (cargs->payload_length) {
+ rc = run_measure_one(cargs, config, &session,
+ cargs->payload_length, &result);
+ if (!rc) {
+ print_result_header();
+ print_result(cargs, cargs->payload_length,
+ config, &result);
+ }
+ } else {
+ unsigned i;
+
+ print_result_header();
+ for (i = 0; i < num_payloads; i++) {
+ rc = run_measure_one(cargs, config, &session,
+ payloads[i], &result);
+ if (rc)
+ break;
+ print_result(cargs, payloads[i],
+ config, &result);
+ }
+ }
+
+ odp_crypto_session_destroy(session);
+
+ return rc;
+}
+
+static int run_thr_func(void *arg)
+{
+ run_measure_one_config((test_run_arg_t *)arg);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ crypto_args_t cargs;
+ odp_pool_t pool;
+ odp_queue_param_t qparam;
+ odp_pool_param_t params;
+ odp_queue_t out_queue = ODP_QUEUE_INVALID;
+ test_run_arg_t test_run_arg;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ int num_workers = 1;
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[num_workers];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odp_pool_capability_t pool_capa;
+ odp_crypto_capability_t crypto_capa;
+ uint32_t max_seg_len;
+ uint32_t i;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ memset(&cargs, 0, sizeof(cargs));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &cargs);
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ odp_init_local(instance, ODP_THREAD_WORKER);
+
+ odp_sys_info_print();
+ memset(&crypto_capa, 0, sizeof(crypto_capa));
+
+ if (odp_crypto_capability(&crypto_capa)) {
+ ODPH_ERR("Crypto capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.schedule && crypto_capa.queue_type_sched == 0) {
+ ODPH_ERR("scheduled type completion queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.poll && crypto_capa.queue_type_plain == 0) {
+ ODPH_ERR("plain type completion queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ max_seg_len = pool_capa.pkt.max_seg_len;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(payloads); i++) {
+ if (payloads[i] + MAX_AUTH_DIGEST_LEN > max_seg_len)
+ break;
+ }
+
+ num_payloads = i;
+
+ /* Create packet pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = max_seg_len;
+ params.pkt.len = max_seg_len;
+ params.pkt.num = POOL_NUM_PKT;
+ params.type = ODP_POOL_PACKET;
+ pool = odp_pool_create("packet_pool", &params);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_pool_print(pool);
+
+ odp_queue_param_init(&qparam);
+ if (cargs.schedule) {
+ odp_schedule_config(NULL);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ out_queue = odp_queue_create("crypto-out", &qparam);
+ } else if (cargs.poll) {
+ qparam.type = ODP_QUEUE_TYPE_PLAIN;
+ out_queue = odp_queue_create("crypto-out", &qparam);
+ }
+ if (cargs.schedule || cargs.poll) {
+ if (out_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("crypto-out queue create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (cargs.schedule) {
+ printf("Run in async scheduled mode\n");
+ num_workers = odp_cpumask_default_worker(&cpumask,
+ num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
+ sizeof(cpumaskstr));
+ printf("num worker threads: %i\n",
+ num_workers);
+ printf("first CPU: %i\n",
+ odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n",
+ cpumaskstr);
+ } else if (cargs.poll) {
+ printf("Run in async poll mode\n");
+ } else {
+ printf("Run in sync mode\n");
+ }
+
+ test_run_arg.crypto_args = cargs;
+ test_run_arg.crypto_alg_config = cargs.alg_config;
+ test_run_arg.crypto_capa = crypto_capa;
+
+ if (cargs.alg_config) {
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ if (cargs.schedule) {
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_thr_func;
+ thr_param.arg = &test_run_arg;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ odph_thread_join(thread_tbl, num_workers);
+ } else {
+ run_measure_one_config(&test_run_arg);
+ }
+ } else {
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++) {
+ test_run_arg.crypto_alg_config = algs_config + i;
+ run_measure_one_config(&test_run_arg);
+ }
+ }
+
+ if (cargs.schedule || cargs.poll)
+ odp_queue_destroy(out_queue);
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+static void parse_args(int argc, char *argv[], crypto_args_t *cargs)
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"algorithm", optional_argument, NULL, 'a'},
+ {"debug", no_argument, NULL, 'd'},
+ {"flight", optional_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"iterations", optional_argument, NULL, 'i'},
+ {"inplace", no_argument, NULL, 'n'},
+ {"payload", optional_argument, NULL, 'l'},
+ {"sessions", optional_argument, NULL, 'm'},
+ {"reuse", no_argument, NULL, 'r'},
+ {"poll", no_argument, NULL, 'p'},
+ {"schedule", no_argument, NULL, 's'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+a:c:df:hi:m:nl:spr";
+
+ cargs->in_place = 0;
+ cargs->in_flight = 1;
+ cargs->debug_packets = 0;
+ cargs->iteration_count = 10000;
+ cargs->payload_length = 0;
+ cargs->alg_config = NULL;
+ cargs->reuse_packet = 0;
+ cargs->schedule = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'a':
+ cargs->alg_config = find_config_by_name(optarg);
+ if (!cargs->alg_config) {
+ printf("cannot test crypto '%s' configuration\n",
+ optarg);
+ usage(argv[0]);
+ exit(-1);
+ }
+ break;
+ case 'd':
+ cargs->debug_packets = 1;
+ break;
+ case 'i':
+ cargs->iteration_count = atoi(optarg);
+ break;
+ case 'f':
+ cargs->in_flight = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'm':
+ cargs->max_sessions = atoi(optarg);
+ break;
+ case 'n':
+ cargs->in_place = 1;
+ break;
+ case 'l':
+ cargs->payload_length = atoi(optarg);
+ break;
+ case 'r':
+ cargs->reuse_packet = 1;
+ break;
+ case 's':
+ cargs->schedule = 1;
+ break;
+ case 'p':
+ cargs->poll = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+
+ if ((cargs->in_flight > 1) && cargs->reuse_packet) {
+ printf("-f (in flight > 1) and -r (reuse packet) options are not compatible\n");
+ usage(argv[0]);
+ exit(-1);
+ }
+ if (cargs->schedule && cargs->poll) {
+ printf("-s (schedule) and -p (poll) options are not compatible\n");
+ usage(argv[0]);
+ exit(-1);
+ }
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i 100000\n"
+ "\n"
+ "OpenDataPlane crypto speed measure.\n"
+ "Optional OPTIONS\n"
+ " -a, --algorithm <name> Specify algorithm name (default all)\n"
+ " Supported values are:\n",
+ progname, progname);
+
+ print_config_names(" ");
+ printf(" -d, --debug Enable dump of processed packets.\n"
+ " -f, --flight <number> Max number of packet processed in parallel (default 1)\n"
+ " -i, --iterations <number> Number of iterations.\n"
+ " -n, --inplace Encrypt on place.\n"
+ " -l, --payload Payload length.\n"
+ " -r, --reuse Output encrypted packet is passed as input\n"
+ " to next encrypt iteration.\n"
+ " -s, --schedule Use scheduler for completion events.\n"
+ " -p, --poll Poll completion queue for completion events.\n"
+ " -h, --help Display help and exit.\n"
+ "\n");
+}
diff --git a/test/performance/odp_crypto_run.sh b/test/performance/odp_crypto_run.sh
new file mode 100755
index 000000000..fcb7435fd
--- /dev/null
+++ b/test/performance/odp_crypto_run.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+# Run with a small number of iterations in make check
+
+$TEST_DIR/odp_crypto${EXEEXT} -i 100
+
+if [ $? -ne 0 ] ; then
+ echo Test FAILED
+ exit 1
+fi
+
+exit 0
diff --git a/test/performance/odp_dma_perf.c b/test/performance/odp_dma_perf.c
new file mode 100644
index 000000000..2f4ca490d
--- /dev/null
+++ b/test/performance/odp_dma_perf.c
@@ -0,0 +1,1951 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2024 Nokia
+ */
+
+/**
+ * @example odp_dma_perf.c
+ *
+ * This tester application can be used to profile the performance of an ODP DMA implementation.
+ * Tester workflow is simple and consists of issuing as many back-to-back DMA transfers as the
+ * implementation allows and then recording key performance statistics (such as function overhead,
+ * latencies etc.).
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define EXIT_NOT_SUP 2
+#define PROG_NAME "odp_dma_perf"
+
+enum {
+ SYNC_DMA = 0U,
+ ASYNC_DMA,
+ SW_COPY
+};
+
+enum {
+ DENSE_PACKET = 0U,
+ SPARSE_PACKET,
+ DENSE_MEMORY,
+ SPARSE_MEMORY
+};
+
+enum {
+ POLL = 0U,
+ EVENT
+};
+
+enum {
+ SINGLE = 0U,
+ MANY
+};
+
+#define DEF_TRS_TYPE SYNC_DMA
+#define DEF_SEG_CNT 1U
+#define DEF_LEN 1024U
+#define DEF_SEG_TYPE DENSE_PACKET
+#define DEF_MODE POLL
+#define DEF_INFLIGHT 1U
+#define DEF_TIME 10U
+#define DEF_WORKERS 1U
+#define DEF_POLICY SINGLE
+
+#define MAX_SEGS 1024U
+#define MAX_WORKERS 24
+#define MAX_MEMORY (256U * 1024U * 1024U)
+
+#define GIGAS 1000000000
+#define MEGAS 1000000
+#define KILOS 1000
+
+#define DATA 0xAA
+
+typedef enum {
+ PRS_OK,
+ PRS_NOK,
+ PRS_TERM,
+ PRS_NOT_SUP
+} parse_result_t;
+
+typedef struct {
+ uint64_t completed;
+ uint64_t start_errs;
+ uint64_t poll_errs;
+ uint64_t scheduler_timeouts;
+ uint64_t transfer_errs;
+ uint64_t data_errs;
+ uint64_t tot_tm;
+ uint64_t trs_tm;
+ uint64_t max_trs_tm;
+ uint64_t min_trs_tm;
+ uint64_t start_cc;
+ uint64_t max_start_cc;
+ uint64_t min_start_cc;
+ uint64_t wait_cc;
+ uint64_t max_wait_cc;
+ uint64_t min_wait_cc;
+ uint64_t trs_cc;
+ uint64_t max_trs_cc;
+ uint64_t min_trs_cc;
+ uint64_t start_cnt;
+ uint64_t wait_cnt;
+ uint64_t trs_poll_cnt;
+ uint64_t trs_cnt;
+} stats_t;
+
+typedef struct {
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_compl_param_t compl_param;
+ odp_ticketlock_t lock;
+ odp_time_t trs_start_tm;
+ uint64_t trs_start_cc;
+ uint64_t trs_poll_cnt;
+ odp_bool_t is_running;
+} trs_info_t;
+
+typedef struct sd_s sd_t;
+typedef void (*ver_fn_t)(trs_info_t *info, stats_t *stats);
+
+typedef struct ODP_ALIGNED_CACHE sd_s {
+ struct {
+ trs_info_t infos[MAX_SEGS];
+ odp_dma_seg_t src_seg[MAX_SEGS];
+ odp_dma_seg_t dst_seg[MAX_SEGS];
+ odp_dma_t handle;
+ odp_pool_t pool;
+ odp_queue_t compl_q;
+ uint32_t num_in_segs;
+ uint32_t num_out_segs;
+ uint32_t src_seg_len;
+ uint32_t dst_seg_len;
+ uint32_t num_inflight;
+ uint8_t trs_type;
+ uint8_t compl_mode;
+ } dma;
+
+ struct {
+ odp_packet_t src_pkt[MAX_SEGS];
+ odp_packet_t dst_pkt[MAX_SEGS];
+ odp_pool_t src_pool;
+ odp_pool_t dst_pool;
+ odp_shm_t src_shm;
+ odp_shm_t dst_shm;
+ void *src;
+ void *dst;
+ void *src_high;
+ void *dst_high;
+ void *cur_src;
+ void *cur_dst;
+ uint64_t shm_size;
+ uint8_t seg_type;
+ } seg;
+
+ odp_schedule_group_t grp;
+ /* Prepare single transfer. */
+ void (*prep_trs_fn)(sd_t *sd, trs_info_t *info);
+ /* Verify single transfer. */
+ ver_fn_t ver_fn;
+} sd_t;
+
+typedef struct prog_config_s prog_config_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ stats_t stats;
+ prog_config_t *prog_config;
+ sd_t *sd;
+} thread_config_t;
+
+typedef struct {
+ /* Configure DMA session specific resources. */
+ odp_bool_t (*session_cfg_fn)(sd_t *sd);
+ /* Setup transfer elements (memory/packet segments). */
+ odp_bool_t (*setup_fn)(sd_t *sd);
+ /* Configure DMA transfers (segment addresses etc.). */
+ void (*trs_fn)(sd_t *sd);
+ /* Configure transfer completion resources (transfer IDs, events etc.). */
+ odp_bool_t (*compl_fn)(sd_t *sd);
+ /* Initiate required initial transfers. */
+ odp_bool_t (*bootstrap_fn)(sd_t *sd);
+ /* Wait and handle finished transfer. */
+ void (*wait_fn)(sd_t *sd, stats_t *stats);
+ /* Handle all unfinished transfers after main test has been stopped. */
+ void (*drain_fn)(sd_t *sd);
+ /* Free any resources that might have been allocated during setup phase. */
+ void (*free_fn)(const sd_t *sd);
+} test_api_t;
+
+typedef struct prog_config_s {
+ odph_thread_t threads[MAX_WORKERS];
+ thread_config_t thread_config[MAX_WORKERS];
+ sd_t sds[MAX_WORKERS];
+ test_api_t api;
+ odp_atomic_u32_t is_running;
+ odp_instance_t odp_instance;
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ odp_dma_compl_mode_t compl_mode_mask;
+ odp_pool_t src_pool;
+ odp_pool_t dst_pool;
+ uint64_t shm_size;
+ uint32_t num_in_segs;
+ uint32_t num_out_segs;
+ uint32_t src_seg_len;
+ uint32_t dst_seg_len;
+ uint32_t num_inflight;
+ double time_sec;
+ uint32_t num_sessions;
+ uint32_t src_cache_size;
+ uint32_t dst_cache_size;
+ int num_workers;
+ odp_bool_t is_verify;
+ uint8_t trs_type;
+ uint8_t seg_type;
+ uint8_t compl_mode;
+ uint8_t policy;
+} prog_config_t;
+
+static prog_config_t *prog_conf;
+
+static const int mode_map[] = { ODP_DMA_COMPL_POLL, ODP_DMA_COMPL_EVENT };
+
+static void terminate(int signal ODP_UNUSED)
+{
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+}
+
+static void init_config(prog_config_t *config)
+{
+ sd_t *sd;
+ trs_info_t *info;
+ stats_t *stats;
+
+ memset(config, 0, sizeof(*config));
+ config->compl_mode_mask |= ODP_DMA_COMPL_SYNC;
+ config->src_pool = ODP_POOL_INVALID;
+ config->dst_pool = ODP_POOL_INVALID;
+ config->num_in_segs = DEF_SEG_CNT;
+ config->num_out_segs = DEF_SEG_CNT;
+ config->src_seg_len = DEF_LEN;
+ config->num_inflight = DEF_INFLIGHT;
+ config->time_sec = DEF_TIME;
+ config->num_workers = DEF_WORKERS;
+ config->trs_type = DEF_TRS_TYPE;
+ config->seg_type = DEF_SEG_TYPE;
+ config->compl_mode = DEF_MODE;
+ config->policy = DEF_POLICY;
+
+ for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
+ sd = &config->sds[i];
+ stats = &config->thread_config[i].stats;
+ memset(sd, 0, sizeof(*sd));
+
+ for (uint32_t j = 0U; j < MAX_SEGS; ++j) {
+ info = &sd->dma.infos[j];
+ info->compl_param.transfer_id = ODP_DMA_TRANSFER_ID_INVALID;
+ info->compl_param.event = ODP_EVENT_INVALID;
+ info->compl_param.queue = ODP_QUEUE_INVALID;
+ odp_ticketlock_init(&info->lock);
+ sd->seg.src_pkt[j] = ODP_PACKET_INVALID;
+ sd->seg.dst_pkt[j] = ODP_PACKET_INVALID;
+ }
+
+ sd->dma.handle = ODP_DMA_INVALID;
+ sd->dma.pool = ODP_POOL_INVALID;
+ sd->dma.compl_q = ODP_QUEUE_INVALID;
+ sd->seg.src_shm = ODP_SHM_INVALID;
+ sd->seg.dst_shm = ODP_SHM_INVALID;
+ sd->grp = ODP_SCHED_GROUP_INVALID;
+ stats->min_trs_tm = UINT64_MAX;
+ stats->min_start_cc = UINT64_MAX;
+ stats->min_wait_cc = UINT64_MAX;
+ stats->min_trs_cc = UINT64_MAX;
+ }
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "DMA performance test. Load DMA subsystem from several workers.\n"
+ "\n"
+ "Usage: " PROG_NAME " [OPTIONS]\n"
+ "\n"
+ " E.g. " PROG_NAME "\n"
+ " " PROG_NAME " -s 10240\n"
+ " " PROG_NAME " -t 0 -i 1 -o 1 -s 51200 -S 2 -f 64 -T 10\n"
+ " " PROG_NAME " -t 1 -i 10 -o 10 -s 4096 -S 0 -m 1 -f 10 -c 4 -p 1\n"
+ " " PROG_NAME " -t 2 -i 10 -o 1 -s 1024 -S 3 -f 10 -c 4 -p 1\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ "\n"
+ " -t, --trs_type Transfer type for test data. %u by default.\n"
+ " Types:\n"
+ " 0: synchronous DMA\n"
+ " 1: asynchronous DMA\n"
+ " 2: SW memory copy\n"
+ " -i, --num_in_seg Number of input segments to transfer. 0 means the maximum\n"
+ " count supported by the implementation. %u by default.\n"
+ " -o, --num_out_seg Number of output segments to transfer to. 0 means the\n"
+ " maximum count supported by the implementation. %u by\n"
+ " default.\n"
+ " -s, --in_seg_len Input segment length in bytes. 0 length means the maximum\n"
+ " segment length supported by the implementation. The actual\n"
+ " maximum might be limited by what type of data is\n"
+ " transferred (packet/memory). %u by default.\n"
+ " -S, --in_seg_type Input segment data type. Dense types can load the DMA\n"
+ " subsystem more heavily as transfer resources are\n"
+ " pre-configured. Sparse types might on the other hand\n"
+ " reflect application usage more precisely as transfer\n"
+ " resources are configured in runtime. %u by default.\n"
+ " Types:\n"
+ " 0: dense packet\n"
+ " 1: sparse packet\n"
+ " 2: dense memory\n"
+ " 3: sparse memory\n"
+ " -m, --compl_mode Completion mode for transfers. %u by default.\n"
+ " Modes:\n"
+ " 0: poll\n"
+ " 1: event\n"
+ " -f, --max_in_flight Maximum transfers in-flight per session. 0 means the\n"
+ " maximum supported by the tester/implementation. %u by\n"
+ " default.\n"
+ " -T, --time_sec Time in seconds to run. 0 means infinite. %u by default.\n"
+ " -c, --worker_count Amount of workers. %u by default.\n"
+ " -p, --policy DMA session policy. %u by default.\n"
+ " Policies:\n"
+ " 0: One session shared by workers\n"
+ " 1: One session per worker\n"
+ " -v, --verify Verify transfers. Checks correctness of destination data\n"
+ " after successful transfers.\n"
+ " -h, --help This help.\n"
+ "\n", DEF_TRS_TYPE, DEF_SEG_CNT, DEF_SEG_CNT, DEF_LEN, DEF_SEG_TYPE, DEF_MODE,
+ DEF_INFLIGHT, DEF_TIME, DEF_WORKERS, DEF_POLICY);
+}
+
+static parse_result_t check_options(prog_config_t *config)
+{
+ int max_workers;
+ odp_dma_capability_t dma_capa;
+ uint32_t num_sessions, max_seg_len, max_trs, max_in, max_out, max_segs;
+ odp_schedule_capability_t sched_capa;
+ odp_pool_capability_t pool_capa;
+ odp_shm_capability_t shm_capa;
+ uint64_t shm_size = 0U;
+
+ if (config->trs_type != SYNC_DMA && config->trs_type != ASYNC_DMA &&
+ config->trs_type != SW_COPY) {
+ ODPH_ERR("Invalid transfer type: %u\n", config->trs_type);
+ return PRS_NOK;
+ }
+
+ if (config->seg_type != DENSE_PACKET && config->seg_type != SPARSE_PACKET &&
+ config->seg_type != DENSE_MEMORY && config->seg_type != SPARSE_MEMORY) {
+ ODPH_ERR("Invalid segment type: %u\n", config->seg_type);
+ return PRS_NOK;
+ }
+
+ max_workers = ODPH_MIN(odp_thread_count_max() - 1, MAX_WORKERS);
+
+ if (config->num_workers <= 0 || config->num_workers > max_workers) {
+ ODPH_ERR("Invalid thread count: %d (min: 1, max: %d)\n", config->num_workers,
+ max_workers);
+ return PRS_NOK;
+ }
+
+ if (config->policy != SINGLE && config->policy != MANY) {
+ ODPH_ERR("Invalid DMA session policy: %u\n", config->policy);
+ return PRS_NOK;
+ }
+
+ if (odp_dma_capability(&dma_capa) < 0) {
+ ODPH_ERR("Error querying DMA capabilities\n");
+ return PRS_NOK;
+ }
+
+ num_sessions = config->policy == SINGLE ? 1 : config->num_workers;
+
+ if (num_sessions > dma_capa.max_sessions) {
+ ODPH_ERR("Not enough DMA sessions supported: %u (max: %u)\n", num_sessions,
+ dma_capa.max_sessions);
+ return PRS_NOT_SUP;
+ }
+
+ config->num_sessions = num_sessions;
+
+ if (config->num_in_segs == 0U)
+ config->num_in_segs = dma_capa.max_src_segs;
+
+ if (config->num_out_segs == 0U)
+ config->num_out_segs = dma_capa.max_dst_segs;
+
+ if (config->num_in_segs > dma_capa.max_src_segs ||
+ config->num_out_segs > dma_capa.max_dst_segs ||
+ config->num_in_segs + config->num_out_segs > dma_capa.max_segs) {
+ ODPH_ERR("Unsupported segment count configuration, in: %u, out: %u (max in: %u, "
+ "max out: %u, max tot: %u)\n", config->num_in_segs, config->num_out_segs,
+ dma_capa.max_src_segs, dma_capa.max_dst_segs, dma_capa.max_segs);
+ return PRS_NOT_SUP;
+ }
+
+ if (config->src_seg_len == 0U)
+ config->src_seg_len = dma_capa.max_seg_len;
+
+ config->dst_seg_len = config->src_seg_len * config->num_in_segs /
+ config->num_out_segs + config->src_seg_len *
+ config->num_in_segs % config->num_out_segs;
+
+ max_seg_len = ODPH_MAX(config->src_seg_len, config->dst_seg_len);
+
+ if (max_seg_len > dma_capa.max_seg_len) {
+ ODPH_ERR("Unsupported total DMA segment length: %u (max: %u)\n", max_seg_len,
+ dma_capa.max_seg_len);
+ return PRS_NOT_SUP;
+ }
+
+ if (config->trs_type == ASYNC_DMA) {
+ if (config->compl_mode != POLL && config->compl_mode != EVENT) {
+ ODPH_ERR("Invalid completion mode: %u\n", config->compl_mode);
+ return PRS_NOK;
+ }
+
+ if (config->compl_mode == POLL && (dma_capa.compl_mode_mask & ODP_DMA_COMPL_POLL)
+ == 0U) {
+ ODPH_ERR("Unsupported DMA completion mode, poll\n");
+ return PRS_NOT_SUP;
+ }
+
+ if (config->compl_mode == EVENT) {
+ if (config->num_sessions > dma_capa.pool.max_pools) {
+ ODPH_ERR("Unsupported amount of completion pools: %u (max: %u)\n",
+ config->num_sessions, dma_capa.pool.max_pools);
+ return PRS_NOT_SUP;
+ }
+
+ if ((dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) == 0U) {
+ ODPH_ERR("Unsupported DMA completion mode, event\n");
+ return PRS_NOT_SUP;
+ }
+
+ if (dma_capa.queue_type_sched == 0) {
+ ODPH_ERR("Unsupported DMA queueing type, scheduled\n");
+ return PRS_NOT_SUP;
+ }
+
+ if (config->num_inflight > dma_capa.pool.max_num) {
+ ODPH_ERR("Unsupported amount of completion events: %u (max: %u)\n",
+ config->num_inflight, dma_capa.pool.max_num);
+ return PRS_NOT_SUP;
+ }
+
+ if (odp_schedule_capability(&sched_capa) < 0) {
+ ODPH_ERR("Error querying scheduler capabilities\n");
+ return PRS_NOK;
+ }
+
+ if (config->num_sessions > sched_capa.max_groups - 3U) {
+ ODPH_ERR("Unsupported amount of scheduler groups: %u (max: %u)\n",
+ config->num_sessions, sched_capa.max_groups - 3U);
+ return PRS_NOT_SUP;
+ }
+ }
+
+ config->compl_mode_mask |= mode_map[config->compl_mode];
+ }
+
+ max_trs = ODPH_MIN(dma_capa.max_transfers, MAX_SEGS);
+
+ if (config->num_inflight == 0U)
+ config->num_inflight = max_trs;
+
+ if (config->num_inflight > max_trs) {
+ ODPH_ERR("Unsupported amount of in-flight DMA transfers: %u (max: %u)\n",
+ config->num_inflight, max_trs);
+ return PRS_NOT_SUP;
+ }
+
+ max_in = config->num_in_segs * config->num_inflight;
+ max_out = config->num_out_segs * config->num_inflight;
+ max_segs = ODPH_MAX(max_in, max_out);
+
+ if (max_segs > MAX_SEGS) {
+ ODPH_ERR("Unsupported input/output * inflight segment combination: %u (max: %u)\n",
+ max_segs, MAX_SEGS);
+ return PRS_NOT_SUP;
+ }
+
+ if (config->seg_type == DENSE_PACKET || config->seg_type == SPARSE_PACKET) {
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("Error querying pool capabilities\n");
+ return PRS_NOK;
+ }
+
+ if (pool_capa.pkt.max_pools < 2U) {
+ ODPH_ERR("Unsupported amount of packet pools: 2 (max: %u)\n",
+ pool_capa.pkt.max_pools);
+ return PRS_NOT_SUP;
+ }
+
+ if (pool_capa.pkt.max_len != 0U && max_seg_len > pool_capa.pkt.max_len) {
+ ODPH_ERR("Unsupported packet size: %u (max: %u)\n", max_seg_len,
+ pool_capa.pkt.max_len);
+ return PRS_NOT_SUP;
+ }
+
+ if (pool_capa.pkt.max_num != 0U &&
+ max_segs * num_sessions > pool_capa.pkt.max_num) {
+ ODPH_ERR("Unsupported amount of packet pool elements: %u (max: %u)\n",
+ max_segs * num_sessions, pool_capa.pkt.max_num);
+ return PRS_NOT_SUP;
+ }
+
+ config->src_cache_size = ODPH_MIN(ODPH_MAX(max_in, pool_capa.pkt.min_cache_size),
+ pool_capa.pkt.max_cache_size);
+ config->dst_cache_size = ODPH_MIN(ODPH_MAX(max_out, pool_capa.pkt.min_cache_size),
+ pool_capa.pkt.max_cache_size);
+ } else {
+ /* If SHM implementation capabilities are very puny, program will have already
+ * failed when reserving memory for global program configuration. */
+ if (odp_shm_capability(&shm_capa) < 0) {
+ ODPH_ERR("Error querying SHM capabilities\n");
+ return PRS_NOK;
+ }
+
+ /* One block for program configuration, one for source memory and one for
+ * destination memory. */
+ if (shm_capa.max_blocks < 3U) {
+ ODPH_ERR("Unsupported amount of SHM blocks: 3 (max: %u)\n",
+ shm_capa.max_blocks);
+ return PRS_NOT_SUP;
+ }
+
+ shm_size = (uint64_t)config->dst_seg_len * config->num_out_segs *
+ config->num_inflight;
+
+ if (shm_capa.max_size != 0U && shm_size > shm_capa.max_size) {
+ ODPH_ERR("Unsupported total SHM block size: %" PRIu64 ""
+ " (max: %" PRIu64 ")\n", shm_size, shm_capa.max_size);
+ return PRS_NOT_SUP;
+ }
+
+ if (config->seg_type == SPARSE_MEMORY && shm_size < MAX_MEMORY)
+ shm_size = shm_capa.max_size != 0U ?
+ ODPH_MIN(shm_capa.max_size, MAX_MEMORY) : MAX_MEMORY;
+
+ config->shm_size = shm_size;
+ }
+
+ return PRS_OK;
+}
+
+static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
+{
+ int opt, long_index;
+ static const struct option longopts[] = {
+ { "trs_type", required_argument, NULL, 't' },
+ { "num_in_seg", required_argument, NULL, 'i' },
+ { "num_out_seg", required_argument, NULL, 'o' },
+ { "in_seg_len", required_argument, NULL, 's' },
+ { "in_seg_type", required_argument, NULL, 'S' },
+ { "compl_mode", required_argument, NULL, 'm' },
+ { "max_in_flight", required_argument, NULL, 'f'},
+ { "time_sec", required_argument, NULL, 'T' },
+ { "worker_count", required_argument, NULL, 'c' },
+ { "policy", required_argument, NULL, 'p' },
+ { "verify", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ static const char *shortopts = "t:i:o:s:S:m:f:T:c:p:vh";
+
+ init_config(config);
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 't':
+ config->trs_type = atoi(optarg);
+ break;
+ case 'i':
+ config->num_in_segs = atoi(optarg);
+ break;
+ case 'o':
+ config->num_out_segs = atoi(optarg);
+ break;
+ case 's':
+ config->src_seg_len = atoi(optarg);
+ break;
+ case 'S':
+ config->seg_type = atoi(optarg);
+ break;
+ case 'm':
+ config->compl_mode = atoi(optarg);
+ break;
+ case 'f':
+ config->num_inflight = atoi(optarg);
+ break;
+ case 'T':
+ config->time_sec = atof(optarg);
+ break;
+ case 'c':
+ config->num_workers = atoi(optarg);
+ break;
+ case 'p':
+ config->policy = atoi(optarg);
+ break;
+ case 'v':
+ config->is_verify = true;
+ break;
+ case 'h':
+ print_usage();
+ return PRS_TERM;
+ case '?':
+ default:
+ print_usage();
+ return PRS_NOK;
+ }
+ }
+
+ return check_options(config);
+}
+
+static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
+{
+ struct sigaction action = { .sa_handler = terminate };
+
+ if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
+ sigaddset(&action.sa_mask, SIGTERM) == -1 ||
+ sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
+ sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
+ ODPH_ERR("Error installing signal handler\n");
+ return PRS_NOK;
+ }
+
+ return parse_options(argc, argv, config);
+}
+
+static odp_pool_t get_src_packet_pool(void)
+{
+ odp_pool_param_t param;
+ uint32_t num_pkts_per_worker = ODPH_MAX(prog_conf->num_inflight * prog_conf->num_in_segs,
+ prog_conf->src_cache_size);
+
+ if (prog_conf->src_pool != ODP_POOL_INVALID)
+ return prog_conf->src_pool;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = num_pkts_per_worker * prog_conf->num_workers;
+ param.pkt.len = prog_conf->src_seg_len;
+ param.pkt.seg_len = prog_conf->src_seg_len;
+ param.pkt.cache_size = prog_conf->src_cache_size;
+ prog_conf->src_pool = odp_pool_create(PROG_NAME "_src_pkts", &param);
+
+ return prog_conf->src_pool;
+}
+
+static odp_pool_t get_dst_packet_pool(void)
+{
+ odp_pool_param_t param;
+ uint32_t num_pkts_per_worker = ODPH_MAX(prog_conf->num_inflight * prog_conf->num_out_segs,
+ prog_conf->dst_cache_size);
+
+ if (prog_conf->dst_pool != ODP_POOL_INVALID)
+ return prog_conf->dst_pool;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = num_pkts_per_worker * prog_conf->num_workers;
+ param.pkt.len = prog_conf->dst_seg_len;
+ param.pkt.seg_len = prog_conf->dst_seg_len;
+ param.pkt.cache_size = prog_conf->dst_cache_size;
+ prog_conf->dst_pool = odp_pool_create(PROG_NAME "_dst_pkts", &param);
+
+ return prog_conf->dst_pool;
+}
+
+static odp_bool_t configure_packets(sd_t *sd)
+{
+ sd->seg.src_pool = get_src_packet_pool();
+
+ if (sd->seg.src_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating source packet pool\n");
+ return false;
+ }
+
+ sd->seg.dst_pool = get_dst_packet_pool();
+
+ if (sd->seg.dst_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating destination packet pool\n");
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t allocate_packets(sd_t *sd)
+{
+ for (uint32_t i = 0U; i < sd->dma.num_inflight * sd->dma.num_in_segs; ++i) {
+ sd->seg.src_pkt[i] = odp_packet_alloc(sd->seg.src_pool, sd->dma.src_seg_len);
+
+ if (sd->seg.src_pkt[i] == ODP_PACKET_INVALID) {
+ ODPH_ERR("Error allocating source segment packets\n");
+ return false;
+ }
+ }
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight * sd->dma.num_out_segs; ++i) {
+ sd->seg.dst_pkt[i] = odp_packet_alloc(sd->seg.dst_pool, sd->dma.dst_seg_len);
+
+ if (sd->seg.dst_pkt[i] == ODP_PACKET_INVALID) {
+ ODPH_ERR("Error allocating destination segment packets\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static odp_bool_t setup_packet_segments(sd_t *sd)
+{
+ return configure_packets(sd) &&
+ (sd->seg.seg_type == DENSE_PACKET ? allocate_packets(sd) : true);
+}
+
+static inline void fill_data(uint8_t *data, uint32_t len)
+{
+ memset(data, DATA, len);
+}
+
+static void configure_packet_transfer(sd_t *sd)
+{
+ odp_dma_seg_t *start_src_seg, *start_dst_seg, *seg;
+ uint32_t k = 0U, z = 0U, len;
+ odp_packet_t pkt;
+ odp_dma_transfer_param_t *param;
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight; ++i) {
+ start_src_seg = &sd->dma.src_seg[k];
+ start_dst_seg = &sd->dma.dst_seg[z];
+
+ for (uint32_t j = 0U; j < sd->dma.num_in_segs; ++j, ++k) {
+ pkt = sd->seg.src_pkt[k];
+ seg = &start_src_seg[j];
+ seg->packet = pkt;
+ seg->offset = 0U;
+ seg->len = sd->dma.src_seg_len;
+
+ if (seg->packet != ODP_PACKET_INVALID)
+ fill_data(odp_packet_data(seg->packet), seg->len);
+ }
+
+ len = sd->dma.num_in_segs * sd->dma.src_seg_len;
+
+ for (uint32_t j = 0U; j < sd->dma.num_out_segs; ++j, ++z) {
+ pkt = sd->seg.dst_pkt[z];
+ seg = &start_dst_seg[j];
+ seg->packet = pkt;
+ seg->offset = 0U;
+ seg->len = ODPH_MIN(len, sd->dma.dst_seg_len);
+ len -= sd->dma.dst_seg_len;
+ }
+
+ param = &sd->dma.infos[i].trs_param;
+ odp_dma_transfer_param_init(param);
+ param->src_format = ODP_DMA_FORMAT_PACKET;
+ param->dst_format = ODP_DMA_FORMAT_PACKET;
+ param->num_src = sd->dma.num_in_segs;
+ param->num_dst = sd->dma.num_out_segs;
+ param->src_seg = start_src_seg;
+ param->dst_seg = start_dst_seg;
+ }
+}
+
+static void free_packets(const sd_t *sd)
+{
+ for (uint32_t i = 0U; i < sd->dma.num_inflight * sd->dma.num_in_segs; ++i) {
+ if (sd->seg.src_pkt[i] != ODP_PACKET_INVALID)
+ odp_packet_free(sd->seg.src_pkt[i]);
+ }
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight * sd->dma.num_out_segs; ++i) {
+ if (sd->seg.dst_pkt[i] != ODP_PACKET_INVALID)
+ odp_packet_free(sd->seg.dst_pkt[i]);
+ }
+}
+
+static odp_bool_t allocate_memory(sd_t *sd)
+{
+ sd->seg.src_shm = odp_shm_reserve(PROG_NAME "_src_shm", sd->seg.shm_size,
+ ODP_CACHE_LINE_SIZE, 0U);
+ sd->seg.dst_shm = odp_shm_reserve(PROG_NAME "_dst_shm", sd->seg.shm_size,
+ ODP_CACHE_LINE_SIZE, 0U);
+
+ if (sd->seg.src_shm == ODP_SHM_INVALID || sd->seg.dst_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error allocating SHM block\n");
+ return false;
+ }
+
+ sd->seg.src = odp_shm_addr(sd->seg.src_shm);
+ sd->seg.dst = odp_shm_addr(sd->seg.dst_shm);
+
+ if (sd->seg.src == NULL || sd->seg.dst == NULL) {
+ ODPH_ERR("Error resolving SHM block address\n");
+ return false;
+ }
+
+ sd->seg.src_high = (uint8_t *)sd->seg.src + sd->seg.shm_size - sd->dma.src_seg_len;
+ sd->seg.dst_high = (uint8_t *)sd->seg.dst + sd->seg.shm_size - sd->dma.dst_seg_len;
+ sd->seg.cur_src = sd->seg.src;
+ sd->seg.cur_dst = sd->seg.dst;
+
+ return true;
+}
+
+static odp_bool_t setup_memory_segments(sd_t *sd)
+{
+ return allocate_memory(sd);
+}
+
+static void configure_address_transfer(sd_t *sd)
+{
+ odp_dma_seg_t *start_src_seg, *start_dst_seg, *seg;
+ uint32_t k = 0U, z = 0U, len;
+ odp_dma_transfer_param_t *param;
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight; ++i) {
+ start_src_seg = &sd->dma.src_seg[k];
+ start_dst_seg = &sd->dma.dst_seg[z];
+
+ for (uint32_t j = 0U; j < sd->dma.num_in_segs; ++j, ++k) {
+ seg = &start_src_seg[j];
+ seg->addr = sd->seg.seg_type == SPARSE_MEMORY ?
+ NULL : (uint8_t *)sd->seg.src + k * sd->dma.src_seg_len;
+ seg->len = sd->dma.src_seg_len;
+
+ if (seg->addr != NULL)
+ fill_data(seg->addr, seg->len);
+ }
+
+ len = sd->dma.num_in_segs * sd->dma.src_seg_len;
+
+ for (uint32_t j = 0U; j < sd->dma.num_out_segs; ++j, ++z) {
+ seg = &start_dst_seg[j];
+ seg->addr = sd->seg.seg_type == SPARSE_MEMORY ?
+ NULL : (uint8_t *)sd->seg.dst + z * sd->dma.dst_seg_len;
+ seg->len = ODPH_MIN(len, sd->dma.dst_seg_len);
+ len -= sd->dma.dst_seg_len;
+ }
+
+ param = &sd->dma.infos[i].trs_param;
+ odp_dma_transfer_param_init(param);
+ param->src_format = ODP_DMA_FORMAT_ADDR;
+ param->dst_format = ODP_DMA_FORMAT_ADDR;
+ param->num_src = sd->dma.num_in_segs;
+ param->num_dst = sd->dma.num_out_segs;
+ param->src_seg = start_src_seg;
+ param->dst_seg = start_dst_seg;
+ }
+}
+
+static void free_memory(const sd_t *sd)
+{
+ if (sd->seg.src_shm != ODP_SHM_INVALID)
+ (void)odp_shm_free(sd->seg.src_shm);
+
+ if (sd->seg.dst_shm != ODP_SHM_INVALID)
+ (void)odp_shm_free(sd->seg.dst_shm);
+}
+
+static void run_transfer(odp_dma_t handle, trs_info_t *info, stats_t *stats, ver_fn_t ver_fn)
+{
+ odp_time_t start_tm, end_tm;
+ uint64_t start_cc, end_cc, trs_tm, trs_cc;
+ odp_dma_result_t res;
+ int ret;
+
+ start_tm = odp_time_local_strict();
+ start_cc = odp_cpu_cycles();
+ ret = odp_dma_transfer(handle, &info->trs_param, &res);
+ end_cc = odp_cpu_cycles();
+ end_tm = odp_time_local_strict();
+
+ if (odp_unlikely(ret <= 0)) {
+ ++stats->start_errs;
+ } else {
+ trs_tm = odp_time_diff_ns(end_tm, start_tm);
+ stats->max_trs_tm = ODPH_MAX(trs_tm, stats->max_trs_tm);
+ stats->min_trs_tm = ODPH_MIN(trs_tm, stats->min_trs_tm);
+ stats->trs_tm += trs_tm;
+ trs_cc = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_trs_cc = ODPH_MAX(trs_cc, stats->max_trs_cc);
+ stats->min_trs_cc = ODPH_MIN(trs_cc, stats->min_trs_cc);
+ stats->trs_cc += trs_cc;
+ ++stats->trs_cnt;
+ stats->max_start_cc = stats->max_trs_cc;
+ stats->min_start_cc = stats->min_trs_cc;
+ stats->start_cc += trs_cc;
+ ++stats->start_cnt;
+
+ if (odp_unlikely(!res.success)) {
+ ++stats->transfer_errs;
+ } else {
+ ++stats->completed;
+
+ if (ver_fn != NULL)
+ ver_fn(info, stats);
+ }
+ }
+}
+
+static void run_transfers_mt_unsafe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ odp_dma_t handle = sd->dma.handle;
+ trs_info_t *infos = sd->dma.infos, *info;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ run_transfer(handle, info, stats, sd->ver_fn);
+ }
+}
+
+static void run_transfers_mt_safe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ odp_dma_t handle = sd->dma.handle;
+ trs_info_t *infos = sd->dma.infos, *info;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (odp_ticketlock_trylock(&info->lock)) {
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ run_transfer(handle, info, stats, sd->ver_fn);
+ odp_ticketlock_unlock(&info->lock);
+ }
+ }
+}
+
+static odp_bool_t configure_poll_compl(sd_t *sd)
+{
+ odp_dma_compl_param_t *param;
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight; ++i) {
+ param = &sd->dma.infos[i].compl_param;
+
+ odp_dma_compl_param_init(param);
+ param->compl_mode = mode_map[sd->dma.compl_mode];
+ param->transfer_id = odp_dma_transfer_id_alloc(sd->dma.handle);
+
+ if (param->transfer_id == ODP_DMA_TRANSFER_ID_INVALID) {
+ ODPH_ERR("Error allocating transfer ID\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void poll_transfer(sd_t *sd, trs_info_t *info, stats_t *stats)
+{
+ uint64_t start_cc, end_cc, trs_tm, trs_cc, wait_cc, start_cc_diff;
+ odp_time_t start_tm;
+ odp_dma_t handle = sd->dma.handle;
+ odp_dma_result_t res;
+ int ret;
+
+ if (info->is_running) {
+ start_cc = odp_cpu_cycles();
+ ret = odp_dma_transfer_done(handle, info->compl_param.transfer_id, &res);
+ end_cc = odp_cpu_cycles();
+
+ if (odp_unlikely(ret < 0)) {
+ ++stats->poll_errs;
+ return;
+ }
+
+ ++info->trs_poll_cnt;
+ wait_cc = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_wait_cc = ODPH_MAX(wait_cc, stats->max_wait_cc);
+ stats->min_wait_cc = ODPH_MIN(wait_cc, stats->min_wait_cc);
+ stats->wait_cc += wait_cc;
+ ++stats->wait_cnt;
+
+ if (ret == 0)
+ return;
+
+ trs_tm = odp_time_diff_ns(odp_time_global_strict(), info->trs_start_tm);
+ stats->max_trs_tm = ODPH_MAX(trs_tm, stats->max_trs_tm);
+ stats->min_trs_tm = ODPH_MIN(trs_tm, stats->min_trs_tm);
+ stats->trs_tm += trs_tm;
+ trs_cc = odp_cpu_cycles_diff(odp_cpu_cycles(), info->trs_start_cc);
+ stats->max_trs_cc = ODPH_MAX(trs_cc, stats->max_trs_cc);
+ stats->min_trs_cc = ODPH_MIN(trs_cc, stats->min_trs_cc);
+ stats->trs_cc += trs_cc;
+ stats->trs_poll_cnt += info->trs_poll_cnt;
+ ++stats->trs_cnt;
+
+ if (odp_unlikely(!res.success)) {
+ ++stats->transfer_errs;
+ } else {
+ ++stats->completed;
+
+ if (sd->ver_fn != NULL)
+ sd->ver_fn(info, stats);
+ }
+
+ info->is_running = false;
+ } else {
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ start_tm = odp_time_global_strict();
+ start_cc = odp_cpu_cycles();
+ ret = odp_dma_transfer_start(handle, &info->trs_param, &info->compl_param);
+ end_cc = odp_cpu_cycles();
+
+ if (odp_unlikely(ret <= 0)) {
+ ++stats->start_errs;
+ } else {
+ info->trs_start_tm = start_tm;
+ info->trs_start_cc = start_cc;
+ info->trs_poll_cnt = 0U;
+ start_cc_diff = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_start_cc = ODPH_MAX(start_cc_diff, stats->max_start_cc);
+ stats->min_start_cc = ODPH_MIN(start_cc_diff, stats->min_start_cc);
+ stats->start_cc += start_cc_diff;
+ ++stats->start_cnt;
+ info->is_running = true;
+ }
+ }
+}
+
+static void poll_transfers_mt_unsafe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ trs_info_t *infos = sd->dma.infos;
+
+ for (uint32_t i = 0U; i < count; ++i)
+ poll_transfer(sd, &infos[i], stats);
+}
+
+static void poll_transfers_mt_safe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ trs_info_t *infos = sd->dma.infos, *info;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (odp_ticketlock_trylock(&info->lock)) {
+ poll_transfer(sd, info, stats);
+ odp_ticketlock_unlock(&info->lock);
+ }
+ }
+}
+
+static void drain_poll_transfers(sd_t *sd)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ trs_info_t *infos = sd->dma.infos, *info;
+ odp_dma_t handle = sd->dma.handle;
+ int rc;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (info->is_running) {
+ do {
+ rc = odp_dma_transfer_done(handle, info->compl_param.transfer_id,
+ NULL);
+ } while (rc == 0);
+ }
+ }
+}
+
+static odp_bool_t configure_event_compl_session(sd_t *sd)
+{
+ odp_thrmask_t zero;
+ odp_dma_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+
+ odp_thrmask_zero(&zero);
+ sd->grp = odp_schedule_group_create(PROG_NAME "_scd_grp", &zero);
+
+ if (sd->grp == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("Error creating scheduler group for DMA session\n");
+ return false;
+ }
+
+ odp_dma_pool_param_init(&pool_param);
+ pool_param.num = sd->dma.num_inflight;
+ sd->dma.pool = odp_dma_pool_create(PROG_NAME "_dma_evs", &pool_param);
+
+ if (sd->dma.pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating DMA event completion pool\n");
+ return false;
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.group = sd->grp;
+ sd->dma.compl_q = odp_queue_create(PROG_NAME, &queue_param);
+
+ if (sd->dma.compl_q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error creating DMA completion queue\n");
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t configure_event_compl(sd_t *sd)
+{
+ odp_dma_compl_param_t *param;
+ odp_dma_compl_t c_ev;
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight; ++i) {
+ param = &sd->dma.infos[i].compl_param;
+
+ odp_dma_compl_param_init(param);
+ param->compl_mode = mode_map[sd->dma.compl_mode];
+ c_ev = odp_dma_compl_alloc(sd->dma.pool);
+
+ if (c_ev == ODP_DMA_COMPL_INVALID) {
+ ODPH_ERR("Error allocating completion event\n");
+ return false;
+ }
+
+ param->event = odp_dma_compl_to_event(c_ev);
+ param->queue = sd->dma.compl_q;
+ param->user_ptr = &sd->dma.infos[i];
+ }
+
+ return true;
+}
+
+static odp_bool_t start_initial_transfers(sd_t *sd)
+{
+ odp_time_t start_tm;
+ uint64_t start_cc;
+ trs_info_t *info;
+ int ret;
+
+ for (uint32_t i = 0U; i < sd->dma.num_inflight; ++i) {
+ info = &sd->dma.infos[i];
+
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ start_tm = odp_time_global_strict();
+ start_cc = odp_cpu_cycles();
+ ret = odp_dma_transfer_start(sd->dma.handle, &info->trs_param, &info->compl_param);
+
+ if (ret <= 0) {
+ ODPH_ERR("Error starting DMA transfer\n");
+ return false;
+ }
+
+ info->trs_start_tm = start_tm;
+ info->trs_start_cc = start_cc;
+ }
+
+ return true;
+}
+
+static void wait_compl_event(sd_t *sd, stats_t *stats)
+{
+ uint64_t start_cc, end_cc, wait_cc, trs_tm, trs_cc, start_cc_diff;
+ odp_time_t start_tm;
+ odp_event_t ev;
+ odp_dma_result_t res;
+ trs_info_t *info;
+ int ret;
+
+ start_cc = odp_cpu_cycles();
+ ev = odp_schedule(NULL, odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+ end_cc = odp_cpu_cycles();
+
+ if (odp_unlikely(ev == ODP_EVENT_INVALID)) {
+ ++stats->scheduler_timeouts;
+ return;
+ }
+
+ odp_dma_compl_result(odp_dma_compl_from_event(ev), &res);
+ info = res.user_ptr;
+ trs_tm = odp_time_diff_ns(odp_time_global_strict(), info->trs_start_tm);
+ stats->max_trs_tm = ODPH_MAX(trs_tm, stats->max_trs_tm);
+ stats->min_trs_tm = ODPH_MIN(trs_tm, stats->min_trs_tm);
+ stats->trs_tm += trs_tm;
+ trs_cc = odp_cpu_cycles_diff(odp_cpu_cycles(), info->trs_start_cc);
+ stats->max_trs_cc = ODPH_MAX(trs_cc, stats->max_trs_cc);
+ stats->min_trs_cc = ODPH_MIN(trs_cc, stats->min_trs_cc);
+ stats->trs_cc += trs_cc;
+ ++stats->trs_cnt;
+ wait_cc = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_wait_cc = ODPH_MAX(wait_cc, stats->max_wait_cc);
+ stats->min_wait_cc = ODPH_MIN(wait_cc, stats->min_wait_cc);
+ stats->wait_cc += wait_cc;
+ ++stats->wait_cnt;
+
+ if (odp_unlikely(!res.success)) {
+ ++stats->transfer_errs;
+ } else {
+ ++stats->completed;
+
+ if (sd->ver_fn != NULL)
+ sd->ver_fn(info, stats);
+ }
+
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ start_tm = odp_time_global_strict();
+ start_cc = odp_cpu_cycles();
+ ret = odp_dma_transfer_start(sd->dma.handle, &info->trs_param, &info->compl_param);
+ end_cc = odp_cpu_cycles();
+
+ if (odp_unlikely(ret <= 0)) {
+ ++stats->start_errs;
+ } else {
+ info->trs_start_tm = start_tm;
+ info->trs_start_cc = start_cc;
+ start_cc_diff = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_start_cc = ODPH_MAX(start_cc_diff, stats->max_start_cc);
+ stats->min_start_cc = ODPH_MIN(start_cc_diff, stats->min_start_cc);
+ stats->start_cc += start_cc_diff;
+ ++stats->start_cnt;
+ }
+}
+
+static void drain_compl_events(ODP_UNUSED sd_t *sd)
+{
+ odp_event_t ev;
+
+ while (true) {
+ ev = odp_schedule(NULL, odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+ }
+}
+
+static void run_memcpy(trs_info_t *info, stats_t *stats, ver_fn_t ver_fn)
+{
+ odp_time_t start_tm;
+ uint64_t start_cc, end_cc, trs_tm, trs_cc;
+ const odp_dma_transfer_param_t *param = &info->trs_param;
+ uint32_t tot_len, src_len, dst_len, min_len, len, i = 0U, j = 0U, src_off = 0U,
+ dst_off = 0U, src_rem, dst_rem;
+ const odp_bool_t is_addr = param->src_format == ODP_DMA_FORMAT_ADDR;
+ uint8_t *src_data, *dst_data;
+
+ /* Test data is configured so that total source and total destination sizes always match,
+ * all source and all destination segments have the same size and in case of packets,
+ * there's always just a single segment. */
+ tot_len = param->num_src * param->src_seg->len;
+ src_len = param->src_seg->len;
+ dst_len = param->dst_seg->len;
+ min_len = ODPH_MIN(src_len, dst_len);
+ len = min_len;
+ start_tm = odp_time_local_strict();
+ start_cc = odp_cpu_cycles();
+
+ while (tot_len > 0U) {
+ if (is_addr) {
+ src_data = param->src_seg[i].addr;
+ dst_data = param->dst_seg[j].addr;
+ } else {
+ src_data = odp_packet_data(param->src_seg[i].packet);
+ dst_data = odp_packet_data(param->dst_seg[j].packet);
+ }
+
+ memcpy(dst_data + dst_off, src_data + src_off, len);
+ dst_off += len;
+ src_off += len;
+ src_rem = src_len - src_off;
+ dst_rem = dst_len - dst_off;
+ tot_len -= len;
+ len = ODPH_MIN(ODPH_MAX(src_rem, dst_rem), min_len);
+
+ if (dst_rem > 0U) {
+ ++i;
+ src_off = 0U;
+ } else {
+ ++j;
+ dst_off = 0U;
+ }
+ }
+
+ end_cc = odp_cpu_cycles();
+ trs_tm = odp_time_diff_ns(odp_time_local_strict(), start_tm);
+ stats->max_trs_tm = ODPH_MAX(trs_tm, stats->max_trs_tm);
+ stats->min_trs_tm = ODPH_MIN(trs_tm, stats->min_trs_tm);
+ stats->trs_tm += trs_tm;
+ trs_cc = odp_cpu_cycles_diff(end_cc, start_cc);
+ stats->max_trs_cc = ODPH_MAX(trs_cc, stats->max_trs_cc);
+ stats->min_trs_cc = ODPH_MIN(trs_cc, stats->min_trs_cc);
+ stats->trs_cc += trs_cc;
+ ++stats->trs_cnt;
+ stats->max_start_cc = stats->max_trs_cc;
+ stats->min_start_cc = stats->min_trs_cc;
+ stats->start_cc += trs_cc;
+ ++stats->start_cnt;
+ ++stats->completed;
+
+ if (ver_fn != NULL)
+ ver_fn(info, stats);
+}
+
+static void run_memcpy_mt_unsafe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ trs_info_t *infos = sd->dma.infos, *info;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ run_memcpy(info, stats, sd->ver_fn);
+ }
+}
+
+static void run_memcpy_mt_safe(sd_t *sd, stats_t *stats)
+{
+ const uint32_t count = sd->dma.num_inflight;
+ trs_info_t *infos = sd->dma.infos, *info;
+
+ for (uint32_t i = 0U; i < count; ++i) {
+ info = &infos[i];
+
+ if (odp_ticketlock_trylock(&info->lock)) {
+ if (sd->prep_trs_fn != NULL)
+ sd->prep_trs_fn(sd, info);
+
+ run_memcpy(info, stats, sd->ver_fn);
+ odp_ticketlock_unlock(&info->lock);
+ }
+ }
+}
+
+static void setup_api(prog_config_t *config)
+{
+ if (config->seg_type == DENSE_PACKET || config->seg_type == SPARSE_PACKET) {
+ config->api.setup_fn = setup_packet_segments;
+ config->api.trs_fn = configure_packet_transfer;
+ config->api.free_fn = free_packets;
+ } else {
+ config->api.setup_fn = setup_memory_segments;
+ config->api.trs_fn = configure_address_transfer;
+ config->api.free_fn = free_memory;
+ }
+
+ if (config->trs_type == SYNC_DMA) {
+ config->api.session_cfg_fn = NULL;
+ config->api.compl_fn = NULL;
+ config->api.bootstrap_fn = NULL;
+ config->api.wait_fn = config->num_workers == 1 || config->policy == MANY ?
+ run_transfers_mt_unsafe : run_transfers_mt_safe;
+ config->api.drain_fn = NULL;
+ } else if (config->trs_type == ASYNC_DMA) {
+ if (config->compl_mode == POLL) {
+ config->api.session_cfg_fn = NULL;
+ config->api.compl_fn = configure_poll_compl;
+ config->api.bootstrap_fn = NULL;
+ config->api.wait_fn = config->num_workers == 1 || config->policy == MANY ?
+ poll_transfers_mt_unsafe : poll_transfers_mt_safe;
+ config->api.drain_fn = drain_poll_transfers;
+ } else {
+ config->api.session_cfg_fn = configure_event_compl_session;
+ config->api.compl_fn = configure_event_compl;
+ config->api.bootstrap_fn = start_initial_transfers;
+ config->api.wait_fn = wait_compl_event;
+ config->api.drain_fn = drain_compl_events;
+ }
+ } else {
+ config->api.session_cfg_fn = NULL;
+ config->api.compl_fn = NULL;
+ config->api.bootstrap_fn = NULL;
+ config->api.wait_fn = config->num_workers == 1 || config->policy == MANY ?
+ run_memcpy_mt_unsafe : run_memcpy_mt_safe;
+ config->api.drain_fn = NULL;
+ }
+}
+
+static void prepare_packet_transfer(sd_t *sd, trs_info_t *info)
+{
+ odp_dma_transfer_param_t *param = &info->trs_param;
+ odp_dma_seg_t *seg;
+
+ for (uint32_t i = 0U; i < param->num_src; ++i) {
+ seg = &param->src_seg[i];
+
+ if (odp_likely(seg->packet != ODP_PACKET_INVALID))
+ odp_packet_free(seg->packet);
+
+ seg->packet = odp_packet_alloc(sd->seg.src_pool, seg->len);
+
+ if (odp_unlikely(seg->packet == ODP_PACKET_INVALID))
+ /* There should always be enough packets. */
+ ODPH_ABORT("Failed to allocate packet, aborting\n");
+
+ fill_data(odp_packet_data(seg->packet), seg->len);
+ }
+
+ for (uint32_t i = 0U; i < param->num_dst; ++i) {
+ seg = &param->dst_seg[i];
+
+ if (odp_likely(seg->packet != ODP_PACKET_INVALID))
+ odp_packet_free(seg->packet);
+
+ seg->packet = odp_packet_alloc(sd->seg.dst_pool, seg->len);
+
+ if (odp_unlikely(seg->packet == ODP_PACKET_INVALID))
+ /* There should always be enough packets. */
+ ODPH_ABORT("Failed to allocate packet, aborting\n");
+ }
+}
+
+static void prepare_address_transfer(sd_t *sd, trs_info_t *info)
+{
+ odp_dma_transfer_param_t *param = &info->trs_param;
+ uint8_t *addr = sd->seg.cur_src;
+ odp_dma_seg_t *seg;
+
+ for (uint32_t i = 0U; i < param->num_src; ++i) {
+ seg = &param->src_seg[i];
+
+ if (odp_unlikely(addr > (uint8_t *)sd->seg.src_high))
+ addr = sd->seg.src;
+
+ seg->addr = addr;
+ addr += sd->dma.src_seg_len;
+ fill_data(seg->addr, seg->len);
+ }
+
+ sd->seg.cur_src = addr + ODP_CACHE_LINE_SIZE;
+ addr = sd->seg.cur_dst;
+
+ for (uint32_t i = 0U; i < param->num_dst; ++i) {
+ if (odp_unlikely(addr > (uint8_t *)sd->seg.dst_high))
+ addr = sd->seg.dst;
+
+ param->dst_seg[i].addr = addr;
+ addr += sd->dma.dst_seg_len;
+ }
+
+ sd->seg.cur_dst = addr + ODP_CACHE_LINE_SIZE;
+}
+
+static void verify_transfer(trs_info_t *info, stats_t *stats)
+{
+ odp_dma_transfer_param_t *param = &info->trs_param;
+ odp_dma_seg_t *seg;
+ const odp_bool_t is_addr = param->dst_format == ODP_DMA_FORMAT_ADDR;
+ uint8_t *data;
+
+ for (uint32_t i = 0U; i < param->num_dst; ++i) {
+ seg = &param->dst_seg[i];
+ data = is_addr ? seg->addr : odp_packet_data(seg->packet);
+
+ for (uint32_t j = 0U; j < seg->len; ++j)
+ if (odp_unlikely(data[j] != DATA)) {
+ ++stats->data_errs;
+ return;
+ }
+ }
+}
+
+static odp_bool_t setup_session_descriptors(prog_config_t *config)
+{
+ sd_t *sd;
+ const odp_dma_param_t dma_params = {
+ .direction = ODP_DMA_MAIN_TO_MAIN,
+ .type = ODP_DMA_TYPE_COPY,
+ .compl_mode_mask = config->compl_mode_mask,
+ .mt_mode = config->num_workers == 1 || config->policy == MANY ?
+ ODP_DMA_MT_SERIAL : ODP_DMA_MT_SAFE,
+ .order = ODP_DMA_ORDER_NONE };
+
+ for (uint32_t i = 0U; i < config->num_sessions; ++i) {
+ char name[ODP_DMA_NAME_LEN];
+
+ sd = &config->sds[i];
+ sd->dma.num_in_segs = config->num_in_segs;
+ sd->dma.num_out_segs = config->num_out_segs;
+ sd->dma.src_seg_len = config->src_seg_len;
+ sd->dma.dst_seg_len = config->dst_seg_len;
+ sd->dma.num_inflight = config->num_inflight;
+ sd->dma.trs_type = config->trs_type;
+ sd->dma.compl_mode = config->compl_mode;
+ snprintf(name, sizeof(name), PROG_NAME "_dma_%u", i);
+ sd->dma.handle = odp_dma_create(name, &dma_params);
+
+ if (sd->dma.handle == ODP_DMA_INVALID) {
+ ODPH_ERR("Error creating DMA session\n");
+ return false;
+ }
+
+ if (config->api.session_cfg_fn != NULL && !config->api.session_cfg_fn(sd))
+ return false;
+
+ sd->seg.shm_size = config->shm_size;
+ sd->seg.seg_type = config->seg_type;
+ sd->prep_trs_fn = config->seg_type == SPARSE_PACKET ? prepare_packet_transfer :
+ config->seg_type == SPARSE_MEMORY ?
+ prepare_address_transfer : NULL;
+ sd->ver_fn = config->is_verify ? verify_transfer : NULL;
+ }
+
+ return true;
+}
+
+static odp_bool_t setup_data(prog_config_t *config)
+{
+ sd_t *sd;
+
+ for (uint32_t i = 0U; i < config->num_sessions; ++i) {
+ sd = &config->sds[i];
+
+ if (!config->api.setup_fn(sd))
+ return false;
+
+ config->api.trs_fn(sd);
+
+ if (config->api.compl_fn != NULL && !config->api.compl_fn(sd))
+ return false;
+ }
+
+ return true;
+}
+
+static int transfer(void *args)
+{
+ thread_config_t *thr_config = args;
+ prog_config_t *prog_config = thr_config->prog_config;
+ sd_t *sd = thr_config->sd;
+ stats_t *stats = &thr_config->stats;
+ test_api_t *api = &prog_conf->api;
+ odp_thrmask_t mask;
+ odp_time_t start_tm;
+
+ odp_barrier_wait(&prog_config->init_barrier);
+
+ if (sd->grp != ODP_SCHED_GROUP_INVALID) {
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, odp_thread_id());
+
+ if (odp_schedule_group_join(sd->grp, &mask) < 0) {
+ ODPH_ERR("Error joining scheduler group\n");
+ goto out;
+ }
+ }
+
+ start_tm = odp_time_local_strict();
+
+ while (odp_atomic_load_u32(&prog_config->is_running))
+ api->wait_fn(sd, stats);
+
+ thr_config->stats.tot_tm = odp_time_diff_ns(odp_time_local_strict(), start_tm);
+
+ if (api->drain_fn != NULL)
+ api->drain_fn(sd);
+
+out:
+ odp_barrier_wait(&prog_config->term_barrier);
+
+ return 0;
+}
+
+static odp_bool_t setup_workers(prog_config_t *config)
+{
+ odp_cpumask_t cpumask;
+ int num_workers;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_params[config->num_workers], *thr_param;
+ thread_config_t *thr_config;
+ sd_t *sd;
+
+ /* Barrier init count for control and worker. */
+ odp_barrier_init(&config->init_barrier, config->num_workers + 1);
+ odp_barrier_init(&config->term_barrier, config->num_workers);
+ num_workers = odp_cpumask_default_worker(&cpumask, config->num_workers);
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = config->odp_instance;
+ thr_common.cpumask = &cpumask;
+
+ for (int i = 0; i < config->num_workers; ++i) {
+ thr_param = &thr_params[i];
+ thr_config = &config->thread_config[i];
+ sd = config->policy == SINGLE ? &config->sds[0U] : &config->sds[i];
+
+ odph_thread_param_init(thr_param);
+ thr_param->start = transfer;
+ thr_param->thr_type = ODP_THREAD_WORKER;
+ thr_config->prog_config = config;
+ thr_config->sd = sd;
+ thr_param->arg = thr_config;
+ }
+
+ num_workers = odph_thread_create(config->threads, &thr_common, thr_params, num_workers);
+
+ if (num_workers != config->num_workers) {
+ ODPH_ERR("Error configuring worker threads\n");
+ return false;
+ }
+
+ for (uint32_t i = 0U; i < config->num_sessions; ++i) {
+ if (config->api.bootstrap_fn != NULL && !config->api.bootstrap_fn(&config->sds[i]))
+ return false;
+ }
+
+ odp_barrier_wait(&config->init_barrier);
+
+ return true;
+}
+
+static odp_bool_t setup_test(prog_config_t *config)
+{
+ setup_api(config);
+
+ return setup_session_descriptors(config) && setup_data(config) && setup_workers(config);
+}
+
+static void stop_test(prog_config_t *config)
+{
+ (void)odph_thread_join(config->threads, config->num_workers);
+}
+
+static void teardown_data(const sd_t *sd, void (*free_fn)(const sd_t *sd))
+{
+ const odp_dma_compl_param_t *compl_param;
+
+ for (uint32_t i = 0U; i < MAX_SEGS; ++i) {
+ compl_param = &sd->dma.infos[i].compl_param;
+
+ if (compl_param->transfer_id != ODP_DMA_TRANSFER_ID_INVALID)
+ odp_dma_transfer_id_free(sd->dma.handle, compl_param->transfer_id);
+
+ if (compl_param->event != ODP_EVENT_INVALID)
+ odp_event_free(compl_param->event);
+ }
+
+ free_fn(sd);
+}
+
+static void teardown_test(prog_config_t *config)
+{
+ sd_t *sd;
+
+ for (uint32_t i = 0U; i < config->num_sessions; ++i) {
+ sd = &config->sds[i];
+ teardown_data(sd, config->api.free_fn);
+
+ if (sd->dma.compl_q != ODP_QUEUE_INVALID)
+ (void)odp_queue_destroy(sd->dma.compl_q);
+
+ if (sd->dma.pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(sd->dma.pool);
+
+ if (sd->grp != ODP_SCHED_GROUP_INVALID)
+ (void)odp_schedule_group_destroy(sd->grp);
+
+ if (sd->dma.handle != ODP_DMA_INVALID)
+ (void)odp_dma_destroy(sd->dma.handle);
+ }
+
+ if (config->src_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->src_pool);
+
+ if (config->dst_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->dst_pool);
+}
+
+static void print_humanised(uint64_t value, const char *type)
+{
+ if (value > GIGAS)
+ printf("%.2f G%s\n", (double)value / GIGAS, type);
+ else if (value > MEGAS)
+ printf("%.2f M%s\n", (double)value / MEGAS, type);
+ else if (value > KILOS)
+ printf("%.2f k%s\n", (double)value / KILOS, type);
+ else
+ printf("%" PRIu64 " %s\n", value, type);
+}
+
+static void print_stats(const prog_config_t *config)
+{
+ const stats_t *stats;
+ uint64_t data_cnt = config->num_in_segs * config->src_seg_len, tot_completed = 0U,
+ tot_tm = 0U, tot_trs_tm = 0U, tot_trs_cc = 0U, tot_trs_cnt = 0U, tot_min_tm = UINT64_MAX,
+ tot_max_tm = 0U, tot_min_cc = UINT64_MAX, tot_max_cc = 0U, avg_start_cc, avg_wait_cc;
+ double avg_tot_tm;
+
+ printf("\n======================\n\n"
+ "DMA performance test done\n\n"
+ " mode: %s\n"
+ " input segment count: %u\n"
+ " output segment count: %u\n"
+ " segment length: %u\n"
+ " segment type: %s\n"
+ " inflight count: %u\n"
+ " session policy: %s\n\n",
+ config->trs_type == SYNC_DMA ? "DMA synchronous" :
+ config->trs_type == ASYNC_DMA && config->compl_mode == POLL ?
+ "DMA asynchronous-poll" :
+ config->trs_type == ASYNC_DMA && config->compl_mode == EVENT ?
+ "DMA asynchronous-event" : "SW", config->num_in_segs,
+ config->num_out_segs, config->src_seg_len,
+ config->seg_type == DENSE_PACKET ? "dense packet" :
+ config->seg_type == SPARSE_PACKET ? "sparse packet" :
+ config->seg_type == DENSE_MEMORY ? "dense memory" : "sparse memory",
+ config->num_inflight, config->policy == SINGLE ? "shared" : "per-worker");
+
+ for (int i = 0; i < config->num_workers; ++i) {
+ stats = &config->thread_config[i].stats;
+ tot_completed += stats->completed;
+ tot_tm += stats->tot_tm;
+ tot_trs_tm += stats->trs_tm;
+ tot_trs_cc += stats->trs_cc;
+ tot_trs_cnt += stats->trs_cnt;
+ tot_min_tm = ODPH_MIN(tot_min_tm, stats->min_trs_tm);
+ tot_max_tm = ODPH_MAX(tot_max_tm, stats->max_trs_tm);
+ tot_min_cc = ODPH_MIN(tot_min_cc, stats->min_trs_cc);
+ tot_max_cc = ODPH_MAX(tot_max_cc, stats->max_trs_cc);
+
+ printf(" worker %d:\n", i);
+ printf(" successful transfers: %" PRIu64 "\n"
+ " start errors: %" PRIu64 "\n",
+ stats->completed, stats->start_errs);
+
+ if (config->trs_type == ASYNC_DMA) {
+ if (config->compl_mode == POLL)
+ printf(" poll errors: %" PRIu64 "\n",
+ stats->poll_errs);
+ else
+ printf(" scheduler timeouts: %" PRIu64 "\n",
+ stats->scheduler_timeouts);
+ }
+
+ printf(" transfer errors: %" PRIu64 "\n", stats->transfer_errs);
+
+ if (config->is_verify)
+ printf(" data errors: %" PRIu64 "\n", stats->data_errs);
+
+ printf(" run time: %" PRIu64 " ns\n", stats->tot_tm);
+
+ if (config->policy == MANY) {
+ printf(" session:\n"
+ " average time per transfer: %" PRIu64 " "
+ "(min: %" PRIu64 ", max: %" PRIu64 ") ns\n"
+ " average cycles per transfer: %" PRIu64 " "
+ "(min: %" PRIu64 ", max: %" PRIu64 ")\n"
+ " ops: ",
+ stats->trs_cnt > 0U ? stats->trs_tm / stats->trs_cnt : 0U,
+ stats->trs_cnt > 0U ? stats->min_trs_tm : 0U,
+ stats->trs_cnt > 0U ? stats->max_trs_tm : 0U,
+ stats->trs_cnt > 0U ? stats->trs_cc / stats->trs_cnt : 0U,
+ stats->trs_cnt > 0U ? stats->min_trs_cc : 0U,
+ stats->trs_cnt > 0U ? stats->max_trs_cc : 0U);
+ print_humanised(stats->completed /
+ ((double)stats->tot_tm / ODP_TIME_SEC_IN_NS),
+ "OPS");
+ printf(" speed: ");
+ print_humanised(stats->completed * data_cnt /
+ ((double)stats->tot_tm / ODP_TIME_SEC_IN_NS), "B/s");
+ }
+
+ avg_start_cc = stats->start_cnt > 0U ? stats->start_cc / stats->start_cnt : 0U;
+ printf(" average cycles breakdown:\n");
+
+ if (config->trs_type == SYNC_DMA) {
+ printf(" odp_dma_transfer(): %" PRIu64 " "
+ "(min: %" PRIu64 ", max: %" PRIu64 ")\n", avg_start_cc,
+ avg_start_cc > 0U ? stats->min_start_cc : 0U,
+ avg_start_cc > 0U ? stats->max_start_cc : 0U);
+ } else if (config->trs_type == SW_COPY) {
+ printf(" memcpy(): %" PRIu64 " "
+ "(min: %" PRIu64 ", max: %" PRIu64 ")\n", avg_start_cc,
+ avg_start_cc > 0U ? stats->min_start_cc : 0U,
+ avg_start_cc > 0U ? stats->max_start_cc : 0U);
+ } else {
+ printf(" odp_dma_transfer_start(): %" PRIu64 " "
+ "(min: %" PRIu64 ", max: %" PRIu64 ")\n", avg_start_cc,
+ avg_start_cc > 0U ? stats->min_start_cc : 0U,
+ avg_start_cc > 0U ? stats->max_start_cc : 0U);
+
+ avg_wait_cc = stats->wait_cnt > 0U ? stats->wait_cc / stats->wait_cnt : 0U;
+
+ if (config->compl_mode == POLL) {
+ printf(" odp_dma_transfer_done(): %" PRIu64 ""
+ " (min: %" PRIu64 ", max: %" PRIu64 ", x%" PRIu64 ""
+ " per transfer)\n", avg_wait_cc,
+ avg_wait_cc > 0U ? stats->min_wait_cc : 0U,
+ avg_wait_cc > 0U ? stats->max_wait_cc : 0U,
+ stats->trs_cnt > 0U ?
+ stats->trs_poll_cnt / stats->trs_cnt : 0U);
+ } else {
+ printf(" odp_schedule(): %" PRIu64 " "
+ " (min: %" PRIu64 ", max: %" PRIu64 ")\n", avg_wait_cc,
+ avg_wait_cc > 0U ? stats->min_wait_cc : 0U,
+ avg_wait_cc > 0U ? stats->max_wait_cc : 0U);
+ }
+ }
+
+ printf("\n");
+ }
+
+ avg_tot_tm = (double)tot_tm / config->num_workers / ODP_TIME_SEC_IN_NS;
+ printf(" total:\n"
+ " average time per transfer: %" PRIu64 " (min: %" PRIu64
+ ", max: %" PRIu64 ") ns\n"
+ " average cycles per transfer: %" PRIu64 " (min: %" PRIu64
+ ", max: %" PRIu64 ")\n"
+ " ops: ",
+ tot_trs_cnt > 0U ? tot_trs_tm / tot_trs_cnt : 0U,
+ tot_trs_cnt > 0U ? tot_min_tm : 0U,
+ tot_trs_cnt > 0U ? tot_max_tm : 0U,
+ tot_trs_cnt > 0U ? tot_trs_cc / tot_trs_cnt : 0U,
+ tot_trs_cnt > 0U ? tot_min_cc : 0U,
+ tot_trs_cnt > 0U ? tot_max_cc : 0U);
+ print_humanised(avg_tot_tm > 0U ? tot_completed / avg_tot_tm : 0U, "OPS");
+ printf(" speed: ");
+ print_humanised(avg_tot_tm > 0U ? tot_completed * data_cnt / avg_tot_tm : 0U, "B/s");
+ printf("\n");
+ printf("======================\n");
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t odph_opts;
+ odp_init_t init_param;
+ odp_instance_t odp_instance;
+ odp_shm_t shm_cfg = ODP_SHM_INVALID;
+ parse_result_t parse_res;
+ int ret = EXIT_SUCCESS;
+
+ argc = odph_parse_options(argc, argv);
+
+ if (odph_options(&odph_opts)) {
+ ODPH_ERR("Error while reading ODP helper options, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = odph_opts.mem_model;
+
+ if (odp_init_global(&odp_instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(odp_instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("ODP local init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
+ 0U);
+
+ if (shm_cfg == ODP_SHM_INVALID) {
+ ODPH_ERR("Error reserving shared memory\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ prog_conf = odp_shm_addr(shm_cfg);
+
+ if (prog_conf == NULL) {
+ ODPH_ERR("Error resolving shared memory address\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ parse_res = setup_program(argc, argv, prog_conf);
+
+ if (parse_res == PRS_NOK) {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (parse_res == PRS_TERM) {
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if (parse_res == PRS_NOT_SUP) {
+ ret = EXIT_NOT_SUP;
+ goto out;
+ }
+
+ if (odp_schedule_config(NULL) < 0) {
+ ODPH_ERR("Error configuring scheduler\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ prog_conf->odp_instance = odp_instance;
+ odp_atomic_init_u32(&prog_conf->is_running, 1U);
+
+ if (!setup_test(prog_conf)) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ if (prog_conf->time_sec > 0.001) {
+ struct timespec ts;
+
+ ts.tv_sec = prog_conf->time_sec;
+ ts.tv_nsec = (prog_conf->time_sec - ts.tv_sec) * ODP_TIME_SEC_IN_NS;
+ nanosleep(&ts, NULL);
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+ }
+
+ stop_test(prog_conf);
+ print_stats(prog_conf);
+
+out_test:
+ /* Release all resources that have been allocated during 'setup_test()'. */
+ teardown_test(prog_conf);
+
+out:
+ if (shm_cfg != ODP_SHM_INVALID)
+ (void)odp_shm_free(shm_cfg);
+
+ if (odp_term_local()) {
+ ODPH_ERR("ODP local terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(odp_instance)) {
+ ODPH_ERR("ODP global terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_dma_perf_run.sh b/test/performance/odp_dma_perf_run.sh
new file mode 100755
index 000000000..fb7b2bb34
--- /dev/null
+++ b/test/performance/odp_dma_perf_run.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022-2023 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+BIN_NAME=odp_dma_perf
+SEGC=0
+SEGS=1024
+INFL=1
+TIME=0.1
+TESTS_RUN=0
+
+check_result()
+{
+ if [ $1 -eq 0 ]; then
+ TESTS_RUN=`expr $TESTS_RUN + 1`
+ elif [ $1 -eq 1 ]; then
+ echo "Test FAILED, exiting"
+ exit 1
+ else
+ echo "Test SKIPPED"
+ fi
+}
+
+echo "odp_dma_perf: synchronous DMA transfer 1"
+echo "===================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 0 -i $SEGC -o $SEGC -s $SEGS -S 0 -f $INFL -T $TIME -v
+
+check_result $?
+
+echo "odp_dma_perf: synchronous DMA transfer 2"
+echo "===================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 0 -i $SEGC -o $SEGC -s $SEGS -S 1 -f $INFL -T $TIME -v
+
+check_result $?
+
+echo "odp_dma_perf: asynchronous DMA transfer 1"
+echo "====================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 1 -i $SEGC -o $SEGC -s $SEGS -S 2 -m 0 -f $INFL -T $TIME -v
+
+check_result $?
+
+echo "odp_dma_perf: asynchronous DMA transfer 2"
+echo "====================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 1 -i $SEGC -o $SEGC -s $SEGS -S 3 -m 1 -f $INFL -T $TIME -v
+
+check_result $?
+
+echo "odp_dma_perf: SW transfer 1"
+echo "====================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 2 -i $SEGC -o $SEGC -s $SEGS -S 0 -f $INFL -T $TIME -v
+
+check_result $?
+
+echo "odp_dma_perf: SW transfer 2"
+echo "====================================="
+
+${TEST_DIR}/${BIN_NAME}${EXEEXT} -t 2 -i $SEGC -o $SEGC -s $SEGS -S 2 -f $INFL -T $TIME -v
+
+check_result $?
+
+if [ $TESTS_RUN -eq 0 ]; then
+ exit 77
+fi
+
+exit 0
diff --git a/test/performance/odp_dmafwd.c b/test/performance/odp_dmafwd.c
new file mode 100644
index 000000000..694973ce0
--- /dev/null
+++ b/test/performance/odp_dmafwd.c
@@ -0,0 +1,1475 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+/**
+ * @example odp_dmafwd.c
+ *
+ * This tester application can be used to profile the performance of an ODP DMA implementation.
+ * Tester workflow consists of packet reception, copy and forwarding steps. Packets are first
+ * received from configured interfaces after which packets are copied, either with plain SW memory
+ * copy or with DMA offload copy. Finally, copied packets are echoed back to the sender(s).
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define EXIT_NOT_SUP 2
+#define PROG_NAME "odp_dmafwd"
+#define DELIMITER ","
+
+enum {
+ SW_COPY = 0U,
+ DMA_COPY_EV,
+ DMA_COPY_POLL
+};
+
+#define DEF_CPY_TYPE SW_COPY
+#define DEF_CNT 32768U
+#define DEF_LEN 1024U
+#define DEF_WORKERS 1U
+#define DEF_TIME 0U
+
+#define MAX_IFS 2U
+#define MAX_OUT_QS 32U
+#define MAX_BURST 32U
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+#define DIV_IF(a, b) ((b) > 0U ? ((a) / (b)) : 0U)
+
+ODP_STATIC_ASSERT(MAX_IFS < UINT8_MAX, "Too large maximum interface count");
+ODP_STATIC_ASSERT(MAX_OUT_QS < UINT8_MAX, "Too large maximum output queue count");
+
+typedef struct {
+ uint32_t burst_size;
+ uint32_t num_pkts;
+ uint32_t pkt_len;
+ uint32_t cache_size;
+} dynamic_defs_t;
+
+typedef enum {
+ PRS_OK,
+ PRS_NOK,
+ PRS_TERM,
+ PRS_NOT_SUP
+} parse_result_t;
+
+typedef struct prog_config_s prog_config_t;
+
+typedef struct {
+ uint64_t copy_errs;
+ uint64_t trs;
+ uint64_t start_errs;
+ uint64_t trs_errs;
+ uint64_t buf_alloc_errs;
+ uint64_t compl_alloc_errs;
+ uint64_t pkt_alloc_errs;
+ uint64_t trs_poll_errs;
+ uint64_t trs_polled;
+ uint64_t fwd_pkts;
+ uint64_t discards;
+ uint64_t sched_cc;
+ uint64_t tot_cc;
+ uint64_t sched_rounds;
+} stats_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ prog_config_t *prog_config;
+ odp_dma_t dma_handle;
+ odp_pool_t compl_pool;
+ odp_pool_t copy_pool;
+ odp_pool_t trs_pool;
+ odp_queue_t compl_q;
+ odp_stash_t inflight_stash;
+ stats_t stats;
+ int thr_idx;
+} thread_config_t;
+
+typedef struct pktio_s {
+ odp_pktout_queue_t out_qs[MAX_OUT_QS];
+ char *name;
+ odp_pktio_t handle;
+ uint8_t num_out_qs;
+} pktio_t;
+
+typedef struct {
+ odp_packet_t src_pkts[MAX_BURST];
+ odp_packet_t dst_pkts[MAX_BURST];
+ pktio_t *pktio;
+ int num;
+} transfer_t;
+
+/* Function for initializing transfer structures */
+typedef transfer_t *(*init_fn_t)(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
+ odp_dma_seg_t *dst_segs, pktio_t *pktio, thread_config_t *config);
+/* Function for starting transfers */
+typedef odp_bool_t (*start_fn_t)(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, thread_config_t *config);
+/* Function for setting up packets for copy */
+typedef void (*pkt_fn_t)(odp_packet_t pkts[], int num, pktio_t *pktio, init_fn_t init_fn,
+ start_fn_t start_fn, thread_config_t *config);
+/* Function for draining and tearing down inflight operations */
+typedef void (*drain_fn_t)(thread_config_t *config);
+
+typedef struct prog_config_s {
+ uint8_t pktio_idx_map[ODP_PKTIO_MAX_INDEX + 1];
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ thread_config_t thread_config[MAX_WORKERS];
+ pktio_t pktios[MAX_IFS];
+ dynamic_defs_t dyn_defs;
+ odp_instance_t odp_instance;
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ odp_atomic_u32_t is_running;
+ odp_pool_t pktio_pool;
+ odp_pool_t copy_pool;
+ odp_pool_t trs_pool;
+
+ struct {
+ init_fn_t init_fn;
+ start_fn_t start_fn;
+ pkt_fn_t pkt_fn;
+ drain_fn_t drain_fn;
+ };
+
+ uint64_t inflight_obj_size;
+ uint32_t burst_size;
+ uint32_t num_pkts;
+ uint32_t pkt_len;
+ uint32_t cache_size;
+ uint32_t num_inflight;
+ uint32_t trs_cache_size;
+ uint32_t compl_cache_size;
+ uint32_t stash_cache_size;
+ double time_sec;
+ odp_stash_type_t stash_type;
+ int num_thrs;
+ uint8_t num_ifs;
+ uint8_t copy_type;
+} prog_config_t;
+
+typedef struct {
+ odp_packet_t pkts[MAX_BURST * 2U];
+ pktio_t *pktio;
+ int num;
+} pkt_vec_t;
+
+static prog_config_t *prog_conf;
+
+static void terminate(int signal ODP_UNUSED)
+{
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+}
+
+static void init_config(prog_config_t *config)
+{
+ odp_dma_capability_t dma_capa;
+ uint32_t burst_size;
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+ thread_config_t *thr;
+
+ memset(config, 0, sizeof(*config));
+
+ if (odp_dma_capability(&dma_capa) == 0) {
+ burst_size = ODPH_MIN(dma_capa.max_src_segs, dma_capa.max_dst_segs);
+ burst_size = ODPH_MIN(burst_size, MAX_BURST);
+ config->dyn_defs.burst_size = burst_size;
+ }
+
+ if (odp_pool_capability(&pool_capa) == 0) {
+ config->dyn_defs.num_pkts = pool_capa.pkt.max_num > 0U ?
+ ODPH_MIN(pool_capa.pkt.max_num, DEF_CNT) : DEF_CNT;
+ config->dyn_defs.pkt_len = pool_capa.pkt.max_len > 0U ?
+ ODPH_MIN(pool_capa.pkt.max_len, DEF_LEN) : DEF_LEN;
+ odp_pool_param_init(&pool_param);
+ config->dyn_defs.cache_size = pool_param.pkt.cache_size;
+ }
+
+ config->pktio_pool = ODP_POOL_INVALID;
+ config->copy_pool = ODP_POOL_INVALID;
+ config->trs_pool = ODP_POOL_INVALID;
+ config->burst_size = config->dyn_defs.burst_size;
+ config->num_pkts = config->dyn_defs.num_pkts;
+ config->pkt_len = config->dyn_defs.pkt_len;
+ config->cache_size = config->dyn_defs.cache_size;
+ config->time_sec = DEF_TIME;
+ config->num_thrs = DEF_WORKERS;
+ config->copy_type = DEF_CPY_TYPE;
+
+ for (int i = 0; i < MAX_WORKERS; ++i) {
+ thr = &config->thread_config[i];
+ thr->dma_handle = ODP_DMA_INVALID;
+ thr->compl_pool = ODP_POOL_INVALID;
+ thr->compl_q = ODP_QUEUE_INVALID;
+ thr->inflight_stash = ODP_STASH_INVALID;
+ }
+
+ for (uint32_t i = 0U; i < MAX_IFS; ++i)
+ config->pktios[i].handle = ODP_PKTIO_INVALID;
+}
+
+static void print_usage(dynamic_defs_t *dyn_defs)
+{
+ printf("\n"
+ "DMA performance tester with packet I/O. Receive and forward packets after\n"
+ "software copy or DMA offload copy.\n"
+ "\n"
+ "Usage: " PROG_NAME " OPTIONS\n"
+ "\n"
+ " E.g. " PROG_NAME " -i eth0\n"
+ " " PROG_NAME " -i eth0 -t 0\n"
+ " " PROG_NAME " -i eth0 -t 1 -b 15 -l 4096 -c 5\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ "\n"
+ " -i, --interfaces Ethernet interfaces for packet I/O, comma-separated, no\n"
+ " spaces.\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ "\n"
+ " -t, --copy_type Type of copy. %u by default.\n"
+ " 0: SW\n"
+ " 1: DMA with event completion\n"
+ " 2: DMA with poll completion\n"
+ " -b, --burst_size Copy burst size. This many packets are accumulated before\n"
+ " copy. %u by default.\n"
+ " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
+ " %u by default.\n"
+ " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
+ " default.\n"
+ " -c, --worker_count Amount of workers. %u by default.\n"
+ " -C, --cache_size Maximum cache size for pools. %u by default.\n"
+ " -T, --time_sec Time in seconds to run. 0 means infinite. %u by default.\n"
+ " -h, --help This help.\n"
+ "\n", DEF_CPY_TYPE, dyn_defs->burst_size, dyn_defs->num_pkts, dyn_defs->pkt_len,
+ DEF_WORKERS, dyn_defs->cache_size, DEF_TIME);
+}
+
+static void parse_interfaces(prog_config_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, DELIMITER);
+
+ while (tmp && config->num_ifs < MAX_IFS) {
+ config->pktios[config->num_ifs].name = strdup(tmp);
+
+ if (config->pktios[config->num_ifs].name != NULL)
+ ++config->num_ifs;
+
+ tmp = strtok(NULL, DELIMITER);
+ }
+
+ free(tmp_str);
+}
+
+static odp_bool_t get_stash_capa(odp_stash_capability_t *stash_capa, odp_stash_type_t *stash_type)
+{
+ if (odp_stash_capability(stash_capa, ODP_STASH_TYPE_FIFO) == 0) {
+ *stash_type = ODP_STASH_TYPE_FIFO;
+ return true;
+ }
+
+ if (odp_stash_capability(stash_capa, ODP_STASH_TYPE_DEFAULT) == 0) {
+ *stash_type = ODP_STASH_TYPE_DEFAULT;
+ return true;
+ }
+
+ return false;
+}
+
+static parse_result_t check_options(prog_config_t *config)
+{
+ odp_dma_capability_t dma_capa;
+ uint32_t burst_size;
+ odp_stash_capability_t stash_capa;
+ const uint64_t obj_size = sizeof(odp_dma_transfer_id_t);
+ uint64_t max_num;
+ odp_pool_capability_t pool_capa;
+
+ if (config->num_ifs == 0U) {
+ ODPH_ERR("Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
+ MAX_IFS);
+ return PRS_NOK;
+ }
+
+ if (config->copy_type != SW_COPY && config->copy_type != DMA_COPY_EV &&
+ config->copy_type != DMA_COPY_POLL) {
+ ODPH_ERR("Invalid copy type: %u\n", config->copy_type);
+ return PRS_NOK;
+ }
+
+ if (config->num_thrs <= 0 || config->num_thrs > MAX_WORKERS) {
+ ODPH_ERR("Invalid worker count: %d (min: 1, max: %d)\n", config->num_thrs,
+ MAX_WORKERS);
+ return PRS_NOK;
+ }
+
+ if (odp_dma_capability(&dma_capa) < 0) {
+ ODPH_ERR("Error querying DMA capabilities\n");
+ return PRS_NOK;
+ }
+
+ if ((uint32_t)config->num_thrs > dma_capa.max_sessions) {
+ ODPH_ERR("Unsupported DMA session count: %d (max: %u)\n", config->num_thrs,
+ dma_capa.max_sessions);
+ return PRS_NOT_SUP;
+ }
+
+ burst_size = ODPH_MIN(dma_capa.max_src_segs, dma_capa.max_dst_segs);
+ burst_size = ODPH_MIN(burst_size, MAX_BURST);
+
+ if (config->burst_size == 0U || config->burst_size > burst_size) {
+ ODPH_ERR("Invalid segment count for DMA: %u (min: 1, max: %u)\n",
+ config->burst_size, burst_size);
+ return PRS_NOK;
+ }
+
+ if (config->pkt_len > dma_capa.max_seg_len) {
+ ODPH_ERR("Invalid packet length for DMA: %u (max: %u)\n", config->pkt_len,
+ dma_capa.max_seg_len);
+ return PRS_NOK;
+ }
+
+ config->num_inflight = dma_capa.max_transfers;
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("Error querying pool capabilities\n");
+ return PRS_NOK;
+ }
+
+ if (config->cache_size < pool_capa.pkt.min_cache_size ||
+ config->cache_size > pool_capa.pkt.max_cache_size) {
+ ODPH_ERR("Invalid pool cache size: %u (min: %u, max: %u)\n", config->cache_size,
+ pool_capa.pkt.min_cache_size, pool_capa.pkt.max_cache_size);
+ return PRS_NOK;
+ }
+
+ if (config->copy_type != SW_COPY)
+ config->trs_cache_size = ODPH_MIN(ODPH_MAX(config->cache_size,
+ pool_capa.buf.min_cache_size),
+ pool_capa.buf.max_cache_size);
+
+ if (config->copy_type == DMA_COPY_EV) {
+ if ((dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) == 0U ||
+ !dma_capa.queue_type_sched) {
+ ODPH_ERR("Unsupported DMA completion mode: event (mode support: %x, "
+ "scheduled queue support: %u)\n", dma_capa.compl_mode_mask,
+ dma_capa.queue_type_sched);
+ return PRS_NOT_SUP;
+ }
+
+ if ((uint32_t)config->num_thrs > dma_capa.pool.max_pools) {
+ ODPH_ERR("Invalid amount of DMA completion pools: %d (max: %u)\n",
+ config->num_thrs, dma_capa.pool.max_pools);
+ return PRS_NOK;
+ }
+
+ if (config->num_inflight > dma_capa.pool.max_num) {
+ ODPH_ERR("Invalid amount of DMA completion events: %u (max: %u)\n",
+ config->num_inflight, dma_capa.pool.max_num);
+ return PRS_NOK;
+ }
+
+ config->compl_cache_size = ODPH_MIN(ODPH_MAX(config->cache_size,
+ dma_capa.pool.min_cache_size),
+ dma_capa.pool.max_cache_size);
+ } else if (config->copy_type == DMA_COPY_POLL) {
+ if ((dma_capa.compl_mode_mask & ODP_DMA_COMPL_POLL) == 0U) {
+ ODPH_ERR("Unsupported DMA completion mode: poll (mode support: %x)\n",
+ dma_capa.compl_mode_mask);
+ return PRS_NOT_SUP;
+ }
+
+ if (!get_stash_capa(&stash_capa, &config->stash_type)) {
+ ODPH_ERR("Error querying stash capabilities\n");
+ return PRS_NOK;
+ }
+
+ if ((uint32_t)config->num_thrs > stash_capa.max_stashes) {
+ ODPH_ERR("Invalid amount of stashes: %d (max: %u)\n", config->num_thrs,
+ stash_capa.max_stashes);
+ return PRS_NOK;
+ }
+
+ if (obj_size == sizeof(uint8_t)) {
+ max_num = stash_capa.max_num.u8;
+ } else if (obj_size == sizeof(uint16_t)) {
+ max_num = stash_capa.max_num.u16;
+ } else if (obj_size <= sizeof(uint32_t)) {
+ max_num = stash_capa.max_num.u32;
+ } else if (obj_size <= sizeof(uint64_t)) {
+ max_num = stash_capa.max_num.u64;
+ } else if (obj_size <= sizeof(odp_u128_t)) {
+ max_num = stash_capa.max_num.u128;
+ } else {
+ ODPH_ERR("Invalid stash object size: %" PRIu64 "\n", obj_size);
+ return PRS_NOK;
+ }
+
+ if (config->num_inflight > max_num) {
+ ODPH_ERR("Invalid stash size: %u (max: %" PRIu64 ")\n",
+ config->num_inflight, max_num);
+ return PRS_NOK;
+ }
+
+ config->inflight_obj_size = obj_size;
+ config->stash_cache_size = ODPH_MIN(config->cache_size, stash_capa.max_cache_size);
+ }
+
+ if (config->num_pkts == 0U ||
+ (pool_capa.pkt.max_num > 0U && config->num_pkts > pool_capa.pkt.max_num)) {
+ ODPH_ERR("Invalid pool packet count: %u (min: 1, max: %u)\n", config->num_pkts,
+ pool_capa.pkt.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->pkt_len == 0U ||
+ (pool_capa.pkt.max_len > 0U && config->pkt_len > pool_capa.pkt.max_len)) {
+ ODPH_ERR("Invalid pool packet length: %u (min: 1, max: %u)\n", config->pkt_len,
+ pool_capa.pkt.max_len);
+ return PRS_NOK;
+ }
+
+ if (config->num_inflight > pool_capa.buf.max_num) {
+ ODPH_ERR("Invalid pool buffer count: %u (max: %u)\n", config->num_inflight,
+ pool_capa.buf.max_num);
+ return PRS_NOK;
+ }
+
+ return PRS_OK;
+}
+
+static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
+{
+ int opt, long_index;
+
+ static const struct option longopts[] = {
+ { "interfaces", required_argument, NULL, 'i' },
+ { "copy_type", required_argument, NULL, 't' },
+ { "burst_size", required_argument, NULL, 'b' },
+ { "num_pkts", required_argument, NULL, 'n' },
+ { "pkt_len", required_argument, NULL, 'l' },
+ { "worker_count", required_argument, NULL, 'c' },
+ { "cache_size", required_argument, NULL, 'C' },
+ { "time_sec", required_argument, NULL, 'T' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "i:t:b:n:l:c:C:T:h";
+
+ init_config(config);
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'i':
+ parse_interfaces(config, optarg);
+ break;
+ case 't':
+ config->copy_type = atoi(optarg);
+ break;
+ case 'b':
+ config->burst_size = atoi(optarg);
+ break;
+ case 'n':
+ config->num_pkts = atoi(optarg);
+ break;
+ case 'l':
+ config->pkt_len = atoi(optarg);
+ break;
+ case 'c':
+ config->num_thrs = atoi(optarg);
+ break;
+ case 'C':
+ config->cache_size = atoi(optarg);
+ break;
+ case 'T':
+ config->time_sec = atof(optarg);
+ break;
+ case 'h':
+ print_usage(&config->dyn_defs);
+ return PRS_TERM;
+ case '?':
+ default:
+ print_usage(&config->dyn_defs);
+ return PRS_NOK;
+ }
+ }
+
+ return check_options(config);
+}
+
+static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
+{
+ struct sigaction action = { .sa_handler = terminate };
+
+ if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
+ sigaddset(&action.sa_mask, SIGTERM) == -1 ||
+ sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
+ sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
+ ODPH_ERR("Error installing signal handler\n");
+ return PRS_NOK;
+ }
+
+ return parse_options(argc, argv, config);
+}
+
+static inline int send_packets(odp_pktout_queue_t queue, odp_packet_t pkts[], int num)
+{
+ int ret = odp_pktout_send(queue, pkts, num);
+
+ if (odp_unlikely(ret < num)) {
+ ret = ret < 0 ? 0 : ret;
+ odp_packet_free_multi(&pkts[ret], num - ret);
+ }
+
+ return ret;
+}
+
+static void sw_copy_and_send_packets(odp_packet_t pkts[], int num, pktio_t *pktio,
+ init_fn_t init_fn ODP_UNUSED, start_fn_t start_fn ODP_UNUSED,
+ thread_config_t *config)
+{
+ odp_packet_t old_pkt, new_pkt;
+ odp_pool_t copy_pool = config->copy_pool;
+ odp_packet_t out_pkts[num];
+ int num_out_pkts = 0, num_sent;
+ stats_t *stats = &config->stats;
+
+ for (int i = 0; i < num; ++i) {
+ old_pkt = pkts[i];
+ new_pkt = odp_packet_copy(old_pkt, copy_pool);
+
+ if (new_pkt != ODP_PACKET_INVALID)
+ out_pkts[num_out_pkts++] = new_pkt;
+ else
+ ++stats->copy_errs;
+
+ odp_packet_free(old_pkt);
+ }
+
+ if (num_out_pkts > 0) {
+ num_sent = send_packets(pktio->out_qs[config->thr_idx % pktio->num_out_qs],
+ out_pkts, num_out_pkts);
+ stats->fwd_pkts += num_sent;
+ stats->discards += num_out_pkts - num_sent;
+ }
+}
+
+static transfer_t *init_dma_ev_trs(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
+ odp_dma_seg_t *dst_segs, pktio_t *pktio,
+ thread_config_t *config)
+{
+ odp_buffer_t buf;
+ stats_t *stats = &config->stats;
+ transfer_t *trs;
+ odp_dma_compl_t c_ev;
+
+ buf = odp_buffer_alloc(config->trs_pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID)) {
+ ++stats->buf_alloc_errs;
+ return NULL;
+ }
+
+ trs = (transfer_t *)odp_buffer_addr(buf);
+ trs->num = 0;
+ trs->pktio = pktio;
+ trs_param->src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param->dst_format = ODP_DMA_FORMAT_PACKET;
+ trs_param->num_src = 0U;
+ trs_param->num_dst = 0U;
+ trs_param->src_seg = src_segs;
+ trs_param->dst_seg = dst_segs;
+ compl_param->compl_mode = ODP_DMA_COMPL_EVENT;
+ c_ev = odp_dma_compl_alloc(config->compl_pool);
+
+ if (odp_unlikely(c_ev == ODP_DMA_COMPL_INVALID)) {
+ odp_buffer_free(buf);
+ ++stats->compl_alloc_errs;
+ return NULL;
+ }
+
+ compl_param->event = odp_dma_compl_to_event(c_ev);
+ compl_param->queue = config->compl_q;
+ compl_param->user_ptr = buf;
+ memset(src_segs, 0, sizeof(*src_segs) * MAX_BURST);
+ memset(dst_segs, 0, sizeof(*dst_segs) * MAX_BURST);
+
+ return trs;
+}
+
+static transfer_t *init_dma_poll_trs(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
+ odp_dma_seg_t *dst_segs, pktio_t *pktio,
+ thread_config_t *config)
+{
+ odp_buffer_t buf;
+ stats_t *stats = &config->stats;
+ transfer_t *trs;
+
+ buf = odp_buffer_alloc(config->trs_pool);
+
+ if (odp_unlikely(buf == ODP_BUFFER_INVALID)) {
+ ++stats->buf_alloc_errs;
+ return NULL;
+ }
+
+ trs = (transfer_t *)odp_buffer_addr(buf);
+ trs->num = 0;
+ trs->pktio = pktio;
+ trs_param->src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param->dst_format = ODP_DMA_FORMAT_PACKET;
+ trs_param->num_src = 0U;
+ trs_param->num_dst = 0U;
+ trs_param->src_seg = src_segs;
+ trs_param->dst_seg = dst_segs;
+ compl_param->compl_mode = ODP_DMA_COMPL_POLL;
+ compl_param->transfer_id = odp_dma_transfer_id_alloc(config->dma_handle);
+
+ if (odp_unlikely(compl_param->transfer_id == ODP_DMA_TRANSFER_ID_INVALID)) {
+ odp_buffer_free(buf);
+ ++stats->compl_alloc_errs;
+ return NULL;
+ }
+
+ compl_param->user_ptr = buf;
+ memset(src_segs, 0, sizeof(*src_segs) * MAX_BURST);
+ memset(dst_segs, 0, sizeof(*dst_segs) * MAX_BURST);
+
+ return trs;
+}
+
+static odp_bool_t start_dma_ev_trs(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, thread_config_t *config)
+{
+ const int ret = odp_dma_transfer_start(config->dma_handle, trs_param, compl_param);
+
+ if (odp_unlikely(ret <= 0)) {
+ odp_buffer_free(compl_param->user_ptr);
+ odp_event_free(compl_param->event);
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t start_dma_poll_trs(odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_param_t *compl_param, thread_config_t *config)
+{
+ const int ret = odp_dma_transfer_start(config->dma_handle, trs_param, compl_param);
+
+ if (odp_unlikely(ret <= 0)) {
+ odp_buffer_free(compl_param->user_ptr);
+ odp_dma_transfer_id_free(config->dma_handle, compl_param->transfer_id);
+ return false;
+ }
+
+ if (odp_unlikely(odp_stash_put(config->inflight_stash, &compl_param->transfer_id, 1) != 1))
+ /* Should not happen, but make it visible if it somehow does */
+ ODPH_ABORT("DMA inflight transfer stash overflow, aborting");
+
+ return true;
+}
+
+static void dma_copy(odp_packet_t pkts[], int num, pktio_t *pktio, init_fn_t init_fn,
+ start_fn_t start_fn, thread_config_t *config)
+{
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_compl_param_t compl_param;
+ odp_packet_t pkt;
+ transfer_t *trs = NULL;
+ odp_dma_seg_t src_segs[MAX_BURST], dst_segs[MAX_BURST];
+ uint32_t num_segs = 0U, pkt_len;
+ odp_pool_t copy_pool = config->copy_pool;
+ stats_t *stats = &config->stats;
+
+ odp_dma_transfer_param_init(&trs_param);
+ odp_dma_compl_param_init(&compl_param);
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+
+ if (odp_unlikely(trs == NULL)) {
+ trs = init_fn(&trs_param, &compl_param, src_segs, dst_segs, pktio, config);
+
+ if (trs == NULL) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ }
+
+ pkt_len = odp_packet_len(pkt);
+ src_segs[num_segs].packet = pkt;
+ src_segs[num_segs].len = pkt_len;
+ dst_segs[num_segs].packet = odp_packet_alloc(copy_pool, pkt_len);
+
+ if (odp_unlikely(dst_segs[num_segs].packet == ODP_PACKET_INVALID)) {
+ odp_packet_free(pkt);
+ ++stats->pkt_alloc_errs;
+ continue;
+ }
+
+ dst_segs[num_segs].len = pkt_len;
+ trs->src_pkts[num_segs] = src_segs[num_segs].packet;
+ trs->dst_pkts[num_segs] = dst_segs[num_segs].packet;
+ ++trs->num;
+ ++trs_param.num_src;
+ ++trs_param.num_dst;
+ ++num_segs;
+ }
+
+ if (num_segs > 0U)
+ if (odp_unlikely(!start_fn(&trs_param, &compl_param, config))) {
+ odp_packet_free_multi(trs->src_pkts, trs->num);
+ odp_packet_free_multi(trs->dst_pkts, trs->num);
+ ++stats->start_errs;
+ }
+}
+
+static void drain_events(thread_config_t *config ODP_UNUSED)
+{
+ odp_event_t ev;
+ odp_event_type_t type;
+ odp_dma_result_t res;
+ odp_buffer_t buf;
+ transfer_t *trs;
+
+ while (true) {
+ ev = odp_schedule(NULL, odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ type = odp_event_type(ev);
+
+ if (type == ODP_EVENT_DMA_COMPL) {
+ memset(&res, 0, sizeof(res));
+ odp_dma_compl_result(odp_dma_compl_from_event(ev), &res);
+ buf = (odp_buffer_t)res.user_ptr;
+ trs = (transfer_t *)odp_buffer_addr(buf);
+ odp_packet_free_multi(trs->src_pkts, trs->num);
+ odp_packet_free_multi(trs->dst_pkts, trs->num);
+ odp_buffer_free(buf);
+ }
+
+ odp_event_free(ev);
+ }
+}
+
+static void drain_polled(thread_config_t *config)
+{
+ odp_dma_transfer_id_t id;
+ odp_dma_result_t res;
+ int ret;
+ odp_buffer_t buf;
+ transfer_t *trs;
+
+ while (true) {
+ if (odp_stash_get(config->inflight_stash, &id, 1) != 1)
+ break;
+
+ memset(&res, 0, sizeof(res));
+
+ do {
+ ret = odp_dma_transfer_done(config->dma_handle, id, &res);
+ } while (ret == 0);
+
+ odp_dma_transfer_id_free(config->dma_handle, id);
+
+ if (ret < 0)
+ continue;
+
+ buf = (odp_buffer_t)res.user_ptr;
+ trs = (transfer_t *)odp_buffer_addr(buf);
+ odp_packet_free_multi(trs->src_pkts, trs->num);
+ odp_packet_free_multi(trs->dst_pkts, trs->num);
+ odp_buffer_free(buf);
+ }
+}
+
+static odp_bool_t setup_copy(prog_config_t *config)
+{
+ odp_pool_param_t pool_param;
+ thread_config_t *thr;
+ const odp_dma_param_t dma_param = {
+ .direction = ODP_DMA_MAIN_TO_MAIN,
+ .type = ODP_DMA_TYPE_COPY,
+ .compl_mode_mask = ODP_DMA_COMPL_EVENT | ODP_DMA_COMPL_POLL,
+ .mt_mode = ODP_DMA_MT_SERIAL,
+ .order = ODP_DMA_ORDER_NONE };
+ odp_dma_pool_param_t compl_pool_param;
+ odp_queue_param_t queue_param;
+ odp_stash_param_t stash_param;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.seg_len = config->pkt_len;
+ pool_param.pkt.len = config->pkt_len;
+ pool_param.pkt.num = config->num_pkts;
+ pool_param.pkt.cache_size = config->cache_size;
+ pool_param.type = ODP_POOL_PACKET;
+ config->copy_pool = odp_pool_create(PROG_NAME "_copy", &pool_param);
+
+ if (config->copy_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating packet copy pool\n");
+ return false;
+ }
+
+ if (config->copy_type == SW_COPY) {
+ config->pkt_fn = sw_copy_and_send_packets;
+
+ for (int i = 0; i < config->num_thrs; ++i)
+ config->thread_config[i].copy_pool = config->copy_pool;
+
+ return true;
+ }
+
+ pool_param.buf.num = config->num_inflight;
+ pool_param.buf.size = sizeof(transfer_t);
+ pool_param.buf.cache_size = config->trs_cache_size;
+ pool_param.type = ODP_POOL_BUFFER;
+ config->trs_pool = odp_pool_create(PROG_NAME "_dma_trs", &pool_param);
+
+ if (config->trs_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating DMA transfer tracking pool\n");
+ return false;
+ }
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ thr = &config->thread_config[i];
+ thr->copy_pool = config->copy_pool;
+ thr->trs_pool = config->trs_pool;
+ thr->dma_handle = odp_dma_create(PROG_NAME "_dma", &dma_param);
+
+ if (thr->dma_handle == ODP_DMA_INVALID) {
+ ODPH_ERR("Error creating DMA session\n");
+ return false;
+ }
+
+ if (config->copy_type == DMA_COPY_EV) {
+ odp_dma_pool_param_init(&compl_pool_param);
+ compl_pool_param.num = config->num_inflight;
+ compl_pool_param.cache_size = config->compl_cache_size;
+ thr->compl_pool = odp_dma_pool_create(PROG_NAME "_dma_compl",
+ &compl_pool_param);
+
+ if (thr->compl_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating DMA event completion pool\n");
+ return false;
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.prio = odp_schedule_max_prio();
+ thr->compl_q = odp_queue_create(PROG_NAME "_dma_compl", &queue_param);
+
+ if (thr->compl_q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error creating DMA completion queue\n");
+ return false;
+ }
+
+ config->init_fn = init_dma_ev_trs;
+ config->start_fn = start_dma_ev_trs;
+ config->drain_fn = drain_events;
+ } else {
+ odp_stash_param_init(&stash_param);
+ stash_param.type = config->stash_type;
+ stash_param.put_mode = ODP_STASH_OP_LOCAL;
+ stash_param.get_mode = ODP_STASH_OP_LOCAL;
+ stash_param.num_obj = config->num_inflight;
+ stash_param.obj_size = config->inflight_obj_size;
+ stash_param.cache_size = config->stash_cache_size;
+ thr->inflight_stash = odp_stash_create("_dma_inflight", &stash_param);
+
+ if (thr->inflight_stash == ODP_STASH_INVALID) {
+ ODPH_ERR("Error creating DMA inflight transfer stash\n");
+ return false;
+ }
+
+ config->init_fn = init_dma_poll_trs;
+ config->start_fn = start_dma_poll_trs;
+ config->drain_fn = drain_polled;
+ }
+ }
+
+ config->pkt_fn = dma_copy;
+
+ return true;
+}
+
+static odp_bool_t setup_pktios(prog_config_t *config)
+{
+ odp_pool_param_t pool_param;
+ pktio_t *pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ uint32_t num_input_qs, num_output_qs;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.seg_len = config->pkt_len;
+ pool_param.pkt.len = config->pkt_len;
+ pool_param.pkt.num = config->num_pkts;
+ pool_param.pkt.cache_size = config->cache_size;
+ pool_param.type = ODP_POOL_PACKET;
+ config->pktio_pool = odp_pool_create(PROG_NAME, &pool_param);
+
+ if (config->pktio_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating packet I/O pool\n");
+ return false;
+ }
+
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ pktio = &config->pktios[i];
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+ pktio->handle = odp_pktio_open(pktio->name, config->pktio_pool, &pktio_param);
+
+ if (pktio->handle == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Error opening packet I/O (%s)\n", pktio->name);
+ return false;
+ }
+
+ config->pktio_idx_map[odp_pktio_index(pktio->handle)] = i;
+
+ if (odp_pktio_capability(pktio->handle, &capa) < 0) {
+ ODPH_ERR("Error querying packet I/O capabilities (%s)\n", pktio->name);
+ return false;
+ }
+
+ num_input_qs = ODPH_MIN((uint32_t)config->num_thrs, capa.max_input_queues);
+ num_output_qs = ODPH_MIN((uint32_t)config->num_thrs, capa.max_output_queues);
+ num_output_qs = ODPH_MIN(num_output_qs, MAX_OUT_QS);
+ odp_pktin_queue_param_init(&pktin_param);
+
+ if (num_input_qs > 1) {
+ pktin_param.hash_enable = true;
+ pktin_param.hash_proto.proto.ipv4 = 1U;
+ }
+
+ pktin_param.num_queues = num_input_qs;
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+
+ if (odp_pktin_queue_config(pktio->handle, &pktin_param) < 0) {
+ ODPH_ERR("Error configuring packet I/O input queues (%s)\n", pktio->name);
+ return false;
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+
+ if (num_output_qs == (uint32_t)config->num_thrs)
+ pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+ pktout_param.num_queues = num_output_qs;
+ pktio->num_out_qs = num_output_qs;
+
+ if (odp_pktout_queue_config(pktio->handle, &pktout_param) < 0) {
+ ODPH_ERR("Error configuring packet I/O output queues (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (odp_pktout_queue(pktio->handle, pktio->out_qs, num_output_qs) !=
+ (int)num_output_qs) {
+ ODPH_ERR("Error querying packet I/O output queues (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (odp_pktio_start(pktio->handle) < 0) {
+ ODPH_ERR("Error starting packet I/O (%s)\n", pktio->name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static inline void send_dma_poll_trs_pkts(int burst_size, thread_config_t *config)
+{
+ odp_stash_t stash_handle = config->inflight_stash;
+ odp_dma_transfer_id_t ids[burst_size], id;
+ int32_t num;
+ odp_dma_t dma_handle = config->dma_handle;
+ odp_dma_result_t res;
+ int ret;
+ odp_buffer_t buf;
+ transfer_t *trs;
+ pktio_t *pktio;
+ int num_sent;
+ stats_t *stats = &config->stats;
+
+ while (true) {
+ num = odp_stash_get(stash_handle, &ids, burst_size);
+
+ if (num <= 0)
+ break;
+
+ for (int32_t i = 0; i < num; ++i) {
+ id = ids[i];
+ ret = odp_dma_transfer_done(dma_handle, id, &res);
+
+ if (ret == 0) {
+ if (odp_unlikely(odp_stash_put(stash_handle, &id, 1) != 1))
+ /* Should not happen, but make it visible if it somehow
+ * does */
+ ODPH_ABORT("DMA inflight transfer stash overflow,"
+ " aborting");
+
+ ++stats->trs_polled;
+ continue;
+ }
+
+ odp_dma_transfer_id_free(dma_handle, id);
+
+ if (ret < 0) {
+ ++stats->trs_poll_errs;
+ continue;
+ }
+
+ buf = (odp_buffer_t)res.user_ptr;
+ trs = (transfer_t *)odp_buffer_addr(buf);
+
+ if (res.success) {
+ pktio = trs->pktio;
+ num_sent = send_packets(pktio->out_qs[config->thr_idx %
+ pktio->num_out_qs],
+ trs->dst_pkts, trs->num);
+ ++stats->trs;
+ stats->fwd_pkts += num_sent;
+ stats->discards += trs->num - num_sent;
+ } else {
+ odp_packet_free_multi(trs->dst_pkts, trs->num);
+ ++stats->trs_errs;
+ }
+
+ odp_packet_free_multi(trs->src_pkts, trs->num);
+ odp_buffer_free(buf);
+ }
+ }
+}
+
+static inline void send_dma_ev_trs_pkts(odp_dma_compl_t compl_ev, thread_config_t *config)
+{
+ odp_dma_result_t res;
+ odp_buffer_t buf;
+ transfer_t *trs;
+ pktio_t *pktio;
+ int num_sent;
+ stats_t *stats = &config->stats;
+
+ memset(&res, 0, sizeof(res));
+ odp_dma_compl_result(compl_ev, &res);
+ buf = (odp_buffer_t)res.user_ptr;
+ trs = (transfer_t *)odp_buffer_addr(buf);
+
+ if (res.success) {
+ pktio = trs->pktio;
+ num_sent = send_packets(pktio->out_qs[config->thr_idx % pktio->num_out_qs],
+ trs->dst_pkts, trs->num);
+ ++stats->trs;
+ stats->fwd_pkts += num_sent;
+ stats->discards += trs->num - num_sent;
+ } else {
+ odp_packet_free_multi(trs->dst_pkts, trs->num);
+ ++stats->trs_errs;
+ }
+
+ odp_packet_free_multi(trs->src_pkts, trs->num);
+ odp_buffer_free(buf);
+ odp_dma_compl_free(compl_ev);
+}
+
+static inline void push_packet(odp_packet_t pkt, pkt_vec_t pkt_vecs[], uint8_t *pktio_idx_map)
+{
+ uint8_t idx = pktio_idx_map[odp_packet_input_index(pkt)];
+ pkt_vec_t *pkt_vec = &pkt_vecs[idx];
+
+ pkt_vec->pkts[pkt_vec->num++] = pkt;
+}
+
+static inline void pop_packets(pkt_vec_t *pkt_vec, int num_procd)
+{
+ pkt_vec->num -= num_procd;
+
+ for (int i = 0, j = num_procd; i < pkt_vec->num; ++i, ++j)
+ pkt_vec->pkts[i] = pkt_vec->pkts[j];
+}
+
+static void free_pending_packets(pkt_vec_t pkt_vecs[], uint32_t num_ifs)
+{
+ for (uint32_t i = 0U; i < num_ifs; ++i)
+ odp_packet_free_multi(pkt_vecs[i].pkts, pkt_vecs[i].num);
+}
+
+static int process_packets(void *args)
+{
+ thread_config_t *config = args;
+ const uint8_t num_ifs = config->prog_config->num_ifs;
+ pkt_vec_t pkt_vecs[num_ifs], *pkt_vec;
+ odp_atomic_u32_t *is_running = &config->prog_config->is_running;
+ uint64_t c1, c2, c3, c4, cdiff = 0U, rounds = 0U;
+ const uint8_t copy_type = config->prog_config->copy_type;
+ const int burst_size = config->prog_config->burst_size;
+ odp_event_t evs[burst_size];
+ int num_evs;
+ odp_event_t ev;
+ odp_event_type_t type;
+ uint8_t *pktio_map = config->prog_config->pktio_idx_map;
+ stats_t *stats = &config->stats;
+ init_fn_t init_fn = config->prog_config->init_fn;
+ start_fn_t start_fn = config->prog_config->start_fn;
+ pkt_fn_t pkt_fn = config->prog_config->pkt_fn;
+
+ for (uint32_t i = 0U; i < num_ifs; ++i) {
+ pkt_vecs[i].pktio = &config->prog_config->pktios[i];
+ pkt_vecs[i].num = 0;
+ }
+
+ config->thr_idx = odp_thread_id();
+ odp_barrier_wait(&config->prog_config->init_barrier);
+ c1 = odp_cpu_cycles();
+
+ while (odp_atomic_load_u32(is_running)) {
+ c3 = odp_cpu_cycles();
+ num_evs = odp_schedule_multi_no_wait(NULL, evs, burst_size);
+ c4 = odp_cpu_cycles();
+ cdiff += odp_cpu_cycles_diff(c4, c3);
+ ++rounds;
+
+ if (copy_type == DMA_COPY_POLL)
+ send_dma_poll_trs_pkts(burst_size, config);
+
+ if (num_evs == 0)
+ continue;
+
+ for (int i = 0; i < num_evs; ++i) {
+ ev = evs[i];
+ type = odp_event_type(ev);
+
+ if (type == ODP_EVENT_DMA_COMPL) {
+ send_dma_ev_trs_pkts(odp_dma_compl_from_event(ev), config);
+ } else if (type == ODP_EVENT_PACKET) {
+ push_packet(odp_packet_from_event(ev), pkt_vecs, pktio_map);
+ } else {
+ odp_event_free(ev);
+ ++stats->discards;
+ }
+ }
+
+ for (uint32_t i = 0U; i < num_ifs; ++i) {
+ pkt_vec = &pkt_vecs[i];
+
+ if (pkt_vec->num >= burst_size) {
+ pkt_fn(pkt_vec->pkts, burst_size, pkt_vec->pktio, init_fn,
+ start_fn, config);
+ pop_packets(pkt_vec, burst_size);
+ }
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ stats->sched_cc = cdiff;
+ stats->tot_cc = odp_cpu_cycles_diff(c2, c1);
+ stats->sched_rounds = rounds;
+ free_pending_packets(pkt_vecs, num_ifs);
+ odp_barrier_wait(&config->prog_config->term_barrier);
+
+ if (config->prog_config->drain_fn)
+ config->prog_config->drain_fn(config);
+
+ return 0;
+}
+
+static odp_bool_t setup_workers(prog_config_t *config)
+{
+ odp_cpumask_t cpumask;
+ int num_workers;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[config->num_thrs];
+
+ num_workers = odp_cpumask_default_worker(&cpumask, config->num_thrs);
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = config->odp_instance;
+ thr_common.cpumask = &cpumask;
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = process_packets;
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ config->thread_config[i].prog_config = config;
+ thr_param[i].arg = &config->thread_config[i];
+ }
+
+ num_workers = odph_thread_create(config->thread_tbl, &thr_common, thr_param, num_workers);
+
+ if (num_workers != config->num_thrs) {
+ ODPH_ERR("Error configuring worker threads\n");
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t setup_test(prog_config_t *config)
+{
+ odp_barrier_init(&config->init_barrier, config->num_thrs + 1);
+ odp_barrier_init(&config->term_barrier, config->num_thrs + 1);
+
+ if (!setup_copy(config))
+ return false;
+
+ if (!setup_pktios(config))
+ return false;
+
+ if (!setup_workers(config))
+ return false;
+
+ odp_barrier_wait(&config->init_barrier);
+
+ return true;
+}
+
+static void stop_test(prog_config_t *config)
+{
+ for (uint32_t i = 0U; i < config->num_ifs; ++i)
+ if (config->pktios[i].handle != ODP_PKTIO_INVALID)
+ (void)odp_pktio_stop(config->pktios[i].handle);
+
+ odp_barrier_wait(&config->term_barrier);
+ (void)odph_thread_join(config->thread_tbl, config->num_thrs);
+}
+
+static void teardown(prog_config_t *config)
+{
+ thread_config_t *thr;
+
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ free(config->pktios[i].name);
+
+ if (config->pktios[i].handle != ODP_PKTIO_INVALID)
+ (void)odp_pktio_close(config->pktios[i].handle);
+ }
+
+ if (config->pktio_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->pktio_pool);
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ thr = &config->thread_config[i];
+
+ if (thr->inflight_stash != ODP_STASH_INVALID)
+ (void)odp_stash_destroy(thr->inflight_stash);
+
+ if (thr->compl_q != ODP_QUEUE_INVALID)
+ (void)odp_queue_destroy(thr->compl_q);
+
+ if (thr->compl_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(thr->compl_pool);
+
+ if (thr->dma_handle != ODP_DMA_INVALID)
+ (void)odp_dma_destroy(thr->dma_handle);
+ }
+
+ if (config->copy_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->copy_pool);
+
+ if (config->trs_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->trs_pool);
+}
+
+static void print_stats(const prog_config_t *config)
+{
+ const stats_t *stats;
+ const char *align1 = config->copy_type == DMA_COPY_EV ? " " : "";
+ const char *align2 = config->copy_type == SW_COPY ? " " :
+ config->copy_type == DMA_COPY_EV ? " " :
+ " ";
+
+ printf("\n==================\n\n"
+ "DMA forwarder done\n\n"
+ " copy mode: %s\n"
+ " burst size: %u\n"
+ " packet length: %u\n"
+ " max cache size: %u\n", config->copy_type == SW_COPY ? "SW" :
+ config->copy_type == DMA_COPY_EV ? "DMA-event" : "DMA-poll",
+ config->burst_size, config->pkt_len, config->cache_size);
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ stats = &config->thread_config[i].stats;
+
+ printf("\n worker %d:\n", i);
+
+ if (config->copy_type == SW_COPY) {
+ printf(" packet copy errors: %" PRIu64 "\n",
+ stats->copy_errs);
+ } else {
+ printf(" successful DMA transfers: %s%" PRIu64 "\n"
+ " DMA transfer start errors: %s%" PRIu64 "\n"
+ " DMA transfer errors: %s%" PRIu64 "\n"
+ " transfer buffer allocation errors: %s%" PRIu64 "\n"
+ " copy packet allocation errors: %s%" PRIu64 "\n",
+ align1, stats->trs, align1, stats->start_errs, align1,
+ stats->trs_errs, align1, stats->buf_alloc_errs, align1,
+ stats->pkt_alloc_errs);
+
+ if (config->copy_type == DMA_COPY_EV)
+ printf(" completion event allocation errors: %" PRIu64 "\n",
+ stats->compl_alloc_errs);
+ else
+ printf(" transfer ID allocation errors: %" PRIu64 "\n"
+ " transfer poll errors: %" PRIu64 "\n"
+ " transfers polled: %" PRIu64 "\n",
+ stats->compl_alloc_errs, stats->trs_poll_errs,
+ stats->trs_polled);
+ }
+
+ printf(" packets forwarded:%s%" PRIu64 "\n"
+ " packets dropped: %s%" PRIu64 "\n"
+ " call cycles per schedule round:\n"
+ " total: %" PRIu64 "\n"
+ " schedule: %" PRIu64 "\n"
+ " rounds: %" PRIu64 "\n", align2, stats->fwd_pkts, align2,
+ stats->discards, DIV_IF(stats->tot_cc, stats->sched_rounds),
+ DIV_IF(stats->sched_cc, stats->sched_rounds), stats->sched_rounds);
+ }
+
+ printf("\n==================\n");
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t odph_opts;
+ odp_init_t init_param;
+ odp_instance_t odp_instance;
+ odp_shm_t shm_cfg = ODP_SHM_INVALID;
+ int ret = EXIT_SUCCESS;
+ parse_result_t parse_res;
+
+ argc = odph_parse_options(argc, argv);
+
+ if (odph_options(&odph_opts) == -1) {
+ ODPH_ERR("Error while reading ODP helper options, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = odph_opts.mem_model;
+
+ if (odp_init_global(&odp_instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(odp_instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("ODP local init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
+ 0U);
+
+ if (shm_cfg == ODP_SHM_INVALID) {
+ ODPH_ERR("Error reserving shared memory\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ prog_conf = odp_shm_addr(shm_cfg);
+
+ if (prog_conf == NULL) {
+ ODPH_ERR("Error resolving shared memory address\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ parse_res = setup_program(argc, argv, prog_conf);
+
+ if (parse_res == PRS_NOK) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ if (parse_res == PRS_TERM) {
+ ret = EXIT_SUCCESS;
+ goto out_test;
+ }
+
+ if (parse_res == PRS_NOT_SUP) {
+ ret = EXIT_NOT_SUP;
+ goto out_test;
+ }
+
+ if (odp_schedule_config(NULL) < 0) {
+ ODPH_ERR("Error configuring scheduler\n");
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ prog_conf->odp_instance = odp_instance;
+ odp_atomic_init_u32(&prog_conf->is_running, 1U);
+
+ if (!setup_test(prog_conf)) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ if (prog_conf->time_sec > 0.001) {
+ struct timespec ts;
+
+ ts.tv_sec = prog_conf->time_sec;
+ ts.tv_nsec = (prog_conf->time_sec - ts.tv_sec) * ODP_TIME_SEC_IN_NS;
+ nanosleep(&ts, NULL);
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+ } else {
+ while (odp_atomic_load_u32(&prog_conf->is_running))
+ sleep(1U);
+ }
+
+ stop_test(prog_conf);
+ print_stats(prog_conf);
+
+out_test:
+ teardown(prog_conf);
+
+out:
+ if (shm_cfg != ODP_SHM_INVALID)
+ (void)odp_shm_free(shm_cfg);
+
+ if (odp_term_local()) {
+ ODPH_ERR("ODP local terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(odp_instance)) {
+ ODPH_ERR("ODP global terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_dmafwd_run.sh b/test/performance/odp_dmafwd_run.sh
new file mode 100755
index 000000000..38fcc8dc2
--- /dev/null
+++ b/test/performance/odp_dmafwd_run.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2023 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$PWD}"
+TEST_SRC_DIR=$(dirname $0)
+PERF_TEST_DIR=platform/${ODP_PLATFORM}/test/performance
+PERF_TEST_DIR=${TEST_SRC_DIR}/../../${PERF_TEST_DIR}
+
+BIN_NAME=odp_dmafwd
+BATCH=10
+TIME=0.1
+TESTS_RUN=0
+
+check_env()
+{
+ if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+ elif [ "${ODP_PLATFORM}" = "" ]; then
+ echo "$0: ERROR: ODP_PLATFORM must be defined"
+ exit 1
+ elif [ -f ${PERF_TEST_DIR}/dmafwd/pktio_env ]; then
+ . ${PERF_TEST_DIR}/dmafwd/pktio_env
+ else
+ echo "ERROR: unable to find pktio_env"
+ echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test/"
+ echo "ODP_PLATFORM=\"${ODP_PLATFORM}\""
+ exit 1
+ fi
+}
+
+check_result()
+{
+ if [ $1 -eq 0 ]; then
+ TESTS_RUN=`expr $TESTS_RUN + 1`
+ elif [ $1 -eq 1 ]; then
+ echo "Test FAILED, exiting"
+ exit 1
+ else
+ echo "Test SKIPPED"
+ return 0
+ fi
+
+ validate_result
+}
+
+check_exit()
+{
+ if [ $TESTS_RUN -eq 0 ]; then
+ exit 77
+ fi
+
+ exit 0
+}
+
+check_env
+setup_interfaces
+echo "${BIN_NAME}: SW copy"
+echo "==================="
+./${BIN_NAME}${EXEEXT} -i ${IF0} -b ${BATCH} -T ${TIME} -t 0
+check_result $?
+echo "${BIN_NAME}: DMA copy event"
+echo "===================="
+./${BIN_NAME}${EXEEXT} -i ${IF0} -b ${BATCH} -T ${TIME} -t 1
+check_result $?
+echo "${BIN_NAME}: DMA copy poll"
+echo "===================="
+./${BIN_NAME}${EXEEXT} -i ${IF0} -b ${BATCH} -T ${TIME} -t 2
+check_result $?
+cleanup_interfaces
+check_exit
diff --git a/test/performance/odp_ipsec.c b/test/performance/odp_ipsec.c
new file mode 100644
index 000000000..58be03dad
--- /dev/null
+++ b/test/performance/odp_ipsec.c
@@ -0,0 +1,1420 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2022 Marvell
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @example odp_ipsec.c
+ *
+ * Performance test application for IPsec APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <inttypes.h>
+
+/** @def POOL_NUM_PKT
+ * Number of packets in the pool
+ */
+#define POOL_NUM_PKT 4096
+
+#define MAX_DEQUEUE_BURST 16
+
+static uint8_t test_salt[16] = "0123456789abcdef";
+
+static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10,
+};
+
+static uint8_t test_key20[20] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+};
+
+static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18
+};
+
+static uint8_t test_key32[32] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20,
+};
+
+static uint8_t test_key64[64] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x4b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x55, 0x36, 0x37,
+ 0x38, 0x39, 0x5a, 0x3b, 0x3c,
+ 0x3d, 0x3e, 0x5f, 0x40,
+};
+
+/**
+ * Structure that holds template for sa create call
+ * for different algorithms supported by test
+ */
+typedef struct {
+ const char *name; /**< Algorithm name */
+ odp_ipsec_crypto_param_t crypto; /**< Prefilled SA crypto param */
+} ipsec_alg_config_t;
+
+/**
+ * Parsed command line crypto arguments. Describes test configuration.
+ */
+typedef struct {
+ /**
+ * If non zero prints content of packets. Enabled by -d or
+ * --debug option.
+ */
+ int debug_packets;
+
+ /**
+ * Maximum number of outstanding encryption requests. Note code
+ * poll for results over queue and if nothing is available it can
+ * submit more encryption requests up to maximum number specified by
+ * this option. Specified through -f or --flight option.
+ */
+ int in_flight;
+
+ /**
+ * Number of packets to be IPsec processed to get good average number.
+ * Specified through -c or --count option.
+ * Default is 10000.
+ */
+ int packet_count;
+
+ /**
+ * Payload size to test. If 0 set of predefined payload sizes
+ * is tested. Specified through -p or --payload option.
+ */
+ unsigned int payload_length;
+
+ /**
+ * Pointer to selected algorithm to test. If NULL all available
+ * alogorthims are tested. Name of algorithm is passed through
+ * -a or --algorithm option.
+ */
+ ipsec_alg_config_t *alg_config;
+
+ /**
+ * Use scheduler to get completion events from crypto operation.
+ * Specified through -s argument.
+ * */
+ int schedule;
+
+ /*
+ * Poll completion queue for crypto completion events.
+ * Specified through -p argument.
+ */
+ int poll;
+
+ /*
+ * Use tunnel instead of transport mode.
+ * Specified through -t argument.
+ */
+ int tunnel;
+
+ /*
+ * Use AH transformation.
+ * Specified through -u argument.
+ */
+ int ah;
+
+ /*
+ * Burst size.
+ * Prepare and submit as many packets for IPsec processing in each
+ * iteration of the loop.
+ */
+ int burst_size;
+
+ /*
+ * Use vector packet completion from IPsec APIs.
+ * Specified through -v or --vector argument.
+ */
+ uint32_t vec_pkt_size;
+} ipsec_args_t;
+
+/*
+ * Helper structure that holds averages for test of one algorithm
+ * for given payload size.
+ */
+typedef struct {
+ /**
+ * Elapsed time for one crypto operation.
+ */
+ double elapsed;
+
+ /**
+ * CPU time spent pre one crypto operation by whole process
+ * i.e include current and all other threads in process.
+ * It is filled with 'getrusage(RUSAGE_SELF, ...)' call.
+ */
+ double rusage_self;
+
+ /**
+ * CPU time spent per one crypto operation by current thread
+ * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)'
+ * call.
+ */
+ double rusage_thread;
+} ipsec_run_result_t;
+
+/**
+ * Structure holds one snap to misc times of current process.
+ */
+typedef struct {
+ struct timeval tv; /**< Elapsed time */
+ struct rusage ru_self; /**< Rusage value for whole process */
+ struct rusage ru_thread; /**< Rusage value for current thread */
+} time_record_t;
+
+/**
+ * Set of predefined payloads.
+ */
+static unsigned int global_payloads[] = {
+ 64,
+ 256,
+ 1024,
+ 8192,
+ 16384
+};
+
+/** Number of payloads used in the test */
+static unsigned int global_num_payloads;
+
+/**
+ * Set of known algorithms to test
+ */
+static ipsec_alg_config_t algs_config[] = {
+ {
+ .name = "3des-cbc-null",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key = {
+ .data = test_key24,
+ .length = sizeof(test_key24)
+ },
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "3des-cbc-hmac-md5-96",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key = {
+ .data = test_key24,
+ .length = sizeof(test_key24)
+ },
+ .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ },
+ },
+ {
+ .name = "null-hmac-md5-96",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ },
+ },
+ {
+ .name = "aes-cbc-null",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "aes-cbc-hmac-sha1-96",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ },
+ },
+ {
+ .name = "aes-ctr-null",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_key_extra = {
+ .data = test_salt,
+ .length = 4,
+ },
+ .auth_alg = ODP_AUTH_ALG_NULL
+ },
+ },
+ {
+ .name = "aes-ctr-hmac-sha1-96",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_key_extra = {
+ .data = test_salt,
+ .length = 4,
+ },
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ },
+ },
+ {
+ .name = "null-hmac-sha1-96",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key = {
+ .data = test_key20,
+ .length = sizeof(test_key20)
+ },
+ },
+ },
+ {
+ .name = "null-hmac-sha256-128",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA256_HMAC,
+ .auth_key = {
+ .data = test_key32,
+ .length = sizeof(test_key32)
+ },
+ },
+ },
+ {
+ .name = "null-hmac-sha512-256",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_SHA512_HMAC,
+ .auth_key = {
+ .data = test_key64,
+ .length = sizeof(test_key64)
+ },
+ },
+ },
+ {
+ .name = "null-aes-gmac",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_NULL,
+ .auth_alg = ODP_AUTH_ALG_AES_GMAC,
+ .auth_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .auth_key_extra = {
+ .data = test_salt,
+ .length = 4,
+ },
+ },
+ },
+ {
+ .name = "aes-gcm",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_GCM,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_key_extra = {
+ .data = test_salt,
+ .length = 4,
+ },
+ .auth_alg = ODP_AUTH_ALG_AES_GCM,
+ },
+ },
+ {
+ .name = "aes-ccm",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_AES_CCM,
+ .cipher_key = {
+ .data = test_key16,
+ .length = sizeof(test_key16)
+ },
+ .cipher_key_extra = {
+ .data = test_salt,
+ .length = 3,
+ },
+ .auth_alg = ODP_AUTH_ALG_AES_CCM,
+ },
+ },
+ {
+ .name = "chacha20-poly1305",
+ .crypto = {
+ .cipher_alg = ODP_CIPHER_ALG_CHACHA20_POLY1305,
+ .cipher_key = {
+ .data = test_key32,
+ .length = sizeof(test_key32)
+ },
+ .cipher_key_extra = {
+ .data = test_salt,
+ .length = 4,
+ },
+ .auth_alg = ODP_AUTH_ALG_CHACHA20_POLY1305,
+ },
+ },
+};
+
+/**
+ * Find corresponding config for given name. Returns NULL
+ * if config for given name is not found.
+ */
+static ipsec_alg_config_t *
+find_config_by_name(const char *name)
+{
+ unsigned int i;
+ ipsec_alg_config_t *ret = NULL;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++) {
+ if (strcmp(algs_config[i].name, name) == 0) {
+ ret = algs_config + i;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Helper function that prints list of algorithms that this
+ * test understands.
+ */
+static void
+print_config_names(const char *prefix)
+{
+ unsigned int i;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++)
+ printf("%s %s\n", prefix, algs_config[i].name);
+}
+
+/**
+ * Snap current time values and put them into 'rec'.
+ */
+static void
+fill_time_record(time_record_t *rec)
+{
+ gettimeofday(&rec->tv, NULL);
+ getrusage(RUSAGE_SELF, &rec->ru_self);
+ getrusage(RUSAGE_THREAD, &rec->ru_thread);
+}
+
+/**
+ * Calculated CPU time difference for given two rusage structures.
+ * Note it adds user space and system time together.
+ */
+static unsigned long long
+get_rusage_diff(struct rusage *start, struct rusage *end)
+{
+ unsigned long long rusage_diff;
+ unsigned long long rusage_start;
+ unsigned long long rusage_end;
+
+ rusage_start = (start->ru_utime.tv_sec * 1000000) +
+ (start->ru_utime.tv_usec);
+ rusage_start += (start->ru_stime.tv_sec * 1000000) +
+ (start->ru_stime.tv_usec);
+
+ rusage_end = (end->ru_utime.tv_sec * 1000000) +
+ (end->ru_utime.tv_usec);
+ rusage_end += (end->ru_stime.tv_sec * 1000000) +
+ (end->ru_stime.tv_usec);
+
+ rusage_diff = rusage_end - rusage_start;
+
+ return rusage_diff;
+}
+
+/**
+ * Get diff for RUSAGE_SELF (whole process) between two time snap
+ * records.
+ */
+static unsigned long long
+get_rusage_self_diff(time_record_t *start, time_record_t *end)
+{
+ return get_rusage_diff(&start->ru_self, &end->ru_self);
+}
+
+/**
+ * Get diff for RUSAGE_THREAD (current thread only) between two
+ * time snap records.
+ */
+static unsigned long long
+get_rusage_thread_diff(time_record_t *start, time_record_t *end)
+{
+ return get_rusage_diff(&start->ru_thread, &end->ru_thread);
+}
+
+/**
+ * Get diff of elapsed time between two time snap records
+ */
+static unsigned long long
+get_elapsed_usec(time_record_t *start, time_record_t *end)
+{
+ unsigned long long s;
+ unsigned long long e;
+
+ s = (start->tv.tv_sec * 1000000) + (start->tv.tv_usec);
+ e = (end->tv.tv_sec * 1000000) + (end->tv.tv_usec);
+
+ return e - s;
+}
+
+/**
+ * Print header line for our report.
+ */
+static void
+print_result_header(void)
+{
+ printf("\n%30.30s %15s %15s %15s %15s %15s %15s\n",
+ "algorithm", "avg over #", "payload (bytes)", "elapsed (us)",
+ "rusg self (us)", "rusg thrd (us)", "throughput (Kb)");
+}
+
+/**
+ * Print one line of our report.
+ */
+static void
+print_result(ipsec_args_t *cargs,
+ unsigned int payload_length,
+ ipsec_alg_config_t *config,
+ ipsec_run_result_t *result)
+{
+ unsigned int throughput;
+
+ throughput = (1000000.0 / result->elapsed) * payload_length / 1024;
+ printf("%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n",
+ config->name, cargs->packet_count, payload_length,
+ result->elapsed, result->rusage_self, result->rusage_thread,
+ throughput);
+}
+
+#define IPV4ADDR(a, b, c, d) odp_cpu_to_be_32((a << 24) | \
+ (b << 16) | \
+ (c << 8) | \
+ (d << 0))
+
+/**
+ * Create ODP IPsec SA for given config.
+ */
+static odp_ipsec_sa_t
+create_sa_from_config(ipsec_alg_config_t *config,
+ ipsec_args_t *cargs)
+{
+ odp_ipsec_sa_param_t param;
+ odp_queue_t out_queue;
+
+ odp_ipsec_sa_param_init(&param);
+ memcpy(&param.crypto, &config->crypto,
+ sizeof(odp_ipsec_crypto_param_t));
+
+ param.proto = ODP_IPSEC_ESP;
+ param.dir = ODP_IPSEC_DIR_OUTBOUND;
+
+ if (cargs->tunnel) {
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+ odp_ipsec_tunnel_param_t tunnel;
+
+ memset(&tunnel, 0, sizeof(tunnel));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ param.mode = ODP_IPSEC_MODE_TUNNEL;
+ param.outbound.tunnel = tunnel;
+ } else {
+ param.mode = ODP_IPSEC_MODE_TRANSPORT;
+ }
+
+ if (cargs->schedule || cargs->poll) {
+ out_queue = odp_queue_lookup("ipsec-out");
+ if (out_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("ipsec-out queue not found\n");
+ return ODP_IPSEC_SA_INVALID;
+ }
+ param.dest_queue = out_queue;
+ } else {
+ param.dest_queue = ODP_QUEUE_INVALID;
+ }
+
+ return odp_ipsec_sa_create(&param);
+}
+
+static uint8_t test_data[] = {
+ /* IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00
+};
+
+static inline void debug_packets(int debug, odp_packet_t *pkt, int num_pkts)
+{
+ if (odp_likely(!debug))
+ return;
+ for (int i = 0; i < num_pkts; i++)
+ odp_packet_print_data(pkt[i], 0, odp_packet_len(pkt[i]));
+}
+
+static int
+make_packet_multi(odp_pool_t pkt_pool, unsigned int payload_length,
+ odp_packet_t pkt[], int num)
+{
+ int i, ret;
+
+ ret = odp_packet_alloc_multi(pkt_pool, payload_length, pkt, num);
+ if (ret != num) {
+ ODPH_ERR("Could not allocate buffer\n");
+ if (ret > 0)
+ odp_packet_free_sp(pkt, ret);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ odp_packet_copy_from_mem(pkt[i], 0, sizeof(test_data), test_data);
+ odp_packet_l3_offset_set(pkt[i], 0);
+
+ uint8_t *mem = odp_packet_data(pkt[i]);
+ ((odph_ipv4hdr_t *)mem)->tot_len = odp_cpu_to_be_16(payload_length);
+ memset(mem + sizeof(test_data), 1, payload_length - sizeof(test_data));
+ }
+
+ return 0;
+}
+
+static inline void check_ipsec_result(odp_packet_t ipsec_pkt)
+{
+ odp_ipsec_packet_result_t result;
+
+ if (odp_unlikely(odp_ipsec_result(&result, ipsec_pkt)))
+ ODPH_ERR("odp_ipsec_result() failed\n");
+ else if (odp_unlikely(result.status.error.all))
+ ODPH_ERR("IPsec processing error: %" PRIu32 "\n",
+ result.status.error.all);
+}
+
+/**
+ * Run measurement iterations for given config and payload size.
+ * Result of run returned in 'result' out parameter.
+ */
+static int
+run_measure_one(ipsec_args_t *cargs,
+ odp_ipsec_sa_t sa,
+ unsigned int payload_length,
+ time_record_t *start,
+ time_record_t *end)
+{
+ int in_flight, pkts_allowed, num_out, num_pkts, rc = 0;
+ const int max_in_flight = cargs->in_flight;
+ const int burst_size = cargs->burst_size;
+ const int packet_count = cargs->packet_count;
+ const int debug = cargs->debug_packets;
+ odp_ipsec_out_param_t param;
+ odp_pool_t pkt_pool;
+
+ pkt_pool = odp_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("pkt_pool not found\n");
+ return -1;
+ }
+
+ if (payload_length < sizeof(test_data))
+ return -1;
+
+ int packets_sent = 0;
+ int packets_received = 0;
+
+ /* Initialize parameters block */
+ memset(&param, 0, sizeof(param));
+ param.num_sa = 1;
+ param.num_opt = 0;
+ param.sa = &sa;
+
+ fill_time_record(start);
+
+ while ((packets_sent < packet_count) ||
+ (packets_received < packet_count)) {
+ num_pkts = packet_count - packets_sent;
+
+ /* Enqueue up to burst size */
+ num_pkts = num_pkts > burst_size ? burst_size : num_pkts;
+
+ /* Enqueue up to (max in flight - current in flight) */
+ in_flight = packets_sent - packets_received;
+ pkts_allowed = max_in_flight - in_flight;
+
+ /* Enqueue either a burst of packets or skip */
+ num_pkts = num_pkts > pkts_allowed ? 0 : num_pkts;
+
+ if (odp_likely(num_pkts)) {
+ odp_packet_t out_pkt[num_pkts];
+ odp_packet_t pkt[num_pkts];
+ int i;
+
+ if (odp_unlikely(make_packet_multi(pkt_pool,
+ payload_length,
+ pkt,
+ num_pkts)))
+ return -1;
+
+ debug_packets(debug, pkt, num_pkts);
+ num_out = num_pkts;
+
+ rc = odp_ipsec_out(pkt, num_pkts,
+ out_pkt, &num_out,
+ &param);
+ if (odp_unlikely(rc <= 0)) {
+ ODPH_ERR("Failed odp_ipsec_out: rc = %d\n", rc);
+ odp_packet_free_sp(pkt, num_pkts);
+ break;
+ }
+
+ for (i = 0; i < num_out; i++)
+ check_ipsec_result(out_pkt[i]);
+
+ packets_sent += rc;
+ packets_received += num_out;
+ debug_packets(debug, out_pkt, num_out);
+
+ if (odp_unlikely(rc != num_pkts))
+ odp_packet_free_sp(&pkt[rc], num_pkts - rc);
+ odp_packet_free_sp(out_pkt, num_out);
+ }
+ }
+
+ fill_time_record(end);
+
+ return rc < 0 ? rc : 0;
+}
+
+static uint32_t dequeue_burst(odp_queue_t polled_queue,
+ odp_event_t *events,
+ int max_burst)
+{
+ int num = 0;
+
+ if (polled_queue != ODP_QUEUE_INVALID) {
+ int rc = odp_queue_deq_multi(polled_queue,
+ events,
+ max_burst);
+ num = odp_likely(rc >= 0) ? rc : 0;
+ } else {
+ num = odp_schedule_multi(NULL,
+ ODP_SCHED_NO_WAIT,
+ events,
+ max_burst);
+ }
+ return num;
+}
+
+static inline uint32_t vec_pkt_handle(int debug, odp_event_t ev)
+{
+ odp_packet_vector_t vec = odp_packet_vector_from_event(ev);
+ uint32_t vec_size = odp_packet_vector_size(vec);
+ odp_packet_t *pkt_tbl;
+ uint32_t j;
+
+ odp_packet_vector_tbl(vec, &pkt_tbl);
+
+ for (j = 0; j < vec_size; j++)
+ check_ipsec_result(pkt_tbl[j]);
+
+ debug_packets(debug, pkt_tbl, vec_size);
+
+ odp_packet_free_sp(pkt_tbl, vec_size);
+ odp_packet_vector_free(vec);
+
+ return vec_size;
+}
+
+static int
+run_measure_one_async(ipsec_args_t *cargs,
+ odp_ipsec_sa_t sa,
+ unsigned int payload_length,
+ time_record_t *start,
+ time_record_t *end)
+{
+ int in_flight, packets_allowed, num_pkts, rc = 0;
+ const int max_in_flight = cargs->in_flight;
+ const int burst_size = cargs->burst_size;
+ const int packet_count = cargs->packet_count;
+ const int debug = cargs->debug_packets;
+ odp_ipsec_out_param_t param;
+ odp_pool_t pkt_pool;
+ odp_queue_t polled_queue = ODP_QUEUE_INVALID;
+
+ pkt_pool = odp_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("pkt_pool not found\n");
+ return -1;
+ }
+
+ if (cargs->poll) {
+ polled_queue = odp_queue_lookup("ipsec-out");
+ if (polled_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("ipsec-out queue not found\n");
+ return -1;
+ }
+ }
+
+ if (payload_length < sizeof(test_data))
+ return -1;
+
+ int packets_sent = 0;
+ int packets_received = 0;
+
+ /* Initialize parameters block */
+ memset(&param, 0, sizeof(param));
+ param.num_sa = 1;
+ param.num_opt = 0;
+ param.sa = &sa;
+
+ fill_time_record(start);
+
+ while ((packets_sent < packet_count) ||
+ (packets_received < packet_count)) {
+
+ num_pkts = packet_count - packets_sent;
+
+ /* Enqueue up to burst size */
+ num_pkts = num_pkts > burst_size ? burst_size : num_pkts;
+
+ /* Enqueue up to (max in flight - current in flight) */
+ in_flight = packets_sent - packets_received;
+ packets_allowed = max_in_flight - in_flight;
+
+ if (num_pkts > 0 && num_pkts <= packets_allowed) {
+ odp_packet_t pkt[num_pkts];
+
+ if (odp_unlikely(make_packet_multi(pkt_pool,
+ payload_length,
+ pkt,
+ num_pkts)))
+ return -1;
+
+ debug_packets(debug, pkt, num_pkts);
+
+ rc = odp_ipsec_out_enq(pkt, num_pkts, &param);
+ if (odp_unlikely(rc <= 0)) {
+ ODPH_ERR("Failed odp_ipsec_out_enq: rc = %d\n",
+ rc);
+ odp_packet_free_sp(pkt, num_pkts);
+ break;
+ }
+
+ if (odp_unlikely(rc != num_pkts))
+ odp_packet_free_sp(&pkt[rc], num_pkts - rc);
+
+ packets_sent += rc;
+ } else {
+ odp_packet_t pkt_out[max_in_flight];
+ int i = 0;
+
+ /*
+ * Dequeue packets until we can enqueue the next burst
+ * or until we have received all remaining packets
+ * when there are no more packets to be sent.
+ */
+ while (num_pkts > packets_allowed ||
+ (num_pkts == 0 && packets_received < packet_count)) {
+ odp_event_t events[MAX_DEQUEUE_BURST];
+ uint32_t num;
+
+ num = dequeue_burst(polled_queue, events, MAX_DEQUEUE_BURST);
+
+ for (uint32_t n = 0; n < num; n++) {
+ if (odp_event_type(events[n]) == ODP_EVENT_PACKET_VECTOR) {
+ uint32_t vec_size;
+
+ vec_size = vec_pkt_handle(debug, events[n]);
+ packets_received += vec_size - 1;
+ packets_allowed += vec_size - 1;
+ } else {
+ pkt_out[i] = odp_ipsec_packet_from_event(events[n]);
+ check_ipsec_result(pkt_out[i]);
+ i++;
+ }
+
+ }
+ packets_received += num;
+ packets_allowed += num;
+ }
+ debug_packets(debug, pkt_out, i);
+
+ if (i > 0)
+ odp_packet_free_sp(pkt_out, i);
+ }
+ }
+
+ fill_time_record(end);
+
+ return rc < 0 ? rc : 0;
+}
+
+/**
+ * Process one algorithm. Note if paload size is specicified it is
+ * only one run. Or iterate over set of predefined payloads.
+ */
+static int
+run_measure_one_config(ipsec_args_t *cargs,
+ ipsec_alg_config_t *config)
+{
+ unsigned int num_payloads = global_num_payloads;
+ unsigned int *payloads = global_payloads;
+ odp_ipsec_capability_t capa;
+ odp_ipsec_sa_t sa;
+ unsigned int i;
+ int rc = 0;
+
+ if (odp_ipsec_capability(&capa) < 0) {
+ ODPH_ERR("IPSEC capability call failed.\n");
+ return -1;
+ }
+
+ if (cargs->ah && (ODP_SUPPORT_NO == capa.proto_ah)) {
+ ODPH_ERR("IPSEC AH protocol not supported.\n");
+ return -1;
+ }
+
+ rc = odph_ipsec_alg_check(&capa, config->crypto.cipher_alg,
+ config->crypto.cipher_key.length,
+ config->crypto.auth_alg,
+ config->crypto.auth_key.length);
+
+ if (rc) {
+ printf(" => %s skipped\n\n", config->name);
+ return 0;
+ }
+
+ sa = create_sa_from_config(config, cargs);
+ if (sa == ODP_IPSEC_SA_INVALID) {
+ ODPH_ERR("IPsec SA create failed.\n");
+ return -1;
+ }
+
+ print_result_header();
+ if (cargs->payload_length) {
+ num_payloads = 1;
+ payloads = &cargs->payload_length;
+ }
+
+ for (i = 0; i < num_payloads; i++) {
+ double count;
+ ipsec_run_result_t result;
+ time_record_t start, end;
+
+ if (cargs->schedule || cargs->poll)
+ rc = run_measure_one_async(cargs, sa,
+ payloads[i],
+ &start, &end);
+ else
+ rc = run_measure_one(cargs, sa,
+ payloads[i],
+ &start, &end);
+ if (rc)
+ break;
+
+ count = get_elapsed_usec(&start, &end);
+ result.elapsed = count / cargs->packet_count;
+
+ count = get_rusage_self_diff(&start, &end);
+ result.rusage_self = count / cargs->packet_count;
+
+ count = get_rusage_thread_diff(&start, &end);
+ result.rusage_thread = count / cargs->packet_count;
+
+ print_result(cargs, payloads[i],
+ config, &result);
+ }
+
+ odp_ipsec_sa_disable(sa);
+ if (cargs->schedule || cargs->poll) {
+ odp_queue_t out_queue = odp_queue_lookup("ipsec-out");
+ odp_ipsec_status_t status;
+
+ while (1) {
+ odp_event_t event;
+
+ if (cargs->poll)
+ event = odp_queue_deq(out_queue);
+ else
+ event = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (event != ODP_EVENT_INVALID &&
+ odp_event_type(event) == ODP_EVENT_IPSEC_STATUS &&
+ odp_ipsec_status(&status, event) == ODP_IPSEC_OK &&
+ status.id == ODP_IPSEC_STATUS_SA_DISABLE &&
+ status.sa == sa)
+ break;
+ }
+ }
+ odp_ipsec_sa_destroy(sa);
+
+ return rc;
+}
+
+typedef struct thr_arg {
+ ipsec_args_t ipsec_args;
+ ipsec_alg_config_t *ipsec_alg_config;
+} thr_arg_t;
+
+static int run_thr_func(void *arg)
+{
+ thr_arg_t *thr_args = (thr_arg_t *)arg;
+
+ run_measure_one_config(&thr_args->ipsec_args,
+ thr_args->ipsec_alg_config);
+ return 0;
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i 100000\n"
+ "\n"
+ "OpenDataPlane crypto speed measure.\n"
+ "Optional OPTIONS\n"
+ " -a, --algorithm <name> Specify algorithm name (default all)\n"
+ " Supported values are:\n",
+ progname, progname);
+
+ print_config_names(" ");
+ printf(" -d, --debug Enable dump of processed packets.\n"
+ " -f, --flight <number> Max number of packet processed in parallel (default 1)\n"
+ " -c, --count <number> Number of packets (default 10000)\n"
+ " -b, --burst <number> Number of packets in one IPsec API submission (default 1)\n"
+ " -v, --vector <number> Enable vector packet completion from IPsec APIs with specified vector size.\n"
+ " -l, --payload Payload length.\n"
+ " -s, --schedule Use scheduler for completion events.\n"
+ " -p, --poll Poll completion queue for completion events.\n"
+ " -t, --tunnel Use tunnel-mode IPsec transformation.\n"
+ " -u, --ah Use AH transformation instead of ESP.\n"
+ " -h, --help Display help and exit.\n"
+ "\n");
+}
+
+static void parse_args(int argc, char *argv[], ipsec_args_t *cargs)
+{
+ int opt;
+ int long_index;
+ static const struct option longopts[] = {
+ {"algorithm", optional_argument, NULL, 'a'},
+ {"debug", no_argument, NULL, 'd'},
+ {"flight", optional_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"count", optional_argument, NULL, 'c'},
+ {"burst", optional_argument, NULL, 'b'},
+ {"vector", optional_argument, NULL, 'v'},
+ {"payload", optional_argument, NULL, 'l'},
+ {"sessions", optional_argument, NULL, 'm'},
+ {"poll", no_argument, NULL, 'p'},
+ {"schedule", no_argument, NULL, 's'},
+ {"tunnel", no_argument, NULL, 't'},
+ {"ah", no_argument, NULL, 'u'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+a:b:c:df:hm:nl:sptuv:";
+
+ cargs->in_flight = 1;
+ cargs->debug_packets = 0;
+ cargs->packet_count = 10000;
+ cargs->burst_size = 1;
+ cargs->vec_pkt_size = 0;
+ cargs->payload_length = 0;
+ cargs->alg_config = NULL;
+ cargs->schedule = 0;
+ cargs->ah = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'a':
+ cargs->alg_config = find_config_by_name(optarg);
+ if (!cargs->alg_config) {
+ printf("cannot test crypto '%s' configuration\n",
+ optarg);
+ usage(argv[0]);
+ exit(-1);
+ }
+ break;
+ case 'd':
+ cargs->debug_packets = 1;
+ break;
+ case 'c':
+ cargs->packet_count = atoi(optarg);
+ break;
+ case 'b':
+ if (optarg == NULL)
+ cargs->burst_size = 32;
+ else
+ cargs->burst_size = atoi(optarg);
+ if (cargs->burst_size > POOL_NUM_PKT) {
+ printf("Invalid burst size (max allowed: %d)\n", POOL_NUM_PKT);
+ exit(-1);
+ }
+ break;
+ case 'v':
+ if (optarg == NULL)
+ cargs->vec_pkt_size = 32;
+ else
+ cargs->vec_pkt_size = atoi(optarg);
+ break;
+ case 'f':
+ cargs->in_flight = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'l':
+ cargs->payload_length = atoi(optarg);
+ break;
+ case 's':
+ cargs->schedule = 1;
+ break;
+ case 'p':
+ cargs->poll = 1;
+ break;
+ case 't':
+ cargs->tunnel = 1;
+ break;
+ case 'u':
+ cargs->ah = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cargs->in_flight < cargs->burst_size) {
+ printf("-f (flight) must be greater than or equal to -b (burst)\n");
+ exit(-1);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+
+ if (cargs->schedule && cargs->poll) {
+ printf("-s (schedule) and -p (poll) options are not compatible\n");
+ usage(argv[0]);
+ exit(-1);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ odp_pool_t vec_pool = ODP_POOL_INVALID;
+ ipsec_args_t cargs;
+ odp_pool_t pool;
+ odp_queue_param_t qparam;
+ odp_pool_param_t param;
+ odp_queue_t out_queue = ODP_QUEUE_INVALID;
+ thr_arg_t thr_arg;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ int num_workers = 1;
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[num_workers];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odp_ipsec_capability_t ipsec_capa;
+ odp_pool_capability_t capa;
+ odp_ipsec_config_t config;
+ uint32_t max_seg_len;
+ unsigned int i;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ memset(&cargs, 0, sizeof(cargs));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &cargs);
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_WORKER)) {
+ ODPH_ERR("ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_sys_info_print();
+
+ if (odp_pool_capability(&capa)) {
+ ODPH_ERR("Pool capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ max_seg_len = capa.pkt.max_seg_len;
+
+ for (i = 0; i < ODPH_ARRAY_SIZE(global_payloads); i++) {
+ if (global_payloads[i] > max_seg_len)
+ break;
+ }
+
+ global_num_payloads = i;
+
+ /* Create packet pool */
+ odp_pool_param_init(&param);
+ param.pkt.seg_len = max_seg_len;
+ param.pkt.len = max_seg_len;
+ param.pkt.num = POOL_NUM_PKT;
+ param.type = ODP_POOL_PACKET;
+ pool = odp_pool_create("packet_pool", &param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_pool_print(pool);
+
+ if (odp_ipsec_capability(&ipsec_capa) < 0) {
+ ODPH_ERR("IPSEC capability call failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.schedule && !ipsec_capa.queue_type_sched) {
+ ODPH_ERR("Scheduled type destination queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.poll && !ipsec_capa.queue_type_plain) {
+ ODPH_ERR("Plain type destination queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.vec_pkt_size) {
+ if (capa.vector.max_pools < 1) {
+ ODPH_ERR("Vector packet pool not available");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!ipsec_capa.vector.supported) {
+ ODPH_ERR("Vector packet completion not supported by IPsec.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (capa.vector.max_size < cargs.vec_pkt_size) {
+ ODPH_ERR("Vector size larger than max size supported by vector pool.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!cargs.schedule && !cargs.poll) {
+ ODPH_ERR("Vector packet is not supported with sync APIs.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create vector pool */
+ odp_pool_param_init(&param);
+ param.vector.num = POOL_NUM_PKT;
+ param.vector.max_size = cargs.vec_pkt_size;
+ param.type = ODP_POOL_VECTOR;
+ vec_pool = odp_pool_create("vector_pool", &param);
+
+ if (vec_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Vector packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pool_print(vec_pool);
+ }
+
+ odp_ipsec_config_init(&config);
+ config.max_num_sa = 2;
+ config.inbound.chksums.all_chksum = 0;
+ config.outbound.all_chksum = 0;
+
+ if (vec_pool != ODP_POOL_INVALID) {
+ config.vector.enable = true;
+ config.vector.pool = vec_pool;
+ config.vector.max_size = cargs.vec_pkt_size;
+ config.vector.max_tmo_ns = ipsec_capa.vector.max_tmo_ns;
+ }
+
+ odp_queue_param_init(&qparam);
+ if (cargs.schedule) {
+ odp_schedule_config(NULL);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ out_queue = odp_queue_create("ipsec-out", &qparam);
+ } else if (cargs.poll) {
+ qparam.type = ODP_QUEUE_TYPE_PLAIN;
+ out_queue = odp_queue_create("ipsec-out", &qparam);
+ }
+ if (cargs.schedule || cargs.poll) {
+ if (out_queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("ipsec-out queue create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ config.inbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ config.outbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ config.inbound.default_queue = out_queue;
+ } else {
+ config.inbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ config.outbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ config.inbound.default_queue = ODP_QUEUE_INVALID;
+ }
+ if (odp_ipsec_config(&config)) {
+ ODPH_ERR("odp_ipsec_config() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cargs.schedule) {
+ printf("Run in async scheduled mode\n");
+
+ thr_arg.ipsec_args = cargs;
+ thr_arg.ipsec_alg_config = cargs.alg_config;
+ num_workers = odp_cpumask_default_worker(&cpumask,
+ num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
+ sizeof(cpumaskstr));
+ printf("num worker threads: %i\n",
+ num_workers);
+ printf("first CPU: %i\n",
+ odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n",
+ cpumaskstr);
+ } else if (cargs.poll) {
+ printf("Run in async poll mode\n");
+ } else {
+ printf("Run in sync mode\n");
+ }
+
+ if (cargs.alg_config) {
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ if (cargs.schedule) {
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_thr_func;
+ thr_param.arg = &thr_arg;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ odph_thread_join(thread_tbl, num_workers);
+ } else {
+ run_measure_one_config(&cargs, cargs.alg_config);
+ }
+ } else {
+ for (i = 0; i < ODPH_ARRAY_SIZE(algs_config); i++) {
+ if (cargs.ah &&
+ algs_config[i].crypto.cipher_alg !=
+ ODP_CIPHER_ALG_NULL)
+ continue;
+ run_measure_one_config(&cargs, algs_config + i);
+ }
+ }
+
+ if (cargs.schedule || cargs.poll)
+ odp_queue_destroy(out_queue);
+
+ if (cargs.vec_pkt_size) {
+ if (odp_pool_destroy(vec_pool)) {
+ ODPH_ERR("Error: vector pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
diff --git a/test/performance/odp_ipsec_run.sh b/test/performance/odp_ipsec_run.sh
new file mode 100755
index 000000000..f050cb8e0
--- /dev/null
+++ b/test/performance/odp_ipsec_run.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+# Run with a small number of packets in make check
+
+$TEST_DIR/odp_ipsec${EXEEXT} -c 100
+
+if [ $? -ne 0 ] ; then
+ echo Test FAILED
+ exit 1
+fi
+
+exit 0
diff --git a/test/performance/odp_ipsecfwd.c b/test/performance/odp_ipsecfwd.c
new file mode 100644
index 000000000..0220cf6ae
--- /dev/null
+++ b/test/performance/odp_ipsecfwd.c
@@ -0,0 +1,2074 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022-2023 Nokia
+ */
+
+/**
+ * @example odp_ipsecfwd.c
+ *
+ * Simple IPsec performance tester application which forwards and processes
+ * plain and IPsec packets.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <errno.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <libconfig.h>
+
+#define PROG_NAME "odp_ipsecfwd"
+#define SHORT_PROG_NAME "ipsfwd"
+#define DELIMITER ","
+
+#define MAX_IFS 2U
+#define MAX_SAS 4000U
+#define MAX_FWDS 64U
+#define MAX_SPIS (UINT16_MAX + 1U)
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+#define MAX_QUEUES 64U
+#define MAX_SA_QUEUES 1024U
+#define PKT_SIZE 1024U
+#define PKT_CNT 32768U
+#define MAX_BURST 32U
+#define ORDERED 0U
+#define IP_ADDR_LEN 32U
+
+#define ALG_ENTRY(_alg_name, _type) \
+ { \
+ .idx = (_alg_name), \
+ .type = (_type), \
+ .name = #_alg_name \
+ }
+
+enum {
+ CIPHER_TYPE,
+ COMB_CIPHER_TYPE,
+ AUTH_TYPE,
+ COMB_AUTH_TYPE
+};
+
+typedef enum {
+ PRS_OK,
+ PRS_NOK,
+ PRS_TERM
+} parse_result_t;
+
+enum {
+ DIR_IN = 0,
+ DIR_OUT
+};
+
+typedef struct pktio_s pktio_t;
+
+typedef struct pktio_s {
+ union {
+ odp_pktout_queue_t out_dir_qs[MAX_QUEUES];
+ odp_queue_t out_ev_qs[MAX_QUEUES];
+ };
+
+ odp_pktin_queue_t in_dir_qs[MAX_QUEUES];
+ odph_ethaddr_t src_mac;
+ char *name;
+ odp_pktio_t handle;
+ uint32_t (*send_fn)(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num);
+ uint32_t num_tx_qs;
+ uint8_t idx;
+} pktio_t;
+
+typedef struct {
+ uint32_t prefix;
+ uint32_t mask;
+ odph_ethaddr_t dst_mac;
+ const pktio_t *pktio;
+} fwd_entry_t;
+
+typedef struct {
+ fwd_entry_t entries[MAX_FWDS];
+ uint32_t num;
+} lookup_table_t;
+
+typedef struct {
+ uint64_t ipsec_in_pkts;
+ uint64_t ipsec_out_pkts;
+ uint64_t ipsec_in_errs;
+ uint64_t ipsec_out_errs;
+ uint64_t status_errs;
+ uint64_t fwd_pkts;
+ uint64_t discards;
+} stats_t;
+
+typedef struct prog_config_s prog_config_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ stats_t stats;
+ prog_config_t *prog_config;
+ int thr_idx;
+ uint8_t pktio;
+} thread_config_t;
+
+typedef struct {
+ odp_ipsec_sa_param_t sa_param;
+ char cipher_key[65U];
+ char cipher_key_extra[5U];
+ char auth_key[65U];
+ char auth_key_extra[5U];
+ odp_u32be_t lkp_dst_ip;
+ odp_u32be_t src_ip;
+ odp_u32be_t dst_ip;
+} sa_config_t;
+
+typedef uint32_t (*rx_fn_t)(thread_config_t *config, odp_event_t evs[], int num);
+typedef void (*ipsec_fn_t)(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl, stats_t *stats);
+typedef void (*drain_fn_t)(prog_config_t *config);
+
+typedef struct {
+ rx_fn_t rx;
+ ipsec_fn_t proc;
+ ipsec_fn_t compl;
+ drain_fn_t drain;
+} ops_t;
+
+typedef struct prog_config_s {
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ thread_config_t thread_config[MAX_WORKERS];
+ odp_ipsec_sa_t sas[MAX_SAS];
+ odp_queue_t sa_qs[MAX_SA_QUEUES];
+ pktio_t pktios[MAX_IFS];
+ lookup_table_t fwd_tbl;
+ odp_atomic_u32_t is_running;
+ sa_config_t default_cfg;
+ ops_t ops;
+ char *conf_file;
+ odp_instance_t odp_instance;
+ odp_queue_t compl_q;
+ odp_pool_t pktio_pool;
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ uint32_t num_input_qs;
+ uint32_t num_sa_qs;
+ uint32_t num_output_qs;
+ uint32_t num_pkts;
+ uint32_t pkt_len;
+ uint32_t num_ifs;
+ uint32_t num_sas;
+ int num_thrs;
+ odp_bool_t is_dir_rx;
+ odp_bool_t is_hashed_tx;
+ uint8_t mode;
+} prog_config_t;
+
+typedef struct {
+ const char *name;
+ int idx;
+ int type;
+} exposed_alg_t;
+
+typedef struct {
+ odp_packet_t pkts[MAX_BURST];
+ const pktio_t *pktio;
+ uint32_t num;
+} pkt_vec_t;
+
+typedef struct {
+ pkt_vec_t vecs[MAX_QUEUES];
+ uint8_t num_qs;
+} pkt_out_t;
+
+typedef struct {
+ pkt_out_t ifs[MAX_IFS];
+ odp_bool_t is_hashed_tx;
+ uint8_t q_idx;
+} pkt_ifs_t;
+
+static const exposed_alg_t exposed_algs[] = {
+ ALG_ENTRY(ODP_CIPHER_ALG_NULL, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_DES, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_3DES_CBC, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_AES_CBC, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_AES_CTR, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_AES_ECB, CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_AES_GCM, COMB_CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_AES_CCM, COMB_CIPHER_TYPE),
+ ALG_ENTRY(ODP_CIPHER_ALG_CHACHA20_POLY1305, COMB_CIPHER_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_NULL, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_MD5_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_SHA1_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_SHA224_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_SHA256_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_SHA384_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_SHA512_HMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_AES_GCM, COMB_AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_AES_GMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_AES_CCM, COMB_AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_AES_CMAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_AES_XCBC_MAC, AUTH_TYPE),
+ ALG_ENTRY(ODP_AUTH_ALG_CHACHA20_POLY1305, COMB_AUTH_TYPE)
+};
+
+/* SPIs for in and out directions */
+static odp_ipsec_sa_t *spi_to_sa_map[2U][MAX_SPIS];
+static const int ipsec_out_mark;
+static __thread pkt_ifs_t ifs;
+static prog_config_t *prog_conf;
+
+static void init_config(prog_config_t *config)
+{
+ memset(config, 0, sizeof(*config));
+ odp_ipsec_sa_param_init(&config->default_cfg.sa_param);
+ config->compl_q = ODP_QUEUE_INVALID;
+ config->pktio_pool = ODP_POOL_INVALID;
+ config->num_input_qs = 1;
+ config->num_sa_qs = 1;
+ config->num_output_qs = 1;
+ config->num_thrs = 1;
+}
+
+static void terminate(int signal ODP_UNUSED)
+{
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+}
+
+static void parse_interfaces(prog_config_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, DELIMITER);
+
+ while (tmp && config->num_ifs < MAX_IFS) {
+ config->pktios[config->num_ifs].name = strdup(tmp);
+
+ if (config->pktios[config->num_ifs].name != NULL)
+ ++config->num_ifs;
+
+ tmp = strtok(NULL, DELIMITER);
+ }
+
+ free(tmp_str);
+}
+
+static void print_supported_algos(const odp_ipsec_capability_t *ipsec_capa)
+{
+ int c_cnt, a_cnt;
+ const size_t len = ODPH_ARRAY_SIZE(exposed_algs);
+
+ printf(" Cipher algorithms:\n");
+
+ for (size_t i = 0U; i < len; ++i) {
+ if ((exposed_algs[i].type == CIPHER_TYPE ||
+ exposed_algs[i].type == COMB_CIPHER_TYPE) &&
+ (ipsec_capa->ciphers.all_bits & (1 << exposed_algs[i].idx)) > 0U) {
+ c_cnt = odp_ipsec_cipher_capability(exposed_algs[i].idx, NULL, 0);
+
+ if (c_cnt < 0)
+ continue;
+
+ printf(" %d: %s",
+ exposed_algs[i].idx, exposed_algs[i].name);
+ printf(exposed_algs[i].type == COMB_CIPHER_TYPE ? " (combined)" : "");
+
+ odp_ipsec_cipher_capability_t capa[c_cnt];
+
+ (void)odp_ipsec_cipher_capability(exposed_algs[i].idx, capa, c_cnt);
+
+ for (int j = 0; j < c_cnt; ++j)
+ printf(j == 0 ? " (key lengths: %u" : ", %u", capa[j].key_len);
+
+ printf(")\n");
+ }
+ }
+
+ printf(" Authentication algorithms:\n");
+
+ for (size_t i = 0U; i < len; ++i) {
+ if ((exposed_algs[i].type == AUTH_TYPE ||
+ exposed_algs[i].type == COMB_AUTH_TYPE) &&
+ (ipsec_capa->auths.all_bits & (1 << exposed_algs[i].idx)) > 0U) {
+ a_cnt = odp_ipsec_auth_capability(exposed_algs[i].idx, NULL, 0);
+
+ if (a_cnt < 0)
+ continue;
+
+ printf(" %d: %s",
+ exposed_algs[i].idx, exposed_algs[i].name);
+ printf(exposed_algs[i].type == COMB_AUTH_TYPE ? " (combined)" : "");
+
+ odp_ipsec_auth_capability_t capa[a_cnt];
+
+ (void)odp_ipsec_auth_capability(exposed_algs[i].idx, capa, a_cnt);
+
+ for (int j = 0; j < a_cnt; ++j)
+ printf(j == 0 ? " (key/icv lengths: %u/%u" : ", %u/%u",
+ capa[j].key_len, capa[j].icv_len);
+
+ printf(")\n");
+ }
+ }
+}
+
+static void print_usage(void)
+{
+ odp_pool_capability_t pool_capa;
+ odp_ipsec_capability_t ipsec_capa;
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("Error querying pool capabilities\n");
+ return;
+ }
+
+ if (odp_ipsec_capability(&ipsec_capa) < 0) {
+ ODPH_ERR("Error querying IPsec capabilities\n");
+ return;
+ }
+
+ printf("\n"
+ "Simple IPsec performance tester. Forward and process plain and IPsec packets.\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ "\n"
+ " E.g. %s -i ens9f1 -C /etc/odp/ipsecfwd.conf\n"
+ "\n"
+ " With ipsecfwd.conf containing, for example:\n"
+ " default: {\n"
+ " dir = 1\n"
+ " proto = 0\n"
+ " mode = 0\n"
+ " crypto: {\n"
+ " cipher_alg = 4\n"
+ " cipher_key = \"jWnZr4t7w!zwC*F-\"\n"
+ " auth_alg = 2\n"
+ " auth_key = \"n2r5u7x!A%%D*\"\n"
+ " icv_len = 12\n"
+ " };\n"
+ " };\n"
+ "\n"
+ " sa: (\n"
+ " {\n"
+ " spi = 1337\n"
+ " outbound: {\n"
+ " tunnel: {\n"
+ " src_addr = \"192.168.1.10\"\n"
+ " dst_addr = \"192.168.1.16\"\n"
+ " };\n"
+ " };\n"
+ " },\n"
+ " {\n"
+ " spi = 1338\n"
+ " outbound: {\n"
+ " tunnel: {\n"
+ " src_addr = \"192.168.3.110\"\n"
+ " dst_addr = \"192.168.3.116\"\n"
+ " };\n"
+ " };\n"
+ " }\n"
+ " );\n"
+ "\n"
+ " fwd: (\n"
+ " {\n"
+ " prefix: \"192.168.1.0/24\"\n"
+ " if: \"ens9f1\"\n"
+ " dst_mac: \"00:00:05:00:07:00\"\n"
+ " },\n"
+ " {\n"
+ " prefix: \"192.1.0.0/16\"\n"
+ " if: \"ens9f0\"\n"
+ " dst_mac: \"00:00:05:00:08:00\"\n"
+ " }\n"
+ " );\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ "\n"
+ " -i, --interfaces Ethernet interfaces for packet I/O, comma-separated,\n"
+ " no spaces.\n"
+ " -C, --conf Configuration file. 'libconfig' syntax is expected.\n"
+ " SA configuration supports default fallback, i.e.\n"
+ " individual SA configuration blocks may omit some\n"
+ " parameters and instead set these once in default block\n"
+ " which then are used to fill missing parameters. The only\n"
+ " required argument for an SA is the 'spi' parameter.\n"
+ " Individual SA parameter blocks are expected to be in\n"
+ " 'sa'-named list. Parameter naming follows API\n"
+ " specification, see 'odp_ipsec_sa_param_t' for parameter\n"
+ " names and hierarchy. Traffic is mapped to SAs based on UDP\n"
+ " port: the port is used as the SPI. For forwarding entries,\n"
+ " individual parameter blocks are similarly expected to be\n"
+ " in 'fwd'-named list. With forwarding entries, every\n"
+ " parameter is always required and interfaces present in\n"
+ " forwarding entries should be one of the interfaces passed\n"
+ " with '--interfaces' option. The entries are looked up\n"
+ " in the order they are in the list. See example above for\n"
+ " potential SA and forwarding configuration.\n"
+ "\n"
+ " Supported cipher and authentication algorithms for SAs:\n",
+ PROG_NAME, PROG_NAME);
+ print_supported_algos(&ipsec_capa);
+ printf("\n"
+ "Optional OPTIONS:\n"
+ "\n"
+ " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
+ " %u by default.\n"
+ " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
+ " default.\n"
+ " -c, --count Worker thread count. 1 by default.\n"
+ " -m, --mode Queueing mode.\n"
+ " 0: ordered (default)\n"
+ " 1: parallel\n"
+ " -I, --num_input_qs Input queue count. 1 by default.\n"
+ " -S, --num_sa_qs SA queue count. 1 by default.\n"
+ " -O, --num_output_qs Output queue count. 1 by default.\n"
+ " -d, --direct_rx Use direct RX. Interfaces will be polled by workers\n"
+ " directly. '--mode', '--num_input_qs' and '--num_output_qs'\n"
+ " options are ignored, input and output queue counts will\n"
+ " match worker count.\n"
+ " -h, --help This help.\n"
+ "\n", pool_capa.pkt.max_num > 0U ? ODPH_MIN(pool_capa.pkt.max_num, PKT_CNT) :
+ PKT_CNT, pool_capa.pkt.max_len > 0U ? ODPH_MIN(pool_capa.pkt.max_len, PKT_SIZE) :
+ PKT_SIZE);
+}
+
+static inline odp_ipsec_sa_t *get_in_sa(odp_packet_t pkt)
+{
+ odph_esphdr_t esp;
+ uint32_t spi;
+
+ if (!odp_packet_has_ipsec(pkt))
+ return NULL;
+
+ if (odp_packet_copy_to_mem(pkt, odp_packet_l4_offset(pkt), ODPH_ESPHDR_LEN, &esp) < 0)
+ return NULL;
+
+ spi = odp_be_to_cpu_32(esp.spi);
+
+ return spi <= UINT16_MAX ? spi_to_sa_map[DIR_IN][spi] : NULL;
+}
+
+static inline int process_ipsec_in_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
+{
+ odp_ipsec_in_param_t param;
+ int left, sent = 0, ret;
+
+ memset(&param, 0, sizeof(param));
+ /* IPsec in/out need to be identified somehow, so use user_ptr for this. */
+ for (int i = 0; i < num; ++i)
+ odp_packet_user_ptr_set(pkts[i], NULL);
+
+ while (sent < num) {
+ left = num - sent;
+ param.num_sa = left;
+ param.sa = &sas[sent];
+ ret = odp_ipsec_in_enq(&pkts[sent], left, &param);
+
+ if (odp_unlikely(ret <= 0))
+ break;
+
+ sent += ret;
+ }
+
+ return sent;
+}
+
+static inline odp_ipsec_sa_t *get_out_sa(odp_packet_t pkt)
+{
+ odph_udphdr_t udp;
+ uint16_t dst_port;
+
+ if (!odp_packet_has_udp(pkt))
+ return NULL;
+
+ if (odp_packet_copy_to_mem(pkt, odp_packet_l4_offset(pkt), ODPH_UDPHDR_LEN, &udp) < 0)
+ return NULL;
+
+ dst_port = odp_be_to_cpu_16(udp.dst_port);
+
+ return dst_port ? spi_to_sa_map[DIR_OUT][dst_port] : NULL;
+}
+
+static inline int process_ipsec_out_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
+{
+ odp_ipsec_out_param_t param;
+ int left, sent = 0, ret;
+
+ memset(&param, 0, sizeof(param));
+ /* IPsec in/out need to be identified somehow, so use user_ptr for this. */
+ for (int i = 0; i < num; ++i)
+ odp_packet_user_ptr_set(pkts[i], &ipsec_out_mark);
+
+ while (sent < num) {
+ left = num - sent;
+ param.num_sa = left;
+ param.sa = &sas[sent];
+ ret = odp_ipsec_out_enq(&pkts[sent], left, &param);
+
+ if (odp_unlikely(ret <= 0))
+ break;
+
+ sent += ret;
+ }
+
+ return sent;
+}
+
+static inline const fwd_entry_t *get_fwd_entry(lookup_table_t *table, uint32_t ip)
+{
+ fwd_entry_t *entry;
+
+ for (uint32_t i = 0U; i < table->num; ++i) {
+ entry = &table->entries[i];
+
+ if ((ip & entry->mask) == entry->prefix)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static inline const pktio_t *lookup_and_apply(odp_packet_t pkt, lookup_table_t *fwd_tbl,
+ uint8_t *q_idx)
+{
+ const uint32_t l3_off = odp_packet_l3_offset(pkt);
+ odph_ipv4hdr_t ipv4;
+ uint32_t dst_ip, src_ip;
+ const fwd_entry_t *fwd;
+ odph_ethhdr_t eth;
+
+ if (odp_packet_copy_to_mem(pkt, l3_off, ODPH_IPV4HDR_LEN, &ipv4) < 0)
+ return NULL;
+
+ dst_ip = odp_be_to_cpu_32(ipv4.dst_addr);
+ fwd = get_fwd_entry(fwd_tbl, dst_ip);
+
+ if (fwd == NULL)
+ return NULL;
+
+ if (l3_off != ODPH_ETHHDR_LEN) {
+ if (l3_off > ODPH_ETHHDR_LEN) {
+ if (odp_packet_pull_head(pkt, l3_off - ODPH_ETHHDR_LEN) == NULL)
+ return NULL;
+ } else {
+ if (odp_packet_push_head(pkt, ODPH_ETHHDR_LEN - l3_off) == NULL)
+ return NULL;
+ }
+ }
+
+ eth.dst = fwd->dst_mac;
+ eth.src = fwd->pktio->src_mac;
+ eth.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ if (odp_packet_copy_from_mem(pkt, 0U, ODPH_ETHHDR_LEN, &eth) < 0)
+ return NULL;
+
+ if (q_idx != NULL) {
+ src_ip = odp_be_to_cpu_32(ipv4.src_addr);
+ *q_idx = (src_ip ^ dst_ip) % fwd->pktio->num_tx_qs;
+ }
+
+ return fwd->pktio;
+}
+
+static inline uint32_t forward_packets(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl)
+{
+ odp_packet_t pkt;
+ odp_bool_t is_hashed_tx = ifs.is_hashed_tx;
+ uint8_t q_idx = is_hashed_tx ? 0U : ifs.q_idx, qs_done;
+ uint8_t *q_idx_ptr = is_hashed_tx ? &q_idx : NULL;
+ const pktio_t *pktio;
+ pkt_out_t *out;
+ pkt_vec_t *vec;
+ uint32_t num_procd = 0U, ret;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+ pktio = lookup_and_apply(pkt, fwd_tbl, q_idx_ptr);
+
+ if (pktio == NULL) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ out = &ifs.ifs[pktio->idx];
+ vec = &out->vecs[q_idx];
+
+ if (vec->num == 0U)
+ out->num_qs++;
+
+ vec->pkts[vec->num++] = pkt;
+ vec->pktio = pktio;
+ }
+
+ for (uint32_t i = 0U; i < MAX_IFS; ++i) {
+ qs_done = 0U;
+ out = &ifs.ifs[i];
+
+ for (uint32_t j = 0U; j < MAX_QUEUES && qs_done < out->num_qs; ++j) {
+ if (out->vecs[j].num == 0U)
+ continue;
+
+ vec = &out->vecs[j];
+ pktio = vec->pktio;
+ ret = pktio->send_fn(pktio, j, vec->pkts, vec->num);
+
+ if (odp_unlikely(ret < vec->num))
+ odp_packet_free_multi(&vec->pkts[ret], vec->num - ret);
+
+ ++qs_done;
+ vec->num = 0U;
+ num_procd += ret;
+ }
+
+ out->num_qs = 0U;
+ }
+
+ return num_procd;
+}
+
+static inline void process_packets_out_enq(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
+ stats_t *stats)
+{
+ odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_fwd[MAX_BURST];
+ odp_ipsec_sa_t *sa, sas[MAX_BURST];
+ int num_pkts_ips = 0, num_pkts_fwd = 0, num_procd;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+ sa = get_out_sa(pkt);
+
+ if (sa != NULL) {
+ sas[num_pkts_ips] = *sa;
+ pkts_ips[num_pkts_ips] = pkt;
+ ++num_pkts_ips;
+ } else {
+ pkts_fwd[num_pkts_fwd++] = pkt;
+ }
+ }
+
+ if (num_pkts_ips > 0) {
+ num_procd = process_ipsec_out_enq(pkts_ips, sas, num_pkts_ips);
+
+ if (odp_unlikely(num_procd < num_pkts_ips)) {
+ stats->ipsec_out_errs += num_pkts_ips - num_procd;
+ odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
+ }
+ }
+
+ if (num_pkts_fwd > 0) {
+ num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
+ stats->discards += num_pkts_fwd - num_procd;
+ stats->fwd_pkts += num_procd;
+ }
+}
+
+static void process_packets_in_enq(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
+ stats_t *stats)
+{
+ odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_out[MAX_BURST];
+ odp_ipsec_sa_t *sa, sas[MAX_BURST];
+ int num_pkts_ips = 0, num_pkts_out = 0, num_procd;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+
+ if (odp_unlikely(odp_packet_has_error(pkt))) {
+ ++stats->discards;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ sa = get_in_sa(pkt);
+
+ if (sa != NULL) {
+ sas[num_pkts_ips] = *sa;
+ pkts_ips[num_pkts_ips] = pkt;
+ ++num_pkts_ips;
+ } else {
+ pkts_out[num_pkts_out++] = pkt;
+ }
+ }
+
+ if (num_pkts_ips > 0) {
+ num_procd = process_ipsec_in_enq(pkts_ips, sas, num_pkts_ips);
+
+ if (odp_unlikely(num_procd < num_pkts_ips)) {
+ stats->ipsec_in_errs += num_pkts_ips - num_procd;
+ odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
+ }
+ }
+
+ if (num_pkts_out > 0)
+ process_packets_out_enq(pkts_out, num_pkts_out, fwd_tbl, stats);
+}
+
+static inline odp_bool_t is_ipsec_in(odp_packet_t pkt)
+{
+ return odp_packet_user_ptr(pkt) == NULL;
+}
+
+static void complete_ipsec_ops(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
+ stats_t *stats)
+{
+ odp_packet_t pkt, pkts_out[MAX_BURST], pkts_fwd[MAX_BURST];
+ odp_bool_t is_in;
+ odp_ipsec_packet_result_t result;
+ int num_pkts_out = 0, num_pkts_fwd = 0, num_procd;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+ is_in = is_ipsec_in(pkt);
+
+ if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
+ is_in ? ++stats->ipsec_in_errs : ++stats->ipsec_out_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
+ is_in ? ++stats->ipsec_in_errs : ++stats->ipsec_out_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (is_in) {
+ ++stats->ipsec_in_pkts;
+ pkts_out[num_pkts_out++] = pkt;
+ } else {
+ ++stats->ipsec_out_pkts;
+ pkts_fwd[num_pkts_fwd++] = pkt;
+ }
+ }
+
+ if (num_pkts_out > 0)
+ process_packets_out_enq(pkts_out, num_pkts_out, fwd_tbl, stats);
+
+ if (num_pkts_fwd > 0) {
+ num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
+ stats->discards += num_pkts_fwd - num_procd;
+ stats->fwd_pkts += num_procd;
+ }
+}
+
+static void drain_scheduler(prog_config_t *config ODP_UNUSED)
+{
+ odp_event_t ev;
+
+ while (true) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+}
+
+static inline int process_ipsec_in(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
+ odp_packet_t pkts_out[])
+{
+ odp_ipsec_in_param_t param;
+ int left, sent = 0, num_out, ret;
+
+ memset(&param, 0, sizeof(param));
+
+ while (sent < num) {
+ left = num - sent;
+ num_out = left;
+ param.num_sa = left;
+ param.sa = &sas[sent];
+ ret = odp_ipsec_in(&pkts[sent], left, &pkts_out[sent], &num_out, &param);
+
+ if (odp_unlikely(ret <= 0))
+ break;
+
+ sent += ret;
+ }
+
+ return sent;
+}
+
+static inline int process_ipsec_out(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
+ odp_packet_t pkts_out[])
+{
+ odp_ipsec_out_param_t param;
+ int left, sent = 0, num_out, ret;
+
+ memset(&param, 0, sizeof(param));
+
+ while (sent < num) {
+ left = num - sent;
+ num_out = left;
+ param.num_sa = left;
+ param.sa = &sas[sent];
+ ret = odp_ipsec_out(&pkts[sent], left, &pkts_out[sent], &num_out, &param);
+
+ if (odp_unlikely(ret <= 0))
+ break;
+
+ sent += ret;
+ }
+
+ return sent;
+}
+
+static inline void process_packets_out(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
+ stats_t *stats)
+{
+ odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_fwd[MAX_BURST], pkts_ips_out[MAX_BURST];
+ odp_ipsec_sa_t *sa, sas[MAX_BURST];
+ int num_pkts_ips = 0, num_pkts_fwd = 0, num_procd;
+ odp_ipsec_packet_result_t result;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+ sa = get_out_sa(pkt);
+
+ if (sa != NULL) {
+ sas[num_pkts_ips] = *sa;
+ pkts_ips[num_pkts_ips] = pkt;
+ ++num_pkts_ips;
+ } else {
+ pkts_fwd[num_pkts_fwd++] = pkt;
+ }
+ }
+
+ if (num_pkts_ips > 0) {
+ num_procd = process_ipsec_out(pkts_ips, sas, num_pkts_ips, pkts_ips_out);
+
+ if (odp_unlikely(num_procd < num_pkts_ips)) {
+ stats->ipsec_out_errs += num_pkts_ips - num_procd;
+ odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
+ }
+
+ for (int i = 0; i < num_procd; ++i) {
+ pkt = pkts_ips_out[i];
+
+ if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
+ ++stats->ipsec_out_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
+ ++stats->ipsec_out_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ ++stats->ipsec_out_pkts;
+ pkts_fwd[num_pkts_fwd++] = pkt;
+ }
+ }
+
+ if (num_pkts_fwd > 0) {
+ num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
+ stats->discards += num_pkts_fwd - num_procd;
+ stats->fwd_pkts += num_procd;
+ }
+}
+
+static void process_packets_in(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
+ stats_t *stats)
+{
+ odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_out[MAX_BURST], pkts_ips_out[MAX_BURST];
+ odp_ipsec_sa_t *sa, sas[MAX_BURST];
+ int num_pkts_ips = 0, num_pkts_out = 0, num_procd;
+ odp_ipsec_packet_result_t result;
+
+ for (int i = 0; i < num; ++i) {
+ pkt = pkts[i];
+
+ if (odp_unlikely(odp_packet_has_error(pkt))) {
+ ++stats->discards;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ sa = get_in_sa(pkt);
+
+ if (sa != NULL) {
+ sas[num_pkts_ips] = *sa;
+ pkts_ips[num_pkts_ips] = pkt;
+ ++num_pkts_ips;
+ } else {
+ pkts_out[num_pkts_out++] = pkt;
+ }
+ }
+
+ if (num_pkts_ips > 0) {
+ num_procd = process_ipsec_in(pkts_ips, sas, num_pkts_ips, pkts_ips_out);
+
+ if (odp_unlikely(num_procd < num_pkts_ips)) {
+ stats->ipsec_in_errs += num_pkts_ips - num_procd;
+ odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
+ }
+
+ for (int i = 0; i < num_procd; ++i) {
+ pkt = pkts_ips_out[i];
+
+ if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
+ ++stats->ipsec_in_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
+ ++stats->ipsec_in_errs;
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ ++stats->ipsec_in_pkts;
+ pkts_out[num_pkts_out++] = pkt;
+ }
+ }
+
+ if (num_pkts_out > 0)
+ process_packets_out(pkts_out, num_pkts_out, fwd_tbl, stats);
+}
+
+static void drain_direct_inputs(prog_config_t *config)
+{
+ odp_packet_t pkt;
+
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ for (uint32_t j = 0U; j < config->num_input_qs; ++j) {
+ while (odp_pktin_recv(config->pktios[i].in_dir_qs[j], &pkt, 1) == 1)
+ odp_packet_free(pkt);
+ }
+ }
+}
+
+static odp_bool_t setup_ipsec(prog_config_t *config)
+{
+ odp_queue_param_t q_param;
+ odp_ipsec_config_t ipsec_config;
+ char q_name[ODP_QUEUE_NAME_LEN];
+
+ if (!config->is_dir_rx) {
+ snprintf(q_name, sizeof(q_name), SHORT_PROG_NAME "_sa_status");
+ odp_queue_param_init(&q_param);
+ q_param.type = ODP_QUEUE_TYPE_SCHED;
+ q_param.sched.prio = odp_schedule_default_prio();
+ q_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ q_param.sched.group = ODP_SCHED_GROUP_ALL;
+ config->compl_q = odp_queue_create(q_name, &q_param);
+
+ if (config->compl_q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error creating IPsec completion queue\n");
+ return false;
+ }
+ }
+
+ odp_ipsec_config_init(&ipsec_config);
+
+ if (!config->is_dir_rx) {
+ ipsec_config.inbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ ipsec_config.outbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ config->ops.proc = process_packets_in_enq;
+ config->ops.compl = complete_ipsec_ops;
+ config->ops.drain = drain_scheduler;
+ } else {
+ ipsec_config.inbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ ipsec_config.outbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+ config->ops.proc = process_packets_in;
+ config->ops.compl = NULL;
+ config->ops.drain = drain_direct_inputs;
+ }
+
+ ipsec_config.inbound.default_queue = config->compl_q;
+ /* For tunnel to tunnel, we need to parse up to this to check the UDP port for SA. */
+ ipsec_config.inbound.parse_level = ODP_PROTO_LAYER_L4;
+
+ if (odp_ipsec_config(&ipsec_config) < 0) {
+ ODPH_ERR("Error configuring IPsec\n");
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t create_sa_dest_queues(odp_ipsec_capability_t *ipsec_capa,
+ prog_config_t *config)
+{
+ odp_queue_param_t q_param;
+ const uint32_t max_sa_qs = ODPH_MIN(MAX_SA_QUEUES, ipsec_capa->max_queues);
+
+ if (config->num_sa_qs == 0U || config->num_sa_qs > max_sa_qs) {
+ ODPH_ERR("Invalid number of SA queues: %u (min: 1, max: %u)\n", config->num_sa_qs,
+ max_sa_qs);
+ config->num_sa_qs = 0U;
+ return false;
+ }
+
+ for (uint32_t i = 0U; i < config->num_sa_qs; ++i) {
+ char q_name[ODP_QUEUE_NAME_LEN];
+
+ snprintf(q_name, sizeof(q_name), SHORT_PROG_NAME "_sa_compl_%u", i);
+ odp_queue_param_init(&q_param);
+ q_param.type = ODP_QUEUE_TYPE_SCHED;
+ q_param.sched.prio = odp_schedule_max_prio();
+ q_param.sched.sync = config->mode == ORDERED ? ODP_SCHED_SYNC_ORDERED :
+ ODP_SCHED_SYNC_PARALLEL;
+ q_param.sched.group = ODP_SCHED_GROUP_ALL;
+ config->sa_qs[i] = odp_queue_create(q_name, &q_param);
+
+ if (config->sa_qs[i] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error creating SA destination queue (created count: %u)\n", i);
+ config->num_sa_qs = i;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void parse_crypto(config_setting_t *cfg, sa_config_t *config)
+{
+ int val;
+ const char *val_str;
+ config_setting_t *cs = config_setting_lookup(cfg, "crypto");
+
+ if (cs == NULL)
+ return;
+
+ if (config_setting_lookup_int(cs, "cipher_alg", &val) == CONFIG_TRUE)
+ config->sa_param.crypto.cipher_alg = val;
+
+ if (config_setting_lookup_string(cs, "cipher_key", &val_str) == CONFIG_TRUE) {
+ strcpy(config->cipher_key, val_str);
+ config->sa_param.crypto.cipher_key.data = (uint8_t *)config->cipher_key;
+ config->sa_param.crypto.cipher_key.length =
+ strlen((const char *)config->cipher_key);
+ }
+
+ if (config_setting_lookup_string(cs, "cipher_key_extra", &val_str) == CONFIG_TRUE) {
+ strcpy(config->cipher_key_extra, val_str);
+ config->sa_param.crypto.cipher_key_extra.data =
+ (uint8_t *)config->cipher_key_extra;
+ config->sa_param.crypto.cipher_key_extra.length =
+ strlen((const char *)config->cipher_key_extra);
+ }
+
+ if (config_setting_lookup_int(cs, "auth_alg", &val) == CONFIG_TRUE)
+ config->sa_param.crypto.auth_alg = val;
+
+ if (config_setting_lookup_string(cs, "auth_key", &val_str) == CONFIG_TRUE) {
+ strcpy(config->auth_key, val_str);
+ config->sa_param.crypto.auth_key.data = (uint8_t *)config->auth_key;
+ config->sa_param.crypto.auth_key.length = strlen((const char *)config->auth_key);
+ }
+
+ if (config_setting_lookup_string(cs, "auth_key_extra", &val_str) == CONFIG_TRUE) {
+ strcpy(config->auth_key_extra, val_str);
+ config->sa_param.crypto.auth_key_extra.data = (uint8_t *)config->auth_key_extra;
+ config->sa_param.crypto.auth_key_extra.length =
+ strlen((const char *)config->auth_key_extra);
+ }
+
+ if (config_setting_lookup_int(cs, "icv_len", &val) == CONFIG_TRUE)
+ config->sa_param.crypto.icv_len = val;
+}
+
+static void parse_opt(config_setting_t *cfg, sa_config_t *config)
+{
+ int val;
+ config_setting_t *cs = config_setting_lookup(cfg, "opt");
+
+ if (cs == NULL)
+ return;
+
+ if (config_setting_lookup_int(cs, "esn", &val) == CONFIG_TRUE)
+ config->sa_param.opt.esn = val;
+
+ if (config_setting_lookup_int(cs, "udp_encap", &val) == CONFIG_TRUE)
+ config->sa_param.opt.udp_encap = val;
+
+ if (config_setting_lookup_int(cs, "copy_dscp", &val) == CONFIG_TRUE)
+ config->sa_param.opt.copy_dscp = val;
+
+ if (config_setting_lookup_int(cs, "copy_flabel", &val) == CONFIG_TRUE)
+ config->sa_param.opt.copy_flabel = val;
+
+ if (config_setting_lookup_int(cs, "copy_df", &val) == CONFIG_TRUE)
+ config->sa_param.opt.copy_df = val;
+
+ if (config_setting_lookup_int(cs, "dec_ttl", &val) == CONFIG_TRUE)
+ config->sa_param.opt.dec_ttl = val;
+}
+
+static void parse_limits(config_setting_t *cfg, sa_config_t *config)
+{
+ config_setting_t *cs = config_setting_lookup(cfg, "lifetime"), *soft, *hard;
+ long long val;
+
+ if (cs == NULL)
+ return;
+
+ soft = config_setting_lookup(cs, "soft_limit");
+ hard = config_setting_lookup(cs, "hard_limit");
+
+ if (soft != NULL) {
+ if (config_setting_lookup_int64(soft, "bytes", &val) == CONFIG_TRUE)
+ config->sa_param.lifetime.soft_limit.bytes = val;
+
+ if (config_setting_lookup_int64(soft, "packets", &val) == CONFIG_TRUE)
+ config->sa_param.lifetime.soft_limit.packets = val;
+ }
+
+ if (hard != NULL) {
+ if (config_setting_lookup_int64(hard, "bytes", &val) == CONFIG_TRUE)
+ config->sa_param.lifetime.hard_limit.bytes = val;
+
+ if (config_setting_lookup_int64(hard, "packets", &val) == CONFIG_TRUE)
+ config->sa_param.lifetime.hard_limit.packets = val;
+ }
+}
+
+static void parse_inbound(config_setting_t *cfg, sa_config_t *config)
+{
+ config_setting_t *cs = config_setting_lookup(cfg, "inbound");
+ int val;
+ const char *val_str;
+
+ if (cs == NULL)
+ return;
+
+ if (config_setting_lookup_int(cs, "lookup_mode", &val) == CONFIG_TRUE)
+ config->sa_param.inbound.lookup_mode = val;
+
+ if (config_setting_lookup_string(cs, "lookup_dst_addr", &val_str) == CONFIG_TRUE) {
+ if (odph_ipv4_addr_parse(&config->lkp_dst_ip, val_str) == 0) {
+ config->lkp_dst_ip = odp_cpu_to_be_32(config->lkp_dst_ip);
+ config->sa_param.inbound.lookup_param.dst_addr = &config->lkp_dst_ip;
+ }
+ }
+
+ if (config_setting_lookup_int(cs, "antireplay_ws", &val) == CONFIG_TRUE)
+ config->sa_param.inbound.antireplay_ws = val;
+
+ if (config_setting_lookup_int(cs, "reassembly_en", &val) == CONFIG_TRUE)
+ config->sa_param.inbound.reassembly_en = val;
+}
+
+static void parse_outbound(config_setting_t *cfg, sa_config_t *config)
+{
+ config_setting_t *cs = config_setting_lookup(cfg, "outbound"), *tunnel;
+ const char *val_str;
+ int val;
+
+ if (cs == NULL)
+ return;
+
+ tunnel = config_setting_lookup(cs, "tunnel");
+
+ if (tunnel != NULL) {
+ if (config_setting_lookup_string(tunnel, "src_addr", &val_str) == CONFIG_TRUE) {
+ if (odph_ipv4_addr_parse(&config->src_ip, val_str) == 0) {
+ config->src_ip = odp_cpu_to_be_32(config->src_ip);
+ config->sa_param.outbound.tunnel.ipv4.src_addr = &config->src_ip;
+ }
+ }
+
+ if (config_setting_lookup_string(tunnel, "dst_addr", &val_str) == CONFIG_TRUE) {
+ if (odph_ipv4_addr_parse(&config->dst_ip, val_str) == 0) {
+ config->dst_ip = odp_cpu_to_be_32(config->dst_ip);
+ config->sa_param.outbound.tunnel.ipv4.dst_addr = &config->dst_ip;
+ }
+ }
+
+ if (config_setting_lookup_int(tunnel, "dscp", &val) == CONFIG_TRUE)
+ config->sa_param.outbound.tunnel.ipv4.dscp = val;
+
+ if (config_setting_lookup_int(tunnel, "df", &val) == CONFIG_TRUE)
+ config->sa_param.outbound.tunnel.ipv4.df = val;
+
+ if (config_setting_lookup_int(tunnel, "ttl", &val) == CONFIG_TRUE)
+ config->sa_param.outbound.tunnel.ipv4.ttl = val;
+ }
+
+ if (config_setting_lookup_int(cs, "frag_mode", &val) == CONFIG_TRUE)
+ config->sa_param.outbound.frag_mode = val;
+
+ if (config_setting_lookup_int(cs, "mtu", &val) == CONFIG_TRUE)
+ config->sa_param.outbound.mtu = val;
+}
+
+static void parse_sa_entry(config_setting_t *cfg, sa_config_t *config)
+{
+ int val;
+
+ if (config_setting_lookup_int(cfg, "dir", &val) == CONFIG_TRUE)
+ config->sa_param.dir = val;
+
+ if (config_setting_lookup_int(cfg, "proto", &val) == CONFIG_TRUE)
+ config->sa_param.proto = val;
+
+ if (config_setting_lookup_int(cfg, "mode", &val) == CONFIG_TRUE)
+ config->sa_param.mode = val;
+
+ if (config_setting_lookup_int(cfg, "spi", &val) == CONFIG_TRUE)
+ config->sa_param.spi = val;
+
+ parse_crypto(cfg, config);
+ parse_opt(cfg, config);
+ parse_limits(cfg, config);
+ parse_inbound(cfg, config);
+ parse_outbound(cfg, config);
+}
+
+static void create_sa_entry(odp_ipsec_sa_param_t *sa_param, prog_config_t *config,
+ uint32_t max_num_sa)
+{
+ uint32_t dir = sa_param->dir;
+ uint32_t spi = sa_param->spi;
+ odp_ipsec_sa_t sa;
+
+ if (config->num_sas == max_num_sa) {
+ ODPH_ERR("Maximum number of SAs parsed (%u), ignoring rest\n", max_num_sa);
+ return;
+ }
+
+ if (spi > UINT16_MAX) {
+ ODPH_ERR("Unsupported SPI value for SA %u (> %u)\n", spi, UINT16_MAX);
+ return;
+ }
+
+ if (spi_to_sa_map[dir][spi] != NULL) {
+ ODPH_ERR("Non-unique SPIs not supported for SA %u\n", spi);
+ return;
+ }
+
+ sa_param->dest_queue = config->sa_qs[config->num_sas % config->num_sa_qs];
+ sa = odp_ipsec_sa_create(sa_param);
+
+ if (sa == ODP_IPSEC_SA_INVALID) {
+ ODPH_ERR("Error creating SA handle for SA %u\n", spi);
+ return;
+ }
+
+ config->sas[config->num_sas] = sa;
+ spi_to_sa_map[dir][spi] = &config->sas[config->num_sas];
+ ++config->num_sas;
+}
+
+static void parse_and_create_sa_entries(config_t *cfg, prog_config_t *config, uint32_t max_num_sa)
+{
+ config_setting_t *cs;
+ int count;
+
+ cs = config_lookup(cfg, "default");
+
+ if (cs != NULL)
+ parse_sa_entry(cs, &config->default_cfg);
+
+ cs = config_lookup(cfg, "sa");
+
+ if (cs == NULL)
+ return;
+
+ count = config_setting_length(cs);
+
+ for (int i = 0; i < count; i++) {
+ sa_config_t sa_cfg;
+ config_setting_t *sa;
+ int val;
+
+ sa_cfg = config->default_cfg;
+ sa = config_setting_get_elem(cs, i);
+
+ if (sa == NULL)
+ continue;
+
+ if (config_setting_lookup_int(sa, "spi", &val) == CONFIG_TRUE) {
+ parse_sa_entry(sa, &sa_cfg);
+ create_sa_entry(&sa_cfg.sa_param, config, max_num_sa);
+ }
+ }
+}
+
+static void parse_sas(config_t *cfg, prog_config_t *config)
+{
+ odp_ipsec_capability_t ipsec_capa;
+ uint32_t max_num_sa;
+
+ if (odp_ipsec_capability(&ipsec_capa) < 0) {
+ ODPH_ERR("Error querying IPsec capabilities\n");
+ return;
+ }
+
+ if (!setup_ipsec(config))
+ return;
+
+ if (!config->is_dir_rx && !create_sa_dest_queues(&ipsec_capa, config))
+ return;
+
+ max_num_sa = ODPH_MIN(MAX_SAS, ipsec_capa.max_num_sa);
+ parse_and_create_sa_entries(cfg, config, max_num_sa);
+}
+
+static const pktio_t *get_pktio(const char *iface, const prog_config_t *config)
+{
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ if (strcmp(iface, config->pktios[i].name) == 0)
+ return &config->pktios[i];
+ }
+
+ return NULL;
+}
+
+static void create_fwd_table_entry(config_setting_t *cfg, prog_config_t *config)
+{
+ const char *val_str;
+ char dst_ip_str[16U] = { 0 };
+ uint32_t mask, dst_ip;
+ odph_ethaddr_t dst_mac;
+ const pktio_t *pktio = NULL;
+ fwd_entry_t *entry;
+
+ if (config->fwd_tbl.num == MAX_FWDS) {
+ ODPH_ERR("Maximum number of forwarding entries parsed (%u), ignoring rest\n",
+ MAX_FWDS);
+ return;
+ }
+
+ if (config_setting_lookup_string(cfg, "prefix", &val_str) == CONFIG_TRUE) {
+ if (sscanf(val_str, "%[^/]/%u", dst_ip_str, &mask) != 2) {
+ ODPH_ERR("Error parsing IP and subnet mask for forwarding entry\n");
+ return;
+ }
+
+ if (odph_ipv4_addr_parse(&dst_ip, dst_ip_str) < 0) {
+ ODPH_ERR("Syntax error in IP address for forwarding entry\n");
+ return;
+ }
+
+ if (mask > IP_ADDR_LEN) {
+ ODPH_ERR("Invalid subnet mask for forwarding entry: %u\n", mask);
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (config_setting_lookup_string(cfg, "if", &val_str) == CONFIG_TRUE) {
+ pktio = get_pktio(val_str, config);
+
+ if (pktio == NULL) {
+ ODPH_ERR("Error parsing next interface for forwarding entry\n");
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (config_setting_lookup_string(cfg, "dst_mac", &val_str) == CONFIG_TRUE) {
+ if (odph_eth_addr_parse(&dst_mac, val_str) < 0) {
+ ODPH_ERR("Syntax error in destination MAC for forwarding entry\n");
+ return;
+ }
+ } else {
+ return;
+ }
+
+ mask = mask > 0U ? 0xFFFFFFFF << (IP_ADDR_LEN - mask) : 0U;
+ entry = &config->fwd_tbl.entries[config->fwd_tbl.num];
+ entry->prefix = dst_ip & mask;
+ entry->mask = mask;
+ entry->dst_mac = dst_mac;
+ entry->pktio = pktio;
+ ++config->fwd_tbl.num;
+}
+
+static void parse_fwd_table(config_t *cfg, prog_config_t *config)
+{
+ config_setting_t *cs;
+ int count;
+
+ cs = config_lookup(cfg, "fwd");
+
+ if (cs == NULL)
+ return;
+
+ count = config_setting_length(cs);
+
+ for (int i = 0; i < count; i++) {
+ config_setting_t *fwd = config_setting_get_elem(cs, i);
+
+ if (fwd == NULL)
+ continue;
+
+ create_fwd_table_entry(fwd, config);
+ }
+}
+
+static parse_result_t check_options(prog_config_t *config)
+{
+ odp_pool_capability_t pool_capa;
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("Error querying pool capabilities\n");
+ return PRS_NOK;
+ }
+
+ if (config->num_ifs == 0U) {
+ ODPH_ERR("Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
+ MAX_IFS);
+ return PRS_NOK;
+ }
+
+ if (config->fwd_tbl.num == 0U) {
+ ODPH_ERR("Invalid number of forwarding entries: %u (min: 1, max: %u)\n",
+ config->fwd_tbl.num, MAX_FWDS);
+ return PRS_NOK;
+ }
+
+ if (pool_capa.pkt.max_num > 0U && config->num_pkts > pool_capa.pkt.max_num) {
+ ODPH_ERR("Invalid pool packet count: %u (max: %u)\n", config->num_pkts,
+ pool_capa.pkt.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->num_pkts == 0U)
+ config->num_pkts = pool_capa.pkt.max_num > 0U ?
+ ODPH_MIN(pool_capa.pkt.max_num, PKT_CNT) : PKT_CNT;
+
+ if (pool_capa.pkt.max_len > 0U && config->pkt_len > pool_capa.pkt.max_len) {
+ ODPH_ERR("Invalid pool packet length: %u (max: %u)\n", config->pkt_len,
+ pool_capa.pkt.max_len);
+ return PRS_NOK;
+ }
+
+ if (config->pkt_len == 0U)
+ config->pkt_len = pool_capa.pkt.max_len > 0U ?
+ ODPH_MIN(pool_capa.pkt.max_len, PKT_SIZE) : PKT_SIZE;
+
+ if (config->num_thrs <= 0 || config->num_thrs > MAX_WORKERS) {
+ ODPH_ERR("Invalid thread count: %d (min: 1, max: %d)\n", config->num_thrs,
+ MAX_WORKERS);
+ return PRS_NOK;
+ }
+
+ if (config->is_dir_rx) {
+ config->num_input_qs = config->num_thrs;
+ config->num_output_qs = config->num_thrs;
+ }
+
+ return PRS_OK;
+}
+
+static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
+{
+ int opt, long_index;
+ config_t cfg;
+
+ static const struct option longopts[] = {
+ { "interfaces", required_argument, NULL, 'i' },
+ { "num_pkts", required_argument, NULL, 'n' },
+ { "pkt_len", required_argument, NULL, 'l' },
+ { "count", required_argument, NULL, 'c' },
+ { "mode", required_argument, NULL, 'm' },
+ { "conf", required_argument, NULL, 'C' },
+ { "num_input_qs", required_argument, NULL, 'I' },
+ { "num_sa_qs", required_argument, NULL, 'S' },
+ { "num_output_qs", required_argument, NULL, 'O' },
+ { "direct_rx", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "i:n:l:c:m:C:I:S:O:dh";
+
+ while (true) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'i':
+ parse_interfaces(config, optarg);
+ break;
+ case 'n':
+ config->num_pkts = atoi(optarg);
+ break;
+ case 'l':
+ config->pkt_len = atoi(optarg);
+ break;
+ case 'c':
+ config->num_thrs = atoi(optarg);
+ break;
+ case 'm':
+ config->mode = !!atoi(optarg);
+ break;
+ case 'C':
+ config->conf_file = strdup(optarg);
+ break;
+ case 'I':
+ config->num_input_qs = atoi(optarg);
+ break;
+ case 'S':
+ config->num_sa_qs = atoi(optarg);
+ break;
+ case 'O':
+ config->num_output_qs = atoi(optarg);
+ break;
+ case 'd':
+ config->is_dir_rx = true;
+ break;
+ case 'h':
+ print_usage();
+ return PRS_TERM;
+ case '?':
+ default:
+ print_usage();
+ return PRS_NOK;
+ }
+ }
+
+ config_init(&cfg);
+
+ if (config_read_file(&cfg, config->conf_file) == CONFIG_FALSE) {
+ ODPH_ERR("Error opening SA configuration file: %s\n", config_error_text(&cfg));
+ config_destroy(&cfg);
+ return PRS_NOK;
+ }
+
+ parse_sas(&cfg, config);
+ parse_fwd_table(&cfg, config);
+ config_destroy(&cfg);
+
+ return check_options(config);
+}
+
+static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
+{
+ struct sigaction action = { .sa_handler = terminate };
+
+ if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
+ sigaddset(&action.sa_mask, SIGTERM) == -1 ||
+ sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
+ sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
+ ODPH_ERR("Error installing signal handler\n");
+ return PRS_NOK;
+ }
+
+ return parse_options(argc, argv, config);
+}
+
+static uint32_t schedule(thread_config_t *config ODP_UNUSED, odp_event_t evs[], int num)
+{
+ return odp_schedule_multi_no_wait(NULL, evs, num);
+}
+
+static uint32_t recv(thread_config_t *config, odp_event_t evs[], int num)
+{
+ prog_config_t *prog_config = config->prog_config;
+ pktio_t *pktio = &prog_config->pktios[config->pktio++ % prog_config->num_ifs];
+ odp_pktin_queue_t in_q = pktio->in_dir_qs[config->thr_idx % prog_config->num_input_qs];
+ odp_packet_t pkts[num];
+ int ret;
+
+ ret = odp_pktin_recv(in_q, pkts, num);
+
+ if (odp_unlikely(ret <= 0))
+ return 0U;
+
+ odp_packet_to_event_multi(pkts, evs, ret);
+
+ return ret;
+}
+
+static uint32_t send(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num)
+{
+ int ret = odp_pktout_send(pktio->out_dir_qs[index], pkts, num);
+
+ return ret < 0 ? 0U : (uint32_t)ret;
+}
+
+static uint32_t enqueue(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num)
+{
+ odp_event_t evs[MAX_BURST];
+ int ret;
+
+ odp_packet_to_event_multi(pkts, evs, num);
+
+ ret = odp_queue_enq_multi(pktio->out_ev_qs[index], evs, num);
+
+ return ret < 0 ? 0U : (uint32_t)ret;
+}
+
+static odp_bool_t setup_pktios(prog_config_t *config)
+{
+ odp_pool_param_t pool_param;
+ pktio_t *pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_capability_t capa;
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktio_config_t pktio_config;
+ uint32_t max_output_qs;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.seg_len = config->pkt_len;
+ pool_param.pkt.len = config->pkt_len;
+ pool_param.pkt.num = config->num_pkts;
+ pool_param.type = ODP_POOL_PACKET;
+ config->pktio_pool = odp_pool_create(PROG_NAME, &pool_param);
+
+ if (config->pktio_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating packet I/O pool\n");
+ return false;
+ }
+
+ config->ops.rx = !config->is_dir_rx ? schedule : recv;
+ config->is_hashed_tx = !config->is_dir_rx && config->mode == ORDERED;
+
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ pktio = &config->pktios[i];
+ pktio->idx = i;
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = !config->is_dir_rx ?
+ ODP_PKTIN_MODE_SCHED : ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = config->is_hashed_tx ?
+ ODP_PKTOUT_MODE_QUEUE : ODP_PKTOUT_MODE_DIRECT;
+ pktio->handle = odp_pktio_open(pktio->name, config->pktio_pool, &pktio_param);
+
+ if (pktio->handle == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Error opening packet I/O (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (odp_pktio_capability(pktio->handle, &capa) < 0) {
+ ODPH_ERR("Error querying packet I/O capabilities (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (config->num_input_qs == 0U || config->num_input_qs > capa.max_input_queues) {
+ ODPH_ERR("Invalid number of input queues for packet I/O: %u (min: 1, max: "
+ "%u) (%s)\n", config->num_input_qs, capa.max_input_queues,
+ pktio->name);
+ return false;
+ }
+
+ max_output_qs = ODPH_MIN(MAX_QUEUES, capa.max_output_queues);
+
+ if (config->num_output_qs == 0U || config->num_output_qs > max_output_qs) {
+ ODPH_ERR("Invalid number of output queues for packet I/O: %u (min: 1, "
+ "max: %u) (%s)\n", config->num_output_qs, max_output_qs,
+ pktio->name);
+ return false;
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ if (config->is_hashed_tx)
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+
+ if (config->num_input_qs > 1U) {
+ pktin_param.hash_enable = true;
+ pktin_param.hash_proto.proto.ipv4_udp = 1U;
+ pktin_param.num_queues = config->num_input_qs;
+ }
+
+ pktin_param.op_mode = (config->is_dir_rx &&
+ config->num_thrs > (int)config->num_input_qs) ?
+ ODP_PKTIO_OP_MT : ODP_PKTIO_OP_MT_UNSAFE;
+
+ if (odp_pktin_queue_config(pktio->handle, &pktin_param) < 0) {
+ ODPH_ERR("Error configuring packet I/O input queues (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (config->is_dir_rx) {
+ if (odp_pktin_queue(pktio->handle, pktio->in_dir_qs, config->num_input_qs)
+ != (int)config->num_input_qs) {
+ ODPH_ERR("Error querying packet I/O input queue (%s)\n",
+ pktio->name);
+ return false;
+ }
+ }
+
+ pktio->send_fn = config->is_hashed_tx ? enqueue : send;
+ pktio->num_tx_qs = config->num_output_qs;
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.num_queues = pktio->num_tx_qs;
+
+ if (!config->is_hashed_tx) {
+ pktout_param.op_mode = config->num_thrs > (int)pktio->num_tx_qs ?
+ ODP_PKTIO_OP_MT : ODP_PKTIO_OP_MT_UNSAFE;
+ }
+
+ if (odp_pktout_queue_config(pktio->handle, &pktout_param) < 0) {
+ ODPH_ERR("Error configuring packet I/O output queues (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (config->is_hashed_tx) {
+ if (odp_pktout_event_queue(pktio->handle, pktio->out_ev_qs,
+ pktio->num_tx_qs) != (int)pktio->num_tx_qs) {
+ ODPH_ERR("Error querying packet I/O output event queue (%s)\n",
+ pktio->name);
+ return false;
+ }
+ } else {
+ if (odp_pktout_queue(pktio->handle, pktio->out_dir_qs, pktio->num_tx_qs)
+ != (int)pktio->num_tx_qs) {
+ ODPH_ERR("Error querying packet I/O output queue (%s)\n",
+ pktio->name);
+ return false;
+ }
+ }
+
+ odp_pktio_config_init(&pktio_config);
+
+ if (odp_pktio_config(pktio->handle, &pktio_config) < 0) {
+ ODPH_ERR("Error configuring packet I/O extra options (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (odp_pktio_mac_addr(pktio->handle, &pktio->src_mac, sizeof(pktio->src_mac))
+ != sizeof(pktio->src_mac)) {
+ ODPH_ERR("Error getting packet I/O MAC address (%s)\n", pktio->name);
+ return false;
+ }
+
+ if (odp_pktio_start(pktio->handle) < 0) {
+ ODPH_ERR("Error starting packet I/O (%s)\n", pktio->name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static inline void check_ipsec_status_ev(odp_event_t ev, stats_t *stats)
+{
+ odp_ipsec_status_t status;
+
+ if (odp_unlikely(odp_ipsec_status(&status, ev) < 0 || status.result < 0))
+ ++stats->status_errs;
+
+ odp_event_free(ev);
+}
+
+static int process_packets(void *args)
+{
+ thread_config_t *config = args;
+ int thr_idx = odp_thread_id();
+ odp_event_t evs[MAX_BURST], ev;
+ ops_t ops = config->prog_config->ops;
+ odp_atomic_u32_t *is_running = &config->prog_config->is_running;
+ uint32_t cnt;
+ odp_event_type_t type;
+ odp_event_subtype_t subtype;
+ odp_packet_t pkt, pkts_in[MAX_BURST], pkts_ips[MAX_BURST];
+ lookup_table_t *fwd_tbl = &config->prog_config->fwd_tbl;
+ stats_t *stats = &config->stats;
+
+ ifs.is_hashed_tx = config->prog_config->is_hashed_tx;
+ ifs.q_idx = thr_idx % config->prog_config->num_output_qs;
+ config->thr_idx = thr_idx;
+ odp_barrier_wait(&config->prog_config->init_barrier);
+
+ while (odp_atomic_load_u32(is_running)) {
+ int num_pkts_in = 0, num_pkts_ips = 0;
+ /* TODO: Add possibility to configure scheduler and ipsec enq/deq burst sizes. */
+ cnt = ops.rx(config, evs, MAX_BURST);
+
+ if (cnt == 0U)
+ continue;
+
+ for (uint32_t i = 0U; i < cnt; ++i) {
+ ev = evs[i];
+ type = odp_event_types(ev, &subtype);
+ pkt = odp_packet_from_event(ev);
+
+ if (type == ODP_EVENT_PACKET) {
+ if (subtype == ODP_EVENT_PACKET_BASIC) {
+ pkts_in[num_pkts_in++] = pkt;
+ } else if (subtype == ODP_EVENT_PACKET_IPSEC) {
+ pkts_ips[num_pkts_ips++] = pkt;
+ } else {
+ ++stats->discards;
+ odp_event_free(ev);
+ }
+ } else if (type == ODP_EVENT_IPSEC_STATUS) {
+ check_ipsec_status_ev(ev, stats);
+ } else {
+ ++stats->discards;
+ odp_event_free(ev);
+ }
+ }
+
+ if (num_pkts_in > 0)
+ ops.proc(pkts_in, num_pkts_in, fwd_tbl, stats);
+
+ if (ops.compl && num_pkts_ips > 0)
+ ops.compl(pkts_ips, num_pkts_ips, fwd_tbl, stats);
+ }
+
+ odp_barrier_wait(&config->prog_config->term_barrier);
+ ops.drain(config->prog_config);
+
+ return 0;
+}
+
+static odp_bool_t setup_workers(prog_config_t *config)
+{
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[config->num_thrs];
+ odp_cpumask_t cpumask;
+ int num_workers;
+
+ num_workers = odp_cpumask_default_worker(&cpumask, config->num_thrs);
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = config->odp_instance;
+ thr_common.cpumask = &cpumask;
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = process_packets;
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ config->thread_config[i].prog_config = config;
+ thr_param[i].arg = &config->thread_config[i];
+ }
+
+ num_workers = odph_thread_create(config->thread_tbl, &thr_common, thr_param, num_workers);
+
+ if (num_workers != config->num_thrs) {
+ ODPH_ERR("Error configuring worker threads\n");
+ return false;
+ }
+
+ return true;
+}
+
+static odp_bool_t setup_test(prog_config_t *config)
+{
+ odp_barrier_init(&config->init_barrier, config->num_thrs + 1);
+ odp_barrier_init(&config->term_barrier, config->num_thrs + 1);
+
+ if (!setup_pktios(config))
+ return false;
+
+ if (!setup_workers(config))
+ return false;
+
+ odp_barrier_wait(&config->init_barrier);
+
+ return true;
+}
+
+static void stop_test(prog_config_t *config)
+{
+ for (uint32_t i = 0U; i < config->num_ifs; ++i)
+ if (config->pktios[i].handle != ODP_PKTIO_INVALID)
+ (void)odp_pktio_stop(config->pktios[i].handle);
+
+ odp_barrier_wait(&config->term_barrier);
+ (void)odph_thread_join(config->thread_tbl, config->num_thrs);
+}
+
+static void print_stats(const prog_config_t *config)
+{
+ const stats_t *stats;
+
+ printf("\n====================\n\n"
+ "IPsec forwarder done\n\n"
+ " configuration file: %s\n"
+ " queuing mode: %s\n"
+ " input queue count: %u\n"
+ " SA queue count: %u\n"
+ " output queue count: %u\n"
+ " RX mode: %s\n", config->conf_file,
+ config->mode == ORDERED ? "ordered" : "parallel", config->num_input_qs,
+ config->num_sa_qs, config->num_output_qs,
+ config->is_dir_rx ? "direct" : "scheduled");
+
+ for (int i = 0; i < config->num_thrs; ++i) {
+ stats = &config->thread_config[i].stats;
+
+ printf("\n worker %d:\n"
+ " IPsec in packets: %" PRIu64 "\n"
+ " IPsec out packets: %" PRIu64 "\n"
+ " IPsec in packet errors: %" PRIu64 "\n"
+ " IPsec out packet errors: %" PRIu64 "\n"
+ " IPsec status errors: %" PRIu64 "\n"
+ " packets forwarded: %" PRIu64 "\n"
+ " packets dropped: %" PRIu64 "\n", i, stats->ipsec_in_pkts,
+ stats->ipsec_out_pkts, stats->ipsec_in_errs, stats->ipsec_out_errs,
+ stats->status_errs, stats->fwd_pkts, stats->discards);
+ }
+
+ printf("\n====================\n");
+}
+
+static void wait_sas_disabled(uint32_t num_sas)
+{
+ uint32_t num_sas_dis = 0U;
+ odp_event_t ev;
+ odp_ipsec_status_t status;
+
+ while (num_sas_dis < num_sas) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ if (odp_ipsec_status(&status, ev) < 0) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ if (status.id == ODP_IPSEC_STATUS_SA_DISABLE)
+ ++num_sas_dis;
+
+ odp_event_free(ev);
+ }
+}
+
+static void teardown_test(const prog_config_t *config)
+{
+ for (uint32_t i = 0U; i < config->num_ifs; ++i) {
+ free(config->pktios[i].name);
+
+ if (config->pktios[i].handle != ODP_PKTIO_INVALID)
+ (void)odp_pktio_close(config->pktios[i].handle);
+ }
+
+ if (config->pktio_pool != ODP_POOL_INVALID)
+ (void)odp_pool_destroy(config->pktio_pool);
+
+ for (uint32_t i = 0U; i < config->num_sas; ++i)
+ (void)odp_ipsec_sa_disable(config->sas[i]);
+
+ if (!config->is_dir_rx)
+ /* Drain SA status events. */
+ wait_sas_disabled(config->num_sas);
+
+ for (uint32_t i = 0U; i < config->num_sas; ++i)
+ (void)odp_ipsec_sa_destroy(config->sas[i]);
+
+ for (uint32_t i = 0U; i < config->num_sa_qs; ++i)
+ (void)odp_queue_destroy(config->sa_qs[i]);
+
+ if (config->compl_q != ODP_QUEUE_INVALID)
+ (void)odp_queue_destroy(config->compl_q);
+
+ free(config->conf_file);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t odph_opts;
+ odp_init_t init_param;
+ odp_instance_t odp_instance;
+ odp_shm_t shm_cfg = ODP_SHM_INVALID;
+ parse_result_t parse_res;
+ int ret = EXIT_SUCCESS;
+
+ argc = odph_parse_options(argc, argv);
+
+ if (odph_options(&odph_opts) == -1) {
+ ODPH_ERR("Error while reading ODP helper options, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = odph_opts.mem_model;
+
+ if (odp_init_global(&odp_instance, &init_param, NULL) < 0) {
+ ODPH_ERR("ODP global init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(odp_instance, ODP_THREAD_CONTROL) < 0) {
+ ODPH_ERR("ODP local init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
+ 0U);
+
+ if (shm_cfg == ODP_SHM_INVALID) {
+ ODPH_ERR("Error reserving shared memory\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ prog_conf = odp_shm_addr(shm_cfg);
+
+ if (prog_conf == NULL) {
+ ODPH_ERR("Error resolving shared memory address\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ init_config(prog_conf);
+
+ if (!prog_conf->is_dir_rx && odp_schedule_config(NULL) < 0) {
+ ODPH_ERR("Error configuring scheduler\n");
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ parse_res = setup_program(argc, argv, prog_conf);
+
+ if (parse_res == PRS_NOK) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ if (parse_res == PRS_TERM) {
+ ret = EXIT_SUCCESS;
+ goto out_test;
+ }
+
+ prog_conf->odp_instance = odp_instance;
+ odp_atomic_init_u32(&prog_conf->is_running, 1U);
+
+ if (!setup_test(prog_conf)) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ while (odp_atomic_load_u32(&prog_conf->is_running))
+ odp_cpu_pause();
+
+ stop_test(prog_conf);
+ print_stats(prog_conf);
+
+out_test:
+ teardown_test(prog_conf);
+
+out:
+ if (shm_cfg != ODP_SHM_INVALID)
+ (void)odp_shm_free(shm_cfg);
+
+ if (odp_term_local() < 0) {
+ ODPH_ERR("ODP local terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(odp_instance) < 0) {
+ ODPH_ERR("ODP global terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_ipsecfwd.conf b/test/performance/odp_ipsecfwd.conf
new file mode 100644
index 000000000..81ddaef7e
--- /dev/null
+++ b/test/performance/odp_ipsecfwd.conf
@@ -0,0 +1,41 @@
+default: {
+ dir = 1
+ proto = 0
+ mode = 0
+ crypto: {
+ cipher_alg = 4
+ cipher_key = "jWnZr4t7w!zwC*F-"
+ auth_alg = 2
+ auth_key = "n2r5u7x!A%D*"
+ icv_len = 12
+ };
+};
+
+sa: (
+ {
+ spi = 1337
+ outbound: {
+ tunnel: {
+ src_addr = "192.168.1.10"
+ dst_addr = "192.168.1.16"
+ };
+ };
+ },
+ {
+ spi = 1338
+ outbound: {
+ tunnel: {
+ src_addr = "192.168.3.110"
+ dst_addr = "192.168.3.116"
+ };
+ };
+ }
+);
+
+fwd: (
+ {
+ prefix: "192.168.1.0/24"
+ if: "ens9f1"
+ dst_mac: "00:00:05:00:07:00"
+ }
+);
diff --git a/test/performance/odp_l2fwd.c b/test/performance/odp_l2fwd.c
new file mode 100644
index 000000000..ce63c3f53
--- /dev/null
+++ b/test/performance/odp_l2fwd.c
@@ -0,0 +1,3113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2024 Nokia
+ * Copyright (c) 2020-2021 Marvell
+ */
+
+/**
+ * @example odp_l2fwd.c
+ *
+ * This L2 forwarding application can be used as example as well as performance
+ * test for different ODP packet I/O modes (direct, queue or scheduled).
+ *
+ * Note that this example is tuned for performance. As a result, when using
+ * scheduled packet input mode with direct or queued output mode and multiple
+ * output queues, packet order is not guaranteed. To maintain packet order,
+ * use a single worker thread or output interfaces with one output queue.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/* Maximum number of worker threads */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+/* Default number of packets per pool */
+#define DEFAULT_NUM_PKT (16 * 1024)
+
+/* Packet length to pool create */
+#define POOL_PKT_LEN 1536
+
+/* Maximum number of packet in a burst */
+#define MAX_PKT_BURST 32
+
+/* Maximum number of pktio queues per interface */
+#define MAX_QUEUES 32
+
+/* Maximum number of schedule groups */
+#define MAX_GROUPS 32
+
+/* Maximum number of pktio interfaces */
+#define MAX_PKTIOS 8
+
+/* Default vector size */
+#define DEFAULT_VEC_SIZE MAX_PKT_BURST
+
+/* Default vector timeout */
+#define DEFAULT_VEC_TMO ODP_TIME_MSEC_IN_NS
+
+/* Maximum thread info string length */
+#define EXTRA_STR_LEN 32
+
+/* Packet input mode */
+typedef enum pktin_mode_t {
+ DIRECT_RECV,
+ PLAIN_QUEUE,
+ SCHED_PARALLEL,
+ SCHED_ATOMIC,
+ SCHED_ORDERED,
+} pktin_mode_t;
+
+/* Packet output modes */
+typedef enum pktout_mode_t {
+ PKTOUT_DIRECT,
+ PKTOUT_QUEUE
+} pktout_mode_t;
+
+static inline int sched_mode(pktin_mode_t in_mode)
+{
+ return (in_mode == SCHED_PARALLEL) ||
+ (in_mode == SCHED_ATOMIC) ||
+ (in_mode == SCHED_ORDERED);
+}
+
+/* Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+/*
+ * Parsed command line application arguments
+ */
+typedef struct {
+ /* Some extra features (e.g. error checks) have been enabled */
+ uint8_t extra_feat;
+
+ /* Has some state that needs to be maintained across tx and/or rx */
+ uint8_t has_state;
+
+ /* Prefetch packet data */
+ uint8_t prefetch;
+
+ /* Change destination eth addresses */
+ uint8_t dst_change;
+
+ /* Change source eth addresses */
+ uint8_t src_change;
+
+ /* Read packet data in uint64_t words */
+ uint16_t data_rd;
+
+ /* Check packet errors */
+ uint8_t error_check;
+
+ /* Packet copy */
+ uint8_t packet_copy;
+
+ /* Checksum offload */
+ uint8_t chksum;
+
+ /* Print debug info on every packet */
+ uint8_t verbose_pkt;
+
+ unsigned int cpu_count;
+ int if_count; /* Number of interfaces to be used */
+ int addr_count; /* Number of dst addresses to be used */
+ int num_workers; /* Number of worker threads */
+ char **if_names; /* Array of pointers to interface names */
+ odph_ethaddr_t addrs[MAX_PKTIOS]; /* Array of dst addresses */
+ pktin_mode_t in_mode; /* Packet input mode */
+ pktout_mode_t out_mode; /* Packet output mode */
+ int time; /* Time in seconds to run. */
+ int accuracy; /* Number of seconds to get and print stats */
+ char *if_str; /* Storage for interface names */
+ int sched_mode; /* Scheduler mode */
+ int num_groups; /* Number of scheduling groups */
+ int group_mode; /* How threads join groups */
+ int burst_rx; /* Receive burst size */
+ int rx_queues; /* RX queues per interface */
+ int pool_per_if; /* Create pool per interface */
+ uint32_t num_pkt; /* Number of packets per pool */
+ int flow_control; /* Flow control mode */
+ bool pause_rx; /* Reception of pause frames enabled */
+ bool pause_tx; /* Transmission of pause frames enabled */
+ bool vector_mode; /* Vector mode enabled */
+ uint32_t num_vec; /* Number of vectors per pool */
+ uint64_t vec_tmo_ns; /* Vector formation timeout in ns */
+ uint32_t vec_size; /* Vector size */
+ int verbose; /* Verbose output */
+ uint32_t packet_len; /* Maximum packet length supported */
+ uint32_t seg_len; /* Pool segment length */
+ int promisc_mode; /* Promiscuous mode enabled */
+ int flow_aware; /* Flow aware scheduling enabled */
+ uint8_t input_ts; /* Packet input timestamping enabled */
+ int mtu; /* Interface MTU */
+ int num_om;
+ int num_prio;
+
+ struct {
+ odp_packet_tx_compl_mode_t mode;
+ uint32_t nth;
+ uint32_t thr_compl_id;
+ uint32_t tot_compl_id;
+ } tx_compl;
+
+ char *output_map[MAX_PKTIOS]; /* Destination port mappings for interfaces */
+ odp_schedule_prio_t prio[MAX_PKTIOS]; /* Priority of input queues of an interface */
+
+} appl_args_t;
+
+/* Statistics */
+typedef union ODP_ALIGNED_CACHE {
+ struct {
+ /* Number of forwarded packets */
+ uint64_t packets;
+ /* Packets dropped due to receive error */
+ uint64_t rx_drops;
+ /* Packets dropped due to transmit error */
+ uint64_t tx_drops;
+ /* Number of transmit completion start misses (previous incomplete) */
+ uint64_t tx_c_misses;
+ /* Number of transmit completion start failures */
+ uint64_t tx_c_fails;
+ /* Number of failed packet copies */
+ uint64_t copy_fails;
+ /* Dummy sum of packet data */
+ uint64_t dummy_sum;
+ } s;
+
+ uint8_t padding[ODP_CACHE_LINE_SIZE];
+} stats_t;
+
+/* Transmit completion specific state data */
+typedef struct {
+ /* Options that are passed to transmit completion requests */
+ odp_packet_tx_compl_opt_t opt;
+ /* Thread specific initial value for transmit completion IDs */
+ uint32_t init;
+ /* Thread specific maximum value for transmit completion IDs */
+ uint32_t max;
+ /* Next free completion ID to be used for a transmit completion request */
+ uint32_t free_head;
+ /* Next completion ID to be polled for transmit completion readiness */
+ uint32_t poll_head;
+ /* Number of active requests */
+ uint32_t num_act;
+ /* Maximum number of active requests */
+ uint32_t max_act;
+ /* Transmit completion request interval for packets */
+ int interval;
+ /* Next packet in a send burst for which to request transmit completion */
+ int next_req;
+} tx_compl_t;
+
+/* Thread specific state data */
+typedef struct {
+ tx_compl_t tx_compl;
+} state_t;
+
+/* Thread specific data */
+typedef struct thread_args_t {
+ stats_t stats;
+ state_t state;
+
+ struct {
+ odp_pktin_queue_t pktin;
+ odp_pktout_queue_t pktout;
+ odp_queue_t rx_queue;
+ odp_queue_t tx_queue;
+ int rx_idx;
+ int tx_idx;
+ int rx_queue_idx;
+ int tx_queue_idx;
+ } pktio[MAX_PKTIOS];
+
+ /* Groups to join */
+ odp_schedule_group_t group[MAX_GROUPS];
+
+ int thr_idx;
+ int num_pktio;
+ int num_grp_join;
+
+} thread_args_t;
+
+/*
+ * Grouping of all global data
+ */
+typedef struct {
+ /* Thread table */
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ /* Thread specific arguments */
+ thread_args_t thread_args[MAX_WORKERS];
+ /* Barriers to synchronize main and workers */
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ /* Application (parsed) arguments */
+ appl_args_t appl;
+ /* Table of port ethernet addresses */
+ odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
+ /* Table of dst ethernet addresses */
+ odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
+ /* Table of dst ports. This is used by non-sched modes. */
+ int dst_port[MAX_PKTIOS];
+ /* Table of pktio handles */
+ struct {
+ odp_pktio_t pktio;
+ odp_pktin_queue_t pktin[MAX_QUEUES];
+ odp_pktout_queue_t pktout[MAX_QUEUES];
+ odp_queue_t rx_q[MAX_QUEUES];
+ odp_queue_t tx_q[MAX_QUEUES];
+ odp_queue_t compl_q;
+ int num_rx_thr;
+ int num_tx_thr;
+ int num_rx_queue;
+ int num_tx_queue;
+ int next_rx_queue;
+ int next_tx_queue;
+ } pktios[MAX_PKTIOS];
+
+ /* Destination port lookup table.
+ * Table index is pktio_index of the API. This is used by the sched
+ * mode. */
+ uint8_t dst_port_from_idx[ODP_PKTIO_MAX_INDEX + 1];
+ /* Break workers loop if set to 1 */
+ odp_atomic_u32_t exit_threads;
+
+ uint32_t pkt_len;
+ uint32_t num_pkt;
+ uint32_t seg_len;
+ uint32_t vector_num;
+ uint32_t vector_max_size;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+
+} args_t;
+
+/* Global pointer to args */
+static args_t *gbl_args;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (gbl_args == NULL)
+ return;
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+}
+
+static int setup_sig_handler(void)
+{
+ struct sigaction action = { .sa_handler = sig_handler };
+
+ if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Drop packets which input parsing marked as containing errors.
+ *
+ * Frees packets with error and modifies pkt_tbl[] to only contain packets with
+ * no detected errors.
+ *
+ * pkt_tbl Array of packets
+ * num Number of packets in pkt_tbl[]
+ *
+ * Returns number of packets dropped
+ */
+static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
+{
+ odp_packet_t pkt;
+ unsigned dropped = 0;
+ unsigned i, j;
+
+ for (i = 0, j = 0; i < num; ++i) {
+ pkt = pkt_tbl[i];
+
+ if (odp_unlikely(odp_packet_has_error(pkt))) {
+ odp_packet_free(pkt); /* Drop */
+ dropped++;
+ } else if (odp_unlikely(i != j++)) {
+ pkt_tbl[j - 1] = pkt;
+ }
+ }
+
+ return dropped;
+}
+
+static inline void prefetch_data(uint8_t prefetch, odp_packet_t pkt_tbl[], uint32_t num)
+{
+ if (prefetch == 0)
+ return;
+
+ for (uint32_t i = 0; i < num; i++)
+ odp_packet_prefetch(pkt_tbl[i], 0, prefetch * 64);
+}
+
+/*
+ * Fill packets' eth addresses according to the destination port
+ *
+ * pkt_tbl Array of packets
+ * num Number of packets in the array
+ * dst_port Destination port
+ */
+static inline void fill_eth_addrs(odp_packet_t pkt_tbl[],
+ unsigned num, int dst_port)
+{
+ odp_packet_t pkt;
+ odph_ethhdr_t *eth;
+ unsigned i;
+
+ if (!gbl_args->appl.dst_change && !gbl_args->appl.src_change)
+ return;
+
+ for (i = 0; i < num; ++i) {
+ pkt = pkt_tbl[i];
+ eth = odp_packet_data(pkt);
+
+ if (gbl_args->appl.src_change)
+ eth->src = gbl_args->port_eth_addr[dst_port];
+
+ if (gbl_args->appl.dst_change)
+ eth->dst = gbl_args->dst_eth_addr[dst_port];
+ }
+}
+
+static inline int event_queue_send(odp_queue_t queue, odp_packet_t *pkt_tbl,
+ unsigned pkts)
+{
+ int ret;
+ unsigned sent = 0;
+ odp_event_t ev_tbl[pkts];
+
+ odp_packet_to_event_multi(pkt_tbl, ev_tbl, pkts);
+
+ while (sent < pkts) {
+ ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
+
+ if (odp_unlikely(ret <= 0)) {
+ if (ret < 0 || odp_atomic_load_u32(&gbl_args->exit_threads))
+ break;
+ }
+
+ sent += ret;
+ }
+
+ return sent;
+}
+
+static inline void chksum_insert(odp_packet_t *pkt_tbl, int pkts)
+{
+ odp_packet_t pkt;
+ int i;
+
+ for (i = 0; i < pkts; i++) {
+ pkt = pkt_tbl[i];
+ odp_packet_l3_chksum_insert(pkt, 1);
+ odp_packet_l4_chksum_insert(pkt, 1);
+ }
+}
+
+static void print_packets(odp_packet_t *pkt_tbl, int num)
+{
+ odp_packet_t pkt;
+ uintptr_t data_ptr;
+ uint32_t bit, align;
+
+ for (int i = 0; i < num; i++) {
+ pkt = pkt_tbl[i];
+ data_ptr = (uintptr_t)odp_packet_data(pkt);
+
+ for (bit = 0, align = 1; bit < 32; bit++, align *= 2)
+ if (data_ptr & (0x1 << bit))
+ break;
+
+ printf(" Packet data: 0x%" PRIxPTR "\n"
+ " Packet len: %u\n"
+ " Packet seg len: %u\n"
+ " Data align: %u\n"
+ " Num segments: %i\n"
+ " Headroom size: %u\n"
+ " User area size: %u\n\n",
+ data_ptr, odp_packet_len(pkt), odp_packet_seg_len(pkt), align,
+ odp_packet_num_segs(pkt), odp_packet_headroom(pkt),
+ odp_packet_user_area_size(pkt));
+ }
+}
+
+static inline void data_rd(odp_packet_t *pkt_tbl, int num, uint16_t rd_words, stats_t *stats)
+{
+ odp_packet_t pkt;
+ uint64_t *data;
+ int i;
+ uint32_t len, words, j;
+ uint64_t sum = 0;
+
+ for (i = 0; i < num; i++) {
+ pkt = pkt_tbl[i];
+ data = odp_packet_data(pkt);
+ len = odp_packet_seg_len(pkt);
+
+ words = rd_words;
+ if (rd_words * 8 > len)
+ words = len / 8;
+
+ for (j = 0; j < words; j++)
+ sum += data[j];
+ }
+
+ stats->s.dummy_sum += sum;
+}
+
+static inline int copy_packets(odp_packet_t *pkt_tbl, int pkts)
+{
+ odp_packet_t old_pkt, new_pkt;
+ odp_pool_t pool;
+ int i;
+ int copy_fails = 0;
+
+ for (i = 0; i < pkts; i++) {
+ old_pkt = pkt_tbl[i];
+ pool = odp_packet_pool(old_pkt);
+ new_pkt = odp_packet_copy(old_pkt, pool);
+ if (odp_likely(new_pkt != ODP_PACKET_INVALID)) {
+ pkt_tbl[i] = new_pkt;
+ odp_packet_free(old_pkt);
+ } else {
+ copy_fails++;
+ }
+ }
+
+ return copy_fails;
+}
+
+/*
+ * Return number of packets remaining in the pkt_tbl
+ */
+static inline int process_extra_features(const appl_args_t *appl_args, odp_packet_t *pkt_tbl,
+ int pkts, stats_t *stats)
+{
+ if (odp_unlikely(appl_args->extra_feat)) {
+ uint16_t rd_words = appl_args->data_rd;
+
+ if (appl_args->verbose_pkt)
+ print_packets(pkt_tbl, pkts);
+
+ if (rd_words)
+ data_rd(pkt_tbl, pkts, rd_words, stats);
+
+ if (appl_args->packet_copy) {
+ int fails;
+
+ fails = copy_packets(pkt_tbl, pkts);
+ stats->s.copy_fails += fails;
+ }
+
+ if (appl_args->chksum)
+ chksum_insert(pkt_tbl, pkts);
+
+ if (appl_args->error_check) {
+ int rx_drops;
+
+ /* Drop packets with errors */
+ rx_drops = drop_err_pkts(pkt_tbl, pkts);
+
+ if (odp_unlikely(rx_drops)) {
+ stats->s.rx_drops += rx_drops;
+ if (pkts == rx_drops)
+ return 0;
+
+ pkts -= rx_drops;
+ }
+ }
+ }
+ return pkts;
+}
+
+static inline void handle_tx_event_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num,
+ int tx_idx, stats_t *stats)
+{
+ odp_packet_t pkt;
+ int next_req = tx_c->next_req;
+ const int interval = tx_c->interval;
+
+ tx_c->opt.queue = gbl_args->pktios[tx_idx].compl_q;
+
+ while (next_req <= num) {
+ pkt = pkts[next_req - 1];
+
+ if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
+ stats->s.tx_c_fails++;
+ /* Missed one, try requesting for the first packet of next burst. */
+ next_req = num + 1;
+ break;
+ }
+
+ next_req += interval;
+ }
+
+ tx_c->next_req = next_req - num;
+}
+
+static inline void handle_tx_poll_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num, int tx_idx,
+ stats_t *stats)
+{
+ uint32_t num_act = tx_c->num_act, poll_head = tx_c->poll_head, free_head = tx_c->free_head;
+ const uint32_t max = tx_c->max, init = tx_c->init, max_act = tx_c->max_act;
+ odp_pktio_t pktio = gbl_args->pktios[tx_idx].pktio;
+ int next_req = tx_c->next_req;
+ odp_packet_t pkt;
+ const int interval = tx_c->interval;
+
+ while (num_act > 0) {
+ if (odp_packet_tx_compl_done(pktio, poll_head) < 1)
+ break;
+
+ --num_act;
+
+ if (++poll_head > max)
+ poll_head = init;
+ }
+
+ while (next_req <= num) {
+ pkt = pkts[next_req - 1];
+
+ if (num_act == max_act) {
+ stats->s.tx_c_misses++;
+ /* Missed one, try requesting for the first packet of next burst. */
+ next_req = num + 1;
+ break;
+ }
+
+ tx_c->opt.compl_id = free_head;
+
+ if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
+ stats->s.tx_c_fails++;
+ /* Missed one, try requesting for the first packet of next burst. */
+ next_req = num + 1;
+ break;
+ }
+
+ if (++free_head > max)
+ free_head = init;
+
+ ++num_act;
+ next_req += interval;
+ }
+
+ tx_c->free_head = free_head;
+ tx_c->poll_head = poll_head;
+ tx_c->num_act = num_act;
+ tx_c->next_req = next_req - num;
+}
+
+static inline void handle_tx_state(state_t *state, odp_packet_t pkts[], int num, int tx_idx,
+ stats_t *stats)
+{
+ tx_compl_t *tx_c = &state->tx_compl;
+
+ if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_EVENT)
+ handle_tx_event_compl(tx_c, pkts, num, tx_idx, stats);
+ else if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_POLL)
+ handle_tx_poll_compl(tx_c, pkts, num, tx_idx, stats);
+}
+
+static inline void handle_state_failure(state_t *state, odp_packet_t packet)
+{
+ if (odp_packet_has_tx_compl_request(packet) != 0) {
+ --state->tx_compl.num_act;
+ --state->tx_compl.free_head;
+
+ if (state->tx_compl.free_head == UINT32_MAX ||
+ state->tx_compl.free_head < state->tx_compl.init)
+ state->tx_compl.free_head = state->tx_compl.max;
+ }
+}
+
+static inline void send_packets(odp_packet_t *pkt_tbl,
+ int pkts,
+ int use_event_queue,
+ int tx_idx,
+ odp_queue_t tx_queue,
+ odp_pktout_queue_t pktout_queue,
+ state_t *state,
+ stats_t *stats)
+{
+ int sent;
+ unsigned int tx_drops;
+ int i;
+ odp_packet_t pkt;
+
+ if (odp_unlikely(state != NULL))
+ handle_tx_state(state, pkt_tbl, pkts, tx_idx, stats);
+
+ if (odp_unlikely(use_event_queue))
+ sent = event_queue_send(tx_queue, pkt_tbl, pkts);
+ else
+ sent = odp_pktout_send(pktout_queue, pkt_tbl, pkts);
+
+ sent = odp_unlikely(sent < 0) ? 0 : sent;
+ tx_drops = pkts - sent;
+
+ if (odp_unlikely(tx_drops)) {
+ stats->s.tx_drops += tx_drops;
+
+ /* Drop rejected packets */
+ for (i = sent; i < pkts; i++) {
+ pkt = pkt_tbl[i];
+ handle_state_failure(state, pkt);
+ odp_packet_free(pkt);
+ }
+ }
+
+ stats->s.packets += pkts;
+}
+
+static int handle_rx_state(state_t *state, odp_event_t evs[], int num)
+{
+ if (state->tx_compl.opt.mode != ODP_PACKET_TX_COMPL_EVENT ||
+ odp_event_type(evs[0]) != ODP_EVENT_PACKET_TX_COMPL)
+ return num;
+
+ odp_event_free_multi(evs, num);
+
+ return 0;
+}
+
+/*
+ * Packet IO worker thread using scheduled queues and vector mode.
+ *
+ * arg thread arguments of type 'thread_args_t *'
+ */
+static int run_worker_sched_mode_vector(void *arg)
+{
+ int thr;
+ int i;
+ int pktio, num_pktio;
+ uint16_t max_burst;
+ odp_thrmask_t mask;
+ odp_pktout_queue_t pktout[MAX_PKTIOS];
+ odp_queue_t tx_queue[MAX_PKTIOS];
+ thread_args_t *thr_args = arg;
+ stats_t *stats = &thr_args->stats;
+ const appl_args_t *appl_args = &gbl_args->appl;
+ state_t *state = appl_args->has_state ? &thr_args->state : NULL;
+ int use_event_queue = gbl_args->appl.out_mode;
+ pktin_mode_t in_mode = gbl_args->appl.in_mode;
+
+ thr = odp_thread_id();
+ max_burst = gbl_args->appl.burst_rx;
+
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr);
+
+ /* Join non-default groups */
+ for (i = 0; i < thr_args->num_grp_join; i++) {
+ if (odp_schedule_group_join(thr_args->group[i], &mask)) {
+ ODPH_ERR("Join failed: %i\n", i);
+ return -1;
+ }
+ }
+
+ num_pktio = thr_args->num_pktio;
+
+ if (num_pktio > MAX_PKTIOS) {
+ ODPH_ERR("Too many pktios %i\n", num_pktio);
+ return -1;
+ }
+
+ for (pktio = 0; pktio < num_pktio; pktio++) {
+ tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
+ pktout[pktio] = thr_args->pktio[pktio].pktout;
+ }
+
+ printf("[%02i] PKTIN_SCHED_%s_VECTOR, %s\n", thr,
+ (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
+ ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
+ (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
+
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ odp_event_t ev_tbl[MAX_PKT_BURST];
+ int events;
+
+ events = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
+
+ if (events <= 0)
+ continue;
+
+ for (i = 0; i < events; i++) {
+ odp_packet_vector_t pkt_vec = ODP_PACKET_VECTOR_INVALID;
+ odp_packet_t *pkt_tbl = NULL;
+ odp_packet_t pkt;
+ int src_idx, dst_idx;
+ int pkts = 0;
+
+ if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev_tbl[i]);
+ pkt_tbl = &pkt;
+ pkts = 1;
+ } else if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET_VECTOR) {
+ pkt_vec = odp_packet_vector_from_event(ev_tbl[i]);
+ pkts = odp_packet_vector_tbl(pkt_vec, &pkt_tbl);
+ } else if (state != NULL) {
+ pkts = handle_rx_state(state, ev_tbl, events);
+
+ if (pkts <= 0)
+ continue;
+ }
+
+ prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
+
+ pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
+
+ if (odp_unlikely(pkts) == 0) {
+ if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
+ odp_packet_vector_free(pkt_vec);
+ continue;
+ }
+
+ /* packets from the same queue are from the same interface */
+ src_idx = odp_packet_input_index(pkt_tbl[0]);
+ ODPH_ASSERT(src_idx >= 0);
+ dst_idx = gbl_args->dst_port_from_idx[src_idx];
+ fill_eth_addrs(pkt_tbl, pkts, dst_idx);
+
+ send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
+ pktout[dst_idx], state, stats);
+
+ if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
+ odp_packet_vector_free(pkt_vec);
+ }
+ }
+
+ /*
+ * Free prefetched packets before entering the thread barrier.
+ * Such packets can block sending of later packets in other threads
+ * that then would never enter the thread barrier and we would
+ * end up in a dead-lock.
+ */
+ odp_schedule_pause();
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ /* Make sure that latest stat writes are visible to other threads */
+ odp_mb_full();
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ /* Free remaining events in queues */
+ odp_schedule_resume();
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL,
+ odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ return 0;
+}
+
+/*
+ * Packet IO worker thread using scheduled queues
+ *
+ * arg thread arguments of type 'thread_args_t *'
+ */
+static int run_worker_sched_mode(void *arg)
+{
+ int pkts;
+ int thr;
+ int dst_idx;
+ int i;
+ int pktio, num_pktio;
+ uint16_t max_burst;
+ odp_thrmask_t mask;
+ odp_pktout_queue_t pktout[MAX_PKTIOS];
+ odp_queue_t tx_queue[MAX_PKTIOS];
+ char extra_str[EXTRA_STR_LEN];
+ thread_args_t *thr_args = arg;
+ stats_t *stats = &thr_args->stats;
+ const appl_args_t *appl_args = &gbl_args->appl;
+ state_t *state = appl_args->has_state ? &thr_args->state : NULL;
+ int use_event_queue = gbl_args->appl.out_mode;
+ pktin_mode_t in_mode = gbl_args->appl.in_mode;
+
+ thr = odp_thread_id();
+ max_burst = gbl_args->appl.burst_rx;
+
+ memset(extra_str, 0, EXTRA_STR_LEN);
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr);
+
+ /* Join non-default groups */
+ for (i = 0; i < thr_args->num_grp_join; i++) {
+ if (odp_schedule_group_join(thr_args->group[i], &mask)) {
+ ODPH_ERR("Join failed: %i\n", i);
+ return -1;
+ }
+
+ if (gbl_args->appl.verbose) {
+ uint64_t tmp = (uint64_t)(uintptr_t)thr_args->group[i];
+
+ printf("[%02i] Joined group 0x%" PRIx64 "\n", thr, tmp);
+ }
+ }
+
+ if (thr_args->num_grp_join)
+ snprintf(extra_str, EXTRA_STR_LEN, ", joined %i groups", thr_args->num_grp_join);
+ else if (gbl_args->appl.num_groups == 0)
+ snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_ALL");
+ else if (gbl_args->appl.num_groups)
+ snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_WORKER");
+
+ num_pktio = thr_args->num_pktio;
+
+ if (num_pktio > MAX_PKTIOS) {
+ ODPH_ERR("Too many pktios %i\n", num_pktio);
+ return -1;
+ }
+
+ for (pktio = 0; pktio < num_pktio; pktio++) {
+ tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
+ pktout[pktio] = thr_args->pktio[pktio].pktout;
+ }
+
+ printf("[%02i] PKTIN_SCHED_%s, %s%s\n", thr,
+ (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
+ ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
+ (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT", extra_str);
+
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ odp_event_t ev_tbl[MAX_PKT_BURST];
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ int src_idx;
+
+ pkts = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
+
+ if (pkts <= 0)
+ continue;
+
+ if (odp_unlikely(state != NULL)) {
+ pkts = handle_rx_state(state, ev_tbl, pkts);
+
+ if (pkts <= 0)
+ continue;
+ }
+
+ odp_packet_from_event_multi(pkt_tbl, ev_tbl, pkts);
+
+ prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
+
+ pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
+
+ if (odp_unlikely(pkts) == 0)
+ continue;
+
+ /* packets from the same queue are from the same interface */
+ src_idx = odp_packet_input_index(pkt_tbl[0]);
+ ODPH_ASSERT(src_idx >= 0);
+ dst_idx = gbl_args->dst_port_from_idx[src_idx];
+ fill_eth_addrs(pkt_tbl, pkts, dst_idx);
+
+ send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
+ pktout[dst_idx], state, stats);
+ }
+
+ /*
+ * Free prefetched packets before entering the thread barrier.
+ * Such packets can block sending of later packets in other threads
+ * that then would never enter the thread barrier and we would
+ * end up in a dead-lock.
+ */
+ odp_schedule_pause();
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+ odp_event_free(ev);
+ }
+
+ /* Make sure that latest stat writes are visible to other threads */
+ odp_mb_full();
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ /* Free remaining events in queues */
+ odp_schedule_resume();
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL,
+ odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ return 0;
+}
+
+/*
+ * Packet IO worker thread using plain queues
+ *
+ * arg thread arguments of type 'thread_args_t *'
+ */
+static int run_worker_plain_queue_mode(void *arg)
+{
+ int thr;
+ int pkts;
+ uint16_t max_burst;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ int dst_idx, num_pktio;
+ odp_queue_t queue;
+ odp_pktout_queue_t pktout;
+ odp_queue_t tx_queue;
+ int pktio = 0;
+ thread_args_t *thr_args = arg;
+ stats_t *stats = &thr_args->stats;
+ const appl_args_t *appl_args = &gbl_args->appl;
+ state_t *state = appl_args->has_state ? &thr_args->state : NULL;
+ int use_event_queue = gbl_args->appl.out_mode;
+ int i;
+
+ thr = odp_thread_id();
+ max_burst = gbl_args->appl.burst_rx;
+
+ num_pktio = thr_args->num_pktio;
+ dst_idx = thr_args->pktio[pktio].tx_idx;
+ queue = thr_args->pktio[pktio].rx_queue;
+ pktout = thr_args->pktio[pktio].pktout;
+ tx_queue = thr_args->pktio[pktio].tx_queue;
+
+ printf("[%02i] num pktios %i, PKTIN_QUEUE, %s\n", thr, num_pktio,
+ (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
+
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ odp_event_t event[MAX_PKT_BURST];
+
+ if (num_pktio > 1) {
+ dst_idx = thr_args->pktio[pktio].tx_idx;
+ queue = thr_args->pktio[pktio].rx_queue;
+ pktout = thr_args->pktio[pktio].pktout;
+ if (odp_unlikely(use_event_queue))
+ tx_queue = thr_args->pktio[pktio].tx_queue;
+
+ pktio++;
+ if (pktio == num_pktio)
+ pktio = 0;
+ }
+
+ pkts = odp_queue_deq_multi(queue, event, max_burst);
+ if (odp_unlikely(pkts <= 0))
+ continue;
+
+ odp_packet_from_event_multi(pkt_tbl, event, pkts);
+
+ prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
+
+ pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
+
+ if (odp_unlikely(pkts) == 0)
+ continue;
+
+ fill_eth_addrs(pkt_tbl, pkts, dst_idx);
+
+ send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
+ stats);
+ }
+
+ /* Make sure that latest stat writes are visible to other threads */
+ odp_mb_full();
+
+ /* Wait until pktio devices are stopped */
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ /* Free remaining events in queues */
+ for (i = 0; i < num_pktio; i++) {
+ odp_time_t recv_last = odp_time_local();
+ odp_time_t since_last;
+
+ queue = thr_args->pktio[i].rx_queue;
+ do {
+ odp_event_t ev = odp_queue_deq(queue);
+
+ if (ev != ODP_EVENT_INVALID) {
+ recv_last = odp_time_local();
+ odp_event_free(ev);
+ }
+
+ since_last = odp_time_diff(odp_time_local(), recv_last);
+ } while (odp_time_to_ns(since_last) < ODP_TIME_SEC_IN_NS);
+ }
+
+ return 0;
+}
+
+/*
+ * Packet IO worker thread accessing IO resources directly
+ *
+ * arg thread arguments of type 'thread_args_t *'
+ */
+static int run_worker_direct_mode(void *arg)
+{
+ int thr;
+ int pkts;
+ uint16_t max_burst;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ int dst_idx, num_pktio;
+ odp_pktin_queue_t pktin;
+ odp_pktout_queue_t pktout;
+ odp_queue_t tx_queue;
+ int pktio = 0;
+ thread_args_t *thr_args = arg;
+ stats_t *stats = &thr_args->stats;
+ const appl_args_t *appl_args = &gbl_args->appl;
+ state_t *state = appl_args->has_state ? &thr_args->state : NULL;
+ int use_event_queue = gbl_args->appl.out_mode;
+
+ thr = odp_thread_id();
+ max_burst = gbl_args->appl.burst_rx;
+
+ num_pktio = thr_args->num_pktio;
+ dst_idx = thr_args->pktio[pktio].tx_idx;
+ pktin = thr_args->pktio[pktio].pktin;
+ pktout = thr_args->pktio[pktio].pktout;
+ tx_queue = thr_args->pktio[pktio].tx_queue;
+
+ printf("[%02i] num pktios %i, PKTIN_DIRECT, %s\n", thr, num_pktio,
+ (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
+
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ if (num_pktio > 1) {
+ dst_idx = thr_args->pktio[pktio].tx_idx;
+ pktin = thr_args->pktio[pktio].pktin;
+ pktout = thr_args->pktio[pktio].pktout;
+ if (odp_unlikely(use_event_queue))
+ tx_queue = thr_args->pktio[pktio].tx_queue;
+
+ pktio++;
+ if (pktio == num_pktio)
+ pktio = 0;
+ }
+
+ pkts = odp_pktin_recv(pktin, pkt_tbl, max_burst);
+ if (odp_unlikely(pkts <= 0))
+ continue;
+
+ prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
+
+ pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
+
+ if (odp_unlikely(pkts) == 0)
+ continue;
+
+ fill_eth_addrs(pkt_tbl, pkts, dst_idx);
+
+ send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
+ stats);
+ }
+
+ /* Make sure that latest stat writes are visible to other threads */
+ odp_mb_full();
+
+ return 0;
+}
+
+static int set_pktin_vector_params(odp_pktin_queue_param_t *pktin_param, odp_pool_t vec_pool,
+ odp_pktio_capability_t pktio_capa)
+{
+ uint64_t vec_tmo_ns;
+ uint32_t vec_size;
+
+ pktin_param->vector.enable = true;
+ pktin_param->vector.pool = vec_pool;
+
+ if (gbl_args->appl.vec_size == 0)
+ vec_size = DEFAULT_VEC_SIZE;
+ else
+ vec_size = gbl_args->appl.vec_size;
+
+ if (vec_size > pktio_capa.vector.max_size ||
+ vec_size < pktio_capa.vector.min_size) {
+ if (gbl_args->appl.vec_size == 0) {
+ vec_size = (vec_size > pktio_capa.vector.max_size) ?
+ pktio_capa.vector.max_size : pktio_capa.vector.min_size;
+ printf("\nWarning: Modified vector size to %u\n\n", vec_size);
+ } else {
+ ODPH_ERR("Invalid pktio vector size %u, valid range [%u, %u]\n",
+ vec_size, pktio_capa.vector.min_size, pktio_capa.vector.max_size);
+ return -1;
+ }
+ }
+ pktin_param->vector.max_size = vec_size;
+
+ if (gbl_args->appl.vec_tmo_ns == 0)
+ vec_tmo_ns = DEFAULT_VEC_TMO;
+ else
+ vec_tmo_ns = gbl_args->appl.vec_tmo_ns;
+
+ if (vec_tmo_ns > pktio_capa.vector.max_tmo_ns ||
+ vec_tmo_ns < pktio_capa.vector.min_tmo_ns) {
+ if (gbl_args->appl.vec_tmo_ns == 0) {
+ vec_tmo_ns = (vec_tmo_ns > pktio_capa.vector.max_tmo_ns) ?
+ pktio_capa.vector.max_tmo_ns : pktio_capa.vector.min_tmo_ns;
+ printf("\nWarning: Modified vector timeout to %" PRIu64 "\n\n", vec_tmo_ns);
+ } else {
+ ODPH_ERR("Invalid vector timeout %" PRIu64 ", valid range [%" PRIu64
+ ", %" PRIu64 "]\n", vec_tmo_ns,
+ pktio_capa.vector.min_tmo_ns, pktio_capa.vector.max_tmo_ns);
+ return -1;
+ }
+ }
+ pktin_param->vector.max_tmo_ns = vec_tmo_ns;
+
+ return 0;
+}
+
+/*
+ * Create a pktio handle, optionally associating a default input queue.
+ *
+ * dev Name of device to open
+ * index Pktio index
+ * pool Pool to associate with device for packet RX/TX
+ *
+ * Returns 0 on success, -1 on failure
+ */
+static int create_pktio(const char *dev, int idx, int num_rx, int num_tx, odp_pool_t pool,
+ odp_pool_t vec_pool, odp_schedule_group_t group)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_schedule_sync_t sync_mode;
+ odp_pktio_capability_t pktio_capa;
+ odp_pktio_config_t config;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_queue_param_t compl_queue;
+ odp_pktio_op_mode_t mode_rx;
+ odp_pktio_op_mode_t mode_tx;
+ pktin_mode_t in_mode = gbl_args->appl.in_mode;
+ odp_pktio_info_t info;
+ uint8_t *addr;
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (in_mode == PLAIN_QUEUE)
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+ else if (in_mode != DIRECT_RECV) /* pktin_mode SCHED_* */
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ if (gbl_args->appl.out_mode != PKTOUT_DIRECT)
+ pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
+
+ if (num_rx == 0)
+ pktio_param.in_mode = ODP_PKTIN_MODE_DISABLED;
+
+ if (num_tx == 0)
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DISABLED;
+
+ pktio = odp_pktio_open(dev, pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Pktio open failed: %s\n", dev);
+ return -1;
+ }
+
+ if (odp_pktio_info(pktio, &info)) {
+ ODPH_ERR("Pktio info failed: %s\n", dev);
+ return -1;
+ }
+
+ if (odp_pktio_capability(pktio, &pktio_capa)) {
+ ODPH_ERR("Pktio capability query failed: %s\n", dev);
+ return -1;
+ }
+
+ odp_pktio_config_init(&config);
+
+ if (gbl_args->appl.input_ts) {
+ if (!pktio_capa.config.pktin.bit.ts_all) {
+ ODPH_ERR("Packet input timestamping not supported: %s\n", dev);
+ return -1;
+ }
+ config.pktin.bit.ts_all = 1;
+ }
+
+ config.parser.layer = ODP_PROTO_LAYER_NONE;
+ if (gbl_args->appl.error_check || gbl_args->appl.chksum)
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+
+ if (gbl_args->appl.chksum) {
+ config.pktout.bit.ipv4_chksum_ena = 1;
+ config.pktout.bit.udp_chksum_ena = 1;
+ config.pktout.bit.tcp_chksum_ena = 1;
+ }
+
+ if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED) {
+ if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
+ !(pktio_capa.tx_compl.mode_event && pktio_capa.tx_compl.queue_type_sched)) {
+ ODPH_ERR("Transmit event completion not supported: %s\n", dev);
+ return -1;
+ }
+
+ if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL &&
+ !(pktio_capa.tx_compl.mode_poll &&
+ pktio_capa.tx_compl.max_compl_id >= gbl_args->appl.tx_compl.tot_compl_id)) {
+ ODPH_ERR("Transmit poll completion not supported: %s\n", dev);
+ return -1;
+ }
+
+ if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT)
+ config.tx_compl.mode_event = 1;
+
+ if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
+ config.tx_compl.mode_poll = 1;
+ config.tx_compl.max_compl_id = gbl_args->appl.tx_compl.tot_compl_id;
+ }
+ }
+
+ /* Provide hint to pktio that packet references are not used */
+ config.pktout.bit.no_packet_refs = 1;
+
+ if (gbl_args->appl.pause_rx) {
+ if (!pktio_capa.flow_control.pause_rx) {
+ ODPH_ERR("Reception of pause frames not supported: %s\n", dev);
+ return -1;
+ }
+ config.flow_control.pause_rx = ODP_PKTIO_LINK_PAUSE_ON;
+ }
+
+ if (gbl_args->appl.pause_tx) {
+ if (!pktio_capa.flow_control.pause_tx) {
+ ODPH_ERR("Transmission of pause frames not supported: %s\n", dev);
+ return -1;
+ }
+ config.flow_control.pause_tx = ODP_PKTIO_LINK_PAUSE_ON;
+ }
+
+ odp_pktio_config(pktio, &config);
+
+ if (gbl_args->appl.promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
+ if (!pktio_capa.set_op.op.promisc_mode) {
+ ODPH_ERR("Promisc mode set not supported: %s\n", dev);
+ return -1;
+ }
+
+ /* Enable promisc mode */
+ if (odp_pktio_promisc_mode_set(pktio, true)) {
+ ODPH_ERR("Promisc mode enable failed: %s\n", dev);
+ return -1;
+ }
+ }
+
+ if (gbl_args->appl.mtu) {
+ uint32_t maxlen_input = pktio_capa.maxlen.max_input ? gbl_args->appl.mtu : 0;
+ uint32_t maxlen_output = pktio_capa.maxlen.max_output ? gbl_args->appl.mtu : 0;
+
+ if (!pktio_capa.set_op.op.maxlen) {
+ ODPH_ERR("Modifying interface MTU not supported: %s\n", dev);
+ return -1;
+ }
+
+ if (maxlen_input &&
+ (maxlen_input < pktio_capa.maxlen.min_input ||
+ maxlen_input > pktio_capa.maxlen.max_input)) {
+ ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
+ "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_input, dev,
+ pktio_capa.maxlen.min_input, pktio_capa.maxlen.max_input);
+ return -1;
+ }
+ if (maxlen_output &&
+ (maxlen_output < pktio_capa.maxlen.min_output ||
+ maxlen_output > pktio_capa.maxlen.max_output)) {
+ ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
+ "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_output, dev,
+ pktio_capa.maxlen.min_output, pktio_capa.maxlen.max_output);
+ return -1;
+ }
+
+ if (odp_pktio_maxlen_set(pktio, maxlen_input, maxlen_output)) {
+ ODPH_ERR("Setting MTU failed: %s\n", dev);
+ return -1;
+ }
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ odp_pktout_queue_param_init(&pktout_param);
+
+ /* By default use a queue per worker. Sched mode ignores rx side
+ * setting. */
+ mode_rx = ODP_PKTIO_OP_MT_UNSAFE;
+ mode_tx = ODP_PKTIO_OP_MT_UNSAFE;
+
+ if (gbl_args->appl.sched_mode) {
+ odp_schedule_prio_t prio;
+
+ if (gbl_args->appl.num_prio) {
+ prio = gbl_args->appl.prio[idx];
+ } else {
+ prio = odp_schedule_default_prio();
+ gbl_args->appl.prio[idx] = prio;
+ }
+
+ if (gbl_args->appl.in_mode == SCHED_ATOMIC)
+ sync_mode = ODP_SCHED_SYNC_ATOMIC;
+ else if (gbl_args->appl.in_mode == SCHED_ORDERED)
+ sync_mode = ODP_SCHED_SYNC_ORDERED;
+ else
+ sync_mode = ODP_SCHED_SYNC_PARALLEL;
+
+ pktin_param.queue_param.sched.prio = prio;
+ pktin_param.queue_param.sched.sync = sync_mode;
+ pktin_param.queue_param.sched.group = group;
+
+ if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT) {
+ odp_queue_param_init(&compl_queue);
+ compl_queue.type = ODP_QUEUE_TYPE_SCHED;
+ compl_queue.sched.prio = prio;
+ compl_queue.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ compl_queue.sched.group = group;
+ gbl_args->pktios[idx].compl_q = odp_queue_create(NULL, &compl_queue);
+
+ if (gbl_args->pktios[idx].compl_q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Creating completion queue failed: %s\n", dev);
+ return -1;
+ }
+ }
+ }
+
+ if (num_rx > (int)pktio_capa.max_input_queues) {
+ num_rx = pktio_capa.max_input_queues;
+ mode_rx = ODP_PKTIO_OP_MT;
+ printf("Warning: %s: maximum number of input queues: %i\n", dev, num_rx);
+ }
+
+ if (num_rx < gbl_args->appl.num_workers)
+ printf("Warning: %s: sharing %i input queues between %i workers\n",
+ dev, num_rx, gbl_args->appl.num_workers);
+
+ if (num_tx > (int)pktio_capa.max_output_queues) {
+ printf("Warning: %s: sharing %i output queues between %i workers\n",
+ dev, pktio_capa.max_output_queues, num_tx);
+ num_tx = pktio_capa.max_output_queues;
+ mode_tx = ODP_PKTIO_OP_MT;
+ }
+
+ pktin_param.hash_enable = (num_rx > 1 || gbl_args->appl.flow_aware) ? 1 : 0;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ pktin_param.num_queues = num_rx;
+ pktin_param.op_mode = mode_rx;
+
+ pktout_param.op_mode = mode_tx;
+ pktout_param.num_queues = num_tx;
+
+ if (gbl_args->appl.vector_mode) {
+ if (!pktio_capa.vector.supported) {
+ ODPH_ERR("Packet vector input not supported: %s\n", dev);
+ return -1;
+ }
+ if (set_pktin_vector_params(&pktin_param, vec_pool, pktio_capa))
+ return -1;
+ }
+
+ if (num_rx > 0 && odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Input queue config failed: %s\n", dev);
+ return -1;
+ }
+
+ if (num_tx > 0 && odp_pktout_queue_config(pktio, &pktout_param)) {
+ ODPH_ERR("Output queue config failed: %s\n", dev);
+ return -1;
+ }
+
+ if (num_rx > 0) {
+ if (gbl_args->appl.in_mode == DIRECT_RECV) {
+ if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin, num_rx)
+ != num_rx) {
+ ODPH_ERR("Pktin queue query failed: %s\n", dev);
+ return -1;
+ }
+ } else {
+ if (odp_pktin_event_queue(pktio, gbl_args->pktios[idx].rx_q, num_rx)
+ != num_rx) {
+ ODPH_ERR("Pktin event queue query failed: %s\n", dev);
+ return -1;
+ }
+ }
+ }
+
+ if (num_tx > 0) {
+ if (gbl_args->appl.out_mode == PKTOUT_DIRECT) {
+ if (odp_pktout_queue(pktio, gbl_args->pktios[idx].pktout, num_tx)
+ != num_tx) {
+ ODPH_ERR("Pktout queue query failed: %s\n", dev);
+ return -1;
+ }
+ } else {
+ if (odp_pktout_event_queue(pktio, gbl_args->pktios[idx].tx_q, num_tx)
+ != num_tx) {
+ ODPH_ERR("Event queue query failed: %s\n", dev);
+ return -1;
+ }
+ }
+ }
+
+ if (odp_pktio_mac_addr(pktio, gbl_args->port_eth_addr[idx].addr,
+ ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("Reading interface Ethernet address failed: %s\n", dev);
+ return -1;
+ }
+ addr = gbl_args->port_eth_addr[idx].addr;
+
+ printf(" dev: %s, drv: %s, rx_queues: %i, tx_queues: %i, mac: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", dev, info.drv_name, num_rx, num_tx,
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ if (gbl_args->appl.verbose)
+ odp_pktio_print(pktio);
+
+ gbl_args->pktios[idx].num_rx_queue = num_rx;
+ gbl_args->pktios[idx].num_tx_queue = num_tx;
+ gbl_args->pktios[idx].pktio = pktio;
+
+ return 0;
+}
+
+/*
+ * Print statistics
+ *
+ * num_workers Number of worker threads
+ * thr_stats Pointers to stats storage
+ * duration Number of seconds to loop in
+ * timeout Number of seconds for stats calculation
+ */
+static int print_speed_stats(int num_workers, stats_t **thr_stats,
+ int duration, int timeout)
+{
+ uint64_t pkts = 0;
+ uint64_t pkts_prev = 0;
+ uint64_t pps;
+ uint64_t rx_drops, tx_drops, tx_c_misses, tx_c_fails, copy_fails;
+ uint64_t maximum_pps = 0;
+ int i;
+ int elapsed = 0;
+ int stats_enabled = 1;
+ int loop_forever = (duration == 0);
+
+ if (timeout <= 0) {
+ stats_enabled = 0;
+ timeout = 1;
+ }
+ /* Wait for all threads to be ready*/
+ odp_barrier_wait(&gbl_args->init_barrier);
+
+ do {
+ pkts = 0;
+ rx_drops = 0;
+ tx_drops = 0;
+ tx_c_misses = 0;
+ tx_c_fails = 0;
+ copy_fails = 0;
+
+ sleep(timeout);
+
+ for (i = 0; i < num_workers; i++) {
+ pkts += thr_stats[i]->s.packets;
+ rx_drops += thr_stats[i]->s.rx_drops;
+ tx_drops += thr_stats[i]->s.tx_drops;
+ tx_c_misses += thr_stats[i]->s.tx_c_misses;
+ tx_c_fails += thr_stats[i]->s.tx_c_fails;
+ copy_fails += thr_stats[i]->s.copy_fails;
+ }
+ if (stats_enabled) {
+ pps = (pkts - pkts_prev) / timeout;
+ if (pps > maximum_pps)
+ maximum_pps = pps;
+ printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
+ maximum_pps);
+
+ if (gbl_args->appl.packet_copy)
+ printf("%" PRIu64 " copy fails, ", copy_fails);
+
+ if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
+ printf("%" PRIu64 " tx compl misses, %" PRIu64 " tx compl fails, ",
+ tx_c_misses, tx_c_fails);
+
+ printf("%" PRIu64 " rx drops, %" PRIu64 " tx drops\n",
+ rx_drops, tx_drops);
+
+ pkts_prev = pkts;
+ }
+ elapsed += timeout;
+ } while (!odp_atomic_load_u32(&gbl_args->exit_threads) && (loop_forever ||
+ (elapsed < duration)));
+
+ if (stats_enabled)
+ printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
+ maximum_pps);
+
+ return pkts > 100 ? 0 : -1;
+}
+
+static void print_port_mapping(void)
+{
+ int if_count;
+ int pktio;
+
+ if_count = gbl_args->appl.if_count;
+
+ printf("\nPort config\n--------------------\n");
+
+ for (pktio = 0; pktio < if_count; pktio++) {
+ const char *dev = gbl_args->appl.if_names[pktio];
+
+ printf("Port %i (%s)\n", pktio, dev);
+ printf(" rx workers %i\n",
+ gbl_args->pktios[pktio].num_rx_thr);
+ printf(" tx workers %i\n",
+ gbl_args->pktios[pktio].num_tx_thr);
+ printf(" rx queues %i\n",
+ gbl_args->pktios[pktio].num_rx_queue);
+ printf(" tx queues %i\n",
+ gbl_args->pktios[pktio].num_tx_queue);
+ }
+
+ printf("\n");
+}
+
+/*
+ * Find the destination port for a given input port
+ *
+ * port Input port index
+ */
+static int find_dest_port(int port)
+{
+ const char *output = gbl_args->appl.output_map[port];
+
+ /* Check output mappings first */
+ if (output != NULL)
+ for (int i = 0; i < gbl_args->appl.if_count; i++)
+ if (strcmp(output, gbl_args->appl.if_names[i]) == 0)
+ return i;
+
+ /* Even number of ports */
+ if (gbl_args->appl.if_count % 2 == 0)
+ return (port % 2 == 0) ? port + 1 : port - 1;
+
+ /* Odd number of ports */
+ if (port == gbl_args->appl.if_count - 1)
+ return 0;
+ else
+ return port + 1;
+}
+
+/*
+ * Bind worker threads to interfaces and calculate number of queues needed
+ *
+ * less workers (N) than interfaces (M)
+ * - assign each worker to process every Nth interface
+ * - workers process inequal number of interfaces, when M is not divisible by N
+ * - needs only single queue per interface
+ * otherwise
+ * - assign an interface to every Mth worker
+ * - interfaces are processed by inequal number of workers, when N is not
+ * divisible by M
+ * - tries to configure a queue per worker per interface
+ * - shares queues, if interface capability does not allows a queue per worker
+ */
+static void bind_workers(void)
+{
+ int if_count, num_workers;
+ int rx_idx, tx_idx, thr, pktio, i;
+ thread_args_t *thr_args;
+
+ if_count = gbl_args->appl.if_count;
+ num_workers = gbl_args->appl.num_workers;
+
+ if (gbl_args->appl.sched_mode) {
+ /* all threads receive and send on all pktios */
+ for (i = 0; i < if_count; i++) {
+ gbl_args->pktios[i].num_rx_thr = num_workers;
+ gbl_args->pktios[i].num_tx_thr = num_workers;
+ }
+
+ for (thr = 0; thr < num_workers; thr++) {
+ thr_args = &gbl_args->thread_args[thr];
+ thr_args->num_pktio = if_count;
+
+ /* In sched mode, pktios are not cross connected with
+ * local pktio indexes */
+ for (i = 0; i < if_count; i++) {
+ thr_args->pktio[i].rx_idx = i;
+ thr_args->pktio[i].tx_idx = i;
+ }
+ }
+ } else {
+ /* initialize port forwarding table */
+ for (rx_idx = 0; rx_idx < if_count; rx_idx++)
+ gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
+
+ if (if_count > num_workers) {
+ /* Less workers than pktios. Assign single worker per
+ * pktio. */
+ thr = 0;
+
+ for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
+ thr_args = &gbl_args->thread_args[thr];
+ pktio = thr_args->num_pktio;
+ /* Cross connect rx to tx */
+ tx_idx = gbl_args->dst_port[rx_idx];
+ thr_args->pktio[pktio].rx_idx = rx_idx;
+ thr_args->pktio[pktio].tx_idx = tx_idx;
+ thr_args->num_pktio++;
+
+ gbl_args->pktios[rx_idx].num_rx_thr++;
+ gbl_args->pktios[tx_idx].num_tx_thr++;
+
+ thr++;
+ if (thr >= num_workers)
+ thr = 0;
+ }
+ } else {
+ /* More workers than pktios. Assign at least one worker
+ * per pktio. */
+ rx_idx = 0;
+
+ for (thr = 0; thr < num_workers; thr++) {
+ thr_args = &gbl_args->thread_args[thr];
+ pktio = thr_args->num_pktio;
+ /* Cross connect rx to tx */
+ tx_idx = gbl_args->dst_port[rx_idx];
+ thr_args->pktio[pktio].rx_idx = rx_idx;
+ thr_args->pktio[pktio].tx_idx = tx_idx;
+ thr_args->num_pktio++;
+
+ gbl_args->pktios[rx_idx].num_rx_thr++;
+ gbl_args->pktios[tx_idx].num_tx_thr++;
+
+ rx_idx++;
+ if (rx_idx >= if_count)
+ rx_idx = 0;
+ }
+ }
+ }
+}
+
+/*
+ * Bind queues to threads and fill in missing thread arguments (handles)
+ */
+static void bind_queues(void)
+{
+ int num_workers;
+ int thr, i;
+
+ num_workers = gbl_args->appl.num_workers;
+
+ printf("\nQueue binding (indexes)\n-----------------------\n");
+
+ for (thr = 0; thr < num_workers; thr++) {
+ int rx_idx, tx_idx;
+ thread_args_t *thr_args = &gbl_args->thread_args[thr];
+ int num = thr_args->num_pktio;
+
+ printf("worker %i\n", thr);
+
+ for (i = 0; i < num; i++) {
+ int rx_queue, tx_queue;
+
+ rx_idx = thr_args->pktio[i].rx_idx;
+ tx_idx = thr_args->pktio[i].tx_idx;
+ rx_queue = gbl_args->pktios[rx_idx].next_rx_queue;
+ tx_queue = gbl_args->pktios[tx_idx].next_tx_queue;
+
+ thr_args->pktio[i].rx_queue_idx = rx_queue;
+ thr_args->pktio[i].tx_queue_idx = tx_queue;
+ thr_args->pktio[i].pktin =
+ gbl_args->pktios[rx_idx].pktin[rx_queue];
+ thr_args->pktio[i].rx_queue =
+ gbl_args->pktios[rx_idx].rx_q[rx_queue];
+ thr_args->pktio[i].pktout =
+ gbl_args->pktios[tx_idx].pktout[tx_queue];
+ thr_args->pktio[i].tx_queue =
+ gbl_args->pktios[tx_idx].tx_q[tx_queue];
+
+ if (!gbl_args->appl.sched_mode)
+ printf(" rx: pktio %i, queue %i\n",
+ rx_idx, rx_queue);
+
+ printf(" tx: pktio %i, queue %i\n",
+ tx_idx, tx_queue);
+
+ rx_queue++;
+ tx_queue++;
+
+ if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue)
+ rx_queue = 0;
+ if (tx_queue >= gbl_args->pktios[tx_idx].num_tx_queue)
+ tx_queue = 0;
+
+ gbl_args->pktios[rx_idx].next_rx_queue = rx_queue;
+ gbl_args->pktios[tx_idx].next_tx_queue = tx_queue;
+ }
+ }
+
+ printf("\n");
+}
+
+static void init_state(const appl_args_t *args, state_t *state, int thr_idx)
+{
+ const uint32_t cnt = args->tx_compl.thr_compl_id + 1;
+
+ state->tx_compl.opt.mode = args->tx_compl.mode;
+ state->tx_compl.init = thr_idx * cnt;
+ state->tx_compl.max = state->tx_compl.init + cnt - 1;
+ state->tx_compl.free_head = state->tx_compl.init;
+ state->tx_compl.poll_head = state->tx_compl.init;
+ state->tx_compl.num_act = 0;
+ state->tx_compl.max_act = state->tx_compl.max - state->tx_compl.init + 1;
+ state->tx_compl.interval = args->tx_compl.nth;
+ state->tx_compl.next_req = state->tx_compl.interval;
+}
+
+static void init_port_lookup_tbl(void)
+{
+ int rx_idx, if_count;
+
+ if_count = gbl_args->appl.if_count;
+
+ for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
+ odp_pktio_t pktio = gbl_args->pktios[rx_idx].pktio;
+ int pktio_idx = odp_pktio_index(pktio);
+ int dst_port = find_dest_port(rx_idx);
+
+ if (pktio_idx < 0) {
+ ODPH_ERR("Reading pktio (%s) index failed: %i\n",
+ gbl_args->appl.if_names[rx_idx], pktio_idx);
+
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args->dst_port_from_idx[pktio_idx] = dst_port;
+ }
+}
+
+/*
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane L2 forwarding application.\n"
+ "\n"
+ "Usage: %s [options]\n"
+ "\n"
+ " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
+ " In the above example,\n"
+ " eth0 will send pkts to eth1 and vice versa\n"
+ " eth2 will send pkts to eth3 and vice versa\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface <name> Eth interfaces (comma-separated, no spaces)\n"
+ " Interface count min 1, max %i\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -m, --mode <arg> Packet input mode\n"
+ " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n"
+ " 1: Scheduler mode with parallel queues:\n"
+ " PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n"
+ " 2: Scheduler mode with atomic queues:\n"
+ " PKTIN_MODE_SCHED + SCHED_SYNC_ATOMIC\n"
+ " 3: Scheduler mode with ordered queues:\n"
+ " PKTIN_MODE_SCHED + SCHED_SYNC_ORDERED\n"
+ " 4: Plain queue mode: PKTIN_MODE_QUEUE\n"
+ " -o, --out_mode <arg> Packet output mode\n"
+ " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n"
+ " 1: Queue mode: PKTOUT_MODE_QUEUE\n"
+ " -O, --output_map <list> List of destination ports for passed interfaces\n"
+ " (comma-separated, no spaces). Ordering follows\n"
+ " the '--interface' option, e.g. passing\n"
+ " '-i eth0,eth1' and '-O eth0,eth1' would result\n"
+ " in eth0 and eth1 looping packets back.\n"
+ " -c, --count <num> CPU count, 0=all available, default=1\n"
+ " -t, --time <sec> Time in seconds to run.\n"
+ " -a, --accuracy <sec> Time in seconds get print statistics\n"
+ " (default is 1 second).\n"
+ " -d, --dst_change <arg> 0: Don't change packets' dst eth addresses\n"
+ " 1: Change packets' dst eth addresses (default)\n"
+ " -s, --src_change <arg> 0: Don't change packets' src eth addresses\n"
+ " 1: Change packets' src eth addresses (default)\n"
+ " -r, --dst_addr <addr> Destination addresses (comma-separated, no\n"
+ " spaces) Requires also the -d flag to be set\n"
+ " -e, --error_check <arg> 0: Don't check packet errors (default)\n"
+ " 1: Check packet errors\n"
+ " -k, --chksum <arg> 0: Don't use checksum offload (default)\n"
+ " 1: Use checksum offload\n",
+ NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS);
+
+ printf(" -g, --groups <num> Number of new groups to create (1 ... num).\n"
+ " Interfaces are placed into the groups in round\n"
+ " robin.\n"
+ " 0: Use SCHED_GROUP_ALL (default)\n"
+ " -1: Use SCHED_GROUP_WORKER\n"
+ " -G, --group_mode <arg> Select how threads join new groups\n"
+ " (when -g > 0)\n"
+ " 0: All threads join all created groups\n"
+ " (default)\n"
+ " 1: All threads join first N created groups.\n"
+ " N is number of interfaces (== active\n"
+ " groups).\n"
+ " 2: Each thread joins a part of the first N\n"
+ " groups (in round robin).\n"
+ " -I, --prio <prio list> Schedule priority of packet input queues.\n"
+ " Comma separated list of priorities (no spaces).\n"
+ " A value per interface. All queues of an\n"
+ " interface have the same priority. Values must\n"
+ " be between odp_schedule_min_prio and\n"
+ " odp_schedule_max_prio.\n"
+ " odp_schedule_default_prio is used by default.\n"
+ " -b, --burst_rx <num> 0: Use max burst size (default)\n"
+ " num: Max number of packets per receive call\n"
+ " -q, --rx_queues <num> Number of RX queues per interface in scheduler\n"
+ " mode\n"
+ " 0: RX queue per worker CPU (default)\n"
+ " -p, --packet_copy 0: Don't copy packet (default)\n"
+ " 1: Create and send copy of the received packet.\n"
+ " Free the original packet.\n"
+ " -R, --data_rd <num> Number of packet data words (uint64_t) to read\n"
+ " from every received packet. Number of words is\n"
+ " rounded down to fit into the first segment of a\n"
+ " packet. Default is 0.\n"
+ " -y, --pool_per_if Create a packet (and packet vector) pool per\n"
+ " interface.\n"
+ " 0: Share a single pool between all interfaces\n"
+ " (default)\n"
+ " 1: Create a pool per interface\n"
+ " -n, --num_pkt <num> Number of packets per pool. Default is 16k or\n"
+ " the maximum capability. Use 0 for the default.\n"
+ " -u, --vector_mode Enable vector mode.\n"
+ " Supported only with scheduler packet input\n"
+ " modes (1-3).\n"
+ " -w, --num_vec <num> Number of vectors per pool.\n"
+ " Default is num_pkts divided by vec_size.\n"
+ " -x, --vec_size <num> Vector size (default %i).\n"
+ " -z, --vec_tmo_ns <ns> Vector timeout in ns (default %llu ns).\n"
+ " -M, --mtu <len> Interface MTU in bytes.\n"
+ " -P, --promisc_mode Enable promiscuous mode.\n"
+ " -l, --packet_len <len> Maximum length of packets supported\n"
+ " (default %d).\n"
+ " -L, --seg_len <len> Packet pool segment length\n"
+ " (default equal to packet length).\n"
+ " -F, --prefetch <num> Prefetch packet data in 64 byte multiples\n"
+ " (default 1).\n"
+ " -f, --flow_aware Enable flow aware scheduling.\n"
+ " -T, --input_ts Enable packet input timestamping.\n",
+ DEFAULT_VEC_SIZE, DEFAULT_VEC_TMO, POOL_PKT_LEN);
+
+ printf(" -C, --tx_compl <mode,n,max_id> Enable transmit completion with a specified\n"
+ " completion mode for nth packet, with maximum\n"
+ " completion ID per worker thread in case of poll\n"
+ " completion (comma-separated, no spaces).\n"
+ " 0: Event completion mode\n"
+ " 1: Poll completion mode\n"
+ " -X, --flow_control <mode> Ethernet flow control mode.\n"
+ " 0: Flow control disabled (default)\n"
+ " 1: Enable reception of pause frames\n"
+ " 2: Enable transmission of pause frames\n"
+ " 3: Enable reception and transmission of pause\n"
+ " frames\n"
+ " -v, --verbose Verbose output.\n"
+ " -V, --verbose_pkt Print debug information on every received\n"
+ " packet.\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n");
+}
+
+/*
+ * Parse and store the command line arguments
+ *
+ * argc argument count
+ * argv[] argument vector
+ * appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *token;
+ char *tmp_str, *tmp;
+ size_t str_len, len;
+ int i;
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"time", required_argument, NULL, 't'},
+ {"accuracy", required_argument, NULL, 'a'},
+ {"interface", required_argument, NULL, 'i'},
+ {"mode", required_argument, NULL, 'm'},
+ {"out_mode", required_argument, NULL, 'o'},
+ {"output_map", required_argument, NULL, 'O'},
+ {"dst_addr", required_argument, NULL, 'r'},
+ {"dst_change", required_argument, NULL, 'd'},
+ {"src_change", required_argument, NULL, 's'},
+ {"error_check", required_argument, NULL, 'e'},
+ {"chksum", required_argument, NULL, 'k'},
+ {"groups", required_argument, NULL, 'g'},
+ {"group_mode", required_argument, NULL, 'G'},
+ {"prio", required_argument, NULL, 'I'},
+ {"burst_rx", required_argument, NULL, 'b'},
+ {"rx_queues", required_argument, NULL, 'q'},
+ {"packet_copy", required_argument, NULL, 'p'},
+ {"data_rd", required_argument, NULL, 'R'},
+ {"pool_per_if", required_argument, NULL, 'y'},
+ {"num_pkt", required_argument, NULL, 'n'},
+ {"num_vec", required_argument, NULL, 'w'},
+ {"vec_size", required_argument, NULL, 'x'},
+ {"vec_tmo_ns", required_argument, NULL, 'z'},
+ {"vector_mode", no_argument, NULL, 'u'},
+ {"mtu", required_argument, NULL, 'M'},
+ {"promisc_mode", no_argument, NULL, 'P'},
+ {"packet_len", required_argument, NULL, 'l'},
+ {"seg_len", required_argument, NULL, 'L'},
+ {"prefetch", required_argument, NULL, 'F'},
+ {"flow_aware", no_argument, NULL, 'f'},
+ {"input_ts", no_argument, NULL, 'T'},
+ {"tx_compl", required_argument, NULL, 'C'},
+ {"flow_control", required_argument, NULL, 'X'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"verbose_pkt", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:t:a:i:m:o:O:r:d:s:e:k:g:G:I:"
+ "b:q:p:R:y:n:l:L:w:x:X:z:M:F:uPfTC:vVh";
+
+ appl_args->time = 0; /* loop forever if time to run is 0 */
+ appl_args->accuracy = 1; /* get and print pps stats second */
+ appl_args->cpu_count = 1; /* use one worker by default */
+ appl_args->dst_change = 1; /* change eth dst address by default */
+ appl_args->src_change = 1; /* change eth src address by default */
+ appl_args->num_groups = 0; /* use default group */
+ appl_args->group_mode = 0;
+ appl_args->error_check = 0; /* don't check packet errors by default */
+ appl_args->packet_copy = 0;
+ appl_args->burst_rx = 0;
+ appl_args->rx_queues = 0;
+ appl_args->verbose = 0;
+ appl_args->verbose_pkt = 0;
+ appl_args->chksum = 0; /* don't use checksum offload by default */
+ appl_args->pool_per_if = 0;
+ appl_args->num_pkt = 0;
+ appl_args->packet_len = POOL_PKT_LEN;
+ appl_args->seg_len = UINT32_MAX;
+ appl_args->mtu = 0;
+ appl_args->promisc_mode = 0;
+ appl_args->vector_mode = 0;
+ appl_args->num_vec = 0;
+ appl_args->vec_size = 0;
+ appl_args->vec_tmo_ns = 0;
+ appl_args->flow_aware = 0;
+ appl_args->input_ts = 0;
+ appl_args->num_prio = 0;
+ appl_args->prefetch = 1;
+ appl_args->data_rd = 0;
+ appl_args->flow_control = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'a':
+ appl_args->accuracy = atoi(optarg);
+ break;
+ case 'r':
+ len = strlen(optarg);
+ if (len == 0) {
+ ODPH_ERR("Bad dest address string\n");
+ exit(EXIT_FAILURE);
+ }
+
+ str_len = len + 1;
+
+ tmp_str = malloc(str_len);
+ if (tmp_str == NULL) {
+ ODPH_ERR("Dest address malloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* store the mac addresses names */
+ memcpy(tmp_str, optarg, str_len);
+ for (token = strtok(tmp_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ if (i >= MAX_PKTIOS) {
+ ODPH_ERR("Too many MAC addresses\n");
+ exit(EXIT_FAILURE);
+ }
+ if (odph_eth_addr_parse(&appl_args->addrs[i], token) != 0) {
+ ODPH_ERR("Invalid MAC address\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ appl_args->addr_count = i;
+ if (appl_args->addr_count < 1) {
+ ODPH_ERR("Bad dest address count\n");
+ exit(EXIT_FAILURE);
+ }
+ free(tmp_str);
+ break;
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ ODPH_ERR("Bad pktio interface string\n");
+ exit(EXIT_FAILURE);
+ }
+
+ str_len = len + 1;
+
+ appl_args->if_str = malloc(str_len);
+ if (appl_args->if_str == NULL) {
+ ODPH_ERR("Pktio interface malloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ memcpy(appl_args->if_str, optarg, str_len);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ appl_args->if_count = i;
+
+ if (appl_args->if_count < 1 || appl_args->if_count > MAX_PKTIOS) {
+ ODPH_ERR("Bad pktio interface count: %i\n", appl_args->if_count);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names = calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ memcpy(appl_args->if_str, optarg, str_len);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+ case 'm':
+ i = atoi(optarg);
+ if (i == 1)
+ appl_args->in_mode = SCHED_PARALLEL;
+ else if (i == 2)
+ appl_args->in_mode = SCHED_ATOMIC;
+ else if (i == 3)
+ appl_args->in_mode = SCHED_ORDERED;
+ else if (i == 4)
+ appl_args->in_mode = PLAIN_QUEUE;
+ else
+ appl_args->in_mode = DIRECT_RECV;
+ break;
+ case 'o':
+ i = atoi(optarg);
+ if (i != 0)
+ appl_args->out_mode = PKTOUT_QUEUE;
+ break;
+ case 'O':
+ if (strlen(optarg) == 0) {
+ ODPH_ERR("Bad output map string\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tmp_str = strdup(optarg);
+
+ if (tmp_str == NULL) {
+ ODPH_ERR("Output map string duplication failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ token = strtok(tmp_str, ",");
+
+ while (token) {
+ if (appl_args->num_om >= MAX_PKTIOS) {
+ ODPH_ERR("Bad output map element count\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->output_map[appl_args->num_om] = strdup(token);
+
+ if (appl_args->output_map[appl_args->num_om] == NULL) {
+ ODPH_ERR("Output map element duplication failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->num_om++;
+ token = strtok(NULL, ",");
+ }
+
+ free(tmp_str);
+ break;
+ case 'd':
+ appl_args->dst_change = atoi(optarg);
+ break;
+ case 's':
+ appl_args->src_change = atoi(optarg);
+ break;
+ case 'e':
+ appl_args->error_check = atoi(optarg);
+ break;
+ case 'k':
+ appl_args->chksum = atoi(optarg);
+ break;
+ case 'g':
+ appl_args->num_groups = atoi(optarg);
+ break;
+ case 'G':
+ appl_args->group_mode = atoi(optarg);
+ break;
+ case 'I':
+ len = strlen(optarg);
+ if (len == 0) {
+ ODPH_ERR("Bad priority list\n");
+ exit(EXIT_FAILURE);
+ }
+
+ str_len = len + 1;
+
+ tmp_str = malloc(str_len);
+ if (tmp_str == NULL) {
+ ODPH_ERR("Priority list malloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(tmp_str, optarg, str_len);
+ token = strtok(tmp_str, ",");
+
+ for (i = 0; token != NULL; token = strtok(NULL, ","), i++) {
+ if (i >= MAX_PKTIOS) {
+ ODPH_ERR("Too many priorities\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->prio[i] = atoi(token);
+ appl_args->num_prio++;
+ }
+
+ if (appl_args->num_prio == 0) {
+ ODPH_ERR("Bad priority list\n");
+ exit(EXIT_FAILURE);
+ }
+
+ free(tmp_str);
+ break;
+ case 'b':
+ appl_args->burst_rx = atoi(optarg);
+ break;
+ case 'q':
+ appl_args->rx_queues = atoi(optarg);
+ break;
+ case 'p':
+ appl_args->packet_copy = atoi(optarg);
+ break;
+ case 'R':
+ appl_args->data_rd = atoi(optarg);
+ break;
+ case 'y':
+ appl_args->pool_per_if = atoi(optarg);
+ break;
+ case 'n':
+ appl_args->num_pkt = atoi(optarg);
+ break;
+ case 'l':
+ appl_args->packet_len = atoi(optarg);
+ break;
+ case 'L':
+ appl_args->seg_len = atoi(optarg);
+ break;
+ case 'M':
+ appl_args->mtu = atoi(optarg);
+ break;
+ case 'P':
+ appl_args->promisc_mode = 1;
+ break;
+ case 'u':
+ appl_args->vector_mode = 1;
+ break;
+ case 'w':
+ appl_args->num_vec = atoi(optarg);
+ break;
+ case 'x':
+ appl_args->vec_size = atoi(optarg);
+ break;
+ case 'X':
+ appl_args->flow_control = atoi(optarg);
+ if (appl_args->flow_control == 1 || appl_args->flow_control == 3)
+ appl_args->pause_rx = true;
+ if (appl_args->flow_control == 2 || appl_args->flow_control == 3)
+ appl_args->pause_tx = true;
+ break;
+ case 'z':
+ appl_args->vec_tmo_ns = atoi(optarg);
+ break;
+ case 'F':
+ appl_args->prefetch = atoi(optarg);
+ break;
+ case 'f':
+ appl_args->flow_aware = 1;
+ break;
+ case 'T':
+ appl_args->input_ts = 1;
+ break;
+ case 'C':
+ if (strlen(optarg) == 0) {
+ ODPH_ERR("Bad transmit completion parameter string\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tmp_str = strdup(optarg);
+
+ if (tmp_str == NULL) {
+ ODPH_ERR("Transmit completion parameter string duplication"
+ " failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tmp = strtok(tmp_str, ",");
+
+ if (tmp == NULL) {
+ ODPH_ERR("Invalid transmit completion parameter format\n");
+ exit(EXIT_FAILURE);
+ }
+
+ i = atoi(tmp);
+
+ if (i == 0)
+ appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_EVENT;
+ else if (i == 1)
+ appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_POLL;
+
+ tmp = strtok(NULL, ",");
+
+ if (tmp == NULL) {
+ ODPH_ERR("Invalid transmit completion parameter format\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->tx_compl.nth = atoi(tmp);
+
+ if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
+ tmp = strtok(NULL, ",");
+
+ if (tmp == NULL) {
+ ODPH_ERR("Invalid transmit completion parameter format\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->tx_compl.thr_compl_id = atoi(tmp);
+ }
+
+ free(tmp_str);
+ break;
+ case 'v':
+ appl_args->verbose = 1;
+ break;
+ case 'V':
+ appl_args->verbose_pkt = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->if_count == 0) {
+ ODPH_ERR("No pktio interfaces\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->num_om && appl_args->num_om != appl_args->if_count) {
+ ODPH_ERR("Different number of output mappings and pktio interfaces\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->num_prio && appl_args->num_prio != appl_args->if_count) {
+ ODPH_ERR("Different number of priorities and pktio interfaces\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->addr_count != 0 && appl_args->addr_count != appl_args->if_count) {
+ ODPH_ERR("Number of dest addresses differs from number of interfaces\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->burst_rx > MAX_PKT_BURST) {
+ ODPH_ERR("Burst size (%i) too large. Maximum is %i.\n",
+ appl_args->burst_rx, MAX_PKT_BURST);
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED &&
+ appl_args->tx_compl.nth == 0) {
+ ODPH_ERR("Invalid packet interval for transmit completion: %u\n",
+ appl_args->tx_compl.nth);
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
+ (appl_args->in_mode == PLAIN_QUEUE || appl_args->in_mode == DIRECT_RECV)) {
+ ODPH_ERR("Transmit event completion mode not supported with plain queue or direct "
+ "input modes\n");
+ exit(EXIT_FAILURE);
+ }
+
+ appl_args->tx_compl.tot_compl_id = (appl_args->tx_compl.thr_compl_id + 1) *
+ appl_args->cpu_count - 1;
+
+ if (appl_args->burst_rx == 0)
+ appl_args->burst_rx = MAX_PKT_BURST;
+
+ appl_args->extra_feat = 0;
+ if (appl_args->error_check || appl_args->chksum ||
+ appl_args->packet_copy || appl_args->data_rd || appl_args->verbose_pkt)
+ appl_args->extra_feat = 1;
+
+ appl_args->has_state = 0;
+ if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
+ appl_args->has_state = 1;
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+static void print_options(void)
+{
+ int i;
+ appl_args_t *appl_args = &gbl_args->appl;
+
+ printf("\n"
+ "odp_l2fwd options\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ", appl_args->if_count);
+
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+ printf("\n"
+ "Mode: ");
+ if (appl_args->in_mode == DIRECT_RECV)
+ printf("PKTIN_DIRECT, ");
+ else if (appl_args->in_mode == PLAIN_QUEUE)
+ printf("PKTIN_QUEUE, ");
+ else if (appl_args->in_mode == SCHED_PARALLEL)
+ printf("PKTIN_SCHED_PARALLEL, ");
+ else if (appl_args->in_mode == SCHED_ATOMIC)
+ printf("PKTIN_SCHED_ATOMIC, ");
+ else if (appl_args->in_mode == SCHED_ORDERED)
+ printf("PKTIN_SCHED_ORDERED, ");
+
+ if (appl_args->out_mode)
+ printf("PKTOUT_QUEUE\n");
+ else
+ printf("PKTOUT_DIRECT\n");
+
+ if (appl_args->num_om > 0) {
+ printf("Output mappings: ");
+
+ for (i = 0; i < appl_args->num_om; ++i)
+ printf(" %s", appl_args->output_map[i]);
+
+ printf("\n");
+ }
+
+ printf("MTU: ");
+ if (appl_args->mtu)
+ printf("%i bytes\n", appl_args->mtu);
+ else
+ printf("interface default\n");
+ printf("Promisc mode: %s\n", appl_args->promisc_mode ?
+ "enabled" : "disabled");
+ if (appl_args->flow_control)
+ printf("Flow control: %s%s\n",
+ appl_args->pause_rx ? "rx " : "",
+ appl_args->pause_tx ? "tx" : "");
+ printf("Flow aware: %s\n", appl_args->flow_aware ?
+ "yes" : "no");
+ printf("Input TS: %s\n", appl_args->input_ts ? "yes" : "no");
+ printf("Burst size: %i\n", appl_args->burst_rx);
+ printf("RX queues per IF: %i\n", appl_args->rx_queues);
+ printf("Number of pools: %i\n", appl_args->pool_per_if ?
+ appl_args->if_count : 1);
+
+ if (appl_args->extra_feat || appl_args->has_state) {
+ printf("Extra features: %s%s%s%s%s%s\n",
+ appl_args->error_check ? "error_check " : "",
+ appl_args->chksum ? "chksum " : "",
+ appl_args->packet_copy ? "packet_copy " : "",
+ appl_args->data_rd ? "data_rd" : "",
+ appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED ? "tx_compl" : "",
+ appl_args->verbose_pkt ? "verbose_pkt" : "");
+ }
+
+ printf("Num worker threads: %i\n", appl_args->num_workers);
+ printf("CPU mask: %s\n", gbl_args->cpumaskstr);
+
+ if (appl_args->num_groups > 0)
+ printf("num groups: %i\n", appl_args->num_groups);
+ else if (appl_args->num_groups == 0)
+ printf("group: ODP_SCHED_GROUP_ALL\n");
+ else
+ printf("group: ODP_SCHED_GROUP_WORKER\n");
+
+ printf("Packets per pool: %u\n", appl_args->num_pkt);
+ printf("Packet length: %u\n", appl_args->packet_len);
+ printf("Segment length: %u\n", appl_args->seg_len == UINT32_MAX ? 0 :
+ appl_args->seg_len);
+ printf("Read data: %u bytes\n", appl_args->data_rd * 8);
+ printf("Prefetch data %u bytes\n", appl_args->prefetch * 64);
+ printf("Vectors per pool: %u\n", appl_args->num_vec);
+ printf("Vector size: %u\n", appl_args->vec_size);
+ printf("Priority per IF: ");
+
+ for (i = 0; i < appl_args->if_count; i++)
+ printf(" %i", appl_args->prio[i]);
+
+ printf("\n\n");
+}
+
+static void gbl_args_init(args_t *args)
+{
+ int pktio, queue;
+
+ memset(args, 0, sizeof(args_t));
+ odp_atomic_init_u32(&args->exit_threads, 0);
+
+ for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
+ args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
+
+ for (queue = 0; queue < MAX_QUEUES; queue++)
+ args->pktios[pktio].rx_q[queue] = ODP_QUEUE_INVALID;
+
+ args->pktios[pktio].compl_q = ODP_QUEUE_INVALID;
+ }
+
+ args->appl.tx_compl.mode = ODP_PACKET_TX_COMPL_DISABLED;
+}
+
+static void create_groups(int num, odp_schedule_group_t *group)
+{
+ int i;
+ odp_thrmask_t zero;
+
+ odp_thrmask_zero(&zero);
+
+ /* Create groups */
+ for (i = 0; i < num; i++) {
+ group[i] = odp_schedule_group_create(NULL, &zero);
+
+ if (group[i] == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("Group create failed\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static int set_vector_pool_params(odp_pool_param_t *params, const odp_pool_capability_t *pool_capa)
+{
+ uint32_t num_vec, vec_size;
+
+ if (gbl_args->appl.vec_size == 0)
+ vec_size = DEFAULT_VEC_SIZE;
+ else
+ vec_size = gbl_args->appl.vec_size;
+
+ ODPH_ASSERT(pool_capa->vector.max_size > 0);
+ if (vec_size > pool_capa->vector.max_size) {
+ if (gbl_args->appl.vec_size == 0) {
+ vec_size = pool_capa->vector.max_size;
+ printf("\nWarning: Vector size reduced to %u\n\n", vec_size);
+ } else {
+ ODPH_ERR("Vector size too big %u. Maximum is %u.\n",
+ vec_size, pool_capa->vector.max_size);
+ return -1;
+ }
+ }
+
+ if (gbl_args->appl.num_vec == 0) {
+ uint32_t num_pkt = gbl_args->appl.num_pkt ?
+ gbl_args->appl.num_pkt : DEFAULT_NUM_PKT;
+
+ num_vec = (num_pkt + vec_size - 1) / vec_size;
+ } else {
+ num_vec = gbl_args->appl.num_vec;
+ }
+
+ if (pool_capa->vector.max_num && num_vec > pool_capa->vector.max_num) {
+ if (gbl_args->appl.num_vec == 0) {
+ num_vec = pool_capa->vector.max_num;
+ printf("\nWarning: number of vectors reduced to %u\n\n", num_vec);
+ } else {
+ ODPH_ERR("Too many vectors (%u) per pool. Maximum is %u.\n",
+ num_vec, pool_capa->vector.max_num);
+ return -1;
+ }
+ }
+
+ params->vector.num = num_vec;
+ params->vector.max_size = vec_size;
+ params->type = ODP_POOL_VECTOR;
+
+ return 0;
+}
+
+/*
+ * L2 forwarding main function
+ */
+int main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_param_t thr_param[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ int i;
+ int num_workers, num_thr;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask;
+ odph_ethaddr_t new_addr;
+ odp_pool_param_t params;
+ int ret;
+ stats_t *stats[MAX_WORKERS];
+ int if_count, num_pools, num_vec_pools;
+ int (*thr_run_func)(void *);
+ odp_instance_t instance;
+ int num_groups, max_groups;
+ odp_schedule_group_t group[MAX_GROUPS];
+ odp_pool_t pool_tbl[MAX_PKTIOS], vec_pool_tbl[MAX_PKTIOS];
+ odp_pool_t pool, vec_pool;
+ odp_init_t init;
+ odp_pool_capability_t pool_capa;
+ odp_schedule_config_t sched_config;
+ odp_schedule_capability_t sched_capa;
+ uint32_t pkt_len, num_pkt, seg_len;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init);
+
+ /* List features not to be used (may optimize performance) */
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ if (setup_sig_handler()) {
+ ODPH_ERR("Signal handler setup failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+
+ if (gbl_args == NULL) {
+ ODPH_ERR("Shared mem addr failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ gbl_args_init(gbl_args);
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ odp_sys_info_print();
+
+ if (sched_mode(gbl_args->appl.in_mode))
+ gbl_args->appl.sched_mode = 1;
+
+ num_workers = MAX_WORKERS;
+ if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
+ num_workers = gbl_args->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, gbl_args->cpumaskstr, sizeof(gbl_args->cpumaskstr));
+
+ gbl_args->appl.num_workers = num_workers;
+
+ print_options();
+
+ for (i = 0; i < num_workers; i++)
+ gbl_args->thread_args[i].thr_idx = i;
+
+ if_count = gbl_args->appl.if_count;
+
+ num_pools = 1;
+ if (gbl_args->appl.pool_per_if)
+ num_pools = if_count;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ if (num_pools > (int)pool_capa.pkt.max_pools) {
+ ODPH_ERR("Too many pools %i\n", num_pools);
+ return -1;
+ }
+
+ pkt_len = gbl_args->appl.packet_len;
+
+ if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len) {
+ pkt_len = pool_capa.pkt.max_len;
+ printf("\nWarning: packet length reduced to %u\n\n", pkt_len);
+ }
+
+ if (gbl_args->appl.seg_len == UINT32_MAX)
+ seg_len = gbl_args->appl.packet_len;
+ else
+ seg_len = gbl_args->appl.seg_len;
+
+ /* Check whether we have sufficient segments to support requested packet
+ * length, if not adjust to bigger segment size */
+ if (seg_len < (pkt_len / pool_capa.pkt.max_segs_per_pkt))
+ seg_len = pkt_len / pool_capa.pkt.max_segs_per_pkt;
+
+ if (pool_capa.pkt.min_seg_len && seg_len < pool_capa.pkt.min_seg_len)
+ seg_len = pool_capa.pkt.min_seg_len;
+
+ if (pool_capa.pkt.max_seg_len && seg_len > pool_capa.pkt.max_seg_len)
+ seg_len = pool_capa.pkt.max_seg_len;
+
+ if ((gbl_args->appl.seg_len != UINT32_MAX) && (seg_len != gbl_args->appl.seg_len))
+ printf("\nWarning: Segment length requested %d configured %d\n",
+ gbl_args->appl.seg_len, seg_len);
+
+ if (seg_len < gbl_args->appl.data_rd * 8) {
+ ODPH_ERR("Requested data read length %u exceeds maximum segment length %u\n",
+ gbl_args->appl.data_rd * 8, seg_len);
+ return -1;
+ }
+
+ /* zero means default number of packets */
+ if (gbl_args->appl.num_pkt == 0)
+ num_pkt = DEFAULT_NUM_PKT;
+ else
+ num_pkt = gbl_args->appl.num_pkt;
+
+ if (pool_capa.pkt.max_num && num_pkt > pool_capa.pkt.max_num) {
+ if (gbl_args->appl.num_pkt == 0) {
+ num_pkt = pool_capa.pkt.max_num;
+ printf("\nWarning: number of packets reduced to %u\n\n",
+ num_pkt);
+ } else {
+ ODPH_ERR("Too many packets %u. Maximum is %u.\n",
+ num_pkt, pool_capa.pkt.max_num);
+ return -1;
+ }
+ }
+
+ gbl_args->num_pkt = num_pkt;
+ gbl_args->pkt_len = pkt_len;
+ gbl_args->seg_len = seg_len;
+
+ printf("Resulting pool parameter values:\n");
+ printf("Packets per pool: %u\n", num_pkt);
+ printf("Packet length: %u\n", pkt_len);
+ printf("Segment length: %u\n", seg_len);
+
+ /* Create packet pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = seg_len;
+ params.pkt.len = pkt_len;
+ params.pkt.num = num_pkt;
+ params.type = ODP_POOL_PACKET;
+
+ for (i = 0; i < num_pools; i++) {
+ pool_tbl[i] = odp_pool_create("packet pool", &params);
+
+ if (pool_tbl[i] == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed %i\n", i);
+ exit(EXIT_FAILURE);
+ }
+
+ if (gbl_args->appl.verbose)
+ odp_pool_print(pool_tbl[i]);
+ }
+
+ /* Create vector pool */
+ num_vec_pools = 0;
+ if (gbl_args->appl.vector_mode) {
+ if (!sched_mode(gbl_args->appl.in_mode)) {
+ ODPH_ERR("Vector mode only supports scheduler pktin modes (1-3)\n");
+ return -1;
+ }
+
+ num_vec_pools = gbl_args->appl.pool_per_if ? if_count : 1;
+ if (num_vec_pools > (int)pool_capa.vector.max_pools) {
+ ODPH_ERR("Too many vector pools %i\n", num_vec_pools);
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ if (set_vector_pool_params(&params, &pool_capa))
+ return -1;
+
+ gbl_args->vector_num = params.vector.num;
+ gbl_args->vector_max_size = params.vector.max_size;
+
+ /* Print resulting values */
+ printf("Vectors per pool: %u\n", gbl_args->vector_num);
+ printf("Vector size: %u\n", gbl_args->vector_max_size);
+
+ for (i = 0; i < num_vec_pools; i++) {
+ vec_pool_tbl[i] = odp_pool_create("vector pool", &params);
+
+ if (vec_pool_tbl[i] == ODP_POOL_INVALID) {
+ ODPH_ERR("Vector pool create failed %i\n", i);
+ exit(EXIT_FAILURE);
+ }
+
+ if (gbl_args->appl.verbose)
+ odp_pool_print(vec_pool_tbl[i]);
+ }
+ }
+
+ printf("\n");
+
+ bind_workers();
+
+ odp_schedule_config_init(&sched_config);
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("Schedule capability failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (gbl_args->appl.flow_aware) {
+ if (sched_capa.max_flow_id) {
+ sched_config.max_flow_id = sched_capa.max_flow_id;
+ } else {
+ ODPH_ERR("Flow aware mode not supported\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ num_groups = gbl_args->appl.num_groups;
+ /* Predefined groups are enabled by default */
+ max_groups = sched_capa.max_groups - 3;
+ if (max_groups > MAX_GROUPS)
+ max_groups = MAX_GROUPS;
+
+ if (num_groups > max_groups) {
+ ODPH_ERR("Too many groups. Maximum is %i.\n", max_groups);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_schedule_config(&sched_config);
+
+ /* Default */
+ if (num_groups == 0) {
+ group[0] = ODP_SCHED_GROUP_ALL;
+ num_groups = 1;
+ } else if (num_groups == -1) {
+ group[0] = ODP_SCHED_GROUP_WORKER;
+ num_groups = 1;
+ } else {
+ create_groups(num_groups, group);
+ }
+
+ pool = pool_tbl[0];
+ vec_pool = vec_pool_tbl[0];
+
+ printf("\nInterfaces\n----------\n");
+
+ for (i = 0; i < if_count; ++i) {
+ const char *dev = gbl_args->appl.if_names[i];
+ int num_rx, num_tx;
+ odp_schedule_group_t grp;
+
+ /* A queue per worker in scheduled mode */
+ num_rx = gbl_args->appl.rx_queues > 0 ? gbl_args->appl.rx_queues : num_workers;
+ num_tx = num_workers;
+
+ if (!gbl_args->appl.sched_mode) {
+ /* A queue per assigned worker */
+ num_rx = gbl_args->pktios[i].num_rx_thr;
+ num_tx = gbl_args->pktios[i].num_tx_thr;
+ }
+
+ /* Round robin pktios to groups */
+ grp = group[i % num_groups];
+
+ if (gbl_args->appl.pool_per_if) {
+ pool = pool_tbl[i];
+ vec_pool = vec_pool_tbl[i];
+ }
+
+ if (create_pktio(dev, i, num_rx, num_tx, pool, vec_pool, grp))
+ exit(EXIT_FAILURE);
+
+ /* Save destination eth address */
+ if (gbl_args->appl.dst_change) {
+ /* 02:00:00:00:00:XX */
+ memset(&new_addr, 0, sizeof(odph_ethaddr_t));
+ if (gbl_args->appl.addr_count) {
+ memcpy(&new_addr, &gbl_args->appl.addrs[i],
+ sizeof(odph_ethaddr_t));
+ } else {
+ new_addr.addr[0] = 0x02;
+ new_addr.addr[5] = i;
+ }
+ gbl_args->dst_eth_addr[i] = new_addr;
+ }
+ }
+
+ gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
+
+ bind_queues();
+
+ init_port_lookup_tbl();
+
+ if (!gbl_args->appl.sched_mode)
+ print_port_mapping();
+
+ odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
+ odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
+
+ if (gbl_args->appl.in_mode == DIRECT_RECV)
+ thr_run_func = run_worker_direct_mode;
+ else if (gbl_args->appl.in_mode == PLAIN_QUEUE)
+ thr_run_func = run_worker_plain_queue_mode;
+ else /* SCHED_PARALLEL / SCHED_ATOMIC / SCHED_ORDERED */
+ thr_run_func = gbl_args->appl.vector_mode ?
+ run_worker_sched_mode_vector : run_worker_sched_mode;
+
+ /* Create worker threads */
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ /* Synchronize thread start up. Test runs are more repeatable when
+ * thread / thread ID / CPU ID mapping stays constant. */
+ thr_common.sync = 1;
+
+ for (i = 0; i < num_workers; ++i) {
+ int j;
+ int num_join;
+ int mode = gbl_args->appl.group_mode;
+
+ init_state(&gbl_args->appl, &gbl_args->thread_args[i].state, i);
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = thr_run_func;
+ thr_param[i].arg = &gbl_args->thread_args[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+
+ gbl_args->thread_args[i].num_grp_join = 0;
+
+ /* Fill in list of groups to join */
+ if (gbl_args->appl.num_groups > 0) {
+ num_join = if_count < num_groups ? if_count : num_groups;
+
+ if (mode == 0 || mode == 1) {
+ /* All threads join all groups */
+ if (mode == 0)
+ num_join = num_groups;
+
+ gbl_args->thread_args[i].num_grp_join = num_join;
+
+ for (j = 0; j < num_join; j++)
+ gbl_args->thread_args[i].group[j] = group[j];
+ } else {
+ /* Thread joins first groups in round robin */
+ if (num_workers >= num_join) {
+ gbl_args->thread_args[i].num_grp_join = 1;
+ gbl_args->thread_args[i].group[0] = group[i % num_join];
+ } else {
+ int cnt = 0;
+
+ for (j = 0; i + j < num_join; j += num_workers) {
+ gbl_args->thread_args[i].group[cnt] = group[i + j];
+ cnt++;
+ }
+
+ gbl_args->thread_args[i].num_grp_join = cnt;
+ }
+ }
+ }
+
+ stats[i] = &gbl_args->thread_args[i].stats;
+ }
+
+ num_thr = odph_thread_create(gbl_args->thread_tbl, &thr_common,
+ thr_param, num_workers);
+
+ if (num_thr != num_workers) {
+ ODPH_ERR("Worker create failed: %i\n", num_thr);
+ exit(EXIT_FAILURE);
+ }
+
+ if (gbl_args->appl.verbose)
+ odp_shm_print_all();
+
+ /* Start packet receive and transmit */
+ for (i = 0; i < if_count; ++i) {
+ odp_pktio_t pktio;
+
+ pktio = gbl_args->pktios[i].pktio;
+ ret = odp_pktio_start(pktio);
+ if (ret) {
+ ODPH_ERR("Pktio start failed: %s\n", gbl_args->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
+ gbl_args->appl.accuracy);
+
+ for (i = 0; i < if_count; ++i) {
+ if (odp_pktio_stop(gbl_args->pktios[i].pktio)) {
+ ODPH_ERR("Pktio stop failed: %s\n", gbl_args->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+ if (gbl_args->appl.in_mode != DIRECT_RECV)
+ odp_barrier_wait(&gbl_args->term_barrier);
+
+ /* Master thread waits for other threads to exit */
+ num_thr = odph_thread_join(gbl_args->thread_tbl, num_workers);
+ if (num_thr != num_workers) {
+ ODPH_ERR("Worker join failed: %i\n", num_thr);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < if_count; ++i) {
+ odp_pktio_t pktio = gbl_args->pktios[i].pktio;
+
+ if (gbl_args->appl.verbose && odp_pktio_extra_stat_info(pktio, NULL, 0) > 0) {
+ printf("Pktio %s extra statistics:\n", gbl_args->appl.if_names[i]);
+ odp_pktio_extra_stats_print(pktio);
+ }
+
+ if (gbl_args->pktios[i].compl_q != ODP_QUEUE_INVALID)
+ (void)odp_queue_destroy(gbl_args->pktios[i].compl_q);
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Pktio close failed: %s\n", gbl_args->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ free(gbl_args->appl.if_names);
+ free(gbl_args->appl.if_str);
+
+ for (i = 0; i < gbl_args->appl.num_om; i++)
+ free(gbl_args->appl.output_map[i]);
+
+ gbl_args = NULL;
+ odp_mb_full();
+
+ for (i = 0; i < num_pools; i++) {
+ if (odp_pool_destroy(pool_tbl[i])) {
+ ODPH_ERR("Pool destroy failed: %i\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (i = 0; i < num_vec_pools; i++) {
+ if (odp_pool_destroy(vec_pool_tbl[i])) {
+ ODPH_ERR("Vector pool destroy failed: %i\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shm free failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Term local failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Term global failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_l2fwd_run.sh b/test/performance/odp_l2fwd_run.sh
new file mode 100755
index 000000000..ae5bdef18
--- /dev/null
+++ b/test/performance/odp_l2fwd_run.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2015-2018 Linaro Limited
+#
+
+# TEST_DIR is set by Makefile, when we add a rule to Makefile for odp_l2fwd_run
+# we can use TEST_DIR the same way odp_pktio_run uses it now.
+# If TEST_DIR is not set it means we are not running with make, and in this case
+# there are two situations:
+# 1. user build ODP in the same dir as the source (most likely)
+# here the user can simply call odp_l2fwd_run
+# 2. user may have built ODP in a separate build dir (like bitbake usually does)
+# here the user has to do something like $ODP/test/performance/odp_l2fwd_run
+#
+# In both situations the script assumes that the user is in the directory where
+# odp_l2fwd exists. If that's not true, then the user has to specify the path
+# to it and run:
+# TEST_DIR=$builddir $ODP/test/performance/odp_l2fwd_run
+
+# directory where test binaries have been built
+TEST_DIR="${TEST_DIR:-$PWD}"
+# directory where test sources are, including scripts
+TEST_SRC_DIR=$(dirname $0)
+
+PATH=$TEST_DIR:$PATH
+
+# exit codes expected by automake for skipped tests
+TEST_SKIPPED=77
+
+VALIDATION_TESTDIR=platform/$ODP_PLATFORM/test/validation
+PLATFORM_VALIDATION=${TEST_SRC_DIR}/../../$VALIDATION_TESTDIR
+
+# Use installed pktio env or for make check take it from platform directory
+if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+elif [ "$ODP_PLATFORM" = "" ]; then
+ echo "$0: error: ODP_PLATFORM must be defined"
+ # not skipped as this should never happen via "make check"
+ exit 1
+elif [ -f ${PLATFORM_VALIDATION}/api/pktio/pktio_env ]; then
+ . ${PLATFORM_VALIDATION}/api/pktio/pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test."
+ echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
+ exit 1
+fi
+
+run_l2fwd()
+{
+ setup_pktio_env clean # install trap to call cleanup_pktio_env
+
+ if [ $? -ne 0 ]; then
+ echo "setup_pktio_env error $?"
+ exit $TEST_SKIPPED
+ fi
+
+ type odp_packet_gen > /dev/null
+ if [ $? -ne 0 ]; then
+ echo "odp_packet_gen not installed. Aborting."
+ cleanup_pktio_env
+ exit 1
+ fi
+
+ # Run odp_packet_gen with one tx thread
+ (odp_packet_gen${EXEEXT} --gap 0 -i $IF0 \
+ --ipv4_src 192.168.0.1 --ipv4_dst 192.168.0.2 \
+ -r 0 -t 1 2>&1 > /dev/null) \
+ 2>&1 > /dev/null &
+
+ GEN_PID=$!
+
+ LOG=odp_l2fwd_tmp.log
+
+ # Max 2 workers
+ odp_l2fwd${EXEEXT} -i $IF1,$IF2 -m 0 -t 5 -c 2 | tee $LOG
+ ret=${PIPESTATUS[0]}
+
+ kill -2 ${GEN_PID}
+
+ if [ ! -f $LOG ]; then
+ echo "FAIL: $LOG not found"
+ ret=1
+ elif [ $ret -eq 0 ]; then
+ PASS_PPS=5000
+ if [ "${TEST}" = "coverage" ]; then
+ PASS_PPS=10
+ fi
+ MAX_PPS=$(awk '/TEST RESULT/ {print $3}' $LOG)
+ if [ "$MAX_PPS" -lt "$PASS_PPS" ]; then
+ echo "FAIL: pps below threshold $MAX_PPS < $PASS_PPS"
+ ret=1
+ fi
+ fi
+
+ rm -f $LOG
+ cleanup_pktio_env
+
+ exit $ret
+}
+
+case "$1" in
+ setup) setup_pktio_env ;;
+ cleanup) cleanup_pktio_env ;;
+ *) run_l2fwd ;;
+esac
diff --git a/test/performance/odp_lock_perf.c b/test/performance/odp_lock_perf.c
new file mode 100644
index 000000000..43dea0728
--- /dev/null
+++ b/test/performance/odp_lock_perf.c
@@ -0,0 +1,696 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_lock_perf.c
+ *
+ * Performance test application for lock APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/* Max number of workers if num_cpu=0 */
+#define DEFAULT_MAX_WORKERS 10
+
+/* Max number of counters */
+#define MAX_COUNTERS 8
+
+#define TEST_INFO(name, test, validate) { name, test, validate }
+
+typedef enum repeat_t {
+ REPEAT_NO,
+ REPEAT_UNTIL_FAIL,
+ REPEAT_FOREVER,
+} repeat_t;
+
+typedef enum place_t {
+ PLACE_PACK,
+ PLACE_SEPARATE,
+ PLACE_ALL_SEPARATE,
+} place_t;
+
+/* Command line options */
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t type;
+ uint64_t num_round;
+ repeat_t repeat;
+ uint32_t num_counter;
+ place_t place;
+} test_options_t;
+
+/* command line options default values */
+static test_options_t test_options_def = {
+ .num_cpu = 0,
+ .type = 0,
+ .num_round = 100000,
+ .repeat = REPEAT_NO,
+ .num_counter = 2,
+ .place = 2,
+};
+
+typedef struct test_global_t test_global_t;
+
+/* Test function template */
+typedef void (*test_fn_t)(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter);
+/* Test result validation function template */
+typedef int (*validate_fn_t)(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter);
+
+/* Worker thread context */
+typedef struct test_thread_ctx_t {
+ test_global_t *global;
+ test_fn_t func;
+ uint64_t nsec;
+ uint32_t idx;
+} test_thread_ctx_t;
+
+/* Global data */
+struct test_global_t {
+ test_options_t test_options;
+ uint32_t cur_type;
+ odp_barrier_t barrier;
+ odp_cpumask_t cpumask;
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_thread_ctx_t thread_ctx[ODP_THREAD_COUNT_MAX];
+ struct {
+ struct ODP_ALIGNED_CACHE {
+ odp_spinlock_t lock;
+ uint64_t counter[MAX_COUNTERS];
+ } spinlock;
+ struct ODP_ALIGNED_CACHE {
+ odp_spinlock_recursive_t lock;
+ uint64_t counter[MAX_COUNTERS];
+ } spinlock_recursive;
+ struct ODP_ALIGNED_CACHE {
+ odp_rwlock_t lock;
+ uint64_t counter[MAX_COUNTERS];
+ } rwlock;
+ struct ODP_ALIGNED_CACHE {
+ odp_rwlock_recursive_t lock;
+ uint64_t counter[MAX_COUNTERS];
+ } rwlock_recursive;
+ struct ODP_ALIGNED_CACHE {
+ odp_ticketlock_t lock;
+ uint64_t counter[MAX_COUNTERS];
+ } ticketlock;
+ struct ODP_ALIGNED_CACHE {
+ uint64_t counter[MAX_COUNTERS];
+ } separate;
+ struct {
+ uint64_t ODP_ALIGNED_CACHE counter;
+ } all_separate[MAX_COUNTERS];
+ } item;
+};
+
+typedef struct {
+ const char *name;
+ test_fn_t test_fn;
+ validate_fn_t validate_fn;
+} test_case_t;
+
+static test_global_t *test_global;
+
+static inline void test_spinlock(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ odp_spinlock_t *lock = &g->item.spinlock.lock;
+
+ for (uint64_t i = 0; i < g->test_options.num_round; i++) {
+ odp_spinlock_lock(lock);
+ for (uint32_t j = 0; j < num_counter; j++)
+ (*counter[j])++;
+ odp_spinlock_unlock(lock);
+ }
+}
+
+static inline void test_spinlock_recursive(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ odp_spinlock_recursive_t *lock = &g->item.spinlock_recursive.lock;
+
+ for (uint64_t i = 0; i < g->test_options.num_round; i++) {
+ odp_spinlock_recursive_lock(lock);
+ odp_spinlock_recursive_lock(lock);
+ for (uint32_t j = 0; j < num_counter; j++)
+ (*counter[j])++;
+ odp_spinlock_recursive_unlock(lock);
+ odp_spinlock_recursive_unlock(lock);
+ }
+}
+
+static inline void test_rwlock(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ odp_rwlock_t *lock = &g->item.rwlock.lock;
+
+ for (uint64_t i = 0; i < g->test_options.num_round; i++) {
+ odp_rwlock_write_lock(lock);
+ for (uint32_t j = 0; j < num_counter; j++)
+ (*counter[j])++;
+ odp_rwlock_write_unlock(lock);
+ odp_rwlock_read_lock(lock);
+ for (uint32_t j = 1; j < num_counter; j++)
+ if (*counter[0] != *counter[j]) {
+ odp_rwlock_read_unlock(lock);
+ ODPH_ERR("Error: Counter mismatch\n");
+ return;
+ }
+ odp_rwlock_read_unlock(lock);
+ }
+}
+
+static inline void test_rwlock_recursive(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ odp_rwlock_recursive_t *lock = &g->item.rwlock_recursive.lock;
+
+ for (uint64_t i = 0; i < g->test_options.num_round; i++) {
+ odp_rwlock_recursive_write_lock(lock);
+ odp_rwlock_recursive_write_lock(lock);
+ for (uint32_t j = 0; j < num_counter; j++)
+ (*counter[j])++;
+ odp_rwlock_recursive_write_unlock(lock);
+ odp_rwlock_recursive_write_unlock(lock);
+ odp_rwlock_recursive_read_lock(lock);
+ odp_rwlock_recursive_read_lock(lock);
+ for (uint32_t j = 1; j < num_counter; j++)
+ if (*counter[0] != *counter[j]) {
+ odp_rwlock_recursive_read_unlock(lock);
+ odp_rwlock_recursive_read_unlock(lock);
+ ODPH_ERR("Error: Counter mismatch\n");
+ return;
+ }
+ odp_rwlock_recursive_read_unlock(lock);
+ odp_rwlock_recursive_read_unlock(lock);
+ }
+}
+
+static inline void test_ticketlock(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ odp_ticketlock_t *lock = &g->item.ticketlock.lock;
+
+ for (uint64_t i = 0; i < g->test_options.num_round; i++) {
+ odp_ticketlock_lock(lock);
+ for (uint32_t j = 0; j < num_counter; j++)
+ (*counter[j])++;
+ odp_ticketlock_unlock(lock);
+ }
+}
+
+static inline int validate_generic(test_global_t *g, uint64_t **counter,
+ uint32_t num_counter)
+{
+ int status = 0;
+ uint64_t total = (uint64_t)g->test_options.num_cpu * g->test_options.num_round;
+
+ for (uint32_t i = 0; i < num_counter; i++) {
+ if (*counter[i] != total) {
+ status = 1;
+ ODPH_ERR("Error: Counter %d value %" PRIu64 " expected %" PRIu64 "\n",
+ i, *counter[i], total);
+ }
+ }
+
+ return status;
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Lock performance test\n"
+ "\n"
+ "Usage: odp_lock_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs (or max %d) (default)\n"
+ " -t, --type Lock type to test. 0: all (default %u)\n"
+ " 1: odp_spinlock_t\n"
+ " 2: odp_spinlock_recursive_t\n"
+ " 3: odp_rwlock_t\n"
+ " 4: odp_rwlock_recursive_t\n"
+ " 5: odp_ticketlock_t\n"
+ " -r, --num_round Number of rounds (default %" PRIu64 ")\n"
+ " -e, --repeat Repeat the tests (default %u)\n"
+ " 0: no repeat, run the tests once\n"
+ " 1: repeat until failure\n"
+ " 2: repeat forever\n"
+ " -o, --num_counter Number of counters (default %u)\n"
+ " -p, --place Counter placement (default %d)\n"
+ " 0: pack to same cache line with lock\n"
+ " 1: pack to separate cache line\n"
+ " 2: place each counter to separate cache line\n"
+ " -h, --help This help\n"
+ "\n",
+ DEFAULT_MAX_WORKERS, test_options_def.type,
+ test_options_def.num_round, test_options_def.repeat,
+ test_options_def.num_counter, test_options_def.place);
+}
+
+static void print_info(test_options_t *test_options)
+{
+ printf("\nLock performance test configuration:\n");
+ printf(" num cpu %u\n", test_options->num_cpu);
+ printf(" type %u\n", test_options->type);
+ printf(" num rounds %" PRIu64 "\n", test_options->num_round);
+ printf(" repeat %u\n", test_options->repeat);
+ printf(" num counters %u\n", test_options->num_counter);
+ printf(" place %u\n", test_options->place);
+ printf("\n\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ { "num_cpu", required_argument, NULL, 'c' },
+ { "type", required_argument, NULL, 't' },
+ { "num_round", required_argument, NULL, 'r' },
+ { "repeat", required_argument, NULL, 'e' },
+ { "num_counter", required_argument, NULL, 'o' },
+ { "place", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+c:t:r:e:o:p:h";
+
+ *test_options = test_options_def;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 't':
+ test_options->type = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atoll(optarg);
+ break;
+ case 'e':
+ test_options->repeat = atoi(optarg);
+ break;
+ case 'o':
+ test_options->num_counter = atoi(optarg);
+ break;
+ case 'p':
+ test_options->place = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_round < 1) {
+ ODPH_ERR("Invalid number of test rounds: %" PRIu64 "\n",
+ test_options->num_round);
+ return -1;
+ }
+
+ if (test_options->num_counter < 1 ||
+ test_options->num_counter > MAX_COUNTERS) {
+ ODPH_ERR("Invalid number of counters: %" PRIu32 "\n",
+ test_options->num_counter);
+ return -1;
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret, max_num;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ max_num = num_cpu;
+ if (num_cpu == 0) {
+ max_num = ODP_THREAD_COUNT_MAX - 1;
+ if (max_num > DEFAULT_MAX_WORKERS)
+ max_num = DEFAULT_MAX_WORKERS;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, max_num);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Too many workers. Max supported %i.\n", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ if (ret > max_num) {
+ ODPH_ERR("Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
+ return -1;
+ }
+
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ return 0;
+}
+
+static int init_test(test_global_t *g, const char *name)
+{
+ printf("TEST: %s\n", name);
+
+ memset(&g->item, 0, sizeof(g->item));
+ odp_spinlock_init(&g->item.spinlock.lock);
+ odp_spinlock_recursive_init(&g->item.spinlock_recursive.lock);
+ odp_rwlock_init(&g->item.rwlock.lock);
+ odp_rwlock_recursive_init(&g->item.rwlock_recursive.lock);
+ odp_ticketlock_init(&g->item.ticketlock.lock);
+
+ return 0;
+}
+
+static void fill_counter_ptrs(test_global_t *g, uint64_t **counter_out)
+{
+ test_options_t *test_options = &g->test_options;
+
+ memset(counter_out, 0, sizeof(uint64_t *) * MAX_COUNTERS);
+
+ switch (test_options->place) {
+ case PLACE_PACK:
+ for (uint32_t i = 0; i < test_options->num_counter; i++) {
+ switch (g->cur_type) {
+ case 0:
+ counter_out[i] = &g->item.spinlock.counter[i];
+ break;
+ case 1:
+ counter_out[i] = &g->item.spinlock_recursive.counter[i];
+ break;
+ case 2:
+ counter_out[i] = &g->item.rwlock.counter[i];
+ break;
+ case 3:
+ counter_out[i] = &g->item.rwlock_recursive.counter[i];
+ break;
+ case 4:
+ counter_out[i] = &g->item.ticketlock.counter[i];
+ break;
+ }
+ }
+ break;
+ case PLACE_SEPARATE:
+ for (uint32_t i = 0; i < test_options->num_counter; i++)
+ counter_out[i] = &g->item.separate.counter[i];
+ break;
+ case PLACE_ALL_SEPARATE:
+ for (uint32_t i = 0; i < test_options->num_counter; i++)
+ counter_out[i] = &g->item.all_separate[i].counter;
+ break;
+ }
+}
+
+static int run_test(void *arg)
+{
+ uint64_t nsec;
+ odp_time_t t1, t2;
+ test_thread_ctx_t *thread_ctx = arg;
+ test_global_t *global = thread_ctx->global;
+ test_options_t *test_options = &global->test_options;
+ test_fn_t test_func = thread_ctx->func;
+ uint64_t *counter[MAX_COUNTERS];
+
+ fill_counter_ptrs(global, counter);
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ test_func(global, counter, test_options->num_counter);
+ t2 = odp_time_local();
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats */
+ thread_ctx->nsec = nsec;
+
+ return 0;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance,
+ test_fn_t func)
+{
+ odph_thread_common_param_t param;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ odph_thread_common_param_init(&param);
+ param.instance = instance;
+ param.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
+
+ thread_ctx->global = global;
+ thread_ctx->idx = i;
+ thread_ctx->func = func;
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ thr_param[i].start = run_test;
+ thr_param[i].arg = thread_ctx;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &param, thr_param,
+ num_cpu);
+ if (ret != num_cpu) {
+ ODPH_ERR("Failed to create all threads %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int validate_results(test_global_t *global, validate_fn_t validate)
+{
+ test_options_t *test_options = &global->test_options;
+ uint64_t *counter[MAX_COUNTERS];
+
+ fill_counter_ptrs(global, counter);
+
+ if (validate(global, counter, test_options->num_counter))
+ return -1;
+
+ return 0;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double nsec_ave;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ uint64_t num_round = test_options->num_round;
+ uint64_t nsec_sum = 0;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ nsec_sum += global->thread_ctx[i].nsec;
+
+ if (nsec_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ nsec_ave = nsec_sum / num_cpu;
+ num = 0;
+
+ printf("------------------------------------------------\n");
+ printf("Per thread results (Millions of rounds per sec):\n");
+ printf("------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->thread_ctx[i].nsec) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%8.3f ", num_round / (global->thread_ctx[i].nsec / 1000.0));
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("Average results over %i threads:\n", num_cpu);
+ printf("------------------------------------------\n");
+ printf(" duration: %8.3f sec\n",
+ nsec_ave / ODP_TIME_SEC_IN_NS);
+ printf(" rounds per cpu: %8.3fM rounds/sec\n",
+ num_round / (nsec_ave / 1000.0));
+ printf(" total rounds: %8.3fM rounds/sec\n",
+ ((uint64_t)num_cpu * num_round) / (nsec_ave / 1000.0));
+ printf("\n\n");
+}
+
+/**
+ * Test functions
+ */
+static test_case_t test_suite[] = {
+ TEST_INFO("odp_spinlock", test_spinlock, validate_generic),
+ TEST_INFO("odp_spinlock_recursive", test_spinlock_recursive, validate_generic),
+ TEST_INFO("odp_rwlock", test_rwlock, validate_generic),
+ TEST_INFO("odp_rwlock_recursive", test_rwlock_recursive, validate_generic),
+ TEST_INFO("odp_ticketlock", test_ticketlock, validate_generic),
+};
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_options_t test_options;
+ int num_tests, i;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (parse_options(argc, argv, &test_options))
+ exit(EXIT_FAILURE);
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.stash = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for global data from shared mem */
+ shm = odp_shm_reserve("test_global", sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ test_global = odp_shm_addr(shm);
+ if (test_global == NULL) {
+ ODPH_ERR("Shared memory alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(test_global, 0, sizeof(test_global_t));
+ test_global->test_options = test_options;
+
+ odp_sys_info_print();
+
+ if (set_num_cpu(test_global))
+ exit(EXIT_FAILURE);
+
+ print_info(&test_global->test_options);
+
+ /* Loop all test cases */
+ num_tests = ODPH_ARRAY_SIZE(test_suite);
+
+ while (1) {
+ for (i = 0; i < num_tests; i++) {
+ if (test_options.type && test_options.type != (uint32_t)i + 1)
+ continue;
+
+ test_global->cur_type = i;
+
+ /* Initialize test variables */
+ if (init_test(test_global, test_suite[i].name)) {
+ ODPH_ERR("Failed to initialize test.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Start workers */
+ if (start_workers(test_global, instance, test_suite[i].test_fn))
+ exit(EXIT_FAILURE);
+
+ /* Wait workers to exit */
+ odph_thread_join(test_global->thread_tbl,
+ test_global->test_options.num_cpu);
+
+ print_stat(test_global);
+
+ /* Validate test results */
+ if (validate_results(test_global, test_suite[i].validate_fn)) {
+ ODPH_ERR("Test %s result validation failed.\n",
+ test_suite[i].name);
+ if (test_options.repeat != REPEAT_FOREVER)
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (test_options.repeat == REPEAT_NO)
+ break;
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shm free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_mem_perf.c b/test/performance/odp_mem_perf.c
new file mode 100644
index 000000000..5a7642a10
--- /dev/null
+++ b/test/performance/odp_mem_perf.c
@@ -0,0 +1,484 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_mem_perf.c
+ *
+ * Test application for measuring memory system bandwidth
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t num_round;
+ uint64_t data_len;
+ uint32_t shm_flags;
+ int private;
+ int mode;
+
+} test_options_t;
+
+typedef struct test_global_t test_global_t;
+
+typedef struct test_thread_ctx_t {
+ test_global_t *global;
+ void *shm_addr;
+ uint64_t nsec;
+
+} test_thread_ctx_t;
+
+struct test_global_t {
+ test_options_t test_options;
+
+ odp_barrier_t barrier;
+ uint32_t num_shm;
+ odp_shm_t shm[ODP_THREAD_COUNT_MAX];
+ void *shm_addr[ODP_THREAD_COUNT_MAX];
+ odp_cpumask_t cpumask;
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_thread_ctx_t thread_ctx[ODP_THREAD_COUNT_MAX];
+
+};
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Memory performance test\n"
+ "\n"
+ "Usage: odp_mem_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
+ " -r, --num_round Number of rounds\n"
+ " -l, --data_len Data length in bytes\n"
+ " -f, --flags SHM flags parameter. Default 0.\n"
+ " -p, --private 0: The same memory area is shared between threads (default)\n"
+ " 1: Memory areas are private to each thread. This increases\n"
+ " memory consumption to num_cpu * data_len.\n"
+ " -m, --mode 0: Memset data (default)\n"
+ " 1: Memcpy data. On each round, reads data from one half of the memory area\n"
+ " and writes it to the other half.\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_round", required_argument, NULL, 'r'},
+ {"data_len", required_argument, NULL, 'l'},
+ {"flags", required_argument, NULL, 'f'},
+ {"private", required_argument, NULL, 'p'},
+ {"mode", required_argument, NULL, 'm'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:r:l:f:p:m:h";
+
+ test_options->num_cpu = 1;
+ test_options->num_round = 1000;
+ test_options->data_len = 10 * 1024 * 1024;
+ test_options->shm_flags = 0;
+ test_options->private = 0;
+ test_options->mode = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atoi(optarg);
+ break;
+ case 'l':
+ test_options->data_len = strtoull(optarg, NULL, 0);
+ break;
+ case 'f':
+ test_options->shm_flags = strtoul(optarg, NULL, 0);
+ break;
+ case 'p':
+ test_options->private = atoi(optarg);
+ break;
+ case 'm':
+ test_options->mode = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret, max_num;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ max_num = num_cpu;
+ if (num_cpu == 0)
+ max_num = ODP_THREAD_COUNT_MAX - 1;
+
+ ret = odp_cpumask_default_worker(&global->cpumask, max_num);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Too many workers. Max supported %i.\n", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ if (ret > max_num) {
+ ODPH_ERR("Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
+ return -1;
+ }
+
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ return 0;
+}
+
+static int create_shm(test_global_t *global)
+{
+ odp_shm_capability_t shm_capa;
+ odp_shm_t shm;
+ void *addr;
+ uint32_t i, num_shm;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ uint32_t num_cpu = test_options->num_cpu;
+ uint64_t data_len = test_options->data_len;
+ uint32_t shm_flags = test_options->shm_flags;
+ int private = test_options->private;
+ char name[] = "mem_perf_00";
+
+ num_shm = 1;
+ if (private)
+ num_shm = num_cpu;
+
+ printf("\nMemory performance test\n");
+ printf(" num cpu %u\n", num_cpu);
+ printf(" num rounds %u\n", num_round);
+ printf(" data len %" PRIu64 "\n", data_len);
+ printf(" memory footprint %" PRIu64 "\n", num_shm * data_len);
+ printf(" shm flags 0x%x\n", shm_flags);
+ printf(" num shm %u\n", num_shm);
+ printf(" private %i\n", private);
+ printf(" mode %i\n", test_options->mode);
+
+ if (odp_shm_capability(&shm_capa)) {
+ ODPH_ERR("SHM capa failed.\n");
+ return -1;
+ }
+
+ if (shm_capa.max_size && data_len > shm_capa.max_size) {
+ ODPH_ERR("Data len too large. Maximum len is %" PRIu64 "\n", shm_capa.max_size);
+ return -1;
+ }
+
+ if (num_shm > shm_capa.max_blocks) {
+ ODPH_ERR("Too many SHM blocks. Maximum is %u\n", shm_capa.max_blocks);
+ return -1;
+ }
+
+ for (i = 0; i < num_shm; i++) {
+ name[9] = '0' + i / 10;
+ name[10] = '0' + i % 10;
+
+ shm = odp_shm_reserve(name, data_len, ODP_CACHE_LINE_SIZE, shm_flags);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM[%u] reserve failed.\n", i);
+ return -1;
+ }
+
+ global->shm[i] = shm;
+
+ addr = odp_shm_addr(shm);
+ if (addr == NULL) {
+ ODPH_ERR("SHM[%u] addr failed.\n", i);
+ return -1;
+ }
+
+ global->shm_addr[i] = addr;
+
+ printf(" shm addr[%u] %p\n", i, addr);
+ }
+
+ printf("\n");
+ global->num_shm = num_shm;
+
+ odp_shm_print_all();
+
+ return 0;
+}
+
+static int free_shm(test_global_t *global)
+{
+ uint32_t i;
+
+ for (i = 0; i < global->num_shm; i++) {
+ if (odp_shm_free(global->shm[i])) {
+ ODPH_ERR("SHM[%u] free failed.\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int run_test(void *arg)
+{
+ int thr;
+ uint32_t i;
+ uint64_t nsec;
+ odp_time_t t1, t2;
+ test_thread_ctx_t *thread_ctx = arg;
+ test_global_t *global = thread_ctx->global;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ uint64_t data_len = test_options->data_len;
+ uint64_t half_len = data_len / 2;
+ int mode = test_options->mode;
+ uint8_t *addr = thread_ctx->shm_addr;
+
+ thr = odp_thread_id();
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+
+ if (mode == 0) {
+ for (i = 0; i < num_round; i++)
+ memset(addr, thr + i, data_len);
+ } else {
+ for (i = 0; i < num_round; i++) {
+ if ((i & 0x1) == 0)
+ memcpy(&addr[half_len], addr, half_len);
+ else
+ memcpy(addr, &addr[half_len], half_len);
+ }
+ }
+
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats */
+ thread_ctx->nsec = nsec;
+
+ return 0;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t param;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ odph_thread_common_param_init(&param);
+ param.instance = instance;
+ param.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
+
+ thread_ctx->global = global;
+ thread_ctx->shm_addr = global->shm_addr[0];
+ if (global->test_options.private)
+ thread_ctx->shm_addr = global->shm_addr[i];
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ thr_param[i].start = run_test;
+ thr_param[i].arg = thread_ctx;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &param, thr_param, num_cpu);
+ if (ret != num_cpu) {
+ ODPH_ERR("Failed to create all threads %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double nsec_ave;
+ uint64_t data_touch;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ uint32_t num_round = test_options->num_round;
+ uint64_t data_len = test_options->data_len;
+ uint64_t nsec_sum = 0;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ nsec_sum += global->thread_ctx[i].nsec;
+
+ if (nsec_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ data_touch = num_round * data_len;
+ nsec_ave = nsec_sum / num_cpu;
+ num = 0;
+
+ printf("RESULTS - per thread (MB per sec):\n");
+ printf("----------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->thread_ctx[i].nsec) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%8.1f ", data_touch / (global->thread_ctx[i].nsec / 1000.0));
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("RESULTS - average over %i threads:\n", num_cpu);
+ printf("----------------------------------\n");
+ printf(" duration: %.6f sec\n", nsec_ave / 1000000000);
+ printf(" bandwidth per cpu: %.3f MB/s\n", data_touch / (nsec_ave / 1000.0));
+ printf(" total bandwidth: %.3f MB/s\n", (num_cpu * data_touch) / (nsec_ave / 1000.0));
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ return -1;
+ }
+
+ shm = odp_shm_reserve("mem_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(test_global_t));
+
+ if (parse_options(argc, argv, &global->test_options))
+ return -1;
+
+ odp_sys_info_print();
+
+ if (set_num_cpu(global))
+ return -1;
+
+ if (create_shm(global))
+ return -1;
+
+ /* Start workers */
+ if (start_workers(global, instance))
+ return -1;
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
+
+ print_stat(global);
+
+ if (free_shm(global))
+ return -1;
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("term local failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("term global failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_packet_gen.c b/test/performance/odp_packet_gen.c
new file mode 100644
index 000000000..c7b3eb747
--- /dev/null
+++ b/test/performance/odp_packet_gen.c
@@ -0,0 +1,2319 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2024 Nokia
+ */
+
+/**
+ * @example odp_packet_gen.c
+ *
+ * Performance optimized packet generator application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/* enable usleep */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#if ODP_THREAD_COUNT_MAX > 33
+/* One control thread, even number of workers */
+#define MAX_THREADS 33
+#else
+#define MAX_THREADS ODP_THREAD_COUNT_MAX
+#endif
+
+#define MAX_WORKERS (MAX_THREADS - 1)
+
+/* At least one control and one worker thread */
+ODP_STATIC_ASSERT(MAX_WORKERS >= 1, "Too few threads");
+
+/* Maximum number of packet IO interfaces */
+#define MAX_PKTIOS 16
+/* Maximum number of packets to be allocated for
+ * one transmit round: bursts * burst_size * bins */
+#define MAX_ALLOC_PACKETS (64 * 1024)
+/* Maximum number of packet length bins */
+#define MAX_BINS 1024
+#define MAX_PKTIO_NAME 255
+#define RX_THREAD 1
+#define TX_THREAD 2
+#define MAX_VLANS 4
+/* Number of random 16-bit words used to generate random length packets */
+#define RAND_16BIT_WORDS 128
+/* Max retries to generate random data */
+#define MAX_RAND_RETRIES 1000
+
+/* Use don't free */
+#define TX_MODE_DF 0
+/* Use static references */
+#define TX_MODE_REF 1
+/* Use packet copy */
+#define TX_MODE_COPY 2
+
+/* Minimum number of packets to receive in CI test */
+#define MIN_RX_PACKETS_CI 800
+
+/* Identifier for payload-timestamped packets */
+#define TS_MAGIC 0xff88ee99ddaaccbb
+
+enum {
+ L4_PROTO_UDP = 0,
+ L4_PROTO_TCP
+};
+
+ODP_STATIC_ASSERT(MAX_PKTIOS <= UINT8_MAX, "Interface index must fit into uint8_t\n");
+
+typedef struct test_options_t {
+ uint64_t gap_nsec;
+ uint64_t quit;
+ uint64_t update_msec;
+ uint32_t num_rx;
+ uint32_t num_tx;
+ uint32_t num_cpu;
+ uint32_t num_pktio;
+ uint32_t num_pkt;
+ uint32_t pkt_len;
+ uint8_t use_rand_pkt_len;
+ uint8_t direct_rx;
+ uint32_t rand_pkt_len_min;
+ uint32_t rand_pkt_len_max;
+ uint32_t rand_pkt_len_bins;
+ uint32_t hdr_len;
+ uint32_t burst_size;
+ uint32_t bursts;
+ uint32_t num_vlan;
+ uint32_t ipv4_src;
+ uint32_t ipv4_dst;
+ uint16_t src_port;
+ uint16_t dst_port;
+ uint32_t wait_sec;
+ uint32_t wait_start_sec;
+ uint32_t mtu;
+ uint8_t l4_proto;
+ int tx_mode;
+ odp_bool_t promisc_mode;
+ odp_bool_t calc_latency;
+ odp_bool_t calc_cs;
+ odp_bool_t fill_pl;
+
+ struct vlan_hdr {
+ uint16_t tpid;
+ uint16_t tci;
+ } vlan[MAX_VLANS];
+
+ struct {
+ uint32_t src_port;
+ uint32_t dst_port;
+ } c_mode;
+
+ char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
+ char ipv4_src_s[24];
+ char ipv4_dst_s[24];
+
+} test_options_t;
+
+typedef struct thread_arg_t {
+ void *global;
+ int tx_thr;
+
+ /* pktout queue per pktio interface (per thread) */
+ odp_pktout_queue_t pktout[MAX_PKTIOS];
+
+ /* In direct_rx mode, pktin queue per pktio interface (per thread) */
+ odp_pktin_queue_t pktin[MAX_PKTIOS];
+
+ /* Pre-built packets for TX thread */
+ odp_packet_t packet[MAX_PKTIOS][MAX_ALLOC_PACKETS];
+
+} thread_arg_t;
+
+typedef struct ODP_ALIGNED_CACHE thread_stat_t {
+ uint64_t time_nsec;
+ uint64_t rx_timeouts;
+ uint64_t rx_packets;
+ uint64_t rx_bytes;
+ uint64_t rx_lat_nsec;
+ uint64_t rx_lat_min_nsec;
+ uint64_t rx_lat_max_nsec;
+ uint64_t rx_lat_packets;
+
+ uint64_t tx_timeouts;
+ uint64_t tx_packets;
+ uint64_t tx_bytes;
+ uint64_t tx_drops;
+
+ int thread_type;
+
+ struct {
+ uint64_t rx_packets;
+ uint64_t tx_packets;
+
+ } pktio[MAX_PKTIOS];
+
+} thread_stat_t;
+
+typedef struct test_global_t {
+ test_options_t test_options;
+ odp_atomic_u32_t exit_test;
+ odp_barrier_t barrier;
+ odp_cpumask_t cpumask;
+ odp_pool_t pool;
+ uint64_t drained;
+ odph_thread_t thread_tbl[MAX_THREADS];
+ thread_stat_t stat[MAX_THREADS];
+ thread_arg_t thread_arg[MAX_THREADS];
+
+ struct {
+ odph_ethaddr_t eth_src;
+ odph_ethaddr_t eth_dst;
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout[MAX_THREADS];
+ odp_pktin_queue_t pktin[MAX_THREADS];
+ int started;
+
+ } pktio[MAX_PKTIOS];
+
+ /* Interface lookup table. Table index is pktio_index of the API. */
+ uint8_t if_from_pktio_idx[ODP_PKTIO_MAX_INDEX + 1];
+
+ uint32_t num_tx_pkt;
+ uint32_t num_bins;
+ uint32_t len_bin[MAX_BINS];
+
+} test_global_t;
+
+typedef struct ODP_PACKED {
+ uint64_t magic;
+ uint64_t tx_ts;
+} ts_data_t;
+
+typedef struct {
+ uint64_t nsec;
+ uint64_t min;
+ uint64_t max;
+ uint64_t packets;
+} rx_lat_data_t;
+
+static test_global_t *test_global;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "ODP packet generator\n"
+ "\n"
+ "Usage: odp_packet_gen [options]\n"
+ "\n"
+ " Mandatory:\n"
+ " -i, --interface <name> Packet IO interfaces. Comma-separated list of\n"
+ " interface names (no spaces) e.g. eth0,eth1.\n"
+ " At least one interface is required.\n"
+ "\n");
+ printf(" Optional:\n"
+ " -e, --eth_dst <mac> Destination MAC address. Comma-separated list of\n"
+ " addresses (no spaces), one address per packet IO\n"
+ " interface e.g. AA:BB:CC:DD:EE:FF,11:22:33:44:55:66\n"
+ " Default per interface: 02:00:00:A0:B0:CX, where X = 0,1,...\n"
+ " -v, --vlan <tpid:tci> VLAN configuration. Comma-separated list of VLAN TPID:TCI\n"
+ " values in hexadecimal, starting from the outer most VLAN.\n"
+ " For example:\n"
+ " VLAN 200 (decimal): 8100:c8\n"
+ " Double tagged VLANs 1 and 2: 88a8:1,8100:2\n"
+ " -r, --num_rx Number of receive threads. Default: 1\n"
+ " -t, --num_tx Number of transmit threads. Default: 1\n"
+ " -n, --num_pkt Number of packets in the pool. Default: 1000\n"
+ " -l, --len Packet length. Default: 512\n"
+ " -L, --len_range <min,max,bins>\n"
+ " Random packet length. Specify the minimum and maximum\n"
+ " packet lengths and the number of bins. To reduce pool size\n"
+ " requirement the length range can be divided into even sized\n"
+ " bins (max %u). Min and max size packets are always used and included\n"
+ " into the number of bins (bins >= 2). Bin value of 0 means\n"
+ " that each packet length is used. Comma-separated (no spaces).\n"
+ " Overrides standard packet length option.\n"
+ " -D, --direct_rx Direct input mode (default: 0)\n"
+ " 0: Use scheduler for packet input\n"
+ " 1: Poll packet input in direct mode\n", MAX_BINS);
+ printf(" -m, --tx_mode Transmit mode (default 1):\n"
+ " 0: Re-send packets with don't free option\n"
+ " 1: Send static packet references. Some features may\n"
+ " not be available with references.\n"
+ " 2: Send copies of packets\n"
+ " -M, --mtu <len> Interface MTU in bytes.\n"
+ " -b, --burst_size Transmit burst size. Default: 8\n"
+ " -x, --bursts Number of bursts per one transmit round. Default: 1\n"
+ " -g, --gap Gap between transmit rounds in nsec. Default: 1000000\n"
+ " Transmit packet rate per interface:\n"
+ " num_tx * burst_size * bursts * (10^9 / gap)\n"
+ " -s, --ipv4_src IPv4 source address. Default: 192.168.0.1\n"
+ " -d, --ipv4_dst IPv4 destination address. Default: 192.168.0.2\n"
+ " -o, --src_port UDP/TCP source port. Default: 10000\n"
+ " -p, --dst_port UDP/TCP destination port. Default: 20000\n"
+ " -N, --proto L4 protocol. Default: 0\n"
+ " 0: UDP\n"
+ " 1: TCP\n"
+ " -P, --promisc_mode Enable promiscuous mode.\n"
+ " -a, --latency Calculate latency. Cannot be used with packet\n"
+ " references (see \"--tx_mode\").\n"
+ " -c, --c_mode <counts> Counter mode for incrementing UDP/TCP port numbers.\n"
+ " Specify the number of port numbers used starting from\n"
+ " src_port/dst_port. Comma-separated (no spaces) list of\n"
+ " count values: <src_port count>,<dst_port count>\n"
+ " Default value: 0,0\n"
+ " -C, --no_udp_checksum Do not calculate UDP checksum. Instead, set it to\n"
+ " zero in every packet.\n"
+ " -A, --no_payload_fill Do not fill payload. By default, payload is filled\n"
+ " with a pattern until the end of first packet\n"
+ " segment.\n"
+ " -q, --quit Quit after this many transmit rounds.\n"
+ " Default: 0 (don't quit)\n"
+ " -u, --update_stat <msec> Update and print statistics every <msec> milliseconds.\n"
+ " 0: Don't print statistics periodically (default)\n"
+ " -h, --help This help\n"
+ " -w, --wait <sec> Wait up to <sec> seconds for network links to be up.\n"
+ " Default: 0 (don't check link status)\n"
+ " -W, --wait_start <sec> Wait <sec> seconds before starting traffic. Default: 0\n"
+ "\n");
+}
+
+static int parse_vlan(const char *str, test_global_t *global)
+{
+ struct vlan_hdr *vlan;
+ const char *start = str;
+ char *end;
+ int num_vlan = 0;
+ intptr_t str_len = strlen(str);
+
+ while (num_vlan < MAX_VLANS) {
+ vlan = &global->test_options.vlan[num_vlan];
+
+ /* TPID in hexadecimal */
+ end = NULL;
+ vlan->tpid = strtoul(start, &end, 16);
+ if (end < start)
+ break;
+
+ /* Skip ':' */
+ start = end + 1;
+ if (start - str >= str_len)
+ break;
+
+ /* TCI in hexadecimal */
+ end = NULL;
+ vlan->tci = strtoul(start, &end, 16);
+ if (end < start)
+ break;
+
+ num_vlan++;
+
+ /* Skip ',' or stop at the string end */
+ start = end + 1;
+ if (start - str >= str_len)
+ break;
+ }
+
+ return num_vlan;
+}
+
+static int init_bins(test_global_t *global)
+{
+ uint32_t i, bin_size;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_bins = test_options->rand_pkt_len_bins;
+ uint32_t len_min = test_options->rand_pkt_len_min;
+ uint32_t len_max = test_options->rand_pkt_len_max;
+ uint32_t num_bytes = len_max - len_min + 1;
+
+ if (len_max <= len_min) {
+ ODPH_ERR("Error: Bad max packet length\n");
+ return -1;
+ }
+
+ if (num_bins == 0)
+ num_bins = num_bytes;
+
+ if (num_bins == 1 || num_bins > MAX_BINS || num_bins > num_bytes) {
+ ODPH_ERR("Error: Bad number of packet length bins: %u\n", num_bins);
+ return -1;
+ }
+
+ bin_size = (len_max - len_min + 1) / (num_bins - 1);
+
+ /* Min length is the first bin */
+ for (i = 0; i < num_bins - 1; i++)
+ global->len_bin[i] = len_min + (i * bin_size);
+
+ /* Max length is the last bin */
+ global->len_bin[i] = len_max;
+ global->num_bins = num_bins;
+
+ return 0;
+}
+
+static int parse_options(int argc, char *argv[], test_global_t *global)
+{
+ int opt, i, len, str_len, long_index, port;
+ unsigned long int count;
+ uint32_t min_packets, num_tx_pkt, num_tx_alloc, pkt_len, val, bins;
+ char *name, *str, *end;
+ test_options_t *test_options = &global->test_options;
+ int ret = 0;
+ uint8_t default_eth_dst[6] = {0x02, 0x00, 0x00, 0xa0, 0xb0, 0xc0};
+
+ static const struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'},
+ {"eth_dst", required_argument, NULL, 'e'},
+ {"num_rx", required_argument, NULL, 'r'},
+ {"num_tx", required_argument, NULL, 't'},
+ {"num_pkt", required_argument, NULL, 'n'},
+ {"proto", required_argument, NULL, 'N'},
+ {"len", required_argument, NULL, 'l'},
+ {"len_range", required_argument, NULL, 'L'},
+ {"direct_rx", required_argument, NULL, 'D'},
+ {"tx_mode", required_argument, NULL, 'm'},
+ {"burst_size", required_argument, NULL, 'b'},
+ {"bursts", required_argument, NULL, 'x'},
+ {"gap", required_argument, NULL, 'g'},
+ {"vlan", required_argument, NULL, 'v'},
+ {"ipv4_src", required_argument, NULL, 's'},
+ {"ipv4_dst", required_argument, NULL, 'd'},
+ {"src_port", required_argument, NULL, 'o'},
+ {"dst_port", required_argument, NULL, 'p'},
+ {"promisc_mode", no_argument, NULL, 'P'},
+ {"latency", no_argument, NULL, 'a'},
+ {"c_mode", required_argument, NULL, 'c'},
+ {"no_udp_checksum", no_argument, NULL, 'C'},
+ {"no_payload_fill", no_argument, NULL, 'A'},
+ {"mtu", required_argument, NULL, 'M'},
+ {"quit", required_argument, NULL, 'q'},
+ {"wait", required_argument, NULL, 'w'},
+ {"wait_start", required_argument, NULL, 'W'},
+ {"update_stat", required_argument, NULL, 'u'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+i:e:r:t:n:N:l:L:D:m:M:b:x:g:v:s:d:o:p:c:CAq:u:w:W:Pah";
+
+ test_options->num_pktio = 0;
+ test_options->num_rx = 1;
+ test_options->num_tx = 1;
+ test_options->num_pkt = 1000;
+ test_options->pkt_len = 512;
+ test_options->use_rand_pkt_len = 0;
+ test_options->direct_rx = 0;
+ test_options->tx_mode = TX_MODE_REF;
+ test_options->burst_size = 8;
+ test_options->bursts = 1;
+ test_options->gap_nsec = 1000000;
+ test_options->num_vlan = 0;
+ test_options->promisc_mode = 0;
+ test_options->calc_latency = 0;
+ test_options->calc_cs = 1;
+ test_options->fill_pl = 1;
+ strncpy(test_options->ipv4_src_s, "192.168.0.1",
+ sizeof(test_options->ipv4_src_s) - 1);
+ strncpy(test_options->ipv4_dst_s, "192.168.0.2",
+ sizeof(test_options->ipv4_dst_s) - 1);
+ if (odph_ipv4_addr_parse(&test_options->ipv4_src, test_options->ipv4_src_s)) {
+ ODPH_ERR("Address parse failed\n");
+ return -1;
+ }
+ if (odph_ipv4_addr_parse(&test_options->ipv4_dst, test_options->ipv4_dst_s)) {
+ ODPH_ERR("Address parse failed\n");
+ return -1;
+ }
+ test_options->src_port = 10000;
+ test_options->dst_port = 20000;
+ test_options->c_mode.src_port = 0;
+ test_options->c_mode.dst_port = 0;
+ test_options->quit = 0;
+ test_options->update_msec = 0;
+ test_options->wait_sec = 0;
+ test_options->wait_start_sec = 0;
+ test_options->mtu = 0;
+ test_options->l4_proto = L4_PROTO_UDP;
+
+ for (i = 0; i < MAX_PKTIOS; i++) {
+ memcpy(global->pktio[i].eth_dst.addr, default_eth_dst, 6);
+ global->pktio[i].eth_dst.addr[5] += i;
+ }
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'i':
+ i = 0;
+ str = optarg;
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ len = strcspn(str, ",");
+ str_len -= len + 1;
+
+ if (i == MAX_PKTIOS) {
+ ODPH_ERR("Error: Too many interfaces\n");
+ ret = -1;
+ break;
+ }
+
+ if (len > MAX_PKTIO_NAME) {
+ ODPH_ERR("Error: Too long interface name %s\n", str);
+ ret = -1;
+ break;
+ }
+
+ name = test_options->pktio_name[i];
+ memcpy(name, str, len);
+ str += len + 1;
+ i++;
+ }
+
+ test_options->num_pktio = i;
+
+ break;
+ case 'e':
+ i = 0;
+ str = optarg;
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ odph_ethaddr_t *dst = &global->pktio[i].eth_dst;
+
+ len = strcspn(str, ",");
+ str_len -= len + 1;
+
+ if (i == MAX_PKTIOS) {
+ ODPH_ERR("Error: Too many MAC addresses\n");
+ ret = -1;
+ break;
+ }
+
+ if (odph_eth_addr_parse(dst, str)) {
+ ODPH_ERR("Error: Bad MAC address: %s\n", str);
+ ret = -1;
+ break;
+ }
+
+ str += len + 1;
+ i++;
+ }
+ break;
+ case 'o':
+ port = atoi(optarg);
+ if (port < 0 || port > UINT16_MAX) {
+ ODPH_ERR("Error: Bad source port: %d\n", port);
+ ret = -1;
+ break;
+ }
+ test_options->src_port = port;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port < 0 || port > UINT16_MAX) {
+ ODPH_ERR("Error: Bad destination port: %d\n", port);
+ ret = -1;
+ break;
+ }
+ test_options->dst_port = port;
+ break;
+ case 'P':
+ test_options->promisc_mode = 1;
+ break;
+ case 'a':
+ test_options->calc_latency = 1;
+ break;
+ case 'r':
+ test_options->num_rx = atoi(optarg);
+ break;
+ case 't':
+ test_options->num_tx = atoi(optarg);
+ break;
+ case 'n':
+ test_options->num_pkt = atoi(optarg);
+ break;
+ case 'N':
+ test_options->l4_proto = atoi(optarg);
+ break;
+ case 'l':
+ test_options->pkt_len = atoi(optarg);
+ break;
+ case 'L':
+ pkt_len = strtoul(optarg, &end, 0);
+ test_options->rand_pkt_len_min = pkt_len;
+ end++;
+ pkt_len = strtoul(end, &str, 0);
+ test_options->rand_pkt_len_max = pkt_len;
+ str++;
+ val = strtoul(str, NULL, 0);
+ test_options->rand_pkt_len_bins = val;
+ test_options->use_rand_pkt_len = 1;
+ break;
+ case 'D':
+ test_options->direct_rx = atoi(optarg);
+ break;
+ case 'm':
+ test_options->tx_mode = atoi(optarg);
+ break;
+ case 'M':
+ test_options->mtu = atoi(optarg);
+ break;
+ case 'b':
+ test_options->burst_size = atoi(optarg);
+ break;
+ case 'x':
+ test_options->bursts = atoi(optarg);
+ break;
+ case 'g':
+ test_options->gap_nsec = atoll(optarg);
+ break;
+ case 'v':
+ test_options->num_vlan = parse_vlan(optarg, global);
+ if (test_options->num_vlan == 0) {
+ ODPH_ERR("Error: Did not find any VLANs\n");
+ ret = -1;
+ }
+ break;
+ case 's':
+ if (odph_ipv4_addr_parse(&test_options->ipv4_src,
+ optarg)) {
+ ODPH_ERR("Error: Bad IPv4 source address: %s\n", optarg);
+ ret = -1;
+ }
+ strncpy(test_options->ipv4_src_s, optarg,
+ sizeof(test_options->ipv4_src_s) - 1);
+ break;
+ case 'd':
+ if (odph_ipv4_addr_parse(&test_options->ipv4_dst,
+ optarg)) {
+ ODPH_ERR("Error: Bad IPv4 destination address: %s\n", optarg);
+ ret = -1;
+ }
+ strncpy(test_options->ipv4_dst_s, optarg,
+ sizeof(test_options->ipv4_dst_s) - 1);
+ break;
+ case 'c':
+ count = strtoul(optarg, &end, 0);
+ test_options->c_mode.src_port = count;
+
+ end++;
+ count = strtoul(end, NULL, 0);
+ test_options->c_mode.dst_port = count;
+ break;
+ case 'C':
+ test_options->calc_cs = 0;
+ break;
+ case 'A':
+ test_options->fill_pl = 0;
+ break;
+ case 'q':
+ test_options->quit = atoll(optarg);
+ break;
+ case 'u':
+ test_options->update_msec = atoll(optarg);
+ break;
+ case 'w':
+ test_options->wait_sec = atoi(optarg);
+ break;
+ case 'W':
+ test_options->wait_start_sec = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret)
+ return -1;
+
+ if (test_options->num_pktio == 0) {
+ ODPH_ERR("Error: At least one packet IO interface is needed.\n");
+ ODPH_ERR(" Use -i <name> to specify interfaces.\n");
+ return -1;
+ }
+
+ if (test_options->num_rx < 1 && test_options->num_tx < 1) {
+ ODPH_ERR("Error: At least one rx or tx thread needed.\n");
+ return -1;
+ }
+
+ test_options->num_cpu = test_options->num_rx + test_options->num_tx;
+
+ if (test_options->num_cpu > MAX_WORKERS) {
+ ODPH_ERR("Error: Too many worker threads\n");
+ return -1;
+ }
+
+ num_tx_pkt = test_options->burst_size * test_options->bursts;
+ global->num_tx_pkt = num_tx_pkt;
+
+ if (num_tx_pkt == 0) {
+ ODPH_ERR("Error: Bad number of tx packets: %u\n", num_tx_pkt);
+ return -1;
+ }
+
+ if (test_options->use_rand_pkt_len) {
+ if (init_bins(global))
+ return -1;
+ }
+
+ bins = global->num_bins ? global->num_bins : 1;
+ num_tx_alloc = num_tx_pkt * bins;
+ if (num_tx_alloc > MAX_ALLOC_PACKETS) {
+ ODPH_ERR("Error: Too many tx packets: %u\n", num_tx_alloc);
+ return -1;
+ }
+
+ /* Pool needs to have enough packets for all TX side pre-allocated packets and
+ * a burst per thread (for packet copies). RX side needs one burst per thread per pktio. */
+ min_packets = test_options->num_pktio * test_options->num_tx * num_tx_alloc;
+ min_packets += test_options->num_tx * test_options->burst_size;
+ min_packets += test_options->num_pktio * test_options->num_rx * test_options->burst_size;
+
+ if (test_options->num_pkt < min_packets) {
+ ODPH_ERR("Error: Pool needs to have at least %u packets\n", min_packets);
+ return -1;
+ }
+
+ if (test_options->calc_latency && test_options->tx_mode == TX_MODE_REF) {
+ ODPH_ERR("Error: Latency test is not supported with packet references (--tx_mode 1)\n");
+ return -1;
+ }
+ if (test_options->calc_latency && (test_options->num_rx < 1 || test_options->num_tx < 1)) {
+ ODPH_ERR("Error: Latency test requires both rx and tx threads\n");
+ return -1;
+ }
+
+ if (test_options->gap_nsec) {
+ double gap_hz = 1000000000.0 / test_options->gap_nsec;
+
+ if (gap_hz > (double)odp_time_local_res()) {
+ ODPH_ERR("\nWARNING: Burst gap exceeds time counter resolution "
+ "%" PRIu64 "\n\n", odp_time_local_res());
+ }
+ }
+
+ if (global->num_bins) {
+ if (num_tx_pkt > global->num_bins && num_tx_pkt % global->num_bins)
+ ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible into packet length bins.\n\n");
+
+ if (num_tx_pkt < global->num_bins)
+ ODPH_ERR("\nWARNING: Not enough packets for every packet length bin.\n\n");
+ }
+
+ if (test_options->c_mode.dst_port && num_tx_pkt % test_options->c_mode.dst_port)
+ ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible by destination port count.\n\n");
+
+ if (test_options->c_mode.src_port && num_tx_pkt % test_options->c_mode.src_port)
+ ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible by source port count.\n\n");
+
+ if (test_options->l4_proto != L4_PROTO_TCP && test_options->l4_proto != L4_PROTO_UDP) {
+ ODPH_ERR("Error: Invalid L4 protocol: %" PRIu8 "\n", test_options->l4_proto);
+ return -1;
+ }
+ if (test_options->l4_proto == L4_PROTO_TCP && test_options->tx_mode != TX_MODE_COPY) {
+ ODPH_ERR("Error: TCP protocol supported only with copy transmit mode\n");
+ return -1;
+ }
+
+ test_options->hdr_len = ODPH_ETHHDR_LEN + (test_options->num_vlan * ODPH_VLANHDR_LEN) +
+ ODPH_IPV4HDR_LEN;
+ test_options->hdr_len += test_options->l4_proto == L4_PROTO_UDP ?
+ ODPH_UDPHDR_LEN : ODPH_TCPHDR_LEN;
+
+ pkt_len = test_options->use_rand_pkt_len ?
+ test_options->rand_pkt_len_min : test_options->pkt_len;
+ if (test_options->hdr_len >= pkt_len) {
+ ODPH_ERR("Error: Headers do not fit into packet length %" PRIu32 "\n", pkt_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
+
+ if (ret != num_cpu) {
+ int cpu;
+
+ /* Normally we want to use only worker threads */
+ if (ret > 1) {
+ ODPH_ERR("Error: Too many workers. Maximum supported %i.\n", ret);
+ return -1;
+ }
+
+ /* When number of workers is very limited (e.g. ODP project CI),
+ * we try to use any CPUs available. */
+ ret = odp_cpumask_all_available(&global->cpumask);
+ if (ret < num_cpu) {
+ ODPH_ERR("Error: Not enough CPUs. Maximum supported %i.\n", ret);
+ return -1;
+ }
+
+ /* Remove extra CPUs from the mask */
+ cpu = odp_cpumask_first(&global->cpumask);
+ while (ret > num_cpu) {
+ odp_cpumask_clr(&global->cpumask, cpu);
+ cpu = odp_cpumask_first(&global->cpumask);
+ ret--;
+ }
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu + 1);
+
+ return 0;
+}
+
+static int open_pktios(test_global_t *global)
+{
+ odp_pool_capability_t pool_capa;
+ odp_pktio_capability_t pktio_capa;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_t pktio;
+ odp_pktio_config_t pktio_config;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ char *name;
+ uint32_t i, seg_len;
+ int j, pktio_idx;
+ test_options_t *test_options = &global->test_options;
+ int num_rx = test_options->num_rx;
+ int num_tx = test_options->num_tx;
+ uint32_t num_pktio = test_options->num_pktio;
+ uint32_t num_pkt = test_options->num_pkt;
+ uint32_t pkt_len = test_options->use_rand_pkt_len ?
+ test_options->rand_pkt_len_max : test_options->pkt_len;
+
+ printf("\nODP packet generator\n");
+ printf(" quit test after %" PRIu64 " rounds\n",
+ test_options->quit);
+ printf(" num rx threads %i\n", num_rx);
+ printf(" num tx threads %i\n", num_tx);
+ printf(" num packets %u\n", num_pkt);
+ if (test_options->use_rand_pkt_len)
+ printf(" packet length %u-%u bytes, %u bins\n",
+ test_options->rand_pkt_len_min,
+ test_options->rand_pkt_len_max,
+ test_options->rand_pkt_len_bins);
+ else
+ printf(" packet length %u bytes\n", pkt_len);
+ printf(" MTU: ");
+ if (test_options->mtu)
+ printf("%u bytes\n", test_options->mtu);
+ else
+ printf("interface default\n");
+ printf(" packet input mode: %s\n", test_options->direct_rx ? "direct" : "scheduler");
+ printf(" promisc mode: %s\n", test_options->promisc_mode ? "enabled" : "disabled");
+ printf(" transmit mode: %i\n", test_options->tx_mode);
+ printf(" measure latency: %s\n", test_options->calc_latency ? "enabled" : "disabled");
+ printf(" UDP checksum: %s\n", test_options->calc_cs ? "enabled" : "disabled");
+ printf(" payload filling: %s\n", test_options->fill_pl ? "enabled" : "disabled");
+ printf(" tx burst size %u\n", test_options->burst_size);
+ printf(" tx bursts %u\n", test_options->bursts);
+ printf(" tx burst gap %" PRIu64 " nsec\n",
+ test_options->gap_nsec);
+ printf(" clock resolution %" PRIu64 " Hz\n", odp_time_local_res());
+ for (i = 0; i < test_options->num_vlan; i++) {
+ printf(" VLAN[%i] %x:%x\n", i,
+ test_options->vlan[i].tpid, test_options->vlan[i].tci);
+ }
+ printf(" IPv4 source %s\n", test_options->ipv4_src_s);
+ printf(" IPv4 destination %s\n", test_options->ipv4_dst_s);
+ printf(" L4 protocol: %s\n",
+ test_options->l4_proto == L4_PROTO_UDP ? "UDP" : "TCP");
+ printf(" source port %u\n", test_options->src_port);
+ printf(" destination port %u\n", test_options->dst_port);
+ printf(" src port count %u\n", test_options->c_mode.src_port);
+ printf(" dst port count %u\n", test_options->c_mode.dst_port);
+ printf(" num pktio %u\n", num_pktio);
+
+ printf(" interfaces names: ");
+ for (i = 0; i < num_pktio; i++) {
+ if (i > 0)
+ printf(" ");
+ printf("%s\n", test_options->pktio_name[i]);
+ }
+
+ printf(" destination MACs: ");
+ for (i = 0; i < num_pktio; i++) {
+ uint8_t *eth_dst = global->pktio[i].eth_dst.addr;
+
+ if (i > 0)
+ printf(" ");
+ printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ eth_dst[0], eth_dst[1], eth_dst[2],
+ eth_dst[3], eth_dst[4], eth_dst[5]);
+ }
+ printf("\n");
+
+ global->pool = ODP_POOL_INVALID;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Error: Pool capability failed.\n");
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_num &&
+ num_pkt > pool_capa.pkt.max_num) {
+ ODPH_ERR("Error: Too many packets. Max %u supported.\n", pool_capa.pkt.max_num);
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len) {
+ ODPH_ERR("Error: Too large packets. Max %u supported length.\n",
+ pool_capa.pkt.max_len);
+ return -1;
+ }
+
+ seg_len = test_options->hdr_len;
+ if (pool_capa.pkt.max_seg_len &&
+ seg_len > pool_capa.pkt.max_seg_len) {
+ ODPH_ERR("Error: Max segment length is too small %u\n", pool_capa.pkt.max_seg_len);
+ return -1;
+ }
+
+ /* Create pool */
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = num_pkt;
+ pool_param.pkt.len = pkt_len;
+ pool_param.pkt.seg_len = seg_len;
+
+ pool = odp_pool_create("packet gen pool", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: Pool create failed.\n");
+ return -1;
+ }
+
+ global->pool = pool;
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = num_rx ? (test_options->direct_rx ?
+ ODP_PKTIN_MODE_DIRECT : ODP_PKTIN_MODE_SCHED) :
+ ODP_PKTIN_MODE_DISABLED;
+
+ pktio_param.out_mode = num_tx ? ODP_PKTOUT_MODE_DIRECT : ODP_PKTOUT_MODE_DISABLED;
+
+ for (i = 0; i < num_pktio; i++)
+ global->pktio[i].pktio = ODP_PKTIO_INVALID;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_pktio; i++) {
+ name = test_options->pktio_name[i];
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Error (%s): Pktio open failed.\n", name);
+ return -1;
+ }
+
+ global->pktio[i].pktio = pktio;
+
+ odp_pktio_print(pktio);
+
+ pktio_idx = odp_pktio_index(pktio);
+ if (pktio_idx < 0) {
+ ODPH_ERR("Error (%s): Reading pktio index failed: %i\n", name, pktio_idx);
+ return -1;
+ }
+ global->if_from_pktio_idx[pktio_idx] = i;
+
+ if (odp_pktio_capability(pktio, &pktio_capa)) {
+ ODPH_ERR("Error (%s): Pktio capability failed.\n", name);
+ return -1;
+ }
+
+ if (num_rx > (int)pktio_capa.max_input_queues) {
+ ODPH_ERR("Error (%s): Too many RX threads. Interface supports max %u input queues.\n",
+ name, pktio_capa.max_input_queues);
+ return -1;
+ }
+
+ if (num_tx > (int)pktio_capa.max_output_queues) {
+ ODPH_ERR("Error (%s): Too many TX threads. Interface supports max %u output queues.\n",
+ name, pktio_capa.max_output_queues);
+ return -1;
+ }
+
+ if (odp_pktio_mac_addr(pktio,
+ &global->pktio[i].eth_src.addr,
+ ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("Error (%s): MAC address read failed.\n", name);
+ return -1;
+ }
+
+ if (test_options->mtu) {
+ uint32_t maxlen_input = pktio_capa.maxlen.max_input ? test_options->mtu : 0;
+ uint32_t maxlen_output = pktio_capa.maxlen.max_output ?
+ test_options->mtu : 0;
+
+ if (!pktio_capa.set_op.op.maxlen) {
+ ODPH_ERR("Error (%s): modifying interface MTU not supported.\n",
+ name);
+ return -1;
+ }
+
+ if (maxlen_input &&
+ (maxlen_input < pktio_capa.maxlen.min_input ||
+ maxlen_input > pktio_capa.maxlen.max_input)) {
+ ODPH_ERR("Error (%s): unsupported MTU value %" PRIu32 " "
+ "(min %" PRIu32 ", max %" PRIu32 ")\n", name, maxlen_input,
+ pktio_capa.maxlen.min_input, pktio_capa.maxlen.max_input);
+ return -1;
+ }
+ if (maxlen_output &&
+ (maxlen_output < pktio_capa.maxlen.min_output ||
+ maxlen_output > pktio_capa.maxlen.max_output)) {
+ ODPH_ERR("Error (%s): unsupported MTU value %" PRIu32 " "
+ "(min %" PRIu32 ", max %" PRIu32 ")\n", name,
+ maxlen_output, pktio_capa.maxlen.min_output,
+ pktio_capa.maxlen.max_output);
+ return -1;
+ }
+
+ if (odp_pktio_maxlen_set(pktio, maxlen_input, maxlen_output)) {
+ ODPH_ERR("Error (%s): setting MTU failed\n", name);
+ return -1;
+ }
+ }
+
+ if (test_options->tx_mode == TX_MODE_DF && pktio_capa.free_ctrl.dont_free == 0) {
+ ODPH_ERR("Error (%s): Don't free mode not supported\n", name);
+ return -1;
+ }
+
+ odp_pktio_config_init(&pktio_config);
+ pktio_config.parser.layer = ODP_PROTO_LAYER_ALL;
+
+ odp_pktio_config(pktio, &pktio_config);
+
+ if (test_options->promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
+ if (!pktio_capa.set_op.op.promisc_mode) {
+ ODPH_ERR("Error (%s): promisc mode set not supported\n", name);
+ return -1;
+ }
+
+ if (odp_pktio_promisc_mode_set(pktio, true)) {
+ ODPH_ERR("Error (%s): promisc mode enable failed\n", name);
+ return -1;
+ }
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ if (test_options->direct_rx) {
+ pktin_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ } else {
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ pktin_param.num_queues = num_rx;
+
+ if (num_rx > 1) {
+ pktin_param.hash_enable = 1;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ }
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Error (%s): Pktin config failed.\n", name);
+ return -1;
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ pktout_param.num_queues = num_tx;
+
+ if (odp_pktout_queue_config(pktio, &pktout_param)) {
+ ODPH_ERR("Error (%s): Pktout config failed.\n", name);
+ return -1;
+ }
+
+ if (num_tx > 0) {
+ odp_pktout_queue_t pktout[MAX_THREADS];
+
+ if (odp_pktout_queue(pktio, pktout, num_tx) != num_tx) {
+ ODPH_ERR("Error (%s): Pktout queue request failed.\n", name);
+ return -1;
+ }
+
+ for (j = 0; j < num_tx; j++)
+ global->pktio[i].pktout[j] = pktout[j];
+ }
+
+ if (num_rx > 0 && test_options->direct_rx) {
+ odp_pktin_queue_t pktin[MAX_THREADS];
+
+ if (odp_pktin_queue(pktio, pktin, num_rx) != num_rx) {
+ ODPH_ERR("Error (%s): Pktin queue request failed.\n", name);
+ return -1;
+ }
+
+ for (j = 0; j < num_rx; j++)
+ global->pktio[i].pktin[j] = pktin[j];
+ }
+ }
+
+ return 0;
+}
+
+static int print_link_info(odp_pktio_t pktio)
+{
+ odp_pktio_link_info_t info;
+
+ if (odp_pktio_link_info(pktio, &info)) {
+ ODPH_ERR("Error: Pktio link info failed.\n");
+ return -1;
+ }
+
+ printf(" autoneg %s\n",
+ (info.autoneg == ODP_PKTIO_LINK_AUTONEG_ON ? "on" :
+ (info.autoneg == ODP_PKTIO_LINK_AUTONEG_OFF ? "off" : "unknown")));
+ printf(" duplex %s\n",
+ (info.duplex == ODP_PKTIO_LINK_DUPLEX_HALF ? "half" :
+ (info.duplex == ODP_PKTIO_LINK_DUPLEX_FULL ? "full" : "unknown")));
+ printf(" media %s\n", info.media);
+ printf(" pause_rx %s\n",
+ (info.pause_rx == ODP_PKTIO_LINK_PAUSE_ON ? "on" :
+ (info.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF ? "off" : "unknown")));
+ printf(" pause_tx %s\n",
+ (info.pause_tx == ODP_PKTIO_LINK_PAUSE_ON ? "on" :
+ (info.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF ? "off" : "unknown")));
+ printf(" speed(Mbit/s) %" PRIu32 "\n\n", info.speed);
+
+ return 0;
+}
+
+static int start_pktios(test_global_t *global)
+{
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_pktio = test_options->num_pktio;
+ uint32_t link_wait = 0;
+
+ for (i = 0; i < num_pktio; i++) {
+ if (odp_pktio_start(global->pktio[i].pktio)) {
+ ODPH_ERR("Error (%s): Pktio start failed.\n", test_options->pktio_name[i]);
+
+ return -1;
+ }
+
+ global->pktio[i].started = 1;
+ }
+
+ /* Wait until all links are up */
+ for (i = 0; test_options->wait_sec && i < num_pktio; i++) {
+ while (1) {
+ odp_pktio_t pktio = global->pktio[i].pktio;
+
+ if (odp_pktio_link_status(pktio) == ODP_PKTIO_LINK_STATUS_UP) {
+ printf("pktio:%s\n", test_options->pktio_name[i]);
+ if (print_link_info(pktio)) {
+ ODPH_ERR("Error (%s): Printing link info failed.\n",
+ test_options->pktio_name[i]);
+ return -1;
+ }
+ break;
+ }
+ link_wait++;
+ if (link_wait > test_options->wait_sec) {
+ ODPH_ERR("Error (%s): Pktio link down.\n",
+ test_options->pktio_name[i]);
+ return -1;
+ }
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
+ }
+ }
+
+ if (test_options->wait_start_sec)
+ odp_time_wait_ns(test_options->wait_start_sec * ODP_TIME_SEC_IN_NS);
+
+ return 0;
+}
+
+static int stop_pktios(test_global_t *global)
+{
+ uint32_t i;
+ odp_pktio_t pktio;
+ int ret = 0;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_pktio = test_options->num_pktio;
+
+ for (i = 0; i < num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID || global->pktio[i].started == 0)
+ continue;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Error (%s): Pktio stop failed.\n", test_options->pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int close_pktios(test_global_t *global)
+{
+ uint32_t i;
+ odp_pktio_t pktio;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_pktio = test_options->num_pktio;
+ int ret = 0;
+
+ for (i = 0; i < num_pktio; i++) {
+ pktio = global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_close(pktio)) {
+ ODPH_ERR("Error (%s): Pktio close failed.\n", test_options->pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ if (global->pool != ODP_POOL_INVALID &&
+ odp_pool_destroy(global->pool)) {
+ ODPH_ERR("Error: Pool destroy failed.\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static inline void get_timestamp(odp_packet_t pkt, uint32_t ts_off, rx_lat_data_t *lat_data,
+ uint64_t rx_ts)
+{
+ ts_data_t ts_data;
+ uint64_t nsec;
+
+ if (odp_unlikely(odp_packet_copy_to_mem(pkt, ts_off, sizeof(ts_data), &ts_data) < 0 ||
+ ts_data.magic != TS_MAGIC))
+ return;
+
+ nsec = rx_ts - ts_data.tx_ts;
+
+ if (nsec < lat_data->min)
+ lat_data->min = nsec;
+
+ if (nsec > lat_data->max)
+ lat_data->max = nsec;
+
+ lat_data->nsec += nsec;
+ lat_data->packets++;
+}
+
+static int rx_thread(void *arg)
+{
+ int i, thr, num;
+ uint32_t exit_test;
+ uint64_t bytes;
+ odp_time_t t1, t2, exit_time;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ int direct_rx = global->test_options.direct_rx;
+ int periodic_stat = global->test_options.update_msec ? 1 : 0;
+ uint64_t rx_timeouts = 0;
+ uint64_t rx_packets = 0;
+ uint64_t rx_bytes = 0;
+ uint64_t nsec = 0;
+ int ret = 0;
+ int clock_started = 0;
+ int exit_timer_started = 0;
+ int paused = 0;
+ const int max_num = 32;
+ int pktin = 0;
+ int num_pktio = global->test_options.num_pktio;
+ odp_pktin_queue_t pktin_queue[num_pktio];
+ odp_packet_t pkt[max_num];
+ uint32_t ts_off = global->test_options.calc_latency ? global->test_options.hdr_len : 0;
+ uint64_t rx_ts = 0;
+ rx_lat_data_t rx_lat_data = { .nsec = 0, .min = UINT64_MAX, .max = 0, .packets = 0 };
+
+ thr = odp_thread_id();
+ global->stat[thr].thread_type = RX_THREAD;
+
+ if (direct_rx) {
+ for (i = 0; i < num_pktio; i++)
+ pktin_queue[i] = thread_arg->pktin[i];
+ }
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ while (1) {
+ if (direct_rx) {
+ num = odp_pktin_recv(pktin_queue[pktin], pkt, max_num);
+
+ if (odp_unlikely(num < 0)) {
+ ODPH_ERR("pktin (%i) recv failed: %i\n", pktin, num);
+ ret = -1;
+ num = 0;
+ break;
+ }
+
+ pktin++;
+ if (pktin >= num_pktio)
+ pktin = 0;
+ } else {
+ odp_event_t ev[max_num];
+
+ num = odp_schedule_multi_no_wait(NULL, ev, max_num);
+
+ if (num)
+ odp_packet_from_event_multi(pkt, ev, num);
+ }
+
+ if (ts_off && num)
+ rx_ts = odp_time_global_ns();
+
+ exit_test = odp_atomic_load_u32(&global->exit_test);
+ if (exit_test) {
+ /* Wait 1 second for possible in flight packets sent by the tx threads */
+ if (exit_timer_started == 0) {
+ exit_time = odp_time_local();
+ t2 = exit_time;
+ exit_timer_started = 1;
+ } else if (odp_time_diff_ns(odp_time_local(), exit_time) >
+ ODP_TIME_SEC_IN_NS) {
+ if (direct_rx == 0 && paused == 0) {
+ odp_schedule_pause();
+ paused = 1;
+ } else if (num == 0) {
+ /* Exit main loop after (schedule paused and) no more
+ * packets received */
+ break;
+ }
+ }
+ /* Use last received packet as stop time and don't increase rx_timeouts
+ * counter since tx threads have already been stopped */
+ if (num)
+ t2 = odp_time_local();
+ else
+ continue;
+ }
+
+ if (num == 0) {
+ if (direct_rx == 0)
+ rx_timeouts++;
+
+ continue;
+ }
+
+ if (!clock_started) {
+ t1 = odp_time_local();
+ clock_started = 1;
+ }
+
+ bytes = 0;
+ for (i = 0; i < num; i++) {
+ bytes += odp_packet_len(pkt[i]);
+
+ if (ts_off)
+ get_timestamp(pkt[i], ts_off, &rx_lat_data, rx_ts);
+ }
+
+ rx_packets += num;
+ rx_bytes += bytes;
+
+ if (odp_unlikely(periodic_stat)) {
+ /* All packets from the same queue are from the same pktio interface */
+ int index = odp_packet_input_index(pkt[0]);
+
+ if (index >= 0) {
+ int if_idx = global->if_from_pktio_idx[index];
+
+ global->stat[thr].pktio[if_idx].rx_packets += num;
+ }
+ }
+
+ odp_packet_free_multi(pkt, num);
+ }
+
+ if (clock_started)
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats*/
+ global->stat[thr].time_nsec = nsec;
+ global->stat[thr].rx_timeouts = rx_timeouts;
+ global->stat[thr].rx_packets = rx_packets;
+ global->stat[thr].rx_bytes = rx_bytes;
+ global->stat[thr].rx_lat_nsec = rx_lat_data.nsec;
+ global->stat[thr].rx_lat_min_nsec = rx_lat_data.min;
+ global->stat[thr].rx_lat_max_nsec = rx_lat_data.max;
+ global->stat[thr].rx_lat_packets = rx_lat_data.packets;
+
+ return ret;
+}
+
+static void drain_scheduler(test_global_t *global)
+{
+ odp_event_t ev;
+ uint64_t wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+
+ while ((ev = odp_schedule(NULL, wait_time)) != ODP_EVENT_INVALID) {
+ global->drained++;
+ odp_event_free(ev);
+ }
+}
+
+static void drain_direct_input(test_global_t *global)
+{
+ odp_pktin_queue_t pktin;
+ odp_packet_t pkt;
+ int i, j;
+ int num_pktio = global->test_options.num_pktio;
+ int num_rx = global->test_options.num_rx;
+
+ for (i = 0; i < num_pktio; i++) {
+ for (j = 0; j < num_rx; j++) {
+ pktin = global->pktio[i].pktin[j];
+
+ while (odp_pktin_recv(pktin, &pkt, 1) == 1) {
+ global->drained++;
+ odp_packet_free(pkt);
+ }
+ }
+ }
+}
+
+static int init_packets(test_global_t *global, int pktio,
+ odp_packet_t packet[], uint32_t num, uint16_t seq)
+{
+ odp_packet_t pkt;
+ uint32_t i, j, pkt_len, seg_len, payload_len, l2_len;
+ void *data;
+ uint8_t *u8;
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ uint16_t tpid;
+ test_options_t *test_options = &global->test_options;
+ const odp_bool_t use_tcp = test_options->l4_proto == L4_PROTO_TCP;
+ uint32_t num_vlan = test_options->num_vlan;
+ uint32_t hdr_len = test_options->hdr_len;
+ uint16_t src_port = test_options->src_port;
+ uint16_t dst_port = test_options->dst_port;
+ uint32_t src_cnt = 0;
+ uint32_t dst_cnt = 0;
+ uint32_t tcp_seqnum = 0x1234;
+ odph_vlanhdr_t *vlan = NULL; /* Fixes bogus compiler warning */
+
+ if (num_vlan > MAX_VLANS)
+ num_vlan = MAX_VLANS;
+
+ for (i = 0; i < num; i++) {
+ pkt = packet[i];
+ pkt_len = odp_packet_len(pkt);
+ seg_len = odp_packet_seg_len(pkt);
+ data = odp_packet_data(pkt);
+ payload_len = pkt_len - hdr_len;
+
+ if (seg_len < hdr_len) {
+ ODPH_ERR("Error: First segment too short %u\n", seg_len);
+ return -1;
+ }
+
+ /* Ethernet */
+ eth = data;
+ memcpy(eth->dst.addr, global->pktio[pktio].eth_dst.addr, 6);
+ memcpy(eth->src.addr, global->pktio[pktio].eth_src.addr, 6);
+ eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+ l2_len = ODPH_ETHHDR_LEN;
+
+ /* VLAN(s) */
+ if (num_vlan) {
+ tpid = test_options->vlan[0].tpid;
+ eth->type = odp_cpu_to_be_16(tpid);
+ }
+
+ for (j = 0; j < num_vlan; j++) {
+ vlan = (odph_vlanhdr_t *)((uint8_t *)data + l2_len);
+ vlan->tci = odp_cpu_to_be_16(test_options->vlan[j].tci);
+ if (j < num_vlan - 1) {
+ tpid = test_options->vlan[j + 1].tpid;
+ vlan->type = odp_cpu_to_be_16(tpid);
+ }
+
+ l2_len += ODPH_VLANHDR_LEN;
+ }
+
+ if (num_vlan)
+ vlan->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ /* IPv4 */
+ ip = (odph_ipv4hdr_t *)((uint8_t *)data + l2_len);
+ memset(ip, 0, ODPH_IPV4HDR_LEN);
+ ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(pkt_len - l2_len);
+ ip->id = odp_cpu_to_be_16(seq + i);
+ ip->ttl = 64;
+ ip->proto = use_tcp ? ODPH_IPPROTO_TCP : ODPH_IPPROTO_UDP;
+ ip->src_addr = odp_cpu_to_be_32(test_options->ipv4_src);
+ ip->dst_addr = odp_cpu_to_be_32(test_options->ipv4_dst);
+ ip->chksum = ~odp_chksum_ones_comp16(ip, ODPH_IPV4HDR_LEN);
+
+ u8 = ((uint8_t *)data + l2_len + ODPH_IPV4HDR_LEN);
+
+ if (use_tcp) {
+ odph_tcphdr_t *tcp = (odph_tcphdr_t *)u8;
+
+ memset(tcp, 0, ODPH_TCPHDR_LEN);
+ tcp->src_port = odp_cpu_to_be_16(src_port);
+ tcp->dst_port = odp_cpu_to_be_16(dst_port);
+ tcp->seq_no = odp_cpu_to_be_32(tcp_seqnum);
+ tcp->ack_no = odp_cpu_to_be_32(0x12345678);
+ tcp->window = odp_cpu_to_be_16(0x4000);
+ tcp->hl = 5;
+ tcp->ack = 1;
+ tcp_seqnum += payload_len;
+ } else {
+ odph_udphdr_t *udp = (odph_udphdr_t *)u8;
+
+ memset(udp, 0, ODPH_UDPHDR_LEN);
+ udp->src_port = odp_cpu_to_be_16(src_port);
+ udp->dst_port = odp_cpu_to_be_16(dst_port);
+ udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
+ udp->chksum = 0;
+ }
+
+ u8 = data;
+ u8 += hdr_len;
+
+ if (test_options->fill_pl) {
+ /* Init payload until the end of the first segment */
+ for (j = 0; j < seg_len - hdr_len; j++)
+ u8[j] = j;
+ }
+
+ /* Insert checksum */
+ odp_packet_l3_offset_set(pkt, l2_len);
+ odp_packet_l4_offset_set(pkt, l2_len + ODPH_IPV4HDR_LEN);
+ odp_packet_has_eth_set(pkt, 1);
+ odp_packet_has_ipv4_set(pkt, 1);
+ if (use_tcp) {
+ odp_packet_has_tcp_set(pkt, 1);
+ /* TCP checksum is always updated before TX */
+ } else {
+ odp_packet_has_udp_set(pkt, 1);
+ if (!test_options->calc_latency && test_options->calc_cs)
+ odph_udp_chksum_set(pkt);
+ }
+
+ /* Increment port numbers */
+ if (test_options->c_mode.src_port) {
+ src_cnt++;
+ if (src_cnt < test_options->c_mode.src_port) {
+ src_port++;
+ } else {
+ src_port = test_options->src_port;
+ src_cnt = 0;
+ }
+ }
+ if (test_options->c_mode.dst_port) {
+ dst_cnt++;
+ if (dst_cnt < test_options->c_mode.dst_port) {
+ dst_port++;
+ } else {
+ dst_port = test_options->dst_port;
+ dst_cnt = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline void update_tcp_hdr(odp_packet_t pkt, odp_packet_t base_pkt, uint32_t hdr_len)
+{
+ odph_tcphdr_t *tcp = odp_packet_l4_ptr(pkt, NULL);
+ odph_tcphdr_t *tcp_base = odp_packet_l4_ptr(base_pkt, NULL);
+ uint32_t prev_seqnum = odp_be_to_cpu_32(tcp_base->seq_no);
+
+ tcp->seq_no = odp_cpu_to_be_32(prev_seqnum + (odp_packet_len(pkt) - hdr_len));
+
+ /* Last used sequence number is stored in the base packet */
+ tcp_base->seq_no = tcp->seq_no;
+
+ odph_tcp_chksum_set(pkt);
+}
+
+static inline int update_rand_data(uint8_t *data, uint32_t data_len)
+{
+ uint32_t generated = 0;
+ uint32_t retries = 0;
+
+ while (generated < data_len) {
+ int32_t ret = odp_random_data(data, data_len - generated, ODP_RANDOM_BASIC);
+
+ if (odp_unlikely(ret < 0)) {
+ ODPH_ERR("Error: odp_random_data() failed: %" PRId32 "\n", ret);
+ return -1;
+ } else if (odp_unlikely(ret == 0)) {
+ retries++;
+ if (odp_unlikely(retries > MAX_RAND_RETRIES)) {
+ ODPH_ERR("Error: Failed to create random data\n");
+ return -1;
+ }
+ continue;
+ }
+ data += ret;
+ generated += ret;
+ }
+ return 0;
+}
+
+static inline void set_timestamp(odp_packet_t pkt, uint32_t ts_off)
+{
+ const ts_data_t ts_data = { .magic = TS_MAGIC, .tx_ts = odp_time_global_ns() };
+
+ (void)odp_packet_copy_from_mem(pkt, ts_off, sizeof(ts_data), &ts_data);
+}
+
+static int alloc_packets(odp_pool_t pool, odp_packet_t *pkt_tbl, uint32_t num,
+ test_global_t *global)
+{
+ uint32_t i, pkt_len;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_bins = global->num_bins;
+
+ pkt_len = test_options->pkt_len;
+
+ for (i = 0; i < num; i++) {
+ if (num_bins)
+ pkt_len = global->len_bin[i % num_bins];
+
+ pkt_tbl[i] = odp_packet_alloc(pool, pkt_len);
+ if (pkt_tbl[i] == ODP_PACKET_INVALID) {
+ ODPH_ERR("Error: Alloc of %uB packet failed\n", pkt_len);
+ break;
+ }
+ }
+
+ if (i == 0)
+ return -1;
+
+ if (i != num) {
+ odp_packet_free_multi(pkt_tbl, i);
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline uint32_t form_burst(odp_packet_t out_pkt[], uint32_t burst_size, uint32_t num_bins,
+ uint32_t burst, odp_packet_t *pkt_tbl, odp_pool_t pool,
+ int tx_mode, odp_bool_t calc_latency, uint32_t hdr_len,
+ odp_bool_t calc_udp_cs, uint64_t *total_bytes, uint8_t l4_proto)
+{
+ uint32_t i, idx;
+ odp_packet_t pkt;
+ static __thread int rand_idx = RAND_16BIT_WORDS;
+ static __thread uint16_t rand_data[RAND_16BIT_WORDS];
+ uint64_t bytes = 0;
+
+ idx = burst * burst_size;
+ if (num_bins)
+ idx = burst * burst_size * num_bins;
+
+ for (i = 0; i < burst_size; i++) {
+ if (num_bins) {
+ uint32_t bin;
+
+ if (rand_idx >= RAND_16BIT_WORDS) {
+ if (odp_unlikely(update_rand_data((uint8_t *)rand_data,
+ RAND_16BIT_WORDS * 2)))
+ break;
+ rand_idx = 0;
+ }
+ /* Select random length bin */
+ bin = rand_data[rand_idx++] % num_bins;
+ pkt = pkt_tbl[idx + bin];
+ idx += num_bins;
+ } else {
+ pkt = pkt_tbl[idx];
+ idx++;
+ }
+
+ if (tx_mode == TX_MODE_DF) {
+ out_pkt[i] = pkt;
+ } else if (tx_mode == TX_MODE_REF) {
+ out_pkt[i] = odp_packet_ref_static(pkt);
+
+ if (odp_unlikely(out_pkt[i] == ODP_PACKET_INVALID))
+ break;
+ } else {
+ out_pkt[i] = odp_packet_copy(pkt, pool);
+
+ if (odp_unlikely(out_pkt[i] == ODP_PACKET_INVALID))
+ break;
+
+ if (calc_latency)
+ set_timestamp(out_pkt[i], hdr_len);
+
+ if (l4_proto == L4_PROTO_TCP)
+ update_tcp_hdr(out_pkt[i], pkt, hdr_len);
+ else if (calc_latency && calc_udp_cs)
+ odph_udp_chksum_set(out_pkt[i]);
+ }
+
+ bytes += odp_packet_len(out_pkt[i]);
+ }
+
+ *total_bytes = bytes;
+
+ return i;
+}
+
+static inline uint32_t send_burst(odp_pktout_queue_t pktout, odp_packet_t pkt[],
+ uint32_t num, int tx_mode, uint64_t *drop_bytes)
+{
+ int ret;
+ uint32_t sent;
+ uint64_t bytes = 0;
+
+ ret = odp_pktout_send(pktout, pkt, num);
+
+ sent = ret;
+ if (odp_unlikely(ret < 0))
+ sent = 0;
+
+ if (odp_unlikely(sent != num)) {
+ uint32_t i;
+ uint32_t num_drop = num - sent;
+
+ for (i = sent; i < num; i++)
+ bytes += odp_packet_len(pkt[i]);
+
+ if (tx_mode != TX_MODE_DF)
+ odp_packet_free_multi(&pkt[sent], num_drop);
+ }
+
+ *drop_bytes = bytes;
+
+ return sent;
+}
+
+static int tx_thread(void *arg)
+{
+ int i, thr, tx_thr;
+ uint32_t exit_test, num_alloc, j;
+ odp_time_t t1, t2, next_tmo;
+ uint64_t diff_ns, t1_nsec;
+ odp_packet_t *pkt_tbl;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ test_options_t *test_options = &global->test_options;
+ int periodic_stat = test_options->update_msec ? 1 : 0;
+ odp_pool_t pool = global->pool;
+ uint64_t gap_nsec = test_options->gap_nsec;
+ uint64_t quit = test_options->quit;
+ uint64_t tx_timeouts = 0;
+ uint64_t tx_bytes = 0;
+ uint64_t tx_packets = 0;
+ uint64_t tx_drops = 0;
+ int ret = 0;
+ const uint32_t hdr_len = test_options->hdr_len;
+ const uint32_t burst_size = test_options->burst_size;
+ const uint32_t bursts = test_options->bursts;
+ const uint32_t num_tx = test_options->num_tx;
+ const uint8_t l4_proto = test_options->l4_proto;
+ const int tx_mode = test_options->tx_mode;
+ const odp_bool_t calc_cs = test_options->calc_cs;
+ const odp_bool_t calc_latency = test_options->calc_latency;
+ int num_pktio = test_options->num_pktio;
+ odp_pktout_queue_t pktout[num_pktio];
+ uint32_t tot_packets = 0;
+ uint32_t num_bins = global->num_bins;
+
+ thr = odp_thread_id();
+ tx_thr = thread_arg->tx_thr;
+ global->stat[thr].thread_type = TX_THREAD;
+
+ num_alloc = global->num_tx_pkt;
+ if (num_bins)
+ num_alloc = global->num_tx_pkt * num_bins;
+
+ for (i = 0; i < num_pktio; i++) {
+ int seq = i * num_alloc;
+
+ pktout[i] = thread_arg->pktout[i];
+ pkt_tbl = thread_arg->packet[i];
+
+ if (alloc_packets(pool, pkt_tbl, num_alloc, global)) {
+ ret = -1;
+ break;
+ }
+
+ tot_packets += num_alloc;
+
+ if (init_packets(global, i, pkt_tbl, num_alloc, seq)) {
+ ret = -1;
+ break;
+ }
+
+ if (tx_mode == TX_MODE_DF) {
+ for (j = 0; j < num_alloc; j++)
+ odp_packet_free_ctrl_set(pkt_tbl[j],
+ ODP_PACKET_FREE_CTRL_DONT_FREE);
+ }
+ }
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+
+ /* Start TX burst at different per thread offset */
+ t1_nsec = odp_time_to_ns(t1) + gap_nsec + (tx_thr * gap_nsec / num_tx);
+
+ while (ret == 0) {
+ exit_test = odp_atomic_load_u32(&global->exit_test);
+ if (exit_test)
+ break;
+
+ if (quit && tx_timeouts >= quit) {
+ odp_atomic_inc_u32(&global->exit_test);
+ break;
+ }
+
+ if (gap_nsec) {
+ uint64_t nsec = t1_nsec + tx_timeouts * gap_nsec;
+
+ next_tmo = odp_time_local_from_ns(nsec);
+ odp_time_wait_until(next_tmo);
+ }
+ tx_timeouts++;
+
+ /* Send bursts to each pktio */
+ for (i = 0; i < num_pktio; i++) {
+ uint32_t num, sent;
+ uint64_t total_bytes, drop_bytes;
+ odp_packet_t pkt[burst_size];
+
+ pkt_tbl = thread_arg->packet[i];
+
+ for (j = 0; j < bursts; j++) {
+ num = form_burst(pkt, burst_size, num_bins, j, pkt_tbl, pool,
+ tx_mode, calc_latency, hdr_len, calc_cs,
+ &total_bytes, l4_proto);
+
+ if (odp_unlikely(num == 0)) {
+ ret = -1;
+ tx_drops += burst_size;
+ break;
+ }
+
+ sent = send_burst(pktout[i], pkt, num, tx_mode, &drop_bytes);
+
+ if (odp_unlikely(sent == 0)) {
+ ret = -1;
+ tx_drops += burst_size;
+ break;
+ }
+
+ tx_bytes += total_bytes - drop_bytes;
+ tx_packets += sent;
+ if (odp_unlikely(sent < burst_size))
+ tx_drops += burst_size - sent;
+
+ if (odp_unlikely(periodic_stat))
+ global->stat[thr].pktio[i].tx_packets += sent;
+ }
+ }
+ }
+
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+
+ for (i = 0; i < num_pktio; i++) {
+ pkt_tbl = thread_arg->packet[i];
+
+ if (tot_packets == 0)
+ break;
+
+ odp_packet_free_multi(pkt_tbl, num_alloc);
+ tot_packets -= num_alloc;
+ }
+
+ /* Update stats */
+ global->stat[thr].time_nsec = diff_ns;
+ global->stat[thr].tx_timeouts = tx_timeouts;
+ global->stat[thr].tx_bytes = tx_bytes;
+ global->stat[thr].tx_packets = tx_packets;
+ global->stat[thr].tx_drops = tx_drops;
+
+ return ret;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t thr_common;
+ int i, j, ret, tx_thr;
+ test_options_t *test_options = &global->test_options;
+ int num_pktio = test_options->num_pktio;
+ int num_rx = test_options->num_rx;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &global->cpumask;
+
+ /* Receive threads */
+ for (i = 0; i < num_rx; i++) {
+ /* In direct mode, dedicate a pktin queue per pktio interface (per RX thread) */
+ for (j = 0; test_options->direct_rx && j < num_pktio; j++)
+ global->thread_arg[i].pktin[j] = global->pktio[j].pktin[i];
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = rx_thread;
+ thr_param[i].arg = &global->thread_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ /* Transmit threads */
+ tx_thr = 0;
+ for (i = num_rx; i < num_cpu; i++) {
+ for (j = 0; j < num_pktio; j++) {
+ odp_pktout_queue_t pktout;
+
+ global->thread_arg[i].tx_thr = tx_thr;
+
+ /* Dedicate a pktout queue per pktio interface
+ * (per TX thread) */
+ pktout = global->pktio[j].pktout[tx_thr];
+ global->thread_arg[i].pktout[j] = pktout;
+ }
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = tx_thread;
+ thr_param[i].arg = &global->thread_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ tx_thr++;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
+ num_cpu);
+
+ if (ret != num_cpu) {
+ ODPH_ERR("Error: thread create failed %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_periodic_stat(test_global_t *global, uint64_t nsec)
+{
+ int i, j;
+ int num_pktio = global->test_options.num_pktio;
+ double sec = nsec / 1000000000.0;
+ uint64_t num_tx[num_pktio];
+ uint64_t num_rx[num_pktio];
+
+ for (i = 0; i < num_pktio; i++) {
+ num_tx[i] = 0;
+ num_rx[i] = 0;
+
+ for (j = 0; j < MAX_THREADS; j++) {
+ if (global->stat[j].thread_type == RX_THREAD)
+ num_rx[i] += global->stat[j].pktio[i].rx_packets;
+ else if (global->stat[j].thread_type == TX_THREAD)
+ num_tx[i] += global->stat[j].pktio[i].tx_packets;
+ }
+ }
+ if (global->test_options.num_tx) {
+ printf(" TX: %12.6fs", sec);
+ for (i = 0; i < num_pktio; i++)
+ printf(" %10" PRIu64 "", num_tx[i]);
+ printf("\n");
+ }
+
+ if (global->test_options.num_rx) {
+ printf(" RX: %12.6fs", sec);
+ for (i = 0; i < num_pktio; i++)
+ printf(" %10" PRIu64 "", num_rx[i]);
+ printf("\n");
+ }
+}
+
+static void periodic_print_loop(test_global_t *global)
+{
+ odp_time_t t1, t2;
+ uint64_t nsec;
+ int i;
+ int num_pktio = global->test_options.num_pktio;
+
+ printf("\n\nPackets per interface\n");
+ printf(" Dir Time");
+ for (i = 0; i < num_pktio; i++)
+ printf(" %10i", i);
+
+ printf("\n -----------------");
+ for (i = 0; i < num_pktio; i++)
+ printf("-----------");
+
+ printf("\n");
+
+ t1 = odp_time_local();
+ while (odp_atomic_load_u32(&global->exit_test) == 0) {
+ usleep(1000 * global->test_options.update_msec);
+ t2 = odp_time_local();
+ nsec = odp_time_diff_ns(t2, t1);
+ print_periodic_stat(global, nsec);
+ }
+}
+
+static void print_humanised_time(double time_nsec)
+{
+ if (time_nsec > ODP_TIME_SEC_IN_NS)
+ printf("%.2f s\n", time_nsec / ODP_TIME_SEC_IN_NS);
+ else if (time_nsec > ODP_TIME_MSEC_IN_NS)
+ printf("%.2f ms\n", time_nsec / ODP_TIME_MSEC_IN_NS);
+ else if (time_nsec > ODP_TIME_USEC_IN_NS)
+ printf("%.2f us\n", time_nsec / ODP_TIME_USEC_IN_NS);
+ else
+ printf("%.0f ns\n", time_nsec);
+}
+
+static void print_humanised_latency(double lat_nsec, double lat_min_nsec, double lat_max_nsec)
+{
+ printf(" rx ave packet latency: ");
+ print_humanised_time(lat_nsec);
+ printf(" rx min packet latency: ");
+ print_humanised_time(lat_min_nsec);
+ printf(" rx max packet latency: ");
+ print_humanised_time(lat_max_nsec);
+}
+
+static int print_final_stat(test_global_t *global)
+{
+ int i, num_thr;
+ double rx_mbit_per_sec, tx_mbit_per_sec;
+ test_options_t *test_options = &global->test_options;
+ int num_rx = test_options->num_rx;
+ int num_tx = test_options->num_tx;
+ uint64_t rx_nsec_sum = 0;
+ uint64_t rx_pkt_sum = 0;
+ uint64_t rx_byte_sum = 0;
+ uint64_t rx_tmo_sum = 0;
+ uint64_t rx_lat_nsec_sum = 0;
+ uint64_t rx_lat_min_nsec = UINT64_MAX;
+ uint64_t rx_lat_max_nsec = 0;
+ uint64_t rx_lat_pkt_sum = 0;
+ uint64_t tx_nsec_sum = 0;
+ uint64_t tx_pkt_sum = 0;
+ uint64_t tx_byte_sum = 0;
+ uint64_t tx_drop_sum = 0;
+ uint64_t tx_tmo_sum = 0;
+ double rx_pkt_ave = 0.0;
+ double rx_pkt_per_sec = 0.0;
+ double rx_byte_per_sec = 0.0;
+ double rx_pkt_len = 0.0;
+ double rx_sec = 0.0;
+ double rx_ave_lat_nsec = 0.0;
+ double tx_pkt_per_sec = 0.0;
+ double tx_byte_per_sec = 0.0;
+ double tx_sec = 0.0;
+
+ printf("\nRESULTS PER THREAD\n");
+ printf(" rx thread:\n");
+ printf(" 1 2 3 4 5 6 7 8\n");
+ printf(" ---------------------------------------------------------------------------------------\n");
+ printf(" ");
+
+ num_thr = 0;
+ for (i = 0; i < MAX_THREADS; i++) {
+ if (global->stat[i].thread_type != RX_THREAD)
+ continue;
+
+ if (num_thr && (num_thr % 8) == 0)
+ printf("\n ");
+
+ printf("%10" PRIu64 " ", global->stat[i].rx_packets);
+ num_thr++;
+ }
+
+ printf("\n\n");
+
+ printf(" tx thread:\n");
+ printf(" 1 2 3 4 5 6 7 8\n");
+ printf(" ---------------------------------------------------------------------------------------\n");
+ printf(" ");
+
+ num_thr = 0;
+ for (i = 0; i < MAX_THREADS; i++) {
+ if (global->stat[i].thread_type != TX_THREAD)
+ continue;
+
+ if (num_thr && (num_thr % 8) == 0)
+ printf("\n ");
+
+ printf("%10" PRIu64 " ", global->stat[i].tx_packets);
+ num_thr++;
+ }
+
+ printf("\n\n");
+
+ for (i = 0; i < MAX_THREADS; i++) {
+ if (global->stat[i].thread_type == RX_THREAD) {
+ rx_tmo_sum += global->stat[i].rx_timeouts;
+ rx_pkt_sum += global->stat[i].rx_packets;
+ rx_byte_sum += global->stat[i].rx_bytes;
+ rx_nsec_sum += global->stat[i].time_nsec;
+ rx_lat_nsec_sum += global->stat[i].rx_lat_nsec;
+ rx_lat_pkt_sum += global->stat[i].rx_lat_packets;
+
+ if (global->stat[i].rx_lat_min_nsec < rx_lat_min_nsec)
+ rx_lat_min_nsec = global->stat[i].rx_lat_min_nsec;
+
+ if (global->stat[i].rx_lat_max_nsec > rx_lat_max_nsec)
+ rx_lat_max_nsec = global->stat[i].rx_lat_max_nsec;
+ } else if (global->stat[i].thread_type == TX_THREAD) {
+ tx_tmo_sum += global->stat[i].tx_timeouts;
+ tx_pkt_sum += global->stat[i].tx_packets;
+ tx_byte_sum += global->stat[i].tx_bytes;
+ tx_drop_sum += global->stat[i].tx_drops;
+ tx_nsec_sum += global->stat[i].time_nsec;
+ }
+ }
+
+ if (num_rx)
+ rx_pkt_ave = (double)rx_pkt_sum / num_rx;
+ rx_sec = rx_nsec_sum / 1000000000.0;
+ tx_sec = tx_nsec_sum / 1000000000.0;
+
+ /* Packets and bytes per thread per sec */
+ if (rx_nsec_sum) {
+ rx_pkt_per_sec = (1000000000.0 * (double)rx_pkt_sum) /
+ (double)rx_nsec_sum;
+
+ rx_byte_per_sec = 1000000000.0;
+ rx_byte_per_sec *= (rx_byte_sum + 24 * rx_pkt_sum);
+ rx_byte_per_sec /= (double)rx_nsec_sum;
+ }
+
+ if (tx_nsec_sum) {
+ tx_pkt_per_sec = (1000000000.0 * (double)tx_pkt_sum) /
+ (double)tx_nsec_sum;
+
+ tx_byte_per_sec = 1000000000.0;
+ tx_byte_per_sec *= (tx_byte_sum + 24 * tx_pkt_sum);
+ tx_byte_per_sec /= (double)tx_nsec_sum;
+ }
+
+ /* Total Mbit/s */
+ rx_mbit_per_sec = (num_rx * 8 * rx_byte_per_sec) / 1000000.0;
+ tx_mbit_per_sec = (num_tx * 8 * tx_byte_per_sec) / 1000000.0;
+
+ if (rx_pkt_sum)
+ rx_pkt_len = (double)rx_byte_sum / rx_pkt_sum;
+
+ if (rx_lat_pkt_sum)
+ rx_ave_lat_nsec = (double)rx_lat_nsec_sum / rx_lat_pkt_sum;
+
+ printf("TOTAL (%i rx and %i tx threads)\n", num_rx, num_tx);
+ printf(" rx timeouts: %" PRIu64 "\n", rx_tmo_sum);
+ printf(" rx time spent (sec): %.3f\n", rx_sec);
+ printf(" rx packets: %" PRIu64 "\n", rx_pkt_sum);
+ printf(" rx packets drained: %" PRIu64 "\n", global->drained);
+ printf(" rx packets per thr: %.1f\n", rx_pkt_ave);
+ printf(" rx packets per thr per sec: %.1f\n", rx_pkt_per_sec);
+ printf(" rx packets per sec: %.1f\n", num_rx * rx_pkt_per_sec);
+ printf(" rx ave packet len: %.1f\n", rx_pkt_len);
+
+ if (rx_lat_pkt_sum)
+ print_humanised_latency(rx_ave_lat_nsec, rx_lat_min_nsec, rx_lat_max_nsec);
+
+ printf(" rx Mbit/s: %.1f\n", rx_mbit_per_sec);
+ printf("\n");
+ printf(" tx timeouts: %" PRIu64 "\n", tx_tmo_sum);
+ printf(" tx time spent (sec): %.3f\n", tx_sec);
+ printf(" tx packets: %" PRIu64 "\n", tx_pkt_sum);
+ printf(" tx dropped packets: %" PRIu64 "\n", tx_drop_sum);
+ printf(" tx packets per thr per sec: %.1f\n", tx_pkt_per_sec);
+ printf(" tx packets per sec: %.1f\n", num_tx * tx_pkt_per_sec);
+ printf(" tx Mbit/s: %.1f\n", tx_mbit_per_sec);
+ printf("\n");
+
+ if (rx_pkt_sum < MIN_RX_PACKETS_CI)
+ return -1;
+
+ return 0;
+}
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ if (test_global == NULL)
+ return;
+
+ odp_atomic_add_u32(&test_global->exit_test, 1);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ test_global_t *global;
+ odp_shm_t shm;
+ int i;
+ int ret = 0;
+
+ signal(SIGINT, sig_handler);
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Error: Global init failed.\n");
+ return 1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: Local init failed.\n");
+ return 1;
+ }
+
+ shm = odp_shm_reserve("packet_gen_global", sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: SHM reserve failed.\n");
+ return 1;
+ }
+
+ global = odp_shm_addr(shm);
+ test_global = global;
+
+ memset(global, 0, sizeof(test_global_t));
+ odp_atomic_init_u32(&global->exit_test, 0);
+
+ for (i = 0; i < MAX_THREADS; i++)
+ global->thread_arg[i].global = global;
+
+ if (parse_options(argc, argv, global)) {
+ ret = 1;
+ goto term;
+ }
+
+ odp_sys_info_print();
+
+ /* Avoid all scheduler API calls in direct input mode */
+ if (global->test_options.direct_rx == 0)
+ odp_schedule_config(NULL);
+
+ if (set_num_cpu(global)) {
+ ret = 1;
+ goto term;
+ }
+
+ if (open_pktios(global)) {
+ ret = 1;
+ goto term;
+ }
+
+ if (start_pktios(global)) {
+ ret = 1;
+ goto term;
+ }
+
+ /* Start worker threads */
+ start_workers(global, instance);
+
+ /* Wait until workers have started. */
+ odp_barrier_wait(&global->barrier);
+
+ /* Periodic statistics printing */
+ if (global->test_options.update_msec)
+ periodic_print_loop(global);
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl,
+ global->test_options.num_cpu);
+
+ if (stop_pktios(global))
+ ret = 1;
+
+ if (global->test_options.direct_rx)
+ drain_direct_input(global);
+ else
+ drain_scheduler(global);
+
+ if (close_pktios(global))
+ ret = 1;
+
+ if (print_final_stat(global))
+ ret = 2;
+
+term:
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: SHM free failed.\n");
+ return 1;
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local failed.\n");
+ return 1;
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global failed.\n");
+ return 1;
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_packet_gen_run.sh b/test/performance/odp_packet_gen_run.sh
new file mode 100755
index 000000000..bb0eb3fb6
--- /dev/null
+++ b/test/performance/odp_packet_gen_run.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Nokia
+#
+
+# directory where test binaries have been built
+TEST_DIR="${TEST_DIR:-$PWD}"
+
+# directory where test sources are, including scripts
+TEST_SRC_DIR=$(dirname $0)
+
+PATH=$TEST_DIR:$PATH
+
+# exit codes expected by automake for skipped tests
+TEST_SKIPPED=77
+
+VALIDATION_TESTDIR=platform/$ODP_PLATFORM/test/validation
+PLATFORM_VALIDATION=${TEST_SRC_DIR}/../../$VALIDATION_TESTDIR
+
+# Use installed pktio env or for make check take it from platform directory
+if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+elif [ "$ODP_PLATFORM" = "" ]; then
+ echo "$0: error: ODP_PLATFORM must be defined"
+ # not skipped as this should never happen via "make check"
+ exit 1
+elif [ -f ${PLATFORM_VALIDATION}/api/pktio/pktio_env ]; then
+ . ${PLATFORM_VALIDATION}/api/pktio/pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory or "
+ echo "in platform/\$ODP_PLATFORM/test."
+ echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
+ exit 1
+fi
+
+run_packet_gen()
+{
+ setup_pktio_env clean # install trap to call cleanup_pktio_env
+
+ if [ $? -ne 0 ]; then
+ echo "setup_pktio_env error $?"
+ exit $TEST_SKIPPED
+ fi
+
+ # Runs 500 * 10ms = 5 sec
+ # Sends 500 packets through both interfaces => total 1000 packets
+
+ # Static packet length
+ odp_packet_gen${EXEEXT} -i $IF0,$IF1 -b 1 -g 10000000 -q 500 -w 10
+ ret=$?
+
+ if [ $ret -eq 2 ]; then
+ echo "FAIL: too few packets received"
+ fi
+ if [ $ret -ne 0 ]; then
+ echo "FAIL: test failed: $ret"
+ cleanup_pktio_env
+ exit $ret
+ fi
+
+ # Random packet length
+ odp_packet_gen${EXEEXT} -i $IF0,$IF1 -b 1 -g 10000000 -q 500 -L 60,1514,10 -w 10
+ ret=$?
+
+ if [ $ret -eq 2 ]; then
+ echo "FAIL: too few packets received"
+ fi
+ if [ $ret -ne 0 ]; then
+ echo "FAIL: test failed: $ret"
+ fi
+
+ cleanup_pktio_env
+
+ exit $ret
+}
+
+case "$1" in
+ setup) setup_pktio_env ;;
+ cleanup) cleanup_pktio_env ;;
+ *) run_packet_gen ;;
+esac
diff --git a/test/performance/odp_pktio_ordered.c b/test/performance/odp_pktio_ordered.c
new file mode 100644
index 000000000..18845a5df
--- /dev/null
+++ b/test/performance/odp_pktio_ordered.c
@@ -0,0 +1,1374 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ */
+
+/**
+ * @example odp_pktio_ordered.c
+ *
+ * Test application for ordered packet IO
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/** enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "dummy_crc.h"
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+/** Jenkins hash support.
+ *
+ * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ * are externally useful functions. Routines to test the hash are included
+ * if SELF_TEST is defined. You can use this free for any purpose. It's in
+ * the public domain. It has no warranty.
+ *
+ * $FreeBSD$
+ */
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+#define mix(a, b, c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c, 16); c += b; \
+ b -= a; b ^= rot(a, 19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+#define final(a, b, c) \
+{ \
+ c ^= b; c -= rot(b, 14); \
+ a ^= c; a -= rot(c, 11); \
+ b ^= a; b -= rot(a, 25); \
+ c ^= b; c -= rot(b, 16); \
+ a ^= c; a -= rot(c, 4); \
+ b ^= a; b -= rot(a, 14); \
+ c ^= b; c -= rot(b, 24); \
+}
+
+#define JHASH_GOLDEN_RATIO 0x9e3779b9
+
+/* Maximum pool and queue size */
+#define MAX_NUM_PKT (8 * 1024)
+
+/** Maximum number of worker threads */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+/** Buffer size of the packet pool buffer in bytes*/
+#define PKT_POOL_BUF_SIZE 1856
+
+/** Packet user area size in bytes */
+#define PKT_UAREA_SIZE 32
+
+/** Maximum number of packets in a burst */
+#define MAX_PKT_BURST 32
+
+/** Maximum number of pktio queues per interface */
+#define MAX_QUEUES 32
+
+/** Maximum number of pktio interfaces */
+#define MAX_PKTIOS 8
+
+/** Maximum number of packet flows */
+#define MAX_FLOWS 128
+
+ODP_STATIC_ASSERT(MAX_PKTIOS < MAX_FLOWS,
+ "MAX_FLOWS must be greater than MAX_PKTIOS\n");
+
+/** Minimum valid packet length */
+#define MIN_PACKET_LEN (ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN + ODPH_UDPHDR_LEN)
+
+/** Default number of input queues */
+#define DEF_NUM_RX_QUEUES 1
+
+/** Default number of flows */
+#define DEF_NUM_FLOWS 12
+
+/** Default number of extra processing rounds */
+#define DEF_EXTRA_ROUNDS 15
+
+/** Default statistics print interval in seconds */
+#define DEF_STATS_INT 1
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+
+/**
+ * Packet input mode
+ */
+typedef enum pktin_mode_t {
+ SCHED_ORDERED = 0,
+ SCHED_ATOMIC,
+ SCHED_PARALLEL
+} pktin_mode_t;
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ unsigned int cpu_count; /**< CPU count */
+ int if_count; /**< Number of interfaces to be used */
+ int addr_count; /**< Number of dst addresses to be used */
+ int num_rx_q; /**< Number of input queues per interface */
+ int num_flows; /**< Number of packet flows */
+ int extra_rounds; /**< Number of extra input processing rounds */
+ char **if_names; /**< Array of pointers to interface names */
+ odph_ethaddr_t addrs[MAX_PKTIOS]; /**< Array of dst addresses */
+ pktin_mode_t in_mode; /**< Packet input mode */
+ int time; /**< Time in seconds to run. */
+ int accuracy; /**< Statistics print interval */
+ char *if_str; /**< Storage for interface names */
+ int promisc_mode; /**< Promiscuous mode enabled */
+} appl_args_t;
+
+/**
+ * Queue context
+ */
+typedef struct {
+ odp_bool_t input_queue; /**< Input queue */
+ uint64_t idx; /**< Queue index */
+ uint64_t seq[MAX_FLOWS]; /**< Per flow sequence numbers */
+} qcontext_t;
+
+/**
+ * Flow info stored in the packet user area
+ */
+typedef struct {
+ uint64_t seq; /**< Sequence number */
+ uint32_t crc; /**< CRC hash */
+ uint16_t idx; /**< Flow index */
+ uint8_t src_idx; /**< Source port index */
+ uint8_t dst_idx; /**< Destination port index */
+
+} flow_t;
+ODP_STATIC_ASSERT(sizeof(flow_t) <= PKT_UAREA_SIZE,
+ "Flow data doesn't fit in the packet user area\n");
+
+/**
+ * Statistics
+ */
+typedef union ODP_ALIGNED_CACHE {
+ struct {
+ /** Number of forwarded packets */
+ uint64_t packets;
+ /** Packets dropped due to a receive error */
+ uint64_t rx_drops;
+ /** Packets dropped due to a transmit error */
+ uint64_t tx_drops;
+ /** Packets with invalid sequence number */
+ uint64_t invalid_seq;
+ } s;
+
+ uint8_t padding[ODP_CACHE_LINE_SIZE];
+} stats_t;
+
+/**
+ * IPv4 5-tuple
+ */
+typedef struct {
+ uint32_t src_ip;
+ uint32_t dst_ip;
+ uint16_t src_port;
+ uint16_t dst_port;
+ uint8_t proto;
+ uint8_t pad0;
+ uint16_t pad1;
+} ipv4_tuple5_t;
+
+/**
+ * Packet headers
+ */
+typedef struct {
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ipv4;
+ odph_udphdr_t *udp;
+} packet_hdr_t;
+
+/**
+ * Thread specific arguments
+ */
+typedef struct thread_args_t {
+ stats_t *stats; /**< Pointer to per thread statistics */
+} thread_args_t;
+
+/**
+ * Grouping of all global data
+ */
+typedef struct {
+ /** Per thread packet stats */
+ stats_t stats[MAX_WORKERS];
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Thread specific arguments */
+ thread_args_t thread[MAX_WORKERS];
+ /** Table of port ethernet addresses */
+ odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
+ /** Table of dst ethernet addresses */
+ odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
+ /** Table of dst ports */
+ int dst_port[MAX_PKTIOS];
+ /** Table of atomic queues for flows */
+ odp_queue_t fqueue[MAX_PKTIOS][MAX_FLOWS];
+ /** Table of flow queue contexts */
+ qcontext_t flow_qcontext[MAX_PKTIOS][MAX_FLOWS];
+ /** Table of input queue contexts */
+ qcontext_t input_qcontext[MAX_PKTIOS][MAX_QUEUES];
+ /** Table of pktio handles */
+ struct {
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout[MAX_FLOWS];
+ odp_queue_t pktin[MAX_QUEUES];
+ int num_rx_queue;
+ int num_tx_queue;
+ } pktios[MAX_PKTIOS];
+ /** Global barrier to synchronize main and workers */
+ odp_barrier_t barrier;
+ /** Break workers loop if set to 1 */
+ odp_atomic_u32_t exit_threads;
+} args_t;
+
+/** Global pointer to args */
+static args_t *gbl_args;
+
+/**
+ * Lookup the destination port for a given packet
+ *
+ * @param pkt ODP packet handle
+ */
+static inline int lookup_dest_port(odp_packet_t pkt)
+{
+ int i, src_idx;
+ odp_pktio_t pktio_src;
+
+ pktio_src = odp_packet_input(pkt);
+
+ for (src_idx = -1, i = 0; gbl_args->pktios[i].pktio
+ != ODP_PKTIO_INVALID; i++)
+ if (gbl_args->pktios[i].pktio == pktio_src)
+ src_idx = i;
+
+ if (src_idx == -1)
+ ODPH_ABORT("Failed to determine pktio input\n");
+
+ return gbl_args->dst_port[src_idx];
+}
+
+/**
+ * Map required packet headers
+ *
+ * @param pkt Packet handle
+ * @param hdr[out] Packet headers
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static inline int packet_hdr(odp_packet_t pkt, packet_hdr_t *hdr)
+{
+ uint8_t *udp;
+ uint16_t eth_type;
+ uint8_t ihl;
+
+ if (odp_unlikely(odp_packet_seg_len(pkt) < MIN_PACKET_LEN))
+ return -1;
+
+ if (odp_unlikely(!odp_packet_has_eth(pkt)))
+ return -1;
+
+ hdr->eth = odp_packet_l2_ptr(pkt, NULL);
+ eth_type = odp_be_to_cpu_16(hdr->eth->type);
+ if (odp_unlikely(eth_type != ODPH_ETHTYPE_IPV4))
+ return -1;
+
+ hdr->ipv4 = (odph_ipv4hdr_t *)(hdr->eth + 1);
+ if (odp_unlikely(hdr->ipv4->proto != ODPH_IPPROTO_UDP))
+ return -1;
+
+ ihl = ODPH_IPV4HDR_IHL(hdr->ipv4->ver_ihl);
+ if (odp_unlikely(ihl < ODPH_IPV4HDR_IHL_MIN))
+ return -1;
+
+ udp = (uint8_t *)hdr->ipv4 + (ihl * 4);
+
+ hdr->udp = (odph_udphdr_t *)udp;
+
+ return 0;
+}
+
+/**
+ * Compute hash from a 5-tuple
+ *
+ * @param key IPv4 5-tuple
+ *
+ * @return 32-bit hash value
+ */
+static inline uint64_t calc_ipv4_5tuple_hash(ipv4_tuple5_t *tuple)
+{
+ uint32_t a, b, c;
+
+ a = tuple->proto + JHASH_GOLDEN_RATIO;
+ b = tuple->src_ip + JHASH_GOLDEN_RATIO;
+ c = tuple->dst_ip + JHASH_GOLDEN_RATIO;
+
+ mix(a, b, c);
+
+ a += ((uint32_t)tuple->src_port << 16) + tuple->dst_port + JHASH_GOLDEN_RATIO;
+ final(a, b, c);
+
+ return c;
+}
+
+/**
+ * Compute packet flow index
+ *
+ * @param hdr Packet headers
+ *
+ * @return Flow index
+ */
+static inline uint64_t calc_flow_idx(packet_hdr_t *hdr)
+{
+ ipv4_tuple5_t tuple;
+ uint64_t idx;
+
+ tuple.dst_ip = odp_be_to_cpu_32(hdr->ipv4->dst_addr);
+ tuple.src_ip = odp_be_to_cpu_32(hdr->ipv4->src_addr);
+ tuple.proto = hdr->ipv4->proto;
+ tuple.src_port = odp_be_to_cpu_16(hdr->udp->src_port);
+ tuple.dst_port = odp_be_to_cpu_16(hdr->udp->dst_port);
+ tuple.pad0 = 0;
+ tuple.pad1 = 0;
+ idx = calc_ipv4_5tuple_hash(&tuple);
+
+ return idx % gbl_args->appl.num_flows;
+}
+
+/**
+ * Fill packet's eth addresses according to the destination port
+ *
+ * @param hdr[out] Packet headers
+ * @param dst_port Destination port
+ */
+static inline void fill_eth_addrs(packet_hdr_t *hdr, int dst_port)
+{
+ hdr->eth->src = gbl_args->port_eth_addr[dst_port];
+ hdr->eth->dst = gbl_args->dst_eth_addr[dst_port];
+}
+
+/**
+ * Process flow queue
+ *
+ * @param ev_tbl Array of events
+ * @param num Number of events in the array
+ * @param stats Pointer for storing thread statistics
+ * @param qcontext Source queue context
+ * @param pktout Arrays of output queues
+ */
+static inline void process_flow(odp_event_t ev_tbl[], int num, stats_t *stats,
+ qcontext_t *qcontext,
+ odp_pktout_queue_t pktout[][MAX_FLOWS])
+{
+ odp_packet_t pkt;
+ flow_t *flow;
+ uint64_t queue_seq;
+ int dst_if;
+ int i;
+ int sent;
+
+ for (i = 0; i < num; i++) {
+ pkt = odp_packet_from_event(ev_tbl[i]);
+
+ flow = odp_packet_user_area(pkt);
+
+ queue_seq = qcontext->seq[flow->src_idx];
+
+ /* Check sequence number */
+ if (gbl_args->appl.in_mode != SCHED_PARALLEL &&
+ odp_unlikely(flow->seq != queue_seq)) {
+ printf("Invalid sequence number: packet_seq=%" PRIu64 ""
+ " queue_seq=%" PRIu64 ", src_if=%" PRIu8 ", "
+ "dst_if=%" PRIu8 ", flow=%" PRIu16 "\n",
+ flow->seq, queue_seq, flow->src_idx,
+ flow->dst_idx, flow->idx);
+ qcontext->seq[flow->src_idx] = flow->seq + 1;
+ stats->s.invalid_seq++;
+ } else {
+ qcontext->seq[flow->src_idx]++;
+ }
+
+ dst_if = flow->dst_idx;
+ sent = odp_pktout_send(pktout[dst_if][flow->idx], &pkt, 1);
+
+ if (odp_unlikely(sent != 1)) {
+ stats->s.tx_drops++;
+ odp_packet_free(pkt);
+ }
+ stats->s.packets++;
+ }
+}
+
+/**
+ * Process input queue
+ *
+ * @param ev_tbl Array of events
+ * @param num Number of events in the array
+ * @param stats Pointer for storing thread statistics
+ * @param qcontext Source queue context
+ */
+static inline void process_input(odp_event_t ev_tbl[], int num, stats_t *stats,
+ qcontext_t *qcontext)
+{
+ flow_t *flow;
+ flow_t *flow_tbl[MAX_PKT_BURST];
+ int ret;
+ int i, j;
+ int pkts = 0;
+
+ for (i = 0; i < num; i++) {
+ odp_packet_t pkt;
+ packet_hdr_t hdr;
+ int flow_idx;
+
+ pkt = odp_packet_from_event(ev_tbl[i]);
+
+ odp_packet_prefetch(pkt, 0, MIN_PACKET_LEN);
+
+ ret = packet_hdr(pkt, &hdr);
+ if (odp_unlikely(ret)) {
+ odp_packet_free(pkt);
+ stats->s.rx_drops++;
+ continue;
+ }
+
+ flow_idx = calc_flow_idx(&hdr);
+
+ fill_eth_addrs(&hdr, flow_idx);
+
+ flow = odp_packet_user_area(pkt);
+ flow->idx = flow_idx;
+ flow->src_idx = qcontext->idx;
+ flow->dst_idx = lookup_dest_port(pkt);
+ flow_tbl[pkts] = flow;
+
+ /* Simulate "fat pipe" processing by generating extra work */
+ for (j = 0; j < gbl_args->appl.extra_rounds; j++)
+ flow->crc = dummy_hash_crc32c(odp_packet_data(pkt),
+ odp_packet_len(pkt), 0);
+ pkts++;
+ }
+
+ if (odp_unlikely(!pkts))
+ return;
+
+ /* Set sequence numbers */
+ if (gbl_args->appl.in_mode == SCHED_ORDERED)
+ odp_schedule_order_lock(0);
+
+ for (i = 0; i < pkts; i++) {
+ flow = flow_tbl[i];
+ flow->seq = qcontext->seq[flow->idx]++;
+ }
+
+ if (gbl_args->appl.in_mode == SCHED_ORDERED)
+ odp_schedule_order_unlock(0);
+
+ for (i = 0; i < pkts; i++) {
+ flow = flow_tbl[i];
+ ret = odp_queue_enq(gbl_args->fqueue[flow->dst_idx][flow->idx],
+ ev_tbl[i]);
+
+ if (odp_unlikely(ret != 0)) {
+ ODPH_ERR("odp_queue_enq() failed\n");
+ stats->s.tx_drops++;
+ odp_event_free(ev_tbl[i]);
+ } else {
+ stats->s.packets++;
+ }
+ }
+}
+
+/**
+ * Worker thread
+ *
+ * @param arg Thread arguments of type 'thread_args_t *'
+ */
+static int run_worker(void *arg)
+{
+ odp_event_t ev_tbl[MAX_PKT_BURST];
+ odp_queue_t queue;
+ odp_pktout_queue_t pktout[MAX_PKTIOS][MAX_FLOWS];
+ qcontext_t *qcontext;
+ thread_args_t *thr_args = arg;
+ stats_t *stats = thr_args->stats;
+ int pkts;
+ int i, j;
+
+ memset(pktout, 0, sizeof(pktout));
+
+ for (i = 0; i < gbl_args->appl.if_count; i++) {
+ for (j = 0; j < gbl_args->appl.num_flows; j++) {
+ pktout[i][j] = gbl_args->pktios[i].pktout[j %
+ gbl_args->pktios[i].num_tx_queue];
+ }
+ }
+ odp_barrier_wait(&gbl_args->barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
+ pkts = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, ev_tbl,
+ MAX_PKT_BURST);
+ if (pkts <= 0)
+ continue;
+
+ qcontext = odp_queue_context(queue);
+
+ if (qcontext->input_queue)
+ process_input(ev_tbl, pkts, stats, qcontext);
+ else
+ process_flow(ev_tbl, pkts, stats, qcontext, pktout);
+ }
+
+ /* Free remaining events in queues */
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL,
+ odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ return 0;
+}
+
+/**
+ * Create a pktio handle and associate with input queues
+ *
+ * @param dev Name of device to open
+ * @param index Pktio index
+ * @param num_rx Number of input queues
+ * @param num_tx Number of output queues
+ * @param pool Pool to associate with device for packet RX/TX
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
+ odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktio_op_mode_t mode_rx;
+ odp_pktio_op_mode_t mode_tx;
+ int i;
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(dev, pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Error: failed to open %s\n", dev);
+ return -1;
+ }
+
+ printf("Created pktio %" PRIu64 " (%s)\n",
+ odp_pktio_to_u64(pktio), dev);
+
+ if (odp_pktio_capability(pktio, &capa)) {
+ ODPH_ERR("Error: capability query failed %s\n", dev);
+ odp_pktio_close(pktio);
+ return -1;
+ }
+
+ odp_pktio_config_init(&config);
+ config.parser.layer = ODP_PROTO_LAYER_L2;
+ odp_pktio_config(pktio, &config);
+
+ if (gbl_args->appl.promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
+ if (!capa.set_op.op.promisc_mode) {
+ ODPH_ERR("Error: promisc mode set not supported %s\n",
+ dev);
+ return -1;
+ }
+
+ /* Enable promisc mode */
+ if (odp_pktio_promisc_mode_set(pktio, true)) {
+ ODPH_ERR("Error: promisc mode enable failed %s\n", dev);
+ return -1;
+ }
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ odp_pktout_queue_param_init(&pktout_param);
+
+ mode_tx = ODP_PKTIO_OP_MT;
+ mode_rx = ODP_PKTIO_OP_MT;
+
+ if (gbl_args->appl.in_mode == SCHED_ATOMIC) {
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ } else if (gbl_args->appl.in_mode == SCHED_PARALLEL) {
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ } else {
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ pktin_param.queue_param.sched.lock_count = 1;
+ }
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ if (num_rx > (int)capa.max_input_queues) {
+ printf("Allocating %i shared input queues, %i requested\n",
+ capa.max_input_queues, num_rx);
+ num_rx = capa.max_input_queues;
+ mode_rx = ODP_PKTIO_OP_MT;
+ }
+
+ if (num_tx > (int)capa.max_output_queues) {
+ printf("Allocating %i shared output queues, %i requested\n",
+ capa.max_output_queues, num_tx);
+ num_tx = capa.max_output_queues;
+ mode_tx = ODP_PKTIO_OP_MT;
+ }
+
+ pktin_param.hash_enable = (num_rx > 1) ? 1 : 0;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ pktin_param.num_queues = num_rx;
+ pktin_param.op_mode = mode_rx;
+
+ pktout_param.op_mode = mode_tx;
+ pktout_param.num_queues = num_tx;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Error: input queue config failed %s\n", dev);
+ return -1;
+ }
+
+ if (odp_pktout_queue_config(pktio, &pktout_param)) {
+ ODPH_ERR("Error: output queue config failed %s\n", dev);
+ return -1;
+ }
+
+ if (odp_pktin_event_queue(pktio, gbl_args->pktios[idx].pktin,
+ num_rx) != num_rx) {
+ ODPH_ERR("Error: pktin event queue query failed %s\n", dev);
+ return -1;
+ }
+
+ /* Set queue contexts */
+ for (i = 0; i < num_rx; i++) {
+ gbl_args->input_qcontext[idx][i].idx = idx;
+ gbl_args->input_qcontext[idx][i].input_queue = 1;
+
+ if (odp_queue_context_set(gbl_args->pktios[idx].pktin[i],
+ &gbl_args->input_qcontext[idx][i],
+ sizeof(qcontext_t))) {
+ ODPH_ERR("Error: pktin queue context set failed %s\n",
+ dev);
+ return -1;
+ }
+ }
+
+ if (odp_pktout_queue(pktio,
+ gbl_args->pktios[idx].pktout,
+ num_tx) != num_tx) {
+ ODPH_ERR("Error: pktout queue query failed %s\n", dev);
+ return -1;
+ }
+
+ printf("Created %i input and %i output queues on (%s)\n",
+ num_rx, num_tx, dev);
+
+ gbl_args->pktios[idx].num_rx_queue = num_rx;
+ gbl_args->pktios[idx].num_tx_queue = num_tx;
+ gbl_args->pktios[idx].pktio = pktio;
+
+ return 0;
+}
+
+/**
+ * Print statistics
+ *
+ * @param num_workers Number of worker threads
+ * @param thr_stats Pointer to stats storage
+ * @param duration Number of seconds to loop in
+ * @param timeout Number of seconds for stats calculation
+ *
+ */
+static int print_speed_stats(int num_workers, stats_t *thr_stats,
+ int duration, int timeout)
+{
+ uint64_t pkts = 0;
+ uint64_t pkts_prev = 0;
+ uint64_t pps;
+ uint64_t rx_drops, tx_drops, invalid_seq;
+ uint64_t maximum_pps = 0;
+ int i;
+ int elapsed = 0;
+ int stats_enabled = 1;
+ int loop_forever = (duration == 0);
+
+ if (timeout <= 0) {
+ stats_enabled = 0;
+ timeout = 1;
+ }
+ /* Wait for all threads to be ready*/
+ odp_barrier_wait(&gbl_args->barrier);
+
+ do {
+ pkts = 0;
+ rx_drops = 0;
+ tx_drops = 0;
+ invalid_seq = 0;
+
+ sleep(timeout);
+
+ for (i = 0; i < num_workers; i++) {
+ pkts += thr_stats[i].s.packets;
+ rx_drops += thr_stats[i].s.rx_drops;
+ tx_drops += thr_stats[i].s.tx_drops;
+ invalid_seq += thr_stats[i].s.invalid_seq;
+ }
+ if (stats_enabled) {
+ pps = (pkts - pkts_prev) / timeout;
+ if (pps > maximum_pps)
+ maximum_pps = pps;
+ printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
+ maximum_pps);
+
+ printf("%" PRIu64 " rx drops, %" PRIu64 " tx drops, ",
+ rx_drops, tx_drops);
+
+ printf("%" PRIu64 " invalid seq\n", invalid_seq);
+
+ pkts_prev = pkts;
+ }
+ elapsed += timeout;
+ } while (loop_forever || (elapsed < duration));
+
+ if (stats_enabled)
+ printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
+ maximum_pps);
+
+ return (pkts > 100 && !invalid_seq) ? 0 : -1;
+}
+
+/**
+ * Find the destination port for a given input port
+ *
+ * @param port Input port index
+ */
+static int find_dest_port(int port)
+{
+ /* Even number of ports */
+ if (gbl_args->appl.if_count % 2 == 0)
+ return (port % 2 == 0) ? port + 1 : port - 1;
+
+ /* Odd number of ports */
+ if (port == gbl_args->appl.if_count - 1)
+ return 0;
+ else
+ return port + 1;
+}
+
+/**
+ * Initialize port forwarding table
+ */
+static void init_forwarding_tbl(void)
+{
+ int rx_idx;
+
+ for (rx_idx = 0; rx_idx < gbl_args->appl.if_count; rx_idx++)
+ gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane ordered pktio application.\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth0,eth1\n"
+ " In the above example,\n"
+ " eth0 will send pkts to eth1 and vice versa\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " Interface count min 1, max %i\n"
+ "\n"
+ "Optional OPTIONS:\n"
+ " -m, --mode Packet input mode\n"
+ " 0: Scheduled ordered queues (default)\n"
+ " 1: Scheduled atomic queues\n"
+ " 2: Scheduled parallel queues (packet order not maintained)\n"
+ " -r, --num_rx_q Number of RX queues per interface\n"
+ " -f, --num_flows Number of packet flows\n"
+ " -e, --extra_input <number> Number of extra input processing rounds\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -t, --time <number> Time in seconds to run.\n"
+ " -a, --accuracy <number> Statistics print interval in seconds\n"
+ " (default is 1 second).\n"
+ " -d, --dst_addr Destination addresses (comma-separated, no spaces)\n"
+ " -P, --promisc_mode Enable promiscuous mode.\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS
+ );
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *token;
+ char *addr_str;
+ size_t len;
+ int i;
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"time", required_argument, NULL, 't'},
+ {"accuracy", required_argument, NULL, 'a'},
+ {"interface", required_argument, NULL, 'i'},
+ {"mode", required_argument, NULL, 'm'},
+ {"dst_addr", required_argument, NULL, 'd'},
+ {"num_rx_q", required_argument, NULL, 'r'},
+ {"num_flows", required_argument, NULL, 'f'},
+ {"extra_input", required_argument, NULL, 'e'},
+ {"promisc_mode", no_argument, NULL, 'P'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:t:a:i:m:d:r:f:e:Ph";
+
+ appl_args->time = 0; /* loop forever if time to run is 0 */
+ appl_args->accuracy = DEF_STATS_INT;
+ appl_args->cpu_count = 1; /* use one worker by default */
+ appl_args->num_rx_q = DEF_NUM_RX_QUEUES;
+ appl_args->num_flows = DEF_NUM_FLOWS;
+ appl_args->extra_rounds = DEF_EXTRA_ROUNDS;
+ appl_args->promisc_mode = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'a':
+ appl_args->accuracy = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'd':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ addr_str = malloc(len);
+ if (addr_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* store the mac addresses names */
+ strcpy(addr_str, optarg);
+ for (token = strtok(addr_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ if (i >= MAX_PKTIOS) {
+ printf("too many MAC addresses\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (odph_eth_addr_parse(&appl_args->addrs[i],
+ token) != 0) {
+ printf("invalid MAC address\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ appl_args->addr_count = i;
+ if (appl_args->addr_count < 1) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ free(addr_str);
+ break;
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_str = malloc(len);
+ if (appl_args->if_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ appl_args->if_count = i;
+
+ if (appl_args->if_count < 1 ||
+ appl_args->if_count > MAX_PKTIOS) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+ case 'm':
+ i = atoi(optarg);
+ if (i == 1)
+ appl_args->in_mode = SCHED_ATOMIC;
+ else if (i == 2)
+ appl_args->in_mode = SCHED_PARALLEL;
+ else
+ appl_args->in_mode = SCHED_ORDERED;
+ break;
+ case 'r':
+ appl_args->num_rx_q = atoi(optarg);
+ break;
+ case 'f':
+ appl_args->num_flows = atoi(optarg);
+ break;
+ case 'e':
+ appl_args->extra_rounds = atoi(optarg);
+ break;
+ case 'P':
+ appl_args->promisc_mode = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->num_flows > MAX_FLOWS) {
+ printf("Too many flows requested %d, max: %d\n",
+ appl_args->num_flows, MAX_FLOWS);
+ exit(EXIT_FAILURE);
+ }
+
+ if (appl_args->if_count == 0 || appl_args->num_flows == 0 ||
+ appl_args->num_rx_q == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (appl_args->addr_count != 0 &&
+ appl_args->addr_count != appl_args->if_count) {
+ printf("Number of destination addresses differs from number"
+ " of interfaces\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ odp_sys_info_print();
+
+ printf("%s options\n"
+ "-------------------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+ printf("\n"
+ "Input queues: %d\n"
+ "Mode: %s\n"
+ "Flows: %d\n"
+ "Extra rounds: %d\n"
+ "Promisc mode: %s\n", appl_args->num_rx_q,
+ (appl_args->in_mode == SCHED_ATOMIC) ? "PKTIN_SCHED_ATOMIC" :
+ (appl_args->in_mode == SCHED_PARALLEL ? "PKTIN_SCHED_PARALLEL" :
+ "PKTIN_SCHED_ORDERED"), appl_args->num_flows,
+ appl_args->extra_rounds, appl_args->promisc_mode ?
+ "enabled" : "disabled");
+ fflush(NULL);
+}
+
+static void gbl_args_init(args_t *args)
+{
+ int pktio, queue;
+
+ memset(args, 0, sizeof(args_t));
+ odp_atomic_init_u32(&args->exit_threads, 0);
+
+ for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
+ args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
+
+ for (queue = 0; queue < MAX_QUEUES; queue++)
+ args->pktios[pktio].pktin[queue] = ODP_QUEUE_INVALID;
+ }
+}
+
+/**
+ * ODP ordered pktio application
+ */
+int main(int argc, char *argv[])
+{
+ odp_cpumask_t cpumask;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ odp_shm_t shm;
+ odp_schedule_capability_t schedule_capa;
+ odp_schedule_config_t schedule_config;
+ odp_pool_capability_t pool_capa;
+ odph_ethaddr_t new_addr;
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_WORKERS];
+ stats_t *stats;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ int i, j;
+ int if_count;
+ int ret;
+ int num_workers;
+ uint32_t queue_size, pool_size;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_schedule_capability(&schedule_capa)) {
+ printf("Error: Schedule capa failed.\n");
+ return -1;
+ }
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Error: Pool capa failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args = odp_shm_addr(shm);
+
+ if (gbl_args == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ odp_shm_free(shm);
+ exit(EXIT_FAILURE);
+ }
+ gbl_args_init(gbl_args);
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ odp_schedule_config_init(&schedule_config);
+ odp_schedule_config(&schedule_config);
+
+ if (gbl_args->appl.in_mode == SCHED_ORDERED) {
+ /* At least one ordered lock required */
+ if (schedule_capa.max_ordered_locks < 1) {
+ ODPH_ERR("Error: Ordered locks not available.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &gbl_args->appl);
+
+ num_workers = MAX_WORKERS;
+ if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
+ num_workers = gbl_args->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ if_count = gbl_args->appl.if_count;
+
+ printf("Num worker threads: %i\n", num_workers);
+ printf("First CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("CPU mask: %s\n\n", cpumaskstr);
+
+ pool_size = MAX_NUM_PKT;
+ if (pool_capa.pkt.max_num && pool_capa.pkt.max_num < MAX_NUM_PKT)
+ pool_size = pool_capa.pkt.max_num;
+
+ queue_size = MAX_NUM_PKT;
+ if (schedule_config.queue_size &&
+ schedule_config.queue_size < MAX_NUM_PKT)
+ queue_size = schedule_config.queue_size;
+
+ /* Pool should not be larger than queue, otherwise queue enqueues at
+ * packet input may fail. */
+ if (pool_size > queue_size)
+ pool_size = queue_size;
+
+ /* Create packet pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_BUF_SIZE;
+ params.pkt.len = PKT_POOL_BUF_SIZE;
+ params.pkt.num = pool_size;
+ params.pkt.uarea_size = PKT_UAREA_SIZE;
+ params.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet pool", &params);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_pool_print(pool);
+
+ init_forwarding_tbl();
+
+ for (i = 0; i < if_count; ++i) {
+ const char *dev = gbl_args->appl.if_names[i];
+ int num_rx, num_tx;
+
+ num_rx = gbl_args->appl.num_rx_q;
+ num_tx = gbl_args->appl.num_flows;
+
+ if (create_pktio(dev, i, num_rx, num_tx, pool))
+ exit(EXIT_FAILURE);
+
+ /* Save interface ethernet address */
+ if (odp_pktio_mac_addr(gbl_args->pktios[i].pktio,
+ gbl_args->port_eth_addr[i].addr,
+ ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("Error: interface ethernet address unknown\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktio_print(gbl_args->pktios[i].pktio);
+
+ /* Save destination eth address */
+ /* 02:00:00:00:00:XX */
+ memset(&new_addr, 0, sizeof(odph_ethaddr_t));
+ if (gbl_args->appl.addr_count) {
+ memcpy(&new_addr, &gbl_args->appl.addrs[i],
+ sizeof(odph_ethaddr_t));
+ } else {
+ new_addr.addr[0] = 0x02;
+ new_addr.addr[5] = i;
+ }
+ gbl_args->dst_eth_addr[i] = new_addr;
+ }
+
+ gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
+
+ /* Allocate the same number of flows to each interface */
+ for (i = 0; i < if_count; i++) {
+ odp_pktio_capability_t capa;
+
+ if (odp_pktio_capability(gbl_args->pktios[i].pktio, &capa)) {
+ ODPH_ERR("Error: pktio capability failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((uint32_t)gbl_args->appl.num_flows > capa.max_output_queues)
+ gbl_args->appl.num_flows = capa.max_output_queues;
+ }
+
+ /* Create atomic queues for packet tagging */
+ for (i = 0; i < if_count; i++) {
+ for (j = 0; j < gbl_args->appl.num_flows; j++) {
+ odp_queue_t queue;
+ odp_queue_param_t qparam;
+ char qname[ODP_QUEUE_NAME_LEN];
+
+ snprintf(qname, sizeof(qname), "flow_%d_%d", i, j);
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ qparam.size = queue_size;
+
+ gbl_args->flow_qcontext[i][j].idx = i;
+ gbl_args->flow_qcontext[i][j].input_queue = 0;
+ qparam.context = &gbl_args->flow_qcontext[i][j];
+ qparam.context_len = sizeof(qcontext_t);
+
+ queue = odp_queue_create(qname, &qparam);
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Error: flow queue create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ gbl_args->fqueue[i][j] = queue;
+ }
+ }
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ stats = gbl_args->stats;
+
+ odp_barrier_init(&gbl_args->barrier, num_workers + 1);
+
+ /* Create worker threads */
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+
+ for (i = 0; i < num_workers; ++i) {
+ gbl_args->thread[i].stats = &stats[i];
+
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = run_worker;
+ thr_param[i].arg = &gbl_args->thread[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
+
+ /* Start packet receive and transmit */
+ for (i = 0; i < if_count; ++i) {
+ odp_pktio_t pktio;
+
+ pktio = gbl_args->pktios[i].pktio;
+ ret = odp_pktio_start(pktio);
+ if (ret) {
+ ODPH_ERR("Error: unable to start %s\n",
+ gbl_args->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
+ gbl_args->appl.accuracy);
+
+ /* Stop receiving new packet */
+ for (i = 0; i < if_count; i++)
+ odp_pktio_stop(gbl_args->pktios[i].pktio);
+
+ odp_atomic_store_u32(&gbl_args->exit_threads, 1);
+
+ /* Master thread waits for other threads to exit */
+ odph_thread_join(thread_tbl, num_workers);
+
+ for (i = 0; i < if_count; i++) {
+ odp_pktio_close(gbl_args->pktios[i].pktio);
+
+ for (j = 0; j < gbl_args->appl.num_flows; j++)
+ odp_queue_destroy(gbl_args->fqueue[i][j]);
+ }
+
+ free(gbl_args->appl.if_names);
+ free(gbl_args->appl.if_str);
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Error: pool destroy\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: shm free\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_pktio_ordered_run.sh b/test/performance/odp_pktio_ordered_run.sh
new file mode 100755
index 000000000..017c97091
--- /dev/null
+++ b/test/performance/odp_pktio_ordered_run.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
+#
+
+TEST_SRC_DIR=$(dirname $0)
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+DURATION=1
+LOG=odp_pktio_ordered.log
+LOOPS=100000000
+PASS_PPS=100
+PCAP_IN=`find . ${TEST_SRC_DIR} $(dirname $0) -name udp64.pcap -print -quit`
+PCAP_OUT=/dev/null
+
+if [ ! -f ${PCAP_IN} ]; then
+ echo "FAIL: no udp64.pcap"
+ exit 1
+fi
+
+${TEST_DIR}/odp_pktio_ordered${EXEEXT} \
+ -i pcap:in=${PCAP_IN}:loops=$LOOPS,pcap:out=${PCAP_OUT} \
+ -t $DURATION | tee $LOG
+ret=${PIPESTATUS[0]}
+
+if [ $ret -ne 0 ]; then
+ echo "FAIL: no odp_pktio_ordered${EXEEXT}"
+ rm -f $LOG
+ exit $ret
+fi
+
+if [ ! -f $LOG ]; then
+ echo "FAIL: $LOG not found"
+ ret=1
+ exit $ret
+fi
+
+MAX_PPS=$(awk '/TEST RESULT/ {print $3}' $LOG)
+echo "MAX_PPS=$MAX_PPS"
+if [ $MAX_PPS -lt $PASS_PPS ]; then
+ echo "FAIL: pps below threshold $MAX_PPS < $PASS_PPS"
+ ret=1
+fi
+
+rm -f $LOG
+
+exit $ret
diff --git a/test/performance/odp_pktio_perf.c b/test/performance/odp_pktio_perf.c
new file mode 100644
index 000000000..8ca9d076e
--- /dev/null
+++ b/test/performance/odp_pktio_perf.c
@@ -0,0 +1,1125 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+/**
+ * @example odp_pktio_perf.c
+ *
+ * Packet IO basic performance test application. Runs a number of transmit and
+ * receive workers on separate cores, the transmitters generate packets at a
+ * defined rate and the receivers consume them. Generated packets are UDP and
+ * each packet is marked with a magic number in the UDP payload allowing
+ * receiver to distinguish them from other traffic.
+ *
+ * Each test iteration runs for a fixed period, at the end of the iteration
+ * it is verified that the number of packets transmitted was as expected and
+ * that all transmitted packets were received.
+ *
+ * The default mode is to run multiple test iterations at different rates to
+ * determine the maximum rate at which no packet loss occurs. Alternatively
+ * a single packet rate can be specified on the command line.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <odp_api.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define TEST_SKIP 77
+
+#define PKT_BUF_NUM (32 * 1024)
+#define MAX_NUM_IFACES 2
+#define TEST_HDR_MAGIC 0x92749451
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+#define BATCH_LEN_MAX 32
+
+/* Packet rate at which to start when using binary search */
+#define RATE_SEARCH_INITIAL_PPS 1000000
+
+/* When using the binary search method to determine the maximum
+ * achievable packet rate, this value specifies how close the pass
+ * and fail measurements must be before the test is terminated. */
+#define RATE_SEARCH_ACCURACY_PPS 100000
+
+/* Amount of time to wait, in nanoseconds, between the transmitter(s)
+ * completing and the receiver(s) being shutdown. Any packets not
+ * received by this time will be assumed to have been lost. */
+#define SHUTDOWN_DELAY_NS (ODP_TIME_MSEC_IN_NS * 100)
+
+/* Number of duration units in a second. */
+#define T_SCALE 10
+
+/* Default test duration in T_SCALE units */
+#define DEFAULT_DURATION 1
+
+#define CACHE_ALIGN_ROUNDUP(x)\
+ ((ODP_CACHE_LINE_SIZE) * \
+ (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE)))
+
+#define PKT_HDR_LEN (sizeof(pkt_head_t) + ODPH_UDPHDR_LEN + \
+ ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN)
+
+/** Parsed command line application arguments */
+typedef struct {
+ unsigned int cpu_count; /* CPU count */
+ int num_tx_workers;/* Number of CPUs to use for transmit */
+ int duration; /* Time to run each iteration of the test for */
+ uint32_t tx_batch_len; /* Number of packets to send in a single
+ batch */
+ int schedule; /* 1: receive packets via scheduler
+ 0: receive packets via direct deq */
+ uint32_t rx_batch_len; /* Number of packets to receive in a single
+ batch */
+ uint64_t pps; /* Attempted packet rate */
+ int verbose; /* Print verbose information, such as per
+ thread statistics */
+ unsigned pkt_len; /* Packet payload length in bytes (not
+ including headers) */
+ int search; /* Set implicitly when pps is not configured.
+ Perform a search at different packet rates
+ to determine the maximum rate at which no
+ packet loss occurs. */
+
+ char *if_str;
+ const char *ifaces[MAX_NUM_IFACES];
+ int num_ifaces;
+} test_args_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ uint64_t rx_cnt; /* Valid packets received */
+ uint64_t rx_ignore; /* Ignored packets */
+} pkt_rx_stats_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ uint64_t tx_cnt; /* Packets transmitted */
+ uint64_t alloc_failures;/* Packet allocation failures */
+ uint64_t enq_failures; /* Enqueue failures */
+ odp_time_t idle_ticks; /* Idle ticks count in TX loop */
+} pkt_tx_stats_t;
+
+/* Test global variables */
+typedef struct {
+ odp_instance_t instance;
+ test_args_t args;
+ odp_barrier_t rx_barrier;
+ odp_barrier_t tx_barrier;
+ odp_pktio_t pktio_tx;
+ odp_pktio_t pktio_rx;
+ /* Pool from which transmitted packets are allocated */
+ odp_pool_t transmit_pkt_pool;
+ pkt_rx_stats_t *rx_stats;
+ pkt_tx_stats_t *tx_stats;
+ uint8_t src_mac[ODPH_ETHADDR_LEN];
+ uint8_t dst_mac[ODPH_ETHADDR_LEN];
+ uint32_t rx_stats_size;
+ uint32_t tx_stats_size;
+ /* Indicate to the receivers to shutdown */
+ odp_atomic_u32_t shutdown;
+ /* Sequence number of IP packets */
+ odp_atomic_u32_t ip_seq ODP_ALIGNED_CACHE;
+} test_globals_t;
+
+/* Status of max rate search */
+typedef struct {
+ uint64_t pps_curr; /* Current attempted PPS */
+ uint64_t pps_pass; /* Highest passing PPS */
+ uint64_t pps_fail; /* Lowest failing PPS */
+ int warmup; /* Warmup stage - ignore results */
+} test_status_t;
+
+/* Thread specific arguments */
+typedef struct {
+ int batch_len; /* Number of packets per transmit batch */
+ int duration; /* Run duration in scaled time units */
+ uint64_t pps; /* Packets per second for this thread */
+} thread_args_t;
+
+typedef struct {
+ odp_u32be_t magic; /* Packet header magic number */
+} pkt_head_t;
+
+/* Application global data */
+static test_globals_t *gbl_args;
+
+/*
+ * Generate a single test packet for transmission.
+ */
+static odp_packet_t pktio_create_packet(uint32_t seq)
+{
+ odp_packet_t pkt;
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ odph_udphdr_t *udp;
+ char *buf;
+ uint32_t offset;
+ pkt_head_t pkt_hdr;
+ size_t payload_len;
+
+ payload_len = sizeof(pkt_hdr) + gbl_args->args.pkt_len;
+
+ pkt = odp_packet_alloc(gbl_args->transmit_pkt_pool,
+ payload_len + ODPH_UDPHDR_LEN +
+ ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN);
+
+ if (pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ buf = odp_packet_data(pkt);
+
+ /* Ethernet */
+ offset = 0;
+ odp_packet_l2_offset_set(pkt, offset);
+ eth = (odph_ethhdr_t *)buf;
+ memcpy(eth->src.addr, gbl_args->src_mac, ODPH_ETHADDR_LEN);
+ memcpy(eth->dst.addr, gbl_args->dst_mac, ODPH_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ /* IP */
+ offset += ODPH_ETHHDR_LEN;
+ odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
+ ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
+ ip->dst_addr = odp_cpu_to_be_32(0);
+ ip->src_addr = odp_cpu_to_be_32(0);
+ ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN +
+ ODPH_IPV4HDR_LEN);
+ ip->ttl = 128;
+ ip->proto = ODPH_IPPROTO_UDP;
+ ip->id = odp_cpu_to_be_16(seq);
+ ip->chksum = 0;
+ odph_ipv4_csum_update(pkt);
+
+ /* UDP */
+ offset += ODPH_IPV4HDR_LEN;
+ odp_packet_l4_offset_set(pkt, offset);
+ udp = (odph_udphdr_t *)(buf + offset);
+ udp->src_port = odp_cpu_to_be_16(0);
+ udp->dst_port = odp_cpu_to_be_16(0);
+ udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
+ udp->chksum = 0;
+
+ /* payload */
+ offset += ODPH_UDPHDR_LEN;
+ pkt_hdr.magic = TEST_HDR_MAGIC;
+ if (odp_packet_copy_from_mem(pkt, offset, sizeof(pkt_hdr),
+ &pkt_hdr) != 0)
+ ODPH_ABORT("Failed to generate test packet.\n");
+
+ return pkt;
+}
+
+/*
+ * Check if a packet payload contains test payload magic number.
+ */
+static int pktio_pkt_has_magic(odp_packet_t pkt)
+{
+ size_t l4_off;
+ pkt_head_t pkt_hdr;
+
+ l4_off = ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN;
+ int ret = odp_packet_copy_to_mem(pkt,
+ l4_off + ODPH_UDPHDR_LEN,
+ sizeof(pkt_hdr), &pkt_hdr);
+
+ if (ret != 0)
+ return 0;
+
+ if (pkt_hdr.magic == TEST_HDR_MAGIC)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Allocate packets for transmission.
+ */
+static int alloc_packets(odp_packet_t *pkt_tbl, int num_pkts)
+{
+ int n;
+ uint16_t seq;
+
+ seq = odp_atomic_fetch_add_u32(&gbl_args->ip_seq, num_pkts);
+ for (n = 0; n < num_pkts; ++n) {
+ pkt_tbl[n] = pktio_create_packet(seq + n);
+ if (pkt_tbl[n] == ODP_PACKET_INVALID)
+ break;
+ }
+
+ return n;
+}
+
+static int send_packets(odp_pktout_queue_t pktout,
+ odp_packet_t *pkt_tbl, unsigned pkts)
+{
+ unsigned tx_drops;
+ unsigned sent = 0;
+
+ if (pkts == 0)
+ return 0;
+
+ while (sent < pkts) {
+ int ret;
+
+ ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
+
+ if (odp_likely(ret > 0))
+ sent += ret;
+ else
+ break;
+ }
+
+ tx_drops = pkts - sent;
+
+ if (odp_unlikely(tx_drops))
+ odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
+
+ return sent;
+}
+
+/*
+ * Main packet transmit routine. Transmit packets at a fixed rate for
+ * specified length of time.
+ */
+static int run_thread_tx(void *arg)
+{
+ test_globals_t *globals;
+ int thr_id;
+ odp_pktout_queue_t pktout;
+ pkt_tx_stats_t *stats;
+ odp_time_t cur_time, send_time_end, send_duration;
+ odp_time_t burst_gap_end, burst_gap;
+ uint32_t batch_len;
+ int unsent_pkts = 0;
+ odp_packet_t tx_packet[BATCH_LEN_MAX];
+ odp_time_t idle_start = ODP_TIME_NULL;
+
+ thread_args_t *targs = arg;
+
+ batch_len = targs->batch_len;
+
+ if (batch_len > BATCH_LEN_MAX)
+ batch_len = BATCH_LEN_MAX;
+
+ thr_id = odp_thread_id();
+
+ globals = odp_shm_addr(odp_shm_lookup("test_globals"));
+ stats = &globals->tx_stats[thr_id];
+
+ if (odp_pktout_queue(globals->pktio_tx, &pktout, 1) != 1)
+ ODPH_ABORT("Failed to get output queue for thread %d\n",
+ thr_id);
+
+ burst_gap = odp_time_local_from_ns(
+ ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len));
+ send_duration =
+ odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS / T_SCALE);
+
+ odp_barrier_wait(&globals->tx_barrier);
+
+ cur_time = odp_time_local();
+ send_time_end = odp_time_sum(cur_time, send_duration);
+ burst_gap_end = cur_time;
+ while (odp_time_cmp(send_time_end, cur_time) > 0) {
+ unsigned alloc_cnt = 0, tx_cnt;
+
+ if (odp_time_cmp(burst_gap_end, cur_time) > 0) {
+ cur_time = odp_time_local();
+ if (!odp_time_cmp(idle_start, ODP_TIME_NULL))
+ idle_start = cur_time;
+ continue;
+ }
+
+ if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) {
+ odp_time_t diff = odp_time_diff(cur_time, idle_start);
+
+ stats->idle_ticks =
+ odp_time_sum(diff, stats->idle_ticks);
+
+ idle_start = ODP_TIME_NULL;
+ }
+
+ burst_gap_end = odp_time_sum(burst_gap_end, burst_gap);
+
+ alloc_cnt = alloc_packets(tx_packet, batch_len - unsent_pkts);
+ if (alloc_cnt != batch_len)
+ stats->alloc_failures++;
+
+ tx_cnt = send_packets(pktout, tx_packet, alloc_cnt);
+ unsent_pkts = alloc_cnt - tx_cnt;
+ stats->enq_failures += unsent_pkts;
+ stats->tx_cnt += tx_cnt;
+
+ cur_time = odp_time_local();
+ }
+
+ if (gbl_args->args.verbose)
+ printf(" %02d: TxPkts %-8" PRIu64 " EnqFail %-6" PRIu64
+ " AllocFail %-6" PRIu64 " Idle %" PRIu64 "ms\n",
+ thr_id, stats->tx_cnt, stats->enq_failures,
+ stats->alloc_failures,
+ odp_time_to_ns(stats->idle_ticks) /
+ (uint64_t)ODP_TIME_MSEC_IN_NS);
+
+ return 0;
+}
+
+static int receive_packets(odp_queue_t queue,
+ odp_event_t *event_tbl, unsigned num_pkts)
+{
+ int n_ev = 0;
+
+ if (num_pkts == 0)
+ return 0;
+
+ if (queue != ODP_QUEUE_INVALID) {
+ if (num_pkts == 1) {
+ event_tbl[0] = odp_queue_deq(queue);
+ n_ev = event_tbl[0] != ODP_EVENT_INVALID;
+ } else {
+ n_ev = odp_queue_deq_multi(queue, event_tbl, num_pkts);
+ }
+ } else {
+ if (num_pkts == 1) {
+ event_tbl[0] = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ n_ev = event_tbl[0] != ODP_EVENT_INVALID;
+ } else {
+ n_ev = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
+ event_tbl, num_pkts);
+ }
+ }
+ return n_ev;
+}
+
+static int run_thread_rx(void *arg)
+{
+ test_globals_t *globals;
+ int thr_id, batch_len;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_packet_t pkt;
+
+ thread_args_t *targs = arg;
+
+ batch_len = targs->batch_len;
+
+ if (batch_len > BATCH_LEN_MAX)
+ batch_len = BATCH_LEN_MAX;
+
+ thr_id = odp_thread_id();
+
+ globals = odp_shm_addr(odp_shm_lookup("test_globals"));
+
+ pkt_rx_stats_t *stats = &globals->rx_stats[thr_id];
+
+ if (gbl_args->args.schedule == 0) {
+ if (odp_pktin_event_queue(globals->pktio_rx, &queue, 1) != 1)
+ ODPH_ABORT("No input queue.\n");
+ }
+
+ odp_barrier_wait(&globals->rx_barrier);
+ while (1) {
+ odp_event_t ev[BATCH_LEN_MAX];
+ int i, n_ev;
+
+ n_ev = receive_packets(queue, ev, batch_len);
+
+ for (i = 0; i < n_ev; ++i) {
+ if (odp_event_type(ev[i]) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev[i]);
+ if (pktio_pkt_has_magic(pkt))
+ stats->rx_cnt++;
+ else
+ stats->rx_ignore++;
+ }
+ odp_event_free(ev[i]);
+ }
+ if (n_ev == 0 && odp_atomic_load_u32(&gbl_args->shutdown))
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Process the results from a single fixed rate test run to determine whether
+ * it passed or failed. Pass criteria are that the requested transmit packet
+ * rate was achieved and that all of the transmitted packets were received.
+ */
+static int process_results(uint64_t expected_tx_cnt,
+ test_status_t *status)
+{
+ int fail = 0;
+ uint64_t drops = 0;
+ uint64_t rx_pkts = 0;
+ uint64_t tx_pkts = 0;
+ uint64_t attempted_pps;
+ int i;
+ char str[512];
+ int len = 0;
+
+ for (i = 0; i < odp_thread_count_max(); ++i) {
+ rx_pkts += gbl_args->rx_stats[i].rx_cnt;
+ tx_pkts += gbl_args->tx_stats[i].tx_cnt;
+ }
+
+ if (rx_pkts == 0) {
+ ODPH_ERR("no packets received\n");
+ return -1;
+ }
+
+ if (tx_pkts < (expected_tx_cnt - (expected_tx_cnt / 100))) {
+ /* failed to transmit packets at (99% of) requested rate */
+ fail = 1;
+ } else if (tx_pkts > rx_pkts) {
+ /* failed to receive all of the transmitted packets */
+ fail = 1;
+ drops = tx_pkts - rx_pkts;
+ }
+
+ attempted_pps = status->pps_curr;
+
+ len += snprintf(&str[len], sizeof(str) - 1 - len,
+ "PPS: %-8" PRIu64 " ", attempted_pps);
+ len += snprintf(&str[len], sizeof(str) - 1 - len,
+ "Succeeded: %-4s ", fail ? "No" : "Yes");
+ len += snprintf(&str[len], sizeof(str) - 1 - len,
+ "TxPkts: %-8" PRIu64 " ", tx_pkts);
+ len += snprintf(&str[len], sizeof(str) - 1 - len,
+ "RxPkts: %-8" PRIu64 " ", rx_pkts);
+ len += snprintf(&str[len], sizeof(str) - 1 - len,
+ "DropPkts: %-8" PRIu64 " ", drops);
+ printf("%s\n", str);
+
+ if (gbl_args->args.search == 0) {
+ printf("Result: %s\n", fail ? "FAILED" : "PASSED");
+ return fail ? -1 : 0;
+ }
+
+ if (fail && (status->pps_fail == 0 ||
+ attempted_pps < status->pps_fail)) {
+ status->pps_fail = attempted_pps;
+ } else if (attempted_pps > status->pps_pass) {
+ status->pps_pass = attempted_pps;
+ }
+
+ if (status->pps_fail == 0) {
+ /* ramping up, double the previously attempted pps */
+ status->pps_curr *= 2;
+ } else {
+ /* set the new target to half way between the upper and lower
+ * limits */
+ status->pps_curr = status->pps_pass +
+ ((status->pps_fail - status->pps_pass) / 2);
+ }
+
+ /* stop once the pass and fail measurements are within range */
+ if ((status->pps_fail - status->pps_pass) < RATE_SEARCH_ACCURACY_PPS) {
+ unsigned pkt_len = gbl_args->args.pkt_len + PKT_HDR_LEN;
+ int mbps = (pkt_len * status->pps_pass * 8) / 1024 / 1024;
+
+ printf("Maximum packet rate: %" PRIu64 " PPS (%d Mbps)\n",
+ status->pps_pass, mbps);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx,
+ odp_cpumask_t *thd_mask_rx)
+{
+ odp_cpumask_t cpumask;
+ int num_workers, num_tx_workers, num_rx_workers;
+ int i, cpu;
+
+ num_workers =
+ odp_cpumask_default_worker(&cpumask,
+ gbl_args->args.cpu_count);
+ if (num_workers < 2) {
+ ODPH_ERR("Need at least two cores\n");
+ return TEST_SKIP;
+ }
+
+ if (num_workers > MAX_WORKERS) {
+ ODPH_DBG("Worker count limited to MAX_WORKERS define (=%d)\n",
+ MAX_WORKERS);
+ num_workers = MAX_WORKERS;
+ }
+
+ if (gbl_args->args.num_tx_workers) {
+ if (gbl_args->args.num_tx_workers > (num_workers - 1)) {
+ ODPH_ERR("Invalid TX worker count\n");
+ return -1;
+ }
+ num_tx_workers = gbl_args->args.num_tx_workers;
+ } else {
+ /* default is to split the available cores evenly into TX and
+ * RX workers, favour TX for odd core count */
+ num_tx_workers = (num_workers + 1) / 2;
+ }
+
+ odp_cpumask_zero(thd_mask_tx);
+ odp_cpumask_zero(thd_mask_rx);
+
+ cpu = odp_cpumask_first(&cpumask);
+ for (i = 0; i < num_workers; ++i) {
+ if (i < num_tx_workers)
+ odp_cpumask_set(thd_mask_tx, cpu);
+ else
+ odp_cpumask_set(thd_mask_rx, cpu);
+ cpu = odp_cpumask_next(&cpumask, cpu);
+ }
+
+ num_rx_workers = odp_cpumask_count(thd_mask_rx);
+
+ odp_barrier_init(&gbl_args->rx_barrier, num_rx_workers + 1);
+ odp_barrier_init(&gbl_args->tx_barrier, num_tx_workers + 1);
+
+ return 0;
+}
+
+/*
+ * Run a single instance of the throughput test. When attempting to determine
+ * the maximum packet rate this will be invoked multiple times with the only
+ * difference between runs being the target PPS rate.
+ */
+static int run_test_single(odp_cpumask_t *thd_mask_tx,
+ odp_cpumask_t *thd_mask_rx,
+ test_status_t *status)
+{
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ thread_args_t args_tx, args_rx;
+ uint64_t expected_tx_cnt;
+ int num_tx_workers, num_rx_workers;
+
+ odp_atomic_store_u32(&gbl_args->shutdown, 0);
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
+ memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
+
+ expected_tx_cnt = status->pps_curr * gbl_args->args.duration / T_SCALE;
+
+ /* start receiver threads first */
+
+ num_rx_workers = odp_cpumask_count(thd_mask_rx);
+ args_rx.batch_len = gbl_args->args.rx_batch_len;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = gbl_args->instance;
+ thr_common.cpumask = thd_mask_rx;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_thread_rx;
+ thr_param.arg = &args_rx;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_rx_workers);
+ odp_barrier_wait(&gbl_args->rx_barrier);
+
+ /* then start transmitters */
+
+ num_tx_workers = odp_cpumask_count(thd_mask_tx);
+ args_tx.pps = status->pps_curr / num_tx_workers;
+ args_tx.duration = gbl_args->args.duration;
+ args_tx.batch_len = gbl_args->args.tx_batch_len;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = gbl_args->instance;
+ thr_common.cpumask = thd_mask_tx;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_thread_tx;
+ thr_param.arg = &args_tx;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(&thread_tbl[num_rx_workers], &thr_common, &thr_param, num_tx_workers);
+ odp_barrier_wait(&gbl_args->tx_barrier);
+
+ /* wait for transmitter threads to terminate */
+ odph_thread_join(&thread_tbl[num_rx_workers], num_tx_workers);
+
+ /* delay to allow transmitted packets to reach the receivers */
+ odp_time_wait_ns(SHUTDOWN_DELAY_NS);
+
+ /* indicate to the receivers to exit */
+ odp_atomic_store_u32(&gbl_args->shutdown, 1);
+
+ /* wait for receivers */
+ odph_thread_join(thread_tbl, num_rx_workers);
+
+ if (!status->warmup)
+ return process_results(expected_tx_cnt, status);
+
+ return 1;
+}
+
+static int run_test(void)
+{
+ int ret;
+ int i;
+ odp_cpumask_t txmask, rxmask;
+ test_status_t status = {
+ .pps_curr = gbl_args->args.pps,
+ .pps_pass = 0,
+ .pps_fail = 0,
+ .warmup = 1,
+ };
+
+ ret = setup_txrx_masks(&txmask, &rxmask);
+ if (ret)
+ return ret;
+
+ printf("Starting test with params:\n");
+ printf("\tTransmit workers: \t%d\n", odp_cpumask_count(&txmask));
+ printf("\tReceive workers: \t%d\n", odp_cpumask_count(&rxmask));
+ printf("\tDuration (seconds): \t%d\n", gbl_args->args.duration);
+ printf("\tTransmit batch length:\t%" PRIu32 "\n",
+ gbl_args->args.tx_batch_len);
+ printf("\tReceive batch length: \t%" PRIu32 "\n",
+ gbl_args->args.rx_batch_len);
+ printf("\tPacket receive method:\t%s\n",
+ gbl_args->args.schedule ? "schedule" : "plain");
+ printf("\tInterface(s): \t");
+ for (i = 0; i < gbl_args->args.num_ifaces; ++i)
+ printf("%s ", gbl_args->args.ifaces[i]);
+ printf("\n");
+
+ /* first time just run the test but throw away the results */
+ run_test_single(&txmask, &rxmask, &status);
+ status.warmup = 0;
+
+ while (1) {
+ ret = run_test_single(&txmask, &rxmask, &status);
+ if (ret <= 0)
+ break;
+ }
+
+ return ret;
+}
+
+static odp_pktio_t create_pktio(const char *iface, int schedule)
+{
+ odp_pool_t pool;
+ odp_pktio_t pktio;
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_param_t params;
+ odp_pktio_param_t pktio_param;
+
+ odp_pool_param_init(&params);
+ params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
+ params.pkt.seg_len = params.pkt.len;
+ params.pkt.num = PKT_BUF_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s", iface);
+ pool = odp_pool_create(pool_name, &params);
+ if (pool == ODP_POOL_INVALID)
+ return ODP_PKTIO_INVALID;
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (schedule)
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ else
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+
+ pktio = odp_pktio_open(iface, pool, &pktio_param);
+
+ return pktio;
+}
+
+static int test_init(void)
+{
+ odp_pool_param_t params;
+ const char *iface;
+ int schedule;
+ odp_pktio_config_t cfg;
+
+ odp_pool_param_init(&params);
+ params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
+ params.pkt.seg_len = params.pkt.len;
+ params.pkt.num = PKT_BUF_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ gbl_args->transmit_pkt_pool = odp_pool_create("pkt_pool_transmit",
+ &params);
+ if (gbl_args->transmit_pkt_pool == ODP_POOL_INVALID)
+ ODPH_ABORT("Failed to create transmit pool\n");
+
+ odp_atomic_init_u32(&gbl_args->ip_seq, 0);
+ odp_atomic_init_u32(&gbl_args->shutdown, 0);
+
+ iface = gbl_args->args.ifaces[0];
+ schedule = gbl_args->args.schedule;
+
+ if (schedule)
+ odp_schedule_config(NULL);
+
+ /* create pktios and associate input/output queues */
+ gbl_args->pktio_tx = create_pktio(iface, schedule);
+ if (gbl_args->args.num_ifaces > 1) {
+ iface = gbl_args->args.ifaces[1];
+ gbl_args->pktio_rx = create_pktio(iface, schedule);
+ } else {
+ gbl_args->pktio_rx = gbl_args->pktio_tx;
+ }
+
+ odp_pktio_mac_addr(gbl_args->pktio_tx, gbl_args->src_mac,
+ ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(gbl_args->pktio_rx, gbl_args->dst_mac,
+ ODPH_ETHADDR_LEN);
+
+ if (gbl_args->pktio_rx == ODP_PKTIO_INVALID ||
+ gbl_args->pktio_tx == ODP_PKTIO_INVALID) {
+ ODPH_ERR("failed to open pktio\n");
+ return -1;
+ }
+
+ /* Create single queue with default parameters */
+ if (odp_pktout_queue_config(gbl_args->pktio_tx, NULL)) {
+ ODPH_ERR("failed to configure pktio_tx queue\n");
+ return -1;
+ }
+
+ /* Configure also input side (with defaults) */
+ if (odp_pktin_queue_config(gbl_args->pktio_tx, NULL)) {
+ ODPH_ERR("failed to configure pktio_tx queue\n");
+ return -1;
+ }
+
+ /* Disable packet parsing as this is done in the driver where it
+ * affects scalability.
+ */
+ odp_pktio_config_init(&cfg);
+ cfg.parser.layer = ODP_PROTO_LAYER_NONE;
+ odp_pktio_config(gbl_args->pktio_rx, &cfg);
+
+ if (gbl_args->args.num_ifaces > 1) {
+ if (odp_pktout_queue_config(gbl_args->pktio_rx, NULL)) {
+ ODPH_ERR("failed to configure pktio_rx queue\n");
+ return -1;
+ }
+
+ if (odp_pktin_queue_config(gbl_args->pktio_rx, NULL)) {
+ ODPH_ERR("failed to configure pktio_rx queue\n");
+ return -1;
+ }
+ }
+
+ if (odp_pktio_start(gbl_args->pktio_tx) != 0)
+ return -1;
+ if (gbl_args->args.num_ifaces > 1 &&
+ odp_pktio_start(gbl_args->pktio_rx))
+ return -1;
+
+ return 0;
+}
+
+static int empty_inq(odp_pktio_t pktio)
+{
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_event_t ev;
+ odp_queue_type_t q_type;
+
+ if (odp_pktin_event_queue(pktio, &queue, 1) != 1)
+ return -1;
+
+ q_type = odp_queue_type(queue);
+
+ /* flush any pending events */
+ while (1) {
+ if (q_type == ODP_QUEUE_TYPE_PLAIN)
+ ev = odp_queue_deq(queue);
+ else
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ break;
+ }
+
+ return 0;
+}
+
+static int test_term(void)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_t pool;
+ int i;
+ int ret = 0;
+
+ if (gbl_args->pktio_tx != gbl_args->pktio_rx) {
+ if (odp_pktio_stop(gbl_args->pktio_tx)) {
+ ODPH_ERR("Failed to stop pktio_tx\n");
+ return -1;
+ }
+
+ if (odp_pktio_close(gbl_args->pktio_tx)) {
+ ODPH_ERR("Failed to close pktio_tx\n");
+ ret = -1;
+ }
+ }
+
+ empty_inq(gbl_args->pktio_rx);
+
+ if (odp_pktio_stop(gbl_args->pktio_rx)) {
+ ODPH_ERR("Failed to stop pktio_rx\n");
+ return -1;
+ }
+
+ if (odp_pktio_close(gbl_args->pktio_rx) != 0) {
+ ODPH_ERR("Failed to close pktio_rx\n");
+ ret = -1;
+ }
+
+ for (i = 0; i < gbl_args->args.num_ifaces; ++i) {
+ snprintf(pool_name, sizeof(pool_name),
+ "pkt_pool_%s", gbl_args->args.ifaces[i]);
+ pool = odp_pool_lookup(pool_name);
+ if (pool == ODP_POOL_INVALID)
+ continue;
+
+ if (odp_pool_destroy(pool) != 0) {
+ ODPH_ERR("Failed to destroy pool %s\n", pool_name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(gbl_args->transmit_pkt_pool) != 0) {
+ ODPH_ERR("Failed to destroy transmit pool\n");
+ ret = -1;
+ }
+
+ free(gbl_args->args.if_str);
+
+ if (odp_shm_free(odp_shm_lookup("test_globals")) != 0) {
+ ODPH_ERR("Failed to free test_globals\n");
+ ret = -1;
+ }
+ if (odp_shm_free(odp_shm_lookup("test_globals.rx_stats")) != 0) {
+ ODPH_ERR("Failed to free test_globals.rx_stats\n");
+ ret = -1;
+ }
+ if (odp_shm_free(odp_shm_lookup("test_globals.tx_stats")) != 0) {
+ ODPH_ERR("Failed to free test_globals.tx_stats\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void usage(void)
+{
+ printf("\nUsage: odp_pktio_perf [options]\n\n");
+ printf(" -c, --count <number> CPU count, 0=all available, default=2\n");
+ printf(" -t, --txcount <number> Number of CPUs to use for TX\n");
+ printf(" default: cpu_count+1/2\n");
+ printf(" -b, --txbatch <length> Number of packets per TX batch\n");
+ printf(" default: %d\n", BATCH_LEN_MAX);
+ printf(" -p, --plain Plain input queue for packet RX\n");
+ printf(" default: disabled (use scheduler)\n");
+ printf(" -R, --rxbatch <length> Number of packets per RX batch\n");
+ printf(" default: %d\n", BATCH_LEN_MAX);
+ printf(" -l, --length <length> Additional payload length in bytes\n");
+ printf(" default: 0\n");
+ printf(" -r, --rate <number> Attempted packet rate in PPS\n");
+ printf(" -i, --interface <list> List of interface names to use\n");
+ printf(" -d, --duration <secs> Duration of each test iteration\n");
+ printf(" -v, --verbose Print verbose information\n");
+ printf(" -h, --help This help\n");
+ printf("\n");
+}
+
+static void parse_args(int argc, char *argv[], test_args_t *args)
+{
+ int opt;
+ int long_index;
+
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"txcount", required_argument, NULL, 't'},
+ {"txbatch", required_argument, NULL, 'b'},
+ {"plain", no_argument, NULL, 'p'},
+ {"rxbatch", required_argument, NULL, 'R'},
+ {"length", required_argument, NULL, 'l'},
+ {"rate", required_argument, NULL, 'r'},
+ {"interface", required_argument, NULL, 'i'},
+ {"duration", required_argument, NULL, 'd'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:t:b:pR:l:r:i:d:vh";
+
+ args->cpu_count = 2;
+ args->num_tx_workers = 0; /* defaults to cpu_count+1/2 */
+ args->tx_batch_len = BATCH_LEN_MAX;
+ args->rx_batch_len = BATCH_LEN_MAX;
+ args->duration = DEFAULT_DURATION;
+ args->pps = RATE_SEARCH_INITIAL_PPS;
+ args->search = 1;
+ args->schedule = 1;
+ args->verbose = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts,
+ longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ case 'c':
+ args->cpu_count = atoi(optarg);
+ break;
+ case 't':
+ args->num_tx_workers = atoi(optarg);
+ break;
+ case 'd':
+ args->duration = atoi(optarg) * T_SCALE;
+ break;
+ case 'r':
+ args->pps = atoi(optarg);
+ args->search = 0;
+ args->verbose = 1;
+ break;
+ case 'i':
+ {
+ char *token;
+
+ args->if_str = malloc(strlen(optarg) + 1);
+
+ if (!args->if_str)
+ ODPH_ABORT("Failed to alloc iface storage\n");
+
+ strcpy(args->if_str, optarg);
+ for (token = strtok(args->if_str, ",");
+ token != NULL && args->num_ifaces < MAX_NUM_IFACES;
+ token = strtok(NULL, ","))
+ args->ifaces[args->num_ifaces++] = token;
+ }
+ break;
+ case 'p':
+ args->schedule = 0;
+ break;
+ case 'b':
+ args->tx_batch_len = atoi(optarg);
+ break;
+ case 'R':
+ args->rx_batch_len = atoi(optarg);
+ break;
+ case 'v':
+ args->verbose = 1;
+ break;
+ case 'l':
+ args->pkt_len = atoi(optarg);
+ break;
+ }
+ }
+
+ if (args->num_ifaces == 0) {
+ args->ifaces[0] = "loop";
+ args->num_ifaces = 1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ odp_shm_t shm;
+ int max_thrs;
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init_param;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (odp_init_global(&instance, &init_param, NULL) != 0)
+ ODPH_ABORT("Failed global init.\n");
+
+ if (odp_init_local(instance, ODP_THREAD_CONTROL) != 0)
+ ODPH_ABORT("Failed local init.\n");
+
+ odp_sys_info_print();
+
+ shm = odp_shm_reserve("test_globals",
+ sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+
+ gbl_args = odp_shm_addr(shm);
+ if (gbl_args == NULL)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+ memset(gbl_args, 0, sizeof(test_globals_t));
+
+ max_thrs = odp_thread_count_max();
+
+ gbl_args->instance = instance;
+ gbl_args->rx_stats_size = max_thrs * sizeof(pkt_rx_stats_t);
+ gbl_args->tx_stats_size = max_thrs * sizeof(pkt_tx_stats_t);
+
+ shm = odp_shm_reserve("test_globals.rx_stats",
+ gbl_args->rx_stats_size,
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+
+ gbl_args->rx_stats = odp_shm_addr(shm);
+
+ if (gbl_args->rx_stats == NULL)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+
+ memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
+
+ shm = odp_shm_reserve("test_globals.tx_stats",
+ gbl_args->tx_stats_size,
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+
+ gbl_args->tx_stats = odp_shm_addr(shm);
+
+ if (gbl_args->tx_stats == NULL)
+ ODPH_ABORT("Shared memory reserve failed.\n");
+
+ memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
+
+ parse_args(argc, argv, &gbl_args->args);
+
+ ret = test_init();
+
+ if (ret == 0) {
+ ret = run_test();
+ test_term();
+ }
+
+ odp_term_local();
+ odp_term_global(instance);
+
+ return ret;
+}
diff --git a/test/performance/odp_pool_latency.c b/test/performance/odp_pool_latency.c
new file mode 100644
index 000000000..0afe2f317
--- /dev/null
+++ b/test/performance/odp_pool_latency.c
@@ -0,0 +1,1407 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+/**
+ * @example odp_pool_latency.c
+ *
+ * Pool latency tester. Allocate from different kind of pools with a varying set of configurations
+ * and record latencies.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define PROG_NAME "odp_pool_latency"
+#define DELIMITER ","
+#define ALLOC '+'
+#define FREE '-'
+#define TOP 't'
+#define BOTTOM 'b'
+#define DELAY 'd'
+
+enum {
+ BUFFER = 0U,
+ PACKET,
+ TMO,
+ VECTOR
+};
+
+enum {
+ SINGLE = 0U,
+ MANY
+};
+
+#define DEF_ALLOC 1U
+#define DEF_FREE 1U
+#define DEF_DIR TOP
+#define DEF_TYPE BUFFER
+#define DEF_CNT 32768U
+#define DEF_SIZE 1024U
+#define DEF_POLICY MANY
+#define DEF_ROUNDS 100000U
+#define DEF_IGNORE 0U
+#define DEF_WORKERS 1U
+#define DEF_UA_SIZE 0U
+
+#define MAX_PATTERN_LEN 32U
+#define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
+#define MAX_RETRIES 10U
+
+#define COND_MIN(a, b) ((a) > 0U ? ODPH_MIN((a), (b)) : (b))
+#define UA_DATA 0xAA
+
+ODP_STATIC_ASSERT(MAX_PATTERN_LEN < UINT8_MAX, "Too long pattern length");
+
+typedef struct {
+ uint32_t num_evs_buf;
+ uint32_t num_evs_pkt;
+ uint32_t num_evs_tmo;
+ uint32_t num_evs_vec;
+ uint32_t data_size_buf;
+ uint32_t data_size_pkt;
+ uint32_t data_size_vec;
+ uint32_t cache_size_buf;
+ uint32_t cache_size_pkt;
+ uint32_t cache_size_tmo;
+ uint32_t cache_size_vec;
+} dynamic_defs_t;
+
+typedef enum {
+ PRS_OK,
+ PRS_NOK,
+ PRS_TERM
+} parse_result_t;
+
+typedef struct {
+ uint64_t tot_tm;
+ uint64_t alloc_tm;
+ uint64_t max_alloc_tm;
+ uint64_t min_alloc_tm;
+ uint64_t max_alloc_rnd;
+ uint64_t min_alloc_rnd;
+ uint64_t alloc_cnt;
+ uint64_t alloc_b_cnt;
+ uint64_t uarea_tm;
+ uint64_t max_uarea_tm;
+ uint64_t min_uarea_tm;
+ uint64_t max_uarea_rnd;
+ uint64_t min_uarea_rnd;
+ uint64_t free_tm;
+ uint64_t max_free_tm;
+ uint64_t min_free_tm;
+ uint64_t max_free_rnd;
+ uint64_t min_free_rnd;
+ uint64_t free_b_cnt;
+ uint64_t reallocs;
+ uint64_t alloc_errs;
+ uint64_t pattern_errs;
+ uint64_t act_num_rounds;
+ uint8_t max_alloc_pt;
+ uint8_t min_alloc_pt;
+ uint8_t max_uarea_pt;
+ uint8_t min_uarea_pt;
+ uint8_t max_free_pt;
+ uint8_t min_free_pt;
+} stats_t;
+
+typedef struct {
+ uint32_t val;
+ uint8_t op;
+ uint8_t opt;
+} alloc_elem_t;
+
+typedef struct prog_config_s prog_config_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ stats_t stats;
+ odp_pool_t pool;
+ void *data;
+ prog_config_t *prog_config;
+ odp_shm_t shm;
+ uint32_t data_size;
+ uint32_t uarea_size;
+} worker_config_t;
+
+typedef uint32_t (*alloc_fn_t)(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved);
+typedef void (*free_fn_t)(void *data, uint32_t idx, uint32_t num, stats_t *stats,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved);
+
+typedef struct prog_config_s {
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ worker_config_t worker_config[MAX_WORKERS];
+ alloc_elem_t alloc_elems[MAX_PATTERN_LEN];
+ dynamic_defs_t dyn_defs;
+ odp_instance_t odp_instance;
+ odp_cpumask_t worker_mask;
+ odp_barrier_t init_barrier;
+ odp_barrier_t term_barrier;
+ alloc_fn_t alloc_fn;
+ free_fn_t free_fn;
+ int64_t cache_size;
+ uint64_t num_rounds;
+ uint64_t num_ignore;
+ odp_atomic_u32_t is_running;
+ uint32_t num_data_elems;
+ uint32_t seg_len;
+ uint32_t handle_size;
+ uint32_t num_evs;
+ uint32_t data_size;
+ uint32_t num_workers;
+ uint32_t uarea_size;
+ uint8_t num_elems;
+ uint8_t type;
+ uint8_t policy;
+} prog_config_t;
+
+static prog_config_t *prog_conf;
+
+static void terminate(int signal ODP_UNUSED)
+{
+ odp_atomic_store_u32(&prog_conf->is_running, 0U);
+}
+
+static void init_config(prog_config_t *config)
+{
+ alloc_elem_t *alloc_elem;
+ odp_pool_capability_t capa;
+ odp_pool_param_t param;
+ worker_config_t *worker;
+
+ memset(config, 0, sizeof(*config));
+ alloc_elem = &config->alloc_elems[0];
+ alloc_elem->val = DEF_ALLOC;
+ alloc_elem->op = ALLOC;
+ alloc_elem = &config->alloc_elems[1];
+ alloc_elem->val = DEF_FREE;
+ alloc_elem->op = FREE;
+ alloc_elem->opt = DEF_DIR;
+ config->num_elems = 2U;
+
+ if (odp_pool_capability(&capa) == 0) {
+ config->dyn_defs.num_evs_buf = COND_MIN(capa.buf.max_num, DEF_CNT);
+ config->dyn_defs.num_evs_pkt = COND_MIN(capa.pkt.max_num, DEF_CNT);
+ config->dyn_defs.num_evs_tmo = COND_MIN(capa.tmo.max_num, DEF_CNT);
+ config->dyn_defs.num_evs_vec = COND_MIN(capa.vector.max_num, DEF_CNT);
+ config->dyn_defs.data_size_buf = COND_MIN(capa.buf.max_size, DEF_SIZE);
+ config->dyn_defs.data_size_pkt = COND_MIN(capa.pkt.max_len, DEF_SIZE);
+ config->dyn_defs.data_size_vec = COND_MIN(capa.vector.max_size, DEF_SIZE);
+ odp_pool_param_init(&param);
+ config->dyn_defs.cache_size_buf = param.buf.cache_size;
+ config->dyn_defs.cache_size_pkt = param.pkt.cache_size;
+ config->dyn_defs.cache_size_tmo = param.tmo.cache_size;
+ config->dyn_defs.cache_size_vec = param.vector.cache_size;
+ }
+
+ config->cache_size = -1;
+ config->num_rounds = DEF_ROUNDS;
+ config->num_ignore = DEF_IGNORE;
+ config->num_workers = DEF_WORKERS;
+ config->uarea_size = DEF_UA_SIZE;
+ config->type = DEF_TYPE;
+ config->policy = DEF_POLICY;
+
+ for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
+ worker = &config->worker_config[i];
+ worker->stats.min_alloc_tm = UINT64_MAX;
+ worker->stats.min_uarea_tm = UINT64_MAX;
+ worker->stats.min_free_tm = UINT64_MAX;
+ worker->pool = ODP_POOL_INVALID;
+ worker->shm = ODP_SHM_INVALID;
+ }
+}
+
+static void parse_burst_pattern(prog_config_t *config, const char *optarg)
+{
+ char *tmp_str = strdup(optarg), *tmp, op, opt;
+ uint8_t num_elems = 0U;
+ alloc_elem_t *elem;
+ uint32_t val;
+ int ret;
+
+ if (tmp_str == NULL)
+ return;
+
+ tmp = strtok(tmp_str, DELIMITER);
+
+ while (tmp && num_elems < MAX_PATTERN_LEN) {
+ elem = &config->alloc_elems[num_elems];
+ ret = sscanf(tmp, "%c%u%c", &op, &val, &opt);
+
+ if (ret == 2 || ret == 3) {
+ if (op == ALLOC || (op == FREE && (opt == TOP || opt == BOTTOM)) ||
+ op == DELAY) {
+ if (op == FREE)
+ elem->opt = opt;
+
+ elem->val = val;
+ elem->op = op;
+ ++num_elems;
+ }
+ }
+
+ tmp = strtok(NULL, DELIMITER);
+ }
+
+ free(tmp_str);
+ config->num_elems = num_elems;
+}
+
+static void print_usage(const dynamic_defs_t *dyn_defs)
+{
+ printf("\n"
+ "Pool latency tester. Allocate from different kind of pools with a varying set of\n"
+ "configurations and record latencies.\n"
+ "\n"
+ "Usage: " PROG_NAME " [OPTIONS]\n");
+ printf("\n"
+ " E.g. " PROG_NAME "\n"
+ " " PROG_NAME " -b %c7" DELIMITER "%c1%c" DELIMITER "%c3" DELIMITER "%c9%c\n",
+ ALLOC, FREE, TOP, ALLOC, FREE, BOTTOM);
+ printf(" " PROG_NAME " -b %c10" DELIMITER "%c1000" DELIMITER "%c10%c -t 1 -d 2048 "
+ "-p 0 -w 64\n", ALLOC, DELAY, FREE, TOP);
+ printf("\n"
+ "Optional OPTIONS:\n"
+ "\n"
+ " -b, --burst_pattern Burst pattern for allocations, frees and delays per round,\n"
+ " delimited by '%s', no spaces. Allocations are indicated\n"
+ " with a '%c' prefix, frees with a '%c' prefix. The location\n"
+ " of frees are indicated from the top of a previously\n"
+ " allocated array of events with a '%c' suffix and from the\n"
+ " bottom with a '%c' suffix. Delays are indicated with a\n"
+ " '%c' prefix, followed by a delay in nanoseconds.\n"
+ " Allocations and frees should be equal in the aggregate and\n"
+ " frees should never outnumber allocations at any instant.\n"
+ " '%c%u%s%c%u%c' by default. Maximum pattern length is %u.\n"
+ " -t, --type Pool type. %u by default.\n"
+ " 0: buffer\n"
+ " 1: packet\n"
+ " 2: timeout\n"
+ " 3: vector\n"
+ " -e, --event_count Number of events. Defaults:\n"
+ " buffer: %u\n"
+ " packet: %u\n"
+ " timeout: %u\n"
+ " vector: %u\n"
+ " -d, --data_size Data size in bytes, ignored in case of timeout pools, with\n"
+ " vector pools, defines the vector size.\n"
+ " Defaults:\n"
+ " buffer: %u\n"
+ " packet: %u\n"
+ " vector: %u\n"
+ " -p, --policy Pool allocation policy. %u by default.\n"
+ " Policies:\n"
+ " 0: One pool shared by workers\n"
+ " 1: One pool per worker\n"
+ " -r, --round_count Number of rounds to run. Use 0 to run indefinitely. %u by\n"
+ " default.\n"
+ " -i, --ignore_rounds Ignore an amount of initial rounds. %u by default.\n"
+ " -c, --worker_count Number of workers. %u by default.\n"
+ " -C, --cache_size Maximum cache size for pools. Defaults:\n"
+ " buffer: %u\n"
+ " packet: %u\n"
+ " timeout: %u\n"
+ " vector: %u\n"
+ " -w, --write_uarea Write data to allocated event user areas. 0 bytes disables\n"
+ " user area write. %u by default.\n"
+ " -h, --help This help.\n"
+ "\n", DELIMITER, ALLOC, FREE, TOP, BOTTOM, DELAY, ALLOC, DEF_ALLOC, DELIMITER, FREE,
+ DEF_FREE, DEF_DIR, MAX_PATTERN_LEN, DEF_TYPE, dyn_defs->num_evs_buf,
+ dyn_defs->num_evs_pkt, dyn_defs->num_evs_tmo, dyn_defs->num_evs_vec,
+ dyn_defs->data_size_buf, dyn_defs->data_size_pkt, dyn_defs->data_size_vec,
+ DEF_POLICY, DEF_ROUNDS, DEF_IGNORE, DEF_WORKERS, dyn_defs->cache_size_buf,
+ dyn_defs->cache_size_pkt, dyn_defs->cache_size_tmo, dyn_defs->cache_size_vec,
+ DEF_UA_SIZE);
+}
+
+static parse_result_t check_options(prog_config_t *config)
+{
+ odp_pool_capability_t pool_capa;
+ uint32_t max_workers, num_pools;
+ alloc_elem_t *elem;
+ int64_t num_tot = 0;
+ odp_shm_capability_t shm_capa;
+ uint64_t shm_size;
+
+ if (config->type != BUFFER && config->type != PACKET && config->type != TMO &&
+ config->type != VECTOR) {
+ ODPH_ERR("Invalid pool type: %u\n", config->type);
+ return PRS_NOK;
+ }
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("Error querying pool capabilities\n");
+ return PRS_NOK;
+ }
+
+ max_workers = ODPH_MIN(MAX_WORKERS, (uint32_t)odp_cpumask_default_worker(NULL, 0));
+
+ if (config->num_workers == 0U || config->num_workers > max_workers) {
+ ODPH_ERR("Invalid worker count: %u (min: 1, max: %u)\n", config->num_workers,
+ max_workers);
+ return PRS_NOK;
+ }
+
+ (void)odp_cpumask_default_worker(&config->worker_mask, config->num_workers);
+ num_pools = config->policy == SINGLE ? 1U : config->num_workers;
+
+ if (config->type == BUFFER) {
+ if (config->num_evs == 0U)
+ config->num_evs = config->dyn_defs.num_evs_buf;
+
+ if (config->data_size == 0U)
+ config->data_size = config->dyn_defs.data_size_buf;
+
+ if (config->cache_size == -1)
+ config->cache_size = config->dyn_defs.cache_size_buf;
+
+ if (config->num_evs > pool_capa.buf.max_num) {
+ ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
+ pool_capa.buf.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->data_size > pool_capa.buf.max_size) {
+ ODPH_ERR("Invalid data size: %u (max: %u)\n", config->data_size,
+ pool_capa.buf.max_size);
+ return PRS_NOK;
+ }
+
+ if (config->cache_size < pool_capa.buf.min_cache_size ||
+ config->cache_size > pool_capa.buf.max_cache_size) {
+ ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
+ config->cache_size, pool_capa.buf.min_cache_size,
+ pool_capa.buf.max_cache_size);
+ return PRS_NOK;
+ }
+
+ if (num_pools > pool_capa.buf.max_pools) {
+ ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
+ pool_capa.buf.max_pools);
+ return PRS_NOK;
+ }
+
+ config->handle_size = sizeof(odp_buffer_t);
+ config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.buf.max_uarea_size);
+ } else if (config->type == PACKET) {
+ if (config->num_evs == 0U)
+ config->num_evs = config->dyn_defs.num_evs_pkt;
+
+ if (config->data_size == 0U)
+ config->data_size = config->dyn_defs.data_size_pkt;
+
+ if (config->cache_size == -1)
+ config->cache_size = config->dyn_defs.cache_size_pkt;
+
+ if (config->num_evs > pool_capa.pkt.max_num) {
+ ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
+ pool_capa.pkt.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->data_size > pool_capa.pkt.max_len) {
+ ODPH_ERR("Invalid data size: %u (max: %u)\n", config->data_size,
+ pool_capa.pkt.max_len);
+ return PRS_NOK;
+ }
+
+ if (config->cache_size < pool_capa.pkt.min_cache_size ||
+ config->cache_size > pool_capa.pkt.max_cache_size) {
+ ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
+ config->cache_size, pool_capa.pkt.min_cache_size,
+ pool_capa.pkt.max_cache_size);
+ return PRS_NOK;
+ }
+
+ if (num_pools > pool_capa.pkt.max_pools) {
+ ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
+ pool_capa.pkt.max_pools);
+ return PRS_NOK;
+ }
+
+ config->seg_len = pool_capa.pkt.max_seg_len > config->data_size ?
+ config->data_size : pool_capa.pkt.max_seg_len;
+ config->handle_size = sizeof(odp_packet_t);
+ config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.pkt.max_uarea_size);
+ } else if (config->type == TMO) {
+ if (config->num_evs == 0U)
+ config->num_evs = config->dyn_defs.num_evs_tmo;
+
+ if (config->cache_size == -1)
+ config->cache_size = config->dyn_defs.cache_size_tmo;
+
+ if (config->num_evs > pool_capa.tmo.max_num) {
+ ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
+ pool_capa.tmo.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->cache_size < pool_capa.tmo.min_cache_size ||
+ config->cache_size > pool_capa.tmo.max_cache_size) {
+ ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
+ config->cache_size, pool_capa.tmo.min_cache_size,
+ pool_capa.tmo.max_cache_size);
+ return PRS_NOK;
+ }
+
+ if (num_pools > pool_capa.tmo.max_pools) {
+ ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
+ pool_capa.tmo.max_pools);
+ return PRS_NOK;
+ }
+
+ config->handle_size = sizeof(odp_timeout_t);
+ config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.tmo.max_uarea_size);
+ } else {
+ if (config->num_evs == 0U)
+ config->num_evs = config->dyn_defs.num_evs_vec;
+
+ if (config->data_size == 0U)
+ config->data_size = config->dyn_defs.data_size_vec;
+
+ if (config->cache_size == -1)
+ config->cache_size = config->dyn_defs.cache_size_vec;
+
+ if (config->num_evs > pool_capa.vector.max_num) {
+ ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
+ pool_capa.vector.max_num);
+ return PRS_NOK;
+ }
+
+ if (config->data_size > pool_capa.vector.max_size) {
+ ODPH_ERR("Invalid vector size: %u (max: %u)\n", config->data_size,
+ pool_capa.vector.max_size);
+ return PRS_NOK;
+ }
+
+ if (config->cache_size < pool_capa.vector.min_cache_size ||
+ config->cache_size > pool_capa.vector.max_cache_size) {
+ ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
+ config->cache_size, pool_capa.vector.min_cache_size,
+ pool_capa.vector.max_cache_size);
+ return PRS_NOK;
+ }
+
+ if (num_pools > pool_capa.vector.max_pools) {
+ ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
+ pool_capa.vector.max_pools);
+ return PRS_NOK;
+ }
+
+ config->handle_size = sizeof(odp_packet_vector_t);
+ config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.vector.max_uarea_size);
+ }
+
+ if (config->num_elems == 0U) {
+ ODPH_ERR("Invalid burst pattern, no elements\n");
+ return PRS_NOK;
+ }
+
+ for (uint8_t i = 0U; i < config->num_elems; ++i) {
+ elem = &config->alloc_elems[i];
+
+ if (elem->op == ALLOC)
+ num_tot += elem->val;
+ else if (elem->op == FREE)
+ num_tot -= elem->val;
+
+ if (num_tot < 0) {
+ ODPH_ERR("Invalid burst pattern, frees exceed allocations "
+ "instantaneously\n");
+ return PRS_NOK;
+ }
+
+ config->num_data_elems += (elem->op == ALLOC ? elem->val : 0U);
+ }
+
+ if (num_tot != 0) {
+ ODPH_ERR("Invalid burst pattern, cumulative sum not zero: %" PRId64 "\n", num_tot);
+ return PRS_NOK;
+ }
+
+ if (odp_shm_capability(&shm_capa) < 0) {
+ ODPH_ERR("Error querying SHM capabilities\n");
+ return PRS_NOK;
+ }
+
+ if (shm_capa.max_blocks < config->num_workers + 1U) {
+ ODPH_ERR("Invalid amount of SHM blocks: %u (max: %u)\n", config->num_workers + 1U,
+ shm_capa.max_blocks);
+ return PRS_NOK;
+ }
+
+ shm_size = (uint64_t)config->num_data_elems * config->handle_size;
+
+ if (shm_capa.max_size != 0U && shm_size > shm_capa.max_size) {
+ ODPH_ERR("Invalid total SHM block size: %" PRIu64 " (max: %" PRIu64 ")\n",
+ shm_size, shm_capa.max_size);
+ return PRS_NOK;
+ }
+
+ if (config->policy != SINGLE && config->policy != MANY) {
+ ODPH_ERR("Invalid pool policy: %u\n", config->policy);
+ return PRS_NOK;
+ }
+
+ if (config->num_rounds > 0U && config->num_ignore >= config->num_rounds) {
+ ODPH_ERR("Invalid round ignore count: %" PRIu64 " (max: %" PRIu64 ")\n",
+ config->num_ignore, config->num_rounds - 1U);
+ return PRS_NOK;
+ }
+
+ return PRS_OK;
+}
+
+static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
+{
+ int opt, long_index;
+
+ static const struct option longopts[] = {
+ { "burst_pattern", required_argument, NULL, 'b' },
+ { "type", required_argument, NULL, 't' },
+ { "event_count", required_argument, NULL, 'e' },
+ { "data_size", required_argument, NULL, 'd' },
+ { "policy", required_argument, NULL, 'p' },
+ { "round_count", required_argument, NULL, 'r' },
+ { "ignore_rounds", required_argument, NULL, 'i' },
+ { "worker_count", required_argument, NULL, 'c' },
+ { "cache_size", required_argument, NULL, 'C' },
+ { "write_uarea", required_argument, NULL, 'w' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "b:t:e:d:p:r:i:c:C:w:h";
+
+ init_config(config);
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ parse_burst_pattern(config, optarg);
+ break;
+ case 't':
+ config->type = atoi(optarg);
+ break;
+ case 'e':
+ config->num_evs = atoi(optarg);
+ break;
+ case 'd':
+ config->data_size = atoi(optarg);
+ break;
+ case 'p':
+ config->policy = atoi(optarg);
+ break;
+ case 'r':
+ config->num_rounds = atoll(optarg);
+ break;
+ case 'i':
+ config->num_ignore = atoll(optarg);
+ break;
+ case 'c':
+ config->num_workers = atoi(optarg);
+ break;
+ case 'C':
+ config->cache_size = atoi(optarg);
+ break;
+ case 'w':
+ config->uarea_size = atoi(optarg);
+ break;
+ case 'h':
+ print_usage(&config->dyn_defs);
+ return PRS_TERM;
+ case '?':
+ default:
+ print_usage(&config->dyn_defs);
+ return PRS_NOK;
+ }
+ }
+
+ return check_options(config);
+}
+
+static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
+{
+ struct sigaction action = { .sa_handler = terminate };
+
+ if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
+ sigaddset(&action.sa_mask, SIGTERM) == -1 ||
+ sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
+ sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
+ ODPH_ERR("Error installing signal handler\n");
+ return PRS_NOK;
+ }
+
+ return parse_options(argc, argv, config);
+}
+
+static inline void save_alloc_stats(odp_time_t t1, odp_time_t t2, uint32_t num_alloc,
+ uint64_t round, uint8_t pattern, stats_t *stats)
+{
+ const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
+
+ stats->alloc_tm += tm_diff;
+ stats->alloc_cnt += num_alloc;
+ ++stats->alloc_b_cnt;
+
+ if (tm_diff > stats->max_alloc_tm) {
+ stats->max_alloc_tm = tm_diff;
+ stats->max_alloc_rnd = round;
+ stats->max_alloc_pt = pattern;
+ }
+
+ if (tm_diff < stats->min_alloc_tm) {
+ stats->min_alloc_tm = tm_diff;
+ stats->min_alloc_rnd = round;
+ stats->min_alloc_pt = pattern;
+ }
+}
+
+static inline void write_to_uarea(uint8_t *data, uint32_t size)
+{
+ memset(data, UA_DATA, size);
+}
+
+static inline void save_uarea_stats(odp_time_t t1, odp_time_t t2, uint64_t round, uint8_t pattern,
+ stats_t *stats)
+{
+ const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
+
+ stats->uarea_tm += tm_diff;
+
+ if (tm_diff > stats->max_uarea_tm) {
+ stats->max_uarea_tm = tm_diff;
+ stats->max_uarea_rnd = round;
+ stats->max_uarea_pt = pattern;
+ }
+
+ if (tm_diff < stats->min_uarea_tm) {
+ stats->min_uarea_tm = tm_diff;
+ stats->min_uarea_rnd = round;
+ stats->min_uarea_pt = pattern;
+ }
+}
+
+static inline void save_free_stats(odp_time_t t1, odp_time_t t2, uint64_t round, uint8_t pattern,
+ stats_t *stats)
+{
+ const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
+
+ stats->free_tm += tm_diff;
+ ++stats->free_b_cnt;
+
+ if (tm_diff > stats->max_free_tm) {
+ stats->max_free_tm = tm_diff;
+ stats->max_free_rnd = round;
+ stats->max_free_pt = pattern;
+ }
+
+ if (tm_diff < stats->min_free_tm) {
+ stats->min_free_tm = tm_diff;
+ stats->min_free_rnd = round;
+ stats->min_free_pt = pattern;
+ }
+
+ stats->max_free_tm = ODPH_MAX(tm_diff, stats->max_free_tm);
+ stats->min_free_tm = ODPH_MIN(tm_diff, stats->min_free_tm);
+}
+
+static uint32_t allocate_buffers(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_pool_t pool = config->pool;
+ uint32_t retries = MAX_RETRIES;
+ odp_buffer_t *bufs = &((odp_buffer_t *)data)[idx];
+ uint32_t num_alloc, num_tot = 0U;
+ int ret;
+ stats_t *stats = &config->stats;
+
+ while (retries-- > 0U && num_tot < num) {
+ num_alloc = num - num_tot;
+ t1 = odp_time_local_strict();
+ ret = odp_buffer_alloc_multi(pool, &bufs[num_tot], num_alloc);
+ t2 = odp_time_local_strict();
+
+ if (odp_unlikely(ret < 0)) {
+ ++stats->alloc_errs;
+ break;
+ }
+
+ if (odp_unlikely((uint32_t)ret < num_alloc))
+ ++stats->reallocs;
+
+ num_tot += ret;
+
+ if (odp_likely(is_saved))
+ save_alloc_stats(t1, t2, ret, round, pattern, stats);
+ }
+
+ if (config->uarea_size > 0U) {
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num_tot; ++i)
+ write_to_uarea(odp_buffer_user_area(bufs[i]), config->uarea_size);
+
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_uarea_stats(t1, t2, round, pattern, stats);
+ }
+
+ return num_tot;
+}
+
+static void free_buffers(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
+ uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_buffer_t *bufs = &((odp_buffer_t *)data)[idx];
+
+ t1 = odp_time_local_strict();
+ odp_buffer_free_multi(bufs, num);
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_free_stats(t1, t2, round, pattern, stats);
+}
+
+static uint32_t allocate_packets(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_pool_t pool = config->pool;
+ uint32_t retries = MAX_RETRIES, data_size = config->data_size;
+ odp_packet_t *pkts = &((odp_packet_t *)data)[idx];
+ uint32_t num_alloc, num_tot = 0U;
+ int ret;
+ stats_t *stats = &config->stats;
+
+ while (retries-- > 0U && num_tot < num) {
+ num_alloc = num - num_tot;
+ t1 = odp_time_local_strict();
+ ret = odp_packet_alloc_multi(pool, data_size, &pkts[num_tot], num_alloc);
+ t2 = odp_time_local_strict();
+
+ if (odp_unlikely(ret < 0)) {
+ ++stats->alloc_errs;
+ break;
+ }
+
+ if (odp_unlikely((uint32_t)ret < num_alloc))
+ ++stats->reallocs;
+
+ num_tot += ret;
+
+ if (odp_likely(is_saved))
+ save_alloc_stats(t1, t2, ret, round, pattern, stats);
+ }
+
+ if (config->uarea_size > 0U) {
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num_tot; ++i)
+ write_to_uarea(odp_packet_user_area(pkts[i]), config->uarea_size);
+
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_uarea_stats(t1, t2, round, pattern, stats);
+ }
+
+ return num_tot;
+}
+
+static void free_packets(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
+ uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_packet_t *pkts = &((odp_packet_t *)data)[idx];
+
+ t1 = odp_time_local_strict();
+ odp_packet_free_multi(pkts, num);
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_free_stats(t1, t2, round, pattern, stats);
+}
+
+static uint32_t allocate_timeouts(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_pool_t pool = config->pool;
+ uint32_t retries = MAX_RETRIES;
+ odp_timeout_t *tmos = &((odp_timeout_t *)data)[idx];
+ uint32_t num_alloc, num_tot = 0U;
+ int ret;
+ stats_t *stats = &config->stats;
+
+ while (retries-- > 0U && num_tot < num) {
+ num_alloc = num - num_tot;
+ t1 = odp_time_local_strict();
+ ret = odp_timeout_alloc_multi(pool, &tmos[num_tot], num_alloc);
+ t2 = odp_time_local_strict();
+
+ if (odp_unlikely(ret < 0)) {
+ ++stats->alloc_errs;
+ break;
+ }
+
+ if (odp_unlikely((uint32_t)ret < num_alloc))
+ ++stats->reallocs;
+
+ num_tot += ret;
+
+ if (odp_likely(is_saved))
+ save_alloc_stats(t1, t2, ret, round, pattern, stats);
+ }
+
+ if (config->uarea_size > 0U) {
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num_tot; ++i)
+ write_to_uarea(odp_timeout_user_area(tmos[i]), config->uarea_size);
+
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_uarea_stats(t1, t2, round, pattern, stats);
+ }
+
+ return num_tot;
+}
+
+static void free_timeouts(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
+ uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_timeout_t *tmos = &((odp_timeout_t *)data)[idx];
+
+ t1 = odp_time_local_strict();
+ odp_timeout_free_multi(tmos, num);
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_free_stats(t1, t2, round, pattern, stats);
+}
+
+static uint32_t allocate_vectors(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
+ uint64_t round, uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_pool_t pool = config->pool;
+ uint32_t num_tot = 0U;
+ odp_packet_vector_t *vecs = &((odp_packet_vector_t *)data)[idx], vec;
+ stats_t *stats = &config->stats;
+
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num; ++i) {
+ vec = odp_packet_vector_alloc(pool);
+
+ if (odp_unlikely(vec == ODP_PACKET_VECTOR_INVALID))
+ break;
+
+ vecs[num_tot++] = vec;
+ }
+
+ t2 = odp_time_local_strict();
+
+ if (odp_unlikely(num_tot == 0))
+ ++stats->alloc_errs;
+ else if (odp_likely(is_saved))
+ save_alloc_stats(t1, t2, num_tot, round, pattern, stats);
+
+ if (config->uarea_size > 0U) {
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num_tot; ++i)
+ write_to_uarea(odp_packet_vector_user_area(vecs[i]), config->uarea_size);
+
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_uarea_stats(t1, t2, round, pattern, stats);
+ }
+
+ return num_tot;
+}
+
+static void free_vectors(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
+ uint8_t pattern, odp_bool_t is_saved)
+{
+ odp_time_t t1, t2;
+ odp_packet_vector_t *vecs = &((odp_packet_vector_t *)data)[idx];
+
+ t1 = odp_time_local_strict();
+
+ for (uint32_t i = 0U; i < num; ++i)
+ odp_packet_vector_free(vecs[i]);
+
+ t2 = odp_time_local_strict();
+
+ if (odp_likely(is_saved))
+ save_free_stats(t1, t2, round, pattern, stats);
+}
+
+static odp_pool_t create_pool(const char *name, const odp_pool_param_t *params, uint8_t policy)
+{
+ static odp_pool_t pool = ODP_POOL_INVALID;
+
+ if (policy == SINGLE && pool != ODP_POOL_INVALID)
+ return pool;
+
+ pool = odp_pool_create(name, params);
+
+ return pool;
+}
+
+static odp_bool_t setup_worker_config(prog_config_t *config)
+{
+ odp_pool_param_t param;
+ odp_pool_t pool;
+ worker_config_t *worker;
+ odp_shm_t shm;
+ void *data;
+
+ odp_pool_param_init(&param);
+
+ if (config->type == BUFFER) {
+ param.type = ODP_POOL_BUFFER;
+ param.buf.num = config->num_evs;
+ param.buf.size = config->data_size;
+ param.buf.uarea_size = config->uarea_size;
+ param.buf.cache_size = config->cache_size;
+ config->alloc_fn = allocate_buffers;
+ config->free_fn = free_buffers;
+ } else if (config->type == PACKET) {
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = config->num_evs;
+ param.pkt.len = config->data_size;
+ param.pkt.seg_len = config->seg_len;
+ param.pkt.uarea_size = config->uarea_size;
+ param.pkt.cache_size = config->cache_size;
+ config->alloc_fn = allocate_packets;
+ config->free_fn = free_packets;
+ } else if (config->type == TMO) {
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = config->num_evs;
+ param.tmo.uarea_size = config->uarea_size;
+ param.tmo.cache_size = config->cache_size;
+ config->alloc_fn = allocate_timeouts;
+ config->free_fn = free_timeouts;
+ } else {
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = config->num_evs;
+ param.vector.max_size = config->data_size;
+ param.vector.uarea_size = config->uarea_size;
+ param.vector.cache_size = config->cache_size;
+ config->alloc_fn = allocate_vectors;
+ config->free_fn = free_vectors;
+ }
+
+ for (uint32_t i = 0U; i < config->num_workers; ++i) {
+ pool = create_pool(PROG_NAME "_pool", &param, config->policy);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error creating worker pool\n");
+ return false;
+ }
+
+ shm = odp_shm_reserve(PROG_NAME "_shm",
+ config->handle_size * config->num_data_elems,
+ ODP_CACHE_LINE_SIZE, 0U);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error creating worker SHM\n");
+ return false;
+ }
+
+ data = odp_shm_addr(shm);
+
+ if (data == NULL) {
+ ODPH_ERR("Error resolving worker SHM\n");
+ return false;
+ }
+
+ worker = &config->worker_config[i];
+ worker->pool = pool;
+ worker->data = data;
+ worker->prog_config = config;
+ worker->shm = shm;
+ worker->data_size = config->data_size;
+ worker->uarea_size = config->uarea_size;
+ }
+
+ return true;
+}
+
+static int run_test(void *args)
+{
+ worker_config_t *config = args;
+ odp_time_t t1, t2;
+ uint64_t i, num_ignore = config->prog_config->num_ignore;
+ const uint64_t num_rnds = config->prog_config->num_rounds;
+ odp_atomic_u32_t *is_running = &config->prog_config->is_running;
+ uint32_t head_idx, cur_idx, val, num_alloc, idx;
+ odp_bool_t is_saved;
+ const uint8_t num_elems = config->prog_config->num_elems;
+ const alloc_elem_t *elems = config->prog_config->alloc_elems, *elem;
+ uint8_t op;
+ void *data = config->data;
+ const alloc_fn_t alloc_fn = config->prog_config->alloc_fn;
+ stats_t *stats = &config->stats;
+ const free_fn_t free_fn = config->prog_config->free_fn;
+
+ odp_barrier_wait(&config->prog_config->init_barrier);
+ t1 = odp_time_local_strict();
+
+ for (i = 0U; (i < num_rnds || num_rnds == 0U) && odp_atomic_load_u32(is_running); ++i) {
+ head_idx = 0U;
+ cur_idx = head_idx;
+ is_saved = (num_ignore > 0U ? num_ignore-- : num_ignore) == 0U;
+
+ for (uint8_t j = 0U; j < num_elems; ++j) {
+ elem = &elems[j];
+ val = elem->val;
+ op = elem->op;
+
+ if (op == ALLOC) {
+ num_alloc = alloc_fn(config, data, cur_idx, val, i, j, is_saved);
+
+ if (odp_unlikely(num_alloc < val))
+ ++stats->pattern_errs;
+
+ cur_idx += num_alloc;
+ } else if (op == FREE) {
+ /* Due to potential pattern errors, there might not be expected
+ * amount of freeable events. */
+ val = ODPH_MIN(val, cur_idx - head_idx);
+
+ if (elem->opt == TOP) {
+ idx = head_idx;
+ head_idx += val;
+ } else {
+ cur_idx -= val;
+ idx = cur_idx;
+ }
+
+ free_fn(data, idx, val, stats, i, j, is_saved);
+ } else {
+ odp_time_wait_ns(val);
+ }
+ }
+ }
+
+ t2 = odp_time_local_strict();
+ stats->tot_tm = odp_time_diff_ns(t2, t1);
+ stats->act_num_rounds = i;
+ odp_barrier_wait(&config->prog_config->term_barrier);
+
+ return 0;
+}
+
+static odp_bool_t setup_workers(prog_config_t *config)
+{
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_params[config->num_workers], *thr_param;
+
+ odp_barrier_init(&config->init_barrier, config->num_workers + 1);
+ odp_barrier_init(&config->term_barrier, config->num_workers + 1);
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = config->odp_instance;
+ thr_common.cpumask = &config->worker_mask;
+
+ for (uint32_t i = 0; i < config->num_workers; ++i) {
+ thr_param = &thr_params[i];
+ odph_thread_param_init(thr_param);
+ thr_param->start = run_test;
+ thr_param->thr_type = ODP_THREAD_WORKER;
+ thr_param->arg = &config->worker_config[i];
+ }
+
+ if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
+ config->num_workers) != config->num_workers) {
+ ODPH_ERR("Error configuring worker threads\n");
+ return false;
+ }
+
+ odp_barrier_wait(&config->init_barrier);
+
+ return true;
+}
+
+static odp_bool_t setup_test(prog_config_t *config)
+{
+ return setup_worker_config(config) && setup_workers(config);
+}
+
+static void stop_test(prog_config_t *config)
+{
+ odp_barrier_wait(&config->term_barrier);
+ (void)odph_thread_join(config->thread_tbl, config->num_workers);
+}
+
+static void print_stats(const prog_config_t *config)
+{
+ const alloc_elem_t *elem;
+ const stats_t *stats;
+ uint64_t ev_rate, ave_b_alloc_tm, b_alloc_min, b_alloc_max, ave_b_free_tm, b_free_min,
+ b_free_max, ave_alloc_tm, ave_free_tm, ave_ua_b_tm, b_ua_min, b_ua_max, ave_ua_tm,
+ tot_b_alloc_tm = 0U, tot_b_free_tm = 0U, tot_alloc_tm = 0U, tot_free_tm = 0U,
+ tot_alloc_min = 0U, tot_alloc_max = 0U, tot_free_min = 0U, tot_free_max = 0U,
+ tot_b_ua_tm = 0U, tot_ua_tm = 0U, tot_ua_min = 0U, tot_ua_max = 0U;
+
+ printf("\n==================\n\n"
+ "Pool latency test done\n\n"
+ " type: %s\n"
+ " event count: %u\n", config->type == BUFFER ? "buffer" :
+ config->type == PACKET ? "packet" : config->type == TMO ? "timeout" : "vector",
+ config->num_evs);
+
+ if (config->type != TMO)
+ printf(" %s %u\n",
+ config->type != VECTOR ? "data size: " : "vector size: ",
+ config->data_size);
+
+ printf(" pool policy: %s\n"
+ " target round count: %" PRIu64 "\n"
+ " ignore count: %" PRIu64 "\n"
+ " cache size: %" PRIi64 "\n"
+ " user area: %u (B)\n"
+ " burst pattern:\n", config->policy == SINGLE ? "shared" : "per-worker",
+ config->num_rounds, config->num_ignore, config->cache_size, config->uarea_size);
+
+ for (uint8_t i = 0U; i < config->num_elems; ++i) {
+ elem = &config->alloc_elems[i];
+ printf(" %s %u%s\n", elem->op == ALLOC ? "allocate:" :
+ elem->op == FREE && elem->opt == TOP ? "free (t):" :
+ elem->op == FREE && elem->opt == BOTTOM ? "free (b):" :
+ "delay: ", elem->val, elem->op == DELAY ? " (ns)" : "");
+ }
+
+ printf("\n");
+
+ for (uint32_t i = 0U; i < config->num_workers; ++i) {
+ stats = &config->worker_config[i].stats;
+ ev_rate = stats->tot_tm > 0U ?
+ (double)stats->alloc_cnt / stats->tot_tm * ODP_TIME_SEC_IN_NS : 0U;
+ ave_b_alloc_tm = stats->alloc_b_cnt > 0U ?
+ stats->alloc_tm / stats->alloc_b_cnt : 0U;
+ b_alloc_min = ave_b_alloc_tm > 0U ? stats->min_alloc_tm : 0U;
+ b_alloc_max = ave_b_alloc_tm > 0U ? stats->max_alloc_tm : 0U;
+ ave_b_free_tm = stats->free_b_cnt > 0U ?
+ stats->free_tm / stats->free_b_cnt : 0U;
+ b_free_min = ave_b_free_tm > 0U ? stats->min_free_tm : 0U;
+ b_free_max = ave_b_free_tm > 0U ? stats->max_free_tm : 0U;
+ ave_alloc_tm = stats->alloc_cnt > 0U ? stats->alloc_tm / stats->alloc_cnt : 0U;
+ ave_free_tm = stats->alloc_cnt > 0U ? stats->free_tm / stats->alloc_cnt : 0U;
+
+ printf(" worker %d:\n"
+ " actual round count: %" PRIu64 "\n"
+ " significant events allocated/freed: %" PRIu64 "\n"
+ " allocation retries: %" PRIu64 "\n"
+ " allocation errors: %" PRIu64 "\n"
+ " pattern errors: %" PRIu64 "\n"
+ " run time: %" PRIu64 " (ns)\n"
+ " event rate %" PRIu64 " (evs/s)\n"
+ " average latency breakdown (ns):\n"
+ " per allocation burst: %" PRIu64 " (min: %" PRIu64 " (round: %"
+ PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %" PRIu64 ", pattern: %u))"
+ "\n"
+ " per allocation: %" PRIu64 "\n"
+ " per free burst: %" PRIu64 " (min: %" PRIu64 " (round: %"
+ PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %" PRIu64 ", pattern: %u))"
+ "\n"
+ " per free: %" PRIu64 "\n", i, stats->act_num_rounds,
+ stats->alloc_cnt, stats->reallocs, stats->alloc_errs, stats->pattern_errs,
+ stats->tot_tm, ev_rate, ave_b_alloc_tm, b_alloc_min, stats->min_alloc_rnd,
+ stats->min_alloc_pt, b_alloc_max, stats->max_alloc_rnd, stats->max_alloc_pt,
+ ave_alloc_tm, ave_b_free_tm, b_free_min, stats->min_free_rnd,
+ stats->min_free_pt, b_free_max, stats->max_free_rnd, stats->max_free_pt,
+ ave_free_tm);
+ tot_b_alloc_tm += ave_b_alloc_tm;
+ tot_b_free_tm += ave_b_free_tm;
+ tot_alloc_tm += ave_alloc_tm;
+ tot_free_tm += ave_free_tm;
+ tot_alloc_min += b_alloc_min;
+ tot_alloc_max += b_alloc_max;
+ tot_free_min += b_free_min;
+ tot_free_max += b_free_max;
+
+ if (config->uarea_size > 0U) {
+ ave_ua_b_tm = stats->alloc_b_cnt > 0U ?
+ stats->uarea_tm / stats->alloc_b_cnt : 0U;
+ ave_ua_tm = stats->alloc_cnt > 0U ?
+ stats->uarea_tm / stats->alloc_cnt : 0U;
+ b_ua_min = ave_ua_b_tm > 0U ? stats->min_uarea_tm : 0U;
+ b_ua_max = ave_ua_b_tm > 0U ? stats->max_uarea_tm : 0U;
+ printf(" per ua write burst: %" PRIu64 " (min: %" PRIu64 " ("
+ "round: %" PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %"
+ PRIu64 ", pattern: %u))\n"
+ " per ua write: %" PRIu64 "\n", ave_ua_b_tm,
+ b_ua_min, stats->min_uarea_rnd, stats->min_uarea_pt, b_ua_max,
+ stats->max_uarea_rnd, stats->max_uarea_pt, ave_ua_tm);
+ tot_b_ua_tm += ave_ua_b_tm;
+ tot_ua_tm += ave_ua_tm;
+ tot_ua_min += b_ua_min;
+ tot_ua_max += b_ua_max;
+ }
+
+ printf("\n");
+ }
+
+ printf(" total (ns):\n"
+ " per allocation burst: %" PRIu64 " (min: %" PRIu64 ", max: %" PRIu64 ")\n"
+ " per allocation: %" PRIu64 "\n"
+ " per free burst: %" PRIu64 " (min: %" PRIu64 ", max: %" PRIu64 ")\n"
+ " per free: %" PRIu64 "\n",
+ tot_b_alloc_tm / config->num_workers, tot_alloc_min / config->num_workers,
+ tot_alloc_max / config->num_workers, tot_alloc_tm / config->num_workers,
+ tot_b_free_tm / config->num_workers, tot_free_min / config->num_workers,
+ tot_free_max / config->num_workers, tot_free_tm / config->num_workers);
+
+ if (config->uarea_size > 0U) {
+ printf(" per ua write burst: %" PRIu64 " (min: %" PRIu64 ", max: %"
+ PRIu64 ")\n"
+ " per ua write: %" PRIu64 "\n",
+ tot_b_ua_tm / config->num_workers, tot_ua_min / config->num_workers,
+ tot_ua_max / config->num_workers, tot_ua_tm / config->num_workers);
+ }
+
+ printf("\n==================\n");
+}
+
+static void destroy_pool(odp_pool_t pool, uint8_t policy)
+{
+ static odp_bool_t is_destroyed;
+
+ if (policy == SINGLE && is_destroyed)
+ return;
+
+ (void)odp_pool_destroy(pool);
+ is_destroyed = true;
+}
+
+static void teardown(const prog_config_t *config)
+{
+ const worker_config_t *worker;
+
+ for (uint32_t i = 0U; i < config->num_workers; ++i) {
+ worker = &config->worker_config[i];
+
+ if (worker->pool != ODP_POOL_INVALID)
+ destroy_pool(worker->pool, config->policy);
+
+ if (worker->shm != ODP_SHM_INVALID)
+ (void)odp_shm_free(worker->shm);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t odph_opts;
+ odp_init_t init_param;
+ odp_instance_t odp_instance;
+ odp_shm_t shm_cfg = ODP_SHM_INVALID;
+ int ret = EXIT_SUCCESS;
+ parse_result_t parse_res;
+
+ argc = odph_parse_options(argc, argv);
+
+ if (odph_options(&odph_opts) == -1) {
+ ODPH_ERR("Error while reading ODP helper options, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = odph_opts.mem_model;
+
+ if (odp_init_global(&odp_instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(odp_instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("ODP local init failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
+ 0U);
+
+ if (shm_cfg == ODP_SHM_INVALID) {
+ ODPH_ERR("Error reserving shared memory\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ prog_conf = odp_shm_addr(shm_cfg);
+
+ if (prog_conf == NULL) {
+ ODPH_ERR("Error resolving shared memory address\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ parse_res = setup_program(argc, argv, prog_conf);
+
+ if (parse_res == PRS_NOK) {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (parse_res == PRS_TERM) {
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ prog_conf->odp_instance = odp_instance;
+ odp_atomic_init_u32(&prog_conf->is_running, 1U);
+
+ if (!setup_test(prog_conf)) {
+ ret = EXIT_FAILURE;
+ goto out_test;
+ }
+
+ stop_test(prog_conf);
+ print_stats(prog_conf);
+
+out_test:
+ teardown(prog_conf);
+
+out:
+ if (shm_cfg != ODP_SHM_INVALID)
+ (void)odp_shm_free(shm_cfg);
+
+ if (odp_term_local()) {
+ ODPH_ERR("ODP local terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(odp_instance)) {
+ ODPH_ERR("ODP global terminate failed, exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_pool_perf.c b/test/performance/odp_pool_perf.c
new file mode 100644
index 000000000..c79465e53
--- /dev/null
+++ b/test/performance/odp_pool_perf.c
@@ -0,0 +1,747 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ */
+
+/**
+ * @example odp_pool_perf.c
+ *
+ * Performance test application for pool APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define STAT_AVAILABLE 0x1
+#define STAT_CACHE 0x2
+#define STAT_THR_CACHE 0x4
+#define STAT_ALLOC_OPS 0x10
+#define STAT_FREE_OPS 0x20
+#define STAT_TOTAL_OPS 0x40
+
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t num_event;
+ uint32_t num_round;
+ uint32_t max_burst;
+ uint32_t num_burst;
+ uint32_t data_size;
+ uint32_t cache_size;
+ uint32_t stats_mode;
+ int pool_type;
+
+} test_options_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t frees;
+ uint64_t events;
+ uint64_t nsec;
+ uint64_t cycles;
+
+} test_stat_t;
+
+typedef struct test_global_t {
+ test_options_t test_options;
+
+ odp_barrier_t barrier;
+ odp_pool_t pool;
+ odp_cpumask_t cpumask;
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+
+} test_global_t;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Pool performance test\n"
+ "\n"
+ "Usage: odp_pool_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
+ " -e, --num_event Number of events\n"
+ " -r, --num_round Number of rounds\n"
+ " -b, --burst Maximum number of events per operation\n"
+ " -n, --num_burst Number of bursts allocated/freed back-to-back\n"
+ " -s, --data_size Data size in bytes\n"
+ " -S, --stats_mode Pool statistics usage. Enable counters with combination of these flags:\n"
+ " 0: no pool statistics (default)\n"
+ " 0x1: available\n"
+ " 0x2: cache_available\n"
+ " 0x4: thread_cache_available\n"
+ " 0x10: alloc_ops\n"
+ " 0x20: free_ops\n"
+ " 0x40: total_ops\n"
+ " -t, --pool_type 0: Buffer pool (default)\n"
+ " 1: Packet pool\n"
+ " -C, --cache_size Pool cache size (per thread)\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_event", required_argument, NULL, 'e'},
+ {"num_round", required_argument, NULL, 'r'},
+ {"burst", required_argument, NULL, 'b'},
+ {"num_burst", required_argument, NULL, 'n'},
+ {"data_size", required_argument, NULL, 's'},
+ {"stats_mode", required_argument, NULL, 'S'},
+ {"pool_type", required_argument, NULL, 't'},
+ {"cache_size", required_argument, NULL, 'C'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:e:r:b:n:s:S:t:C:h";
+
+ test_options->num_cpu = 1;
+ test_options->num_event = 1000;
+ test_options->num_round = 100000;
+ test_options->max_burst = 100;
+ test_options->num_burst = 1;
+ test_options->data_size = 64;
+ test_options->stats_mode = 0;
+ test_options->pool_type = 0;
+ test_options->cache_size = UINT32_MAX;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'e':
+ test_options->num_event = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atoi(optarg);
+ break;
+ case 'b':
+ test_options->max_burst = atoi(optarg);
+ break;
+ case 'n':
+ test_options->num_burst = atoi(optarg);
+ break;
+ case 's':
+ test_options->data_size = atoi(optarg);
+ break;
+ case 'S':
+ test_options->stats_mode = strtoul(optarg, NULL, 0);
+ break;
+ case 't':
+ test_options->pool_type = atoi(optarg);
+ break;
+ case 'C':
+ test_options->cache_size = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_burst * test_options->max_burst >
+ test_options->num_event) {
+ printf("Not enough events (%u) for the burst configuration.\n"
+ "Use smaller burst size (%u) or less bursts (%u)\n",
+ test_options->num_event, test_options->max_burst,
+ test_options->num_burst);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ printf("Error: Too many workers. Maximum is %i.\n",
+ ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ printf("Error: Too many workers. Max supported %i.\n", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ return 0;
+}
+
+static int create_pool(test_global_t *global)
+{
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ odp_pool_stats_opt_t stats, stats_capa;
+ uint32_t max_num, max_size, min_cache_size, max_cache_size;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_event = test_options->num_event;
+ uint32_t num_round = test_options->num_round;
+ uint32_t max_burst = test_options->max_burst;
+ uint32_t num_burst = test_options->num_burst;
+ uint32_t num_cpu = test_options->num_cpu;
+ uint32_t data_size = test_options->data_size;
+ uint32_t cache_size = test_options->cache_size;
+ uint32_t stats_mode = test_options->stats_mode;
+ int packet_pool = test_options->pool_type;
+
+ stats.all = 0;
+
+ odp_pool_param_init(&pool_param);
+
+ if (cache_size == UINT32_MAX)
+ cache_size = packet_pool ? pool_param.pkt.cache_size :
+ pool_param.buf.cache_size;
+
+ if (stats_mode & STAT_AVAILABLE)
+ stats.bit.available = 1;
+ if (stats_mode & STAT_CACHE)
+ stats.bit.cache_available = 1;
+ if (stats_mode & STAT_THR_CACHE)
+ stats.bit.thread_cache_available = 1;
+ if (stats_mode & STAT_ALLOC_OPS)
+ stats.bit.alloc_ops = 1;
+ if (stats_mode & STAT_FREE_OPS)
+ stats.bit.free_ops = 1;
+ if (stats_mode & STAT_TOTAL_OPS)
+ stats.bit.total_ops = 1;
+
+ printf("\nPool performance test\n");
+ printf(" num cpu %u\n", num_cpu);
+ printf(" num rounds %u\n", num_round);
+ printf(" num events %u\n", num_event);
+ printf(" max burst %u\n", max_burst);
+ printf(" num bursts %u\n", num_burst);
+ printf(" data size %u\n", data_size);
+ printf(" cache size %u\n", cache_size);
+ printf(" stats mode 0x%x\n", stats_mode);
+ printf(" pool type %s\n\n", packet_pool ? "packet" : "buffer");
+
+ if (odp_pool_capability(&pool_capa)) {
+ printf("Error: Pool capa failed.\n");
+ return -1;
+ }
+
+ if (packet_pool) {
+ max_num = pool_capa.pkt.max_num;
+ max_size = pool_capa.pkt.max_len;
+ max_cache_size = pool_capa.pkt.max_cache_size;
+ min_cache_size = pool_capa.pkt.min_cache_size;
+ stats_capa = pool_capa.pkt.stats;
+ } else {
+ max_num = pool_capa.buf.max_num;
+ max_size = pool_capa.buf.max_size;
+ max_cache_size = pool_capa.buf.max_cache_size;
+ min_cache_size = pool_capa.buf.min_cache_size;
+ stats_capa = pool_capa.buf.stats;
+ }
+
+ if ((stats_capa.all & stats.all) != stats.all) {
+ printf("Error: requested statistics not supported (0x%" PRIx64 " / 0x%" PRIx64 ")\n",
+ stats.all, stats_capa.all);
+ return -1;
+ }
+
+ if (cache_size < min_cache_size) {
+ printf("Error: min cache size supported %u\n", min_cache_size);
+ return -1;
+ }
+
+ if (cache_size > max_cache_size) {
+ printf("Error: max cache size supported %u\n", max_cache_size);
+ return -1;
+ }
+
+ if (max_num && num_event > max_num) {
+ printf("Error: max events supported %u\n", max_num);
+ return -1;
+ }
+
+ if (max_size && data_size > max_size) {
+ printf("Error: max data size supported %u\n", max_size);
+ return -1;
+ }
+
+ if (packet_pool) {
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = num_event;
+ pool_param.pkt.len = data_size;
+ pool_param.pkt.max_num = num_event;
+ pool_param.pkt.max_len = data_size;
+ pool_param.pkt.cache_size = cache_size;
+ } else {
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = num_event;
+ pool_param.buf.size = data_size;
+ pool_param.buf.cache_size = cache_size;
+ }
+
+ pool_param.stats.all = stats.all;
+
+ pool = odp_pool_create("pool perf", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ printf("Error: Pool create failed.\n");
+ return -1;
+ }
+
+ global->pool = pool;
+
+ return 0;
+}
+
+static int test_buffer_pool(void *arg)
+{
+ int ret, thr;
+ uint32_t num, num_free, num_freed, i, rounds;
+ uint64_t c1, c2, cycles, nsec;
+ uint64_t events, frees;
+ odp_time_t t1, t2;
+ test_global_t *global = arg;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ uint32_t max_burst = test_options->max_burst;
+ uint32_t num_burst = test_options->num_burst;
+ uint32_t max_num = num_burst * max_burst;
+ odp_pool_t pool = global->pool;
+ odp_buffer_t buf[max_num];
+
+ thr = odp_thread_id();
+
+ for (i = 0; i < max_num; i++)
+ buf[i] = ODP_BUFFER_INVALID;
+
+ events = 0;
+ frees = 0;
+ ret = 0;
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ c1 = odp_cpu_cycles();
+
+ for (rounds = 0; rounds < num_round; rounds++) {
+ num = 0;
+
+ for (i = 0; i < num_burst; i++) {
+ ret = odp_buffer_alloc_multi(pool, &buf[num],
+ max_burst);
+ if (odp_unlikely(ret < 0)) {
+ printf("Error: Alloc failed. Round %u\n",
+ rounds);
+ if (num)
+ odp_buffer_free_multi(buf, num);
+
+ return -1;
+ }
+
+ num += ret;
+ }
+
+ if (odp_unlikely(num == 0))
+ continue;
+
+ events += num;
+ num_freed = 0;
+
+ while (num_freed < num) {
+ num_free = num - num_freed;
+ if (num_free > max_burst)
+ num_free = max_burst;
+
+ odp_buffer_free_multi(&buf[num_freed], num_free);
+ frees++;
+ num_freed += num_free;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+ cycles = odp_cpu_cycles_diff(c2, c1);
+
+ /* Update stats*/
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].frees = frees;
+ global->stat[thr].events = events;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].cycles = cycles;
+
+ return 0;
+}
+
+static int test_packet_pool(void *arg)
+{
+ int ret, thr;
+ uint32_t num, num_free, num_freed, i, rounds;
+ uint64_t c1, c2, cycles, nsec;
+ uint64_t events, frees;
+ odp_time_t t1, t2;
+ test_global_t *global = arg;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_round = test_options->num_round;
+ uint32_t max_burst = test_options->max_burst;
+ uint32_t num_burst = test_options->num_burst;
+ uint32_t max_num = num_burst * max_burst;
+ uint32_t data_size = test_options->data_size;
+ odp_pool_t pool = global->pool;
+ odp_packet_t pkt[max_num];
+
+ thr = odp_thread_id();
+
+ for (i = 0; i < max_num; i++)
+ pkt[i] = ODP_PACKET_INVALID;
+
+ events = 0;
+ frees = 0;
+ ret = 0;
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ c1 = odp_cpu_cycles();
+
+ for (rounds = 0; rounds < num_round; rounds++) {
+ num = 0;
+
+ for (i = 0; i < num_burst; i++) {
+ ret = odp_packet_alloc_multi(pool, data_size, &pkt[num],
+ max_burst);
+ if (odp_unlikely(ret < 0)) {
+ printf("Error: Alloc failed. Round %u\n",
+ rounds);
+
+ if (num)
+ odp_packet_free_multi(pkt, num);
+
+ return -1;
+ }
+
+ num += ret;
+ }
+
+ if (odp_unlikely(num == 0))
+ continue;
+
+ events += num;
+ num_freed = 0;
+
+ while (num_freed < num) {
+ num_free = num - num_freed;
+ if (num_free > max_burst)
+ num_free = max_burst;
+
+ odp_packet_free_multi(&pkt[num_freed], num_free);
+ frees++;
+ num_freed += num_free;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+ cycles = odp_cpu_cycles_diff(c2, c1);
+
+ /* Update stats*/
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].frees = frees;
+ global->stat[thr].events = events;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].cycles = cycles;
+
+ return 0;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ int packet_pool = test_options->pool_type;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &global->cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.arg = global;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ if (packet_pool)
+ thr_param.start = test_packet_pool;
+ else
+ thr_param.start = test_buffer_pool;
+
+ if (odph_thread_create(global->thread_tbl, &thr_common, &thr_param,
+ num_cpu) != num_cpu)
+ return -1;
+
+ return 0;
+}
+
+static void test_stats_perf(test_global_t *global)
+{
+ odp_pool_stats_t stats;
+ odp_time_t t1, t2;
+ uint64_t nsec;
+ int i;
+ int num_thr = global->test_options.num_cpu + 1; /* workers + main thread */
+ odp_pool_t pool = global->pool;
+ double nsec_ave = 0.0;
+ const int rounds = 1000;
+
+ if (num_thr > ODP_POOL_MAX_THREAD_STATS)
+ num_thr = ODP_POOL_MAX_THREAD_STATS;
+
+ memset(&stats, 0, sizeof(odp_pool_stats_t));
+ stats.thread.first = 0;
+ stats.thread.last = num_thr - 1;
+
+ t1 = odp_time_local_strict();
+
+ for (i = 0; i < rounds; i++) {
+ if (odp_pool_stats(pool, &stats)) {
+ printf("Error: Stats request failed on round %i\n", i);
+ break;
+ }
+ }
+
+ t2 = odp_time_local_strict();
+ nsec = odp_time_diff_ns(t2, t1);
+
+ if (i > 0)
+ nsec_ave = (double)nsec / i;
+
+ printf("Pool statistics:\n");
+ printf(" odp_pool_stats() calls %i\n", i);
+ printf(" ave call latency %.2f nsec\n", nsec_ave);
+ printf(" num threads %i\n", num_thr);
+ printf(" alloc_ops %" PRIu64 "\n", stats.alloc_ops);
+ printf(" free_ops %" PRIu64 "\n", stats.free_ops);
+ printf(" total_ops %" PRIu64 "\n", stats.total_ops);
+ printf(" available %" PRIu64 "\n", stats.available);
+ printf(" cache_available %" PRIu64 "\n", stats.cache_available);
+ for (i = 0; i < num_thr; i++) {
+ printf(" thr[%2i] cache_available %" PRIu64 "\n",
+ i, stats.thread.cache_available[i]);
+ }
+
+ printf("\n");
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double rounds_ave, allocs_ave, frees_ave;
+ double events_ave, nsec_ave, cycles_ave;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ uint32_t num_burst = test_options->num_burst;
+ uint64_t rounds_sum = 0;
+ uint64_t frees_sum = 0;
+ uint64_t events_sum = 0;
+ uint64_t nsec_sum = 0;
+ uint64_t cycles_sum = 0;
+
+ /* Averages */
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ rounds_sum += global->stat[i].rounds;
+ frees_sum += global->stat[i].frees;
+ events_sum += global->stat[i].events;
+ nsec_sum += global->stat[i].nsec;
+ cycles_sum += global->stat[i].cycles;
+ }
+
+ if (rounds_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ rounds_ave = rounds_sum / num_cpu;
+ allocs_ave = (num_burst * rounds_sum) / num_cpu;
+ frees_ave = frees_sum / num_cpu;
+ events_ave = events_sum / num_cpu;
+ nsec_ave = nsec_sum / num_cpu;
+ cycles_ave = cycles_sum / num_cpu;
+ num = 0;
+
+ printf("RESULTS - per thread (Million events per sec):\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (1000.0 * global->stat[i].events) /
+ global->stat[i].nsec);
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("RESULTS - average over %i threads:\n", num_cpu);
+ printf("----------------------------------\n");
+ printf(" alloc calls: %.3f\n", allocs_ave);
+ printf(" free calls: %.3f\n", frees_ave);
+ printf(" duration: %.3f msec\n", nsec_ave / 1000000);
+ printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
+ printf(" cycles per round: %.3f\n",
+ cycles_ave / rounds_ave);
+ printf(" cycles per event: %.3f\n",
+ cycles_ave / events_ave);
+ printf(" ave events allocated: %.3f\n",
+ events_ave / allocs_ave);
+ printf(" allocs per sec: %.3f M\n",
+ (1000.0 * allocs_ave) / nsec_ave);
+ printf(" frees per sec: %.3f M\n",
+ (1000.0 * frees_ave) / nsec_ave);
+ printf(" events per sec: %.3f M\n\n",
+ (1000.0 * events_ave) / nsec_ave);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ printf("Error: Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ printf("Error: Local init failed.\n");
+ return -1;
+ }
+
+ shm = odp_shm_reserve("pool_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Error: Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(test_global_t));
+ global->pool = ODP_POOL_INVALID;
+
+ if (parse_options(argc, argv, &global->test_options))
+ return -1;
+
+ odp_sys_info_print();
+
+ if (set_num_cpu(global))
+ return -1;
+
+ if (create_pool(global))
+ return -1;
+
+ /* Start workers */
+ start_workers(global, instance);
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
+
+ if (global->test_options.stats_mode)
+ test_stats_perf(global);
+
+ print_stat(global);
+
+ if (odp_pool_destroy(global->pool)) {
+ printf("Error: Pool destroy failed.\n");
+ return -1;
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: Shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ printf("Error: term local failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Error: term global failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_queue_perf.c b/test/performance/odp_queue_perf.c
new file mode 100644
index 000000000..153f87d10
--- /dev/null
+++ b/test/performance/odp_queue_perf.c
@@ -0,0 +1,649 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+/**
+ * @example odp_queue_perf.c
+ *
+ * Performance test application for queue APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_QUEUES (32 * 1024)
+
+typedef struct test_options_t {
+ uint32_t num_queue;
+ uint32_t num_event;
+ uint32_t num_round;
+ uint32_t max_burst;
+ odp_nonblocking_t nonblock;
+ int single;
+ int num_cpu;
+
+} test_options_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t events;
+ uint64_t nsec;
+ uint64_t cycles;
+ uint64_t deq_retry;
+ uint64_t enq_retry;
+
+} test_stat_t;
+
+typedef struct test_global_t {
+ odp_barrier_t barrier;
+ test_options_t options;
+ odp_instance_t instance;
+ odp_shm_t shm;
+ odp_pool_t pool;
+ odp_queue_t queue[MAX_QUEUES];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+
+} test_global_t;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Plain queue performance test\n"
+ "\n"
+ "Usage: odp_queue_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of worker threads. Default: 1\n"
+ " -q, --num_queue Number of queues. Default: 1\n"
+ " -e, --num_event Number of events per queue. Default: 1\n"
+ " -b, --burst_size Maximum number of events per operation. Default: 1\n"
+ " -r, --num_round Number of rounds\n"
+ " -l, --lockfree Lockfree queues\n"
+ " -w, --waitfree Waitfree queues\n"
+ " -s, --single Single producer, single consumer\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_queue", required_argument, NULL, 'q'},
+ {"num_event", required_argument, NULL, 'e'},
+ {"burst_size", required_argument, NULL, 'b'},
+ {"num_round", required_argument, NULL, 'r'},
+ {"lockfree", no_argument, NULL, 'l'},
+ {"waitfree", no_argument, NULL, 'w'},
+ {"single", no_argument, NULL, 's'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:q:e:b:r:lwsh";
+
+ test_options->num_cpu = 1;
+ test_options->num_queue = 1;
+ test_options->num_event = 1;
+ test_options->max_burst = 1;
+ test_options->num_round = 1000;
+ test_options->nonblock = ODP_BLOCKING;
+ test_options->single = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'q':
+ test_options->num_queue = atoi(optarg);
+ break;
+ case 'e':
+ test_options->num_event = atoi(optarg);
+ break;
+ case 'b':
+ test_options->max_burst = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atoi(optarg);
+ break;
+ case 'l':
+ test_options->nonblock = ODP_NONBLOCKING_LF;
+ break;
+ case 'w':
+ test_options->nonblock = ODP_NONBLOCKING_WF;
+ break;
+ case 's':
+ test_options->single = 1;
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_queue > MAX_QUEUES) {
+ printf("Too many queues %u. Test maximum %u.\n",
+ test_options->num_queue, MAX_QUEUES);
+ return -1;
+ }
+
+ return ret;
+}
+
+static int create_queues(test_global_t *global)
+{
+ odp_pool_capability_t pool_capa;
+ odp_queue_capability_t queue_capa;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_pool_t pool;
+ uint32_t i, j, max_size, max_num;
+ test_options_t *test_options = &global->options;
+ odp_nonblocking_t nonblock = test_options->nonblock;
+ uint32_t num_queue = test_options->num_queue;
+ uint32_t num_event = test_options->num_event;
+ uint32_t num_round = test_options->num_round;
+ uint32_t tot_event = num_queue * num_event;
+ int ret = 0;
+ odp_queue_t *queue = global->queue;
+ odp_event_t event[tot_event];
+
+ printf("\nTesting %s queues\n",
+ nonblock == ODP_BLOCKING ? "NORMAL" :
+ (nonblock == ODP_NONBLOCKING_LF ? "LOCKFREE" :
+ (nonblock == ODP_NONBLOCKING_WF ? "WAITFREE" : "???")));
+ printf(" num rounds %u\n", num_round);
+ printf(" num queues %u\n", num_queue);
+ printf(" num events per queue %u\n", num_event);
+ printf(" max burst size %u\n", test_options->max_burst);
+
+ for (i = 0; i < num_queue; i++)
+ queue[i] = ODP_QUEUE_INVALID;
+
+ for (i = 0; i < tot_event; i++)
+ event[i] = ODP_EVENT_INVALID;
+
+ if (odp_queue_capability(&queue_capa)) {
+ printf("Error: Queue capa failed.\n");
+ return -1;
+ }
+
+ if (odp_pool_capability(&pool_capa)) {
+ printf("Error: Pool capa failed.\n");
+ return -1;
+ }
+
+ if (nonblock == ODP_BLOCKING) {
+ if (num_queue > queue_capa.plain.max_num) {
+ printf("Max queues supported %u\n",
+ queue_capa.plain.max_num);
+ return -1;
+ }
+
+ max_size = queue_capa.plain.max_size;
+ if (max_size && num_event > max_size) {
+ printf("Max queue size supported %u\n", max_size);
+ return -1;
+ }
+ } else if (nonblock == ODP_NONBLOCKING_LF) {
+ if (queue_capa.plain.lockfree.max_num == 0) {
+ printf("Lockfree queues not supported\n");
+ return -1;
+ }
+
+ if (num_queue > queue_capa.plain.lockfree.max_num) {
+ printf("Max lockfree queues supported %u\n",
+ queue_capa.plain.lockfree.max_num);
+ return -1;
+ }
+
+ max_size = queue_capa.plain.lockfree.max_size;
+ if (max_size && num_event > max_size) {
+ printf("Max lockfree queue size supported %u\n",
+ max_size);
+ return -1;
+ }
+ } else if (nonblock == ODP_NONBLOCKING_WF) {
+ if (queue_capa.plain.waitfree.max_num == 0) {
+ printf("Waitfree queues not supported\n");
+ return -1;
+ }
+
+ if (num_queue > queue_capa.plain.waitfree.max_num) {
+ printf("Max waitfree queues supported %u\n",
+ queue_capa.plain.waitfree.max_num);
+ return -1;
+ }
+
+ max_size = queue_capa.plain.waitfree.max_size;
+ if (max_size && num_event > max_size) {
+ printf("Max waitfree queue size supported %u\n",
+ max_size);
+ return -1;
+ }
+ } else {
+ printf("Error: Bad queue blocking type\n");
+ return -1;
+ }
+
+ max_num = pool_capa.buf.max_num;
+
+ if (max_num && tot_event > max_num) {
+ printf("Error: max events supported %u\n", max_num);
+ return -1;
+ }
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = tot_event;
+
+ pool = odp_pool_create("queue perf pool", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ printf("Error: Pool create failed.\n");
+ return -1;
+ }
+
+ global->pool = pool;
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ queue_param.nonblocking = nonblock;
+ queue_param.size = num_event;
+
+ if (test_options->single) {
+ queue_param.enq_mode = ODP_QUEUE_OP_MT_UNSAFE;
+ queue_param.deq_mode = ODP_QUEUE_OP_MT_UNSAFE;
+ }
+
+ for (i = 0; i < num_queue; i++) {
+ queue[i] = odp_queue_create(NULL, &queue_param);
+
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ printf("Error: Queue create failed %u.\n", i);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < tot_event; i++) {
+ event[i] = odp_buffer_to_event(odp_buffer_alloc(pool));
+
+ if (event[i] == ODP_EVENT_INVALID) {
+ printf("Error: Event alloc failed %u.\n", i);
+ ret = -1;
+ goto free_events;
+ }
+ }
+
+ for (i = 0; i < num_queue; i++) {
+ for (j = 0; j < num_event; j++) {
+ uint32_t id = i * num_event + j;
+
+ if (odp_queue_enq(queue[i], event[id])) {
+ printf("Error: Queue enq failed %u/%u\n", i, j);
+ ret = -1;
+ goto free_events;
+ }
+
+ event[id] = ODP_EVENT_INVALID;
+ }
+ }
+
+free_events:
+ /* Free events that were not stored into queues */
+ for (i = 0; i < tot_event; i++) {
+ if (event[i] != ODP_EVENT_INVALID)
+ odp_event_free(event[i]);
+ }
+
+ return ret;
+}
+
+static int destroy_queues(test_global_t *global)
+{
+ odp_event_t ev;
+ uint32_t i, j;
+ int ret = 0;
+ test_options_t *test_options = &global->options;
+ uint32_t num_queue = test_options->num_queue;
+ uint32_t num_event = test_options->num_event;
+ odp_queue_t *queue = global->queue;
+ odp_pool_t pool = global->pool;
+
+ for (i = 0; i < num_queue; i++) {
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ printf("Error: Invalid queue handle (i: %u).\n", i);
+ break;
+ }
+
+ for (j = 0; j < num_event; j++) {
+ ev = odp_queue_deq(queue[i]);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ }
+
+ if (odp_queue_destroy(queue[i])) {
+ printf("Error: Queue destroy failed %u.\n", i);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (pool != ODP_POOL_INVALID && odp_pool_destroy(pool)) {
+ printf("Error: Pool destroy failed.\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int run_test(void *arg)
+{
+ uint64_t c1, c2, cycles, nsec;
+ odp_time_t t1, t2;
+ uint32_t rounds;
+ int num_ev;
+ test_stat_t *stat;
+ test_global_t *global = arg;
+ test_options_t *test_options = &global->options;
+ odp_queue_t queue;
+ uint64_t num_deq_retry = 0;
+ uint64_t num_enq_retry = 0;
+ uint64_t events = 0;
+ uint32_t num_queue = test_options->num_queue;
+ uint32_t num_round = test_options->num_round;
+ int thr = odp_thread_id();
+ int ret = 0;
+ uint32_t i = 0;
+ uint32_t max_burst = test_options->max_burst;
+ odp_event_t ev[max_burst];
+
+ stat = &global->stat[thr];
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ c1 = odp_cpu_cycles();
+
+ for (rounds = 0; rounds < num_round; rounds++) {
+ int num_enq = 0;
+
+ do {
+ queue = global->queue[i++];
+
+ if (i == num_queue)
+ i = 0;
+
+ num_ev = odp_queue_deq_multi(queue, ev, max_burst);
+
+ if (odp_unlikely(num_ev < 0))
+ ODPH_ABORT("odp_queue_deq_multi() failed\n");
+
+ if (odp_unlikely(num_ev == 0))
+ num_deq_retry++;
+
+ } while (num_ev == 0);
+
+ while (num_enq < num_ev) {
+ int num = odp_queue_enq_multi(queue, &ev[num_enq], num_ev - num_enq);
+
+ if (odp_unlikely(num < 0))
+ ODPH_ABORT("odp_queue_enq_multi() failed\n");
+
+ num_enq += num;
+
+ if (odp_unlikely(num_enq != num_ev))
+ num_enq_retry++;
+ }
+ events += num_ev;
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+ cycles = odp_cpu_cycles_diff(c2, c1);
+
+ stat->rounds = rounds;
+ stat->events = events;
+ stat->nsec = nsec;
+ stat->cycles = cycles;
+ stat->deq_retry = num_deq_retry;
+ stat->enq_retry = num_enq_retry;
+
+ return ret;
+}
+
+static int start_workers(test_global_t *global)
+{
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_cpumask_t cpumask;
+ int ret;
+ test_options_t *test_options = &global->options;
+ int num_cpu = test_options->num_cpu;
+
+ ret = odp_cpumask_default_worker(&cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ printf("Error: Too many workers. Max supported %i\n.", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ printf(" num workers %u\n\n", num_cpu);
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = global->instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_test;
+ thr_param.arg = global;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ if (odph_thread_create(global->thread_tbl, &thr_common, &thr_param,
+ num_cpu) != num_cpu)
+ return -1;
+
+ return 0;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double rounds_ave, events_ave, nsec_ave, cycles_ave;
+ test_options_t *test_options = &global->options;
+ int num_cpu = test_options->num_cpu;
+ uint64_t rounds_sum = 0;
+ uint64_t events_sum = 0;
+ uint64_t nsec_sum = 0;
+ uint64_t cycles_sum = 0;
+ uint64_t deq_retry_sum = 0;
+ uint64_t enq_retry_sum = 0;
+
+ /* Averages */
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ rounds_sum += global->stat[i].rounds;
+ events_sum += global->stat[i].events;
+ nsec_sum += global->stat[i].nsec;
+ cycles_sum += global->stat[i].cycles;
+ deq_retry_sum += global->stat[i].deq_retry;
+ enq_retry_sum += global->stat[i].enq_retry;
+ }
+
+ if (rounds_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ rounds_ave = rounds_sum / num_cpu;
+ events_ave = events_sum / num_cpu;
+ nsec_ave = nsec_sum / num_cpu;
+ cycles_ave = cycles_sum / num_cpu;
+ num = 0;
+
+ printf("RESULTS - per thread (Million events per sec):\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (1000.0 * global->stat[i].events) /
+ global->stat[i].nsec);
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("RESULTS - per thread average (%i threads):\n", num_cpu);
+ printf("------------------------------------------\n");
+ printf(" duration: %.3f msec\n", nsec_ave / 1000000);
+ printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
+ printf(" events per dequeue: %.3f\n",
+ events_ave / rounds_ave);
+ printf(" cycles per event: %.3f\n",
+ cycles_ave / events_ave);
+ printf(" dequeue retries: %" PRIu64 "\n", deq_retry_sum);
+ printf(" enqueue retries: %" PRIu64 "\n", enq_retry_sum);
+ printf(" events per sec: %.3f M\n\n",
+ (1000.0 * events_ave) / nsec_ave);
+
+ printf("TOTAL events per sec: %.3f M\n\n",
+ (1000.0 * events_sum) / nsec_ave);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ printf("Error: Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_WORKER)) {
+ printf("Error: Local init failed.\n");
+ return -1;
+ }
+
+ shm = odp_shm_reserve("queue_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Error: Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(test_global_t));
+
+ if (parse_options(argc, argv, &global->options))
+ return -1;
+
+ odp_sys_info_print();
+
+ global->instance = instance;
+
+ if (create_queues(global)) {
+ printf("Error: Create queues failed.\n");
+ goto destroy;
+ }
+
+ if (start_workers(global)) {
+ printf("Error: Test start failed.\n");
+ return -1;
+ }
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, global->options.num_cpu);
+
+ print_stat(global);
+
+destroy:
+ if (destroy_queues(global)) {
+ printf("Error: Destroy queues failed.\n");
+ return -1;
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: Shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ printf("Error: term local failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Error: term global failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_random.c b/test/performance/odp_random.c
new file mode 100644
index 000000000..119ef41aa
--- /dev/null
+++ b/test/performance/odp_random.c
@@ -0,0 +1,549 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+/**
+ * @example odp_random.c
+ *
+ * Performance test application for random data APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define PSEUDO_RANDOM (-1)
+
+#define MB (1024ull * 1024ull)
+
+typedef struct test_global_t test_global_t;
+
+typedef struct thread_arg_t {
+ test_global_t *global;
+ int thread_idx;
+ uint8_t *data;
+
+} thread_arg_t;
+
+struct test_global_t {
+ odp_barrier_t barrier;
+ odp_random_kind_t type;
+ uint8_t *data;
+ uint32_t rounds;
+
+ thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
+
+ struct {
+ uint64_t nsec[ODP_THREAD_COUNT_MAX];
+ uint64_t sum[ODP_THREAD_COUNT_MAX];
+ uint64_t min[ODP_THREAD_COUNT_MAX];
+ uint64_t max[ODP_THREAD_COUNT_MAX];
+ } stat;
+};
+
+/* Command line options */
+typedef struct {
+ int mode;
+ int num_threads;
+ uint32_t size;
+ uint32_t rounds;
+ uint64_t delay;
+
+} options_t;
+
+static options_t options;
+static const options_t options_def = {
+ .mode = 0,
+ .num_threads = 1,
+ .size = 256,
+ .rounds = 100000,
+ .delay = 0,
+};
+
+static void print_usage(void)
+{
+ printf("\n"
+ "random data performance test\n"
+ "\n"
+ "Usage: odp_random [options]\n"
+ "\n"
+ " -m, --mode Test mode select (default: 0):\n"
+ " 0: Data throughput\n"
+ " 1: Data generation latency (size: 8B by default)\n"
+ " -t, --threads Number of worker threads (default %u)\n"
+ " -s, --size Size of buffer in bytes (default %u)\n"
+ " -r, --rounds Number of test rounds (default %u)\n"
+ " Divided by 100 for ODP_RANDOM_TRUE\n"
+ " -d, --delay Delay (nsec) between buffer fills (default %" PRIu64 ").\n"
+ " Affects only latency mode.\n"
+ " -h, --help This help\n"
+ "\n",
+ options_def.num_threads, options_def.size, options_def.rounds, options_def.delay);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "threads", required_argument, NULL, 't' },
+ { "size", required_argument, NULL, 's' },
+ { "rounds", required_argument, NULL, 'r' },
+ { "delay", required_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+m:t:s:r:d:h";
+
+ options = options_def;
+ options.size = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'm':
+ options.mode = atoi(optarg);
+ break;
+ case 't':
+ options.num_threads = atol(optarg);
+ break;
+ case 's':
+ options.size = atol(optarg);
+ break;
+ case 'r':
+ options.rounds = atol(optarg);
+ break;
+ case 'd':
+ options.delay = atol(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (options.num_threads < 1 || options.num_threads > ODP_THREAD_COUNT_MAX) {
+ ODPH_ERR("Bad number of threads: %i\n", options.num_threads);
+ return -1;
+ }
+
+ if (options.size == 0) {
+ options.size = options_def.size;
+
+ if (options.mode)
+ options.size = 8;
+ }
+
+ printf("\nOptions:\n");
+ printf("------------------------\n");
+ printf(" mode: %i\n", options.mode);
+ printf(" threads: %i\n", options.num_threads);
+ printf(" size: %u\n", options.size);
+ printf(" rounds: %u\n", options.rounds);
+ printf(" delay: %" PRIu64 "\n", options.delay);
+ printf("\n");
+
+ return ret;
+}
+
+static inline void random_data_loop(odp_random_kind_t type, uint32_t rounds,
+ uint8_t *data, uint32_t size)
+{
+ uint32_t i;
+ int32_t ret;
+
+ if ((int)type == PSEUDO_RANDOM) {
+ uint64_t seed = 0;
+
+ for (i = 0; i < rounds; i++) {
+ uint32_t pos = 0;
+
+ while (pos < size) {
+ ret = odp_random_test_data(data + pos, size - pos, &seed);
+
+ if (ret < 0) {
+ ODPH_ERR("odp_random_test_data() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pos += ret;
+ }
+ }
+ } else {
+ for (i = 0; i < rounds; i++) {
+ uint32_t pos = 0;
+
+ while (pos < size) {
+ ret = odp_random_data(data + pos, size - pos, type);
+
+ if (ret < 0) {
+ ODPH_ERR("odp_random_data() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pos += ret;
+ }
+ }
+ }
+}
+
+static int test_random_perf(void *ptr)
+{
+ odp_time_t start;
+ uint64_t nsec;
+ thread_arg_t *thread_arg = ptr;
+ test_global_t *global = thread_arg->global;
+ odp_random_kind_t type = global->type;
+ int thread_idx = thread_arg->thread_idx;
+ uint8_t *data = thread_arg->data;
+ uint32_t size = options.size;
+ uint32_t rounds = global->rounds;
+
+ /* One warm up round */
+ random_data_loop(type, 1, data, size);
+
+ odp_barrier_wait(&global->barrier);
+
+ /* Test run */
+ start = odp_time_local();
+
+ random_data_loop(type, rounds, data, size);
+
+ nsec = odp_time_diff_ns(odp_time_local(), start);
+
+ global->stat.nsec[thread_idx] = nsec;
+
+ return 0;
+}
+
+static inline void random_data_latency(test_global_t *global, int thread_idx,
+ uint32_t rounds, uint8_t *data, uint32_t size)
+{
+ uint32_t i;
+ int32_t ret;
+ odp_time_t t1, t2, start;
+ uint64_t nsec;
+ odp_random_kind_t type = global->type;
+ uint64_t delay = options.delay;
+ uint64_t min = UINT64_MAX;
+ uint64_t max = 0;
+ uint64_t sum = 0;
+ uint64_t seed = 0;
+
+ start = odp_time_local();
+
+ for (i = 0; i < rounds; i++) {
+ uint32_t pos = 0;
+
+ if (delay)
+ odp_time_wait_ns(delay);
+
+ if ((int)type == PSEUDO_RANDOM) {
+ t1 = odp_time_local_strict();
+ while (pos < size) {
+ ret = odp_random_test_data(data + pos, size - pos, &seed);
+
+ if (ret < 0) {
+ ODPH_ERR("odp_random_test_data() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pos += ret;
+ }
+ t2 = odp_time_local_strict();
+ } else {
+ t1 = odp_time_local_strict();
+ while (pos < size) {
+ ret = odp_random_data(data + pos, size - pos, type);
+
+ if (ret < 0) {
+ ODPH_ERR("odp_random_data() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pos += ret;
+ }
+ t2 = odp_time_local_strict();
+ }
+
+ nsec = odp_time_diff_ns(t2, t1);
+ sum += nsec;
+
+ if (nsec > max)
+ max = nsec;
+ if (nsec < min)
+ min = nsec;
+ }
+
+ nsec = odp_time_diff_ns(odp_time_local(), start);
+
+ global->stat.nsec[thread_idx] = nsec;
+ global->stat.sum[thread_idx] = sum;
+ global->stat.min[thread_idx] = min;
+ global->stat.max[thread_idx] = max;
+}
+
+static int test_random_latency(void *ptr)
+{
+ thread_arg_t *thread_arg = ptr;
+ test_global_t *global = thread_arg->global;
+ odp_random_kind_t type = global->type;
+ int thread_idx = thread_arg->thread_idx;
+ uint8_t *data = thread_arg->data;
+ uint32_t size = options.size;
+ uint32_t rounds = global->rounds;
+
+ /* One warm up round */
+ random_data_loop(type, 1, data, size);
+
+ odp_barrier_wait(&global->barrier);
+
+ /* Test run */
+ random_data_latency(global, thread_idx, rounds, data, size);
+
+ return 0;
+}
+
+static uint32_t type_rounds(odp_random_kind_t type)
+{
+ switch (type) {
+ case ODP_RANDOM_TRUE:
+ return options.rounds / 100;
+ default:
+ return options.rounds;
+ }
+}
+
+static void test_type(odp_instance_t instance, test_global_t *global, odp_random_kind_t type)
+{
+ int i;
+ int num_threads = options.num_threads;
+ uint32_t rounds = type_rounds(type);
+ uint32_t size = options.size;
+
+ memset(&global->stat, 0, sizeof(global->stat));
+ global->type = type;
+ global->rounds = rounds;
+ odp_barrier_init(&global->barrier, num_threads);
+
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[num_threads];
+ odph_thread_t thr_worker[num_threads];
+
+ if (odp_cpumask_default_worker(&cpumask, num_threads) != num_threads) {
+ ODPH_ERR("Failed to get default CPU mask.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+
+ for (i = 0; i < num_threads; i++) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ thr_param[i].arg = &global->thread_arg[i];
+
+ if (options.mode == 0)
+ thr_param[i].start = test_random_perf;
+ else
+ thr_param[i].start = test_random_latency;
+ }
+
+ memset(&thr_worker, 0, sizeof(thr_worker));
+
+ if (odph_thread_create(thr_worker, &thr_common, thr_param, num_threads) != num_threads) {
+ ODPH_ERR("Failed to create worker threads.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odph_thread_join(thr_worker, num_threads) != num_threads) {
+ ODPH_ERR("Failed to join worker threads.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ double mb, seconds, nsec = 0;
+
+ for (i = 0; i < num_threads; i++)
+ nsec += global->stat.nsec[i];
+
+ nsec /= num_threads;
+
+ switch (type) {
+ case ODP_RANDOM_BASIC:
+ printf("ODP_RANDOM_BASIC\n");
+ break;
+ case ODP_RANDOM_CRYPTO:
+ printf("ODP_RANDOM_CRYPTO\n");
+ break;
+ case ODP_RANDOM_TRUE:
+ printf("ODP_RANDOM_TRUE\n");
+ break;
+ default:
+ printf("odp_random_test_data\n");
+ }
+
+ printf("--------------------\n");
+ printf("threads: %d size: %u B rounds: %u ", num_threads, size, rounds);
+ mb = (uint64_t)num_threads * (uint64_t)size * (uint64_t)rounds;
+ mb /= MB;
+ seconds = (double)nsec / (double)ODP_TIME_SEC_IN_NS;
+ printf("MB: %.3f seconds: %.3f ", mb, seconds);
+ printf("MB/s: %.3f ", mb / seconds);
+ printf("MB/s/thread: %.3f\n", mb / seconds / (double)num_threads);
+
+ if (options.mode) {
+ double ave;
+ uint64_t min = UINT64_MAX;
+ uint64_t max = 0;
+ uint64_t sum = 0;
+
+ printf(" latency (nsec)\n");
+ printf(" thread min max ave\n");
+ for (i = 0; i < num_threads; i++) {
+ ave = (double)global->stat.sum[i] / rounds;
+ sum += global->stat.sum[i];
+
+ if (global->stat.min[i] < min)
+ min = global->stat.min[i];
+
+ if (global->stat.max[i] > max)
+ max = global->stat.max[i];
+
+ printf("%8i %8" PRIu64 " %8" PRIu64 " %10.1f\n", i, global->stat.min[i],
+ global->stat.max[i], ave);
+ }
+
+ printf(" all %8" PRIu64 " %8" PRIu64 " %10.1f\n",
+ min, max, ((double)sum / rounds) / num_threads);
+ }
+
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm_glb, shm_data;
+ test_global_t *global;
+ int num_threads, i;
+ uint64_t tot_size, size;
+ uint8_t *addr;
+
+ if (parse_options(argc, argv))
+ exit(EXIT_FAILURE);
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.stash = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_sys_info_print();
+
+ global = NULL;
+ shm_glb = odp_shm_reserve("test_globals", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm_glb != ODP_SHM_INVALID)
+ global = (test_global_t *)odp_shm_addr(shm_glb);
+
+ if (!global) {
+ ODPH_ERR("Failed to reserve shm\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(test_global_t));
+
+ num_threads = options.num_threads;
+ addr = NULL;
+ size = ODP_CACHE_LINE_SIZE + ODP_CACHE_LINE_ROUNDUP(options.size);
+ tot_size = num_threads * size;
+ shm_data = odp_shm_reserve("test_data", tot_size, ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm_data != ODP_SHM_INVALID)
+ addr = odp_shm_addr(shm_data);
+
+ if (!addr) {
+ ODPH_ERR("Failed to reserve shm: size %" PRIu64 " bytes\n", tot_size);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ global->thread_arg[i].global = global;
+ global->thread_arg[i].thread_idx = i;
+ global->thread_arg[i].data = addr + i * size;
+ }
+
+ odp_shm_print_all();
+
+ switch (odp_random_max_kind()) {
+ case ODP_RANDOM_TRUE:
+ test_type(instance, global, ODP_RANDOM_TRUE);
+ /* fall through */
+ case ODP_RANDOM_CRYPTO:
+ test_type(instance, global, ODP_RANDOM_CRYPTO);
+ /* fall through */
+ default:
+ test_type(instance, global, ODP_RANDOM_BASIC);
+ test_type(instance, global, PSEUDO_RANDOM);
+ }
+
+ if (odp_shm_free(shm_data)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm_glb)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_sched_latency.c b/test/performance/odp_sched_latency.c
new file mode 100644
index 000000000..f3230cc17
--- /dev/null
+++ b/test/performance/odp_sched_latency.c
@@ -0,0 +1,1070 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2016-2018 Linaro Limited
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+/**
+ * @example odp_sched_latency.c
+ *
+ * Scheduling latency benchmark application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+/* ODP main header */
+#include <odp_api.h>
+
+/* ODP helper for Linux apps */
+#include <odp/helper/odph_api.h>
+
+/* GNU lib C */
+#include <getopt.h>
+
+#define MAX_QUEUES 4096 /**< Maximum number of queues */
+#define MAX_GROUPS 64
+#define EVENT_POOL_SIZE (1024 * 1024) /**< Event pool size */
+#define MAIN_THREAD 1 /**< Thread ID performing maintenance tasks */
+
+#define CACHE_ALIGN_ROUNDUP(x)\
+ ((ODP_CACHE_LINE_SIZE) * \
+ (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE)))
+
+/* Test priorities */
+#define NUM_PRIOS 2 /**< Number of tested priorities */
+#define HI_PRIO 0
+#define LO_PRIO 1
+
+/* Test event forwarding mode */
+#define EVENT_FORWARD_RAND 0
+#define EVENT_FORWARD_INC 1
+#define EVENT_FORWARD_NONE 2
+
+/** Test event types */
+typedef enum {
+ WARM_UP, /**< Warm-up event */
+ COOL_DOWN,/**< Last event on queue */
+ TRAFFIC, /**< Event used only as traffic load */
+ SAMPLE /**< Event used to measure latency */
+} event_type_t;
+
+/** Test event */
+typedef struct {
+ odp_time_t time_stamp; /**< Send timestamp */
+ event_type_t type; /**< Message type */
+ int src_idx[NUM_PRIOS]; /**< Source ODP queue */
+ int prio; /**< Source queue priority */
+ int warm_up_rounds; /**< Number of completed warm-up rounds */
+} test_event_t;
+
+/** Test arguments */
+typedef struct {
+ unsigned int cpu_count; /**< CPU count */
+ odp_schedule_sync_t sync_type; /**< Scheduler sync type */
+ int forward_mode; /**< Event forwarding mode */
+ int num_group;
+ int isolate;
+ int test_rounds; /**< Number of test rounds (millions) */
+ int warm_up_rounds; /**< Number of warm-up rounds */
+ struct {
+ int queues; /**< Number of scheduling queues */
+ int events; /**< Number of events */
+ int sample_events;
+ odp_bool_t events_per_queue; /**< Allocate 'queues' x 'events'
+ test events */
+ } prio[NUM_PRIOS];
+ odp_bool_t sample_per_prio; /**< Allocate a separate sample event for
+ each priority */
+} test_args_t;
+
+/** Latency measurements statistics */
+typedef struct {
+ uint64_t events; /**< Total number of received events */
+ uint64_t sample_events; /**< Number of received sample events */
+ uint64_t tot; /**< Total event latency. Sum of all events. */
+ uint64_t min; /**< Minimum event latency */
+ uint64_t max; /**< Maximum event latency */
+ uint64_t max_idx; /**< Index of the maximum latency sample event */
+} test_stat_t;
+
+/** Performance test statistics (per core) */
+typedef struct ODP_ALIGNED_CACHE {
+ test_stat_t prio[NUM_PRIOS]; /**< Test statistics per priority */
+} core_stat_t;
+
+/** Test global variables */
+typedef struct {
+ /** Core specific stats */
+ core_stat_t core_stat[ODP_THREAD_COUNT_MAX];
+ odp_barrier_t barrier; /**< Barrier for thread synchronization */
+ odp_pool_t pool; /**< Pool for allocating test events */
+ test_args_t args; /**< Parsed command line arguments */
+ odp_queue_t queue[NUM_PRIOS][MAX_QUEUES]; /**< Scheduled queues */
+
+ odp_schedule_group_t group[NUM_PRIOS][MAX_GROUPS];
+
+} test_globals_t;
+
+/**
+ * Clear all scheduled queues.
+ *
+ * Use special cool_down event to guarantee that queue is drained.
+ */
+static void clear_sched_queues(test_globals_t *globals)
+{
+ odp_event_t ev;
+ odp_buffer_t buf;
+ test_event_t *event;
+ int i, j;
+ odp_queue_t fromq;
+
+ /* Allocate the cool_down event. */
+ buf = odp_buffer_alloc(globals->pool);
+ if (buf == ODP_BUFFER_INVALID)
+ ODPH_ABORT("Buffer alloc failed.\n");
+
+ event = odp_buffer_addr(buf);
+ event->type = COOL_DOWN;
+ ev = odp_buffer_to_event(buf);
+
+ for (i = 0; i < NUM_PRIOS; i++) {
+ for (j = 0; j < globals->args.prio[i].queues; j++) {
+ /* Enqueue cool_down event on each queue. */
+ if (odp_queue_enq(globals->queue[i][j], ev))
+ ODPH_ABORT("Queue enqueue failed.\n");
+
+ /* Invoke scheduler until cool_down event has been
+ * received. */
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_WAIT);
+ buf = odp_buffer_from_event(ev);
+ event = odp_buffer_addr(buf);
+ if (event->type == COOL_DOWN)
+ break;
+ odp_event_free(ev);
+ }
+ }
+ }
+
+ /* Free the cool_down event. */
+ odp_event_free(ev);
+
+ /* Call odp_schedule() to trigger a release of any scheduler context. */
+ ev = odp_schedule(&fromq, ODP_SCHED_NO_WAIT);
+ if (ev != ODP_EVENT_INVALID)
+ ODPH_ABORT("Queue %" PRIu64 " not empty.\n",
+ odp_queue_to_u64(fromq));
+}
+
+/**
+ * Enqueue events into queues
+ *
+ * @param prio Queue priority (HI_PRIO/LO_PRIO)
+ * @param num_queues Number of queues
+ * @param num_events Number of 'TRAFFIC' events
+ * @param num_samples Number of 'SAMPLE' events
+ * @param div_events If true, divide 'num_events' between 'num_queues'. if
+ * false, enqueue 'num_events' to each queue.
+ * @param globals Test shared data
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int enqueue_events(int prio, int num_queues, int num_events,
+ int num_samples, odp_bool_t div_events,
+ test_globals_t *globals)
+{
+ odp_buffer_t buf[num_events + num_samples];
+ odp_event_t ev[num_events + num_samples];
+ odp_queue_t queue;
+ test_event_t *event;
+ int i, j, ret;
+ int enq_events;
+ int events_per_queue;
+ int tot_events;
+ int rdy_events = 0;
+
+ tot_events = num_events + num_samples;
+
+ if (!num_queues || !tot_events)
+ return 0;
+
+ events_per_queue = tot_events;
+ if (div_events)
+ events_per_queue = (tot_events + num_queues - 1) / num_queues;
+
+ for (i = 0; i < num_queues; i++) {
+ queue = globals->queue[prio][i];
+
+ ret = odp_buffer_alloc_multi(globals->pool, buf,
+ events_per_queue);
+ if (ret != events_per_queue) {
+ ODPH_ERR("Buffer alloc failed. Try increasing EVENT_POOL_SIZE.\n");
+ ret = ret < 0 ? 0 : ret;
+ odp_buffer_free_multi(buf, ret);
+ return -1;
+ }
+ for (j = 0; j < events_per_queue; j++) {
+ if (!odp_buffer_is_valid(buf[j])) {
+ ODPH_ERR("Buffer alloc failed\n");
+ odp_buffer_free_multi(buf, events_per_queue);
+ return -1;
+ }
+
+ event = odp_buffer_addr(buf[j]);
+ memset(event, 0, sizeof(test_event_t));
+
+ /* Latency isn't measured from the first processing
+ * rounds. */
+ if (num_samples > 0) {
+ event->type = WARM_UP;
+ event->warm_up_rounds = 0;
+ num_samples--;
+ } else {
+ event->type = TRAFFIC;
+ }
+ event->src_idx[prio] = i;
+ event->prio = prio;
+ ev[j] = odp_buffer_to_event(buf[j]);
+ }
+
+ enq_events = 0;
+ do {
+ ret = odp_queue_enq_multi(queue, &ev[enq_events],
+ events_per_queue -
+ enq_events);
+ if (ret < 0) {
+ ODPH_ERR("Queue enqueue failed.\n");
+ return -1;
+ }
+ enq_events += ret;
+ } while (enq_events < events_per_queue);
+
+ rdy_events += events_per_queue;
+ if (div_events && rdy_events >= tot_events)
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Print latency measurement results
+ *
+ * @param globals Test shared data
+ */
+static void print_results(test_globals_t *globals)
+{
+ test_stat_t *lat;
+ odp_schedule_sync_t stype;
+ test_stat_t total;
+ test_args_t *args;
+ uint64_t avg;
+ unsigned int i, j;
+
+ args = &globals->args;
+ stype = globals->args.sync_type;
+
+ printf("\n%s queue scheduling latency\n",
+ (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" :
+ ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL"));
+
+ printf(" Forwarding mode: %s\n",
+ (args->forward_mode == EVENT_FORWARD_RAND) ? "random" :
+ ((args->forward_mode == EVENT_FORWARD_INC) ? "incremental" :
+ "none"));
+
+ printf(" LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues);
+ if (args->prio[LO_PRIO].events_per_queue)
+ printf(" LO_PRIO event per queue: %i\n",
+ args->prio[LO_PRIO].events);
+ else
+ printf(" LO_PRIO events: %i\n", args->prio[LO_PRIO].events);
+
+ printf(" LO_PRIO sample events: %i\n", args->prio[LO_PRIO].sample_events);
+
+ printf(" HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues);
+ if (args->prio[HI_PRIO].events_per_queue)
+ printf(" HI_PRIO event per queue: %i\n\n",
+ args->prio[HI_PRIO].events);
+ else
+ printf(" HI_PRIO events: %i\n", args->prio[HI_PRIO].events);
+
+ printf(" HI_PRIO sample events: %i\n\n", args->prio[HI_PRIO].sample_events);
+
+ for (i = 0; i < NUM_PRIOS; i++) {
+ memset(&total, 0, sizeof(test_stat_t));
+ total.min = UINT64_MAX;
+
+ printf("%s priority\n"
+ "Thread Avg[ns] Min[ns] Max[ns] Samples Total Max idx\n"
+ "-----------------------------------------------------------------------\n",
+ i == HI_PRIO ? "HIGH" : "LOW");
+ for (j = 1; j <= args->cpu_count; j++) {
+ lat = &globals->core_stat[j].prio[i];
+
+ if (lat->sample_events == 0) {
+ printf("%-8d N/A\n", j);
+ continue;
+ }
+
+ if (lat->max > total.max)
+ total.max = lat->max;
+ if (lat->min < total.min)
+ total.min = lat->min;
+ total.tot += lat->tot;
+ total.sample_events += lat->sample_events;
+ total.events += lat->events;
+
+ avg = lat->events ? lat->tot / lat->sample_events : 0;
+ printf("%-8d %-10" PRIu64 " %-10" PRIu64 " "
+ "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n",
+ j, avg, lat->min, lat->max, lat->sample_events,
+ lat->events, lat->max_idx);
+ }
+ printf("-----------------------------------------------------------------------\n");
+ if (total.sample_events == 0) {
+ printf("Total N/A\n\n");
+ continue;
+ }
+ avg = total.events ? total.tot / total.sample_events : 0;
+ printf("Total %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " "
+ "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, total.min,
+ total.max, total.sample_events, total.events);
+ }
+}
+
+static int join_groups(test_globals_t *globals, int thr)
+{
+ odp_thrmask_t thrmask;
+ odp_schedule_group_t group;
+ int i, num;
+ int num_group = globals->args.num_group;
+
+ if (num_group <= 0)
+ return 0;
+
+ num = num_group;
+ if (globals->args.isolate)
+ num = 2 * num_group;
+
+ odp_thrmask_zero(&thrmask);
+ odp_thrmask_set(&thrmask, thr);
+
+ for (i = 0; i < num; i++) {
+ if (globals->args.isolate)
+ group = globals->group[i % 2][i / 2];
+ else
+ group = globals->group[0][i];
+
+ if (odp_schedule_group_join(group, &thrmask)) {
+ ODPH_ERR("Group join failed %i (thr %i)\n", i, thr);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Measure latency of scheduled ODP events
+ *
+ * Schedule and enqueue events until 'test_rounds' events have been processed.
+ * Scheduling latency is measured only from type 'SAMPLE' events. Other events
+ * are simply enqueued back to the scheduling queues.
+ *
+ * For 'TRAFFIC' type events the destination queue is selected from the same
+ * priority class as source queue. 'SAMPLE' type event may change priority
+ * depending on the command line arguments.
+ *
+ * @param thr Thread ID
+ * @param globals Test shared data
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int test_schedule(int thr, test_globals_t *globals)
+{
+ odp_time_t time;
+ odp_event_t ev;
+ odp_buffer_t buf;
+ odp_queue_t dst_queue;
+ uint64_t latency;
+ uint64_t i;
+ test_event_t *event;
+ test_stat_t *stats;
+ int dst_idx, change_queue;
+ int warm_up_rounds = globals->args.warm_up_rounds;
+ uint64_t test_rounds = globals->args.test_rounds * (uint64_t)1000000;
+
+ memset(&globals->core_stat[thr], 0, sizeof(core_stat_t));
+ globals->core_stat[thr].prio[HI_PRIO].min = UINT64_MAX;
+ globals->core_stat[thr].prio[LO_PRIO].min = UINT64_MAX;
+
+ change_queue = globals->args.forward_mode != EVENT_FORWARD_NONE ? 1 : 0;
+
+ odp_barrier_wait(&globals->barrier);
+
+ for (i = 0; i < test_rounds; i++) {
+ ev = odp_schedule(NULL, ODP_SCHED_WAIT);
+
+ time = odp_time_global_strict();
+
+ buf = odp_buffer_from_event(ev);
+ event = odp_buffer_addr(buf);
+
+ stats = &globals->core_stat[thr].prio[event->prio];
+
+ if (event->type == SAMPLE) {
+ latency = odp_time_to_ns(time) - odp_time_to_ns(event->time_stamp);
+
+ if (latency > stats->max) {
+ stats->max = latency;
+ stats->max_idx = stats->sample_events;
+ }
+ if (latency < stats->min)
+ stats->min = latency;
+ stats->tot += latency;
+ stats->sample_events++;
+
+ /* Move sample event to a different priority */
+ if (!globals->args.sample_per_prio &&
+ globals->args.prio[!event->prio].queues)
+ event->prio = !event->prio;
+ }
+
+ if (odp_unlikely(event->type == WARM_UP)) {
+ event->warm_up_rounds++;
+ if (event->warm_up_rounds >= warm_up_rounds)
+ event->type = SAMPLE;
+ } else {
+ stats->events++;
+ }
+
+ /* Move event to next queue if forwarding is enabled */
+ if (change_queue)
+ dst_idx = event->src_idx[event->prio] + 1;
+ else
+ dst_idx = event->src_idx[event->prio];
+ if (dst_idx >= globals->args.prio[event->prio].queues)
+ dst_idx = 0;
+ event->src_idx[event->prio] = dst_idx;
+ dst_queue = globals->queue[event->prio][dst_idx];
+
+ if (event->type == SAMPLE)
+ event->time_stamp = odp_time_global_strict();
+
+ if (odp_queue_enq(dst_queue, ev)) {
+ ODPH_ERR("[%i] Queue enqueue failed.\n", thr);
+ odp_event_free(ev);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ while (1) {
+ odp_queue_t src_queue;
+
+ ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ if (odp_queue_enq(src_queue, ev)) {
+ ODPH_ERR("[%i] Queue enqueue failed.\n", thr);
+ odp_event_free(ev);
+ return -1;
+ }
+ }
+
+ odp_barrier_wait(&globals->barrier);
+
+ if (thr == MAIN_THREAD) {
+ odp_schedule_resume();
+ clear_sched_queues(globals);
+ print_results(globals);
+ }
+
+ return 0;
+}
+
+/**
+ * Worker thread
+ *
+ * @param arg Arguments
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int run_thread(void *arg ODP_UNUSED)
+{
+ odp_shm_t shm;
+ test_globals_t *globals;
+ test_args_t *args;
+ int thr;
+
+ thr = odp_thread_id();
+
+ shm = odp_shm_lookup("test_globals");
+ globals = odp_shm_addr(shm);
+
+ if (globals == NULL) {
+ ODPH_ERR("Shared mem lookup failed\n");
+ return -1;
+ }
+
+ if (join_groups(globals, thr))
+ return -1;
+
+ if (thr == MAIN_THREAD) {
+ args = &globals->args;
+
+ if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues,
+ args->prio[HI_PRIO].events, args->prio[HI_PRIO].sample_events,
+ !args->prio[HI_PRIO].events_per_queue,
+ globals))
+ return -1;
+
+ if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues,
+ args->prio[LO_PRIO].events, args->prio[LO_PRIO].sample_events,
+ !args->prio[LO_PRIO].events_per_queue,
+ globals))
+ return -1;
+ }
+
+ if (test_schedule(thr, globals))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Print usage information
+ */
+static void usage(void)
+{
+ printf("\n"
+ "OpenDataPlane scheduler latency benchmark application.\n"
+ "\n"
+ "Usage: ./odp_sched_latency [options]\n"
+ "Optional OPTIONS:\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -d, --duration <number> Test duration in scheduling rounds (millions), default=10, min=1\n"
+ " -f, --forward-mode <mode> Selection of target queue\n"
+ " 0: Random (default)\n"
+ " 1: Incremental\n"
+ " 2: Use source queue\n"
+ " -g, --num_group <num> Number of schedule groups. Round robins queues into groups.\n"
+ " -1: SCHED_GROUP_WORKER\n"
+ " 0: SCHED_GROUP_ALL (default)\n"
+ " -i, --isolate <mode> Select if shared or isolated groups are used. Ignored when num_group <= 0.\n"
+ " 0: All queues share groups (default)\n"
+ " 1: Separate groups for high and low priority queues. Creates 2xnum_group groups.\n"
+ " -l, --lo-prio-queues <number> Number of low priority scheduled queues (default=64)\n"
+ " -t, --hi-prio-queues <number> Number of high priority scheduled queues (default=16)\n"
+ " -m, --lo-prio-events-per-queue <number> Number of events per low priority queue (default=32).\n"
+ " Does not include sample event.\n"
+ " -n, --hi-prio-events-per-queue <number> Number of events per high priority queues (default=0)\n"
+ " Does not include sample event.\n"
+ " -o, --lo-prio-events <number> Total number of low priority events. Overrides the\n"
+ " number of events per queue, does not include sample event.\n"
+ " -p, --hi-prio-events <number> Total number of high priority events. Overrides the\n"
+ " number of events per queue, does not include sample event.\n"
+ " -r --sample-per-prio Allocate a separate sample event for each priority. By default\n"
+ " a single sample event is used and its priority is changed after\n"
+ " each processing round.\n"
+ " -s, --sync Scheduled queues' sync type\n"
+ " 0: ODP_SCHED_SYNC_PARALLEL (default)\n"
+ " 1: ODP_SCHED_SYNC_ATOMIC\n"
+ " 2: ODP_SCHED_SYNC_ORDERED\n"
+ " -w, --warm-up <number> Number of warm-up rounds, default=100, min=1\n"
+ " -h, --help Display help and exit.\n\n");
+}
+
+/**
+ * Parse arguments
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @param args Test arguments
+ */
+static void parse_args(int argc, char *argv[], test_args_t *args)
+{
+ int opt;
+ int long_index;
+ int i;
+
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"duration", required_argument, NULL, 'd'},
+ {"forward-mode", required_argument, NULL, 'f'},
+ {"num_group", required_argument, NULL, 'g'},
+ {"isolate", required_argument, NULL, 'i'},
+ {"lo-prio-queues", required_argument, NULL, 'l'},
+ {"hi-prio-queues", required_argument, NULL, 't'},
+ {"lo-prio-events-per-queue", required_argument, NULL, 'm'},
+ {"hi-prio-events-per-queue", required_argument, NULL, 'n'},
+ {"lo-prio-events", required_argument, NULL, 'o'},
+ {"hi-prio-events", required_argument, NULL, 'p'},
+ {"sync", required_argument, NULL, 's'},
+ {"warm-up", required_argument, NULL, 'w'},
+ {"sample-per-prio", no_argument, NULL, 'r'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:d:f:g:i:l:t:m:n:o:p:s:w:rh";
+
+ args->cpu_count = 1;
+ args->forward_mode = EVENT_FORWARD_RAND;
+ args->num_group = 0;
+ args->isolate = 0;
+ args->test_rounds = 10;
+ args->warm_up_rounds = 100;
+ args->sync_type = ODP_SCHED_SYNC_PARALLEL;
+ args->sample_per_prio = 0;
+ args->prio[LO_PRIO].queues = 64;
+ args->prio[HI_PRIO].queues = 16;
+ args->prio[LO_PRIO].events = 32;
+ args->prio[HI_PRIO].events = 0;
+ args->prio[LO_PRIO].events_per_queue = 1;
+ args->prio[HI_PRIO].events_per_queue = 0;
+ args->prio[LO_PRIO].sample_events = 0;
+ args->prio[HI_PRIO].sample_events = 1;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ args->cpu_count = atoi(optarg);
+ break;
+ case 'd':
+ args->test_rounds = atoi(optarg);
+ break;
+ case 'f':
+ args->forward_mode = atoi(optarg);
+ break;
+ case 'g':
+ args->num_group = atoi(optarg);
+ break;
+ case 'i':
+ args->isolate = atoi(optarg);
+ break;
+ case 'l':
+ args->prio[LO_PRIO].queues = atoi(optarg);
+ break;
+ case 't':
+ args->prio[HI_PRIO].queues = atoi(optarg);
+ break;
+ case 'm':
+ args->prio[LO_PRIO].events = atoi(optarg);
+ args->prio[LO_PRIO].events_per_queue = 1;
+ break;
+ case 'n':
+ args->prio[HI_PRIO].events = atoi(optarg);
+ args->prio[HI_PRIO].events_per_queue = 1;
+ break;
+ case 'o':
+ args->prio[LO_PRIO].events = atoi(optarg);
+ args->prio[LO_PRIO].events_per_queue = 0;
+ break;
+ case 'p':
+ args->prio[HI_PRIO].events = atoi(optarg);
+ args->prio[HI_PRIO].events_per_queue = 0;
+ break;
+ case 's':
+ i = atoi(optarg);
+ if (i == 1)
+ args->sync_type = ODP_SCHED_SYNC_ATOMIC;
+ else if (i == 2)
+ args->sync_type = ODP_SCHED_SYNC_ORDERED;
+ else
+ args->sync_type = ODP_SCHED_SYNC_PARALLEL;
+ break;
+ case 'r':
+ args->sample_per_prio = 1;
+ break;
+ case 'w':
+ args->warm_up_rounds = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Make sure arguments are valid */
+ /* -1 for main thread */
+ if (args->cpu_count > ODP_THREAD_COUNT_MAX - 1)
+ args->cpu_count = ODP_THREAD_COUNT_MAX - 1;
+ if (args->prio[LO_PRIO].queues > MAX_QUEUES)
+ args->prio[LO_PRIO].queues = MAX_QUEUES;
+ if (args->prio[HI_PRIO].queues > MAX_QUEUES)
+ args->prio[HI_PRIO].queues = MAX_QUEUES;
+ if (args->test_rounds < 1)
+ args->test_rounds = 1;
+ if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) {
+ printf("No queues configured\n");
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ if (args->forward_mode > EVENT_FORWARD_NONE ||
+ args->forward_mode < EVENT_FORWARD_RAND) {
+ printf("Invalid forwarding mode\n");
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (args->num_group > MAX_GROUPS) {
+ ODPH_ERR("Too many groups. Max supported %i.\n", MAX_GROUPS);
+ exit(EXIT_FAILURE);
+ }
+
+ if (args->prio[HI_PRIO].queues == 0 || args->sample_per_prio)
+ args->prio[LO_PRIO].sample_events = 1;
+}
+
+static void randomize_queues(odp_queue_t queues[], uint32_t num, uint64_t *seed)
+{
+ uint32_t i;
+
+ for (i = 0; i < num; i++) {
+ uint32_t new_index;
+ odp_queue_t swap_queue;
+ odp_queue_t cur_queue = queues[i];
+
+ odp_random_test_data((uint8_t *)&new_index, sizeof(new_index),
+ seed);
+ new_index = new_index % num;
+ swap_queue = queues[new_index];
+
+ queues[new_index] = cur_queue;
+ queues[i] = swap_queue;
+ }
+}
+
+static int create_groups(test_globals_t *globals, odp_schedule_group_t group[], int num)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_thrmask_t zeromask;
+ int i, j, max;
+
+ if (num <= 0)
+ return 0;
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("Schedule capability failed\n");
+ return 0;
+ }
+
+ max = sched_capa.max_groups - 3;
+ if (num > max) {
+ printf("Too many schedule groups %i (max %u)\n", num, max);
+ return 0;
+ }
+
+ for (i = 0; i < NUM_PRIOS; i++)
+ for (j = 0; j < MAX_GROUPS; j++)
+ globals->group[i][j] = ODP_SCHED_GROUP_INVALID;
+
+ odp_thrmask_zero(&zeromask);
+
+ for (i = 0; i < num; i++) {
+ group[i] = odp_schedule_group_create("test_group", &zeromask);
+
+ if (group[i] == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("Group create failed %i\n", i);
+ break;
+ }
+
+ if (globals->args.isolate) {
+ globals->group[i % 2][i / 2] = group[i];
+ } else {
+ globals->group[0][i] = group[i];
+ globals->group[1][i] = group[i];
+ }
+ }
+
+ return i;
+}
+
+static int destroy_groups(odp_schedule_group_t group[], int num)
+{
+ int i;
+
+ if (num <= 0)
+ return 0;
+
+ for (i = 0; i < num; i++) {
+ if (odp_schedule_group_destroy(group[i])) {
+ ODPH_ERR("Group destroy failed %i\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_cpumask_t cpumask;
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t params;
+ test_globals_t *globals;
+ test_args_t args;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ uint32_t pool_size;
+ int i, j, ret;
+ int num_group, tot_group;
+ odp_schedule_group_t group[2 * MAX_GROUPS];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ int err = 0;
+ int num_workers = 0;
+ odp_shm_t shm = ODP_SHM_INVALID;
+ odp_pool_t pool = ODP_POOL_INVALID;
+
+ printf("\nODP scheduling latency benchmark starts\n\n");
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ memset(&args, 0, sizeof(args));
+ parse_args(argc, argv, &args);
+
+ /* ODP global init */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Init this thread. It makes also ODP calls when
+ * setting up resources for worker threads.
+ */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_sys_info_print();
+
+ num_group = args.num_group;
+
+ tot_group = 0;
+ if (num_group > 0)
+ tot_group = args.isolate ? 2 * num_group : num_group;
+
+ /* Get default worker cpumask */
+ if (args.cpu_count)
+ num_workers = args.cpu_count;
+
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ args.cpu_count = num_workers;
+
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("Test options:\n");
+ printf(" Worker threads: %i\n", num_workers);
+ printf(" First CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf(" CPU mask: %s\n", cpumaskstr);
+ printf(" Test rounds: %iM\n", args.test_rounds);
+ printf(" Warm-up rounds: %i\n", args.warm_up_rounds);
+ printf(" Isolated groups: %i\n", args.isolate);
+ printf(" Number of groups: %i\n", num_group);
+ printf(" Created groups: %i\n", tot_group);
+ printf("\n");
+
+ shm = odp_shm_reserve("test_globals", sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed.\n");
+ err = -1;
+ goto error;
+ }
+
+ globals = odp_shm_addr(shm);
+ memset(globals, 0, sizeof(test_globals_t));
+ memcpy(&globals->args, &args, sizeof(test_args_t));
+
+ odp_schedule_config(NULL);
+
+ /*
+ * Create event pool
+ */
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("pool capa failed\n");
+ err = -1;
+ goto error;
+ }
+
+ pool_size = EVENT_POOL_SIZE;
+ if (pool_capa.buf.max_num && pool_capa.buf.max_num < EVENT_POOL_SIZE)
+ pool_size = pool_capa.buf.max_num;
+
+ odp_pool_param_init(&params);
+ params.buf.size = sizeof(test_event_t);
+ params.buf.align = 0;
+ params.buf.num = pool_size;
+ params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("event_pool", &params);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed.\n");
+ err = -1;
+ goto error;
+ }
+ globals->pool = pool;
+
+ /* Create groups */
+ ret = create_groups(globals, group, tot_group);
+ if (ret != tot_group) {
+ ODPH_ERR("Group create failed.\n");
+ tot_group = ret;
+ err = -1;
+ goto error;
+ }
+
+ /*
+ * Create queues for schedule test
+ */
+ for (i = 0; i < NUM_PRIOS; i++) {
+ char name[] = "sched_XX_YY";
+ odp_queue_t queue;
+ odp_queue_param_t param;
+ odp_schedule_group_t grp;
+ int prio;
+
+ grp = ODP_SCHED_GROUP_ALL;
+ if (num_group < 0)
+ grp = ODP_SCHED_GROUP_WORKER;
+
+ if (i == HI_PRIO)
+ prio = odp_schedule_max_prio();
+ else
+ prio = odp_schedule_min_prio();
+
+ name[6] = '0' + (prio / 10);
+ name[7] = '0' + prio - (10 * (prio / 10));
+
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_SCHED;
+ param.sched.prio = prio;
+ param.sched.sync = args.sync_type;
+
+ for (j = 0; j < args.prio[i].queues; j++) {
+ name[9] = '0' + j / 10;
+ name[10] = '0' + j - 10 * (j / 10);
+
+ /* Round robin queues into groups */
+ if (num_group > 0)
+ grp = globals->group[i][j % num_group];
+
+ param.sched.group = grp;
+
+ queue = odp_queue_create(name, &param);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Scheduled queue create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ globals->queue[i][j] = queue;
+ }
+ if (args.forward_mode == EVENT_FORWARD_RAND) {
+ uint64_t seed = i;
+
+ randomize_queues(globals->queue[i], args.prio[i].queues,
+ &seed);
+ }
+ }
+
+ odp_barrier_init(&globals->barrier, num_workers);
+
+ /* Create and launch worker threads */
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_thread;
+ thr_param.arg = NULL;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ /* Wait for worker threads to terminate */
+ odph_thread_join(thread_tbl, num_workers);
+
+ printf("ODP scheduling latency test complete\n\n");
+
+ for (i = 0; i < NUM_PRIOS; i++) {
+ odp_queue_t queue;
+ int num_queues;
+
+ num_queues = args.prio[i].queues;
+
+ for (j = 0; j < num_queues; j++) {
+ queue = globals->queue[i][j];
+ if (odp_queue_destroy(queue)) {
+ ODPH_ERR("Queue destroy failed [%i][%i]\n", i, j);
+ err = -1;
+ break;
+ }
+ }
+ }
+
+error:
+ if (destroy_groups(group, tot_group)) {
+ ODPH_ERR("Group destroy failed\n");
+ err = -1;
+ }
+
+ if (pool != ODP_POOL_INVALID) {
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ err = -1;
+ }
+ }
+
+ if (shm != ODP_SHM_INVALID) {
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("SHM destroy failed\n");
+ err = -1;
+ }
+ }
+
+ err += odp_term_local();
+ err += odp_term_global(instance);
+
+ return err;
+}
diff --git a/test/performance/odp_sched_latency_run.sh b/test/performance/odp_sched_latency_run.sh
new file mode 100755
index 000000000..8cd6dd480
--- /dev/null
+++ b/test/performance/odp_sched_latency_run.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2016-2018 Linaro Limited
+#
+# Script that passes command line arguments to odp_sched_latency test when
+# launched by 'make check'
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+ALL=0
+
+run()
+{
+ echo odp_sched_latency_run starts requesting $1 worker threads
+ echo =========================================================
+
+ if [ $(nproc) -lt $1 ]; then
+ echo "Not enough CPU cores. Skipping test."
+ else
+ $TEST_DIR/odp_sched_latency${EXEEXT} -c $1 -d 1 || exit $?
+ fi
+}
+
+run 1
+run 5
+run 8
+run 11
+run $ALL
+
+exit 0
diff --git a/test/performance/odp_sched_perf.c b/test/performance/odp_sched_perf.c
new file mode 100644
index 000000000..fdfa4854e
--- /dev/null
+++ b/test/performance/odp_sched_perf.c
@@ -0,0 +1,1516 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2020-2024 Nokia
+ */
+
+/**
+ * @example odp_sched_perf.c
+ *
+ * Performance test application for scheduling
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for sigaction */
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_QUEUES (256 * 1024)
+#define MAX_GROUPS 256
+
+/* Max time to wait for new events in nanoseconds */
+#define MAX_SCHED_WAIT_NS (10 * ODP_TIME_SEC_IN_NS)
+
+/* Scheduling round interval to check for MAX_SCHED_WAIT_NS */
+#define TIME_CHECK_INTERVAL (1024 * 1024)
+
+/* Round up 'X' to a multiple of 'NUM' */
+#define ROUNDUP(X, NUM) ((NUM) * (((X) + (NUM) - 1) / (NUM)))
+
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t num_queue;
+ uint32_t num_low;
+ uint32_t num_high;
+ uint32_t num_dummy;
+ uint32_t num_event;
+ uint32_t num_sched;
+ int num_group;
+ uint32_t num_join;
+ uint32_t max_burst;
+ odp_pool_type_t pool_type;
+ int queue_type;
+ int forward;
+ int fairness;
+ uint32_t event_size;
+ uint32_t queue_size;
+ uint32_t tot_queue;
+ uint32_t tot_event;
+ int touch_data;
+ uint32_t rd_words;
+ uint32_t rw_words;
+ uint32_t ctx_size;
+ uint32_t ctx_rd_words;
+ uint32_t ctx_rw_words;
+ uint32_t uarea_rd;
+ uint32_t uarea_rw;
+ uint32_t uarea_size;
+ uint64_t wait_ns;
+ int verbose;
+
+} test_options_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t enqueues;
+ uint64_t events;
+ uint64_t nsec;
+ uint64_t cycles;
+ uint64_t waits;
+ uint64_t dummy_sum;
+ uint8_t failed;
+
+} test_stat_t;
+
+typedef struct thread_arg_t {
+ void *global;
+ int first_group;
+
+} thread_arg_t;
+
+typedef struct test_global_t {
+ test_options_t test_options;
+ odp_schedule_config_t schedule_config;
+ odp_barrier_t barrier;
+ odp_pool_t pool;
+ odp_cpumask_t cpumask;
+ odp_shm_t ctx_shm;
+ odp_queue_t queue[MAX_QUEUES];
+ odp_schedule_group_t group[MAX_GROUPS];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+ thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
+ odp_atomic_u32_t num_worker;
+ odp_atomic_u32_t exit_threads;
+
+} test_global_t;
+
+typedef struct {
+ odp_queue_t next;
+ odp_atomic_u64_t count;
+} queue_context_t;
+
+static test_global_t *test_globals;
+
+static void sig_handler(int signum ODP_UNUSED)
+{
+ odp_atomic_store_u32(&test_globals->exit_threads, 1);
+}
+
+static int setup_sig_handler(void)
+{
+ struct sigaction action = { .sa_handler = sig_handler };
+
+ if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
+ return -1;
+
+ return 0;
+}
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Scheduler performance test\n"
+ "\n"
+ "Usage: odp_sched_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1.\n"
+ " -q, --num_queue Number of queues. Default: 1.\n"
+ " -L, --num_low Number of lowest priority queues out of '--num_queue' queues. Rest of\n"
+ " the queues are default (or highest) priority. Default: 0.\n"
+ " -H, --num_high Number of highest priority queues out of '--num_queue' queues. Rest of\n"
+ " the queues are default (or lowest) priority. Default: 0.\n"
+ " -d, --num_dummy Number of empty queues. Default: 0.\n"
+ " -e, --num_event Number of events per queue. Default: 100.\n"
+ " -s, --num_sched Number of events to schedule per thread. If zero, the application runs\n"
+ " until SIGINT is received. Default: 100 000.\n"
+ " -g, --num_group Number of schedule groups. Round robins threads and queues into groups.\n"
+ " -1: SCHED_GROUP_WORKER\n"
+ " 0: SCHED_GROUP_ALL (default)\n"
+ " -j, --num_join Number of groups a thread joins. Threads are divide evenly into groups,\n"
+ " if num_cpu is multiple of num_group and num_group is multiple of num_join.\n"
+ " 0: join all groups (default)\n"
+ " -b, --burst Maximum number of events per operation. Default: 100.\n"
+ " -t, --type Queue type. 0: parallel, 1: atomic, 2: ordered. Default: 0.\n"
+ " -f, --forward 0: Keep event in the original queue, 1: Forward event to the next queue. Default: 0.\n"
+ " -a, --fairness 0: Don't count events per queue, 1: Count and report events relative to average. Default: 0.\n"
+ " -w, --wait_ns Number of nsec to wait before enqueueing events. Default: 0.\n"
+ " -k, --ctx_rd_words Number of queue context words (uint64_t) to read on every event. Default: 0.\n"
+ " -l, --ctx_rw_words Number of queue context words (uint64_t) to modify on every event. Default: 0.\n"
+ " -n, --rd_words Number of event data words (uint64_t) to read before enqueueing it. Default: 0.\n"
+ " -m, --rw_words Number of event data words (uint64_t) to modify before enqueueing it. Default: 0.\n"
+ " -u, --uarea_rd Number of user area words (uint64_t) to read on every event. Default: 0.\n"
+ " -U, --uarea_rw Number of user area words (uint64_t) to modify on every event. Default: 0.\n"
+ " -p, --pool_type Pool type. 0: buffer, 1: packet. Default: 0.\n"
+ " -v, --verbose Verbose output.\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt, long_index, num_group, num_join;
+ int ret = 0;
+ uint32_t ctx_size = 0;
+ int pool_type = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_queue", required_argument, NULL, 'q'},
+ {"num_low", required_argument, NULL, 'L'},
+ {"num_high", required_argument, NULL, 'H'},
+ {"num_dummy", required_argument, NULL, 'd'},
+ {"num_event", required_argument, NULL, 'e'},
+ {"num_sched", required_argument, NULL, 's'},
+ {"num_group", required_argument, NULL, 'g'},
+ {"num_join", required_argument, NULL, 'j'},
+ {"burst", required_argument, NULL, 'b'},
+ {"type", required_argument, NULL, 't'},
+ {"forward", required_argument, NULL, 'f'},
+ {"fairness", required_argument, NULL, 'a'},
+ {"wait_ns", required_argument, NULL, 'w'},
+ {"ctx_rd_words", required_argument, NULL, 'k'},
+ {"ctx_rw_words", required_argument, NULL, 'l'},
+ {"rd_words", required_argument, NULL, 'n'},
+ {"rw_words", required_argument, NULL, 'm'},
+ {"uarea_rd", required_argument, NULL, 'u'},
+ {"uarea_rw", required_argument, NULL, 'U'},
+ {"pool_type", required_argument, NULL, 'p'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:q:L:H:d:e:s:g:j:b:t:f:a:w:k:l:n:m:p:u:U:vh";
+
+ test_options->num_cpu = 1;
+ test_options->num_queue = 1;
+ test_options->num_low = 0;
+ test_options->num_high = 0;
+ test_options->num_dummy = 0;
+ test_options->num_event = 100;
+ test_options->num_sched = 100000;
+ test_options->num_group = 0;
+ test_options->num_join = 0;
+ test_options->max_burst = 100;
+ test_options->queue_type = 0;
+ test_options->forward = 0;
+ test_options->fairness = 0;
+ test_options->ctx_rd_words = 0;
+ test_options->ctx_rw_words = 0;
+ test_options->rd_words = 0;
+ test_options->rw_words = 0;
+ test_options->uarea_rd = 0;
+ test_options->uarea_rw = 0;
+ test_options->wait_ns = 0;
+ test_options->verbose = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'q':
+ test_options->num_queue = atoi(optarg);
+ break;
+ case 'L':
+ test_options->num_low = atoi(optarg);
+ break;
+ case 'H':
+ test_options->num_high = atoi(optarg);
+ break;
+ case 'd':
+ test_options->num_dummy = atoi(optarg);
+ break;
+ case 'e':
+ test_options->num_event = atoi(optarg);
+ break;
+ case 's':
+ test_options->num_sched = atoi(optarg);
+ break;
+ case 'g':
+ test_options->num_group = atoi(optarg);
+ break;
+ case 'j':
+ test_options->num_join = atoi(optarg);
+ break;
+ case 'b':
+ test_options->max_burst = atoi(optarg);
+ break;
+ case 't':
+ test_options->queue_type = atoi(optarg);
+ break;
+ case 'f':
+ test_options->forward = atoi(optarg);
+ break;
+ case 'a':
+ test_options->fairness = atoi(optarg);
+ break;
+ case 'k':
+ test_options->ctx_rd_words = atoi(optarg);
+ break;
+ case 'l':
+ test_options->ctx_rw_words = atoi(optarg);
+ break;
+ case 'n':
+ test_options->rd_words = atoi(optarg);
+ break;
+ case 'm':
+ test_options->rw_words = atoi(optarg);
+ break;
+ case 'u':
+ test_options->uarea_rd = atoi(optarg);
+ break;
+ case 'U':
+ test_options->uarea_rw = atoi(optarg);
+ break;
+ case 'p':
+ pool_type = atoi(optarg);
+ break;
+ case 'w':
+ test_options->wait_ns = atoll(optarg);
+ break;
+ case 'v':
+ test_options->verbose = 1;
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+ if (pool_type == 0) {
+ test_options->pool_type = ODP_POOL_BUFFER;
+ } else if (pool_type == 1) {
+ test_options->pool_type = ODP_POOL_PACKET;
+ } else {
+ ODPH_ERR("Invalid pool type: %d.\n", pool_type);
+ ret = -1;
+ }
+
+ test_options->touch_data = test_options->rd_words ||
+ test_options->rw_words;
+
+ if ((test_options->num_queue + test_options->num_dummy) > MAX_QUEUES) {
+ ODPH_ERR("Too many queues. Max supported %i.\n", MAX_QUEUES);
+ ret = -1;
+ }
+
+ if ((test_options->num_low + test_options->num_high) > test_options->num_queue) {
+ ODPH_ERR("Number of low/high prio %u/%u exceed number of queues %u.\n",
+ test_options->num_low, test_options->num_high, test_options->num_queue);
+ ret = -1;
+ }
+
+ num_group = test_options->num_group;
+ num_join = test_options->num_join;
+ if (num_group > MAX_GROUPS) {
+ ODPH_ERR("Too many groups. Max supported %i.\n", MAX_GROUPS);
+ ret = -1;
+ }
+
+ if (num_group > 0 && num_join > num_group) {
+ ODPH_ERR("num_join (%i) larger than num_group (%i).\n", num_join, num_group);
+ ret = -1;
+ }
+
+ if (num_join && num_group > (int)(test_options->num_cpu * num_join)) {
+ printf("WARNING: Too many groups (%i). Some groups (%i) are not served.\n\n",
+ num_group, num_group - (test_options->num_cpu * num_join));
+
+ if (test_options->forward) {
+ printf("Error: Cannot forward when some queues are not served.\n");
+ ret = -1;
+ }
+ }
+
+ test_options->tot_queue = test_options->num_queue +
+ test_options->num_dummy;
+ test_options->tot_event = test_options->num_queue *
+ test_options->num_event;
+
+ test_options->queue_size = test_options->num_event;
+
+ if (test_options->forward) {
+ /* When forwarding, all events may end up into
+ * a single queue */
+ test_options->queue_size = test_options->tot_event;
+ }
+
+ if (test_options->forward || test_options->fairness)
+ ctx_size = sizeof(queue_context_t);
+
+ if (test_options->ctx_rd_words || test_options->ctx_rw_words) {
+ /* Round up queue handle size to a multiple of 8 for correct
+ * context data alignment */
+ ctx_size = ROUNDUP(ctx_size, 8);
+ ctx_size += 8 * test_options->ctx_rd_words;
+ ctx_size += 8 * test_options->ctx_rw_words;
+ }
+
+ /* When context data is modified, round up to cache line size to avoid
+ * false sharing */
+ if (test_options->fairness || test_options->ctx_rw_words)
+ ctx_size = ROUNDUP(ctx_size, ODP_CACHE_LINE_SIZE);
+
+ test_options->ctx_size = ctx_size;
+ test_options->uarea_size = 8 * (test_options->uarea_rd + test_options->uarea_rw);
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ printf("Error: Too many workers. Maximum is %i.\n",
+ ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ printf("Error: Too many workers. Max supported %i\n.", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ return 0;
+}
+
+static int create_pool(test_global_t *global)
+{
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ uint32_t max_num, max_size, max_uarea;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ uint32_t num_queue = test_options->num_queue;
+ uint32_t num_dummy = test_options->num_dummy;
+ uint32_t num_event = test_options->num_event;
+ uint32_t num_sched = test_options->num_sched;
+ uint32_t max_burst = test_options->max_burst;
+ uint32_t tot_queue = test_options->tot_queue;
+ uint32_t tot_event = test_options->tot_event;
+ uint32_t queue_size = test_options->queue_size;
+ int num_group = test_options->num_group;
+ uint32_t num_join = test_options->num_join;
+ int forward = test_options->forward;
+ uint64_t wait_ns = test_options->wait_ns;
+ uint32_t event_size = 16;
+ int touch_data = test_options->touch_data;
+ uint32_t ctx_size = test_options->ctx_size;
+ uint32_t uarea_size = test_options->uarea_size;
+
+ if (touch_data) {
+ event_size = test_options->rd_words + test_options->rw_words;
+ event_size = 8 * event_size;
+ }
+ test_options->event_size = event_size;
+
+ printf("\nScheduler performance test\n");
+ printf(" num sched %u\n", num_sched);
+ printf(" num cpu %u\n", num_cpu);
+ printf(" num queues %u\n", num_queue);
+ printf(" num lowest prio queues %u\n", test_options->num_low);
+ printf(" num highest prio queues %u\n", test_options->num_high);
+ printf(" num empty queues %u\n", num_dummy);
+ printf(" total queues %u\n", tot_queue);
+ printf(" num groups %i", num_group);
+ if (num_group == -1)
+ printf(" (ODP_SCHED_GROUP_WORKER)\n");
+ else if (num_group == 0)
+ printf(" (ODP_SCHED_GROUP_ALL)\n");
+ else
+ printf("\n");
+
+ printf(" num join %u\n", num_join);
+ printf(" forward events %i\n", forward ? 1 : 0);
+ printf(" wait nsec %" PRIu64 "\n", wait_ns);
+ printf(" events per queue %u\n", num_event);
+ printf(" queue size %u\n", queue_size);
+ printf(" max burst size %u\n", max_burst);
+ printf(" total events %u\n", tot_event);
+ printf(" event size %u bytes", event_size);
+ if (touch_data)
+ printf(" (rd: %u, rw: %u)", 8 * test_options->rd_words, 8 * test_options->rw_words);
+ printf("\n");
+
+ printf(" context size %u bytes", ctx_size);
+ if (test_options->ctx_rd_words || test_options->ctx_rw_words) {
+ printf(" (rd: %u, rw: %u)",
+ 8 * test_options->ctx_rd_words,
+ 8 * test_options->ctx_rw_words);
+ }
+ printf("\n");
+
+ printf(" user area size %u bytes", uarea_size);
+ if (uarea_size)
+ printf(" (rd: %u, rw: %u)", 8 * test_options->uarea_rd, 8 * test_options->uarea_rw);
+ printf("\n");
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Error: pool capa failed\n");
+ return -1;
+ }
+
+ if (test_options->pool_type == ODP_POOL_BUFFER) {
+ printf(" pool type buffer\n");
+ max_num = pool_capa.buf.max_num;
+ max_size = pool_capa.buf.max_size;
+ max_uarea = pool_capa.buf.max_uarea_size;
+ } else {
+ printf(" pool type packet\n");
+ max_num = pool_capa.pkt.max_num;
+ max_size = pool_capa.pkt.max_seg_len;
+ max_uarea = pool_capa.pkt.max_uarea_size;
+ }
+
+ if (max_num && tot_event > max_num) {
+ ODPH_ERR("Error: max events supported %u\n", max_num);
+ return -1;
+ }
+
+ if (max_size && event_size > max_size) {
+ ODPH_ERR("Error: max supported event size %u\n", max_size);
+ return -1;
+ }
+
+ if (uarea_size > max_uarea) {
+ ODPH_ERR("Error: max supported user area size %u\n", max_uarea);
+ return -1;
+ }
+
+ odp_pool_param_init(&pool_param);
+ if (test_options->pool_type == ODP_POOL_BUFFER) {
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = tot_event;
+ pool_param.buf.size = event_size;
+ pool_param.buf.align = 8;
+ pool_param.buf.uarea_size = uarea_size;
+ } else {
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = tot_event;
+ pool_param.pkt.len = event_size;
+ pool_param.pkt.seg_len = event_size;
+ pool_param.pkt.align = 8;
+ pool_param.pkt.uarea_size = uarea_size;
+ }
+
+ pool = odp_pool_create("sched perf", &pool_param);
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Error: pool create failed\n");
+ return -1;
+ }
+
+ global->pool = pool;
+
+ return 0;
+}
+
+static int create_groups(test_global_t *global)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_thrmask_t thrmask;
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_group = test_options->num_group;
+
+ if (test_options->num_group <= 0)
+ return 0;
+
+ if (odp_schedule_capability(&sched_capa)) {
+ printf("Error: schedule capability failed\n");
+ return -1;
+ }
+
+ if (num_group > sched_capa.max_groups) {
+ printf("Error: Too many sched groups (max_groups capa %u)\n",
+ sched_capa.max_groups);
+ return -1;
+ }
+
+ odp_thrmask_zero(&thrmask);
+
+ for (i = 0; i < num_group; i++) {
+ odp_schedule_group_t group;
+
+ group = odp_schedule_group_create("test_group", &thrmask);
+
+ if (group == ODP_SCHED_GROUP_INVALID) {
+ printf("Error: Group create failed %u\n", i);
+ return -1;
+ }
+
+ global->group[i] = group;
+ }
+
+ return 0;
+}
+
+static int create_queues(test_global_t *global)
+{
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ odp_schedule_sync_t sync;
+ odp_schedule_prio_t prio;
+ const char *type_str;
+ uint32_t i, j, first;
+ test_options_t *test_options = &global->test_options;
+ uint32_t event_size = test_options->event_size;
+ uint32_t num_event = test_options->num_event;
+ uint32_t queue_size = test_options->queue_size;
+ uint32_t tot_queue = test_options->tot_queue;
+ uint32_t num_low = test_options->num_low;
+ uint32_t num_high = test_options->num_high;
+ uint32_t num_default = test_options->num_queue - num_low - num_high;
+ int num_group = test_options->num_group;
+ int type = test_options->queue_type;
+ odp_pool_t pool = global->pool;
+ uint8_t *ctx = NULL;
+ uint32_t ctx_size = test_options->ctx_size;
+
+ if (type == 0) {
+ type_str = "parallel";
+ sync = ODP_SCHED_SYNC_PARALLEL;
+ } else if (type == 1) {
+ type_str = "atomic";
+ sync = ODP_SCHED_SYNC_ATOMIC;
+ } else {
+ type_str = "ordered";
+ sync = ODP_SCHED_SYNC_ORDERED;
+ }
+
+ printf(" queue type %s\n\n", type_str);
+
+ if (tot_queue > global->schedule_config.num_queues) {
+ printf("Max queues supported %u\n",
+ global->schedule_config.num_queues);
+ return -1;
+ }
+
+ if (global->schedule_config.queue_size &&
+ queue_size > global->schedule_config.queue_size) {
+ printf("Max queue size %u\n",
+ global->schedule_config.queue_size);
+ return -1;
+ }
+
+ if (ctx_size) {
+ ctx = odp_shm_addr(global->ctx_shm);
+ if (ctx == NULL) {
+ printf("Bad queue context\n");
+ return -1;
+ }
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = sync;
+ queue_param.size = queue_size;
+ if (num_group == -1)
+ queue_param.sched.group = ODP_SCHED_GROUP_WORKER;
+ else
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ first = test_options->num_dummy;
+
+ for (i = 0; i < tot_queue; i++) {
+ if (num_group > 0) {
+ odp_schedule_group_t group;
+
+ /* Divide all queues evenly into groups */
+ group = global->group[i % num_group];
+ queue_param.sched.group = group;
+ }
+
+ /* Create low, high and default queues in a mixed order. Dummy queues are created
+ * first and with default priority. */
+ prio = odp_schedule_default_prio();
+ if (i >= first) {
+ switch (i % 3) {
+ case 0:
+ if (num_low) {
+ num_low--;
+ prio = odp_schedule_min_prio();
+ } else if (num_high) {
+ num_high--;
+ prio = odp_schedule_max_prio();
+ } else {
+ num_default--;
+ }
+ break;
+ case 1:
+ if (num_high) {
+ num_high--;
+ prio = odp_schedule_max_prio();
+ } else if (num_low) {
+ num_low--;
+ prio = odp_schedule_min_prio();
+ } else {
+ num_default--;
+ }
+ break;
+ default:
+ if (num_default) {
+ num_default--;
+ } else if (num_high) {
+ num_high--;
+ prio = odp_schedule_max_prio();
+ } else {
+ num_low--;
+ prio = odp_schedule_min_prio();
+ }
+ break;
+ }
+ }
+
+ queue_param.sched.prio = prio;
+
+ queue = odp_queue_create(NULL, &queue_param);
+
+ global->queue[i] = queue;
+
+ if (queue == ODP_QUEUE_INVALID) {
+ printf("Error: Queue create failed %u\n", i);
+ return -1;
+ }
+ }
+
+ /* Store events into queues. Dummy queues are allocated from
+ * the beginning of the array, so that usage of those affect allocation
+ * of active queues. Dummy queues are left empty. */
+ for (i = first; i < tot_queue; i++) {
+ queue = global->queue[i];
+
+ if (ctx_size) {
+ /*
+ * Cast increases alignment, but it's ok, since ctx and
+ * ctx_size are both cache line aligned.
+ */
+ queue_context_t *qc = (queue_context_t *)(uintptr_t)ctx;
+
+ if (test_options->forward) {
+ uint32_t next = i + 1;
+
+ if (next == tot_queue)
+ next = first;
+
+ qc->next = global->queue[next];
+ }
+
+ if (test_options->fairness)
+ odp_atomic_init_u64(&qc->count, 0);
+
+ if (odp_queue_context_set(queue, ctx, ctx_size)) {
+ printf("Error: Context set failed %u\n", i);
+ return -1;
+ }
+
+ ctx += ctx_size;
+ }
+
+ for (j = 0; j < num_event; j++) {
+ odp_event_t ev;
+
+ if (test_options->pool_type == ODP_POOL_BUFFER) {
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ if (buf == ODP_BUFFER_INVALID) {
+ ODPH_ERR("Error: alloc failed %u/%u\n", i, j);
+ return -1;
+ }
+ ev = odp_buffer_to_event(buf);
+ } else {
+ odp_packet_t pkt = odp_packet_alloc(pool, event_size);
+
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("Error: alloc failed %u/%u\n", i, j);
+ return -1;
+ }
+ ev = odp_packet_to_event(pkt);
+ }
+ if (odp_queue_enq(queue, ev)) {
+ ODPH_ERR("Error: enqueue failed %u/%u\n", i, j);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int join_group(test_global_t *global, int grp_index, int thr)
+{
+ odp_thrmask_t thrmask;
+ odp_schedule_group_t group;
+
+ odp_thrmask_zero(&thrmask);
+ odp_thrmask_set(&thrmask, thr);
+ group = global->group[grp_index];
+
+ if (odp_schedule_group_join(group, &thrmask)) {
+ printf("Error: Group %i join failed (thr %i)\n",
+ grp_index, thr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int join_all_groups(test_global_t *global, int thr)
+{
+ int i;
+ test_options_t *test_options = &global->test_options;
+ int num_group = test_options->num_group;
+
+ if (num_group <= 0)
+ return 0;
+
+ for (i = 0; i < num_group; i++) {
+ if (join_group(global, i, thr)) {
+ printf("Error: Group %u join failed (thr %i)\n",
+ i, thr);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void print_queue_fairness(test_global_t *global)
+{
+ uint32_t i;
+ queue_context_t *ctx;
+ test_options_t *test_options = &global->test_options;
+ uint32_t first = test_options->num_dummy;
+ uint32_t num_queue = test_options->num_queue;
+ uint32_t tot_queue = test_options->tot_queue;
+ uint64_t total = 0;
+ double average;
+
+ if (!test_options->fairness)
+ return;
+
+ for (i = first; i < tot_queue; i++) {
+ ctx = odp_queue_context(global->queue[i]);
+ total += odp_atomic_load_u64(&ctx->count);
+ }
+
+ average = (double)total / (double)num_queue;
+
+ printf("\n");
+ printf("RESULTS - events per queue (percent of average):\n");
+ printf("------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = first; i < tot_queue; i++) {
+ ctx = odp_queue_context(global->queue[i]);
+
+ if ((i % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)odp_atomic_load_u64(&ctx->count) /
+ average * 100.0);
+ }
+
+ printf("\n");
+}
+
+static int destroy_queues(test_global_t *global)
+{
+ uint32_t i;
+ odp_event_t ev;
+ uint64_t wait;
+ test_options_t *test_options = &global->test_options;
+ uint32_t tot_queue = test_options->tot_queue;
+ int thr = odp_thread_id();
+
+ if (join_all_groups(global, thr))
+ return -1;
+
+ wait = odp_schedule_wait_time(200 * ODP_TIME_MSEC_IN_NS);
+
+ while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ for (i = 0; i < tot_queue; i++) {
+ if (global->queue[i] != ODP_QUEUE_INVALID) {
+ if (odp_queue_destroy(global->queue[i])) {
+ printf("Error: Queue destroy failed %u\n", i);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_groups(test_global_t *global)
+{
+ int i;
+ test_options_t *test_options = &global->test_options;
+ int num_group = test_options->num_group;
+
+ if (num_group <= 0)
+ return 0;
+
+ for (i = 0; i < num_group; i++) {
+ odp_schedule_group_t group = global->group[i];
+
+ if (odp_schedule_group_destroy(group)) {
+ printf("Error: Group destroy failed %u\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static uint64_t rw_uarea(odp_event_t ev[], int num, uint32_t rd_words, uint32_t rw_words)
+{
+ uint64_t *data;
+ int i;
+ uint32_t j;
+ uint64_t sum = 0;
+
+ for (i = 0; i < num; i++) {
+ data = odp_event_user_area(ev[i]);
+
+ for (j = 0; j < rd_words; j++)
+ sum += data[j];
+
+ for (; j < rd_words + rw_words; j++) {
+ sum += data[j];
+ data[j] += 1;
+ }
+ }
+
+ return sum;
+}
+
+static inline uint64_t rw_ctx_data(void *ctx, uint32_t offset,
+ uint32_t rd_words, uint32_t rw_words)
+{
+ uint64_t *data;
+ uint32_t i;
+ uint64_t sum = 0;
+
+ data = (uint64_t *)(uintptr_t)((uint8_t *)ctx + offset);
+
+ for (i = 0; i < rd_words; i++)
+ sum += data[i];
+
+ for (; i < rd_words + rw_words; i++) {
+ sum += data[i];
+ data[i] += 1;
+ }
+
+ return sum;
+}
+
+static uint64_t rw_data(odp_event_t ev[], int num,
+ uint32_t rd_words, uint32_t rw_words, odp_pool_type_t pool_type)
+{
+ uint64_t *data;
+ int i;
+ uint32_t j;
+ uint64_t sum = 0;
+
+ for (i = 0; i < num; i++) {
+ if (pool_type == ODP_POOL_BUFFER)
+ data = odp_buffer_addr(odp_buffer_from_event(ev[i]));
+ else
+ data = odp_packet_data(odp_packet_from_event(ev[i]));
+
+ for (j = 0; j < rd_words; j++)
+ sum += data[j];
+
+ for (; j < rd_words + rw_words; j++) {
+ sum += data[j];
+ data[j] += 1;
+ }
+ }
+
+ return sum;
+}
+
+static int test_sched(void *arg)
+{
+ int num, num_enq, ret, thr;
+ uint32_t i, rounds;
+ uint64_t c1, c2, cycles, nsec;
+ uint64_t events, enqueues, waits, events_prev;
+ odp_time_t t1, t2, last_retry_ts;
+ odp_queue_t queue;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_sched = test_options->num_sched;
+ uint32_t max_burst = test_options->max_burst;
+ int num_group = test_options->num_group;
+ int forward = test_options->forward;
+ int fairness = test_options->fairness;
+ int touch_data = test_options->touch_data;
+ uint32_t rd_words = test_options->rd_words;
+ uint32_t rw_words = test_options->rw_words;
+ uint32_t ctx_size = test_options->ctx_size;
+ uint32_t ctx_rd_words = test_options->ctx_rd_words;
+ uint32_t ctx_rw_words = test_options->ctx_rw_words;
+ const uint32_t uarea_size = test_options->uarea_size;
+ const uint32_t uarea_rd = test_options->uarea_rd;
+ const uint32_t uarea_rw = test_options->uarea_rw;
+ odp_pool_type_t pool_type = test_options->pool_type;
+ int touch_ctx = ctx_rd_words || ctx_rw_words;
+ odp_atomic_u32_t *exit_threads = &global->exit_threads;
+ uint32_t ctx_offset = 0;
+ uint32_t sched_retries = 0;
+ uint64_t data_sum = 0;
+ uint64_t ctx_sum = 0;
+ uint64_t uarea_sum = 0;
+ uint64_t wait_ns = test_options->wait_ns;
+ odp_event_t ev[max_burst];
+
+ thr = odp_thread_id();
+
+ if (forward || fairness)
+ ctx_offset = ROUNDUP(sizeof(queue_context_t), 8);
+
+ if (num_group > 0) {
+ uint32_t num_join = test_options->num_join;
+
+ if (num_join) {
+ int pos = 0;
+ int n = 512;
+ char str[n];
+ int group_index = thread_arg->first_group;
+
+ pos += snprintf(&str[pos], n - pos,
+ "Thread %i joined groups:", thr);
+
+ for (i = 0; i < num_join; i++) {
+ if (join_group(global, group_index, thr))
+ return -1;
+
+ pos += snprintf(&str[pos], n - pos, " %i",
+ group_index);
+
+ group_index = (group_index + 1) % num_group;
+ }
+
+ printf("%s\n", str);
+
+ } else {
+ if (join_all_groups(global, thr))
+ return -1;
+ }
+ }
+
+ for (i = 0; i < max_burst; i++)
+ ev[i] = ODP_EVENT_INVALID;
+
+ enqueues = 0;
+ events = 0;
+ events_prev = 0;
+ waits = 0;
+ ret = 0;
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ c1 = odp_cpu_cycles();
+ last_retry_ts = t1;
+
+ for (rounds = 0; odp_likely(!odp_atomic_load_u32(exit_threads)); rounds++) {
+ if (odp_unlikely(num_sched && events >= num_sched))
+ break;
+
+ num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
+ ev, max_burst);
+
+ if (odp_likely(num > 0)) {
+ sched_retries = 0;
+ events += num;
+ i = 0;
+
+ if (odp_unlikely(uarea_size))
+ uarea_sum += rw_uarea(ev, num, uarea_rd, uarea_rw);
+
+ if (odp_unlikely(ctx_size)) {
+ queue_context_t *ctx = odp_queue_context(queue);
+
+ if (forward)
+ queue = ctx->next;
+
+ if (fairness)
+ odp_atomic_add_u64(&ctx->count, num);
+
+ if (odp_unlikely(touch_ctx))
+ ctx_sum += rw_ctx_data(ctx, ctx_offset,
+ ctx_rd_words,
+ ctx_rw_words);
+ }
+
+ if (odp_unlikely(touch_data))
+ data_sum += rw_data(ev, num, rd_words,
+ rw_words, pool_type);
+
+ if (odp_unlikely(wait_ns)) {
+ waits++;
+ odp_time_wait_ns(wait_ns);
+ }
+
+ while (num) {
+ num_enq = odp_queue_enq_multi(queue, &ev[i],
+ num);
+
+ if (num_enq < 0) {
+ printf("Error: Enqueue failed. Round %u\n",
+ rounds);
+ odp_event_free_multi(&ev[i], num);
+ ret = -1;
+ break;
+ }
+
+ num -= num_enq;
+ i += num_enq;
+ enqueues++;
+ }
+
+ if (odp_unlikely(ret))
+ break;
+
+ continue;
+ } else if (num == 0) {
+ sched_retries++;
+ if (odp_unlikely(sched_retries > TIME_CHECK_INTERVAL)) {
+ odp_time_t cur_time = odp_time_local();
+
+ /* Measure time from the last received event and
+ * break if MAX_SCHED_WAIT_NS is exceeded */
+ sched_retries = 0;
+ if (events_prev != events) {
+ events_prev = events;
+ last_retry_ts = cur_time;
+ } else if (odp_time_diff_ns(cur_time,
+ last_retry_ts) >
+ MAX_SCHED_WAIT_NS) {
+ printf("Error: scheduling timed out\n");
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ /* <0 not specified as an error but checking anyway */
+ if (num < 0) {
+ printf("Error: Sched failed. Round %u\n", rounds);
+ ret = -1;
+ break;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+ cycles = odp_cpu_cycles_diff(c2, c1);
+
+ /* Update stats*/
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].enqueues = enqueues;
+ global->stat[thr].events = events;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].cycles = cycles;
+ global->stat[thr].waits = waits;
+ global->stat[thr].dummy_sum = data_sum + ctx_sum + uarea_sum;
+ global->stat[thr].failed = ret;
+
+ if (odp_atomic_fetch_dec_u32(&global->num_worker) == 1) {
+ /* The last worker frees all events. This is needed when the main
+ * thread cannot do the clean up (ODP_SCHED_GROUP_WORKER). */
+ odp_event_t event;
+ uint64_t sched_wait = odp_schedule_wait_time(200 * ODP_TIME_MSEC_IN_NS);
+
+ /* Print queue and scheduler status at the end of the test, before any queues
+ * are emptied or destroyed. */
+ if (test_options->verbose) {
+ odp_queue_print_all();
+ odp_schedule_print();
+ }
+
+ while ((event = odp_schedule(NULL, sched_wait)) != ODP_EVENT_INVALID)
+ odp_event_free(event);
+ }
+
+ /* Pause scheduling before thread exit */
+ odp_schedule_pause();
+
+ while (1) {
+ ev[0] = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
+
+ if (ev[0] == ODP_EVENT_INVALID)
+ break;
+
+ if (odp_unlikely(forward))
+ queue = ((queue_context_t *)odp_queue_context(queue))->next;
+
+ if (odp_queue_enq(queue, ev[0])) {
+ printf("Error: Queue enqueue failed\n");
+ odp_event_free(ev[0]);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t thr_common;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_group = test_options->num_group;
+ uint32_t num_join = test_options->num_join;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ odp_atomic_init_u32(&global->num_worker, num_cpu);
+
+ memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = test_sched;
+ thr_param[i].arg = &global->thread_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+
+ global->thread_arg[i].global = global;
+ global->thread_arg[i].first_group = 0;
+
+ if (num_group > 0 && num_join) {
+ /* Each thread joins only num_join groups, starting
+ * from this group index and wrapping around the group
+ * table. */
+ int first_group = (i * num_join) % num_group;
+
+ global->thread_arg[i].first_group = first_group;
+ }
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
+ num_cpu);
+
+ if (ret != num_cpu) {
+ printf("Error: thread create failed %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static double measure_wait_time_cycles(uint64_t wait_ns)
+{
+ uint64_t i, c1, c2, diff;
+ uint64_t rounds;
+ double wait_cycles;
+
+ if (wait_ns == 0)
+ return 0.0;
+
+ /* Run measurement for 100msec or at least two times, so that effect
+ * from CPU frequency scaling is minimized. */
+ rounds = (100 * ODP_TIME_MSEC_IN_NS) / wait_ns;
+ if (rounds == 0)
+ rounds = 2;
+
+ c1 = odp_cpu_cycles();
+
+ for (i = 0; i < rounds; i++)
+ odp_time_wait_ns(wait_ns);
+
+ c2 = odp_cpu_cycles();
+ diff = odp_cpu_cycles_diff(c2, c1);
+ wait_cycles = (double)diff / rounds;
+
+ printf("\nMeasured wait cycles: %.3f\n", wait_cycles);
+
+ return wait_cycles;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double rounds_ave, enqueues_ave, events_ave, nsec_ave, cycles_ave;
+ double waits_ave, wait_cycles, wait_cycles_ave;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ uint64_t wait_ns = test_options->wait_ns;
+ uint64_t rounds_sum = 0;
+ uint64_t enqueues_sum = 0;
+ uint64_t events_sum = 0;
+ uint64_t nsec_sum = 0;
+ uint64_t cycles_sum = 0;
+ uint64_t waits_sum = 0;
+
+ wait_cycles = measure_wait_time_cycles(wait_ns);
+
+ /* Averages */
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].failed) {
+ num_cpu--;
+ continue;
+ }
+ rounds_sum += global->stat[i].rounds;
+ enqueues_sum += global->stat[i].enqueues;
+ events_sum += global->stat[i].events;
+ nsec_sum += global->stat[i].nsec;
+ cycles_sum += global->stat[i].cycles;
+ waits_sum += global->stat[i].waits;
+ }
+
+ if (rounds_sum == 0 || num_cpu <= 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ rounds_ave = rounds_sum / num_cpu;
+ enqueues_ave = enqueues_sum / num_cpu;
+ events_ave = events_sum / num_cpu;
+ nsec_ave = nsec_sum / num_cpu;
+ cycles_ave = cycles_sum / num_cpu;
+ waits_ave = waits_sum / num_cpu;
+ wait_cycles_ave = waits_ave * wait_cycles;
+ num = 0;
+
+ printf("\n");
+ printf("RESULTS - per thread (Million events per sec):\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ if (global->stat[i].failed)
+ printf(" n/a ");
+ else
+ printf("%6.1f ",
+ (1000.0 * global->stat[i].events) /
+ global->stat[i].nsec);
+
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("RESULTS - average over %i threads:\n", num_cpu);
+ printf("----------------------------------\n");
+ printf(" schedule calls: %.3f\n", rounds_ave);
+ printf(" enqueue calls: %.3f\n", enqueues_ave);
+ printf(" duration: %.3f msec\n", nsec_ave / 1000000);
+ printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
+ printf(" cycles per round: %.3f\n",
+ cycles_ave / rounds_ave);
+ printf(" cycles per event: %.3f\n",
+ cycles_ave / events_ave);
+ if (wait_ns) {
+ printf(" without wait_ns cycles: %.3f\n",
+ (cycles_ave - wait_cycles_ave) / events_ave);
+ }
+ printf(" ave events received: %.3f\n",
+ events_ave / rounds_ave);
+ printf(" rounds per sec: %.3f M\n",
+ (1000.0 * rounds_ave) / nsec_ave);
+ printf(" events per sec: %.3f M\n\n",
+ (1000.0 * events_ave) / nsec_ave);
+
+ printf("TOTAL events per sec: %.3f M\n\n",
+ (1000.0 * events_sum) / nsec_ave);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ printf("Error: Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ printf("Error: Local init failed.\n");
+ return -1;
+ }
+
+ shm = odp_shm_reserve("sched_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: SHM reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Error: SHM alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ test_globals = global;
+
+ memset(global, 0, sizeof(test_global_t));
+ global->pool = ODP_POOL_INVALID;
+ global->ctx_shm = ODP_SHM_INVALID;
+ odp_atomic_init_u32(&global->exit_threads, 0);
+
+ if (setup_sig_handler()) {
+ ODPH_ERR("Error: signal handler setup failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (parse_options(argc, argv, &global->test_options))
+ return -1;
+
+ odp_sys_info_print();
+
+ if (global->test_options.ctx_size) {
+ uint64_t size = (uint64_t)global->test_options.ctx_size *
+ global->test_options.tot_queue;
+
+ global->ctx_shm = odp_shm_reserve("queue contexts", size,
+ ODP_CACHE_LINE_SIZE, 0);
+ if (global->ctx_shm == ODP_SHM_INVALID) {
+ printf("Error: SHM reserve %" PRIu64 " bytes failed\n",
+ size);
+ return -1;
+ }
+ }
+
+ odp_schedule_config_init(&global->schedule_config);
+ odp_schedule_config(&global->schedule_config);
+
+ if (set_num_cpu(global))
+ return -1;
+
+ if (create_pool(global))
+ return -1;
+
+ if (create_groups(global))
+ return -1;
+
+ if (create_queues(global))
+ return -1;
+
+ if (global->test_options.verbose)
+ odp_shm_print_all();
+
+ /* Start workers */
+ start_workers(global, instance);
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
+
+ print_queue_fairness(global);
+
+ if (destroy_queues(global))
+ return -1;
+
+ if (destroy_groups(global))
+ return -1;
+
+ print_stat(global);
+
+ if (odp_pool_destroy(global->pool)) {
+ printf("Error: Pool destroy failed.\n");
+ return -1;
+ }
+
+ if (global->ctx_shm != ODP_SHM_INVALID)
+ odp_shm_free(global->ctx_shm);
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: SHM free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ printf("Error: term local failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Error: term global failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_sched_perf_run.sh b/test/performance/odp_sched_perf_run.sh
new file mode 100755
index 000000000..d4c8ebf6e
--- /dev/null
+++ b/test/performance/odp_sched_perf_run.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021-2024 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+run()
+{
+ # Maximum number of workers may be less than the number of available processors. One worker
+ # should be always available.
+ MAX_WORKERS=$(($(nproc) - 2))
+ if [ $MAX_WORKERS -lt 1 ]; then
+ MAX_WORKERS=1
+ fi
+
+ if [ $MAX_WORKERS -lt $1 ]; then
+ echo "Not enough CPU cores (requested $1, available $MAX_WORKERS). Skipping test."
+ else
+ echo odp_sched_perf -p 0 -c $1
+ echo ===============================================
+ $TEST_DIR/odp_sched_perf${EXEEXT} -p 0 -c $1
+ RET_VAL=$?
+ if [ $RET_VAL -ne 0 ]; then
+ echo odp_sched_perf FAILED
+ exit $RET_VAL
+ fi
+
+ echo odp_sched_perf -p 1 -c $1
+ echo ===============================================
+ $TEST_DIR/odp_sched_perf${EXEEXT} -p 1 -c $1
+ RET_VAL=$?
+ if [ $RET_VAL -ne 0 ]; then
+ echo odp_sched_perf FAILED
+ exit $RET_VAL
+ fi
+ fi
+}
+
+run 1
+run 2
+run 6
+
+exit 0
diff --git a/test/performance/odp_sched_pktio.c b/test/performance/odp_sched_pktio.c
new file mode 100644
index 000000000..eb79b6b69
--- /dev/null
+++ b/test/performance/odp_sched_pktio.c
@@ -0,0 +1,1598 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+/**
+ * @example odp_sched_pktio.c
+ *
+ * Test application for scheduled packet IO
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define DEBUG_PRINT 0
+#define MAX_WORKERS 64
+#define MAX_PKTIOS (ODP_PKTIO_MAX_INDEX + 1)
+#define MAX_PKTIO_NAME 31
+#define MAX_PKTIO_QUEUES MAX_WORKERS
+#define MAX_PIPE_STAGES 64
+#define MAX_PIPE_QUEUES 1024
+#define MAX_PKT_LEN 1514
+#define MAX_PKT_NUM (128 * 1024)
+#define MIN_PKT_SEG_LEN 64
+#define CHECK_PERIOD 10000
+#define TEST_PASSED_LIMIT 5000
+#define SCHED_MODE_PARAL 1
+#define SCHED_MODE_ATOMIC 2
+#define SCHED_MODE_ORDER 3
+
+typedef struct test_options_t {
+ long int timeout_us;
+ int sched_mode;
+ int num_worker;
+ int num_pktio;
+ int num_pktio_queue;
+ int burst_size;
+ int pipe_stages;
+ int pipe_queues;
+ uint32_t pipe_queue_size;
+ uint8_t collect_stat;
+ char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
+
+} test_options_t;
+
+typedef struct {
+ int worker_id;
+ void *test_global_ptr;
+} worker_arg_t;
+
+typedef struct ODP_ALIGNED_CACHE {
+ uint64_t rx_pkt;
+ uint64_t tx_pkt;
+ uint64_t pipe_pkt;
+ uint64_t tx_drop;
+ uint64_t pipe_drop;
+ uint64_t tmo;
+} worker_stat_t;
+
+typedef struct pktin_queue_context_t {
+ /* Queue context must start with stage and idx */
+ uint16_t stage;
+ uint16_t queue_idx;
+
+ uint8_t dst_pktio;
+ uint8_t dst_queue;
+ uint8_t src_pktio;
+ uint8_t src_queue;
+ odp_pktout_queue_t dst_pktout;
+} pktin_queue_context_t;
+
+typedef struct pipe_queue_context_t {
+ /* Queue context must start with stage and idx. */
+ uint16_t stage;
+ uint16_t queue_idx;
+} pipe_queue_context_t;
+
+typedef struct {
+ volatile int stop_workers;
+ odp_barrier_t worker_start;
+
+ test_options_t opt;
+
+ int max_workers;
+ odp_cpumask_t cpumask;
+ odp_instance_t instance;
+
+ int worker_cpu[MAX_WORKERS];
+
+ odp_pool_t pool;
+ uint32_t pkt_len;
+ uint32_t pkt_num;
+
+ struct {
+ odp_pktio_t pktio;
+ int pktio_index;
+ int started;
+ odph_ethaddr_t my_addr;
+ odp_queue_t input_queue[MAX_PKTIO_QUEUES];
+ odp_pktout_queue_t pktout[MAX_PKTIO_QUEUES];
+ pktin_queue_context_t queue_context[MAX_PKTIO_QUEUES];
+
+ } pktio[MAX_PKTIOS];
+
+ struct {
+ odp_timer_pool_t timer_pool;
+ odp_pool_t timeout_pool;
+ uint64_t timeout_tick;
+ odp_timer_t timer[MAX_PKTIOS][MAX_PKTIO_QUEUES];
+
+ } timer;
+
+ struct {
+ odp_queue_t queue[MAX_PIPE_QUEUES];
+ } pipe_queue[MAX_PKTIOS][MAX_PIPE_STAGES];
+
+ struct {
+ pipe_queue_context_t ctx;
+ } pipe_queue_ctx[MAX_PIPE_STAGES][MAX_PIPE_QUEUES];
+
+ worker_arg_t worker_arg[MAX_WORKERS];
+
+ worker_stat_t worker_stat[MAX_WORKERS];
+ uint64_t rx_pkt_sum;
+ uint64_t tx_pkt_sum;
+
+ odp_schedule_config_t schedule_config;
+
+} test_global_t;
+
+static test_global_t *test_global;
+
+static inline void set_dst_eth_addr(odph_ethaddr_t *eth_addr, int index)
+{
+ eth_addr->addr[0] = 0x02;
+ eth_addr->addr[1] = 0;
+ eth_addr->addr[2] = 0;
+ eth_addr->addr[3] = 0;
+ eth_addr->addr[4] = 0;
+ eth_addr->addr[5] = index;
+}
+
+static inline void fill_eth_addr(odp_packet_t pkt[], int num,
+ test_global_t *test_global, int out)
+{
+ odph_ethhdr_t *eth;
+ int i;
+
+ for (i = 0; i < num; ++i) {
+ eth = odp_packet_data(pkt[i]);
+
+ eth->src = test_global->pktio[out].my_addr;
+ set_dst_eth_addr(&eth->dst, out);
+ }
+}
+
+static inline void send_packets(test_global_t *test_global,
+ odp_packet_t pkt[], int num_pkt,
+ int output, odp_pktout_queue_t pktout,
+ int worker_id)
+{
+ int sent, drop;
+
+ fill_eth_addr(pkt, num_pkt, test_global, output);
+
+ sent = odp_pktout_send(pktout, pkt, num_pkt);
+
+ if (odp_unlikely(sent < 0))
+ sent = 0;
+
+ drop = num_pkt - sent;
+
+ if (odp_unlikely(drop > 0))
+ odp_packet_free_multi(&pkt[sent], drop);
+
+ if (odp_unlikely(test_global->opt.collect_stat)) {
+ test_global->worker_stat[worker_id].tx_pkt += sent;
+ test_global->worker_stat[worker_id].tx_drop += drop;
+ }
+}
+
+static int worker_thread_direct(void *arg)
+{
+ int num_pkt, out;
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue;
+ pktin_queue_context_t *queue_context;
+ worker_arg_t *worker_arg = arg;
+ test_global_t *test_global = worker_arg->test_global_ptr;
+ int worker_id = worker_arg->worker_id;
+ uint32_t polls = 0;
+ int burst_size = test_global->opt.burst_size;
+
+ printf("Worker %i started\n", worker_id);
+
+ /* Wait for other workers to start */
+ odp_barrier_wait(&test_global->worker_start);
+
+ while (1) {
+ odp_event_t ev[burst_size];
+ odp_packet_t pkt[burst_size];
+
+ polls++;
+
+ if (polls == CHECK_PERIOD) {
+ polls = 0;
+ if (test_global->stop_workers)
+ break;
+ }
+
+ num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
+ ev, burst_size);
+
+ if (num_pkt <= 0)
+ continue;
+
+ queue_context = odp_queue_context(queue);
+
+ if (DEBUG_PRINT)
+ printf("worker %i: [%i/%i] -> [%i/%i], %i packets\n",
+ worker_id,
+ queue_context->src_pktio,
+ queue_context->src_queue,
+ queue_context->dst_pktio,
+ queue_context->dst_queue, num_pkt);
+
+ odp_packet_from_event_multi(pkt, ev, num_pkt);
+
+ pktout = queue_context->dst_pktout;
+ out = queue_context->dst_pktio;
+
+ send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
+
+ if (odp_unlikely(test_global->opt.collect_stat))
+ test_global->worker_stat[worker_id].rx_pkt += num_pkt;
+ }
+
+ /*
+ * Free prefetched packets before exiting worker thread as
+ * such packets can block main thread event cleanup or
+ * cause buffer leak.
+ */
+ odp_schedule_pause();
+ while (1) {
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+ odp_event_free(ev);
+ }
+
+ /* Non-prefetched events in scheduler are cleaned up by main thread */
+ printf("Worker %i stopped\n", worker_id);
+
+ return 0;
+}
+
+static inline void enqueue_events(odp_queue_t dst_queue, odp_event_t ev[],
+ int num, int worker_id)
+{
+ int sent, drop;
+
+ sent = odp_queue_enq_multi(dst_queue, ev, num);
+
+ if (odp_unlikely(sent < 0))
+ sent = 0;
+
+ drop = num - sent;
+
+ if (odp_unlikely(drop))
+ odp_event_free_multi(&ev[sent], drop);
+
+ if (odp_unlikely(test_global->opt.collect_stat))
+ test_global->worker_stat[worker_id].pipe_drop += drop;
+}
+
+static inline odp_queue_t next_queue(test_global_t *test_global, int input,
+ uint16_t stage, uint16_t queue_idx)
+{
+ return test_global->pipe_queue[input][stage].queue[queue_idx];
+}
+
+static int worker_thread_pipeline(void *arg)
+{
+ int i, num_pkt, input, output, output_queue;
+ odp_queue_t queue, dst_queue;
+ odp_pktout_queue_t pktout;
+ pipe_queue_context_t *pipe_context;
+ uint16_t stage, queue_idx;
+ worker_arg_t *worker_arg = arg;
+ test_global_t *test_global = worker_arg->test_global_ptr;
+ int worker_id = worker_arg->worker_id;
+ int pipe_stages = test_global->opt.pipe_stages;
+ int pipe_queues = test_global->opt.pipe_queues;
+ int num_pktio = test_global->opt.num_pktio;
+ int num_pktio_queue = test_global->opt.num_pktio_queue;
+ uint32_t polls = 0;
+ int burst_size = test_global->opt.burst_size;
+
+ printf("Worker %i started\n", worker_id);
+
+ /* Wait for other workers to start */
+ odp_barrier_wait(&test_global->worker_start);
+
+ while (1) {
+ odp_event_t ev[burst_size];
+ odp_packet_t pkt[burst_size];
+
+ num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
+ ev, burst_size);
+
+ polls++;
+
+ if (polls == CHECK_PERIOD) {
+ polls = 0;
+ if (test_global->stop_workers)
+ break;
+ }
+
+ if (num_pkt <= 0)
+ continue;
+
+ pipe_context = odp_queue_context(queue);
+ stage = pipe_context->stage;
+ queue_idx = pipe_context->queue_idx;
+
+ /* A queue is connected to a single input interface. All
+ * packets from a queue are from the same interface. */
+ input = odp_packet_input_index(odp_packet_from_event(ev[0]));
+
+ if (DEBUG_PRINT)
+ printf("worker %i: stage %u, idx %u, %i packets\n",
+ worker_id, stage, queue_idx, num_pkt);
+
+ if (stage == 0) {
+ if (odp_unlikely(test_global->opt.collect_stat))
+ test_global->worker_stat[worker_id].rx_pkt +=
+ num_pkt;
+
+ /* The first stage (packet input). Forward packet flows
+ * into first pipeline queues. */
+ if (pipe_queues > num_pktio_queue) {
+ /* More pipeline queues than input queues.
+ * Use flow hash to spread flows into pipeline
+ * queues. */
+ odp_packet_t p;
+ worker_stat_t *stat;
+ uint32_t hash;
+ uint16_t idx;
+ int drop = 0;
+
+ stat = &test_global->worker_stat[worker_id];
+
+ for (i = 0; i < num_pkt; i++) {
+ p = odp_packet_from_event(ev[i]);
+ hash = odp_packet_flow_hash(p);
+ idx = queue_idx;
+
+ if (odp_packet_has_flow_hash(p))
+ idx = hash % pipe_queues;
+
+ dst_queue = next_queue(test_global,
+ input, stage,
+ idx);
+
+ if (odp_queue_enq(dst_queue, ev[i])) {
+ odp_event_free(ev[i]);
+ drop++;
+ }
+ }
+
+ if (odp_unlikely(test_global->opt.collect_stat))
+ stat->pipe_drop += drop;
+ } else {
+ queue_idx = queue_idx % pipe_queues;
+ dst_queue = next_queue(test_global, input,
+ stage, queue_idx);
+
+ enqueue_events(dst_queue, ev, num_pkt,
+ worker_id);
+ }
+ continue;
+ }
+
+ if (stage < pipe_stages) {
+ /* Middle stages */
+ dst_queue = next_queue(test_global, input, stage,
+ queue_idx);
+ enqueue_events(dst_queue, ev, num_pkt, worker_id);
+
+ if (odp_unlikely(test_global->opt.collect_stat))
+ test_global->worker_stat[worker_id].pipe_pkt +=
+ num_pkt;
+
+ continue;
+ }
+
+ /* The last stage, send packets out */
+ odp_packet_from_event_multi(pkt, ev, num_pkt);
+
+ /* If single interface loopback, otherwise forward to the next
+ * interface. */
+ output = (input + 1) % num_pktio;
+ output_queue = queue_idx % num_pktio_queue;
+ pktout = test_global->pktio[output].pktout[output_queue];
+
+ send_packets(test_global, pkt, num_pkt, output, pktout,
+ worker_id);
+ }
+
+ printf("Worker %i stopped\n", worker_id);
+
+ return 0;
+}
+
+static int worker_thread_timers(void *arg)
+{
+ int num, num_pkt, out, tmos, i, src_pktio, src_queue;
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue;
+ pktin_queue_context_t *queue_context;
+ odp_timer_t timer;
+ odp_timer_retval_t ret;
+ odp_timer_start_t start_param;
+ worker_arg_t *worker_arg = arg;
+ test_global_t *test_global = worker_arg->test_global_ptr;
+ int worker_id = worker_arg->worker_id;
+ uint32_t polls = 0;
+ int burst_size = test_global->opt.burst_size;
+ uint64_t tick = test_global->timer.timeout_tick;
+
+ printf("Worker (timers) %i started\n", worker_id);
+
+ /* Wait for other workers to start */
+ odp_barrier_wait(&test_global->worker_start);
+
+ while (1) {
+ odp_event_t ev[burst_size];
+ odp_packet_t pkt[burst_size];
+
+ num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
+ ev, burst_size);
+
+ polls++;
+
+ if (polls == CHECK_PERIOD) {
+ polls = 0;
+ if (test_global->stop_workers)
+ break;
+ }
+
+ if (num <= 0)
+ continue;
+
+ tmos = 0;
+ queue_context = odp_queue_context(queue);
+ src_pktio = queue_context->src_pktio;
+ src_queue = queue_context->src_queue;
+ timer = test_global->timer.timer[src_pktio][src_queue];
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tick;
+ start_param.tmo_ev = ODP_EVENT_INVALID;
+
+ for (i = 0; i < num; i++) {
+ if (odp_unlikely(odp_event_type(ev[i]) ==
+ ODP_EVENT_TIMEOUT)) {
+ tmos++;
+
+ start_param.tmo_ev = ev[i];
+ ret = odp_timer_start(timer, &start_param);
+
+ if (odp_unlikely(ret != ODP_TIMER_SUCCESS)) {
+ /* Should never happen. Timeout event
+ * has been received, timer should be
+ * ready to be set again. */
+ printf("Expired timer reset failed "
+ "%i\n", ret);
+ odp_event_free(ev[i]);
+ }
+
+ if (odp_unlikely(tmos > 1)) {
+ /* Should never happen */
+ printf("Too many timeouts\n");
+ }
+ } else {
+ pkt[i - tmos] = odp_packet_from_event(ev[i]);
+ }
+ }
+
+ if (tmos == 0) {
+ /* Reset timer with existing timeout event */
+ ret = odp_timer_restart(timer, &start_param);
+
+ if (odp_unlikely(ret != ODP_TIMER_SUCCESS &&
+ ret != ODP_TIMER_FAIL)) {
+ /* Tick period is too short or long. Normally,
+ * reset either succeeds or fails due to timer
+ * expiration, in which case timeout event will
+ * be received soon and reset will be done
+ * then. */
+ printf("Timer reset failed %i\n", ret);
+ }
+ }
+
+ num_pkt = num - tmos;
+
+ if (DEBUG_PRINT)
+ printf("worker %i: [%i/%i] -> [%i/%i], %i packets "
+ "%i timeouts\n",
+ worker_id,
+ queue_context->src_pktio,
+ queue_context->src_queue,
+ queue_context->dst_pktio,
+ queue_context->dst_queue, num_pkt, tmos);
+
+ if (odp_unlikely(test_global->opt.collect_stat && tmos))
+ test_global->worker_stat[worker_id].tmo += tmos;
+
+ if (odp_unlikely(num_pkt == 0))
+ continue;
+
+ pktout = queue_context->dst_pktout;
+ out = queue_context->dst_pktio;
+
+ send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
+
+ if (odp_unlikely(test_global->opt.collect_stat))
+ test_global->worker_stat[worker_id].rx_pkt += num_pkt;
+ }
+
+ printf("Worker %i stopped\n", worker_id);
+
+ return 0;
+}
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ if (test_global) {
+ test_global->stop_workers = 1;
+ odp_mb_full();
+ }
+}
+
+/* Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x))
+
+static void print_usage(const char *progname)
+{
+ printf("\n"
+ "Scheduler with packet IO test application.\n"
+ "\n"
+ "Usage: %s [options]\n"
+ "\n"
+ "OPTIONS:\n"
+ " -i, --interface <name> Packet IO interfaces (comma-separated, no spaces)\n"
+ " -c, --num_cpu <number> Worker thread count. Default: 1\n"
+ " -q, --num_queue <number> Number of pktio queues. Default: Worker thread count\n"
+ " -b, --burst <number> Maximum number of events requested from scheduler. Default: 32\n"
+ " -t, --timeout <number> Flow inactivity timeout (in usec) per packet. Default: 0 (don't use timers)\n"
+ " --pipe-stages <number> Number of pipeline stages per interface\n"
+ " --pipe-queues <number> Number of queues per pipeline stage\n"
+ " --pipe-queue-size <num> Number of events a pipeline queue must be able to store. Default 256.\n"
+ " -m, --sched_mode <mode> Scheduler synchronization mode for all queues. 1: parallel, 2: atomic, 3: ordered. Default: 2\n"
+ " -s, --stat Collect statistics.\n"
+ " -h, --help Display help and exit.\n\n",
+ NO_PATH(progname));
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int i, opt, long_index;
+ char *name, *str;
+ int len, str_len, sched_mode;
+ const struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'},
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_queue", required_argument, NULL, 'q'},
+ {"burst", required_argument, NULL, 'b'},
+ {"timeout", required_argument, NULL, 't'},
+ {"sched_mode", required_argument, NULL, 'm'},
+ {"pipe-stages", required_argument, NULL, 0},
+ {"pipe-queues", required_argument, NULL, 1},
+ {"pipe-queue-size", required_argument, NULL, 2},
+ {"stat", no_argument, NULL, 's'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *shortopts = "+i:c:q:b:t:m:sh";
+ int ret = 0;
+
+ memset(test_options, 0, sizeof(test_options_t));
+
+ test_options->sched_mode = SCHED_MODE_ATOMIC;
+ test_options->num_worker = 1;
+ test_options->num_pktio_queue = 0;
+ test_options->burst_size = 32;
+ test_options->pipe_queue_size = 256;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 0:
+ test_options->pipe_stages = atoi(optarg);
+ break;
+ case 1:
+ test_options->pipe_queues = atoi(optarg);
+ break;
+ case 2:
+ test_options->pipe_queue_size = atoi(optarg);
+ break;
+ case 'i':
+ i = 0;
+ str = optarg;
+ str_len = strlen(str);
+
+ while (str_len > 0) {
+ len = strcspn(str, ",");
+ str_len -= len + 1;
+
+ if (i == MAX_PKTIOS) {
+ printf("Error: Too many interfaces\n");
+ ret = -1;
+ break;
+ }
+
+ if (len > MAX_PKTIO_NAME) {
+ printf("Error: Too long interface name %s\n",
+ str);
+ ret = -1;
+ break;
+ }
+
+ name = test_options->pktio_name[i];
+ memcpy(name, str, len);
+ str += len + 1;
+ i++;
+ }
+
+ test_options->num_pktio = i;
+
+ break;
+ case 'c':
+ test_options->num_worker = atoi(optarg);
+ break;
+ case 'q':
+ test_options->num_pktio_queue = atoi(optarg);
+ break;
+ case 'b':
+ test_options->burst_size = atoi(optarg);
+ break;
+ case 't':
+ test_options->timeout_us = atol(optarg);
+ break;
+ case 'm':
+ test_options->sched_mode = atoi(optarg);
+ break;
+ case 's':
+ test_options->collect_stat = 1;
+ break;
+ case 'h':
+ print_usage(argv[0]);
+ ret = -1;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->timeout_us && test_options->pipe_stages) {
+ printf("Error: Cannot run timeout and pipeline tests simultaneously\n");
+ ret = -1;
+ }
+
+ if (test_options->pipe_stages > MAX_PIPE_STAGES) {
+ printf("Error: Too many pipeline stages\n");
+ ret = -1;
+ }
+
+ if (test_options->pipe_queues > MAX_PIPE_QUEUES) {
+ printf("Error: Too many queues per pipeline stage\n");
+ ret = -1;
+ }
+
+ if (test_options->num_pktio == 0) {
+ printf("Error: At least one pktio interface needed.\n");
+ ret = -1;
+ }
+
+ sched_mode = test_options->sched_mode;
+ if (sched_mode != SCHED_MODE_PARAL &&
+ sched_mode != SCHED_MODE_ATOMIC &&
+ sched_mode != SCHED_MODE_ORDER) {
+ printf("Error: Bad scheduler mode: %i\n", sched_mode);
+ ret = -1;
+ }
+
+ if (test_options->num_pktio_queue == 0)
+ test_options->num_pktio_queue = test_options->num_worker;
+
+ return ret;
+}
+
+static odp_schedule_sync_t sched_sync_mode(test_global_t *test_global)
+{
+ switch (test_global->opt.sched_mode) {
+ case SCHED_MODE_PARAL:
+ return ODP_SCHED_SYNC_PARALLEL;
+ case SCHED_MODE_ATOMIC:
+ return ODP_SCHED_SYNC_ATOMIC;
+ case SCHED_MODE_ORDER:
+ return ODP_SCHED_SYNC_ORDERED;
+ default:
+ return -1;
+ }
+}
+
+static int config_setup(test_global_t *test_global)
+{
+ int i, cpu;
+ odp_pool_capability_t pool_capa;
+ uint32_t pkt_len, pkt_num;
+ odp_cpumask_t *cpumask = &test_global->cpumask;
+
+ test_global->max_workers = odp_cpumask_default_worker(cpumask, 0);
+
+ if (test_global->opt.num_worker > test_global->max_workers ||
+ test_global->opt.num_worker > MAX_WORKERS) {
+ printf("Error: Too many workers %i.\n",
+ test_global->opt.num_worker);
+ return -1;
+ }
+
+ cpu = odp_cpumask_first(cpumask);
+ for (i = 0; i < test_global->opt.num_worker; ++i) {
+ test_global->worker_cpu[i] = cpu;
+ cpu = odp_cpumask_next(cpumask, cpu);
+ }
+
+ odp_schedule_config_init(&test_global->schedule_config);
+ odp_schedule_config(&test_global->schedule_config);
+
+ if (odp_pool_capability(&pool_capa)) {
+ printf("Error: Pool capability failed.\n");
+ return -1;
+ }
+
+ pkt_len = MAX_PKT_LEN;
+ pkt_num = MAX_PKT_NUM;
+
+ if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len)
+ pkt_len = pool_capa.pkt.max_len;
+
+ if (pool_capa.pkt.max_num && pkt_num > pool_capa.pkt.max_num) {
+ pkt_num = pool_capa.pkt.max_num;
+ printf("Warning: Pool size rounded down to %u\n", pkt_num);
+ }
+
+ test_global->pkt_len = pkt_len;
+ test_global->pkt_num = pkt_num;
+
+ return 0;
+}
+
+static void print_config(test_global_t *test_global)
+{
+ char cpumask_str[ODP_CPUMASK_STR_SIZE];
+ int i;
+
+ odp_cpumask_to_str(&test_global->cpumask, cpumask_str,
+ ODP_CPUMASK_STR_SIZE);
+
+ printf("\n"
+ "Test configuration:\n"
+ " max workers: %i\n"
+ " available worker cpus: %s\n"
+ " num workers: %i\n"
+ " worker cpus: ",
+ test_global->max_workers,
+ cpumask_str,
+ test_global->opt.num_worker);
+
+ for (i = 0; i < test_global->opt.num_worker; i++)
+ printf(" %i", test_global->worker_cpu[i]);
+
+ printf("\n"
+ " num interfaces: %i\n"
+ " interface names: ", test_global->opt.num_pktio);
+
+ for (i = 0; i < test_global->opt.num_pktio; i++)
+ printf(" %s", test_global->opt.pktio_name[i]);
+
+ printf("\n"
+ " queues per interface: %i\n",
+ test_global->opt.num_pktio_queue);
+
+ printf(" burst size: %u\n", test_global->opt.burst_size);
+ printf(" collect statistics: %u\n", test_global->opt.collect_stat);
+ printf(" timeout usec: %li\n", test_global->opt.timeout_us);
+
+ printf("\n");
+}
+
+static void print_stat(test_global_t *test_global, uint64_t nsec)
+{
+ int i;
+ uint64_t rx, tx, pipe, drop, tmo;
+ uint64_t rx_sum = 0;
+ uint64_t tx_sum = 0;
+ uint64_t pipe_sum = 0;
+ uint64_t tmo_sum = 0;
+ double sec = 0.0;
+
+ printf("\nTest statistics\n");
+ printf(" worker rx_pkt tx_pkt pipe dropped tmo\n");
+
+ for (i = 0; i < test_global->opt.num_worker; i++) {
+ rx = test_global->worker_stat[i].rx_pkt;
+ tx = test_global->worker_stat[i].tx_pkt;
+ pipe = test_global->worker_stat[i].pipe_pkt;
+ tmo = test_global->worker_stat[i].tmo;
+ rx_sum += rx;
+ tx_sum += tx;
+ pipe_sum += pipe;
+ tmo_sum += tmo;
+ drop = test_global->worker_stat[i].tx_drop +
+ test_global->worker_stat[i].pipe_drop;
+
+ printf(" %6i %16" PRIu64 " %16" PRIu64 " %16" PRIu64 " %16"
+ PRIu64 " %16" PRIu64 "\n", i, rx, tx, pipe, drop, tmo);
+ }
+
+ test_global->rx_pkt_sum = rx_sum;
+ test_global->tx_pkt_sum = tx_sum;
+ drop = rx_sum - tx_sum;
+
+ printf(" ------------------------------------------------------------------------------------\n");
+ printf(" total %16" PRIu64 " %16" PRIu64 " %16" PRIu64 " %16"
+ PRIu64 " %16" PRIu64 "\n\n", rx_sum, tx_sum, pipe_sum, drop,
+ tmo_sum);
+
+ sec = nsec / 1000000000.0;
+ printf(" Total test time: %.2f sec\n", sec);
+ printf(" Rx packet rate: %.2f pps\n", rx_sum / sec);
+ printf(" Tx packet rate: %.2f pps\n", tx_sum / sec);
+ printf(" Drop rate: %.2f pps\n", drop / sec);
+ printf(" Timeout rate: %.2f per sec\n\n", tmo_sum / sec);
+}
+
+static int open_pktios(test_global_t *test_global)
+{
+ odp_pool_param_t pool_param;
+ odp_pktio_param_t pktio_param;
+ odp_pool_t pool;
+ odp_pktio_t pktio;
+ odp_pktio_capability_t pktio_capa;
+ odp_pktio_config_t pktio_config;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktout_queue_param_t pktout_param;
+ odp_schedule_sync_t sched_sync;
+ uint32_t num_queue, j;
+ char *name;
+ int i, num_pktio, ret;
+
+ num_pktio = test_global->opt.num_pktio;
+ num_queue = test_global->opt.num_pktio_queue;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.seg_len = MIN_PKT_SEG_LEN;
+ pool_param.pkt.len = test_global->pkt_len;
+ pool_param.pkt.num = test_global->pkt_num;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet pool", &pool_param);
+
+ test_global->pool = pool;
+
+ if (pool == ODP_POOL_INVALID) {
+ printf("Error: Pool create.\n");
+ return -1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ sched_sync = sched_sync_mode(test_global);
+
+ for (i = 0; i < num_pktio; i++)
+ test_global->pktio[i].pktio = ODP_PKTIO_INVALID;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_pktio; i++) {
+ name = test_global->opt.pktio_name[i];
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ printf("Error (%s): Pktio open failed.\n", name);
+ return -1;
+ }
+
+ test_global->pktio[i].pktio = pktio;
+ test_global->pktio[i].pktio_index = odp_pktio_index(pktio);
+
+ ret = odp_pktio_mac_addr(pktio,
+ test_global->pktio[i].my_addr.addr,
+ ODPH_ETHADDR_LEN);
+ if (ret != ODPH_ETHADDR_LEN) {
+ printf("Error (%s): Bad MAC address len.\n", name);
+ return -1;
+ }
+
+ odp_pktio_print(pktio);
+
+ if (odp_pktio_capability(pktio, &pktio_capa)) {
+ printf("Error (%s): Pktio capa failed.\n", name);
+ return -1;
+ }
+
+ if (num_queue > pktio_capa.max_input_queues) {
+ printf("Error (%s): Too many input queues: %u\n",
+ name, num_queue);
+ return -1;
+ }
+
+ if (num_queue > pktio_capa.max_output_queues) {
+ printf("Error (%s): Too many output queues: %u\n",
+ name, num_queue);
+ return -1;
+ }
+
+ odp_pktio_config_init(&pktio_config);
+ pktio_config.parser.layer = ODP_PROTO_LAYER_NONE;
+
+ odp_pktio_config(pktio, &pktio_config);
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = sched_sync;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ if (num_queue > 1) {
+ pktin_param.hash_enable = 1;
+ pktin_param.hash_proto.proto.ipv4_udp = 1;
+ }
+
+ pktin_param.num_queues = num_queue;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ printf("Error (%s): Pktin config failed.\n", name);
+ return -1;
+ }
+
+ if (odp_pktin_event_queue(pktio,
+ test_global->pktio[i].input_queue,
+ num_queue) != (int)num_queue) {
+ printf("Error (%s): Input queue query failed.\n", name);
+ return -1;
+ }
+
+ for (j = 0; j < num_queue; j++) {
+ odp_queue_t queue;
+ void *ctx;
+ uint32_t len = sizeof(pktin_queue_context_t);
+
+ queue = test_global->pktio[i].input_queue[j];
+ ctx = &test_global->pktio[i].queue_context[j];
+
+ if (odp_queue_context_set(queue, ctx, len)) {
+ printf("Error (%s): Queue ctx set failed.\n",
+ name);
+ return -1;
+ }
+ }
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.num_queues = num_queue;
+ pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+ if (test_global->opt.pipe_stages)
+ pktout_param.op_mode = ODP_PKTIO_OP_MT;
+
+ if (odp_pktout_queue_config(pktio, &pktout_param)) {
+ printf("Error (%s): Pktout config failed.\n", name);
+ return -1;
+ }
+
+ if (odp_pktout_queue(pktio,
+ test_global->pktio[i].pktout,
+ num_queue) != (int)num_queue) {
+ printf("Error (%s): Output queue query failed.\n",
+ name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void link_pktios(test_global_t *test_global)
+{
+ int i, num_pktio, input, output;
+ int num_queue;
+ odp_pktout_queue_t pktout;
+ pktin_queue_context_t *ctx;
+
+ num_pktio = test_global->opt.num_pktio;
+ num_queue = test_global->opt.num_pktio_queue;
+
+ printf("Forwarding table (pktio indexes)\n");
+
+ /* If single interface loopback, otherwise forward to the next
+ * interface. */
+ for (input = 0; input < num_pktio; input++) {
+ output = (input + 1) % num_pktio;
+ printf(" input %i, output %i\n", input, output);
+
+ for (i = 0; i < num_queue; i++) {
+ ctx = &test_global->pktio[input].queue_context[i];
+ pktout = test_global->pktio[output].pktout[i];
+ ctx->stage = 0;
+ ctx->queue_idx = i;
+ ctx->dst_pktout = pktout;
+ ctx->dst_pktio = output;
+ ctx->dst_queue = i;
+ ctx->src_pktio = input;
+ ctx->src_queue = i;
+ }
+ }
+
+ printf("\n");
+}
+
+static int start_pktios(test_global_t *test_global)
+{
+ int i;
+
+ for (i = 0; i < test_global->opt.num_pktio; i++) {
+ if (odp_pktio_start(test_global->pktio[i].pktio)) {
+ printf("Error (%s): Pktio start failed.\n",
+ test_global->opt.pktio_name[i]);
+
+ return -1;
+ }
+
+ test_global->pktio[i].started = 1;
+ }
+
+ return 0;
+}
+
+static int stop_pktios(test_global_t *test_global)
+{
+ odp_pktio_t pktio;
+ int i, ret = 0;
+
+ for (i = 0; i < test_global->opt.num_pktio; i++) {
+ pktio = test_global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID ||
+ test_global->pktio[i].started == 0)
+ continue;
+
+ if (odp_pktio_stop(pktio)) {
+ printf("Error (%s): Pktio stop failed.\n",
+ test_global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static void empty_queues(uint64_t wait_ns)
+{
+ odp_event_t ev;
+ uint64_t wait_time = odp_schedule_wait_time(wait_ns);
+
+ /* Drop all events from all queues */
+ while (1) {
+ ev = odp_schedule(NULL, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+}
+
+static int close_pktios(test_global_t *test_global)
+{
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ int i, ret = 0;
+
+ for (i = 0; i < test_global->opt.num_pktio; i++) {
+ pktio = test_global->pktio[i].pktio;
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_close(pktio)) {
+ printf("Error (%s): Pktio close failed.\n",
+ test_global->opt.pktio_name[i]);
+ ret = -1;
+ }
+ }
+
+ pool = test_global->pool;
+
+ if (pool == ODP_POOL_INVALID)
+ return ret;
+
+ if (odp_pool_destroy(pool)) {
+ printf("Error: Pool destroy failed.\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int create_pipeline_queues(test_global_t *test_global)
+{
+ int i, j, k, num_pktio, stages, queues, ctx_size;
+ pipe_queue_context_t *ctx;
+ odp_queue_param_t queue_param;
+ odp_schedule_sync_t sched_sync;
+ int ret = 0;
+
+ num_pktio = test_global->opt.num_pktio;
+ stages = test_global->opt.pipe_stages;
+ queues = test_global->opt.pipe_queues;
+ sched_sync = sched_sync_mode(test_global);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.sync = sched_sync;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ queue_param.size = test_global->opt.pipe_queue_size;
+ if (test_global->schedule_config.queue_size &&
+ queue_param.size > test_global->schedule_config.queue_size) {
+ printf("Error: Pipeline queue max size is %u\n",
+ test_global->schedule_config.queue_size);
+ return -1;
+ }
+
+ ctx_size = sizeof(pipe_queue_context_t);
+
+ for (i = 0; i < stages; i++) {
+ for (j = 0; j < queues; j++) {
+ ctx = &test_global->pipe_queue_ctx[i][j].ctx;
+
+ /* packet input is stage 0 */
+ ctx->stage = i + 1;
+ ctx->queue_idx = j;
+ }
+ }
+
+ for (k = 0; k < num_pktio; k++) {
+ for (i = 0; i < stages; i++) {
+ for (j = 0; j < queues; j++) {
+ odp_queue_t q;
+
+ q = odp_queue_create(NULL, &queue_param);
+ test_global->pipe_queue[k][i].queue[j] = q;
+
+ if (q == ODP_QUEUE_INVALID) {
+ printf("Error: Queue create failed [%i] %i/%i\n",
+ k, i, j);
+ ret = -1;
+ break;
+ }
+
+ ctx = &test_global->pipe_queue_ctx[i][j].ctx;
+
+ if (odp_queue_context_set(q, ctx, ctx_size)) {
+ printf("Error: Queue ctx set failed [%i] %i/%i\n",
+ k, i, j);
+ ret = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void destroy_pipeline_queues(test_global_t *test_global)
+{
+ int i, j, k, num_pktio, stages, queues;
+ odp_queue_t queue;
+
+ num_pktio = test_global->opt.num_pktio;
+ stages = test_global->opt.pipe_stages;
+ queues = test_global->opt.pipe_queues;
+
+ for (k = 0; k < num_pktio; k++) {
+ for (i = 0; i < stages; i++) {
+ for (j = 0; j < queues; j++) {
+ queue = test_global->pipe_queue[k][i].queue[j];
+
+ if (queue == ODP_QUEUE_INVALID) {
+ printf("Error: Bad queue handle [%i] %i/%i\n",
+ k, i, j);
+ return;
+ }
+
+ if (odp_queue_destroy(queue)) {
+ printf("Error: Queue destroy failed [%i] %i/%i\n",
+ k, i, j);
+ return;
+ }
+ }
+ }
+ }
+}
+
+static int create_timers(test_global_t *test_global)
+{
+ int num_timer, num_pktio, num_queue, i, j;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_timer_pool_t timer_pool;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_capability_t timer_capa;
+ odp_timer_t timer;
+ odp_queue_t queue;
+ uint64_t res_ns, tick;
+ uint64_t timeout_ns = 1000 * test_global->opt.timeout_us;
+
+ num_pktio = test_global->opt.num_pktio;
+ num_queue = test_global->opt.num_pktio_queue;
+ num_timer = num_pktio * num_queue;
+
+ /* Always init globals for destroy calls */
+ test_global->timer.timer_pool = ODP_TIMER_POOL_INVALID;
+ test_global->timer.timeout_pool = ODP_POOL_INVALID;
+
+ for (i = 0; i < num_pktio; i++)
+ for (j = 0; j < num_queue; j++)
+ test_global->timer.timer[i][j] = ODP_TIMER_INVALID;
+
+ /* Timers not used */
+ if (test_global->opt.timeout_us == 0)
+ return 0;
+
+ if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
+ printf("Timer capa failed\n");
+ return -1;
+ }
+
+ res_ns = timeout_ns / 10;
+
+ if (timer_capa.highest_res_ns > res_ns) {
+ printf("Timeout too short. Min timeout %" PRIu64 " usec\n",
+ timer_capa.highest_res_ns / 100);
+ return -1;
+ }
+
+ odp_timer_pool_param_init(&timer_param);
+ timer_param.res_ns = res_ns;
+ timer_param.min_tmo = timeout_ns;
+ timer_param.max_tmo = timeout_ns;
+ timer_param.num_timers = num_timer;
+ timer_param.clk_src = ODP_CLOCK_DEFAULT;
+
+ timer_pool = odp_timer_pool_create("sched_pktio_timer", &timer_param);
+
+ if (timer_pool == ODP_TIMER_POOL_INVALID) {
+ printf("Timer pool create failed\n");
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ return -1;
+ }
+
+ test_global->timer.timer_pool = timer_pool;
+ tick = odp_timer_ns_to_tick(timer_pool, timeout_ns);
+ test_global->timer.timeout_tick = tick;
+
+ for (i = 0; i < num_pktio; i++) {
+ for (j = 0; j < num_queue; j++) {
+ queue = test_global->pktio[i].input_queue[j];
+ timer = odp_timer_alloc(timer_pool, queue, NULL);
+
+ if (timer == ODP_TIMER_INVALID) {
+ printf("Timer alloc failed.\n");
+ return -1;
+ }
+
+ test_global->timer.timer[i][j] = timer;
+ }
+ }
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = num_timer;
+
+ pool = odp_pool_create("timeout pool", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ printf("Timeout pool create failed.\n");
+ return -1;
+ }
+
+ test_global->timer.timeout_pool = pool;
+
+ return 0;
+}
+
+static int start_timers(test_global_t *test_global)
+{
+ int i, j;
+ odp_timeout_t timeout;
+ odp_timer_t timer;
+ odp_timer_retval_t ret;
+ odp_timer_start_t start_param;
+ uint64_t timeout_tick = test_global->timer.timeout_tick;
+ int num_pktio = test_global->opt.num_pktio;
+ int num_queue = test_global->opt.num_pktio_queue;
+ odp_pool_t pool = test_global->timer.timeout_pool;
+
+ /* Timers not used */
+ if (test_global->opt.timeout_us == 0)
+ return 0;
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = timeout_tick;
+
+ for (i = 0; i < num_pktio; i++) {
+ for (j = 0; j < num_queue; j++) {
+ timer = test_global->timer.timer[i][j];
+
+ timeout = odp_timeout_alloc(pool);
+ if (timeout == ODP_TIMEOUT_INVALID) {
+ printf("Timeout alloc failed\n");
+ return -1;
+ }
+
+ start_param.tmo_ev = odp_timeout_to_event(timeout);
+
+ ret = odp_timer_start(timer, &start_param);
+ if (ret != ODP_TIMER_SUCCESS) {
+ printf("Timer set failed\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void destroy_timers(test_global_t *test_global)
+{
+ int i, j;
+ odp_timer_t timer;
+ int num_pktio = test_global->opt.num_pktio;
+ int num_queue = test_global->opt.num_pktio_queue;
+ odp_timer_pool_t timer_pool = test_global->timer.timer_pool;
+ odp_pool_t pool = test_global->timer.timeout_pool;
+
+ if (timer_pool == ODP_TIMER_POOL_INVALID)
+ return;
+
+ /* Wait any remaining timers to expire */
+ empty_queues(2000 * test_global->opt.timeout_us);
+
+ for (i = 0; i < num_pktio; i++) {
+ for (j = 0; j < num_queue; j++) {
+ timer = test_global->timer.timer[i][j];
+
+ if (timer == ODP_TIMER_INVALID)
+ break;
+
+ if (odp_timer_free(timer))
+ printf("Timer free failed: %i, %i\n", i, j);
+ }
+ }
+
+ if (pool != ODP_POOL_INVALID) {
+ if (odp_pool_destroy(pool))
+ printf("Timeout pool destroy failed\n");
+ }
+
+ odp_timer_pool_destroy(timer_pool);
+}
+
+static void start_workers(odph_thread_t thread[],
+ test_global_t *test_global)
+{
+ int i;
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param[MAX_WORKERS];
+ int num = test_global->opt.num_worker;
+
+ odp_cpumask_zero(&cpumask);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = test_global->instance;
+ thr_common.cpumask = &cpumask;
+
+ for (i = 0; i < num; i++) {
+ odp_cpumask_set(&cpumask, test_global->worker_cpu[i]);
+ test_global->worker_arg[i].worker_id = i;
+ test_global->worker_arg[i].test_global_ptr = test_global;
+
+ odph_thread_param_init(&thr_param[i]);
+
+ if (!i) {
+ if (test_global->opt.timeout_us)
+ thr_param[0].start = worker_thread_timers;
+ else if (test_global->opt.pipe_stages)
+ thr_param[0].start = worker_thread_pipeline;
+ else
+ thr_param[0].start = worker_thread_direct;
+ } else {
+ thr_param[i].start = thr_param[0].start;
+ }
+
+ thr_param[i].arg = &test_global->worker_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ memset(thread, 0, num * sizeof(odph_thread_t));
+ odph_thread_create(thread, &thr_common, thr_param, num);
+}
+
+static void wait_workers(odph_thread_t thread[], test_global_t *test_global)
+{
+ odph_thread_join(thread, test_global->opt.num_worker);
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ odp_time_t t1 = ODP_TIME_NULL, t2 = ODP_TIME_NULL;
+ odph_helper_options_t helper_options;
+ odph_thread_t thread[MAX_WORKERS];
+ test_options_t test_options;
+ int ret = 0;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ printf("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGINT, sig_handler);
+
+ if (parse_options(argc, argv, &test_options))
+ return -1;
+
+ /* List features not to be used (may optimize performance) */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.tm = 1;
+ init.not_used.feat.timer = 1;
+
+ if (test_options.timeout_us)
+ init.not_used.feat.timer = 0;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ printf("Error: Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ printf("Error: Local init failed.\n");
+ return -1;
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("test_global", sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ printf("Error: shm reserve failed.\n");
+ return -1;
+ }
+
+ test_global = odp_shm_addr(shm);
+ memset(test_global, 0, sizeof(test_global_t));
+
+ test_global->instance = instance;
+ test_global->pool = ODP_POOL_INVALID;
+
+ memcpy(&test_global->opt, &test_options, sizeof(test_options_t));
+
+ odp_sys_info_print();
+
+ if (config_setup(test_global))
+ goto quit;
+
+ print_config(test_global);
+
+ if (open_pktios(test_global))
+ goto quit;
+
+ link_pktios(test_global);
+
+ if (create_pipeline_queues(test_global))
+ goto quit;
+
+ if (create_timers(test_global))
+ goto quit;
+
+ if (start_pktios(test_global))
+ goto quit;
+
+ odp_barrier_init(&test_global->worker_start,
+ test_global->opt.num_worker + 1);
+
+ start_workers(thread, test_global);
+
+ if (start_timers(test_global)) {
+ test_global->stop_workers = 1;
+ odp_mb_full();
+ }
+
+ /* Synchronize pktio configuration with workers. Worker are now ready
+ * to process packets. */
+ odp_barrier_wait(&test_global->worker_start);
+
+ t1 = odp_time_local();
+
+ wait_workers(thread, test_global);
+
+ t2 = odp_time_local();
+
+quit:
+ stop_pktios(test_global);
+ empty_queues(ODP_TIME_SEC_IN_NS / 2);
+ close_pktios(test_global);
+ destroy_pipeline_queues(test_global);
+ destroy_timers(test_global);
+
+ if (test_global->opt.collect_stat) {
+ print_stat(test_global, odp_time_diff_ns(t2, t1));
+
+ /* Encode return value for validation test usage. */
+ if (test_global->rx_pkt_sum > TEST_PASSED_LIMIT)
+ ret += 1;
+
+ if (test_global->tx_pkt_sum > TEST_PASSED_LIMIT)
+ ret += 2;
+ }
+ test_global = NULL;
+ odp_mb_full();
+
+ if (odp_shm_free(shm)) {
+ printf("Error: shm free failed.\n");
+ ret = -1;
+ }
+
+ if (odp_term_local()) {
+ printf("Error: term local failed.\n");
+ ret = -1;
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Error: term global failed.\n");
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_sched_pktio_run.sh b/test/performance/odp_sched_pktio_run.sh
new file mode 100755
index 000000000..0b7538ce9
--- /dev/null
+++ b/test/performance/odp_sched_pktio_run.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2018 Linaro Limited
+#
+
+# directory where test binaries have been built
+TEST_DIR="${TEST_DIR:-$PWD}"
+# directory where test sources are, including scripts
+TEST_SRC_DIR=$(dirname $0)
+
+PATH=$TEST_DIR:$PATH
+
+# exit codes expected by automake for skipped tests
+TEST_SKIPPED=77
+
+VALIDATION_TESTDIR=platform/$ODP_PLATFORM/test/validation
+PLATFORM_VALIDATION=${TEST_SRC_DIR}/../../$VALIDATION_TESTDIR
+
+# Use installed pktio env or for make check take it from platform directory
+if [ -f "./pktio_env" ]; then
+ . ./pktio_env
+elif [ "$ODP_PLATFORM" = "" ]; then
+ echo "$0: error: ODP_PLATFORM must be defined"
+ # not skipped as this should never happen via "make check"
+ exit 1
+elif [ -f ${PLATFORM_VALIDATION}/api/pktio/pktio_env ]; then
+ . ${PLATFORM_VALIDATION}/api/pktio/pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory or "
+ echo "in platform/\$ODP_PLATFORM/test."
+ echo "ODP_PLATFORM=\"$ODP_PLATFORM\""
+ exit 1
+fi
+
+run_sched_pktio()
+{
+ setup_pktio_env clean # install trap to call cleanup_pktio_env
+
+ if [ $? -ne 0 ]; then
+ echo "setup_pktio_env error $?"
+ exit $TEST_SKIPPED
+ fi
+
+ type odp_packet_gen > /dev/null
+ if [ $? -ne 0 ]; then
+ echo "odp_packet_gen not installed. Aborting."
+ cleanup_pktio_env
+ exit 1
+ fi
+
+ # 1 worker
+ odp_sched_pktio${EXEEXT} -i $IF1,$IF2 -c 1 -s &
+
+ TEST_PID=$!
+
+ sleep 1
+
+ # Run odp_packet_gen with one tx thread
+ (odp_packet_gen${EXEEXT} --gap 0 -i $IF0 \
+ --ipv4_src 192.168.0.1 --ipv4_dst 192.168.0.2 \
+ -r 0 -t 1 2>&1 > /dev/null) \
+ 2>&1 > /dev/null &
+
+ GEN_PID=$!
+
+ # Run test for 5 sec
+ sleep 5
+
+ kill -2 ${GEN_PID}
+ wait ${GEN_PID}
+
+ # Kill with SIGINT to output statistics
+ kill -2 ${TEST_PID}
+ wait ${TEST_PID}
+
+ ret=$?
+
+ if [ $ret -eq 3 ]; then
+ echo "PASS: received and transmitted over 5000 packets"
+ ret=0
+ else
+ echo "FAIL: less than thousand rx or tx packets $ret"
+ ret=1
+ fi
+
+ cleanup_pktio_env
+
+ exit $ret
+}
+
+case "$1" in
+ setup) setup_pktio_env ;;
+ cleanup) cleanup_pktio_env ;;
+ *) run_sched_pktio ;;
+esac
diff --git a/test/performance/odp_stash_perf.c b/test/performance/odp_stash_perf.c
new file mode 100644
index 000000000..cb223999e
--- /dev/null
+++ b/test/performance/odp_stash_perf.c
@@ -0,0 +1,523 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ * Copyright (c) 2023 Arm
+ */
+
+/**
+ * @example odp_stash_perf.c
+ *
+ * Performance test application for stash APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_STASHES (32)
+
+typedef struct test_options_t {
+ uint32_t num_stash;
+ uint32_t num_round;
+ uint32_t max_burst;
+ uint32_t stash_size;
+ int strict;
+ int num_cpu;
+
+} test_options_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t ops;
+ uint64_t nsec;
+ uint64_t cycles;
+ uint64_t num_retry;
+
+} test_stat_t;
+
+typedef struct test_global_t {
+ odp_barrier_t barrier;
+ test_options_t options;
+ odp_instance_t instance;
+ odp_shm_t shm;
+ odp_pool_t pool;
+ odp_stash_t stash[MAX_STASHES];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+
+} test_global_t;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Stash performance test\n"
+ "\n"
+ "Usage: odp_stash_perf [options]\n"
+ "\n"
+ " -c, --num_cpu <num> Number of worker threads. Default: 1\n"
+ " -n, --num_stash <num> Number of stashes. Default: 1\n"
+ " -b, --burst_size <num> Max number of objects per stash call. Default: 1\n"
+ " -s, --stash_size <num> Stash size. Default: 1000\n"
+ " -r, --num_round <num> Number of rounds. Default: 1000\n"
+ " -m, --strict Strict size stash\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ { "num_cpu", required_argument, NULL, 'c' },
+ { "num_stash", required_argument, NULL, 'n' },
+ { "burst_size", required_argument, NULL, 'b' },
+ { "stash_size", required_argument, NULL, 's' },
+ { "num_round", required_argument, NULL, 'r' },
+ { "strict", no_argument, NULL, 'm' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+c:n:b:s:r:mh";
+
+ test_options->num_cpu = 1;
+ test_options->num_stash = 1;
+ test_options->max_burst = 1;
+ test_options->stash_size = 1000;
+ test_options->num_round = 1000;
+ test_options->strict = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'n':
+ test_options->num_stash = atoi(optarg);
+ break;
+ case 'b':
+ test_options->max_burst = atoi(optarg);
+ break;
+ case 's':
+ test_options->stash_size = atoi(optarg);
+ break;
+ case 'r':
+ test_options->num_round = atoi(optarg);
+ break;
+ case 'm':
+ test_options->strict = 1;
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_stash > MAX_STASHES) {
+ ODPH_ERR("Too many stashes %u. Test maximum %u.\n",
+ test_options->num_stash, MAX_STASHES);
+ return -1;
+ }
+
+ return ret;
+}
+
+static int create_stashes(test_global_t *global)
+{
+ uint32_t i;
+ uint32_t tmp = 0;
+ test_options_t *test_options = &global->options;
+
+ uint32_t num_stash = test_options->num_stash;
+ uint32_t num_round = test_options->num_round;
+ int num_stored;
+ uint32_t num_remain;
+ odp_stash_t *stash = global->stash;
+ odp_stash_capability_t stash_capa;
+
+ printf("\nTesting %s stashes\n",
+ test_options->strict == 0 ? "NORMAL" : "STRICT_SIZE");
+ printf(" num rounds %u\n", num_round);
+ printf(" num stashes %u\n", num_stash);
+ printf(" stash size %u\n", test_options->stash_size);
+ printf(" max burst size %u\n", test_options->max_burst);
+
+ if (odp_stash_capability(&stash_capa, ODP_STASH_TYPE_DEFAULT)) {
+ ODPH_ERR("Get stash capability failed\n");
+ return -1;
+ }
+
+ if (test_options->stash_size > stash_capa.max_num_obj) {
+ ODPH_ERR("Max stash size supported %" PRIu64 "\n",
+ stash_capa.max_num_obj);
+ return -1;
+ }
+
+ if (test_options->num_stash > stash_capa.max_stashes) {
+ ODPH_ERR("Max stash supported %u\n", stash_capa.max_stashes);
+ return -1;
+ }
+
+ for (i = 0; i < num_stash; i++) {
+ odp_stash_param_t stash_param;
+
+ odp_stash_param_init(&stash_param);
+ stash_param.num_obj = test_options->stash_size;
+ stash_param.obj_size = sizeof(uint32_t);
+ stash_param.strict_size = test_options->strict;
+
+ stash[i] = odp_stash_create("test_stash_u32", &stash_param);
+ if (stash[i] == ODP_STASH_INVALID) {
+ ODPH_ERR("Stash create failed\n");
+ return -1;
+ }
+
+ num_remain = test_options->stash_size;
+ do {
+ num_stored = odp_stash_put_u32(stash[i], &tmp, 1);
+ if (num_stored < 0) {
+ ODPH_ERR("Error: Stash put failed\n");
+ return -1;
+ }
+ num_remain -= num_stored;
+ } while (num_remain);
+ }
+
+ return 0;
+}
+
+static int destroy_stashes(test_global_t *global)
+{
+ odp_stash_t *stash = global->stash;
+ test_options_t *test_options = &global->options;
+ uint32_t num_stash = test_options->num_stash;
+ uint32_t tmp;
+ int num;
+
+ for (uint32_t i = 0; i < num_stash; i++) {
+ do {
+ num = odp_stash_get_u32(stash[i], &tmp, 1);
+ if (num < 0) {
+ ODPH_ERR("Error: Stash get failed %u\n", i);
+ return -1;
+ }
+ } while (num);
+
+ if (odp_stash_destroy(stash[i])) {
+ ODPH_ERR("Stash destroy failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int run_test(void *arg)
+{
+ uint64_t c1, c2, cycles, nsec;
+ odp_time_t t1, t2;
+ uint32_t rounds;
+ int num_stored;
+ int num_remain;
+ int num_obj;
+ test_stat_t *stat;
+ test_global_t *global = arg;
+ test_options_t *test_options = &global->options;
+ odp_stash_t stash;
+ uint64_t num_retry = 0;
+ uint64_t ops = 0;
+ uint32_t num_stash = test_options->num_stash;
+ uint32_t num_round = test_options->num_round;
+ int thr = odp_thread_id();
+ int ret = 0;
+ uint32_t i = 0;
+ uint32_t max_burst = test_options->max_burst;
+ uint32_t *tmp = malloc(sizeof(uint32_t) * max_burst);
+
+ if (tmp == NULL) {
+ ODPH_ERR("Error: malloc failed\n");
+ ret = -1;
+ goto error;
+ }
+
+ stat = &global->stat[thr];
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+ c1 = odp_cpu_cycles();
+
+ for (rounds = 0; rounds < num_round; rounds++) {
+ stash = global->stash[i++];
+
+ if (i == num_stash)
+ i = 0;
+
+ num_obj = odp_stash_get_u32(stash, tmp, max_burst);
+ if (num_obj == 0)
+ continue;
+
+ if (num_obj < 0) {
+ ODPH_ERR("Error: Stash get failed\n");
+ ret = -1;
+ goto error;
+ }
+ num_remain = num_obj;
+ do {
+ num_stored = odp_stash_put_u32(stash, tmp, num_remain);
+ if (num_stored < 0) {
+ ODPH_ERR("Error: Stash put failed\n");
+ ret = -1;
+ goto error;
+ }
+
+ if (num_stored != num_remain)
+ num_retry++;
+
+ num_remain -= num_stored;
+ } while (num_remain);
+ ops += num_obj;
+ }
+
+ c2 = odp_cpu_cycles();
+ t2 = odp_time_local();
+
+ nsec = odp_time_diff_ns(t2, t1);
+ cycles = odp_cpu_cycles_diff(c2, c1);
+
+ stat->rounds = rounds;
+ stat->ops = ops;
+ stat->nsec = nsec;
+ stat->cycles = cycles;
+ stat->num_retry = num_retry;
+error:
+ free(tmp);
+ return ret;
+}
+
+static int start_workers(test_global_t *global)
+{
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_cpumask_t cpumask;
+ int ret;
+ test_options_t *test_options = &global->options;
+ int num_cpu = test_options->num_cpu;
+
+ ret = odp_cpumask_default_worker(&cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Error: Too many workers. Max supported %i\n.", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ printf(" num workers %u\n\n", num_cpu);
+
+ odp_barrier_init(&global->barrier, num_cpu);
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = global->instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_test;
+ thr_param.arg = global;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ if (odph_thread_create(global->thread_tbl, &thr_common, &thr_param,
+ num_cpu) != num_cpu)
+ return -1;
+
+ return 0;
+}
+
+static void print_stat(test_global_t *global)
+{
+ int i, num;
+ double rounds_ave, ops_ave, nsec_ave, cycles_ave, retry_ave;
+ test_options_t *test_options = &global->options;
+ int num_cpu = test_options->num_cpu;
+ uint64_t rounds_sum = 0;
+ uint64_t ops_sum = 0;
+ uint64_t nsec_sum = 0;
+ uint64_t cycles_sum = 0;
+ uint64_t retry_sum = 0;
+
+ /* Averages */
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ rounds_sum += global->stat[i].rounds;
+ ops_sum += global->stat[i].ops;
+ nsec_sum += global->stat[i].nsec;
+ cycles_sum += global->stat[i].cycles;
+ retry_sum += global->stat[i].num_retry;
+ }
+
+ if (rounds_sum == 0) {
+ printf("No results.\n");
+ return;
+ }
+
+ rounds_ave = rounds_sum / num_cpu;
+ ops_ave = ops_sum / num_cpu;
+ nsec_ave = nsec_sum / num_cpu;
+ cycles_ave = cycles_sum / num_cpu;
+ retry_ave = retry_sum / num_cpu;
+ num = 0;
+
+ printf("RESULTS - per thread (Million ops per sec):\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (1000.0 * global->stat[i].ops) /
+ global->stat[i].nsec);
+ num++;
+ }
+ }
+ printf("\n\n");
+
+ printf("RESULTS - per thread average (%i threads):\n", num_cpu);
+ printf("------------------------------------------\n");
+ printf(" duration: %.3f msec\n", nsec_ave / 1000000);
+ printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
+ printf(" ops per get: %.3f\n", ops_ave / rounds_ave);
+ printf(" cycles per ops: %.3f\n", cycles_ave / ops_ave);
+ printf(" retries per sec: %.3f k\n",
+ (1000000.0 * retry_ave) / nsec_ave);
+ printf(" ops per sec: %.3f M\n\n",
+ (1000.0 * ops_ave) / nsec_ave);
+
+ printf("TOTAL ops per sec: %.3f M\n\n",
+ (1000.0 * ops_sum) / nsec_ave);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Error: Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_WORKER)) {
+ ODPH_ERR("Error: Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm = odp_shm_reserve("stash_perf_global", sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Error: Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global, 0, sizeof(test_global_t));
+
+ if (parse_options(argc, argv, &global->options))
+ exit(EXIT_FAILURE);
+
+ odp_sys_info_print();
+
+ global->instance = instance;
+
+ if (create_stashes(global)) {
+ ODPH_ERR("Error: Create stashes failed.\n");
+ goto destroy;
+ }
+
+ if (start_workers(global)) {
+ ODPH_ERR("Error: Test start failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, global->options.num_cpu);
+
+ print_stat(global);
+
+destroy:
+ if (destroy_stashes(global)) {
+ ODPH_ERR("Error: Destroy stashes failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Error: Shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_stress.c b/test/performance/odp_stress.c
new file mode 100644
index 000000000..07faef886
--- /dev/null
+++ b/test/performance/odp_stress.c
@@ -0,0 +1,874 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 Nokia
+ */
+
+/**
+ * @example odp_stress.c
+ *
+ * Test application that can be used to stress CPU, memory, and HW accelerators.
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint64_t period_ns;
+ uint64_t rounds;
+ uint64_t mem_size;
+ int mode;
+ int group_mode;
+
+} test_options_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t tot_nsec;
+ uint64_t work_nsec;
+
+} test_stat_t;
+
+typedef struct test_stat_sum_t {
+ uint64_t rounds;
+ uint64_t tot_nsec;
+ uint64_t work_nsec;
+
+} test_stat_sum_t;
+
+typedef struct thread_arg_t {
+ void *global;
+ int worker_idx;
+
+} thread_arg_t;
+
+typedef struct test_global_t {
+ test_options_t test_options;
+ odp_atomic_u32_t exit_test;
+ odp_barrier_t barrier;
+ odp_cpumask_t cpumask;
+ odp_timer_pool_t timer_pool;
+ odp_pool_t tmo_pool;
+ uint64_t period_ticks;
+ uint8_t *worker_mem;
+ odp_timer_t timer[ODP_THREAD_COUNT_MAX];
+ odp_queue_t tmo_queue[ODP_THREAD_COUNT_MAX];
+ odp_schedule_group_t group[ODP_THREAD_COUNT_MAX];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+ thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
+ test_stat_sum_t stat_sum;
+ odp_atomic_u64_t tot_rounds;
+
+} test_global_t;
+
+test_global_t *test_global;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Stress test options:\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1\n"
+ " -p, --period_ns Timeout period in nsec. Default: 100 ms\n"
+ " -r, --rounds Number of timeout rounds. Default: 2\n"
+ " -m, --mode Select test mode. Default: 1\n"
+ " 0: No stress, just wait for timeouts\n"
+ " 1: Memcpy\n"
+ " -s, --mem_size Memory size per worker in bytes. Default: 2048\n"
+ " -g, --group_mode Select schedule group mode: Default: 1\n"
+ " 0: Use GROUP_ALL group. Scheduler load balances timeout events.\n"
+ " 1: Create a group per CPU. Dedicated timeout event per CPU.\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"period_ns", required_argument, NULL, 'p'},
+ {"rounds", required_argument, NULL, 'r'},
+ {"mode", required_argument, NULL, 'm'},
+ {"mem_size", required_argument, NULL, 's'},
+ {"group_mode", required_argument, NULL, 'g'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:p:r:m:s:g:h";
+
+ test_options->num_cpu = 1;
+ test_options->period_ns = 100 * ODP_TIME_MSEC_IN_NS;
+ test_options->rounds = 2;
+ test_options->mode = 1;
+ test_options->mem_size = 2048;
+ test_options->group_mode = 1;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'p':
+ test_options->period_ns = atoll(optarg);
+ break;
+ case 'r':
+ test_options->rounds = atoll(optarg);
+ break;
+ case 'm':
+ test_options->mode = atoi(optarg);
+ break;
+ case 's':
+ test_options->mem_size = atoll(optarg);
+ break;
+ case 'g':
+ test_options->group_mode = atoi(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->mode) {
+ if (test_options->mem_size < 2) {
+ ODPH_ERR("Too small memory size\n");
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+
+ /* One thread used for the main thread */
+ if (num_cpu < 0 || num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ ODPH_ERR("Bad number of workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Too many workers. Max supported %i\n.", ret);
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ odp_barrier_init(&global->barrier, num_cpu + 1);
+
+ return 0;
+}
+
+static int join_group(test_global_t *global, int worker_idx, int thr)
+{
+ odp_thrmask_t thrmask;
+ odp_schedule_group_t group;
+
+ odp_thrmask_zero(&thrmask);
+ odp_thrmask_set(&thrmask, thr);
+ group = global->group[worker_idx];
+
+ if (odp_schedule_group_join(group, &thrmask)) {
+ ODPH_ERR("Thread %i failed to join group %i\n", thr, worker_idx);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int worker_thread(void *arg)
+{
+ int thr, timer_ret;
+ uint32_t exit_test;
+ odp_event_t ev;
+ odp_timeout_t tmo;
+ odp_timer_t timer;
+ uint64_t tot_nsec, work_sum, max_nsec;
+ odp_timer_start_t start_param;
+ odp_time_t t1, t2, max_time;
+ odp_time_t work_t1, work_t2;
+ uint8_t *src = NULL, *dst = NULL;
+ thread_arg_t *thread_arg = arg;
+ int worker_idx = thread_arg->worker_idx;
+ test_global_t *global = thread_arg->global;
+ test_options_t *test_options = &global->test_options;
+ int mode = test_options->mode;
+ int group_mode = test_options->group_mode;
+ uint64_t mem_size = test_options->mem_size;
+ uint64_t copy_size = mem_size / 2;
+ uint64_t rounds = 0;
+ int ret = 0;
+ uint32_t done = 0;
+ uint64_t wait = ODP_SCHED_WAIT;
+ uint64_t tot_rounds = test_options->rounds * test_options->num_cpu;
+
+ thr = odp_thread_id();
+ max_nsec = 2 * test_options->rounds * test_options->period_ns;
+ max_time = odp_time_local_from_ns(max_nsec);
+ printf("Thread %i starting on CPU %i\n", thr, odp_cpu_id());
+
+ if (group_mode == 0) {
+ /* Timeout events are load balanced. Using this
+ * period to poll exit status. */
+ wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ } else {
+ if (join_group(global, worker_idx, thr)) {
+ /* Join failed, exit after barrier */
+ wait = ODP_SCHED_NO_WAIT;
+ done = 1;
+ }
+ }
+
+ if (mode) {
+ src = global->worker_mem + worker_idx * mem_size;
+ dst = src + copy_size;
+ }
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = global->period_ticks;
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ work_sum = 0;
+ t1 = odp_time_local();
+ max_time = odp_time_sum(t1, max_time);
+
+ while (1) {
+ ev = odp_schedule(NULL, wait);
+
+ exit_test = odp_atomic_load_u32(&global->exit_test);
+ exit_test += done;
+
+ if (ev == ODP_EVENT_INVALID) {
+ odp_time_t cur_time = odp_time_local();
+
+ if (odp_time_cmp(cur_time, max_time) > 0)
+ exit_test += 1;
+
+ if (exit_test) {
+ /* Exit loop without schedule context */
+ break;
+ }
+
+ continue;
+ }
+
+ rounds++;
+
+ if (group_mode) {
+ if (rounds >= test_options->rounds)
+ done = 1;
+ } else {
+ if (odp_atomic_fetch_inc_u64(&global->tot_rounds) >= (tot_rounds - 1))
+ done = 1;
+ }
+
+ if (done == 0) {
+ tmo = odp_timeout_from_event(ev);
+ timer = odp_timeout_timer(tmo);
+ start_param.tmo_ev = ev;
+
+ timer_ret = odp_timer_start(timer, &start_param);
+
+ if (timer_ret != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer start failed (%" PRIu64 ")\n", rounds);
+ done = 1;
+ }
+ }
+
+ /* Do work */
+ if (mode) {
+ work_t1 = odp_time_local();
+
+ memcpy(dst, src, copy_size);
+
+ work_t2 = odp_time_local();
+ work_sum += odp_time_diff_ns(work_t2, work_t1);
+ }
+
+ if (done) {
+ /* Stop timer and do not wait events */
+ wait = ODP_SCHED_NO_WAIT;
+ odp_event_free(ev);
+ }
+ }
+
+ t2 = odp_time_local();
+ tot_nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats*/
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].tot_nsec = tot_nsec;
+ global->stat[thr].work_nsec = work_sum;
+
+ return ret;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t thr_common;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ odph_thread_param_init(&thr_param[i]);
+ thr_param[i].start = worker_thread;
+ thr_param[i].arg = &global->thread_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param, num_cpu);
+
+ if (ret != num_cpu) {
+ ODPH_ERR("Thread create failed %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_timers(test_global_t *global)
+{
+ odp_timer_capability_t timer_capa;
+ odp_timer_res_capability_t timer_res_capa;
+ odp_timer_pool_param_t timer_pool_param;
+ odp_timer_pool_t tp;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ double duration;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ uint64_t period_ns = test_options->period_ns;
+ uint64_t res_ns = period_ns / 1000;
+
+ if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
+ ODPH_ERR("Timer capability failed\n");
+ return -1;
+ }
+
+ if (timer_capa.queue_type_sched == 0) {
+ ODPH_ERR("Timer does not support sched queues\n");
+ return -1;
+ }
+
+ memset(&timer_res_capa, 0, sizeof(odp_timer_res_capability_t));
+ timer_res_capa.max_tmo = 2 * period_ns;
+ if (odp_timer_res_capability(ODP_CLOCK_DEFAULT, &timer_res_capa)) {
+ ODPH_ERR("Timer resolution capability failed. Too long period.\n");
+ return -1;
+ }
+
+ if (res_ns < timer_res_capa.res_ns)
+ res_ns = timer_res_capa.res_ns;
+
+ duration = test_options->rounds * (double)period_ns / ODP_TIME_SEC_IN_NS;
+
+ printf(" num timers %u\n", num_cpu);
+ printf(" resolution %" PRIu64 " nsec\n", res_ns);
+ printf(" period %" PRIu64 " nsec\n", period_ns);
+ printf(" test duration %.2f sec\n", duration);
+ if (test_options->group_mode == 0)
+ printf(" force stop after %.2f sec\n", 2 * duration);
+ printf("\n");
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = num_cpu;
+
+ pool = odp_pool_create("Timeout pool", &pool_param);
+ global->tmo_pool = pool;
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+
+ odp_timer_pool_param_init(&timer_pool_param);
+ timer_pool_param.res_ns = res_ns;
+ timer_pool_param.min_tmo = period_ns / 2;
+ timer_pool_param.max_tmo = 2 * period_ns;
+ timer_pool_param.num_timers = 2 * num_cpu; /* extra for stop events */
+ timer_pool_param.clk_src = ODP_CLOCK_DEFAULT;
+
+ tp = odp_timer_pool_create("Stress timers", &timer_pool_param);
+ global->timer_pool = tp;
+ if (tp == ODP_TIMER_POOL_INVALID) {
+ ODPH_ERR("Timer pool create failed\n");
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&tp, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ return -1;
+ }
+
+ global->period_ticks = odp_timer_ns_to_tick(tp, period_ns);
+
+ return 0;
+}
+
+static int create_queues(test_global_t *global)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_thrmask_t thrmask;
+ odp_queue_param_t queue_param;
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("Schedule capability failed\n");
+ return -1;
+ }
+
+ if (test_options->group_mode) {
+ if ((sched_capa.max_groups - 1) < num_cpu) {
+ ODPH_ERR("Too many workers. Not enough schedule groups.\n");
+ return -1;
+ }
+
+ odp_thrmask_zero(&thrmask);
+
+ /* A group per worker thread */
+ for (i = 0; i < num_cpu; i++) {
+ global->group[i] = odp_schedule_group_create(NULL, &thrmask);
+
+ if (global->group[i] == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("Schedule group create failed (%u)\n", i);
+ return -1;
+ }
+ }
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ for (i = 0; i < num_cpu; i++) {
+ if (test_options->group_mode)
+ queue_param.sched.group = global->group[i];
+
+ global->tmo_queue[i] = odp_queue_create(NULL, &queue_param);
+
+ if (global->tmo_queue[i] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Timeout dest queue create failed (%u)\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int start_timers(test_global_t *global)
+{
+ odp_timer_start_t start_param;
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ odp_timeout_t tmo[num_cpu];
+ odp_timer_t timer[num_cpu];
+
+ for (i = 0; i < num_cpu; i++) {
+ tmo[i] = odp_timeout_alloc(global->tmo_pool);
+
+ if (tmo[i] == ODP_TIMEOUT_INVALID) {
+ ODPH_ERR("Timeout alloc failed (%u)\n", i);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < num_cpu; i++) {
+ timer[i] = odp_timer_alloc(global->timer_pool, global->tmo_queue[i], NULL);
+
+ if (timer[i] == ODP_TIMER_INVALID) {
+ ODPH_ERR("Timer alloc failed (%u)\n", i);
+ return -1;
+ }
+
+ global->timer[i] = timer[i];
+ }
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = global->period_ticks;
+
+ for (i = 0; i < num_cpu; i++) {
+ start_param.tmo_ev = odp_timeout_to_event(tmo[i]);
+
+ if (odp_timer_start(timer[i], &start_param) != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer start failed (%u)\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void destroy_timers(test_global_t *global)
+{
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+
+ for (i = 0; i < num_cpu; i++) {
+ odp_timer_t timer = global->timer[i];
+
+ if (timer == ODP_TIMER_INVALID)
+ continue;
+
+ if (odp_timer_free(timer))
+ ODPH_ERR("Timer free failed (%u)\n", i);
+ }
+
+ if (global->timer_pool != ODP_TIMER_POOL_INVALID)
+ odp_timer_pool_destroy(global->timer_pool);
+
+ for (i = 0; i < num_cpu; i++) {
+ odp_queue_t queue = global->tmo_queue[i];
+
+ if (queue == ODP_QUEUE_INVALID)
+ continue;
+
+ if (odp_queue_destroy(queue))
+ ODPH_ERR("Queue destroy failed (%u)\n", i);
+ }
+
+ if (test_options->group_mode) {
+ for (i = 0; i < num_cpu; i++) {
+ odp_schedule_group_t group = global->group[i];
+
+ if (group == ODP_SCHED_GROUP_INVALID)
+ continue;
+
+ if (odp_schedule_group_destroy(group))
+ ODPH_ERR("Schedule group destroy failed (%u)\n", i);
+ }
+ }
+
+ if (global->tmo_pool != ODP_POOL_INVALID)
+ odp_pool_destroy(global->tmo_pool);
+}
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ if (test_global == NULL)
+ return;
+
+ odp_atomic_add_u32(&test_global->exit_test, 1);
+}
+
+static void stop_workers(test_global_t *global)
+{
+ uint32_t i;
+ odp_timeout_t tmo;
+ odp_event_t ev;
+ odp_queue_t queue;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+
+ odp_atomic_add_u32(&test_global->exit_test, 1);
+
+ for (i = 0; i < num_cpu; i++) {
+ queue = global->tmo_queue[i];
+ if (queue == ODP_QUEUE_INVALID)
+ continue;
+
+ tmo = odp_timeout_alloc(global->tmo_pool);
+
+ if (tmo == ODP_TIMEOUT_INVALID)
+ continue;
+
+ ev = odp_timeout_to_event(tmo);
+ if (odp_queue_enq(queue, ev)) {
+ ODPH_ERR("Enqueue failed %u\n", i);
+ odp_event_free(ev);
+ }
+ }
+}
+
+static void sum_stat(test_global_t *global)
+{
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ test_stat_sum_t *sum = &global->stat_sum;
+
+ memset(sum, 0, sizeof(test_stat_sum_t));
+
+ for (i = 1; i < num_cpu + 1 ; i++) {
+ sum->rounds += global->stat[i].rounds;
+ sum->tot_nsec += global->stat[i].tot_nsec;
+ sum->work_nsec += global->stat[i].work_nsec;
+ }
+}
+
+static void print_stat(test_global_t *global)
+{
+ uint32_t i;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ int mode = test_options->mode;
+ test_stat_sum_t *sum = &global->stat_sum;
+ double sec_ave, work_ave, perc;
+ double round_ave = 0.0;
+ double copy_ave = 0.0;
+ double copy_tot = 0.0;
+ double cpu_load = 0.0;
+ const double mega = 1000000.0;
+ const double giga = 1000000000.0;
+ uint32_t num = 0;
+
+ if (num_cpu == 0)
+ return;
+
+ sec_ave = (sum->tot_nsec / giga) / num_cpu;
+ work_ave = (sum->work_nsec / giga) / num_cpu;
+
+ printf("\n");
+ printf("CPU load from work (percent) per thread:\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 1; i < num_cpu + 1; i++) {
+ if (global->stat[i].tot_nsec == 0)
+ continue;
+
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ perc = 100.0 * ((double)global->stat[i].work_nsec) / global->stat[i].tot_nsec;
+
+ printf("%6.2f ", perc);
+ num++;
+ }
+
+ if (sec_ave != 0.0) {
+ round_ave = (double)sum->rounds / num_cpu;
+ cpu_load = 100.0 * (work_ave / sec_ave);
+
+ if (mode) {
+ uint64_t copy_bytes = sum->rounds * test_options->mem_size / 2;
+
+ copy_ave = copy_bytes / (sum->work_nsec / giga);
+ copy_tot = copy_ave * num_cpu;
+ }
+ }
+
+ printf("\n\n");
+ printf("TOTAL (%i workers)\n", num_cpu);
+ printf(" ave time: %.2f sec\n", sec_ave);
+ printf(" ave work: %.2f sec\n", work_ave);
+ printf(" ave CPU load: %.2f\n", cpu_load);
+ printf(" ave rounds per sec: %.2f\n", round_ave / sec_ave);
+ printf(" ave copy speed: %.2f MB/sec\n", copy_ave / mega);
+ printf(" total copy speed: %.2f MB/sec\n", copy_tot / mega);
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm, shm_global;
+ odp_schedule_config_t sched_config;
+ test_global_t *global;
+ test_options_t *test_options;
+ int i, mode;
+ uint32_t num_cpu;
+ uint64_t mem_size;
+ odp_shm_t shm_work = ODP_SHM_INVALID;
+
+ signal(SIGINT, sig_handler);
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_init_param_init(&init);
+ init.mem_model = helper_options.mem_model;
+
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ shm = odp_shm_reserve("Stress global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ shm_global = shm;
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("SHM addr failed\n");
+ exit(EXIT_FAILURE);
+ }
+ test_global = global;
+
+ memset(global, 0, sizeof(test_global_t));
+ odp_atomic_init_u32(&global->exit_test, 0);
+ odp_atomic_init_u64(&global->tot_rounds, 0);
+
+ global->timer_pool = ODP_TIMER_POOL_INVALID;
+ global->tmo_pool = ODP_POOL_INVALID;
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ global->timer[i] = ODP_TIMER_INVALID;
+ global->tmo_queue[i] = ODP_QUEUE_INVALID;
+ global->group[i] = ODP_SCHED_GROUP_INVALID;
+
+ global->thread_arg[i].global = global;
+ global->thread_arg[i].worker_idx = i;
+ }
+
+ if (parse_options(argc, argv, &global->test_options))
+ exit(EXIT_FAILURE);
+
+ test_options = &global->test_options;
+ mode = test_options->mode;
+
+ odp_sys_info_print();
+
+ odp_schedule_config_init(&sched_config);
+ sched_config.sched_group.all = 1;
+ sched_config.sched_group.control = 0;
+ sched_config.sched_group.worker = 0;
+
+ odp_schedule_config(&sched_config);
+
+ if (set_num_cpu(global))
+ exit(EXIT_FAILURE);
+
+ num_cpu = test_options->num_cpu;
+
+ /* Memory for workers */
+ if (mode) {
+ mem_size = test_options->mem_size * num_cpu;
+
+ shm = odp_shm_reserve("Test memory", mem_size, ODP_CACHE_LINE_SIZE, 0);
+ shm_work = shm;
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global->worker_mem = odp_shm_addr(shm);
+ if (global->worker_mem == NULL) {
+ ODPH_ERR("SHM addr failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(global->worker_mem, 0, mem_size);
+ }
+
+ printf("\n");
+ printf("Test parameters\n");
+ printf(" num workers %u\n", num_cpu);
+ printf(" mode %i\n", mode);
+ printf(" group mode %i\n", test_options->group_mode);
+ printf(" mem size per worker %" PRIu64 " bytes\n", test_options->mem_size);
+
+ if (create_timers(global))
+ exit(EXIT_FAILURE);
+
+ if (create_queues(global))
+ exit(EXIT_FAILURE);
+
+ /* Start worker threads */
+ start_workers(global, instance);
+
+ /* Wait until all workers are ready */
+ odp_barrier_wait(&global->barrier);
+
+ if (start_timers(global)) {
+ /* Stop all workers, if some timer did not start */
+ ODPH_ERR("Timers did not start. Stopping workers.\n");
+ stop_workers(global);
+ }
+
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl, num_cpu);
+
+ sum_stat(global);
+
+ print_stat(global);
+
+ destroy_timers(global);
+
+ if (mode) {
+ if (odp_shm_free(shm_work)) {
+ ODPH_ERR("SHM free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (odp_shm_free(shm_global)) {
+ ODPH_ERR("SHM free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Term local failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Term global failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_timer_accuracy.c b/test/performance/odp_timer_accuracy.c
new file mode 100644
index 000000000..e5df1c24e
--- /dev/null
+++ b/test/performance/odp_timer_accuracy.c
@@ -0,0 +1,1435 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+/**
+ * @example odp_timer_accuracy.c
+ *
+ * ODP timer accuracy test application
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+#define MAX_QUEUES 1024
+#define MAX_FILENAME 128
+
+enum mode_e {
+ MODE_ONESHOT = 0,
+ MODE_RESTART_ABS,
+ MODE_RESTART_REL,
+ MODE_PERIODIC,
+};
+
+typedef struct test_opt_t {
+ int cpu_count;
+ unsigned long long period_ns;
+ long long res_ns;
+ unsigned long long res_hz;
+ unsigned long long offset_ns;
+ unsigned long long max_tmo_ns;
+ unsigned long long num;
+ unsigned long long num_warmup;
+ unsigned long long burst;
+ unsigned long long burst_gap;
+ odp_fract_u64_t freq;
+ unsigned long long max_multiplier;
+ unsigned long long multiplier;
+ enum mode_e mode;
+ int clk_src;
+ odp_queue_type_t queue_type;
+ int num_queue;
+ int groups;
+ int init;
+ int output;
+ int early_retry;
+ uint64_t warmup_timers;
+ uint64_t tot_timers;
+ uint64_t alloc_timers;
+ char filename[MAX_FILENAME + 1];
+} test_opt_t;
+
+typedef struct timer_ctx_t {
+ odp_timer_t timer;
+ odp_event_t event;
+ uint64_t nsec;
+ uint64_t count;
+ uint64_t first_period;
+ int tmo_tick;
+ int64_t first_tmo_diff;
+ int64_t nsec_final;
+
+} timer_ctx_t;
+
+typedef struct {
+ uint64_t nsec_before_sum;
+ uint64_t nsec_before_min;
+ uint64_t nsec_before_min_idx;
+ uint64_t nsec_before_max;
+ uint64_t nsec_before_max_idx;
+
+ uint64_t nsec_after_sum;
+ uint64_t nsec_after_min;
+ uint64_t nsec_after_min_idx;
+ uint64_t nsec_after_max;
+ uint64_t nsec_after_max_idx;
+
+ uint64_t num_before;
+ uint64_t num_exact;
+ uint64_t num_after;
+
+ uint64_t num_too_near;
+
+} test_stat_t;
+
+typedef struct test_log_t {
+ uint64_t tmo_ns;
+ int64_t diff_ns;
+ int tid;
+
+} test_log_t;
+
+typedef struct test_global_t {
+ test_opt_t opt;
+
+ test_stat_t stat[MAX_WORKERS];
+
+ odp_queue_t queue[MAX_QUEUES];
+ odp_schedule_group_t group[MAX_WORKERS];
+ odp_timer_pool_t timer_pool;
+ odp_pool_t timeout_pool;
+ timer_ctx_t *timer_ctx;
+ double res_ns;
+ uint64_t start_tick;
+ uint64_t start_ns;
+ uint64_t period_tick;
+ double period_dbl;
+ odp_fract_u64_t base_freq;
+ test_log_t *log;
+ FILE *file;
+ odp_barrier_t barrier;
+ odp_atomic_u64_t events;
+ odp_atomic_u64_t last_events;
+
+} test_global_t;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Timer accuracy test application.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -c, --count <num> CPU count, 0=all available, default=1\n"
+ " -p, --period <nsec> Timeout period in nsec. Not used in periodic mode. Default: 200 msec\n"
+ " -r, --res_ns <nsec> Timeout resolution in nsec. Default value is 0. Special values:\n"
+ " 0: Use period / 10 as the resolution\n"
+ " -1: In periodic mode, use resolution from capabilities\n"
+ " -R, --res_hz <hertz> Timeout resolution in hertz. Set resolution either with -r (nsec) or -R (hertz),\n"
+ " and leave other to 0. Default: 0 (not used)\n"
+ " -f, --first <nsec> First timer offset in nsec. Default: 0 for periodic mode, otherwise 300 msec\n"
+ " -x, --max_tmo <nsec> Maximum timeout in nsec. Not used in periodic mode.\n"
+ " When 0, max tmo is calculated from other options. Default: 0\n"
+ " -n, --num <number> Number of timeout periods. Default: 50\n"
+ " -w, --warmup <number> Number of warmup periods. Default: 0\n"
+ " -b, --burst <number> Number of timers per a timeout period. Default: 1\n"
+ " -g, --burst_gap <nsec> Gap (in nsec) between timers within a burst. Default: 0\n"
+ " In periodic mode, first + burst * burst_gap must be less than period length.\n"
+ " -m, --mode <number> Test mode select (default: 0):\n"
+ " 0: One-shot. Start all timers at init phase.\n"
+ " 1: One-shot. Each period, restart timers with absolute time.\n"
+ " 2: One-shot. Each period, restart timers with relative time.\n"
+ " 3: Periodic.\n"
+ " -P, --periodic <freq_integer:freq_numer:freq_denom:max_multiplier>\n"
+ " Periodic timer pool parameters. Default: 5:0:0:1 (5 Hz)\n"
+ " -M, --multiplier Periodic timer multiplier. Default: 1\n"
+ " -o, --output <file> Output file for measurement logs\n"
+ " -e, --early_retry <num> When timer restart fails due to ODP_TIMER_TOO_NEAR, retry this many times\n"
+ " with expiration time incremented by the period. Default: 0\n"
+ " -s, --clk_src Clock source select (default 0):\n"
+ " 0: ODP_CLOCK_DEFAULT\n"
+ " 1: ODP_CLOCK_SRC_1, ...\n"
+ " -t, --queue_type Queue sync type. Default is 0 (PARALLEL).\n"
+ " 0: PARALLEL\n"
+ " 1: ATOMIC\n"
+ " 2: ORDERED\n"
+ " -q, --num_queue Number of queues. Default is 1.\n"
+ " -G, --sched_groups Use dedicated schedule group for each worker.\n"
+ " -i, --init Set global init parameters. Default: init params not set.\n"
+ " -h, --help Display help and exit.\n\n");
+}
+
+static int parse_options(int argc, char *argv[], test_opt_t *test_opt)
+{
+ int opt, long_index;
+ const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"period", required_argument, NULL, 'p'},
+ {"res_ns", required_argument, NULL, 'r'},
+ {"res_hz", required_argument, NULL, 'R'},
+ {"first", required_argument, NULL, 'f'},
+ {"max_tmo", required_argument, NULL, 'x'},
+ {"num", required_argument, NULL, 'n'},
+ {"warmup", required_argument, NULL, 'w'},
+ {"burst", required_argument, NULL, 'b'},
+ {"burst_gap", required_argument, NULL, 'g'},
+ {"mode", required_argument, NULL, 'm'},
+ {"periodic", required_argument, NULL, 'P'},
+ {"multiplier", required_argument, NULL, 'M'},
+ {"output", required_argument, NULL, 'o'},
+ {"early_retry", required_argument, NULL, 'e'},
+ {"clk_src", required_argument, NULL, 's'},
+ {"queue_type", required_argument, NULL, 't'},
+ {"num_queue", required_argument, NULL, 'q'},
+ {"sched_groups", no_argument, NULL, 'G'},
+ {"init", no_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *shortopts = "+c:p:r:R:f:x:n:w:b:g:m:P:M:o:e:s:t:q:Gih";
+ int ret = 0;
+
+ memset(test_opt, 0, sizeof(*test_opt));
+
+ test_opt->cpu_count = 1;
+ test_opt->period_ns = 200 * ODP_TIME_MSEC_IN_NS;
+ test_opt->res_ns = 0;
+ test_opt->res_hz = 0;
+ test_opt->offset_ns = UINT64_MAX;
+ test_opt->max_tmo_ns = 0;
+ test_opt->num = 50;
+ test_opt->num_warmup = 0;
+ test_opt->burst = 1;
+ test_opt->burst_gap = 0;
+ test_opt->mode = MODE_ONESHOT;
+ test_opt->freq.integer = ODP_TIME_SEC_IN_NS / test_opt->period_ns;
+ test_opt->freq.numer = 0;
+ test_opt->freq.denom = 0;
+ test_opt->max_multiplier = 1;
+ test_opt->multiplier = 1;
+ test_opt->clk_src = ODP_CLOCK_DEFAULT;
+ test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
+ test_opt->groups = 0;
+ test_opt->num_queue = 1;
+ test_opt->init = 0;
+ test_opt->output = 0;
+ test_opt->early_retry = 0;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ test_opt->cpu_count = atoi(optarg);
+ break;
+ case 'p':
+ test_opt->period_ns = strtoull(optarg, NULL, 0);
+ break;
+ case 'r':
+ test_opt->res_ns = strtoll(optarg, NULL, 0);
+ break;
+ case 'R':
+ test_opt->res_hz = strtoull(optarg, NULL, 0);
+ break;
+ case 'f':
+ test_opt->offset_ns = strtoull(optarg, NULL, 0);
+ break;
+ case 'x':
+ test_opt->max_tmo_ns = strtoull(optarg, NULL, 0);
+ break;
+ case 'n':
+ test_opt->num = strtoull(optarg, NULL, 0);
+ break;
+ case 'w':
+ test_opt->num_warmup = strtoull(optarg, NULL, 0);
+ break;
+ case 'b':
+ test_opt->burst = strtoull(optarg, NULL, 0);
+ break;
+ case 'g':
+ test_opt->burst_gap = strtoull(optarg, NULL, 0);
+ break;
+ case 'm':
+ test_opt->mode = atoi(optarg);
+ break;
+ case 'P':
+ sscanf(optarg, "%" SCNu64 ":%" SCNu64 ":%" SCNu64 ":%llu",
+ &test_opt->freq.integer, &test_opt->freq.numer,
+ &test_opt->freq.denom, &test_opt->max_multiplier);
+ break;
+ case 'M':
+ test_opt->multiplier = strtoull(optarg, NULL, 0);
+ break;
+ case 'o':
+ test_opt->output = 1;
+ /* filename is NULL terminated in anycase */
+ strncpy(test_opt->filename, optarg, MAX_FILENAME);
+ break;
+ case 'e':
+ test_opt->early_retry = atoi(optarg);
+ break;
+ case 's':
+ test_opt->clk_src = atoi(optarg);
+ break;
+ case 't':
+ switch (atoi(optarg)) {
+ case 1:
+ test_opt->queue_type = ODP_SCHED_SYNC_ATOMIC;
+ break;
+ case 2:
+ test_opt->queue_type = ODP_SCHED_SYNC_ORDERED;
+ break;
+ default:
+ test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
+ break;
+ }
+ break;
+ case 'q':
+ test_opt->num_queue = atoi(optarg);
+ break;
+ case 'G':
+ test_opt->groups = 1;
+ break;
+ case 'i':
+ test_opt->init = 1;
+ break;
+ case 'h':
+ print_usage();
+ ret = -1;
+ break;
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_opt->mode == MODE_PERIODIC) {
+ if ((test_opt->freq.integer == 0 && test_opt->freq.numer == 0) ||
+ (test_opt->freq.numer != 0 && test_opt->freq.denom == 0)) {
+ printf("Bad frequency\n");
+ return -1;
+ }
+
+ test_opt->period_ns =
+ ODP_TIME_SEC_IN_NS / odp_fract_u64_to_dbl(&test_opt->freq);
+
+ if (test_opt->offset_ns == UINT64_MAX)
+ test_opt->offset_ns = 0;
+ } else {
+ if (test_opt->res_ns < 0) {
+ printf("Resolution (res_ns) must be >= 0 with single shot timer\n");
+ return -1;
+ }
+
+ if (test_opt->offset_ns == UINT64_MAX)
+ test_opt->offset_ns = 300 * ODP_TIME_MSEC_IN_NS;
+ }
+
+ test_opt->warmup_timers = test_opt->num_warmup * test_opt->burst;
+ test_opt->tot_timers =
+ test_opt->warmup_timers + test_opt->num * test_opt->burst;
+
+ if (test_opt->mode == MODE_ONESHOT)
+ test_opt->alloc_timers = test_opt->tot_timers;
+ else
+ test_opt->alloc_timers = test_opt->burst;
+
+ return ret;
+}
+
+static int single_shot_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
+ odp_timer_capability_t *timer_capa)
+{
+ uint64_t res_ns, res_hz;
+ uint64_t max_res_ns, max_res_hz;
+ uint64_t period_ns = test_global->opt.period_ns;
+ uint64_t num_tmo = test_global->opt.num + test_global->opt.num_warmup;
+ uint64_t offset_ns = test_global->opt.offset_ns;
+ enum mode_e mode = test_global->opt.mode;
+
+ max_res_ns = timer_capa->max_res.res_ns;
+ max_res_hz = timer_capa->max_res.res_hz;
+
+ /* Default resolution */
+ if (test_global->opt.res_ns == 0 && test_global->opt.res_hz == 0) {
+ res_ns = test_global->opt.period_ns / 10;
+ res_hz = 0;
+ } else if (test_global->opt.res_ns) {
+ res_ns = test_global->opt.res_ns;
+ res_hz = 0;
+ } else {
+ res_ns = 0;
+ res_hz = test_global->opt.res_hz;
+ }
+
+ if (res_ns && res_ns < max_res_ns) {
+ printf("Resolution %" PRIu64 " nsec too high. Highest resolution %" PRIu64 " nsec. "
+ "Default resolution is period / 10.\n\n",
+ res_ns, max_res_ns);
+ return -1;
+ }
+
+ if (res_hz && res_hz > max_res_hz) {
+ printf("Resolution %" PRIu64 " hz too high. Highest resolution %" PRIu64 " hz. "
+ "Default resolution is period / 10.\n\n",
+ res_hz, max_res_hz);
+ return -1;
+ }
+
+ if (res_ns)
+ timer_param->res_ns = res_ns;
+ else
+ timer_param->res_hz = res_hz;
+
+ if (mode == MODE_ONESHOT) {
+ timer_param->min_tmo = offset_ns / 2;
+ timer_param->max_tmo = offset_ns + ((num_tmo + 1) * period_ns);
+ } else {
+ timer_param->min_tmo = period_ns / 10;
+ timer_param->max_tmo = offset_ns + (2 * period_ns);
+ }
+
+ if (test_global->opt.max_tmo_ns) {
+ if (test_global->opt.max_tmo_ns < timer_param->max_tmo) {
+ printf("Max tmo is too small. Must be at least %" PRIu64 " nsec.\n",
+ timer_param->max_tmo);
+ return -1;
+ }
+
+ timer_param->max_tmo = test_global->opt.max_tmo_ns;
+ }
+
+ printf(" period: %" PRIu64 " nsec\n", period_ns);
+ printf(" max res nsec: %" PRIu64 "\n", max_res_ns);
+ printf(" max res hertz: %" PRIu64 "\n", max_res_hz);
+
+ test_global->period_dbl = period_ns;
+
+ return 0;
+}
+
+static int periodic_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
+ odp_timer_capability_t *timer_capa)
+{
+ int ret;
+ uint64_t res_ns;
+ odp_timer_periodic_capability_t capa;
+ double freq_dbl, min_freq, max_freq;
+ double opt_freq = odp_fract_u64_to_dbl(&test_global->opt.freq);
+ odp_fract_u64_t freq = test_global->opt.freq;
+ uint64_t res_hz = test_global->opt.res_hz;
+ uint64_t max_multiplier = test_global->opt.max_multiplier;
+ uint64_t multiplier = test_global->opt.multiplier;
+
+ if (res_hz) {
+ res_ns = ODP_TIME_SEC_IN_NS / res_hz;
+ } else {
+ res_ns = test_global->opt.res_ns;
+
+ /* Default resolution */
+ if (res_ns == 0)
+ res_ns = ODP_TIME_SEC_IN_NS / (10 * multiplier * opt_freq);
+ }
+
+ if (res_ns == 0) {
+ printf("Too high resolution\n");
+ return -1;
+ }
+
+ /* Resolution from capa */
+ if (test_global->opt.res_ns < 0)
+ res_ns = 0;
+
+ min_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.min_base_freq_hz);
+ max_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.max_base_freq_hz);
+
+ capa.base_freq_hz = freq;
+ capa.max_multiplier = max_multiplier;
+ capa.res_ns = res_ns;
+
+ ret = odp_timer_periodic_capability(test_global->opt.clk_src, &capa);
+
+ if (ret < 0) {
+ printf("Requested periodic timer capabilities are not supported.\n"
+ "Capabilities: min base freq %g Hz, max base freq %g Hz, "
+ "max res %" PRIu64 " Hz\n", min_freq, max_freq, timer_capa->max_res.res_hz);
+ return -1;
+ }
+
+ if (ret == 0) {
+ printf("Requested base frequency is not met. Using %.2f Hz instead of %.2f Hz.\n",
+ odp_fract_u64_to_dbl(&capa.base_freq_hz), opt_freq);
+
+ freq = capa.base_freq_hz;
+ }
+
+ if (res_ns == 0)
+ res_ns = capa.res_ns;
+
+ freq_dbl = odp_fract_u64_to_dbl(&freq);
+ test_global->base_freq = freq;
+ test_global->period_dbl = ODP_TIME_SEC_IN_NS / (multiplier * freq_dbl);
+
+ /* Min/max tmo are ignored, leave those to default values */
+ timer_param->timer_type = ODP_TIMER_TYPE_PERIODIC;
+ timer_param->periodic.base_freq_hz = freq;
+ timer_param->periodic.max_multiplier = max_multiplier;
+
+ if (res_hz)
+ timer_param->res_hz = res_hz;
+ else
+ timer_param->res_ns = res_ns;
+
+ printf(" min freq capa: %.2f hz\n", min_freq);
+ printf(" max freq capa: %.2f hz\n", max_freq);
+ printf(" freq option: %.2f hz\n", opt_freq);
+ printf(" freq: %.2f hz\n", freq_dbl);
+ printf(" freq integer: %" PRIu64 "\n", freq.integer);
+ printf(" freq numer: %" PRIu64 "\n", freq.numer);
+ printf(" freq denom: %" PRIu64 "\n", freq.denom);
+ printf(" max_multiplier: %" PRIu64 "\n", max_multiplier);
+ printf(" multiplier: %" PRIu64 "\n", multiplier);
+ printf(" timer freq: %.2f hz\n", multiplier * freq_dbl);
+ printf(" timer period: %.2f nsec\n", test_global->period_dbl);
+ printf(" resolution capa: %" PRIu64 " nsec\n", capa.res_ns);
+
+ return 0;
+}
+
+static int create_timers(test_global_t *test_global)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_timer_pool_t timer_pool;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_capability_t timer_capa;
+ odp_timer_t timer;
+ odp_queue_t *queue;
+ odp_schedule_group_t *group;
+ odp_queue_param_t queue_param;
+ uint64_t offset_ns;
+ uint32_t max_timers;
+ odp_event_t event;
+ odp_timeout_t timeout;
+ uint64_t i, num_tmo, num_warmup, burst, burst_gap;
+ uint64_t tot_timers, alloc_timers;
+ enum mode_e mode;
+ odp_timer_clk_src_t clk_src;
+ int ret;
+
+ mode = test_global->opt.mode;
+ alloc_timers = test_global->opt.alloc_timers;
+ tot_timers = test_global->opt.tot_timers;
+ num_warmup = test_global->opt.num_warmup;
+ num_tmo = num_warmup + test_global->opt.num;
+ burst = test_global->opt.burst;
+ burst_gap = test_global->opt.burst_gap;
+ offset_ns = test_global->opt.offset_ns;
+ queue = test_global->queue;
+ group = test_global->group;
+
+ /* Always init globals for destroy calls */
+ test_global->timer_pool = ODP_TIMER_POOL_INVALID;
+ test_global->timeout_pool = ODP_POOL_INVALID;
+
+ for (i = 0; i < alloc_timers; i++) {
+ test_global->timer_ctx[i].timer = ODP_TIMER_INVALID;
+ test_global->timer_ctx[i].event = ODP_EVENT_INVALID;
+ }
+
+ if (test_global->opt.groups) {
+ /* Create groups */
+
+ odp_thrmask_t zero;
+
+ odp_thrmask_zero(&zero);
+
+ for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
+ group[i] = odp_schedule_group_create(NULL, &zero);
+
+ if (group[i] == ODP_SCHED_GROUP_INVALID) {
+ printf("Group create failed.\n");
+ return -1;
+ }
+ }
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.sync = test_global->opt.queue_type;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
+ if (test_global->opt.groups)
+ queue_param.sched.group = group[i % test_global->opt.cpu_count];
+
+ queue[i] = odp_queue_create(NULL, &queue_param);
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ printf("Queue create failed.\n");
+ return -1;
+ }
+ }
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = alloc_timers;
+
+ pool = odp_pool_create("timeout pool", &pool_param);
+
+ if (pool == ODP_POOL_INVALID) {
+ printf("Timeout pool create failed.\n");
+ return -1;
+ }
+
+ test_global->timeout_pool = pool;
+ clk_src = test_global->opt.clk_src;
+
+ if (odp_timer_capability(clk_src, &timer_capa)) {
+ printf("Timer capa failed\n");
+ return -1;
+ }
+
+ max_timers = timer_capa.max_timers;
+
+ if (mode == MODE_PERIODIC) {
+ if (timer_capa.periodic.max_pools < 1) {
+ printf("Error: Periodic timers not supported.\n");
+ return -1;
+ }
+ max_timers = timer_capa.periodic.max_timers;
+ }
+
+ if (max_timers && test_global->opt.alloc_timers > max_timers) {
+ printf("Error: Too many timers: %" PRIu64 ".\n"
+ " Max timers: %u\n",
+ test_global->opt.alloc_timers, max_timers);
+ return -1;
+ }
+
+ printf("\nTest parameters:\n");
+ printf(" clock source: %i\n", clk_src);
+ printf(" max timers capa: %" PRIu32 "\n", max_timers);
+ printf(" mode: %i\n", mode);
+ printf(" queue type: %i\n", test_global->opt.queue_type);
+ printf(" num queue: %i\n", test_global->opt.num_queue);
+ printf(" sched groups: %s\n", test_global->opt.groups ? "yes" : "no");
+
+ odp_timer_pool_param_init(&timer_param);
+
+ if (mode == MODE_PERIODIC)
+ ret = periodic_params(test_global, &timer_param, &timer_capa);
+ else
+ ret = single_shot_params(test_global, &timer_param, &timer_capa);
+
+ if (ret)
+ return ret;
+
+ if (timer_param.res_hz) {
+ test_global->res_ns = 1000000000.0 / timer_param.res_hz;
+ printf(" resolution: %" PRIu64 " Hz\n", timer_param.res_hz);
+ } else {
+ test_global->res_ns = timer_param.res_ns;
+ printf(" resolution: %" PRIu64 " nsec\n", timer_param.res_ns);
+ }
+
+ timer_param.num_timers = alloc_timers;
+ timer_param.clk_src = clk_src;
+
+ printf(" restart retries: %i\n", test_global->opt.early_retry);
+ if (test_global->opt.output)
+ printf(" log file: %s\n", test_global->opt.filename);
+ printf(" start offset: %" PRIu64 " nsec\n", offset_ns);
+ printf(" min timeout: %" PRIu64 " nsec\n", timer_param.min_tmo);
+ printf(" max timeout: %" PRIu64 " nsec\n", timer_param.max_tmo);
+ printf(" num timeout: %" PRIu64 "\n", num_tmo);
+ printf(" num warmup: %" PRIu64 "\n", num_warmup);
+ printf(" burst size: %" PRIu64 "\n", burst);
+ printf(" burst gap: %" PRIu64 "\n", burst_gap);
+ printf(" total timers: %" PRIu64 "\n", tot_timers);
+ printf(" warmup timers: %" PRIu64 "\n", test_global->opt.warmup_timers);
+ printf(" alloc timers: %" PRIu64 "\n", alloc_timers);
+ printf(" warmup time: %.2f sec\n",
+ (offset_ns + (num_warmup * test_global->period_dbl)) / 1000000000.0);
+ printf(" test run time: %.2f sec\n\n",
+ (offset_ns + (num_tmo * test_global->period_dbl)) / 1000000000.0);
+
+ timer_pool = odp_timer_pool_create("timer_accuracy", &timer_param);
+
+ if (timer_pool == ODP_TIMER_POOL_INVALID) {
+ printf("Timer pool create failed\n");
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
+ ODPH_ERR("Timer pool start failed\n");
+ return -1;
+ }
+
+ odp_timer_pool_print(timer_pool);
+
+ /* Spend some time so that current tick would not be zero */
+ odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS);
+
+ test_global->timer_pool = timer_pool;
+
+ for (i = 0; i < alloc_timers; i++) {
+ timer_ctx_t *ctx = &test_global->timer_ctx[i];
+
+ timer = odp_timer_alloc(timer_pool, queue[i % test_global->opt.num_queue], ctx);
+
+ if (timer == ODP_TIMER_INVALID) {
+ printf("Timer alloc failed.\n");
+ return -1;
+ }
+
+ ctx->timer = timer;
+
+ timeout = odp_timeout_alloc(pool);
+ if (timeout == ODP_TIMEOUT_INVALID) {
+ printf("Timeout alloc failed\n");
+ return -1;
+ }
+
+ ctx->event = odp_timeout_to_event(timeout);
+ }
+
+ /* Run scheduler few times to ensure that (software) timer is active */
+ for (i = 0; i < 1000; i++) {
+ event = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (event != ODP_EVENT_INVALID) {
+ printf("Spurious event received\n");
+ odp_event_free(event);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int start_timers(test_global_t *test_global)
+{
+ odp_timer_pool_t timer_pool;
+ uint64_t start_tick;
+ uint64_t period_ns, start_ns, nsec, offset_ns;
+ odp_time_t time;
+ uint64_t i, j, idx, num_tmo, num_warmup, burst, burst_gap;
+ enum mode_e mode;
+
+ mode = test_global->opt.mode;
+ num_warmup = test_global->opt.num_warmup;
+ num_tmo = num_warmup + test_global->opt.num;
+ burst = test_global->opt.burst;
+ burst_gap = test_global->opt.burst_gap;
+ period_ns = test_global->opt.period_ns;
+ offset_ns = test_global->opt.offset_ns;
+ timer_pool = test_global->timer_pool;
+ idx = 0;
+
+ /* Record test start time and tick. Memory barriers forbid compiler and out-of-order
+ * CPU to move samples apart. */
+ odp_mb_full();
+ start_tick = odp_timer_current_tick(timer_pool);
+ time = odp_time_global();
+ odp_mb_full();
+
+ start_ns = odp_time_to_ns(time);
+ test_global->start_tick = start_tick;
+ test_global->start_ns = start_ns;
+ test_global->period_tick = odp_timer_ns_to_tick(timer_pool, period_ns);
+
+ /* When mode is not one-shot, set only one burst of timers initially */
+ if (mode != MODE_ONESHOT)
+ num_tmo = 1;
+
+ for (i = 0; i < num_tmo; i++) {
+ odp_timer_retval_t retval;
+
+ for (j = 0; j < burst; j++) {
+ timer_ctx_t *ctx = &test_global->timer_ctx[idx];
+ odp_timer_start_t start_param;
+
+ if (mode == MODE_PERIODIC) {
+ odp_timer_periodic_start_t periodic_start;
+
+ nsec = offset_ns + (j * burst_gap);
+
+ /* By default, timer starts one period after current time. Round
+ * floating point to closest integer number. */
+ ctx->nsec = start_ns + test_global->period_dbl + 0.5;
+ if (nsec)
+ ctx->nsec = start_ns + nsec;
+
+ ctx->count = 0;
+ ctx->first_period = start_tick +
+ odp_timer_ns_to_tick(timer_pool,
+ test_global->period_dbl + 0.5);
+ periodic_start.freq_multiplier = test_global->opt.multiplier;
+ periodic_start.first_tick = 0;
+ if (nsec)
+ periodic_start.first_tick =
+ start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
+ periodic_start.tmo_ev = ctx->event;
+ retval = odp_timer_periodic_start(ctx->timer, &periodic_start);
+ } else {
+ nsec = offset_ns + (i * period_ns) + (j * burst_gap);
+ ctx->nsec = start_ns + nsec;
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick =
+ start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
+ start_param.tmo_ev = ctx->event;
+ retval = odp_timer_start(ctx->timer, &start_param);
+ }
+
+ if (retval != ODP_TIMER_SUCCESS) {
+ printf("Timer[%" PRIu64 "] set failed: %i\n", idx, retval);
+ return -1;
+ }
+
+ idx++;
+ }
+ }
+
+ printf("\nStarting timers took %" PRIu64 " nsec\n", odp_time_global_ns() - start_ns);
+
+ return 0;
+}
+
+static int destroy_timers(test_global_t *test_global)
+{
+ uint64_t i, alloc_timers;
+ odp_timer_t timer;
+ int ret = 0;
+
+ alloc_timers = test_global->opt.alloc_timers;
+
+ for (i = 0; i < alloc_timers; i++) {
+ timer = test_global->timer_ctx[i].timer;
+
+ if (timer == ODP_TIMER_INVALID)
+ break;
+
+ if (odp_timer_free(timer)) {
+ printf("Timer free failed: %" PRIu64 "\n", i);
+ ret = -1;
+ }
+ }
+
+ if (test_global->timer_pool != ODP_TIMER_POOL_INVALID)
+ odp_timer_pool_destroy(test_global->timer_pool);
+
+ if (test_global->timeout_pool != ODP_POOL_INVALID) {
+ if (odp_pool_destroy(test_global->timeout_pool)) {
+ printf("Pool destroy failed.\n");
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
+ if (odp_queue_destroy(test_global->queue[i])) {
+ printf("Queue destroy failed.\n");
+ ret = -1;
+ }
+ }
+
+ if (test_global->opt.groups) {
+ for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
+ if (odp_schedule_group_destroy(test_global->group[i])) {
+ printf("Group destroy failed.\n");
+ ret = -1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void print_nsec_error(const char *str, int64_t nsec, double res_ns,
+ int tid, int idx)
+{
+ printf(" %s: %12" PRIi64 " / %.3fx resolution",
+ str, nsec, (double)nsec / res_ns);
+ if (tid >= 0)
+ printf(", thread %d", tid);
+ if (idx >= 0)
+ printf(", event %d", idx);
+ printf("\n");
+}
+
+static void print_stat(test_global_t *test_global)
+{
+ test_stat_t test_stat;
+ test_stat_t *stat = &test_stat;
+ uint64_t tot_timers;
+ test_stat_t *s = test_global->stat;
+ test_log_t *log = test_global->log;
+ double res_ns = test_global->res_ns;
+ uint64_t ave_after = 0;
+ uint64_t ave_before = 0;
+ uint64_t nsec_before_min_tid = 0;
+ uint64_t nsec_before_max_tid = 0;
+ uint64_t nsec_after_min_tid = 0;
+ uint64_t nsec_after_max_tid = 0;
+
+ memset(stat, 0, sizeof(*stat));
+ stat->nsec_before_min = UINT64_MAX;
+ stat->nsec_after_min = UINT64_MAX;
+
+ for (int i = 1; i < test_global->opt.cpu_count + 1; i++) {
+ stat->nsec_before_sum += s[i].nsec_before_sum;
+ stat->nsec_after_sum += s[i].nsec_after_sum;
+ stat->num_before += s[i].num_before;
+ stat->num_exact += s[i].num_exact;
+ stat->num_after += s[i].num_after;
+ stat->num_too_near += s[i].num_too_near;
+
+ if (s[i].nsec_before_min < stat->nsec_before_min) {
+ stat->nsec_before_min = s[i].nsec_before_min;
+ stat->nsec_before_min_idx = s[i].nsec_before_min_idx;
+ nsec_before_min_tid = i;
+ }
+
+ if (s[i].nsec_after_min < stat->nsec_after_min) {
+ stat->nsec_after_min = s[i].nsec_after_min;
+ stat->nsec_after_min_idx = s[i].nsec_after_min_idx;
+ nsec_after_min_tid = i;
+ }
+
+ if (s[i].nsec_before_max > stat->nsec_before_max) {
+ stat->nsec_before_max = s[i].nsec_before_max;
+ stat->nsec_before_max_idx = s[i].nsec_before_max_idx;
+ nsec_before_max_tid = i;
+ }
+
+ if (s[i].nsec_after_max > stat->nsec_after_max) {
+ stat->nsec_after_max = s[i].nsec_after_max;
+ stat->nsec_after_max_idx = s[i].nsec_after_max_idx;
+ nsec_after_max_tid = i;
+ }
+ }
+
+ if (stat->num_after)
+ ave_after = stat->nsec_after_sum / stat->num_after;
+ else
+ stat->nsec_after_min = 0;
+
+ if (stat->num_before)
+ ave_before = stat->nsec_before_sum / stat->num_before;
+ else
+ stat->nsec_before_min = 0;
+
+ tot_timers = stat->num_before + stat->num_after + stat->num_exact;
+
+ if (log) {
+ FILE *file = test_global->file;
+
+ fprintf(file, " Timer thread tmo(ns) diff(ns)\n");
+
+ for (uint64_t i = 0; i < tot_timers; i++) {
+ fprintf(file, "%8" PRIu64 " %7u %12" PRIu64 " %10"
+ PRIi64 "\n", i, log[i].tid, log[i].tmo_ns, log[i].diff_ns);
+ }
+
+ fprintf(file, "\n");
+ }
+
+ printf("\nTest results:\n");
+ printf(" num after: %12" PRIu64 " / %.2f%%\n",
+ stat->num_after, 100.0 * stat->num_after / tot_timers);
+ printf(" num before: %12" PRIu64 " / %.2f%%\n",
+ stat->num_before, 100.0 * stat->num_before / tot_timers);
+ printf(" num exact: %12" PRIu64 " / %.2f%%\n",
+ stat->num_exact, 100.0 * stat->num_exact / tot_timers);
+ printf(" num retry: %12" PRIu64 " / %.2f%%\n",
+ stat->num_too_near, 100.0 * stat->num_too_near / tot_timers);
+ printf(" error after (nsec):\n");
+ print_nsec_error("min", stat->nsec_after_min, res_ns, nsec_after_min_tid,
+ stat->nsec_after_min_idx);
+ print_nsec_error("max", stat->nsec_after_max, res_ns, nsec_after_max_tid,
+ stat->nsec_after_max_idx);
+ print_nsec_error("ave", ave_after, res_ns, -1, -1);
+ printf(" error before (nsec):\n");
+ print_nsec_error("min", stat->nsec_before_min, res_ns, nsec_before_min_tid,
+ stat->nsec_before_min_idx);
+ print_nsec_error("max", stat->nsec_before_max, res_ns, nsec_before_max_tid,
+ stat->nsec_before_max_idx);
+ print_nsec_error("ave", ave_before, res_ns, -1, -1);
+
+ if (test_global->opt.mode == MODE_PERIODIC && !test_global->opt.offset_ns) {
+ int idx = 0;
+ int64_t max = 0;
+
+ for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
+ timer_ctx_t *t = &test_global->timer_ctx[i];
+ int64_t v = t->first_tmo_diff;
+
+ if (ODPH_ABS(v) > ODPH_ABS(max)) {
+ max = v;
+ idx = i;
+ }
+ }
+
+ printf(" first timeout difference to one period, based on %s (nsec):\n",
+ test_global->timer_ctx[idx].tmo_tick ? "timeout tick" : "time");
+ print_nsec_error("max", max, res_ns, -1, -1);
+ }
+
+ int64_t max = 0;
+
+ for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
+ timer_ctx_t *t = &test_global->timer_ctx[i];
+ int64_t v = t->nsec_final;
+
+ if (ODPH_ABS(v) > ODPH_ABS(max))
+ max = v;
+ }
+
+ printf(" final timeout error (nsec):\n");
+ print_nsec_error("max", max, res_ns, -1, -1);
+
+ printf("\n");
+}
+
+static void cancel_periodic_timers(test_global_t *test_global)
+{
+ uint64_t i, alloc_timers;
+ odp_timer_t timer;
+
+ alloc_timers = test_global->opt.alloc_timers;
+
+ for (i = 0; i < alloc_timers; i++) {
+ timer = test_global->timer_ctx[i].timer;
+
+ if (timer == ODP_TIMER_INVALID)
+ break;
+
+ if (odp_timer_periodic_cancel(timer))
+ printf("Failed to cancel periodic timer.\n");
+ }
+}
+
+static int run_test(void *arg)
+{
+ test_global_t *test_global = (test_global_t *)arg;
+ odp_event_t ev;
+ odp_time_t time;
+ uint64_t time_ns, diff_ns;
+ odp_timeout_t tmo;
+ uint64_t tmo_ns;
+ timer_ctx_t *ctx;
+ odp_thrmask_t mask;
+ uint64_t wait = odp_schedule_wait_time(10 * ODP_TIME_MSEC_IN_NS);
+ odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
+ test_log_t *log = test_global->log;
+ enum mode_e mode = test_global->opt.mode;
+ uint64_t tot_timers = test_global->opt.tot_timers;
+ double period_dbl = test_global->period_dbl;
+ odp_timer_pool_t tp = test_global->timer_pool;
+ int tid = odp_thread_id();
+
+ if (tid > test_global->opt.cpu_count) {
+ printf("Error: tid %d is larger than cpu_count %d.\n", tid,
+ test_global->opt.cpu_count);
+ return 0;
+ }
+
+ test_stat_t *stat = &test_global->stat[tid];
+
+ memset(stat, 0, sizeof(*stat));
+ stat->nsec_before_min = UINT64_MAX;
+ stat->nsec_after_min = UINT64_MAX;
+
+ if (test_global->opt.groups) {
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, tid);
+ group = test_global->group[tid - 1];
+
+ if (odp_schedule_group_join(group, &mask)) {
+ printf("odp_schedule_group_join() failed\n");
+ return 0;
+ }
+ }
+
+ odp_barrier_wait(&test_global->barrier);
+
+ while (1) {
+ ev = odp_schedule(NULL, wait);
+ time = odp_time_global_strict();
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (mode == MODE_PERIODIC) {
+ if (odp_atomic_load_u64(&test_global->last_events) >=
+ test_global->opt.alloc_timers)
+ break;
+
+ } else if (odp_atomic_load_u64(&test_global->events) >= tot_timers) {
+ break;
+ }
+
+ continue;
+ }
+
+ time_ns = odp_time_to_ns(time);
+ tmo = odp_timeout_from_event(ev);
+ ctx = odp_timeout_user_ptr(tmo);
+ tmo_ns = ctx->nsec;
+
+ if (mode == MODE_PERIODIC) {
+ if (!ctx->count && !test_global->opt.offset_ns) {
+ /*
+ * If first_tick is zero, the API allows the implementation to
+ * place the timer where it can, so we have to adjust our
+ * expectation of the timeout time.
+ */
+
+ uint64_t tmo_tick = odp_timeout_tick(tmo);
+
+ if (tmo_tick) {
+ /*
+ * Adjust by the difference between one period after start
+ * time and the timeout tick.
+ */
+ ctx->tmo_tick = 1;
+ ctx->first_tmo_diff =
+ (int64_t)odp_timer_tick_to_ns(tp, tmo_tick) -
+ (int64_t)odp_timer_tick_to_ns(tp, ctx->first_period);
+ tmo_ns += ctx->first_tmo_diff;
+ } else {
+ /*
+ * Timeout tick is not provided, so the best we can do is
+ * to just take the current time as a baseline.
+ */
+ ctx->first_tmo_diff = (int64_t)time_ns - (int64_t)tmo_ns;
+ tmo_ns = ctx->nsec = time_ns;
+ }
+
+ ctx->nsec = tmo_ns;
+ }
+
+ /* round to closest integer number */
+ tmo_ns += ctx->count * period_dbl + 0.5;
+ ctx->count++;
+ }
+
+ uint64_t events = odp_atomic_fetch_inc_u64(&test_global->events);
+
+ if (events >= test_global->opt.warmup_timers && events < tot_timers) {
+ uint64_t i = events - test_global->opt.warmup_timers;
+
+ ctx->nsec_final = (int64_t)time_ns - (int64_t)tmo_ns;
+
+ if (log) {
+ log[i].tmo_ns = tmo_ns;
+ log[i].tid = tid;
+ }
+
+ if (time_ns > tmo_ns) {
+ diff_ns = time_ns - tmo_ns;
+ stat->num_after++;
+ stat->nsec_after_sum += diff_ns;
+ if (diff_ns < stat->nsec_after_min) {
+ stat->nsec_after_min = diff_ns;
+ stat->nsec_after_min_idx = i;
+ }
+ if (diff_ns > stat->nsec_after_max) {
+ stat->nsec_after_max = diff_ns;
+ stat->nsec_after_max_idx = i;
+ }
+ if (log)
+ log[i].diff_ns = diff_ns;
+
+ } else if (time_ns < tmo_ns) {
+ diff_ns = tmo_ns - time_ns;
+ stat->num_before++;
+ stat->nsec_before_sum += diff_ns;
+ if (diff_ns < stat->nsec_before_min) {
+ stat->nsec_before_min = diff_ns;
+ stat->nsec_before_min_idx = i;
+ }
+ if (diff_ns > stat->nsec_before_max) {
+ stat->nsec_before_max = diff_ns;
+ stat->nsec_before_max_idx = i;
+ }
+ if (log)
+ log[i].diff_ns = -diff_ns;
+ } else {
+ stat->num_exact++;
+ }
+ }
+
+ if ((mode == MODE_RESTART_ABS || mode == MODE_RESTART_REL) &&
+ events < tot_timers - 1) {
+ /* Reset timer for next period */
+ odp_timer_t tim;
+ uint64_t nsec, tick;
+ odp_timer_retval_t ret;
+ unsigned int j;
+ unsigned int retries = test_global->opt.early_retry;
+ uint64_t start_ns = test_global->start_ns;
+ uint64_t period_ns = test_global->opt.period_ns;
+ odp_timer_start_t start_param;
+
+ tim = ctx->timer;
+
+ /* Depending on the option, retry when expiration
+ * time is too early */
+ for (j = 0; j < retries + 1; j++) {
+ if (mode == MODE_RESTART_ABS) {
+ /* Absolute time */
+ ctx->nsec += period_ns;
+ nsec = ctx->nsec - start_ns;
+ tick = test_global->start_tick +
+ odp_timer_ns_to_tick(tp, nsec);
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ } else {
+ /* Relative time */
+ tick = test_global->period_tick;
+ time = odp_time_local();
+ time_ns = odp_time_to_ns(time);
+ ctx->nsec = time_ns + period_ns;
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ }
+
+ start_param.tmo_ev = ev;
+ start_param.tick = tick;
+
+ ret = odp_timer_start(tim, &start_param);
+ if (ret == ODP_TIMER_TOO_NEAR) {
+ if (events >= test_global->opt.warmup_timers)
+ stat->num_too_near++;
+ } else {
+ break;
+ }
+ }
+
+ if (ret != ODP_TIMER_SUCCESS) {
+ printf("Timer set failed: %i. Timeout nsec "
+ "%" PRIu64 "\n", ret, ctx->nsec);
+ return 0;
+ }
+ } else if (mode == MODE_PERIODIC) {
+ int ret = odp_timer_periodic_ack(ctx->timer, ev);
+
+ if (ret < 0)
+ printf("Failed to ack a periodic timer.\n");
+
+ if (ret == 2)
+ odp_atomic_inc_u64(&test_global->last_events);
+
+ if (ret == 2 || ret < 0)
+ odp_event_free(ev);
+ } else {
+ odp_event_free(ev);
+ }
+ }
+
+ if (test_global->opt.groups) {
+ if (odp_schedule_group_leave(group, &mask))
+ printf("odp_schedule_group_leave() failed\n");
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ odp_instance_t instance;
+ odp_init_t init;
+ test_opt_t test_opt;
+ test_global_t *test_global;
+ odph_helper_options_t helper_options;
+ odp_init_t *init_ptr = NULL;
+ int ret = 0;
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (parse_options(argc, argv, &test_opt))
+ return -1;
+
+ /* List features not to be used (may optimize performance) */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ if (test_opt.init)
+ init_ptr = &init;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, init_ptr, NULL)) {
+ printf("Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ printf("Local init failed.\n");
+ return -1;
+ }
+
+ odp_sys_info_print();
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
+ odp_shm_t shm = ODP_SHM_INVALID, shm_ctx = ODP_SHM_INVALID, shm_log = ODP_SHM_INVALID;
+ uint64_t size = sizeof(test_global_t);
+
+ shm = odp_shm_reserve("timer_accuracy", size,
+ ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
+
+ if (shm == ODP_SHM_INVALID) {
+ printf("Shm alloc failed.\n");
+ return -1;
+ }
+
+ test_global = odp_shm_addr(shm);
+ memset(test_global, 0, size);
+ memcpy(&test_global->opt, &test_opt, sizeof(test_opt_t));
+
+ size = test_global->opt.alloc_timers * sizeof(timer_ctx_t);
+ shm_ctx = odp_shm_reserve("timer_accuracy_ctx", size,
+ ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
+
+ if (shm_ctx == ODP_SHM_INVALID) {
+ printf("Timer context alloc failed.\n");
+ ret = -1;
+ goto quit;
+ }
+
+ test_global->timer_ctx = odp_shm_addr(shm_ctx);
+ memset(test_global->timer_ctx, 0, size);
+
+ if (test_global->opt.output) {
+ test_global->file = fopen(test_global->opt.filename, "w");
+ if (test_global->file == NULL) {
+ printf("Failed to open output file %s: %s\n",
+ test_global->opt.filename, strerror(errno));
+ ret = -1;
+ goto quit;
+ }
+
+ size = (test_global->opt.tot_timers - test_global->opt.warmup_timers) *
+ sizeof(test_log_t);
+ shm_log = odp_shm_reserve("timer_accuracy_log", size, sizeof(test_log_t),
+ ODP_SHM_SINGLE_VA);
+
+ if (shm_log == ODP_SHM_INVALID) {
+ printf("Test log alloc failed.\n");
+ ret = -1;
+ goto quit;
+ }
+
+ test_global->log = odp_shm_addr(shm_log);
+ memset(test_global->log, 0, size);
+ }
+
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ int num_workers;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ num_workers = MAX_WORKERS;
+ if (test_global->opt.cpu_count && test_global->opt.cpu_count < MAX_WORKERS)
+ num_workers = test_global->opt.cpu_count;
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ test_global->opt.cpu_count = num_workers;
+ odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ ret = create_timers(test_global);
+ if (ret)
+ goto quit;
+
+ odp_barrier_init(&test_global->barrier, num_workers + 1);
+ odp_atomic_init_u64(&test_global->events, 0);
+ odp_atomic_init_u64(&test_global->last_events, 0);
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_test;
+ thr_param.arg = (void *)test_global;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+ odp_barrier_wait(&test_global->barrier);
+
+ ret = start_timers(test_global);
+ if (ret)
+ goto quit;
+
+ if (test_global->opt.mode == MODE_PERIODIC) {
+ while (odp_atomic_load_u64(&test_global->events) < test_global->opt.tot_timers)
+ odp_time_wait_ns(10 * ODP_TIME_MSEC_IN_NS);
+
+ cancel_periodic_timers(test_global);
+ }
+
+ odph_thread_join(thread_tbl, num_workers);
+ print_stat(test_global);
+
+quit:
+ if (test_global->file)
+ fclose(test_global->file);
+
+ if (destroy_timers(test_global))
+ ret = -1;
+
+ if (shm_log != ODP_SHM_INVALID && odp_shm_free(shm_log))
+ ret = -1;
+
+ if (shm_ctx != ODP_SHM_INVALID && odp_shm_free(shm_ctx))
+ ret = -1;
+
+ if (odp_shm_free(shm))
+ ret = -1;
+
+ if (odp_term_local()) {
+ printf("Term local failed.\n");
+ ret = -1;
+ }
+
+ if (odp_term_global(instance)) {
+ printf("Term global failed.\n");
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/test/performance/odp_timer_accuracy_run.sh b/test/performance/odp_timer_accuracy_run.sh
new file mode 100755
index 000000000..84ad2a573
--- /dev/null
+++ b/test/performance/odp_timer_accuracy_run.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2022-2024 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+$TEST_DIR/odp_timer_accuracy${EXEEXT} -p 100000000 -n 10
+
+RET_VAL=$?
+if [ $RET_VAL -ne 0 ] ; then
+ echo odp_timer_accuracy FAILED
+ exit $RET_VAL
+fi
+
+exit 0
diff --git a/test/performance/odp_timer_perf.c b/test/performance/odp_timer_perf.c
new file mode 100644
index 000000000..6da5f2296
--- /dev/null
+++ b/test/performance/odp_timer_perf.c
@@ -0,0 +1,1400 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+/**
+ * @example odp_timer_perf.c
+ *
+ * Performance test application for timer APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define MODE_SCHED_OVERH 0
+#define MODE_START_CANCEL 1
+#define MODE_START_EXPIRE 2
+#define MAX_TIMER_POOLS 32
+#define MAX_TIMERS 10000
+#define START_NS (100 * ODP_TIME_MSEC_IN_NS)
+
+typedef struct test_options_t {
+ uint32_t num_cpu;
+ uint32_t num_tp;
+ uint32_t num_timer;
+ uint64_t res_ns;
+ uint64_t period_ns;
+ int shared;
+ int mode;
+ uint64_t test_rounds;
+
+} test_options_t;
+
+typedef struct time_stat_t {
+ uint64_t num;
+ uint64_t sum_ns;
+ uint64_t max_ns;
+
+} time_stat_t;
+
+typedef struct test_stat_t {
+ uint64_t rounds;
+ uint64_t events;
+ uint64_t nsec;
+ uint64_t cycles_0;
+ uint64_t cycles_1;
+
+ uint64_t cancels;
+ uint64_t sets;
+
+ time_stat_t before;
+ time_stat_t after;
+
+} test_stat_t;
+
+typedef struct test_stat_sum_t {
+ uint64_t rounds;
+ uint64_t events;
+ uint64_t nsec;
+ uint64_t cycles_0;
+ uint64_t cycles_1;
+
+ uint64_t cancels;
+ uint64_t sets;
+
+ time_stat_t before;
+ time_stat_t after;
+
+ double time_ave;
+ uint32_t num;
+
+} test_stat_sum_t;
+
+typedef struct thread_arg_t {
+ void *global;
+ int worker_idx;
+
+} thread_arg_t;
+
+typedef struct timer_ctx_t {
+ uint64_t target_ns;
+ uint64_t target_tick;
+ uint32_t tp_idx;
+ uint32_t timer_idx;
+ int last;
+
+} timer_ctx_t;
+
+typedef struct timer_pool_t {
+ odp_timer_pool_t tp;
+ uint64_t start_tick;
+ uint64_t period_tick;
+
+} timer_pool_t;
+
+typedef struct test_global_t {
+ test_options_t test_options;
+ odp_atomic_u32_t exit_test;
+ odp_atomic_u32_t timers_started;
+ odp_barrier_t barrier;
+ odp_cpumask_t cpumask;
+ timer_pool_t timer_pool[MAX_TIMER_POOLS];
+ odp_pool_t pool[MAX_TIMER_POOLS];
+ odp_queue_t queue[MAX_TIMER_POOLS];
+ odp_timer_t timer[MAX_TIMER_POOLS][MAX_TIMERS];
+ timer_ctx_t timer_ctx[MAX_TIMER_POOLS][MAX_TIMERS];
+ odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
+ test_stat_t stat[ODP_THREAD_COUNT_MAX];
+ thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
+ test_stat_sum_t stat_sum;
+
+} test_global_t;
+
+test_global_t *test_global;
+
+static void print_usage(void)
+{
+ printf("\n"
+ "Timer performance test\n"
+ "\n"
+ "Usage: odp_timer_perf [options]\n"
+ "\n"
+ " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1\n"
+ " -n, --num_tp Number of timer pools. Default: 1\n"
+ " -t, --num_timer Number of timers per timer pool. Default: 10\n"
+ " -r, --res_ns Resolution in nsec. Default: 10000000\n"
+ " -p, --period_ns Timeout period in nsec. Default: 100000000\n"
+ " -s, --shared Shared vs private timer pool. Currently, private pools can be\n"
+ " tested only with single CPU. Default: 1\n"
+ " 0: Private timer pools\n"
+ " 1: Shared timer pools\n"
+ " -m, --mode Select test mode. Default: 0\n"
+ " 0: Measure odp_schedule() overhead when using timers\n"
+ " 1: Measure timer set + cancel performance\n"
+ " 2: Measure schedule and timer start overhead while continuously\n"
+ " restarting expiring timers\n"
+ " -R, --rounds Number of test rounds in timer set + cancel test.\n"
+ " Default: 100000\n"
+ " -h, --help This help\n"
+ "\n");
+}
+
+static int parse_options(int argc, char *argv[], test_options_t *test_options)
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ {"num_cpu", required_argument, NULL, 'c'},
+ {"num_tp ", required_argument, NULL, 'n'},
+ {"num_timer", required_argument, NULL, 't'},
+ {"res_ns", required_argument, NULL, 'r'},
+ {"period_ns", required_argument, NULL, 'p'},
+ {"shared", required_argument, NULL, 's'},
+ {"mode", required_argument, NULL, 'm'},
+ {"rounds", required_argument, NULL, 'R'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:n:t:r:p:s:m:R:h";
+
+ test_options->num_cpu = 1;
+ test_options->num_tp = 1;
+ test_options->num_timer = 10;
+ test_options->res_ns = 10 * ODP_TIME_MSEC_IN_NS;
+ test_options->period_ns = 100 * ODP_TIME_MSEC_IN_NS;
+ test_options->shared = 1;
+ test_options->mode = 0;
+ test_options->test_rounds = 100000;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ test_options->num_cpu = atoi(optarg);
+ break;
+ case 'n':
+ test_options->num_tp = atoi(optarg);
+ break;
+ case 't':
+ test_options->num_timer = atoi(optarg);
+ break;
+ case 'r':
+ test_options->res_ns = atoll(optarg);
+ break;
+ case 'p':
+ test_options->period_ns = atoll(optarg);
+ break;
+ case 's':
+ test_options->shared = atoi(optarg);
+ break;
+ case 'm':
+ test_options->mode = atoi(optarg);
+ break;
+ case 'R':
+ test_options->test_rounds = atoll(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (test_options->num_timer > MAX_TIMERS) {
+ ODPH_ERR("Too many timers. Max %u\n", MAX_TIMERS);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int set_num_cpu(test_global_t *global)
+{
+ int ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ int shared = test_options->shared;
+
+ /* One thread used for the main thread */
+ if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
+ ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
+ return -1;
+ }
+
+ ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
+
+ if (num_cpu && ret != num_cpu) {
+ ODPH_ERR("Too many workers. Max supported %i\n.", ret);
+ return -1;
+ }
+
+ if (shared == 0 && num_cpu != 1) {
+ ODPH_ERR("Private pool test supports only single CPU\n.");
+ return -1;
+ }
+
+ /* Zero: all available workers */
+ if (num_cpu == 0) {
+ num_cpu = ret;
+ test_options->num_cpu = num_cpu;
+ }
+
+ if (shared) /* Main thread + all workers */
+ odp_barrier_init(&global->barrier, num_cpu + 1);
+ else /* Only the main thread */
+ odp_barrier_init(&global->barrier, 1);
+
+ return 0;
+}
+
+static int create_timer_pools(test_global_t *global)
+{
+ odp_timer_capability_t timer_capa;
+ odp_timer_res_capability_t timer_res_capa;
+ odp_timer_pool_param_t timer_pool_param;
+ odp_timer_pool_t tp;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ uint64_t max_tmo_ns, min_tmo_ns;
+ uint32_t i, j;
+ uint32_t max_timers;
+ int priv;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_cpu = test_options->num_cpu;
+ uint32_t num_tp = test_options->num_tp;
+ uint32_t num_timer = test_options->num_timer;
+ uint64_t res_ns = test_options->res_ns;
+ uint64_t period_ns = test_options->period_ns;
+ int mode = test_options->mode;
+ char tp_name[] = "timer_pool_00";
+
+ max_tmo_ns = START_NS + (num_timer * period_ns);
+ min_tmo_ns = START_NS / 2;
+
+ if (test_options->mode == MODE_START_EXPIRE) {
+ /*
+ * Timers are set to 1-2 periods from current time. Add an
+ * arbitrary margin of one period, resulting in maximum of
+ * three periods.
+ */
+ max_tmo_ns = period_ns * 3;
+ min_tmo_ns = test_options->res_ns / 2;
+ }
+
+ priv = 0;
+ if (test_options->shared == 0)
+ priv = 1;
+
+ printf("\nTimer performance test\n");
+ printf(" mode %i\n", mode);
+ printf(" num cpu %u\n", num_cpu);
+ printf(" private pool %i\n", priv);
+ printf(" num timer pool %u\n", num_tp);
+ printf(" num timer %u\n", num_timer);
+ printf(" resolution %" PRIu64 " nsec\n", res_ns);
+ printf(" period %" PRIu64 " nsec\n", period_ns);
+ printf(" max timeout %" PRIu64 " nsec\n", max_tmo_ns);
+ printf(" min timeout %" PRIu64 " nsec\n", min_tmo_ns);
+ printf(" first timer at %.2f sec\n", (double)START_NS / ODP_TIME_SEC_IN_NS);
+ if (mode == MODE_SCHED_OVERH)
+ printf(" test duration %.2f sec\n", (double)max_tmo_ns / ODP_TIME_SEC_IN_NS);
+ else
+ printf(" test rounds %" PRIu64 "\n", test_options->test_rounds);
+
+ for (i = 0; i < MAX_TIMER_POOLS; i++) {
+ global->timer_pool[i].tp = ODP_TIMER_POOL_INVALID;
+ global->pool[i] = ODP_POOL_INVALID;
+ global->queue[i] = ODP_QUEUE_INVALID;
+
+ for (j = 0; j < MAX_TIMERS; j++)
+ global->timer[i][j] = ODP_TIMER_INVALID;
+ }
+
+ if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
+ ODPH_ERR("Timer capability failed\n");
+ return -1;
+ }
+
+ memset(&timer_res_capa, 0, sizeof(odp_timer_res_capability_t));
+ timer_res_capa.res_ns = res_ns;
+ if (odp_timer_res_capability(ODP_CLOCK_DEFAULT, &timer_res_capa)) {
+ ODPH_ERR("Timer resolution capability failed\n");
+ return -1;
+ }
+
+ if (res_ns < timer_capa.max_res.res_ns) {
+ ODPH_ERR("Too high resolution\n");
+ return -1;
+ }
+
+ if (min_tmo_ns < timer_res_capa.min_tmo) {
+ ODPH_ERR("Too short min timeout\n");
+ return -1;
+ }
+
+ if (max_tmo_ns > timer_res_capa.max_tmo) {
+ ODPH_ERR("Too long max timeout\n");
+ return -1;
+ }
+
+ max_timers = timer_capa.max_timers;
+ if (max_timers && num_timer > max_timers) {
+ ODPH_ERR("Too many timers (max %u)\n", max_timers);
+ return -1;
+ }
+
+ if (num_tp > timer_capa.max_pools) {
+ ODPH_ERR("Too many timer pools (max %u)\n", timer_capa.max_pools);
+ return -1;
+ }
+
+ odp_timer_pool_param_init(&timer_pool_param);
+ timer_pool_param.res_ns = res_ns;
+ timer_pool_param.min_tmo = min_tmo_ns;
+ timer_pool_param.max_tmo = max_tmo_ns;
+ timer_pool_param.num_timers = num_timer;
+ timer_pool_param.priv = priv;
+ timer_pool_param.clk_src = ODP_CLOCK_DEFAULT;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = num_timer;
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ for (i = 0; i < num_tp; i++) {
+ if (num_tp < 100) {
+ tp_name[11] = '0' + i / 10;
+ tp_name[12] = '0' + i % 10;
+ }
+
+ tp = odp_timer_pool_create(tp_name, &timer_pool_param);
+ global->timer_pool[i].tp = tp;
+ if (tp == ODP_TIMER_POOL_INVALID) {
+ ODPH_ERR("Timer pool create failed (%u)\n", i);
+ return -1;
+ }
+
+ if (odp_timer_pool_start_multi(&tp, 1) != 1) {
+ ODPH_ERR("Timer pool start failed (%u)\n", i);
+ return -1;
+ }
+
+ pool = odp_pool_create(tp_name, &pool_param);
+ global->pool[i] = pool;
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool create failed (%u)\n", i);
+ return -1;
+ }
+
+ queue = odp_queue_create(tp_name, &queue_param);
+ global->queue[i] = queue;
+ if (queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed (%u)\n", i);
+ return -1;
+ }
+
+ global->timer_pool[i].period_tick = odp_timer_ns_to_tick(tp,
+ test_options->period_ns);
+ global->timer_pool[i].start_tick = odp_timer_ns_to_tick(tp, START_NS);
+ }
+
+ printf(" start %" PRIu64 " tick\n", global->timer_pool[0].start_tick);
+ printf(" period %" PRIu64 " ticks\n", global->timer_pool[0].period_tick);
+ printf("\n");
+
+ return 0;
+}
+
+static int set_timers(test_global_t *global)
+{
+ odp_timer_pool_info_t timer_pool_info;
+ odp_timer_pool_t tp;
+ odp_timer_t timer;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_time_t time;
+ uint64_t tick_cur, nsec, time_ns;
+ uint64_t max_tmo_ns;
+ uint32_t i, j;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_tp = test_options->num_tp;
+ uint32_t num_timer = test_options->num_timer;
+ uint64_t period_ns = test_options->period_ns;
+
+ max_tmo_ns = START_NS + (num_timer * period_ns);
+
+ for (i = 0; i < num_tp; i++) {
+ tp = global->timer_pool[i].tp;
+ pool = global->pool[i];
+ queue = global->queue[i];
+
+ nsec = max_tmo_ns;
+ tick_cur = odp_timer_current_tick(tp);
+ time = odp_time_global();
+ time_ns = odp_time_to_ns(time);
+
+ for (j = 0; j < num_timer; j++) {
+ uint64_t tick_ns;
+ odp_timeout_t timeout;
+ odp_event_t ev;
+ int status;
+ timer_ctx_t *ctx = &global->timer_ctx[i][j];
+ odp_timer_start_t start_param;
+
+ /* Set timers backwards, the last timer is set first */
+ if (j == 0)
+ ctx->last = 1;
+
+ ctx->target_ns = time_ns + nsec;
+ ctx->tp_idx = i;
+ ctx->timer_idx = j;
+
+ timeout = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(timeout);
+
+ timer = odp_timer_alloc(tp, queue, ctx);
+ global->timer[i][j] = timer;
+
+ tick_ns = odp_timer_ns_to_tick(tp, nsec);
+ nsec = nsec - period_ns;
+
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick = tick_cur + tick_ns;
+ start_param.tmo_ev = ev;
+
+ if (test_options->mode == MODE_START_EXPIRE) {
+ uint64_t offset_ns = period_ns + j * period_ns / num_timer;
+
+ ctx->target_ns = time_ns + offset_ns;
+ ctx->target_tick = tick_cur + odp_timer_ns_to_tick(tp, offset_ns);
+ start_param.tick = ctx->target_tick;
+ }
+
+ status = odp_timer_start(timer, &start_param);
+ if (status != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer set %i/%i (ret %i)\n", i, j, status);
+ return -1;
+ }
+ }
+
+ if (odp_timer_pool_info(tp, &timer_pool_info)) {
+ ODPH_ERR("Timer pool info failed\n");
+ return -1;
+ }
+
+ printf("Timer pool info [%i]:\n", i);
+ printf(" cur_timers %u\n", timer_pool_info.cur_timers);
+ printf(" hwm_timers %u\n", timer_pool_info.hwm_timers);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int destroy_timer_pool(test_global_t *global)
+{
+ odp_timer_pool_t tp;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_timer_t timer;
+ uint32_t i, j;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_timer = test_options->num_timer;
+ uint32_t num_tp = test_options->num_tp;
+
+ for (i = 0; i < num_tp; i++) {
+ for (j = 0; j < num_timer; j++) {
+ timer = global->timer[i][j];
+
+ if (timer == ODP_TIMER_INVALID)
+ break;
+
+ if (odp_timer_free(timer))
+ printf("Timer free failed: %i/%i\n", i, j);
+ }
+
+ queue = global->queue[i];
+ if (queue != ODP_QUEUE_INVALID)
+ odp_queue_destroy(queue);
+
+ pool = global->pool[i];
+ if (pool != ODP_POOL_INVALID)
+ odp_pool_destroy(pool);
+
+ tp = global->timer_pool[i].tp;
+ if (tp != ODP_TIMER_POOL_INVALID)
+ odp_timer_pool_destroy(tp);
+ }
+
+ return 0;
+}
+
+static int sched_mode_worker(void *arg)
+{
+ int thr;
+ uint32_t exit_test;
+ odp_event_t ev;
+ odp_timeout_t tmo;
+ uint64_t c2, diff, nsec, time_ns, target_ns;
+ odp_time_t t1, t2, time;
+ time_stat_t before, after;
+ timer_ctx_t *ctx;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_tp = test_options->num_tp;
+ uint64_t cycles = 0;
+ uint64_t events = 0;
+ uint64_t rounds = 0;
+ uint64_t c1 = 0;
+ int meas = 1;
+ int ret = 0;
+
+ memset(&before, 0, sizeof(time_stat_t));
+ memset(&after, 0, sizeof(time_stat_t));
+
+ thr = odp_thread_id();
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+
+ while (1) {
+ if (meas) {
+ c1 = odp_cpu_cycles();
+ meas = 0;
+ }
+
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ rounds++;
+
+ exit_test = odp_atomic_load_u32(&global->exit_test);
+ if (odp_likely(ev == ODP_EVENT_INVALID && exit_test < num_tp))
+ continue;
+
+ c2 = odp_cpu_cycles();
+ diff = odp_cpu_cycles_diff(c2, c1);
+ cycles += diff;
+
+ if (ev == ODP_EVENT_INVALID && exit_test >= num_tp)
+ break;
+
+ time = odp_time_global();
+ time_ns = odp_time_to_ns(time);
+ events++;
+ meas = 1;
+
+ tmo = odp_timeout_from_event(ev);
+ ctx = odp_timeout_user_ptr(tmo);
+ odp_timeout_free(tmo);
+
+ target_ns = ctx->target_ns;
+ if (time_ns < target_ns) {
+ diff = target_ns - time_ns;
+ before.num++;
+ before.sum_ns += diff;
+ if (diff > before.max_ns)
+ before.max_ns = diff;
+
+ ODPH_DBG("before %" PRIu64 "\n", diff);
+ } else {
+ diff = time_ns - target_ns;
+ after.num++;
+ after.sum_ns += diff;
+ if (diff > after.max_ns)
+ after.max_ns = diff;
+
+ ODPH_DBG("after %" PRIu64 "\n", time_ns - target_ns);
+ }
+
+ if (ctx->last)
+ odp_atomic_inc_u32(&global->exit_test);
+ }
+
+ t2 = odp_time_local();
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Update stats*/
+ global->stat[thr].events = events;
+ global->stat[thr].cycles_0 = cycles;
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].before = before;
+ global->stat[thr].after = after;
+
+ return ret;
+}
+
+static int cancel_timers(test_global_t *global, uint32_t worker_idx)
+{
+ uint32_t i, j;
+ int r;
+ odp_timer_t timer;
+ odp_event_t ev;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_tp = test_options->num_tp;
+ uint32_t num_timer = test_options->num_timer;
+ uint32_t num_worker = test_options->num_cpu;
+ int ret = 0;
+
+ for (i = 0; i < num_tp; i++) {
+ for (j = worker_idx; j < num_timer; j += num_worker) {
+ timer = global->timer[i][j];
+ if (timer == ODP_TIMER_INVALID)
+ continue;
+
+ r = odp_timer_cancel(timer, &ev);
+
+ if (r == ODP_TIMER_SUCCESS) {
+ odp_event_free(ev);
+ } else if (r == ODP_TIMER_TOO_NEAR) {
+ ret = 1;
+ } else {
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int set_cancel_mode_worker(void *arg)
+{
+ uint64_t tick, start_tick, period_tick, nsec;
+ uint64_t c1, c2;
+ int thr, status;
+ uint32_t i, j, worker_idx;
+ odp_event_t ev;
+ odp_time_t t1, t2;
+ odp_timer_t timer;
+ odp_timer_pool_t tp;
+ odp_timeout_t tmo;
+ odp_timer_start_t start_param;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ test_options_t *test_options = &global->test_options;
+ uint32_t num_tp = test_options->num_tp;
+ uint32_t num_timer = test_options->num_timer;
+ uint32_t num_worker = test_options->num_cpu;
+ int ret = 0;
+ int started = 0;
+ uint64_t test_rounds = test_options->test_rounds;
+ uint64_t num_tmo = 0;
+ uint64_t num_cancel = 0;
+ uint64_t num_set = 0;
+ uint64_t cancel_cycles = 0, start_cycles = 0;
+ odp_event_t ev_tbl[MAX_TIMERS];
+
+ thr = odp_thread_id();
+ worker_idx = thread_arg->worker_idx;
+ t1 = ODP_TIME_NULL;
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (odp_unlikely(ev != ODP_EVENT_INVALID)) {
+ /* Timeout, set timer again. When start_tick is large enough, this should
+ * not happen. */
+ timer_ctx_t *ctx;
+
+ tmo = odp_timeout_from_event(ev);
+ ctx = odp_timeout_user_ptr(tmo);
+ i = ctx->tp_idx;
+ j = ctx->timer_idx;
+ timer = global->timer[i][j];
+ start_tick = global->timer_pool[i].start_tick;
+ period_tick = global->timer_pool[i].period_tick;
+ tick = start_tick + j * period_tick;
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tick;
+ start_param.tmo_ev = ev;
+
+ status = odp_timer_start(timer, &start_param);
+ num_tmo++;
+ num_set++;
+
+ if (status != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer set (tmo) failed (ret %i)\n", status);
+ ret = -1;
+ break;
+ }
+
+ continue;
+ }
+
+ if (odp_unlikely(odp_atomic_load_u32(&global->exit_test)))
+ break;
+
+ if (odp_unlikely(started == 0)) {
+ /* Run schedule loop while waiting for timers to be created */
+ if (odp_atomic_load_acq_u32(&global->timers_started) == 0)
+ continue;
+
+ /* Start measurements */
+ started = 1;
+ t1 = odp_time_local();
+ }
+
+ /* Cancel and set timers again */
+ for (i = 0; i < num_tp; i++) {
+ tp = global->timer_pool[i].tp;
+ if (tp == ODP_TIMER_POOL_INVALID)
+ continue;
+
+ start_tick = global->timer_pool[i].start_tick;
+ period_tick = global->timer_pool[i].period_tick;
+
+ tick = odp_timer_current_tick(tp) + start_tick;
+ c1 = odp_cpu_cycles();
+
+ for (j = worker_idx; j < num_timer; j += num_worker) {
+ ev_tbl[j] = ODP_EVENT_INVALID;
+
+ timer = global->timer[i][j];
+ if (timer == ODP_TIMER_INVALID)
+ continue;
+
+ status = odp_timer_cancel(timer, &ev_tbl[j]);
+ num_cancel++;
+
+ if (odp_unlikely(status == ODP_TIMER_TOO_NEAR)) {
+ continue;
+ } else if (odp_unlikely(status != ODP_TIMER_SUCCESS)) {
+ ODPH_ERR("Timer (%u/%u) cancel failed (ret %i)\n", i, j,
+ status);
+ ret = -1;
+ break;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ cancel_cycles += odp_cpu_cycles_diff(c2, c1);
+ c1 = c2;
+
+ for (j = worker_idx; j < num_timer; j += num_worker) {
+ if (ev_tbl[j] == ODP_EVENT_INVALID)
+ continue;
+
+ timer = global->timer[i][j];
+ if (timer == ODP_TIMER_INVALID)
+ continue;
+
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick = tick + j * period_tick;
+ start_param.tmo_ev = ev_tbl[j];
+
+ status = odp_timer_start(timer, &start_param);
+ num_set++;
+
+ if (status != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer (%u/%u) set failed (ret %i)\n", i, j,
+ status);
+ ret = -1;
+ break;
+ }
+ }
+
+ c2 = odp_cpu_cycles();
+ start_cycles += odp_cpu_cycles_diff(c2, c1);
+ }
+
+ if (test_rounds) {
+ test_rounds--;
+ if (test_rounds == 0)
+ break;
+ }
+ }
+
+ t2 = odp_time_local();
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Cancel all timers that belong to this thread */
+ if (cancel_timers(global, worker_idx))
+ ODPH_ERR("Timer cancel failed\n");
+
+ /* Update stats */
+ global->stat[thr].events = num_tmo;
+ global->stat[thr].rounds = test_options->test_rounds - test_rounds;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].cycles_0 = cancel_cycles;
+ global->stat[thr].cycles_1 = start_cycles;
+
+ global->stat[thr].cancels = num_cancel;
+ global->stat[thr].sets = num_set;
+
+ return ret;
+}
+
+static int set_expire_mode_worker(void *arg)
+{
+ int status, thr;
+ uint32_t i, j, exit_test;
+ odp_event_t ev;
+ odp_timeout_t tmo;
+ uint64_t c2, c3, c4, diff, nsec, time_ns, target_ns, period_tick, wait;
+ odp_timer_t timer;
+ odp_timer_start_t start_param;
+ odp_time_t t1, t2;
+ time_stat_t before, after;
+ timer_ctx_t *ctx;
+ thread_arg_t *thread_arg = arg;
+ test_global_t *global = thread_arg->global;
+ test_options_t *opt = &global->test_options;
+ uint32_t num_tp = opt->num_tp;
+ uint64_t sched_cycles = 0;
+ uint64_t start_cycles = 0;
+ uint64_t events = 0;
+ uint64_t rounds = 0;
+ uint64_t c1 = 0;
+ int meas = 1;
+ int ret = 0;
+
+ memset(&before, 0, sizeof(time_stat_t));
+ memset(&after, 0, sizeof(time_stat_t));
+
+ thr = odp_thread_id();
+
+ /* Start all workers at the same time */
+ odp_barrier_wait(&global->barrier);
+
+ t1 = odp_time_local();
+
+ while (events < opt->test_rounds * opt->num_timer / opt->num_cpu) {
+ if (meas) {
+ c1 = odp_cpu_cycles();
+ meas = 0;
+ }
+
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ rounds++;
+
+ exit_test = odp_atomic_load_u32(&global->exit_test);
+ if (odp_likely(ev == ODP_EVENT_INVALID && exit_test < num_tp))
+ continue;
+
+ c2 = odp_cpu_cycles();
+ diff = odp_cpu_cycles_diff(c2, c1);
+ sched_cycles += diff;
+
+ if (ev == ODP_EVENT_INVALID && exit_test >= num_tp)
+ break;
+
+ events++;
+ meas = 1;
+ tmo = odp_timeout_from_event(ev);
+ ctx = odp_timeout_user_ptr(tmo);
+ i = ctx->tp_idx;
+ j = ctx->timer_idx;
+ timer = global->timer[i][j];
+ period_tick = global->timer_pool[i].period_tick;
+ time_ns = odp_time_global_ns();
+ target_ns = ctx->target_ns;
+
+ if (time_ns < target_ns) {
+ diff = target_ns - time_ns;
+ before.num++;
+ before.sum_ns += diff;
+ if (diff > before.max_ns)
+ before.max_ns = diff;
+
+ ODPH_DBG("before %" PRIu64 "\n", diff);
+ } else {
+ diff = time_ns - target_ns;
+ after.num++;
+ after.sum_ns += diff;
+ if (diff > after.max_ns)
+ after.max_ns = diff;
+
+ ODPH_DBG("after %" PRIu64 "\n", diff);
+ }
+
+ /* Start the timer again */
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ ctx->target_ns += opt->period_ns;
+ ctx->target_tick += period_tick;
+ start_param.tick = ctx->target_tick;
+ start_param.tmo_ev = ev;
+ c3 = odp_cpu_cycles();
+
+ status = odp_timer_start(timer, &start_param);
+
+ c4 = odp_cpu_cycles();
+ diff = odp_cpu_cycles_diff(c4, c3);
+ start_cycles += diff;
+
+ if (status != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Timer set (tmo) failed (ret %i)\n", status);
+ ret = -1;
+ break;
+ }
+ }
+
+ t2 = odp_time_local();
+ nsec = odp_time_diff_ns(t2, t1);
+
+ /* Cancel all timers that belong to this thread */
+ status = cancel_timers(global, thread_arg->worker_idx);
+
+ wait = ODP_SCHED_NO_WAIT;
+ if (status > 0)
+ wait = odp_schedule_wait_time(opt->period_ns);
+
+ /* Wait and free remaining events */
+ while (1) {
+ ev = odp_schedule(NULL, wait);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+ odp_event_free(ev);
+ }
+
+ /* Update stats*/
+ global->stat[thr].events = events;
+ global->stat[thr].cycles_0 = sched_cycles;
+ global->stat[thr].cycles_1 = start_cycles;
+ global->stat[thr].rounds = rounds;
+ global->stat[thr].nsec = nsec;
+ global->stat[thr].before = before;
+ global->stat[thr].after = after;
+
+ return ret;
+}
+
+static int start_workers(test_global_t *global, odp_instance_t instance)
+{
+ odph_thread_common_param_t thr_common;
+ int i, ret;
+ test_options_t *test_options = &global->test_options;
+ int num_cpu = test_options->num_cpu;
+ odph_thread_param_t thr_param[num_cpu];
+
+ memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
+ odph_thread_common_param_init(&thr_common);
+
+ thr_common.instance = instance;
+ thr_common.cpumask = &global->cpumask;
+
+ for (i = 0; i < num_cpu; i++) {
+ odph_thread_param_init(&thr_param[i]);
+
+ if (test_options->mode == MODE_SCHED_OVERH)
+ thr_param[i].start = sched_mode_worker;
+ else if (test_options->mode == MODE_START_CANCEL)
+ thr_param[i].start = set_cancel_mode_worker;
+ else
+ thr_param[i].start = set_expire_mode_worker;
+
+ thr_param[i].arg = &global->thread_arg[i];
+ thr_param[i].thr_type = ODP_THREAD_WORKER;
+ }
+
+ ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
+ num_cpu);
+
+ if (ret != num_cpu) {
+ ODPH_ERR("Thread create failed %i\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void sum_stat(test_global_t *global)
+{
+ int i;
+ test_stat_sum_t *sum = &global->stat_sum;
+
+ memset(sum, 0, sizeof(test_stat_sum_t));
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds == 0)
+ continue;
+
+ sum->num++;
+ sum->events += global->stat[i].events;
+ sum->rounds += global->stat[i].rounds;
+ sum->cycles_0 += global->stat[i].cycles_0;
+ sum->cycles_1 += global->stat[i].cycles_1;
+ sum->nsec += global->stat[i].nsec;
+ sum->cancels += global->stat[i].cancels;
+ sum->sets += global->stat[i].sets;
+
+ sum->before.num += global->stat[i].before.num;
+ sum->before.sum_ns += global->stat[i].before.sum_ns;
+ sum->after.num += global->stat[i].after.num;
+ sum->after.sum_ns += global->stat[i].after.sum_ns;
+
+ if (global->stat[i].before.max_ns > sum->before.max_ns)
+ sum->before.max_ns = global->stat[i].before.max_ns;
+
+ if (global->stat[i].after.max_ns > sum->after.max_ns)
+ sum->after.max_ns = global->stat[i].after.max_ns;
+ }
+
+ if (sum->num)
+ sum->time_ave = ((double)sum->nsec / sum->num) / ODP_TIME_SEC_IN_NS;
+}
+
+static void print_stat_sched_mode(test_global_t *global)
+{
+ int i;
+ test_stat_sum_t *sum = &global->stat_sum;
+ double round_ave = 0.0;
+ double before_ave = 0.0;
+ double after_ave = 0.0;
+ int num = 0;
+
+ printf("\n");
+ printf("RESULTS - schedule() cycles per thread:\n");
+ printf("----------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)global->stat[i].cycles_0 / global->stat[i].rounds);
+ num++;
+ }
+ }
+
+ printf("\n\n");
+
+ if (sum->num)
+ round_ave = (double)sum->rounds / sum->num;
+
+ if (sum->before.num)
+ before_ave = (double)sum->before.sum_ns / sum->before.num;
+
+ if (sum->after.num)
+ after_ave = (double)sum->after.sum_ns / sum->after.num;
+
+ printf("TOTAL (%i workers)\n", sum->num);
+ printf(" events: %" PRIu64 "\n", sum->events);
+ printf(" ave time: %.2f sec\n", sum->time_ave);
+ printf(" ave rounds per sec: %.2fM\n", (round_ave / sum->time_ave) / 1000000.0);
+ printf(" num before: %" PRIu64 "\n", sum->before.num);
+ printf(" ave before: %.1f nsec\n", before_ave);
+ printf(" max before: %" PRIu64 " nsec\n", sum->before.max_ns);
+ printf(" num after: %" PRIu64 "\n", sum->after.num);
+ printf(" ave after: %.1f nsec\n", after_ave);
+ printf(" max after: %" PRIu64 " nsec\n", sum->after.max_ns);
+ printf("\n");
+}
+
+static void print_stat_set_cancel_mode(test_global_t *global)
+{
+ int i;
+ test_stat_sum_t *sum = &global->stat_sum;
+ double set_ave = 0.0;
+ int num = 0;
+
+ printf("\n");
+ printf("RESULTS\n");
+ printf("odp_timer_cancel() cycles per thread:\n");
+ printf("-------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ const test_stat_t *si = &global->stat[i];
+
+ if (si->cancels) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)si->cycles_0 / si->cancels);
+ num++;
+ }
+ }
+
+ printf("\n\n");
+
+ num = 0;
+ printf("odp_timer_start() cycles per thread:\n");
+ printf("-------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ const test_stat_t *si = &global->stat[i];
+
+ if (si->sets) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)si->cycles_1 / si->sets);
+ num++;
+ }
+ }
+
+ if (sum->num)
+ set_ave = (double)sum->sets / sum->num;
+
+ printf("\n\n");
+ printf("TOTAL (%i workers)\n", sum->num);
+ printf(" rounds: %" PRIu64 "\n", sum->rounds);
+ printf(" timeouts: %" PRIu64 "\n", sum->events);
+ printf(" timer cancels: %" PRIu64 "\n", sum->cancels);
+ printf(" cancels failed: %" PRIu64 "\n", sum->cancels - sum->sets);
+ printf(" timer sets: %" PRIu64 "\n", sum->sets);
+ printf(" ave time: %.2f sec\n", sum->time_ave);
+ printf(" cancel+set per cpu: %.2fM per sec\n", (set_ave / sum->time_ave) / 1000000.0);
+ printf("\n");
+}
+
+static void print_stat_expire_mode(test_global_t *global)
+{
+ int i;
+ test_stat_sum_t *sum = &global->stat_sum;
+ double round_ave = 0.0;
+ double before_ave = 0.0;
+ double after_ave = 0.0;
+ int num = 0;
+
+ printf("\n");
+ printf("RESULTS\n");
+ printf("odp_schedule() cycles per thread:\n");
+ printf("-------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].rounds) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)global->stat[i].cycles_0 / global->stat[i].rounds);
+ num++;
+ }
+ }
+
+ printf("\n\n");
+
+ num = 0;
+ printf("odp_timer_start() cycles per thread:\n");
+ printf("-------------------------------------------------\n");
+ printf(" 1 2 3 4 5 6 7 8 9 10");
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ if (global->stat[i].events) {
+ if ((num % 10) == 0)
+ printf("\n ");
+
+ printf("%6.1f ", (double)global->stat[i].cycles_1 / global->stat[i].events);
+ num++;
+ }
+ }
+
+ printf("\n\n");
+
+ if (sum->num)
+ round_ave = (double)sum->rounds / sum->num;
+
+ if (sum->before.num)
+ before_ave = (double)sum->before.sum_ns / sum->before.num;
+
+ if (sum->after.num)
+ after_ave = (double)sum->after.sum_ns / sum->after.num;
+
+ printf("TOTAL (%i workers)\n", sum->num);
+ printf(" events: %" PRIu64 "\n", sum->events);
+ printf(" ave time: %.2f sec\n", sum->time_ave);
+ printf(" ave rounds per sec: %.2fM\n", (round_ave / sum->time_ave) / 1000000.0);
+ printf(" num before: %" PRIu64 "\n", sum->before.num);
+ printf(" ave before: %.1f nsec\n", before_ave);
+ printf(" max before: %" PRIu64 " nsec\n", sum->before.max_ns);
+ printf(" num after: %" PRIu64 "\n", sum->after.num);
+ printf(" ave after: %.1f nsec\n", after_ave);
+ printf(" max after: %" PRIu64 " nsec\n", sum->after.max_ns);
+ printf("\n");
+}
+
+static void sig_handler(int signo)
+{
+ (void)signo;
+
+ if (test_global == NULL)
+ return;
+ odp_atomic_add_u32(&test_global->exit_test, MAX_TIMER_POOLS);
+}
+
+int main(int argc, char **argv)
+{
+ odph_helper_options_t helper_options;
+ odp_instance_t instance;
+ odp_init_t init;
+ odp_shm_t shm;
+ test_global_t *global;
+ test_options_t *test_options;
+ int i, shared, mode;
+
+ signal(SIGINT, sig_handler);
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.tm = 1;
+
+ init.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ return -1;
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ return -1;
+ }
+
+ shm = odp_shm_reserve("timer_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+ if (global == NULL) {
+ ODPH_ERR("Shared mem alloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ test_global = global;
+
+ memset(global, 0, sizeof(test_global_t));
+ odp_atomic_init_u32(&global->exit_test, 0);
+ odp_atomic_init_u32(&global->timers_started, 0);
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
+ global->thread_arg[i].global = global;
+ global->thread_arg[i].worker_idx = i;
+ }
+
+ if (parse_options(argc, argv, &global->test_options))
+ return -1;
+
+ test_options = &global->test_options;
+ shared = test_options->shared;
+ mode = test_options->mode;
+
+ odp_sys_info_print();
+
+ odp_schedule_config(NULL);
+
+ if (set_num_cpu(global))
+ return -1;
+
+ if (create_timer_pools(global))
+ return -1;
+
+ if (shared) {
+ /* Start worker threads */
+ start_workers(global, instance);
+
+ /* Wait until workers have started.
+ * Scheduler calls from workers may be needed to run timer
+ * pools in a software implementation. Wait 1 msec to ensure
+ * that timer pools are running before setting timers. */
+ odp_barrier_wait(&global->barrier);
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ }
+
+ /* Set timers. Force workers to exit on failure. */
+ if (set_timers(global))
+ odp_atomic_add_u32(&global->exit_test, MAX_TIMER_POOLS);
+ else
+ odp_atomic_store_rel_u32(&global->timers_started, 1);
+
+ if (!shared) {
+ /* Test private pools on the master thread */
+ if (mode == MODE_SCHED_OVERH) {
+ if (sched_mode_worker(&global->thread_arg[0])) {
+ ODPH_ERR("Sched_mode_worker failed\n");
+ return -1;
+ }
+ } else if (mode == MODE_START_CANCEL) {
+ if (set_cancel_mode_worker(&global->thread_arg[0])) {
+ ODPH_ERR("Set_cancel_mode_worker failed\n");
+ return -1;
+ }
+ } else {
+ if (set_expire_mode_worker(&global->thread_arg[0])) {
+ ODPH_ERR("Set_expire_mode_worker failed\n");
+ return -1;
+ }
+ }
+ } else {
+ /* Wait workers to exit */
+ odph_thread_join(global->thread_tbl,
+ global->test_options.num_cpu);
+ }
+
+ sum_stat(global);
+
+ if (mode == MODE_SCHED_OVERH)
+ print_stat_sched_mode(global);
+ else if (mode == MODE_START_CANCEL)
+ print_stat_set_cancel_mode(global);
+ else
+ print_stat_expire_mode(global);
+
+ destroy_timer_pool(global);
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("Shared mem free failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Term local failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Term global failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/performance/odp_timer_perf_run.sh b/test/performance/odp_timer_perf_run.sh
new file mode 100755
index 000000000..aa8890e8e
--- /dev/null
+++ b/test/performance/odp_timer_perf_run.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 Nokia
+#
+
+TEST_DIR="${TEST_DIR:-$(dirname $0)}"
+
+echo odp_timer_perf: odp_schedule overhead mode
+echo ===============================================
+
+$TEST_DIR/odp_timer_perf${EXEEXT} -m 0 -c 1
+
+RET_VAL=$?
+if [ $RET_VAL -ne 0 ]; then
+ echo odp_timer_perf -m 0: FAILED
+ exit $RET_VAL
+fi
+
+echo odp_timer_perf: timer set + cancel mode
+echo ===============================================
+
+$TEST_DIR/odp_timer_perf${EXEEXT} -m 1 -c 1 -t 10 -R 50
+
+RET_VAL=$?
+if [ $RET_VAL -ne 0 ]; then
+ echo odp_timer_perf -m 1: FAILED
+ exit $RET_VAL
+fi
+
+exit 0
diff --git a/test/performance/udp64.pcap b/test/performance/udp64.pcap
new file mode 100644
index 000000000..45f9d6e63
--- /dev/null
+++ b/test/performance/udp64.pcap
Binary files differ
diff --git a/test/test_debug.h b/test/test_debug.h
deleted file mode 100644
index aec0977d1..000000000
--- a/test/test_debug.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-/**
- * @file
- *
- * test debug
- */
-
-#ifndef TEST_DEBUG_H_
-#define TEST_DEBUG_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef TEST_DEBUG_PRINT
-#define TEST_DEBUG_PRINT 1
-#endif
-
-/**
- * log level.
- */
-typedef enum test_log_level {
- TEST_LOG_DBG,
- TEST_LOG_ERR,
- TEST_LOG_ABORT
-} test_log_level_e;
-
-/**
- * default LOG macro.
- */
-#define TEST_LOG(level, fmt, ...) \
-do { \
- switch (level) { \
- case TEST_LOG_ERR: \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case TEST_LOG_DBG: \
- if (TEST_DEBUG_PRINT == 1) \
- fprintf(stderr, "%s:%d:%s():" fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- break; \
- case TEST_LOG_ABORT: \
- fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
- __LINE__, __func__, ##__VA_ARGS__); \
- abort(); \
- break; \
- default: \
- fprintf(stderr, "Unknown LOG level"); \
- break;\
- } \
-} while (0)
-
-/**
- * Debug printing macro, which prints output when DEBUG flag is set.
- */
-#define LOG_DBG(fmt, ...) \
- TEST_LOG(TEST_LOG_DBG, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function).
- */
-#define LOG_ERR(fmt, ...) \
- TEST_LOG(TEST_LOG_ERR, fmt, ##__VA_ARGS__)
-
-/**
- * Print output to stderr (file, line and function),
- * then abort.
- */
-#define LOG_ABORT(fmt, ...) \
- TEST_LOG(TEST_LOG_ABORT, fmt, ##__VA_ARGS__)
-
-/**
- * @}
- */
-
-/**
- * Mark intentionally unused argument for functions
- */
-#define TEST_UNUSED __attribute__((__unused__))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/test/validation/Makefile.am b/test/validation/Makefile.am
new file mode 100644
index 000000000..328543780
--- /dev/null
+++ b/test/validation/Makefile.am
@@ -0,0 +1,3 @@
+if test_vald
+ SUBDIRS = api
+endif
diff --git a/test/linux-generic/.gitignore b/test/validation/api/.gitignore
index 5dabf91c1..5dabf91c1 100644
--- a/test/linux-generic/.gitignore
+++ b/test/validation/api/.gitignore
diff --git a/test/validation/api/Makefile.am b/test/validation/api/Makefile.am
new file mode 100644
index 000000000..5a3c0216b
--- /dev/null
+++ b/test/validation/api/Makefile.am
@@ -0,0 +1,100 @@
+ODP_MODULES = align \
+ atomic \
+ barrier \
+ buffer \
+ byteorder \
+ chksum \
+ classification \
+ comp \
+ cpumask \
+ crypto \
+ dma \
+ errno \
+ event \
+ hash \
+ hints \
+ init \
+ ipsec \
+ lock \
+ ml \
+ queue \
+ packet \
+ pktio \
+ pool \
+ random \
+ scheduler \
+ stash \
+ std \
+ thread \
+ time \
+ timer \
+ traffic_mngr \
+ shmem \
+ system
+
+SUBDIRS = $(ODP_MODULES)
+
+include $(top_srcdir)/test/Makefile.inc
+TESTS_ENVIRONMENT += TEST_DIR=${top_builddir}/test/validation/api
+
+TESTS = \
+ align/align_main$(EXEEXT) \
+ atomic/atomic_main$(EXEEXT) \
+ barrier/barrier_main$(EXEEXT) \
+ buffer/buffer_main$(EXEEXT) \
+ byteorder/byteorder_main$(EXEEXT) \
+ chksum/chksum_main$(EXEEXT) \
+ classification/classification_main$(EXEEXT) \
+ comp/comp_main$(EXEEXT) \
+ cpumask/cpumask_main$(EXEEXT) \
+ crypto/crypto_main$(EXEEXT) \
+ dma/dma_main$(EXEEXT) \
+ errno/errno_main$(EXEEXT) \
+ event/event_main$(EXEEXT) \
+ hash/hash_main$(EXEEXT) \
+ hints/hints_main$(EXEEXT) \
+ init/init_defaults.sh \
+ init/init_abort.sh \
+ init/init_log.sh \
+ init/init_log_thread.sh \
+ init/init_num_thr.sh \
+ init/init_feature_enabled.sh \
+ init/init_feature_disabled.sh \
+ init/init_test_param_init.sh \
+ init/init_test_term_abnormal.sh \
+ ipsec/ipsec_sync.sh \
+ ipsec/ipsec_async.sh \
+ ipsec/ipsec_inline_in.sh \
+ ipsec/ipsec_inline_out.sh \
+ lock/lock_main$(EXEEXT) \
+ ml/ml_main$(EXEEXT) \
+ packet/packet_main$(EXEEXT) \
+ pktio/pktio_main$(EXEEXT) \
+ pool/pool_main$(EXEEXT) \
+ queue/queue_main$(EXEEXT) \
+ random/random_main$(EXEEXT) \
+ scheduler/scheduler_main$(EXEEXT) \
+ scheduler/scheduler_no_predef_groups$(EXEEXT) \
+ stash/stash_main$(EXEEXT) \
+ std/std_main$(EXEEXT) \
+ thread/thread_main$(EXEEXT) \
+ time/time_main$(EXEEXT) \
+ timer/timer_main$(EXEEXT) \
+ traffic_mngr/traffic_mngr_main$(EXEEXT) \
+ shmem/shmem_main$(EXEEXT) \
+ system/system_main$(EXEEXT)
+
+TESTNAME = validation
+
+TESTENV = tests-$(TESTNAME).env
+
+test_DATA = $(TESTENV)
+
+DISTCLEANFILES = $(TESTENV)
+.PHONY: $(TESTENV)
+$(TESTENV):
+ echo "TESTS=\"$(TESTS)\"" > $@
+ echo "$(TESTS_ENVIRONMENT)" >> $@
+ echo "$(LOG_COMPILER)" >> $@
+
+.NOTPARALLEL:
diff --git a/test/validation/api/Makefile.inc b/test/validation/api/Makefile.inc
new file mode 100644
index 000000000..8610b5687
--- /dev/null
+++ b/test/validation/api/Makefile.inc
@@ -0,0 +1,3 @@
+include $(top_srcdir)/test/Makefile.inc
+
+PRELDADD += $(LIBCUNIT_COMMON)
diff --git a/test/validation/api/README b/test/validation/api/README
new file mode 100644
index 000000000..665bb7896
--- /dev/null
+++ b/test/validation/api/README
@@ -0,0 +1,32 @@
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2015-2018 Linaro Limited
+
+To add tests in here, please observe the rules listed below. This list
+is a brief overview, for a more detailed explanation of the test
+framework refer to the ODP Implementers' Guide, which can built as
+follows:
+
+ ./configure --enable-user-guides
+ make
+
+Output will be in doc/output/. If this fails, check the documentation
+section of the DEPENDENCIES file.
+
+Rules for all tests under this tree:
+
+1. Tests must be placed in the directory of the module they belong to.
+
+2. Tests must be platform agnostic, i.e.
+
+ - should be written in plain C only.
+ - may only use C standard library functions, CUnit functions and of
+ course ODP functions
+ - should be expected to pass on all ODP implementations
+
+ Tests that do not follow these rules should be placed in the platform
+ specific test area (currently platform/<platform>/test/).
+
+3. If a new ODP API module is created, please update the Makefile.am.
+
+4. Symbols exported from test libraries must respect the naming
+ convention detailed in the ODP Implementers' Guide.
diff --git a/test/validation/api/align/.gitignore b/test/validation/api/align/.gitignore
new file mode 100644
index 000000000..3031151a1
--- /dev/null
+++ b/test/validation/api/align/.gitignore
@@ -0,0 +1 @@
+align_main
diff --git a/test/validation/api/align/Makefile.am b/test/validation/api/align/Makefile.am
new file mode 100644
index 000000000..dd6e36bcd
--- /dev/null
+++ b/test/validation/api/align/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = align_main
+align_main_SOURCES = align.c
diff --git a/test/validation/api/align/align.c b/test/validation/api/align/align.c
new file mode 100644
index 000000000..ddbd1a2fe
--- /dev/null
+++ b/test/validation/api/align/align.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Test struct without gaps */
+typedef struct ODP_PACKED {
+ uint8_t a;
+ uint8_t b;
+ uint16_t c;
+ uint32_t d;
+} test_type_t;
+
+/* Test struct with gaps */
+typedef struct ODP_PACKED {
+ uint8_t a;
+ uint16_t b;
+ uint8_t c;
+ uint32_t d;
+} test_type_2_t;
+
+static void test_aligned(void)
+{
+ uint8_t align_2 ODP_ALIGNED(2);
+ uint16_t align_4 ODP_ALIGNED(4);
+ uint32_t align_8 ODP_ALIGNED(8);
+ uint64_t align_16 ODP_ALIGNED(16);
+
+ CU_ASSERT((uintptr_t)&align_2 % 2 == 0);
+ CU_ASSERT((uintptr_t)&align_4 % 4 == 0);
+ CU_ASSERT((uintptr_t)&align_8 % 8 == 0);
+ CU_ASSERT((uintptr_t)&align_16 % 16 == 0);
+}
+
+static void test_packed(void)
+{
+ uint32_t offset;
+
+ offset = 0;
+ CU_ASSERT(offsetof(test_type_t, a) == offset);
+
+ offset += sizeof(uint8_t);
+ CU_ASSERT(offsetof(test_type_t, b) == offset);
+
+ offset += sizeof(uint8_t);
+ CU_ASSERT(offsetof(test_type_t, c) == offset);
+
+ offset += sizeof(uint16_t);
+ CU_ASSERT(offsetof(test_type_t, d) == offset);
+
+ offset = 0;
+ CU_ASSERT(offsetof(test_type_2_t, a) == offset);
+
+ offset += sizeof(uint8_t);
+ CU_ASSERT(offsetof(test_type_2_t, b) == offset);
+
+ offset += sizeof(uint16_t);
+ CU_ASSERT(offsetof(test_type_2_t, c) == offset);
+
+ offset += sizeof(uint8_t);
+ CU_ASSERT(offsetof(test_type_2_t, d) == offset);
+}
+
+static void test_offsetof(void)
+{
+ CU_ASSERT(ODP_OFFSETOF(test_type_t, a) == offsetof(test_type_t, a));
+ CU_ASSERT(ODP_OFFSETOF(test_type_t, b) == offsetof(test_type_t, b));
+ CU_ASSERT(ODP_OFFSETOF(test_type_t, c) == offsetof(test_type_t, c));
+ CU_ASSERT(ODP_OFFSETOF(test_type_t, d) == offsetof(test_type_t, d));
+ CU_ASSERT(ODP_OFFSETOF(test_type_2_t, a) == offsetof(test_type_2_t, a));
+ CU_ASSERT(ODP_OFFSETOF(test_type_2_t, b) == offsetof(test_type_2_t, b));
+ CU_ASSERT(ODP_OFFSETOF(test_type_2_t, c) == offsetof(test_type_2_t, c));
+ CU_ASSERT(ODP_OFFSETOF(test_type_2_t, d) == offsetof(test_type_2_t, d));
+}
+
+static void test_field_sizeof(void)
+{
+ test_type_t tt;
+
+ CU_ASSERT(ODP_FIELD_SIZEOF(test_type_t, a) == sizeof(tt.a));
+ CU_ASSERT(ODP_FIELD_SIZEOF(test_type_t, b) == sizeof(tt.b));
+ CU_ASSERT(ODP_FIELD_SIZEOF(test_type_t, c) == sizeof(tt.c));
+ CU_ASSERT(ODP_FIELD_SIZEOF(test_type_t, d) == sizeof(tt.d));
+}
+
+static void test_cache_line_size(void)
+{
+ CU_ASSERT(ODP_CACHE_LINE_SIZE > 0);
+ CU_ASSERT(ODP_CACHE_LINE_SIZE % 2 == 0);
+}
+
+static void test_page_size(void)
+{
+ CU_ASSERT(ODP_PAGE_SIZE > 0);
+ CU_ASSERT(ODP_PAGE_SIZE % 2 == 0);
+}
+
+static void test_aligned_cache(void)
+{
+ uint8_t arr[123] ODP_ALIGNED_CACHE;
+
+ CU_ASSERT((uintptr_t)arr % ODP_CACHE_LINE_SIZE == 0);
+}
+
+static void test_aligned_page(void)
+{
+ uint8_t arr[123] ODP_ALIGNED_PAGE;
+
+ CU_ASSERT((uintptr_t)arr % ODP_PAGE_SIZE == 0);
+}
+
+static void test_cache_line_roundup(void)
+{
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(123) % ODP_CACHE_LINE_SIZE == 0);
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(ODP_CACHE_LINE_SIZE) == ODP_CACHE_LINE_SIZE);
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(0) == 0);
+}
+
+odp_testinfo_t align_suite[] = {
+ ODP_TEST_INFO(test_aligned),
+ ODP_TEST_INFO(test_packed),
+ ODP_TEST_INFO(test_offsetof),
+ ODP_TEST_INFO(test_field_sizeof),
+ ODP_TEST_INFO(test_cache_line_size),
+ ODP_TEST_INFO(test_page_size),
+ ODP_TEST_INFO(test_aligned_cache),
+ ODP_TEST_INFO(test_aligned_page),
+ ODP_TEST_INFO(test_cache_line_roundup),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t align_suites[] = {
+ {"align", NULL, NULL, align_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* Parse common options */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(align_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/atomic/.gitignore b/test/validation/api/atomic/.gitignore
index 610ffeab0..610ffeab0 100644
--- a/test/common_plat/validation/api/atomic/.gitignore
+++ b/test/validation/api/atomic/.gitignore
diff --git a/test/validation/api/atomic/Makefile.am b/test/validation/api/atomic/Makefile.am
new file mode 100644
index 000000000..41ad2e6b6
--- /dev/null
+++ b/test/validation/api/atomic/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = atomic_main
+atomic_main_SOURCES = atomic.c
diff --git a/test/validation/api/atomic/atomic.c b/test/validation/api/atomic/atomic.c
new file mode 100644
index 000000000..8ae541fe4
--- /dev/null
+++ b/test/validation/api/atomic/atomic.c
@@ -0,0 +1,1715 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <malloc.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include <unistd.h>
+
+#define MAX_WORKERS 32
+
+#define ADD_SUB_CNT 5
+
+#define CNT 100000ULL
+#define U32_INIT_VAL (1UL << 31)
+#define U64_INIT_VAL (1ULL << 63)
+#define U32_MAGIC 0xa23f65b2
+#define U64_MAGIC 0xf2e1c5430cb6a52e
+
+#define GLOBAL_SHM_NAME "GlobalLockTest"
+
+#define UNUSED __attribute__((__unused__))
+
+typedef __volatile uint32_t volatile_u32_t;
+typedef __volatile uint64_t volatile_u64_t;
+
+typedef struct {
+ odp_atomic_u128_t a128u;
+ odp_atomic_u64_t a64u;
+ odp_atomic_u64_t a64u_min;
+ odp_atomic_u64_t a64u_max;
+ odp_atomic_u64_t a64u_xchg;
+ odp_atomic_u32_t a32u;
+ odp_atomic_u32_t a32u_min;
+ odp_atomic_u32_t a32u_max;
+ odp_atomic_u32_t a32u_xchg;
+
+ uint32_t g_num_threads;
+
+ odp_barrier_t global_barrier;
+} global_shared_mem_t;
+
+static odp_shm_t global_shm;
+static global_shared_mem_t *global_mem;
+
+/* Initialise per-thread memory */
+static void thread_init(void)
+{
+ global_shared_mem_t *global_mem;
+ odp_shm_t global_shm;
+
+ global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ global_mem = odp_shm_addr(global_shm);
+ CU_ASSERT(global_mem != NULL);
+}
+
+static void test_atomic_inc_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_inc_u32(&global_mem->a32u);
+}
+
+static void test_atomic_inc_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_inc_u64(&global_mem->a64u);
+}
+
+static void test_atomic_dec_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_dec_u32(&global_mem->a32u);
+}
+
+static void test_atomic_dec_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_dec_u64(&global_mem->a64u);
+}
+
+static void test_atomic_fetch_inc_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_inc_u32(&global_mem->a32u);
+}
+
+static void test_atomic_fetch_inc_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_inc_u64(&global_mem->a64u);
+}
+
+static void test_atomic_fetch_dec_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_dec_u32(&global_mem->a32u);
+}
+
+static void test_atomic_fetch_dec_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_dec_u64(&global_mem->a64u);
+}
+
+static void test_atomic_add_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_add_u32(&global_mem->a32u, ADD_SUB_CNT);
+}
+
+static void test_atomic_add_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_add_u64(&global_mem->a64u, ADD_SUB_CNT);
+}
+
+static void test_atomic_sub_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_sub_u32(&global_mem->a32u, ADD_SUB_CNT);
+}
+
+static void test_atomic_sub_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_sub_u64(&global_mem->a64u, ADD_SUB_CNT);
+}
+
+static void test_atomic_fetch_add_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_add_u32(&global_mem->a32u, ADD_SUB_CNT);
+}
+
+static void test_atomic_fetch_add_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_add_u64(&global_mem->a64u, ADD_SUB_CNT);
+}
+
+static void test_atomic_fetch_sub_32(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_sub_u32(&global_mem->a32u, ADD_SUB_CNT);
+}
+
+static void test_atomic_fetch_sub_64(void)
+{
+ uint64_t i;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++)
+ odp_atomic_fetch_sub_u64(&global_mem->a64u, ADD_SUB_CNT);
+}
+
+static void test_atomic_min_32(void)
+{
+ uint64_t i;
+ uint32_t tmp;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_fetch_dec_u32(&global_mem->a32u);
+ odp_atomic_min_u32(&global_mem->a32u_min, tmp);
+ }
+}
+
+static void test_atomic_min_64(void)
+{
+ uint64_t i, tmp;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_fetch_dec_u64(&global_mem->a64u);
+ odp_atomic_min_u64(&global_mem->a64u_min, tmp);
+ }
+}
+
+static void test_atomic_max_32(void)
+{
+ uint64_t i;
+ uint32_t tmp;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_fetch_inc_u32(&global_mem->a32u);
+ odp_atomic_max_u32(&global_mem->a32u_max, tmp);
+ }
+}
+
+static void test_atomic_max_64(void)
+{
+ uint64_t i, tmp;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_fetch_inc_u64(&global_mem->a64u);
+ odp_atomic_max_u64(&global_mem->a64u_max, tmp);
+ }
+}
+
+static void test_atomic_cas_inc_32(void)
+{
+ uint64_t i, old_mismatch = 0;
+ uint32_t old, old_old;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u32(a32u);
+ old_old = old;
+
+ while (odp_atomic_cas_u32(a32u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_acq_inc_32(void)
+{
+ uint64_t i, old_mismatch = 0;
+ uint32_t old, old_old;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u32(a32u);
+ old_old = old;
+
+ while (odp_atomic_cas_acq_u32(a32u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_rel_inc_32(void)
+{
+ uint64_t i, old_mismatch = 0;
+ uint32_t old, old_old;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u32(a32u);
+ old_old = old;
+
+ while (odp_atomic_cas_rel_u32(a32u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_acq_rel_inc_32(void)
+{
+ uint64_t i, old_mismatch = 0;
+ uint32_t old, old_old;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u32(a32u);
+ old_old = old;
+
+ while (odp_atomic_cas_acq_rel_u32(a32u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_dec_32(void)
+{
+ uint64_t i;
+ uint32_t old;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u32(a32u);
+
+ while (odp_atomic_cas_u32(a32u, &old, old - 1) == 0)
+ ;
+ }
+}
+
+static void test_atomic_cas_inc_64(void)
+{
+ uint64_t i, old, old_old, old_mismatch = 0;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u64(a64u);
+ old_old = old;
+
+ while (odp_atomic_cas_u64(a64u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_acq_inc_64(void)
+{
+ uint64_t i, old, old_old, old_mismatch = 0;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u64(a64u);
+ old_old = old;
+
+ while (odp_atomic_cas_acq_u64(a64u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_rel_inc_64(void)
+{
+ uint64_t i, old, old_old, old_mismatch = 0;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u64(a64u);
+ old_old = old;
+
+ while (odp_atomic_cas_rel_u64(a64u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_acq_rel_inc_64(void)
+{
+ uint64_t i, old, old_old, old_mismatch = 0;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u64(a64u);
+ old_old = old;
+
+ while (odp_atomic_cas_acq_rel_u64(a64u, &old, old + 1) == 0) {
+ if (old == old_old)
+ old_mismatch++;
+
+ old_old = old;
+ }
+
+ if (old != old_old)
+ old_mismatch++;
+ }
+
+ CU_ASSERT(old_mismatch == 0);
+}
+
+static void test_atomic_cas_dec_64(void)
+{
+ uint64_t i, old;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u64(a64u);
+
+ while (odp_atomic_cas_u64(a64u, &old, old - 1) == 0)
+ ;
+ }
+}
+
+#define BUF_SIZE (64 * 1024)
+
+static void test_atomic_xchg_32(void)
+{
+ uint32_t old, new;
+ uint64_t i;
+ odp_atomic_u32_t *a32u_xchg = &global_mem->a32u_xchg;
+ uint8_t buf[BUF_SIZE];
+ uint64_t seed = odp_thread_id();
+ uint64_t count_old = 0, count_new = 0;
+
+ odp_random_test_data(buf, BUF_SIZE, &seed);
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ new = buf[i & (BUF_SIZE - 1)];
+ old = odp_atomic_xchg_u32(a32u_xchg, new);
+ count_old += old;
+ count_new += new;
+ }
+
+ odp_atomic_add_u32(a32u_xchg, count_old);
+ odp_atomic_sub_u32(a32u_xchg, count_new);
+}
+
+static void test_atomic_xchg_64(void)
+{
+ uint64_t old, new;
+ uint64_t i;
+ odp_atomic_u64_t *a64u_xchg = &global_mem->a64u_xchg;
+ uint8_t buf[BUF_SIZE];
+ uint64_t seed = odp_thread_id();
+ uint64_t count_old = 0, count_new = 0;
+
+ odp_random_test_data(buf, BUF_SIZE, &seed);
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ new = buf[i & (BUF_SIZE - 1)];
+ old = odp_atomic_xchg_u64(a64u_xchg, new);
+ count_old += old;
+ count_new += new;
+ }
+
+ odp_atomic_add_u64(a64u_xchg, count_old);
+ odp_atomic_sub_u64(a64u_xchg, count_new);
+}
+
+static void test_atomic_non_relaxed_32(void)
+{
+ uint64_t i;
+ uint32_t tmp;
+ odp_atomic_u32_t *a32u = &global_mem->a32u;
+ odp_atomic_u32_t *a32u_min = &global_mem->a32u_min;
+ odp_atomic_u32_t *a32u_max = &global_mem->a32u_max;
+ odp_atomic_u32_t *a32u_xchg = &global_mem->a32u_xchg;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_load_acq_u32(a32u);
+ odp_atomic_store_rel_u32(a32u, tmp);
+
+ tmp = odp_atomic_load_acq_u32(a32u_max);
+ odp_atomic_add_rel_u32(a32u_max, 1);
+
+ tmp = odp_atomic_load_acq_u32(a32u_min);
+ odp_atomic_sub_rel_u32(a32u_min, 1);
+
+ tmp = odp_atomic_load_u32(a32u_xchg);
+ while (odp_atomic_cas_acq_u32(a32u_xchg, &tmp, tmp + 1) == 0)
+ ;
+
+ tmp = odp_atomic_load_u32(a32u_xchg);
+ while (odp_atomic_cas_rel_u32(a32u_xchg, &tmp, tmp + 1) == 0)
+ ;
+
+ tmp = odp_atomic_load_u32(a32u_xchg);
+ while (odp_atomic_cas_acq_rel_u32(a32u_xchg, &tmp, tmp + 1) == 0)
+ ;
+ }
+}
+
+static void test_atomic_non_relaxed_64(void)
+{
+ uint64_t i, tmp;
+ odp_atomic_u64_t *a64u = &global_mem->a64u;
+ odp_atomic_u64_t *a64u_min = &global_mem->a64u_min;
+ odp_atomic_u64_t *a64u_max = &global_mem->a64u_max;
+ odp_atomic_u64_t *a64u_xchg = &global_mem->a64u_xchg;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ tmp = odp_atomic_load_acq_u64(a64u);
+ odp_atomic_store_rel_u64(a64u, tmp);
+
+ tmp = odp_atomic_load_acq_u64(a64u_max);
+ odp_atomic_add_rel_u64(a64u_max, 1);
+
+ tmp = odp_atomic_load_acq_u64(a64u_min);
+ odp_atomic_sub_rel_u64(a64u_min, 1);
+
+ tmp = odp_atomic_load_u64(a64u_xchg);
+ while (odp_atomic_cas_acq_u64(a64u_xchg, &tmp, tmp + 1) == 0)
+ ;
+
+ tmp = odp_atomic_load_u64(a64u_xchg);
+ while (odp_atomic_cas_rel_u64(a64u_xchg, &tmp, tmp + 1) == 0)
+ ;
+
+ tmp = odp_atomic_load_u64(a64u_xchg);
+ while (odp_atomic_cas_acq_rel_u64(a64u_xchg, &tmp, tmp + 1) == 0)
+ ;
+ }
+}
+
+static void test_atomic_relaxed_128(void)
+{
+ int ret;
+ uint64_t i;
+ odp_u128_t old, new;
+ odp_atomic_u128_t *a128u = &global_mem->a128u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u128(a128u);
+
+ do {
+ new.u64[0] = old.u64[0] + 2;
+ new.u64[1] = old.u64[1] + 1;
+
+ ret = odp_atomic_cas_u128(a128u, &old, new);
+
+ } while (ret == 0);
+ }
+}
+
+static void test_atomic_non_relaxed_128_acq(void)
+{
+ int ret;
+ uint64_t i;
+ odp_u128_t old, new;
+ odp_atomic_u128_t *a128u = &global_mem->a128u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u128(a128u);
+
+ do {
+ new.u64[0] = old.u64[0] + 2;
+ new.u64[1] = old.u64[1] + 1;
+
+ ret = odp_atomic_cas_acq_u128(a128u, &old, new);
+
+ } while (ret == 0);
+ }
+}
+
+static void test_atomic_non_relaxed_128_rel(void)
+{
+ int ret;
+ uint64_t i;
+ odp_u128_t old, new;
+ odp_atomic_u128_t *a128u = &global_mem->a128u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u128(a128u);
+
+ do {
+ new.u64[0] = old.u64[0] + 2;
+ new.u64[1] = old.u64[1] + 1;
+
+ ret = odp_atomic_cas_rel_u128(a128u, &old, new);
+
+ } while (ret == 0);
+ }
+}
+
+static void test_atomic_non_relaxed_128_acq_rel(void)
+{
+ int ret;
+ uint64_t i;
+ odp_u128_t old, new;
+ odp_atomic_u128_t *a128u = &global_mem->a128u;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ for (i = 0; i < CNT; i++) {
+ old = odp_atomic_load_u128(a128u);
+
+ do {
+ new.u64[0] = old.u64[0] + 2;
+ new.u64[1] = old.u64[1] + 1;
+
+ ret = odp_atomic_cas_acq_rel_u128(a128u, &old, new);
+
+ } while (ret == 0);
+ }
+}
+
+static void test_atomic_inc_dec_32(void)
+{
+ test_atomic_inc_32();
+ test_atomic_dec_32();
+}
+
+static void test_atomic_inc_dec_64(void)
+{
+ test_atomic_inc_64();
+ test_atomic_dec_64();
+}
+
+static void test_atomic_fetch_inc_dec_32(void)
+{
+ test_atomic_fetch_inc_32();
+ test_atomic_fetch_dec_32();
+}
+
+static void test_atomic_fetch_inc_dec_64(void)
+{
+ test_atomic_fetch_inc_64();
+ test_atomic_fetch_dec_64();
+}
+
+static void test_atomic_add_sub_32(void)
+{
+ test_atomic_add_32();
+ test_atomic_sub_32();
+}
+
+static void test_atomic_add_sub_64(void)
+{
+ test_atomic_add_64();
+ test_atomic_sub_64();
+}
+
+static void test_atomic_fetch_add_sub_32(void)
+{
+ test_atomic_fetch_add_32();
+ test_atomic_fetch_sub_32();
+}
+
+static void test_atomic_fetch_add_sub_64(void)
+{
+ test_atomic_fetch_add_64();
+ test_atomic_fetch_sub_64();
+}
+
+static void test_atomic_inc_add_32(void)
+{
+ test_atomic_inc_32();
+ test_atomic_fetch_inc_32();
+ test_atomic_add_32();
+ test_atomic_fetch_add_32();
+ test_atomic_cas_inc_32();
+}
+
+static void test_atomic_inc_add_64(void)
+{
+ test_atomic_inc_64();
+ test_atomic_fetch_inc_64();
+ test_atomic_add_64();
+ test_atomic_fetch_add_64();
+ test_atomic_cas_inc_64();
+}
+
+static void test_atomic_dec_sub_32(void)
+{
+ test_atomic_dec_32();
+ test_atomic_fetch_dec_32();
+ test_atomic_sub_32();
+ test_atomic_fetch_sub_32();
+ test_atomic_cas_dec_32();
+}
+
+static void test_atomic_dec_sub_64(void)
+{
+ test_atomic_dec_64();
+ test_atomic_fetch_dec_64();
+ test_atomic_sub_64();
+ test_atomic_fetch_sub_64();
+ test_atomic_cas_dec_64();
+}
+
+static void test_atomic_max_min_32(void)
+{
+ test_atomic_max_32();
+ test_atomic_min_32();
+}
+
+static void test_atomic_max_min_64(void)
+{
+ test_atomic_max_64();
+ test_atomic_min_64();
+}
+
+static void test_atomic_cas_inc_dec_32(void)
+{
+ test_atomic_cas_inc_32();
+ test_atomic_cas_dec_32();
+}
+
+static void test_atomic_cas_inc_dec_64(void)
+{
+ test_atomic_cas_inc_64();
+ test_atomic_cas_dec_64();
+}
+
+static void test_atomic_cas_inc_128(void)
+{
+ test_atomic_relaxed_128();
+ test_atomic_non_relaxed_128_acq();
+ test_atomic_non_relaxed_128_rel();
+ test_atomic_non_relaxed_128_acq_rel();
+}
+
+static void test_atomic_init(void)
+{
+ odp_atomic_init_u32(&global_mem->a32u, 0);
+ odp_atomic_init_u64(&global_mem->a64u, 0);
+ odp_atomic_init_u32(&global_mem->a32u_min, 0);
+ odp_atomic_init_u32(&global_mem->a32u_max, 0);
+ odp_atomic_init_u64(&global_mem->a64u_min, 0);
+ odp_atomic_init_u64(&global_mem->a64u_max, 0);
+ odp_atomic_init_u32(&global_mem->a32u_xchg, 0);
+ odp_atomic_init_u64(&global_mem->a64u_xchg, 0);
+
+ odp_u128_t a128u_tmp;
+
+ a128u_tmp.u64[0] = 0;
+ a128u_tmp.u64[1] = 0;
+ odp_atomic_init_u128(&global_mem->a128u, a128u_tmp);
+}
+
+static void test_atomic_store(void)
+{
+ odp_atomic_store_u32(&global_mem->a32u, U32_INIT_VAL);
+ odp_atomic_store_u64(&global_mem->a64u, U64_INIT_VAL);
+ odp_atomic_store_u32(&global_mem->a32u_min, U32_INIT_VAL);
+ odp_atomic_store_u32(&global_mem->a32u_max, U32_INIT_VAL);
+ odp_atomic_store_u64(&global_mem->a64u_min, U64_INIT_VAL);
+ odp_atomic_store_u64(&global_mem->a64u_max, U64_INIT_VAL);
+ odp_atomic_store_u32(&global_mem->a32u_xchg, U32_INIT_VAL);
+ odp_atomic_store_u64(&global_mem->a64u_xchg, U64_INIT_VAL);
+
+ odp_u128_t a128u_tmp;
+
+ a128u_tmp.u64[0] = U64_INIT_VAL;
+ a128u_tmp.u64[1] = U64_INIT_VAL;
+ odp_atomic_store_u128(&global_mem->a128u, a128u_tmp);
+}
+
+static void test_atomic_validate_init_val_32_64(void)
+{
+ CU_ASSERT(U32_INIT_VAL == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void test_atomic_validate_init_val_128(void)
+{
+ odp_u128_t a128u = odp_atomic_load_u128(&global_mem->a128u);
+
+ CU_ASSERT(U64_INIT_VAL == a128u.u64[0]);
+ CU_ASSERT(U64_INIT_VAL == a128u.u64[1]);
+}
+
+static void test_atomic_validate_init_val(void)
+{
+ test_atomic_validate_init_val_32_64();
+ test_atomic_validate_init_val_128();
+}
+
+static void test_atomic_validate_inc_add(void)
+{
+ test_atomic_validate_init_val_128();
+
+ /* Two increment tests, one cas increment test and two add tests. */
+ const uint64_t total_count = CNT * (3 + 2 * ADD_SUB_CNT) * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL + total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL + total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void test_atomic_validate_dec_sub(void)
+{
+ test_atomic_validate_init_val_128();
+
+ /* Two decrement tests, one cas decrement test and two sub tests. */
+ const uint64_t total_count = CNT * (3 + 2 * ADD_SUB_CNT) * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL - total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL - total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void test_atomic_validate_cas_inc_dec(void)
+{
+ test_atomic_validate_init_val_32_64();
+
+ odp_u128_t a128u = odp_atomic_load_u128(&global_mem->a128u);
+ const uint64_t iterations = a128u.u64[0] - a128u.u64[1];
+
+ CU_ASSERT(iterations == 4 * CNT * global_mem->g_num_threads);
+}
+
+static void test_atomic_validate_max_min(void)
+{
+ test_atomic_validate_init_val();
+
+ const uint64_t total_count = CNT * global_mem->g_num_threads;
+ /*
+ * Max is the result of fetch_inc, so the final max value is total_count - 1. In
+ * a long test, counter may overflow, in which case max is saturated at
+ * UINT32_MAX, and min at 0.
+ */
+ const uint32_t a32u_max = ODPH_MIN(U32_INIT_VAL + total_count - 1, UINT32_MAX);
+ const uint32_t a32u_min = U32_INIT_VAL + total_count - 1 > UINT32_MAX ? 0 : U32_INIT_VAL;
+
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_max) == a32u_max);
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_min) == a32u_min);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_max) == U64_INIT_VAL + total_count - 1);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_min) == U64_INIT_VAL);
+}
+
+static void test_atomic_validate_xchg(void)
+{
+ test_atomic_validate_init_val();
+
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_xchg) == U32_INIT_VAL);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_xchg) == U64_INIT_VAL);
+}
+
+static void test_atomic_validate_non_relaxed(void)
+{
+ test_atomic_validate_init_val();
+
+ const uint64_t total_count = CNT * global_mem->g_num_threads;
+ /* 3 increments per round. */
+ const uint32_t a32u = U32_INIT_VAL + 3 * total_count;
+ /* 1 increment per round. */
+ const uint32_t a32u_max = U32_INIT_VAL + total_count;
+ const uint32_t a32u_min = U32_INIT_VAL - total_count;
+
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_xchg) == a32u);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_xchg) == U64_INIT_VAL + 3 * total_count);
+
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_max) == a32u_max);
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_min) == a32u_min);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_max) == U64_INIT_VAL + total_count);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_min) == U64_INIT_VAL - total_count);
+}
+
+static int atomic_init(odp_instance_t *inst)
+{
+ uint32_t workers_count, max_threads;
+ int ret = 0;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(global_shared_mem_t), 64, 0);
+ if (ODP_SHM_INVALID == global_shm) {
+ ODPH_ERR("Unable to reserve memory for global_shm\n");
+ return -1;
+ }
+
+ global_mem = odp_shm_addr(global_shm);
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ global_mem->g_num_threads = MAX_WORKERS;
+
+ workers_count = odp_cpumask_default_worker(NULL, 0);
+
+ max_threads = (workers_count >= MAX_WORKERS) ?
+ MAX_WORKERS : workers_count;
+
+ if (max_threads < global_mem->g_num_threads) {
+ printf("Requested num of threads is too large\n");
+ printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
+ global_mem->g_num_threads,
+ max_threads);
+ global_mem->g_num_threads = max_threads;
+ }
+
+ printf("Num of threads used = %" PRIu32 "\n",
+ global_mem->g_num_threads);
+
+ odp_barrier_init(&global_mem->global_barrier, global_mem->g_num_threads);
+
+ return ret;
+}
+
+static int atomic_term(odp_instance_t inst)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ if (0 != odp_shm_free(shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Atomic tests */
+
+static int test_atomic_inc_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_inc_32();
+ test_atomic_inc_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_dec_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_dec_32();
+ test_atomic_dec_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_add_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_add_32();
+ test_atomic_add_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_sub_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_sub_32();
+ test_atomic_sub_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_inc_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_inc_32();
+ test_atomic_fetch_inc_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_dec_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_dec_32();
+ test_atomic_fetch_dec_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_add_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_add_32();
+ test_atomic_fetch_add_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_sub_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_sub_32();
+ test_atomic_fetch_sub_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_max_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_max_32();
+ test_atomic_max_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_min_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_min_32();
+ test_atomic_min_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_cas_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_cas_inc_32();
+ test_atomic_cas_inc_64();
+ test_atomic_relaxed_128();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_cas_acq_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_cas_acq_inc_32();
+ test_atomic_cas_acq_inc_64();
+ test_atomic_non_relaxed_128_acq();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_cas_rel_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_cas_rel_inc_32();
+ test_atomic_cas_rel_inc_64();
+ test_atomic_non_relaxed_128_rel();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_cas_acq_rel_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_cas_acq_rel_inc_32();
+ test_atomic_cas_acq_rel_inc_64();
+ test_atomic_non_relaxed_128_acq_rel();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_inc_dec_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_inc_dec_32();
+ test_atomic_inc_dec_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_add_sub_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_add_sub_32();
+ test_atomic_add_sub_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_inc_dec_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_inc_dec_32();
+ test_atomic_fetch_inc_dec_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_fetch_add_sub_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_fetch_add_sub_32();
+ test_atomic_fetch_add_sub_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_inc_add_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_inc_add_32();
+ test_atomic_inc_add_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_dec_sub_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_dec_sub_32();
+ test_atomic_dec_sub_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_max_min_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_max_min_32();
+ test_atomic_max_min_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_cas_inc_dec_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_cas_inc_dec_32();
+ test_atomic_cas_inc_dec_64();
+ test_atomic_cas_inc_128();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_xchg_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_xchg_32();
+ test_atomic_xchg_64();
+
+ return CU_get_number_of_failures();
+}
+
+static int test_atomic_non_relaxed_thread(void *arg UNUSED)
+{
+ thread_init();
+ test_atomic_non_relaxed_32();
+ test_atomic_non_relaxed_64();
+
+ return CU_get_number_of_failures();
+}
+
+static void test_atomic_functional(int test_fn(void *), void validate_fn(void))
+{
+ int num = global_mem->g_num_threads;
+
+ test_atomic_init();
+ test_atomic_store();
+ odp_cunit_thread_create(num, test_fn, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+ validate_fn();
+}
+
+static void test_atomic_op_lock_free_set(void)
+{
+ odp_atomic_op_t atomic_op;
+
+ memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t));
+ atomic_op.all_bits = 0;
+
+ CU_ASSERT(atomic_op.all_bits == 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ CU_ASSERT(atomic_op.op.load == 0);
+ CU_ASSERT(atomic_op.op.store == 0);
+ CU_ASSERT(atomic_op.op.fetch_add == 0);
+ CU_ASSERT(atomic_op.op.add == 0);
+ CU_ASSERT(atomic_op.op.fetch_sub == 0);
+ CU_ASSERT(atomic_op.op.sub == 0);
+ CU_ASSERT(atomic_op.op.fetch_inc == 0);
+ CU_ASSERT(atomic_op.op.inc == 0);
+ CU_ASSERT(atomic_op.op.fetch_dec == 0);
+ CU_ASSERT(atomic_op.op.dec == 0);
+ CU_ASSERT(atomic_op.op.min == 0);
+ CU_ASSERT(atomic_op.op.max == 0);
+ CU_ASSERT(atomic_op.op.cas == 0);
+ CU_ASSERT(atomic_op.op.xchg == 0);
+
+ /* Test setting first, last and couple of other bits */
+ atomic_op.op.init = 1;
+ CU_ASSERT(atomic_op.op.init == 1);
+ CU_ASSERT(atomic_op.all_bits != 0);
+ atomic_op.op.init = 0;
+ CU_ASSERT(atomic_op.all_bits == 0);
+
+ atomic_op.op.xchg = 1;
+ CU_ASSERT(atomic_op.op.xchg == 1);
+ CU_ASSERT(atomic_op.all_bits != 0);
+ atomic_op.op.xchg = 0;
+ CU_ASSERT(atomic_op.all_bits == 0);
+
+ atomic_op.op.add = 1;
+ CU_ASSERT(atomic_op.op.add == 1);
+ CU_ASSERT(atomic_op.all_bits != 0);
+ atomic_op.op.add = 0;
+ CU_ASSERT(atomic_op.all_bits == 0);
+
+ atomic_op.op.dec = 1;
+ CU_ASSERT(atomic_op.op.dec == 1);
+ CU_ASSERT(atomic_op.all_bits != 0);
+ atomic_op.op.dec = 0;
+ CU_ASSERT(atomic_op.all_bits == 0);
+}
+
+static void test_atomic_op_lock_free_64(void)
+{
+ odp_atomic_op_t atomic_op;
+ int ret_null, ret;
+
+ memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t));
+ ret = odp_atomic_lock_free_u64(&atomic_op);
+ ret_null = odp_atomic_lock_free_u64(NULL);
+
+ CU_ASSERT(ret == ret_null);
+
+ /* Init operation is not atomic by the spec. Call to
+ * odp_atomic_lock_free_u64() zeros it but never sets it. */
+
+ if (ret == 0) {
+ /* none are lock free */
+ CU_ASSERT(atomic_op.all_bits == 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ CU_ASSERT(atomic_op.op.load == 0);
+ CU_ASSERT(atomic_op.op.store == 0);
+ CU_ASSERT(atomic_op.op.fetch_add == 0);
+ CU_ASSERT(atomic_op.op.add == 0);
+ CU_ASSERT(atomic_op.op.fetch_sub == 0);
+ CU_ASSERT(atomic_op.op.sub == 0);
+ CU_ASSERT(atomic_op.op.fetch_inc == 0);
+ CU_ASSERT(atomic_op.op.inc == 0);
+ CU_ASSERT(atomic_op.op.fetch_dec == 0);
+ CU_ASSERT(atomic_op.op.dec == 0);
+ CU_ASSERT(atomic_op.op.min == 0);
+ CU_ASSERT(atomic_op.op.max == 0);
+ CU_ASSERT(atomic_op.op.cas == 0);
+ CU_ASSERT(atomic_op.op.xchg == 0);
+ }
+
+ if (ret == 1) {
+ /* some are lock free */
+ CU_ASSERT(atomic_op.all_bits != 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ }
+
+ if (ret == 2) {
+ /* all are lock free */
+ CU_ASSERT(atomic_op.all_bits != 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ CU_ASSERT(atomic_op.op.load == 1);
+ CU_ASSERT(atomic_op.op.store == 1);
+ CU_ASSERT(atomic_op.op.fetch_add == 1);
+ CU_ASSERT(atomic_op.op.add == 1);
+ CU_ASSERT(atomic_op.op.fetch_sub == 1);
+ CU_ASSERT(atomic_op.op.sub == 1);
+ CU_ASSERT(atomic_op.op.fetch_inc == 1);
+ CU_ASSERT(atomic_op.op.inc == 1);
+ CU_ASSERT(atomic_op.op.fetch_dec == 1);
+ CU_ASSERT(atomic_op.op.dec == 1);
+ CU_ASSERT(atomic_op.op.min == 1);
+ CU_ASSERT(atomic_op.op.max == 1);
+ CU_ASSERT(atomic_op.op.cas == 1);
+ CU_ASSERT(atomic_op.op.xchg == 1);
+ }
+}
+
+static void test_atomic_op_lock_free_128(void)
+{
+ odp_atomic_op_t atomic_op;
+ int ret_null, ret;
+
+ memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t));
+ ret = odp_atomic_lock_free_u128(&atomic_op);
+ ret_null = odp_atomic_lock_free_u128(NULL);
+
+ CU_ASSERT(ret == ret_null);
+
+ /* Init operation is not atomic by the spec. Call to
+ * odp_atomic_lock_free_u128() zeros it but never sets it. */
+
+ if (ret == 0) {
+ /* none are lock free */
+ CU_ASSERT(atomic_op.all_bits == 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ CU_ASSERT(atomic_op.op.load == 0);
+ CU_ASSERT(atomic_op.op.store == 0);
+ CU_ASSERT(atomic_op.op.cas == 0);
+ }
+
+ if (ret == 1) {
+ /* some are lock free */
+ CU_ASSERT(atomic_op.all_bits != 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ }
+
+ if (ret == 2) {
+ /* all are lock free */
+ CU_ASSERT(atomic_op.all_bits != 0);
+ CU_ASSERT(atomic_op.op.init == 0);
+ CU_ASSERT(atomic_op.op.load == 1);
+ CU_ASSERT(atomic_op.op.store == 1);
+ CU_ASSERT(atomic_op.op.cas == 1);
+ }
+}
+
+static void atomic_test_atomic_init(void)
+{
+ uint64_t i;
+ odp_atomic_u128_t *a128u = &global_mem->a128u;
+
+ for (i = 0; i < CNT; i++) {
+ odp_u128_t a128u_tmp;
+
+ odp_atomic_init_u32(&global_mem->a32u, i);
+ odp_atomic_init_u64(&global_mem->a64u, i);
+ odp_atomic_init_u32(&global_mem->a32u_min, i);
+ odp_atomic_init_u32(&global_mem->a32u_max, i);
+ odp_atomic_init_u64(&global_mem->a64u_min, i);
+ odp_atomic_init_u64(&global_mem->a64u_max, i);
+ odp_atomic_init_u32(&global_mem->a32u_xchg, i);
+ odp_atomic_init_u64(&global_mem->a64u_xchg, i);
+
+ a128u_tmp.u64[0] = i;
+ a128u_tmp.u64[1] = i;
+ odp_atomic_init_u128(&global_mem->a128u, a128u_tmp);
+
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u) == i);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u) == i);
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_min) == i);
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_max) == i);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_min) == i);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_max) == i);
+ CU_ASSERT(odp_atomic_load_u32(&global_mem->a32u_xchg) == i);
+ CU_ASSERT(odp_atomic_load_u64(&global_mem->a64u_xchg) == i);
+
+ a128u_tmp = odp_atomic_load_u128(a128u);
+ CU_ASSERT(a128u_tmp.u64[0] == i);
+ CU_ASSERT(a128u_tmp.u64[1] == i);
+ }
+}
+
+static void test_atomic_validate_inc(void)
+{
+ const uint64_t total_count = CNT * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL + total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL + total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void atomic_test_atomic_inc(void)
+{
+ test_atomic_functional(test_atomic_inc_thread, test_atomic_validate_inc);
+}
+
+static void test_atomic_validate_dec(void)
+{
+ const uint64_t total_count = CNT * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL - total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL - total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void atomic_test_atomic_dec(void)
+{
+ test_atomic_functional(test_atomic_dec_thread, test_atomic_validate_dec);
+}
+
+static void test_atomic_validate_add(void)
+{
+ const uint64_t total_count = CNT * ADD_SUB_CNT * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL + total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL + total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void atomic_test_atomic_add(void)
+{
+ test_atomic_functional(test_atomic_add_thread, test_atomic_validate_add);
+}
+
+static void test_atomic_validate_sub(void)
+{
+ const uint64_t total_count = CNT * ADD_SUB_CNT * global_mem->g_num_threads;
+ const uint32_t a32u = U32_INIT_VAL - total_count;
+
+ CU_ASSERT(a32u == odp_atomic_load_u32(&global_mem->a32u));
+ CU_ASSERT(U64_INIT_VAL - total_count == odp_atomic_load_u64(&global_mem->a64u));
+}
+
+static void atomic_test_atomic_sub(void)
+{
+ test_atomic_functional(test_atomic_sub_thread, test_atomic_validate_sub);
+}
+
+static void atomic_test_atomic_fetch_inc(void)
+{
+ test_atomic_functional(test_atomic_fetch_inc_thread, test_atomic_validate_inc);
+}
+
+static void atomic_test_atomic_fetch_dec(void)
+{
+ test_atomic_functional(test_atomic_fetch_dec_thread, test_atomic_validate_dec);
+}
+
+static void atomic_test_atomic_fetch_add(void)
+{
+ test_atomic_functional(test_atomic_fetch_add_thread, test_atomic_validate_add);
+}
+
+static void atomic_test_atomic_fetch_sub(void)
+{
+ test_atomic_functional(test_atomic_fetch_sub_thread, test_atomic_validate_sub);
+}
+
+static void test_atomic_validate_max(void)
+{
+ const uint64_t total_count = CNT * global_mem->g_num_threads - 1;
+ /* In a long test, counter may overflow, in which case max is saturated at UINT32_MAX. */
+ const uint32_t a32u_max = ODPH_MIN(U32_INIT_VAL + total_count, UINT32_MAX);
+
+ CU_ASSERT(a32u_max == odp_atomic_load_u32(&global_mem->a32u_max));
+ CU_ASSERT(U64_INIT_VAL + total_count == odp_atomic_load_u64(&global_mem->a64u_max));
+}
+
+static void atomic_test_atomic_max(void)
+{
+ test_atomic_functional(test_atomic_max_thread, test_atomic_validate_max);
+}
+
+static void test_atomic_validate_min(void)
+{
+ const uint64_t total_count = CNT * global_mem->g_num_threads - 1;
+ /* In a long test, counter may underflow, in which case min is saturated at 0. */
+ const uint32_t a32u_min = ODPH_MAX((int64_t)U32_INIT_VAL - (int64_t)total_count, 0);
+
+ CU_ASSERT(a32u_min == odp_atomic_load_u32(&global_mem->a32u_min));
+ CU_ASSERT(U64_INIT_VAL - total_count == odp_atomic_load_u64(&global_mem->a64u_min));
+}
+
+static void atomic_test_atomic_min(void)
+{
+ test_atomic_functional(test_atomic_min_thread, test_atomic_validate_min);
+}
+
+static void test_atomic_validate_cas_128(void)
+{
+ odp_u128_t a128u = odp_atomic_load_u128(&global_mem->a128u);
+ const uint64_t iterations = a128u.u64[0] - a128u.u64[1];
+
+ CU_ASSERT(iterations == CNT * global_mem->g_num_threads);
+}
+
+static void test_atomic_validate_cas(void)
+{
+ test_atomic_validate_inc();
+ test_atomic_validate_cas_128();
+}
+
+static void atomic_test_atomic_cas(void)
+{
+ test_atomic_functional(test_atomic_cas_thread, test_atomic_validate_cas);
+}
+
+static void atomic_test_atomic_cas_acq(void)
+{
+ test_atomic_functional(test_atomic_cas_acq_thread, test_atomic_validate_cas);
+}
+
+static void atomic_test_atomic_cas_rel(void)
+{
+ test_atomic_functional(test_atomic_cas_rel_thread, test_atomic_validate_cas);
+}
+
+static void atomic_test_atomic_cas_acq_rel(void)
+{
+ test_atomic_functional(test_atomic_cas_acq_rel_thread, test_atomic_validate_cas);
+}
+
+static void atomic_test_atomic_inc_dec(void)
+{
+ test_atomic_functional(test_atomic_inc_dec_thread, test_atomic_validate_init_val);
+}
+
+static void atomic_test_atomic_add_sub(void)
+{
+ test_atomic_functional(test_atomic_add_sub_thread, test_atomic_validate_init_val);
+}
+
+static void atomic_test_atomic_fetch_inc_dec(void)
+{
+ test_atomic_functional(test_atomic_fetch_inc_dec_thread, test_atomic_validate_init_val);
+}
+
+static void atomic_test_atomic_fetch_add_sub(void)
+{
+ test_atomic_functional(test_atomic_fetch_add_sub_thread, test_atomic_validate_init_val);
+}
+
+static void atomic_test_atomic_inc_add(void)
+{
+ test_atomic_functional(test_atomic_inc_add_thread, test_atomic_validate_inc_add);
+}
+
+static void atomic_test_atomic_dec_sub(void)
+{
+ test_atomic_functional(test_atomic_dec_sub_thread, test_atomic_validate_dec_sub);
+}
+
+static void atomic_test_atomic_max_min(void)
+{
+ test_atomic_functional(test_atomic_max_min_thread, test_atomic_validate_max_min);
+}
+
+static void atomic_test_atomic_cas_inc_dec(void)
+{
+ test_atomic_functional(test_atomic_cas_inc_dec_thread, test_atomic_validate_cas_inc_dec);
+}
+
+static void atomic_test_atomic_xchg(void)
+{
+ test_atomic_functional(test_atomic_xchg_thread, test_atomic_validate_xchg);
+}
+
+static void atomic_test_atomic_non_relaxed(void)
+{
+ test_atomic_functional(test_atomic_non_relaxed_thread,
+ test_atomic_validate_non_relaxed);
+}
+
+static void atomic_test_atomic_op_lock_free(void)
+{
+ test_atomic_op_lock_free_set();
+ test_atomic_op_lock_free_64();
+ test_atomic_op_lock_free_128();
+}
+
+odp_testinfo_t atomic_suite_atomic[] = {
+ ODP_TEST_INFO(atomic_test_atomic_init),
+ ODP_TEST_INFO(atomic_test_atomic_inc),
+ ODP_TEST_INFO(atomic_test_atomic_dec),
+ ODP_TEST_INFO(atomic_test_atomic_add),
+ ODP_TEST_INFO(atomic_test_atomic_sub),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_inc),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_dec),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_add),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_sub),
+ ODP_TEST_INFO(atomic_test_atomic_max),
+ ODP_TEST_INFO(atomic_test_atomic_min),
+ ODP_TEST_INFO(atomic_test_atomic_cas),
+ ODP_TEST_INFO(atomic_test_atomic_cas_acq),
+ ODP_TEST_INFO(atomic_test_atomic_cas_rel),
+ ODP_TEST_INFO(atomic_test_atomic_cas_acq_rel),
+ ODP_TEST_INFO(atomic_test_atomic_inc_dec),
+ ODP_TEST_INFO(atomic_test_atomic_add_sub),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_inc_dec),
+ ODP_TEST_INFO(atomic_test_atomic_fetch_add_sub),
+ ODP_TEST_INFO(atomic_test_atomic_inc_add),
+ ODP_TEST_INFO(atomic_test_atomic_dec_sub),
+ ODP_TEST_INFO(atomic_test_atomic_max_min),
+ ODP_TEST_INFO(atomic_test_atomic_cas_inc_dec),
+ ODP_TEST_INFO(atomic_test_atomic_xchg),
+ ODP_TEST_INFO(atomic_test_atomic_non_relaxed),
+ ODP_TEST_INFO(atomic_test_atomic_op_lock_free),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t atomic_suites[] = {
+ {"atomic", NULL, NULL,
+ atomic_suite_atomic},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(atomic_init);
+ odp_cunit_register_global_term(atomic_term);
+
+ ret = odp_cunit_register(atomic_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/barrier/.gitignore b/test/validation/api/barrier/.gitignore
index 2e0ee7ade..2e0ee7ade 100644
--- a/test/common_plat/validation/api/barrier/.gitignore
+++ b/test/validation/api/barrier/.gitignore
diff --git a/test/validation/api/barrier/Makefile.am b/test/validation/api/barrier/Makefile.am
new file mode 100644
index 000000000..f5f751b88
--- /dev/null
+++ b/test/validation/api/barrier/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = barrier_main
+barrier_main_SOURCES = barrier.c
diff --git a/test/validation/api/barrier/barrier.c b/test/validation/api/barrier/barrier.c
new file mode 100644
index 000000000..aaf646e8a
--- /dev/null
+++ b/test/validation/api/barrier/barrier.c
@@ -0,0 +1,457 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <malloc.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include <unistd.h>
+
+#define VERBOSE 0
+#define MAX_WORKERS 32
+#define MAX_ITERATIONS 1000
+#define BARRIER_ITERATIONS 64
+
+#define SLOW_BARRIER_DELAY 400
+#define BASE_DELAY 6
+
+#define NUM_TEST_BARRIERS BARRIER_ITERATIONS
+#define NUM_RESYNC_BARRIERS 100
+
+#define BARRIER_DELAY 10
+
+#define GLOBAL_SHM_NAME "GlobalLockTest"
+
+#define UNUSED __attribute__((__unused__))
+
+static volatile int temp_result;
+
+typedef __volatile uint32_t volatile_u32_t;
+typedef __volatile uint64_t volatile_u64_t;
+
+typedef struct {
+ odp_atomic_u32_t wait_cnt;
+} custom_barrier_t;
+
+typedef struct {
+ /* Global variables */
+ uint32_t g_num_threads;
+ uint32_t g_iterations;
+ uint32_t g_verbose;
+ uint32_t g_max_num_cores;
+
+ odp_barrier_t test_barriers[NUM_TEST_BARRIERS];
+ custom_barrier_t custom_barrier1[NUM_TEST_BARRIERS];
+ custom_barrier_t custom_barrier2[NUM_TEST_BARRIERS];
+ volatile_u32_t slow_thread_num;
+ volatile_u32_t barrier_cnt1;
+ volatile_u32_t barrier_cnt2;
+ odp_barrier_t global_barrier;
+
+} global_shared_mem_t;
+
+/* Per-thread memory */
+typedef struct {
+ global_shared_mem_t *global_mem;
+
+ int thread_id;
+ int thread_core;
+
+ volatile_u64_t delay_counter;
+} per_thread_mem_t;
+
+static odp_shm_t global_shm;
+static global_shared_mem_t *global_mem;
+
+/*
+* Delay a consistent amount of time. Ideally the amount of CPU time taken
+* is linearly proportional to "iterations". The goal is to try to do some
+* work that the compiler optimizer won't optimize away, and also to
+* minimize loads and stores (at least to different memory addresses)
+* so as to not affect or be affected by caching issues. This does NOT have to
+* correlate to a specific number of cpu cycles or be consistent across
+* CPU architectures.
+*/
+static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations)
+{
+ volatile_u64_t *counter_ptr;
+ uint32_t cnt;
+
+ counter_ptr = &per_thread_mem->delay_counter;
+
+ for (cnt = 1; cnt <= iterations; cnt++)
+ (*counter_ptr)++;
+}
+
+/* Initialise per-thread memory */
+static per_thread_mem_t *thread_init(void)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_shm_t global_shm;
+ uint32_t per_thread_mem_len;
+
+ per_thread_mem_len = sizeof(per_thread_mem_t);
+ per_thread_mem = malloc(per_thread_mem_len);
+ memset(per_thread_mem, 0, per_thread_mem_len);
+
+ per_thread_mem->delay_counter = 1;
+
+ per_thread_mem->thread_id = odp_thread_id();
+ per_thread_mem->thread_core = odp_cpu_id();
+
+ global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ global_mem = odp_shm_addr(global_shm);
+ CU_ASSERT(global_mem != NULL);
+
+ per_thread_mem->global_mem = global_mem;
+
+ return per_thread_mem;
+}
+
+static void thread_finalize(per_thread_mem_t *per_thread_mem)
+{
+ free(per_thread_mem);
+}
+
+static void custom_barrier_init(custom_barrier_t *custom_barrier,
+ uint32_t num_threads)
+{
+ odp_atomic_init_u32(&custom_barrier->wait_cnt, num_threads);
+}
+
+static void custom_barrier_wait(custom_barrier_t *custom_barrier)
+{
+ volatile_u64_t counter = 1;
+ uint32_t delay_cnt, wait_cnt;
+
+ odp_atomic_sub_u32(&custom_barrier->wait_cnt, 1);
+
+ wait_cnt = 1;
+ while (wait_cnt != 0) {
+ for (delay_cnt = 1; delay_cnt <= BARRIER_DELAY; delay_cnt++)
+ counter++;
+
+ wait_cnt = odp_atomic_load_u32(&custom_barrier->wait_cnt);
+ }
+}
+
+static uint32_t barrier_test(per_thread_mem_t *per_thread_mem,
+ odp_bool_t no_barrier_test)
+{
+ global_shared_mem_t *global_mem;
+ uint32_t barrier_errs, iterations, cnt, i_am_slow_thread;
+ uint32_t thread_num, slow_thread_num, next_slow_thread, num_threads;
+ uint32_t lock_owner_delay, barrier_cnt1, barrier_cnt2;
+
+ thread_num = odp_thread_id();
+ global_mem = per_thread_mem->global_mem;
+ num_threads = global_mem->g_num_threads;
+ iterations = BARRIER_ITERATIONS;
+
+ barrier_errs = 0;
+ lock_owner_delay = SLOW_BARRIER_DELAY;
+
+ for (cnt = 1; cnt < iterations; cnt++) {
+ /* Wait here until all of the threads reach this point */
+ custom_barrier_wait(&global_mem->custom_barrier1[cnt]);
+
+ barrier_cnt1 = global_mem->barrier_cnt1;
+ barrier_cnt2 = global_mem->barrier_cnt2;
+
+ if ((barrier_cnt1 != cnt) || (barrier_cnt2 != cnt)) {
+ printf("thread_num=%" PRIu32 " barrier_cnts of %" PRIu32
+ " %" PRIu32 " cnt=%" PRIu32 "\n",
+ thread_num, barrier_cnt1, barrier_cnt2, cnt);
+ barrier_errs++;
+ }
+
+ /* Wait here until all of the threads reach this point */
+ custom_barrier_wait(&global_mem->custom_barrier2[cnt]);
+
+ slow_thread_num = global_mem->slow_thread_num;
+ i_am_slow_thread = thread_num == slow_thread_num;
+ next_slow_thread = slow_thread_num + 1;
+ if (num_threads < next_slow_thread)
+ next_slow_thread = 1;
+
+ /*
+ * Now run the test, which involves having all but one thread
+ * immediately calling odp_barrier_wait(), and one thread wait a
+ * moderate amount of time and then calling odp_barrier_wait().
+ * The test fails if any of the first group of threads
+ * has not waited for the "slow" thread. The "slow" thread is
+ * responsible for re-initializing the barrier for next trial.
+ */
+ if (i_am_slow_thread) {
+ thread_delay(per_thread_mem, lock_owner_delay);
+ lock_owner_delay += BASE_DELAY;
+ if ((global_mem->barrier_cnt1 != cnt) ||
+ (global_mem->barrier_cnt2 != cnt) ||
+ (global_mem->slow_thread_num
+ != slow_thread_num))
+ barrier_errs++;
+ }
+
+ if (no_barrier_test == 0)
+ odp_barrier_wait(&global_mem->test_barriers[cnt]);
+
+ global_mem->barrier_cnt1 = cnt + 1;
+ odp_mb_full();
+
+ if (i_am_slow_thread) {
+ global_mem->slow_thread_num = next_slow_thread;
+ global_mem->barrier_cnt2 = cnt + 1;
+ odp_mb_full();
+ } else {
+ while (global_mem->barrier_cnt2 != (cnt + 1))
+ thread_delay(per_thread_mem, BASE_DELAY);
+ }
+ }
+
+ if ((global_mem->g_verbose) && (barrier_errs != 0))
+ printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " barrier_errs in %" PRIu32 " iterations\n", thread_num,
+ per_thread_mem->thread_id,
+ per_thread_mem->thread_core, barrier_errs, iterations);
+
+ return barrier_errs;
+}
+
+static int no_barrier_functional_test(void *arg UNUSED)
+{
+ per_thread_mem_t *per_thread_mem;
+ uint32_t barrier_errs;
+
+ per_thread_mem = thread_init();
+ barrier_errs = barrier_test(per_thread_mem, 1);
+
+ /*
+ * Note that the following CU_ASSERT MAY appear incorrect, but for the
+ * no_barrier test it should see barrier_errs or else there is something
+ * wrong with the test methodology or the ODP thread implementation.
+ * So this test PASSES only if it sees barrier_errs or a single
+ * worker was used.
+ */
+ CU_ASSERT(barrier_errs != 0 || global_mem->g_num_threads == 1);
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int barrier_functional_test(void *arg UNUSED)
+{
+ per_thread_mem_t *per_thread_mem;
+ uint32_t barrier_errs;
+
+ per_thread_mem = thread_init();
+ barrier_errs = barrier_test(per_thread_mem, 0);
+
+ CU_ASSERT(barrier_errs == 0);
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static void barrier_test_init(void)
+{
+ uint32_t num_threads, idx;
+
+ num_threads = global_mem->g_num_threads;
+
+ for (idx = 0; idx < NUM_TEST_BARRIERS; idx++) {
+ odp_barrier_init(&global_mem->test_barriers[idx], num_threads);
+ custom_barrier_init(&global_mem->custom_barrier1[idx],
+ num_threads);
+ custom_barrier_init(&global_mem->custom_barrier2[idx],
+ num_threads);
+ }
+
+ global_mem->slow_thread_num = 1;
+ global_mem->barrier_cnt1 = 1;
+ global_mem->barrier_cnt2 = 1;
+}
+
+/* Barrier tests */
+static void barrier_test_memory_barrier(void)
+{
+ volatile int a = 0;
+ volatile int b = 0;
+ volatile int c = 0;
+ volatile int d = 0;
+ volatile int e = 0;
+ volatile int f = 0;
+ volatile int g = 0;
+
+ /* Call all memory barriers to verify that those are implemented */
+ a = 1;
+ odp_mb_release();
+ b = 1;
+ odp_mb_acquire();
+ c = 1;
+ odp_mb_full();
+ d = 1;
+ odp_mb_sync();
+ e = 1;
+ odp_mb_sync_load();
+ f = 1;
+ odp_mb_sync_store();
+ g = 1;
+
+ /* Avoid "variable set but not used" warning */
+ temp_result = a + b + c + d + e + f + g;
+}
+
+static int barrier_init(odp_instance_t *inst)
+{
+ uint32_t workers_count, max_threads;
+ int ret = 0;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(global_shared_mem_t), 64, 0);
+ if (ODP_SHM_INVALID == global_shm) {
+ ODPH_ERR("Unable to reserve memory for global_shm\n");
+ return -1;
+ }
+
+ global_mem = odp_shm_addr(global_shm);
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ global_mem->g_num_threads = MAX_WORKERS;
+ global_mem->g_iterations = MAX_ITERATIONS;
+ global_mem->g_verbose = VERBOSE;
+
+ workers_count = odp_cpumask_default_worker(NULL, 0);
+
+ max_threads = (workers_count >= MAX_WORKERS) ?
+ MAX_WORKERS : workers_count;
+
+ if (max_threads < global_mem->g_num_threads) {
+ printf("Requested num of threads is too large\n");
+ printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
+ global_mem->g_num_threads,
+ max_threads);
+ global_mem->g_num_threads = max_threads;
+ }
+
+ printf("Num of threads used = %" PRIu32 "\n",
+ global_mem->g_num_threads);
+
+ return ret;
+}
+
+static int barrier_term(odp_instance_t inst)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ if (0 != odp_shm_free(shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void barrier_single_thread(void)
+{
+ odp_barrier_t barrier;
+
+ odp_barrier_init(&barrier, 1);
+
+ printf(" Calling wait...");
+
+ odp_barrier_wait(&barrier);
+
+ printf(" 1");
+
+ odp_barrier_wait(&barrier);
+
+ printf(" 2");
+
+ odp_barrier_wait(&barrier);
+
+ printf(" 3. ");
+}
+
+static void barrier_test_no_barrier_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ barrier_test_init();
+ odp_cunit_thread_create(num, no_barrier_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void barrier_test_barrier_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ barrier_test_init();
+ odp_cunit_thread_create(num, barrier_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t barrier_suite_barrier[] = {
+ ODP_TEST_INFO(barrier_test_memory_barrier),
+ ODP_TEST_INFO(barrier_single_thread),
+ ODP_TEST_INFO(barrier_test_no_barrier_functional),
+ ODP_TEST_INFO(barrier_test_barrier_functional),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t barrier_suites[] = {
+ {"barrier", NULL, NULL,
+ barrier_suite_barrier},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(barrier_init);
+ odp_cunit_register_global_term(barrier_term);
+
+ ret = odp_cunit_register(barrier_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/buffer/.gitignore b/test/validation/api/buffer/.gitignore
index 0e8ac15c1..0e8ac15c1 100644
--- a/test/common_plat/validation/api/buffer/.gitignore
+++ b/test/validation/api/buffer/.gitignore
diff --git a/test/validation/api/buffer/Makefile.am b/test/validation/api/buffer/Makefile.am
new file mode 100644
index 000000000..f459010c3
--- /dev/null
+++ b/test/validation/api/buffer/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = buffer_main
+buffer_main_SOURCES = buffer.c
diff --git a/test/validation/api/buffer/buffer.c b/test/validation/api/buffer/buffer.c
new file mode 100644
index 000000000..ec2aa45a0
--- /dev/null
+++ b/test/validation/api/buffer/buffer.c
@@ -0,0 +1,608 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2022 Nokia
+ * Copyright (c) 2022 Marvell
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_debug.h>
+#include "odp_cunit_common.h"
+
+#define BUF_ALIGN ODP_CACHE_LINE_SIZE
+#define BUF_SIZE 1500
+#define BUF_NUM 100
+#define BURST 8
+
+static odp_pool_capability_t pool_capa;
+static odp_pool_param_t default_param;
+
+static int buffer_suite_init(void)
+{
+ uint32_t size, num, align;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ size = BUF_SIZE;
+ if (pool_capa.buf.max_size && size > pool_capa.buf.max_size)
+ size = pool_capa.buf.max_size;
+
+ num = BUF_NUM;
+ if (pool_capa.buf.max_num && num > pool_capa.buf.max_num)
+ num = pool_capa.buf.max_num;
+
+ align = BUF_ALIGN;
+ if (align > pool_capa.buf.max_align)
+ align = pool_capa.buf.max_align;
+
+ odp_pool_param_init(&default_param);
+ default_param.type = ODP_POOL_BUFFER;
+ default_param.buf.size = size;
+ default_param.buf.num = num;
+ default_param.buf.align = align;
+
+ printf("Default buffer pool\n");
+ printf(" size %u\n", size);
+ printf(" num %u\n", num);
+ printf(" align %u\n\n", align);
+
+ return 0;
+}
+
+static void test_pool_alloc_free(const odp_pool_param_t *param)
+{
+ odp_pool_t pool;
+ odp_event_t ev;
+ uint32_t i;
+ uint32_t num_buf = 0;
+ void *addr;
+ odp_event_subtype_t subtype;
+ uint32_t num = param->buf.num;
+ uint32_t size = param->buf.size;
+ uint32_t align = param->buf.align;
+
+ odp_buffer_t buffer[num];
+ odp_bool_t wrong_type = false, wrong_subtype = false;
+ odp_bool_t wrong_size = false, wrong_align = false;
+
+ pool = odp_pool_create("default pool", param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_pool_print(pool);
+
+ for (i = 0; i < num; i++) {
+ odp_buffer_t buf;
+
+ buffer[i] = odp_buffer_alloc(pool);
+
+ if (buffer[i] == ODP_BUFFER_INVALID)
+ break;
+ num_buf++;
+
+ CU_ASSERT(odp_buffer_is_valid(buffer[i]) == 1)
+
+ CU_ASSERT(odp_buffer_pool(buffer[i]) == pool);
+
+ ev = odp_buffer_to_event(buffer[i]);
+ CU_ASSERT(odp_buffer_from_event(ev) == buffer[i]);
+
+ odp_buffer_to_event_multi(&buffer[i], &ev, 1);
+ odp_buffer_from_event_multi(&buf, &ev, 1);
+ CU_ASSERT(buf == buffer[i]);
+
+ CU_ASSERT(odp_event_pool(ev) == pool);
+
+ if (odp_event_type(ev) != ODP_EVENT_BUFFER)
+ wrong_type = true;
+ if (odp_event_subtype(ev) != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_BUFFER)
+ wrong_type = true;
+ if (subtype != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+ if (odp_buffer_size(buffer[i]) < size)
+ wrong_size = true;
+
+ addr = odp_buffer_addr(buffer[i]);
+
+ if (((uintptr_t)addr % align) != 0)
+ wrong_align = true;
+
+ if (wrong_type || wrong_subtype || wrong_size || wrong_align) {
+ ODPH_ERR("Buffer has error\n");
+ odp_buffer_print(buffer[i]);
+ break;
+ }
+
+ /* Write buffer data */
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num);
+ CU_ASSERT(!wrong_type);
+ CU_ASSERT(!wrong_subtype);
+ CU_ASSERT(!wrong_size);
+ CU_ASSERT(!wrong_align);
+
+ for (i = 0; i < num_buf; i++)
+ odp_buffer_free(buffer[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void test_pool_alloc_free_multi(const odp_pool_param_t *param)
+{
+ odp_pool_t pool;
+ uint32_t i, num_buf;
+ int ret;
+ odp_event_t ev;
+ void *addr;
+ odp_event_subtype_t subtype;
+ uint32_t num = param->buf.num;
+ uint32_t size = param->buf.size;
+ uint32_t align = param->buf.align;
+
+ odp_buffer_t buffer[num + BURST];
+ odp_bool_t wrong_type = false, wrong_subtype = false;
+ odp_bool_t wrong_size = false, wrong_align = false;
+
+ pool = odp_pool_create("default pool", param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ ret = 0;
+ for (i = 0; i < num; i += ret) {
+ odp_buffer_t buf[BURST];
+ odp_event_t event[BURST];
+
+ ret = odp_buffer_alloc_multi(pool, &buffer[i], BURST);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT(ret <= BURST);
+
+ if (ret <= 0)
+ break;
+
+ odp_buffer_to_event_multi(&buffer[i], event, ret);
+ odp_buffer_from_event_multi(buf, event, ret);
+ for (int j = 0; j < ret; j++)
+ CU_ASSERT(buf[j] == buffer[i + j]);
+ }
+
+ num_buf = i;
+ CU_ASSERT(num_buf == num);
+
+ for (i = 0; i < num_buf; i++) {
+ if (buffer[i] == ODP_BUFFER_INVALID)
+ break;
+
+ CU_ASSERT(odp_buffer_is_valid(buffer[i]) == 1)
+
+ CU_ASSERT(odp_buffer_pool(buffer[i]) == pool);
+
+ ev = odp_buffer_to_event(buffer[i]);
+ CU_ASSERT(odp_buffer_from_event(ev) == buffer[i]);
+
+ if (odp_event_type(ev) != ODP_EVENT_BUFFER)
+ wrong_type = true;
+ if (odp_event_subtype(ev) != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_BUFFER)
+ wrong_type = true;
+ if (subtype != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+ if (odp_buffer_size(buffer[i]) < size)
+ wrong_size = true;
+
+ addr = odp_buffer_addr(buffer[i]);
+
+ if (((uintptr_t)addr % align) != 0)
+ wrong_align = true;
+
+ if (wrong_type || wrong_subtype || wrong_size || wrong_align) {
+ ODPH_ERR("Buffer has error\n");
+ odp_buffer_print(buffer[i]);
+ break;
+ }
+
+ /* Write buffer data */
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num_buf);
+ CU_ASSERT(!wrong_type);
+ CU_ASSERT(!wrong_subtype);
+ CU_ASSERT(!wrong_size);
+ CU_ASSERT(!wrong_align);
+
+ if (num_buf)
+ odp_buffer_free_multi(buffer, num_buf);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void test_pool_single_pool(odp_pool_param_t *param)
+{
+ odp_pool_t pool;
+ odp_buffer_t buffer;
+
+ param->buf.num = 1;
+
+ pool = odp_pool_create("pool 0", param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_pool_print(pool);
+
+ /* Allocate the only buffer from the pool */
+ buffer = odp_buffer_alloc(pool);
+ CU_ASSERT(buffer != ODP_BUFFER_INVALID);
+
+ /* Pool should be empty */
+ CU_ASSERT(odp_buffer_alloc(pool) == ODP_BUFFER_INVALID)
+
+ if (buffer != ODP_BUFFER_INVALID) {
+ odp_event_t ev = odp_buffer_to_event(buffer);
+
+ CU_ASSERT(odp_buffer_to_u64(buffer) !=
+ odp_buffer_to_u64(ODP_BUFFER_INVALID));
+ CU_ASSERT(odp_event_to_u64(ev) !=
+ odp_event_to_u64(ODP_EVENT_INVALID));
+ CU_ASSERT(odp_buffer_pool(buffer) == pool);
+ odp_buffer_print(buffer);
+ odp_buffer_free(buffer);
+ }
+
+ /* Check that the buffer was returned back to the pool */
+ buffer = odp_buffer_alloc(pool);
+ CU_ASSERT(buffer != ODP_BUFFER_INVALID);
+
+ if (buffer != ODP_BUFFER_INVALID)
+ odp_buffer_free(buffer);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void test_pool_two_pools(odp_pool_param_t *param)
+{
+ odp_pool_t pool0, pool1;
+ odp_buffer_t buf, buffer[2];
+ int num = 0;
+
+ if (pool_capa.buf.max_pools < 2)
+ return;
+
+ param->buf.num = 1;
+
+ pool0 = odp_pool_create("pool 0", param);
+ CU_ASSERT_FATAL(pool0 != ODP_POOL_INVALID);
+
+ pool1 = odp_pool_create("pool 1", param);
+ CU_ASSERT_FATAL(pool1 != ODP_POOL_INVALID);
+
+ buffer[0] = odp_buffer_alloc(pool0);
+ CU_ASSERT(buffer[0] != ODP_BUFFER_INVALID);
+
+ buffer[1] = odp_buffer_alloc(pool1);
+ CU_ASSERT(buffer[1] != ODP_BUFFER_INVALID);
+
+ if (buffer[0] != ODP_BUFFER_INVALID) {
+ CU_ASSERT(odp_buffer_pool(buffer[0]) == pool0);
+ num++;
+ }
+ if (buffer[1] != ODP_BUFFER_INVALID) {
+ CU_ASSERT(odp_buffer_pool(buffer[1]) == pool1);
+ num++;
+ }
+
+ CU_ASSERT(odp_buffer_alloc(pool0) == ODP_BUFFER_INVALID);
+ CU_ASSERT(odp_buffer_alloc(pool1) == ODP_BUFFER_INVALID);
+
+ /* free buffers from two different pools */
+ if (num)
+ odp_buffer_free_multi(buffer, num);
+
+ /* Check that buffers were returned back into pools */
+ buf = odp_buffer_alloc(pool0);
+ CU_ASSERT(buf != ODP_BUFFER_INVALID);
+
+ if (buf != ODP_BUFFER_INVALID) {
+ CU_ASSERT(odp_buffer_pool(buf) == pool0);
+ odp_buffer_free(buf);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool0) == 0);
+
+ buf = odp_buffer_alloc(pool1);
+ CU_ASSERT(buf != ODP_BUFFER_INVALID);
+
+ if (buf != ODP_BUFFER_INVALID) {
+ CU_ASSERT(odp_buffer_pool(buf) == pool1);
+ odp_buffer_free(buf);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool1) == 0);
+}
+
+static void test_pool_max_pools(odp_pool_param_t *param)
+{
+ uint32_t i, num_pool, num_buf;
+ void *addr;
+ odp_event_t ev;
+ uint32_t max_pools = pool_capa.buf.max_pools;
+ uint32_t size = param->buf.size;
+ uint32_t align = param->buf.align;
+ odp_pool_t pool[max_pools];
+ odp_buffer_t buffer[max_pools];
+
+ CU_ASSERT_FATAL(max_pools != 0);
+
+ printf("\n Creating %u pools\n", max_pools);
+
+ param->buf.num = 1;
+
+ for (i = 0; i < max_pools; i++) {
+ pool[i] = odp_pool_create(NULL, param);
+
+ if (pool[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ num_pool = i;
+
+ CU_ASSERT(num_pool == max_pools);
+ if (num_pool != max_pools)
+ ODPH_ERR("Created only %u pools\n", num_pool);
+
+ for (i = 0; i < num_pool; i++) {
+ buffer[i] = odp_buffer_alloc(pool[i]);
+
+ if (buffer[i] == ODP_BUFFER_INVALID)
+ break;
+
+ CU_ASSERT_FATAL(odp_buffer_pool(buffer[i]) == pool[i]);
+
+ ev = odp_buffer_to_event(buffer[i]);
+ CU_ASSERT(odp_buffer_from_event(ev) == buffer[i]);
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_BUFFER);
+
+ addr = odp_buffer_addr(buffer[i]);
+ CU_ASSERT(((uintptr_t)addr % align) == 0);
+
+ /* Write buffer data */
+ memset(addr, 0, size);
+ }
+
+ num_buf = i;
+ CU_ASSERT(num_buf == num_pool);
+
+ if (num_buf)
+ odp_buffer_free_multi(buffer, num_buf);
+
+ for (i = 0; i < num_pool; i++)
+ CU_ASSERT(odp_pool_destroy(pool[i]) == 0);
+}
+
+static void buffer_test_pool_alloc_free(void)
+{
+ test_pool_alloc_free(&default_param);
+}
+
+static void buffer_test_pool_alloc_free_min_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.min_cache_size;
+ test_pool_alloc_free(&param);
+}
+
+static void buffer_test_pool_alloc_free_max_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.max_cache_size;
+ test_pool_alloc_free(&param);
+}
+
+static void buffer_test_pool_alloc_free_multi(void)
+{
+ test_pool_alloc_free_multi(&default_param);
+}
+
+static void buffer_test_pool_alloc_free_multi_min_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.min_cache_size;
+ test_pool_alloc_free_multi(&param);
+}
+
+static void buffer_test_pool_alloc_free_multi_max_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.max_cache_size;
+ test_pool_alloc_free_multi(&param);
+}
+
+static void buffer_test_pool_single_pool(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ test_pool_single_pool(&param);
+}
+
+static void buffer_test_pool_single_pool_min_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.min_cache_size;
+ test_pool_single_pool(&param);
+}
+
+static void buffer_test_pool_single_pool_max_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.max_cache_size;
+ test_pool_single_pool(&param);
+}
+
+static void buffer_test_pool_two_pools(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ test_pool_two_pools(&param);
+}
+
+static void buffer_test_pool_two_pools_min_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.min_cache_size;
+ test_pool_two_pools(&param);
+}
+
+static void buffer_test_pool_two_pools_max_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.max_cache_size;
+ test_pool_two_pools(&param);
+}
+
+static void buffer_test_pool_max_pools(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ test_pool_max_pools(&param);
+}
+
+static void buffer_test_pool_max_pools_min_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.min_cache_size;
+ test_pool_max_pools(&param);
+}
+
+static void buffer_test_pool_max_pools_max_cache(void)
+{
+ odp_pool_param_t param;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.cache_size = pool_capa.buf.max_cache_size;
+ test_pool_max_pools(&param);
+}
+
+static void buffer_test_user_area(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ uint32_t i, num;
+ void *addr;
+ void *prev = NULL;
+ uint32_t num_alloc = 0;
+ uint32_t size = 1024;
+ const uint32_t max_size = pool_capa.buf.max_uarea_size;
+
+ if (max_size == 0) {
+ ODPH_DBG("Buffer user area not supported\n");
+ return;
+ }
+
+ if (size > max_size)
+ size = max_size;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+ param.buf.uarea_size = size;
+
+ num = param.buf.num;
+
+ odp_buffer_t buffer[num];
+
+ pool = odp_pool_create("test_user_area", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag = 0;
+
+ buffer[i] = odp_buffer_alloc(pool);
+
+ if (buffer[i] == ODP_BUFFER_INVALID)
+ break;
+ num_alloc++;
+
+ addr = odp_buffer_user_area(buffer[i]);
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ ev = odp_buffer_to_event(buffer[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag < 0);
+
+ prev = addr;
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num);
+
+ if (num_alloc)
+ odp_buffer_free_multi(buffer, num_alloc);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+odp_testinfo_t buffer_suite[] = {
+ ODP_TEST_INFO(buffer_test_pool_alloc_free),
+ ODP_TEST_INFO(buffer_test_pool_alloc_free_min_cache),
+ ODP_TEST_INFO(buffer_test_pool_alloc_free_max_cache),
+ ODP_TEST_INFO(buffer_test_pool_alloc_free_multi),
+ ODP_TEST_INFO(buffer_test_pool_alloc_free_multi_min_cache),
+ ODP_TEST_INFO(buffer_test_pool_alloc_free_multi_max_cache),
+ ODP_TEST_INFO(buffer_test_pool_single_pool),
+ ODP_TEST_INFO(buffer_test_pool_single_pool_min_cache),
+ ODP_TEST_INFO(buffer_test_pool_single_pool_max_cache),
+ ODP_TEST_INFO(buffer_test_pool_two_pools),
+ ODP_TEST_INFO(buffer_test_pool_two_pools_min_cache),
+ ODP_TEST_INFO(buffer_test_pool_two_pools_max_cache),
+ ODP_TEST_INFO(buffer_test_pool_max_pools),
+ ODP_TEST_INFO(buffer_test_pool_max_pools_min_cache),
+ ODP_TEST_INFO(buffer_test_pool_max_pools_max_cache),
+ ODP_TEST_INFO(buffer_test_user_area),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t buffer_suites[] = {
+ {"buffer tests", buffer_suite_init, NULL, buffer_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(buffer_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/byteorder/.gitignore b/test/validation/api/byteorder/.gitignore
new file mode 100644
index 000000000..0b0d91053
--- /dev/null
+++ b/test/validation/api/byteorder/.gitignore
@@ -0,0 +1 @@
+byteorder_main
diff --git a/test/validation/api/byteorder/Makefile.am b/test/validation/api/byteorder/Makefile.am
new file mode 100644
index 000000000..186f4ecbc
--- /dev/null
+++ b/test/validation/api/byteorder/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = byteorder_main
+byteorder_main_SOURCES = byteorder.c
diff --git a/test/validation/api/byteorder/byteorder.c b/test/validation/api/byteorder/byteorder.c
new file mode 100644
index 000000000..087dfce95
--- /dev/null
+++ b/test/validation/api/byteorder/byteorder.c
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <stdint.h>
+
+static void test_defines(void)
+{
+ /* Endianness */
+ CU_ASSERT(ODP_BIG_ENDIAN || ODP_LITTLE_ENDIAN);
+
+ if (ODP_BIG_ENDIAN) {
+ CU_ASSERT(ODP_BYTE_ORDER == ODP_BIG_ENDIAN);
+ CU_ASSERT(!ODP_LITTLE_ENDIAN);
+ }
+
+ if (ODP_LITTLE_ENDIAN) {
+ CU_ASSERT(ODP_BYTE_ORDER == ODP_LITTLE_ENDIAN);
+ CU_ASSERT(!ODP_BIG_ENDIAN);
+ }
+
+ /* Bitfield endianness */
+ CU_ASSERT(ODP_BIG_ENDIAN_BITFIELD || ODP_LITTLE_ENDIAN_BITFIELD);
+
+ if (ODP_BIG_ENDIAN_BITFIELD) {
+ CU_ASSERT(ODP_BITFIELD_ORDER == ODP_BIG_ENDIAN_BITFIELD);
+ CU_ASSERT(!ODP_LITTLE_ENDIAN_BITFIELD);
+ }
+
+ if (ODP_LITTLE_ENDIAN_BITFIELD) {
+ CU_ASSERT(ODP_BITFIELD_ORDER == ODP_LITTLE_ENDIAN_BITFIELD);
+ CU_ASSERT(!ODP_BIG_ENDIAN_BITFIELD);
+ }
+}
+
+static void test_types(void)
+{
+ const uint16_t u16_val = 0x1234;
+ const uint32_t u32_val = 0x12345678;
+ const uint64_t u64_val = 0x1234567890123456;
+ const uint16_t u16_val_conv = 0x3412;
+ const uint32_t u32_val_conv = 0x78563412;
+ const uint64_t u64_val_conv = 0x5634129078563412;
+ odp_u16be_t be16 = odp_cpu_to_be_16(u16_val);
+ odp_u32be_t be32 = odp_cpu_to_be_32(u32_val);
+ odp_u64be_t be64 = odp_cpu_to_be_64(u64_val);
+ odp_u16le_t le16 = odp_cpu_to_le_16(u16_val);
+ odp_u32le_t le32 = odp_cpu_to_le_32(u32_val);
+ odp_u64le_t le64 = odp_cpu_to_le_64(u64_val);
+ odp_u16sum_t sum16 = u16_val;
+ odp_u32sum_t sum32 = u16_val;
+
+ CU_ASSERT(sum16 == sum32);
+
+ if (ODP_BIG_ENDIAN) {
+ CU_ASSERT(be16 == u16_val);
+ CU_ASSERT(be32 == u32_val);
+ CU_ASSERT(be64 == u64_val);
+ CU_ASSERT(le16 == u16_val_conv);
+ CU_ASSERT(le32 == u32_val_conv);
+ CU_ASSERT(le64 == u64_val_conv);
+ } else {
+ CU_ASSERT(le16 == u16_val);
+ CU_ASSERT(le32 == u32_val);
+ CU_ASSERT(le64 == u64_val);
+ CU_ASSERT(be16 == u16_val_conv);
+ CU_ASSERT(be32 == u32_val_conv);
+ CU_ASSERT(be64 == u64_val_conv);
+ }
+
+ CU_ASSERT(odp_be_to_cpu_16(be16) == u16_val);
+ CU_ASSERT(odp_be_to_cpu_32(be32) == u32_val);
+ CU_ASSERT(odp_be_to_cpu_64(be64) == u64_val);
+ CU_ASSERT(odp_le_to_cpu_16(le16) == u16_val);
+ CU_ASSERT(odp_le_to_cpu_32(le32) == u32_val);
+ CU_ASSERT(odp_le_to_cpu_64(le64) == u64_val);
+}
+
+odp_testinfo_t byteorder_suite[] = {
+ ODP_TEST_INFO(test_defines),
+ ODP_TEST_INFO(test_types),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t byteorder_suites[] = {
+ {"byteorder", NULL, NULL, byteorder_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* Parse common options */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(byteorder_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/chksum/.gitignore b/test/validation/api/chksum/.gitignore
new file mode 100644
index 000000000..c69e8c470
--- /dev/null
+++ b/test/validation/api/chksum/.gitignore
@@ -0,0 +1 @@
+chksum_main
diff --git a/test/validation/api/chksum/Makefile.am b/test/validation/api/chksum/Makefile.am
new file mode 100644
index 000000000..349fdd641
--- /dev/null
+++ b/test/validation/api/chksum/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = chksum_main
+chksum_main_SOURCES = chksum.c
diff --git a/test/validation/api/chksum/chksum.c b/test/validation/api/chksum/chksum.c
new file mode 100644
index 000000000..17f8fed12
--- /dev/null
+++ b/test/validation/api/chksum/chksum.c
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#define NUM_IP_HDR 5
+#define IP_HDR_LEN 20
+
+#define NUM_UDP 4
+#define MAX_UDP_LEN 128
+
+static uint8_t ip_hdr_test_vect[NUM_IP_HDR][IP_HDR_LEN] ODP_ALIGNED(4) = {
+ { 0x45, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0x33, 0xC0, 0xA8, 0x2C, 0xA2, 0xC0, 0xA8, 0x21, 0x99
+ },
+ { 0x45, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0xCA, 0xC0, 0xA8, 0x2C, 0x5E, 0xC0, 0xA8, 0x21, 0x46
+ },
+ { 0x45, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0x64, 0xC0, 0xA8, 0x2C, 0x20, 0xC0, 0xA8, 0x21, 0xEA
+ },
+ { 0x45, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAB, 0x59, 0xC0, 0xA8, 0x2C, 0xD2, 0xC0, 0xA8, 0x21, 0x43
+ },
+ { 0x45, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11,
+ 0xAC, 0x06, 0xC0, 0xA8, 0x2C, 0x5C, 0xC0, 0xA8, 0x21, 0x0C
+ }
+};
+
+struct udp_test_vect_s {
+ uint32_t len;
+
+ uint8_t data[MAX_UDP_LEN];
+};
+
+static struct udp_test_vect_s udp_test_vect[NUM_UDP] ODP_ALIGNED(4) = {
+ {.len = 38,
+ .data = { 0x00, 0x11, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1A, 0xFF, 0x3C,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xC7, 0xBF
+ } },
+
+ {.len = 39,
+ .data = { 0x00, 0x11, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x40, 0x09, 0x35, 0x00, 0x1B, 0xD6, 0x43,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xBF, 0xB7
+ } },
+
+ {.len = 59,
+ .data = { 0x00, 0x11, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x32, 0x5F, 0x01, 0x71, 0x00, 0x2F, 0xCB, 0xC0,
+ 0x09, 0x8B, 0x61, 0x3E, 0x3A, 0x7F, 0x30, 0x0F, 0x4D, 0xEE,
+ 0x2D, 0x7D, 0x11, 0xBB, 0xBB, 0x34, 0x0E, 0x9E, 0xC5, 0x3D,
+ 0xBB, 0x81, 0x9A, 0x7F, 0xF2, 0x2A, 0xFC, 0x85, 0xA0, 0x1B,
+ 0x73, 0x81, 0xC1, 0xB6, 0xE8, 0x91, 0x8C, 0xD8, 0x7F
+ } },
+
+ {.len = 109,
+ .data = { 0x00, 0x11, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x32, 0x5F, 0x01, 0x71, 0x00, 0x61, 0xCB, 0x5C,
+ 0x61, 0xAD, 0xFD, 0xE7, 0x4E, 0x98, 0x69, 0x59, 0x12, 0x3F,
+ 0xDF, 0xF6, 0x79, 0x8B, 0xB3, 0x94, 0x94, 0x9F, 0x8C, 0x0B,
+ 0x67, 0xBA, 0xBA, 0x3C, 0xE2, 0x5F, 0xCA, 0x52, 0x13, 0xB4,
+ 0x57, 0x48, 0x99, 0x29, 0x23, 0xAC, 0x5C, 0x59, 0x66, 0xA2,
+ 0x7B, 0x35, 0x65, 0x2B, 0x86, 0x5F, 0x47, 0xA7, 0xEE, 0xD4,
+ 0x24, 0x99, 0xB9, 0xCE, 0x60, 0xAB, 0x7A, 0xE9, 0x37, 0xF2,
+ 0x81, 0x84, 0x98, 0x72, 0x4F, 0x6A, 0x37, 0xE5, 0x4D, 0xB2,
+ 0xDE, 0xB8, 0xBD, 0xE3, 0x03, 0x57, 0xF0, 0x5C, 0xA0, 0xAA,
+ 0xB9, 0xF3, 0x3F, 0xDF, 0x23, 0xDD, 0x54, 0x2F, 0xCE
+ } }
+};
+
+/* Correct checksum in network byte order is 0xF3, 0x96 */
+#define UDP_LONG_CHKSUM odp_be_to_cpu_16(0xF396)
+
+/* Number of padding bytes in the end of the array */
+#define UDP_LONG_PADDING 11
+
+/* Long UDP packet with pseudo header. Checksum field is set to zero.
+ * The array contains padding, so that a possible overrun is more likely
+ * detected (overrun bytes are not all zeros). */
+static uint8_t udp_test_vect_long[] ODP_ALIGNED(4) = {
+ 0x00, 0x11, 0x05, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x3F, 0x05, 0xED, 0x00, 0x00,
+ 0x0B, 0x2C, 0x9C, 0x06, 0x07, 0xF3, 0x51, 0x05, 0xF7, 0xA7,
+ 0xF4, 0x24, 0xBB, 0x2F, 0x19, 0xBB, 0x23, 0xB4, 0x99, 0x50,
+ 0x69, 0x10, 0x34, 0xAD, 0xFF, 0x83, 0x7B, 0x36, 0x8B, 0xA8,
+ 0xEE, 0x7A, 0x31, 0xA8, 0x61, 0x08, 0x10, 0xAE, 0xA4, 0x84,
+ 0x0F, 0x9B, 0x62, 0xFA, 0xD9, 0xFA, 0x4A, 0x71, 0x26, 0x36,
+ 0x6D, 0xC4, 0x3D, 0x0A, 0xE7, 0xAD, 0xB6, 0x3A, 0xC2, 0x23,
+ 0x85, 0x81, 0x17, 0xE6, 0x34, 0xCA, 0x76, 0x58, 0x70, 0xA3,
+ 0x88, 0x8E, 0xC7, 0xEE, 0xF3, 0xA7, 0xB0, 0xD9, 0x7E, 0x5C,
+ 0xCC, 0x11, 0x76, 0xD9, 0x6B, 0x33, 0x50, 0xCB, 0x52, 0x84,
+ 0x8F, 0xBB, 0xBC, 0xE1, 0xE9, 0x9A, 0x9E, 0xF9, 0xA5, 0x9D,
+ 0x2F, 0xB9, 0x47, 0x4B, 0xA8, 0x08, 0x6F, 0xD4, 0x48, 0xB5,
+ 0xFF, 0xA4, 0x9C, 0xAD, 0x7C, 0x38, 0xFD, 0x72, 0xB7, 0x30,
+ 0x5A, 0xC8, 0xAC, 0xA6, 0x38, 0xB2, 0xAA, 0xBF, 0xF6, 0x9C,
+ 0xB4, 0x7F, 0x56, 0xDA, 0x28, 0xE2, 0x39, 0x51, 0x9E, 0x28,
+ 0xAA, 0x74, 0x13, 0x70, 0x1E, 0x73, 0x47, 0x88, 0xFA, 0xAE,
+ 0x63, 0x04, 0x56, 0x70, 0xDF, 0x8D, 0x01, 0xDA, 0xA3, 0x7C,
+ 0xC5, 0x1D, 0x6C, 0xA8, 0xEC, 0xED, 0x72, 0x7B, 0x02, 0x32,
+ 0x48, 0x4C, 0xDD, 0x00, 0x03, 0xE8, 0xDB, 0xC9, 0x9E, 0x72,
+ 0x6B, 0xD1, 0x9D, 0x6E, 0xCF, 0xE3, 0xF1, 0x18, 0x20, 0x43,
+ 0x1C, 0xFA, 0x92, 0xE3, 0x95, 0xBD, 0xF9, 0xD9, 0x6E, 0x40,
+ 0x8C, 0x11, 0x34, 0xE7, 0xE9, 0x3F, 0x17, 0x36, 0x5A, 0x18,
+ 0x4A, 0x9E, 0x57, 0xC2, 0xAD, 0x96, 0x4C, 0x89, 0xAA, 0xEE,
+ 0x9C, 0xD3, 0x5C, 0x60, 0xE5, 0x56, 0xF7, 0x69, 0x86, 0x88,
+ 0x64, 0x57, 0x5E, 0x57, 0x2B, 0xA2, 0xD7, 0x1B, 0x96, 0x7F,
+ 0x23, 0xC9, 0x14, 0xB3, 0xC6, 0x69, 0xDB, 0xA5, 0x55, 0xCA,
+ 0xD1, 0xB8, 0x7D, 0x74, 0xAD, 0xD7, 0x46, 0xDE, 0x59, 0x52,
+ 0x99, 0xE1, 0x9B, 0xE8, 0x01, 0x6D, 0xF5, 0x25, 0xAE, 0x7B,
+ 0xCA, 0xEE, 0xBF, 0x42, 0xC1, 0x5A, 0xC1, 0xAE, 0x6F, 0xC3,
+ 0x72, 0x0E, 0x30, 0x1D, 0xCB, 0x0D, 0x55, 0x87, 0x3E, 0xE3,
+ 0x85, 0x20, 0x3D, 0xCC, 0x5D, 0x1C, 0xFB, 0xB9, 0x5A, 0x17,
+ 0x76, 0x46, 0xF9, 0xA8, 0xB5, 0xED, 0x1C, 0x3A, 0x4E, 0x79,
+ 0xB0, 0x17, 0x2C, 0xBD, 0x8D, 0xC6, 0x8F, 0x85, 0x9D, 0x97,
+ 0x54, 0xCD, 0x41, 0x7C, 0x77, 0x31, 0xF0, 0x1A, 0xD6, 0xA5,
+ 0x22, 0x20, 0x38, 0x66, 0x6C, 0xD8, 0x8D, 0x31, 0xEC, 0xFC,
+ 0x78, 0xB2, 0xE7, 0xCA, 0x0A, 0x3F, 0xB1, 0x5A, 0xD1, 0xC6,
+ 0xCD, 0x30, 0x11, 0x04, 0x56, 0x0E, 0x51, 0xF8, 0x9D, 0x0B,
+ 0x19, 0x98, 0x14, 0xE9, 0xFB, 0xC2, 0x6B, 0xA4, 0xD0, 0x75,
+ 0xF8, 0x47, 0x44, 0x41, 0x5C, 0x03, 0x95, 0xFC, 0x64, 0x82,
+ 0x97, 0xB7, 0x2D, 0x79, 0xFC, 0xF0, 0x8C, 0x7D, 0xD2, 0x0A,
+ 0xAC, 0x17, 0x4D, 0xFA, 0xF9, 0x1A, 0xC7, 0x42, 0x3D, 0x34,
+ 0x05, 0x23, 0x09, 0xC4, 0xBC, 0x0E, 0x57, 0xEB, 0x53, 0x5D,
+ 0x6D, 0xE6, 0xEB, 0x40, 0x05, 0x9B, 0x9D, 0xFA, 0xAA, 0x71,
+ 0x02, 0x4D, 0x65, 0x65, 0xF9, 0x32, 0x99, 0x5B, 0xA4, 0xCE,
+ 0x2C, 0xB1, 0xB4, 0xA2, 0xE7, 0x1A, 0xE7, 0x02, 0x20, 0xAA,
+ 0xCE, 0xC8, 0x96, 0x2D, 0xD4, 0x49, 0x9C, 0xBD, 0x7C, 0x88,
+ 0x7A, 0x94, 0xEA, 0xAA, 0x10, 0x1E, 0xA5, 0xAA, 0xBC, 0x52,
+ 0x9B, 0x4E, 0x7E, 0x43, 0x66, 0x5A, 0x5A, 0xF2, 0xCD, 0x03,
+ 0xFE, 0x67, 0x8E, 0xA6, 0xA5, 0x00, 0x5B, 0xBA, 0x3B, 0x08,
+ 0x22, 0x04, 0xC2, 0x8B, 0x91, 0x09, 0xF4, 0x69, 0xDA, 0xC9,
+ 0x2A, 0xAA, 0xB3, 0xAA, 0x7C, 0x11, 0xA1, 0xB3, 0x2A, 0xF4,
+ 0x77, 0xFA, 0x3B, 0x4B, 0x19, 0x60, 0x63, 0x06, 0x86, 0x7B,
+ 0x2A, 0xA4, 0x16, 0xD4, 0x4B, 0x01, 0x00, 0x53, 0x5A, 0x6F,
+ 0x1E, 0xF7, 0xAA, 0x09, 0xF5, 0xCA, 0x6E, 0x44, 0xF0, 0x15,
+ 0x1E, 0xC7, 0xEC, 0xD0, 0x1D, 0x7D, 0xF9, 0x5C, 0x98, 0xE8,
+ 0x5F, 0x75, 0xB1, 0xB8, 0xE0, 0x62, 0xDD, 0x9C, 0x3D, 0x6E,
+ 0x8A, 0x58, 0xFC, 0x9C, 0x06, 0x18, 0x67, 0x97, 0x9C, 0x03,
+ 0xD8, 0xE8, 0x00, 0x14, 0x34, 0x6B, 0xED, 0x25, 0xB6, 0x04,
+ 0x0C, 0x4D, 0xEE, 0x8E, 0x18, 0x6C, 0x09, 0x14, 0x40, 0x04,
+ 0x52, 0x44, 0xCC, 0x4B, 0xF9, 0x20, 0x04, 0x7E, 0x13, 0xF7,
+ 0x4D, 0x77, 0xE3, 0x94, 0x96, 0x82, 0x58, 0xB5, 0xF2, 0x6D,
+ 0xD6, 0xBF, 0x86, 0xDE, 0x3A, 0xAF, 0xA6, 0xE9, 0x18, 0x54,
+ 0x3B, 0xE2, 0x46, 0xBC, 0x68, 0x70, 0x9F, 0xEC, 0x6D, 0xE3,
+ 0x01, 0xD3, 0xCB, 0xC8, 0x98, 0x81, 0xA9, 0xBA, 0x5F, 0x95,
+ 0x76, 0x7B, 0xE3, 0xF4, 0xD0, 0x43, 0x4B, 0xC1, 0xA4, 0x57,
+ 0x95, 0x89, 0x97, 0xDE, 0x22, 0xBD, 0xA5, 0xF0, 0x75, 0x66,
+ 0x08, 0xF1, 0x38, 0x14, 0x5C, 0x1D, 0x7F, 0x17, 0x00, 0x63,
+ 0x51, 0xF2, 0xBF, 0x77, 0x65, 0x0D, 0xB0, 0x23, 0x29, 0xAA,
+ 0x5A, 0xDE, 0x08, 0x1A, 0x1C, 0x9F, 0xED, 0x31, 0xCD, 0xF3,
+ 0x03, 0xF7, 0x9F, 0x4E, 0xC0, 0xA0, 0x49, 0x9E, 0x21, 0xBF,
+ 0x65, 0x26, 0x37, 0xB6, 0x16, 0x8A, 0xE2, 0x71, 0xEF, 0x26,
+ 0xDD, 0x54, 0x10, 0xA4, 0xFA, 0x71, 0x92, 0xCE, 0xAF, 0xFF,
+ 0x26, 0xA0, 0xD2, 0x07, 0xF8, 0xA2, 0xA1, 0x61, 0xDA, 0x75,
+ 0x05, 0xDF, 0x9E, 0xAB, 0x18, 0xC8, 0xC3, 0xDC, 0xE4, 0x88,
+ 0x55, 0xF5, 0x6E, 0xA5, 0x0E, 0xD4, 0xF3, 0xF5, 0xC1, 0x12,
+ 0x71, 0x0F, 0xF9, 0x20, 0x69, 0xFF, 0xDD, 0x96, 0xE3, 0x3E,
+ 0x56, 0xC7, 0xB4, 0xE1, 0x74, 0x99, 0xFC, 0x10, 0x0E, 0x94,
+ 0xF2, 0xBA, 0xA9, 0x38, 0xE4, 0x87, 0x9B, 0x94, 0x79, 0x0E,
+ 0x71, 0x14, 0x3A, 0x49, 0x8D, 0x9B, 0x50, 0x45, 0x14, 0xD1,
+ 0x8F, 0x1E, 0x07, 0xBF, 0xB4, 0x2B, 0xAF, 0x99, 0xEB, 0x76,
+ 0x3F, 0xDA, 0x95, 0x6F, 0xB5, 0x96, 0x47, 0xE1, 0x01, 0x0C,
+ 0x16, 0x24, 0x69, 0x7C, 0x12, 0x95, 0x2C, 0x38, 0x10, 0x43,
+ 0x65, 0xFC, 0xAD, 0xEB, 0x33, 0x82, 0x8F, 0x27, 0x17, 0x52,
+ 0xEE, 0xE2, 0x9D, 0xD8, 0x53, 0x0F, 0x3F, 0xB8, 0xA0, 0x9A,
+ 0x86, 0x66, 0x51, 0x9F, 0x72, 0xF1, 0x01, 0xDB, 0x1C, 0x1F,
+ 0x30, 0x60, 0x9A, 0xBF, 0x43, 0x8C, 0x23, 0x3B, 0xCC, 0x3B,
+ 0x73, 0x6D, 0x0C, 0x3C, 0x71, 0xB3, 0xB7, 0x02, 0x10, 0x46,
+ 0xF6, 0x9C, 0x73, 0xC7, 0xB2, 0xE8, 0x54, 0x1B, 0x10, 0x03,
+ 0xA6, 0x79, 0x38, 0x03, 0x79, 0xC1, 0x5B, 0xE9, 0x1F, 0x10,
+ 0xF4, 0xD1, 0x8D, 0x91, 0x4E, 0x6C, 0x03, 0x96, 0x46, 0xB0,
+ 0xF0, 0xE7, 0x52, 0xCE, 0x10, 0x59, 0xC2, 0x65, 0xD7, 0xA3,
+ 0x46, 0xF5, 0x12, 0x6E, 0xB1, 0x96, 0xCC, 0xAB, 0xFC, 0xEA,
+ 0x6E, 0x29, 0x8E, 0x50, 0x2B, 0x67, 0xBA, 0x5A, 0x9B, 0xA7,
+ 0x8A, 0x82, 0xA6, 0x43, 0xBB, 0x18, 0xA4, 0x44, 0x08, 0x7F,
+ 0xC2, 0x31, 0xAC, 0x99, 0xA8, 0x25, 0x22, 0x80, 0x59, 0x24,
+ 0x2F, 0x77, 0x5A, 0xAF, 0x22, 0x20, 0x16, 0x96, 0x5B, 0xEF,
+ 0x81, 0x0E, 0x0A, 0xDE, 0xFC, 0x03, 0x39, 0x62, 0x79, 0xB0,
+ 0x0D, 0x9E, 0xDF, 0x6C, 0x48, 0xD7, 0xB0, 0xC7, 0x13, 0x29,
+ 0xE9, 0xD5, 0xFB, 0x78, 0x29, 0xCA, 0x39, 0xA9, 0x16, 0xC7,
+ 0x36, 0x11, 0xFC, 0xF4, 0x4E, 0x2D, 0xB8, 0xCF, 0xD4, 0x94,
+ 0xD5, 0xC4, 0x57, 0x2B, 0xF4, 0xFD, 0x24, 0x98, 0x71, 0x7B,
+ 0x0C, 0xF9, 0x43, 0x66, 0x68, 0xD5, 0x24, 0xA1, 0x5A, 0x52,
+ 0xF3, 0xA2, 0x55, 0xA9, 0x56, 0x81, 0xDF, 0xD8, 0xA3, 0x4E,
+ 0x95, 0x97, 0x01, 0xA8, 0x70, 0x8C, 0xCA, 0x8B, 0x48, 0xC2,
+ 0x34, 0x6A, 0x96, 0x58, 0x31, 0x7E, 0x7E, 0x76, 0x93, 0x5D,
+ 0x0D, 0x85, 0x74, 0xCE, 0xBF, 0xA0, 0xD5, 0xDC, 0x44, 0x45,
+ 0x85, 0x29, 0x83, 0x51, 0x45, 0x85, 0xE0, 0x2B, 0x29, 0xBF,
+ 0xBA, 0x3F, 0x41, 0xBB, 0x38, 0xAE, 0x79, 0xC5, 0x46, 0x43,
+ 0xBE, 0x25, 0xDA, 0xAA, 0x62, 0xF4, 0x7C, 0xDC, 0xC2, 0x2E,
+ 0x05, 0xDE, 0x26, 0x08, 0xA7, 0xAB, 0xE8, 0x83, 0x2D, 0x6F,
+ 0xD9, 0x41, 0x84, 0xF5, 0xE0, 0x97, 0x7B, 0x63, 0xE4, 0xE5,
+ 0xC7, 0x25, 0xEC, 0x22, 0x4A, 0x27, 0x85, 0xBB, 0x95, 0x47,
+ 0x65, 0x9E, 0xAB, 0x0A, 0x4D, 0x91, 0x07, 0x8D, 0x34, 0xC9,
+ 0xE1, 0xBF, 0xA1, 0xB8, 0xAE, 0xCE, 0x59, 0x26, 0xE6, 0xDF,
+ 0x3A, 0x83, 0x09, 0x02, 0x67, 0x7C, 0xE4, 0x65, 0xA5, 0xCC,
+ 0x11, 0xC8, 0x05, 0x55, 0xBD, 0x30, 0xC4, 0x6F, 0xAD, 0xE0,
+ 0x6F, 0x80, 0x83, 0x85, 0x4A, 0xCD, 0x3E, 0xB1, 0xF3, 0x8F,
+ 0x01, 0x8A, 0x43, 0x4A, 0x15, 0xC4, 0x75, 0x5A, 0x30, 0xCC,
+ 0x8A, 0xCE, 0xF8, 0x46, 0xEB, 0x7A, 0xC3, 0xBA, 0x51, 0x48,
+ 0xA1, 0x8A, 0xE2, 0xCF, 0x9C, 0x28, 0x9D, 0x27, 0x3E, 0x85,
+ 0xF7, 0xFB, 0x54, 0xCD, 0xC6, 0xDF, 0xF2, 0x51, 0x5F, 0xE4,
+ 0xB7, 0xC5, 0xFB, 0x6A, 0x52, 0xAB, 0x60, 0x36, 0x45, 0x0F,
+ 0xBD, 0xC5, 0xE9, 0x75, 0xD5, 0xDF, 0xB3, 0x10, 0x5F, 0x6F,
+ 0xB5, 0x34, 0xAD, 0x91, 0x68, 0x0E, 0x8D, 0xED, 0xA8, 0x93,
+ 0x6D, 0x44, 0x00, 0xB6, 0xC2, 0x48, 0x28, 0xDE, 0xAA, 0xB1,
+ 0xCC, 0x97, 0xCF, 0x8D, 0x8F, 0x87, 0x8F, 0xD7, 0x50, 0xA5,
+ 0x5B, 0x4C, 0xAC, 0xA5, 0x5D, 0x7A, 0xC4, 0xB6, 0x5E, 0x1A,
+ 0x40, 0x70, 0xE6, 0x9F, 0x94, 0x08, 0xA5, 0x0F, 0x81, 0xC7,
+ 0x11, 0x12, 0xDF, 0xBA, 0x51, 0x49, 0x9B, 0xAA, 0x5A, 0xE0,
+ 0xFC, 0x4E, 0x58, 0x67, 0x2A, 0xC0, 0x4F, 0xDD, 0xF0, 0x2E,
+ 0x02, 0x0E, 0xC1, 0xD2, 0x14, 0x20, 0xF9, 0x24, 0x6D, 0x68,
+ 0x66, 0x4E, 0xDF, 0x82, 0x07, 0xE0, 0x09, 0xA0, 0x13, 0xC5,
+ 0x7C, 0x22, 0x3D, 0x76, 0x1D, 0x67, 0x37, 0x6D, 0xCB, 0xE3,
+ 0x75, 0xDD, 0x41, 0x72, 0x33, 0xA0, 0x3D, 0xEC, 0xB9, 0x70,
+ 0xE2, 0xFA, 0xDE, 0x5B, 0x5A, 0x28, 0xCB, 0x71, 0xC1, 0x3B,
+ 0x01, 0xC0, 0x3E, 0xC4, 0x9E, 0x82, 0x73, 0xF5, 0xDB, 0x94,
+ 0x18, 0xB4, 0xDA, 0x2A, 0xE2, 0xEE, 0x9F, 0xC2, 0xAA, 0x2E,
+ 0x5C, 0x56, 0xCB, 0x6E, 0xF1, 0xD6, 0xCC, 0x2D, 0xB3, 0xD5,
+ 0x3F, 0xC1, 0x6C, 0x83, 0xE8, 0xEF, 0xA4, 0xDB, 0x22, 0xB9,
+ 0x1F, 0x1D, 0x7F, 0x77, 0xA7, 0x7F, 0xAF, 0x29, 0x0C, 0x1F,
+ 0xA3, 0x0C, 0x68, 0x3D, 0xF1, 0x6B, 0xA7, 0xA7, 0x7B, 0xB8,
+ 0x47, 0x74, 0x4C, 0xDB, 0x5D, 0xF5, 0xC2, 0xCA, 0xD9, 0xE9,
+ 0xF2, 0x5A, 0x0A, 0xF0, 0x48, 0x55, 0x65, 0x43, 0x6E, 0xCC,
+ 0x82, 0xA1, 0x6F, 0xAE, 0x67, 0x8D, 0x1D, 0x9A, 0x09, 0xB6,
+ 0xB0, 0xF2, 0x10, 0xB7, 0xAF, 0x31, 0xDB, 0x00, 0x14, 0x7E,
+ 0xC4, 0x14, 0xAB, 0x81, 0xA5, 0xF6, 0xBB, 0x75, 0x9B, 0xDD,
+ 0xE8, 0x7E, 0x09, 0x2F, 0x58, 0x3D, 0xE0, 0xAD, 0x15, 0xA2,
+ 0x1E, 0xEB, 0xB2, 0x02, 0x95, 0x04, 0x32, 0x6A, 0xEE, 0x8B,
+ 0x25, 0x32, 0xED, 0xC5, 0x14, 0xD5, 0xF7, 0x15, 0x1F, 0x00,
+ 0xD1, 0xB7, 0xE5, 0xE8, 0xAA, 0xB1, 0xA4, 0xE1, 0x5C, 0x07,
+ 0xA1, 0x2D, 0xEF, 0x2F, 0xCB, 0x11, 0x5E, 0xC4, 0x9B, 0x2E,
+ 0x9E, 0x7F, 0x3E, 0x0F, 0xDD, 0x62, 0xF6, 0xB3, 0xE2, 0xEE,
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xEE, 0xEE, 0xDE, 0xAD, 0xBE, 0xEF
+};
+
+/* Test ones complement sum with IPv4 headers */
+static void chksum_ones_complement_ip(void)
+{
+ int i;
+ uint16_t sum, res;
+
+ for (i = 0; i < NUM_IP_HDR; i++) {
+ sum = odp_chksum_ones_comp16(ip_hdr_test_vect[i], IP_HDR_LEN);
+ res = ~sum;
+
+ CU_ASSERT(res == 0);
+ }
+}
+
+/* Test ones complement sum with various length pseudo UDP packets */
+static void chksum_ones_complement_udp(void)
+{
+ int i;
+ uint16_t sum, res;
+
+ for (i = 0; i < NUM_UDP; i++) {
+ sum = odp_chksum_ones_comp16(udp_test_vect[i].data,
+ udp_test_vect[i].len);
+ res = ~sum;
+
+ CU_ASSERT(res == 0);
+ }
+}
+
+/* Test ones complement sum with a long pseudo UDP packet */
+static void chksum_ones_complement_udp_long(void)
+{
+ int i;
+ uint16_t sum, res;
+ uint32_t offset, frag_sum;
+ uint32_t len = sizeof(udp_test_vect_long) - UDP_LONG_PADDING;
+ int num_frag = 7;
+ uint32_t frag_len = len / num_frag;
+
+ /* Checksum all data */
+ sum = odp_chksum_ones_comp16(udp_test_vect_long, len);
+ res = ~sum;
+
+ CU_ASSERT(res == UDP_LONG_CHKSUM);
+
+ /* Checksum data in fragments */
+ frag_sum = 0;
+ offset = 0;
+
+ for (i = 0; i < num_frag; i++) {
+ if (i == num_frag - 1)
+ frag_len = len - offset;
+
+ /* Check that test passes 16 bit aligned addresses */
+ CU_ASSERT_FATAL((offset % 2) == 0);
+
+ frag_sum += odp_chksum_ones_comp16(&udp_test_vect_long[offset],
+ frag_len);
+
+ offset += frag_len;
+ }
+
+ /* Fold 32-bit sum to 16 bits */
+ while (frag_sum >> 16)
+ frag_sum = (frag_sum & 0xffff) + (frag_sum >> 16);
+
+ res = ~frag_sum;
+
+ CU_ASSERT(res == UDP_LONG_CHKSUM);
+}
+
+static uint16_t chksum_rfc1071(const void *p, uint32_t len)
+{
+ uint32_t sum = 0;
+ const uint16_t *data = p;
+
+ while (len > 1) {
+ sum += *data++;
+ len -= 2;
+ }
+
+ /* Add left-over byte, if any */
+ if (len > 0) {
+ uint16_t left_over = 0;
+
+ *(uint8_t *)&left_over = *(const uint8_t *)data;
+ sum += left_over;
+ }
+
+ /* Fold 32-bit sum to 16 bits */
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return sum;
+}
+
+/*
+ * 64-bit KISS RNGs
+ * George Marsaglia
+ * https://www.thecodingforums.com/threads/64-bit-kiss-rngs.673657
+ */
+
+static unsigned long long x = 1234567890987654321ULL, c = 123456123456123456ULL,
+ y = 362436362436362436ULL, z = 1066149217761810ULL, t;
+
+#define MWC (t = (x << 58) + c, c = (x >> 6), x += t, c += (x < t), x)
+#define XSH (y ^= (y << 13), y ^= (y >> 17), y ^= (y << 43))
+#define CNG (z = 6906969069LL * z + 1234567)
+#define KISS (MWC + XSH + CNG)
+
+/*
+ * Test with pseudorandom data and different data lengths and alignments.
+ */
+static void chksum_ones_complement_pseudorandom(void)
+{
+ const int size = 32 * 1024;
+ const unsigned long page = 4096;
+ /* Allocate some extra pages for alignment and length. */
+ uint8_t *buf = (uint8_t *)malloc(size + page * 4);
+ uint8_t *data = (uint8_t *)(((uintptr_t)buf + (page - 1)) & ~(page - 1));
+
+ for (int i = 0; i < (size + (int)page * 3) / 8; i++)
+ ((uint64_t *)(uintptr_t)data)[i] = KISS;
+
+ /* Test data lengths from 1 to more than 9000 bytes. */
+ for (int len = 1; len < 10000; len++) {
+ /*
+ * To avoid spending too much time on long data, the number of
+ * rounds goes down as data length goes up.
+ */
+ int rounds = 1000000000 / (len * len + 1000000);
+
+ for (int i = 0; i < rounds; i++) {
+ /* Align p to two bytes. */
+ uint8_t *p = data + (KISS & (size - 1) & ~1UL);
+ /*
+ * Generate some fresh random bits at the start of the
+ * data to be checksummed.
+ */
+ uint64_t rnd = KISS;
+
+ memcpy(p, &rnd, sizeof(rnd));
+ CU_ASSERT(chksum_rfc1071(p, len) ==
+ odp_chksum_ones_comp16(p, len));
+ }
+ }
+
+ free(buf);
+}
+
+/*
+ * Test with very long data with most of the bits set. The idea is to
+ * maximize the number of carries.
+ */
+static void chksum_ones_complement_very_long(void)
+{
+ const int size = 64 * 1024;
+ const unsigned long page = 4096;
+ /* Allocate two extra pages for alignment. */
+ uint8_t *buf = (uint8_t *)malloc(size + page * 2);
+ uint8_t *data = (uint8_t *)(((uintptr_t)buf + (page - 1)) & ~(page - 1));
+
+ /* Start with all bits set. */
+ memset(data, 0xff, size + page);
+
+ for (int i = 0; i < 100; i++) {
+ for (int len = size - 8; len <= size; len++) {
+ /* Alignment 0, 2, 4, 6, 8. */
+ for (int a = 0; a <= 8; a += 2)
+ CU_ASSERT(chksum_rfc1071(data + a, len) ==
+ odp_chksum_ones_comp16(data + a, len));
+ }
+
+ /* Turn off some random bits in the data. */
+ uint64_t rnd = KISS;
+ ((uint8_t *)data)[rnd & (size - 1)] &= (rnd >> 32) & 0xff;
+ }
+
+ free(buf);
+}
+
+odp_testinfo_t chksum_suite[] = {
+ ODP_TEST_INFO(chksum_ones_complement_ip),
+ ODP_TEST_INFO(chksum_ones_complement_udp),
+ ODP_TEST_INFO(chksum_ones_complement_udp_long),
+ ODP_TEST_INFO(chksum_ones_complement_pseudorandom),
+ ODP_TEST_INFO(chksum_ones_complement_very_long),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t chksum_suites[] = {
+ {"Checksum", NULL, NULL, chksum_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(chksum_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/classification/.gitignore b/test/validation/api/classification/.gitignore
index e2cdfefe1..e2cdfefe1 100644
--- a/test/common_plat/validation/api/classification/.gitignore
+++ b/test/validation/api/classification/.gitignore
diff --git a/test/validation/api/classification/Makefile.am b/test/validation/api/classification/Makefile.am
new file mode 100644
index 000000000..e17f9f654
--- /dev/null
+++ b/test/validation/api/classification/Makefile.am
@@ -0,0 +1,11 @@
+include ../Makefile.inc
+
+test_PROGRAMS = classification_main
+classification_main_SOURCES = \
+ odp_classification_basic.c \
+ odp_classification_tests.c \
+ odp_classification_testsuites.h \
+ odp_classification_test_pmr.c \
+ odp_classification_common.c \
+ classification.c \
+ classification.h
diff --git a/test/validation/api/classification/classification.c b/test/validation/api/classification/classification.c
new file mode 100644
index 000000000..ef975c237
--- /dev/null
+++ b/test/validation/api/classification/classification.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include "odp_classification_testsuites.h"
+#include "classification.h"
+
+odp_suiteinfo_t classification_suites[] = {
+ { .name = "classification basic",
+ .testinfo_tbl = classification_suite_basic,
+ },
+ { .name = "classification pmr tests",
+ .testinfo_tbl = classification_suite_pmr,
+ .init_func = classification_suite_pmr_init,
+ .term_func = classification_suite_pmr_term,
+ },
+ { .name = "classification tests",
+ .testinfo_tbl = classification_suite,
+ .init_func = classification_suite_init,
+ .term_func = classification_suite_term,
+ },
+ { .name = "classification packet vector tests",
+ .testinfo_tbl = classification_suite_pktv,
+ .init_func = classification_suite_pktv_init,
+ .term_func = classification_suite_pktv_term,
+ },
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(classification_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/classification/classification.h b/test/validation/api/classification/classification.h
new file mode 100644
index 000000000..1f66b832a
--- /dev/null
+++ b/test/validation/api/classification/classification.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef _ODP_TEST_CLASSIFICATION_H_
+#define _ODP_TEST_CLASSIFICATION_H_
+
+#include <odp_cunit_common.h>
+
+#define SHM_PKT_NUM_BUFS 32
+#define SHM_PKT_BUF_SIZE 1024
+
+/* Config values for Default CoS */
+#define TEST_DEFAULT 1
+#define CLS_DEFAULT 0
+#define CLS_DEFAULT_SADDR "10.0.0.1/32"
+#define CLS_DEFAULT_DADDR "10.0.0.100/32"
+#define CLS_DEFAULT_SPORT 1024
+#define CLS_DEFAULT_DPORT 2048
+#define CLS_DEFAULT_DMAC {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}
+#define CLS_DEFAULT_SMAC {0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}
+#define CLS_MAGIC_VAL 0xdeadbeef
+
+/* Config values for Drop CoS */
+#define TEST_DROP 1
+#define CLS_DROP 6
+#define CLS_DROP_PORT 4001
+
+/* Config values for Error CoS */
+#define TEST_ERROR 1
+#define CLS_ERROR 1
+
+/* Config values for PMR_CHAIN */
+#define TEST_PMR_CHAIN 1
+#define CLS_PMR_CHAIN_SRC 2
+#define CLS_PMR_CHAIN_DST 3
+#define CLS_PMR_CHAIN_SADDR "10.0.0.5/32"
+#define CLS_PMR_CHAIN_PORT 3000
+
+/* Config values for PMR */
+#define TEST_PMR 1
+#define CLS_PMR 4
+#define CLS_PMR_PORT 4000
+
+/* Config values for PMR SET */
+#define TEST_PMR_SET 1
+#define CLS_PMR_SET 5
+#define CLS_PMR_SET_SADDR "10.0.0.6/32"
+#define CLS_PMR_SET_PORT 5000
+
+/* Config values for CoS L2 Priority */
+#define TEST_L2_QOS 1
+#define CLS_L2_QOS_0 7
+#define CLS_L2_QOS_MAX 5
+
+#define CLS_ENTRIES (CLS_L2_QOS_0 + CLS_L2_QOS_MAX)
+
+/* Test Packet values */
+#define DATA_MAGIC 0x01020304
+#define TEST_SEQ_INVALID ((uint32_t)~0)
+
+/* Test packet Time-to-live */
+#define DEFAULT_TTL 128
+
+/* Test packet default DSCP value */
+#define LOW_DROP_PRECEDENCE 0x02
+#define MEDIUM_DROP_PRECEDENCE 0x04
+#define HIGH_DROP_PRECEDENCE 0x06
+#define DROP_PRECEDENCE_MASK 0x06
+#define DSCP_CLASS1 0x08
+#define DSCP_CLASS2 0x10
+#define DSCP_CLASS3 0x18
+#define DSCP_CLASS4 0x20
+#define DEFAULT_DSCP (DSCP_CLASS2 | LOW_DROP_PRECEDENCE)
+
+/* Test packet default ECN */
+#define DEFAULT_ECN ODPH_IP_ECN_ECT0
+
+/* Test packet default TOS */
+#define DEFAULT_TOS ((DEFAULT_DSCP << ODPH_IP_TOS_DSCP_SHIFT) | \
+ DEFAULT_ECN)
+
+#endif
diff --git a/test/validation/api/classification/odp_classification_basic.c b/test/validation/api/classification/odp_classification_basic.c
new file mode 100644
index 000000000..b5ccdcfea
--- /dev/null
+++ b/test/validation/api/classification/odp_classification_basic.c
@@ -0,0 +1,751 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp_cunit_common.h>
+#include "odp_classification_testsuites.h"
+#include "classification.h"
+
+/* Limit handle array allocation from stack to about 256kB */
+#define MAX_HANDLES (32 * 1024)
+
+static void test_defaults(uint8_t fill)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_pmr_param_t pmr_param;
+
+ memset(&cos_param, fill, sizeof(cos_param));
+ odp_cls_cos_param_init(&cos_param);
+
+ CU_ASSERT(cos_param.action == ODP_COS_ACTION_ENQUEUE);
+ CU_ASSERT(cos_param.num_queue == 1);
+ CU_ASSERT(cos_param.stats_enable == false);
+ CU_ASSERT(cos_param.red.enable == false);
+ CU_ASSERT(cos_param.bp.enable == false);
+ CU_ASSERT(cos_param.vector.enable == false);
+
+ memset(&pmr_param, fill, sizeof(pmr_param));
+ odp_cls_pmr_param_init(&pmr_param);
+ CU_ASSERT(pmr_param.range_term == false);
+}
+
+static void cls_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void cls_create_cos(void)
+{
+ odp_cos_t cos;
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t pool;
+ odp_queue_t queue;
+
+ pool = pool_create("cls_basic_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ queue = queue_create("cls_basic_queue", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(NULL, &cls_param);
+ CU_ASSERT(odp_cos_to_u64(cos) != odp_cos_to_u64(ODP_COS_INVALID));
+ odp_cos_destroy(cos);
+ odp_pool_destroy(pool);
+ odp_queue_destroy(queue);
+}
+
+static void cls_create_cos_max_common(odp_bool_t stats)
+{
+ uint32_t i, num;
+ odp_cls_cos_param_t cls_param;
+ odp_cls_capability_t capa;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+
+ num = capa.max_cos;
+ if (num > MAX_HANDLES)
+ num = MAX_HANDLES;
+
+ if (stats && capa.max_cos_stats < num)
+ num = capa.max_cos_stats;
+
+ odp_cos_t cos[num];
+
+ for (i = 0; i < num; i++) {
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.action = ODP_COS_ACTION_DROP;
+ cls_param.stats_enable = stats;
+
+ cos[i] = odp_cls_cos_create(NULL, &cls_param);
+ if (cos[i] == ODP_COS_INVALID) {
+ ODPH_ERR("odp_cls_cos_create() failed at CoS %u out of %u.\n", i + 1, num);
+ break;
+ }
+ }
+
+ CU_ASSERT(i == num);
+
+ for (uint32_t j = 0; j < i; j++)
+ CU_ASSERT(!odp_cos_destroy(cos[j]));
+}
+
+static int cos_create_multi(const char *name[], const odp_cls_cos_param_t param[], odp_cos_t cos[],
+ uint32_t num)
+{
+ const uint32_t max_retries = 100;
+ uint32_t num_created = 0;
+ uint32_t num_retries = 0;
+
+ do {
+ const char **cur_name = (name != NULL) ? &name[num_created] : NULL;
+ int ret = odp_cls_cos_create_multi(cur_name, &param[num_created],
+ &cos[num_created], num - num_created);
+ if (ret < 0) {
+ CU_FAIL("CoS create multi failed");
+ break;
+ }
+ num_retries = (ret == 0) ? num_retries + 1 : 0;
+ num_created += ret;
+ } while (num_created < num && num_retries < max_retries);
+
+ return num_created;
+}
+
+static void cos_destroy_multi(odp_cos_t cos[], uint32_t num)
+{
+ uint32_t num_left = num;
+ uint32_t num_freed = 0;
+
+ while (num_left) {
+ int ret = odp_cos_destroy_multi(&cos[num_freed], num_left);
+
+ CU_ASSERT_FATAL(ret > 0 && (uint32_t)ret <= num_left);
+
+ num_left -= ret;
+ num_freed += ret;
+ }
+ CU_ASSERT_FATAL(num_freed == num);
+}
+
+static void cls_create_cos_multi(void)
+{
+ odp_cls_cos_param_t param_single;
+ odp_cls_cos_param_t param[MAX_HANDLES];
+ odp_cls_capability_t capa;
+ odp_cos_t cos[MAX_HANDLES];
+ const char *name[MAX_HANDLES] = {NULL, "aaa", NULL, "bbb", "ccc", NULL, "ddd"};
+ uint32_t num, num_created;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+ CU_ASSERT_FATAL(capa.max_cos);
+
+ num = capa.max_cos < MAX_HANDLES ? capa.max_cos : MAX_HANDLES;
+
+ for (uint32_t i = 0; i < num; i++) {
+ odp_cls_cos_param_init(&param[i]);
+ param[i].action = ODP_COS_ACTION_DROP;
+ }
+ odp_cls_cos_param_init(&param_single);
+ param_single.action = ODP_COS_ACTION_DROP;
+
+ num_created = cos_create_multi(NULL, &param_single, cos, 1);
+ CU_ASSERT(num_created == 1)
+ cos_destroy_multi(cos, num_created);
+
+ num_created = cos_create_multi(name, param, cos, num);
+ CU_ASSERT(num_created == num)
+ cos_destroy_multi(cos, num_created);
+
+ num_created = cos_create_multi(NULL, param, cos, num);
+ CU_ASSERT(num_created == num)
+ cos_destroy_multi(cos, num_created);
+}
+
+static void cls_create_cos_max(void)
+{
+ cls_create_cos_max_common(false);
+}
+
+static void cls_create_cos_max_stats(void)
+{
+ cls_create_cos_max_common(true);
+}
+
+static void cls_destroy_cos(void)
+{
+ odp_cos_t cos;
+ char name[ODP_COS_NAME_LEN];
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_cls_cos_param_t cls_param;
+ int retval;
+
+ pool = pool_create("cls_basic_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ queue = queue_create("cls_basic_queue", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ sprintf(name, "ClassOfService");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(name, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+ retval = odp_cos_destroy(cos);
+ CU_ASSERT(retval == 0);
+ retval = odp_cos_destroy(ODP_COS_INVALID);
+ CU_ASSERT(retval < 0);
+
+ odp_pool_destroy(pool);
+ odp_queue_destroy(queue);
+}
+
+static void cls_create_pmr_match(void)
+{
+ odp_pmr_t pmr;
+ uint16_t val;
+ uint16_t mask;
+ int retval;
+ odp_pmr_param_t pmr_param;
+ odp_cos_t default_cos;
+ odp_cos_t cos;
+ odp_queue_t default_queue;
+ odp_queue_t queue;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_pool_t pkt_pool;
+ odp_cls_cos_param_t cls_param;
+ odp_pktio_t pktio;
+
+ pkt_pool = pool_create("pkt_pool");
+ CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID);
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("pmr_match", true);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("pmr_match");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create("pmr_match", &cls_param);
+ CU_ASSERT(cos != ODP_COS_INVALID);
+
+ val = 1024;
+ mask = 0xffff;
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = find_first_supported_l3_pmr();
+ pmr_param.range_term = false;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+ CU_ASSERT(odp_pmr_to_u64(pmr) != odp_pmr_to_u64(ODP_PMR_INVALID));
+ /* destroy the created PMR */
+ retval = odp_cls_pmr_destroy(pmr);
+ CU_ASSERT(retval == 0);
+
+ /* destroy an INVALID PMR */
+ retval = odp_cls_pmr_destroy(ODP_PMR_INVALID);
+ CU_ASSERT(retval < 0);
+
+ odp_cos_destroy(cos);
+ odp_queue_destroy(queue);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(pkt_pool);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ odp_queue_destroy(default_queue);
+ odp_pool_destroy(default_pool);
+ odp_pktio_close(pktio);
+}
+
+/* Create maximum number of PMRs into the default CoS */
+static void cls_max_pmr_from_default_action(int drop)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_queue_param_t queue_param;
+ odp_cls_capability_t capa;
+ odp_schedule_capability_t sched_capa;
+ odp_pmr_param_t pmr_param;
+ odp_pool_t pool;
+ odp_pktio_t pktio;
+ odp_cos_t default_cos;
+ uint32_t i, num_cos, num_pmr;
+ int ret;
+ uint32_t cos_created = 0;
+ uint32_t queue_created = 0;
+ uint32_t pmr_created = 0;
+ uint16_t val = 1024;
+ uint16_t mask = 0xffff;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+
+ CU_ASSERT_FATAL(odp_schedule_capability(&sched_capa) == 0);
+
+ pool = pool_create("pkt_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ num_cos = capa.max_cos;
+
+ if (num_cos > sched_capa.max_queues)
+ num_cos = sched_capa.max_queues;
+
+ if (num_cos > MAX_HANDLES)
+ num_cos = MAX_HANDLES;
+
+ CU_ASSERT_FATAL(num_cos > 1);
+
+ num_pmr = capa.max_pmr_per_cos;
+
+ odp_cos_t cos[num_cos];
+ odp_queue_t queue[num_cos];
+ odp_pmr_t pmr[num_pmr];
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+
+ odp_cls_cos_param_init(&cos_param);
+ if (drop)
+ cos_param.action = ODP_COS_ACTION_DROP;
+
+ for (i = 0; i < num_cos; i++) {
+ if (!drop) {
+ queue[i] = odp_queue_create(NULL, &queue_param);
+
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ ODPH_ERR("odp_queue_create() failed %u / %u\n", i + 1, num_cos);
+ break;
+ }
+
+ cos_param.queue = queue[i];
+ queue_created++;
+ }
+
+ cos[i] = odp_cls_cos_create(NULL, &cos_param);
+
+ if (cos[i] == ODP_COS_INVALID) {
+ ODPH_ERR("odp_cls_cos_create() failed %u / %u\n", i + 1, num_cos);
+ break;
+ }
+
+ cos_created++;
+ }
+
+ if (!drop)
+ CU_ASSERT(queue_created == num_cos);
+
+ CU_ASSERT(cos_created == num_cos);
+
+ if (cos_created != num_cos)
+ goto destroy_cos;
+
+ default_cos = cos[0];
+
+ ret = odp_pktio_default_cos_set(pktio, default_cos);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = find_first_supported_l3_pmr();
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ for (i = 0; i < num_pmr; i++) {
+ pmr[i] = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos[i + 1]);
+
+ if (pmr[i] == ODP_PMR_INVALID) {
+ ODPH_ERR("odp_cls_pmr_create() failed %u / %u\n", i + 1, num_pmr);
+ break;
+ }
+
+ val++;
+ pmr_created++;
+ }
+
+ printf("\n Number of CoS created: %u\n Number of PMR created: %u\n", cos_created,
+ pmr_created);
+
+ CU_ASSERT(pmr_created == num_pmr);
+
+ for (i = 0; i < pmr_created; i++)
+ CU_ASSERT(odp_cls_pmr_destroy(pmr[i]) == 0);
+
+ ret = odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ CU_ASSERT_FATAL(ret == 0);
+
+destroy_cos:
+ for (i = 0; i < cos_created; i++)
+ CU_ASSERT(odp_cos_destroy(cos[i]) == 0);
+
+ for (i = 0; i < queue_created; i++)
+ CU_ASSERT(odp_queue_destroy(queue[i]) == 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void cls_max_pmr_from_default_drop(void)
+{
+ cls_max_pmr_from_default_action(1);
+}
+
+static void cls_max_pmr_from_default_enqueue(void)
+{
+ cls_max_pmr_from_default_action(0);
+}
+
+static void cls_create_pmr_multi(void)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_cls_capability_t capa;
+ odp_pool_t pool;
+ odp_pktio_t pktio;
+ uint32_t i, num_cos, num_pmr, num_left;
+ int ret;
+ const uint32_t max_retries = 100;
+ uint32_t num_retries = 0;
+ uint32_t num_freed = 0;
+ uint32_t cos_created = 0;
+ uint32_t pmr_created = 0;
+ uint16_t mask = 0xffff;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+
+ pool = pool_create("pkt_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ num_cos = capa.max_cos;
+ if (num_cos > MAX_HANDLES)
+ num_cos = MAX_HANDLES;
+
+ CU_ASSERT_FATAL(num_cos > 1);
+
+ num_pmr = num_cos - 1;
+ if (num_pmr > capa.max_pmr)
+ num_pmr = capa.max_pmr;
+
+ odp_cos_t src_cos[num_cos];
+ odp_cos_t cos[num_cos];
+ odp_pmr_t pmr[num_pmr];
+ odp_pmr_create_opt_t pmr_opt[num_pmr];
+ odp_pmr_param_t pmr_param[num_pmr];
+ uint16_t val[num_pmr];
+
+ odp_cls_cos_param_init(&cos_param);
+ cos_param.action = ODP_COS_ACTION_DROP;
+
+ for (i = 0; i < num_cos; i++) {
+ cos[i] = odp_cls_cos_create(NULL, &cos_param);
+
+ if (cos[i] == ODP_COS_INVALID) {
+ ODPH_ERR("odp_cls_cos_create() failed %u / %u\n", i + 1, num_cos);
+ break;
+ }
+ /* Same source CoS used for all PMRs */
+ src_cos[i] = cos[0];
+
+ cos_created++;
+ }
+
+ CU_ASSERT(cos_created == num_cos);
+
+ if (cos_created != num_cos)
+ goto destroy_cos;
+
+ ret = odp_pktio_default_cos_set(pktio, cos[0]);
+ CU_ASSERT_FATAL(ret == 0);
+
+ for (i = 0; i < num_pmr; i++) {
+ val[i] = 1024 + i;
+
+ odp_cls_pmr_param_init(&pmr_param[i]);
+ pmr_param[i].term = find_first_supported_l3_pmr();
+ pmr_param[i].match.value = &val[i];
+ pmr_param[i].match.mask = &mask;
+ pmr_param[i].val_sz = sizeof(val[i]);
+
+ odp_cls_pmr_create_opt_init(&pmr_opt[i]);
+ pmr_opt[i].terms = &pmr_param[i];
+ pmr_opt[i].num_terms = 1;
+ }
+
+ do {
+ ret = odp_cls_pmr_create_multi(&pmr_opt[pmr_created],
+ &src_cos[pmr_created],
+ &cos[pmr_created + 1],
+ &pmr[pmr_created],
+ num_pmr - pmr_created);
+ CU_ASSERT_FATAL(ret <= (int)(num_pmr - pmr_created));
+
+ if (ret < 0)
+ break;
+
+ num_retries = (ret == 0) ? num_retries + 1 : 0;
+ pmr_created += ret;
+ } while (pmr_created < num_pmr && num_retries < max_retries);
+
+ CU_ASSERT(pmr_created > 0);
+
+ num_left = pmr_created;
+ while (num_left) {
+ ret = odp_cls_pmr_destroy_multi(&pmr[num_freed], num_left);
+
+ CU_ASSERT_FATAL(ret > 0 && (uint32_t)ret <= num_left);
+
+ num_left -= ret;
+ num_freed += ret;
+ }
+
+ ret = odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ CU_ASSERT_FATAL(ret == 0);
+
+destroy_cos:
+ for (i = 0; i < cos_created; i++)
+ CU_ASSERT(odp_cos_destroy(cos[i]) == 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void cls_cos_set_queue(void)
+{
+ int retval;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_queue_t queue_cos;
+ odp_cos_t cos_queue;
+ odp_queue_t recvqueue;
+ odp_queue_t queue_out = ODP_QUEUE_INVALID;
+
+ pool = pool_create("cls_basic_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ queue = queue_create("cls_basic_queue", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ sprintf(cosname, "CoSQueue");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos_queue = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_queue != ODP_COS_INVALID);
+
+ queue_cos = queue_create("QueueCoS", true);
+ CU_ASSERT_FATAL(queue_cos != ODP_QUEUE_INVALID);
+
+ retval = odp_cos_queue_set(cos_queue, queue_cos);
+ CU_ASSERT(retval == 0);
+ recvqueue = odp_cos_queue(cos_queue);
+ CU_ASSERT(recvqueue == queue_cos);
+ CU_ASSERT(odp_cls_cos_num_queue(cos_queue) == 1);
+ CU_ASSERT(odp_cls_cos_queues(cos_queue, &queue_out, 1) == 1);
+ CU_ASSERT(queue_out == queue_cos);
+
+ odp_cos_destroy(cos_queue);
+ odp_queue_destroy(queue_cos);
+ odp_queue_destroy(queue);
+ odp_pool_destroy(pool);
+}
+
+static void cls_cos_set_pool(void)
+{
+ int retval;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_pool_t cos_pool;
+ odp_cos_t cos;
+ odp_pool_t recvpool;
+
+ pool = pool_create("cls_basic_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ queue = queue_create("cls_basic_queue", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ sprintf(cosname, "CoSQueue");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ cos_pool = pool_create("PoolCoS");
+ CU_ASSERT_FATAL(cos_pool != ODP_POOL_INVALID);
+
+ retval = odp_cls_cos_pool_set(cos, cos_pool);
+ CU_ASSERT(retval == 0);
+ recvpool = odp_cls_cos_pool(cos);
+ CU_ASSERT(recvpool == cos_pool);
+
+ odp_cos_destroy(cos);
+ odp_queue_destroy(queue);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(cos_pool);
+}
+
+static void cls_pmr_composite_create(void)
+{
+ odp_cls_capability_t capa;
+ odp_pmr_t pmr_composite;
+ int retval;
+ odp_cos_t default_cos;
+ odp_cos_t cos;
+ odp_queue_t default_queue;
+ odp_queue_t queue;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_pool_t pkt_pool;
+ odp_cls_cos_param_t cls_param;
+ odp_pktio_t pktio;
+ uint32_t max_terms_per_pmr;
+ uint16_t val = 1024;
+ uint16_t mask = 0xffff;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+
+ pkt_pool = pool_create("pkt_pool");
+ CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID);
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("pmr_match", true);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("pmr_match");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create("pmr_match", &cls_param);
+ CU_ASSERT(cos != ODP_COS_INVALID);
+
+ max_terms_per_pmr = capa.max_terms_per_pmr;
+ odp_pmr_param_t pmr_terms[max_terms_per_pmr];
+
+ for (uint32_t i = 0; i < max_terms_per_pmr; i++) {
+ odp_cls_pmr_param_init(&pmr_terms[i]);
+ pmr_terms[i].term = ODP_PMR_TCP_DPORT;
+ pmr_terms[i].match.value = &val;
+ pmr_terms[i].range_term = false;
+ pmr_terms[i].match.mask = &mask;
+ pmr_terms[i].val_sz = sizeof(val);
+ }
+
+ pmr_composite = odp_cls_pmr_create(pmr_terms, max_terms_per_pmr, default_cos, cos);
+ CU_ASSERT(odp_pmr_to_u64(pmr_composite) !=
+ odp_pmr_to_u64(ODP_PMR_INVALID));
+
+ printf("\n");
+ odp_cls_print_all();
+
+ retval = odp_cls_pmr_destroy(pmr_composite);
+ CU_ASSERT(retval == 0);
+
+ odp_cos_destroy(cos);
+ odp_queue_destroy(queue);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(pkt_pool);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ odp_queue_destroy(default_queue);
+ odp_pool_destroy(default_pool);
+ odp_pktio_close(pktio);
+}
+
+static void cls_create_cos_with_hash_queues(void)
+{
+ odp_pool_t pool;
+ odp_cls_capability_t capa;
+ int ret;
+ odp_queue_param_t q_param;
+ odp_cls_cos_param_t cls_param;
+ odp_cos_t cos;
+
+ pool = pool_create("cls_basic_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ ret = odp_cls_capability(&capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT_FATAL(capa.hash_protocols.all_bits != 0);
+
+ odp_queue_param_init(&q_param);
+ q_param.type = ODP_QUEUE_TYPE_SCHED;
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.num_queue = capa.max_hash_queues;
+ cls_param.queue_param = q_param;
+ cls_param.hash_proto.all_bits = capa.hash_protocols.all_bits;
+ cls_param.pool = pool;
+
+ cos = odp_cls_cos_create(NULL, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ ret = odp_cos_destroy(cos);
+ CU_ASSERT(ret == 0);
+
+ odp_pool_destroy(pool);
+}
+
+static int check_capa_cos_hashing(void)
+{
+ odp_cls_capability_t capa;
+
+ if (odp_cls_capability(&capa) < 0)
+ return ODP_TEST_INACTIVE;
+
+ return capa.max_hash_queues > 1 ? ODP_TEST_ACTIVE : ODP_TEST_INACTIVE;
+}
+
+odp_testinfo_t classification_suite_basic[] = {
+ ODP_TEST_INFO(cls_default_values),
+ ODP_TEST_INFO(cls_create_cos),
+ ODP_TEST_INFO(cls_create_cos_multi),
+ ODP_TEST_INFO(cls_create_cos_max),
+ ODP_TEST_INFO(cls_create_cos_max_stats),
+ ODP_TEST_INFO(cls_destroy_cos),
+ ODP_TEST_INFO(cls_create_pmr_match),
+ ODP_TEST_INFO(cls_create_pmr_multi),
+ ODP_TEST_INFO(cls_max_pmr_from_default_drop),
+ ODP_TEST_INFO(cls_max_pmr_from_default_enqueue),
+ ODP_TEST_INFO(cls_cos_set_queue),
+ ODP_TEST_INFO(cls_cos_set_pool),
+ ODP_TEST_INFO(cls_pmr_composite_create),
+ ODP_TEST_INFO_CONDITIONAL(cls_create_cos_with_hash_queues, check_capa_cos_hashing),
+ ODP_TEST_INFO_NULL,
+};
diff --git a/test/validation/api/classification/odp_classification_common.c b/test/validation/api/classification/odp_classification_common.c
new file mode 100644
index 000000000..b767a7582
--- /dev/null
+++ b/test/validation/api/classification/odp_classification_common.c
@@ -0,0 +1,595 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020 Nokia
+ */
+
+#include "odp_classification_testsuites.h"
+#include "classification.h"
+#include <odp_cunit_common.h>
+#include <odp/helper/odph_api.h>
+
+typedef struct cls_test_packet {
+ odp_u32be_t magic;
+ odp_u32be_t seq;
+} cls_test_packet_t;
+
+static uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.0.0.1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 1
+};
+
+static uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.0.0.100 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 100
+};
+
+#define ODP_GTPU_UDP_PORT 2152
+#define AH_HDR_LEN 24
+
+odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool,
+ odp_bool_t cls_enable)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ int ret;
+
+ if (pool == ODP_POOL_INVALID)
+ return ODP_PKTIO_INVALID;
+
+ odp_pktio_param_init(&pktio_param);
+ if (q_type == ODP_QUEUE_TYPE_PLAIN)
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+ else
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open("loop", pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ret = odp_pool_destroy(pool);
+ if (ret)
+ ODPH_ERR("Unable to destroy pool\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ pktin_param.classifier_enable = cls_enable;
+ pktin_param.hash_enable = false;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Pktin queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Pktout queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ return pktio;
+}
+
+int stop_pktio(odp_pktio_t pktio)
+{
+ odp_event_t ev;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Pktio stop failed\n");
+ return -1;
+ }
+
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ break;
+ }
+
+ return 0;
+}
+
+static uint32_t seqno_offset(odp_packet_t pkt)
+{
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+ int rc;
+ uint16_t len = 0;
+
+ CU_ASSERT_FATAL(l3_offset != ODP_PACKET_OFFSET_INVALID);
+
+ if (odp_packet_has_ipv4(pkt)) {
+ odph_ipv4hdr_t ip;
+
+ rc = odp_packet_copy_to_mem(pkt, l3_offset, sizeof(ip), &ip);
+ CU_ASSERT_FATAL(rc == 0);
+ len = odp_be_to_cpu_16(ip.tot_len);
+ } else if (odp_packet_has_ipv6(pkt)) {
+ odph_ipv6hdr_t ip;
+
+ rc = odp_packet_copy_to_mem(pkt, l3_offset, sizeof(ip), &ip);
+ CU_ASSERT_FATAL(rc == 0);
+ len = sizeof(ip) + odp_be_to_cpu_16(ip.payload_len);
+ } else {
+ CU_FAIL_FATAL("Unexpected packet type");
+ }
+
+ return l3_offset + len - sizeof(cls_test_packet_t);
+}
+
+int cls_pkt_set_seq(odp_packet_t pkt)
+{
+ cls_test_packet_t data;
+ static uint32_t seq;
+ uint32_t offset;
+ int status;
+
+ data.magic = DATA_MAGIC;
+ data.seq = ++seq;
+
+ offset = seqno_offset(pkt);
+
+ status = odp_packet_copy_from_mem(pkt, offset, sizeof(data), &data);
+
+ return status;
+}
+
+uint32_t cls_pkt_get_seq(odp_packet_t pkt)
+{
+ cls_test_packet_t data;
+ uint32_t offset;
+ int rc;
+
+ offset = seqno_offset(pkt);
+
+ rc = odp_packet_copy_to_mem(pkt, offset, sizeof(data), &data);
+ CU_ASSERT_FATAL(rc == 0);
+
+ if (data.magic == DATA_MAGIC)
+ return data.seq;
+
+ return TEST_SEQ_INVALID;
+}
+
+int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask)
+{
+ int b[4];
+ int qualifier = 32;
+ int converted;
+
+ if (strchr(ipaddress, '/')) {
+ converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
+ &b[3], &b[2], &b[1], &b[0],
+ &qualifier);
+ if (5 != converted)
+ return -1;
+ } else {
+ converted = sscanf(ipaddress, "%d.%d.%d.%d",
+ &b[3], &b[2], &b[1], &b[0]);
+ if (4 != converted)
+ return -1;
+ }
+
+ if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
+ return -1;
+ if (!qualifier || (qualifier > 32))
+ return -1;
+
+ *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
+ if (mask)
+ *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1));
+
+ return 0;
+}
+
+void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio)
+{
+ odp_pktout_queue_t pktout;
+ int ret;
+
+ ret = odp_pktout_queue(pktio, &pktout, 1);
+
+ if (ret != 1) {
+ CU_FAIL_FATAL("No pktout queue");
+ return;
+ }
+
+ CU_ASSERT(odp_pktout_send(pktout, &pkt, 1) == 1);
+}
+
+odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns, odp_bool_t enable_pktv)
+{
+ odp_event_t ev;
+ uint64_t wait = odp_schedule_wait_time(ns);
+
+ ev = odp_schedule(queue, wait);
+ if (ev == ODP_EVENT_INVALID)
+ return ODP_PACKET_INVALID;
+
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ return odp_packet_from_event(ev);
+ } else if (enable_pktv && odp_event_type(ev) == ODP_EVENT_PACKET_VECTOR) {
+ odp_packet_vector_t pktv;
+ odp_packet_t *pkt_tbl;
+ odp_packet_t pkt;
+ uint32_t pktv_len;
+
+ pktv = odp_packet_vector_from_event(ev);
+ pktv_len = odp_packet_vector_tbl(pktv, &pkt_tbl);
+
+ CU_ASSERT_FATAL(pktv_len > 0);
+
+ pkt = pkt_tbl[0];
+ if (pktv_len > 1)
+ odp_packet_free_multi(&pkt_tbl[1], pktv_len - 1);
+ odp_packet_vector_free(pktv);
+ return pkt;
+ }
+
+ odp_event_free(ev);
+ return ODP_PACKET_INVALID;
+
+}
+
+odp_queue_t queue_create(const char *queuename, bool sched)
+{
+ odp_queue_t queue;
+ odp_queue_param_t qparam;
+
+ if (sched) {
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ queue = odp_queue_create(queuename, &qparam);
+ } else {
+ queue = odp_queue_create(queuename, NULL);
+ }
+
+ return queue;
+}
+
+odp_pool_t pool_create(const char *poolname)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+ param.pkt.seg_len = SHM_PKT_BUF_SIZE;
+ param.pkt.len = SHM_PKT_BUF_SIZE;
+ param.pkt.num = SHM_PKT_NUM_BUFS;
+ param.type = ODP_POOL_PACKET;
+
+ return odp_pool_create(poolname, &param);
+}
+
+odp_pool_t pktv_pool_create(const char *poolname)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t param;
+
+ if (odp_pool_capability(&capa)) {
+ ODPH_ERR("Pool capability failed\n");
+ return ODP_POOL_INVALID;
+ }
+
+ if (capa.vector.max_pools == 0) {
+ ODPH_ERR("No packet vector pools available\n");
+ return ODP_POOL_INVALID;
+ }
+
+ if (capa.vector.max_num && capa.vector.max_num < SHM_PKT_NUM_BUFS) {
+ ODPH_ERR("Unable to create large enough (%d) packet vector pool\n",
+ SHM_PKT_NUM_BUFS);
+ return ODP_POOL_INVALID;
+ }
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = SHM_PKT_NUM_BUFS;
+ param.vector.max_size = capa.vector.max_size;
+
+ return odp_pool_create(poolname, &param);
+}
+
+odp_packet_t create_packet(cls_packet_info_t pkt_info)
+{
+ uint32_t seqno;
+ odph_ethhdr_t *ethhdr;
+ odph_udphdr_t *udp;
+ odph_tcphdr_t *tcp;
+ odph_sctphdr_t *sctp;
+ odph_icmphdr_t *icmp;
+ odph_ipv4hdr_t *ip;
+ odph_ipv6hdr_t *ipv6;
+ odph_gtphdr_t *gtpu;
+ odph_igmphdr_t *igmp;
+ odph_ahhdr_t *ah;
+ odph_esphdr_t *esp;
+ uint8_t *hlen = 0;
+ uint16_t payload_len;
+ uint32_t addr = 0;
+ uint32_t mask;
+ odp_packet_t pkt;
+ int packet_len = 0;
+ uint32_t version, tc, flow, ver_tc_flow;
+ uint8_t *buf, next_hdr;
+ uint32_t l4_len, l3_len, l2_len, l3_offset, l4_offset;
+ uint16_t vlan_hdr_len = 0;
+ uint16_t l2_hdr_len = 0;
+ uint16_t l3_hdr_len = 0;
+ uint16_t l4_hdr_len = 0;
+ uint16_t eth_type;
+ odph_vlanhdr_t *vlan_hdr;
+ uint8_t src_mac[] = CLS_DEFAULT_SMAC;
+ uint8_t dst_mac[] = CLS_DEFAULT_DMAC;
+
+ payload_len = sizeof(cls_test_packet_t) + pkt_info.len;
+ if (pkt_info.l4_type == CLS_PKT_L4_GTP)
+ payload_len += sizeof(odph_gtphdr_t);
+
+ seqno = odp_atomic_fetch_inc_u32(pkt_info.seq);
+
+ vlan_hdr_len = pkt_info.vlan ? ODPH_VLANHDR_LEN : 0;
+ vlan_hdr_len = pkt_info.vlan_qinq ? 2 * vlan_hdr_len : vlan_hdr_len;
+ l3_hdr_len = pkt_info.ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
+ eth_type = pkt_info.ipv6 ? ODPH_ETHTYPE_IPV6 : ODPH_ETHTYPE_IPV4;
+ next_hdr = ODPH_IPPROTO_TCP;
+ l4_hdr_len = ODPH_TCPHDR_LEN;
+
+ switch (pkt_info.l4_type) {
+ case CLS_PKT_L4_TCP:
+ next_hdr = ODPH_IPPROTO_TCP;
+ l4_hdr_len = ODPH_TCPHDR_LEN;
+ break;
+ case CLS_PKT_L4_GTP:
+ case CLS_PKT_L4_UDP:
+ next_hdr = ODPH_IPPROTO_UDP;
+ l4_hdr_len = ODPH_UDPHDR_LEN;
+ break;
+ case CLS_PKT_L4_SCTP:
+ next_hdr = ODPH_IPPROTO_SCTP;
+ l4_hdr_len = ODPH_SCTPHDR_LEN;
+ break;
+ case CLS_PKT_L4_ICMP:
+ next_hdr = ODPH_IPPROTO_ICMPV4;
+ l4_hdr_len = ODPH_ICMPHDR_LEN;
+ break;
+ case CLS_PKT_L4_IGMP:
+ next_hdr = ODPH_IPPROTO_IGMP;
+ l4_hdr_len = ODPH_IGMP_HLEN;
+ break;
+ case CLS_PKT_L4_AH:
+ next_hdr = ODPH_IPPROTO_AH;
+ l4_hdr_len = AH_HDR_LEN;
+ break;
+ case CLS_PKT_L4_ESP:
+ next_hdr = ODPH_IPPROTO_ESP;
+ l4_hdr_len = ODPH_ESPHDR_LEN;
+ break;
+ default:
+ ODPH_ASSERT(0);
+ }
+
+ l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
+ l4_len = l4_hdr_len + payload_len;
+ l3_len = l3_hdr_len + l4_len;
+ l2_len = l2_hdr_len + l3_len;
+ packet_len = l2_len;
+
+ pkt = odp_packet_alloc(pkt_info.pool, packet_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ /* Ethernet Header */
+ buf = odp_packet_data(pkt);
+ odp_packet_l2_offset_set(pkt, 0);
+ ethhdr = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ memcpy(ethhdr->src.addr, &src_mac, ODPH_ETHADDR_LEN);
+ memcpy(ethhdr->dst.addr, &dst_mac, ODPH_ETHADDR_LEN);
+ vlan_hdr = (odph_vlanhdr_t *)(ethhdr + 1);
+
+ if (pkt_info.vlan) {
+ if (pkt_info.vlan_qinq) {
+ odp_packet_has_vlan_qinq_set(pkt, 1);
+ ethhdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN_OUTER);
+ vlan_hdr->tci = odp_cpu_to_be_16(0);
+ vlan_hdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN);
+ vlan_hdr++;
+ } else {
+ odp_packet_has_vlan_set(pkt, 1);
+ ethhdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN);
+ }
+ /* Default vlan header */
+ vlan_hdr->tci = odp_cpu_to_be_16(0);
+ vlan_hdr->type = odp_cpu_to_be_16(eth_type);
+ } else {
+ CU_ASSERT_FATAL(!pkt_info.vlan_qinq);
+ ethhdr->type = odp_cpu_to_be_16(eth_type);
+ }
+
+ l3_offset = l2_hdr_len;
+ odp_packet_l3_offset_set(pkt, l3_offset);
+
+ if (!pkt_info.ipv6) {
+ /* ipv4 */
+ ip = (odph_ipv4hdr_t *)(buf + l3_offset);
+
+ parse_ipv4_string(CLS_DEFAULT_DADDR, &addr, &mask);
+ ip->dst_addr = odp_cpu_to_be_32(addr);
+
+ parse_ipv4_string(CLS_DEFAULT_SADDR, &addr, &mask);
+ ip->src_addr = odp_cpu_to_be_32(addr);
+ ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->id = odp_cpu_to_be_16(seqno);
+ ip->proto = next_hdr;
+ ip->tot_len = odp_cpu_to_be_16(l3_len);
+ ip->ttl = DEFAULT_TTL;
+ ip->frag_offset = 0;
+ ip->tos = pkt_info.dscp << ODPH_IP_TOS_DSCP_SHIFT;
+ odp_packet_has_ipv4_set(pkt, 1);
+ odph_ipv4_csum_update(pkt);
+ } else {
+ /* ipv6 */
+ odp_packet_has_ipv6_set(pkt, 1);
+ ipv6 = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ version = ODPH_IPV6 << ODPH_IPV6HDR_VERSION_SHIFT;
+ tc = pkt_info.dscp << ODPH_IP_TOS_DSCP_SHIFT;
+ tc <<= ODPH_IPV6HDR_TC_SHIFT;
+ flow = seqno << ODPH_IPV6HDR_FLOW_LABEL_SHIFT;
+ ver_tc_flow = version | tc | flow;
+
+ ipv6->ver_tc_flow = odp_cpu_to_be_32(ver_tc_flow);
+ ipv6->payload_len = odp_cpu_to_be_16(l4_len);
+ ipv6->next_hdr = next_hdr;
+ ipv6->hop_limit = DEFAULT_TTL;
+ memcpy(ipv6->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
+ memcpy(ipv6->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
+ }
+
+ l4_offset = l3_offset + l3_hdr_len;
+ odp_packet_l4_offset_set(pkt, l4_offset);
+ tcp = (odph_tcphdr_t *)(buf + l4_offset);
+ udp = (odph_udphdr_t *)(buf + l4_offset);
+ sctp = (odph_sctphdr_t *)(buf + l4_offset);
+ icmp = (odph_icmphdr_t *)(buf + l4_offset);
+ igmp = (odph_igmphdr_t *)(buf + l4_offset);
+ ah = (odph_ahhdr_t *)(buf + l4_offset);
+ esp = (odph_esphdr_t *)(buf + l4_offset);
+
+ if (pkt_info.l4_type == CLS_PKT_L4_IGMP) {
+ igmp->group = odp_cpu_to_be_32(CLS_MAGIC_VAL);
+ igmp->type = 0x12;
+ igmp->code = 0;
+ igmp->csum = 0;
+ } else if (pkt_info.l4_type == CLS_PKT_L4_ICMP) {
+ icmp->type = ODPH_ICMP_ECHO;
+ icmp->code = 0;
+ icmp->un.echo.id = 0;
+ icmp->un.echo.sequence = 0;
+ icmp->chksum = 0;
+ } else if (pkt_info.l4_type == CLS_PKT_L4_SCTP) {
+ sctp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ sctp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ sctp->tag = 0;
+ sctp->chksum = 0;
+ odp_packet_has_sctp_set(pkt, 1);
+ if (odph_sctp_chksum_set(pkt) != 0) {
+ ODPH_ERR("odph_sctp_chksum failed\n");
+ return ODP_PACKET_INVALID;
+ }
+ } else if (pkt_info.l4_type == CLS_PKT_L4_UDP) {
+ /* udp */
+ udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
+ udp->chksum = 0;
+ odp_packet_has_udp_set(pkt, 1);
+ if (odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) {
+ ODPH_ERR("odph_udp_tcp_chksum failed\n");
+ return ODP_PACKET_INVALID;
+ }
+ } else if (pkt_info.l4_type == CLS_PKT_L4_GTP) {
+ udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ udp->dst_port = odp_cpu_to_be_16(ODP_GTPU_UDP_PORT);
+ udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
+ udp->chksum = 0;
+ odp_packet_has_udp_set(pkt, 1);
+ hlen = (uint8_t *)odp_packet_l4_ptr(pkt, NULL);
+ gtpu = (odph_gtphdr_t *)(hlen + sizeof(odph_udphdr_t));
+ gtpu->teid = odp_cpu_to_be_32(CLS_MAGIC_VAL);
+ /* GTPv1 without optional headers */
+ gtpu->gtp_hdr_info = 0x30;
+ /* GTP echo request */
+ gtpu->msg_type = 1;
+ gtpu->plen = sizeof(cls_test_packet_t);
+ if (odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) {
+ ODPH_ERR("odph_udp_tcp_chksum failed\n");
+ return ODP_PACKET_INVALID;
+ }
+ } else if (pkt_info.l4_type == CLS_PKT_L4_AH) {
+ ah->next_header = ODPH_IPV4;
+ ah->ah_len = AH_HDR_LEN / 4 - 2;
+ ah->pad = 0;
+ ah->spi = 256;
+ ah->seq_no = 1;
+ } else if (pkt_info.l4_type == CLS_PKT_L4_ESP) {
+ esp->spi = 256;
+ esp->seq_no = 1;
+ } else {
+ tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ tcp->doffset_flags = 0;
+ tcp->seq_no = 0;
+ tcp->ack_no = 0;
+ tcp->window = 0;
+ tcp->urgptr = 0;
+ tcp->hl = ODPH_TCPHDR_LEN / 4;
+ tcp->ack = 1;
+ tcp->cksm = 0;
+ odp_packet_has_tcp_set(pkt, 1);
+ if (odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) {
+ ODPH_ERR("odph_udp_tcp_chksum failed\n");
+ return ODP_PACKET_INVALID;
+ }
+
+ }
+
+ /* set pkt sequence number */
+ cls_pkt_set_seq(pkt);
+
+ return pkt;
+}
+
+odp_cls_pmr_term_t find_first_supported_l3_pmr(void)
+{
+ odp_cls_pmr_term_t term = ODP_PMR_TCP_DPORT;
+ odp_cls_capability_t capability;
+
+ odp_cls_capability(&capability);
+
+ /* choose supported PMR */
+ if (capability.supported_terms.bit.udp_sport)
+ term = ODP_PMR_UDP_SPORT;
+ else if (capability.supported_terms.bit.udp_dport)
+ term = ODP_PMR_UDP_DPORT;
+ else if (capability.supported_terms.bit.tcp_sport)
+ term = ODP_PMR_TCP_SPORT;
+ else if (capability.supported_terms.bit.tcp_dport)
+ term = ODP_PMR_TCP_DPORT;
+ else
+ CU_FAIL("Implementations doesn't support any TCP/UDP PMR");
+
+ return term;
+}
+
+int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port)
+{
+ odph_udphdr_t *udp;
+ odph_tcphdr_t *tcp;
+ odp_cls_pmr_term_t term;
+
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ port = odp_cpu_to_be_16(port);
+ term = find_first_supported_l3_pmr();
+ switch (term) {
+ case ODP_PMR_UDP_SPORT:
+ udp->src_port = port;
+ break;
+ case ODP_PMR_UDP_DPORT:
+ udp->dst_port = port;
+ break;
+ case ODP_PMR_TCP_DPORT:
+ tcp->dst_port = port;
+ break;
+ case ODP_PMR_TCP_SPORT:
+ tcp->src_port = port;
+ break;
+ default:
+ CU_FAIL("Unsupported L3 term");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/validation/api/classification/odp_classification_test_pmr.c b/test/validation/api/classification/odp_classification_test_pmr.c
new file mode 100644
index 000000000..04cf098e3
--- /dev/null
+++ b/test/validation/api/classification/odp_classification_test_pmr.c
@@ -0,0 +1,2184 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+#include "odp_classification_testsuites.h"
+#include "classification.h"
+#include <odp_cunit_common.h>
+#include <odp/helper/odph_api.h>
+
+#define MAX_NUM_UDP 4
+#define MARK_IP 1
+#define MARK_UDP 2
+#define TEST_IPV4 false
+#define TEST_IPV6 true
+
+static odp_pool_t pkt_pool;
+/** sequence number of IP packets */
+static odp_atomic_u32_t seq;
+
+static cls_packet_info_t default_pkt_info;
+static odp_cls_capability_t cls_capa;
+
+int classification_suite_pmr_init(void)
+{
+ memset(&cls_capa, 0, sizeof(odp_cls_capability_t));
+
+ if (odp_cls_capability(&cls_capa)) {
+ ODPH_ERR("Classifier capability call failed\n");
+ return -1;
+ }
+
+ pkt_pool = pool_create("classification_pmr_pool");
+ if (ODP_POOL_INVALID == pkt_pool) {
+ ODPH_ERR("Packet pool creation failed\n");
+ return -1;
+ }
+
+ memset(&default_pkt_info, 0, sizeof(cls_packet_info_t));
+ default_pkt_info.pool = pkt_pool;
+ default_pkt_info.seq = &seq;
+
+ odp_atomic_init_u32(&seq, 0);
+
+ return 0;
+}
+
+static int start_pktio(odp_pktio_t pktio)
+{
+ if (odp_pktio_start(pktio)) {
+ ODPH_ERR("Unable to start loop\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos,
+ odp_queue_t *queue, odp_pool_t *pool)
+{
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t default_pool;
+ odp_cos_t default_cos;
+ odp_queue_t default_queue;
+ int retval;
+ char cosname[ODP_COS_NAME_LEN];
+
+ default_pool = pool_create("DefaultPool");
+ CU_ASSERT(default_pool != ODP_POOL_INVALID);
+
+ default_queue = queue_create("DefaultQueue", true);
+ CU_ASSERT(default_queue != ODP_QUEUE_INVALID);
+
+ sprintf(cosname, "DefaultCos");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = default_pool;
+ cls_param.queue = default_queue;
+
+ default_cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT(default_cos != ODP_COS_INVALID);
+
+ retval = odp_pktio_default_cos_set(pktio, default_cos);
+ CU_ASSERT(retval == 0);
+
+ *cos = default_cos;
+ *queue = default_queue;
+ *pool = default_pool;
+}
+
+int classification_suite_pmr_term(void)
+{
+ int ret = 0;
+
+ if (0 != odp_pool_destroy(pkt_pool)) {
+ ODPH_ERR("Packet pool destroy failed\n");
+ ret += -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ ret += -1;
+
+ return ret;
+}
+
+static void cls_pktin_classifier_flag(void)
+{
+ odp_packet_t pkt;
+ odph_tcphdr_t *tcp;
+ uint32_t seqno;
+ uint16_t val;
+ uint16_t mask;
+ int retval;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t pool;
+ odp_pool_t pool_recv;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+
+ val = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+ seqno = 0;
+
+ /* classifier is disabled in pktin queue configuration */
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, false);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("tcp_dport1", true);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("tcp_dport1");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sprintf(cosname, "tcp_dport");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT(cos != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_TCP_DPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp->dst_port = val;
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ /* since classifier flag is disabled in pktin queue configuration
+ packet will not be delivered in classifier queues */
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pool_recv = odp_packet_pool(pkt);
+ /* since classifier is disabled packet should not be received in
+ pool and queue configured with classifier */
+ CU_ASSERT(pool != pool_recv);
+ CU_ASSERT(retqueue != queue);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+
+ odp_packet_free(pkt);
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(default_pool);
+ odp_pktio_close(pktio);
+}
+
+static void cls_pmr_term_tcp_dport_n(int num_pkt)
+{
+ odp_packet_t pkt;
+ odph_tcphdr_t *tcp;
+ uint32_t seqno[num_pkt];
+ uint16_t val;
+ uint16_t mask;
+ int retval, i, sent_queue, recv_queue, sent_default, recv_default;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pool_t recvpool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pool_t pool;
+ odp_pool_t pool_recv;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ val = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("tcp_dport1", true);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("tcp_dport1");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sprintf(cosname, "tcp_dport");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT(cos != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_TCP_DPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+
+ for (i = 0; i < num_pkt; i++) {
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno[i] = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno[i] != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp->dst_port = val;
+
+ enqueue_pktio_interface(pkt, pktio);
+ }
+
+ for (i = 0; i < num_pkt; i++) {
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pool_recv = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_recv);
+ CU_ASSERT(retqueue == queue);
+ CU_ASSERT(seqno[i] == cls_pkt_get_seq(pkt));
+
+ odp_packet_free(pkt);
+ }
+
+ /* Other packets are delivered to default queue */
+ for (i = 0; i < num_pkt; i++) {
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno[i] = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno[i] != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
+
+ enqueue_pktio_interface(pkt, pktio);
+ }
+
+ for (i = 0; i < num_pkt; i++) {
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno[i] == cls_pkt_get_seq(pkt));
+ CU_ASSERT(retqueue == default_queue);
+ recvpool = odp_packet_pool(pkt);
+ CU_ASSERT(recvpool == default_pool);
+
+ odp_packet_free(pkt);
+ }
+
+ sent_queue = 0;
+ sent_default = 0;
+
+ /* Both queues simultaneously */
+ for (i = 0; i < 2 * num_pkt; i++) {
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+
+ if ((i % 5) < 2) {
+ sent_queue++;
+ tcp->dst_port = val;
+ } else {
+ sent_default++;
+ tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
+ }
+
+ enqueue_pktio_interface(pkt, pktio);
+ }
+
+ recv_queue = 0;
+ recv_default = 0;
+
+ for (i = 0; i < 2 * num_pkt; i++) {
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(retqueue == queue || retqueue == default_queue);
+
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+
+ if (retqueue == queue) {
+ recv_queue++;
+ CU_ASSERT(tcp->dst_port == val);
+ } else if (retqueue == default_queue) {
+ recv_default++;
+ CU_ASSERT(tcp->dst_port ==
+ odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1));
+ }
+ odp_packet_free(pkt);
+ }
+
+ CU_ASSERT(sent_queue == recv_queue);
+ CU_ASSERT(sent_default == recv_default);
+
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(default_pool);
+ odp_pktio_close(pktio);
+}
+
+typedef enum match_t {
+ MATCH,
+ NO_MATCH
+} match_t;
+
+/*
+ * Test that PMR created using the given parameters matches or does not match
+ * given packet. The packet, that gets consumed, must have been created using
+ * create_packet() so that it contains the testing sequence number.
+ *
+ * Ethernet addresses of the packet will be overwritten.
+ */
+static void test_pmr(const odp_pmr_param_t *pmr_param, odp_packet_t pkt,
+ match_t match)
+{
+ uint32_t seqno;
+ int retval;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_pool_t recvpool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ odp_cls_cos_param_t cls_param;
+ odph_ethhdr_t *eth;
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("PMR test queue", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("PMR test pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create("PMR test cos", &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ pmr = odp_cls_pmr_create(pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio);
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ recvpool = odp_packet_pool(pkt);
+
+ if (match == MATCH) {
+ CU_ASSERT(retqueue == queue);
+ CU_ASSERT(recvpool == pool);
+ } else {
+ CU_ASSERT(retqueue == default_queue);
+ CU_ASSERT(recvpool == default_pool);
+ }
+
+ odp_packet_free(pkt);
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_pool_destroy(default_pool);
+ odp_pool_destroy(pool);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pktio_close(pktio);
+}
+
+static void cls_pmr_term_tcp_sport(void)
+{
+ odp_packet_t pkt;
+ odph_tcphdr_t *tcp;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+
+ val = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_TCP_SPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp->src_port = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_udp_dport(void)
+{
+ odp_packet_t pkt;
+ odph_udphdr_t *udp;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_UDP_DPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->dst_port = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_udp_sport(void)
+{
+ odp_packet_t pkt;
+ odph_udphdr_t *udp;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_UDP_SPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->src_port = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_proto_ip(odp_bool_t ipv6)
+{
+ odp_packet_t pkt;
+ uint8_t val;
+ uint8_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = ODPH_IPPROTO_UDP;
+ mask = 0xff;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IPPROTO;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.ipv6 = ipv6;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt_info.l4_type = CLS_PKT_L4_TCP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipv4_proto(void)
+{
+ cls_pmr_term_proto_ip(TEST_IPV4);
+}
+
+static void cls_pmr_term_ipv6_proto(void)
+{
+ cls_pmr_term_proto_ip(TEST_IPV6);
+}
+
+static void cls_pmr_term_dscp_ip(odp_bool_t ipv6)
+{
+ odp_packet_t pkt;
+ uint8_t val;
+ uint8_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = DSCP_CLASS4;
+ mask = 0x3f;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IP_DSCP;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.ipv6 = ipv6;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt_info.dscp = DSCP_CLASS4;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt_info.dscp = 0;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipv4_dscp(void)
+{
+ cls_pmr_term_dscp_ip(TEST_IPV4);
+}
+
+static void cls_pmr_term_ipv6_dscp(void)
+{
+ cls_pmr_term_dscp_ip(TEST_IPV6);
+}
+
+static void cls_pmr_term_dmac(void)
+{
+ odp_packet_t pkt;
+ uint32_t seqno;
+ int retval;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_pool_t recvpool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ cls_packet_info_t pkt_info;
+ uint8_t val[] = {0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee};
+ uint8_t mask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ seqno = 0;
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("dmac", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("dmac");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sprintf(cosname, "dmac");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_DMAC;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = ODPH_ETHADDR_LEN;
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ memcpy(eth->dst.addr, val, ODPH_ETHADDR_LEN);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ recvpool = odp_packet_pool(pkt);
+ CU_ASSERT(recvpool == pool);
+ CU_ASSERT(retqueue == queue);
+ odp_packet_free(pkt);
+
+ /* Other packets delivered to default queue */
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ recvpool = odp_packet_pool(pkt);
+ CU_ASSERT(recvpool == default_pool);
+ CU_ASSERT(retqueue == default_queue);
+
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ odp_packet_free(pkt);
+ stop_pktio(pktio);
+ odp_pool_destroy(default_pool);
+ odp_pool_destroy(pool);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pktio_close(pktio);
+}
+
+static void cls_pmr_term_packet_len(void)
+{
+ odp_packet_t pkt;
+ uint32_t val;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = 1024;
+ /*Mask value will match any packet of length 1000 - 1099*/
+ mask = 0xff00;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_LEN;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ /* create packet of payload length 1024 */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt_info.len = 1024;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_vlan_id_0(void)
+{
+ odp_packet_t pkt;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ odph_vlanhdr_t *vlan_0;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(0x123);
+ mask = odp_cpu_to_be_16(0xfff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_VLAN_ID_0;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.vlan = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_0 = (odph_vlanhdr_t *)(eth + 1);
+ vlan_0->tci = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_vlan_id_x(void)
+{
+ odp_packet_t pkt;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ odph_vlanhdr_t *vlan_x;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(0x345);
+ mask = odp_cpu_to_be_16(0xfff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_VLAN_ID_X;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ /* Single VLAN */
+ pkt_info = default_pkt_info;
+ pkt_info.vlan = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_x = (odph_vlanhdr_t *)(eth + 1);
+ vlan_x->tci = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ /* Two VLANs */
+ pkt_info.vlan_qinq = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_x = (odph_vlanhdr_t *)(eth + 1);
+ vlan_x++;
+ vlan_x->tci = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ /* No VLAN */
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_vlan_pcp_0(void)
+{
+ odp_packet_t pkt;
+ uint8_t val;
+ uint8_t mask;
+ uint16_t tci;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ odph_vlanhdr_t *vlan_0;
+ cls_packet_info_t pkt_info;
+
+ val = 5;
+ mask = 0x7;
+ tci = ((uint16_t)val) << ODPH_VLANHDR_PCP_SHIFT;
+ tci |= 0x123;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_VLAN_PCP_0;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.vlan = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_0 = (odph_vlanhdr_t *)(eth + 1);
+ vlan_0->tci = odp_cpu_to_be_16(tci);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_eth_type_0(void)
+{
+ odp_packet_t pkt;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV6);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_ETHTYPE_0;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.ipv6 = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_eth_type_x(void)
+{
+ odp_packet_t pkt;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ odph_vlanhdr_t *vlan_x;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(0x0800);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_ETHTYPE_X;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ /* Single VLAN */
+ pkt_info = default_pkt_info;
+ pkt_info.vlan = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_x = (odph_vlanhdr_t *)(eth + 1);
+ vlan_x->tci = odp_cpu_to_be_16(0x123);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ /* Two VLANs */
+ pkt_info.vlan_qinq = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ vlan_x = (odph_vlanhdr_t *)(eth + 1);
+ vlan_x++;
+ vlan_x->tci = odp_cpu_to_be_16(0x123);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ /* No VLAN */
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_pool_set(void)
+{
+ odp_packet_t pkt;
+ uint32_t seqno;
+ uint8_t val;
+ uint8_t mask;
+ int retval;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_pool_t pool_new;
+ odp_pool_t recvpool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ cls_packet_info_t pkt_info;
+
+ val = ODPH_IPPROTO_UDP;
+ mask = 0xff;
+ seqno = 0;
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("ipproto1", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("ipproto1");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sprintf(cosname, "ipproto1");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ pool_new = pool_create("ipproto2");
+ CU_ASSERT_FATAL(pool_new != ODP_POOL_INVALID);
+
+ /* new pool is set on CoS */
+ retval = odp_cls_cos_pool_set(cos, pool_new);
+ CU_ASSERT(retval == 0);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IPPROTO;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ recvpool = odp_packet_pool(pkt);
+ CU_ASSERT(recvpool == pool_new);
+ CU_ASSERT(retqueue == queue);
+ odp_packet_free(pkt);
+
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_pool_destroy(default_pool);
+ odp_pool_destroy(pool);
+ odp_pool_destroy(pool_new);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pktio_close(pktio);
+}
+
+static void cls_pmr_queue_set(void)
+{
+ odp_packet_t pkt;
+ uint32_t seqno;
+ uint8_t val;
+ uint8_t mask;
+ int retval;
+ odp_pktio_t pktio;
+ odp_queue_t queue;
+ odp_queue_t retqueue;
+ odp_queue_t default_queue;
+ odp_cos_t default_cos;
+ odp_pool_t default_pool;
+ odp_pool_t pool;
+ odp_queue_t queue_new;
+ odp_pool_t recvpool;
+ odp_pmr_t pmr;
+ odp_cos_t cos;
+ char cosname[ODP_COS_NAME_LEN];
+ odp_cls_cos_param_t cls_param;
+ odp_pmr_param_t pmr_param;
+ odph_ethhdr_t *eth;
+ cls_packet_info_t pkt_info;
+
+ val = ODPH_IPPROTO_UDP;
+ mask = 0xff;
+ seqno = 0;
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ queue = queue_create("ipproto1", true);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = pool_create("ipproto1");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sprintf(cosname, "ipproto1");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue;
+
+ cos = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ queue_new = queue_create("ipproto2", true);
+ CU_ASSERT_FATAL(queue_new != ODP_QUEUE_INVALID);
+
+ /* new queue is set on CoS */
+ retval = odp_cos_queue_set(cos, queue_new);
+ CU_ASSERT(retval == 0);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IPPROTO;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT(pmr != ODP_PMR_INVALID);
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ recvpool = odp_packet_pool(pkt);
+ CU_ASSERT(recvpool == pool);
+ CU_ASSERT(retqueue == queue_new);
+ odp_packet_free(pkt);
+
+ odp_cls_pmr_destroy(pmr);
+ odp_cos_destroy(cos);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_pool_destroy(default_pool);
+ odp_pool_destroy(pool);
+ odp_queue_destroy(queue_new);
+ odp_queue_destroy(queue);
+ odp_queue_destroy(default_queue);
+ odp_pktio_close(pktio);
+}
+
+static void test_pmr_term_ipv4_addr(int dst)
+{
+ odp_packet_t pkt;
+ uint32_t dst_addr, src_addr;
+ uint32_t dst_mask, src_mask;
+ odp_pmr_param_t pmr_param;
+ odph_ipv4hdr_t *ip;
+ const char *src_str = "10.0.0.88/32";
+ const char *dst_str = "10.0.0.99/32";
+
+ parse_ipv4_string(src_str, &src_addr, &src_mask);
+ parse_ipv4_string(dst_str, &dst_addr, &dst_mask);
+ src_addr = odp_cpu_to_be_32(src_addr);
+ src_mask = odp_cpu_to_be_32(src_mask);
+ dst_addr = odp_cpu_to_be_32(dst_addr);
+ dst_mask = odp_cpu_to_be_32(dst_mask);
+
+ odp_cls_pmr_param_init(&pmr_param);
+
+ if (dst) {
+ pmr_param.term = ODP_PMR_DIP_ADDR;
+ pmr_param.match.value = &dst_addr;
+ pmr_param.match.mask = &dst_mask;
+ pmr_param.val_sz = sizeof(dst_addr);
+ } else {
+ pmr_param.term = ODP_PMR_SIP_ADDR;
+ pmr_param.match.value = &src_addr;
+ pmr_param.match.mask = &src_mask;
+ pmr_param.val_sz = sizeof(src_addr);
+ }
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ ip->src_addr = src_addr;
+ ip->dst_addr = dst_addr;
+ odph_ipv4_csum_update(pkt);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipv4_saddr(void)
+{
+ test_pmr_term_ipv4_addr(0);
+}
+
+static void cls_pmr_term_ipv4_daddr(void)
+{
+ test_pmr_term_ipv4_addr(1);
+}
+
+static void cls_pmr_term_ipv6daddr(void)
+{
+ odp_packet_t pkt;
+ odp_pmr_param_t pmr_param;
+ odph_ipv6hdr_t *ip;
+ cls_packet_info_t pkt_info;
+
+ uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.1.1.100 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 1, 1, 100
+ };
+ uint8_t ipv6_mask[ODPH_IPV6ADDR_LEN] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_DIP6_ADDR;
+ pmr_param.match.value = IPV6_DST_ADDR;
+ pmr_param.match.mask = ipv6_mask;
+ pmr_param.val_sz = ODPH_IPV6ADDR_LEN;
+
+ pkt_info = default_pkt_info;
+ pkt_info.ipv6 = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ip = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ memcpy(ip->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipv6saddr(void)
+{
+ odp_packet_t pkt;
+ odp_pmr_param_t pmr_param;
+ odph_ipv6hdr_t *ip;
+ cls_packet_info_t pkt_info;
+ uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.0.0.100 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 1, 1, 1
+ };
+ uint8_t ipv6_mask[ODPH_IPV6ADDR_LEN] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_SIP6_ADDR;
+ pmr_param.match.value = IPV6_SRC_ADDR;
+ pmr_param.match.mask = ipv6_mask;
+ pmr_param.val_sz = ODPH_IPV6ADDR_LEN;
+
+ pkt_info = default_pkt_info;
+ pkt_info.ipv6 = true;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ip = (odph_ipv6hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ memcpy(ip->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_tcp_dport(void)
+{
+ cls_pmr_term_tcp_dport_n(2);
+}
+
+static void cls_pmr_term_tcp_dport_multi(void)
+{
+ cls_pmr_term_tcp_dport_n(SHM_PKT_NUM_BUFS / 4);
+}
+
+static void test_pmr_term_custom(int custom_l3)
+{
+ odp_packet_t pkt;
+ uint32_t dst_addr, src_addr;
+ uint32_t addr_be, mask_be;
+ uint32_t dst_mask, src_mask;
+ odp_pmr_param_t pmr_param;
+ odph_ipv4hdr_t *ip;
+ const char *pmr_src_str = "10.0.8.0/24";
+ const char *pmr_dst_str = "10.0.9.0/24";
+ const char *pkt_src_str = "10.0.8.88/32";
+ const char *pkt_dst_str = "10.0.9.99/32";
+
+ /* Match values for custom PRM rules are passed in network endian */
+ parse_ipv4_string(pmr_src_str, &src_addr, &src_mask);
+ parse_ipv4_string(pmr_dst_str, &dst_addr, &dst_mask);
+
+ odp_cls_pmr_param_init(&pmr_param);
+
+ if (custom_l3) {
+ addr_be = odp_cpu_to_be_32(dst_addr);
+ mask_be = odp_cpu_to_be_32(dst_mask);
+ pmr_param.term = ODP_PMR_CUSTOM_L3;
+ pmr_param.match.value = &addr_be;
+ pmr_param.match.mask = &mask_be;
+ pmr_param.val_sz = sizeof(addr_be);
+ /* Offset from start of L3 to IPv4 dst address */
+ pmr_param.offset = 16;
+ } else {
+ addr_be = odp_cpu_to_be_32(src_addr);
+ mask_be = odp_cpu_to_be_32(src_mask);
+ pmr_param.term = ODP_PMR_CUSTOM_FRAME;
+ pmr_param.match.value = &addr_be;
+ pmr_param.match.mask = &mask_be;
+ pmr_param.val_sz = sizeof(addr_be);
+ /* Offset from start of ethernet/IPv4 frame to IPv4
+ * src address */
+ pmr_param.offset = 26;
+ }
+
+ /* IPv4 packet with matching addresses */
+ parse_ipv4_string(pkt_src_str, &src_addr, NULL);
+ parse_ipv4_string(pkt_dst_str, &dst_addr, NULL);
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ ip->src_addr = odp_cpu_to_be_32(src_addr);
+ ip->dst_addr = odp_cpu_to_be_32(dst_addr);
+ odph_ipv4_csum_update(pkt);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+/*
+ * Test a series of PMR rules and CoS. When num_udp is 1, test is serial
+ * from IP CoS to UDP CoS. When num_udp is larger than 1, a set of parallel
+ * UDP CoS are tested.
+ *
+ * dst IP dst UDP[0 ... 3]
+ * default_cos -> cos_ip -> cos_udp[0 ... 3]
+ */
+static void test_pmr_series(const int num_udp, int marking)
+{
+ odp_packet_t pkt;
+ uint32_t seqno;
+ int i, retval;
+ cls_packet_info_t pkt_info;
+ odp_pktio_t pktio;
+ odp_pool_t pool;
+ odp_queue_t default_queue;
+ odp_pool_t default_pool;
+ odp_cos_t default_cos;
+ odp_queue_t retqueue;
+ odp_pmr_t pmr_ip;
+ odp_queue_t queue_ip;
+ odp_cos_t cos_ip;
+ uint32_t dst_addr;
+ uint32_t dst_addr_be, ip_mask_be;
+ uint32_t dst_mask;
+ odp_pmr_param_t pmr_param;
+ odp_cls_cos_param_t cls_param;
+ odp_pmr_create_opt_t create_opt;
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ odph_udphdr_t *udp;
+ odp_cos_t cos_udp[num_udp];
+ odp_queue_t queue_udp[num_udp];
+ odp_pmr_t pmr_udp[num_udp];
+ uint16_t dst_port = 1000;
+
+ pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool, true);
+ retval = start_pktio(pktio);
+ CU_ASSERT(retval == 0);
+
+ configure_default_cos(pktio, &default_cos,
+ &default_queue, &default_pool);
+
+ pool = pool_create("pmr_series");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Dest IP address */
+ queue_ip = queue_create("queue_ip", true);
+ CU_ASSERT_FATAL(queue_ip != ODP_QUEUE_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue_ip;
+
+ cos_ip = odp_cls_cos_create("cos_ip", &cls_param);
+ CU_ASSERT_FATAL(cos_ip != ODP_COS_INVALID);
+
+ parse_ipv4_string("10.0.9.99/32", &dst_addr, &dst_mask);
+ dst_addr_be = odp_cpu_to_be_32(dst_addr);
+ ip_mask_be = odp_cpu_to_be_32(dst_mask);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_DIP_ADDR;
+ pmr_param.match.value = &dst_addr_be;
+ pmr_param.match.mask = &ip_mask_be;
+ pmr_param.val_sz = sizeof(dst_addr_be);
+ pmr_param.offset = 0;
+
+ if (marking) {
+ odp_cls_pmr_create_opt_init(&create_opt);
+ create_opt.terms = &pmr_param;
+ create_opt.num_terms = 1;
+ create_opt.mark = MARK_IP;
+
+ pmr_ip = odp_cls_pmr_create_opt(&create_opt, default_cos, cos_ip);
+ } else {
+ pmr_ip = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos_ip);
+ }
+
+ CU_ASSERT_FATAL(pmr_ip != ODP_PMR_INVALID);
+
+ /* Dest UDP port */
+ for (i = 0; i < num_udp; i++) {
+ uint16_t dst_port_be = odp_cpu_to_be_16(dst_port + i);
+ uint16_t port_mask_be = odp_cpu_to_be_16(0xffff);
+ char name[] = "udp_0";
+
+ name[4] += i;
+ queue_udp[i] = queue_create(name, true);
+ CU_ASSERT_FATAL(queue_udp[i] != ODP_QUEUE_INVALID);
+
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool;
+ cls_param.queue = queue_udp[i];
+
+ cos_udp[i] = odp_cls_cos_create(name, &cls_param);
+ CU_ASSERT_FATAL(cos_udp[i] != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_UDP_DPORT;
+ pmr_param.match.value = &dst_port_be;
+ pmr_param.match.mask = &port_mask_be;
+ pmr_param.val_sz = 2;
+ pmr_param.offset = 0;
+
+ if (marking) {
+ odp_cls_pmr_create_opt_init(&create_opt);
+ create_opt.terms = &pmr_param;
+ create_opt.num_terms = 1;
+ create_opt.mark = MARK_UDP + i;
+
+ pmr_udp[i] = odp_cls_pmr_create_opt(&create_opt, cos_ip, cos_udp[i]);
+ } else {
+ pmr_udp[i] = odp_cls_pmr_create(&pmr_param, 1, cos_ip, cos_udp[i]);
+ }
+
+ CU_ASSERT_FATAL(pmr_udp[i] != ODP_PMR_INVALID);
+ }
+
+ /* Matching TCP/IP packet */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_TCP;
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+ ip->dst_addr = dst_addr_be;
+ odph_ipv4_csum_update(pkt);
+
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ CU_ASSERT(retqueue == queue_ip);
+
+ if (marking) {
+ CU_ASSERT(odp_packet_cls_mark(pkt) == MARK_IP);
+ CU_ASSERT(odp_packet_reset(pkt, odp_packet_len(pkt)) == 0);
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+ } else {
+ /* Default is 0 */
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+ }
+
+ odp_packet_free(pkt);
+
+ /* Matching UDP/IP packets */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+
+ for (i = 0; i < num_udp; i++) {
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+ ip->dst_addr = dst_addr_be;
+ odph_ipv4_csum_update(pkt);
+ udp->dst_port = odp_cpu_to_be_16(dst_port + i);
+
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ CU_ASSERT(retqueue == queue_udp[i]);
+
+ if (marking) {
+ CU_ASSERT(odp_packet_cls_mark(pkt) == (uint64_t)(MARK_UDP + i));
+ } else {
+ /* Default is 0 */
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+ }
+
+ odp_packet_free(pkt);
+ }
+
+ /* Other packets delivered to default queue */
+ pkt = create_packet(default_pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+ odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN);
+ odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN);
+
+ enqueue_pktio_interface(pkt, pktio);
+
+ pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ CU_ASSERT(retqueue == default_queue);
+ odp_packet_free(pkt);
+ odp_cls_pmr_destroy(pmr_ip);
+
+ for (i = 0; i < num_udp; i++) {
+ odp_cls_pmr_destroy(pmr_udp[i]);
+ odp_cos_destroy(cos_udp[i]);
+ }
+
+ odp_cos_destroy(cos_ip);
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ stop_pktio(pktio);
+ odp_pool_destroy(default_pool);
+ odp_pool_destroy(pool);
+
+ for (i = 0; i < num_udp; i++)
+ odp_queue_destroy(queue_udp[i]);
+
+ odp_queue_destroy(queue_ip);
+ odp_queue_destroy(default_queue);
+ odp_pktio_close(pktio);
+}
+
+static void cls_pmr_term_sctp_port(bool is_dport)
+{
+ odp_packet_t pkt;
+ odph_sctphdr_t *sctp;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(CLS_DEFAULT_SPORT);
+ if (is_dport)
+ val = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_SCTP_SPORT;
+ if (is_dport)
+ pmr_param.term = ODP_PMR_SCTP_DPORT;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_SCTP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ sctp = (odph_sctphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ if (is_dport)
+ sctp->dst_port = val;
+ else
+ sctp->src_port = val;
+ CU_ASSERT(odph_sctp_chksum_set(pkt) == 0);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ sctp = (odph_sctphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ if (is_dport)
+ sctp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1);
+ else
+ sctp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1);
+ CU_ASSERT(odph_sctp_chksum_set(pkt) == 0);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_sctp_sport(void)
+{
+ cls_pmr_term_sctp_port(0);
+}
+
+static void cls_pmr_term_sctp_dport(void)
+{
+ cls_pmr_term_sctp_port(1);
+}
+
+static void cls_pmr_term_icmp_type(void)
+{
+ odp_packet_t pkt;
+ odph_icmphdr_t *icmp;
+ uint8_t val;
+ uint8_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = ODPH_ICMP_ECHO;
+ mask = 0xff;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_ICMP_TYPE;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_ICMP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->type = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->type = ODPH_ICMP_ECHOREPLY;
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_icmp_code(void)
+{
+ odp_packet_t pkt;
+ odph_icmphdr_t *icmp;
+ uint8_t val;
+ uint8_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = 0x1;
+ mask = 0xff;
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_ICMP_CODE;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_ICMP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->code = 0x1;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->code = 0;
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_icmp_id(void)
+{
+ odp_packet_t pkt;
+ odph_icmphdr_t *icmp;
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_16(0x1234);
+ mask = odp_cpu_to_be_16(0xffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_ICMP_ID;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_ICMP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->un.echo.id = odp_cpu_to_be_16(0x1234);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ icmp = (odph_icmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ icmp->un.echo.id = 0x4567;
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_gtpu_teid(void)
+{
+ odp_packet_t pkt;
+ odph_gtphdr_t *gtpu;
+ odph_udphdr_t *udp;
+ uint32_t val;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+ uint8_t *hlen = 0;
+
+ val = odp_cpu_to_be_32(CLS_MAGIC_VAL);
+ mask = odp_cpu_to_be_32(0xffffffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_GTPV1_TEID;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_GTP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ /* Check packet with wrong UDP port, packets should goto default cos */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_GTP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+
+ /* Check GTPv2 packets, should goto default cos */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_GTP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ hlen = (uint8_t *)odp_packet_l4_ptr(pkt, NULL);
+ gtpu = (odph_gtphdr_t *)(hlen + ODPH_UDPHDR_LEN);
+ /* Version:2, piggybacking:1, teid:1 */
+ gtpu->gtp_hdr_info = 0x58;
+ CU_ASSERT(odph_udp_tcp_chksum(pkt, ODPH_CHKSUM_GENERATE, NULL) == 0);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+
+ /* All other packets should goto default cos */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_GTP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ hlen = (uint8_t *)odp_packet_l4_ptr(pkt, NULL);
+ gtpu = (odph_gtphdr_t *)(hlen + ODPH_UDPHDR_LEN);
+ gtpu->teid = odp_cpu_to_be_32(CLS_MAGIC_VAL + 1);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_igmp_grpaddr(void)
+{
+ odp_packet_t pkt;
+ odph_igmphdr_t *igmp;
+ uint32_t val;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+
+ val = odp_cpu_to_be_32(CLS_MAGIC_VAL);
+ mask = odp_cpu_to_be_32(0xffffffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IGMP_GRP_ADDR;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_IGMP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_IGMP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ igmp = (odph_igmphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ igmp->group = odp_cpu_to_be_32(CLS_MAGIC_VAL + 1);
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_serial(void)
+{
+ test_pmr_series(1, 0);
+}
+
+static void cls_pmr_parallel(void)
+{
+ test_pmr_series(MAX_NUM_UDP, 0);
+}
+
+static void cls_pmr_marking(void)
+{
+ test_pmr_series(MAX_NUM_UDP, 1);
+}
+
+static void cls_pmr_term_custom_frame(void)
+{
+ test_pmr_term_custom(0);
+}
+
+static void cls_pmr_term_custom_l3(void)
+{
+ test_pmr_term_custom(1);
+}
+
+static void test_pmr_term_ipsec_spi_ah(odp_bool_t is_ipv6)
+{
+ uint32_t val;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+ odp_packet_t pkt;
+ odph_ahhdr_t *ah;
+
+ val = odp_cpu_to_be_32(0x11223344);
+ mask = odp_cpu_to_be_32(0xffffffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IPSEC_SPI;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_AH;
+ pkt_info.ipv6 = is_ipv6;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ah = (odph_ahhdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ ah->spi = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ ah = (odph_ahhdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ ah->spi = val + 1;
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipsec_spi_ah_ipv4(void)
+{
+ test_pmr_term_ipsec_spi_ah(TEST_IPV4);
+}
+
+static void test_pmr_term_ipsec_spi_esp(odp_bool_t is_ipv6)
+{
+ uint32_t val;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ cls_packet_info_t pkt_info;
+ odp_packet_t pkt;
+ odph_esphdr_t *esp;
+
+ val = odp_cpu_to_be_32(0x11223344);
+ mask = odp_cpu_to_be_32(0xffffffff);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_IPSEC_SPI;
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_ESP;
+ pkt_info.ipv6 = is_ipv6;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ esp = (odph_esphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ esp->spi = val;
+
+ test_pmr(&pmr_param, pkt, MATCH);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ esp = (odph_esphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ esp->spi = val + 1;
+
+ test_pmr(&pmr_param, pkt, NO_MATCH);
+}
+
+static void cls_pmr_term_ipsec_spi_esp_ipv4(void)
+{
+ test_pmr_term_ipsec_spi_esp(TEST_IPV4);
+}
+
+static void cls_pmr_term_ipsec_spi_ah_ipv6(void)
+{
+ test_pmr_term_ipsec_spi_ah(TEST_IPV6);
+}
+
+static void cls_pmr_term_ipsec_spi_esp_ipv6(void)
+{
+ test_pmr_term_ipsec_spi_esp(TEST_IPV6);
+}
+
+static int check_capa_tcp_dport(void)
+{
+ return cls_capa.supported_terms.bit.tcp_dport;
+}
+
+static int check_capa_tcp_sport(void)
+{
+ return cls_capa.supported_terms.bit.tcp_sport;
+}
+
+static int check_capa_udp_dport(void)
+{
+ return cls_capa.supported_terms.bit.udp_dport;
+}
+
+static int check_capa_udp_sport(void)
+{
+ return cls_capa.supported_terms.bit.udp_sport;
+}
+
+static int check_capa_ip_proto(void)
+{
+ return cls_capa.supported_terms.bit.ip_proto;
+}
+
+static int check_capa_ip_dscp(void)
+{
+ return cls_capa.supported_terms.bit.ip_dscp;
+}
+
+static int check_capa_dmac(void)
+{
+ return cls_capa.supported_terms.bit.dmac;
+}
+
+static int check_capa_ipv4_saddr(void)
+{
+ return cls_capa.supported_terms.bit.sip_addr;
+}
+
+static int check_capa_ipv4_daddr(void)
+{
+ return cls_capa.supported_terms.bit.dip_addr;
+}
+
+static int check_capa_ipv6_saddr(void)
+{
+ return cls_capa.supported_terms.bit.sip6_addr;
+}
+
+static int check_capa_ipv6_daddr(void)
+{
+ return cls_capa.supported_terms.bit.dip6_addr;
+}
+
+static int check_capa_packet_len(void)
+{
+ return cls_capa.supported_terms.bit.len;
+}
+
+static int check_capa_vlan_id_0(void)
+{
+ return cls_capa.supported_terms.bit.vlan_id_0;
+}
+
+static int check_capa_vlan_id_x(void)
+{
+ return cls_capa.supported_terms.bit.vlan_id_x;
+}
+
+static int check_capa_vlan_pcp_0(void)
+{
+ return cls_capa.supported_terms.bit.vlan_pcp_0;
+}
+
+static int check_capa_ethtype_0(void)
+{
+ return cls_capa.supported_terms.bit.ethtype_0;
+}
+
+static int check_capa_ethtype_x(void)
+{
+ return cls_capa.supported_terms.bit.ethtype_x;
+}
+
+static int check_capa_custom_frame(void)
+{
+ return cls_capa.supported_terms.bit.custom_frame;
+}
+
+static int check_capa_custom_l3(void)
+{
+ return cls_capa.supported_terms.bit.custom_l3;
+}
+
+static int check_capa_ipsec_spi(void)
+{
+ return cls_capa.supported_terms.bit.ipsec_spi;
+}
+
+static int check_capa_pmr_series(void)
+{
+ uint64_t support;
+
+ support = cls_capa.supported_terms.bit.dip_addr &&
+ cls_capa.supported_terms.bit.udp_dport;
+
+ return support;
+}
+
+static int check_capa_pmr_marking(void)
+{
+ uint64_t terms;
+
+ terms = cls_capa.supported_terms.bit.dip_addr &&
+ cls_capa.supported_terms.bit.udp_dport;
+
+ /* one PMR for IP, MAX_NUM_UDP PMRs for UDP */
+ if (terms && cls_capa.max_mark >= (MARK_UDP + MAX_NUM_UDP - 1))
+ return 1;
+
+ return 0;
+}
+
+static int check_capa_sctp_sport(void)
+{
+ return cls_capa.supported_terms.bit.sctp_sport;
+}
+
+static int check_capa_sctp_dport(void)
+{
+ return cls_capa.supported_terms.bit.sctp_dport;
+}
+
+static int check_capa_icmp_type(void)
+{
+ return cls_capa.supported_terms.bit.icmp_type;
+}
+
+static int check_capa_icmp_code(void)
+{
+ return cls_capa.supported_terms.bit.icmp_code;
+}
+
+static int check_capa_icmp_id(void)
+{
+ return cls_capa.supported_terms.bit.icmp_id;
+}
+
+static int check_capa_gtpu_teid(void)
+{
+ return cls_capa.supported_terms.bit.gtpv1_teid;
+}
+
+static int check_capa_igmp_grpaddr(void)
+{
+ return cls_capa.supported_terms.bit.igmp_grp_addr;
+}
+
+odp_testinfo_t classification_suite_pmr[] = {
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_tcp_dport, check_capa_tcp_dport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_tcp_sport, check_capa_tcp_sport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_udp_dport, check_capa_udp_dport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_udp_sport, check_capa_udp_sport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_gtpu_teid, check_capa_gtpu_teid),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_igmp_grpaddr, check_capa_igmp_grpaddr),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_sctp_sport, check_capa_sctp_sport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_sctp_dport, check_capa_sctp_dport),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_icmp_type, check_capa_icmp_type),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_icmp_code, check_capa_icmp_code),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_icmp_id, check_capa_icmp_id),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv4_proto, check_capa_ip_proto),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv6_proto, check_capa_ip_proto),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv4_dscp, check_capa_ip_dscp),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv6_dscp, check_capa_ip_dscp),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_dmac, check_capa_dmac),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_pool_set, check_capa_ip_proto),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_queue_set, check_capa_ip_proto),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv4_saddr, check_capa_ipv4_saddr),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv4_daddr, check_capa_ipv4_daddr),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv6saddr, check_capa_ipv6_saddr),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipv6daddr, check_capa_ipv6_daddr),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_packet_len, check_capa_packet_len),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_vlan_id_0, check_capa_vlan_id_0),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_vlan_id_x, check_capa_vlan_id_x),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_vlan_pcp_0, check_capa_vlan_pcp_0),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_eth_type_0, check_capa_ethtype_0),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_eth_type_x, check_capa_ethtype_x),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_custom_frame, check_capa_custom_frame),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_custom_l3, check_capa_custom_l3),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipsec_spi_ah_ipv4, check_capa_ipsec_spi),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipsec_spi_esp_ipv4, check_capa_ipsec_spi),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipsec_spi_ah_ipv6, check_capa_ipsec_spi),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_term_ipsec_spi_esp_ipv6, check_capa_ipsec_spi),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_serial, check_capa_pmr_series),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_parallel, check_capa_pmr_series),
+ ODP_TEST_INFO(cls_pktin_classifier_flag),
+ ODP_TEST_INFO(cls_pmr_term_tcp_dport_multi),
+ ODP_TEST_INFO_CONDITIONAL(cls_pmr_marking, check_capa_pmr_marking),
+ ODP_TEST_INFO_NULL,
+};
diff --git a/test/validation/api/classification/odp_classification_tests.c b/test/validation/api/classification/odp_classification_tests.c
new file mode 100644
index 000000000..5a715c63d
--- /dev/null
+++ b/test/validation/api/classification/odp_classification_tests.c
@@ -0,0 +1,978 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include "odp_classification_testsuites.h"
+#include "classification.h"
+#include <odp_cunit_common.h>
+#include <odp/helper/odph_api.h>
+
+static odp_cos_t cos_list[CLS_ENTRIES];
+static odp_pmr_t pmr_list[CLS_ENTRIES];
+static odp_queue_t queue_list[CLS_ENTRIES];
+static odp_pool_t pool_list[CLS_ENTRIES];
+
+static odp_pool_t pool_default;
+static odp_pktio_t pktio_loop;
+static odp_pktio_capability_t pktio_capa;
+static odp_cls_testcase_u tc;
+
+#define NUM_COS_PMR_CHAIN 2
+#define NUM_COS_DEFAULT 1
+#define NUM_COS_DROP 1
+#define NUM_COS_ERROR 1
+#define NUM_COS_L2_PRIO CLS_L2_QOS_MAX
+#define NUM_COS_PMR 1
+#define NUM_COS_COMPOSITE 1
+#define PKTV_DEFAULT_SIZE 8
+
+/** sequence number of IP packets */
+static odp_atomic_u32_t seq;
+
+/* default packet info */
+static cls_packet_info_t default_pkt_info;
+
+/* Packet vector configuration */
+static odp_pktin_vector_config_t pktv_config;
+
+static int classification_suite_common_init(odp_bool_t enable_pktv)
+{
+ int i;
+ int ret;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+
+ tc.all_bits = 0;
+
+ pool_default = pool_create("classification_pool");
+ if (ODP_POOL_INVALID == pool_default) {
+ ODPH_ERR("Packet pool creation failed\n");
+ return -1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio_loop = odp_pktio_open("loop", pool_default, &pktio_param);
+ if (pktio_loop == ODP_PKTIO_INVALID) {
+ ret = odp_pool_destroy(pool_default);
+ if (ret)
+ ODPH_ERR("Unable to destroy pool\n");
+ return -1;
+ }
+
+ ret = odp_pktio_capability(pktio_loop, &pktio_capa);
+ if (ret) {
+ ODPH_ERR("Unable to get pktio capability\n");
+ return -1;
+ }
+
+ memset(&default_pkt_info, 0, sizeof(cls_packet_info_t));
+ default_pkt_info.pool = pool_default;
+ default_pkt_info.seq = &seq;
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ pktin_param.classifier_enable = true;
+ pktin_param.hash_enable = false;
+
+ if (enable_pktv) {
+ odp_pktio_capability_t capa;
+ odp_pool_t pktv_pool;
+
+ pktv_pool = pktv_pool_create("packet_vector_pool");
+ if (pktv_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet vector pool creation failed\n");
+ return -1;
+ }
+
+ if (odp_pktio_capability(pktio_loop, &capa)) {
+ ODPH_ERR("Pktio capability failed\n");
+ return -1;
+ }
+
+ if (!capa.vector.supported) {
+ printf("Packet vector mode is not supported. Test suite skipped.\n");
+ pktv_config.enable = false;
+ pktv_config.pool = pktv_pool;
+ } else {
+ pktin_param.vector.enable = true;
+ pktin_param.vector.pool = pktv_pool;
+ pktin_param.vector.max_size = capa.vector.max_size < PKTV_DEFAULT_SIZE ?
+ capa.vector.max_size : PKTV_DEFAULT_SIZE;
+ pktin_param.vector.max_tmo_ns = capa.vector.min_tmo_ns;
+
+ /* Copy packet vector config for global access */
+ pktv_config = pktin_param.vector;
+ }
+ }
+
+ if (odp_pktin_queue_config(pktio_loop, &pktin_param)) {
+ ODPH_ERR("Pktin queue config failed\n");
+ return -1;
+ }
+
+ if (odp_pktout_queue_config(pktio_loop, NULL)) {
+ ODPH_ERR("Pktout queue config failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < CLS_ENTRIES; i++)
+ cos_list[i] = ODP_COS_INVALID;
+
+ for (i = 0; i < CLS_ENTRIES; i++)
+ pmr_list[i] = ODP_PMR_INVALID;
+
+ for (i = 0; i < CLS_ENTRIES; i++)
+ queue_list[i] = ODP_QUEUE_INVALID;
+
+ for (i = 0; i < CLS_ENTRIES; i++)
+ pool_list[i] = ODP_POOL_INVALID;
+
+ odp_atomic_init_u32(&seq, 0);
+
+ ret = odp_pktio_start(pktio_loop);
+ if (ret) {
+ ODPH_ERR("Unable to start loop\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int classification_suite_common_term(odp_bool_t enable_pktv)
+{
+ int i;
+ int retcode = 0;
+
+ if (0 > stop_pktio(pktio_loop)) {
+ ODPH_ERR("Stop pktio failed\n");
+ retcode = -1;
+ }
+
+ if (0 > odp_pktio_close(pktio_loop)) {
+ ODPH_ERR("Pktio close failed\n");
+ retcode = -1;
+ }
+
+ for (i = 0; i < CLS_ENTRIES; i++) {
+ if (pmr_list[i] != ODP_PMR_INVALID)
+ odp_cls_pmr_destroy(pmr_list[i]);
+ }
+
+ for (i = 0; i < CLS_ENTRIES; i++) {
+ if (cos_list[i] != ODP_COS_INVALID)
+ odp_cos_destroy(cos_list[i]);
+ }
+
+ if (0 != odp_pool_destroy(pool_default)) {
+ ODPH_ERR("Pool_default destroy failed\n");
+ retcode = -1;
+ }
+
+ if (enable_pktv) {
+ if (odp_pool_destroy(pktv_config.pool)) {
+ ODPH_ERR("Packet vector pool destroy failed\n");
+ retcode = -1;
+ }
+ }
+
+ for (i = 0; i < CLS_ENTRIES; i++) {
+ if (queue_list[i] != ODP_QUEUE_INVALID)
+ odp_queue_destroy(queue_list[i]);
+ }
+
+ for (i = 0; i < CLS_ENTRIES; i++) {
+ if (pool_list[i] != ODP_POOL_INVALID)
+ odp_pool_destroy(pool_list[i]);
+ }
+
+ if (odp_cunit_print_inactive())
+ retcode = -1;
+
+ return retcode;
+}
+
+int classification_suite_init(void)
+{
+ return classification_suite_common_init(false);
+}
+
+int classification_suite_term(void)
+{
+ return classification_suite_common_term(false);
+}
+
+int classification_suite_pktv_init(void)
+{
+ return classification_suite_common_init(true);
+}
+
+int classification_suite_pktv_term(void)
+{
+ return classification_suite_common_term(true);
+}
+
+void configure_cls_pmr_chain(odp_bool_t enable_pktv)
+{
+ /* PKTIO --> PMR_SRC(SRC IP ADDR) --> PMR_DST (TCP SPORT) */
+
+ /* Packet matching only the SRC IP ADDR should be delivered
+ in queue[CLS_PMR_CHAIN_SRC] and a packet matching both SRC IP ADDR and
+ TCP SPORT should be delivered to queue[CLS_PMR_CHAIN_DST] */
+
+ uint16_t val;
+ uint16_t maskport;
+ char cosname[ODP_QUEUE_NAME_LEN];
+ odp_queue_param_t qparam;
+ odp_cls_cos_param_t cls_param;
+ char queuename[ODP_QUEUE_NAME_LEN];
+ char poolname[ODP_POOL_NAME_LEN];
+ uint32_t addr;
+ uint32_t mask;
+ odp_pmr_param_t pmr_param;
+ odp_schedule_capability_t schedule_capa;
+
+ CU_ASSERT_FATAL(odp_schedule_capability(&schedule_capa) == 0);
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ qparam.sched.lock_count = schedule_capa.max_ordered_locks;
+ sprintf(queuename, "%s", "SrcQueue");
+
+ queue_list[CLS_PMR_CHAIN_SRC] = odp_queue_create(queuename, &qparam);
+
+ CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_SRC] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "%s", "SrcPool");
+ pool_list[CLS_PMR_CHAIN_SRC] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_SRC] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "SrcCos");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_PMR_CHAIN_SRC];
+ cls_param.queue = queue_list[CLS_PMR_CHAIN_SRC];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_PMR_CHAIN_SRC] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_SRC] != ODP_COS_INVALID);
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ sprintf(queuename, "%s", "DstQueue");
+
+ queue_list[CLS_PMR_CHAIN_DST] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_DST] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "%s", "DstPool");
+ pool_list[CLS_PMR_CHAIN_DST] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_DST] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "DstCos");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_PMR_CHAIN_DST];
+ cls_param.queue = queue_list[CLS_PMR_CHAIN_DST];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_PMR_CHAIN_DST] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_DST] != ODP_COS_INVALID);
+
+ parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
+ addr = odp_cpu_to_be_32(addr);
+ mask = odp_cpu_to_be_32(mask);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_SIP_ADDR;
+ pmr_param.match.value = &addr;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(addr);
+ pmr_list[CLS_PMR_CHAIN_SRC] =
+ odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_DEFAULT],
+ cos_list[CLS_PMR_CHAIN_SRC]);
+ CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_SRC] != ODP_PMR_INVALID);
+
+ val = odp_cpu_to_be_16(CLS_PMR_CHAIN_PORT);
+ maskport = odp_cpu_to_be_16(0xffff);
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = find_first_supported_l3_pmr();
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &maskport;
+ pmr_param.val_sz = sizeof(val);
+ pmr_list[CLS_PMR_CHAIN_DST] =
+ odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_PMR_CHAIN_SRC],
+ cos_list[CLS_PMR_CHAIN_DST]);
+ CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_DST] != ODP_PMR_INVALID);
+}
+
+void test_cls_pmr_chain(odp_bool_t enable_pktv)
+{
+ odp_packet_t pkt;
+ odph_ipv4hdr_t *ip;
+ odp_queue_t queue;
+ odp_pool_t pool;
+ uint32_t addr = 0;
+ uint32_t mask;
+ uint32_t seqno = 0;
+ cls_packet_info_t pkt_info;
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
+ ip->src_addr = odp_cpu_to_be_32(addr);
+ odph_ipv4_csum_update(pkt);
+
+ set_first_supported_pmr_port(pkt, CLS_PMR_CHAIN_PORT);
+
+ enqueue_pktio_interface(pkt, pktio_loop);
+
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_DST]);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_DST]);
+ odp_packet_free(pkt);
+
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask);
+ ip->src_addr = odp_cpu_to_be_32(addr);
+ odph_ipv4_csum_update(pkt);
+
+ enqueue_pktio_interface(pkt, pktio_loop);
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_SRC]);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_SRC]);
+ odp_packet_free(pkt);
+}
+
+void configure_pktio_default_cos(odp_bool_t enable_pktv)
+{
+ int retval;
+ odp_queue_param_t qparam;
+ odp_cls_cos_param_t cls_param;
+ char cosname[ODP_COS_NAME_LEN];
+ char queuename[ODP_QUEUE_NAME_LEN];
+ char poolname[ODP_POOL_NAME_LEN];
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ sprintf(queuename, "%s", "DefaultQueue");
+ queue_list[CLS_DEFAULT] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(queue_list[CLS_DEFAULT] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "DefaultPool");
+ pool_list[CLS_DEFAULT] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_DEFAULT] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "DefaultCoS");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_DEFAULT];
+ cls_param.queue = queue_list[CLS_DEFAULT];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_DEFAULT] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_DEFAULT] != ODP_COS_INVALID);
+
+ retval = odp_pktio_default_cos_set(pktio_loop, cos_list[CLS_DEFAULT]);
+ CU_ASSERT(retval == 0);
+}
+
+void test_pktio_default_cos(odp_bool_t enable_pktv)
+{
+ odp_packet_t pkt;
+ odp_queue_t queue;
+ uint32_t seqno = 0;
+ odp_pool_t pool;
+ cls_packet_info_t pkt_info;
+
+ /* create a default packet */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ enqueue_pktio_interface(pkt, pktio_loop);
+
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ /* Default packet should be received in default queue */
+ CU_ASSERT(queue == queue_list[CLS_DEFAULT]);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_DEFAULT]);
+
+ odp_packet_free(pkt);
+}
+
+void configure_pktio_drop_cos(odp_bool_t enable_pktv, uint32_t max_cos_stats)
+{
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ odp_cls_cos_param_t cls_param;
+ char cosname[ODP_COS_NAME_LEN];
+
+ sprintf(cosname, "DropCoS");
+ odp_cls_cos_param_init(&cls_param);
+
+ cls_param.action = ODP_COS_ACTION_DROP;
+ cls_param.stats_enable = max_cos_stats > 0;
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_DROP] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_DROP] != ODP_COS_INVALID);
+
+ val = odp_cpu_to_be_16(CLS_DROP_PORT);
+ mask = odp_cpu_to_be_16(0xffff);
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = find_first_supported_l3_pmr();
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr_list[CLS_DROP] = odp_cls_pmr_create(&pmr_param, 1,
+ cos_list[CLS_DEFAULT],
+ cos_list[CLS_DROP]);
+ CU_ASSERT_FATAL(pmr_list[CLS_DROP] != ODP_PMR_INVALID);
+}
+
+void test_pktio_drop_cos(odp_bool_t enable_pktv)
+{
+ odp_packet_t pkt;
+ odp_queue_t queue;
+ uint32_t seqno = 0;
+ cls_packet_info_t pkt_info;
+ odp_cls_capability_t capa;
+ odp_cls_cos_stats_t start, stop;
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ set_first_supported_pmr_port(pkt, CLS_DROP_PORT);
+ CU_ASSERT(odp_cls_cos_stats(cos_list[CLS_DROP], &start) == 0);
+ enqueue_pktio_interface(pkt, pktio_loop);
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT(odp_cls_cos_stats(cos_list[CLS_DROP], &stop) == 0);
+ CU_ASSERT_FATAL(pkt == ODP_PACKET_INVALID);
+ if (capa.stats.cos.counter.packets)
+ CU_ASSERT((stop.packets - start.packets) == 1);
+ if (capa.stats.cos.counter.discards)
+ CU_ASSERT((stop.discards - start.discards) == 0);
+ if (capa.stats.cos.counter.errors)
+ CU_ASSERT((stop.errors - start.errors) == 0);
+}
+
+static int check_queue_stats(void)
+{
+ odp_cls_capability_t capa;
+
+ if (odp_cls_capability(&capa))
+ return ODP_TEST_INACTIVE;
+
+ if (capa.stats.queue.all_counters)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void cls_queue_stats(odp_bool_t enable_pktv)
+{
+ odp_cls_capability_t capa;
+ odp_cls_queue_stats_t stats_start;
+ odp_cls_queue_stats_t stats_stop;
+ odp_cos_t cos;
+ odp_queue_t queue;
+
+ /* Default CoS used for test packets */
+ if (!tc.default_cos || !TEST_DEFAULT) {
+ printf("Default CoS not supported, skipping test\n");
+ return;
+ }
+
+ cos = cos_list[CLS_DEFAULT];
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+ queue = odp_cos_queue(cos);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ CU_ASSERT_FATAL(odp_cls_capability(&capa) == 0);
+
+ CU_ASSERT(odp_cls_queue_stats(cos, queue, &stats_start) == 0);
+
+ test_pktio_default_cos(enable_pktv);
+
+ CU_ASSERT(odp_cls_queue_stats(cos, queue, &stats_stop) == 0);
+
+ if (capa.stats.queue.counter.packets)
+ CU_ASSERT(stats_stop.packets > stats_start.packets);
+ if (capa.stats.queue.counter.octets)
+ CU_ASSERT(stats_stop.octets > stats_start.octets);
+ CU_ASSERT((stats_stop.discards - stats_start.discards) == 0);
+ CU_ASSERT((stats_stop.errors - stats_start.errors) == 0);
+
+ printf("\nQueue statistics\n----------------\n");
+ printf(" discards: %" PRIu64 "\n", stats_stop.discards);
+ printf(" errors: %" PRIu64 "\n", stats_stop.errors);
+ printf(" octets: %" PRIu64 "\n", stats_stop.octets);
+ printf(" packets: %" PRIu64 "\n", stats_stop.packets);
+
+ /* Check that all unsupported counters are still zero */
+ if (!capa.stats.queue.counter.discards)
+ CU_ASSERT(stats_stop.discards == 0);
+ if (!capa.stats.queue.counter.errors)
+ CU_ASSERT(stats_stop.errors == 0);
+ if (!capa.stats.queue.counter.octets)
+ CU_ASSERT(stats_stop.octets == 0);
+ if (!capa.stats.queue.counter.packets)
+ CU_ASSERT(stats_stop.packets == 0);
+}
+
+static void cls_queue_stats_pkt(void)
+{
+ cls_queue_stats(false);
+}
+
+static void cls_queue_stats_pktv(void)
+{
+ cls_queue_stats(true);
+}
+
+void configure_pktio_error_cos(odp_bool_t enable_pktv)
+{
+ int retval;
+ odp_queue_param_t qparam;
+ odp_cls_cos_param_t cls_param;
+ char queuename[ODP_QUEUE_NAME_LEN];
+ char cosname[ODP_COS_NAME_LEN];
+ char poolname[ODP_POOL_NAME_LEN];
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_min_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ sprintf(queuename, "%s", "ErrorCos");
+
+ queue_list[CLS_ERROR] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(queue_list[CLS_ERROR] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "ErrorPool");
+ pool_list[CLS_ERROR] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_ERROR] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "%s", "ErrorCos");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_ERROR];
+ cls_param.queue = queue_list[CLS_ERROR];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_ERROR] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_ERROR] != ODP_COS_INVALID);
+
+ retval = odp_pktio_error_cos_set(pktio_loop, cos_list[CLS_ERROR]);
+ CU_ASSERT(retval == 0);
+}
+
+void test_pktio_error_cos(odp_bool_t enable_pktv)
+{
+ odp_queue_t queue;
+ odp_packet_t pkt;
+ odp_pool_t pool;
+ cls_packet_info_t pkt_info;
+
+ /*Create an error packet */
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+
+ /* Incorrect IpV4 version */
+ ip->ver_ihl = 8 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->chksum = 0;
+ enqueue_pktio_interface(pkt, pktio_loop);
+
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ /* Error packet should be received in error queue */
+ CU_ASSERT(queue == queue_list[CLS_ERROR]);
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_ERROR]);
+ odp_packet_free(pkt);
+}
+
+static void cls_pktio_set_skip(void)
+{
+ int retval;
+ size_t offset = 5;
+
+ retval = odp_pktio_skip_set(pktio_loop, offset);
+ CU_ASSERT(retval == 0);
+
+ retval = odp_pktio_skip_set(ODP_PKTIO_INVALID, offset);
+ CU_ASSERT(retval < 0);
+
+ /* reset skip value to zero as validation suite expects
+ offset to be zero*/
+
+ retval = odp_pktio_skip_set(pktio_loop, 0);
+ CU_ASSERT(retval == 0);
+}
+
+static void cls_pktio_set_headroom(void)
+{
+ size_t headroom;
+ int retval;
+
+ headroom = 5;
+ retval = odp_pktio_headroom_set(pktio_loop, headroom);
+ CU_ASSERT(retval == 0);
+
+ retval = odp_pktio_headroom_set(ODP_PKTIO_INVALID, headroom);
+ CU_ASSERT(retval < 0);
+}
+
+void configure_pmr_cos(odp_bool_t enable_pktv)
+{
+ uint16_t val;
+ uint16_t mask;
+ odp_pmr_param_t pmr_param;
+ odp_queue_param_t qparam;
+ odp_cls_cos_param_t cls_param;
+ char cosname[ODP_COS_NAME_LEN];
+ char queuename[ODP_QUEUE_NAME_LEN];
+ char poolname[ODP_POOL_NAME_LEN];
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ sprintf(queuename, "%s", "PMR_CoS");
+
+ queue_list[CLS_PMR] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(queue_list[CLS_PMR] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "PMR_Pool");
+ pool_list[CLS_PMR] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_PMR] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "PMR_CoS");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_PMR];
+ cls_param.queue = queue_list[CLS_PMR];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_PMR] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_PMR] != ODP_COS_INVALID);
+
+ val = odp_cpu_to_be_16(CLS_PMR_PORT);
+ mask = odp_cpu_to_be_16(0xffff);
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = find_first_supported_l3_pmr();
+ pmr_param.match.value = &val;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = sizeof(val);
+
+ pmr_list[CLS_PMR] = odp_cls_pmr_create(&pmr_param, 1,
+ cos_list[CLS_DEFAULT],
+ cos_list[CLS_PMR]);
+ CU_ASSERT_FATAL(pmr_list[CLS_PMR] != ODP_PMR_INVALID);
+}
+
+void test_pmr_cos(odp_bool_t enable_pktv)
+{
+ odp_packet_t pkt;
+ odp_queue_t queue;
+ odp_pool_t pool;
+ uint32_t seqno = 0;
+ cls_packet_info_t pkt_info;
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+ set_first_supported_pmr_port(pkt, CLS_PMR_PORT);
+ enqueue_pktio_interface(pkt, pktio_loop);
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(queue == queue_list[CLS_PMR]);
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_PMR]);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ odp_packet_free(pkt);
+}
+
+void configure_pktio_pmr_composite(odp_bool_t enable_pktv)
+{
+ odp_pmr_param_t pmr_params[2];
+ uint16_t val;
+ uint16_t maskport;
+ int num_terms = 2; /* one pmr for each L3 and L4 */
+ odp_queue_param_t qparam;
+ odp_cls_cos_param_t cls_param;
+ char cosname[ODP_COS_NAME_LEN];
+ char queuename[ODP_QUEUE_NAME_LEN];
+ char poolname[ODP_POOL_NAME_LEN];
+ uint32_t addr = 0;
+ uint32_t mask;
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ sprintf(queuename, "%s", "cos_pmr_composite_queue");
+
+ queue_list[CLS_PMR_SET] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(queue_list[CLS_PMR_SET] != ODP_QUEUE_INVALID);
+
+ sprintf(poolname, "cos_pmr_composite_pool");
+ pool_list[CLS_PMR_SET] = pool_create(poolname);
+ CU_ASSERT_FATAL(pool_list[CLS_PMR_SET] != ODP_POOL_INVALID);
+
+ sprintf(cosname, "cos_pmr_composite");
+ odp_cls_cos_param_init(&cls_param);
+ cls_param.pool = pool_list[CLS_PMR_SET];
+ cls_param.queue = queue_list[CLS_PMR_SET];
+
+ if (enable_pktv) {
+ cls_param.vector.enable = true;
+ cls_param.vector.pool = pktv_config.pool;
+ cls_param.vector.max_size = pktv_config.max_size;
+ cls_param.vector.max_tmo_ns = pktv_config.max_tmo_ns;
+ }
+
+ cos_list[CLS_PMR_SET] = odp_cls_cos_create(cosname, &cls_param);
+ CU_ASSERT_FATAL(cos_list[CLS_PMR_SET] != ODP_COS_INVALID);
+
+ parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask);
+ addr = odp_cpu_to_be_32(addr);
+ mask = odp_cpu_to_be_32(mask);
+
+ odp_cls_pmr_param_init(&pmr_params[0]);
+ pmr_params[0].term = ODP_PMR_SIP_ADDR;
+ pmr_params[0].match.value = &addr;
+ pmr_params[0].match.mask = &mask;
+ pmr_params[0].val_sz = sizeof(addr);
+
+ val = odp_cpu_to_be_16(CLS_PMR_SET_PORT);
+ maskport = odp_cpu_to_be_16(0xffff);
+ odp_cls_pmr_param_init(&pmr_params[1]);
+ pmr_params[1].term = find_first_supported_l3_pmr();
+ pmr_params[1].match.value = &val;
+ pmr_params[1].match.mask = &maskport;
+ pmr_params[1].range_term = false;
+ pmr_params[1].val_sz = sizeof(val);
+
+ pmr_list[CLS_PMR_SET] = odp_cls_pmr_create(pmr_params, num_terms,
+ cos_list[CLS_DEFAULT],
+ cos_list[CLS_PMR_SET]);
+ CU_ASSERT_FATAL(pmr_list[CLS_PMR_SET] != ODP_PMR_INVALID);
+}
+
+void test_pktio_pmr_composite_cos(odp_bool_t enable_pktv)
+{
+ uint32_t addr = 0;
+ uint32_t mask;
+ odph_ipv4hdr_t *ip;
+ odp_packet_t pkt;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ uint32_t seqno = 0;
+ cls_packet_info_t pkt_info;
+
+ pkt_info = default_pkt_info;
+ pkt_info.l4_type = CLS_PKT_L4_UDP;
+ pkt = create_packet(pkt_info);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ seqno = cls_pkt_get_seq(pkt);
+ CU_ASSERT(seqno != TEST_SEQ_INVALID);
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask);
+ ip->src_addr = odp_cpu_to_be_32(addr);
+ odph_ipv4_csum_update(pkt);
+
+ set_first_supported_pmr_port(pkt, CLS_PMR_SET_PORT);
+ enqueue_pktio_interface(pkt, pktio_loop);
+ pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS, enable_pktv);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(queue == queue_list[CLS_PMR_SET]);
+ pool = odp_packet_pool(pkt);
+ CU_ASSERT(pool == pool_list[CLS_PMR_SET]);
+ CU_ASSERT(seqno == cls_pkt_get_seq(pkt));
+ odp_packet_free(pkt);
+}
+
+static void cls_pktio_configure_common(odp_bool_t enable_pktv)
+{
+ odp_cls_capability_t capa;
+ int num_cos;
+
+ odp_cls_capability(&capa);
+ num_cos = capa.max_cos;
+
+ /* Configure the Different CoS for the pktio interface */
+ if (num_cos >= NUM_COS_DEFAULT && TEST_DEFAULT) {
+ configure_pktio_default_cos(enable_pktv);
+ tc.default_cos = 1;
+ num_cos -= NUM_COS_DEFAULT;
+ }
+ if (num_cos >= NUM_COS_DEFAULT && TEST_DROP) {
+ configure_pktio_drop_cos(enable_pktv, capa.max_cos_stats);
+ tc.drop_cos = 1;
+ num_cos -= NUM_COS_DROP;
+ }
+ if (num_cos >= NUM_COS_ERROR && TEST_ERROR) {
+ configure_pktio_error_cos(enable_pktv);
+ tc.error_cos = 1;
+ num_cos -= NUM_COS_ERROR;
+ }
+ if (num_cos >= NUM_COS_PMR_CHAIN && TEST_PMR_CHAIN) {
+ configure_cls_pmr_chain(enable_pktv);
+ tc.pmr_chain = 1;
+ num_cos -= NUM_COS_PMR_CHAIN;
+ }
+ if (num_cos >= NUM_COS_PMR && TEST_PMR) {
+ configure_pmr_cos(enable_pktv);
+ tc.pmr_cos = 1;
+ num_cos -= NUM_COS_PMR;
+ }
+ if (num_cos >= NUM_COS_COMPOSITE && TEST_PMR_SET) {
+ configure_pktio_pmr_composite(enable_pktv);
+ tc.pmr_composite_cos = 1;
+ num_cos -= NUM_COS_COMPOSITE;
+ }
+
+}
+
+static void cls_pktio_configure(void)
+{
+ cls_pktio_configure_common(false);
+}
+
+static void cls_pktio_configure_pktv(void)
+{
+ cls_pktio_configure_common(true);
+}
+
+static void cls_pktio_test_common(odp_bool_t enable_pktv)
+{
+ /* Test Different CoS on the pktio interface */
+ if (tc.default_cos && TEST_DEFAULT)
+ test_pktio_default_cos(enable_pktv);
+ if (tc.drop_cos && TEST_DROP)
+ test_pktio_drop_cos(enable_pktv);
+ if (tc.error_cos && TEST_ERROR)
+ test_pktio_error_cos(enable_pktv);
+ if (tc.pmr_chain && TEST_PMR_CHAIN)
+ test_cls_pmr_chain(enable_pktv);
+ if (tc.pmr_cos && TEST_PMR)
+ test_pmr_cos(enable_pktv);
+ if (tc.pmr_composite_cos && TEST_PMR_SET)
+ test_pktio_pmr_composite_cos(enable_pktv);
+}
+
+static void cls_pktio_test(void)
+{
+ cls_pktio_test_common(false);
+}
+
+static void cls_pktio_test_pktv(void)
+{
+ cls_pktio_test_common(true);
+}
+
+static int check_pktv(void)
+{
+ return pktv_config.enable ? ODP_TEST_ACTIVE : ODP_TEST_INACTIVE;
+}
+
+static int check_capa_skip_offset(void)
+{
+ return pktio_capa.set_op.op.skip_offset;
+}
+
+odp_testinfo_t classification_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(cls_pktio_set_skip, check_capa_skip_offset),
+ ODP_TEST_INFO(cls_pktio_set_headroom),
+ ODP_TEST_INFO(cls_pktio_configure),
+ ODP_TEST_INFO(cls_pktio_test),
+ ODP_TEST_INFO_CONDITIONAL(cls_queue_stats_pkt, check_queue_stats),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_testinfo_t classification_suite_pktv[] = {
+ ODP_TEST_INFO_CONDITIONAL(cls_pktio_configure_pktv, check_pktv),
+ ODP_TEST_INFO_CONDITIONAL(cls_pktio_test_pktv, check_pktv),
+ ODP_TEST_INFO_CONDITIONAL(cls_queue_stats_pktv, check_queue_stats),
+ ODP_TEST_INFO_NULL,
+};
diff --git a/test/validation/api/classification/odp_classification_testsuites.h b/test/validation/api/classification/odp_classification_testsuites.h
new file mode 100644
index 000000000..34f93ee8d
--- /dev/null
+++ b/test/validation/api/classification/odp_classification_testsuites.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#ifndef ODP_CLASSIFICATION_TESTSUITES_H_
+#define ODP_CLASSIFICATION_TESTSUITES_H_
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include <stdbool.h>
+
+typedef enum cls_packet_l4_info {
+ CLS_PKT_L4_TCP,
+ CLS_PKT_L4_UDP,
+ CLS_PKT_L4_SCTP,
+ CLS_PKT_L4_ICMP,
+ CLS_PKT_L4_GTP,
+ CLS_PKT_L4_IGMP,
+ CLS_PKT_L4_AH,
+ CLS_PKT_L4_ESP
+} cls_packet_l4_info;
+
+typedef struct cls_packet_info {
+ odp_pool_t pool;
+ bool vlan;
+ bool vlan_qinq;
+ odp_atomic_u32_t *seq;
+ cls_packet_l4_info l4_type;
+ odp_bool_t ipv6;
+ uint8_t dscp;
+ uint32_t len;
+} cls_packet_info_t;
+
+typedef union odp_cls_testcase {
+ struct {
+ uint32_t default_cos:1;
+ uint32_t drop_cos:1;
+ uint32_t error_cos:1;
+ uint32_t pmr_chain:1;
+ uint32_t pmr_cos:1;
+ uint32_t pmr_composite_cos:1;
+ };
+ uint32_t all_bits;
+} odp_cls_testcase_u;
+
+extern odp_testinfo_t classification_suite[];
+extern odp_testinfo_t classification_suite_basic[];
+extern odp_testinfo_t classification_suite_pmr[];
+extern odp_testinfo_t classification_suite_pktv[];
+
+int classification_suite_init(void);
+int classification_suite_term(void);
+
+int classification_suite_pmr_term(void);
+int classification_suite_pmr_init(void);
+
+int classification_suite_pktv_init(void);
+int classification_suite_pktv_term(void);
+
+odp_packet_t create_packet(cls_packet_info_t pkt_info);
+int cls_pkt_set_seq(odp_packet_t pkt);
+uint32_t cls_pkt_get_seq(odp_packet_t pkt);
+odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool,
+ odp_bool_t cls_enable);
+void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos,
+ odp_queue_t *queue, odp_pool_t *pool);
+int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask);
+void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio);
+odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns, odp_bool_t enable_pktv);
+odp_pool_t pool_create(const char *poolname);
+odp_pool_t pktv_pool_create(const char *poolname);
+odp_queue_t queue_create(const char *queuename, bool sched);
+void configure_pktio_default_cos(odp_bool_t enable_pktv);
+void test_pktio_default_cos(odp_bool_t enable_pktv);
+void configure_pktio_drop_cos(odp_bool_t enable_pktv, uint32_t max_cos_stats);
+void test_pktio_drop_cos(odp_bool_t enable_pktv);
+void configure_pktio_error_cos(odp_bool_t enable_pktv);
+void test_pktio_error_cos(odp_bool_t enable_pktv);
+void configure_cls_pmr_chain(odp_bool_t enable_pktv);
+void test_cls_pmr_chain(odp_bool_t enable_pktv);
+void configure_cos_with_l2_priority(odp_bool_t enable_pktv);
+void test_cos_with_l2_priority(odp_bool_t enable_pktv);
+void configure_pmr_cos(odp_bool_t enable_pktv);
+void test_pmr_cos(odp_bool_t enable_pktv);
+void configure_pktio_pmr_composite(odp_bool_t enable_pktv);
+void test_pktio_pmr_composite_cos(odp_bool_t enable_pktv);
+int stop_pktio(odp_pktio_t pktio);
+odp_cls_pmr_term_t find_first_supported_l3_pmr(void);
+int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port);
+
+#endif /* ODP_BUFFER_TESTSUITES_H_ */
diff --git a/test/validation/api/comp/.gitignore b/test/validation/api/comp/.gitignore
new file mode 100644
index 000000000..97aea05ab
--- /dev/null
+++ b/test/validation/api/comp/.gitignore
@@ -0,0 +1 @@
+comp_main
diff --git a/test/validation/api/comp/Makefile.am b/test/validation/api/comp/Makefile.am
new file mode 100644
index 000000000..2e5d3a26b
--- /dev/null
+++ b/test/validation/api/comp/Makefile.am
@@ -0,0 +1,7 @@
+include ../Makefile.inc
+
+test_PROGRAMS = comp_main
+
+comp_main_SOURCES = \
+ comp.c \
+ test_vectors.h
diff --git a/test/validation/api/comp/comp.c b/test/validation/api/comp/comp.c
new file mode 100644
index 000000000..7078453df
--- /dev/null
+++ b/test/validation/api/comp/comp.c
@@ -0,0 +1,571 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_cunit_common.h>
+#include "test_vectors.h"
+
+#define TEST_NUM_PKT 64
+#define TEST_PKT_LEN (8 * 1024)
+
+#define SEGMENTED_TEST_PKT_LEN (16 * 1024)
+#define SEGMENTED_TEST_PATTERN 0xAA
+
+#define COMP_PACKET_POOL "packet_pool"
+#define COMP_OUT_QUEUE "comp-out"
+
+struct suite_context_s {
+ odp_comp_op_mode_t op_mode;
+ odp_pool_t pool;
+ odp_queue_t queue;
+};
+
+static struct suite_context_s suite_context;
+
+/**
+ * Check if given compression and hash algorithms are supported
+ *
+ * @param comp Compression algorithm
+ * @param hash Hash algorithm
+ *
+ * @retval ODP_TEST_ACTIVE when both algorithms are supported
+ * @retval ODP_TEST_INACTIVE when either algorithm is not supported
+ */
+static int check_comp_alg_support(odp_comp_alg_t comp,
+ odp_comp_hash_alg_t hash)
+{
+ odp_comp_capability_t capability;
+
+ if (odp_comp_capability(&capability))
+ return ODP_TEST_INACTIVE;
+
+ if (suite_context.op_mode == ODP_COMP_OP_MODE_SYNC &&
+ capability.sync == ODP_SUPPORT_NO)
+ return ODP_TEST_INACTIVE;
+ if (suite_context.op_mode == ODP_COMP_OP_MODE_ASYNC &&
+ capability.async == ODP_SUPPORT_NO)
+ return ODP_TEST_INACTIVE;
+
+ /* Compression algorithms */
+ switch (comp) {
+ case ODP_COMP_ALG_NULL:
+ if (!capability.comp_algos.bit.null)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_COMP_ALG_DEFLATE:
+ if (!capability.comp_algos.bit.deflate)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_COMP_ALG_ZLIB:
+ if (!capability.comp_algos.bit.zlib)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_COMP_ALG_LZS:
+ if (!capability.comp_algos.bit.lzs)
+ return ODP_TEST_INACTIVE;
+ break;
+ default:
+ ODPH_ERR("Unsupported compression algorithm\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ /* Hash algorithms */
+ switch (hash) {
+ case ODP_COMP_HASH_ALG_NONE:
+ if (!capability.hash_algos.bit.none)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_COMP_HASH_ALG_SHA1:
+ if (!capability.hash_algos.bit.sha1)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_COMP_HASH_ALG_SHA256:
+ if (!capability.hash_algos.bit.sha256)
+ return ODP_TEST_INACTIVE;
+ break;
+ default:
+ ODPH_ERR("Unsupported hash algorithm\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static odp_packet_t run_comp_op(odp_comp_op_t op,
+ odp_comp_alg_t comp_alg,
+ odp_comp_hash_alg_t hash_alg,
+ odp_packet_t inpkt,
+ unsigned int outtext_len)
+{
+ odp_comp_session_t session;
+ odp_comp_capability_t capa;
+ odp_comp_alg_capability_t comp_capa;
+ odp_comp_hash_alg_capability_t hash_capa;
+ odp_comp_session_param_t ses_params;
+ odp_comp_packet_op_param_t op_params;
+ odp_packet_t outpkt;
+ odp_comp_packet_result_t comp_result;
+ int rc;
+
+ rc = odp_comp_capability(&capa);
+ CU_ASSERT_FATAL(!rc);
+
+ if (comp_alg == ODP_COMP_ALG_NULL &&
+ !(capa.comp_algos.bit.null))
+ rc = -1;
+ if (comp_alg == ODP_COMP_ALG_DEFLATE &&
+ !(capa.comp_algos.bit.deflate))
+ rc = -1;
+ if (comp_alg == ODP_COMP_ALG_ZLIB &&
+ !(capa.comp_algos.bit.zlib))
+ rc = -1;
+ if (comp_alg == ODP_COMP_ALG_LZS &&
+ !(capa.comp_algos.bit.lzs))
+ rc = -1;
+
+ CU_ASSERT(!rc);
+
+ if (hash_alg == ODP_COMP_HASH_ALG_NONE &&
+ !(capa.hash_algos.bit.none))
+ rc = -1;
+ if (hash_alg == ODP_COMP_HASH_ALG_SHA1 &&
+ !(capa.hash_algos.bit.sha1))
+ rc = -1;
+ if (hash_alg == ODP_COMP_HASH_ALG_SHA256 &&
+ !(capa.hash_algos.bit.sha256))
+ rc = -1;
+
+ CU_ASSERT(!rc);
+
+ rc = odp_comp_alg_capability(comp_alg, &comp_capa);
+ CU_ASSERT(!rc);
+
+ rc = odp_comp_hash_alg_capability(hash_alg, &hash_capa);
+ CU_ASSERT(!rc);
+
+ if (hash_alg == ODP_COMP_HASH_ALG_NONE &&
+ !(comp_capa.hash_algo.bit.none))
+ rc = -1;
+ if (hash_alg == ODP_COMP_HASH_ALG_SHA1 &&
+ !(comp_capa.hash_algo.bit.sha1))
+ rc = -1;
+ if (hash_alg == ODP_COMP_HASH_ALG_SHA256 &&
+ !(comp_capa.hash_algo.bit.sha256))
+ rc = -1;
+
+ CU_ASSERT(!rc);
+
+ /* Create a compression session */
+ odp_comp_session_param_init(&ses_params);
+ ses_params.op = op;
+ ses_params.comp_algo = comp_alg;
+ ses_params.hash_algo = hash_alg;
+ ses_params.compl_queue = suite_context.queue;
+ ses_params.mode = suite_context.op_mode;
+
+ session = odp_comp_session_create(&ses_params);
+ CU_ASSERT_FATAL(session != ODP_COMP_SESSION_INVALID);
+ CU_ASSERT(odp_comp_session_to_u64(session) !=
+ odp_comp_session_to_u64(ODP_COMP_SESSION_INVALID));
+
+ /* Allocate compression output packet */
+ outpkt = odp_packet_alloc(suite_context.pool, outtext_len);
+ CU_ASSERT(outpkt != ODP_PACKET_INVALID);
+
+ op_params.out_data_range.offset = 0;
+ op_params.out_data_range.length = outtext_len;
+ op_params.in_data_range.offset = 0;
+ op_params.in_data_range.length = odp_packet_len(inpkt);
+ op_params.session = session;
+
+ if (suite_context.op_mode == ODP_COMP_OP_MODE_SYNC) {
+ rc = odp_comp_op(&inpkt, &outpkt, 1, &op_params);
+ CU_ASSERT(rc >= 0);
+ if (rc < 0)
+ goto cleanup;
+ } else {
+ odp_event_t event;
+ odp_packet_t packet;
+
+ rc = odp_comp_op_enq(&inpkt, &outpkt, 1, &op_params);
+ CU_ASSERT(rc == 1);
+ if (rc <= 0)
+ goto cleanup;
+ /* Poll completion queue for results */
+ do {
+ event = odp_queue_deq(suite_context.queue);
+ } while (event == ODP_EVENT_INVALID);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event));
+ CU_ASSERT(ODP_EVENT_PACKET_COMP ==
+ odp_event_subtype(event));
+
+ packet = odp_comp_packet_from_event(event);
+ CU_ASSERT(packet != ODP_PACKET_INVALID);
+ CU_ASSERT(packet == outpkt);
+ }
+
+ rc = odp_comp_result(&comp_result, outpkt);
+ CU_ASSERT(!rc);
+ CU_ASSERT(comp_result.status == ODP_COMP_STATUS_SUCCESS);
+ CU_ASSERT(comp_result.output_data_range.offset == 0);
+ odp_packet_trunc_tail(&outpkt,
+ odp_packet_len(outpkt) -
+ comp_result.output_data_range.length,
+ NULL, NULL);
+
+cleanup:
+
+ rc = odp_comp_session_destroy(session);
+ CU_ASSERT(!rc);
+
+ if (rc < 0) {
+ odp_packet_free(outpkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ return outpkt;
+}
+
+static void packet_cmp(odp_packet_t pkt,
+ const uint8_t *text,
+ unsigned int text_len)
+{
+ odp_packet_seg_t seg;
+ uint32_t cmp_offset = 0, outlen = 0;
+ uint32_t compare_len = 0;
+ uint8_t *outdata;
+
+ seg = odp_packet_first_seg(pkt);
+ do {
+ outdata = odp_packet_seg_data(pkt, seg);
+ outlen = odp_packet_seg_data_len(pkt, seg);
+ compare_len = outlen < (text_len - cmp_offset) ?
+ outlen : (text_len - cmp_offset);
+
+ CU_ASSERT(!memcmp(outdata,
+ text + cmp_offset, compare_len));
+ cmp_offset += compare_len;
+ seg = odp_packet_next_seg(pkt, seg);
+ } while (seg != ODP_PACKET_SEG_INVALID && cmp_offset < text_len);
+}
+
+static void comp_decomp_alg_test(odp_comp_alg_t comp_alg,
+ odp_comp_hash_alg_t hash_alg,
+ const uint8_t *plaintext,
+ unsigned int plaintext_len)
+{
+ odp_packet_t decomp_outpkt, comp_outpkt, inpkt;
+ int rc;
+
+ /* Allocate compression input packet */
+ inpkt = odp_packet_alloc(suite_context.pool, plaintext_len);
+ CU_ASSERT(inpkt != ODP_PACKET_INVALID);
+
+ /* copy test data in to pkt memory */
+ rc = odp_packet_copy_from_mem(inpkt, 0,
+ plaintext_len, plaintext);
+ CU_ASSERT_FATAL(!rc);
+
+ comp_outpkt = run_comp_op(ODP_COMP_OP_COMPRESS,
+ comp_alg, hash_alg,
+ inpkt,
+ plaintext_len);
+ if (comp_outpkt == ODP_PACKET_INVALID)
+ goto clean_in;
+
+ decomp_outpkt = run_comp_op(ODP_COMP_OP_DECOMPRESS,
+ comp_alg, hash_alg,
+ comp_outpkt,
+ plaintext_len);
+ if (decomp_outpkt == ODP_PACKET_INVALID)
+ goto cleanup;
+
+ packet_cmp(decomp_outpkt, plaintext, plaintext_len);
+
+ odp_packet_free(decomp_outpkt);
+
+cleanup:
+ odp_packet_free(comp_outpkt);
+clean_in:
+ odp_packet_free(inpkt);
+}
+
+static void comp_alg_test(odp_comp_alg_t comp_alg,
+ odp_comp_hash_alg_t hash_alg,
+ const uint8_t *plaintext,
+ unsigned int plaintext_len)
+{
+ odp_packet_t comp_outpkt, inpkt;
+ int rc;
+
+ /* Allocate compression input packet */
+ inpkt = odp_packet_alloc(suite_context.pool, plaintext_len);
+ CU_ASSERT(inpkt != ODP_PACKET_INVALID);
+
+ /* copy test data in to pkt memory */
+ rc = odp_packet_copy_from_mem(inpkt, 0,
+ plaintext_len, plaintext);
+ CU_ASSERT_FATAL(!rc);
+
+ comp_outpkt = run_comp_op(ODP_COMP_OP_COMPRESS,
+ comp_alg, hash_alg,
+ inpkt,
+ plaintext_len);
+ if (comp_outpkt == ODP_PACKET_INVALID)
+ goto clean_in;
+
+ odp_packet_free(comp_outpkt);
+clean_in:
+ odp_packet_free(inpkt);
+}
+
+static void decomp_alg_test(odp_comp_alg_t comp_alg,
+ odp_comp_hash_alg_t hash_alg,
+ const uint8_t *comptext,
+ unsigned int comptext_len,
+ const uint8_t *plaintext,
+ unsigned int plaintext_len)
+{
+ odp_packet_t decomp_outpkt, inpkt;
+ int rc;
+
+ /* Allocate compression input packet */
+ inpkt = odp_packet_alloc(suite_context.pool, comptext_len);
+ CU_ASSERT(inpkt != ODP_PACKET_INVALID);
+
+ /* copy test data in to pkt memory */
+ rc = odp_packet_copy_from_mem(inpkt, 0,
+ comptext_len, comptext);
+ CU_ASSERT_FATAL(!rc);
+
+ decomp_outpkt = run_comp_op(ODP_COMP_OP_DECOMPRESS,
+ comp_alg, hash_alg,
+ inpkt,
+ plaintext_len);
+ if (decomp_outpkt == ODP_PACKET_INVALID)
+ goto cleanup;
+
+ packet_cmp(decomp_outpkt, plaintext, plaintext_len);
+
+ odp_packet_free(decomp_outpkt);
+cleanup:
+ odp_packet_free(inpkt);
+}
+
+static int comp_check_deflate_none(void)
+{
+ return check_comp_alg_support(ODP_COMP_ALG_DEFLATE,
+ ODP_COMP_HASH_ALG_NONE);
+}
+
+/* Compress content using deflate algorithm */
+static void comp_test_compress_alg_deflate_none(void)
+{
+ comp_alg_test(ODP_COMP_ALG_DEFLATE,
+ ODP_COMP_HASH_ALG_NONE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+/* Decompress content using deflate algorithm */
+static void comp_test_decompress_alg_deflate_none(void)
+{
+ decomp_alg_test(ODP_COMP_ALG_DEFLATE,
+ ODP_COMP_HASH_ALG_NONE,
+ compressed_text_def, COMP_DEFLATE_SIZE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+static int comp_check_zlib_none(void)
+{
+ return check_comp_alg_support(ODP_COMP_ALG_ZLIB,
+ ODP_COMP_HASH_ALG_NONE);
+}
+
+/* Compress content using zlib algorithm */
+static void comp_test_compress_alg_zlib_none(void)
+{
+ comp_alg_test(ODP_COMP_ALG_ZLIB, ODP_COMP_HASH_ALG_NONE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+/* Decompress content using zlib algorithm */
+static void comp_test_decompress_alg_zlib_none(void)
+{
+ decomp_alg_test(ODP_COMP_ALG_ZLIB, ODP_COMP_HASH_ALG_NONE,
+ compressed_text_zlib, COMP_ZLIB_SIZE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+/* Compress/Decompress content using deflate algorithm */
+static void comp_test_comp_decomp_alg_deflate_none(void)
+{
+ comp_decomp_alg_test(ODP_COMP_ALG_DEFLATE,
+ ODP_COMP_HASH_ALG_NONE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+/* Compress/Decompress content using zlib algorithm */
+static void comp_test_comp_decomp_alg_zlib_none(void)
+{
+ comp_decomp_alg_test(ODP_COMP_ALG_ZLIB,
+ ODP_COMP_HASH_ALG_NONE,
+ plaintext, PLAIN_TEXT_SIZE);
+}
+
+static int comp_suite_sync_init(void)
+{
+ suite_context.pool = odp_pool_lookup(COMP_PACKET_POOL);
+ if (suite_context.pool == ODP_POOL_INVALID)
+ return -1;
+
+ suite_context.queue = ODP_QUEUE_INVALID;
+ suite_context.op_mode = ODP_COMP_OP_MODE_SYNC;
+ return 0;
+}
+
+static int comp_suite_async_init(void)
+{
+ suite_context.pool = odp_pool_lookup(COMP_PACKET_POOL);
+ if (suite_context.pool == ODP_POOL_INVALID)
+ return -1;
+ suite_context.queue = odp_queue_lookup(COMP_OUT_QUEUE);
+ if (suite_context.queue == ODP_QUEUE_INVALID)
+ return -1;
+
+ suite_context.op_mode = ODP_COMP_OP_MODE_ASYNC;
+ return 0;
+}
+
+static odp_testinfo_t comp_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(comp_test_compress_alg_deflate_none,
+ comp_check_deflate_none),
+ ODP_TEST_INFO_CONDITIONAL(comp_test_compress_alg_zlib_none,
+ comp_check_zlib_none),
+ ODP_TEST_INFO_CONDITIONAL(comp_test_decompress_alg_deflate_none,
+ comp_check_deflate_none),
+ ODP_TEST_INFO_CONDITIONAL(comp_test_decompress_alg_zlib_none,
+ comp_check_zlib_none),
+ ODP_TEST_INFO_CONDITIONAL(comp_test_comp_decomp_alg_deflate_none,
+ comp_check_deflate_none),
+ ODP_TEST_INFO_CONDITIONAL(comp_test_comp_decomp_alg_zlib_none,
+ comp_check_zlib_none),
+ ODP_TEST_INFO_NULL,
+};
+
+/* Suite names */
+#define ODP_COMP_SYNC_TEST "Comp/decomp sync test"
+#define ODP_COMP_ASYNC_TEST "Comp/decomp async test"
+
+static odp_suiteinfo_t comp_suites[] = {
+ {ODP_COMP_SYNC_TEST, comp_suite_sync_init,
+ NULL, comp_suite},
+ {ODP_COMP_ASYNC_TEST, comp_suite_async_init,
+ NULL, comp_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+static int comp_init(odp_instance_t *inst)
+{
+ odp_pool_param_t params;
+ odp_pool_t pool;
+ odp_queue_t out_queue;
+ odp_pool_capability_t pool_capa;
+
+ if (0 != odp_init_global(inst, NULL, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = TEST_PKT_LEN;
+ params.pkt.len = TEST_PKT_LEN;
+ params.pkt.num = TEST_NUM_PKT;
+ params.type = ODP_POOL_PACKET;
+
+ if (pool_capa.pkt.max_seg_len &&
+ TEST_PKT_LEN > pool_capa.pkt.max_seg_len) {
+ ODPH_ERR("Warning: small packet segment length\n");
+ params.pkt.seg_len = pool_capa.pkt.max_seg_len;
+ }
+
+ pool = odp_pool_create(COMP_PACKET_POOL, &params);
+ if (ODP_POOL_INVALID == pool) {
+ ODPH_ERR("Packet pool creation failed\n");
+ return -1;
+ }
+
+ /* Queue to store compression/decompression events */
+ out_queue = odp_queue_create(COMP_OUT_QUEUE, NULL);
+ if (ODP_QUEUE_INVALID == out_queue) {
+ ODPH_ERR("Comp outq creation failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int comp_term(odp_instance_t inst)
+{
+ odp_pool_t pool;
+ odp_queue_t out_queue;
+
+ out_queue = odp_queue_lookup(COMP_OUT_QUEUE);
+ if (ODP_QUEUE_INVALID != out_queue) {
+ if (odp_queue_destroy(out_queue))
+ ODPH_ERR("Comp outq destroy failed\n");
+ } else {
+ ODPH_ERR("Comp outq not found\n");
+ }
+
+ pool = odp_pool_lookup(COMP_PACKET_POOL);
+ if (ODP_POOL_INVALID != pool) {
+ if (odp_pool_destroy(pool))
+ ODPH_ERR("Packet pool destroy failed\n");
+ } else {
+ ODPH_ERR("Packet pool not found\n");
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(comp_init);
+ odp_cunit_register_global_term(comp_term);
+
+ ret = odp_cunit_register(comp_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/comp/test_vectors.h b/test/validation/api/comp/test_vectors.h
new file mode 100644
index 000000000..c99041c9a
--- /dev/null
+++ b/test/validation/api/comp/test_vectors.h
@@ -0,0 +1,1995 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#ifndef _ODP_TEST_COMP_VECTORS_H_
+#define _ODP_TEST_COMP_VECTORS_H_
+
+#define PLAIN_TEXT_SIZE 8192
+
+static uint8_t plaintext[PLAIN_TEXT_SIZE] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x45, 0x8b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x98, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xdc, 0xb0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x94, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x1f, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x58, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x41, 0xb1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x1e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xa9, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x5f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x08, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x27,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x23, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xcd, 0x90,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x0f, 0x0e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xf9, 0x9c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xc2, 0xdc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xc4, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfb, 0x6a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x50, 0x9b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xba, 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x30, 0x71,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xd9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x61, 0x6c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x89,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xb1, 0x3a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xa3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xa8, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x5a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x84, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xbd, 0xed,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x8c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xd0, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x76, 0x9a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x24, 0xf3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xc4, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xf8, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xf5, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x8d, 0x7b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1a, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xdd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc8, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0xd4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xc2, 0x9a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xad, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x82, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x5f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc6, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0xb9, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0xd3, 0xea,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xd7, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x58, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x42, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xd4, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x9a, 0xd8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x8d, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x89, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x7f, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xf9, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x78, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x40, 0xc2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xde, 0xa1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x85, 0xe6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xed, 0x0e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xc1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xb7, 0x9b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x65, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x15, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x8c, 0x39,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xe9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xaf, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xb6, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xb6, 0x6a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x57, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x35,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xe4, 0x9b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x50,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7e, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x0b, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x84, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0xea,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x82, 0x81,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x8f, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x4a, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0x8a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x47, 0xb9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x12, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3f, 0x1e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x47, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xc5, 0xd9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xfd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2b, 0xf7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x7b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x3e, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xb1, 0xf2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xd3, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x81, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xee, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xca, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0xea,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x59,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xe0, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xd9, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x5e, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xa8, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x7e, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0e, 0xde,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xc5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xd6, 0xf6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xd4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xc3, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x4f, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x84, 0xf1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x24, 0xf3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x30, 0xda,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x37, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x1e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xac, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x8f, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0xbd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x5a, 0xc7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x82, 0xa1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x55, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xe7, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xd3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x12, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xf6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x99, 0x29,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xca, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xea, 0xad,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x5d, 0x15,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x1b, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xc5, 0xa0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x4b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x28, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x6a, 0xd8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xd4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xfe, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x91, 0x59,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x59,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x6a, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xaa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x8d, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xec,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x21, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0xe3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x17, 0x27,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xb7, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x29,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xff, 0x75,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x50,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x12, 0xb0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xac, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xe3, 0x97,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x6b, 0xfd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x8d, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x4a, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x2e, 0xf7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x8a, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x2c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xaf, 0xc6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x85, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xc6, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x06, 0x27,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf6, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf1, 0xd2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xae,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x47, 0x3e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xfe, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xb9, 0x51,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xab,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x57, 0x4c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7, 0x9d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xb6, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xc2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x9d, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xf3, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xf8, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x9d, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x4e, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x4d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x9e, 0x81,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xd4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x67, 0x21,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xe7, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x4c, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xde,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x8c, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x3d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x8c, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xd3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2e, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x8a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2e, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x36, 0xd5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x85, 0xaa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xec,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x48, 0x59,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x2f, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0b, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x5e, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xab, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xac,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xf6, 0x47,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x80, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xae, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xcc, 0xb1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xfb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x46, 0x93,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x39, 0x49,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xd2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2c, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5d, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x5a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x4f, 0x79,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xdf, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0xd5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x81, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x27,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf8, 0xcd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xb1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x63, 0x8a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xd9, 0x6a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xc1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xac, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x6e, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xa2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xcf, 0x51,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x57, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xcd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x79, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x25, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xb3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x71, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x51,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x1b, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xab, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x63, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x8b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x41, 0xe3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xe4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xb2, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x95, 0x42,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x3e, 0x97,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xd8, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xea,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xf4, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x8d, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x36, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0xbe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x8d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x5b, 0xbe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x2c, 0xa0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x81,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x6f, 0x90,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x9d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xd2, 0x97,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x8a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x6c, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xf5, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x09, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x37,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xb4, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x71, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xd3, 0xf2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xaf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xb9, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xab,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x9a, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xae,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xf2, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x72, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xb7, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb6, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x70, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x94, 0xd3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x80, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x6a, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0xed,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0xe5, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x60,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0xbd, 0xf4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x97, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xac, 0x71,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xbd, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0xb8, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x76,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x2b, 0x21,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x25, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7b, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x97,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x46, 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4c, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x45, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xdf, 0xaa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xda,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x9c, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x60,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x41, 0x56,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x4d, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x27,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x66, 0xce,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x17, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7d, 0xac,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x07, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x43, 0x29,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb2, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x12, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x8d, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x6f, 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xd3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xc0, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xd0, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x77, 0xa3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xb0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0xe1, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x10, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x1a, 0xe3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x4a, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x69, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x61,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x7f, 0x9f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x25, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x23, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x45, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xd5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xd7, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xac, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xe6, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x38, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xd6, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xae, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xb9, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x62, 0x35,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xca,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xb2, 0x35,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x28, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xfd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xfd, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x5e, 0xe9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x7a, 0xa2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x83,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x02, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x9d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0xec, 0xa2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x72, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xa1, 0xd2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x29, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x3a, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0x2f, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x6b, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xde,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x60, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6e, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xc2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xef, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x3f, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x1d, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0xaa, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xb9, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xc3, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xed,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x35,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xb2, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x54, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xdb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x2d, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3e, 0x84,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xa1, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xce, 0xab,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xaa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xc4, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xbe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xcb, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xd6, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x41, 0x15,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x65, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xa6, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xb6, 0x93,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x69,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x53, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xba,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x9f, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x60, 0xde,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xf4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe1, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x84, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xec, 0x79,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xc9, 0xc7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x27, 0xa3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xd2, 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xec,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x56, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x9d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0xf0, 0xf7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x86, 0xc6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xad, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2c, 0xc1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x51, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xbd, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x9d, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xaa, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x51, 0x29,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x8c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x8d, 0xfb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xd5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xab, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x79,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xfb, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x75,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0xa1, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x0d, 0xcb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x11, 0xec,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x90, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x32, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x3d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x5f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x91, 0xce,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x42, 0xfd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x4f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x7b, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xac, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xde, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xb2, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd3, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xe5, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd4, 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf2, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xfb, 0x9e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x45, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x03, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0xa4, 0x3d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x95, 0x71,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xfe, 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xf5, 0x9d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x7a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xb7, 0x90,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xa2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xa0, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xa0, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x01, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xb1, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xe6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x43, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x6d, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x67, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x38, 0x51,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xad,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x75, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x83, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x73, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x97,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x3d, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xe0, 0xdc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xbc, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0xd4, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x2e, 0x23,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xaf, 0xd3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xe8, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x61,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x17, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x2c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xe7, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x84,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x62, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xed, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x9b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xfb, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xd7, 0x54,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x7c, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x16, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x53, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x85, 0x75,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xbd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xcd, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x5a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x1a, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xcf, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0xca,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x2d, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x2b, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xe4, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xc9, 0x5f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x62, 0xe5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xb6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xe1, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x5d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x19, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xb9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xdc, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x96,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x04, 0x9f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x56, 0xa6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x46, 0x7d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x8d, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xcb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x15, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x5a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x26, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x57, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfa, 0xcd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x84,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xad, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x26,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc8, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x39, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0xd7, 0xeb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x9c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x48, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0xb9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xfa, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x72, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf8, 0x56,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xca, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0xcf, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xa6, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdc, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xb7, 0x2e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xed, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf7, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xe7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x92, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x35, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xed, 0xdc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xe4, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x35,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc7, 0x87,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xde,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x97, 0xa6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x39,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xb5, 0x8d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x90,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xb1, 0x8b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xde, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x8f, 0x54,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x85,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xdc, 0xe5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x42, 0x1c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x93,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x6d, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xa2, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x17, 0xb2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x09, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x25,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x8f, 0xc6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6e, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x7d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4d, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x52,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xb3, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x31, 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x4b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x4e, 0xc4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0xe6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdb, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x66, 0xb7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xba,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x7f, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0xf6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40, 0x45,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x62, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xf0, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xb1, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x35, 0x6b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xc6, 0xa6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xee, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7, 0x56,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x7f, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd4, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xaf, 0x4e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x87,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xe0, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0xd2, 0xc2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xc7, 0xca,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xad,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2e, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x68, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xae,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x24, 0x8e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x0a, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x2a, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x56, 0x69,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xdc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8d, 0x90,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x8b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xa5, 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xcb, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0xaa, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x4a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xf1, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x16, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xa0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x06, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xf7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x80, 0x45,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xd8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x26, 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x48,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x86, 0xfb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x75, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x03, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x97, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x20, 0xa4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xc2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x08, 0xe9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x76,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x9e, 0xb5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x95,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x02, 0xe2,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xe9, 0xb0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xcd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd9, 0xc5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x17, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xca,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x13, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x2e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x6a, 0x76,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x19,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x26, 0xc1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xeb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xf1, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x4c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x33, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xa2, 0xf6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x67, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xa5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x42, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xb3, 0x8c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xc0, 0xed,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xd9, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x5f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0xdb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xa4, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x3b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xa9, 0x5c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x7d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd0, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xc1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x47, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xe3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xef, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xb1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xfc, 0xd7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9d, 0x75,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x62, 0x87,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xd0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x9f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x79, 0xa0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xce,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0xaa, 0x37,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xbc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xcc, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x5e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1f, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x8d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x1a, 0x3a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xed, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x6c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xd4, 0x86,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x92,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xa8, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x7d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0f, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x3e, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x5b, 0xc0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x2e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x09, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x44, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xa6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x46, 0xd6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xa7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x76, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xad,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xac, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x7c, 0x83,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xac, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x48
+};
+
+/** Length of the pre-compressed data using deflate algorithm */
+#define COMP_DEFLATE_SIZE 3769
+
+/** Pre-compressed data using deflate algorithm */
+static uint8_t compressed_text_def[COMP_DEFLATE_SIZE] = {
+ 0x35, 0x99, 0x7b, 0x5c, 0xcf, 0x77, 0xfb, 0xc7,
+ 0xdf, 0xe5, 0x18, 0x8b, 0xb0, 0x8a, 0xd8, 0x72,
+ 0x9e, 0xb3, 0x64, 0x0c, 0x91, 0x39, 0xe6, 0x90,
+ 0xd3, 0x44, 0x6e, 0xa7, 0x9b, 0x8c, 0x26, 0x87,
+ 0x85, 0x9b, 0xcd, 0x34, 0xe5, 0x7c, 0x33, 0x8d,
+ 0xed, 0x76, 0x3e, 0xe6, 0x9c, 0x8a, 0x08, 0x35,
+ 0x16, 0x33, 0x67, 0x99, 0x15, 0xe5, 0xd0, 0xb4,
+ 0xe6, 0xd4, 0x9c, 0x8a, 0xe4, 0x14, 0x39, 0xfc,
+ 0x7e, 0x8f, 0xf7, 0xf3, 0xb5, 0xfd, 0xb1, 0xd7,
+ 0x5e, 0xeb, 0xfb, 0xfd, 0x7c, 0xde, 0x87, 0xeb,
+ 0x7a, 0x5d, 0xaf, 0xeb, 0xfa, 0x1a, 0xf3, 0xff,
+ 0xff, 0x04, 0x77, 0xf9, 0xce, 0xd8, 0x7f, 0x4e,
+ 0xd5, 0xb2, 0x30, 0x76, 0x55, 0x5b, 0xf8, 0x94,
+ 0x6e, 0x16, 0xfa, 0x65, 0xed, 0x81, 0xbf, 0x1b,
+ 0x6a, 0xa1, 0xc7, 0xb2, 0xbb, 0xf0, 0xdc, 0x41,
+ 0x16, 0xea, 0x7b, 0x7e, 0x0f, 0x3f, 0x1f, 0x66,
+ 0xe1, 0xd0, 0xa0, 0xaa, 0xf0, 0xd8, 0x2b, 0x16,
+ 0x0a, 0x3a, 0xec, 0x85, 0x17, 0x7d, 0x68, 0xe1,
+ 0xd6, 0xce, 0x9b, 0xf0, 0xae, 0x37, 0x2c, 0x84,
+ 0x99, 0x7f, 0xc3, 0x8f, 0x05, 0x59, 0x08, 0x28,
+ 0x5d, 0x1d, 0x5e, 0xf8, 0x91, 0x85, 0xaa, 0xb5,
+ 0xdc, 0xe0, 0x77, 0xef, 0x59, 0xb8, 0x73, 0x7e,
+ 0x29, 0x3c, 0xb2, 0x93, 0x85, 0x69, 0xe5, 0xcb,
+ 0xc1, 0x07, 0xd7, 0xb1, 0xd0, 0xe4, 0xe5, 0x3a,
+ 0xf8, 0xa8, 0xc9, 0x16, 0x9a, 0x1f, 0xcb, 0x82,
+ 0x6f, 0x64, 0x3d, 0x67, 0x4f, 0xec, 0x80, 0xaf,
+ 0x29, 0x65, 0x61, 0x4c, 0x51, 0x08, 0xdc, 0x7b,
+ 0x98, 0x05, 0xe7, 0xbe, 0x6b, 0xe1, 0x49, 0x7c,
+ 0xbe, 0xd9, 0xa1, 0xc6, 0xf0, 0x41, 0xb7, 0x2d,
+ 0x6c, 0xf1, 0x9a, 0xa4, 0xf7, 0x65, 0x5a, 0xa8,
+ 0x33, 0x72, 0x3c, 0x7c, 0xd8, 0x42, 0x0b, 0x25,
+ 0xf6, 0xb6, 0x81, 0xbb, 0x6f, 0xe1, 0x6b, 0xd1,
+ 0xac, 0xd3, 0xdc, 0x1b, 0x6c, 0x61, 0xf8, 0xdc,
+ 0x51, 0xf0, 0x8c, 0x68, 0x0b, 0xb1, 0x87, 0xf3,
+ 0xe0, 0x09, 0x8b, 0x2d, 0x9c, 0x4f, 0xeb, 0x0f,
+ 0x3f, 0x75, 0xdd, 0xc2, 0xda, 0x69, 0x6b, 0xe0,
+ 0xfb, 0x37, 0x58, 0x08, 0xa8, 0xfd, 0x04, 0x5e,
+ 0x61, 0xbe, 0x85, 0x72, 0x27, 0x5a, 0xc2, 0x67,
+ 0x7f, 0x60, 0xe1, 0x3f, 0x85, 0xcb, 0xe1, 0x1d,
+ 0xf8, 0x7b, 0x8d, 0x67, 0xe1, 0xf0, 0x76, 0x87,
+ 0x2d, 0x64, 0x45, 0xce, 0x80, 0x2f, 0xc8, 0xb7,
+ 0x10, 0xea, 0x51, 0x53, 0xeb, 0xfb, 0xd3, 0x82,
+ 0xef, 0x99, 0x92, 0xf0, 0x4d, 0x19, 0x3c, 0xe6,
+ 0x98, 0xde, 0x7f, 0xa3, 0xd0, 0xc2, 0xeb, 0x5d,
+ 0x65, 0xe1, 0xc1, 0xc4, 0x8b, 0xef, 0xec, 0xe2,
+ 0x70, 0x07, 0xee, 0x71, 0xe6, 0xa9, 0xda, 0xf0,
+ 0x95, 0x0d, 0x2c, 0xdc, 0x3f, 0xa8, 0xfd, 0x66,
+ 0xf5, 0xb0, 0x30, 0x2e, 0xfd, 0x3e, 0x7c, 0xc5,
+ 0x57, 0x16, 0x96, 0x5c, 0xd1, 0xf3, 0x5a, 0x6d,
+ 0xb5, 0x30, 0x74, 0xd0, 0x00, 0x78, 0x83, 0xde,
+ 0x16, 0x72, 0x3b, 0xf2, 0x1e, 0xb3, 0x87, 0xf8,
+ 0xf2, 0xc9, 0xe0, 0xff, 0x9b, 0x22, 0xd6, 0xe9,
+ 0xbd, 0xe6, 0x2a, 0x3c, 0xfe, 0x37, 0x0b, 0x6d,
+ 0x23, 0x27, 0xc0, 0x03, 0x96, 0xf0, 0xfd, 0x85,
+ 0xda, 0x5f, 0x65, 0xe2, 0xf2, 0x5a, 0xb8, 0xee,
+ 0x63, 0x28, 0xef, 0x73, 0x7c, 0xf9, 0x0e, 0xee,
+ 0x41, 0xbc, 0xbf, 0xfd, 0x7a, 0x19, 0xbc, 0xd3,
+ 0xcf, 0xbc, 0xe6, 0xd3, 0x63, 0xf0, 0x57, 0x75,
+ 0x2d, 0xc4, 0x64, 0x6f, 0x82, 0xb7, 0x39, 0xce,
+ 0xdf, 0xe7, 0xfd, 0x0d, 0xaf, 0xbf, 0xcd, 0xc2,
+ 0x85, 0x3c, 0xc5, 0xe3, 0xdf, 0xed, 0x2d, 0x94,
+ 0xc8, 0xd7, 0x7a, 0xdb, 0xfe, 0x6a, 0x21, 0x2c,
+ 0x49, 0xf1, 0xb5, 0xec, 0xb4, 0x85, 0xa9, 0xa3,
+ 0x5b, 0xc1, 0xaf, 0x96, 0xb7, 0x70, 0xc4, 0xd5,
+ 0x01, 0x3e, 0x92, 0xf8, 0x58, 0xb8, 0xb8, 0x35,
+ 0xfc, 0x25, 0xf1, 0x3f, 0x34, 0xfe, 0x00, 0xfc,
+ 0x67, 0xd6, 0x13, 0x9d, 0xf8, 0x31, 0x7c, 0x35,
+ 0x79, 0x5b, 0x3e, 0x51, 0xf1, 0xbc, 0xfc, 0x53,
+ 0x0b, 0x7b, 0x03, 0xdf, 0x83, 0x3f, 0x68, 0x61,
+ 0xe1, 0xf1, 0x6d, 0xbd, 0x7f, 0x5f, 0x5f, 0xd6,
+ 0x37, 0x93, 0x7b, 0x35, 0x8f, 0x88, 0xff, 0x17,
+ 0x65, 0x95, 0x1f, 0xe6, 0x17, 0xfb, 0xef, 0x7b,
+ 0x73, 0xd3, 0xa0, 0x9b, 0xb8, 0xb7, 0x36, 0xb3,
+ 0x67, 0xc1, 0x73, 0xca, 0x58, 0x48, 0x59, 0xc2,
+ 0xba, 0x4d, 0xd9, 0x50, 0x0b, 0xe7, 0x7a, 0x68,
+ 0xfd, 0x69, 0xc4, 0x5d, 0xb7, 0x66, 0x8b, 0xe0,
+ 0x7e, 0x8e, 0x16, 0x3e, 0xf7, 0x3b, 0x08, 0x3f,
+ 0xbc, 0xc2, 0x82, 0x67, 0x45, 0xe9, 0x47, 0x2d,
+ 0xde, 0xff, 0x61, 0x7b, 0x74, 0xc2, 0x44, 0x13,
+ 0xef, 0xd5, 0xfc, 0x78, 0x8f, 0x99, 0xf1, 0x90,
+ 0xef, 0x9f, 0x24, 0xef, 0xcc, 0xc9, 0x37, 0x16,
+ 0xde, 0x6f, 0xf8, 0x02, 0x3e, 0x85, 0xb8, 0x1e,
+ 0xec, 0xbb, 0x42, 0x7f, 0x9f, 0x6d, 0x61, 0xf8,
+ 0xde, 0x02, 0x78, 0x4f, 0xe2, 0x68, 0x7a, 0xba,
+ 0xd6, 0x3b, 0xaa, 0xa9, 0x05, 0x9f, 0x59, 0xe8,
+ 0x8e, 0x09, 0xfd, 0x8b, 0xe7, 0x3f, 0xe4, 0x1e,
+ 0x4d, 0x6d, 0xe2, 0xab, 0x42, 0x8a, 0xe2, 0x63,
+ 0x03, 0xfb, 0x73, 0xaa, 0xa0, 0xf8, 0xcd, 0xfa,
+ 0x97, 0x85, 0x98, 0xeb, 0x49, 0xf0, 0x8c, 0x21,
+ 0x16, 0xe2, 0x32, 0x15, 0x7f, 0x05, 0x15, 0x2c,
+ 0x54, 0x1d, 0xfe, 0x5f, 0xb8, 0x4b, 0x0d, 0x0b,
+ 0xf1, 0xd1, 0xca, 0x17, 0x1f, 0x9e, 0xd7, 0x7c,
+ 0x26, 0x3a, 0x68, 0xce, 0xdf, 0xb1, 0x70, 0xab,
+ 0x5c, 0x36, 0xbc, 0xef, 0x49, 0x0b, 0xdd, 0x2e,
+ 0x3f, 0x87, 0xfb, 0x91, 0x8f, 0xae, 0xc7, 0xc9,
+ 0x2b, 0x33, 0x94, 0x73, 0xfe, 0xb9, 0xcf, 0x6b,
+ 0xf8, 0x97, 0xf0, 0x9a, 0x73, 0x1f, 0xc3, 0xab,
+ 0xc0, 0x0f, 0xfd, 0xa3, 0x17, 0x6b, 0x03, 0x2d,
+ 0x7c, 0xeb, 0xf5, 0x07, 0xfc, 0x19, 0xfb, 0x2c,
+ 0xfb, 0xc9, 0x4f, 0xf0, 0x1b, 0x9c, 0xbb, 0x47,
+ 0x9c, 0xd6, 0x5b, 0x8d, 0xef, 0x87, 0x2f, 0x51,
+ 0x3c, 0xd4, 0x42, 0x3f, 0x0a, 0x07, 0x13, 0xb7,
+ 0xa6, 0x3e, 0xeb, 0x2f, 0xac, 0x3c, 0x1c, 0xbe,
+ 0x95, 0x38, 0xac, 0x3a, 0x5b, 0xf9, 0x51, 0x89,
+ 0xfc, 0x3e, 0x30, 0x80, 0xba, 0x61, 0x52, 0x58,
+ 0x77, 0xef, 0x3b, 0x5a, 0xef, 0xdd, 0x74, 0x0b,
+ 0xab, 0x2a, 0x9e, 0x85, 0x7b, 0xb3, 0xcf, 0x56,
+ 0xab, 0xeb, 0xc3, 0xaf, 0x53, 0x67, 0xa6, 0xa7,
+ 0xfc, 0x08, 0xef, 0xc5, 0x73, 0xda, 0xdd, 0xdf,
+ 0x05, 0xff, 0xd8, 0xc3, 0x42, 0xf2, 0x30, 0x57,
+ 0xf8, 0xbf, 0x27, 0xf2, 0xfc, 0xaa, 0x33, 0xe1,
+ 0x5f, 0x71, 0xff, 0xaf, 0x4e, 0x46, 0xc1, 0xcf,
+ 0xf5, 0xb4, 0x30, 0xbe, 0x9e, 0xd6, 0x5b, 0x82,
+ 0xf8, 0x89, 0x0b, 0x91, 0x7e, 0xcc, 0xe7, 0x7c,
+ 0x6b, 0xbc, 0x75, 0x81, 0x37, 0x7c, 0x65, 0x21,
+ 0xe6, 0x07, 0xee, 0xd9, 0x78, 0x80, 0x03, 0x42,
+ 0x88, 0x13, 0xb3, 0x39, 0xc6, 0xc2, 0x91, 0x48,
+ 0x67, 0x78, 0x68, 0x2e, 0xfb, 0xad, 0x41, 0xdc,
+ 0x9a, 0x29, 0xb7, 0x2c, 0xf8, 0xb8, 0x53, 0xc7,
+ 0x4c, 0x71, 0x27, 0x0b, 0x43, 0x93, 0x74, 0x9e,
+ 0xe9, 0xec, 0xb3, 0xe5, 0xbb, 0xa9, 0xf0, 0x65,
+ 0x7d, 0x2d, 0xec, 0xab, 0xb8, 0x07, 0x1e, 0xcf,
+ 0xb9, 0xdc, 0x8c, 0xa3, 0xae, 0x99, 0x7c, 0xce,
+ 0xed, 0xf6, 0xad, 0x95, 0xf0, 0x0d, 0xe4, 0x45,
+ 0x9f, 0x71, 0xec, 0xc3, 0x78, 0x13, 0xa7, 0xae,
+ 0xaa, 0x8b, 0xa6, 0x7b, 0x33, 0x0b, 0x6f, 0x7a,
+ 0x70, 0x2e, 0x66, 0x36, 0xba, 0xd1, 0xbb, 0x89,
+ 0xf2, 0x65, 0x27, 0xbc, 0xf4, 0x22, 0xdd, 0x77,
+ 0x68, 0x23, 0x0b, 0x19, 0xf1, 0xa7, 0xe0, 0x09,
+ 0xe8, 0xe5, 0xa2, 0x79, 0xd7, 0xe1, 0xf5, 0x39,
+ 0xef, 0x80, 0x53, 0xab, 0xe1, 0xdd, 0x2a, 0x59,
+ 0x58, 0x53, 0x52, 0xfb, 0x2b, 0xc3, 0x7d, 0x25,
+ 0x3f, 0x27, 0xce, 0xcc, 0x25, 0xea, 0x51, 0xb9,
+ 0xc7, 0x17, 0xe1, 0x95, 0x77, 0x5b, 0x88, 0xf6,
+ 0xf3, 0x85, 0x77, 0xce, 0xb7, 0x10, 0xf7, 0x36,
+ 0x0d, 0x3e, 0x84, 0xfa, 0xf8, 0xe4, 0x60, 0x3f,
+ 0xf8, 0xf7, 0xb1, 0x16, 0xfc, 0x03, 0xfd, 0xe1,
+ 0x57, 0xd6, 0x5b, 0x68, 0x9c, 0xc8, 0xbe, 0xcc,
+ 0x5a, 0x74, 0xd9, 0x69, 0xbd, 0xf2, 0xb3, 0x23,
+ 0xfb, 0xce, 0x79, 0x42, 0x1e, 0x99, 0x92, 0xac,
+ 0xe7, 0x44, 0xa1, 0xfc, 0x43, 0x73, 0xea, 0x40,
+ 0xfc, 0xfa, 0xfd, 0xf0, 0xf3, 0xc4, 0xc7, 0x96,
+ 0xde, 0xfa, 0xfc, 0xdc, 0x5e, 0x16, 0xc2, 0x37,
+ 0x48, 0xdf, 0x1a, 0x13, 0x0f, 0xbb, 0x82, 0xc9,
+ 0x5b, 0x93, 0xf1, 0xb9, 0x85, 0x69, 0x77, 0xa4,
+ 0xbf, 0x7e, 0x9c, 0x6b, 0xb6, 0xbf, 0xea, 0xb5,
+ 0x37, 0x79, 0x5b, 0x6d, 0xb1, 0xf4, 0x27, 0xb7,
+ 0x9d, 0x85, 0x1e, 0x8b, 0x15, 0x1f, 0x27, 0x88,
+ 0x77, 0xaf, 0x26, 0x5f, 0xc2, 0x9f, 0xa3, 0x8b,
+ 0xd5, 0x9b, 0x50, 0x47, 0x4c, 0x2d, 0xd6, 0x3b,
+ 0xaf, 0xe5, 0x25, 0xf8, 0xf8, 0x79, 0x16, 0x8a,
+ 0xe6, 0x11, 0x67, 0x26, 0x81, 0xf8, 0x2a, 0xd5,
+ 0x4d, 0xf1, 0x58, 0x1c, 0x3d, 0x7b, 0xda, 0x34,
+ 0x4c, 0xef, 0xf3, 0xb1, 0x50, 0xb6, 0x6c, 0x65,
+ 0xf8, 0xc1, 0x7a, 0x3c, 0x7f, 0x78, 0x75, 0xf8,
+ 0x21, 0xea, 0xc9, 0xfc, 0x58, 0x6f, 0xf8, 0xf1,
+ 0x38, 0x0b, 0xbe, 0xcf, 0xfd, 0xe0, 0x25, 0xa8,
+ 0xcf, 0x8f, 0x23, 0x52, 0xf5, 0x3c, 0xf4, 0x38,
+ 0x73, 0xb7, 0x78, 0xf0, 0x17, 0x16, 0x9a, 0xff,
+ 0x26, 0x3f, 0x97, 0x54, 0x64, 0x61, 0x75, 0xd7,
+ 0xff, 0xc1, 0xfb, 0xe2, 0x07, 0xb7, 0xb4, 0xee,
+ 0x0e, 0xbf, 0xc5, 0xbd, 0xbf, 0xdf, 0x88, 0x38,
+ 0x30, 0xe9, 0xc1, 0x3c, 0x6f, 0x98, 0xfc, 0xc8,
+ 0xc7, 0xdc, 0xf7, 0x8b, 0x3e, 0xd3, 0xe1, 0xc3,
+ 0xa9, 0xbb, 0x51, 0x7f, 0x3d, 0x82, 0x17, 0x70,
+ 0x0e, 0x2e, 0xb3, 0xd0, 0x55, 0x13, 0x4d, 0x9c,
+ 0x3d, 0x2f, 0x3c, 0xaf, 0xf5, 0xb2, 0x8e, 0x65,
+ 0xa3, 0x54, 0x5f, 0x1c, 0xd0, 0xd5, 0x23, 0x99,
+ 0xaa, 0x7f, 0xfb, 0xa9, 0xb7, 0xc9, 0x71, 0x09,
+ 0xf0, 0xce, 0x9c, 0xe7, 0xd7, 0x13, 0xc5, 0x5f,
+ 0x6d, 0xb6, 0xd0, 0x3d, 0x55, 0xf1, 0x36, 0x96,
+ 0xf7, 0xfe, 0x6d, 0xb8, 0x67, 0x53, 0x0b, 0x1d,
+ 0x49, 0x0b, 0xc4, 0x27, 0x1a, 0x0f, 0xde, 0xfb,
+ 0xc7, 0xf4, 0xce, 0xfa, 0x3c, 0x79, 0x1e, 0x52,
+ 0x47, 0x7a, 0x32, 0x73, 0x9f, 0x05, 0xff, 0x49,
+ 0x3e, 0xe2, 0x3c, 0xb7, 0x5f, 0xd5, 0xae, 0xf0,
+ 0x3a, 0x9c, 0xe3, 0xbe, 0x58, 0xed, 0xa7, 0xdb,
+ 0x38, 0x0b, 0x73, 0x47, 0xa9, 0x1e, 0xf5, 0xc7,
+ 0x87, 0xb7, 0xe9, 0x80, 0x6e, 0x98, 0x65, 0xf8,
+ 0xcd, 0xa2, 0x04, 0xf9, 0x91, 0x66, 0xf8, 0xca,
+ 0xd5, 0xcb, 0x3b, 0xc2, 0x97, 0x12, 0xd7, 0xde,
+ 0xbe, 0xd2, 0x83, 0x40, 0xe2, 0xaa, 0xf3, 0x55,
+ 0xe9, 0xe9, 0x43, 0xea, 0xd4, 0xda, 0xa7, 0xdc,
+ 0xab, 0x49, 0x66, 0x1d, 0xf7, 0x22, 0xb5, 0xfe,
+ 0x1c, 0xe2, 0xae, 0x4e, 0xcb, 0xb7, 0xf0, 0x54,
+ 0xea, 0x70, 0xe9, 0x8e, 0x47, 0xe0, 0xcf, 0x22,
+ 0xf9, 0xfc, 0x10, 0xf1, 0x9b, 0xf8, 0xa1, 0xe1,
+ 0x8d, 0xa2, 0xb4, 0x5e, 0xf2, 0x64, 0xc4, 0x97,
+ 0x4b, 0xe1, 0x31, 0xe4, 0xe7, 0xc5, 0x8b, 0x5a,
+ 0x4f, 0x02, 0xf7, 0x92, 0x36, 0x5e, 0xf9, 0x30,
+ 0x8f, 0xf3, 0x7c, 0xf5, 0x4c, 0xfe, 0x34, 0x20,
+ 0xcd, 0xc2, 0x55, 0x27, 0x7c, 0x92, 0x69, 0xf1,
+ 0x89, 0x85, 0xbb, 0xfb, 0x5d, 0xe0, 0x19, 0xc9,
+ 0x16, 0xc6, 0x4c, 0x52, 0xbd, 0x9b, 0xcd, 0x3e,
+ 0x3e, 0x4f, 0x57, 0x3d, 0x5f, 0x15, 0x6f, 0x21,
+ 0x53, 0x76, 0xc2, 0x44, 0xa3, 0x17, 0x0b, 0xd6,
+ 0xc8, 0x4f, 0x4e, 0x45, 0x6f, 0x46, 0x17, 0xc8,
+ 0x5f, 0x85, 0x92, 0x0f, 0x83, 0x27, 0xab, 0x7f,
+ 0x59, 0x04, 0x6f, 0x9f, 0x24, 0xff, 0x13, 0x84,
+ 0x2e, 0x44, 0x24, 0xaa, 0xff, 0xa9, 0x8f, 0xbe,
+ 0x75, 0x0e, 0xdd, 0x09, 0xcf, 0xa6, 0x6f, 0x09,
+ 0x5b, 0x46, 0x1e, 0x9b, 0x6d, 0xd4, 0x81, 0x85,
+ 0x11, 0xaa, 0x9f, 0xbd, 0x89, 0xef, 0xc0, 0x10,
+ 0xad, 0xff, 0x5f, 0xf8, 0xfa, 0xf4, 0x1c, 0xc5,
+ 0x47, 0xbf, 0x11, 0x16, 0x76, 0x1d, 0x7e, 0x0a,
+ 0x8f, 0x23, 0xde, 0xe6, 0xaf, 0xf4, 0x84, 0x2f,
+ 0xa7, 0x5f, 0x89, 0x88, 0x53, 0x7f, 0x91, 0x5b,
+ 0xda, 0x82, 0xfb, 0x61, 0xf2, 0xca, 0xdc, 0x26,
+ 0x6f, 0xe6, 0xfd, 0xa4, 0xfd, 0x3d, 0x9e, 0x66,
+ 0x61, 0x71, 0x43, 0xe9, 0xd3, 0x7b, 0xe8, 0xd0,
+ 0x98, 0x3a, 0xf2, 0x2f, 0x8f, 0xc9, 0xbb, 0xb0,
+ 0x19, 0xac, 0xcb, 0x1c, 0xe5, 0x5e, 0xc2, 0xba,
+ 0xfe, 0x00, 0xff, 0x19, 0x1d, 0xa8, 0xe9, 0x2f,
+ 0xff, 0xf8, 0x5a, 0xf5, 0xa5, 0x8b, 0x3b, 0x7c,
+ 0x0c, 0xe7, 0xff, 0xc7, 0x5f, 0xd2, 0x9f, 0x91,
+ 0xf8, 0x86, 0xb2, 0xeb, 0xf4, 0xf7, 0x51, 0xec,
+ 0x27, 0xbe, 0xc3, 0x40, 0x78, 0x10, 0x7e, 0x29,
+ 0xb9, 0x97, 0xf2, 0x6b, 0x0e, 0xf9, 0xba, 0x7f,
+ 0xcc, 0xef, 0xf0, 0xb1, 0xac, 0xbb, 0xa9, 0xbb,
+ 0xf4, 0xb2, 0x0d, 0x71, 0xf7, 0xee, 0x5b, 0x74,
+ 0xc8, 0xc4, 0x13, 0x8f, 0x1f, 0x95, 0xd2, 0x7d,
+ 0xb9, 0x91, 0x47, 0xff, 0xeb, 0x24, 0x3f, 0x10,
+ 0x87, 0x1f, 0x2d, 0x95, 0xa0, 0xf3, 0xf4, 0x24,
+ 0xde, 0x7f, 0xaa, 0xa8, 0x7c, 0x9a, 0x40, 0xbf,
+ 0x56, 0x21, 0x12, 0x5f, 0x6f, 0x3e, 0x26, 0x9e,
+ 0x1b, 0x7f, 0xa9, 0xfd, 0x46, 0x72, 0x6f, 0x8f,
+ 0x8e, 0xaa, 0x1f, 0xe8, 0x43, 0x3c, 0x2f, 0x4c,
+ 0x2b, 0x06, 0xcf, 0x60, 0xfd, 0x89, 0x5f, 0x6d,
+ 0xd1, 0xfe, 0xf6, 0x58, 0x68, 0x71, 0x43, 0xfd,
+ 0xcc, 0xaf, 0xc4, 0xc3, 0x69, 0x97, 0x8a, 0xf0,
+ 0xdb, 0x3c, 0xa7, 0xb6, 0x87, 0xf2, 0x75, 0x0e,
+ 0xeb, 0x0a, 0xee, 0xe1, 0x05, 0xbf, 0x1a, 0x61,
+ 0x21, 0x6f, 0x2c, 0x79, 0x6e, 0x56, 0x8c, 0xb4,
+ 0x50, 0x31, 0x7c, 0x23, 0x3c, 0x97, 0xba, 0xd3,
+ 0xa5, 0x0e, 0x7d, 0x87, 0x69, 0x4d, 0xbf, 0xe6,
+ 0x58, 0x4b, 0x7a, 0x79, 0x15, 0xbd, 0xcd, 0xe9,
+ 0xa2, 0x7e, 0xba, 0x0c, 0x7a, 0x58, 0x78, 0x85,
+ 0x75, 0x9b, 0xf5, 0xc4, 0xc5, 0x57, 0x71, 0xd2,
+ 0x53, 0xa7, 0xd1, 0x16, 0x2e, 0xfc, 0xad, 0xf3,
+ 0xdb, 0xc6, 0xf7, 0x56, 0xb4, 0x52, 0xfe, 0xfd,
+ 0x8a, 0xef, 0x78, 0x7a, 0xf9, 0x32, 0xdc, 0x13,
+ 0x3f, 0xb2, 0x7c, 0xb7, 0xf2, 0x31, 0x86, 0x3e,
+ 0x65, 0xf6, 0x41, 0xad, 0x27, 0x85, 0xfe, 0x69,
+ 0x7c, 0x10, 0x7d, 0x83, 0xe9, 0x9e, 0x62, 0x61,
+ 0x77, 0x82, 0xf8, 0x52, 0xea, 0xce, 0xf9, 0x7a,
+ 0x8a, 0x5f, 0x37, 0x7c, 0xc8, 0x17, 0x6f, 0xa4,
+ 0x3f, 0x87, 0xf0, 0x6d, 0x71, 0xc3, 0xe9, 0x5b,
+ 0xcc, 0x37, 0x7c, 0x7e, 0xfb, 0x37, 0xe8, 0xae,
+ 0x29, 0x98, 0x63, 0x61, 0xbf, 0x63, 0x07, 0x78,
+ 0x34, 0xfa, 0x91, 0x92, 0xab, 0xbf, 0xaf, 0x66,
+ 0x1d, 0x09, 0x93, 0xa5, 0xa7, 0xc7, 0x4e, 0x58,
+ 0xf8, 0x64, 0x93, 0xfc, 0x44, 0x03, 0xde, 0x7b,
+ 0xae, 0x3e, 0x71, 0x61, 0x4a, 0xe3, 0x63, 0x52,
+ 0xdb, 0xd0, 0x07, 0x9a, 0x91, 0xf8, 0xfa, 0xb3,
+ 0x4d, 0x15, 0xff, 0xc7, 0xf1, 0xe3, 0x11, 0xe3,
+ 0x54, 0x5f, 0x86, 0x53, 0xaf, 0x27, 0x8e, 0xd0,
+ 0x79, 0x17, 0xc3, 0x67, 0xd5, 0x9b, 0x28, 0x3f,
+ 0xff, 0x07, 0xfe, 0xc2, 0xff, 0x91, 0xfc, 0xe7,
+ 0x95, 0xea, 0x16, 0x42, 0xda, 0xeb, 0x3e, 0xab,
+ 0x50, 0x67, 0xf3, 0x3e, 0x50, 0x3c, 0x5f, 0x44,
+ 0x37, 0xd2, 0x63, 0xa4, 0x1f, 0xab, 0xd9, 0xaf,
+ 0xff, 0x41, 0xf9, 0xc1, 0xe9, 0x3c, 0xff, 0x3b,
+ 0x23, 0x3f, 0x66, 0xc8, 0xaf, 0x9a, 0xc7, 0xf5,
+ 0xf7, 0x81, 0xe8, 0xc5, 0x9a, 0xe2, 0xfa, 0x7e,
+ 0x06, 0xe7, 0x5c, 0x39, 0x41, 0x7e, 0xe1, 0x02,
+ 0x79, 0xfd, 0x36, 0x40, 0xf7, 0x75, 0xfb, 0x9a,
+ 0x85, 0xcc, 0xc6, 0xf8, 0x32, 0x73, 0x9e, 0xfb,
+ 0xea, 0xe2, 0x3b, 0x17, 0xbe, 0x85, 0xfa, 0xfd,
+ 0xc3, 0x26, 0xf5, 0x1b, 0xa7, 0xd0, 0x31, 0x87,
+ 0xb6, 0xf2, 0xc7, 0xef, 0xf0, 0xf7, 0x67, 0x7f,
+ 0x47, 0x37, 0x4d, 0x03, 0xf2, 0x3c, 0xf3, 0xc4,
+ 0x7f, 0xe0, 0xae, 0xe8, 0xbe, 0xc3, 0x39, 0xe5,
+ 0x4b, 0x27, 0x9e, 0xd3, 0xf4, 0x72, 0x3e, 0xfc,
+ 0x21, 0x3a, 0xea, 0xda, 0x41, 0xfe, 0xdb, 0x91,
+ 0x7a, 0xb0, 0x60, 0xb4, 0xf2, 0x7f, 0x24, 0xfe,
+ 0x39, 0x6c, 0xbb, 0xfa, 0xcf, 0x4a, 0x63, 0x2c,
+ 0x04, 0x25, 0xca, 0x2f, 0x6c, 0x18, 0x6b, 0x61,
+ 0x6c, 0x7f, 0xf9, 0xb9, 0xd7, 0x87, 0x2c, 0x4c,
+ 0xde, 0x48, 0x9d, 0x36, 0xb3, 0x4a, 0xb0, 0xad,
+ 0x11, 0xea, 0x87, 0x26, 0xa1, 0x9b, 0xa3, 0x6f,
+ 0xc8, 0x8f, 0x6c, 0x27, 0xbf, 0x7d, 0xe7, 0xca,
+ 0xff, 0xc4, 0x12, 0x0f, 0xdd, 0x73, 0xe5, 0x27,
+ 0x52, 0x39, 0xef, 0x49, 0x67, 0xd5, 0xaf, 0xf4,
+ 0x44, 0xef, 0x7f, 0xff, 0x48, 0xf9, 0xdd, 0x66,
+ 0x95, 0x85, 0xa9, 0x17, 0x35, 0xdf, 0xd9, 0x81,
+ 0xbf, 0xea, 0x33, 0x50, 0xeb, 0x9f, 0x46, 0x3c,
+ 0xde, 0xcf, 0x97, 0x5f, 0x9e, 0x89, 0x3f, 0xfe,
+ 0x7c, 0xbe, 0xfc, 0xf1, 0x3b, 0xd6, 0x3d, 0x6b,
+ 0x57, 0x1a, 0xfc, 0xc1, 0x03, 0xb6, 0xdd, 0x08,
+ 0xdf, 0x61, 0xde, 0x10, 0x17, 0x6f, 0xfb, 0xc9,
+ 0x2f, 0x1f, 0xa7, 0x7e, 0xac, 0x3d, 0x2c, 0xbf,
+ 0x1d, 0xcc, 0x7d, 0xfe, 0xb2, 0x5e, 0xfa, 0xe2,
+ 0xcc, 0x7d, 0xdd, 0x8b, 0x51, 0x7c, 0x2d, 0x46,
+ 0x1f, 0x67, 0xf6, 0x93, 0xfe, 0xf5, 0x66, 0x3e,
+ 0xe4, 0x1d, 0x89, 0xef, 0x32, 0x87, 0xd1, 0x83,
+ 0x97, 0xb1, 0xdc, 0xbb, 0x09, 0x63, 0xdf, 0x8b,
+ 0x8b, 0x98, 0x0b, 0x98, 0x10, 0xfa, 0x8a, 0xd3,
+ 0x9b, 0xd4, 0x8f, 0x0e, 0xc1, 0x2f, 0x6e, 0x75,
+ 0x3e, 0x07, 0x6f, 0xcb, 0x3d, 0x3b, 0xba, 0x68,
+ 0x1e, 0xf7, 0x94, 0x39, 0x4d, 0x42, 0x05, 0xce,
+ 0xc1, 0x68, 0x2c, 0x35, 0x79, 0x69, 0x75, 0xfe,
+ 0xc3, 0x8d, 0xf3, 0xca, 0xf5, 0x96, 0x5f, 0x7e,
+ 0x82, 0xbf, 0x75, 0x98, 0xdb, 0x10, 0xde, 0x8b,
+ 0xf9, 0x4f, 0xfe, 0x0f, 0xd2, 0x7b, 0x83, 0xce,
+ 0xb8, 0x74, 0x54, 0x7f, 0xf2, 0x5d, 0x1f, 0xb6,
+ 0x3d, 0x43, 0xeb, 0x4d, 0xa5, 0x7e, 0xae, 0x8e,
+ 0x43, 0xb7, 0x4c, 0x5f, 0xd6, 0x3d, 0x24, 0x5b,
+ 0xe7, 0xe1, 0xce, 0xfe, 0x37, 0x26, 0x68, 0xbe,
+ 0xf5, 0x3d, 0xe7, 0x92, 0x91, 0xae, 0x7a, 0xb5,
+ 0x0a, 0xbd, 0x2d, 0x93, 0xa3, 0x7c, 0x18, 0x49,
+ 0x5c, 0x14, 0xcb, 0x50, 0x3e, 0x5f, 0x40, 0x4f,
+ 0x93, 0x0b, 0xd4, 0x3f, 0xed, 0xa0, 0xcf, 0x76,
+ 0x2e, 0x52, 0xbe, 0x1d, 0x61, 0x1e, 0xb1, 0xb6,
+ 0x0b, 0x71, 0x69, 0x7e, 0x21, 0x4e, 0x63, 0x8b,
+ 0xe9, 0x3c, 0xca, 0xb1, 0xfe, 0x4b, 0x5b, 0xdb,
+ 0xe9, 0x7d, 0xac, 0xc3, 0xe1, 0xfd, 0xaf, 0xb5,
+ 0x3d, 0xe2, 0x29, 0x67, 0xb9, 0xea, 0x75, 0x01,
+ 0xf7, 0x79, 0xf9, 0xad, 0xe2, 0xe9, 0x39, 0xf5,
+ 0xe8, 0xdb, 0x67, 0xc4, 0x91, 0xf1, 0xfd, 0xc6,
+ 0xc2, 0xc9, 0xa4, 0xa5, 0x70, 0x37, 0x74, 0xee,
+ 0xfb, 0x28, 0xf9, 0x87, 0x1a, 0xe8, 0x74, 0x93,
+ 0x28, 0xe9, 0x4d, 0x63, 0xe2, 0x27, 0xde, 0x41,
+ 0x7a, 0xed, 0xc8, 0x7d, 0x9d, 0xda, 0xab, 0xfe,
+ 0xe3, 0x20, 0x73, 0xa7, 0x51, 0x9d, 0x34, 0x1f,
+ 0x3b, 0x4b, 0xde, 0x2c, 0x9a, 0x20, 0xbd, 0xf0,
+ 0x64, 0xbd, 0xa1, 0xc1, 0xe8, 0x8c, 0x59, 0x49,
+ 0x3d, 0xc9, 0x6e, 0x25, 0xbf, 0xfb, 0x1e, 0x7d,
+ 0xf4, 0xc0, 0xa9, 0x8a, 0xcf, 0x85, 0xe8, 0x8e,
+ 0xc7, 0x1c, 0xf9, 0x9d, 0x86, 0xe8, 0x4a, 0x8d,
+ 0x29, 0xea, 0xd7, 0xab, 0xe2, 0x1f, 0x1c, 0x3a,
+ 0x7d, 0x0a, 0x2f, 0x45, 0xfd, 0x70, 0x6e, 0xc7,
+ 0x73, 0xcd, 0x55, 0x74, 0xfc, 0xcd, 0x75, 0xcd,
+ 0x5b, 0xbf, 0xc3, 0x5f, 0xb8, 0x25, 0x2b, 0x3e,
+ 0x8e, 0x11, 0x3f, 0x9b, 0x32, 0xf8, 0x9e, 0xd9,
+ 0x4a, 0x3f, 0x7f, 0xab, 0x89, 0xd6, 0x9f, 0xca,
+ 0xfe, 0x2e, 0xc6, 0xcb, 0x5f, 0xfd, 0xe8, 0x05,
+ 0xbf, 0xab, 0xf9, 0xe1, 0x2a, 0xea, 0x67, 0x4f,
+ 0x77, 0xc5, 0x4b, 0x0b, 0xf2, 0x71, 0xe4, 0x1d,
+ 0xcd, 0x17, 0x2e, 0xa1, 0x7b, 0x03, 0x82, 0xd2,
+ 0xe0, 0x17, 0xa8, 0x67, 0xe3, 0xf3, 0x74, 0x9f,
+ 0xcd, 0x99, 0x7b, 0xfd, 0x59, 0xf4, 0x4f, 0x3d,
+ 0xa1, 0x3e, 0x24, 0x5f, 0x09, 0x80, 0xbf, 0x40,
+ 0x17, 0xf3, 0xc2, 0xa4, 0xe7, 0xd9, 0xf8, 0xcd,
+ 0x4a, 0x6e, 0xf2, 0xff, 0x8f, 0xa8, 0x33, 0x39,
+ 0xfd, 0x55, 0x6f, 0xaa, 0xf3, 0xb9, 0xd3, 0x0e,
+ 0xc4, 0x91, 0xb9, 0x49, 0x5c, 0xc6, 0xce, 0x53,
+ 0x3f, 0xff, 0x27, 0xf3, 0x92, 0xad, 0xe7, 0xb5,
+ 0xdf, 0x5e, 0xf4, 0x3b, 0xb3, 0x3c, 0xe4, 0x1f,
+ 0xfe, 0xcb, 0xb9, 0x56, 0x4b, 0x55, 0x7d, 0xef,
+ 0x4f, 0x7d, 0xf5, 0x68, 0xac, 0xfc, 0x7d, 0x48,
+ 0x1d, 0x7a, 0xd0, 0x50, 0xf7, 0x39, 0x06, 0xdf,
+ 0x5e, 0xfb, 0xb6, 0xe6, 0xb3, 0xfe, 0xf8, 0x11,
+ 0x9f, 0xb3, 0x9a, 0x9f, 0x4f, 0xc7, 0xdf, 0x7f,
+ 0x18, 0x94, 0x03, 0x8f, 0x4e, 0xb4, 0x10, 0xa7,
+ 0xb9, 0xad, 0x29, 0x42, 0x5f, 0x42, 0xaa, 0x68,
+ 0xfd, 0x5f, 0x60, 0x9c, 0x9f, 0x64, 0xe9, 0x79,
+ 0x83, 0x88, 0xbf, 0xae, 0xc5, 0xe5, 0x4f, 0x4a,
+ 0x32, 0x37, 0xf2, 0x1b, 0xb8, 0x1d, 0xde, 0x10,
+ 0xbf, 0x51, 0xb7, 0xeb, 0xb7, 0xf0, 0x72, 0x70,
+ 0xe7, 0x48, 0x74, 0xde, 0x5c, 0x44, 0x57, 0x1e,
+ 0xb8, 0x2a, 0x5e, 0x13, 0xd8, 0xaf, 0x67, 0x5d,
+ 0x7c, 0xad, 0x19, 0xef, 0x65, 0xa1, 0x4d, 0xa0,
+ 0xf4, 0xde, 0xe7, 0x99, 0x85, 0xa3, 0xaf, 0xd4,
+ 0xcf, 0x05, 0x70, 0x7f, 0x0d, 0x76, 0xa9, 0x5f,
+ 0x8e, 0xe5, 0x1c, 0x0e, 0x9d, 0xd1, 0xfd, 0xf5,
+ 0xc6, 0xc7, 0x15, 0xb6, 0xd6, 0x79, 0x3e, 0x87,
+ 0x9f, 0xbe, 0x82, 0xee, 0x1a, 0x37, 0xe6, 0xfe,
+ 0x1b, 0xba, 0xa9, 0x7e, 0x4c, 0x61, 0x7f, 0x15,
+ 0x5e, 0xa9, 0xbf, 0x2a, 0xcd, 0x7d, 0x5f, 0x9b,
+ 0xac, 0xfe, 0xb7, 0x38, 0xf9, 0x39, 0xa2, 0x50,
+ 0xfe, 0xb5, 0x26, 0xf3, 0xaa, 0x32, 0x29, 0xd2,
+ 0x9f, 0x1d, 0xc4, 0x4f, 0xaf, 0x54, 0xe6, 0x84,
+ 0xa6, 0x99, 0x8b, 0x85, 0x03, 0xdb, 0xe5, 0xaf,
+ 0x87, 0x10, 0x77, 0xc5, 0xb2, 0xd4, 0x9f, 0x45,
+ 0x91, 0x67, 0xce, 0x49, 0x4d, 0xf4, 0x3c, 0xe2,
+ 0xc2, 0x39, 0x4f, 0xf1, 0xeb, 0xc7, 0x5c, 0x78,
+ 0xd8, 0x0b, 0x7d, 0x5e, 0xf3, 0xbe, 0xb5, 0x3f,
+ 0xca, 0x6f, 0x2c, 0xc0, 0xb7, 0x7e, 0x5d, 0x4e,
+ 0xf7, 0x3d, 0x10, 0x5f, 0x7b, 0xa9, 0x85, 0xfe,
+ 0x1e, 0x8a, 0x1f, 0xf2, 0xcf, 0x53, 0x7e, 0xad,
+ 0xe3, 0x9c, 0xe6, 0xdf, 0x96, 0x7e, 0xdd, 0xc7,
+ 0x2f, 0x94, 0x3f, 0xbd, 0x00, 0xbe, 0x8a, 0xba,
+ 0x59, 0xb0, 0x52, 0xf7, 0xf7, 0x80, 0x39, 0xf2,
+ 0xba, 0x03, 0xf4, 0x75, 0xa6, 0xff, 0x52, 0xd6,
+ 0xb7, 0x57, 0xbf, 0xff, 0xec, 0xc0, 0x47, 0xbe,
+ 0xca, 0xd6, 0x7d, 0x0e, 0x8e, 0xb0, 0x70, 0x75,
+ 0x89, 0xf2, 0x63, 0x0f, 0xe7, 0x7a, 0x20, 0x4b,
+ 0xf1, 0x75, 0x0d, 0x3d, 0xee, 0xdb, 0xb1, 0x1a,
+ 0xfc, 0x18, 0x75, 0xfd, 0xcd, 0x84, 0x7c, 0xf8,
+ 0x30, 0xce, 0xcd, 0x69, 0xb3, 0xfc, 0xc4, 0x60,
+ 0xf2, 0xb6, 0x81, 0xbb, 0xea, 0xeb, 0x36, 0xf2,
+ 0xe5, 0xa6, 0x13, 0x3a, 0x6a, 0xb6, 0xa0, 0xd3,
+ 0x45, 0x4b, 0x54, 0x5f, 0x93, 0xe8, 0x0b, 0x2b,
+ 0x4d, 0x94, 0x3e, 0xf9, 0x11, 0x77, 0x01, 0xbd,
+ 0x98, 0xdb, 0x98, 0x35, 0x9f, 0x59, 0x68, 0xb6,
+ 0x0f, 0x9f, 0x60, 0x46, 0xbd, 0x6f, 0xc1, 0xbb,
+ 0x19, 0xf7, 0x68, 0x6a, 0x31, 0xef, 0xeb, 0xdd,
+ 0x1b, 0xdf, 0x68, 0x7e, 0x47, 0x2f, 0xa7, 0x5d,
+ 0x93, 0xbe, 0x0d, 0x61, 0x6e, 0x34, 0x75, 0x8c,
+ 0xce, 0x77, 0x12, 0xfe, 0x23, 0x31, 0x5c, 0xfd,
+ 0x55, 0x2f, 0xe6, 0x91, 0x35, 0x3e, 0xed, 0x02,
+ 0x1f, 0xc7, 0x7d, 0xd7, 0x0b, 0x7a, 0xa6, 0xcf,
+ 0xb3, 0x8e, 0x26, 0xf9, 0x7a, 0x7f, 0x9d, 0x34,
+ 0x0b, 0xa9, 0x7b, 0x35, 0xcf, 0xfd, 0x04, 0x1d,
+ 0x8f, 0x68, 0xa1, 0x7a, 0xfe, 0xf2, 0x8c, 0x85,
+ 0xac, 0x53, 0xba, 0x8f, 0x20, 0xf6, 0xb7, 0xee,
+ 0xa1, 0xe6, 0x51, 0x57, 0x88, 0xc3, 0x2a, 0xe1,
+ 0xca, 0x97, 0x3d, 0xbc, 0xef, 0xc3, 0x0c, 0x17,
+ 0xf8, 0x04, 0x7c, 0x6d, 0x8f, 0x78, 0xfd, 0x9e,
+ 0xd0, 0x87, 0x7b, 0xbe, 0x70, 0x5d, 0xfd, 0x7b,
+ 0xd8, 0x75, 0x0b, 0x53, 0x2e, 0xea, 0xf7, 0x0a,
+ 0x4f, 0xce, 0xbf, 0xc7, 0x69, 0x74, 0xc5, 0xdc,
+ 0x43, 0xef, 0x67, 0x34, 0x41, 0x37, 0xcc, 0x51,
+ 0xea, 0xd4, 0xe0, 0x2f, 0xf0, 0xc5, 0xa6, 0x19,
+ 0xfd, 0xb2, 0x73, 0x6d, 0xcd, 0xe3, 0x67, 0x30,
+ 0x2f, 0x5a, 0x57, 0x46, 0xef, 0x6f, 0x49, 0x3d,
+ 0xcd, 0x6b, 0xa0, 0x7a, 0x95, 0x42, 0x9c, 0x0c,
+ 0x19, 0x88, 0x8f, 0x33, 0xc9, 0xc4, 0xa5, 0x63,
+ 0xe4, 0x52, 0xf8, 0x35, 0xe2, 0xea, 0xc0, 0x36,
+ 0xd5, 0xc3, 0x6c, 0xf4, 0xb2, 0xdd, 0x39, 0xf9,
+ 0xf1, 0xcf, 0xc8, 0x83, 0xc4, 0x18, 0xf5, 0x6b,
+ 0x81, 0xe8, 0xad, 0xe3, 0x63, 0x2f, 0x78, 0x06,
+ 0x7a, 0x7c, 0xc2, 0x4d, 0x7e, 0xcf, 0x3f, 0xca,
+ 0x42, 0xed, 0x92, 0xf2, 0xf7, 0xcb, 0xf1, 0x65,
+ 0x67, 0x22, 0x74, 0x3f, 0x2b, 0x99, 0xe3, 0x1e,
+ 0xa8, 0xab, 0x7e, 0xaf, 0x22, 0xf9, 0x12, 0x31,
+ 0x5f, 0x7e, 0xc9, 0x8b, 0xdf, 0x95, 0x2e, 0x4e,
+ 0x55, 0x7d, 0xbd, 0xc6, 0xef, 0x09, 0x23, 0x8b,
+ 0x69, 0xfe, 0x76, 0x9d, 0x38, 0x1b, 0xb8, 0x52,
+ 0xf9, 0xfa, 0x86, 0xfc, 0x72, 0xab, 0xae, 0xf5,
+ 0x75, 0xe2, 0x5c, 0xcf, 0x94, 0x56, 0x3f, 0x33,
+ 0x89, 0xfe, 0xfb, 0xdd, 0x06, 0xd5, 0xe3, 0x14,
+ 0x7c, 0x48, 0x2f, 0x47, 0xe9, 0xd5, 0x01, 0xf4,
+ 0x3a, 0xfa, 0xde, 0x1e, 0xf8, 0x22, 0x74, 0xae,
+ 0x54, 0x26, 0x73, 0xfd, 0x7f, 0xe6, 0x5d, 0x37,
+ 0xdc, 0xa5, 0x37, 0x4e, 0xdc, 0x53, 0xf3, 0x4a,
+ 0x8a, 0xbf, 0xed, 0xe8, 0xc8, 0x80, 0x10, 0xde,
+ 0x63, 0x02, 0xab, 0x58, 0xf0, 0xa9, 0x2b, 0x7f,
+ 0xf9, 0x01, 0xba, 0xf7, 0xf0, 0xb1, 0xea, 0x61,
+ 0x3e, 0x73, 0xd2, 0xa6, 0xcd, 0x75, 0x3e, 0x13,
+ 0xf1, 0x29, 0xd5, 0x37, 0xeb, 0xf7, 0x03, 0x47,
+ 0xe2, 0xbe, 0x7b, 0xb0, 0xfa, 0x8f, 0x59, 0xe8,
+ 0xd8, 0xcd, 0x8e, 0xea, 0x1f, 0xa3, 0x88, 0xa3,
+ 0xf0, 0x7d, 0xf8, 0x4c, 0x53, 0x88, 0x5e, 0xde,
+ 0x3a, 0x2a, 0x23, 0xe8, 0x87, 0xbe, 0x8d, 0xcd,
+ 0xf4, 0x82, 0xdf, 0xa2, 0xee, 0x54, 0xf8, 0x4c,
+ 0xfd, 0x54, 0x22, 0xfd, 0xc8, 0xaa, 0xad, 0xc4,
+ 0x89, 0x39, 0x48, 0x9d, 0xed, 0xb0, 0x53, 0xf5,
+ 0x79, 0x23, 0xf9, 0x5b, 0x39, 0x4d, 0xf9, 0x53,
+ 0x93, 0x7d, 0x44, 0xfb, 0x69, 0x5e, 0xdd, 0x93,
+ 0x3e, 0xfa, 0xcc, 0xa3, 0x29, 0xf0, 0x37, 0xcc,
+ 0xf7, 0x36, 0xbf, 0x46, 0xa7, 0x4d, 0x71, 0xe6,
+ 0x5d, 0x1e, 0xeb, 0x55, 0x5f, 0x97, 0x72, 0x7f,
+ 0x4f, 0x83, 0xa4, 0x77, 0xdd, 0xd3, 0x2c, 0xbc,
+ 0x1d, 0xa3, 0xfa, 0xe5, 0x4a, 0x5e, 0xf6, 0x9c,
+ 0x1e, 0x05, 0xef, 0x86, 0x2f, 0x5d, 0x11, 0xc3,
+ 0x9c, 0xc9, 0x34, 0x66, 0x3f, 0x77, 0x7f, 0xd3,
+ 0x3c, 0xdf, 0x15, 0x3f, 0x59, 0xc7, 0x53, 0xeb,
+ 0x39, 0x87, 0x4e, 0x0e, 0xf5, 0xd0, 0xef, 0xb9,
+ 0x4b, 0xa8, 0xa3, 0xbb, 0xf3, 0x42, 0xe1, 0x13,
+ 0xf8, 0xdd, 0xb7, 0x4b, 0x86, 0xf6, 0xdb, 0x95,
+ 0xb8, 0xfa, 0x28, 0x5a, 0xfd, 0xef, 0x7c, 0xf6,
+ 0x9b, 0x53, 0x5e, 0xf3, 0xb6, 0xf6, 0xdc, 0xc7,
+ 0x4e, 0x5f, 0xcd, 0x17, 0x23, 0x99, 0x93, 0x2f,
+ 0x1a, 0x72, 0x14, 0x3e, 0x89, 0xfb, 0x5e, 0xe4,
+ 0xa4, 0x7e, 0xb8, 0x11, 0xf5, 0x75, 0x6a, 0x67,
+ 0xd5, 0xc3, 0xad, 0xe8, 0x46, 0x72, 0x57, 0xd5,
+ 0xd7, 0x10, 0xfa, 0xf9, 0x87, 0xd3, 0x54, 0xdf,
+ 0x0f, 0x91, 0xdf, 0xe1, 0x71, 0xf2, 0x17, 0xad,
+ 0xf3, 0x2d, 0x38, 0x86, 0xcd, 0xd1, 0xfe, 0x78,
+ 0x5f, 0x70, 0x9c, 0xf2, 0xf7, 0x7e, 0xb7, 0xff,
+ 0x03
+};
+
+/** Length of the pre-compressed data using zlib algorithm */
+#define COMP_ZLIB_SIZE 3771
+
+/** Pre-compressed data using zlib algorithm */
+static uint8_t compressed_text_zlib[COMP_ZLIB_SIZE] = {
+ 0x78, 0x9c, 0x35, 0x99, 0x77, 0x5c, 0xd6, 0xe5,
+ 0x1a, 0xc6, 0x1f, 0x70, 0xa2, 0xe1, 0x0c, 0x50,
+ 0xb4, 0x70, 0xe7, 0x44, 0xc5, 0x34, 0x27, 0xe6,
+ 0x04, 0x15, 0x51, 0x73, 0x1e, 0x51, 0x8e, 0x60,
+ 0x42, 0xe2, 0x08, 0xf5, 0x68, 0x99, 0x24, 0xb8,
+ 0xd3, 0x24, 0xad, 0xe3, 0x9e, 0xb8, 0x11, 0x50,
+ 0x12, 0x15, 0xd2, 0xd0, 0xcc, 0x2d, 0x66, 0x28,
+ 0xe0, 0x20, 0x89, 0x5c, 0xe4, 0xc4, 0x81, 0x0b,
+ 0xc5, 0x71, 0xce, 0xe7, 0xf9, 0x5e, 0xf5, 0x47,
+ 0x57, 0x57, 0xbc, 0xef, 0xef, 0xf7, 0x8c, 0xfb,
+ 0xbe, 0xee, 0xeb, 0xbe, 0x5f, 0x63, 0xfe, 0xff,
+ 0x4f, 0x68, 0xb7, 0x6f, 0x8d, 0xfd, 0xe7, 0x78,
+ 0x6d, 0x0b, 0xa3, 0x57, 0xb4, 0x87, 0x4f, 0xf2,
+ 0xb5, 0xd0, 0x2f, 0xe7, 0x47, 0xf8, 0xdb, 0x61,
+ 0x16, 0x7a, 0x2e, 0xb9, 0x05, 0xbf, 0x37, 0xc4,
+ 0x42, 0x03, 0x8f, 0xef, 0xe0, 0x67, 0x22, 0x2c,
+ 0xec, 0x1f, 0x52, 0x0d, 0x1e, 0x7f, 0xd1, 0x42,
+ 0x41, 0xa7, 0x5d, 0xf0, 0xa2, 0xf7, 0x2d, 0x5c,
+ 0xdf, 0x7e, 0x0d, 0xde, 0xfd, 0xaa, 0x85, 0x08,
+ 0xf3, 0x6f, 0xf8, 0xe1, 0x60, 0x0b, 0x03, 0x4a,
+ 0xd7, 0x80, 0x17, 0x7e, 0x60, 0xa1, 0x5a, 0x6d,
+ 0x57, 0xf8, 0xad, 0xdb, 0x16, 0x6e, 0x9e, 0x59,
+ 0x0c, 0x8f, 0xee, 0x62, 0x61, 0x4a, 0xf9, 0x72,
+ 0xf0, 0xa1, 0x75, 0x2d, 0x78, 0xbe, 0x58, 0x03,
+ 0x1f, 0x39, 0xd1, 0x42, 0x8b, 0xc3, 0x39, 0xf0,
+ 0xf5, 0xac, 0xe7, 0xd4, 0xd1, 0x6d, 0xf0, 0x55,
+ 0xa5, 0x2c, 0x84, 0x14, 0x85, 0xc1, 0xbd, 0x86,
+ 0x5b, 0x70, 0xee, 0xbb, 0x1a, 0x9e, 0xc2, 0xe7,
+ 0x9b, 0xef, 0x6f, 0x02, 0x1f, 0x72, 0xc3, 0xc2,
+ 0xa6, 0x66, 0x13, 0xf4, 0xbe, 0x6c, 0x0b, 0x75,
+ 0x83, 0xc6, 0xc2, 0x87, 0xcf, 0xb7, 0x50, 0x62,
+ 0x57, 0x5b, 0xb8, 0xdb, 0x26, 0xbe, 0x16, 0xcb,
+ 0x3a, 0xcd, 0xed, 0xa1, 0x16, 0x02, 0x67, 0x8f,
+ 0x84, 0x67, 0xc5, 0x5a, 0x88, 0x3f, 0x90, 0x0f,
+ 0x4f, 0x5a, 0x68, 0xe1, 0xcc, 0xd9, 0xfe, 0xf0,
+ 0xe3, 0x57, 0x2c, 0xac, 0x9e, 0xb2, 0x0a, 0xbe,
+ 0x67, 0x1d, 0xe7, 0x53, 0xe7, 0x31, 0xbc, 0xe2,
+ 0x5c, 0x0b, 0xe5, 0x8e, 0xb6, 0x82, 0xcf, 0x7c,
+ 0xcf, 0xc2, 0x7f, 0x0a, 0x97, 0xc2, 0x3b, 0xf1,
+ 0xf7, 0x9a, 0x4f, 0x23, 0xe1, 0x1d, 0x0e, 0x58,
+ 0xc8, 0x89, 0x9e, 0x06, 0x9f, 0xf7, 0xd0, 0x42,
+ 0xb8, 0x7b, 0x2d, 0xad, 0xef, 0x4f, 0x0b, 0xde,
+ 0x27, 0x4b, 0xc2, 0x37, 0x64, 0xf1, 0x98, 0xc3,
+ 0x7a, 0xff, 0xd5, 0x42, 0x0b, 0xaf, 0x76, 0x94,
+ 0x85, 0x87, 0x12, 0x2f, 0xde, 0x33, 0x8b, 0xc3,
+ 0x1d, 0xb8, 0xc7, 0xe9, 0xc7, 0xeb, 0xc0, 0x97,
+ 0x37, 0xb4, 0x70, 0x67, 0x9f, 0xf6, 0x9b, 0xd3,
+ 0xd3, 0xc2, 0x98, 0xcc, 0x3b, 0xf0, 0x65, 0x5f,
+ 0x58, 0x58, 0x74, 0x51, 0xcf, 0x6b, 0xbd, 0xd9,
+ 0xc2, 0xb0, 0x21, 0x03, 0xe1, 0x0d, 0xfd, 0x2d,
+ 0xdc, 0xeb, 0xcc, 0x7b, 0xcc, 0x8f, 0xc4, 0x57,
+ 0xbb, 0x2c, 0xfe, 0xbf, 0x29, 0x62, 0x9d, 0x5e,
+ 0xab, 0x2e, 0xc1, 0x13, 0x7f, 0xb3, 0xd0, 0x3e,
+ 0x7a, 0x1c, 0x7c, 0xc0, 0x22, 0xbe, 0x3f, 0x5f,
+ 0xfb, 0xab, 0x42, 0x5c, 0x5e, 0x8e, 0xd4, 0x7d,
+ 0x0c, 0xe3, 0x7d, 0x8e, 0x2f, 0xde, 0xc2, 0xdd,
+ 0x89, 0xf7, 0x37, 0x5f, 0x2e, 0x81, 0x77, 0xf9,
+ 0x99, 0xd7, 0x7c, 0x7c, 0x18, 0xfe, 0xb2, 0x9e,
+ 0x85, 0xb8, 0xdc, 0x0d, 0xf0, 0xb6, 0x47, 0xf8,
+ 0xfb, 0x9c, 0xbf, 0xe1, 0x0d, 0xb6, 0x58, 0x38,
+ 0x97, 0xaf, 0x78, 0xfc, 0xbb, 0xa3, 0x85, 0x12,
+ 0x0f, 0xb5, 0xde, 0xf6, 0xbf, 0x5a, 0x88, 0x48,
+ 0x51, 0x7c, 0x2d, 0x39, 0x61, 0x61, 0xf2, 0xa8,
+ 0xd6, 0xf0, 0x4b, 0xe5, 0x2d, 0x1c, 0x74, 0x71,
+ 0x80, 0x07, 0x11, 0x1f, 0xf3, 0x17, 0xb6, 0x81,
+ 0xbf, 0x20, 0xfe, 0x87, 0x25, 0xee, 0x85, 0xff,
+ 0xcc, 0x7a, 0x62, 0x93, 0x3f, 0x84, 0xaf, 0x24,
+ 0x6f, 0xcb, 0x27, 0x2b, 0x9e, 0x97, 0x7e, 0x6c,
+ 0x61, 0xd7, 0xe0, 0x77, 0xe0, 0x77, 0x5b, 0x5a,
+ 0x78, 0x74, 0x43, 0xef, 0xdf, 0xdd, 0x97, 0xf5,
+ 0x4d, 0xe7, 0x5e, 0xcd, 0x03, 0xe2, 0xff, 0x79,
+ 0x59, 0xe5, 0x87, 0xf9, 0xc5, 0xfe, 0xfb, 0xf6,
+ 0xec, 0xb3, 0xd0, 0x0d, 0xdc, 0x5b, 0xdb, 0x99,
+ 0x33, 0xe0, 0x79, 0x65, 0x2c, 0xa4, 0x2d, 0x62,
+ 0xdd, 0xa6, 0x6c, 0xb8, 0x85, 0xd3, 0x3d, 0xb5,
+ 0xfe, 0xb3, 0xc4, 0x9d, 0x6f, 0xf3, 0x05, 0x70,
+ 0x1f, 0x47, 0x0b, 0x9f, 0xfa, 0xec, 0x83, 0x1f,
+ 0x58, 0x66, 0xc1, 0xa3, 0x92, 0xf4, 0xa3, 0x36,
+ 0xef, 0x7f, 0xbf, 0x23, 0x3a, 0x61, 0x62, 0x89,
+ 0xf7, 0xea, 0x3e, 0xbc, 0xc7, 0x4c, 0xbb, 0xcf,
+ 0xf7, 0x8f, 0x91, 0x77, 0xe6, 0xd8, 0x6b, 0x0b,
+ 0xef, 0x36, 0x7a, 0x0e, 0x9f, 0x44, 0x5c, 0x0f,
+ 0xf5, 0x5e, 0xa6, 0xbf, 0xcf, 0xb4, 0x10, 0xb8,
+ 0xab, 0x00, 0xde, 0x8b, 0x38, 0x9a, 0x9a, 0xa9,
+ 0xf5, 0x8e, 0x6c, 0x6a, 0xa1, 0xdd, 0x0c, 0x74,
+ 0xc7, 0x84, 0xff, 0xc5, 0xf3, 0xef, 0x73, 0x8f,
+ 0xa6, 0x0e, 0xf1, 0x55, 0x31, 0x4d, 0xf1, 0xb1,
+ 0x8e, 0xfd, 0x39, 0x55, 0x54, 0xfc, 0xe6, 0xfc,
+ 0xcb, 0x42, 0xdc, 0x95, 0x14, 0x78, 0x56, 0x80,
+ 0x85, 0x84, 0x6c, 0xc5, 0x5f, 0x41, 0x45, 0x0b,
+ 0xd5, 0x02, 0xbf, 0x81, 0x57, 0xa8, 0x69, 0x21,
+ 0x31, 0x56, 0xf9, 0xd2, 0x8e, 0xe7, 0xb5, 0x98,
+ 0x8e, 0x0e, 0x9a, 0x33, 0x37, 0x2d, 0x5c, 0x2f,
+ 0x97, 0x0b, 0xef, 0x7b, 0xcc, 0x82, 0xef, 0x85,
+ 0x67, 0x70, 0x1f, 0xf2, 0xd1, 0xe5, 0x08, 0x79,
+ 0x65, 0x86, 0x71, 0xce, 0x3f, 0xf7, 0x79, 0x05,
+ 0xff, 0x1c, 0x5e, 0x6b, 0xf6, 0x23, 0x78, 0x55,
+ 0xf8, 0xfe, 0x7f, 0xf4, 0x62, 0xf5, 0x60, 0x0b,
+ 0x5f, 0x37, 0xfb, 0x03, 0xfe, 0x94, 0x7d, 0x96,
+ 0xfd, 0xe8, 0x27, 0xf8, 0x55, 0xce, 0xdd, 0x3d,
+ 0x41, 0xeb, 0xad, 0xce, 0xf7, 0x23, 0x17, 0x29,
+ 0x1e, 0x6a, 0xa3, 0x1f, 0x85, 0x43, 0x89, 0x5b,
+ 0xd3, 0x80, 0xf5, 0x17, 0x56, 0x09, 0x84, 0x6f,
+ 0x26, 0x0e, 0xab, 0xcd, 0x54, 0x7e, 0x54, 0x26,
+ 0xbf, 0xf7, 0x0e, 0xa4, 0x6e, 0x98, 0x34, 0xd6,
+ 0xed, 0x7f, 0x53, 0xeb, 0xbd, 0x95, 0x69, 0x61,
+ 0x45, 0xa5, 0x53, 0x70, 0x2f, 0xf6, 0xd9, 0x7a,
+ 0x65, 0x03, 0xf8, 0x15, 0xea, 0xcc, 0xd4, 0xb4,
+ 0x1f, 0xe0, 0xbd, 0x79, 0x4e, 0x87, 0x3b, 0x3b,
+ 0xe0, 0x1f, 0xba, 0x5b, 0x48, 0x1d, 0xee, 0x02,
+ 0xff, 0xf7, 0x78, 0x9e, 0x5f, 0x6d, 0x3a, 0xfc,
+ 0x0b, 0xee, 0xff, 0xe5, 0xb1, 0x18, 0xf8, 0xe9,
+ 0x5e, 0x16, 0xc6, 0xd6, 0xd7, 0x7a, 0x4b, 0x10,
+ 0x3f, 0x09, 0x61, 0xd2, 0x8f, 0xb9, 0x9c, 0x6f,
+ 0xcd, 0x37, 0x15, 0xe0, 0x8d, 0x5e, 0x5a, 0x88,
+ 0xfb, 0x9e, 0x7b, 0x36, 0xee, 0xe0, 0xc0, 0x30,
+ 0xe2, 0xc4, 0x6c, 0x8c, 0xb3, 0x70, 0x30, 0xda,
+ 0x19, 0x1e, 0x7e, 0x8f, 0xfd, 0xd6, 0x24, 0x6e,
+ 0xcd, 0xa4, 0xeb, 0x16, 0xda, 0xb9, 0x51, 0xc7,
+ 0x4c, 0x71, 0x27, 0x0b, 0xc3, 0x52, 0x74, 0x9e,
+ 0x99, 0xec, 0xb3, 0xd5, 0xdb, 0xc9, 0xf0, 0x25,
+ 0xe4, 0xe3, 0xee, 0x4a, 0xaa, 0xb7, 0x89, 0x9c,
+ 0xcb, 0xb5, 0x04, 0xea, 0x9a, 0x79, 0xc8, 0xb9,
+ 0xdd, 0xb8, 0xbe, 0x1c, 0xbe, 0x8e, 0xbc, 0xe8,
+ 0x33, 0x86, 0x7d, 0x18, 0x2f, 0xe2, 0xd4, 0x45,
+ 0x75, 0xd1, 0xf4, 0x68, 0x6e, 0xe1, 0x75, 0x4f,
+ 0xce, 0xc5, 0xcc, 0x44, 0x37, 0xfc, 0x3d, 0x95,
+ 0x2f, 0xdb, 0xe1, 0xa5, 0x17, 0xe8, 0xbe, 0xc3,
+ 0x1b, 0x5b, 0xc8, 0x4a, 0x3c, 0x0e, 0x4f, 0x42,
+ 0x2f, 0x17, 0xcc, 0xb9, 0x02, 0x6f, 0xc0, 0x79,
+ 0x0f, 0x38, 0xbe, 0x12, 0xee, 0x5b, 0xd9, 0xc2,
+ 0xaa, 0x92, 0xda, 0x5f, 0x19, 0xee, 0x2b, 0xf5,
+ 0x19, 0x71, 0x66, 0xce, 0x53, 0x8f, 0xca, 0x3d,
+ 0xca, 0x80, 0x57, 0xd9, 0x69, 0x21, 0xd6, 0xc7,
+ 0x1b, 0xde, 0x95, 0xfa, 0x93, 0xf0, 0x46, 0xfa,
+ 0x12, 0x40, 0x7d, 0x7c, 0xbc, 0xaf, 0x1f, 0xfc,
+ 0xbb, 0x78, 0x0b, 0x7e, 0x83, 0xfd, 0xe0, 0x17,
+ 0xd7, 0x5a, 0x68, 0x92, 0xcc, 0xbe, 0xcc, 0x6a,
+ 0x74, 0xd9, 0x69, 0xad, 0xf2, 0xb3, 0x33, 0xfb,
+ 0xce, 0x7b, 0x4c, 0x1e, 0x99, 0x92, 0xac, 0xe7,
+ 0x68, 0xa1, 0xfc, 0x43, 0x0b, 0xea, 0x40, 0xe2,
+ 0xda, 0x3d, 0xf0, 0x33, 0xc4, 0xc7, 0x26, 0x7f,
+ 0x7d, 0x7e, 0x76, 0x6f, 0x0b, 0x91, 0xeb, 0xa4,
+ 0x6f, 0x4d, 0x88, 0x87, 0x1d, 0xa1, 0xe4, 0xad,
+ 0xc9, 0xfa, 0xd4, 0xc2, 0x94, 0x9b, 0xd2, 0x5f,
+ 0x1f, 0xce, 0x35, 0xd7, 0x4f, 0xf5, 0xda, 0x8b,
+ 0xbc, 0xad, 0xbe, 0x50, 0xfa, 0x73, 0xaf, 0x83,
+ 0x85, 0x9e, 0x0b, 0x15, 0x1f, 0x47, 0x89, 0xf7,
+ 0x66, 0x9e, 0x9f, 0xc3, 0x9f, 0xa1, 0x8b, 0x35,
+ 0x3c, 0xa9, 0x23, 0xa6, 0x36, 0xeb, 0x9d, 0xd3,
+ 0xea, 0x3c, 0x7c, 0xec, 0x1c, 0x0b, 0x45, 0x73,
+ 0x88, 0x33, 0x93, 0x44, 0x7c, 0x95, 0xf2, 0x55,
+ 0x3c, 0x16, 0x47, 0xcf, 0x9e, 0x34, 0x8d, 0xd0,
+ 0xfb, 0xda, 0x59, 0x28, 0x5b, 0xb6, 0x0a, 0x7c,
+ 0x5f, 0x7d, 0x9e, 0x1f, 0x58, 0x03, 0xbe, 0x9f,
+ 0x7a, 0x32, 0x37, 0xde, 0x0b, 0x7e, 0x24, 0xc1,
+ 0x82, 0xf7, 0x33, 0x1f, 0x78, 0x09, 0xea, 0xf3,
+ 0xa3, 0xa8, 0x74, 0x3d, 0x0f, 0x3d, 0xce, 0xde,
+ 0x29, 0x1e, 0xfa, 0x99, 0x85, 0x16, 0xbf, 0xc9,
+ 0xcf, 0xa5, 0x14, 0x59, 0x58, 0xd9, 0xfd, 0xbf,
+ 0xf0, 0xbe, 0xf8, 0xc1, 0x4d, 0x6d, 0x7a, 0xc0,
+ 0xaf, 0x73, 0xef, 0xef, 0x36, 0x26, 0x0e, 0x4c,
+ 0x66, 0x28, 0xcf, 0x1b, 0x2e, 0x3f, 0xf2, 0x21,
+ 0xf7, 0xfd, 0xbc, 0xcf, 0x54, 0x78, 0x20, 0x75,
+ 0x37, 0xe6, 0xaf, 0x07, 0xf0, 0x02, 0xce, 0xa1,
+ 0xc2, 0x0c, 0x74, 0xd5, 0xc4, 0x12, 0x67, 0xcf,
+ 0x0a, 0xcf, 0x68, 0xbd, 0xac, 0x63, 0xc9, 0x48,
+ 0xd5, 0x17, 0x07, 0x74, 0xf5, 0x60, 0xb6, 0xea,
+ 0xdf, 0x1e, 0xea, 0x6d, 0x6a, 0x42, 0x12, 0xbc,
+ 0x2b, 0xe7, 0xf9, 0xe5, 0x78, 0xf1, 0x97, 0x1b,
+ 0x2d, 0xf4, 0x48, 0x57, 0xbc, 0x8d, 0xe6, 0xbd,
+ 0x7f, 0x1b, 0xee, 0xd9, 0xd4, 0x46, 0x47, 0xce,
+ 0x0e, 0xc6, 0x27, 0x1a, 0x77, 0xde, 0xfb, 0xc7,
+ 0xd4, 0xae, 0xfa, 0x3c, 0x79, 0x1e, 0x56, 0x57,
+ 0x7a, 0x32, 0x7d, 0xb7, 0x05, 0xbf, 0x09, 0xed,
+ 0xc4, 0x79, 0x6e, 0xbf, 0x6a, 0xdd, 0xe1, 0x75,
+ 0x39, 0xc7, 0xdd, 0xf1, 0xda, 0x8f, 0xef, 0x18,
+ 0x0b, 0xb3, 0x47, 0xaa, 0x1e, 0xf5, 0xc7, 0x87,
+ 0xb7, 0xed, 0x84, 0x6e, 0x98, 0x25, 0xf8, 0xcd,
+ 0xa2, 0x24, 0xf9, 0x91, 0xe6, 0xf8, 0xca, 0x95,
+ 0x4b, 0x3b, 0xc3, 0x17, 0x13, 0xd7, 0x5e, 0xde,
+ 0xd2, 0x83, 0xc1, 0xc4, 0x55, 0xd7, 0x4b, 0xd2,
+ 0xd3, 0xfb, 0xd4, 0xa9, 0xd5, 0x4f, 0xb8, 0x57,
+ 0x93, 0xca, 0x3a, 0x6e, 0x47, 0x6b, 0xfd, 0x79,
+ 0xc4, 0x5d, 0xdd, 0x56, 0x6f, 0xe0, 0xe9, 0xd4,
+ 0xe1, 0xd2, 0x9d, 0x0f, 0xc2, 0x9f, 0x46, 0xf3,
+ 0xf9, 0x00, 0xf1, 0x6b, 0xf8, 0xa1, 0xc0, 0xc6,
+ 0x31, 0x5a, 0x2f, 0x79, 0x32, 0xe2, 0x73, 0xf9,
+ 0xf1, 0x38, 0xf2, 0x33, 0x23, 0x43, 0xeb, 0x49,
+ 0xe2, 0x5e, 0xce, 0x8e, 0x55, 0x3e, 0xcc, 0xe1,
+ 0x3c, 0x5f, 0x3e, 0x95, 0x3f, 0x1d, 0x40, 0xde,
+ 0x5f, 0x72, 0xc2, 0x27, 0x99, 0x96, 0x1f, 0x59,
+ 0xb8, 0xb5, 0x47, 0xfa, 0x9b, 0x95, 0x6a, 0x21,
+ 0x64, 0x82, 0xea, 0xdd, 0x4c, 0xf6, 0xf1, 0x69,
+ 0xa6, 0xea, 0xf9, 0x8a, 0x44, 0x0b, 0xd9, 0xb2,
+ 0x13, 0x26, 0x16, 0xbd, 0x98, 0xb7, 0x4a, 0x7e,
+ 0x72, 0x32, 0x7a, 0x33, 0xaa, 0x40, 0xfe, 0x2a,
+ 0x9c, 0x7c, 0x18, 0x3a, 0x51, 0xfd, 0xcb, 0x02,
+ 0x78, 0xc7, 0x14, 0xf9, 0x9f, 0x60, 0x74, 0x21,
+ 0x2a, 0x59, 0xfd, 0x4f, 0x03, 0xf4, 0xad, 0x6b,
+ 0xf8, 0x76, 0x78, 0x2e, 0x7d, 0x4b, 0xc4, 0x12,
+ 0xf2, 0xd8, 0x6c, 0xa1, 0x0e, 0xcc, 0x8f, 0x52,
+ 0xfd, 0xf4, 0x27, 0xbe, 0x07, 0x87, 0x69, 0xfd,
+ 0xff, 0xc2, 0xd7, 0x67, 0xe6, 0x29, 0x3e, 0xfa,
+ 0x8d, 0xb0, 0xb0, 0xe3, 0xc0, 0x13, 0x78, 0x02,
+ 0xf1, 0x36, 0x77, 0xb9, 0x07, 0x7c, 0x29, 0xfd,
+ 0x4a, 0x54, 0x82, 0xfa, 0x8b, 0x7b, 0xa5, 0x2d,
+ 0xb8, 0x1d, 0x20, 0xaf, 0xcc, 0x0d, 0xf2, 0x66,
+ 0xce, 0x4f, 0xda, 0xdf, 0xa3, 0x29, 0x16, 0x16,
+ 0x36, 0x92, 0x3e, 0xbd, 0x83, 0x0e, 0x85, 0xd4,
+ 0x95, 0x7f, 0x79, 0x44, 0xde, 0x45, 0x4c, 0x63,
+ 0x5d, 0xe6, 0x10, 0xf7, 0x12, 0xd1, 0xfd, 0x7b,
+ 0xf8, 0xcf, 0xe8, 0x40, 0x2d, 0x3f, 0xf9, 0xc7,
+ 0x57, 0xaa, 0x2f, 0xdd, 0xdc, 0xe0, 0x21, 0x9c,
+ 0xff, 0x1f, 0x7f, 0x49, 0x7f, 0x82, 0xf0, 0x0d,
+ 0x65, 0xd7, 0xe8, 0xef, 0x23, 0xd9, 0x4f, 0x62,
+ 0xa7, 0x41, 0xf0, 0x60, 0xfc, 0x52, 0x6a, 0x6f,
+ 0xe5, 0xd7, 0x2c, 0xf2, 0x75, 0x4f, 0xc8, 0xef,
+ 0xf0, 0xd1, 0xac, 0xbb, 0xa9, 0x9b, 0xf4, 0xb2,
+ 0x2d, 0x71, 0xf7, 0xf6, 0x6b, 0x74, 0xc8, 0x24,
+ 0x12, 0x8f, 0x1f, 0x94, 0xd2, 0x7d, 0xb9, 0x92,
+ 0x47, 0xff, 0xed, 0x22, 0x3f, 0x90, 0x80, 0x1f,
+ 0x2d, 0x95, 0xa4, 0xf3, 0xf4, 0x20, 0xde, 0x7f,
+ 0xaa, 0xa4, 0x7c, 0x1a, 0x47, 0xbf, 0x56, 0x31,
+ 0x1a, 0x5f, 0x6f, 0x3e, 0x24, 0x9e, 0x9b, 0x7c,
+ 0xae, 0xfd, 0x46, 0x73, 0x6f, 0x0f, 0x0e, 0xa9,
+ 0x1f, 0xe8, 0x43, 0x3c, 0xcf, 0x3f, 0x5b, 0x0c,
+ 0x9e, 0xc5, 0xfa, 0x93, 0xbf, 0xd8, 0xa4, 0xfd,
+ 0x51, 0x87, 0x5b, 0x5e, 0x55, 0x3f, 0xf3, 0x2b,
+ 0xf1, 0x70, 0xa2, 0x42, 0x25, 0xf8, 0x0d, 0x9e,
+ 0x53, 0xc7, 0x5d, 0xf9, 0x3a, 0x8b, 0x75, 0x85,
+ 0xf6, 0x6c, 0x06, 0xbf, 0x14, 0x65, 0x21, 0x7f,
+ 0x34, 0x79, 0x6e, 0x96, 0x05, 0x59, 0xa8, 0x14,
+ 0xb9, 0x1e, 0x7e, 0x8f, 0xba, 0xd3, 0xad, 0x2e,
+ 0x7d, 0x87, 0x69, 0x43, 0xbf, 0xe6, 0x58, 0x5b,
+ 0x7a, 0x79, 0x09, 0xbd, 0xcd, 0xeb, 0xa6, 0x7e,
+ 0xba, 0x0c, 0x7a, 0x58, 0x78, 0x91, 0x75, 0x9b,
+ 0xb5, 0xc4, 0xc5, 0x17, 0x09, 0xd2, 0x53, 0xa7,
+ 0x51, 0x16, 0xce, 0xfd, 0xad, 0xf3, 0xdb, 0xc2,
+ 0xf7, 0x96, 0xb5, 0x56, 0xfe, 0xfd, 0x8a, 0xef,
+ 0x78, 0x72, 0xe1, 0x02, 0xdc, 0x03, 0x3f, 0xb2,
+ 0x74, 0xa7, 0xf2, 0x31, 0x8e, 0x3e, 0x65, 0xe6,
+ 0x3e, 0xad, 0x27, 0x8d, 0xfe, 0x69, 0x6c, 0x30,
+ 0x7d, 0x83, 0xe9, 0x91, 0x66, 0x61, 0x67, 0x92,
+ 0xf8, 0x62, 0xea, 0xce, 0x99, 0xfa, 0x8a, 0x5f,
+ 0x57, 0x7c, 0xc8, 0x67, 0xaf, 0xa5, 0x3f, 0xfb,
+ 0xf1, 0x6d, 0x09, 0x81, 0xf4, 0x2d, 0xe6, 0x2b,
+ 0x3e, 0xbf, 0xf5, 0x2b, 0x74, 0xd7, 0x14, 0xcc,
+ 0xb2, 0xb0, 0xc7, 0xb1, 0x13, 0x3c, 0x16, 0xfd,
+ 0x48, 0xbb, 0xa7, 0xbf, 0xaf, 0x64, 0x1d, 0x49,
+ 0x13, 0xa5, 0xa7, 0x87, 0x8f, 0x5a, 0xf8, 0x68,
+ 0x83, 0xfc, 0x44, 0x43, 0xde, 0x7b, 0xba, 0x01,
+ 0x71, 0x61, 0x4a, 0xe3, 0x63, 0xd2, 0xdb, 0xd2,
+ 0x07, 0x9a, 0x20, 0x7c, 0xfd, 0xa9, 0xa6, 0x8a,
+ 0xff, 0x23, 0xf8, 0xf1, 0xa8, 0x31, 0xaa, 0x2f,
+ 0x81, 0xd4, 0xeb, 0xf1, 0x23, 0x74, 0xde, 0xc5,
+ 0xf0, 0x59, 0xf5, 0xc7, 0xcb, 0xcf, 0xff, 0x81,
+ 0xbf, 0xf0, 0x7b, 0x20, 0xff, 0x79, 0xb1, 0x86,
+ 0x85, 0xb0, 0x8e, 0xba, 0xcf, 0xaa, 0xd4, 0xd9,
+ 0xfc, 0xf7, 0x14, 0xcf, 0x19, 0xe8, 0x46, 0x66,
+ 0x9c, 0xf4, 0x63, 0x25, 0xfb, 0xf5, 0xdb, 0x27,
+ 0x3f, 0x38, 0x95, 0xe7, 0x7f, 0x6b, 0xe4, 0xc7,
+ 0x0c, 0xf9, 0x55, 0xeb, 0x88, 0xfe, 0x3e, 0x08,
+ 0xbd, 0x58, 0x55, 0x5c, 0xdf, 0xcf, 0xe2, 0x9c,
+ 0xab, 0x24, 0xc9, 0x2f, 0x9c, 0x23, 0xaf, 0xdf,
+ 0x0c, 0xd0, 0x7d, 0xdd, 0xb8, 0x6c, 0x21, 0xbb,
+ 0x09, 0xbe, 0xcc, 0x9c, 0xe1, 0xbe, 0xba, 0x79,
+ 0xcf, 0x86, 0x6f, 0xa2, 0x7e, 0x7f, 0xbf, 0x41,
+ 0xfd, 0xc6, 0x71, 0x74, 0xcc, 0xa1, 0xbd, 0xfc,
+ 0xf1, 0x5b, 0xfc, 0xfd, 0xa9, 0xdf, 0xd1, 0x4d,
+ 0xd3, 0x90, 0x3c, 0xcf, 0x3e, 0xfa, 0x1f, 0xb8,
+ 0x0b, 0xba, 0xef, 0x70, 0x5a, 0xf9, 0xd2, 0x85,
+ 0xe7, 0x34, 0xbd, 0x80, 0x6f, 0x33, 0xf7, 0xd1,
+ 0x51, 0x97, 0x4e, 0xf2, 0xdf, 0x8e, 0xd4, 0x83,
+ 0x79, 0xa3, 0x94, 0xff, 0x41, 0xf8, 0xe7, 0x88,
+ 0xad, 0xea, 0x3f, 0x2b, 0x87, 0x58, 0x08, 0x4e,
+ 0x96, 0x5f, 0x58, 0x37, 0xda, 0xc2, 0xe8, 0xfe,
+ 0xf2, 0x73, 0xaf, 0xf6, 0x5b, 0x98, 0xb8, 0x9e,
+ 0x3a, 0x6d, 0x66, 0x94, 0x60, 0x5b, 0x23, 0xd4,
+ 0x0f, 0x4d, 0x40, 0x37, 0x47, 0x5d, 0x95, 0x1f,
+ 0xd9, 0x4a, 0x7e, 0x7b, 0xcf, 0x96, 0xff, 0x89,
+ 0x27, 0x1e, 0x7a, 0xdc, 0x93, 0x9f, 0x48, 0xe7,
+ 0xbc, 0x27, 0x9c, 0x52, 0xbf, 0xd2, 0x0b, 0xbd,
+ 0xff, 0xfd, 0x03, 0xe5, 0x77, 0xdb, 0x15, 0x16,
+ 0x26, 0x67, 0x68, 0xbe, 0xb3, 0x0d, 0x7f, 0xd5,
+ 0x67, 0x90, 0xd6, 0x3f, 0x85, 0x78, 0xbc, 0xf3,
+ 0x50, 0x7e, 0x79, 0x3a, 0xfe, 0xf8, 0xd3, 0xb9,
+ 0xf2, 0xc7, 0x6f, 0x59, 0xf7, 0x8c, 0x1d, 0xf2,
+ 0xaf, 0x77, 0xef, 0xb2, 0xed, 0xc6, 0xf8, 0x0e,
+ 0xf3, 0x9a, 0xb8, 0x78, 0xd3, 0x4f, 0x7e, 0xf9,
+ 0x08, 0xf5, 0x63, 0xf5, 0x01, 0xf9, 0xed, 0x50,
+ 0xee, 0xf3, 0x97, 0xb5, 0xd2, 0x17, 0x67, 0xee,
+ 0xeb, 0x76, 0x9c, 0xe2, 0x6b, 0x21, 0xfa, 0x38,
+ 0xbd, 0x9f, 0xf4, 0xcf, 0x9f, 0xf9, 0x90, 0x57,
+ 0x34, 0xbe, 0xcb, 0x1c, 0x40, 0x0f, 0x5e, 0xc4,
+ 0x73, 0xef, 0x26, 0x82, 0x7d, 0x2f, 0x2c, 0x62,
+ 0x2e, 0x60, 0xc2, 0xe8, 0x2b, 0x4e, 0x6c, 0x50,
+ 0x3f, 0x1a, 0x80, 0x5f, 0xdc, 0xec, 0x7c, 0x1a,
+ 0xde, 0x9e, 0x7b, 0x76, 0xac, 0xa0, 0x79, 0xdc,
+ 0x13, 0xe6, 0x34, 0x49, 0x15, 0x39, 0x07, 0xa3,
+ 0xb1, 0xd4, 0xc4, 0xc5, 0x35, 0xf8, 0x0f, 0x57,
+ 0xce, 0xeb, 0x9e, 0x97, 0xfc, 0xf2, 0x63, 0xfc,
+ 0xad, 0xc3, 0xec, 0x46, 0xf0, 0xde, 0xcc, 0x7f,
+ 0x1e, 0x7e, 0x2f, 0xbd, 0x37, 0xe8, 0x4c, 0x85,
+ 0xce, 0xea, 0x4f, 0xbe, 0xed, 0xc3, 0xb6, 0xa7,
+ 0x69, 0xbd, 0xe9, 0xd4, 0xcf, 0x95, 0x09, 0xe8,
+ 0x96, 0xe9, 0xcb, 0xba, 0x03, 0x72, 0x75, 0x1e,
+ 0x6e, 0xec, 0x7f, 0x7d, 0x92, 0xe6, 0x5b, 0xdf,
+ 0x71, 0x2e, 0x59, 0x99, 0xaa, 0x57, 0x2b, 0xd0,
+ 0xdb, 0x32, 0x79, 0xca, 0x87, 0x20, 0xe2, 0xa2,
+ 0x58, 0x96, 0xf2, 0xf9, 0x1c, 0x7a, 0x9a, 0x5a,
+ 0xa0, 0xfe, 0x69, 0x1b, 0x7d, 0xb6, 0x73, 0x91,
+ 0xf2, 0xed, 0x20, 0xf3, 0x88, 0xd5, 0xdd, 0x88,
+ 0x4b, 0xf3, 0x0b, 0x71, 0x1a, 0x5f, 0x4c, 0xe7,
+ 0x51, 0x8e, 0xf5, 0x9f, 0xdf, 0xdc, 0x41, 0xef,
+ 0x63, 0x1d, 0x0e, 0xef, 0x7e, 0xa9, 0xed, 0x11,
+ 0x4f, 0x79, 0x4b, 0x55, 0xaf, 0x0b, 0xb8, 0xcf,
+ 0x0b, 0x6f, 0x14, 0x4f, 0xcf, 0xa8, 0x47, 0x5f,
+ 0x3f, 0x25, 0x8e, 0x8c, 0xf7, 0x57, 0x16, 0x8e,
+ 0xa5, 0xc8, 0x2f, 0xb9, 0xa2, 0x73, 0xdf, 0xc5,
+ 0xc8, 0x3f, 0xd4, 0x44, 0xa7, 0x3d, 0x63, 0xa4,
+ 0x37, 0x4d, 0x88, 0x9f, 0x44, 0x07, 0xe9, 0xb5,
+ 0x23, 0xf7, 0x75, 0x7c, 0x97, 0xfa, 0x8f, 0x7d,
+ 0xcc, 0x9d, 0x46, 0x76, 0xd1, 0x7c, 0xec, 0x14,
+ 0x79, 0xb3, 0x60, 0x9c, 0xf4, 0xc2, 0x83, 0xf5,
+ 0x86, 0x87, 0xa2, 0x33, 0x66, 0x39, 0xf5, 0x24,
+ 0xb7, 0xb5, 0xfc, 0xee, 0x3b, 0xf4, 0xd1, 0x83,
+ 0x26, 0x2b, 0x3e, 0xe7, 0xa3, 0x3b, 0xee, 0xb3,
+ 0xe4, 0x77, 0x1a, 0xa1, 0x2b, 0x35, 0x27, 0xa9,
+ 0x5f, 0xaf, 0x86, 0x7f, 0x70, 0xe8, 0xf2, 0x31,
+ 0xbc, 0x14, 0xf5, 0xc3, 0xb9, 0x03, 0xcf, 0x35,
+ 0x97, 0xd0, 0xf1, 0xd7, 0x57, 0x34, 0x6f, 0xfd,
+ 0x16, 0x7f, 0xe1, 0x9a, 0xaa, 0xf8, 0x38, 0x4c,
+ 0xfc, 0x6c, 0xc8, 0xe2, 0x7b, 0x66, 0x33, 0xfd,
+ 0xfc, 0x75, 0x4f, 0xad, 0x3f, 0x9d, 0xfd, 0x65,
+ 0x24, 0xca, 0x5f, 0xfd, 0x40, 0x1d, 0xcd, 0xb8,
+ 0xa5, 0xf9, 0xe1, 0x0a, 0xea, 0x67, 0x2f, 0x37,
+ 0xc5, 0x4b, 0x4b, 0xf2, 0x31, 0xe8, 0xa6, 0xe6,
+ 0x0b, 0xe7, 0xd1, 0xbd, 0x81, 0xc1, 0xca, 0xc7,
+ 0x73, 0xd4, 0xb3, 0xb1, 0xf9, 0xba, 0xcf, 0x16,
+ 0xcc, 0xbd, 0xfe, 0x2c, 0xfa, 0xa7, 0x9e, 0x50,
+ 0x1f, 0x52, 0x2f, 0x0e, 0x80, 0x3f, 0x47, 0x17,
+ 0xf3, 0x23, 0xa4, 0xe7, 0xb9, 0xf8, 0xcd, 0xca,
+ 0xae, 0xf2, 0xff, 0x0f, 0xa8, 0x33, 0x79, 0xfd,
+ 0x55, 0x6f, 0x6a, 0xf0, 0xb9, 0x13, 0x0e, 0xc4,
+ 0x91, 0xb9, 0x46, 0x5c, 0xc6, 0xcf, 0x51, 0x3f,
+ 0xff, 0x27, 0xf3, 0x92, 0xcd, 0x67, 0xb4, 0xdf,
+ 0xde, 0xf4, 0x3b, 0x33, 0xdc, 0xe5, 0x1f, 0xbe,
+ 0xe1, 0x5c, 0xab, 0xa7, 0xab, 0xbe, 0xf7, 0xa7,
+ 0xbe, 0xba, 0x37, 0x51, 0xfe, 0xde, 0xa7, 0x0e,
+ 0xdd, 0x6d, 0xa4, 0xfb, 0x0c, 0xc1, 0xb7, 0xd7,
+ 0xb9, 0xa1, 0xf9, 0xac, 0x1f, 0x7e, 0xa4, 0xdd,
+ 0x29, 0xcd, 0xcf, 0xa7, 0xe2, 0xef, 0xdf, 0x0f,
+ 0xce, 0x83, 0xc7, 0x26, 0x5b, 0x48, 0xd0, 0xdc,
+ 0xd6, 0x14, 0xa1, 0x2f, 0x61, 0x55, 0xb5, 0xfe,
+ 0xcf, 0x30, 0xce, 0x8f, 0x73, 0xf4, 0xbc, 0x21,
+ 0xc4, 0x5f, 0xf7, 0xe2, 0xf2, 0x27, 0x25, 0x99,
+ 0x1b, 0xf9, 0x0c, 0xda, 0x0a, 0x6f, 0x84, 0xdf,
+ 0xa8, 0xd7, 0xfd, 0x6b, 0x78, 0x39, 0xb8, 0x73,
+ 0x34, 0x3a, 0x6f, 0x32, 0xd0, 0x95, 0xbb, 0x2e,
+ 0x8a, 0xd7, 0x24, 0xf6, 0xeb, 0x51, 0x0f, 0x5f,
+ 0x6b, 0xc6, 0x72, 0x9f, 0x6d, 0x07, 0x4b, 0xef,
+ 0xdb, 0x3d, 0xb5, 0x70, 0xe8, 0xa5, 0xfa, 0xb9,
+ 0x01, 0xdc, 0x5f, 0xc3, 0x1d, 0xea, 0x97, 0xe3,
+ 0x39, 0x87, 0xfd, 0x27, 0x75, 0x7f, 0xfe, 0xf8,
+ 0xb8, 0xc2, 0x36, 0x3a, 0xcf, 0x67, 0xf0, 0x13,
+ 0x17, 0xd1, 0x5d, 0xe3, 0xca, 0xdc, 0x7f, 0x9d,
+ 0xaf, 0xea, 0xc7, 0x24, 0xf6, 0x57, 0xf1, 0xa5,
+ 0xfa, 0xab, 0xd2, 0xdc, 0xf7, 0xe5, 0x89, 0xea,
+ 0x7f, 0x8b, 0x93, 0x9f, 0x23, 0x0a, 0xe5, 0x5f,
+ 0x6b, 0x31, 0xaf, 0x2a, 0x93, 0x26, 0xfd, 0xd9,
+ 0x46, 0xfc, 0xf4, 0x4e, 0x67, 0x4e, 0x68, 0x9a,
+ 0xe3, 0xeb, 0xf7, 0x6e, 0x95, 0xbf, 0x0e, 0x20,
+ 0xee, 0x8a, 0xe5, 0xa8, 0x3f, 0x8b, 0x21, 0xcf,
+ 0x9c, 0x53, 0x3c, 0xf5, 0x3c, 0xe2, 0xc2, 0x39,
+ 0x5f, 0xf1, 0xeb, 0xc3, 0x5c, 0x78, 0xf8, 0x73,
+ 0x7d, 0x5e, 0xf3, 0xbe, 0xd5, 0x3f, 0xc8, 0x6f,
+ 0xcc, 0xc3, 0xb7, 0x7e, 0x59, 0x4e, 0xf7, 0x3d,
+ 0x08, 0x5f, 0x7b, 0xbe, 0xa5, 0xfe, 0x1e, 0x8e,
+ 0x1f, 0xf2, 0xcb, 0x57, 0x7e, 0xad, 0xe1, 0x9c,
+ 0xe6, 0xde, 0x90, 0x7e, 0xdd, 0xc1, 0x2f, 0x94,
+ 0x3f, 0x31, 0x0f, 0xbe, 0x82, 0xba, 0x59, 0xb0,
+ 0x5c, 0xf7, 0x77, 0x97, 0x39, 0xf2, 0x9a, 0xbd,
+ 0xf4, 0x75, 0xa6, 0x3f, 0x3a, 0xe4, 0xbc, 0x4b,
+ 0xbf, 0xff, 0x6c, 0xc3, 0x47, 0xbe, 0xcc, 0xd5,
+ 0x7d, 0x0e, 0x8d, 0xb2, 0x70, 0x69, 0x91, 0xf2,
+ 0xe3, 0x47, 0xce, 0x75, 0x6f, 0x8e, 0xe2, 0xeb,
+ 0x32, 0x7a, 0xdc, 0xb7, 0x73, 0x75, 0xf8, 0x61,
+ 0xea, 0xfa, 0xeb, 0x71, 0xf2, 0x07, 0xc3, 0x39,
+ 0x37, 0xa7, 0x8d, 0xf2, 0x13, 0x43, 0xc9, 0xdb,
+ 0x86, 0x6e, 0xaa, 0xaf, 0x5b, 0xc8, 0x97, 0x6b,
+ 0x4e, 0xe8, 0xa8, 0xd9, 0x84, 0x4e, 0x17, 0x2d,
+ 0x52, 0x7d, 0x4d, 0xa1, 0x2f, 0xac, 0x3c, 0x5e,
+ 0xfa, 0xe4, 0x43, 0xdc, 0x0d, 0xe8, 0xcd, 0xdc,
+ 0xc6, 0xac, 0xfa, 0xc4, 0x42, 0xf3, 0xdd, 0xf8,
+ 0x04, 0x33, 0xf2, 0x5d, 0x0b, 0x5e, 0xcd, 0xb9,
+ 0x47, 0x53, 0x9b, 0x79, 0x9f, 0xbf, 0x3f, 0xbe,
+ 0xd1, 0xfc, 0x8e, 0x5e, 0x4e, 0xb9, 0x2c, 0x7d,
+ 0x0b, 0x60, 0x6e, 0x34, 0x39, 0x44, 0xe7, 0x3b,
+ 0x01, 0xff, 0x91, 0x1c, 0xa9, 0xfe, 0xaa, 0x37,
+ 0xf3, 0xc8, 0x9a, 0x1f, 0x77, 0x83, 0x8f, 0xe1,
+ 0xbe, 0xeb, 0x07, 0x3f, 0xd5, 0xe7, 0x59, 0x87,
+ 0xe7, 0x43, 0xbd, 0xbf, 0x2e, 0xba, 0x93, 0xbe,
+ 0x4b, 0xf3, 0xdc, 0x8f, 0xd0, 0xf1, 0xa8, 0x96,
+ 0xaa, 0xe7, 0x2f, 0x4e, 0x5a, 0xc8, 0x39, 0xae,
+ 0xfb, 0x08, 0x66, 0x7f, 0x6b, 0xee, 0x6b, 0x1e,
+ 0x75, 0x91, 0x38, 0xac, 0x1a, 0xa9, 0x7c, 0xf9,
+ 0x91, 0xf7, 0xbd, 0x9f, 0xa5, 0x7e, 0x72, 0x1c,
+ 0xbe, 0xb6, 0x67, 0xa2, 0x7e, 0x4f, 0xe8, 0xc3,
+ 0x3d, 0x9f, 0xbb, 0xa2, 0xfe, 0x3d, 0xe2, 0x8a,
+ 0x85, 0x49, 0x19, 0xfa, 0xbd, 0xc2, 0x83, 0xf3,
+ 0xef, 0x79, 0x02, 0x5d, 0x31, 0xb7, 0xd1, 0xfb,
+ 0x69, 0x9e, 0xe8, 0x86, 0x39, 0x44, 0x9d, 0x1a,
+ 0xfa, 0x19, 0xbe, 0xd8, 0x34, 0xa7, 0x5f, 0x76,
+ 0xae, 0xa3, 0x79, 0xfc, 0x34, 0xe6, 0x45, 0x6b,
+ 0xca, 0xe8, 0xfd, 0xad, 0xa8, 0xa7, 0xf9, 0x0d,
+ 0x55, 0xaf, 0xd2, 0x88, 0x93, 0x80, 0x41, 0xf8,
+ 0x38, 0x93, 0x4a, 0x5c, 0x3a, 0x46, 0xab, 0xbe,
+ 0x5d, 0x26, 0xae, 0xf6, 0x6e, 0x51, 0x3d, 0xcc,
+ 0x45, 0x2f, 0x3b, 0x9c, 0x96, 0x1f, 0xff, 0x84,
+ 0x3c, 0x48, 0x8e, 0x53, 0xbf, 0x36, 0x18, 0xbd,
+ 0x75, 0x7c, 0xa4, 0xfe, 0x29, 0x0b, 0x3d, 0x3e,
+ 0xea, 0x2a, 0xbf, 0xe7, 0x17, 0x63, 0xa1, 0x4e,
+ 0x49, 0xf9, 0xfb, 0xa5, 0xf8, 0xb2, 0x93, 0x51,
+ 0xba, 0x9f, 0xe5, 0xcc, 0x71, 0xf7, 0xd6, 0x53,
+ 0xbf, 0x57, 0x89, 0x7c, 0x89, 0x9a, 0x2b, 0xbf,
+ 0xd4, 0x8c, 0xdf, 0x95, 0x32, 0x26, 0xab, 0xbe,
+ 0x5e, 0xe6, 0xf7, 0x84, 0xa0, 0x62, 0x9a, 0xbf,
+ 0x5d, 0x21, 0xce, 0x06, 0x2d, 0x57, 0xbe, 0xbe,
+ 0x26, 0xbf, 0x5c, 0x6b, 0x68, 0x7d, 0x5d, 0x38,
+ 0xd7, 0x93, 0xa5, 0xd5, 0xcf, 0x4c, 0xa0, 0xff,
+ 0x7e, 0xbb, 0x4e, 0xf5, 0x38, 0x0d, 0x1f, 0xd2,
+ 0xdb, 0x51, 0x7a, 0xb5, 0x17, 0xbd, 0x8e, 0xbd,
+ 0xad, 0x79, 0xee, 0x02, 0x74, 0xae, 0x54, 0x36,
+ 0x73, 0xfd, 0x7f, 0xe6, 0x5d, 0x57, 0xdd, 0xa4,
+ 0x37, 0x4e, 0xdc, 0x53, 0x8b, 0xca, 0x8a, 0xbf,
+ 0xad, 0xe8, 0xc8, 0xc0, 0xb0, 0x29, 0x3a, 0x9f,
+ 0xaa, 0x16, 0xda, 0xd5, 0x93, 0xbf, 0x7c, 0x0f,
+ 0xdd, 0xbb, 0xff, 0x48, 0xf5, 0xf0, 0x21, 0x73,
+ 0xd2, 0xa6, 0x2d, 0x74, 0x3e, 0xe3, 0xf1, 0x29,
+ 0x35, 0x36, 0xea, 0xf7, 0x03, 0x47, 0xe2, 0xbe,
+ 0x47, 0xa8, 0xfa, 0x8f, 0x19, 0xe8, 0xd8, 0xb5,
+ 0xce, 0xea, 0x1f, 0x63, 0x88, 0xa3, 0xc8, 0xdd,
+ 0xf8, 0x4c, 0x53, 0x88, 0x5e, 0x5e, 0x3f, 0x24,
+ 0x23, 0xe8, 0x83, 0xbe, 0x8d, 0xce, 0xd6, 0xfd,
+ 0x5c, 0xa7, 0xee, 0x54, 0xfc, 0x44, 0xfd, 0x54,
+ 0x32, 0xfd, 0xc8, 0x8a, 0xcd, 0xc4, 0x89, 0xd9,
+ 0x47, 0x9d, 0xed, 0xb4, 0x5d, 0xf5, 0x79, 0x3d,
+ 0xf9, 0x5b, 0xe5, 0xac, 0xf2, 0xa7, 0x16, 0xfb,
+ 0x88, 0xf5, 0xd1, 0xbc, 0xba, 0x17, 0x7d, 0xf4,
+ 0xc9, 0x07, 0x93, 0xe0, 0xaf, 0x99, 0xef, 0x6d,
+ 0x7c, 0x85, 0x4e, 0x9b, 0xe2, 0xcc, 0xbb, 0xdc,
+ 0xd7, 0xaa, 0xbe, 0x2e, 0xe6, 0xfe, 0x9e, 0x04,
+ 0x4b, 0xef, 0x7a, 0x90, 0x7f, 0x6f, 0x42, 0x54,
+ 0xbf, 0x5c, 0xc8, 0xcb, 0x5e, 0x53, 0x63, 0xe0,
+ 0xbe, 0xf8, 0xd2, 0x65, 0x71, 0xcc, 0x99, 0x4c,
+ 0x13, 0xf6, 0x73, 0xeb, 0x37, 0xcd, 0xf3, 0x5d,
+ 0xf0, 0x93, 0x75, 0x3d, 0xb4, 0x9e, 0xd3, 0xe8,
+ 0xe4, 0x30, 0x77, 0xfd, 0x9e, 0xbb, 0x88, 0x3a,
+ 0xba, 0x33, 0x9f, 0xdf, 0x3d, 0xcc, 0x38, 0x7e,
+ 0xf7, 0xed, 0x96, 0xa5, 0xfd, 0x76, 0x27, 0xae,
+ 0x3e, 0x88, 0x55, 0xff, 0x3b, 0x97, 0xfd, 0xe6,
+ 0x95, 0xd7, 0xbc, 0xad, 0x23, 0xf7, 0xb1, 0xdd,
+ 0x5b, 0xf3, 0xc5, 0x68, 0xe6, 0xe4, 0x0b, 0x02,
+ 0x0e, 0xc1, 0x27, 0x70, 0xdf, 0x0b, 0x9c, 0xd4,
+ 0x0f, 0x37, 0xa6, 0xbe, 0x4e, 0xee, 0xaa, 0x7a,
+ 0xb8, 0x19, 0xdd, 0x48, 0xed, 0xae, 0xfa, 0x1a,
+ 0x46, 0x3f, 0x7f, 0x7f, 0x8a, 0xea, 0xfb, 0x7e,
+ 0xf2, 0x3b, 0x32, 0x41, 0xfe, 0xa2, 0x0d, 0x3a,
+ 0xec, 0x18, 0x31, 0x4b, 0xfb, 0xe3, 0x7d, 0xa1,
+ 0x09, 0xca, 0xdf, 0x3b, 0xbe, 0xff, 0x03, 0x4e,
+ 0x9a, 0x03, 0x4d
+};
+
+#endif
diff --git a/test/common_plat/validation/api/cpumask/.gitignore b/test/validation/api/cpumask/.gitignore
index 655a1640f..655a1640f 100644
--- a/test/common_plat/validation/api/cpumask/.gitignore
+++ b/test/validation/api/cpumask/.gitignore
diff --git a/test/validation/api/cpumask/Makefile.am b/test/validation/api/cpumask/Makefile.am
new file mode 100644
index 000000000..3872c1bd4
--- /dev/null
+++ b/test/validation/api/cpumask/Makefile.am
@@ -0,0 +1,5 @@
+include ../Makefile.inc
+
+test_PROGRAMS = cpumask_main
+cpumask_main_SOURCES = cpumask.c
+LDADD += $(LIBCPUMASK_COMMON)
diff --git a/test/validation/api/cpumask/cpumask.c b/test/validation/api/cpumask/cpumask.c
new file mode 100644
index 000000000..9ca182fc9
--- /dev/null
+++ b/test/validation/api/cpumask/cpumask.c
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <odp_api.h>
+
+#include "odp_cunit_common.h"
+#include "mask_common.h"
+
+static int cpumask_max_count(void)
+{
+ odp_cpumask_t mask;
+
+ odp_cpumask_setall(&mask);
+
+ return odp_cpumask_count(&mask);
+}
+
+static void cpumask_test_odp_cpumask_def_control(void)
+{
+ odp_cpumask_t mask;
+ int num, count, all;
+ int max = cpumask_max_count();
+ int request = 7;
+
+ CU_ASSERT_FATAL(max > 1);
+
+ if (request > max)
+ request = max - 1;
+
+ all = odp_cpumask_default_control(&mask, 0);
+ num = all;
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num == count);
+ CU_ASSERT(num <= max);
+
+ num = odp_cpumask_default_control(&mask, max);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num == count);
+ CU_ASSERT(num <= max);
+ CU_ASSERT(num == all);
+
+ num = odp_cpumask_default_control(&mask, 1);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num == 1);
+ CU_ASSERT(num == count);
+
+ num = odp_cpumask_default_control(&mask, request);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num <= request);
+ CU_ASSERT(num == count);
+
+ CU_ASSERT(odp_cpumask_default_control(NULL, request) == num);
+ CU_ASSERT(odp_cpumask_default_control(NULL, 0) == all);
+ CU_ASSERT(odp_cpumask_default_control(NULL, 1) == 1);
+}
+
+static void cpumask_test_odp_cpumask_def_worker(void)
+{
+ odp_cpumask_t mask;
+ int num, count, all;
+ int max = cpumask_max_count();
+ int request = 7;
+
+ CU_ASSERT_FATAL(max > 1);
+
+ if (request > max)
+ request = max - 1;
+
+ all = odp_cpumask_default_worker(&mask, 0);
+ num = all;
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num == count);
+ CU_ASSERT(num <= max);
+
+ num = odp_cpumask_default_worker(&mask, max);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num == count);
+ CU_ASSERT(num <= max);
+ CU_ASSERT(num == all);
+
+ num = odp_cpumask_default_worker(&mask, 1);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num == 1);
+ CU_ASSERT(num == count);
+
+ num = odp_cpumask_default_worker(&mask, request);
+ count = odp_cpumask_count(&mask);
+
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num <= request);
+ CU_ASSERT(num == count);
+
+ CU_ASSERT(odp_cpumask_default_worker(NULL, request) == num);
+ CU_ASSERT(odp_cpumask_default_worker(NULL, 0) == all);
+ CU_ASSERT(odp_cpumask_default_worker(NULL, 1) == 1);
+}
+
+static void cpumask_test_odp_cpumask_def(void)
+{
+ odp_cpumask_t mask, all_mask, overlap;
+ int count, all, num_worker, num_control, request;
+ int max = cpumask_max_count();
+ int cpu_count = odp_cpu_count();
+
+ all = odp_cpumask_all_available(&all_mask);
+ count = odp_cpumask_count(&all_mask);
+
+ CU_ASSERT_FATAL(cpu_count > 0);
+ CU_ASSERT_FATAL(all > 0);
+ CU_ASSERT(all == cpu_count);
+ CU_ASSERT(all <= max);
+ CU_ASSERT(all == count);
+
+ request = all - 1;
+ if (request == 0)
+ request = 1;
+
+ num_worker = odp_cpumask_default_worker(&mask, request);
+ count = odp_cpumask_count(&mask);
+ CU_ASSERT(num_worker > 0);
+ CU_ASSERT(num_worker <= request);
+ CU_ASSERT(num_worker == count);
+
+ /* Check that CPUs are in the all CPUs mask */
+ odp_cpumask_zero(&overlap);
+ odp_cpumask_and(&overlap, &mask, &all_mask);
+ CU_ASSERT(odp_cpumask_count(&overlap) == num_worker);
+
+ num_control = odp_cpumask_default_control(&mask, 1);
+ count = odp_cpumask_count(&mask);
+ CU_ASSERT(num_control == 1);
+ CU_ASSERT(num_control == count);
+
+ odp_cpumask_zero(&overlap);
+ odp_cpumask_and(&overlap, &mask, &all_mask);
+ CU_ASSERT(odp_cpumask_count(&overlap) == num_control);
+
+ CU_ASSERT(odp_cpumask_default_worker(NULL, request) == num_worker);
+ CU_ASSERT(odp_cpumask_default_worker(NULL, 0) <= all);
+ CU_ASSERT(odp_cpumask_default_control(NULL, 0) <= all);
+}
+
+odp_testinfo_t cpumask_suite[] = {
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_to_from_str),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_equal),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_zero),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_set),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_clr),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_isset),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_count),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_and),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_or),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_xor),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_copy),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_first),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_last),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_next),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_setall),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_def_control),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_def_worker),
+ ODP_TEST_INFO(cpumask_test_odp_cpumask_def),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t cpumask_suites[] = {
+ {"Cpumask", NULL, NULL, cpumask_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(cpumask_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/crypto/.gitignore b/test/validation/api/crypto/.gitignore
index 0ac55e35e..0ac55e35e 100644
--- a/test/common_plat/validation/api/crypto/.gitignore
+++ b/test/validation/api/crypto/.gitignore
diff --git a/test/validation/api/crypto/Makefile.am b/test/validation/api/crypto/Makefile.am
new file mode 100644
index 000000000..ead21a336
--- /dev/null
+++ b/test/validation/api/crypto/Makefile.am
@@ -0,0 +1,14 @@
+include ../Makefile.inc
+
+test_PROGRAMS = crypto_main
+crypto_main_SOURCES = \
+ odp_crypto_test_inp.c \
+ crypto_op_test.c \
+ crypto_op_test.h \
+ test_vectors.h \
+ test_vectors_len.h \
+ test_vector_defs.h \
+ util.h \
+ util.c
+
+PRELDADD += $(LIBPACKET_COMMON)
diff --git a/test/validation/api/crypto/crypto_op_test.c b/test/validation/api/crypto/crypto_op_test.c
new file mode 100644
index 000000000..f2703c5cc
--- /dev/null
+++ b/test/validation/api/crypto/crypto_op_test.c
@@ -0,0 +1,616 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2024 Nokia
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <packet_common.h>
+#include "crypto_op_test.h"
+#include "util.h"
+
+#define MAX_FAILURE_PRINTS 20
+
+#define MAX_IGNORED_RANGES 3
+
+/*
+ * Output packet parts that we ignore since they have undefined values
+ */
+typedef struct ignore_t {
+ uint32_t byte_offset; /* offset to a byte which has bits to be ignored */
+ uint8_t byte_mask; /* mask of ignored bits in the byte */
+ struct {
+ uint32_t offset;
+ uint32_t length;
+ } ranges[MAX_IGNORED_RANGES]; /* byte ranges to be ignored */
+ uint32_t num_ranges;
+} ignore_t;
+
+/* Add room for bytes that are not included in ref->length */
+#define MAX_EXP_DATA_LEN (MAX_DATA_LEN + 200)
+
+/*
+ * Expected packet data
+ */
+typedef struct expected_t {
+ uint8_t data[MAX_EXP_DATA_LEN];
+ uint32_t len;
+ ignore_t ignore;
+} expected_t;
+
+int crypto_op(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ odp_bool_t *ok,
+ const odp_crypto_packet_op_param_t *op_params,
+ odp_crypto_op_type_t session_op_type,
+ odp_crypto_op_type_t op_type)
+{
+ int rc;
+ odp_event_t event, event2;
+ odp_crypto_packet_result_t result;
+ odp_event_subtype_t subtype;
+ odp_packet_t orig_pkt_out;
+
+ if (op_type == ODP_CRYPTO_OP_TYPE_LEGACY)
+ *pkt_out = pkt_in;
+ else if (op_type == ODP_CRYPTO_OP_TYPE_BASIC)
+ *pkt_out = ODP_PACKET_INVALID;
+ orig_pkt_out = *pkt_out;
+
+ if (suite_context.op_mode == ODP_CRYPTO_SYNC) {
+ rc = odp_crypto_op(&pkt_in, pkt_out, op_params, 1);
+ if (rc <= 0) {
+ CU_FAIL("Failed odp_crypto_packet_op()");
+ goto fail;
+ }
+ } else {
+ odp_packet_t *out_param = pkt_out;
+
+ if (session_op_type == ODP_CRYPTO_OP_TYPE_BASIC)
+ out_param = NULL;
+
+ rc = odp_crypto_op_enq(&pkt_in, out_param, op_params, 1);
+ if (rc <= 0) {
+ CU_FAIL("Failed odp_crypto_op_enq()");
+ goto fail;
+ }
+
+ /* Get crypto completion event from compl_queue. */
+ CU_ASSERT_FATAL(NULL != suite_context.compl_queue_deq);
+ do {
+ event = suite_context.compl_queue_deq();
+ } while (event == ODP_EVENT_INVALID);
+
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event));
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == subtype);
+
+ *pkt_out = odp_crypto_packet_from_event(event);
+ }
+
+ if (op_type != ODP_CRYPTO_OP_TYPE_BASIC)
+ CU_ASSERT(*pkt_out == orig_pkt_out);
+
+ event = odp_packet_to_event(*pkt_out);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event));
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == subtype);
+ CU_ASSERT(odp_packet_subtype(*pkt_out) == ODP_EVENT_PACKET_CRYPTO);
+
+ event2 = odp_crypto_packet_to_event(*pkt_out);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(event2));
+ CU_ASSERT(ODP_EVENT_PACKET_CRYPTO == odp_event_subtype(event2));
+ CU_ASSERT(odp_event_to_u64(event) == odp_event_to_u64(event2));
+
+ rc = odp_crypto_result(&result, *pkt_out);
+ if (rc < -1)
+ CU_FAIL("Failed odp_crypto_result()");
+ CU_ASSERT(rc == 0 || rc == -1);
+
+ if (op_type == ODP_CRYPTO_OP_TYPE_OOP &&
+ suite_context.op_mode == ODP_CRYPTO_ASYNC)
+ CU_ASSERT(result.pkt_in == pkt_in);
+
+ *ok = (rc == 0);
+
+ return 0;
+fail:
+ odp_packet_free(pkt_in);
+ if (op_type == ODP_CRYPTO_OP_TYPE_OOP)
+ odp_packet_free(*pkt_out);
+ return -1;
+}
+
+/*
+ * Try to adjust packet so that the first segment holds 'first_seg_len' bytes
+ * of packet data (+ tailroom if first_seg_len is longer than the packet).
+ *
+ * If 'first_seg_len' is zero, do not try to add segments but make headroom
+ * zero.
+ *
+ * Packet data bytes are not preserved.
+ */
+static void adjust_segments(odp_packet_t *pkt, uint32_t first_seg_len)
+{
+ uint32_t shift;
+
+ shift = odp_packet_headroom(*pkt) + first_seg_len;
+
+ if (odp_packet_extend_head(pkt, shift, NULL, NULL) < 0) {
+ CU_FAIL("odp_packet_extend_head() failed\n");
+ return;
+ }
+ if (odp_packet_trunc_tail(pkt, shift, NULL, NULL) < 0) {
+ CU_FAIL("odp_packet_trunc_tail() failed\n");
+ return;
+ }
+ /*
+ * ODP API does not seem to guarantee that we ever have a multi-segment
+ * packet at this point, but we can print a message about it.
+ */
+ if (first_seg_len == 1 &&
+ first_seg_len != odp_packet_seg_len(*pkt))
+ printf("Could not create a segmented packet for testing.\n");
+}
+
+static void write_header_and_trailer(odp_packet_t pkt,
+ uint32_t header_len, uint32_t trailer_len)
+{
+ uint32_t trailer_offset = odp_packet_len(pkt) - trailer_len;
+ uint32_t max_len = header_len > trailer_len ? header_len : trailer_len;
+
+ if (!max_len)
+ return;
+
+ uint8_t buffer[max_len];
+ int rc;
+
+ fill_with_pattern(buffer, sizeof(buffer));
+
+ rc = odp_packet_copy_from_mem(pkt, 0, header_len, buffer);
+ CU_ASSERT(rc == 0);
+ rc = odp_packet_copy_from_mem(pkt, trailer_offset, trailer_len, buffer);
+ CU_ASSERT(rc == 0);
+}
+
+static void prepare_crypto_ranges(const crypto_op_test_param_t *param,
+ odp_packet_data_range_t *cipher_range,
+ odp_packet_data_range_t *auth_range)
+{
+ uint32_t c_scale = param->session.cipher_range_in_bits ? 8 : 1;
+ uint32_t a_scale = param->session.auth_range_in_bits ? 8 : 1;
+
+ *cipher_range = param->cipher_range;
+ *auth_range = param->auth_range;
+ cipher_range->offset += c_scale * param->header_len;
+ auth_range->offset += a_scale * param->header_len;
+}
+
+static int prepare_input_packet(const crypto_op_test_param_t *param,
+ odp_packet_t *pkt_in)
+{
+ crypto_test_reference_t *ref = param->ref;
+ uint32_t reflength = ref_length_in_bytes(ref);
+ odp_packet_t pkt;
+ uint32_t digest_offset = param->digest_offset;
+ uint32_t pkt_len;
+
+ pkt_len = param->header_len + reflength + param->trailer_len;
+ if (param->digest_offset == param->header_len + reflength)
+ pkt_len += ref->digest_length;
+ if (pkt_len == 0)
+ pkt_len = 1;
+
+ pkt = odp_packet_alloc(suite_context.pool, pkt_len);
+
+ CU_ASSERT(pkt != ODP_PACKET_INVALID);
+ if (pkt == ODP_PACKET_INVALID)
+ return -1;
+
+ if (param->adjust_segmentation)
+ adjust_segments(&pkt, param->first_seg_len);
+
+ write_header_and_trailer(pkt, param->header_len, param->trailer_len);
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ odp_packet_copy_from_mem(pkt, param->header_len,
+ reflength, ref->plaintext);
+ } else {
+ odp_packet_copy_from_mem(pkt, param->header_len,
+ reflength, ref->ciphertext);
+ odp_packet_copy_from_mem(pkt, digest_offset,
+ ref->digest_length,
+ ref->digest);
+ if (param->wrong_digest) {
+ uint8_t byte = ~ref->digest[0];
+
+ odp_packet_copy_from_mem(pkt, digest_offset, 1, &byte);
+ }
+ }
+ *pkt_in = pkt;
+ return 0;
+}
+
+static void prepare_oop_output_packet(const crypto_op_test_param_t *param,
+ odp_packet_t *pkt_out,
+ uint32_t pkt_len)
+{
+ uint32_t reflength = ref_length_in_bytes(param->ref);
+ const uint32_t oop_extra_len = 5;
+ uint32_t trl_len;
+ uint32_t hdr_len;
+ uint32_t oop_len;
+
+ oop_len = pkt_len + param->oop_shift + oop_extra_len;
+ *pkt_out = odp_packet_alloc(suite_context.pool, oop_len);
+ CU_ASSERT_FATAL(*pkt_out != ODP_PACKET_INVALID);
+
+ uint8_t buf[oop_len];
+
+ memset(buf, 0x55, sizeof(buf));
+ odp_packet_copy_from_mem(*pkt_out, 0, sizeof(buf), buf);
+
+ hdr_len = param->header_len + param->oop_shift;
+ trl_len = oop_len - hdr_len - reflength;
+
+ write_header_and_trailer(*pkt_out, hdr_len, trl_len);
+
+ /* have different metadata than in the input packet */
+ memset(odp_packet_user_area(*pkt_out), 0xab,
+ odp_packet_user_area_size(*pkt_out));
+}
+
+static int is_packet_data_equal(odp_packet_t pkt_1, odp_packet_t pkt_2)
+{
+ uint32_t len = odp_packet_len(pkt_1);
+ uint8_t buf_1[len];
+ uint8_t buf_2[len];
+
+ if (len != odp_packet_len(pkt_2) ||
+ odp_packet_copy_to_mem(pkt_1, 0, len, buf_1) ||
+ odp_packet_copy_to_mem(pkt_2, 0, len, buf_2))
+ return 0;
+
+ return !memcmp(buf_1, buf_2, len);
+}
+
+static int is_in_range(uint32_t offs, uint32_t range_offs, uint32_t range_len)
+{
+ return offs >= range_offs && offs < range_offs + range_len;
+}
+
+static void add_ignored_range(ignore_t *ign, uint32_t offs, uint32_t len)
+{
+ if (len == 0)
+ return;
+ CU_ASSERT_FATAL(ign->num_ranges < MAX_IGNORED_RANGES);
+ ign->ranges[ign->num_ranges].offset = offs;
+ ign->ranges[ign->num_ranges].length = len;
+ ign->num_ranges++;
+}
+
+static void clear_ignored_data(const ignore_t *ign, uint8_t *data, uint32_t data_len)
+{
+ CU_ASSERT_FATAL(ign->byte_offset < data_len);
+ data[ign->byte_offset] &= ~ign->byte_mask;
+
+ for (uint32_t n = 0; n < ign->num_ranges; n++) {
+ uint32_t offset = ign->ranges[n].offset;
+ uint32_t length = ign->ranges[n].length;
+
+ CU_ASSERT(offset + length <= data_len);
+ memset(data + offset, 0, length);
+ }
+}
+
+static void prepare_ignore_info(const crypto_op_test_param_t *param,
+ uint32_t shift,
+ uint32_t cipher_offset,
+ uint32_t cipher_len,
+ uint32_t auth_offset,
+ uint32_t auth_len,
+ ignore_t *ignore)
+{
+ memset(ignore, 0, sizeof(*ignore));
+
+ /*
+ * Leftover bits in the last byte of the cipher range of bit mode
+ * ciphers have undefined values.
+ */
+ if (param->session.cipher_range_in_bits &&
+ param->ref->cipher != ODP_CIPHER_ALG_NULL) {
+ uint8_t leftover_bits = ref_length_in_bits(param->ref) % 8;
+
+ ignore->byte_offset = cipher_offset + cipher_len - 1 + shift;
+ if (leftover_bits > 0)
+ ignore->byte_mask = ~(0xff << (8 - leftover_bits));
+ else
+ ignore->byte_mask = 0;
+ }
+
+ /*
+ * In decode sessions the bytes in the hash location have
+ * undefined values.
+ */
+ if (param->ref->auth != ODP_AUTH_ALG_NULL &&
+ param->session.op == ODP_CRYPTO_OP_DECODE) {
+ uint32_t offs = param->digest_offset;
+
+ if (param->op_type != ODP_CRYPTO_OP_TYPE_OOP ||
+ is_in_range(offs, cipher_offset, cipher_len) ||
+ is_in_range(offs, auth_offset, auth_len)) {
+ add_ignored_range(ignore,
+ param->digest_offset + shift,
+ param->ref->digest_length);
+ }
+ }
+
+ /* Decrypted bytes are undefined if authentication fails. */
+ if (param->session.op == ODP_CRYPTO_OP_DECODE &&
+ param->wrong_digest) {
+ add_ignored_range(ignore, cipher_offset + shift, cipher_len);
+ /* In OOP case, auth range may not get copied */
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP)
+ add_ignored_range(ignore, auth_offset + shift, auth_len);
+ }
+}
+
+static void prepare_expected_data(const crypto_op_test_param_t *param,
+ const odp_packet_data_range_t *cipher_range,
+ const odp_packet_data_range_t *auth_range,
+ odp_packet_t pkt_in,
+ odp_packet_t pkt_out,
+ expected_t *ex)
+{
+ uint32_t digest_offset = param->digest_offset;
+ uint32_t cipher_offset = cipher_range->offset;
+ uint32_t cipher_len = cipher_range->length;
+ uint32_t auth_offset = auth_range->offset;
+ uint32_t auth_len = auth_range->length;
+ const int32_t shift = param->op_type == ODP_CRYPTO_OP_TYPE_OOP ? param->oop_shift
+ : 0;
+ const odp_packet_t base_pkt = param->op_type == ODP_CRYPTO_OP_TYPE_OOP ? pkt_out
+ : pkt_in;
+ int rc;
+ uint32_t cipher_offset_in_ref = param->cipher_range.offset;
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE)
+ digest_offset += shift;
+
+ if (param->session.cipher_range_in_bits) {
+ cipher_offset_in_ref /= 8;
+ cipher_offset /= 8;
+ cipher_len = (cipher_len + 7) / 8;
+ }
+ if (param->session.auth_range_in_bits) {
+ auth_offset /= 8;
+ auth_len = (auth_len + 7) / 8;
+ }
+ if (param->ref->auth == ODP_AUTH_ALG_AES_GCM ||
+ param->ref->auth == ODP_AUTH_ALG_AES_CCM ||
+ param->ref->auth == ODP_AUTH_ALG_CHACHA20_POLY1305) {
+ /* auth range is ignored with AEAD algorithms */
+ auth_len = 0;
+ }
+
+ /* copy all data from base packet */
+ ex->len = odp_packet_len(base_pkt);
+ CU_ASSERT_FATAL(ex->len <= sizeof(ex->data));
+ rc = odp_packet_copy_to_mem(base_pkt, 0, ex->len, ex->data);
+ CU_ASSERT(rc == 0);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP && auth_len > 0) {
+ /* copy auth range from input packet */
+ rc = odp_packet_copy_to_mem(pkt_in, auth_offset, auth_len,
+ ex->data + auth_offset + shift);
+ CU_ASSERT(rc == 0);
+ }
+
+ if (param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ /* copy hash first */
+ memcpy(ex->data + digest_offset,
+ param->ref->digest,
+ param->ref->digest_length);
+ /*
+ * Copy ciphertext, possibly overwriting hash.
+ * The other order (hash overwriting some cipher
+ * text) does not work in any real use case anyway.
+ */
+ memcpy(ex->data + cipher_offset + shift,
+ param->ref->ciphertext + cipher_offset_in_ref,
+ cipher_len);
+ } else {
+ memcpy(ex->data + cipher_offset + shift,
+ param->ref->plaintext + cipher_offset_in_ref,
+ cipher_len);
+ }
+
+ prepare_ignore_info(param, shift,
+ cipher_offset, cipher_len,
+ auth_offset, auth_len,
+ &ex->ignore);
+}
+
+static void print_data(const char *title, uint8_t *data, uint32_t len)
+{
+ static uint64_t limit;
+
+ if (limit++ > MAX_FAILURE_PRINTS)
+ return;
+
+ printf("%s\n", title);
+ for (uint32_t n = 0; n < len ; n++) {
+ printf(" %02x", data[n]);
+ if ((n + 1) % 16 == 0)
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void check_output_packet_data(odp_packet_t pkt, expected_t *ex)
+{
+ int rc;
+ uint8_t pkt_data[ex->len];
+
+ CU_ASSERT(odp_packet_len(pkt) == ex->len);
+ rc = odp_packet_copy_to_mem(pkt, 0, ex->len, pkt_data);
+ CU_ASSERT(rc == 0);
+
+ clear_ignored_data(&ex->ignore, pkt_data, sizeof(pkt_data));
+ clear_ignored_data(&ex->ignore, ex->data, sizeof(ex->data));
+
+ if (memcmp(pkt_data, ex->data, ex->len)) {
+ CU_FAIL("packet data does not match expected data");
+ print_data("packet:", pkt_data, ex->len);
+ print_data("expected:", ex->data, ex->len);
+ }
+}
+
+static int is_digest_in_cipher_range(const crypto_op_test_param_t *param,
+ const odp_crypto_packet_op_param_t *op_params)
+{
+ /*
+ * Do not use op_params.hash_result_offset here as it refers to
+ * the output packet which (in the OOP case) might be shifted
+ * relative to the input packet.
+ */
+ uint32_t d_offset = param->digest_offset;
+
+ if (param->session.cipher_range_in_bits)
+ d_offset *= 8;
+
+ return d_offset >= op_params->cipher_range.offset &&
+ d_offset < op_params->cipher_range.offset + op_params->cipher_range.length;
+}
+
+static void do_test_crypto_op(const crypto_op_test_param_t *param)
+{
+ odp_bool_t ok = false;
+ odp_packet_t pkt;
+ odp_packet_t pkt_copy = ODP_PACKET_INVALID;
+ odp_packet_t pkt_out = ODP_PACKET_INVALID;
+ test_packet_md_t md_in, md_out, md_out_orig;
+ expected_t expected;
+ odp_crypto_packet_op_param_t op_params = {
+ .session = param->session.session,
+ .cipher_iv_ptr = param->ref->cipher_iv,
+ .auth_iv_ptr = param->ref->auth_iv,
+ .hash_result_offset = param->digest_offset,
+ .aad_ptr = param->ref->aad,
+ .dst_offset_shift = param->oop_shift,
+ .null_crypto = param->null_crypto,
+ };
+ odp_bool_t failure_allowed = false;
+
+ /*
+ * Test detection of wrong digest value in input packet
+ * only when decoding and using non-null auth algorithm.
+ */
+ if (param->wrong_digest &&
+ (param->ref->auth == ODP_AUTH_ALG_NULL ||
+ param->session.op == ODP_CRYPTO_OP_ENCODE))
+ return;
+
+ prepare_crypto_ranges(param, &op_params.cipher_range, &op_params.auth_range);
+ if (prepare_input_packet(param, &pkt))
+ return;
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ prepare_oop_output_packet(param, &pkt_out, odp_packet_len(pkt));
+
+ pkt_copy = odp_packet_copy(pkt, suite_context.pool);
+ CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID);
+ test_packet_get_md(pkt_out, &md_out_orig);
+
+ /* Non-zero-length ranges do not have to be supported. */
+ if ((param->ref->cipher == ODP_CIPHER_ALG_NULL &&
+ op_params.cipher_range.length != 0))
+ failure_allowed = true;
+ if ((param->ref->auth == ODP_AUTH_ALG_NULL &&
+ op_params.auth_range.length != 0))
+ failure_allowed = true;
+ }
+
+ prepare_expected_data(param, &op_params.cipher_range, &op_params.auth_range,
+ pkt, pkt_out, &expected);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP &&
+ param->session.op == ODP_CRYPTO_OP_ENCODE) {
+ /*
+ * In this type of sessions digest offset is an offset to the output
+ * packet, so apply the shift.
+ */
+ op_params.hash_result_offset += param->oop_shift;
+ }
+
+ test_packet_set_md(pkt);
+ test_packet_get_md(pkt, &md_in);
+
+ if (crypto_op(pkt, &pkt_out, &ok, &op_params,
+ param->session.op_type, param->op_type))
+ return;
+
+ test_packet_get_md(pkt_out, &md_out);
+
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_OOP) {
+ test_packet_md_t md;
+
+ /* check that input packet has not changed */
+ CU_ASSERT(is_packet_data_equal(pkt, pkt_copy));
+ odp_packet_free(pkt_copy);
+ test_packet_get_md(pkt, &md);
+ CU_ASSERT(test_packet_is_md_equal(&md, &md_in));
+ odp_packet_free(pkt);
+
+ /* check that metadata of output packet has not changed */
+ CU_ASSERT(test_packet_is_md_equal(&md_out, &md_out_orig));
+ } else {
+ CU_ASSERT(test_packet_is_md_equal(&md_out, &md_in));
+ }
+
+ if (param->ref->cipher != ODP_CIPHER_ALG_NULL &&
+ param->ref->auth != ODP_AUTH_ALG_NULL &&
+ is_digest_in_cipher_range(param, &op_params)) {
+ /*
+ * Not all implementations support digest offset in cipher
+ * range, so allow crypto op failure without further checks
+ * in this case.
+ */
+ failure_allowed = true;
+ }
+
+ if (!ok && failure_allowed)
+ goto out;
+
+ if (param->wrong_digest) {
+ CU_ASSERT(!ok);
+ } else {
+ CU_ASSERT(ok);
+ }
+
+ check_output_packet_data(pkt_out, &expected);
+out:
+ odp_packet_free(pkt_out);
+}
+
+void test_crypto_op(const crypto_op_test_param_t *param)
+{
+ crypto_op_test_param_t null_param = *param;
+ crypto_test_reference_t ref = *param->ref;
+
+ if (param->session.null_crypto_enable && param->null_crypto) {
+ null_param = *param;
+ null_param.ref = &ref;
+ ref = *param->ref;
+ ref.cipher = ODP_CIPHER_ALG_NULL;
+ ref.auth = ODP_AUTH_ALG_NULL;
+ ref.digest_length = 0;
+ memcpy(ref.ciphertext, ref.plaintext, sizeof(ref.ciphertext));
+ param = &null_param;
+ }
+ do_test_crypto_op(param);
+}
diff --git a/test/validation/api/crypto/crypto_op_test.h b/test/validation/api/crypto/crypto_op_test.h
new file mode 100644
index 000000000..966e0a643
--- /dev/null
+++ b/test/validation/api/crypto/crypto_op_test.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef CRYPTO_OP_TEST_H
+#define CRYPTO_OP_TEST_H
+
+#include <odp_api.h>
+#include <stdint.h>
+#include "test_vectors.h"
+
+typedef struct crypto_session_t {
+ odp_crypto_session_t session;
+ odp_crypto_op_t op;
+ odp_crypto_op_type_t op_type;
+ odp_bool_t cipher_range_in_bits;
+ odp_bool_t auth_range_in_bits;
+ odp_bool_t null_crypto_enable;
+} crypto_session_t;
+
+typedef struct crypto_op_test_param_t {
+ crypto_session_t session;
+ odp_crypto_op_type_t op_type;
+ int32_t oop_shift;
+ crypto_test_reference_t *ref;
+ odp_packet_data_range_t cipher_range;
+ odp_packet_data_range_t auth_range;
+ uint32_t digest_offset;
+ odp_bool_t null_crypto;
+ odp_bool_t adjust_segmentation;
+ odp_bool_t wrong_digest;
+ uint32_t first_seg_len;
+ uint32_t header_len;
+ uint32_t trailer_len;
+} crypto_op_test_param_t;
+
+void test_crypto_op(const crypto_op_test_param_t *param);
+
+int crypto_op(odp_packet_t pkt_in,
+ odp_packet_t *pkt_out,
+ odp_bool_t *ok,
+ const odp_crypto_packet_op_param_t *op_params,
+ odp_crypto_op_type_t session_op_type,
+ odp_crypto_op_type_t op_type);
+
+#endif
diff --git a/test/validation/api/crypto/odp_crypto_test_inp.c b/test/validation/api/crypto/odp_crypto_test_inp.c
new file mode 100644
index 000000000..10f0968d7
--- /dev/null
+++ b/test/validation/api/crypto/odp_crypto_test_inp.c
@@ -0,0 +1,2412 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include "test_vectors.h"
+#include "test_vector_defs.h"
+#include "crypto_op_test.h"
+#include "util.h"
+
+/*
+ * If nonzero, run time consuming tests too.
+ * Set through FULL_TEST environment variable.
+ */
+static int full_test;
+
+#define MAX_FAILURE_PRINTS 20
+
+#define PKT_POOL_NUM 64
+#define PKT_POOL_LEN 1200 /* enough for a test packet and some headroom */
+#define UAREA_SIZE 8
+
+static void test_defaults(uint8_t fill)
+{
+ odp_crypto_session_param_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_crypto_session_param_init(&param);
+
+ CU_ASSERT(param.op == ODP_CRYPTO_OP_ENCODE);
+ CU_ASSERT(param.op_type == ODP_CRYPTO_OP_TYPE_LEGACY);
+ CU_ASSERT(param.cipher_range_in_bits == false);
+ CU_ASSERT(param.auth_range_in_bits == false);
+ CU_ASSERT(param.auth_cipher_text == false);
+ CU_ASSERT(param.null_crypto_enable == false);
+ CU_ASSERT(param.op_mode == ODP_CRYPTO_SYNC);
+ CU_ASSERT(param.cipher_alg == ODP_CIPHER_ALG_NULL);
+ CU_ASSERT(param.cipher_iv_len == 0);
+ CU_ASSERT(param.auth_alg == ODP_AUTH_ALG_NULL);
+ CU_ASSERT(param.auth_iv_len == 0);
+ CU_ASSERT(param.auth_aad_len == 0);
+}
+
+static void test_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void print_alg_test_param(const crypto_op_test_param_t *p)
+{
+ const char *cipher_mode = p->session.cipher_range_in_bits ? "bit" : "byte";
+ const char *auth_mode = p->session.auth_range_in_bits ? "bit" : "byte";
+
+ switch (p->session.op_type) {
+ case ODP_CRYPTO_OP_TYPE_LEGACY:
+ printf("legacy ");
+ break;
+ case ODP_CRYPTO_OP_TYPE_BASIC:
+ printf("basic ");
+ break;
+ case ODP_CRYPTO_OP_TYPE_OOP:
+ printf("out-of-place ");
+ break;
+ case ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP:
+ printf("basic-and-out-of-place (%s)",
+ p->op_type == ODP_CRYPTO_OP_TYPE_BASIC ? "basic" : "oop");
+ break;
+ default:
+ printf("unknown (internal error) ");
+ break;
+ }
+ printf("%s\n", p->session.op == ODP_CRYPTO_OP_ENCODE ? "encode" : "decode");
+
+ printf("cipher: %s, %s mode\n", cipher_alg_name(p->ref->cipher), cipher_mode);
+ printf(" key length: %d, iv length: %d\n",
+ p->ref->cipher_key_length, p->ref->cipher_iv_length);
+ printf(" range: offset %d, length %d\n",
+ p->cipher_range.offset, p->cipher_range.length);
+
+ printf("auth: %s, %s mode\n", auth_alg_name(p->ref->auth), auth_mode);
+ printf(" key length: %d, iv length: %d\n",
+ p->ref->auth_key_length, p->ref->auth_iv_length);
+ printf(" range: offset %d, length %d; aad length: %d\n",
+ p->auth_range.offset, p->auth_range.length, p->ref->aad_length);
+ printf(" digest offset: %d, digest length %d\n",
+ p->digest_offset, p->ref->digest_length);
+
+ if (p->wrong_digest)
+ printf("wrong digest test\n");
+ printf("header length: %d, trailer length: %d\n", p->header_len, p->trailer_len);
+ if (p->adjust_segmentation)
+ printf("segmentation adjusted, first_seg_len: %d\n", p->first_seg_len);
+ if (p->op_type == ODP_CRYPTO_OP_TYPE_OOP)
+ printf("oop_shift: %d\n", p->oop_shift);
+ if (p->session.null_crypto_enable)
+ printf("null crypto enabled in session\n");
+ if (p->null_crypto)
+ printf("null crypto requested\n");
+}
+
+static void alg_test_execute_and_print(crypto_op_test_param_t *param)
+{
+ static int print_limit = MAX_FAILURE_PRINTS;
+ unsigned int num = CU_get_number_of_failures();
+
+ test_crypto_op(param);
+
+ if (CU_get_number_of_failures() > num) {
+ if (print_limit > 0) {
+ printf("\nTest failed:\n");
+ print_alg_test_param(param);
+ printf("\n");
+ print_limit--;
+ if (print_limit == 0)
+ printf("Suppressing further failure output\n");
+ }
+ }
+}
+
+static void alg_test_op2(crypto_op_test_param_t *param)
+{
+ int32_t oop_shifts[] = {0, 3, 130, -10};
+
+ for (uint32_t n = 0; n < ODPH_ARRAY_SIZE(oop_shifts); n++) {
+ if (oop_shifts[n] != 0 &&
+ param->op_type != ODP_CRYPTO_OP_TYPE_OOP)
+ continue;
+ if ((int32_t)param->header_len + oop_shifts[n] < 0)
+ continue;
+ param->oop_shift = oop_shifts[n];
+
+ param->wrong_digest = false;
+ alg_test_execute_and_print(param);
+
+ param->null_crypto = true;
+ alg_test_execute_and_print(param);
+ param->null_crypto = false;
+
+ if (full_test)
+ alg_test_execute_and_print(param); /* rerun with the same parameters */
+
+ if (!full_test && param->session.null_crypto_enable)
+ break;
+ if (!full_test && param->session.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP)
+ break;
+
+ param->wrong_digest = true;
+ alg_test_execute_and_print(param);
+ }
+}
+
+static void alg_test_op(crypto_op_test_param_t *param)
+{
+ param->op_type = param->session.op_type;
+ if (param->op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ param->op_type = ODP_CRYPTO_OP_TYPE_BASIC;
+ alg_test_op2(param);
+ param->op_type = ODP_CRYPTO_OP_TYPE_OOP;
+ }
+ alg_test_op2(param);
+}
+
+static int combo_warning_shown;
+static int oop_warning_shown;
+
+typedef enum {
+ HASH_NO_OVERLAP,
+ HASH_OVERLAP,
+} hash_test_mode_t;
+
+typedef enum {
+ AUTH_CIPHERTEXT,
+ AUTH_PLAINTEXT
+} alg_order_t;
+
+static int session_create(crypto_session_t *session,
+ alg_order_t order,
+ crypto_test_reference_t *ref,
+ hash_test_mode_t hash_mode,
+ odp_bool_t must_fail)
+{
+ int rc;
+ odp_crypto_ses_create_err_t status;
+ odp_crypto_session_param_t ses_params;
+ uint8_t cipher_key_data[MAX_KEY_LEN];
+ uint8_t auth_key_data[MAX_KEY_LEN];
+ odp_crypto_key_t cipher_key = {
+ .data = cipher_key_data,
+ .length = ref->cipher_key_length
+ };
+ odp_crypto_key_t auth_key = {
+ .data = auth_key_data,
+ .length = ref->auth_key_length
+ };
+
+ memcpy(cipher_key_data, ref->cipher_key, ref->cipher_key_length);
+ memcpy(auth_key_data, ref->auth_key, ref->auth_key_length);
+
+ /* Create a crypto session */
+ odp_crypto_session_param_init(&ses_params);
+ ses_params.op = session->op;
+ ses_params.op_type = session->op_type;
+ ses_params.cipher_range_in_bits = session->cipher_range_in_bits;
+ ses_params.auth_range_in_bits = session->auth_range_in_bits;
+ ses_params.auth_cipher_text = (order == AUTH_CIPHERTEXT);
+ ses_params.null_crypto_enable = session->null_crypto_enable;
+ ses_params.op_mode = suite_context.op_mode;
+ ses_params.cipher_alg = ref->cipher;
+ ses_params.auth_alg = ref->auth;
+ ses_params.compl_queue = suite_context.queue;
+ ses_params.output_pool = suite_context.pool;
+ ses_params.cipher_key = cipher_key;
+ ses_params.cipher_iv_len = ref->cipher_iv_length;
+ ses_params.auth_iv_len = ref->auth_iv_length;
+ ses_params.auth_key = auth_key;
+ ses_params.auth_digest_len = ref->digest_length;
+ ses_params.auth_aad_len = ref->aad_length;
+ ses_params.hash_result_in_auth_range = (hash_mode == HASH_OVERLAP);
+ rc = odp_crypto_session_create(&ses_params, &session->session, &status);
+
+ if (must_fail) {
+ CU_ASSERT(rc < 0);
+ if (rc == 0) {
+ rc = odp_crypto_session_destroy(session->session);
+ CU_ASSERT(rc == 0);
+ }
+ return -1;
+ }
+
+ if (rc < 0 && status == ODP_CRYPTO_SES_ERR_ALG_COMBO) {
+ if (!combo_warning_shown) {
+ combo_warning_shown = 1;
+ printf("\n Unsupported algorithm combination: %s, %s\n",
+ cipher_alg_name(ref->cipher),
+ auth_alg_name(ref->auth));
+ }
+ return -1;
+ }
+
+ /*
+ * Allow ODP_CRYPTO_SES_ERR_ALG_ORDER only in async op mode.
+ * In sync mode an implementation should be able to support both
+ * orders without much difficulty.
+ */
+ if (rc < 0 && status == ODP_CRYPTO_SES_ERR_ALG_ORDER &&
+ ses_params.op_mode == ODP_CRYPTO_ASYNC) {
+ printf("\n Unsupported algorithm order: %s, %s, auth_cipher_text: %d\n",
+ cipher_alg_name(ref->cipher),
+ auth_alg_name(ref->auth),
+ ses_params.auth_cipher_text);
+ return -1;
+ }
+
+ /* For now, allow out-of-place sessions not to be supported. */
+ if (rc < 0 && status == ODP_CRYPTO_SES_ERR_PARAMS &&
+ (ses_params.op_type == ODP_CRYPTO_OP_TYPE_OOP ||
+ ses_params.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP)) {
+ if (!oop_warning_shown)
+ printf("\n Skipping out-of-place tests\n");
+ oop_warning_shown = 1;
+ return -1;
+ }
+
+ CU_ASSERT_FATAL(!rc);
+ CU_ASSERT(status == ODP_CRYPTO_SES_ERR_NONE);
+ CU_ASSERT(odp_crypto_session_to_u64(session->session) !=
+ odp_crypto_session_to_u64(ODP_CRYPTO_SESSION_INVALID));
+
+ /*
+ * Clear session creation parameters so that we might notice if
+ * the implementation still tried to use them.
+ */
+ memset(cipher_key_data, 0, sizeof(cipher_key_data));
+ memset(auth_key_data, 0, sizeof(auth_key_data));
+ memset(&ses_params, 0, sizeof(ses_params));
+
+ return 0;
+}
+
+static void alg_test_ses(odp_crypto_op_t op,
+ odp_crypto_op_type_t op_type,
+ alg_order_t order,
+ crypto_test_reference_t *ref,
+ odp_packet_data_range_t cipher_range,
+ odp_packet_data_range_t auth_range,
+ uint32_t digest_offset,
+ odp_bool_t cipher_range_in_bits,
+ odp_bool_t auth_range_in_bits,
+ odp_bool_t null_crypto_enable,
+ odp_bool_t session_creation_must_fail)
+{
+ unsigned int initial_num_failures = CU_get_number_of_failures();
+ const uint32_t reflength = ref_length_in_bytes(ref);
+ const uint32_t auth_scale = auth_range_in_bits ? 8 : 1;
+ hash_test_mode_t hash_mode = HASH_NO_OVERLAP;
+ int rc;
+ uint32_t seg_len;
+ uint32_t max_shift;
+ crypto_op_test_param_t test_param;
+
+ if (null_crypto_enable && suite_context.op_mode == ODP_CRYPTO_SYNC)
+ return;
+
+ if (digest_offset * auth_scale >= auth_range.offset &&
+ digest_offset * auth_scale < auth_range.offset + auth_range.length)
+ hash_mode = HASH_OVERLAP;
+
+ memset(&test_param, 0, sizeof(test_param));
+ test_param.session.op = op;
+ test_param.session.op_type = op_type;
+ test_param.session.cipher_range_in_bits = cipher_range_in_bits;
+ test_param.session.auth_range_in_bits = auth_range_in_bits;
+ test_param.session.null_crypto_enable = null_crypto_enable;
+ if (session_create(&test_param.session, order, ref, hash_mode, session_creation_must_fail))
+ return;
+ test_param.ref = ref;
+ test_param.cipher_range = cipher_range;
+ test_param.auth_range = auth_range;
+ test_param.digest_offset = digest_offset;
+
+ alg_test_op(&test_param);
+
+ max_shift = reflength + ref->digest_length;
+ seg_len = 0;
+
+ if (!full_test)
+ if ((ref->cipher != ODP_CIPHER_ALG_NULL &&
+ ref->auth != ODP_AUTH_ALG_NULL) ||
+ test_param.session.null_crypto_enable ||
+ test_param.session.op_type == ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP) {
+ /* run the loop body just once */
+ seg_len = max_shift / 2;
+ max_shift = seg_len;
+ }
+
+ /*
+ * Test with segmented packets with all possible segment boundaries
+ * within the packet data
+ */
+ for (; seg_len <= max_shift; seg_len++) {
+ /*
+ * CUnit chokes on too many assertion failures, so bail
+ * out if this test has already failed.
+ */
+ if (CU_get_number_of_failures() > initial_num_failures)
+ break;
+
+ test_param.adjust_segmentation = true;
+ test_param.first_seg_len = seg_len;
+ test_param.header_len = 0;
+ test_param.trailer_len = 0;
+ test_param.digest_offset = digest_offset;
+ alg_test_op(&test_param);
+
+ /* Test partial packet crypto with odd alignment. */
+ test_param.header_len = 13;
+ test_param.trailer_len = 32;
+ test_param.digest_offset = test_param.header_len + digest_offset;
+ alg_test_op(&test_param);
+ }
+
+ rc = odp_crypto_session_destroy(test_param.session.session);
+ CU_ASSERT(!rc);
+}
+
+static void alg_test_op_types(odp_crypto_op_t op,
+ alg_order_t order,
+ crypto_test_reference_t *ref,
+ odp_packet_data_range_t cipher_range,
+ odp_packet_data_range_t auth_range,
+ uint32_t digest_offset,
+ odp_bool_t cipher_range_in_bits,
+ odp_bool_t auth_range_in_bits,
+ odp_bool_t session_creation_must_fail)
+{
+ odp_crypto_op_type_t op_types[] = {
+ ODP_CRYPTO_OP_TYPE_LEGACY,
+ ODP_CRYPTO_OP_TYPE_BASIC,
+ ODP_CRYPTO_OP_TYPE_OOP,
+ ODP_CRYPTO_OP_TYPE_BASIC_AND_OOP,
+ };
+
+ for (unsigned int n = 0; n < ODPH_ARRAY_SIZE(op_types); n++) {
+ for (unsigned int null_crypto = 0 ; null_crypto <= 1; null_crypto++)
+ alg_test_ses(op,
+ op_types[n],
+ order,
+ ref,
+ cipher_range,
+ auth_range,
+ digest_offset,
+ cipher_range_in_bits,
+ auth_range_in_bits,
+ null_crypto,
+ session_creation_must_fail);
+ }
+}
+
+static void alg_test(odp_crypto_op_t op,
+ alg_order_t order,
+ crypto_test_reference_t *ref,
+ odp_packet_data_range_t cipher_bit_range,
+ odp_packet_data_range_t auth_bit_range,
+ uint32_t digest_offset,
+ odp_bool_t is_bit_mode_cipher,
+ odp_bool_t is_bit_mode_auth)
+{
+ odp_packet_data_range_t cipher_range;
+ odp_packet_data_range_t auth_range;
+
+ for (int cr_in_bits = 0; cr_in_bits <= 1; cr_in_bits++) {
+ if (!cr_in_bits && cipher_bit_range.length % 8 != 0)
+ continue;
+ for (int ar_in_bits = 0; ar_in_bits <= 1; ar_in_bits++) {
+ odp_bool_t session_creation_must_fail;
+
+ if (!ar_in_bits && auth_bit_range.length % 8 != 0)
+ continue;
+
+ cipher_range = cipher_bit_range;
+ auth_range = auth_bit_range;
+ if (!cr_in_bits) {
+ cipher_range.offset /= 8;
+ cipher_range.length /= 8;
+ }
+ if (!ar_in_bits) {
+ auth_range.offset /= 8;
+ auth_range.length /= 8;
+ }
+ session_creation_must_fail = ((ar_in_bits && !is_bit_mode_auth) ||
+ (cr_in_bits && !is_bit_mode_cipher));
+ alg_test_op_types(op, order, ref, cipher_range, auth_range,
+ digest_offset, cr_in_bits, ar_in_bits,
+ session_creation_must_fail);
+ }
+ }
+}
+
+static odp_bool_t aad_len_ok(const odp_crypto_auth_capability_t *capa, uint32_t len)
+{
+ if (len < capa->aad_len.min || len > capa->aad_len.max)
+ return false;
+
+ if (len == capa->aad_len.min)
+ return true;
+ if (capa->aad_len.inc == 0)
+ return false;
+
+ return ((len - capa->aad_len.min) % capa->aad_len.inc) == 0;
+}
+
+static void check_alg(odp_crypto_op_t op,
+ crypto_test_reference_t *ref,
+ size_t count)
+{
+ int rc, i;
+ const odp_cipher_alg_t cipher_alg = ref->cipher;
+ const odp_auth_alg_t auth_alg = ref->auth;
+ int cipher_num = odp_crypto_cipher_capability(cipher_alg, NULL, 0);
+ int auth_num = odp_crypto_auth_capability(auth_alg, NULL, 0);
+ odp_bool_t cipher_ok = false;
+ odp_bool_t auth_ok = false;
+ size_t idx;
+
+ CU_ASSERT_FATAL(cipher_num > 0);
+ CU_ASSERT_FATAL(auth_num > 0);
+
+ init_reference(ref, count);
+
+ odp_crypto_cipher_capability_t cipher_capa[cipher_num];
+ odp_crypto_auth_capability_t auth_capa[auth_num];
+ odp_bool_t cipher_tested[cipher_num];
+ odp_bool_t auth_tested[auth_num];
+
+ rc = odp_crypto_cipher_capability(cipher_alg, cipher_capa, cipher_num);
+ CU_ASSERT_FATAL(rc == cipher_num);
+
+ rc = odp_crypto_auth_capability(auth_alg, auth_capa, auth_num);
+ CU_ASSERT_FATAL(rc == auth_num);
+
+ memset(cipher_tested, 0, sizeof(cipher_tested));
+ memset(auth_tested, 0, sizeof(auth_tested));
+
+ oop_warning_shown = 0; /* allow OOP-unsupported warning again */
+
+ for (idx = 0; idx < count; idx++) {
+ int cipher_idx = -1, auth_idx = -1;
+ odp_bool_t is_bit_mode_cipher = false;
+ odp_bool_t is_bit_mode_auth = false;
+ uint32_t digest_offs = ref_length_in_bytes(&ref[idx]);
+ odp_packet_data_range_t cipher_bit_range = {.offset = 0};
+ odp_packet_data_range_t auth_bit_range = {.offset = 0};
+
+ for (i = 0; i < cipher_num; i++) {
+ if (cipher_capa[i].key_len ==
+ ref[idx].cipher_key_length &&
+ cipher_capa[i].iv_len ==
+ ref[idx].cipher_iv_length) {
+ cipher_idx = i;
+ is_bit_mode_cipher = cipher_capa[i].bit_mode;
+ break;
+ }
+ }
+
+ if (cipher_idx < 0) {
+ printf("\n Unsupported: alg=%s, key_len=%" PRIu32
+ ", iv_len=%" PRIu32 "\n",
+ cipher_alg_name(cipher_alg),
+ ref[idx].cipher_key_length,
+ ref[idx].cipher_iv_length);
+ continue;
+ }
+
+ for (i = 0; i < auth_num; i++) {
+ if (auth_capa[i].digest_len ==
+ ref[idx].digest_length &&
+ auth_capa[i].iv_len ==
+ ref[idx].auth_iv_length &&
+ auth_capa[i].key_len ==
+ ref[idx].auth_key_length &&
+ aad_len_ok(&auth_capa[i], ref[idx].aad_length)) {
+ auth_idx = i;
+ is_bit_mode_auth = auth_capa[i].bit_mode;
+ break;
+ }
+ }
+
+ if (auth_idx < 0) {
+ printf("\n Unsupported: alg=%s, key_len=%" PRIu32
+ ", iv_len=%" PRIu32 ", digest_len=%" PRIu32
+ "\n",
+ auth_alg_name(auth_alg),
+ ref[idx].auth_key_length,
+ ref[idx].auth_iv_length,
+ ref[idx].digest_length);
+ continue;
+ }
+
+ cipher_bit_range.length = ref_length_in_bits(&ref[idx]);
+ auth_bit_range.length = ref_length_in_bits(&ref[idx]);
+
+ alg_test(op, AUTH_PLAINTEXT, &ref[idx],
+ cipher_bit_range, auth_bit_range, digest_offs,
+ is_bit_mode_cipher, is_bit_mode_auth);
+ alg_test(op, AUTH_CIPHERTEXT, &ref[idx],
+ cipher_bit_range, auth_bit_range, digest_offs,
+ is_bit_mode_cipher, is_bit_mode_auth);
+
+ cipher_tested[cipher_idx] = true;
+ auth_tested[auth_idx] = true;
+ }
+
+ for (i = 0; i < cipher_num; i++) {
+ cipher_ok |= cipher_tested[i];
+ if (!cipher_tested[i] && cipher_alg != ODP_CIPHER_ALG_NULL)
+ printf("\n Untested: alg=%s, key_len=%" PRIu32 ", "
+ "iv_len=%" PRIu32 "%s\n",
+ cipher_alg_name(cipher_alg),
+ cipher_capa[i].key_len,
+ cipher_capa[i].iv_len,
+ cipher_capa[i].bit_mode ? ", bit mode" : "");
+ }
+
+ for (i = 0; i < auth_num; i++) {
+ auth_ok |= auth_tested[i];
+ if (!auth_tested[i] && auth_alg != ODP_AUTH_ALG_NULL)
+ printf("\n Untested: alg=%s, key_len=%" PRIu32 ", "
+ "digest_len=%" PRIu32 "%s\n",
+ auth_alg_name(auth_alg),
+ auth_capa[i].key_len,
+ auth_capa[i].digest_len,
+ auth_capa[i].bit_mode ? ", bit mode" : "");
+ }
+
+ /* Verify that we were able to run at least one test */
+ CU_ASSERT(cipher_ok);
+ CU_ASSERT(auth_ok);
+}
+
+static void test_capability(void)
+{
+ odp_crypto_capability_t capa = {.max_sessions = 1};
+ int rc;
+
+ rc = odp_crypto_capability(&capa);
+ CU_ASSERT(!rc);
+ if (capa.max_sessions > 0)
+ CU_ASSERT(capa.sync_mode || capa.async_mode);
+ CU_ASSERT((~capa.ciphers.all_bits & capa.hw_ciphers.all_bits) == 0);
+ CU_ASSERT((~capa.auths.all_bits & capa.hw_auths.all_bits) == 0);
+}
+
+/*
+ * Create a test reference, which can be used in tests where the hash
+ * result is within auth_range.
+ *
+ * The ciphertext packet and the hash are calculated using an encode
+ * operation with hash_result_offset outside the auth_range and by
+ * copying the hash in the ciphertext packet.
+ */
+static int create_hash_test_reference(odp_auth_alg_t auth,
+ const odp_crypto_auth_capability_t *capa,
+ crypto_test_reference_t *ref,
+ uint32_t digest_offset,
+ uint8_t digest_fill)
+{
+ crypto_session_t session;
+ int rc;
+ odp_packet_t pkt;
+ odp_bool_t ok;
+ const uint32_t auth_bytes = 100;
+ uint32_t enc_digest_offset = auth_bytes;
+
+ ref->cipher = ODP_CIPHER_ALG_NULL;
+ ref->auth = auth;
+ ref->auth_key_length = capa->key_len;
+ ref->auth_iv_length = capa->iv_len;
+ ref->digest_length = capa->digest_len;
+ ref->is_length_in_bits = false;
+ ref->length = auth_bytes;
+
+ if (ref->auth_key_length > MAX_KEY_LEN ||
+ ref->auth_iv_length > MAX_IV_LEN ||
+ auth_bytes > MAX_DATA_LEN ||
+ digest_offset + ref->digest_length > MAX_DATA_LEN)
+ CU_FAIL_FATAL("Internal error\n");
+
+ fill_with_pattern(ref->auth_key, ref->auth_key_length);
+ fill_with_pattern(ref->auth_iv, ref->auth_iv_length);
+ fill_with_pattern(ref->plaintext, auth_bytes);
+
+ memset(ref->plaintext + digest_offset, digest_fill, ref->digest_length);
+
+ pkt = odp_packet_alloc(suite_context.pool, auth_bytes + ref->digest_length);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ rc = odp_packet_copy_from_mem(pkt, 0, auth_bytes, ref->plaintext);
+ CU_ASSERT(rc == 0);
+
+ session.op = ODP_CRYPTO_OP_ENCODE;
+ session.op_type = ODP_CRYPTO_OP_TYPE_BASIC;
+ session.cipher_range_in_bits = false;
+ session.auth_range_in_bits = false;
+ session.null_crypto_enable = false;
+ if (session_create(&session, AUTH_PLAINTEXT, ref, HASH_NO_OVERLAP, false))
+ return -1;
+
+ odp_crypto_packet_op_param_t op_params = {
+ .session = session.session,
+ .cipher_iv_ptr = ref->cipher_iv,
+ .auth_iv_ptr = ref->auth_iv,
+ .hash_result_offset = enc_digest_offset,
+ .aad_ptr = ref->aad,
+ .cipher_range = {.offset = 0, .length = 0},
+ .auth_range = { .offset = 0, .length = auth_bytes },
+ .dst_offset_shift = 0,
+ };
+ rc = crypto_op(pkt, &pkt, &ok, &op_params,
+ ODP_CRYPTO_OP_TYPE_BASIC, ODP_CRYPTO_OP_TYPE_BASIC);
+
+ CU_ASSERT(rc == 0);
+ if (rc) {
+ (void)odp_crypto_session_destroy(session.session);
+ return -1;
+ }
+ CU_ASSERT(ok);
+
+ rc = odp_crypto_session_destroy(session.session);
+ CU_ASSERT(rc == 0);
+
+ /* copy the processed packet to the ciphertext packet in ref */
+ rc = odp_packet_copy_to_mem(pkt, 0, auth_bytes, ref->ciphertext);
+ CU_ASSERT(rc == 0);
+
+ /* copy the calculated digest in the ciphertext packet in ref */
+ rc = odp_packet_copy_to_mem(pkt, enc_digest_offset, ref->digest_length,
+ &ref->ciphertext[digest_offset]);
+ CU_ASSERT(rc == 0);
+
+ /* copy the calculated digest the digest field in ref */
+ rc = odp_packet_copy_to_mem(pkt, enc_digest_offset, ref->digest_length,
+ &ref->digest);
+ CU_ASSERT(rc == 0);
+
+ odp_packet_free(pkt);
+
+ return 0;
+}
+
+static void test_auth_hash_in_auth_range(odp_auth_alg_t auth,
+ const odp_crypto_auth_capability_t *capa,
+ odp_bool_t is_bit_mode_cipher,
+ alg_order_t order)
+{
+ static crypto_test_reference_t ref = {.length = 0};
+ uint32_t digest_offset = 13;
+ const odp_packet_data_range_t cipher_bit_range = {.offset = 0, .length = 0};
+ odp_packet_data_range_t auth_bit_range;
+
+ if (!full_test && capa->digest_len % 4 != 0)
+ return;
+
+ /*
+ * Create test packets with auth hash in the authenticated range and
+ * zeroes in the hash location in the plaintext packet.
+ */
+ if (create_hash_test_reference(auth, capa, &ref, digest_offset, 0))
+ return;
+
+ auth_bit_range.offset = 0;
+ auth_bit_range.length = ref_length_in_bits(&ref);
+
+ /*
+ * Decode the ciphertext packet.
+ *
+ * Check that auth hash verification works even if hash_result_offset
+ * is within the auth range. The ODP implementation must clear the
+ * hash bytes in the ciphertext packet before calculating the hash.
+ */
+ alg_test(ODP_CRYPTO_OP_DECODE,
+ order,
+ &ref,
+ cipher_bit_range, auth_bit_range,
+ digest_offset,
+ is_bit_mode_cipher,
+ capa->bit_mode);
+
+ /*
+ * Create test packets with auth hash in the authenticated range and
+ * ones in the hash location in the plaintext packet.
+ */
+ if (create_hash_test_reference(auth, capa, &ref, digest_offset, 1))
+ return;
+
+ auth_bit_range.offset = 0;
+ auth_bit_range.length = ref_length_in_bits(&ref);
+
+ /*
+ * Encode the plaintext packet.
+ *
+ * Check that auth hash generation works even if hash_result_offset
+ * is within the auth range. The ODP implementation must not clear
+ * the hash bytes in the plaintext packet before calculating the hash.
+ */
+ alg_test(ODP_CRYPTO_OP_ENCODE,
+ order,
+ &ref,
+ cipher_bit_range, auth_bit_range,
+ digest_offset,
+ is_bit_mode_cipher,
+ capa->bit_mode);
+}
+
+/*
+ * Cipher algorithms that are not AEAD algorithms
+ */
+static odp_cipher_alg_t cipher_algs[] = {
+ ODP_CIPHER_ALG_NULL,
+ ODP_CIPHER_ALG_DES,
+ ODP_CIPHER_ALG_3DES_CBC,
+ ODP_CIPHER_ALG_3DES_ECB,
+ ODP_CIPHER_ALG_AES_CBC,
+ ODP_CIPHER_ALG_AES_CTR,
+ ODP_CIPHER_ALG_AES_ECB,
+ ODP_CIPHER_ALG_AES_CFB128,
+ ODP_CIPHER_ALG_AES_XTS,
+ ODP_CIPHER_ALG_KASUMI_F8,
+ ODP_CIPHER_ALG_SNOW3G_UEA2,
+ ODP_CIPHER_ALG_AES_EEA2,
+ ODP_CIPHER_ALG_ZUC_EEA3,
+};
+
+/*
+ * Authentication algorithms and hashes that may use auth_range
+ * parameter. AEAD algorithms are excluded.
+ */
+static odp_auth_alg_t auth_algs[] = {
+ ODP_AUTH_ALG_NULL,
+ ODP_AUTH_ALG_MD5_HMAC,
+ ODP_AUTH_ALG_SHA1_HMAC,
+ ODP_AUTH_ALG_SHA224_HMAC,
+ ODP_AUTH_ALG_SHA256_HMAC,
+ ODP_AUTH_ALG_SHA384_HMAC,
+ ODP_AUTH_ALG_SHA512_HMAC,
+ ODP_AUTH_ALG_AES_GMAC,
+ ODP_AUTH_ALG_AES_CMAC,
+ ODP_AUTH_ALG_AES_XCBC_MAC,
+ ODP_AUTH_ALG_KASUMI_F9,
+ ODP_AUTH_ALG_SNOW3G_UIA2,
+ ODP_AUTH_ALG_AES_EIA2,
+ ODP_AUTH_ALG_ZUC_EIA3,
+ ODP_AUTH_ALG_MD5,
+ ODP_AUTH_ALG_SHA1,
+ ODP_AUTH_ALG_SHA224,
+ ODP_AUTH_ALG_SHA256,
+ ODP_AUTH_ALG_SHA384,
+ ODP_AUTH_ALG_SHA512,
+};
+
+static void test_auth_hashes_in_auth_range(void)
+{
+ for (size_t n = 0; n < ODPH_ARRAY_SIZE(auth_algs); n++) {
+ odp_auth_alg_t auth = auth_algs[n];
+ odp_crypto_cipher_capability_t c_capa;
+ int num;
+
+ if (check_alg_support(ODP_CIPHER_ALG_NULL, auth) == ODP_TEST_INACTIVE)
+ continue;
+
+ num = odp_crypto_cipher_capability(ODP_CIPHER_ALG_NULL, &c_capa, 1);
+ CU_ASSERT_FATAL(num == 1);
+
+ num = odp_crypto_auth_capability(auth, NULL, 0);
+ CU_ASSERT_FATAL(num > 0);
+
+ odp_crypto_auth_capability_t capa[num];
+
+ num = odp_crypto_auth_capability(auth, capa, num);
+
+ for (int i = 0; i < num; i++) {
+ test_auth_hash_in_auth_range(auth, &capa[i], c_capa.bit_mode,
+ AUTH_PLAINTEXT);
+ test_auth_hash_in_auth_range(auth, &capa[i], c_capa.bit_mode,
+ AUTH_CIPHERTEXT);
+ }
+ }
+}
+
+/*
+ * Encode ref->plaintext and save result in ref->ciphertext.
+ */
+static int crypto_encode_ref(crypto_test_reference_t *ref,
+ odp_packet_data_range_t cipher_range,
+ odp_packet_data_range_t auth_range,
+ uint32_t hash_result_offset)
+{
+ odp_packet_data_range_t zero_range = {.offset = 0, .length = 0};
+ odp_packet_t pkt;
+ int rc;
+ crypto_session_t session;
+ odp_bool_t ok;
+
+ pkt = odp_packet_alloc(suite_context.pool, ref->length);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ rc = odp_packet_copy_from_mem(pkt, 0, ref->length, ref->plaintext);
+ CU_ASSERT(rc == 0);
+
+ session.op = ODP_CRYPTO_OP_ENCODE;
+ session.op_type = ODP_CRYPTO_OP_TYPE_BASIC;
+ session.cipher_range_in_bits = false;
+ session.auth_range_in_bits = false;
+ session.null_crypto_enable = false;
+ if (session_create(&session, AUTH_PLAINTEXT, ref, HASH_OVERLAP, false)) {
+ odp_packet_free(pkt);
+ return 1;
+ }
+
+ if (ref->cipher == ODP_CIPHER_ALG_NULL)
+ cipher_range = zero_range;
+ if (ref->auth == ODP_AUTH_ALG_NULL) {
+ auth_range = zero_range;
+ hash_result_offset = 0;
+ }
+
+ CU_ASSERT_FATAL(hash_result_offset + ref->digest_length <= ref->length);
+
+ odp_crypto_packet_op_param_t op_params = {
+ .session = session.session,
+ .cipher_iv_ptr = ref->cipher_iv,
+ .auth_iv_ptr = ref->auth_iv,
+ .hash_result_offset = hash_result_offset,
+ .aad_ptr = ref->aad,
+ .cipher_range = cipher_range,
+ .auth_range = auth_range,
+ .dst_offset_shift = 0,
+ };
+ rc = crypto_op(pkt, &pkt, &ok, &op_params,
+ ODP_CRYPTO_OP_TYPE_BASIC, ODP_CRYPTO_OP_TYPE_BASIC);
+ CU_ASSERT(rc == 0);
+ if (rc) {
+ (void)odp_crypto_session_destroy(session.session);
+ return -1;
+ }
+ CU_ASSERT(ok);
+
+ rc = odp_crypto_session_destroy(session.session);
+ CU_ASSERT(rc == 0);
+
+ rc = odp_packet_copy_to_mem(pkt, 0, ref->length, ref->ciphertext);
+ CU_ASSERT(rc == 0);
+
+ odp_packet_free(pkt);
+ return 0;
+}
+
+typedef struct crypto_suite_t {
+ odp_cipher_alg_t cipher;
+ odp_auth_alg_t auth;
+ alg_order_t order;
+ const odp_crypto_cipher_capability_t *cipher_capa;
+ const odp_crypto_auth_capability_t *auth_capa;
+} crypto_suite_t;
+
+/*
+ * Create test reference for combined auth & cipher by doing authentication
+ * and ciphering through separate ODP crypto operations.
+ */
+static int create_combined_ref(const crypto_suite_t *suite,
+ crypto_test_reference_t *ref,
+ const odp_packet_data_range_t *cipher_range,
+ const odp_packet_data_range_t *auth_range,
+ uint32_t digest_offset)
+{
+ uint32_t total_len;
+ int rc;
+ crypto_test_reference_t ref_cipher_only;
+ crypto_test_reference_t ref_auth_only;
+ crypto_test_reference_t *first_ref, *second_ref;
+
+ total_len = cipher_range->offset + cipher_range->length;
+ if (auth_range->offset + auth_range->length > total_len)
+ total_len = auth_range->offset + auth_range->length;
+ if (digest_offset + suite->auth_capa->digest_len > total_len)
+ total_len = digest_offset + suite->auth_capa->digest_len;
+
+ ref->cipher = suite->cipher;
+ ref->auth = suite->auth;
+ ref->cipher_key_length = suite->cipher_capa->key_len;
+ ref->cipher_iv_length = suite->cipher_capa->iv_len;
+ ref->auth_key_length = suite->auth_capa->key_len;
+ ref->auth_iv_length = suite->auth_capa->iv_len;
+ ref->digest_length = suite->auth_capa->digest_len;
+ ref->aad_length = 0;
+ ref->is_length_in_bits = false;
+ ref->length = total_len;
+
+ if (ref->auth_key_length > MAX_KEY_LEN ||
+ ref->auth_iv_length > MAX_IV_LEN ||
+ total_len > MAX_DATA_LEN ||
+ digest_offset + ref->digest_length > MAX_DATA_LEN)
+ CU_FAIL_FATAL("Internal error\n");
+
+ fill_with_pattern(ref->cipher_key, ref->cipher_key_length);
+ fill_with_pattern(ref->cipher_iv, ref->cipher_iv_length);
+ fill_with_pattern(ref->auth_key, ref->auth_key_length);
+ fill_with_pattern(ref->auth_iv, ref->auth_iv_length);
+ fill_with_pattern(ref->plaintext, ref->length);
+ memset(ref->plaintext + digest_offset, 0, ref->digest_length);
+
+ ref_cipher_only = *ref;
+ ref_cipher_only.auth = ODP_AUTH_ALG_NULL;
+ ref_cipher_only.auth_key_length = 0;
+ ref_cipher_only.auth_iv_length = 0;
+ ref_cipher_only.aad_length = 0;
+ ref_cipher_only.digest_length = 0;
+
+ ref_auth_only = *ref;
+ ref_auth_only.cipher = ODP_CIPHER_ALG_NULL;
+ ref_auth_only.cipher_key_length = 0;
+ ref_auth_only.cipher_iv_length = 0;
+
+ if (suite->order == AUTH_CIPHERTEXT) {
+ first_ref = &ref_cipher_only;
+ second_ref = &ref_auth_only;
+ } else {
+ first_ref = &ref_auth_only;
+ second_ref = &ref_cipher_only;
+ }
+ rc = crypto_encode_ref(first_ref,
+ *cipher_range, *auth_range,
+ digest_offset);
+ if (rc)
+ return 1;
+ memcpy(second_ref->plaintext, first_ref->ciphertext, ref->length);
+ rc = crypto_encode_ref(second_ref,
+ *cipher_range, *auth_range,
+ digest_offset);
+ if (rc)
+ return 1;
+ memcpy(ref->ciphertext, second_ref->ciphertext, ref->length);
+ /*
+ * These may be encrypted bytes, but that is what alg_test wants if
+ * the digest is encrypted in the input packet.
+ */
+ memcpy(ref->digest, second_ref->ciphertext + digest_offset, ref->digest_length);
+
+ return 0;
+}
+
+/*
+ * Return cipher range that is at least min_len bytes long, multiple of the
+ * block size and at least 3 blocks.
+ */
+static uint32_t get_cipher_range_len(uint32_t min_len)
+{
+#define MAX_BLOCK_SIZE 16
+ uint32_t bs = MAX_BLOCK_SIZE;
+ uint32_t len = 3 * bs;
+
+ if (min_len > len)
+ len = ((min_len + bs - 1) / bs) * bs;
+ return len;
+}
+
+typedef enum range_overlap_t {
+ SEPARATE_AUTH_AND_CIPHER_RANGES,
+ SAME_AUTH_AND_CIPHER_RANGE,
+ RANGES_PARTIALLY_OVERLAP,
+ AUTH_RANGE_IN_CIPHER_RANGE,
+ CIPHER_RANGE_IN_AUTH_RANGE,
+} range_overlap_t;
+#define NUM_RANGE_OVERLAPS 5
+
+typedef enum hash_location_t {
+ HASH_SEPARATE,
+ HASH_IN_AUTH_RANGE_ONLY,
+ HASH_IN_CIPHER_RANGE_ONLY,
+ HASH_IN_AUTH_AND_CIPHER_RANGE,
+} hash_location_t;
+#define NUM_HASH_LOCATIONS 4
+
+static int make_byte_ranges(range_overlap_t overlap,
+ hash_location_t hash_location,
+ uint32_t hash_len,
+ odp_packet_data_range_t *cipher_range,
+ odp_packet_data_range_t *auth_range,
+ uint32_t *digest_offset)
+{
+ const uint32_t padding = 5; /* padding between parts, could also be zero */
+ const uint32_t nonzero_len = 3;
+ uint32_t c_offs = 0, c_len = 0, a_offs = 0, a_len = 0, digest_offs = 0;
+
+ switch (overlap) {
+ case SEPARATE_AUTH_AND_CIPHER_RANGES:
+ switch (hash_location) {
+ case HASH_SEPARATE:
+ /* |cccc_aaaa_dd| */
+ c_offs = 0;
+ c_len = get_cipher_range_len(nonzero_len);
+ a_offs = c_offs + c_len + padding;
+ a_len = nonzero_len;
+ digest_offs = a_offs + a_len + padding;
+ break;
+ case HASH_IN_AUTH_RANGE_ONLY:
+ /*
+ * |cccc_aaaa|
+ * | _dd_|
+ */
+ c_offs = 0;
+ c_len = get_cipher_range_len(nonzero_len);
+ a_offs = c_offs + c_len + padding;
+ a_len = hash_len + 2 * padding;
+ digest_offs = a_offs + padding;
+ break;
+ case HASH_IN_CIPHER_RANGE_ONLY:
+ /*
+ * |cccc_aaaa|
+ * |_dd_ |
+ */
+ c_offs = 0;
+ c_len = get_cipher_range_len(hash_len + 2 * padding);
+ a_offs = c_offs + c_len + padding;
+ a_len = nonzero_len;
+ digest_offs = c_offs + padding;
+ break;
+ case HASH_IN_AUTH_AND_CIPHER_RANGE:
+ /* not possible when ranges are separate */
+ return 1;
+ }
+ break;
+ case SAME_AUTH_AND_CIPHER_RANGE:
+ c_offs = 0;
+ a_offs = 0;
+ switch (hash_location) {
+ case HASH_SEPARATE:
+ /*
+ * |cccc_dd|
+ * |aaaa |
+ */
+ c_len = get_cipher_range_len(nonzero_len);
+ a_len = c_len;
+ digest_offs = c_len + padding;
+ break;
+ case HASH_IN_AUTH_RANGE_ONLY:
+ case HASH_IN_CIPHER_RANGE_ONLY:
+ /* not possible when ranges are the same */
+ return 1;
+ case HASH_IN_AUTH_AND_CIPHER_RANGE:
+ /*
+ * |cccc|
+ * |aaaa|
+ * |_dd_|
+ */
+ c_len = get_cipher_range_len(hash_len + 2 * padding);
+ a_len = c_len;
+ digest_offs = padding;
+ break;
+ }
+ break;
+ case RANGES_PARTIALLY_OVERLAP:
+ a_offs = 0;
+ switch (hash_location) {
+ case HASH_SEPARATE:
+ /*
+ * |aaaa |
+ * | cccc_dd|
+ */
+ a_len = 2 * nonzero_len;
+ c_offs = nonzero_len;
+ c_len = get_cipher_range_len(a_len);
+ digest_offs = c_offs + c_len + padding;
+ break;
+ case HASH_IN_AUTH_RANGE_ONLY:
+ /*
+ * |aaaaa |
+ * |_dd_ccc|
+ */
+ digest_offs = padding;
+ a_len = hash_len + 2 * padding + nonzero_len;
+ c_offs = hash_len + 2 * padding;
+ c_len = get_cipher_range_len(2 * nonzero_len);
+ break;
+ case HASH_IN_CIPHER_RANGE_ONLY:
+ /* PDCP case when AUTH_PLAINTEXT */
+ /*
+ * |aaaadd|
+ * | ccccc|
+ */
+ c_offs = nonzero_len;
+ c_len = get_cipher_range_len(nonzero_len + hash_len);
+ a_len = nonzero_len + c_len - hash_len;
+ digest_offs = c_offs + c_len - hash_len;
+ break;
+ case HASH_IN_AUTH_AND_CIPHER_RANGE:
+ /*
+ * |aaaaaa |
+ * | cccccc|
+ * |¨_dd_ |
+ */
+ c_offs = nonzero_len;
+ c_len = get_cipher_range_len(hash_len + 2 * padding + nonzero_len);
+ a_len = c_offs + hash_len + 2 * padding;
+ digest_offs = c_offs + padding;
+ break;
+ }
+ break;
+ case AUTH_RANGE_IN_CIPHER_RANGE:
+ c_offs = 0;
+ a_offs = nonzero_len;
+ switch (hash_location) {
+ case HASH_SEPARATE:
+ /*
+ * |cccc_dd|
+ * | aa_ |
+ */
+ a_len = nonzero_len;
+ c_len = get_cipher_range_len(a_offs + a_len + padding);
+ digest_offs = c_len + padding;
+ break;
+ case HASH_IN_AUTH_RANGE_ONLY:
+ /* not possible since auth range is in cipher range */
+ return 1;
+ case HASH_IN_CIPHER_RANGE_ONLY:
+ /*
+ * |ccccccc|
+ * | aa_dd_|
+ */
+ a_len = nonzero_len;
+ digest_offs = a_offs + a_len + padding;
+ c_len = get_cipher_range_len(digest_offs + hash_len + padding);
+ break;
+ case HASH_IN_AUTH_AND_CIPHER_RANGE:
+ /*
+ * |cccccc|
+ * | aaaa_|
+ * | _dd_ |
+ */
+ a_len = /**/ hash_len + 2 * padding;
+ c_len = get_cipher_range_len(a_offs + a_len + padding);
+ digest_offs = a_offs + /**/ padding;
+ break;
+ }
+ break;
+ case CIPHER_RANGE_IN_AUTH_RANGE:
+ a_offs = 0;
+ c_offs = nonzero_len;
+ switch (hash_location) {
+ case HASH_SEPARATE:
+ /*
+ * |aaaa_dd|
+ * | cc_ |
+ */
+ c_len = get_cipher_range_len(nonzero_len);
+ a_len = c_offs + c_len + padding;
+ digest_offs = a_len + padding;
+ break;
+ case HASH_IN_AUTH_RANGE_ONLY:
+ /*
+ * |aaaaaaa|
+ * | cc_dd_|
+ */
+ c_len = get_cipher_range_len(nonzero_len);
+ digest_offs = c_offs + c_len + padding;
+ a_len = digest_offs + hash_len + padding;
+ break;
+ case HASH_IN_CIPHER_RANGE_ONLY:
+ /* not possible since cipher range is in auth range */
+ return 1;
+ case HASH_IN_AUTH_AND_CIPHER_RANGE:
+ /*
+ * |aaaaaa|
+ * | cccc_|
+ * | _dd_ |
+ */
+ c_len = get_cipher_range_len(hash_len + 2 * padding);
+ a_len = c_offs + c_len + padding;
+ digest_offs = c_offs + padding;
+ break;
+ }
+ break;
+ }
+ cipher_range->offset = c_offs;
+ cipher_range->length = c_len;
+ auth_range->offset = a_offs;
+ auth_range->length = a_len;
+ *digest_offset = digest_offs;
+ return 0;
+}
+
+static void test_combo(const crypto_suite_t *suite,
+ range_overlap_t overlap,
+ hash_location_t location)
+{
+ int rc;
+
+ odp_packet_data_range_t cipher_range = {0, 0};
+ odp_packet_data_range_t auth_range = {0, 0};
+ uint32_t digest_offset = 0;
+ crypto_test_reference_t ref;
+
+ rc = make_byte_ranges(overlap,
+ location,
+ suite->auth_capa->digest_len,
+ &cipher_range,
+ &auth_range,
+ &digest_offset);
+ if (rc)
+ return;
+
+ rc = create_combined_ref(suite, &ref,
+ &cipher_range, &auth_range,
+ digest_offset);
+ if (rc)
+ return;
+
+ cipher_range.offset *= 8;
+ cipher_range.length *= 8;
+ auth_range.offset *= 8;
+ auth_range.length *= 8;
+
+ alg_test(ODP_CRYPTO_OP_ENCODE,
+ suite->order,
+ &ref,
+ cipher_range, auth_range,
+ digest_offset,
+ suite->cipher_capa->bit_mode,
+ suite->auth_capa->bit_mode);
+
+ alg_test(ODP_CRYPTO_OP_DECODE,
+ suite->order,
+ &ref,
+ cipher_range, auth_range,
+ digest_offset,
+ suite->cipher_capa->bit_mode,
+ suite->auth_capa->bit_mode);
+}
+
+/* Iterate and test different cipher/auth range and hash locations */
+static void test_combo_ranges(const crypto_suite_t *suite)
+{
+ if (!full_test && suite->auth_capa->digest_len % 4 != 0)
+ return;
+
+ for (int overlap = 0; overlap < NUM_RANGE_OVERLAPS; overlap++)
+ for (int location = 0; location < NUM_HASH_LOCATIONS; location++) {
+ if (suite->order == AUTH_CIPHERTEXT &&
+ (location == HASH_IN_CIPHER_RANGE_ONLY ||
+ location == HASH_IN_AUTH_AND_CIPHER_RANGE)) {
+ /*
+ * This combination ís not valid since
+ * the generated hash would overwrite some
+ * ciphertext, preventing decryption.
+ */
+ continue;
+ }
+ test_combo(suite, overlap, location);
+ }
+}
+
+/* Iterate and test all variants (key sizes etc) of an alg combo */
+static void test_combo_variants(odp_cipher_alg_t cipher, odp_auth_alg_t auth)
+{
+ int num, num_ciphers, num_auths;
+
+ /* ODP API says AES-GMAC can be combined with the null cipher only */
+ if (auth == ODP_AUTH_ALG_AES_GMAC &&
+ cipher != ODP_CIPHER_ALG_NULL)
+ return;
+
+ if (check_alg_support(cipher, auth) == ODP_TEST_INACTIVE)
+ return;
+
+ printf(" %s, %s\n",
+ cipher_alg_name(cipher),
+ auth_alg_name(auth));
+
+ num_ciphers = odp_crypto_cipher_capability(cipher, NULL, 0);
+ num_auths = odp_crypto_auth_capability(auth, NULL, 0);
+ CU_ASSERT_FATAL(num_ciphers > 0);
+ CU_ASSERT_FATAL(num_auths > 0);
+
+ odp_crypto_cipher_capability_t cipher_capa[num_ciphers];
+ odp_crypto_auth_capability_t auth_capa[num_auths];
+
+ num = odp_crypto_cipher_capability(cipher, cipher_capa, num_ciphers);
+ CU_ASSERT(num == num_ciphers);
+ num = odp_crypto_auth_capability(auth, auth_capa, num_auths);
+ CU_ASSERT(num == num_auths);
+
+ combo_warning_shown = 0;
+
+ for (int n = 0; n < num_ciphers; n++)
+ for (int i = 0; i < num_auths; i++) {
+ crypto_suite_t suite = {.cipher = cipher,
+ .auth = auth,
+ .cipher_capa = &cipher_capa[n],
+ .auth_capa = &auth_capa[i]};
+ suite.order = AUTH_PLAINTEXT;
+ test_combo_ranges(&suite);
+ suite.order = AUTH_CIPHERTEXT;
+ test_combo_ranges(&suite);
+ }
+}
+
+static void test_all_combinations(void)
+{
+ if (suite_context.partial_test) {
+ printf("skipped ");
+ return;
+ }
+
+ printf("\n");
+ for (size_t n = 0; n < ODPH_ARRAY_SIZE(cipher_algs); n++)
+ for (size_t i = 0; i < ODPH_ARRAY_SIZE(auth_algs); i++)
+ test_combo_variants(cipher_algs[n], auth_algs[i]);
+}
+
+static int check_alg_null(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_null(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ null_reference,
+ ODPH_ARRAY_SIZE(null_reference));
+}
+
+static void crypto_test_dec_alg_null(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ null_reference,
+ ODPH_ARRAY_SIZE(null_reference));
+}
+
+static int check_alg_3des_cbc(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_3DES_CBC, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_3des_cbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ tdes_cbc_reference,
+ ODPH_ARRAY_SIZE(tdes_cbc_reference));
+}
+
+static void crypto_test_dec_alg_3des_cbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ tdes_cbc_reference,
+ ODPH_ARRAY_SIZE(tdes_cbc_reference));
+}
+
+static int check_alg_3des_ecb(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_3DES_ECB, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_3des_ecb(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ tdes_ecb_reference,
+ ODPH_ARRAY_SIZE(tdes_ecb_reference));
+}
+
+static void crypto_test_dec_alg_3des_ecb(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ tdes_ecb_reference,
+ ODPH_ARRAY_SIZE(tdes_ecb_reference));
+}
+
+static int check_alg_chacha20_poly1305(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_CHACHA20_POLY1305,
+ ODP_AUTH_ALG_CHACHA20_POLY1305);
+}
+
+static void crypto_test_enc_alg_chacha20_poly1305(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ chacha20_poly1305_reference,
+ ODPH_ARRAY_SIZE(chacha20_poly1305_reference));
+}
+
+static void crypto_test_dec_alg_chacha20_poly1305(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ chacha20_poly1305_reference,
+ ODPH_ARRAY_SIZE(chacha20_poly1305_reference));
+}
+
+static int check_alg_aes_gcm(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_GCM, ODP_AUTH_ALG_AES_GCM);
+}
+
+static void crypto_test_enc_alg_aes_gcm(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_gcm_reference,
+ ODPH_ARRAY_SIZE(aes_gcm_reference));
+}
+
+static void crypto_test_dec_alg_aes_gcm(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_gcm_reference,
+ ODPH_ARRAY_SIZE(aes_gcm_reference));
+}
+
+static int check_alg_aes_ccm(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_CCM, ODP_AUTH_ALG_AES_CCM);
+}
+
+static void crypto_test_enc_alg_aes_ccm(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_ccm_reference,
+ ODPH_ARRAY_SIZE(aes_ccm_reference));
+}
+
+static void crypto_test_dec_alg_aes_ccm(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_ccm_reference,
+ ODPH_ARRAY_SIZE(aes_ccm_reference));
+}
+
+static int check_alg_aes_cbc(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_CBC, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_cbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_cbc_reference,
+ ODPH_ARRAY_SIZE(aes_cbc_reference));
+}
+
+static void crypto_test_dec_alg_aes_cbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_cbc_reference,
+ ODPH_ARRAY_SIZE(aes_cbc_reference));
+}
+
+static int check_alg_aes_ctr(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_CTR, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_ctr(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_ctr_reference,
+ ODPH_ARRAY_SIZE(aes_ctr_reference));
+}
+
+static void crypto_test_dec_alg_aes_ctr(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_ctr_reference,
+ ODPH_ARRAY_SIZE(aes_ctr_reference));
+}
+
+static int check_alg_aes_ecb(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_ECB, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_ecb(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_ecb_reference,
+ ODPH_ARRAY_SIZE(aes_ecb_reference));
+}
+
+static void crypto_test_dec_alg_aes_ecb(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_ecb_reference,
+ ODPH_ARRAY_SIZE(aes_ecb_reference));
+}
+
+static int check_alg_aes_cfb128(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_CFB128, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_cfb128(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_cfb128_reference,
+ ODPH_ARRAY_SIZE(aes_cfb128_reference));
+}
+
+static void crypto_test_dec_alg_aes_cfb128(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_cfb128_reference,
+ ODPH_ARRAY_SIZE(aes_cfb128_reference));
+}
+
+static int check_alg_aes_xts(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_XTS, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_xts(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_xts_reference,
+ ODPH_ARRAY_SIZE(aes_xts_reference));
+}
+
+static void crypto_test_dec_alg_aes_xts(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_xts_reference,
+ ODPH_ARRAY_SIZE(aes_xts_reference));
+}
+
+static int check_alg_kasumi_f8(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_KASUMI_F8, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_kasumi_f8(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ kasumi_f8_reference,
+ ODPH_ARRAY_SIZE(kasumi_f8_reference));
+}
+
+static void crypto_test_dec_alg_kasumi_f8(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ kasumi_f8_reference,
+ ODPH_ARRAY_SIZE(kasumi_f8_reference));
+}
+
+static int check_alg_snow3g_uea2(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_SNOW3G_UEA2, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_snow3g_uea2(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ snow3g_uea2_reference,
+ ODPH_ARRAY_SIZE(snow3g_uea2_reference));
+}
+
+static void crypto_test_dec_alg_snow3g_uea2(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ snow3g_uea2_reference,
+ ODPH_ARRAY_SIZE(snow3g_uea2_reference));
+}
+
+static int check_alg_aes_eea2(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_AES_EEA2,
+ ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_aes_eea2(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_eea2_reference,
+ ODPH_ARRAY_SIZE(aes_eea2_reference));
+}
+
+static void crypto_test_dec_alg_aes_eea2(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_eea2_reference,
+ ODPH_ARRAY_SIZE(aes_eea2_reference));
+}
+
+static int check_alg_zuc_eea3(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_ZUC_EEA3, ODP_AUTH_ALG_NULL);
+}
+
+static void crypto_test_enc_alg_zuc_eea3(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ zuc_eea3_reference,
+ ODPH_ARRAY_SIZE(zuc_eea3_reference));
+}
+
+static void crypto_test_dec_alg_zuc_eea3(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ zuc_eea3_reference,
+ ODPH_ARRAY_SIZE(zuc_eea3_reference));
+}
+
+static int check_alg_hmac_md5(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_MD5_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_md5(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_md5_reference,
+ ODPH_ARRAY_SIZE(hmac_md5_reference));
+}
+
+static void crypto_test_check_alg_hmac_md5(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_md5_reference,
+ ODPH_ARRAY_SIZE(hmac_md5_reference));
+}
+
+static int check_alg_hmac_sha1(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA1_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_sha1(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_sha1_reference,
+ ODPH_ARRAY_SIZE(hmac_sha1_reference));
+}
+
+static void crypto_test_check_alg_hmac_sha1(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_sha1_reference,
+ ODPH_ARRAY_SIZE(hmac_sha1_reference));
+}
+
+static int check_alg_hmac_sha224(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA224_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_sha224(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_sha224_reference,
+ ODPH_ARRAY_SIZE(hmac_sha224_reference));
+}
+
+static void crypto_test_check_alg_hmac_sha224(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_sha224_reference,
+ ODPH_ARRAY_SIZE(hmac_sha224_reference));
+}
+
+static int check_alg_hmac_sha256(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA256_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_sha256(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_sha256_reference,
+ ODPH_ARRAY_SIZE(hmac_sha256_reference));
+}
+
+static void crypto_test_check_alg_hmac_sha256(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_sha256_reference,
+ ODPH_ARRAY_SIZE(hmac_sha256_reference));
+}
+
+static int check_alg_hmac_sha384(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA384_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_sha384(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_sha384_reference,
+ ODPH_ARRAY_SIZE(hmac_sha384_reference));
+}
+
+static void crypto_test_check_alg_hmac_sha384(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_sha384_reference,
+ ODPH_ARRAY_SIZE(hmac_sha384_reference));
+}
+
+static int check_alg_hmac_sha512(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA512_HMAC);
+}
+
+static void crypto_test_gen_alg_hmac_sha512(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ hmac_sha512_reference,
+ ODPH_ARRAY_SIZE(hmac_sha512_reference));
+}
+
+static void crypto_test_check_alg_hmac_sha512(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ hmac_sha512_reference,
+ ODPH_ARRAY_SIZE(hmac_sha512_reference));
+}
+
+static int check_alg_aes_xcbc(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL,
+ ODP_AUTH_ALG_AES_XCBC_MAC);
+}
+
+static void crypto_test_gen_alg_aes_xcbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_xcbc_reference,
+ ODPH_ARRAY_SIZE(aes_xcbc_reference));
+}
+
+static void crypto_test_check_alg_aes_xcbc(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_xcbc_reference,
+ ODPH_ARRAY_SIZE(aes_xcbc_reference));
+}
+
+static int check_alg_aes_gmac(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_AES_GMAC);
+}
+
+static void crypto_test_gen_alg_aes_gmac(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_gmac_reference,
+ ODPH_ARRAY_SIZE(aes_gmac_reference));
+}
+
+static void crypto_test_check_alg_aes_gmac(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_gmac_reference,
+ ODPH_ARRAY_SIZE(aes_gmac_reference));
+}
+
+static int check_alg_aes_cmac(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_AES_CMAC);
+}
+
+static void crypto_test_gen_alg_aes_cmac(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_cmac_reference,
+ ODPH_ARRAY_SIZE(aes_cmac_reference));
+}
+
+static void crypto_test_check_alg_aes_cmac(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_cmac_reference,
+ ODPH_ARRAY_SIZE(aes_cmac_reference));
+}
+
+static int check_alg_kasumi_f9(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_KASUMI_F9);
+}
+
+static void crypto_test_gen_alg_kasumi_f9(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ kasumi_f9_reference,
+ ODPH_ARRAY_SIZE(kasumi_f9_reference));
+}
+
+static void crypto_test_check_alg_kasumi_f9(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ kasumi_f9_reference,
+ ODPH_ARRAY_SIZE(kasumi_f9_reference));
+}
+
+static int check_alg_snow3g_uia2(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SNOW3G_UIA2);
+}
+
+static void crypto_test_gen_alg_snow3g_uia2(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ snow3g_uia2_reference,
+ ODPH_ARRAY_SIZE(snow3g_uia2_reference));
+}
+
+static void crypto_test_check_alg_snow3g_uia2(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ snow3g_uia2_reference,
+ ODPH_ARRAY_SIZE(snow3g_uia2_reference));
+}
+
+static int check_alg_aes_eia2(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL,
+ ODP_AUTH_ALG_AES_EIA2);
+}
+
+static void crypto_test_gen_alg_aes_eia2(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ aes_eia2_reference,
+ ODPH_ARRAY_SIZE(aes_eia2_reference));
+}
+
+static void crypto_test_check_alg_aes_eia2(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ aes_eia2_reference,
+ ODPH_ARRAY_SIZE(aes_eia2_reference));
+}
+
+static int check_alg_zuc_eia3(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_ZUC_EIA3);
+}
+
+static void crypto_test_gen_alg_zuc_eia3(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ zuc_eia3_reference,
+ ODPH_ARRAY_SIZE(zuc_eia3_reference));
+}
+
+static void crypto_test_check_alg_zuc_eia3(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ zuc_eia3_reference,
+ ODPH_ARRAY_SIZE(zuc_eia3_reference));
+}
+
+static int check_alg_md5(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_MD5);
+}
+
+static void crypto_test_gen_alg_md5(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ md5_reference,
+ ODPH_ARRAY_SIZE(md5_reference));
+}
+
+static void crypto_test_check_alg_md5(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ md5_reference,
+ ODPH_ARRAY_SIZE(md5_reference));
+}
+
+static int check_alg_sha1(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA1);
+}
+
+static void crypto_test_gen_alg_sha1(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ sha1_reference,
+ ODPH_ARRAY_SIZE(sha1_reference));
+}
+
+static void crypto_test_check_alg_sha1(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ sha1_reference,
+ ODPH_ARRAY_SIZE(sha1_reference));
+}
+
+static int check_alg_sha224(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA224);
+}
+
+static void crypto_test_gen_alg_sha224(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ sha224_reference,
+ ODPH_ARRAY_SIZE(sha224_reference));
+}
+
+static void crypto_test_check_alg_sha224(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ sha224_reference,
+ ODPH_ARRAY_SIZE(sha224_reference));
+}
+
+static int check_alg_sha256(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA256);
+}
+
+static void crypto_test_gen_alg_sha256(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ sha256_reference,
+ ODPH_ARRAY_SIZE(sha256_reference));
+}
+
+static void crypto_test_check_alg_sha256(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ sha256_reference,
+ ODPH_ARRAY_SIZE(sha256_reference));
+}
+
+static int check_alg_sha384(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA384);
+}
+
+static void crypto_test_gen_alg_sha384(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ sha384_reference,
+ ODPH_ARRAY_SIZE(sha384_reference));
+}
+
+static void crypto_test_check_alg_sha384(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ sha384_reference,
+ ODPH_ARRAY_SIZE(sha384_reference));
+}
+
+static int check_alg_sha512(void)
+{
+ return check_alg_support(ODP_CIPHER_ALG_NULL, ODP_AUTH_ALG_SHA512);
+}
+
+static void crypto_test_gen_alg_sha512(void)
+{
+ check_alg(ODP_CRYPTO_OP_ENCODE,
+ sha512_reference,
+ ODPH_ARRAY_SIZE(sha512_reference));
+}
+
+static void crypto_test_check_alg_sha512(void)
+{
+ check_alg(ODP_CRYPTO_OP_DECODE,
+ sha512_reference,
+ ODPH_ARRAY_SIZE(sha512_reference));
+}
+
+static odp_queue_t sched_compl_queue_create(void)
+{
+ odp_queue_param_t qparam;
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ return odp_queue_create("crypto-out", &qparam);
+}
+
+static odp_queue_t plain_compl_queue_create(void)
+{
+ return odp_queue_create("crypto-out", NULL);
+}
+
+static odp_event_t sched_compl_queue_deq(void)
+{
+ return odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+}
+
+static odp_event_t plain_compl_queue_deq(void)
+{
+ return odp_queue_deq(suite_context.queue);
+}
+
+static int partial_test_only(odp_crypto_op_mode_t op_mode, odp_queue_type_t q_type)
+{
+ odp_crypto_capability_t capa;
+
+ if (full_test || odp_crypto_capability(&capa))
+ return 0;
+
+ if (!capa.async_mode)
+ return 0;
+
+ if (op_mode == ODP_CRYPTO_SYNC)
+ return 1;
+ else if (q_type == ODP_QUEUE_TYPE_PLAIN && capa.queue_type_sched)
+ return 1;
+
+ return 0;
+}
+
+static int crypto_suite_packet_sync_init(void)
+{
+ suite_context.op_mode = ODP_CRYPTO_SYNC;
+
+ suite_context.pool = odp_pool_lookup("packet_pool");
+ if (suite_context.pool == ODP_POOL_INVALID)
+ return -1;
+
+ suite_context.queue = ODP_QUEUE_INVALID;
+ suite_context.partial_test = partial_test_only(suite_context.op_mode,
+ ODP_QUEUE_TYPE_PLAIN);
+ return 0;
+}
+
+static int crypto_suite_packet_async_plain_init(void)
+{
+ odp_queue_t out_queue;
+
+ suite_context.op_mode = ODP_CRYPTO_ASYNC;
+
+ suite_context.pool = odp_pool_lookup("packet_pool");
+ if (suite_context.pool == ODP_POOL_INVALID)
+ return -1;
+
+ out_queue = plain_compl_queue_create();
+ if (ODP_QUEUE_INVALID == out_queue) {
+ ODPH_ERR("Crypto outq creation failed\n");
+ return -1;
+ }
+ suite_context.queue = out_queue;
+ suite_context.q_type = ODP_QUEUE_TYPE_PLAIN;
+ suite_context.compl_queue_deq = plain_compl_queue_deq;
+ suite_context.partial_test = partial_test_only(suite_context.op_mode,
+ suite_context.q_type);
+
+ return 0;
+}
+
+static int crypto_suite_packet_async_sched_init(void)
+{
+ odp_queue_t out_queue;
+
+ suite_context.op_mode = ODP_CRYPTO_ASYNC;
+
+ suite_context.pool = odp_pool_lookup("packet_pool");
+ if (suite_context.pool == ODP_POOL_INVALID)
+ return -1;
+
+ out_queue = sched_compl_queue_create();
+ if (ODP_QUEUE_INVALID == out_queue) {
+ ODPH_ERR("Crypto outq creation failed\n");
+ return -1;
+ }
+ suite_context.queue = out_queue;
+ suite_context.q_type = ODP_QUEUE_TYPE_SCHED;
+ suite_context.compl_queue_deq = sched_compl_queue_deq;
+ suite_context.partial_test = partial_test_only(suite_context.op_mode,
+ suite_context.q_type);
+
+ return 0;
+}
+
+static int crypto_suite_term(void)
+{
+ if (ODP_QUEUE_INVALID != suite_context.queue) {
+ if (odp_queue_destroy(suite_context.queue))
+ ODPH_ERR("Crypto outq destroy failed\n");
+ } else {
+ ODPH_ERR("Crypto outq not found\n");
+ }
+
+ return odp_cunit_print_inactive();
+}
+
+odp_testinfo_t crypto_suite[] = {
+ ODP_TEST_INFO(test_capability),
+ ODP_TEST_INFO(test_default_values),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_null,
+ check_alg_null),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_null,
+ check_alg_null),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_3des_cbc,
+ check_alg_3des_cbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_3des_cbc,
+ check_alg_3des_cbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_3des_ecb,
+ check_alg_3des_ecb),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_3des_ecb,
+ check_alg_3des_ecb),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_cbc,
+ check_alg_aes_cbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_cbc,
+ check_alg_aes_cbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_ctr,
+ check_alg_aes_ctr),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_ctr,
+ check_alg_aes_ctr),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_ecb,
+ check_alg_aes_ecb),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_ecb,
+ check_alg_aes_ecb),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_cfb128,
+ check_alg_aes_cfb128),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_cfb128,
+ check_alg_aes_cfb128),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_xts,
+ check_alg_aes_xts),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_xts,
+ check_alg_aes_xts),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_kasumi_f8,
+ check_alg_kasumi_f8),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_kasumi_f8,
+ check_alg_kasumi_f8),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_snow3g_uea2,
+ check_alg_snow3g_uea2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_snow3g_uea2,
+ check_alg_snow3g_uea2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_eea2,
+ check_alg_aes_eea2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_eea2,
+ check_alg_aes_eea2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_zuc_eea3,
+ check_alg_zuc_eea3),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_zuc_eea3,
+ check_alg_zuc_eea3),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_gcm,
+ check_alg_aes_gcm),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_gcm,
+ check_alg_aes_gcm),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_aes_ccm,
+ check_alg_aes_ccm),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_aes_ccm,
+ check_alg_aes_ccm),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_enc_alg_chacha20_poly1305,
+ check_alg_chacha20_poly1305),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_dec_alg_chacha20_poly1305,
+ check_alg_chacha20_poly1305),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_md5,
+ check_alg_hmac_md5),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_md5,
+ check_alg_hmac_md5),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha1,
+ check_alg_hmac_sha1),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha1,
+ check_alg_hmac_sha1),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha224,
+ check_alg_hmac_sha224),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha224,
+ check_alg_hmac_sha224),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha256,
+ check_alg_hmac_sha256),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha256,
+ check_alg_hmac_sha256),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha384,
+ check_alg_hmac_sha384),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha384,
+ check_alg_hmac_sha384),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_hmac_sha512,
+ check_alg_hmac_sha512),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_hmac_sha512,
+ check_alg_hmac_sha512),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_aes_xcbc,
+ check_alg_aes_xcbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_aes_xcbc,
+ check_alg_aes_xcbc),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_aes_gmac,
+ check_alg_aes_gmac),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_aes_gmac,
+ check_alg_aes_gmac),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_aes_cmac,
+ check_alg_aes_cmac),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_aes_cmac,
+ check_alg_aes_cmac),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_kasumi_f9,
+ check_alg_kasumi_f9),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_kasumi_f9,
+ check_alg_kasumi_f9),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_snow3g_uia2,
+ check_alg_snow3g_uia2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_snow3g_uia2,
+ check_alg_snow3g_uia2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_aes_eia2,
+ check_alg_aes_eia2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_aes_eia2,
+ check_alg_aes_eia2),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_zuc_eia3,
+ check_alg_zuc_eia3),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_zuc_eia3,
+ check_alg_zuc_eia3),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_md5,
+ check_alg_md5),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_md5,
+ check_alg_md5),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_sha1,
+ check_alg_sha1),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_sha1,
+ check_alg_sha1),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_sha224,
+ check_alg_sha224),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_sha224,
+ check_alg_sha224),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_sha256,
+ check_alg_sha256),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_sha256,
+ check_alg_sha256),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_sha384,
+ check_alg_sha384),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_sha384,
+ check_alg_sha384),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_gen_alg_sha512,
+ check_alg_sha512),
+ ODP_TEST_INFO_CONDITIONAL(crypto_test_check_alg_sha512,
+ check_alg_sha512),
+ ODP_TEST_INFO(test_auth_hashes_in_auth_range),
+ ODP_TEST_INFO(test_all_combinations),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t crypto_suites[] = {
+ {"odp_crypto_packet_sync_inp", crypto_suite_packet_sync_init,
+ NULL, crypto_suite},
+ {"odp_crypto_packet_async_plain_inp",
+ crypto_suite_packet_async_plain_init,
+ crypto_suite_term, crypto_suite},
+ {"odp_crypto_packet_async_sched_inp",
+ crypto_suite_packet_async_sched_init,
+ crypto_suite_term, crypto_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+static int crypto_init(odp_instance_t *inst)
+{
+ odp_pool_param_t params;
+ odp_pool_t pool;
+ odp_pool_capability_t pool_capa;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ /* Configure the scheduler. */
+ if (odp_schedule_config(NULL)) {
+ ODPH_ERR("odp_schedule_config() failed\n");
+ return -1;
+ }
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_LEN;
+ params.pkt.len = PKT_POOL_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ /*
+ * Let's have a user area so that we can check that its
+ * content gets copied along with other metadata when needed.
+ */
+ if (pool_capa.pkt.max_uarea_size >= UAREA_SIZE)
+ params.pkt.uarea_size = UAREA_SIZE;
+ else
+ printf("Warning: could not request packet user area\n");
+
+ if (pool_capa.pkt.max_seg_len &&
+ PKT_POOL_LEN > pool_capa.pkt.max_seg_len) {
+ ODPH_ERR("Warning: small packet segment length\n");
+ params.pkt.seg_len = pool_capa.pkt.max_seg_len;
+ }
+
+ if (pool_capa.pkt.max_len &&
+ PKT_POOL_LEN > pool_capa.pkt.max_len) {
+ ODPH_ERR("Pool max packet length too small\n");
+ return -1;
+ }
+
+ pool = odp_pool_create("packet_pool", &params);
+
+ if (ODP_POOL_INVALID == pool) {
+ ODPH_ERR("Packet pool creation failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_term(odp_instance_t inst)
+{
+ odp_pool_t pool;
+
+ pool = odp_pool_lookup("packet_pool");
+ if (ODP_POOL_INVALID != pool) {
+ if (odp_pool_destroy(pool))
+ ODPH_ERR("Packet pool destroy failed\n");
+ } else {
+ ODPH_ERR("Packet pool not found\n");
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ char *env = getenv("FULL_TEST");
+
+ if (env && strcmp(env, "0"))
+ full_test = 1;
+ printf("Test mode: %s\n", full_test ? "full" : "partial");
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(crypto_init);
+ odp_cunit_register_global_term(crypto_term);
+
+ ret = odp_cunit_register(crypto_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/crypto/test_vector_defs.h b/test/validation/api/crypto/test_vector_defs.h
new file mode 100644
index 000000000..6c2eb2085
--- /dev/null
+++ b/test/validation/api/crypto/test_vector_defs.h
@@ -0,0 +1,3165 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef TEST_VECTOR_DEFS_H
+#define TEST_VECTOR_DEFS_H
+
+#include <odp_api.h>
+#include "test_vectors.h"
+
+ODP_STATIC_ASSERT(ODP_CIPHER_ALG_NULL == 0, "null cipher is not the default");
+ODP_STATIC_ASSERT(ODP_AUTH_ALG_NULL == 0, "null auth is not the default");
+
+static crypto_test_reference_t null_reference[] = {
+ {
+ .length = 8,
+ .plaintext = { 0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56 },
+ .ciphertext = { 0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56 }
+ },
+ {
+ .length = 16,
+ .plaintext = { 0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87,
+ 0x6d, 0x8e, 0xa2, 0x30, 0x94, 0xea, 0x53, 0x09 },
+ .ciphertext = { 0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87,
+ 0x6d, 0x8e, 0xa2, 0x30, 0x94, 0xea, 0x53, 0x09 }
+ }
+};
+
+/* TDES-CBC reference vectors, according to
+ * "http://csrc.nist.gov/groups/STM/cavp/documents/des/DESMMT.pdf"
+ */
+static crypto_test_reference_t tdes_cbc_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key_length = TDES_CBC_KEY_LEN,
+ .cipher_key = { 0x62, 0x7f, 0x46, 0x0e, 0x08, 0x10, 0x4a, 0x10,
+ 0x43, 0xcd, 0x26, 0x5d, 0x58, 0x40, 0xea, 0xf1,
+ 0x31, 0x3e, 0xdf, 0x97, 0xdf, 0x2a, 0x8a, 0x8c},
+ .cipher_iv_length = TDES_CBC_IV_LEN,
+ .cipher_iv = { 0x8e, 0x29, 0xf7, 0x5e, 0xa7, 0x7e, 0x54, 0x75 },
+ .length = 8,
+ .plaintext = { 0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56 },
+ .ciphertext = { 0xb2, 0x2b, 0x8d, 0x66, 0xde, 0x97, 0x06, 0x92 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_3DES_CBC,
+ .cipher_key_length = TDES_CBC_KEY_LEN,
+ .cipher_key = { 0x37, 0xae, 0x5e, 0xbf, 0x46, 0xdf, 0xf2, 0xdc,
+ 0x07, 0x54, 0xb9, 0x4f, 0x31, 0xcb, 0xb3, 0x85,
+ 0x5e, 0x7f, 0xd3, 0x6d, 0xc8, 0x70, 0xbf, 0xae},
+ .cipher_iv_length = TDES_CBC_IV_LEN,
+ .cipher_iv = {0x3d, 0x1d, 0xe3, 0xcc, 0x13, 0x2e, 0x3b, 0x65 },
+ .length = 16,
+ .plaintext = { 0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87,
+ 0x6d, 0x8e, 0xa2, 0x30, 0x94, 0xea, 0x53, 0x09 },
+ .ciphertext = { 0x7b, 0x1f, 0x7c, 0x7e, 0x3b, 0x1c, 0x94, 0x8e,
+ 0xbd, 0x04, 0xa7, 0x5f, 0xfb, 0xa7, 0xd2, 0xf5 }
+ }
+};
+
+/*
+ * TDES-ECB reference vectors, according to
+ * CAVS 18.0 TECBMMT
+ */
+static crypto_test_reference_t tdes_ecb_reference[] = {
+ /* CAVS 18.0 TECBMMT2.rsp #0 */
+ {
+ .cipher = ODP_CIPHER_ALG_3DES_ECB,
+ .cipher_key_length = TDES_ECB_KEY_LEN,
+ .cipher_key = { 0x15, 0x1f, 0x10, 0x38, 0x3d, 0x6d, 0x19, 0x9b,
+ 0x4a, 0x76, 0x3b, 0xd5, 0x4a, 0x46, 0xa4, 0x45,
+ 0x15, 0x1f, 0x10, 0x38, 0x3d, 0x6d, 0x19, 0x9b},
+ .length = 8,
+ .plaintext = { 0xd8, 0xda, 0x89, 0x29, 0x88, 0x78, 0xed, 0x7d },
+ .ciphertext = { 0x89, 0x32, 0x1b, 0xa7, 0x5b, 0xa5, 0x45, 0xdb }
+ },
+ /* CAVS 18.0 TECBMMT2.rsp #2 */
+ {
+ .cipher = ODP_CIPHER_ALG_3DES_ECB,
+ .cipher_key_length = TDES_ECB_KEY_LEN,
+ .cipher_key = { 0xcd, 0x3d, 0x9b, 0xf7, 0x2f, 0x8c, 0x8a, 0xb5,
+ 0xfe, 0xe6, 0x73, 0x34, 0x31, 0x1c, 0xa4, 0x62,
+ 0xcd, 0x3d, 0x9b, 0xf7, 0x2f, 0x8c, 0x8a, 0xb5},
+ .length = 24,
+ .plaintext = { 0x2f, 0x2a, 0x36, 0x1c, 0x8e, 0x14, 0x5d, 0xc0,
+ 0xa7, 0x4a, 0x1b, 0xdb, 0x7c, 0xa9, 0x29, 0xc3,
+ 0x38, 0x14, 0x4d, 0x89, 0x13, 0x5b, 0x50, 0xa7 },
+ .ciphertext = { 0x7f, 0x1f, 0xd3, 0x2b, 0x36, 0x90, 0x05, 0x4b,
+ 0xfa, 0x1b, 0x17, 0x35, 0x15, 0x79, 0x33, 0x80,
+ 0x99, 0xff, 0xa8, 0x4f, 0xea, 0x16, 0x8c, 0x6b }
+ }
+};
+
+static crypto_test_reference_t aes_cbc_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+ 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+ 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+ .length = 16,
+ .plaintext = "Single block msg",
+ .ciphertext = { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+ 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+ 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+ 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+ .length = 32,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+ .ciphertext = { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+ 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+ 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+ 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x6c, 0x3e, 0xa0, 0x47, 0x76, 0x30, 0xce, 0x21,
+ 0xa2, 0xce, 0x33, 0x4a, 0xa7, 0x46, 0xc2, 0xcd},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0xc7, 0x82, 0xdc, 0x4c, 0x09, 0x8c, 0x66, 0xcb,
+ 0xd9, 0xcd, 0x27, 0xd8, 0x25, 0x68, 0x2c, 0x81 },
+ .length = 48,
+ .plaintext = "This is a 48-byte message (exactly 3 AES blocks)",
+ .ciphertext = { 0xd0, 0xa0, 0x2b, 0x38, 0x36, 0x45, 0x17, 0x53,
+ 0xd4, 0x93, 0x66, 0x5d, 0x33, 0xf0, 0xe8, 0x86,
+ 0x2d, 0xea, 0x54, 0xcd, 0xb2, 0x93, 0xab, 0xc7,
+ 0x50, 0x69, 0x39, 0x27, 0x67, 0x72, 0xf8, 0xd5,
+ 0x02, 0x1c, 0x19, 0x21, 0x6b, 0xad, 0x52, 0x5c,
+ 0x85, 0x79, 0x69, 0x5d, 0x83, 0xba, 0x26, 0x84 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x56, 0xe4, 0x7a, 0x38, 0xc5, 0x59, 0x89, 0x74,
+ 0xbc, 0x46, 0x90, 0x3d, 0xba, 0x29, 0x03, 0x49},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x8c, 0xe8, 0x2e, 0xef, 0xbe, 0xa0, 0xda, 0x3c,
+ 0x44, 0x69, 0x9e, 0xd7, 0xdb, 0x51, 0xb7, 0xd9 },
+ .length = 64,
+ .plaintext = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf },
+ .ciphertext = { 0xc3, 0x0e, 0x32, 0xff, 0xed, 0xc0, 0x77, 0x4e,
+ 0x6a, 0xff, 0x6a, 0xf0, 0x86, 0x9f, 0x71, 0xaa,
+ 0x0f, 0x3a, 0xf0, 0x7a, 0x9a, 0x31, 0xa9, 0xc6,
+ 0x84, 0xdb, 0x20, 0x7e, 0xb0, 0xef, 0x8e, 0x4e,
+ 0x35, 0x90, 0x7a, 0xa6, 0x32, 0xc3, 0xff, 0xdf,
+ 0x86, 0x8b, 0xb7, 0xb2, 0x9d, 0x3d, 0x46, 0xad,
+ 0x83, 0xce, 0x9f, 0x9a, 0x10, 0x2e, 0xe9, 0x9d,
+ 0x49, 0xa5, 0x3e, 0x87, 0xf4, 0xc3, 0xda, 0x55 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ 0xde, 0xca, 0xf8, 0x88, 0x01, 0x23, 0x45, 0x67 },
+ .length = 32,
+ .plaintext = { 0x45, 0x00, 0x00, 0x28, 0xa4, 0xad, 0x40, 0x00,
+ 0x40, 0x06, 0x78, 0x80, 0x0a, 0x01, 0x03, 0x8f,
+ 0x0a, 0x01, 0x06, 0x12, 0x80, 0x23, 0x06, 0xb8,
+ 0xcb, 0x71, 0x26, 0x02, 0xdd, 0x6b, 0xb0, 0x3e },
+ .ciphertext = { 0x0d, 0xbe, 0x02, 0xda, 0x68, 0x9c, 0x8f, 0x30,
+ 0xce, 0x7c, 0x91, 0x7d, 0x41, 0x08, 0xf6, 0xf1,
+ 0x8e, 0x0d, 0x7f, 0x02, 0xb6, 0x80, 0x9a, 0x2d,
+ 0x53, 0x1c, 0xc6, 0x98, 0x85, 0xc3, 0x00, 0xe6},
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d,
+ 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
+ 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4,
+ 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
+ 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0,
+ 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
+ 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81,
+ 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab,
+ 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x11, 0x22, 0x33, 0x44, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c },
+ .length = 48,
+ .plaintext = { 0x45, 0x00, 0x00, 0x30, 0x69, 0xa6, 0x40, 0x00,
+ 0x80, 0x06, 0x26, 0x90, 0xc0, 0xa8, 0x01, 0x02,
+ 0x93, 0x89, 0x15, 0x5e, 0x0a, 0x9e, 0x00, 0x8b,
+ 0x2d, 0xc5, 0x7e, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x02, 0x40, 0x00, 0x20, 0xbf, 0x00, 0x00,
+ 0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x04, 0x02 },
+ .ciphertext = { 0x92, 0x1e, 0x2f, 0x37, 0x36, 0x3c, 0x45, 0xda,
+ 0xc9, 0x58, 0xb7, 0x07, 0x06, 0x56, 0x54, 0xc5,
+ 0x93, 0x46, 0x90, 0xb8, 0xcf, 0x0d, 0x4f, 0x79,
+ 0xf1, 0x32, 0xc2, 0xf7, 0x23, 0xb8, 0x83, 0x09,
+ 0xbc, 0x37, 0x1c, 0xeb, 0x95, 0x2c, 0x42, 0x7b,
+ 0x39, 0x10, 0xa8, 0x76, 0xfa, 0xbe, 0x91, 0xe9},
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CBC,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4},
+ .cipher_iv_length = AES_CBC_IV_LEN,
+ .cipher_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba,
+ 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
+ 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d,
+ 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
+ 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf,
+ 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
+ 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc,
+ 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b }
+ }
+};
+
+static crypto_test_reference_t aes_ctr_reference[] = {
+ /* RFC3686 https://tools.ietf.org/html/rfc3686 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7,
+ 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59,
+ 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 },
+ .length = 32,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F },
+ .ciphertext = { 0x51, 0x04, 0xA1, 0x06, 0x16, 0x8A, 0x72, 0xD9,
+ 0x79, 0x0D, 0x41, 0xEE, 0x8E, 0xDA, 0xD3, 0x88,
+ 0xEB, 0x2E, 0x1E, 0xFC, 0x46, 0xDA, 0x57, 0xC8,
+ 0xFC, 0xE6, 0x30, 0xDF, 0x91, 0x41, 0xBE, 0x28}
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee }
+ },
+ /* Generated by Crypto++ 5.6.1 (715 bytes data)*/
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff },
+ .length = 715,
+ .plaintext = { 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f,
+ 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
+ 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac,
+ 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e,
+ 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4,
+ 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52,
+ 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b,
+ 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37,
+ 0x10, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40,
+ 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
+ 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03,
+ 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf,
+ 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c,
+ 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a,
+ 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f,
+ 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c,
+ 0x37, 0x10, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e,
+ 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73,
+ 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e,
+ 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45,
+ 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3,
+ 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a,
+ 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf,
+ 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6,
+ 0x6c, 0x37, 0x10, 0x00, 0x6b, 0xc1, 0xbe, 0xe2,
+ 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+ 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57,
+ 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
+ 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+ 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
+ 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45,
+ 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
+ 0xe6, 0x6c, 0x37, 0x10, 0x00, 0x6b, 0xc1, 0xbe,
+ 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
+ 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a,
+ 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f,
+ 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c,
+ 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
+ 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24,
+ 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41,
+ 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00, 0x6b, 0xc1,
+ 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
+ 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d,
+ 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7,
+ 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8,
+ 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb,
+ 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f,
+ 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b,
+ 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00, 0x6b,
+ 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9,
+ 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+ 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+ 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30,
+ 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5,
+ 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6,
+ 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad,
+ 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
+ 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f,
+ 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
+ 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac,
+ 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e,
+ 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4,
+ 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52,
+ 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b,
+ 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37,
+ 0x10, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40,
+ 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
+ 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03,
+ 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf,
+ 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c,
+ 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a,
+ 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f,
+ 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c,
+ 0x37, 0x10, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e,
+ 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73,
+ 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e,
+ 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45,
+ 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3,
+ 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a,
+ 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf,
+ 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6,
+ 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xEC, 0xE7, 0x1E, 0xCD, 0x7A, 0x4E, 0x3C, 0x2F,
+ 0x64, 0x3B, 0x2B, 0x0B, 0xFB, 0xED, 0x32, 0xF3,
+ 0x1C, 0x85, 0x51, 0xB6, 0x30, 0x6D, 0x52, 0xCF,
+ 0x84, 0x3E, 0xC0, 0xB8, 0x50, 0x15, 0xDC, 0x20,
+ 0x3B, 0x1C, 0x0B, 0x64, 0x3E, 0x2A, 0x6B, 0xAB,
+ 0xAF, 0x51, 0x33, 0xDA, 0x0E, 0xA0, 0x66, 0x16,
+ 0x07, 0x6A, 0xA6, 0xBB, 0xB5, 0x2E, 0xD7, 0x5D,
+ 0xC3, 0xA7, 0x1A, 0x9A, 0x6E, 0x8A, 0xC7, 0xC9,
+ 0xA0, 0x0D, 0x2C, 0x39, 0xAA, 0x68, 0xBF, 0x4E,
+ 0x6F, 0xFE, 0xD9, 0xAA, 0xEE, 0x5A, 0xD6, 0x91,
+ 0x4F, 0xB3, 0xEA, 0x77, 0xC7, 0xB6, 0x1F, 0xF6,
+ 0xBF, 0x56, 0x4F, 0x2F, 0x12, 0x25, 0xAC, 0xB4,
+ 0xB5, 0x88, 0x9C, 0xB1, 0x55, 0x98, 0x88, 0xA5,
+ 0x81, 0x78, 0x49, 0xC3, 0x82, 0xE1, 0x68, 0x48,
+ 0x2F, 0x75, 0x38, 0x1F, 0x63, 0x86, 0x8C, 0x46,
+ 0x8E, 0x4D, 0x15, 0x83, 0xB1, 0xFE, 0x71, 0xDD,
+ 0x80, 0x8C, 0xB9, 0x4D, 0x81, 0x50, 0xAA, 0xB9,
+ 0xD5, 0x30, 0xA0, 0xFC, 0x17, 0xCD, 0xE7, 0x48,
+ 0xE9, 0x55, 0x45, 0xD8, 0xA0, 0x33, 0xB2, 0xF6,
+ 0x1F, 0x19, 0x54, 0xD0, 0xC0, 0x22, 0x61, 0x68,
+ 0x02, 0x2E, 0x1C, 0xD7, 0xE0, 0x31, 0xC5, 0x7D,
+ 0x04, 0x8A, 0xC5, 0x60, 0xF1, 0x52, 0x96, 0x0F,
+ 0x47, 0x70, 0x5E, 0x17, 0x4D, 0x95, 0x6D, 0x4B,
+ 0xB5, 0x3A, 0xE8, 0x0B, 0xFF, 0xCD, 0x1B, 0xD5,
+ 0x69, 0xED, 0x8E, 0xFF, 0xA2, 0x23, 0xC0, 0x05,
+ 0x58, 0xB7, 0x02, 0x40, 0x5F, 0x33, 0xE6, 0xE0,
+ 0xED, 0xB2, 0xD9, 0xB0, 0xC1, 0x48, 0xA1, 0x44,
+ 0x1C, 0xC8, 0x0D, 0x6A, 0xBB, 0xCE, 0x78, 0x5A,
+ 0xA1, 0xB9, 0xDA, 0xB7, 0xCB, 0x88, 0x32, 0xF1,
+ 0xB1, 0x2D, 0x2E, 0xE6, 0x0E, 0xE2, 0xDF, 0xCA,
+ 0x37, 0x94, 0x2C, 0xA1, 0x72, 0x4E, 0x56, 0x02,
+ 0xB7, 0xB7, 0x05, 0x25, 0xAC, 0x96, 0x62, 0x02,
+ 0x8A, 0x22, 0xDB, 0x23, 0x46, 0x76, 0x61, 0x5D,
+ 0xB4, 0x74, 0x53, 0x8C, 0xBC, 0x8D, 0x19, 0x7F,
+ 0x38, 0xC8, 0x8B, 0xCC, 0x4F, 0x9E, 0x8D, 0x20,
+ 0x75, 0x38, 0xCA, 0x18, 0xDE, 0x5F, 0x09, 0x54,
+ 0x20, 0xA2, 0xE4, 0xD5, 0x86, 0x8C, 0xEB, 0xB8,
+ 0xB3, 0x4A, 0x93, 0x77, 0xDC, 0x52, 0xD1, 0x19,
+ 0x79, 0x0B, 0x65, 0x21, 0x0F, 0x1B, 0x34, 0x6F,
+ 0x5E, 0x00, 0xD9, 0xBD, 0x00, 0xA8, 0x84, 0x70,
+ 0x48, 0x91, 0x3D, 0x80, 0x72, 0x6B, 0x9B, 0x74,
+ 0x5D, 0x56, 0x5E, 0x62, 0x84, 0xB9, 0x86, 0xDB,
+ 0xAE, 0xA9, 0x97, 0xFF, 0xC5, 0xA0, 0xDE, 0x50,
+ 0x51, 0x52, 0x7D, 0x44, 0xB2, 0xC1, 0x26, 0x6D,
+ 0xBC, 0x91, 0x30, 0xA6, 0xEB, 0x15, 0xF3, 0x7A,
+ 0x0F, 0x00, 0xB6, 0x28, 0x6D, 0x66, 0x78, 0xCA,
+ 0x65, 0x1C, 0x07, 0x74, 0x3B, 0xD3, 0x7F, 0x2E,
+ 0x8F, 0x6A, 0x94, 0xF5, 0xED, 0x8C, 0x63, 0x42,
+ 0x8A, 0xE4, 0x88, 0x3A, 0x96, 0x95, 0x18, 0x38,
+ 0x07, 0xE1, 0x04, 0xBC, 0x33, 0x5C, 0x64, 0xFE,
+ 0xAA, 0xC4, 0x0A, 0x60, 0x59, 0x13, 0xDF, 0x98,
+ 0xFF, 0x44, 0xE0, 0x80, 0x1B, 0x31, 0xA9, 0x68,
+ 0xCC, 0xE5, 0xDC, 0xAF, 0xAD, 0xE1, 0xE0, 0x17,
+ 0xFA, 0x71, 0x1E, 0x05, 0xFF, 0x5A, 0x54, 0xBF,
+ 0xA1, 0x99, 0x9C, 0x2C, 0x46, 0x3F, 0x97, 0xA3,
+ 0xA6, 0x6B, 0x30, 0x21, 0x1B, 0xD3, 0x06, 0xC8,
+ 0x91, 0x1C, 0x98, 0xF8, 0xEE, 0x5E, 0xF4, 0x7A,
+ 0x54, 0x74, 0x6A, 0x4D, 0x16, 0xB7, 0xC7, 0x42,
+ 0x4A, 0x69, 0x54, 0xB4, 0xFC, 0x3B, 0xCF, 0x1A,
+ 0x41, 0xBD, 0xE8, 0xA1, 0x9C, 0xE1, 0x02, 0x7A,
+ 0xE8, 0x6A, 0x32, 0x0D, 0x0E, 0x5E, 0x7D, 0x3C,
+ 0x7E, 0x50, 0xCF, 0xD0, 0xC4, 0x66, 0x5B, 0x81,
+ 0x1D, 0x86, 0xC3, 0x13, 0xF0, 0x9A, 0xDE, 0x5B,
+ 0x4D, 0xBE, 0x01, 0x72, 0x31, 0x85, 0x98, 0x81,
+ 0xE5, 0x87, 0x3E, 0x9E, 0xDB, 0x20, 0x11, 0xCF,
+ 0x59, 0x20, 0xD2, 0xF7, 0x27, 0x7C, 0x4D, 0xE1,
+ 0xAC, 0x43, 0x0A, 0x18, 0x49, 0xF0, 0xB8, 0x70,
+ 0xA6, 0x9A, 0xBE, 0x70, 0x1B, 0x6D, 0x0B, 0x51,
+ 0x23, 0xE5, 0xFF, 0x53, 0x39, 0x54, 0x09, 0x17,
+ 0x7C, 0xF8, 0x4B, 0xF4, 0x1E, 0xC3, 0x3C, 0x5E,
+ 0x4B, 0xCC, 0x2C, 0xF2, 0x92, 0x58, 0xDC, 0x7C,
+ 0x26, 0x04, 0x71, 0xAA, 0xBD, 0xA4, 0x9F, 0xDE,
+ 0x62, 0x91, 0x57, 0x58, 0xEE, 0x4E, 0x57, 0x8D,
+ 0x0F, 0x76, 0x98, 0xE6, 0x45, 0x6B, 0xC1, 0x44,
+ 0x57, 0x37, 0x39, 0xD5, 0xD5, 0x08, 0xCC, 0x76,
+ 0xB3, 0x89, 0x35, 0x9D, 0x2A, 0x0E, 0xCB, 0x5B,
+ 0x7E, 0xE5, 0xFC, 0xB4, 0xC3, 0x15, 0x1D, 0x5A,
+ 0xF7, 0xC7, 0x18, 0x19, 0xEA, 0x3D, 0xD5, 0xF3,
+ 0x6C, 0x7B, 0x27, 0xE5, 0x51, 0xFD, 0x23, 0x73,
+ 0xD0, 0x7F, 0xFD, 0xC7, 0x6A, 0x13, 0xFC, 0x4B,
+ 0x10, 0xA6, 0xF2, 0x9A, 0x83, 0xD6, 0xF4, 0x65,
+ 0xAC, 0xB6, 0x96, 0x06, 0x71, 0xEA, 0xCF, 0x21,
+ 0xA3, 0xE1, 0xCB, 0x44, 0x11, 0xC4, 0xDA, 0xA0,
+ 0xC2, 0xA8, 0x7D, 0xAE, 0xD2, 0x8A, 0xEE, 0x60,
+ 0xB7, 0xEC, 0x02, 0x58, 0xA9, 0xAF, 0x12, 0x5F,
+ 0x2D, 0xDC, 0x80, 0xB9, 0x87, 0x7E, 0xFE, 0x0F,
+ 0x37, 0x2D, 0x9B, 0x83, 0x2C, 0x78, 0x67, 0x70,
+ 0xA8, 0x4E, 0xA1, 0xA0, 0x7C, 0xB6, 0xE1, 0xA9,
+ 0x90, 0x7D, 0x65, 0x1B, 0xBD, 0x0E, 0xFD, 0xEF,
+ 0x2A, 0xFF, 0xC3 }
+ },
+ /* RFC3686 https://tools.ietf.org/html/rfc3686 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0x02, 0xBF, 0x39, 0x1E, 0xE8, 0xEC, 0xB1, 0x59,
+ 0xB9, 0x59, 0x61, 0x7B, 0x09, 0x65, 0x27, 0x9B,
+ 0xF5, 0x9B, 0x60, 0xA7, 0x86, 0xD3, 0xE0, 0xFE},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0x00, 0x07, 0xBD, 0xFD, 0x5C, 0xBD, 0x60, 0x27,
+ 0x8D, 0xCC, 0x09, 0x12, 0x00, 0x00, 0x00, 0x01 },
+ .length = 36,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23 },
+ .ciphertext = { 0x96, 0x89, 0x3F, 0xC5, 0x5E, 0x5C, 0x72, 0x2F,
+ 0x54, 0x0B, 0x7D, 0xD1, 0xDD, 0xF7, 0xE7, 0x58,
+ 0xD2, 0x88, 0xBC, 0x95, 0xC6, 0x91, 0x65, 0x88,
+ 0x45, 0x36, 0xC8, 0x11, 0x66, 0x2F, 0x21, 0x88,
+ 0xAB, 0xEE, 0x09, 0x35 }
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2,
+ 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
+ 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef,
+ 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
+ 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70,
+ 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
+ 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58,
+ 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50 }
+ },
+ /* RFC3686 https://tools.ietf.org/html/rfc3686 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4,
+ 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,
+ 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF,
+ 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0x00, 0x1C, 0xC5, 0xB7, 0x51, 0xA5, 0x1D, 0x70,
+ 0xA1, 0xC1, 0x11, 0x48, 0x00, 0x00, 0x00, 0x01 },
+ .length = 36,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23 },
+ .ciphertext = { 0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7,
+ 0xCE, 0x75, 0x94, 0x46, 0x2A, 0xCA, 0x4F, 0xAA,
+ 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07,
+ 0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F,
+ 0x1E, 0xC0, 0xE6, 0xB8 },
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CTR,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4},
+ .cipher_iv_length = AES_CTR_IV_LEN,
+ .cipher_iv = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5,
+ 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
+ 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a,
+ 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
+ 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c,
+ 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
+ 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6,
+ 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6 }
+ },
+};
+
+static crypto_test_reference_t aes_ecb_reference[] = {
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_ECB,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10},
+ .ciphertext = { 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60,
+ 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,
+ 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d,
+ 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf,
+ 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23,
+ 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88,
+ 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f,
+ 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4 }
+ },
+ /* Generated by Crypto++ 5.6.1 (528 bytes) */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_ECB,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
+ .length = 528,
+ .plaintext = { 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f,
+ 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
+ 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac,
+ 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e,
+ 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4,
+ 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52,
+ 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b,
+ 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37,
+ 0x10, 0x00, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e,
+ 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73,
+ 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e,
+ 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45,
+ 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3,
+ 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a,
+ 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf,
+ 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6,
+ 0x6c, 0x37, 0x10, 0x00, 0x00, 0x6b, 0xc1, 0xbe,
+ 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
+ 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a,
+ 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f,
+ 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c,
+ 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
+ 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24,
+ 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41,
+ 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00, 0x00, 0x6b,
+ 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9,
+ 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+ 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+ 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30,
+ 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5,
+ 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6,
+ 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad,
+ 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00,
+ 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f,
+ 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
+ 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac,
+ 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e,
+ 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4,
+ 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52,
+ 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b,
+ 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37,
+ 0x10, 0x00, 0x00, 0x6b, 0xc1, 0xbe, 0xe2, 0x2e,
+ 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73,
+ 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e,
+ 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45,
+ 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3,
+ 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a,
+ 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf,
+ 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6,
+ 0x6c, 0x37, 0x10, 0x00, 0x00, 0x6b, 0xc1, 0xbe,
+ 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
+ 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a,
+ 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f,
+ 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c,
+ 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
+ 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24,
+ 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41,
+ 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00, 0x00, 0x6b,
+ 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9,
+ 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+ 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+ 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30,
+ 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5,
+ 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6,
+ 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad,
+ 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, 0x00},
+ .ciphertext = { 0x84, 0xC6, 0xCB, 0xDC, 0x2B, 0x5A, 0x39, 0x98,
+ 0x57, 0x74, 0xB2, 0x3B, 0xAB, 0x06, 0x6A, 0x6A,
+ 0xF8, 0xCB, 0x66, 0xC0, 0x8E, 0x4F, 0x05, 0x8E,
+ 0x5D, 0x3E, 0x7C, 0x35, 0x1E, 0xA8, 0x45, 0xCE,
+ 0xC7, 0xB2, 0x09, 0x21, 0x0E, 0xE7, 0xEF, 0xD3,
+ 0x82, 0x69, 0x62, 0x86, 0x87, 0xF2, 0x1C, 0xB9,
+ 0xBC, 0xEA, 0x34, 0x9D, 0xC0, 0x41, 0x8A, 0xDB,
+ 0xA2, 0xBF, 0x23, 0x64, 0xDF, 0x4D, 0xB1, 0xA1,
+ 0x1A, 0xD8, 0x4C, 0xF6, 0xA4, 0x22, 0xCE, 0x95,
+ 0xC3, 0x7B, 0x2C, 0xF8, 0x11, 0x96, 0x24, 0x5C,
+ 0xD8, 0x57, 0xD0, 0xB9, 0x54, 0xB8, 0x39, 0x85,
+ 0xC1, 0x88, 0x82, 0x30, 0xF3, 0xC3, 0x01, 0x84,
+ 0x7A, 0xAF, 0x71, 0x42, 0x53, 0xEF, 0x76, 0x8C,
+ 0x17, 0xE8, 0x9E, 0x4F, 0x55, 0x13, 0xDB, 0xD5,
+ 0xBE, 0xE1, 0x26, 0x6A, 0x2B, 0x2D, 0x70, 0x63,
+ 0xCE, 0x3D, 0x0B, 0xA8, 0x71, 0x62, 0x52, 0xC5,
+ 0xBC, 0xBB, 0x99, 0x22, 0xCD, 0x46, 0xF3, 0x74,
+ 0xB5, 0x2F, 0xDF, 0xF1, 0xFE, 0xBF, 0x15, 0x5F,
+ 0xF4, 0xAF, 0xEE, 0x18, 0x78, 0x89, 0x99, 0xBC,
+ 0x74, 0x23, 0x4A, 0x3F, 0xFB, 0xA7, 0xB2, 0x85,
+ 0x8B, 0xB2, 0x55, 0x2F, 0x17, 0x2E, 0x56, 0xEC,
+ 0x47, 0x45, 0x68, 0x78, 0x44, 0x0A, 0xBB, 0x5A,
+ 0xDA, 0xE4, 0x99, 0x41, 0xC1, 0xE4, 0x36, 0x16,
+ 0xAC, 0x5D, 0x6E, 0x31, 0xA0, 0x11, 0x61, 0x1B,
+ 0x82, 0x9F, 0x6A, 0x77, 0xBE, 0x1F, 0x50, 0x75,
+ 0x4F, 0x81, 0xF3, 0x5D, 0x24, 0xED, 0x89, 0xFD,
+ 0xE8, 0x04, 0xB1, 0x73, 0x63, 0xF9, 0xA8, 0x1C,
+ 0x3F, 0x12, 0xAE, 0x06, 0x7F, 0xDD, 0x41, 0xA2,
+ 0x98, 0x49, 0x12, 0xCA, 0xE1, 0x92, 0x6C, 0x5F,
+ 0xB3, 0xAC, 0x18, 0xE5, 0x41, 0xFA, 0x4A, 0xD1,
+ 0xE1, 0x71, 0x88, 0x8E, 0x61, 0x42, 0x8F, 0x2A,
+ 0x8F, 0x2E, 0x98, 0x1A, 0xE1, 0x6D, 0x0D, 0x4E,
+ 0x41, 0xD3, 0x3E, 0x5E, 0x67, 0x5F, 0x44, 0x6D,
+ 0xAE, 0x0F, 0x45, 0x4F, 0xC4, 0xCA, 0x05, 0x6F,
+ 0x41, 0xF3, 0xCC, 0x47, 0x44, 0xA9, 0xE9, 0x48,
+ 0x42, 0x8B, 0x22, 0x80, 0xF9, 0x66, 0x63, 0xB7,
+ 0x23, 0x0C, 0x09, 0x69, 0x25, 0x03, 0xC9, 0x5B,
+ 0x3E, 0x34, 0xF8, 0xDE, 0x8D, 0xF2, 0x31, 0x57,
+ 0xF4, 0x5B, 0xDF, 0x68, 0x9B, 0x25, 0x8D, 0x99,
+ 0x4D, 0x9E, 0x6C, 0xE5, 0xD4, 0xDD, 0x6B, 0xDB,
+ 0x96, 0x76, 0x3C, 0xCC, 0x41, 0xDB, 0xBE, 0x57,
+ 0xA4, 0x77, 0x8D, 0x5A, 0x9E, 0x90, 0x22, 0x6D,
+ 0x61, 0x4C, 0x33, 0x5E, 0x44, 0xCA, 0x8A, 0xB4,
+ 0x1E, 0xFE, 0xA8, 0x98, 0xBC, 0x17, 0x0C, 0x65,
+ 0x41, 0x2F, 0x77, 0x19, 0x4A, 0x43, 0xA1, 0x30,
+ 0x5E, 0xF2, 0x3A, 0xC7, 0x0B, 0x05, 0x9E, 0x6E,
+ 0x04, 0x77, 0x96, 0xEF, 0x51, 0x8D, 0x76, 0x96,
+ 0xBC, 0x3D, 0xAD, 0x5E, 0x26, 0x34, 0xF9, 0x2D,
+ 0xD1, 0xC9, 0x0D, 0x20, 0x6A, 0x2B, 0x6D, 0x3A,
+ 0x7C, 0xE8, 0x86, 0x68, 0xBE, 0xAD, 0x64, 0x61,
+ 0x4E, 0x90, 0x00, 0xAC, 0xFB, 0xA7, 0x9E, 0xB3,
+ 0x60, 0x16, 0x06, 0x21, 0x4E, 0x21, 0xE0, 0x8F,
+ 0x14, 0xCE, 0x77, 0xE3, 0x6B, 0xB6, 0x6F, 0xE4,
+ 0xA0, 0xFC, 0xD2, 0xA2, 0x1B, 0xCA, 0xA2, 0x39,
+ 0x1A, 0x9C, 0x20, 0x16, 0xAC, 0x3B, 0xC7, 0xCD,
+ 0xF1, 0x43, 0x8E, 0xB6, 0xDD, 0x26, 0x69, 0x66,
+ 0x44, 0x58, 0x3E, 0x2B, 0x0A, 0x0C, 0x68, 0x62,
+ 0x9D, 0x73, 0x6F, 0x67, 0x23, 0xDF, 0x66, 0x85,
+ 0x9C, 0xF8, 0x0B, 0x4E, 0x5B, 0x5C, 0x5B, 0xF0,
+ 0x3F, 0x33, 0x4D, 0x65, 0xC4, 0x8D, 0xB3, 0xB2,
+ 0x66, 0x0E, 0x2C, 0xE3, 0x3B, 0x51, 0x0F, 0xD6,
+ 0x0C, 0x91, 0x2B, 0x85, 0xD1, 0x6A, 0xEE, 0x7C,
+ 0xDB, 0xFD, 0xF6, 0x28, 0x5B, 0x0A, 0x77, 0xBA,
+ 0xE0, 0x7D, 0x98, 0x7F, 0x9C, 0xE1, 0x72, 0xA5,
+ 0x48, 0xE6, 0xBF, 0x0A, 0x30, 0xCF, 0x09, 0x9A,
+ 0xA8, 0x2B, 0xE0, 0xA2, 0x5E, 0x0E, 0x89, 0x19 }
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_ECB,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b},
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f,
+ 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc,
+ 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad,
+ 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef,
+ 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a,
+ 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e,
+ 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72,
+ 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e }
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_ECB,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4},
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c,
+ 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8,
+ 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26,
+ 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70,
+ 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9,
+ 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d,
+ 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff,
+ 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7}
+ }
+};
+
+static crypto_test_reference_t aes_cfb128_reference[] = {
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CFB128,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
+ .cipher_iv_length = AES_CFB128_IV_LEN,
+ .cipher_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10},
+ .ciphertext = { 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20,
+ 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a,
+ 0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f,
+ 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b,
+ 0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40,
+ 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf,
+ 0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e,
+ 0xea, 0xc4, 0xc6, 0x6f, 0x9f, 0xf7, 0xf2, 0xe6 }
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CFB128,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b},
+ .cipher_iv_length = AES_CFB128_IV_LEN,
+ .cipher_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xcd, 0xc8, 0x0d, 0x6f, 0xdd, 0xf1, 0x8c, 0xab,
+ 0x34, 0xc2, 0x59, 0x09, 0xc9, 0x9a, 0x41, 0x74,
+ 0x67, 0xce, 0x7f, 0x7f, 0x81, 0x17, 0x36, 0x21,
+ 0x96, 0x1a, 0x2b, 0x70, 0x17, 0x1d, 0x3d, 0x7a,
+ 0x2e, 0x1e, 0x8a, 0x1d, 0xd5, 0x9b, 0x88, 0xb1,
+ 0xc8, 0xe6, 0x0f, 0xed, 0x1e, 0xfa, 0xc4, 0xc9,
+ 0xc0, 0x5f, 0x9f, 0x9c, 0xa9, 0x83, 0x4f, 0xa0,
+ 0x42, 0xae, 0x8f, 0xba, 0x58, 0x4b, 0x09, 0xff }
+ },
+ /* NIST Special Publication 800-38A */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CFB128,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4},
+ .cipher_iv_length = AES_CFB128_IV_LEN,
+ .cipher_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 64,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ .ciphertext = { 0xdc, 0x7e, 0x84, 0xbf, 0xda, 0x79, 0x16, 0x4b,
+ 0x7e, 0xcd, 0x84, 0x86, 0x98, 0x5d, 0x38, 0x60,
+ 0x39, 0xff, 0xed, 0x14, 0x3b, 0x28, 0xb1, 0xc8,
+ 0x32, 0x11, 0x3c, 0x63, 0x31, 0xe5, 0x40, 0x7b,
+ 0xdf, 0x10, 0x13, 0x24, 0x15, 0xe5, 0x4b, 0x92,
+ 0xa1, 0x3e, 0xd0, 0xa8, 0x26, 0x7a, 0xe2, 0xf9,
+ 0x75, 0xa3, 0x85, 0x74, 0x1a, 0xb9, 0xce, 0xf8,
+ 0x20, 0x31, 0x62, 0x3d, 0x55, 0xb1, 0xe4, 0x71 }
+ }
+};
+
+static crypto_test_reference_t aes_xts_reference[] = {
+ /* CAVS 11.0 XTSGen information, #1 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES128_XTS_KEY_LEN,
+ .cipher_key = { 0xa1, 0xb9, 0x0c, 0xba, 0x3f, 0x06, 0xac, 0x35,
+ 0x3b, 0x2c, 0x34, 0x38, 0x76, 0x08, 0x17, 0x62,
+ 0x09, 0x09, 0x23, 0x02, 0x6e, 0x91, 0x77, 0x18,
+ 0x15, 0xf2, 0x9d, 0xab, 0x01, 0x93, 0x2f, 0x2f},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0x4f, 0xae, 0xf7, 0x11, 0x7c, 0xda, 0x59, 0xc6,
+ 0x6e, 0x4b, 0x92, 0x01, 0x3e, 0x76, 0x8a, 0xd5},
+ .length = 16,
+ .plaintext = { 0xeb, 0xab, 0xce, 0x95, 0xb1, 0x4d, 0x3c, 0x8d,
+ 0x6f, 0xb3, 0x50, 0x39, 0x07, 0x90, 0x31, 0x1c},
+ .ciphertext = { 0x77, 0x8a, 0xe8, 0xb4, 0x3c, 0xb9, 0x8d, 0x5a,
+ 0x82, 0x50, 0x81, 0xd5, 0xbe, 0x47, 0x1c, 0x63}
+ },
+ /* CAVS 11.0 XTSGen information, #101 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES128_XTS_KEY_LEN,
+ .cipher_key = { 0xb7, 0xb9, 0x3f, 0x51, 0x6a, 0xef, 0x29, 0x5e,
+ 0xff, 0x3a, 0x29, 0xd8, 0x37, 0xcf, 0x1f, 0x13,
+ 0x53, 0x47, 0xe8, 0xa2, 0x1d, 0xae, 0x61, 0x6f,
+ 0xf5, 0x06, 0x2b, 0x2e, 0x8d, 0x78, 0xce, 0x5e},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0x87, 0x3e, 0xde, 0xa6, 0x53, 0xb6, 0x43, 0xbd,
+ 0x8b, 0xcf, 0x51, 0x40, 0x31, 0x97, 0xed, 0x14},
+ .length = 32,
+ .plaintext = { 0x23, 0x6f, 0x8a, 0x5b, 0x58, 0xdd, 0x55, 0xf6,
+ 0x19, 0x4e, 0xd7, 0x0c, 0x4a, 0xc1, 0xa1, 0x7f,
+ 0x1f, 0xe6, 0x0e, 0xc9, 0xa6, 0xc4, 0x54, 0xd0,
+ 0x87, 0xcc, 0xb7, 0x7d, 0x6b, 0x63, 0x8c, 0x47},
+ .ciphertext = { 0x22, 0xe6, 0xa3, 0xc6, 0x37, 0x9d, 0xcf, 0x75,
+ 0x99, 0xb0, 0x52, 0xb5, 0xa7, 0x49, 0xc7, 0xf7,
+ 0x8a, 0xd8, 0xa1, 0x1b, 0x9f, 0x1a, 0xa9, 0x43,
+ 0x0c, 0xf3, 0xae, 0xf4, 0x45, 0x68, 0x2e, 0x19}
+ },
+ /* CAVS 11.0 XTSGen information, #227 TODO (Length 130 bits)*/
+ /* {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES128_XTS_KEY_LEN,
+ .cipher_key = { 0xec, 0x14, 0xc0, 0xa3, 0xb7, 0x72, 0x58, 0x5c,
+ 0x15, 0xd4, 0xeb, 0x94, 0xe6, 0x9e, 0x2c, 0x55,
+ 0x80, 0xcf, 0x3a, 0x63, 0xc1, 0x7c, 0xe9, 0xda,
+ 0xd8, 0x2b, 0xb4, 0x54, 0xe3, 0x87, 0x90, 0x45},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0x4a, 0x02, 0x87, 0xc2, 0x6e, 0xd2, 0x41, 0x26,
+ 0x5b, 0x3a, 0x42, 0xcd, 0xd1, 0x9c, 0xea, 0xe2},
+ .length = 17,
+ .plaintext = { 0x50, 0x82, 0x64, 0x75, 0x82, 0xc6, 0xe5, 0xa7,
+ 0x88, 0x73, 0x6f, 0xc5, 0x90, 0x5e, 0xa5, 0x65,
+ 0xc0 },
+ .ciphertext = { 0x04, 0x3a, 0xb9, 0xc0, 0x3d, 0x5b, 0x44, 0x13,
+ 0x1d, 0x3e, 0x6e, 0xb2, 0x57, 0x61, 0x89, 0xde,
+ 0x80 },
+ }, */
+ /* CAVS 11.0 XTSGen information, #1 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES256_XTS_KEY_LEN,
+ .cipher_key = { 0x1e, 0xa6, 0x61, 0xc5, 0x8d, 0x94, 0x3a, 0x0e,
+ 0x48, 0x01, 0xe4, 0x2f, 0x4b, 0x09, 0x47, 0x14,
+ 0x9e, 0x7f, 0x9f, 0x8e, 0x3e, 0x68, 0xd0, 0xc7,
+ 0x50, 0x52, 0x10, 0xbd, 0x31, 0x1a, 0x0e, 0x7c,
+ 0xd6, 0xe1, 0x3f, 0xfd, 0xf2, 0x41, 0x8d, 0x8d,
+ 0x19, 0x11, 0xc0, 0x04, 0xcd, 0xa5, 0x8d, 0xa3,
+ 0xd6, 0x19, 0xb7, 0xe2, 0xb9, 0x14, 0x1e, 0x58,
+ 0x31, 0x8e, 0xea, 0x39, 0x2c, 0xf4, 0x1b, 0x08},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0xad, 0xf8, 0xd9, 0x26, 0x27, 0x46, 0x4a, 0xd2,
+ 0xf0, 0x42, 0x8e, 0x84, 0xa9, 0xf8, 0x75, 0x64},
+ .length = 32,
+ .plaintext = { 0x2e, 0xed, 0xea, 0x52, 0xcd, 0x82, 0x15, 0xe1,
+ 0xac, 0xc6, 0x47, 0xe8, 0x10, 0xbb, 0xc3, 0x64,
+ 0x2e, 0x87, 0x28, 0x7f, 0x8d, 0x2e, 0x57, 0xe3,
+ 0x6c, 0x0a, 0x24, 0xfb, 0xc1, 0x2a, 0x20, 0x2e},
+ .ciphertext = { 0xcb, 0xaa, 0xd0, 0xe2, 0xf6, 0xce, 0xa3, 0xf5,
+ 0x0b, 0x37, 0xf9, 0x34, 0xd4, 0x6a, 0x9b, 0x13,
+ 0x0b, 0x9d, 0x54, 0xf0, 0x7e, 0x34, 0xf3, 0x6a,
+ 0xf7, 0x93, 0xe8, 0x6f, 0x73, 0xc6, 0xd7, 0xdb},
+ },
+ /* CAVS 11.0 XTSGen information, #110 */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES256_XTS_KEY_LEN,
+ .cipher_key = { 0x6b, 0x19, 0x84, 0xc2, 0x4e, 0x7e, 0xb6, 0x62,
+ 0x8e, 0x3a, 0x11, 0xc9, 0xcc, 0xd2, 0x59, 0x40,
+ 0x33, 0xa3, 0xa0, 0xd9, 0x01, 0x6e, 0xae, 0x65,
+ 0xc2, 0xf2, 0x4e, 0x09, 0xb9, 0xa6, 0x6e, 0x9f,
+ 0xe9, 0xd1, 0x63, 0xa5, 0x06, 0xdf, 0xbc, 0xcf,
+ 0x2d, 0x93, 0xe8, 0x99, 0x1e, 0x2f, 0xc5, 0x60,
+ 0xe1, 0x04, 0x35, 0xb8, 0x90, 0xb5, 0x88, 0x9a,
+ 0x50, 0x03, 0xe4, 0xbf, 0x81, 0x7d, 0xc3, 0xe0},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0x6b, 0xb0, 0xd3, 0xae, 0x4f, 0xa8, 0x6e, 0x43,
+ 0x16, 0x19, 0xe4, 0x07, 0xd5, 0x9a, 0xd4, 0xf4},
+ .length = 48,
+ .plaintext = { 0x6a, 0x74, 0x1a, 0x94, 0x5b, 0xfb, 0xf0, 0xc6,
+ 0x7a, 0xfd, 0x43, 0xba, 0x1f, 0x84, 0x18, 0x16,
+ 0xc0, 0x99, 0x51, 0x58, 0x05, 0xd0, 0xfc, 0x1f,
+ 0x7d, 0xbf, 0x6d, 0xe9, 0x00, 0xe0, 0xaa, 0x7a,
+ 0x21, 0x9c, 0x88, 0x56, 0x32, 0x71, 0xb0, 0x09,
+ 0xd1, 0xac, 0x90, 0xeb, 0x7d, 0xc9, 0x97, 0x35},
+ .ciphertext = { 0xe4, 0x7b, 0xce, 0x29, 0x2b, 0xaa, 0x63, 0xbe,
+ 0xf3, 0x16, 0xf6, 0x80, 0xa5, 0xf4, 0x80, 0xa7,
+ 0xb8, 0x83, 0xdf, 0xab, 0x6e, 0xd5, 0xa5, 0x7f,
+ 0x7e, 0x29, 0xec, 0xb8, 0x9e, 0x35, 0x4a, 0x31,
+ 0xc9, 0xb1, 0x74, 0xc4, 0xab, 0xad, 0x6c, 0xba,
+ 0xba, 0xba, 0x19, 0x14, 0x0c, 0x46, 0x20, 0xa3},
+ },
+ /* CAVS 11.0 XTSGen information, #211 TODO: length 140 bits */
+ /* {
+ .cipher = ODP_CIPHER_ALG_AES_XTS,
+ .cipher_key_length = AES256_XTS_KEY_LEN,
+ .cipher_key = { 0x62, 0xc2, 0xe4, 0xf8, 0x52, 0xa9, 0x3e, 0xea,
+ 0x4a, 0x2f, 0x61, 0xe8, 0x67, 0x68, 0x14, 0xf4,
+ 0xa8, 0x0d, 0xc4, 0x7e, 0xe1, 0x81, 0x32, 0xc8,
+ 0x38, 0xbf, 0x89, 0xa6, 0x18, 0xfd, 0xb8, 0xe2,
+ 0x91, 0x3e, 0x2e, 0x5c, 0x32, 0x1b, 0x19, 0xea,
+ 0x04, 0xbb, 0xa6, 0x34, 0x7d, 0x22, 0x6f, 0x41,
+ 0xdb, 0xee, 0x88, 0x0d, 0x61, 0x67, 0xb8, 0xe1,
+ 0xe9, 0x17, 0xfa, 0xf0, 0x46, 0xf0, 0x87, 0x5e},
+ .cipher_iv_length = AES_XTS_IV_LEN,
+ .cipher_iv = { 0x53, 0x7e, 0xe3, 0xdc, 0x13, 0xce, 0x27, 0xa8,
+ 0xd3, 0x0e, 0x6e, 0x42, 0xb5, 0xb9, 0x96, 0xae},
+ .length = 18,
+ .plaintext = { 0x00, 0xc9, 0xeb, 0x87, 0x78, 0xe0, 0x3d, 0xdd,
+ 0x5f, 0x3d, 0xe8, 0xc1, 0x8b, 0x34, 0x8f, 0xac,
+ 0x9c, 0x30},
+ .ciphertext = { 0x9d, 0x4a, 0x08, 0xac, 0x0f, 0xb4, 0x4e, 0x90,
+ 0xd0, 0x5f, 0x62, 0x86, 0x19, 0x3f, 0x3a, 0xab,
+ 0xc2, 0x90},
+ } */
+};
+
+/* AES-GCM test vectors extracted from
+ * https://tools.ietf.org/html/draft-mcgrew-gcm-test-01#section-2
+ */
+static crypto_test_reference_t aes_gcm_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x4c, 0x80, 0xcd, 0xef, 0xbb, 0x5d, 0x10, 0xda,
+ 0x90, 0x6a, 0xc7, 0x3c, 0x36, 0x13, 0xa6, 0x34},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0x2e, 0x44, 0x3b, 0x68, 0x49, 0x56, 0xed, 0x7e,
+ 0x3b, 0x24, 0x4c, 0xfe },
+ .length = 72,
+ .plaintext = { 0x45, 0x00, 0x00, 0x48, 0x69, 0x9a, 0x00, 0x00,
+ 0x80, 0x11, 0x4d, 0xb7, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x9b, 0xf1, 0x56,
+ 0x38, 0xd3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5f, 0x73, 0x69,
+ 0x70, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x03, 0x73,
+ 0x69, 0x70, 0x09, 0x63, 0x79, 0x62, 0x65, 0x72,
+ 0x63, 0x69, 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00,
+ 0x00, 0x21, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01 },
+ .ciphertext = { 0xfe, 0xcf, 0x53, 0x7e, 0x72, 0x9d, 0x5b, 0x07,
+ 0xdc, 0x30, 0xdf, 0x52, 0x8d, 0xd2, 0x2b, 0x76,
+ 0x8d, 0x1b, 0x98, 0x73, 0x66, 0x96, 0xa6, 0xfd,
+ 0x34, 0x85, 0x09, 0xfa, 0x13, 0xce, 0xac, 0x34,
+ 0xcf, 0xa2, 0x43, 0x6f, 0x14, 0xa3, 0xf3, 0xcf,
+ 0x65, 0x92, 0x5b, 0xf1, 0xf4, 0xa1, 0x3c, 0x5d,
+ 0x15, 0xb2, 0x1e, 0x18, 0x84, 0xf5, 0xff, 0x62,
+ 0x47, 0xae, 0xab, 0xb7, 0x86, 0xb9, 0x3b, 0xce,
+ 0x61, 0xbc, 0x17, 0xd7, 0x68, 0xfd, 0x97, 0x32},
+ .aad_length = 12,
+ .aad = { 0x00, 0x00, 0x43, 0x21, 0x87, 0x65, 0x43, 0x21,
+ 0x00, 0x00, 0x00, 0x00 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0x45, 0x90, 0x18, 0x14, 0x8f, 0x6c, 0xbe, 0x72,
+ 0x2f, 0xd0, 0x47, 0x96, 0x56, 0x2d, 0xfd, 0xb4 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ 0xde, 0xca, 0xf8, 0x88 },
+ .length = 64,
+ .plaintext = { 0x45, 0x00, 0x00, 0x3e, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x11, 0x4d, 0xcc, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x98, 0x00, 0x35,
+ 0x00, 0x2a, 0x23, 0x43, 0xb2, 0xd0, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x73, 0x69, 0x70, 0x09, 0x63, 0x79, 0x62,
+ 0x65, 0x72, 0x63, 0x69, 0x74, 0x79, 0x02, 0x64,
+ 0x6b, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 },
+ .ciphertext = { 0xde, 0xb2, 0x2c, 0xd9, 0xb0, 0x7c, 0x72, 0xc1,
+ 0x6e, 0x3a, 0x65, 0xbe, 0xeb, 0x8d, 0xf3, 0x04,
+ 0xa5, 0xa5, 0x89, 0x7d, 0x33, 0xae, 0x53, 0x0f,
+ 0x1b, 0xa7, 0x6d, 0x5d, 0x11, 0x4d, 0x2a, 0x5c,
+ 0x3d, 0xe8, 0x18, 0x27, 0xc1, 0x0e, 0x9a, 0x4f,
+ 0x51, 0x33, 0x0d, 0x0e, 0xec, 0x41, 0x66, 0x42,
+ 0xcf, 0xbb, 0x85, 0xa5, 0xb4, 0x7e, 0x48, 0xa4,
+ 0xec, 0x3b, 0x9b, 0xa9, 0x5d, 0x91, 0x8b, 0xd1},
+ .aad_length = 8,
+ .aad = { 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0x83, 0xb7, 0x0d, 0x3a, 0xa8, 0xbc, 0x6e, 0xe4,
+ 0xc3, 0x09, 0xe9, 0xd8, 0x5a, 0x41, 0xad, 0x4a }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 },
+ .length = 64,
+ .plaintext = { 0x45, 0x00, 0x00, 0x3c, 0x99, 0xc5, 0x00, 0x00,
+ 0x80, 0x01, 0xcb, 0x7a, 0x40, 0x67, 0x93, 0x18,
+ 0x01, 0x01, 0x01, 0x01, 0x08, 0x00, 0x07, 0x5c,
+ 0x02, 0x00, 0x44, 0x00, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x01, 0x02, 0x02, 0x01 },
+ .ciphertext = { 0x46, 0x88, 0xda, 0xf2, 0xf9, 0x73, 0xa3, 0x92,
+ 0x73, 0x29, 0x09, 0xc3, 0x31, 0xd5, 0x6d, 0x60,
+ 0xf6, 0x94, 0xab, 0xaa, 0x41, 0x4b, 0x5e, 0x7f,
+ 0xf5, 0xfd, 0xcd, 0xff, 0xf5, 0xe9, 0xa2, 0x84,
+ 0x45, 0x64, 0x76, 0x49, 0x27, 0x19, 0xff, 0xb6,
+ 0x4d, 0xe7, 0xd9, 0xdc, 0xa1, 0xe1, 0xd8, 0x94,
+ 0xbc, 0x3b, 0xd5, 0x78, 0x73, 0xed, 0x4d, 0x18,
+ 0x1d, 0x19, 0xd4, 0xd5, 0xc8, 0xc1, 0x8a, 0xf3},
+ .aad_length = 8,
+ .aad = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0xf8, 0x21, 0xd4, 0x96, 0xee, 0xb0, 0x96, 0xe9,
+ 0x8a, 0xd2, 0xb6, 0x9e, 0x47, 0x99, 0xc7, 0x1d }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0x3d, 0xe0, 0x98, 0x74, 0xb3, 0x88, 0xe6, 0x49,
+ 0x19, 0x88, 0xd0, 0xc3, 0x60, 0x7e, 0xae, 0x1f},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0x57, 0x69, 0x0e, 0x43, 0x4e, 0x28, 0x00, 0x00,
+ 0xa2, 0xfc, 0xa1, 0xa3 },
+ .length = 28,
+ .plaintext = { 0x45, 0x00, 0x00, 0x1c, 0x42, 0xa2, 0x00, 0x00,
+ 0x80, 0x01, 0x44, 0x1f, 0x40, 0x67, 0x93, 0xb6,
+ 0xe0, 0x00, 0x00, 0x02, 0x0a, 0x00, 0xf5, 0xff,
+ 0x01, 0x02, 0x02, 0x01 },
+ .ciphertext = { 0xfb, 0xa2, 0xca, 0x84, 0x5e, 0x5d, 0xf9, 0xf0,
+ 0xf2, 0x2c, 0x3e, 0x6e, 0x86, 0xdd, 0x83, 0x1e,
+ 0x1f, 0xc6, 0x57, 0x92, 0xcd, 0x1a, 0xf9, 0x13,
+ 0x0e, 0x13, 0x79, 0xed },
+ .aad_length = 12,
+ .aad = { 0x42, 0xf6, 0x7e, 0x3f, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0x36, 0x9f, 0x07, 0x1f, 0x35, 0xe0, 0x34, 0xbe,
+ 0x95, 0xf1, 0x12, 0xe4, 0xe7, 0xd0, 0x5d, 0x35 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ 0xde, 0xca, 0xf8, 0x88 },
+ .length = 40,
+ .plaintext = { 0x45, 0x00, 0x00, 0x28, 0xa4, 0xad, 0x40, 0x00,
+ 0x40, 0x06, 0x78, 0x80, 0x0a, 0x01, 0x03, 0x8f,
+ 0x0a, 0x01, 0x06, 0x12, 0x80, 0x23, 0x06, 0xb8,
+ 0xcb, 0x71, 0x26, 0x02, 0xdd, 0x6b, 0xb0, 0x3e,
+ 0x50, 0x10, 0x16, 0xd0, 0x75, 0x68, 0x00, 0x01 },
+ .ciphertext = { 0xa5, 0xb1, 0xf8, 0x06, 0x60, 0x29, 0xae, 0xa4,
+ 0x0e, 0x59, 0x8b, 0x81, 0x22, 0xde, 0x02, 0x42,
+ 0x09, 0x38, 0xb3, 0xab, 0x33, 0xf8, 0x28, 0xe6,
+ 0x87, 0xb8, 0x85, 0x8b, 0x5b, 0xfb, 0xdb, 0xd0,
+ 0x31, 0x5b, 0x27, 0x45, 0x21, 0x44, 0xcc, 0x77},
+ .aad_length = 8,
+ .aad = { 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0x95, 0x45, 0x7b, 0x96, 0x52, 0x03, 0x7f, 0x53,
+ 0x18, 0x02, 0x7b, 0x5b, 0x4c, 0xd7, 0xa6, 0x36 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_GCM,
+ .auth = ODP_AUTH_ALG_AES_GCM,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab,
+ 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab},
+ .cipher_iv_length = AES_GCM_IV_LEN,
+ .cipher_iv = { 0x11, 0x22, 0x33, 0x44, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08 },
+ .length = 52,
+ .plaintext = { 0x45, 0x00, 0x00, 0x30, 0x69, 0xa6, 0x40, 0x00,
+ 0x80, 0x06, 0x26, 0x90, 0xc0, 0xa8, 0x01, 0x02,
+ 0x93, 0x89, 0x15, 0x5e, 0x0a, 0x9e, 0x00, 0x8b,
+ 0x2d, 0xc5, 0x7e, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x02, 0x40, 0x00, 0x20, 0xbf, 0x00, 0x00,
+ 0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x04, 0x02,
+ 0x01, 0x02, 0x02, 0x01 },
+ .ciphertext = { 0xff, 0x42, 0x5c, 0x9b, 0x72, 0x45, 0x99, 0xdf,
+ 0x7a, 0x3b, 0xcd, 0x51, 0x01, 0x94, 0xe0, 0x0d,
+ 0x6a, 0x78, 0x10, 0x7f, 0x1b, 0x0b, 0x1c, 0xbf,
+ 0x06, 0xef, 0xae, 0x9d, 0x65, 0xa5, 0xd7, 0x63,
+ 0x74, 0x8a, 0x63, 0x79, 0x85, 0x77, 0x1d, 0x34,
+ 0x7f, 0x05, 0x45, 0x65, 0x9f, 0x14, 0xe9, 0x9d,
+ 0xef, 0x84, 0x2d, 0x8e },
+ .aad_length = 8,
+ .aad = { 0x4a, 0x2c, 0xbf, 0xe3, 0x00, 0x00, 0x00, 0x02 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0xb3, 0x35, 0xf4, 0xee, 0xcf, 0xdb, 0xf8, 0x31,
+ 0x82, 0x4b, 0x4c, 0x49, 0x15, 0x95, 0x6c, 0x96 }
+ }
+};
+
+static crypto_test_reference_t aes_ccm_reference[] = {
+ /*
+ * AES-CCM reference from RFC 3610
+ */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf},
+ .cipher_iv_length = 13,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0x58, 0x8c, 0x97, 0x9a, 0x61, 0xc6, 0x63, 0xd2,
+ 0xf0, 0x66, 0xd0, 0xc2, 0xc0, 0xf9, 0x89, 0x80,
+ 0x6d, 0x5f, 0x6b, 0x61, 0xda, 0xc3, 0x84 },
+ .digest_length = 8,
+ .digest = { 0x17, 0xe8, 0xd1, 0x2c, 0xfd, 0xf9, 0x26, 0xe0 }
+ },
+ /* The rest of test vectors are generated manually, no "interesting"
+ * vectors for use cases in RFC 3610 or SP 800-38C. */
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7},
+ .cipher_iv_length = 13,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0x57, 0x9f, 0xb8, 0x6e, 0xdd, 0xb4, 0xa6, 0x4a,
+ 0xae, 0x5f, 0xe9, 0x6d, 0xbd, 0x75, 0x44, 0x05,
+ 0x33, 0xa9, 0xfc, 0x3a, 0x84, 0x57, 0x36 },
+ .digest_length = 8,
+ .digest = { 0x67, 0xae, 0xc8, 0x0a, 0xc5, 0x88, 0xab, 0x16 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf},
+ .cipher_iv_length = 13,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0x59, 0x61, 0x55, 0x10, 0xa7, 0xc4, 0x3b, 0xfb,
+ 0x12, 0x3d, 0x63, 0x6b, 0x46, 0x13, 0xc0, 0x3c,
+ 0x6c, 0xe2, 0x69, 0x07, 0x10, 0x2a, 0x3f },
+ .digest_length = 8,
+ .digest = { 0xb5, 0x57, 0x2a, 0x17, 0x2d, 0x49, 0x16, 0xd5 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES128_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf},
+ .cipher_iv_length = 11,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0xaa, 0x2d, 0x3e, 0xcb, 0xa6, 0x68, 0x63, 0x75,
+ 0x8f, 0x03, 0x01, 0x51, 0x16, 0xde, 0x30, 0xed,
+ 0x8a, 0xb5, 0x42, 0xdc, 0xfa, 0x72, 0xd0 },
+ .digest_length = 8,
+ .digest = { 0x63, 0xe7, 0x01, 0x5c, 0x69, 0xaf, 0xb4, 0x0c }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES192_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7},
+ .cipher_iv_length = 11,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0xee, 0x99, 0x99, 0x1e, 0xc5, 0x8f, 0xd7, 0x7e,
+ 0x56, 0x71, 0x16, 0x39, 0x8e, 0xc4, 0x4f, 0xcc,
+ 0x14, 0x45, 0x57, 0x3e, 0x38, 0x76, 0x51 },
+ .digest_length = 8,
+ .digest = { 0x31, 0x29, 0x47, 0xa4, 0x6d, 0x76, 0x34, 0xb4 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_CCM,
+ .auth = ODP_AUTH_ALG_AES_CCM,
+ .cipher_key_length = AES256_KEY_LEN,
+ .cipher_key = { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf},
+ .cipher_iv_length = 11,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0,
+ 0xa1, 0xa2, 0xa3 },
+ .aad_length = 8,
+ .aad = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .length = 23,
+ .plaintext = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
+ .ciphertext = { 0xfa, 0x07, 0x47, 0x5c, 0xe8, 0xc9, 0x37, 0x88,
+ 0x54, 0x64, 0xb8, 0xc3, 0x85, 0xbb, 0x76, 0x0b,
+ 0xf2, 0xc2, 0x4c, 0x4e, 0x31, 0x16, 0x77 },
+ .digest_length = 8,
+ .digest = { 0x88, 0x56, 0x7e, 0x19, 0x84, 0x13, 0x29, 0xc4 }
+ },
+};
+
+static crypto_test_reference_t aes_gmac_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_AES_GMAC,
+ .auth_key_length = AES128_KEY_LEN,
+ .auth_key = { 0x4c, 0x80, 0xcd, 0xef, 0xbb, 0x5d, 0x10, 0xda,
+ 0x90, 0x6a, 0xc7, 0x3c, 0x36, 0x13, 0xa6, 0x34},
+ .auth_iv_length = AES_GCM_IV_LEN,
+ .auth_iv = { 0x22, 0x43, 0x3c, 0x64, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 },
+ .length = 68,
+ .plaintext = { 0x00, 0x00, 0x43, 0x21, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x30, 0xda, 0x3a, 0x00, 0x00,
+ 0x80, 0x01, 0xdf, 0x3b, 0xc0, 0xa8, 0x00, 0x05,
+ 0xc0, 0xa8, 0x00, 0x01, 0x08, 0x00, 0xc6, 0xcd,
+ 0x02, 0x00, 0x07, 0x00, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x01, 0x02, 0x02, 0x01 },
+ .ciphertext = { 0x00, 0x00, 0x43, 0x21, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x30, 0xda, 0x3a, 0x00, 0x00,
+ 0x80, 0x01, 0xdf, 0x3b, 0xc0, 0xa8, 0x00, 0x05,
+ 0xc0, 0xa8, 0x00, 0x01, 0x08, 0x00, 0xc6, 0xcd,
+ 0x02, 0x00, 0x07, 0x00, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x01, 0x02, 0x02, 0x01 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0xf2, 0xa9, 0xa8, 0x36, 0xe1, 0x55, 0x10, 0x6a,
+ 0xa8, 0xdc, 0xd6, 0x18, 0xe4, 0x09, 0x9a, 0xaa }
+ },
+ /* AES192-GMAC from DPDK 17.02 */
+ {
+ .auth = ODP_AUTH_ALG_AES_GMAC,
+ .auth_key_length = AES192_KEY_LEN,
+ .auth_key = { 0xaa, 0x74, 0x0a, 0xbf, 0xad, 0xcd, 0xa7, 0x79,
+ 0x22, 0x0d, 0x3b, 0x40, 0x6c, 0x5d, 0x7e, 0xc0,
+ 0x9a, 0x77, 0xfe, 0x9d, 0x94, 0x10, 0x45, 0x39},
+ .auth_iv_length = AES_GCM_IV_LEN,
+ .auth_iv = { 0xab, 0x22, 0x65, 0xb4, 0xc1, 0x68, 0x95, 0x55,
+ 0x61, 0xf0, 0x43, 0x15 },
+ .length = 80,
+ .plaintext = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10},
+ .ciphertext = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10},
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0xCF, 0x82, 0x80, 0x64, 0x02, 0x46, 0xF4, 0xFB,
+ 0x33, 0xAE, 0x1D, 0x90, 0xEA, 0x48, 0x83, 0xDB },
+ },
+ /* AES256-GMAC from DPDK 17.02 */
+ {
+ .auth = ODP_AUTH_ALG_AES_GMAC,
+ .auth_key_length = AES256_KEY_LEN,
+ .auth_key = { 0xb5, 0x48, 0xe4, 0x93, 0x4f, 0x5c, 0x64, 0xd3,
+ 0xc0, 0xf0, 0xb7, 0x8f, 0x7b, 0x4d, 0x88, 0x24,
+ 0xaa, 0xc4, 0x6b, 0x3c, 0x8d, 0x2c, 0xc3, 0x5e,
+ 0xe4, 0xbf, 0xb2, 0x54, 0xe4, 0xfc, 0xba, 0xf7},
+ .auth_iv_length = AES_GCM_IV_LEN,
+ .auth_iv = { 0x2e, 0xed, 0xe1, 0xdc, 0x64, 0x47, 0xc7, 0xaf,
+ 0xc4, 0x41, 0x53, 0x58 },
+ .length = 65,
+ .plaintext = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02 },
+ .ciphertext = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x01, 0x02 },
+ .digest_length = AES_GCM_DIGEST_LEN,
+ .digest = { 0x77, 0x46, 0x0D, 0x6F, 0xB1, 0x87, 0xDB, 0xA9,
+ 0x46, 0xAD, 0xCD, 0xFB, 0xB7, 0xF9, 0x13, 0xA1 },
+ }
+};
+
+/*
+ * Test vectors from SP800-38B / CSRC examples
+ * 12-byte vectors are just truncated 16-byte vectors
+ */
+static crypto_test_reference_t aes_cmac_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_AES_CMAC,
+ .auth_key_length = AES128_KEY_LEN,
+ .auth_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+ .length = 16,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+ .ciphertext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
+ },
+ .digest_length = 16,
+ .digest = { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+ 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c },
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = 12,
+ },
+ {
+ .auth = ODP_AUTH_ALG_AES_CMAC,
+ .auth_key_length = AES192_KEY_LEN,
+ .auth_key = { 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b },
+ .length = 16,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+ .ciphertext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
+ },
+ .digest_length = 16,
+ .digest = { 0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90,
+ 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c, 0x51, 0x84 },
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = 12,
+ },
+ {
+ .auth = ODP_AUTH_ALG_AES_CMAC,
+ .auth_key_length = AES256_KEY_LEN,
+ .auth_key = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 },
+ .length = 16,
+ .plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+ .ciphertext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
+ },
+ .digest_length = 16,
+ .digest = { 0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82,
+ 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c },
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = 12,
+ },
+};
+
+/*
+ * Test vector from RFC 7539, sections 2.8.2, A.5
+ */
+static crypto_test_reference_t chacha20_poly1305_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_CHACHA20_POLY1305,
+ .auth = ODP_AUTH_ALG_CHACHA20_POLY1305,
+ .cipher_key_length = CHACHA20_POLY1305_KEY_LEN,
+ .cipher_key = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f},
+ .cipher_iv_length = CHACHA20_POLY1305_IV_LEN,
+ .cipher_iv = { 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47 },
+ .length = 114,
+ .plaintext = { 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
+ 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
+ 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
+ 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
+ 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
+ 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
+ 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
+ 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
+ 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
+ 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
+ 0x74, 0x2e },
+ .ciphertext = { 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
+ 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
+ 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
+ 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
+ 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
+ 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
+ 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
+ 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
+ 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
+ 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
+ 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
+ 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
+ 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
+ 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
+ 0x61, 0x16 },
+ .aad_length = 12,
+ .aad = { 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4,
+ 0xc5, 0xc6, 0xc7 },
+ .digest_length = 16,
+ .digest = { 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
+ 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 }
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_CHACHA20_POLY1305,
+ .auth = ODP_AUTH_ALG_CHACHA20_POLY1305,
+ .cipher_key_length = CHACHA20_POLY1305_KEY_LEN,
+ .cipher_key = { 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
+ 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
+ 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
+ 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0},
+ .cipher_iv_length = CHACHA20_POLY1305_IV_LEN,
+ .cipher_iv = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08 },
+ .length = 265,
+ .plaintext = { 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x73, 0x20,
+ 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66,
+ 0x74, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x69,
+ 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20,
+ 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20,
+ 0x6f, 0x66, 0x20, 0x73, 0x69, 0x78, 0x20, 0x6d,
+ 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65,
+ 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
+ 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63,
+ 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f,
+ 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x20, 0x61, 0x74, 0x20, 0x61,
+ 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e,
+ 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72,
+ 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20,
+ 0x75, 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61,
+ 0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x20, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61,
+ 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20,
+ 0x63, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65,
+ 0x6d, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20,
+ 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20,
+ 0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72, 0x6b,
+ 0x20, 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x65, 0x73, 0x73, 0x2e, 0x2f, 0xe2, 0x80,
+ 0x9d },
+ .ciphertext = { 0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4,
+ 0x60, 0xf0, 0x62, 0xc7, 0x9b, 0xe6, 0x43, 0xbd,
+ 0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89,
+ 0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2,
+ 0x4c, 0x6c, 0xfc, 0x18, 0x75, 0x5d, 0x43, 0xee,
+ 0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0,
+ 0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00,
+ 0xd4, 0xf0, 0x3b, 0x7f, 0x35, 0x58, 0x94, 0xcf,
+ 0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce,
+ 0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81,
+ 0x14, 0xad, 0x17, 0x6e, 0x00, 0x8d, 0x33, 0xbd,
+ 0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55,
+ 0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61,
+ 0xc1, 0x86, 0x32, 0x4e, 0x2b, 0x35, 0x06, 0x38,
+ 0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0,
+ 0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4,
+ 0xb9, 0x16, 0x6c, 0x76, 0x7b, 0x80, 0x4d, 0x46,
+ 0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9,
+ 0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e,
+ 0xe2, 0x82, 0xa1, 0xb0, 0xa0, 0x6c, 0x52, 0x3e,
+ 0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15,
+ 0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a,
+ 0x0d, 0x07, 0x2b, 0x04, 0xb3, 0x56, 0x4e, 0xea,
+ 0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a,
+ 0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99,
+ 0x19, 0x55, 0xeb, 0xd6, 0x31, 0x59, 0x43, 0x4e,
+ 0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10,
+ 0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10,
+ 0x49, 0xe6, 0x17, 0xd9, 0x1d, 0x36, 0x10, 0x94,
+ 0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30,
+ 0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf,
+ 0x99, 0x7b, 0x71, 0x4d, 0x6c, 0x6f, 0x2c, 0x29,
+ 0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70,
+ 0x9b },
+ .aad_length = 12,
+ .aad = { 0xf3, 0x33, 0x88, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x4e, 0x91 },
+ .digest_length = CHACHA20_POLY1305_CHECK_LEN,
+ .digest = { 0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22,
+ 0x39, 0x23, 0x36, 0xfe, 0xa1, 0x85, 0x1f, 0x38 }
+ },
+};
+
+static crypto_test_reference_t hmac_md5_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key_length = HMAC_MD5_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_MD5_CHECK_LEN,
+ .digest = { 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
+ 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d },
+
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_MD5_96_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key_length = HMAC_MD5_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_MD5_CHECK_LEN,
+ .digest = { 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
+ 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 },
+
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_MD5_96_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_MD5_HMAC,
+ .auth_key_length = HMAC_MD5_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_MD5_CHECK_LEN,
+ .digest = { 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
+ 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_MD5_96_CHECK_LEN,
+ },
+};
+
+static crypto_test_reference_t hmac_sha1_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key_length = HMAC_SHA1_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_SHA1_CHECK_LEN,
+ .digest = { 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64,
+ 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e,
+ 0xf1, 0x46, 0xbe, 0x00 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA1_96_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key_length = HMAC_SHA1_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_SHA1_CHECK_LEN,
+ .digest = { 0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2,
+ 0xd2, 0x74, 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c,
+ 0x25, 0x9a, 0x7c, 0x79 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA1_96_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA1_HMAC,
+ .auth_key_length = HMAC_SHA1_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_SHA1_CHECK_LEN,
+ .digest = { 0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd,
+ 0x91, 0xa3, 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f,
+ 0x63, 0xf1, 0x75, 0xd3 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA1_96_CHECK_LEN,
+ },
+};
+
+static crypto_test_reference_t hmac_sha224_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA224_HMAC,
+ .auth_key_length = HMAC_SHA224_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_SHA224_CHECK_LEN,
+ .digest = { 0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf, 0x19,
+ 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d, 0xf3, 0x3f,
+ 0x47, 0xb4, 0xb1, 0x16, 0x99, 0x12, 0xba, 0x4f,
+ 0x53, 0x68, 0x4b, 0x22 }
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA224_HMAC,
+ .auth_key_length = HMAC_SHA224_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_SHA224_CHECK_LEN,
+ .digest = { 0xa3, 0x0e, 0x01, 0x09, 0x8b, 0xc6, 0xdb, 0xbf,
+ 0x45, 0x69, 0x0f, 0x3a, 0x7e, 0x9e, 0x6d, 0x0f,
+ 0x8b, 0xbe, 0xa2, 0xa3, 0x9e, 0x61, 0x48, 0x00,
+ 0x8f, 0xd0, 0x5e, 0x44 }
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA224_HMAC,
+ .auth_key_length = HMAC_SHA224_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_SHA224_CHECK_LEN,
+ .digest = { 0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6,
+ 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a, 0xd2, 0x64,
+ 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1,
+ 0xec, 0x83, 0x33, 0xea }
+ }
+};
+
+static crypto_test_reference_t hmac_sha256_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA256_HMAC,
+ .auth_key_length = HMAC_SHA256_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_SHA256_CHECK_LEN,
+ .digest = { 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
+ 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
+ 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
+ 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA256_128_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA256_HMAC,
+ .auth_key_length = HMAC_SHA256_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_SHA256_CHECK_LEN,
+ .digest = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+ 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+ 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+ 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA256_128_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA256_HMAC,
+ .auth_key_length = HMAC_SHA256_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_SHA256_CHECK_LEN,
+ .digest = { 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
+ 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7,
+ 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
+ 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA256_128_CHECK_LEN,
+ },
+};
+
+static crypto_test_reference_t hmac_sha384_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA384_HMAC,
+ .auth_key_length = HMAC_SHA384_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_SHA384_CHECK_LEN,
+ .digest = { 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62,
+ 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f,
+ 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+ 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c,
+ 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f,
+ 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA384_192_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA384_HMAC,
+ .auth_key_length = HMAC_SHA384_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_SHA384_CHECK_LEN,
+ .digest = { 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31,
+ 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b,
+ 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+ 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e,
+ 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7,
+ 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA384_192_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA384_HMAC,
+ .auth_key_length = HMAC_SHA384_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_SHA384_CHECK_LEN,
+ .digest = {0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a,
+ 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f,
+ 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+ 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b,
+ 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9,
+ 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA384_192_CHECK_LEN,
+ },
+};
+
+static crypto_test_reference_t hmac_sha512_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA512_HMAC,
+ .auth_key_length = HMAC_SHA512_KEY_LEN,
+ .auth_key = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b },
+ .length = 8,
+ /* "Hi There" */
+ .plaintext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .ciphertext = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
+ .digest_length = HMAC_SHA512_CHECK_LEN,
+ .digest = { 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d,
+ 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0,
+ 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78,
+ 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde,
+ 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, 0x02,
+ 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4,
+ 0xbe, 0x9d, 0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70,
+ 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA512_256_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA512_HMAC,
+ .auth_key_length = HMAC_SHA512_KEY_LEN,
+ /* "Jefe" */
+ .auth_key = { 0x4a, 0x65, 0x66, 0x65 },
+ .length = 28,
+ /* what do ya want for nothing?*/
+ .plaintext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .ciphertext = { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20,
+ 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+ 0x69, 0x6e, 0x67, 0x3f },
+ .digest_length = HMAC_SHA512_CHECK_LEN,
+ .digest = { 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2,
+ 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3,
+ 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6,
+ 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54,
+ 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99, 0x4a,
+ 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd,
+ 0xca, 0xea, 0xb1, 0xa3, 0x4d, 0x4a, 0x6b, 0x4b,
+ 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA512_256_CHECK_LEN,
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA512_HMAC,
+ .auth_key_length = HMAC_SHA512_KEY_LEN,
+ .auth_key = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa },
+ .length = 50,
+ .plaintext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .ciphertext = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd },
+ .digest_length = HMAC_SHA512_CHECK_LEN,
+ .digest = { 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84,
+ 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9,
+ 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36,
+ 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39,
+ 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, 0xc8,
+ 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07,
+ 0xb9, 0x46, 0xa3, 0x37, 0xbe, 0xe8, 0x94, 0x26,
+ 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = HMAC_SHA512_256_CHECK_LEN,
+ },
+};
+
+/*
+ * RFC 3566
+ */
+static crypto_test_reference_t aes_xcbc_reference[] = {
+ /* Test Case #1 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
+ .length = 0,
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = {0x75, 0xf0, 0x25, 0x1d, 0x52, 0x8a, 0xc0, 0x1c,
+ 0x45, 0x73, 0xdf, 0xd5, 0x84, 0xd7, 0x9f, 0x29}
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #2 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 3,
+ .plaintext = { 0x00, 0x01, 0x02 },
+ .ciphertext = { 0x00, 0x01, 0x02 },
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = { 0x5b, 0x37, 0x65, 0x80, 0xae, 0x2f, 0x19, 0xaf,
+ 0xe7, 0x21, 0x9c, 0xee, 0xf1, 0x72, 0x75, 0x6f }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #3 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 16,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .ciphertext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = { 0xd2, 0xa2, 0x46, 0xfa, 0x34, 0x9b, 0x68, 0xa7,
+ 0x99, 0x98, 0xa4, 0x39, 0x4f, 0xf7, 0xa2, 0x63 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #4 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .length = 20,
+ .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13 },
+ .ciphertext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13 },
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = { 0x47, 0xf5, 0x1b, 0x45, 0x64, 0x96, 0x62, 0x15,
+ 0xb8, 0x98, 0x5c, 0x63, 0x05, 0x5e, 0xd3, 0x08 }
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #5 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
+ .length = 32,
+ .plaintext = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f},
+ .ciphertext = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f},
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = {0xf5, 0x4f, 0x0e, 0xc8, 0xd2, 0xb9, 0xf3, 0xd3,
+ 0x68, 0x07, 0x73, 0x4b, 0xd5, 0x28, 0x3f, 0xd4}
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #6 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
+ .length = 34,
+ .plaintext = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21},
+ .ciphertext = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21},
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = {0xbe, 0xcb, 0xb3, 0xbc, 0xcd, 0xb5, 0x18, 0xa3,
+ 0x06, 0x77, 0xd5, 0x48, 0x1f, 0xb6, 0xb4, 0xd8}
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+ /* Test Case #7 */
+ {
+ .auth = ODP_AUTH_ALG_AES_XCBC_MAC,
+ .auth_key_length = AES_XCBC_MAC_KEY_LEN,
+ .auth_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
+ .length = 1000,
+ /* Plaintext is 1000 zero bytes. No explicit init needed. */
+ .digest_length = AES_XCBC_MAC_CHECK_LEN,
+ .digest = {0xf0, 0xda, 0xfe, 0xe8, 0x95, 0xdb, 0x30, 0x25,
+ 0x37, 0x61, 0x10, 0x3b, 0x5d, 0x84, 0x52, 0x8f}
+ },
+ {
+ .copy_previous_vector = 1,
+ .digest_length = AES_XCBC_MAC_96_CHECK_LEN,
+ },
+};
+
+/*
+ * Kasumi F8 and F9 test vectors are taken from
+ * 3GPP TS 35.203 V9.0.0 (2009-12)
+ * 3rd Generation Partnership Project;
+ * Technical Specification Group Services and System Aspects;
+ * 3G Security;
+ * Specification of the 3GPP Confidentiality
+ * and Integrity Algorithms;
+ * Document 3: Implementors' Test Data
+ * (Release 9)
+ */
+static crypto_test_reference_t kasumi_f8_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_KASUMI_F8,
+ .cipher_key_length = KASUMI_F8_KEY_LEN,
+ .cipher_key = { 0x5a, 0xcb, 0x1d, 0x64, 0x4c, 0x0d, 0x51, 0x20,
+ 0x4e, 0xa5, 0xf1, 0x45, 0x10, 0x10, 0xd8, 0x52},
+ .cipher_iv_length = KASUMI_F8_IV_LEN,
+ .cipher_iv = { 0xfa, 0x55, 0x6b, 0x26, 0x1c, 0x00, 0x00, 0x00},
+ .length = 120, /* 15 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0xad, 0x9c, 0x44, 0x1f, 0x89, 0x0b, 0x38, 0xc4,
+ 0x57, 0xa4, 0x9d, 0x42, 0x14, 0x07, 0xe8 },
+ .ciphertext = { 0x9b, 0xc9, 0x2c, 0xa8, 0x03, 0xc6, 0x7b, 0x28,
+ 0xa1, 0x1a, 0x4b, 0xee, 0x5a, 0x0c, 0x25 }
+ }
+};
+
+static crypto_test_reference_t kasumi_f9_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_KASUMI_F9,
+ .auth_key_length = KASUMI_F9_KEY_LEN,
+ .auth_key = { 0xc7, 0x36, 0xc6, 0xaa, 0xb2, 0x2b, 0xff, 0xf9,
+ 0x1e, 0x26, 0x98, 0xd2, 0xe2, 0x2a, 0xd5, 0x7e },
+ .auth_iv_length = KASUMI_F9_IV_LEN,
+ .auth_iv = { 0x14, 0x79, 0x3e, 0x41, 0x03, 0x97, 0xe8, 0xfd,
+ 0x01, },
+ .length = 384, /* 48 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0xd0, 0xa7, 0xd4, 0x63, 0xdf, 0x9f, 0xb2, 0xb2,
+ 0x78, 0x83, 0x3f, 0xa0, 0x2e, 0x23, 0x5a, 0xa1,
+ 0x72, 0xbd, 0x97, 0x0c, 0x14, 0x73, 0xe1, 0x29,
+ 0x07, 0xfb, 0x64, 0x8b, 0x65, 0x99, 0xaa, 0xa0,
+ 0xb2, 0x4a, 0x03, 0x86, 0x65, 0x42, 0x2b, 0x20,
+ 0xa4, 0x99, 0x27, 0x6a, 0x50, 0x42, 0x70, 0x09},
+ .ciphertext = { 0xd0, 0xa7, 0xd4, 0x63, 0xdf, 0x9f, 0xb2, 0xb2,
+ 0x78, 0x83, 0x3f, 0xa0, 0x2e, 0x23, 0x5a, 0xa1,
+ 0x72, 0xbd, 0x97, 0x0c, 0x14, 0x73, 0xe1, 0x29,
+ 0x07, 0xfb, 0x64, 0x8b, 0x65, 0x99, 0xaa, 0xa0,
+ 0xb2, 0x4a, 0x03, 0x86, 0x65, 0x42, 0x2b, 0x20,
+ 0xa4, 0x99, 0x27, 0x6a, 0x50, 0x42, 0x70, 0x09},
+ .digest_length = KASUMI_F9_DIGEST_LEN,
+ .digest = { 0xdd, 0x7d, 0xfa, 0xdd },
+ }
+};
+
+/*
+ * Snow3G UEA2 & UIA2 test vectors are taken from
+ * Specification of the 3GPP Confidentiality and
+ * Integrity Algorithms UEA2 & UIA2
+ * Document 3: Implementors’ Test Data
+ * Version: 1.1
+ * Date: 25 th October 2012
+ */
+static crypto_test_reference_t snow3g_uea2_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_SNOW3G_UEA2,
+ .cipher_key_length = SNOW3G_UEA2_KEY_LEN,
+ .cipher_key = { 0x5a, 0xcb, 0x1d, 0x64, 0x4c, 0x0d, 0x51, 0x20,
+ 0x4e, 0xa5, 0xf1, 0x45, 0x10, 0x10, 0xd8, 0x52},
+ .cipher_iv_length = SNOW3G_UEA2_IV_LEN,
+ .cipher_iv = { 0xfa, 0x55, 0x6b, 0x26, 0x1c, 0x00, 0x00, 0x00,
+ 0xfa, 0x55, 0x6b, 0x26, 0x1c, 0x00, 0x00, 0x00},
+ .length = 120, /* 15 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0xad, 0x9c, 0x44, 0x1f, 0x89, 0x0b, 0x38, 0xc4,
+ 0x57, 0xa4, 0x9d, 0x42, 0x14, 0x07, 0xe8 },
+ .ciphertext = { 0xba, 0x0f, 0x31, 0x30, 0x03, 0x34, 0xc5, 0x6b,
+ 0x52, 0xa7, 0x49, 0x7c, 0xba, 0xc0, 0x46 }
+ }
+};
+
+static crypto_test_reference_t snow3g_uia2_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SNOW3G_UIA2,
+ .auth_key_length = SNOW3G_UIA2_KEY_LEN,
+ .auth_key = { 0xc7, 0x36, 0xc6, 0xaa, 0xb2, 0x2b, 0xff, 0xf9,
+ 0x1e, 0x26, 0x98, 0xd2, 0xe2, 0x2a, 0xd5, 0x7e},
+ .auth_iv_length = SNOW3G_UIA2_IV_LEN,
+ .auth_iv = { 0x14, 0x79, 0x3e, 0x41, 0x03, 0x97, 0xe8, 0xfd,
+ 0x94, 0x79, 0x3e, 0x41, 0x03, 0x97, 0x68, 0xfd },
+ .length = 384, /* 48 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0xd0, 0xa7, 0xd4, 0x63, 0xdf, 0x9f, 0xb2, 0xb2,
+ 0x78, 0x83, 0x3f, 0xa0, 0x2e, 0x23, 0x5a, 0xa1,
+ 0x72, 0xbd, 0x97, 0x0c, 0x14, 0x73, 0xe1, 0x29,
+ 0x07, 0xfb, 0x64, 0x8b, 0x65, 0x99, 0xaa, 0xa0,
+ 0xb2, 0x4a, 0x03, 0x86, 0x65, 0x42, 0x2b, 0x20,
+ 0xa4, 0x99, 0x27, 0x6a, 0x50, 0x42, 0x70, 0x09},
+ .ciphertext = { 0xd0, 0xa7, 0xd4, 0x63, 0xdf, 0x9f, 0xb2, 0xb2,
+ 0x78, 0x83, 0x3f, 0xa0, 0x2e, 0x23, 0x5a, 0xa1,
+ 0x72, 0xbd, 0x97, 0x0c, 0x14, 0x73, 0xe1, 0x29,
+ 0x07, 0xfb, 0x64, 0x8b, 0x65, 0x99, 0xaa, 0xa0,
+ 0xb2, 0x4a, 0x03, 0x86, 0x65, 0x42, 0x2b, 0x20,
+ 0xa4, 0x99, 0x27, 0x6a, 0x50, 0x42, 0x70, 0x09},
+ .digest_length = SNOW3G_UIA2_DIGEST_LEN,
+ .digest = { 0x38, 0xb5, 0x54, 0xc0 }
+ }
+};
+
+/*
+ * AES EEA2 and AES EIA2 test vectors from
+ * Specification of the 3GPP Confidentiality and Integrity
+ * Algorithms 128-EEA2 & 128-EIA2
+ */
+static crypto_test_reference_t aes_eea2_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_AES_EEA2,
+ .cipher_key_length = AES_EEA2_KEY_LEN,
+ .cipher_key = { 0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C,
+ 0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1},
+ .cipher_iv_length = AES_EEA2_IV_LEN,
+ .cipher_iv = { 0x39, 0x8a, 0x59, 0xb4, 0xac, },
+ .length = 253,
+ .is_length_in_bits = true,
+ .plaintext = { 0x98, 0x1B, 0xA6, 0x82, 0x4C, 0x1B, 0xFB, 0x1A,
+ 0xB4, 0x85, 0x47, 0x20, 0x29, 0xB7, 0x1D, 0x80,
+ 0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC,
+ 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66, 0xB1, 0xF0 },
+ .ciphertext = { 0xE9, 0xFE, 0xD8, 0xA6, 0x3D, 0x15, 0x53, 0x04,
+ 0xD7, 0x1D, 0xF2, 0x0B, 0xF3, 0xE8, 0x22, 0x14,
+ 0xB2, 0x0E, 0xD7, 0xDA, 0xD2, 0xF2, 0x33, 0xDC,
+ 0x3C, 0x22, 0xD7, 0xBD, 0xEE, 0xED, 0x8E, 0x78}
+ },
+ {
+ .cipher = ODP_CIPHER_ALG_AES_EEA2,
+ .cipher_key_length = AES_EEA2_KEY_LEN,
+ .cipher_key = { 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC4, 0x40, 0xE0,
+ 0x95, 0x2C, 0x49, 0x10, 0x48, 0x05, 0xFF, 0x48},
+ .cipher_iv_length = AES_EEA2_IV_LEN,
+ .cipher_iv = { 0xc6, 0x75, 0xa6, 0x4b, 0x64, },
+ .length = 798,
+ .is_length_in_bits = true,
+ .plaintext = { 0x7E, 0xC6, 0x12, 0x72, 0x74, 0x3B, 0xF1, 0x61,
+ 0x47, 0x26, 0x44, 0x6A, 0x6C, 0x38, 0xCE, 0xD1,
+ 0x66, 0xF6, 0xCA, 0x76, 0xEB, 0x54, 0x30, 0x04,
+ 0x42, 0x86, 0x34, 0x6C, 0xEF, 0x13, 0x0F, 0x92,
+ 0x92, 0x2B, 0x03, 0x45, 0x0D, 0x3A, 0x99, 0x75,
+ 0xE5, 0xBD, 0x2E, 0xA0, 0xEB, 0x55, 0xAD, 0x8E,
+ 0x1B, 0x19, 0x9E, 0x3E, 0xC4, 0x31, 0x60, 0x20,
+ 0xE9, 0xA1, 0xB2, 0x85, 0xE7, 0x62, 0x79, 0x53,
+ 0x59, 0xB7, 0xBD, 0xFD, 0x39, 0xBE, 0xF4, 0xB2,
+ 0x48, 0x45, 0x83, 0xD5, 0xAF, 0xE0, 0x82, 0xAE,
+ 0xE6, 0x38, 0xBF, 0x5F, 0xD5, 0xA6, 0x06, 0x19,
+ 0x39, 0x01, 0xA0, 0x8F, 0x4A, 0xB4, 0x1A, 0xAB,
+ 0x9B, 0x13, 0x48, 0x80 },
+ .ciphertext = { 0x59, 0x61, 0x60, 0x53, 0x53, 0xC6, 0x4B, 0xDC,
+ 0xA1, 0x5B, 0x19, 0x5E, 0x28, 0x85, 0x53, 0xA9,
+ 0x10, 0x63, 0x25, 0x06, 0xD6, 0x20, 0x0A, 0xA7,
+ 0x90, 0xC4, 0xC8, 0x06, 0xC9, 0x99, 0x04, 0xCF,
+ 0x24, 0x45, 0xCC, 0x50, 0xBB, 0x1C, 0xF1, 0x68,
+ 0xA4, 0x96, 0x73, 0x73, 0x4E, 0x08, 0x1B, 0x57,
+ 0xE3, 0x24, 0xCE, 0x52, 0x59, 0xC0, 0xE7, 0x8D,
+ 0x4C, 0xD9, 0x7B, 0x87, 0x09, 0x76, 0x50, 0x3C,
+ 0x09, 0x43, 0xF2, 0xCB, 0x5A, 0xE8, 0xF0, 0x52,
+ 0xC7, 0xB7, 0xD3, 0x92, 0x23, 0x95, 0x87, 0xB8,
+ 0x95, 0x60, 0x86, 0xBC, 0xAB, 0x18, 0x83, 0x60,
+ 0x42, 0xE2, 0xE6, 0xCE, 0x42, 0x43, 0x2A, 0x17,
+ 0x10, 0x5C, 0x53, 0xD0 }
+ },
+};
+
+static crypto_test_reference_t aes_eia2_reference[] = {
+ /* 3GPP TS 33.401, C.2.1 */
+ {
+ .auth = ODP_AUTH_ALG_AES_EIA2,
+ .auth_key_length = AES_EIA2_KEY_LEN,
+ .auth_key = { 0x2b, 0xd6, 0x45, 0x9f, 0x82, 0xc5, 0xb3, 0x00,
+ 0x95, 0x2c, 0x49, 0x10, 0x48, 0x81, 0xff, 0x48 },
+ .auth_iv_length = AES_EIA2_IV_LEN,
+ .auth_iv = { 0x38, 0xa6, 0xf0, 0x56, 0xc0 },
+ .length = 58,
+ .is_length_in_bits = true,
+ .plaintext = { 0x33, 0x32, 0x34, 0x62, 0x63, 0x39, 0x38, 0x40 },
+ .ciphertext = { 0x33, 0x32, 0x34, 0x62, 0x63, 0x39, 0x38, 0x40 },
+ .digest_length = AES_EIA2_DIGEST_LEN,
+ .digest = { 0x11, 0x8c, 0x6e, 0xb8 }
+ },
+ /* 3GPP TS 33.401, C.2.2. */
+ {
+ .auth = ODP_AUTH_ALG_AES_EIA2,
+ .auth_key_length = AES_EIA2_KEY_LEN,
+ .auth_key = { 0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C,
+ 0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1 },
+ .auth_iv_length = AES_EIA2_IV_LEN,
+ .auth_iv = { 0x39, 0x8a, 0x59, 0xb4, 0xd4, },
+ .length = 64, /* 8 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0x48, 0x45, 0x83, 0xd5, 0xaf, 0xe0, 0x82, 0xae },
+ .ciphertext = { 0x48, 0x45, 0x83, 0xd5, 0xaf, 0xe0, 0x82, 0xae},
+ .digest_length = AES_EIA2_DIGEST_LEN,
+ .digest = { 0xb9, 0x37, 0x87, 0xe6 }
+ },
+ /* 3GPP TS 33.401, C.2.5 */
+ {
+ .auth = ODP_AUTH_ALG_AES_EIA2,
+ .auth_key_length = AES_EIA2_KEY_LEN,
+ .auth_key = { 0x83, 0xfd, 0x23, 0xa2, 0x44, 0xa7, 0x4c, 0xf3,
+ 0x58, 0xda, 0x30, 0x19, 0xf1, 0x72, 0x26, 0x35 },
+ .auth_iv_length = AES_EIA2_IV_LEN,
+ .auth_iv = { 0x36, 0xaf, 0x61, 0x44, 0x7c },
+ .length = 768, /* 96 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0x35, 0xc6, 0x87, 0x16, 0x63, 0x3c, 0x66, 0xfb,
+ 0x75, 0x0c, 0x26, 0x68, 0x65, 0xd5, 0x3c, 0x11,
+ 0xea, 0x05, 0xb1, 0xe9, 0xfa, 0x49, 0xc8, 0x39,
+ 0x8d, 0x48, 0xe1, 0xef, 0xa5, 0x90, 0x9d, 0x39,
+ 0x47, 0x90, 0x28, 0x37, 0xf5, 0xae, 0x96, 0xd5,
+ 0xa0, 0x5b, 0xc8, 0xd6, 0x1c, 0xa8, 0xdb, 0xef,
+ 0x1b, 0x13, 0xa4, 0xb4, 0xab, 0xfe, 0x4f, 0xb1,
+ 0x00, 0x60, 0x45, 0xb6, 0x74, 0xbb, 0x54, 0x72,
+ 0x93, 0x04, 0xc3, 0x82, 0xbe, 0x53, 0xa5, 0xaf,
+ 0x05, 0x55, 0x61, 0x76, 0xf6, 0xea, 0xa2, 0xef,
+ 0x1d, 0x05, 0xe4, 0xb0, 0x83, 0x18, 0x1e, 0xe6,
+ 0x74, 0xcd, 0xa5, 0xa4, 0x85, 0xf7, 0x4d, 0x7a },
+ .ciphertext = { 0x35, 0xc6, 0x87, 0x16, 0x63, 0x3c, 0x66, 0xfb,
+ 0x75, 0x0c, 0x26, 0x68, 0x65, 0xd5, 0x3c, 0x11,
+ 0xea, 0x05, 0xb1, 0xe9, 0xfa, 0x49, 0xc8, 0x39,
+ 0x8d, 0x48, 0xe1, 0xef, 0xa5, 0x90, 0x9d, 0x39,
+ 0x47, 0x90, 0x28, 0x37, 0xf5, 0xae, 0x96, 0xd5,
+ 0xa0, 0x5b, 0xc8, 0xd6, 0x1c, 0xa8, 0xdb, 0xef,
+ 0x1b, 0x13, 0xa4, 0xb4, 0xab, 0xfe, 0x4f, 0xb1,
+ 0x00, 0x60, 0x45, 0xb6, 0x74, 0xbb, 0x54, 0x72,
+ 0x93, 0x04, 0xc3, 0x82, 0xbe, 0x53, 0xa5, 0xaf,
+ 0x05, 0x55, 0x61, 0x76, 0xf6, 0xea, 0xa2, 0xef,
+ 0x1d, 0x05, 0xe4, 0xb0, 0x83, 0x18, 0x1e, 0xe6,
+ 0x74, 0xcd, 0xa5, 0xa4, 0x85, 0xf7, 0x4d, 0x7a },
+ .digest_length = AES_EIA2_DIGEST_LEN,
+ .digest = { 0xe6, 0x57, 0xe1, 0x82 }
+ },
+};
+
+/*
+ * ZUC EEA3 and EIA3 test vectors from
+ * Specification of the 3GPP Confidentiality and Integrity
+ * Algorithms 128-EEA3 & 128-EIA3
+ * Document 3: Implementor’s Test Data
+ * Version: 1.1
+ * Date: 4 th Jan. 2011
+ */
+static crypto_test_reference_t zuc_eea3_reference[] = {
+ {
+ .cipher = ODP_CIPHER_ALG_ZUC_EEA3,
+ .cipher_key_length = ZUC_EEA3_KEY_LEN,
+ .cipher_key = { 0xe5, 0xbd, 0x3e, 0xa0, 0xeb, 0x55, 0xad, 0xe8,
+ 0x66, 0xc6, 0xac, 0x58, 0xbd, 0x54, 0x30, 0x2a},
+ .cipher_iv_length = ZUC_EEA3_IV_LEN,
+ .cipher_iv = { 0x00, 0x05, 0x68, 0x23, 0xc4, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x68, 0x23, 0xc4, 0x00, 0x00, 0x00 },
+ .length = 800, /* 100 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0x14, 0xa8, 0xef, 0x69, 0x3d, 0x67, 0x85, 0x07,
+ 0xbb, 0xe7, 0x27, 0x0a, 0x7f, 0x67, 0xff, 0x50,
+ 0x06, 0xc3, 0x52, 0x5b, 0x98, 0x07, 0xe4, 0x67,
+ 0xc4, 0xe5, 0x60, 0x00, 0xba, 0x33, 0x8f, 0x5d,
+ 0x42, 0x95, 0x59, 0x03, 0x67, 0x51, 0x82, 0x22,
+ 0x46, 0xc8, 0x0d, 0x3b, 0x38, 0xf0, 0x7f, 0x4b,
+ 0xe2, 0xd8, 0xff, 0x58, 0x05, 0xf5, 0x13, 0x22,
+ 0x29, 0xbd, 0xe9, 0x3b, 0xbb, 0xdc, 0xaf, 0x38,
+ 0x2b, 0xf1, 0xee, 0x97, 0x2f, 0xbf, 0x99, 0x77,
+ 0xba, 0xda, 0x89, 0x45, 0x84, 0x7a, 0x2a, 0x6c,
+ 0x9a, 0xd3, 0x4a, 0x66, 0x75, 0x54, 0xe0, 0x4d,
+ 0x1f, 0x7f, 0xa2, 0xc3, 0x32, 0x41, 0xbd, 0x8f,
+ 0x01, 0xba, 0x22, 0x0d },
+ .ciphertext = { 0x13, 0x1d, 0x43, 0xe0, 0xde, 0xa1, 0xbe, 0x5c,
+ 0x5a, 0x1b, 0xfd, 0x97, 0x1d, 0x85, 0x2c, 0xbf,
+ 0x71, 0x2d, 0x7b, 0x4f, 0x57, 0x96, 0x1f, 0xea,
+ 0x32, 0x08, 0xaf, 0xa8, 0xbc, 0xa4, 0x33, 0xf4,
+ 0x56, 0xad, 0x09, 0xc7, 0x41, 0x7e, 0x58, 0xbc,
+ 0x69, 0xcf, 0x88, 0x66, 0xd1, 0x35, 0x3f, 0x74,
+ 0x86, 0x5e, 0x80, 0x78, 0x1d, 0x20, 0x2d, 0xfb,
+ 0x3e, 0xcf, 0xf7, 0xfc, 0xbc, 0x3b, 0x19, 0x0f,
+ 0xe8, 0x2a, 0x20, 0x4e, 0xd0, 0xe3, 0x50, 0xfc,
+ 0x0f, 0x6f, 0x26, 0x13, 0xb2, 0xf2, 0xbc, 0xa6,
+ 0xdf, 0x5a, 0x47, 0x3a, 0x57, 0xa4, 0xa0, 0x0d,
+ 0x98, 0x5e, 0xba, 0xd8, 0x80, 0xd6, 0xf2, 0x38,
+ 0x64, 0xa0, 0x7b, 0x01 }
+ },
+
+ /* Privately generated test data */
+ {
+ .cipher = ODP_CIPHER_ALG_ZUC_EEA3,
+ .cipher_key_length = ZUC_EEA3_256_KEY_LEN,
+ .cipher_key = { 0xf7, 0xb4, 0x04, 0x5a, 0x81, 0x5c, 0x1b, 0x01,
+ 0x82, 0xf9, 0xf4, 0x26, 0x80, 0xd4, 0x56, 0x26,
+ 0xd5, 0xf7, 0x4b, 0x68, 0x48, 0x6b, 0x92, 0x6a,
+ 0x34, 0x1f, 0x86, 0x66, 0x60, 0x0a, 0xfc, 0x57},
+ .cipher_iv_length = ZUC_EEA3_256_IV_LEN,
+ .cipher_iv = { 0x8e, 0x5d, 0xbc, 0x3f, 0xb9, 0xae, 0x66, 0xa3,
+ 0xb9, 0x5c, 0x12, 0x14, 0xdb, 0xc5, 0xbc, 0x18,
+ 0x48, 0x12, 0x09, 0x06, 0x25, 0x33, 0x2e, 0x12,
+ 0x12 },
+ .length = 1024,
+ .is_length_in_bits = true,
+ .plaintext = { 0x36, 0xdb, 0x63, 0x68, 0xb5, 0x1f, 0x4e, 0x92,
+ 0x46, 0x1f, 0xde, 0xdb, 0xc2, 0xec, 0xfa, 0x7e,
+ 0x49, 0x85, 0x77, 0xaa, 0x46, 0x98, 0x30, 0x2d,
+ 0x3b, 0xc4, 0x11, 0x24, 0x98, 0x20, 0xa9, 0xce,
+ 0xfb, 0x0d, 0x36, 0xb0, 0x2c, 0x85, 0x42, 0x72,
+ 0xa4, 0x21, 0x4e, 0x66, 0x0d, 0x48, 0xe4, 0x57,
+ 0xce, 0x5b, 0x01, 0x14, 0xf3, 0x31, 0x42, 0x2e,
+ 0xf5, 0x53, 0x52, 0x8d, 0x73, 0xfc, 0x5c, 0x6e,
+ 0x09, 0x92, 0x1e, 0x35, 0x17, 0x60, 0xa8, 0xbb,
+ 0x81, 0xf6, 0x21, 0x8f, 0x3e, 0x05, 0xe6, 0x0c,
+ 0x60, 0xe7, 0x21, 0x53, 0x18, 0x63, 0x81, 0x0d,
+ 0xb6, 0xd4, 0x9a, 0x29, 0xd0, 0xf6, 0x97, 0xd9,
+ 0x89, 0xb5, 0x0e, 0xa0, 0x15, 0xb6, 0x5c, 0x97,
+ 0xac, 0x7d, 0x26, 0xeb, 0x83, 0x0c, 0xf7, 0xe3,
+ 0xf3, 0x18, 0x37, 0x0b, 0x7b, 0xb8, 0x18, 0x31,
+ 0x8c, 0xb2, 0x5a, 0x5c, 0xa9, 0xf1, 0x35, 0x32 },
+ .ciphertext = { 0xa6, 0xe5, 0x71, 0x58, 0x5c, 0xcf, 0x5d, 0x0d,
+ 0x59, 0xb5, 0x51, 0xab, 0xf5, 0xfa, 0x31, 0xf9,
+ 0x8d, 0x4f, 0xf0, 0x3c, 0x7d, 0x61, 0x8d, 0x7a,
+ 0x6b, 0xcb, 0x2c, 0x79, 0xca, 0x99, 0x06, 0x6f,
+ 0xff, 0x5d, 0x12, 0x5f, 0x0e, 0x7a, 0x33, 0x6b,
+ 0x51, 0xbc, 0x58, 0x53, 0xff, 0xbd, 0x85, 0xc9,
+ 0xac, 0x5f, 0x33, 0xc2, 0xa2, 0xf1, 0x17, 0x7a,
+ 0xd9, 0x3f, 0x81, 0x82, 0x2f, 0x0a, 0xb0, 0xaf,
+ 0xb9, 0x19, 0x3b, 0xfa, 0xcd, 0xa4, 0x06, 0x81,
+ 0x2a, 0x7a, 0xbf, 0x2c, 0x07, 0xde, 0xc1, 0xa4,
+ 0x8c, 0x15, 0x85, 0x81, 0xa6, 0xd3, 0x73, 0x1c,
+ 0x29, 0x0b, 0xee, 0x3c, 0x57, 0xfa, 0x82, 0xad,
+ 0x6f, 0xe0, 0xa1, 0x54, 0x8d, 0xa4, 0x92, 0x29,
+ 0xf4, 0xfa, 0x6d, 0x01, 0xe3, 0x6c, 0xb9, 0x76,
+ 0x80, 0x53, 0xbb, 0x27, 0xb8, 0x18, 0x47, 0x6c,
+ 0xae, 0xb5, 0x44, 0x60, 0x43, 0x9d, 0xa7, 0x3f }
+ },
+ /* Privately generated test data */
+ {
+ .cipher = ODP_CIPHER_ALG_ZUC_EEA3,
+ .cipher_key_length = ZUC_EEA3_256_KEY_LEN,
+ .cipher_key = { 0x1d, 0x0f, 0x0e, 0x75, 0x86, 0xb3, 0xfc, 0x65,
+ 0x94, 0xbf, 0xaa, 0xa8, 0xf5, 0xd0, 0x0f, 0xe8,
+ 0x14, 0x7a, 0x96, 0x61, 0x15, 0x49, 0x79, 0x71,
+ 0x13, 0x82, 0xb4, 0xae, 0x34, 0x04, 0x75, 0x51 },
+ .cipher_iv_length = ZUC_EEA3_256_IV_LEN,
+ .cipher_iv = { 0x98, 0xcc, 0x89, 0x9f, 0xaf, 0x6d, 0x64, 0xb6,
+ 0xb1, 0xe8, 0x21, 0x72, 0xee, 0xb6, 0xcc, 0xe3,
+ 0xcf, 0x32, 0x28, 0x21, 0x21, 0x0d, 0x1e, 0x1c,
+ 0x34 },
+ .length = 1928,
+ .is_length_in_bits = true,
+ .plaintext = { 0xa4, 0xcb, 0x6e, 0x76, 0x99, 0xfb, 0x0c, 0xab,
+ 0x6d, 0x57, 0xb1, 0x69, 0xc0, 0x47, 0x80, 0x63,
+ 0x00, 0xe1, 0xf9, 0x51, 0x10, 0xbe, 0xc0, 0x0f,
+ 0x99, 0x62, 0x2d, 0x71, 0xca, 0x75, 0xa0, 0x6e,
+ 0x41, 0x0e, 0xe4, 0xda, 0x09, 0xf1, 0x86, 0x76,
+ 0x48, 0x37, 0xe0, 0x08, 0x7e, 0x60, 0x6c, 0x7f,
+ 0x41, 0x65, 0xd0, 0x51, 0x24, 0x91, 0x61, 0xbd,
+ 0xf3, 0x8e, 0x2e, 0xbd, 0x04, 0xce, 0x2b, 0x45,
+ 0xdc, 0x0f, 0x1f, 0xe5, 0x00, 0xa5, 0x5c, 0x48,
+ 0xdd, 0x3c, 0x51, 0x5b, 0x9c, 0xbd, 0xda, 0xde,
+ 0x22, 0xab, 0x2f, 0x46, 0x3c, 0x90, 0x03, 0x2f,
+ 0x1f, 0x31, 0xec, 0x23, 0xff, 0x17, 0x68, 0xdb,
+ 0x26, 0x87, 0xc1, 0x27, 0x2d, 0x1d, 0x6f, 0x0a,
+ 0x59, 0xc0, 0x65, 0xf5, 0x7d, 0x40, 0xd3, 0xa0,
+ 0xeb, 0x03, 0xe6, 0x27, 0x93, 0xea, 0x56, 0xb2,
+ 0x1b, 0x42, 0xd5, 0x1b, 0x59, 0x3d, 0xf6, 0x7f,
+ 0xc5, 0xb7, 0xa6, 0xf2, 0xd4, 0x16, 0xfc, 0x2d,
+ 0xd6, 0x61, 0x23, 0x54, 0xa1, 0xf6, 0xf4, 0x8c,
+ 0xf9, 0xda, 0xb3, 0x8d, 0xc4, 0x09, 0x3f, 0xe0,
+ 0x4b, 0x15, 0xfb, 0xa4, 0x52, 0xf1, 0x24, 0x17,
+ 0xa9, 0xca, 0x09, 0x7d, 0xe0, 0x05, 0xab, 0xb7,
+ 0x67, 0xce, 0x0b, 0x08, 0xc4, 0xff, 0x95, 0xbe,
+ 0xd9, 0x48, 0x4b, 0x9e, 0x52, 0x8a, 0x7e, 0x9d,
+ 0x9f, 0x79, 0x42, 0xf2, 0x6a, 0x66, 0x09, 0x13,
+ 0x30, 0x13, 0x91, 0x11, 0x18, 0x3c, 0xc8, 0x7f,
+ 0x0a, 0xd3, 0x88, 0xce, 0xd2, 0x1d, 0x8c, 0xab,
+ 0x65, 0xd7, 0x49, 0xb7, 0x62, 0xc7, 0x55, 0x01,
+ 0x40, 0x97, 0xf3, 0xab, 0xfd, 0xfd, 0xbe, 0x2d,
+ 0x10, 0x4f, 0x3e, 0x28, 0x8b, 0x06, 0xa8, 0x95,
+ 0xd9, 0x30, 0x64, 0xab, 0x4d, 0xf0, 0x57, 0xb2,
+ 0xc8 },
+ .ciphertext = { 0xd0, 0xf9, 0xff, 0xce, 0x03, 0x81, 0x14, 0x9c,
+ 0xd5, 0xf2, 0xbf, 0xe5, 0xff, 0xc8, 0x15, 0x4a,
+ 0x9c, 0x06, 0x2b, 0x17, 0x99, 0xe3, 0x48, 0x70,
+ 0x37, 0x01, 0x5e, 0x24, 0x80, 0x9a, 0x46, 0x4e,
+ 0xa8, 0xc0, 0x59, 0xd7, 0x03, 0x74, 0x28, 0x91,
+ 0x79, 0xb4, 0xb5, 0xd6, 0x52, 0x92, 0x04, 0x77,
+ 0x5b, 0x4f, 0x34, 0xd1, 0xbe, 0xaa, 0x74, 0xd9,
+ 0x01, 0x40, 0x24, 0xc7, 0x8c, 0x62, 0x2a, 0x51,
+ 0x5a, 0x58, 0x0e, 0xc8, 0x70, 0x12, 0x06, 0x1c,
+ 0x62, 0x7f, 0xf5, 0x23, 0xcb, 0x3c, 0xc1, 0xbe,
+ 0x8b, 0x7f, 0x9d, 0x12, 0xb8, 0x26, 0xc8, 0xa3,
+ 0x77, 0x7e, 0x83, 0xda, 0x83, 0xe1, 0x9f, 0xef,
+ 0x33, 0x62, 0x17, 0xa7, 0x74, 0x68, 0x34, 0x5e,
+ 0x16, 0xcc, 0xbc, 0x6c, 0x33, 0x2f, 0x73, 0xf0,
+ 0xfc, 0xe5, 0x2c, 0x2d, 0xfb, 0x81, 0xbe, 0x1e,
+ 0x6e, 0x4f, 0xf4, 0x14, 0x37, 0x7c, 0x97, 0xac,
+ 0xa9, 0xac, 0x68, 0x95, 0xf3, 0x55, 0xb3, 0xfb,
+ 0xf6, 0x64, 0xd9, 0x1b, 0xe1, 0x54, 0x79, 0x6e,
+ 0xfa, 0x21, 0xa4, 0x19, 0x9f, 0xb4, 0x4b, 0xb7,
+ 0xef, 0x52, 0xd8, 0x44, 0x75, 0x99, 0x07, 0x6d,
+ 0xa9, 0xcf, 0x32, 0xc5, 0xc1, 0x31, 0x0c, 0xa8,
+ 0x86, 0x40, 0x75, 0xeb, 0x12, 0xcf, 0x26, 0x5c,
+ 0x5f, 0xa3, 0x3c, 0xb6, 0x12, 0x45, 0xf3, 0x0a,
+ 0x38, 0x09, 0xa8, 0x36, 0x32, 0x4a, 0x2f, 0xad,
+ 0x50, 0x11, 0x38, 0xba, 0x8f, 0xdd, 0xd1, 0x58,
+ 0xd7, 0x3d, 0x3a, 0x40, 0x7c, 0x3f, 0xa7, 0x98,
+ 0xf3, 0x12, 0x7f, 0x9f, 0x89, 0xcf, 0x48, 0x58,
+ 0x01, 0xeb, 0x98, 0x7c, 0x59, 0x11, 0x9f, 0x57,
+ 0x74, 0x5f, 0x70, 0x72, 0x74, 0xa4, 0x82, 0x3c,
+ 0x36, 0xe6, 0x31, 0x9e, 0xba, 0x7b, 0x53, 0xfc,
+ 0x56 }
+ },
+};
+
+static crypto_test_reference_t zuc_eia3_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_ZUC_EIA3,
+ .auth_key_length = ZUC_EIA3_KEY_LEN,
+ .auth_key = { 0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb,
+ 0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a },
+ .auth_iv_length = ZUC_EIA3_IV_LEN,
+ .auth_iv = { 0xa9, 0x40, 0x59, 0xda, 0x50, 0x00, 0x00, 0x00,
+ 0x29, 0x40, 0x59, 0xda, 0x50, 0x00, 0x80, 0x00 },
+ .length = 584, /* 73 bytes */
+ .is_length_in_bits = true,
+ .plaintext = { 0x98, 0x3b, 0x41, 0xd4, 0x7d, 0x78, 0x0c, 0x9e,
+ 0x1a, 0xd1, 0x1d, 0x7e, 0xb7, 0x03, 0x91, 0xb1,
+ 0xde, 0x0b, 0x35, 0xda, 0x2d, 0xc6, 0x2f, 0x83,
+ 0xe7, 0xb7, 0x8d, 0x63, 0x06, 0xca, 0x0e, 0xa0,
+ 0x7e, 0x94, 0x1b, 0x7b, 0xe9, 0x13, 0x48, 0xf9,
+ 0xfc, 0xb1, 0x70, 0xe2, 0x21, 0x7f, 0xec, 0xd9,
+ 0x7f, 0x9f, 0x68, 0xad, 0xb1, 0x6e, 0x5d, 0x7d,
+ 0x21, 0xe5, 0x69, 0xd2, 0x80, 0xed, 0x77, 0x5c,
+ 0xeb, 0xde, 0x3f, 0x40, 0x93, 0xc5, 0x38, 0x81,
+ 0x00, 0x00, 0x00, 0x00 },
+ .ciphertext = { 0x98, 0x3b, 0x41, 0xd4, 0x7d, 0x78, 0x0c, 0x9e,
+ 0x1a, 0xd1, 0x1d, 0x7e, 0xb7, 0x03, 0x91, 0xb1,
+ 0xde, 0x0b, 0x35, 0xda, 0x2d, 0xc6, 0x2f, 0x83,
+ 0xe7, 0xb7, 0x8d, 0x63, 0x06, 0xca, 0x0e, 0xa0,
+ 0x7e, 0x94, 0x1b, 0x7b, 0xe9, 0x13, 0x48, 0xf9,
+ 0xfc, 0xb1, 0x70, 0xe2, 0x21, 0x7f, 0xec, 0xd9,
+ 0x7f, 0x9f, 0x68, 0xad, 0xb1, 0x6e, 0x5d, 0x7d,
+ 0x21, 0xe5, 0x69, 0xd2, 0x80, 0xed, 0x77, 0x5c,
+ 0xeb, 0xde, 0x3f, 0x40, 0x93, 0xc5, 0x38, 0x81,
+ 0x00, 0x00, 0x00, 0x00 },
+ .digest_length = ZUC_EIA3_DIGEST_LEN,
+ .digest = { 0x24, 0xa8, 0x42, 0xb3 }
+ },
+ /* Privately generated test data */
+ {
+ .auth = ODP_AUTH_ALG_ZUC_EIA3,
+ .auth_key_length = ZUC_EIA3_256_KEY_LEN,
+ .auth_key = { 0xe3, 0x8e, 0xaf, 0x08, 0xde, 0x8c, 0x08, 0x41,
+ 0x7f, 0x2b, 0x97, 0x20, 0x10, 0x87, 0xc7, 0xf7,
+ 0xbe, 0x3c, 0xd2, 0x68, 0x80, 0x10, 0x1e, 0x71,
+ 0xfd, 0xb2, 0xbb, 0xad, 0x25, 0x0f, 0x06, 0x08 },
+ .auth_iv_length = ZUC_EIA3_256_IV_LEN,
+ .auth_iv = { 0xf5, 0x8d, 0x08, 0x26, 0x94, 0x14, 0xc7, 0x4d,
+ 0xf5, 0x7c, 0x9c, 0xaa, 0x45, 0x53, 0xfd, 0x85,
+ 0x23, 0x0b, 0x00, 0x0e, 0x26, 0x2b, 0x0f, 0x01,
+ 0x26 },
+ .length = 360,
+ .is_length_in_bits = true,
+ .plaintext = { 0x08, 0xba, 0x8d, 0xf1, 0xf8, 0x62, 0xa6, 0xaf,
+ 0xf9, 0x03, 0x88, 0x9c, 0xa3, 0x68, 0x6b, 0x87,
+ 0xb6, 0x92, 0xd1, 0x47, 0x3e, 0x54, 0xaf, 0x46,
+ 0x07, 0x8f, 0x89, 0xea, 0x26, 0x9d, 0x0e, 0x2f,
+ 0x57, 0x9b, 0x20, 0x4f, 0xfe, 0xc7, 0xfe, 0xf7,
+ 0xca, 0x86, 0x93, 0x6d, 0xee },
+ .ciphertext = { 0x08, 0xba, 0x8d, 0xf1, 0xf8, 0x62, 0xa6, 0xaf,
+ 0xf9, 0x03, 0x88, 0x9c, 0xa3, 0x68, 0x6b, 0x87,
+ 0xb6, 0x92, 0xd1, 0x47, 0x3e, 0x54, 0xaf, 0x46,
+ 0x07, 0x8f, 0x89, 0xea, 0x26, 0x9d, 0x0e, 0x2f,
+ 0x57, 0x9b, 0x20, 0x4f, 0xfe, 0xc7, 0xfe, 0xf7,
+ 0xca, 0x86, 0x93, 0x6d, 0xee },
+ .digest_length = ZUC_EIA3_DIGEST_LEN,
+ .digest = {0x58, 0x19, 0xab, 0xa5}
+ },
+ /* Privately generated test data */
+ {
+ .auth = ODP_AUTH_ALG_ZUC_EIA3,
+ .auth_key_length = ZUC_EIA3_256_KEY_LEN,
+ .auth_key = { 0x6a, 0x7e, 0x4c, 0x7e, 0x51, 0x25, 0xb3, 0x48,
+ 0x84, 0x53, 0x3a, 0x94, 0xfb, 0x31, 0x99, 0x90,
+ 0x32, 0x57, 0x44, 0xee, 0x9b, 0xbc, 0xe9, 0xe5,
+ 0x25, 0xcf, 0x08, 0xf5, 0xe9, 0xe2, 0x5e, 0x53 },
+ .auth_iv_length = ZUC_EIA3_256_IV_LEN,
+ .auth_iv = { 0x60, 0xaa, 0xd2, 0xb2, 0xd0, 0x85, 0xfa, 0x54,
+ 0xd8, 0x35, 0xe8, 0xd4, 0x66, 0x82, 0x64, 0x98,
+ 0xd9, 0x2a, 0x08, 0x1d, 0x35, 0x19, 0x17, 0x01,
+ 0x1A },
+ .length = 2872,
+ .is_length_in_bits = true,
+ .plaintext = { 0xc6, 0x69, 0x73, 0x51, 0xff, 0x4a, 0xec, 0x29,
+ 0xcd, 0xba, 0xab, 0xf2, 0xfb, 0xe3, 0x46, 0x7c,
+ 0xc2, 0x54, 0xf8, 0x1b, 0xe8, 0xe7, 0x8d, 0x76,
+ 0x5a, 0x2e, 0x63, 0x33, 0x9f, 0xc9, 0x9a, 0x66,
+ 0x32, 0x0d, 0xb7, 0x31, 0x58, 0xa3, 0x5a, 0x25,
+ 0x5d, 0x05, 0x17, 0x58, 0xe9, 0x5e, 0xd4, 0xab,
+ 0xb2, 0xcd, 0xc6, 0x9b, 0xb4, 0x54, 0x11, 0x0e,
+ 0x82, 0x74, 0x41, 0x21, 0x3d, 0xdc, 0x87, 0x70,
+ 0xe9, 0x3e, 0xa1, 0x41, 0xe1, 0xfc, 0x67, 0x3e,
+ 0x01, 0x7e, 0x97, 0xea, 0xdc, 0x6b, 0x96, 0x8f,
+ 0x38, 0x5c, 0x2a, 0xec, 0xb0, 0x3b, 0xfb, 0x32,
+ 0xaf, 0x3c, 0x54, 0xec, 0x18, 0xdb, 0x5c, 0x02,
+ 0x1a, 0xfe, 0x43, 0xfb, 0xfa, 0xaa, 0x3a, 0xfb,
+ 0x29, 0xd1, 0xe6, 0x05, 0x3c, 0x7c, 0x94, 0x75,
+ 0xd8, 0xbe, 0x61, 0x89, 0xf9, 0x5c, 0xbb, 0xa8,
+ 0x99, 0x0f, 0x95, 0xb1, 0xeb, 0xf1, 0xb3, 0x05,
+ 0xef, 0xf7, 0x00, 0xe9, 0xa1, 0x3a, 0xe5, 0xca,
+ 0x0b, 0xcb, 0xd0, 0x48, 0x47, 0x64, 0xbd, 0x1f,
+ 0x23, 0x1e, 0xa8, 0x1c, 0x7b, 0x64, 0xc5, 0x14,
+ 0x73, 0x5a, 0xc5, 0x5e, 0x4b, 0x79, 0x63, 0x3b,
+ 0x70, 0x64, 0x24, 0x11, 0x9e, 0x09, 0xdc, 0xaa,
+ 0xd4, 0xac, 0xf2, 0x1b, 0x10, 0xaf, 0x3b, 0x33,
+ 0xcd, 0xe3, 0x50, 0x48, 0x47, 0x15, 0x5c, 0xbb,
+ 0x6f, 0x22, 0x19, 0xba, 0x9b, 0x7d, 0xf5, 0x0b,
+ 0xe1, 0x1a, 0x1c, 0x7f, 0x23, 0xf8, 0x29, 0xf8,
+ 0xa4, 0x1b, 0x13, 0xb5, 0xca, 0x4e, 0xe8, 0x98,
+ 0x32, 0x38, 0xe0, 0x79, 0x4d, 0x3d, 0x34, 0xbc,
+ 0x5f, 0x4e, 0x77, 0xfa, 0xcb, 0x6c, 0x05, 0xac,
+ 0x86, 0x21, 0x2b, 0xaa, 0x1a, 0x55, 0xa2, 0xbe,
+ 0x70, 0xb5, 0x73, 0x3b, 0x04, 0x5c, 0xd3, 0x36,
+ 0x94, 0xb3, 0xaf, 0xe2, 0xf0, 0xe4, 0x9e, 0x4f,
+ 0x32, 0x15, 0x49, 0xfd, 0x82, 0x4e, 0xa9, 0x08,
+ 0x70, 0xd4, 0xb2, 0x8a, 0x29, 0x54, 0x48, 0x9a,
+ 0x0a, 0xbc, 0xd5, 0x0e, 0x18, 0xa8, 0x44, 0xac,
+ 0x5b, 0xf3, 0x8e, 0x4c, 0xd7, 0x2d, 0x9b, 0x09,
+ 0x42, 0xe5, 0x06, 0xc4, 0x33, 0xaf, 0xcd, 0xa3,
+ 0x84, 0x7f, 0x2d, 0xad, 0xd4, 0x76, 0x47, 0xde,
+ 0x32, 0x1c, 0xec, 0x4a, 0xc4, 0x30, 0xf6, 0x20,
+ 0x23, 0x85, 0x6c, 0xfb, 0xb2, 0x07, 0x04, 0xf4,
+ 0xec, 0x0b, 0xb9, 0x20, 0xba, 0x86, 0xc3, 0x3e,
+ 0x05, 0xf1, 0xec, 0xd9, 0x67, 0x33, 0xb7, 0x99,
+ 0x50, 0xa3, 0xe3, 0x14, 0xd3, 0xd9, 0x34, 0xf7,
+ 0x5e, 0xa0, 0xf2, 0x10, 0xa8, 0xf6, 0x05, 0x94,
+ 0x01, 0xbe, 0xb4, 0xbc, 0x44, 0x78, 0xfa, 0x49,
+ 0x69, 0xe6, 0x23, 0xd0, 0x1a, 0xda, 0x69 },
+ .ciphertext = { 0xc6, 0x69, 0x73, 0x51, 0xff, 0x4a, 0xec, 0x29,
+ 0xcd, 0xba, 0xab, 0xf2, 0xfb, 0xe3, 0x46, 0x7c,
+ 0xc2, 0x54, 0xf8, 0x1b, 0xe8, 0xe7, 0x8d, 0x76,
+ 0x5a, 0x2e, 0x63, 0x33, 0x9f, 0xc9, 0x9a, 0x66,
+ 0x32, 0x0d, 0xb7, 0x31, 0x58, 0xa3, 0x5a, 0x25,
+ 0x5d, 0x05, 0x17, 0x58, 0xe9, 0x5e, 0xd4, 0xab,
+ 0xb2, 0xcd, 0xc6, 0x9b, 0xb4, 0x54, 0x11, 0x0e,
+ 0x82, 0x74, 0x41, 0x21, 0x3d, 0xdc, 0x87, 0x70,
+ 0xe9, 0x3e, 0xa1, 0x41, 0xe1, 0xfc, 0x67, 0x3e,
+ 0x01, 0x7e, 0x97, 0xea, 0xdc, 0x6b, 0x96, 0x8f,
+ 0x38, 0x5c, 0x2a, 0xec, 0xb0, 0x3b, 0xfb, 0x32,
+ 0xaf, 0x3c, 0x54, 0xec, 0x18, 0xdb, 0x5c, 0x02,
+ 0x1a, 0xfe, 0x43, 0xfb, 0xfa, 0xaa, 0x3a, 0xfb,
+ 0x29, 0xd1, 0xe6, 0x05, 0x3c, 0x7c, 0x94, 0x75,
+ 0xd8, 0xbe, 0x61, 0x89, 0xf9, 0x5c, 0xbb, 0xa8,
+ 0x99, 0x0f, 0x95, 0xb1, 0xeb, 0xf1, 0xb3, 0x05,
+ 0xef, 0xf7, 0x00, 0xe9, 0xa1, 0x3a, 0xe5, 0xca,
+ 0x0b, 0xcb, 0xd0, 0x48, 0x47, 0x64, 0xbd, 0x1f,
+ 0x23, 0x1e, 0xa8, 0x1c, 0x7b, 0x64, 0xc5, 0x14,
+ 0x73, 0x5a, 0xc5, 0x5e, 0x4b, 0x79, 0x63, 0x3b,
+ 0x70, 0x64, 0x24, 0x11, 0x9e, 0x09, 0xdc, 0xaa,
+ 0xd4, 0xac, 0xf2, 0x1b, 0x10, 0xaf, 0x3b, 0x33,
+ 0xcd, 0xe3, 0x50, 0x48, 0x47, 0x15, 0x5c, 0xbb,
+ 0x6f, 0x22, 0x19, 0xba, 0x9b, 0x7d, 0xf5, 0x0b,
+ 0xe1, 0x1a, 0x1c, 0x7f, 0x23, 0xf8, 0x29, 0xf8,
+ 0xa4, 0x1b, 0x13, 0xb5, 0xca, 0x4e, 0xe8, 0x98,
+ 0x32, 0x38, 0xe0, 0x79, 0x4d, 0x3d, 0x34, 0xbc,
+ 0x5f, 0x4e, 0x77, 0xfa, 0xcb, 0x6c, 0x05, 0xac,
+ 0x86, 0x21, 0x2b, 0xaa, 0x1a, 0x55, 0xa2, 0xbe,
+ 0x70, 0xb5, 0x73, 0x3b, 0x04, 0x5c, 0xd3, 0x36,
+ 0x94, 0xb3, 0xaf, 0xe2, 0xf0, 0xe4, 0x9e, 0x4f,
+ 0x32, 0x15, 0x49, 0xfd, 0x82, 0x4e, 0xa9, 0x08,
+ 0x70, 0xd4, 0xb2, 0x8a, 0x29, 0x54, 0x48, 0x9a,
+ 0x0a, 0xbc, 0xd5, 0x0e, 0x18, 0xa8, 0x44, 0xac,
+ 0x5b, 0xf3, 0x8e, 0x4c, 0xd7, 0x2d, 0x9b, 0x09,
+ 0x42, 0xe5, 0x06, 0xc4, 0x33, 0xaf, 0xcd, 0xa3,
+ 0x84, 0x7f, 0x2d, 0xad, 0xd4, 0x76, 0x47, 0xde,
+ 0x32, 0x1c, 0xec, 0x4a, 0xc4, 0x30, 0xf6, 0x20,
+ 0x23, 0x85, 0x6c, 0xfb, 0xb2, 0x07, 0x04, 0xf4,
+ 0xec, 0x0b, 0xb9, 0x20, 0xba, 0x86, 0xc3, 0x3e,
+ 0x05, 0xf1, 0xec, 0xd9, 0x67, 0x33, 0xb7, 0x99,
+ 0x50, 0xa3, 0xe3, 0x14, 0xd3, 0xd9, 0x34, 0xf7,
+ 0x5e, 0xa0, 0xf2, 0x10, 0xa8, 0xf6, 0x05, 0x94,
+ 0x01, 0xbe, 0xb4, 0xbc, 0x44, 0x78, 0xfa, 0x49,
+ 0x69, 0xe6, 0x23, 0xd0, 0x1a, 0xda, 0x69 },
+ .digest_length = ZUC_EIA3_DIGEST_LEN,
+ .digest = {0xd1, 0x1e, 0x33, 0xf6}
+ },
+};
+
+/*
+ * MD5 test vectors from RFC 1321
+ */
+static crypto_test_reference_t md5_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_MD5,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = MD5_DIGEST_LEN,
+ .digest = { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
+ 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72}
+ },
+ {
+ .auth = ODP_AUTH_ALG_MD5,
+ .length = 62,
+ .plaintext = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 },
+ .ciphertext = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 },
+ .digest_length = MD5_DIGEST_LEN,
+ .digest = { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
+ 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f},
+ }
+};
+
+/*
+ * SHA test vectors from Crypto++
+ */
+static crypto_test_reference_t sha1_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA1,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = SHA1_DIGEST_LEN,
+ .digest = { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A,
+ 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C,
+ 0x9C, 0xD0, 0xD8, 0x9D},
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA1,
+ .length = 56,
+ .plaintext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .ciphertext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .digest_length = SHA1_DIGEST_LEN,
+ .digest = { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
+ 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
+ 0xE5, 0x46, 0x70, 0xF1},
+ }
+};
+
+static crypto_test_reference_t sha224_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA224,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = SHA224_DIGEST_LEN,
+ .digest = { 0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22,
+ 0x86, 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3,
+ 0x2a, 0xad, 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7,
+ 0xe3, 0x6c, 0x9d, 0xa7 },
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA224,
+ .length = 56,
+ .plaintext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .ciphertext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .digest_length = SHA224_DIGEST_LEN,
+ .digest = { 0x75, 0x38, 0x8b, 0x16, 0x51, 0x27, 0x76, 0xcc,
+ 0x5d, 0xba, 0x5d, 0xa1, 0xfd, 0x89, 0x01, 0x50,
+ 0xb0, 0xc6, 0x45, 0x5c, 0xb4, 0xf5, 0x8b, 0x19,
+ 0x52, 0x52, 0x25, 0x25},
+ }
+};
+
+static crypto_test_reference_t sha256_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA256,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = SHA256_DIGEST_LEN,
+ .digest = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad},
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA256,
+ .length = 56,
+ .plaintext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .ciphertext = { 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+ 0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+ 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+ 0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71},
+ .digest_length = SHA256_DIGEST_LEN,
+ .digest = { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1},
+ }
+};
+
+static crypto_test_reference_t sha384_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA384,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = SHA384_DIGEST_LEN,
+ .digest = { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+ 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+ 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+ 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+ 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+ 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7},
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA384,
+ .length = 112,
+ .plaintext = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75},
+ .ciphertext = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75},
+ .digest_length = SHA384_DIGEST_LEN,
+ .digest = { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8,
+ 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47,
+ 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
+ 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12,
+ 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
+ 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39},
+ }
+};
+
+static crypto_test_reference_t sha512_reference[] = {
+ {
+ .auth = ODP_AUTH_ALG_SHA512,
+ .length = 3,
+ .plaintext = { 0x61, 0x62, 0x63 },
+ .ciphertext = { 0x61, 0x62, 0x63 },
+ .digest_length = SHA512_DIGEST_LEN,
+ .digest = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f},
+ },
+ {
+ .auth = ODP_AUTH_ALG_SHA512,
+ .length = 112,
+ .plaintext = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75},
+ .ciphertext = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75},
+ .digest_length = SHA512_DIGEST_LEN,
+ .digest = { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+ 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+ 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+ 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+ 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+ 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+ 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+ 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09},
+ }
+};
+
+#endif
diff --git a/test/validation/api/crypto/test_vectors.h b/test/validation/api/crypto/test_vectors.h
new file mode 100644
index 000000000..33ba52d34
--- /dev/null
+++ b/test/validation/api/crypto/test_vectors.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef TEST_VECTORS_H
+#define TEST_VECTORS_H
+
+#include <odp_api.h>
+#include "test_vectors_len.h"
+
+typedef struct crypto_test_reference_s {
+ uint8_t copy_previous_vector; /* does not copy digest_length */
+ odp_cipher_alg_t cipher;
+ odp_auth_alg_t auth;
+ uint32_t cipher_key_length;
+ uint8_t cipher_key[MAX_KEY_LEN];
+ uint32_t auth_key_length;
+ uint8_t auth_key[MAX_KEY_LEN];
+ uint32_t cipher_iv_length;
+ uint8_t cipher_iv[MAX_IV_LEN];
+ uint32_t auth_iv_length;
+ uint8_t auth_iv[MAX_IV_LEN];
+ uint32_t length;
+ odp_bool_t is_length_in_bits;
+ uint8_t plaintext[MAX_DATA_LEN];
+ uint8_t ciphertext[MAX_DATA_LEN];
+ uint32_t aad_length;
+ uint8_t aad[MAX_AAD_LEN];
+ uint32_t digest_length;
+ uint8_t digest[MAX_DIGEST_LEN];
+} crypto_test_reference_t;
+
+ODP_STATIC_ASSERT(ODP_CIPHER_ALG_NULL == 0, "null cipher is not the default");
+ODP_STATIC_ASSERT(ODP_AUTH_ALG_NULL == 0, "null auth is not the default");
+
+/*
+ * Return test data length in bytes, rounding up to full bytes.
+ */
+static inline uint32_t ref_length_in_bytes(const crypto_test_reference_t *ref)
+{
+ return ref->is_length_in_bits ? (ref->length + 7) / 8 : ref->length;
+}
+
+/*
+ * Return test data length in bits
+ */
+static inline uint32_t ref_length_in_bits(const crypto_test_reference_t *ref)
+{
+ return ref->is_length_in_bits ? ref->length : 8 * ref->length;
+}
+
+static inline void init_reference(crypto_test_reference_t *ref, int size)
+{
+ int n;
+ crypto_test_reference_t *prev = NULL;
+
+ for (n = 0; n < size; n++) {
+ if (prev && ref[n].copy_previous_vector) {
+ uint32_t len;
+
+ len = ref[n].digest_length;
+ ref[n] = *prev;
+ ref[n].digest_length = len;
+ }
+ prev = &ref[n];
+ }
+}
+
+#endif
diff --git a/test/validation/api/crypto/test_vectors_len.h b/test/validation/api/crypto/test_vectors_len.h
new file mode 100644
index 000000000..92b5c8453
--- /dev/null
+++ b/test/validation/api/crypto/test_vectors_len.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+#ifndef TEST_VECTORS_LEN_
+#define TEST_VECTORS_LEN_
+
+/* Maximum */
+#define MAX_KEY_LEN 64
+#define MAX_IV_LEN 32
+#define MAX_DATA_LEN 1000
+#define MAX_AAD_LEN 12
+#define MAX_DIGEST_LEN 64
+
+/* TDES-CBC */
+#define TDES_CBC_KEY_LEN 24
+#define TDES_CBC_IV_LEN 8
+
+/* TDES-ECB */
+#define TDES_ECB_KEY_LEN 24
+
+/* AES common */
+
+#define AES128_KEY_LEN 16
+
+#define AES192_KEY_LEN 24
+
+#define AES256_KEY_LEN 32
+
+/* AES-CBC */
+#define AES_CBC_IV_LEN 16
+
+/* AES-CTR */
+#define AES_CTR_IV_LEN 16
+
+/* AES-CFB128 */
+#define AES_CFB128_IV_LEN 16
+
+/* AES-XTS */
+#define AES128_XTS_KEY_LEN 32
+#define AES256_XTS_KEY_LEN 64
+#define AES_XTS_IV_LEN 16
+
+/* AES-GCM */
+#define AES_GCM_IV_LEN 12
+#define AES_GCM_DIGEST_LEN 16
+
+/* HMAC-MD5 */
+#define HMAC_MD5_KEY_LEN 16
+#define HMAC_MD5_96_CHECK_LEN 12
+#define HMAC_MD5_CHECK_LEN 16
+
+/* HMAC-SHA1 */
+#define HMAC_SHA1_KEY_LEN 20
+#define HMAC_SHA1_96_CHECK_LEN 12
+#define HMAC_SHA1_CHECK_LEN 20
+
+/* HMAC-SHA224 */
+#define HMAC_SHA224_KEY_LEN 28
+#define HMAC_SHA224_CHECK_LEN 28
+
+/* HMAC-SHA256 */
+#define HMAC_SHA256_KEY_LEN 32
+#define HMAC_SHA256_128_CHECK_LEN 16
+#define HMAC_SHA256_CHECK_LEN 32
+
+/* HMAC-SHA384 */
+#define HMAC_SHA384_KEY_LEN 48
+#define HMAC_SHA384_192_CHECK_LEN 24
+#define HMAC_SHA384_CHECK_LEN 48
+
+/* HMAC-SHA512 */
+#define HMAC_SHA512_KEY_LEN 64
+#define HMAC_SHA512_256_CHECK_LEN 32
+#define HMAC_SHA512_CHECK_LEN 64
+
+/* ChaCha20-Poly1305 */
+#define CHACHA20_POLY1305_KEY_LEN 32
+#define CHACHA20_POLY1305_IV_LEN 12
+#define CHACHA20_POLY1305_CHECK_LEN 16
+
+/* AES-XCBC-MAC */
+#define AES_XCBC_MAC_KEY_LEN 16
+#define AES_XCBC_MAC_96_CHECK_LEN 12
+#define AES_XCBC_MAC_CHECK_LEN 16
+
+/* KASUMI_F8 */
+#define KASUMI_F8_KEY_LEN 16
+#define KASUMI_F8_IV_LEN 8
+
+/* SNOW3G_UEA2 */
+#define SNOW3G_UEA2_KEY_LEN 16
+#define SNOW3G_UEA2_IV_LEN 16
+
+/* AES_EEA2 */
+#define AES_EEA2_KEY_LEN 16
+#define AES_EEA2_IV_LEN 16
+
+/* ZUC_EEA3 */
+#define ZUC_EEA3_KEY_LEN 16
+#define ZUC_EEA3_IV_LEN 16
+
+/* ZUC_EEA3_256 */
+#define ZUC_EEA3_256_KEY_LEN 32
+#define ZUC_EEA3_256_IV_LEN 25
+
+/* KASUMI_F9 */
+#define KASUMI_F9_KEY_LEN 16
+#define KASUMI_F9_IV_LEN 9
+#define KASUMI_F9_DIGEST_LEN 4
+
+/* SNOW3G_UIA2 */
+#define SNOW3G_UIA2_KEY_LEN 16
+#define SNOW3G_UIA2_IV_LEN 16
+#define SNOW3G_UIA2_DIGEST_LEN 4
+
+/* AES_EIA2 */
+#define AES_EIA2_KEY_LEN 16
+#define AES_EIA2_IV_LEN 8
+#define AES_EIA2_DIGEST_LEN 4
+
+/* ZUC_EIA3 */
+#define ZUC_EIA3_KEY_LEN 16
+#define ZUC_EIA3_IV_LEN 16
+#define ZUC_EIA3_DIGEST_LEN 4
+
+/* ZUC_EIA3_256 */
+#define ZUC_EIA3_256_KEY_LEN 32
+#define ZUC_EIA3_256_IV_LEN 25
+
+/* MD5 */
+#define MD5_DIGEST_LEN 16
+
+/* SHA1 */
+#define SHA1_DIGEST_LEN 20
+
+/* SHA224 */
+#define SHA224_DIGEST_LEN 28
+
+/* SHA256 */
+#define SHA256_DIGEST_LEN 32
+
+/* SHA384 */
+#define SHA384_DIGEST_LEN 48
+
+/* SHA512 */
+#define SHA512_DIGEST_LEN 64
+
+#endif
diff --git a/test/validation/api/crypto/util.c b/test/validation/api/crypto/util.c
new file mode 100644
index 000000000..787a782f5
--- /dev/null
+++ b/test/validation/api/crypto/util.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <odp/helper/odph_api.h>
+#include "util.h"
+
+struct suite_context_s suite_context;
+
+const char *auth_alg_name(odp_auth_alg_t auth)
+{
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ return "ODP_AUTH_ALG_NULL";
+ case ODP_AUTH_ALG_MD5_HMAC:
+ return "ODP_AUTH_ALG_MD5_HMAC";
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ return "ODP_AUTH_ALG_SHA1_HMAC";
+ case ODP_AUTH_ALG_SHA224_HMAC:
+ return "ODP_AUTH_ALG_SHA224_HMAC";
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ return "ODP_AUTH_ALG_SHA256_HMAC";
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ return "ODP_AUTH_ALG_SHA384_HMAC";
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ return "ODP_AUTH_ALG_SHA512_HMAC";
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ return "ODP_AUTH_ALG_AES_XCBC_MAC";
+ case ODP_AUTH_ALG_AES_GCM:
+ return "ODP_AUTH_ALG_AES_GCM";
+ case ODP_AUTH_ALG_AES_GMAC:
+ return "ODP_AUTH_ALG_AES_GMAC";
+ case ODP_AUTH_ALG_AES_CCM:
+ return "ODP_AUTH_ALG_AES_CCM";
+ case ODP_AUTH_ALG_AES_CMAC:
+ return "ODP_AUTH_ALG_AES_CMAC";
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ return "ODP_AUTH_ALG_CHACHA20_POLY1305";
+ case ODP_AUTH_ALG_KASUMI_F9:
+ return "ODP_AUTH_ALG_KASUMI_F9";
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ return "ODP_AUTH_ALG_SNOW3G_UIA2";
+ case ODP_AUTH_ALG_AES_EIA2:
+ return "ODP_AUTH_ALG_AES_EIA2";
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ return "ODP_AUTH_ALG_ZUC_EIA3";
+ case ODP_AUTH_ALG_MD5:
+ return "ODP_AUTH_ALG_MD5";
+ case ODP_AUTH_ALG_SHA1:
+ return "ODP_AUTH_ALG_SHA1";
+ case ODP_AUTH_ALG_SHA224:
+ return "ODP_AUTH_ALG_SHA224";
+ case ODP_AUTH_ALG_SHA256:
+ return "ODP_AUTH_ALG_SHA256";
+ case ODP_AUTH_ALG_SHA384:
+ return "ODP_AUTH_ALG_SHA384";
+ case ODP_AUTH_ALG_SHA512:
+ return "ODP_AUTH_ALG_SHA512";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *cipher_alg_name(odp_cipher_alg_t cipher)
+{
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ return "ODP_CIPHER_ALG_NULL";
+ case ODP_CIPHER_ALG_DES:
+ return "ODP_CIPHER_ALG_DES";
+ case ODP_CIPHER_ALG_3DES_CBC:
+ return "ODP_CIPHER_ALG_3DES_CBC";
+ case ODP_CIPHER_ALG_3DES_ECB:
+ return "ODP_CIPHER_ALG_3DES_ECB";
+ case ODP_CIPHER_ALG_AES_CBC:
+ return "ODP_CIPHER_ALG_AES_CBC";
+ case ODP_CIPHER_ALG_AES_CTR:
+ return "ODP_CIPHER_ALG_AES_CTR";
+ case ODP_CIPHER_ALG_AES_ECB:
+ return "ODP_CIPHER_ALG_AES_ECB";
+ case ODP_CIPHER_ALG_AES_CFB128:
+ return "ODP_CIPHER_ALG_AES_CFB128";
+ case ODP_CIPHER_ALG_AES_XTS:
+ return "ODP_CIPHER_ALG_AES_XTS";
+ case ODP_CIPHER_ALG_AES_GCM:
+ return "ODP_CIPHER_ALG_AES_GCM";
+ case ODP_CIPHER_ALG_AES_CCM:
+ return "ODP_CIPHER_ALG_AES_CCM";
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ return "ODP_CIPHER_ALG_CHACHA20_POLY1305";
+ case ODP_CIPHER_ALG_KASUMI_F8:
+ return "ODP_CIPHER_ALG_KASUMI_F8";
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ return "ODP_CIPHER_ALG_SNOW3G_UEA2";
+ case ODP_CIPHER_ALG_AES_EEA2:
+ return "ODP_CIPHER_ALG_AES_EEA2";
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ return "ODP_CIPHER_ALG_ZUC_EEA3";
+ default:
+ return "Unknown";
+ }
+}
+
+int check_alg_support(odp_cipher_alg_t cipher, odp_auth_alg_t auth)
+{
+ odp_crypto_capability_t capability;
+
+ memset(&capability, 0, sizeof(odp_crypto_capability_t));
+ if (odp_crypto_capability(&capability)) {
+ ODPH_ERR("odp_crypto_capability() failed\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ if (suite_context.queue != ODP_QUEUE_INVALID) {
+ if (suite_context.q_type == ODP_QUEUE_TYPE_PLAIN &&
+ capability.queue_type_plain == 0)
+ return ODP_TEST_INACTIVE;
+ if (suite_context.q_type == ODP_QUEUE_TYPE_SCHED &&
+ capability.queue_type_sched == 0)
+ return ODP_TEST_INACTIVE;
+ }
+
+ if (suite_context.op_mode == ODP_CRYPTO_SYNC &&
+ capability.sync_mode == ODP_SUPPORT_NO)
+ return ODP_TEST_INACTIVE;
+ if (suite_context.op_mode == ODP_CRYPTO_ASYNC &&
+ capability.async_mode == ODP_SUPPORT_NO)
+ return ODP_TEST_INACTIVE;
+
+ /* Cipher algorithms */
+ switch (cipher) {
+ case ODP_CIPHER_ALG_NULL:
+ if (!capability.ciphers.bit.null)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_DES:
+ if (!capability.ciphers.bit.des)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_3DES_CBC:
+ if (!capability.ciphers.bit.trides_cbc)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_3DES_ECB:
+ if (!capability.ciphers.bit.trides_ecb)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_CBC:
+ if (!capability.ciphers.bit.aes_cbc)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_CTR:
+ if (!capability.ciphers.bit.aes_ctr)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_ECB:
+ if (!capability.ciphers.bit.aes_ecb)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_CFB128:
+ if (!capability.ciphers.bit.aes_cfb128)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_XTS:
+ if (!capability.ciphers.bit.aes_xts)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_GCM:
+ if (!capability.ciphers.bit.aes_gcm)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_CCM:
+ if (!capability.ciphers.bit.aes_ccm)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_CHACHA20_POLY1305:
+ if (!capability.ciphers.bit.chacha20_poly1305)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_KASUMI_F8:
+ if (!capability.ciphers.bit.kasumi_f8)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_SNOW3G_UEA2:
+ if (!capability.ciphers.bit.snow3g_uea2)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_AES_EEA2:
+ if (!capability.ciphers.bit.aes_eea2)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_CIPHER_ALG_ZUC_EEA3:
+ if (!capability.ciphers.bit.zuc_eea3)
+ return ODP_TEST_INACTIVE;
+ break;
+ default:
+ ODPH_ERR("Unsupported cipher algorithm\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ /* Authentication algorithms */
+ switch (auth) {
+ case ODP_AUTH_ALG_NULL:
+ if (!capability.auths.bit.null)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_MD5_HMAC:
+ if (!capability.auths.bit.md5_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA1_HMAC:
+ if (!capability.auths.bit.sha1_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA224_HMAC:
+ if (!capability.auths.bit.sha224_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA256_HMAC:
+ if (!capability.auths.bit.sha256_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA384_HMAC:
+ if (!capability.auths.bit.sha384_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA512_HMAC:
+ if (!capability.auths.bit.sha512_hmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_XCBC_MAC:
+ if (!capability.auths.bit.aes_xcbc_mac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_GCM:
+ if (!capability.auths.bit.aes_gcm)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_GMAC:
+ if (!capability.auths.bit.aes_gmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_CCM:
+ if (!capability.auths.bit.aes_ccm)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_CMAC:
+ if (!capability.auths.bit.aes_cmac)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_CHACHA20_POLY1305:
+ if (!capability.auths.bit.chacha20_poly1305)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_KASUMI_F9:
+ if (!capability.auths.bit.kasumi_f9)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SNOW3G_UIA2:
+ if (!capability.auths.bit.snow3g_uia2)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_AES_EIA2:
+ if (!capability.auths.bit.aes_eia2)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_ZUC_EIA3:
+ if (!capability.auths.bit.zuc_eia3)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_MD5:
+ if (!capability.auths.bit.md5)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA1:
+ if (!capability.auths.bit.sha1)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA224:
+ if (!capability.auths.bit.sha224)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA256:
+ if (!capability.auths.bit.sha256)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA384:
+ if (!capability.auths.bit.sha384)
+ return ODP_TEST_INACTIVE;
+ break;
+ case ODP_AUTH_ALG_SHA512:
+ if (!capability.auths.bit.sha512)
+ return ODP_TEST_INACTIVE;
+ break;
+ default:
+ ODPH_ERR("Unsupported authentication algorithm\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
diff --git a/test/validation/api/crypto/util.h b/test/validation/api/crypto/util.h
new file mode 100644
index 000000000..b6a013255
--- /dev/null
+++ b/test/validation/api/crypto/util.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <stdint.h>
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+struct suite_context_s {
+ odp_crypto_op_mode_t op_mode;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_queue_type_t q_type;
+ odp_event_t (*compl_queue_deq)(void);
+ int partial_test;
+};
+
+extern struct suite_context_s suite_context;
+
+const char *auth_alg_name(odp_auth_alg_t auth);
+
+const char *cipher_alg_name(odp_cipher_alg_t cipher);
+
+/*
+ * Check if given cipher and authentication algorithms are supported
+ *
+ * cipher Cipher algorithm
+ * auth Authentication algorithm
+ *
+ * returns ODP_TEST_ACTIVE when both algorithms are supported or
+ * ODP_TEST_INACTIVE when either algorithm is not supported
+ */
+int check_alg_support(odp_cipher_alg_t cipher, odp_auth_alg_t auth);
+
+static inline void fill_with_pattern(uint8_t *buf, uint32_t len)
+{
+ static uint8_t value;
+
+ for (uint32_t n = 0; n < len; n++)
+ buf[n] = value++;
+}
+
+#endif
diff --git a/test/validation/api/dma/.gitignore b/test/validation/api/dma/.gitignore
new file mode 100644
index 000000000..cc14794d5
--- /dev/null
+++ b/test/validation/api/dma/.gitignore
@@ -0,0 +1 @@
+dma_main
diff --git a/test/validation/api/dma/Makefile.am b/test/validation/api/dma/Makefile.am
new file mode 100644
index 000000000..795825c6b
--- /dev/null
+++ b/test/validation/api/dma/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = dma_main
+dma_main_SOURCES = dma.c
diff --git a/test/validation/api/dma/dma.c b/test/validation/api/dma/dma.c
new file mode 100644
index 000000000..3f71e730f
--- /dev/null
+++ b/test/validation/api/dma/dma.c
@@ -0,0 +1,1703 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define COMPL_POOL_NAME "DMA compl pool"
+
+#define MIN_SEG_LEN 1024
+#define SHM_ALIGN ODP_CACHE_LINE_SIZE
+#define RETRIES 5
+#define TIMEOUT 5
+#define OFFSET 10
+#define TRAILER 10
+#define MULTI 1
+#define RESULT 1
+#define USER_DATA 0xdeadbeef
+#define ELEM_NUM 10u
+#define UAREA 0xaa
+
+typedef struct global_t {
+ odp_dma_capability_t dma_capa;
+ odp_shm_t shm;
+ int disabled;
+ uint8_t *src_addr;
+ uint8_t *dst_addr;
+ uint32_t data_size;
+ uint32_t len;
+ odp_pool_t pkt_pool;
+ uint32_t pkt_len;
+ odp_queue_t queue;
+ odp_pool_t compl_pool;
+ uint32_t event_count;
+ uint32_t cache_size;
+
+} global_t;
+
+typedef struct {
+ uint32_t count;
+ uint8_t mark[ELEM_NUM];
+} uarea_init_t;
+
+static global_t global;
+
+static int dma_suite_init(void)
+{
+ odp_shm_t shm;
+ odp_pool_param_t pool_param;
+ odp_dma_pool_param_t dma_pool_param;
+ odp_pool_capability_t pool_capa;
+ odp_queue_param_t queue_param;
+ uint32_t shm_len, pkt_len;
+ void *addr;
+
+ memset(&global, 0, sizeof(global_t));
+ global.shm = ODP_SHM_INVALID;
+ global.pkt_pool = ODP_POOL_INVALID;
+ global.queue = ODP_QUEUE_INVALID;
+ global.compl_pool = ODP_POOL_INVALID;
+
+ if (odp_dma_capability(&global.dma_capa)) {
+ ODPH_ERR("DMA capability failed\n");
+ return -1;
+ }
+
+ if (global.dma_capa.max_sessions == 0) {
+ global.disabled = 1;
+ ODPH_DBG("DMA test disabled\n");
+ return 0;
+ }
+
+ shm_len = MIN_SEG_LEN * global.dma_capa.max_segs * global.dma_capa.max_transfers;
+ shm = odp_shm_reserve("DMA test", shm_len, SHM_ALIGN, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM reserve failed\n");
+ return -1;
+ }
+
+ addr = odp_shm_addr(shm);
+
+ if (addr == NULL) {
+ ODPH_ERR("SHM addr failed\n");
+ return -1;
+ }
+
+ global.shm = shm;
+ global.data_size = shm_len / 2;
+ global.src_addr = addr;
+ global.dst_addr = (uint8_t *)global.src_addr + global.data_size;
+ global.len = global.data_size - OFFSET - TRAILER;
+
+ if (odp_pool_capability(&pool_capa)) {
+ ODPH_ERR("Pool capa failed\n");
+ return -1;
+ }
+
+ pkt_len = pool_capa.pkt.max_len;
+ if (pkt_len == 0)
+ pkt_len = 4000;
+
+ pkt_len = ODPH_MIN(pkt_len, global.dma_capa.max_seg_len);
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = global.dma_capa.max_src_segs + global.dma_capa.max_dst_segs;
+ pool_param.pkt.len = pkt_len;
+ pool_param.pkt.max_len = pkt_len;
+
+ global.pkt_len = pkt_len;
+ global.pkt_pool = odp_pool_create("DMA test pkt pool", &pool_param);
+
+ if (global.pkt_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool create failed\n");
+ return -1;
+ }
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global.queue = odp_queue_create("DMA test queue", &queue_param);
+
+ if (global.queue == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Queue create failed\n");
+ return -1;
+ }
+
+ if (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) {
+ if (global.dma_capa.pool.max_num < global.dma_capa.max_transfers) {
+ ODPH_ERR("Too small DMA compl pool %u\n", global.dma_capa.pool.max_num);
+ return -1;
+ }
+
+ odp_dma_pool_param_init(&dma_pool_param);
+ dma_pool_param.num = global.dma_capa.max_transfers;
+ global.cache_size = dma_pool_param.cache_size;
+
+ global.compl_pool = odp_dma_pool_create(COMPL_POOL_NAME, &dma_pool_param);
+
+ if (global.compl_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Completion pool create failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int dma_suite_term(void)
+{
+ if (global.compl_pool != ODP_POOL_INVALID &&
+ odp_pool_destroy(global.compl_pool)) {
+ ODPH_ERR("Completion pool destroy failed\n");
+ return -1;
+ }
+
+ if (global.queue != ODP_QUEUE_INVALID &&
+ odp_queue_destroy(global.queue)) {
+ ODPH_ERR("Queue destroy failed\n");
+ return -1;
+ }
+
+ if (global.pkt_pool != ODP_POOL_INVALID &&
+ odp_pool_destroy(global.pkt_pool)) {
+ ODPH_ERR("Packet pool destroy failed\n");
+ return -1;
+ }
+
+ if (global.shm != ODP_SHM_INVALID &&
+ odp_shm_free(global.shm)) {
+ ODPH_ERR("SHM free failed\n");
+ return -1;
+ }
+
+ return odp_cunit_print_inactive();
+}
+
+static void test_dma_capability(void)
+{
+ odp_dma_capability_t capa;
+
+ memset(&capa, 0, sizeof(odp_dma_capability_t));
+ CU_ASSERT_FATAL(odp_dma_capability(&capa) == 0);
+
+ if (capa.max_sessions == 0)
+ return;
+
+ CU_ASSERT(capa.max_transfers > 0);
+ CU_ASSERT(capa.max_src_segs > 0);
+ CU_ASSERT(capa.max_dst_segs > 0);
+ CU_ASSERT(capa.max_segs > 1);
+ CU_ASSERT(capa.max_segs > capa.max_src_segs);
+ CU_ASSERT(capa.max_segs > capa.max_dst_segs);
+ CU_ASSERT(capa.max_seg_len > 0);
+ CU_ASSERT(capa.compl_mode_mask & ODP_DMA_COMPL_SYNC);
+
+ if (capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) {
+ odp_pool_capability_t pool_capa;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+
+ CU_ASSERT(capa.queue_type_sched || capa.queue_type_plain);
+ CU_ASSERT(capa.pool.max_pools > 0 && capa.pool.max_pools <= pool_capa.max_pools);
+ CU_ASSERT(capa.pool.max_num > 0);
+ CU_ASSERT(capa.pool.min_cache_size <= capa.pool.max_cache_size);
+ }
+}
+
+static void test_dma_param(uint8_t fill)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_compl_param_t compl_param;
+ odp_dma_pool_param_t dma_pool_param;
+
+ memset(&dma_param, fill, sizeof(dma_param));
+ odp_dma_param_init(&dma_param);
+ CU_ASSERT(dma_param.direction == ODP_DMA_MAIN_TO_MAIN);
+ CU_ASSERT(dma_param.type == ODP_DMA_TYPE_COPY);
+ CU_ASSERT(dma_param.mt_mode == ODP_DMA_MT_SAFE);
+ CU_ASSERT(dma_param.order == ODP_DMA_ORDER_NONE);
+
+ memset(&trs_param, fill, sizeof(trs_param));
+ odp_dma_transfer_param_init(&trs_param);
+ CU_ASSERT(trs_param.src_format == ODP_DMA_FORMAT_ADDR);
+ CU_ASSERT(trs_param.dst_format == ODP_DMA_FORMAT_ADDR);
+ CU_ASSERT(trs_param.num_src == 1);
+ CU_ASSERT(trs_param.num_dst == 1);
+
+ memset(&compl_param, fill, sizeof(compl_param));
+ odp_dma_compl_param_init(&compl_param);
+ CU_ASSERT(compl_param.user_ptr == NULL);
+
+ memset(&dma_pool_param, fill, sizeof(dma_pool_param));
+ odp_dma_pool_param_init(&dma_pool_param);
+ CU_ASSERT(dma_pool_param.uarea_init.init_fn == NULL);
+ CU_ASSERT(dma_pool_param.uarea_init.args == NULL);
+ CU_ASSERT(dma_pool_param.uarea_size == 0);
+ CU_ASSERT(dma_pool_param.cache_size <= global.dma_capa.pool.max_cache_size);
+ CU_ASSERT(dma_pool_param.cache_size >= global.dma_capa.pool.min_cache_size);
+}
+
+static void test_dma_param_init(void)
+{
+ test_dma_param(0);
+ test_dma_param(0xff);
+}
+
+static void test_dma_debug(void)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_t dma, dma2;
+ uint64_t u64;
+ const char *name = "dma_debug";
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma = odp_dma_create(name, &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ dma2 = odp_dma_lookup(name);
+ CU_ASSERT(dma2 != ODP_DMA_INVALID);
+ CU_ASSERT(dma2 == dma);
+
+ u64 = odp_dma_to_u64(dma);
+ CU_ASSERT(u64 != odp_dma_to_u64(ODP_DMA_INVALID));
+ printf("\n DMA handle: 0x%" PRIx64 "\n", u64);
+
+ odp_dma_print(dma);
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_same_name_null(void)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_t dma_a, dma_b;
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma_a = odp_dma_create(NULL, &dma_param);
+
+ CU_ASSERT_FATAL(dma_a != ODP_DMA_INVALID);
+
+ dma_b = odp_dma_create(NULL, &dma_param);
+
+ CU_ASSERT_FATAL(dma_b != ODP_DMA_INVALID);
+ CU_ASSERT(odp_dma_to_u64(dma_a) != odp_dma_to_u64(dma_b));
+ CU_ASSERT(odp_dma_destroy(dma_a) == 0);
+ CU_ASSERT(odp_dma_destroy(dma_b) == 0);
+}
+
+static void test_dma_same_name_named(void)
+{
+ odp_dma_param_t dma_param;
+ const char *name = "DMA session";
+ odp_dma_t dma, dma_a, dma_b;
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma_a = odp_dma_create(name, &dma_param);
+
+ CU_ASSERT_FATAL(dma_a != ODP_DMA_INVALID);
+
+ dma = odp_dma_lookup(name);
+
+ CU_ASSERT(odp_dma_to_u64(dma) == odp_dma_to_u64(dma_a));
+
+ dma_b = odp_dma_create(name, &dma_param);
+
+ CU_ASSERT_FATAL(dma_b != ODP_DMA_INVALID);
+
+ dma = odp_dma_lookup(name);
+
+ CU_ASSERT(odp_dma_to_u64(dma) == odp_dma_to_u64(dma_a) ||
+ odp_dma_to_u64(dma) == odp_dma_to_u64(dma_b));
+ CU_ASSERT(odp_dma_to_u64(dma_a) != odp_dma_to_u64(dma_b));
+ CU_ASSERT(odp_dma_destroy(dma_a) == 0);
+ CU_ASSERT(odp_dma_destroy(dma_b) == 0);
+}
+
+static void test_dma_compl_pool(void)
+{
+ odp_pool_t pool;
+ odp_pool_info_t pool_info;
+ odp_dma_compl_t compl[global.dma_capa.max_transfers];
+ odp_event_t ev;
+ uint64_t u64;
+ int ret;
+ uint32_t i, j;
+ const char *name = COMPL_POOL_NAME;
+
+ CU_ASSERT_FATAL(global.compl_pool != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(name);
+ CU_ASSERT(pool == global.compl_pool);
+
+ memset(&pool_info, 0x55, sizeof(odp_pool_info_t));
+ ret = odp_pool_info(global.compl_pool, &pool_info);
+ CU_ASSERT(ret == 0);
+ CU_ASSERT(strcmp(pool_info.name, name) == 0);
+ CU_ASSERT(pool_info.pool_ext == 0);
+ CU_ASSERT(pool_info.type == ODP_POOL_DMA_COMPL);
+ CU_ASSERT(pool_info.dma_pool_param.num == global.dma_capa.max_transfers);
+ CU_ASSERT(pool_info.dma_pool_param.uarea_size == 0);
+ CU_ASSERT(pool_info.dma_pool_param.cache_size == global.cache_size);
+
+ for (i = 0; i < global.dma_capa.max_transfers; i++) {
+ compl[i] = odp_dma_compl_alloc(global.compl_pool);
+
+ u64 = odp_dma_compl_to_u64(compl[i]);
+ CU_ASSERT(u64 != odp_dma_compl_to_u64(ODP_DMA_COMPL_INVALID));
+
+ if (compl[i] == ODP_DMA_COMPL_INVALID)
+ break;
+
+ /* No source pool for DMA completion events */
+ ev = odp_dma_compl_to_event(compl[i]);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+
+ printf("\n DMA compl handle: 0x%" PRIx64 "\n", u64);
+ odp_dma_compl_print(compl[i]);
+ }
+
+ for (j = 0; j < i; j++)
+ odp_dma_compl_free(compl[j]);
+}
+
+static void test_dma_compl_pool_same_name(void)
+{
+ odp_dma_pool_param_t dma_pool_param;
+ odp_pool_t pool, pool_a, pool_b;
+ const char *name = COMPL_POOL_NAME;
+
+ pool_a = global.compl_pool;
+
+ pool = odp_pool_lookup(name);
+ CU_ASSERT(pool == pool_a);
+
+ odp_dma_pool_param_init(&dma_pool_param);
+ dma_pool_param.num = global.dma_capa.max_transfers;
+
+ /* Second pool with the same name */
+ pool_b = odp_dma_pool_create(name, &dma_pool_param);
+ CU_ASSERT_FATAL(pool_b != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(name);
+ CU_ASSERT(pool == pool_a || pool == pool_b);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool_b) == 0);
+}
+
+static void test_dma_compl_pool_max_pools(void)
+{
+ odp_dma_pool_param_t dma_pool_param;
+ /* Max pools minus the ones already created in global init */
+ uint32_t num = global.dma_capa.pool.max_pools - 2, i, j;
+ odp_pool_t pools[num];
+ int ret;
+
+ odp_dma_pool_param_init(&dma_pool_param);
+ dma_pool_param.num = global.dma_capa.max_transfers;
+
+ for (i = 0; i < num; i++) {
+ pools[i] = odp_dma_pool_create(NULL, &dma_pool_param);
+ CU_ASSERT(pools[i] != ODP_POOL_INVALID);
+
+ if (pools[i] == ODP_POOL_INVALID) {
+ ODPH_ERR("DMA completion pool create failed: %u / %u\n", i, num);
+ break;
+ }
+ }
+
+ for (j = 0; j < i; j++) {
+ ret = odp_pool_destroy(pools[j]);
+ CU_ASSERT(ret == 0);
+
+ if (ret == -1)
+ ODPH_ERR("DMA completion pool destroy failed: %u / %u\n", j, i);
+ }
+}
+
+static void test_dma_compl_user_area(void)
+{
+ odp_dma_pool_param_t dma_pool_param;
+ uint32_t num = ODPH_MIN(ELEM_NUM, global.dma_capa.pool.max_num),
+ size = global.dma_capa.pool.max_uarea_size, i;
+ odp_pool_t pool;
+ odp_dma_compl_t compl_evs[num];
+ void *addr, *prev = NULL;
+
+ odp_dma_pool_param_init(&dma_pool_param);
+ dma_pool_param.num = num;
+ dma_pool_param.uarea_size = size;
+ pool = odp_dma_pool_create(NULL, &dma_pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag = 0;
+
+ compl_evs[i] = odp_dma_compl_alloc(pool);
+
+ if (compl_evs[i] == ODP_DMA_COMPL_INVALID)
+ break;
+
+ addr = odp_dma_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ ev = odp_dma_compl_to_event(compl_evs[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag < 0);
+
+ prev = addr;
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num);
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_dma_compl_free(compl_evs[j]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void init_event_uarea(void *uarea, uint32_t size, void *args, uint32_t index)
+{
+ uarea_init_t *data = args;
+
+ data->count++;
+ data->mark[index] = 1;
+ memset(uarea, UAREA, size);
+}
+
+static void test_dma_compl_user_area_init(void)
+{
+ odp_dma_pool_param_t dma_pool_param;
+ uint32_t num = ODPH_MIN(ELEM_NUM, global.dma_capa.pool.max_num), i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_dma_compl_t compl_evs[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_dma_pool_param_init(&dma_pool_param);
+ dma_pool_param.uarea_init.init_fn = init_event_uarea;
+ dma_pool_param.uarea_init.args = &data;
+ dma_pool_param.num = num;
+ dma_pool_param.uarea_size = 1;
+ pool = odp_dma_pool_create(NULL, &dma_pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ compl_evs[i] = odp_dma_compl_alloc(pool);
+
+ CU_ASSERT(compl_evs[i] != ODP_DMA_COMPL_INVALID);
+
+ if (compl_evs[i] == ODP_DMA_COMPL_INVALID)
+ break;
+
+ uarea = odp_dma_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_dma_compl_free(compl_evs[j]);
+
+ odp_pool_destroy(pool);
+}
+
+static void init_source(uint8_t *src, uint32_t len)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ src[i] = i;
+}
+
+static int check_equal(uint8_t *src, uint8_t *dst, uint32_t len)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ if (src[i] != dst[i])
+ return -1;
+
+ return 0;
+}
+
+static int check_zero(uint8_t *ptr, uint32_t len)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ if (ptr[i])
+ return -1;
+
+ return 0;
+}
+
+static int do_transfer(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param, int multi, int res)
+{
+ int i, ret;
+ odp_dma_result_t result;
+ const odp_dma_transfer_param_t *trs_ptr[1] = {trs_param};
+ odp_dma_result_t *result_ptr[1] = {&result};
+
+ memset(&result, 0, sizeof(odp_dma_result_t));
+
+ for (i = 0; i < RETRIES; i++) {
+ if (!multi && !res)
+ ret = odp_dma_transfer(dma, trs_param, NULL);
+ else if (!multi && res)
+ ret = odp_dma_transfer(dma, trs_param, &result);
+ else if (multi && !res)
+ ret = odp_dma_transfer_multi(dma, trs_ptr, NULL, 1);
+ else
+ ret = odp_dma_transfer_multi(dma, trs_ptr, result_ptr, 1);
+
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == 1);
+
+ if (res)
+ CU_ASSERT(result.success);
+
+ return ret;
+}
+
+static int do_transfer_async(odp_dma_t dma, odp_dma_transfer_param_t *trs_param,
+ odp_dma_compl_mode_t compl_mode, int multi)
+{
+ int num_trs = multi ? multi : 1;
+ odp_dma_compl_param_t compl_param[num_trs];
+ const odp_dma_compl_param_t *compl_ptr[num_trs];
+ const odp_dma_transfer_param_t *trs_ptr[num_trs];
+ odp_event_t ev;
+ odp_dma_compl_t compl;
+ int i, j, ret, done;
+ uint32_t user_data = USER_DATA;
+ odp_dma_result_t result;
+ uint64_t wait_ns = 500 * ODP_TIME_MSEC_IN_NS;
+ uint64_t sched_wait = odp_schedule_wait_time(wait_ns);
+ void *user_ptr = &user_data;
+
+ for (i = 0; i < num_trs; i++) {
+ odp_dma_compl_param_init(&compl_param[i]);
+ compl_param[i].compl_mode = compl_mode;
+
+ if (compl_mode == ODP_DMA_COMPL_EVENT) {
+ compl = odp_dma_compl_alloc(global.compl_pool);
+
+ CU_ASSERT(compl != ODP_DMA_COMPL_INVALID);
+ if (compl == ODP_DMA_COMPL_INVALID)
+ return -1;
+
+ compl_param[i].event = odp_dma_compl_to_event(compl);
+ compl_param[i].queue = global.queue;
+ } else if (compl_mode == ODP_DMA_COMPL_POLL) {
+ compl_param[i].transfer_id = odp_dma_transfer_id_alloc(dma);
+
+ CU_ASSERT(compl_param[i].transfer_id != ODP_DMA_TRANSFER_ID_INVALID);
+ if (compl_param[i].transfer_id == ODP_DMA_TRANSFER_ID_INVALID)
+ return -1;
+ } else if (compl_mode != ODP_DMA_COMPL_NONE) {
+ ODPH_ERR("Wrong compl mode: %u\n", compl_mode);
+ return -1;
+ }
+
+ compl_param[i].user_ptr = user_ptr;
+
+ if (multi) {
+ trs_ptr[i] = &trs_param[i];
+ compl_ptr[i] = &compl_param[i];
+ }
+ }
+
+ for (i = 0; i < RETRIES; i++) {
+ if (multi)
+ ret = odp_dma_transfer_start_multi(dma, trs_ptr, compl_ptr, num_trs);
+ else
+ ret = odp_dma_transfer_start(dma, trs_param, compl_param);
+
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == num_trs);
+
+ if (ret < 1)
+ return ret;
+
+ for (i = 0; i < ret; i++) {
+ memset(&result, 0, sizeof(odp_dma_result_t));
+
+ if (compl_mode == ODP_DMA_COMPL_POLL) {
+ for (j = 0; j < TIMEOUT; j++) {
+ done = odp_dma_transfer_done(dma, compl_param[i].transfer_id,
+ &result);
+ if (done)
+ break;
+
+ odp_time_wait_ns(wait_ns);
+ }
+
+ CU_ASSERT(done == 1);
+ CU_ASSERT(result.success);
+ CU_ASSERT(result.user_ptr == user_ptr);
+ CU_ASSERT(user_data == USER_DATA);
+
+ odp_dma_transfer_id_free(dma, compl_param[i].transfer_id);
+ } else if (compl_mode == ODP_DMA_COMPL_EVENT) {
+ odp_queue_t from = ODP_QUEUE_INVALID;
+
+ for (j = 0; j < TIMEOUT; j++) {
+ ev = odp_schedule(&from, sched_wait);
+ if (ev != ODP_EVENT_INVALID)
+ break;
+ }
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ if (ev == ODP_EVENT_INVALID)
+ return -1;
+
+ CU_ASSERT(from == global.queue);
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_DMA_COMPL);
+
+ compl = odp_dma_compl_from_event(ev);
+ CU_ASSERT(compl != ODP_DMA_COMPL_INVALID);
+
+ CU_ASSERT(odp_dma_compl_result(compl, &result) == 0);
+ CU_ASSERT(result.success);
+ CU_ASSERT(result.user_ptr == user_ptr);
+ CU_ASSERT(user_data == USER_DATA);
+
+ /* Test also without result struct output */
+ CU_ASSERT(odp_dma_compl_result(compl, NULL) == 0);
+
+ /* Test compl event print on the first event */
+ if (global.event_count == 0) {
+ printf("\n\n");
+ odp_dma_compl_print(compl);
+ }
+
+ /* Test both ways to free the event */
+ if (global.event_count % 2)
+ odp_event_free(ev);
+ else
+ odp_dma_compl_free(compl);
+
+ global.event_count++;
+ }
+ }
+
+ return 1;
+}
+
+static void test_dma_addr_to_addr(odp_dma_compl_mode_t compl_mode_mask, uint32_t num,
+ int multi, int res)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg[num];
+ odp_dma_seg_t dst_seg[num];
+ int ret;
+ uint32_t i, cur_len;
+ uint8_t *src = global.src_addr + OFFSET;
+ uint8_t *dst = global.dst_addr + OFFSET;
+ uint32_t seg_len = ODPH_MIN(global.len / num, global.dma_capa.max_seg_len);
+ uint32_t len = seg_len * num;
+ uint32_t offset = 0;
+
+ init_source(global.src_addr, global.data_size);
+ memset(global.dst_addr, 0, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("addr_to_addr", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ memset(src_seg, 0, sizeof(src_seg));
+ memset(dst_seg, 0, sizeof(dst_seg));
+
+ for (i = 0; i < num; i++) {
+ cur_len = seg_len;
+ if (i == num - 1)
+ cur_len = len - seg_len * i;
+
+ src_seg[i].addr = src + offset;
+ src_seg[i].len = cur_len;
+ dst_seg[i].addr = dst + offset;
+ dst_seg[i].len = cur_len;
+ offset += cur_len;
+ }
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.num_src = num;
+ trs_param.num_dst = num;
+ trs_param.src_seg = src_seg;
+ trs_param.dst_seg = dst_seg;
+
+ if (compl_mode_mask == ODP_DMA_COMPL_SYNC)
+ ret = do_transfer(dma, &trs_param, multi, res);
+ else
+ ret = do_transfer_async(dma, &trs_param, compl_mode_mask, multi);
+
+ if (ret > 0) {
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(global.dst_addr, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, global.len - len + TRAILER) == 0);
+ }
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_addr_to_addr_trs(odp_dma_compl_mode_t compl_mode_mask, uint32_t num_trs,
+ int multi, int res)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg;
+ odp_dma_seg_t dst_seg;
+ int compl_none;
+ uint32_t i, cur_len;
+ odp_dma_compl_mode_t compl_mode;
+ uint8_t *src = global.src_addr + OFFSET;
+ uint8_t *dst = global.dst_addr + OFFSET;
+ uint32_t trs_len = ODPH_MIN(global.len / num_trs, global.dma_capa.max_seg_len);
+ uint32_t len = trs_len * num_trs;
+ uint32_t offset = 0;
+ int ret = -1;
+
+ compl_none = 0;
+ if (compl_mode_mask & ODP_DMA_COMPL_NONE)
+ compl_none = 1;
+
+ init_source(global.src_addr, global.data_size);
+ memset(global.dst_addr, 0, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("addr_to_addr", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_seg = &src_seg;
+ trs_param.dst_seg = &dst_seg;
+
+ memset(&src_seg, 0, sizeof(src_seg));
+ memset(&dst_seg, 0, sizeof(dst_seg));
+
+ for (i = 0; i < num_trs; i++) {
+ compl_mode = compl_mode_mask;
+ if (compl_none)
+ compl_mode = ODP_DMA_COMPL_NONE;
+
+ cur_len = trs_len;
+ if (i == num_trs - 1) {
+ cur_len = len - trs_len * i;
+ compl_mode = compl_mode_mask & ~ODP_DMA_COMPL_NONE;
+ }
+
+ src_seg.addr = src + offset;
+ src_seg.len = cur_len;
+ dst_seg.addr = dst + offset;
+ dst_seg.len = cur_len;
+ offset += cur_len;
+
+ if (compl_mode_mask == ODP_DMA_COMPL_SYNC)
+ ret = do_transfer(dma, &trs_param, multi, res);
+ else
+ ret = do_transfer_async(dma, &trs_param, compl_mode, multi);
+
+ if (ret < 1)
+ break;
+ }
+
+ if (ret > 0) {
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(global.dst_addr, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, global.len - len + TRAILER) == 0);
+ }
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_addr_to_addr_max_trs(odp_dma_compl_mode_t compl_mode_mask)
+{
+ odp_dma_param_t dma_param;
+ uint32_t num_trs = global.dma_capa.max_transfers;
+ odp_dma_transfer_param_t trs_param[num_trs];
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg[num_trs];
+ odp_dma_seg_t dst_seg[num_trs];
+ int ret;
+ uint32_t i, cur_len;
+ uint8_t *src = global.src_addr + OFFSET;
+ uint8_t *dst = global.dst_addr + OFFSET;
+ uint32_t seg_len = ODPH_MIN(global.len / num_trs, global.dma_capa.max_seg_len);
+ uint32_t len = seg_len * num_trs;
+ uint32_t offset = 0;
+
+ init_source(global.src_addr, global.data_size);
+ memset(global.dst_addr, 0, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("addr_to_addr", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ memset(src_seg, 0, sizeof(src_seg));
+ memset(dst_seg, 0, sizeof(dst_seg));
+
+ for (i = 0; i < num_trs; i++) {
+ cur_len = seg_len;
+ if (i == num_trs - 1)
+ cur_len = len - seg_len * i;
+
+ src_seg[i].addr = src + offset;
+ src_seg[i].len = cur_len;
+ dst_seg[i].addr = dst + offset;
+ dst_seg[i].len = cur_len;
+ offset += cur_len;
+ }
+
+ for (i = 0; i < num_trs; i++) {
+ odp_dma_transfer_param_init(&trs_param[i]);
+ trs_param[i].num_src = 1;
+ trs_param[i].num_dst = 1;
+ trs_param[i].src_seg = &src_seg[i];
+ trs_param[i].dst_seg = &dst_seg[i];
+ }
+
+ ret = do_transfer_async(dma, trs_param, compl_mode_mask, num_trs);
+
+ if (ret > 0) {
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(global.dst_addr, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, global.len - len + TRAILER) == 0);
+ }
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_addr_to_pkt(odp_dma_compl_mode_t compl_mode_mask, int multi)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg;
+ odp_dma_seg_t dst_seg;
+ int ret;
+ uint8_t *src, *pkt_data;
+ odp_packet_t pkt;
+ uint32_t len, seg_len;
+
+ init_source(global.src_addr, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("addr_to_pkt", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ pkt = odp_packet_alloc(global.pkt_pool, global.pkt_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ seg_len = odp_packet_seg_len(pkt);
+ pkt_data = odp_packet_data(pkt);
+ memset(pkt_data, 0, seg_len);
+ CU_ASSERT_FATAL(seg_len > OFFSET + TRAILER);
+
+ len = seg_len - OFFSET - TRAILER;
+ if (len > global.len)
+ len = global.len;
+
+ src = global.src_addr + OFFSET;
+
+ memset(&src_seg, 0, sizeof(odp_dma_seg_t));
+ memset(&dst_seg, 0, sizeof(odp_dma_seg_t));
+ src_seg.addr = src;
+ src_seg.len = len;
+ dst_seg.packet = pkt;
+ dst_seg.offset = OFFSET;
+ dst_seg.len = len;
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_ADDR;
+ trs_param.dst_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.src_seg = &src_seg;
+ trs_param.dst_seg = &dst_seg;
+
+ if (compl_mode_mask == ODP_DMA_COMPL_SYNC)
+ ret = do_transfer(dma, &trs_param, multi, 0);
+ else
+ ret = do_transfer_async(dma, &trs_param, compl_mode_mask, multi);
+
+ if (ret > 0) {
+ uint8_t *dst = pkt_data + OFFSET;
+
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(pkt_data, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, TRAILER) == 0);
+ }
+
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_pkt_to_addr(odp_dma_compl_mode_t compl_mode_mask, int multi)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg;
+ odp_dma_seg_t dst_seg;
+ int ret;
+ uint8_t *dst, *pkt_data;
+ odp_packet_t pkt;
+ uint32_t len, seg_len;
+
+ memset(global.dst_addr, 0, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("pkt_to_addr", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ pkt = odp_packet_alloc(global.pkt_pool, global.pkt_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ seg_len = odp_packet_seg_len(pkt);
+ pkt_data = odp_packet_data(pkt);
+ init_source(pkt_data, seg_len);
+
+ CU_ASSERT_FATAL(seg_len > OFFSET + TRAILER);
+
+ len = seg_len - OFFSET - TRAILER;
+ if (len > global.len)
+ len = global.len;
+
+ dst = global.dst_addr + OFFSET;
+
+ memset(&src_seg, 0, sizeof(odp_dma_seg_t));
+ memset(&dst_seg, 0, sizeof(odp_dma_seg_t));
+ src_seg.packet = pkt;
+ src_seg.offset = OFFSET;
+ src_seg.len = len;
+ dst_seg.addr = dst;
+ dst_seg.len = len;
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.dst_format = ODP_DMA_FORMAT_ADDR;
+ trs_param.src_seg = &src_seg;
+ trs_param.dst_seg = &dst_seg;
+
+ if (compl_mode_mask == ODP_DMA_COMPL_SYNC)
+ ret = do_transfer(dma, &trs_param, multi, 0);
+ else
+ ret = do_transfer_async(dma, &trs_param, compl_mode_mask, multi);
+
+ if (ret > 0) {
+ uint8_t *src = pkt_data + OFFSET;
+
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(global.dst_addr, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, TRAILER) == 0);
+ }
+
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_pkt_to_pkt(odp_dma_compl_mode_t compl_mode_mask, int multi)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg;
+ odp_dma_seg_t dst_seg;
+ int ret;
+ uint8_t *pkt_data, *pkt_data_2;
+ odp_packet_t pkt, pkt_2;
+ uint32_t len, seg_len, seg_len_2;
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = compl_mode_mask;
+ dma = odp_dma_create("pkt_to_pkt", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ pkt = odp_packet_alloc(global.pkt_pool, global.pkt_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ pkt_2 = odp_packet_alloc(global.pkt_pool, global.pkt_len);
+ CU_ASSERT_FATAL(pkt_2 != ODP_PACKET_INVALID);
+
+ seg_len = odp_packet_seg_len(pkt);
+ pkt_data = odp_packet_data(pkt);
+ init_source(pkt_data, seg_len);
+
+ seg_len_2 = odp_packet_seg_len(pkt_2);
+ pkt_data_2 = odp_packet_data(pkt_2);
+ memset(pkt_data_2, 0, seg_len_2);
+
+ CU_ASSERT_FATAL(seg_len > OFFSET + TRAILER);
+
+ if (seg_len > seg_len_2)
+ seg_len = seg_len_2;
+
+ len = seg_len - OFFSET - TRAILER;
+
+ memset(&src_seg, 0, sizeof(odp_dma_seg_t));
+ memset(&dst_seg, 0, sizeof(odp_dma_seg_t));
+ src_seg.packet = pkt;
+ src_seg.offset = OFFSET;
+ src_seg.len = len;
+ dst_seg.packet = pkt_2;
+ dst_seg.offset = OFFSET;
+ dst_seg.len = len;
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.dst_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.src_seg = &src_seg;
+ trs_param.dst_seg = &dst_seg;
+
+ if (compl_mode_mask == ODP_DMA_COMPL_SYNC)
+ ret = do_transfer(dma, &trs_param, multi, 0);
+ else
+ ret = do_transfer_async(dma, &trs_param, compl_mode_mask, multi);
+
+ if (ret > 0) {
+ uint8_t *src = pkt_data + OFFSET;
+ uint8_t *dst = pkt_data_2 + OFFSET;
+
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ CU_ASSERT(check_zero(pkt_data_2, OFFSET) == 0);
+ CU_ASSERT(check_zero(dst + len, TRAILER) == 0);
+ }
+
+ odp_packet_free(pkt);
+ odp_packet_free(pkt_2);
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_pkt_segs_to_addr_sync(void)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg;
+ odp_dma_seg_t dst_seg;
+ int ret;
+ uint8_t *dst;
+ odp_packet_t pkt;
+ uint32_t i, len, num_segs;
+ uint32_t pkt_len = ODPH_MIN(global.pkt_len, global.len);
+
+ memset(global.dst_addr, 0, global.data_size);
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma = odp_dma_create("pkt_segs_to_addr", &dma_param);
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ pkt = odp_packet_alloc(global.pkt_pool, pkt_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ num_segs = odp_packet_num_segs(pkt);
+ if (num_segs > global.dma_capa.max_src_segs)
+ num_segs = global.dma_capa.max_src_segs;
+
+ init_source(global.src_addr, global.data_size);
+ CU_ASSERT_FATAL(odp_packet_copy_from_mem(pkt, 0, pkt_len, global.src_addr) == 0);
+
+ len = pkt_len - OFFSET - TRAILER;
+ dst = global.dst_addr + OFFSET;
+
+ memset(&src_seg, 0, sizeof(odp_dma_seg_t));
+ memset(&dst_seg, 0, sizeof(odp_dma_seg_t));
+ src_seg.packet = pkt;
+ src_seg.offset = OFFSET;
+ src_seg.len = len;
+ dst_seg.addr = dst;
+ dst_seg.len = len;
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.dst_format = ODP_DMA_FORMAT_ADDR;
+ trs_param.src_seg = &src_seg;
+ trs_param.dst_seg = &dst_seg;
+
+ for (i = 0; i < RETRIES; i++) {
+ ret = odp_dma_transfer(dma, &trs_param, NULL);
+
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret > 0);
+
+ if (ret > 0) {
+ odp_packet_seg_t pkt_seg = odp_packet_first_seg(pkt);
+ uint8_t *src = odp_packet_data(pkt);
+ uint32_t seg_len = odp_packet_seg_len(pkt);
+
+ src += OFFSET;
+ seg_len -= OFFSET;
+
+ for (i = 0; i < num_segs; i++) {
+ if (i == (num_segs - 1))
+ seg_len -= TRAILER;
+
+ CU_ASSERT(check_equal(src, dst, seg_len) == 0);
+
+ dst += seg_len;
+ pkt_seg = odp_packet_next_seg(pkt, pkt_seg);
+ if (pkt_seg != ODP_PACKET_SEG_INVALID) {
+ src = odp_packet_seg_data(pkt, pkt_seg);
+ seg_len = odp_packet_seg_data_len(pkt, pkt_seg);
+ }
+ }
+
+ CU_ASSERT(check_zero(global.dst_addr, OFFSET) == 0);
+ CU_ASSERT(check_zero(global.dst_addr + OFFSET + len, TRAILER) == 0);
+ }
+
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static int check_sync(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_session_count(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.max_sessions > 1)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_event(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_event_user_area(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if ((global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) &&
+ global.dma_capa.pool.max_uarea_size > 0)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_event_user_area_init(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.pool.max_uarea_size > 0 && global.dma_capa.pool.uarea_persistence)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_scheduled(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.queue_type_sched &&
+ (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_poll(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_POLL)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_sched_none(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.queue_type_sched &&
+ (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) &&
+ (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_NONE))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_poll_none(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_POLL &&
+ global.dma_capa.compl_mode_mask & ODP_DMA_COMPL_NONE)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void test_dma_addr_to_addr_sync(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 1, 0, 0);
+}
+
+static void test_dma_addr_to_addr_sync_mtrs(void)
+{
+ test_dma_addr_to_addr_trs(ODP_DMA_COMPL_SYNC, global.dma_capa.max_transfers * 2, 0, 0);
+}
+
+static void test_dma_addr_to_addr_sync_mseg(void)
+{
+ if (global.dma_capa.max_src_segs > 1 && global.dma_capa.max_dst_segs > 1)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 2, 0, 0);
+
+ if (global.dma_capa.max_src_segs > 2 && global.dma_capa.max_dst_segs > 2)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 3, 0, 0);
+}
+
+static void test_dma_addr_to_addr_sync_res(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 1, 0, RESULT);
+}
+
+static void get_seg_lens(uint32_t max_len, uint32_t *src, uint32_t *dst)
+{
+ uint32_t src_segs = *src, dst_segs = *dst, denom = ODPH_MIN(src_segs, dst_segs);
+
+ max_len = ODPH_MIN(max_len / denom, global.dma_capa.max_seg_len) * denom;
+ *src = max_len / src_segs;
+ *dst = *src * src_segs / dst_segs + *src * src_segs % dst_segs;
+}
+
+static void test_dma_addr_to_addr_sync_max_seg(void)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_dma_seg_t src_seg[global.dma_capa.max_src_segs];
+ odp_dma_seg_t dst_seg[global.dma_capa.max_dst_segs];
+ uint32_t src_len = global.dma_capa.max_src_segs, dst_len = global.dma_capa.max_dst_segs,
+ len;
+ int ret;
+
+ init_source(global.src_addr, global.data_size);
+ memset(global.dst_addr, 0, global.data_size);
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma = odp_dma_create("addr_to_addr_max_seg", &dma_param);
+
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ get_seg_lens(global.len, &src_len, &dst_len);
+
+ for (uint32_t i = 0; i < global.dma_capa.max_src_segs; i++) {
+ uint8_t *addr = global.src_addr + i * src_len;
+
+ memset(&src_seg[i], 0, sizeof(odp_dma_seg_t));
+ src_seg[i].addr = addr;
+ src_seg[i].len = src_len;
+ }
+
+ len = src_len * global.dma_capa.max_src_segs;
+
+ for (uint32_t i = 0; i < global.dma_capa.max_dst_segs; i++) {
+ uint8_t *addr = global.dst_addr + i * dst_len;
+
+ memset(&dst_seg[i], 0, sizeof(odp_dma_seg_t));
+ dst_seg[i].addr = addr;
+ dst_seg[i].len = ODPH_MIN(len, dst_len);
+ len -= dst_len;
+ }
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_ADDR;
+ trs_param.dst_format = ODP_DMA_FORMAT_ADDR;
+ trs_param.num_src = global.dma_capa.max_src_segs;
+ trs_param.num_dst = global.dma_capa.max_dst_segs;
+ trs_param.src_seg = src_seg;
+ trs_param.dst_seg = dst_seg;
+ ret = do_transfer(dma, &trs_param, 0, 0);
+
+ if (ret > 0) {
+ len = src_len * global.dma_capa.max_src_segs;
+
+ CU_ASSERT(check_equal(global.src_addr, global.dst_addr, len) == 0);
+ }
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_addr_to_pkt_sync(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_SYNC, 0);
+}
+
+static void test_dma_pkt_to_addr_sync(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_SYNC, 0);
+}
+
+static void test_dma_pkt_to_pkt_sync(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_SYNC, 0);
+}
+
+static void test_dma_pkt_to_pkt_sync_max_seg(void)
+{
+ odp_dma_param_t dma_param;
+ odp_dma_transfer_param_t trs_param;
+ odp_dma_t dma;
+ odp_packet_t pkt;
+ odp_dma_seg_t src_seg[global.dma_capa.max_src_segs];
+ odp_dma_seg_t dst_seg[global.dma_capa.max_dst_segs];
+ uint32_t src_len = global.dma_capa.max_src_segs, dst_len = global.dma_capa.max_dst_segs,
+ len;
+ int ret;
+
+ odp_dma_param_init(&dma_param);
+ dma_param.compl_mode_mask = ODP_DMA_COMPL_SYNC;
+ dma = odp_dma_create("pkt_to_pkt_max_seg", &dma_param);
+
+ CU_ASSERT_FATAL(dma != ODP_DMA_INVALID);
+
+ pkt = odp_packet_alloc(global.pkt_pool, global.pkt_len);
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ get_seg_lens(odp_packet_seg_len(pkt), &src_len, &dst_len);
+ odp_packet_free(pkt);
+
+ for (uint32_t i = 0; i < global.dma_capa.max_src_segs; i++) {
+ pkt = odp_packet_alloc(global.pkt_pool, src_len);
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ init_source(odp_packet_data(pkt), src_len);
+ memset(&src_seg[i], 0, sizeof(odp_dma_seg_t));
+ src_seg[i].packet = pkt;
+ src_seg[i].len = src_len;
+ }
+
+ len = src_len * global.dma_capa.max_src_segs;
+
+ for (uint32_t i = 0; i < global.dma_capa.max_dst_segs; i++) {
+ pkt = odp_packet_alloc(global.pkt_pool, dst_len);
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ memset(odp_packet_data(pkt), 0, dst_len);
+ memset(&dst_seg[i], 0, sizeof(odp_dma_seg_t));
+ dst_seg[i].packet = pkt;
+ dst_seg[i].len = ODPH_MIN(len, dst_len);
+ len -= dst_len;
+ }
+
+ odp_dma_transfer_param_init(&trs_param);
+ trs_param.src_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.dst_format = ODP_DMA_FORMAT_PACKET;
+ trs_param.num_src = global.dma_capa.max_src_segs;
+ trs_param.num_dst = global.dma_capa.max_dst_segs;
+ trs_param.src_seg = src_seg;
+ trs_param.dst_seg = dst_seg;
+ ret = do_transfer(dma, &trs_param, 0, 0);
+
+ if (ret > 0) {
+ len = src_len * global.dma_capa.max_src_segs;
+ uint8_t src[len], dst[len];
+
+ for (uint32_t i = 0; i < global.dma_capa.max_src_segs; i++) {
+ memcpy(src + i * src_len, odp_packet_data(src_seg[i].packet),
+ src_seg[i].len);
+ odp_packet_free(src_seg[i].packet);
+ }
+
+ for (uint32_t i = 0; i < global.dma_capa.max_dst_segs; i++) {
+ memcpy(dst + i * dst_len, odp_packet_data(dst_seg[i].packet),
+ dst_seg[i].len);
+ odp_packet_free(dst_seg[i].packet);
+ }
+
+ CU_ASSERT(check_equal(src, dst, len) == 0);
+ }
+
+ CU_ASSERT(odp_dma_destroy(dma) == 0);
+}
+
+static void test_dma_addr_to_addr_poll(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_POLL, 1, 0, 0);
+}
+
+static void test_dma_addr_to_addr_poll_mtrs(void)
+{
+ test_dma_addr_to_addr_trs(ODP_DMA_COMPL_POLL, 2, 0, 0);
+}
+
+static void test_dma_addr_to_addr_poll_mseg(void)
+{
+ if (global.dma_capa.max_src_segs > 1 && global.dma_capa.max_dst_segs > 1)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_POLL, 2, 0, 0);
+
+ if (global.dma_capa.max_src_segs > 2 && global.dma_capa.max_dst_segs > 2)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_POLL, 3, 0, 0);
+}
+
+static void test_dma_addr_to_pkt_poll(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_POLL, 0);
+}
+
+static void test_dma_pkt_to_addr_poll(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_POLL, 0);
+}
+
+static void test_dma_pkt_to_pkt_poll(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_POLL, 0);
+}
+
+static void test_dma_addr_to_addr_event(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_EVENT, 1, 0, 0);
+}
+
+static void test_dma_addr_to_addr_event_mtrs(void)
+{
+ test_dma_addr_to_addr_trs(ODP_DMA_COMPL_EVENT, 2, 0, 0);
+}
+
+static void test_dma_addr_to_addr_event_mseg(void)
+{
+ if (global.dma_capa.max_src_segs > 1 && global.dma_capa.max_dst_segs > 1)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_EVENT, 2, 0, 0);
+
+ if (global.dma_capa.max_src_segs > 2 && global.dma_capa.max_dst_segs > 2)
+ test_dma_addr_to_addr(ODP_DMA_COMPL_EVENT, 3, 0, 0);
+}
+
+static void test_dma_addr_to_pkt_event(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_EVENT, 0);
+}
+
+static void test_dma_pkt_to_addr_event(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_EVENT, 0);
+}
+
+static void test_dma_pkt_to_pkt_event(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_EVENT, 0);
+}
+
+static void test_dma_addr_to_addr_poll_none(void)
+{
+ test_dma_addr_to_addr_trs(ODP_DMA_COMPL_POLL | ODP_DMA_COMPL_NONE, 2, 0, 0);
+}
+
+static void test_dma_addr_to_addr_event_none(void)
+{
+ test_dma_addr_to_addr_trs(ODP_DMA_COMPL_EVENT | ODP_DMA_COMPL_NONE, 2, 0, 0);
+}
+
+static void test_dma_multi_addr_to_addr_sync(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 1, MULTI, 0);
+}
+
+static void test_dma_multi_addr_to_addr_sync_res(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_SYNC, 1, MULTI, RESULT);
+}
+
+static void test_dma_multi_addr_to_pkt_sync(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_SYNC, MULTI);
+}
+
+static void test_dma_multi_pkt_to_addr_sync(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_SYNC, MULTI);
+}
+
+static void test_dma_multi_pkt_to_pkt_sync(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_SYNC, MULTI);
+}
+
+static void test_dma_multi_addr_to_addr_poll(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_POLL, 1, MULTI, 0);
+}
+
+static void test_dma_multi_addr_to_addr_poll_max_trs(void)
+{
+ test_dma_addr_to_addr_max_trs(ODP_DMA_COMPL_POLL);
+}
+
+static void test_dma_multi_addr_to_pkt_poll(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_POLL, MULTI);
+}
+
+static void test_dma_multi_pkt_to_addr_poll(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_POLL, MULTI);
+}
+
+static void test_dma_multi_pkt_to_pkt_poll(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_POLL, MULTI);
+}
+
+static void test_dma_multi_addr_to_addr_event(void)
+{
+ test_dma_addr_to_addr(ODP_DMA_COMPL_EVENT, 1, MULTI, 0);
+}
+
+static void test_dma_multi_addr_to_addr_event_max_trs(void)
+{
+ test_dma_addr_to_addr_max_trs(ODP_DMA_COMPL_EVENT);
+}
+
+static void test_dma_multi_addr_to_pkt_event(void)
+{
+ test_dma_addr_to_pkt(ODP_DMA_COMPL_EVENT, MULTI);
+}
+
+static void test_dma_multi_pkt_to_addr_event(void)
+{
+ test_dma_pkt_to_addr(ODP_DMA_COMPL_EVENT, MULTI);
+}
+
+static void test_dma_multi_pkt_to_pkt_event(void)
+{
+ test_dma_pkt_to_pkt(ODP_DMA_COMPL_EVENT, MULTI);
+}
+
+odp_testinfo_t dma_suite[] = {
+ ODP_TEST_INFO(test_dma_capability),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_param_init, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_debug, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_same_name_null, check_session_count),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_same_name_named, check_session_count),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_compl_pool, check_event),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_compl_pool_same_name, check_event),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_compl_pool_max_pools, check_event),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_compl_user_area, check_event_user_area),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_compl_user_area_init, check_event_user_area_init),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_sync_mtrs, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_sync_mseg, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_sync_res, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_sync_max_seg, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_pkt_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_addr_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_pkt_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_pkt_sync_max_seg, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_poll_mtrs, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_poll_mseg, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_pkt_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_addr_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_pkt_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_event_mtrs, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_event_mseg, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_pkt_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_addr_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_to_pkt_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_poll_none, check_poll_none),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_addr_to_addr_event_none, check_sched_none),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_sync_res, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_pkt_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_addr_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_pkt_sync, check_sync),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_poll_max_trs, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_pkt_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_addr_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_pkt_poll, check_poll),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_addr_event_max_trs, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_addr_to_pkt_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_addr_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_multi_pkt_to_pkt_event, check_scheduled),
+ ODP_TEST_INFO_CONDITIONAL(test_dma_pkt_segs_to_addr_sync, check_sync),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t dma_suites[] = {
+ {"DMA", dma_suite_init, dma_suite_term, dma_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(dma_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/errno/.gitignore b/test/validation/api/errno/.gitignore
index 12256e38c..12256e38c 100644
--- a/test/common_plat/validation/api/errno/.gitignore
+++ b/test/validation/api/errno/.gitignore
diff --git a/test/validation/api/errno/Makefile.am b/test/validation/api/errno/Makefile.am
new file mode 100644
index 000000000..de13afbfb
--- /dev/null
+++ b/test/validation/api/errno/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = errno_main
+errno_main_SOURCES = errno.c
diff --git a/test/validation/api/errno/errno.c b/test/validation/api/errno/errno.c
new file mode 100644
index 000000000..1cbd27b1b
--- /dev/null
+++ b/test/validation/api/errno/errno.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include "odp_cunit_common.h"
+
+static void errno_test_odp_errno_sunny_day(void)
+{
+ int my_errno;
+
+ odp_errno_zero();
+ my_errno = odp_errno();
+ CU_ASSERT(my_errno == 0);
+ odp_errno_print("odp_errno");
+ CU_ASSERT(odp_errno_str(my_errno) != NULL);
+}
+
+odp_testinfo_t errno_suite[] = {
+ ODP_TEST_INFO(errno_test_odp_errno_sunny_day),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t errno_suites[] = {
+ {"Errno", NULL, NULL, errno_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(errno_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/event/.gitignore b/test/validation/api/event/.gitignore
new file mode 100644
index 000000000..05d34d7c8
--- /dev/null
+++ b/test/validation/api/event/.gitignore
@@ -0,0 +1 @@
+event_main
diff --git a/test/validation/api/event/Makefile.am b/test/validation/api/event/Makefile.am
new file mode 100644
index 000000000..0d26035ed
--- /dev/null
+++ b/test/validation/api/event/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = event_main
+event_main_SOURCES = event.c
diff --git a/test/validation/api/event/event.c b/test/validation/api/event/event.c
new file mode 100644
index 000000000..a4f967791
--- /dev/null
+++ b/test/validation/api/event/event.c
@@ -0,0 +1,471 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#define NUM_EVENTS 100
+#define EVENT_SIZE 100
+#define EVENT_BURST 10
+
+static void event_test_free(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ int i;
+ odp_buffer_t buf;
+ odp_packet_t pkt;
+ odp_timeout_t tmo;
+ odp_event_subtype_t subtype;
+ odp_event_t event[EVENT_BURST];
+
+ /* Buffer events */
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.num = NUM_EVENTS;
+ pool_param.buf.size = EVENT_SIZE;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < EVENT_BURST; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT(odp_event_is_valid(odp_buffer_to_event(buf)) == 1);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ event[i] = odp_buffer_to_event(buf);
+ CU_ASSERT(odp_event_type(event[i]) == ODP_EVENT_BUFFER);
+ CU_ASSERT(odp_event_subtype(event[i]) == ODP_EVENT_NO_SUBTYPE);
+ CU_ASSERT(odp_event_types(event[i], &subtype) ==
+ ODP_EVENT_BUFFER);
+ CU_ASSERT(subtype == ODP_EVENT_NO_SUBTYPE);
+ }
+
+ for (i = 0; i < EVENT_BURST; i++)
+ odp_event_free(event[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ /* Packet events */
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = NUM_EVENTS;
+ pool_param.pkt.len = EVENT_SIZE;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < EVENT_BURST; i++) {
+ pkt = odp_packet_alloc(pool, EVENT_SIZE);
+ CU_ASSERT(odp_event_is_valid(odp_packet_to_event(pkt)) == 1);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ event[i] = odp_packet_to_event(pkt);
+ CU_ASSERT(odp_event_type(event[i]) == ODP_EVENT_PACKET);
+ CU_ASSERT(odp_event_subtype(event[i]) ==
+ ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_event_types(event[i], &subtype) ==
+ ODP_EVENT_PACKET);
+ CU_ASSERT(subtype == ODP_EVENT_PACKET_BASIC);
+ }
+
+ for (i = 0; i < EVENT_BURST; i++)
+ odp_event_free(event[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ /* Timeout events */
+ odp_pool_param_init(&pool_param);
+ pool_param.tmo.num = NUM_EVENTS;
+ pool_param.type = ODP_POOL_TIMEOUT;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < EVENT_BURST; i++) {
+ tmo = odp_timeout_alloc(pool);
+ CU_ASSERT(odp_event_is_valid(odp_timeout_to_event(tmo)) == 1);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+ event[i] = odp_timeout_to_event(tmo);
+ CU_ASSERT(odp_event_type(event[i]) == ODP_EVENT_TIMEOUT);
+ CU_ASSERT(odp_event_subtype(event[i]) == ODP_EVENT_NO_SUBTYPE);
+ CU_ASSERT(odp_event_types(event[i], &subtype) ==
+ ODP_EVENT_TIMEOUT);
+ CU_ASSERT(subtype == ODP_EVENT_NO_SUBTYPE);
+ }
+
+ for (i = 0; i < EVENT_BURST; i++)
+ odp_event_free(event[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void event_test_free_multi(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ int i, j;
+ odp_buffer_t buf;
+ odp_packet_t pkt;
+ odp_timeout_t tmo;
+ odp_event_t event[EVENT_BURST];
+
+ /* Buffer events */
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.num = NUM_EVENTS;
+ pool_param.buf.size = EVENT_SIZE;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < EVENT_BURST; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ event[i] = odp_buffer_to_event(buf);
+ }
+
+ if (j == 0)
+ odp_event_free_multi(event, EVENT_BURST);
+ else
+ odp_event_free_sp(event, EVENT_BURST);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ /* Packet events */
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = NUM_EVENTS;
+ pool_param.pkt.len = EVENT_SIZE;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < EVENT_BURST; i++) {
+ pkt = odp_packet_alloc(pool, EVENT_SIZE);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ event[i] = odp_packet_to_event(pkt);
+ }
+
+ if (j == 0)
+ odp_event_free_multi(event, EVENT_BURST);
+ else
+ odp_event_free_sp(event, EVENT_BURST);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ /* Timeout events */
+ odp_pool_param_init(&pool_param);
+ pool_param.tmo.num = NUM_EVENTS;
+ pool_param.type = ODP_POOL_TIMEOUT;
+
+ pool = odp_pool_create("event_free", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < EVENT_BURST; i++) {
+ tmo = odp_timeout_alloc(pool);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+ event[i] = odp_timeout_to_event(tmo);
+ }
+
+ if (j == 0)
+ odp_event_free_multi(event, EVENT_BURST);
+ else
+ odp_event_free_sp(event, EVENT_BURST);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void event_test_free_multi_mixed(void)
+{
+ odp_pool_t pool1, pool2, pool3;
+ odp_pool_param_t pool_param;
+ int i, j;
+ odp_buffer_t buf;
+ odp_packet_t pkt;
+ odp_timeout_t tmo;
+ odp_event_t event[3 * EVENT_BURST];
+
+ /* Buffer events */
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.num = NUM_EVENTS;
+ pool_param.buf.size = EVENT_SIZE;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool1 = odp_pool_create("event_free1", &pool_param);
+ CU_ASSERT_FATAL(pool1 != ODP_POOL_INVALID);
+
+ /* Packet events */
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = NUM_EVENTS;
+ pool_param.pkt.len = EVENT_SIZE;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool2 = odp_pool_create("event_free2", &pool_param);
+ CU_ASSERT_FATAL(pool2 != ODP_POOL_INVALID);
+
+ /* Timeout events */
+ odp_pool_param_init(&pool_param);
+ pool_param.tmo.num = NUM_EVENTS;
+ pool_param.type = ODP_POOL_TIMEOUT;
+
+ pool3 = odp_pool_create("event_free3", &pool_param);
+ CU_ASSERT_FATAL(pool3 != ODP_POOL_INVALID);
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 3 * EVENT_BURST;) {
+ buf = odp_buffer_alloc(pool1);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ event[i] = odp_buffer_to_event(buf);
+ i++;
+ pkt = odp_packet_alloc(pool2, EVENT_SIZE);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ event[i] = odp_packet_to_event(pkt);
+ i++;
+ tmo = odp_timeout_alloc(pool3);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+ event[i] = odp_timeout_to_event(tmo);
+ i++;
+ }
+
+ if (j == 0)
+ odp_event_free_multi(event, 3 * EVENT_BURST);
+ else
+ odp_event_free_sp(event, 3 * EVENT_BURST);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool1) == 0);
+ CU_ASSERT(odp_pool_destroy(pool2) == 0);
+ CU_ASSERT(odp_pool_destroy(pool3) == 0);
+}
+
+#define NUM_TYPE_TEST 6
+
+static void type_test_init(odp_pool_t *buf_pool, odp_pool_t *pkt_pool,
+ odp_event_t buf_event[],
+ odp_event_t pkt_event[],
+ odp_event_t event[])
+{
+ odp_pool_t pool1, pool2;
+ odp_pool_param_t pool_param;
+ int i;
+ odp_buffer_t buf;
+ odp_packet_t pkt;
+
+ /* Buffer events */
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.num = NUM_EVENTS;
+ pool_param.buf.size = EVENT_SIZE;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool1 = odp_pool_create("event_type_buf", &pool_param);
+ CU_ASSERT_FATAL(pool1 != ODP_POOL_INVALID);
+
+ for (i = 0; i < NUM_TYPE_TEST; i++) {
+ buf = odp_buffer_alloc(pool1);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ buf_event[i] = odp_buffer_to_event(buf);
+ }
+
+ /* Packet events */
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = NUM_EVENTS;
+ pool_param.pkt.len = EVENT_SIZE;
+ pool_param.type = ODP_POOL_PACKET;
+
+ pool2 = odp_pool_create("event_type_pkt", &pool_param);
+ CU_ASSERT_FATAL(pool2 != ODP_POOL_INVALID);
+
+ for (i = 0; i < NUM_TYPE_TEST; i++) {
+ pkt = odp_packet_alloc(pool2, EVENT_SIZE);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pkt_event[i] = odp_packet_to_event(pkt);
+ }
+
+ /* 1 buf, 1 pkt, 2 buf, 2 pkt, 3 buf, 3 pkt */
+ event[0] = buf_event[0];
+ event[1] = pkt_event[0];
+ event[2] = buf_event[1];
+ event[3] = buf_event[2];
+ event[4] = pkt_event[1];
+ event[5] = pkt_event[2];
+ event[6] = buf_event[3];
+ event[7] = buf_event[4];
+ event[8] = buf_event[5];
+ event[9] = pkt_event[3];
+ event[10] = pkt_event[4];
+ event[11] = pkt_event[5];
+
+ *buf_pool = pool1;
+ *pkt_pool = pool2;
+}
+
+static void event_test_type_multi(void)
+{
+ odp_pool_t buf_pool, pkt_pool;
+ odp_event_type_t type;
+ int num;
+ odp_event_t buf_event[NUM_TYPE_TEST];
+ odp_event_t pkt_event[NUM_TYPE_TEST];
+ odp_event_t event[2 * NUM_TYPE_TEST];
+
+ type_test_init(&buf_pool, &pkt_pool, buf_event, pkt_event, event);
+
+ num = odp_event_type_multi(&event[0], 12, &type);
+ CU_ASSERT(num == 1);
+ CU_ASSERT(type == ODP_EVENT_BUFFER);
+
+ num = odp_event_type_multi(&event[1], 11, &type);
+ CU_ASSERT(num == 1);
+ CU_ASSERT(type == ODP_EVENT_PACKET);
+
+ num = odp_event_type_multi(&event[2], 10, &type);
+ CU_ASSERT(num == 2);
+ CU_ASSERT(type == ODP_EVENT_BUFFER);
+
+ num = odp_event_type_multi(&event[4], 8, &type);
+ CU_ASSERT(num == 2);
+ CU_ASSERT(type == ODP_EVENT_PACKET);
+
+ num = odp_event_type_multi(&event[6], 6, &type);
+ CU_ASSERT(num == 3);
+ CU_ASSERT(type == ODP_EVENT_BUFFER);
+
+ num = odp_event_type_multi(&event[9], 3, &type);
+ CU_ASSERT(num == 3);
+ CU_ASSERT(type == ODP_EVENT_PACKET);
+
+ odp_event_free_multi(buf_event, NUM_TYPE_TEST);
+ odp_event_free_multi(pkt_event, NUM_TYPE_TEST);
+
+ CU_ASSERT(odp_pool_destroy(buf_pool) == 0);
+ CU_ASSERT(odp_pool_destroy(pkt_pool) == 0);
+}
+
+static void event_test_types_multi(void)
+{
+ odp_pool_t buf_pool, pkt_pool;
+ odp_event_t buf_event[NUM_TYPE_TEST];
+ odp_event_t pkt_event[NUM_TYPE_TEST];
+ odp_event_t event[2 * NUM_TYPE_TEST];
+ odp_event_type_t event_types[2 * NUM_TYPE_TEST];
+ odp_event_subtype_t event_subtypes[2 * NUM_TYPE_TEST];
+ int i;
+
+ type_test_init(&buf_pool, &pkt_pool, buf_event, pkt_event, event);
+
+ /* Only buffers */
+ odp_event_types_multi(buf_event, event_types, event_subtypes, NUM_TYPE_TEST);
+ for (i = 0; i < NUM_TYPE_TEST; i++) {
+ CU_ASSERT(event_types[i] == ODP_EVENT_BUFFER);
+ CU_ASSERT(event_subtypes[i] == ODP_EVENT_NO_SUBTYPE);
+ }
+
+ /* Only packets */
+ odp_event_types_multi(pkt_event, event_types, event_subtypes, NUM_TYPE_TEST);
+ for (i = 0; i < NUM_TYPE_TEST; i++) {
+ CU_ASSERT(event_types[i] == ODP_EVENT_PACKET);
+ CU_ASSERT(event_subtypes[i] == ODP_EVENT_PACKET_BASIC);
+ }
+
+ /* Mixed events: B P BB PP BBB PPP */
+ odp_event_types_multi(event, event_types, NULL, 2 * NUM_TYPE_TEST);
+ for (i = 0; i < 2 * NUM_TYPE_TEST; i++) {
+ if (i == 0 || i == 2 || i == 3 || i == 6 || i == 7 || i == 8) {
+ /* CU_ASSERT requires extra brackets */
+ CU_ASSERT(event_types[i] == ODP_EVENT_BUFFER);
+ } else {
+ CU_ASSERT(event_types[i] == ODP_EVENT_PACKET);
+ }
+ }
+
+ odp_event_free_multi(buf_event, NUM_TYPE_TEST);
+ odp_event_free_multi(pkt_event, NUM_TYPE_TEST);
+
+ CU_ASSERT(odp_pool_destroy(buf_pool) == 0);
+ CU_ASSERT(odp_pool_destroy(pkt_pool) == 0);
+}
+
+static void event_test_filter_packet(void)
+{
+ odp_pool_t buf_pool, pkt_pool;
+ int i, num_pkt, num_rem;
+ int num = 2 * NUM_TYPE_TEST;
+ odp_event_t buf_event[NUM_TYPE_TEST];
+ odp_event_t pkt_event[NUM_TYPE_TEST];
+ odp_event_t event[num];
+ odp_packet_t packet[num];
+ odp_event_t remain[num];
+
+ type_test_init(&buf_pool, &pkt_pool, buf_event, pkt_event, event);
+
+ for (i = 0; i < num; i++) {
+ packet[i] = ODP_PACKET_INVALID;
+ remain[i] = ODP_EVENT_INVALID;
+ }
+
+ num_pkt = odp_event_filter_packet(event, packet, remain, num);
+ CU_ASSERT(num_pkt == NUM_TYPE_TEST);
+
+ for (i = 0; i < num_pkt; i++)
+ CU_ASSERT(packet[i] != ODP_PACKET_INVALID);
+
+ num_rem = num - num_pkt;
+ CU_ASSERT(num_rem == NUM_TYPE_TEST);
+
+ for (i = 0; i < num_rem; i++) {
+ CU_ASSERT(remain[i] != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_type(remain[i]) == ODP_EVENT_BUFFER);
+ }
+
+ odp_event_free_multi(event, num);
+
+ CU_ASSERT(odp_pool_destroy(buf_pool) == 0);
+ CU_ASSERT(odp_pool_destroy(pkt_pool) == 0);
+}
+
+static void event_test_is_valid(void)
+{
+ CU_ASSERT(odp_event_is_valid(ODP_EVENT_INVALID) == 0);
+ CU_ASSERT(odp_buffer_is_valid(ODP_BUFFER_INVALID) == 0);
+ CU_ASSERT(odp_packet_is_valid(ODP_PACKET_INVALID) == 0);
+ CU_ASSERT(odp_packet_vector_valid(ODP_PACKET_VECTOR_INVALID) == 0);
+}
+
+odp_testinfo_t event_suite[] = {
+ ODP_TEST_INFO(event_test_free),
+ ODP_TEST_INFO(event_test_free_multi),
+ ODP_TEST_INFO(event_test_free_multi_mixed),
+ ODP_TEST_INFO(event_test_type_multi),
+ ODP_TEST_INFO(event_test_types_multi),
+ ODP_TEST_INFO(event_test_filter_packet),
+ ODP_TEST_INFO(event_test_is_valid),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t event_suites[] = {
+ {"Event", NULL, NULL, event_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(event_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/hash/.gitignore b/test/validation/api/hash/.gitignore
index 6d0bc9314..6d0bc9314 100644
--- a/test/common_plat/validation/api/hash/.gitignore
+++ b/test/validation/api/hash/.gitignore
diff --git a/test/validation/api/hash/Makefile.am b/test/validation/api/hash/Makefile.am
new file mode 100644
index 000000000..0d843ea74
--- /dev/null
+++ b/test/validation/api/hash/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = hash_main
+hash_main_SOURCES = hash.c
diff --git a/test/validation/api/hash/hash.c b/test/validation/api/hash/hash.c
new file mode 100644
index 000000000..60c6755b2
--- /dev/null
+++ b/test/validation/api/hash/hash.c
@@ -0,0 +1,763 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4_with_crc.h>
+
+/* Commonly used CRC check string */
+#define CHECK_STR "123456789"
+#define CHECK_LEN 9
+
+#define CRC32C_INIT 0xffffffff
+#define CRC32C_XOR 0xffffffff
+#define CRC32_INIT 0xffffffff
+#define CRC32_XOR 0xffffffff
+
+/* When Ethernet frame CRC is included into the CRC32 calculation,
+ * the result should match this value. */
+#define ETHCRC_CHECK_VAL 0xdebb20e3
+
+typedef struct hash_test_vector_t {
+ const uint8_t *data;
+ uint32_t len;
+
+ union {
+ uint32_t u32;
+ uint8_t u8[4];
+ } result;
+
+} hash_test_vector_t;
+
+/*
+ * Test vectors 0-4 from RFC 7143.
+ */
+static const uint8_t test_data_0[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t test_data_1[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t test_data_2[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+
+static const uint8_t test_data_3[] = {
+ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+
+static const uint8_t test_data_4[] = {
+ 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Various length strings. Terminating null character is not included into
+ * crc calculation. */
+static const uint8_t test_data_5[] = "abcd";
+
+static const uint8_t test_data_6[] = "abcdefgh";
+
+static const uint8_t test_data_7[] =
+ "The quick brown fox jumps over the lazy dog.";
+
+static const uint8_t test_data_8[] = "a";
+
+static const uint8_t test_data_9[] = "ab";
+
+static const uint8_t test_data_10[] = "abc";
+
+static const uint8_t test_data_11[] = "abcdefg";
+
+static const uint8_t test_data_12[] = "The five boxing wizards jump quickly.";
+
+static const uint8_t test_data_13[] = CHECK_STR;
+
+static const hash_test_vector_t crc32c_test_vector[] = {
+ { .data = test_data_0,
+ .len = sizeof(test_data_0),
+ .result.u32 = 0x8a9136aa
+ },
+ { .data = test_data_1,
+ .len = sizeof(test_data_1),
+ .result.u32 = 0x62a8ab43
+ },
+ { .data = test_data_2,
+ .len = sizeof(test_data_2),
+ .result.u32 = 0x46dd794e
+ },
+ { .data = test_data_3,
+ .len = sizeof(test_data_3),
+ .result.u32 = 0x113fdb5c
+ },
+ { .data = test_data_4,
+ .len = sizeof(test_data_4),
+ .result.u32 = 0xd9963a56
+ },
+ { .data = test_data_5,
+ .len = sizeof(test_data_5) - 1,
+ .result.u32 = 0x92c80a31
+ },
+ { .data = test_data_6,
+ .len = sizeof(test_data_6) - 1,
+ .result.u32 = 0x0a9421b7
+ },
+ { .data = test_data_7,
+ .len = sizeof(test_data_7) - 1,
+ .result.u32 = 0x190097b3
+ },
+ { .data = test_data_8,
+ .len = sizeof(test_data_8) - 1,
+ .result.u32 = 0xc1d04330
+ },
+ { .data = test_data_9,
+ .len = sizeof(test_data_9) - 1,
+ .result.u32 = 0xe2a22936
+ },
+ { .data = test_data_10,
+ .len = sizeof(test_data_10) - 1,
+ .result.u32 = 0x364b3fb7
+ },
+ { .data = test_data_11,
+ .len = sizeof(test_data_11) - 1,
+ .result.u32 = 0xe627f441
+ },
+ { .data = test_data_12,
+ .len = sizeof(test_data_12) - 1,
+ .result.u32 = 0xded3059a
+ },
+ { .data = test_data_13,
+ .len = sizeof(test_data_13) - 1,
+ .result.u32 = 0xe3069283
+ }
+};
+
+static const hash_test_vector_t crc32_test_vector[] = {
+ { .data = test_data_0,
+ .len = sizeof(test_data_0),
+ .result.u32 = 0x190a55ad
+ },
+ { .data = test_data_1,
+ .len = sizeof(test_data_1),
+ .result.u32 = 0xff6cab0b
+ },
+ { .data = test_data_2,
+ .len = sizeof(test_data_2),
+ .result.u32 = 0x91267e8a
+ },
+ { .data = test_data_3,
+ .len = sizeof(test_data_3),
+ .result.u32 = 0x9ab0ef72
+ },
+ { .data = test_data_4,
+ .len = sizeof(test_data_4),
+ .result.u32 = 0x51e17412
+ },
+ { .data = test_data_5,
+ .len = sizeof(test_data_5) - 1,
+ .result.u32 = 0xed82cd11
+ },
+ { .data = test_data_6,
+ .len = sizeof(test_data_6) - 1,
+ .result.u32 = 0xaeef2a50
+ },
+ { .data = test_data_7,
+ .len = sizeof(test_data_7) - 1,
+ .result.u32 = 0x519025e9
+ },
+ { .data = test_data_8,
+ .len = sizeof(test_data_8) - 1,
+ .result.u32 = 0xe8b7be43
+ },
+ { .data = test_data_9,
+ .len = sizeof(test_data_9) - 1,
+ .result.u32 = 0x9e83486d
+ },
+ { .data = test_data_10,
+ .len = sizeof(test_data_10) - 1,
+ .result.u32 = 0x352441c2
+ },
+ { .data = test_data_11,
+ .len = sizeof(test_data_11) - 1,
+ .result.u32 = 0x312a6aa6
+ },
+ { .data = test_data_12,
+ .len = sizeof(test_data_12) - 1,
+ .result.u32 = 0xde912acd
+ },
+ { .data = test_data_13,
+ .len = sizeof(test_data_13) - 1,
+ .result.u32 = 0xcbf43926
+ }
+};
+
+static void hash_test_crc32c(void)
+{
+ uint32_t ret, result;
+ int i;
+ int num = ODPH_ARRAY_SIZE(crc32c_test_vector);
+
+ for (i = 0; i < num; i++) {
+ ret = odp_hash_crc32c(crc32c_test_vector[i].data,
+ crc32c_test_vector[i].len,
+ CRC32C_INIT);
+
+ result = CRC32C_XOR ^ ret;
+ CU_ASSERT(result == crc32c_test_vector[i].result.u32);
+ }
+}
+
+static void hash_test_crc32(void)
+{
+ uint32_t ret, result;
+ int i;
+ int num = ODPH_ARRAY_SIZE(crc32_test_vector);
+
+ for (i = 0; i < num; i++) {
+ ret = odp_hash_crc32(crc32_test_vector[i].data,
+ crc32_test_vector[i].len,
+ CRC32_INIT);
+
+ result = CRC32_XOR ^ ret;
+ CU_ASSERT(result == crc32_test_vector[i].result.u32);
+ }
+}
+
+static void hash_test_ethernet_crc32(void)
+{
+ uint32_t ret;
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_64_crc,
+ sizeof(test_packet_ipv4_udp_64_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_68_crc,
+ sizeof(test_packet_ipv4_udp_68_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_70_crc,
+ sizeof(test_packet_ipv4_udp_70_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_71_crc,
+ sizeof(test_packet_ipv4_udp_71_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_287_crc,
+ sizeof(test_packet_ipv4_udp_287_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_400_crc,
+ sizeof(test_packet_ipv4_udp_400_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ ret = odp_hash_crc32(test_packet_ipv4_udp_503_crc,
+ sizeof(test_packet_ipv4_udp_503_crc), CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+}
+
+static void hash_test_ethernet_crc32_odd_align(void)
+{
+ uint32_t ret, size;
+ const uint32_t max_size = sizeof(test_packet_ipv4_udp_503_crc);
+ uint8_t buf[max_size + 1] ODP_ALIGNED(8);
+
+ memset(buf, 0, sizeof(buf));
+
+ size = sizeof(test_packet_ipv4_udp_64_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_64_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_68_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_68_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_70_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_70_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_71_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_71_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_287_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_287_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_400_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_400_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+
+ size = sizeof(test_packet_ipv4_udp_503_crc);
+ memcpy(&buf[1], test_packet_ipv4_udp_503_crc, size);
+ ret = odp_hash_crc32(&buf[1], size, CRC32_INIT);
+ CU_ASSERT(ret == ETHCRC_CHECK_VAL);
+}
+
+/*
+ * Test various commonly used 32 bit CRCs. Used CRC names, parameters and
+ * check values can be found e.g. here:
+ * http://reveng.sourceforge.net/crc-catalogue
+ */
+static void hash_test_crc32_generic(void)
+{
+ uint64_t result_u64;
+ uint32_t result_u32, result;
+ int i, num;
+ odp_hash_crc_param_t crc_param;
+
+ memset(&crc_param, 0, sizeof(odp_hash_crc_param_t));
+ crc_param.width = 32;
+ crc_param.xor_out = 0;
+
+ /* CRC-32 */
+ crc_param.poly = 0x04c11db7;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ num = ODPH_ARRAY_SIZE(crc32_test_vector);
+
+ for (i = 0; i < num; i++) {
+ if (odp_hash_crc_gen64(crc32_test_vector[i].data,
+ crc32_test_vector[i].len,
+ CRC32_INIT,
+ &crc_param, &result_u64)) {
+ printf("CRC-32 not supported\n.");
+ break;
+ }
+
+ result_u32 = CRC32_XOR ^ result_u64;
+ CU_ASSERT(result_u32 == crc32_test_vector[i].result.u32);
+ }
+
+ /* CRC-32C */
+ crc_param.poly = 0x1edc6f41;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ num = ODPH_ARRAY_SIZE(crc32c_test_vector);
+
+ for (i = 0; i < num; i++) {
+ if (odp_hash_crc_gen64(crc32c_test_vector[i].data,
+ crc32c_test_vector[i].len,
+ CRC32C_INIT,
+ &crc_param, &result_u64)) {
+ printf("CRC-32C not supported\n.");
+ break;
+ }
+
+ result_u32 = CRC32C_XOR ^ result_u64;
+ CU_ASSERT(result_u32 == crc32c_test_vector[i].result.u32);
+ }
+
+ /* CRC-32/AUTOSAR */
+ crc_param.poly = 0xf4acfb13;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0x1697d06a;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0xffffffff ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/AUTOSAR not supported\n.");
+ }
+
+ /* CRC-32/BZIP2 */
+ crc_param.poly = 0x04c11db7;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xfc891918;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0xffffffff ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/BZIP2 not supported\n.");
+ }
+
+ /* CRC-32D */
+ crc_param.poly = 0xa833982b;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0x87315576;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0xffffffff ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32D not supported\n.");
+ }
+
+ /* CRC-32/MPEG-2 */
+ crc_param.poly = 0x04c11db7;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x0376e6e7;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/MPEG-2 not supported\n.");
+ }
+
+ /* CRC-32/POSIX */
+ crc_param.poly = 0x04c11db7;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x765e7680;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0xffffffff ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/POSIX not supported\n.");
+ }
+
+ /* CRC-32/POSIX - with XOR parameter used */
+ crc_param.xor_out = 0xffffffff;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/POSIX (with XOR) not supported\n.");
+ }
+
+ crc_param.xor_out = 0;
+
+ /* CRC-32Q */
+ crc_param.poly = 0x814141ab;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x3010bf7f;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32Q not supported\n.");
+ }
+
+ /* CRC-32/JAMCRC */
+ crc_param.poly = 0x04c11db7;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0x340bc6d9;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/JAMCRC not supported\n.");
+ }
+
+ /* CRC-32/XFER */
+ crc_param.poly = 0x000000af;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xbd0be338;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-32/XFER not supported\n.");
+ }
+}
+
+static void hash_test_crc24_generic(void)
+{
+ uint64_t result_u64;
+ uint32_t result_u32, result;
+ odp_hash_crc_param_t crc_param;
+
+ memset(&crc_param, 0, sizeof(odp_hash_crc_param_t));
+ crc_param.width = 24;
+ crc_param.xor_out = 0;
+
+ /* CRC-24 */
+ crc_param.poly = 0x864cfb;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x21cf02;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xb704ce,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24 not supported\n.");
+ }
+
+ /* CRC-24/FLEXRAY-A */
+ crc_param.poly = 0x5d6dcb;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x7979bd;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xfedcba,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/FLEXRAY-A not supported\n.");
+ }
+
+ /* CRC-24/FLEXRAY-B */
+ result = 0x1f23b8;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xabcdef,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/FLEXRAY-B not supported\n.");
+ }
+
+ /* CRC-24/INTERLAKEN */
+ crc_param.poly = 0x328b63;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xb4f3e6;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffffff,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0xffffff ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/INTERLAKEN not supported\n.");
+ }
+
+ /* CRC-24/LTE-A */
+ crc_param.poly = 0x864cfb;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xcde703;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/LTE-A not supported\n.");
+ }
+
+ /* CRC-24/LTE-B */
+ crc_param.poly = 0x800063;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x23ef52;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/LTE-B not supported\n.");
+ }
+
+ /* CRC-24/BLE */
+ crc_param.poly = 0x00065b;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0xc25a56;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x555555,
+ &crc_param, &result_u64) == 0) {
+ result_u32 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u32 == result);
+ } else {
+ printf("CRC-24/BLE not supported\n.");
+ }
+}
+
+static void hash_test_crc16_generic(void)
+{
+ uint64_t result_u64;
+ uint16_t result_u16, result;
+ odp_hash_crc_param_t crc_param;
+
+ memset(&crc_param, 0, sizeof(odp_hash_crc_param_t));
+ crc_param.width = 16;
+ crc_param.xor_out = 0;
+
+ /* CRC-16/ARC */
+ crc_param.poly = 0x8005;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0xbb3d;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/ARC not supported\n.");
+ }
+
+ /* CRC-16/UMTS */
+ crc_param.poly = 0x8005;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xfee8;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/UMTS not supported\n.");
+ }
+
+ /* CRC-16/CDMA2000 */
+ crc_param.poly = 0xc867;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0x4c06;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffff,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/CDMA2000 not supported\n.");
+ }
+
+ /* CRC-16/GENIBUS */
+ crc_param.poly = 0x1021;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xd64e;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffff,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0xffff ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/GENIBUS not supported\n.");
+ }
+
+ /* CRC-16/T10-DIF */
+ crc_param.poly = 0x8bb7;
+ crc_param.reflect_in = 0;
+ crc_param.reflect_out = 0;
+ result = 0xd0db;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/T10-DIF not supported\n.");
+ }
+
+ /* CRC-16/USB */
+ crc_param.poly = 0x8005;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0xb4c8;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffff,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0xffff ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/USB not supported\n.");
+ }
+
+ /* CRC-16/CCITT */
+ crc_param.poly = 0x1021;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0x2189;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0x0,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0x0 ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/CCITT not supported\n.");
+ }
+
+ /* CRC-16/X-25 */
+ crc_param.poly = 0x1021;
+ crc_param.reflect_in = 1;
+ crc_param.reflect_out = 1;
+ result = 0x906e;
+
+ if (odp_hash_crc_gen64(CHECK_STR, CHECK_LEN, 0xffff,
+ &crc_param, &result_u64) == 0) {
+ result_u16 = 0xffff ^ result_u64;
+ CU_ASSERT(result_u16 == result);
+ } else {
+ printf("CRC-16/X25 not supported\n.");
+ }
+}
+
+odp_testinfo_t hash_suite[] = {
+ ODP_TEST_INFO(hash_test_crc32c),
+ ODP_TEST_INFO(hash_test_crc32),
+ ODP_TEST_INFO(hash_test_ethernet_crc32),
+ ODP_TEST_INFO(hash_test_ethernet_crc32_odd_align),
+ ODP_TEST_INFO(hash_test_crc32_generic),
+ ODP_TEST_INFO(hash_test_crc24_generic),
+ ODP_TEST_INFO(hash_test_crc16_generic),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t hash_suites[] = {
+ {"Hash", NULL, NULL, hash_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(hash_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/hints/.gitignore b/test/validation/api/hints/.gitignore
new file mode 100644
index 000000000..586f429bc
--- /dev/null
+++ b/test/validation/api/hints/.gitignore
@@ -0,0 +1 @@
+hints_main
diff --git a/test/validation/api/hints/Makefile.am b/test/validation/api/hints/Makefile.am
new file mode 100644
index 000000000..bcc77f606
--- /dev/null
+++ b/test/validation/api/hints/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = hints_main
+hints_main_SOURCES = hints.c
diff --git a/test/validation/api/hints/hints.c b/test/validation/api/hints/hints.c
new file mode 100644
index 000000000..4c049f33b
--- /dev/null
+++ b/test/validation/api/hints/hints.c
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+ODP_NORETURN static void test_noreturn(void)
+{
+ abort();
+}
+
+int test_weak(void);
+
+ODP_WEAK_SYMBOL int test_weak(void)
+{
+ return 0;
+}
+
+ODP_COLD_CODE static int test_cold(void)
+{
+ return -1;
+}
+
+ODP_HOT_CODE static int test_hot(void)
+{
+ return 1;
+}
+
+ODP_PRINTF_FORMAT(2, 3)
+static int test_printf_format(int level ODP_UNUSED, const char *fmt ODP_UNUSED, ...)
+{
+ return 0;
+}
+
+static void test_hints(void)
+{
+ volatile int val = 1;
+
+ if (odp_unlikely(!val))
+ test_noreturn();
+
+ test_weak();
+ test_cold();
+
+ if (odp_likely(val))
+ test_hot();
+
+ test_printf_format(0, "test");
+}
+
+static void test_prefetch(void)
+{
+ const int rounds = 10;
+ uint64_t data[rounds];
+
+ for (int i = 0; i < rounds; i++)
+ odp_prefetch(&data[i]);
+
+ for (int i = 0; i < rounds; i++)
+ odp_prefetch_store(&data[i]);
+}
+
+odp_testinfo_t hints_suite[] = {
+ ODP_TEST_INFO(test_hints),
+ ODP_TEST_INFO(test_prefetch),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t align_suites[] = {
+ {"hints", NULL, NULL, hints_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* Parse common options */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(align_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/init/.gitignore b/test/validation/api/init/.gitignore
new file mode 100644
index 000000000..f0ce98bdd
--- /dev/null
+++ b/test/validation/api/init/.gitignore
@@ -0,0 +1 @@
+init_main
diff --git a/test/validation/api/init/Makefile.am b/test/validation/api/init/Makefile.am
new file mode 100644
index 000000000..7465f683f
--- /dev/null
+++ b/test/validation/api/init/Makefile.am
@@ -0,0 +1,15 @@
+include ../Makefile.inc
+
+test_PROGRAMS = init_main
+init_main_SOURCES = init_main.c
+
+EXTRA_DIST = \
+ init_defaults.sh \
+ init_abort.sh \
+ init_log.sh \
+ init_num_thr.sh \
+ init_feature_enabled.sh \
+ init_feature_disabled.sh \
+ init_log_thread.sh \
+ init_test_param_init.sh \
+ init_test_term_abnormal.sh
diff --git a/test/validation/api/init/init_abort.sh b/test/validation/api/init/init_abort.sh
new file mode 100755
index 000000000..27796fcf9
--- /dev/null
+++ b/test/validation/api/init/init_abort.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 1
diff --git a/test/validation/api/init/init_defaults.sh b/test/validation/api/init/init_defaults.sh
new file mode 100755
index 000000000..2215a65a9
--- /dev/null
+++ b/test/validation/api/init/init_defaults.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 0
diff --git a/test/validation/api/init/init_feature_disabled.sh b/test/validation/api/init/init_feature_disabled.sh
new file mode 100755
index 000000000..e538429b6
--- /dev/null
+++ b/test/validation/api/init/init_feature_disabled.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 5
diff --git a/test/validation/api/init/init_feature_enabled.sh b/test/validation/api/init/init_feature_enabled.sh
new file mode 100755
index 000000000..18237cbf9
--- /dev/null
+++ b/test/validation/api/init/init_feature_enabled.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 4
diff --git a/test/validation/api/init/init_log.sh b/test/validation/api/init/init_log.sh
new file mode 100755
index 000000000..cf4177ed5
--- /dev/null
+++ b/test/validation/api/init/init_log.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 2
diff --git a/test/validation/api/init/init_log_thread.sh b/test/validation/api/init/init_log_thread.sh
new file mode 100755
index 000000000..b0bb02220
--- /dev/null
+++ b/test/validation/api/init/init_log_thread.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 6
diff --git a/test/validation/api/init/init_main.c b/test/validation/api/init/init_main.c
new file mode 100644
index 000000000..ab1db421b
--- /dev/null
+++ b/test/validation/api/init/init_main.c
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2024 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Replacement abort function */
+static void ODP_NORETURN my_abort_func(void)
+{
+ abort();
+}
+
+/* Replacement log function */
+ODP_PRINTF_FORMAT(2, 3)
+static int my_log_func(odp_log_level_t level __attribute__((unused)),
+ const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ return r;
+}
+
+static uint32_t my_log_thread_func_count;
+
+/* Thread specific log function */
+ODP_PRINTF_FORMAT(2, 3)
+static int my_log_thread_func(odp_log_level_t level, const char *fmt, ...)
+{
+ (void)level;
+ (void)fmt;
+
+ my_log_thread_func_count++;
+
+ return 0;
+}
+
+static void test_param_init(uint8_t fill)
+{
+ odp_init_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_init_param_init(&param);
+ CU_ASSERT(param.mem_model == ODP_MEM_MODEL_THREAD);
+ CU_ASSERT(param.shm.max_memory == 0);
+}
+
+static void init_test_param_init(void)
+{
+ test_param_init(0);
+ test_param_init(0xff);
+}
+
+static void init_test_defaults(void)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_instance_t current_instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_instance(&current_instance) == 0);
+ CU_ASSERT(memcmp(&current_instance, &instance, sizeof(odp_instance_t)) == 0);
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_abort(void)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+ param.abort_fn = &my_abort_func;
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_log(void)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+ param.log_fn = &my_log_func;
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_log_thread(void)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* Test that our print function is called when set. */
+ odp_log_thread_fn_set(my_log_thread_func);
+ my_log_thread_func_count = 0;
+ odp_sys_info_print();
+ CU_ASSERT(my_log_thread_func_count != 0);
+
+ /* Test that our print function is not called when not set. */
+ odp_log_thread_fn_set(NULL);
+ my_log_thread_func_count = 0;
+ odp_sys_info_print();
+ CU_ASSERT(my_log_thread_func_count == 0);
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_num_thr(void)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+ param.mem_model = ODP_MEM_MODEL_THREAD;
+ param.num_worker = 1;
+ param.num_control = 1;
+ param.worker_cpus = NULL;
+ param.control_cpus = NULL;
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_feature(int disable)
+{
+ int ret;
+ odp_instance_t instance;
+ odp_init_t param;
+
+ odp_init_param_init(&param);
+ param.not_used.all_feat = 0;
+
+ if (disable) {
+ param.not_used.feat.cls = 1;
+ param.not_used.feat.compress = 1;
+ param.not_used.feat.crypto = 1;
+ param.not_used.feat.ipsec = 1;
+ param.not_used.feat.schedule = 1;
+ param.not_used.feat.stash = 1;
+ param.not_used.feat.time = 1;
+ param.not_used.feat.timer = 1;
+ param.not_used.feat.tm = 1;
+ }
+
+ ret = odp_init_global(&instance, &param, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_CONTROL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* Print system and SHM information into test log. It may show
+ * e.g. memory usage difference when features are disabled. */
+ odp_sys_info_print();
+ odp_shm_print_all();
+
+ ret = odp_term_local();
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_term_global(instance);
+ CU_ASSERT(ret == 0);
+}
+
+static void init_test_feature_enabled(void)
+{
+ init_test_feature(0);
+}
+
+static void init_test_feature_disabled(void)
+{
+ init_test_feature(1);
+}
+
+static void init_test_term_abnormal(void)
+{
+ int ret;
+ odp_instance_t instance;
+
+ ret = odp_init_global(&instance, NULL, NULL);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_init_local(instance, ODP_THREAD_WORKER);
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* odp_term_abnormal() is allowed to fail */
+ ret = odp_term_abnormal(instance, 0, NULL);
+
+ if (ret < 0)
+ ODPH_ERR("Failed to perform all abnormal termination actions: %d\n", ret);
+}
+
+odp_testinfo_t testinfo[] = {
+ ODP_TEST_INFO(init_test_defaults),
+ ODP_TEST_INFO(init_test_abort),
+ ODP_TEST_INFO(init_test_log),
+ ODP_TEST_INFO(init_test_num_thr),
+ ODP_TEST_INFO(init_test_feature_enabled),
+ ODP_TEST_INFO(init_test_feature_disabled),
+ ODP_TEST_INFO(init_test_log_thread),
+ ODP_TEST_INFO(init_test_param_init),
+ ODP_TEST_INFO(init_test_term_abnormal)
+};
+
+odp_testinfo_t init_suite[] = {
+ ODP_TEST_INFO_NULL,
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t init_suites[] = {
+ {"Init", NULL, NULL, init_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+static int fill_testinfo(odp_testinfo_t *info, unsigned int test_case)
+{
+ if (test_case >= ODPH_ARRAY_SIZE(testinfo)) {
+ ODPH_ERR("Bad test case number %u\n", test_case);
+ return -1;
+ }
+
+ *info = testinfo[test_case];
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ int test_id;
+
+ /* Parse common options */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ if (argc < 2) {
+ ODPH_ERR("Usage: init_main <test case number>\n");
+ return -1;
+ }
+ test_id = atoi(argv[1]);
+
+ if (fill_testinfo(&init_suite[0], test_id))
+ return -1;
+
+ /* Prevent default ODP init */
+ odp_cunit_register_global_init(NULL);
+ odp_cunit_register_global_term(NULL);
+
+ /* Register the tests */
+ ret = odp_cunit_register(init_suites);
+
+ /* Run the tests */
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/init/init_num_thr.sh b/test/validation/api/init/init_num_thr.sh
new file mode 100755
index 000000000..3889d2a14
--- /dev/null
+++ b/test/validation/api/init/init_num_thr.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 3
diff --git a/test/validation/api/init/init_test_param_init.sh b/test/validation/api/init/init_test_param_init.sh
new file mode 100755
index 000000000..afb4d17a2
--- /dev/null
+++ b/test/validation/api/init/init_test_param_init.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 7
diff --git a/test/validation/api/init/init_test_term_abnormal.sh b/test/validation/api/init/init_test_term_abnormal.sh
new file mode 100755
index 000000000..b3edd8391
--- /dev/null
+++ b/test/validation/api/init/init_test_term_abnormal.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/init"
+$TEST_DIR/init_main$EXEEXT 8
diff --git a/test/validation/api/ipsec/.gitignore b/test/validation/api/ipsec/.gitignore
new file mode 100644
index 000000000..2def047f3
--- /dev/null
+++ b/test/validation/api/ipsec/.gitignore
@@ -0,0 +1 @@
+ipsec_main
diff --git a/test/validation/api/ipsec/Makefile.am b/test/validation/api/ipsec/Makefile.am
new file mode 100644
index 000000000..51b50dd02
--- /dev/null
+++ b/test/validation/api/ipsec/Makefile.am
@@ -0,0 +1,25 @@
+include ../Makefile.inc
+
+noinst_LTLIBRARIES = libtestipsec.la
+libtestipsec_la_SOURCES = \
+ test_vectors.h \
+ reass_test_vectors.h \
+ ipsec_test_in.c \
+ ipsec_test_out.c \
+ ipsec.h \
+ ipsec.c \
+ reass_test_vectors.c
+
+test_PROGRAMS = \
+ ipsec_main
+
+ipsec_main_SOURCES = \
+ ipsec_main.c
+
+PRELDADD += libtestipsec.la
+
+EXTRA_DIST = \
+ ipsec_sync.sh \
+ ipsec_async.sh \
+ ipsec_inline_in.sh \
+ ipsec_inline_out.sh
diff --git a/test/validation/api/ipsec/ipsec.c b/test/validation/api/ipsec/ipsec.c
new file mode 100644
index 000000000..3586671c9
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec.c
@@ -0,0 +1,1551 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2018-2022 Nokia
+ * Copyright (c) 2020-2021 Marvell
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <unistd.h>
+#include <odp/helper/odph_api.h>
+
+#include "ipsec.h"
+
+#include "test_vectors.h"
+#include "reass_test_vectors.h"
+
+#define EVENT_BUFFER_SIZE 3
+
+struct buffered_event_s {
+ odp_queue_t from;
+ odp_event_t event;
+};
+
+static struct buffered_event_s sched_ev_buffer[EVENT_BUFFER_SIZE];
+struct suite_context_s suite_context;
+static odp_ipsec_capability_t capa;
+static int sched_ev_buffer_tail;
+odp_bool_t sa_expiry_notified;
+
+#define PKT_POOL_NUM 64
+#define EVENT_WAIT_TIME ODP_TIME_SEC_IN_NS
+#define STATUS_EVENT_WAIT_TIME ODP_TIME_MSEC_IN_NS
+#define SCHED_EVENT_RETRY_COUNT 2
+
+#define PACKET_USER_PTR ((void *)0x1212fefe)
+#define IPSEC_SA_CTX ((void *)0xfefefafa)
+
+static int ipsec_config(void);
+
+static odp_pktio_t pktio_create(odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_capability_t capa;
+
+ int ret;
+
+ if (pool == ODP_POOL_INVALID)
+ return ODP_PKTIO_INVALID;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+
+ pktio = odp_pktio_open("loop", pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ret = odp_pool_destroy(pool);
+ if (ret)
+ ODPH_ERR("Unable to destroy pool\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktio_capability(pktio, &capa)) {
+ ODPH_ERR("Pktio capabilities failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Pktin queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Pktout queue config failed\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ return pktio;
+}
+
+static int pktio_start(odp_pktio_t pktio, odp_bool_t in, odp_bool_t out)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ if (ODP_PKTIO_INVALID == pktio)
+ return -1;
+
+ if (odp_pktio_capability(pktio, &capa))
+ return -1;
+ /* If inline is not supported, return here. Tests will be marked as
+ * inactive when testing for IPsec capabilities. */
+ if (in && !capa.config.inbound_ipsec)
+ return 0;
+ if (out && !capa.config.outbound_ipsec)
+ return 0;
+
+ odp_pktio_config_init(&config);
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+ config.inbound_ipsec = in;
+ config.outbound_ipsec = out;
+
+ if (odp_pktio_config(pktio, &config))
+ return -1;
+ if (odp_pktio_start(pktio))
+ return -1;
+
+ suite_context.pktio = pktio;
+
+ return 1;
+}
+
+static int sched_event_buffer_add(odp_queue_t from, odp_event_t event)
+{
+ if (sched_ev_buffer_tail + 1 == EVENT_BUFFER_SIZE)
+ return -ENOMEM;
+
+ sched_ev_buffer[sched_ev_buffer_tail].from = from;
+ sched_ev_buffer[sched_ev_buffer_tail].event = event;
+ sched_ev_buffer_tail++;
+
+ return 0;
+}
+
+static odp_event_t sched_event_buffer_get(odp_queue_t from)
+{
+ odp_event_t ev;
+ int i, j;
+
+ if (odp_queue_type(from) == ODP_QUEUE_TYPE_PLAIN)
+ return ODP_EVENT_INVALID;
+
+ /* Look for a matching entry */
+ for (i = 0; i < sched_ev_buffer_tail; i++)
+ if (sched_ev_buffer[i].from == from)
+ break;
+
+ /* Remove entry from buffer */
+ if (i != sched_ev_buffer_tail) {
+ ev = sched_ev_buffer[i].event;
+
+ for (j = 1; i + j < sched_ev_buffer_tail; j++)
+ sched_ev_buffer[i + j - 1] = sched_ev_buffer[i + j];
+
+ sched_ev_buffer_tail--;
+ } else {
+ ev = ODP_EVENT_INVALID;
+ }
+
+ return ev;
+}
+
+static odp_event_t sched_queue_deq(odp_queue_t queue, uint64_t wait_ns)
+{
+ uint64_t wait = odp_schedule_wait_time(wait_ns);
+ odp_event_t ev = ODP_EVENT_INVALID;
+ odp_queue_t from;
+ int retry = 0;
+
+ /* Check if buffered events are available */
+ ev = sched_event_buffer_get(queue);
+ if (ODP_EVENT_INVALID != ev)
+ return ev;
+
+ do {
+ ev = odp_schedule(&from, wait);
+
+ if ((ev != ODP_EVENT_INVALID) && (from != queue)) {
+ CU_ASSERT_FATAL(0 == sched_event_buffer_add(from, ev));
+ ev = ODP_EVENT_INVALID;
+ }
+ } while (ev == ODP_EVENT_INVALID && (++retry < SCHED_EVENT_RETRY_COUNT));
+
+ return ev;
+}
+
+static odp_event_t plain_queue_deq(odp_queue_t queue, uint64_t wait_ns)
+{
+ odp_time_t cur, wait, next;
+ odp_event_t event;
+
+ wait = odp_time_local_from_ns(wait_ns);
+ next = odp_time_sum(odp_time_local(), wait);
+
+ do {
+ event = odp_queue_deq(queue);
+ cur = odp_time_local();
+ } while (event == ODP_EVENT_INVALID && odp_time_cmp(next, cur) >= 0);
+
+ return event;
+}
+
+static odp_event_t recv_event(odp_queue_t queue, uint64_t wait_ns)
+{
+ odp_event_t event;
+
+ if (odp_queue_type(queue) == ODP_QUEUE_TYPE_PLAIN)
+ event = plain_queue_deq(queue, wait_ns);
+ else
+ event = sched_queue_deq(queue, wait_ns);
+
+ return event;
+}
+
+static void pktio_stop(odp_pktio_t pktio)
+{
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+
+ odp_pktin_event_queue(pktio, &queue, 1);
+
+ if (odp_pktio_stop(pktio))
+ ODPH_ERR("IPsec pktio stop failed\n");
+
+ while (1) {
+ odp_event_t ev = recv_event(queue, 0);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ break;
+ }
+}
+
+int ipsec_check(odp_bool_t ah,
+ odp_cipher_alg_t cipher,
+ uint32_t cipher_bits,
+ odp_auth_alg_t auth,
+ uint32_t auth_bits)
+{
+ if ((ODP_IPSEC_OP_MODE_SYNC == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_sync) ||
+ (ODP_IPSEC_OP_MODE_SYNC == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_sync) ||
+ (ODP_IPSEC_OP_MODE_ASYNC == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_async) ||
+ (ODP_IPSEC_OP_MODE_ASYNC == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_async) ||
+ (ODP_IPSEC_OP_MODE_INLINE == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_inline_in) ||
+ (ODP_IPSEC_OP_MODE_INLINE == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_inline_out))
+ return ODP_TEST_INACTIVE;
+
+ if (!(ODP_IPSEC_OP_MODE_SYNC == suite_context.inbound_op_mode &&
+ ODP_IPSEC_OP_MODE_SYNC == suite_context.outbound_op_mode) &&
+ ODP_QUEUE_INVALID != suite_context.queue) {
+ if (suite_context.q_type == ODP_QUEUE_TYPE_PLAIN &&
+ !capa.queue_type_plain)
+ return ODP_TEST_INACTIVE;
+ if (suite_context.q_type == ODP_QUEUE_TYPE_SCHED &&
+ !capa.queue_type_sched)
+ return ODP_TEST_INACTIVE;
+ }
+
+ /* suite_context.pktio is set to ODP_PKTIO_INVALID in ipsec_suite_init()
+ * if the pktio device doesn't support inline IPsec processing. */
+ if (suite_context.pktio == ODP_PKTIO_INVALID &&
+ (ODP_IPSEC_OP_MODE_INLINE == suite_context.inbound_op_mode ||
+ ODP_IPSEC_OP_MODE_INLINE == suite_context.outbound_op_mode))
+ return ODP_TEST_INACTIVE;
+
+ if (ah && (ODP_SUPPORT_NO == capa.proto_ah))
+ return ODP_TEST_INACTIVE;
+
+ if (odph_ipsec_alg_check(&capa, cipher, cipher_bits / 8, auth,
+ auth_bits / 8) < 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+int ipsec_check_ah_sha256(void)
+{
+ return ipsec_check_ah(ODP_AUTH_ALG_SHA256_HMAC, 256);
+}
+
+int ipsec_check_esp_null_sha256(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_NULL, 0,
+ ODP_AUTH_ALG_SHA256_HMAC, 256);
+}
+
+int ipsec_check_esp_aes_cbc_128_null(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128,
+ ODP_AUTH_ALG_NULL, 0);
+}
+
+int ipsec_check_esp_aes_cbc_128_sha1(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128,
+ ODP_AUTH_ALG_SHA1_HMAC, 160);
+}
+
+int ipsec_check_esp_aes_cbc_128_sha256(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128,
+ ODP_AUTH_ALG_SHA256_HMAC, 256);
+}
+
+int ipsec_check_esp_aes_cbc_128_sha384(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128,
+ ODP_AUTH_ALG_SHA384_HMAC, 384);
+}
+
+int ipsec_check_esp_aes_cbc_128_sha512(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128,
+ ODP_AUTH_ALG_SHA512_HMAC, 512);
+}
+
+int ipsec_check_esp_aes_ctr_128_null(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_CTR, 128,
+ ODP_AUTH_ALG_NULL, 0);
+}
+
+int ipsec_check_esp_aes_gcm_128(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 128,
+ ODP_AUTH_ALG_AES_GCM, 0);
+}
+
+int ipsec_check_esp_aes_gcm_256(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 256,
+ ODP_AUTH_ALG_AES_GCM, 0);
+}
+
+int ipsec_check_ah_aes_gmac_128(void)
+{
+ return ipsec_check_ah(ODP_AUTH_ALG_AES_GMAC, 128);
+}
+
+int ipsec_check_esp_null_aes_gmac_128(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_NULL, 0,
+ ODP_AUTH_ALG_AES_GMAC, 128);
+}
+
+int ipsec_check_esp_chacha20_poly1305(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_CHACHA20_POLY1305, 256,
+ ODP_AUTH_ALG_CHACHA20_POLY1305, 0);
+}
+
+int ipsec_check_test_sa_update_seq_num(void)
+{
+ if (!capa.test.sa_operations.seq_num)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+int ipsec_check_esp_aes_gcm_128_reass_ipv4(void)
+{
+ if (suite_context.reass_ipv4)
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 128,
+ ODP_AUTH_ALG_AES_GCM, 0);
+ return ODP_TEST_INACTIVE;
+}
+
+int ipsec_check_esp_aes_gcm_128_reass_ipv6(void)
+{
+ if (suite_context.reass_ipv6)
+ return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 128,
+ ODP_AUTH_ALG_AES_GCM, 0);
+ return ODP_TEST_INACTIVE;
+}
+
+int ipsec_check_esp_null_aes_xcbc(void)
+{
+ return ipsec_check_esp(ODP_CIPHER_ALG_NULL, 0,
+ ODP_AUTH_ALG_AES_XCBC_MAC, 128);
+}
+
+void ipsec_sa_param_fill(odp_ipsec_sa_param_t *param,
+ odp_ipsec_dir_t dir,
+ odp_ipsec_protocol_t proto,
+ uint32_t spi,
+ odp_ipsec_tunnel_param_t *tun,
+ odp_cipher_alg_t cipher_alg,
+ const odp_crypto_key_t *cipher_key,
+ odp_auth_alg_t auth_alg,
+ const odp_crypto_key_t *auth_key,
+ const odp_crypto_key_t *cipher_key_extra,
+ const odp_crypto_key_t *auth_key_extra)
+{
+ odp_ipsec_sa_param_init(param);
+ param->dir = dir;
+ if (dir == ODP_IPSEC_DIR_INBOUND) {
+ param->inbound.lookup_mode = ODP_IPSEC_LOOKUP_SPI;
+ if (auth_alg == ODP_AUTH_ALG_NULL)
+ param->inbound.antireplay_ws = 0;
+ else
+ param->inbound.antireplay_ws = capa.max_antireplay_ws;
+ }
+ param->proto = proto;
+
+ if (tun) {
+ param->mode = ODP_IPSEC_MODE_TUNNEL;
+ if (dir == ODP_IPSEC_DIR_OUTBOUND)
+ param->outbound.tunnel = *tun;
+ } else {
+ param->mode = ODP_IPSEC_MODE_TRANSPORT;
+ }
+
+ param->spi = spi;
+
+ param->dest_queue = suite_context.queue;
+
+ param->context = IPSEC_SA_CTX;
+
+ param->crypto.cipher_alg = cipher_alg;
+ if (cipher_key)
+ param->crypto.cipher_key = *cipher_key;
+
+ param->crypto.auth_alg = auth_alg;
+ if (auth_key)
+ param->crypto.auth_key = *auth_key;
+
+ if (cipher_key_extra)
+ param->crypto.cipher_key_extra = *cipher_key_extra;
+
+ if (auth_key_extra)
+ param->crypto.auth_key_extra = *auth_key_extra;
+
+ /*
+ * Let's use arbitrary non-zero life time values to get life time
+ * checking code paths exercised. Let's not use very small values
+ * to avoid unexpected expiration with implementations that do
+ * not have packet-accurate life time checking but may report
+ * expiration a bit early.
+ */
+ param->lifetime.soft_limit.bytes = 900 * 1000;
+ param->lifetime.hard_limit.bytes = 1000 * 1000;
+ param->lifetime.soft_limit.packets = 9000 * 1000;
+ param->lifetime.hard_limit.packets = 10000 * 1000;
+}
+
+static void ipsec_status_event_handle(odp_event_t ev_status,
+ odp_ipsec_sa_t sa,
+ enum ipsec_test_sa_expiry sa_expiry)
+{
+ int flag = 0;
+ odp_ipsec_status_t status = {
+ .id = 0,
+ .sa = ODP_IPSEC_SA_INVALID,
+ .result = 0,
+ .warn.all = 0,
+ };
+
+ CU_ASSERT_FATAL(ODP_EVENT_INVALID != ev_status);
+ CU_ASSERT(1 == odp_event_is_valid(ev_status));
+ CU_ASSERT_FATAL(ODP_EVENT_IPSEC_STATUS == odp_event_type(ev_status));
+
+ /* No user area or source pool for IPsec status events */
+ CU_ASSERT(odp_event_user_area(ev_status) == NULL);
+ CU_ASSERT(odp_event_user_area_and_flag(ev_status, &flag) == NULL);
+ CU_ASSERT(flag < 0);
+
+ CU_ASSERT(odp_event_pool(ev_status) == ODP_POOL_INVALID);
+
+ CU_ASSERT(0 == odp_ipsec_status(&status, ev_status));
+ CU_ASSERT(ODP_IPSEC_STATUS_WARN == status.id);
+ CU_ASSERT(sa == status.sa);
+ CU_ASSERT(0 == status.result);
+
+ if (IPSEC_TEST_EXPIRY_IGNORED != sa_expiry) {
+ if (IPSEC_TEST_EXPIRY_SOFT_PKT == sa_expiry) {
+ CU_ASSERT(1 == status.warn.soft_exp_packets);
+ sa_expiry_notified = true;
+ } else if (IPSEC_TEST_EXPIRY_SOFT_BYTE == sa_expiry) {
+ CU_ASSERT(1 == status.warn.soft_exp_bytes);
+ sa_expiry_notified = true;
+ }
+ }
+
+ odp_event_free(ev_status);
+}
+
+void ipsec_status_event_get(odp_ipsec_sa_t sa,
+ enum ipsec_test_sa_expiry sa_expiry)
+{
+ uint64_t wait_time = (sa_expiry == IPSEC_TEST_EXPIRY_IGNORED) ? 0 : STATUS_EVENT_WAIT_TIME;
+ odp_event_t ev;
+
+ ev = recv_event(suite_context.queue, wait_time);
+ if (ODP_EVENT_INVALID != ev)
+ ipsec_status_event_handle(ev, sa, sa_expiry);
+}
+
+void ipsec_sa_destroy(odp_ipsec_sa_t sa)
+{
+ odp_event_t event;
+ odp_ipsec_status_t status;
+ int ret;
+
+ CU_ASSERT(IPSEC_SA_CTX == odp_ipsec_sa_context(sa));
+
+ CU_ASSERT(ODP_IPSEC_OK == odp_ipsec_sa_disable(sa));
+
+ if (ODP_QUEUE_INVALID != suite_context.queue) {
+ event = recv_event(suite_context.queue, EVENT_WAIT_TIME);
+
+ CU_ASSERT(odp_event_is_valid(event) == 1);
+ CU_ASSERT(ODP_EVENT_IPSEC_STATUS == odp_event_type(event));
+
+ ret = odp_ipsec_status(&status, event);
+ CU_ASSERT(ret == 0);
+
+ if (ret == 0) {
+ CU_ASSERT(ODP_IPSEC_STATUS_SA_DISABLE == status.id);
+ CU_ASSERT(sa == status.sa);
+ CU_ASSERT(0 == status.result);
+ CU_ASSERT(0 == status.warn.all);
+ }
+
+ odp_event_free(event);
+ }
+
+ CU_ASSERT(ODP_IPSEC_OK == odp_ipsec_sa_destroy(sa));
+}
+
+odp_packet_t ipsec_packet(const ipsec_test_packet *itp)
+{
+ odp_packet_t pkt = odp_packet_alloc(suite_context.pool, itp->len);
+
+ CU_ASSERT_FATAL(ODP_PACKET_INVALID != pkt);
+ if (ODP_PACKET_INVALID == pkt)
+ return pkt;
+
+ CU_ASSERT(0 == odp_packet_copy_from_mem(pkt, 0, itp->len, itp->data));
+ if (itp->l2_offset != ODP_PACKET_OFFSET_INVALID)
+ CU_ASSERT(0 == odp_packet_l2_offset_set(pkt, itp->l2_offset));
+ if (itp->l3_offset != ODP_PACKET_OFFSET_INVALID)
+ CU_ASSERT(0 == odp_packet_l3_offset_set(pkt, itp->l3_offset));
+ if (itp->l4_offset != ODP_PACKET_OFFSET_INVALID)
+ CU_ASSERT(0 == odp_packet_l4_offset_set(pkt, itp->l4_offset));
+
+ odp_packet_user_ptr_set(pkt, PACKET_USER_PTR);
+
+ return pkt;
+}
+
+static void check_l2_header(const ipsec_test_packet *itp, odp_packet_t pkt)
+{
+ uint32_t len = odp_packet_len(pkt);
+ uint8_t data[len];
+ uint32_t l2 = odp_packet_l2_offset(pkt);
+ uint32_t l3 = odp_packet_l3_offset(pkt);
+ uint32_t hdr_len;
+
+ if (!itp)
+ return;
+
+ hdr_len = itp->l3_offset - itp->l2_offset;
+
+ CU_ASSERT_FATAL(l2 != ODP_PACKET_OFFSET_INVALID);
+ CU_ASSERT_FATAL(l3 != ODP_PACKET_OFFSET_INVALID);
+ CU_ASSERT(l3 - l2 == hdr_len);
+ odp_packet_copy_to_mem(pkt, 0, len, data);
+ CU_ASSERT(0 == memcmp(data + l2, itp->data + itp->l2_offset, hdr_len));
+}
+
+/*
+ * Compare packages ignoring everything before L3 header
+ */
+static void ipsec_check_packet(const ipsec_test_packet *itp, odp_packet_t pkt,
+ odp_bool_t is_outbound)
+{
+ uint32_t len = (ODP_PACKET_INVALID == pkt) ? 1 : odp_packet_len(pkt);
+ uint32_t l3, l4;
+ uint8_t data[len];
+ const odph_ipv4hdr_t *itp_ip;
+ odph_ipv4hdr_t *ip;
+
+ if (NULL == itp)
+ return;
+
+ l3 = odp_packet_l3_offset(pkt);
+ l4 = odp_packet_l4_offset(pkt);
+ odp_packet_copy_to_mem(pkt, 0, len, data);
+
+ if (l3 == ODP_PACKET_OFFSET_INVALID) {
+ CU_ASSERT(itp->l3_offset == ODP_PACKET_OFFSET_INVALID);
+ CU_ASSERT(l4 == ODP_PACKET_OFFSET_INVALID);
+
+ return;
+ }
+
+ CU_ASSERT(len - l3 == itp->len - itp->l3_offset);
+ if (len - l3 != itp->len - itp->l3_offset)
+ return;
+
+ CU_ASSERT(l4 - l3 == itp->l4_offset - itp->l3_offset);
+ if (l4 - l3 != itp->l4_offset - itp->l3_offset)
+ return;
+
+ ip = (odph_ipv4hdr_t *) &data[l3];
+ itp_ip = (const odph_ipv4hdr_t *) &itp->data[itp->l3_offset];
+ if (ODPH_IPV4HDR_VER(ip->ver_ihl) == ODPH_IPV4 &&
+ is_outbound &&
+ ip->id != itp_ip->id) {
+ /*
+ * IP ID value chosen by the implementation differs
+ * from the IP value in our test vector. This requires
+ * special handling in outbound checks.
+ */
+ /*
+ * Let's change IP ID and header checksum to same values
+ * as in the test vector to facilitate packet comparison.
+ */
+ CU_ASSERT(odph_ipv4_csum_valid(pkt));
+ ip->id = itp_ip->id;
+ ip->chksum = itp_ip->chksum;
+
+ if (ip->proto == ODPH_IPPROTO_AH) {
+ /*
+ * ID field is included in the authentication so
+ * we cannot check ICV against our test vector.
+ * Check packet data before the first possible
+ * location of the AH ICV field.
+ */
+ CU_ASSERT(0 == memcmp(data + l3, itp->data + itp->l3_offset,
+ ODPH_IPV4HDR_LEN + 12));
+ return;
+ }
+ }
+
+ CU_ASSERT(0 == memcmp(data + l3, itp->data + itp->l3_offset, len - l3));
+}
+
+static int send_pkts(const ipsec_test_part part[], int num_part)
+{
+ odp_packet_t pkt[num_part];
+ odp_pktout_queue_t pktout;
+ int i;
+
+ if (odp_pktout_queue(suite_context.pktio, &pktout, 1) != 1) {
+ CU_FAIL_FATAL("No pktout queue");
+ return 0;
+ }
+
+ for (i = 0; i < num_part; i++)
+ pkt[i] = ipsec_packet(part[i].pkt_in);
+
+ CU_ASSERT(num_part == odp_pktout_send(pktout, pkt, num_part));
+
+ return num_part;
+}
+
+/* Receive async inbound packet */
+static odp_event_t recv_pkt_async_inbound(odp_ipsec_op_status_t status)
+{
+ odp_queue_t queue;
+
+ /*
+ * In case of SA lookup failure, the event is enqueued to the default
+ * queue specified during odp_ipsec_config()
+ */
+ if (status.error.sa_lookup == 0)
+ queue = suite_context.queue;
+ else
+ queue = suite_context.default_queue;
+
+ return recv_event(queue, EVENT_WAIT_TIME);
+}
+
+/* Receive inline processed packets */
+static int recv_pkts_inline(const ipsec_test_part *part,
+ odp_packet_t *pkto)
+{
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ int i;
+
+ CU_ASSERT_FATAL(1 == odp_pktin_event_queue(suite_context.pktio, &queue, 1));
+
+ for (i = 0; i < part->num_pkt;) {
+ odp_event_t ev;
+ odp_event_subtype_t subtype;
+
+ ev = recv_event(queue, 0);
+ if (ODP_EVENT_INVALID != ev) {
+ CU_ASSERT(odp_event_is_valid(ev) == 1);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(ev, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_BASIC == subtype);
+ CU_ASSERT(part->out[i].status.error.sa_lookup);
+
+ pkto[i] = odp_packet_from_event(ev);
+ CU_ASSERT_FATAL(pkto[i] != ODP_PACKET_INVALID);
+ i++;
+ continue;
+ }
+
+ ev = recv_event(suite_context.queue, 0);
+ if (ODP_EVENT_INVALID != ev) {
+ odp_packet_t pkt;
+ int num_pkts = 0;
+ odp_packet_reass_status_t reass_status;
+ odp_packet_reass_info_t reass = {0};
+ odp_packet_reass_partial_state_t reass_state;
+ odp_packet_t frags[MAX_FRAGS];
+ int j;
+
+ CU_ASSERT(odp_event_is_valid(ev) == 1);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_type(ev));
+ pkt = odp_packet_from_event(ev);
+
+ CU_ASSERT(!part->out[i].status.error.sa_lookup);
+
+ reass_status = odp_packet_reass_status(pkt);
+ CU_ASSERT(reass_status == part->out[i].reass_status);
+
+ switch (reass_status) {
+ case ODP_PACKET_REASS_COMPLETE:
+ CU_ASSERT(odp_packet_reass_info(pkt, &reass) == 0);
+ CU_ASSERT(part->out[i].num_frags == reass.num_frags);
+ /* FALLTHROUGH */
+ case ODP_PACKET_REASS_NONE:
+ pkto[i] = pkt;
+ num_pkts = 1;
+ break;
+ case ODP_PACKET_REASS_INCOMPLETE:
+ reass_state.num_frags = 0;
+ CU_ASSERT(0 ==
+ odp_packet_reass_partial_state(pkt, frags, &reass_state));
+ num_pkts = reass_state.num_frags;
+
+ CU_ASSERT_FATAL(i + num_pkts <= part->num_pkt);
+ for (j = 0; j < num_pkts; j++)
+ pkto[i + j] = frags[j];
+ break;
+ default:
+ CU_FAIL("Unknown reassembly status");
+ break;
+ }
+
+ for (; num_pkts > 0; num_pkts--)
+ CU_ASSERT(odp_packet_subtype(pkto[i++]) ==
+ ODP_EVENT_PACKET_IPSEC);
+
+ continue;
+ }
+ }
+
+ return i;
+}
+
+static int ipsec_process_in(const ipsec_test_part *part,
+ odp_ipsec_sa_t sa,
+ odp_packet_t *pkto)
+{
+ odp_ipsec_in_param_t param;
+ int num_out = part->num_pkt;
+ odp_packet_t pkt;
+ int i;
+
+ memset(&param, 0, sizeof(param));
+ if (!part->flags.lookup) {
+ param.num_sa = 1;
+ param.sa = &sa;
+ } else {
+ param.num_sa = 0;
+ param.sa = NULL;
+ }
+
+ if (ODP_IPSEC_OP_MODE_SYNC == suite_context.inbound_op_mode) {
+ pkt = ipsec_packet(part->pkt_in);
+ CU_ASSERT(part->num_pkt == odp_ipsec_in(&pkt, 1, pkto, &num_out, &param));
+ CU_ASSERT(num_out == part->num_pkt);
+ CU_ASSERT_FATAL(*pkto != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_subtype(*pkto) == ODP_EVENT_PACKET_IPSEC);
+ } else if (ODP_IPSEC_OP_MODE_ASYNC == suite_context.inbound_op_mode) {
+ int consumed;
+
+ pkt = ipsec_packet(part->pkt_in);
+ consumed = odp_ipsec_in_enq(&pkt, 1, &param);
+ CU_ASSERT(1 == consumed);
+ if (consumed <= 0)
+ num_out = 0;
+
+ for (i = 0; i < num_out; i++) {
+ odp_event_t event;
+ odp_event_subtype_t subtype;
+
+ event = recv_pkt_async_inbound(part->out[i].status);
+
+ CU_ASSERT(odp_event_is_valid(event) == 1);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_IPSEC == subtype);
+ pkto[i] = odp_ipsec_packet_from_event(event);
+ CU_ASSERT_FATAL(pkto[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_subtype(pkto[i]) ==
+ ODP_EVENT_PACKET_IPSEC);
+ }
+ } else {
+ CU_ASSERT(1 == send_pkts(part, 1));
+ if (part->num_pkt)
+ CU_ASSERT(part->num_pkt == recv_pkts_inline(part, pkto));
+ }
+
+ return num_out;
+}
+
+static int ipsec_check_sa_expiry(enum ipsec_test_sa_expiry sa_expiry,
+ odp_ipsec_packet_result_t *result)
+{
+ if (sa_expiry == IPSEC_TEST_EXPIRY_IGNORED)
+ return 0;
+
+ if (!sa_expiry_notified) {
+ if (sa_expiry == IPSEC_TEST_EXPIRY_SOFT_PKT) {
+ if (result->status.warn.soft_exp_packets)
+ sa_expiry_notified = true;
+ } else if (sa_expiry == IPSEC_TEST_EXPIRY_SOFT_BYTE) {
+ if (result->status.warn.soft_exp_bytes)
+ sa_expiry_notified = true;
+ } else if (sa_expiry == IPSEC_TEST_EXPIRY_HARD_PKT) {
+ if (result->status.error.hard_exp_packets)
+ sa_expiry_notified = true;
+
+ return -1;
+ } else if (sa_expiry == IPSEC_TEST_EXPIRY_HARD_BYTE) {
+ if (result->status.error.hard_exp_bytes)
+ sa_expiry_notified = true;
+
+ return -1;
+ }
+ } else {
+ if (sa_expiry == IPSEC_TEST_EXPIRY_HARD_PKT) {
+ CU_ASSERT(result->status.error.hard_exp_packets);
+
+ return -1;
+ } else if (sa_expiry == IPSEC_TEST_EXPIRY_HARD_BYTE) {
+ CU_ASSERT(result->status.error.hard_exp_bytes);
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int ipsec_send_out_one(const ipsec_test_part *part,
+ odp_ipsec_sa_t sa,
+ odp_packet_t *pkto)
+{
+ odp_ipsec_out_param_t param;
+ int num_out = part->num_pkt;
+ odp_packet_t pkt;
+ int i;
+
+ pkt = ipsec_packet(part->pkt_in);
+
+ memset(&param, 0, sizeof(param));
+ param.num_sa = 1;
+ param.sa = &sa;
+ param.num_opt = part->num_opt;
+ param.opt = &part->opt;
+
+ if (ODP_IPSEC_OP_MODE_SYNC == suite_context.outbound_op_mode) {
+ CU_ASSERT(1 == odp_ipsec_out(&pkt, 1, pkto, &num_out, &param));
+ CU_ASSERT_FATAL(num_out == 1);
+ CU_ASSERT_FATAL(*pkto != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_subtype(*pkto) == ODP_EVENT_PACKET_IPSEC);
+ } else if (ODP_IPSEC_OP_MODE_ASYNC == suite_context.outbound_op_mode) {
+ num_out = odp_ipsec_out_enq(&pkt, 1, &param);
+ CU_ASSERT(1 == num_out);
+
+ num_out = (num_out == 1) ? 1 : 0;
+
+ for (i = 0; i < num_out; i++) {
+ odp_event_t event;
+ odp_event_subtype_t subtype;
+
+ event = recv_event(suite_context.queue, EVENT_WAIT_TIME);
+
+ CU_ASSERT(odp_event_is_valid(event) == 1);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(event, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_IPSEC == subtype);
+ pkto[i] = odp_ipsec_packet_from_event(event);
+ CU_ASSERT_FATAL(pkto[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_subtype(pkto[i]) ==
+ ODP_EVENT_PACKET_IPSEC);
+ }
+ } else {
+ struct odp_ipsec_out_inline_param_t inline_param;
+ uint32_t hdr_len;
+ odph_ethhdr_t hdr;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+
+ if (NULL != part->out[0].pkt_res) {
+ /*
+ * Take L2 header from the expected result.
+ * This way ethertype will be correct for input
+ * processing even with IPv4-in-IPv6-tunnels etc.
+ */
+ hdr_len = part->out[0].pkt_res->l3_offset;
+ CU_ASSERT_FATAL(hdr_len <= sizeof(hdr));
+ memcpy(&hdr, part->out[0].pkt_res->data, hdr_len);
+ } else {
+ hdr_len = 14;
+ memset(&hdr, 0xff, hdr_len);
+
+ if (part->out[0].l3_type == ODP_PROTO_L3_TYPE_IPV6) {
+ hdr.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV6);
+ } else {
+ hdr.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+ }
+ }
+
+ if (part->flags.inline_hdr_in_packet) {
+ /*
+ * Provide the to-be-prepended header to ODP in the
+ * the packet data. Use nonzero L2 offset for better
+ * test coverage.
+ */
+ uint32_t new_l2_offset = 100;
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+ uint32_t new_l3_offset = new_l2_offset + hdr_len;
+ uint32_t l4_offset = odp_packet_l4_offset(pkt);
+ int ret;
+
+ ret = odp_packet_trunc_head(&pkt, l3_offset,
+ NULL, NULL);
+ CU_ASSERT_FATAL(ret >= 0);
+ ret = odp_packet_extend_head(&pkt, new_l3_offset,
+ NULL, NULL);
+ CU_ASSERT_FATAL(ret >= 0);
+ odp_packet_l2_offset_set(pkt, new_l2_offset);
+ odp_packet_l3_offset_set(pkt, new_l3_offset);
+ odp_packet_copy_from_mem(pkt, new_l2_offset, hdr_len, &hdr);
+ if (l4_offset != ODP_PACKET_OFFSET_INVALID)
+ odp_packet_l4_offset_set(pkt, new_l3_offset +
+ l4_offset - l3_offset);
+
+ inline_param.outer_hdr.ptr = NULL;
+ } else {
+ inline_param.outer_hdr.ptr = (void *)&hdr;
+ }
+
+ inline_param.pktio = suite_context.pktio;
+ inline_param.tm_queue = ODP_TM_INVALID;
+ inline_param.outer_hdr.len = hdr_len;
+
+ CU_ASSERT(1 == odp_ipsec_out_inline(&pkt, 1, &param, &inline_param));
+ CU_ASSERT_FATAL(1 == odp_pktin_event_queue(suite_context.pktio, &queue, 1));
+
+ for (i = 0; i < num_out;) {
+ odp_event_t ev;
+ odp_event_subtype_t subtype;
+
+ ev = recv_event(queue, 0);
+ if (ODP_EVENT_INVALID != ev) {
+ CU_ASSERT(odp_event_is_valid(ev) == 1);
+ CU_ASSERT(ODP_EVENT_PACKET == odp_event_types(ev, &subtype));
+ CU_ASSERT(ODP_EVENT_PACKET_BASIC == subtype);
+ CU_ASSERT(!part->out[i].status.error.all);
+
+ pkto[i] = odp_packet_from_event(ev);
+ CU_ASSERT_FATAL(pkto[i] != ODP_PACKET_INVALID);
+
+ if (part->out[i].sa_expiry != IPSEC_TEST_EXPIRY_NONE)
+ ipsec_status_event_get(sa, part->out[i].sa_expiry);
+
+ i++;
+ continue;
+ }
+
+ ev = recv_event(suite_context.queue, 0);
+ if (ODP_EVENT_INVALID != ev) {
+ odp_event_type_t ev_type;
+
+ CU_ASSERT(odp_event_is_valid(ev) == 1);
+ ev_type = odp_event_types(ev, &subtype);
+
+ if ((ODP_EVENT_IPSEC_STATUS == ev_type) &&
+ part->out[i].sa_expiry != IPSEC_TEST_EXPIRY_NONE) {
+ ipsec_status_event_handle(ev, sa, part->out[i].sa_expiry);
+ continue;
+ }
+
+ CU_ASSERT(ODP_EVENT_PACKET == ev_type);
+ CU_ASSERT(ODP_EVENT_PACKET_IPSEC == subtype);
+
+ /* In the case of SA hard expiry tests, hard expiry error bits are
+ * expected to be set. The exact error bits expected to be set based
+ * on sa_expiry is checked eventually in ipsec_check_sa_expiry()
+ * from the caller of this function.
+ */
+ if (part->out[i].sa_expiry == IPSEC_TEST_EXPIRY_NONE)
+ CU_ASSERT(part->out[i].status.error.all);
+
+ pkto[i] = odp_ipsec_packet_from_event(ev);
+ CU_ASSERT_FATAL(pkto[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_subtype(pkto[i]) ==
+ ODP_EVENT_PACKET_IPSEC);
+ i++;
+ continue;
+ }
+ }
+ }
+
+ return num_out;
+}
+
+int ipsec_test_sa_update_seq_num(odp_ipsec_sa_t sa, uint32_t seq_num)
+{
+ odp_ipsec_test_sa_operation_t sa_op;
+ odp_ipsec_test_sa_param_t sa_param;
+
+ sa_op = ODP_IPSEC_TEST_SA_UPDATE_SEQ_NUM;
+ sa_param.seq_num = seq_num;
+
+ return odp_ipsec_test_sa_update(sa, sa_op, &sa_param);
+}
+
+static void ipsec_pkt_seq_num_check(odp_packet_t pkt, uint32_t seq_num)
+{
+ uint32_t l3_off = odp_packet_l3_offset(pkt);
+ uint32_t l4_off;
+ odph_ipv4hdr_t ip;
+
+ CU_ASSERT_FATAL(ODP_PACKET_OFFSET_INVALID != l3_off);
+ CU_ASSERT_FATAL(0 == odp_packet_copy_to_mem(pkt, l3_off, sizeof(ip), &ip));
+
+ if (ODPH_IPV4HDR_VER(ip.ver_ihl) == ODPH_IPV4) {
+ l4_off = l3_off + (ODPH_IPV4HDR_IHL(ip.ver_ihl) * 4);
+
+ if (ip.proto == ODPH_IPPROTO_ESP) {
+ odph_esphdr_t esp;
+
+ odp_packet_copy_to_mem(pkt, l4_off, sizeof(esp), &esp);
+ CU_ASSERT(odp_be_to_cpu_32(esp.seq_no) == seq_num);
+ } else if (ip.proto == ODPH_IPPROTO_AH) {
+ odph_ahhdr_t ah;
+
+ odp_packet_copy_to_mem(pkt, l4_off, sizeof(ah), &ah);
+ CU_ASSERT(odp_be_to_cpu_32(ah.seq_no) == seq_num);
+ } else {
+ CU_FAIL("Unexpected IP Proto");
+ }
+ } else {
+ CU_FAIL("Unexpected IP Version");
+ }
+}
+
+/* Verify inbound processed one part */
+static void verify_in(const ipsec_test_part *part,
+ odp_ipsec_sa_t sa,
+ odp_packet_t *pkto)
+{
+ int i;
+
+ for (i = 0; i < part->num_pkt; i++) {
+ odp_ipsec_packet_result_t result;
+ void *expected_user_ptr = PACKET_USER_PTR;
+
+ if (ODP_EVENT_PACKET_IPSEC !=
+ odp_event_subtype(odp_packet_to_event(pkto[i]))) {
+ /* Inline packet failed SA lookup */
+ CU_ASSERT(1 == part->out[i].status.error.sa_lookup);
+ } else {
+ CU_ASSERT(0 == odp_ipsec_result(&result, pkto[i]));
+ CU_ASSERT(part->out[i].status.error.all == result.status.error.all);
+
+ if (part->out[i].status.error.all != 0) {
+ odp_packet_free(pkto[i]);
+ return;
+ }
+
+ if (0 == result.status.error.all)
+ CU_ASSERT(0 == odp_packet_has_error(pkto[i]));
+ CU_ASSERT((suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE) ==
+ result.flag.inline_mode);
+ CU_ASSERT(sa == result.sa);
+ CU_ASSERT(part->out[i].status.warn.all == result.status.warn.all);
+ if (ODP_IPSEC_SA_INVALID != sa)
+ CU_ASSERT(IPSEC_SA_CTX == odp_ipsec_sa_context(sa));
+ if (suite_context.inbound_op_mode != ODP_IPSEC_OP_MODE_SYNC) {
+ uint32_t len;
+
+ if (part->out[i].orig_ip_len)
+ len = part->out[i].orig_ip_len;
+ else
+ len = part->pkt_in->len - part->pkt_in->l3_offset;
+
+ CU_ASSERT(result.orig_ip_len == 0 ||
+ result.orig_ip_len == len);
+ }
+ }
+ if (part->out[i].l3_type != ODP_PROTO_L3_TYPE_NONE)
+ ipsec_check_packet(part->out[i].pkt_res, pkto[i], false);
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE)
+ expected_user_ptr = NULL;
+ CU_ASSERT(odp_packet_user_ptr(pkto[i]) == expected_user_ptr);
+
+ if (part->out[i].pkt_res != NULL &&
+ part->out[i].l3_type != _ODP_PROTO_L3_TYPE_UNDEF)
+ CU_ASSERT(part->out[i].l3_type == odp_packet_l3_type(pkto[i]));
+ if (part->out[i].pkt_res != NULL &&
+ part->out[i].l4_type != _ODP_PROTO_L4_TYPE_UNDEF)
+ CU_ASSERT(part->out[i].l4_type == odp_packet_l4_type(pkto[i]));
+ odp_packet_free(pkto[i]);
+ }
+}
+
+static void parse_ip(odp_packet_t pkt)
+{
+ uint8_t *ver_ihl;
+ odp_proto_t proto = ODP_PROTO_NONE;
+ uint32_t l3 = odp_packet_l3_offset(pkt);
+
+ ver_ihl = odp_packet_l3_ptr(pkt, NULL);
+ if ((*ver_ihl >> 4) == 4)
+ proto = ODP_PROTO_IPV4;
+ else if ((*ver_ihl >> 4) == 6)
+ proto = ODP_PROTO_IPV6;
+ else
+ CU_FAIL("Invalid IP version");
+
+ odp_packet_parse_param_t param = {
+ .proto = proto,
+ .last_layer = ODP_PROTO_LAYER_L4,
+ };
+ CU_ASSERT(odp_packet_parse(pkt, l3, &param) == 0);
+}
+
+int ipsec_check_out(const ipsec_test_part *part, odp_ipsec_sa_t sa,
+ odp_packet_t *pkto)
+{
+ int i;
+ int num_out;
+
+ num_out = ipsec_send_out_one(part, sa, pkto);
+
+ for (i = 0; i < num_out; i++) {
+ odp_ipsec_packet_result_t result;
+
+ if (ODP_EVENT_PACKET_IPSEC !=
+ odp_event_subtype(odp_packet_to_event(pkto[i]))) {
+ /* Inline packet went through loop */
+ CU_ASSERT(0 == part->out[i].status.error.all);
+ CU_ASSERT(odp_packet_user_ptr(pkto[i]) == NULL);
+ /* L2 header must match the requested one */
+ check_l2_header(part->out[i].pkt_res, pkto[i]);
+ } else {
+ /* IPsec packet */
+ CU_ASSERT(0 == odp_ipsec_result(&result, pkto[i]));
+
+ if (part->out[i].sa_expiry != IPSEC_TEST_EXPIRY_NONE)
+ if (ipsec_check_sa_expiry(part->out[i].sa_expiry, &result) != 0)
+ return num_out;
+
+ CU_ASSERT(part->out[i].status.error.all == result.status.error.all);
+ if (0 == result.status.error.all)
+ CU_ASSERT(0 == odp_packet_has_error(pkto[i]));
+ CU_ASSERT(sa == result.sa);
+ CU_ASSERT(IPSEC_SA_CTX == odp_ipsec_sa_context(sa));
+ CU_ASSERT(odp_packet_user_ptr(pkto[i]) == PACKET_USER_PTR);
+
+ /* Parse the packet to set L4 offset and type */
+ parse_ip(pkto[i]);
+ }
+
+ if (part->flags.test_sa_seq_num)
+ ipsec_pkt_seq_num_check(pkto[i], part->out[i].seq_num);
+
+ ipsec_check_packet(part->out[i].pkt_res,
+ pkto[i],
+ true);
+
+ /*
+ * If we did not have an expected packet to compare the
+ * result packet with, we will check the l3 and l4 types
+ * against the expected ones.
+ */
+ if (part->out[i].pkt_res == NULL) {
+ if (part->out[i].l3_type != _ODP_PROTO_L3_TYPE_UNDEF)
+ CU_ASSERT(part->out[i].l3_type ==
+ odp_packet_l3_type(pkto[i]));
+ if (part->out[i].l4_type != _ODP_PROTO_L4_TYPE_UNDEF)
+ CU_ASSERT(part->out[i].l4_type ==
+ odp_packet_l4_type(pkto[i]));
+ }
+ }
+ return num_out;
+}
+
+void ipsec_check_in_one(const ipsec_test_part *part, odp_ipsec_sa_t sa)
+{
+ odp_packet_t pkto[MAX_FRAGS] = {0};
+ int num_out;
+
+ num_out = ipsec_process_in(part, sa, pkto);
+ CU_ASSERT(num_out == part->num_pkt);
+
+ verify_in(part, sa, pkto);
+}
+
+void ipsec_check_out_one(const ipsec_test_part *part, odp_ipsec_sa_t sa)
+{
+ int num_out = part->num_pkt;
+ odp_packet_t pkto[num_out];
+ int i;
+
+ num_out = ipsec_check_out(part, sa, pkto);
+
+ for (i = 0; i < num_out; i++)
+ odp_packet_free(pkto[i]);
+}
+
+static int ipsec_suite_init(void)
+{
+ int rc = 0;
+
+ if (suite_context.pktio != ODP_PKTIO_INVALID)
+ rc = pktio_start(suite_context.pktio,
+ suite_context.inbound_op_mode ==
+ ODP_IPSEC_OP_MODE_INLINE,
+ suite_context.outbound_op_mode ==
+ ODP_IPSEC_OP_MODE_INLINE);
+ if (rc == 0)
+ suite_context.pktio = ODP_PKTIO_INVALID;
+
+ return rc < 0 ? -1 : 0;
+}
+
+void ipsec_test_packet_from_pkt(ipsec_test_packet *test_pkt, odp_packet_t *pkt)
+{
+ CU_ASSERT_FATAL(odp_packet_len(*pkt) <= sizeof(test_pkt->data));
+
+ test_pkt->len = odp_packet_len(*pkt);
+ test_pkt->l2_offset = odp_packet_l2_offset(*pkt);
+ test_pkt->l3_offset = odp_packet_l3_offset(*pkt);
+ test_pkt->l4_offset = odp_packet_l4_offset(*pkt);
+ odp_packet_copy_to_mem(*pkt, 0, test_pkt->len, test_pkt->data);
+ odp_packet_free(*pkt);
+}
+
+int ipsec_suite_term(void)
+{
+ if (suite_context.pktio != ODP_PKTIO_INVALID)
+ pktio_stop(suite_context.pktio);
+
+ if (ODP_QUEUE_INVALID != suite_context.queue) {
+ if (odp_queue_destroy(suite_context.queue))
+ ODPH_ERR("IPsec destq destroy failed\n");
+ }
+
+ if (odp_cunit_print_inactive())
+ return -1;
+
+ return 0;
+}
+
+static odp_queue_t sched_queue_create(const char *name)
+{
+ odp_queue_param_t qparam;
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ return odp_queue_create(name, &qparam);
+}
+
+static odp_queue_t plain_queue_create(const char *name)
+{
+ return odp_queue_create(name, NULL);
+}
+
+int ipsec_suite_sync_init(void)
+{
+ suite_context.queue = ODP_QUEUE_INVALID;
+
+ /* q_type doesn't matter when queue handle is invalid. */
+ suite_context.q_type = ODP_QUEUE_TYPE_PLAIN;
+
+ return ipsec_suite_init();
+}
+
+int ipsec_suite_plain_init(void)
+{
+ odp_queue_t dest_queue;
+
+ dest_queue = plain_queue_create("ipsec-out");
+ if (ODP_QUEUE_INVALID == dest_queue) {
+ ODPH_ERR("IPsec destq creation failed\n");
+ return -1;
+ }
+
+ suite_context.queue = dest_queue;
+ suite_context.q_type = ODP_QUEUE_TYPE_PLAIN;
+
+ return ipsec_suite_init();
+}
+
+int ipsec_suite_sched_init(void)
+{
+ odp_queue_t dest_queue;
+
+ dest_queue = sched_queue_create("ipsec-out");
+ if (ODP_QUEUE_INVALID == dest_queue) {
+ ODPH_ERR("IPsec destq creation failed\n");
+ return -1;
+ }
+
+ suite_context.queue = dest_queue;
+ suite_context.q_type = ODP_QUEUE_TYPE_SCHED;
+
+ return ipsec_suite_init();
+}
+
+int ipsec_init(odp_instance_t *inst)
+{
+ odp_pool_param_t params;
+ odp_pool_capability_t pool_capa;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ suite_context.reass_ipv4 = false;
+ suite_context.reass_ipv6 = false;
+ suite_context.pool = ODP_POOL_INVALID;
+ suite_context.pktio = ODP_PKTIO_INVALID;
+ suite_context.default_queue = ODP_QUEUE_INVALID;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ if (odp_schedule_config(NULL)) {
+ ODPH_ERR("odp_schedule_config() failed\n");
+ return -1;
+ }
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = MAX_PKT_LEN;
+ params.pkt.len = MAX_PKT_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ if (pool_capa.pkt.max_seg_len &&
+ MAX_PKT_LEN > pool_capa.pkt.max_seg_len) {
+ ODPH_ERR("Warning: small packet segment length\n");
+ params.pkt.seg_len = pool_capa.pkt.max_seg_len;
+ }
+
+ if (pool_capa.pkt.max_len &&
+ MAX_PKT_LEN > pool_capa.pkt.max_len) {
+ ODPH_ERR("Pool max packet length too small\n");
+ return -1;
+ }
+
+ suite_context.pool = odp_pool_create("packet_pool", &params);
+
+ if (suite_context.pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool creation failed\n");
+ return -1;
+ }
+
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE ||
+ suite_context.outbound_op_mode == ODP_IPSEC_OP_MODE_INLINE) {
+ suite_context.pktio = pktio_create(suite_context.pool);
+ if (suite_context.pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("IPsec pktio creation failed\n");
+ return -1;
+ }
+ }
+
+ return ipsec_config();
+}
+
+static int ipsec_config(void)
+{
+ odp_ipsec_config_t ipsec_config;
+
+ if (odp_ipsec_capability(&capa) < 0)
+ return -1;
+
+ /* If we can not setup IPsec due to mode being unsupported, don't
+ * return an error here. It is easier (and more correct) to filter that
+ * in test checking function and just say that the test is inactive. */
+ if ((ODP_IPSEC_OP_MODE_SYNC == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_sync) ||
+ (ODP_IPSEC_OP_MODE_SYNC == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_sync) ||
+ (ODP_IPSEC_OP_MODE_ASYNC == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_async) ||
+ (ODP_IPSEC_OP_MODE_ASYNC == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_async) ||
+ (ODP_IPSEC_OP_MODE_INLINE == suite_context.inbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_inline_in) ||
+ (ODP_IPSEC_OP_MODE_INLINE == suite_context.outbound_op_mode &&
+ ODP_SUPPORT_NO == capa.op_mode_inline_out))
+ return 0;
+
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_ASYNC ||
+ suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE) {
+ if (capa.queue_type_plain)
+ suite_context.default_queue = plain_queue_create("ipsec-default");
+ else if (capa.queue_type_sched)
+ suite_context.default_queue = sched_queue_create("ipsec-default");
+
+ if (ODP_QUEUE_INVALID == suite_context.default_queue) {
+ ODPH_ERR("IPsec defaultq creation failed\n");
+ return -1;
+ }
+ }
+
+ reass_test_vectors_init();
+
+ odp_ipsec_config_init(&ipsec_config);
+ ipsec_config.max_num_sa = capa.max_num_sa;
+ ipsec_config.inbound_mode = suite_context.inbound_op_mode;
+ ipsec_config.outbound_mode = suite_context.outbound_op_mode;
+ ipsec_config.outbound.all_chksum = ~0;
+ ipsec_config.inbound.default_queue = suite_context.default_queue;
+ ipsec_config.inbound.parse_level = ODP_PROTO_LAYER_ALL;
+ ipsec_config.inbound.chksums.all_chksum = ~0;
+ ipsec_config.stats_en = true;
+
+ ipsec_config.inbound.reassembly.max_wait_time = 100 * ODP_TIME_MSEC_IN_NS;
+ if (ipsec_config.inbound.reassembly.max_wait_time > capa.reassembly.max_wait_time)
+ ipsec_config.inbound.reassembly.max_wait_time = capa.reassembly.max_wait_time;
+
+ ipsec_config.inbound.reassembly.max_num_frags = MAX_FRAGS;
+
+ if (capa.reassembly.ip) {
+ ipsec_config.inbound.reassembly.en_ipv4 = true;
+ ipsec_config.inbound.reassembly.en_ipv6 = true;
+ }
+
+ if (capa.reassembly.ipv4)
+ ipsec_config.inbound.reassembly.en_ipv4 = true;
+
+ if (capa.reassembly.ipv6)
+ ipsec_config.inbound.reassembly.en_ipv6 = true;
+
+ if (ODP_IPSEC_OP_MODE_INLINE == suite_context.inbound_op_mode &&
+ !capa.reass_inline) {
+ ipsec_config.inbound.reassembly.en_ipv4 = false;
+ ipsec_config.inbound.reassembly.en_ipv6 = false;
+ }
+
+ if (ODP_IPSEC_OP_MODE_ASYNC == suite_context.inbound_op_mode &&
+ !capa.reass_async) {
+ ipsec_config.inbound.reassembly.en_ipv4 = false;
+ ipsec_config.inbound.reassembly.en_ipv6 = false;
+ }
+
+ if (ODP_IPSEC_OP_MODE_SYNC == suite_context.inbound_op_mode) {
+ ipsec_config.inbound.reassembly.en_ipv4 = false;
+ ipsec_config.inbound.reassembly.en_ipv6 = false;
+ }
+
+ if (capa.reassembly.max_num_frags < MAX_FRAGS) {
+ ipsec_config.inbound.reassembly.en_ipv4 = false;
+ ipsec_config.inbound.reassembly.en_ipv6 = false;
+ }
+
+ if (ipsec_config.inbound.reassembly.en_ipv4)
+ suite_context.reass_ipv4 = true;
+ else
+ suite_context.reass_ipv4 = false;
+
+ if (ipsec_config.inbound.reassembly.en_ipv6)
+ suite_context.reass_ipv6 = true;
+ else
+ suite_context.reass_ipv6 = false;
+
+ if (suite_context.reass_ipv4 || suite_context.reass_ipv6) {
+ if (ODP_IPSEC_OP_MODE_INLINE == suite_context.inbound_op_mode)
+ ipsec_config.inbound.reass_inline = true;
+
+ if (ODP_IPSEC_OP_MODE_ASYNC == suite_context.inbound_op_mode) {
+ ipsec_config.inbound.reass_async = true;
+
+ /* Reassembly with ASYNC not supported */
+ suite_context.reass_ipv4 = false;
+ suite_context.reass_ipv6 = false;
+ }
+ }
+
+ if (ODP_IPSEC_OK != odp_ipsec_config(&ipsec_config))
+ return -1;
+
+ return 0;
+}
+
+int ipsec_term(odp_instance_t inst)
+{
+ odp_pool_t pool = suite_context.pool;
+ odp_queue_t default_queue = suite_context.default_queue;
+ /* suite_context.pktio is set to ODP_PKTIO_INVALID by ipsec_suite_init()
+ if inline processing is not supported. */
+ odp_pktio_t pktio = odp_pktio_lookup("loop");
+
+ if (ODP_PKTIO_INVALID != pktio) {
+ if (odp_pktio_close(pktio))
+ ODPH_ERR("IPsec pktio close failed\n");
+ }
+
+ if (ODP_QUEUE_INVALID != default_queue) {
+ if (odp_queue_destroy(default_queue))
+ ODPH_ERR("IPsec defaultq destroy failed\n");
+ }
+
+ if (ODP_POOL_INVALID != pool) {
+ if (odp_pool_destroy(pool))
+ ODPH_ERR("Packet pool destroy failed\n");
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/test/validation/api/ipsec/ipsec.h b/test/validation/api/ipsec/ipsec.h
new file mode 100644
index 000000000..3daa364cc
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2020 Marvell
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef _ODP_TEST_IPSEC_H_
+#define _ODP_TEST_IPSEC_H_
+
+#include <odp_cunit_common.h>
+
+#define IPV4ADDR(a, b, c, d) odp_cpu_to_be_32(((a) << 24) | \
+ ((b) << 16) | \
+ ((c) << 8) | \
+ ((d) << 0))
+
+/* test arrays: */
+extern odp_testinfo_t ipsec_in_suite[];
+extern odp_testinfo_t ipsec_out_suite[];
+
+int ipsec_init(odp_instance_t *inst);
+int ipsec_term(odp_instance_t inst);
+
+int ipsec_in_inline_init(void);
+int ipsec_out_inline_init(void);
+
+int ipsec_suite_sync_init(void);
+int ipsec_suite_plain_init(void);
+int ipsec_suite_sched_init(void);
+int ipsec_suite_term(void);
+
+struct suite_context_s {
+ odp_bool_t reass_ipv4;
+ odp_bool_t reass_ipv6;
+ odp_ipsec_op_mode_t inbound_op_mode;
+ odp_ipsec_op_mode_t outbound_op_mode;
+ odp_pool_t pool;
+ odp_queue_t default_queue;
+ odp_queue_t queue;
+ odp_pktio_t pktio;
+ odp_queue_type_t q_type;
+};
+
+extern struct suite_context_s suite_context;
+
+#define MAX_FRAG_LEN 1500
+#define MAX_FRAGS 4
+#define MAX_PKT_LEN (MAX_FRAG_LEN * MAX_FRAGS)
+
+typedef struct {
+ uint32_t len;
+ uint32_t l2_offset;
+ uint32_t l3_offset;
+ uint32_t l4_offset;
+ uint8_t data[MAX_PKT_LEN];
+} ipsec_test_packet;
+
+#define _ODP_PROTO_L3_TYPE_UNDEF ((odp_proto_l3_type_t)-1)
+#define _ODP_PROTO_L4_TYPE_UNDEF ((odp_proto_l4_type_t)-1)
+
+enum ipsec_test_stats {
+ IPSEC_TEST_STATS_NONE = 0,
+ IPSEC_TEST_STATS_SUCCESS,
+ IPSEC_TEST_STATS_PROTO_ERR,
+ IPSEC_TEST_STATS_AUTH_ERR,
+};
+
+enum ipsec_test_sa_expiry {
+ IPSEC_TEST_EXPIRY_NONE = 0,
+ IPSEC_TEST_EXPIRY_IGNORED,
+ IPSEC_TEST_EXPIRY_SOFT_BYTE,
+ IPSEC_TEST_EXPIRY_SOFT_PKT,
+ IPSEC_TEST_EXPIRY_HARD_BYTE,
+ IPSEC_TEST_EXPIRY_HARD_PKT,
+};
+
+typedef struct {
+ odp_bool_t lookup;
+ odp_bool_t inline_hdr_in_packet;
+ odp_bool_t test_sa_seq_num;
+} ipsec_test_part_flags_t;
+
+typedef struct {
+ ipsec_test_part_flags_t flags;
+
+ /* Input for the inbound or outbound IPsec operation */
+ const ipsec_test_packet *pkt_in;
+ int num_opt;
+ odp_ipsec_out_opt_t opt;
+
+ /* Expected output */
+ int num_pkt;
+ struct {
+ odp_ipsec_op_status_t status;
+ odp_packet_reass_status_t reass_status;
+ uint16_t num_frags;
+ const ipsec_test_packet *pkt_res;
+ odp_proto_l3_type_t l3_type;
+ odp_proto_l4_type_t l4_type;
+ uint32_t seq_num;
+ /*
+ * Expected original IP length. Non zero only when expected len
+ * differs from that of input test packet (pkt_in).
+ */
+ uint32_t orig_ip_len;
+ enum ipsec_test_sa_expiry sa_expiry;
+ } out[MAX_FRAGS];
+} ipsec_test_part;
+
+extern odp_bool_t sa_expiry_notified;
+
+void ipsec_sa_param_fill(odp_ipsec_sa_param_t *param,
+ odp_ipsec_dir_t dir,
+ odp_ipsec_protocol_t proto,
+ uint32_t spi,
+ odp_ipsec_tunnel_param_t *tun,
+ odp_cipher_alg_t cipher_alg,
+ const odp_crypto_key_t *cipher_key,
+ odp_auth_alg_t auth_alg,
+ const odp_crypto_key_t *auth_key,
+ const odp_crypto_key_t *cipher_key_extra,
+ const odp_crypto_key_t *auth_key_extra);
+
+void ipsec_sa_destroy(odp_ipsec_sa_t sa);
+odp_packet_t ipsec_packet(const ipsec_test_packet *itp);
+void ipsec_check_in_one(const ipsec_test_part *part, odp_ipsec_sa_t sa);
+int ipsec_check_out(const ipsec_test_part *part,
+ odp_ipsec_sa_t sa,
+ odp_packet_t *pkto);
+void ipsec_check_out_one(const ipsec_test_part *part, odp_ipsec_sa_t sa);
+int ipsec_test_sa_update_seq_num(odp_ipsec_sa_t sa, uint32_t seq_num);
+void ipsec_test_packet_from_pkt(ipsec_test_packet *test_pkt, odp_packet_t *pkt);
+int ipsec_check(odp_bool_t ah,
+ odp_cipher_alg_t cipher,
+ uint32_t cipher_bits,
+ odp_auth_alg_t auth,
+ uint32_t auth_bits);
+#define ipsec_check_ah(auth, auth_bits) \
+ ipsec_check(true, ODP_CIPHER_ALG_NULL, 0, auth, auth_bits)
+#define ipsec_check_esp(cipher, cipher_bits, auth, auth_bits) \
+ ipsec_check(false, cipher, cipher_bits, auth, auth_bits)
+int ipsec_check_ah_sha256(void);
+int ipsec_check_esp_null_sha256(void);
+int ipsec_check_esp_aes_cbc_128_null(void);
+int ipsec_check_esp_aes_cbc_128_sha1(void);
+int ipsec_check_esp_aes_cbc_128_sha256(void);
+int ipsec_check_esp_aes_cbc_128_sha384(void);
+int ipsec_check_esp_aes_cbc_128_sha512(void);
+int ipsec_check_esp_aes_ctr_128_null(void);
+int ipsec_check_esp_aes_gcm_128(void);
+int ipsec_check_esp_aes_gcm_256(void);
+int ipsec_check_ah_aes_gmac_128(void);
+int ipsec_check_esp_null_aes_gmac_128(void);
+int ipsec_check_esp_chacha20_poly1305(void);
+int ipsec_check_test_sa_update_seq_num(void);
+int ipsec_check_esp_aes_gcm_128_reass_ipv4(void);
+int ipsec_check_esp_aes_gcm_128_reass_ipv6(void);
+int ipsec_check_esp_null_aes_xcbc(void);
+void ipsec_status_event_get(odp_ipsec_sa_t sa,
+ enum ipsec_test_sa_expiry sa_expiry);
+
+#endif
diff --git a/test/validation/api/ipsec/ipsec_async.sh b/test/validation/api/ipsec/ipsec_async.sh
new file mode 100755
index 000000000..c12f3e70a
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_async.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/ipsec"
+$TEST_DIR/ipsec_main$EXEEXT async
diff --git a/test/validation/api/ipsec/ipsec_inline_in.sh b/test/validation/api/ipsec/ipsec_inline_in.sh
new file mode 100755
index 000000000..c1d1fed36
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_inline_in.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/ipsec"
+$TEST_DIR/ipsec_main$EXEEXT inline-in
diff --git a/test/validation/api/ipsec/ipsec_inline_out.sh b/test/validation/api/ipsec/ipsec_inline_out.sh
new file mode 100755
index 000000000..91ebccac7
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_inline_out.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/ipsec"
+$TEST_DIR/ipsec_main$EXEEXT inline-out
diff --git a/test/validation/api/ipsec/ipsec_main.c b/test/validation/api/ipsec/ipsec_main.c
new file mode 100644
index 000000000..2c343f063
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_main.c
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2024 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include "ipsec.h"
+
+odp_suiteinfo_t ipsec_sync_suites[] = {
+ {"IPsec-in", ipsec_suite_sync_init, ipsec_suite_term, ipsec_in_suite},
+ {"IPsec-out", ipsec_suite_sync_init, ipsec_suite_term, ipsec_out_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+odp_suiteinfo_t ipsec_async_suites[] = {
+ {"IPsec-plain-in", ipsec_suite_plain_init, ipsec_suite_term, ipsec_in_suite},
+ {"IPsec-sched-in", ipsec_suite_sched_init, ipsec_suite_term, ipsec_in_suite},
+ {"IPsec-plain-out", ipsec_suite_plain_init, ipsec_suite_term, ipsec_out_suite},
+ {"IPsec-sched-out", ipsec_suite_sched_init, ipsec_suite_term, ipsec_out_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+odp_suiteinfo_t ipsec_inline_in_suites[] = {
+ {"IPsec-plain-in", ipsec_suite_plain_init, ipsec_suite_term, ipsec_in_suite},
+ {"IPsec-sched-in", ipsec_suite_sched_init, ipsec_suite_term, ipsec_in_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+odp_suiteinfo_t ipsec_inline_out_suites[] = {
+ {"IPsec-plain-out", ipsec_suite_plain_init, ipsec_suite_term, ipsec_out_suite},
+ {"IPsec-sched-out", ipsec_suite_sched_init, ipsec_suite_term, ipsec_out_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+static void usage_exit(void)
+{
+ fprintf(stderr, "Usage: ipsec_main {sync|async|inline-in|inline-out}\n");
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ char *test_mode;
+ odp_suiteinfo_t *suites = NULL;
+ int ret;
+
+ if (odp_cunit_parse_options(&argc, argv))
+ return EXIT_FAILURE;
+
+ if (argc < 2)
+ usage_exit();
+ test_mode = argv[1];
+
+ if ((!strcmp(test_mode, "sync"))) {
+ suite_context.inbound_op_mode = ODP_IPSEC_OP_MODE_SYNC;
+ suite_context.outbound_op_mode = ODP_IPSEC_OP_MODE_SYNC;
+ suites = ipsec_sync_suites;
+ } else if ((!strcmp(test_mode, "async"))) {
+ suite_context.inbound_op_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ suite_context.outbound_op_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ suites = ipsec_async_suites;
+ } else if ((!strcmp(test_mode, "inline-in"))) {
+ suite_context.inbound_op_mode = ODP_IPSEC_OP_MODE_INLINE;
+ suite_context.outbound_op_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ suites = ipsec_inline_in_suites;
+ } else if ((!strcmp(test_mode, "inline-out"))) {
+ suite_context.inbound_op_mode = ODP_IPSEC_OP_MODE_ASYNC;
+ suite_context.outbound_op_mode = ODP_IPSEC_OP_MODE_INLINE;
+ suites = ipsec_inline_out_suites;
+ } else {
+ usage_exit();
+ }
+
+ odp_cunit_register_global_init(ipsec_init);
+ odp_cunit_register_global_term(ipsec_term);
+ ret = odp_cunit_register(suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/ipsec/ipsec_sync.sh b/test/validation/api/ipsec/ipsec_sync.sh
new file mode 100755
index 000000000..2653ddc78
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_sync.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+TEST_DIR="${TEST_DIR:-$(dirname $0)/..}/ipsec"
+$TEST_DIR/ipsec_main$EXEEXT sync
diff --git a/test/validation/api/ipsec/ipsec_test_in.c b/test/validation/api/ipsec/ipsec_test_in.c
new file mode 100644
index 000000000..a93bf9f2a
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_test_in.c
@@ -0,0 +1,2367 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2020-2021 Marvell
+ * Copyright (c) 2021 Nokia
+ */
+
+#include <odp/helper/odph_api.h>
+
+#include "ipsec.h"
+
+#include "reass_test_vectors.h"
+#include "test_vectors.h"
+
+static void part_prep_esp(ipsec_test_part part[], int num_part, bool v6_tunnel)
+{
+ int i;
+
+ memset(part, 0, sizeof(ipsec_test_part) * num_part);
+
+ for (i = 0; i < num_part; i++) {
+ part[i].num_pkt = 1;
+
+ if (v6_tunnel)
+ part[i].out[0].l3_type = ODP_PROTO_L3_TYPE_IPV6;
+ else
+ part[i].out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+
+ part[i].out[0].l4_type = ODP_PROTO_L4_TYPE_ESP;
+ }
+}
+
+static void part_prep_plain(ipsec_test_part *part, int num_pkt, bool v6, bool udp)
+{
+ int i;
+
+ part->num_pkt = num_pkt;
+ for (i = 0; i < num_pkt; i++) {
+ part->out[i].l4_type = _ODP_PROTO_L4_TYPE_UNDEF;
+
+ if (v6)
+ part->out[i].l3_type = ODP_PROTO_L3_TYPE_IPV6;
+ else
+ part->out[i].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+
+ if (udp)
+ part->out[i].l4_type = ODP_PROTO_L4_TYPE_UDP;
+ }
+}
+
+static void test_in_ipv4_ah_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_tun_ipv4_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_tun_ipv6_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_tun_ipv4_notun(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_tun_ipv4_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ /* It is L4_TYPE_IPV4 */
+ .l4_type = _ODP_PROTO_L4_TYPE_UNDEF,
+ .pkt_res = &pkt_ipv4_icmp_0_ipip },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_cbc_null(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_NULL, NULL,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_cbc_null_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_cbc_sha1(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_cbc_sha1_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_cbc_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_cbc_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_cbc_sha384(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA384_HMAC, &key_5a_384,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_cbc_sha384_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_cbc_sha512(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA512_HMAC, &key_5a_512,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_cbc_sha512_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_aes_ctr_null(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CTR, &key_a5_128,
+ ODP_AUTH_ALG_NULL, NULL,
+ &key_mcgrew_gcm_salt_3, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_aes_ctr_null_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1,
+ .flags = {
+ .lookup = 1,
+ },
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1,
+ .flags = {
+ .lookup = 1,
+ },
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_tun_ipv4_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_tun_ipv6_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_udp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_udp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_udp_null_sha256_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_udp_null_sha256_1,
+ .flags = {
+ .lookup = 1,
+ },
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_noreplay(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.inbound.antireplay_ws = 0;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_test_part test_1235 = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1235,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test_1235, sa);
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_replay(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test_repl;
+
+ memset(&test_repl, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.inbound.antireplay_ws = 32;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ test_repl.pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1;
+ test_repl.num_pkt = 1;
+ test_repl.out[0].status.error.antireplay = 1;
+
+ ipsec_test_part test_1235 = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1235,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test_repl, sa);
+ ipsec_check_in_one(&test_1235, sa);
+ ipsec_check_in_one(&test_repl, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_noreplay(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.inbound.antireplay_ws = 0;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_test_part test_1235 = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1235,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test_1235, sa);
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_replay(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test_repl;
+
+ memset(&test_repl, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.inbound.antireplay_ws = 32;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ test_repl.pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1;
+ test_repl.num_pkt = 1;
+ test_repl.out[0].status.error.antireplay = 1;
+
+ ipsec_test_part test_1235 = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1235,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+ ipsec_check_in_one(&test_repl, sa);
+ ipsec_check_in_one(&test_1235, sa);
+ ipsec_check_in_one(&test_repl, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_esp_pkt(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ /* This test will not work properly inbound inline mode.
+ * test_in_ipv4_ah_esp_pkt_lookup will be used instead. */
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE)
+ return;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1;
+ test.num_pkt = 1;
+ test.out[0].status.error.proto = 1;
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_ah_pkt(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ /* This test will not work properly inbound inline mode.
+ * test_in_ipv4_esp_ah_pkt_lookup will be used instead. */
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE)
+ return;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1;
+ test.num_pkt = 1;
+ test.out[0].status.error.proto = 1;
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_esp_pkt_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1;
+ test.flags.lookup = 1;
+ test.num_pkt = 1;
+ test.out[0].status.error.sa_lookup = 1;
+
+ ipsec_check_in_one(&test, ODP_IPSEC_SA_INVALID);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_ah_pkt_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1;
+ test.flags.lookup = 1;
+ test.num_pkt = 1;
+ test.out[0].status.error.sa_lookup = 1;
+
+ ipsec_check_in_one(&test, ODP_IPSEC_SA_INVALID);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_bad1(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1_bad1;
+ test.num_pkt = 1;
+ test.out[0].status.error.auth = 1;
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_sha256_bad2(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_ah_sha256_1_bad2;
+ test.num_pkt = 1;
+ test.out[0].status.error.auth = 1;
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_sha256_bad1(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0_esp_null_sha256_1_bad1;
+ test.num_pkt = 1;
+ test.out[0].status.error.auth = 1;
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_rfc3602_5_esp(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 0x4321, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_rfc3602,
+ ODP_AUTH_ALG_NULL, NULL,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_rfc3602_5_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_rfc3602_5 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_rfc3602_6_esp(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 0x4321, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_rfc3602,
+ ODP_AUTH_ALG_NULL, NULL,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_rfc3602_6_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_rfc3602_6 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_rfc3602_7_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x8765, &tunnel,
+ ODP_CIPHER_ALG_AES_CBC, &key_rfc3602_2,
+ ODP_AUTH_ALG_NULL, NULL,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_rfc3602_7_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_rfc3602_7 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_rfc3602_8_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x8765, &tunnel,
+ ODP_CIPHER_ALG_AES_CBC, &key_rfc3602_2,
+ ODP_AUTH_ALG_NULL, NULL,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_rfc3602_8_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_rfc3602_8 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_2_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0xa5f8, &tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_2,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_2, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_2_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_UDP,
+ .pkt_res = &pkt_mcgrew_gcm_test_2},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_3_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe3, &tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_3,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_3, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_3_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = _ODP_PROTO_L4_TYPE_UNDEF,
+ .pkt_res = &pkt_mcgrew_gcm_test_3},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_4_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x00000000, &tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_4_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_mcgrew_gcm_test_4},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_12_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ /* This test will not work properly inbound inline mode.
+ * Packet might be dropped and we will not check for that. */
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE)
+ return;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x335467ae, &tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_12,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_12, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_12_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_NONE,
+ .l4_type = _ODP_PROTO_L4_TYPE_UNDEF,
+ .pkt_res = &pkt_mcgrew_gcm_test_12},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_12_esp_notun(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x335467ae, NULL,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_12,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_12, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_12_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_NO_NEXT,
+ .pkt_res = &pkt_mcgrew_gcm_test_12_notun },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_mcgrew_gcm_15_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x00004321, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_AES_GMAC, &key_mcgrew_gcm_15,
+ NULL, &key_mcgrew_gcm_salt_15);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_mcgrew_gcm_test_15_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_mcgrew_gcm_test_15},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_rfc7634_chacha(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x01020304, &tunnel,
+ ODP_CIPHER_ALG_CHACHA20_POLY1305, &key_rfc7634,
+ ODP_AUTH_ALG_CHACHA20_POLY1305, NULL,
+ &key_rfc7634_salt, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_rfc7634_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_rfc7634},
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_ah_aes_gmac_128(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_AES_GMAC, &key_a5_128,
+ NULL, &key_mcgrew_gcm_salt_2);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_ah_aes_gmac_128_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv4_esp_null_aes_gmac_128(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_AES_GMAC, &key_a5_128,
+ NULL, &key_mcgrew_gcm_salt_2);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0_esp_null_aes_gmac_128_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_ah_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_ah_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_ah_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_ah_tun_ipv4_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_ah_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_ah_tun_ipv6_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_esp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_esp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_esp_null_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_esp_tun_ipv4_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_esp_null_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_esp_tun_ipv6_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_esp_udp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_esp_udp_null_sha256_1,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_in_ipv6_esp_udp_null_sha256_lookup(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0_esp_udp_null_sha256_1,
+ .flags = {
+ .lookup = 1,
+ },
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV6,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV6,
+ .pkt_res = &pkt_ipv6_icmp_0 },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_ipsec_print(void)
+{
+ odp_ipsec_print();
+}
+
+static void test_ipsec_sa_print(void)
+{
+ odp_ipsec_sa_param_t param_in;
+ odp_ipsec_sa_t in_sa;
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+
+ in_sa = odp_ipsec_sa_create(&param_in);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != in_sa);
+
+ odp_ipsec_sa_print(in_sa);
+
+ ipsec_sa_destroy(in_sa);
+}
+
+static void test_multi_out_in(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa,
+ uint8_t tunnel_ip_ver,
+ int num_input_packets,
+ ipsec_test_packet *input_packets[],
+ ipsec_test_packet *result_packet,
+ odp_packet_reass_status_t reass_status)
+{
+ uint8_t ver_ihl = result_packet->data[result_packet->l3_offset];
+ odp_bool_t is_result_ipv6 = (ODPH_IPV4HDR_VER(ver_ihl) == ODPH_IPV6);
+ uint32_t orig_ip_len = 0;
+ int i;
+
+ for (i = 0; i < num_input_packets; i++) {
+ ipsec_test_part test_out;
+ ipsec_test_part test_in;
+ ipsec_test_packet test_pkt;
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+ uint32_t l3_off, pkt_len;
+
+ /*
+ * Convert plain text packet to IPsec packet through
+ * outbound IPsec processing.
+ */
+ part_prep_esp(&test_out, 1, tunnel_ip_ver == ODPH_IPV6);
+ test_out.pkt_in = input_packets[i];
+ CU_ASSERT(ipsec_check_out(&test_out, out_sa, &pkt) == 1);
+
+ /*
+ * Perform inbound IPsec processing for the IPsec packet.
+ * Expect result packet only for the last packet.
+ */
+ memset(&test_in, 0, sizeof(test_in));
+
+ /*
+ * In case of complete reassembly, the original IP length is the
+ * sum of IP lengths of the ESP packets that contained the
+ * individual fragments.
+ */
+ if (reass_status == ODP_PACKET_REASS_COMPLETE) {
+ pkt_len = odp_packet_len(pkt);
+ l3_off = odp_packet_l3_offset(pkt);
+ CU_ASSERT(ODP_PACKET_OFFSET_INVALID != l3_off)
+
+ orig_ip_len += pkt_len - l3_off;
+ }
+
+ if (i == num_input_packets - 1) {
+ part_prep_plain(&test_in, 1, is_result_ipv6, true);
+ test_in.out[0].pkt_res = result_packet;
+ test_in.out[0].reass_status = reass_status;
+ test_in.out[0].num_frags = num_input_packets;
+ test_in.out[0].orig_ip_len = orig_ip_len;
+ }
+ ipsec_test_packet_from_pkt(&test_pkt, &pkt);
+ test_in.pkt_in = &test_pkt;
+
+ ipsec_check_in_one(&test_in, in_sa);
+ }
+}
+
+static void test_in_ipv4_esp_reass_success_two_frags(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv4_udp_p1_f1,
+ &pkt_ipv4_udp_p1_f2,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv4_udp_p1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV4,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv4_esp_reass_success_four_frags(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv4_udp_p2_f1,
+ &pkt_ipv4_udp_p2_f2,
+ &pkt_ipv4_udp_p2_f3,
+ &pkt_ipv4_udp_p2_f4,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv4_udp_p2;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV4,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv4_esp_reass_success_two_frags_ooo(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv4_udp_p1_f2,
+ &pkt_ipv4_udp_p1_f1,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv4_udp_p1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV4,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv4_esp_reass_success_four_frags_ooo(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv4_udp_p2_f4,
+ &pkt_ipv4_udp_p2_f1,
+ &pkt_ipv4_udp_p2_f2,
+ &pkt_ipv4_udp_p2_f3,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv4_udp_p2;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV4,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv4_esp_reass_incomp_missing(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv4_udp_p1_f1,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv4_udp_p1_f1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV4,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_INCOMPLETE);
+}
+
+static void test_in_ipv4_esp_reass_success(void)
+{
+ odp_ipsec_tunnel_param_t in_tunnel, out_tunnel;
+ odp_ipsec_sa_param_t param_in, param_out;
+ uint32_t src = IPV4ADDR(10, 0, 11, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 22, 2);
+ odp_ipsec_sa_t out_sa, in_sa;
+
+ memset(&in_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ memset(&out_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ out_tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ out_tunnel.ipv4.src_addr = &src;
+ out_tunnel.ipv4.dst_addr = &dst;
+
+ ipsec_sa_param_fill(&param_out,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &out_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &in_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ param_in.inbound.reassembly_en = 1;
+
+ out_sa = odp_ipsec_sa_create(&param_out);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != out_sa);
+
+ in_sa = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != in_sa);
+
+ printf("\n IPv4 two frags");
+ test_in_ipv4_esp_reass_success_two_frags(out_sa, in_sa);
+
+ printf("\n IPv4 four frags");
+ test_in_ipv4_esp_reass_success_four_frags(out_sa, in_sa);
+
+ printf("\n IPv4 two frags out of order");
+ test_in_ipv4_esp_reass_success_two_frags_ooo(out_sa, in_sa);
+
+ printf("\n IPv4 four frags out of order");
+ test_in_ipv4_esp_reass_success_four_frags_ooo(out_sa, in_sa);
+
+ printf("\n");
+
+ ipsec_sa_destroy(in_sa);
+ ipsec_sa_destroy(out_sa);
+}
+
+static void test_in_ipv4_esp_reass_incomp(void)
+{
+ odp_ipsec_tunnel_param_t in_tunnel, out_tunnel;
+ odp_ipsec_sa_param_t param_in, param_out;
+ uint32_t src = IPV4ADDR(10, 0, 11, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 22, 2);
+ odp_ipsec_sa_t out_sa, in_sa;
+
+ memset(&in_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ memset(&out_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ out_tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ out_tunnel.ipv4.src_addr = &src;
+ out_tunnel.ipv4.dst_addr = &dst;
+
+ ipsec_sa_param_fill(&param_out,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &out_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &in_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ param_in.inbound.reassembly_en = 1;
+
+ out_sa = odp_ipsec_sa_create(&param_out);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != out_sa);
+
+ in_sa = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != in_sa);
+
+ printf("\n IPv4 missing frag");
+ test_in_ipv4_esp_reass_incomp_missing(out_sa, in_sa);
+
+ printf("\n");
+
+ ipsec_sa_destroy(in_sa);
+ ipsec_sa_destroy(out_sa);
+}
+
+static void test_in_ipv6_esp_reass_success_two_frags(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv6_udp_p1_f1,
+ &pkt_ipv6_udp_p1_f2,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv6_udp_p1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV6,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv6_esp_reass_success_four_frags(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv6_udp_p2_f1,
+ &pkt_ipv6_udp_p2_f2,
+ &pkt_ipv6_udp_p2_f3,
+ &pkt_ipv6_udp_p2_f4,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv6_udp_p2;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV6,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv6_esp_reass_success_two_frags_ooo(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv6_udp_p1_f2,
+ &pkt_ipv6_udp_p1_f1,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv6_udp_p1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV6,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv6_esp_reass_success_four_frags_ooo(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv6_udp_p2_f2,
+ &pkt_ipv6_udp_p2_f3,
+ &pkt_ipv6_udp_p2_f4,
+ &pkt_ipv6_udp_p2_f1,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv6_udp_p2;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV6,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_COMPLETE);
+}
+
+static void test_in_ipv6_esp_reass_incomp_missing(odp_ipsec_sa_t out_sa,
+ odp_ipsec_sa_t in_sa)
+{
+ ipsec_test_packet *input_packets[] = {
+ &pkt_ipv6_udp_p1_f1,
+ };
+ ipsec_test_packet *result_packet = &pkt_ipv6_udp_p1_f1;
+
+ test_multi_out_in(out_sa, in_sa, ODPH_IPV6,
+ ODPH_ARRAY_SIZE(input_packets),
+ input_packets,
+ result_packet,
+ ODP_PACKET_REASS_INCOMPLETE);
+}
+
+static void test_in_ipv6_esp_reass_success(void)
+{
+ odp_ipsec_tunnel_param_t in_tunnel, out_tunnel;
+ odp_ipsec_sa_param_t param_in, param_out;
+ odp_ipsec_sa_t out_sa, in_sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&in_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ memset(&out_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ out_tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ out_tunnel.ipv6.src_addr = &src;
+ out_tunnel.ipv6.dst_addr = &dst;
+ out_tunnel.ipv6.hlimit = 64;
+
+ ipsec_sa_param_fill(&param_out,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &out_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &in_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+ param_in.inbound.reassembly_en = 1;
+
+ out_sa = odp_ipsec_sa_create(&param_out);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != out_sa);
+
+ in_sa = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != in_sa);
+
+ printf("\n IPv6 two frags");
+ test_in_ipv6_esp_reass_success_two_frags(out_sa, in_sa);
+
+ printf("\n IPv6 four frags");
+ test_in_ipv6_esp_reass_success_four_frags(out_sa, in_sa);
+
+ printf("\n IPv6 two frags out of order");
+ test_in_ipv6_esp_reass_success_two_frags_ooo(out_sa, in_sa);
+
+ printf("\n IPv6 four frags out of order");
+ test_in_ipv6_esp_reass_success_four_frags_ooo(out_sa, in_sa);
+
+ printf("\n");
+
+ ipsec_sa_destroy(in_sa);
+ ipsec_sa_destroy(out_sa);
+}
+
+static void test_in_ipv6_esp_reass_incomp(void)
+{
+ odp_ipsec_tunnel_param_t in_tunnel, out_tunnel;
+ odp_ipsec_sa_param_t param_in, param_out;
+ odp_ipsec_sa_t out_sa, in_sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&in_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ memset(&out_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ out_tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ out_tunnel.ipv6.src_addr = &src;
+ out_tunnel.ipv6.dst_addr = &dst;
+
+ ipsec_sa_param_fill(&param_out,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &out_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &in_tunnel,
+ ODP_CIPHER_ALG_AES_GCM, &key_mcgrew_gcm_4,
+ ODP_AUTH_ALG_AES_GCM, NULL,
+ &key_mcgrew_gcm_salt_4, NULL);
+ param_in.inbound.reassembly_en = 1;
+
+ out_sa = odp_ipsec_sa_create(&param_out);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != out_sa);
+
+ in_sa = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != in_sa);
+
+ printf("\n IPv6 missing frag");
+ test_in_ipv6_esp_reass_incomp_missing(out_sa, in_sa);
+
+ printf("\n");
+
+ ipsec_sa_destroy(in_sa);
+ ipsec_sa_destroy(out_sa);
+}
+
+static void test_in_ipv4_null_aes_xcbc_esp(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 0x100, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_AES_XCBC_MAC, &key_auth_aes_xcbc_128,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_null_aes_xcbc_esp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_UDP,
+ .pkt_res = &pkt_ipv4_null_aes_xcbc_plain,
+ },
+ },
+ };
+
+ ipsec_check_in_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+odp_testinfo_t ipsec_in_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_rfc3602_5_esp,
+ ipsec_check_esp_aes_cbc_128_null),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_rfc3602_6_esp,
+ ipsec_check_esp_aes_cbc_128_null),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_rfc3602_7_esp,
+ ipsec_check_esp_aes_cbc_128_null),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_rfc3602_8_esp,
+ ipsec_check_esp_aes_cbc_128_null),
+ /* test 1, 5, 6, 8 -- 11 -- ESN */
+ /* test 7 -- invalid, plaintext packet includes trl into IP length */
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_2_esp,
+ ipsec_check_esp_aes_gcm_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_3_esp,
+ ipsec_check_esp_aes_gcm_256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_4_esp,
+ ipsec_check_esp_aes_gcm_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_12_esp,
+ ipsec_check_esp_aes_gcm_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_12_esp_notun,
+ ipsec_check_esp_aes_gcm_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_mcgrew_gcm_15_esp,
+ ipsec_check_esp_null_aes_gmac_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_rfc7634_chacha,
+ ipsec_check_esp_chacha20_poly1305),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_tun_ipv4,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_tun_ipv6,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_tun_ipv4_notun,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_cbc_null,
+ ipsec_check_esp_aes_cbc_128_null),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_cbc_sha1,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_cbc_sha256,
+ ipsec_check_esp_aes_cbc_128_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_cbc_sha384,
+ ipsec_check_esp_aes_cbc_128_sha384),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_cbc_sha512,
+ ipsec_check_esp_aes_cbc_128_sha512),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_aes_ctr_null,
+ ipsec_check_esp_aes_ctr_128_null),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_lookup,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_lookup,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_tun_ipv4,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_tun_ipv6,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_udp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_udp_null_sha256_lookup,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_noreplay,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_replay,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_noreplay,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_replay,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_esp_pkt,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_ah_pkt,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_esp_pkt_lookup,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_ah_pkt_lookup,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_bad1,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_sha256_bad2,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_sha256_bad1,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_ah_aes_gmac_128,
+ ipsec_check_ah_aes_gmac_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_null_aes_gmac_128,
+ ipsec_check_esp_null_aes_gmac_128),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_ah_sha256,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_ah_sha256_tun_ipv4,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_ah_sha256_tun_ipv6,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_null_sha256_tun_ipv4,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_null_sha256_tun_ipv6,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_udp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_udp_null_sha256_lookup,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO(test_ipsec_print),
+ ODP_TEST_INFO_CONDITIONAL(test_ipsec_sa_print,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_reass_success,
+ ipsec_check_esp_aes_gcm_128_reass_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_esp_reass_incomp,
+ ipsec_check_esp_aes_gcm_128_reass_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_reass_success,
+ ipsec_check_esp_aes_gcm_128_reass_ipv6),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv6_esp_reass_incomp,
+ ipsec_check_esp_aes_gcm_128_reass_ipv6),
+ ODP_TEST_INFO_CONDITIONAL(test_in_ipv4_null_aes_xcbc_esp,
+ ipsec_check_esp_null_aes_xcbc),
+ ODP_TEST_INFO_NULL,
+};
diff --git a/test/validation/api/ipsec/ipsec_test_out.c b/test/validation/api/ipsec/ipsec_test_out.c
new file mode 100644
index 000000000..fea66b630
--- /dev/null
+++ b/test/validation/api/ipsec/ipsec_test_out.c
@@ -0,0 +1,2066 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2020 Marvell
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+#include <odp/helper/odph_api.h>
+
+#include "ipsec.h"
+#include "test_vectors.h"
+
+/*
+ * Miscellaneous parameters for combined out+in tests
+ */
+typedef struct {
+ ipsec_test_part_flags_t part_flags;
+ odp_bool_t display_algo;
+ odp_bool_t ah;
+ odp_bool_t v6;
+ odp_bool_t tunnel;
+ odp_bool_t tunnel_is_v6;
+ odp_bool_t udp_encap;
+ enum ipsec_test_stats stats;
+} ipsec_test_flags;
+
+static void test_out_in_all(const ipsec_test_flags *flags);
+
+struct cipher_param {
+ const char *name;
+ odp_cipher_alg_t algo;
+ const odp_crypto_key_t *key;
+ const odp_crypto_key_t *key_extra;
+};
+
+struct auth_param {
+ const char *name;
+ odp_auth_alg_t algo;
+ const odp_crypto_key_t *key;
+ const odp_crypto_key_t *key_extra;
+};
+
+#define ALG(alg, key, key_extra) { #alg, alg, key, key_extra }
+
+/*
+ * Ciphers that can be used in ESP and combined with any integrity
+ * algorithm. This excludes combined mode algorithms such as AES-GCM.
+ */
+static struct cipher_param ciphers[] = {
+ ALG(ODP_CIPHER_ALG_NULL, NULL, NULL),
+ ALG(ODP_CIPHER_ALG_DES, &key_des_64, NULL),
+ ALG(ODP_CIPHER_ALG_3DES_CBC, &key_des_192, NULL),
+ ALG(ODP_CIPHER_ALG_AES_CBC, &key_a5_128, NULL),
+ ALG(ODP_CIPHER_ALG_AES_CBC, &key_a5_192, NULL),
+ ALG(ODP_CIPHER_ALG_AES_CBC, &key_a5_256, NULL),
+ ALG(ODP_CIPHER_ALG_AES_CTR, &key_a5_128, &key_mcgrew_gcm_salt_3),
+ ALG(ODP_CIPHER_ALG_AES_CTR, &key_a5_192, &key_mcgrew_gcm_salt_3),
+ ALG(ODP_CIPHER_ALG_AES_CTR, &key_a5_256, &key_mcgrew_gcm_salt_3)
+};
+
+/*
+ * Integrity algorithms that can be used in ESP and AH. This excludes
+ * AES-GMAC which is defined for ESP as a combined-mode algorithm.
+ */
+static struct auth_param auths[] = {
+ ALG(ODP_AUTH_ALG_NULL, NULL, NULL),
+ ALG(ODP_AUTH_ALG_MD5_HMAC, &key_5a_128, NULL),
+ ALG(ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160, NULL),
+ ALG(ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256, NULL),
+ ALG(ODP_AUTH_ALG_SHA384_HMAC, &key_5a_384, NULL),
+ ALG(ODP_AUTH_ALG_SHA512_HMAC, &key_5a_512, NULL),
+ ALG(ODP_AUTH_ALG_AES_CMAC, &key_5a_128, NULL),
+ ALG(ODP_AUTH_ALG_AES_XCBC_MAC, &key_5a_128, NULL)
+};
+
+/*
+ * Integrity algorithms that can be used in AH but not in ESP as
+ * individual algorithms (combined with a cipher).
+ */
+static struct auth_param ah_auths[] = {
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_128, &key_mcgrew_gcm_salt_2),
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_192, &key_mcgrew_gcm_salt_2),
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_256, &key_mcgrew_gcm_salt_2),
+};
+
+struct cipher_auth_comb_param {
+ struct cipher_param cipher;
+ struct auth_param auth;
+};
+
+static struct cipher_auth_comb_param cipher_auth_comb[] = {
+ {
+ ALG(ODP_CIPHER_ALG_AES_GCM, &key_a5_128, &key_mcgrew_gcm_salt_2),
+ ALG(ODP_AUTH_ALG_AES_GCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_AES_GCM, &key_a5_192, &key_mcgrew_gcm_salt_2),
+ ALG(ODP_AUTH_ALG_AES_GCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_AES_GCM, &key_a5_256, &key_mcgrew_gcm_salt_2),
+ ALG(ODP_AUTH_ALG_AES_GCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_NULL, NULL, NULL),
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_128, &key_mcgrew_gcm_salt_2),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_NULL, NULL, NULL),
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_192, &key_mcgrew_gcm_salt_2),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_NULL, NULL, NULL),
+ ALG(ODP_AUTH_ALG_AES_GMAC, &key_a5_256, &key_mcgrew_gcm_salt_2),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_AES_CCM, &key_a5_128, &key_3byte_salt),
+ ALG(ODP_AUTH_ALG_AES_CCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_AES_CCM, &key_a5_192, &key_3byte_salt),
+ ALG(ODP_AUTH_ALG_AES_CCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_AES_CCM, &key_a5_256, &key_3byte_salt),
+ ALG(ODP_AUTH_ALG_AES_CCM, NULL, NULL),
+ },
+ {
+ ALG(ODP_CIPHER_ALG_CHACHA20_POLY1305, &key_rfc7634, &key_rfc7634_salt),
+ ALG(ODP_AUTH_ALG_CHACHA20_POLY1305, NULL, NULL),
+ },
+};
+
+static void test_out_ipv4_ah_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_ah_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_ah_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_ah_tun_ipv4_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_ah_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = src;
+ tunnel.ipv6.dst_addr = dst;
+ tunnel.ipv6.hlimit = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_ah_tun_ipv6_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_esp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_esp_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_esp_null_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res =
+ &pkt_ipv4_icmp_0_esp_tun_ipv4_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_esp_null_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = src;
+ tunnel.ipv6.dst_addr = dst;
+ tunnel.ipv6.hlimit = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res =
+ &pkt_ipv4_icmp_0_esp_tun_ipv6_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_ipsec_stats_zero_assert(odp_ipsec_stats_t *stats)
+{
+ CU_ASSERT(stats->success == 0);
+ CU_ASSERT(stats->proto_err == 0);
+ CU_ASSERT(stats->auth_err == 0);
+ CU_ASSERT(stats->antireplay_err == 0);
+ CU_ASSERT(stats->alg_err == 0);
+ CU_ASSERT(stats->mtu_err == 0);
+ CU_ASSERT(stats->hard_exp_bytes_err == 0);
+ CU_ASSERT(stats->hard_exp_pkts_err == 0);
+ CU_ASSERT(stats->success_bytes == 0);
+}
+
+static void test_ipsec_stats_test_assert(odp_ipsec_stats_t *stats,
+ enum ipsec_test_stats test,
+ uint64_t succ_bytes)
+{
+ if (test == IPSEC_TEST_STATS_SUCCESS) {
+ CU_ASSERT(stats->success == 1);
+ CU_ASSERT(stats->success_bytes >= succ_bytes);
+ } else {
+ CU_ASSERT(stats->success == 0);
+ CU_ASSERT(stats->success_bytes == 0);
+ }
+
+ if (test == IPSEC_TEST_STATS_PROTO_ERR) {
+ /* Braces needed by CU macro */
+ CU_ASSERT(stats->proto_err == 1);
+ } else {
+ /* Braces needed by CU macro */
+ CU_ASSERT(stats->proto_err == 0);
+ }
+
+ if (test == IPSEC_TEST_STATS_AUTH_ERR) {
+ /* Braces needed by CU macro */
+ CU_ASSERT(stats->auth_err == 1);
+ } else {
+ /* Braces needed by CU macro */
+ CU_ASSERT(stats->auth_err == 0);
+ }
+
+ CU_ASSERT(stats->antireplay_err == 0);
+ CU_ASSERT(stats->alg_err == 0);
+ CU_ASSERT(stats->mtu_err == 0);
+ CU_ASSERT(stats->hard_exp_bytes_err == 0);
+ CU_ASSERT(stats->hard_exp_pkts_err == 0);
+}
+
+static void ipsec_pkt_proto_err_set(odp_packet_t pkt)
+{
+ uint32_t l3_off = odp_packet_l3_offset(pkt);
+ odph_ipv4hdr_t ip;
+
+ memset(&ip, 0, sizeof(ip));
+
+ /* Simulate proto error by corrupting protocol field */
+
+ odp_packet_copy_to_mem(pkt, l3_off, sizeof(ip), &ip);
+
+ if (ip.proto == ODPH_IPPROTO_ESP)
+ ip.proto = ODPH_IPPROTO_AH;
+ else
+ ip.proto = ODPH_IPPROTO_ESP;
+
+ odp_packet_copy_from_mem(pkt, l3_off, sizeof(ip), &ip);
+}
+
+static void ipsec_pkt_auth_err_set(odp_packet_t pkt)
+{
+ uint32_t data, len;
+
+ /* Simulate auth error by corrupting ICV */
+
+ len = odp_packet_len(pkt);
+ odp_packet_copy_to_mem(pkt, len - sizeof(data), sizeof(data), &data);
+ data = ~data;
+ odp_packet_copy_from_mem(pkt, len - sizeof(data), sizeof(data), &data);
+}
+
+static void ipsec_pkt_update(odp_packet_t pkt, const ipsec_test_flags *flags)
+{
+ if (flags && flags->stats == IPSEC_TEST_STATS_PROTO_ERR)
+ ipsec_pkt_proto_err_set(pkt);
+
+ if (flags && flags->stats == IPSEC_TEST_STATS_AUTH_ERR)
+ ipsec_pkt_auth_err_set(pkt);
+}
+
+static void ipsec_check_out_in_one(const ipsec_test_part *part_outbound,
+ const ipsec_test_part *part_inbound,
+ odp_ipsec_sa_t sa,
+ odp_ipsec_sa_t sa_in,
+ const ipsec_test_flags *flags)
+{
+ int num_out = part_outbound->num_pkt;
+ odp_packet_t pkto[num_out];
+ int i;
+
+ num_out = ipsec_check_out(part_outbound, sa, pkto);
+
+ for (i = 0; i < num_out; i++) {
+ ipsec_test_part part_in = *part_inbound;
+ ipsec_test_packet pkt_in;
+
+ ipsec_pkt_update(pkto[i], flags);
+
+ ipsec_test_packet_from_pkt(&pkt_in, &pkto[i]);
+ part_in.pkt_in = &pkt_in;
+
+ ipsec_check_in_one(&part_in, sa_in);
+ }
+}
+
+static int sa_creation_failure_ok(const odp_ipsec_sa_param_t *param)
+{
+ odp_cipher_alg_t cipher = param->crypto.cipher_alg;
+ odp_auth_alg_t auth = param->crypto.auth_alg;
+
+ /* Single algorithm must not fail */
+ if (cipher == ODP_CIPHER_ALG_NULL || auth == ODP_AUTH_ALG_NULL)
+ return 0;
+
+ /* Combined mode algorithms must not fail */
+ if (cipher == ODP_CIPHER_ALG_AES_GCM ||
+ cipher == ODP_CIPHER_ALG_AES_CCM ||
+ cipher == ODP_CIPHER_ALG_CHACHA20_POLY1305)
+ return 0;
+
+ /* Combination of mandatory algorithms must not fail */
+ if (cipher == ODP_CIPHER_ALG_AES_CBC && auth == ODP_AUTH_ALG_SHA1_HMAC)
+ return 0;
+
+ printf("\n Algorithm combination (%d, %d) maybe not supported.\n", cipher, auth);
+ printf(" SA creation failed, skipping test.\n");
+ return 1;
+}
+
+static void test_out_in_common(const ipsec_test_flags *flags,
+ odp_cipher_alg_t cipher,
+ const odp_crypto_key_t *cipher_key,
+ odp_auth_alg_t auth,
+ const odp_crypto_key_t *auth_key,
+ const odp_crypto_key_t *cipher_key_extra,
+ const odp_crypto_key_t *auth_key_extra)
+{
+ odp_ipsec_tunnel_param_t *tun_ptr = NULL;
+ odp_ipsec_tunnel_param_t tunnel;
+ uint32_t src_v4 = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst_v4 = IPV4ADDR(10, 0, 222, 2);
+ uint8_t src_v6[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst_v6[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_stats_t stats;
+ odp_ipsec_protocol_t proto = flags->ah ? ODP_IPSEC_AH : ODP_IPSEC_ESP;
+ odp_ipsec_sa_t sa_out;
+ odp_ipsec_sa_t sa_in;
+ odp_proto_l3_type_t out_l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ odp_proto_l4_type_t out_l4_type = ODP_PROTO_L4_TYPE_ESP;
+
+ CU_ASSERT_FATAL(flags != NULL);
+
+ /* ICV won't be generated for NULL AUTH */
+ if ((flags->stats == IPSEC_TEST_STATS_AUTH_ERR) &&
+ (auth == ODP_AUTH_ALG_NULL))
+ return;
+
+ if (flags->tunnel) {
+ if (flags->tunnel_is_v6) {
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = &src_v6;
+ tunnel.ipv6.dst_addr = &dst_v6;
+ tunnel.ipv6.hlimit = 64;
+ tun_ptr = &tunnel;
+ } else {
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src_v4;
+ tunnel.ipv4.dst_addr = &dst_v4;
+ tunnel.ipv4.ttl = 64;
+ tun_ptr = &tunnel;
+ }
+ }
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, proto, 123, tun_ptr,
+ cipher, cipher_key,
+ auth, auth_key,
+ cipher_key_extra, auth_key_extra);
+
+ if (flags->udp_encap)
+ param.opt.udp_encap = 1;
+
+ sa_out = odp_ipsec_sa_create(&param);
+
+ if (sa_out == ODP_IPSEC_SA_INVALID && sa_creation_failure_ok(&param))
+ return;
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa_out);
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, proto, 123, tun_ptr,
+ cipher, cipher_key,
+ auth, auth_key,
+ cipher_key_extra, auth_key_extra);
+
+ if (flags->udp_encap)
+ param.opt.udp_encap = 1;
+
+ sa_in = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa_in);
+
+ if ((flags->tunnel && flags->tunnel_is_v6) ||
+ (!flags->tunnel && flags->v6))
+ out_l3_type = ODP_PROTO_L3_TYPE_IPV6;
+ if (flags->ah)
+ out_l4_type = ODP_PROTO_L4_TYPE_AH;
+ if (flags->udp_encap)
+ out_l4_type = ODP_PROTO_L4_TYPE_UDP;
+
+ ipsec_test_part test_out = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = out_l3_type,
+ .l4_type = out_l4_type,
+ },
+ },
+ };
+ ipsec_test_part test_in = {
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ if (flags->v6) {
+ test_out.pkt_in = &pkt_ipv6_icmp_0;
+ test_in.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV6;
+ test_in.out[0].l4_type = ODP_PROTO_L4_TYPE_ICMPV6;
+ test_in.out[0].pkt_res = &pkt_ipv6_icmp_0;
+ }
+
+ test_out.flags = flags->part_flags;
+ test_in.flags = flags->part_flags;
+
+ if (flags->stats == IPSEC_TEST_STATS_PROTO_ERR)
+ test_in.out[0].status.error.proto = 1;
+ if (flags->stats == IPSEC_TEST_STATS_AUTH_ERR)
+ test_in.out[0].status.error.auth = 1;
+
+ if (flags->stats != IPSEC_TEST_STATS_NONE) {
+ CU_ASSERT(odp_ipsec_stats(sa_out, &stats) == 0);
+ test_ipsec_stats_zero_assert(&stats);
+ CU_ASSERT(odp_ipsec_stats(sa_in, &stats) == 0);
+ test_ipsec_stats_zero_assert(&stats);
+ }
+
+ if (flags->part_flags.test_sa_seq_num) {
+ int rc;
+
+ test_out.out[0].seq_num = 0x1235;
+ rc = ipsec_test_sa_update_seq_num(sa_out,
+ test_out.out[0].seq_num);
+
+ /* Skip further checks related to this specific test if the
+ * SA update call was not successful.
+ */
+ if (rc < 0) {
+ printf("\t >> skipped");
+ test_out.flags.test_sa_seq_num = false;
+ }
+ }
+
+ ipsec_check_out_in_one(&test_out, &test_in, sa_out, sa_in, flags);
+
+ if (flags->stats != IPSEC_TEST_STATS_NONE) {
+ uint64_t succ_bytes = 0;
+
+ /* Minimum bytes to be counted for stats.success_bytes */
+ if (!flags->ah) {
+ succ_bytes = test_out.pkt_in[0].len -
+ test_out.pkt_in[0].l4_offset;
+
+ if (flags->tunnel)
+ succ_bytes += test_out.pkt_in[0].l4_offset -
+ test_out.pkt_in[0].l3_offset;
+ } else {
+ succ_bytes = test_out.pkt_in[0].len -
+ test_out.pkt_in[0].l3_offset;
+
+ if (flags->tunnel)
+ succ_bytes += (flags->tunnel_is_v6 ?
+ ODPH_IPV6HDR_LEN :
+ ODPH_IPV4HDR_LEN);
+ }
+
+ /* All stats tests have outbound operation success and inbound
+ * varying.
+ */
+ CU_ASSERT(odp_ipsec_stats(sa_out, &stats) == 0);
+ test_ipsec_stats_test_assert(&stats, IPSEC_TEST_STATS_SUCCESS,
+ succ_bytes);
+
+ CU_ASSERT(odp_ipsec_stats(sa_in, &stats) == 0);
+ test_ipsec_stats_test_assert(&stats, flags->stats, succ_bytes);
+ }
+
+ ipsec_sa_destroy(sa_out);
+ ipsec_sa_destroy(sa_in);
+}
+
+static void test_esp_out_in(struct cipher_param *cipher,
+ struct auth_param *auth,
+ const ipsec_test_flags *flags)
+{
+ int cipher_keylen = cipher->key ? 8 * cipher->key->length : 0;
+ int auth_keylen = auth->key ? 8 * auth->key->length : 0;
+
+ if (ipsec_check_esp(cipher->algo, cipher_keylen,
+ auth->algo, auth_keylen) != ODP_TEST_ACTIVE)
+ return;
+
+ if (flags->display_algo)
+ printf("\n %s (keylen %d) %s (keylen %d) ",
+ cipher->name, cipher_keylen, auth->name, auth_keylen);
+
+ test_out_in_common(flags, cipher->algo, cipher->key,
+ auth->algo, auth->key,
+ cipher->key_extra, auth->key_extra);
+}
+
+static void test_esp_out_in_all(const ipsec_test_flags *flags_in)
+{
+ uint32_t c;
+ uint32_t a;
+ ipsec_test_flags flags = *flags_in;
+
+ flags.ah = false;
+
+ for (c = 0; c < ODPH_ARRAY_SIZE(ciphers); c++)
+ for (a = 0; a < ODPH_ARRAY_SIZE(auths); a++)
+ test_esp_out_in(&ciphers[c], &auths[a], &flags);
+
+ for (c = 0; c < ODPH_ARRAY_SIZE(cipher_auth_comb); c++)
+ test_esp_out_in(&cipher_auth_comb[c].cipher,
+ &cipher_auth_comb[c].auth,
+ &flags);
+}
+
+/*
+ * Test ESP output followed by input with all combinations of ciphers and
+ * integrity algorithms.
+ */
+static void test_esp_out_in_all_basic(void)
+{
+ ipsec_test_flags flags;
+
+ memset(&flags, 0, sizeof(flags));
+ flags.display_algo = true;
+
+ test_esp_out_in_all(&flags);
+
+ printf("\n ");
+}
+
+static int is_out_mode_inline(void)
+{
+ return suite_context.outbound_op_mode == ODP_IPSEC_OP_MODE_INLINE;
+}
+
+static void test_inline_hdr_in_packet(void)
+{
+ ipsec_test_flags flags = {
+ .part_flags.inline_hdr_in_packet = true,
+ };
+ test_out_in_all(&flags);
+}
+
+static void test_ah_out_in(struct auth_param *auth,
+ const ipsec_test_flags *flags_in)
+{
+ int auth_keylen = auth->key ? 8 * auth->key->length : 0;
+ ipsec_test_flags flags = *flags_in;
+
+ if (ipsec_check_ah(auth->algo, auth_keylen) != ODP_TEST_ACTIVE)
+ return;
+
+ if (flags.display_algo)
+ printf("\n %s (keylen %d) ", auth->name, auth_keylen);
+
+ flags.ah = true;
+
+ test_out_in_common(&flags, ODP_CIPHER_ALG_NULL, NULL,
+ auth->algo, auth->key,
+ NULL, auth->key_extra);
+}
+
+static void test_ah_out_in_all(const ipsec_test_flags *flags)
+{
+ uint32_t a;
+
+ for (a = 0; a < ODPH_ARRAY_SIZE(auths); a++)
+ test_ah_out_in(&auths[a], flags);
+ for (a = 0; a < ODPH_ARRAY_SIZE(ah_auths); a++)
+ test_ah_out_in(&ah_auths[a], flags);
+}
+
+static void test_ah_out_in_all_basic(void)
+{
+ ipsec_test_flags flags;
+
+ memset(&flags, 0, sizeof(flags));
+ flags.display_algo = true;
+
+ test_ah_out_in_all(&flags);
+
+ printf("\n ");
+}
+
+static void test_out_in_all(const ipsec_test_flags *flags)
+{
+ test_esp_out_in_all(flags);
+ test_ah_out_in_all(flags);
+}
+
+static void test_out_ipv4_esp_udp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_esp_udp_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_ah_sha256_frag_check(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+ ipsec_test_part test2;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+ memset(&test2, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.outbound.frag_mode = ODP_IPSEC_FRAG_CHECK;
+ param.outbound.mtu = 100;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0;
+ test.num_pkt = 1;
+ test.out[0].status.error.mtu = 1;
+ test.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ test.out[0].l4_type = ODP_PROTO_L4_TYPE_ICMPV4;
+
+ test2.pkt_in = &pkt_ipv4_icmp_0;
+ test2.num_opt = 1;
+ test2.opt.flag.frag_mode = 1;
+ test2.opt.frag_mode = ODP_IPSEC_FRAG_DISABLED;
+ test2.num_pkt = 1;
+ test2.out[0].pkt_res = &pkt_ipv4_icmp_0_ah_sha256_1;
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_check_out_one(&test2, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_ah_sha256_frag_check_2(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.outbound.frag_mode = ODP_IPSEC_FRAG_CHECK;
+ param.outbound.mtu = 100;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0;
+ test.num_pkt = 1;
+ test.out[0].status.error.mtu = 1;
+ test.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ test.out[0].l4_type = ODP_PROTO_L4_TYPE_ICMPV4;
+
+ ipsec_test_part test2 = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_ah_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ odp_ipsec_sa_mtu_update(sa, 256);
+
+ ipsec_check_out_one(&test2, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_esp_null_sha256_frag_check(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+ ipsec_test_part test2;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+ memset(&test2, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ param.outbound.frag_mode = ODP_IPSEC_FRAG_CHECK;
+ param.outbound.mtu = 100;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0;
+ test.num_pkt = 1;
+ test.out[0].status.error.mtu = 1;
+ test.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ test.out[0].l4_type = ODP_PROTO_L4_TYPE_ICMPV4;
+
+ test2.pkt_in = &pkt_ipv4_icmp_0;
+ test2.num_opt = 1;
+ test2.opt.flag.frag_mode = 1;
+ test2.opt.frag_mode = ODP_IPSEC_FRAG_DISABLED;
+ test2.num_pkt = 1;
+ test2.out[0].pkt_res = &pkt_ipv4_icmp_0_esp_null_sha256_1;
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_check_out_one(&test2, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_esp_null_sha256_frag_check_2(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ ipsec_test_part test;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ param.outbound.frag_mode = ODP_IPSEC_FRAG_CHECK;
+ param.outbound.mtu = 100;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ test.pkt_in = &pkt_ipv4_icmp_0;
+ test.num_pkt = 1;
+ test.out[0].status.error.mtu = 1;
+ test.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ test.out[0].l4_type = ODP_PROTO_L4_TYPE_ICMPV4;
+
+ ipsec_test_part test2 = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_icmp_0_esp_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ odp_ipsec_sa_mtu_update(sa, 256);
+
+ ipsec_check_out_one(&test2, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_ah_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv6_icmp_0_ah_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_ah_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv6_icmp_0_ah_tun_ipv4_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_ah_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = src;
+ tunnel.ipv6.dst_addr = dst;
+ tunnel.ipv6.hlimit = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_AH, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv6_icmp_0_ah_tun_ipv6_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_esp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv6_icmp_0_esp_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_esp_null_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res =
+ &pkt_ipv6_icmp_0_esp_tun_ipv4_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_esp_null_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = &src;
+ tunnel.ipv6.dst_addr = &dst;
+ tunnel.ipv6.hlimit = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res =
+ &pkt_ipv6_icmp_0_esp_tun_ipv6_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv6_esp_udp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+ param.opt.udp_encap = 1;
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv6_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv6_icmp_0_esp_udp_null_sha256_1 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_dummy_esp_null_sha256_tun(odp_ipsec_tunnel_param_t tunnel)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+ odp_ipsec_sa_t sa2;
+ ipsec_test_part test;
+ ipsec_test_part test_in;
+ ipsec_test_part test_empty;
+ odp_proto_l3_type_t out_l3_type = ODP_PROTO_L3_TYPE_IPV4;
+
+ if (tunnel.type == ODP_IPSEC_TUNNEL_IPV6)
+ out_l3_type = ODP_PROTO_L3_TYPE_IPV6;
+
+ memset(&test, 0, sizeof(ipsec_test_part));
+ memset(&test_in, 0, sizeof(ipsec_test_part));
+ memset(&test_empty, 0, sizeof(ipsec_test_part));
+
+ /* This test will not work properly in inbound inline mode.
+ * Packet might be dropped and we will not check for that. */
+ if (suite_context.inbound_op_mode == ODP_IPSEC_OP_MODE_INLINE)
+ return;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa2 = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa2);
+
+ test.pkt_in = &pkt_test_nodata;
+ test.num_opt = 1;
+ test.opt.flag.tfc_dummy = 1;
+ test.opt.tfc_pad_len = 16;
+ test.num_pkt = 1;
+ test.out[0].l3_type = out_l3_type;
+ test.out[0].l4_type = ODP_PROTO_L4_TYPE_ESP;
+
+ test_in.num_pkt = 1;
+ test_in.out[0].l3_type = ODP_PROTO_L3_TYPE_IPV4;
+ test_in.out[0].l4_type = ODP_PROTO_L4_TYPE_NO_NEXT;
+
+ test_empty.pkt_in = &pkt_test_empty;
+ test_empty.num_opt = 1;
+ test_empty.opt.flag.tfc_dummy = 1;
+ test_empty.opt.tfc_pad_len = 16;
+ test_empty.num_pkt = 1;
+ test_empty.out[0].l3_type = out_l3_type;
+ test_empty.out[0].l4_type = ODP_PROTO_L4_TYPE_ESP;
+
+ ipsec_check_out_in_one(&test, &test_in, sa, sa2, NULL);
+ ipsec_check_out_in_one(&test_empty, &test_in, sa, sa2, NULL);
+
+ ipsec_sa_destroy(sa2);
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_dummy_esp_null_sha256_tun_ipv4(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ test_out_dummy_esp_null_sha256_tun(tunnel);
+}
+
+static void test_out_dummy_esp_null_sha256_tun_ipv6(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ uint8_t src[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ };
+ uint8_t dst[16] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ };
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV6;
+ tunnel.ipv6.src_addr = src;
+ tunnel.ipv6.dst_addr = dst;
+ tunnel.ipv6.hlimit = 64;
+
+ test_out_dummy_esp_null_sha256_tun(tunnel);
+}
+
+static void test_out_ipv4_udp_esp_null_sha256(void)
+{
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_SHA256_HMAC, &key_5a_256,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_udp,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .pkt_res = &pkt_ipv4_udp_esp_null_sha256 },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_out_ipv4_null_aes_xcbc(void)
+{
+ odp_ipsec_tunnel_param_t tunnel;
+ odp_ipsec_sa_param_t param;
+ odp_ipsec_sa_t sa;
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+
+ memset(&tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+ tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel.ipv4.src_addr = &src;
+ tunnel.ipv4.dst_addr = &dst;
+ tunnel.ipv4.ttl = 64;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x100, &tunnel,
+ ODP_CIPHER_ALG_NULL, NULL,
+ ODP_AUTH_ALG_AES_XCBC_MAC, &key_auth_aes_xcbc_128,
+ NULL, NULL);
+
+ sa = odp_ipsec_sa_create(&param);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa);
+
+ ipsec_test_part test = {
+ .pkt_in = &pkt_ipv4_null_aes_xcbc_plain,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = _ODP_PROTO_L4_TYPE_UNDEF,
+ .pkt_res = &pkt_ipv4_null_aes_xcbc_esp,
+ },
+ },
+ };
+
+ ipsec_check_out_one(&test, sa);
+
+ ipsec_sa_destroy(sa);
+}
+
+static void test_sa_info(void)
+{
+ uint32_t src = IPV4ADDR(10, 0, 111, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 222, 2);
+ odp_ipsec_tunnel_param_t tunnel_out;
+ odp_ipsec_tunnel_param_t tunnel_in;
+ odp_ipsec_sa_param_t param_out;
+ odp_ipsec_sa_param_t param_in;
+ odp_ipsec_sa_info_t info_out;
+ odp_ipsec_sa_info_t info_in;
+ odp_ipsec_sa_t sa_out;
+ odp_ipsec_sa_t sa_in;
+
+ memset(&tunnel_out, 0, sizeof(tunnel_out));
+ memset(&tunnel_in, 0, sizeof(tunnel_in));
+
+ tunnel_out.type = ODP_IPSEC_TUNNEL_IPV4;
+ tunnel_out.ipv4.src_addr = &src;
+ tunnel_out.ipv4.dst_addr = &dst;
+
+ ipsec_sa_param_fill(&param_out,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 123, &tunnel_out,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+
+ sa_out = odp_ipsec_sa_create(&param_out);
+
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != sa_out);
+
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ 123, &tunnel_in,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+
+ param_in.inbound.antireplay_ws = 32;
+ sa_in = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(sa_in != ODP_IPSEC_SA_INVALID);
+
+ memset(&info_out, 0, sizeof(info_out));
+ CU_ASSERT_FATAL(0 == odp_ipsec_sa_info(sa_out, &info_out));
+
+ CU_ASSERT(info_out.param.dir == param_out.dir);
+ CU_ASSERT(info_out.param.proto == param_out.proto);
+ CU_ASSERT(info_out.param.mode == param_out.mode);
+
+ CU_ASSERT(info_out.param.crypto.cipher_alg == param_out.crypto.cipher_alg);
+ CU_ASSERT(info_out.param.crypto.auth_alg == param_out.crypto.auth_alg);
+ CU_ASSERT(info_out.param.opt.udp_encap == param_out.opt.udp_encap);
+ CU_ASSERT(info_out.param.spi == param_out.spi);
+ CU_ASSERT(info_out.param.opt.esn == param_out.opt.esn);
+ CU_ASSERT(info_out.param.opt.udp_encap == param_out.opt.udp_encap);
+ CU_ASSERT(info_out.param.opt.copy_dscp == param_out.opt.copy_dscp);
+ CU_ASSERT(info_out.param.opt.copy_flabel == param_out.opt.copy_flabel);
+ CU_ASSERT(info_out.param.opt.copy_df == param_out.opt.copy_df);
+
+ CU_ASSERT(ODP_IPSEC_MODE_TUNNEL == info_out.param.mode);
+
+ CU_ASSERT(info_out.param.outbound.tunnel.type == param_out.outbound.tunnel.type);
+ CU_ASSERT(info_out.param.outbound.tunnel.ipv4.dscp == param_out.outbound.tunnel.ipv4.dscp);
+ CU_ASSERT(info_out.param.outbound.tunnel.ipv4.df == param_out.outbound.tunnel.ipv4.df);
+ CU_ASSERT_FATAL(NULL != info_out.param.outbound.tunnel.ipv4.src_addr);
+ CU_ASSERT(0 == memcmp(info_out.param.outbound.tunnel.ipv4.src_addr,
+ param_out.outbound.tunnel.ipv4.src_addr,
+ ODP_IPV4_ADDR_SIZE));
+ CU_ASSERT_FATAL(NULL != info_out.param.outbound.tunnel.ipv4.dst_addr);
+ CU_ASSERT(0 == memcmp(info_out.param.outbound.tunnel.ipv4.dst_addr,
+ param_out.outbound.tunnel.ipv4.dst_addr,
+ ODP_IPV4_ADDR_SIZE));
+
+ CU_ASSERT(info_out.param.lifetime.soft_limit.bytes == param_out.lifetime.soft_limit.bytes);
+ CU_ASSERT(info_out.param.lifetime.hard_limit.bytes == param_out.lifetime.hard_limit.bytes);
+ CU_ASSERT(info_out.param.lifetime.soft_limit.packets ==
+ param_out.lifetime.soft_limit.packets);
+ CU_ASSERT(info_out.param.lifetime.hard_limit.packets ==
+ param_out.lifetime.hard_limit.packets);
+
+ CU_ASSERT(0 == info_out.outbound.seq_num);
+
+ memset(&info_in, 0, sizeof(info_in));
+ CU_ASSERT_FATAL(0 == odp_ipsec_sa_info(sa_in, &info_in));
+ CU_ASSERT(0 == info_in.inbound.antireplay_window_top);
+
+ ipsec_test_part test_out = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ESP,
+ },
+ },
+ };
+ ipsec_test_part test_in = {
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ },
+ },
+ };
+
+ ipsec_check_out_in_one(&test_out, &test_in, sa_out, sa_in, NULL);
+
+ memset(&info_out, 0, sizeof(info_out));
+ CU_ASSERT_FATAL(0 == odp_ipsec_sa_info(sa_out, &info_out));
+ CU_ASSERT(1 == info_out.outbound.seq_num);
+
+ memset(&info_in, 0, sizeof(info_in));
+ CU_ASSERT_FATAL(0 == odp_ipsec_sa_info(sa_in, &info_in));
+ CU_ASSERT(1 == info_in.inbound.antireplay_window_top);
+
+ ipsec_sa_destroy(sa_out);
+ ipsec_sa_destroy(sa_in);
+
+ /*
+ * Additional check for SA lookup parameters. Let's use transport
+ * mode SA and ODP_IPSEC_DSTADD_SPI lookup mode.
+ */
+ ipsec_sa_param_fill(&param_in,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP, 123, NULL,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+ param_in.inbound.lookup_mode = ODP_IPSEC_LOOKUP_DSTADDR_SPI;
+ param_in.inbound.lookup_param.ip_version = ODP_IPSEC_IPV4;
+ param_in.inbound.lookup_param.dst_addr = &dst;
+ sa_in = odp_ipsec_sa_create(&param_in);
+ CU_ASSERT_FATAL(sa_in != ODP_IPSEC_SA_INVALID);
+
+ memset(&info_in, 0, sizeof(info_in));
+ CU_ASSERT_FATAL(odp_ipsec_sa_info(sa_in, &info_in) == 0);
+
+ CU_ASSERT(info_in.param.inbound.lookup_mode ==
+ ODP_IPSEC_LOOKUP_DSTADDR_SPI);
+ CU_ASSERT_FATAL(info_in.param.inbound.lookup_param.dst_addr ==
+ &info_in.inbound.lookup_param.dst_addr);
+ CU_ASSERT(!memcmp(info_in.param.inbound.lookup_param.dst_addr,
+ &dst,
+ ODP_IPV4_ADDR_SIZE));
+ ipsec_sa_destroy(sa_in);
+}
+
+static void test_test_sa_update_seq_num(void)
+{
+ ipsec_test_flags flags;
+
+ memset(&flags, 0, sizeof(flags));
+ flags.display_algo = true;
+ flags.part_flags.test_sa_seq_num = true;
+
+ test_out_in_all(&flags);
+
+ printf("\n ");
+}
+
+#define SOFT_LIMIT_PKT_CNT 1024
+#define HARD_LIMIT_PKT_CNT 2048
+#define DELTA_PKT_CNT 320
+
+static void test_out_ipv4_esp_sa_expiry(enum ipsec_test_sa_expiry expiry)
+{
+ int byte_count_per_packet = pkt_ipv4_icmp_0.len - pkt_ipv4_icmp_0.l3_offset;
+ uint32_t src = IPV4ADDR(10, 0, 11, 2);
+ uint32_t dst = IPV4ADDR(10, 0, 22, 2);
+ odp_ipsec_tunnel_param_t out_tunnel;
+ odp_ipsec_sa_param_t param_out;
+ int i, inc, limit, delta;
+ uint64_t soft_limit_byte;
+ uint64_t hard_limit_byte;
+ uint64_t soft_limit_pkt;
+ uint64_t hard_limit_pkt;
+ odp_ipsec_sa_t out_sa;
+
+ switch (expiry) {
+ case IPSEC_TEST_EXPIRY_SOFT_PKT:
+ soft_limit_pkt = SOFT_LIMIT_PKT_CNT;
+ hard_limit_pkt = HARD_LIMIT_PKT_CNT;
+ soft_limit_byte = 0;
+ hard_limit_byte = 0;
+ delta = DELTA_PKT_CNT;
+ limit = soft_limit_pkt;
+ inc = 1;
+ break;
+ case IPSEC_TEST_EXPIRY_HARD_PKT:
+ soft_limit_pkt = SOFT_LIMIT_PKT_CNT;
+ hard_limit_pkt = HARD_LIMIT_PKT_CNT;
+ soft_limit_byte = 0;
+ hard_limit_byte = 0;
+ delta = DELTA_PKT_CNT;
+ limit = hard_limit_pkt;
+ inc = 1;
+ break;
+ case IPSEC_TEST_EXPIRY_SOFT_BYTE:
+ soft_limit_pkt = 0;
+ hard_limit_pkt = 0;
+ soft_limit_byte = byte_count_per_packet * SOFT_LIMIT_PKT_CNT;
+ hard_limit_byte = byte_count_per_packet * HARD_LIMIT_PKT_CNT;
+ delta = byte_count_per_packet * DELTA_PKT_CNT;
+ limit = soft_limit_byte;
+ inc = byte_count_per_packet;
+ break;
+ case IPSEC_TEST_EXPIRY_HARD_BYTE:
+ soft_limit_pkt = 0;
+ hard_limit_pkt = 0;
+ soft_limit_byte = byte_count_per_packet * SOFT_LIMIT_PKT_CNT;
+ hard_limit_byte = byte_count_per_packet * HARD_LIMIT_PKT_CNT;
+ delta = byte_count_per_packet * DELTA_PKT_CNT;
+ limit = hard_limit_byte;
+ inc = byte_count_per_packet;
+ break;
+ default:
+ return;
+ }
+
+ memset(&out_tunnel, 0, sizeof(odp_ipsec_tunnel_param_t));
+
+ out_tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ out_tunnel.ipv4.src_addr = &src;
+ out_tunnel.ipv4.dst_addr = &dst;
+
+ ipsec_sa_param_fill(&param_out, ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ 0x4a2cbfe7, &out_tunnel,
+ ODP_CIPHER_ALG_AES_CBC, &key_a5_128,
+ ODP_AUTH_ALG_SHA1_HMAC, &key_5a_160,
+ NULL, NULL);
+
+ param_out.lifetime.soft_limit.bytes = soft_limit_byte;
+ param_out.lifetime.hard_limit.bytes = hard_limit_byte;
+ param_out.lifetime.soft_limit.packets = soft_limit_pkt;
+ param_out.lifetime.hard_limit.packets = hard_limit_pkt;
+
+ out_sa = odp_ipsec_sa_create(&param_out);
+ CU_ASSERT_FATAL(ODP_IPSEC_SA_INVALID != out_sa);
+
+ ipsec_test_part test_out = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ESP,
+ },
+ },
+ };
+
+ test_out.out[0].sa_expiry = IPSEC_TEST_EXPIRY_IGNORED;
+
+ for (i = 0; i < limit - delta; i += inc)
+ ipsec_check_out_one(&test_out, out_sa);
+
+ sa_expiry_notified = false;
+ test_out.out[0].sa_expiry = expiry;
+
+ for (; i <= limit && !sa_expiry_notified; i += inc)
+ ipsec_check_out_one(&test_out, out_sa);
+
+ CU_ASSERT(sa_expiry_notified);
+
+ for (; i <= limit + delta; i += inc)
+ ipsec_check_out_one(&test_out, out_sa);
+
+ ipsec_sa_destroy(out_sa);
+}
+
+static void test_out_ipv4_esp_sa_pkt_expiry(void)
+{
+ printf("\n IPv4 IPsec SA packet soft expiry");
+ test_out_ipv4_esp_sa_expiry(IPSEC_TEST_EXPIRY_SOFT_PKT);
+
+ printf("\n IPv4 IPsec SA packet hard expiry");
+ test_out_ipv4_esp_sa_expiry(IPSEC_TEST_EXPIRY_HARD_PKT);
+
+ printf("\n");
+}
+
+static void test_out_ipv4_esp_sa_byte_expiry(void)
+{
+ printf("\n IPv4 IPsec SA byte soft expiry");
+ test_out_ipv4_esp_sa_expiry(IPSEC_TEST_EXPIRY_SOFT_BYTE);
+
+ printf("\n IPv4 IPsec SA byte hard expiry");
+ test_out_ipv4_esp_sa_expiry(IPSEC_TEST_EXPIRY_HARD_BYTE);
+
+ printf("\n");
+}
+
+static void ipsec_test_capability(void)
+{
+ odp_ipsec_capability_t capa;
+
+ CU_ASSERT(odp_ipsec_capability(&capa) == 0);
+}
+
+static void test_defaults(uint8_t fill)
+{
+ odp_ipsec_config_t config;
+ odp_ipsec_sa_param_t sa_param;
+
+ memset(&config, fill, sizeof(config));
+ odp_ipsec_config_init(&config);
+ CU_ASSERT(config.inbound.lookup.min_spi == 0);
+ CU_ASSERT(config.inbound.lookup.max_spi == UINT32_MAX);
+ CU_ASSERT(config.inbound.lookup.spi_overlap == 0);
+ CU_ASSERT(config.inbound.retain_outer == ODP_PROTO_LAYER_NONE);
+ CU_ASSERT(config.inbound.parse_level == ODP_PROTO_LAYER_NONE);
+ CU_ASSERT(config.inbound.chksums.all_chksum == 0);
+ CU_ASSERT(!config.inbound.reassembly.en_ipv4);
+ CU_ASSERT(!config.inbound.reassembly.en_ipv6);
+ CU_ASSERT(config.inbound.reassembly.max_wait_time == 0);
+ CU_ASSERT(config.inbound.reassembly.max_num_frags == 2);
+ CU_ASSERT(!config.inbound.reass_async);
+ CU_ASSERT(!config.inbound.reass_inline);
+ CU_ASSERT(config.outbound.all_chksum == 0);
+ CU_ASSERT(!config.stats_en);
+ CU_ASSERT(!config.vector.enable);
+
+ memset(&sa_param, fill, sizeof(sa_param));
+ odp_ipsec_sa_param_init(&sa_param);
+ CU_ASSERT(sa_param.proto == ODP_IPSEC_ESP);
+ CU_ASSERT(sa_param.crypto.cipher_alg == ODP_CIPHER_ALG_NULL);
+ CU_ASSERT(sa_param.crypto.auth_alg == ODP_AUTH_ALG_NULL);
+ CU_ASSERT(sa_param.crypto.icv_len == 0);
+ CU_ASSERT(sa_param.opt.esn == 0);
+ CU_ASSERT(sa_param.opt.udp_encap == 0);
+ CU_ASSERT(sa_param.opt.copy_dscp == 0);
+ CU_ASSERT(sa_param.opt.copy_flabel == 0);
+ CU_ASSERT(sa_param.opt.copy_df == 0);
+ CU_ASSERT(sa_param.opt.dec_ttl == 0);
+ CU_ASSERT(sa_param.lifetime.soft_limit.bytes == 0);
+ CU_ASSERT(sa_param.lifetime.soft_limit.packets == 0);
+ CU_ASSERT(sa_param.lifetime.hard_limit.bytes == 0);
+ CU_ASSERT(sa_param.lifetime.hard_limit.packets == 0);
+ CU_ASSERT(sa_param.context == NULL);
+ CU_ASSERT(sa_param.context_len == 0);
+ CU_ASSERT(sa_param.inbound.lookup_mode == ODP_IPSEC_LOOKUP_DISABLED);
+ CU_ASSERT(sa_param.inbound.antireplay_ws == 0);
+ CU_ASSERT(sa_param.inbound.pipeline == ODP_IPSEC_PIPELINE_NONE);
+ CU_ASSERT(!sa_param.inbound.reassembly_en);
+ CU_ASSERT(sa_param.outbound.tunnel.type == ODP_IPSEC_TUNNEL_IPV4);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv4.dscp == 0);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv4.df == 0);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv4.ttl == 255);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv6.flabel == 0);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv6.dscp == 0);
+ CU_ASSERT(sa_param.outbound.tunnel.ipv6.hlimit == 255);
+ CU_ASSERT(sa_param.outbound.frag_mode == ODP_IPSEC_FRAG_DISABLED);
+}
+
+static void ipsec_test_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void test_ipsec_stats(void)
+{
+ ipsec_test_flags flags;
+
+ memset(&flags, 0, sizeof(flags));
+
+ printf("\n Stats : success");
+ flags.stats = IPSEC_TEST_STATS_SUCCESS;
+ test_out_in_all(&flags);
+
+ printf("\n Stats : proto err");
+ flags.stats = IPSEC_TEST_STATS_PROTO_ERR;
+ test_out_in_all(&flags);
+
+ printf("\n Stats : auth err");
+ flags.stats = IPSEC_TEST_STATS_AUTH_ERR;
+ test_out_in_all(&flags);
+
+ printf("\n ");
+}
+
+static void test_udp_encap(void)
+{
+ ipsec_test_flags flags;
+
+ memset(&flags, 0, sizeof(flags));
+ flags.udp_encap = 1;
+ flags.tunnel = 0;
+
+ printf("\n IPv4 Transport");
+ flags.v6 = 0;
+ test_esp_out_in_all(&flags);
+
+ printf("\n IPv6 Transport");
+ flags.v6 = 1;
+ test_esp_out_in_all(&flags);
+
+ flags.tunnel = 1;
+
+ printf("\n IPv4-in-IPv4 Tunnel");
+ flags.v6 = 0;
+ flags.tunnel_is_v6 = 0;
+ test_esp_out_in_all(&flags);
+
+ printf("\n IPv4-in-IPv6 Tunnel");
+ flags.v6 = 0;
+ flags.tunnel_is_v6 = 1;
+ test_esp_out_in_all(&flags);
+
+ printf("\n IPv6-in-IPv4 Tunnel");
+ flags.v6 = 1;
+ flags.tunnel_is_v6 = 0;
+ test_esp_out_in_all(&flags);
+
+ printf("\n IPv6-in-IPv6 Tunnel");
+ flags.v6 = 1;
+ flags.tunnel_is_v6 = 1;
+ test_esp_out_in_all(&flags);
+
+ printf("\n ");
+}
+
+static void test_max_num_sa(void)
+{
+ odp_ipsec_capability_t capa;
+ uint32_t sa_pairs;
+ odp_bool_t odd = false;
+ uint32_t n;
+ uint8_t cipher_key_data[128 / 8]; /* 128 bit key for AES */
+ uint8_t auth_key_data[160 / 8]; /* 160 bit key for SHA-1 */
+ odp_crypto_key_t cipher_key;
+ odp_crypto_key_t auth_key;
+ uint32_t tun_src;
+ uint32_t tun_dst;
+ odp_ipsec_tunnel_param_t tun = {
+ .type = ODP_IPSEC_TUNNEL_IPV4,
+ .ipv4.src_addr = &tun_src,
+ .ipv4.dst_addr = &tun_dst,
+ .ipv4.ttl = 64,
+ };
+ odp_ipsec_sa_param_t param;
+ const uint32_t spi_start = 256;
+ odp_ipsec_sa_t sa_odd = ODP_IPSEC_SA_INVALID;
+ ipsec_test_part test_out = {
+ .pkt_in = &pkt_ipv4_icmp_0,
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ESP,
+ },
+ },
+ };
+ ipsec_test_part test_in = {
+ .flags = {
+ /* Test lookup now that we have lots of SAs */
+ .lookup = 1,
+ },
+ .num_pkt = 1,
+ .out = {
+ { .status.warn.all = 0,
+ .status.error.all = 0,
+ .l3_type = ODP_PROTO_L3_TYPE_IPV4,
+ .l4_type = ODP_PROTO_L4_TYPE_ICMPV4,
+ .pkt_res = &pkt_ipv4_icmp_0 },
+ },
+ };
+
+ CU_ASSERT_FATAL(odp_ipsec_capability(&capa) == 0);
+ sa_pairs = capa.max_num_sa / 2;
+ if (capa.max_num_sa > 2 && capa.max_num_sa % 2)
+ odd = true;
+
+ odp_ipsec_sa_t sa_out[sa_pairs];
+ odp_ipsec_sa_t sa_in[sa_pairs];
+
+ memset(cipher_key_data, 0xa5, sizeof(cipher_key_data));
+ cipher_key.data = cipher_key_data;
+ cipher_key.length = sizeof(cipher_key_data);
+
+ memset(auth_key_data, 0x5a, sizeof(auth_key_data));
+ auth_key.data = auth_key_data;
+ auth_key.length = sizeof(auth_key_data);
+
+ for (n = 0; n < sa_pairs; n++) {
+ /* Make keys unique */
+ if (cipher_key.length > sizeof(n))
+ memcpy(cipher_key.data, &n, sizeof(n));
+ if (auth_key.length > sizeof(n))
+ memcpy(auth_key.data, &n, sizeof(n));
+
+ /* These are for outbound SAs only */
+ tun_src = 0x0a000000 + n;
+ tun_dst = 0x0a800000 + n;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ spi_start + n, &tun,
+ ODP_CIPHER_ALG_AES_CBC, &cipher_key,
+ ODP_AUTH_ALG_SHA1_HMAC, &auth_key,
+ NULL, NULL);
+ sa_out[n] = odp_ipsec_sa_create(&param);
+ CU_ASSERT_FATAL(sa_out[n] != ODP_IPSEC_SA_INVALID);
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_INBOUND, ODP_IPSEC_ESP,
+ spi_start + n, &tun,
+ ODP_CIPHER_ALG_AES_CBC, &cipher_key,
+ ODP_AUTH_ALG_SHA1_HMAC, &auth_key,
+ NULL, NULL);
+ sa_in[n] = odp_ipsec_sa_create(&param);
+ CU_ASSERT_FATAL(sa_in[n] != ODP_IPSEC_SA_INVALID);
+ }
+
+ n = sa_pairs - 1;
+ if (odd) {
+ /*
+ * We have an odd number of max SAs. Let's create a similar
+ * SA as the last created outbound SA and test it against
+ * the last created inbound SA.
+ */
+ tun_src = 0x0a000000 + n;
+ tun_dst = 0x0a800000 + n;
+
+ ipsec_sa_param_fill(&param,
+ ODP_IPSEC_DIR_OUTBOUND, ODP_IPSEC_ESP,
+ spi_start + n, &tun,
+ ODP_CIPHER_ALG_AES_CBC, &cipher_key,
+ ODP_AUTH_ALG_SHA1_HMAC, &auth_key,
+ NULL, NULL);
+ sa_odd = odp_ipsec_sa_create(&param);
+ CU_ASSERT_FATAL(sa_odd != ODP_IPSEC_SA_INVALID);
+
+ ipsec_check_out_in_one(&test_out, &test_in,
+ sa_odd, sa_in[n], NULL);
+ }
+
+ for (n = 0; n < sa_pairs; n++)
+ ipsec_check_out_in_one(&test_out, &test_in,
+ sa_out[n], sa_in[n], NULL);
+
+ for (n = 0; n < sa_pairs; n++) {
+ ipsec_sa_destroy(sa_out[n]);
+ ipsec_sa_destroy(sa_in[n]);
+ }
+ if (odd)
+ ipsec_sa_destroy(sa_odd);
+}
+
+odp_testinfo_t ipsec_out_suite[] = {
+ ODP_TEST_INFO(ipsec_test_capability),
+ ODP_TEST_INFO(ipsec_test_default_values),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_ah_sha256,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_ah_sha256_tun_ipv4,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_ah_sha256_tun_ipv6,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_null_sha256_tun_ipv4,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_null_sha256_tun_ipv6,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_udp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_ah_sha256_frag_check,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_ah_sha256_frag_check_2,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_null_sha256_frag_check,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_null_sha256_frag_check_2,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_ah_sha256,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_ah_sha256_tun_ipv4,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_ah_sha256_tun_ipv6,
+ ipsec_check_ah_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_esp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_esp_null_sha256_tun_ipv4,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_esp_null_sha256_tun_ipv6,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv6_esp_udp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_dummy_esp_null_sha256_tun_ipv4,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_dummy_esp_null_sha256_tun_ipv6,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_udp_esp_null_sha256,
+ ipsec_check_esp_null_sha256),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_null_aes_xcbc,
+ ipsec_check_esp_null_aes_xcbc),
+ ODP_TEST_INFO_CONDITIONAL(test_sa_info,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_sa_pkt_expiry,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_CONDITIONAL(test_out_ipv4_esp_sa_byte_expiry,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_CONDITIONAL(test_test_sa_update_seq_num,
+ ipsec_check_test_sa_update_seq_num),
+ ODP_TEST_INFO(test_esp_out_in_all_basic),
+ ODP_TEST_INFO_CONDITIONAL(test_inline_hdr_in_packet,
+ is_out_mode_inline),
+ ODP_TEST_INFO(test_ah_out_in_all_basic),
+ ODP_TEST_INFO(test_ipsec_stats),
+ ODP_TEST_INFO(test_udp_encap),
+ ODP_TEST_INFO_CONDITIONAL(test_max_num_sa,
+ ipsec_check_esp_aes_cbc_128_sha1),
+ ODP_TEST_INFO_NULL,
+};
diff --git a/test/validation/api/ipsec/reass_test_vectors.c b/test/validation/api/ipsec/reass_test_vectors.c
new file mode 100644
index 000000000..45dd7af1b
--- /dev/null
+++ b/test/validation/api/ipsec/reass_test_vectors.c
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+#include "ipsec.h"
+
+/* The source file includes below test vectors */
+
+/* IPv6:
+ *
+ * 1) pkt_ipv6_udp_p1
+ * pkt_ipv6_udp_p1_f1
+ * pkt_ipv6_udp_p1_f2
+ *
+ * 2) pkt_ipv6_udp_p2
+ * pkt_ipv6_udp_p2_f1
+ * pkt_ipv6_udp_p2_f2
+ * pkt_ipv6_udp_p2_f3
+ * pkt_ipv6_udp_p2_f4
+ */
+
+/* IPv4:
+ *
+ * 1) pkt_ipv4_udp_p1
+ * pkt_ipv4_udp_p1_f1
+ * pkt_ipv4_udp_p1_f2
+ *
+ * 2) pkt_ipv4_udp_p2
+ * pkt_ipv4_udp_p2_f1
+ * pkt_ipv4_udp_p2_f2
+ * pkt_ipv4_udp_p2_f3
+ * pkt_ipv4_udp_p2_f4
+ */
+
+ipsec_test_packet pkt_ipv6_udp_p1 = {
+ .len = 1514,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x05, 0xb4, 0x11, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x05, 0xb4, 0x2b, 0xe8,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p1_f1 = {
+ .len = 1398,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x05, 0x40, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x00, 0x01, 0x5c, 0x92, 0xac, 0xf1,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x05, 0xb4, 0x2b, 0xe8,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p1_f2 = {
+ .len = 186,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x84, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x05, 0x38, 0x5c, 0x92, 0xac, 0xf1,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p2 = {
+ .len = 4496,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x11, 0x5a, 0x11, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x11, 0x5a, 0x8a, 0x11,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p2_f1 = {
+ .len = 1398,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x05, 0x40, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x00, 0x01, 0x64, 0x6c, 0x68, 0x9f,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x11, 0x5a, 0x8a, 0x11,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p2_f2 = {
+ .len = 1398,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x05, 0x40, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x05, 0x39, 0x64, 0x6c, 0x68, 0x9f,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p2_f3 = {
+ .len = 1398,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x05, 0x40, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x0a, 0x71, 0x64, 0x6c, 0x68, 0x9f,
+ },
+};
+
+ipsec_test_packet pkt_ipv6_udp_p2_f4 = {
+ .len = 496,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x01, 0xba, 0x2c, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02,
+ 0x11, 0x00, 0x0f, 0xa8, 0x64, 0x6c, 0x68, 0x9f,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p1 = {
+ .len = 1514,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x05, 0xdc, 0x00, 0x01, 0x00, 0x00,
+ 0x40, 0x11, 0x66, 0x0d, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x05, 0xc8, 0xb8, 0x4c,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p1_f1 = {
+ .len = 1434,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x05, 0x8c, 0x00, 0x01, 0x20, 0x00,
+ 0x40, 0x11, 0x46, 0x5d, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x05, 0xc8, 0xb8, 0x4c,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p1_f2 = {
+ .len = 114,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x64, 0x00, 0x01, 0x00, 0xaf,
+ 0x40, 0x11, 0x6a, 0xd6, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p2 = {
+ .len = 4496,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x11, 0x82, 0x00, 0x02, 0x00, 0x00,
+ 0x40, 0x11, 0x5a, 0x66, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x11, 0x6e, 0x16, 0x76,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p2_f1 = {
+ .len = 1434,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x05, 0x8c, 0x00, 0x02, 0x20, 0x00,
+ 0x40, 0x11, 0x46, 0x5c, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+
+ /* UDP */
+ 0x08, 0x00, 0x27, 0x10, 0x11, 0x6e, 0x16, 0x76,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p2_f2 = {
+ .len = 1434,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x05, 0x8c, 0x00, 0x02, 0x20, 0xaf,
+ 0x40, 0x11, 0x45, 0xad, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p2_f3 = {
+ .len = 1434,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x05, 0x8c, 0x00, 0x02, 0x21, 0x5e,
+ 0x40, 0x11, 0x44, 0xfe, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+ },
+};
+
+ipsec_test_packet pkt_ipv4_udp_p2_f4 = {
+ .len = 296,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x01, 0x1a, 0x00, 0x02, 0x02, 0x0d,
+ 0x40, 0x11, 0x68, 0xc1, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02,
+ },
+};
diff --git a/test/validation/api/ipsec/reass_test_vectors.h b/test/validation/api/ipsec/reass_test_vectors.h
new file mode 100644
index 000000000..4c8d8e1f4
--- /dev/null
+++ b/test/validation/api/ipsec/reass_test_vectors.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Marvell
+ */
+
+#ifndef _ODP_REASS_TEST_VECTORS_H_
+#define _ODP_REASS_TEST_VECTORS_H_
+
+extern ipsec_test_packet pkt_ipv6_udp_p1;
+extern ipsec_test_packet pkt_ipv6_udp_p1_f1;
+extern ipsec_test_packet pkt_ipv6_udp_p1_f2;
+extern ipsec_test_packet pkt_ipv6_udp_p2;
+extern ipsec_test_packet pkt_ipv6_udp_p2_f1;
+extern ipsec_test_packet pkt_ipv6_udp_p2_f2;
+extern ipsec_test_packet pkt_ipv6_udp_p2_f3;
+extern ipsec_test_packet pkt_ipv6_udp_p2_f4;
+
+extern ipsec_test_packet pkt_ipv4_udp_p1;
+extern ipsec_test_packet pkt_ipv4_udp_p1_f1;
+extern ipsec_test_packet pkt_ipv4_udp_p1_f2;
+extern ipsec_test_packet pkt_ipv4_udp_p2;
+extern ipsec_test_packet pkt_ipv4_udp_p2_f1;
+extern ipsec_test_packet pkt_ipv4_udp_p2_f2;
+extern ipsec_test_packet pkt_ipv4_udp_p2_f3;
+extern ipsec_test_packet pkt_ipv4_udp_p2_f4;
+
+static inline void
+test_vector_payload_populate(ipsec_test_packet *pkt, odp_bool_t first_frag)
+{
+ uint32_t i = pkt->l4_offset;
+
+ /* For non-fragmented packets and first frag, skip 8 bytes from
+ * l4_offset for UDP header */
+
+ if (first_frag)
+ i += 8;
+
+ for (; i < pkt->len; i++)
+ pkt->data[i] = 0x58;
+}
+
+static inline void
+reass_test_vectors_init(void)
+{
+ test_vector_payload_populate(&pkt_ipv6_udp_p1, true);
+ test_vector_payload_populate(&pkt_ipv6_udp_p1_f1, true);
+ test_vector_payload_populate(&pkt_ipv6_udp_p1_f2, false);
+
+ test_vector_payload_populate(&pkt_ipv6_udp_p2, true);
+ test_vector_payload_populate(&pkt_ipv6_udp_p2_f1, true);
+ test_vector_payload_populate(&pkt_ipv6_udp_p2_f2, false);
+ test_vector_payload_populate(&pkt_ipv6_udp_p2_f3, false);
+ test_vector_payload_populate(&pkt_ipv6_udp_p2_f4, false);
+
+ test_vector_payload_populate(&pkt_ipv4_udp_p1, true);
+ test_vector_payload_populate(&pkt_ipv4_udp_p1_f1, true);
+ test_vector_payload_populate(&pkt_ipv4_udp_p1_f2, false);
+
+ test_vector_payload_populate(&pkt_ipv4_udp_p2, true);
+ test_vector_payload_populate(&pkt_ipv4_udp_p2_f1, true);
+ test_vector_payload_populate(&pkt_ipv4_udp_p2_f2, false);
+ test_vector_payload_populate(&pkt_ipv4_udp_p2_f3, false);
+ test_vector_payload_populate(&pkt_ipv4_udp_p2_f4, false);
+}
+
+#endif
diff --git a/test/validation/api/ipsec/test_vectors.h b/test/validation/api/ipsec/test_vectors.h
new file mode 100644
index 000000000..b02912f68
--- /dev/null
+++ b/test/validation/api/ipsec/test_vectors.h
@@ -0,0 +1,2166 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef _ODP_TEST_IPSEC_VECTORS_H_
+#define _ODP_TEST_IPSEC_VECTORS_H_
+
+#define KEY(name, ...) \
+ static uint8_t name ## _data[] = { __VA_ARGS__ }; \
+ static const ODP_UNUSED odp_crypto_key_t name = { \
+ .data = name ## _data, \
+ .length = sizeof(name ## _data), \
+ }
+
+KEY(key_a5_128, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5);
+KEY(key_5a_128, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a);
+KEY(key_5a_160, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a);
+KEY(key_a5_192, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5);
+KEY(key_a5_256, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5);
+KEY(key_5a_256, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a);
+KEY(key_5a_384, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a);
+KEY(key_5a_512, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a);
+
+KEY(key_rfc3602, 0x90, 0xd3, 0x82, 0xb4, 0x10, 0xee, 0xba, 0x7a,
+ 0xd9, 0x38, 0xc4, 0x6c, 0xec, 0x1a, 0x82, 0xbf);
+KEY(key_rfc3602_2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef);
+KEY(key_mcgrew_gcm_2, 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08);
+KEY(key_mcgrew_gcm_salt_2, 0xca, 0xfe, 0xba, 0xbe);
+KEY(key_mcgrew_gcm_3, 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab,
+ 0xab, 0xbc, 0xcd, 0xde, 0xf0, 0x01, 0x12, 0x23,
+ 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab);
+KEY(key_mcgrew_gcm_salt_3, 0x11, 0x22, 0x33, 0x44);
+KEY(key_mcgrew_gcm_4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+KEY(key_mcgrew_gcm_salt_4, 0x00, 0x00, 0x00, 0x00);
+KEY(key_mcgrew_gcm_12, 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25,
+ 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47);
+KEY(key_mcgrew_gcm_salt_12, 0xd9, 0x66, 0x42, 0x67);
+KEY(key_mcgrew_gcm_15, 0x4c, 0x80, 0xcd, 0xef, 0xbb, 0x5d, 0x10, 0xda,
+ 0x90, 0x6a, 0xc7, 0x3c, 0x36, 0x13, 0xa6, 0x34);
+KEY(key_mcgrew_gcm_salt_15, 0x22, 0x43, 0x3c, 0x64);
+KEY(key_rfc7634, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f);
+KEY(key_rfc7634_salt, 0xa0, 0xa1, 0xa2, 0xa3);
+KEY(key_3byte_salt, 0x01, 0x02, 0x03);
+KEY(key_auth_aes_xcbc_128, 0x61, 0x31, 0x62, 0x32, 0x63, 0x33, 0x64, 0x34,
+ 0x65, 0x35, 0x66, 0x36, 0x67, 0x37, 0x68, 0x38);
+
+/* DES keys have parity bits so that each byte has odd parity */
+KEY(key_des_64, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4);
+KEY(key_des_192, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4);
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0 = {
+ .len = 142,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ipip = {
+ .len = 162,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x04, 0x19, 0x62, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* Inner IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ah_sha256_1 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xd9, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x6c, 0x2e, 0xf7, 0x1f, 0x7c, 0x70, 0x39, 0xa3,
+ 0x4a, 0x77, 0x01, 0x47, 0x9e, 0x45, 0x73, 0x51,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_ah_tun_ipv4_sha256_1 = {
+ .len = 190,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0x19, 0x17, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* AH */
+ 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0xd5, 0x35, 0x9b, 0x21, 0xe6, 0x14, 0x9b, 0x42,
+ 0x1f, 0x00, 0xfa, 0x36, 0x73, 0x4c, 0x53, 0xcf,
+
+ /* Inner IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_ah_tun_ipv6_sha256_1 = {
+ .len = 214,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x33, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* AH */
+ 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x2b, 0x45, 0xbe, 0xd2, 0x9c, 0x9c, 0x3e, 0x0d,
+ 0xe0, 0x32, 0xaf, 0xa0, 0x2d, 0x26, 0xe1, 0x91,
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* Inner IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ah_sha256_1_bad1 = {
+ .len = 168,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xdb, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x6c, 0x2e, 0xf7, 0x1f, 0x7c, 0x70, 0x39, 0xa3,
+ 0x4a, 0x77, 0x01, 0x47, 0x9e, 0x45, 0x73, 0x51,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ah_sha256_1_bad2 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xd9, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x6c, 0x2e, 0xf7, 0x1f, 0x7c, 0x70, 0x39, 0xa3,
+ 0x4a, 0x77, 0x01, 0x47, 0x9e, 0x45, 0x73, 0x51,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5d,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ah_sha256_1235 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xd9, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x12, 0x35,
+ 0x04, 0xef, 0x71, 0x73, 0xa1, 0xd4, 0x71, 0x3f,
+ 0xd6, 0x78, 0xfe, 0xa2, 0x59, 0xe9, 0x93, 0x70,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37,
+
+ /* ICMP echo */
+ 0x12, 0x34, 0x00, 0x00,
+
+ /* data */
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_esp_null_sha256_1 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xda, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x01,
+
+ /* ICV */
+ 0xe9, 0x81, 0xcd, 0x65, 0x9b, 0x25, 0x0b, 0x33,
+ 0xe2, 0xf3, 0x83, 0xf1, 0x6d, 0x14, 0xb4, 0x1f,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_tun_ipv4_null_sha256_1 = {
+ .len = 190,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0x19, 0x18, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* Inner IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x04,
+
+ /* ICV */
+ 0x73, 0x8d, 0xf6, 0x9a, 0x26, 0x06, 0x4d, 0xa1,
+ 0x88, 0x37, 0x65, 0xab, 0x0d, 0xe9, 0x95, 0x3b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_tun_ipv6_null_sha256_1 = {
+ .len = 210,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x32, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* Inner IP */
+ 0x45, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0xac, 0x27, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x04,
+
+ /* ICV */
+ 0x73, 0x8d, 0xf6, 0x9a, 0x26, 0x06, 0x4d, 0xa1,
+ 0x88, 0x37, 0x65, 0xab, 0x0d, 0xe9, 0x95, 0x3b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_udp_null_sha256_1 = {
+ .len = 178,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x11, 0xab, 0xf3, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* UDP encap */
+ 0x11, 0x94, 0x11, 0x94, 0x00, 0x90, 0x00, 0x00,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x01,
+
+ /* ICV */
+ 0xe9, 0x81, 0xcd, 0x65, 0x9b, 0x25, 0x0b, 0x33,
+ 0xe2, 0xf3, 0x83, 0xf1, 0x6d, 0x14, 0xb4, 0x1f,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_null_sha256_1_bad1 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xda, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x01,
+
+ /* ICV */
+ 0x18, 0x00, 0x14, 0x3a, 0x54, 0x72, 0x98, 0xe8,
+ 0xc7, 0x2d, 0xfa, 0xeb, 0x70, 0xe0, 0x24, 0xdf,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_null_sha256_1235 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xda, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x12, 0x35,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x01,
+
+ /* ICV */
+ 0x2f, 0xfb, 0xdd, 0x9d, 0xc0, 0xca, 0xb8, 0x0a,
+ 0xaa, 0xf1, 0x59, 0x31, 0x4e, 0xef, 0x62, 0x50,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_esp_aes_cbc_null_1 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xda, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x96, 0xfa, 0x74, 0x56, 0x78, 0xe4, 0xbb, 0x0c,
+ 0x9e, 0x6e, 0x4a, 0xeb, 0x44, 0xd9, 0xf2, 0xe6,
+
+ /* data */
+ 0x2f, 0xb3, 0xa6, 0xfe, 0x2c, 0x2e, 0xce, 0x65,
+ 0x3a, 0x57, 0xe3, 0x09, 0x5d, 0x66, 0x36, 0x32,
+ 0xb1, 0xc2, 0x59, 0x58, 0xb6, 0xe5, 0x9e, 0xa2,
+ 0x07, 0xf8, 0x26, 0x4a, 0x64, 0xf5, 0x16, 0x01,
+ 0x51, 0x8e, 0xe5, 0x4b, 0x07, 0x2c, 0x4b, 0x23,
+ 0xfa, 0x4e, 0x6e, 0xdb, 0x35, 0xc7, 0x1d, 0x30,
+ 0x42, 0xd9, 0x0f, 0xba, 0x8a, 0x69, 0x7e, 0x29,
+ 0xe7, 0xbd, 0x15, 0xe9, 0x35, 0x9e, 0x81, 0xe7,
+ 0x9e, 0xc9, 0x7d, 0x66, 0x99, 0x58, 0xec, 0x45,
+ 0x29, 0xd0, 0xa4, 0xfd, 0xf1, 0xe7, 0x5b, 0x3e,
+ 0x2a, 0x77, 0x1d, 0x8f, 0x2b, 0x73, 0xba, 0xf8,
+ 0x72, 0xd2, 0xa0, 0x0b, 0x90, 0xb9, 0x73, 0x9c,
+ 0xde, 0x3c, 0xc3, 0xb8, 0x91, 0x97, 0xc4, 0x28,
+ 0xfa, 0x6d, 0xa8, 0x41, 0xb6, 0x83, 0xc8, 0xaa,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_aes_cbc_sha1_1 = {
+ .len = 182,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xce, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x17, 0xc3, 0xfa, 0xaf, 0x1d, 0xeb, 0x94, 0x06,
+ 0x4e, 0xf8, 0x62, 0xb4, 0x1f, 0xa0, 0x17, 0x62,
+
+ /* data */
+ 0xba, 0xf3, 0xfb, 0x10, 0x86, 0xee, 0x80, 0x6f,
+ 0x44, 0xff, 0x94, 0x7f, 0xee, 0xd8, 0x50, 0x62,
+ 0x40, 0x3f, 0x7c, 0x76, 0xb4, 0x65, 0xca, 0x32,
+ 0x91, 0x0e, 0xba, 0xf2, 0xc1, 0x9d, 0x3b, 0xcb,
+ 0x0f, 0xc9, 0xc9, 0xae, 0x33, 0x42, 0x16, 0x36,
+ 0xd3, 0xc8, 0x6c, 0x23, 0xac, 0xbf, 0x98, 0xf2,
+ 0xda, 0x10, 0x95, 0xbc, 0xe8, 0x38, 0xbf, 0x4b,
+ 0x19, 0xd0, 0x58, 0x67, 0xd9, 0xab, 0xd0, 0xf5,
+ 0x59, 0xc9, 0xdc, 0xbb, 0x46, 0xcc, 0x34, 0x26,
+ 0xe6, 0xd6, 0xee, 0x5c, 0xc8, 0xe2, 0x46, 0xc9,
+ 0x14, 0xe9, 0x98, 0xe4, 0xb9, 0xec, 0xf0, 0xa7,
+ 0x12, 0x94, 0x54, 0x4e, 0x56, 0xfd, 0xe8, 0x07,
+ 0xd8, 0x83, 0xf9, 0x78, 0x5f, 0xa6, 0x1a, 0xce,
+ 0xbb, 0xda, 0xbc, 0x7c, 0xd8, 0xb6, 0x7b, 0x4f,
+
+ /* ICV */
+ 0x78, 0x4e, 0xfe, 0xbd, 0x42, 0x7f, 0x42, 0x96,
+ 0x65, 0xe7, 0x60, 0x2f,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_aes_cbc_sha256_1 = {
+ .len = 186,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xca, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x96, 0xfa, 0x74, 0x56, 0x78, 0xe4, 0xbb, 0x0c,
+ 0x9e, 0x6e, 0x4a, 0xeb, 0x44, 0xd9, 0xf2, 0xe6,
+
+ /* data */
+ 0x2f, 0xb3, 0xa6, 0xfe, 0x2c, 0x2e, 0xce, 0x65,
+ 0x3a, 0x57, 0xe3, 0x09, 0x5d, 0x66, 0x36, 0x32,
+ 0xb1, 0xc2, 0x59, 0x58, 0xb6, 0xe5, 0x9e, 0xa2,
+ 0x07, 0xf8, 0x26, 0x4a, 0x64, 0xf5, 0x16, 0x01,
+ 0x51, 0x8e, 0xe5, 0x4b, 0x07, 0x2c, 0x4b, 0x23,
+ 0xfa, 0x4e, 0x6e, 0xdb, 0x35, 0xc7, 0x1d, 0x30,
+ 0x42, 0xd9, 0x0f, 0xba, 0x8a, 0x69, 0x7e, 0x29,
+ 0xe7, 0xbd, 0x15, 0xe9, 0x35, 0x9e, 0x81, 0xe7,
+ 0x9e, 0xc9, 0x7d, 0x66, 0x99, 0x58, 0xec, 0x45,
+ 0x29, 0xd0, 0xa4, 0xfd, 0xf1, 0xe7, 0x5b, 0x3e,
+ 0x2a, 0x77, 0x1d, 0x8f, 0x2b, 0x73, 0xba, 0xf8,
+ 0x72, 0xd2, 0xa0, 0x0b, 0x90, 0xb9, 0x73, 0x9c,
+ 0xde, 0x3c, 0xc3, 0xb8, 0x91, 0x97, 0xc4, 0x28,
+ 0xfa, 0x6d, 0xa8, 0x41, 0xb6, 0x83, 0xc8, 0xaa,
+
+ /* IV */
+ 0x8a, 0x39, 0x10, 0x07, 0x02, 0x97, 0xbb, 0x1c,
+ 0x59, 0xb7, 0x70, 0x33, 0xa4, 0x26, 0xa2, 0xb8
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_aes_cbc_sha384_1 = {
+ .len = 194,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xc2, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0xcd, 0xc7, 0x8d, 0x99, 0xf7, 0x65, 0x21, 0xf6,
+ 0x40, 0xe3, 0x4c, 0x5e, 0x90, 0x84, 0x4c, 0xf3,
+
+ /* data */
+ 0xb5, 0x9c, 0xa2, 0x3d, 0xb6, 0x09, 0x4f, 0x40,
+ 0x73, 0x4a, 0x33, 0x12, 0x90, 0xb2, 0xf1, 0x24,
+ 0x1f, 0xd3, 0xa3, 0x89, 0x53, 0x12, 0xb0, 0x98,
+ 0x6e, 0xec, 0xde, 0xb8, 0xf2, 0xbb, 0xe0, 0x03,
+ 0xee, 0x86, 0x1c, 0x2c, 0xe2, 0x12, 0x26, 0x89,
+ 0x4d, 0x8a, 0x6a, 0x89, 0xd0, 0x31, 0x68, 0x66,
+ 0xe8, 0x14, 0xe7, 0xd7, 0xaa, 0xd8, 0x2a, 0x61,
+ 0x03, 0x62, 0xb7, 0x46, 0x8e, 0x98, 0xa7, 0xfd,
+ 0x96, 0xe7, 0xbb, 0x5d, 0xf0, 0xc7, 0x42, 0xe1,
+ 0xef, 0x96, 0x1c, 0x79, 0xc0, 0xa4, 0x60, 0x69,
+ 0x2c, 0xc8, 0x02, 0x1f, 0xf4, 0xbf, 0x8f, 0xa4,
+ 0x0e, 0xb5, 0x35, 0xca, 0x51, 0x23, 0xc5, 0x62,
+ 0x13, 0x54, 0xbb, 0xcb, 0x2a, 0x4a, 0xdd, 0x79,
+ 0x32, 0x9f, 0x72, 0xa6, 0xeb, 0xe9, 0x04, 0x61,
+
+ /* ICV */
+ 0x79, 0xbc, 0xb6, 0x2d, 0xcc, 0x14, 0xc8, 0xea,
+ 0xfa, 0x5b, 0x57, 0x8d, 0x0a, 0xec, 0x56, 0xb7,
+ 0xca, 0xb2, 0x38, 0x9b, 0x05, 0x79, 0xf8, 0xdd,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_aes_cbc_sha512_1 = {
+ .len = 202,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xba, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0xf9, 0x6b, 0x50, 0xa9, 0x7b, 0x4e, 0xc9, 0xdf,
+ 0x70, 0x29, 0xe2, 0x76, 0xdc, 0x12, 0x1e, 0x8f,
+
+ /* data */
+ 0x9a, 0x7c, 0x5b, 0x96, 0xcd, 0xcb, 0x76, 0x07,
+ 0xf3, 0xb0, 0x86, 0x31, 0xa4, 0xf0, 0xa3, 0xdb,
+ 0xb6, 0x08, 0x46, 0xd4, 0xb2, 0x2c, 0x15, 0x86,
+ 0xdf, 0x4e, 0xb9, 0xd2, 0x75, 0xb5, 0x18, 0x30,
+ 0x25, 0x15, 0x38, 0xbb, 0xbd, 0x17, 0x8b, 0x01,
+ 0xc6, 0xc4, 0x14, 0xe8, 0xe7, 0xc2, 0xc7, 0x63,
+ 0x70, 0x4d, 0xcb, 0x02, 0x95, 0x68, 0x36, 0x85,
+ 0x11, 0x66, 0x76, 0xa0, 0x73, 0xd4, 0xa9, 0x1c,
+ 0x33, 0xff, 0xe6, 0x04, 0x80, 0x47, 0x6d, 0xa4,
+ 0x63, 0x1a, 0x15, 0x89, 0x57, 0xb7, 0x39, 0x4f,
+ 0x61, 0x71, 0x8f, 0x4b, 0xaf, 0x3c, 0x31, 0x0d,
+ 0x9b, 0x1a, 0xea, 0x21, 0x38, 0xb8, 0x64, 0x89,
+ 0x96, 0x76, 0xc7, 0xd2, 0xfc, 0x8e, 0x36, 0x02,
+ 0x35, 0xfe, 0xde, 0x40, 0xc7, 0xd8, 0x60, 0x8d,
+
+ /* ICV */
+ 0xe8, 0x66, 0x6b, 0xb7, 0x4f, 0xb2, 0xa5, 0x08,
+ 0xf1, 0x76, 0x82, 0xa9, 0x3e, 0xed, 0x39, 0xac,
+ 0x17, 0x8f, 0xa8, 0xfe, 0x58, 0x4d, 0x40, 0xed,
+ 0xfe, 0xd9, 0x35, 0x60, 0x13, 0xb5, 0x20, 0xf8,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_esp_aes_ctr_null_1 = {
+ .len = 162,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xe2, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* data */
+ 0x39, 0xab, 0xe5, 0xae, 0x74, 0x57, 0x76, 0x7f,
+ 0x1d, 0x1f, 0xce, 0xe8, 0xca, 0xf1, 0x87, 0xf5,
+ 0xfd, 0x9e, 0x1d, 0x20, 0x38, 0x30, 0x8a, 0xe5,
+ 0xb9, 0x55, 0x80, 0x7b, 0xfd, 0x9d, 0xb9, 0x99,
+ 0x85, 0xcd, 0xb5, 0x30, 0x86, 0xaa, 0xe1, 0x7a,
+ 0x69, 0xe5, 0xfa, 0x38, 0xf3, 0x0f, 0x91, 0x18,
+ 0x75, 0x7b, 0x5f, 0x4e, 0x69, 0x17, 0xaa, 0xe7,
+ 0x84, 0x6c, 0x40, 0x31, 0xec, 0x87, 0x4c, 0x8c,
+ 0xb3, 0xb4, 0x9f, 0x7e, 0xea, 0x83, 0x6f, 0xc6,
+ 0x11, 0xd5, 0xce, 0xbe, 0x65, 0x37, 0x1c, 0xb6,
+ 0xd3, 0xcb, 0x51, 0xa8, 0xa4, 0x0e, 0x3e, 0xe6,
+ 0x26, 0xd8, 0x17, 0xec, 0x8b, 0xca, 0x79, 0x96,
+ 0xa0, 0xcd, 0x6f, 0xdd, 0x9e, 0xe9, 0x6a, 0xc0,
+ 0xf2, 0x6c, 0xdb, 0xfd, 0x99, 0xa2, 0xb5, 0xbf,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_icmp_0_ah_aes_gmac_128_1 = {
+ .len = 178,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0xab, 0xd1, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* AH */
+ 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd6, 0x0e, 0xcc, 0x22, 0x31, 0x79, 0x59, 0x72,
+ 0x68, 0xc9, 0x58, 0xfb, 0x8b, 0xb0, 0xbb, 0xd5,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv4_icmp_0_esp_null_aes_gmac_128_1 = {
+ .len = 178,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0xab, 0xd2, 0xc0, 0xa8, 0x6f, 0x02,
+ 0xc0, 0xa8, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x01,
+
+ /* ICV */
+ 0x16, 0x0e, 0xa6, 0x8f, 0xb3, 0xa6, 0x8c, 0x74,
+ 0x19, 0x59, 0x72, 0x80, 0x91, 0x98, 0x77, 0x5e,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv6_icmp_0 = {
+ .len = 170,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv6_icmp_0_ah_sha256_1 = {
+ .len = 202,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* AH */
+ 0x3a, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0xd9, 0x14, 0x87, 0x27, 0x20, 0x1a, 0xc2, 0x66,
+ 0xc1, 0xca, 0x99, 0x2b, 0x8a, 0xae, 0x2f, 0x27,
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv6_icmp_0_ah_tun_ipv4_sha256_1 = {
+ .len = 218,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x33, 0x18, 0xfb, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* AH */
+ 0x29, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x7f, 0xde, 0x8a, 0x48, 0xc5, 0xc5, 0xfa, 0x52,
+ 0xb8, 0xf6, 0xc2, 0xe3, 0x8f, 0x10, 0xb2, 0x47,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv6_icmp_0_ah_tun_ipv6_sha256_1 = {
+ .len = 242,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x33, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* AH */
+ 0x29, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x62, 0x96, 0x2b, 0x40, 0x3e, 0x53, 0x76, 0x4a,
+ 0x4d, 0x7f, 0xf6, 0x22, 0x35, 0x3c, 0x74, 0xe2,
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv6_icmp_0_esp_null_sha256_1 = {
+ .len = 198,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x3a,
+
+ /* ICV */
+ 0x20, 0xa6, 0x89, 0x7b, 0x0a, 0x52, 0x5b, 0xca,
+ 0x98, 0x56, 0xd1, 0xfe, 0x56, 0xc7, 0xa4, 0x5b,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv6_icmp_0_esp_tun_ipv4_null_sha256_1 = {
+ .len = 218,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0x18, 0xfc, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x29,
+
+ /* ICV */
+ 0xd0, 0x96, 0x6e, 0xda, 0xc5, 0x08, 0xcc, 0x0e,
+ 0xd1, 0x22, 0xa5, 0xed, 0x13, 0x07, 0xd9, 0xcd,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv6_icmp_0_esp_tun_ipv6_null_sha256_1 = {
+ .len = 238,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 54,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x32, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x3a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x29,
+
+ /* ICV */
+ 0xd0, 0x96, 0x6e, 0xda, 0xc5, 0x08, 0xcc, 0x0e,
+ 0xd1, 0x22, 0xa5, 0xed, 0x13, 0x07, 0xd9, 0xcd,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_rfc3602_5 = {
+ .len = 98,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x54, 0x08, 0xf2, 0x00, 0x00,
+ 0x40, 0x01, 0xf9, 0xfe, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0x64,
+
+ /* ICMP */
+ 0x08, 0x00, 0x0e, 0xbd, 0xa7, 0x0a, 0x00, 0x00,
+ 0x8e, 0x9c, 0x08, 0x3d, 0xb9, 0x5b, 0x07, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_rfc3602_5_esp = {
+ .len = 138,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x7c, 0x08, 0xf2, 0x00, 0x00,
+ 0x40, 0x32, 0xf9, 0xa5, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0x64,
+
+ /* ESP */
+ 0x00, 0x00, 0x43, 0x21, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0xe9, 0x6e, 0x8c, 0x08, 0xab, 0x46, 0x57, 0x63,
+ 0xfd, 0x09, 0x8d, 0x45, 0xdd, 0x3f, 0xf8, 0x93,
+
+ /* data */
+ 0xf6, 0x63, 0xc2, 0x5d, 0x32, 0x5c, 0x18, 0xc6,
+ 0xa9, 0x45, 0x3e, 0x19, 0x4e, 0x12, 0x08, 0x49,
+ 0xa4, 0x87, 0x0b, 0x66, 0xcc, 0x6b, 0x99, 0x65,
+ 0x33, 0x00, 0x13, 0xb4, 0x89, 0x8d, 0xc8, 0x56,
+ 0xa4, 0x69, 0x9e, 0x52, 0x3a, 0x55, 0xdb, 0x08,
+ 0x0b, 0x59, 0xec, 0x3a, 0x8e, 0x4b, 0x7e, 0x52,
+ 0x77, 0x5b, 0x07, 0xd1, 0xdb, 0x34, 0xed, 0x9c,
+ 0x53, 0x8a, 0xb5, 0x0c, 0x55, 0x1b, 0x87, 0x4a,
+ 0xa2, 0x69, 0xad, 0xd0, 0x47, 0xad, 0x2d, 0x59,
+ 0x13, 0xac, 0x19, 0xb7, 0xcf, 0xba, 0xd4, 0xa6,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_6 = {
+ .len = 62,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x30, 0x08, 0xfe, 0x00, 0x00,
+ 0x40, 0x01, 0xfa, 0x16, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0x64,
+
+ /* ICMP */
+ 0x08, 0x00, 0xb5, 0xe8, 0xa8, 0x0a, 0x05, 0x00,
+ 0xa6, 0x9c, 0x08, 0x3d, 0x0b, 0x66, 0x0e, 0x00,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_6_esp = {
+ .len = 90,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x4c, 0x08, 0xfe, 0x00, 0x00,
+ 0x40, 0x32, 0xf9, 0xc9, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0x64,
+
+ /* ESP */
+ 0x00, 0x00, 0x43, 0x21, 0x00, 0x00, 0x00, 0x08,
+
+ /* IV */
+ 0x69, 0xd0, 0x8d, 0xf7, 0xd2, 0x03, 0x32, 0x9d,
+ 0xb0, 0x93, 0xfc, 0x49, 0x24, 0xe5, 0xbd, 0x80,
+
+ /* data */
+ 0xf5, 0x19, 0x95, 0x88, 0x1e, 0xc4, 0xe0, 0xc4,
+ 0x48, 0x89, 0x87, 0xce, 0x74, 0x2e, 0x81, 0x09,
+ 0x68, 0x9b, 0xb3, 0x79, 0xd2, 0xd7, 0x50, 0xc0,
+ 0xd9, 0x15, 0xdc, 0xa3, 0x46, 0xa8, 0x9f, 0x75,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_7 = {
+ .len = 98,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x54, 0x09, 0x04, 0x00, 0x00,
+ 0x40, 0x01, 0xf9, 0x88, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0xc8,
+
+ /* ICMP */
+ 0x08, 0x00, 0x9f, 0x76, 0xa9, 0x0a, 0x01, 0x00,
+ 0xb4, 0x9c, 0x08, 0x3d, 0x02, 0xa2, 0x04, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_7_esp = {
+ .len = 154,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x8c, 0x09, 0x05, 0x00, 0x00,
+ 0x40, 0x32, 0xf9, 0x1e, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0xc8,
+
+ /* ESP */
+ 0x00, 0x00, 0x87, 0x65, 0x00, 0x00, 0x00, 0x02,
+
+ /* IV */
+ 0xf4, 0xe7, 0x65, 0x24, 0x4f, 0x64, 0x07, 0xad,
+ 0xf1, 0x3d, 0xc1, 0x38, 0x0f, 0x67, 0x3f, 0x37,
+
+ /* data */
+ 0x77, 0x3b, 0x52, 0x41, 0xa4, 0xc4, 0x49, 0x22,
+ 0x5e, 0x4f, 0x3c, 0xe5, 0xed, 0x61, 0x1b, 0x0c,
+ 0x23, 0x7c, 0xa9, 0x6c, 0xf7, 0x4a, 0x93, 0x01,
+ 0x3c, 0x1b, 0x0e, 0xa1, 0xa0, 0xcf, 0x70, 0xf8,
+ 0xe4, 0xec, 0xae, 0xc7, 0x8a, 0xc5, 0x3a, 0xad,
+ 0x7a, 0x0f, 0x02, 0x2b, 0x85, 0x92, 0x43, 0xc6,
+ 0x47, 0x75, 0x2e, 0x94, 0xa8, 0x59, 0x35, 0x2b,
+ 0x8a, 0x4d, 0x4d, 0x2d, 0xec, 0xd1, 0x36, 0xe5,
+ 0xc1, 0x77, 0xf1, 0x32, 0xad, 0x3f, 0xbf, 0xb2,
+ 0x20, 0x1a, 0xc9, 0x90, 0x4c, 0x74, 0xee, 0x0a,
+ 0x10, 0x9e, 0x0c, 0xa1, 0xe4, 0xdf, 0xe9, 0xd5,
+ 0xa1, 0x00, 0xb8, 0x42, 0xf1, 0xc2, 0x2f, 0x0d,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_8 = {
+ .len = 82,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x44, 0x09, 0x0c, 0x00, 0x00,
+ 0x40, 0x01, 0xf9, 0x90, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0xc8,
+
+ /* ICMP */
+ 0x08, 0x00, 0xd6, 0x3c, 0xaa, 0x0a, 0x02, 0x00,
+ 0xc6, 0x9c, 0x08, 0x3d, 0xa3, 0xde, 0x03, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+};
+
+static const ipsec_test_packet pkt_rfc3602_8_esp = {
+ .len = 138,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x7c, 0x09, 0x0d, 0x00, 0x00,
+ 0x40, 0x32, 0xf9, 0x26, 0xc0, 0xa8, 0x7b, 0x03,
+ 0xc0, 0xa8, 0x7b, 0xc8,
+
+ /* ESP */
+ 0x00, 0x00, 0x87, 0x65, 0x00, 0x00, 0x00, 0x05,
+
+ /* IV */
+ 0x85, 0xd4, 0x72, 0x24, 0xb5, 0xf3, 0xdd, 0x5d,
+ 0x21, 0x01, 0xd4, 0xea, 0x8d, 0xff, 0xab, 0x22,
+
+ /* data */
+ 0x15, 0xb9, 0x26, 0x83, 0x81, 0x95, 0x96, 0xa8,
+ 0x04, 0x72, 0x32, 0xcc, 0x00, 0xf7, 0x04, 0x8f,
+ 0xe4, 0x53, 0x18, 0xe1, 0x1f, 0x8a, 0x0f, 0x62,
+ 0xed, 0xe3, 0xc3, 0xfc, 0x61, 0x20, 0x3b, 0xb5,
+ 0x0f, 0x98, 0x0a, 0x08, 0xc9, 0x84, 0x3f, 0xd3,
+ 0xa1, 0xb0, 0x6d, 0x5c, 0x07, 0xff, 0x96, 0x39,
+ 0xb7, 0xeb, 0x7d, 0xfb, 0x35, 0x12, 0xe5, 0xde,
+ 0x43, 0x5e, 0x72, 0x07, 0xed, 0x97, 0x1e, 0xf3,
+ 0xd2, 0x72, 0x6d, 0x9b, 0x5e, 0xf6, 0xaf, 0xfc,
+ 0x6d, 0x17, 0xa0, 0xde, 0xcb, 0xb1, 0x38, 0x92,
+ },
+};
+
+/*
+ * Several tests from draft-mcgrew-gcm-test-01. It was never completed as an
+ * RFC, but serves good purpopse anyway.
+ *
+ * Note: plaintext texts also contain ESP trailers, which we
+ * do not include here into plaintext packets.
+ */
+static const ipsec_test_packet pkt_mcgrew_gcm_test_2 = {
+ .len = 76,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x3e, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x11, 0x4d, 0xcc, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* UDP */
+ 0x0a, 0x98, 0x00, 0x35, 0x00, 0x2a, 0x23, 0x43,
+ 0xb2, 0xd0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x73, 0x69, 0x70,
+ 0x09, 0x63, 0x79, 0x62, 0x65, 0x72, 0x63, 0x69,
+ 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00, 0x00, 0x01,
+ 0x00, 0x01,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_2_esp = {
+ .len = 130,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x74, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0x75, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a,
+
+ /* IV */
+ 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+
+ /* Data */
+ 0xde, 0xb2, 0x2c, 0xd9, 0xb0, 0x7c, 0x72, 0xc1,
+ 0x6e, 0x3a, 0x65, 0xbe, 0xeb, 0x8d, 0xf3, 0x04,
+ 0xa5, 0xa5, 0x89, 0x7d, 0x33, 0xae, 0x53, 0x0f,
+ 0x1b, 0xa7, 0x6d, 0x5d, 0x11, 0x4d, 0x2a, 0x5c,
+ 0x3d, 0xe8, 0x18, 0x27, 0xc1, 0x0e, 0x9a, 0x4f,
+ 0x51, 0x33, 0x0d, 0x0e, 0xec, 0x41, 0x66, 0x42,
+ 0xcf, 0xbb, 0x85, 0xa5, 0xb4, 0x7e, 0x48, 0xa4,
+ 0xec, 0x3b, 0x9b, 0xa9, 0x5d, 0x91, 0x8b, 0xd4,
+ 0x26, 0xf8, 0x39, 0x1b, 0x99, 0x27, 0xd0, 0xfc,
+ 0xc9, 0x84, 0x56, 0x1b, 0xbb, 0xce, 0x9f, 0xc0,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_3 = {
+ .len = 62,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x30, 0x69, 0xa6, 0x40, 0x00,
+ 0x80, 0x06, 0x26, 0x90, 0xc0, 0xa8, 0x01, 0x02,
+ 0x93, 0x89, 0x15, 0x5e,
+
+ /* TCP */
+ 0x0a, 0x9e, 0x00, 0x8b, 0x2d, 0xc5, 0x7e, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x02, 0x40, 0x00,
+ 0x20, 0xbf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x01, 0x01, 0x04, 0x02,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_3_esp = {
+ .len = 118,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x68, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0x81, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x4a, 0x2c, 0xbf, 0xe3, 0x00, 0x00, 0x00, 0x02,
+
+ /* IV */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+
+ /* Data */
+ 0xff, 0x42, 0x5c, 0x9b, 0x72, 0x45, 0x99, 0xdf,
+ 0x7a, 0x3b, 0xcd, 0x51, 0x01, 0x94, 0xe0, 0x0d,
+ 0x6a, 0x78, 0x10, 0x7f, 0x1b, 0x0b, 0x1c, 0xbf,
+ 0x06, 0xef, 0xae, 0x9d, 0x65, 0xa5, 0xd7, 0x63,
+ 0x74, 0x8a, 0x63, 0x79, 0x85, 0x77, 0x1d, 0x34,
+ 0x7f, 0x05, 0x45, 0x65, 0x9f, 0x14, 0xe9, 0x9d,
+ 0xef, 0x84, 0x2d, 0x8b, 0x42, 0xf5, 0x64, 0xf5,
+ 0x2d, 0xfd, 0xd6, 0xee, 0xf4, 0xf9, 0x2e, 0xad,
+ 0xba, 0xc2, 0x39, 0x90,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_4 = {
+ .len = 74,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x3c, 0x99, 0xc5, 0x00, 0x00,
+ 0x80, 0x01, 0xcb, 0x7a, 0x40, 0x67, 0x93, 0x18,
+ 0x01, 0x01, 0x01, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0x07, 0x5c, 0x02, 0x00, 0x44, 0x00,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_4_esp = {
+ .len = 130,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x74, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0x75, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+
+ /* IV */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* Data */
+ 0x46, 0x88, 0xda, 0xf2, 0xf9, 0x73, 0xa3, 0x92,
+ 0x73, 0x29, 0x09, 0xc3, 0x31, 0xd5, 0x6d, 0x60,
+ 0xf6, 0x94, 0xab, 0xaa, 0x41, 0x4b, 0x5e, 0x7f,
+ 0xf5, 0xfd, 0xcd, 0xff, 0xf5, 0xe9, 0xa2, 0x84,
+ 0x45, 0x64, 0x76, 0x49, 0x27, 0x19, 0xff, 0xb6,
+ 0x4d, 0xe7, 0xd9, 0xdc, 0xa1, 0xe1, 0xd8, 0x94,
+ 0xbc, 0x3b, 0xd5, 0x78, 0x73, 0xed, 0x4d, 0x18,
+ 0x1d, 0x19, 0xd4, 0xd5, 0xc8, 0xc1, 0x8a, 0xf6,
+ 0xfe, 0x1d, 0x73, 0x72, 0x22, 0x8a, 0x69, 0xf4,
+ 0x0d, 0xeb, 0x37, 0x3d, 0xdc, 0x01, 0x67, 0x6b,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_12 = {
+ .len = 14,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = ODP_PACKET_OFFSET_INVALID,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_12_notun = {
+ .len = 34,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x14, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x3b, 0x4d, 0xcc, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_12_esp = {
+ .len = 70,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x38, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0xb2, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x33, 0x54, 0x67, 0xae, 0xff, 0xff, 0xff, 0xff,
+
+ /* IV */
+ 0x43, 0x45, 0x7e, 0x91, 0x82, 0x44, 0x3b, 0xc6,
+
+ /* Data */
+ 0x43, 0x7f, 0x86, 0x51, 0x7e, 0xa5, 0x95, 0xd2,
+ 0xca, 0x00, 0x4c, 0x33, 0x38, 0x8c, 0x46, 0x77,
+ 0x0c, 0x59, 0x0a, 0xd6,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_15 = {
+ .len = 62,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x30, 0xda, 0x3a, 0x00, 0x00,
+ 0x80, 0x01, 0xdf, 0x3b, 0xc0, 0xa8, 0x00, 0x05,
+ 0xc0, 0xa8, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xc6, 0xcd, 0x02, 0x00, 0x07, 0x00,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x71, 0x72, 0x73, 0x74,
+ },
+};
+
+static const ipsec_test_packet pkt_mcgrew_gcm_test_15_esp = {
+ .len = 118,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP - not a part of RFC, added for simplicity */
+ 0x45, 0x00, 0x00, 0x68, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0x81, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x00, 0x00, 0x43, 0x21, 0x00, 0x00, 0x00, 0x07,
+
+ /* IV */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x30, 0xda, 0x3a, 0x00, 0x00,
+ 0x80, 0x01, 0xdf, 0x3b, 0xc0, 0xa8, 0x00, 0x05,
+ 0xc0, 0xa8, 0x00, 0x01, 0x08, 0x00, 0xc6, 0xcd,
+ 0x02, 0x00, 0x07, 0x00, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x01, 0x02, 0x02, 0x04, 0x59, 0x4f, 0x40, 0x55,
+ 0x42, 0x8d, 0x39, 0x9a, 0x9d, 0x66, 0xc1, 0x5e,
+ 0x77, 0x02, 0x3a, 0x98,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_rfc7634 = {
+ .len = 98,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x54, 0xa6, 0xf2, 0x00, 0x00,
+ 0x40, 0x01, 0xe7, 0x78, 0xc6, 0x33, 0x64, 0x05,
+ 0xc0, 0x00, 0x02, 0x05,
+
+ /* ICMP */
+ 0x08, 0x00, 0x5b, 0x7a, 0x3a, 0x08, 0x00, 0x00,
+ 0x55, 0x3b, 0xec, 0x10, 0x00, 0x07, 0x36, 0x27,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet pkt_ipv4_rfc7634_esp = {
+ .len = 154,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x8c, 0x23, 0x45, 0x00, 0x00,
+ 0x40, 0x32, 0xde, 0x5b, 0xcb, 0x00, 0x71, 0x99,
+ 0xcb, 0x00, 0x71, 0x05,
+
+ /* ESP */
+ 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x05,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x24, 0x03, 0x94, 0x28, 0xb9, 0x7f, 0x41, 0x7e,
+ 0x3c, 0x13, 0x75, 0x3a, 0x4f, 0x05, 0x08, 0x7b,
+ 0x67, 0xc3, 0x52, 0xe6, 0xa7, 0xfa, 0xb1, 0xb9,
+ 0x82, 0xd4, 0x66, 0xef, 0x40, 0x7a, 0xe5, 0xc6,
+ 0x14, 0xee, 0x80, 0x99, 0xd5, 0x28, 0x44, 0xeb,
+ 0x61, 0xaa, 0x95, 0xdf, 0xab, 0x4c, 0x02, 0xf7,
+ 0x2a, 0xa7, 0x1e, 0x7c, 0x4c, 0x4f, 0x64, 0xc9,
+ 0xbe, 0xfe, 0x2f, 0xac, 0xc6, 0x38, 0xe8, 0xf3,
+ 0xcb, 0xec, 0x16, 0x3f, 0xac, 0x46, 0x9b, 0x50,
+ 0x27, 0x73, 0xf6, 0xfb, 0x94, 0xe6, 0x64, 0xda,
+ 0x91, 0x65, 0xb8, 0x28, 0x29, 0xf6, 0x41, 0xe0,
+ 0x76, 0xaa, 0xa8, 0x26, 0x6b, 0x7f, 0xb0, 0xf7,
+ 0xb1, 0x1b, 0x36, 0x99, 0x07, 0xe1, 0xad, 0x43,
+ },
+};
+
+static const ODP_UNUSED ipsec_test_packet
+ pkt_ipv6_icmp_0_esp_udp_null_sha256_1 = {
+ .len = 206,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 62,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x86, 0xdd,
+
+ /* IP v6 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x40,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x11, 0x43, 0xff, 0xfe, 0x4a, 0xd7, 0x0a,
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+
+ /* Hop-by-Hop */
+ 0x11, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ /* UDP encap */
+ 0x11, 0x94, 0x11, 0x94, 0x00, 0x90, 0x00, 0x00,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* ICMP */
+ 0x08, 0x00, 0xfb, 0x37, 0x12, 0x34, 0x00, 0x00,
+ 0xba, 0xbe, 0x01, 0x23, 0x45, 0x67, 0xca, 0xfe,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b,
+
+ /* ESP TRL */
+ 0x01, 0x02, 0x02, 0x3a,
+
+ /* ICV */
+ 0x20, 0xa6, 0x89, 0x7b, 0x0a, 0x52, 0x5b, 0xca,
+ 0x98, 0x56, 0xd1, 0xfe, 0x56, 0xc7, 0xa4, 0x5b,
+ },
+};
+
+static const ipsec_test_packet pkt_test_empty = {
+ .len = 1,
+ .l2_offset = ODP_PACKET_OFFSET_INVALID,
+ .l3_offset = ODP_PACKET_OFFSET_INVALID,
+ .l4_offset = ODP_PACKET_OFFSET_INVALID,
+ .data = { 0 },
+};
+
+static const ipsec_test_packet pkt_test_nodata = {
+ .len = 14,
+ .l2_offset = 0,
+ .l3_offset = ODP_PACKET_OFFSET_INVALID,
+ .l4_offset = ODP_PACKET_OFFSET_INVALID,
+ .data = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ },
+};
+
+static const ipsec_test_packet pkt_ipv4_udp = {
+ .len = 76,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x3e, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* UDP */
+ 0x0a, 0x98, 0x00, 0x35, 0x00, 0x2a, 0x00, 0x00,
+ 0xb2, 0xd0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x73, 0x69, 0x70,
+ 0x09, 0x63, 0x79, 0x62, 0x65, 0x72, 0x63, 0x69,
+ 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00, 0x00, 0x01,
+ 0x00, 0x01,
+ },
+};
+
+static const ipsec_test_packet pkt_ipv4_udp_esp_null_sha256 = {
+ .len = 102,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH - not a part of RFC, added for simplicity */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x58, 0x69, 0x8f, 0x00, 0x00,
+ 0x80, 0x32, 0x4d, 0x91, 0xc0, 0xa8, 0x01, 0x02,
+ 0xc0, 0xa8, 0x01, 0x01,
+
+ /* ESP */
+ 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x01,
+
+ /* UDP */
+ 0x0a, 0x98, 0x00, 0x35, 0x00, 0x2a, 0x23, 0x43,
+ 0xb2, 0xd0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x73, 0x69, 0x70,
+ 0x09, 0x63, 0x79, 0x62, 0x65, 0x72, 0x63, 0x69,
+ 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00, 0x00, 0x01,
+ 0x00, 0x01,
+
+ /* ESP TRL */
+ 0x00, 0x11, 0x2d, 0x4a, 0x06, 0x9f, 0x97, 0xcf,
+ 0xa3, 0x05, 0xea, 0x90, 0x7a, 0xf6, 0x6b, 0x0a,
+ 0x3f, 0xc7,
+ },
+};
+
+static const ipsec_test_packet pkt_ipv4_null_aes_xcbc_esp = {
+ .len = 106,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x5c, 0x06, 0x00, 0x00, 0x00,
+ 0x40, 0x32, 0x13, 0x6c, 0x0a, 0x00, 0x6f, 0x02,
+ 0x0a, 0x00, 0xde, 0x02,
+
+ /* ESP */
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x2f, 0x49, 0x37, 0x00, 0x00,
+ 0x40, 0x11, 0x22, 0x84, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x08, 0x00,
+ 0x00, 0x1b, 0x6d, 0x99, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+
+ /* ESP trailer */
+ 0x01, 0x02, 0x03, 0x03, 0x04,
+
+ /* ICV */
+ 0xf1, 0x52, 0x64, 0xd1, 0x9b, 0x62, 0x24, 0xdd,
+ 0xcc, 0x14, 0xf5, 0xc1,
+ },
+};
+
+static const ipsec_test_packet pkt_ipv4_null_aes_xcbc_plain = {
+ .len = 61,
+ .l2_offset = 0,
+ .l3_offset = 14,
+ .l4_offset = 34,
+ .data = {
+ /* ETH */
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0x08, 0x00,
+
+ /* IP */
+ 0x45, 0x00, 0x00, 0x2f, 0x49, 0x37, 0x00, 0x00,
+ 0x40, 0x11, 0x22, 0x84, 0x0d, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x08, 0x00,
+ 0x00, 0x1b, 0x6d, 0x99, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ },
+};
+
+#endif
diff --git a/test/common_plat/validation/api/lock/.gitignore b/test/validation/api/lock/.gitignore
index ff16646f4..ff16646f4 100644
--- a/test/common_plat/validation/api/lock/.gitignore
+++ b/test/validation/api/lock/.gitignore
diff --git a/test/validation/api/lock/Makefile.am b/test/validation/api/lock/Makefile.am
new file mode 100644
index 000000000..ad75e1854
--- /dev/null
+++ b/test/validation/api/lock/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = lock_main
+lock_main_SOURCES = lock.c
diff --git a/test/validation/api/lock/lock.c b/test/validation/api/lock/lock.c
new file mode 100644
index 000000000..e8eb4360b
--- /dev/null
+++ b/test/validation/api/lock/lock.c
@@ -0,0 +1,1259 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+#include <malloc.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include <unistd.h>
+
+#define VERBOSE 0
+
+#define MAX_WORKERS 32
+#define MIN_ITERATIONS 1000
+#define MAX_ITERATIONS 30000
+#define ITER_MPLY_FACTOR 3
+
+#define SLOW_BARRIER_DELAY 400
+#define BASE_DELAY 6
+#define MIN_DELAY 1
+
+#define NUM_RESYNC_BARRIERS 100
+
+#define GLOBAL_SHM_NAME "GlobalLockTest"
+
+#define UNUSED __attribute__((__unused__))
+
+typedef __volatile uint32_t volatile_u32_t;
+typedef __volatile uint64_t volatile_u64_t;
+
+typedef struct {
+ odp_atomic_u32_t wait_cnt;
+} custom_barrier_t;
+
+typedef struct {
+ /* Global variables */
+ uint32_t g_num_threads;
+ uint32_t g_iterations;
+ uint32_t g_verbose;
+ uint32_t g_max_num_cores;
+
+ volatile_u32_t slow_thread_num;
+ volatile_u32_t barrier_cnt1;
+ volatile_u32_t barrier_cnt2;
+ odp_barrier_t global_barrier;
+
+ /* Used to periodically resync within the lock functional tests */
+ odp_barrier_t barrier_array[NUM_RESYNC_BARRIERS];
+
+ /* Locks */
+ odp_spinlock_t global_spinlock;
+ odp_spinlock_recursive_t global_recursive_spinlock;
+ odp_ticketlock_t global_ticketlock;
+ odp_rwlock_t global_rwlock;
+ odp_rwlock_recursive_t global_recursive_rwlock;
+
+ volatile_u32_t global_lock_owner;
+} global_shared_mem_t;
+
+/* Per-thread memory */
+typedef struct {
+ global_shared_mem_t *global_mem;
+
+ int thread_id;
+ int thread_core;
+
+ odp_spinlock_t per_thread_spinlock;
+ odp_spinlock_recursive_t per_thread_recursive_spinlock;
+ odp_ticketlock_t per_thread_ticketlock;
+ odp_rwlock_t per_thread_rwlock;
+ odp_rwlock_recursive_t per_thread_recursive_rwlock;
+
+ volatile_u64_t delay_counter;
+} per_thread_mem_t;
+
+static odp_shm_t global_shm;
+static global_shared_mem_t *global_mem;
+
+/*
+* Delay a consistent amount of time. Ideally the amount of CPU time taken
+* is linearly proportional to "iterations". The goal is to try to do some
+* work that the compiler optimizer won't optimize away, and also to
+* minimize loads and stores (at least to different memory addresses)
+* so as to not affect or be affected by caching issues. This does NOT have to
+* correlate to a specific number of cpu cycles or be consistent across
+* CPU architectures.
+*/
+static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations)
+{
+ volatile_u64_t *counter_ptr;
+ uint32_t cnt;
+
+ counter_ptr = &per_thread_mem->delay_counter;
+
+ for (cnt = 1; cnt <= iterations; cnt++)
+ (*counter_ptr)++;
+}
+
+/* Initialise per-thread memory */
+static per_thread_mem_t *thread_init(void)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_shm_t global_shm;
+ uint32_t per_thread_mem_len;
+
+ per_thread_mem_len = sizeof(per_thread_mem_t);
+ per_thread_mem = malloc(per_thread_mem_len);
+ memset(per_thread_mem, 0, per_thread_mem_len);
+
+ per_thread_mem->delay_counter = 1;
+
+ per_thread_mem->thread_id = odp_thread_id();
+ per_thread_mem->thread_core = odp_cpu_id();
+
+ global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ global_mem = odp_shm_addr(global_shm);
+ CU_ASSERT(global_mem != NULL);
+
+ per_thread_mem->global_mem = global_mem;
+
+ return per_thread_mem;
+}
+
+static void thread_finalize(per_thread_mem_t *per_thread_mem)
+{
+ free(per_thread_mem);
+}
+
+static void spinlock_api_test(odp_spinlock_t *spinlock)
+{
+ odp_spinlock_init(spinlock);
+ CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
+
+ odp_spinlock_lock(spinlock);
+ CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1);
+
+ odp_spinlock_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
+
+ CU_ASSERT(odp_spinlock_trylock(spinlock) == 1);
+
+ CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1);
+
+ odp_spinlock_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0);
+}
+
+static int spinlock_api_tests(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_spinlock_t local_spin_lock;
+
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ spinlock_api_test(&local_spin_lock);
+ spinlock_api_test(&per_thread_mem->per_thread_spinlock);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static void spinlock_recursive_api_test(odp_spinlock_recursive_t *spinlock)
+{
+ odp_spinlock_recursive_init(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
+
+ odp_spinlock_recursive_lock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ odp_spinlock_recursive_lock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ odp_spinlock_recursive_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ odp_spinlock_recursive_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
+
+ CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ odp_spinlock_recursive_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1);
+
+ odp_spinlock_recursive_unlock(spinlock);
+ CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0);
+}
+
+static int spinlock_recursive_api_tests(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_spinlock_recursive_t local_recursive_spin_lock;
+
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ spinlock_recursive_api_test(&local_recursive_spin_lock);
+ spinlock_recursive_api_test(
+ &per_thread_mem->per_thread_recursive_spinlock);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static void ticketlock_api_test(odp_ticketlock_t *ticketlock)
+{
+ odp_ticketlock_init(ticketlock);
+ CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
+
+ odp_ticketlock_lock(ticketlock);
+ CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1);
+
+ odp_ticketlock_unlock(ticketlock);
+ CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
+
+ CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 1);
+ CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 0);
+ CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1);
+
+ odp_ticketlock_unlock(ticketlock);
+ CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0);
+}
+
+static int ticketlock_api_tests(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_ticketlock_t local_ticket_lock;
+
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ ticketlock_api_test(&local_ticket_lock);
+ ticketlock_api_test(&per_thread_mem->per_thread_ticketlock);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static void rwlock_api_test(odp_rwlock_t *rw_lock)
+{
+ int rc = 0;
+
+ odp_rwlock_init(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
+
+ odp_rwlock_read_lock(rw_lock);
+
+ rc = odp_rwlock_read_trylock(rw_lock);
+ CU_ASSERT(rc != 0);
+ if (rc == 1)
+ odp_rwlock_read_unlock(rw_lock);
+
+ rc = odp_rwlock_write_trylock(rw_lock);
+ CU_ASSERT(rc == 0);
+ if (rc == 1)
+ odp_rwlock_write_unlock(rw_lock);
+
+ odp_rwlock_read_unlock(rw_lock);
+
+ rc = odp_rwlock_read_trylock(rw_lock);
+ CU_ASSERT(rc != 0);
+ if (rc == 1)
+ odp_rwlock_read_unlock(rw_lock);
+
+ odp_rwlock_write_lock(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */
+
+ rc = odp_rwlock_read_trylock(rw_lock);
+ CU_ASSERT(rc == 0);
+ if (rc == 1)
+ odp_rwlock_read_unlock(rw_lock);
+
+ rc = odp_rwlock_write_trylock(rw_lock);
+ CU_ASSERT(rc == 0);
+ if (rc == 1)
+ odp_rwlock_write_unlock(rw_lock);
+
+ odp_rwlock_write_unlock(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
+
+ rc = odp_rwlock_write_trylock(rw_lock);
+ CU_ASSERT(rc != 0);
+ if (rc == 1)
+ odp_rwlock_write_unlock(rw_lock);
+}
+
+static int rwlock_api_tests(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_rwlock_t local_rwlock;
+
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ rwlock_api_test(&local_rwlock);
+ rwlock_api_test(&per_thread_mem->per_thread_rwlock);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static void rwlock_recursive_api_test(odp_rwlock_recursive_t *rw_lock)
+{
+ int rc;
+
+ odp_rwlock_recursive_init(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
+
+ odp_rwlock_recursive_read_lock(rw_lock);
+ odp_rwlock_recursive_read_lock(rw_lock);
+ rc = odp_rwlock_recursive_read_trylock(rw_lock);
+ CU_ASSERT(rc == 1);
+ rc = odp_rwlock_recursive_write_trylock(rw_lock);
+ CU_ASSERT(rc == 0);
+
+ odp_rwlock_recursive_read_unlock(rw_lock);
+ odp_rwlock_recursive_read_unlock(rw_lock);
+ odp_rwlock_recursive_read_unlock(rw_lock);
+
+ odp_rwlock_recursive_write_lock(rw_lock);
+ odp_rwlock_recursive_write_lock(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */
+ rc = odp_rwlock_recursive_read_trylock(rw_lock);
+ CU_ASSERT(rc == 0);
+ rc = odp_rwlock_recursive_write_trylock(rw_lock);
+ CU_ASSERT(rc == 1);
+
+ odp_rwlock_recursive_write_unlock(rw_lock);
+ odp_rwlock_recursive_write_unlock(rw_lock);
+ odp_rwlock_recursive_write_unlock(rw_lock);
+ /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */
+}
+
+static int rwlock_recursive_api_tests(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ odp_rwlock_recursive_t local_recursive_rwlock;
+
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ rwlock_recursive_api_test(&local_recursive_rwlock);
+ rwlock_recursive_api_test(&per_thread_mem->per_thread_recursive_rwlock);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+/*
+ * Tests that we do have contention between threads when running.
+ * Also adjust the number of iterations to be done (by other tests)
+ * so we have a fair chance to see that the tested synchronizer
+ * does avoid the race condition.
+ */
+static int no_lock_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, current_errs, lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = 0;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = MAX_ITERATIONS / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ /*
+ * Tuning the iteration number:
+ * Here, we search for an iteration number that guarantees to show
+ * race conditions between the odp threads.
+ * Iterations is set to ITER_MPLY_FACTOR * cnt where cnt is when
+ * the threads start to see "errors" (i.e. effect of other threads
+ * running concurrentely without any synchronisation mechanism).
+ * In other words, "iterations" is set to ITER_MPLY_FACTOR times the
+ * minimum loop count necessary to see a need for synchronisation
+ * mechanism.
+ * If, later, these "errors" disappear when running other tests up to
+ * "iterations" with synchro, the effect of the tested synchro mechanism
+ * is likely proven.
+ * If we reach "MAX_ITERATIONS", and "iteration" remains zero,
+ * it means that we cannot see any race condition between the different
+ * running theads (e.g. the OS is not preemptive) and all other tests
+ * being passed won't tell much about the functionality of the
+ * tested synchro mechanism.
+ */
+ for (cnt = 1; cnt <= MAX_ITERATIONS; cnt++) {
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ if (!iterations)
+ iterations = cnt;
+ }
+
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ thread_delay(per_thread_mem, MIN_DELAY);
+
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ if (!iterations)
+ iterations = cnt;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if (global_mem->g_verbose)
+ printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures in %" PRIu32 " iterations\n",
+ thread_num,
+ per_thread_mem->thread_id,
+ per_thread_mem->thread_core,
+ sync_failures, iterations);
+
+ /* Note that the following CU_ASSERT MAY appear incorrect, but for the
+ * no_lock test it should see sync_failures or else there is something
+ * wrong with the test methodology or the ODP thread implementation.
+ * So this test PASSES only if it sees sync_failures or a single
+ * worker was used.
+ */
+ CU_ASSERT(sync_failures != 0 || global_mem->g_num_threads == 1);
+
+ /*
+ * set the iteration for the future tests to be far above the
+ * contention level
+ */
+ iterations *= ITER_MPLY_FACTOR;
+
+ if (iterations > MAX_ITERATIONS)
+ iterations = MAX_ITERATIONS;
+ if (iterations < MIN_ITERATIONS)
+ iterations = MIN_ITERATIONS;
+
+ /*
+ * Note that the following statement has race conditions:
+ * global_mem->g_iterations should really be an atomic and a TAS
+ * function be used. But this would mean that we would be testing
+ * synchronisers assuming synchronisers works...
+ * If we do not use atomic TAS, we may not get the grand max for
+ * all threads, but we are guaranteed to have passed the error
+ * threshold, for at least some threads, which is good enough
+ */
+ if (iterations > global_mem->g_iterations)
+ global_mem->g_iterations = iterations;
+
+ odp_mb_full();
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int spinlock_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, is_locked_errs, current_errs;
+ uint32_t lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = global_mem->g_iterations;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ is_locked_errs = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = iterations / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ for (cnt = 1; cnt <= iterations; cnt++) {
+ /* Acquire the shared global lock */
+ odp_spinlock_lock(&global_mem->global_spinlock);
+
+ /* Make sure we have the lock AND didn't previously own it */
+ if (odp_spinlock_is_locked(&global_mem->global_spinlock) != 1)
+ is_locked_errs++;
+
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Now set the global_lock_owner to be us, wait a while, and
+ * then we see if anyone else has snuck in and changed the
+ * global_lock_owner to be themselves
+ */
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Release shared lock, and make sure we no longer have it */
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ odp_spinlock_unlock(&global_mem->global_spinlock);
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if ((global_mem->g_verbose) &&
+ ((sync_failures != 0) || (is_locked_errs != 0)))
+ ODPH_ERR("Thread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures and %" PRIu32
+ " is_locked_errs in %" PRIu32
+ " iterations\n", thread_num,
+ per_thread_mem->thread_id, per_thread_mem->thread_core,
+ sync_failures, is_locked_errs, iterations);
+
+ CU_ASSERT(sync_failures == 0);
+ CU_ASSERT(is_locked_errs == 0);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int spinlock_recursive_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, recursive_errs, is_locked_errs, current_errs;
+ uint32_t lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = global_mem->g_iterations;
+
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ recursive_errs = 0;
+ is_locked_errs = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = iterations / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ for (cnt = 1; cnt <= iterations; cnt++) {
+ /* Acquire the shared global lock */
+ odp_spinlock_recursive_lock(
+ &global_mem->global_recursive_spinlock);
+
+ /* Make sure we have the lock AND didn't previously own it */
+ if (odp_spinlock_recursive_is_locked(
+ &global_mem->global_recursive_spinlock) != 1)
+ is_locked_errs++;
+
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Now set the global_lock_owner to be us, wait a while, and
+ * then we see if anyone else has snuck in and changed the
+ * global_lock_owner to be themselves
+ */
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Verify that we can acquire the lock recursively */
+ odp_spinlock_recursive_lock(
+ &global_mem->global_recursive_spinlock);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ recursive_errs++;
+ }
+
+ /* Release the lock and verify that we still have it*/
+ odp_spinlock_recursive_unlock(
+ &global_mem->global_recursive_spinlock);
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ recursive_errs++;
+ }
+
+ /* Release shared lock, and make sure we no longer have it */
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ odp_spinlock_recursive_unlock(
+ &global_mem->global_recursive_spinlock);
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if ((global_mem->g_verbose) &&
+ (sync_failures != 0 || recursive_errs != 0 || is_locked_errs != 0))
+ ODPH_ERR("Thread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures and %" PRIu32
+ " recursive_errs and %" PRIu32
+ " is_locked_errs in %" PRIu32
+ " iterations\n", thread_num,
+ per_thread_mem->thread_id, per_thread_mem->thread_core,
+ sync_failures, recursive_errs, is_locked_errs,
+ iterations);
+
+ CU_ASSERT(sync_failures == 0);
+ CU_ASSERT(recursive_errs == 0);
+ CU_ASSERT(is_locked_errs == 0);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int ticketlock_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, is_locked_errs, current_errs;
+ uint32_t lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = global_mem->g_iterations;
+
+ /* Wait here until all of the threads have also reached this point */
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ is_locked_errs = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = iterations / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ for (cnt = 1; cnt <= iterations; cnt++) {
+ /* Acquire the shared global lock */
+ odp_ticketlock_lock(&global_mem->global_ticketlock);
+
+ /* Make sure we have the lock AND didn't previously own it */
+ if (odp_ticketlock_is_locked(&global_mem->global_ticketlock)
+ != 1)
+ is_locked_errs++;
+
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Now set the global_lock_owner to be us, wait a while, and
+ * then we see if anyone else has snuck in and changed the
+ * global_lock_owner to be themselves
+ */
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Release shared lock, and make sure we no longer have it */
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ odp_ticketlock_unlock(&global_mem->global_ticketlock);
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and then rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if ((global_mem->g_verbose) &&
+ ((sync_failures != 0) || (is_locked_errs != 0)))
+ ODPH_ERR("Thread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures and %" PRIu32
+ " is_locked_errs in %" PRIu32 " iterations\n",
+ thread_num,
+ per_thread_mem->thread_id, per_thread_mem->thread_core,
+ sync_failures, is_locked_errs, iterations);
+
+ CU_ASSERT(sync_failures == 0);
+ CU_ASSERT(is_locked_errs == 0);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int rwlock_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, current_errs, lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = global_mem->g_iterations;
+
+ /* Wait here until all of the threads have also reached this point */
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = iterations / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ for (cnt = 1; cnt <= iterations; cnt++) {
+ /* Verify that we can obtain a read lock */
+ odp_rwlock_read_lock(&global_mem->global_rwlock);
+
+ /* Verify lock is unowned (no writer holds it) */
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Release the read lock */
+ odp_rwlock_read_unlock(&global_mem->global_rwlock);
+
+ /* Acquire the shared global lock */
+ odp_rwlock_write_lock(&global_mem->global_rwlock);
+
+ /* Make sure we have lock now AND didn't previously own it */
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Now set the global_lock_owner to be us, wait a while, and
+ * then we see if anyone else has snuck in and changed the
+ * global_lock_owner to be themselves
+ */
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Release shared lock, and make sure we no longer have it */
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ odp_rwlock_write_unlock(&global_mem->global_rwlock);
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and then rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if ((global_mem->g_verbose) && (sync_failures != 0))
+ ODPH_ERR("Thread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures in %" PRIu32 " iterations\n", thread_num,
+ per_thread_mem->thread_id,
+ per_thread_mem->thread_core,
+ sync_failures, iterations);
+
+ CU_ASSERT(sync_failures == 0);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+static int rwlock_recursive_functional_test(void *arg UNUSED)
+{
+ global_shared_mem_t *global_mem;
+ per_thread_mem_t *per_thread_mem;
+ uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt;
+ uint32_t sync_failures, recursive_errs, current_errs, lock_owner_delay;
+
+ thread_num = odp_cpu_id() + 1;
+ per_thread_mem = thread_init();
+ global_mem = per_thread_mem->global_mem;
+ iterations = global_mem->g_iterations;
+
+ /* Wait here until all of the threads have also reached this point */
+ odp_barrier_wait(&global_mem->global_barrier);
+
+ sync_failures = 0;
+ recursive_errs = 0;
+ current_errs = 0;
+ rs_idx = 0;
+ resync_cnt = iterations / NUM_RESYNC_BARRIERS;
+ lock_owner_delay = BASE_DELAY;
+
+ for (cnt = 1; cnt <= iterations; cnt++) {
+ /* Verify that we can obtain a read lock */
+ odp_rwlock_recursive_read_lock(
+ &global_mem->global_recursive_rwlock);
+
+ /* Verify lock is unowned (no writer holds it) */
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Verify we can get read lock recursively */
+ odp_rwlock_recursive_read_lock(
+ &global_mem->global_recursive_rwlock);
+
+ /* Verify lock is unowned (no writer holds it) */
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Release the read lock */
+ odp_rwlock_recursive_read_unlock(
+ &global_mem->global_recursive_rwlock);
+ odp_rwlock_recursive_read_unlock(
+ &global_mem->global_recursive_rwlock);
+
+ /* Acquire the shared global lock */
+ odp_rwlock_recursive_write_lock(
+ &global_mem->global_recursive_rwlock);
+
+ /* Make sure we have lock now AND didn't previously own it */
+ if (global_mem->global_lock_owner != 0) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Now set the global_lock_owner to be us, wait a while, and
+ * then we see if anyone else has snuck in and changed the
+ * global_lock_owner to be themselves
+ */
+ global_mem->global_lock_owner = thread_num;
+ odp_mb_full();
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ /* Acquire it again and verify we still own it */
+ odp_rwlock_recursive_write_lock(
+ &global_mem->global_recursive_rwlock);
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ recursive_errs++;
+ }
+
+ /* Release the recursive lock and make sure we still own it */
+ odp_rwlock_recursive_write_unlock(
+ &global_mem->global_recursive_rwlock);
+ thread_delay(per_thread_mem, lock_owner_delay);
+ if (global_mem->global_lock_owner != thread_num) {
+ current_errs++;
+ recursive_errs++;
+ }
+
+ /* Release shared lock, and make sure we no longer have it */
+ global_mem->global_lock_owner = 0;
+ odp_mb_full();
+ odp_rwlock_recursive_write_unlock(
+ &global_mem->global_recursive_rwlock);
+ if (global_mem->global_lock_owner == thread_num) {
+ current_errs++;
+ sync_failures++;
+ }
+
+ if (current_errs == 0)
+ lock_owner_delay++;
+
+ /* Wait a small amount of time and then rerun the test */
+ thread_delay(per_thread_mem, BASE_DELAY);
+
+ /* Try to resync all of the threads to increase contention */
+ if ((rs_idx < NUM_RESYNC_BARRIERS) &&
+ ((cnt % resync_cnt) == (resync_cnt - 1)))
+ odp_barrier_wait(&global_mem->barrier_array[rs_idx++]);
+ }
+
+ if ((global_mem->g_verbose) && (sync_failures != 0))
+ ODPH_ERR("Thread %" PRIu32 " (id=%d core=%d) had %" PRIu32
+ " sync_failures and %" PRIu32
+ " recursive_errs in %" PRIu32
+ " iterations\n", thread_num,
+ per_thread_mem->thread_id,
+ per_thread_mem->thread_core,
+ sync_failures, recursive_errs, iterations);
+
+ CU_ASSERT(sync_failures == 0);
+ CU_ASSERT(recursive_errs == 0);
+
+ thread_finalize(per_thread_mem);
+
+ return CU_get_number_of_failures();
+}
+
+/* Thread-unsafe tests */
+static void lock_test_no_lock_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, no_lock_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t lock_suite_no_locking[] = {
+ ODP_TEST_INFO(lock_test_no_lock_functional), /* must be first */
+ ODP_TEST_INFO_NULL
+};
+
+/* Spin lock tests */
+static void lock_test_spinlock_api(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, spinlock_api_tests, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_spinlock_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_spinlock_init(&global_mem->global_spinlock);
+ odp_cunit_thread_create(num, spinlock_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_spinlock_recursive_api(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, spinlock_recursive_api_tests, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_spinlock_recursive_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_spinlock_recursive_init(&global_mem->global_recursive_spinlock);
+ odp_cunit_thread_create(num, spinlock_recursive_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t lock_suite_spinlock[] = {
+ ODP_TEST_INFO(lock_test_spinlock_api),
+ ODP_TEST_INFO(lock_test_spinlock_functional),
+ ODP_TEST_INFO_NULL
+};
+
+odp_testinfo_t lock_suite_spinlock_recursive[] = {
+ ODP_TEST_INFO(lock_test_spinlock_recursive_api),
+ ODP_TEST_INFO(lock_test_spinlock_recursive_functional),
+ ODP_TEST_INFO_NULL
+};
+
+/* Ticket lock tests */
+static void lock_test_ticketlock_api(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, ticketlock_api_tests, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_ticketlock_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_ticketlock_init(&global_mem->global_ticketlock);
+ odp_cunit_thread_create(num, ticketlock_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t lock_suite_ticketlock[] = {
+ ODP_TEST_INFO(lock_test_ticketlock_api),
+ ODP_TEST_INFO(lock_test_ticketlock_functional),
+ ODP_TEST_INFO_NULL
+};
+
+/* RW lock tests */
+static void lock_test_rwlock_api(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, rwlock_api_tests, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_rwlock_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_rwlock_init(&global_mem->global_rwlock);
+ odp_cunit_thread_create(num, rwlock_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t lock_suite_rwlock[] = {
+ ODP_TEST_INFO(lock_test_rwlock_api),
+ ODP_TEST_INFO(lock_test_rwlock_functional),
+ ODP_TEST_INFO_NULL
+};
+
+static void lock_test_rwlock_recursive_api(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_cunit_thread_create(num, rwlock_recursive_api_tests, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+static void lock_test_rwlock_recursive_functional(void)
+{
+ int num = global_mem->g_num_threads;
+
+ odp_rwlock_recursive_init(&global_mem->global_recursive_rwlock);
+ odp_cunit_thread_create(num, rwlock_recursive_functional_test, NULL, 0, 0);
+ odp_cunit_thread_join(num);
+}
+
+odp_testinfo_t lock_suite_rwlock_recursive[] = {
+ ODP_TEST_INFO(lock_test_rwlock_recursive_api),
+ ODP_TEST_INFO(lock_test_rwlock_recursive_functional),
+ ODP_TEST_INFO_NULL
+};
+
+static int lock_suite_init(void)
+{
+ uint32_t num_threads, idx;
+
+ num_threads = global_mem->g_num_threads;
+ odp_barrier_init(&global_mem->global_barrier, num_threads);
+ for (idx = 0; idx < NUM_RESYNC_BARRIERS; idx++)
+ odp_barrier_init(&global_mem->barrier_array[idx], num_threads);
+
+ return 0;
+}
+
+static int lock_init(odp_instance_t *inst)
+{
+ uint32_t workers_count, max_threads;
+ int ret = 0;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(global_shared_mem_t), 64, 0);
+ if (ODP_SHM_INVALID == global_shm) {
+ ODPH_ERR("Unable to reserve memory for global_shm\n");
+ return -1;
+ }
+
+ global_mem = odp_shm_addr(global_shm);
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ global_mem->g_num_threads = MAX_WORKERS;
+ global_mem->g_iterations = 0; /* tuned by first test */
+ global_mem->g_verbose = VERBOSE;
+
+ workers_count = odp_cpumask_default_worker(NULL, 0);
+
+ max_threads = (workers_count >= MAX_WORKERS) ?
+ MAX_WORKERS : workers_count;
+
+ if (max_threads < global_mem->g_num_threads) {
+ printf("Requested num of threads is too large\n");
+ printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
+ global_mem->g_num_threads,
+ max_threads);
+ global_mem->g_num_threads = max_threads;
+ }
+
+ printf("Num of threads used = %" PRIu32 "\n",
+ global_mem->g_num_threads);
+
+ return ret;
+}
+
+static int lock_term(odp_instance_t inst)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ if (0 != odp_shm_free(shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+odp_suiteinfo_t lock_suites[] = {
+ {"nolocking", lock_suite_init, NULL,
+ lock_suite_no_locking}, /* must be first */
+ {"spinlock", lock_suite_init, NULL,
+ lock_suite_spinlock},
+ {"spinlock_recursive", lock_suite_init, NULL,
+ lock_suite_spinlock_recursive},
+ {"ticketlock", lock_suite_init, NULL,
+ lock_suite_ticketlock},
+ {"rwlock", lock_suite_init, NULL,
+ lock_suite_rwlock},
+ {"rwlock_recursive", lock_suite_init, NULL,
+ lock_suite_rwlock_recursive},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(lock_init);
+ odp_cunit_register_global_term(lock_term);
+
+ ret = odp_cunit_register(lock_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/ml/.gitignore b/test/validation/api/ml/.gitignore
new file mode 100644
index 000000000..be2347720
--- /dev/null
+++ b/test/validation/api/ml/.gitignore
@@ -0,0 +1 @@
+ml_main
diff --git a/test/validation/api/ml/Makefile.am b/test/validation/api/ml/Makefile.am
new file mode 100644
index 000000000..b7946a963
--- /dev/null
+++ b/test/validation/api/ml/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = ml_main
+ml_main_SOURCES = ml.c
diff --git a/test/validation/api/ml/ml.c b/test/validation/api/ml/ml.c
new file mode 100644
index 000000000..5f8be1b64
--- /dev/null
+++ b/test/validation/api/ml/ml.c
@@ -0,0 +1,572 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define UAREA 0xaa
+#define NUM_COMPL 10u
+#define COMPL_POOL_NAME "ML compl pool"
+
+typedef struct global_t {
+ int disabled;
+ uint32_t num_compl;
+ odp_ml_capability_t ml_capa;
+} global_t;
+
+typedef struct {
+ uint32_t count;
+ uint8_t mark[NUM_COMPL];
+} uarea_init_t;
+
+static global_t global;
+
+static int ml_suite_init(void)
+{
+ memset(&global, 0, sizeof(global_t));
+
+ if (odp_ml_capability(&global.ml_capa)) {
+ ODPH_ERR("ML capability failed\n");
+ return -1;
+ }
+
+ if (global.ml_capa.max_models == 0) {
+ global.disabled = 1;
+ ODPH_DBG("ML test disabled\n");
+ return 0;
+ }
+
+ global.num_compl = ODPH_MIN(NUM_COMPL, global.ml_capa.pool.max_num);
+
+ return 0;
+}
+
+static int check_ml_support(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void test_ml_capability(void)
+{
+ odp_ml_capability_t ml_capa;
+
+ memset(&ml_capa, 0, sizeof(odp_ml_capability_t));
+ CU_ASSERT(odp_ml_capability(&ml_capa) == 0);
+
+ if (ml_capa.max_models == 0)
+ return;
+
+ CU_ASSERT(ml_capa.max_model_size > 0);
+ CU_ASSERT(ml_capa.max_models_loaded > 0);
+ CU_ASSERT(ml_capa.max_inputs > 0);
+ CU_ASSERT(ml_capa.max_outputs > 0);
+ CU_ASSERT(ml_capa.max_segs_per_input > 0);
+ CU_ASSERT(ml_capa.max_segs_per_output > 0);
+ CU_ASSERT(ml_capa.min_input_align > 0);
+ CU_ASSERT(ml_capa.min_output_align > 0);
+
+ if ((ml_capa.load.compl_mode_mask | ml_capa.run.compl_mode_mask) &
+ ODP_ML_COMPL_MODE_EVENT) {
+ odp_pool_capability_t pool_capa;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+
+ CU_ASSERT(ml_capa.pool.max_pools > 0);
+ CU_ASSERT(ml_capa.pool.max_pools <= pool_capa.max_pools);
+ CU_ASSERT(ml_capa.pool.max_num > 0);
+ CU_ASSERT(ml_capa.pool.max_cache_size >= ml_capa.pool.min_cache_size);
+ }
+
+ CU_ASSERT(ml_capa.load.compl_mode_mask);
+ CU_ASSERT(ml_capa.run.compl_mode_mask);
+
+ if (ml_capa.load.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ CU_ASSERT(ml_capa.load.compl_queue_plain || ml_capa.load.compl_queue_sched);
+
+ if (ml_capa.run.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ CU_ASSERT(ml_capa.run.compl_queue_plain || ml_capa.run.compl_queue_sched);
+}
+
+static void test_ml_param(uint8_t fill)
+{
+ odp_ml_config_t config;
+ odp_ml_model_param_t model_param;
+ odp_ml_compl_pool_param_t pool_param;
+ odp_ml_compl_param_t compl_param;
+ odp_ml_run_param_t run_param;
+
+ memset(&config, fill, sizeof(config));
+ odp_ml_config_init(&config);
+ CU_ASSERT(config.max_models_created == 1);
+ CU_ASSERT(config.max_models_loaded == 1);
+ CU_ASSERT(config.load_mode_mask == 0);
+ CU_ASSERT(config.run_mode_mask == 0);
+
+ memset(&model_param, fill, sizeof(model_param));
+ odp_ml_model_param_init(&model_param);
+ CU_ASSERT(model_param.max_compl_id == 0);
+ CU_ASSERT(!model_param.extra_stat_enable);
+ CU_ASSERT(model_param.extra_param == NULL);
+ CU_ASSERT(model_param.extra_info.num_inputs == 0);
+ CU_ASSERT(model_param.extra_info.num_outputs == 0);
+ CU_ASSERT(model_param.extra_info.input_format == NULL);
+ CU_ASSERT(model_param.extra_info.output_format == NULL);
+
+ memset(&pool_param, fill, sizeof(pool_param));
+ odp_ml_compl_pool_param_init(&pool_param);
+ CU_ASSERT(pool_param.uarea_size == 0);
+ CU_ASSERT(pool_param.uarea_init.args == NULL);
+ CU_ASSERT(pool_param.uarea_init.init_fn == NULL);
+ CU_ASSERT(pool_param.cache_size <= global.ml_capa.pool.max_cache_size);
+ CU_ASSERT(pool_param.cache_size >= global.ml_capa.pool.min_cache_size);
+
+ memset(&compl_param, fill, sizeof(compl_param));
+ odp_ml_compl_param_init(&compl_param);
+ CU_ASSERT(compl_param.user_ptr == NULL);
+
+ memset(&run_param, fill, sizeof(run_param));
+ odp_ml_run_param_init(&run_param);
+ CU_ASSERT(run_param.batch_size == 0);
+ CU_ASSERT(run_param.result == NULL);
+}
+
+static void test_ml_param_init(void)
+{
+ test_ml_param(0x00);
+ test_ml_param(0xff);
+}
+
+static void test_ml_debug(void)
+{
+ odp_ml_print();
+}
+
+static void ml_compl_pool_create_max_pools(void)
+{
+ int ret;
+ uint32_t i, j;
+ odp_ml_compl_pool_param_t ml_pool_param;
+ uint32_t max_pools = global.ml_capa.pool.max_pools;
+ odp_pool_t compl_pools[max_pools];
+
+ odp_ml_compl_pool_param_init(&ml_pool_param);
+ ml_pool_param.num = global.num_compl;
+ for (i = 0; i < max_pools; i++) {
+ compl_pools[i] = odp_ml_compl_pool_create(NULL, &ml_pool_param);
+
+ if (compl_pools[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ CU_ASSERT(i == max_pools);
+
+ /* Destroy the created valid pools */
+ for (j = 0; j < i; j++) {
+ ret = odp_pool_destroy(compl_pools[j]);
+ CU_ASSERT(ret == 0);
+
+ if (ret == -1)
+ ODPH_ERR("ML completion pool destroy failed: %u / %u\n", j, i);
+ }
+}
+
+static void compl_pool_info(void)
+{
+ odp_pool_t pool;
+ odp_pool_t compl_pool;
+ odp_pool_info_t pool_info;
+ odp_ml_compl_pool_param_t pool_param;
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = global.num_compl;
+
+ compl_pool = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(compl_pool != ODP_POOL_INVALID);
+
+ /* Verify info about the created ML completion pool compl_pool */
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == compl_pool);
+
+ memset(&pool_info, 0x66, sizeof(odp_pool_info_t));
+ CU_ASSERT_FATAL(odp_pool_info(compl_pool, &pool_info) == 0);
+
+ CU_ASSERT(!strcmp(pool_info.name, COMPL_POOL_NAME));
+ CU_ASSERT(pool_info.pool_ext == 0);
+ CU_ASSERT(pool_info.type == ODP_POOL_ML_COMPL);
+ CU_ASSERT(pool_info.ml_pool_param.num == NUM_COMPL);
+ CU_ASSERT(pool_info.ml_pool_param.uarea_size == 0);
+ CU_ASSERT(pool_info.ml_pool_param.cache_size == pool_param.cache_size);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(compl_pool) == 0);
+}
+
+static void compl_alloc_max(void)
+{
+ uint64_t u64;
+ odp_event_t event;
+ odp_pool_t compl_pool;
+ odp_ml_compl_pool_param_t pool_param;
+ const int num = ODPH_MIN(global.ml_capa.pool.max_num, 1000000u);
+ odp_ml_compl_t *compl = malloc(num * sizeof(odp_ml_compl_t));
+
+ CU_ASSERT_FATAL(compl != NULL);
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = num;
+
+ compl_pool = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(compl_pool != ODP_POOL_INVALID);
+
+ for (int i = 0; i < num; i++) {
+ compl[i] = odp_ml_compl_alloc(compl_pool);
+ CU_ASSERT_FATAL(compl[i] != ODP_ML_COMPL_INVALID);
+
+ u64 = odp_ml_compl_to_u64(compl[i]);
+ CU_ASSERT(u64 != odp_ml_compl_to_u64(ODP_ML_COMPL_INVALID));
+
+ event = odp_ml_compl_to_event(compl[i]);
+ CU_ASSERT(odp_event_type(event) == ODP_EVENT_ML_COMPL);
+ }
+
+ for (int i = 0; i < num; i++)
+ odp_ml_compl_free(compl[i]);
+
+ free(compl);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(compl_pool) == 0);
+}
+
+static void compl_pool_lookup(void)
+{
+ odp_pool_t pool, pool_a, pool_b;
+ odp_ml_compl_pool_param_t pool_param;
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = global.num_compl;
+
+ pool_a = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(pool_a != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == pool_a);
+
+ /* Second pool with the same name */
+ pool_b = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(pool_b != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == pool_a || pool == pool_b);
+
+ CU_ASSERT(odp_pool_destroy(pool_a) == 0);
+ CU_ASSERT(odp_pool_destroy(pool_b) == 0);
+}
+
+static int check_event_user_area(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (((global.ml_capa.load.compl_mode_mask | global.ml_capa.run.compl_mode_mask) &
+ ODP_ML_COMPL_MODE_EVENT) &&
+ (global.ml_capa.pool.max_uarea_size > 0))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void test_ml_compl_user_area(void)
+{
+ uint32_t i;
+ void *addr;
+ void *prev;
+ odp_pool_t pool;
+ odp_ml_compl_pool_param_t pool_param;
+ uint32_t size = global.ml_capa.pool.max_uarea_size;
+ uint32_t num = global.num_compl;
+ odp_ml_compl_t compl_evs[num];
+
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = num;
+ pool_param.uarea_size = size;
+ pool = odp_ml_compl_pool_create(NULL, &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ prev = NULL;
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag = 0;
+
+ compl_evs[i] = odp_ml_compl_alloc(pool);
+
+ if (compl_evs[i] == ODP_ML_COMPL_INVALID)
+ break;
+
+ addr = odp_ml_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ memset(addr, 0, size);
+
+ ev = odp_ml_compl_to_event(compl_evs[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag < 0);
+
+ prev = addr;
+ }
+ CU_ASSERT(i == num);
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_ml_compl_free(compl_evs[j]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static int check_event_user_area_init(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.ml_capa.pool.max_uarea_size > 0 && global.ml_capa.pool.uarea_persistence)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void init_event_uarea(void *uarea, uint32_t size, void *args, uint32_t index)
+{
+ uarea_init_t *data = args;
+
+ data->count++;
+ data->mark[index] = 1;
+ memset(uarea, UAREA, size);
+}
+
+static void test_ml_compl_user_area_init(void)
+{
+ odp_ml_compl_pool_param_t pool_param;
+ uint32_t num = global.num_compl, i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_ml_compl_t compl_evs[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.uarea_init.init_fn = init_event_uarea;
+ pool_param.uarea_init.args = &data;
+ pool_param.num = num;
+ pool_param.uarea_size = 1;
+ pool = odp_ml_compl_pool_create(NULL, &pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ compl_evs[i] = odp_ml_compl_alloc(pool);
+
+ CU_ASSERT(compl_evs[i] != ODP_ML_COMPL_INVALID);
+
+ if (compl_evs[i] == ODP_ML_COMPL_INVALID)
+ break;
+
+ uarea = odp_ml_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_ml_compl_free(compl_evs[j]);
+
+ odp_pool_destroy(pool);
+}
+
+static void test_ml_fp32_to_uint8(void)
+{
+ uint8_t u8[8];
+ float fp[8] = {-20.f, -16.4f, -14.6f, -12.5f, 0, 31.4f, 80.f, 96.3f};
+ uint8_t expected[8] = {0, 0, 4, 10, 43, 127, 255, 255};
+ float scale = 0.3746f;
+ uint8_t zero_point = 43;
+
+ odp_ml_fp32_to_uint8(u8, fp, 8, scale, zero_point);
+ for (uint32_t i = 0; i < 8; i++)
+ CU_ASSERT(u8[i] == expected[i]);
+}
+
+static void test_ml_fp32_from_uint8(void)
+{
+ float fp[4];
+ float scale = 0.4f;
+ uint8_t zero_point = 43;
+ uint8_t u8[4] = {0, 43, 145, 255};
+ float expected[4] = {-17.2f, 0.0f, 40.8f, 84.8f};
+
+ odp_ml_fp32_from_uint8(fp, u8, 4, scale, zero_point);
+ for (uint32_t i = 0; i < 4; i++)
+ CU_ASSERT(fp[i] == expected[i]);
+}
+
+static void test_ml_fp32_to_int8(void)
+{
+ int8_t i8[5];
+ float scale = 0.0223f;
+ int8_t zero_point = 0;
+ float fp32[5] = {-3.4f, -2.5f, 0, 1.4f, 2.9f};
+ int8_t i8_expected[5] = {-127, -112, 0, 63, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 5, scale, zero_point);
+
+ for (uint32_t i = 0; i < 5; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_to_int8_positive_zp(void)
+{
+ int8_t i8[6];
+ float scale = 0.0223f;
+ int8_t zero_point = 56;
+ float fp32[6] = {-4.1f, -3.4f, -2.5f, 0, 1.4f, 2.9f};
+ int8_t i8_expected[6] = {-127, -96, -56, 56, 119, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_to_int8_negative_zp(void)
+{
+ int8_t i8[6];
+ float scale = 0.0223f;
+ int8_t zero_point = -56;
+ float fp32[6] = {-3.4f, -2.5f, 0, 1.4f, 2.9f, 4.1f};
+ int8_t i8_expected[6] = {-127, -127, -56, 7, 74, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_from_int8(void)
+{
+ float fp32[6];
+ float scale = 0.05f;
+ int8_t zero_point = 56;
+ int8_t i8[6] = {-128, 46, 0, 56, 85, 127};
+ float fp32_expected[6] = {-9.2f, -0.5f, -2.8f, 0.0f, 1.45f, 3.55f};
+
+ odp_ml_fp32_from_int8(fp32, i8, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(fp32[i] == fp32_expected[i]);
+}
+
+static int approx_equal(double a, double b)
+{
+ const double tolerance = .01;
+
+ if (a < 0 && b < 0) {
+ a = -a;
+ b = -b;
+ }
+
+ if (a < b) {
+ double tmp = a;
+
+ a = b;
+ b = tmp;
+ }
+
+ return (a * (1 - tolerance) < b && a * (1 + tolerance) > b);
+}
+
+static void test_ml_fp32_fp16(void)
+{
+ float fp32[4];
+ uint16_t fp16[4];
+ float big = 1, small = 1;
+ const float phi = 1.618033988749;
+
+ fp32[0] = 0.0;
+ fp32[1] = -0.0;
+ memset(fp16, 1, sizeof(fp16));
+ odp_ml_fp32_to_fp16(fp16, fp32, 2);
+ memset(fp32, 1, sizeof(fp32));
+ odp_ml_fp32_from_fp16(fp32, fp16, 2);
+ CU_ASSERT(fp32[0] == 0);
+ CU_ASSERT(fp32[1] == 0);
+
+ /*
+ * 65504 is the largest normal number for fp16 with 5 exponent bits (IEEE 754-2008).
+ */
+ while (big < 65504 / 2) {
+ fp32[0] = big;
+ fp32[1] = -big;
+ fp32[2] = small;
+ fp32[3] = -small;
+ memset(fp16, 0, sizeof(fp16));
+ odp_ml_fp32_to_fp16(fp16, fp32, 4);
+ memset(fp32, 0, sizeof(fp32));
+ odp_ml_fp32_from_fp16(fp32, fp16, 4);
+ CU_ASSERT(approx_equal(fp32[0], big));
+ CU_ASSERT(approx_equal(fp32[1], -big));
+ CU_ASSERT(approx_equal(fp32[2], small));
+ CU_ASSERT(approx_equal(fp32[3], -small));
+ big *= phi;
+ small /= phi;
+ }
+}
+
+odp_testinfo_t ml_suite[] = {
+ ODP_TEST_INFO(test_ml_capability),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_param_init, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_debug, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(ml_compl_pool_create_max_pools, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_pool_info, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_alloc_max, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_pool_lookup, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_compl_user_area, check_event_user_area),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_compl_user_area_init, check_event_user_area_init),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_uint8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_from_uint8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8_positive_zp, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8_negative_zp, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_from_int8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_fp16, check_ml_support),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t ml_suites[] = {
+ {"ML", ml_suite_init, NULL, ml_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(ml_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/packet/.gitignore b/test/validation/api/packet/.gitignore
index c05530d2d..c05530d2d 100644
--- a/test/common_plat/validation/api/packet/.gitignore
+++ b/test/validation/api/packet/.gitignore
diff --git a/test/validation/api/packet/Makefile.am b/test/validation/api/packet/Makefile.am
new file mode 100644
index 000000000..ad5775d7e
--- /dev/null
+++ b/test/validation/api/packet/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = packet_main
+packet_main_SOURCES = packet.c
diff --git a/test/validation/api/packet/packet.c b/test/validation/api/packet/packet.c
new file mode 100644
index 000000000..2362be977
--- /dev/null
+++ b/test/validation/api/packet/packet.c
@@ -0,0 +1,4584 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <stdlib.h>
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4.h>
+#include <test_packet_ipsec.h>
+#include <test_packet_ipv6.h>
+
+#include <odp/helper/odph_api.h>
+
+/* Reserve some tailroom for tests */
+#define TAILROOM_RESERVE 4
+/* Number of packets in the test packet pool */
+#define PACKET_POOL_NUM 300
+/* Number of large, possibly segmented, test packets */
+#define PACKET_POOL_NUM_SEG 4
+ODP_STATIC_ASSERT(PACKET_POOL_NUM_SEG > 1 &&
+ PACKET_POOL_NUM_SEG < PACKET_POOL_NUM,
+ "Invalid PACKET_POOL_NUM_SEG value");
+
+/* Number of packets in parse test */
+#define PARSE_TEST_NUM_PKT 10
+/* Maximum offset to Ethernet in parse tests */
+#define MAX_PARSE_L2_OFFSET 207
+
+/* Default packet vector size */
+#define PKT_VEC_SIZE 64
+/* Number of packet vectors in default pool */
+#define PKT_VEC_NUM 10
+/* Number of preallocated packet vector test packets */
+#define PKT_VEC_PACKET_NUM PKT_VEC_NUM
+
+/* Maximum packet length when 'pool_capa.pkt.max_len == 0' */
+#define DEFAULT_MAX_LEN (32 * 1024)
+
+static odp_pool_capability_t pool_capa;
+static odp_pool_param_t default_param;
+static odp_pool_t default_pool;
+static uint32_t packet_len;
+
+static uint32_t segmented_packet_len;
+static odp_bool_t segmentation_supported = true;
+
+odp_packet_t test_packet, segmented_test_packet;
+/* Packet vector globals */
+static odp_packet_t pkt_vec[PKT_VEC_PACKET_NUM];
+static odp_packet_vector_t pktv_default = ODP_PACKET_VECTOR_INVALID;
+static odp_pool_t vector_default_pool = ODP_POOL_INVALID;
+
+static struct udata_struct {
+ uint64_t u64;
+ uint32_t u32;
+ char str[10];
+} test_packet_udata = {
+ 123456,
+ 789912,
+ "abcdefg",
+};
+
+static struct {
+ odp_pool_t pool;
+ odp_proto_chksums_t all_chksums;
+ uint32_t l2_offset[PARSE_TEST_NUM_PKT];
+} parse_test;
+
+static uint32_t parse_test_pkt_len[] = {
+ sizeof(test_packet_arp),
+ sizeof(test_packet_ipv4_icmp),
+ sizeof(test_packet_ipv4_tcp),
+ sizeof(test_packet_ipv4_udp),
+ sizeof(test_packet_vlan_ipv4_udp),
+ sizeof(test_packet_vlan_qinq_ipv4_udp),
+ sizeof(test_packet_ipv6_icmp),
+ sizeof(test_packet_ipv6_tcp),
+ sizeof(test_packet_ipv6_udp),
+ sizeof(test_packet_vlan_ipv6_udp),
+ sizeof(test_packet_ipv4_sctp),
+ sizeof(test_packet_ipv4_ipsec_ah),
+ sizeof(test_packet_ipv4_ipsec_esp),
+ sizeof(test_packet_ipv6_ipsec_ah),
+ sizeof(test_packet_ipv6_ipsec_esp),
+ sizeof(test_packet_mcast_eth_ipv4_udp),
+ sizeof(test_packet_bcast_eth_ipv4_udp),
+ sizeof(test_packet_mcast_eth_ipv6_udp),
+ sizeof(test_packet_ipv4_udp_first_frag),
+ sizeof(test_packet_ipv4_udp_last_frag),
+ sizeof(test_packet_ipv4_rr_nop_icmp)
+};
+
+#define packet_compare_offset(pkt1, off1, pkt2, off2, len) \
+ _packet_compare_offset((pkt1), (off1), (pkt2), (off2), (len), __LINE__)
+
+#define packet_compare_data(pkt1, pkt2) \
+ _packet_compare_data((pkt1), (pkt2), __LINE__)
+
+static void _packet_compare_data(odp_packet_t pkt1, odp_packet_t pkt2,
+ int line)
+{
+ uint32_t len = odp_packet_len(pkt1);
+ uint32_t offset = 0;
+ uint32_t seglen1, seglen2, cmplen;
+ void *pkt1map, *pkt2map;
+ int ret;
+
+ CU_ASSERT_FATAL(len == odp_packet_len(pkt2));
+
+ while (len > 0) {
+ seglen1 = 0;
+ seglen2 = 0;
+ pkt1map = odp_packet_offset(pkt1, offset, &seglen1, NULL);
+ pkt2map = odp_packet_offset(pkt2, offset, &seglen2, NULL);
+
+ CU_ASSERT_FATAL(pkt1map != NULL);
+ CU_ASSERT_FATAL(pkt2map != NULL);
+ cmplen = seglen1 < seglen2 ? seglen1 : seglen2;
+ ret = memcmp(pkt1map, pkt2map, cmplen);
+
+ if (ret) {
+ printf("\ncompare_data failed: line %i, offset %"
+ PRIu32 "\n", line, offset);
+ }
+
+ CU_ASSERT(ret == 0);
+
+ offset += cmplen;
+ len -= cmplen;
+ }
+}
+
+static int packet_sanity_check(odp_packet_t pkt)
+{
+ odp_packet_seg_t seg;
+ uint32_t len = 0;
+
+ for (seg = odp_packet_first_seg(pkt);
+ seg != ODP_PACKET_SEG_INVALID;
+ seg = odp_packet_next_seg(pkt, seg)) {
+ uint32_t seglen = odp_packet_seg_data_len(pkt, seg);
+
+ CU_ASSERT(seglen != 0);
+ if (seglen == 0)
+ return 1;
+ len += seglen;
+ }
+ CU_ASSERT(len == odp_packet_len(pkt));
+ return len != odp_packet_len(pkt);
+}
+
+static int fill_data_forward(odp_packet_t pkt, uint32_t offset, uint32_t len,
+ uint32_t *cur_data)
+{
+ uint8_t buf[len];
+ uint32_t i, data;
+
+ data = *cur_data;
+
+ for (i = 0; i < len; i++)
+ buf[i] = data++;
+
+ *cur_data = data;
+
+ return odp_packet_copy_from_mem(pkt, offset, len, buf);
+}
+
+static int fill_data_backward(odp_packet_t pkt, uint32_t offset, uint32_t len,
+ uint32_t *cur_data)
+{
+ uint8_t buf[len];
+ uint32_t i, data;
+
+ data = *cur_data;
+
+ for (i = 0; i < len; i++)
+ buf[len - i - 1] = data++;
+
+ *cur_data = data;
+
+ return odp_packet_copy_from_mem(pkt, offset, len, buf);
+}
+
+static int packet_suite_init(void)
+{
+ odp_pool_param_t params;
+ odp_packet_t pkt_tbl[PACKET_POOL_NUM_SEG];
+ struct udata_struct *udat;
+ uint32_t uarea_size;
+ uint8_t data = 0;
+ uint32_t i;
+ uint32_t num = PACKET_POOL_NUM;
+ int ret;
+
+ memset(&pool_capa, 0, sizeof(odp_pool_capability_t));
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed\n");
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_uarea_size == 0)
+ printf("Warning: Packet user area not supported\n");
+
+ if (pool_capa.pkt.max_segs_per_pkt == 0)
+ pool_capa.pkt.max_segs_per_pkt = 10;
+
+ /* Pick a typical packet size and decrement it to the single segment
+ * limit if needed (min_seg_len maybe equal to max_len
+ * on some systems). */
+ packet_len = 512;
+ while (packet_len > (pool_capa.pkt.min_seg_len - TAILROOM_RESERVE))
+ packet_len--;
+
+ if (pool_capa.pkt.max_len) {
+ segmented_packet_len = pool_capa.pkt.max_len;
+ } else {
+ segmented_packet_len = pool_capa.pkt.min_seg_len *
+ pool_capa.pkt.max_segs_per_pkt;
+ }
+ if (pool_capa.pkt.max_num != 0 && pool_capa.pkt.max_num < num)
+ num = pool_capa.pkt.max_num;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.seg_len = pool_capa.pkt.min_seg_len;
+ params.pkt.len = pool_capa.pkt.min_seg_len;
+ /* Defining max_len to ensure packet of segmented_packet_len
+ * length can be allocated from this pool.
+ */
+ params.pkt.max_len = segmented_packet_len;
+ params.pkt.num = num;
+ params.pkt.uarea_size = sizeof(struct udata_struct);
+
+ if (params.pkt.uarea_size > pool_capa.pkt.max_uarea_size)
+ params.pkt.uarea_size = pool_capa.pkt.max_uarea_size;
+
+ uarea_size = params.pkt.uarea_size;
+ memcpy(&default_param, &params, sizeof(odp_pool_param_t));
+
+ default_pool = odp_pool_create("default_pool", &params);
+ if (default_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Default pool create failed\n");
+ return -1;
+ }
+
+ test_packet = odp_packet_alloc(default_pool, packet_len);
+
+ if (test_packet == ODP_PACKET_INVALID) {
+ ODPH_ERR("Packet alloc failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < packet_len; i++) {
+ odp_packet_copy_from_mem(test_packet, i, 1, &data);
+ data++;
+ }
+
+ /* Try to allocate PACKET_POOL_NUM_SEG largest possible packets to see
+ * if segmentation is supported */
+ do {
+ ret = odp_packet_alloc_multi(default_pool, segmented_packet_len,
+ pkt_tbl, PACKET_POOL_NUM_SEG);
+ if (ret != PACKET_POOL_NUM_SEG) {
+ if (ret > 0)
+ odp_packet_free_multi(pkt_tbl, ret);
+ segmented_packet_len -= pool_capa.pkt.min_seg_len;
+ continue;
+ }
+ } while (ret != PACKET_POOL_NUM_SEG &&
+ segmented_packet_len > pool_capa.pkt.min_seg_len);
+
+ if (ret != PACKET_POOL_NUM_SEG) {
+ ODPH_ERR("Packet alloc failed\n");
+ return -1;
+ }
+ segmented_test_packet = pkt_tbl[0];
+ odp_packet_free_multi(&pkt_tbl[1], PACKET_POOL_NUM_SEG - 1);
+
+ if (odp_packet_is_valid(test_packet) == 0 ||
+ odp_packet_is_valid(segmented_test_packet) == 0) {
+ ODPH_ERR("odp_packet_is_valid() failed\n");
+ return -1;
+ }
+
+ segmentation_supported = odp_packet_is_segmented(segmented_test_packet);
+
+ data = 0;
+ for (i = 0; i < segmented_packet_len; i++) {
+ odp_packet_copy_from_mem(segmented_test_packet, i, 1, &data);
+ data++;
+ }
+
+ udat = odp_packet_user_area(test_packet);
+ if (odp_packet_user_area_size(test_packet) < uarea_size) {
+ ODPH_ERR("Bad packet user area size %u\n", odp_packet_user_area_size(test_packet));
+ return -1;
+ }
+
+ odp_pool_print(default_pool);
+ memcpy(udat, &test_packet_udata, uarea_size);
+
+ udat = odp_packet_user_area(segmented_test_packet);
+ if (odp_packet_user_area_size(segmented_test_packet) < uarea_size) {
+ ODPH_ERR("Bad segmented packet user area size %u\n",
+ odp_packet_user_area_size(segmented_test_packet));
+ return -1;
+ }
+
+ memcpy(udat, &test_packet_udata, uarea_size);
+
+ return 0;
+}
+
+static int packet_suite_term(void)
+{
+ odp_packet_free(test_packet);
+ odp_packet_free(segmented_test_packet);
+
+ if (odp_pool_destroy(default_pool) != 0)
+ return -1;
+
+ return 0;
+}
+
+/* Set all non-conflicting metadata flags */
+static void packet_set_inflags_common(odp_packet_t pkt, int val)
+{
+ odp_packet_has_l2_set(pkt, val);
+ odp_packet_has_l3_set(pkt, val);
+ odp_packet_has_l4_set(pkt, val);
+ odp_packet_has_eth_set(pkt, val);
+ odp_packet_has_eth_bcast_set(pkt, val);
+ odp_packet_has_eth_mcast_set(pkt, val);
+ odp_packet_has_jumbo_set(pkt, val);
+ odp_packet_has_vlan_set(pkt, val);
+ odp_packet_has_ipv4_set(pkt, val);
+ odp_packet_has_ip_bcast_set(pkt, val);
+ odp_packet_has_ipfrag_set(pkt, val);
+ odp_packet_has_ipopt_set(pkt, val);
+ odp_packet_has_ipsec_set(pkt, val);
+ odp_packet_has_udp_set(pkt, val);
+ odp_packet_user_flag_set(pkt, val);
+}
+
+/* Check all non-conflicting metadata flags */
+static void packet_check_inflags_common(odp_packet_t pkt, int val)
+{
+ CU_ASSERT(odp_packet_has_l2(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_l3(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_l4(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth_bcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth_mcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_jumbo(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_vlan(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipv4(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ip_bcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipfrag(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipopt(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipsec(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_udp(pkt) == !!val);
+ CU_ASSERT(odp_packet_user_flag(pkt) == !!val);
+}
+
+/* Check all metadata flags */
+static void packet_check_inflags_all(odp_packet_t pkt, int val)
+{
+ CU_ASSERT(odp_packet_has_l2(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_l3(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_l4(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth_bcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_eth_mcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_jumbo(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_vlan(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_vlan_qinq(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_arp(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipv4(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipv6(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ip_bcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ip_mcast(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipfrag(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipopt(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_ipsec(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_udp(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_tcp(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_sctp(pkt) == !!val);
+ CU_ASSERT(odp_packet_has_icmp(pkt) == !!val);
+ CU_ASSERT(odp_packet_user_flag(pkt) == !!val);
+}
+
+static void packet_test_alloc_free(void)
+{
+ odp_pool_t pool;
+ odp_packet_t packet;
+ odp_pool_param_t params;
+ odp_event_subtype_t subtype;
+ odp_event_t ev;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.seg_len = pool_capa.pkt.min_seg_len;
+ params.pkt.len = pool_capa.pkt.min_seg_len;
+ params.pkt.num = 1;
+ params.pkt.max_num = 1;
+
+ pool = odp_pool_create("packet_pool_alloc", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Allocate the only buffer from the pool */
+ packet = odp_packet_alloc(pool, packet_len);
+ CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(packet) == packet_len);
+
+ ev = odp_packet_to_event(packet);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+ CU_ASSERT(odp_event_subtype(ev) == ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_event_types(ev, &subtype) == ODP_EVENT_PACKET);
+ CU_ASSERT(subtype == ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_event_pool(ev) == pool);
+
+ CU_ASSERT(odp_packet_subtype(packet) == ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_packet_to_u64(packet) !=
+ odp_packet_to_u64(ODP_PACKET_INVALID));
+
+ /* User pointer should be NULL after alloc */
+ CU_ASSERT(odp_packet_user_ptr(packet) == NULL);
+
+ /* Packet flags should be zero */
+ packet_check_inflags_all(packet, 0);
+
+ /* Pool should have only one packet */
+ CU_ASSERT_FATAL(odp_packet_alloc(pool, packet_len)
+ == ODP_PACKET_INVALID);
+
+ odp_packet_free(packet);
+
+ /* Check that the buffer was returned back to the pool */
+ packet = odp_packet_alloc(pool, packet_len);
+ CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(packet) == packet_len);
+
+ odp_packet_free(packet);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+/* Wrapper to call odp_packet_alloc_multi multiple times until
+ * either no mure buffers are returned, or num buffers were alloced */
+static int packet_alloc_multi(odp_pool_t pool, uint32_t pkt_len,
+ odp_packet_t pkt[], int num)
+{
+ int ret, total = 0;
+
+ do {
+ ret = odp_packet_alloc_multi(pool, pkt_len, pkt + total,
+ num - total);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT(ret <= num - total);
+ total += ret;
+ } while (total < num && ret > 0);
+
+ return total;
+}
+
+static void packet_test_alloc_free_multi(void)
+{
+ const int num_pkt = 2;
+ odp_pool_t pool[2];
+ int i, ret;
+ odp_packet_t packet[2 * num_pkt + 1];
+ odp_packet_t inval_pkt[num_pkt];
+ odp_pool_param_t params;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.seg_len = pool_capa.pkt.min_seg_len;
+ params.pkt.len = pool_capa.pkt.min_seg_len;
+ params.pkt.num = num_pkt;
+ params.pkt.max_num = num_pkt;
+
+ pool[0] = odp_pool_create("packet_pool_alloc_multi_0", &params);
+ pool[1] = odp_pool_create("packet_pool_alloc_multi_1", &params);
+ CU_ASSERT_FATAL(pool[0] != ODP_POOL_INVALID);
+ CU_ASSERT_FATAL(pool[1] != ODP_POOL_INVALID);
+
+ /* Allocate all the packets from the pools */
+
+ ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt + 1);
+ CU_ASSERT_FATAL(ret == num_pkt);
+ ret = packet_alloc_multi(pool[1], packet_len,
+ &packet[num_pkt], num_pkt + 1);
+ CU_ASSERT_FATAL(ret == num_pkt);
+
+ for (i = 0; i < 2 * num_pkt; ++i) {
+ odp_event_subtype_t subtype;
+
+ CU_ASSERT(odp_packet_len(packet[i]) == packet_len);
+ CU_ASSERT(odp_event_type(odp_packet_to_event(packet[i])) ==
+ ODP_EVENT_PACKET);
+ CU_ASSERT(odp_event_subtype(odp_packet_to_event(packet[i])) ==
+ ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_event_types(odp_packet_to_event(packet[i]),
+ &subtype) ==
+ ODP_EVENT_PACKET);
+ CU_ASSERT(subtype == ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_packet_subtype(packet[i]) ==
+ ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_packet_to_u64(packet[i]) !=
+ odp_packet_to_u64(ODP_PACKET_INVALID));
+
+ /* User pointer should be NULL after alloc */
+ CU_ASSERT(odp_packet_user_ptr(packet[i]) == NULL);
+ }
+
+ /* Pools should have no more packets */
+ ret = odp_packet_alloc_multi(pool[0], packet_len, inval_pkt, num_pkt);
+ CU_ASSERT(ret == 0);
+ ret = odp_packet_alloc_multi(pool[1], packet_len, inval_pkt, num_pkt);
+ CU_ASSERT(ret == 0);
+
+ /* Free all packets from all pools at once */
+ odp_packet_free_multi(packet, 2 * num_pkt);
+
+ /* Check that all the packets were returned back to their pools */
+ ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt);
+ CU_ASSERT(ret);
+ ret = packet_alloc_multi(pool[1], packet_len,
+ &packet[num_pkt], num_pkt);
+ CU_ASSERT(ret);
+
+ for (i = 0; i < 2 * num_pkt; ++i) {
+ CU_ASSERT_FATAL(packet[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(packet[i]) == packet_len);
+ }
+ odp_packet_free_multi(packet, 2 * num_pkt);
+ CU_ASSERT(odp_pool_destroy(pool[0]) == 0);
+ CU_ASSERT(odp_pool_destroy(pool[1]) == 0);
+}
+
+static void packet_test_free_sp(void)
+{
+ const int num_pkt = 10;
+ odp_pool_t pool;
+ int i, ret;
+ odp_packet_t packet[num_pkt];
+ odp_pool_param_t params;
+ uint32_t len = packet_len;
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.len = len;
+ params.pkt.num = num_pkt;
+
+ pool = odp_pool_create("packet_pool_free_sp", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ ret = packet_alloc_multi(pool, len, packet, num_pkt);
+ CU_ASSERT_FATAL(ret == num_pkt);
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT_FATAL(packet[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(packet[i]) == len);
+ }
+ odp_packet_free_sp(packet, num_pkt);
+
+ /* Check that all the packets were returned back to the pool */
+ ret = packet_alloc_multi(pool, len, packet, num_pkt);
+ CU_ASSERT_FATAL(ret == num_pkt);
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT_FATAL(packet[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(packet[i]) == len);
+ }
+ odp_packet_free_sp(packet, num_pkt);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_alloc_segmented(void)
+{
+ const int num = 5;
+ odp_packet_t pkts[num];
+ odp_packet_t pkt;
+ uint32_t max_len;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ int ret, i, num_alloc;
+
+ if (pool_capa.pkt.max_segs_per_pkt == 0)
+ pool_capa.pkt.max_segs_per_pkt = 10;
+
+ if (pool_capa.pkt.max_len)
+ max_len = pool_capa.pkt.max_len;
+ else
+ max_len = pool_capa.pkt.min_seg_len *
+ pool_capa.pkt.max_segs_per_pkt;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.seg_len = pool_capa.pkt.min_seg_len;
+ params.pkt.len = max_len;
+
+ /* Ensure that 'num' segmented packets can be allocated */
+ params.pkt.num = num * pool_capa.pkt.max_segs_per_pkt;
+
+ pool = odp_pool_create("pool_alloc_segmented", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Less than max len allocs */
+ pkt = odp_packet_alloc(pool, max_len / 2);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt) == max_len / 2);
+
+ odp_packet_free(pkt);
+
+ num_alloc = 0;
+ for (i = 0; i < num; i++) {
+ ret = odp_packet_alloc_multi(pool, max_len / 2,
+ &pkts[num_alloc], num - num_alloc);
+ CU_ASSERT_FATAL(ret >= 0);
+ num_alloc += ret;
+ if (num_alloc >= num)
+ break;
+ }
+
+ CU_ASSERT(num_alloc == num);
+
+ for (i = 0; i < num_alloc; i++)
+ CU_ASSERT(odp_packet_len(pkts[i]) == max_len / 2);
+
+ odp_packet_free_multi(pkts, num_alloc);
+
+ /* Max len allocs */
+ pkt = odp_packet_alloc(pool, max_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt) == max_len);
+
+ odp_packet_free(pkt);
+
+ num_alloc = 0;
+ for (i = 0; i < num; i++) {
+ ret = odp_packet_alloc_multi(pool, max_len,
+ &pkts[num_alloc], num - num_alloc);
+ CU_ASSERT_FATAL(ret >= 0);
+ num_alloc += ret;
+ if (num_alloc >= num)
+ break;
+ }
+
+ CU_ASSERT(num_alloc == num);
+
+ for (i = 0; i < num_alloc; i++)
+ CU_ASSERT(odp_packet_len(pkts[i]) == max_len);
+
+ odp_packet_free_multi(pkts, num_alloc);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_alloc_max_len(void)
+{
+ const int num = 5;
+ odp_packet_t pkts[num];
+ odp_packet_t pkt;
+ uint32_t max_len;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ int ret, i, num_alloc;
+
+ max_len = pool_capa.pkt.max_len;
+ if (!max_len)
+ max_len = DEFAULT_MAX_LEN;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_PACKET;
+ params.pkt.len = max_len;
+ params.pkt.num = num;
+
+ pool = odp_pool_create("pool_alloc_max_len", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pkt = odp_packet_alloc(pool, max_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt) == max_len);
+
+ odp_packet_free(pkt);
+
+ num_alloc = 0;
+ for (i = 0; i < num; i++) {
+ ret = odp_packet_alloc_multi(pool, max_len,
+ &pkts[num_alloc], num - num_alloc);
+ CU_ASSERT_FATAL(ret >= 0);
+ num_alloc += ret;
+ if (num_alloc >= num)
+ break;
+ }
+
+ CU_ASSERT(num_alloc == num);
+
+ for (i = 0; i < num_alloc; i++)
+ CU_ASSERT(odp_packet_len(pkts[i]) == max_len);
+
+ odp_packet_free_multi(pkts, num_alloc);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_alloc_max_segment(void)
+{
+ const int num = 5;
+ uint32_t max_len, max_seg_len;
+ odp_packet_t pkt;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+
+ max_len = pool_capa.pkt.max_len;
+ if (max_len == 0)
+ max_len = DEFAULT_MAX_LEN;
+
+ max_seg_len = pool_capa.pkt.max_seg_len;
+ if (max_seg_len == 0 || max_seg_len > max_len)
+ max_seg_len = max_len;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_PACKET;
+ params.pkt.seg_len = max_seg_len;
+ params.pkt.len = max_len;
+ params.pkt.num = num;
+
+ pool = odp_pool_create("pool_alloc_max_segment", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pkt = odp_packet_alloc(pool, max_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt) == max_len);
+ CU_ASSERT(odp_packet_seg_len(pkt) >= max_seg_len);
+
+ odp_packet_free(pkt);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_alloc_align(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ uintptr_t data, mask;
+ uint32_t i;
+ uint32_t error_print = 10;
+ uint32_t len = packet_len;
+ uint32_t align = 256;
+ uint32_t num = 100;
+ odp_packet_t pkt[num];
+
+ CU_ASSERT(pool_capa.pkt.max_align >= 2);
+
+ if (align > pool_capa.pkt.max_align)
+ align = pool_capa.pkt.max_align;
+
+ mask = align - 1;
+
+ odp_pool_param_init(&params);
+
+ params.type = ODP_POOL_PACKET;
+ params.pkt.len = len;
+ params.pkt.num = num;
+ params.pkt.align = align;
+
+ pool = odp_pool_create("packet_pool_align", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Allocate the only buffer from the pool */
+ for (i = 0; i < num; i++) {
+ pkt[i] = odp_packet_alloc(pool, len);
+ CU_ASSERT_FATAL(pkt[i] != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt[i]) == len);
+ data = (uintptr_t)odp_packet_data(pkt[i]);
+
+ if (data & mask) {
+ /* Print only first couple of failures to the log */
+ if (error_print > 0) {
+ CU_ASSERT((data & mask) == 0);
+ printf("\nError: Bad data align. Pointer %p, requested align %u\n",
+ (void *)data, align);
+ error_print--;
+ }
+ }
+ }
+
+ odp_packet_free_multi(pkt, num);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_event_conversion(void)
+{
+ odp_packet_t pkt0 = test_packet;
+ odp_packet_t pkt1 = segmented_test_packet;
+ odp_packet_t tmp_pkt;
+ odp_event_t event;
+ odp_event_subtype_t subtype;
+ odp_packet_t pkt[2] = {pkt0, pkt1};
+ odp_event_t ev[2];
+ int i;
+
+ event = odp_packet_to_event(pkt0);
+ CU_ASSERT_FATAL(event != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_type(event) == ODP_EVENT_PACKET);
+ CU_ASSERT(odp_event_subtype(event) == ODP_EVENT_PACKET_BASIC);
+ CU_ASSERT(odp_event_types(event, &subtype) ==
+ ODP_EVENT_PACKET);
+ CU_ASSERT(subtype == ODP_EVENT_PACKET_BASIC);
+
+ tmp_pkt = odp_packet_from_event(event);
+ CU_ASSERT_FATAL(tmp_pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(tmp_pkt == pkt0);
+ packet_compare_data(tmp_pkt, pkt0);
+
+ odp_packet_to_event_multi(pkt, ev, 2);
+
+ for (i = 0; i < 2; i++) {
+ CU_ASSERT_FATAL(ev[i] != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_type(ev[i]) == ODP_EVENT_PACKET);
+ CU_ASSERT(odp_event_subtype(ev[i]) == ODP_EVENT_PACKET_BASIC);
+ }
+
+ odp_packet_from_event_multi(pkt, ev, 2);
+ CU_ASSERT(pkt[0] == pkt0);
+ CU_ASSERT(pkt[1] == pkt1);
+ packet_compare_data(pkt[0], pkt0);
+ packet_compare_data(pkt[1], pkt1);
+}
+
+static void packet_test_basic_metadata(void)
+{
+ odp_packet_t pkt = test_packet;
+ odp_time_t ts;
+ odp_packet_data_range_t range;
+
+ CU_ASSERT(odp_packet_head(pkt) != NULL);
+ CU_ASSERT(odp_packet_data(pkt) != NULL);
+
+ CU_ASSERT(odp_packet_pool(pkt) != ODP_POOL_INVALID);
+ /* Packet was allocated by application so shouldn't have valid pktio. */
+ CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_input_index(pkt) < 0);
+
+ /* Packet was not received from a packet IO, shouldn't have ones
+ * complement calculated. */
+ odp_packet_ones_comp(pkt, &range);
+ CU_ASSERT(range.length == 0);
+
+ odp_packet_flow_hash_set(pkt, UINT32_MAX);
+ CU_ASSERT(odp_packet_has_flow_hash(pkt));
+ CU_ASSERT(odp_packet_flow_hash(pkt) == UINT32_MAX);
+ odp_packet_has_flow_hash_clr(pkt);
+ CU_ASSERT(!odp_packet_has_flow_hash(pkt));
+
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+
+ ts = odp_time_global();
+ odp_packet_ts_set(pkt, ts);
+ CU_ASSERT_FATAL(odp_packet_has_ts(pkt));
+ CU_ASSERT(!odp_time_cmp(ts, odp_packet_ts(pkt)));
+ odp_packet_has_ts_clr(pkt);
+ CU_ASSERT(!odp_packet_has_ts(pkt));
+
+ CU_ASSERT(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DISABLED);
+ odp_packet_free_ctrl_set(pkt, ODP_PACKET_FREE_CTRL_DONT_FREE);
+ CU_ASSERT(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DONT_FREE);
+ odp_packet_free_ctrl_set(pkt, ODP_PACKET_FREE_CTRL_DISABLED);
+ CU_ASSERT(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DISABLED);
+}
+
+static void packet_test_length(void)
+{
+ odp_packet_t pkt = test_packet;
+ uint32_t buf_len, headroom, tailroom, seg_len;
+ void *data;
+
+ buf_len = odp_packet_buf_len(pkt);
+ headroom = odp_packet_headroom(pkt);
+ tailroom = odp_packet_tailroom(pkt);
+ data = odp_packet_data(pkt);
+
+ CU_ASSERT(data != NULL);
+ CU_ASSERT(odp_packet_len(pkt) == packet_len);
+ CU_ASSERT(odp_packet_seg_len(pkt) <= packet_len);
+ CU_ASSERT(odp_packet_data_seg_len(pkt, &seg_len) == data);
+ CU_ASSERT(seg_len == odp_packet_seg_len(pkt));
+ CU_ASSERT(headroom >= pool_capa.pkt.min_headroom);
+ CU_ASSERT(tailroom >= pool_capa.pkt.min_tailroom);
+
+ CU_ASSERT(buf_len >= packet_len + headroom + tailroom);
+}
+
+static void packet_test_reset(void)
+{
+ uint32_t len, headroom;
+ uintptr_t ptr_len;
+ void *data, *new_data, *tail, *new_tail;
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(default_pool, packet_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ len = odp_packet_len(pkt);
+ CU_ASSERT(len == packet_len);
+
+ headroom = odp_packet_headroom(pkt);
+
+ if (headroom) {
+ data = odp_packet_data(pkt);
+ new_data = odp_packet_push_head(pkt, 1);
+ CU_ASSERT(odp_packet_len(pkt) == len + 1);
+ CU_ASSERT((uintptr_t)new_data == ((uintptr_t)data - 1));
+ CU_ASSERT(odp_packet_headroom(pkt) == headroom - 1);
+ ptr_len = (uintptr_t)odp_packet_data(pkt) -
+ (uintptr_t)odp_packet_head(pkt);
+ CU_ASSERT(ptr_len == (headroom - 1));
+ CU_ASSERT(odp_packet_reset(pkt, len) == 0);
+ CU_ASSERT(odp_packet_len(pkt) == len);
+ CU_ASSERT(odp_packet_headroom(pkt) == headroom);
+ ptr_len = (uintptr_t)odp_packet_data(pkt) -
+ (uintptr_t)odp_packet_head(pkt);
+ CU_ASSERT(ptr_len == headroom);
+ }
+
+ data = odp_packet_data(pkt);
+ new_data = odp_packet_pull_head(pkt, 1);
+ CU_ASSERT(odp_packet_len(pkt) == len - 1);
+ CU_ASSERT((uintptr_t)new_data == ((uintptr_t)data + 1));
+ CU_ASSERT(odp_packet_headroom(pkt) == headroom + 1);
+ ptr_len = (uintptr_t)odp_packet_data(pkt) -
+ (uintptr_t)odp_packet_head(pkt);
+ CU_ASSERT(ptr_len == (headroom + 1));
+ CU_ASSERT(odp_packet_reset(pkt, len) == 0);
+ CU_ASSERT(odp_packet_len(pkt) == len);
+ CU_ASSERT(odp_packet_headroom(pkt) == headroom);
+ ptr_len = (uintptr_t)odp_packet_data(pkt) -
+ (uintptr_t)odp_packet_head(pkt);
+ CU_ASSERT(ptr_len == headroom);
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+
+ tail = odp_packet_tail(pkt);
+ new_tail = odp_packet_pull_tail(pkt, 1);
+ CU_ASSERT(odp_packet_len(pkt) == len - 1);
+ CU_ASSERT((uintptr_t)new_tail == ((uintptr_t)tail - 1));
+ CU_ASSERT(odp_packet_reset(pkt, len) == 0);
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ packet_set_inflags_common(pkt, 1);
+ packet_check_inflags_common(pkt, 1);
+ CU_ASSERT(odp_packet_reset(pkt, len) == 0);
+ packet_check_inflags_all(pkt, 0);
+
+ CU_ASSERT(odp_packet_reset(pkt, len - 1) == 0);
+ CU_ASSERT(odp_packet_len(pkt) == (len - 1));
+
+ len = len - len / 2;
+ CU_ASSERT(odp_packet_reset(pkt, len) == 0);
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ odp_packet_free(pkt);
+}
+
+static void packet_test_prefetch(void)
+{
+ odp_packet_prefetch(test_packet, 0, odp_packet_len(test_packet));
+ CU_PASS();
+}
+
+static void packet_test_debug(void)
+{
+ CU_ASSERT(odp_packet_is_valid(test_packet) == 1);
+ printf("\n\n");
+ odp_packet_print(test_packet);
+ odp_packet_print_data(test_packet, 0, 100);
+ odp_packet_print_data(test_packet, 14, 20);
+}
+
+static void packet_test_context(void)
+{
+ void *prev_ptr;
+ struct udata_struct *udat;
+ uint32_t uarea_size;
+ odp_packet_t pkt = test_packet;
+ char ptr_test_value = 2;
+
+ prev_ptr = odp_packet_user_ptr(pkt);
+ odp_packet_user_ptr_set(pkt, &ptr_test_value);
+ CU_ASSERT(odp_packet_user_ptr(pkt) == &ptr_test_value);
+ odp_packet_user_ptr_set(pkt, prev_ptr);
+
+ udat = odp_packet_user_area(pkt);
+ uarea_size = odp_packet_user_area_size(pkt);
+ CU_ASSERT(uarea_size >= default_param.pkt.uarea_size);
+
+ if (uarea_size) {
+ CU_ASSERT(udat != NULL);
+ CU_ASSERT(memcmp(udat, &test_packet_udata, default_param.pkt.uarea_size) == 0);
+ } else {
+ CU_ASSERT(udat == NULL);
+ }
+
+ odp_packet_user_ptr_set(pkt, NULL);
+ CU_ASSERT(odp_packet_user_ptr(pkt) == NULL);
+ odp_packet_user_ptr_set(pkt, (void *)0xdead);
+ CU_ASSERT(odp_packet_user_ptr(pkt) == (void *)0xdead);
+
+ odp_packet_reset(pkt, packet_len);
+
+ /* User pointer should be NULL after reset */
+ CU_ASSERT(odp_packet_user_ptr(pkt) == NULL);
+}
+
+static void packet_test_payload_offset(void)
+{
+ odp_packet_t pkt = test_packet;
+ uint32_t pkt_len = odp_packet_len(pkt);
+
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, 42) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == 42);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, 0) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == 0);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, pkt_len - 1) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == pkt_len - 1);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, ODP_PACKET_OFFSET_INVALID) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+}
+
+static void packet_test_layer_offsets(void)
+{
+ odp_packet_t pkt = test_packet;
+ uint8_t *l2_addr, *l3_addr, *l4_addr;
+ uint32_t seg_len = 0;
+ const uint32_t l2_off = 2;
+ const uint32_t l3_off = l2_off + 14;
+ const uint32_t l4_off = l3_off + 14;
+ int ret;
+
+ /* Set offsets to the same value */
+ ret = odp_packet_l2_offset_set(pkt, l2_off);
+ CU_ASSERT(ret == 0);
+ ret = odp_packet_l3_offset_set(pkt, l2_off);
+ CU_ASSERT(ret == 0);
+ ret = odp_packet_l4_offset_set(pkt, l2_off);
+ CU_ASSERT(ret == 0);
+
+ /* Addresses should be the same */
+ l2_addr = odp_packet_l2_ptr(pkt, &seg_len);
+ CU_ASSERT(seg_len != 0);
+ l3_addr = odp_packet_l3_ptr(pkt, &seg_len);
+ CU_ASSERT(seg_len != 0);
+ l4_addr = odp_packet_l4_ptr(pkt, &seg_len);
+ CU_ASSERT(seg_len != 0);
+ CU_ASSERT(l2_addr != NULL);
+ CU_ASSERT(l2_addr == l3_addr);
+ CU_ASSERT(l2_addr == l4_addr);
+
+ /* Set offsets to the different values */
+ odp_packet_l2_offset_set(pkt, l2_off);
+ CU_ASSERT(odp_packet_l2_offset(pkt) == l2_off);
+ odp_packet_l3_offset_set(pkt, l3_off);
+ CU_ASSERT(odp_packet_l3_offset(pkt) == l3_off);
+ odp_packet_l4_offset_set(pkt, l4_off);
+ CU_ASSERT(odp_packet_l4_offset(pkt) == l4_off);
+
+ /* Addresses should not be the same */
+ l2_addr = odp_packet_l2_ptr(pkt, NULL);
+ CU_ASSERT(l2_addr != NULL);
+ l3_addr = odp_packet_l3_ptr(pkt, NULL);
+ CU_ASSERT(l3_addr != NULL);
+ l4_addr = odp_packet_l4_ptr(pkt, NULL);
+ CU_ASSERT(l4_addr != NULL);
+
+ CU_ASSERT(l2_addr != l3_addr);
+ CU_ASSERT(l2_addr != l4_addr);
+ CU_ASSERT(l3_addr != l4_addr);
+}
+
+static void _verify_headroom_shift(odp_packet_t *pkt,
+ int shift)
+{
+ uint32_t room = odp_packet_headroom(*pkt);
+ uint32_t seg_data_len = odp_packet_seg_len(*pkt);
+ uint32_t pkt_data_len = odp_packet_len(*pkt);
+ void *data = NULL;
+ char *data_orig = odp_packet_data(*pkt);
+ char *head_orig = odp_packet_head(*pkt);
+ uint32_t seg_len;
+ int extended, rc;
+
+ if (shift >= 0) {
+ if ((uint32_t)abs(shift) <= room) {
+ data = odp_packet_push_head(*pkt, shift);
+ extended = 0;
+ } else {
+ rc = odp_packet_extend_head(pkt, shift,
+ &data, &seg_len);
+ extended = 1;
+ }
+ } else {
+ if ((uint32_t)abs(shift) < seg_data_len) {
+ data = odp_packet_pull_head(*pkt, -shift);
+ extended = 0;
+ } else {
+ rc = odp_packet_trunc_head(pkt, -shift,
+ &data, &seg_len);
+ extended = 1;
+ }
+ }
+ packet_sanity_check(*pkt);
+
+ CU_ASSERT(data != NULL);
+ if (extended) {
+ CU_ASSERT(rc >= 0);
+ CU_ASSERT(odp_packet_seg_len(*pkt) == seg_len);
+ } else {
+ CU_ASSERT(odp_packet_headroom(*pkt) == room - shift);
+ CU_ASSERT(odp_packet_seg_len(*pkt) == seg_data_len + shift);
+ CU_ASSERT(data == data_orig - shift);
+ CU_ASSERT(odp_packet_head(*pkt) == head_orig);
+ }
+
+ CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift);
+ CU_ASSERT(odp_packet_data(*pkt) == data);
+}
+
+static void packet_test_headroom(void)
+{
+ odp_packet_t pkt = odp_packet_copy(test_packet,
+ odp_packet_pool(test_packet));
+ uint32_t room;
+ uint32_t seg_data_len;
+ uint32_t push_val, pull_val;
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ room = odp_packet_headroom(pkt);
+
+ CU_ASSERT(room >= pool_capa.pkt.min_headroom);
+
+ seg_data_len = odp_packet_seg_len(pkt);
+ CU_ASSERT(seg_data_len >= 1);
+
+ pull_val = seg_data_len / 2;
+ push_val = room;
+
+ _verify_headroom_shift(&pkt, -pull_val);
+ _verify_headroom_shift(&pkt, push_val + pull_val);
+ _verify_headroom_shift(&pkt, -push_val);
+ _verify_headroom_shift(&pkt, 0);
+
+ if (segmentation_supported) {
+ push_val = room * 2;
+ _verify_headroom_shift(&pkt, push_val);
+ _verify_headroom_shift(&pkt, 0);
+ _verify_headroom_shift(&pkt, -push_val);
+ }
+
+ odp_packet_free(pkt);
+}
+
+static void _verify_tailroom_shift(odp_packet_t *pkt,
+ int shift)
+{
+ odp_packet_seg_t seg;
+ uint32_t room;
+ uint32_t seg_data_len, pkt_data_len, seg_len;
+ void *tail;
+ char *tail_orig;
+ int extended, rc;
+
+ room = odp_packet_tailroom(*pkt);
+ pkt_data_len = odp_packet_len(*pkt);
+ tail_orig = odp_packet_tail(*pkt);
+
+ seg = odp_packet_last_seg(*pkt);
+ CU_ASSERT(seg != ODP_PACKET_SEG_INVALID);
+ seg_data_len = odp_packet_seg_data_len(*pkt, seg);
+
+ if (shift >= 0) {
+ uint32_t l2_off, l3_off, l4_off;
+
+ l2_off = odp_packet_l2_offset(*pkt);
+ l3_off = odp_packet_l3_offset(*pkt);
+ l4_off = odp_packet_l4_offset(*pkt);
+
+ if ((uint32_t)abs(shift) <= room) {
+ tail = odp_packet_push_tail(*pkt, shift);
+ extended = 0;
+ } else {
+ rc = odp_packet_extend_tail(pkt, shift,
+ &tail, &seg_len);
+ extended = 1;
+ }
+
+ CU_ASSERT(l2_off == odp_packet_l2_offset(*pkt));
+ CU_ASSERT(l3_off == odp_packet_l3_offset(*pkt));
+ CU_ASSERT(l4_off == odp_packet_l4_offset(*pkt));
+ } else {
+ if ((uint32_t)abs(shift) < seg_data_len) {
+ tail = odp_packet_pull_tail(*pkt, -shift);
+ extended = 0;
+ } else {
+ rc = odp_packet_trunc_tail(pkt, -shift,
+ &tail, &seg_len);
+ extended = 1;
+ }
+ }
+ packet_sanity_check(*pkt);
+
+ CU_ASSERT(tail != NULL);
+ if (extended) {
+ CU_ASSERT(rc >= 0);
+
+ if (shift >= 0) {
+ if (rc == 0)
+ CU_ASSERT(tail == tail_orig);
+ } else {
+ CU_ASSERT(odp_packet_tail(*pkt) == tail);
+ CU_ASSERT(odp_packet_tailroom(*pkt) == seg_len);
+ }
+ } else {
+ CU_ASSERT(odp_packet_seg_data_len(*pkt, seg) ==
+ seg_data_len + shift);
+ CU_ASSERT(odp_packet_tailroom(*pkt) == room - shift);
+ if (room == 0 || (room - shift) == 0)
+ return;
+ if (shift >= 0) {
+ CU_ASSERT(odp_packet_tail(*pkt) == tail_orig + shift);
+ CU_ASSERT(tail == tail_orig);
+ } else {
+ CU_ASSERT(odp_packet_tail(*pkt) == tail);
+ CU_ASSERT(tail == tail_orig + shift);
+ }
+ }
+
+ CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift);
+}
+
+static void packet_test_tailroom(void)
+{
+ odp_packet_t pkt = odp_packet_copy(test_packet,
+ odp_packet_pool(test_packet));
+ odp_packet_seg_t segment;
+ uint32_t room;
+ uint32_t seg_data_len;
+ uint32_t push_val, pull_val;
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ segment = odp_packet_last_seg(pkt);
+ CU_ASSERT(segment != ODP_PACKET_SEG_INVALID);
+ room = odp_packet_tailroom(pkt);
+ CU_ASSERT(room >= pool_capa.pkt.min_tailroom);
+
+ seg_data_len = odp_packet_seg_data_len(pkt, segment);
+ CU_ASSERT(seg_data_len >= 1);
+
+ pull_val = seg_data_len / 2;
+ /* Leave one byte in a tailroom for odp_packet_tail() to succeed */
+ push_val = (room > 0) ? room - 1 : room;
+
+ _verify_tailroom_shift(&pkt, -pull_val);
+ _verify_tailroom_shift(&pkt, push_val + pull_val);
+ _verify_tailroom_shift(&pkt, -push_val);
+ _verify_tailroom_shift(&pkt, 0);
+
+ if (segmentation_supported) {
+ push_val = room + 100;
+ _verify_tailroom_shift(&pkt, push_val);
+ _verify_tailroom_shift(&pkt, 0);
+ _verify_tailroom_shift(&pkt, -push_val);
+ }
+
+ odp_packet_free(pkt);
+}
+
+static void packet_test_segments(void)
+{
+ int num_segs, seg_index;
+ uint32_t data_len;
+ odp_packet_seg_t seg;
+ odp_packet_t pkt = test_packet;
+ odp_packet_t seg_pkt = segmented_test_packet;
+
+ CU_ASSERT(odp_packet_is_valid(pkt) == 1);
+
+ num_segs = odp_packet_num_segs(pkt);
+ CU_ASSERT(num_segs != 0);
+
+ if (odp_packet_is_segmented(pkt)) {
+ CU_ASSERT(num_segs > 1);
+ } else {
+ CU_ASSERT(num_segs == 1);
+ }
+
+ CU_ASSERT(odp_packet_is_segmented(pkt) == 0);
+ if (segmentation_supported)
+ CU_ASSERT(odp_packet_is_segmented(seg_pkt) == 1);
+
+ seg = odp_packet_first_seg(pkt);
+ data_len = 0;
+ seg_index = 0;
+ while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) {
+ uint32_t seg_data_len;
+ void *seg_data;
+
+ seg_data_len = odp_packet_seg_data_len(pkt, seg);
+ seg_data = odp_packet_seg_data(pkt, seg);
+
+ CU_ASSERT(seg_data_len > 0);
+ CU_ASSERT(seg_data != NULL);
+ CU_ASSERT(odp_packet_seg_to_u64(seg) !=
+ odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID));
+ CU_ASSERT(odp_memcmp(seg_data, seg_data, seg_data_len) == 0);
+
+ data_len += seg_data_len;
+
+ seg_index++;
+ seg = odp_packet_next_seg(pkt, seg);
+ }
+
+ CU_ASSERT(seg_index == num_segs);
+ CU_ASSERT(data_len <= odp_packet_buf_len(pkt));
+ CU_ASSERT(data_len == odp_packet_len(pkt));
+
+ if (seg_index == num_segs)
+ CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
+
+ seg = odp_packet_first_seg(seg_pkt);
+ num_segs = odp_packet_num_segs(seg_pkt);
+
+ data_len = 0;
+ seg_index = 0;
+
+ while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) {
+ uint32_t seg_data_len;
+ void *seg_data;
+
+ seg_data_len = odp_packet_seg_data_len(seg_pkt, seg);
+ seg_data = odp_packet_seg_data(seg_pkt, seg);
+
+ CU_ASSERT(seg_data_len > 0);
+ CU_ASSERT(seg_data != NULL);
+ CU_ASSERT(odp_packet_seg_to_u64(seg) !=
+ odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID));
+ CU_ASSERT(odp_memcmp(seg_data, seg_data, seg_data_len) == 0);
+
+ data_len += seg_data_len;
+
+ seg_index++;
+ seg = odp_packet_next_seg(seg_pkt, seg);
+ }
+
+ CU_ASSERT(seg_index == num_segs);
+ CU_ASSERT(data_len <= odp_packet_buf_len(seg_pkt));
+ CU_ASSERT(data_len == odp_packet_len(seg_pkt));
+
+ if (seg_index == num_segs)
+ CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
+}
+
+static void packet_test_segment_last(void)
+{
+ odp_packet_t pkt = test_packet;
+ odp_packet_seg_t seg;
+
+ seg = odp_packet_last_seg(pkt);
+ CU_ASSERT_FATAL(seg != ODP_PACKET_SEG_INVALID);
+
+ seg = odp_packet_next_seg(pkt, seg);
+ CU_ASSERT(seg == ODP_PACKET_SEG_INVALID);
+}
+
+#define TEST_INFLAG(packet, flag) \
+do { \
+ odp_packet_##flag##_set(packet, 0); \
+ CU_ASSERT(odp_packet_##flag(packet) == 0); \
+ odp_packet_##flag##_set(packet, 1); \
+ CU_ASSERT(odp_packet_##flag(packet) != 0); \
+} while (0)
+
+static void packet_test_in_flags(void)
+{
+ odp_packet_t pkt = test_packet;
+
+ packet_set_inflags_common(pkt, 0);
+ packet_check_inflags_common(pkt, 0);
+ packet_set_inflags_common(pkt, 1);
+ packet_check_inflags_common(pkt, 1);
+
+ TEST_INFLAG(pkt, has_l2);
+ TEST_INFLAG(pkt, has_l3);
+ TEST_INFLAG(pkt, has_l4);
+ TEST_INFLAG(pkt, has_eth);
+ TEST_INFLAG(pkt, has_eth_bcast);
+ TEST_INFLAG(pkt, has_eth_mcast);
+ TEST_INFLAG(pkt, has_jumbo);
+ TEST_INFLAG(pkt, has_vlan);
+ TEST_INFLAG(pkt, has_vlan_qinq);
+ TEST_INFLAG(pkt, has_arp);
+ TEST_INFLAG(pkt, has_ipv4);
+ TEST_INFLAG(pkt, has_ipv6);
+ TEST_INFLAG(pkt, has_ip_bcast);
+ TEST_INFLAG(pkt, has_ip_mcast);
+ TEST_INFLAG(pkt, has_ipfrag);
+ TEST_INFLAG(pkt, has_ipopt);
+ TEST_INFLAG(pkt, has_ipsec);
+ TEST_INFLAG(pkt, has_udp);
+ TEST_INFLAG(pkt, has_tcp);
+ TEST_INFLAG(pkt, has_sctp);
+ TEST_INFLAG(pkt, has_icmp);
+ TEST_INFLAG(pkt, user_flag);
+
+ packet_set_inflags_common(pkt, 0);
+ packet_check_inflags_common(pkt, 0);
+}
+
+static void packet_test_vlan_flags(void)
+{
+ odp_packet_t pkt = test_packet;
+
+ odp_packet_reset(pkt, odp_packet_len(test_packet));
+
+ CU_ASSERT(!odp_packet_has_vlan(pkt));
+ CU_ASSERT(!odp_packet_has_vlan_qinq(pkt));
+
+ odp_packet_has_vlan_qinq_set(pkt, 1);
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_vlan_qinq(pkt));
+
+ odp_packet_has_vlan_qinq_set(pkt, 0);
+ CU_ASSERT(!odp_packet_has_vlan(pkt));
+ CU_ASSERT(!odp_packet_has_vlan_qinq(pkt));
+
+ odp_packet_has_vlan_set(pkt, 1);
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(!odp_packet_has_vlan_qinq(pkt));
+
+ odp_packet_reset(pkt, odp_packet_len(test_packet));
+}
+
+static void packet_test_error_flags(void)
+{
+ odp_packet_t pkt = test_packet;
+ int err;
+
+ /**
+ * The packet have not been classified so it doesn't have error flags
+ * properly set. Just check that functions return one of allowed values.
+ */
+ err = odp_packet_has_error(pkt);
+ CU_ASSERT(err == 0 || err == 1);
+
+ err = odp_packet_has_l2_error(pkt);
+ CU_ASSERT(err == 0 || err == 1);
+
+ err = odp_packet_has_l3_error(pkt);
+ CU_ASSERT(err == 0 || err == 1);
+
+ err = odp_packet_has_l4_error(pkt);
+ CU_ASSERT(err == 0 || err == 1);
+}
+
+struct packet_metadata {
+ uint32_t l2_off;
+ uint32_t l3_off;
+ uint32_t l4_off;
+ void *usr_ptr;
+ uint64_t usr_u64;
+};
+
+static void packet_test_add_rem_data(void)
+{
+ odp_packet_t pkt, new_pkt;
+ uint32_t pkt_len, offset, add_len;
+ void *usr_ptr;
+ struct udata_struct *udat;
+ int ret;
+ uint32_t min_seg_len;
+ uint32_t uarea_size = default_param.pkt.uarea_size;
+
+ min_seg_len = pool_capa.pkt.min_seg_len;
+
+ pkt = odp_packet_alloc(default_pool, packet_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ pkt_len = odp_packet_len(pkt);
+ usr_ptr = odp_packet_user_ptr(pkt);
+
+ if (uarea_size) {
+ udat = odp_packet_user_area(pkt);
+
+ CU_ASSERT_FATAL(udat != NULL);
+ CU_ASSERT_FATAL(odp_packet_user_area_size(pkt) >= uarea_size);
+ memcpy(udat, &test_packet_udata, uarea_size);
+ }
+
+ offset = pkt_len / 2;
+
+ if (segmentation_supported) {
+ /* Insert one more packet length in the middle of a packet */
+ add_len = min_seg_len;
+ } else {
+ /* Add diff between largest and smaller packets
+ * which is at least tailroom */
+ add_len = segmented_packet_len - packet_len;
+ }
+
+ new_pkt = pkt;
+ ret = odp_packet_add_data(&new_pkt, offset, add_len);
+ CU_ASSERT(ret >= 0);
+ if (ret < 0)
+ goto free_packet;
+ packet_sanity_check(new_pkt);
+ CU_ASSERT(odp_packet_len(new_pkt) == pkt_len + add_len);
+ /* Verify that user metadata is preserved */
+ CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr);
+
+ if (uarea_size) {
+ /* Verify that user metadata has been preserved */
+ udat = odp_packet_user_area(new_pkt);
+
+ CU_ASSERT_FATAL(udat != NULL);
+ CU_ASSERT(odp_packet_user_area_size(new_pkt) >= uarea_size);
+ CU_ASSERT(memcmp(udat, &test_packet_udata, uarea_size) == 0);
+ }
+
+ pkt = new_pkt;
+
+ pkt_len = odp_packet_len(pkt);
+ usr_ptr = odp_packet_user_ptr(pkt);
+
+ ret = odp_packet_rem_data(&new_pkt, offset, add_len);
+ CU_ASSERT(ret >= 0);
+ if (ret < 0)
+ goto free_packet;
+ packet_sanity_check(new_pkt);
+ CU_ASSERT(odp_packet_len(new_pkt) == pkt_len - add_len);
+ CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr);
+
+ if (uarea_size) {
+ /* Verify that user metadata has been preserved */
+ udat = odp_packet_user_area(new_pkt);
+
+ CU_ASSERT(udat != NULL);
+ CU_ASSERT(odp_packet_user_area_size(new_pkt) >= uarea_size);
+ CU_ASSERT(memcmp(udat, &test_packet_udata, uarea_size) == 0);
+ }
+
+ pkt = new_pkt;
+
+free_packet:
+ odp_packet_free(pkt);
+}
+
+#define COMPARE_INFLAG(p1, p2, flag) \
+ CU_ASSERT(odp_packet_##flag(p1) == odp_packet_##flag(p2))
+
+static void packet_compare_inflags(odp_packet_t pkt1, odp_packet_t pkt2)
+{
+ COMPARE_INFLAG(pkt1, pkt2, has_l2);
+ COMPARE_INFLAG(pkt1, pkt2, has_l3);
+ COMPARE_INFLAG(pkt1, pkt2, has_l4);
+ COMPARE_INFLAG(pkt1, pkt2, has_eth);
+ COMPARE_INFLAG(pkt1, pkt2, has_eth_bcast);
+ COMPARE_INFLAG(pkt1, pkt2, has_eth_mcast);
+ COMPARE_INFLAG(pkt1, pkt2, has_jumbo);
+ COMPARE_INFLAG(pkt1, pkt2, has_vlan);
+ COMPARE_INFLAG(pkt1, pkt2, has_vlan_qinq);
+ COMPARE_INFLAG(pkt1, pkt2, has_arp);
+ COMPARE_INFLAG(pkt1, pkt2, has_ipv4);
+ COMPARE_INFLAG(pkt1, pkt2, has_ipv6);
+ COMPARE_INFLAG(pkt1, pkt2, has_ip_bcast);
+ COMPARE_INFLAG(pkt1, pkt2, has_ip_mcast);
+ COMPARE_INFLAG(pkt1, pkt2, has_ipfrag);
+ COMPARE_INFLAG(pkt1, pkt2, has_ipopt);
+ COMPARE_INFLAG(pkt1, pkt2, has_ipsec);
+ COMPARE_INFLAG(pkt1, pkt2, has_udp);
+ COMPARE_INFLAG(pkt1, pkt2, has_tcp);
+ COMPARE_INFLAG(pkt1, pkt2, has_sctp);
+ COMPARE_INFLAG(pkt1, pkt2, has_icmp);
+ COMPARE_INFLAG(pkt1, pkt2, user_flag);
+ COMPARE_INFLAG(pkt1, pkt2, has_flow_hash);
+ COMPARE_INFLAG(pkt1, pkt2, has_ts);
+
+ COMPARE_INFLAG(pkt1, pkt2, color);
+ COMPARE_INFLAG(pkt1, pkt2, drop_eligible);
+ COMPARE_INFLAG(pkt1, pkt2, shaper_len_adjust);
+}
+
+static void packet_compare_udata(odp_packet_t pkt1, odp_packet_t pkt2)
+{
+ uint32_t usize1 = odp_packet_user_area_size(pkt1);
+ uint32_t usize2 = odp_packet_user_area_size(pkt2);
+
+ void *uaddr1 = odp_packet_user_area(pkt1);
+ void *uaddr2 = odp_packet_user_area(pkt2);
+
+ uint32_t cmplen = usize1 <= usize2 ? usize1 : usize2;
+
+ if (cmplen)
+ CU_ASSERT(!memcmp(uaddr1, uaddr2, cmplen));
+}
+
+static void _packet_compare_offset(odp_packet_t pkt1, uint32_t off1,
+ odp_packet_t pkt2, uint32_t off2,
+ uint32_t len, int line)
+{
+ void *pkt1map, *pkt2map;
+ uint32_t seglen1, seglen2, cmplen;
+ int ret;
+
+ if (off1 + len > odp_packet_len(pkt1) ||
+ off2 + len > odp_packet_len(pkt2))
+ return;
+
+ while (len > 0) {
+ seglen1 = 0;
+ seglen2 = 0;
+ pkt1map = odp_packet_offset(pkt1, off1, &seglen1, NULL);
+ pkt2map = odp_packet_offset(pkt2, off2, &seglen2, NULL);
+
+ CU_ASSERT_FATAL(pkt1map != NULL);
+ CU_ASSERT_FATAL(pkt2map != NULL);
+ cmplen = seglen1 < seglen2 ? seglen1 : seglen2;
+ if (len < cmplen)
+ cmplen = len;
+
+ ret = memcmp(pkt1map, pkt2map, cmplen);
+
+ if (ret) {
+ printf("\ncompare_offset failed: line %i, off1 %"
+ PRIu32 ", off2 %" PRIu32 "\n", line, off1, off2);
+ }
+
+ CU_ASSERT(ret == 0);
+
+ off1 += cmplen;
+ off2 += cmplen;
+ len -= cmplen;
+ }
+}
+
+static void packet_test_meta_data_copy(void)
+{
+ odp_packet_t pkt, copy;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ odp_pktio_t pktio;
+ odp_time_t t1, t2;
+
+ memcpy(&pool_param, &default_param, sizeof(odp_pool_param_t));
+ pool = odp_pool_create("meta_data_copy", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pktio = odp_pktio_open("loop", pool, NULL);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ t1 = odp_time_global();
+
+ pkt = odp_packet_alloc(pool, packet_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ packet_check_inflags_all(pkt, 0);
+
+ CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_l3_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+ CU_ASSERT(odp_packet_l4_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+
+ packet_set_inflags_common(pkt, 1);
+ packet_check_inflags_common(pkt, 1);
+
+ odp_packet_input_set(pkt, pktio);
+ odp_packet_user_ptr_set(pkt, (void *)(uintptr_t)0xdeadbeef);
+ CU_ASSERT(odp_packet_l2_offset_set(pkt, 20) == 0);
+ CU_ASSERT(odp_packet_l3_offset_set(pkt, 30) == 0);
+ CU_ASSERT(odp_packet_l4_offset_set(pkt, 40) == 0);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, 50) == 0);
+ odp_packet_flow_hash_set(pkt, 0xcafe);
+ odp_packet_ts_set(pkt, t1);
+ odp_packet_color_set(pkt, ODP_PACKET_RED);
+ odp_packet_drop_eligible_set(pkt, 1);
+ odp_packet_shaper_len_adjust_set(pkt, 1);
+
+ /* Make a copy of the packet and check that meta data values are the same */
+ copy = odp_packet_copy(pkt, pool);
+ CU_ASSERT_FATAL(copy != ODP_PACKET_INVALID);
+
+ packet_compare_inflags(pkt, copy);
+ CU_ASSERT(odp_packet_input(copy) == pktio);
+ CU_ASSERT(odp_packet_user_ptr(copy) == (void *)(uintptr_t)0xdeadbeef);
+ CU_ASSERT(odp_packet_l2_offset(copy) == 20);
+ CU_ASSERT(odp_packet_l3_offset(copy) == 30);
+ CU_ASSERT(odp_packet_l4_offset(copy) == 40);
+ CU_ASSERT(odp_packet_payload_offset(copy) == 50);
+ CU_ASSERT(odp_packet_flow_hash(copy) == 0xcafe);
+ t2 = odp_packet_ts(copy);
+ CU_ASSERT(odp_time_cmp(t2, t1) == 0);
+ CU_ASSERT(odp_packet_color(copy) == ODP_PACKET_RED);
+ CU_ASSERT(odp_packet_drop_eligible(copy) == 1);
+ CU_ASSERT(odp_packet_shaper_len_adjust(copy) == 1);
+
+ odp_packet_free(pkt);
+ odp_packet_free(copy);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_copy(void)
+{
+ odp_packet_t pkt;
+ odp_packet_t pkt_part;
+ odp_pool_param_t param;
+ odp_pool_t pool, pool_min_uarea, pool_large_uarea;
+ void *pkt_data;
+ uint32_t i, plen, src_offset, dst_offset, uarea_size;
+ uint32_t seg_len = 0;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+
+ param.pkt.uarea_size = 0;
+ pool_min_uarea = odp_pool_create("min_uarea", &param);
+ CU_ASSERT_FATAL(pool_min_uarea != ODP_POOL_INVALID);
+
+ uarea_size = 2 * sizeof(struct udata_struct);
+ if (uarea_size > pool_capa.pkt.max_uarea_size)
+ uarea_size = pool_capa.pkt.max_uarea_size;
+
+ param.pkt.uarea_size = uarea_size;
+
+ pool_large_uarea = odp_pool_create("large_uarea", &param);
+ CU_ASSERT_FATAL(pool_large_uarea != ODP_POOL_INVALID);
+
+ /* Pool with minimal user area */
+ pkt = odp_packet_copy(test_packet, pool_min_uarea);
+ if (pkt != ODP_PACKET_INVALID) {
+ /* Pool has enough user area also when zero was requested */
+ CU_ASSERT(odp_packet_user_area_size(pkt) >= sizeof(struct udata_struct));
+
+ packet_compare_inflags(pkt, test_packet);
+ packet_compare_udata(pkt, test_packet);
+ packet_compare_data(pkt, test_packet);
+
+ odp_packet_free(pkt);
+ }
+
+ /* The same pool */
+ pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(pkt != test_packet);
+ CU_ASSERT(odp_packet_pool(pkt) == odp_packet_pool(test_packet));
+ CU_ASSERT(odp_packet_user_area(pkt) != odp_packet_user_area(test_packet));
+ CU_ASSERT(odp_packet_user_area_size(pkt) == odp_packet_user_area_size(test_packet));
+ CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(test_packet));
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(test_packet));
+
+ packet_compare_inflags(pkt, test_packet);
+ packet_compare_udata(pkt, test_packet);
+ packet_compare_data(pkt, test_packet);
+
+ odp_packet_free(pkt);
+
+ /* Pool with larger user area */
+ pkt = odp_packet_copy(test_packet, pool_large_uarea);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(pkt != test_packet);
+ CU_ASSERT(odp_packet_pool(pkt) == pool_large_uarea);
+ CU_ASSERT(odp_packet_user_area(pkt) != odp_packet_user_area(test_packet));
+ CU_ASSERT(odp_packet_user_area_size(pkt) >= uarea_size);
+ CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(test_packet));
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(test_packet));
+
+ packet_compare_inflags(pkt, test_packet);
+ packet_compare_udata(pkt, test_packet);
+ packet_compare_data(pkt, test_packet);
+
+ /* Now test copy_part */
+ pool = pool_large_uarea;
+ pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt) + 1, pool);
+ CU_ASSERT(pkt_part == ODP_PACKET_INVALID);
+ pkt_part = odp_packet_copy_part(pkt, odp_packet_len(pkt), 1, pool);
+ CU_ASSERT(pkt_part == ODP_PACKET_INVALID);
+
+ pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt), pool);
+ CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID);
+ CU_ASSERT(pkt != pkt_part);
+ CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_part));
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_part));
+
+ packet_compare_data(pkt, pkt_part);
+ odp_packet_free(pkt_part);
+
+ plen = odp_packet_len(pkt);
+ for (i = 0; i < plen / 2; i += 5) {
+ pkt_part = odp_packet_copy_part(pkt, i, plen / 4, pool);
+ CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(pkt_part) == plen / 4);
+ packet_compare_offset(pkt_part, 0, pkt, i, plen / 4);
+ odp_packet_free(pkt_part);
+ }
+
+ /* Test copy and move apis */
+ CU_ASSERT(odp_packet_copy_data(pkt, 0, plen - plen / 8, plen / 8) == 0);
+ packet_compare_offset(pkt, 0, pkt, plen - plen / 8, plen / 8);
+ packet_compare_offset(pkt, 0, test_packet, plen - plen / 8, plen / 8);
+
+ /* Test segment crossing if we support segments */
+ pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL);
+ CU_ASSERT_FATAL(pkt_data != NULL);
+
+ if (seg_len < plen) {
+ src_offset = seg_len - 15;
+ dst_offset = seg_len - 5;
+ } else {
+ src_offset = seg_len - 40;
+ dst_offset = seg_len - 25;
+ }
+
+ pkt_part = odp_packet_copy_part(pkt, src_offset, 20, pool);
+ CU_ASSERT(odp_packet_move_data(pkt, dst_offset, src_offset, 20) == 0);
+ packet_compare_offset(pkt, dst_offset, pkt_part, 0, 20);
+
+ odp_packet_free(pkt_part);
+ odp_packet_free(pkt);
+
+ CU_ASSERT(odp_pool_destroy(pool_min_uarea) == 0);
+ CU_ASSERT(odp_pool_destroy(pool_large_uarea) == 0);
+}
+
+static void packet_test_copydata(void)
+{
+ odp_packet_t pkt = test_packet;
+ uint32_t pkt_len = odp_packet_len(pkt);
+ uint8_t *data_buf;
+ uint32_t i;
+ int correct_memory;
+
+ CU_ASSERT_FATAL(pkt_len > 0);
+
+ data_buf = malloc(pkt_len);
+ CU_ASSERT_FATAL(data_buf != NULL);
+
+ for (i = 0; i < pkt_len; i++)
+ data_buf[i] = (uint8_t)i;
+
+ CU_ASSERT(!odp_packet_copy_from_mem(pkt, 0, pkt_len, data_buf));
+ memset(data_buf, 0, pkt_len);
+ CU_ASSERT(!odp_packet_copy_to_mem(pkt, 0, pkt_len, data_buf));
+
+ correct_memory = 1;
+ for (i = 0; i < pkt_len; i++)
+ if (data_buf[i] != (uint8_t)i) {
+ correct_memory = 0;
+ break;
+ }
+ CU_ASSERT(correct_memory);
+
+ free(data_buf);
+
+ pkt = odp_packet_alloc(odp_packet_pool(test_packet), pkt_len / 2);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, test_packet, 0,
+ pkt_len) < 0);
+ CU_ASSERT(odp_packet_copy_from_pkt(pkt, pkt_len, test_packet, 0,
+ 1) < 0);
+
+ for (i = 0; i < pkt_len / 2; i++) {
+ CU_ASSERT(odp_packet_copy_from_pkt(pkt, i, test_packet, i,
+ 1) == 0);
+ }
+
+ packet_compare_offset(pkt, 0, test_packet, 0, pkt_len / 2);
+ odp_packet_free(pkt);
+
+ pkt = odp_packet_alloc(odp_packet_pool(segmented_test_packet),
+ odp_packet_len(segmented_test_packet) / 2);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, segmented_test_packet,
+ odp_packet_len(pkt) / 4,
+ odp_packet_len(pkt)) == 0);
+ packet_compare_offset(pkt, 0, segmented_test_packet,
+ odp_packet_len(pkt) / 4,
+ odp_packet_len(pkt));
+ odp_packet_free(pkt);
+}
+
+static void packet_test_concatsplit(void)
+{
+ odp_packet_t pkt, pkt2;
+ uint32_t pkt_len;
+ odp_packet_t splits[4] = {ODP_PACKET_INVALID};
+ odp_pool_t pool;
+
+ pool = odp_packet_pool(test_packet);
+ pkt = odp_packet_copy(test_packet, pool);
+ pkt2 = odp_packet_copy(test_packet, pool);
+ pkt_len = odp_packet_len(test_packet);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
+ CU_ASSERT(pkt_len == odp_packet_len(pkt));
+ CU_ASSERT(pkt_len == odp_packet_len(pkt2));
+
+ CU_ASSERT(odp_packet_concat(&pkt, pkt2) >= 0);
+ CU_ASSERT(odp_packet_len(pkt) == pkt_len * 2);
+ packet_sanity_check(pkt);
+ packet_compare_offset(pkt, 0, pkt, pkt_len, pkt_len);
+
+ CU_ASSERT(odp_packet_split(&pkt, pkt_len, &pkt2) == 0);
+ CU_ASSERT(pkt != pkt2);
+ CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt2));
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt2));
+ packet_sanity_check(pkt);
+ packet_sanity_check(pkt2);
+ packet_compare_data(pkt, pkt2);
+ packet_compare_data(pkt, test_packet);
+
+ odp_packet_free(pkt);
+ odp_packet_free(pkt2);
+
+ pkt = odp_packet_copy(segmented_test_packet,
+ odp_packet_pool(segmented_test_packet));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pkt_len = odp_packet_len(pkt);
+ packet_sanity_check(pkt);
+ packet_compare_data(pkt, segmented_test_packet);
+
+ CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0);
+ CU_ASSERT(pkt != splits[0]);
+ CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(splits[0]));
+ CU_ASSERT(odp_packet_len(pkt) == pkt_len / 2);
+ CU_ASSERT(odp_packet_len(pkt) + odp_packet_len(splits[0]) == pkt_len);
+ packet_sanity_check(pkt);
+ packet_sanity_check(splits[0]);
+ packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2);
+ packet_compare_offset(splits[0], 0, segmented_test_packet,
+ pkt_len / 2, odp_packet_len(splits[0]));
+
+ CU_ASSERT(odp_packet_concat(&pkt, splits[0]) >= 0);
+ packet_sanity_check(pkt);
+ packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2);
+ packet_compare_offset(pkt, pkt_len / 2, segmented_test_packet,
+ pkt_len / 2, pkt_len / 2);
+ packet_compare_offset(pkt, 0, segmented_test_packet, 0,
+ pkt_len);
+
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet));
+ packet_compare_data(pkt, segmented_test_packet);
+
+ CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_split(&pkt, pkt_len / 4, &splits[1]) == 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_split(&pkt, pkt_len / 8, &splits[2]) == 0);
+ packet_sanity_check(pkt);
+
+ packet_sanity_check(splits[0]);
+ packet_sanity_check(splits[1]);
+ packet_sanity_check(splits[2]);
+ CU_ASSERT(odp_packet_len(splits[0]) + odp_packet_len(splits[1]) +
+ odp_packet_len(splits[2]) + odp_packet_len(pkt) == pkt_len);
+
+ CU_ASSERT(odp_packet_concat(&pkt, splits[2]) >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_concat(&pkt, splits[1]) >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_concat(&pkt, splits[0]) >= 0);
+ packet_sanity_check(pkt);
+
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet));
+ packet_compare_data(pkt, segmented_test_packet);
+
+ odp_packet_free(pkt);
+}
+
+static void packet_test_concat_small(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_packet_t pkt, pkt2;
+ int ret;
+ uint8_t *data;
+ uint32_t i;
+ uint32_t len = PACKET_POOL_NUM / 4;
+ uint8_t buf[len];
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = PACKET_POOL_NUM;
+
+ pool = odp_pool_create("packet_pool_concat", &param);
+ CU_ASSERT(pool != ODP_POOL_INVALID);
+
+ pkt = odp_packet_alloc(pool, 1);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ data = odp_packet_data(pkt);
+ *data = 0;
+
+ for (i = 0; i < len - 1; i++) {
+ pkt2 = odp_packet_alloc(pool, 1);
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
+
+ data = odp_packet_data(pkt2);
+ *data = i + 1;
+
+ ret = odp_packet_concat(&pkt, pkt2);
+ CU_ASSERT(ret >= 0);
+
+ if (ret < 0) {
+ odp_packet_free(pkt2);
+ break;
+ }
+
+ if (packet_sanity_check(pkt))
+ break;
+ }
+
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ len = odp_packet_len(pkt);
+
+ memset(buf, 0, len);
+ CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
+
+ for (i = 0; i < len; i++)
+ CU_ASSERT(buf[i] == (i % 256));
+
+ odp_packet_free(pkt);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_concat_extend_trunc(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_packet_t pkt, pkt2;
+ int i, ret;
+ uint32_t alloc_len, ext_len, trunc_len, cur_len;
+ uint32_t len = 1900;
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ alloc_len = len / 8;
+ ext_len = len / 4;
+ trunc_len = len / 3;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = PACKET_POOL_NUM;
+
+ pool = odp_pool_create("packet_pool_concat", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pkt = odp_packet_alloc(pool, alloc_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ cur_len = odp_packet_len(pkt);
+
+ for (i = 0; i < 2; i++) {
+ pkt2 = odp_packet_alloc(pool, alloc_len);
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
+
+ ret = odp_packet_concat(&pkt, pkt2);
+ CU_ASSERT(ret >= 0);
+ packet_sanity_check(pkt);
+
+ if (ret < 0)
+ odp_packet_free(pkt2);
+
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len + alloc_len));
+ cur_len = odp_packet_len(pkt);
+ }
+
+ ret = odp_packet_extend_tail(&pkt, ext_len, NULL, NULL);
+ CU_ASSERT(ret >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len + ext_len));
+ cur_len = odp_packet_len(pkt);
+
+ ret = odp_packet_extend_head(&pkt, ext_len, NULL, NULL);
+ CU_ASSERT(ret >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len + ext_len));
+ cur_len = odp_packet_len(pkt);
+
+ pkt2 = odp_packet_alloc(pool, alloc_len);
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID);
+
+ ret = odp_packet_concat(&pkt, pkt2);
+ CU_ASSERT(ret >= 0);
+
+ if (ret < 0)
+ odp_packet_free(pkt2);
+
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len + alloc_len));
+ cur_len = odp_packet_len(pkt);
+
+ ret = odp_packet_trunc_head(&pkt, trunc_len, NULL, NULL);
+ CU_ASSERT(ret >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len - trunc_len));
+ cur_len = odp_packet_len(pkt);
+
+ ret = odp_packet_trunc_tail(&pkt, trunc_len, NULL, NULL);
+ CU_ASSERT(ret >= 0);
+ packet_sanity_check(pkt);
+ CU_ASSERT(odp_packet_len(pkt) == (cur_len - trunc_len));
+ cur_len = odp_packet_len(pkt);
+
+ odp_packet_free(pkt);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_extend_small(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_packet_t pkt;
+ int ret, round;
+ uint8_t *data;
+ uint32_t i, seg_len;
+ int tail = 1;
+ uint32_t len = 32000;
+ uint8_t buf[len];
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = PACKET_POOL_NUM;
+
+ pool = odp_pool_create("packet_pool_extend", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (round = 0; round < 2; round++) {
+ pkt = odp_packet_alloc(pool, 1);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ data = odp_packet_data(pkt);
+ *data = 0;
+
+ for (i = 0; i < len - 1; i++) {
+ if (tail) {
+ ret = odp_packet_extend_tail(&pkt, 1,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ } else {
+ ret = odp_packet_extend_head(&pkt, 1,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ }
+
+ if (ret < 0)
+ break;
+
+ if (packet_sanity_check(pkt))
+ break;
+
+ if (tail) {
+ /* assert needs brackets */
+ CU_ASSERT(seg_len == 1);
+ } else {
+ CU_ASSERT(seg_len > 0);
+ }
+
+ *data = i + 1;
+ }
+
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ len = odp_packet_len(pkt);
+
+ memset(buf, 0, len);
+ CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
+
+ for (i = 0; i < len; i++) {
+ int match;
+
+ if (tail) {
+ match = (buf[i] == (i % 256));
+ CU_ASSERT(match);
+ } else {
+ match = (buf[len - 1 - i] == (i % 256));
+ CU_ASSERT(match);
+ }
+
+ /* Limit the number of failed asserts to
+ one per packet */
+ if (!match)
+ break;
+ }
+
+ odp_packet_free(pkt);
+
+ tail = 0;
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_extend_large(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_packet_t pkt;
+ int ret, round;
+ uint8_t *data;
+ uint32_t i, seg_len, ext_len, cur_len, cur_data;
+ int tail = 1;
+ int num_div = 16;
+ int div = 1;
+ uint32_t len = 32000;
+ uint8_t buf[len];
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = PACKET_POOL_NUM;
+
+ pool = odp_pool_create("packet_pool_extend", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (round = 0; round < 2 * num_div; round++) {
+ ext_len = len / div;
+ cur_len = ext_len;
+
+ pkt = odp_packet_alloc(pool, ext_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ cur_data = 0;
+
+ if (tail) {
+ ret = fill_data_forward(pkt, 0, ext_len, &cur_data);
+ CU_ASSERT(ret == 0);
+ } else {
+ ret = fill_data_backward(pkt, 0, ext_len, &cur_data);
+ CU_ASSERT(ret == 0);
+ }
+
+ while (cur_len < len) {
+ if ((len - cur_len) < ext_len)
+ ext_len = len - cur_len;
+
+ if (tail) {
+ ret = odp_packet_extend_tail(&pkt, ext_len,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ } else {
+ ret = odp_packet_extend_head(&pkt, ext_len,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ }
+
+ if (ret < 0)
+ break;
+
+ if (packet_sanity_check(pkt))
+ break;
+
+ if (tail) {
+ /* assert needs brackets */
+ CU_ASSERT((seg_len > 0) &&
+ (seg_len <= ext_len));
+ ret = fill_data_forward(pkt, cur_len, ext_len,
+ &cur_data);
+ CU_ASSERT(ret == 0);
+ } else {
+ CU_ASSERT(seg_len > 0);
+ CU_ASSERT(data == odp_packet_data(pkt));
+ ret = fill_data_backward(pkt, 0, ext_len,
+ &cur_data);
+ CU_ASSERT(ret == 0);
+ }
+
+ cur_len += ext_len;
+ }
+
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ len = odp_packet_len(pkt);
+
+ memset(buf, 0, len);
+ CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
+
+ for (i = 0; i < len; i++) {
+ int match;
+
+ if (tail) {
+ match = (buf[i] == (i % 256));
+ CU_ASSERT(match);
+ } else {
+ match = (buf[len - 1 - i] == (i % 256));
+ CU_ASSERT(match);
+ }
+
+ /* Limit the number of failed asserts to
+ one per packet */
+ if (!match)
+ break;
+ }
+
+ odp_packet_free(pkt);
+
+ div++;
+ if (div > num_div) {
+ /* test extend head */
+ div = 1;
+ tail = 0;
+ }
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_extend_mix(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_packet_t pkt;
+ int ret, round;
+ uint8_t *data;
+ uint32_t i, seg_len, ext_len, cur_len, cur_data;
+ int small_count;
+ int tail = 1;
+ uint32_t len = 32000;
+ uint8_t buf[len];
+
+ if (pool_capa.pkt.max_len && pool_capa.pkt.max_len < len)
+ len = pool_capa.pkt.max_len;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = PACKET_POOL_NUM;
+
+ pool = odp_pool_create("packet_pool_extend", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (round = 0; round < 2; round++) {
+ small_count = 30;
+ ext_len = len / 10;
+ cur_len = ext_len;
+
+ pkt = odp_packet_alloc(pool, ext_len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ cur_data = 0;
+
+ if (tail) {
+ ret = fill_data_forward(pkt, 0, ext_len, &cur_data);
+ CU_ASSERT(ret == 0);
+ } else {
+ ret = fill_data_backward(pkt, 0, ext_len, &cur_data);
+ CU_ASSERT(ret == 0);
+ }
+
+ while (cur_len < len) {
+ if (small_count) {
+ small_count--;
+ ext_len = len / 100;
+ } else {
+ ext_len = len / 4;
+ }
+
+ if ((len - cur_len) < ext_len)
+ ext_len = len - cur_len;
+
+ if (tail) {
+ ret = odp_packet_extend_tail(&pkt, ext_len,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT((seg_len > 0) &&
+ (seg_len <= ext_len));
+ ret = fill_data_forward(pkt, cur_len, ext_len,
+ &cur_data);
+ CU_ASSERT(ret == 0);
+ } else {
+ ret = odp_packet_extend_head(&pkt, ext_len,
+ (void **)&data,
+ &seg_len);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT(seg_len > 0);
+ CU_ASSERT(data == odp_packet_data(pkt));
+ ret = fill_data_backward(pkt, 0, ext_len,
+ &cur_data);
+ CU_ASSERT(ret == 0);
+ }
+
+ if (packet_sanity_check(pkt))
+ break;
+
+ cur_len += ext_len;
+ }
+
+ CU_ASSERT(odp_packet_len(pkt) == len);
+
+ len = odp_packet_len(pkt);
+
+ memset(buf, 0, len);
+ CU_ASSERT(odp_packet_copy_to_mem(pkt, 0, len, buf) == 0);
+
+ for (i = 0; i < len; i++) {
+ int match;
+
+ if (tail) {
+ match = (buf[i] == (i % 256));
+ CU_ASSERT(match);
+ } else {
+ match = (buf[len - 1 - i] == (i % 256));
+ CU_ASSERT(match);
+ }
+
+ /* Limit the number of failed asserts to
+ one per packet */
+ if (!match)
+ break;
+ }
+
+ odp_packet_free(pkt);
+
+ tail = 0;
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_test_extend_ref(void)
+{
+ odp_packet_t max_pkt, ref;
+ uint32_t hr, tr, max_len;
+
+ max_pkt = odp_packet_copy(segmented_test_packet,
+ odp_packet_pool(segmented_test_packet));
+ CU_ASSERT_FATAL(max_pkt != ODP_PACKET_INVALID);
+ max_len = odp_packet_len(max_pkt);
+
+ /* Maximize the max pkt */
+ hr = odp_packet_headroom(max_pkt);
+ tr = odp_packet_tailroom(max_pkt);
+ odp_packet_push_head(max_pkt, hr);
+ odp_packet_push_tail(max_pkt, tr);
+
+ /* Max packet should not be extendable at either end */
+ if (max_len == pool_capa.pkt.max_len) {
+ CU_ASSERT(odp_packet_extend_tail(&max_pkt, 1, NULL, NULL) < 0);
+ CU_ASSERT(odp_packet_extend_head(&max_pkt, 1, NULL, NULL) < 0);
+ }
+
+ /* See if we can trunc and extend anyway */
+ CU_ASSERT(odp_packet_trunc_tail(&max_pkt, hr + tr + 1,
+ NULL, NULL) >= 0);
+ CU_ASSERT(odp_packet_extend_head(&max_pkt, 1, NULL, NULL) >= 0);
+ CU_ASSERT(odp_packet_len(max_pkt) == max_len);
+ packet_sanity_check(max_pkt);
+
+ /* Now try with a reference in place */
+ CU_ASSERT(odp_packet_trunc_tail(&max_pkt, 100, NULL, NULL) >= 0);
+ packet_sanity_check(max_pkt);
+ ref = odp_packet_ref(max_pkt, 100);
+
+ /* Verify ref lengths */
+ CU_ASSERT(ref != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_len(ref) == max_len - 200);
+ if (odp_packet_has_ref(ref) == 1) {
+ /* And ref's affect on max_pkt */
+ CU_ASSERT(odp_packet_has_ref(max_pkt) == 1);
+ }
+
+ /* Now extend max_pkt and verify effect */
+ CU_ASSERT(odp_packet_extend_head(&max_pkt, 10, NULL, NULL) >= 0);
+ CU_ASSERT(odp_packet_len(max_pkt) == max_len - 90);
+ packet_sanity_check(max_pkt);
+
+ /* Extend on max_pkt should not affect ref */
+ CU_ASSERT(odp_packet_len(ref) == max_len - 200);
+
+ /* Now extend ref and verify effect*/
+ CU_ASSERT(odp_packet_extend_head(&ref, 20, NULL, NULL) >= 0);
+ CU_ASSERT(odp_packet_len(ref) == max_len - 180);
+ packet_sanity_check(max_pkt);
+
+ /* Extend on ref should not affect max_pkt */
+ CU_ASSERT(odp_packet_len(max_pkt) == max_len - 90);
+
+ /* Trunc max_pkt of all unshared len */
+ CU_ASSERT(odp_packet_trunc_head(&max_pkt, 110, NULL, NULL) >= 0);
+ packet_sanity_check(max_pkt);
+
+ /* Verify effect on max_pkt */
+ CU_ASSERT(odp_packet_len(max_pkt) == max_len - 200);
+
+ /* Verify that ref is unchanged */
+ CU_ASSERT(odp_packet_len(ref) == max_len - 180);
+
+ /* Free ref and verify that max_pkt is back to being unreferenced */
+ odp_packet_free(ref);
+ CU_ASSERT(odp_packet_has_ref(max_pkt) == 0);
+ CU_ASSERT(odp_packet_len(max_pkt) == max_len - 200);
+ packet_sanity_check(max_pkt);
+
+ odp_packet_free(max_pkt);
+}
+
+static void packet_test_align(void)
+{
+ odp_packet_t pkt;
+ uint32_t pkt_len, offset;
+ uint32_t seg_len = 0, aligned_seglen = 0;
+ void *pkt_data, *aligned_data;
+ const uint32_t max_align = 32;
+
+ pkt = odp_packet_copy_part(segmented_test_packet, 0,
+ odp_packet_len(segmented_test_packet) / 2,
+ odp_packet_pool(segmented_test_packet));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ pkt_len = odp_packet_len(pkt);
+ seg_len = odp_packet_seg_len(pkt);
+
+ if (odp_packet_is_segmented(pkt)) {
+ /* Can't address across segment boundaries */
+ CU_ASSERT(odp_packet_align(&pkt, 0, pkt_len, 0) < 0);
+
+ offset = seg_len - 5;
+ (void)odp_packet_offset(pkt, offset, &seg_len, NULL);
+
+ /* Realign for addressability */
+ CU_ASSERT(odp_packet_align(&pkt, offset,
+ seg_len + 2, 0) >= 0);
+
+ /* Alignment doesn't change packet length or contents */
+ CU_ASSERT(odp_packet_len(pkt) == pkt_len);
+ (void)odp_packet_offset(pkt, offset, &aligned_seglen, NULL);
+ packet_compare_offset(pkt, offset,
+ segmented_test_packet, offset,
+ aligned_seglen);
+
+ /* Verify requested contiguous addressabilty */
+ CU_ASSERT(aligned_seglen >= seg_len + 2);
+
+ packet_sanity_check(pkt);
+ }
+
+ /* Get a misaligned address */
+ pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL);
+ offset = seg_len - 5;
+ pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL);
+ if ((uintptr_t)pkt_data % max_align == 0) {
+ offset--;
+ pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL);
+ }
+
+ /* Realign for alignment */
+ CU_ASSERT(odp_packet_align(&pkt, offset, 1, max_align) >= 0);
+ aligned_data = odp_packet_offset(pkt, offset, &aligned_seglen, NULL);
+
+ CU_ASSERT(odp_packet_len(pkt) == pkt_len);
+ packet_compare_offset(pkt, offset, segmented_test_packet, offset,
+ aligned_seglen);
+ CU_ASSERT((uintptr_t)aligned_data % max_align == 0);
+ packet_sanity_check(pkt);
+
+ odp_packet_free(pkt);
+}
+
+static void packet_test_offset(void)
+{
+ odp_packet_t pkt = test_packet;
+ uint32_t seg_len = 0;
+ uint32_t full_seg_len;
+ uint8_t *ptr, *start_ptr;
+ uint32_t offset;
+ odp_packet_seg_t seg = ODP_PACKET_SEG_INVALID;
+
+ ptr = odp_packet_offset(pkt, 0, &seg_len, &seg);
+ CU_ASSERT(seg != ODP_PACKET_SEG_INVALID);
+ CU_ASSERT(seg_len > 1);
+ CU_ASSERT(seg_len == odp_packet_seg_len(pkt));
+ CU_ASSERT(seg_len == odp_packet_seg_data_len(pkt, seg));
+ CU_ASSERT(ptr != NULL);
+ CU_ASSERT(ptr == odp_packet_data(pkt));
+ CU_ASSERT(ptr == odp_packet_seg_data(pkt, seg));
+
+ /* Query a second byte */
+ start_ptr = ptr;
+ full_seg_len = seg_len;
+ offset = 1;
+
+ ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
+ CU_ASSERT(ptr != NULL);
+ CU_ASSERT(ptr == start_ptr + offset);
+ CU_ASSERT(seg_len == full_seg_len - offset);
+
+ /* Query the last byte in a segment */
+ offset = full_seg_len - 1;
+
+ ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
+ CU_ASSERT(ptr != NULL);
+ CU_ASSERT(ptr == start_ptr + offset);
+ CU_ASSERT(seg_len == full_seg_len - offset);
+
+ /* Query the last byte in a packet */
+ offset = odp_packet_len(pkt) - 1;
+ ptr = odp_packet_offset(pkt, offset, &seg_len, NULL);
+ CU_ASSERT(ptr != NULL);
+ CU_ASSERT(seg_len == 1);
+
+ /* Pass NULL to [out] arguments */
+ ptr = odp_packet_offset(pkt, 0, NULL, NULL);
+ CU_ASSERT(ptr != NULL);
+}
+
+static void packet_test_ref(void)
+{
+ odp_packet_t base_pkt, segmented_base_pkt, hdr_pkt[4],
+ ref_pkt[4], refhdr_pkt[4], hdr_cpy;
+ odp_packet_t pkt, pkt2, pkt3, ref, ref2;
+ uint32_t pkt_len, segmented_pkt_len, hdr_len[4], offset[4], hr[4],
+ base_hr, ref_len[4];
+ int i, ret;
+ odp_pool_t pool;
+
+ /* Create references and compare data */
+ pool = odp_packet_pool(test_packet);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ ref = odp_packet_ref_static(pkt);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ packet_compare_data(pkt, ref);
+ odp_packet_free(ref);
+ odp_packet_free(pkt);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ ref = odp_packet_ref(pkt, 0);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ packet_compare_data(pkt, ref);
+ odp_packet_free(ref);
+ odp_packet_free(pkt);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ pkt3 = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ CU_ASSERT_FATAL(pkt3 != ODP_PACKET_INVALID)
+ ret = odp_packet_concat(&pkt3, pkt);
+ CU_ASSERT_FATAL(ret >= 0);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ pkt2 = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID)
+ ref = odp_packet_ref_pkt(pkt, 0, pkt2);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ packet_compare_data(pkt3, ref);
+ odp_packet_free(ref);
+ odp_packet_free(pkt);
+ odp_packet_free(pkt3);
+
+ /* Do the same for segmented packets */
+ pool = odp_packet_pool(segmented_test_packet);
+
+ pkt = odp_packet_copy(segmented_test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ ref = odp_packet_ref_static(pkt);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ packet_compare_data(pkt, ref);
+ odp_packet_free(ref);
+ odp_packet_free(pkt);
+
+ pkt = odp_packet_copy(segmented_test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ ref = odp_packet_ref(pkt, 0);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ packet_compare_data(pkt, ref);
+ odp_packet_free(ref);
+ odp_packet_free(pkt);
+
+ /* Avoid to create too large packets with concat */
+ pool = odp_packet_pool(test_packet);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ pkt2 = odp_packet_copy(test_packet, pool);
+ pkt3 = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID)
+ CU_ASSERT_FATAL(pkt3 != ODP_PACKET_INVALID)
+ ret = odp_packet_concat(&pkt3, pkt2);
+ CU_ASSERT_FATAL(ret >= 0);
+ ret = odp_packet_concat(&pkt3, pkt);
+ CU_ASSERT_FATAL(ret >= 0);
+
+ pkt = odp_packet_copy(test_packet, pool);
+ pkt2 = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID)
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID)
+ ref = odp_packet_ref_pkt(pkt, 0, pkt2);
+ CU_ASSERT_FATAL(ref != ODP_PACKET_INVALID)
+ pkt2 = odp_packet_copy(test_packet, pool);
+ CU_ASSERT_FATAL(pkt2 != ODP_PACKET_INVALID)
+ ref2 = odp_packet_ref_pkt(ref, 0, pkt2);
+ CU_ASSERT_FATAL(ref2 != ODP_PACKET_INVALID)
+ packet_compare_data(pkt3, ref2);
+
+ /* Try print function on a reference */
+ printf("\n\n");
+ odp_packet_print(ref2);
+ odp_packet_print_data(ref2, 0, 100);
+ odp_packet_print_data(ref2, 14, 20);
+
+ odp_packet_free(ref);
+ odp_packet_free(ref2);
+ odp_packet_free(pkt);
+ odp_packet_free(pkt3);
+
+ /* Test has_ref, lengths, etc */
+ base_pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
+ CU_ASSERT_FATAL(base_pkt != ODP_PACKET_INVALID);
+ base_hr = odp_packet_headroom(base_pkt);
+ pkt_len = odp_packet_len(test_packet);
+
+ segmented_base_pkt =
+ odp_packet_copy(segmented_test_packet,
+ odp_packet_pool(segmented_test_packet));
+ segmented_pkt_len = odp_packet_len(segmented_test_packet);
+ CU_ASSERT_FATAL(segmented_base_pkt != ODP_PACKET_INVALID);
+
+ CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
+
+ hdr_pkt[0] =
+ odp_packet_copy_part(segmented_test_packet, 0,
+ odp_packet_len(segmented_test_packet) / 4,
+ odp_packet_pool(segmented_test_packet));
+ CU_ASSERT_FATAL(hdr_pkt[0] != ODP_PACKET_INVALID);
+ hdr_len[0] = odp_packet_len(hdr_pkt[0]);
+ offset[0] = 0;
+
+ hdr_pkt[1] =
+ odp_packet_copy_part(segmented_test_packet, 10,
+ odp_packet_len(segmented_test_packet) / 8,
+ odp_packet_pool(segmented_test_packet));
+ CU_ASSERT_FATAL(hdr_pkt[1] != ODP_PACKET_INVALID);
+ hdr_len[1] = odp_packet_len(hdr_pkt[1]);
+ offset[1] = 5;
+
+ hdr_pkt[2] = odp_packet_copy_part(test_packet, 0,
+ odp_packet_len(test_packet) / 4,
+ odp_packet_pool(test_packet));
+ CU_ASSERT_FATAL(hdr_pkt[2] != ODP_PACKET_INVALID);
+ hdr_len[2] = odp_packet_len(hdr_pkt[2]);
+ offset[2] = 64;
+
+ hdr_pkt[3] = odp_packet_copy_part(test_packet, 0,
+ odp_packet_len(test_packet) / 4,
+ odp_packet_pool(test_packet));
+ CU_ASSERT_FATAL(hdr_pkt[3] != ODP_PACKET_INVALID);
+ hdr_len[3] = odp_packet_len(hdr_pkt[3]);
+ offset[3] = 64;
+
+ /* Nothing is a ref or has a ref before we start */
+ for (i = 0; i < 4; i++) {
+ CU_ASSERT(odp_packet_has_ref(hdr_pkt[i]) == 0);
+ }
+
+ /* Create a couple of refs */
+ refhdr_pkt[0] = odp_packet_ref_pkt(base_pkt, offset[0], hdr_pkt[0]);
+ refhdr_pkt[1] = odp_packet_ref_pkt(base_pkt, offset[1], hdr_pkt[1]);
+
+ CU_ASSERT(refhdr_pkt[0] != ODP_PACKET_INVALID);
+ CU_ASSERT(refhdr_pkt[1] != ODP_PACKET_INVALID);
+
+ /* If base packet has now references, ref packet should be also
+ * references. */
+ if (odp_packet_has_ref(base_pkt) == 1) {
+ CU_ASSERT(odp_packet_has_ref(refhdr_pkt[0]) == 1);
+ CU_ASSERT(odp_packet_has_ref(refhdr_pkt[1]) == 1);
+ }
+
+ CU_ASSERT(odp_packet_len(refhdr_pkt[0]) ==
+ hdr_len[0] + pkt_len - offset[0]);
+ CU_ASSERT(odp_packet_len(refhdr_pkt[1]) ==
+ hdr_len[1] + pkt_len - offset[1]);
+
+ packet_compare_offset(refhdr_pkt[0], hdr_len[0],
+ base_pkt, offset[0],
+ pkt_len - offset[0]);
+
+ packet_compare_offset(refhdr_pkt[1], hdr_len[1],
+ base_pkt, offset[1],
+ pkt_len - offset[1]);
+
+ /* See if compound references are supported and if so that they
+ * operate properly */
+ hdr_cpy = odp_packet_copy(hdr_pkt[2], odp_packet_pool(hdr_pkt[2]));
+ CU_ASSERT_FATAL(hdr_cpy != ODP_PACKET_INVALID);
+
+ refhdr_pkt[2] = odp_packet_ref_pkt(refhdr_pkt[0], 2, hdr_cpy);
+ CU_ASSERT(refhdr_pkt[2] != ODP_PACKET_INVALID);
+
+ if (odp_packet_has_ref(refhdr_pkt[2]) == 1) {
+ CU_ASSERT(odp_packet_has_ref(refhdr_pkt[0]) == 1);
+ }
+
+ /* Delete the refs */
+ odp_packet_free(refhdr_pkt[0]);
+ odp_packet_free(refhdr_pkt[1]);
+ odp_packet_free(refhdr_pkt[2]);
+
+ /* Verify that base_pkt no longer has a ref */
+ CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
+
+ /* Now create a two more shared refs */
+ refhdr_pkt[2] = odp_packet_ref_pkt(base_pkt, offset[2], hdr_pkt[2]);
+ refhdr_pkt[3] = odp_packet_ref_pkt(base_pkt, offset[3], hdr_pkt[3]);
+
+ CU_ASSERT(hdr_pkt[2] != ODP_PACKET_INVALID);
+ CU_ASSERT(hdr_pkt[3] != ODP_PACKET_INVALID);
+
+ if (odp_packet_has_ref(base_pkt) == 1) {
+ CU_ASSERT(odp_packet_has_ref(refhdr_pkt[2]) == 1);
+ CU_ASSERT(odp_packet_has_ref(refhdr_pkt[3]) == 1);
+ }
+
+ CU_ASSERT(odp_packet_len(refhdr_pkt[2]) ==
+ odp_packet_len(refhdr_pkt[3]));
+
+ packet_compare_offset(refhdr_pkt[2], 0,
+ refhdr_pkt[3], 0,
+ odp_packet_len(hdr_pkt[2]));
+
+ /* Delete the headers */
+ odp_packet_free(refhdr_pkt[2]);
+ odp_packet_free(refhdr_pkt[3]);
+
+ /* Verify that base_pkt is no longer ref'd */
+ CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
+
+ /* Create a static reference */
+ ref_pkt[0] = odp_packet_ref_static(base_pkt);
+ CU_ASSERT(ref_pkt[0] != ODP_PACKET_INVALID);
+
+ if (odp_packet_has_ref(base_pkt) == 1) {
+ CU_ASSERT(odp_packet_has_ref(ref_pkt[0]) == 1);
+ }
+
+ CU_ASSERT(odp_packet_len(ref_pkt[0]) == odp_packet_len(base_pkt));
+ packet_compare_offset(ref_pkt[0], 0, base_pkt, 0,
+ odp_packet_len(base_pkt));
+
+ /* Now delete it */
+ odp_packet_free(ref_pkt[0]);
+ CU_ASSERT(odp_packet_has_ref(base_pkt) == 0);
+
+ /* Create references */
+ ref_pkt[0] = odp_packet_ref(segmented_base_pkt, offset[0]);
+ CU_ASSERT_FATAL(ref_pkt[0] != ODP_PACKET_INVALID);
+
+ if (odp_packet_has_ref(ref_pkt[0]) == 1) {
+ /* CU_ASSERT needs braces */
+ CU_ASSERT(odp_packet_has_ref(segmented_base_pkt) == 1);
+ }
+
+ ref_pkt[1] = odp_packet_ref(segmented_base_pkt, offset[1]);
+ CU_ASSERT_FATAL(ref_pkt[1] != ODP_PACKET_INVALID);
+
+ if (odp_packet_has_ref(ref_pkt[1]) == 1) {
+ /* CU_ASSERT needs braces */
+ CU_ASSERT(odp_packet_has_ref(segmented_base_pkt) == 1);
+ }
+
+ /* Verify reference lengths */
+ CU_ASSERT(odp_packet_len(ref_pkt[0]) == segmented_pkt_len - offset[0]);
+ CU_ASSERT(odp_packet_len(ref_pkt[1]) == segmented_pkt_len - offset[1]);
+
+ /* Free the base pkts -- references should still be valid */
+ odp_packet_free(base_pkt);
+ odp_packet_free(segmented_base_pkt);
+
+ packet_compare_offset(ref_pkt[0], 0,
+ segmented_test_packet, offset[0],
+ segmented_pkt_len - offset[0]);
+ packet_compare_offset(ref_pkt[1], 0,
+ segmented_test_packet, offset[1],
+ segmented_pkt_len - offset[1]);
+
+ /* Verify we can modify the refs */
+ hr[0] = odp_packet_headroom(ref_pkt[0]);
+ hr[1] = odp_packet_headroom(ref_pkt[1]);
+
+ CU_ASSERT(odp_packet_push_head(ref_pkt[0], hr[0]) != NULL);
+
+ CU_ASSERT(odp_packet_len(ref_pkt[0]) ==
+ hr[0] + segmented_pkt_len - offset[0]);
+
+ CU_ASSERT(odp_packet_pull_head(ref_pkt[0], hr[0] / 2) != NULL);
+
+ if (hr[1] > 0) {
+ CU_ASSERT(odp_packet_push_head(ref_pkt[1], 1) != NULL);
+ CU_ASSERT(odp_packet_len(ref_pkt[1]) ==
+ 1 + segmented_pkt_len - offset[1]);
+ CU_ASSERT(odp_packet_pull_head(ref_pkt[1], 1) != NULL);
+ CU_ASSERT(odp_packet_len(ref_pkt[1]) ==
+ segmented_pkt_len - offset[1]);
+ }
+
+ odp_packet_free(ref_pkt[0]);
+ odp_packet_free(ref_pkt[1]);
+
+ /* Verify we can modify base packet after reference is created */
+ base_pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet));
+
+ ref_pkt[1] = odp_packet_ref(base_pkt, offset[1]);
+ CU_ASSERT_FATAL(ref_pkt[1] != ODP_PACKET_INVALID);
+ ref_len[1] = odp_packet_len(ref_pkt[1]);
+ CU_ASSERT(ref_len[1] == odp_packet_len(base_pkt) - offset[1]);
+
+ CU_ASSERT(odp_packet_push_head(base_pkt, base_hr / 2) != NULL);
+
+ CU_ASSERT(odp_packet_len(ref_pkt[1]) == ref_len[1]);
+
+ ref_pkt[0] = odp_packet_ref(base_pkt, offset[0]);
+ CU_ASSERT_FATAL(ref_pkt[0] != ODP_PACKET_INVALID);
+ ref_len[0] = odp_packet_len(ref_pkt[0]);
+ CU_ASSERT(ref_len[0] == odp_packet_len(base_pkt) - offset[0]);
+
+ CU_ASSERT(odp_packet_push_head(base_pkt,
+ base_hr - base_hr / 2) != NULL);
+ CU_ASSERT(odp_packet_len(ref_pkt[1]) == ref_len[1]);
+ CU_ASSERT(odp_packet_len(ref_pkt[0]) == ref_len[0]);
+
+ hr[0] = odp_packet_headroom(ref_pkt[0]);
+ hr[1] = odp_packet_headroom(ref_pkt[1]);
+ CU_ASSERT(odp_packet_push_head(ref_pkt[0], hr[0]) != NULL);
+ CU_ASSERT(odp_packet_push_head(ref_pkt[1], hr[1]) != NULL);
+
+ odp_packet_free(base_pkt);
+ odp_packet_free(ref_pkt[0]);
+ odp_packet_free(ref_pkt[1]);
+}
+
+static void packet_vector_test_event_conversion(void)
+{
+ odp_packet_vector_t pktv0 = pktv_default;
+ odp_packet_vector_t pktv1;
+ odp_event_t event;
+
+ event = odp_packet_vector_to_event(pktv0);
+ CU_ASSERT_FATAL(event != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_type(event) == ODP_EVENT_PACKET_VECTOR);
+
+ pktv1 = odp_packet_vector_from_event(event);
+ CU_ASSERT_FATAL(pktv1 != ODP_PACKET_VECTOR_INVALID);
+ CU_ASSERT(pktv1 == pktv0);
+}
+
+static int remove_invalid_pkts_tbl(odp_packet_t *pkt_tbl, int num_pkts)
+{
+ int i, j, count = 0;
+
+ for (i = 0; i < (num_pkts - count) ; i++) {
+ if (pkt_tbl[i] == ODP_PACKET_INVALID) {
+ for (j = i; j < num_pkts; j++)
+ pkt_tbl[j] = pkt_tbl[j + 1];
+
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void packet_vector_test_tbl(void)
+{
+ odp_packet_vector_t pktv = ODP_PACKET_VECTOR_INVALID;
+ odp_packet_t *pkt_tbl, packet;
+ odp_packet_t clone_packet = ODP_PACKET_INVALID;
+ odp_packet_t orig_pkt_tbl[PKT_VEC_SIZE];
+ odp_pool_param_t params;
+ odp_pool_capability_t capa;
+ odp_pool_t pool;
+ uint32_t i, num;
+ uint32_t max_size = PKT_VEC_SIZE;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+ CU_ASSERT_FATAL(capa.vector.max_size > 0);
+
+ if (capa.vector.max_size < max_size)
+ max_size = capa.vector.max_size;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = 1;
+ params.vector.max_size = max_size;
+
+ pool = odp_pool_create("vector_pool_alloc", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Allocate the only vector from the pool */
+ pktv = odp_packet_vector_alloc(pool);
+ /* Check if vector packet is valid */
+ CU_ASSERT_FATAL(odp_packet_vector_valid(pktv) == 1)
+ CU_ASSERT(odp_packet_vector_to_u64(pktv) !=
+ odp_packet_vector_to_u64(ODP_PACKET_VECTOR_INVALID));
+
+ /* Allocate packets */
+ for (i = 0; i < max_size; i++) {
+ orig_pkt_tbl[i] = odp_packet_alloc(default_pool,
+ default_param.pkt.len);
+ CU_ASSERT_FATAL(orig_pkt_tbl[i] != ODP_PACKET_INVALID);
+ }
+
+ /* Get packet vector table */
+ num = odp_packet_vector_tbl(pktv, &pkt_tbl);
+ /* Make sure there are initially no packets in the vector */
+ CU_ASSERT(num == 0);
+
+ /* Fill the allocated packets in the vector */
+ for (i = 0; i < max_size; i++)
+ pkt_tbl[i] = orig_pkt_tbl[i];
+
+ /* Set number of packets stored in the vector */
+ odp_packet_vector_size_set(pktv, max_size);
+
+ /* Get number of packets in the vector */
+ num = odp_packet_vector_size(pktv);
+ CU_ASSERT(num == max_size);
+
+ if (max_size < 4) {
+ printf("Max vector size too small to run all tests.\n");
+ goto cleanup;
+ }
+
+ /* Preparing a copy of the packet */
+ packet = orig_pkt_tbl[0];
+ clone_packet = odp_packet_copy(packet, odp_packet_pool(packet));
+ CU_ASSERT_FATAL(clone_packet != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_to_u64(clone_packet) != odp_packet_to_u64(packet));
+
+ /* Change one packet handle in the table */
+ pkt_tbl[1] = clone_packet;
+ /* Read packet vector table. */
+ num = odp_packet_vector_tbl(pktv, &pkt_tbl);
+ /* Packets available should be equal to last updated */
+ CU_ASSERT(num == max_size);
+ /* Check if packet handle still corresponds to cloned packet */
+ CU_ASSERT(odp_packet_to_u64(pkt_tbl[1]) ==
+ odp_packet_to_u64(clone_packet));
+
+ /* Mark the first packet as invalid */
+ pkt_tbl[0] = ODP_PACKET_INVALID;
+ /* Reading the table to confirm if the first packet is invalid */
+ num = odp_packet_vector_tbl(pktv, &pkt_tbl);
+ CU_ASSERT(odp_packet_is_valid(pkt_tbl[0]) == 0);
+
+ /* Invalid packet should never be present in the table, following logic
+ * updates the pkt_tble array and returns the number of invalid packets
+ * removed. */
+ num = remove_invalid_pkts_tbl(pkt_tbl, odp_packet_vector_size(pktv));
+ CU_ASSERT(num == 1);
+ /* Update number of valid packets in the table */
+ odp_packet_vector_size_set(pktv, odp_packet_vector_size(pktv) - num);
+ CU_ASSERT(odp_packet_vector_size(pktv) == max_size - num);
+ /* The first packet should be valid now */
+ CU_ASSERT(odp_packet_is_valid(pkt_tbl[0]) == 1);
+
+cleanup:
+ if (clone_packet != ODP_PACKET_INVALID)
+ odp_packet_free(clone_packet);
+ odp_packet_free_multi(orig_pkt_tbl, max_size);
+ odp_packet_vector_free(pktv);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_vector_test_debug(void)
+{
+ CU_ASSERT_FATAL(odp_packet_vector_valid(pktv_default) == 1);
+ printf("\n\n");
+ odp_packet_vector_print(pktv_default);
+}
+
+static void packet_vector_test_alloc_free(void)
+{
+ odp_packet_vector_t pktv = ODP_PACKET_VECTOR_INVALID;
+ odp_pool_param_t params;
+ odp_pool_capability_t capa;
+ odp_pool_t pool;
+ odp_packet_t pkt;
+ odp_packet_t *pkts_tbl;
+ uint32_t max_size = PKT_VEC_SIZE;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+ CU_ASSERT_FATAL(capa.vector.max_size > 0);
+
+ if (capa.vector.max_size < max_size)
+ max_size = capa.vector.max_size;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = 1;
+ params.vector.max_size = max_size;
+
+ pool = odp_pool_create("vector_pool_alloc", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Allocate the only vector from the pool */
+ pktv = odp_packet_vector_alloc(pool);
+ /* Check if vector packet is valid */
+ CU_ASSERT_FATAL(odp_packet_vector_valid(pktv) == 1)
+ CU_ASSERT(odp_packet_vector_to_u64(pktv) !=
+ odp_packet_vector_to_u64(ODP_PACKET_VECTOR_INVALID));
+
+ /* Vector size and user flag should be initially zero */
+ CU_ASSERT(odp_packet_vector_size(pktv) == 0);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) == 0);
+ odp_packet_vector_user_flag_set(pktv, 1);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) != 0);
+ odp_packet_vector_user_flag_set(pktv, 0);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) == 0);
+
+ /* Included packet should not be freed by odp_packet_vector_free() */
+ pkt = odp_packet_alloc(default_pool, default_param.pkt.len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ CU_ASSERT(odp_packet_vector_tbl(pktv, &pkts_tbl) == 0);
+ pkts_tbl[0] = pkt;
+ odp_packet_vector_size_set(pktv, 1);
+
+ /* Free with flag still set, alloc should clear it. */
+ odp_packet_vector_user_flag_set(pktv, 1);
+ odp_packet_vector_free(pktv);
+
+ /* Check that included packet is still valid */
+ CU_ASSERT(odp_packet_is_valid(pkt));
+
+ pktv = odp_packet_vector_alloc(pool);
+ CU_ASSERT(odp_packet_vector_size(pktv) == 0);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) == 0);
+
+ /* Since it was only one buffer pool, more vector packets can't be
+ * allocated.
+ */
+ CU_ASSERT_FATAL(odp_packet_vector_alloc(pool) == ODP_PACKET_VECTOR_INVALID);
+
+ /* Freeing the buffer back to pool */
+ odp_packet_vector_free(pktv);
+
+ /* Check that the buffer was returned back to the pool */
+ pktv = odp_packet_vector_alloc(pool);
+ CU_ASSERT_FATAL(pktv != ODP_PACKET_VECTOR_INVALID);
+ CU_ASSERT(odp_packet_vector_size(pktv) == 0);
+
+ /* Free packet vector and included packet using odp_event_free() */
+ CU_ASSERT(odp_packet_vector_tbl(pktv, &pkts_tbl) == 0);
+ pkts_tbl[0] = pkt;
+ odp_packet_vector_size_set(pktv, 1);
+
+ odp_event_free(odp_packet_vector_to_event(pktv));
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void packet_vector_basic_test(void)
+{
+ odp_packet_t *pkt_tbl;
+ odp_pool_capability_t capa;
+ uint32_t i, num;
+ uint32_t max_size = PKT_VEC_PACKET_NUM;
+ odp_event_t ev;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+ if (capa.vector.max_size < max_size)
+ max_size = capa.vector.max_size;
+
+ /* Checking if default vector packet is valid */
+ CU_ASSERT(odp_packet_vector_valid(pktv_default) == 1)
+
+ /* Making sure default vector packet is from default vector pool */
+ CU_ASSERT(odp_packet_vector_pool(pktv_default) == vector_default_pool)
+ ev = odp_packet_vector_to_event(pktv_default);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(odp_event_pool(ev) == vector_default_pool);
+
+ /* Get packet vector table */
+ num = odp_packet_vector_tbl(pktv_default, &pkt_tbl);
+ /* Making sure initially no packet in the vector */
+ CU_ASSERT(num == 0);
+
+ /* Fill the preallocated packets in vector */
+ for (i = 0; i < max_size; i++)
+ pkt_tbl[i] = pkt_vec[i];
+
+ /* Setting up number of packets stored in vector */
+ odp_packet_vector_size_set(pktv_default, max_size);
+
+ /* Get number of packets in vector */
+ num = odp_packet_vector_size(pktv_default);
+ CU_ASSERT(num == max_size);
+
+ CU_ASSERT(odp_packet_vector_valid(pktv_default) == 1);
+}
+
+static void packet_vector_test_user_area(void)
+{
+ odp_pool_param_t param;
+ odp_pool_t pool;
+ uint32_t i;
+ void *addr;
+ uint32_t num = 10;
+ void *prev = NULL;
+ uint32_t num_alloc = 0;
+ uint32_t size = 1024;
+ const uint32_t max_size = pool_capa.vector.max_uarea_size;
+
+ if (max_size == 0) {
+ ODPH_DBG("Packet vector user area not supported\n");
+ return;
+ }
+
+ if (size > max_size)
+ size = max_size;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = num;
+ param.vector.max_size = pool_capa.vector.max_size;
+ param.vector.uarea_size = size;
+
+ odp_packet_vector_t pktv[num];
+
+ pool = odp_pool_create("test_user_area", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag;
+
+ pktv[i] = odp_packet_vector_alloc(pool);
+
+ if (pktv[i] == ODP_PACKET_VECTOR_INVALID)
+ break;
+ num_alloc++;
+
+ addr = odp_packet_vector_user_area(pktv[i]);
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ ev = odp_packet_vector_to_event(pktv[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag == 0);
+
+ prev = addr;
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num);
+
+ for (i = 0; i < num_alloc; i++)
+ odp_packet_vector_free(pktv[i]);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+static int packet_vector_suite_init(void)
+{
+ uint32_t num_pkt = PKT_VEC_PACKET_NUM;
+ uint32_t num = PACKET_POOL_NUM;
+ odp_pool_param_t params;
+ uint32_t i, ret, len;
+
+ memset(&pool_capa, 0, sizeof(odp_pool_capability_t));
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("pool_capability failed\n");
+ return -1;
+ }
+
+ if (pool_capa.pkt.max_num != 0 && pool_capa.pkt.max_num < num)
+ num = pool_capa.pkt.max_num;
+
+ /* Creating default packet pool */
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_PACKET;
+ params.pkt.len = pool_capa.pkt.min_seg_len;
+ params.pkt.num = num;
+
+ memcpy(&default_param, &params, sizeof(odp_pool_param_t));
+
+ default_pool = odp_pool_create("default_pool", &params);
+ if (default_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("default pool create failed\n");
+ return -1;
+ }
+
+ /* Allocating ipv4-udp packets */
+ len = sizeof(test_packet_ipv4_udp);
+ ret = odp_packet_alloc_multi(default_pool, len, pkt_vec, num_pkt);
+ if (ret != num_pkt) {
+ ODPH_ERR("packet allocation failed\n");
+ if (ret > 0)
+ odp_packet_free_multi(pkt_vec, ret);
+ goto err;
+ }
+
+ for (i = 0; i < num_pkt; i++) {
+ ret = odp_packet_copy_from_mem(pkt_vec[i], 0, len,
+ test_packet_ipv4_udp);
+ if (ret != 0) {
+ ODPH_ERR("packet preparation failed\n");
+ goto err1;
+ }
+ }
+
+ /* Creating the vector pool */
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = PKT_VEC_NUM;
+ params.vector.max_size = pool_capa.vector.max_size < PKT_VEC_SIZE ?
+ pool_capa.vector.max_size : PKT_VEC_SIZE;
+
+ vector_default_pool = odp_pool_create("vector_default_pool", &params);
+
+ if (vector_default_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Default vector pool create failed\n");
+ goto err1;
+ }
+
+ /* Allocating a default vector */
+ pktv_default = odp_packet_vector_alloc(vector_default_pool);
+ if (pktv_default == ODP_PACKET_VECTOR_INVALID) {
+ ODPH_ERR("Default vector packet allocation failed\n");
+ goto err2;
+ }
+ return 0;
+err2:
+ odp_pool_destroy(vector_default_pool);
+err1:
+ odp_packet_free_multi(pkt_vec, PKT_VEC_PACKET_NUM);
+err:
+ odp_pool_destroy(default_pool);
+ return -1;
+}
+
+static int packet_vector_suite_term(void)
+{
+ odp_packet_free_multi(pkt_vec, PKT_VEC_PACKET_NUM);
+
+ odp_pool_destroy(default_pool);
+
+ odp_packet_vector_free(pktv_default);
+ odp_pool_destroy(vector_default_pool);
+ return 0;
+}
+static void packet_test_max_pools(void)
+{
+ odp_pool_param_t param;
+ uint32_t i, num_pool, num_pkt;
+ void *addr;
+ odp_event_t ev;
+ uint32_t len = 500;
+ /* Suite init has created one pool already */
+ uint32_t max_pools = pool_capa.pkt.max_pools - 1;
+ odp_pool_t pool[max_pools];
+ odp_packet_t packet[max_pools];
+
+ CU_ASSERT_FATAL(max_pools != 0);
+
+ printf("\n Creating %u pools\n", max_pools);
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = len;
+ param.pkt.num = 1;
+ param.pkt.max_num = 1;
+
+ for (i = 0; i < max_pools; i++) {
+ pool[i] = odp_pool_create(NULL, &param);
+
+ if (pool[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ num_pool = i;
+
+ CU_ASSERT(num_pool == max_pools);
+ if (num_pool != max_pools)
+ ODPH_ERR("Created only %u pools\n", num_pool);
+
+ for (i = 0; i < num_pool; i++) {
+ packet[i] = odp_packet_alloc(pool[i], len);
+
+ if (packet[i] == ODP_PACKET_INVALID)
+ break;
+
+ CU_ASSERT_FATAL(odp_packet_pool(packet[i]) == pool[i]);
+
+ ev = odp_packet_to_event(packet[i]);
+ CU_ASSERT(odp_packet_from_event(ev) == packet[i]);
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET);
+
+ CU_ASSERT(odp_packet_len(packet[i]) == len);
+ addr = odp_packet_data(packet[i]);
+
+ /* Write packet data */
+ memset(addr, 0, len);
+ }
+
+ num_pkt = i;
+ CU_ASSERT(num_pkt == num_pool);
+
+ if (num_pkt)
+ odp_packet_free_multi(packet, num_pkt);
+
+ for (i = 0; i < num_pool; i++)
+ CU_ASSERT(odp_pool_destroy(pool[i]) == 0);
+}
+
+static void packet_test_user_area(void)
+{
+ odp_pool_param_t param;
+ odp_packet_t pkt;
+ odp_pool_t pool;
+ odp_event_t ev;
+ int flag;
+
+ memcpy(&param, &default_param, sizeof(odp_pool_param_t));
+
+ param.pkt.uarea_size = 0;
+ pool = odp_pool_create("zero_uarea", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ pkt = odp_packet_alloc(pool, param.pkt.len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_user_area_size(pkt) <= pool_capa.pkt.max_uarea_size);
+ if (odp_packet_user_area_size(pkt)) {
+ /* CU_ASSERT needs these extra bracets */
+ CU_ASSERT(odp_packet_user_area(pkt) != NULL);
+ } else {
+ CU_ASSERT(odp_packet_user_area(pkt) == NULL);
+ }
+ ev = odp_packet_to_event(pkt);
+ CU_ASSERT(odp_event_user_area(ev) == odp_packet_user_area(pkt));
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == odp_packet_user_area(pkt));
+ CU_ASSERT(flag == 0);
+
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ if (pool_capa.pkt.max_uarea_size == 0)
+ return;
+
+ param.pkt.uarea_size = 1;
+ pool = odp_pool_create("one_uarea", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ pkt = odp_packet_alloc(pool, param.pkt.len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT_FATAL(odp_packet_user_area(pkt) != NULL);
+ ev = odp_packet_to_event(pkt);
+ CU_ASSERT(odp_event_user_area(ev) == odp_packet_user_area(pkt));
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == odp_packet_user_area(pkt));
+ CU_ASSERT(flag == 0);
+ CU_ASSERT(odp_packet_user_area_size(pkt) >= 1);
+ *(char *)odp_packet_user_area(pkt) = 0;
+ CU_ASSERT_FATAL(odp_packet_is_valid(pkt) == 1);
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+
+ param.pkt.uarea_size = pool_capa.pkt.max_uarea_size;
+ pool = odp_pool_create("max_uarea", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ pkt = odp_packet_alloc(pool, param.pkt.len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ odp_packet_user_flag_set(pkt, 1);
+ CU_ASSERT_FATAL(odp_packet_user_area(pkt) != NULL);
+ ev = odp_packet_to_event(pkt);
+ CU_ASSERT(odp_event_user_area(ev) == odp_packet_user_area(pkt));
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == odp_packet_user_area(pkt));
+ CU_ASSERT(flag > 0);
+ CU_ASSERT(odp_packet_user_area_size(pkt) == param.pkt.uarea_size);
+ memset(odp_packet_user_area(pkt), 0, param.pkt.uarea_size);
+ CU_ASSERT_FATAL(odp_packet_is_valid(pkt) == 1);
+ odp_packet_free(pkt);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static int packet_parse_suite_init(void)
+{
+ int num_test_pkt, i;
+ uint32_t max_len;
+ odp_pool_param_t param;
+
+ memset(&pool_capa, 0, sizeof(odp_pool_capability_t));
+
+ if (odp_pool_capability(&pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed\n");
+ return -1;
+ }
+
+ num_test_pkt = ODPH_ARRAY_SIZE(parse_test_pkt_len);
+ max_len = 0;
+
+ for (i = 0; i < num_test_pkt; i++) {
+ if (max_len < parse_test_pkt_len[i])
+ max_len = parse_test_pkt_len[i];
+ }
+ max_len += MAX_PARSE_L2_OFFSET;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.seg_len = max_len;
+ param.pkt.len = max_len;
+ param.pkt.num = 100;
+
+ parse_test.pool = odp_pool_create("parse_test_pool", &param);
+
+ if (parse_test.pool == ODP_POOL_INVALID)
+ return -1;
+
+ parse_test.all_chksums.all_chksum = 0;
+ parse_test.all_chksums.chksum.ipv4 = 1;
+ parse_test.all_chksums.chksum.udp = 1;
+ parse_test.all_chksums.chksum.tcp = 1;
+ parse_test.all_chksums.chksum.sctp = 1;
+
+ return 0;
+}
+
+static int packet_parse_suite_term(void)
+{
+ if (odp_pool_destroy(parse_test.pool))
+ return -1;
+
+ return 0;
+}
+
+static void parse_test_alloc(odp_packet_t pkt[], const uint8_t test_packet[],
+ uint32_t len, int num_pkt)
+{
+ int ret, i;
+ static uint32_t l2_offset[PARSE_TEST_NUM_PKT] = {0 /* must be zero */,
+ 2, 8, 12, 19, 36, 64, 120, MAX_PARSE_L2_OFFSET};
+
+ CU_ASSERT_FATAL(num_pkt <= PARSE_TEST_NUM_PKT);
+
+ for (i = 0; i < num_pkt; i++) {
+ uint32_t offs = l2_offset[i];
+ uint32_t data = 0;
+
+ parse_test.l2_offset[i] = offs;
+ pkt[i] = odp_packet_alloc(parse_test.pool, len + offs);
+ CU_ASSERT_FATAL(pkt[i] != ODP_PACKET_INVALID);
+
+ if (offs > 0) {
+ ret = fill_data_forward(pkt[i], 0, offs, &data);
+ CU_ASSERT(ret == 0);
+ }
+ ret = odp_packet_copy_from_mem(pkt[i], offs, len, test_packet);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+}
+
+/* Ethernet/IPv4/UDP */
+static void parse_eth_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ odp_packet_chksum_status_t chksum_status;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_udp,
+ sizeof(test_packet_ipv4_udp), num_pkt);
+
+ for (i = 0; i < num_pkt; i++) {
+ chksum_status = odp_packet_l3_chksum_status(pkt[i]);
+ CU_ASSERT(chksum_status == ODP_PACKET_CHKSUM_UNKNOWN);
+ chksum_status = odp_packet_l4_chksum_status(pkt[i]);
+ CU_ASSERT(chksum_status == ODP_PACKET_CHKSUM_UNKNOWN);
+ }
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_ALL;
+ parse.chksums = parse_test.all_chksums;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV4);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_UDP);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet SNAP/IPv4/UDP */
+static void parse_eth_snap_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ odp_packet_chksum_status_t chksum_status;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_snap_ipv4_udp,
+ sizeof(test_packet_snap_ipv4_udp), num_pkt);
+
+ for (i = 0; i < num_pkt; i++) {
+ chksum_status = odp_packet_l3_chksum_status(pkt[i]);
+ CU_ASSERT(chksum_status == ODP_PACKET_CHKSUM_UNKNOWN);
+ chksum_status = odp_packet_l4_chksum_status(pkt[i]);
+ CU_ASSERT(chksum_status == ODP_PACKET_CHKSUM_UNKNOWN);
+ }
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_ALL;
+ parse.chksums = parse_test.all_chksums;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV4);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_UDP);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* IPv4/UDP */
+static void parse_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+ uint32_t offset[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_udp,
+ sizeof(test_packet_ipv4_udp), num_pkt);
+
+ for (i = 0; i < num_pkt; i++)
+ offset[i] = parse_test.l2_offset[i] + 14;
+
+ parse.proto = ODP_PROTO_IPV4;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], offset[0], &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], &offset[1],
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV4);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_UDP);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/TCP */
+static void parse_eth_ipv4_tcp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_tcp,
+ sizeof(test_packet_ipv4_tcp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV4);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_TCP);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv6/UDP */
+static void parse_eth_ipv6_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv6_udp,
+ sizeof(test_packet_ipv6_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv6/TCP */
+static void parse_eth_ipv6_tcp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv6_tcp,
+ sizeof(test_packet_ipv6_tcp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_ALL;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/VLAN/IPv4/UDP */
+static void parse_eth_vlan_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_vlan_ipv4_udp,
+ sizeof(test_packet_vlan_ipv4_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/VLAN/IPv6/UDP */
+static void parse_eth_vlan_ipv6_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_vlan_ipv6_udp,
+ sizeof(test_packet_vlan_ipv6_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV6);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_UDP);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/VLAN/VLAN/IPv4/UDP */
+static void parse_eth_vlan_qinq_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_vlan_qinq_ipv4_udp,
+ sizeof(test_packet_vlan_qinq_ipv4_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(odp_packet_has_vlan_qinq(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/ARP */
+static void parse_eth_arp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_arp,
+ sizeof(test_packet_arp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_arp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/ICMP */
+static void parse_eth_ipv4_icmp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_icmp,
+ sizeof(test_packet_ipv4_icmp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_icmp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv6/ICMP */
+static void parse_eth_ipv6_icmp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv6_icmp,
+ sizeof(test_packet_ipv6_icmp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_icmp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/SCTP */
+static void parse_eth_ipv4_sctp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_sctp,
+ sizeof(test_packet_ipv4_sctp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_sctp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/IPSEC AH*/
+static void parse_eth_ipv4_ipsec_ah(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_ipsec_ah,
+ sizeof(test_packet_ipv4_ipsec_ah), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipsec(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/IPSEC ESP*/
+static void parse_eth_ipv4_ipsec_esp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_ipsec_esp,
+ sizeof(test_packet_ipv4_ipsec_esp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipsec(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv6/IPSEC AH*/
+static void parse_eth_ipv6_ipsec_ah(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv6_ipsec_ah,
+ sizeof(test_packet_ipv6_ipsec_ah), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipsec(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV6);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_AH);
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv6/IPSEC ESP*/
+static void parse_eth_ipv6_ipsec_esp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv6_ipsec_esp,
+ sizeof(test_packet_ipv6_ipsec_esp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipsec(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet mcast/IPv4 mcast/UDP */
+static void parse_mcast_eth_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_mcast_eth_ipv4_udp,
+ sizeof(test_packet_mcast_eth_ipv4_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_eth_mcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ip_mcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ip_bcast(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet bcast/IPv4 bcast/UDP */
+static void parse_bcast_eth_ipv4_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_bcast_eth_ipv4_udp,
+ sizeof(test_packet_bcast_eth_ipv4_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_eth_bcast(pkt[i]));
+ /* API specifies that Ethernet broadcast is also multicast */
+ CU_ASSERT(odp_packet_has_eth_mcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ip_bcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ip_mcast(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet mcast/IPv6 mcast/UDP */
+static void parse_mcast_eth_ipv6_udp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_mcast_eth_ipv6_udp,
+ sizeof(test_packet_mcast_eth_ipv6_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_eth_mcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_ip_mcast(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ip_bcast(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/UDP first fragment */
+static void parse_eth_ipv4_udp_first_frag(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_udp_first_frag,
+ sizeof(test_packet_ipv4_udp_first_frag), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipfrag(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipopt(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4/UDP last fragment */
+static void parse_eth_ipv4_udp_last_frag(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_udp_last_frag,
+ sizeof(test_packet_ipv4_udp_last_frag), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipfrag(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipopt(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+/* Ethernet/IPv4 + options (Record route, NOP)/ICMP */
+static void parse_eth_ipv4_rr_nop_icmp(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+
+ parse_test_alloc(pkt, test_packet_ipv4_rr_nop_icmp,
+ sizeof(test_packet_ipv4_rr_nop_icmp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_L4;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipopt(pkt[i]));
+ CU_ASSERT(odp_packet_has_icmp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipfrag(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(!odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+static void parse_result(void)
+{
+ odp_packet_parse_param_t parse;
+ int i;
+ int num_pkt = PARSE_TEST_NUM_PKT;
+ odp_packet_t pkt[num_pkt];
+ odp_packet_parse_result_t result[num_pkt];
+ odp_packet_parse_result_t *result_ptr[num_pkt];
+
+ /* Ethernet/VLAN/IPv6/UDP */
+ parse_test_alloc(pkt, test_packet_vlan_ipv6_udp,
+ sizeof(test_packet_vlan_ipv6_udp), num_pkt);
+
+ parse.proto = ODP_PROTO_ETH;
+ parse.last_layer = ODP_PROTO_LAYER_ALL;
+ parse.chksums.all_chksum = 0;
+
+ CU_ASSERT(odp_packet_parse(pkt[0], 0, &parse) == 0);
+ CU_ASSERT(odp_packet_parse_multi(&pkt[1], parse_test.l2_offset + 1,
+ num_pkt - 1, &parse) == (num_pkt - 1));
+
+ for (i = 0; i < num_pkt; i++) {
+ result_ptr[i] = &result[i];
+ memset(&result[i], 0, sizeof(odp_packet_parse_result_t));
+ }
+
+ odp_packet_parse_result(pkt[0], result_ptr[0]);
+ odp_packet_parse_result_multi(&pkt[1], &result_ptr[1], num_pkt - 1);
+
+ for (i = 0; i < num_pkt; i++) {
+ CU_ASSERT(odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(odp_packet_l2_type(pkt[i]) == ODP_PROTO_L2_TYPE_ETH);
+ CU_ASSERT(odp_packet_l3_type(pkt[i]) == ODP_PROTO_L3_TYPE_IPV6);
+ CU_ASSERT(odp_packet_l4_type(pkt[i]) == ODP_PROTO_L4_TYPE_UDP);
+
+ CU_ASSERT(result[i].flag.all != 0);
+ CU_ASSERT(result[i].flag.has_error ==
+ !!odp_packet_has_error(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l2_error ==
+ !!odp_packet_has_l2_error(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l3_error ==
+ !!odp_packet_has_l3_error(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l4_error ==
+ !!odp_packet_has_l4_error(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l2 ==
+ !!odp_packet_has_l2(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l3 ==
+ !!odp_packet_has_l3(pkt[i]));
+ CU_ASSERT(result[i].flag.has_l4 ==
+ !!odp_packet_has_l4(pkt[i]));
+ CU_ASSERT(result[i].flag.has_eth ==
+ !!odp_packet_has_eth(pkt[i]));
+ CU_ASSERT(result[i].flag.has_eth_bcast ==
+ !!odp_packet_has_eth_bcast(pkt[i]));
+ CU_ASSERT(result[i].flag.has_eth_mcast ==
+ !!odp_packet_has_eth_mcast(pkt[i]));
+ CU_ASSERT(result[i].flag.has_jumbo ==
+ !!odp_packet_has_jumbo(pkt[i]));
+ CU_ASSERT(result[i].flag.has_vlan ==
+ !!odp_packet_has_vlan(pkt[i]));
+ CU_ASSERT(result[i].flag.has_vlan_qinq ==
+ !!odp_packet_has_vlan_qinq(pkt[i]));
+ CU_ASSERT(result[i].flag.has_arp ==
+ !!odp_packet_has_arp(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ipv4 ==
+ !!odp_packet_has_ipv4(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ipv6 ==
+ !!odp_packet_has_ipv6(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ip_bcast ==
+ !!odp_packet_has_ip_bcast(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ip_mcast ==
+ !!odp_packet_has_ip_mcast(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ipfrag ==
+ !!odp_packet_has_ipfrag(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ipopt ==
+ !!odp_packet_has_ipopt(pkt[i]));
+ CU_ASSERT(result[i].flag.has_ipsec ==
+ !!odp_packet_has_ipsec(pkt[i]));
+ CU_ASSERT(result[i].flag.has_udp ==
+ !!odp_packet_has_udp(pkt[i]));
+ CU_ASSERT(result[i].flag.has_tcp ==
+ !!odp_packet_has_tcp(pkt[i]));
+ CU_ASSERT(result[i].flag.has_sctp ==
+ !!odp_packet_has_sctp(pkt[i]));
+ CU_ASSERT(result[i].flag.has_icmp ==
+ !!odp_packet_has_icmp(pkt[i]));
+
+ CU_ASSERT(result[i].packet_len == odp_packet_len(pkt[i]));
+ CU_ASSERT(result[i].l2_offset == odp_packet_l2_offset(pkt[i]));
+ CU_ASSERT(result[i].l3_offset == odp_packet_l3_offset(pkt[i]));
+ CU_ASSERT(result[i].l4_offset == odp_packet_l4_offset(pkt[i]));
+ CU_ASSERT(result[i].l3_chksum_status ==
+ odp_packet_l3_chksum_status(pkt[i]));
+ CU_ASSERT(result[i].l4_chksum_status ==
+ odp_packet_l4_chksum_status(pkt[i]));
+ CU_ASSERT(result[i].l2_type == odp_packet_l2_type(pkt[i]));
+ CU_ASSERT(result[i].l3_type == odp_packet_l3_type(pkt[i]));
+ CU_ASSERT(result[i].l4_type == odp_packet_l4_type(pkt[i]));
+ }
+
+ odp_packet_free_multi(pkt, num_pkt);
+}
+
+odp_testinfo_t packet_suite[] = {
+ ODP_TEST_INFO(packet_test_alloc_free),
+ ODP_TEST_INFO(packet_test_alloc_free_multi),
+ ODP_TEST_INFO(packet_test_free_sp),
+ ODP_TEST_INFO(packet_test_alloc_segmented),
+ ODP_TEST_INFO(packet_test_alloc_max_len),
+ ODP_TEST_INFO(packet_test_alloc_max_segment),
+ ODP_TEST_INFO(packet_test_alloc_align),
+ ODP_TEST_INFO(packet_test_basic_metadata),
+ ODP_TEST_INFO(packet_test_debug),
+ ODP_TEST_INFO(packet_test_segments),
+ ODP_TEST_INFO(packet_test_length),
+ ODP_TEST_INFO(packet_test_reset),
+ ODP_TEST_INFO(packet_test_prefetch),
+ ODP_TEST_INFO(packet_test_headroom),
+ ODP_TEST_INFO(packet_test_tailroom),
+ ODP_TEST_INFO(packet_test_context),
+ ODP_TEST_INFO(packet_test_payload_offset),
+ ODP_TEST_INFO(packet_test_event_conversion),
+ ODP_TEST_INFO(packet_test_layer_offsets),
+ ODP_TEST_INFO(packet_test_segment_last),
+ ODP_TEST_INFO(packet_test_in_flags),
+ ODP_TEST_INFO(packet_test_vlan_flags),
+ ODP_TEST_INFO(packet_test_error_flags),
+ ODP_TEST_INFO(packet_test_add_rem_data),
+ ODP_TEST_INFO(packet_test_meta_data_copy),
+ ODP_TEST_INFO(packet_test_copy),
+ ODP_TEST_INFO(packet_test_copydata),
+ ODP_TEST_INFO(packet_test_concatsplit),
+ ODP_TEST_INFO(packet_test_concat_small),
+ ODP_TEST_INFO(packet_test_concat_extend_trunc),
+ ODP_TEST_INFO(packet_test_extend_small),
+ ODP_TEST_INFO(packet_test_extend_large),
+ ODP_TEST_INFO(packet_test_extend_mix),
+ ODP_TEST_INFO(packet_test_extend_ref),
+ ODP_TEST_INFO(packet_test_align),
+ ODP_TEST_INFO(packet_test_offset),
+ ODP_TEST_INFO(packet_test_ref),
+ ODP_TEST_INFO(packet_test_max_pools),
+ ODP_TEST_INFO(packet_test_user_area),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_testinfo_t packet_vector_parse_suite[] = {
+ ODP_TEST_INFO(packet_vector_test_debug),
+ ODP_TEST_INFO(packet_vector_basic_test),
+ ODP_TEST_INFO(packet_vector_test_alloc_free),
+ ODP_TEST_INFO(packet_vector_test_tbl),
+ ODP_TEST_INFO(packet_vector_test_user_area),
+ ODP_TEST_INFO(packet_vector_test_event_conversion),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_testinfo_t packet_parse_suite[] = {
+ ODP_TEST_INFO(parse_eth_ipv4_udp),
+ ODP_TEST_INFO(parse_eth_snap_ipv4_udp),
+ ODP_TEST_INFO(parse_ipv4_udp),
+ ODP_TEST_INFO(parse_eth_ipv4_tcp),
+ ODP_TEST_INFO(parse_eth_ipv6_udp),
+ ODP_TEST_INFO(parse_eth_ipv6_tcp),
+ ODP_TEST_INFO(parse_eth_vlan_ipv4_udp),
+ ODP_TEST_INFO(parse_eth_vlan_ipv6_udp),
+ ODP_TEST_INFO(parse_eth_vlan_qinq_ipv4_udp),
+ ODP_TEST_INFO(parse_eth_arp),
+ ODP_TEST_INFO(parse_eth_ipv4_icmp),
+ ODP_TEST_INFO(parse_eth_ipv6_icmp),
+ ODP_TEST_INFO(parse_eth_ipv4_sctp),
+ ODP_TEST_INFO(parse_eth_ipv4_ipsec_ah),
+ ODP_TEST_INFO(parse_eth_ipv4_ipsec_esp),
+ ODP_TEST_INFO(parse_eth_ipv6_ipsec_ah),
+ ODP_TEST_INFO(parse_eth_ipv6_ipsec_esp),
+ ODP_TEST_INFO(parse_mcast_eth_ipv4_udp),
+ ODP_TEST_INFO(parse_bcast_eth_ipv4_udp),
+ ODP_TEST_INFO(parse_mcast_eth_ipv6_udp),
+ ODP_TEST_INFO(parse_eth_ipv4_udp_first_frag),
+ ODP_TEST_INFO(parse_eth_ipv4_udp_last_frag),
+ ODP_TEST_INFO(parse_eth_ipv4_rr_nop_icmp),
+ ODP_TEST_INFO(parse_result),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t packet_suites[] = {
+ { .name = "packet tests",
+ .testinfo_tbl = packet_suite,
+ .init_func = packet_suite_init,
+ .term_func = packet_suite_term,
+ },
+ { .name = "packet parse tests",
+ .testinfo_tbl = packet_parse_suite,
+ .init_func = packet_parse_suite_init,
+ .term_func = packet_parse_suite_term,
+ },
+ { .name = "packet vector tests",
+ .testinfo_tbl = packet_vector_parse_suite,
+ .init_func = packet_vector_suite_init,
+ .term_func = packet_vector_suite_term,
+ },
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(packet_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/pktio/.gitignore b/test/validation/api/pktio/.gitignore
index 1a5dd46e4..1a5dd46e4 100644
--- a/test/common_plat/validation/api/pktio/.gitignore
+++ b/test/validation/api/pktio/.gitignore
diff --git a/test/validation/api/pktio/Makefile.am b/test/validation/api/pktio/Makefile.am
new file mode 100644
index 000000000..c63809f8c
--- /dev/null
+++ b/test/validation/api/pktio/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = pktio_main
+pktio_main_SOURCES = pktio.c parser.c parser.h lso.c lso.h
diff --git a/test/validation/api/pktio/lso.c b/test/validation/api/pktio/lso.c
new file mode 100644
index 000000000..40d0917b4
--- /dev/null
+++ b/test/validation/api/pktio/lso.c
@@ -0,0 +1,938 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2022 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4.h>
+#include <test_packet_custom.h>
+
+#include <odp/helper/odph_api.h>
+
+#include "lso.h"
+
+#define MAX_NUM_IFACES 2
+#define PKT_POOL_NUM 256
+#define PKT_POOL_LEN (2 * 1024)
+
+/* Maximum number of segments test is prepared to receive per outgoing packet */
+#define MAX_NUM_SEG 256
+
+/* Pktio interface info
+ */
+typedef struct {
+ const char *name;
+ odp_pktio_t hdl;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+ odp_pktio_capability_t capa;
+} pktio_info_t;
+
+/* Interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/* Test interfaces */
+static pktio_info_t pktios[MAX_NUM_IFACES];
+static pktio_info_t *pktio_a;
+static pktio_info_t *pktio_b;
+
+/* Number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/* Some interface types cannot be restarted.
+ * These control test case execution in that case. */
+static int num_starts;
+static int disable_restart;
+
+/* While testing real-world interfaces additional time may be needed for
+ * external network to enable link to pktio interface that just become up. */
+static int wait_for_network;
+
+/* LSO test packet pool */
+odp_pool_t lso_pool = ODP_POOL_INVALID;
+
+/* Check test packet size */
+ODP_STATIC_ASSERT(sizeof(test_packet_ipv4_udp_1500) == 1500, "error: size is not 1500");
+ODP_STATIC_ASSERT(sizeof(test_packet_ipv4_udp_325) == 325, "error: size is not 325");
+ODP_STATIC_ASSERT(sizeof(test_packet_custom_eth_1) == 723, "error: size is not 723");
+
+static inline void wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+}
+
+static int pkt_pool_create(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&capa) != 0) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ if (capa.pkt.max_num && capa.pkt.max_num < PKT_POOL_NUM) {
+ ODPH_ERR("Packet pool size not supported. Max %" PRIu32 "\n", capa.pkt.max_num);
+ return -1;
+ } else if (capa.pkt.max_len && capa.pkt.max_len < PKT_POOL_LEN) {
+ ODPH_ERR("Packet length not supported.\n");
+ return -1;
+ } else if (capa.pkt.max_seg_len &&
+ capa.pkt.max_seg_len < PKT_POOL_LEN) {
+ ODPH_ERR("Segment length not supported.\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_LEN;
+ params.pkt.len = PKT_POOL_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ lso_pool = odp_pool_create("lso_pool", &params);
+ if (lso_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool create failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int idx, const char *name, odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t *capa;
+ int tx = (idx == 0) ? 1 : 0;
+ int rx = (idx == 0) ? 0 : 1;
+
+ if (num_ifaces == 1) {
+ tx = 1;
+ rx = 1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+ pktios[idx].hdl = pktio;
+ pktios[idx].name = name;
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktio_capability(pktio, &pktios[idx].capa)) {
+ ODPH_ERR("Pktio capa failed: %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ capa = &pktios[idx].capa;
+
+ odp_pktio_config_init(&config);
+
+ if (tx) {
+ if (capa->config.enable_lso)
+ config.enable_lso = 1;
+ else
+ ODPH_DBG("LSO not supported\n");
+ }
+
+ if (rx) {
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+ if (capa->config.pktin.bit.ipv4_chksum)
+ config.pktin.bit.ipv4_chksum = 1;
+ else
+ ODPH_DBG("IPv4 checksum not verified\n");
+ }
+
+ if (odp_pktio_config(pktio, &config)) {
+ ODPH_ERR("Failed to configure %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ /* By default, single input and output queue is used */
+ if (odp_pktin_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config input queue for %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config output queue for %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_packet_t create_packet(const uint8_t *data, uint32_t len)
+{
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(lso_pool, len);
+ if (pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ if (odp_packet_copy_from_mem(pkt, 0, len, data)) {
+ ODPH_ERR("Failed to copy test packet data\n");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ odp_packet_l2_offset_set(pkt, 0);
+
+ return pkt;
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+}
+
+static int send_packets(odp_lso_profile_t lso_profile, pktio_info_t *pktio_a, pktio_info_t *pktio_b,
+ const uint8_t *data, uint32_t len, uint32_t hdr_len, uint32_t max_payload,
+ uint32_t l3_offset, int use_opt)
+{
+ odp_packet_t pkt;
+ int ret;
+ odp_packet_lso_opt_t lso_opt;
+ odp_packet_lso_opt_t *opt_ptr = NULL;
+ int retries = 10;
+
+ pkt = create_packet(data, len);
+ if (pkt == ODP_PACKET_INVALID) {
+ CU_FAIL("failed to generate test packet");
+ return -1;
+ }
+
+ pktio_pkt_set_macs(pkt, pktio_a->hdl, pktio_b->hdl);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ lso_opt.lso_profile = lso_profile;
+ lso_opt.payload_offset = hdr_len;
+ lso_opt.max_payload_len = max_payload;
+
+ if (use_opt) {
+ opt_ptr = &lso_opt;
+ } else {
+ if (odp_packet_lso_request(pkt, &lso_opt)) {
+ CU_FAIL("LSO request failed");
+ return -1;
+ }
+
+ CU_ASSERT(odp_packet_has_lso_request(pkt));
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ }
+
+ if (l3_offset)
+ odp_packet_l3_offset_set(pkt, l3_offset);
+
+ while (retries) {
+ ret = odp_pktout_send_lso(pktio_a->pktout, &pkt, 1, opt_ptr);
+
+ CU_ASSERT_FATAL(ret < 2);
+
+ if (ret < 0) {
+ CU_FAIL("LSO send failed\n");
+ odp_packet_free(pkt);
+ return -1;
+ }
+ if (ret == 1)
+ break;
+
+ odp_time_wait_ns(10 * ODP_TIME_MSEC_IN_NS);
+ retries--;
+ }
+
+ if (ret < 1) {
+ CU_FAIL("LSO send timeout\n");
+ odp_packet_free(pkt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int recv_packets(pktio_info_t *pktio_info, uint64_t timeout_ns,
+ odp_packet_t *pkt_out, int max_num)
+{
+ odp_packet_t pkt;
+ odp_time_t wait_time, end;
+ int ret;
+ odp_pktin_queue_t pktin = pktio_info->pktin;
+ int num = 0;
+
+ wait_time = odp_time_local_from_ns(timeout_ns);
+ end = odp_time_sum(odp_time_local(), wait_time);
+
+ do {
+ pkt = ODP_PACKET_INVALID;
+ ret = odp_pktin_recv(pktin, &pkt, 1);
+
+ CU_ASSERT_FATAL(ret < 2);
+ if (ret < 0) {
+ CU_FAIL("Packet receive failed\n");
+ if (num)
+ odp_packet_free_multi(pkt_out, num);
+ return -1;
+ }
+
+ if (ret == 1) {
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pkt_out[num] = pkt;
+ num++;
+ if (num == max_num) {
+ CU_FAIL("Too many packets received\n");
+ return num;
+ }
+ }
+ } while (odp_time_cmp(end, odp_time_local()) > 0);
+
+ return num;
+}
+
+static int compare_data(odp_packet_t pkt, uint32_t offset, const uint8_t *data, uint32_t len)
+{
+ uint32_t i;
+ uint8_t *u8;
+
+ for (i = 0; i < len; i++) {
+ u8 = odp_packet_offset(pkt, offset + i, NULL, NULL);
+ if (*u8 != data[i])
+ return i;
+ }
+
+ return -1;
+}
+
+static int start_interfaces(void)
+{
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio = pktios[i].hdl;
+
+ if (odp_pktio_start(pktio)) {
+ ODPH_ERR("Failed to start interface: %s\n", pktios[i].name);
+ return -1;
+ }
+
+ wait_linkup(pktio);
+ }
+
+ return 0;
+}
+
+static int stop_interfaces(void)
+{
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio = pktios[i].hdl;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Failed to stop interface: %s\n", pktios[i].name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int lso_suite_init(void)
+{
+ int i;
+
+ if (getenv("ODP_PKTIO_TEST_DISABLE_START_STOP"))
+ disable_restart = 1;
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = 1;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ if (pkt_pool_create() != 0) {
+ ODPH_ERR("Failed to create pool\n");
+ return -1;
+ }
+
+ /* Create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio;
+ const char *name = iface_name[i];
+
+ pktio = create_pktio(i, name, lso_pool);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open interface: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktout_queue(pktio, &pktios[i].pktout, 1) != 1) {
+ ODPH_ERR("Failed to get pktout queue: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktin_queue(pktio, &pktios[i].pktin, 1) != 1) {
+ ODPH_ERR("Failed to get pktin queue: %s\n", name);
+ return -1;
+ }
+ }
+
+ pktio_a = &pktios[0];
+ pktio_b = &pktios[1];
+ if (num_ifaces == 1)
+ pktio_b = pktio_a;
+
+ return 0;
+}
+
+int lso_suite_term(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ if (odp_pktio_close(pktios[i].hdl)) {
+ ODPH_ERR("Failed to close pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(lso_pool) != 0) {
+ ODPH_ERR("Failed to destroy pool\n");
+ ret = -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+static int check_lso_custom(void)
+{
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pktio_a->capa.lso.proto.custom == 0 || pktio_a->capa.lso.mod_op.add_segment_num == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_custom_segs(uint32_t num)
+{
+ if (check_lso_custom() == ODP_TEST_INACTIVE)
+ return ODP_TEST_INACTIVE;
+
+ if (num > pktio_a->capa.lso.max_segments)
+ return ODP_TEST_INACTIVE;
+
+ if (disable_restart && num_starts > 0)
+ return ODP_TEST_INACTIVE;
+
+ /* Run only one packet IO test case when interface restart is disabled */
+ num_starts++;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_custom_segs_1(void)
+{
+ return check_lso_custom_segs(1);
+}
+
+static int check_lso_custom_segs_2(void)
+{
+ return check_lso_custom_segs(2);
+}
+
+static int check_lso_custom_segs_3(void)
+{
+ return check_lso_custom_segs(3);
+}
+
+static int check_lso_ipv4(void)
+{
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pktio_a->capa.lso.proto.ipv4 == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_ipv4_segs(uint32_t num)
+{
+ if (check_lso_ipv4() == ODP_TEST_INACTIVE)
+ return ODP_TEST_INACTIVE;
+
+ if (num > pktio_a->capa.lso.max_segments)
+ return ODP_TEST_INACTIVE;
+
+ if (disable_restart && num_starts > 0)
+ return ODP_TEST_INACTIVE;
+
+ num_starts++;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_ipv4_segs_1(void)
+{
+ return check_lso_ipv4_segs(1);
+}
+
+static int check_lso_ipv4_segs_2(void)
+{
+ return check_lso_ipv4_segs(2);
+}
+
+static int check_lso_ipv4_segs_3(void)
+{
+ return check_lso_ipv4_segs(3);
+}
+
+static void lso_capability(void)
+{
+ /* LSO not supported when max_profiles is zero */
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return;
+
+ CU_ASSERT(pktio_a->capa.lso.max_profiles >= pktio_a->capa.lso.max_profiles_per_pktio);
+ CU_ASSERT(pktio_a->capa.lso.max_packet_segments > 0);
+ /* At least 32 bytes of payload */
+ CU_ASSERT(pktio_a->capa.lso.max_payload_len >= 32);
+ /* LSO can create at least two segments */
+ CU_ASSERT(pktio_a->capa.lso.max_segments > 1);
+ /* LSO can copy at least Ethernet header to segments */
+ CU_ASSERT(pktio_a->capa.lso.max_payload_offset >= 14);
+
+ if (pktio_a->capa.lso.proto.custom) {
+ CU_ASSERT(pktio_a->capa.lso.max_num_custom > 0);
+
+ CU_ASSERT(pktio_a->capa.lso.mod_op.add_segment_num ||
+ pktio_a->capa.lso.mod_op.add_payload_len ||
+ pktio_a->capa.lso.mod_op.add_payload_offset)
+ }
+}
+
+static void lso_create_ipv4_profile(void)
+{
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+
+ odp_lso_profile_param_init(&param);
+ CU_ASSERT(param.lso_proto == ODP_LSO_PROTO_NONE);
+ CU_ASSERT(param.custom.num_custom == 0);
+
+ param.lso_proto = ODP_LSO_PROTO_IPV4;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_create_custom_profile(void)
+{
+ odp_lso_profile_param_t param_0, param_1;
+ odp_lso_profile_t profile_0, profile_1;
+
+ odp_lso_profile_param_init(&param_0);
+ CU_ASSERT(param_0.lso_proto == ODP_LSO_PROTO_NONE);
+ CU_ASSERT(param_0.custom.num_custom == 0);
+
+ param_0.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param_0.custom.num_custom = 1;
+ param_0.custom.field[0].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param_0.custom.field[0].offset = 16;
+ param_0.custom.field[0].size = 2;
+
+ profile_0 = odp_lso_profile_create(pktio_a->hdl, &param_0);
+ CU_ASSERT_FATAL(profile_0 != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_0) == 0);
+
+ if (pktio_a->capa.lso.max_profiles < 2 || pktio_a->capa.lso.max_num_custom < 3)
+ return;
+
+ if (pktio_a->capa.lso.mod_op.add_payload_len == 0 ||
+ pktio_a->capa.lso.mod_op.add_payload_offset == 0)
+ return;
+
+ odp_lso_profile_param_init(&param_1);
+ param_1.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param_1.custom.num_custom = 3;
+ param_1.custom.field[0].mod_op = ODP_LSO_ADD_PAYLOAD_LEN;
+ param_1.custom.field[0].offset = 14;
+ param_1.custom.field[0].size = 2;
+ param_1.custom.field[1].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param_1.custom.field[1].offset = 16;
+ param_1.custom.field[1].size = 2;
+ param_1.custom.field[2].mod_op = ODP_LSO_ADD_PAYLOAD_OFFSET;
+ param_1.custom.field[2].offset = 18;
+ param_1.custom.field[2].size = 2;
+
+ profile_0 = odp_lso_profile_create(pktio_a->hdl, &param_0);
+ CU_ASSERT_FATAL(profile_0 != ODP_LSO_PROFILE_INVALID);
+
+ profile_1 = odp_lso_profile_create(pktio_a->hdl, &param_1);
+ CU_ASSERT_FATAL(profile_1 != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_1) == 0);
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_0) == 0);
+}
+
+static void test_lso_request_clear(odp_lso_profile_t lso_profile, const uint8_t *data,
+ uint32_t len, uint32_t hdr_len, uint32_t max_payload)
+{
+ odp_packet_t pkt;
+ odp_packet_lso_opt_t lso_opt;
+
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ lso_opt.lso_profile = lso_profile;
+ lso_opt.payload_offset = hdr_len;
+ lso_opt.max_payload_len = max_payload;
+
+ pkt = create_packet(data, len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+ CU_ASSERT(odp_packet_lso_request(pkt, &lso_opt) == 0);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) != 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ odp_packet_lso_request_clr(pkt);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, ODP_PACKET_OFFSET_INVALID) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+
+ odp_packet_free(pkt);
+}
+
+static void lso_send_custom_eth(const uint8_t *test_packet, uint32_t pkt_len, uint32_t max_payload,
+ int use_opt)
+{
+ int i, ret, num, num_rcv;
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+ uint32_t offset, len, payload_len, payload_sum;
+ uint16_t segnum;
+ odp_packet_t pkt_out[MAX_NUM_SEG];
+ /* Ethernet 14B + custom headers 8B */
+ uint32_t hdr_len = 22;
+ /* Offset to "segment number" field */
+ uint32_t segnum_offset = 16;
+ uint32_t sent_payload = pkt_len - hdr_len;
+
+ odp_lso_profile_param_init(&param);
+ param.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param.custom.num_custom = 1;
+ param.custom.field[0].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param.custom.field[0].offset = segnum_offset;
+ param.custom.field[0].size = 2;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(start_interfaces() == 0);
+
+ test_lso_request_clear(profile, test_packet, pkt_len, hdr_len, max_payload);
+
+ ret = send_packets(profile, pktio_a, pktio_b, test_packet, pkt_len, hdr_len,
+ max_payload, 0, use_opt);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ODPH_DBG("\n Sent payload length: %u bytes\n", sent_payload);
+
+ /* Wait a bit to receive all created segments. Timeout and MAX_NUM_SEG values should be
+ * large enough to ensure that we receive all created segments. */
+ num = recv_packets(pktio_b, 100 * ODP_TIME_MSEC_IN_NS, pkt_out, MAX_NUM_SEG);
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num < MAX_NUM_SEG);
+
+ offset = hdr_len;
+ payload_sum = 0;
+ segnum = 0xffff;
+ num_rcv = 0;
+ for (i = 0; i < num; i++) {
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt_out[i], NULL);
+
+ /* Filter out possible non-test packets */
+ if (odp_be_to_cpu_16(eth->type) != 0x88B5)
+ continue;
+
+ len = odp_packet_len(pkt_out[i]);
+ payload_len = len - hdr_len;
+
+ ret = odp_packet_copy_to_mem(pkt_out[i], segnum_offset, 2, &segnum);
+
+ if (ret == 0) {
+ segnum = odp_be_to_cpu_16(segnum);
+ CU_ASSERT(segnum == num_rcv);
+ } else {
+ CU_FAIL("Seg num field read failed\n");
+ }
+
+ ODPH_DBG(" LSO segment[%u] payload: %u bytes\n", segnum, payload_len);
+
+ CU_ASSERT(payload_len <= max_payload);
+
+ if (compare_data(pkt_out[i], hdr_len,
+ test_packet_custom_eth_1 + offset, payload_len) >= 0) {
+ ODPH_ERR(" Payload compare failed at offset %u\n", offset);
+ CU_FAIL("Payload compare failed\n");
+ }
+
+ offset += payload_len;
+ payload_sum += payload_len;
+ num_rcv++;
+ }
+
+ ODPH_DBG(" Received payload length: %u bytes\n", payload_sum);
+
+ CU_ASSERT(payload_sum == sent_payload);
+
+ if (num > 0)
+ odp_packet_free_multi(pkt_out, num);
+
+ CU_ASSERT_FATAL(stop_interfaces() == 0);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_send_custom_eth_723(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_custom_eth_1);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_custom_eth(test_packet_custom_eth_1, pkt_len, max_payload, use_opt);
+}
+
+/* No segmentation needed: packet size 723 bytes, LSO segment payload 800 bytes */
+static void lso_send_custom_eth_723_800_pkt_meta(void)
+{
+ lso_send_custom_eth_723(800, 0);
+}
+
+static void lso_send_custom_eth_723_800_opt(void)
+{
+ lso_send_custom_eth_723(800, 1);
+}
+
+/* At least 2 segments: packet size 723 bytes, LSO segment payload 500 bytes */
+static void lso_send_custom_eth_723_500_pkt_meta(void)
+{
+ lso_send_custom_eth_723(500, 0);
+}
+
+static void lso_send_custom_eth_723_500_opt(void)
+{
+ lso_send_custom_eth_723(500, 1);
+}
+
+/* At least 3 segments: packet size 723 bytes, LSO segment payload 288 bytes */
+static void lso_send_custom_eth_723_288_pkt_meta(void)
+{
+ lso_send_custom_eth_723(288, 0);
+}
+
+static void lso_send_custom_eth_723_288_opt(void)
+{
+ lso_send_custom_eth_723(288, 1);
+}
+
+static void lso_send_ipv4(const uint8_t *test_packet, uint32_t pkt_len, uint32_t max_payload,
+ int use_opt)
+{
+ int i, ret, num;
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+ uint32_t offset, len, payload_len, payload_sum;
+ odp_packet_t packet[MAX_NUM_SEG];
+ /* Ethernet 14B + IPv4 header 20B */
+ uint32_t hdr_len = 34;
+ uint32_t sent_payload = pkt_len - hdr_len;
+
+ odp_lso_profile_param_init(&param);
+ param.lso_proto = ODP_LSO_PROTO_IPV4;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(start_interfaces() == 0);
+
+ test_lso_request_clear(profile, test_packet, pkt_len, hdr_len, max_payload);
+
+ ret = send_packets(profile, pktio_a, pktio_b, test_packet, pkt_len,
+ hdr_len, max_payload, 14, use_opt);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ODPH_DBG("\n Sent payload length: %u bytes\n", sent_payload);
+
+ /* Wait a bit to receive all created segments. Timeout and MAX_NUM_SEG values should be
+ * large enough to ensure that we receive all created segments. */
+ num = recv_packets(pktio_b, 100 * ODP_TIME_MSEC_IN_NS, packet, MAX_NUM_SEG);
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num < MAX_NUM_SEG);
+
+ offset = hdr_len;
+ payload_sum = 0;
+ for (i = 0; i < num; i++) {
+ if (!odp_packet_has_ipv4(packet[i]))
+ continue;
+
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(packet[i], NULL);
+
+ /* Filter out possible non-test packets */
+ if (odp_be_to_cpu_32(ip->dst_addr) != 0xc0a80101 ||
+ odp_be_to_cpu_32(ip->src_addr) != 0xc0a80102)
+ continue;
+
+ len = odp_packet_len(packet[i]);
+ payload_len = len - hdr_len;
+
+ ODPH_DBG(" LSO segment[%i] payload: %u bytes\n", i, payload_len);
+
+ CU_ASSERT(odp_packet_has_error(packet[i]) == 0);
+ CU_ASSERT(payload_len <= max_payload);
+
+ if (pkt_len > max_payload)
+ CU_ASSERT(odp_packet_has_ipfrag(packet[i]));
+
+ if (compare_data(packet[i], hdr_len, test_packet + offset, payload_len) >= 0) {
+ ODPH_ERR(" Payload compare failed at offset %u\n", offset);
+ CU_FAIL("Payload compare failed\n");
+ }
+
+ offset += payload_len;
+ payload_sum += payload_len;
+ }
+
+ ODPH_DBG(" Received payload length: %u bytes\n", payload_sum);
+
+ CU_ASSERT(payload_sum == sent_payload);
+
+ if (num > 0)
+ odp_packet_free_multi(packet, num);
+
+ CU_ASSERT_FATAL(stop_interfaces() == 0);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_send_ipv4_udp_325(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_ipv4_udp_325);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_ipv4(test_packet_ipv4_udp_325, pkt_len, max_payload, use_opt);
+}
+
+static void lso_send_ipv4_udp_1500(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_ipv4_udp_1500);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_ipv4(test_packet_ipv4_udp_1500, pkt_len, max_payload, use_opt);
+}
+
+/* No segmentation needed: packet size 325 bytes, LSO segment payload 700 bytes */
+static void lso_send_ipv4_325_700_pkt_meta(void)
+{
+ lso_send_ipv4_udp_325(700, 0);
+}
+
+static void lso_send_ipv4_325_700_opt(void)
+{
+ lso_send_ipv4_udp_325(700, 1);
+}
+
+/* At least 2 segments: packet size 1500 bytes, LSO segment payload 1000 bytes */
+static void lso_send_ipv4_1500_1000_pkt_meta(void)
+{
+ lso_send_ipv4_udp_1500(1000, 0);
+}
+
+static void lso_send_ipv4_1500_1000_opt(void)
+{
+ lso_send_ipv4_udp_1500(1000, 1);
+}
+
+/* At least 3 segments: packet size 1500 bytes, LSO segment payload 700 bytes */
+static void lso_send_ipv4_1500_700_pkt_meta(void)
+{
+ lso_send_ipv4_udp_1500(700, 0);
+}
+
+static void lso_send_ipv4_1500_700_opt(void)
+{
+ lso_send_ipv4_udp_1500(700, 1);
+}
+
+odp_testinfo_t lso_suite[] = {
+ ODP_TEST_INFO(lso_capability),
+ ODP_TEST_INFO_CONDITIONAL(lso_create_ipv4_profile, check_lso_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(lso_create_custom_profile, check_lso_custom),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_325_700_pkt_meta, check_lso_ipv4_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_325_700_opt, check_lso_ipv4_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_1000_pkt_meta, check_lso_ipv4_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_1000_opt, check_lso_ipv4_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_700_pkt_meta, check_lso_ipv4_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_700_opt, check_lso_ipv4_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_800_pkt_meta, check_lso_custom_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_800_opt, check_lso_custom_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_500_pkt_meta, check_lso_custom_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_500_opt, check_lso_custom_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_288_pkt_meta, check_lso_custom_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_288_opt, check_lso_custom_segs_3),
+ ODP_TEST_INFO_NULL
+};
diff --git a/test/validation/api/pktio/lso.h b/test/validation/api/pktio/lso.h
new file mode 100644
index 000000000..d5688369f
--- /dev/null
+++ b/test/validation/api/pktio/lso.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Nokia
+ */
+
+#ifndef _ODP_TEST_PKTIO_LSO_H_
+#define _ODP_TEST_PKTIO_LSO_H_
+
+#include <odp_cunit_common.h>
+
+/* test array init/term functions: */
+int lso_suite_term(void);
+int lso_suite_init(void);
+
+/* test arrays: */
+extern odp_testinfo_t lso_suite[];
+
+#endif
diff --git a/test/validation/api/pktio/parser.c b/test/validation/api/pktio/parser.c
new file mode 100644
index 000000000..a21a9c0f8
--- /dev/null
+++ b/test/validation/api/pktio/parser.c
@@ -0,0 +1,607 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4.h>
+#include <test_packet_ipv6.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <stdlib.h>
+#include "parser.h"
+
+#define MAX_NUM_IFACES 2
+#define PKT_POOL_NUM 256
+#define PKT_POOL_BUF_LEN (2 * 1024)
+
+/**
+ * local container for pktio attributes
+ */
+typedef struct {
+ const char *name;
+ odp_pktio_t hdl;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+} pktio_info_t;
+
+/** Interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/** Test interfaces */
+pktio_info_t pktios[MAX_NUM_IFACES];
+pktio_info_t *pktio_a;
+pktio_info_t *pktio_b;
+
+/** Number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/** While testing real-world interfaces additional time may be needed for
+ * external network to enable link to pktio interface that just become up.
+ */
+static bool wait_for_network;
+
+/** Parser packet pool */
+odp_pool_t parser_pool = ODP_POOL_INVALID;
+
+static inline void wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+}
+
+static int pkt_pool_create(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&capa) != 0) {
+ ODPH_ERR("Unable to query pool capability\n");
+ return -1;
+ }
+
+ if (capa.pkt.max_num && capa.pkt.max_num < PKT_POOL_NUM) {
+ ODPH_ERR("Packet pool size not supported: MAX=%" PRIu32 "\n", capa.pkt.max_num);
+ return -1;
+ } else if (capa.pkt.max_len && capa.pkt.max_len < PKT_POOL_BUF_LEN) {
+ ODPH_ERR("Packet length not supported\n");
+ return -1;
+ } else if (capa.pkt.max_seg_len &&
+ capa.pkt.max_seg_len < PKT_POOL_BUF_LEN) {
+ ODPH_ERR("Segment length not supported\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_BUF_LEN;
+ params.pkt.len = PKT_POOL_BUF_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ parser_pool = odp_pool_create("pkt_pool_default", &params);
+ if (parser_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool create failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int iface_idx, odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ odp_pktio_param_t pktio_param;
+ const char *iface = iface_name[iface_idx];
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface, pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktio_config_init(&config);
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+ if (odp_pktio_config(pktio, &config)) {
+ ODPH_ERR("Failed to configure %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ /* By default, single input and output queue is used */
+ if (odp_pktin_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config input queue for %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config output queue for %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_packet_t create_packet(const uint8_t *data, uint32_t len)
+{
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(parser_pool, len);
+ if (pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ if (odp_packet_copy_from_mem(pkt, 0, len, data)) {
+ ODPH_ERR("Failed to copy test packet data\n");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ odp_packet_l2_offset_set(pkt, 0);
+
+ return pkt;
+}
+
+/**
+ * Receive incoming packets and compare them to the original. Function returns
+ * a valid packet handle only when the received packet matches to the original
+ * packet.
+ */
+static odp_packet_t recv_and_cmp_packet(odp_pktin_queue_t pktin,
+ odp_packet_t orig_pkt, uint64_t ns)
+{
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+ odp_time_t wait_time, end;
+ uint32_t orig_len;
+ uint8_t *orig_data;
+
+ orig_len = odp_packet_len(orig_pkt);
+ orig_data = odp_packet_data(orig_pkt);
+ wait_time = odp_time_local_from_ns(ns);
+ end = odp_time_sum(odp_time_local(), wait_time);
+
+ do {
+ int ret;
+ odp_packet_t tmp_pkt;
+
+ ret = odp_pktin_recv(pktin, &tmp_pkt, 1);
+ if (ret < 0)
+ break;
+
+ if (ret == 1) {
+ uint32_t len;
+ uint8_t *data;
+
+ len = odp_packet_len(tmp_pkt);
+ data = odp_packet_data(tmp_pkt);
+
+ if (len == orig_len &&
+ memcmp(data, orig_data, len) == 0) {
+ pkt = tmp_pkt;
+ break;
+ }
+ odp_packet_free(tmp_pkt);
+ }
+ } while (odp_time_cmp(end, odp_time_local()) > 0);
+
+ return pkt;
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+}
+
+/**
+ * Creates a test packet from data array and loops it through the test pktio
+ * interfaces forcing packet parsing.
+ */
+static odp_packet_t loopback_packet(pktio_info_t *pktio_a,
+ pktio_info_t *pktio_b, const uint8_t *data,
+ uint32_t len)
+{
+ odp_packet_t pkt;
+ odp_packet_t sent_pkt;
+
+ pkt = create_packet(data, len);
+ if (pkt == ODP_PACKET_INVALID) {
+ CU_FAIL("failed to generate test packet");
+ return ODP_PACKET_INVALID;
+ }
+
+ pktio_pkt_set_macs(pkt, pktio_a->hdl, pktio_b->hdl);
+
+ sent_pkt = odp_packet_copy(pkt, parser_pool);
+ if (sent_pkt == ODP_PACKET_INVALID) {
+ CU_FAIL_FATAL("failed to copy test packet");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ while (1) {
+ int ret = odp_pktout_send(pktio_a->pktout, &pkt, 1);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet");
+ odp_packet_free(pkt);
+ odp_packet_free(sent_pkt);
+ return ODP_PACKET_INVALID;
+ }
+ if (ret == 1)
+ break;
+ }
+
+ /* and wait for them to arrive back */
+ pkt = recv_and_cmp_packet(pktio_b->pktin, sent_pkt, ODP_TIME_SEC_IN_NS);
+ odp_packet_free(sent_pkt);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b->hdl);
+ CU_ASSERT(odp_packet_has_error(pkt) == 0);
+
+ return pkt;
+}
+
+static void parser_test_arp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_arp,
+ sizeof(test_packet_arp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_arp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_icmp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_icmp,
+ sizeof(test_packet_ipv4_icmp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_icmp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_tcp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_tcp,
+ sizeof(test_packet_ipv4_tcp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_tcp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_udp,
+ sizeof(test_packet_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv4_udp,
+ sizeof(test_packet_vlan_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_qinq_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_qinq_ipv4_udp,
+ sizeof(test_packet_vlan_qinq_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_vlan_qinq(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_sctp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_sctp,
+ sizeof(test_packet_ipv4_sctp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_sctp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_icmp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_icmp,
+ sizeof(test_packet_ipv6_icmp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_icmp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_tcp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_tcp,
+ sizeof(test_packet_ipv6_tcp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_tcp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_udp,
+ sizeof(test_packet_ipv6_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_ipv6_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv6_udp,
+ sizeof(test_packet_vlan_ipv6_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_sctp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_sctp,
+ sizeof(test_packet_ipv6_sctp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_sctp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+int parser_suite_init(void)
+{
+ int i;
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = true;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ if (pkt_pool_create() != 0) {
+ ODPH_ERR("Failed to create parser pool\n");
+ return -1;
+ }
+
+ /* Create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio_info_t *io;
+
+ io = &pktios[i];
+ io->name = iface_name[i];
+ io->hdl = create_pktio(i, parser_pool);
+ if (io->hdl == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open iface");
+ return -1;
+ }
+
+ if (odp_pktout_queue(io->hdl, &io->pktout, 1) != 1) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ if (odp_pktin_queue(io->hdl, &io->pktin, 1) != 1) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ if (odp_pktio_start(io->hdl)) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ wait_linkup(io->hdl);
+ }
+
+ pktio_a = &pktios[0];
+ pktio_b = &pktios[1];
+ if (num_ifaces == 1)
+ pktio_b = pktio_a;
+
+ return 0;
+}
+
+int parser_suite_term(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ if (odp_pktio_stop(pktios[i].hdl)) {
+ ODPH_ERR("Failed to stop pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ if (odp_pktio_close(pktios[i].hdl)) {
+ ODPH_ERR("Failed to close pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(parser_pool) != 0) {
+ ODPH_ERR("Failed to destroy packet pool\n");
+ ret = -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+/**
+ * Certain tests can only be run with 'loop' pktio.
+ */
+static int loop_pktio(void)
+{
+ if (strcmp(iface_name[0], "loop") == 0)
+ return ODP_TEST_ACTIVE;
+ else
+ return ODP_TEST_INACTIVE;
+}
+
+odp_testinfo_t parser_suite[] = {
+ ODP_TEST_INFO(parser_test_arp),
+ ODP_TEST_INFO(parser_test_ipv4_icmp),
+ ODP_TEST_INFO(parser_test_ipv4_tcp),
+ ODP_TEST_INFO(parser_test_ipv4_udp),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv4_udp, loop_pktio),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_qinq_ipv4_udp, loop_pktio),
+ ODP_TEST_INFO(parser_test_ipv4_sctp),
+ ODP_TEST_INFO(parser_test_ipv6_icmp),
+ ODP_TEST_INFO(parser_test_ipv6_tcp),
+ ODP_TEST_INFO(parser_test_ipv6_udp),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv6_udp, loop_pktio),
+ ODP_TEST_INFO(parser_test_ipv6_sctp),
+ ODP_TEST_INFO_NULL
+};
diff --git a/test/validation/api/pktio/parser.h b/test/validation/api/pktio/parser.h
new file mode 100644
index 000000000..8341d9a2c
--- /dev/null
+++ b/test/validation/api/pktio/parser.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2017-2018 Linaro Limited
+ */
+
+#ifndef _ODP_TEST_PARSER_H_
+#define _ODP_TEST_PARSER_H_
+
+#include <odp_cunit_common.h>
+
+/* test array init/term functions: */
+int parser_suite_term(void);
+int parser_suite_init(void);
+
+/* test arrays: */
+extern odp_testinfo_t parser_suite[];
+
+#endif
diff --git a/test/validation/api/pktio/pktio.c b/test/validation/api/pktio/pktio.c
new file mode 100644
index 000000000..c616986b0
--- /dev/null
+++ b/test/validation/api/pktio/pktio.c
@@ -0,0 +1,5516 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020-2024 Nokia
+ * Copyright (c) 2020 Marvell
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include "parser.h"
+#include "lso.h"
+
+#define PKT_BUF_NUM 128
+#define PKT_BUF_SIZE (9 * 1024)
+#define PKT_LEN_NORMAL 64
+#define PKT_LEN_MAX (PKT_BUF_SIZE - ODPH_ETHHDR_LEN - \
+ ODPH_IPV4HDR_LEN - ODPH_UDPHDR_LEN)
+
+#define USE_MTU 0
+#define MAX_NUM_IFACES 2
+#define TEST_SEQ_INVALID ((uint32_t)~0)
+#define TEST_SEQ_MAGIC 0x92749451
+#define TX_BATCH_LEN 4
+#define PKTV_TX_BATCH_LEN 32
+#define PKTV_DEFAULT_SIZE 8
+#define MAX_QUEUES 128
+
+#define PKTIO_TS_INTERVAL (50 * ODP_TIME_MSEC_IN_NS)
+#define PKTIO_TS_MIN_RES 1000
+#define PKTIO_TS_MAX_RES 10000000000
+
+#define PKTIO_SRC_MAC {1, 2, 3, 4, 5, 6}
+#define PKTIO_DST_MAC {6, 5, 4, 3, 2, 1}
+#undef DEBUG_STATS
+
+/** interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/** number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/** while testing real-world interfaces additional time may be
+ needed for external network to enable link to pktio
+ interface that just become up.*/
+static bool wait_for_network;
+
+/* Dummy global variable to avoid compiler optimizing out API calls */
+static volatile uint64_t test_pktio_dummy_u64;
+
+/** local container for pktio attributes */
+typedef struct {
+ const char *name;
+ odp_pktio_t id;
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue_out;
+ odp_queue_t inq;
+ odp_pktin_mode_t in_mode;
+} pktio_info_t;
+
+/** magic number and sequence at start of UDP payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+ odp_u32be_t seq;
+} pkt_head_t;
+
+/** magic number at end of UDP payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+} pkt_tail_t;
+
+/** Run mode */
+typedef enum {
+ PKT_POOL_UNSEGMENTED,
+ PKT_POOL_SEGMENTED,
+} pkt_segmented_e;
+
+typedef enum {
+ TXRX_MODE_SINGLE,
+ TXRX_MODE_MULTI,
+ TXRX_MODE_MULTI_EVENT
+} txrx_mode_e;
+
+typedef enum {
+ RECV_TMO,
+ RECV_MQ_TMO,
+ RECV_MQ_TMO_NO_IDX,
+} recv_tmo_mode_e;
+
+typedef enum {
+ ETH_UNICAST,
+ ETH_BROADCAST,
+} eth_addr_type_e;
+
+/** size of transmitted packets */
+static uint32_t packet_len = PKT_LEN_NORMAL;
+
+/** default packet pool */
+odp_pool_t default_pkt_pool = ODP_POOL_INVALID;
+
+/** default packet vector pool */
+odp_pool_t default_pktv_pool = ODP_POOL_INVALID;
+
+/** sequence number of IP packets */
+odp_atomic_u32_t ip_seq;
+
+/** Type of pool segmentation */
+pkt_segmented_e pool_segmentation = PKT_POOL_UNSEGMENTED;
+
+odp_pool_t pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
+
+odp_pool_t pktv_pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
+
+static inline void _pktio_wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ if (ret != -1) {
+ /* assert only if link state supported and
+ * it's down. */
+ CU_ASSERT_FATAL(ret == 1);
+ }
+}
+
+static void set_pool_len(odp_pool_param_t *params, odp_pool_capability_t *capa)
+{
+ uint32_t len;
+ uint32_t seg_len;
+
+ len = (capa->pkt.max_len && capa->pkt.max_len < PKT_BUF_SIZE) ?
+ capa->pkt.max_len : PKT_BUF_SIZE;
+ seg_len = (capa->pkt.max_seg_len && capa->pkt.max_seg_len < PKT_BUF_SIZE) ?
+ capa->pkt.max_seg_len : PKT_BUF_SIZE;
+
+ switch (pool_segmentation) {
+ case PKT_POOL_SEGMENTED:
+ /* Force segment to minimum size */
+ params->pkt.seg_len = 0;
+ params->pkt.len = len;
+ break;
+ case PKT_POOL_UNSEGMENTED:
+ default:
+ params->pkt.seg_len = seg_len;
+ params->pkt.len = len;
+ break;
+ }
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst,
+ eth_addr_type_e dst_addr_type)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ if (dst_addr_type == ETH_UNICAST) {
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+ } else {
+ CU_ASSERT(odph_eth_addr_parse(&eth->dst, "ff:ff:ff:ff:ff:ff") == 0);
+ }
+}
+
+static uint32_t pktio_pkt_set_seq(odp_packet_t pkt, size_t l4_hdr_len)
+{
+ static uint32_t tstseq;
+ size_t off;
+ pkt_head_t head;
+ pkt_tail_t tail;
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ CU_FAIL("packet L4 offset not set");
+ return TEST_SEQ_INVALID;
+ }
+
+ head.magic = TEST_SEQ_MAGIC;
+ head.seq = tstseq;
+
+ off += l4_hdr_len;
+ if (odp_packet_copy_from_mem(pkt, off, sizeof(head), &head) != 0)
+ return TEST_SEQ_INVALID;
+
+ tail.magic = TEST_SEQ_MAGIC;
+ off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
+ if (odp_packet_copy_from_mem(pkt, off, sizeof(tail), &tail) != 0)
+ return TEST_SEQ_INVALID;
+
+ tstseq++;
+
+ return head.seq;
+}
+
+static uint32_t pktio_pkt_seq_hdr(odp_packet_t pkt, size_t l4_hdr_len)
+{
+ size_t off;
+ uint32_t seq = TEST_SEQ_INVALID;
+ pkt_head_t head;
+ pkt_tail_t tail;
+
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("pkt invalid\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ ODPH_ERR("offset invalid\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ off += l4_hdr_len;
+ if (odp_packet_copy_to_mem(pkt, off, sizeof(head), &head) != 0) {
+ ODPH_ERR("header copy failed\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ if (head.magic != TEST_SEQ_MAGIC) {
+ ODPH_ERR("header magic invalid 0x%" PRIx32 "\n", head.magic);
+ odp_packet_print(pkt);
+ return TEST_SEQ_INVALID;
+ }
+
+ if (odp_packet_len(pkt) == packet_len) {
+ off = packet_len - sizeof(tail);
+ if (odp_packet_copy_to_mem(pkt, off, sizeof(tail),
+ &tail) != 0) {
+ ODPH_ERR("header copy failed\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ if (tail.magic == TEST_SEQ_MAGIC) {
+ seq = head.seq;
+ CU_ASSERT(seq != TEST_SEQ_INVALID);
+ } else {
+ ODPH_ERR("tail magic invalid 0x%" PRIx32 "\n", tail.magic);
+ }
+ } else {
+ ODPH_ERR("packet length invalid: %" PRIu32 "(%" PRIu32 ")\n",
+ odp_packet_len(pkt), packet_len);
+ }
+
+ return seq;
+}
+
+static uint32_t pktio_pkt_seq(odp_packet_t pkt)
+{
+ return pktio_pkt_seq_hdr(pkt, ODPH_UDPHDR_LEN);
+}
+
+static void pktio_init_packet_eth_ipv4(odp_packet_t pkt, uint8_t proto)
+{
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ char *buf;
+ uint16_t seq;
+ uint8_t src_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_SRC_MAC;
+ uint8_t dst_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_DST_MAC;
+ int pkt_len = odp_packet_len(pkt);
+
+ buf = odp_packet_data(pkt);
+
+ /* Ethernet */
+ odp_packet_l2_offset_set(pkt, 0);
+ eth = (odph_ethhdr_t *)buf;
+ memcpy(eth->src.addr, src_mac, ODPH_ETHADDR_LEN);
+ memcpy(eth->dst.addr, dst_mac, ODPH_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ /* IP */
+ odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
+ ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
+ ip->dst_addr = odp_cpu_to_be_32(0x0a000064);
+ ip->src_addr = odp_cpu_to_be_32(0x0a000001);
+ ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(pkt_len - ODPH_ETHHDR_LEN);
+ ip->ttl = 128;
+ ip->proto = proto;
+ seq = odp_atomic_fetch_inc_u32(&ip_seq);
+ ip->id = odp_cpu_to_be_16(seq);
+ ip->chksum = 0;
+ ip->frag_offset = 0;
+ ip->tos = 0;
+ odph_ipv4_csum_update(pkt);
+}
+
+static uint32_t pktio_init_packet_udp(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp;
+ char *buf;
+ int pkt_len = odp_packet_len(pkt);
+
+ buf = odp_packet_data(pkt);
+
+ pktio_init_packet_eth_ipv4(pkt, ODPH_IPPROTO_UDP);
+
+ /* UDP */
+ odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ udp = (odph_udphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ udp->src_port = odp_cpu_to_be_16(12049);
+ udp->dst_port = odp_cpu_to_be_16(12050);
+ udp->length = odp_cpu_to_be_16(pkt_len -
+ ODPH_ETHHDR_LEN - ODPH_IPV4HDR_LEN);
+ udp->chksum = 0;
+
+ return pktio_pkt_set_seq(pkt, ODPH_UDPHDR_LEN);
+}
+
+static uint32_t pktio_init_packet_sctp(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp;
+ char *buf;
+
+ buf = odp_packet_data(pkt);
+
+ pktio_init_packet_eth_ipv4(pkt, ODPH_IPPROTO_SCTP);
+
+ /* SCTP */
+ odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ sctp = (odph_sctphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ sctp->src_port = odp_cpu_to_be_16(12049);
+ sctp->dst_port = odp_cpu_to_be_16(12050);
+ sctp->tag = 0;
+ sctp->chksum = 0;
+
+ return pktio_pkt_set_seq(pkt, ODPH_SCTPHDR_LEN);
+}
+
+static int pktio_zero_checksums(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip;
+ uint32_t len;
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len);
+
+ ip->chksum = 0;
+
+ if (ip->proto == ODPH_IPPROTO_UDP) {
+ odph_udphdr_t *udp;
+
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, &len);
+ udp->chksum = 0;
+ } else if (ip->proto == ODPH_IPPROTO_SCTP) {
+ odph_sctphdr_t *sctp;
+
+ sctp = (odph_sctphdr_t *)odp_packet_l4_ptr(pkt, &len);
+ sctp->chksum = 0;
+ } else {
+ CU_FAIL("unexpected L4 protocol");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_fixup_checksums(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip;
+
+ pktio_zero_checksums(pkt);
+
+ odph_ipv4_csum_update(pkt);
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ if (ip->proto == ODPH_IPPROTO_UDP) {
+ odph_udphdr_t *udp;
+
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->chksum = odph_ipv4_udp_chksum(pkt);
+ } else if (ip->proto == ODPH_IPPROTO_SCTP) {
+ odph_sctp_chksum_set(pkt);
+ } else {
+ CU_FAIL("unexpected L4 protocol");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int default_pool_create(void)
+{
+ odp_pool_param_t params;
+ odp_pool_capability_t pool_capa;
+ char pool_name[ODP_POOL_NAME_LEN];
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (default_pkt_pool != ODP_POOL_INVALID)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ params.pkt.num = PKT_BUF_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ snprintf(pool_name, sizeof(pool_name),
+ "pkt_pool_default_%d", pool_segmentation);
+ default_pkt_pool = odp_pool_create(pool_name, &params);
+ if (default_pkt_pool == ODP_POOL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+static int default_pktv_pool_create(void)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (pool_capa.vector.max_num < PKT_BUF_NUM)
+ return -1;
+
+ if (default_pktv_pool != ODP_POOL_INVALID)
+ return -1;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = PKT_BUF_NUM;
+ params.vector.max_size = pool_capa.vector.max_size;
+
+ snprintf(pool_name, sizeof(pool_name),
+ "pktv_pool_default_%d", pool_segmentation);
+ default_pktv_pool = odp_pool_create(pool_name, &params);
+ if (default_pktv_pool == ODP_POOL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int iface_idx, odp_pktin_mode_t imode,
+ odp_pktout_mode_t omode)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ const char *iface = iface_name[iface_idx];
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = imode;
+ pktio_param.out_mode = omode;
+
+ pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_pktio_to_u64(pktio) !=
+ odp_pktio_to_u64(ODP_PKTIO_INVALID));
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ /* Atomic queue when in scheduled mode */
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ /* By default, single input and output queue in all modes. Config can
+ * be overridden before starting the interface. */
+ CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0);
+ CU_ASSERT(odp_pktout_queue_config(pktio, NULL) == 0);
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_pktio_t create_pktv_pktio(int iface_idx, odp_pktin_mode_t imode,
+ odp_pktout_mode_t omode, odp_schedule_sync_t sync_mode)
+{
+ const char *iface = iface_name[iface_idx];
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = imode;
+ pktio_param.out_mode = omode;
+
+ pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_capability(pktio, &capa) == 0);
+ if (!capa.vector.supported) {
+ printf("Vector mode is not supported. Test Skipped.\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ if (imode == ODP_PKTIN_MODE_SCHED) {
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = sync_mode;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ pktin_param.hash_enable = 0;
+ pktin_param.num_queues = 1;
+ pktin_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ pktin_param.vector.enable = 1;
+ pktin_param.vector.pool = pktv_pool[iface_idx];
+ pktin_param.vector.max_size = capa.vector.max_size < PKTV_DEFAULT_SIZE ?
+ capa.vector.max_size : PKTV_DEFAULT_SIZE;
+ pktin_param.vector.max_tmo_ns = capa.vector.min_tmo_ns;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0);
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ pktout_param.num_queues = 1;
+ CU_ASSERT(odp_pktout_queue_config(pktio, &pktout_param) == 0);
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static int flush_input_queue(odp_pktio_t pktio, odp_pktin_mode_t imode)
+{
+ odp_event_t ev;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+
+ if (imode == ODP_PKTIN_MODE_QUEUE) {
+ /* Assert breaks else-if without brackets */
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio, &queue, 1) == 1);
+ } else if (imode == ODP_PKTIN_MODE_DIRECT) {
+ return 0;
+ }
+
+ /* flush any pending events */
+ while (1) {
+ if (queue != ODP_QUEUE_INVALID)
+ ev = odp_queue_deq(queue);
+ else
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ break;
+ }
+
+ return 0;
+}
+
+static int create_packets_udp(odp_packet_t pkt_tbl[],
+ uint32_t pkt_seq[],
+ int num,
+ odp_pktio_t pktio_src,
+ odp_pktio_t pktio_dst,
+ odp_bool_t fix_cs,
+ eth_addr_type_e dst_addr_type)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len);
+ if (pkt_tbl[i] == ODP_PACKET_INVALID)
+ break;
+
+ pkt_seq[i] = pktio_init_packet_udp(pkt_tbl[i]);
+ if (pkt_seq[i] == TEST_SEQ_INVALID) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+
+ pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst, dst_addr_type);
+
+ /* Set user pointer. It should be NULL on receive side. */
+ odp_packet_user_ptr_set(pkt_tbl[i], (void *)1);
+
+ if (fix_cs)
+ ret = pktio_fixup_checksums(pkt_tbl[i]);
+ else
+ ret = pktio_zero_checksums(pkt_tbl[i]);
+ if (ret != 0) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int create_packets_sctp(odp_packet_t pkt_tbl[],
+ uint32_t pkt_seq[],
+ int num,
+ odp_pktio_t pktio_src,
+ odp_pktio_t pktio_dst)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len);
+ if (pkt_tbl[i] == ODP_PACKET_INVALID)
+ break;
+
+ pkt_seq[i] = pktio_init_packet_sctp(pkt_tbl[i]);
+ if (pkt_seq[i] == TEST_SEQ_INVALID) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+
+ pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst, ETH_UNICAST);
+
+ ret = pktio_zero_checksums(pkt_tbl[i]);
+ if (ret != 0) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int create_packets(odp_packet_t pkt_tbl[], uint32_t pkt_seq[], int num,
+ odp_pktio_t pktio_src, odp_pktio_t pktio_dst)
+{
+ return create_packets_udp(pkt_tbl, pkt_seq, num, pktio_src, pktio_dst,
+ true, ETH_UNICAST);
+}
+
+static int get_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ int num, txrx_mode_e mode, odp_bool_t vector_mode)
+{
+ odp_event_t evt_tbl[num];
+ int num_evts = 0;
+ int num_pkts = 0;
+ int i, ret;
+
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_DIRECT) {
+ odp_pktin_queue_t pktin;
+
+ ret = odp_pktin_queue(pktio_rx->id, &pktin, 1);
+
+ if (ret != 1) {
+ CU_FAIL_FATAL("No pktin queues");
+ return -1;
+ }
+
+ return odp_pktin_recv(pktin, pkt_tbl, num);
+ }
+
+ if (mode == TXRX_MODE_MULTI) {
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
+ num_evts = odp_queue_deq_multi(pktio_rx->inq, evt_tbl,
+ num);
+ else
+ num_evts = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
+ evt_tbl, num);
+ } else {
+ odp_event_t evt_tmp = ODP_EVENT_INVALID;
+
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
+ evt_tmp = odp_queue_deq(pktio_rx->inq);
+ else
+ evt_tmp = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (evt_tmp != ODP_EVENT_INVALID)
+ evt_tbl[num_evts++] = evt_tmp;
+ }
+
+ /* convert events to packets, discarding any non-packet events */
+ for (i = 0; i < num_evts; ++i) {
+ if (odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET) {
+ pkt_tbl[num_pkts++] = odp_packet_from_event(evt_tbl[i]);
+ } else if (vector_mode && odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET_VECTOR &&
+ num_pkts < num) {
+ odp_packet_vector_t pktv;
+ odp_packet_t *pkts;
+ int pktv_len;
+
+ pktv = odp_packet_vector_from_event(evt_tbl[i]);
+ pktv_len = odp_packet_vector_tbl(pktv, &pkts);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) == 0);
+
+ /* Make sure too many packets are not received */
+ if (num_pkts + pktv_len > num) {
+ int new_pkts = num - num_pkts;
+
+ memcpy(&pkt_tbl[num_pkts], pkts, new_pkts * sizeof(odp_packet_t));
+ odp_packet_free_multi(&pkts[new_pkts], pktv_len - new_pkts);
+ num_pkts += new_pkts;
+
+ } else {
+ memcpy(&pkt_tbl[num_pkts], pkts, pktv_len * sizeof(odp_packet_t));
+ num_pkts += pktv_len;
+ }
+ odp_packet_vector_free(pktv);
+ } else {
+ odp_event_free(evt_tbl[i]);
+ }
+ }
+
+ return num_pkts;
+}
+
+static int wait_for_packets_hdr(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, txrx_mode_e mode,
+ uint64_t ns, size_t l4_hdr_len, odp_bool_t vector_mode)
+{
+ odp_time_t wait_time, end, start;
+ int num_rx = 0;
+ int i;
+ odp_packet_t pkt_tmp[num];
+
+ wait_time = odp_time_local_from_ns(ns);
+ start = odp_time_local();
+ end = odp_time_sum(start, wait_time);
+
+ while (num_rx < num && odp_time_cmp(end, odp_time_local()) > 0) {
+ int n = get_packets(pktio_rx, pkt_tmp, num - num_rx, mode, vector_mode);
+
+ if (n < 0)
+ break;
+
+ if (n == 0)
+ continue;
+
+ for (i = 0; i < n; ++i) {
+ if (pktio_pkt_seq_hdr(pkt_tmp[i], l4_hdr_len) ==
+ seq_tbl[num_rx])
+ pkt_tbl[num_rx++] = pkt_tmp[i];
+ else
+ odp_packet_free(pkt_tmp[i]);
+ }
+ }
+
+ return num_rx;
+}
+
+static int wait_for_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, txrx_mode_e mode,
+ uint64_t ns, odp_bool_t vector_mode)
+{
+ return wait_for_packets_hdr(pktio_rx, pkt_tbl, seq_tbl, num, mode, ns,
+ ODPH_UDPHDR_LEN, vector_mode);
+}
+
+static int recv_packets_tmo(odp_pktio_t pktio, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, recv_tmo_mode_e mode,
+ uint64_t tmo, uint64_t ns, int no_pkt)
+{
+ odp_packet_t pkt_tmp[num];
+ odp_pktin_queue_t pktin[MAX_QUEUES];
+ odp_time_t ts1, ts2;
+ int num_rx = 0;
+ int num_q;
+ int i;
+ int n;
+ uint32_t from_val = 0;
+ uint32_t *from = NULL;
+
+ if (mode == RECV_MQ_TMO)
+ from = &from_val;
+
+ num_q = odp_pktin_queue(pktio, pktin, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_q > 0);
+
+ /** Multiple odp_pktin_recv_tmo()/odp_pktin_recv_mq_tmo() calls may be
+ * required to discard possible non-test packets. */
+ do {
+ ts1 = odp_time_global();
+ if (mode == RECV_TMO)
+ n = odp_pktin_recv_tmo(pktin[0], pkt_tmp, num - num_rx,
+ tmo);
+ else
+ n = odp_pktin_recv_mq_tmo(pktin, (uint32_t)num_q, from, pkt_tmp,
+ num - num_rx, tmo);
+ ts2 = odp_time_global();
+
+ CU_ASSERT(n >= 0);
+
+ if (n <= 0)
+ break;
+
+ /* When we don't expect any packets, drop all packets and
+ * retry timeout test. */
+ if (no_pkt) {
+ printf(" drop %i dummy packets\n", n);
+ odp_packet_free_multi(pkt_tmp, n);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx])
+ pkt_tbl[num_rx++] = pkt_tmp[i];
+ else
+ odp_packet_free(pkt_tmp[i]);
+ }
+ if (mode == RECV_MQ_TMO)
+ CU_ASSERT(from_val < (uint32_t)num_q);
+ } while (num_rx < num);
+
+ if (num_rx < num) {
+ uint64_t diff = odp_time_diff_ns(ts2, ts1);
+
+ if (diff < ns)
+ printf(" diff %" PRIu64 ", ns %" PRIu64 "\n",
+ diff, ns);
+
+ CU_ASSERT(diff >= ns);
+ }
+
+ return num_rx;
+}
+
+static int send_packets(odp_pktout_queue_t pktout,
+ odp_packet_t *pkt_tbl, unsigned pkts)
+{
+ int ret;
+ unsigned sent = 0;
+
+ while (sent < pkts) {
+ ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet");
+ return -1;
+ }
+
+ sent += ret;
+ }
+
+ return 0;
+}
+
+static int send_packet_events(odp_queue_t queue,
+ odp_packet_t *pkt_tbl, unsigned pkts)
+{
+ int ret;
+ unsigned i;
+ unsigned sent = 0;
+ odp_event_t ev_tbl[pkts];
+
+ for (i = 0; i < pkts; i++)
+ ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
+
+ while (sent < pkts) {
+ ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet as events");
+ return -1;
+ }
+
+ sent += ret;
+ }
+
+ return 0;
+}
+
+static void check_parser_capa(odp_pktio_t pktio, int *l2, int *l3, int *l4)
+{
+ int ret;
+ odp_pktio_capability_t capa;
+
+ *l2 = 0;
+ *l3 = 0;
+ *l4 = 0;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ CU_ASSERT(ret == 0);
+
+ if (ret < 0)
+ return;
+
+ switch (capa.config.parser.layer) {
+ case ODP_PROTO_LAYER_ALL:
+ /* Fall through */
+ case ODP_PROTO_LAYER_L4:
+ *l2 = 1;
+ *l3 = 1;
+ *l4 = 1;
+ break;
+ case ODP_PROTO_LAYER_L3:
+ *l2 = 1;
+ *l3 = 1;
+ break;
+ case ODP_PROTO_LAYER_L2:
+ *l2 = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void pktio_txrx_multi(pktio_info_t *pktio_info_a,
+ pktio_info_t *pktio_info_b,
+ int num_pkts, txrx_mode_e mode,
+ odp_bool_t vector_mode)
+{
+ odp_packet_t tx_pkt[num_pkts];
+ odp_packet_t rx_pkt[num_pkts];
+ uint32_t tx_seq[num_pkts];
+ int i, ret, num_rx;
+ int parser_l2, parser_l3, parser_l4;
+ odp_pktio_t pktio_a = pktio_info_a->id;
+ odp_pktio_t pktio_b = pktio_info_b->id;
+ int pktio_index_b = odp_pktio_index(pktio_b);
+
+ /* Check RX interface parser capability */
+ check_parser_capa(pktio_b, &parser_l2, &parser_l3, &parser_l4);
+
+ if (packet_len == USE_MTU) {
+ odp_pool_capability_t pool_capa;
+ uint32_t maxlen;
+
+ maxlen = odp_pktout_maxlen(pktio_a);
+ if (odp_pktout_maxlen(pktio_b) < maxlen)
+ maxlen = odp_pktout_maxlen(pktio_b);
+ CU_ASSERT_FATAL(maxlen > 0);
+ packet_len = maxlen;
+ if (packet_len > PKT_LEN_MAX)
+ packet_len = PKT_LEN_MAX;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+
+ if (pool_capa.pkt.max_len &&
+ packet_len > pool_capa.pkt.max_len)
+ packet_len = pool_capa.pkt.max_len;
+ }
+
+ /* generate test packets to send */
+ ret = create_packets(tx_pkt, tx_seq, num_pkts, pktio_a, pktio_b);
+ if (ret != num_pkts) {
+ CU_FAIL("failed to generate test packets");
+ return;
+ }
+
+ /* send packet(s) out */
+ if (mode == TXRX_MODE_SINGLE) {
+ for (i = 0; i < num_pkts; ++i) {
+ ret = odp_pktout_send(pktio_info_a->pktout,
+ &tx_pkt[i], 1);
+ if (ret != 1) {
+ CU_FAIL_FATAL("failed to send test packet");
+ odp_packet_free(tx_pkt[i]);
+ return;
+ }
+ }
+ } else if (mode == TXRX_MODE_MULTI) {
+ send_packets(pktio_info_a->pktout, tx_pkt, num_pkts);
+ } else {
+ send_packet_events(pktio_info_a->queue_out, tx_pkt, num_pkts);
+ }
+
+ /* and wait for them to arrive back */
+ num_rx = wait_for_packets(pktio_info_b, rx_pkt, tx_seq, num_pkts, mode,
+ ODP_TIME_SEC_IN_NS, vector_mode);
+ CU_ASSERT(num_rx == num_pkts);
+ if (num_rx != num_pkts)
+ ODPH_ERR("received %i, out of %i packets\n", num_rx, num_pkts);
+
+ for (i = 0; i < num_rx; ++i) {
+ odp_packet_data_range_t range;
+ uint16_t sum;
+ odp_packet_t pkt = rx_pkt[i];
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b);
+ CU_ASSERT(odp_packet_input_index(pkt) == pktio_index_b);
+ CU_ASSERT(odp_packet_has_error(pkt) == 0);
+ if (parser_l2) {
+ CU_ASSERT(odp_packet_has_l2(pkt));
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ }
+ if (parser_l3) {
+ CU_ASSERT(odp_packet_has_l3(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ }
+ if (parser_l4) {
+ CU_ASSERT(odp_packet_has_l4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+ }
+
+ CU_ASSERT(odp_packet_user_flag(pkt) == 0);
+ CU_ASSERT(odp_packet_user_ptr(pkt) == NULL);
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+
+ odp_packet_input_set(pkt, ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_input_index(pkt) < 0);
+
+ odp_packet_input_set(pkt, pktio_b);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b);
+ CU_ASSERT(odp_packet_input_index(pkt) == pktio_index_b);
+
+ /* Dummy read to ones complement in case pktio has set it */
+ sum = odp_packet_ones_comp(pkt, &range);
+ if (range.length > 0)
+ test_pktio_dummy_u64 += sum;
+
+ /* Dummy read to flow hash in case pktio has set it */
+ if (odp_packet_has_flow_hash(pkt))
+ test_pktio_dummy_u64 += odp_packet_flow_hash(pkt);
+
+ odp_packet_free(pkt);
+ }
+}
+
+static void test_txrx(odp_pktin_mode_t in_mode, int num_pkts,
+ txrx_mode_e mode, odp_schedule_sync_t sync_mode,
+ odp_bool_t vector_mode)
+{
+ int ret, i, if_b;
+ pktio_info_t pktios[MAX_NUM_IFACES];
+ pktio_info_t *io;
+
+ /* create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_pktout_mode_t out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ if (mode == TXRX_MODE_MULTI_EVENT)
+ out_mode = ODP_PKTOUT_MODE_QUEUE;
+
+ io = &pktios[i];
+
+ io->name = iface_name[i];
+ if (vector_mode)
+ io->id = create_pktv_pktio(i, in_mode, out_mode, sync_mode);
+ else
+ io->id = create_pktio(i, in_mode, out_mode);
+ if (io->id == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open iface");
+ return;
+ }
+
+ if (mode == TXRX_MODE_MULTI_EVENT) {
+ CU_ASSERT_FATAL(odp_pktout_event_queue(io->id,
+ &queue, 1) == 1);
+ } else {
+ CU_ASSERT_FATAL(odp_pktout_queue(io->id,
+ &pktout, 1) == 1);
+ io->pktout = pktout;
+ }
+
+ io->queue_out = queue;
+ io->in_mode = in_mode;
+
+ if (in_mode == ODP_PKTIN_MODE_QUEUE) {
+ CU_ASSERT_FATAL(odp_pktin_event_queue(io->id, &queue, 1)
+ == 1);
+ io->inq = queue;
+ } else {
+ io->inq = ODP_QUEUE_INVALID;
+ }
+
+ ret = odp_pktio_start(io->id);
+ CU_ASSERT(ret == 0);
+
+ _pktio_wait_linkup(io->id);
+ }
+
+ /* if we have two interfaces then send through one and receive on
+ * another but if there's only one assume it's a loopback */
+ if_b = (num_ifaces == 1) ? 0 : 1;
+ pktio_txrx_multi(&pktios[0], &pktios[if_b], num_pkts, mode, vector_mode);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ ret = odp_pktio_stop(pktios[i].id);
+ CU_ASSERT_FATAL(ret == 0);
+ flush_input_queue(pktios[i].id, in_mode);
+ ret = odp_pktio_close(pktios[i].id);
+ CU_ASSERT(ret == 0);
+ }
+}
+
+static void pktio_test_plain_queue(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_SINGLE, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_plain_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_plain_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_sched_queue(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_SINGLE, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_sched_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_sched_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_recv(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_recv_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_recv_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_recv_queue(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_param_t out_queue_param;
+ odp_pktout_queue_t pktout_queue[MAX_QUEUES];
+ odp_pktin_queue_t pktin_queue[MAX_QUEUES];
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_packet_t tmp_pkt[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_time_t wait_time, end;
+ int num_rx = 0;
+ int num_queues;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ num_queues = capa.max_input_queues;
+ in_queue_param.num_queues = num_queues;
+ in_queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ in_queue_param.hash_proto.proto.ipv4_udp = 1;
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_pktout_queue_param_init(&out_queue_param);
+ out_queue_param.num_queues = capa.max_output_queues;
+
+ ret = odp_pktout_queue_config(pktio[i], &out_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; ++i)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ if (num_ifaces > 1)
+ pktio_rx = pktio[1];
+ else
+ pktio_rx = pktio_tx;
+
+ /* Allocate and initialize test packets */
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ if (ret != TX_BATCH_LEN) {
+ CU_FAIL("Failed to generate test packets");
+ return;
+ }
+
+ /* Send packets */
+ num_queues = odp_pktout_queue(pktio_tx, pktout_queue, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_queues > 0);
+ if (num_queues > MAX_QUEUES)
+ num_queues = MAX_QUEUES;
+
+ ret = odp_pktout_send(pktout_queue[num_queues - 1], pkt_tbl,
+ TX_BATCH_LEN);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ /* Receive packets */
+ num_queues = odp_pktin_queue(pktio_rx, pktin_queue, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_queues > 0);
+ if (num_queues > MAX_QUEUES)
+ num_queues = MAX_QUEUES;
+
+ wait_time = odp_time_local_from_ns(ODP_TIME_SEC_IN_NS);
+ end = odp_time_sum(odp_time_local(), wait_time);
+ do {
+ int n = 0;
+
+ for (i = 0; i < num_queues; i++) {
+ n = odp_pktin_recv(pktin_queue[i], tmp_pkt,
+ TX_BATCH_LEN);
+ if (n != 0)
+ break;
+ }
+ if (n < 0)
+ break;
+ for (i = 0; i < n; i++) {
+ if (pktio_pkt_seq(tmp_pkt[i]) == pkt_seq[num_rx])
+ pkt_tbl[num_rx++] = tmp_pkt[i];
+ else
+ odp_packet_free(tmp_pkt[i]);
+ }
+ } while (num_rx < TX_BATCH_LEN &&
+ odp_time_cmp(end, odp_time_local()) > 0);
+
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void test_recv_tmo(recv_tmo_mode_e mode)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_t pktout_queue;
+ int test_pkt_count = 6;
+ odp_packet_t pkt_tbl[test_pkt_count];
+ uint32_t pkt_seq[test_pkt_count];
+ uint64_t ns;
+ uint32_t num_q;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ if (mode == RECV_TMO)
+ num_q = 1;
+ else
+ num_q = (capa.max_input_queues < MAX_QUEUES) ?
+ capa.max_input_queues : MAX_QUEUES;
+ in_queue_param.num_queues = num_q;
+ in_queue_param.hash_enable = (num_q > 1) ? 1 : 0;
+ in_queue_param.hash_proto.proto.ipv4_udp = 1;
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(pkt_seq, 0, sizeof(pkt_seq));
+
+ ns = 100 * ODP_TIME_MSEC_IN_NS;
+
+ ret = create_packets(pkt_tbl, pkt_seq, test_pkt_count, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == test_pkt_count);
+
+ ret = odp_pktout_send(pktout_queue, pkt_tbl, test_pkt_count);
+ CU_ASSERT_FATAL(ret == test_pkt_count);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode,
+ odp_pktin_wait_time(10 * ODP_TIME_SEC_IN_NS),
+ 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[1], &pkt_seq[1], 1, mode,
+ ODP_PKTIN_NO_WAIT, 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[2], &pkt_seq[2], 1, mode,
+ odp_pktin_wait_time(0), 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[3], &pkt_seq[3], 3, mode,
+ odp_pktin_wait_time(ns), ns, 0);
+ CU_ASSERT_FATAL(ret == 3);
+
+ for (i = 0; i < test_pkt_count; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_recv_tmo(void)
+{
+ test_recv_tmo(RECV_TMO);
+}
+
+static void pktio_test_recv_mq_tmo(void)
+{
+ test_recv_tmo(RECV_MQ_TMO);
+ test_recv_tmo(RECV_MQ_TMO_NO_IDX);
+}
+
+static void pktio_test_recv_mtu(void)
+{
+ packet_len = USE_MTU;
+ pktio_test_sched_multi();
+ packet_len = PKT_LEN_NORMAL;
+}
+
+static void pktio_test_maxlen(void)
+{
+ int ret;
+ uint32_t maxlen;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ maxlen = odp_pktout_maxlen(pktio);
+ CU_ASSERT(maxlen > 0);
+
+ maxlen = odp_pktin_maxlen(pktio);
+ CU_ASSERT(maxlen > 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static int pktio_check_maxlen_set(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.set_op.op.maxlen)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_maxlen_set(void)
+{
+ odp_pktio_capability_t capa;
+ int ret;
+ uint32_t maxlen, input_orig, output_orig;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(!odp_pktio_capability(pktio, &capa));
+
+ input_orig = odp_pktin_maxlen(pktio);
+ CU_ASSERT(input_orig > 0);
+
+ output_orig = odp_pktout_maxlen(pktio);
+ CU_ASSERT(output_orig > 0);
+
+ if (capa.maxlen.equal) { /* Input and output values have to be equal */
+ CU_ASSERT(capa.maxlen.min_input == capa.maxlen.min_output);
+ CU_ASSERT(capa.maxlen.max_input == capa.maxlen.max_output);
+ CU_ASSERT(capa.maxlen.max_input > capa.maxlen.min_input);
+
+ maxlen = capa.maxlen.min_input;
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, maxlen, maxlen));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == maxlen);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == maxlen);
+
+ maxlen = capa.maxlen.max_input;
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, maxlen, maxlen));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == maxlen);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == maxlen);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, input_orig));
+ } else {
+ CU_ASSERT(capa.maxlen.max_input || capa.maxlen.max_output);
+ if (capa.maxlen.max_output == 0) { /* Only input supported */
+ CU_ASSERT(capa.maxlen.min_output == 0);
+ CU_ASSERT(capa.maxlen.min_input < capa.maxlen.max_input);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input, 0));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input, 0));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, 0));
+ } else if (capa.maxlen.max_input == 0) { /* Only output supported */
+ CU_ASSERT(capa.maxlen.min_input == 0);
+ CU_ASSERT(capa.maxlen.min_output < capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, capa.maxlen.min_output));
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, capa.maxlen.max_output));
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, output_orig));
+ } else { /* Both directions supported */
+ CU_ASSERT(capa.maxlen.min_input < capa.maxlen.max_input);
+ CU_ASSERT(capa.maxlen.min_output < capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input,
+ capa.maxlen.min_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input,
+ capa.maxlen.max_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input,
+ capa.maxlen.min_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input,
+ capa.maxlen.max_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, output_orig));
+ }
+ }
+ CU_ASSERT(odp_pktin_maxlen(pktio) == input_orig);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == output_orig);
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static void pktio_test_promisc(void)
+{
+ int ret;
+ odp_pktio_capability_t capa;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT(ret == 0 || ret == 1);
+
+ if (capa.set_op.op.promisc_mode) {
+ /* Disabled by default */
+ CU_ASSERT(ret == 0);
+ }
+
+ if (!capa.set_op.op.promisc_mode) {
+ printf("promiscuous mode not supported\n");
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+ return;
+ }
+
+ ret = odp_pktio_promisc_mode_set(pktio, 1);
+ CU_ASSERT(0 == ret);
+
+ /* Verify that promisc mode set */
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(1 == ret);
+
+ ret = odp_pktio_promisc_mode_set(pktio, 0);
+ CU_ASSERT(0 == ret);
+
+ /* Verify that promisc mode is not set */
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(0 == ret);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static void pktio_test_mac(void)
+{
+ unsigned char mac_addr[ODP_PKTIO_MACADDR_MAXSIZE];
+ unsigned char mac_addr_ref[ODP_PKTIO_MACADDR_MAXSIZE] = {
+ 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};
+ int mac_len;
+ int ret;
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ printf("testing mac for %s\n", iface_name[0]);
+
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr,
+ ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ODPH_ETHADDR_LEN == mac_len);
+ CU_ASSERT(ODP_PKTIO_MACADDR_MAXSIZE >= mac_len);
+
+ printf(" %X:%X:%X:%X:%X:%X ",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ /* Fail case: wrong addr_size. Expected <0. */
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr, 2);
+ CU_ASSERT(mac_len < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+ if (capa.set_op.op.mac_addr) {
+ /* Fail case: wrong addr_size. Expected <0. */
+ ret = odp_pktio_mac_addr_set(pktio, mac_addr_ref, 2);
+ CU_ASSERT_FATAL(ret < 0);
+
+ ret = odp_pktio_mac_addr_set(pktio, mac_addr_ref,
+ ODPH_ETHADDR_LEN);
+ CU_ASSERT_FATAL(ret == 0);
+
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr,
+ ODPH_ETHADDR_LEN);
+ CU_ASSERT(ODPH_ETHADDR_LEN == mac_len);
+
+ CU_ASSERT(odp_memcmp(mac_addr_ref, mac_addr,
+ ODPH_ETHADDR_LEN) == 0);
+ } else
+ printf("\n mac address set not supported for %s!\n",
+ iface_name[0]);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(0 == ret);
+}
+
+static void test_defaults(uint8_t fill)
+{
+ odp_pktio_param_t pktio_p;
+ odp_pktin_queue_param_t qp_in;
+ odp_pktout_queue_param_t qp_out;
+ odp_pktio_config_t pktio_conf;
+
+ memset(&pktio_p, fill, sizeof(pktio_p));
+ odp_pktio_param_init(&pktio_p);
+ CU_ASSERT(pktio_p.in_mode == ODP_PKTIN_MODE_DIRECT);
+ CU_ASSERT(pktio_p.out_mode == ODP_PKTOUT_MODE_DIRECT);
+
+ memset(&qp_in, fill, sizeof(qp_in));
+ odp_pktin_queue_param_init(&qp_in);
+ CU_ASSERT(qp_in.op_mode == ODP_PKTIO_OP_MT);
+ CU_ASSERT(qp_in.classifier_enable == 0);
+ CU_ASSERT(qp_in.hash_enable == 0);
+ CU_ASSERT(qp_in.hash_proto.all_bits == 0);
+ CU_ASSERT(qp_in.num_queues == 1);
+ CU_ASSERT(qp_in.queue_size[0] == 0);
+ CU_ASSERT(qp_in.queue_param.enq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(qp_in.queue_param.sched.prio == odp_schedule_default_prio());
+ CU_ASSERT(qp_in.queue_param.sched.sync == ODP_SCHED_SYNC_PARALLEL);
+ CU_ASSERT(qp_in.queue_param.sched.group == ODP_SCHED_GROUP_ALL);
+ CU_ASSERT(qp_in.queue_param.sched.lock_count == 0);
+ CU_ASSERT(qp_in.queue_param.order == ODP_QUEUE_ORDER_KEEP);
+ CU_ASSERT(qp_in.queue_param.nonblocking == ODP_BLOCKING);
+ CU_ASSERT(qp_in.queue_param.context == NULL);
+ CU_ASSERT(qp_in.queue_param.context_len == 0);
+ CU_ASSERT(qp_in.queue_param_ovr == NULL);
+ CU_ASSERT(qp_in.vector.enable == false);
+
+ memset(&qp_out, fill, sizeof(qp_out));
+ odp_pktout_queue_param_init(&qp_out);
+ CU_ASSERT(qp_out.op_mode == ODP_PKTIO_OP_MT);
+ CU_ASSERT(qp_out.num_queues == 1);
+ CU_ASSERT(qp_out.queue_size[0] == 0);
+
+ memset(&pktio_conf, fill, sizeof(pktio_conf));
+ odp_pktio_config_init(&pktio_conf);
+ CU_ASSERT(pktio_conf.pktin.all_bits == 0);
+ CU_ASSERT(pktio_conf.pktout.all_bits == 0);
+ CU_ASSERT(pktio_conf.parser.layer == ODP_PROTO_LAYER_ALL);
+ CU_ASSERT(pktio_conf.enable_loop == false);
+ CU_ASSERT(pktio_conf.inbound_ipsec == false);
+ CU_ASSERT(pktio_conf.outbound_ipsec == false);
+ CU_ASSERT(pktio_conf.enable_lso == false);
+ CU_ASSERT(pktio_conf.reassembly.en_ipv4 == false);
+ CU_ASSERT(pktio_conf.reassembly.en_ipv6 == false);
+ CU_ASSERT(pktio_conf.reassembly.max_wait_time == 0);
+ CU_ASSERT(pktio_conf.reassembly.max_num_frags == 2);
+}
+
+static void pktio_test_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void pktio_test_open(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ int i;
+
+ /* test the sequence open->close->open->close() */
+ for (i = 0; i < 2; ++i) {
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open("nothere", default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio == ODP_PKTIO_INVALID);
+}
+
+static void pktio_test_lookup(void)
+{
+ odp_pktio_t pktio, pktio_inval;
+ odp_pktio_param_t pktio_param;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_lookup(iface_name[0]) == pktio);
+
+ pktio_inval = odp_pktio_open(iface_name[0], default_pkt_pool,
+ &pktio_param);
+ CU_ASSERT(pktio_inval == ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+
+ CU_ASSERT(odp_pktio_lookup(iface_name[0]) == ODP_PKTIO_INVALID);
+}
+
+static void pktio_test_index(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ int ndx;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio != ODP_PKTIO_INVALID);
+
+ ndx = odp_pktio_index(pktio);
+ CU_ASSERT(ndx >= 0);
+
+ CU_ASSERT(ODP_PKTIO_MAX_INDEX >= odp_pktio_max_index());
+ CU_ASSERT(ODP_PKTIO_MAX_INDEX >= 0 && ODP_PKTIO_MAX_INDEX <= 1024);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_print(void)
+{
+ odp_pktio_t pktio;
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ /* Print pktio debug info and test that the
+ * odp_pktio_print() function is implemented. */
+ odp_pktio_print(pktio);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_pktio_config(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ const char *iface = iface_name[0];
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ memset(&config, 0xff, sizeof(config));
+ odp_pktio_config_init(&config);
+
+ /* Check default values */
+ CU_ASSERT(config.pktin.all_bits == 0);
+ CU_ASSERT(config.pktout.all_bits == 0);
+ CU_ASSERT(config.parser.layer == ODP_PROTO_LAYER_ALL);
+ CU_ASSERT(!config.enable_loop);
+ CU_ASSERT(!config.inbound_ipsec);
+ CU_ASSERT(!config.outbound_ipsec);
+ CU_ASSERT(!config.enable_lso);
+ CU_ASSERT(!config.reassembly.en_ipv4);
+ CU_ASSERT(!config.reassembly.en_ipv6);
+ CU_ASSERT(config.reassembly.max_wait_time == 0);
+ CU_ASSERT(config.reassembly.max_num_frags == 2);
+ CU_ASSERT(config.flow_control.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF);
+ CU_ASSERT(config.flow_control.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF);
+
+ /* Indicate packet refs might be used */
+ config.pktout.bit.no_packet_refs = 0;
+
+ CU_ASSERT(odp_pktio_config(pktio, NULL) == 0);
+
+ CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ /* Loop interface supports loopback mode by definition */
+ if (!strcmp(iface, "loop"))
+ CU_ASSERT(capa.config.enable_loop);
+
+ config = capa.config;
+
+ /* Disable inbound_ipsec as it requires IPsec config to be done */
+ config.inbound_ipsec = 0;
+
+ CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_info(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_info_t pktio_info;
+ int i;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_info(pktio, &pktio_info) == 0);
+
+ printf("pktio %d\n name %s\n driver %s\n", i,
+ pktio_info.name, pktio_info.drv_name);
+
+ CU_ASSERT(strcmp(pktio_info.name, iface_name[i]) == 0);
+ CU_ASSERT(pktio_info.pool == pool[i]);
+ CU_ASSERT(pktio_info.param.in_mode == ODP_PKTIN_MODE_QUEUE);
+ CU_ASSERT(pktio_info.param.out_mode == ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT(odp_pktio_info(ODP_PKTIO_INVALID, &pktio_info) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_link_info(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_link_info_t link_info;
+ int i;
+
+ for (i = 0; i < num_ifaces; i++) {
+ memset(&link_info, 0, sizeof(link_info));
+
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_link_info(pktio, &link_info) == 0);
+
+ CU_ASSERT(link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_UNKNOWN ||
+ link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_ON ||
+ link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_OFF);
+ CU_ASSERT(link_info.duplex == ODP_PKTIO_LINK_DUPLEX_UNKNOWN ||
+ link_info.duplex == ODP_PKTIO_LINK_DUPLEX_HALF ||
+ link_info.duplex == ODP_PKTIO_LINK_DUPLEX_FULL);
+ CU_ASSERT(link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_UNKNOWN ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_ON ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PFC_ON);
+ CU_ASSERT(link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_UNKNOWN ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_ON ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PFC_ON);
+ CU_ASSERT(link_info.status == ODP_PKTIO_LINK_STATUS_UNKNOWN ||
+ link_info.status == ODP_PKTIO_LINK_STATUS_UP ||
+ link_info.status == ODP_PKTIO_LINK_STATUS_DOWN);
+ CU_ASSERT(link_info.media != NULL);
+
+ CU_ASSERT(odp_pktio_link_info(ODP_PKTIO_INVALID, &link_info) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static int pktio_check_flow_control(int pfc, int rx)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pfc == 0 && rx == 1 && capa.flow_control.pause_rx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 1 && rx == 1 && capa.flow_control.pfc_rx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 0 && rx == 0 && capa.flow_control.pause_tx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 1 && rx == 0 && capa.flow_control.pfc_tx == 1)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int pktio_check_pause_rx(void)
+{
+ return pktio_check_flow_control(0, 1);
+}
+
+static int pktio_check_pause_tx(void)
+{
+ return pktio_check_flow_control(0, 0);
+}
+
+static int pktio_check_pause_both(void)
+{
+ int rx = pktio_check_pause_rx();
+ int tx = pktio_check_pause_tx();
+
+ if (rx == ODP_TEST_ACTIVE && tx == ODP_TEST_ACTIVE)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int pktio_check_pfc_rx(void)
+{
+ return pktio_check_flow_control(1, 1);
+}
+
+static int pktio_check_pfc_tx(void)
+{
+ return pktio_check_flow_control(1, 0);
+}
+
+static int pktio_check_pfc_both(void)
+{
+ int rx = pktio_check_pfc_rx();
+ int tx = pktio_check_pfc_tx();
+
+ if (rx == ODP_TEST_ACTIVE && tx == ODP_TEST_ACTIVE)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static odp_cos_t set_default_cos(odp_pktio_t pktio, odp_queue_t queue)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_cos_t cos;
+ int ret;
+
+ odp_cls_cos_param_init(&cos_param);
+ cos_param.queue = queue;
+ cos_param.pool = pool[0];
+
+ cos = odp_cls_cos_create("Default CoS", &cos_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ ret = odp_pktio_default_cos_set(pktio, cos);
+ CU_ASSERT_FATAL(ret == 0);
+
+ return cos;
+}
+
+static odp_cos_t create_pfc_cos(odp_cos_t default_cos, odp_queue_t queue, odp_pmr_t *pmr_out)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_cos_t cos;
+ odp_pmr_param_t pmr_param;
+ odp_pmr_t pmr;
+ uint8_t pcp = 1;
+ uint8_t mask = 0x7;
+
+ /* Setup a CoS to control generation of PFC frame generation. PFC for the VLAN
+ * priority level is generated when queue/pool resource usage gets above 80%. */
+ odp_cls_cos_param_init(&cos_param);
+ cos_param.queue = queue;
+ cos_param.pool = pool[0];
+ cos_param.bp.enable = 1;
+ cos_param.bp.threshold.type = ODP_THRESHOLD_PERCENT;
+ cos_param.bp.threshold.percent.max = 80;
+ cos_param.bp.pfc_level = pcp;
+
+ cos = odp_cls_cos_create("PFC CoS", &cos_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_VLAN_PCP_0;
+ pmr_param.match.value = &pcp;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = 1;
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT_FATAL(pmr != ODP_PMR_INVALID);
+
+ *pmr_out = pmr;
+
+ return cos;
+}
+
+static void pktio_config_flow_control(int pfc, int rx, int tx)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ int ret;
+ odp_cos_t default_cos = ODP_COS_INVALID;
+ odp_cos_t cos = ODP_COS_INVALID;
+ odp_pmr_t pmr = ODP_PMR_INVALID;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_pktio_link_pause_t mode = ODP_PKTIO_LINK_PAUSE_ON;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ odp_pktio_config_init(&config);
+
+ if (pfc)
+ mode = ODP_PKTIO_LINK_PFC_ON;
+
+ if (rx)
+ config.flow_control.pause_rx = mode;
+
+ if (tx)
+ config.flow_control.pause_tx = mode;
+
+ ret = odp_pktio_config(pktio, &config);
+ CU_ASSERT_FATAL(ret == 0);
+
+ if (pfc && tx) {
+ /* Enable classifier for PFC backpressure configuration. Overrides previous
+ * pktin queue config. */
+ odp_pktin_queue_param_t pktin_param;
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ pktin_param.classifier_enable = 1;
+
+ ret = odp_pktin_queue_config(pktio, &pktin_param);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT(ret == 0);
+
+ if (pfc && tx) {
+ odp_queue_param_t qparam;
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue = odp_queue_create("CoS queue", &qparam);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ default_cos = set_default_cos(pktio, queue);
+
+ cos = create_pfc_cos(default_cos, queue, &pmr);
+ }
+
+ if (pmr != ODP_PMR_INVALID)
+ odp_cls_pmr_destroy(pmr);
+
+ if (cos != ODP_COS_INVALID)
+ odp_cos_destroy(cos);
+
+ if (default_cos != ODP_COS_INVALID) {
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ }
+
+ if (queue != ODP_QUEUE_INVALID)
+ odp_queue_destroy(queue);
+
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_enable_pause_rx(void)
+{
+ pktio_config_flow_control(0, 1, 0);
+}
+
+static void pktio_test_enable_pause_tx(void)
+{
+ pktio_config_flow_control(0, 0, 1);
+}
+
+static void pktio_test_enable_pause_both(void)
+{
+ pktio_config_flow_control(0, 1, 1);
+}
+
+static void pktio_test_enable_pfc_rx(void)
+{
+ pktio_config_flow_control(1, 1, 0);
+}
+
+static void pktio_test_enable_pfc_tx(void)
+{
+ pktio_config_flow_control(1, 0, 1);
+}
+
+static void pktio_test_enable_pfc_both(void)
+{
+ pktio_config_flow_control(1, 1, 1);
+}
+
+static void pktio_test_pktin_queue_config_direct(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ odp_queue_t in_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_capability(ODP_PKTIO_INVALID, &capa) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES)
+ == num_queues);
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) < 0);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ queue_param.num_queues = 1;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_sched(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ odp_queue_t in_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
+ == num_queues);
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0);
+
+ queue_param.num_queues = 1;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_multi_sched(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_queue_t in_queues[MAX_QUEUES];
+ odp_pktin_queue_param_ovr_t queue_param_ovr[MAX_QUEUES];
+ int num_queues, i;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = (capa.max_input_queues < MAX_QUEUES) ?
+ capa.max_input_queues : MAX_QUEUES;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = 0;
+ queue_param.num_queues = num_queues;
+ queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ for (i = 0; i < num_queues; i++)
+ queue_param_ovr[i].group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param_ovr = queue_param_ovr;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
+ == num_queues);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_queue(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+ CU_ASSERT_FATAL(num_queues <= ODP_PKTIN_MAX_QUEUES);
+
+ CU_ASSERT(capa.min_input_queue_size <= capa.max_input_queue_size);
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ for (int i = 0; i < num_queues; i++)
+ queue_param.queue_size[i] = capa.max_input_queue_size;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) == num_queues);
+
+ queue_param.num_queues = 1;
+ queue_param.queue_size[0] = capa.min_input_queue_size;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktout_queue_config(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktout_queue_param_t queue_param;
+ odp_pktout_queue_t pktout_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_output_queues > 0);
+ num_queues = capa.max_output_queues;
+ CU_ASSERT_FATAL(num_queues <= ODP_PKTOUT_MAX_QUEUES);
+
+ CU_ASSERT(capa.min_output_queue_size <= capa.max_output_queue_size);
+
+ odp_pktout_queue_param_init(&queue_param);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ queue_param.num_queues = num_queues;
+ for (int i = 0; i < num_queues; i++)
+ queue_param.queue_size[i] = capa.max_output_queue_size;
+
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktout_queue(pktio, pktout_queues, MAX_QUEUES)
+ == num_queues);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT;
+ queue_param.num_queues = 1;
+ queue_param.queue_size[0] = capa.min_output_queue_size;
+
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktout_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
+
+ queue_param.num_queues = capa.max_output_queues + 1;
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+#ifdef DEBUG_STATS
+static void _print_pktio_stats(odp_pktio_stats_t *s, const char *name)
+{
+ ODPH_ERR("\n%s:\n"
+ " in_octets %" PRIu64 "\n"
+ " in_packets %" PRIu64 "\n"
+ " in_ucast_pkts %" PRIu64 "\n"
+ " in_mcast_pkts %" PRIu64 "\n"
+ " in_bcast_pkts %" PRIu64 "\n"
+ " in_discards %" PRIu64 "\n"
+ " in_errors %" PRIu64 "\n"
+ " out_octets %" PRIu64 "\n"
+ " out_packets %" PRIu64 "\n"
+ " out_ucast_pkts %" PRIu64 "\n"
+ " out_mcast_pkts %" PRIu64 "\n"
+ " out_bcast_pkts %" PRIu64 "\n"
+ " out_discards %" PRIu64 "\n"
+ " out_errors %" PRIu64 "\n",
+ name,
+ s->in_octets,
+ s->in_packets,
+ s->in_ucast_pkts,
+ s->in_mcast_pkts,
+ s->in_bcast_pkts,
+ s->in_discards,
+ s->in_errors,
+ s->out_octets,
+ s->out_packets,
+ s->out_ucast_pkts,
+ s->out_mcast_pkts,
+ s->out_bcast_pkts,
+ s->out_discards,
+ s->out_errors);
+}
+#endif
+
+static int pktio_check_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.stats.pktio.all_counters == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktio_stats_t stats[2];
+ odp_pktio_stats_t *rx_stats, *tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ ret = odp_pktio_start(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_start(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ ret = odp_pktio_stats_reset(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_stats_reset(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ ret = odp_pktio_stats(pktio_tx, &stats[0]);
+ CU_ASSERT(ret == 0);
+ tx_stats = &stats[0];
+
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_octets == 0) ||
+ (tx_stats->out_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_packets == 0) ||
+ (tx_stats->out_packets >= (uint64_t)pkts));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_ucast_pkts == 0) ||
+ (tx_stats->out_ucast_pkts >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats->out_discards == 0);
+ CU_ASSERT(tx_stats->out_errors == 0);
+
+ rx_stats = &stats[0];
+ if (num_ifaces > 1) {
+ rx_stats = &stats[1];
+ ret = odp_pktio_stats(pktio_rx, rx_stats);
+ CU_ASSERT(ret == 0);
+ }
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_octets == 0) ||
+ (rx_stats->in_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_packets == 0) ||
+ (rx_stats->in_packets >= (uint64_t)pkts));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_ucast_pkts == 0) ||
+ (rx_stats->in_ucast_pkts >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats->in_discards == 0);
+ CU_ASSERT(rx_stats->in_errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktio.counter.in_octets)
+ CU_ASSERT(rx_stats->in_octets == 0);
+ if (!rx_capa.stats.pktio.counter.in_packets)
+ CU_ASSERT(rx_stats->in_packets == 0);
+ if (!rx_capa.stats.pktio.counter.in_ucast_pkts)
+ CU_ASSERT(rx_stats->in_ucast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_mcast_pkts)
+ CU_ASSERT(rx_stats->in_mcast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_bcast_pkts)
+ CU_ASSERT(rx_stats->in_bcast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_discards)
+ CU_ASSERT(rx_stats->in_discards == 0);
+ if (!rx_capa.stats.pktio.counter.in_errors)
+ CU_ASSERT(rx_stats->in_errors == 0);
+
+ if (!tx_capa.stats.pktio.counter.out_octets)
+ CU_ASSERT(tx_stats->out_octets == 0);
+ if (!tx_capa.stats.pktio.counter.out_packets)
+ CU_ASSERT(tx_stats->out_packets == 0);
+ if (!tx_capa.stats.pktio.counter.out_ucast_pkts)
+ CU_ASSERT(tx_stats->out_ucast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_mcast_pkts)
+ CU_ASSERT(tx_stats->out_mcast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_bcast_pkts)
+ CU_ASSERT(tx_stats->out_bcast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_discards)
+ CU_ASSERT(tx_stats->out_discards == 0);
+ if (!tx_capa.stats.pktio.counter.out_errors)
+ CU_ASSERT(tx_stats->out_errors == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+#ifdef DEBUG_STATS
+ _print_pktio_stats(&stats[i], iface_name[i]);
+#endif
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_statistics_counters_bcast(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktio.counter.in_bcast_pkts == 0 &&
+ capa.stats.pktio.counter.out_bcast_pkts == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_statistics_counters_bcast(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktio_stats_t stats[2];
+ odp_pktio_stats_t *rx_stats, *tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ alloc = create_packets_udp(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx,
+ true, ETH_BROADCAST);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT(odp_pktio_stats(pktio_tx, &stats[0]) == 0);
+ tx_stats = &stats[0];
+
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_bcast_pkts == 0) ||
+ (tx_stats->out_bcast_pkts >= (uint64_t)pkts));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_octets == 0) ||
+ (tx_stats->out_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_packets == 0) ||
+ (tx_stats->out_packets >= (uint64_t)pkts));
+
+ rx_stats = &stats[0];
+ if (num_ifaces > 1) {
+ rx_stats = &stats[1];
+ CU_ASSERT(odp_pktio_stats(pktio_rx, rx_stats) == 0);
+ }
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_bcast_pkts == 0) ||
+ (rx_stats->in_bcast_pkts >= (uint64_t)pkts));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_octets == 0) ||
+ (rx_stats->in_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_packets == 0) ||
+ (rx_stats->in_packets >= (uint64_t)pkts));
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+#ifdef DEBUG_STATS
+ _print_pktio_stats(&stats[i], iface_name[i]);
+#endif
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktin_queue.all_counters == 0 &&
+ capa.stats.pktout_queue.all_counters == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+ uint64_t wait = odp_pktin_wait_time(ODP_TIME_SEC_IN_NS);
+ odp_pktin_queue_stats_t rx_stats;
+ odp_pktout_queue_stats_t tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_queue(pktio_rx, &pktin, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ odp_packet_t pkt;
+
+ if (odp_pktin_recv_tmo(pktin, &pkt, 1, wait) != 1)
+ break;
+
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+
+ odp_packet_free(pkt);
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT_FATAL(odp_pktout_queue_stats(pktout, &tx_stats) == 0);
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.octets) ||
+ (tx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.packets) ||
+ (tx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats.discards == 0);
+ CU_ASSERT(tx_stats.errors == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_queue_stats(pktin, &rx_stats) == 0);
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.octets) ||
+ (rx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.packets) ||
+ (rx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats.discards == 0);
+ CU_ASSERT(rx_stats.errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktin_queue.counter.octets)
+ CU_ASSERT(rx_stats.octets == 0);
+ if (!rx_capa.stats.pktin_queue.counter.packets)
+ CU_ASSERT(rx_stats.packets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.octets)
+ CU_ASSERT(tx_stats.octets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.packets)
+ CU_ASSERT(tx_stats.packets == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_event_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktin_queue.all_counters == 0 &&
+ capa.stats.pktout_queue.all_counters == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_event_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts;
+ odp_queue_t pktout;
+ odp_queue_t pktin;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktin_queue_stats_t rx_stats;
+ odp_pktout_queue_stats_t tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_QUEUE);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio_rx, &pktin, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_event_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ tx_pkts = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ CU_ASSERT_FATAL(send_packet_events(pktout, tx_pkt, tx_pkts) == 0);
+
+ /* Receive */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT_FATAL(odp_pktout_event_queue_stats(pktio_tx, pktout, &tx_stats) == 0);
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.octets) ||
+ (tx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.packets) ||
+ (tx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats.discards == 0);
+ CU_ASSERT(tx_stats.errors == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue_stats(pktio_rx, pktin, &rx_stats) == 0);
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.octets) ||
+ (rx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.packets) ||
+ (rx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats.discards == 0);
+ CU_ASSERT(rx_stats.errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktin_queue.counter.octets)
+ CU_ASSERT(rx_stats.octets == 0);
+ if (!rx_capa.stats.pktin_queue.counter.packets)
+ CU_ASSERT(rx_stats.packets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.octets)
+ CU_ASSERT(tx_stats.octets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.packets)
+ CU_ASSERT(tx_stats.packets == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_extra_stats(void)
+{
+ odp_pktio_t pktio;
+ int num_info, num_stats, i, ret;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio) == 0);
+
+ num_info = odp_pktio_extra_stat_info(pktio, NULL, 0);
+ CU_ASSERT_FATAL(num_info >= 0);
+
+ num_stats = odp_pktio_extra_stats(pktio, NULL, 0);
+ CU_ASSERT_FATAL(num_stats >= 0);
+
+ CU_ASSERT_FATAL(num_info == num_stats);
+
+ /* No extra statistics supported */
+ if (num_stats == 0) {
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ return;
+ }
+
+ odp_pktio_extra_stat_info_t stats_info[num_stats];
+ uint64_t extra_stats[num_stats];
+
+ ret = odp_pktio_extra_stat_info(pktio, stats_info, num_stats);
+ CU_ASSERT(ret == num_stats);
+ num_info = ret;
+
+ ret = odp_pktio_extra_stats(pktio, extra_stats, num_stats);
+ CU_ASSERT(ret == num_stats);
+ CU_ASSERT_FATAL(ret <= num_stats);
+ num_stats = ret;
+
+ CU_ASSERT_FATAL(num_info == num_stats);
+
+ printf("\nPktio extra statistics\n----------------------\n");
+ for (i = 0; i < num_stats; i++)
+ printf(" %s=%" PRIu64 "\n", stats_info[i].name, extra_stats[i]);
+
+ for (i = 0; i < num_stats; i++) {
+ uint64_t stat = 0;
+
+ CU_ASSERT(odp_pktio_extra_stat_counter(pktio, i, &stat) == 0);
+ }
+
+ odp_pktio_extra_stats_print(pktio);
+
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static int pktio_check_proto_statistics_counters(void)
+{
+ odp_proto_stats_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_proto_stats_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.tx.counters.all_bits == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void validate_proto_stats(odp_proto_stats_t stat, odp_packet_proto_stats_opt_t opt,
+ odp_proto_stats_capability_t capa, uint64_t pkts)
+{
+ odp_proto_stats_data_t data;
+ int ret;
+
+ ret = odp_proto_stats(stat, &data);
+ CU_ASSERT(ret == 0);
+
+ CU_ASSERT(!(capa.tx.counters.bit.tx_pkt_drops && (data.tx_pkt_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_oct_count0_drops && (data.tx_oct_count0_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_oct_count1_drops && (data.tx_oct_count1_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_pkts && (data.tx_pkts != pkts)));
+
+ if (capa.tx.counters.bit.tx_oct_count0) {
+ int64_t counted_bytes = PKT_LEN_NORMAL;
+
+ if (capa.tx.oct_count0_adj)
+ counted_bytes += opt.oct_count0_adj;
+ CU_ASSERT(data.tx_oct_count0 == counted_bytes * pkts);
+ }
+
+ if (capa.tx.counters.bit.tx_oct_count1) {
+ int64_t counted_bytes = PKT_LEN_NORMAL;
+
+ if (capa.tx.oct_count1_adj)
+ counted_bytes += opt.oct_count1_adj;
+ CU_ASSERT(data.tx_oct_count1 == counted_bytes * pkts);
+ }
+}
+
+static void pktio_test_proto_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ const uint32_t num_pkts = 10;
+ odp_packet_t tx_pkt[num_pkts];
+ uint32_t pkt_seq[num_pkts];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ uint64_t flow0_pkts = 0, flow1_pkts = 0;
+ odp_proto_stats_capability_t capa;
+ odp_packet_proto_stats_opt_t opt0;
+ odp_packet_proto_stats_opt_t opt1;
+ odp_proto_stats_param_t param;
+ odp_pktio_config_t config;
+ odp_proto_stats_t stat0;
+ odp_proto_stats_t stat1;
+
+ memset(&pktout, 0, sizeof(pktout));
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ /* Enable protocol stats on Tx interface */
+ odp_pktio_config_init(&config);
+ config.pktout.bit.proto_stats_ena = 1;
+ ret = odp_pktio_config(pktio_tx, &config);
+ CU_ASSERT(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ ret = odp_pktio_start(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_start(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ odp_proto_stats_param_init(&param);
+ odp_proto_stats_capability(pktio_tx, &capa);
+ CU_ASSERT(capa.tx.counters.all_bits != 0);
+ param.counters.all_bits = capa.tx.counters.all_bits;
+ /* Create statistics object with all supported counters */
+ stat0 = odp_proto_stats_create("flow0_stat", &param);
+ CU_ASSERT_FATAL(stat0 != ODP_PROTO_STATS_INVALID);
+ stat1 = odp_proto_stats_create("flow1_stat", &param);
+ CU_ASSERT_FATAL(stat1 != ODP_PROTO_STATS_INVALID);
+
+ /* Flow-0 options */
+ opt0.stat = stat0;
+ opt0.oct_count0_adj = 0;
+ /* oct1 contains byte count of packets excluding Ethernet header */
+ opt0.oct_count1_adj = -14;
+
+ /* Flow-1 options */
+ opt1.stat = stat1;
+ opt1.oct_count0_adj = -8;
+ opt1.oct_count1_adj = 14;
+
+ alloc = create_packets(tx_pkt, pkt_seq, num_pkts, pktio_tx, pktio_rx);
+
+ /* Attach statistics object to all Tx packets */
+ for (pkts = 0; pkts < alloc; pkts++) {
+ if ((pkts % 2) == 0) {
+ odp_packet_proto_stats_request(tx_pkt[pkts], &opt0);
+ flow0_pkts++;
+ } else {
+ odp_packet_proto_stats_request(tx_pkt[pkts], &opt1);
+ flow1_pkts++;
+ }
+ }
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < (int)num_pkts && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ /* Validate Flow-0 packet statistics */
+ validate_proto_stats(stat0, opt0, capa, flow0_pkts);
+
+ /* Validate Flow-1 packet statistics */
+ validate_proto_stats(stat1, opt1, capa, flow1_pkts);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ /* Destroy proto statistics object */
+ CU_ASSERT(odp_proto_stats_destroy(stat0) == 0);
+ CU_ASSERT(odp_proto_stats_destroy(stat1) == 0);
+}
+
+static int pktio_check_start_stop(void)
+{
+ if (getenv("ODP_PKTIO_TEST_DISABLE_START_STOP"))
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_start_stop(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES];
+ odp_pktio_t pktio_in;
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio[0], &pktout, 1) == 1);
+
+ /* Interfaces are stopped by default,
+ * Check that stop when stopped generates an error */
+ ret = odp_pktio_stop(pktio[0]);
+ CU_ASSERT(ret < 0);
+
+ /* start first */
+ ret = odp_pktio_start(pktio[0]);
+ CU_ASSERT(ret == 0);
+ /* Check that start when started generates an error */
+ ret = odp_pktio_start(pktio[0]);
+ CU_ASSERT(ret < 0);
+
+ _pktio_wait_linkup(pktio[0]);
+
+ /* Test Rx on a stopped interface. Only works if there are 2 */
+ if (num_ifaces > 1) {
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0],
+ pktio[1]);
+
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts],
+ alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to enqueue packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ /* check that packets did not arrive */
+ for (i = 0, pkts = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ if (pkts)
+ CU_FAIL("pktio stopped, received unexpected events");
+
+ /* start both, send and get packets */
+ /* 0 already started */
+ ret = odp_pktio_start(pktio[1]);
+ CU_ASSERT(ret == 0);
+
+ _pktio_wait_linkup(pktio[1]);
+
+ /* flush packets with magic number in pipes */
+ for (i = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ }
+ }
+
+ if (num_ifaces > 1)
+ pktio_in = pktio[1];
+ else
+ pktio_in = pktio[0];
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0], pktio_in);
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to enqueue packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+ CU_ASSERT(pkts == alloc);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ /* Verify that a schedule call after stop and close does not generate
+ errors. */
+ ev = odp_schedule(NULL, wait);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+}
+
+static void pktio_test_recv_on_wonly(void)
+{
+ odp_pktio_t pktio;
+ int ret;
+ odp_pktin_queue_t pktin;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DISABLED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open pktio");
+ return;
+ }
+
+ CU_ASSERT(odp_pktin_queue(pktio, &pktin, 1) == 0);
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ _pktio_wait_linkup(pktio);
+
+ ret = odp_pktio_stop(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static void pktio_test_send_on_ronly(void)
+{
+ odp_pktio_t pktio;
+ int ret;
+ odp_pktout_queue_t pktout;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DISABLED);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open pktio");
+ return;
+ }
+
+ CU_ASSERT(odp_pktout_queue(pktio, &pktout, 1) == 0);
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ _pktio_wait_linkup(pktio);
+
+ ret = odp_pktio_stop(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static int pktio_check_pktin_ts(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.config.pktin.bit.ts_all)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktin_ts(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ uint64_t ns1, ns2;
+ uint64_t res, res_ns, input_delay;
+ odp_time_t ts_prev;
+ odp_time_t ts;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.ts_all = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ /* Test odp_pktio_ts_res() and odp_pktio_ts_from_ns() */
+ res = odp_pktio_ts_res(pktio_tx);
+ CU_ASSERT(res > PKTIO_TS_MIN_RES);
+ CU_ASSERT(res < PKTIO_TS_MAX_RES);
+ ns1 = 100;
+ ts = odp_pktio_ts_from_ns(pktio_tx, ns1);
+ ns2 = odp_time_to_ns(ts);
+ CU_ASSERT_FATAL(res != 0);
+ res_ns = ODP_TIME_SEC_IN_NS / res;
+ if (ODP_TIME_SEC_IN_NS % res)
+ res_ns++;
+ /* Allow some arithmetic tolerance */
+ CU_ASSERT((ns2 <= (ns1 + res_ns)) && (ns2 >= (ns1 - res_ns)));
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ if (ret != 1)
+ break;
+
+ /* Compare to packet IO time to input timestamp */
+ ts = odp_pktio_time(pktio_rx_info.id, NULL);
+ CU_ASSERT_FATAL(odp_packet_has_ts(pkt_tbl[i]));
+ ts_prev = odp_packet_ts(pkt_tbl[i]);
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) >= 0);
+ input_delay = odp_time_diff_ns(ts, ts_prev);
+ if (input_delay > 100 * ODP_TIME_MSEC_IN_NS) {
+ printf(" Test packet %d input delay: %" PRIu64 "ns\n", i, input_delay);
+ CU_FAIL("Packet input delay too long");
+ }
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ ts_prev = ODP_TIME_NULL;
+ for (i = 0; i < num_rx; i++) {
+ ts = odp_packet_ts(pkt_tbl[i]);
+
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0);
+
+ ts_prev = ts;
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_ts(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.config.pktout.bit.ts_ena)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktout_ts(void)
+{
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktout_queue_t pktout_queue;
+ odp_pktio_t pktio_tx, pktio_rx;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_time_t ts_prev;
+ odp_time_t ts;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ts_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Start with current pktio time */
+ ts_prev = odp_pktio_time(pktio_tx, NULL);
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Enable ts capture on this pkt */
+ odp_packet_ts_request(pkt_tbl[i], 1);
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS,
+ false);
+ if (ret != 1)
+ break;
+
+ /* Since we got packet back, check for sent ts */
+ CU_ASSERT_FATAL(odp_pktout_ts_read(pktio_tx, &ts) == 0);
+
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0);
+ ts_prev = ts;
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_pktout_compl_event(bool use_plain_queue)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_queue_t compl_queue[TX_BATCH_LEN];
+ odp_schedule_capability_t sched_capa;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ char queuename[ODP_QUEUE_NAME_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_queue_capability_t queue_capa;
+ uint16_t seq_found[TX_BATCH_LEN];
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_packet_tx_compl_t tx_compl;
+ odp_packet_tx_compl_opt_t opt;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ odp_queue_param_t qparam;
+ int flag, ret, i, num_rx = 0;
+ odp_event_t ev;
+ uint64_t wait;
+
+ /* Create queues to receive PKTIO Tx completion events */
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ CU_ASSERT_FATAL(!odp_queue_capability(&queue_capa));
+
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ sprintf(queuename, "TxComplQueue%u", i);
+ odp_queue_param_init(&qparam);
+
+ if (use_plain_queue) {
+ qparam.type = ODP_QUEUE_TYPE_PLAIN;
+ } else {
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+ compl_queue[i] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(compl_queue[i] != ODP_QUEUE_INVALID);
+ }
+
+ memset(&pktout_queue, 0, sizeof(pktout_queue));
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx completion offload for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_event == 1);
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_all ==
+ pktio_capa.tx_compl.mode_event);
+ if (use_plain_queue) {
+ /* CU_ASSERT needs these extra braces */
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.queue_type_plain != 0);
+ } else {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.queue_type_sched != 0);
+ }
+
+ odp_pktio_config_init(&config);
+ config.tx_compl.mode_event = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(&opt, 0, sizeof(opt));
+
+ /* Disabled by default */
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Check that disable works. Also COMPL_ALL should be still supported. */
+ opt.queue = compl_queue[0];
+ opt.mode = ODP_PACKET_TX_COMPL_ALL;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+ opt.queue = compl_queue[0];
+ opt.mode = ODP_PACKET_TX_COMPL_EVENT;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Prepare batch of pkts with different tx completion queues */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) == 0);
+ opt.queue = compl_queue[i];
+ opt.mode = ODP_PACKET_TX_COMPL_EVENT;
+ odp_packet_tx_compl_request(pkt_tbl[i], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) != 0);
+ /* Set pkt sequence number as its user ptr */
+ odp_packet_user_ptr_set(pkt_tbl[i], (const void *)&pkt_seq[i]);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ wait = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS);
+ memset(seq_found, 0, sizeof(seq_found));
+
+ /* Receive Packet Tx completion events for all sent/dropped pkts */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ if (use_plain_queue) {
+ ev = odp_queue_deq(compl_queue[i]);
+
+ /* Event validation */
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT_FATAL(odp_event_is_valid(ev) == 1);
+ CU_ASSERT_FATAL(odp_event_type(ev) == ODP_EVENT_PACKET_TX_COMPL);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_from_event(ev) !=
+ ODP_PACKET_TX_COMPL_INVALID);
+
+ tx_compl = odp_packet_tx_compl_from_event(ev);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_to_event(tx_compl) == ev);
+
+ /* User ptr should be same as packet's user ptr */
+ CU_ASSERT(odp_packet_tx_compl_user_ptr(tx_compl) ==
+ (const void *)&pkt_seq[i]);
+
+ /* No user area or source pool for TX completion events */
+ CU_ASSERT(odp_event_user_area(ev) == NULL);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == NULL);
+ CU_ASSERT(flag < 0);
+
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+
+ /* Alternatively call event free / compl free */
+ if (i % 2)
+ odp_packet_tx_compl_free(tx_compl);
+ else
+ odp_event_free(ev);
+ } else {
+ odp_queue_t rcv_queue;
+ int j;
+
+ ev = odp_schedule(&rcv_queue, wait);
+
+ /* Event validation */
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT_FATAL(odp_event_is_valid(ev) == 1);
+ CU_ASSERT_FATAL(odp_event_type(ev) == ODP_EVENT_PACKET_TX_COMPL);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_from_event(ev) !=
+ ODP_PACKET_TX_COMPL_INVALID);
+
+ tx_compl = odp_packet_tx_compl_from_event(ev);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_to_event(tx_compl) == ev);
+
+ /* User ptr should be same as packet's user ptr i.e seq array ptr */
+ for (j = 0; j < TX_BATCH_LEN; j++) {
+ if (!seq_found[j] &&
+ ((const void *)&pkt_seq[j] ==
+ odp_packet_tx_compl_user_ptr(tx_compl))) {
+ /* Mark that sequence number is found */
+ seq_found[j] = 1;
+
+ /* Receive queue validation */
+ CU_ASSERT(rcv_queue == compl_queue[j]);
+ break;
+ }
+ }
+
+ /* No user area or source pool for TX completion events */
+ CU_ASSERT(odp_event_user_area(ev) == NULL);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == NULL);
+ CU_ASSERT(flag < 0);
+
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+
+ /* Check that sequence number is found */
+ CU_ASSERT(j < TX_BATCH_LEN);
+
+ /* Alternatively call event free / compl free */
+ if (i % 2)
+ odp_packet_tx_compl_free(tx_compl);
+ else
+ odp_event_free(ev);
+ }
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ odp_schedule_pause();
+
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ odp_schedule_resume();
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ odp_queue_destroy(compl_queue[i]);
+}
+
+static void pktio_test_pktout_compl_poll(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_packet_tx_compl_opt_t opt;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ int ret, i, num_rx = 0;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx completion offload for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_poll == 1);
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.max_compl_id >= (TX_BATCH_LEN - 1));
+
+ odp_pktio_config_init(&config);
+ config.tx_compl.mode_poll = 1;
+ config.tx_compl.max_compl_id = TX_BATCH_LEN - 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Completion status is initially zero */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) == 0);
+ }
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(&opt, 0, sizeof(opt));
+
+ /* Disabled by default */
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Check that disable works */
+ opt.compl_id = 0;
+ opt.mode = ODP_PACKET_TX_COMPL_POLL;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Prepare batch of pkts with different tx completion identifiers */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) == 0);
+ opt.compl_id = i;
+ opt.mode = ODP_PACKET_TX_COMPL_POLL;
+ odp_packet_tx_compl_request(pkt_tbl[i], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) != 0);
+ /* Set pkt sequence number as its user ptr */
+ odp_packet_user_ptr_set(pkt_tbl[i], (const void *)&pkt_seq[i]);
+
+ /* Completion status should be still zero after odp_packet_tx_compl_request() */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_rx; i++) {
+ /* Transmits should be complete since we received the packets already */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) > 0);
+
+ /* Check that the previous call did not clear the status */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) > 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_compl_event(bool plain)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.tx_compl.mode_event ||
+ (plain && !capa.tx_compl.queue_type_plain) ||
+ (!plain && !capa.tx_compl.queue_type_sched))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktout_compl_poll(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.tx_compl.mode_poll == 0 ||
+ capa.tx_compl.max_compl_id < (TX_BATCH_LEN - 1))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktout_compl_event_plain_queue(void)
+{
+ return pktio_check_pktout_compl_event(true);
+}
+
+static int pktio_check_pktout_compl_event_sched_queue(void)
+{
+ return pktio_check_pktout_compl_event(false);
+}
+
+static void pktio_test_pktout_compl_event_plain_queue(void)
+{
+ pktio_test_pktout_compl_event(true);
+}
+
+static void pktio_test_pktout_compl_event_sched_queue(void)
+{
+ pktio_test_pktout_compl_event(false);
+}
+
+static void pktio_test_pktout_dont_free(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt, rx_pkt;
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ odp_pktio_t pktio_tx, pktio_rx;
+ pktio_info_t pktio_rx_info;
+ uint32_t pkt_seq;
+ int ret, i;
+ const int num_pkt = 1;
+ int transmits = 5;
+ int num_rx = 0;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ /* Check TX interface capa */
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &pktio_capa) == 0);
+ CU_ASSERT_FATAL(pktio_capa.free_ctrl.dont_free == 1);
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(&pkt, &pkt_seq, num_pkt, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == num_pkt);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Set don't free flag */
+ CU_ASSERT(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DISABLED);
+ odp_packet_free_ctrl_set(pkt, ODP_PACKET_FREE_CTRL_DONT_FREE);
+ CU_ASSERT_FATAL(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DONT_FREE);
+
+ while (transmits--) {
+ /* Retransmit the same packet after it has been received from the RX interface */
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, &pkt, num_pkt) == num_pkt);
+
+ num_rx = wait_for_packets(&pktio_rx_info, &rx_pkt, &pkt_seq, num_pkt,
+ TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == num_pkt);
+
+ if (num_rx != num_pkt)
+ break;
+
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(rx_pkt));
+ odp_packet_free(rx_pkt);
+ }
+
+ odp_packet_free(pkt);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_dont_free(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret == 0 && capa.free_ctrl.dont_free == 1)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void pktio_test_chksum(void (*config_fn)(odp_pktio_t, odp_pktio_t),
+ void (*prep_fn)(odp_packet_t pkt),
+ void (*test_fn)(odp_packet_t pkt))
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ pktio_info_t pktio_rx_info;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret;
+ int i, num_rx;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ config_fn(pktio_tx, pktio_rx);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ _pktio_wait_linkup(pktio[i]);
+ }
+
+ ret = create_packets_udp(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx, false, ETH_UNICAST);
+ CU_ASSERT(ret == TX_BATCH_LEN);
+ if (ret != TX_BATCH_LEN) {
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+ return;
+ }
+
+ /* Provide L3 and L4 proto for pktout HW checksum generation */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ odp_packet_has_ipv4_set(pkt_tbl[i], true);
+ odp_packet_has_udp_set(pkt_tbl[i], true);
+ }
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ if (prep_fn)
+ prep_fn(pkt_tbl[i]);
+
+ send_packets(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq,
+ TX_BATCH_LEN, TXRX_MODE_MULTI,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++) {
+ test_fn(pkt_tbl[i]);
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_chksum_sctp(void (*config_fn)(odp_pktio_t, odp_pktio_t),
+ void (*prep_fn)(odp_packet_t pkt),
+ void (*test_fn)(odp_packet_t pkt))
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ pktio_info_t pktio_rx_info;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret;
+ int i, num_rx;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ config_fn(pktio_tx, pktio_rx);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ _pktio_wait_linkup(pktio[i]);
+ }
+
+ ret = create_packets_sctp(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT(ret == TX_BATCH_LEN);
+ if (ret != TX_BATCH_LEN) {
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+ return;
+ }
+
+ /* Provide L3 and L4 proto for pktout HW checksum generation */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ odp_packet_has_ipv4_set(pkt_tbl[i], true);
+ odp_packet_has_sctp_set(pkt_tbl[i], true);
+ }
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ if (prep_fn)
+ prep_fn(pkt_tbl[i]);
+
+ send_packets(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ num_rx = wait_for_packets_hdr(&pktio_rx_info, pkt_tbl, pkt_seq,
+ TX_BATCH_LEN, TXRX_MODE_MULTI,
+ ODP_TIME_SEC_IN_NS, ODPH_SCTPHDR_LEN, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++) {
+ test_fn(pkt_tbl[i]);
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_chksum_in_ipv4(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.ipv4_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_ipv4_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.ipv4_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_ipv4_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+}
+
+static void pktio_test_chksum_in_ipv4_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l3_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_ipv4(void)
+{
+ pktio_test_chksum(pktio_test_chksum_in_ipv4_config,
+ pktio_test_chksum_in_ipv4_prep,
+ pktio_test_chksum_in_ipv4_test);
+}
+
+static int pktio_check_chksum_in_udp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.udp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_udp_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.udp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_udp_prep(odp_packet_t pkt)
+{
+ odp_packet_has_ipv4_set(pkt, 1);
+ odp_packet_has_udp_set(pkt, 1);
+ odph_ipv4_csum_update(pkt);
+ odph_udp_chksum_set(pkt);
+}
+
+static void pktio_test_chksum_in_udp_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l4_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_udp(void)
+{
+ pktio_test_chksum(pktio_test_chksum_in_udp_config,
+ pktio_test_chksum_in_udp_prep,
+ pktio_test_chksum_in_udp_test);
+}
+
+static int pktio_check_chksum_in_sctp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.sctp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_sctp_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.sctp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_sctp_prep(odp_packet_t pkt)
+{
+ odp_packet_has_ipv4_set(pkt, 1);
+ odp_packet_has_sctp_set(pkt, 1);
+ odph_ipv4_csum_update(pkt);
+ odph_sctp_chksum_set(pkt);
+}
+
+static void pktio_test_chksum_in_sctp_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l4_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_sctp(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_in_sctp_config,
+ pktio_test_chksum_in_sctp_prep,
+ pktio_test_chksum_in_sctp_test);
+}
+
+static int pktio_check_chksum_out_ipv4(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.ipv4_chksum_ena ||
+ !capa.config.pktout.bit.ipv4_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_ipv4_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ipv4_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum != 0);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l3_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_config,
+ pktio_test_chksum_out_ipv4_no_ovr_prep,
+ pktio_test_chksum_out_ipv4_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l3_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum != 0);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_config,
+ pktio_test_chksum_out_ipv4_ovr_prep,
+ pktio_test_chksum_out_ipv4_ovr_test);
+}
+
+static void pktio_test_chksum_out_ipv4_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ipv4_chksum_ena = 1;
+ config.pktout.bit.ipv4_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_pktio(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_pktio_config,
+ NULL,
+ pktio_test_chksum_out_ipv4_test);
+}
+
+static int pktio_check_chksum_out_udp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.udp_chksum_ena ||
+ !capa.config.pktout.bit.udp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_udp_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.udp_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_udp_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL) {
+ CU_ASSERT(udp->chksum != 0);
+ CU_ASSERT(!odph_udp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_udp_no_ovr_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+ odp_packet_l4_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_udp_no_ovr_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL)
+ CU_ASSERT(udp->chksum == 0);
+}
+
+static void pktio_test_chksum_out_udp_no_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_config,
+ pktio_test_chksum_out_udp_no_ovr_prep,
+ pktio_test_chksum_out_udp_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_udp_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l4_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_udp_ovr_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL) {
+ CU_ASSERT(udp->chksum != 0);
+ CU_ASSERT(!odph_udp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_udp_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_config,
+ pktio_test_chksum_out_udp_ovr_prep,
+ pktio_test_chksum_out_udp_ovr_test);
+}
+
+static void pktio_test_chksum_out_udp_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.udp_chksum_ena = 1;
+ config.pktout.bit.udp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_udp_pktio(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_pktio_config,
+ NULL,
+ pktio_test_chksum_out_udp_test);
+}
+
+static int pktio_check_chksum_out_sctp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.sctp_chksum_ena ||
+ !capa.config.pktout.bit.sctp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_sctp_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.sctp_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_sctp_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL) {
+ CU_ASSERT(sctp->chksum != 0);
+ CU_ASSERT(!odph_sctp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+ odp_packet_l4_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL)
+ CU_ASSERT(sctp->chksum == 0);
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_config,
+ pktio_test_chksum_out_sctp_no_ovr_prep,
+ pktio_test_chksum_out_sctp_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_sctp_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l4_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_sctp_ovr_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL) {
+ CU_ASSERT(sctp->chksum != 0);
+ CU_ASSERT(!odph_sctp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_sctp_ovr(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_config,
+ pktio_test_chksum_out_sctp_ovr_prep,
+ pktio_test_chksum_out_sctp_ovr_test);
+}
+
+static void pktio_test_chksum_out_sctp_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.sctp_chksum_ena = 1;
+ config.pktout.bit.sctp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_sctp_pktio(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_pktio_config,
+ NULL,
+ pktio_test_chksum_out_sctp_test);
+}
+
+static int create_pool(const char *iface, int num)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_param_t params;
+ odp_pool_capability_t pool_capa;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ /* Allocate enough buffers taking into consideration core starvation
+ * due to caching */
+ params.pkt.num = PKT_BUF_NUM + params.pkt.cache_size;
+ params.type = ODP_POOL_PACKET;
+
+ snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s_%d",
+ iface, pool_segmentation);
+
+ pool[num] = odp_pool_create(pool_name, &params);
+ if (ODP_POOL_INVALID == pool[num]) {
+ ODPH_ERR("failed to create pool: %s\n", pool_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_pktv_pool(const char *iface, int num)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (pool_capa.vector.max_num < PKT_BUF_NUM)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = PKT_BUF_NUM;
+ params.vector.max_size = pool_capa.vector.max_size;
+
+ snprintf(pool_name, sizeof(pool_name), "pktv_pool_%s_%d",
+ iface, pool_segmentation);
+
+ pktv_pool[num] = odp_pool_create(pool_name, &params);
+ if (ODP_POOL_INVALID == pktv_pool[num]) {
+ ODPH_ERR("failed to create pool: %s\n", pool_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_check_pktv(odp_pktin_mode_t in_mode)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = in_mode;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.vector.supported)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktv_queue(void)
+{
+ return pktio_check_pktv(ODP_PKTIN_MODE_QUEUE);
+}
+
+static int pktio_check_pktv_sched(void)
+{
+ return pktio_check_pktv(ODP_PKTIN_MODE_SCHED);
+}
+
+static void pktio_test_pktv_recv_plain(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, true);
+}
+
+static void pktio_test_pktv_recv_parallel(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_PARALLEL, true);
+}
+
+static void pktio_test_pktv_recv_ordered(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_ORDERED, true);
+}
+
+static void pktio_test_pktv_recv_atomic(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_ATOMIC, true);
+}
+
+static void pktio_test_pktv_pktin_queue_config(odp_pktin_mode_t in_mode)
+{
+ odp_pktin_queue_param_t queue_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int num_queues;
+ int i;
+
+ pktio = create_pktio(0, in_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ queue_param.vector.enable = 1;
+ queue_param.vector.pool = default_pktv_pool;
+ queue_param.vector.max_size = capa.vector.min_size;
+ queue_param.vector.max_tmo_ns = capa.vector.min_tmo_ns;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.vector.max_size = capa.vector.max_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ if (capa.vector.max_size != capa.vector.min_size) {
+ queue_param.vector.max_size = capa.vector.max_size - capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+ }
+
+ queue_param.vector.max_size = capa.vector.min_size - 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ queue_param.vector.max_size = capa.vector.max_size + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio = create_pktio(i, in_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ if (!capa.vector.supported) {
+ printf("Vector mode is not supported. Test Skipped\n");
+ return;
+ }
+
+ queue_param.vector.enable = 1;
+ queue_param.vector.pool = pktv_pool[i];
+ queue_param.vector.max_size = capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.vector.max_size = capa.vector.max_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ if (capa.vector.max_size != capa.vector.min_size) {
+ queue_param.vector.max_size = capa.vector.max_size - capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+ }
+
+ queue_param.vector.max_size = capa.vector.min_size - 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ queue_param.vector.max_size = capa.vector.max_size + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_pktv_pktin_queue_config_queue(void)
+{
+ pktio_test_pktv_pktin_queue_config(ODP_PKTIN_MODE_QUEUE);
+}
+
+static void pktio_test_pktv_pktin_queue_config_sched(void)
+{
+ pktio_test_pktv_pktin_queue_config(ODP_PKTIN_MODE_SCHED);
+}
+
+static void pktio_test_recv_maxlen_set(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ uint32_t max_len = PKT_LEN_MAX;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; i++) {
+ uint32_t maxlen_tmp;
+
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(!odp_pktio_capability(pktio[i], &capa));
+ CU_ASSERT_FATAL(capa.set_op.op.maxlen);
+
+ odp_pktio_config_init(&config);
+ CU_ASSERT_FATAL(!odp_pktio_config(pktio[i], &config));
+
+ maxlen_tmp = capa.maxlen.max_input;
+ if (maxlen_tmp == 0)
+ maxlen_tmp = odp_pktin_maxlen(pktio[i]);
+ if (maxlen_tmp < max_len)
+ max_len = maxlen_tmp;
+
+ maxlen_tmp = capa.maxlen.max_output;
+ if (maxlen_tmp == 0)
+ maxlen_tmp = odp_pktout_maxlen(pktio[i]);
+ if (maxlen_tmp < max_len)
+ max_len = maxlen_tmp;
+
+ CU_ASSERT_FATAL(!odp_pktio_maxlen_set(pktio[i], capa.maxlen.max_input,
+ capa.maxlen.max_output));
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ packet_len = max_len;
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ if (ret != 1)
+ break;
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ if (num_rx)
+ odp_packet_free_multi(pkt_tbl, num_rx);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(!odp_pktio_stop(pktio[i]));
+ CU_ASSERT_FATAL(!odp_pktio_close(pktio[i]));
+ }
+
+ /* Restore global variable */
+ packet_len = PKT_LEN_NORMAL;
+}
+
+static int pktio_check_pktout_aging_tmo(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.max_tx_aging_tmo_ns)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktout_aging_tmo(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ int ret, i, num_rx = 0;
+ uint64_t tmo_0, tmo_1;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx aging for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.max_tx_aging_tmo_ns > 0);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.aging_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Prepare packets with aging */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Aging disabled by default */
+ CU_ASSERT(odp_packet_aging_tmo(pkt_tbl[i]) == 0);
+
+ /* Test tmo set relatively since we don't know about supported resolution */
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns - 1);
+ tmo_0 = odp_packet_aging_tmo(pkt_tbl[i]);
+
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns / 2);
+ tmo_1 = odp_packet_aging_tmo(pkt_tbl[i]);
+ CU_ASSERT(tmo_0 > tmo_1);
+
+ /* Set max before transmitting */
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns);
+ CU_ASSERT(odp_packet_aging_tmo(pkt_tbl[i]) != 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_pktin_event_queue(odp_pktin_mode_t pktin_mode)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_param_t out_queue_param;
+ odp_pktout_queue_t pktout_queue;
+ odp_queue_t queue, from = ODP_QUEUE_INVALID;
+ odp_pool_t buf_pool;
+ odp_pool_param_t pool_param;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret, i;
+ odp_time_t t1, t2;
+ int inactive = 0;
+ int num_pkt = 0;
+ int num_buf = 0;
+ int num_bad = 0;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ uint64_t wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = 2 * TX_BATCH_LEN;
+ pool_param.buf.size = 100;
+
+ buf_pool = odp_pool_create("buffer pool", &pool_param);
+ CU_ASSERT_FATAL(buf_pool != ODP_POOL_INVALID);
+
+ buf = odp_buffer_alloc(buf_pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ in_queue_param.num_queues = 1;
+ in_queue_param.hash_enable = 0;
+ in_queue_param.classifier_enable = 0;
+
+ if (pktin_mode == ODP_PKTIN_MODE_SCHED) {
+ in_queue_param.queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ in_queue_param.queue_param.sched.prio = odp_schedule_default_prio();
+ in_queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ in_queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ odp_pktout_queue_param_init(&out_queue_param);
+ out_queue_param.num_queues = 1;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, pktin_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktout_queue_config(pktio[i], &out_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; ++i)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ if (num_ifaces > 1)
+ pktio_rx = pktio[1];
+ else
+ pktio_rx = pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio_rx, &queue, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout_queue, 1) == 1);
+
+ /* Allocate and initialize test packets */
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ if (ret != TX_BATCH_LEN) {
+ CU_FAIL("Failed to generate test packets");
+ return;
+ }
+
+ /* Send packets */
+ ret = odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ /* Send buffer event */
+ ret = odp_queue_enq(queue, odp_buffer_to_event(buf));
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* Receive events */
+ while (1) {
+ /* Break after a period of inactivity */
+ if (pktin_mode == ODP_PKTIN_MODE_SCHED) {
+ ev = odp_schedule(&from, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+ } else {
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (inactive == 0) {
+ inactive = 1;
+ t1 = odp_time_local();
+ continue;
+ } else {
+ t2 = odp_time_local();
+ if (odp_time_diff_ns(t2, t1) > ODP_TIME_SEC_IN_NS)
+ break;
+
+ continue;
+ }
+ }
+
+ inactive = 0;
+ }
+
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID) {
+ num_pkt++;
+
+ if (pktin_mode == ODP_PKTIN_MODE_SCHED)
+ CU_ASSERT(from == queue);
+ }
+ } else if (odp_event_type(ev) == ODP_EVENT_BUFFER) {
+ num_buf++;
+ } else {
+ CU_FAIL("Bad event type");
+ num_bad++;
+ }
+
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(num_pkt == TX_BATCH_LEN);
+ CU_ASSERT(num_buf == 1);
+ CU_ASSERT(num_bad == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pool_destroy(buf_pool) == 0);
+}
+
+static void pktio_test_pktin_event_sched(void)
+{
+ pktio_test_pktin_event_queue(ODP_PKTIN_MODE_SCHED);
+}
+
+static int pktio_check_pktin_event_sched(void)
+{
+ if (odp_cunit_ci_skip("pktio_test_pktin_event_sched"))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_suite_init(void)
+{
+ int i;
+
+ odp_atomic_init_u32(&ip_seq, 0);
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = true;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ if (create_pool(iface_name[i], i) != 0)
+ return -1;
+
+ if (create_pktv_pool(iface_name[i], i) != 0)
+ return -1;
+ }
+
+ if (default_pool_create() != 0) {
+ ODPH_ERR("failed to create default pool\n");
+ return -1;
+ }
+
+ if (default_pktv_pool_create() != 0) {
+ ODPH_ERR("failed to create default pktv pool\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_suite_init_unsegmented(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktio_suite_init_segmented(void)
+{
+ pool_segmentation = PKT_POOL_SEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktv_suite_init(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktio_suite_term(void)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_t pool;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ snprintf(pool_name, sizeof(pool_name),
+ "pkt_pool_%s_%d", iface_name[i], pool_segmentation);
+ pool = odp_pool_lookup(pool_name);
+ if (pool == ODP_POOL_INVALID)
+ continue;
+
+ if (odp_pool_destroy(pool) != 0) {
+ ODPH_ERR("failed to destroy pool %s\n", pool_name);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < num_ifaces; ++i) {
+ snprintf(pool_name, sizeof(pool_name),
+ "pktv_pool_%s_%d", iface_name[i], pool_segmentation);
+ pool = odp_pool_lookup(pool_name);
+ if (pool == ODP_POOL_INVALID)
+ continue;
+
+ if (odp_pool_destroy(pool) != 0) {
+ ODPH_ERR("failed to destroy pool %s\n", pool_name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(default_pkt_pool) != 0) {
+ ODPH_ERR("failed to destroy default pool\n");
+ ret = -1;
+ }
+ default_pkt_pool = ODP_POOL_INVALID;
+
+ if (odp_pool_destroy(default_pktv_pool) != 0) {
+ ODPH_ERR("failed to destroy default pktv pool\n");
+ ret = -1;
+ }
+ default_pktv_pool = ODP_POOL_INVALID;
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+static int pktv_suite_term(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_term();
+}
+
+odp_testinfo_t pktio_suite_unsegmented[] = {
+ ODP_TEST_INFO(pktio_test_default_values),
+ ODP_TEST_INFO(pktio_test_open),
+ ODP_TEST_INFO(pktio_test_lookup),
+ ODP_TEST_INFO(pktio_test_index),
+ ODP_TEST_INFO(pktio_test_print),
+ ODP_TEST_INFO(pktio_test_pktio_config),
+ ODP_TEST_INFO(pktio_test_info),
+ ODP_TEST_INFO(pktio_test_link_info),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_direct),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_sched),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_multi_sched),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_queue),
+ ODP_TEST_INFO(pktio_test_pktout_queue_config),
+ ODP_TEST_INFO(pktio_test_plain_queue),
+ ODP_TEST_INFO(pktio_test_plain_multi),
+ ODP_TEST_INFO(pktio_test_sched_queue),
+ ODP_TEST_INFO(pktio_test_sched_multi),
+ ODP_TEST_INFO(pktio_test_recv),
+ ODP_TEST_INFO(pktio_test_recv_multi),
+ ODP_TEST_INFO(pktio_test_recv_queue),
+ ODP_TEST_INFO(pktio_test_recv_tmo),
+ ODP_TEST_INFO(pktio_test_recv_mq_tmo),
+ ODP_TEST_INFO(pktio_test_recv_mtu),
+ ODP_TEST_INFO(pktio_test_maxlen),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_maxlen_set,
+ pktio_check_maxlen_set),
+ ODP_TEST_INFO(pktio_test_promisc),
+ ODP_TEST_INFO(pktio_test_mac),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_start_stop,
+ pktio_check_start_stop),
+ ODP_TEST_INFO(pktio_test_recv_on_wonly),
+ ODP_TEST_INFO(pktio_test_send_on_ronly),
+ ODP_TEST_INFO(pktio_test_plain_multi_event),
+ ODP_TEST_INFO(pktio_test_sched_multi_event),
+ ODP_TEST_INFO(pktio_test_recv_multi_event),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_event_sched,
+ pktio_check_pktin_event_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters,
+ pktio_check_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters_bcast,
+ pktio_check_statistics_counters_bcast),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_queue_statistics_counters,
+ pktio_check_queue_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_event_queue_statistics_counters,
+ pktio_check_event_queue_statistics_counters),
+ ODP_TEST_INFO(pktio_test_extra_stats),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_proto_statistics_counters,
+ pktio_check_proto_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_ts,
+ pktio_check_pktin_ts),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_ts,
+ pktio_check_pktout_ts),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_ipv4,
+ pktio_check_chksum_in_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_udp,
+ pktio_check_chksum_in_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_sctp,
+ pktio_check_chksum_in_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_no_ovr,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_pktio,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_ovr,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_no_ovr,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_pktio,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_ovr,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_no_ovr,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_pktio,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_ovr,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_recv_maxlen_set,
+ pktio_check_maxlen_set),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_aging_tmo,
+ pktio_check_pktout_aging_tmo),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_event_plain_queue,
+ pktio_check_pktout_compl_event_plain_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_event_sched_queue,
+ pktio_check_pktout_compl_event_sched_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_poll, pktio_check_pktout_compl_poll),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_dont_free, pktio_check_pktout_dont_free),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_rx, pktio_check_pause_rx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_tx, pktio_check_pause_tx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_both, pktio_check_pause_both),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_rx, pktio_check_pfc_rx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_tx, pktio_check_pfc_tx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_both, pktio_check_pfc_both),
+ ODP_TEST_INFO_NULL
+};
+
+odp_testinfo_t pktio_suite_segmented[] = {
+ ODP_TEST_INFO(pktio_test_plain_queue),
+ ODP_TEST_INFO(pktio_test_plain_multi),
+ ODP_TEST_INFO(pktio_test_sched_queue),
+ ODP_TEST_INFO(pktio_test_sched_multi),
+ ODP_TEST_INFO(pktio_test_recv),
+ ODP_TEST_INFO(pktio_test_recv_multi),
+ ODP_TEST_INFO(pktio_test_recv_mtu),
+ ODP_TEST_INFO_NULL
+};
+
+odp_testinfo_t pktv_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_pktin_queue_config_queue, pktio_check_pktv_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_pktin_queue_config_sched, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_plain, pktio_check_pktv_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_parallel, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_ordered, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_atomic, pktio_check_pktv_sched),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t pktio_suites[] = {
+ {"Packet I/O Unsegmented", pktio_suite_init_unsegmented,
+ pktio_suite_term, pktio_suite_unsegmented},
+ {"Packet I/O Segmented", pktio_suite_init_segmented,
+ pktio_suite_term, pktio_suite_segmented},
+ {"Packet parser", parser_suite_init, parser_suite_term, parser_suite},
+ {"Packet vector", pktv_suite_init, pktv_suite_term, pktv_suite},
+ {"Large Segment Offload", lso_suite_init, lso_suite_term, lso_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(pktio_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/pool/.gitignore b/test/validation/api/pool/.gitignore
index fc91b28d6..fc91b28d6 100644
--- a/test/common_plat/validation/api/pool/.gitignore
+++ b/test/validation/api/pool/.gitignore
diff --git a/test/validation/api/pool/Makefile.am b/test/validation/api/pool/Makefile.am
new file mode 100644
index 000000000..1b0d5934c
--- /dev/null
+++ b/test/validation/api/pool/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = pool_main
+pool_main_SOURCES = pool.c
diff --git a/test/validation/api/pool/pool.c b/test/validation/api/pool/pool.c
new file mode 100644
index 000000000..b2ae4acd9
--- /dev/null
+++ b/test/validation/api/pool/pool.c
@@ -0,0 +1,2384 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2020 Marvell
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp_api.h>
+#include "odp_cunit_common.h"
+#include "test_common_macros.h"
+#include <odp/helper/odph_api.h>
+
+#define MAX_WORKERS 32
+
+#define BUF_SIZE 1500
+#define BUF_NUM 1000
+#define TMO_NUM 1000
+#define VEC_NUM 1000
+#define VEC_LEN 32
+#define PKT_LEN 400
+#define PKT_NUM 500
+#define ELEM_NUM 10u
+#define ELEM_SIZE 128u
+#define CACHE_SIZE 32
+#define MAX_NUM_DEFAULT (10 * 1024 * 1024)
+#define UAREA 0xaa
+
+#define EXT_NUM_BUF 10
+#define EXT_BUF_SIZE 2048
+#define EXT_BUF_ALIGN 64
+#define EXT_APP_HDR_SIZE 128
+#define EXT_UAREA_SIZE 32
+#define EXT_HEADROOM 16
+#define MAGIC_U8 0x7a
+
+typedef struct {
+ odp_barrier_t init_barrier;
+ odp_atomic_u32_t index;
+ uint32_t nb_threads;
+ odp_pool_t pool;
+} global_shared_mem_t;
+
+typedef struct {
+ uint32_t count;
+ uint8_t mark[ELEM_NUM];
+} uarea_init_t;
+
+static global_shared_mem_t *global_mem;
+
+static odp_pool_capability_t global_pool_capa;
+static odp_pool_param_t default_pool_param;
+static odp_pool_ext_capability_t global_pool_ext_capa;
+
+static void test_param_init(uint8_t fill)
+{
+ odp_pool_param_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_pool_param_init(&param);
+
+ CU_ASSERT(param.uarea_init.init_fn == NULL);
+ CU_ASSERT(param.uarea_init.args == NULL);
+
+ CU_ASSERT(param.buf.uarea_size == 0);
+ CU_ASSERT(param.buf.cache_size >= global_pool_capa.buf.min_cache_size &&
+ param.buf.cache_size <= global_pool_capa.buf.max_cache_size);
+
+ CU_ASSERT(param.pkt.max_num == 0);
+ CU_ASSERT(param.pkt.num_subparam == 0);
+ CU_ASSERT(param.pkt.uarea_size == 0);
+ CU_ASSERT(param.pkt.cache_size >= global_pool_capa.pkt.min_cache_size &&
+ param.pkt.cache_size <= global_pool_capa.pkt.max_cache_size);
+
+ CU_ASSERT(param.tmo.uarea_size == 0);
+ CU_ASSERT(param.tmo.cache_size >= global_pool_capa.tmo.min_cache_size &&
+ param.tmo.cache_size <= global_pool_capa.tmo.max_cache_size);
+
+ CU_ASSERT(param.vector.uarea_size == 0);
+ CU_ASSERT(param.vector.cache_size >= global_pool_capa.vector.min_cache_size &&
+ param.vector.cache_size <= global_pool_capa.vector.max_cache_size);
+}
+
+static void pool_test_param_init(void)
+{
+ test_param_init(0);
+ test_param_init(0xff);
+}
+
+static void pool_create_destroy(odp_pool_param_t *param)
+{
+ odp_pool_t pool;
+
+ pool = odp_pool_create(NULL, param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(odp_pool_to_u64(pool) !=
+ odp_pool_to_u64(ODP_POOL_INVALID));
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_create_destroy_buffer(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.size = BUF_SIZE;
+ param.buf.num = BUF_NUM;
+
+ pool_create_destroy(&param);
+}
+
+static void pool_test_create_destroy_packet(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num = PKT_NUM;
+
+ pool_create_destroy(&param);
+}
+
+static void pool_test_create_destroy_timeout(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = TMO_NUM;
+
+ pool_create_destroy(&param);
+}
+
+static void pool_test_create_destroy_vector(void)
+{
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t max_num = VEC_NUM;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ CU_ASSERT_FATAL(capa.vector.max_pools > 0);
+
+ if (capa.vector.max_num && capa.vector.max_num < max_num)
+ max_num = capa.vector.max_num;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = max_num;
+ param.vector.max_size = capa.vector.max_size < VEC_LEN ? capa.vector.max_size : VEC_LEN;
+
+ pool_create_destroy(&param);
+}
+
+static int pool_check_buffer_uarea_init(void)
+{
+ if (global_pool_capa.buf.max_uarea_size == 0 || !global_pool_capa.buf.uarea_persistence)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_packet_uarea_init(void)
+{
+ if (global_pool_capa.pkt.max_uarea_size == 0 || !global_pool_capa.pkt.uarea_persistence)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_vector_uarea_init(void)
+{
+ if (global_pool_capa.vector.max_uarea_size == 0 ||
+ !global_pool_capa.vector.uarea_persistence)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_timeout_uarea_init(void)
+{
+ if (global_pool_capa.tmo.max_uarea_size == 0 || !global_pool_capa.tmo.uarea_persistence)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void init_event_uarea(void *uarea, uint32_t size, void *args, uint32_t index)
+{
+ uarea_init_t *data = args;
+
+ data->count++;
+ data->mark[index] = 1;
+ memset(uarea, UAREA, size);
+}
+
+static void pool_test_buffer_uarea_init(void)
+{
+ odp_pool_param_t param;
+ uint32_t num = ODPH_MIN(global_pool_capa.buf.max_num, ELEM_NUM),
+ size = ODPH_MIN(global_pool_capa.buf.max_size, ELEM_SIZE), i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_buffer_t bufs[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_BUFFER;
+ param.uarea_init.init_fn = init_event_uarea;
+ param.uarea_init.args = &data;
+ param.buf.num = num;
+ param.buf.size = size;
+ param.buf.uarea_size = 1;
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ bufs[i] = odp_buffer_alloc(pool);
+
+ CU_ASSERT(bufs[i] != ODP_BUFFER_INVALID);
+
+ if (bufs[i] == ODP_BUFFER_INVALID)
+ break;
+
+ uarea = odp_buffer_user_area(bufs[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ odp_buffer_free_multi(bufs, i);
+ odp_pool_destroy(pool);
+}
+
+static void pool_test_packet_uarea_init(void)
+{
+ odp_pool_param_t param;
+ uint32_t num = ODPH_MIN(global_pool_capa.pkt.max_num, ELEM_NUM),
+ size = ODPH_MIN(global_pool_capa.pkt.max_len, ELEM_SIZE), i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_packet_t pkts[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.uarea_init.init_fn = init_event_uarea;
+ param.uarea_init.args = &data;
+ param.pkt.num = num;
+ param.pkt.len = size;
+ param.pkt.uarea_size = 1;
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ pkts[i] = odp_packet_alloc(pool, ELEM_SIZE);
+
+ CU_ASSERT(pkts[i] != ODP_PACKET_INVALID);
+
+ if (pkts[i] == ODP_PACKET_INVALID)
+ break;
+
+ uarea = odp_packet_user_area(pkts[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ odp_packet_free_multi(pkts, i);
+ odp_pool_destroy(pool);
+}
+
+static void pool_test_vector_uarea_init(void)
+{
+ odp_pool_param_t param;
+ uint32_t num = ODPH_MIN(global_pool_capa.vector.max_num, ELEM_NUM),
+ size = ODPH_MIN(global_pool_capa.vector.max_size, ELEM_NUM), i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_packet_vector_t vecs[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_VECTOR;
+ param.uarea_init.init_fn = init_event_uarea;
+ param.uarea_init.args = &data;
+ param.vector.num = num;
+ param.vector.max_size = size;
+ param.vector.uarea_size = 1;
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ vecs[i] = odp_packet_vector_alloc(pool);
+
+ CU_ASSERT(vecs[i] != ODP_PACKET_VECTOR_INVALID);
+
+ if (vecs[i] == ODP_PACKET_VECTOR_INVALID)
+ break;
+
+ uarea = odp_packet_vector_user_area(vecs[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_packet_vector_free(vecs[j]);
+
+ odp_pool_destroy(pool);
+}
+
+static void pool_test_timeout_uarea_init(void)
+{
+ odp_pool_param_t param;
+ uint32_t num = ODPH_MIN(global_pool_capa.tmo.max_num, ELEM_NUM), i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_timeout_t tmos[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_TIMEOUT;
+ param.uarea_init.init_fn = init_event_uarea;
+ param.uarea_init.args = &data;
+ param.tmo.num = num;
+ param.tmo.uarea_size = 1;
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ tmos[i] = odp_timeout_alloc(pool);
+
+ CU_ASSERT(tmos[i] != ODP_TIMEOUT_INVALID);
+
+ if (tmos[i] == ODP_TIMEOUT_INVALID)
+ break;
+
+ uarea = odp_timeout_user_area(tmos[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_timeout_free(tmos[j]);
+
+ odp_pool_destroy(pool);
+}
+
+static void pool_test_lookup_info_print(void)
+{
+ odp_pool_t pool;
+ const char pool_name[] = "pool_for_lookup_test";
+ odp_pool_info_t info;
+ odp_pool_param_t param;
+
+ memset(&info, 0, sizeof(info));
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.size = BUF_SIZE;
+ param.buf.num = BUF_NUM;
+
+ pool = odp_pool_create(pool_name, &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(pool_name);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0);
+ CU_ASSERT(strncmp(pool_name, info.name, sizeof(pool_name)) == 0);
+ CU_ASSERT(param.buf.size <= info.params.buf.size);
+ CU_ASSERT(param.buf.align <= info.params.buf.align);
+ CU_ASSERT(param.buf.num <= info.params.buf.num);
+ CU_ASSERT(param.type == info.params.type);
+
+ odp_pool_print(pool);
+ odp_pool_print_all();
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_same_name(const odp_pool_param_t *param)
+{
+ odp_pool_t pool, pool_a, pool_b;
+ const char *name = "same_name";
+
+ pool_a = odp_pool_create(name, param);
+ CU_ASSERT_FATAL(pool_a != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(name);
+ CU_ASSERT(pool == pool_a);
+
+ /* Second pool with the same name */
+ pool_b = odp_pool_create(name, param);
+ CU_ASSERT_FATAL(pool_b != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(name);
+ CU_ASSERT(pool == pool_a || pool == pool_b);
+
+ CU_ASSERT(odp_pool_destroy(pool_a) == 0);
+ CU_ASSERT(odp_pool_destroy(pool_b) == 0);
+}
+
+static void pool_test_same_name_buf(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.size = BUF_SIZE;
+ param.buf.num = BUF_NUM;
+
+ pool_test_same_name(&param);
+}
+
+static void pool_test_same_name_pkt(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num = PKT_NUM;
+
+ pool_test_same_name(&param);
+}
+
+static void pool_test_same_name_tmo(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = TMO_NUM;
+
+ pool_test_same_name(&param);
+}
+
+static void pool_test_same_name_vec(void)
+{
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = 10;
+ param.vector.max_size = 2;
+
+ pool_test_same_name(&param);
+}
+
+static void alloc_buffer(uint32_t cache_size)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ uint32_t i, num;
+ odp_buffer_t buf[BUF_NUM];
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.num = BUF_NUM;
+ param.buf.size = BUF_SIZE;
+ param.pkt.cache_size = cache_size;
+
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ num = 0;
+
+ for (i = 0; i < PKT_NUM; i++) {
+ buf[num] = odp_buffer_alloc(pool);
+ CU_ASSERT(buf[num] != ODP_BUFFER_INVALID);
+
+ if (buf[num] != ODP_BUFFER_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_buffer_free(buf[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_alloc_buffer(void)
+{
+ alloc_buffer(default_pool_param.buf.cache_size);
+}
+
+static void pool_test_alloc_buffer_min_cache(void)
+{
+ alloc_buffer(global_pool_capa.buf.min_cache_size);
+}
+
+static void pool_test_alloc_buffer_max_cache(void)
+{
+ alloc_buffer(global_pool_capa.buf.max_cache_size);
+}
+
+static void alloc_packet_vector(uint32_t cache_size)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t i, num;
+ odp_packet_vector_t pkt_vec[VEC_NUM];
+ uint32_t max_num = VEC_NUM;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ if (capa.vector.max_num && capa.vector.max_num < max_num)
+ max_num = capa.vector.max_num;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = max_num;
+ param.vector.max_size = capa.vector.max_size < VEC_LEN ? capa.vector.max_size : VEC_LEN;
+ param.vector.cache_size = cache_size;
+
+ pool = odp_pool_create(NULL, &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ num = 0;
+ for (i = 0; i < max_num; i++) {
+ odp_packet_vector_t pktv = odp_packet_vector_alloc(pool);
+
+ CU_ASSERT(pktv != ODP_PACKET_VECTOR_INVALID);
+
+ if (pktv == ODP_PACKET_VECTOR_INVALID)
+ continue;
+
+ CU_ASSERT(odp_packet_vector_valid(pktv) == 1);
+ CU_ASSERT(odp_event_is_valid(odp_packet_vector_to_event(pktv)) == 1);
+ CU_ASSERT(odp_packet_vector_size(pktv) == 0);
+
+ pkt_vec[num] = pktv;
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_packet_vector_free(pkt_vec[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_alloc_packet_vector(void)
+{
+ alloc_packet_vector(default_pool_param.vector.cache_size);
+}
+
+static void pool_test_alloc_packet_vector_min_cache(void)
+{
+ alloc_packet_vector(global_pool_capa.vector.min_cache_size);
+}
+
+static void pool_test_alloc_packet_vector_max_cache(void)
+{
+ alloc_packet_vector(global_pool_capa.vector.max_cache_size);
+}
+
+static void alloc_packet(uint32_t cache_size)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ uint32_t i, num;
+ odp_packet_t pkt[PKT_NUM];
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = PKT_NUM;
+ param.pkt.len = PKT_LEN;
+ param.pkt.cache_size = cache_size;
+
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ num = 0;
+
+ for (i = 0; i < PKT_NUM; i++) {
+ pkt[num] = odp_packet_alloc(pool, PKT_LEN);
+ CU_ASSERT(pkt[num] != ODP_PACKET_INVALID);
+
+ if (pkt[num] != ODP_PACKET_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_packet_free(pkt[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_alloc_packet(void)
+{
+ alloc_packet(default_pool_param.pkt.cache_size);
+}
+
+static void pool_test_alloc_packet_min_cache(void)
+{
+ alloc_packet(global_pool_capa.pkt.min_cache_size);
+}
+
+static void pool_test_alloc_packet_max_cache(void)
+{
+ alloc_packet(global_pool_capa.pkt.max_cache_size);
+}
+
+static void pool_test_alloc_packet_subparam(void)
+{
+ odp_pool_t pool;
+ odp_pool_capability_t capa;
+ odp_pool_param_t param;
+ uint32_t i, j, num, num_sub;
+ odp_packet_t pkt[PKT_NUM];
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+ num_sub = capa.pkt.max_num_subparam;
+
+ CU_ASSERT_FATAL(num_sub <= ODP_POOL_MAX_SUBPARAMS);
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = PKT_NUM;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num_subparam = num_sub;
+
+ for (i = 0; i < num_sub; i++) {
+ param.pkt.sub[i].num = PKT_NUM;
+ param.pkt.sub[i].len = PKT_LEN + (i * 100);
+ }
+
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ num = 0;
+
+ for (i = 0; i < PKT_NUM; i++) {
+ pkt[num] = odp_packet_alloc(pool, PKT_LEN);
+ CU_ASSERT(pkt[num] != ODP_PACKET_INVALID);
+
+ if (pkt[num] != ODP_PACKET_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_packet_free(pkt[i]);
+
+ for (j = 0; j < num_sub; j++) {
+ num = 0;
+
+ for (i = 0; i < param.pkt.sub[j].num; i++) {
+ pkt[num] = odp_packet_alloc(pool, param.pkt.sub[j].len);
+ CU_ASSERT(pkt[num] != ODP_PACKET_INVALID);
+
+ if (pkt[num] != ODP_PACKET_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_packet_free(pkt[i]);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void alloc_timeout(uint32_t cache_size)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ uint32_t i, num;
+ odp_timeout_t tmo[TMO_NUM];
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = TMO_NUM;
+ param.tmo.cache_size = cache_size;
+
+ pool = odp_pool_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ num = 0;
+
+ for (i = 0; i < PKT_NUM; i++) {
+ tmo[num] = odp_timeout_alloc(pool);
+ CU_ASSERT(tmo[num] != ODP_TIMEOUT_INVALID);
+
+ if (tmo[num] != ODP_TIMEOUT_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++)
+ odp_timeout_free(tmo[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_alloc_timeout(void)
+{
+ alloc_timeout(default_pool_param.tmo.cache_size);
+}
+
+static void pool_test_alloc_timeout_min_cache(void)
+{
+ alloc_timeout(global_pool_capa.tmo.min_cache_size);
+}
+
+static void pool_test_alloc_timeout_max_cache(void)
+{
+ alloc_timeout(global_pool_capa.tmo.max_cache_size);
+}
+
+static void pool_test_info_packet(void)
+{
+ odp_pool_t pool;
+ odp_pool_info_t info;
+ odp_pool_param_t param;
+ const char pool_name[] = "test_pool_name";
+
+ memset(&info, 0, sizeof(info));
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = PKT_NUM;
+ param.pkt.len = PKT_LEN;
+
+ pool = odp_pool_create(pool_name, &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ memset(&info, 0, sizeof(odp_pool_info_t));
+ CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0);
+
+ CU_ASSERT(strncmp(pool_name, info.name, sizeof(pool_name)) == 0);
+ CU_ASSERT(info.params.type == ODP_POOL_PACKET);
+ CU_ASSERT(info.params.pkt.num == param.pkt.num);
+ CU_ASSERT(info.params.pkt.len == param.pkt.len);
+ CU_ASSERT(info.pkt.max_num >= param.pkt.num);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_info_data_range(void)
+{
+ odp_pool_t pool;
+ odp_pool_info_t info;
+ odp_pool_param_t param;
+ odp_packet_t pkt[PKT_NUM];
+ uint32_t i, num;
+ uintptr_t pool_len;
+
+ memset(&info, 0, sizeof(info));
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = PKT_NUM;
+ param.pkt.len = PKT_LEN;
+
+ pool = odp_pool_create(NULL, &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0);
+
+ pool_len = info.max_data_addr - info.min_data_addr + 1;
+ CU_ASSERT(pool_len >= PKT_NUM * PKT_LEN);
+
+ num = 0;
+
+ for (i = 0; i < PKT_NUM; i++) {
+ pkt[num] = odp_packet_alloc(pool, PKT_LEN);
+ CU_ASSERT(pkt[num] != ODP_PACKET_INVALID);
+
+ if (pkt[num] != ODP_PACKET_INVALID)
+ num++;
+ }
+
+ for (i = 0; i < num; i++) {
+ uintptr_t pkt_data, pkt_data_end;
+ uint32_t offset = 0;
+ uint32_t seg_len = 0;
+ uint32_t pkt_len = odp_packet_len(pkt[i]);
+
+ while (offset < pkt_len) {
+ pkt_data = (uintptr_t)odp_packet_offset(pkt[i], offset,
+ &seg_len, NULL);
+ pkt_data_end = pkt_data + seg_len - 1;
+ CU_ASSERT((pkt_data >= info.min_data_addr) &&
+ (pkt_data_end <= info.max_data_addr));
+ offset += seg_len;
+ }
+
+ odp_packet_free(pkt[i]);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_buf_max_num(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t max_num, num, i;
+ odp_shm_t shm;
+ odp_buffer_t *buf;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ max_num = MAX_NUM_DEFAULT;
+ if (capa.buf.max_num)
+ max_num = capa.buf.max_num;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.num = max_num;
+ param.buf.size = 10;
+
+ pool = odp_pool_create("test_buf_max_num", &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = odp_shm_reserve("test_max_num_shm",
+ max_num * sizeof(odp_buffer_t),
+ sizeof(odp_buffer_t), 0);
+
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ buf = odp_shm_addr(shm);
+
+ num = 0;
+ for (i = 0; i < max_num; i++) {
+ buf[num] = odp_buffer_alloc(pool);
+
+ if (buf[num] != ODP_BUFFER_INVALID) {
+ CU_ASSERT(odp_buffer_is_valid(buf[num]) == 1);
+ CU_ASSERT(odp_event_is_valid(odp_buffer_to_event(buf[num])) == 1);
+ num++;
+ }
+ }
+
+ CU_ASSERT(num == max_num);
+
+ for (i = 0; i < num; i++)
+ odp_buffer_free(buf[i]);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_pkt_max_num(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t max_num, num, i;
+ odp_shm_t shm;
+ odp_packet_t *pkt;
+ uint32_t len = 10;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ max_num = MAX_NUM_DEFAULT;
+ if (capa.pkt.max_num)
+ max_num = capa.pkt.max_num;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = max_num;
+ param.pkt.max_num = max_num;
+ param.pkt.len = len;
+ param.pkt.max_len = len;
+ param.pkt.headroom = 0;
+
+ pool = odp_pool_create("test_packet_max_num", &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = odp_shm_reserve("test_max_num_shm",
+ max_num * sizeof(odp_packet_t),
+ sizeof(odp_packet_t), 0);
+
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ pkt = odp_shm_addr(shm);
+
+ num = 0;
+ for (i = 0; i < max_num; i++) {
+ pkt[num] = odp_packet_alloc(pool, len);
+
+ if (pkt[num] != ODP_PACKET_INVALID) {
+ CU_ASSERT(odp_packet_is_valid(pkt[num]) == 1);
+ CU_ASSERT(odp_event_is_valid(odp_packet_to_event(pkt[num])) == 1);
+ num++;
+ }
+ }
+
+ CU_ASSERT(num == max_num);
+
+ for (i = 0; i < num; i++)
+ odp_packet_free(pkt[i]);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_packet_vector_max_num(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t num, i;
+ odp_shm_t shm;
+ odp_packet_vector_t *pktv;
+ uint32_t max_num = VEC_NUM;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ if (capa.vector.max_num)
+ max_num = capa.vector.max_num;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_VECTOR;
+ param.vector.num = max_num;
+ param.vector.max_size = 1;
+
+ pool = odp_pool_create("test_packet_vector_max_num", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = odp_shm_reserve("test_max_num_shm", max_num * sizeof(odp_packet_vector_t),
+ sizeof(odp_packet_vector_t), 0);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ pktv = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(pktv != NULL);
+
+ num = 0;
+ for (i = 0; i < max_num; i++) {
+ pktv[num] = odp_packet_vector_alloc(pool);
+
+ if (pktv[num] != ODP_PACKET_VECTOR_INVALID)
+ num++;
+ }
+
+ CU_ASSERT(num == max_num);
+
+ for (i = 0; i < num; i++)
+ odp_packet_vector_free(pktv[i]);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_pkt_seg_len(void)
+{
+ uint32_t len = 1500;
+ uint32_t min_seg_len = 42;
+ uint32_t max_num = 10;
+ uint32_t num = 0;
+ uint32_t i;
+ odp_packet_t pkt_tbl[max_num];
+ odp_pool_t pool;
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_PACKET;
+ param.pkt.num = max_num;
+ param.pkt.len = len;
+ param.pkt.max_len = len;
+ param.pkt.seg_len = min_seg_len;
+
+ pool = odp_pool_create("test_packet_seg_len", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < max_num; i++) {
+ pkt_tbl[i] = odp_packet_alloc(pool, len);
+
+ if (pkt_tbl[i] != ODP_PACKET_INVALID)
+ num++;
+ }
+
+ CU_ASSERT(num == max_num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(odp_packet_seg_len(pkt_tbl[i]) >= min_seg_len);
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void pool_test_tmo_max_num(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ odp_pool_capability_t capa;
+ uint32_t max_num, num, i;
+ odp_shm_t shm;
+ odp_timeout_t *tmo;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0);
+
+ max_num = MAX_NUM_DEFAULT;
+ if (capa.tmo.max_num)
+ max_num = capa.tmo.max_num;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = max_num;
+
+ pool = odp_pool_create("test_tmo_max_num", &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = odp_shm_reserve("test_max_num_shm",
+ max_num * sizeof(odp_packet_t),
+ sizeof(odp_packet_t), 0);
+
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ tmo = odp_shm_addr(shm);
+
+ num = 0;
+ for (i = 0; i < max_num; i++) {
+ tmo[num] = odp_timeout_alloc(pool);
+
+ if (tmo[num] != ODP_TIMEOUT_INVALID) {
+ CU_ASSERT(odp_event_is_valid(odp_timeout_to_event(tmo[num])) == 1);
+ num++;
+ }
+ }
+
+ CU_ASSERT(num == max_num);
+
+ for (i = 0; i < num; i++)
+ odp_timeout_free(tmo[i]);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void buffer_alloc_loop(odp_pool_t pool, int num, int buffer_size)
+{
+ int allocs;
+
+ /* Allocate, modify, and free buffers */
+ for (allocs = 0; allocs < num;) {
+ odp_buffer_t buf;
+ uint8_t *data;
+ int i;
+
+ buf = odp_buffer_alloc(pool);
+ if (buf == ODP_BUFFER_INVALID)
+ continue;
+
+ data = odp_buffer_addr(buf);
+
+ for (i = 0; i < buffer_size; i++)
+ data[i] = i;
+
+ odp_buffer_free(buf);
+ allocs++;
+ }
+}
+
+static int run_pool_test_create_after_fork(void *arg ODP_UNUSED)
+{
+ int thr_index;
+
+ thr_index = odp_atomic_fetch_inc_u32(&global_mem->index);
+
+ /* Thread 0 allocates the shared pool */
+ if (thr_index == 0) {
+ odp_pool_t pool;
+ odp_pool_param_t param;
+
+ odp_pool_param_init(&param);
+
+ param.type = ODP_POOL_BUFFER;
+ param.buf.size = BUF_SIZE;
+ param.buf.num = BUF_NUM;
+
+ pool = odp_pool_create(NULL, &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ global_mem->pool = pool;
+ }
+
+ odp_barrier_wait(&global_mem->init_barrier);
+
+ buffer_alloc_loop(global_mem->pool, BUF_NUM, BUF_SIZE);
+
+ return CU_get_number_of_failures();
+}
+
+static void pool_test_create_after_fork(void)
+{
+ odp_shm_t shm;
+ int num;
+
+ /* No single VA required since reserve is done before fork */
+ shm = odp_shm_reserve(NULL, sizeof(global_shared_mem_t), 0, 0);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ global_mem = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(global_mem != NULL);
+
+ num = odp_cpumask_default_worker(NULL, 0);
+ if (num > MAX_WORKERS)
+ num = MAX_WORKERS;
+
+ global_mem->nb_threads = num;
+ global_mem->pool = ODP_POOL_INVALID;
+ odp_barrier_init(&global_mem->init_barrier, num + 1);
+ odp_atomic_init_u32(&global_mem->index, 0);
+
+ /* Fork here */
+ odp_cunit_thread_create(num, run_pool_test_create_after_fork, NULL, 0, 0);
+
+ /* Wait until thread 0 has created the test pool */
+ odp_barrier_wait(&global_mem->init_barrier);
+
+ buffer_alloc_loop(global_mem->pool, BUF_NUM, BUF_SIZE);
+
+ /* Wait for all thread endings */
+ CU_ASSERT(odp_cunit_thread_join(num) >= 0);
+
+ CU_ASSERT(!odp_pool_destroy(global_mem->pool));
+
+ CU_ASSERT(!odp_shm_free(shm));
+}
+
+static void pool_test_pool_index(void)
+{
+ uint32_t max_pools = global_pool_capa.pkt.max_pools;
+ uint32_t i, num_pools;
+ unsigned int max_index = odp_pool_max_index();
+ odp_packet_t pool_lookup[max_index + 1];
+ odp_packet_t pkt;
+ odp_pool_t pool[max_pools];
+ odp_pool_param_t param;
+ int pool_index;
+
+ CU_ASSERT_FATAL(max_pools > 0);
+
+ /* Pool max index should match to pool capability */
+ CU_ASSERT_FATAL(max_index >= global_pool_capa.max_pools - 1);
+ CU_ASSERT_FATAL(max_index >= global_pool_capa.pkt.max_pools - 1);
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num = 1;
+ param.pkt.max_num = 1;
+
+ for (i = 0; i < max_pools; i++) {
+ pool[i] = odp_pool_create(NULL, &param);
+
+ if (pool[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ /* Ensuring max possible pools are created */
+ num_pools = i;
+ CU_ASSERT(num_pools == max_pools);
+
+ for (i = 0; i < num_pools; i++) {
+ pkt = odp_packet_alloc(pool[i], PKT_LEN);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+
+ /* Only one packet should be possible from each pool */
+ CU_ASSERT_FATAL(odp_packet_alloc(pool[i], PKT_LEN) == ODP_PACKET_INVALID);
+
+ /* Check pool index validity */
+ pool_index = odp_pool_index(pool[i]);
+ CU_ASSERT_FATAL(pool_index >= 0);
+ CU_ASSERT_FATAL((unsigned int)pool_index <= odp_pool_max_index());
+
+ /* Store packet handle in pool lookup table */
+ pool_lookup[pool_index] = pkt;
+ }
+
+ for (i = 0; i < num_pools; i++) {
+ pool_index = odp_pool_index(pool[i]);
+
+ /* Free the packet using pool lookup */
+ odp_packet_free(pool_lookup[pool_index]);
+
+ /* Now packet allocation from the pool should be possible */
+ pkt = odp_packet_alloc(pool[i], PKT_LEN);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ odp_packet_free(pkt);
+
+ /* Destroy the pool */
+ CU_ASSERT(odp_pool_destroy(pool[i]) == 0);
+ }
+}
+
+static void pool_test_create_max_pkt_pools(void)
+{
+ uint32_t max_pools = global_pool_capa.pkt.max_pools;
+ uint32_t i, num_pools, num_shm;
+ odp_pool_t pool[max_pools];
+ odp_pool_param_t param;
+ odp_shm_capability_t shm_capa;
+ uint32_t shm_size = 32;
+ uint32_t uarea_size = 32;
+
+ CU_ASSERT_FATAL(max_pools > 0);
+
+ /* Reserve maximum number of SHM blocks */
+ CU_ASSERT_FATAL(odp_shm_capability(&shm_capa) == 0);
+ CU_ASSERT_FATAL(shm_capa.max_blocks > 0);
+
+ odp_shm_t shm[shm_capa.max_blocks];
+
+ if (shm_capa.max_size && shm_capa.max_size < shm_size)
+ shm_size = shm_capa.max_size;
+
+ for (i = 0; i < shm_capa.max_blocks; i++) {
+ shm[i] = odp_shm_reserve(NULL, shm_size, 0, 0);
+
+ if (shm[i] == ODP_SHM_INVALID)
+ break;
+ }
+ num_shm = i;
+ CU_ASSERT(num_shm == shm_capa.max_blocks);
+
+ /* Create maximum number of packet pools */
+ if (uarea_size > global_pool_capa.pkt.max_uarea_size)
+ uarea_size = global_pool_capa.pkt.max_uarea_size;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_PACKET;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num = 1;
+ param.pkt.max_num = 1;
+ param.pkt.uarea_size = uarea_size;
+
+ for (i = 0; i < max_pools; i++) {
+ pool[i] = odp_pool_create(NULL, &param);
+
+ if (pool[i] == ODP_POOL_INVALID)
+ break;
+ }
+ num_pools = i;
+ CU_ASSERT(num_pools == max_pools);
+
+ for (i = 0; i < num_pools; i++)
+ CU_ASSERT(odp_pool_destroy(pool[i]) == 0);
+
+ for (i = 0; i < num_shm; i++)
+ CU_ASSERT(odp_shm_free(shm[i]) == 0);
+}
+
+static int pool_check_buffer_pool_statistics(void)
+{
+ if (global_pool_capa.buf.stats.all == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_packet_pool_statistics(void)
+{
+ if (global_pool_capa.pkt.stats.all == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_packet_vector_pool_statistics(void)
+{
+ if (global_pool_capa.vector.stats.all == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pool_check_timeout_pool_statistics(void)
+{
+ if (global_pool_capa.tmo.stats.all == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pool_test_pool_statistics(odp_pool_type_t pool_type)
+{
+ odp_pool_stats_t stats;
+ odp_pool_stats_selected_t selected;
+ odp_pool_param_t param;
+ odp_pool_stats_opt_t supported;
+ uint32_t i, j, num_pool, num_obj, cache_size, num_thr;
+ uint32_t max_pools = 2;
+ uint16_t first = 0;
+ uint16_t last = ODP_POOL_MAX_THREAD_STATS - 1;
+
+ if (last > odp_thread_count_max() - 1)
+ last = odp_thread_count_max() - 1;
+
+ odp_pool_param_init(&param);
+
+ if (pool_type == ODP_POOL_BUFFER) {
+ max_pools = global_pool_capa.buf.max_pools < max_pools ?
+ global_pool_capa.buf.max_pools : max_pools;
+ num_obj = BUF_NUM;
+ supported.all = global_pool_capa.buf.stats.all;
+ param.type = ODP_POOL_BUFFER;
+ cache_size = CACHE_SIZE > global_pool_capa.buf.max_cache_size ?
+ global_pool_capa.buf.max_cache_size : CACHE_SIZE;
+ param.buf.cache_size = cache_size;
+ param.buf.size = BUF_SIZE;
+ param.buf.num = num_obj;
+ } else if (pool_type == ODP_POOL_PACKET) {
+ max_pools = global_pool_capa.pkt.max_pools < max_pools ?
+ global_pool_capa.pkt.max_pools : max_pools;
+ num_obj = PKT_NUM;
+ supported.all = global_pool_capa.pkt.stats.all;
+ param.type = ODP_POOL_PACKET;
+ cache_size = CACHE_SIZE > global_pool_capa.pkt.max_cache_size ?
+ global_pool_capa.pkt.max_cache_size : CACHE_SIZE;
+ param.pkt.cache_size = cache_size;
+ param.pkt.len = PKT_LEN;
+ param.pkt.num = num_obj;
+ param.pkt.max_num = num_obj;
+ } else if (pool_type == ODP_POOL_VECTOR) {
+ max_pools = global_pool_capa.vector.max_pools < max_pools ?
+ global_pool_capa.vector.max_pools : max_pools;
+ num_obj = VEC_NUM;
+ if (global_pool_capa.vector.max_num && global_pool_capa.vector.max_num < num_obj)
+ num_obj = global_pool_capa.vector.max_num;
+ supported.all = global_pool_capa.vector.stats.all;
+ param.type = ODP_POOL_VECTOR;
+ cache_size = CACHE_SIZE > global_pool_capa.vector.max_cache_size ?
+ global_pool_capa.vector.max_cache_size : CACHE_SIZE;
+ param.vector.cache_size = cache_size;
+ param.vector.num = num_obj;
+ param.vector.max_size = global_pool_capa.vector.max_size < VEC_LEN ?
+ global_pool_capa.vector.max_size : VEC_LEN;
+ } else {
+ max_pools = global_pool_capa.tmo.max_pools < max_pools ?
+ global_pool_capa.tmo.max_pools : max_pools;
+ num_obj = TMO_NUM;
+ supported.all = global_pool_capa.tmo.stats.all;
+ param.type = ODP_POOL_TIMEOUT;
+ cache_size = CACHE_SIZE > global_pool_capa.tmo.max_cache_size ?
+ global_pool_capa.tmo.max_cache_size : CACHE_SIZE;
+ param.tmo.cache_size = cache_size;
+ param.tmo.num = num_obj;
+ }
+
+ param.stats.all = supported.all;
+
+ CU_ASSERT_FATAL(max_pools != 0);
+
+ /* Extra alloc rounds for testing odp_pool_stats_t.alloc_fails */
+ uint32_t num_allocs = num_obj + 100;
+ odp_event_t event[max_pools][num_allocs];
+ uint32_t num_event[max_pools];
+ odp_pool_t pool[max_pools];
+
+ for (i = 0; i < max_pools; i++) {
+ pool[i] = odp_pool_create(NULL, &param);
+
+ if (pool[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ num_pool = i;
+ CU_ASSERT(num_pool == max_pools);
+
+ for (i = 0; i < num_pool; i++) {
+ uint32_t num_events = 0;
+ uint32_t num_fails = 0;
+
+ memset(&stats, 0xff, sizeof(odp_pool_stats_t));
+ memset(&selected, 0xff, sizeof(odp_pool_stats_selected_t));
+
+ CU_ASSERT_FATAL(odp_pool_stats_reset(pool[i]) == 0);
+
+ stats.thread.first = first;
+ stats.thread.last = last;
+ num_thr = last - first + 1;
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats_selected(pool[i], &selected, &supported) == 0);
+
+ CU_ASSERT(stats.available <= num_obj);
+ if (supported.bit.available)
+ CU_ASSERT(selected.available <= num_obj);
+ CU_ASSERT(stats.alloc_ops == 0);
+ if (supported.bit.alloc_ops)
+ CU_ASSERT(selected.alloc_ops == 0);
+ CU_ASSERT(stats.alloc_fails == 0);
+ if (supported.bit.alloc_fails)
+ CU_ASSERT(selected.alloc_fails == 0);
+ CU_ASSERT(stats.free_ops == 0);
+ if (supported.bit.free_ops)
+ CU_ASSERT(selected.free_ops == 0);
+ CU_ASSERT(stats.total_ops == 0);
+ if (supported.bit.total_ops)
+ CU_ASSERT(selected.total_ops == 0);
+ CU_ASSERT(stats.cache_available <= num_obj);
+ if (supported.bit.cache_available)
+ CU_ASSERT(selected.cache_available <= num_obj);
+ CU_ASSERT(stats.cache_alloc_ops == 0);
+ if (supported.bit.cache_alloc_ops)
+ CU_ASSERT(selected.cache_alloc_ops == 0);
+ CU_ASSERT(stats.cache_free_ops == 0);
+ if (supported.bit.cache_free_ops)
+ CU_ASSERT(selected.cache_free_ops == 0);
+
+ CU_ASSERT(stats.thread.first == first);
+ CU_ASSERT(stats.thread.last == last);
+
+ if (supported.bit.thread_cache_available) {
+ for (j = 0; j < num_thr; j++)
+ CU_ASSERT(stats.thread.cache_available[j] <= stats.cache_available);
+ }
+
+ /* Allocate the events */
+ for (j = 0; j < num_allocs; j++) {
+ odp_event_t new_event = ODP_EVENT_INVALID;
+ uint64_t total_cached = 0;
+ uint16_t first_id = 0;
+ uint16_t last_id = last;
+
+ if (pool_type == ODP_POOL_BUFFER) {
+ odp_buffer_t buf = odp_buffer_alloc(pool[i]);
+
+ if (buf != ODP_BUFFER_INVALID)
+ new_event = odp_buffer_to_event(buf);
+ } else if (pool_type == ODP_POOL_PACKET) {
+ odp_packet_t pkt = odp_packet_alloc(pool[i], PKT_LEN);
+
+ if (pkt != ODP_PACKET_INVALID)
+ new_event = odp_packet_to_event(pkt);
+ } else if (pool_type == ODP_POOL_VECTOR) {
+ odp_packet_vector_t pktv = odp_packet_vector_alloc(pool[i]);
+
+ if (pktv != ODP_PACKET_VECTOR_INVALID)
+ new_event = odp_packet_vector_to_event(pktv);
+ } else {
+ odp_timeout_t tmo = odp_timeout_alloc(pool[i]);
+
+ if (tmo != ODP_TIMEOUT_INVALID)
+ new_event = odp_timeout_to_event(tmo);
+ }
+
+ if (new_event != ODP_EVENT_INVALID)
+ event[i][num_events++] = new_event;
+ else
+ num_fails++;
+
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats_selected(pool[i], &selected,
+ &supported) == 0);
+ CU_ASSERT(stats.available <= num_obj - num_events);
+ if (supported.bit.available)
+ CU_ASSERT(selected.available <= num_obj - num_events);
+ CU_ASSERT(stats.cache_available <= num_obj - num_events);
+ if (supported.bit.cache_available)
+ CU_ASSERT(selected.cache_available <= num_obj - num_events);
+
+ if (supported.bit.thread_cache_available) {
+ while (first_id < odp_thread_count_max()) {
+ memset(&stats, 0xff, sizeof(odp_pool_stats_t));
+
+ stats.thread.first = first_id;
+ stats.thread.last = last_id;
+ num_thr = last_id - first_id + 1;
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+
+ for (uint32_t k = 0; k < num_thr; k++) {
+ uint64_t cached = stats.thread.cache_available[k];
+
+ CU_ASSERT(cached <= num_obj - num_events);
+ total_cached += cached;
+ }
+ first_id = last_id + 1;
+ last_id += ODP_POOL_MAX_THREAD_STATS;
+ if (last_id >= odp_thread_count_max())
+ last_id = odp_thread_count_max() - 1;
+ };
+
+ if (supported.bit.cache_available &&
+ ODP_POOL_MAX_THREAD_STATS >= odp_thread_count_max())
+ CU_ASSERT(stats.cache_available == total_cached);
+ }
+ }
+
+ CU_ASSERT(num_events == num_obj);
+ num_event[i] = num_events;
+
+ /* Allow implementation some time to update counters */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+
+ memset(&stats, 0xff, sizeof(odp_pool_stats_t));
+ memset(&selected, 0xff, sizeof(odp_pool_stats_selected_t));
+
+ stats.thread.first = first;
+ stats.thread.last = last;
+ num_thr = last - first + 1;
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats_selected(pool[i], &selected, &supported) == 0);
+
+ /* All events are allocated, available count in pool and pool
+ * local caches should be zero. */
+ CU_ASSERT(stats.available == 0);
+ if (supported.bit.available)
+ CU_ASSERT(selected.available == 0);
+ CU_ASSERT(stats.cache_available == 0);
+ if (supported.bit.cache_available)
+ CU_ASSERT(selected.cache_available == 0);
+ if (supported.bit.thread_cache_available) {
+ for (j = 0; j < num_thr; j++)
+ CU_ASSERT(stats.thread.cache_available[j] == 0);
+ }
+ if (supported.bit.alloc_ops) {
+ CU_ASSERT(stats.alloc_ops > 0 && stats.alloc_ops <= num_allocs);
+ CU_ASSERT(selected.alloc_ops > 0 && selected.alloc_ops <= num_allocs);
+ }
+ if (supported.bit.alloc_fails) {
+ CU_ASSERT(stats.alloc_fails == num_fails);
+ CU_ASSERT(selected.alloc_fails == num_fails);
+ }
+ if (supported.bit.total_ops) {
+ CU_ASSERT(stats.total_ops > 0 && stats.total_ops <= num_allocs);
+ CU_ASSERT(selected.total_ops > 0 && selected.total_ops <= num_allocs);
+ }
+ CU_ASSERT(stats.free_ops == 0);
+ if (supported.bit.free_ops)
+ CU_ASSERT(selected.free_ops == 0);
+ CU_ASSERT(stats.cache_alloc_ops <= num_allocs);
+ if (supported.bit.cache_alloc_ops)
+ CU_ASSERT(selected.cache_alloc_ops <= num_allocs);
+ CU_ASSERT(stats.cache_free_ops == 0);
+ if (supported.bit.cache_free_ops)
+ CU_ASSERT(selected.cache_free_ops == 0);
+ }
+
+ for (i = 0; i < num_pool; i++) {
+ odp_event_free_multi(event[i], num_event[i]);
+
+ /* Allow implementation some time to update counters */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+
+ stats.thread.first = odp_thread_id();
+ stats.thread.last = odp_thread_id();
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats_selected(pool[i], &selected, &supported) == 0);
+
+ if (supported.bit.available && supported.bit.cache_available) {
+ CU_ASSERT(stats.available + stats.cache_available == num_obj);
+ CU_ASSERT(selected.available + selected.cache_available == num_obj);
+ }
+ if (supported.bit.free_ops) {
+ CU_ASSERT(stats.free_ops > 0);
+ CU_ASSERT(selected.free_ops > 0);
+ }
+ if (supported.bit.total_ops) {
+ CU_ASSERT(stats.total_ops > 0);
+ CU_ASSERT(selected.total_ops > 0);
+ }
+
+ if (i == 0) {
+ printf("\nPool Statistics\n---------------\n");
+ printf(" available: %" PRIu64 "\n", stats.available);
+ printf(" alloc_ops: %" PRIu64 "\n", stats.alloc_ops);
+ printf(" alloc_fails: %" PRIu64 "\n", stats.alloc_fails);
+ printf(" free_ops: %" PRIu64 "\n", stats.free_ops);
+ printf(" total_ops: %" PRIu64 "\n", stats.total_ops);
+ printf(" cache_available: %" PRIu64 "\n", stats.cache_available);
+ printf(" cache_alloc_ops: %" PRIu64 "\n", stats.cache_alloc_ops);
+ printf(" cache_free_ops: %" PRIu64 "\n", stats.cache_free_ops);
+ if (supported.bit.thread_cache_available)
+ printf(" thread.cache_available[0]: %" PRIu64 "\n",
+ stats.thread.cache_available[0]);
+ }
+
+ CU_ASSERT_FATAL(odp_pool_stats_reset(pool[i]) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats(pool[i], &stats) == 0);
+ CU_ASSERT_FATAL(odp_pool_stats_selected(pool[i], &selected, &supported) == 0);
+
+ CU_ASSERT(stats.alloc_ops == 0);
+ if (supported.bit.alloc_ops)
+ CU_ASSERT(selected.alloc_ops == 0);
+ CU_ASSERT(stats.alloc_fails == 0);
+ if (supported.bit.alloc_fails)
+ CU_ASSERT(selected.alloc_fails == 0);
+ CU_ASSERT(stats.free_ops == 0);
+ if (supported.bit.free_ops)
+ CU_ASSERT(selected.free_ops == 0);
+ CU_ASSERT(stats.total_ops == 0);
+ if (supported.bit.total_ops)
+ CU_ASSERT(selected.total_ops == 0);
+ CU_ASSERT(stats.cache_alloc_ops == 0);
+ if (supported.bit.cache_alloc_ops)
+ CU_ASSERT(selected.cache_alloc_ops == 0);
+ CU_ASSERT(stats.cache_free_ops == 0);
+ if (supported.bit.cache_free_ops)
+ CU_ASSERT(selected.cache_free_ops == 0);
+
+ CU_ASSERT(odp_pool_destroy(pool[i]) == 0);
+ }
+}
+
+static void pool_test_buffer_pool_statistics(void)
+{
+ pool_test_pool_statistics(ODP_POOL_BUFFER);
+}
+
+static void pool_test_packet_pool_statistics(void)
+{
+ pool_test_pool_statistics(ODP_POOL_PACKET);
+}
+
+static void pool_test_packet_vector_pool_statistics(void)
+{
+ pool_test_pool_statistics(ODP_POOL_VECTOR);
+}
+
+static void pool_test_timeout_pool_statistics(void)
+{
+ pool_test_pool_statistics(ODP_POOL_TIMEOUT);
+}
+
+static void pool_ext_init_packet_pool_param(odp_pool_ext_param_t *param)
+{
+ odp_pool_ext_capability_t capa;
+ uint32_t head_offset, head_align, trailer_size;
+ odp_pool_type_t type = ODP_POOL_PACKET;
+ uint32_t num_buf = EXT_NUM_BUF;
+ uint32_t buf_size = EXT_BUF_SIZE;
+ uint32_t uarea_size = EXT_UAREA_SIZE;
+ uint32_t headroom = EXT_HEADROOM;
+ uint32_t app_hdr_size = EXT_APP_HDR_SIZE;
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(type, &capa) == 0);
+
+ odp_pool_ext_param_init(type, param);
+
+ if (num_buf > capa.pkt.max_num_buf)
+ num_buf = capa.pkt.max_num_buf;
+
+ if (buf_size > capa.pkt.max_buf_size)
+ buf_size = capa.pkt.max_buf_size;
+
+ if (uarea_size > capa.pkt.max_uarea_size)
+ uarea_size = capa.pkt.max_uarea_size;
+
+ if (headroom > capa.pkt.max_headroom)
+ headroom = capa.pkt.max_headroom;
+
+ head_align = capa.pkt.min_head_align;
+ head_offset = capa.pkt.odp_header_size + app_hdr_size;
+ trailer_size = capa.pkt.odp_trailer_size;
+
+ CU_ASSERT_FATAL(head_offset < buf_size);
+ CU_ASSERT_FATAL((head_offset + trailer_size) < buf_size);
+
+ while (head_offset % head_align) {
+ app_hdr_size++;
+ head_offset = capa.pkt.odp_header_size + app_hdr_size;
+
+ if (head_offset >= buf_size) {
+ ODPH_ERR("Head align too large (%u). No room for data.\n", head_align);
+ break;
+ }
+ }
+
+ CU_ASSERT_FATAL(head_offset < buf_size);
+ CU_ASSERT_FATAL((head_offset + trailer_size) < buf_size);
+ CU_ASSERT_FATAL((head_offset % head_align) == 0);
+
+ param->pkt.num_buf = num_buf;
+ param->pkt.buf_size = buf_size;
+ param->pkt.app_header_size = app_hdr_size;
+ param->pkt.uarea_size = uarea_size;
+ param->pkt.headroom = headroom;
+}
+
+static void test_packet_pool_ext_capa(void)
+{
+ odp_pool_ext_capability_t capa;
+ odp_pool_type_t type;
+ const odp_pool_type_t unsupported_types[] = {ODP_POOL_BUFFER, ODP_POOL_TIMEOUT,
+ ODP_POOL_VECTOR, ODP_POOL_DMA_COMPL,
+ ODP_POOL_ML_COMPL};
+ const int num_types = ODPH_ARRAY_SIZE(unsupported_types);
+
+ /* Verify operation for unsupported pool types */
+ for (int i = 0; i < num_types; i++) {
+ type = unsupported_types[i];
+ CU_ASSERT_FATAL(odp_pool_ext_capability(type, &capa) == 0);
+ CU_ASSERT(capa.max_pools == 0);
+ }
+
+ type = ODP_POOL_PACKET;
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(type, &capa) == 0);
+
+ CU_ASSERT(capa.type == type);
+
+ /* External memory pools not supported */
+ if (capa.max_pools == 0)
+ return;
+
+ CU_ASSERT(capa.max_pools > 0);
+ CU_ASSERT(capa.min_cache_size <= capa.max_cache_size);
+ CU_ASSERT(capa.pkt.max_num_buf > 0);
+ CU_ASSERT(capa.pkt.max_buf_size > 0);
+ CU_ASSERT(capa.pkt.min_mem_align > 0);
+ CU_ASSERT(TEST_CHECK_POW2(capa.pkt.min_mem_align));
+ CU_ASSERT(capa.pkt.min_buf_align > 0);
+ CU_ASSERT(capa.pkt.min_head_align > 0);
+ CU_ASSERT(capa.pkt.max_headroom > 0);
+ CU_ASSERT(capa.pkt.max_headroom_size > 0);
+ CU_ASSERT(capa.pkt.max_headroom_size >= capa.pkt.max_headroom);
+ CU_ASSERT(capa.pkt.max_segs_per_pkt > 0);
+}
+
+static void test_ext_param_init(uint8_t fill)
+{
+ odp_pool_ext_param_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_pool_ext_param_init(ODP_POOL_PACKET, &param);
+
+ CU_ASSERT(param.type == ODP_POOL_PACKET);
+ CU_ASSERT(param.uarea_init.init_fn == NULL);
+ CU_ASSERT(param.uarea_init.args == NULL);
+ CU_ASSERT(param.cache_size >= global_pool_ext_capa.min_cache_size &&
+ param.cache_size <= global_pool_ext_capa.max_cache_size);
+ CU_ASSERT(param.stats.all == 0);
+ CU_ASSERT(param.pkt.app_header_size == 0);
+ CU_ASSERT(param.pkt.uarea_size == 0);
+}
+
+static void test_packet_pool_ext_param_init(void)
+{
+ test_ext_param_init(0);
+ test_ext_param_init(0xff);
+}
+
+static void test_packet_pool_ext_create(void)
+{
+ odp_pool_t pool;
+ odp_pool_ext_param_t param;
+
+ pool_ext_init_packet_pool_param(&param);
+
+ pool = odp_pool_ext_create("pool_ext_0", &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void test_packet_pool_ext_lookup(void)
+{
+ odp_pool_t pool, pool_1;
+ odp_pool_ext_param_t param;
+ const char *name = "pool_ext_0";
+
+ pool_ext_init_packet_pool_param(&param);
+
+ pool = odp_pool_ext_create(name, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ pool_1 = odp_pool_lookup(name);
+
+ CU_ASSERT_FATAL(pool_1 != ODP_POOL_INVALID);
+ CU_ASSERT(pool == pool_1);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void test_packet_pool_ext_info(void)
+{
+ odp_pool_t pool;
+ odp_pool_ext_param_t param;
+ odp_pool_info_t info;
+ const char *name = "pool_ext_0";
+
+ pool_ext_init_packet_pool_param(&param);
+
+ pool = odp_pool_ext_create(name, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ memset(&info, 0, sizeof(odp_pool_info_t));
+ CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0);
+
+ CU_ASSERT(info.pool_ext);
+ CU_ASSERT(strncmp(name, info.name, strlen(name)) == 0);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static odp_shm_t populate_pool(odp_pool_t pool, odp_pool_ext_capability_t *capa,
+ void *buf[], uint32_t num, uint32_t buf_size)
+{
+ odp_shm_t shm;
+ uint8_t *buf_ptr;
+ uint32_t i;
+ uint32_t shm_size, mem_align;
+ uint32_t flags = 0;
+ uint32_t buf_align = EXT_BUF_ALIGN;
+ uint32_t min_align = capa->pkt.min_buf_align;
+
+ CU_ASSERT_FATAL(min_align > 0);
+
+ if (min_align > buf_align)
+ buf_align = min_align;
+
+ if (capa->pkt.buf_size_aligned) {
+ buf_align = buf_size;
+ CU_ASSERT_FATAL((buf_size % min_align) == 0);
+ }
+
+ mem_align = buf_align;
+ if (capa->pkt.min_mem_align > mem_align)
+ mem_align = capa->pkt.min_mem_align;
+
+ /* Prepare to align every buffer */
+ shm_size = (num + 1) * (buf_size + buf_align);
+
+ shm = odp_shm_reserve("test_pool_ext_populate", shm_size, mem_align, 0);
+ if (shm == ODP_SHM_INVALID)
+ return ODP_SHM_INVALID;
+
+ buf_ptr = odp_shm_addr(shm);
+ CU_ASSERT_FATAL((uintptr_t)buf_ptr % mem_align == 0);
+
+ /* initialize entire pool memory with a pattern */
+ memset(buf_ptr, MAGIC_U8, shm_size);
+
+ /* Move from mem_align to buf_align */
+ while ((uintptr_t)buf_ptr % buf_align)
+ buf_ptr++;
+
+ for (i = 0; i < num; i++) {
+ if (i == num - 1)
+ flags = ODP_POOL_POPULATE_DONE;
+
+ buf[i] = buf_ptr;
+ CU_ASSERT_FATAL(odp_pool_ext_populate(pool, &buf[i], buf_size, 1, flags) == 0);
+
+ buf_ptr += buf_size;
+ while ((uintptr_t)buf_ptr % buf_align)
+ buf_ptr++;
+ }
+
+ return shm;
+}
+
+static void test_packet_pool_ext_populate(void)
+{
+ odp_shm_t shm;
+ odp_pool_t pool;
+ odp_pool_ext_param_t param;
+ odp_pool_ext_capability_t capa;
+ uint32_t buf_size, num_buf;
+ void *buf[EXT_NUM_BUF];
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(ODP_POOL_PACKET, &capa) == 0);
+
+ pool_ext_init_packet_pool_param(&param);
+ num_buf = param.pkt.num_buf;
+ buf_size = param.pkt.buf_size;
+
+ CU_ASSERT_FATAL(capa.pkt.min_head_align > 0);
+
+ pool = odp_pool_ext_create("pool_ext_0", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = populate_pool(pool, &capa, buf, num_buf, buf_size);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static uint32_t find_buf(odp_packet_t pkt, void *buf[], uint32_t num, uint32_t head_offset)
+{
+ uint32_t i;
+ uint8_t *ptr;
+ uint8_t *head = odp_packet_head(pkt);
+
+ for (i = 0; i < num; i++) {
+ ptr = buf[i];
+ ptr += head_offset;
+
+ if (head == ptr)
+ break;
+ }
+
+ return i;
+}
+
+#define PKT_LEN_NORMAL 0
+#define PKT_LEN_MAX 1
+#define PKT_LEN_SEGMENTED 2
+
+static void packet_pool_ext_alloc(int len_test)
+{
+ odp_shm_t shm;
+ odp_pool_t pool;
+ odp_pool_ext_param_t param;
+ odp_pool_ext_capability_t capa;
+ uint32_t i, j, buf_size, num_buf, num_pkt, num_alloc, buf_index;
+ uint32_t pkt_len, head_offset, trailer_size, headroom, max_headroom;
+ uint32_t hr, tr, uarea_size, max_payload, buf_data_size, app_hdr_size;
+ int num_seg;
+ uint8_t *app_hdr;
+ void *buf[EXT_NUM_BUF];
+ odp_packet_t pkt[EXT_NUM_BUF];
+ uint32_t seg_len = 0;
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(ODP_POOL_PACKET, &capa) == 0);
+
+ pool_ext_init_packet_pool_param(&param);
+ num_buf = param.pkt.num_buf;
+ buf_size = param.pkt.buf_size;
+ uarea_size = param.pkt.uarea_size;
+
+ pool = odp_pool_ext_create("pool_ext_0", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = populate_pool(pool, &capa, buf, num_buf, buf_size);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ app_hdr_size = param.pkt.app_header_size;
+ head_offset = capa.pkt.odp_header_size + app_hdr_size;
+ max_headroom = capa.pkt.max_headroom_size;
+ headroom = param.pkt.headroom;
+ trailer_size = capa.pkt.odp_trailer_size;
+ buf_data_size = buf_size - head_offset - trailer_size;
+ max_payload = buf_data_size - max_headroom;
+ num_pkt = num_buf;
+ num_seg = 1;
+
+ if (len_test == PKT_LEN_NORMAL) {
+ pkt_len = (buf_data_size - headroom) / 2;
+ } else if (len_test == PKT_LEN_MAX) {
+ pkt_len = max_payload;
+ } else {
+ CU_ASSERT_FATAL(capa.pkt.max_segs_per_pkt > 1);
+ /* length that results 2 segments */
+ pkt_len = max_payload + (buf_size / 2);
+ num_seg = 2;
+ num_pkt = num_buf / num_seg;
+ }
+
+ for (i = 0; i < num_pkt; i++) {
+ pkt[i] = odp_packet_alloc(pool, pkt_len);
+ CU_ASSERT(pkt[i] != ODP_PACKET_INVALID);
+ if (pkt[i] == ODP_PACKET_INVALID)
+ break;
+
+ CU_ASSERT(odp_packet_is_valid(pkt[i]) == 1);
+ CU_ASSERT(odp_event_is_valid(odp_packet_to_event(pkt[i])) == 1);
+ CU_ASSERT(odp_packet_len(pkt[i]) == pkt_len);
+ CU_ASSERT(odp_packet_headroom(pkt[i]) >= headroom);
+ buf_index = find_buf(pkt[i], buf, num_buf, head_offset);
+ CU_ASSERT(buf_index < num_buf);
+ hr = (uintptr_t)odp_packet_data(pkt[i]) - (uintptr_t)odp_packet_head(pkt[i]);
+ CU_ASSERT(hr == odp_packet_headroom(pkt[i]));
+ CU_ASSERT(num_seg == odp_packet_num_segs(pkt[i]));
+ CU_ASSERT(odp_packet_data(pkt[i]) == odp_packet_data_seg_len(pkt[i], &seg_len));
+ CU_ASSERT(odp_packet_seg_len(pkt[i]) == seg_len);
+
+ if (num_seg == 1) {
+ tr = buf_data_size - hr - pkt_len;
+ CU_ASSERT(tr == odp_packet_tailroom(pkt[i]));
+ CU_ASSERT(odp_packet_seg_len(pkt[i]) == pkt_len);
+ } else {
+ odp_packet_seg_t seg = odp_packet_last_seg(pkt[i]);
+ uint32_t last_seg_len = odp_packet_seg_data_len(pkt[i], seg);
+ uint32_t max_tr = buf_data_size - last_seg_len;
+
+ CU_ASSERT(odp_packet_tailroom(pkt[i]) <= max_tr);
+ CU_ASSERT(pkt_len == (odp_packet_seg_len(pkt[i]) + last_seg_len));
+ }
+
+ CU_ASSERT(odp_packet_buf_len(pkt[i]) == num_seg * buf_data_size);
+
+ if (uarea_size) {
+ CU_ASSERT(odp_packet_user_area(pkt[i]) != NULL);
+ CU_ASSERT(odp_packet_user_area_size(pkt[i]) >= uarea_size);
+ }
+
+ /* Check that application header content has not changed */
+ app_hdr = (uint8_t *)odp_packet_head(pkt[i]) - app_hdr_size;
+ for (j = 0; j < app_hdr_size; j++)
+ CU_ASSERT(app_hdr[j] == MAGIC_U8);
+ }
+
+ num_alloc = i;
+ CU_ASSERT(num_alloc == num_pkt);
+
+ /* Pool is now empty */
+ CU_ASSERT(odp_packet_alloc(pool, pkt_len) == ODP_PACKET_INVALID);
+
+ for (i = 0; i < num_alloc; i++)
+ odp_packet_free(pkt[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static void test_packet_pool_ext_alloc(void)
+{
+ packet_pool_ext_alloc(PKT_LEN_NORMAL);
+}
+
+static void test_packet_pool_ext_uarea_init(void)
+{
+ odp_pool_ext_capability_t capa;
+ odp_pool_ext_param_t param;
+ uint32_t num = ELEM_NUM, i;
+ uint32_t max_payload;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_shm_t shm;
+ uint8_t *uarea;
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(ODP_POOL_PACKET, &capa) == 0);
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ pool_ext_init_packet_pool_param(&param);
+ param.uarea_init.init_fn = init_event_uarea;
+ param.uarea_init.args = &data;
+ num = ODPH_MIN(num, param.pkt.num_buf);
+ param.pkt.num_buf = num;
+ param.pkt.uarea_size = 1;
+ pool = odp_pool_ext_create(NULL, &param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ void *buf[num];
+ odp_packet_t pkts[num];
+
+ shm = populate_pool(pool, &capa, buf, num, param.pkt.buf_size);
+
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ CU_ASSERT(data.count == num);
+
+ max_payload = param.pkt.buf_size;
+ max_payload -= capa.pkt.odp_header_size + param.pkt.app_header_size;
+ max_payload -= capa.pkt.max_headroom_size;
+ max_payload -= capa.pkt.odp_trailer_size;
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ pkts[i] = odp_packet_alloc(pool, max_payload);
+
+ CU_ASSERT(pkts[i] != ODP_PACKET_INVALID);
+
+ if (pkts[i] == ODP_PACKET_INVALID)
+ break;
+
+ uarea = odp_packet_user_area(pkts[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ odp_packet_free_multi(pkts, i);
+ odp_pool_destroy(pool);
+ odp_shm_free(shm);
+}
+
+static void test_packet_pool_ext_alloc_max(void)
+{
+ packet_pool_ext_alloc(PKT_LEN_MAX);
+}
+
+static void test_packet_pool_ext_alloc_seg(void)
+{
+ packet_pool_ext_alloc(PKT_LEN_SEGMENTED);
+}
+
+static void test_packet_pool_ext_disassemble(void)
+{
+ odp_shm_t shm;
+ odp_pool_t pool;
+ odp_pool_ext_param_t param;
+ odp_pool_ext_capability_t capa;
+ uint32_t i, j, buf_size, num_buf, num_pkt, num_alloc, buf_index;
+ uint32_t pkt_len, head_offset, trailer_size, headroom, max_headroom;
+ uint32_t hr, max_payload, buf_data_size;
+ uint32_t num_seg;
+ void *buf[EXT_NUM_BUF];
+ odp_packet_t pkt_tbl[EXT_NUM_BUF];
+
+ CU_ASSERT_FATAL(odp_pool_ext_capability(ODP_POOL_PACKET, &capa) == 0);
+ CU_ASSERT_FATAL(capa.pkt.max_segs_per_pkt > 1);
+
+ pool_ext_init_packet_pool_param(&param);
+ num_buf = param.pkt.num_buf;
+ buf_size = param.pkt.buf_size;
+
+ pool = odp_pool_ext_create("pool_ext_0", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ shm = populate_pool(pool, &capa, buf, num_buf, buf_size);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ head_offset = capa.pkt.odp_header_size + param.pkt.app_header_size;
+ max_headroom = capa.pkt.max_headroom_size;
+ headroom = param.pkt.headroom;
+ trailer_size = capa.pkt.odp_trailer_size;
+ buf_data_size = buf_size - head_offset - trailer_size;
+ max_payload = buf_data_size - max_headroom;
+
+ /* length that results 2 segments */
+ pkt_len = max_payload + (buf_size / 2);
+ num_seg = 2;
+ num_pkt = num_buf / num_seg;
+
+ for (i = 0; i < num_pkt; i++) {
+ odp_packet_t pkt;
+ odp_packet_seg_t seg;
+ uint32_t num_pkt_buf, data_offset, data_len;
+ void *head, *data, *pkt_head;
+ odp_packet_buf_t pkt_buf[num_seg];
+ void *seg_data[num_seg];
+ uint32_t seg_len[num_seg];
+
+ pkt = odp_packet_alloc(pool, pkt_len);
+ pkt_tbl[i] = pkt;
+ CU_ASSERT(pkt != ODP_PACKET_INVALID);
+ if (pkt == ODP_PACKET_INVALID)
+ break;
+
+ CU_ASSERT(odp_packet_len(pkt) == pkt_len);
+ CU_ASSERT(odp_packet_headroom(pkt) >= headroom);
+ buf_index = find_buf(pkt, buf, num_buf, head_offset);
+ CU_ASSERT(buf_index < num_buf);
+ pkt_head = odp_packet_head(pkt);
+ hr = (uintptr_t)odp_packet_data(pkt) - (uintptr_t)pkt_head;
+ CU_ASSERT(hr == odp_packet_headroom(pkt));
+ CU_ASSERT((int)num_seg == odp_packet_num_segs(pkt));
+
+ seg = odp_packet_first_seg(pkt);
+ for (j = 0; j < num_seg; j++) {
+ seg_data[j] = odp_packet_seg_data(pkt, seg);
+ seg_len[j] = odp_packet_seg_data_len(pkt, seg);
+ seg = odp_packet_next_seg(pkt, seg);
+ }
+
+ CU_ASSERT(odp_packet_data(pkt) == seg_data[0]);
+ CU_ASSERT(odp_packet_seg_len(pkt) == seg_len[0])
+
+ /* Disassemble packet */
+ num_pkt_buf = odp_packet_disassemble(pkt, pkt_buf, num_seg);
+ CU_ASSERT_FATAL(num_pkt_buf == num_seg);
+
+ CU_ASSERT(odp_packet_buf_head(pkt_buf[0]) == pkt_head);
+ CU_ASSERT(odp_packet_buf_data_offset(pkt_buf[0]) == hr);
+
+ for (j = 0; j < num_seg; j++) {
+ CU_ASSERT(odp_packet_buf_size(pkt_buf[j]) == buf_data_size);
+
+ head = odp_packet_buf_head(pkt_buf[j]);
+ data_offset = odp_packet_buf_data_offset(pkt_buf[j]);
+ data = (uint8_t *)head + data_offset;
+ CU_ASSERT(seg_data[j] == data);
+ data_len = odp_packet_buf_data_len(pkt_buf[j]);
+ CU_ASSERT(seg_len[j] == data_len);
+
+ CU_ASSERT(odp_packet_buf_from_head(pool, head) == pkt_buf[j]);
+
+ /* Pull in head and tail by one byte */
+ odp_packet_buf_data_set(pkt_buf[j], data_offset + 1, data_len - 2);
+ CU_ASSERT(odp_packet_buf_data_offset(pkt_buf[j]) == data_offset + 1);
+ CU_ASSERT(odp_packet_buf_data_len(pkt_buf[j]) == data_len - 2);
+ }
+
+ /* Reassemble packet, each segment is now 2 bytes shorter */
+ pkt = odp_packet_reassemble(pool, pkt_buf, num_seg);
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_num_segs(pkt) == (int)num_seg);
+ pkt_tbl[i] = pkt;
+
+ CU_ASSERT(odp_packet_len(pkt) == (pkt_len - (num_seg * 2)));
+ }
+
+ num_alloc = i;
+ CU_ASSERT(num_alloc == num_pkt);
+
+ /* Pool is now empty */
+ CU_ASSERT(odp_packet_alloc(pool, pkt_len) == ODP_PACKET_INVALID);
+
+ for (i = 0; i < num_alloc; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int pool_suite_init(void)
+{
+ memset(&global_pool_capa, 0, sizeof(odp_pool_capability_t));
+ memset(&default_pool_param, 0, sizeof(odp_pool_param_t));
+
+ if (odp_pool_capability(&global_pool_capa) < 0) {
+ ODPH_ERR("odp_pool_capability() failed in suite init\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&default_pool_param);
+
+ return 0;
+}
+
+static int pool_ext_suite_init(void)
+{
+ memset(&global_pool_ext_capa, 0, sizeof(odp_pool_ext_capability_t));
+
+ if (odp_pool_ext_capability(ODP_POOL_PACKET, &global_pool_ext_capa)) {
+ ODPH_ERR("Pool ext capa failed in suite init\n");
+ return -1;
+ }
+
+ if (global_pool_ext_capa.type != ODP_POOL_PACKET) {
+ ODPH_ERR("Bad type from pool ext capa in suite init\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_pool_ext_support(void)
+{
+ if (global_pool_ext_capa.max_pools == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_pool_ext_uarea_init_support(void)
+{
+ if (global_pool_ext_capa.max_pools == 0 || !global_pool_ext_capa.pkt.uarea_persistence ||
+ global_pool_ext_capa.pkt.max_uarea_size == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_pool_ext_segment_support(void)
+{
+ if (global_pool_ext_capa.max_pools == 0 || global_pool_ext_capa.pkt.max_segs_per_pkt < 2)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+odp_testinfo_t pool_suite[] = {
+ ODP_TEST_INFO(pool_test_param_init),
+ ODP_TEST_INFO(pool_test_create_destroy_buffer),
+ ODP_TEST_INFO(pool_test_create_destroy_packet),
+ ODP_TEST_INFO(pool_test_create_destroy_timeout),
+ ODP_TEST_INFO(pool_test_create_destroy_vector),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_buffer_uarea_init, pool_check_buffer_uarea_init),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_packet_uarea_init, pool_check_packet_uarea_init),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_vector_uarea_init, pool_check_vector_uarea_init),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_timeout_uarea_init, pool_check_timeout_uarea_init),
+ ODP_TEST_INFO(pool_test_lookup_info_print),
+ ODP_TEST_INFO(pool_test_same_name_buf),
+ ODP_TEST_INFO(pool_test_same_name_pkt),
+ ODP_TEST_INFO(pool_test_same_name_tmo),
+ ODP_TEST_INFO(pool_test_same_name_vec),
+ ODP_TEST_INFO(pool_test_alloc_buffer),
+ ODP_TEST_INFO(pool_test_alloc_buffer_min_cache),
+ ODP_TEST_INFO(pool_test_alloc_buffer_max_cache),
+ ODP_TEST_INFO(pool_test_alloc_packet_vector),
+ ODP_TEST_INFO(pool_test_alloc_packet_vector_min_cache),
+ ODP_TEST_INFO(pool_test_alloc_packet_vector_max_cache),
+ ODP_TEST_INFO(pool_test_alloc_packet),
+ ODP_TEST_INFO(pool_test_alloc_packet_min_cache),
+ ODP_TEST_INFO(pool_test_alloc_packet_max_cache),
+ ODP_TEST_INFO(pool_test_alloc_packet_subparam),
+ ODP_TEST_INFO(pool_test_alloc_timeout),
+ ODP_TEST_INFO(pool_test_alloc_timeout_min_cache),
+ ODP_TEST_INFO(pool_test_alloc_timeout_max_cache),
+ ODP_TEST_INFO(pool_test_info_packet),
+ ODP_TEST_INFO(pool_test_info_data_range),
+ ODP_TEST_INFO(pool_test_buf_max_num),
+ ODP_TEST_INFO(pool_test_pkt_max_num),
+ ODP_TEST_INFO(pool_test_packet_vector_max_num),
+ ODP_TEST_INFO(pool_test_pkt_seg_len),
+ ODP_TEST_INFO(pool_test_tmo_max_num),
+ ODP_TEST_INFO(pool_test_create_after_fork),
+ ODP_TEST_INFO(pool_test_pool_index),
+ ODP_TEST_INFO(pool_test_create_max_pkt_pools),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_buffer_pool_statistics,
+ pool_check_buffer_pool_statistics),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_packet_pool_statistics,
+ pool_check_packet_pool_statistics),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_packet_vector_pool_statistics,
+ pool_check_packet_vector_pool_statistics),
+ ODP_TEST_INFO_CONDITIONAL(pool_test_timeout_pool_statistics,
+ pool_check_timeout_pool_statistics),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_testinfo_t pool_ext_suite[] = {
+ ODP_TEST_INFO(test_packet_pool_ext_capa),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_param_init, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_create, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_lookup, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_info, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_populate, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_alloc, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_uarea_init,
+ check_pool_ext_uarea_init_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_alloc_max, check_pool_ext_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_alloc_seg, check_pool_ext_segment_support),
+ ODP_TEST_INFO_CONDITIONAL(test_packet_pool_ext_disassemble, check_pool_ext_segment_support),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t pool_suites[] = {
+ { .name = "Pool tests",
+ .testinfo_tbl = pool_suite,
+ .init_func = pool_suite_init,
+ },
+ { .name = "Ext mem pool tests",
+ .testinfo_tbl = pool_ext_suite,
+ .init_func = pool_ext_suite_init,
+ },
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(pool_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/queue/.gitignore b/test/validation/api/queue/.gitignore
index 469506a13..469506a13 100644
--- a/test/common_plat/validation/api/queue/.gitignore
+++ b/test/validation/api/queue/.gitignore
diff --git a/test/validation/api/queue/Makefile.am b/test/validation/api/queue/Makefile.am
new file mode 100644
index 000000000..94a6b28a9
--- /dev/null
+++ b/test/validation/api/queue/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = queue_main
+queue_main_SOURCES = queue.c
diff --git a/test/validation/api/queue/queue.c b/test/validation/api/queue/queue.c
new file mode 100644
index 000000000..b3778d085
--- /dev/null
+++ b/test/validation/api/queue/queue.c
@@ -0,0 +1,1174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2021-2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+
+#define MAX_WORKERS 32
+#define BURST_SIZE (8)
+#define MAX_NUM_EVENT (1 * 1024)
+#define MAX_ITERATION (100)
+#define MAX_QUEUES (64 * 1024)
+#define GLOBALS_NAME "queue_test_globals"
+#define DEQ_RETRIES 100
+#define ENQ_RETRIES 100
+
+typedef struct {
+ int num_workers;
+ odp_barrier_t barrier;
+ odp_queue_t queue;
+ odp_atomic_u32_t num_event;
+
+ struct {
+ odp_queue_t queue_a;
+ odp_queue_t queue_b;
+ int passed_a;
+ int passed_b;
+ int burst;
+ odp_pool_t pool;
+ odp_barrier_t barrier;
+ odp_atomic_u32_t counter;
+ } pair;
+
+ struct {
+ uint32_t num_event;
+ } thread[ODP_THREAD_COUNT_MAX];
+
+} test_globals_t;
+
+static int queue_context = 0xff;
+static odp_pool_t pool;
+
+static void generate_name(char *name, uint32_t index)
+{
+ /* Uniqueue name for up to 300M queues */
+ name[0] = 'A' + ((index / (26 * 26 * 26 * 26 * 26)) % 26);
+ name[1] = 'A' + ((index / (26 * 26 * 26 * 26)) % 26);
+ name[2] = 'A' + ((index / (26 * 26 * 26)) % 26);
+ name[3] = 'A' + ((index / (26 * 26)) % 26);
+ name[4] = 'A' + ((index / 26) % 26);
+ name[5] = 'A' + (index % 26);
+}
+
+static int queue_suite_init(void)
+{
+ odp_shm_t shm;
+ test_globals_t *globals;
+ odp_pool_param_t params;
+ int num_workers;
+
+ shm = odp_shm_reserve(GLOBALS_NAME, sizeof(test_globals_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed\n");
+ return -1;
+ }
+
+ globals = odp_shm_addr(shm);
+ memset(globals, 0, sizeof(test_globals_t));
+
+ num_workers = odp_cpumask_default_worker(NULL, 0);
+
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ globals->num_workers = num_workers;
+ odp_barrier_init(&globals->barrier, num_workers);
+
+ odp_pool_param_init(&params);
+
+ params.buf.size = 4;
+ /* Allocate enough buffers taking into consideration core starvation
+ * due to caching */
+ params.buf.num = MAX_NUM_EVENT + params.buf.cache_size;
+ params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("msg_pool", &params);
+
+ if (ODP_POOL_INVALID == pool) {
+ ODPH_ERR("Pool create failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int queue_suite_term(void)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBALS_NAME);
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("SHM lookup failed\n");
+ return -1;
+ }
+
+ if (odp_shm_free(shm)) {
+ ODPH_ERR("SHM free failed\n");
+ return -1;
+ }
+
+ if (odp_pool_destroy(pool)) {
+ ODPH_ERR("Pool destroy failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void queue_test_capa(void)
+{
+ odp_queue_capability_t capa;
+
+ memset(&capa, 0, sizeof(odp_queue_capability_t));
+ CU_ASSERT_FATAL(odp_queue_capability(&capa) == 0);
+
+ CU_ASSERT(capa.max_queues > 0);
+ CU_ASSERT(capa.max_queues >= capa.plain.max_num);
+ CU_ASSERT(capa.max_queues >= capa.plain.lockfree.max_num);
+ CU_ASSERT(capa.max_queues >= capa.plain.waitfree.max_num);
+}
+
+static void test_defaults(uint8_t fill)
+{
+ odp_queue_param_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_queue_param_init(&param);
+ CU_ASSERT(param.type == ODP_QUEUE_TYPE_PLAIN);
+ CU_ASSERT(param.enq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(param.deq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(param.sched.prio == odp_schedule_default_prio());
+ CU_ASSERT(param.sched.sync == ODP_SCHED_SYNC_PARALLEL);
+ CU_ASSERT(param.sched.group == ODP_SCHED_GROUP_ALL);
+ CU_ASSERT(param.sched.lock_count == 0);
+ CU_ASSERT(param.order == ODP_QUEUE_ORDER_KEEP);
+ CU_ASSERT(param.nonblocking == ODP_BLOCKING);
+ CU_ASSERT(param.context == NULL);
+ CU_ASSERT(param.context_len == 0);
+ CU_ASSERT(param.size == 0);
+}
+
+static void queue_test_param_init(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void queue_test_max_plain(void)
+{
+ odp_queue_capability_t capa;
+ odp_queue_param_t qparams;
+ char name[ODP_QUEUE_NAME_LEN];
+ odp_queue_t queue[MAX_QUEUES];
+ uint32_t num_queues, min, i;
+
+ memset(&capa, 0, sizeof(odp_queue_capability_t));
+ CU_ASSERT(odp_queue_capability(&capa) == 0);
+
+ CU_ASSERT(capa.max_queues != 0);
+ CU_ASSERT(capa.plain.max_num != 0);
+
+ min = capa.plain.max_num;
+
+ CU_ASSERT(capa.max_queues >= min);
+
+ for (i = 0; i < ODP_QUEUE_NAME_LEN; i++)
+ name[i] = 'A' + (i % 26);
+
+ name[ODP_QUEUE_NAME_LEN - 1] = 0;
+
+ odp_queue_param_init(&qparams);
+ CU_ASSERT(qparams.nonblocking == ODP_BLOCKING);
+
+ num_queues = capa.plain.max_num;
+
+ if (num_queues > MAX_QUEUES)
+ num_queues = MAX_QUEUES;
+
+ for (i = 0; i < num_queues; i++) {
+ generate_name(name, i);
+ queue[i] = odp_queue_create(name, &qparams);
+
+ if (queue[i] == ODP_QUEUE_INVALID) {
+ CU_FAIL("Queue create failed");
+ num_queues = i;
+ break;
+ }
+
+ CU_ASSERT(odp_queue_lookup(name) != ODP_QUEUE_INVALID);
+ }
+
+ for (i = 0; i < num_queues; i++)
+ CU_ASSERT(odp_queue_destroy(queue[i]) == 0);
+}
+
+static int queue_create_multi(const char *name[], const odp_queue_param_t param[],
+ odp_bool_t share_param, odp_queue_t queue[], uint32_t num)
+{
+ const uint32_t max_retries = 100;
+ uint32_t num_created = 0;
+ uint32_t num_retries = 0;
+
+ do {
+ const char **cur_name = name != NULL ? &name[num_created] : NULL;
+ const odp_queue_param_t *cur_param = share_param ? &param[0] : &param[num_created];
+ int ret = odp_queue_create_multi(cur_name, cur_param, share_param,
+ &queue[num_created], num - num_created);
+ if (ret < 0) {
+ CU_FAIL("Queue create multi failed");
+ break;
+ }
+ CU_ASSERT_FATAL((uint32_t)ret <= num - num_created);
+
+ num_retries = ret == 0 ? num_retries + 1 : 0;
+ num_created += ret;
+ } while (num_created < num && num_retries < max_retries);
+
+ return num_created;
+}
+
+static void queue_destroy_multi(odp_queue_t queue[], uint32_t num)
+{
+ uint32_t num_left = num;
+ uint32_t num_freed = 0;
+
+ while (num_left) {
+ int ret = odp_queue_destroy_multi(&queue[num_freed], num_left);
+
+ CU_ASSERT_FATAL(ret > 0 && (uint32_t)ret <= num_left);
+
+ num_left -= ret;
+ num_freed += ret;
+ }
+ CU_ASSERT_FATAL(num_freed == num);
+}
+
+static void queue_test_create_destroy_multi(void)
+{
+ odp_queue_capability_t capa;
+ odp_queue_param_t param_single;
+ odp_queue_param_t param[MAX_QUEUES];
+ odp_queue_t queue[MAX_QUEUES];
+ const char *name[MAX_QUEUES] = {NULL, "aaa", NULL, "bbb", "ccc", NULL, "ddd"};
+ uint32_t num_queues, num_created;
+
+ CU_ASSERT_FATAL(odp_queue_capability(&capa) == 0);
+ CU_ASSERT_FATAL(capa.plain.max_num != 0);
+
+ num_queues = capa.plain.max_num < MAX_QUEUES ? capa.plain.max_num : MAX_QUEUES;
+ for (uint32_t i = 0; i < num_queues; i++)
+ odp_queue_param_init(&param[i]);
+ odp_queue_param_init(&param_single);
+
+ /* Create queues using shared parameters */
+ num_created = queue_create_multi(name, &param_single, true, queue, num_queues);
+ CU_ASSERT(num_created == num_queues);
+ queue_destroy_multi(queue, num_created);
+
+ num_created = queue_create_multi(NULL, &param_single, true, queue, num_queues);
+ CU_ASSERT(num_created == num_queues);
+ queue_destroy_multi(queue, num_created);
+
+ /* Use separate parameters for each queue */
+ num_created = queue_create_multi(name, param, false, queue, num_queues);
+ CU_ASSERT(num_created == num_queues);
+ queue_destroy_multi(queue, num_created);
+
+ num_created = queue_create_multi(NULL, param, false, queue, num_queues);
+ CU_ASSERT(num_created == num_queues);
+ queue_destroy_multi(queue, num_created);
+}
+
+static void queue_test_mode(void)
+{
+ odp_queue_param_t qparams;
+ odp_queue_t queue;
+ int i, j;
+ odp_queue_op_mode_t mode[3] = { ODP_QUEUE_OP_MT,
+ ODP_QUEUE_OP_MT_UNSAFE,
+ ODP_QUEUE_OP_DISABLED };
+
+ odp_queue_param_init(&qparams);
+
+ /* Plain queue modes */
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ /* Should not disable both enq and deq */
+ if (i == 2 && j == 2)
+ break;
+
+ qparams.enq_mode = mode[i];
+ qparams.deq_mode = mode[j];
+ queue = odp_queue_create("test_queue", &qparams);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+ if (queue != ODP_QUEUE_INVALID)
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ }
+ }
+
+ odp_queue_param_init(&qparams);
+ qparams.type = ODP_QUEUE_TYPE_SCHED;
+
+ /* Scheduled queue modes. Dequeue mode is fixed. */
+ for (i = 0; i < 3; i++) {
+ qparams.enq_mode = mode[i];
+ queue = odp_queue_create("test_queue", &qparams);
+ CU_ASSERT(queue != ODP_QUEUE_INVALID);
+ if (queue != ODP_QUEUE_INVALID)
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ }
+}
+
+static odp_event_t dequeue_event(odp_queue_t queue)
+{
+ odp_event_t ev;
+ int i;
+
+ for (i = 0; i < MAX_ITERATION; i++) {
+ ev = odp_queue_deq(queue);
+ if (ev != ODP_EVENT_INVALID)
+ break;
+ }
+
+ return ev;
+}
+
+static void test_burst(odp_nonblocking_t nonblocking,
+ odp_queue_op_mode_t enq_mode,
+ odp_queue_op_mode_t deq_mode)
+{
+ odp_queue_param_t param;
+ odp_queue_t queue;
+ odp_queue_capability_t capa;
+ uint32_t max_burst, burst, i, j;
+ odp_pool_t pool;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t *data;
+
+ CU_ASSERT_FATAL(odp_queue_capability(&capa) == 0);
+
+ max_burst = capa.plain.max_size;
+
+ if (nonblocking == ODP_NONBLOCKING_LF) {
+ if (capa.plain.lockfree.max_num == 0) {
+ printf(" NO LOCKFREE QUEUES. Test skipped.\n");
+ return;
+ }
+
+ max_burst = capa.plain.lockfree.max_size;
+ }
+
+ if (max_burst == 0 || max_burst > MAX_NUM_EVENT)
+ max_burst = MAX_NUM_EVENT;
+
+ pool = odp_pool_lookup("msg_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_PLAIN;
+ param.nonblocking = nonblocking;
+ param.size = max_burst;
+ param.enq_mode = enq_mode;
+ param.deq_mode = deq_mode;
+
+ queue = odp_queue_create("burst test", &param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ CU_ASSERT(odp_queue_deq(queue) == ODP_EVENT_INVALID);
+
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ CU_ASSERT(odp_queue_enq(queue, ev) == 0);
+ ev = dequeue_event(queue);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ for (j = 0; j < 2; j++) {
+ if (j == 0)
+ burst = max_burst / 4;
+ else
+ burst = max_burst;
+
+ for (i = 0; i < burst; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ data = odp_buffer_addr(buf);
+ *data = i;
+ ev = odp_buffer_to_event(buf);
+ CU_ASSERT(odp_queue_enq(queue, ev) == 0);
+ }
+
+ for (i = 0; i < burst; i++) {
+ ev = dequeue_event(queue);
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ if (ev != ODP_EVENT_INVALID) {
+ buf = odp_buffer_from_event(ev);
+ data = odp_buffer_addr(buf);
+ CU_ASSERT(*data == i);
+ odp_event_free(ev);
+ }
+ }
+ }
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void queue_test_burst(void)
+{
+ test_burst(ODP_BLOCKING, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_burst_spmc(void)
+{
+ test_burst(ODP_BLOCKING, ODP_QUEUE_OP_MT_UNSAFE, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_burst_mpsc(void)
+{
+ test_burst(ODP_BLOCKING, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_burst_spsc(void)
+{
+ test_burst(ODP_BLOCKING, ODP_QUEUE_OP_MT_UNSAFE,
+ ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_burst_lf(void)
+{
+ test_burst(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_burst_lf_spmc(void)
+{
+ test_burst(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT_UNSAFE, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_burst_lf_mpsc(void)
+{
+ test_burst(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_burst_lf_spsc(void)
+{
+ test_burst(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT_UNSAFE,
+ ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static int queue_pair_work_loop(void *arg)
+{
+ uint32_t i, events, burst, retry, max_retry;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t *data;
+ odp_queue_t src_queue, dst_queue;
+ odp_pool_t pool;
+ int passed;
+ int thread_a;
+ test_globals_t *globals = arg;
+
+ burst = globals->pair.burst;
+ pool = globals->pair.pool;
+
+ /* Select which thread is A */
+ thread_a = odp_atomic_fetch_inc_u32(&globals->pair.counter);
+
+ if (thread_a) {
+ src_queue = globals->pair.queue_a;
+ dst_queue = globals->pair.queue_b;
+ } else {
+ src_queue = globals->pair.queue_b;
+ dst_queue = globals->pair.queue_a;
+ }
+
+ for (i = 0; i < burst; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT(buf != ODP_BUFFER_INVALID);
+
+ if (buf == ODP_BUFFER_INVALID)
+ return -1;
+
+ data = odp_buffer_addr(buf);
+ *data = i;
+ ev = odp_buffer_to_event(buf);
+ CU_ASSERT(odp_queue_enq(dst_queue, ev) == 0);
+ }
+
+ /* Wait until both threads are ready */
+ odp_barrier_wait(&globals->pair.barrier);
+ events = 0;
+ retry = 0;
+ max_retry = 0;
+ i = 0;
+ while (events < 10000 && retry < 300) {
+ ev = odp_queue_deq(src_queue);
+ if (ev == ODP_EVENT_INVALID) {
+ retry++;
+ /* Slow down polling period after 100 retries. This
+ * gives time for the other thread to answer, if it
+ * was e.g. interrupted by the OS. We give up if
+ * the source queue stays empty for about 100ms. */
+ if (retry > 200)
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ else if (retry > 100)
+ odp_time_wait_ns(ODP_TIME_USEC_IN_NS);
+
+ if (retry > max_retry)
+ max_retry = retry;
+
+ continue;
+ }
+
+ events++;
+ retry = 0;
+ buf = odp_buffer_from_event(ev);
+ data = odp_buffer_addr(buf);
+ if (*data != i) {
+ ODPH_ERR("Seq error: expected %u, recv %u\n", i, *data);
+ CU_FAIL("Sequence number error");
+ }
+
+ i++;
+ if (i == burst)
+ i = 0;
+
+ CU_ASSERT(odp_queue_enq(dst_queue, ev) == 0);
+ }
+
+ passed = (events == 10000);
+
+ if (thread_a) {
+ globals->pair.passed_a = passed;
+ if (max_retry > 100)
+ printf("\n thread_a max_retry %u\n", max_retry);
+ } else {
+ globals->pair.passed_b = passed;
+ if (max_retry > 100)
+ printf("\n thread_b max_retry %u\n", max_retry);
+ }
+
+ return 0;
+}
+
+static void test_pair(odp_nonblocking_t nonblocking,
+ odp_queue_op_mode_t enq_mode,
+ odp_queue_op_mode_t deq_mode)
+{
+ odp_queue_param_t param;
+ odp_queue_t queue;
+ odp_queue_capability_t capa;
+ uint32_t max_burst, num;
+ odp_pool_t pool;
+ odp_event_t ev;
+ odp_shm_t shm;
+ test_globals_t *globals;
+ void *arg;
+
+ shm = odp_shm_lookup(GLOBALS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+
+ CU_ASSERT_FATAL(odp_queue_capability(&capa) == 0);
+
+ max_burst = 2 * BURST_SIZE;
+
+ if (nonblocking == ODP_NONBLOCKING_LF) {
+ if (capa.plain.lockfree.max_num == 0) {
+ printf(" NO LOCKFREE QUEUES. Test skipped.\n");
+ return;
+ }
+
+ if (capa.plain.lockfree.max_size &&
+ capa.plain.lockfree.max_size < max_burst)
+ max_burst = capa.plain.lockfree.max_size;
+ } else {
+ if (capa.plain.max_size && capa.plain.max_size < max_burst)
+ max_burst = capa.plain.max_size;
+ }
+
+ globals->pair.burst = max_burst / 2;
+
+ pool = odp_pool_lookup("msg_pool");
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ globals->pair.pool = pool;
+
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_PLAIN;
+ param.nonblocking = nonblocking;
+ param.size = max_burst;
+ param.enq_mode = enq_mode;
+ param.deq_mode = deq_mode;
+
+ queue = odp_queue_create("queue_a", &param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+ globals->pair.queue_a = queue;
+ CU_ASSERT(odp_queue_deq(queue) == ODP_EVENT_INVALID);
+
+ queue = odp_queue_create("queue_b", &param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+ globals->pair.queue_b = queue;
+ CU_ASSERT(odp_queue_deq(queue) == ODP_EVENT_INVALID);
+
+ odp_barrier_init(&globals->pair.barrier, 2);
+ globals->pair.passed_a = 0;
+ globals->pair.passed_b = 0;
+ odp_atomic_init_u32(&globals->pair.counter, 0);
+
+ /* Create one worker thread */
+ arg = globals;
+ odp_cunit_thread_create(1, queue_pair_work_loop, &arg, 0, 0);
+
+ /* Run this thread as the second thread */
+ CU_ASSERT(queue_pair_work_loop(globals) == 0);
+
+ /* Wait worker to terminate */
+ odp_cunit_thread_join(1);
+
+ CU_ASSERT(globals->pair.passed_a);
+ CU_ASSERT(globals->pair.passed_b);
+
+ num = 0;
+
+ while ((ev = dequeue_event(globals->pair.queue_a))
+ != ODP_EVENT_INVALID) {
+ num++;
+ odp_event_free(ev);
+ }
+
+ while ((ev = dequeue_event(globals->pair.queue_b))
+ != ODP_EVENT_INVALID) {
+ num++;
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(num == max_burst);
+ CU_ASSERT(odp_queue_destroy(globals->pair.queue_a) == 0);
+ CU_ASSERT(odp_queue_destroy(globals->pair.queue_b) == 0);
+}
+
+static void queue_test_pair(void)
+{
+ test_pair(ODP_BLOCKING, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_pair_spmc(void)
+{
+ test_pair(ODP_BLOCKING, ODP_QUEUE_OP_MT_UNSAFE, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_pair_mpsc(void)
+{
+ test_pair(ODP_BLOCKING, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_pair_spsc(void)
+{
+ test_pair(ODP_BLOCKING, ODP_QUEUE_OP_MT_UNSAFE, ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_pair_lf(void)
+{
+ test_pair(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_pair_lf_spmc(void)
+{
+ test_pair(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT_UNSAFE, ODP_QUEUE_OP_MT);
+}
+
+static void queue_test_pair_lf_mpsc(void)
+{
+ test_pair(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT, ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_pair_lf_spsc(void)
+{
+ test_pair(ODP_NONBLOCKING_LF, ODP_QUEUE_OP_MT_UNSAFE,
+ ODP_QUEUE_OP_MT_UNSAFE);
+}
+
+static void queue_test_param(void)
+{
+ odp_queue_t queue, null_queue;
+ odp_event_t enev[BURST_SIZE] = {ODP_EVENT_INVALID};
+ odp_event_t deev[BURST_SIZE] = {ODP_EVENT_INVALID};
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_pool_t msg_pool;
+ odp_event_t *pev_tmp;
+ int i, deq_ret, ret;
+ int nr_deq_entries = 0;
+ int max_iteration = MAX_ITERATION;
+ odp_queue_param_t qparams;
+ odp_buffer_t enbuf;
+
+ odp_queue_param_init(&qparams);
+
+ /* Schedule type queue */
+ qparams.type = ODP_QUEUE_TYPE_SCHED;
+ qparams.sched.prio = odp_schedule_min_prio();
+ qparams.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ qparams.sched.group = ODP_SCHED_GROUP_WORKER;
+
+ queue = odp_queue_create("test_queue", &qparams);
+ CU_ASSERT_FATAL(ODP_QUEUE_INVALID != queue);
+ CU_ASSERT(odp_queue_to_u64(queue) !=
+ odp_queue_to_u64(ODP_QUEUE_INVALID));
+ CU_ASSERT(queue == odp_queue_lookup("test_queue"));
+ CU_ASSERT(ODP_QUEUE_TYPE_SCHED == odp_queue_type(queue));
+ CU_ASSERT(odp_schedule_min_prio() == odp_queue_sched_prio(queue));
+ CU_ASSERT(ODP_SCHED_SYNC_PARALLEL == odp_queue_sched_type(queue));
+ CU_ASSERT(ODP_SCHED_GROUP_WORKER == odp_queue_sched_group(queue));
+
+ CU_ASSERT(odp_queue_context(queue) == NULL);
+ CU_ASSERT(0 == odp_queue_context_set(queue, &queue_context,
+ sizeof(queue_context)));
+
+ CU_ASSERT(&queue_context == odp_queue_context(queue));
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+
+ /* Create queue with no name */
+ odp_queue_param_init(&qparams);
+ null_queue = odp_queue_create(NULL, &qparams);
+ CU_ASSERT_FATAL(ODP_QUEUE_INVALID != null_queue);
+ CU_ASSERT(odp_queue_context(null_queue) == NULL);
+
+ /* Plain type queue */
+ odp_queue_param_init(&qparams);
+ qparams.type = ODP_QUEUE_TYPE_PLAIN;
+ qparams.context = &queue_context;
+ qparams.context_len = sizeof(queue_context);
+
+ queue = odp_queue_create("test_queue", &qparams);
+ CU_ASSERT_FATAL(ODP_QUEUE_INVALID != queue);
+ CU_ASSERT(queue == odp_queue_lookup("test_queue"));
+ CU_ASSERT(ODP_QUEUE_TYPE_PLAIN == odp_queue_type(queue));
+ CU_ASSERT(&queue_context == odp_queue_context(queue));
+
+ /* Destroy queue with no name */
+ CU_ASSERT(odp_queue_destroy(null_queue) == 0);
+
+ msg_pool = odp_pool_lookup("msg_pool");
+ buf = odp_buffer_alloc(msg_pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+ if (ret) {
+ odp_buffer_free(buf);
+ } else {
+ CU_ASSERT(ev == odp_queue_deq(queue));
+ odp_buffer_free(buf);
+ }
+
+ for (i = 0; i < BURST_SIZE; i++) {
+ buf = odp_buffer_alloc(msg_pool);
+ enev[i] = odp_buffer_to_event(buf);
+ }
+
+ /*
+ * odp_queue_enq_multi may return 0..n buffers due to the resource
+ * constraints in the implementation at that given point of time.
+ * But here we assume that we succeed in enqueuing all buffers.
+ */
+ ret = odp_queue_enq_multi(queue, enev, BURST_SIZE);
+ CU_ASSERT(BURST_SIZE == ret);
+ i = ret < 0 ? 0 : ret;
+ for ( ; i < BURST_SIZE; i++)
+ odp_event_free(enev[i]);
+
+ pev_tmp = deev;
+ do {
+ deq_ret = odp_queue_deq_multi(queue, pev_tmp, BURST_SIZE);
+ nr_deq_entries += deq_ret;
+ max_iteration--;
+ pev_tmp += deq_ret;
+ CU_ASSERT(max_iteration >= 0);
+ } while (nr_deq_entries < BURST_SIZE);
+
+ for (i = 0; i < BURST_SIZE; i++) {
+ enbuf = odp_buffer_from_event(enev[i]);
+ CU_ASSERT(enev[i] == deev[i]);
+ odp_buffer_free(enbuf);
+ }
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void queue_test_same_name(int sched)
+{
+ odp_queue_t queue, queue_a, queue_b;
+ odp_queue_param_t param;
+ const char *name = "same_name";
+
+ odp_queue_param_init(&param);
+
+ if (sched)
+ param.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue_a = odp_queue_create(name, &param);
+ CU_ASSERT_FATAL(queue_a != ODP_QUEUE_INVALID);
+
+ queue = odp_queue_lookup(name);
+ CU_ASSERT(queue == queue_a);
+
+ /* Second queue with the same name */
+ queue_b = odp_queue_create(name, &param);
+ CU_ASSERT_FATAL(queue_b != ODP_QUEUE_INVALID);
+
+ queue = odp_queue_lookup(name);
+ CU_ASSERT(queue == queue_a || queue == queue_b);
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue_a) == 0);
+ CU_ASSERT_FATAL(odp_queue_destroy(queue_b) == 0);
+}
+
+static void queue_test_same_name_plain(void)
+{
+ queue_test_same_name(0);
+}
+
+static void queue_test_same_name_sched(void)
+{
+ queue_test_same_name(1);
+}
+
+static void queue_test_info(void)
+{
+ odp_queue_t q_plain, q_order;
+ const char *const nq_plain = "test_q_plain";
+ const char *const nq_order = "test_q_order";
+ odp_queue_info_t info;
+ odp_queue_param_t param;
+ odp_queue_capability_t capability;
+ odp_schedule_capability_t sched_capa;
+ char q_plain_ctx[] = "test_q_plain context data";
+ char q_order_ctx[] = "test_q_order context data";
+ uint32_t lock_count;
+ char *ctx;
+ uint32_t ret;
+
+ /* Create a plain queue and set context */
+ q_plain = odp_queue_create(nq_plain, NULL);
+ CU_ASSERT_FATAL(ODP_QUEUE_INVALID != q_plain);
+ CU_ASSERT(odp_queue_context_set(q_plain, q_plain_ctx,
+ sizeof(q_plain_ctx)) == 0);
+
+ memset(&capability, 0, sizeof(odp_queue_capability_t));
+ CU_ASSERT(odp_queue_capability(&capability) == 0);
+ CU_ASSERT(odp_schedule_capability(&sched_capa) == 0);
+ /* Create a scheduled ordered queue with explicitly set params */
+ odp_queue_param_init(&param);
+ param.type = ODP_QUEUE_TYPE_SCHED;
+ param.sched.prio = odp_schedule_default_prio();
+ param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ param.sched.group = ODP_SCHED_GROUP_ALL;
+ param.sched.lock_count = sched_capa.max_ordered_locks;
+ if (param.sched.lock_count == 0)
+ printf("\n Ordered locks NOT supported\n");
+ param.context = q_order_ctx;
+ q_order = odp_queue_create(nq_order, &param);
+ CU_ASSERT_FATAL(ODP_QUEUE_INVALID != q_order);
+
+ /* Check info and call print for a plain queue */
+ CU_ASSERT(odp_queue_info(q_plain, &info) == 0);
+ CU_ASSERT(strcmp(nq_plain, info.name) == 0);
+ CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_PLAIN);
+ CU_ASSERT(info.param.type == odp_queue_type(q_plain));
+ CU_ASSERT(info.param.enq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(info.param.deq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(info.param.order == ODP_QUEUE_ORDER_KEEP);
+ CU_ASSERT(info.param.nonblocking == ODP_BLOCKING);
+ ctx = info.param.context; /* 'char' context ptr */
+ CU_ASSERT(ctx == q_plain_ctx);
+ CU_ASSERT(info.param.context == odp_queue_context(q_plain));
+ odp_queue_print(q_plain);
+
+ /* Check info and call print for a scheduled ordered queue */
+ CU_ASSERT(odp_queue_info(q_order, &info) == 0);
+ CU_ASSERT(strcmp(nq_order, info.name) == 0);
+ CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_SCHED);
+ CU_ASSERT(info.param.type == odp_queue_type(q_order));
+ CU_ASSERT(info.param.enq_mode == ODP_QUEUE_OP_MT);
+ CU_ASSERT(info.param.deq_mode == ODP_QUEUE_OP_DISABLED);
+ CU_ASSERT(info.param.order == ODP_QUEUE_ORDER_KEEP);
+ CU_ASSERT(info.param.nonblocking == ODP_BLOCKING);
+ ctx = info.param.context; /* 'char' context ptr */
+ CU_ASSERT(ctx == q_order_ctx);
+ CU_ASSERT(info.param.context == odp_queue_context(q_order));
+ CU_ASSERT(info.param.sched.prio == odp_queue_sched_prio(q_order));
+ CU_ASSERT(info.param.sched.sync == odp_queue_sched_type(q_order));
+ CU_ASSERT(info.param.sched.group == odp_queue_sched_group(q_order));
+ ret = odp_queue_lock_count(q_order);
+ CU_ASSERT(ret == param.sched.lock_count);
+ lock_count = ret;
+ CU_ASSERT(info.param.sched.lock_count == lock_count);
+ odp_queue_print(q_order);
+
+ odp_queue_print_all();
+
+ CU_ASSERT(odp_queue_destroy(q_plain) == 0);
+ CU_ASSERT(odp_queue_destroy(q_order) == 0);
+}
+
+static uint32_t alloc_and_enqueue(odp_queue_t queue, odp_pool_t pool,
+ uint32_t num)
+{
+ uint32_t i, ret;
+ odp_buffer_t buf;
+ odp_event_t ev;
+
+ for (i = 0; i < num; i++) {
+ buf = odp_buffer_alloc(pool);
+
+ CU_ASSERT(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+
+ ret = odp_queue_enq(queue, ev);
+
+ CU_ASSERT(ret == 0);
+
+ if (ret)
+ break;
+ }
+
+ return i;
+}
+
+static uint32_t dequeue_and_free_all(odp_queue_t queue)
+{
+ odp_event_t ev;
+ uint32_t num, retries;
+
+ num = 0;
+ retries = 0;
+
+ while (1) {
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (retries >= DEQ_RETRIES)
+ return num;
+
+ retries++;
+ continue;
+ }
+
+ retries = 0;
+ num++;
+
+ odp_event_free(ev);
+ }
+
+ return num;
+}
+
+static int enqueue_with_retry(odp_queue_t queue, odp_event_t ev)
+{
+ int i;
+
+ for (i = 0; i < ENQ_RETRIES; i++)
+ if (odp_queue_enq(queue, ev) == 0)
+ return 0;
+
+ return -1;
+}
+
+static int queue_test_worker(void *arg)
+{
+ uint32_t num, retries, num_workers;
+ int thr_id, ret;
+ odp_event_t ev;
+ odp_queue_t queue;
+ test_globals_t *globals = arg;
+
+ thr_id = odp_thread_id();
+ queue = globals->queue;
+ num_workers = globals->num_workers;
+
+ if (num_workers > 1)
+ odp_barrier_wait(&globals->barrier);
+
+ retries = 0;
+ num = odp_atomic_fetch_inc_u32(&globals->num_event);
+
+ /* On average, each worker deq-enq each event once */
+ while (num < (num_workers * MAX_NUM_EVENT)) {
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (retries < DEQ_RETRIES) {
+ retries++;
+ continue;
+ }
+
+ /* Prevent thread to starve */
+ num = odp_atomic_fetch_inc_u32(&globals->num_event);
+ retries = 0;
+ continue;
+ }
+
+ globals->thread[thr_id].num_event++;
+
+ ret = enqueue_with_retry(queue, ev);
+
+ CU_ASSERT(ret == 0);
+
+ num = odp_atomic_fetch_inc_u32(&globals->num_event);
+ }
+
+ return 0;
+}
+
+static void reset_thread_stat(test_globals_t *globals)
+{
+ int i;
+
+ odp_atomic_init_u32(&globals->num_event, 0);
+
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ globals->thread[i].num_event = 0;
+}
+
+static void multithread_test(odp_nonblocking_t nonblocking)
+{
+ odp_shm_t shm;
+ test_globals_t *globals;
+ odp_queue_t queue;
+ odp_queue_param_t qparams;
+ odp_queue_capability_t capa;
+ uint32_t queue_size, max_size;
+ uint32_t num, sum, num_free, i;
+ int num_workers;
+ void *arg;
+
+ CU_ASSERT(odp_queue_capability(&capa) == 0);
+
+ queue_size = 2 * MAX_NUM_EVENT;
+
+ max_size = capa.plain.max_size;
+
+ if (nonblocking == ODP_NONBLOCKING_LF) {
+ if (capa.plain.lockfree.max_num == 0) {
+ printf(" NO LOCKFREE QUEUES. Test skipped.\n");
+ return;
+ }
+
+ max_size = capa.plain.lockfree.max_size;
+ }
+
+ if (max_size && queue_size > max_size)
+ queue_size = max_size;
+
+ num = MAX_NUM_EVENT;
+
+ if (num > queue_size)
+ num = queue_size / 2;
+
+ shm = odp_shm_lookup(GLOBALS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ globals = odp_shm_addr(shm);
+ num_workers = globals->num_workers;
+
+ odp_queue_param_init(&qparams);
+ qparams.type = ODP_QUEUE_TYPE_PLAIN;
+ qparams.size = queue_size;
+ qparams.nonblocking = nonblocking;
+
+ queue = odp_queue_create("queue_test_mt", &qparams);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ globals->queue = queue;
+ reset_thread_stat(globals);
+
+ CU_ASSERT(alloc_and_enqueue(queue, pool, num) == num);
+
+ arg = globals;
+ odp_cunit_thread_create(num_workers, queue_test_worker, &arg, 0, 0);
+
+ /* Wait for worker threads to terminate */
+ odp_cunit_thread_join(num_workers);
+
+ sum = 0;
+ for (i = 0; i < ODP_THREAD_COUNT_MAX; i++)
+ sum += globals->thread[i].num_event;
+
+ CU_ASSERT(sum != 0);
+
+ num_free = dequeue_and_free_all(queue);
+
+ CU_ASSERT(num_free == num);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void queue_test_mt_plain_block(void)
+{
+ multithread_test(ODP_BLOCKING);
+}
+
+static void queue_test_mt_plain_nonblock_lf(void)
+{
+ multithread_test(ODP_NONBLOCKING_LF);
+}
+
+odp_testinfo_t queue_suite[] = {
+ ODP_TEST_INFO(queue_test_capa),
+ ODP_TEST_INFO(queue_test_param_init),
+ ODP_TEST_INFO(queue_test_mode),
+ ODP_TEST_INFO(queue_test_max_plain),
+ ODP_TEST_INFO(queue_test_create_destroy_multi),
+ ODP_TEST_INFO(queue_test_burst),
+ ODP_TEST_INFO(queue_test_burst_spmc),
+ ODP_TEST_INFO(queue_test_burst_mpsc),
+ ODP_TEST_INFO(queue_test_burst_spsc),
+ ODP_TEST_INFO(queue_test_burst_lf),
+ ODP_TEST_INFO(queue_test_burst_lf_spmc),
+ ODP_TEST_INFO(queue_test_burst_lf_mpsc),
+ ODP_TEST_INFO(queue_test_burst_lf_spsc),
+ ODP_TEST_INFO(queue_test_pair),
+ ODP_TEST_INFO(queue_test_pair_spmc),
+ ODP_TEST_INFO(queue_test_pair_mpsc),
+ ODP_TEST_INFO(queue_test_pair_spsc),
+ ODP_TEST_INFO(queue_test_pair_lf),
+ ODP_TEST_INFO(queue_test_pair_lf_spmc),
+ ODP_TEST_INFO(queue_test_pair_lf_mpsc),
+ ODP_TEST_INFO(queue_test_pair_lf_spsc),
+ ODP_TEST_INFO(queue_test_param),
+ ODP_TEST_INFO(queue_test_same_name_plain),
+ ODP_TEST_INFO(queue_test_same_name_sched),
+ ODP_TEST_INFO(queue_test_info),
+ ODP_TEST_INFO(queue_test_mt_plain_block),
+ ODP_TEST_INFO(queue_test_mt_plain_nonblock_lf),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t queue_suites[] = {
+ {"Queue", queue_suite_init, queue_suite_term, queue_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(queue_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/random/.gitignore b/test/validation/api/random/.gitignore
index 2c88ec0b8..2c88ec0b8 100644
--- a/test/common_plat/validation/api/random/.gitignore
+++ b/test/validation/api/random/.gitignore
diff --git a/test/validation/api/random/Makefile.am b/test/validation/api/random/Makefile.am
new file mode 100644
index 000000000..743ecf1ff
--- /dev/null
+++ b/test/validation/api/random/Makefile.am
@@ -0,0 +1,5 @@
+include ../Makefile.inc
+
+test_PROGRAMS = random_main
+random_main_SOURCES = random.c
+LDADD += -lm
diff --git a/test/validation/api/random/random.c b/test/validation/api/random/random.c
new file mode 100644
index 000000000..6c32cb0f7
--- /dev/null
+++ b/test/validation/api/random/random.c
@@ -0,0 +1,536 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2021-2022 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+static void random_test_get_size(odp_random_kind_t kind)
+{
+ /* odp_random_data may fail to return data on every call (i.e. lack of
+ * entropy). Therefore loop with some sane loop timeout value. Note that
+ * it is not required for implementation to return data in the "timeout"
+ * amount of steps. Rather it is a way for preventing the test to loop
+ * forever.
+ * Also note that the timeout value here is chosen completely
+ * arbitrarily (although considered sane) and neither platforms or
+ * applications are not required to use it.
+ */
+ int32_t ret, timeout_ns = 1 * ODP_TIME_MSEC_IN_NS, sleep_ns = 100;
+ uint32_t bytes = 0;
+ uint8_t buf[32];
+
+ do {
+ ret = odp_random_data(buf + bytes, sizeof(buf) - bytes,
+ kind);
+ bytes += ret;
+ if (ret < 0 || bytes >= sizeof(buf))
+ break;
+ odp_time_wait_ns(sleep_ns);
+ timeout_ns -= sleep_ns;
+ } while (timeout_ns > 0);
+
+ CU_ASSERT(ret > 0);
+ CU_ASSERT(bytes == (int32_t)sizeof(buf));
+}
+
+static void random_test_get_size_basic(void)
+{
+ random_test_get_size(ODP_RANDOM_BASIC);
+}
+
+static void random_test_get_size_crypto(void)
+{
+ random_test_get_size(ODP_RANDOM_CRYPTO);
+}
+
+static void random_test_get_size_true(void)
+{
+ random_test_get_size(ODP_RANDOM_TRUE);
+}
+
+static void random_test_kind(void)
+{
+ int32_t rc;
+ uint8_t buf[4096];
+ uint32_t buf_size = sizeof(buf);
+ odp_random_kind_t max_kind = odp_random_max_kind();
+
+ rc = odp_random_data(buf, buf_size, max_kind);
+ CU_ASSERT(rc > 0);
+
+ switch (max_kind) {
+ case ODP_RANDOM_BASIC:
+ rc = odp_random_data(buf, 4, ODP_RANDOM_CRYPTO);
+ CU_ASSERT(rc < 0);
+ /* Fall through */
+
+ case ODP_RANDOM_CRYPTO:
+ rc = odp_random_data(buf, 4, ODP_RANDOM_TRUE);
+ CU_ASSERT(rc < 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void random_test_repeat(void)
+{
+ uint8_t buf1[1024];
+ uint8_t buf2[1024];
+ int32_t rc;
+ uint64_t seed1 = 12345897;
+ uint64_t seed2 = seed1;
+
+ rc = odp_random_test_data(buf1, sizeof(buf1), &seed1);
+ CU_ASSERT(rc == sizeof(buf1));
+
+ rc = odp_random_test_data(buf2, sizeof(buf2), &seed2);
+ CU_ASSERT(rc == sizeof(buf2));
+
+ CU_ASSERT(seed1 == seed2);
+ CU_ASSERT(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+}
+
+static void random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind)
+{
+ static uint64_t seed;
+
+ switch (kind) {
+ case ODP_RANDOM_BASIC:
+ case ODP_RANDOM_CRYPTO:
+ case ODP_RANDOM_TRUE:
+ for (uint32_t i = 0; i < len;) {
+ int32_t r = odp_random_data(buf + i, len - i, kind);
+
+ CU_ASSERT_FATAL(r >= 0);
+ i += r;
+ }
+ break;
+ default:
+ CU_ASSERT_FATAL(odp_random_test_data(buf, len, &seed) ==
+ (int32_t)len);
+ }
+}
+
+static void random_test_align_and_overflow(odp_random_kind_t kind)
+{
+ uint8_t ODP_ALIGNED_CACHE buf[64];
+
+ for (int align = 8; align < 16; align++) {
+ for (int len = 1; len <= 16; len++) {
+ memset(buf, 1, sizeof(buf));
+ random_data(buf + align, len, kind);
+ CU_ASSERT(buf[align - 1] == 1);
+ CU_ASSERT(buf[align + len] == 1);
+ }
+ }
+}
+
+static void random_test_align_and_overflow_test(void)
+{
+ random_test_align_and_overflow(-1);
+}
+
+static void random_test_align_and_overflow_basic(void)
+{
+ random_test_align_and_overflow(ODP_RANDOM_BASIC);
+}
+
+static void random_test_align_and_overflow_crypto(void)
+{
+ random_test_align_and_overflow(ODP_RANDOM_CRYPTO);
+}
+
+static void random_test_align_and_overflow_true(void)
+{
+ random_test_align_and_overflow(ODP_RANDOM_TRUE);
+}
+
+/*
+ * Randomness tests
+ *
+ * The purpose of the following tests is to check that random data looks random.
+ * Some of the tests are based on [1].
+ *
+ * [1] Special Publication 800-22 revision 1a: A Statistical Test Suite for
+ * Random and Pseudorandom Number Generators for Cryptographic Applications
+ * National Institute of Standards and Technology (NIST), April 2010
+ * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-22r1a.pdf
+ */
+
+/*
+ * Alpha for P-value tests. This does not affect the tests that use a
+ * precomputed critical value.
+ */
+static const double alpha = 0.00000001;
+
+static uint32_t random_bits(int n, odp_random_kind_t kind)
+{
+ static uint8_t buf[32 * 1024];
+ const int size = sizeof(buf);
+ static int cur_n;
+ static odp_random_kind_t cur_kind;
+ static int bit;
+ uint32_t r = 0;
+
+ if (n != cur_n || kind != cur_kind) {
+ cur_n = n;
+ cur_kind = kind;
+ bit = size * 8;
+ }
+
+ for (int i = 0; i < n; ) {
+ if (bit >= size * 8) {
+ random_data(buf, size, kind);
+ bit = 0;
+ }
+ if (n - i >= 8 && !(bit & 7)) {
+ /* Full byte. */
+ r <<= 8;
+ r |= buf[bit / 8];
+ bit += 8;
+ i += 8;
+ continue;
+ }
+ /* Single bit. */
+ r <<= 1;
+ r |= (buf[bit / 8] >> (7 - (bit & 7))) & 1;
+ bit++;
+ i++;
+ }
+
+ return r;
+}
+
+static const char *res_str(int pass)
+{
+ return pass ? "pass" : "FAIL";
+}
+
+/*
+ * Pearson's chi-squared goodness-of-fit test for uniform distribution. The test
+ * is run with multiple different bit block lengths. The null hypothesis is that
+ * each possible bit pattern is equally likely. If the chi-squared statistic is
+ * equal to or larger than the critical value, we conclude that the data is
+ * biased.
+ */
+static void random_test_frequency(odp_random_kind_t kind)
+{
+ /* Mean number of hits per cell. */
+ const uint32_t expected = 50;
+
+ /* From LibreOffice CHISQ.INV.RT(0.00000001; df). */
+ const double critical[] = {
+ 32.8413, 40.1300, 50.8129, 68.0293,
+ 97.0285, 147.463, 237.614, 402.685,
+ 711.187, 1297.50, 2426.64, 4623.37,
+ 8929.74, 17419.3, 34224.0, 67587.1,
+ };
+
+ printf("\n\n");
+
+ for (int bits = 1; bits <= 8; bits++) {
+ const uint32_t cells = 1 << bits;
+ const uint64_t num = expected * cells;
+ uint64_t f[256] = { 0 };
+
+ for (uint64_t i = 0; i < num; i++)
+ f[random_bits(bits, kind)]++;
+
+ double chisq = 0, crit = critical[bits - 1];
+
+ for (uint64_t i = 0; i < cells; i++) {
+ double dif = (double)f[i] - expected;
+
+ chisq += dif * dif / expected;
+ }
+
+ printf("bits %d ; chisq %g ; df %u ; crit %g ; %s\n",
+ bits, chisq, cells - 1, crit, res_str(chisq < crit));
+
+ CU_ASSERT(chisq < crit);
+ }
+
+ printf("\n");
+}
+
+static void random_test_frequency_crypto(void)
+{
+ random_test_frequency(ODP_RANDOM_CRYPTO);
+}
+
+static void random_test_frequency_true(void)
+{
+ random_test_frequency(ODP_RANDOM_TRUE);
+}
+
+/*
+ * Pearson's chi-squared test for independence. The null hypothesis is that the
+ * values of different bytes are independent. If the chi-squared statistic is
+ * equal to or greater than the critical value, we conclude that the bytes in
+ * the byte pairs selected from the data are not independent.
+ */
+static void random_test_independence(odp_random_kind_t kind)
+{
+ /* Mean number of hits per cell. */
+ const uint32_t expected = 100;
+
+ /* LibreOffice CHISQ.INV.RT(0.00000001; 255*255) */
+ const double critical = 67069.2;
+
+ printf("\n\n");
+ printf("critical value: %g\n", critical);
+
+ for (int lag = 1; lag <= 8; lag++) {
+ const uint32_t cells = 256 * 256;
+ const uint64_t num = expected * cells;
+ const int size = 32 * 1024;
+ int pos = size;
+ uint8_t buf[size];
+ uint64_t freq[256][256] = { { 0 } };
+ uint32_t row[256] = { 0 }, col[256] = { 0 };
+
+ for (uint64_t i = 0; i < num; i++) {
+ if (pos + lag >= size) {
+ random_data(buf, size, kind);
+ pos = 0;
+ }
+
+ uint8_t r = buf[pos], c = buf[pos + lag];
+
+ freq[r][c]++;
+ row[r]++;
+ col[c]++;
+ pos++;
+ }
+
+ double chisq = 0;
+
+ for (int i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ double expect = (double)row[i] * (double)col[j] / (double)num;
+ double diff = (double)freq[i][j] - expect;
+
+ chisq += diff * diff / expect;
+ }
+ }
+
+ printf("lag %d ; chisq %g ; %s\n",
+ lag, chisq, res_str(chisq < critical));
+
+ CU_ASSERT(chisq < critical);
+ }
+
+ printf("\n");
+}
+
+static void random_test_independence_crypto(void)
+{
+ random_test_independence(ODP_RANDOM_CRYPTO);
+}
+
+/*
+ * Sec. 2.3 Runs Test [1]. The test is run with several different n values. A
+ * few long runs may go unnoticed if n is large, while longer period
+ * non-randomness may go unnoticed if n is small.
+ */
+static void random_test_runs(odp_random_kind_t kind)
+{
+ printf("\n\n");
+ printf("alpha: %g\n", alpha);
+
+ for (int n = 128; n <= 1024 * 1024; n *= 2) {
+ double pi, P_value;
+ int bit = random_bits(1, kind);
+ uint64_t ones = bit, V = 1;
+
+ for (int i = 1; i < n; i++) {
+ int prev_bit = bit;
+
+ bit = random_bits(1, kind);
+ ones += bit;
+ V += (bit != prev_bit);
+ }
+
+ pi = (double)ones / n;
+
+ /*
+ * Skip the prerequisite frequency test (Sec. 2.3.4
+ * step (2)), since it's effectively the same as
+ * random_test_frequency() with bits = 1.
+ */
+
+ P_value = erfc(fabs(V - 2 * n * pi * (1 - pi)) /
+ (2 * sqrt(2 * n) * pi * (1 - pi)));
+ printf("n %d ; pi %g ; V %" PRIu64 " ; P_value %g ; %s\n",
+ n, pi, V, P_value, res_str(P_value >= alpha));
+
+ CU_ASSERT(P_value >= alpha);
+ }
+
+ printf("\n");
+}
+
+static void random_test_runs_crypto(void)
+{
+ random_test_runs(ODP_RANDOM_CRYPTO);
+}
+
+static void random_test_runs_true(void)
+{
+ random_test_runs(ODP_RANDOM_TRUE);
+}
+
+static int mx_bit(uint32_t *m, int r, int c)
+{
+ return (m[r] >> c) & 1;
+}
+
+static int mx_rank(uint32_t *m, int rows, int cols)
+{
+ int rank = 0;
+
+ for (int r = 0, c = 0; r < rows && c < cols; ) {
+ int swapped = r;
+
+ if (!mx_bit(m, r, c)) {
+ for (int sr = r + 1; sr < rows; sr++) {
+ if (mx_bit(m, sr, c)) {
+ uint32_t t = m[r];
+
+ m[r] = m[sr];
+ m[sr] = t;
+ swapped = sr;
+ break;
+ }
+ }
+ if (!mx_bit(m, r, c)) {
+ c++;
+ continue;
+ }
+ }
+
+ rank++;
+
+ for (int sr = swapped + 1; sr < rows; sr++) {
+ if (mx_bit(m, sr, c))
+ m[sr] ^= m[r];
+ }
+
+ r++;
+ }
+
+ return rank;
+}
+
+/*
+ * Sec. 2.5 Binary Matrix Rank Test [1].
+ */
+static void random_test_matrix_rank(odp_random_kind_t kind)
+{
+ const int N = 100; /* [1] recommends at least 38. */
+ const double p[3] = { 0.2888, 0.5776, 0.1336 };
+
+ printf("\n\n");
+ printf("alpha: %g\n", alpha);
+ printf("N: %d\n", N);
+
+ int F[3] = { 0 };
+
+ for (int i = 0; i < N; i++) {
+ uint32_t mx[32];
+
+ random_data((uint8_t *)mx, sizeof(mx), kind);
+
+ switch (mx_rank(mx, 32, 32)) {
+ case 32:
+ F[0]++;
+ break;
+ case 31:
+ F[1]++;
+ break;
+ default:
+ F[2]++;
+ }
+ }
+
+ double chisq, P_value;
+
+ chisq = pow(F[0] - p[0] * N, 2) / (p[0] * N) +
+ pow(F[1] - p[1] * N, 2) / (p[1] * N) +
+ pow(F[2] - p[2] * N, 2) / (p[2] * N);
+ P_value = exp(-chisq / 2);
+
+ printf("P_value %g ; %s\n", P_value, res_str(P_value >= alpha));
+
+ CU_ASSERT(P_value >= alpha);
+}
+
+static void random_test_matrix_rank_crypto(void)
+{
+ random_test_matrix_rank(ODP_RANDOM_CRYPTO);
+}
+
+static void random_test_matrix_rank_true(void)
+{
+ random_test_matrix_rank(ODP_RANDOM_TRUE);
+}
+
+static int check_kind_basic(void)
+{
+ return odp_random_max_kind() >= ODP_RANDOM_BASIC;
+}
+
+static int check_kind_crypto(void)
+{
+ return odp_random_max_kind() >= ODP_RANDOM_CRYPTO;
+}
+
+static int check_kind_true(void)
+{
+ return odp_random_max_kind() >= ODP_RANDOM_TRUE;
+}
+
+odp_testinfo_t random_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(random_test_get_size_basic, check_kind_basic),
+ ODP_TEST_INFO_CONDITIONAL(random_test_get_size_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_get_size_true, check_kind_true),
+ ODP_TEST_INFO(random_test_kind),
+ ODP_TEST_INFO(random_test_repeat),
+ ODP_TEST_INFO(random_test_align_and_overflow_test),
+ ODP_TEST_INFO_CONDITIONAL(random_test_align_and_overflow_basic, check_kind_basic),
+ ODP_TEST_INFO_CONDITIONAL(random_test_align_and_overflow_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_align_and_overflow_true, check_kind_true),
+ ODP_TEST_INFO_CONDITIONAL(random_test_frequency_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_frequency_true, check_kind_true),
+ ODP_TEST_INFO_CONDITIONAL(random_test_independence_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_runs_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_runs_true, check_kind_true),
+ ODP_TEST_INFO_CONDITIONAL(random_test_matrix_rank_crypto, check_kind_crypto),
+ ODP_TEST_INFO_CONDITIONAL(random_test_matrix_rank_true, check_kind_true),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t random_suites[] = {
+ {"Random", NULL, NULL, random_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(random_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/scheduler/.gitignore b/test/validation/api/scheduler/.gitignore
new file mode 100644
index 000000000..6892e6224
--- /dev/null
+++ b/test/validation/api/scheduler/.gitignore
@@ -0,0 +1,2 @@
+scheduler_main
+scheduler_no_predef_groups
diff --git a/test/validation/api/scheduler/Makefile.am b/test/validation/api/scheduler/Makefile.am
new file mode 100644
index 000000000..fc41ae5fe
--- /dev/null
+++ b/test/validation/api/scheduler/Makefile.am
@@ -0,0 +1,5 @@
+include ../Makefile.inc
+
+test_PROGRAMS = scheduler_main scheduler_no_predef_groups
+scheduler_main_SOURCES = scheduler.c
+scheduler_no_predef_groups = scheduler_no_predef_groups.c
diff --git a/test/validation/api/scheduler/scheduler.c b/test/validation/api/scheduler/scheduler.c
new file mode 100644
index 000000000..1b8a46ec8
--- /dev/null
+++ b/test/validation/api/scheduler/scheduler.c
@@ -0,0 +1,3768 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2024 Nokia
+ */
+
+#include <odp_api.h>
+#include "odp_cunit_common.h"
+#include <odp/helper/odph_api.h>
+
+#define MAX_WORKERS 32
+#define MAX_ORDERED_LOCKS 2
+#define MAX_POOL_SIZE (1024 * 1024)
+#define MSG_POOL_SIZE (64 * 1024)
+#define QUEUES_PER_PRIO 16
+#define BUF_SIZE 64
+#define BUFS_PER_QUEUE 100
+#define BUFS_PER_QUEUE_EXCL 10000
+#define BURST_BUF_SIZE 4
+#define NUM_BUFS_PAUSE 1000
+#define NUM_BUFS_BEFORE_PAUSE 10
+#define NUM_GROUPS 2
+#define MAX_QUEUES (64 * 1024)
+
+#define DEFAULT_NUM_EV 50
+
+#define MAX_FLOWS 16
+#define FLOW_TEST_NUM_EV (10 * MAX_FLOWS)
+
+#define GLOBALS_SHM_NAME "test_globals"
+#define MSG_POOL_NAME "msg_pool"
+#define QUEUE_CTX_POOL_NAME "queue_ctx_pool"
+#define SHM_THR_ARGS_NAME "shm_thr_args"
+
+#define ONE_Q 1
+#define ONE_PRIO 1
+
+#define SCHD_ONE 0
+#define SCHD_MULTI 1
+
+#define DISABLE_EXCL_ATOMIC 0
+#define ENABLE_EXCL_ATOMIC 1
+
+#define MAGIC 0xdeadbeef
+#define MAGIC1 0xdeadbeef
+#define MAGIC2 0xcafef00d
+
+#define CHAOS_NUM_QUEUES 6
+#define CHAOS_NUM_BUFS_PER_QUEUE 6
+#define CHAOS_NUM_ROUNDS 1000
+#define CHAOS_NUM_EVENTS (CHAOS_NUM_QUEUES * CHAOS_NUM_BUFS_PER_QUEUE)
+#define CHAOS_DEBUG (CHAOS_NUM_ROUNDS < 1000)
+#define CHAOS_PTR_TO_NDX(p) ((uint64_t)(uint32_t)(uintptr_t)p)
+#define CHAOS_NDX_TO_PTR(n) ((void *)(uintptr_t)n)
+
+#define WAIT_TIMEOUT (100 * ODP_TIME_MSEC_IN_NS)
+#define WAIT_ROUNDS 5
+#define WAIT_TOLERANCE (15 * ODP_TIME_MSEC_IN_NS)
+#define WAIT_1MS_RETRIES 1000
+
+#define SCHED_AND_PLAIN_ROUNDS 10000
+#define ATOMICITY_ROUNDS 100
+
+#define FIFO_MAX_EVENTS 151
+
+/* Test global variables */
+typedef struct {
+ int num_workers;
+ odp_barrier_t barrier;
+ int buf_count;
+ int buf_count_cpy;
+ int queues_per_prio;
+ int test_debug_print;
+ odp_shm_t shm_glb;
+ odp_shm_t shm_args;
+ odp_pool_t pool;
+ odp_pool_t queue_ctx_pool;
+ uint32_t max_sched_queue_size;
+ uint64_t num_flows;
+ odp_ticketlock_t lock;
+ odp_spinlock_t atomic_lock;
+ struct {
+ odp_queue_t handle;
+ odp_atomic_u32_t state;
+ } atomicity_q;
+ struct {
+ odp_queue_t handle;
+ char name[ODP_QUEUE_NAME_LEN];
+ } chaos_q[CHAOS_NUM_QUEUES];
+ struct {
+ odp_queue_t sched;
+ odp_queue_t plain;
+ } sched_and_plain_q;
+ struct {
+ odp_atomic_u32_t helper_ready;
+ odp_atomic_u32_t helper_active;
+ } order_wait;
+ struct {
+ odp_barrier_t barrier;
+ int multi;
+ odp_queue_t queue;
+ odp_pool_t pool;
+ uint32_t num_events;
+ uint32_t num_enq;
+ uint32_t burst;
+ odp_atomic_u32_t cur_thr;
+ uint16_t num_thr;
+ odp_event_t event[FIFO_MAX_EVENTS];
+ } fifo;
+
+} test_globals_t;
+
+typedef struct {
+ test_globals_t *globals;
+ odp_schedule_sync_t sync;
+ int num_queues;
+ int num_prio;
+ int num_bufs;
+ int num_workers;
+ int enable_schd_multi;
+ int enable_excl_atomic;
+} thread_args_t;
+
+typedef struct {
+ uint64_t sequence;
+ uint64_t lock_sequence[MAX_ORDERED_LOCKS];
+ uint64_t output_sequence;
+} buf_contents;
+
+typedef struct {
+ odp_buffer_t ctx_handle;
+ odp_queue_t pq_handle;
+ uint64_t sequence;
+ uint64_t lock_sequence[MAX_ORDERED_LOCKS];
+} queue_context;
+
+typedef struct {
+ uint64_t evno;
+ uint64_t seqno;
+} chaos_buf;
+
+static test_globals_t *globals;
+
+static int drain_queues(void)
+{
+ odp_event_t ev;
+ uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ int ret = 0;
+
+ while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID) {
+ odp_event_free(ev);
+ ret++;
+ }
+
+ return ret;
+}
+
+static void release_context(odp_schedule_sync_t sync)
+{
+ if (sync == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+ else if (sync == ODP_SCHED_SYNC_ORDERED)
+ odp_schedule_release_ordered();
+}
+
+static void test_init(uint8_t fill)
+{
+ odp_schedule_config_t default_config;
+
+ memset(&default_config, fill, sizeof(default_config));
+ odp_schedule_config_init(&default_config);
+
+ CU_ASSERT(default_config.max_flow_id == 0);
+
+ CU_ASSERT(default_config.sched_group.all);
+ CU_ASSERT(default_config.sched_group.control);
+ CU_ASSERT(default_config.sched_group.worker);
+}
+
+static void scheduler_test_init(void)
+{
+ test_init(0);
+ test_init(0xff);
+}
+
+static void scheduler_test_capa(void)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_queue_capability_t queue_capa;
+
+ memset(&sched_capa, 0, sizeof(odp_schedule_capability_t));
+ CU_ASSERT_FATAL(odp_schedule_capability(&sched_capa) == 0);
+ CU_ASSERT_FATAL(odp_queue_capability(&queue_capa) == 0);
+
+ CU_ASSERT(sched_capa.max_groups != 0);
+ CU_ASSERT(sched_capa.max_prios != 0);
+ CU_ASSERT(sched_capa.max_queues != 0);
+ CU_ASSERT(queue_capa.max_queues >= sched_capa.max_queues);
+}
+
+static void sched_queue_param_init(odp_queue_param_t *param)
+{
+ odp_queue_param_init(param);
+ param->type = ODP_QUEUE_TYPE_SCHED;
+ param->sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ param->sched.prio = odp_schedule_default_prio();
+ param->sched.group = ODP_SCHED_GROUP_ALL;
+}
+
+static void scheduler_test_wait_time(void)
+{
+ int i;
+ odp_queue_t queue;
+ odp_event_t ev;
+ uint64_t wait_time;
+ odp_queue_param_t qp;
+ odp_time_t lower_limit, upper_limit;
+ odp_time_t start_time, end_time, diff;
+ uint64_t duration_ns = WAIT_ROUNDS * WAIT_TIMEOUT;
+
+ /* check on read */
+ wait_time = odp_schedule_wait_time(0);
+ wait_time = odp_schedule_wait_time(1);
+
+ /* check ODP_SCHED_NO_WAIT */
+ sched_queue_param_init(&qp);
+ queue = odp_queue_create("dummy_queue", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ wait_time = odp_schedule_wait_time(WAIT_TIMEOUT);
+ start_time = odp_time_local();
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ end_time = odp_time_local();
+ CU_ASSERT_FATAL(ev == ODP_EVENT_INVALID);
+
+ diff = odp_time_diff(end_time, start_time);
+ lower_limit = ODP_TIME_NULL;
+ upper_limit = odp_time_local_from_ns(WAIT_TOLERANCE);
+
+ CU_ASSERT(odp_time_cmp(diff, lower_limit) >= 0);
+ CU_ASSERT(odp_time_cmp(diff, upper_limit) <= 0);
+
+ /* check time correctness */
+ printf("\nTesting wait time for %.3f sec ...\n", (double)duration_ns / ODP_TIME_SEC_IN_NS);
+ start_time = odp_time_local();
+ for (i = 0; i < WAIT_ROUNDS; i++) {
+ ev = odp_schedule(NULL, wait_time);
+ CU_ASSERT_FATAL(ev == ODP_EVENT_INVALID);
+ }
+ end_time = odp_time_local();
+
+ diff = odp_time_diff(end_time, start_time);
+ lower_limit = odp_time_local_from_ns(duration_ns - WAIT_TOLERANCE);
+ upper_limit = odp_time_local_from_ns(duration_ns + WAIT_TOLERANCE);
+
+ if (odp_time_cmp(diff, lower_limit) <= 0) {
+ ODPH_ERR("Exceed lower limit: diff is %" PRIu64 ", lower_limit %" PRIu64 "\n",
+ odp_time_to_ns(diff), odp_time_to_ns(lower_limit));
+ CU_FAIL("Exceed lower limit\n");
+ }
+
+ if (odp_time_cmp(diff, upper_limit) >= 0) {
+ ODPH_ERR("Exceed upper limit: diff is %" PRIu64 ", upper_limit %" PRIu64 "\n",
+ odp_time_to_ns(diff), odp_time_to_ns(upper_limit));
+ CU_FAIL("Exceed upper limit\n");
+ }
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+}
+
+static void scheduler_test_num_prio(void)
+{
+ int num_prio, min_prio, max_prio, default_prio;
+
+ num_prio = odp_schedule_num_prio();
+ CU_ASSERT(num_prio > 0);
+
+ min_prio = odp_schedule_min_prio();
+ max_prio = odp_schedule_max_prio();
+ default_prio = odp_schedule_default_prio();
+
+ CU_ASSERT(min_prio <= max_prio);
+ CU_ASSERT(min_prio <= default_prio);
+ CU_ASSERT(default_prio <= max_prio);
+ CU_ASSERT(num_prio == (max_prio - min_prio + 1));
+}
+
+static void scheduler_test_queue_destroy(void)
+{
+ odp_pool_t p;
+ odp_pool_param_t params;
+ odp_queue_param_t qp;
+ odp_queue_t queue, from;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t *u32;
+ int i, ret;
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+
+ odp_pool_param_init(&params);
+ params.buf.size = 100;
+ params.buf.align = 0;
+ params.buf.num = 1;
+ params.type = ODP_POOL_BUFFER;
+
+ p = odp_pool_create("sched_destroy_pool", &params);
+
+ CU_ASSERT_FATAL(p != ODP_POOL_INVALID);
+
+ sched_queue_param_init(&qp);
+
+ for (i = 0; i < 3; i++) {
+ qp.sched.sync = sync[i];
+ queue = odp_queue_create("sched_destroy_queue", &qp);
+
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ buf = odp_buffer_alloc(p);
+
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ u32 = odp_buffer_addr(buf);
+ u32[0] = MAGIC;
+
+ ev = odp_buffer_to_event(buf);
+
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+ if (ret)
+ odp_buffer_free(buf);
+
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+
+ CU_ASSERT_FATAL(from == queue);
+
+ buf = odp_buffer_from_event(ev);
+ u32 = odp_buffer_addr(buf);
+
+ CU_ASSERT_FATAL(u32[0] == MAGIC);
+
+ odp_buffer_free(buf);
+ release_context(qp.sched.sync);
+
+ /* Make sure atomic/ordered context is released */
+ CU_ASSERT(drain_queues() == 0);
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pool_destroy(p) == 0);
+}
+
+static void scheduler_test_wait(void)
+{
+ odp_pool_t p;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue, from;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t *u32;
+ uint32_t i, j, num_enq, retry;
+ int ret;
+ uint32_t num_ev = 50;
+ uint32_t num_retry = 1000;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 10;
+ pool_param.buf.num = num_ev;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ p = odp_pool_create("sched_test_wait", &pool_param);
+
+ CU_ASSERT_FATAL(p != ODP_POOL_INVALID);
+
+ sched_queue_param_init(&queue_param);
+ queue = odp_queue_create("sched_test_wait", &queue_param);
+
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (i = 0; i < 4; i++) {
+ num_enq = 0;
+
+ for (j = 0; j < num_ev; j++) {
+ buf = odp_buffer_alloc(p);
+
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ u32 = odp_buffer_addr(buf);
+ u32[0] = MAGIC;
+
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+ if (ret) {
+ odp_buffer_free(buf);
+ continue;
+ }
+
+ num_enq++;
+ }
+
+ CU_ASSERT(num_enq == num_ev);
+
+ for (j = 0; j < num_enq; j++) {
+ if (i == 0) {
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ } else if (i == 1) {
+ ret = odp_schedule_multi_wait(&from, &ev, 1);
+ CU_ASSERT_FATAL(ret == 1);
+ } else if (i == 2) {
+ retry = 0;
+ do {
+ ev = odp_schedule(&from,
+ ODP_SCHED_NO_WAIT);
+ retry++;
+ } while (ev == ODP_EVENT_INVALID &&
+ retry < num_retry);
+ } else {
+ retry = 0;
+ do {
+ ret = odp_schedule_multi_no_wait(&from,
+ &ev,
+ 1);
+ retry++;
+ } while (ret == 0 && retry < num_retry);
+ CU_ASSERT_FATAL(ret == 1);
+ }
+
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+
+ buf = odp_buffer_from_event(ev);
+ u32 = odp_buffer_addr(buf);
+
+ CU_ASSERT(u32[0] == MAGIC);
+
+ odp_buffer_free(buf);
+ }
+ }
+
+ /* Make sure that scheduler is empty */
+ drain_queues();
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ CU_ASSERT_FATAL(odp_pool_destroy(p) == 0);
+}
+
+static void scheduler_test_queue_size(void)
+{
+ odp_schedule_config_t default_config;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue, from;
+ odp_event_t ev;
+ odp_buffer_t buf;
+ uint32_t i, j, queue_size, num;
+ int ret;
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+
+ queue_size = DEFAULT_NUM_EV;
+
+ /* Scheduler has been already configured. Use default config as max
+ * queue size. */
+ odp_schedule_config_init(&default_config);
+ if (default_config.queue_size &&
+ queue_size > default_config.queue_size)
+ queue_size = default_config.queue_size;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 100;
+ pool_param.buf.align = 0;
+ pool_param.buf.num = DEFAULT_NUM_EV;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("test_queue_size", &pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < 3; i++) {
+ /* Ensure that scheduler is empty */
+ for (j = 0; j < 10;) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ j++;
+ }
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync[i];
+ queue_param.size = queue_size;
+
+ queue = odp_queue_create("test_queue_size", &queue_param);
+
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (j = 0; j < queue_size; j++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+
+ if (ret)
+ odp_event_free(ev);
+ }
+
+ num = 0;
+ for (j = 0; j < 100 * DEFAULT_NUM_EV; j++) {
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ CU_ASSERT(from == queue);
+ odp_event_free(ev);
+ num++;
+ }
+
+ CU_ASSERT(num == queue_size);
+
+ CU_ASSERT(drain_queues() == 0);
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+static void scheduler_test_full_queues(void)
+{
+ odp_schedule_config_t default_config;
+ odp_pool_t pool;
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t pool_param;
+ odp_schedule_capability_t sched_capa;
+ odp_queue_param_t queue_param;
+ odp_event_t ev;
+ uint32_t i, j, k, num_bufs, events_per_queue, num_queues;
+ uint32_t queue_size = 2048;
+ int ret;
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ if (sched_capa.max_queue_size && queue_size > sched_capa.max_queue_size)
+ queue_size = sched_capa.max_queue_size;
+
+ /* Scheduler has been already configured. Use default config as queue
+ * size and queue count. */
+ odp_schedule_config_init(&default_config);
+ if (default_config.queue_size)
+ queue_size = default_config.queue_size;
+ num_queues = default_config.num_queues;
+
+ odp_queue_t queue[num_queues];
+
+ CU_ASSERT_FATAL(!odp_pool_capability(&pool_capa));
+ num_bufs = num_queues * queue_size;
+ if (pool_capa.buf.max_num && num_bufs > pool_capa.buf.max_num)
+ num_bufs = pool_capa.buf.max_num;
+ if (num_bufs > MAX_POOL_SIZE)
+ num_bufs = MAX_POOL_SIZE;
+ events_per_queue = num_bufs / num_queues;
+
+ /* Make sure there is at least one event for each queue */
+ while (events_per_queue == 0) {
+ num_queues--;
+ events_per_queue = num_bufs / num_queues;
+ }
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 100;
+ pool_param.buf.align = 0;
+ pool_param.buf.num = num_bufs;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("test_full_queues", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Ensure that scheduler is empty */
+ drain_queues();
+
+ /* Run test for each scheduler synchronization type */
+ for (i = 0; i < 3; i++) {
+ uint64_t wait_time;
+ uint32_t num_enq = 0;
+ uint32_t num = 0;
+
+ /* Create and fill all queues */
+ for (j = 0; j < num_queues; j++) {
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync[i];
+ queue_param.size = events_per_queue;
+
+ queue[j] = odp_queue_create("test_full_queues",
+ &queue_param);
+ CU_ASSERT_FATAL(queue[j] != ODP_QUEUE_INVALID);
+
+ for (k = 0; k < events_per_queue; k++) {
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ CU_ASSERT(buf != ODP_BUFFER_INVALID);
+ if (buf == ODP_BUFFER_INVALID)
+ continue;
+
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue[j], ev);
+ CU_ASSERT(ret == 0);
+ if (ret) {
+ odp_event_free(ev);
+ continue;
+ }
+ num_enq++;
+ }
+ }
+ /* Run normal scheduling rounds */
+ wait_time = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ for (j = 0; j < num_bufs; j++) {
+ odp_queue_t src_queue;
+
+ ev = odp_schedule(&src_queue, wait_time);
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ ret = odp_queue_enq(src_queue, ev);
+ CU_ASSERT(ret == 0);
+ if (ret) {
+ odp_event_free(ev);
+ num_enq--;
+ }
+ }
+ /* Clean-up */
+ wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ for (j = 0; j < num_enq; j++) {
+ ev = odp_schedule(NULL, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ odp_event_free(ev);
+ num++;
+ }
+
+ CU_ASSERT(num == num_enq);
+ CU_ASSERT(drain_queues() == 0);
+
+ for (j = 0; j < num_queues; j++)
+ CU_ASSERT_FATAL(odp_queue_destroy(queue[j]) == 0);
+ }
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void scheduler_test_max_queues(odp_schedule_sync_t sync)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_schedule_capability_t sched_capa;
+ odp_queue_param_t queue_param;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_queue_t src_queue;
+ uint64_t wait_time;
+ uint32_t i, src_idx;
+ uint32_t num_rounds = 4;
+ uint32_t num_queues = 64 * 1024;
+
+ CU_ASSERT_FATAL(odp_schedule_capability(&sched_capa) == 0);
+ if (num_queues > sched_capa.max_queues)
+ num_queues = sched_capa.max_queues;
+
+ CU_ASSERT_FATAL(num_queues > 0);
+
+ odp_queue_t queue[num_queues];
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.size = 100;
+ pool_param.buf.num = 1;
+
+ pool = odp_pool_create("test_max_queues", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Ensure that scheduler is empty */
+ drain_queues();
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync;
+
+ for (i = 0; i < num_queues; i++) {
+ queue[i] = odp_queue_create("test_max_queues", &queue_param);
+ if (queue[i] == ODP_QUEUE_INVALID)
+ ODPH_ERR("Queue create failed %u/%u\n", i, num_queues);
+
+ CU_ASSERT_FATAL(queue[i] != ODP_QUEUE_INVALID);
+ }
+
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+
+ CU_ASSERT_FATAL(odp_queue_enq(queue[0], ev) == 0);
+
+ wait_time = odp_schedule_wait_time(500 * ODP_TIME_MSEC_IN_NS);
+ src_idx = 0;
+
+ /* Send one event through all queues couple of times */
+ for (i = 0; i < (num_rounds * num_queues); i++) {
+ uint32_t round = i / num_queues;
+
+ ev = odp_schedule(&src_queue, wait_time);
+ if (ev == ODP_EVENT_INVALID) {
+ ODPH_ERR("Event was lost. Round %u, queue idx %u\n", round, src_idx);
+ CU_FAIL("Event was lost\n");
+ break;
+ }
+
+ CU_ASSERT(src_queue == queue[src_idx]);
+
+ src_idx++;
+ if (src_idx == num_queues)
+ src_idx = 0;
+
+ if (odp_queue_enq(queue[src_idx], ev)) {
+ ODPH_ERR("Enqueue failed. Round %u, queue idx %u\n", round, src_idx);
+ CU_FAIL("Enqueue failed\n")
+ odp_event_free(ev);
+ break;
+ }
+ }
+
+ /* Free event and scheduling context */
+ for (i = 0; i < 2; i++) {
+ ev = odp_schedule(NULL, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(drain_queues() == 0);
+
+ for (i = 0; i < num_queues; i++)
+ CU_ASSERT_FATAL(odp_queue_destroy(queue[i]) == 0);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void scheduler_test_max_queues_p(void)
+{
+ scheduler_test_max_queues(ODP_SCHED_SYNC_PARALLEL);
+}
+
+static void scheduler_test_max_queues_a(void)
+{
+ scheduler_test_max_queues(ODP_SCHED_SYNC_ATOMIC);
+}
+
+static void scheduler_test_max_queues_o(void)
+{
+ scheduler_test_max_queues(ODP_SCHED_SYNC_ORDERED);
+}
+
+static void scheduler_test_order_ignore(void)
+{
+ odp_queue_capability_t queue_capa;
+ odp_schedule_config_t default_config;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_queue_t ordered, plain, from;
+ odp_event_t ev;
+ odp_buffer_t buf;
+ uint32_t j, queue_size, num;
+ int ret;
+
+ odp_schedule_config_init(&default_config);
+ CU_ASSERT_FATAL(odp_queue_capability(&queue_capa) == 0);
+
+ queue_size = DEFAULT_NUM_EV;
+ if (default_config.queue_size &&
+ queue_size > default_config.queue_size)
+ queue_size = default_config.queue_size;
+
+ if (queue_capa.plain.max_size &&
+ queue_size > queue_capa.plain.max_size)
+ queue_size = queue_capa.plain.max_size;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 100;
+ pool_param.buf.align = 0;
+ pool_param.buf.num = DEFAULT_NUM_EV;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("test_order_ignore", &pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Ensure that scheduler is empty */
+ for (j = 0; j < 10;) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ j++;
+ }
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+
+ ordered = odp_queue_create("ordered", &queue_param);
+ CU_ASSERT_FATAL(ordered != ODP_QUEUE_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ queue_param.order = ODP_QUEUE_ORDER_IGNORE;
+
+ plain = odp_queue_create("plain", &queue_param);
+ CU_ASSERT_FATAL(plain != ODP_QUEUE_INVALID);
+
+ num = 0;
+ for (j = 0; j < queue_size; j++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(ordered, ev);
+
+ if (ret)
+ odp_event_free(ev);
+ else
+ num++;
+ }
+
+ CU_ASSERT(num == queue_size);
+
+ num = 0;
+ for (j = 0; j < 100 * DEFAULT_NUM_EV; j++) {
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ CU_ASSERT(from == ordered);
+ ret = odp_queue_enq(plain, ev);
+
+ if (ret)
+ odp_event_free(ev);
+ else
+ num++;
+ }
+
+ CU_ASSERT(num == queue_size);
+
+ num = 0;
+ for (j = 0; j < 100 * DEFAULT_NUM_EV; j++) {
+ ev = odp_queue_deq(plain);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ odp_event_free(ev);
+ num++;
+ }
+
+ CU_ASSERT(num == queue_size);
+
+ CU_ASSERT(drain_queues() == 0);
+ CU_ASSERT_FATAL(odp_queue_destroy(ordered) == 0);
+ CU_ASSERT_FATAL(odp_queue_destroy(plain) == 0);
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+static void scheduler_test_group_info_predef(void)
+{
+ odp_schedule_group_info_t info;
+ odp_thrmask_t thrmask;
+ odp_schedule_group_t group;
+ int thr;
+
+ thr = odp_thread_id();
+
+ group = ODP_SCHED_GROUP_ALL;
+ odp_thrmask_zero(&thrmask);
+ CU_ASSERT(odp_schedule_group_thrmask(group, &thrmask) == 0);
+ CU_ASSERT(odp_thrmask_isset(&thrmask, thr));
+ memset(&info, 0, sizeof(odp_schedule_group_info_t));
+ CU_ASSERT(odp_schedule_group_info(group, &info) == 0);
+ CU_ASSERT(odp_thrmask_equal(&info.thrmask, &thrmask));
+ printf("\n Schedule group all name: %s\n", info.name);
+
+ /* This test case runs a control thread */
+ group = ODP_SCHED_GROUP_CONTROL;
+ odp_thrmask_zero(&thrmask);
+ CU_ASSERT(odp_schedule_group_thrmask(group, &thrmask) == 0);
+ CU_ASSERT(odp_thrmask_isset(&thrmask, thr));
+ memset(&info, 0, sizeof(odp_schedule_group_info_t));
+ CU_ASSERT(odp_schedule_group_info(group, &info) == 0);
+ CU_ASSERT(odp_thrmask_equal(&info.thrmask, &thrmask));
+ printf(" Schedule group control name: %s\n", info.name);
+
+ group = ODP_SCHED_GROUP_WORKER;
+ odp_thrmask_zero(&thrmask);
+ CU_ASSERT(odp_schedule_group_thrmask(group, &thrmask) == 0);
+ CU_ASSERT(!odp_thrmask_isset(&thrmask, thr));
+ memset(&info, 0, sizeof(odp_schedule_group_info_t));
+ CU_ASSERT(odp_schedule_group_info(group, &info) == 0);
+ CU_ASSERT(odp_thrmask_equal(&info.thrmask, &thrmask));
+ printf(" Schedule group worker name: %s\n", info.name);
+}
+
+static void scheduler_test_create_group(void)
+{
+ odp_thrmask_t mask;
+ odp_schedule_group_t group;
+ int thr_id;
+ odp_pool_t pool;
+ odp_pool_param_t pool_params;
+ odp_queue_t queue, from;
+ odp_queue_param_t qp;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint64_t wait_time;
+
+ thr_id = odp_thread_id();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ group = odp_schedule_group_create("create_group", &mask);
+ CU_ASSERT_FATAL(group != ODP_SCHED_GROUP_INVALID);
+
+ odp_pool_param_init(&pool_params);
+ pool_params.buf.size = 100;
+ pool_params.buf.num = 2;
+ pool_params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("create_group", &pool_params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sched_queue_param_init(&qp);
+ qp.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qp.sched.group = group;
+
+ queue = odp_queue_create("create_group", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+
+ CU_ASSERT_FATAL(odp_queue_enq(queue, ev) == 0);
+
+ wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ ev = odp_schedule(&from, wait_time);
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ /* Free schedule context */
+ drain_queues();
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(group) == 0);
+
+ /* Run scheduler after the group has been destroyed */
+ CU_ASSERT_FATAL(odp_schedule(NULL, wait_time) == ODP_EVENT_INVALID);
+}
+
+static void scheduler_test_create_max_groups(void)
+{
+ odp_thrmask_t mask;
+ int thr_id;
+ uint32_t i;
+ odp_queue_param_t queue_param;
+ odp_schedule_capability_t sched_capa;
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ uint32_t max_groups = sched_capa.max_groups - 3; /* Enabled predefined groups */
+ odp_schedule_group_t group[max_groups];
+ odp_queue_t queue[max_groups];
+
+ CU_ASSERT_FATAL(max_groups > 0);
+ CU_ASSERT_FATAL(sched_capa.max_queues >= sched_capa.max_groups);
+
+ thr_id = odp_thread_id();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ for (i = 0; i < max_groups; i++) {
+ group[i] = odp_schedule_group_create("max_groups", &mask);
+ if (group[i] == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("schedule group create %u failed\n", i);
+ break;
+ }
+
+ queue_param.sched.group = group[i];
+ queue[i] = odp_queue_create("max_groups", &queue_param);
+ CU_ASSERT_FATAL(queue[i] != ODP_QUEUE_INVALID);
+ }
+
+ CU_ASSERT(i == max_groups);
+ max_groups = i;
+
+ for (i = 0; i < max_groups; i++) {
+ CU_ASSERT_FATAL(odp_queue_destroy(queue[i]) == 0);
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(group[i]) == 0);
+ }
+}
+
+static void scheduler_test_groups(void)
+{
+ odp_pool_t p;
+ odp_pool_param_t params;
+ odp_queue_t queue_grp1, queue_grp2;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t *u32;
+ int i, j, rc;
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+ int thr_id = odp_thread_id();
+ odp_thrmask_t zeromask, mymask, testmask;
+ odp_schedule_group_t mygrp1, mygrp2, null_grp, lookup;
+ odp_schedule_group_info_t info;
+
+ odp_thrmask_zero(&zeromask);
+ odp_thrmask_zero(&mymask);
+ odp_thrmask_set(&mymask, thr_id);
+
+ /* Can't find a group before we create it */
+ lookup = odp_schedule_group_lookup("Test Group 1");
+ CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID);
+
+ /* Now create the group */
+ mygrp1 = odp_schedule_group_create("Test Group 1", &zeromask);
+ CU_ASSERT_FATAL(mygrp1 != ODP_SCHED_GROUP_INVALID);
+
+ /* Verify we can now find it */
+ lookup = odp_schedule_group_lookup("Test Group 1");
+ CU_ASSERT(lookup == mygrp1);
+
+ /* Threadmask should be retrievable and be what we expect */
+ rc = odp_schedule_group_thrmask(mygrp1, &testmask);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
+
+ /* Now join the group and verify we're part of it */
+ rc = odp_schedule_group_join(mygrp1, &mymask);
+ CU_ASSERT(rc == 0);
+
+ rc = odp_schedule_group_thrmask(mygrp1, &testmask);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(odp_thrmask_isset(&testmask, thr_id));
+
+ /* Info struct */
+ memset(&info, 0, sizeof(odp_schedule_group_info_t));
+ rc = odp_schedule_group_info(mygrp1, &info);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(odp_thrmask_equal(&info.thrmask, &mymask) != 0);
+ CU_ASSERT(strcmp(info.name, "Test Group 1") == 0);
+
+ /* We can't join or leave an unknown group */
+ rc = odp_schedule_group_join(ODP_SCHED_GROUP_INVALID, &mymask);
+ CU_ASSERT(rc != 0);
+
+ rc = odp_schedule_group_leave(ODP_SCHED_GROUP_INVALID, &mymask);
+ CU_ASSERT(rc != 0);
+
+ /* But we can leave our group */
+ rc = odp_schedule_group_leave(mygrp1, &mymask);
+ CU_ASSERT(rc == 0);
+
+ rc = odp_schedule_group_thrmask(mygrp1, &testmask);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
+
+ /* Create group with no name */
+ null_grp = odp_schedule_group_create(NULL, &zeromask);
+ CU_ASSERT(null_grp != ODP_SCHED_GROUP_INVALID);
+
+ /* We shouldn't be able to find our second group before creating it */
+ lookup = odp_schedule_group_lookup("Test Group 2");
+ CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID);
+
+ /* Now create it and verify we can find it */
+ mygrp2 = odp_schedule_group_create("Test Group 2", &mymask);
+ CU_ASSERT_FATAL(mygrp2 != ODP_SCHED_GROUP_INVALID);
+
+ lookup = odp_schedule_group_lookup("Test Group 2");
+ CU_ASSERT(lookup == mygrp2);
+
+ /* Destroy group with no name */
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(null_grp) == 0);
+
+ /* Verify we're part of group 2 */
+ rc = odp_schedule_group_thrmask(mygrp2, &testmask);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(odp_thrmask_isset(&testmask, thr_id));
+
+ /* Leave group 2 */
+ rc = odp_schedule_group_leave(mygrp2, &mymask);
+ CU_ASSERT(rc == 0);
+
+ /* Verify we're not part of group 2 anymore */
+ rc = odp_schedule_group_thrmask(mygrp2, &testmask);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id));
+
+ /* Now verify scheduler adherence to groups */
+ odp_pool_param_init(&params);
+ params.buf.size = 100;
+ params.buf.align = 0;
+ params.buf.num = 2;
+ params.type = ODP_POOL_BUFFER;
+
+ p = odp_pool_create("sched_group_pool", &params);
+
+ CU_ASSERT_FATAL(p != ODP_POOL_INVALID);
+
+ for (i = 0; i < 3; i++) {
+ odp_queue_param_t qp;
+ odp_queue_t queue, from;
+ odp_schedule_group_t mygrp[NUM_GROUPS];
+ odp_queue_t queue_grp[NUM_GROUPS];
+ uint64_t wait_time;
+ int num = NUM_GROUPS;
+ int schedule_retries;
+
+ sched_queue_param_init(&qp);
+ qp.sched.sync = sync[i];
+ qp.sched.group = mygrp1;
+
+ /* Create and populate a group in group 1 */
+ queue_grp1 = odp_queue_create("sched_group_test_queue_1", &qp);
+ CU_ASSERT_FATAL(queue_grp1 != ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp1) == mygrp1);
+
+ buf = odp_buffer_alloc(p);
+
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ u32 = odp_buffer_addr(buf);
+ u32[0] = MAGIC1;
+
+ ev = odp_buffer_to_event(buf);
+ rc = odp_queue_enq(queue_grp1, ev);
+ CU_ASSERT(rc == 0);
+ if (rc)
+ odp_buffer_free(buf);
+
+ /* Now create and populate a queue in group 2 */
+ qp.sched.group = mygrp2;
+ queue_grp2 = odp_queue_create("sched_group_test_queue_2", &qp);
+ CU_ASSERT_FATAL(queue_grp2 != ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp2) == mygrp2);
+
+ buf = odp_buffer_alloc(p);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ u32 = odp_buffer_addr(buf);
+ u32[0] = MAGIC2;
+
+ ev = odp_buffer_to_event(buf);
+ rc = odp_queue_enq(queue_grp2, ev);
+ CU_ASSERT(rc == 0);
+ if (rc)
+ odp_buffer_free(buf);
+
+ /* Swap between two groups. Application should serve both
+ * groups to avoid potential head of line blocking in
+ * scheduler. */
+ mygrp[0] = mygrp1;
+ mygrp[1] = mygrp2;
+ queue_grp[0] = queue_grp1;
+ queue_grp[1] = queue_grp2;
+ j = 0;
+
+ /* Ensure that each test run starts from mygrp1 */
+ odp_schedule_group_leave(mygrp1, &mymask);
+ odp_schedule_group_leave(mygrp2, &mymask);
+ odp_schedule_group_join(mygrp1, &mymask);
+
+ wait_time = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ schedule_retries = 0;
+ while (num) {
+ queue = queue_grp[j];
+ ev = odp_schedule(&from, wait_time);
+
+ if (ev == ODP_EVENT_INVALID) {
+ CU_ASSERT_FATAL(schedule_retries <
+ WAIT_1MS_RETRIES);
+ schedule_retries++;
+ continue;
+ } else {
+ schedule_retries = 0;
+ }
+
+ CU_ASSERT_FATAL(from == queue);
+
+ buf = odp_buffer_from_event(ev);
+ u32 = odp_buffer_addr(buf);
+
+ if (from == queue_grp1) {
+ /* CU_ASSERT_FATAL needs these brackets */
+ CU_ASSERT_FATAL(u32[0] == MAGIC1);
+ } else {
+ CU_ASSERT_FATAL(u32[0] == MAGIC2);
+ }
+
+ odp_buffer_free(buf);
+
+ /* Change group */
+ rc = odp_schedule_group_leave(mygrp[j], &mymask);
+ CU_ASSERT_FATAL(rc == 0);
+
+ j = (j + 1) % NUM_GROUPS;
+ rc = odp_schedule_group_join(mygrp[j], &mymask);
+ CU_ASSERT_FATAL(rc == 0);
+
+ /* Tell scheduler we're about to request an event.
+ * Not needed, but a convenient place to test this API.
+ */
+ odp_schedule_prefetch(1);
+
+ num--;
+ }
+
+ /* Release scheduler context and leave groups */
+ odp_schedule_group_join(mygrp1, &mymask);
+ odp_schedule_group_join(mygrp2, &mymask);
+ CU_ASSERT(drain_queues() == 0);
+ odp_schedule_group_leave(mygrp1, &mymask);
+ odp_schedule_group_leave(mygrp2, &mymask);
+
+ /* Done with queues for this round */
+ CU_ASSERT_FATAL(odp_queue_destroy(queue_grp1) == 0);
+ CU_ASSERT_FATAL(odp_queue_destroy(queue_grp2) == 0);
+
+ /* Verify we can no longer find our queues */
+ CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_1") ==
+ ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_2") ==
+ ODP_QUEUE_INVALID);
+ }
+
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp1) == 0);
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp2) == 0);
+ CU_ASSERT_FATAL(odp_pool_destroy(p) == 0);
+}
+
+static int chaos_thread(void *arg)
+{
+ uint64_t i, wait;
+ int rc;
+ chaos_buf *cbuf;
+ odp_event_t ev;
+ odp_queue_t from;
+ thread_args_t *args = (thread_args_t *)arg;
+ test_globals_t *globals = args->globals;
+ int me = odp_thread_id();
+ odp_time_t start_time, end_time, diff;
+
+ if (CHAOS_DEBUG)
+ printf("Chaos thread %d starting...\n", me);
+
+ /* Wait for all threads to start */
+ odp_barrier_wait(&globals->barrier);
+ start_time = odp_time_local();
+
+ /* Run the test */
+ wait = odp_schedule_wait_time(5 * ODP_TIME_MSEC_IN_NS);
+ for (i = 0; i < CHAOS_NUM_ROUNDS; i++) {
+ ev = odp_schedule(&from, wait);
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ cbuf = odp_buffer_addr(odp_buffer_from_event(ev));
+ CU_ASSERT_FATAL(cbuf != NULL);
+ if (CHAOS_DEBUG)
+ printf("Thread %d received event %" PRIu64
+ " seq %" PRIu64
+ " from Q %s, sending to Q %s\n",
+ me, cbuf->evno, cbuf->seqno,
+ globals->
+ chaos_q
+ [CHAOS_PTR_TO_NDX(odp_queue_context(from))].name,
+ globals->
+ chaos_q[cbuf->seqno % CHAOS_NUM_QUEUES].name);
+
+ rc = odp_queue_enq(
+ globals->
+ chaos_q[cbuf->seqno++ % CHAOS_NUM_QUEUES].handle,
+ ev);
+ CU_ASSERT_FATAL(rc == 0);
+ }
+
+ if (CHAOS_DEBUG)
+ printf("Thread %d completed %d rounds...terminating\n",
+ odp_thread_id(), CHAOS_NUM_EVENTS);
+
+ end_time = odp_time_local();
+ diff = odp_time_diff(end_time, start_time);
+
+ printf("Thread %d ends, elapsed time = %" PRIu64 "us\n",
+ odp_thread_id(), odp_time_to_ns(diff) / 1000);
+
+ /* Make sure scheduling context is released */
+ odp_schedule_pause();
+ while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT))
+ != ODP_EVENT_INVALID) {
+ odp_event_free(ev);
+ }
+
+ /* Don't resume scheduling until all threads have finished */
+ odp_barrier_wait(&globals->barrier);
+ odp_schedule_resume();
+
+ drain_queues();
+
+ return 0;
+}
+
+static void chaos_run(unsigned int qtype)
+{
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ odp_queue_param_t qp;
+ odp_buffer_t buf;
+ chaos_buf *cbuf;
+ test_globals_t *globals;
+ thread_args_t *args;
+ odp_shm_t shm;
+ int i, rc;
+ void *arg_ptr;
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+ const unsigned int num_sync = ODPH_ARRAY_SIZE(sync);
+ const char *const qtypes[] = {"parallel", "atomic", "ordered"};
+
+ /* Set up the scheduling environment */
+ shm = odp_shm_lookup(GLOBALS_SHM_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(globals != NULL);
+
+ shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ args = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(args != NULL);
+
+ args->globals = globals;
+
+ odp_pool_param_init(&params);
+ params.buf.size = sizeof(chaos_buf);
+ params.buf.align = 0;
+ params.buf.num = CHAOS_NUM_EVENTS;
+ params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("sched_chaos_pool", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ sched_queue_param_init(&qp);
+
+ for (i = 0; i < CHAOS_NUM_QUEUES; i++) {
+ uint32_t ndx = (qtype == num_sync ? i % num_sync : qtype);
+
+ qp.sched.sync = sync[ndx];
+ snprintf(globals->chaos_q[i].name,
+ sizeof(globals->chaos_q[i].name),
+ "chaos queue %d - %s", i,
+ qtypes[ndx]);
+
+ globals->chaos_q[i].handle =
+ odp_queue_create(globals->chaos_q[i].name, &qp);
+ CU_ASSERT_FATAL(globals->chaos_q[i].handle !=
+ ODP_QUEUE_INVALID);
+ rc = odp_queue_context_set(globals->chaos_q[i].handle,
+ CHAOS_NDX_TO_PTR(i), 0);
+ CU_ASSERT_FATAL(rc == 0);
+ }
+
+ /* Now populate the queues with the initial seed elements */
+ for (i = 0; i < CHAOS_NUM_EVENTS; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ cbuf = odp_buffer_addr(buf);
+ cbuf->evno = i;
+ cbuf->seqno = 0;
+ rc = odp_queue_enq(
+ globals->chaos_q[i % CHAOS_NUM_QUEUES].handle,
+ odp_buffer_to_event(buf));
+ CU_ASSERT_FATAL(rc == 0);
+ }
+
+ arg_ptr = args;
+ odp_cunit_thread_create(globals->num_workers, chaos_thread, &arg_ptr, 0, 0);
+
+ odp_cunit_thread_join(globals->num_workers);
+
+ if (CHAOS_DEBUG)
+ printf("Thread %d returning from chaos threads..cleaning up\n",
+ odp_thread_id());
+
+ for (i = 0; i < CHAOS_NUM_QUEUES; i++) {
+ if (CHAOS_DEBUG)
+ printf("Destroying queue %s\n",
+ globals->chaos_q[i].name);
+ rc = odp_queue_destroy(globals->chaos_q[i].handle);
+ CU_ASSERT(rc == 0);
+ }
+
+ rc = odp_pool_destroy(pool);
+ CU_ASSERT(rc == 0);
+}
+
+static void scheduler_test_parallel(void)
+{
+ chaos_run(0);
+}
+
+static void scheduler_test_atomic(void)
+{
+ chaos_run(1);
+}
+
+static void scheduler_test_ordered(void)
+{
+ chaos_run(2);
+}
+
+static void scheduler_test_chaos(void)
+{
+ chaos_run(3);
+}
+
+static int schedule_common_(void *arg)
+{
+ thread_args_t *args = (thread_args_t *)arg;
+ odp_schedule_sync_t sync;
+ test_globals_t *globals;
+ queue_context *qctx;
+ buf_contents *bctx, *bctx_cpy;
+ odp_pool_t pool;
+ int locked;
+ int num;
+ odp_buffer_t buf;
+ odp_queue_t from;
+
+ globals = args->globals;
+ sync = args->sync;
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ if (args->num_workers > 1)
+ odp_barrier_wait(&globals->barrier);
+
+ while (1) {
+ from = ODP_QUEUE_INVALID;
+ num = 0;
+
+ odp_ticketlock_lock(&globals->lock);
+ if (globals->buf_count == 0) {
+ odp_ticketlock_unlock(&globals->lock);
+ break;
+ }
+ odp_ticketlock_unlock(&globals->lock);
+
+ if (args->enable_schd_multi) {
+ odp_event_t events[BURST_BUF_SIZE],
+ ev_cpy[BURST_BUF_SIZE];
+ odp_buffer_t buf_cpy[BURST_BUF_SIZE];
+ int j;
+
+ num = odp_schedule_multi(&from, ODP_SCHED_NO_WAIT,
+ events, BURST_BUF_SIZE);
+ CU_ASSERT(num >= 0);
+ CU_ASSERT(num <= BURST_BUF_SIZE);
+ if (num == 0)
+ continue;
+
+ if (sync == ODP_SCHED_SYNC_ORDERED) {
+ uint32_t ndx;
+ uint32_t ndx_max;
+ int rc;
+
+ ndx_max = odp_queue_lock_count(from);
+ CU_ASSERT_FATAL(ndx_max > 0);
+
+ qctx = odp_queue_context(from);
+
+ for (j = 0; j < num; j++) {
+ bctx = odp_buffer_addr(
+ odp_buffer_from_event
+ (events[j]));
+
+ buf_cpy[j] = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf_cpy[j] !=
+ ODP_BUFFER_INVALID);
+ bctx_cpy = odp_buffer_addr(buf_cpy[j]);
+ memcpy(bctx_cpy, bctx,
+ sizeof(buf_contents));
+ bctx_cpy->output_sequence =
+ bctx_cpy->sequence;
+ ev_cpy[j] =
+ odp_buffer_to_event(buf_cpy[j]);
+ }
+
+ rc = odp_queue_enq_multi(qctx->pq_handle,
+ ev_cpy, num);
+ CU_ASSERT(rc == num);
+
+ bctx = odp_buffer_addr(
+ odp_buffer_from_event(events[0]));
+ for (ndx = 0; ndx < ndx_max; ndx++) {
+ odp_schedule_order_lock(ndx);
+ CU_ASSERT(bctx->sequence ==
+ qctx->lock_sequence[ndx]);
+ qctx->lock_sequence[ndx] += num;
+ odp_schedule_order_unlock(ndx);
+ }
+ }
+
+ for (j = 0; j < num; j++) {
+ CU_ASSERT(odp_event_is_valid(events[j]) == 1);
+ odp_event_free(events[j]);
+ }
+ } else {
+ odp_event_t ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ CU_ASSERT(odp_event_is_valid(ev) == 1);
+ buf = odp_buffer_from_event(ev);
+ num = 1;
+ if (sync == ODP_SCHED_SYNC_ORDERED) {
+ uint32_t ndx;
+ uint32_t ndx_max;
+ int rc;
+ odp_buffer_t buf_cpy;
+
+ ndx_max = odp_queue_lock_count(from);
+ CU_ASSERT_FATAL(ndx_max > 0);
+
+ qctx = odp_queue_context(from);
+ bctx = odp_buffer_addr(buf);
+ buf_cpy = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf_cpy != ODP_BUFFER_INVALID);
+ bctx_cpy = odp_buffer_addr(buf_cpy);
+ memcpy(bctx_cpy, bctx, sizeof(buf_contents));
+ bctx_cpy->output_sequence = bctx_cpy->sequence;
+
+ rc = odp_queue_enq(qctx->pq_handle,
+ odp_buffer_to_event
+ (buf_cpy));
+ CU_ASSERT(rc == 0);
+
+ for (ndx = 0; ndx < ndx_max; ndx++) {
+ odp_schedule_order_lock(ndx);
+ CU_ASSERT(bctx->sequence ==
+ qctx->lock_sequence[ndx]);
+ qctx->lock_sequence[ndx] += num;
+ odp_schedule_order_unlock(ndx);
+ }
+ }
+
+ odp_buffer_free(buf);
+ }
+
+ if (args->enable_excl_atomic) {
+ locked = odp_spinlock_trylock(&globals->atomic_lock);
+ CU_ASSERT(locked != 0);
+ CU_ASSERT(from != ODP_QUEUE_INVALID);
+ if (locked) {
+ int cnt;
+ odp_time_t time = ODP_TIME_NULL;
+ /* Do some work here to keep the thread busy */
+ for (cnt = 0; cnt < 1000; cnt++)
+ time = odp_time_sum(time,
+ odp_time_local());
+
+ odp_spinlock_unlock(&globals->atomic_lock);
+ }
+ }
+
+ release_context(sync);
+ odp_ticketlock_lock(&globals->lock);
+
+ globals->buf_count -= num;
+
+ if (globals->buf_count < 0) {
+ odp_ticketlock_unlock(&globals->lock);
+ CU_FAIL_FATAL("Buffer counting failed");
+ }
+
+ odp_ticketlock_unlock(&globals->lock);
+ }
+
+ if (args->num_workers > 1)
+ odp_barrier_wait(&globals->barrier);
+
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ locked = odp_ticketlock_trylock(&globals->lock);
+ else
+ locked = 0;
+
+ if (locked && globals->buf_count_cpy > 0) {
+ odp_event_t ev;
+ odp_queue_t pq;
+ uint64_t seq;
+ uint64_t bcount = 0;
+ int i, j;
+ char name[32];
+ uint64_t num_bufs = args->num_bufs;
+ uint64_t buf_count = globals->buf_count_cpy;
+
+ for (i = 0; i < args->num_prio; i++) {
+ for (j = 0; j < args->num_queues; j++) {
+ snprintf(name, sizeof(name),
+ "plain_%d_%d_o", i, j);
+ pq = odp_queue_lookup(name);
+ CU_ASSERT_FATAL(pq != ODP_QUEUE_INVALID);
+
+ seq = 0;
+ while (1) {
+ ev = odp_queue_deq(pq);
+
+ if (ev == ODP_EVENT_INVALID) {
+ CU_ASSERT(seq == num_bufs);
+ break;
+ }
+
+ bctx = odp_buffer_addr(
+ odp_buffer_from_event(ev));
+
+ CU_ASSERT(bctx->sequence == seq);
+ seq++;
+ bcount++;
+ odp_event_free(ev);
+ }
+ }
+ }
+ CU_ASSERT(bcount == buf_count);
+ globals->buf_count_cpy = 0;
+ }
+
+ if (locked)
+ odp_ticketlock_unlock(&globals->lock);
+
+ /* Clear scheduler atomic / ordered context between tests */
+ CU_ASSERT(drain_queues() == 0);
+
+ if (num)
+ printf("\nDROPPED %i events\n\n", num);
+
+ return 0;
+}
+
+static void fill_queues(thread_args_t *args)
+{
+ odp_schedule_sync_t sync;
+ int num_queues, num_prio;
+ odp_pool_t pool;
+ int i, j, k;
+ int buf_count = 0;
+ test_globals_t *globals;
+ char name[32];
+ int ret;
+ odp_buffer_t buf;
+ odp_event_t ev;
+
+ globals = args->globals;
+ sync = args->sync;
+ num_queues = args->num_queues;
+ num_prio = args->num_prio;
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num_prio; i++) {
+ for (j = 0; j < num_queues; j++) {
+ odp_queue_t queue;
+
+ switch (sync) {
+ case ODP_SCHED_SYNC_PARALLEL:
+ snprintf(name, sizeof(name),
+ "sched_%d_%d_n", i, j);
+ break;
+ case ODP_SCHED_SYNC_ATOMIC:
+ snprintf(name, sizeof(name),
+ "sched_%d_%d_a", i, j);
+ break;
+ case ODP_SCHED_SYNC_ORDERED:
+ snprintf(name, sizeof(name),
+ "sched_%d_%d_o", i, j);
+ break;
+ default:
+ CU_ASSERT_FATAL(0);
+ break;
+ }
+
+ queue = odp_queue_lookup(name);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (k = 0; k < args->num_bufs; k++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ if (sync == ODP_SCHED_SYNC_ORDERED) {
+ queue_context *qctx =
+ odp_queue_context(queue);
+ buf_contents *bctx =
+ odp_buffer_addr(buf);
+ bctx->sequence = qctx->sequence++;
+ }
+
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+
+ if (ret)
+ odp_buffer_free(buf);
+ else
+ buf_count++;
+ }
+ }
+ }
+
+ globals->buf_count = buf_count;
+ globals->buf_count_cpy = buf_count;
+}
+
+static void reset_queues(thread_args_t *args)
+{
+ int i, j, k;
+ int num_prio = args->num_prio;
+ int num_queues = args->num_queues;
+ char name[32];
+
+ for (i = 0; i < num_prio; i++) {
+ for (j = 0; j < num_queues; j++) {
+ odp_queue_t queue;
+
+ snprintf(name, sizeof(name),
+ "sched_%d_%d_o", i, j);
+ queue = odp_queue_lookup(name);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (k = 0; k < args->num_bufs; k++) {
+ queue_context *qctx =
+ odp_queue_context(queue);
+ uint32_t ndx;
+ uint32_t ndx_max;
+
+ ndx_max = odp_queue_lock_count(queue);
+ CU_ASSERT_FATAL(ndx_max > 0);
+ qctx->sequence = 0;
+ for (ndx = 0; ndx < ndx_max; ndx++)
+ qctx->lock_sequence[ndx] = 0;
+ }
+ }
+ }
+}
+
+static void schedule_common(odp_schedule_sync_t sync, int num_queues,
+ int num_prio, int enable_schd_multi)
+{
+ thread_args_t args;
+ odp_shm_t shm;
+ test_globals_t *globals;
+
+ shm = odp_shm_lookup(GLOBALS_SHM_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(globals != NULL);
+
+ memset(&args, 0, sizeof(thread_args_t));
+ args.globals = globals;
+ args.sync = sync;
+ args.num_queues = num_queues;
+ args.num_prio = num_prio;
+ args.num_bufs = BUFS_PER_QUEUE;
+ args.num_workers = 1;
+ args.enable_schd_multi = enable_schd_multi;
+ args.enable_excl_atomic = 0; /* Not needed with a single CPU */
+
+ fill_queues(&args);
+
+ schedule_common_(&args);
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ reset_queues(&args);
+}
+
+static void parallel_execute(odp_schedule_sync_t sync, int num_queues,
+ int num_prio, int enable_schd_multi,
+ int enable_excl_atomic)
+{
+ odp_shm_t shm;
+ test_globals_t *globals;
+ thread_args_t *args;
+ void *arg_ptr;
+
+ shm = odp_shm_lookup(GLOBALS_SHM_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(globals != NULL);
+
+ shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ args = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(args != NULL);
+
+ args->globals = globals;
+ args->sync = sync;
+ args->num_queues = num_queues;
+ args->num_prio = num_prio;
+ if (enable_excl_atomic)
+ args->num_bufs = globals->max_sched_queue_size;
+ else
+ args->num_bufs = BUFS_PER_QUEUE;
+ args->num_workers = globals->num_workers;
+ args->enable_schd_multi = enable_schd_multi;
+ args->enable_excl_atomic = enable_excl_atomic;
+
+ fill_queues(args);
+
+ if (globals->test_debug_print)
+ odp_schedule_print();
+
+ /* Create and launch worker threads */
+ arg_ptr = args;
+ odp_cunit_thread_create(globals->num_workers, schedule_common_, &arg_ptr, 0, 0);
+
+ /* Wait for worker threads to terminate */
+ odp_cunit_thread_join(globals->num_workers);
+
+ /* Cleanup ordered queues for next pass */
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ reset_queues(args);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL */
+static void scheduler_test_1q_1t_n(void)
+{
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_ONE);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC */
+static void scheduler_test_1q_1t_a(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED */
+static void scheduler_test_1q_1t_o(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_ONE);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL */
+static void scheduler_test_mq_1t_n(void)
+{
+ /* Only one priority involved in these tests, but use
+ the same number of queues the more general case uses */
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio,
+ ONE_PRIO, SCHD_ONE);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC */
+static void scheduler_test_mq_1t_a(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio,
+ ONE_PRIO, SCHD_ONE);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED */
+static void scheduler_test_mq_1t_o(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio,
+ ONE_PRIO, SCHD_ONE);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL */
+static void scheduler_test_mq_1t_prio_n(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio, prio,
+ SCHD_ONE);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC */
+static void scheduler_test_mq_1t_prio_a(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio, prio,
+ SCHD_ONE);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED */
+static void scheduler_test_mq_1t_prio_o(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio, prio,
+ SCHD_ONE);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL */
+static void scheduler_test_mq_mt_prio_n(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio,
+ prio, SCHD_ONE, DISABLE_EXCL_ATOMIC);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC */
+static void scheduler_test_mq_mt_prio_a(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio, prio,
+ SCHD_ONE, DISABLE_EXCL_ATOMIC);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED */
+static void scheduler_test_mq_mt_prio_o(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio, prio,
+ SCHD_ONE, DISABLE_EXCL_ATOMIC);
+}
+
+/* 1 queue many threads check exclusive access on ATOMIC queues */
+static void scheduler_test_1q_mt_a_excl(void)
+{
+ parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE,
+ ENABLE_EXCL_ATOMIC);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL multi */
+static void scheduler_test_multi_1q_1t_n(void)
+{
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_MULTI);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC multi */
+static void scheduler_test_multi_1q_1t_a(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI);
+}
+
+/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED multi */
+static void scheduler_test_multi_1q_1t_o(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_MULTI);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL multi */
+static void scheduler_test_multi_mq_1t_n(void)
+{
+ /* Only one priority involved in these tests, but use
+ the same number of queues the more general case uses */
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio,
+ ONE_PRIO, SCHD_MULTI);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC multi */
+static void scheduler_test_multi_mq_1t_a(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio,
+ ONE_PRIO, SCHD_MULTI);
+}
+
+/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED multi */
+static void scheduler_test_multi_mq_1t_o(void)
+{
+ schedule_common(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio,
+ ONE_PRIO, SCHD_MULTI);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL multi */
+static void scheduler_test_multi_mq_1t_prio_n(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio, prio,
+ SCHD_MULTI);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC multi */
+static void scheduler_test_multi_mq_1t_prio_a(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio, prio,
+ SCHD_MULTI);
+}
+
+/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED multi */
+static void scheduler_test_multi_mq_1t_prio_o(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ schedule_common(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio, prio,
+ SCHD_MULTI);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL multi */
+static void scheduler_test_multi_mq_mt_prio_n(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_PARALLEL, globals->queues_per_prio,
+ prio, SCHD_MULTI, 0);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC multi */
+static void scheduler_test_multi_mq_mt_prio_a(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio, prio,
+ SCHD_MULTI, 0);
+}
+
+/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED multi */
+static void scheduler_test_multi_mq_mt_prio_o(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ parallel_execute(ODP_SCHED_SYNC_ORDERED, globals->queues_per_prio, prio,
+ SCHD_MULTI, 0);
+}
+
+/* 1 queue many threads check exclusive access on ATOMIC queues multi */
+static void scheduler_test_multi_1q_mt_a_excl(void)
+{
+ parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI,
+ ENABLE_EXCL_ATOMIC);
+}
+
+static void scheduler_test_pause_resume(void)
+{
+ odp_queue_param_t qp;
+ odp_queue_t queue;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_queue_t from;
+ odp_pool_t pool;
+ int i;
+ int local_bufs = 0;
+ int ret;
+
+ sched_queue_param_init(&qp);
+ queue = odp_queue_create("pause_resume", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < NUM_BUFS_PAUSE; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+ buf = odp_buffer_from_event(ev);
+ odp_buffer_free(buf);
+ }
+
+ odp_schedule_pause();
+
+ while (1) {
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ CU_ASSERT(from == queue);
+ buf = odp_buffer_from_event(ev);
+ odp_buffer_free(buf);
+ local_bufs++;
+ }
+
+ CU_ASSERT(local_bufs <= NUM_BUFS_PAUSE - NUM_BUFS_BEFORE_PAUSE);
+
+ odp_schedule_resume();
+
+ for (i = local_bufs + NUM_BUFS_BEFORE_PAUSE; i < NUM_BUFS_PAUSE; i++) {
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT(from == queue);
+ buf = odp_buffer_from_event(ev);
+ odp_buffer_free(buf);
+ }
+
+ CU_ASSERT(drain_queues() == 0);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void scheduler_test_pause_enqueue(void)
+{
+ odp_queue_param_t qp;
+ odp_queue_t queue;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_event_t ev_tbl[NUM_BUFS_BEFORE_PAUSE];
+ odp_queue_t from;
+ odp_pool_t pool;
+ int i;
+ int ret;
+ int local_bufs;
+
+ sched_queue_param_init(&qp);
+ queue = odp_queue_create("pause_enqueue", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < NUM_BUFS_PAUSE; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+ ev_tbl[i] = ev;
+ }
+
+ /* Pause, enqueue, schedule, resume */
+ odp_schedule_pause();
+
+ for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
+ ev = ev_tbl[i];
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ local_bufs = 0;
+ while (1) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ CU_ASSERT(from == queue);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+
+ local_bufs++;
+ CU_ASSERT_FATAL(local_bufs <= NUM_BUFS_PAUSE);
+ }
+
+ odp_schedule_resume();
+
+ for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+ ev_tbl[i] = ev;
+ }
+
+ /* Pause, schedule, enqueue, resume */
+ odp_schedule_pause();
+
+ local_bufs = 0;
+ while (1) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ CU_ASSERT(from == queue);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+
+ local_bufs++;
+ CU_ASSERT_FATAL(local_bufs <= NUM_BUFS_PAUSE - NUM_BUFS_BEFORE_PAUSE);
+ }
+
+ for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) {
+ ev = ev_tbl[i];
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ odp_schedule_resume();
+
+ /* Free all */
+ CU_ASSERT(drain_queues() == NUM_BUFS_PAUSE);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+/* Basic, single threaded ordered lock API testing */
+static void scheduler_test_ordered_lock(void)
+{
+ odp_queue_param_t qp;
+ odp_queue_t queue;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ odp_queue_t from;
+ odp_pool_t pool;
+ int i;
+ int ret;
+ uint32_t lock_count;
+ odp_schedule_capability_t sched_capa;
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+
+ sched_queue_param_init(&qp);
+ qp.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ qp.sched.lock_count = sched_capa.max_ordered_locks;
+
+ queue = odp_queue_create("ordered_lock", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_type(queue) == ODP_QUEUE_TYPE_SCHED);
+ CU_ASSERT_FATAL(odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ORDERED);
+
+ lock_count = odp_queue_lock_count(queue);
+
+ if (lock_count == 0) {
+ printf(" NO ORDERED LOCKS. Ordered locks not tested.\n");
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ return;
+ }
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < BUFS_PER_QUEUE; i++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+
+ if (ret)
+ odp_buffer_free(buf);
+ }
+
+ for (i = 0; i < BUFS_PER_QUEUE / 2; i++) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+ buf = odp_buffer_from_event(ev);
+ odp_schedule_order_lock(0);
+ odp_schedule_order_unlock(0);
+ odp_buffer_free(buf);
+ }
+
+ if (lock_count < 2) {
+ printf(" ONLY ONE ORDERED LOCK. Unlock_lock not tested.\n");
+ CU_ASSERT(drain_queues() == BUFS_PER_QUEUE / 2);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ return;
+ }
+
+ for (i = 0; i < BUFS_PER_QUEUE / 2; i++) {
+ from = ODP_QUEUE_INVALID;
+ ev = odp_schedule(&from, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+ buf = odp_buffer_from_event(ev);
+ odp_schedule_order_lock(0);
+ odp_schedule_order_unlock_lock(0, 1);
+ odp_schedule_order_unlock(1);
+ odp_buffer_free(buf);
+ }
+
+ CU_ASSERT(drain_queues() == 0);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void enqueue_event(odp_queue_t queue)
+{
+ odp_pool_t pool;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ int ret;
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+ ev = odp_buffer_to_event(buf);
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static void scheduler_test_order_wait_1_thread(void)
+{
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ odp_event_t ev;
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ queue = odp_queue_create("ordered queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_type(queue) == ODP_QUEUE_TYPE_SCHED);
+ CU_ASSERT_FATAL(odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ORDERED);
+
+ /* Set up an ordered scheduling context */
+ enqueue_event(queue);
+ ev = odp_schedule(NULL, ODP_SCHED_WAIT);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ odp_event_free(ev);
+
+ /* Check that order wait does not get stuck or crash */
+ odp_schedule_order_wait();
+
+ /* Release the context */
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static int order_wait_helper(void *arg ODP_UNUSED)
+{
+ odp_event_t ev;
+
+ ev = odp_schedule(NULL, odp_schedule_wait_time(ODP_TIME_SEC_IN_NS));
+
+ if (ev != ODP_EVENT_INVALID) {
+ odp_event_free(ev);
+
+ odp_atomic_store_rel_u32(&globals->order_wait.helper_active, 1);
+ odp_atomic_store_rel_u32(&globals->order_wait.helper_ready, 1);
+
+ /* Wait that the main thread can attempt to overtake us */
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
+
+ odp_atomic_store_rel_u32(&globals->order_wait.helper_active, 0);
+ }
+
+ /* We are not interested in further events */
+ odp_schedule_pause();
+ /* Release context */
+ while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT))
+ != ODP_EVENT_INVALID) {
+ /* We got an event that was meant for the main thread */
+ odp_event_free(ev);
+ }
+
+ return 0;
+}
+
+static void scheduler_test_order_wait_2_threads(void)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ int ret;
+ odp_time_t start;
+ odp_event_t ev;
+ int num = 1;
+
+ CU_ASSERT(!odp_schedule_capability(&sched_capa));
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ queue = odp_queue_create("ordered queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+ CU_ASSERT_FATAL(odp_queue_type(queue) == ODP_QUEUE_TYPE_SCHED);
+ CU_ASSERT_FATAL(odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ORDERED);
+
+ odp_atomic_init_u32(&globals->order_wait.helper_ready, 0);
+ odp_atomic_init_u32(&globals->order_wait.helper_active, 0);
+
+ ret = odp_cunit_thread_create(num, order_wait_helper, NULL, 0, 0);
+ CU_ASSERT_FATAL(ret == num);
+
+ /* Send an event to the helper thread */
+ enqueue_event(queue);
+
+ /* Wait that the helper thread gets the event */
+ start = odp_time_local();
+ while (!odp_atomic_load_acq_u32(&globals->order_wait.helper_ready)) {
+ odp_time_t now = odp_time_local();
+
+ if (odp_time_diff_ns(now, start) > ODP_TIME_SEC_IN_NS) {
+ CU_FAIL("Timeout waiting for helper\n");
+ break;
+ }
+ }
+
+ /* Try to send an event to ourselves */
+ enqueue_event(queue);
+ /*
+ * If ordered queues are implemented as atomic queues, the schedule
+ * call here will not return anything until the helper thread has
+ * released the scheduling context of the first event. So we have
+ * to wait long enough before giving up.
+ */
+ ev = odp_schedule(NULL, odp_schedule_wait_time(2 * ODP_TIME_SEC_IN_NS));
+ if (ev == ODP_EVENT_INVALID) {
+ /* Helper thread got the event. Give up. */
+ printf("SKIPPED...");
+ goto out;
+ }
+ odp_event_free(ev);
+
+ /*
+ * We are now in an ordered scheduling context and behind the helper
+ * thread in source queue order if the helper thread has not released
+ * the scheuduling context.
+ */
+
+ if (!odp_atomic_load_acq_u32(&globals->order_wait.helper_active)) {
+ /*
+ * Helper thread has released the context already.
+ * We cannot test order wait fully.
+ */
+ printf("reduced test...");
+ }
+
+ /*
+ * The function we are testing: Wait until there are no scheduling
+ * contexts that precede ours.
+ */
+ odp_schedule_order_wait();
+
+ /*
+ * If order wait is supported, we are now first in the source queue
+ * order, so the helper thread must have released its context.
+ */
+ if (sched_capa.order_wait)
+ CU_ASSERT(!odp_atomic_load_acq_u32(&globals->order_wait.helper_active));
+
+ /* Release the context */
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+
+out:
+ CU_ASSERT(odp_cunit_thread_join(num) == 0);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static int sched_and_plain_thread(void *arg)
+{
+ odp_event_t ev1, ev2;
+ thread_args_t *args = (thread_args_t *)arg;
+ test_globals_t *globals = args->globals;
+ odp_queue_t sched_queue = globals->sched_and_plain_q.sched;
+ odp_queue_t plain_queue = globals->sched_and_plain_q.plain;
+ odp_schedule_sync_t sync = odp_queue_sched_type(sched_queue);
+ uint64_t i, wait;
+
+ /* Wait for all threads to start */
+ odp_barrier_wait(&globals->barrier);
+
+ /* Run the test */
+ wait = odp_schedule_wait_time(10 * ODP_TIME_MSEC_IN_NS);
+ for (i = 0; i < SCHED_AND_PLAIN_ROUNDS; i++) {
+ uint32_t rand_val;
+
+ /* Dequeue events from scheduled and plain queues */
+ ev1 = odp_schedule(NULL, wait);
+ if (ev1 == ODP_EVENT_INVALID)
+ continue;
+
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ odp_schedule_order_lock(0);
+
+ ev2 = odp_queue_deq(plain_queue);
+ CU_ASSERT_FATAL(ev2 != ODP_EVENT_INVALID);
+
+ /* Add random delay to stress scheduler implementation */
+ odp_random_data((uint8_t *)&rand_val, sizeof(rand_val),
+ ODP_RANDOM_BASIC);
+ odp_time_wait_ns(rand_val % ODP_TIME_USEC_IN_NS);
+
+ /* Enqueue events back to the end of the queues */
+ CU_ASSERT_FATAL(!odp_queue_enq(plain_queue, ev2));
+
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ odp_schedule_order_unlock(0);
+
+ CU_ASSERT_FATAL(!odp_queue_enq(sched_queue, ev1));
+ }
+
+ /* Make sure scheduling context is released */
+ odp_schedule_pause();
+ while ((ev1 = odp_schedule(NULL, ODP_SCHED_NO_WAIT)) != ODP_EVENT_INVALID) {
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ odp_schedule_order_lock(0);
+
+ ev2 = odp_queue_deq(plain_queue);
+ CU_ASSERT_FATAL(ev2 != ODP_EVENT_INVALID);
+
+ CU_ASSERT_FATAL(!odp_queue_enq(plain_queue, ev2));
+
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ odp_schedule_order_unlock(0);
+
+ CU_ASSERT_FATAL(!odp_queue_enq(sched_queue, ev1));
+ }
+
+ /* Don't resume scheduling until all threads have finished */
+ odp_barrier_wait(&globals->barrier);
+ odp_schedule_resume();
+
+ return 0;
+}
+
+static void scheduler_test_sched_and_plain(odp_schedule_sync_t sync)
+{
+ thread_args_t *args;
+ test_globals_t *globals;
+ odp_queue_t sched_queue;
+ odp_queue_t plain_queue;
+ odp_pool_t pool;
+ odp_queue_param_t queue_param;
+ odp_pool_param_t pool_param;
+ odp_queue_capability_t queue_capa;
+ odp_schedule_capability_t sched_capa;
+ odp_shm_t shm;
+ odp_event_t ev;
+ uint32_t *buf_data;
+ uint32_t seq;
+ uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ uint32_t events_per_queue = BUFS_PER_QUEUE / 2;
+ uint32_t prev_seq;
+ int first;
+ void *arg_ptr;
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ CU_ASSERT_FATAL(!odp_queue_capability(&queue_capa))
+
+ if (sync == ODP_SCHED_SYNC_ORDERED &&
+ sched_capa.max_ordered_locks == 0) {
+ printf("\n NO ORDERED LOCKS. scheduler_test_ordered_and_plain skipped.\n");
+ return;
+ }
+
+ /* Set up the scheduling environment */
+ shm = odp_shm_lookup(GLOBALS_SHM_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(globals != NULL);
+
+ shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ args = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(args != NULL);
+ args->globals = globals;
+
+ /* Make sure all events fit to queues */
+ if (sched_capa.max_queue_size &&
+ sched_capa.max_queue_size < events_per_queue)
+ events_per_queue = sched_capa.max_queue_size;
+ if (queue_capa.plain.max_size &&
+ queue_capa.plain.max_size < events_per_queue)
+ events_per_queue = queue_capa.plain.max_size;
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync;
+ queue_param.size = events_per_queue;
+ if (sync == ODP_SCHED_SYNC_ORDERED)
+ queue_param.sched.lock_count = 1;
+
+ sched_queue = odp_queue_create(NULL, &queue_param);
+ CU_ASSERT_FATAL(sched_queue != ODP_QUEUE_INVALID);
+ globals->sched_and_plain_q.sched = sched_queue;
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ queue_param.size = events_per_queue;
+
+ plain_queue = odp_queue_create(NULL, &queue_param);
+ CU_ASSERT_FATAL(sched_queue != ODP_QUEUE_INVALID);
+ globals->sched_and_plain_q.plain = plain_queue;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 100;
+ pool_param.buf.num = 2 * events_per_queue;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("sched_to_plain_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ /* Create and enq test events with sequential sequence numbers */
+ for (seq = 0; seq < events_per_queue; seq++) {
+ odp_buffer_t buf1, buf2;
+
+ buf1 = odp_buffer_alloc(pool);
+ if (buf1 == ODP_BUFFER_INVALID)
+ break;
+ buf2 = odp_buffer_alloc(pool);
+ if (buf2 == ODP_BUFFER_INVALID) {
+ odp_buffer_free(buf1);
+ break;
+ }
+ buf_data = odp_buffer_addr(buf1);
+ *buf_data = seq;
+ buf_data = odp_buffer_addr(buf2);
+ *buf_data = seq;
+
+ /* Events flow id is 0 by default */
+ CU_ASSERT_FATAL(!odp_queue_enq(sched_queue,
+ odp_buffer_to_event(buf1)));
+ CU_ASSERT_FATAL(!odp_queue_enq(plain_queue,
+ odp_buffer_to_event(buf2)));
+ }
+ CU_ASSERT_FATAL(seq > 2);
+
+ arg_ptr = args;
+ odp_cunit_thread_create(globals->num_workers, sched_and_plain_thread, &arg_ptr, 0, 0);
+
+ odp_cunit_thread_join(globals->num_workers);
+
+ /* Check plain queue sequence numbers and free events */
+ first = 1;
+ while (1) {
+ ev = odp_queue_deq(plain_queue);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ buf_data = odp_buffer_addr(odp_buffer_from_event(ev));
+ seq = *buf_data;
+
+ if (first) {
+ first = 0;
+ prev_seq = seq;
+ continue;
+ }
+
+ CU_ASSERT(seq == prev_seq + 1 || seq == 0)
+ prev_seq = seq;
+ odp_event_free(ev);
+ }
+
+ /* Check scheduled queue sequence numbers and free events */
+ first = 1;
+ while (1) {
+ ev = odp_schedule(NULL, wait);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ buf_data = odp_buffer_addr(odp_buffer_from_event(ev));
+ seq = *buf_data;
+
+ if (first) {
+ first = 0;
+ prev_seq = seq;
+ continue;
+ }
+
+ CU_ASSERT(seq == prev_seq + 1 || seq == 0)
+ prev_seq = seq;
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(!odp_queue_destroy(sched_queue));
+ CU_ASSERT(!odp_queue_destroy(plain_queue));
+ CU_ASSERT(!odp_pool_destroy(pool));
+}
+
+static void scheduler_test_atomic_and_plain(void)
+{
+ scheduler_test_sched_and_plain(ODP_SCHED_SYNC_ATOMIC);
+}
+
+static void scheduler_test_ordered_and_plain(void)
+{
+ scheduler_test_sched_and_plain(ODP_SCHED_SYNC_ORDERED);
+}
+
+static void scheduler_fifo_init(odp_schedule_sync_t sync, int multi, uint32_t num_thr)
+{
+ odp_queue_t queue;
+ odp_pool_t pool;
+ odp_buffer_t buf;
+ uint32_t *seq;
+ uint32_t i;
+ odp_queue_param_t queue_param;
+ odp_pool_param_t pool_param;
+ odp_schedule_capability_t sched_capa;
+ uint32_t num_events = FIFO_MAX_EVENTS;
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+
+ /* Make sure events fit into the queue */
+ if (sched_capa.max_queue_size && num_events > sched_capa.max_queue_size)
+ num_events = sched_capa.max_queue_size;
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync;
+ queue_param.size = num_events;
+
+ queue = odp_queue_create("sched_fifo", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.size = 32;
+ pool_param.buf.num = num_events;
+
+ pool = odp_pool_create("sched_fifo", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num_events; i++) {
+ buf = odp_buffer_alloc(pool);
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ seq = odp_buffer_addr(buf);
+ *seq = i;
+ globals->fifo.event[i] = odp_buffer_to_event(buf);
+ }
+
+ CU_ASSERT_FATAL(i == num_events);
+
+ odp_barrier_init(&globals->fifo.barrier, num_thr);
+
+ globals->fifo.multi = multi;
+ globals->fifo.queue = queue;
+ globals->fifo.pool = pool;
+ globals->fifo.num_events = num_events;
+ globals->fifo.num_enq = 0;
+ globals->fifo.burst = 0;
+ globals->fifo.num_thr = num_thr;
+ odp_atomic_init_u32(&globals->fifo.cur_thr, 0);
+}
+
+static int scheduler_fifo_test(void *arg)
+{
+ odp_queue_t from;
+ odp_buffer_t buf;
+ int ret;
+ uint32_t *seq;
+ uint32_t i, num, cur_thr;
+ uint32_t num_enq = 0;
+ uint32_t thr;
+ uint16_t num_thr = globals->fifo.num_thr;
+ uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ int multi = globals->fifo.multi;
+ odp_queue_t queue = globals->fifo.queue;
+ odp_pool_t pool = globals->fifo.pool;
+ uint32_t num_events = globals->fifo.num_events;
+ odp_event_t events[num_events];
+
+ /* Thread index as argument */
+ thr = (uintptr_t)arg;
+
+ odp_barrier_wait(&globals->fifo.barrier);
+
+ /* Threads enqueue events in round robin */
+ while (1) {
+ cur_thr = odp_atomic_load_acq_u32(&globals->fifo.cur_thr);
+ if (cur_thr != thr)
+ continue;
+
+ num_enq = globals->fifo.num_enq;
+
+ if (num_enq >= num_events) {
+ odp_atomic_store_u32(&globals->fifo.cur_thr, (cur_thr + 1) % num_thr);
+ break;
+ }
+
+ if (multi) {
+ num = globals->fifo.burst + 1;
+ globals->fifo.burst = num % 10;
+
+ if (num > (num_events - num_enq))
+ num = num_events - num_enq;
+
+ ret = odp_queue_enq_multi(queue, &globals->fifo.event[num_enq], num);
+ CU_ASSERT(ret > 0);
+ CU_ASSERT_FATAL(ret <= (int)num);
+ } else {
+ ret = odp_queue_enq(queue, globals->fifo.event[num_enq]);
+ CU_ASSERT(ret == 0);
+ if (ret == 0)
+ ret = 1;
+ }
+
+ if (ret > 0)
+ globals->fifo.num_enq += ret;
+
+ odp_atomic_store_rel_u32(&globals->fifo.cur_thr, (cur_thr + 1) % num_thr);
+ }
+
+ odp_barrier_wait(&globals->fifo.barrier);
+
+ if (thr != 0)
+ return 0;
+
+ /* Thread 0 checks event order and destroys queue/pool */
+ CU_ASSERT(globals->fifo.num_enq == num_events);
+ if (globals->fifo.num_enq > num_events)
+ return -1;
+
+ num_events = globals->fifo.num_enq;
+
+ for (i = 0; i < num_events; i++)
+ events[i] = ODP_EVENT_INVALID;
+
+ num = 0;
+
+ while (1) {
+ uint32_t num_recv;
+ int max_num = 3;
+ odp_event_t ev[max_num];
+
+ from = ODP_QUEUE_INVALID;
+
+ if (multi) {
+ ret = odp_schedule_multi(&from, wait, ev, max_num);
+ CU_ASSERT_FATAL(ret >= 0 && ret <= max_num);
+
+ if (ret == 0)
+ break;
+ } else {
+ ev[0] = odp_schedule(&from, wait);
+ if (ev[0] == ODP_EVENT_INVALID)
+ break;
+
+ ret = 1;
+ }
+
+ num_recv = ret;
+ CU_ASSERT(num < num_events);
+
+ if (num >= num_events) {
+ /* Drop extra events */
+ odp_event_free_multi(ev, num_recv);
+ continue;
+ }
+
+ for (i = 0; i < num_recv; i++) {
+ CU_ASSERT(odp_event_type(ev[i]) == ODP_EVENT_BUFFER);
+ events[num] = ev[i];
+ num++;
+ }
+
+ CU_ASSERT(from == queue);
+ }
+
+ CU_ASSERT(num == num_events);
+
+ for (i = 0; i < num; i++) {
+ buf = odp_buffer_from_event(events[i]);
+ seq = odp_buffer_addr(buf);
+
+ CU_ASSERT(*seq == i);
+
+ if (*seq != i)
+ ODPH_ERR("Bad sequence number %u, expected %u\n", *seq, i);
+
+ odp_buffer_free(buf);
+ }
+
+ CU_ASSERT_FATAL(!odp_queue_destroy(queue));
+ CU_ASSERT_FATAL(!odp_pool_destroy(pool));
+
+ return 0;
+}
+
+static void scheduler_fifo_parallel_single(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_PARALLEL, 0, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_parallel_multi(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_PARALLEL, 1, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_atomic_single(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_ATOMIC, 0, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_atomic_multi(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_ATOMIC, 1, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_ordered_single(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_ORDERED, 0, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_ordered_multi(void)
+{
+ scheduler_fifo_init(ODP_SCHED_SYNC_ORDERED, 1, 1);
+ scheduler_fifo_test(0);
+}
+
+static void scheduler_fifo_mt(odp_schedule_sync_t sync, int multi)
+{
+ uint32_t i;
+ uint32_t num_thr = globals->num_workers;
+ uintptr_t arg[num_thr];
+
+ scheduler_fifo_init(sync, multi, num_thr);
+
+ for (i = 0; i < num_thr; i++)
+ arg[i] = i;
+
+ odp_cunit_thread_create(num_thr, scheduler_fifo_test, (void **)&arg[0], 1, 0);
+
+ /* Wait for worker threads to terminate */
+ odp_cunit_thread_join(num_thr);
+}
+
+static void scheduler_fifo_mt_parallel_single(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_PARALLEL, 0);
+}
+
+static void scheduler_fifo_mt_parallel_multi(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_PARALLEL, 1);
+}
+
+static void scheduler_fifo_mt_atomic_single(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_ATOMIC, 0);
+}
+
+static void scheduler_fifo_mt_atomic_multi(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_ATOMIC, 1);
+}
+
+static void scheduler_fifo_mt_ordered_single(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_ORDERED, 0);
+}
+
+static void scheduler_fifo_mt_ordered_multi(void)
+{
+ scheduler_fifo_mt(ODP_SCHED_SYNC_ORDERED, 1);
+}
+
+static int atomicity_test_run(void *arg)
+{
+ thread_args_t *args = (thread_args_t *)arg;
+ odp_event_t ev;
+ odp_queue_t atomic_queue = args->globals->atomicity_q.handle;
+ odp_queue_t from;
+ odp_atomic_u32_t *state;
+ uint32_t old;
+ uint32_t num_processed = 0;
+
+ if (args->num_workers > 1)
+ odp_barrier_wait(&globals->barrier);
+
+ while (num_processed < ATOMICITY_ROUNDS) {
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ CU_ASSERT(from == atomic_queue);
+ if (from != atomic_queue) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ state = odp_queue_context(from);
+ CU_ASSERT_FATAL(state != NULL);
+
+ old = 0;
+ CU_ASSERT_FATAL(odp_atomic_cas_acq_rel_u32(state, &old, 1));
+
+ /* Hold atomic context a while to better reveal possible atomicity bugs */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+
+ old = 1;
+ CU_ASSERT_FATAL(odp_atomic_cas_acq_rel_u32(state, &old, 0));
+
+ CU_ASSERT_FATAL(odp_queue_enq(from, ev) == 0);
+
+ num_processed++;
+ }
+
+ /* Release atomic context and get rid of possible prescheduled events */
+ odp_schedule_pause();
+ while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT)) != ODP_EVENT_INVALID)
+ CU_ASSERT_FATAL(odp_queue_enq(atomic_queue, ev) == 0);
+
+ if (args->num_workers > 1)
+ odp_barrier_wait(&globals->barrier);
+
+ odp_schedule_resume();
+ drain_queues();
+
+ return 0;
+}
+
+static void scheduler_test_atomicity(void)
+{
+ odp_shm_t shm;
+ test_globals_t *globals;
+ thread_args_t *args;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_queue_param_t queue_param;
+ int i;
+ void *arg_ptr;
+
+ shm = odp_shm_lookup(GLOBALS_SHM_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ globals = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(globals != NULL);
+
+ shm = odp_shm_lookup(SHM_THR_ARGS_NAME);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+ args = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(args != NULL);
+
+ pool = odp_pool_lookup(MSG_POOL_NAME);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ queue_param.size = globals->max_sched_queue_size;
+ queue_param.context = &globals->atomicity_q.state;
+ queue_param.context_len = sizeof(globals->atomicity_q.state);
+
+ queue = odp_queue_create("atomicity_test", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (i = 0; i < BUFS_PER_QUEUE; i++) {
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ CU_ASSERT_FATAL(odp_queue_enq(queue, odp_buffer_to_event(buf)) == 0);
+ }
+ globals->atomicity_q.handle = queue;
+ odp_atomic_init_u32(&globals->atomicity_q.state, 0);
+
+ /* Create and launch worker threads */
+ args->num_workers = globals->num_workers;
+ arg_ptr = args;
+ odp_cunit_thread_create(globals->num_workers, atomicity_test_run, &arg_ptr, 0, 0);
+
+ /* Wait for worker threads to terminate */
+ odp_cunit_thread_join(globals->num_workers);
+
+ odp_queue_destroy(globals->atomicity_q.handle);
+}
+
+static int create_queues(test_globals_t *globals)
+{
+ int i, j, prios, rc;
+ odp_queue_capability_t queue_capa;
+ odp_schedule_capability_t sched_capa;
+ odp_schedule_config_t default_config;
+ odp_pool_t queue_ctx_pool;
+ odp_pool_param_t params;
+ odp_buffer_t queue_ctx_buf;
+ queue_context *qctx, *pqctx;
+ uint32_t ndx;
+ odp_queue_param_t p;
+ unsigned int num_sched;
+ unsigned int num_plain;
+ int queues_per_prio;
+ int sched_types;
+
+ if (odp_queue_capability(&queue_capa) < 0) {
+ ODPH_ERR("Queue capability query failed\n");
+ return -1;
+ }
+
+ if (odp_schedule_capability(&sched_capa) < 0) {
+ ODPH_ERR("Queue capability query failed\n");
+ return -1;
+ }
+
+ /* Limit to test maximum */
+ if (sched_capa.max_ordered_locks > MAX_ORDERED_LOCKS) {
+ sched_capa.max_ordered_locks = MAX_ORDERED_LOCKS;
+ printf("Testing only %u ordered locks\n",
+ sched_capa.max_ordered_locks);
+ }
+
+ globals->max_sched_queue_size = BUFS_PER_QUEUE_EXCL;
+ odp_schedule_config_init(&default_config);
+ if (default_config.queue_size &&
+ globals->max_sched_queue_size > default_config.queue_size) {
+ printf("Max sched queue size %u\n", default_config.queue_size);
+ globals->max_sched_queue_size = default_config.queue_size;
+ }
+
+ prios = odp_schedule_num_prio();
+
+ /* Adjust 'queues_per_prio' until all required queues can be created */
+ sched_types = 3;
+ queues_per_prio = QUEUES_PER_PRIO;
+ num_sched = (prios * queues_per_prio * sched_types) + CHAOS_NUM_QUEUES;
+ num_plain = (prios * queues_per_prio);
+ while ((num_sched > default_config.num_queues ||
+ num_plain > queue_capa.plain.max_num ||
+ num_sched + num_plain > queue_capa.max_queues) &&
+ queues_per_prio) {
+ queues_per_prio--;
+ num_sched = (prios * queues_per_prio * sched_types) +
+ CHAOS_NUM_QUEUES;
+ num_plain = (prios * queues_per_prio);
+ }
+ if (!queues_per_prio) {
+ ODPH_ERR("Not enough queues. At least %d scheduled queues and "
+ "%d plain queues required.\n",
+ ((prios * sched_types) + CHAOS_NUM_QUEUES), prios);
+ return -1;
+ }
+ globals->queues_per_prio = queues_per_prio;
+
+ odp_pool_param_init(&params);
+ params.buf.size = sizeof(queue_context);
+ params.buf.num = prios * queues_per_prio * 2;
+ params.type = ODP_POOL_BUFFER;
+
+ queue_ctx_pool = odp_pool_create(QUEUE_CTX_POOL_NAME, &params);
+
+ if (queue_ctx_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool creation failed (queue ctx)\n");
+ return -1;
+ }
+ globals->queue_ctx_pool = queue_ctx_pool;
+
+ for (i = 0; i < prios; i++) {
+ odp_queue_param_init(&p);
+ p.type = ODP_QUEUE_TYPE_SCHED;
+ p.sched.prio = i;
+
+ for (j = 0; j < queues_per_prio; j++) {
+ /* Per sched sync type */
+ char name[32];
+ odp_queue_t q, pq;
+
+ snprintf(name, sizeof(name), "sched_%d_%d_n", i, j);
+ p.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ q = odp_queue_create(name, &p);
+
+ if (q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Parallel queue create failed\n");
+ return -1;
+ }
+
+ snprintf(name, sizeof(name), "sched_%d_%d_a", i, j);
+ p.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ p.size = globals->max_sched_queue_size;
+ q = odp_queue_create(name, &p);
+
+ if (q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Atomic queue create failed\n");
+ return -1;
+ }
+
+ snprintf(name, sizeof(name), "plain_%d_%d_o", i, j);
+ pq = odp_queue_create(name, NULL);
+ if (pq == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Plain queue create failed\n");
+ return -1;
+ }
+
+ queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool);
+
+ if (queue_ctx_buf == ODP_BUFFER_INVALID) {
+ ODPH_ERR("Cannot allocate plain queue ctx buf\n");
+ return -1;
+ }
+
+ pqctx = odp_buffer_addr(queue_ctx_buf);
+ pqctx->ctx_handle = queue_ctx_buf;
+ pqctx->sequence = 0;
+
+ rc = odp_queue_context_set(pq, pqctx, 0);
+
+ if (rc != 0) {
+ ODPH_ERR("Cannot set plain queue context\n");
+ return -1;
+ }
+
+ snprintf(name, sizeof(name), "sched_%d_%d_o", i, j);
+ p.sched.sync = ODP_SCHED_SYNC_ORDERED;
+ p.sched.lock_count = sched_capa.max_ordered_locks;
+ p.size = 0;
+ q = odp_queue_create(name, &p);
+
+ if (q == ODP_QUEUE_INVALID) {
+ ODPH_ERR("Ordered queue create failed\n");
+ return -1;
+ }
+ if (odp_queue_lock_count(q) !=
+ sched_capa.max_ordered_locks) {
+ printf("Queue %" PRIu64 " created with "
+ "%d locks instead of expected %d\n",
+ odp_queue_to_u64(q),
+ odp_queue_lock_count(q),
+ sched_capa.max_ordered_locks);
+ return -1;
+ }
+
+ queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool);
+
+ if (queue_ctx_buf == ODP_BUFFER_INVALID) {
+ ODPH_ERR("Cannot allocate queue ctx buf\n");
+ return -1;
+ }
+
+ qctx = odp_buffer_addr(queue_ctx_buf);
+ qctx->ctx_handle = queue_ctx_buf;
+ qctx->pq_handle = pq;
+ qctx->sequence = 0;
+
+ for (ndx = 0;
+ ndx < sched_capa.max_ordered_locks;
+ ndx++) {
+ qctx->lock_sequence[ndx] = 0;
+ }
+
+ rc = odp_queue_context_set(q, qctx, 0);
+
+ if (rc != 0) {
+ ODPH_ERR("Cannot set queue context\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_queue(const char *name)
+{
+ odp_queue_t q;
+ queue_context *qctx;
+
+ q = odp_queue_lookup(name);
+
+ if (q == ODP_QUEUE_INVALID)
+ return -1;
+ qctx = odp_queue_context(q);
+ if (qctx)
+ odp_buffer_free(qctx->ctx_handle);
+
+ return odp_queue_destroy(q);
+}
+
+static int destroy_queues(void)
+{
+ int i, j, prios;
+
+ prios = odp_schedule_num_prio();
+
+ for (i = 0; i < prios; i++) {
+ for (j = 0; j < globals->queues_per_prio; j++) {
+ char name[32];
+
+ snprintf(name, sizeof(name), "sched_%d_%d_n", i, j);
+ if (destroy_queue(name) != 0)
+ return -1;
+
+ snprintf(name, sizeof(name), "sched_%d_%d_a", i, j);
+ if (destroy_queue(name) != 0)
+ return -1;
+
+ snprintf(name, sizeof(name), "sched_%d_%d_o", i, j);
+ if (destroy_queue(name) != 0)
+ return -1;
+
+ snprintf(name, sizeof(name), "plain_%d_%d_o", i, j);
+ if (destroy_queue(name) != 0)
+ return -1;
+ }
+ }
+
+ if (odp_pool_destroy(globals->queue_ctx_pool) != 0) {
+ ODPH_ERR("Failed to destroy queue ctx pool\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_flow_aware_support(void)
+{
+ if (globals->num_flows == 0) {
+ printf("\nTest: scheduler_test_flow_aware: SKIPPED\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void scheduler_test_flow_aware(void)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_schedule_config_t sched_config;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue, from;
+ uint32_t j, queue_size, num, num_flows, flow_id;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ int i, ret;
+ uint32_t flow_stat[MAX_FLOWS];
+ odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL,
+ ODP_SCHED_SYNC_ATOMIC,
+ ODP_SCHED_SYNC_ORDERED};
+
+ /* Test should be skipped when no flows */
+ CU_ASSERT_FATAL(globals->num_flows);
+ CU_ASSERT_FATAL(odp_schedule_capability(&sched_capa) == 0);
+
+ num_flows = globals->num_flows;
+
+ queue_size = FLOW_TEST_NUM_EV;
+ odp_schedule_config_init(&sched_config);
+ if (sched_config.queue_size &&
+ queue_size > sched_config.queue_size)
+ queue_size = sched_config.queue_size;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = 100;
+ pool_param.buf.align = 0;
+ pool_param.buf.num = FLOW_TEST_NUM_EV;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("test_flow_aware", &pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < 3; i++) {
+ memset(flow_stat, 0, sizeof(flow_stat));
+ flow_id = 0;
+
+ sched_queue_param_init(&queue_param);
+ queue_param.sched.sync = sync[i];
+ queue_param.size = queue_size;
+
+ queue = odp_queue_create("test_flow_aware", &queue_param);
+
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ for (j = 0; j < queue_size; j++) {
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+
+ odp_event_flow_id_set(ev, flow_id);
+ CU_ASSERT(odp_event_flow_id(ev) == flow_id);
+
+ ret = odp_queue_enq(queue, ev);
+ CU_ASSERT(ret == 0);
+
+ if (ret) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ flow_stat[flow_id]++;
+
+ flow_id++;
+ if (flow_id == num_flows)
+ flow_id = 0;
+ }
+
+ num = 0;
+ for (j = 0; j < 100 * FLOW_TEST_NUM_EV; j++) {
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ CU_ASSERT(from == queue);
+
+ flow_id = odp_event_flow_id(ev);
+ flow_stat[flow_id]--;
+
+ odp_event_free(ev);
+ num++;
+ }
+
+ CU_ASSERT(num == queue_size);
+
+ for (j = 0; j < num_flows; j++) {
+ CU_ASSERT(flow_stat[j] == 0);
+ if (flow_stat[j])
+ printf("flow id %" PRIu32 ", missing %" PRIi32
+ " events\n", j, flow_stat[j]);
+ }
+
+ drain_queues();
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+/* Queues created but no events */
+static void scheduler_test_print(void)
+{
+ odp_schedule_print();
+}
+
+/* Queues with initial events enqueued */
+static void scheduler_test_mq_mt_prio_a_print(void)
+{
+ int prio = odp_schedule_num_prio();
+
+ globals->test_debug_print = 1;
+
+ parallel_execute(ODP_SCHED_SYNC_ATOMIC, globals->queues_per_prio, prio,
+ SCHD_ONE, DISABLE_EXCL_ATOMIC);
+
+ globals->test_debug_print = 0;
+}
+
+static int scheduler_test_global_init(void)
+{
+ odp_shm_t shm;
+ thread_args_t *args;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ uint64_t num_flows;
+ odp_schedule_capability_t sched_capa;
+ odp_schedule_config_t sched_config;
+
+ shm = odp_shm_reserve(GLOBALS_SHM_NAME,
+ sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed (globals)\n");
+ return -1;
+ }
+
+ globals = odp_shm_addr(shm);
+
+ if (!globals) {
+ ODPH_ERR("Shared memory reserve failed (globals)\n");
+ return -1;
+ }
+
+ memset(globals, 0, sizeof(test_globals_t));
+ globals->shm_glb = shm;
+
+ globals->num_workers = odp_cpumask_default_worker(NULL, 0);
+ if (globals->num_workers > MAX_WORKERS)
+ globals->num_workers = MAX_WORKERS;
+
+ shm = odp_shm_reserve(SHM_THR_ARGS_NAME, sizeof(thread_args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Shared memory reserve failed (args)\n");
+ return -1;
+ }
+
+ args = odp_shm_addr(shm);
+ globals->shm_args = shm;
+
+ if (!args) {
+ ODPH_ERR("Shared memory reserve failed (args)\n");
+ return -1;
+ }
+
+ memset(args, 0, sizeof(thread_args_t));
+
+ /* Barrier to sync test case execution */
+ odp_barrier_init(&globals->barrier, globals->num_workers);
+ odp_ticketlock_init(&globals->lock);
+ odp_spinlock_init(&globals->atomic_lock);
+
+ odp_pool_param_init(&params);
+ params.buf.size = BUF_SIZE;
+ params.buf.align = 0;
+ params.buf.num = MSG_POOL_SIZE;
+ params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create(MSG_POOL_NAME, &params);
+
+ if (pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Pool creation failed (msg)\n");
+ return -1;
+ }
+
+ globals->pool = pool;
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("odp_schedule_capability() failed\n");
+ return -1;
+ }
+
+ num_flows = 0;
+ odp_schedule_config_init(&sched_config);
+
+ /* Enable flow aware scheduling */
+ if (sched_capa.max_flow_id > 0) {
+ num_flows = MAX_FLOWS;
+ if ((MAX_FLOWS - 1) > sched_capa.max_flow_id)
+ num_flows = sched_capa.max_flow_id + 1;
+
+ sched_config.max_flow_id = num_flows - 1;
+ }
+
+ globals->num_flows = num_flows;
+
+ /* Configure the scheduler. All test cases share the config. */
+ if (odp_schedule_config(&sched_config)) {
+ ODPH_ERR("odp_schedule_config() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int scheduler_multi_suite_init(void)
+{
+ /* Line feeds to separate output from basic suite prints */
+ printf("\n\n");
+
+ if (create_queues(globals) != 0)
+ return -1;
+
+ return 0;
+}
+
+static int scheduler_multi_suite_term(void)
+{
+ if (destroy_queues() != 0) {
+ ODPH_ERR("Failed to destroy queues\n");
+ return -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ return -1;
+
+ return 0;
+}
+
+static int scheduler_basic_suite_init(void)
+{
+ return 0;
+}
+
+static int scheduler_basic_suite_term(void)
+{
+ if (odp_cunit_print_inactive())
+ return -1;
+
+ return 0;
+}
+
+static int global_init(odp_instance_t *inst)
+{
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed.\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed.\n");
+ return -1;
+ }
+
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed.\n");
+ return -1;
+ }
+
+ if (scheduler_test_global_init()) {
+ ODPH_ERR("scheduler test global init failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int global_term(odp_instance_t inst)
+{
+ if (odp_pool_destroy(globals->pool))
+ ODPH_ERR("Failed to destroy pool\n");
+
+ if (odp_shm_free(globals->shm_args))
+ ODPH_ERR("Failed to free shm\n");
+
+ if (odp_shm_free(globals->shm_glb))
+ ODPH_ERR("Failed to free shm\n");
+
+ if (odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed.\n");
+ return -1;
+ }
+
+ if (odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Basic scheduler test suite */
+odp_testinfo_t scheduler_basic_suite[] = {
+ ODP_TEST_INFO(scheduler_test_init),
+ ODP_TEST_INFO(scheduler_test_capa),
+ ODP_TEST_INFO(scheduler_test_wait_time),
+ ODP_TEST_INFO(scheduler_test_num_prio),
+ ODP_TEST_INFO(scheduler_test_queue_destroy),
+ ODP_TEST_INFO(scheduler_test_wait),
+ ODP_TEST_INFO(scheduler_test_queue_size),
+ ODP_TEST_INFO(scheduler_test_full_queues),
+ ODP_TEST_INFO(scheduler_test_max_queues_p),
+ ODP_TEST_INFO(scheduler_test_max_queues_a),
+ ODP_TEST_INFO(scheduler_test_max_queues_o),
+ ODP_TEST_INFO(scheduler_test_order_ignore),
+ ODP_TEST_INFO(scheduler_test_group_info_predef),
+ ODP_TEST_INFO(scheduler_test_create_group),
+ ODP_TEST_INFO(scheduler_test_create_max_groups),
+ ODP_TEST_INFO(scheduler_test_groups),
+ ODP_TEST_INFO(scheduler_test_pause_resume),
+ ODP_TEST_INFO(scheduler_test_pause_enqueue),
+ ODP_TEST_INFO(scheduler_test_ordered_lock),
+ ODP_TEST_INFO(scheduler_test_order_wait_1_thread),
+ ODP_TEST_INFO(scheduler_test_order_wait_2_threads),
+ ODP_TEST_INFO_CONDITIONAL(scheduler_test_flow_aware,
+ check_flow_aware_support),
+ ODP_TEST_INFO(scheduler_test_parallel),
+ ODP_TEST_INFO(scheduler_test_atomic),
+ ODP_TEST_INFO(scheduler_test_ordered),
+ ODP_TEST_INFO(scheduler_test_atomic_and_plain),
+ ODP_TEST_INFO(scheduler_test_ordered_and_plain),
+ ODP_TEST_INFO(scheduler_fifo_parallel_single),
+ ODP_TEST_INFO(scheduler_fifo_parallel_multi),
+ ODP_TEST_INFO(scheduler_fifo_atomic_single),
+ ODP_TEST_INFO(scheduler_fifo_atomic_multi),
+ ODP_TEST_INFO(scheduler_fifo_ordered_single),
+ ODP_TEST_INFO(scheduler_fifo_ordered_multi),
+ ODP_TEST_INFO(scheduler_fifo_mt_parallel_single),
+ ODP_TEST_INFO(scheduler_fifo_mt_parallel_multi),
+ ODP_TEST_INFO(scheduler_fifo_mt_atomic_single),
+ ODP_TEST_INFO(scheduler_fifo_mt_atomic_multi),
+ ODP_TEST_INFO(scheduler_fifo_mt_ordered_single),
+ ODP_TEST_INFO(scheduler_fifo_mt_ordered_multi),
+ ODP_TEST_INFO(scheduler_test_atomicity),
+ ODP_TEST_INFO_NULL
+};
+
+/* Scheduler test suite which runs events through hundreds of queues. Queues are created once
+ * in suite init phase. */
+odp_testinfo_t scheduler_multi_suite[] = {
+ ODP_TEST_INFO(scheduler_test_print),
+ ODP_TEST_INFO(scheduler_test_chaos),
+ ODP_TEST_INFO(scheduler_test_1q_1t_n),
+ ODP_TEST_INFO(scheduler_test_1q_1t_a),
+ ODP_TEST_INFO(scheduler_test_1q_1t_o),
+ ODP_TEST_INFO(scheduler_test_mq_1t_n),
+ ODP_TEST_INFO(scheduler_test_mq_1t_a),
+ ODP_TEST_INFO(scheduler_test_mq_1t_o),
+ ODP_TEST_INFO(scheduler_test_mq_1t_prio_n),
+ ODP_TEST_INFO(scheduler_test_mq_1t_prio_a),
+ ODP_TEST_INFO(scheduler_test_mq_1t_prio_o),
+ ODP_TEST_INFO(scheduler_test_mq_mt_prio_n),
+ ODP_TEST_INFO(scheduler_test_mq_mt_prio_a),
+ ODP_TEST_INFO(scheduler_test_mq_mt_prio_o),
+ ODP_TEST_INFO(scheduler_test_1q_mt_a_excl),
+ ODP_TEST_INFO(scheduler_test_multi_1q_1t_n),
+ ODP_TEST_INFO(scheduler_test_multi_1q_1t_a),
+ ODP_TEST_INFO(scheduler_test_multi_1q_1t_o),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_n),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_a),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_o),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_n),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_a),
+ ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_o),
+ ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_n),
+ ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_a),
+ ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_o),
+ ODP_TEST_INFO(scheduler_test_multi_1q_mt_a_excl),
+ ODP_TEST_INFO(scheduler_test_mq_mt_prio_a_print),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t scheduler_suites[] = {
+ {"Scheduler basic",
+ scheduler_basic_suite_init, scheduler_basic_suite_term, scheduler_basic_suite
+ },
+ {"Scheduler multi",
+ scheduler_multi_suite_init, scheduler_multi_suite_term, scheduler_multi_suite
+ },
+
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(global_init);
+ odp_cunit_register_global_term(global_term);
+
+ ret = odp_cunit_register(scheduler_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/scheduler/scheduler_no_predef_groups.c b/test/validation/api/scheduler/scheduler_no_predef_groups.c
new file mode 100644
index 000000000..d2ea48eb6
--- /dev/null
+++ b/test/validation/api/scheduler/scheduler_no_predef_groups.c
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
+ */
+
+#include <odp_api.h>
+#include "odp_cunit_common.h"
+#include <odp/helper/odph_api.h>
+
+static int drain_queues(void)
+{
+ odp_event_t ev;
+ uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ int ret = 0;
+
+ while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID) {
+ odp_event_free(ev);
+ ret++;
+ }
+
+ return ret;
+}
+
+static void scheduler_test_create_group(void)
+{
+ odp_thrmask_t mask;
+ odp_schedule_group_t group;
+ int thr_id;
+ odp_pool_t pool;
+ odp_pool_param_t pool_params;
+ odp_queue_t queue, from;
+ odp_queue_param_t qp;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint64_t wait_time;
+
+ thr_id = odp_thread_id();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ group = odp_schedule_group_create("create_group", &mask);
+ CU_ASSERT_FATAL(group != ODP_SCHED_GROUP_INVALID);
+
+ odp_pool_param_init(&pool_params);
+ pool_params.buf.size = 100;
+ pool_params.buf.num = 2;
+ pool_params.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("create_group", &pool_params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&qp);
+ qp.type = ODP_QUEUE_TYPE_SCHED;
+ qp.sched.prio = odp_schedule_default_prio();
+ qp.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qp.sched.group = group;
+
+ queue = odp_queue_create("create_group", &qp);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ buf = odp_buffer_alloc(pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ ev = odp_buffer_to_event(buf);
+
+ CU_ASSERT_FATAL(odp_queue_enq(queue, ev) == 0);
+
+ wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+ ev = odp_schedule(&from, wait_time);
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(from == queue);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ /* Free schedule context */
+ drain_queues();
+
+ CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0);
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(group) == 0);
+
+ /* Run scheduler after the group has been destroyed */
+ CU_ASSERT_FATAL(odp_schedule(NULL, wait_time) == ODP_EVENT_INVALID);
+}
+
+static void scheduler_test_create_max_groups(void)
+{
+ odp_thrmask_t mask;
+ int thr_id;
+ uint32_t i;
+ odp_queue_param_t queue_param;
+ odp_schedule_capability_t sched_capa;
+
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ uint32_t max_groups = sched_capa.max_groups;
+ odp_schedule_group_t group[max_groups];
+ odp_queue_t queue[max_groups];
+
+ CU_ASSERT_FATAL(max_groups > 0);
+ CU_ASSERT_FATAL(sched_capa.max_queues >= sched_capa.max_groups);
+
+ thr_id = odp_thread_id();
+ odp_thrmask_zero(&mask);
+ odp_thrmask_set(&mask, thr_id);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.prio = odp_schedule_default_prio();
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ for (i = 0; i < max_groups; i++) {
+ group[i] = odp_schedule_group_create("max_groups", &mask);
+ if (group[i] == ODP_SCHED_GROUP_INVALID) {
+ ODPH_ERR("schedule group create %u failed\n", i);
+ break;
+ }
+
+ queue_param.sched.group = group[i];
+ queue[i] = odp_queue_create("max_groups", &queue_param);
+ CU_ASSERT_FATAL(queue[i] != ODP_QUEUE_INVALID);
+ }
+
+ CU_ASSERT(i == max_groups);
+ max_groups = i;
+
+ for (i = 0; i < max_groups; i++) {
+ CU_ASSERT_FATAL(odp_queue_destroy(queue[i]) == 0);
+ CU_ASSERT_FATAL(odp_schedule_group_destroy(group[i]) == 0);
+ }
+}
+
+static int scheduler_suite_init(void)
+{
+ odp_schedule_capability_t sched_capa;
+ odp_schedule_config_t sched_config;
+
+ if (odp_schedule_capability(&sched_capa)) {
+ ODPH_ERR("odp_schedule_capability() failed\n");
+ return -1;
+ }
+
+ odp_schedule_config_init(&sched_config);
+
+ /* Disable all predefined groups */
+ sched_config.sched_group.all = false;
+ sched_config.sched_group.control = false;
+ sched_config.sched_group.worker = false;
+
+ /* Configure the scheduler. All test cases share the config. */
+ if (odp_schedule_config(&sched_config)) {
+ ODPH_ERR("odp_schedule_config() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int scheduler_suite_term(void)
+{
+ if (odp_cunit_print_inactive())
+ return -1;
+
+ return 0;
+}
+
+/* Default scheduler config */
+odp_testinfo_t scheduler_suite[] = {
+ ODP_TEST_INFO(scheduler_test_create_group),
+ ODP_TEST_INFO(scheduler_test_create_max_groups),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t scheduler_suites[] = {
+ {"Scheduler no predefined groups",
+ scheduler_suite_init, scheduler_suite_term, scheduler_suite
+ },
+ ODP_SUITE_INFO_NULL,
+};
+
+static int global_init(odp_instance_t *inst)
+{
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed.\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed.\n");
+ return -1;
+ }
+
+ if (odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(global_init);
+ ret = odp_cunit_register(scheduler_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/shmem/.gitignore b/test/validation/api/shmem/.gitignore
index 4d82fd53a..4d82fd53a 100644
--- a/test/common_plat/validation/api/shmem/.gitignore
+++ b/test/validation/api/shmem/.gitignore
diff --git a/test/validation/api/shmem/Makefile.am b/test/validation/api/shmem/Makefile.am
new file mode 100644
index 000000000..52e33fdca
--- /dev/null
+++ b/test/validation/api/shmem/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = shmem_main
+shmem_main_SOURCES = shmem.c
diff --git a/test/validation/api/shmem/shmem.c b/test/validation/api/shmem/shmem.c
new file mode 100644
index 000000000..57a21dd02
--- /dev/null
+++ b/test/validation/api/shmem/shmem.c
@@ -0,0 +1,1173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ * Copyright (c) 2019-2021 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <odp/helper/odph_api.h>
+#include <stdlib.h>
+
+#define MAX_WORKERS 32
+#define ALIGN_SIZE (128)
+#define MEM_NAME "test_shmem"
+#define NAME_LEN (sizeof(MEM_NAME) + 20)
+#define TEST_SHARE_FOO (0xf0f0f0f0)
+#define TEST_SHARE_BAR (0xf0f0f0f)
+#define SMALL_MEM 10
+#define MEDIUM_MEM 4096
+#define BIG_MEM 65536
+#define STRESS_SIZE 32 /* power of 2 and <=256 */
+#define STRESS_RANDOM_SZ 5
+#define STRESS_ITERATION 5000
+#define MAX_SIZE_TESTED (100 * 1000000UL)
+#define MAX_ALIGN_TESTED (1024 * 1024)
+
+typedef enum {
+ STRESS_FREE, /* entry is free and can be allocated */
+ STRESS_BUSY, /* entry is being processed: don't touch */
+ STRESS_ALLOC /* entry is allocated and can be freed */
+} stress_state_t;
+
+typedef struct {
+ stress_state_t state;
+ odp_shm_t shm;
+ char name[NAME_LEN];
+ void *address;
+ uint32_t flags;
+ uint32_t size;
+ uint64_t align;
+ uint8_t data_val;
+} stress_data_t;
+
+typedef struct {
+ odp_barrier_t test_barrier1;
+ odp_barrier_t test_barrier2;
+ odp_barrier_t test_barrier3;
+ odp_barrier_t test_barrier4;
+ uint32_t foo;
+ uint32_t bar;
+ odp_atomic_u32_t index;
+ uint32_t nb_threads;
+ odp_shm_t shm[MAX_WORKERS];
+ void *address[MAX_WORKERS];
+ char name[MAX_WORKERS][NAME_LEN];
+ odp_spinlock_t stress_lock;
+ stress_data_t stress[STRESS_SIZE];
+} shared_test_data_t;
+
+/* memory stuff expected to fit in a single page */
+typedef struct {
+ int data[SMALL_MEM];
+} shared_test_data_small_t;
+
+/* memory stuff expected to fit in a huge page */
+typedef struct {
+ int data[MEDIUM_MEM];
+} shared_test_data_medium_t;
+
+/* memory stuff expected to fit in many huge pages */
+typedef struct {
+ int data[BIG_MEM];
+} shared_test_data_big_t;
+
+/* SHM capability saved at suite init phase */
+static odp_shm_capability_t _global_shm_capa;
+
+/*
+ * thread part for the shmem_test_basic test
+ */
+static int run_test_basic_thread(void *arg ODP_UNUSED)
+{
+ odp_shm_info_t info;
+ odp_shm_t shm;
+ shared_test_data_t *shared_test_data;
+ int thr;
+ int pagesz_match = 0;
+
+ thr = odp_thread_id();
+ printf("Thread %i starts\n", thr);
+
+ shm = odp_shm_lookup(MEM_NAME);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ shared_test_data = odp_shm_addr(shm);
+ CU_ASSERT(NULL != shared_test_data);
+
+ odp_barrier_wait(&shared_test_data->test_barrier1);
+ odp_shm_print_all();
+ CU_ASSERT(TEST_SHARE_FOO == shared_test_data->foo);
+ CU_ASSERT(TEST_SHARE_BAR == shared_test_data->bar);
+ CU_ASSERT_FATAL(0 == odp_shm_info(shm, &info));
+ CU_ASSERT(0 == strcmp(MEM_NAME, info.name));
+ CU_ASSERT(0 == info.flags);
+ CU_ASSERT(shared_test_data == info.addr);
+ CU_ASSERT(sizeof(shared_test_data_t) <= info.size);
+
+ if (info.page_size == odp_sys_page_size()) {
+ pagesz_match = 1;
+ } else {
+ int num = odp_sys_huge_page_size_all(NULL, 0);
+
+ if (num > 0) {
+ uint64_t pagesz_tbs[num];
+ int i;
+
+ num = odp_sys_huge_page_size_all(pagesz_tbs, num);
+ for (i = 0; i < num; i++) {
+ if (info.page_size == pagesz_tbs[i]) {
+ pagesz_match = 1;
+ break;
+ }
+ }
+ }
+ }
+ CU_ASSERT(pagesz_match == 1);
+
+ odp_shm_print_all();
+
+ fflush(stdout);
+ return CU_get_number_of_failures();
+}
+
+/*
+ * test basic things: shmem creation, info, share, and free
+ */
+static void shmem_test_multi_thread(void)
+{
+ odp_shm_t shm;
+ odp_shm_t shm2;
+ shared_test_data_t *shared_test_data;
+ int i, num;
+ char max_name[ODP_SHM_NAME_LEN];
+
+ for (i = 0; i < ODP_SHM_NAME_LEN; i++)
+ max_name[i] = 'A' + (i % 26);
+
+ max_name[ODP_SHM_NAME_LEN - 1] = 0;
+
+ /* NULL name */
+ shm = odp_shm_reserve(NULL,
+ sizeof(shared_test_data_t), ALIGN_SIZE, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ shared_test_data = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(NULL != shared_test_data);
+ shared_test_data->foo = 0;
+ CU_ASSERT(0 == odp_shm_free(shm));
+
+ /* Maximum length name */
+ shm = odp_shm_reserve(max_name,
+ sizeof(shared_test_data_t), ALIGN_SIZE, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ shm2 = odp_shm_lookup(max_name);
+ CU_ASSERT(ODP_SHM_INVALID != shm2);
+ CU_ASSERT(odp_shm_addr(shm) == odp_shm_addr(shm2));
+ shared_test_data = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(NULL != shared_test_data);
+ shared_test_data->foo = 0;
+ CU_ASSERT(0 == odp_shm_free(shm));
+
+ /* Non-unique name */
+ shm = odp_shm_reserve(MEM_NAME,
+ sizeof(shared_test_data_t), ALIGN_SIZE, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ CU_ASSERT(odp_shm_to_u64(shm) !=
+ odp_shm_to_u64(ODP_SHM_INVALID));
+ shm2 = odp_shm_reserve(MEM_NAME,
+ sizeof(shared_test_data_t), ALIGN_SIZE, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm2);
+ CU_ASSERT(odp_shm_to_u64(shm2) !=
+ odp_shm_to_u64(ODP_SHM_INVALID));
+
+ CU_ASSERT(odp_shm_addr(shm) != odp_shm_addr(shm2));
+ shared_test_data = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(NULL != shared_test_data);
+ shared_test_data->foo = 0;
+ shared_test_data = odp_shm_addr(shm2);
+ CU_ASSERT_FATAL(NULL != shared_test_data);
+ shared_test_data->foo = 0;
+ CU_ASSERT(0 == odp_shm_free(shm));
+ CU_ASSERT(0 == odp_shm_free(shm2));
+ CU_ASSERT(ODP_SHM_INVALID == odp_shm_lookup(MEM_NAME));
+
+ /* Share with multiple threads */
+ shm = odp_shm_reserve(MEM_NAME,
+ sizeof(shared_test_data_t), ALIGN_SIZE, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+
+ shared_test_data = odp_shm_addr(shm);
+ CU_ASSERT_FATAL(NULL != shared_test_data);
+ shared_test_data->foo = TEST_SHARE_FOO;
+ shared_test_data->bar = TEST_SHARE_BAR;
+
+ num = odp_cpumask_default_worker(NULL, 0);
+
+ if (num > MAX_WORKERS)
+ num = MAX_WORKERS;
+
+ odp_barrier_init(&shared_test_data->test_barrier1, num);
+ odp_cunit_thread_create(num, run_test_basic_thread, NULL, 0, 0);
+ CU_ASSERT(odp_cunit_thread_join(num) >= 0);
+
+ odp_shm_print(shm);
+
+ CU_ASSERT(0 == odp_shm_free(shm));
+}
+
+static void shmem_test_capability(void)
+{
+ odp_shm_capability_t capa;
+
+ CU_ASSERT_FATAL(odp_shm_capability(&capa) == 0);
+
+ CU_ASSERT(capa.max_blocks);
+
+ printf("\nSHM capability\n--------------\n");
+
+ printf(" max_blocks: %u\n", capa.max_blocks);
+ printf(" max_size: %" PRIu64 "\n", capa.max_size);
+ printf(" max_align: %" PRIu64 "\n", capa.max_align);
+ printf(" flags: ");
+ if (capa.flags & ODP_SHM_PROC)
+ printf("ODP_SHM_PROC ");
+ if (capa.flags & ODP_SHM_SINGLE_VA)
+ printf("ODP_SHM_SINGLE_VA ");
+ if (capa.flags & ODP_SHM_EXPORT)
+ printf("ODP_SHM_EXPORT ");
+ if (capa.flags & ODP_SHM_HP)
+ printf("ODP_SHM_HP ");
+ if (capa.flags & ODP_SHM_HW_ACCESS)
+ printf("ODP_SHM_HW_ACCESS ");
+ if (capa.flags & ODP_SHM_NO_HP)
+ printf("ODP_SHM_NO_HP ");
+ printf("\n\n");
+}
+
+static void shmem_test_reserve(void)
+{
+ odp_shm_t shm;
+ void *addr;
+
+ shm = odp_shm_reserve(MEM_NAME, MEDIUM_MEM, ALIGN_SIZE, 0);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ addr = odp_shm_addr(shm);
+ CU_ASSERT(addr != NULL);
+
+ if (addr)
+ memset(addr, 0, MEDIUM_MEM);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static void shmem_test_info(void)
+{
+ odp_shm_t shm;
+ void *addr;
+ int ret;
+ uint32_t i;
+ uint64_t sum_len;
+ uintptr_t next;
+ odp_shm_info_t info;
+ const char *name = "info_test";
+ uint32_t num_seg = 32;
+ uint64_t size = 4 * 1024 * 1024;
+ uint64_t align = 64;
+ int support_pa = 0;
+ int support_iova = 0;
+
+ if (_global_shm_capa.max_size && _global_shm_capa.max_size < size)
+ size = _global_shm_capa.max_size;
+
+ if (_global_shm_capa.max_align < align)
+ align = _global_shm_capa.max_align;
+
+ shm = odp_shm_reserve(name, size, align, 0);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ addr = odp_shm_addr(shm);
+ CU_ASSERT(addr != NULL);
+
+ if (addr)
+ memset(addr, 0, size);
+
+ memset(&info, 0, sizeof(odp_shm_info_t));
+ ret = odp_shm_info(shm, &info);
+
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT(strcmp(name, info.name) == 0);
+ CU_ASSERT(info.addr == addr);
+ CU_ASSERT(info.size == size);
+ CU_ASSERT(info.page_size > 0);
+ CU_ASSERT(info.flags == 0);
+ CU_ASSERT(info.num_seg > 0);
+
+ /* Limit number of segments as it may get large with small page sizes */
+ if (info.num_seg < num_seg)
+ num_seg = info.num_seg;
+
+ /* all segments */
+ odp_shm_segment_info_t seginfo_a[num_seg];
+
+ memset(seginfo_a, 0, num_seg * sizeof(odp_shm_segment_info_t));
+
+ ret = odp_shm_segment_info(shm, 0, num_seg, seginfo_a);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT(seginfo_a[0].addr == (uintptr_t)addr);
+
+ sum_len = 0;
+ next = 0;
+
+ printf("\n\n");
+ printf("SHM segment info\n");
+ printf("%3s %16s %16s %16s %16s\n", "idx", "addr", "iova", "pa", "len");
+
+ for (i = 0; i < num_seg; i++) {
+ printf("%3u %16" PRIxPTR " %16" PRIx64 " %16" PRIx64 " %16" PRIu64 "\n",
+ i, seginfo_a[i].addr, seginfo_a[i].iova, seginfo_a[i].pa, seginfo_a[i].len);
+
+ CU_ASSERT(seginfo_a[i].addr != 0);
+ CU_ASSERT(seginfo_a[i].len > 0);
+
+ if (next) {
+ CU_ASSERT(seginfo_a[i].addr == next);
+ next += seginfo_a[i].len;
+ } else {
+ next = seginfo_a[i].addr + seginfo_a[i].len;
+ }
+
+ if (seginfo_a[i].iova != ODP_SHM_IOVA_INVALID)
+ support_iova = 1;
+
+ if (seginfo_a[i].pa != ODP_SHM_PA_INVALID)
+ support_pa = 1;
+
+ sum_len += seginfo_a[i].len;
+ }
+
+ printf("\n");
+ printf("IOVA: %s, PA: %s\n\n", support_iova ? "supported" : "not supported",
+ support_pa ? "supported" : "not supported");
+
+ CU_ASSERT(sum_len == size);
+
+ if (num_seg > 1) {
+ /* all, except the first one */
+ odp_shm_segment_info_t seginfo_b[num_seg];
+
+ memset(seginfo_b, 0xff, num_seg * sizeof(odp_shm_segment_info_t));
+
+ ret = odp_shm_segment_info(shm, 1, num_seg - 1, &seginfo_b[1]);
+ CU_ASSERT_FATAL(ret == 0);
+
+ for (i = 1; i < num_seg; i++) {
+ CU_ASSERT(seginfo_a[i].addr == seginfo_b[i].addr);
+ CU_ASSERT(seginfo_a[i].iova == seginfo_b[i].iova);
+ CU_ASSERT(seginfo_a[i].pa == seginfo_b[i].pa);
+ CU_ASSERT(seginfo_a[i].len == seginfo_b[i].len);
+ }
+ }
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int shmem_check_flag_hp(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_HP)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+/*
+ * test reserving memory from huge pages
+ */
+static void shmem_test_flag_hp(void)
+{
+ odp_shm_t shm;
+ odp_shm_info_t info;
+ int i;
+ int num_sizes = odp_sys_huge_page_size_all(NULL, 0);
+
+ CU_ASSERT_FATAL(num_sizes >= 0);
+
+ shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t),
+ ALIGN_SIZE, ODP_SHM_HP);
+ if (shm == ODP_SHM_INVALID) {
+ printf(" No huge pages available\n");
+ return;
+ }
+
+ /* Make sure that the memory is reserved from huge pages */
+
+ CU_ASSERT_FATAL(num_sizes > 0);
+ CU_ASSERT_FATAL(odp_shm_info(shm, &info) == 0);
+
+ uint64_t hp_sizes[num_sizes];
+
+ CU_ASSERT_FATAL(odp_sys_huge_page_size_all(hp_sizes, num_sizes) ==
+ num_sizes);
+
+ for (i = 0; i < num_sizes; i++) {
+ if (hp_sizes[i] == info.page_size)
+ break;
+ }
+
+ CU_ASSERT(i < num_sizes);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int shmem_check_flag_no_hp(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_NO_HP)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+/*
+ * Test reserving memory from normal pages
+ */
+static void shmem_test_flag_no_hp(void)
+{
+ odp_shm_t shm;
+ odp_shm_info_t info;
+
+ shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t), 0,
+ ODP_SHM_NO_HP);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ /* Make sure that the memory is reserved from normal pages */
+ CU_ASSERT_FATAL(odp_shm_info(shm, &info) == 0);
+
+ CU_ASSERT(info.page_size == odp_sys_page_size());
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int shmem_check_flag_proc(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_PROC)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+static void shmem_test_flag_proc(void)
+{
+ odp_shm_t shm;
+ void *addr;
+
+ shm = odp_shm_reserve(MEM_NAME, MEDIUM_MEM, ALIGN_SIZE, ODP_SHM_PROC);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ addr = odp_shm_addr(shm);
+
+ CU_ASSERT(addr != NULL);
+
+ if (addr)
+ memset(addr, 0, MEDIUM_MEM);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int shmem_check_flag_export(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_EXPORT)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+static void shmem_test_flag_export(void)
+{
+ odp_shm_t shm;
+ void *addr;
+
+ shm = odp_shm_reserve(MEM_NAME, MEDIUM_MEM, ALIGN_SIZE, ODP_SHM_EXPORT);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ addr = odp_shm_addr(shm);
+
+ CU_ASSERT(addr != NULL);
+
+ if (addr)
+ memset(addr, 0, MEDIUM_MEM);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+static int shmem_check_flag_hw_access(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_HW_ACCESS)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+static void shmem_test_flag_hw_access(void)
+{
+ odp_shm_t shm;
+ void *addr;
+
+ shm = odp_shm_reserve(MEM_NAME, MEDIUM_MEM, ALIGN_SIZE,
+ ODP_SHM_HW_ACCESS);
+ CU_ASSERT_FATAL(shm != ODP_SHM_INVALID);
+
+ addr = odp_shm_addr(shm);
+
+ CU_ASSERT(addr != NULL);
+
+ if (addr)
+ memset(addr, 0, MEDIUM_MEM);
+
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+/*
+ * maximum size reservation
+ */
+static void shmem_test_max_reserve(void)
+{
+ odp_shm_capability_t capa;
+ odp_shm_t shm;
+ uint64_t size, align;
+ uint8_t *data;
+ uint64_t i;
+
+ memset(&capa, 0, sizeof(odp_shm_capability_t));
+ CU_ASSERT_FATAL(odp_shm_capability(&capa) == 0);
+
+ CU_ASSERT(capa.max_blocks > 0);
+
+ size = capa.max_size;
+ align = capa.max_align;
+
+ /* Assuming that system has at least MAX_SIZE_TESTED bytes available */
+ if (capa.max_size == 0 || capa.max_size > MAX_SIZE_TESTED)
+ size = MAX_SIZE_TESTED;
+
+ if (capa.max_align == 0 || capa.max_align > MAX_ALIGN_TESTED)
+ align = MAX_ALIGN_TESTED;
+
+ printf("\n size: %" PRIu64 "\n", size);
+ printf(" align: %" PRIu64 "\n", align);
+
+ shm = odp_shm_reserve("test_max_reserve", size, align, 0);
+ CU_ASSERT(shm != ODP_SHM_INVALID);
+
+ data = odp_shm_addr(shm);
+ CU_ASSERT(data != NULL);
+
+ if (data) {
+ memset(data, 0xde, size);
+ for (i = 0; i < size; i++) {
+ if (data[i] != 0xde) {
+ printf(" data error i:%" PRIu64 ", data %x"
+ "\n", i, data[i]);
+ CU_FAIL("Data error");
+ break;
+ }
+ }
+ }
+
+ if (shm != ODP_SHM_INVALID)
+ CU_ASSERT(odp_shm_free(shm) == 0);
+}
+
+/*
+ * thread part for the shmem_test_reserve_after_fork
+ */
+static int run_test_reserve_after_fork(void *arg ODP_UNUSED)
+{
+ odp_shm_t shm;
+ shared_test_data_t *glob_data;
+ int thr;
+ int thr_index;
+ int size;
+ shared_test_data_small_t *pattern_small;
+ shared_test_data_medium_t *pattern_medium;
+ shared_test_data_big_t *pattern_big;
+ int i;
+
+ thr = odp_thread_id();
+ printf("Thread %i starts\n", thr);
+
+ shm = odp_shm_lookup(MEM_NAME);
+ glob_data = odp_shm_addr(shm);
+
+ /*
+ * odp_thread_id are not guaranteed to be consecutive, so we create
+ * a consecutive ID
+ */
+ thr_index = odp_atomic_fetch_inc_u32(&glob_data->index);
+
+ /* allocate some memory (of different sizes) and fill with pattern */
+ snprintf(glob_data->name[thr_index], NAME_LEN, "%s-%09d",
+ MEM_NAME, thr_index);
+ switch (thr_index % 3) {
+ case 0:
+ size = sizeof(shared_test_data_small_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_small = odp_shm_addr(shm);
+ CU_ASSERT(pattern_small != NULL);
+ for (i = 0; i < SMALL_MEM; i++)
+ pattern_small->data[i] = i;
+ break;
+ case 1:
+ size = sizeof(shared_test_data_medium_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_medium = odp_shm_addr(shm);
+ CU_ASSERT(pattern_medium != NULL);
+ for (i = 0; i < MEDIUM_MEM; i++)
+ pattern_medium->data[i] = (i << 2);
+ break;
+ case 2:
+ size = sizeof(shared_test_data_big_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size, 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_big = odp_shm_addr(shm);
+ CU_ASSERT(pattern_big != NULL);
+ for (i = 0; i < BIG_MEM; i++)
+ pattern_big->data[i] = (i >> 2);
+ break;
+ }
+
+ /* print block address */
+ printf("In thread: Block index: %d mapped at %p\n",
+ thr_index, odp_shm_addr(shm));
+
+ odp_barrier_wait(&glob_data->test_barrier1);
+ odp_barrier_wait(&glob_data->test_barrier2);
+
+ fflush(stdout);
+ return CU_get_number_of_failures();
+}
+
+/*
+ * test sharing memory reserved after odp_thread creation (e.g. fork()):
+ */
+static void shmem_test_reserve_after_fork(void)
+{
+ odp_shm_t shm;
+ odp_shm_t thr_shm;
+ shared_test_data_t *glob_data;
+ int thr_index, i, num;
+ shared_test_data_small_t *pattern_small;
+ shared_test_data_medium_t *pattern_medium;
+ shared_test_data_big_t *pattern_big;
+
+ shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t), 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ glob_data = odp_shm_addr(shm);
+ CU_ASSERT(glob_data != NULL);
+
+ num = odp_cpumask_default_worker(NULL, 0);
+ if (num > MAX_WORKERS)
+ num = MAX_WORKERS;
+
+ odp_barrier_init(&glob_data->test_barrier1, num + 1);
+ odp_barrier_init(&glob_data->test_barrier2, num + 1);
+ odp_atomic_store_u32(&glob_data->index, 0);
+
+ odp_cunit_thread_create(num, run_test_reserve_after_fork, NULL, 0, 0);
+
+ /* wait until all threads have made their shm_reserve: */
+ odp_barrier_wait(&glob_data->test_barrier1);
+
+ /* perform a lookup of all memories: */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ thr_shm = odp_shm_lookup(glob_data->name[thr_index]);
+ CU_ASSERT(thr_shm == glob_data->shm[thr_index]);
+ }
+
+ /* check that the patterns are correct: */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ switch (thr_index % 3) {
+ case 0:
+ pattern_small =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT(pattern_small != NULL);
+ for (i = 0; i < SMALL_MEM; i++)
+ CU_ASSERT(pattern_small->data[i] == i);
+ break;
+ case 1:
+ pattern_medium =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT(pattern_medium != NULL);
+ for (i = 0; i < MEDIUM_MEM; i++)
+ CU_ASSERT(pattern_medium->data[i] == (i << 2));
+ break;
+ case 2:
+ pattern_big =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT(pattern_big != NULL);
+ for (i = 0; i < BIG_MEM; i++)
+ CU_ASSERT(pattern_big->data[i] == (i >> 2));
+ break;
+ }
+ }
+
+ /*
+ * print the mapping address of the blocks
+ */
+ for (thr_index = 0; thr_index < num; thr_index++)
+ printf("In main Block index: %d mapped at %p\n",
+ thr_index, odp_shm_addr(glob_data->shm[thr_index]));
+
+ /* unblock the threads and let them terminate (no free is done): */
+ odp_barrier_wait(&glob_data->test_barrier2);
+
+ /* at the same time, (race),free of all memories: */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ thr_shm = glob_data->shm[thr_index];
+ CU_ASSERT(odp_shm_free(thr_shm) == 0);
+ }
+
+ /* wait for all thread endings: */
+ CU_ASSERT(odp_cunit_thread_join(num) >= 0);
+
+ /* just glob_data should remain: */
+
+ CU_ASSERT(0 == odp_shm_free(shm));
+}
+
+/*
+ * thread part for the shmem_test_singleva_after_fork
+ */
+static int run_test_singleva_after_fork(void *arg ODP_UNUSED)
+{
+ odp_shm_t shm;
+ shared_test_data_t *glob_data;
+ int thr;
+ int thr_index;
+ int size;
+ shared_test_data_small_t *pattern_small;
+ shared_test_data_medium_t *pattern_medium;
+ shared_test_data_big_t *pattern_big;
+ uint32_t i;
+ int ret;
+
+ thr = odp_thread_id();
+ printf("Thread %i starts\n", thr);
+
+ shm = odp_shm_lookup(MEM_NAME);
+ glob_data = odp_shm_addr(shm);
+
+ /*
+ * odp_thread_id are not guaranteed to be consecutive, so we create
+ * a consecutive ID
+ */
+ thr_index = odp_atomic_fetch_inc_u32(&glob_data->index);
+
+ /* allocate some memory (of different sizes) and fill with pattern */
+ snprintf(glob_data->name[thr_index], NAME_LEN, "%s-%09d",
+ MEM_NAME, thr_index);
+ switch (thr_index % 3) {
+ case 0:
+ size = sizeof(shared_test_data_small_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size,
+ 0, ODP_SHM_SINGLE_VA);
+ CU_ASSERT_FATAL(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_small = odp_shm_addr(shm);
+ CU_ASSERT(pattern_small != NULL);
+ glob_data->address[thr_index] = (void *)pattern_small;
+ for (i = 0; i < SMALL_MEM; i++)
+ pattern_small->data[i] = i;
+ break;
+ case 1:
+ size = sizeof(shared_test_data_medium_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size,
+ 0, ODP_SHM_SINGLE_VA);
+ CU_ASSERT_FATAL(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_medium = odp_shm_addr(shm);
+ CU_ASSERT(pattern_medium != NULL);
+ glob_data->address[thr_index] = (void *)pattern_medium;
+ for (i = 0; i < MEDIUM_MEM; i++)
+ pattern_medium->data[i] = (i << 2);
+ break;
+ case 2:
+ size = sizeof(shared_test_data_big_t);
+ shm = odp_shm_reserve(glob_data->name[thr_index], size,
+ 0, ODP_SHM_SINGLE_VA);
+ CU_ASSERT_FATAL(ODP_SHM_INVALID != shm);
+ glob_data->shm[thr_index] = shm;
+ pattern_big = odp_shm_addr(shm);
+ CU_ASSERT(pattern_big != NULL);
+ glob_data->address[thr_index] = (void *)pattern_big;
+ for (i = 0; i < BIG_MEM; i++)
+ pattern_big->data[i] = (i >> 2);
+ break;
+ }
+
+ /* print block address */
+ printf("In thread: Block index: %d mapped at %p\n",
+ thr_index, odp_shm_addr(shm));
+
+ odp_barrier_wait(&glob_data->test_barrier1);
+ odp_barrier_wait(&glob_data->test_barrier2);
+
+ /* map each-other block, checking common address: */
+ for (i = 0; i < glob_data->nb_threads; i++) {
+ shm = odp_shm_lookup(glob_data->name[i]);
+ CU_ASSERT(shm == glob_data->shm[i]);
+ CU_ASSERT(odp_shm_addr(shm) == glob_data->address[i]);
+ }
+
+ /* wait for main control task and free the allocated block */
+ odp_barrier_wait(&glob_data->test_barrier3);
+ odp_barrier_wait(&glob_data->test_barrier4);
+ ret = odp_shm_free(glob_data->shm[thr_index]);
+ CU_ASSERT(ret == 0);
+
+ fflush(stdout);
+ return CU_get_number_of_failures();
+}
+
+static int shmem_check_flag_single_va(void)
+{
+ if (_global_shm_capa.flags & ODP_SHM_SINGLE_VA)
+ return ODP_TEST_ACTIVE;
+ return ODP_TEST_INACTIVE;
+}
+
+/*
+ * test sharing memory reserved after odp_thread creation (e.g. fork()):
+ * with single VA flag.
+ */
+static void shmem_test_singleva_after_fork(void)
+{
+ odp_shm_t shm;
+ odp_shm_t thr_shm;
+ shared_test_data_t *glob_data;
+ int thr_index, i, num;
+ void *address;
+ shared_test_data_small_t *pattern_small;
+ shared_test_data_medium_t *pattern_medium;
+ shared_test_data_big_t *pattern_big;
+
+ shm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t),
+ 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != shm);
+ glob_data = odp_shm_addr(shm);
+ CU_ASSERT(glob_data != NULL);
+
+ num = odp_cpumask_default_worker(NULL, 3);
+ if (num > MAX_WORKERS)
+ num = MAX_WORKERS;
+
+ glob_data->nb_threads = num;
+ odp_barrier_init(&glob_data->test_barrier1, num + 1);
+ odp_barrier_init(&glob_data->test_barrier2, num + 1);
+ odp_barrier_init(&glob_data->test_barrier3, num + 1);
+ odp_barrier_init(&glob_data->test_barrier4, num + 1);
+ odp_atomic_store_u32(&glob_data->index, 0);
+
+ odp_cunit_thread_create(num, run_test_singleva_after_fork, NULL, 0, 0);
+
+ /* wait until all threads have made their shm_reserve: */
+ odp_barrier_wait(&glob_data->test_barrier1);
+
+ /* perform a lookup of all memories: */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ thr_shm = odp_shm_lookup(glob_data->name[thr_index]);
+ CU_ASSERT(thr_shm == glob_data->shm[thr_index]);
+ }
+
+ /* check that the patterns are correct: */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ switch (thr_index % 3) {
+ case 0:
+ pattern_small =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT_FATAL(pattern_small != NULL);
+ for (i = 0; i < SMALL_MEM; i++)
+ CU_ASSERT(pattern_small->data[i] == i);
+ break;
+ case 1:
+ pattern_medium =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT_FATAL(pattern_medium != NULL);
+ for (i = 0; i < MEDIUM_MEM; i++)
+ CU_ASSERT(pattern_medium->data[i] == (i << 2));
+ break;
+ case 2:
+ pattern_big =
+ odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT_FATAL(pattern_big != NULL);
+ for (i = 0; i < BIG_MEM; i++)
+ CU_ASSERT(pattern_big->data[i] == (i >> 2));
+ break;
+ }
+ }
+
+ /*
+ * check that the mapping address is common to all (SINGLE_VA):
+ */
+ for (thr_index = 0; thr_index < num; thr_index++) {
+ address = odp_shm_addr(glob_data->shm[thr_index]);
+ CU_ASSERT(glob_data->address[thr_index] == address);
+ }
+
+ /* unblock the threads and let them map each-other blocks: */
+ odp_barrier_wait(&glob_data->test_barrier2);
+
+ /* then check mem status */
+ odp_barrier_wait(&glob_data->test_barrier3);
+
+ /* unblock the threads and let them free all thread blocks: */
+ odp_barrier_wait(&glob_data->test_barrier4);
+
+ /* wait for all thread endings: */
+ CU_ASSERT(odp_cunit_thread_join(num) >= 0);
+
+ /* just glob_data should remain: */
+
+ CU_ASSERT(0 == odp_shm_free(shm));
+}
+
+/*
+ * thread part for the shmem_test_stress
+ */
+static int run_test_stress(void *arg ODP_UNUSED)
+{
+ odp_shm_t shm;
+ uint8_t *address;
+ shared_test_data_t *glob_data;
+ uint8_t random_bytes[STRESS_RANDOM_SZ];
+ uint32_t index;
+ uint32_t size;
+ uint64_t align;
+ uint32_t flags;
+ uint8_t data;
+ uint32_t iter;
+ uint32_t i;
+
+ shm = odp_shm_lookup(MEM_NAME);
+ glob_data = odp_shm_addr(shm);
+ CU_ASSERT(glob_data != NULL);
+
+ /* wait for general GO! */
+ odp_barrier_wait(&glob_data->test_barrier1);
+
+ /*
+ * at each iteration: pick up a random index for
+ * glob_data->stress[index]: If the entry is free, allocated mem
+ * randomly. If it is already allocated, make checks and free it:
+ * Note that different tread can allocate or free a given block
+ */
+ for (iter = 0; iter < STRESS_ITERATION; iter++) {
+ /* get 4 random bytes from which index, size ,align, flags
+ * and data will be derived:
+ */
+ odp_random_data(random_bytes, STRESS_RANDOM_SZ, 0);
+ index = random_bytes[0] & (STRESS_SIZE - 1);
+
+ odp_spinlock_lock(&glob_data->stress_lock);
+
+ switch (glob_data->stress[index].state) {
+ case STRESS_FREE:
+ /* allocated a new block for this entry */
+
+ glob_data->stress[index].state = STRESS_BUSY;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+
+ size = (random_bytes[1] + 1) << 6; /* up to 16Kb */
+ /* we just play with the VA flag. randomly setting
+ * the mlock flag may exceed user ulimit -l
+ */
+ flags = (_global_shm_capa.flags & ODP_SHM_SINGLE_VA) ?
+ (random_bytes[2] & ODP_SHM_SINGLE_VA) : 0;
+
+ align = (random_bytes[3] + 1) << 6;/* up to 16Kb */
+ data = random_bytes[4];
+
+ snprintf(glob_data->stress[index].name, NAME_LEN,
+ "%s-%09d", MEM_NAME, index);
+ shm = odp_shm_reserve(glob_data->stress[index].name,
+ size, align, flags);
+ glob_data->stress[index].shm = shm;
+ if (shm == ODP_SHM_INVALID) { /* out of mem ? */
+ odp_spinlock_lock(&glob_data->stress_lock);
+ glob_data->stress[index].state = STRESS_ALLOC;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+ continue;
+ }
+
+ address = odp_shm_addr(shm);
+ CU_ASSERT(address != NULL);
+ glob_data->stress[index].address = address;
+ glob_data->stress[index].flags = flags;
+ glob_data->stress[index].size = size;
+ glob_data->stress[index].align = align;
+ glob_data->stress[index].data_val = data;
+
+ /* write some data: writing each byte would be a
+ * waste of time: just make sure each page is reached */
+ for (i = 0; i < size; i += 256)
+ address[i] = (data++) & 0xFF;
+ odp_spinlock_lock(&glob_data->stress_lock);
+ glob_data->stress[index].state = STRESS_ALLOC;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+
+ break;
+
+ case STRESS_ALLOC:
+ /* free the block for this entry */
+
+ glob_data->stress[index].state = STRESS_BUSY;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+ shm = glob_data->stress[index].shm;
+
+ if (shm == ODP_SHM_INVALID) { /* out of mem ? */
+ odp_spinlock_lock(&glob_data->stress_lock);
+ glob_data->stress[index].state = STRESS_FREE;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+ continue;
+ }
+
+ CU_ASSERT(odp_shm_lookup(glob_data->stress[index].name)
+ != 0);
+
+ address = odp_shm_addr(shm);
+ CU_ASSERT(address != NULL);
+
+ align = glob_data->stress[index].align;
+ if (align) {
+ align = glob_data->stress[index].align;
+ CU_ASSERT(((uintptr_t)address & (align - 1))
+ == 0)
+ }
+
+ flags = glob_data->stress[index].flags;
+ if (flags & ODP_SHM_SINGLE_VA)
+ CU_ASSERT(glob_data->stress[index].address ==
+ address)
+
+ /* check that data is reachable and correct: */
+ data = glob_data->stress[index].data_val;
+ size = glob_data->stress[index].size;
+ for (i = 0; i < size; i += 256) {
+ CU_ASSERT(address[i] == (data & 0xFF));
+ data++;
+ }
+
+ CU_ASSERT(!odp_shm_free(glob_data->stress[index].shm));
+
+ odp_spinlock_lock(&glob_data->stress_lock);
+ glob_data->stress[index].state = STRESS_FREE;
+ odp_spinlock_unlock(&glob_data->stress_lock);
+
+ break;
+
+ case STRESS_BUSY:
+ default:
+ odp_spinlock_unlock(&glob_data->stress_lock);
+ break;
+ }
+ }
+
+ fflush(stdout);
+ return CU_get_number_of_failures();
+}
+
+/*
+ * stress tests
+ */
+static void shmem_test_stress(void)
+{
+ odp_shm_t shm;
+ odp_shm_t globshm;
+ shared_test_data_t *glob_data;
+ uint32_t i;
+ int num;
+
+ globshm = odp_shm_reserve(MEM_NAME, sizeof(shared_test_data_t),
+ 0, 0);
+ CU_ASSERT(ODP_SHM_INVALID != globshm);
+ glob_data = odp_shm_addr(globshm);
+ CU_ASSERT(glob_data != NULL);
+
+ num = odp_cpumask_default_worker(NULL, 0);
+ if (num > MAX_WORKERS)
+ num = MAX_WORKERS;
+
+ glob_data->nb_threads = num;
+ odp_barrier_init(&glob_data->test_barrier1, num);
+ odp_spinlock_init(&glob_data->stress_lock);
+
+ /* before starting the threads, mark all entries as free: */
+ for (i = 0; i < STRESS_SIZE; i++)
+ glob_data->stress[i].state = STRESS_FREE;
+
+ /* create threads */
+ odp_cunit_thread_create(num, run_test_stress, NULL, 0, 0);
+
+ /* wait for all thread endings: */
+ CU_ASSERT(odp_cunit_thread_join(num) >= 0);
+
+ /* release left overs: */
+ for (i = 0; i < STRESS_SIZE; i++) {
+ shm = glob_data->stress[i].shm;
+ if ((glob_data->stress[i].state == STRESS_ALLOC) &&
+ (glob_data->stress[i].shm != ODP_SHM_INVALID)) {
+ CU_ASSERT(odp_shm_lookup(glob_data->stress[i].name) ==
+ shm);
+ CU_ASSERT(!odp_shm_free(shm));
+ }
+ }
+
+ CU_ASSERT(0 == odp_shm_free(globshm));
+
+ /* check that no memory is left over: */
+}
+
+static int shm_suite_init(void)
+{
+ if (odp_shm_capability(&_global_shm_capa)) {
+ ODPH_ERR("Failed to read SHM capability\n");
+ return -1;
+ }
+ return 0;
+}
+
+odp_testinfo_t shmem_suite[] = {
+ ODP_TEST_INFO(shmem_test_capability),
+ ODP_TEST_INFO(shmem_test_reserve),
+ ODP_TEST_INFO(shmem_test_info),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_flag_hp, shmem_check_flag_hp),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_flag_no_hp, shmem_check_flag_no_hp),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_flag_proc, shmem_check_flag_proc),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_flag_export, shmem_check_flag_export),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_flag_hw_access, shmem_check_flag_hw_access),
+ ODP_TEST_INFO(shmem_test_max_reserve),
+ ODP_TEST_INFO(shmem_test_multi_thread),
+ ODP_TEST_INFO(shmem_test_reserve_after_fork),
+ ODP_TEST_INFO_CONDITIONAL(shmem_test_singleva_after_fork, shmem_check_flag_single_va),
+ ODP_TEST_INFO(shmem_test_stress),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t shmem_suites[] = {
+ {"Shared Memory", shm_suite_init, NULL, shmem_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(shmem_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/stash/.gitignore b/test/validation/api/stash/.gitignore
new file mode 100644
index 000000000..577dc61c9
--- /dev/null
+++ b/test/validation/api/stash/.gitignore
@@ -0,0 +1 @@
+stash_main
diff --git a/test/validation/api/stash/Makefile.am b/test/validation/api/stash/Makefile.am
new file mode 100644
index 000000000..5ff3a7533
--- /dev/null
+++ b/test/validation/api/stash/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = stash_main
+stash_main_SOURCES = stash.c
diff --git a/test/validation/api/stash/stash.c b/test/validation/api/stash/stash.c
new file mode 100644
index 000000000..86d2cb0b6
--- /dev/null
+++ b/test/validation/api/stash/stash.c
@@ -0,0 +1,1395 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020-2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "odp_cunit_common.h"
+
+#include <string.h>
+
+#define MAGIC_U64 0x8b7438fa56c82e96
+#define MAGIC_U32 0x74a13b94
+#define MAGIC_U16 0x25bf
+#define MAGIC_U8 0xab
+
+#define VAL_U64 0x6b89f0742a672c34
+#define VAL_U32 0x713d847b
+#define VAL_U16 0xb587
+#define VAL_U8 0x9d
+
+#define NUM_U64 1024
+#define NUM_U32 1024
+#define NUM_U16 1024
+#define NUM_U8 256
+
+#define CACHE_SIZE 8
+#define BURST 32
+#define BATCH 16
+#define MAX_RETRY 1024
+#define RETRY_MSEC 100
+
+typedef enum stash_op_t {
+ STASH_GEN,
+ STASH_U32,
+ STASH_U64,
+ STASH_PTR
+} stash_op_t;
+
+typedef struct num_obj_t {
+ uint32_t u64;
+ uint32_t u32;
+ uint32_t u16;
+ uint32_t u8;
+
+} num_obj_t;
+
+typedef struct global_t {
+ odp_stash_capability_t capa_default;
+ odp_stash_capability_t capa_fifo;
+ int fifo_supported;
+ num_obj_t num_default;
+ num_obj_t num_fifo;
+ uint32_t cache_size_default;
+
+} global_t;
+
+static global_t global;
+
+static int stash_suite_init(void)
+{
+ odp_stash_capability_t *capa_default = &global.capa_default;
+ odp_stash_capability_t *capa_fifo = &global.capa_fifo;
+
+ if (odp_stash_capability(capa_default, ODP_STASH_TYPE_DEFAULT)) {
+ ODPH_ERR("Stash capability failed for the default type\n");
+ return -1;
+ }
+
+ global.num_default.u64 = NUM_U64;
+ global.num_default.u32 = NUM_U32;
+ global.num_default.u16 = NUM_U16;
+ global.num_default.u8 = NUM_U8;
+
+ if (global.num_default.u64 > capa_default->max_num_obj)
+ global.num_default.u64 = capa_default->max_num_obj;
+ if (global.num_default.u32 > capa_default->max_num_obj)
+ global.num_default.u32 = capa_default->max_num_obj;
+ if (global.num_default.u16 > capa_default->max_num_obj)
+ global.num_default.u16 = capa_default->max_num_obj;
+ if (global.num_default.u8 > capa_default->max_num_obj)
+ global.num_default.u8 = capa_default->max_num_obj;
+
+ global.cache_size_default = CACHE_SIZE;
+ if (global.cache_size_default > capa_default->max_cache_size)
+ global.cache_size_default = capa_default->max_cache_size;
+
+ global.fifo_supported = 0;
+ if (odp_stash_capability(capa_fifo, ODP_STASH_TYPE_FIFO) == 0) {
+ if (capa_fifo->max_stashes)
+ global.fifo_supported = 1;
+ }
+
+ if (global.fifo_supported) {
+ global.num_fifo.u64 = NUM_U64;
+ global.num_fifo.u32 = NUM_U32;
+ global.num_fifo.u16 = NUM_U16;
+ global.num_fifo.u8 = NUM_U8;
+
+ if (global.num_fifo.u64 > capa_fifo->max_num_obj)
+ global.num_fifo.u64 = capa_fifo->max_num_obj;
+ if (global.num_fifo.u32 > capa_fifo->max_num_obj)
+ global.num_fifo.u32 = capa_fifo->max_num_obj;
+ if (global.num_fifo.u16 > capa_fifo->max_num_obj)
+ global.num_fifo.u16 = capa_fifo->max_num_obj;
+ if (global.num_fifo.u8 > capa_fifo->max_num_obj)
+ global.num_fifo.u8 = capa_fifo->max_num_obj;
+ }
+
+ return 0;
+}
+
+static void stash_capability(void)
+{
+ odp_stash_capability_t capa;
+
+ memset(&capa, 0, sizeof(odp_stash_capability_t));
+ CU_ASSERT_FATAL(odp_stash_capability(&capa, ODP_STASH_TYPE_DEFAULT) == 0);
+ CU_ASSERT(capa.max_stashes_any_type > 0);
+ CU_ASSERT(capa.max_stashes > 0);
+ CU_ASSERT(capa.max_num_obj > 0);
+ CU_ASSERT(capa.max_obj_size >= sizeof(uint32_t));
+ CU_ASSERT(capa.max_get_batch >= 1);
+ CU_ASSERT(capa.max_put_batch >= 1);
+
+ CU_ASSERT(capa.max_num.u8 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.u16 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.u32 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.max_obj_size >= capa.max_num_obj);
+ if (capa.max_obj_size >= 8)
+ CU_ASSERT(capa.max_num.u64 >= capa.max_num_obj);
+ if (capa.max_obj_size < 8)
+ CU_ASSERT(capa.max_num.u64 == 0);
+ if (capa.max_obj_size >= 16)
+ CU_ASSERT(capa.max_num.u128 >= capa.max_num_obj);
+ if (capa.max_obj_size < 16)
+ CU_ASSERT(capa.max_num.u128 == 0);
+
+ memset(&capa, 0, sizeof(odp_stash_capability_t));
+ CU_ASSERT_FATAL(odp_stash_capability(&capa, ODP_STASH_TYPE_FIFO) == 0);
+ CU_ASSERT(capa.max_stashes_any_type > 0);
+
+ if (capa.max_stashes == 0)
+ return;
+
+ CU_ASSERT(capa.max_num_obj > 0);
+ CU_ASSERT(capa.max_obj_size >= sizeof(uint32_t));
+ CU_ASSERT(capa.max_get_batch >= 1);
+ CU_ASSERT(capa.max_put_batch >= 1);
+
+ CU_ASSERT(capa.max_num.u8 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.u16 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.u32 >= capa.max_num_obj);
+ CU_ASSERT(capa.max_num.max_obj_size >= capa.max_num_obj);
+ if (capa.max_obj_size >= 8)
+ CU_ASSERT(capa.max_num.u64 >= capa.max_num_obj);
+ if (capa.max_obj_size < 8)
+ CU_ASSERT(capa.max_num.u64 == 0);
+ if (capa.max_obj_size >= 16)
+ CU_ASSERT(capa.max_num.u128 >= capa.max_num_obj);
+ if (capa.max_obj_size < 16)
+ CU_ASSERT(capa.max_num.u128 == 0);
+}
+
+static void param_defaults(uint8_t fill)
+{
+ odp_stash_param_t param;
+
+ memset(&param, fill, sizeof(param));
+ odp_stash_param_init(&param);
+ CU_ASSERT(param.type == ODP_STASH_TYPE_DEFAULT);
+ CU_ASSERT(param.put_mode == ODP_STASH_OP_MT);
+ CU_ASSERT(param.get_mode == ODP_STASH_OP_MT);
+ CU_ASSERT(param.cache_size == 0);
+ CU_ASSERT(param.stats.all == 0);
+ CU_ASSERT(param.stats.bit.count == 0);
+ CU_ASSERT(param.stats.bit.cache_count == 0);
+ CU_ASSERT(param.strict_size == 0);
+}
+
+static void stash_param_defaults(void)
+{
+ param_defaults(0);
+ param_defaults(0xff);
+}
+
+static void stash_create_u64(void)
+{
+ odp_stash_t stash, lookup;
+ odp_stash_param_t param;
+ uint32_t num = global.num_default.u64;
+
+ odp_stash_param_init(&param);
+ param.num_obj = num;
+ param.obj_size = sizeof(uint64_t);
+
+ stash = odp_stash_create("test_stash_u64", &param);
+
+ CU_ASSERT_FATAL(stash != ODP_STASH_INVALID);
+
+ printf("\n Stash handle: 0x%" PRIx64 "\n", odp_stash_to_u64(stash));
+
+ lookup = odp_stash_lookup("test_stash_u64");
+ CU_ASSERT(lookup != ODP_STASH_INVALID);
+ CU_ASSERT(stash == lookup);
+ CU_ASSERT(odp_stash_lookup("foo") == ODP_STASH_INVALID);
+
+ CU_ASSERT_FATAL(odp_stash_destroy(stash) == 0);
+}
+
+static void stash_create_u32(void)
+{
+ odp_stash_t stash, lookup;
+ odp_stash_param_t param;
+ uint32_t num = global.num_default.u32;
+
+ odp_stash_param_init(&param);
+ param.num_obj = num;
+ param.obj_size = sizeof(uint32_t);
+
+ stash = odp_stash_create("test_stash_u32", &param);
+
+ CU_ASSERT_FATAL(stash != ODP_STASH_INVALID);
+
+ printf("\n Stash handle: 0x%" PRIx64 "\n", odp_stash_to_u64(stash));
+
+ printf("\n--- Stash print ----\n");
+ odp_stash_print(stash);
+
+ lookup = odp_stash_lookup("test_stash_u32");
+ CU_ASSERT(lookup != ODP_STASH_INVALID);
+ CU_ASSERT(stash == lookup);
+ CU_ASSERT(odp_stash_lookup("foo") == ODP_STASH_INVALID);
+
+ CU_ASSERT_FATAL(odp_stash_destroy(stash) == 0);
+}
+
+static void stash_create_u64_all(void)
+{
+ odp_stash_param_t param;
+ uint64_t input, output;
+ uint32_t i, retry;
+ int32_t ret;
+ uint32_t num_obj = global.num_default.u32;
+ uint32_t num_stash = global.capa_default.max_stashes;
+ odp_stash_t stash[num_stash];
+
+ CU_ASSERT_FATAL(num_stash > 0);
+
+ odp_stash_param_init(&param);
+ param.num_obj = num_obj;
+ param.obj_size = sizeof(uint64_t);
+
+ for (i = 0; i < num_stash; i++) {
+ stash[i] = odp_stash_create("test_stash_u64_all", &param);
+ CU_ASSERT_FATAL(stash[i] != ODP_STASH_INVALID);
+ CU_ASSERT_FATAL(odp_stash_get(stash[i], &output, 1) == 0);
+
+ input = i;
+ CU_ASSERT(odp_stash_put(stash[i], &input, 1) == 1);
+ }
+
+ for (i = 0; i < num_stash; i++) {
+ ret = 0;
+
+ for (retry = 0; retry < RETRY_MSEC; retry++) {
+ /* Extra delay allows HW latency from put() to get() */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ ret = odp_stash_get(stash[i], &output, 1);
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == 1);
+ CU_ASSERT(output == i);
+ }
+
+ for (i = 0; i < num_stash; i++)
+ CU_ASSERT_FATAL(odp_stash_destroy(stash[i]) == 0);
+}
+
+static void stash_create_u32_all(void)
+{
+ odp_stash_param_t param;
+ uint32_t i, retry, input, output;
+ int32_t ret;
+ uint32_t num_obj = global.num_default.u32;
+ uint32_t num_stash = global.capa_default.max_stashes;
+ odp_stash_t stash[num_stash];
+
+ CU_ASSERT_FATAL(num_stash > 0);
+
+ odp_stash_param_init(&param);
+ param.num_obj = num_obj;
+ param.obj_size = sizeof(uint32_t);
+
+ for (i = 0; i < num_stash; i++) {
+ stash[i] = odp_stash_create("test_stash_u32_all", &param);
+ CU_ASSERT_FATAL(stash[i] != ODP_STASH_INVALID);
+ CU_ASSERT_FATAL(odp_stash_get(stash[i], &output, 1) == 0);
+
+ input = i;
+ CU_ASSERT(odp_stash_put(stash[i], &input, 1) == 1);
+ }
+
+ for (i = 0; i < num_stash; i++) {
+ ret = 0;
+
+ for (retry = 0; retry < RETRY_MSEC; retry++) {
+ /* Extra delay allows HW latency from put() to get() */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ ret = odp_stash_get(stash[i], &output, 1);
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == 1);
+ CU_ASSERT(output == i);
+ }
+
+ for (i = 0; i < num_stash; i++)
+ CU_ASSERT_FATAL(odp_stash_destroy(stash[i]) == 0);
+}
+
+static void stash_create_fifo_u64_all(void)
+{
+ odp_stash_param_t param;
+ uint64_t input, output;
+ uint32_t i, retry;
+ int32_t ret;
+ uint32_t num_obj = global.num_fifo.u64;
+ uint32_t num_stash = global.capa_fifo.max_stashes;
+ odp_stash_t stash[num_stash];
+
+ CU_ASSERT_FATAL(num_stash > 0);
+
+ odp_stash_param_init(&param);
+ param.type = ODP_STASH_TYPE_FIFO;
+ param.num_obj = num_obj;
+ param.obj_size = sizeof(uint64_t);
+
+ for (i = 0; i < num_stash; i++) {
+ stash[i] = odp_stash_create(NULL, &param);
+ CU_ASSERT_FATAL(stash[i] != ODP_STASH_INVALID);
+ CU_ASSERT_FATAL(odp_stash_get(stash[i], &output, 1) == 0);
+
+ input = i;
+ CU_ASSERT(odp_stash_put(stash[i], &input, 1) == 1);
+ }
+
+ for (i = 0; i < num_stash; i++) {
+ ret = 0;
+
+ for (retry = 0; retry < RETRY_MSEC; retry++) {
+ /* Extra delay allows HW latency from put() to get() */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ ret = odp_stash_get(stash[i], &output, 1);
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == 1);
+ CU_ASSERT(output == i);
+ }
+
+ for (i = 0; i < num_stash; i++)
+ CU_ASSERT_FATAL(odp_stash_destroy(stash[i]) == 0);
+}
+
+static void stash_create_fifo_u32_all(void)
+{
+ odp_stash_param_t param;
+ uint32_t i, retry, input, output;
+ int32_t ret;
+ uint32_t num_obj = global.num_fifo.u32;
+ uint32_t num_stash = global.capa_fifo.max_stashes;
+ odp_stash_t stash[num_stash];
+
+ CU_ASSERT_FATAL(num_stash > 0);
+
+ odp_stash_param_init(&param);
+ param.type = ODP_STASH_TYPE_FIFO;
+ param.num_obj = num_obj;
+ param.obj_size = sizeof(uint32_t);
+
+ for (i = 0; i < num_stash; i++) {
+ stash[i] = odp_stash_create(NULL, &param);
+ CU_ASSERT_FATAL(stash[i] != ODP_STASH_INVALID);
+ CU_ASSERT_FATAL(odp_stash_get(stash[i], &output, 1) == 0);
+
+ input = i;
+ CU_ASSERT(odp_stash_put(stash[i], &input, 1) == 1);
+ }
+
+ for (i = 0; i < num_stash; i++) {
+ ret = 0;
+
+ for (retry = 0; retry < RETRY_MSEC; retry++) {
+ /* Extra delay allows HW latency from put() to get() */
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS);
+ ret = odp_stash_get(stash[i], &output, 1);
+ if (ret)
+ break;
+ }
+
+ CU_ASSERT(ret == 1);
+ CU_ASSERT(output == i);
+ }
+
+ for (i = 0; i < num_stash; i++)
+ CU_ASSERT_FATAL(odp_stash_destroy(stash[i]) == 0);
+}
+
+static void stash_stats_u32(void)
+{
+ odp_stash_t stash;
+ odp_stash_param_t param;
+ odp_stash_stats_t stats;
+ int capa_count, capa_cache_count;
+ uint32_t i, input, output;
+ uint32_t max_num = 10;
+ uint32_t num = max_num / 2;
+ uint32_t num_put = 0;
+
+ capa_count = global.capa_default.stats.bit.count;
+ capa_cache_count = global.capa_default.stats.bit.cache_count;
+
+ odp_stash_param_init(&param);
+ param.num_obj = max_num;
+ param.obj_size = sizeof(uint32_t);
+ param.stats.bit.count = capa_count;
+ param.stats.bit.cache_count = capa_cache_count;
+
+ stash = odp_stash_create("test_stats_u32", &param);
+ CU_ASSERT_FATAL(stash != ODP_STASH_INVALID);
+
+ memset(&stats, 0xff, sizeof(odp_stash_stats_t));
+
+ CU_ASSERT_FATAL(odp_stash_stats(stash, &stats) == 0);
+ CU_ASSERT(stats.count == 0);
+ CU_ASSERT(stats.cache_count == 0);
+
+ for (i = 0; i < num; i++) {
+ input = i;
+ if (odp_stash_put_u32(stash, &input, 1) == 1)
+ num_put++;
+ }
+
+ CU_ASSERT(num_put == num);
+
+ memset(&stats, 0xff, sizeof(odp_stash_stats_t));
+
+ CU_ASSERT_FATAL(odp_stash_stats(stash, &stats) == 0);
+
+ if (capa_count) {
+ /* CU_ASSERT needs extra brackets */
+ CU_ASSERT(stats.count <= num_put);
+ } else {
+ CU_ASSERT(stats.count == 0);
+ }
+
+ if (capa_cache_count) {
+ /* CU_ASSERT needs extra brackets */
+ CU_ASSERT(stats.cache_count <= num_put);
+ } else {
+ CU_ASSERT(stats.cache_count == 0);
+ }
+
+ if (capa_count && capa_cache_count)
+ CU_ASSERT((stats.count + stats.cache_count) == num_put);
+
+ for (i = 0; i < num_put; i++) {
+ output = -1;
+ CU_ASSERT(odp_stash_get_u32(stash, &output, 1) == 1);
+ CU_ASSERT(output < num);
+ }
+
+ CU_ASSERT_FATAL(odp_stash_destroy(stash) == 0);
+}
+
+static void stash_default_put(uint32_t size, int32_t burst, stash_op_t op, int batch,
+ odp_bool_t strict_size)
+{
+ odp_stash_t stash;
+ odp_stash_param_t param;
+ int32_t i, ret, retry, num_left;
+ int32_t num, max_burst, num_stashed;
+ void *input, *output;
+
+ if (batch) {
+ CU_ASSERT_FATAL(global.capa_default.max_get_batch >= 1);
+ CU_ASSERT_FATAL(global.capa_default.max_put_batch >= 1);
+
+ if (burst > (int32_t)global.capa_default.max_get_batch)
+ burst = global.capa_default.max_get_batch;
+ if (burst > (int32_t)global.capa_default.max_put_batch)
+ burst = global.capa_default.max_put_batch;
+ }
+
+ uint64_t input_u64[burst];
+ uint64_t output_u64[burst + 2];
+ uint32_t input_u32[burst];
+ uint32_t output_u32[burst + 2];
+ uint16_t input_u16[burst];
+ uint16_t output_u16[burst + 2];
+ uint8_t input_u8[burst];
+ uint8_t output_u8[burst + 2];
+
+ if (size == sizeof(uint64_t)) {
+ num = global.num_default.u64;
+ input = input_u64;
+ output = &output_u64[1];
+ } else if (size == sizeof(uint32_t)) {
+ num = global.num_default.u32;
+ input = input_u32;
+ output = &output_u32[1];
+ } else if (size == sizeof(uint16_t)) {
+ num = global.num_default.u16;
+ input = input_u16;
+ output = &output_u16[1];
+ } else {
+ num = global.num_default.u8;
+ input = input_u8;
+ output = &output_u8[1];
+ }
+
+ for (i = 0; i < burst; i++) {
+ input_u64[i] = VAL_U64;
+ input_u32[i] = VAL_U32;
+ input_u16[i] = VAL_U16;
+ input_u8[i] = VAL_U8;
+ }
+
+ odp_stash_param_init(&param);
+ param.num_obj = num;
+ param.obj_size = size;
+ param.cache_size = global.cache_size_default;
+ param.strict_size = strict_size;
+
+ stash = odp_stash_create("test_stash_default", &param);
+
+ CU_ASSERT_FATAL(stash != ODP_STASH_INVALID);
+
+ /* Stash is empty */
+ CU_ASSERT_FATAL(odp_stash_get(stash, output, 1) == 0);
+
+ retry = MAX_RETRY;
+ num_left = num;
+ max_burst = burst;
+ num_stashed = 0;
+
+ /* Try to store extra objects if strict mode is not enabled */
+ if (!strict_size)
+ num_left += burst;
+
+ while (num_left > 0) {
+ if (op == STASH_GEN) {
+ if (batch)
+ ret = odp_stash_put_batch(stash, input, burst);
+ else
+ ret = odp_stash_put(stash, input, burst);
+ } else if (op == STASH_U32) {
+ if (batch)
+ ret = odp_stash_put_u32_batch(stash, input_u32, burst);
+ else
+ ret = odp_stash_put_u32(stash, input_u32, burst);
+ } else if (op == STASH_U64) {
+ if (batch)
+ ret = odp_stash_put_u64_batch(stash, input_u64, burst);
+ else
+ ret = odp_stash_put_u64(stash, input_u64, burst);
+ } else if (op == STASH_PTR) {
+ if (batch)
+ ret = odp_stash_put_ptr_batch(stash, input, burst);
+ else
+ ret = odp_stash_put_ptr(stash, input, burst);
+ } else {
+ ret = -1;
+ }
+ CU_ASSERT_FATAL(ret >= 0);
+ CU_ASSERT_FATAL(ret <= burst);
+
+ num_stashed += ret;
+
+ if (batch) {
+ CU_ASSERT(ret == 0 || ret == burst);
+ if (num_left - ret < burst)
+ burst = num_left - ret;
+ }
+
+ if (ret) {
+ num_left -= ret;
+ retry = MAX_RETRY;
+ } else {
+ /* Stash full */
+ if (num_stashed >= num)
+ break;
+ retry--;
+ CU_ASSERT_FATAL(retry > 0);
+ }
+ }
+
+ burst = max_burst;
+ retry = MAX_RETRY;
+ num_left = num_stashed;
+ while (num_left > 0) {
+ memset(output, 0, burst * size);
+
+ /* Init first and last array element for under-/overflow checking */
+ if (size == sizeof(uint64_t)) {
+ output_u64[0] = MAGIC_U64;
+ output_u64[burst + 1] = MAGIC_U64;
+ } else if (size == sizeof(uint32_t)) {
+ output_u32[0] = MAGIC_U32;
+ output_u32[burst + 1] = MAGIC_U32;
+ } else if (size == sizeof(uint16_t)) {
+ output_u16[0] = MAGIC_U16;
+ output_u16[burst + 1] = MAGIC_U16;
+ } else {
+ output_u8[0] = MAGIC_U8;
+ output_u8[burst + 1] = MAGIC_U8;
+ }
+ if (op == STASH_GEN) {
+ if (batch)
+ ret = odp_stash_get_batch(stash, output, burst);
+ else
+ ret = odp_stash_get(stash, output, burst);
+ } else if (op == STASH_U32) {
+ if (batch)
+ ret = odp_stash_get_u32_batch(stash, &output_u32[1], burst);
+ else
+ ret = odp_stash_get_u32(stash, &output_u32[1], burst);
+ } else if (op == STASH_U64) {
+ if (batch)
+ ret = odp_stash_get_u64_batch(stash, &output_u64[1], burst);
+ else
+ ret = odp_stash_get_u64(stash, &output_u64[1], burst);
+ } else if (op == STASH_PTR) {
+ if (batch)
+ ret = odp_stash_get_ptr_batch(stash, output, burst);
+ else
+ ret = odp_stash_get_ptr(stash, output, burst);
+ } else {
+ ret = -1;
+ }
+ CU_ASSERT_FATAL(ret >= 0);
+ CU_ASSERT_FATAL(ret <= burst);
+
+ if (size == sizeof(uint64_t)) {
+ CU_ASSERT_FATAL(output_u64[0] == MAGIC_U64);
+ CU_ASSERT_FATAL(output_u64[burst + 1] == MAGIC_U64);
+ } else if (size == sizeof(uint32_t)) {
+ CU_ASSERT_FATAL(output_u32[0] == MAGIC_U32);
+ CU_ASSERT_FATAL(output_u32[burst + 1] == MAGIC_U32);
+ } else if (size == sizeof(uint16_t)) {
+ CU_ASSERT_FATAL(output_u16[0] == MAGIC_U16);
+ CU_ASSERT_FATAL(output_u16[burst + 1] == MAGIC_U16);
+ } else {
+ CU_ASSERT_FATAL(output_u8[0] == MAGIC_U8);
+ CU_ASSERT_FATAL(output_u8[burst + 1] == MAGIC_U8);
+ }
+
+ if (batch) {
+ CU_ASSERT(ret == 0 || ret == burst);
+ if (num_left - ret < burst)
+ burst = num_left - ret;
+ }
+
+ if (ret) {
+ for (i = 0; i < ret; i++) {
+ if (size == sizeof(uint64_t)) {
+ /* CU_ASSERT needs brackets around it */
+ CU_ASSERT(output_u64[i + 1] == VAL_U64);
+ } else if (size == sizeof(uint32_t)) {
+ CU_ASSERT(output_u32[i + 1] == VAL_U32);
+ } else if (size == sizeof(uint16_t)) {
+ CU_ASSERT(output_u16[i + 1] == VAL_U16);
+ } else {
+ CU_ASSERT(output_u8[i + 1] == VAL_U8);
+ }
+ }
+
+ num_left -= ret;
+ retry = MAX_RETRY;
+ } else {
+ retry--;
+ CU_ASSERT_FATAL(retry > 0);
+ }
+ }
+
+ /* Stash is empty again */
+ CU_ASSERT(odp_stash_get(stash, output, 1) == 0);
+ CU_ASSERT(odp_stash_flush_cache(stash) == 0);
+
+ CU_ASSERT_FATAL(odp_stash_destroy(stash) == 0);
+}
+
+static void stash_fifo_put(uint32_t size, int32_t burst, stash_op_t op, int batch,
+ odp_bool_t strict_size)
+{
+ odp_stash_t stash;
+ odp_stash_param_t param;
+ int32_t i, ret, retry, num_left;
+ int32_t num, max_burst, num_stashed;
+ void *input, *output;
+
+ if (batch) {
+ CU_ASSERT_FATAL(global.capa_fifo.max_get_batch >= 1);
+ CU_ASSERT_FATAL(global.capa_fifo.max_put_batch >= 1);
+
+ if (burst > (int32_t)global.capa_fifo.max_get_batch)
+ burst = global.capa_fifo.max_get_batch;
+ if (burst > (int32_t)global.capa_fifo.max_put_batch)
+ burst = global.capa_fifo.max_put_batch;
+ }
+
+ uint64_t input_u64[burst];
+ uint64_t output_u64[burst + 2];
+ uint32_t input_u32[burst];
+ uint32_t output_u32[burst + 2];
+ uint16_t input_u16[burst];
+ uint16_t output_u16[burst + 2];
+ uint8_t input_u8[burst];
+ uint8_t output_u8[burst + 2];
+
+ if (size == sizeof(uint64_t)) {
+ num = global.num_fifo.u64;
+ input = input_u64;
+ output = &output_u64[1];
+ } else if (size == sizeof(uint32_t)) {
+ num = global.num_fifo.u32;
+ input = input_u32;
+ output = &output_u32[1];
+ } else if (size == sizeof(uint16_t)) {
+ num = global.num_fifo.u16;
+ input = input_u16;
+ output = &output_u16[1];
+ } else {
+ num = global.num_fifo.u8;
+ input = input_u8;
+ output = &output_u8[1];
+ }
+
+ odp_stash_param_init(&param);
+ param.type = ODP_STASH_TYPE_FIFO;
+ param.num_obj = num;
+ param.obj_size = size;
+ param.strict_size = strict_size;
+
+ stash = odp_stash_create("test_stash_fifo", &param);
+
+ CU_ASSERT_FATAL(stash != ODP_STASH_INVALID);
+
+ /* Stash is empty */
+ CU_ASSERT_FATAL(odp_stash_get(stash, output, 1) == 0);
+
+ retry = MAX_RETRY;
+ num_left = num;
+ max_burst = burst;
+ num_stashed = 0;
+
+ /* Try to store extra objects if strict mode is not enabled */
+ if (!strict_size)
+ num_left += burst;
+
+ while (num_left > 0) {
+ for (i = 0; i < burst; i++) {
+ if (size == sizeof(uint64_t))
+ input_u64[i] = VAL_U64 + num_left - i;
+ else if (size == sizeof(uint32_t))
+ input_u32[i] = VAL_U32 + num_left - i;
+ else if (size == sizeof(uint16_t))
+ input_u16[i] = VAL_U16 + num_left - i;
+ else
+ input_u8[i] = VAL_U8 + num_left - i;
+ }
+ if (op == STASH_GEN) {
+ if (batch)
+ ret = odp_stash_put_batch(stash, input, burst);
+ else
+ ret = odp_stash_put(stash, input, burst);
+ } else if (op == STASH_U32) {
+ if (batch)
+ ret = odp_stash_put_u32_batch(stash, input_u32, burst);
+ else
+ ret = odp_stash_put_u32(stash, input_u32, burst);
+ } else if (op == STASH_U64) {
+ if (batch)
+ ret = odp_stash_put_u64_batch(stash, input_u64, burst);
+ else
+ ret = odp_stash_put_u64(stash, input_u64, burst);
+
+ } else if (op == STASH_PTR) {
+ if (batch)
+ ret = odp_stash_put_ptr_batch(stash, input, burst);
+ else
+ ret = odp_stash_put_ptr(stash, input, burst);
+ } else {
+ ret = -1;
+ }
+ CU_ASSERT_FATAL(ret >= 0);
+ CU_ASSERT_FATAL(ret <= burst);
+
+ num_stashed += ret;
+
+ if (batch) {
+ CU_ASSERT(ret == 0 || ret == burst);
+ if (num_left - ret < burst)
+ burst = num_left - ret;
+ }
+
+ if (ret) {
+ num_left -= ret;
+ retry = MAX_RETRY;
+ } else {
+ /* Stash full */
+ if (num_stashed >= num)
+ break;
+ retry--;
+ CU_ASSERT_FATAL(retry > 0);
+ }
+ }
+
+ burst = max_burst;
+ retry = MAX_RETRY;
+ num_left = num_stashed;
+ while (num_left > 0) {
+ memset(output, 0, burst * size);
+
+ /* Init first and last array element for under-/overflow checking */
+ if (size == sizeof(uint64_t)) {
+ output_u64[0] = MAGIC_U64;
+ output_u64[burst + 1] = MAGIC_U64;
+ } else if (size == sizeof(uint32_t)) {
+ output_u32[0] = MAGIC_U32;
+ output_u32[burst + 1] = MAGIC_U32;
+ } else if (size == sizeof(uint16_t)) {
+ output_u16[0] = MAGIC_U16;
+ output_u16[burst + 1] = MAGIC_U16;
+ } else {
+ output_u8[0] = MAGIC_U8;
+ output_u8[burst + 1] = MAGIC_U8;
+ }
+
+ if (op == STASH_GEN) {
+ if (batch)
+ ret = odp_stash_get_batch(stash, output, burst);
+ else
+ ret = odp_stash_get(stash, output, burst);
+ } else if (op == STASH_U32) {
+ if (batch)
+ ret = odp_stash_get_u32_batch(stash, &output_u32[1], burst);
+ else
+ ret = odp_stash_get_u32(stash, &output_u32[1], burst);
+ } else if (op == STASH_U64) {
+ if (batch)
+ ret = odp_stash_get_u64_batch(stash, &output_u64[1], burst);
+ else
+ ret = odp_stash_get_u64(stash, &output_u64[1], burst);
+ } else if (op == STASH_PTR) {
+ if (batch)
+ ret = odp_stash_get_ptr_batch(stash, output, burst);
+ else
+ ret = odp_stash_get_ptr(stash, output, burst);
+ } else {
+ ret = -1;
+ }
+ CU_ASSERT_FATAL(ret >= 0);
+ CU_ASSERT_FATAL(ret <= burst);
+
+ if (size == sizeof(uint64_t)) {
+ CU_ASSERT_FATAL(output_u64[0] == MAGIC_U64);
+ CU_ASSERT_FATAL(output_u64[burst + 1] == MAGIC_U64);
+ } else if (size == sizeof(uint32_t)) {
+ CU_ASSERT_FATAL(output_u32[0] == MAGIC_U32);
+ CU_ASSERT_FATAL(output_u32[burst + 1] == MAGIC_U32);
+ } else if (size == sizeof(uint16_t)) {
+ CU_ASSERT_FATAL(output_u16[0] == MAGIC_U16);
+ CU_ASSERT_FATAL(output_u16[burst + 1] == MAGIC_U16);
+ } else {
+ CU_ASSERT_FATAL(output_u8[0] == MAGIC_U8);
+ CU_ASSERT_FATAL(output_u8[burst + 1] == MAGIC_U8);
+ }
+
+ if (batch) {
+ CU_ASSERT(ret == 0 || ret == burst);
+ if (num_left - ret < burst)
+ burst = num_left - ret;
+ }
+
+ if (ret) {
+ for (i = 0; i < ret; i++) {
+ if (size == sizeof(uint64_t)) {
+ uint64_t val = VAL_U64 + num_left - i;
+
+ CU_ASSERT(output_u64[i + 1] == val);
+ } else if (size == sizeof(uint32_t)) {
+ uint32_t val = VAL_U32 + num_left - i;
+
+ CU_ASSERT(output_u32[i + 1] == val);
+ } else if (size == sizeof(uint16_t)) {
+ uint16_t val = VAL_U16 + num_left - i;
+
+ CU_ASSERT(output_u16[i + 1] == val);
+ } else {
+ uint8_t val = VAL_U8 + num_left - i;
+
+ CU_ASSERT(output_u8[i + 1] == val);
+ }
+ }
+
+ num_left -= ret;
+ retry = MAX_RETRY;
+ } else {
+ retry--;
+ CU_ASSERT_FATAL(retry > 0);
+ }
+ }
+
+ /* Stash is empty again */
+ CU_ASSERT(odp_stash_get(stash, output, 1) == 0);
+ CU_ASSERT(odp_stash_flush_cache(stash) == 0);
+
+ CU_ASSERT_FATAL(odp_stash_destroy(stash) == 0);
+}
+
+static int check_support_64(void)
+{
+ if (global.capa_default.max_obj_size >= sizeof(uint64_t))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_support_ptr(void)
+{
+ if (global.capa_default.max_obj_size >= sizeof(uintptr_t))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_support_fifo_64(void)
+{
+ if (global.fifo_supported &&
+ global.capa_fifo.max_obj_size >= sizeof(uint64_t))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_support_fifo_ptr(void)
+{
+ if (global.fifo_supported &&
+ global.capa_fifo.max_obj_size >= sizeof(uintptr_t))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_support_fifo(void)
+{
+ if (global.fifo_supported)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void stash_default_put_u64_1(void)
+{
+ stash_default_put(sizeof(uint64_t), 1, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint64_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u64_n(void)
+{
+ stash_default_put(sizeof(uint64_t), BURST, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint64_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_default_u64_put_u64_1(void)
+{
+ stash_default_put(sizeof(uint64_t), 1, STASH_U64, 0, false);
+ stash_default_put(sizeof(uint64_t), 1, STASH_U64, 0, true);
+}
+
+static void stash_default_u64_put_u64_n(void)
+{
+ stash_default_put(sizeof(uint64_t), BURST, STASH_U64, 0, false);
+ stash_default_put(sizeof(uint64_t), BURST, STASH_U64, 0, true);
+}
+
+static void stash_default_put_ptr_1(void)
+{
+ stash_default_put(sizeof(uintptr_t), 1, STASH_PTR, 0, false);
+ stash_default_put(sizeof(uintptr_t), 1, STASH_PTR, 0, true);
+}
+
+static void stash_default_put_ptr_n(void)
+{
+ stash_default_put(sizeof(uintptr_t), BURST, STASH_PTR, 0, false);
+ stash_default_put(sizeof(uintptr_t), BURST, STASH_PTR, 0, true);
+}
+
+static void stash_default_put_u64_1_batch(void)
+{
+ stash_default_put(sizeof(uint64_t), 1, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint64_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_default_put_u64_n_batch(void)
+{
+ stash_default_put(sizeof(uint64_t), BATCH, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint64_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_default_u64_put_u64_1_batch(void)
+{
+ stash_default_put(sizeof(uint64_t), 1, STASH_U64, 1, false);
+ stash_default_put(sizeof(uint64_t), 1, STASH_U64, 1, true);
+}
+
+static void stash_default_u64_put_u64_n_batch(void)
+{
+ stash_default_put(sizeof(uint64_t), BATCH, STASH_U64, 1, false);
+ stash_default_put(sizeof(uint64_t), BATCH, STASH_U64, 1, true);
+}
+
+static void stash_default_put_ptr_1_batch(void)
+{
+ stash_default_put(sizeof(uintptr_t), 1, STASH_PTR, 1, false);
+ stash_default_put(sizeof(uintptr_t), 1, STASH_PTR, 1, true);
+}
+
+static void stash_default_put_ptr_n_batch(void)
+{
+ stash_default_put(sizeof(uintptr_t), BATCH, STASH_PTR, 1, false);
+ stash_default_put(sizeof(uintptr_t), BATCH, STASH_PTR, 1, true);
+}
+
+static void stash_default_put_u32_1(void)
+{
+ stash_default_put(sizeof(uint32_t), 1, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint32_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u32_n(void)
+{
+ stash_default_put(sizeof(uint32_t), BURST, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint32_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_default_u32_put_u32_1(void)
+{
+ stash_default_put(sizeof(uint32_t), 1, STASH_U32, 0, false);
+ stash_default_put(sizeof(uint32_t), 1, STASH_U32, 0, true);
+}
+
+static void stash_default_u32_put_u32_n(void)
+{
+ stash_default_put(sizeof(uint32_t), BURST, STASH_U32, 0, false);
+ stash_default_put(sizeof(uint32_t), BURST, STASH_U32, 0, true);
+}
+
+static void stash_default_put_u16_1(void)
+{
+ stash_default_put(sizeof(uint16_t), 1, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint16_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u16_n(void)
+{
+ stash_default_put(sizeof(uint16_t), BURST, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint16_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u8_1(void)
+{
+ stash_default_put(sizeof(uint8_t), 1, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint8_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u8_n(void)
+{
+ stash_default_put(sizeof(uint8_t), BURST, STASH_GEN, 0, false);
+ stash_default_put(sizeof(uint8_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_default_put_u32_1_batch(void)
+{
+ stash_default_put(sizeof(uint32_t), 1, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint32_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_default_put_u32_n_batch(void)
+{
+ stash_default_put(sizeof(uint32_t), BATCH, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint32_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_default_u32_put_u32_1_batch(void)
+{
+ stash_default_put(sizeof(uint32_t), 1, STASH_U32, 1, false);
+ stash_default_put(sizeof(uint32_t), 1, STASH_U32, 1, true);
+}
+
+static void stash_default_u32_put_u32_n_batch(void)
+{
+ stash_default_put(sizeof(uint32_t), BATCH, STASH_U32, 1, false);
+ stash_default_put(sizeof(uint32_t), BATCH, STASH_U32, 1, true);
+}
+
+static void stash_default_put_u16_1_batch(void)
+{
+ stash_default_put(sizeof(uint16_t), 1, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint16_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_default_put_u16_n_batch(void)
+{
+ stash_default_put(sizeof(uint16_t), BATCH, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint16_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_default_put_u8_1_batch(void)
+{
+ stash_default_put(sizeof(uint8_t), 1, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint8_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_default_put_u8_n_batch(void)
+{
+ stash_default_put(sizeof(uint8_t), BATCH, STASH_GEN, 1, false);
+ stash_default_put(sizeof(uint8_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u64_1(void)
+{
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u64_n(void)
+{
+ stash_fifo_put(sizeof(uint64_t), BURST, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint64_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_u64_put_u64_1(void)
+{
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_U64, 0, false);
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_U64, 0, true);
+}
+
+static void stash_fifo_u64_put_u64_n(void)
+{
+ stash_fifo_put(sizeof(uint64_t), BURST, STASH_U64, 0, false);
+ stash_fifo_put(sizeof(uint64_t), BURST, STASH_U64, 0, true);
+}
+
+static void stash_fifo_put_ptr_1(void)
+{
+ stash_fifo_put(sizeof(uintptr_t), 1, STASH_PTR, 0, false);
+ stash_fifo_put(sizeof(uintptr_t), 1, STASH_PTR, 0, true);
+}
+
+static void stash_fifo_put_ptr_n(void)
+{
+ stash_fifo_put(sizeof(uintptr_t), BURST, STASH_PTR, 0, false);
+ stash_fifo_put(sizeof(uintptr_t), BURST, STASH_PTR, 0, true);
+}
+
+static void stash_fifo_put_u32_1(void)
+{
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u32_n(void)
+{
+ stash_fifo_put(sizeof(uint32_t), BURST, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint32_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_u32_put_u32_1(void)
+{
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_U32, 0, false);
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_U32, 0, true);
+}
+
+static void stash_fifo_u32_put_u32_n(void)
+{
+ stash_fifo_put(sizeof(uint32_t), BURST, STASH_U32, 0, false);
+ stash_fifo_put(sizeof(uint32_t), BURST, STASH_U32, 0, true);
+}
+
+static void stash_fifo_put_u16_1(void)
+{
+ stash_fifo_put(sizeof(uint16_t), 1, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint16_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u16_n(void)
+{
+ stash_fifo_put(sizeof(uint16_t), BURST, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint16_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u8_1(void)
+{
+ stash_fifo_put(sizeof(uint8_t), 1, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint8_t), 1, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u8_n(void)
+{
+ stash_fifo_put(sizeof(uint8_t), BURST, STASH_GEN, 0, false);
+ stash_fifo_put(sizeof(uint8_t), BURST, STASH_GEN, 0, true);
+}
+
+static void stash_fifo_put_u64_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u64_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint64_t), BATCH, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint64_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_u64_put_u64_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_U64, 1, false);
+ stash_fifo_put(sizeof(uint64_t), 1, STASH_U64, 1, true);
+}
+
+static void stash_fifo_u64_put_u64_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint64_t), BATCH, STASH_U64, 1, false);
+ stash_fifo_put(sizeof(uint64_t), BATCH, STASH_U64, 1, true);
+}
+
+static void stash_fifo_put_ptr_1_batch(void)
+{
+ stash_fifo_put(sizeof(uintptr_t), 1, STASH_PTR, 1, false);
+ stash_fifo_put(sizeof(uintptr_t), 1, STASH_PTR, 1, true);
+}
+
+static void stash_fifo_put_ptr_n_batch(void)
+{
+ stash_fifo_put(sizeof(uintptr_t), BATCH, STASH_PTR, 1, false);
+ stash_fifo_put(sizeof(uintptr_t), BATCH, STASH_PTR, 1, true);
+}
+
+static void stash_fifo_put_u32_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u32_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint32_t), BATCH, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint32_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_u32_put_u32_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_U32, 1, false);
+ stash_fifo_put(sizeof(uint32_t), 1, STASH_U32, 1, true);
+}
+
+static void stash_fifo_u32_put_u32_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint32_t), BATCH, STASH_U32, 1, false);
+ stash_fifo_put(sizeof(uint32_t), BATCH, STASH_U32, 1, true);
+}
+
+static void stash_fifo_put_u16_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint16_t), 1, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint16_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u16_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint16_t), BATCH, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint16_t), BATCH, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u8_1_batch(void)
+{
+ stash_fifo_put(sizeof(uint8_t), 1, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint8_t), 1, STASH_GEN, 1, true);
+}
+
+static void stash_fifo_put_u8_n_batch(void)
+{
+ stash_fifo_put(sizeof(uint8_t), BATCH, STASH_GEN, 1, false);
+ stash_fifo_put(sizeof(uint8_t), BATCH, STASH_GEN, 1, true);
+}
+
+odp_testinfo_t stash_suite[] = {
+ ODP_TEST_INFO(stash_capability),
+ ODP_TEST_INFO(stash_param_defaults),
+ ODP_TEST_INFO_CONDITIONAL(stash_create_u64, check_support_64),
+ ODP_TEST_INFO(stash_create_u32),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_u64_1, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_u64_n, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_u64_put_u64_1, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_u64_put_u64_n, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_ptr_1, check_support_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_ptr_n, check_support_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_u64_1_batch, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_u64_n_batch, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_u64_put_u64_1_batch, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_u64_put_u64_n_batch, check_support_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_ptr_1_batch, check_support_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_default_put_ptr_n_batch, check_support_ptr),
+ ODP_TEST_INFO(stash_default_put_u32_1),
+ ODP_TEST_INFO(stash_default_put_u32_n),
+ ODP_TEST_INFO(stash_default_u32_put_u32_1),
+ ODP_TEST_INFO(stash_default_u32_put_u32_n),
+ ODP_TEST_INFO(stash_default_put_u16_1),
+ ODP_TEST_INFO(stash_default_put_u16_n),
+ ODP_TEST_INFO(stash_default_put_u8_1),
+ ODP_TEST_INFO(stash_default_put_u8_n),
+ ODP_TEST_INFO(stash_default_put_u32_1_batch),
+ ODP_TEST_INFO(stash_default_put_u32_n_batch),
+ ODP_TEST_INFO(stash_default_u32_put_u32_1_batch),
+ ODP_TEST_INFO(stash_default_u32_put_u32_n_batch),
+ ODP_TEST_INFO(stash_default_put_u16_1_batch),
+ ODP_TEST_INFO(stash_default_put_u16_n_batch),
+ ODP_TEST_INFO(stash_default_put_u8_1_batch),
+ ODP_TEST_INFO(stash_default_put_u8_n_batch),
+ ODP_TEST_INFO_CONDITIONAL(stash_create_u64_all, check_support_64),
+ ODP_TEST_INFO(stash_create_u32_all),
+ ODP_TEST_INFO(stash_stats_u32),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u64_1, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u64_n, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u64_put_u64_1, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u64_put_u64_n, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_ptr_1, check_support_fifo_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_ptr_n, check_support_fifo_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u32_1, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u32_n, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u32_put_u32_1, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u32_put_u32_n, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u16_1, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u16_n, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u8_1, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u8_n, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u64_1_batch, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u64_n_batch, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u64_put_u64_1_batch, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u64_put_u64_n_batch, check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_ptr_1_batch, check_support_fifo_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_ptr_n_batch, check_support_fifo_ptr),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u32_1_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u32_n_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u32_put_u32_1_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_u32_put_u32_n_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u16_1_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u16_n_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u8_1_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_fifo_put_u8_n_batch, check_support_fifo),
+ ODP_TEST_INFO_CONDITIONAL(stash_create_fifo_u64_all,
+ check_support_fifo_64),
+ ODP_TEST_INFO_CONDITIONAL(stash_create_fifo_u32_all,
+ check_support_fifo),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t stash_suites[] = {
+ {"Stash", stash_suite_init, NULL, stash_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(stash_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/validation/api/std/.gitignore b/test/validation/api/std/.gitignore
new file mode 100644
index 000000000..51fbc1d95
--- /dev/null
+++ b/test/validation/api/std/.gitignore
@@ -0,0 +1 @@
+std_main
diff --git a/test/validation/api/std/Makefile.am b/test/validation/api/std/Makefile.am
new file mode 100644
index 000000000..7cebadb83
--- /dev/null
+++ b/test/validation/api/std/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = std_main
+std_main_SOURCES = std.c
diff --git a/test/validation/api/std/std.c b/test/validation/api/std/std.c
new file mode 100644
index 000000000..85a8ec0a5
--- /dev/null
+++ b/test/validation/api/std/std.c
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <string.h>
+
+#define PATTERN 0x5e
+
+static void std_test_memcpy(void)
+{
+ uint8_t src[] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15};
+ uint8_t dst[16];
+ int ret;
+
+ memset(dst, 0, sizeof(dst));
+
+ odp_memcpy(dst, src, sizeof(dst));
+
+ ret = memcmp(dst, src, sizeof(dst));
+
+ CU_ASSERT(ret == 0);
+}
+
+static void std_test_memset(void)
+{
+ uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15};
+ uint8_t ref[16];
+ int ret;
+
+ odp_memset(data, PATTERN, sizeof(data));
+
+ memset(ref, PATTERN, sizeof(ref));
+
+ ret = memcmp(data, ref, sizeof(data));
+
+ CU_ASSERT(ret == 0);
+}
+
+static void std_test_memcmp(void)
+{
+ uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16};
+ uint8_t equal[] = {1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16};
+ uint8_t greater_11[] = {1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 99, 12, 13, 14, 15, 16};
+ uint8_t less_6[] = {1, 2, 3, 4, 5, 2, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16};
+ size_t i;
+
+ CU_ASSERT(odp_memcmp(data, equal, 0) == 0);
+ CU_ASSERT(odp_memcmp(data, equal, sizeof(data)) == 0);
+ CU_ASSERT(odp_memcmp(data, equal, sizeof(data) - 3) == 0);
+
+ CU_ASSERT(odp_memcmp(greater_11, data, sizeof(data)) > 0);
+ CU_ASSERT(odp_memcmp(greater_11, data, 11) > 0);
+ CU_ASSERT(odp_memcmp(greater_11, data, 10) == 0);
+
+ CU_ASSERT(odp_memcmp(less_6, data, sizeof(data)) < 0);
+ CU_ASSERT(odp_memcmp(less_6, data, 6) < 0);
+ CU_ASSERT(odp_memcmp(less_6, data, 5) == 0);
+
+ for (i = 0; i < sizeof(data); i++) {
+ uint8_t tmp;
+
+ CU_ASSERT(odp_memcmp(data, equal, i + 1) == 0);
+ tmp = equal[i];
+ equal[i] = 88;
+ CU_ASSERT(odp_memcmp(data, equal, i + 1) < 0);
+ equal[i] = 0;
+ CU_ASSERT(odp_memcmp(data, equal, i + 1) > 0);
+ equal[i] = tmp;
+ }
+}
+
+odp_testinfo_t std_suite[] = {
+ ODP_TEST_INFO(std_test_memcpy),
+ ODP_TEST_INFO(std_test_memset),
+ ODP_TEST_INFO(std_test_memcmp),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t std_suites[] = {
+ {"Std", NULL, NULL, std_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(std_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/system/.gitignore b/test/validation/api/system/.gitignore
index 347b1ee21..347b1ee21 100644
--- a/test/common_plat/validation/api/system/.gitignore
+++ b/test/validation/api/system/.gitignore
diff --git a/test/validation/api/system/Makefile.am b/test/validation/api/system/Makefile.am
new file mode 100644
index 000000000..8090b0b5a
--- /dev/null
+++ b/test/validation/api/system/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = system_main
+system_main_SOURCES = system.c
diff --git a/test/validation/api/system/system.c b/test/validation/api/system/system.c
new file mode 100644
index 000000000..5c63453c9
--- /dev/null
+++ b/test/validation/api/system/system.c
@@ -0,0 +1,697 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ */
+
+#include <ctype.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include "odp_cunit_common.h"
+#include "test_common_macros.h"
+
+#define PERIODS_100_MSEC 160
+#define RES_TRY_NUM 10
+#define PAGESZ_NUM 10
+
+#define GIGA_HZ 1000000000ULL
+#define KILO_HZ 1000ULL
+
+/* 10 usec wait time assumes >100kHz resolution on CPU cycles counter */
+#define WAIT_TIME (10 * ODP_TIME_USEC_IN_NS)
+
+static void test_version_api_str(void)
+{
+ int char_ok = 0;
+ char version_string[128];
+ char *s = version_string;
+
+ strncpy(version_string, odp_version_api_str(),
+ sizeof(version_string) - 1);
+
+ while (*s) {
+ if (isdigit((int)*s) || (strncmp(s, ".", 1) == 0)) {
+ char_ok = 1;
+ s++;
+ } else {
+ char_ok = 0;
+ ODPH_DBG("\nBAD VERSION=%s\n", version_string);
+ break;
+ }
+ }
+ CU_ASSERT(char_ok);
+}
+
+static void test_version_str(void)
+{
+ printf("\nAPI version:\n");
+ printf("%s\n\n", odp_version_api_str());
+
+ printf("Implementation name:\n");
+ printf("%s\n\n", odp_version_impl_name());
+
+ printf("Implementation details:\n");
+ printf("%s\n\n", odp_version_impl_str());
+}
+
+static void test_version_macro(void)
+{
+ CU_ASSERT(ODP_VERSION_API_NUM(0, 0, 0) < ODP_VERSION_API_NUM(0, 0, 1));
+ CU_ASSERT(ODP_VERSION_API_NUM(0, 0, 1) < ODP_VERSION_API_NUM(0, 1, 0));
+ CU_ASSERT(ODP_VERSION_API_NUM(0, 1, 0) < ODP_VERSION_API_NUM(1, 0, 0));
+ CU_ASSERT(ODP_VERSION_API_NUM(1, 90, 0) <
+ ODP_VERSION_API_NUM(1, 90, 1));
+
+ CU_ASSERT(ODP_VERSION_API_NUM(ODP_VERSION_API_GENERATION,
+ ODP_VERSION_API_MAJOR,
+ ODP_VERSION_API_MINOR) ==
+ ODP_VERSION_API);
+
+ CU_ASSERT(ODP_VERSION_API_NUM(ODP_VERSION_API_GENERATION,
+ ODP_VERSION_API_MAJOR, 0) <=
+ ODP_VERSION_API);
+
+ CU_ASSERT(ODP_VERSION_API_NUM(ODP_VERSION_API_GENERATION,
+ ODP_VERSION_API_MAJOR + 1, 0) >
+ ODP_VERSION_API);
+}
+
+static void system_test_odp_cpu_count(void)
+{
+ int cpus;
+
+ cpus = odp_cpu_count();
+ CU_ASSERT(0 < cpus);
+}
+
+static void system_test_cpu_cycles(void)
+{
+ uint64_t c2, c1, diff, max;
+
+ c1 = odp_cpu_cycles();
+ odp_time_wait_ns(WAIT_TIME);
+ c2 = odp_cpu_cycles();
+
+ CU_ASSERT(c2 != c1);
+
+ max = odp_cpu_cycles_max();
+
+ /* With 10 usec delay, diff should be small compared to the maximum.
+ * Otherwise, counter is going backwards. */
+ if (c2 > c1) {
+ diff = c2 - c1;
+ CU_ASSERT(diff < (max - diff));
+ }
+
+ /* Same applies also when there was a wrap. */
+ if (c2 < c1) {
+ diff = max - c1 + c2;
+ CU_ASSERT(diff < (max - diff));
+ }
+}
+
+static void system_test_cpu_cycles_max(void)
+{
+ uint64_t c2, c1;
+ uint64_t max1, max2;
+
+ max1 = odp_cpu_cycles_max();
+ odp_time_wait_ns(WAIT_TIME);
+ max2 = odp_cpu_cycles_max();
+
+ CU_ASSERT(max1 >= UINT32_MAX / 2);
+ CU_ASSERT(max1 == max2);
+
+ c1 = odp_cpu_cycles();
+ odp_time_wait_ns(WAIT_TIME);
+ c2 = odp_cpu_cycles();
+
+ CU_ASSERT(c1 <= max1 && c2 <= max1);
+}
+
+static void system_test_cpu_cycles_resolution(void)
+{
+ int i;
+ uint64_t res;
+ uint64_t c2, c1, max;
+ uint64_t test_cycles = odp_cpu_hz() / 100; /* CPU cycles in 10 msec */
+
+ max = odp_cpu_cycles_max();
+
+ res = odp_cpu_cycles_resolution();
+ CU_ASSERT(res != 0);
+ CU_ASSERT(res < max / 1024);
+
+ for (i = 0; i < RES_TRY_NUM; i++) {
+ c1 = odp_cpu_cycles();
+ odp_time_wait_ns(10 * ODP_TIME_MSEC_IN_NS + i);
+ c2 = odp_cpu_cycles();
+
+ /* Diff may be zero with low resolution */
+ if (test_cycles && test_cycles > res) {
+ uint64_t diff = odp_cpu_cycles_diff(c2, c1);
+
+ CU_ASSERT(diff >= res);
+ }
+
+ }
+}
+
+static void system_test_cpu_cycles_diff(void)
+{
+ uint64_t c2, c1, max;
+ uint64_t tmp, diff, res;
+
+ res = odp_cpu_cycles_resolution();
+ max = odp_cpu_cycles_max();
+
+ c1 = res;
+ c2 = 2 * res;
+ diff = odp_cpu_cycles_diff(c2, c1);
+ CU_ASSERT(diff == res);
+
+ c1 = odp_cpu_cycles();
+ odp_time_wait_ns(WAIT_TIME);
+ c2 = odp_cpu_cycles();
+ diff = odp_cpu_cycles_diff(c2, c1);
+ CU_ASSERT(diff > 0);
+ CU_ASSERT(diff < (max - diff));
+
+ /* check resolution for wrap */
+ c1 = max - 2 * res;
+ do
+ c2 = odp_cpu_cycles();
+ while (c1 < c2);
+
+ diff = odp_cpu_cycles_diff(c1, c1);
+ CU_ASSERT(diff == 0);
+
+ /* wrap */
+ tmp = c2 + (max - c1) + res;
+ diff = odp_cpu_cycles_diff(c2, c1);
+ CU_ASSERT(diff == tmp);
+
+ /* no wrap, revert args */
+ tmp = c1 - c2;
+ diff = odp_cpu_cycles_diff(c1, c2);
+ CU_ASSERT(diff == tmp);
+}
+
+static void system_test_cpu_cycles_long_period(void)
+{
+ int i;
+ int periods = PERIODS_100_MSEC;
+ uint64_t max_period_duration = 100 * ODP_TIME_MSEC_IN_NS + periods - 1;
+ uint64_t c2, c1, c3, max;
+ uint64_t tmp, diff, res;
+
+ res = odp_cpu_cycles_resolution();
+ max = odp_cpu_cycles_max();
+
+ c3 = odp_cpu_cycles();
+
+ CU_ASSERT(c3 <= max);
+ /*
+ * If the cycle counter is not close to wrapping around during
+ * the test, then speed up the test by not trying to see the wrap
+ * around too hard. Assume cycle counter frequency of less than 10 GHz.
+ */
+ CU_ASSERT(odp_cpu_hz_max() < 10ULL * ODP_TIME_SEC_IN_NS);
+ if (max - c3 > 10 * periods * max_period_duration)
+ periods = 10;
+
+ printf("\n Testing CPU cycles for %i seconds... ", periods / 10);
+
+ for (i = 0; i < periods; i++) {
+ c1 = odp_cpu_cycles();
+ odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS + i);
+ c2 = odp_cpu_cycles();
+
+ CU_ASSERT(c2 != c1);
+ CU_ASSERT(c1 <= max && c2 <= max);
+
+ if (c2 > c1)
+ tmp = c2 - c1;
+ else
+ tmp = c2 + (max - c1) + res;
+
+ diff = odp_cpu_cycles_diff(c2, c1);
+ CU_ASSERT(diff == tmp);
+
+ /* wrap is detected and verified */
+ if (c2 < c1)
+ break;
+ }
+
+ /* wrap was detected, no need to continue */
+ if (i < periods) {
+ printf("wrap was detected.\n");
+ return;
+ }
+
+ /* wrap has to be detected if possible */
+ CU_ASSERT(max > UINT32_MAX);
+ CU_ASSERT((max - c3) > UINT32_MAX);
+
+ printf("wrap was not detected.\n");
+}
+
+static void system_test_odp_sys_cache_line_size(void)
+{
+ uint64_t cache_size;
+
+ cache_size = odp_sys_cache_line_size();
+ CU_ASSERT(0 < cache_size);
+ CU_ASSERT(0 < ODP_CACHE_LINE_SIZE);
+ CU_ASSERT(TEST_CHECK_POW2(cache_size));
+ CU_ASSERT(TEST_CHECK_POW2(ODP_CACHE_LINE_SIZE));
+ if (ODP_CACHE_LINE_SIZE != cache_size)
+ printf("WARNING: ODP_CACHE_LINE_SIZE and odp_sys_cache_line_size() not matching\n");
+
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(0) == 0);
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(1) == ODP_CACHE_LINE_SIZE);
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(ODP_CACHE_LINE_SIZE) ==
+ ODP_CACHE_LINE_SIZE);
+ CU_ASSERT(ODP_CACHE_LINE_ROUNDUP(ODP_CACHE_LINE_SIZE + 1) ==
+ 2 * ODP_CACHE_LINE_SIZE);
+}
+
+static void system_test_odp_cpu_model_str(void)
+{
+ char model[128];
+
+ snprintf(model, 128, "%s", odp_cpu_model_str());
+ CU_ASSERT(strlen(model) > 0);
+ CU_ASSERT(strlen(model) < 127);
+}
+
+static void system_test_odp_cpu_model_str_id(void)
+{
+ char model[128];
+ odp_cpumask_t mask;
+ int i, num, cpu;
+
+ num = odp_cpumask_all_available(&mask);
+ cpu = odp_cpumask_first(&mask);
+
+ for (i = 0; i < num; i++) {
+ snprintf(model, 128, "%s", odp_cpu_model_str_id(cpu));
+ CU_ASSERT(strlen(model) > 0);
+ CU_ASSERT(strlen(model) < 127);
+ cpu = odp_cpumask_next(&mask, cpu);
+ }
+}
+
+static void system_test_odp_sys_page_size(void)
+{
+ uint64_t page;
+
+ page = odp_sys_page_size();
+ CU_ASSERT(0 < page);
+ CU_ASSERT(ODP_PAGE_SIZE == page);
+}
+
+static void system_test_odp_sys_huge_page_size(void)
+{
+ uint64_t page;
+
+ page = odp_sys_huge_page_size();
+ if (page == 0)
+ /* Not an error, but just to be sure to hit logs */
+ ODPH_ERR("Huge pages do not seem to be supported\n");
+ else
+ CU_ASSERT(page % ODP_PAGE_SIZE == 0);
+}
+
+static void system_test_odp_sys_huge_page_size_all(void)
+{
+ uint64_t pagesz_tbs[PAGESZ_NUM];
+ uint64_t prev_pagesz = 0;
+ int num;
+ int i;
+
+ num = odp_sys_huge_page_size_all(NULL, 0);
+ CU_ASSERT(num >= 0);
+
+ num = odp_sys_huge_page_size_all(pagesz_tbs, PAGESZ_NUM);
+ CU_ASSERT(num >= 0);
+ for (i = 0; i < num && i < PAGESZ_NUM; i++) {
+ CU_ASSERT(pagesz_tbs[i] > 0);
+ CU_ASSERT(pagesz_tbs[i] > prev_pagesz);
+ prev_pagesz = pagesz_tbs[i];
+ }
+}
+
+static int system_check_cycle_counter(void)
+{
+ if (odp_cpu_cycles_max() == 0) {
+ printf("Cycle counter is not supported, skipping test\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int system_check_odp_cpu_hz(void)
+{
+ if (odp_cpu_hz() == 0) {
+ printf("odp_cpu_hz() is not supported, skipping test\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void system_test_odp_cpu_hz(void)
+{
+ uint64_t hz = odp_cpu_hz();
+
+ /* Test value sanity: less than 10GHz */
+ CU_ASSERT(hz < 10 * GIGA_HZ);
+
+ /* larger than 1kHz */
+ CU_ASSERT(hz > 1 * KILO_HZ);
+}
+
+static int system_check_odp_cpu_hz_id(void)
+{
+ uint64_t hz;
+ odp_cpumask_t mask;
+ int i, num, cpu;
+
+ num = odp_cpumask_all_available(&mask);
+ cpu = odp_cpumask_first(&mask);
+
+ for (i = 0; i < num; i++) {
+ hz = odp_cpu_hz_id(cpu);
+ if (hz == 0) {
+ printf("odp_cpu_hz_id() is not supported by CPU %d, skipping test\n", cpu);
+ return ODP_TEST_INACTIVE;
+ }
+ cpu = odp_cpumask_next(&mask, cpu);
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void system_test_odp_cpu_hz_id(void)
+{
+ uint64_t hz;
+ odp_cpumask_t mask;
+ int i, num, cpu;
+
+ num = odp_cpumask_all_available(&mask);
+ cpu = odp_cpumask_first(&mask);
+
+ for (i = 0; i < num; i++) {
+ hz = odp_cpu_hz_id(cpu);
+ /* Test value sanity: less than 10GHz */
+ CU_ASSERT(hz < 10 * GIGA_HZ);
+ /* larger than 1kHz */
+ CU_ASSERT(hz > 1 * KILO_HZ);
+ cpu = odp_cpumask_next(&mask, cpu);
+ }
+}
+
+static int system_check_odp_cpu_hz_max(void)
+{
+ if (odp_cpu_hz_max() == 0) {
+ printf("odp_cpu_hz_max() is not supported, skipping test\n");
+ return ODP_TEST_INACTIVE;
+ }
+ return ODP_TEST_ACTIVE;
+}
+
+static void system_test_odp_cpu_hz_max(void)
+{
+ uint64_t hz = odp_cpu_hz_max();
+
+ /* Sanity check value */
+ CU_ASSERT(hz > 1 * KILO_HZ);
+ CU_ASSERT(hz < 20 * GIGA_HZ);
+}
+
+static int system_check_odp_cpu_hz_max_id(void)
+{
+ uint64_t hz;
+ odp_cpumask_t mask;
+ int i, num, cpu;
+
+ num = odp_cpumask_all_available(&mask);
+ cpu = odp_cpumask_first(&mask);
+
+ for (i = 0; i < num; i++) {
+ hz = odp_cpu_hz_max_id(cpu);
+ if (hz == 0) {
+ printf("odp_cpu_hz_max_id() is not supported by CPU %d, skipping test\n",
+ cpu);
+ return ODP_TEST_INACTIVE;
+ }
+ cpu = odp_cpumask_next(&mask, cpu);
+ }
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void system_test_odp_cpu_hz_max_id(void)
+{
+ uint64_t hz;
+ odp_cpumask_t mask;
+ int i, num, cpu;
+
+ num = odp_cpumask_all_available(&mask);
+ cpu = odp_cpumask_first(&mask);
+
+ for (i = 0; i < num; i++) {
+ hz = odp_cpu_hz_max_id(cpu);
+ /* Sanity check value */
+ CU_ASSERT(hz > 1 * KILO_HZ);
+ CU_ASSERT(hz < 20 * GIGA_HZ);
+ cpu = odp_cpumask_next(&mask, cpu);
+ }
+}
+
+static void system_test_info_print(void)
+{
+ printf("\n\nCalling system info print...\n");
+ odp_sys_info_print();
+ printf("...done. ");
+}
+
+static void system_test_config_print(void)
+{
+ printf("\n\nCalling system config print...\n");
+ odp_sys_config_print();
+ printf("...done. ");
+}
+
+static void system_test_info(void)
+{
+ odp_system_info_t info;
+ odp_cpu_arch_t cpu_arch;
+
+ memset(&info, 0xff, sizeof(odp_system_info_t));
+ CU_ASSERT(odp_system_info(&info) == 0);
+ cpu_arch = info.cpu_arch;
+
+ memset(&info, 0, sizeof(odp_system_info_t));
+ CU_ASSERT(odp_system_info(&info) == 0);
+
+ CU_ASSERT(info.cpu_arch == cpu_arch);
+ CU_ASSERT(info.cpu_arch >= ODP_CPU_ARCH_UNKNOWN && info.cpu_arch <= ODP_CPU_ARCH_X86);
+
+ if (info.cpu_arch == ODP_CPU_ARCH_X86) {
+ printf("\n ODP_CPU_ARCH_X86\n");
+ CU_ASSERT(info.cpu_isa_sw.x86 != ODP_CPU_ARCH_X86_UNKNOWN);
+
+ if (info.cpu_isa_sw.x86 == ODP_CPU_ARCH_X86_64)
+ printf(" ODP_CPU_ARCH_X86_64\n");
+ else if (info.cpu_isa_sw.x86 == ODP_CPU_ARCH_X86_I686)
+ printf(" ODP_CPU_ARCH_X86_I686\n");
+
+ if (info.cpu_isa_hw.x86 != ODP_CPU_ARCH_X86_UNKNOWN)
+ CU_ASSERT(info.cpu_isa_sw.x86 <= info.cpu_isa_hw.x86);
+ }
+
+ if (info.cpu_arch == ODP_CPU_ARCH_ARM) {
+ printf("\n ODP_CPU_ARCH_ARM\n");
+ CU_ASSERT(info.cpu_isa_sw.arm != ODP_CPU_ARCH_ARM_UNKNOWN);
+
+ if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV6)
+ printf(" ODP_CPU_ARCH_ARMV6\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV7)
+ printf(" ODP_CPU_ARCH_ARMV7\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_0)
+ printf(" ODP_CPU_ARCH_ARMV8_0\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_1)
+ printf(" ODP_CPU_ARCH_ARMV8_1\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_2)
+ printf(" ODP_CPU_ARCH_ARMV8_2\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_3)
+ printf(" ODP_CPU_ARCH_ARMV8_3\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_4)
+ printf(" ODP_CPU_ARCH_ARMV8_4\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_5)
+ printf(" ODP_CPU_ARCH_ARMV8_5\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_6)
+ printf(" ODP_CPU_ARCH_ARMV8_6\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_7)
+ printf(" ODP_CPU_ARCH_ARMV8_7\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_8)
+ printf(" ODP_CPU_ARCH_ARMV8_8\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV8_9)
+ printf(" ODP_CPU_ARCH_ARMV8_9\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV9_0)
+ printf(" ODP_CPU_ARCH_ARMV9_0\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV9_1)
+ printf(" ODP_CPU_ARCH_ARMV9_1\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV9_2)
+ printf(" ODP_CPU_ARCH_ARMV9_2\n");
+ else if (info.cpu_isa_sw.arm == ODP_CPU_ARCH_ARMV9_3)
+ printf(" ODP_CPU_ARCH_ARMV9_3\n");
+ else
+ CU_FAIL("Unknown CPU ISA SW ARCH found!");
+
+ if (info.cpu_isa_hw.arm != ODP_CPU_ARCH_ARM_UNKNOWN)
+ CU_ASSERT(info.cpu_isa_sw.arm <= info.cpu_isa_hw.arm);
+
+ if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV6)
+ printf(" ODP_CPU_ARCH_ARMV6\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV7)
+ printf(" ODP_CPU_ARCH_ARMV7\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_0)
+ printf(" ODP_CPU_ARCH_ARMV8_0\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_1)
+ printf(" ODP_CPU_ARCH_ARMV8_1\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_2)
+ printf(" ODP_CPU_ARCH_ARMV8_2\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_3)
+ printf(" ODP_CPU_ARCH_ARMV8_3\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_4)
+ printf(" ODP_CPU_ARCH_ARMV8_4\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_5)
+ printf(" ODP_CPU_ARCH_ARMV8_5\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_6)
+ printf(" ODP_CPU_ARCH_ARMV8_6\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_7)
+ printf(" ODP_CPU_ARCH_ARMV8_7\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_8)
+ printf(" ODP_CPU_ARCH_ARMV8_8\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV8_9)
+ printf(" ODP_CPU_ARCH_ARMV8_9\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV9_0)
+ printf(" ODP_CPU_ARCH_ARMV9_0\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV9_1)
+ printf(" ODP_CPU_ARCH_ARMV9_1\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV9_2)
+ printf(" ODP_CPU_ARCH_ARMV9_2\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARMV9_3)
+ printf(" ODP_CPU_ARCH_ARMV9_3\n");
+ else if (info.cpu_isa_hw.arm == ODP_CPU_ARCH_ARM_UNKNOWN)
+ printf(" ODP_CPU_ARCH_ARM_UNKNOWN\n");
+ else
+ CU_FAIL("Unknown CPU ISA HW ARCH found!");
+
+ }
+}
+
+static void system_test_meminfo(void)
+{
+ const int32_t max_num = 128;
+ odp_system_meminfo_t info, info_0;
+ int32_t ret, ret_0, num, i;
+ odp_system_memblock_t block[max_num];
+
+ /* Meminfo without blocks */
+ ret_0 = odp_system_meminfo(&info_0, NULL, 0);
+ CU_ASSERT_FATAL(ret_0 >= 0);
+
+ ret = odp_system_meminfo(&info, block, max_num);
+ CU_ASSERT_FATAL(ret >= 0);
+
+ /* Totals should match independent of per block output */
+ CU_ASSERT(ret == ret_0);
+ CU_ASSERT(info_0.total_mapped == info.total_mapped);
+ CU_ASSERT(info_0.total_used == info.total_used);
+ CU_ASSERT(info_0.total_overhead == info.total_overhead);
+
+ CU_ASSERT(info.total_mapped >= info.total_used);
+ CU_ASSERT(info.total_used >= info.total_overhead);
+
+ num = ret;
+ if (ret > max_num)
+ num = max_num;
+
+ printf("\n\n");
+ printf("System meminfo contain %i blocks, printing %i blocks:\n", ret, num);
+
+ printf(" %s %-32s %16s %14s %14s %12s\n", "index", "name", "addr",
+ "used", "overhead", "page_size");
+
+ for (i = 0; i < num; i++) {
+ printf(" [%3i] %-32s %16" PRIxPTR " %14" PRIu64 " %14" PRIu64 " %12" PRIu64 "\n",
+ i, block[i].name, block[i].addr, block[i].used, block[i].overhead,
+ block[i].page_size);
+ }
+
+ printf("\n");
+ printf("Total mapped: %" PRIu64 "\n", info.total_mapped);
+ printf("Total used: %" PRIu64 "\n", info.total_used);
+ printf("Total overhead: %" PRIu64 "\n\n", info.total_overhead);
+}
+
+odp_testinfo_t system_suite[] = {
+ ODP_TEST_INFO(test_version_api_str),
+ ODP_TEST_INFO(test_version_str),
+ ODP_TEST_INFO(test_version_macro),
+ ODP_TEST_INFO(system_test_odp_cpu_count),
+ ODP_TEST_INFO(system_test_odp_sys_cache_line_size),
+ ODP_TEST_INFO(system_test_odp_cpu_model_str),
+ ODP_TEST_INFO(system_test_odp_cpu_model_str_id),
+ ODP_TEST_INFO(system_test_odp_sys_page_size),
+ ODP_TEST_INFO(system_test_odp_sys_huge_page_size),
+ ODP_TEST_INFO(system_test_odp_sys_huge_page_size_all),
+ ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz,
+ system_check_odp_cpu_hz),
+ ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz_id,
+ system_check_odp_cpu_hz_id),
+ ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz_max,
+ system_check_odp_cpu_hz_max),
+ ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz_max_id,
+ system_check_odp_cpu_hz_max_id),
+ ODP_TEST_INFO_CONDITIONAL(system_test_cpu_cycles,
+ system_check_cycle_counter),
+ ODP_TEST_INFO_CONDITIONAL(system_test_cpu_cycles_max,
+ system_check_cycle_counter),
+ ODP_TEST_INFO_CONDITIONAL(system_test_cpu_cycles_resolution,
+ system_check_cycle_counter),
+ ODP_TEST_INFO_CONDITIONAL(system_test_cpu_cycles_diff,
+ system_check_cycle_counter),
+ ODP_TEST_INFO_CONDITIONAL(system_test_cpu_cycles_long_period,
+ system_check_cycle_counter),
+ ODP_TEST_INFO(system_test_info),
+ ODP_TEST_INFO(system_test_meminfo),
+ ODP_TEST_INFO(system_test_info_print),
+ ODP_TEST_INFO(system_test_config_print),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t system_suites[] = {
+ {"System Info", NULL, NULL, system_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(system_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/thread/.gitignore b/test/validation/api/thread/.gitignore
index ab1787d97..ab1787d97 100644
--- a/test/common_plat/validation/api/thread/.gitignore
+++ b/test/validation/api/thread/.gitignore
diff --git a/test/validation/api/thread/Makefile.am b/test/validation/api/thread/Makefile.am
new file mode 100644
index 000000000..cbd8b2a7b
--- /dev/null
+++ b/test/validation/api/thread/Makefile.am
@@ -0,0 +1,6 @@
+include ../Makefile.inc
+
+test_PROGRAMS = thread_main
+thread_main_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_THRMASK
+thread_main_SOURCES = thread.c
+LDADD += $(LIBTHRMASK_COMMON)
diff --git a/test/validation/api/thread/thread.c b/test/validation/api/thread/thread.c
new file mode 100644
index 000000000..bd72c1de2
--- /dev/null
+++ b/test/validation/api/thread/thread.c
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include <odp_cunit_common.h>
+#include <mask_common.h>
+
+#define GLOBAL_SHM_NAME "GlobalThreadTest"
+
+typedef struct {
+ /* Test thread entry and exit synchronization barriers */
+ odp_barrier_t bar_entry;
+ odp_barrier_t bar_exit;
+
+ /* Storage for thread ID assignment order test */
+ int thread_id[ODP_THREAD_COUNT_MAX];
+} global_shared_mem_t;
+
+static global_shared_mem_t *global_mem;
+
+static int thread_global_init(odp_instance_t *inst)
+{
+ odp_shm_t global_shm;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(global_shared_mem_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (global_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Unable to reserve memory for global_shm\n");
+ return -1;
+ }
+
+ global_mem = odp_shm_addr(global_shm);
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ return 0;
+}
+
+static int thread_global_term(odp_instance_t inst)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ if (0 != odp_shm_free(shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void thread_test_odp_cpu_id(void)
+{
+ CU_ASSERT(odp_cpu_id() >= 0);
+}
+
+static void thread_test_odp_thread_id(void)
+{
+ int id = odp_thread_id();
+
+ /* First thread which called odp_init_local() */
+ CU_ASSERT(id == 0);
+
+ CU_ASSERT(id >= 0);
+ CU_ASSERT(id < odp_thread_count_max());
+ CU_ASSERT(id < ODP_THREAD_COUNT_MAX);
+}
+
+static void thread_test_odp_thread_count(void)
+{
+ int count = odp_thread_count();
+
+ /* One control thread running */
+ CU_ASSERT(count == 1);
+ CU_ASSERT(odp_thread_control_count() == 1);
+ CU_ASSERT(odp_thread_control_count() <= odp_thread_control_count_max());
+ CU_ASSERT(odp_thread_worker_count() == 0);
+
+ CU_ASSERT(count >= 1);
+ CU_ASSERT(count <= odp_thread_count_max());
+ CU_ASSERT(count <= ODP_THREAD_COUNT_MAX);
+}
+
+static void thread_test_odp_thread_count_max(void)
+{
+ int max_threads = odp_thread_count_max();
+ int max_control = odp_thread_control_count_max();
+ int max_worker = odp_thread_worker_count_max();
+
+ CU_ASSERT(max_threads > 0);
+ CU_ASSERT(max_threads <= ODP_THREAD_COUNT_MAX);
+
+ CU_ASSERT(max_control >= 0);
+ CU_ASSERT(max_control <= max_threads);
+
+ CU_ASSERT(max_worker >= 0);
+ CU_ASSERT(max_worker <= max_threads);
+}
+
+static int thread_func(void *arg)
+{
+ int *id_ptr = arg;
+
+ /* Indicate that thread has started */
+ odp_barrier_wait(&global_mem->bar_entry);
+
+ /* Record thread identifier for ID assignment order check */
+ *id_ptr = odp_thread_id();
+
+ CU_ASSERT(*id_ptr > 0);
+ CU_ASSERT(*id_ptr < odp_thread_count_max());
+
+ CU_ASSERT(odp_thread_type() == ODP_THREAD_WORKER);
+
+ /* Wait for indication that we can exit */
+ odp_barrier_wait(&global_mem->bar_exit);
+
+ return CU_get_number_of_failures();
+}
+
+static void thread_test_odp_thrmask_worker(void)
+{
+ odp_thrmask_t mask;
+ int ret;
+ int num = odp_cpumask_default_worker(NULL, 0);
+
+ CU_ASSERT_FATAL(num > 0);
+ CU_ASSERT_FATAL(odp_thread_type() == ODP_THREAD_CONTROL);
+
+ /* Control and worker threads may share CPUs */
+ if (num > 1)
+ num--;
+
+ void *args[num];
+
+ for (int i = 0; i < num; i++) {
+ global_mem->thread_id[i] = -1;
+ args[i] = &global_mem->thread_id[i];
+ }
+
+ odp_barrier_init(&global_mem->bar_entry, num + 1);
+ odp_barrier_init(&global_mem->bar_exit, num + 1);
+
+ /* should start out with 0 worker threads */
+ ret = odp_thrmask_worker(&mask);
+ CU_ASSERT(ret == odp_thrmask_count(&mask));
+ CU_ASSERT(ret == 0);
+
+ /* start the test thread(s) */
+ ret = odp_cunit_thread_create(num, thread_func, args, 1, 1);
+ CU_ASSERT(ret == num);
+
+ if (ret != num)
+ return;
+
+ /* wait for thread(s) to start */
+ odp_barrier_wait(&global_mem->bar_entry);
+
+ ret = odp_thrmask_worker(&mask);
+ CU_ASSERT(ret == odp_thrmask_count(&mask));
+ CU_ASSERT(ret == num);
+ CU_ASSERT(ret == odp_thread_worker_count());
+ CU_ASSERT(ret <= odp_thread_count_max());
+ CU_ASSERT(ret <= odp_thread_worker_count_max());
+
+ /* allow thread(s) to exit */
+ odp_barrier_wait(&global_mem->bar_exit);
+
+ /* Thread ID 0 is used by this control thread */
+ for (int i = 0; i < num; i++)
+ CU_ASSERT(global_mem->thread_id[i] == i + 1);
+
+ odp_cunit_thread_join(num);
+}
+
+static void thread_test_odp_thrmask_control(void)
+{
+ odp_thrmask_t mask;
+ int ret;
+
+ CU_ASSERT(odp_thread_type() == ODP_THREAD_CONTROL);
+
+ /* Should start out with 1 control thread */
+ ret = odp_thrmask_control(&mask);
+ CU_ASSERT(ret == odp_thrmask_count(&mask));
+ CU_ASSERT(ret == odp_thread_control_count());
+ CU_ASSERT(ret == 1);
+}
+
+odp_testinfo_t thread_suite[] = {
+ ODP_TEST_INFO(thread_test_odp_cpu_id),
+ ODP_TEST_INFO(thread_test_odp_thread_id),
+ ODP_TEST_INFO(thread_test_odp_thread_count),
+ ODP_TEST_INFO(thread_test_odp_thread_count_max),
+ ODP_TEST_INFO(thread_test_odp_thrmask_to_from_str),
+ ODP_TEST_INFO(thread_test_odp_thrmask_equal),
+ ODP_TEST_INFO(thread_test_odp_thrmask_zero),
+ ODP_TEST_INFO(thread_test_odp_thrmask_set),
+ ODP_TEST_INFO(thread_test_odp_thrmask_clr),
+ ODP_TEST_INFO(thread_test_odp_thrmask_isset),
+ ODP_TEST_INFO(thread_test_odp_thrmask_count),
+ ODP_TEST_INFO(thread_test_odp_thrmask_and),
+ ODP_TEST_INFO(thread_test_odp_thrmask_or),
+ ODP_TEST_INFO(thread_test_odp_thrmask_xor),
+ ODP_TEST_INFO(thread_test_odp_thrmask_copy),
+ ODP_TEST_INFO(thread_test_odp_thrmask_first),
+ ODP_TEST_INFO(thread_test_odp_thrmask_last),
+ ODP_TEST_INFO(thread_test_odp_thrmask_next),
+ ODP_TEST_INFO(thread_test_odp_thrmask_worker),
+ ODP_TEST_INFO(thread_test_odp_thrmask_control),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t thread_suites[] = {
+ {"thread", NULL, NULL, thread_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(thread_global_init);
+ odp_cunit_register_global_term(thread_global_term);
+
+ ret = odp_cunit_register(thread_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/time/.gitignore b/test/validation/api/time/.gitignore
index 0ef3e6162..0ef3e6162 100644
--- a/test/common_plat/validation/api/time/.gitignore
+++ b/test/validation/api/time/.gitignore
diff --git a/test/validation/api/time/Makefile.am b/test/validation/api/time/Makefile.am
new file mode 100644
index 000000000..9b0392eb9
--- /dev/null
+++ b/test/validation/api/time/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = time_main
+time_main_SOURCES = time.c
diff --git a/test/validation/api/time/time.c b/test/validation/api/time/time.c
new file mode 100644
index 000000000..666a39b81
--- /dev/null
+++ b/test/validation/api/time/time.c
@@ -0,0 +1,1200 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2024 Nokia
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <time.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define BUSY_LOOP_CNT 30000000 /* used for t > min resolution */
+#define MIN_TIME_RATE 32000
+#define MAX_TIME_RATE 15000000000
+#define DELAY_TOLERANCE 40000000 /* deviation for delay */
+#define WAIT_SECONDS 3
+#define MAX_WORKERS 32
+#define TEST_ROUNDS 1024
+#define TIME_SAMPLES 2
+#define TIME_TOLERANCE_NS 1000000
+#define TIME_TOLERANCE_CI_NS 40000000
+#define TIME_TOLERANCE_1CPU_NS 40000000
+#define GLOBAL_SHM_NAME "GlobalTimeTest"
+#define YEAR_IN_NS (365 * 24 * ODP_TIME_HOUR_IN_NS)
+
+static uint64_t local_res;
+static uint64_t global_res;
+
+typedef odp_time_t time_cb(void);
+typedef uint64_t time_res_cb(void);
+typedef odp_time_t time_from_ns_cb(uint64_t ns);
+typedef uint64_t time_nsec_cb(void);
+
+typedef struct {
+ uint32_t num_threads;
+ odp_barrier_t test_barrier;
+ odp_time_t time[MAX_WORKERS + 1][TIME_SAMPLES];
+ odp_queue_t queue[MAX_WORKERS];
+ uint32_t num_queues;
+ odp_atomic_u32_t event_count;
+} global_shared_mem_t;
+
+static global_shared_mem_t *global_mem;
+static odp_instance_t *instance;
+
+static int time_global_init(odp_instance_t *inst)
+{
+ odp_shm_t global_shm;
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+ uint32_t workers_count, max_threads;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(global_shared_mem_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (global_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Unable reserve memory for global_shm\n");
+ return -1;
+ }
+
+ global_mem = odp_shm_addr(global_shm);
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ global_mem->num_threads = MAX_WORKERS;
+
+ workers_count = odp_cpumask_default_worker(NULL, 0);
+
+ max_threads = (workers_count >= MAX_WORKERS) ?
+ MAX_WORKERS : workers_count;
+
+ if (max_threads < global_mem->num_threads) {
+ printf("Requested num of threads is too large\n");
+ printf("reducing from %" PRIu32 " to %" PRIu32 "\n",
+ global_mem->num_threads,
+ max_threads);
+ global_mem->num_threads = max_threads;
+ }
+
+ printf("Num of threads used = %" PRIu32 "\n",
+ global_mem->num_threads);
+
+ instance = inst;
+
+ return 0;
+}
+
+static int time_global_term(odp_instance_t inst)
+{
+ odp_shm_t shm;
+
+ shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ if (0 != odp_shm_free(shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void time_test_constants(void)
+{
+ uint64_t ns;
+
+ CU_ASSERT(ODP_TIME_USEC_IN_NS == 1000);
+
+ ns = ODP_TIME_HOUR_IN_NS;
+ CU_ASSERT(ns == 60 * ODP_TIME_MIN_IN_NS);
+ ns = ODP_TIME_MIN_IN_NS;
+ CU_ASSERT(ns == 60 * ODP_TIME_SEC_IN_NS);
+ ns = ODP_TIME_SEC_IN_NS;
+ CU_ASSERT(ns == 1000 * ODP_TIME_MSEC_IN_NS);
+ ns = ODP_TIME_MSEC_IN_NS;
+ CU_ASSERT(ns == 1000 * ODP_TIME_USEC_IN_NS);
+
+ ns = ODP_TIME_SEC_IN_NS / 1000;
+ CU_ASSERT(ns == ODP_TIME_MSEC_IN_NS);
+ ns /= 1000;
+ CU_ASSERT(ns == ODP_TIME_USEC_IN_NS);
+}
+
+static void time_test_startup_time(void)
+{
+ odp_time_startup_t startup;
+ uint64_t ns1, ns2, ns3;
+ odp_time_t time;
+
+ memset(&startup, 0, sizeof(odp_time_startup_t));
+
+ odp_time_startup(&startup);
+ ns1 = startup.global_ns;
+ ns2 = odp_time_to_ns(startup.global);
+
+ CU_ASSERT(UINT64_MAX - ns1 >= 10 * YEAR_IN_NS);
+ CU_ASSERT(UINT64_MAX - ns2 >= 10 * YEAR_IN_NS);
+
+ time = odp_time_global();
+ ns3 = odp_time_to_ns(time);
+ CU_ASSERT(odp_time_cmp(time, startup.global) > 0);
+
+ time = odp_time_global_from_ns(10 * YEAR_IN_NS);
+ time = odp_time_sum(startup.global, time);
+ CU_ASSERT(odp_time_cmp(time, startup.global) > 0);
+
+ printf("\n");
+ printf(" Startup time in nsec: %" PRIu64 "\n", ns1);
+ printf(" Startup time to nsec: %" PRIu64 "\n", ns2);
+ printf(" Nsec since startup: %" PRIu64 "\n\n", ns3 - startup.global_ns);
+}
+
+static void time_test_res(time_res_cb time_res, uint64_t *res)
+{
+ uint64_t rate;
+
+ rate = time_res();
+ CU_ASSERT(rate > MIN_TIME_RATE);
+ CU_ASSERT(rate < MAX_TIME_RATE);
+
+ *res = ODP_TIME_SEC_IN_NS / rate;
+ if (ODP_TIME_SEC_IN_NS % rate)
+ (*res)++;
+}
+
+static void time_test_local_res(void)
+{
+ time_test_res(odp_time_local_res, &local_res);
+}
+
+static void time_test_global_res(void)
+{
+ time_test_res(odp_time_global_res, &global_res);
+}
+
+/* check that related conversions come back to the same value */
+static void time_test_conversion(time_from_ns_cb time_from_ns, uint64_t res)
+{
+ uint64_t ns1, ns2;
+ odp_time_t time;
+ uint64_t upper_limit, lower_limit;
+
+ ns1 = 100;
+ time = time_from_ns(ns1);
+
+ ns2 = odp_time_to_ns(time);
+
+ /* need to check within arithmetic tolerance that the same
+ * value in ns is returned after conversions */
+ upper_limit = ns1 + res;
+ lower_limit = ns1 - res;
+ CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
+
+ ns1 = 60 * 11 * ODP_TIME_SEC_IN_NS;
+ time = time_from_ns(ns1);
+
+ ns2 = odp_time_to_ns(time);
+
+ /* need to check within arithmetic tolerance that the same
+ * value in ns is returned after conversions */
+ upper_limit = ns1 + res;
+ lower_limit = ns1 - res;
+ CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
+
+ /* test on 0 */
+ ns1 = odp_time_to_ns(ODP_TIME_NULL);
+ CU_ASSERT(ns1 == 0);
+}
+
+static void time_test_local_conversion(void)
+{
+ time_test_conversion(odp_time_local_from_ns, local_res);
+}
+
+static void time_test_global_conversion(void)
+{
+ time_test_conversion(odp_time_global_from_ns, global_res);
+}
+
+static void time_test_monotony(void)
+{
+ volatile uint64_t count = 0;
+ odp_time_t l_t1, l_t2, l_t3;
+ odp_time_t ls_t1, ls_t2, ls_t3;
+ odp_time_t g_t1, g_t2, g_t3;
+ odp_time_t gs_t1, gs_t2, gs_t3;
+ uint64_t l_ns1, l_ns2, l_ns3;
+ uint64_t ls_ns1, ls_ns2, ls_ns3;
+ uint64_t g_ns1, g_ns2, g_ns3;
+ uint64_t gs_ns1, gs_ns2, gs_ns3;
+ uint64_t ns1, ns2, ns3;
+ uint64_t s_ns1, s_ns2, s_ns3;
+ uint64_t limit;
+
+ l_t1 = odp_time_local();
+ ls_t1 = odp_time_local_strict();
+ l_ns1 = odp_time_local_ns();
+ ls_ns1 = odp_time_local_strict_ns();
+
+ g_t1 = odp_time_global();
+ gs_t1 = odp_time_global_strict();
+ g_ns1 = odp_time_global_ns();
+ gs_ns1 = odp_time_global_strict_ns();
+
+ while (count < BUSY_LOOP_CNT) {
+ count++;
+ };
+
+ l_t2 = odp_time_local();
+ ls_t2 = odp_time_local_strict();
+ l_ns2 = odp_time_local_ns();
+ ls_ns2 = odp_time_local_strict_ns();
+
+ g_t2 = odp_time_global();
+ gs_t2 = odp_time_global_strict();
+ g_ns2 = odp_time_global_ns();
+ gs_ns2 = odp_time_global_strict_ns();
+
+ count = 0;
+ while (count < BUSY_LOOP_CNT) {
+ count++;
+ };
+
+ l_t3 = odp_time_local();
+ ls_t3 = odp_time_local_strict();
+ l_ns3 = odp_time_local_ns();
+ ls_ns3 = odp_time_local_strict_ns();
+
+ g_t3 = odp_time_global();
+ gs_t3 = odp_time_global_strict();
+ g_ns3 = odp_time_global_ns();
+ gs_ns3 = odp_time_global_strict_ns();
+
+ /* Local time tests
+ * ---------------- */
+
+ ns1 = odp_time_to_ns(l_t1);
+ ns2 = odp_time_to_ns(l_t2);
+ ns3 = odp_time_to_ns(l_t3);
+
+ s_ns1 = odp_time_to_ns(ls_t1);
+ s_ns2 = odp_time_to_ns(ls_t2);
+ s_ns3 = odp_time_to_ns(ls_t3);
+
+ /* Time should not wrap in at least 10 years from ODP start. Ignoring delay from start up
+ * and other test cases, which should be few seconds. */
+ limit = 10 * YEAR_IN_NS;
+ CU_ASSERT(UINT64_MAX - ns1 > limit);
+ CU_ASSERT(UINT64_MAX - s_ns1 > limit);
+ CU_ASSERT(UINT64_MAX - l_ns1 > limit);
+ CU_ASSERT(UINT64_MAX - ls_ns1 > limit);
+
+ /* Time stamp */
+ CU_ASSERT(ns2 > ns1);
+ CU_ASSERT(ns3 > ns2);
+
+ /* Strict time stamp */
+ CU_ASSERT(s_ns2 > s_ns1);
+ CU_ASSERT(s_ns3 > s_ns2);
+
+ /* Nsec time */
+ CU_ASSERT(l_ns2 > l_ns1);
+ CU_ASSERT(l_ns3 > l_ns2);
+
+ /* Strict nsec time */
+ CU_ASSERT(ls_ns2 > ls_ns1);
+ CU_ASSERT(ls_ns3 > ls_ns2);
+
+ /* Strict time stamp order is maintained */
+ CU_ASSERT(ls_ns1 >= s_ns1);
+ CU_ASSERT(ls_ns2 >= s_ns2);
+ CU_ASSERT(ls_ns3 >= s_ns3);
+
+ /* Time in nanoseconds have the same time base. Allow less than 100 msec error
+ * between time stamp converted to nsec and nsec time. */
+ CU_ASSERT((ls_ns1 - s_ns1) < (100 * ODP_TIME_MSEC_IN_NS));
+ CU_ASSERT((ls_ns2 - s_ns2) < (100 * ODP_TIME_MSEC_IN_NS));
+ CU_ASSERT((ls_ns3 - s_ns3) < (100 * ODP_TIME_MSEC_IN_NS));
+
+ /* Global time tests
+ * ----------------- */
+
+ ns1 = odp_time_to_ns(g_t1);
+ ns2 = odp_time_to_ns(g_t2);
+ ns3 = odp_time_to_ns(g_t3);
+
+ s_ns1 = odp_time_to_ns(gs_t1);
+ s_ns2 = odp_time_to_ns(gs_t2);
+ s_ns3 = odp_time_to_ns(gs_t3);
+
+ /* Time should not wrap in at least 10 years from ODP start. Ignoring delay from start up
+ * and other test cases, which should be few seconds. */
+ limit = 10 * YEAR_IN_NS;
+ CU_ASSERT(UINT64_MAX - ns1 > limit);
+ CU_ASSERT(UINT64_MAX - s_ns1 > limit);
+ CU_ASSERT(UINT64_MAX - g_ns1 > limit);
+ CU_ASSERT(UINT64_MAX - gs_ns1 > limit);
+
+ /* Time stamp */
+ CU_ASSERT(ns2 > ns1);
+ CU_ASSERT(ns3 > ns2);
+
+ /* Strict time stamp */
+ CU_ASSERT(s_ns2 > s_ns1);
+ CU_ASSERT(s_ns3 > s_ns2);
+
+ /* Nsec time */
+ CU_ASSERT(g_ns2 > g_ns1);
+ CU_ASSERT(g_ns3 > g_ns2);
+
+ /* Strict nsec time */
+ CU_ASSERT(gs_ns2 > gs_ns1);
+ CU_ASSERT(gs_ns3 > gs_ns2);
+
+ /* Strict time stamp order is maintained */
+ CU_ASSERT(gs_ns1 >= s_ns1);
+ CU_ASSERT(gs_ns2 >= s_ns2);
+ CU_ASSERT(gs_ns3 >= s_ns3);
+
+ /* Time in nanoseconds have the same time base. Allow less than 100 msec error
+ * between time stamp converted to nsec and nsec time. */
+ CU_ASSERT((gs_ns1 - s_ns1) < (100 * ODP_TIME_MSEC_IN_NS));
+ CU_ASSERT((gs_ns2 - s_ns2) < (100 * ODP_TIME_MSEC_IN_NS));
+ CU_ASSERT((gs_ns3 - s_ns3) < (100 * ODP_TIME_MSEC_IN_NS));
+
+ /* Tight error margin cannot be used due to possible OS interrupts during the test.
+ * Record all time stamp values into the log to help debugging their relative order and
+ * accuracy. */
+ printf("\n Time stamp values in nsec:\n");
+ printf(" odp_time_local(): %" PRIu64 "\n", odp_time_to_ns(l_t1));
+ printf(" odp_time_local_strict(): %" PRIu64 "\n", odp_time_to_ns(ls_t1));
+ printf(" odp_time_local_ns(): %" PRIu64 "\n", l_ns1);
+ printf(" odp_time_local_strict_ns(): %" PRIu64 "\n", ls_ns1);
+ printf(" odp_time_global(): %" PRIu64 "\n", odp_time_to_ns(g_t1));
+ printf(" odp_time_global_strict(): %" PRIu64 "\n", odp_time_to_ns(gs_t1));
+ printf(" odp_time_global_ns(): %" PRIu64 "\n", g_ns1);
+ printf(" odp_time_global_strict_ns(): %" PRIu64 "\n\n", gs_ns1);
+}
+
+static void time_test_cmp(time_cb time_cur, time_from_ns_cb time_from_ns)
+{
+ /* volatile to stop optimization of busy loop */
+ volatile int count = 0;
+ odp_time_t t1, t2, t3;
+
+ t1 = time_cur();
+
+ while (count < BUSY_LOOP_CNT) {
+ count++;
+ };
+
+ t2 = time_cur();
+
+ while (count < BUSY_LOOP_CNT * 2) {
+ count++;
+ };
+
+ t3 = time_cur();
+
+ CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+ CU_ASSERT(odp_time_cmp(t3, t2) > 0);
+ CU_ASSERT(odp_time_cmp(t3, t1) > 0);
+ CU_ASSERT(odp_time_cmp(t1, t2) < 0);
+ CU_ASSERT(odp_time_cmp(t2, t3) < 0);
+ CU_ASSERT(odp_time_cmp(t1, t3) < 0);
+ CU_ASSERT(odp_time_cmp(t1, t1) == 0);
+ CU_ASSERT(odp_time_cmp(t2, t2) == 0);
+ CU_ASSERT(odp_time_cmp(t3, t3) == 0);
+
+ t2 = time_from_ns(60 * 10 * ODP_TIME_SEC_IN_NS);
+ t1 = time_from_ns(3);
+
+ CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+ CU_ASSERT(odp_time_cmp(t1, t2) < 0);
+
+ t1 = time_from_ns(0);
+ CU_ASSERT(odp_time_cmp(t1, ODP_TIME_NULL) == 0);
+}
+
+static void time_test_local_cmp(void)
+{
+ time_test_cmp(odp_time_local, odp_time_local_from_ns);
+}
+
+static void time_test_global_cmp(void)
+{
+ time_test_cmp(odp_time_global, odp_time_global_from_ns);
+}
+
+static void time_test_local_strict_cmp(void)
+{
+ time_test_cmp(odp_time_local_strict, odp_time_local_from_ns);
+}
+
+static void time_test_global_strict_cmp(void)
+{
+ time_test_cmp(odp_time_global_strict, odp_time_global_from_ns);
+}
+
+/* check that a time difference gives a reasonable result */
+static void time_test_diff(time_cb time_cur,
+ time_from_ns_cb time_from_ns,
+ uint64_t res)
+{
+ /* volatile to stop optimization of busy loop */
+ volatile int count = 0;
+ odp_time_t diff, t1, t2;
+ uint64_t ns1, ns2, ns;
+ uint64_t nsdiff, diff_ns;
+ uint64_t upper_limit, lower_limit;
+
+ /* test timestamp diff */
+ t1 = time_cur();
+
+ while (count < BUSY_LOOP_CNT) {
+ count++;
+ };
+
+ t2 = time_cur();
+ CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+
+ diff = odp_time_diff(t2, t1);
+ CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
+
+ diff_ns = odp_time_diff_ns(t2, t1);
+ CU_ASSERT(diff_ns > 0);
+
+ ns1 = odp_time_to_ns(t1);
+ ns2 = odp_time_to_ns(t2);
+ ns = ns2 - ns1;
+ nsdiff = odp_time_to_ns(diff);
+
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+ CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
+ CU_ASSERT((diff_ns <= upper_limit) && (diff_ns >= lower_limit));
+
+ /* test timestamp and interval diff */
+ ns1 = 54;
+ t1 = time_from_ns(ns1);
+ ns = ns2 - ns1;
+
+ diff = odp_time_diff(t2, t1);
+ CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
+
+ diff_ns = odp_time_diff_ns(t2, t1);
+ CU_ASSERT(diff_ns > 0);
+
+ nsdiff = odp_time_to_ns(diff);
+
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+ CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
+ CU_ASSERT((diff_ns <= upper_limit) && (diff_ns >= lower_limit));
+
+ /* test interval diff */
+ ns2 = 60 * 10 * ODP_TIME_SEC_IN_NS;
+ ns = ns2 - ns1;
+
+ t2 = time_from_ns(ns2);
+ diff = odp_time_diff(t2, t1);
+ CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
+
+ diff_ns = odp_time_diff_ns(t2, t1);
+ CU_ASSERT(diff_ns > 0);
+
+ nsdiff = odp_time_to_ns(diff);
+
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+ CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit));
+ CU_ASSERT((diff_ns <= upper_limit) && (diff_ns >= lower_limit));
+
+ /* same time has to diff to 0 */
+ diff = odp_time_diff(t2, t2);
+ CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) == 0);
+
+ diff = odp_time_diff(t2, ODP_TIME_NULL);
+ CU_ASSERT(odp_time_cmp(t2, diff) == 0);
+
+ diff_ns = odp_time_diff_ns(t2, t2);
+ CU_ASSERT(diff_ns == 0);
+}
+
+static void time_test_local_diff(void)
+{
+ time_test_diff(odp_time_local, odp_time_local_from_ns, local_res);
+}
+
+static void time_test_global_diff(void)
+{
+ time_test_diff(odp_time_global, odp_time_global_from_ns, global_res);
+}
+
+static void time_test_local_strict_diff(void)
+{
+ time_test_diff(odp_time_local_strict, odp_time_local_from_ns, local_res);
+}
+
+static void time_test_global_strict_diff(void)
+{
+ time_test_diff(odp_time_global_strict, odp_time_global_from_ns, global_res);
+}
+
+/* check that a time sum gives a reasonable result */
+static void time_test_sum(time_cb time_cur,
+ time_from_ns_cb time_from_ns,
+ uint64_t res)
+{
+ odp_time_t sum, t1, t2;
+ uint64_t nssum, ns1, ns2, ns, diff;
+ uint64_t upper_limit, lower_limit;
+
+ /* sum timestamp and interval */
+ t1 = time_cur();
+ ns2 = 103;
+ t2 = time_from_ns(ns2);
+ ns1 = odp_time_to_ns(t1);
+ ns = ns1 + ns2;
+
+ sum = odp_time_sum(t2, t1);
+ CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
+ nssum = odp_time_to_ns(sum);
+
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+ CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit));
+
+ /* sum intervals */
+ ns1 = 60 * 13 * ODP_TIME_SEC_IN_NS;
+ t1 = time_from_ns(ns1);
+ ns = ns1 + ns2;
+
+ sum = odp_time_sum(t2, t1);
+ CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
+ nssum = odp_time_to_ns(sum);
+
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+ CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit));
+
+ /* test on 0 */
+ sum = odp_time_sum(t2, ODP_TIME_NULL);
+ CU_ASSERT(odp_time_cmp(t2, sum) == 0);
+
+ /* test add nsec */
+ ns = ODP_TIME_SEC_IN_NS;
+ upper_limit = ns + 2 * res;
+ lower_limit = ns - 2 * res;
+
+ t1 = time_cur();
+ t2 = odp_time_add_ns(t1, ns);
+
+ CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+
+ diff = odp_time_diff_ns(t2, t1);
+ CU_ASSERT((diff <= upper_limit) && (diff >= lower_limit));
+
+ t1 = ODP_TIME_NULL;
+ t2 = odp_time_add_ns(t1, ns);
+
+ CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+
+ diff = odp_time_diff_ns(t2, t1);
+ CU_ASSERT((diff <= upper_limit) && (diff >= lower_limit));
+}
+
+static void time_test_local_sum(void)
+{
+ time_test_sum(odp_time_local, odp_time_local_from_ns, local_res);
+}
+
+static void time_test_global_sum(void)
+{
+ time_test_sum(odp_time_global, odp_time_global_from_ns, global_res);
+}
+
+static void time_test_local_strict_sum(void)
+{
+ time_test_sum(odp_time_local_strict, odp_time_local_from_ns, local_res);
+}
+
+static void time_test_global_strict_sum(void)
+{
+ time_test_sum(odp_time_global_strict, odp_time_global_from_ns, global_res);
+}
+
+static void time_test_wait_until(time_cb time_cur, time_from_ns_cb time_from_ns)
+{
+ int i;
+ odp_time_t lower_limit, upper_limit;
+ odp_time_t start_time, end_time, wait;
+ odp_time_t second = time_from_ns(ODP_TIME_SEC_IN_NS);
+
+ start_time = time_cur();
+ wait = start_time;
+ for (i = 0; i < WAIT_SECONDS; i++) {
+ wait = odp_time_sum(wait, second);
+ odp_time_wait_until(wait);
+ }
+ end_time = time_cur();
+
+ wait = odp_time_diff(end_time, start_time);
+ lower_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS -
+ DELAY_TOLERANCE);
+ upper_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS +
+ DELAY_TOLERANCE);
+
+ if (odp_time_cmp(wait, lower_limit) < 0) {
+ ODPH_ERR("Exceed lower limit: wait is %" PRIu64 ", lower_limit %" PRIu64 "\n",
+ odp_time_to_ns(wait), odp_time_to_ns(lower_limit));
+ CU_FAIL("Exceed lower limit\n");
+ }
+
+ if (odp_time_cmp(wait, upper_limit) > 0) {
+ ODPH_ERR("Exceed upper limit: wait is %" PRIu64 ", upper_limit %" PRIu64 "\n",
+ odp_time_to_ns(wait), odp_time_to_ns(lower_limit));
+ CU_FAIL("Exceed upper limit\n");
+ }
+}
+
+static void time_test_local_wait_until(void)
+{
+ time_test_wait_until(odp_time_local, odp_time_local_from_ns);
+}
+
+static void time_test_global_wait_until(void)
+{
+ time_test_wait_until(odp_time_global, odp_time_global_from_ns);
+}
+
+static void time_test_wait_ns(void)
+{
+ int i;
+ odp_time_t lower_limit, upper_limit;
+ odp_time_t start_time, end_time, diff;
+
+ start_time = odp_time_local();
+ for (i = 0; i < WAIT_SECONDS; i++)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
+ end_time = odp_time_local();
+
+ diff = odp_time_diff(end_time, start_time);
+
+ lower_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS -
+ DELAY_TOLERANCE);
+ upper_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS +
+ DELAY_TOLERANCE);
+
+ if (odp_time_cmp(diff, lower_limit) < 0) {
+ ODPH_ERR("Exceed lower limit: diff is %" PRIu64 ", lower_limit %" PRIu64 "\n",
+ odp_time_to_ns(diff), odp_time_to_ns(lower_limit));
+ CU_FAIL("Exceed lower limit\n");
+ }
+
+ if (odp_time_cmp(diff, upper_limit) > 0) {
+ ODPH_ERR("Exceed upper limit: diff is %" PRIu64 ", upper_limit %" PRIu64 "\n",
+ odp_time_to_ns(diff), odp_time_to_ns(upper_limit));
+ CU_FAIL("Exceed upper limit\n");
+ }
+}
+
+/* Check that ODP time is within +-5% of system time */
+static void check_time_diff(double t_odp, double t_system,
+ const char *test, int id)
+{
+ if (t_odp > t_system * 1.05) {
+ CU_FAIL("ODP time too high");
+ ODPH_ERR("ODP time too high (%s/%d): t_odp: %f, t_system: %f\n",
+ test, id, t_odp, t_system);
+ }
+ if (t_odp < t_system * 0.95) {
+ CU_FAIL("ODP time too low");
+ ODPH_ERR("ODP time too low (%s/%d): t_odp: %f, t_system: %f\n",
+ test, id, t_odp, t_system);
+ }
+}
+
+static void time_test_accuracy(time_cb time_cur,
+ time_cb time_cur_strict, time_from_ns_cb time_from_ns)
+{
+ int i;
+ odp_time_t t1[2], t2[2], wait;
+ struct timespec ts1, ts2, tsdiff;
+ double sec_c;
+ odp_time_t sec = time_from_ns(ODP_TIME_SEC_IN_NS);
+
+ i = clock_gettime(CLOCK_MONOTONIC, &ts1);
+ CU_ASSERT(i == 0);
+ t1[0] = time_cur_strict();
+ t1[1] = time_cur();
+
+ wait = odp_time_sum(t1[0], sec);
+ for (i = 0; i < 5; i++) {
+ odp_time_wait_until(wait);
+ wait = odp_time_add_ns(wait, ODP_TIME_SEC_IN_NS);
+ }
+
+ i = clock_gettime(CLOCK_MONOTONIC, &ts2);
+ CU_ASSERT(i == 0);
+ t2[0] = time_cur_strict();
+ t2[1] = time_cur();
+
+ if (ts2.tv_nsec < ts1.tv_nsec) {
+ tsdiff.tv_nsec = 1000000000L + ts2.tv_nsec - ts1.tv_nsec;
+ tsdiff.tv_sec = ts2.tv_sec - 1 - ts1.tv_sec;
+ } else {
+ tsdiff.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;
+ tsdiff.tv_sec = ts2.tv_sec - ts1.tv_sec;
+ }
+ sec_c = ((double)(tsdiff.tv_nsec) / 1000000000L) + tsdiff.tv_sec;
+
+ for (i = 0; i < 2; i++) {
+ odp_time_t diff = odp_time_diff(t2[i], t1[i]);
+ double sec_t = ((double)odp_time_to_ns(diff)) / ODP_TIME_SEC_IN_NS;
+
+ check_time_diff(sec_t, sec_c, __func__, i);
+ }
+}
+
+static void time_test_local_accuracy(void)
+{
+ time_test_accuracy(odp_time_local, odp_time_local_strict, odp_time_local_from_ns);
+}
+
+static void time_test_global_accuracy(void)
+{
+ time_test_accuracy(odp_time_global, odp_time_global_strict, odp_time_global_from_ns);
+}
+
+static void time_test_accuracy_nsec(void)
+{
+ uint64_t t1[4], t2[4];
+ struct timespec ts1, ts2, tsdiff;
+ double sec_c;
+ int i, ret;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts1);
+ CU_ASSERT(ret == 0);
+ t1[0] = odp_time_global_strict_ns();
+ t1[1] = odp_time_local_strict_ns();
+ t1[2] = odp_time_global_ns();
+ t1[3] = odp_time_local_ns();
+
+ for (i = 0; i < 5; i++)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS);
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts2);
+ CU_ASSERT(ret == 0);
+ t2[0] = odp_time_global_strict_ns();
+ t2[1] = odp_time_local_strict_ns();
+ t2[2] = odp_time_global_ns();
+ t2[3] = odp_time_local_ns();
+
+ if (ts2.tv_nsec < ts1.tv_nsec) {
+ tsdiff.tv_nsec = 1000000000L + ts2.tv_nsec - ts1.tv_nsec;
+ tsdiff.tv_sec = ts2.tv_sec - 1 - ts1.tv_sec;
+ } else {
+ tsdiff.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;
+ tsdiff.tv_sec = ts2.tv_sec - ts1.tv_sec;
+ }
+ sec_c = ((double)(tsdiff.tv_nsec) / 1000000000L) + tsdiff.tv_sec;
+
+ for (i = 0; i < 4; i++) {
+ uint64_t diff = t2[i] - t1[i];
+ double sec_t = ((double)diff) / ODP_TIME_SEC_IN_NS;
+
+ check_time_diff(sec_t, sec_c, __func__, i);
+ }
+}
+
+static int time_test_global_sync_thr(void *arg ODP_UNUSED)
+{
+ int tid = odp_thread_id();
+ odp_shm_t global_shm = odp_shm_lookup(GLOBAL_SHM_NAME);
+ global_shared_mem_t *global_mem = odp_shm_addr(global_shm);
+
+ if (!global_mem)
+ return 1;
+
+ odp_barrier_wait(&global_mem->test_barrier);
+ global_mem->time[tid][0] = odp_time_global();
+ odp_time_wait_ns(ODP_TIME_MSEC_IN_NS * 100);
+ odp_barrier_wait(&global_mem->test_barrier);
+ global_mem->time[tid][1] = odp_time_global();
+
+ return 0;
+}
+
+static void time_test_global_sync(const int ctrl)
+{
+ odp_cpumask_t cpumask;
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ uint64_t tolerance = odp_cunit_ci() ? TIME_TOLERANCE_CI_NS : TIME_TOLERANCE_NS;
+ const int num = ctrl ? 2 : global_mem->num_threads;
+
+ if (num < 2) {
+ printf(" number of threads is less than two, test skipped. ");
+ return;
+ }
+
+ odp_barrier_init(&global_mem->test_barrier, num);
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = time_test_global_sync_thr;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = *instance;
+
+ int thr = 0;
+
+ if (ctrl) {
+ /* Test sync between one control and one worker thread. */
+ int control_cpu;
+ int worker_cpu;
+
+ odp_cpumask_default_control(&cpumask, 1);
+ thr_common.cpumask = &cpumask;
+ thr_param.thr_type = ODP_THREAD_CONTROL;
+ control_cpu = odp_cpumask_first(&cpumask);
+
+ int r = odph_thread_create(&thread_tbl[thr++],
+ &thr_common, &thr_param, 1);
+ CU_ASSERT_FATAL(r == 1);
+ odp_cpumask_default_worker(&cpumask, 1);
+ worker_cpu = odp_cpumask_first(&cpumask);
+ if (control_cpu == worker_cpu) {
+ printf(" single CPU, relaxing tolerance. ");
+ tolerance = TIME_TOLERANCE_1CPU_NS;
+ }
+ } else {
+ /* Test sync between num worker threads. */
+ odp_cpumask_default_worker(&cpumask, num);
+ }
+
+ int cpu = odp_cpumask_first(&cpumask);
+
+ while (cpu >= 0) {
+ odp_cpumask_t cpumask_one;
+
+ /*
+ * Delay for more than the tolerance, so that we notice if the
+ * thread's view of global time is affected.
+ */
+ odp_time_wait_ns(tolerance * 2);
+
+ odp_cpumask_zero(&cpumask_one);
+ odp_cpumask_set(&cpumask_one, cpu);
+ thr_common.cpumask = &cpumask_one;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ int r = odph_thread_create(&thread_tbl[thr++],
+ &thr_common, &thr_param, 1);
+ CU_ASSERT_FATAL(r == 1);
+
+ cpu = odp_cpumask_next(&cpumask, cpu);
+ }
+
+ CU_ASSERT(odph_thread_join(thread_tbl, num) == num);
+
+ for (int s = 0; s < TIME_SAMPLES; s++) {
+ int min_idx = 0, max_idx = 0;
+ uint64_t min = UINT64_MAX, max = 0;
+ double avg = 0;
+
+ for (int i = 1; i < num + 1; i++) {
+ uint64_t t = odp_time_to_ns(global_mem->time[i][s]);
+
+ if (t < min) {
+ min = t;
+ min_idx = i;
+ }
+ }
+
+ printf("\nround %d\nthread time diffs: ", s);
+
+ for (int i = 1; i < num + 1; i++) {
+ uint64_t t = odp_time_to_ns(global_mem->time[i][s]) - min;
+
+ printf("%" PRIu64 " ", t);
+
+ if (t > max) {
+ max = t;
+ max_idx = i;
+ }
+
+ avg += t;
+ }
+
+ /* The min result itself is not included in the average. */
+ avg /= num - 1;
+ printf("\nmin: %" PRIu64 " (tid %d) max diff: %" PRIu64
+ " (tid %d) avg diff: %g", min, min_idx, max, max_idx, avg);
+ CU_ASSERT(max < tolerance);
+ }
+
+ printf("\n");
+}
+
+static void time_test_global_sync_workers(void)
+{
+ time_test_global_sync(0);
+}
+
+static void time_test_global_sync_control(void)
+{
+ time_test_global_sync(1);
+}
+
+static odp_queue_t select_dst_queue(int thread_id, const odp_queue_t queue[], uint32_t num)
+{
+ uint8_t rand_u8;
+ int rand_id = 0;
+
+ if (num == 1)
+ return queue[0];
+
+ do {
+ odp_random_data(&rand_u8, 1, ODP_RANDOM_BASIC);
+ rand_id = rand_u8 % num;
+ } while (rand_id == thread_id);
+
+ return queue[rand_id];
+}
+
+static int run_time_global_thread(void *arg)
+{
+ global_shared_mem_t *gbl = arg;
+ const int thread_id = odp_thread_id();
+ const odp_queue_t src_queue = gbl->queue[thread_id % gbl->num_queues];
+ const odp_queue_t *queues = gbl->queue;
+ const uint32_t num_queues = gbl->num_queues;
+ odp_atomic_u32_t *event_count = &gbl->event_count;
+
+ odp_barrier_wait(&gbl->test_barrier);
+
+ while (odp_atomic_load_u32(event_count) < TEST_ROUNDS) {
+ odp_time_t *ts;
+ odp_time_t cur_time;
+ odp_buffer_t buf;
+ odp_queue_t dst_queue;
+ odp_event_t ev = odp_queue_deq(src_queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ odp_cpu_pause();
+ continue;
+ }
+
+ cur_time = odp_time_global();
+
+ buf = odp_buffer_from_event(ev);
+ ts = odp_buffer_addr(buf);
+
+ CU_ASSERT(odp_time_cmp(cur_time, *ts) >= 0);
+
+ *ts = cur_time;
+
+ dst_queue = select_dst_queue(thread_id, queues, num_queues);
+
+ CU_ASSERT_FATAL(odp_queue_enq(dst_queue, ev) == 0);
+
+ odp_atomic_inc_u32(event_count);
+ }
+ return 0;
+}
+
+static void time_test_global_mt(void)
+{
+ odp_cpumask_t cpumask;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_pool_capability_t pool_capa;
+ odp_queue_param_t queue_param;
+ odp_queue_capability_t queue_capa;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ odp_time_t cur_time;
+ uint32_t i;
+ int num_workers = odp_cpumask_default_worker(&cpumask, global_mem->num_threads);
+ uint32_t num_events = num_workers;
+ uint32_t num_queues = num_workers;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+ CU_ASSERT_FATAL(odp_queue_capability(&queue_capa) == 0);
+
+ if (pool_capa.buf.max_num && num_events > pool_capa.buf.max_num)
+ num_events = pool_capa.buf.max_num;
+
+ if (queue_capa.plain.max_size && num_events > queue_capa.plain.max_size)
+ num_events = queue_capa.plain.max_size;
+
+ if (queue_capa.plain.max_num < num_queues)
+ num_queues = queue_capa.plain.max_num;
+ CU_ASSERT_FATAL(num_queues > 0);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.buf.size = sizeof(odp_time_t);
+ pool_param.buf.num = num_events;
+ pool_param.type = ODP_POOL_BUFFER;
+
+ pool = odp_pool_create("test event pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.size = num_events;
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+
+ for (i = 0; i < num_queues; i++) {
+ global_mem->queue[i] = odp_queue_create(NULL, &queue_param);
+ CU_ASSERT_FATAL(global_mem->queue[i] != ODP_QUEUE_INVALID);
+ }
+ global_mem->num_queues = num_queues;
+
+ odp_atomic_init_u32(&global_mem->event_count, 0);
+
+ for (i = 0; i < num_events; i++) {
+ odp_time_t *ts;
+ odp_buffer_t buf = odp_buffer_alloc(pool);
+
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ ts = odp_buffer_addr(buf);
+ *ts = odp_time_global();
+
+ CU_ASSERT_FATAL(odp_queue_enq(global_mem->queue[i % num_queues],
+ odp_buffer_to_event(buf)) == 0);
+ }
+ CU_ASSERT_FATAL(i > 0);
+ CU_ASSERT(i == num_events);
+
+ odp_barrier_init(&global_mem->test_barrier, num_workers);
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = run_time_global_thread;
+ thr_param.arg = global_mem;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = *instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ CU_ASSERT_FATAL(odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers) ==
+ num_workers);
+
+ CU_ASSERT(odph_thread_join(thread_tbl, num_workers) == num_workers);
+
+ cur_time = odp_time_global_strict();
+
+ for (i = 0; i < num_queues; i++) {
+ odp_queue_t queue = global_mem->queue[i];
+
+ while (1) {
+ odp_buffer_t buf;
+ odp_time_t *ts;
+ odp_event_t ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ buf = odp_buffer_from_event(ev);
+ ts = odp_buffer_addr(buf);
+
+ CU_ASSERT(odp_time_cmp(cur_time, *ts) >= 0);
+ odp_buffer_free(buf);
+ };
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+odp_testinfo_t time_suite_time[] = {
+ ODP_TEST_INFO(time_test_constants),
+ ODP_TEST_INFO(time_test_startup_time),
+ ODP_TEST_INFO(time_test_local_res),
+ ODP_TEST_INFO(time_test_local_conversion),
+ ODP_TEST_INFO(time_test_local_cmp),
+ ODP_TEST_INFO(time_test_local_diff),
+ ODP_TEST_INFO(time_test_local_sum),
+ ODP_TEST_INFO(time_test_global_mt),
+ ODP_TEST_INFO(time_test_global_res),
+ ODP_TEST_INFO(time_test_global_conversion),
+ ODP_TEST_INFO(time_test_global_cmp),
+ ODP_TEST_INFO(time_test_global_diff),
+ ODP_TEST_INFO(time_test_global_sum),
+ ODP_TEST_INFO(time_test_wait_ns),
+ ODP_TEST_INFO(time_test_monotony),
+ ODP_TEST_INFO(time_test_local_wait_until),
+ ODP_TEST_INFO(time_test_global_wait_until),
+ ODP_TEST_INFO(time_test_local_accuracy),
+ ODP_TEST_INFO(time_test_global_accuracy),
+ ODP_TEST_INFO(time_test_accuracy_nsec),
+ ODP_TEST_INFO(time_test_local_strict_diff),
+ ODP_TEST_INFO(time_test_local_strict_sum),
+ ODP_TEST_INFO(time_test_local_strict_cmp),
+ ODP_TEST_INFO(time_test_global_strict_diff),
+ ODP_TEST_INFO(time_test_global_strict_sum),
+ ODP_TEST_INFO(time_test_global_strict_cmp),
+ ODP_TEST_INFO(time_test_global_sync_workers),
+ ODP_TEST_INFO(time_test_global_sync_control),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t time_suites[] = {
+ {"Time", NULL, NULL, time_suite_time},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(time_global_init);
+ odp_cunit_register_global_term(time_global_term);
+
+ ret = odp_cunit_register(time_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/test/common_plat/validation/api/timer/.gitignore b/test/validation/api/timer/.gitignore
index 74e8fa992..74e8fa992 100644
--- a/test/common_plat/validation/api/timer/.gitignore
+++ b/test/validation/api/timer/.gitignore
diff --git a/test/validation/api/timer/Makefile.am b/test/validation/api/timer/Makefile.am
new file mode 100644
index 000000000..bc33e731f
--- /dev/null
+++ b/test/validation/api/timer/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = timer_main
+timer_main_SOURCES = timer.c
diff --git a/test/validation/api/timer/timer.c b/test/validation/api/timer/timer.c
new file mode 100644
index 000000000..e091b1cd4
--- /dev/null
+++ b/test/validation/api/timer/timer.c
@@ -0,0 +1,3307 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2019-2023 Nokia
+ */
+
+/* For rand_r and nanosleep */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <time.h>
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#include <stdint.h>
+
+#define MAX_WORKERS 32
+
+#define GLOBAL_SHM_NAME "GlobalTimerTest"
+
+#define MAX_TIMER_POOLS 1024
+
+/* Timeout range in milliseconds (ms) */
+#define RANGE_MS 2000
+
+/* Number of timers per thread */
+#define NTIMERS 2000
+
+/* Number of extra timers per thread */
+#define EXTRA_TIMERS 256
+
+#define NAME "timer_pool"
+#define MSEC ODP_TIME_MSEC_IN_NS
+#define THREE_POINT_THREE_MSEC (10 * ODP_TIME_MSEC_IN_NS / 3)
+#define USER_PTR ((void *)0xdead)
+#define TICK_INVALID (~(uint64_t)0)
+#define YEAR_IN_NS (365 * 24 * ODP_TIME_HOUR_IN_NS)
+
+/* Test case options */
+#define PRIV 1
+#define EXP_RELAX 1
+#define FIRST_TICK 1
+#define RELATIVE ODP_TIMER_TICK_REL
+#define ABSOLUTE ODP_TIMER_TICK_ABS
+
+enum {
+ TIMEOUT = 0,
+ CANCEL
+};
+
+enum {
+ START = 0,
+ RESTART
+};
+
+/* Timer helper structure */
+struct test_timer {
+ odp_timer_t tim; /* Timer handle */
+ odp_event_t ev; /* Timeout event */
+ odp_event_t ev2; /* Copy of event handle */
+ uint64_t tick; /* Expiration tick or TICK_INVALID */
+};
+
+typedef struct {
+ /* Periodic timer support */
+ int periodic_support;
+
+ /* Default resolution / timeout parameters */
+ struct {
+ uint64_t res_ns;
+ uint64_t min_tmo;
+ uint64_t max_tmo;
+ odp_bool_t queue_type_sched;
+ odp_bool_t queue_type_plain;
+ } param;
+
+ /* Timeout pool handle used by all threads */
+ odp_pool_t tbp;
+
+ /* Timer pool handle used by all threads */
+ odp_timer_pool_t tp;
+
+ /* Barrier for thread synchronization */
+ odp_barrier_t test_barrier;
+
+ /* Count of timeouts delivered too late */
+ odp_atomic_u32_t ndelivtoolate;
+
+ /* Sum of all allocated timers from all threads. Thread-local
+ * caches may make this number lower than the capacity of the pool */
+ odp_atomic_u32_t timers_allocated;
+
+ /* Number of timers allocated per thread */
+ uint32_t timers_per_thread;
+
+ /* Queue type to be tested */
+ odp_queue_type_t test_queue_type;
+
+} global_shared_mem_t;
+
+typedef struct {
+ odp_timer_clk_src_t clk_src;
+ global_shared_mem_t global_mem;
+
+} test_global_t;
+
+static global_shared_mem_t *global_mem;
+static test_global_t *test_global;
+static odp_shm_t global_shm;
+static odp_instance_t inst;
+
+static int global_init(void)
+{
+ odp_init_t init_param;
+ odph_helper_options_t helper_options;
+
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("odph_options() failed\n");
+ return -1;
+ }
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ if (0 != odp_init_global(&inst, &init_param, NULL)) {
+ ODPH_ERR("odp_init_global() failed\n");
+ return -1;
+ }
+ if (0 != odp_init_local(inst, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("odp_init_local() failed\n");
+ return -1;
+ }
+
+ global_shm = odp_shm_reserve(GLOBAL_SHM_NAME,
+ sizeof(test_global_t),
+ ODP_CACHE_LINE_SIZE, 0);
+ if (global_shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Unable to reserve memory for global_shm\n");
+ return -1;
+ }
+
+ test_global = odp_shm_addr(global_shm);
+ memset(test_global, 0, sizeof(*test_global));
+ global_mem = &test_global->global_mem;
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
+ return 0;
+}
+
+static int global_term(void)
+{
+ if (0 != odp_shm_free(global_shm)) {
+ ODPH_ERR("odp_shm_free() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_local()) {
+ ODPH_ERR("odp_term_local() failed\n");
+ return -1;
+ }
+
+ if (0 != odp_term_global(inst)) {
+ ODPH_ERR("odp_term_global() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int timer_global_init(odp_instance_t *instance)
+{
+ odp_timer_capability_t capa;
+ odp_timer_res_capability_t res_capa;
+ uint64_t res_ns, min_tmo, max_tmo;
+ unsigned int range;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ *instance = inst;
+
+ memset(global_mem, 0, sizeof(global_shared_mem_t));
+
+ memset(&capa, 0, sizeof(capa));
+ if (odp_timer_capability(clk_src, &capa)) {
+ ODPH_ERR("Timer capability failed\n");
+ return -1;
+ }
+
+ global_mem->periodic_support = capa.periodic.max_pools > 0;
+
+ /* By default 2 msec resolution */
+ res_ns = 2 * ODP_TIME_MSEC_IN_NS;
+ if (res_ns < capa.max_res.res_ns)
+ res_ns = capa.max_res.res_ns;
+
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.res_ns = res_ns;
+
+ if (odp_timer_res_capability(clk_src, &res_capa)) {
+ ODPH_ERR("Timer resolution capability failed\n");
+ return -1;
+ }
+
+ /* Try to keep min timeout error margin within +-20% */
+ min_tmo = 5 * res_ns;
+ if (min_tmo < res_capa.min_tmo)
+ min_tmo = res_capa.min_tmo;
+
+ /* Max 1 hour */
+ max_tmo = 3600 * ODP_TIME_SEC_IN_NS;
+ if (max_tmo > res_capa.max_tmo)
+ max_tmo = res_capa.max_tmo;
+
+ range = (RANGE_MS * 1000) + THREE_POINT_THREE_MSEC;
+ if ((max_tmo - min_tmo) < range) {
+ ODPH_ERR("Validation test needs %u msec range\n", range);
+ return -1;
+ }
+
+ /* Default parameters for test cases using the default clock source */
+ global_mem->param.res_ns = res_ns;
+ global_mem->param.min_tmo = min_tmo;
+ global_mem->param.max_tmo = max_tmo;
+ global_mem->param.queue_type_plain = capa.queue_type_plain;
+ global_mem->param.queue_type_sched = capa.queue_type_sched;
+
+ return 0;
+}
+
+static int timer_global_term(odp_instance_t inst)
+{
+ (void)inst;
+
+ return 0;
+}
+
+static int
+check_sched_queue_support(void)
+{
+ if (global_mem->param.queue_type_sched)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int
+check_plain_queue_support(void)
+{
+ if (global_mem->param.queue_type_plain)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_periodic_support(void)
+{
+ if (global_mem->periodic_support)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_periodic_sched_support(void)
+{
+ if (global_mem->periodic_support && global_mem->param.queue_type_sched)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int check_periodic_plain_support(void)
+{
+ if (global_mem->periodic_support && global_mem->param.queue_type_plain)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void timer_test_capa(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_res_capability_t res_capa;
+ int ret;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT(capa.highest_res_ns == capa.max_res.res_ns);
+ /* Assuming max resolution to be 100 msec or better */
+ CU_ASSERT(capa.max_res.res_ns <= 100000000);
+ CU_ASSERT(capa.max_res.res_hz >= 10);
+ CU_ASSERT(capa.max_res.res_ns < capa.max_res.max_tmo);
+ CU_ASSERT(capa.max_res.min_tmo < capa.max_res.max_tmo);
+
+ /* With max timeout, resolution may be low (worse than 1 sec) */
+ CU_ASSERT(capa.max_tmo.res_ns < capa.max_tmo.max_tmo);
+ CU_ASSERT(capa.max_tmo.min_tmo < capa.max_tmo.max_tmo);
+ CU_ASSERT(capa.max_tmo.res_ns != 0 || capa.max_tmo.res_hz != 0);
+ if (capa.max_tmo.res_hz == 0)
+ CU_ASSERT(capa.max_tmo.res_ns > 1000000000);
+
+ /* Set max resolution in nsec */
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.res_ns = capa.max_res.res_ns;
+
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT(res_capa.res_ns == capa.max_res.res_ns);
+ CU_ASSERT(res_capa.min_tmo == capa.max_res.min_tmo);
+ CU_ASSERT(res_capa.max_tmo == capa.max_res.max_tmo);
+
+ if (capa.max_res.res_ns > 1) {
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.res_ns = capa.max_res.res_ns - 1;
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT(ret < 0);
+ }
+
+ /* Set max resolution in hz */
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.res_hz = capa.max_res.res_hz;
+
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT(res_capa.res_hz == capa.max_res.res_hz);
+ CU_ASSERT(res_capa.min_tmo == capa.max_res.min_tmo);
+ CU_ASSERT(res_capa.max_tmo == capa.max_res.max_tmo);
+
+ if (capa.max_res.res_hz < UINT64_MAX) {
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.res_hz = capa.max_res.res_hz + 1;
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT(ret < 0);
+ }
+
+ /* Set max timeout */
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.max_tmo = capa.max_tmo.max_tmo;
+
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT(res_capa.max_tmo == capa.max_tmo.max_tmo);
+ CU_ASSERT(res_capa.min_tmo == capa.max_tmo.min_tmo);
+ CU_ASSERT(res_capa.res_ns == capa.max_tmo.res_ns);
+ CU_ASSERT(res_capa.res_hz == capa.max_tmo.res_hz);
+
+ if (capa.max_tmo.max_tmo < UINT64_MAX) {
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.max_tmo = capa.max_tmo.max_tmo + 1;
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT(ret < 0);
+ }
+}
+
+static void timer_test_capa_allsrc(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_clk_src_t clk_src;
+ int i;
+
+ /* Check that all API clock source enumeration values exist */
+ CU_ASSERT_FATAL(ODP_CLOCK_DEFAULT == ODP_CLOCK_SRC_0);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_0 + 1 == ODP_CLOCK_SRC_1);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_0 + 2 == ODP_CLOCK_SRC_2);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_0 + 3 == ODP_CLOCK_SRC_3);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_0 + 4 == ODP_CLOCK_SRC_4);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_0 + 5 == ODP_CLOCK_SRC_5);
+ CU_ASSERT_FATAL(ODP_CLOCK_SRC_5 + 1 == ODP_CLOCK_NUM_SRC);
+
+ CU_ASSERT(odp_timer_capability(ODP_CLOCK_DEFAULT, &capa) == 0);
+
+ for (i = 0; i < ODP_CLOCK_NUM_SRC; i++) {
+ int ret;
+
+ clk_src = ODP_CLOCK_SRC_0 + i;
+
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT(ret == 0 || ret == -1);
+ }
+}
+
+static void test_param_init(uint8_t fill)
+{
+ odp_timer_pool_param_t tp_param;
+
+ memset(&tp_param, fill, sizeof(tp_param));
+
+ odp_timer_pool_param_init(&tp_param);
+ CU_ASSERT(tp_param.res_ns == 0);
+ CU_ASSERT(tp_param.res_hz == 0);
+ CU_ASSERT(tp_param.min_tmo == 0);
+ CU_ASSERT(tp_param.priv == 0);
+ CU_ASSERT(tp_param.clk_src == ODP_CLOCK_DEFAULT);
+ CU_ASSERT(tp_param.exp_mode == ODP_TIMER_EXP_AFTER);
+ CU_ASSERT(tp_param.timer_type == ODP_TIMER_TYPE_SINGLE);
+ CU_ASSERT(tp_param.periodic.base_freq_hz.integer == 0);
+ CU_ASSERT(tp_param.periodic.base_freq_hz.numer == 0);
+ CU_ASSERT(tp_param.periodic.base_freq_hz.denom == 0);
+}
+
+static void timer_test_param_init(void)
+{
+ test_param_init(0);
+ test_param_init(0xff);
+}
+
+static void timer_test_timeout_pool_alloc(void)
+{
+ odp_pool_t pool;
+ const int num = 3;
+ odp_timeout_t tmo[num];
+ odp_event_t ev;
+ int index;
+ odp_bool_t wrong_type = false, wrong_subtype = false;
+ odp_pool_param_t params;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = num;
+
+ pool = odp_pool_create("timeout_pool_alloc", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_pool_print(pool);
+
+ /* Try to allocate num items from the pool */
+ for (index = 0; index < num; index++) {
+ odp_event_subtype_t subtype;
+
+ tmo[index] = odp_timeout_alloc(pool);
+
+ if (tmo[index] == ODP_TIMEOUT_INVALID)
+ break;
+
+ ev = odp_timeout_to_event(tmo[index]);
+ if (odp_event_type(ev) != ODP_EVENT_TIMEOUT)
+ wrong_type = true;
+ if (odp_event_subtype(ev) != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_TIMEOUT)
+ wrong_type = true;
+ if (subtype != ODP_EVENT_NO_SUBTYPE)
+ wrong_subtype = true;
+
+ /* No source pool for timeout events */
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+ }
+
+ /* Check that the pool had at least num items */
+ CU_ASSERT(index == num);
+ /* index points out of buffer[] or it point to an invalid buffer */
+ index--;
+
+ /* Check that the pool had correct buffers */
+ CU_ASSERT(!wrong_type);
+ CU_ASSERT(!wrong_subtype);
+
+ for (; index >= 0; index--)
+ odp_timeout_free(tmo[index]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_timeout_pool_alloc_multi(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ uint32_t num_timeouts = 1000;
+ uint32_t num_allocated = 0;
+ uint32_t num_freed = 0;
+ uint32_t num_retries = 0;
+
+ CU_ASSERT_FATAL(!odp_pool_capability(&capa));
+
+ if (capa.tmo.max_num && capa.tmo.max_num < num_timeouts)
+ num_timeouts = capa.tmo.max_num;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = num_timeouts;
+
+ pool = odp_pool_create("timeout_pool_alloc_multi", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_timeout_t tmo[num_timeouts];
+
+ do {
+ int ret;
+ int num = (num_timeouts - num_allocated) / 2;
+
+ if (num < 1)
+ num = 1;
+
+ ret = odp_timeout_alloc_multi(pool, &tmo[num_allocated], num);
+ if (ret < 0) {
+ CU_FAIL("Timeout alloc multi failed");
+ break;
+ }
+ CU_ASSERT_FATAL(ret <= num);
+
+ num_retries = (ret == 0) ? num_retries + 1 : 0;
+ num_allocated += ret;
+ } while (num_allocated < num_timeouts && num_retries < 100);
+ CU_ASSERT(num_allocated == num_timeouts)
+
+ if (num_allocated) {
+ do {
+ int num = num_allocated / 2;
+
+ if (num < 1)
+ num = 1;
+
+ odp_timeout_free_multi(&tmo[num_freed], num);
+
+ num_freed += num;
+ num_allocated -= num;
+ } while (num_allocated);
+ }
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_timeout_from_event(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t param;
+ uint32_t i;
+ const uint32_t num = 10;
+ uint32_t num_alloc = 0;
+ odp_timeout_t tmo_tbl[num];
+ odp_timeout_t tmo2_tbl[num];
+ odp_event_t ev_tbl[num];
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = num;
+
+ pool = odp_pool_create("test_timeout_from_event", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num; i++) {
+ tmo_tbl[i] = odp_timeout_alloc(pool);
+ if (tmo_tbl[i] == ODP_TIMEOUT_INVALID)
+ break;
+ ev_tbl[i] = odp_timeout_to_event(tmo_tbl[i]);
+ num_alloc++;
+ }
+
+ CU_ASSERT(num_alloc == num);
+
+ for (i = 0; i < num_alloc; i++) {
+ odp_timeout_t tmo = odp_timeout_from_event(ev_tbl[i]);
+
+ CU_ASSERT(odp_timeout_to_u64(tmo) == odp_timeout_to_u64(tmo_tbl[i]));
+ }
+
+ odp_timeout_from_event_multi(tmo2_tbl, ev_tbl, num_alloc);
+ for (i = 0; i < num_alloc; i++)
+ CU_ASSERT(odp_timeout_to_u64(tmo2_tbl[i]) == odp_timeout_to_u64(tmo_tbl[i]));
+
+ for (i = 0; i < num_alloc; i++)
+ odp_timeout_free(tmo_tbl[i]);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_timeout_pool_free(void)
+{
+ odp_pool_t pool;
+ odp_timeout_t tmo;
+ odp_pool_param_t params;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = 1;
+
+ pool = odp_pool_create("timeout_pool_free", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ odp_pool_print(pool);
+
+ /* Allocate the only timeout from the pool */
+ tmo = odp_timeout_alloc(pool);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+
+ /* Pool should have only one timeout */
+ CU_ASSERT_FATAL(odp_timeout_alloc(pool) == ODP_TIMEOUT_INVALID)
+
+ odp_timeout_free(tmo);
+
+ /* Check that the timeout was returned back to the pool */
+ tmo = odp_timeout_alloc(pool);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+
+ odp_timeout_free(tmo);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_timeout_user_area(void)
+{
+ odp_pool_t pool;
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t param;
+ uint32_t i, max_size;
+ void *addr;
+ void *prev = NULL;
+ const uint32_t num = 10;
+ uint32_t num_alloc = 0;
+ uint32_t size = 1024;
+ odp_timeout_t tmo[num];
+
+ CU_ASSERT_FATAL(!odp_pool_capability(&pool_capa));
+ max_size = pool_capa.tmo.max_uarea_size;
+
+ if (max_size == 0) {
+ ODPH_DBG("Timeout user area not supported\n");
+ return;
+ }
+
+ if (size > max_size)
+ size = max_size;
+
+ odp_pool_param_init(&param);
+ param.type = ODP_POOL_TIMEOUT;
+ param.tmo.num = num;
+ param.tmo.uarea_size = size;
+
+ pool = odp_pool_create("test_user_area", &param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag = 0;
+
+ tmo[i] = odp_timeout_alloc(pool);
+
+ if (tmo[i] == ODP_TIMEOUT_INVALID)
+ break;
+
+ num_alloc++;
+
+ addr = odp_timeout_user_area(tmo[i]);
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ ev = odp_timeout_to_event(tmo[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag < 0);
+
+ prev = addr;
+ memset(addr, 0, size);
+ }
+
+ CU_ASSERT(i == num);
+
+ for (i = 0; i < num_alloc; i++)
+ odp_timeout_free(tmo[i]);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_pool_create_destroy(void)
+{
+ odp_timer_pool_param_t tparam;
+ odp_queue_param_t queue_param;
+ odp_timer_capability_t capa;
+ odp_timer_pool_info_t info;
+ odp_timer_pool_t tp[2];
+ odp_timer_t tim;
+ odp_queue_t queue;
+ int ret;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_queue_param_init(&queue_param);
+ if (capa.queue_type_plain) {
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ } else if (capa.queue_type_sched) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+ queue = odp_queue_create("timer_queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ odp_timer_pool_param_init(&tparam);
+ tparam.res_ns = global_mem->param.res_ns;
+ tparam.min_tmo = global_mem->param.min_tmo;
+ tparam.max_tmo = global_mem->param.max_tmo;
+ tparam.num_timers = 100;
+ tparam.priv = 0;
+ tparam.clk_src = clk_src;
+
+ tp[0] = odp_timer_pool_create("timer_pool_a", &tparam);
+ CU_ASSERT(tp[0] != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp[0], 1) == 1);
+
+ tim = odp_timer_alloc(tp[0], queue, USER_PTR);
+ CU_ASSERT(tim != ODP_TIMER_INVALID);
+ CU_ASSERT(odp_timer_free(tim) == 0);
+
+ odp_timer_pool_destroy(tp[0]);
+
+ tp[0] = odp_timer_pool_create("timer_pool_b", &tparam);
+ CU_ASSERT(tp[0] != ODP_TIMER_POOL_INVALID);
+ tp[1] = odp_timer_pool_create("timer_pool_c", &tparam);
+ CU_ASSERT(tp[1] != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(tp, 2) == 2);
+
+ odp_timer_pool_destroy(tp[0]);
+
+ tp[0] = odp_timer_pool_create("timer_pool_d", &tparam);
+ CU_ASSERT(tp[0] != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp[0], 1) == 1);
+
+ memset(&info, 0, sizeof(odp_timer_pool_info_t));
+ CU_ASSERT(odp_timer_pool_info(tp[1], &info) == 0);
+ CU_ASSERT(strcmp(info.name, "timer_pool_c") == 0);
+
+ tim = odp_timer_alloc(tp[1], queue, USER_PTR);
+ CU_ASSERT(tim != ODP_TIMER_INVALID);
+ CU_ASSERT(odp_timer_free(tim) == 0);
+
+ odp_timer_pool_destroy(tp[1]);
+
+ memset(&info, 0, sizeof(odp_timer_pool_info_t));
+ CU_ASSERT(odp_timer_pool_info(tp[0], &info) == 0);
+ CU_ASSERT(strcmp(info.name, "timer_pool_d") == 0);
+
+ tim = odp_timer_alloc(tp[0], queue, USER_PTR);
+ CU_ASSERT(tim != ODP_TIMER_INVALID);
+ CU_ASSERT(odp_timer_free(tim) == 0);
+
+ odp_timer_pool_destroy(tp[0]);
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void timer_pool_create_max(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_pool_param_t tp_param;
+ odp_queue_param_t queue_param;
+ odp_queue_t queue;
+ uint32_t i;
+ int ret;
+ uint64_t tmo_ns = ODP_TIME_SEC_IN_NS;
+ uint64_t res_ns = ODP_TIME_SEC_IN_NS / 10;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ uint32_t num = capa.max_pools;
+
+ if (num > MAX_TIMER_POOLS)
+ num = MAX_TIMER_POOLS;
+
+ odp_timer_pool_t tp[num];
+ odp_timer_t timer[num];
+
+ if (capa.max_tmo.max_tmo < tmo_ns) {
+ tmo_ns = capa.max_tmo.max_tmo;
+ res_ns = capa.max_tmo.res_ns;
+ }
+
+ odp_queue_param_init(&queue_param);
+
+ if (capa.queue_type_sched)
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue = odp_queue_create("timer_queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ odp_timer_pool_param_init(&tp_param);
+
+ tp_param.res_ns = res_ns;
+ tp_param.min_tmo = tmo_ns / 2;
+ tp_param.max_tmo = tmo_ns;
+ tp_param.num_timers = 1;
+ tp_param.clk_src = clk_src;
+
+ for (i = 0; i < num; i++) {
+ tp[i] = odp_timer_pool_create("test_max", &tp_param);
+ if (tp[i] == ODP_TIMER_POOL_INVALID)
+ ODPH_ERR("Timer pool create failed: %u / %u\n", i, num);
+
+ CU_ASSERT_FATAL(tp[i] != ODP_TIMER_POOL_INVALID);
+ }
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(tp, num) == (int)num);
+
+ for (i = 0; i < num; i++) {
+ timer[i] = odp_timer_alloc(tp[i], queue, USER_PTR);
+
+ if (timer[i] == ODP_TIMER_INVALID)
+ ODPH_ERR("Timer alloc failed: %u / %u\n", i, num);
+
+ CU_ASSERT_FATAL(timer[i] != ODP_TIMER_INVALID);
+
+ /* Pool should have only one timer */
+ CU_ASSERT_FATAL(odp_timer_alloc(tp[i], queue, USER_PTR) == ODP_TIMER_INVALID);
+ }
+
+ for (i = 0; i < num; i++)
+ CU_ASSERT(odp_timer_free(timer[i]) == 0);
+
+ for (i = 0; i < num; i++)
+ odp_timer_pool_destroy(tp[i]);
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+}
+
+static void timer_pool_max_res(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_pool_param_t tp_param;
+ odp_queue_param_t queue_param;
+ odp_timer_pool_t tp;
+ odp_timer_t timer;
+ odp_timer_start_t start_param;
+ odp_pool_param_t pool_param;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_timeout_t tmo;
+ odp_event_t ev;
+ uint64_t tick;
+ int ret, i;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = 10;
+ pool = odp_pool_create("timeout_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ if (capa.queue_type_plain) {
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ } else if (capa.queue_type_sched) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+ queue = odp_queue_create("timer_queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ /* Highest resolution: first in nsec, then in hz */
+ for (i = 0; i < 2; i++) {
+ odp_timer_pool_param_init(&tp_param);
+
+ if (i == 0) {
+ printf("\n Highest resolution %" PRIu64 " nsec\n",
+ capa.max_res.res_ns);
+ tp_param.res_ns = capa.max_res.res_ns;
+ } else {
+ printf(" Highest resolution %" PRIu64 " Hz\n",
+ capa.max_res.res_hz);
+ tp_param.res_hz = capa.max_res.res_hz;
+ }
+
+ tp_param.min_tmo = capa.max_res.min_tmo;
+ tp_param.max_tmo = capa.max_res.max_tmo;
+ tp_param.num_timers = 100;
+ tp_param.priv = 0;
+ tp_param.clk_src = clk_src;
+
+ tp = odp_timer_pool_create("high_res_tp", &tp_param);
+ CU_ASSERT_FATAL(tp != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ /* Maximum timeout length with maximum resolution */
+ tick = odp_timer_ns_to_tick(tp, capa.max_res.max_tmo);
+
+ timer = odp_timer_alloc(tp, queue, USER_PTR);
+ CU_ASSERT_FATAL(timer != ODP_TIMER_INVALID);
+
+ tmo = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(tmo);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tick;
+ start_param.tmo_ev = ev;
+
+ ret = odp_timer_start(timer, &start_param);
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+
+ ev = ODP_EVENT_INVALID;
+ ret = odp_timer_cancel(timer, &ev);
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+
+ if (ret == ODP_TIMER_SUCCESS) {
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(odp_timer_free(timer) == 0);
+ odp_timer_pool_destroy(tp);
+ }
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static odp_event_t wait_event(odp_queue_type_t queue_type, odp_queue_t queue,
+ odp_time_t t1, uint64_t wait_ns)
+{
+ odp_time_t t2;
+ odp_event_t ev;
+ odp_queue_t from = ODP_QUEUE_INVALID;
+
+ while (1) {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(&from, ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ if (ev != ODP_EVENT_INVALID) {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ CU_ASSERT(from == queue);
+
+ return ev;
+ }
+
+ t2 = odp_time_global();
+ if (odp_time_diff_ns(t2, t1) > wait_ns)
+ break;
+ }
+
+ return ODP_EVENT_INVALID;
+}
+
+static void free_schedule_context(odp_queue_type_t queue_type)
+{
+ if (queue_type != ODP_QUEUE_TYPE_SCHED)
+ return;
+
+ while (1) {
+ odp_event_t ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+}
+
+static void timer_single_shot(odp_queue_type_t queue_type, odp_timer_tick_type_t tick_type,
+ int restart, int cancel, int rounds, uint64_t tmo_ns)
+{
+ odp_timer_capability_t capa;
+ odp_timer_res_capability_t res_capa;
+ odp_timer_pool_param_t tp_param;
+ odp_queue_param_t queue_param;
+ odp_pool_param_t pool_param;
+ odp_timer_start_t start_param;
+ odp_timer_pool_t tp;
+ odp_timer_t timer;
+ odp_pool_t pool;
+ odp_queue_t queue;
+ odp_timeout_t tmo;
+ odp_event_t ev;
+ odp_time_t t1, t2;
+ uint64_t tick, nsec, res_ns, min_tmo;
+ int ret, i;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT_FATAL(capa.max_tmo.max_tmo > 0);
+
+ /* Use timeout and resolution values that are within capability limits */
+ if (capa.max_tmo.max_tmo < tmo_ns)
+ tmo_ns = capa.max_tmo.max_tmo;
+
+ memset(&res_capa, 0, sizeof(res_capa));
+ res_capa.max_tmo = tmo_ns;
+
+ ret = odp_timer_res_capability(clk_src, &res_capa);
+ CU_ASSERT_FATAL(ret == 0);
+ CU_ASSERT_FATAL(res_capa.res_ns > 0);
+
+ res_ns = tmo_ns / 10;
+
+ if (res_ns < res_capa.res_ns)
+ res_ns = res_capa.res_ns;
+
+ /* Test expects better resolution than 0.5x timeout */
+ CU_ASSERT_FATAL(res_ns < tmo_ns / 2);
+
+ min_tmo = tmo_ns / 4;
+ if (min_tmo < res_capa.min_tmo)
+ min_tmo = res_capa.min_tmo;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = 10;
+ pool = odp_pool_create("timeout_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = queue_type;
+
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ queue = odp_queue_create("timer_queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ odp_timer_pool_param_init(&tp_param);
+
+ tp_param.res_ns = res_ns;
+ tp_param.min_tmo = min_tmo;
+ tp_param.max_tmo = tmo_ns;
+ tp_param.num_timers = 1;
+ tp_param.clk_src = clk_src;
+
+ tp = odp_timer_pool_create("test_single", &tp_param);
+ CU_ASSERT_FATAL(tp != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ timer = odp_timer_alloc(tp, queue, USER_PTR);
+ CU_ASSERT_FATAL(timer != ODP_TIMER_INVALID);
+
+ tmo = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(tmo);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+
+ nsec = tmo_ns;
+ if (restart)
+ nsec = tmo_ns / 2;
+
+ for (i = 0; i < rounds; i++) {
+ tick = odp_timer_ns_to_tick(tp, nsec);
+ if (tick_type == ODP_TIMER_TICK_ABS)
+ tick += odp_timer_current_tick(tp);
+
+ start_param.tick_type = tick_type;
+ start_param.tick = tick;
+ start_param.tmo_ev = ev;
+
+ ret = odp_timer_start(timer, &start_param);
+ CU_ASSERT_FATAL(ret == ODP_TIMER_SUCCESS);
+
+ if (restart) {
+ tick = odp_timer_ns_to_tick(tp, tmo_ns);
+ if (tick_type == ODP_TIMER_TICK_ABS)
+ tick += odp_timer_current_tick(tp);
+
+ start_param.tick = tick;
+ start_param.tmo_ev = ODP_EVENT_INVALID;
+
+ ret = odp_timer_restart(timer, &start_param);
+ CU_ASSERT_FATAL(ret == ODP_TIMER_SUCCESS);
+ }
+
+ ev = ODP_EVENT_INVALID;
+
+ if (cancel) {
+ ret = odp_timer_cancel(timer, &ev);
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+
+ if (ret == ODP_TIMER_SUCCESS)
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ } else {
+ uint64_t diff_ns;
+
+ t1 = odp_time_global();
+ ev = wait_event(queue_type, queue, t1, 10 * tmo_ns);
+ t2 = odp_time_global();
+ diff_ns = odp_time_diff_ns(t2, t1);
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+ CU_ASSERT(diff_ns < 2 * tmo_ns);
+ CU_ASSERT((double)diff_ns > 0.5 * tmo_ns);
+ }
+
+ if (ev != ODP_EVENT_INVALID) {
+ CU_ASSERT_FATAL(odp_event_type(ev) == ODP_EVENT_TIMEOUT);
+ tmo = odp_timeout_from_event(ev);
+ CU_ASSERT(odp_timeout_user_ptr(tmo) == USER_PTR);
+ CU_ASSERT(odp_timeout_timer(tmo) == timer);
+
+ if (!cancel) {
+ if (tick_type == ODP_TIMER_TICK_ABS) {
+ /* CU_ASSERT needs these extra brackets */
+ CU_ASSERT(odp_timeout_tick(tmo) == tick);
+ } else {
+ CU_ASSERT(odp_timeout_tick(tmo) > tick);
+ }
+ }
+ } else {
+ ODPH_DBG("Event missing\n");
+ break;
+ }
+ }
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ free_schedule_context(queue_type);
+
+ CU_ASSERT(odp_timer_free(timer) == 0);
+ odp_timer_pool_destroy(tp);
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_plain_rel_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, RELATIVE, START, TIMEOUT, 2, 50 * MSEC);
+}
+
+static void timer_plain_abs_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, ABSOLUTE, START, TIMEOUT, 2, 50 * MSEC);
+}
+
+static void timer_plain_rel_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, RELATIVE, START, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_plain_abs_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, ABSOLUTE, START, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_plain_rel_restart_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, RELATIVE, RESTART, TIMEOUT, 2, 60 * MSEC);
+}
+
+static void timer_plain_abs_restart_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, ABSOLUTE, RESTART, TIMEOUT, 2, 60 * MSEC);
+}
+
+static void timer_plain_rel_restart_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, RELATIVE, RESTART, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_plain_abs_restart_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, ABSOLUTE, RESTART, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_plain_abs_wait_3sec(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_PLAIN, ABSOLUTE, START, TIMEOUT, 30, 110 * MSEC);
+}
+
+static void timer_sched_rel_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, RELATIVE, START, TIMEOUT, 2, 50 * MSEC);
+}
+
+static void timer_sched_abs_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, ABSOLUTE, START, TIMEOUT, 2, 50 * MSEC);
+}
+
+static void timer_sched_rel_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, RELATIVE, START, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_sched_abs_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, ABSOLUTE, START, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_sched_rel_restart_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, RELATIVE, RESTART, TIMEOUT, 2, 60 * MSEC);
+}
+
+static void timer_sched_abs_restart_wait(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, ABSOLUTE, RESTART, TIMEOUT, 2, 60 * MSEC);
+}
+
+static void timer_sched_rel_restart_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, RELATIVE, RESTART, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_sched_abs_restart_cancel(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, ABSOLUTE, RESTART, CANCEL, 5, 100 * MSEC);
+}
+
+static void timer_sched_abs_wait_3sec(void)
+{
+ timer_single_shot(ODP_QUEUE_TYPE_SCHED, ABSOLUTE, START, TIMEOUT, 30, 110 * MSEC);
+}
+
+static void timer_pool_current_tick(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_pool_param_t tp_param;
+ odp_timer_pool_t tp;
+ uint64_t t1, t2, ticks, min, max, limit;
+ uint64_t nsec = 100 * ODP_TIME_MSEC_IN_NS;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_src, &capa) == 0);
+
+ /* Highest resolution */
+ odp_timer_pool_param_init(&tp_param);
+ tp_param.res_hz = capa.max_res.res_hz;
+ tp_param.min_tmo = capa.max_res.min_tmo;
+ tp_param.max_tmo = capa.max_res.max_tmo;
+ tp_param.num_timers = 100;
+ tp_param.clk_src = clk_src;
+
+ tp = odp_timer_pool_create("cur_tick", &tp_param);
+ CU_ASSERT_FATAL(tp != ODP_TIMER_POOL_INVALID);
+
+ /* API to be deprecated */
+ odp_timer_pool_start();
+
+ /* Allow +-10% error margin */
+ min = odp_timer_ns_to_tick(tp, 0.9 * nsec);
+ max = odp_timer_ns_to_tick(tp, 1.1 * nsec);
+
+ t1 = odp_timer_current_tick(tp);
+
+ odp_time_wait_ns(nsec);
+
+ t2 = odp_timer_current_tick(tp);
+
+ ticks = t2 - t1;
+
+ CU_ASSERT(t2 >= t1);
+ CU_ASSERT(ticks >= min);
+ CU_ASSERT(ticks <= max);
+
+ /* Timer tick (or tick in nsec) should not wrap in at least 10 years from ODP start.
+ * Ignoring delay from start up and other test cases, which should be few seconds. */
+ limit = 10 * YEAR_IN_NS;
+ nsec = odp_timer_tick_to_ns(tp, t1);
+ CU_ASSERT(UINT64_MAX - nsec > limit);
+ CU_ASSERT(UINT64_MAX - t1 > odp_timer_ns_to_tick(tp, limit));
+
+ printf("\nClock source %i\n", clk_src);
+ printf(" Time nsec: %" PRIu64 "\n", nsec);
+ printf(" Measured ticks: %" PRIu64 "\n", ticks);
+ printf(" Expected ticks: %" PRIu64 "\n", odp_timer_ns_to_tick(tp, nsec));
+
+ odp_timer_pool_destroy(tp);
+}
+
+static void timer_pool_sample_ticks(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_pool_param_t tp_param;
+ odp_timer_pool_t tp[2];
+ uint64_t t1[2], t2[2], ticks[2], min[2], max[2];
+ uint64_t clk_count[2] = {0};
+ odp_timer_clk_src_t clk_1 = ODP_CLOCK_DEFAULT;
+ odp_timer_clk_src_t clk_2 = test_global->clk_src;
+ uint64_t nsec = 100 * ODP_TIME_MSEC_IN_NS;
+
+ /* Highest resolution */
+ odp_timer_pool_param_init(&tp_param);
+ tp_param.num_timers = 100;
+
+ /* First timer pool: default clock source */
+ memset(&capa, 0, sizeof(capa));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_1, &capa) == 0);
+ tp_param.clk_src = clk_1;
+ tp_param.res_hz = capa.max_res.res_hz;
+ tp_param.min_tmo = capa.max_res.min_tmo;
+ tp_param.max_tmo = capa.max_res.max_tmo;
+
+ tp[0] = odp_timer_pool_create("timer_pool_0", &tp_param);
+ CU_ASSERT_FATAL(tp[0] != ODP_TIMER_POOL_INVALID);
+
+ /* Second timer pool: another clock source */
+ memset(&capa, 0, sizeof(capa));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_2, &capa) == 0);
+ tp_param.clk_src = clk_2;
+ tp_param.res_hz = capa.max_res.res_hz;
+ tp_param.min_tmo = capa.max_res.min_tmo;
+ tp_param.max_tmo = capa.max_res.max_tmo;
+
+ tp[1] = odp_timer_pool_create("timer_pool_1", &tp_param);
+ CU_ASSERT_FATAL(tp[1] != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(tp, 2) == 2);
+
+ /* Allow +-10% error margin */
+ min[0] = odp_timer_ns_to_tick(tp[0], 0.9 * nsec);
+ max[0] = odp_timer_ns_to_tick(tp[0], 1.1 * nsec);
+ min[1] = odp_timer_ns_to_tick(tp[1], 0.9 * nsec);
+ max[1] = odp_timer_ns_to_tick(tp[1], 1.1 * nsec);
+
+ CU_ASSERT_FATAL(odp_timer_sample_ticks(tp, t1, NULL, 2) == 0);
+
+ odp_time_wait_ns(nsec);
+
+ CU_ASSERT_FATAL(odp_timer_sample_ticks(tp, t2, clk_count, 2) == 0);
+
+ ticks[0] = t2[0] - t1[0];
+ ticks[1] = t2[1] - t1[1];
+
+ CU_ASSERT(t2[0] >= t1[0]);
+ CU_ASSERT(t2[1] >= t1[1]);
+ CU_ASSERT(ticks[0] >= min[0]);
+ CU_ASSERT(ticks[1] >= min[1]);
+ CU_ASSERT(ticks[0] <= max[0]);
+ CU_ASSERT(ticks[1] <= max[1]);
+
+ printf("\nClock source: %i, %i\n", clk_1, clk_2);
+ printf(" Time nsec: %" PRIu64 "\n", nsec);
+ printf(" Measured ticks: %" PRIu64 ", %" PRIu64 "\n", ticks[0], ticks[1]);
+ printf(" Expected ticks: %" PRIu64 ", %" PRIu64 "\n",
+ odp_timer_ns_to_tick(tp[0], nsec), odp_timer_ns_to_tick(tp[1], nsec));
+ printf(" T2 tick: %" PRIu64 ", %" PRIu64 "\n", t2[0], t2[1]);
+ printf(" Clk count: %" PRIu64 ", %" PRIu64 "\n", clk_count[0], clk_count[1]);
+
+ odp_timer_pool_destroy(tp[0]);
+ odp_timer_pool_destroy(tp[1]);
+}
+
+static void timer_pool_tick_info(void)
+{
+ odp_timer_capability_t capa;
+ odp_timer_pool_param_t tp_param;
+ odp_timer_pool_t tp;
+ odp_timer_pool_info_t info;
+ uint64_t ticks_per_sec;
+ double tick_hz, tick_nsec, tick_to_nsec, tick_low;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_src, &capa) == 0);
+
+ /* Highest resolution */
+ odp_timer_pool_param_init(&tp_param);
+ tp_param.res_hz = capa.max_res.res_hz;
+ tp_param.min_tmo = capa.max_res.min_tmo;
+ tp_param.max_tmo = capa.max_res.max_tmo;
+ tp_param.num_timers = 100;
+ tp_param.priv = 0;
+ tp_param.clk_src = clk_src;
+
+ tp = odp_timer_pool_create("tick_info_tp", &tp_param);
+ CU_ASSERT_FATAL(tp != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ memset(&info, 0, sizeof(odp_timer_pool_info_t));
+ CU_ASSERT_FATAL(odp_timer_pool_info(tp, &info) == 0);
+
+ /* Tick frequency in hertz. Allow 1 hz rounding error between odp_timer_ns_to_tick()
+ * and tick_info. */
+ ticks_per_sec = odp_timer_ns_to_tick(tp, ODP_TIME_SEC_IN_NS);
+ tick_hz = odp_fract_u64_to_dbl(&info.tick_info.freq);
+
+ CU_ASSERT(((double)(ticks_per_sec - 1)) <= tick_hz);
+ CU_ASSERT(((double)(ticks_per_sec + 1)) >= tick_hz);
+
+ /* Tick frequency must be the same or higher that resolution */
+ CU_ASSERT(tick_hz >= tp_param.res_hz);
+
+ printf("\nClock source %i\n", clk_src);
+ printf(" Ticks per second: %" PRIu64 "\n", ticks_per_sec);
+ printf(" Tick info freq: %" PRIu64 " + %" PRIu64 " / %" PRIu64 "\n",
+ info.tick_info.freq.integer,
+ info.tick_info.freq.numer,
+ info.tick_info.freq.denom);
+ printf(" Tick info freq dbl: %f\n", tick_hz);
+
+ /* One tick on nsec. For better resolution, convert 1000 ticks (and use double)
+ * instead of one tick. Allow 1 nsec rounding error between odp_timer_tick_to_ns()
+ * and tick_info. */
+ tick_to_nsec = odp_timer_tick_to_ns(tp, 1000) / 1000.0;
+ tick_nsec = odp_fract_u64_to_dbl(&info.tick_info.nsec);
+ tick_low = tick_to_nsec - 1.0;
+ if (tick_to_nsec < 1.0)
+ tick_low = 0.0;
+
+ CU_ASSERT(tick_low <= tick_nsec);
+ CU_ASSERT((tick_to_nsec + 1.0) >= tick_nsec);
+
+ printf(" Tick in nsec: %f\n", tick_to_nsec);
+ printf(" Tick info nsec: %" PRIu64 " + %" PRIu64 " / %" PRIu64 "\n",
+ info.tick_info.nsec.integer,
+ info.tick_info.nsec.numer,
+ info.tick_info.nsec.denom);
+ printf(" Tick info nsec dbl: %f\n", tick_nsec);
+
+ /* One tick in source clock cycles. Depending on clock source it may be zero.
+ * Print the values to have a reference to the fields. */
+ printf(" Tick info clk cycles: %" PRIu64 " + %" PRIu64 " / %" PRIu64 "\n",
+ info.tick_info.clk_cycle.integer,
+ info.tick_info.clk_cycle.numer,
+ info.tick_info.clk_cycle.denom);
+
+ odp_timer_pool_destroy(tp);
+}
+
+static void timer_test_event_type(odp_queue_type_t queue_type,
+ odp_event_type_t event_type, int rounds)
+{
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_pool_t timer_pool;
+ odp_timer_start_t start_param;
+ odp_queue_t queue;
+ odp_timeout_t tmo;
+ odp_buffer_t buf;
+ odp_packet_t pkt;
+ odp_event_t ev;
+ odp_time_t t1, t2;
+ uint64_t period_ns, period_tick, duration_ns;
+ int i, ret, num_tmo;
+ const char *user_ctx = "User context";
+ int test_print = 0;
+ int num = 5;
+ odp_timer_t timer[num];
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ odp_timer_pool_param_init(&timer_param);
+ timer_param.res_ns = global_mem->param.res_ns;
+ timer_param.min_tmo = global_mem->param.min_tmo;
+ period_ns = 2 * global_mem->param.min_tmo;
+ timer_param.max_tmo = global_mem->param.max_tmo;
+ timer_param.num_timers = num;
+ timer_param.clk_src = clk_src;
+
+ timer_pool = odp_timer_pool_create("timer_pool", &timer_param);
+ if (timer_pool == ODP_TIMER_POOL_INVALID)
+ CU_FAIL_FATAL("Timer pool create failed");
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&timer_pool, 1) == 1);
+
+ odp_pool_param_init(&pool_param);
+
+ if (event_type == ODP_EVENT_BUFFER) {
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = num;
+ } else if (event_type == ODP_EVENT_PACKET) {
+ pool_param.type = ODP_POOL_PACKET;
+ pool_param.pkt.num = num;
+ } else if (event_type == ODP_EVENT_TIMEOUT) {
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = num;
+ test_print = 1;
+ } else {
+ CU_FAIL("Bad event_type");
+ return;
+ }
+
+ pool = odp_pool_create("timeout_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+
+ queue = odp_queue_create("timeout_queue", &queue_param);
+ if (queue == ODP_QUEUE_INVALID)
+ CU_FAIL_FATAL("Queue create failed");
+
+ period_tick = odp_timer_ns_to_tick(timer_pool, period_ns);
+ duration_ns = num * period_ns;
+
+ ODPH_DBG("\nTimer pool parameters:\n");
+ ODPH_DBG(" res_ns %" PRIu64 "\n", timer_param.res_ns);
+ ODPH_DBG(" min_tmo %" PRIu64 "\n", timer_param.min_tmo);
+ ODPH_DBG(" max_tmo %" PRIu64 "\n", timer_param.max_tmo);
+ ODPH_DBG(" period_ns %" PRIu64 "\n", period_ns);
+ ODPH_DBG(" period_tick %" PRIu64 "\n", period_tick);
+ ODPH_DBG(" duration_ns %" PRIu64 "\n", duration_ns);
+ ODPH_DBG(" user_ptr %p\n\n", (const void *)user_ctx);
+
+ for (i = 0; i < num; i++) {
+ timer[i] = odp_timer_alloc(timer_pool, queue, user_ctx);
+ CU_ASSERT_FATAL(timer[i] != ODP_TIMER_INVALID);
+ }
+
+ for (int round = 0; round < rounds; round++) {
+ for (i = 0; i < num; i++) {
+ if (event_type == ODP_EVENT_BUFFER) {
+ buf = odp_buffer_alloc(pool);
+ ev = odp_buffer_to_event(buf);
+ } else if (event_type == ODP_EVENT_PACKET) {
+ pkt = odp_packet_alloc(pool, 10);
+ ev = odp_packet_to_event(pkt);
+ } else {
+ tmo = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(tmo);
+ }
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = (i + 1) * period_tick;
+ start_param.tmo_ev = ev;
+
+ ret = odp_timer_start(timer[i], &start_param);
+
+ if (ret == ODP_TIMER_TOO_NEAR)
+ ODPH_DBG("Timer set failed. Too near %i.\n", i);
+ else if (ret == ODP_TIMER_TOO_FAR)
+ ODPH_DBG("Timer set failed. Too far %i.\n", i);
+ else if (ret == ODP_TIMER_FAIL)
+ ODPH_DBG("Timer set failed %i\n", i);
+
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+ }
+
+ if (test_print) {
+ printf("\n");
+ odp_timer_pool_print(timer_pool);
+ odp_timer_print(timer[0]);
+ }
+
+ ev = ODP_EVENT_INVALID;
+ num_tmo = 0;
+ t1 = odp_time_local();
+
+ /* Wait for timers. Make sure that scheduler context is not held when
+ * exiting the loop. */
+ do {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ t2 = odp_time_local();
+ if (odp_time_diff_ns(t2, t1) > (10 * duration_ns))
+ break;
+
+ continue;
+ }
+
+ CU_ASSERT(odp_event_type(ev) == event_type);
+
+ if (test_print) {
+ test_print = 0;
+ tmo = odp_timeout_from_event(ev);
+ odp_timeout_print(tmo);
+ printf("\n");
+ }
+
+ odp_event_free(ev);
+ num_tmo++;
+
+ } while (num_tmo < num || ev != ODP_EVENT_INVALID);
+
+ CU_ASSERT(num_tmo == num);
+ }
+
+ for (i = 0; i < num; i++)
+ CU_ASSERT(odp_timer_free(timer[i]) == 0);
+
+ odp_timer_pool_destroy(timer_pool);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_tmo_event_plain(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_PLAIN, ODP_EVENT_TIMEOUT, 1);
+}
+
+static void timer_test_tmo_event_sched(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_TIMEOUT, 1);
+}
+
+static void timer_test_buf_event_plain(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_PLAIN, ODP_EVENT_BUFFER, 1);
+}
+
+static void timer_test_buf_event_sched(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_BUFFER, 1);
+}
+
+static void timer_test_pkt_event_plain(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_PLAIN, ODP_EVENT_PACKET, 1);
+}
+
+static void timer_test_pkt_event_sched(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_PACKET, 1);
+}
+
+static void timer_test_tmo_event_reuse(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_TIMEOUT, 2);
+}
+
+static void timer_test_buf_event_reuse(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_BUFFER, 2);
+}
+
+static void timer_test_pkt_event_reuse(void)
+{
+ timer_test_event_type(ODP_QUEUE_TYPE_SCHED, ODP_EVENT_PACKET, 2);
+}
+
+static void timer_test_queue_type(odp_queue_type_t queue_type, int priv, int exp_relax)
+{
+ odp_pool_t pool;
+ const int num = 10;
+ odp_timeout_t tmo;
+ odp_event_t ev;
+ odp_queue_param_t queue_param;
+ odp_timer_pool_param_t tparam;
+ odp_timer_pool_t tp;
+ odp_timer_start_t start_param;
+ odp_queue_t queue;
+ odp_timer_t tim;
+ int i, ret, num_tmo;
+ uint64_t tick_base, tick, nsec_base, nsec;
+ uint64_t res_ns, period_ns, period_tick, test_period;
+ uint64_t diff_test;
+ odp_pool_param_t params;
+ odp_time_t t0, t1;
+ odp_timer_t timer[num];
+ uint64_t target_tick[num];
+ uint64_t target_nsec[num];
+ void *user_ptr[num];
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = num;
+
+ pool = odp_pool_create("timeout_pool", &params);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ res_ns = global_mem->param.res_ns;
+
+ odp_timer_pool_param_init(&tparam);
+ tparam.res_ns = global_mem->param.res_ns;
+ tparam.min_tmo = global_mem->param.min_tmo;
+ tparam.max_tmo = global_mem->param.max_tmo;
+ tparam.num_timers = num + 1;
+ tparam.priv = priv;
+ tparam.clk_src = clk_src;
+
+ if (exp_relax)
+ tparam.exp_mode = ODP_TIMER_EXP_RELAXED;
+
+ ODPH_DBG("\nTimer pool parameters:\n");
+ ODPH_DBG(" res_ns %" PRIu64 "\n", tparam.res_ns);
+ ODPH_DBG(" min_tmo %" PRIu64 "\n", tparam.min_tmo);
+ ODPH_DBG(" max_tmo %" PRIu64 "\n", tparam.max_tmo);
+
+ tp = odp_timer_pool_create("timer_pool", &tparam);
+ if (tp == ODP_TIMER_POOL_INVALID)
+ CU_FAIL_FATAL("Timer pool create failed");
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ odp_queue_param_init(&queue_param);
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ queue = odp_queue_create("timer_queue", &queue_param);
+ if (queue == ODP_QUEUE_INVALID)
+ CU_FAIL_FATAL("Queue create failed");
+
+ period_ns = 4 * tparam.min_tmo;
+ period_tick = odp_timer_ns_to_tick(tp, period_ns);
+ test_period = num * period_ns;
+
+ ODPH_DBG(" period_ns %" PRIu64 "\n", period_ns);
+ ODPH_DBG(" period_tick %" PRIu64 "\n\n", period_tick);
+
+ tick_base = odp_timer_current_tick(tp);
+ t0 = odp_time_local();
+ t1 = t0;
+ nsec_base = odp_time_to_ns(t0);
+
+ for (i = 0; i < num; i++) {
+ tmo = odp_timeout_alloc(pool);
+ CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID);
+ ev = odp_timeout_to_event(tmo);
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+
+ user_ptr[i] = (void *)(uintptr_t)i;
+ tim = odp_timer_alloc(tp, queue, user_ptr[i]);
+ CU_ASSERT_FATAL(tim != ODP_TIMER_INVALID);
+ timer[i] = tim;
+
+ tick = tick_base + ((i + 1) * period_tick);
+
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick = tick;
+ start_param.tmo_ev = ev;
+
+ ret = odp_timer_start(tim, &start_param);
+ target_tick[i] = tick;
+ target_nsec[i] = nsec_base + ((i + 1) * period_ns);
+
+ ODPH_DBG("abs timer tick %" PRIu64 "\n", tick);
+ if (ret == ODP_TIMER_TOO_NEAR)
+ ODPH_DBG("Timer set failed. Too near %" PRIu64 ".\n", tick);
+ else if (ret == ODP_TIMER_TOO_FAR)
+ ODPH_DBG("Timer set failed. Too far %" PRIu64 ".\n", tick);
+ else if (ret == ODP_TIMER_FAIL)
+ ODPH_DBG("Timer set failed %" PRIu64 "\n", tick);
+
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+ }
+
+ num_tmo = 0;
+
+ do {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ t1 = odp_time_local();
+ nsec = odp_time_to_ns(t1);
+ diff_test = nsec - nsec_base;
+
+ if (ev != ODP_EVENT_INVALID) {
+ uint64_t target;
+
+ tmo = odp_timeout_from_event(ev);
+ tim = odp_timeout_timer(tmo);
+ tick = odp_timeout_tick(tmo);
+ target = target_nsec[num_tmo];
+
+ CU_ASSERT(timer[num_tmo] == tim);
+ CU_ASSERT(target_tick[num_tmo] == tick);
+ CU_ASSERT(user_ptr[num_tmo] == odp_timeout_user_ptr(tmo));
+
+ CU_ASSERT(nsec < (target + (5 * res_ns)));
+
+ if (exp_relax) {
+ CU_ASSERT(nsec > (target - (5 * res_ns)));
+ } else {
+ /* Timeout must not arrive before the current time has passed
+ * the target time (in timer ticks). */
+ CU_ASSERT(target_tick[num_tmo] <= odp_timer_current_tick(tp));
+
+ /* Timeout should not arrive before the target wall clock time.
+ * However, allow small drift or error between wall clock and
+ * timer. */
+ CU_ASSERT(nsec > (target - res_ns));
+ }
+
+ ODPH_DBG("timeout tick %" PRIu64 ", nsec %" PRIu64 ", "
+ "target nsec %" PRIu64 ", diff nsec %" PRIi64 "\n",
+ tick, nsec, target, (int64_t)(nsec - target));
+
+ odp_timeout_free(tmo);
+ CU_ASSERT(odp_timer_free(tim) == 0);
+
+ num_tmo++;
+ }
+
+ } while (diff_test < (2 * test_period) && num_tmo < num);
+
+ ODPH_DBG("test period %" PRIu64 "\n", diff_test);
+
+ CU_ASSERT(num_tmo == num);
+ CU_ASSERT(diff_test > (test_period - period_ns));
+ CU_ASSERT(diff_test < (test_period + period_ns));
+
+ /* Reset scheduler context for the next test case */
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ odp_schedule_pause();
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ CU_FAIL("Drop extra event\n");
+ odp_event_free(ev);
+ }
+ odp_schedule_resume();
+ }
+
+ odp_timer_pool_destroy(tp);
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_plain_queue(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_PLAIN, 0, 0);
+}
+
+static void timer_test_sched_queue(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_SCHED, 0, 0);
+}
+
+static void timer_test_plain_queue_priv(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_PLAIN, PRIV, 0);
+}
+
+static void timer_test_sched_queue_priv(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_SCHED, PRIV, 0);
+}
+
+static void timer_test_plain_queue_exp_relax(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_PLAIN, 0, EXP_RELAX);
+}
+
+static void timer_test_sched_queue_exp_relax(void)
+{
+ timer_test_queue_type(ODP_QUEUE_TYPE_SCHED, 0, EXP_RELAX);
+}
+
+static void timer_test_cancel(void)
+{
+ odp_pool_t pool;
+ odp_pool_param_t params;
+ odp_timer_pool_param_t tparam;
+ odp_queue_param_t queue_param;
+ odp_timer_capability_t capa;
+ odp_timer_pool_t tp;
+ odp_timer_start_t start_param;
+ odp_queue_t queue;
+ odp_timer_t tim;
+ odp_event_t ev;
+ odp_timeout_t tmo;
+ odp_timer_retval_t rc;
+ int ret;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&capa, 0, sizeof(capa));
+ ret = odp_timer_capability(clk_src, &capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = 1;
+
+ pool = odp_pool_create("tmo_pool_for_cancel", &params);
+
+ if (pool == ODP_POOL_INVALID)
+ CU_FAIL_FATAL("Timeout pool create failed");
+
+ odp_timer_pool_param_init(&tparam);
+ tparam.res_ns = global_mem->param.res_ns;
+ tparam.min_tmo = global_mem->param.min_tmo;
+ tparam.max_tmo = global_mem->param.max_tmo;
+ tparam.num_timers = 1;
+ tparam.priv = 0;
+ tparam.clk_src = clk_src;
+ tp = odp_timer_pool_create(NULL, &tparam);
+ if (tp == ODP_TIMER_POOL_INVALID)
+ CU_FAIL_FATAL("Timer pool create failed");
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ odp_queue_param_init(&queue_param);
+ if (capa.queue_type_plain) {
+ queue_param.type = ODP_QUEUE_TYPE_PLAIN;
+ } else if (capa.queue_type_sched) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+
+ queue = odp_queue_create("timer_queue", &queue_param);
+ if (queue == ODP_QUEUE_INVALID)
+ CU_FAIL_FATAL("Queue create failed");
+
+ tim = odp_timer_alloc(tp, queue, USER_PTR);
+ if (tim == ODP_TIMER_INVALID)
+ CU_FAIL_FATAL("Failed to allocate timer");
+ ODPH_DBG("Timer handle: %" PRIu64 "\n", odp_timer_to_u64(tim));
+
+ ev = odp_timeout_to_event(odp_timeout_alloc(pool));
+ if (ev == ODP_EVENT_INVALID)
+ CU_FAIL_FATAL("Failed to allocate timeout");
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = odp_timer_ns_to_tick(tp, tparam.max_tmo / 2);
+ start_param.tmo_ev = ev;
+
+ rc = odp_timer_start(tim, &start_param);
+ if (rc != ODP_TIMER_SUCCESS)
+ CU_FAIL_FATAL("Failed to set timer (relative time)");
+
+ ev = ODP_EVENT_INVALID;
+ if (odp_timer_cancel(tim, &ev) != ODP_TIMER_SUCCESS)
+ CU_FAIL_FATAL("Failed to cancel timer (relative time)");
+
+ if (ev == ODP_EVENT_INVALID)
+ CU_FAIL_FATAL("Cancel did not return event");
+
+ tmo = odp_timeout_from_event(ev);
+ if (tmo == ODP_TIMEOUT_INVALID)
+ CU_FAIL_FATAL("Cancel did not return timeout");
+ ODPH_DBG("Timeout handle: %" PRIu64 "\n", odp_timeout_to_u64(tmo));
+
+ if (odp_timeout_timer(tmo) != tim)
+ CU_FAIL("Cancel invalid tmo.timer");
+
+ if (odp_timeout_user_ptr(tmo) != USER_PTR)
+ CU_FAIL("Cancel invalid tmo.user_ptr");
+
+ odp_timeout_free(tmo);
+
+ CU_ASSERT_FATAL(odp_timer_free(tim) == 0);
+
+ odp_timer_pool_destroy(tp);
+
+ if (odp_queue_destroy(queue) != 0)
+ CU_FAIL_FATAL("Failed to destroy queue");
+
+ if (odp_pool_destroy(pool) != 0)
+ CU_FAIL_FATAL("Failed to destroy pool");
+}
+
+static void timer_test_tmo_limit(odp_queue_type_t queue_type,
+ int max_res, int min)
+{
+ odp_timer_capability_t timer_capa;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_pool_t timer_pool;
+ odp_timer_start_t start_param;
+ odp_queue_t queue;
+ odp_timeout_t tmo;
+ odp_event_t ev;
+ odp_time_t t1, t2;
+ uint64_t res_ns, min_tmo, max_tmo;
+ uint64_t tmo_ns, tmo_tick, diff_ns, max_wait;
+ int i, ret, num_tmo;
+ int num = 5;
+ odp_timer_t timer[num];
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&timer_capa, 0, sizeof(timer_capa));
+ ret = odp_timer_capability(clk_src, &timer_capa);
+ CU_ASSERT_FATAL(ret == 0);
+
+ if (max_res) {
+ /* Maximum resolution parameters */
+ res_ns = timer_capa.max_res.res_ns;
+ min_tmo = timer_capa.max_res.min_tmo;
+ max_tmo = timer_capa.max_res.max_tmo;
+ } else {
+ /* Maximum timeout parameters */
+ res_ns = timer_capa.max_tmo.res_ns;
+ min_tmo = timer_capa.max_tmo.min_tmo;
+ max_tmo = timer_capa.max_tmo.max_tmo;
+ }
+
+ odp_timer_pool_param_init(&timer_param);
+ timer_param.res_ns = res_ns;
+ timer_param.min_tmo = min_tmo;
+ timer_param.max_tmo = max_tmo;
+ timer_param.num_timers = num;
+ timer_param.clk_src = clk_src;
+
+ timer_pool = odp_timer_pool_create("timer_pool", &timer_param);
+ if (timer_pool == ODP_TIMER_POOL_INVALID)
+ CU_FAIL_FATAL("Timer pool create failed");
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&timer_pool, 1) == 1);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = num;
+
+ pool = odp_pool_create("timeout_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+
+ queue = odp_queue_create("timeout_queue", &queue_param);
+ if (queue == ODP_QUEUE_INVALID)
+ CU_FAIL_FATAL("Queue create failed");
+
+ if (min)
+ tmo_ns = min_tmo;
+ else
+ tmo_ns = max_tmo;
+
+ tmo_tick = odp_timer_ns_to_tick(timer_pool, tmo_ns);
+ /* Min_tmo maybe zero. Wait min timeouts at least 20ms + resolution */
+ max_wait = (20 * ODP_TIME_MSEC_IN_NS + res_ns + 10 * tmo_ns);
+
+ ODPH_DBG("\nTimer pool parameters:\n");
+ ODPH_DBG(" res_ns %" PRIu64 "\n", timer_param.res_ns);
+ ODPH_DBG(" min_tmo %" PRIu64 "\n", timer_param.min_tmo);
+ ODPH_DBG(" max_tmo %" PRIu64 "\n", timer_param.max_tmo);
+ ODPH_DBG(" tmo_ns %" PRIu64 "\n", tmo_ns);
+ ODPH_DBG(" tmo_tick %" PRIu64 "\n\n", tmo_tick);
+
+ if (min) {
+ /*
+ * Prevent the test from taking too long by asserting that the
+ * timeout is reasonably short.
+ */
+ CU_ASSERT_FATAL(tmo_ns < 5 * ODP_TIME_SEC_IN_NS);
+ }
+
+ for (i = 0; i < num; i++) {
+ timer[i] = odp_timer_alloc(timer_pool, queue, NULL);
+ CU_ASSERT_FATAL(timer[i] != ODP_TIMER_INVALID);
+ }
+
+ num_tmo = 0;
+
+ for (i = 0; i < num; i++) {
+ tmo = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(tmo);
+
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+
+ t1 = odp_time_local();
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tmo_tick;
+ start_param.tmo_ev = ev;
+
+ ret = odp_timer_start(timer[i], &start_param);
+
+ if (ret == ODP_TIMER_TOO_NEAR)
+ ODPH_DBG("Timer set failed. Too near %i.\n", i);
+ else if (ret == ODP_TIMER_TOO_FAR)
+ ODPH_DBG("Timer set failed. Too late %i.\n", i);
+ else if (ret == ODP_TIMER_FAIL)
+ ODPH_DBG("Timer set failed %i\n", i);
+
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+
+ if (min) {
+ /* Min timeout - wait for events */
+ int break_loop = 0;
+
+ while (1) {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(NULL,
+ ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+
+ if (ev != ODP_EVENT_INVALID) {
+ odp_event_free(ev);
+ num_tmo++;
+ break_loop = 1;
+ ODPH_DBG("Timeout [%i]: %" PRIu64 " "
+ "nsec\n", i, diff_ns);
+ continue;
+ }
+
+ /* Ensure that schedule context is free */
+ if (break_loop)
+ break;
+
+ /* Give up after waiting max wait time */
+ if (diff_ns > max_wait)
+ break;
+ }
+ } else {
+ /* Max timeout - cancel events */
+ ev = ODP_EVENT_INVALID;
+
+ ret = odp_timer_cancel(timer[i], &ev);
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+
+ CU_ASSERT(ret == ODP_TIMER_SUCCESS);
+ CU_ASSERT(ev != ODP_EVENT_INVALID);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ ODPH_DBG("Cancelled [%i]: %" PRIu64 " nsec\n", i,
+ diff_ns);
+ }
+ }
+
+ if (min)
+ CU_ASSERT(num_tmo == num);
+
+ for (i = 0; i < num; i++)
+ CU_ASSERT(odp_timer_free(timer[i]) == 0);
+
+ odp_timer_pool_destroy(timer_pool);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_max_res_min_tmo_plain(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_PLAIN, 1, 1);
+}
+
+static void timer_test_max_res_min_tmo_sched(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_SCHED, 1, 1);
+}
+
+static void timer_test_max_res_max_tmo_plain(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_PLAIN, 1, 0);
+}
+
+static void timer_test_max_res_max_tmo_sched(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_SCHED, 1, 0);
+}
+
+static void timer_test_max_tmo_min_tmo_plain(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_PLAIN, 0, 1);
+}
+
+static void timer_test_max_tmo_min_tmo_sched(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_SCHED, 0, 1);
+}
+
+static void timer_test_max_tmo_max_tmo_plain(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_PLAIN, 0, 0);
+}
+
+static void timer_test_max_tmo_max_tmo_sched(void)
+{
+ timer_test_tmo_limit(ODP_QUEUE_TYPE_SCHED, 0, 0);
+}
+
+/* Handle a received (timeout) event */
+static void handle_tmo(odp_event_t ev, bool stale, uint64_t prev_tick)
+{
+ odp_event_subtype_t subtype;
+ odp_timeout_t tmo;
+ odp_timer_t tim;
+ uint64_t tick;
+ struct test_timer *ttp;
+
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); /* Internal error */
+ if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) {
+ /* Not a timeout event */
+ CU_FAIL("Unexpected event type received");
+ return;
+ }
+ if (odp_event_subtype(ev) != ODP_EVENT_NO_SUBTYPE) {
+ /* Not a timeout event */
+ CU_FAIL("Unexpected event subtype received");
+ return;
+ }
+ if (odp_event_types(ev, &subtype) != ODP_EVENT_TIMEOUT) {
+ /* Not a timeout event */
+ CU_FAIL("Unexpected event type received");
+ return;
+ }
+ if (subtype != ODP_EVENT_NO_SUBTYPE) {
+ /* Not a timeout event */
+ CU_FAIL("Unexpected event subtype received");
+ return;
+ }
+
+ /* Read the metadata from the timeout */
+ tmo = odp_timeout_from_event(ev);
+ tim = odp_timeout_timer(tmo);
+ tick = odp_timeout_tick(tmo);
+ ttp = odp_timeout_user_ptr(tmo);
+
+ if (tim == ODP_TIMER_INVALID)
+ CU_FAIL("odp_timeout_timer() invalid timer");
+
+ if (ttp == NULL) {
+ CU_FAIL("odp_timeout_user_ptr() null user ptr");
+ return;
+ }
+
+ if (ttp->ev2 != ev)
+ CU_FAIL("odp_timeout_user_ptr() wrong user ptr");
+
+ if (ttp->tim != tim)
+ CU_FAIL("odp_timeout_timer() wrong timer");
+
+ if (!stale) {
+#if ODP_DEPRECATED_API
+ if (!odp_timeout_fresh(tmo))
+ CU_FAIL("Wrong status (stale) for fresh timeout");
+#endif
+ /* tmo tick cannot be smaller than pre-calculated tick */
+ if (tick < ttp->tick) {
+ ODPH_DBG("Too small tick: pre-calculated %" PRIu64 " "
+ "timeout %" PRIu64 "\n", ttp->tick, tick);
+ CU_FAIL("odp_timeout_tick() too small tick");
+ }
+
+ if (tick > odp_timer_current_tick(global_mem->tp))
+ CU_FAIL("Timeout delivered early in ODP_TIMER_EXP_AFTER mode");
+
+ if (tick < prev_tick) {
+ ODPH_DBG("Too late tick: %" PRIu64 " prev_tick "
+ "%" PRIu64 "\n", tick, prev_tick);
+ /* We don't report late timeouts using CU_FAIL */
+ odp_atomic_inc_u32(&global_mem->ndelivtoolate);
+ }
+ }
+
+ /* Internal error */
+ CU_ASSERT_FATAL(ttp->ev == ODP_EVENT_INVALID);
+ ttp->ev = ev;
+}
+
+/* Worker thread entrypoint which performs timer alloc/set/cancel/free
+ * tests */
+static int worker_entrypoint(void *arg ODP_UNUSED)
+{
+ int thr = odp_thread_id();
+ uint32_t i, allocated;
+ unsigned seed = thr;
+ odp_queue_t queue;
+ struct test_timer *tt;
+ uint32_t nset;
+ uint64_t tck;
+ uint32_t nrcv;
+ uint32_t nreset;
+ uint32_t ncancel;
+ uint32_t ntoolate;
+ uint32_t ms;
+ uint64_t prev_tick, late_margin, nsec;
+ odp_event_t ev;
+ struct timespec ts;
+ uint32_t nstale;
+ odp_timer_retval_t rc;
+ odp_timer_start_t start_param;
+ odp_timer_pool_t tp = global_mem->tp;
+ odp_pool_t tbp = global_mem->tbp;
+ uint32_t num_timers = global_mem->timers_per_thread;
+ uint64_t min_tmo = global_mem->param.min_tmo;
+ odp_queue_param_t queue_param;
+ odp_thrmask_t thr_mask;
+ odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID;
+ uint64_t sched_tmo;
+ uint64_t res_ns = global_mem->param.res_ns;
+ odp_queue_type_t queue_type = global_mem->test_queue_type;
+
+ odp_queue_param_init(&queue_param);
+ queue_param.type = queue_type;
+
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ odp_thrmask_zero(&thr_mask);
+ odp_thrmask_set(&thr_mask, odp_thread_id());
+ group = odp_schedule_group_create(NULL, &thr_mask);
+ if (group == ODP_SCHED_GROUP_INVALID)
+ CU_FAIL_FATAL("Schedule group create failed");
+
+ queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
+ queue_param.sched.group = group;
+ }
+
+ queue = odp_queue_create("timer_queue", &queue_param);
+ if (queue == ODP_QUEUE_INVALID)
+ CU_FAIL_FATAL("Queue create failed");
+
+ tt = malloc(sizeof(struct test_timer) * num_timers);
+ if (!tt)
+ CU_FAIL_FATAL("malloc failed");
+
+ /* Prepare all timers */
+ for (i = 0; i < num_timers; i++) {
+ tt[i].ev = odp_timeout_to_event(odp_timeout_alloc(tbp));
+ if (tt[i].ev == ODP_EVENT_INVALID) {
+ ODPH_DBG("Failed to allocate timeout ("
+ "%" PRIu32 "/%d)\n", i, num_timers);
+ break;
+ }
+ tt[i].tim = odp_timer_alloc(tp, queue, &tt[i]);
+ if (tt[i].tim == ODP_TIMER_INVALID) {
+ ODPH_DBG("Failed to allocate timer (%" PRIu32 "/%d)\n",
+ i, num_timers);
+ odp_event_free(tt[i].ev);
+ break;
+ }
+ tt[i].ev2 = tt[i].ev;
+ tt[i].tick = TICK_INVALID;
+ }
+ allocated = i;
+ if (allocated == 0)
+ CU_FAIL_FATAL("unable to alloc a timer");
+ odp_atomic_fetch_add_u32(&global_mem->timers_allocated, allocated);
+
+ odp_barrier_wait(&global_mem->test_barrier);
+
+ /* Initial set all timers with a random expiration time */
+ nset = 0;
+ for (i = 0; i < allocated; i++) {
+ nsec = min_tmo + THREE_POINT_THREE_MSEC +
+ (rand_r(&seed) % RANGE_MS) * 1000000ULL;
+ tck = odp_timer_current_tick(tp) +
+ odp_timer_ns_to_tick(tp, nsec);
+
+ start_param.tick_type = ODP_TIMER_TICK_ABS;
+ start_param.tick = tck;
+ start_param.tmo_ev = tt[i].ev;
+ tt[i].ev = ODP_EVENT_INVALID;
+
+ rc = odp_timer_start(tt[i].tim, &start_param);
+ if (rc == ODP_TIMER_TOO_NEAR) {
+ ODPH_ERR("Missed tick, setting timer\n");
+ } else if (rc != ODP_TIMER_SUCCESS) {
+ ODPH_ERR("Failed to set timer: %d\n", rc);
+ CU_FAIL("Failed to set timer");
+ } else {
+ tt[i].tick = tck;
+ nset++;
+ }
+ }
+
+ /* Step through wall time, 1ms at a time and check for expired timers */
+ nrcv = 0;
+ nreset = 0;
+ ncancel = 0;
+ ntoolate = 0;
+ late_margin = odp_timer_ns_to_tick(tp, 2 * res_ns);
+ prev_tick = odp_timer_current_tick(tp);
+
+ for (ms = 0; ms < 7 * RANGE_MS / 10 && allocated > 0; ms++) {
+ while ((ev = queue_type == ODP_QUEUE_TYPE_PLAIN ?
+ odp_queue_deq(queue) :
+ odp_schedule(NULL, ODP_SCHED_NO_WAIT))
+ != ODP_EVENT_INVALID) {
+ /* Allow timeouts to be delivered late_margin ticks late */
+ handle_tmo(ev, false, prev_tick - late_margin);
+ nrcv++;
+ }
+ prev_tick = odp_timer_current_tick(tp);
+ i = rand_r(&seed) % allocated;
+ if (tt[i].ev == ODP_EVENT_INVALID &&
+ (rand_r(&seed) % 2 == 0)) {
+ if (odp_timer_current_tick(tp) >= tt[i].tick)
+ /* Timer just expired. */
+ goto sleep;
+ /* Timer active, cancel it */
+ rc = odp_timer_cancel(tt[i].tim, &tt[i].ev);
+
+ if (rc == ODP_TIMER_SUCCESS) {
+ tt[i].tick = TICK_INVALID;
+ ncancel++;
+ } else if (rc == ODP_TIMER_TOO_NEAR) {
+ /* Cancel failed, timer already expired */
+ ntoolate++;
+ ODPH_DBG("Failed to cancel timer, already expired\n");
+ } else {
+ CU_FAIL_FATAL("Cancel failed");
+ }
+ } else {
+ uint64_t cur_tick;
+ int reset_timer = 0;
+
+ if (tt[i].ev != ODP_EVENT_INVALID) {
+ /* Timer inactive => set */
+ nset++;
+ } else if (odp_timer_current_tick(tp) >= tt[i].tick) {
+ /* Timer just expired. */
+ goto sleep;
+ } else {
+ /* Timer active => reset */
+ nreset++;
+ reset_timer = 1;
+ }
+
+ nsec = min_tmo + THREE_POINT_THREE_MSEC +
+ (rand_r(&seed) % RANGE_MS) * 1000000ULL;
+ tck = odp_timer_ns_to_tick(tp, nsec);
+
+ cur_tick = odp_timer_current_tick(tp);
+
+ start_param.tick_type = ODP_TIMER_TICK_REL;
+ start_param.tick = tck;
+ start_param.tmo_ev = tt[i].ev;
+
+ if (reset_timer)
+ rc = odp_timer_restart(tt[i].tim, &start_param);
+ else
+ rc = odp_timer_start(tt[i].tim, &start_param);
+
+ if (rc == ODP_TIMER_TOO_NEAR) {
+ CU_FAIL("Failed to set timer: TOO NEAR");
+ } else if (rc == ODP_TIMER_TOO_FAR) {
+ CU_FAIL("Failed to set timer: TOO FAR");
+ } else if (rc == ODP_TIMER_FAIL) {
+ /* Set/reset failed, timer already expired */
+ ntoolate++;
+ } else if (rc == ODP_TIMER_SUCCESS) {
+ /* Save expected expiration tick on success */
+ tt[i].tick = cur_tick + tck;
+ /* ODP timer owns the event now */
+ tt[i].ev = ODP_EVENT_INVALID;
+ } else {
+ CU_FAIL("Failed to set timer: bad return code");
+ }
+ }
+sleep:
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000; /* 1ms */
+ if (nanosleep(&ts, NULL) < 0)
+ CU_FAIL_FATAL("nanosleep failed");
+ }
+
+ /* Try to cancel all active timers */
+ nstale = 0;
+ for (i = 0; i < allocated; i++) {
+ if (tt[i].ev != ODP_EVENT_INVALID)
+ continue;
+
+ rc = odp_timer_cancel(tt[i].tim, &tt[i].ev);
+ tt[i].tick = TICK_INVALID;
+
+ if (rc == ODP_TIMER_TOO_NEAR) {
+ /* Cancel too late, timer already expired and timeout enqueued */
+ nstale++;
+ } else if (rc != ODP_TIMER_SUCCESS) {
+ CU_FAIL("Timer cancel failed");
+ }
+ }
+
+ ODPH_DBG("Thread %u: %" PRIu32 " timers set\n", thr, nset);
+ ODPH_DBG("Thread %u: %" PRIu32 " timers reset\n", thr, nreset);
+ ODPH_DBG("Thread %u: %" PRIu32 " timers cancelled\n", thr, ncancel);
+ ODPH_DBG("Thread %u: %" PRIu32 " timers reset/cancelled too late\n",
+ thr, ntoolate);
+ ODPH_DBG("Thread %u: %" PRIu32 " timeouts received\n", thr, nrcv);
+ ODPH_DBG("Thread %u: %" PRIu32 " "
+ "stale timeout(s) after odp_timer_cancel()\n", thr, nstale);
+
+ /* Delay some more to ensure timeouts for expired timers can be
+ * received. Can not use busy loop here to make background timer
+ * thread finish their work. */
+ ts.tv_sec = 0;
+ ts.tv_nsec = (3 * RANGE_MS / 10 + 50) * ODP_TIME_MSEC_IN_NS;
+ if (nanosleep(&ts, NULL) < 0)
+ CU_FAIL_FATAL("nanosleep failed");
+
+ sched_tmo = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS * RANGE_MS);
+ while (nstale != 0) {
+ if (queue_type == ODP_QUEUE_TYPE_PLAIN)
+ ev = odp_queue_deq(queue);
+ else
+ ev = odp_schedule(NULL, sched_tmo);
+ if (ev != ODP_EVENT_INVALID) {
+ handle_tmo(ev, true, 0/*Don't care for stale tmo's*/);
+ nstale--;
+ } else {
+ CU_FAIL("Failed to receive stale timeout");
+ break;
+ }
+ }
+
+ for (i = 0; i < allocated; i++) {
+ if (odp_timer_free(tt[i].tim))
+ CU_FAIL("odp_timer_free");
+ }
+
+ /* Check if there any more (unexpected) events */
+ if (queue_type == ODP_QUEUE_TYPE_PLAIN)
+ ev = odp_queue_deq(queue);
+ else
+ ev = odp_schedule(NULL, sched_tmo);
+ if (ev != ODP_EVENT_INVALID)
+ CU_FAIL("Unexpected event received");
+
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ for (i = 0; i < allocated; i++) {
+ if (tt[i].ev != ODP_EVENT_INVALID)
+ odp_event_free(tt[i].ev);
+ }
+
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ CU_ASSERT(odp_schedule_group_destroy(group) == 0);
+
+ free(tt);
+ ODPH_DBG("Thread %u: exiting\n", thr);
+ return CU_get_number_of_failures();
+}
+
+static void timer_test_all(odp_queue_type_t queue_type)
+{
+ int rc;
+ odp_pool_param_t params;
+ odp_timer_pool_param_t tparam;
+ odp_timer_pool_info_t tpinfo;
+ uint64_t ns, tick, ns2;
+ uint64_t res_ns, min_tmo, max_tmo;
+ uint32_t timers_allocated;
+ odp_pool_capability_t pool_capa;
+ odp_timer_capability_t timer_capa;
+ odp_schedule_capability_t sched_capa;
+ odp_pool_t tbp;
+ odp_timer_pool_t tp;
+ uint32_t num_timers;
+ uint32_t num_workers;
+ int timers_per_thread;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ CU_ASSERT_FATAL(odp_schedule_capability(&sched_capa) == 0);
+ /* Reserve at least one core for running other processes so the timer
+ * test hopefully can run undisturbed and thus get better timing
+ * results. */
+ num_workers = odp_cpumask_default_worker(NULL, 0);
+
+ /* force to max CPU count */
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ if (queue_type == ODP_QUEUE_TYPE_SCHED &&
+ num_workers > sched_capa.max_groups)
+ num_workers = sched_capa.max_groups;
+
+ /* On a single-CPU machine run at least one thread */
+ if (num_workers < 1)
+ num_workers = 1;
+
+ num_timers = num_workers * NTIMERS;
+ CU_ASSERT_FATAL(!odp_timer_capability(clk_src, &timer_capa));
+ if (timer_capa.max_timers && timer_capa.max_timers < num_timers)
+ num_timers = timer_capa.max_timers;
+
+ CU_ASSERT_FATAL(!odp_pool_capability(&pool_capa));
+ if (pool_capa.tmo.max_num && num_timers > pool_capa.tmo.max_num)
+ num_timers = pool_capa.tmo.max_num;
+
+ /* Create timeout pools */
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_TIMEOUT;
+ params.tmo.num = num_timers;
+
+ timers_per_thread = (num_timers / num_workers) - EXTRA_TIMERS;
+ global_mem->timers_per_thread = timers_per_thread > 1 ?
+ timers_per_thread : 1;
+
+ global_mem->tbp = odp_pool_create("tmo_pool", &params);
+ if (global_mem->tbp == ODP_POOL_INVALID)
+ CU_FAIL_FATAL("Timeout pool create failed");
+ tbp = global_mem->tbp;
+
+ /* Create a timer pool */
+ res_ns = global_mem->param.res_ns;
+ max_tmo = global_mem->param.max_tmo;
+ min_tmo = global_mem->param.min_tmo;
+
+ odp_timer_pool_param_init(&tparam);
+ tparam.res_ns = res_ns;
+ tparam.min_tmo = min_tmo;
+ tparam.max_tmo = max_tmo;
+ tparam.num_timers = num_timers;
+ tparam.priv = 0;
+ tparam.clk_src = clk_src;
+ global_mem->tp = odp_timer_pool_create(NAME, &tparam);
+ if (global_mem->tp == ODP_TIMER_POOL_INVALID)
+ CU_FAIL_FATAL("Timer pool create failed");
+ tp = global_mem->tp;
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&tp, 1) == 1);
+
+ if (odp_timer_pool_info(tp, &tpinfo) != 0)
+ CU_FAIL("odp_timer_pool_info");
+ CU_ASSERT(strcmp(tpinfo.name, NAME) == 0);
+ CU_ASSERT(tpinfo.param.res_ns == res_ns);
+ CU_ASSERT(tpinfo.param.min_tmo == min_tmo);
+ CU_ASSERT(tpinfo.param.max_tmo == max_tmo);
+ CU_ASSERT(strcmp(tpinfo.name, NAME) == 0);
+
+ ODPH_DBG("Timer pool handle: %" PRIu64 "\n", odp_timer_pool_to_u64(tp));
+ ODPH_DBG("Resolution: %" PRIu64 "\n", tparam.res_ns);
+ ODPH_DBG("Min timeout: %" PRIu64 "\n", tparam.min_tmo);
+ ODPH_DBG("Max timeout: %" PRIu64 "\n", tparam.max_tmo);
+ ODPH_DBG("Num timers: %u\n", tparam.num_timers);
+ ODPH_DBG("Tmo range: %u ms (%" PRIu64 " ticks)\n", RANGE_MS,
+ odp_timer_ns_to_tick(tp, 1000000ULL * RANGE_MS));
+ ODPH_DBG("Max timers: %" PRIu32 "\n", timer_capa.max_timers);
+ ODPH_DBG("Max timer pools: %" PRIu32 "\n", timer_capa.max_pools);
+ ODPH_DBG("Max timer pools combined: %" PRIu32 "\n",
+ timer_capa.max_pools_combined);
+
+ tick = odp_timer_ns_to_tick(tp, 0);
+ CU_ASSERT(tick == 0);
+ ns2 = odp_timer_tick_to_ns(tp, tick);
+ CU_ASSERT(ns2 == 0);
+
+ for (ns = res_ns; ns < max_tmo; ns += res_ns) {
+ tick = odp_timer_ns_to_tick(tp, ns);
+ ns2 = odp_timer_tick_to_ns(tp, tick);
+
+ if (ns2 < ns - res_ns) {
+ ODPH_DBG("FAIL ns:%" PRIu64 " tick:%" PRIu64 " ns2:"
+ "%" PRIu64 "\n", ns, tick, ns2);
+ CU_FAIL("tick conversion: nsec too small\n");
+ }
+
+ if (ns2 > ns + res_ns) {
+ ODPH_DBG("FAIL ns:%" PRIu64 " tick:%" PRIu64 " ns2:"
+ "%" PRIu64 "\n", ns, tick, ns2);
+ CU_FAIL("tick conversion: nsec too large\n");
+ }
+ }
+
+ /* Initialize barrier used by worker threads for synchronization */
+ odp_barrier_init(&global_mem->test_barrier, num_workers);
+
+ /* Initialize the shared timeout counter */
+ odp_atomic_init_u32(&global_mem->ndelivtoolate, 0);
+
+ /* Initialize the number of finally allocated elements */
+ odp_atomic_init_u32(&global_mem->timers_allocated, 0);
+
+ /* Create and start worker threads */
+ global_mem->test_queue_type = queue_type;
+ odp_cunit_thread_create(num_workers, worker_entrypoint, NULL, 0, 0);
+
+ /* Wait for worker threads to exit */
+ odp_cunit_thread_join(num_workers);
+ ODPH_DBG("Number of timeouts delivered/received too late: "
+ "%" PRIu32 "\n",
+ odp_atomic_load_u32(&global_mem->ndelivtoolate));
+
+ /* Check some statistics after the test */
+ if (odp_timer_pool_info(tp, &tpinfo) != 0)
+ CU_FAIL("odp_timer_pool_info");
+ CU_ASSERT(tpinfo.param.num_timers == num_timers);
+ CU_ASSERT(tpinfo.cur_timers == 0);
+ timers_allocated = odp_atomic_load_u32(&global_mem->timers_allocated);
+ CU_ASSERT(tpinfo.hwm_timers == timers_allocated);
+
+ /* Destroy timer pool, all timers must have been freed */
+ odp_timer_pool_destroy(tp);
+
+ /* Destroy timeout pool, all timeouts must have been freed */
+ rc = odp_pool_destroy(tbp);
+ CU_ASSERT(rc == 0);
+}
+
+static void timer_test_plain_all(void)
+{
+ timer_test_all(ODP_QUEUE_TYPE_PLAIN);
+}
+
+static void timer_test_sched_all(void)
+{
+ timer_test_all(ODP_QUEUE_TYPE_SCHED);
+}
+
+static void timer_test_periodic_capa(void)
+{
+ odp_timer_capability_t timer_capa;
+ odp_timer_periodic_capability_t capa;
+ odp_fract_u64_t min_fract, max_fract, base_freq;
+ uint64_t freq_range, freq_step, first_hz, res_ns, max_multiplier;
+ double freq, min_freq, max_freq;
+ int ret;
+ uint32_t i, j;
+ uint32_t num = 100;
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&timer_capa, 0, sizeof(odp_timer_capability_t));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_src, &timer_capa) == 0);
+ CU_ASSERT(timer_capa.periodic.max_pools);
+ CU_ASSERT(timer_capa.periodic.max_timers);
+
+ min_fract = timer_capa.periodic.min_base_freq_hz;
+ max_fract = timer_capa.periodic.max_base_freq_hz;
+
+ CU_ASSERT_FATAL(min_fract.integer || min_fract.numer);
+ CU_ASSERT_FATAL(max_fract.integer || max_fract.numer);
+
+ if (min_fract.numer) {
+ CU_ASSERT_FATAL(min_fract.denom);
+ CU_ASSERT_FATAL(min_fract.numer < min_fract.denom);
+ }
+
+ if (max_fract.numer) {
+ CU_ASSERT_FATAL(max_fract.denom);
+ CU_ASSERT_FATAL(max_fract.numer < max_fract.denom);
+ }
+
+ min_freq = odp_fract_u64_to_dbl(&min_fract);
+ max_freq = odp_fract_u64_to_dbl(&max_fract);
+ CU_ASSERT(min_freq <= max_freq);
+
+ memset(&capa, 0, sizeof(odp_timer_periodic_capability_t));
+
+ /* Min freq, capa fills in resolution */
+ capa.base_freq_hz = min_fract;
+ capa.max_multiplier = 1;
+ capa.res_ns = 0;
+
+ CU_ASSERT(odp_timer_periodic_capability(clk_src, &capa) == 1);
+ CU_ASSERT(capa.base_freq_hz.integer == min_fract.integer);
+ CU_ASSERT(capa.base_freq_hz.numer == min_fract.numer);
+ CU_ASSERT(capa.base_freq_hz.denom == min_fract.denom);
+ CU_ASSERT(capa.max_multiplier >= 1);
+ CU_ASSERT(capa.res_ns > 0);
+
+ /* Max freq, capa fills in resolution */
+ capa.base_freq_hz = max_fract;
+ capa.max_multiplier = 1;
+ capa.res_ns = 0;
+
+ CU_ASSERT(odp_timer_periodic_capability(clk_src, &capa) == 1);
+ CU_ASSERT(capa.base_freq_hz.integer == max_fract.integer);
+ CU_ASSERT(capa.base_freq_hz.numer == max_fract.numer);
+ CU_ASSERT(capa.base_freq_hz.denom == max_fract.denom);
+ CU_ASSERT(capa.max_multiplier >= 1);
+ CU_ASSERT(capa.res_ns > 0);
+
+ freq_range = max_fract.integer - min_fract.integer;
+
+ if (freq_range < 10 * num)
+ num = freq_range / 10;
+
+ /* Too short frequency range */
+ if (num == 0)
+ return;
+
+ freq_step = freq_range / num;
+ first_hz = min_fract.integer + 1;
+
+ ODPH_DBG("min %" PRIu64 ", max %" PRIu64 ", range %" PRIu64 ", step %" PRIu64 "\n",
+ min_fract.integer, max_fract.integer, freq_range, freq_step);
+
+ for (i = 0; i < num; i++) {
+ base_freq.integer = first_hz + i * freq_step;
+ base_freq.numer = 0;
+ base_freq.denom = 0;
+
+ freq = odp_fract_u64_to_dbl(&base_freq);
+
+ if (freq > max_freq)
+ base_freq = max_fract;
+
+ for (j = 0; j < 4; j++) {
+ capa.base_freq_hz = base_freq;
+
+ max_multiplier = 1;
+ res_ns = 0;
+
+ if (j & 0x1)
+ max_multiplier = 2;
+
+ if (j & 0x2)
+ res_ns = 1 + (ODP_TIME_SEC_IN_NS / (10 * base_freq.integer));
+
+ capa.max_multiplier = max_multiplier;
+ capa.res_ns = res_ns;
+
+ ODPH_DBG("freq %" PRIu64 ", multip %" PRIu64 ", res %" PRIu64 ",\n",
+ base_freq.integer, max_multiplier, res_ns);
+
+ ret = odp_timer_periodic_capability(clk_src, &capa);
+
+ if (ret == 1) {
+ CU_ASSERT(capa.base_freq_hz.integer == base_freq.integer);
+ CU_ASSERT(capa.base_freq_hz.numer == base_freq.numer);
+ CU_ASSERT(capa.base_freq_hz.denom == base_freq.denom);
+ } else if (ret == 0) {
+ CU_ASSERT(capa.base_freq_hz.integer != base_freq.integer ||
+ capa.base_freq_hz.numer != base_freq.numer ||
+ capa.base_freq_hz.denom != base_freq.denom)
+
+ if (capa.base_freq_hz.numer) {
+ CU_ASSERT_FATAL(capa.base_freq_hz.denom);
+ CU_ASSERT_FATAL(capa.base_freq_hz.numer <
+ capa.base_freq_hz.denom);
+ }
+
+ CU_ASSERT(odp_fract_u64_to_dbl(&capa.base_freq_hz) >= min_freq);
+ CU_ASSERT(odp_fract_u64_to_dbl(&capa.base_freq_hz) <= max_freq);
+ }
+
+ if (ret >= 0) {
+ CU_ASSERT(capa.max_multiplier >= max_multiplier);
+
+ if (res_ns) {
+ /* Same or better resolution */
+ CU_ASSERT(capa.res_ns <= res_ns);
+ } else {
+ CU_ASSERT(capa.res_ns > 0);
+ }
+ }
+ }
+ }
+}
+
+static void timer_test_periodic(odp_queue_type_t queue_type, int use_first, int rounds,
+ int reuse_event)
+{
+ odp_timer_capability_t timer_capa;
+ odp_timer_periodic_capability_t periodic_capa;
+ odp_pool_t pool;
+ odp_pool_param_t pool_param;
+ odp_queue_param_t queue_param;
+ odp_timer_pool_param_t timer_param;
+ odp_timer_pool_t timer_pool;
+ odp_timer_periodic_start_t start_param;
+ odp_queue_t queue;
+ odp_timeout_t tmo;
+ odp_event_t ev = ODP_EVENT_INVALID;
+ odp_timer_t timer;
+ odp_time_t t1, t2;
+ uint64_t tick, cur_tick, period_ns, duration_ns, diff_ns, offset_ns;
+ double freq, freq_out, min_freq, max_freq;
+ int ret;
+ const char *user_ctx = "User context";
+ int num_tmo;
+ int done;
+ const int num = 200;
+ /* Test frequency: 1x 1000 Hz, or 1x min/max_base_freq */
+ const uint64_t multiplier = 1;
+ odp_fract_u64_t base_freq = {1000, 0, 0};
+ odp_timer_clk_src_t clk_src = test_global->clk_src;
+
+ memset(&timer_capa, 0, sizeof(odp_timer_capability_t));
+ CU_ASSERT_FATAL(odp_timer_capability(clk_src, &timer_capa) == 0);
+
+ CU_ASSERT_FATAL(timer_capa.periodic.max_pools);
+ CU_ASSERT_FATAL(timer_capa.periodic.max_timers);
+ CU_ASSERT_FATAL(timer_capa.periodic.min_base_freq_hz.integer ||
+ timer_capa.periodic.min_base_freq_hz.numer);
+ CU_ASSERT_FATAL(timer_capa.periodic.max_base_freq_hz.integer ||
+ timer_capa.periodic.max_base_freq_hz.numer);
+
+ min_freq = odp_fract_u64_to_dbl(&timer_capa.periodic.min_base_freq_hz);
+ max_freq = odp_fract_u64_to_dbl(&timer_capa.periodic.max_base_freq_hz);
+ CU_ASSERT(min_freq <= max_freq);
+
+ if (odp_fract_u64_to_dbl(&base_freq) < min_freq)
+ base_freq = timer_capa.periodic.min_base_freq_hz;
+ else if (odp_fract_u64_to_dbl(&base_freq) > max_freq)
+ base_freq = timer_capa.periodic.max_base_freq_hz;
+
+ freq = odp_fract_u64_to_dbl(&base_freq);
+
+ /* No resolution requirement */
+ memset(&periodic_capa, 0, sizeof(odp_timer_periodic_capability_t));
+ periodic_capa.base_freq_hz = base_freq;
+ periodic_capa.max_multiplier = multiplier;
+
+ ret = odp_timer_periodic_capability(clk_src, &periodic_capa);
+ CU_ASSERT(ret == 0 || ret == 1);
+
+ if (ret < 0) {
+ ODPH_ERR("Periodic timer does not support tested frequency\n");
+ return;
+ }
+
+ freq_out = odp_fract_u64_to_dbl(&periodic_capa.base_freq_hz);
+
+ if (ret == 0) {
+ /* Allow 10% difference in outputted base frequency */
+ CU_ASSERT((freq_out > (0.9 * freq)) && (freq_out < (1.1 * freq)));
+
+ if (periodic_capa.base_freq_hz.numer) {
+ CU_ASSERT_FATAL(periodic_capa.base_freq_hz.numer <
+ periodic_capa.base_freq_hz.denom);
+ }
+ } else {
+ CU_ASSERT(base_freq.integer == periodic_capa.base_freq_hz.integer);
+ CU_ASSERT(base_freq.numer == periodic_capa.base_freq_hz.numer);
+ CU_ASSERT(base_freq.denom == periodic_capa.base_freq_hz.denom);
+ }
+
+ CU_ASSERT(periodic_capa.res_ns > 0);
+ CU_ASSERT(periodic_capa.max_multiplier >= multiplier);
+
+ base_freq = periodic_capa.base_freq_hz;
+ freq = odp_fract_u64_to_dbl(&base_freq);
+ period_ns = ODP_TIME_SEC_IN_NS / (freq * multiplier);
+ duration_ns = num * period_ns;
+
+ odp_timer_pool_param_init(&timer_param);
+ timer_param.timer_type = ODP_TIMER_TYPE_PERIODIC;
+ timer_param.res_ns = 2 * periodic_capa.res_ns;
+ timer_param.num_timers = 1;
+ timer_param.clk_src = clk_src;
+ timer_param.periodic.base_freq_hz = base_freq;
+ timer_param.periodic.max_multiplier = multiplier;
+
+ ODPH_DBG("\n");
+ ODPH_DBG("Periodic timer pool create params:\n");
+ ODPH_DBG(" Resolution ns: %" PRIu64 "\n", timer_param.res_ns);
+ ODPH_DBG(" Base freq hz: %" PRIu64 " + %" PRIu64 "/%" PRIu64 " (%f)\n",
+ timer_param.periodic.base_freq_hz.integer,
+ timer_param.periodic.base_freq_hz.numer,
+ timer_param.periodic.base_freq_hz.denom, freq);
+ ODPH_DBG(" Max multiplier: %" PRIu64 "\n", timer_param.periodic.max_multiplier);
+ ODPH_DBG("Capabilities:\n");
+ ODPH_DBG(" Max multiplier: %" PRIu64 " (with %f hz)\n",
+ periodic_capa.max_multiplier, freq);
+ ODPH_DBG(" Max resolution: %" PRIu64 " ns (with %f hz)\n", periodic_capa.res_ns, freq);
+ ODPH_DBG(" Min base freq: %f hz\n", min_freq);
+ ODPH_DBG(" Max base freq: %f hz\n", max_freq);
+
+ timer_pool = odp_timer_pool_create("periodic_timer", &timer_param);
+ CU_ASSERT_FATAL(timer_pool != ODP_TIMER_POOL_INVALID);
+
+ CU_ASSERT_FATAL(odp_timer_pool_start_multi(&timer_pool, 1) == 1);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_TIMEOUT;
+ pool_param.tmo.num = 1;
+
+ pool = odp_pool_create("timeout_pool", &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ odp_queue_param_init(&queue_param);
+ if (queue_type == ODP_QUEUE_TYPE_SCHED) {
+ queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ }
+
+ queue = odp_queue_create("timeout_queue", &queue_param);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ timer = odp_timer_alloc(timer_pool, queue, user_ctx);
+ CU_ASSERT_FATAL(timer != ODP_TIMER_INVALID);
+
+ /* Pool should have only one timer */
+ CU_ASSERT_FATAL(odp_timer_alloc(timer_pool, queue, user_ctx) == ODP_TIMER_INVALID);
+
+ memset(&start_param, 0, sizeof(odp_timer_periodic_start_t));
+ offset_ns = period_ns / 2;
+
+ if (use_first) {
+ /* First tick moves timer to start before the first period */
+ duration_ns -= (period_ns - offset_ns);
+ }
+
+ for (int round = 0; round < rounds; round++) {
+ num_tmo = 0;
+ done = 0;
+
+ if (!reuse_event || round == 0) {
+ tmo = odp_timeout_alloc(pool);
+ ev = odp_timeout_to_event(tmo);
+ }
+
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ cur_tick = odp_timer_current_tick(timer_pool);
+ tick = cur_tick + odp_timer_ns_to_tick(timer_pool, offset_ns);
+
+ if (use_first)
+ start_param.first_tick = tick;
+
+ start_param.freq_multiplier = multiplier;
+ start_param.tmo_ev = ev;
+
+ ODPH_DBG("Periodic timer start:\n");
+ ODPH_DBG(" Current tick: %" PRIu64 "\n", cur_tick);
+ ODPH_DBG(" First tick: %" PRIu64 "\n", start_param.first_tick);
+ ODPH_DBG(" Multiplier: %" PRIu64 "\n", start_param.freq_multiplier);
+ ODPH_DBG(" Period: %" PRIu64 " nsec\n", period_ns);
+ ODPH_DBG("Expected duration: %" PRIu64 " nsec\n", duration_ns);
+
+ ret = odp_timer_periodic_start(timer, &start_param);
+
+ if (ret == ODP_TIMER_TOO_NEAR)
+ ODPH_ERR("First tick too near\n");
+ else if (ret == ODP_TIMER_TOO_FAR)
+ ODPH_ERR("First tick too far\n");
+ else if (ret == ODP_TIMER_FAIL)
+ ODPH_ERR("Periodic timer start failed\n");
+
+ CU_ASSERT_FATAL(ret == ODP_TIMER_SUCCESS);
+
+ t1 = odp_time_local();
+
+ /* Wait for timeouts. Make sure that scheduler context is not held when
+ * exiting the loop. */
+ while (1) {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+ if (diff_ns > (10 * duration_ns))
+ break;
+
+ if (num_tmo >= num)
+ break;
+
+ continue;
+ }
+
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_TIMEOUT);
+
+ if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ CU_ASSERT(odp_timer_periodic_ack(timer, ev) == 0);
+ num_tmo++;
+ }
+
+ CU_ASSERT(num_tmo == num);
+
+ /* Allow +-30% error on test duration */
+ CU_ASSERT((diff_ns > 0.7 * duration_ns) && (diff_ns < 1.3 * duration_ns));
+
+ /* Stop periodic timer */
+ ret = odp_timer_periodic_cancel(timer);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ODPH_DBG("Measured duration: %" PRIu64 " nsec\n", diff_ns);
+
+ t1 = odp_time_local();
+ while (1) {
+ if (queue_type == ODP_QUEUE_TYPE_SCHED)
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+ else
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ t2 = odp_time_local();
+ diff_ns = odp_time_diff_ns(t2, t1);
+ if (diff_ns > (10 * duration_ns))
+ break;
+
+ if (done)
+ break;
+
+ continue;
+ }
+
+ CU_ASSERT(odp_event_type(ev) == ODP_EVENT_TIMEOUT);
+
+ if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) {
+ odp_event_free(ev);
+ continue;
+ }
+
+ ret = odp_timer_periodic_ack(timer, ev);
+ CU_ASSERT(ret == 1 || ret == 2);
+
+ if (ret == 2) {
+ done = 1;
+ if (reuse_event && round < rounds - 1)
+ break;
+ odp_event_free(ev);
+ }
+ }
+
+ /* Check that ack() returned 2 on the last event */
+ CU_ASSERT(done);
+ CU_ASSERT(ret == 2);
+ }
+
+ CU_ASSERT(odp_timer_free(timer) == 0);
+ odp_timer_pool_destroy(timer_pool);
+ CU_ASSERT(odp_queue_destroy(queue) == 0);
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static void timer_test_periodic_sched(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_SCHED, 0, 1, 0);
+}
+
+static void timer_test_periodic_plain(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_PLAIN, 0, 1, 0);
+}
+
+static void timer_test_periodic_sched_first(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_SCHED, FIRST_TICK, 1, 0);
+}
+
+static void timer_test_periodic_plain_first(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_PLAIN, FIRST_TICK, 1, 0);
+}
+
+static void timer_test_periodic_reuse(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_SCHED, 0, 2, 0);
+}
+
+static void timer_test_periodic_event_reuse(void)
+{
+ timer_test_periodic(ODP_QUEUE_TYPE_SCHED, 0, 2, 1);
+}
+
+odp_testinfo_t timer_general_suite[] = {
+ ODP_TEST_INFO(timer_test_param_init),
+ ODP_TEST_INFO(timer_test_timeout_pool_alloc),
+ ODP_TEST_INFO(timer_test_timeout_pool_alloc_multi),
+ ODP_TEST_INFO(timer_test_timeout_from_event),
+ ODP_TEST_INFO(timer_test_timeout_pool_free),
+ ODP_TEST_INFO(timer_test_timeout_user_area),
+ ODP_TEST_INFO(timer_test_capa_allsrc),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t timer_general_suites[] = {
+ {"Timer general", NULL, NULL, timer_general_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+odp_testinfo_t timer_suite[] = {
+ ODP_TEST_INFO(timer_test_capa),
+ ODP_TEST_INFO(timer_pool_create_destroy),
+ ODP_TEST_INFO(timer_pool_create_max),
+ ODP_TEST_INFO(timer_pool_max_res),
+ ODP_TEST_INFO(timer_pool_current_tick),
+ ODP_TEST_INFO(timer_pool_sample_ticks),
+ ODP_TEST_INFO(timer_pool_tick_info),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_rel_wait, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_abs_wait, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_rel_cancel, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_abs_cancel, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_rel_restart_wait, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_abs_restart_wait, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_rel_restart_cancel, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_abs_restart_cancel, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_plain_abs_wait_3sec, check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_rel_wait, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_abs_wait, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_rel_cancel, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_abs_cancel, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_rel_restart_wait, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_abs_restart_wait, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_rel_restart_cancel, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_abs_restart_cancel, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_sched_abs_wait_3sec, check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_tmo_event_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_tmo_event_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_buf_event_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_buf_event_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_pkt_event_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_pkt_event_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_tmo_event_reuse,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_buf_event_reuse,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_pkt_event_reuse,
+ check_sched_queue_support),
+ ODP_TEST_INFO(timer_test_cancel),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_res_min_tmo_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_res_min_tmo_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_res_max_tmo_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_res_max_tmo_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_tmo_min_tmo_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_tmo_min_tmo_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_tmo_max_tmo_plain,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_max_tmo_max_tmo_sched,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_plain_queue,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_sched_queue,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_plain_queue_priv,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_sched_queue_priv,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_plain_queue_exp_relax,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_sched_queue_exp_relax,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_plain_all,
+ check_plain_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_sched_all,
+ check_sched_queue_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_capa,
+ check_periodic_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_sched,
+ check_periodic_sched_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_sched_first,
+ check_periodic_sched_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_plain,
+ check_periodic_plain_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_plain_first,
+ check_periodic_plain_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_reuse,
+ check_periodic_sched_support),
+ ODP_TEST_INFO_CONDITIONAL(timer_test_periodic_event_reuse,
+ check_periodic_sched_support),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t timer_suites[] = {
+ {"Timer", NULL, NULL, timer_suite},
+ ODP_SUITE_INFO_NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ if (global_init())
+ return -1;
+
+ odp_cunit_register_global_init(timer_global_init);
+ odp_cunit_register_global_term(timer_global_term);
+
+ if (odp_cunit_register(timer_general_suites))
+ goto fail;
+
+ if (odp_cunit_run())
+ goto fail;
+
+ for (int i = ODP_CLOCK_SRC_0; i < ODP_CLOCK_NUM_SRC; i++) {
+ odp_timer_capability_t capa;
+
+ if (odp_timer_capability(i, &capa))
+ continue;
+
+ printf("\n\n"
+ "-------------------------------------------------------------------------------\n"
+ " Running tests with clock source %d\n"
+ "-------------------------------------------------------------------------------\n\n",
+ i);
+
+ test_global->clk_src = i;
+
+ odp_cunit_register_global_init(timer_global_init);
+ odp_cunit_register_global_term(timer_global_term);
+
+ if (odp_cunit_register(timer_suites))
+ goto fail;
+
+ if (odp_cunit_run())
+ ret = -1;
+ }
+
+ global_term();
+ return ret;
+
+fail:
+ global_term();
+ return -1;
+}
diff --git a/test/common_plat/validation/api/traffic_mngr/.gitignore b/test/validation/api/traffic_mngr/.gitignore
index efd07a27d..efd07a27d 100644
--- a/test/common_plat/validation/api/traffic_mngr/.gitignore
+++ b/test/validation/api/traffic_mngr/.gitignore
diff --git a/test/validation/api/traffic_mngr/Makefile.am b/test/validation/api/traffic_mngr/Makefile.am
new file mode 100644
index 000000000..53a00f5e6
--- /dev/null
+++ b/test/validation/api/traffic_mngr/Makefile.am
@@ -0,0 +1,5 @@
+include ../Makefile.inc
+
+test_PROGRAMS = traffic_mngr_main
+traffic_mngr_main_SOURCES = traffic_mngr.c
+LDADD += -lm
diff --git a/test/validation/api/traffic_mngr/traffic_mngr.c b/test/validation/api/traffic_mngr/traffic_mngr.c
new file mode 100644
index 000000000..ca44c59a2
--- /dev/null
+++ b/test/validation/api/traffic_mngr/traffic_mngr.c
@@ -0,0 +1,5045 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2015-2018 Linaro Limited
+ * Copyright (c) 2022 Marvell
+ * Copyright (c) 2022 Nokia
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define TM_DEBUG 0
+
+#define MAX_NUM_IFACES 2
+#define MAX_TM_SYSTEMS 3
+#define NUM_LEVELS 3
+#define NUM_PRIORITIES 4
+#define NUM_QUEUES_PER_NODE NUM_PRIORITIES
+#define FANIN_RATIO 8u
+#define NUM_LEVEL0_TM_NODES 1
+#define NUM_LEVEL1_TM_NODES FANIN_RATIO
+#define NUM_LEVEL2_TM_NODES (FANIN_RATIO * FANIN_RATIO)
+#define NUM_TM_QUEUES (NUM_LEVEL2_TM_NODES * NUM_QUEUES_PER_NODE)
+#define NUM_SHAPER_PROFILES 64
+#define NUM_SCHED_PROFILES 64
+#define NUM_THRESHOLD_PROFILES 64
+#define NUM_WRED_PROFILES 64
+#define NUM_SHAPER_TEST_PROFILES 8
+#define NUM_SCHED_TEST_PROFILES 8
+#define NUM_THRESH_TEST_PROFILES 8
+#define NUM_WRED_TEST_PROFILES 8
+
+#define ODP_NUM_PKT_COLORS ODP_NUM_PACKET_COLORS
+#define PKT_GREEN ODP_PACKET_GREEN
+#define PKT_YELLOW ODP_PACKET_YELLOW
+#define PKT_RED ODP_PACKET_RED
+
+#define MIN_COMMIT_BW (64 * 1024)
+#define MIN_COMMIT_BURST 8000
+#define MIN_PEAK_BW 2000000
+#define MIN_PEAK_BURST 16000
+
+#define INITIAL_RCV_GAP_DROP 10 /* This is a percent of rcvd pkts */
+#define ENDING_RCV_GAP_DROP 20 /* This is a percent of rcvd pkts */
+
+#define MIN_SHAPER_BW_RCV_GAP 80 /* Percent of expected_rcv_gap */
+#define MAX_SHAPER_BW_RCV_GAP 125 /* Percent of expected_rcv_gap */
+
+#define MIN_PKT_THRESHOLD 10
+#define MIN_BYTE_THRESHOLD 2048
+
+#define MIN_WRED_THRESH 5
+#define MED_WRED_THRESH 10
+#define MED_DROP_PROB 4
+#define MAX_DROP_PROB 8
+
+#define MAX_PKTS 1000u
+#define PKT_BUF_SIZE 1460
+#define MIN_HDR_LEN (ODPH_ETHHDR_LEN + ODPH_UDPHDR_LEN + ODPH_IPV4HDR_LEN)
+#define MAX_PAYLOAD (PKT_BUF_SIZE - MIN_HDR_LEN)
+#define USE_IPV4 false
+#define USE_IPV6 true
+#define USE_UDP false
+#define USE_TCP true
+#define LOW_DROP_PRECEDENCE 0x02
+#define MEDIUM_DROP_PRECEDENCE 0x04
+#define HIGH_DROP_PRECEDENCE 0x06
+#define DROP_PRECEDENCE_MASK 0x06
+#define DSCP_CLASS1 0x08
+#define DSCP_CLASS2 0x10
+#define DSCP_CLASS3 0x18
+#define DSCP_CLASS4 0x20
+#define DEFAULT_DSCP (DSCP_CLASS2 | LOW_DROP_PRECEDENCE)
+#define DEFAULT_ECN ODPH_IP_ECN_ECT0
+#define DEFAULT_TOS ((DEFAULT_DSCP << ODPH_IP_TOS_DSCP_SHIFT) | \
+ DEFAULT_ECN)
+#define DEFAULT_TTL 128
+#define DEFAULT_UDP_SRC_PORT 12049
+#define DEFAULT_UDP_DST_PORT 12050
+#define DEFAULT_TCP_SRC_PORT 0xDEAD
+#define DEFAULT_TCP_DST_PORT 0xBABE
+#define DEFAULT_TCP_SEQ_NUM 0x12345678
+#define DEFAULT_TCP_ACK_NUM 0x12340000
+#define DEFAULT_TCP_WINDOW 0x4000
+#define VLAN_PRIORITY_BK 1 /* Background - lowest priority */
+#define VLAN_PRIORITY_BE 0 /* Best Effort */
+#define VLAN_PRIORITY_EE 2 /* Excellent Effort */
+#define VLAN_PRIORITY_NC 7 /* Network Control - highest priority */
+#define VLAN_DEFAULT_VID 12
+#define VLAN_NO_DEI ((VLAN_PRIORITY_EE << 13) | VLAN_DEFAULT_VID)
+#define ETHERNET_IFG 12 /* Ethernet Interframe Gap */
+#define ETHERNET_PREAMBLE 8
+#define ETHERNET_OVHD_LEN (ETHERNET_IFG + ETHERNET_PREAMBLE)
+#define CRC_LEN 4
+#define SHAPER_LEN_ADJ ETHERNET_OVHD_LEN
+#define TM_NAME_LEN 32
+#define BILLION 1000000000ULL
+#define MS 1000000 /* Millisecond in units of NS */
+#define MBPS 1000000
+#define GBPS 1000000000
+
+#define TM_PERCENT(percent) ((uint32_t)(100 * percent))
+
+typedef enum {
+ SHAPER_PROFILE, SCHED_PROFILE, THRESHOLD_PROFILE, WRED_PROFILE
+} profile_kind_t;
+
+typedef enum {
+ THRESHOLD_BYTE,
+ THRESHOLD_PACKET,
+ THRESHOLD_BYTE_AND_PACKET
+} threshold_type_t;
+
+typedef struct {
+ uint32_t num_queues;
+ odp_tm_queue_t tm_queues[];
+} tm_queue_desc_t;
+
+typedef struct tm_node_desc_s tm_node_desc_t;
+
+struct tm_node_desc_s {
+ uint32_t level;
+ uint32_t node_idx;
+ uint32_t num_children;
+ char *node_name;
+ odp_tm_node_t node;
+ odp_tm_node_t parent_node;
+ tm_queue_desc_t *queue_desc;
+ tm_node_desc_t *children[];
+};
+
+typedef struct {
+ uint32_t num_samples;
+ uint32_t min_rcv_gap;
+ uint32_t max_rcv_gap;
+ uint32_t total_rcv_gap;
+ uint64_t total_rcv_gap_squared;
+ uint32_t avg_rcv_gap;
+ uint32_t std_dev_gap;
+} rcv_stats_t;
+
+typedef struct {
+ odp_time_t xmt_time;
+ odp_time_t rcv_time;
+ uint64_t delta_ns;
+ odp_tm_queue_t tm_queue;
+ uint16_t pkt_len;
+ uint16_t xmt_unique_id;
+ uint16_t xmt_idx;
+ uint8_t pkt_class;
+ uint8_t was_rcvd;
+} xmt_pkt_desc_t;
+
+typedef struct {
+ odp_time_t rcv_time;
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ uint16_t rcv_unique_id;
+ uint16_t xmt_idx;
+ uint8_t errors;
+ uint8_t matched;
+ uint8_t pkt_class;
+ uint8_t is_ipv4_pkt;
+} rcv_pkt_desc_t;
+
+typedef struct {
+ odp_tm_percent_t confidence_percent;
+ odp_tm_percent_t drop_percent;
+ uint32_t min_cnt;
+ uint32_t max_cnt;
+} wred_pkt_cnts_t;
+
+typedef struct {
+ uint32_t num_queues;
+ uint32_t priority;
+ odp_tm_queue_t tm_queues[NUM_LEVEL2_TM_NODES];
+} queue_array_t;
+
+typedef struct {
+ queue_array_t queue_array[NUM_PRIORITIES];
+} queues_set_t;
+
+typedef struct {
+ uint16_t vlan_tci;
+ uint8_t pkt_class;
+ uint8_t ip_tos; /* TOS for IPv4 and TC for IPv6 */
+ odp_packet_color_t pkt_color;
+ odp_bool_t drop_eligible;
+ odp_bool_t use_vlan; /* Else no VLAN header */
+ odp_bool_t use_ipv6; /* Else use IPv4 */
+ odp_bool_t use_tcp; /* Else use UDP */
+} pkt_info_t;
+
+static const char ALPHABET[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/* The following constant table determines the minimum and maximum number of
+ * pkts that will be received when sending 100 pkts through a system with a
+ * drop probability of p% (using a uniform probability distribution), with a
+ * confidence of 99.9% 99.99% and 99.999%. The confidence is interpreted as
+ * follows: a 99.99% confidence says that receiving LESS pkts than the given
+ * minimum or receiving MORE pkts than the given maximum (assuming a uniform
+ * drop percent of p) will happen less than 1 time in 10,000 trials.
+ * Mathematically the minimum pkt cnt is the largest value of cnt
+ * that satisfies the following equation:
+ * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=0..cnt)",
+ * where cf is the confidence, caret (^) represents exponentiation,
+ * binomial(n,k) is the binomial coefficient defined as n! / (k! * (n-k)!).
+ * and p is the drop probability. Similarly the maximum pkt cnt is the
+ * smallest value of cnt that satisfies the equation:
+ * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=cnt..100)".
+ * As a consequence of this, it should be the case that:
+ * cf/100 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=min..max)".
+ */
+static wred_pkt_cnts_t EXPECTED_PKT_RCVD[] = {
+ { TM_PERCENT(99.0), TM_PERCENT(10.0), 82, 97 },
+ { TM_PERCENT(99.0), TM_PERCENT(20.0), 69, 90 },
+ { TM_PERCENT(99.0), TM_PERCENT(30.0), 58, 81 },
+ { TM_PERCENT(99.0), TM_PERCENT(40.0), 47, 72 },
+ { TM_PERCENT(99.0), TM_PERCENT(50.0), 37, 63 },
+ { TM_PERCENT(99.0), TM_PERCENT(60.0), 28, 53 },
+ { TM_PERCENT(99.0), TM_PERCENT(70.0), 19, 42 },
+ { TM_PERCENT(99.0), TM_PERCENT(80.0), 10, 31 },
+ { TM_PERCENT(99.0), TM_PERCENT(90.0), 3, 18 },
+
+ { TM_PERCENT(99.9), TM_PERCENT(10.0), 79, 98 },
+ { TM_PERCENT(99.9), TM_PERCENT(20.0), 66, 92 },
+ { TM_PERCENT(99.9), TM_PERCENT(30.0), 54, 84 },
+ { TM_PERCENT(99.9), TM_PERCENT(40.0), 44, 76 },
+ { TM_PERCENT(99.9), TM_PERCENT(50.0), 34, 66 },
+ { TM_PERCENT(99.9), TM_PERCENT(60.0), 24, 56 },
+ { TM_PERCENT(99.9), TM_PERCENT(70.0), 16, 46 },
+ { TM_PERCENT(99.9), TM_PERCENT(80.0), 8, 34 },
+ { TM_PERCENT(99.9), TM_PERCENT(90.0), 2, 21 },
+
+ { TM_PERCENT(99.99), TM_PERCENT(10.0), 77, 99 },
+ { TM_PERCENT(99.99), TM_PERCENT(20.0), 63, 94 },
+ { TM_PERCENT(99.99), TM_PERCENT(30.0), 51, 87 },
+ { TM_PERCENT(99.99), TM_PERCENT(40.0), 41, 78 },
+ { TM_PERCENT(99.99), TM_PERCENT(50.0), 31, 69 },
+ { TM_PERCENT(99.99), TM_PERCENT(60.0), 22, 59 },
+ { TM_PERCENT(99.99), TM_PERCENT(70.0), 13, 49 },
+ { TM_PERCENT(99.99), TM_PERCENT(80.0), 6, 37 },
+ { TM_PERCENT(99.99), TM_PERCENT(90.0), 1, 23 },
+};
+
+static uint8_t EQUAL_WEIGHTS[FANIN_RATIO] = {
+ 16, 16, 16, 16, 16, 16, 16, 16
+};
+
+static uint8_t INCREASING_WEIGHTS[FANIN_RATIO] = {
+ 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static uint8_t IPV4_SRC_ADDR[ODPH_IPV4ADDR_LEN] = {
+ 10, 0, 0, 1 /* I.e. 10.0.0.1 */
+};
+
+static uint8_t IPV4_DST_ADDR[ODPH_IPV4ADDR_LEN] = {
+ 10, 0, 0, 100 /* I.e. 10.0.0.100 */
+};
+
+static uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.0.0.1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 1
+};
+
+static uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = {
+ /* I.e. ::ffff:10.0.0.100 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 100
+};
+
+static odp_tm_t odp_tm_systems[MAX_TM_SYSTEMS];
+static tm_node_desc_t *root_node_descs[MAX_TM_SYSTEMS];
+static uint32_t num_odp_tm_systems;
+
+static odp_tm_capabilities_t tm_capabilities;
+
+static bool dynamic_shaper_update = true;
+static bool dynamic_sched_update = true;
+static bool dynamic_threshold_update = true;
+static bool dynamic_wred_update = true;
+
+static odp_tm_shaper_t shaper_profiles[NUM_SHAPER_PROFILES];
+static odp_tm_sched_t sched_profiles[NUM_SCHED_PROFILES];
+static odp_tm_threshold_t threshold_profiles[NUM_THRESHOLD_PROFILES];
+static odp_tm_wred_t wred_profiles[NUM_WRED_PROFILES][ODP_NUM_PKT_COLORS];
+
+static uint32_t num_shaper_profiles;
+static uint32_t num_sched_profiles;
+static uint32_t num_threshold_profiles;
+static uint32_t num_wred_profiles;
+
+static uint8_t payload_data[MAX_PAYLOAD];
+
+static odp_packet_t xmt_pkts[MAX_PKTS];
+static xmt_pkt_desc_t xmt_pkt_descs[MAX_PKTS];
+static uint32_t num_pkts_made;
+static uint32_t num_pkts_sent;
+
+static odp_packet_t rcv_pkts[MAX_PKTS];
+static rcv_pkt_desc_t rcv_pkt_descs[MAX_PKTS];
+static uint32_t num_rcv_pkts;
+
+static uint32_t rcv_gaps[MAX_PKTS];
+static uint32_t rcv_gap_cnt;
+
+static queues_set_t queues_set;
+static uint32_t unique_id_list[MAX_PKTS];
+
+/* interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/** number of interfaces being used (1=loopback, 2=pair) */
+static uint32_t num_ifaces;
+
+static odp_pool_t pools[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
+
+static odp_pktio_t pktios[MAX_NUM_IFACES];
+static odp_bool_t pktio_started[MAX_NUM_IFACES];
+static odp_pktin_queue_t pktins[MAX_NUM_IFACES];
+static odp_pktin_queue_t rcv_pktin;
+static odp_pktio_t xmt_pktio;
+static odp_pktio_capability_t xmt_pktio_capa;
+static odp_lso_profile_t lso_ipv4_profile;
+
+static odph_ethaddr_t src_mac;
+static odph_ethaddr_t dst_mac;
+
+static uint32_t cpu_unique_id;
+static uint32_t cpu_tcp_seq_num;
+
+static int8_t suite_inactive;
+
+static uint64_t tm_shaper_min_rate;
+static uint64_t tm_shaper_max_rate;
+static uint32_t tm_shaper_min_burst;
+static uint32_t tm_shaper_max_burst;
+
+static void busy_wait(uint64_t nanoseconds)
+{
+ odp_time_t start_time, end_time;
+
+ start_time = odp_time_local();
+ end_time = odp_time_sum(start_time,
+ odp_time_local_from_ns(nanoseconds));
+
+ while (odp_time_cmp(odp_time_local(), end_time) < 0)
+ odp_cpu_pause();
+}
+
+static odp_bool_t approx_eq32(uint32_t val, uint32_t correct)
+{
+ uint64_t low_bound, val_times_100, high_bound;
+
+ if (val == correct)
+ return true;
+
+ low_bound = 98 * (uint64_t)correct;
+ val_times_100 = 100 * (uint64_t)val;
+ high_bound = 102 * (uint64_t)correct;
+
+ if ((low_bound <= val_times_100) && (val_times_100 <= high_bound))
+ return true;
+ else
+ return false;
+}
+
+static odp_bool_t approx_eq64(uint64_t val, uint64_t correct)
+{
+ uint64_t low_bound, val_times_100, high_bound;
+
+ if (val == correct)
+ return true;
+
+ low_bound = 98 * correct;
+ val_times_100 = 100 * val;
+ high_bound = 102 * correct;
+
+ if ((low_bound <= val_times_100) && (val_times_100 <= high_bound))
+ return true;
+ else
+ return false;
+}
+
+static uint64_t
+clamp_rate(uint64_t rate)
+{
+ return ODPH_MIN(ODPH_MAX(rate, tm_shaper_min_rate), tm_shaper_max_rate);
+}
+
+static uint32_t
+clamp_burst(uint32_t burst)
+{
+ return ODPH_MIN(ODPH_MAX(burst, tm_shaper_min_burst), tm_shaper_max_burst);
+}
+
+static int test_overall_capabilities(void)
+{
+ odp_tm_level_capabilities_t *per_level;
+ odp_tm_capabilities_t capabilities_array[2];
+ odp_tm_capabilities_t *cap_ptr;
+ odp_tm_egress_t egress;
+ odp_bool_t *prio_modes;
+ uint32_t num_records, idx, num_levels, level;
+ int rc;
+
+ odp_tm_egress_init(&egress);
+ egress.egress_kind = ODP_TM_EGRESS_PKT_IO;
+ egress.pktio = xmt_pktio;
+
+ rc = odp_tm_egress_capabilities(&capabilities_array[0], &egress);
+ CU_ASSERT_FATAL(rc == 0);
+ num_records = 1;
+
+ /* Get capabilities for egress kind function. */
+ odp_tm_egress_init(&egress);
+ egress.egress_kind = ODP_TM_EGRESS_FN;
+ rc = odp_tm_egress_capabilities(&capabilities_array[1], &egress);
+ CU_ASSERT_FATAL(rc == 0);
+
+ /* Validate this record only if egress function is supported */
+ if (capabilities_array[1].max_tm_queues)
+ num_records++;
+
+ /* Loop through the returned capabilities (there MUST be at least one)
+ * and do some basic checks to prove that it isn't just an empty
+ * record. */
+ for (idx = 0; idx < num_records; idx++) {
+ cap_ptr = &capabilities_array[idx];
+ if (cap_ptr->max_tm_queues == 0) {
+ CU_ASSERT(cap_ptr->max_tm_queues != 0);
+ return -1;
+ }
+
+ if (cap_ptr->max_levels == 0) {
+ CU_ASSERT(cap_ptr->max_levels != 0);
+ return -1;
+ }
+
+ num_levels = cap_ptr->max_levels;
+ for (level = 0; level < num_levels; level++) {
+ per_level = &cap_ptr->per_level[level];
+
+ if (per_level->max_num_tm_nodes == 0) {
+ CU_ASSERT(per_level->max_num_tm_nodes != 0);
+ return -1;
+ }
+
+ if (per_level->max_fanin_per_node == 0) {
+ CU_ASSERT(per_level->max_fanin_per_node != 0);
+ return -1;
+ }
+
+ if (per_level->max_priority == 0) {
+ CU_ASSERT(per_level->max_priority != 0);
+ return -1;
+ }
+
+ if (per_level->tm_node_shaper_supported ||
+ per_level->tm_node_rate_limiter_supported) {
+ CU_ASSERT(per_level->max_burst > 0);
+ CU_ASSERT(per_level->min_rate > 0);
+ CU_ASSERT(per_level->max_rate > 0);
+ }
+
+ if (per_level->tm_node_shaper_packet_mode) {
+ CU_ASSERT(per_level->max_burst_packets > 0);
+ CU_ASSERT(per_level->min_rate_packets > 0);
+ CU_ASSERT(per_level->max_rate_packets > 0);
+ }
+ }
+
+ /* At least one pkt priority mode needs to be supported */
+ prio_modes = cap_ptr->pkt_prio_modes;
+ CU_ASSERT((prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE] != 0) ||
+ (prio_modes[ODP_TM_PKT_PRIO_MODE_OVERWRITE] != 0))
+ }
+
+ return 0;
+}
+
+static int wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ return ret;
+}
+
+static int open_pktios(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_config_t pktio_config;
+ odp_pool_param_t pool_param;
+ odp_pktio_t pktio;
+ odp_pool_t pkt_pool;
+ uint32_t iface;
+ char pool_name[ODP_POOL_NAME_LEN];
+ int rc, ret;
+ int pkt_aging = 0;
+ int lso = 0;
+
+ odp_pool_param_init(&pool_param);
+ pool_param.pkt.num = 10 * MAX_PKTS;
+ pool_param.type = ODP_POOL_PACKET;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ for (iface = 0; iface < num_ifaces; iface++) {
+ snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s",
+ iface_name[iface]);
+
+ pkt_pool = odp_pool_create(pool_name, &pool_param);
+ if (pkt_pool == ODP_POOL_INVALID) {
+ CU_FAIL("unable to create pool");
+ return -1;
+ }
+
+ pools[iface] = pkt_pool;
+
+ /* Zero'th device is always PKTOUT TM as we use it from XMIT */
+ if (iface == 0) {
+ pktio_param.out_mode = ODP_PKTOUT_MODE_TM;
+
+ pktio = odp_pktio_open(iface_name[iface], pkt_pool,
+ &pktio_param);
+
+ /* On failure check if pktio can be opened in non-TM mode.
+ * If non-TM mode works, then we can assume that PKTIO
+ * does not support TM
+ */
+ if (pktio == ODP_PKTIO_INVALID) {
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+ pktio = odp_pktio_open(iface_name[iface], pkt_pool,
+ &pktio_param);
+
+ /* Return >0 to indicate no TM support */
+ if (pktio != ODP_PKTIO_INVALID) {
+ odp_pktio_close(pktio);
+ return 1;
+ }
+ }
+ } else {
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DISABLED;
+
+ pktio = odp_pktio_open(iface_name[iface], pkt_pool,
+ &pktio_param);
+ }
+
+ pktios[iface] = pktio;
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("odp_pktio_open() failed\n");
+ return -1;
+ }
+
+ /* Set defaults for PktIn and PktOut queues */
+ (void)odp_pktin_queue_config(pktio, NULL);
+ rc = odp_pktio_promisc_mode_set(pktio, true);
+ if (rc != 0)
+ printf("****** promisc_mode_set failed ******\n");
+
+ if (odp_pktin_queue(pktio, &pktins[iface], 1) != 1) {
+ odp_pktio_close(pktio);
+ ODPH_ERR("odp_pktio_open() failed: no pktin queue\n");
+ return -1;
+ }
+
+ rc = -1;
+ if (iface == 0)
+ rc = odp_pktio_mac_addr(pktio, &src_mac,
+ ODPH_ETHADDR_LEN);
+
+ if ((iface == 1) || (num_ifaces == 1))
+ rc = odp_pktio_mac_addr(pktio, &dst_mac,
+ ODPH_ETHADDR_LEN);
+
+ if (rc != ODPH_ETHADDR_LEN) {
+ ODPH_ERR("odp_pktio_mac_addr() failed\n");
+ return -1;
+ }
+ }
+
+ if (2 <= num_ifaces) {
+ xmt_pktio = pktios[0];
+ rcv_pktin = pktins[1];
+ ret = odp_pktio_start(pktios[1]);
+ if (ret != 0) {
+ ODPH_ERR("odp_pktio_start() failed\n");
+ return -1;
+ }
+ pktio_started[1] = true;
+ } else {
+ xmt_pktio = pktios[0];
+ rcv_pktin = pktins[0];
+ }
+
+ if (odp_pktio_capability(xmt_pktio, &xmt_pktio_capa)) {
+ ODPH_ERR("pktio capa failed\n");
+ return -1;
+ }
+
+ odp_pktio_config_init(&pktio_config);
+
+ /* Enable packet aging if supported */
+ if (xmt_pktio_capa.max_tx_aging_tmo_ns) {
+ pkt_aging = 1;
+ pktio_config.pktout.bit.aging_ena = 1;
+ }
+
+ /* Enable LSO if supported */
+ if (xmt_pktio_capa.lso.max_profiles && xmt_pktio_capa.lso.max_profiles_per_pktio) {
+ lso = 1;
+ pktio_config.enable_lso = 1;
+ }
+
+ /* Enable selected features */
+ if (lso || pkt_aging) {
+ if (odp_pktio_config(xmt_pktio, &pktio_config)) {
+ ODPH_ERR("pktio configure failed\n");
+ return -1;
+ }
+ }
+
+ /* Add LSO profiles before start */
+ if (lso) {
+ odp_lso_profile_param_t prof_param;
+
+ if (xmt_pktio_capa.lso.proto.ipv4) {
+ odp_lso_profile_param_init(&prof_param);
+ prof_param.lso_proto = ODP_LSO_PROTO_IPV4;
+
+ lso_ipv4_profile = odp_lso_profile_create(xmt_pktio, &prof_param);
+ if (lso_ipv4_profile == ODP_LSO_PROFILE_INVALID) {
+ ODPH_ERR("Failed to create IPv4 LSO profile\n");
+ return -1;
+ }
+ }
+ }
+
+ ret = odp_pktio_start(xmt_pktio);
+ if (ret != 0) {
+ ODPH_ERR("odp_pktio_start() failed\n");
+ return -1;
+ }
+ pktio_started[0] = true;
+
+ /* Now wait until the link or links are up. */
+ rc = wait_linkup(xmt_pktio);
+ if (rc != 1) {
+ ODPH_ERR("link %" PRIX64 " not up\n", odp_pktio_to_u64(xmt_pktio));
+ return -1;
+ }
+
+ if (num_ifaces < 2)
+ return 0;
+
+ /* Wait for 2nd link to be up */
+ rc = wait_linkup(pktios[1]);
+ if (rc != 1) {
+ ODPH_ERR("link %" PRIX64 " not up\n", odp_pktio_to_u64(pktios[1]));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_unique_id(odp_packet_t odp_pkt,
+ uint16_t *unique_id_ptr,
+ uint8_t *is_ipv4_pkt_ptr)
+{
+ odp_u32be_t be_ver_tc_flow;
+ odp_u16be_t be_ip_ident;
+ odp_bool_t is_ipv4;
+ uint32_t l3_offset, ident_offset, flow_offset, ver_tc_flow;
+ uint16_t unique_id;
+
+ l3_offset = odp_packet_l3_offset(odp_pkt);
+
+ if (odp_packet_has_ipv4(odp_pkt)) {
+ /* For IPv4 pkts use the ident field to store the unique_id. */
+ ident_offset = l3_offset + offsetof(odph_ipv4hdr_t, id);
+
+ CU_ASSERT_FATAL(odp_packet_copy_to_mem(odp_pkt, ident_offset, 2,
+ &be_ip_ident) == 0);
+ unique_id = odp_be_to_cpu_16(be_ip_ident);
+ is_ipv4 = true;
+ } else if (odp_packet_has_ipv6(odp_pkt)) {
+ /* For IPv6 pkts use the flow field to store the unique_id. */
+ flow_offset = l3_offset + offsetof(odph_ipv6hdr_t, ver_tc_flow);
+
+ CU_ASSERT_FATAL(odp_packet_copy_to_mem(odp_pkt, flow_offset, 4,
+ &be_ver_tc_flow) == 0);
+ ver_tc_flow = odp_be_to_cpu_32(be_ver_tc_flow);
+ unique_id = ver_tc_flow & ODPH_IPV6HDR_FLOW_LABEL_MASK;
+ is_ipv4 = false;
+ } else {
+ return -1;
+ }
+
+ if (unique_id_ptr != NULL)
+ *unique_id_ptr = unique_id;
+
+ if (is_ipv4_pkt_ptr != NULL)
+ *is_ipv4_pkt_ptr = is_ipv4;
+
+ return 0;
+}
+
+static int get_vlan_tci(odp_packet_t odp_pkt, uint16_t *vlan_tci_ptr)
+{
+ odph_vlanhdr_t *vlan_hdr;
+ odph_ethhdr_t *ether_hdr;
+ uint32_t hdr_len;
+ uint16_t vlan_tci;
+
+ if (!odp_packet_has_vlan(odp_pkt))
+ return -1;
+
+ /* *TBD* check value of hdr_len? */
+ ether_hdr = odp_packet_l2_ptr(odp_pkt, &hdr_len);
+ vlan_hdr = (odph_vlanhdr_t *)(ether_hdr + 1);
+ vlan_tci = odp_be_to_cpu_16(vlan_hdr->tci);
+ if (vlan_tci_ptr != NULL)
+ *vlan_tci_ptr = vlan_tci;
+
+ return 0;
+}
+
+/* Returns either the TOS field for IPv4 pkts or the TC field for IPv6 pkts. */
+static int get_ip_tos(odp_packet_t odp_pkt, uint8_t *tos_ptr)
+{
+ odph_ipv4hdr_t *ipv4_hdr;
+ odph_ipv6hdr_t *ipv6_hdr;
+ uint32_t ver_tc_flow;
+ uint8_t tos, tc;
+ uint32_t hdr_len = 0;
+
+ if (odp_packet_has_ipv4(odp_pkt)) {
+ ipv4_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
+ if (hdr_len < 12)
+ return -1;
+
+ tos = ipv4_hdr->tos;
+ } else if (odp_packet_has_ipv6(odp_pkt)) {
+ ipv6_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len);
+ if (hdr_len < 4)
+ return -1;
+
+ ver_tc_flow = odp_be_to_cpu_32(ipv6_hdr->ver_tc_flow);
+ tc = (ver_tc_flow & ODPH_IPV6HDR_TC_MASK)
+ >> ODPH_IPV6HDR_TC_SHIFT;
+ tos = tc;
+ } else {
+ return -1;
+ }
+
+ if (tos_ptr != NULL)
+ *tos_ptr = tos;
+
+ return 0;
+}
+
+static odp_packet_t make_pkt(odp_pool_t pkt_pool,
+ uint32_t payload_len,
+ uint16_t unique_id,
+ pkt_info_t *pkt_info)
+{
+ odph_vlanhdr_t *vlan_hdr;
+ odph_ipv4hdr_t *ipv4_hdr;
+ odph_ipv6hdr_t *ipv6_hdr;
+ odph_ethhdr_t *eth_hdr;
+ odph_udphdr_t *udp_hdr;
+ odph_tcphdr_t *tcp_hdr;
+ odp_packet_t odp_pkt;
+ uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len;
+ uint32_t l4_len, l3_len, l2_len, pkt_len, l3_offset, l4_offset;
+ uint32_t version, tc, flow, ver_tc_flow, app_offset;
+ uint16_t final_ether_type;
+ uint8_t *buf, *pkt_class_ptr, next_hdr;
+ int rc;
+
+ if (payload_len > MAX_PAYLOAD) {
+ ODPH_ERR("packet payload length of %u exceeds MAX_PAYLOAD of %u\n",
+ payload_len, MAX_PAYLOAD);
+ return ODP_PACKET_INVALID;
+ }
+
+ l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN;
+ l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
+ vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0;
+ l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
+ l4_len = l4_hdr_len + payload_len;
+ l3_len = l3_hdr_len + l4_len;
+ l2_len = l2_hdr_len + l3_len;
+ pkt_len = l2_len;
+ if (unique_id == 0) {
+ ODPH_ERR("%s called with invalid unique_id of 0\n", __func__);
+ return ODP_PACKET_INVALID;
+ }
+
+ odp_pkt = odp_packet_alloc(pkt_pool, pkt_len);
+ if (odp_pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ buf = odp_packet_data(odp_pkt);
+
+ /* Ethernet Header */
+ odp_packet_l2_offset_set(odp_pkt, 0);
+ eth_hdr = (odph_ethhdr_t *)buf;
+ final_ether_type = pkt_info->use_ipv6 ? ODPH_ETHTYPE_IPV6
+ : ODPH_ETHTYPE_IPV4;
+ memcpy(eth_hdr->src.addr, &src_mac, ODPH_ETHADDR_LEN);
+ memcpy(eth_hdr->dst.addr, &dst_mac, ODPH_ETHADDR_LEN);
+
+ /* Vlan Header */
+ if (pkt_info->use_vlan) {
+ odp_packet_has_vlan_set(odp_pkt, 1);
+ eth_hdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN);
+ vlan_hdr = (odph_vlanhdr_t *)(eth_hdr + 1);
+ vlan_hdr->tci = odp_cpu_to_be_16(pkt_info->vlan_tci);
+ vlan_hdr->type = odp_cpu_to_be_16(final_ether_type);
+ } else {
+ eth_hdr->type = odp_cpu_to_be_16(final_ether_type);
+ }
+
+ l3_offset = l2_hdr_len;
+ next_hdr = pkt_info->use_tcp ? ODPH_IPPROTO_TCP : ODPH_IPPROTO_UDP;
+ odp_packet_l3_offset_set(odp_pkt, l3_offset);
+ if (pkt_info->use_ipv6) {
+ /* IPv6 Header */
+ odp_packet_has_ipv6_set(odp_pkt, 1);
+ version = ODPH_IPV6 << ODPH_IPV6HDR_VERSION_SHIFT;
+ tc = pkt_info->ip_tos << ODPH_IPV6HDR_TC_SHIFT;
+ flow = unique_id << ODPH_IPV6HDR_FLOW_LABEL_SHIFT;
+ ver_tc_flow = version | tc | flow;
+
+ ipv6_hdr = (odph_ipv6hdr_t *)(buf + l3_offset);
+ ipv6_hdr->ver_tc_flow = odp_cpu_to_be_32(ver_tc_flow);
+ ipv6_hdr->payload_len = odp_cpu_to_be_16(l4_len);
+ ipv6_hdr->next_hdr = next_hdr;
+ ipv6_hdr->hop_limit = DEFAULT_TTL;
+ memcpy(ipv6_hdr->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN);
+ memcpy(ipv6_hdr->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN);
+ } else {
+ /* IPv4 Header */
+ odp_packet_has_ipv4_set(odp_pkt, 1);
+ ipv4_hdr = (odph_ipv4hdr_t *)(buf + l3_offset);
+ ipv4_hdr->ver_ihl = (ODPH_IPV4 << 4) | ODPH_IPV4HDR_IHL_MIN;
+ ipv4_hdr->tos = pkt_info->ip_tos;
+ ipv4_hdr->tot_len = odp_cpu_to_be_16(l3_len);
+ ipv4_hdr->id = odp_cpu_to_be_16(unique_id);
+ ipv4_hdr->frag_offset = 0;
+ ipv4_hdr->ttl = DEFAULT_TTL;
+ ipv4_hdr->proto = next_hdr;
+ ipv4_hdr->chksum = 0;
+ memcpy(&ipv4_hdr->src_addr, IPV4_SRC_ADDR, ODPH_IPV4ADDR_LEN);
+ memcpy(&ipv4_hdr->dst_addr, IPV4_DST_ADDR, ODPH_IPV4ADDR_LEN);
+ }
+
+ l4_offset = l3_offset + l3_hdr_len;
+ odp_packet_l4_offset_set(odp_pkt, l4_offset);
+ tcp_hdr = (odph_tcphdr_t *)(buf + l4_offset);
+ udp_hdr = (odph_udphdr_t *)(buf + l4_offset);
+
+ if (pkt_info->use_tcp) {
+ /* TCP Header */
+ odp_packet_has_tcp_set(odp_pkt, 1);
+ tcp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_TCP_SRC_PORT);
+ tcp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_TCP_DST_PORT);
+ tcp_hdr->seq_no = odp_cpu_to_be_32(cpu_tcp_seq_num);
+ tcp_hdr->ack_no = odp_cpu_to_be_32(DEFAULT_TCP_ACK_NUM);
+ tcp_hdr->window = odp_cpu_to_be_16(DEFAULT_TCP_WINDOW);
+ tcp_hdr->cksm = 0;
+ tcp_hdr->urgptr = 0;
+
+ tcp_hdr->doffset_flags = 0;
+ tcp_hdr->hl = 5;
+ tcp_hdr->ack = 1;
+ cpu_tcp_seq_num += payload_len;
+ } else {
+ /* UDP Header */
+ odp_packet_has_udp_set(odp_pkt, 1);
+ udp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_UDP_SRC_PORT);
+ udp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_UDP_DST_PORT);
+ udp_hdr->length = odp_cpu_to_be_16(l4_len);
+ udp_hdr->chksum = 0;
+ }
+
+ app_offset = l4_offset + l4_hdr_len;
+ rc = odp_packet_copy_from_mem(odp_pkt, app_offset, payload_len,
+ payload_data);
+ CU_ASSERT_FATAL(rc == 0);
+
+ pkt_class_ptr = odp_packet_offset(odp_pkt, app_offset, NULL, NULL);
+ CU_ASSERT_FATAL(pkt_class_ptr != NULL);
+ *pkt_class_ptr = pkt_info->pkt_class;
+
+ /* Calculate and insert checksums. First the IPv4 header checksum. */
+ if (!pkt_info->use_ipv6)
+ odph_ipv4_csum_update(odp_pkt);
+
+ /* Next the UDP/TCP checksum. */
+ if (odph_udp_tcp_chksum(odp_pkt, ODPH_CHKSUM_GENERATE, NULL) != 0)
+ ODPH_ERR("odph_udp_tcp_chksum failed\n");
+
+ return odp_pkt;
+}
+
+static xmt_pkt_desc_t *find_matching_xmt_pkt_desc(uint16_t unique_id)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ uint32_t xmt_pkt_idx;
+
+ if (unique_id == 0)
+ return NULL;
+
+ for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) {
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
+ if (xmt_pkt_desc->xmt_unique_id == unique_id)
+ return xmt_pkt_desc;
+ }
+
+ return NULL;
+}
+
+static int32_t receive_loop(odp_tm_t tm, odp_pktin_queue_t pktin, uint32_t num_pkts,
+ uint64_t timeout_ns)
+{
+ odp_time_t start_time, current_time;
+ uint64_t duration_ns;
+ uint32_t pkts_rcvd;
+ int rc;
+
+ pkts_rcvd = 0;
+ start_time = odp_time_local();
+ duration_ns = 0;
+
+ while ((pkts_rcvd < num_pkts) || (!odp_tm_is_idle(tm))) {
+ rc = odp_pktin_recv(pktin, &rcv_pkts[pkts_rcvd], 1);
+ if (rc < 0)
+ return -1;
+
+ current_time = odp_time_local();
+ duration_ns = odp_time_diff_ns(current_time, start_time);
+
+ if (rc == 1)
+ rcv_pkt_descs[pkts_rcvd++].rcv_time = current_time;
+ else if (timeout_ns < duration_ns)
+ break;
+ }
+
+ return pkts_rcvd;
+}
+
+static int receive_pkts(odp_tm_t tm, odp_pktin_queue_t pktin, uint32_t num_pkts,
+ uint64_t rate_bps)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ rcv_pkt_desc_t *rcv_pkt_desc;
+ odp_packet_t rcv_pkt;
+ odp_time_t xmt_time;
+ odp_time_t rcv_time, delta_time;
+ uint64_t delta_ns, tmp, timeout_ns;
+ uint32_t pkts_rcvd, rcv_idx, l4_offset, l4_hdr_len, app_offset;
+ uint16_t unique_id;
+ uint8_t *pkt_class_ptr, pkt_class, is_ipv4_pkt;
+ int32_t rc;
+
+ tmp = (1000000ULL * 10000ULL * (uint64_t)num_pkts) / rate_bps;
+ timeout_ns = 1000ULL * ((4ULL * tmp) + 10000ULL);
+
+ rc = receive_loop(tm, pktin, num_pkts, timeout_ns);
+ if (rc < 0)
+ return -1;
+
+ pkts_rcvd = rc;
+
+ /* Now go through matching the rcv pkts to the xmt pkts, determining
+ * which xmt_pkts were lost and for the ones that did arrive, how
+ * long did they take. We don't do this work while receiving the pkts
+ * in the loop above because we want to try to get as accurate a
+ * rcv timestamp as possible. */
+ for (rcv_idx = 0; rcv_idx < pkts_rcvd; rcv_idx++) {
+ rcv_pkt = rcv_pkts[rcv_idx];
+ rcv_pkt_desc = &rcv_pkt_descs[rcv_idx];
+
+ if (odp_packet_has_error(rcv_pkt)) {
+ rcv_pkt_desc->errors = 0x01 |
+ (odp_packet_has_l2_error(rcv_pkt) << 1) |
+ (odp_packet_has_l3_error(rcv_pkt) << 2) |
+ (odp_packet_has_l4_error(rcv_pkt) << 3);
+
+ ODPH_ERR("received a pkt with the following errors\n");
+ ODPH_ERR(" l2_err=%u l3_err=%u l4_err=%u. "
+ "Skipping\n",
+ (rcv_pkt_desc->errors >> 1) & 0x1,
+ (rcv_pkt_desc->errors >> 2) & 0x1,
+ (rcv_pkt_desc->errors >> 3) & 0x1);
+ }
+
+ unique_id = 0;
+ rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4_pkt);
+ if (rc != 0) {
+ ODPH_ERR("received a non IPv4/IPv6 pkt\n");
+ return -1;
+ }
+
+ rcv_pkt_desc->rcv_unique_id = unique_id;
+ rcv_pkt_desc->is_ipv4_pkt = is_ipv4_pkt;
+ if (odp_packet_has_udp(rcv_pkt))
+ l4_hdr_len = ODPH_UDPHDR_LEN;
+ else if (odp_packet_has_tcp(rcv_pkt))
+ l4_hdr_len = ODPH_TCPHDR_LEN;
+ else
+ l4_hdr_len = 0;
+
+ l4_offset = odp_packet_l4_offset(rcv_pkt);
+ app_offset = l4_offset + l4_hdr_len;
+ pkt_class_ptr = odp_packet_offset(rcv_pkt, app_offset,
+ NULL, NULL);
+ if (pkt_class_ptr != NULL)
+ rcv_pkt_desc->pkt_class = *pkt_class_ptr;
+
+ xmt_pkt_desc = find_matching_xmt_pkt_desc(unique_id);
+ if (xmt_pkt_desc != NULL) {
+ rcv_pkt_desc->xmt_pkt_desc = xmt_pkt_desc;
+ rcv_pkt_desc->matched = true;
+
+ xmt_time = xmt_pkt_desc->xmt_time;
+ rcv_time = rcv_pkt_desc->rcv_time;
+ pkt_class = rcv_pkt_desc->pkt_class;
+ delta_time = odp_time_diff(rcv_time, xmt_time);
+ delta_ns = odp_time_to_ns(delta_time);
+
+ rcv_pkt_desc->xmt_idx = xmt_pkt_desc->xmt_idx;
+ xmt_pkt_desc->rcv_time = rcv_time;
+ xmt_pkt_desc->delta_ns = delta_ns;
+ xmt_pkt_desc->pkt_class = pkt_class;
+ xmt_pkt_desc->was_rcvd = 1;
+ }
+ }
+
+ return pkts_rcvd;
+}
+
+static void dump_rcvd_pkts(uint32_t first_rcv_idx, uint32_t last_rcv_idx)
+{
+ rcv_pkt_desc_t *rcv_pkt_desc;
+ odp_packet_t rcv_pkt;
+ uint32_t rcv_idx;
+ int32_t xmt_idx;
+ uint16_t unique_id = 0;
+ uint8_t is_ipv4 = 0;
+ int rc;
+
+ for (rcv_idx = first_rcv_idx; rcv_idx <= last_rcv_idx; rcv_idx++) {
+ rcv_pkt = rcv_pkts[rcv_idx];
+ rcv_pkt_desc = &rcv_pkt_descs[rcv_idx];
+ rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4);
+ xmt_idx = -1;
+ if (rcv_pkt_desc->matched)
+ xmt_idx = rcv_pkt_desc->xmt_pkt_desc->xmt_idx;
+
+ printf("rcv_idx=%" PRIu32 " odp_pkt=0x%" PRIX64 " "
+ "xmt_idx=%" PRId32 " pkt_class=%u is_ipv4=%u "
+ "unique_id=0x%X (rc=%d)\n",
+ rcv_idx, odp_packet_to_u64(rcv_pkt), xmt_idx,
+ rcv_pkt_desc->pkt_class, is_ipv4, unique_id, rc);
+ }
+}
+
+static void free_rcvd_pkts(void)
+{
+ odp_packet_t rcv_pkt;
+ uint32_t rcv_idx;
+
+ /* Go through all of the received pkts and free them. */
+ for (rcv_idx = 0; rcv_idx < num_rcv_pkts; rcv_idx++) {
+ rcv_pkt = rcv_pkts[rcv_idx];
+ if (rcv_pkt != ODP_PACKET_INVALID) {
+ odp_packet_free(rcv_pkt);
+ rcv_pkts[rcv_idx] = ODP_PACKET_INVALID;
+ }
+ }
+}
+
+static void flush_leftover_pkts(odp_tm_t odp_tm, odp_pktin_queue_t pktin)
+{
+ odp_packet_t rcv_pkt;
+ odp_time_t start_time, current_time, duration;
+ uint64_t min_timeout_ns, max_timeout_ns, duration_ns;
+ int rc;
+
+ /* Set the timeout to be at least 10 milliseconds and at most 100
+ * milliseconds */
+ min_timeout_ns = 10 * ODP_TIME_MSEC_IN_NS;
+ max_timeout_ns = 100 * ODP_TIME_MSEC_IN_NS;
+ start_time = odp_time_local();
+
+ while (true) {
+ rc = odp_pktin_recv(pktin, &rcv_pkt, 1);
+ if (rc == 1)
+ odp_packet_free(rcv_pkt);
+
+ current_time = odp_time_local();
+ duration = odp_time_diff(current_time, start_time);
+ duration_ns = odp_time_to_ns(duration);
+
+ if (max_timeout_ns <= duration_ns)
+ break;
+ else if (duration_ns < min_timeout_ns)
+ ;
+ else if ((odp_tm_is_idle(odp_tm)) && (rc == 0))
+ break;
+
+ /* Busy wait here a little bit to prevent overwhelming the
+ * odp_pktin_recv logic. */
+ busy_wait(10000);
+ }
+}
+
+static void init_xmt_pkts(pkt_info_t *pkt_info)
+{
+ memset(xmt_pkts, 0, sizeof(xmt_pkts));
+ memset(xmt_pkt_descs, 0, sizeof(xmt_pkt_descs));
+ num_pkts_made = 0;
+ num_pkts_sent = 0;
+
+ free_rcvd_pkts();
+ memset(rcv_pkts, 0, sizeof(rcv_pkts));
+ memset(rcv_pkt_descs, 0, sizeof(rcv_pkt_descs));
+ num_rcv_pkts = 0;
+
+ memset(rcv_gaps, 0, sizeof(rcv_gaps));
+ rcv_gap_cnt = 0;
+ memset(pkt_info, 0, sizeof(pkt_info_t));
+ pkt_info->ip_tos = DEFAULT_TOS;
+}
+
+static int make_pkts(uint32_t num_pkts,
+ uint32_t pkt_len,
+ pkt_info_t *pkt_info)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_packet_t odp_pkt;
+ uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len;
+ uint32_t hdrs_len, payload_len, idx, unique_id, xmt_pkt_idx;
+
+ l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN;
+ l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN;
+ vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0;
+ l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len;
+
+ hdrs_len = l2_hdr_len + l3_hdr_len + l4_hdr_len;
+ payload_len = pkt_len - hdrs_len;
+
+ for (idx = 0; idx < num_pkts; idx++) {
+ unique_id = cpu_unique_id++;
+ xmt_pkt_idx = num_pkts_made++;
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
+ xmt_pkt_desc->pkt_len = pkt_len;
+ xmt_pkt_desc->xmt_unique_id = unique_id;
+ xmt_pkt_desc->pkt_class = pkt_info->pkt_class;
+
+ odp_pkt = make_pkt(pools[0], payload_len, unique_id, pkt_info);
+ if (odp_pkt == ODP_PACKET_INVALID)
+ return -1;
+
+ odp_packet_color_set(odp_pkt, pkt_info->pkt_color);
+ odp_packet_drop_eligible_set(odp_pkt, pkt_info->drop_eligible);
+ odp_packet_shaper_len_adjust_set(odp_pkt, SHAPER_LEN_ADJ);
+
+ xmt_pkts[xmt_pkt_idx] = odp_pkt;
+ }
+
+ return 0;
+}
+
+static uint32_t send_pkts_multi(odp_tm_queue_t tm_queue, uint32_t num_pkts)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_packet_t odp_pkt;
+ uint32_t xmt_pkt_idx, pkts_sent;
+ int64_t rc, i = 0;
+
+ /* Now send the pkts as fast as we can. RED drops are internally
+ * consumed by odp_tm_enq_multi().
+ */
+ xmt_pkt_idx = num_pkts_sent;
+ rc = odp_tm_enq_multi(tm_queue, &xmt_pkts[xmt_pkt_idx], num_pkts);
+ CU_ASSERT(rc >= 0);
+ CU_ASSERT(rc <= num_pkts);
+
+ /* Record consumed packets */
+ pkts_sent = 0;
+ for (i = 0; i < rc; i++) {
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx + i];
+ xmt_pkt_desc->xmt_idx = xmt_pkt_idx + i;
+ xmt_pkt_desc->xmt_time = odp_time_local();
+ xmt_pkt_desc->tm_queue = tm_queue;
+ pkts_sent++;
+ }
+
+ /* Free rejected pkts */
+ for (; i < num_pkts; i++) {
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx + i];
+ xmt_pkt_desc->xmt_idx = xmt_pkt_idx + i;
+
+ odp_pkt = xmt_pkts[xmt_pkt_idx + i];
+ odp_packet_free(odp_pkt);
+ xmt_pkts[xmt_pkt_idx + i] = ODP_PACKET_INVALID;
+ }
+ num_pkts_sent += num_pkts;
+
+ return pkts_sent;
+}
+
+static uint32_t send_pkts(odp_tm_queue_t tm_queue, uint32_t num_pkts)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_packet_t odp_pkt;
+ uint32_t idx, xmt_pkt_idx, pkts_sent;
+ int rc;
+
+ /* Now send the pkts as fast as we can. */
+ pkts_sent = 0;
+ for (idx = 0; idx < num_pkts; idx++) {
+ xmt_pkt_idx = num_pkts_sent;
+ odp_pkt = xmt_pkts[xmt_pkt_idx];
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
+
+ /* Alternate calling with odp_tm_enq and odp_tm_enq_with_cnt */
+ if ((idx & 1) == 0) {
+ rc = odp_tm_enq(tm_queue, odp_pkt);
+ CU_ASSERT(rc <= 0);
+ } else {
+ rc = odp_tm_enq_with_cnt(tm_queue, odp_pkt);
+ }
+
+ xmt_pkt_desc->xmt_idx = xmt_pkt_idx;
+ if (0 <= rc) {
+ xmt_pkt_desc->xmt_time = odp_time_local();
+ xmt_pkt_desc->tm_queue = tm_queue;
+ pkts_sent++;
+ } else {
+ odp_packet_free(odp_pkt);
+ xmt_pkts[xmt_pkt_idx] = ODP_PACKET_INVALID;
+ }
+
+ num_pkts_sent++;
+ }
+
+ return pkts_sent;
+}
+
+static uint32_t send_pkts_lso(odp_tm_queue_t tm_queue, uint32_t num_pkts,
+ odp_lso_protocol_t lso_proto, uint32_t max_len)
+{
+ odp_packet_lso_opt_t lso_opt;
+ odp_lso_profile_t profile;
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_packet_t pkt;
+ uint32_t offset, pkts_sent;
+ int64_t rc, i, idx;
+
+ pkt = xmt_pkts[0];
+
+ if (lso_proto == ODP_LSO_PROTO_IPV4) {
+ profile = lso_ipv4_profile;
+ offset = odp_packet_l4_offset(pkt);
+ } else {
+ ODPH_ERR("Bad LSO protocol\n");
+ return 0;
+ }
+
+ lso_opt.lso_profile = profile;
+ lso_opt.payload_offset = offset;
+ lso_opt.max_payload_len = max_len;
+
+ pkts_sent = 0;
+
+ for (i = 0; i < num_pkts; i++) {
+ idx = num_pkts_sent + i;
+ pkt = xmt_pkts[idx];
+ xmt_pkt_desc = &xmt_pkt_descs[idx];
+
+ rc = odp_tm_enq_multi_lso(tm_queue, &pkt, 1, &lso_opt);
+
+ CU_ASSERT(rc == 0 || rc == 1);
+
+ if (rc < 0) {
+ ODPH_ERR("Enqueue LSO failed\n");
+ num_pkts_sent += i;
+ return pkts_sent;
+ }
+
+ xmt_pkt_desc->xmt_idx = idx;
+
+ if (rc > 0) {
+ /* Record consumed packets */
+ xmt_pkt_desc->xmt_time = odp_time_local();
+ xmt_pkt_desc->tm_queue = tm_queue;
+ pkts_sent++;
+ } else {
+ /* Free rejected pkts */
+ odp_packet_free(pkt);
+ xmt_pkts[idx] = ODP_PACKET_INVALID;
+ }
+ }
+
+ num_pkts_sent += num_pkts;
+
+ return pkts_sent;
+}
+
+static uint32_t pkts_rcvd_in_send_order(void)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_time_t last_rcv_time, rcv_time;
+ uint32_t xmt_pkt_idx, pkts_rcvd;
+
+ pkts_rcvd = 0;
+ last_rcv_time = ODP_TIME_NULL;
+ for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) {
+ xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx];
+ rcv_time = xmt_pkt_desc->rcv_time;
+ if (xmt_pkt_desc->was_rcvd != 0) {
+ if ((pkts_rcvd != 0) &&
+ (odp_time_cmp(rcv_time, last_rcv_time) < 0))
+ return 0;
+
+ pkts_rcvd++;
+ last_rcv_time = xmt_pkt_desc->rcv_time;
+ }
+ }
+
+ return pkts_rcvd;
+}
+
+static int unique_id_list_idx(uint32_t unique_id,
+ uint32_t unique_id_list[],
+ uint32_t unique_id_list_len)
+{
+ uint32_t idx;
+
+ for (idx = 0; idx < unique_id_list_len; idx++)
+ if (unique_id_list[idx] == unique_id)
+ return idx;
+
+ return -1;
+}
+
+static uint32_t pkts_rcvd_in_given_order(uint32_t unique_id_list[],
+ uint32_t unique_id_list_len,
+ uint8_t pkt_class,
+ odp_bool_t match_pkt_class,
+ odp_bool_t ignore_pkt_class)
+{
+ rcv_pkt_desc_t *rcv_pkt_desc;
+ odp_bool_t is_match;
+ uint32_t rcv_pkt_idx, pkts_in_order;
+ uint32_t rcv_unique_id;
+ int last_pkt_idx, pkt_idx;
+
+ pkts_in_order = 1;
+ last_pkt_idx = -1;
+ pkt_idx = -1;
+
+ for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
+ rcv_pkt_desc = &rcv_pkt_descs[rcv_pkt_idx];
+
+ if (ignore_pkt_class)
+ is_match = true;
+ else if (match_pkt_class)
+ is_match = rcv_pkt_desc->pkt_class == pkt_class;
+ else
+ is_match = rcv_pkt_desc->pkt_class != pkt_class;
+
+ if (is_match) {
+ rcv_unique_id = rcv_pkt_desc->rcv_unique_id;
+ pkt_idx = unique_id_list_idx(rcv_unique_id,
+ unique_id_list,
+ unique_id_list_len);
+ if (0 <= pkt_idx) {
+ if (0 <= last_pkt_idx && last_pkt_idx < pkt_idx)
+ pkts_in_order++;
+
+ last_pkt_idx = pkt_idx;
+ }
+ }
+ }
+
+ return pkts_in_order;
+}
+
+static inline void record_rcv_gap(odp_time_t rcv_time, odp_time_t last_rcv_time)
+{
+ odp_time_t delta_time;
+ uint64_t delta_ns;
+ uint32_t rcv_gap;
+
+ rcv_gap = 0;
+ if (odp_time_cmp(last_rcv_time, rcv_time) <= 0) {
+ delta_time = odp_time_diff(rcv_time, last_rcv_time);
+ delta_ns = odp_time_to_ns(delta_time);
+ rcv_gap = delta_ns / 1000;
+ }
+
+ /* Note that rcv_gap is in units of microseconds. */
+ rcv_gaps[rcv_gap_cnt++] = rcv_gap;
+}
+
+static int rcv_gap_cmp(const void *left_ptr, const void *right_ptr)
+{
+ uint32_t left_value, right_value;
+
+ left_value = * (const uint32_t *)left_ptr;
+ right_value = * (const uint32_t *)right_ptr;
+
+ if (left_value < right_value)
+ return -1;
+ else if (left_value == right_value)
+ return 0;
+ else
+ return 1;
+}
+
+static inline void calc_rcv_stats(rcv_stats_t *rcv_stats,
+ uint32_t initial_drop_percent,
+ uint32_t ending_drop_percent)
+{
+ uint32_t first_rcv_gap_idx, last_rcv_gap_idx, idx, rcv_gap;
+
+ /* Sort the rcv_gaps, and then drop the outlying x values before doing
+ * doing the rcv stats on the remaining */
+ qsort(&rcv_gaps[0], rcv_gap_cnt, sizeof(uint32_t), rcv_gap_cmp);
+
+ /* Next we drop the outlying values before doing doing the rcv stats
+ * on the remaining rcv_gap values. The number of initial (very low)
+ * rcv_gaps dropped and the number of ending (very high) rcv_gaps
+ * drops is based on the percentages passed in. */
+ first_rcv_gap_idx = (rcv_gap_cnt * initial_drop_percent) / 100;
+ last_rcv_gap_idx = (rcv_gap_cnt * (100 - ending_drop_percent)) / 100;
+ for (idx = first_rcv_gap_idx; idx <= last_rcv_gap_idx; idx++) {
+ rcv_gap = rcv_gaps[idx];
+ rcv_stats->min_rcv_gap = ODPH_MIN(rcv_stats->min_rcv_gap, rcv_gap);
+ rcv_stats->max_rcv_gap = ODPH_MAX(rcv_stats->max_rcv_gap, rcv_gap);
+ rcv_stats->total_rcv_gap += rcv_gap;
+ rcv_stats->total_rcv_gap_squared += rcv_gap * rcv_gap;
+ rcv_stats->num_samples++;
+ }
+}
+
+static int rcv_rate_stats(rcv_stats_t *rcv_stats, uint8_t pkt_class)
+{
+ xmt_pkt_desc_t *xmt_pkt_desc;
+ odp_time_t last_rcv_time, rcv_time;
+ uint32_t pkt_idx, pkts_rcvd, num;
+ uint32_t avg, variance, std_dev;
+
+ pkts_rcvd = 0;
+ last_rcv_time = ODP_TIME_NULL;
+ memset(rcv_stats, 0, sizeof(rcv_stats_t));
+ rcv_stats->min_rcv_gap = 1000000000;
+
+ for (pkt_idx = 0; pkt_idx < num_pkts_sent; pkt_idx++) {
+ xmt_pkt_desc = &xmt_pkt_descs[pkt_idx];
+ if ((xmt_pkt_desc->was_rcvd != 0) &&
+ (xmt_pkt_desc->pkt_class == pkt_class)) {
+ rcv_time = xmt_pkt_desc->rcv_time;
+ if (pkts_rcvd != 0)
+ record_rcv_gap(rcv_time, last_rcv_time);
+ pkts_rcvd++;
+ last_rcv_time = rcv_time;
+ }
+ }
+
+ if (pkts_rcvd == 0)
+ return -1;
+
+ calc_rcv_stats(rcv_stats, INITIAL_RCV_GAP_DROP, ENDING_RCV_GAP_DROP);
+ num = rcv_stats->num_samples;
+ if (num == 0)
+ return -1;
+
+ avg = rcv_stats->total_rcv_gap / num;
+ variance = (rcv_stats->total_rcv_gap_squared / num) - avg * avg;
+ std_dev = (uint32_t)sqrt((double)variance);
+
+ rcv_stats->avg_rcv_gap = avg;
+ rcv_stats->std_dev_gap = std_dev;
+ return 0;
+}
+
+static int create_tm_queue(odp_tm_t odp_tm,
+ odp_tm_node_t tm_node,
+ uint32_t node_idx,
+ tm_queue_desc_t *queue_desc,
+ uint32_t priority)
+{
+ odp_tm_queue_params_t queue_params;
+ odp_tm_queue_t tm_queue;
+ odp_tm_wred_t green_profile, yellow_profile, red_profile;
+ int rc;
+
+ odp_tm_queue_params_init(&queue_params);
+ queue_params.priority = priority;
+ if (priority == 0) {
+ green_profile = wred_profiles[node_idx][PKT_GREEN];
+ yellow_profile = wred_profiles[node_idx][PKT_YELLOW];
+ red_profile = wred_profiles[node_idx][PKT_RED];
+
+ queue_params.shaper_profile = shaper_profiles[0];
+ queue_params.threshold_profile = threshold_profiles[0];
+ queue_params.wred_profile[PKT_GREEN] = green_profile;
+ queue_params.wred_profile[PKT_YELLOW] = yellow_profile;
+ queue_params.wred_profile[PKT_RED] = red_profile;
+ queue_params.ordered_enqueue = true;
+ }
+
+ tm_queue = odp_tm_queue_create(odp_tm, &queue_params);
+ if (tm_queue == ODP_TM_INVALID) {
+ ODPH_ERR("odp_tm_queue_create() failed\n");
+ return -1;
+ }
+
+ queue_desc->tm_queues[priority] = tm_queue;
+ rc = odp_tm_queue_connect(tm_queue, tm_node);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_connect() failed for queue %" PRIx64
+ "\n", odp_tm_queue_to_u64(tm_queue));
+ odp_tm_queue_destroy(tm_queue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int destroy_tm_queue(odp_tm_queue_t tm_queue)
+{
+ odp_tm_queue_disconnect(tm_queue);
+ return odp_tm_queue_destroy(tm_queue);
+}
+
+static tm_node_desc_t *create_tm_node(odp_tm_t odp_tm,
+ uint32_t level,
+ uint32_t num_levels,
+ uint32_t node_idx,
+ tm_node_desc_t *parent_node_desc)
+{
+ odp_tm_node_params_t node_params;
+ tm_queue_desc_t *queue_desc;
+ tm_node_desc_t *node_desc;
+ odp_tm_wred_t green_profile, yellow_profile, red_profile;
+ odp_tm_node_t tm_node, parent_node;
+ uint32_t node_desc_size, queue_desc_size, priority;
+ char node_name[TM_NAME_LEN];
+ int rc;
+
+ odp_tm_node_params_init(&node_params);
+ node_params.shaper_profile = ODP_TM_INVALID;
+ node_params.threshold_profile = ODP_TM_INVALID;
+ node_params.wred_profile[PKT_GREEN] = ODP_TM_INVALID;
+ node_params.wred_profile[PKT_YELLOW] = ODP_TM_INVALID;
+ node_params.wred_profile[PKT_RED] = ODP_TM_INVALID;
+ if (node_idx == 0) {
+ node_params.shaper_profile = shaper_profiles[0];
+ node_params.threshold_profile = threshold_profiles[0];
+ if (level == num_levels) {
+ green_profile = wred_profiles[node_idx][PKT_GREEN];
+ yellow_profile = wred_profiles[node_idx][PKT_YELLOW];
+ red_profile = wred_profiles[node_idx][PKT_RED];
+
+ node_params.wred_profile[PKT_GREEN] = green_profile;
+ node_params.wred_profile[PKT_YELLOW] = yellow_profile;
+ node_params.wred_profile[PKT_RED] = red_profile;
+ }
+ }
+
+ node_params.max_fanin = FANIN_RATIO;
+ node_params.level = level;
+ /* This is ignored when pkt priority mode is not overwrite */
+ node_params.priority = 0;
+
+ if (parent_node_desc == NULL)
+ snprintf(node_name, sizeof(node_name), "node_%" PRIu32,
+ node_idx + 1);
+ else
+ snprintf(node_name, sizeof(node_name), "%s_%" PRIu32,
+ parent_node_desc->node_name, node_idx + 1);
+
+ tm_node = odp_tm_node_create(odp_tm, node_name, &node_params);
+ if (tm_node == ODP_TM_INVALID) {
+ ODPH_ERR("odp_tm_node_create() failed @ level=%" PRIu32 "\n",
+ level);
+ return NULL;
+ }
+
+ /* Now connect this node to the lower level "parent" node. */
+ if (level == 0 || !parent_node_desc)
+ parent_node = ODP_TM_ROOT;
+ else
+ parent_node = parent_node_desc->node;
+
+ rc = odp_tm_node_connect(tm_node, parent_node);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_connect() failed @ level=%" PRIu32 "\n",
+ level);
+ odp_tm_node_destroy(tm_node);
+ return NULL;
+ }
+
+ node_desc_size = sizeof(tm_node_desc_t) +
+ sizeof(odp_tm_node_t) * FANIN_RATIO;
+ node_desc = malloc(node_desc_size);
+ memset(node_desc, 0, node_desc_size);
+ node_desc->level = level;
+ node_desc->node_idx = node_idx;
+ node_desc->num_children = FANIN_RATIO;
+ node_desc->node = tm_node;
+ node_desc->parent_node = parent_node;
+ node_desc->node_name = strdup(node_name);
+
+ /* Finally if the level is the highest then make fanin_ratio tm_queues
+ * feeding this node. */
+ if (level < (num_levels - 1))
+ return node_desc;
+
+ node_desc->num_children = 0;
+ queue_desc_size = sizeof(tm_queue_desc_t) +
+ sizeof(odp_tm_queue_t) * NUM_QUEUES_PER_NODE;
+ queue_desc = malloc(queue_desc_size);
+ memset(queue_desc, 0, queue_desc_size);
+ queue_desc->num_queues = NUM_QUEUES_PER_NODE;
+ node_desc->queue_desc = queue_desc;
+
+ for (priority = 0; priority < NUM_QUEUES_PER_NODE; priority++) {
+ rc = create_tm_queue(odp_tm, tm_node, node_idx, queue_desc,
+ priority);
+ if (rc != 0) {
+ ODPH_ERR("create_tm_queue() failed @ "
+ "level=%" PRIu32 "\n", level);
+ while (priority > 0)
+ (void)destroy_tm_queue
+ (queue_desc->tm_queues[--priority]);
+ free(queue_desc);
+ free(node_desc);
+ return NULL;
+ }
+ }
+
+ return node_desc;
+}
+
+static tm_node_desc_t *create_tm_subtree(odp_tm_t odp_tm,
+ uint32_t level,
+ uint32_t num_levels,
+ uint32_t node_idx,
+ tm_node_desc_t *parent_node)
+{
+ tm_node_desc_t *node_desc, *child_desc;
+ uint32_t child_idx;
+
+ node_desc = create_tm_node(odp_tm, level, num_levels,
+ node_idx, parent_node);
+ if (node_desc == NULL) {
+ ODPH_ERR("create_tm_node() failed @ level=%" PRIu32 "\n",
+ level);
+ return NULL;
+ }
+
+ if (level < (num_levels - 1)) {
+ for (child_idx = 0; child_idx < FANIN_RATIO; child_idx++) {
+ child_desc = create_tm_subtree(odp_tm, level + 1,
+ num_levels, child_idx,
+ node_desc);
+ if (child_desc == NULL) {
+ ODPH_ERR("%s failed level=%" PRIu32 "\n",
+ __func__, level);
+
+ return NULL;
+ }
+
+ node_desc->children[child_idx] = child_desc;
+ }
+ }
+
+ return node_desc;
+}
+
+static odp_tm_node_t find_tm_node(uint8_t tm_system_idx, const char *node_name)
+{
+ return odp_tm_node_lookup(odp_tm_systems[tm_system_idx], node_name);
+}
+
+static tm_node_desc_t *find_node_desc(uint8_t tm_system_idx,
+ const char *node_name)
+{
+ tm_node_desc_t *node_desc;
+ uint32_t child_num;
+ char *name_ptr;
+
+ /* Assume node_name is "node_" followed by a sequence of integers
+ * separated by underscores, where each integer is the child number to
+ * get to the next level node. */
+ node_desc = root_node_descs[tm_system_idx];
+ name_ptr = strchr(node_name, '_');
+ if (name_ptr == NULL)
+ return NULL;
+
+ /* Skip over the first integer */
+ name_ptr++;
+ name_ptr = strchr(name_ptr, '_');
+ if (name_ptr != NULL)
+ name_ptr++;
+
+ while (node_desc != NULL) {
+ if (strncmp(node_desc->node_name, node_name, TM_NAME_LEN) == 0)
+ return node_desc;
+
+ if (name_ptr == NULL)
+ return NULL;
+
+ child_num = atoi(name_ptr);
+ if (node_desc->num_children < child_num)
+ return NULL;
+
+ node_desc = node_desc->children[child_num - 1];
+ name_ptr = strchr(name_ptr, '_');
+ if (name_ptr != NULL)
+ name_ptr++;
+ }
+
+ return NULL;
+}
+
+static odp_tm_queue_t find_tm_queue(uint8_t tm_system_idx,
+ const char *node_name,
+ uint8_t priority)
+{
+ tm_queue_desc_t *queue_desc;
+ tm_node_desc_t *node_desc;
+
+ node_desc = find_node_desc(tm_system_idx, node_name);
+ if (node_desc == NULL)
+ return ODP_TM_INVALID;
+
+ queue_desc = node_desc->queue_desc;
+ if (queue_desc == NULL)
+ return ODP_TM_INVALID;
+
+ return queue_desc->tm_queues[priority];
+}
+
+static uint32_t find_child_queues(uint8_t tm_system_idx,
+ tm_node_desc_t *node_desc,
+ uint8_t priority,
+ odp_tm_queue_t tm_queues[],
+ uint32_t max_queues)
+{
+ tm_queue_desc_t *queue_desc;
+ tm_node_desc_t *child_node_desc;
+ uint32_t num_children, num_queues, child_idx, rem_queues;
+
+ if (max_queues == 0)
+ return 0;
+
+ queue_desc = node_desc->queue_desc;
+ if (queue_desc != NULL) {
+ tm_queues[0] = queue_desc->tm_queues[priority];
+ return 1;
+ }
+
+ num_children = node_desc->num_children;
+ num_queues = 0;
+
+ for (child_idx = 0; child_idx < num_children; child_idx++) {
+ child_node_desc = node_desc->children[child_idx];
+ rem_queues = max_queues - num_queues;
+ num_queues += find_child_queues(tm_system_idx, child_node_desc,
+ priority,
+ &tm_queues[num_queues],
+ rem_queues);
+ if (num_queues == max_queues)
+ break;
+ }
+
+ return num_queues;
+}
+
+static void
+set_reqs_based_on_capas(odp_tm_requirements_t *req)
+{
+ odp_packet_color_t color;
+ int j;
+
+ /* Use tm capabilities identified based on egress capabilities
+ * to see what can be enabled.
+ */
+ if (tm_capabilities.ecn_marking_supported)
+ req->ecn_marking_needed = true;
+ if (tm_capabilities.drop_prec_marking_supported)
+ req->drop_prec_marking_needed = true;
+ if (tm_capabilities.tm_queue_wred_supported)
+ req->tm_queue_wred_needed = true;
+ if (tm_capabilities.tm_queue_dual_slope_supported)
+ req->tm_queue_dual_slope_needed = true;
+ if (tm_capabilities.vlan_marking_supported)
+ req->vlan_marking_needed = true;
+ if (tm_capabilities.tm_queue_threshold.byte ||
+ tm_capabilities.tm_queue_threshold.packet ||
+ tm_capabilities.tm_queue_threshold.byte_and_packet)
+ req->tm_queue_threshold_needed = true;
+
+ for (j = 0; j < tm_capabilities.max_levels; j++) {
+ if (tm_capabilities.per_level[j].tm_node_threshold.byte ||
+ tm_capabilities.per_level[j].tm_node_threshold.packet ||
+ tm_capabilities.per_level[j].tm_node_threshold.byte_and_packet)
+ req->per_level[j].tm_node_threshold_needed = true;
+ }
+
+ /* Mark colors as needed if at least one of the marking
+ * feature is needed.
+ * */
+ if (req->ecn_marking_needed || req->drop_prec_marking_needed) {
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++)
+ req->marking_colors_needed[color] = true;
+ }
+
+ if (tm_capabilities.tm_queue_shaper_supported ||
+ tm_capabilities.tm_queue_rate_limiter_supported)
+ req->tm_queue_shaper_needed = true;
+
+ /* We can use any packet priority mode since it does not affect
+ * our tests. Our scheduler test tests scheduling only in a node
+ * directly connected to TM queues and such nodes see the original
+ * packet priority before it could have been overwritten by any node.
+ */
+ req->pkt_prio_mode = ODP_TM_PKT_PRIO_MODE_PRESERVE;
+ if (!tm_capabilities.pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE])
+ req->pkt_prio_mode = ODP_TM_PKT_PRIO_MODE_OVERWRITE;
+
+}
+
+static int create_tm_system(void)
+{
+ odp_tm_level_requirements_t *per_level;
+ odp_tm_requirements_t requirements;
+ odp_tm_egress_t egress;
+ tm_node_desc_t *root_node_desc;
+ uint32_t level, max_nodes[ODP_TM_MAX_LEVELS];
+ odp_tm_t odp_tm, found_odp_tm;
+ char tm_name[TM_NAME_LEN];
+ int rc;
+
+ odp_tm_requirements_init(&requirements);
+ odp_tm_egress_init(&egress);
+
+ requirements.max_tm_queues = NUM_TM_QUEUES;
+ requirements.num_levels = NUM_LEVELS;
+
+ set_reqs_based_on_capas(&requirements);
+
+ /* Set the max_num_tm_nodes to be double the expected number of nodes
+ * at that level */
+ memset(max_nodes, 0, sizeof(max_nodes));
+ max_nodes[0] = 2 * NUM_LEVEL0_TM_NODES;
+ max_nodes[1] = 2 * NUM_LEVEL1_TM_NODES;
+ max_nodes[2] = 2 * NUM_LEVEL2_TM_NODES;
+ max_nodes[3] = 2 * NUM_LEVEL2_TM_NODES * FANIN_RATIO;
+
+ for (level = 0; level < NUM_LEVELS; level++) {
+ per_level = &requirements.per_level[level];
+ per_level->max_priority = NUM_PRIORITIES - 1;
+ per_level->max_num_tm_nodes = max_nodes[level];
+ per_level->max_fanin_per_node = FANIN_RATIO;
+ per_level->tm_node_shaper_needed = true;
+ per_level->tm_node_wred_needed = false;
+ per_level->tm_node_dual_slope_needed = false;
+ per_level->fair_queuing_needed = true;
+ per_level->weights_needed = true;
+ }
+
+ egress.egress_kind = ODP_TM_EGRESS_PKT_IO;
+ egress.pktio = xmt_pktio;
+
+ snprintf(tm_name, sizeof(tm_name), "TM_system_%" PRIu32,
+ num_odp_tm_systems);
+ odp_tm = odp_tm_create(tm_name, &requirements, &egress);
+ CU_ASSERT_FATAL(odp_tm != ODP_TM_INVALID);
+
+
+ odp_tm_systems[num_odp_tm_systems] = odp_tm;
+
+ root_node_desc = create_tm_subtree(odp_tm, 0, NUM_LEVELS, 0, NULL);
+ root_node_descs[num_odp_tm_systems] = root_node_desc;
+ if (root_node_desc == NULL) {
+ ODPH_ERR("create_tm_subtree() failed\n");
+ return -1;
+ }
+
+ num_odp_tm_systems++;
+
+ /* Test odp_tm_capability and odp_tm_find. */
+ rc = odp_tm_capability(odp_tm, &tm_capabilities);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_capability() failed for tm: %" PRIx64 "\n",
+ odp_tm_to_u64(odp_tm));
+ return -1;
+ }
+
+ /* Update dynamic capability flags from created tm system */
+ dynamic_shaper_update = tm_capabilities.dynamic_shaper_update;
+ dynamic_sched_update = tm_capabilities.dynamic_sched_update;
+ dynamic_threshold_update = tm_capabilities.dynamic_threshold_update;
+ dynamic_wred_update = tm_capabilities.dynamic_wred_update;
+
+ found_odp_tm = odp_tm_find(tm_name, &requirements, &egress);
+ if ((found_odp_tm == ODP_TM_INVALID) || (found_odp_tm != odp_tm)) {
+ ODPH_ERR("odp_tm_find() failed\n");
+ return -1;
+ }
+
+ /* Start TM system */
+ CU_ASSERT((rc = odp_tm_start(odp_tm)) == 0);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_start() failed for tm: %" PRIx64 "\n",
+ odp_tm_to_u64(odp_tm));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dump_tm_subtree(tm_node_desc_t *node_desc)
+{
+ odp_tm_node_info_t node_info;
+ uint32_t idx, num_queues, child_idx;
+ int rc;
+
+ for (idx = 0; idx < node_desc->level; idx++)
+ printf(" ");
+
+ rc = odp_tm_node_info(node_desc->node, &node_info);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n",
+ odp_tm_node_to_u64(node_desc->node));
+ }
+
+ num_queues = 0;
+ if (node_desc->queue_desc != NULL)
+ num_queues = node_desc->queue_desc->num_queues;
+
+ printf("node_desc=%p name='%s' tm_node=0x%" PRIX64 " idx=%" PRIu32 " "
+ "level=%" PRIu32" parent=0x%" PRIX64 " children=%" PRIu32 " "
+ "queues=%" PRIu32 " queue_fanin=%" PRIu32 " "
+ "node_fanin=%" PRIu32 "\n",
+ (void *)node_desc, node_desc->node_name,
+ odp_tm_node_to_u64(node_desc->node), node_desc->node_idx,
+ node_desc->level, odp_tm_node_to_u64(node_desc->parent_node),
+ node_desc->num_children, num_queues, node_info.tm_queue_fanin,
+ node_info.tm_node_fanin);
+
+ for (child_idx = 0; child_idx < node_desc->num_children; child_idx++)
+ dump_tm_subtree(node_desc->children[child_idx]);
+}
+
+static void dump_tm_tree(uint32_t tm_idx)
+{
+ tm_node_desc_t *root_node_desc;
+
+ if (!TM_DEBUG)
+ return;
+
+ root_node_desc = root_node_descs[tm_idx];
+ dump_tm_subtree(root_node_desc);
+}
+
+static int unconfig_tm_queue_profiles(odp_tm_queue_t tm_queue)
+{
+ odp_tm_queue_info_t queue_info;
+ odp_tm_wred_t wred_profile;
+ uint32_t color;
+ int rc;
+
+ rc = odp_tm_queue_info(tm_queue, &queue_info);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_info failed code=%d\n", rc);
+ return rc;
+ }
+
+ if (queue_info.shaper_profile != ODP_TM_INVALID) {
+ rc = odp_tm_queue_shaper_config(tm_queue, ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_shaper_config failed code=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (queue_info.threshold_profile != ODP_TM_INVALID) {
+ rc = odp_tm_queue_threshold_config(tm_queue, ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_threshold_config failed "
+ "code=%d\n", rc);
+ return rc;
+ }
+ }
+
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+ wred_profile = queue_info.wred_profile[color];
+ if (wred_profile != ODP_TM_INVALID) {
+ rc = odp_tm_queue_wred_config(tm_queue, color,
+ ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_wred_config failed "
+ "color=%" PRIu32 " code=%d\n",
+ color, rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_tm_queues(tm_queue_desc_t *queue_desc)
+{
+ odp_tm_queue_t tm_queue;
+ uint32_t num_queues, queue_idx;
+ int rc;
+
+ num_queues = queue_desc->num_queues;
+ for (queue_idx = 0; queue_idx < num_queues; queue_idx++) {
+ tm_queue = queue_desc->tm_queues[queue_idx];
+ if (tm_queue != ODP_TM_INVALID) {
+ rc = odp_tm_queue_disconnect(tm_queue);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_disconnect failed "
+ "idx=%" PRIu32 " code=%d\n",
+ queue_idx, rc);
+ return rc;
+ }
+
+ rc = unconfig_tm_queue_profiles(tm_queue);
+ if (rc != 0) {
+ ODPH_ERR("unconfig_tm_queue_profiles failed "
+ "idx=%" PRIu32 " code=%d\n",
+ queue_idx, rc);
+ return rc;
+ }
+
+ rc = odp_tm_queue_destroy(tm_queue);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_queue_destroy failed "
+ "idx=%" PRIu32 " code=%d\n",
+ queue_idx, rc);
+ return rc;
+ }
+ }
+ }
+
+ free(queue_desc);
+ return 0;
+}
+
+static int unconfig_tm_node_profiles(odp_tm_node_t tm_node)
+{
+ odp_tm_node_info_t node_info;
+ odp_tm_wred_t wred_profile;
+ uint32_t color;
+ int rc;
+
+ rc = odp_tm_node_info(tm_node, &node_info);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_info failed code=%d\n", rc);
+ return rc;
+ }
+
+ if (node_info.shaper_profile != ODP_TM_INVALID) {
+ rc = odp_tm_node_shaper_config(tm_node, ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_shaper_config failed code=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (node_info.threshold_profile != ODP_TM_INVALID) {
+ rc = odp_tm_node_threshold_config(tm_node, ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_threshold_config failed "
+ "code=%d\n", rc);
+ return rc;
+ }
+ }
+
+ for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+ wred_profile = node_info.wred_profile[color];
+ if (wred_profile != ODP_TM_INVALID) {
+ rc = odp_tm_node_wred_config(tm_node, color,
+ ODP_TM_INVALID);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_wred_config failed "
+ "color=%" PRIu32 " code=%d\n",
+ color, rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_tm_subtree(tm_node_desc_t *node_desc)
+{
+ tm_queue_desc_t *queue_desc;
+ tm_node_desc_t *child_desc;
+ odp_tm_node_t tm_node;
+ uint32_t num_children, child_num;
+ int rc;
+
+ num_children = node_desc->num_children;
+ for (child_num = 0; child_num < num_children; child_num++) {
+ child_desc = node_desc->children[child_num];
+ if (child_desc != NULL) {
+ rc = destroy_tm_subtree(child_desc);
+ if (rc != 0) {
+ ODPH_ERR("%s failed child_num=%" PRIu32 " "
+ "code=%d\n", __func__, child_num, rc);
+ return rc;
+ }
+ }
+ }
+
+ queue_desc = node_desc->queue_desc;
+ if (queue_desc != NULL) {
+ rc = destroy_tm_queues(queue_desc);
+ if (rc != 0) {
+ ODPH_ERR("destroy_tm_queues failed code=%d\n", rc);
+ return rc;
+ }
+ }
+
+ tm_node = node_desc->node;
+ rc = odp_tm_node_disconnect(tm_node);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_disconnect failed code=%d\n", rc);
+ return rc;
+ }
+
+ rc = unconfig_tm_node_profiles(tm_node);
+ if (rc != 0) {
+ ODPH_ERR("unconfig_tm_node_profiles failed code=%d\n", rc);
+ return rc;
+ }
+
+ rc = odp_tm_node_destroy(tm_node);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_destroy failed code=%d\n", rc);
+ return rc;
+ }
+
+ if (node_desc->node_name)
+ free(node_desc->node_name);
+
+ free(node_desc);
+ return 0;
+}
+
+static int destroy_all_shaper_profiles(void)
+{
+ odp_tm_shaper_t shaper_profile;
+ uint32_t idx;
+ int rc;
+
+ for (idx = 0; idx < NUM_SHAPER_PROFILES; idx++) {
+ shaper_profile = shaper_profiles[idx];
+ if (shaper_profile != ODP_TM_INVALID) {
+ rc = odp_tm_shaper_destroy(shaper_profile);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_sched_destroy failed "
+ "node=%" PRIx64 " idx=%" PRIu32
+ " code=%d\n",
+ odp_tm_shaper_to_u64(shaper_profile),
+ idx, rc);
+ return rc;
+ }
+ shaper_profiles[idx] = ODP_TM_INVALID;
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_all_sched_profiles(void)
+{
+ odp_tm_sched_t sched_profile;
+ uint32_t idx;
+ int rc;
+
+ for (idx = 0; idx < NUM_SCHED_PROFILES; idx++) {
+ sched_profile = sched_profiles[idx];
+ if (sched_profile != ODP_TM_INVALID) {
+ rc = odp_tm_sched_destroy(sched_profile);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_sched_destroy failed "
+ "node=%" PRIx64 " idx=%" PRIu32
+ " code=%d\n",
+ odp_tm_sched_to_u64(sched_profile),
+ idx, rc);
+ return rc;
+ }
+ sched_profiles[idx] = ODP_TM_INVALID;
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_all_threshold_profiles(void)
+{
+ odp_tm_threshold_t thr_profile;
+ uint32_t idx;
+ int rc;
+
+ for (idx = 0; idx < NUM_THRESHOLD_PROFILES; idx++) {
+ thr_profile = threshold_profiles[idx];
+ if (thr_profile != ODP_TM_INVALID) {
+ rc = odp_tm_threshold_destroy(thr_profile);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_threshold_destroy failed "
+ "node=%" PRIx64 " idx=%" PRIu32
+ " code=%d\n",
+ odp_tm_threshold_to_u64(thr_profile),
+ idx, rc);
+ return rc;
+ }
+ threshold_profiles[idx] = ODP_TM_INVALID;
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_all_wred_profiles(void)
+{
+ odp_tm_wred_t wred_prof;
+ uint32_t idx, color;
+ int rc;
+
+ for (idx = 0; idx < NUM_WRED_PROFILES; idx++) {
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ wred_prof = wred_profiles[idx][color];
+ if (wred_prof != ODP_TM_INVALID) {
+ rc = odp_tm_wred_destroy(wred_prof);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_wred_destroy failed "
+ "node=%" PRIx64 " idx=%" PRIu32
+ " color=%" PRIu32 " code=%d\n",
+ odp_tm_wred_to_u64(wred_prof),
+ idx, color, rc);
+ return rc;
+ }
+ wred_profiles[idx][color] = ODP_TM_INVALID;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int destroy_all_profiles(void)
+{
+ int rc;
+
+ rc = destroy_all_shaper_profiles();
+ if (rc != 0) {
+ ODPH_ERR("destroy_all_shaper_profiles failed code=%d\n", rc);
+ return rc;
+ }
+
+ rc = destroy_all_sched_profiles();
+ if (rc != 0) {
+ ODPH_ERR("destroy_all_sched_profiles failed code=%d\n", rc);
+ return rc;
+ }
+
+ rc = destroy_all_threshold_profiles();
+ if (rc != 0) {
+ ODPH_ERR("destroy_all_threshold_profiles failed code=%d\n", rc);
+ return rc;
+ }
+
+ rc = destroy_all_wred_profiles();
+ if (rc != 0) {
+ ODPH_ERR("destroy_all_wred_profiles failed code=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int destroy_tm_systems(void)
+{
+ uint32_t idx;
+
+ /* Close/free the TM systems. */
+ for (idx = 0; idx < num_odp_tm_systems; idx++) {
+ if (odp_tm_stop(odp_tm_systems[idx]) != 0)
+ return -1;
+
+ if (destroy_tm_subtree(root_node_descs[idx]) != 0)
+ return -1;
+
+ if (odp_tm_destroy(odp_tm_systems[idx]) != 0)
+ return -1;
+
+ odp_tm_systems[idx] = ODP_TM_INVALID;
+ }
+
+ /* Close/free the TM profiles. */
+ if (destroy_all_profiles() != 0)
+ return -1;
+
+ return 0;
+}
+
+static int traffic_mngr_suite_init(void)
+{
+ odp_tm_capabilities_t egress_capa;
+ uint32_t payload_len, copy_len;
+ odp_tm_egress_t egress;
+ int j, ret;
+
+ /* Initialize some global variables. */
+ num_pkts_made = 0;
+ num_pkts_sent = 0;
+ num_rcv_pkts = 0;
+ cpu_unique_id = 1;
+ cpu_tcp_seq_num = DEFAULT_TCP_SEQ_NUM;
+ memset(xmt_pkts, 0, sizeof(xmt_pkts));
+ memset(rcv_pkts, 0, sizeof(rcv_pkts));
+
+ payload_len = 0;
+ while (payload_len < MAX_PAYLOAD) {
+ copy_len = ODPH_MIN(MAX_PAYLOAD - payload_len, sizeof(ALPHABET));
+ memcpy(&payload_data[payload_len], ALPHABET, copy_len);
+ payload_len += copy_len;
+ }
+
+ /* Next open a single or pair of interfaces. This should be the same
+ * logic as in the pktio_suite_init() function in the
+ * test/validation/pktio.c file. */
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ pktios[0] = ODP_PKTIO_INVALID;
+ pktios[1] = ODP_PKTIO_INVALID;
+
+ ret = open_pktios();
+ if (ret < 0)
+ return -1;
+
+ /* Positive return indicates, that pktio open failed with out mode as TM
+ * but succeeded with direct mode.
+ */
+ if (ret > 0)
+ goto skip_tests;
+
+ odp_tm_egress_init(&egress);
+ egress.egress_kind = ODP_TM_EGRESS_PKT_IO;
+ egress.pktio = xmt_pktio;
+
+ /* Get TM capabilities */
+ ret = odp_tm_egress_capabilities(&egress_capa, &egress);
+ if (ret) {
+ ODPH_ERR("Failed to retrieve tm capabilities");
+ return ret;
+ }
+
+ /* Check for sufficient TM queues */
+ if (egress_capa.max_tm_queues < NUM_TM_QUEUES)
+ goto skip_tests;
+
+ /* Check for sufficient TM levels */
+ if (egress_capa.max_levels < NUM_LEVELS)
+ goto skip_tests;
+
+ tm_shaper_min_rate = egress_capa.per_level[0].min_rate;
+ tm_shaper_max_rate = egress_capa.per_level[0].max_rate;
+ tm_shaper_min_burst = egress_capa.per_level[0].min_burst;
+ tm_shaper_max_burst = egress_capa.per_level[0].max_burst;
+
+ for (j = 0; j < NUM_LEVELS; j++) {
+ odp_tm_level_capabilities_t *per_level =
+ &egress_capa.per_level[j];
+
+ /* Per node fanin */
+ if (per_level->max_fanin_per_node < FANIN_RATIO)
+ break;
+
+ if (j == 0)
+ continue;
+
+ if (per_level->min_rate > tm_shaper_min_rate)
+ tm_shaper_min_rate = per_level->min_rate;
+
+ if (per_level->min_burst > tm_shaper_min_burst)
+ tm_shaper_min_burst = per_level->min_burst;
+
+ if (per_level->max_rate < tm_shaper_max_rate)
+ tm_shaper_max_rate = per_level->max_rate;
+
+ if (per_level->max_burst < tm_shaper_max_burst)
+ tm_shaper_max_burst = per_level->max_burst;
+ }
+
+ if (tm_shaper_min_rate > tm_shaper_max_rate ||
+ tm_shaper_min_burst > tm_shaper_max_burst)
+ goto skip_tests;
+
+ if (j != NUM_LEVELS)
+ goto skip_tests;
+
+ if (egress_capa.pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE] &&
+ egress_capa.max_schedulers_per_node < NUM_QUEUES_PER_NODE)
+ goto skip_tests;
+
+ if (!egress_capa.pkt_prio_modes[ODP_TM_PKT_PRIO_MODE_PRESERVE] &&
+ egress_capa.max_schedulers_per_node < 1)
+ goto skip_tests;
+
+ /* Init tm capabilities with matching egress capa until tm is created */
+ tm_capabilities = egress_capa;
+
+ if (!tm_capabilities.dynamic_shaper_update)
+ dynamic_shaper_update = false;
+
+ if (!tm_capabilities.dynamic_sched_update)
+ dynamic_sched_update = false;
+
+ if (!tm_capabilities.dynamic_threshold_update)
+ dynamic_threshold_update = false;
+
+ if (!tm_capabilities.dynamic_wred_update)
+ dynamic_wred_update = false;
+
+ return 0;
+skip_tests:
+ /* Mark all tests as inactive under this suite */
+ odp_cunit_set_inactive();
+ suite_inactive++;
+ return 0;
+}
+
+static int traffic_mngr_suite_term(void)
+{
+ uint32_t iface;
+
+ /* Close the pktios and associated packet pools. */
+ free_rcvd_pkts();
+ for (iface = 0; iface < num_ifaces; iface++) {
+ /* Skip pktios not initialized */
+ if (pktios[iface] != ODP_PKTIO_INVALID) {
+ if (pktio_started[iface] &&
+ odp_pktio_stop(pktios[iface]) != 0)
+ return -1;
+
+ if (odp_pktio_close(pktios[iface]) != 0)
+ return -1;
+ pktios[iface] = ODP_PKTIO_INVALID;
+ pktio_started[iface] = false;
+ }
+
+ if (odp_pool_destroy(pools[iface]) != 0)
+ return -1;
+
+ pools[iface] = ODP_POOL_INVALID;
+ }
+
+ if (odp_cunit_print_inactive())
+ return -1;
+
+ return 0;
+}
+
+static void check_shaper_profile(char *shaper_name, uint32_t shaper_idx)
+{
+ odp_tm_shaper_params_t shaper_params;
+ odp_tm_shaper_t profile;
+ int rc;
+
+ profile = odp_tm_shaper_lookup(shaper_name);
+ CU_ASSERT(profile != ODP_TM_INVALID);
+ CU_ASSERT(profile == shaper_profiles[shaper_idx - 1]);
+ if (profile != shaper_profiles[shaper_idx - 1])
+ return;
+
+ memset(&shaper_params, 0, sizeof(shaper_params));
+ rc = odp_tm_shaper_params_read(profile, &shaper_params);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(approx_eq64(shaper_params.commit_rate,
+ clamp_rate(shaper_idx * MIN_COMMIT_BW)));
+ CU_ASSERT(approx_eq64(shaper_params.peak_rate,
+ clamp_rate(shaper_idx * MIN_PEAK_BW)));
+ CU_ASSERT(approx_eq32(shaper_params.commit_burst,
+ clamp_burst(shaper_idx * MIN_COMMIT_BURST)));
+ CU_ASSERT(approx_eq32(shaper_params.peak_burst,
+ clamp_burst(shaper_idx * MIN_PEAK_BURST)));
+
+ CU_ASSERT(shaper_params.shaper_len_adjust == SHAPER_LEN_ADJ);
+ CU_ASSERT(shaper_params.dual_rate == true);
+}
+
+static void traffic_mngr_test_shaper_profile(void)
+{
+ odp_tm_shaper_params_t shaper_params;
+ odp_tm_shaper_t profile;
+ uint32_t idx, shaper_idx, i;
+ char shaper_name[TM_NAME_LEN];
+
+ odp_tm_shaper_params_init(&shaper_params);
+ shaper_params.shaper_len_adjust = SHAPER_LEN_ADJ;
+ shaper_params.dual_rate = true;
+
+ for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) {
+ snprintf(shaper_name, sizeof(shaper_name),
+ "shaper_profile_%" PRIu32, idx);
+ shaper_params.commit_rate = clamp_rate(idx * MIN_COMMIT_BW);
+ shaper_params.peak_rate = clamp_rate(idx * MIN_PEAK_BW);
+ shaper_params.commit_burst = clamp_burst(idx * MIN_COMMIT_BURST);
+ shaper_params.peak_burst = clamp_burst(idx * MIN_PEAK_BURST);
+
+ profile = odp_tm_shaper_create(shaper_name, &shaper_params);
+ CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
+
+ /* Make sure profile handle is unique */
+ for (i = 1; i < idx - 1; i++)
+ CU_ASSERT(profile != shaper_profiles[i - 1]);
+
+ shaper_profiles[idx - 1] = profile;
+ num_shaper_profiles++;
+ }
+
+ /* Now test odp_tm_shaper_lookup */
+ for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) {
+ /* The following equation is designed is somewhat randomize
+ * the lookup of the profiles to catch any implementations
+ *taking shortcuts. */
+ shaper_idx = ((3 + 7 * idx) % NUM_SHAPER_TEST_PROFILES) + 1;
+ snprintf(shaper_name, sizeof(shaper_name),
+ "shaper_profile_%" PRIu32, shaper_idx);
+
+ check_shaper_profile(shaper_name, shaper_idx);
+ }
+}
+
+static void check_sched_profile(char *sched_name, uint32_t sched_idx)
+{
+ odp_tm_sched_params_t sched_params;
+ odp_tm_sched_t profile;
+ uint32_t priority;
+
+ profile = odp_tm_sched_lookup(sched_name);
+ CU_ASSERT(profile != ODP_TM_INVALID);
+ CU_ASSERT(profile == sched_profiles[sched_idx - 1]);
+ if (profile != sched_profiles[sched_idx - 1])
+ return;
+
+ odp_tm_sched_params_read(profile, &sched_params);
+ for (priority = 0; priority < NUM_PRIORITIES; priority++) {
+ CU_ASSERT(sched_params.sched_modes[priority] ==
+ ODP_TM_BYTE_BASED_WEIGHTS);
+ CU_ASSERT(approx_eq32(sched_params.sched_weights[priority],
+ 8 + sched_idx + priority));
+ }
+}
+
+static void traffic_mngr_test_sched_profile(void)
+{
+ odp_tm_sched_params_t sched_params;
+ odp_tm_sched_t profile;
+ uint32_t idx, priority, sched_idx, i;
+ char sched_name[TM_NAME_LEN];
+
+ odp_tm_sched_params_init(&sched_params);
+
+ for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) {
+ snprintf(sched_name, sizeof(sched_name),
+ "sched_profile_%" PRIu32, idx);
+ for (priority = 0; priority < ODP_TM_MAX_PRIORITIES; priority++) {
+ sched_params.sched_modes[priority] =
+ ODP_TM_BYTE_BASED_WEIGHTS;
+ sched_params.sched_weights[priority] = 8 + idx +
+ priority;
+ }
+
+ profile = odp_tm_sched_create(sched_name, &sched_params);
+ CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
+
+ /* Make sure profile handle is unique */
+ for (i = 1; i < idx - 1; i++)
+ CU_ASSERT(profile != sched_profiles[i - 1]);
+
+ sched_profiles[idx - 1] = profile;
+ num_sched_profiles++;
+ }
+
+ /* Now test odp_tm_sched_lookup */
+ for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) {
+ /* The following equation is designed is somewhat randomize
+ * the lookup of the profiles to catch any implementations
+ * taking shortcuts. */
+ sched_idx = ((3 + 7 * idx) % NUM_SCHED_TEST_PROFILES) + 1;
+ snprintf(sched_name, sizeof(sched_name),
+ "sched_profile_%" PRIu32, sched_idx);
+ check_sched_profile(sched_name, sched_idx);
+ }
+}
+
+static void check_threshold_profile(char *threshold_name,
+ uint32_t threshold_idx,
+ threshold_type_t threshold)
+{
+ odp_tm_threshold_params_t threshold_params;
+ odp_tm_threshold_t profile;
+ int ret;
+
+ profile = odp_tm_thresholds_lookup(threshold_name);
+ CU_ASSERT(profile != ODP_TM_INVALID);
+ CU_ASSERT(profile == threshold_profiles[threshold_idx - 1]);
+
+ if (profile == threshold_profiles[threshold_idx - 1])
+ return;
+
+ ret = odp_tm_thresholds_params_read(profile, &threshold_params);
+ CU_ASSERT(ret == 0);
+
+ if (ret)
+ return;
+
+ if (threshold == THRESHOLD_PACKET || threshold == THRESHOLD_BYTE_AND_PACKET) {
+ CU_ASSERT(threshold_params.enable_max_pkts == 1);
+ CU_ASSERT(threshold_params.max_pkts == threshold_idx * MIN_PKT_THRESHOLD);
+ }
+ if (threshold == THRESHOLD_BYTE || threshold == THRESHOLD_BYTE_AND_PACKET) {
+ CU_ASSERT(threshold_params.enable_max_bytes == 1);
+ CU_ASSERT(threshold_params.max_bytes == threshold_idx * MIN_BYTE_THRESHOLD);
+ }
+}
+
+static void traffic_mngr_test_threshold_profile(threshold_type_t threshold)
+{
+ odp_tm_threshold_params_t threshold_params;
+ odp_tm_threshold_t profile;
+ uint32_t idx, threshold_idx, i;
+ char threshold_name[TM_NAME_LEN];
+
+ odp_tm_threshold_params_init(&threshold_params);
+
+ if (threshold == THRESHOLD_PACKET || threshold == THRESHOLD_BYTE_AND_PACKET)
+ threshold_params.enable_max_pkts = 1;
+ if (threshold == THRESHOLD_BYTE || threshold == THRESHOLD_BYTE_AND_PACKET)
+ threshold_params.enable_max_bytes = 1;
+
+ for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) {
+ snprintf(threshold_name, sizeof(threshold_name),
+ "threshold_profile_%" PRIu32, idx);
+ if (threshold == THRESHOLD_PACKET || threshold == THRESHOLD_BYTE_AND_PACKET)
+ threshold_params.max_pkts = idx * MIN_PKT_THRESHOLD;
+ if (threshold == THRESHOLD_BYTE || threshold == THRESHOLD_BYTE_AND_PACKET)
+ threshold_params.max_bytes = idx * MIN_BYTE_THRESHOLD;
+
+ profile = odp_tm_threshold_create(threshold_name,
+ &threshold_params);
+ CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
+
+ /* Make sure profile handle is unique */
+ for (i = 1; i < idx - 1; i++)
+ CU_ASSERT(profile != threshold_profiles[i - 1]);
+
+ threshold_profiles[idx - 1] = profile;
+ num_threshold_profiles++;
+ }
+
+ /* Now test odp_tm_threshold_lookup */
+ for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) {
+ /* The following equation is designed is somewhat randomize
+ * the lookup of the profiles to catch any implementations
+ * taking shortcuts. */
+ threshold_idx = ((3 + 7 * idx) % NUM_THRESH_TEST_PROFILES) + 1;
+ snprintf(threshold_name, sizeof(threshold_name),
+ "threshold_profile_%" PRIu32, threshold_idx);
+ check_threshold_profile(threshold_name, threshold_idx, threshold);
+ }
+
+ for (i = 0; i < NUM_THRESH_TEST_PROFILES; i++) {
+ CU_ASSERT(odp_tm_threshold_destroy(threshold_profiles[i]) == 0);
+ num_threshold_profiles--;
+ }
+}
+
+static void traffic_mngr_test_threshold_profile_byte(void)
+{
+ traffic_mngr_test_threshold_profile(THRESHOLD_BYTE);
+}
+
+static void traffic_mngr_test_threshold_profile_packet(void)
+{
+ traffic_mngr_test_threshold_profile(THRESHOLD_PACKET);
+}
+
+static void traffic_mngr_test_threshold_profile_byte_and_packet(void)
+{
+ traffic_mngr_test_threshold_profile(THRESHOLD_BYTE_AND_PACKET);
+}
+
+static void check_wred_profile(char *wred_name,
+ uint32_t wred_idx,
+ uint32_t color)
+{
+ odp_tm_wred_params_t wred_params;
+ odp_tm_wred_t profile;
+ int ret;
+
+ profile = odp_tm_wred_lookup(wred_name);
+ CU_ASSERT(profile != ODP_TM_INVALID);
+ CU_ASSERT(profile == wred_profiles[wred_idx - 1][color]);
+ if (profile != wred_profiles[wred_idx - 1][color])
+ return;
+
+ ret = odp_tm_wred_params_read(profile, &wred_params);
+ CU_ASSERT(ret == 0);
+
+ if (ret)
+ return;
+
+ CU_ASSERT(wred_params.min_threshold == wred_idx * MIN_WRED_THRESH);
+ CU_ASSERT(wred_params.med_threshold == wred_idx * MED_WRED_THRESH);
+ CU_ASSERT(wred_params.med_drop_prob == wred_idx * MED_DROP_PROB);
+ CU_ASSERT(wred_params.max_drop_prob == wred_idx * MAX_DROP_PROB);
+
+ CU_ASSERT(wred_params.enable_wred == 1);
+ CU_ASSERT(wred_params.use_byte_fullness == 0);
+}
+
+static void traffic_mngr_test_wred_profile(void)
+{
+ odp_tm_wred_params_t wred_params;
+ odp_tm_wred_t profile;
+ uint32_t idx, color, wred_idx, i, c;
+ char wred_name[TM_NAME_LEN];
+
+ odp_tm_wred_params_init(&wred_params);
+ wred_params.enable_wred = 1;
+ wred_params.use_byte_fullness = 0;
+
+ for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) {
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ snprintf(wred_name, sizeof(wred_name),
+ "wred_profile_%" PRIu32 "_%" PRIu32,
+ idx, color);
+ wred_params.min_threshold = idx * MIN_WRED_THRESH;
+ wred_params.med_threshold = idx * MED_WRED_THRESH;
+ wred_params.med_drop_prob = idx * MED_DROP_PROB;
+ wred_params.max_drop_prob = idx * MAX_DROP_PROB;
+
+ profile = odp_tm_wred_create(wred_name, &wred_params);
+ CU_ASSERT_FATAL(profile != ODP_TM_INVALID);
+
+ /* Make sure profile handle is unique */
+ for (i = 1; i < idx - 1; i++)
+ for (c = 0; c < ODP_NUM_PKT_COLORS; c++)
+ CU_ASSERT(profile !=
+ wred_profiles[i - 1][c]);
+
+ wred_profiles[idx - 1][color] = profile;
+ }
+
+ num_wred_profiles++;
+ }
+
+ /* Now test odp_tm_wred_lookup */
+ for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) {
+ /* The following equation is designed is somewhat randomize
+ * the lookup of the profiles to catch any implementations
+ * taking shortcuts. */
+ wred_idx = ((3 + 7 * idx) % NUM_WRED_TEST_PROFILES) + 1;
+
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ snprintf(wred_name, sizeof(wred_name),
+ "wred_profile_%" PRIu32 "_%" PRIu32,
+ wred_idx, color);
+ check_wred_profile(wred_name, wred_idx, color);
+ }
+ }
+}
+
+static int set_shaper(const char *node_name,
+ const char *shaper_name,
+ uint64_t commit_bps,
+ uint64_t commit_burst_in_bits)
+{
+ odp_tm_shaper_params_t shaper_params;
+ odp_tm_shaper_t shaper_profile;
+ odp_tm_node_t tm_node;
+ int rc;
+
+ commit_bps = clamp_rate(commit_bps);
+ commit_burst_in_bits = clamp_burst(commit_burst_in_bits);
+
+ tm_node = find_tm_node(0, node_name);
+ if (tm_node == ODP_TM_INVALID) {
+ ODPH_ERR("find_tm_node(%s) failed\n", node_name);
+ CU_ASSERT_FATAL(tm_node != ODP_TM_INVALID);
+ return -1;
+ }
+
+ odp_tm_shaper_params_init(&shaper_params);
+ shaper_params.commit_rate = commit_bps;
+ shaper_params.peak_rate = 0;
+ shaper_params.commit_burst = commit_burst_in_bits;
+ shaper_params.peak_burst = 0;
+ shaper_params.shaper_len_adjust = 0;
+ shaper_params.dual_rate = 0;
+
+ if (!dynamic_shaper_update) {
+ /* Stop TM system before update when dynamic update is not
+ * supported.
+ */
+ CU_ASSERT_FATAL(odp_tm_stop(odp_tm_systems[0]) == 0);
+ }
+
+ if (!shaper_name) {
+ shaper_profile = ODP_TM_INVALID;
+ goto skip_profile;
+ }
+
+ /* First see if a shaper profile already exists with this name, in
+ * which case we use that profile, else create a new one. */
+ shaper_profile = odp_tm_shaper_lookup(shaper_name);
+ if (shaper_profile != ODP_TM_INVALID) {
+ odp_tm_shaper_params_update(shaper_profile, &shaper_params);
+ } else {
+ shaper_profile = odp_tm_shaper_create(shaper_name,
+ &shaper_params);
+ shaper_profiles[num_shaper_profiles] = shaper_profile;
+ num_shaper_profiles++;
+ }
+
+skip_profile:
+ rc = odp_tm_node_shaper_config(tm_node, shaper_profile);
+
+ if (!dynamic_shaper_update) {
+ /* Start TM system, post update */
+ CU_ASSERT_FATAL(odp_tm_start(odp_tm_systems[0]) == 0);
+ }
+ return rc;
+}
+
+static int traffic_mngr_check_shaper(void)
+{
+ odp_cpumask_t cpumask;
+ int cpucount = odp_cpumask_all_available(&cpumask);
+
+/* Skip the shaper test on arm64 systems */
+#if defined(__aarch64__)
+ printf("\nTemporarily skip shaper test which intermittently "
+ "fails on arm64 systems. Will be activated when issue "
+ "is resolved\n");
+ return ODP_TEST_INACTIVE;
+#endif
+
+ if (cpucount < 2) {
+ ODPH_DBG("\nSkipping shaper test because cpucount = %d "
+ "is less then min number 2 required\n", cpucount);
+ ODPH_DBG("Rerun with more cpu resources\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ /* This test needs 1 Mbps, 4 Mbps, 10 Mpbs, 40 Mbps, 100 Mbps */
+ if ((tm_shaper_min_rate > 100 * MBPS) || (tm_shaper_max_rate < 1 * MBPS))
+ return ODP_TEST_INACTIVE;
+
+ /* All the subtests run with burst of 10000 bits */
+ if ((tm_shaper_min_burst > 10000) || tm_shaper_max_burst < 10000)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_scheduler(void)
+{
+ odp_cpumask_t cpumask;
+ int cpucount = odp_cpumask_all_available(&cpumask);
+
+ if (cpucount < 2) {
+ ODPH_DBG("\nSkipping scheduler test because cpucount = %d "
+ "is less then min number 2 required\n", cpucount);
+ ODPH_DBG("Rerun with more cpu resources\n");
+ return ODP_TEST_INACTIVE;
+ }
+
+ /* Scheduler test test_sched_queue_priority() depends on rate of
+ * 64 Kbps and burst of 5600.
+ */
+ if ((tm_shaper_min_rate > 64 * 1000) ||
+ (tm_shaper_max_rate < 64 * 1000) ||
+ (tm_shaper_min_burst > 5600) ||
+ (tm_shaper_max_burst < 5600))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int test_shaper_bw(const char *shaper_name,
+ const char *node_name,
+ uint8_t priority,
+ uint64_t commit_bps)
+{
+ odp_tm_queue_t tm_queue;
+ rcv_stats_t rcv_stats;
+ pkt_info_t pkt_info;
+ uint64_t expected_rcv_gap_us;
+ uint32_t num_pkts, pkt_len, pkts_rcvd_in_order, avg_rcv_gap;
+ uint32_t min_rcv_gap, max_rcv_gap, pkts_sent;
+ int rc, ret_code;
+
+ /* This test can support a commit_bps from 64K to 2 Gbps and possibly
+ * up to a max of 10 Gbps, but no higher. */
+ CU_ASSERT_FATAL(commit_bps <= (10ULL * 1000000000ULL));
+
+ /* Pick a tm_queue and set the parent node's shaper BW to be commit_bps
+ * with a small burst tolerance. Then send the traffic with a pkt_len
+ * such that the pkt start time to next pkt start time is 10,000 bit
+ * times and then measure the average inter-arrival receive "gap" in
+ * microseconds. */
+ tm_queue = find_tm_queue(0, node_name, priority);
+ if (set_shaper(node_name, shaper_name, commit_bps, 10000) != 0)
+ return -1;
+
+ init_xmt_pkts(&pkt_info);
+ num_pkts = 50;
+ pkt_len = (10000 / 8) - (ETHERNET_OVHD_LEN + CRC_LEN);
+ pkt_info.pkt_class = 1;
+ if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ pkts_sent = send_pkts(tm_queue, num_pkts);
+
+ /* The expected inter arrival receive gap in seconds is equal to
+ * "10,000 bits / commit_bps". To get the gap time in microseconds
+ * we multiply this by one million. The timeout we use is 50 times
+ * this gap time (since we send 50 pkts) multiplied by 4 to be
+ * conservative, plus a constant time of 1 millisecond to account for
+ * testing delays. This then needs to be expressed in nanoseconds by
+ * multiplying by 1000. */
+ expected_rcv_gap_us = (1000000ULL * 10000ULL) / commit_bps;
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
+ commit_bps);
+ pkts_rcvd_in_order = pkts_rcvd_in_send_order();
+ ret_code = -1;
+
+ /* First verify that MOST of the pkts were received in any order. */
+ if (num_rcv_pkts <= (pkts_sent / 2)) {
+ /* This is fairly major failure in that most of the pkts didn't
+ * even get received, regardless of rate or order. Log the error
+ * to assist with debugging */
+ ODPH_ERR("Sent %" PRIu32 " pkts but only %" PRIu32 " "
+ "came back\n", pkts_sent, num_rcv_pkts);
+ CU_ASSERT(num_rcv_pkts <= (pkts_sent / 2));
+ } else if (pkts_rcvd_in_order <= 32) {
+ ODPH_ERR("Sent %" PRIu32 " pkts but only %" PRIu32 " "
+ "came back (%" PRIu32 " in order)\n",
+ pkts_sent, num_rcv_pkts, pkts_rcvd_in_order);
+ CU_ASSERT(pkts_rcvd_in_order <= 32);
+ } else {
+ if (pkts_rcvd_in_order < pkts_sent)
+ ODPH_DBG("Info: of %" PRIu32 " pkts sent %" PRIu32 " "
+ "came back (%" PRIu32 " in order)\n",
+ pkts_sent, num_rcv_pkts, pkts_rcvd_in_order);
+
+ /* Next determine the inter arrival receive pkt statistics. */
+ rc = rcv_rate_stats(&rcv_stats, pkt_info.pkt_class);
+ CU_ASSERT(rc == 0);
+
+ /* Next verify that the rcvd pkts have an average inter-receive
+ * gap of "expected_rcv_gap_us" microseconds, +/- 25%. */
+ avg_rcv_gap = rcv_stats.avg_rcv_gap;
+ min_rcv_gap = ((MIN_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) /
+ 100) - 2;
+ max_rcv_gap = ((MAX_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) /
+ 100) + 2;
+ if ((avg_rcv_gap < min_rcv_gap) ||
+ (max_rcv_gap < avg_rcv_gap)) {
+ ODPH_ERR("min=%" PRIu32 " avg_rcv_gap=%" PRIu32 " "
+ "max=%" PRIu32 " std_dev_gap=%" PRIu32 "\n",
+ rcv_stats.min_rcv_gap, avg_rcv_gap,
+ rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap);
+ ODPH_ERR(" expected_rcv_gap=%" PRIu64 " acceptable "
+ "rcv_gap range=%" PRIu32 "..%" PRIu32 "\n",
+ expected_rcv_gap_us, min_rcv_gap, max_rcv_gap);
+ ODPH_ERR("agv_rcv_gap=%" PRIu32 " acceptable "
+ "rcv_gap range=%" PRIu32 "..%" PRIu32 "\n",
+ avg_rcv_gap, min_rcv_gap, max_rcv_gap);
+ ret_code = -1;
+ } else if (expected_rcv_gap_us < rcv_stats.std_dev_gap) {
+ ODPH_ERR("min=%" PRIu32 " avg_rcv_gap=%" PRIu32 " "
+ "max=%" PRIu32 " std_dev_gap=%" PRIu32 "\n",
+ rcv_stats.min_rcv_gap, avg_rcv_gap,
+ rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap);
+ ODPH_ERR(" expected_rcv_gap=%" PRIu64 " acceptable "
+ "rcv_gap range=%" PRIu32 "..%" PRIu32 "\n",
+ expected_rcv_gap_us, min_rcv_gap, max_rcv_gap);
+ ODPH_ERR("std_dev_gap=%" PRIu32 " > "
+ "expected_rcv_gap_us=%" PRIu64 "\n",
+ rcv_stats.std_dev_gap, expected_rcv_gap_us);
+ ret_code = -1;
+ } else {
+ ret_code = 0;
+ }
+ }
+
+ /* Disable the shaper, so as to get the pkts out quicker. */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return ret_code;
+}
+
+static int set_sched_fanin(const char *node_name,
+ const char *sched_base_name,
+ odp_tm_sched_mode_t sched_mode,
+ uint8_t sched_weights[FANIN_RATIO])
+{
+ odp_tm_sched_params_t sched_params;
+ odp_tm_sched_t sched_profile;
+ tm_node_desc_t *node_desc, *child_desc;
+ odp_tm_node_t tm_node, fanin_node;
+ uint32_t fanin_cnt, fanin, priority;
+ uint8_t sched_weight;
+ char sched_name[TM_NAME_LEN];
+ int rc;
+
+ node_desc = find_node_desc(0, node_name);
+ if (node_desc == NULL)
+ return -1;
+
+ if (!dynamic_sched_update) {
+ /* Stop TM system before update when dynamic update is not
+ * supported.
+ */
+ CU_ASSERT_FATAL(odp_tm_stop(odp_tm_systems[0]) == 0);
+ }
+
+ fanin_cnt = ODPH_MIN(node_desc->num_children, FANIN_RATIO);
+ for (fanin = 0; fanin < fanin_cnt; fanin++) {
+ odp_tm_sched_params_init(&sched_params);
+ sched_weight = sched_weights[fanin];
+
+ /* Set the weights and mode the same for all priorities */
+ for (priority = 0; priority < NUM_PRIORITIES; priority++) {
+ sched_params.sched_modes[priority] = sched_mode;
+ sched_params.sched_weights[priority] = sched_weight;
+ }
+
+ /* Create the scheduler profile name using the sched_base_name
+ * and the fanin index */
+ snprintf(sched_name, sizeof(sched_name), "%s_%" PRIu32,
+ sched_base_name, fanin);
+
+ /* First see if a sched profile already exists with this name,
+ * in which case we use that profile, else create a new one. */
+ sched_profile = odp_tm_sched_lookup(sched_name);
+ if (sched_profile != ODP_TM_INVALID) {
+ odp_tm_sched_params_update(sched_profile,
+ &sched_params);
+ } else {
+ sched_profile = odp_tm_sched_create(sched_name,
+ &sched_params);
+ sched_profiles[num_sched_profiles] = sched_profile;
+ num_sched_profiles++;
+ }
+
+ /* Apply the weights to the nodes fan-in. */
+ child_desc = node_desc->children[fanin];
+ tm_node = node_desc->node;
+ fanin_node = child_desc->node;
+ rc = odp_tm_node_sched_config(tm_node, fanin_node,
+ sched_profile);
+ if (rc != 0)
+ goto exit;
+ }
+
+exit:
+ if (!dynamic_sched_update) {
+ /* Start TM system, post update */
+ CU_ASSERT_FATAL(odp_tm_start(odp_tm_systems[0]) == 0);
+ }
+ return rc;
+}
+
+static int test_sched_queue_priority(const char *shaper_name,
+ const char *node_name,
+ uint32_t num_pkts)
+{
+ odp_tm_queue_t tm_queues[NUM_PRIORITIES];
+ pkt_info_t pkt_info;
+ uint32_t pkt_cnt, pkts_in_order, base_idx;
+ uint32_t idx, unique_id, pkt_len, base_pkt_len, pkts_sent;
+ int priority;
+
+ memset(unique_id_list, 0, sizeof(unique_id_list));
+ for (priority = 0; priority < NUM_PRIORITIES; priority++)
+ tm_queues[priority] = find_tm_queue(0, node_name, priority);
+
+ /* Enable the shaper to be low bandwidth. */
+ pkt_len = 1400;
+ set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len);
+
+ /* Make a couple of low priority dummy pkts first. */
+ init_xmt_pkts(&pkt_info);
+ if (make_pkts(4, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ /* Now make "num_pkts" first at the lowest priority, then "num_pkts"
+ * at the second lowest priority, etc until "num_pkts" are made last
+ * at the highest priority (which is always priority 0). */
+ pkt_cnt = NUM_PRIORITIES * num_pkts;
+ base_pkt_len = 256;
+ for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
+ unique_id = cpu_unique_id;
+ pkt_info.pkt_class = priority + 1;
+ pkt_len = base_pkt_len + 64 * priority;
+ if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ base_idx = priority * num_pkts;
+ for (idx = 0; idx < num_pkts; idx++)
+ unique_id_list[base_idx + idx] = unique_id++;
+ }
+
+ /* Send the low priority dummy pkts first. The arrival order of
+ * these pkts will be ignored. */
+ pkts_sent = send_pkts_multi(tm_queues[NUM_PRIORITIES - 1], 4);
+
+ /* Now send "num_pkts" first at the lowest priority, then "num_pkts"
+ * at the second lowest priority, etc until "num_pkts" are sent last
+ * at the highest priority. */
+ for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--)
+ pkts_sent += send_pkts_multi(tm_queues[priority], num_pkts);
+
+ busy_wait(100 * ODP_TIME_MSEC_IN_NS);
+
+ /* Disable the shaper, so as to get the pkts out quicker.
+ * We cannot do this if dynamic shaper update is not supported. Without
+ * dynamic update support set_shaper() can cause packet drops due to
+ * start/stop.
+ */
+ if (dynamic_shaper_update)
+ set_shaper(node_name, NULL, 0, 0);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
+ pkt_cnt + 4, 64 * 1000);
+
+ /* Check rcvd packet arrivals to make sure that pkts arrived in
+ * priority order, except for perhaps the first few lowest priority
+ * dummy pkts. */
+ pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, pkt_cnt, 0,
+ false, false);
+ if (pkts_in_order != pkt_cnt) {
+ ODPH_ERR("pkts_sent=%" PRIu32 " pkt_cnt=%" PRIu32 " "
+ "num_rcv_pkts=%" PRIu32 " rcvd_in_order=%" PRIu32 "\n",
+ pkts_sent, pkt_cnt, num_rcv_pkts, pkts_in_order);
+ }
+
+ CU_ASSERT(pkts_in_order == pkt_cnt);
+
+ /* Disable shaper in case it is still enabled */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return 0;
+}
+
+static int test_sched_node_priority(const char *shaper_name,
+ const char *node_name,
+ uint32_t num_pkts)
+{
+ odp_tm_queue_t *tm_queues, tm_queue;
+ tm_node_desc_t *node_desc;
+ queue_array_t *queue_array;
+ pkt_info_t pkt_info;
+ uint32_t total_num_queues, max_queues, num_queues, pkt_cnt;
+ uint32_t pkts_in_order, base_idx, queue_idx, idx, unique_id;
+ uint32_t pkt_len, base_pkt_len, total_pkt_cnt, pkts_sent;
+ int priority;
+
+ memset(unique_id_list, 0, sizeof(unique_id_list));
+ node_desc = find_node_desc(0, node_name);
+ if (node_desc == NULL)
+ return -1;
+
+ total_num_queues = 0;
+ for (priority = 0; priority < NUM_PRIORITIES; priority++) {
+ max_queues = NUM_LEVEL2_TM_NODES;
+ queue_array = &queues_set.queue_array[priority];
+ tm_queues = queue_array->tm_queues;
+ num_queues = find_child_queues(0, node_desc, priority,
+ tm_queues, max_queues);
+ queue_array->num_queues = num_queues;
+ queue_array->priority = priority;
+ total_num_queues += num_queues;
+ }
+
+ /* Enable the shaper to be low bandwidth. */
+ pkt_len = 1400;
+ set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len);
+
+ /* Make a couple of low priority large dummy pkts first. */
+ init_xmt_pkts(&pkt_info);
+ if (make_pkts(4, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ /* Now make "num_pkts" for each tm_queue at the lowest priority, then
+ * "num_pkts" for each tm_queue at the second lowest priority, etc.
+ * until "num_pkts" for each tm_queue at the highest priority are made
+ * last. Note that the highest priority is always priority 0. */
+ total_pkt_cnt = total_num_queues * num_pkts;
+ base_pkt_len = 256;
+ base_idx = 0;
+ for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
+ unique_id = cpu_unique_id;
+ queue_array = &queues_set.queue_array[priority];
+ num_queues = queue_array->num_queues;
+ pkt_cnt = num_queues * num_pkts;
+ pkt_info.pkt_class = priority + 1;
+ pkt_len = base_pkt_len + 64 * priority;
+ if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ base_idx = priority * num_pkts;
+ for (idx = 0; idx < pkt_cnt; idx++)
+ unique_id_list[base_idx + idx] = unique_id++;
+ }
+
+ /* Send the low priority dummy pkts first. The arrival order of
+ * these pkts will be ignored. */
+ queue_array = &queues_set.queue_array[NUM_PRIORITIES - 1];
+ tm_queue = queue_array->tm_queues[0];
+ pkts_sent = send_pkts(tm_queue, 4);
+
+ /* Now send "num_pkts" for each tm_queue at the lowest priority, then
+ * "num_pkts" for each tm_queue at the second lowest priority, etc.
+ * until "num_pkts" for each tm_queue at the highest priority are sent
+ * last. */
+ for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) {
+ queue_array = &queues_set.queue_array[priority];
+ num_queues = queue_array->num_queues;
+ for (queue_idx = 0; queue_idx < num_queues; queue_idx++) {
+ tm_queue = queue_array->tm_queues[queue_idx];
+ pkts_sent += send_pkts(tm_queue, num_pkts);
+ }
+ }
+
+ busy_wait(100 * ODP_TIME_MSEC_IN_NS);
+
+ /* Disable the shaper, so as to get the pkts out quicker.
+ * We cannot do this if dynamic shaper update is not supported. Without
+ * dynamic update support set_shaper() can cause packet drops due to
+ * start/stop.
+ */
+ if (dynamic_shaper_update)
+ set_shaper(node_name, NULL, 0, 0);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
+ pkts_sent, 64 * 1000);
+
+ /* Check rcvd packet arrivals to make sure that pkts arrived in
+ * priority order, except for perhaps the first few lowest priority
+ * dummy pkts. */
+ pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, total_pkt_cnt,
+ 0, false, false);
+ CU_ASSERT(pkts_in_order == total_pkt_cnt);
+
+ /* Disable shaper in case it is still enabled */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return 0;
+}
+
+static int test_sched_wfq(const char *sched_base_name,
+ const char *shaper_name,
+ const char *node_name,
+ odp_tm_sched_mode_t sched_mode,
+ uint8_t sched_weights[FANIN_RATIO])
+{
+ odp_tm_queue_t tm_queues[FANIN_RATIO], tm_queue;
+ tm_node_desc_t *node_desc, *child_desc;
+ rcv_stats_t rcv_stats[FANIN_RATIO];
+ pkt_info_t pkt_info;
+ uint32_t fanin_cnt, fanin, num_queues, pkt_cnt;
+ uint32_t pkt_len, pkts_sent, pkt_idx;
+ uint8_t pkt_class;
+ int priority, rc;
+
+ memset(tm_queues, 0, sizeof(tm_queues));
+ node_desc = find_node_desc(0, node_name);
+ if (node_desc == NULL)
+ return -1;
+
+ rc = set_sched_fanin(node_name, sched_base_name, sched_mode,
+ sched_weights);
+ if (rc != 0)
+ return -1;
+
+ /* Now determine at least one tm_queue that feeds into each fanin/
+ * child node. */
+ priority = 0;
+ fanin_cnt = ODPH_MIN(node_desc->num_children, FANIN_RATIO);
+ for (fanin = 0; fanin < fanin_cnt; fanin++) {
+ child_desc = node_desc->children[fanin];
+ num_queues = find_child_queues(0, child_desc, priority,
+ &tm_queues[fanin], 1);
+ if (num_queues != 1)
+ return -1;
+ }
+
+ /* Enable the shaper to be low bandwidth. */
+ pkt_len = 1400;
+ set_shaper(node_name, shaper_name, 64 * 1000, 8 * pkt_len);
+
+ /* Make a couple of low priority dummy pkts first. */
+ init_xmt_pkts(&pkt_info);
+ if (make_pkts(4, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ /* Make 100 pkts for each fanin of this node, alternating amongst
+ * the inputs. */
+ pkt_cnt = FANIN_RATIO * 100;
+ fanin = 0;
+ for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
+ pkt_len = 128 + 128 * fanin;
+ pkt_info.pkt_class = 1 + fanin++;
+ if (make_pkts(1, pkt_len, &pkt_info) != 0)
+ return -1;
+
+ if (FANIN_RATIO <= fanin)
+ fanin = 0;
+ }
+
+ /* Send the low priority dummy pkts first. The arrival order of
+ * these pkts will be ignored. */
+ pkts_sent = send_pkts(tm_queues[NUM_PRIORITIES - 1], 4);
+
+ /* Now send the test pkts, alternating amongst the input queues. */
+ fanin = 0;
+ for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
+ tm_queue = tm_queues[fanin++];
+ pkts_sent += send_pkts(tm_queue, 1);
+ if (FANIN_RATIO <= fanin)
+ fanin = 0;
+ }
+ CU_ASSERT(pkts_sent == pkt_cnt + 4);
+
+ busy_wait(1000000); /* wait 1 millisecond */
+
+ /* Disable the shaper, so as to get the pkts out quicker.
+ * We cannot do this if dynamic shaper update is not supported. Without
+ * dynamic update support set_shaper() can cause packet drops due to
+ * start/stop.
+ */
+ if (dynamic_shaper_update)
+ set_shaper(node_name, NULL, 0, 0);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
+ pkt_cnt + 4, 64 * 1000);
+
+ /* Check rcvd packet arrivals to make sure that pkts arrived in
+ * an order commensurate with their weights, sched mode and pkt_len. */
+ for (fanin = 0; fanin < fanin_cnt; fanin++) {
+ pkt_class = 1 + fanin;
+ CU_ASSERT(rcv_rate_stats(&rcv_stats[fanin], pkt_class) == 0);
+ }
+
+ /* Disable shaper in case it is still enabled */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return 0;
+}
+
+static int set_queue_thresholds(odp_tm_queue_t tm_queue,
+ const char *threshold_name,
+ odp_tm_threshold_params_t *threshold_params)
+{
+ odp_tm_threshold_t threshold_profile;
+ int ret;
+
+ if (!dynamic_threshold_update) {
+ /* Stop TM system before update when dynamic update is not
+ * supported.
+ */
+ CU_ASSERT_FATAL(odp_tm_stop(odp_tm_systems[0]) == 0);
+ }
+
+ /* First see if a threshold profile already exists with this name, in
+ * which case we use that profile, else create a new one. */
+ threshold_profile = odp_tm_thresholds_lookup(threshold_name);
+ if (threshold_profile != ODP_TM_INVALID) {
+ ret = odp_tm_thresholds_params_update(threshold_profile,
+ threshold_params);
+ if (ret)
+ goto exit;
+ } else {
+ threshold_profile = odp_tm_threshold_create(threshold_name,
+ threshold_params);
+ if (threshold_profile == ODP_TM_INVALID) {
+ ret = -1;
+ goto exit;
+ }
+ threshold_profiles[num_threshold_profiles] = threshold_profile;
+ num_threshold_profiles++;
+ }
+
+ ret = odp_tm_queue_threshold_config(tm_queue, threshold_profile);
+exit:
+ if (!dynamic_threshold_update) {
+ /* Start TM system, post update */
+ CU_ASSERT_FATAL(odp_tm_start(odp_tm_systems[0]) == 0);
+ }
+ return ret;
+}
+
+static int test_threshold(const char *threshold_name,
+ const char *shaper_name,
+ const char *node_name,
+ uint8_t priority,
+ uint32_t max_pkts,
+ uint32_t max_bytes)
+{
+ odp_tm_threshold_params_t threshold_params;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ uint32_t pkt_len, pkts_sent;
+ uint32_t num_pkts = 0;
+
+ odp_tm_threshold_params_init(&threshold_params);
+ if (max_pkts != 0) {
+ max_pkts = ODPH_MIN(max_pkts, MAX_PKTS / 3);
+ threshold_params.max_pkts = max_pkts;
+ threshold_params.enable_max_pkts = true;
+ num_pkts = 2 * max_pkts;
+ pkt_len = 256;
+ }
+
+ if (max_bytes != 0) {
+ max_bytes = ODPH_MIN(max_bytes, MAX_PKTS * MAX_PAYLOAD / 3);
+ threshold_params.max_bytes = max_bytes;
+ threshold_params.enable_max_bytes = true;
+ num_pkts = 2 * max_bytes / MAX_PAYLOAD;
+ pkt_len = MAX_PAYLOAD;
+ }
+
+ if (max_pkts == 0 && max_bytes == 0)
+ return -1;
+
+ /* Pick a tm_queue and set the tm_queue's threshold profile and then
+ * send in twice the amount of traffic as suggested by the thresholds
+ * and make sure at least SOME pkts get dropped. */
+ tm_queue = find_tm_queue(0, node_name, priority);
+ if (set_queue_thresholds(tm_queue, threshold_name,
+ &threshold_params) != 0) {
+ ODPH_ERR("set_queue_thresholds failed\n");
+ return -1;
+ }
+
+ /* Enable the shaper to be very low bandwidth. */
+ set_shaper(node_name, shaper_name, 256 * 1000, 8 * pkt_len);
+
+ init_xmt_pkts(&pkt_info);
+ pkt_info.drop_eligible = true;
+ pkt_info.pkt_class = 1;
+ if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0) {
+ ODPH_ERR("make_pkts failed\n");
+ return -1;
+ }
+
+ pkts_sent = send_pkts(tm_queue, num_pkts);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
+ 1 * GBPS);
+
+ /* Disable the shaper, so as to get the pkts out quicker. */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+
+ if (num_rcv_pkts < num_pkts)
+ return 0;
+
+ CU_ASSERT(num_rcv_pkts < pkts_sent);
+ return 0;
+}
+
+static wred_pkt_cnts_t *search_expected_pkt_rcv_tbl(odp_tm_percent_t confidence,
+ odp_tm_percent_t drop_perc)
+{
+ wred_pkt_cnts_t *wred_pkt_cnts;
+ uint32_t idx, table_size;
+
+ /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
+ table_size = ODPH_ARRAY_SIZE(EXPECTED_PKT_RCVD);
+ for (idx = 0; idx < table_size; idx++) {
+ wred_pkt_cnts = &EXPECTED_PKT_RCVD[idx];
+ if ((wred_pkt_cnts->confidence_percent == confidence) &&
+ (wred_pkt_cnts->drop_percent == drop_perc))
+ return wred_pkt_cnts;
+ }
+
+ return NULL;
+}
+
+static int set_queue_wred(odp_tm_queue_t tm_queue,
+ const char *wred_name,
+ uint8_t pkt_color,
+ odp_tm_percent_t drop_percent,
+ odp_bool_t use_byte_fullness,
+ odp_bool_t use_dual_slope)
+{
+ odp_tm_wred_params_t wred_params;
+ odp_tm_wred_t wred_profile;
+ int rc;
+
+ odp_tm_wred_params_init(&wred_params);
+ if (use_dual_slope) {
+ wred_params.min_threshold = TM_PERCENT(20);
+ wred_params.med_threshold = TM_PERCENT(40);
+ wred_params.med_drop_prob = drop_percent;
+ wred_params.max_drop_prob = drop_percent;
+ } else {
+ wred_params.min_threshold = 0;
+ wred_params.med_threshold = TM_PERCENT(20);
+ wred_params.med_drop_prob = 0;
+ wred_params.max_drop_prob = 2 * drop_percent;
+ }
+
+ wred_params.enable_wred = true;
+ wred_params.use_byte_fullness = use_byte_fullness;
+
+ if (!dynamic_wred_update) {
+ /* Stop TM system before update when dynamic update is not
+ * supported.
+ */
+ CU_ASSERT_FATAL(odp_tm_stop(odp_tm_systems[0]) == 0);
+ }
+
+ /* First see if a wred profile already exists with this name, in
+ * which case we use that profile, else create a new one. */
+ wred_profile = odp_tm_wred_lookup(wred_name);
+ if (wred_profile != ODP_TM_INVALID) {
+ odp_tm_wred_params_update(wred_profile, &wred_params);
+ } else {
+ wred_profile = odp_tm_wred_create(wred_name, &wred_params);
+ if (wred_profiles[num_wred_profiles - 1][pkt_color] ==
+ ODP_TM_INVALID) {
+ wred_profiles[num_wred_profiles - 1][pkt_color] =
+ wred_profile;
+ } else {
+ wred_profiles[num_wred_profiles][pkt_color] =
+ wred_profile;
+ num_wred_profiles++;
+ }
+ }
+
+ rc = odp_tm_queue_wred_config(tm_queue, pkt_color, wred_profile);
+
+ if (!dynamic_wred_update) {
+ /* Start TM system, post update */
+ CU_ASSERT_FATAL(odp_tm_start(odp_tm_systems[0]) == 0);
+ }
+ return rc;
+
+}
+
+static int test_byte_wred(const char *wred_name,
+ const char *shaper_name,
+ const char *threshold_name,
+ const char *node_name,
+ uint8_t priority,
+ uint8_t pkt_color,
+ odp_tm_percent_t drop_percent,
+ odp_bool_t use_dual_slope)
+{
+ odp_tm_threshold_params_t threshold_params;
+ wred_pkt_cnts_t *wred_pkt_cnts;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ uint32_t num_fill_pkts, num_test_pkts, pkts_sent;
+ int ret;
+
+ /* Pick the tm_queue and set the tm_queue's wred profile to drop the
+ * given percentage of traffic, then send 100 pkts and see how many
+ * pkts are received. */
+ tm_queue = find_tm_queue(0, node_name, priority);
+ set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent,
+ true, use_dual_slope);
+
+ /* Enable the shaper to be very low bandwidth. */
+ set_shaper(node_name, shaper_name, 64 * 1000, 8 * PKT_BUF_SIZE);
+
+ /* Set the threshold to be byte based and to handle 200 pkts of
+ * size PKT_BUF_SIZE. This way the byte-fullness for the wred test
+ * pkts will be around 60%. */
+ odp_tm_threshold_params_init(&threshold_params);
+ threshold_params.max_bytes = 200 * PKT_BUF_SIZE;
+ threshold_params.enable_max_bytes = true;
+ if (set_queue_thresholds(tm_queue, threshold_name,
+ &threshold_params) != 0) {
+ ODPH_ERR("set_queue_thresholds failed\n");
+ return -1;
+ }
+
+ /* Make and send the first batch of pkts whose job is to set the
+ * queue byte fullness to around 60% for the subsequent test packets.
+ * These packets MUST have drop_eligible false. */
+ init_xmt_pkts(&pkt_info);
+ num_fill_pkts = 120;
+ pkt_info.pkt_color = pkt_color;
+ pkt_info.pkt_class = 0;
+ pkt_info.drop_eligible = false;
+ if (make_pkts(num_fill_pkts, PKT_BUF_SIZE, &pkt_info) != 0)
+ return -1;
+
+ send_pkts(tm_queue, num_fill_pkts);
+
+ /* Now send the real test pkts, which are all small so as to try to
+ * keep the byte fullness still close to the 60% point. These pkts
+ * MUST have drop_eligible true. */
+ num_test_pkts = 100;
+ pkt_info.pkt_class = 1;
+ pkt_info.drop_eligible = true;
+ if (make_pkts(num_test_pkts, 128, &pkt_info) != 0)
+ return -1;
+
+ pkts_sent = send_pkts(tm_queue, num_test_pkts);
+
+ /* Disable the shaper, so as to get the pkts out quicker.
+ * We cannot do this if dynamic shaper update is not supported. Without
+ * dynamic update support set_shaper() can cause packet drops due to
+ * start/stop.
+ */
+ if (dynamic_shaper_update)
+ set_shaper(node_name, NULL, 0, 0);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin,
+ num_fill_pkts + pkts_sent, 64 * 1000);
+
+ /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
+ wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9),
+ drop_percent);
+ if (wred_pkt_cnts == NULL)
+ return -1;
+
+ /* Disable shaper in case it is still enabled */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+
+ ret = !((wred_pkt_cnts->min_cnt <= pkts_sent) &&
+ (pkts_sent <= wred_pkt_cnts->max_cnt));
+ if (ret)
+ ODPH_DBG("min %" PRIu32 " pkts %" PRIu32 " max %" PRIu32 "\n",
+ wred_pkt_cnts->min_cnt, pkts_sent,
+ wred_pkt_cnts->max_cnt);
+ return odp_cunit_ret(ret);
+}
+
+static int test_pkt_wred(const char *wred_name,
+ const char *shaper_name,
+ const char *threshold_name,
+ const char *node_name,
+ uint8_t priority,
+ uint8_t pkt_color,
+ odp_tm_percent_t drop_percent,
+ odp_bool_t use_dual_slope)
+{
+ odp_tm_threshold_params_t threshold_params;
+ wred_pkt_cnts_t *wred_pkt_cnts;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ uint32_t num_fill_pkts, num_test_pkts, pkts_sent;
+ int ret;
+
+ /* Pick the tm_queue and set the tm_queue's wred profile to drop the
+ * given percentage of traffic, then send 100 pkts and see how many
+ * pkts are received. */
+ tm_queue = find_tm_queue(0, node_name, priority);
+ set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent,
+ false, use_dual_slope);
+
+ /* Enable the shaper to be very low bandwidth. */
+ set_shaper(node_name, shaper_name, 64 * 1000, 1000);
+
+ /* Set the threshold to be pkt based and to handle 1000 pkts. This
+ * way the pkt-fullness for the wred test pkts will be around 60%. */
+ odp_tm_threshold_params_init(&threshold_params);
+ threshold_params.max_pkts = 1000;
+ threshold_params.enable_max_pkts = true;
+
+ ret = set_queue_thresholds(tm_queue, threshold_name,
+ &threshold_params);
+ if (ret) {
+ ODPH_ERR("set_queue_thresholds failed\n");
+ return -1;
+ }
+
+ /* Make and send the first batch of pkts whose job is to set the
+ * queue pkt fullness to around 60% for the subsequent test packets.
+ * These packets MUST have drop_eligible false. */
+ init_xmt_pkts(&pkt_info);
+ num_fill_pkts = 600;
+ pkt_info.pkt_color = pkt_color;
+ pkt_info.pkt_class = 0;
+ pkt_info.drop_eligible = false;
+ if (make_pkts(num_fill_pkts, 80, &pkt_info) != 0)
+ return -1;
+
+ send_pkts(tm_queue, num_fill_pkts);
+
+ /* Now send the real test pkts. These pkts MUST have drop_eligible
+ * true. */
+ num_test_pkts = 100;
+ pkt_info.pkt_class = 1;
+ pkt_info.drop_eligible = true;
+ if (make_pkts(num_test_pkts, 80, &pkt_info) != 0)
+ return -1;
+
+ pkts_sent = send_pkts(tm_queue, num_test_pkts);
+
+ /* Disable the shaper, so as to get the pkts out quicker.
+ * We cannot do this if dynamic shaper update is not supported. Without
+ * dynamic update support set_shaper() can cause packet drops due to
+ * start/stop.
+ */
+ if (dynamic_shaper_update)
+ set_shaper(node_name, NULL, 0, 0);
+
+ ret = receive_pkts(odp_tm_systems[0], rcv_pktin,
+ num_fill_pkts + pkts_sent, 64 * 1000);
+ if (ret < 0)
+ return -1;
+
+ num_rcv_pkts = ret;
+
+ /* Search the EXPECTED_PKT_RCVD table to find a matching entry */
+ wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9),
+ drop_percent);
+ if (wred_pkt_cnts == NULL)
+ return -1;
+
+ /* Disable shaper in case it is still enabled */
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+
+ if ((pkts_sent < wred_pkt_cnts->min_cnt) ||
+ (pkts_sent > wred_pkt_cnts->max_cnt)) {
+ ODPH_ERR("min_cnt %d <= pkts_sent %d <= max_cnt %d\n",
+ wred_pkt_cnts->min_cnt, pkts_sent,
+ wred_pkt_cnts->max_cnt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_query_functions(const char *shaper_name,
+ const char *node_name,
+ uint8_t priority,
+ uint32_t num_pkts)
+{
+ odp_tm_query_info_t query_info;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ uint64_t commit_bps, expected_pkt_cnt, expected_byte_cnt;
+ int rc;
+
+ /* Pick a tm_queue and set the egress node's shaper BW to be 64K bps
+ * with a small burst tolerance. Then send the traffic. */
+ tm_queue = find_tm_queue(0, node_name, priority);
+ commit_bps = 64 * 1000;
+ if (set_shaper(node_name, shaper_name, commit_bps, 1000) != 0)
+ return -1;
+
+ init_xmt_pkts(&pkt_info);
+ pkt_info.pkt_class = 1;
+ if (make_pkts(num_pkts, PKT_BUF_SIZE, &pkt_info) != 0)
+ return -1;
+
+ send_pkts(tm_queue, num_pkts);
+
+ /* Assume all but 2 of the pkts are still in the queue.*/
+ expected_pkt_cnt = num_pkts - 2;
+ expected_byte_cnt = expected_pkt_cnt * PKT_BUF_SIZE;
+
+ memset(&query_info, 0, sizeof(query_info));
+ rc = odp_tm_queue_query(tm_queue,
+ ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
+ &query_info);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(query_info.total_pkt_cnt_valid);
+ CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
+ CU_ASSERT(query_info.total_byte_cnt_valid);
+ CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
+
+ memset(&query_info, 0, sizeof(query_info));
+ rc = odp_tm_priority_query(odp_tm_systems[0], priority,
+ ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
+ &query_info);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(query_info.total_pkt_cnt_valid);
+ CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
+ CU_ASSERT(query_info.total_byte_cnt_valid);
+ CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
+
+ memset(&query_info, 0, sizeof(query_info));
+ rc = odp_tm_total_query(odp_tm_systems[0],
+ ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT,
+ &query_info);
+ CU_ASSERT(rc == 0);
+ CU_ASSERT(query_info.total_pkt_cnt_valid);
+ CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt);
+ CU_ASSERT(query_info.total_byte_cnt_valid);
+ CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt);
+
+ /* Disable the shaper, so as to get the pkts out quicker. */
+ set_shaper(node_name, NULL, 0, 0);
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, num_pkts,
+ commit_bps);
+
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return 0;
+}
+
+static int check_vlan_marking_pkts(void)
+{
+ odp_packet_t rcv_pkt;
+ uint32_t rcv_pkt_idx, err_cnt;
+ uint16_t tci;
+ uint8_t pkt_class, dei, expected_dei;
+
+ /* Check rcvd packets to make sure that pkt_class 1 pkts continue to
+ * not have a VLAN header, pkt class 2 pkts have a VLAN header with the
+ * drop precedence not set and pkt class 3 pkts have a VLAN header with
+ * the DEI bit set. */
+ err_cnt = 0;
+ for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
+ rcv_pkt = rcv_pkts[rcv_pkt_idx];
+ pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class;
+
+ switch (pkt_class) {
+ case 1:
+ /* Make sure no VLAN header. */
+ if (odp_packet_has_vlan(rcv_pkt)) {
+ err_cnt++;
+ ODPH_ERR("VLAN incorrectly added\n");
+ CU_ASSERT(odp_packet_has_vlan(rcv_pkt));
+ }
+ break;
+
+ case 2:
+ case 3:
+ /* Make sure it does have a VLAN header */
+ if (!odp_packet_has_vlan(rcv_pkt)) {
+ err_cnt++;
+ ODPH_ERR("VLAN header missing\n");
+ CU_ASSERT(!odp_packet_has_vlan(rcv_pkt));
+ break;
+ }
+
+ /* Make sure DEI bit is 0 if pkt_class == 2, and 1 if
+ * pkt_class == 3. */
+ if (get_vlan_tci(rcv_pkt, &tci) != 0) {
+ err_cnt++;
+ ODPH_ERR("VLAN header missing\n");
+ CU_ASSERT(!odp_packet_has_vlan(rcv_pkt));
+ break;
+ }
+
+ dei = (tci >> ODPH_VLANHDR_DEI_SHIFT) & 1;
+ expected_dei = (pkt_class == 2) ? 0 : 1;
+ if (dei != expected_dei) {
+ ODPH_ERR("expected_dei=%u rcvd dei=%u\n",
+ expected_dei, dei);
+ err_cnt++;
+ CU_ASSERT(dei == expected_dei);
+ }
+ break;
+
+ default:
+ /* Log error but otherwise ignore, since it is
+ * probably a stray pkt from a previous test. */
+ ODPH_ERR("Pkt rcvd with invalid pkt class\n");
+ }
+ }
+
+ return (err_cnt == 0) ? 0 : -1;
+}
+
+static int test_vlan_marking(const char *node_name,
+ odp_packet_color_t pkt_color)
+{
+ odp_packet_color_t color;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ odp_tm_t odp_tm;
+ uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent;
+ int rc;
+
+ /* First disable vlan marking for all colors. These "disable" calls
+ * should NEVER fail. */
+ odp_tm = odp_tm_systems[0];
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ rc = odp_tm_vlan_marking(odp_tm, color, false);
+ if (rc != 0) {
+ ODPH_ERR("disabling odp_tm_vlan_marking() failed\n");
+ return -1;
+ }
+ }
+
+ /* Next enable vlan marking for just the given color parameter */
+ rc = odp_tm_vlan_marking(odp_tm, pkt_color, true);
+
+ tm_queue = find_tm_queue(0, node_name, 0);
+ if (tm_queue == ODP_TM_INVALID) {
+ ODPH_ERR("No tm_queue found for node_name='%s'\n", node_name);
+ return -1;
+ }
+
+ /* Next make 2*X pkts of each color, half with vlan headers -
+ * half without. */
+ init_xmt_pkts(&pkt_info);
+
+ pkt_cnt = 5;
+ num_pkts = 0;
+ pkt_len = 600;
+ pkt_info.pkt_class = 1;
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ num_pkts += pkt_cnt;
+ pkt_info.pkt_color = color;
+ if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
+ return -1;
+ }
+
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ num_pkts += pkt_cnt;
+ pkt_info.pkt_color = color;
+ pkt_info.pkt_class = (color == pkt_color) ? 3 : 2;
+ pkt_info.use_vlan = true;
+ pkt_info.vlan_tci = VLAN_NO_DEI;
+ if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0)
+ return -1;
+ }
+
+ pkts_sent = send_pkts(tm_queue, num_pkts);
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
+ 1000 * 1000);
+ if (num_rcv_pkts == 0) {
+ ODPH_ERR("No pkts received\n");
+ rc = -1;
+ } else if (num_rcv_pkts != pkts_sent) {
+ ODPH_ERR("pkts_sent=%" PRIu32 " but num_rcv_pkts=%" PRIu32 "\n",
+ pkts_sent, num_rcv_pkts);
+ dump_rcvd_pkts(0, num_rcv_pkts - 1);
+ CU_ASSERT(num_rcv_pkts == pkts_sent);
+ } else {
+ rc = check_vlan_marking_pkts();
+ }
+
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return rc;
+}
+
+static int check_tos_marking_pkts(odp_bool_t use_ipv6,
+ odp_bool_t use_tcp,
+ odp_bool_t test_ecn,
+ odp_bool_t test_drop_prec,
+ uint8_t unmarked_tos,
+ uint8_t new_dscp,
+ uint8_t dscp_mask)
+{
+ odp_packet_t rcv_pkt;
+ uint32_t rcv_pkt_idx;
+ uint8_t unmarked_ecn, unmarked_dscp, shifted_dscp, pkt_class;
+ uint8_t tos, expected_tos;
+ int rc;
+
+ /* Turn off test_ecn for UDP pkts, since ECN marking should
+ * only happen for TCP pkts. */
+ if (!use_tcp)
+ test_ecn = false;
+
+ /* The expected_tos value is only the expected TOS/TC field for pkts
+ * that have been enabled for modification, as indicated by the
+ * pkt_class associated with this pkt. */
+ unmarked_ecn = (unmarked_tos & ODPH_IP_TOS_ECN_MASK)
+ >> ODPH_IP_TOS_ECN_SHIFT;
+ unmarked_dscp = (unmarked_tos & ODPH_IP_TOS_DSCP_MASK)
+ >> ODPH_IP_TOS_DSCP_SHIFT;
+ new_dscp = (new_dscp & dscp_mask) | (unmarked_dscp & ~dscp_mask);
+ shifted_dscp = new_dscp << ODPH_IP_TOS_DSCP_SHIFT;
+
+ if (test_ecn && test_drop_prec)
+ expected_tos = shifted_dscp | ODPH_IP_ECN_CE;
+ else if (test_ecn)
+ expected_tos = unmarked_tos | ODPH_IP_ECN_CE;
+ else if (test_drop_prec)
+ expected_tos = shifted_dscp | unmarked_ecn;
+ else
+ expected_tos = unmarked_tos;
+
+ for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) {
+ rcv_pkt = rcv_pkts[rcv_pkt_idx];
+ pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class;
+
+ /* Check that the pkts match the use_ipv6 setting */
+ if (use_ipv6)
+ rc = odp_packet_has_ipv6(rcv_pkt);
+ else
+ rc = odp_packet_has_ipv4(rcv_pkt);
+
+ if (rc != 1) {
+ if (use_ipv6)
+ ODPH_ERR("Expected IPv6 pkt but got IPv4");
+ else
+ ODPH_ERR("Expected IPv4 pkt but got IPv6");
+
+ return -1;
+ }
+
+ /* Check that the pkts match the use_tcp setting */
+ if (use_tcp)
+ rc = odp_packet_has_tcp(rcv_pkt);
+ else
+ rc = odp_packet_has_udp(rcv_pkt);
+
+ if (rc != 1) {
+ if (use_tcp)
+ ODPH_ERR("Expected TCP pkt but got UDP");
+ else
+ ODPH_ERR("Expected UDP pkt but got TCP");
+
+ return -1;
+ }
+
+ /* Now get the tos field to see if it was changed */
+ rc = get_ip_tos(rcv_pkt, &tos);
+ if (rc != 0) {
+ ODPH_ERR("get_ip_tos failed\n");
+ return -1;
+ }
+
+ switch (pkt_class) {
+ case 2:
+ /* Tos field must be unchanged. */
+ if (unmarked_tos != tos) {
+ ODPH_ERR("Tos was changed from 0x%X to 0x%X\n",
+ unmarked_tos, tos);
+ return -1;
+ }
+ break;
+
+ case 3:
+ /* Tos field must be changed. */
+ if (tos != expected_tos) {
+ ODPH_ERR("tos=0x%X instead of expected 0x%X\n",
+ tos, expected_tos);
+ CU_ASSERT(tos == expected_tos);
+ }
+ break;
+
+ default:
+ /* Log error but otherwise ignore, since it is
+ * probably a stray pkt from a previous test. */
+ ODPH_ERR("Pkt rcvd with invalid pkt class=%u\n",
+ pkt_class);
+ }
+ }
+
+ return 0;
+}
+
+static int test_ip_marking(const char *node_name,
+ odp_packet_color_t pkt_color,
+ odp_bool_t use_ipv6,
+ odp_bool_t use_tcp,
+ odp_bool_t test_ecn,
+ odp_bool_t test_drop_prec,
+ uint8_t new_dscp,
+ uint8_t dscp_mask)
+{
+ odp_packet_color_t color;
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ odp_tm_t odp_tm;
+ uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent;
+ int rc, ret_code;
+
+ /* First disable IP TOS marking for all colors. These "disable" calls
+ * should NEVER fail. */
+ odp_tm = odp_tm_systems[0];
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ rc = odp_tm_ecn_marking(odp_tm, color, false);
+ if (rc != 0) {
+ ODPH_ERR("disabling odp_tm_ecn_marking() failed\n");
+ return -1;
+ }
+
+ rc = odp_tm_drop_prec_marking(odp_tm, color, false);
+ if (rc != 0) {
+ ODPH_ERR("disabling odp_tm_drop_prec_marking failed\n");
+ return -1;
+ }
+ }
+
+ /* Next enable IP TOS marking for just the given color parameter */
+ if ((!test_ecn) && (!test_drop_prec))
+ return 0;
+
+ if (test_ecn) {
+ rc = odp_tm_ecn_marking(odp_tm, pkt_color, true);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_ecn_marking() call failed\n");
+ return -1;
+ }
+ }
+
+ if (test_drop_prec) {
+ rc = odp_tm_drop_prec_marking(odp_tm, pkt_color, true);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_drop_prec_marking() call failed\n");
+ return -1;
+ }
+ }
+
+ tm_queue = find_tm_queue(0, node_name, 0);
+ if (tm_queue == ODP_TM_INVALID) {
+ ODPH_ERR("No tm_queue found for node_name='%s'\n", node_name);
+ return -1;
+ }
+
+ init_xmt_pkts(&pkt_info);
+ pkt_info.use_ipv6 = use_ipv6;
+ pkt_info.use_tcp = use_tcp;
+ pkt_info.ip_tos = DEFAULT_TOS;
+
+ pkt_cnt = 5;
+ num_pkts = 0;
+ pkt_len = 1340;
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ num_pkts += pkt_cnt;
+ pkt_info.pkt_color = color;
+ if (test_drop_prec || (test_ecn && use_tcp))
+ pkt_info.pkt_class = (color == pkt_color) ? 3 : 2;
+ else
+ pkt_info.pkt_class = 2;
+
+ if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) {
+ ODPH_ERR("make_pkts failed\n");
+ return -1;
+ }
+ }
+
+ pkts_sent = send_pkts(tm_queue, num_pkts);
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
+ 1000 * 1000);
+ ret_code = -1;
+
+ if (num_rcv_pkts == 0) {
+ ODPH_ERR("No pkts received\n");
+ CU_ASSERT(num_rcv_pkts != 0);
+ ret_code = -1;
+ } else if (num_rcv_pkts != pkts_sent) {
+ ODPH_ERR("pkts_sent=%" PRIu32 " but num_rcv_pkts=%" PRIu32 "\n",
+ pkts_sent, num_rcv_pkts);
+ dump_rcvd_pkts(0, num_rcv_pkts - 1);
+ CU_ASSERT(num_rcv_pkts == pkts_sent);
+ ret_code = -1;
+ } else {
+ rc = check_tos_marking_pkts(use_ipv6, use_tcp, test_ecn,
+ test_drop_prec, DEFAULT_TOS,
+ new_dscp, dscp_mask);
+ CU_ASSERT(rc == 0);
+ ret_code = (rc == 0) ? 0 : -1;
+ }
+
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+ return ret_code;
+}
+
+static int test_protocol_marking(const char *node_name,
+ odp_packet_color_t pkt_color,
+ odp_bool_t test_ecn,
+ odp_bool_t test_drop_prec,
+ uint8_t new_dscp,
+ uint8_t dscp_mask)
+{
+ uint32_t errs = 0;
+ int rc;
+
+ /* Now call test_ip_marking once for all combinations of IPv4 or IPv6
+ * pkts AND for UDP or TCP. */
+ rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_UDP,
+ test_ecn, test_drop_prec, new_dscp, dscp_mask);
+ CU_ASSERT(rc == 0);
+ if (rc != 0) {
+ ODPH_ERR("test_ip_marking failed using IPV4/UDP pkts color=%u "
+ "test_ecn=%u test_drop_prec=%u\n",
+ pkt_color, test_ecn, test_drop_prec);
+ errs++;
+ }
+
+ rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_UDP,
+ test_ecn, test_drop_prec, new_dscp, dscp_mask);
+ CU_ASSERT(rc == 0);
+ if (rc != 0) {
+ ODPH_ERR("test_ip_marking failed using IPV6/UDP pkts color=%u "
+ "test_ecn=%u test_drop_prec=%u\n",
+ pkt_color, test_ecn, test_drop_prec);
+ errs++;
+ }
+
+ rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_TCP,
+ test_ecn, test_drop_prec, new_dscp, dscp_mask);
+ CU_ASSERT(rc == 0);
+ if (rc != 0) {
+ ODPH_ERR("test_ip_marking failed using IPV4/TCP pkts color=%u "
+ "test_ecn=%u test_drop_prec=%u\n",
+ pkt_color, test_ecn, test_drop_prec);
+ errs++;
+ }
+
+ rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_TCP,
+ test_ecn, test_drop_prec, new_dscp, dscp_mask);
+ CU_ASSERT(rc == 0);
+ if (rc != 0) {
+ ODPH_ERR("test_ip_marking failed using IPV6/TCP pkts color=%u "
+ "test_ecn=%u test_drop_prec=%u\n",
+ pkt_color, test_ecn, test_drop_prec);
+ errs++;
+ }
+
+ return (errs == 0) ? 0 : -1;
+}
+
+static int ip_marking_tests(const char *node_name,
+ odp_bool_t test_ecn,
+ odp_bool_t test_drop_prec)
+{
+ odp_packet_color_t color;
+ uint32_t errs = 0;
+ uint8_t new_dscp, dscp_mask;
+ int rc;
+
+ dscp_mask = DROP_PRECEDENCE_MASK;
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ if (tm_capabilities.marking_colors_supported[color]) {
+ if (color == PKT_YELLOW)
+ new_dscp = MEDIUM_DROP_PRECEDENCE;
+ else if (color == PKT_RED)
+ new_dscp = HIGH_DROP_PRECEDENCE;
+ else
+ new_dscp = LOW_DROP_PRECEDENCE;
+
+ rc = test_protocol_marking(node_name, color, test_ecn,
+ test_drop_prec, new_dscp,
+ dscp_mask);
+ CU_ASSERT(rc == 0);
+ if (rc != 0)
+ errs++;
+ }
+ }
+
+ return (errs == 0) ? 0 : -1;
+}
+
+static int walk_tree_backwards(odp_tm_node_t tm_node)
+{
+ odp_tm_node_fanin_info_t fanin_info;
+ odp_tm_node_info_t node_info;
+ odp_tm_queue_t first_tm_queue;
+ odp_tm_node_t first_tm_node;
+ uint32_t tm_queue_fanin, tm_node_fanin;
+ int rc;
+
+ /* Start from the given tm_node and try to go backwards until a valid
+ * and active tm_queue is reached. */
+ rc = odp_tm_node_info(tm_node, &node_info);
+ if (rc != 0) {
+ ODPH_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n",
+ odp_tm_node_to_u64(tm_node));
+ return rc;
+ }
+
+ if ((node_info.tm_queue_fanin == 0) &&
+ (node_info.tm_node_fanin == 0)) {
+ ODPH_ERR("odp_tm_node_info showed no fanin for this node\n");
+ return -1;
+ }
+
+ fanin_info.tm_queue = ODP_TM_INVALID;
+ fanin_info.tm_node = ODP_TM_INVALID;
+ fanin_info.is_last = false;
+
+ /* TBD* Loop over the entire fanin list verifying the fanin counts.
+ * Also remember the first tm_queue and tm_node seen. */
+ tm_queue_fanin = 0;
+ tm_node_fanin = 0;
+ first_tm_queue = ODP_TM_INVALID;
+ first_tm_node = ODP_TM_INVALID;
+
+ while (!fanin_info.is_last) {
+ rc = odp_tm_node_fanin_info(tm_node, &fanin_info);
+ if (rc != 0)
+ return rc;
+
+ if ((fanin_info.tm_queue != ODP_TM_INVALID) &&
+ (fanin_info.tm_node != ODP_TM_INVALID)) {
+ ODPH_ERR("Both tm_queue and tm_node are set\n");
+ return -1;
+ } else if (fanin_info.tm_queue != ODP_TM_INVALID) {
+ tm_queue_fanin++;
+ if (first_tm_queue == ODP_TM_INVALID)
+ first_tm_queue = fanin_info.tm_queue;
+ } else if (fanin_info.tm_node != ODP_TM_INVALID) {
+ tm_node_fanin++;
+ if (first_tm_node == ODP_TM_INVALID)
+ first_tm_node = fanin_info.tm_node;
+ } else {
+ ODPH_ERR("both tm_queue and tm_node are INVALID\n");
+ return -1;
+ }
+ }
+
+ if (tm_queue_fanin != node_info.tm_queue_fanin)
+ ODPH_ERR("tm_queue_fanin count error\n");
+ else if (tm_node_fanin != node_info.tm_node_fanin)
+ ODPH_ERR("tm_node_fanin count error\n");
+
+ /* If we have found a tm_queue then we are successfully done. */
+ if (first_tm_queue != ODP_TM_INVALID)
+ return 0;
+
+ /* Now recurse up a level */
+ return walk_tree_backwards(first_tm_node);
+}
+
+static int test_fanin_info(const char *node_name)
+{
+ tm_node_desc_t *node_desc;
+ odp_tm_node_t tm_node;
+
+ node_desc = find_node_desc(0, node_name);
+ if (node_desc == NULL) {
+ ODPH_ERR("node_name %s not found\n", node_name);
+ return -1;
+ }
+
+ tm_node = node_desc->node;
+ if (tm_node == ODP_TM_INVALID) {
+ ODPH_ERR("tm_node is ODP_TM_INVALID\n");
+ return -1;
+ }
+
+ return walk_tree_backwards(node_desc->node);
+}
+
+static void test_packet_aging(uint64_t tmo_ns, uint32_t pkt_len, odp_bool_t is_dropping)
+{
+ odp_tm_queue_t tm_queue;
+ const char *node_name = "node_1_1_1";
+ const char *shaper_name = "test_shaper";
+ const uint64_t rate = 256 * 1000;
+ pkt_info_t pkt_info;
+ const uint16_t num_pkts = 4;
+ int recv_pkts;
+
+ tm_queue = find_tm_queue(0, node_name, 0);
+ CU_ASSERT_FATAL(tm_queue != ODP_TM_INVALID);
+ init_xmt_pkts(&pkt_info);
+ pkt_info.drop_eligible = false;
+ pkt_info.pkt_class = 1;
+ CU_ASSERT_FATAL(make_pkts(num_pkts, pkt_len, &pkt_info) == 0);
+
+ for (int i = 0; i < num_pkts; i++)
+ odp_packet_aging_tmo_set(xmt_pkts[i], tmo_ns);
+
+ CU_ASSERT_FATAL(set_shaper(node_name, shaper_name, rate, rate) == 0);
+ CU_ASSERT(send_pkts(tm_queue, num_pkts) == num_pkts);
+ recv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, num_pkts, MBPS);
+
+ if (is_dropping)
+ CU_ASSERT(recv_pkts < num_pkts)
+ else
+ CU_ASSERT(recv_pkts == num_pkts);
+
+ set_shaper(node_name, NULL, 0, 0);
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+}
+
+static void test_defaults(uint8_t fill)
+{
+ odp_tm_requirements_t req;
+ odp_tm_shaper_params_t shaper;
+ odp_tm_sched_params_t sched;
+ odp_tm_threshold_params_t threshold;
+ odp_tm_wred_params_t wred;
+ odp_tm_node_params_t node;
+ odp_tm_queue_params_t queue;
+ int n;
+
+ memset(&req, fill, sizeof(req));
+ odp_tm_requirements_init(&req);
+ CU_ASSERT(req.num_levels == 0);
+ CU_ASSERT(!req.tm_queue_shaper_needed);
+ CU_ASSERT(!req.tm_queue_wred_needed);
+ CU_ASSERT(!req.tm_queue_dual_slope_needed);
+ CU_ASSERT(!req.tm_queue_threshold_needed);
+ CU_ASSERT(!req.vlan_marking_needed);
+ CU_ASSERT(!req.ecn_marking_needed);
+ CU_ASSERT(!req.drop_prec_marking_needed);
+ for (n = 0; n < ODP_NUM_PACKET_COLORS; n++)
+ CU_ASSERT(!req.marking_colors_needed[n]);
+ CU_ASSERT(req.pkt_prio_mode == ODP_TM_PKT_PRIO_MODE_PRESERVE);
+ for (n = 0; n < ODP_TM_MAX_LEVELS; n++) {
+ odp_tm_level_requirements_t *l_req = &req.per_level[n];
+
+ CU_ASSERT(!l_req->tm_node_shaper_needed);
+ CU_ASSERT(!l_req->tm_node_wred_needed);
+ CU_ASSERT(!l_req->tm_node_dual_slope_needed);
+ CU_ASSERT(!l_req->fair_queuing_needed);
+ CU_ASSERT(!l_req->weights_needed);
+ CU_ASSERT(!l_req->tm_node_threshold_needed);
+ }
+
+ memset(&shaper, fill, sizeof(shaper));
+ odp_tm_shaper_params_init(&shaper);
+ CU_ASSERT(shaper.packet_mode == ODP_TM_SHAPER_RATE_SHAPE);
+ CU_ASSERT(shaper.shaper_len_adjust == 0);
+ CU_ASSERT(!shaper.dual_rate);
+ CU_ASSERT(!shaper.packet_mode);
+
+ memset(&sched, 0xff, sizeof(sched));
+ odp_tm_sched_params_init(&sched);
+ for (n = 0; n < ODP_TM_MAX_PRIORITIES; n++)
+ CU_ASSERT(sched.sched_modes[n] == ODP_TM_BYTE_BASED_WEIGHTS);
+
+ memset(&threshold, fill, sizeof(threshold));
+ odp_tm_threshold_params_init(&threshold);
+ CU_ASSERT(!threshold.enable_max_pkts);
+ CU_ASSERT(!threshold.enable_max_bytes);
+
+ memset(&wred, fill, sizeof(wred));
+ odp_tm_wred_params_init(&wred);
+ CU_ASSERT(!wred.enable_wred);
+ CU_ASSERT(!wred.use_byte_fullness);
+
+ memset(&node, fill, sizeof(node));
+ odp_tm_node_params_init(&node);
+ CU_ASSERT(node.shaper_profile == ODP_TM_INVALID);
+ CU_ASSERT(node.threshold_profile == ODP_TM_INVALID);
+ for (n = 0; n < ODP_NUM_PACKET_COLORS; n++)
+ CU_ASSERT(node.wred_profile[n] == ODP_TM_INVALID);
+
+ memset(&queue, fill, sizeof(queue));
+ odp_tm_queue_params_init(&queue);
+ CU_ASSERT(queue.shaper_profile == ODP_TM_INVALID);
+ CU_ASSERT(queue.threshold_profile == ODP_TM_INVALID);
+ for (n = 0; n < ODP_NUM_PACKET_COLORS; n++)
+ CU_ASSERT(queue.wred_profile[n] == ODP_TM_INVALID);
+ CU_ASSERT(queue.priority == 0);
+ CU_ASSERT(queue.ordered_enqueue);
+}
+
+static void traffic_mngr_test_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void traffic_mngr_test_capabilities(void)
+{
+ CU_ASSERT(test_overall_capabilities() == 0);
+}
+
+static void traffic_mngr_test_tm_create(void)
+{
+ /* Create the first/primary TM system. */
+ CU_ASSERT_FATAL(create_tm_system() == 0);
+ dump_tm_tree(0);
+}
+
+static void traffic_mngr_test_shaper(void)
+{
+ if ((tm_shaper_min_rate <= 1 * MBPS) &&
+ (tm_shaper_max_rate >= 1 * MBPS)) {
+ CU_ASSERT(!odp_cunit_ret(test_shaper_bw("bw1",
+ "node_1_1_1",
+ 0,
+ MBPS * 1)));
+ }
+
+ if ((tm_shaper_min_rate <= 4 * MBPS) &&
+ (tm_shaper_max_rate >= 4 * MBPS)) {
+ CU_ASSERT(!odp_cunit_ret(test_shaper_bw("bw4",
+ "node_1_1_1",
+ 1,
+ 4 * MBPS)));
+ }
+
+ if ((tm_shaper_min_rate <= 10 * MBPS) &&
+ (tm_shaper_max_rate >= 10 * MBPS)) {
+ CU_ASSERT(!odp_cunit_ret(test_shaper_bw("bw10",
+ "node_1_1_1",
+ 2,
+ 10 * MBPS)));
+ }
+
+ if ((tm_shaper_min_rate <= 40 * MBPS) &&
+ (tm_shaper_max_rate >= 40 * MBPS)) {
+ CU_ASSERT(!odp_cunit_ret(test_shaper_bw("bw40",
+ "node_1_1_1",
+ 3,
+ 40 * MBPS)));
+ }
+
+ if ((tm_shaper_min_rate <= 100 * MBPS) &&
+ (tm_shaper_max_rate >= 100 * MBPS)) {
+ CU_ASSERT(!odp_cunit_ret(test_shaper_bw("bw100",
+ "node_1_1_2",
+ 0,
+ 100 * MBPS)));
+ }
+}
+
+static void traffic_mngr_test_scheduler(void)
+{
+ CU_ASSERT(test_sched_queue_priority("que_prio", "node_1_1_3", 10) == 0);
+ return;
+
+ /* The following tests are not quite ready for production use. */
+ CU_ASSERT(test_sched_node_priority("node_prio", "node_1_3", 4) == 0);
+
+ CU_ASSERT(test_sched_wfq("sched_rr", "shaper_rr", "node_1_3",
+ ODP_TM_FRAME_BASED_WEIGHTS,
+ EQUAL_WEIGHTS) == 0);
+ CU_ASSERT(test_sched_wfq("sched_wrr", "shaper_wrr", "node_1_3",
+ ODP_TM_FRAME_BASED_WEIGHTS,
+ INCREASING_WEIGHTS) == 0);
+ CU_ASSERT(test_sched_wfq("sched_wfq", "shaper_wfq", "node_1_3",
+ ODP_TM_BYTE_BASED_WEIGHTS,
+ INCREASING_WEIGHTS) == 0);
+}
+
+static int traffic_mngr_check_thresholds_byte(void)
+{
+ /* Check only for TM queue threshold support as we only test queue threshold. */
+ if (!tm_capabilities.tm_queue_threshold.byte)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_thresholds_packet(void)
+{
+ /* Check only for TM queue threshold support as we only test queue threshold. */
+ if (!tm_capabilities.tm_queue_threshold.packet)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_thresholds_byte_and_packet(void)
+{
+ /* Check only for TM queue threshold support as we only test queue threshold. */
+ if (!tm_capabilities.tm_queue_threshold.byte_and_packet)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_thresholds_byte(void)
+{
+ CU_ASSERT(test_threshold("thresh_byte", "shaper_B", "node_1_2_1", 1,
+ 0, 6400) == 0);
+}
+
+static void traffic_mngr_test_thresholds_packet(void)
+{
+ CU_ASSERT(test_threshold("thresh_packet", "shaper_A", "node_1_2_1", 0,
+ 16, 0) == 0);
+}
+
+static void traffic_mngr_test_thresholds_byte_and_packet(void)
+{
+ CU_ASSERT(test_threshold("thresh_byte_and_packet", "shaper_A", "node_1_2_1", 0,
+ 16, 6400) == 0);
+}
+
+static int traffic_mngr_check_queue_stats(void)
+{
+ if (tm_capabilities.queue_stats.all_counters == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_queue_stats(void)
+{
+ odp_tm_queue_stats_t stats_start, stats_stop;
+ odp_tm_queue_t tm_queue;
+ odp_tm_capabilities_t capa;
+ pkt_info_t pkt_info;
+ uint32_t pkts_sent;
+ uint32_t num_pkts = ODPH_MIN(50u, MAX_PKTS);
+ uint32_t pkt_len = 256;
+
+ CU_ASSERT_FATAL(odp_tm_capability(odp_tm_systems[0], &capa) == 0);
+
+ /* Reuse threshold test node */
+ tm_queue = find_tm_queue(0, "node_1_2_1", 0);
+ CU_ASSERT_FATAL(tm_queue != ODP_TM_INVALID);
+
+ init_xmt_pkts(&pkt_info);
+ pkt_info.drop_eligible = false;
+ pkt_info.pkt_class = 1;
+ CU_ASSERT_FATAL(make_pkts(num_pkts, pkt_len, &pkt_info) == 0);
+
+ CU_ASSERT(odp_tm_queue_stats(tm_queue, &stats_start) == 0);
+
+ pkts_sent = send_pkts(tm_queue, num_pkts);
+
+ num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent,
+ 1 * GBPS);
+
+ odp_tm_stats_print(odp_tm_systems[0]);
+
+ CU_ASSERT(odp_tm_queue_stats(tm_queue, &stats_stop) == 0);
+
+ if (capa.queue_stats.counter.packets)
+ CU_ASSERT(stats_stop.packets >= stats_start.packets + num_rcv_pkts);
+ if (capa.queue_stats.counter.octets)
+ CU_ASSERT(stats_stop.octets >= stats_start.octets + (num_rcv_pkts * pkt_len));
+ CU_ASSERT((stats_stop.discards - stats_start.discards) == 0);
+ CU_ASSERT((stats_stop.discard_octets - stats_start.discard_octets) == 0);
+ CU_ASSERT((stats_stop.errors - stats_start.errors) == 0);
+
+ printf("\nTM queue statistics\n-------------------\n");
+ printf(" discards: %" PRIu64 "\n", stats_stop.discards);
+ printf(" discard octets: %" PRIu64 "\n", stats_stop.discard_octets);
+ printf(" errors: %" PRIu64 "\n", stats_stop.errors);
+ printf(" octets: %" PRIu64 "\n", stats_stop.octets);
+ printf(" packets: %" PRIu64 "\n", stats_stop.packets);
+
+ /* Check that all unsupported counters are still zero */
+ if (!capa.queue_stats.counter.discards)
+ CU_ASSERT(stats_stop.discards == 0);
+ if (!capa.queue_stats.counter.discard_octets)
+ CU_ASSERT(stats_stop.discard_octets == 0);
+ if (!capa.queue_stats.counter.errors)
+ CU_ASSERT(stats_stop.errors == 0);
+ if (!capa.queue_stats.counter.octets)
+ CU_ASSERT(stats_stop.octets == 0);
+ if (!capa.queue_stats.counter.packets)
+ CU_ASSERT(stats_stop.packets == 0);
+
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+}
+
+static int traffic_mngr_check_wred(void)
+{
+ /* Check if wred is part of created odp_tm_t capabilities */
+ if (!tm_capabilities.tm_queue_wred_supported)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_byte_wred(void)
+{
+ /* Check if wred is part of created odp_tm_t capabilities */
+ if (!tm_capabilities.tm_queue_wred_supported ||
+ !tm_capabilities.tm_queue_threshold.byte)
+ return ODP_TEST_INACTIVE;
+
+ if ((tm_shaper_min_rate > 64 * 1000) ||
+ (tm_shaper_max_rate < 64 * 1000) ||
+ (tm_shaper_min_burst > 8 * PKT_BUF_SIZE) ||
+ (tm_shaper_max_burst < 8 * PKT_BUF_SIZE))
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_pkt_wred(void)
+{
+ /* Check if wred is part of created odp_tm_t capabilities */
+ if (!tm_capabilities.tm_queue_wred_supported ||
+ !tm_capabilities.tm_queue_threshold.packet)
+ return ODP_TEST_INACTIVE;
+
+ if ((tm_shaper_min_rate > 64 * 1000) ||
+ (tm_shaper_max_rate < 64 * 1000) ||
+ (tm_shaper_min_burst > 1000) ||
+ (tm_shaper_max_burst < 1000))
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_byte_wred(void)
+{
+ CU_ASSERT(test_byte_wred("byte_wred_30G", "byte_bw_30G",
+ "byte_thresh_30G", "node_1_3_1", 1,
+ ODP_PACKET_GREEN, TM_PERCENT(30), true) == 0);
+ CU_ASSERT(test_byte_wred("byte_wred_50Y", "byte_bw_50Y",
+ "byte_thresh_50Y", "node_1_3_1", 2,
+ ODP_PACKET_YELLOW, TM_PERCENT(50), true) == 0);
+ CU_ASSERT(test_byte_wred("byte_wred_70R", "byte_bw_70R",
+ "byte_thresh_70R", "node_1_3_1", 3,
+ ODP_PACKET_RED, TM_PERCENT(70), true) == 0);
+
+ CU_ASSERT(test_byte_wred("byte_wred_40G", "byte_bw_40G",
+ "byte_thresh_40G", "node_1_3_1", 1,
+ ODP_PACKET_GREEN, TM_PERCENT(30), false) == 0);
+}
+
+static void traffic_mngr_test_pkt_wred(void)
+{
+ int rc;
+
+ rc = test_pkt_wred("pkt_wred_40G", "pkt_bw_40G",
+ "pkt_thresh_40G", "node_1_3_2", 1,
+ ODP_PACKET_GREEN, TM_PERCENT(30), false);
+ if (odp_cunit_ret(rc) != 0)
+ CU_FAIL("40G test failed\n");
+
+ if (!tm_capabilities.tm_queue_dual_slope_supported) {
+ ODPH_DBG("since tm_capabilities indicates no dual slope "
+ "WRED support these tests are skipped.\n");
+ return;
+ }
+
+ rc = test_pkt_wred("pkt_wred_30G", "pkt_bw_30G",
+ "pkt_thresh_30G", "node_1_3_2", 1,
+ ODP_PACKET_GREEN, TM_PERCENT(30), true);
+ if (odp_cunit_ret(rc) != 0)
+ CU_FAIL("30G test failed\n");
+
+ rc = test_pkt_wred("pkt_wred_50Y", "pkt_bw_50Y",
+ "pkt_thresh_50Y", "node_1_3_2", 2,
+ ODP_PACKET_YELLOW, TM_PERCENT(50), true);
+ if (odp_cunit_ret(rc) != 0)
+ CU_FAIL("50Y test failed\n");
+
+ rc = test_pkt_wred("pkt_wred_70R", "pkt_bw_70R",
+ "pkt_thresh_70R", "node_1_3_2", 3,
+ ODP_PACKET_RED, TM_PERCENT(70), true);
+ if (odp_cunit_ret(rc) != 0)
+ CU_FAIL("70Y test failed\n");
+}
+
+static int traffic_mngr_check_query(void)
+{
+ uint32_t query_flags = (ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT);
+
+ /* We need both pkt count and byte count query support */
+ if ((tm_capabilities.tm_queue_query_flags & query_flags) != query_flags)
+ return ODP_TEST_INACTIVE;
+
+ /* This test uses 64 Kbps rate and a 1000 bit burst size */
+ if (tm_shaper_min_rate > 64 * 1000 ||
+ tm_shaper_max_rate < 64 * 1000 ||
+ tm_shaper_min_burst > 1000 ||
+ tm_shaper_max_burst < 1000)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_query(void)
+{
+ CU_ASSERT(test_query_functions("query_shaper", "node_1_3_3", 3, 10)
+ == 0);
+}
+
+static int traffic_mngr_check_vlan_marking(void)
+{
+ if (!tm_capabilities.vlan_marking_supported)
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_ecn_marking(void)
+{
+ if (!tm_capabilities.ecn_marking_supported)
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_drop_prec_marking(void)
+{
+ if (!tm_capabilities.drop_prec_marking_supported)
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static int traffic_mngr_check_ecn_drop_prec_marking(void)
+{
+ if (!tm_capabilities.ecn_marking_supported ||
+ !tm_capabilities.drop_prec_marking_supported)
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_vlan_marking(void)
+{
+ odp_packet_color_t color;
+
+ for (color = 0; color < ODP_NUM_PKT_COLORS; color++) {
+ /* Tree is 3 level */
+ CU_ASSERT(test_vlan_marking("node_1_3_1", color) == 0);
+ }
+}
+
+static void traffic_mngr_test_ecn_marking(void)
+{
+ CU_ASSERT(ip_marking_tests("node_1_3_2", true, false) == 0);
+}
+
+static void traffic_mngr_test_drop_prec_marking(void)
+{
+ CU_ASSERT(ip_marking_tests("node_1_4_2", false, true) == 0);
+}
+
+static void traffic_mngr_test_ecn_drop_prec_marking(void)
+{
+ CU_ASSERT(ip_marking_tests("node_1_4_2", true, true) == 0);
+}
+
+static int traffic_mngr_check_tx_aging(void)
+{
+ return xmt_pktio_capa.max_tx_aging_tmo_ns ? ODP_TEST_ACTIVE : ODP_TEST_INACTIVE;
+}
+
+static void traffic_mngr_test_tx_aging_no_drop(void)
+{
+ /* Set very long aging tmo, packets should not be dropped due to aging */
+ test_packet_aging(60000000000, 128, false);
+}
+
+static void traffic_mngr_test_tx_aging_drop(void)
+{
+ /* Set very short aging tmo, there should be drops due to aging */
+ test_packet_aging(10, MAX_PAYLOAD, true);
+}
+
+static void traffic_mngr_test_fanin_info(void)
+{
+ CU_ASSERT(test_fanin_info("node_1") == 0);
+ CU_ASSERT(test_fanin_info("node_1_2") == 0);
+ CU_ASSERT(test_fanin_info("node_1_3_7") == 0);
+}
+
+static int traffic_mngr_check_lso_ipv4(void)
+{
+ if (xmt_pktio_capa.lso.max_profiles == 0 || xmt_pktio_capa.lso.max_profiles_per_pktio == 0)
+ return ODP_TEST_INACTIVE;
+
+ if (xmt_pktio_capa.lso.proto.ipv4 == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void traffic_mngr_test_lso_ipv4(void)
+{
+ odp_tm_queue_t tm_queue;
+ pkt_info_t pkt_info;
+ uint32_t pkts_sent;
+ int32_t ret, expect;
+ uint32_t num_pkts = 20;
+ uint32_t pkt_len = 256;
+
+ /* Reuse shaper test node */
+ tm_queue = find_tm_queue(0, "node_1_1_1", 0);
+ CU_ASSERT_FATAL(tm_queue != ODP_TM_INVALID);
+
+ /* IPv4 / UDP packets */
+ init_xmt_pkts(&pkt_info);
+ pkt_info.drop_eligible = false;
+ pkt_info.pkt_class = 1;
+ pkt_info.use_ipv6 = 0;
+ pkt_info.use_tcp = 0;
+ CU_ASSERT_FATAL(make_pkts(num_pkts, pkt_len, &pkt_info) == 0);
+
+ pkts_sent = send_pkts_lso(tm_queue, num_pkts, ODP_LSO_PROTO_IPV4, pkt_len / 2);
+ CU_ASSERT(pkts_sent == num_pkts);
+
+ expect = 2 * pkts_sent;
+ ret = receive_loop(odp_tm_systems[0], rcv_pktin, expect, 1000 * ODP_TIME_MSEC_IN_NS);
+ CU_ASSERT(ret > 0);
+
+ num_rcv_pkts = ret;
+
+ /* As packet size is small, there should not be a reason to
+ * split each packet into more than two segments. */
+ CU_ASSERT(ret == expect)
+ if (ret != expect) {
+ ODPH_ERR("\nReceived %i packets, expected %i\n", ret, expect);
+
+ if (ret < 0)
+ num_rcv_pkts = 0;
+ }
+
+ flush_leftover_pkts(odp_tm_systems[0], rcv_pktin);
+ CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0]));
+}
+
+static void traffic_mngr_test_destroy(void)
+{
+ CU_ASSERT(destroy_tm_systems() == 0);
+}
+
+odp_testinfo_t traffic_mngr_suite[] = {
+ ODP_TEST_INFO(traffic_mngr_test_default_values),
+ ODP_TEST_INFO(traffic_mngr_test_capabilities),
+ ODP_TEST_INFO(traffic_mngr_test_tm_create),
+ ODP_TEST_INFO(traffic_mngr_test_shaper_profile),
+ ODP_TEST_INFO(traffic_mngr_test_sched_profile),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_threshold_profile_byte,
+ traffic_mngr_check_thresholds_byte),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_threshold_profile_packet,
+ traffic_mngr_check_thresholds_packet),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_threshold_profile_byte_and_packet,
+ traffic_mngr_check_thresholds_byte_and_packet),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_wred_profile,
+ traffic_mngr_check_wred),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_shaper,
+ traffic_mngr_check_shaper),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_scheduler,
+ traffic_mngr_check_scheduler),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_thresholds_byte,
+ traffic_mngr_check_thresholds_byte),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_thresholds_packet,
+ traffic_mngr_check_thresholds_packet),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_thresholds_byte_and_packet,
+ traffic_mngr_check_thresholds_byte_and_packet),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_byte_wred,
+ traffic_mngr_check_byte_wred),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_pkt_wred,
+ traffic_mngr_check_pkt_wred),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_query,
+ traffic_mngr_check_query),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_queue_stats,
+ traffic_mngr_check_queue_stats),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_vlan_marking,
+ traffic_mngr_check_vlan_marking),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_ecn_marking,
+ traffic_mngr_check_ecn_marking),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_drop_prec_marking,
+ traffic_mngr_check_drop_prec_marking),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_ecn_drop_prec_marking,
+ traffic_mngr_check_ecn_drop_prec_marking),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_tx_aging_no_drop,
+ traffic_mngr_check_tx_aging),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_tx_aging_drop,
+ traffic_mngr_check_tx_aging),
+ ODP_TEST_INFO(traffic_mngr_test_fanin_info),
+ ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_lso_ipv4, traffic_mngr_check_lso_ipv4),
+ ODP_TEST_INFO(traffic_mngr_test_destroy),
+ ODP_TEST_INFO_NULL,
+};
+
+odp_suiteinfo_t traffic_mngr_suites[] = {
+ { "traffic_mngr tests", traffic_mngr_suite_init,
+ traffic_mngr_suite_term, traffic_mngr_suite },
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ int ret = odp_cunit_register(traffic_mngr_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ /* Exit with 77 in order to indicate that test is skipped completely */
+ if (!ret && suite_inactive == (ODPH_ARRAY_SIZE(traffic_mngr_suites) - 1))
+ return 77;
+ return ret;
+}